赞
踩
前端 vue.js + d3.js。
D3.vue
<template>
<div
class="d3-container">
<div
class="types">
<span
v-for="(value, key, index) of typeCategories"
:key="index"
:style="{backgroundColor: typeColor[key]}"
@mouseover="handleTypeMouseover(value, key)"
@mouseout="handleTypeMouseout()">
{{ key }}({{ value.length }})
</span>
</div>
<div
class="info">
{{ info }}
</div>
<div
class="btns">
<span
id="zoomIn">
+
</span>
<span
id="zoomOut">
-
</span>
<span
id="reset">
reset
</span>
</div>
<svg />
</div>
</template>
<script>
import * as d3 from 'd3'
import mock from './mock.json'
export default {
data() {
return {
info: '',
typeCategories: {},
typeColor: {}, // 类型配色
type: ['Disease', 'Symptom', 'Age', 'Sex', 'BodyStructure', 'Check', 'Thing', 'Severity', 'Population', 'Side', 'BetterAndWorse', 'Body', 'Aggravate', 'Imaging', 'Relieve', 'Event', 'Base', 'Finding', 'BodyStructure_test', 'Action', 'Time'], // 分类
nodes: [], // 节点集
links: [], // 关系集
visibleFlag: false
}
},
watch: {
nodes: {
handler(val) {
// 监听节点变化,设置类型标签
const obj = {}
val.forEach(e => {
if (Object.keys(obj).indexOf('' + e.semantic_type) === -1) {
obj[e.semantic_type] = []
}
obj[e.semantic_type].push(e)
})
this.typeCategories = obj
},
deep: true
}
},
mounted() {
this.setTypeColor()
this.initD3()
},
methods: {
handleTypeMouseout() {
d3.selectAll('.single-node').style('opacity', 1)
d3.selectAll('.single-line').style('opacity', 1)
},
handleTypeMouseover(data) {
d3.selectAll('.single-node').style('opacity', 0.1)
d3.selectAll('.single-line').style('opacity', 0.1)
for (let i = 0; i < data.length; i++) {
const nodeID = '#single-node' + data[i].id
d3.selectAll(nodeID).style('opacity', 1)
}
},
setTypeColor() {
const obj = {}
this.type.forEach(e => {
if (Object.keys(obj).indexOf('' + e) === -1) {
obj[e] = ''
}
obj[e] = this.randomColor()
})
this.typeColor = obj
},
randomColor() {
const colors = ['#F4AB87', '#EEC88D', '#76CADF', '#97DA9D', '#88DCD8', '#FB7F89', '#F0E403', '#F576BE', '#ACADFF', '#7EC3FB', '#D0DB02', '#C07B11', '#00ACC2', '#2AAD41', '#A59D00', '#EB4747', '#CD0EBD', '#DE3997']
return colors[Math.floor(Math.random() * colors.length)]
},
initD3() {
const _this = this
// 数据示例
// nodes = [
// { id: 'a', name: 'a' },
// { id: 'b', name: 'b' },
// { id: 'c', name: 'c' }
// ]
// links = [
// { id: 'ab', source: 'a', target: 'b' },
// { id: 'bc', source: 'b', target: 'c' }
// ]
// 容器
const svg = d3.select('svg')
.attr('viewBox', [-window.innerWidth / 2, -window.innerHeight / 2, window.innerWidth, window.innerHeight])
// 缩放
const zoom = d3.zoom()
.on('zoom', function () {
svg.attr('transform', d3.zoomTransform(svg.node()))
// const tran = d3.zoomTransform(svg.node())
// const _k = tran.k
// console.log(tran)
// console.log(Math.floor(_k * 100) / 100)
})
svg.call(zoom)
d3.select('#reset')
.on('click', function () {
svg.call(zoom.transform, d3.zoomIdentity)
})
d3.select('#zoomIn')
.on('click', function () {
zoom.scaleBy(svg, 1.1)
})
d3.select('#zoomOut')
.on('click', function () {
zoom.scaleBy(svg, 0.9) // 执行该方法后 会触发 zoom 事件 0.9 缩小
})
// 新建一个力导向图
const simulation = d3.forceSimulation()
.force('charge', d3.forceManyBody().strength(-1000))
.force('link', d3.forceLink().id(d => d.id).distance(200))
.force('x', d3.forceX())
.force('y', d3.forceY())
.on('tick', ticked)
// 关系路径
let link = svg.append('g')
.attr('class', 'link-container')
.attr('stroke', '#000')
.attr('stroke-width', 1)
.selectAll('line')
// 关系文字
let linkText = svg.append('g')
.attr('class', 'link-text-container')
.attr('stroke', '#000')
.attr('stroke-width', 1.5)
.selectAll('text')
// 节点
let node = svg.append('g')
.attr('class', 'node-container')
.selectAll('circle')
function ticked() {
node
.attr('transform', function (d) {
return 'translate(' + d.x + ',' + d.y + ')'
})
link.attr('x1', d => d.source.x)
.attr('y1', d => d.source.y)
.attr('x2', d => d.target.x)
.attr('y2', d => d.target.y)
linkText
.attr('x', d => (d.source.x + d.target.x) / 2)
.attr('y', d => (d.source.y + d.target.y) / 2)
}
// 更新
const updateObj = Object.assign(svg.node(), {
update({ nodes, links }) {
// 做一个浅复制,以防止突变,回收旧节点以保持位置和速度
const old = new Map(node.data().map(d => [d.id, d]))
nodes = nodes.map(d => Object.assign(old.get(d.id) || {}, d))
links = links.map(d => Object.assign({}, d))
// 节点
node = node
.data(nodes, d => d.id)
.join(
enter =>
enter.append('g')
.attr('class', 'single-node')
.attr('id', (d) => {
return 'single-node' + d.id
})
)
.call(d3.drag()
.on('start', dragstarted)
.on('drag', dragged)
.on('end', dragended))
d3.selectAll('.single-node')
.append('circle')
.attr('r', 30)
.attr('fill', nodeColor)
.style('cursor', 'pointer')
// 节点文字
d3.selectAll('.single-node')
.append('text')
.attr('y', 0)
.attr('dy', 5)
.attr('text-anchor', 'middle')
.style('cursor', 'pointer')
.attr('x', function (d) {
return textBreaking(d3.select(this), d, false)
})
// 绘制箭头
svg.append('g')
.attr('class', 'arrow-marker')
.append('marker')
.attr('id', 'arrow-marker')
.attr('markerUnits', 'strokeWidth') // 设置为 strokeWidth 箭头会随着线的粗细发生变化
.attr('markerUnits', 'userSpaceOnUse')
.attr('viewBox', '0 -5 10 10') // 坐标系的区域
.attr('refX', 40) // 箭头坐标
.attr('refY', 0)
.attr('markerWidth', 10) // 标识的大小
.attr('markerHeight', 10)
.attr('orient', 'auto') // 绘制方向,可设定为:auto(自动确认方向)和 角度值
.attr('stroke-width', 2) // 箭头宽度
.append('path')
.attr('d', 'M0,-5L10,0L0,5') // 箭头的路径
.attr('fill', '#000') // 箭头颜色
// 关系路径
link = link
.data(links, d => [d.source, d.target])
.join(
enter =>
enter.append('line')
.attr('class', 'single-line')
.attr('id', (d) => {
return 'single-line' + d.id
})
.attr('marker-end', 'url(#arrow-marker)') // 根据箭头标记的 id 号标记箭头
)
// 路径文字
linkText = linkText
.data(links, d => [d.source, d.target])
.join(
enter =>
enter.append('text')
.attr('class', 'link-text')
.attr('id', (d) => {
return 'link-text' + d.id
})
.text((d) => {
return d.semantic_type
})
.attr('stroke', '#000')
.attr('stroke-width', '1')
.attr('fill', 'none')
.style('cursor', 'pointer')
)
simulation.nodes(nodes)
simulation.force('link').links(links)
simulation.alpha(1).restart()
node
.on('click', function (d) {
_this.visibleFlag = !_this.visibleFlag
toggleMenu(d3.select(this), d, _this.visibleFlag)
})
.on('mouseover', function (d) {
// 鼠标移入节点,高亮当前节点及与当前节点有关系的路径和节点
d3.selectAll('.single-node').style('opacity', 0.2)
d3.selectAll('.single-line').style('opacity', 0.2)
d3.selectAll('.link-text').style('opacity', 0.2)
d3.select('#single-node' + d.id).style('opacity', 1)
const relationLinks = []
_this.links.forEach((item) => {
if (item.source === d.id || item.target === d.id) {
relationLinks.push(item)
}
})
relationLinks.forEach((item) => {
d3.select('#single-line' + item.id).style('opacity', 1)
d3.select('#link-text' + item.id).style('opacity', 1)
d3.select('#single-node' + item.source).style('opacity', 1)
d3.select('#single-node' + item.target).style('opacity', 1)
})
_this.info = JSON.stringify(d)
})
.on('mouseout', function () {
d3.selectAll('.single-node').style('opacity', 1)
d3.selectAll('.single-line').style('opacity', 1)
d3.selectAll('.link-text').style('opacity', 1)
_this.info = ''
})
link
.on('mouseover', function (d) {
d3.selectAll('.single-node').style('opacity', 0.2)
d3.selectAll('.single-line').style('opacity', 0.2)
d3.selectAll('.link-text').style('opacity', 0.2)
d3.select('#single-line' + d.id).style('opacity', 1)
d3.select('#link-text' + d.id).style('opacity', 1)
d3.select('#single-node' + d.source.id).style('opacity', 1)
d3.select('#single-node' + d.target.id).style('opacity', 1)
_this.info = JSON.stringify(d)
})
.on('mouseout', function () {
d3.selectAll('.single-node').style('opacity', 1)
d3.selectAll('.single-line').style('opacity', 1)
d3.selectAll('.link-text').style('opacity', 1)
_this.info = ''
})
}
})
/**
* @name: 设置节点颜色
* @param {*} node
*/
function nodeColor(node) {
const type = node.semantic_type
if (_this.typeColor[type]) {
return _this.typeColor[type]
} else {
return '#ddd'
}
}
/**
* @name: 新增节点和关系
* @param {*} node
*/
function addNodesAndLinks(node) {
// 模拟接口返回节点和关系数据
_this.$nextTick(() => {
const res = mock
const resData = res.data.relations
const edgeResult = []
for (let i = 0; i < resData.length; i++) {
edgeResult[i] = {
id: resData[i].id,
source: resData[i].direction === 0 ? node.id : resData[i].relation_node.id,
target: resData[i].direction === 0 ? resData[i].relation_node.id : node.id,
relation: resData[i].name,
name: resData[i].name,
properties: resData[i].properties,
semantic_type: resData[i].semantic_type
}
}
const nodeReault = resData.map(_ => _.relation_node)
nodeReault.forEach((item) => {
_this.nodes.push(item)
})
_this.nodes.push({
'id': 'qwertyuiop',
'name': '测试无关系节点',
'semantic_type': 'Symptom',
'labels': ['Concept', 'Symptom'],
'properties': {
'scenes': 'allinmd',
'status': 1,
'lastModified': 1618293198,
'releaseDate': 1618293198
}
})
edgeResult.forEach((item) => {
_this.links.push(item)
})
updateObj.update({
nodes: _this.nodes,
links: _this.links
})
})
}
/**
* @name: 关联节点去重重组
* @param {*} objarray
*/
function uniqObjInArray(objarray) {
const len = objarray.length
const tempJson = {}
const res = []
for (let i = 0; i < len; i++) {
// 取出每一个对象
tempJson[JSON.stringify(objarray[i])] = true
}
const keyItems = Object.keys(tempJson)
for (let j = 0; j < keyItems.length; j++) {
res.push(JSON.parse(keyItems[j]))
}
return res
}
/**
* @name: 收起,删除当前节点下一级没有其他关系的节点
* @param {*} node
*/
function deleteNextNodes(node) {
const relationNode = []
const relationList = []
const hasRelationList = []
d3.selectAll('.single-line').each(function (e) {
if (e.source.id === node.id) {
hasRelationList.push(e)
} else {
relationList.push(e) // 删除节点有关系的其他关系
}
// 需要删除的节点相关的节点
if (e.source.id === node.id) {
relationNode.push(e.target)
}
if (e.target.id === node.id) {
relationNode.push(e.source)
}
})
let tempNodeList = JSON.parse(JSON.stringify(relationNode))
tempNodeList = uniqObjInArray(tempNodeList)
// 区分下级节点是否是孤节点,如果还有其他关系,则不能删除
tempNodeList.forEach(function (item) {
const hasLine = relationList.findIndex(jtem => jtem.target.id === item.id || jtem.source.id === item.id)
if (hasLine >= 0) {
item.notSingle = true
}
})
tempNodeList.forEach(function (item) {
if (!item.notSingle) {
d3.select('#single-node' + item.id).remove()
}
})
const otherTempNode = []
tempNodeList = tempNodeList.map(item => {
if (!item.notSingle) {
otherTempNode.push(item)
}
})
hasRelationList.forEach(item => {
otherTempNode.forEach(jtem => {
if (jtem.id === item.source.id || jtem.id === item.target.id) {
d3.select('#single-line' + item.id).remove()
d3.select('#link-text' + item.id).remove()
}
})
})
d3.select('.menu-circle').remove()
}
/**
* @name: 隐藏,删除当前及下一级没有其他关系的节点
* @param {*} node
*/
function deleteNodeAndLinks(node) {
const removeIndex = _this.nodes.findIndex(data => data.id === node.id)
_this.nodes.splice(removeIndex, 1)
const relationNode = []
const relationList = []
const clickNode = node.id
d3.selectAll('.single-line').each(function (e) {
if (e.source.id === node.id || e.target.id === node.id) {
d3.select(this).remove()
} else {
relationList.push(e)
}
// 需要删除的节点相关的节点
if (e.source.id === node.id) {
relationNode.push(e.target)
}
})
let tempNodeList = JSON.parse(JSON.stringify(relationNode))
tempNodeList = uniqObjInArray(tempNodeList)
// 区分下级节点是否是孤节点
tempNodeList.forEach(function (item) {
const hasLine = relationList.findIndex(jtem => jtem.target.id === item.id || jtem.source.id === item.id)
if (hasLine >= 0) {
item.notSingle = true
}
})
tempNodeList.forEach(function (item) {
if (!item.notSingle) {
d3.select('#single-node' + item.id).remove()
}
})
d3.selectAll('.single-node').each(function (d) {
const temp = d.id
// 删除当前需要隐藏的节点
if (temp === clickNode) {
d3.select('.menu-circle').remove()
d3.select(this).remove()
}
})
d3.selectAll('.link-text').each(function (e) {
if (e.source === node || e.target === node) {
d3.select(this).remove()
}
})
}
/**
* @name: 生成操作菜单
* @param {*} current 当前元素
* @param {*} d 当前元素对应的数据
* @param {*} flag 显隐
*/
function toggleMenu(current, d, flag) {
const currentD = d
const data = [{
population: 30,
value: '隐藏',
type: 'delete'
}, {
population: 30,
value: '收起',
type: 'showOn'
}, {
population: 30,
value: '展开',
type: 'showOff'
}]
// 创建一个环生成器
const arc = d3.arc()
.innerRadius(80) // 内半径
.outerRadius(35) // 外半径
const pie = d3.pie()
.value(function (d) {
return d.population
})
.sort(null)
const pieData = pie(data)
const pieAngle = pieData.map(function (p) {
return (p.startAngle + p.endAngle) / 2 / Math.PI * 180
})
// 菜单容器
const g = current
.append('g')
.attr('class', 'menu-circle')
.attr('width', 100)
.attr('height', 100)
const Pie = g.append('g')
Pie.selectAll('path')
.data(pie(data))
.enter()
.append('path')
.attr('d', arc)
.attr('fill', '#d3d7dc')
.style('stroke', '#fff')
.style('cursor', 'pointer')
.on('click', function (d) {
if (d.data.type === 'delete') {
deleteNodeAndLinks(currentD)
} else if (d.data.type === 'showOn') {
deleteNextNodes(currentD)
} else {
addNodesAndLinks(currentD)
}
d3.event.stopPropagation()
})
.on('mouseover', function () {
d3.select(this)
.style('fill', '#d3d7dc')
.transition()
.style('fill', '#aaaeb4')
})
.on('mouseout', function () {
d3.select(this)
.style('fill', '#aaaeb4')
.transition()
.style('fill', '#d3d7dc')
})
// 安妮文字
const labelFontSize = 12
const labelValRadius = (170 * 0.35 - labelFontSize * 0.35)
const labelValRadius1 = (170 * 0.35 + labelFontSize * 0.35)
const labelsVals = current
.select('.menu-circle')
.append('g')
.classed('labelsvals', true)
// 定义两条路径以使标签的方向正确
labelsVals.append('def')
.append('path')
.attr('id', 'label-path-1')
.attr('d', `m0 ${-labelValRadius} a${labelValRadius} ${labelValRadius} 0 1,1 -0.01 0`)
labelsVals.append('def')
.append('path')
.attr('id', 'label-path-2')
.attr('d', `m0 ${-labelValRadius1} a${labelValRadius1} ${labelValRadius1} 0 1,0 0.01 0`)
labelsVals.selectAll('text')
.data(data)
.enter()
.append('text')
.attr('dy', function (d) {
if (d.type === 'showOn') {
return -5
} else {
return 5
}
})
.style('font-size', labelFontSize)
.style('fill', 'black')
.style('font-weight', 'bold')
.style('text-anchor', 'middle')
.append('textPath')
.style('cursor', 'pointer')
.attr('href', function (d, i) {
const angle = pieAngle[i]
if (angle > 90 && angle <= 270) { // 根据角度选择路径
return '#label-path-2'
} else {
return '#label-path-1'
}
})
.attr('startOffset', function (d, i) {
const p = pieData[i]
const angle = pieAngle[i]
const percent = (p.startAngle + p.endAngle) / 2 / 2 / Math.PI * 100
if (angle > 90 && angle <= 270) { // 分别计算每条路径的正确百分比
return 100 - percent + '%'
}
return percent + '%'
})
.text(function (d) {
return d.value
})
.on('click', function (d) {
if (d.type === 'delete') {
deleteNodeAndLinks(currentD)
} else if (d.type === 'showOn') {
deleteNextNodes(currentD)
} else {
addNodesAndLinks(currentD)
}
d3.event.stopPropagation()
}, true)
if (flag === false) {
d3.selectAll('.menu-circle').remove()
}
}
/**
* @name: 节点文字换行
* @param {*} dom
* @param {*} data
* @param {*} breaking 是否换行
*/
function textBreaking(dom, data, breaking) {
const text = data.name
if (breaking) {
const len = text.length
if (len <= 3) {
dom.append('tspan')
.attr('x', 0)
.attr('y', 0)
.text(text)
} else {
const topText = text.substring(0, 3)
const midText = text.substring(3, 7)
let botText = text.substring(7, len)
let topY = -22
let midY = 8
const botY = 34
if (len <= 9) {
topY += 10
midY += 10
} else {
botText = text.substring(7, 9) + '...'
}
dom.text('')
dom.append('tspan')
.attr('x', 0)
.attr('y', topY)
.text(function () {
return topText
})
dom.append('tspan')
.attr('x', 0)
.attr('y', midY)
.text(function () {
return midText
})
dom.append('tspan')
.attr('x', 0)
.attr('y', botY - 7)
.text(function () {
return botText
})
}
} else {
dom.append('tspan')
.attr('x', 0)
.attr('y', 0)
.style('font-size', 12)
.style('stroke', '#333')
.text(data.name)
}
}
/**
* @name: 拖动
* @param {*} event
*/
function dragstarted(event) {
if (!d3.event.active) {
simulation.alphaTarget(0.8).restart() // 设置衰减系数,对节点位置移动过程的模拟,数值越高移动越快,数值范围[0, 1]
}
event.fx = event.x
event.fy = event.y
}
function dragged(event) {
event.fx = d3.event.x
event.fy = d3.event.y
}
function dragended(event) {
if (!d3.event.active) {
simulation.alphaTarget(0)
}
event.fx = null
event.fy = null
}
// updateObj.update({
// nodes: _this.nodes,
// links: _this.links
// })
// 模拟接口返回节点信息
_this.$nextTick(() => {
// 初次返回单个节点
const res = {
'code': 0,
'message': '',
'data': {
'id': '83c8aeb69c1011eb892ad31672d12132',
'name': '骨筋膜室综合症',
'semantic_type': 'Disease',
'labels': ['Concept', 'Disease'],
'properties': {
'scenes': 'allinmd',
'status': 1,
'lastModified': 1618293198,
'releaseDate': 1618293198
}
}
}
const data = res.data
_this.nodes.push(data)
updateObj.update({
nodes: _this.nodes,
links: _this.links
})
})
}
}
}
</script>
<style>
.d3-container {
position: relative;
}
.d3-container .info {
background: #fff;
position: absolute;
left: 50px;
bottom: 50px;
z-index: 9;
}
.d3-container .btns {
background: #fff;
position: absolute;
right: 50px;
bottom: 200px;
z-index: 99;
}
.d3-container .btns span {
cursor: pointer;
}
.d3-container .types {
position: absolute;
left: 50px;
top: 50px;
z-index: 9;
}
.d3-container .types span {
display: inline-block;
background: #a5abb6;
border-radius: 4px;
margin-right: 10px;
padding: 5px 6px;
cursor: pointer;
color: #2e0f00;
font-size: 12px;
}
</style>
mock.json
{
"code": 0,
"message": "",
"data": {
"relations": [{
"id": "2730",
"name": "composed_of",
"semantic_type": "composed_of",
"properties": {
"scenes": "allinmd",
"status": 1,
"lastModified": 1618832915,
"releaseDate": 1618832915
},
"direction": 0,
"relation_node": {
"id": "2c29f5249cf211ebbc710242c0a8c409",
"name": "外伤史",
"semantic_type": "Symptom",
"labels": ["Concept", "Symptom"],
"properties": {
"scenes": "allinmd",
"status": 1,
"lastModified": 1618384950,
"releaseDate": 1618384950
}
}
}, {
"id": "2677",
"name": "composed_of",
"semantic_type": "composed_of",
"properties": {
"scenes": "allinmd",
"status": 1,
"lastModified": 1618803809,
"releaseDate": 1618803809
},
"direction": 1,
"relation_node": {
"id": "8f2d01129c1011eb892ad31672d12132",
"name": "膝关节外伤及手术史",
"semantic_type": "Symptom",
"labels": ["Concept", "Symptom"],
"properties": {
"scenes": "allinmd",
"status": 1,
"lastModified": 1618293198,
"releaseDate": 1618293198
}
}
}, {
"id": "2649",
"name": "relation",
"semantic_type": "relation",
"properties": {
"scenes": "allinmd",
"status": 1,
"lastModified": 1618885401,
"releaseDate": 1618885401
},
"direction": 1,
"relation_node": {
"id": "8e8922369c1011eb892ad31672d12132",
"name": "髌骨肌腱炎",
"semantic_type": "BodyStructure",
"labels": ["Concept", "Disease"],
"properties": {
"scenes": "allinmd",
"status": 1,
"lastModified": 1618293198,
"releaseDate": 1618293198
}
}
}, {
"id": "2672",
"name": "relation",
"semantic_type": "relation",
"properties": {
"scenes": "allinmd",
"status": 1,
"lastModified": 1618885282,
"releaseDate": 1618885282
},
"direction": 1,
"relation_node": {
"id": "8f0c21689c1011eb892ad31672d12132",
"name": "膝关节脱位",
"semantic_type": "Disease",
"labels": ["Concept", "Disease"],
"properties": {
"scenes": "allinmd",
"status": 1,
"lastModified": 1618293198,
"releaseDate": 1618293198
}
}
}, {
"id": "2662",
"name": "relation",
"semantic_type": "relation",
"properties": {
"scenes": "allinmd",
"status": 1,
"lastModified": 1618885172,
"releaseDate": 1618885172
},
"direction": 1,
"relation_node": {
"id": "8eec11d49c1011eb892ad31672d12132",
"name": "股骨髁骨折",
"semantic_type": "Side",
"labels": ["Concept", "Disease"],
"properties": {
"scenes": "allinmd",
"status": 1,
"lastModified": 1618293198,
"releaseDate": 1618293198
}
}
}, {
"id": "2660",
"name": "relation",
"semantic_type": "relation",
"properties": {
"scenes": "allinmd",
"status": 1,
"lastModified": 1618885118,
"releaseDate": 1618885118
},
"direction": 1,
"relation_node": {
"id": "8ed52bcc9c1011eb892ad31672d12132",
"name": "膝关节内侧副韧带损伤",
"semantic_type": "Disease",
"labels": ["Concept", "Disease"],
"properties": {
"scenes": "allinmd",
"status": 1,
"lastModified": 1618293198,
"releaseDate": 1618293198
}
}
}, {
"id": "2636",
"name": "relation",
"semantic_type": "relation",
"properties": {
"scenes": "allinmd",
"status": 1,
"lastModified": 1618884966,
"releaseDate": 1618884966
},
"direction": 1,
"relation_node": {
"id": "8ebdb2d09c1011eb892ad31672d12132",
"name": "膝髌韧带裂(扭)伤",
"semantic_type": "Disease",
"labels": ["Concept", "Disease"],
"properties": {
"scenes": "allinmd",
"status": 1,
"lastModified": 1618293198,
"releaseDate": 1618293198
}
}
}, {
"id": "2621",
"name": "relation",
"semantic_type": "relation",
"properties": {
"scenes": "allinmd",
"status": 1,
"lastModified": 1618883252,
"releaseDate": 1618883252
},
"direction": 1,
"relation_node": {
"id": "84417f449c1011eb892ad31672d12132",
"name": "剥脱性骨软骨炎",
"semantic_type": "Disease",
"labels": ["Concept", "Disease"],
"properties": {
"scenes": "allinmd",
"status": 1,
"lastModified": 1618293198,
"releaseDate": 1618293198
}
}
}, {
"id": "2553",
"name": "relation",
"semantic_type": "relation",
"properties": {
"scenes": "allinmd",
"status": 1,
"lastModified": 1618886711,
"releaseDate": 1618886711
},
"direction": 1,
"relation_node": {
"id": "8ef701fc9c1011eb892ad31672d12132",
"name": "髌骨脱位",
"semantic_type": "BetterAndWorse",
"labels": ["Concept", "Disease"],
"properties": {
"scenes": "allinmd",
"status": 1,
"lastModified": 1618293198,
"releaseDate": 1618293198
}
}
}, {
"id": "2538",
"name": "relation",
"semantic_type": "relation",
"properties": {
"scenes": "allinmd",
"status": 1,
"lastModified": 1618886139,
"releaseDate": 1618886139
},
"direction": 1,
"relation_node": {
"id": "8edf38889c1011eb892ad31672d12132",
"name": "膝关节游离体",
"semantic_type": "Disease",
"labels": ["Concept", "Disease"],
"properties": {
"scenes": "allinmd",
"status": 1,
"lastModified": 1618293198,
"releaseDate": 1618293198
}
}
}, {
"id": "2531",
"name": "relation",
"semantic_type": "relation",
"properties": {
"scenes": "allinmd",
"status": 1,
"lastModified": 1618886026,
"releaseDate": 1618886026
},
"direction": 1,
"relation_node": {
"id": "8eb96dc49c1011eb892ad31672d12132",
"name": "髌骨软骨软化",
"semantic_type": "Relieve",
"labels": ["Concept", "Disease"],
"properties": {
"scenes": "allinmd",
"status": 1,
"lastModified": 1618293198,
"releaseDate": 1618293198
}
}
}, {
"id": "2527",
"name": "relation",
"semantic_type": "relation",
"properties": {
"scenes": "allinmd",
"status": 1,
"lastModified": 1618885900,
"releaseDate": 1618885900
},
"direction": 1,
"relation_node": {
"id": "84254cac9c1011eb892ad31672d12132",
"name": "半月板囊肿",
"semantic_type": "Disease",
"labels": ["Concept", "Disease"],
"properties": {
"scenes": "allinmd",
"status": 1,
"lastModified": 1618293198,
"releaseDate": 1618293198
}
}
}, {
"id": "2582",
"name": "relation",
"semantic_type": "relation",
"properties": {
"scenes": "allinmd",
"status": 1,
"lastModified": 1618803809,
"releaseDate": 1618803809
},
"direction": 1,
"relation_node": {
"id": "8ed7c3509c1011eb892ad31672d12132",
"name": "闭合型胫骨平台骨折",
"semantic_type": "Disease",
"labels": ["Concept", "Disease"],
"properties": {
"scenes": "allinmd",
"status": 1,
"lastModified": 1618293198,
"releaseDate": 1618293198
}
}
}, {
"id": "2552",
"name": "relation",
"semantic_type": "relation",
"properties": {
"scenes": "allinmd",
"status": 1,
"lastModified": 1618803809,
"releaseDate": 1618803809
},
"direction": 1,
"relation_node": {
"id": "8401f0d69c1011eb892ad31672d12132",
"name": "半月板损伤",
"semantic_type": "Disease",
"labels": ["Concept", "Disease"],
"properties": {
"scenes": "allinmd",
"status": 1,
"lastModified": 1618293198,
"releaseDate": 1618293198
}
}
}, {
"id": "5534",
"name": "relation",
"semantic_type": "relation",
"properties": {
"scenes": "allinmd",
"status": 1,
"lastModified": 1618882945,
"releaseDate": 1618882945
},
"direction": 1,
"relation_node": {
"id": "8ee67ed69c1011eb892ad31672d12132",
"name": "髌骨骨折",
"semantic_type": "Action",
"labels": ["Concept", "Disease"],
"properties": {
"scenes": "allinmd",
"status": 1,
"lastModified": 1618293198,
"releaseDate": 1618293198
}
}
}, {
"id": "2533",
"name": "relation",
"semantic_type": "relation",
"properties": {
"scenes": "allinmd",
"status": 1,
"lastModified": 1618886473,
"releaseDate": 1618886473
},
"direction": 1,
"relation_node": {
"id": "8ee6f92e9c1011eb892ad31672d12132",
"name": "膝关节或韧带的脱位、劳损或扭伤",
"semantic_type": "Disease",
"labels": ["Concept", "Disease"],
"properties": {
"scenes": "allinmd",
"status": 1,
"lastModified": 1618293198,
"releaseDate": 1618293198
}
}
}]
}
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。