Commit 4ee6c475 authored by 郭铭瑶's avatar 郭铭瑶 🤘

调整

parent 674f4769
...@@ -14,4 +14,5 @@ export default { ...@@ -14,4 +14,5 @@ export default {
DELETE_RELATION: '/node/relation', DELETE_RELATION: '/node/relation',
GET_NODES: '/node/relations', GET_NODES: '/node/relations',
DELETE_NODE: '/node/{id}', DELETE_NODE: '/node/{id}',
GET_SYSTEMS: '/systems',
} }
...@@ -103,19 +103,6 @@ export default { ...@@ -103,19 +103,6 @@ export default {
const graphData = ref({ nodes: [], links: [] }) const graphData = ref({ nodes: [], links: [] })
const subjectNodes = ref([]) const subjectNodes = ref([])
function fetchSubjects() { function fetchSubjects() {
// ajax
// .get({
// url: api.GET_NODES,
// params: { nodeId: '572563400077869056' },
// })
// .then((res) => {
// console.log('nodes:', res)
// graphData.value = res.data.content
// systemOptions.value = graphData.value.nodes
// .filter((node) => node.nodeLabel === 'System')
// .map((node) => ({ label: node.systemName, value: node.nodeId }))
// })
ajax ajax
.get({ .get({
url: api.GET_SUBJECTS, url: api.GET_SUBJECTS,
...@@ -138,14 +125,28 @@ export default { ...@@ -138,14 +125,28 @@ export default {
if (!res.data || !res.data.content) return if (!res.data || !res.data.content) return
relationOptions.value = res.data.content.map((item) => ({ relationOptions.value = res.data.content.map((item) => ({
label: item.relationName, label: item.relationName,
value: item.id, value: item.id + '',
}))
})
ajax
.get({
url: api.GET_SYSTEMS,
})
.then((res) => {
if (!res.data || !res.data.content) return
systemOptions.value = res.data.content.map((item) => ({
label: item.systemName,
value: item.id + '',
})) }))
}) })
function fetchSubordinates(params = null) { function fetchSubordinates(params = null) {
ajax ajax
.get({ .get({
url: api.GET_NODES, url: api.GET_NODES,
params, params: {
type: params.nodeLabel,
nodeId: params.nodeId,
},
}) })
.then((res) => { .then((res) => {
const { links, nodes } = res.data.content const { links, nodes } = res.data.content
...@@ -166,9 +167,6 @@ export default { ...@@ -166,9 +167,6 @@ export default {
), ),
], ],
} }
systemOptions.value = graphData.value.nodes
.filter((node) => node.nodeLabel === 'System')
.map((node) => ({ label: node.systemName, value: node.nodeId }))
}) })
} }
function handleAdd({ nodeId }) { function handleAdd({ nodeId }) {
...@@ -197,7 +195,10 @@ export default { ...@@ -197,7 +195,10 @@ export default {
}, },
}) })
.then(() => { .then(() => {
fetchSubordinates({ nodeId: subjectId.value }) fetchSubordinates({
nodeId: subjectId.value,
nodeLabel: 'Subject',
})
isLoading.value = false isLoading.value = false
showDrawer.value = false showDrawer.value = false
message.success('提交成功') message.success('提交成功')
...@@ -224,7 +225,6 @@ export default { ...@@ -224,7 +225,6 @@ export default {
if (data.nodeLabel === 'Subject') { if (data.nodeLabel === 'Subject') {
fetchSubjects() fetchSubjects()
} else { } else {
// fetchSubordinates({ nodeId: data.subjectId })
graphData.value = { graphData.value = {
nodes: graphData.value.nodes.filter( nodes: graphData.value.nodes.filter(
(node) => node.id != data.id (node) => node.id != data.id
...@@ -264,7 +264,6 @@ export default { ...@@ -264,7 +264,6 @@ export default {
}) })
} }
const handleSearch = (key) => { const handleSearch = (key) => {
console.log(key)
if (!key) { if (!key) {
fetchSubjects() fetchSubjects()
return return
...@@ -276,9 +275,6 @@ export default { ...@@ -276,9 +275,6 @@ export default {
}) })
.then((res) => { .then((res) => {
graphData.value = res.data.content graphData.value = res.data.content
systemOptions.value = graphData.value.nodes
.filter((node) => node.nodeLabel === 'System')
.map((node) => ({ label: node.systemName, value: node.nodeId }))
}) })
} }
return { return {
...@@ -317,13 +313,13 @@ export default { ...@@ -317,13 +313,13 @@ export default {
trigger: ['input', 'blur'], trigger: ['input', 'blur'],
}, },
], ],
// systemId: [ systemId: [
// { {
// required: true, required: true,
// message: '请选择所属系统', message: '请选择所属系统',
// trigger: ['input', 'blur'], trigger: ['input', 'blur'],
// }, },
// ], ],
}, },
curNode, curNode,
handleDelete, handleDelete,
......
...@@ -79,7 +79,7 @@ const defaultConfig = { ...@@ -79,7 +79,7 @@ const defaultConfig = {
isScale: true, // 是否启用缩放平移zoom功能 isScale: true, // 是否启用缩放平移zoom功能
scaleExtent: [0.2, 1.5], // 缩放的比例尺 scaleExtent: [0.2, 1.5], // 缩放的比例尺
chargeStrength: -100, // 万有引力 chargeStrength: -100, // 万有引力
collide: 100, // 碰撞力的大小 (节点之间的间距) collide: 90, // 碰撞力的大小 (节点之间的间距)
alphaDecay: 0.0228, // 控制力学模拟衰减率 alphaDecay: 0.0228, // 控制力学模拟衰减率
r: 45, // 圈圈的半径 [30 - 45] r: 45, // 圈圈的半径 [30 - 45]
nodeColor: 'skyblue', // 圈圈节点背景颜色 nodeColor: 'skyblue', // 圈圈节点背景颜色
...@@ -116,24 +116,11 @@ export default class RelationGraph { ...@@ -116,24 +116,11 @@ export default class RelationGraph {
// 画布 // 画布
this.graph = d3.select(selector) this.graph = d3.select(selector)
// 转换links的source和target
const nodes = [...data.nodes]
const links = [...data.links]
links.forEach((link) => {
nodes.forEach((node) => {
if (node.id === link.source) {
link.source = node
} else if (node.id === link.target) {
link.target = node
}
})
})
// 合并配置 // 合并配置
this.config = Object.assign( this.config = Object.assign(
{}, {},
defaultConfig, defaultConfig,
{ links, nodes }, this.transformData(data),
{ {
width: mapW, width: mapW,
height: mapH, height: mapH,
...@@ -145,21 +132,11 @@ export default class RelationGraph { ...@@ -145,21 +132,11 @@ export default class RelationGraph {
this.dependsNode = [] this.dependsNode = []
this.dependsLinkAndText = [] this.dependsLinkAndText = []
// console.log('init', this.config)
this.createSimulation()
this.init() this.init()
} }
setKey(key) { // 转换links的source和target
d3.selectAll('.node-text') transformData(data) {
.text((d) => (key && d[key]) || '无')
// .text((d) => (key && d[key]) || d[d.nodeLabel.toLowerCase() + 'Name'])
.call(textWrap, this.config.r * 1.8)
}
update(data) {
const self = this
const t = d3.transition().duration(750)
// 转换links的source和target
const nodes = [...data.nodes] const nodes = [...data.nodes]
const links = [...data.links] const links = [...data.links]
links.forEach((link) => { links.forEach((link) => {
...@@ -171,14 +148,20 @@ export default class RelationGraph { ...@@ -171,14 +148,20 @@ export default class RelationGraph {
} }
}) })
}) })
this.config.nodes = [...nodes] return { links, nodes }
this.config.links = [...links] }
console.log('update', this.config)
// d3.selectAll('pattern.circle-bg').data(this.config.nodes) setKey(key) {
// d3.selectAll('g.circle-wrapper').data(this.config.nodes) d3.selectAll('.node-text')
// d3.selectAll('g.edge').data(this.config.links) .text((d) => (key && d[key]) || '无')
// .text((d) => (key && d[key]) || d[d.nodeLabel.toLowerCase() + 'Name'])
.call(textWrap, this.config.r * 1.8)
}
this.patterns.remove() initPatterns() {
this.patterns && this.patterns.remove()
// 3.2 添加多个圈圈图片的 <pattern>
this.patterns = this.defs this.patterns = this.defs
.selectAll('pattern.circle-bg') .selectAll('pattern.circle-bg')
.data(this.config.nodes) .data(this.config.nodes)
...@@ -210,17 +193,19 @@ export default class RelationGraph { ...@@ -210,17 +193,19 @@ export default class RelationGraph {
.attr('fill', this.config.fontColor) .attr('fill', this.config.fontColor)
.style('font-size', this.config.r / 3.8) .style('font-size', this.config.r / 3.8)
.text((d) => d[d.nodeLabel.toLowerCase() + 'Name']) .text((d) => d[d.nodeLabel.toLowerCase() + 'Name'])
}
initLinks() {
const self = this
this.edges && this.edges.remove()
// 5.关系图添加线 // 5.关系图添加线
// 5.1 每条线是个容器,有线 和一个装文字的容器 // 5.1 每条线是个容器,有线 和一个装文字的容器
this.edges.remove()
this.edges = this.relMap_g this.edges = this.relMap_g
.selectAll('g.edge') .selectAll('g.edge')
.data(this.config.links) .data(this.config.links)
.enter() .enter()
.append('g') .append('g')
.attr('class', 'edge') .attr('class', 'edge')
.merge(this.edges)
.on('mouseover', function (e, d) { .on('mouseover', function (e, d) {
if (self.config.isHighLight) { if (self.config.isHighLight) {
self.highlightLinks(d) self.highlightLinks(d)
...@@ -272,16 +257,20 @@ export default class RelationGraph { ...@@ -272,16 +257,20 @@ export default class RelationGraph {
.attr('text-anchor', 'middle') // <text>文本中轴对齐方式居中 start | middle | end .attr('text-anchor', 'middle') // <text>文本中轴对齐方式居中 start | middle | end
.style('font-size', 12) .style('font-size', 12)
.text((d) => d.name) .text((d) => d.name)
}
initCircles() {
const self = this
this.circlesWrapper && this.circlesWrapper.remove()
// 给圈圈节点添加g便于后面添加menu // 给圈圈节点添加g便于后面添加menu
this.circlesWrapper.remove()
this.circlesWrapper = this.relMap_g this.circlesWrapper = this.relMap_g
.selectAll('g.circle-wrapper') .selectAll('g.circle-wrapper')
.data(this.config.nodes) .data(this.config.nodes)
.enter() .enter()
.append('g') .append('g')
.attr('class', 'circle-wrapper') .attr('class', 'circle-wrapper')
.merge(this.circlesWrapper) // .merge(this.circlesWrapper)
this.circlesWrapper.exit().remove() this.circlesWrapper.exit().remove()
// 6.关系图添加用于显示圈圈的节点 // 6.关系图添加用于显示圈圈的节点
...@@ -303,11 +292,6 @@ export default class RelationGraph { ...@@ -303,11 +292,6 @@ export default class RelationGraph {
if (self.config.isHighLight) { if (self.config.isHighLight) {
self.highlightObject(d) self.highlightObject(d)
} }
// tooltip
// .html(d.name)
// .style('left', e.pageX + 10 + 'px')
// .style('top', e.pageY + 10 + 'px')
// .style('opacity', 1)
}) })
.on('mouseout', function (e, d) { .on('mouseout', function (e, d) {
d3.select(this).attr('stroke-width', self.config.strokeWidth) d3.select(this).attr('stroke-width', self.config.strokeWidth)
...@@ -315,7 +299,6 @@ export default class RelationGraph { ...@@ -315,7 +299,6 @@ export default class RelationGraph {
if (self.config.isHighLight) { if (self.config.isHighLight) {
self.highlightObject(null) self.highlightObject(null)
} }
// tooltip.style('opacity', 0)
}) })
.on('click', function (e, d) { .on('click', function (e, d) {
if (menu && menu.curNodeData == d) { if (menu && menu.curNodeData == d) {
...@@ -325,7 +308,7 @@ export default class RelationGraph { ...@@ -325,7 +308,7 @@ export default class RelationGraph {
if (d.nodeLabel === 'Subject') { if (d.nodeLabel === 'Subject') {
self.openMenu(this, d) self.openMenu(this, d)
} else { } else {
self.openMenu(this, d, 'del') self.openMenu(this, d, ['del', 'branch'])
} }
} }
//阻止事件冒泡 阻止事件默认行为 //阻止事件冒泡 阻止事件默认行为
...@@ -368,9 +351,13 @@ export default class RelationGraph { ...@@ -368,9 +351,13 @@ export default class RelationGraph {
// 文字折行 // 文字折行
this.SVG.selectAll('text.node-text').call(textWrap, this.config.r * 1.8) this.SVG.selectAll('text.node-text').call(textWrap, this.config.r * 1.8)
}
this.simulation // 创建力学模拟器
.nodes(this.config.nodes) initSimulation() {
// 1. 创建一个力学模拟器
this.simulation = d3
.forceSimulation(this.config.nodes)
// simulation.force(name,[force])函数,添加某种力 // simulation.force(name,[force])函数,添加某种力
.force('link', d3.forceLink(this.config.links)) .force('link', d3.forceLink(this.config.links))
// 万有引力 // 万有引力
...@@ -391,25 +378,58 @@ export default class RelationGraph { ...@@ -391,25 +378,58 @@ export default class RelationGraph {
.alphaDecay(this.config.alphaDecay) .alphaDecay(this.config.alphaDecay)
// 监听事件 ,tick|end ,例如监听 tick 滴答事件 // 监听事件 ,tick|end ,例如监听 tick 滴答事件
.on('tick', () => this.ticked()) .on('tick', () => this.ticked())
}
this.simulation.alphaTarget(0.3).restart() update(data) {
const { nodes, links } = this.transformData(data)
this.config.nodes = [...nodes]
this.config.links = [...links]
console.log('update', this.config)
this.initPatterns()
this.initLinks()
this.initCircles()
// this.initSimulation()
// // 1. 创建一个力学模拟器
this.simulation
.nodes(this.config.nodes)
.force('link', d3.forceLink(this.config.links))
// .force('charge', d3.forceManyBody().strength(this.config.chargeStrength))
// .force(
// 'center',
// d3.forceCenter(this.config.width / 2, this.config.height / 2)
// )
// // .force(
// // 'collide',
// // d3.forceCollide(this.config.collide).strength(0.2).iterations(5)
// // )
// // .alphaDecay(this.config.alphaDecay)
// // .on('tick', () => this.ticked())
this.simulation.alphaTarget(0.1).restart()
const timer = setTimeout(() => { const timer = setTimeout(() => {
this.simulation.alphaTarget(0) this.simulation.alphaTarget(0)
clearTimeout(timer) clearTimeout(timer)
}, 500) }, 500)
} }
openMenu(self, d, type) {
openMenu(self, d, types) {
this.setCurNode(d) this.setCurNode(d)
menu = new RadialMenu().radius(50).thickness(40).appendTo(self.parentNode) menu = new RadialMenu().radius(50).thickness(40).appendTo(self.parentNode)
if (!type) { if (!types) {
menu.show(this.menuData, d) menu.show(this.menuData, d)
} else { } else {
menu.show( menu.show(
this.menuData.filter((e) => e.key === type), this.menuData.filter((e) => types.indexOf(e.key) >= 0),
d d
) )
} }
} }
closeMenu() { closeMenu() {
if (menu) { if (menu) {
menu.hide() menu.hide()
...@@ -417,38 +437,7 @@ export default class RelationGraph { ...@@ -417,38 +437,7 @@ export default class RelationGraph {
} }
} }
// 创建力学模拟器
createSimulation() {
// 1. 创建一个力学模拟器
this.simulation = d3
.forceSimulation(this.config.nodes)
// simulation.force(name,[force])函数,添加某种力
.force('link', d3.forceLink(this.config.links))
// 万有引力
.force('charge', d3.forceManyBody().strength(this.config.chargeStrength))
// d3.forceCenter()用指定的x坐标和y坐标创建一个新的居中力。
.force(
'center',
d3.forceCenter(this.config.width / 2, this.config.height / 2)
)
// 碰撞作用力,为节点指定一个radius区域来防止节点重叠,设置碰撞力的强度,范围[0,1], 默认为0.7。
// 设置迭代次数,默认为1,迭代次数越多最终的布局效果越好,但是计算复杂度更高
.force(
'collide',
d3.forceCollide(this.config.collide).strength(0.2).iterations(5)
)
// 在计时器的每一帧中,仿真的alpha系数会不断削减,当alpha到达一个系数时,仿真将会停止,也就是alpha的目标系数alphaTarget,该值区间为[0,1]. 默认为0,
// 控制力学模拟衰减率,[0-1] ,设为0则不停止 , 默认0.0228,直到0.001
.alphaDecay(this.config.alphaDecay)
// 监听事件 ,tick|end ,例如监听 tick 滴答事件
.on('tick', () => this.ticked())
}
init() { init() {
const self = this
// this.createSimulation()
// 2.创建svg标签 // 2.创建svg标签
this.SVG = this.graph this.SVG = this.graph
.append('svg') .append('svg')
...@@ -486,202 +475,25 @@ export default class RelationGraph { ...@@ -486,202 +475,25 @@ export default class RelationGraph {
.attr('d', 'M 0 0 8 4 0 8Z') //箭头的路径 从 (0,0) 到 (8,4) 到(0,8) .attr('d', 'M 0 0 8 4 0 8Z') //箭头的路径 从 (0,0) 到 (8,4) 到(0,8)
.attr('fill', this.config.linkColor) .attr('fill', this.config.linkColor)
// 3.2 添加多个圈圈图片的 <pattern> this.initSimulation()
this.patterns = this.defs
.selectAll('pattern.circle-bg')
.data(this.config.nodes)
.enter()
.append('pattern')
.attr('class', 'circle-bg')
.attr('id', (d) => d.id)
.attr('width', '1')
.attr('height', '1')
this.patterns
.append('rect')
.attr('width', 2 * this.config.r)
.attr('height', 2 * this.config.r)
.attr(
'fill',
(d) =>
(d && this.config.colorList[d.nodeLabel || 'default']) ||
this.config.nodeColor
)
this.patterns this.initPatterns()
.append('text')
.attr('class', 'node-text')
.attr('x', this.config.r)
.attr('y', this.config.r) // edit
.attr('text-anchor', 'middle')
.attr('dominant-baseline', 'middle')
.attr('fill', this.config.fontColor)
.style('font-size', this.config.r / 3.8)
.text((d) => d[d.nodeLabel.toLowerCase() + 'Name'])
// 4.放关系图的容器 // 4.放关系图的容器
this.relMap_g = this.SVG.append('g') this.relMap_g = this.SVG.append('g')
.attr('class', 'relMap_g') .attr('class', 'relMap_g')
.attr('width', this.config.width) .attr('width', this.config.width)
.attr('height', this.config.height) .attr('height', this.config.height)
// 5.关系图添加线
// 5.1 每条线是个容器,有线 和一个装文字的容器
this.edges = this.relMap_g
.selectAll('g.edge')
.data(this.config.links)
.enter()
.append('g')
.attr('class', 'edge')
.on('mouseover', function (e, d) {
if (self.config.isHighLight) {
self.highlightLinks(d)
}
d3.select(this).selectAll('path.links').attr('stroke-width', 2)
d3.select(this).selectAll('.rect_g text').style('font-weight', 'bold')
})
.on('mouseout', function () {
if (self.config.isHighLight) {
self.highlightLinks(null)
}
d3.select(this).selectAll('path.links').attr('stroke-width', 1)
d3.select(this).selectAll('.rect_g text').style('font-weight', 'normal')
})
.on('click', function (e, d) {
console.log('线click')
})
.attr('fill', (d) => d.color || this.config.linkColor)
// 5.2 添加线
this.links = this.edges
.append('path')
.attr('class', 'links')
.attr(
'd',
(d) => `M${this.config.linkSrc},0 L${getDis(d.source, d.target)},0`
)
.style('marker-end', 'url(#marker)')
.attr('stroke', (d) => d.color || this.config.linkColor)
// 5.3 添加关系文字的容器
this.rect_g = this.edges.append('g').attr('class', 'rect_g')
// 5.4 添加rect
this.rects = this.rect_g
.append('rect')
.attr('x', 40)
.attr('y', -10)
.attr('width', 50)
.attr('height', 20)
.attr('fill', '#f9fbfd')
.attr('stroke', 'transparent')
// 5.5 文本标签 坐标(x,y)代表 文本的左下角的点 this.initLinks()
this.texts = this.rect_g
.append('text')
.attr('x', 40)
.attr('y', 5)
.attr('text-anchor', 'middle') // <text>文本中轴对齐方式居中 start | middle | end
.style('font-size', 12)
.text((d) => d.name)
const tooltip = d3 // const tooltip = d3
.select('body') // .select('body')
.append('div') // .append('div')
.attr('class', 'relation-tooltip') // .attr('class', 'relation-tooltip')
.style('opacity', 0) // .style('opacity', 0)
// 给圈圈节点添加g便于后面添加menu this.initCircles()
this.circlesWrapper = this.relMap_g
.selectAll('g.circle-wrapper')
.data(this.config.nodes)
.enter()
.append('g')
.attr('class', 'circle-wrapper')
// 6.关系图添加用于显示圈圈的节点
this.circles = this.circlesWrapper
.append('circle')
.attr('r', this.config.r)
.attr('class', 'node')
.style('cursor', 'pointer')
.attr('fill', (d) => `url(#${d.id})`)
.attr('stroke', this.config.strokeColor)
.attr('stroke-width', this.config.strokeWidth)
.on('mouseover', function (e, d) {
d3.select(this).attr(
'stroke-width',
self.config.strokeWidth == 0 ? 3 : 1.5 * self.config.strokeWidth
)
d3.select(this).attr('stroke', 'rgba(0,0,0,0.1)')
// d3.select(this).attr('stroke', d.strokeColor || self.config.strokeColor)
if (self.config.isHighLight) {
self.highlightObject(d)
}
// tooltip
// .html(d.name)
// .style('left', e.pageX + 10 + 'px')
// .style('top', e.pageY + 10 + 'px')
// .style('opacity', 1)
})
.on('mouseout', function (e, d) {
d3.select(this).attr('stroke-width', self.config.strokeWidth)
d3.select(this).attr('stroke', d.strokeColor || self.config.strokeColor)
if (self.config.isHighLight) {
self.highlightObject(null)
}
// tooltip.style('opacity', 0)
})
.on('click', function (e, d) {
if (menu && menu.curNodeData == d) {
self.closeMenu()
} else {
self.closeMenu()
if (d.nodeLabel === 'Subject') {
self.openMenu(this, d)
} else {
self.openMenu(this, d, 'del')
}
}
//阻止事件冒泡 阻止事件默认行为
e.stopPropagation ? e.stopPropagation() : (e.cancelBubble = true)
e.preventDefault ? e.preventDefault() : (e.returnValue = false)
})
.on('contextmenu', function (e) {
// 取消鼠标右键菜单默认行为
e.cancelBubble = true
e.returnValue = false
})
// 应用 自定义的 拖拽事件
.call(
d3
.drag()
.on('start', (e, d) => {
e.sourceEvent.stopPropagation()
// restart()方法重新启动模拟器的内部计时器并返回模拟器。
// 与simulation.alphaTarget或simulation.alpha一起使用时,此方法可用于在交互
// 过程中进行“重新加热”模拟,例如在拖动节点时,在simulation.stop暂停之后恢复模拟。
// 当前alpha值为0,需设置alphaTarget让节点动起来
if (!e.active) this.simulation.alphaTarget(0.3).restart()
d.fx = d.x
d.fy = d.y
})
.on('drag', (e, d) => {
// d.fx属性- 节点的固定x位置
// 在每次tick结束时,d.x被重置为d.fx ,并将节点 d.vx设置为零
// 要取消节点,请将节点 .fx和节点 .fy设置为空,或删除这些属性。
d.fx = e.x
d.fy = e.y
})
.on('end', (e, d) => {
// 让alpha目标值值恢复为默认值0,停止力模型
if (!e.active) this.simulation.alphaTarget(0)
d.fx = null
d.fy = null
})
)
// 文字折行
this.SVG.selectAll('text.node-text').call(textWrap, this.config.r * 1.8)
} }
ticked() { ticked() {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment