Commit 6ddd03b6 authored by 郭铭瑶's avatar 郭铭瑶 🤘

主体客体区分形状&编辑节点&添加标签

parent a5189eeb
...@@ -8,14 +8,16 @@ switch (process.env.NODE_ENV) { ...@@ -8,14 +8,16 @@ switch (process.env.NODE_ENV) {
export default { export default {
BASE_URL, BASE_URL,
GET_SUBJECTS: '/subjects/v2', GET_SUBJECTS: '/subjects/v2',
PUT_SUBJECT: '/subject/{id}',
GET_RELATIONS: '/relations', GET_RELATIONS: '/relations',
POST_SUBJECT: '/subject', POST_SUBJECT: '/subject',
POST_NODE: '/node/relation/v2', POST_NODE: '/node/relation/v2',
DELETE_RELATION: '/node/relation/{id}', DELETE_RELATION: '/node/relation/{id}',
GET_NODES: '/node/relations', GET_NODES: '/node/relations',
DELETE_NODE: '/node/{id}', DELETE_NODE: '/node/{id}',
PUT_NODE: '/node/{nodeType}/{id}',
GET_SYSTEMS: '/systems', GET_SYSTEMS: '/systems',
POST_LINK_SUBJECTS: '/node/relations', POST_LINK_SUBJECTS: '/node/relations',
GET_TAGS: '/label/classification', GET_TAG_CLASS: '/label/classification',
GET_TAGS: '/labels',
POST_TAGS: '/node/labels',
} }
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1629870391503" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2057" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M729.483636 995.607273H218.996364A127.301818 127.301818 0 0 1 91.810909 868.421818V233.658182A127.301818 127.301818 0 0 1 218.996364 106.472727h382.952727a29.090909 29.090909 0 1 1 0 58.181818H218.996364a69.003636 69.003636 0 0 0-69.003637 69.003637v634.763636a69.003636 69.003636 0 0 0 69.003637 69.003637h510.487272a69.003636 69.003636 0 0 0 69.003637-69.003637v-400.290909a29.090909 29.090909 0 0 1 58.181818 0v400.290909a127.301818 127.301818 0 0 1-127.185455 127.185455z" fill="#ffffff" p-id="2058"></path><path d="M514.792727 569.250909a29.090909 29.090909 0 0 1-28.974545-31.883636C488.727273 506.298182 500.363636 402.269091 529.105455 368.290909l254.836363-303.709091a99.607273 99.607273 0 1 1 152.552727 128l-254.836363 303.709091c-28.509091 33.978182-129.047273 63.767273-159.185455 72.029091a28.974545 28.974545 0 0 1-7.68 0.930909zM860.16 87.156364a41.309091 41.309091 0 0 0-31.650909 14.778181l-254.836364 303.709091c-8.494545 10.123636-18.036364 50.734545-24.552727 93.090909 41.192727-13.847273 79.476364-30.254545 87.970909-40.378181l254.836364-303.709091a41.309091 41.309091 0 0 0-31.650909-67.956364z" fill="#ffffff" p-id="2059"></path></svg>
\ No newline at end of file
...@@ -40,6 +40,7 @@ import branch from '@/assets/images/branch.svg' ...@@ -40,6 +40,7 @@ import branch from '@/assets/images/branch.svg'
import link from '@/assets/images/link.svg' import link from '@/assets/images/link.svg'
import add from '@/assets/images/add.svg' import add from '@/assets/images/add.svg'
import del from '@/assets/images/delete.svg' import del from '@/assets/images/delete.svg'
import edit from '@/assets/images/edit.svg'
export default { export default {
name: 'D3', name: 'D3',
props: { props: {
...@@ -54,7 +55,7 @@ export default { ...@@ -54,7 +55,7 @@ export default {
}, },
}, },
}, },
emits: ['link', 'add', 'del', 'branch', 'curNode', 'del-link'], emits: ['link', 'add', 'del', 'edit', 'branch', 'curNode', 'del-link'],
setup(props, ctx) { setup(props, ctx) {
const colorList = { const colorList = {
default: 'skyblue', default: 'skyblue',
...@@ -76,6 +77,12 @@ export default { ...@@ -76,6 +77,12 @@ export default {
// action: (d) => ctx.emit('branch', d), // action: (d) => ctx.emit('branch', d),
// title: '分支', // title: '分支',
// }, // },
{
key: 'edit',
icon: edit,
action: (d) => ctx.emit('edit', d),
title: '编辑',
},
{ {
key: 'link', key: 'link',
icon: link, icon: link,
...@@ -96,27 +103,49 @@ export default { ...@@ -96,27 +103,49 @@ export default {
}, },
] ]
const curTagKey = ref(null) const curTagKey = ref(null)
const setCurNode = (type) => { const setCurNode = (node) => {
if (type.nodeLabel !== 'Subject') { if (node.nodeLabel !== 'Subject') {
curTagKey.value = null curTagKey.value = null
} }
ctx.emit('curNode', type) ctx.emit('curNode', node)
} }
const handleLinkClick = (d) => { const handleLinkClick = (d) => {
if (d.target.nodeLabel === 'System') { const { nodeLabel } = d.target
if (nodeLabel === 'System' || nodeLabel === 'Subject') {
ctx.emit('del-link', d) ctx.emit('del-link', d)
} }
} }
const showBranch = (d) => { const showBranch = (d) => {
ctx.emit('branch', d) ctx.emit('branch', d)
} }
function init() { function init() {
if (!container.value) return if (!container.value) return
const data = { const data = transData(props.data)
links: props.data.links,
nodes: props.data.nodes.map((node) => { if (instance) {
updateGraph(props.data)
} else {
instance = new RelationGraph(
container.value,
data,
props.config,
menuData,
colorList,
setCurNode,
handleLinkClick,
showBranch
)
}
setLegend(data)
}
function transData(data) {
return {
links: data.links,
nodes: data.nodes.map((node) => {
let key = node.nodeLabel let key = node.nodeLabel
if (key === 'Property') { if (key === 'Property') {
const relation = props.data.links.find((link) => { const relation = props.data.links.find((link) => {
...@@ -134,21 +163,10 @@ export default { ...@@ -134,21 +163,10 @@ export default {
return node return node
}), }),
} }
if (instance) {
instance.update(data)
} else {
instance = new RelationGraph(
container.value,
data,
props.config,
menuData,
colorList,
setCurNode,
handleLinkClick,
showBranch
)
} }
setLegend(data)
function updateGraph(data) {
instance.update(transData(data))
} }
function setKey(key) { function setKey(key) {
...@@ -269,6 +287,7 @@ export default { ...@@ -269,6 +287,7 @@ export default {
colorList, colorList,
container, container,
setKey, setKey,
updateGraph,
nodeList, nodeList,
linkList, linkList,
defaultColor: { defaultColor: {
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
@add="openAddDrawer" @add="openAddDrawer"
@curNode="curNode = $event" @curNode="curNode = $event"
@del="deleteNode" @del="deleteNode"
@edit="openEditDrawer"
@link="openLinkDrawer" @link="openLinkDrawer"
@del-link="deleteLink" @del-link="deleteLink"
/> />
...@@ -50,7 +51,30 @@ ...@@ -50,7 +51,30 @@
:options="relationOptions" :options="relationOptions"
/> />
</n-form-item> </n-form-item>
<n-form-item label="所属系统" path="systemId"> <template v-if="getTagName(data.relationId) === '标签'">
<n-form-item label="标签分类" path="classification">
<n-select
v-model:value="data.classification"
placeholder="请选择"
:options="tagClassOptions"
/>
</n-form-item>
<n-form-item label="标签名称" path="tag">
<n-select
v-model:value="data.tag"
placeholder="请选择"
filterable
tag
:options="tagOptions"
/>
</n-form-item>
<n-form-item path="isPositive" :show-label="false">
<n-checkbox v-model:checked="data.isPositive"
>是否为积极的标签</n-checkbox
>
</n-form-item>
</template>
<n-form-item v-else label="所属系统" path="systemId">
<n-select <n-select
v-model:value="data.systemIds" v-model:value="data.systemIds"
placeholder="请选择" placeholder="请选择"
...@@ -141,7 +165,7 @@ ...@@ -141,7 +165,7 @@
</n-drawer-content> </n-drawer-content>
</n-drawer> </n-drawer>
<n-drawer v-model:show="showLinkDrawer" :width="340" placement="left"> <n-drawer v-model:show="showLinkDrawer" :width="340" placement="left">
<n-drawer-content title="连接要素" closable> <n-drawer-content title="关联要素" closable>
<n-form ref="linkFormRef" :model="linkData" :rules="rules" size="small"> <n-form ref="linkFormRef" :model="linkData" :rules="rules" size="small">
<n-form-item path="sourceNodeId" label="源要素"> <n-form-item path="sourceNodeId" label="源要素">
<n-select <n-select
...@@ -171,6 +195,50 @@ ...@@ -171,6 +195,50 @@
</n-form> </n-form>
</n-drawer-content> </n-drawer-content>
</n-drawer> </n-drawer>
<n-drawer v-model:show="showEditDrawer" :width="340" placement="left">
<n-drawer-content title="编辑节点" closable>
<n-form ref="editFormRef" :model="editData" :rules="rules" size="small">
<template v-if="curNode.nodeLabel === 'Subject'">
<n-form-item path="subjectName" label="名称">
<n-input
v-model:value="editData.subjectName"
placeholder="请输入"
@keydown.enter.prevent
/>
</n-form-item>
<n-form-item path="subjectType" label="类型">
<n-select
v-model:value="editData.subjectType"
placeholder="请选择"
:options="[
{ label: '主体', value: '1' },
{ label: '客体', value: '2' },
]"
/>
</n-form-item>
</template>
<template v-else-if="curNode.nodeLabel === 'Property'">
<n-form-item label="节点名称" path="propertyName">
<n-input
v-model:value="editData.propertyName"
placeholder="请输入"
@keydown.enter.prevent
/>
</n-form-item>
</template>
<n-space justify="end">
<n-button
:loading="isLoading"
size="small"
type="primary"
@click="editNode"
>
提交
</n-button>
</n-space>
</n-form>
</n-drawer-content>
</n-drawer>
</template> </template>
<script setup> <script setup>
...@@ -219,13 +287,30 @@ function fetchSubjects() { ...@@ -219,13 +287,30 @@ function fetchSubjects() {
} }
fetchSubjects() fetchSubjects()
const tagClassOptions = ref([])
const tagOptions = ref([])
function fetchTags() { function fetchTags() {
ajax
.get({
url: api.GET_TAG_CLASS,
})
.then((res) => {
const data = res.data.content
tagClassOptions.value = data.map((e) => ({
label: e.classification,
value: e.classification,
}))
})
ajax ajax
.get({ .get({
url: api.GET_TAGS, url: api.GET_TAGS,
}) })
.then((res) => { .then((res) => {
console.log('tags', res.data.content) const data = (res.data && res.data.content) || []
tagOptions.value = data.map((e) => ({
label: e.labelName,
value: e.id,
}))
}) })
} }
fetchTags() fetchTags()
...@@ -302,31 +387,80 @@ function setLabelKey(key) { ...@@ -302,31 +387,80 @@ function setLabelKey(key) {
d3Ref.value.setKey(key) d3Ref.value.setKey(key)
} }
function getTagName(relationId) {
if (!relationId) return null
const tag = relationOptions.value.find((e) => e.value === relationId)
return tag && tag.label
}
function submitNewNode(e) { function submitNewNode(e) {
e.preventDefault() e.preventDefault()
formRef.value.validate((errors) => { formRef.value.validate(async (errors) => {
if (!errors) { if (!errors) {
isLoading.value = true isLoading.value = true
const { nodeId, subjectName, subjectType } = curSubject.value const { nodeId, subjectName, subjectType } = curSubject.value
ajax const tagData = []
.post({ const otherData = []
formData.value.forEach((item) => {
if (getTagName(item.relationId) === '标签') {
tagData.push(item)
} else {
otherData.push(item)
}
})
if (otherData.length > 0) {
await ajax.post({
url: api.POST_NODE, url: api.POST_NODE,
params: { params: {
subjectId: nodeId, subjectId: nodeId,
subjectName, subjectName,
subjectType, subjectType,
propertyList: formData.value, propertyList: otherData.map((item) => ({
propertyName: item.propertyName,
systemIds: item.systemIds,
relationId: item.relationId,
})),
}, },
}) })
.then(() => { }
if (tagData.length > 0) {
await ajax.post({
url: api.POST_TAGS,
params: {
subjectId: nodeId,
subjectName,
subjectType,
relationId: tagData[0].relationId,
labelList: tagData.map((item) => {
let obj = {
id: null,
labelName: item.tag,
}
if (tagOptions.value.findIndex((e) => e.value == item.tag) >= 0) {
obj = {
id: item.tag,
labelName: null,
}
}
return {
classification: item.classification,
isPositive: item.isPositive,
...obj,
}
}),
},
})
}
fetchSubordinates({ fetchSubordinates({
nodeId: nodeId, nodeId,
nodeLabel: 'Subject', nodeLabel: 'Subject',
}) })
isLoading.value = false isLoading.value = false
showDrawer.value = false showDrawer.value = false
message.success('提交成功') message.success('新增成功')
})
} }
}) })
} }
...@@ -366,9 +500,12 @@ function deleteNode(data) { ...@@ -366,9 +500,12 @@ function deleteNode(data) {
function deleteLink(data) { function deleteLink(data) {
const { source, target } = data const { source, target } = data
const name = `'${source.propertyName || target.subjectName}_${
target.systemName || source.subjectName
}'`
dialog.error({ dialog.error({
title: '删除关联', title: '删除关联',
content: `确定是否删除 '${source.propertyName}_${target.systemName}' 关联关系?`, content: `确定是否删除 ${name} 关联关系?`,
positiveText: '确定', positiveText: '确定',
negativeText: '取消', negativeText: '取消',
maskClosable: false, maskClosable: false,
...@@ -410,7 +547,7 @@ const addSubject = (e) => { ...@@ -410,7 +547,7 @@ const addSubject = (e) => {
fetchSubjects() fetchSubjects()
isLoading.value = false isLoading.value = false
showSubjectDrawer.value = false showSubjectDrawer.value = false
message.success('提交成功') message.success('新增成功')
}) })
} }
}) })
...@@ -467,7 +604,7 @@ const linkSubject = (e) => { ...@@ -467,7 +604,7 @@ const linkSubject = (e) => {
}) })
isLoading.value = false isLoading.value = false
showLinkDrawer.value = false showLinkDrawer.value = false
message.success('提交成功') message.success('关联成功')
}) })
} }
}) })
...@@ -497,7 +634,21 @@ const rules = { ...@@ -497,7 +634,21 @@ const rules = {
relationId: [ relationId: [
{ {
required: true, required: true,
message: '请选择从属类型', message: '请选择从属关系',
trigger: ['change', 'blur'],
},
],
classification: [
{
required: true,
message: '请选择标签分类',
trigger: ['change', 'blur'],
},
],
tag: [
{
required: true,
message: '请选择标签名称',
trigger: ['change', 'blur'], trigger: ['change', 'blur'],
}, },
], ],
...@@ -553,7 +704,7 @@ function openSubjectDrawer() { ...@@ -553,7 +704,7 @@ function openSubjectDrawer() {
function openLinkDrawer() { function openLinkDrawer() {
showLinkDrawer.value = true showLinkDrawer.value = true
linkData.value = { linkData.value = {
sourceNodeId: null, sourceNodeId: curSubject.value.nodeId,
targetNodeId: null, targetNodeId: null,
} }
} }
...@@ -567,6 +718,55 @@ function addNewItem() { ...@@ -567,6 +718,55 @@ function addNewItem() {
function deleteItem(i) { function deleteItem(i) {
formData.value.splice(i, 1) formData.value.splice(i, 1)
} }
const editData = ref(null)
const showEditDrawer = ref(false)
const editFormRef = ref(null)
function openEditDrawer(d) {
editData.value = d
showEditDrawer.value = true
}
function editNode(e) {
e.preventDefault()
editFormRef.value.validate((errors) => {
if (!errors) {
isLoading.value = true
const { nodeLabel, nodeId } = curNode.value
let params = {}
if (nodeLabel === 'Subject') {
params = {
subjectName: editData.value.subjectName,
subjectType: editData.value.subjectType,
}
} else if (nodeLabel === 'Property') {
params = {
propertyName: editData.value.propertyName,
}
}
ajax
.put({
url: api.PUT_NODE.replace('{nodeType}', nodeLabel).replace(
'{id}',
nodeId
),
params,
})
.then(() => {
graphData.value.nodes.forEach((node) => {
if (node.nodeId === nodeId) {
for (const key in params) {
node[key] = params[key]
}
}
})
d3Ref.value.updateGraph(graphData.value)
isLoading.value = false
showEditDrawer.value = false
message.success('编辑成功')
})
}
})
}
</script> </script>
<style lang="stylus" scoped> <style lang="stylus" scoped>
......
...@@ -11,6 +11,7 @@ import { ...@@ -11,6 +11,7 @@ import {
NForm, NForm,
NFormItem, NFormItem,
NInput, NInput,
NCheckbox,
NInputGroup, NInputGroup,
NSelect, NSelect,
NMessageProvider, NMessageProvider,
...@@ -29,6 +30,7 @@ const naive = create({ ...@@ -29,6 +30,7 @@ const naive = create({
NForm, NForm,
NFormItem, NFormItem,
NInput, NInput,
NCheckbox,
NInputGroup, NInputGroup,
NSelect, NSelect,
NMessageProvider, NMessageProvider,
......
...@@ -34,11 +34,11 @@ function textWrap(text, width) { ...@@ -34,11 +34,11 @@ function textWrap(text, width) {
text.each(function () { text.each(function () {
const text = d3.select(this), const text = d3.select(this),
words = text.text().split('').reverse(), words = text.text().split('').reverse(),
lineHeight = 1.2, // ems lineHeight = 1.1, // ems
dy = 0, dy = 0,
x = text.attr('x') x = text.attr('x')
let y = text.attr('y') let y = +text.attr('y') + 4
if (words.length > 4 && words.length <= 8) { if (words.length > 4 && words.length <= 8) {
y = +y - 4 y = +y - 4
...@@ -85,8 +85,8 @@ function polygonPoints(cx, cy, r, n) { ...@@ -85,8 +85,8 @@ function polygonPoints(cx, cy, r, n) {
let a = Math.PI / 2 + alpha / 2 let a = Math.PI / 2 + alpha / 2
let points = '' let points = ''
for (let i = 0; i < n; i++) { for (let i = 0; i < n; i++) {
x = cx + r * Math.cos(a) const x = cx + r * Math.cos(a)
y = cy + r * Math.sin(a) const y = cy + r * Math.sin(a)
points += x + ',' + y + ',' points += x + ',' + y + ','
a += alpha a += alpha
} }
...@@ -103,7 +103,7 @@ const defaultConfig = { ...@@ -103,7 +103,7 @@ const defaultConfig = {
chargeStrength: 300, // 万有引力 chargeStrength: 300, // 万有引力
collide: 80, // 碰撞力的大小 (节点之间的间距) collide: 80, // 碰撞力的大小 (节点之间的间距)
alphaDecay: 0.1, // 控制力学模拟衰减率 alphaDecay: 0.1, // 控制力学模拟衰减率
r: 36, // 圈圈的半径 [30 - 45] r: 40, // 圈圈的半径 [30 - 45]
nodeColor: 'skyblue', // 圈圈节点背景颜色 nodeColor: 'skyblue', // 圈圈节点背景颜色
fontColor: '#2c3e50', // 圈圈内文字的颜色 fontColor: '#2c3e50', // 圈圈内文字的颜色
linkSrc: 30, // 划线时候的弧度 linkSrc: 30, // 划线时候的弧度
...@@ -207,7 +207,7 @@ export default class RelationGraph { ...@@ -207,7 +207,7 @@ export default class RelationGraph {
d3.selectAll('.node-text') d3.selectAll('.node-text')
.text((d) => (key && d[key]) || '无') .text((d) => (key && d[key]) || '无')
// .text((d) => (key && d[key]) || d[d.nodeLabel.toLowerCase() + 'Name']) // .text((d) => (key && d[key]) || d[d.nodeLabel.toLowerCase() + 'Name'])
.call(textWrap, this.config.r * 1.8) .call(textWrap, this.config.r * 1.6)
} }
initPatterns() { initPatterns() {
...@@ -239,7 +239,7 @@ export default class RelationGraph { ...@@ -239,7 +239,7 @@ export default class RelationGraph {
.append('text') .append('text')
.attr('class', 'node-text') .attr('class', 'node-text')
.attr('x', this.config.r) .attr('x', this.config.r)
.attr('y', this.config.r) // edit .attr('y', this.config.r)
.attr('text-anchor', 'middle') .attr('text-anchor', 'middle')
.attr('dominant-baseline', 'middle') .attr('dominant-baseline', 'middle')
.attr('fill', '#000') .attr('fill', '#000')
...@@ -313,27 +313,72 @@ export default class RelationGraph { ...@@ -313,27 +313,72 @@ export default class RelationGraph {
.text((d) => d.name) .text((d) => d.name)
} }
initCircles(circleWrapper) { initCircles(wrappers) {
const self = this if (this.config.others.length === 0) return
if (wrappers) {
// 给圈圈节点添加g便于后面添加menu this.circleWrappers = wrappers
if (circleWrapper) {
this.circlesWrapper = circleWrapper
} else { } else {
this.circlesWrapper = this.relMap_g // 给节点添加g便于后面添加menu
this.circleWrappers = this.relMap_g
.selectAll('g.circle-wrapper') .selectAll('g.circle-wrapper')
.data(this.config.nodes) .data(this.config.others)
.enter() .enter()
.append('g') .append('g')
.attr('class', 'circle-wrapper') .attr('class', 'circle-wrapper node')
.raise() .raise()
} }
// 6.关系图添加用于显示圈圈的节点 // 6.关系图添加用于显示圈圈的节点
this.circles && this.circles.remove() this.circles && this.circles.remove()
this.circles = this.circlesWrapper this.circles = this.circleWrappers
.append('circle') .append('circle')
.attr('class', '.circle')
.attr('r', this.config.r) .attr('r', this.config.r)
this.addEvent(this.circles)
}
initSquares(wrappers) {
if (this.config.subjects.length === 0) return
if (wrappers) {
this.squareWrappers = wrappers
} else {
this.squareWrappers = this.relMap_g
.selectAll('g.square-wrapper')
.data(this.config.subjects)
.enter()
.append('g')
.attr('class', 'square-wrapper')
.raise()
}
this.squares && this.squares.remove()
this.squares = this.squareWrappers.append('polygon').attr('class', 'square')
this.addEvent(this.squares)
}
initPentagons(wrappers) {
if (this.config.objects.length === 0) return
if (wrappers) {
this.pentagonWrappers = wrappers
} else {
this.pentagonWrappers = this.relMap_g
.selectAll('g.pentagon-wrapper')
.data(this.config.objects)
.enter()
.append('g')
.attr('class', 'pentagon-wrapper')
.raise()
}
this.pentagons && this.pentagons.remove()
this.pentagons = this.pentagonWrappers
.append('polygon')
.attr('class', 'pentagons')
this.addEvent(this.pentagons)
}
addEvent(graph) {
if (!graph) return
const self = this
graph
.attr('class', 'node') .attr('class', 'node')
.style('cursor', 'pointer') .style('cursor', 'pointer')
.attr('fill', (d) => `url(#${d.id})`) .attr('fill', (d) => `url(#${d.id})`)
...@@ -374,8 +419,10 @@ export default class RelationGraph { ...@@ -374,8 +419,10 @@ export default class RelationGraph {
self.closeMenu(true) self.closeMenu(true)
if (d.nodeLabel === 'Subject') { if (d.nodeLabel === 'Subject') {
self.openMenu(this, d) self.openMenu(this, d)
} else if (d.nodeLabel === 'System') {
self.openMenu(this, d, ['del'])
} else { } else {
self.openMenu(this, d, ['del', 'branch']) self.openMenu(this, d, ['del', 'edit'])
} }
} }
}) })
...@@ -404,17 +451,20 @@ export default class RelationGraph { ...@@ -404,17 +451,20 @@ export default class RelationGraph {
// 要取消节点,请将节点 .fx和节点 .fy设置为空,或删除这些属性。 // 要取消节点,请将节点 .fx和节点 .fy设置为空,或删除这些属性。
d.fx = e.x d.fx = e.x
d.fy = e.y d.fy = e.y
d.x = e.x
d.y = e.y
}) })
.on('end', (e, d) => { .on('end', (e, d) => {
// 让alpha目标值值恢复为默认值0,停止力模型 // 让alpha目标值值恢复为默认值0,停止力模型
if (!e.active) this.simulation.alphaTarget(0) if (!e.active) this.simulation.alphaTarget(0)
d.fx = e.x d.fx = e.x
d.fy = e.y d.fy = e.y
d.x = e.x
d.y = e.y
}) })
) )
// 文字折行 // 文字折行
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.5)
} }
// 创建力学模拟器 // 创建力学模拟器
...@@ -445,26 +495,53 @@ export default class RelationGraph { ...@@ -445,26 +495,53 @@ export default class RelationGraph {
} }
update(data) { update(data) {
const { nodes, links } = this.transformData(data) const { links, nodes, subjects, objects, others } = this.transformData(data)
this.config.nodes = [...nodes] this.config.nodes = [...nodes]
this.config.links = [...links] this.config.links = [...links]
this.config.subjects = [...subjects]
this.config.objects = [...objects]
this.config.others = [...others]
console.log('update', this.config) console.log('update', this.config)
this.initPatterns() this.initPatterns()
this.initLinks() this.initLinks()
const updateNodes = this.relMap_g const newCircles = this.relMap_g.selectAll('g.circle-wrapper').data(others)
.selectAll('g.circle-wrapper') newCircles.exit().remove()
.data(this.config.nodes)
updateNodes.exit().remove()
this.initCircles( this.initCircles(
updateNodes newCircles
.enter() .enter()
.append('g') .append('g')
.attr('class', 'circle-wrapper') .attr('class', 'circle-wrapper')
.merge(updateNodes) .merge(newCircles)
.raise()
)
const newSquares = this.relMap_g
.selectAll('g.square-wrapper')
.data(subjects)
newSquares.exit().remove()
this.initSquares(
newSquares
.enter()
.append('g')
.attr('class', 'square-wrapper')
.merge(newSquares)
.raise()
)
const newPentagons = this.relMap_g
.selectAll('g.pentagon-wrapper')
.data(objects)
newPentagons.exit().remove()
this.initPentagons(
newPentagons
.enter()
.append('g')
.attr('class', 'pentagon-wrapper')
.merge(newPentagons)
.raise() .raise()
) )
...@@ -564,9 +641,12 @@ export default class RelationGraph { ...@@ -564,9 +641,12 @@ export default class RelationGraph {
this.initLinks() this.initLinks()
this.initCircles() this.initCircles()
this.initPentagons()
this.initSquares()
} }
ticked() { ticked() {
const { r } = this.config
// 7.1 修改每条容器edge的位置 // 7.1 修改每条容器edge的位置
this.edges.attr('transform', (d) => this.edges.attr('transform', (d) =>
getTransform(d.source, d.target, getDis(d.source, d.target)) getTransform(d.source, d.target, getDis(d.source, d.target))
...@@ -596,7 +676,6 @@ export default class RelationGraph { ...@@ -596,7 +676,6 @@ export default class RelationGraph {
return 'rotate(0)' return 'rotate(0)'
} }
}) })
// 7.4 修改线中装文本矩形rect的位置 // 7.4 修改线中装文本矩形rect的位置
this.rects.attr('x', function (d) { this.rects.attr('x', function (d) {
return getDis(d.source, d.target) / 2 - d3.select(this).attr('width') / 2 return getDis(d.source, d.target) / 2 - d3.select(this).attr('width') / 2
...@@ -604,6 +683,14 @@ export default class RelationGraph { ...@@ -604,6 +683,14 @@ export default class RelationGraph {
// 5.修改节点的位置 // 5.修改节点的位置
this.circles.attr('cx', (d) => d.x).attr('cy', (d) => d.y) this.circles.attr('cx', (d) => d.x).attr('cy', (d) => d.y)
this.squares
.attr('x', (d) => d.x - r)
.attr('y', (d) => d.y - r)
.attr('points', (d) => polygonPoints(d.x, d.y, r, 6))
this.pentagons
.attr('x', (d) => d.x - r)
.attr('y', (d) => d.y - r)
.attr('points', (d) => polygonPoints(d.x, d.y, r, 10))
// 让menu随圆圈移动 // 让menu随圆圈移动
this.circles.each(function () { this.circles.each(function () {
...@@ -612,7 +699,25 @@ export default class RelationGraph { ...@@ -612,7 +699,25 @@ export default class RelationGraph {
const self = d3.select(this) const self = d3.select(this)
const cx = self.attr('cx') const cx = self.attr('cx')
const cy = self.attr('cy') const cy = self.attr('cy')
menuEle.attr('transform', `translate(${cx},${cy})`) menuEle.attr('transform', `translate(${cx},${cy})`).raise()
}
})
this.squares.each(function () {
const menuEle = d3.select(this.nextSibling)
if (!menuEle.empty()) {
const self = d3.select(this)
const cx = +self.attr('x') + r
const cy = +self.attr('y') + r
menuEle.attr('transform', `translate(${cx},${cy})`).raise()
}
})
this.pentagons.each(function () {
const menuEle = d3.select(this.nextSibling)
if (!menuEle.empty()) {
const self = d3.select(this)
const cx = +self.attr('x') + r
const cy = +self.attr('y') + r
menuEle.attr('transform', `translate(${cx},${cy})`).raise()
} }
}) })
} }
...@@ -650,7 +755,7 @@ export default class RelationGraph { ...@@ -650,7 +755,7 @@ export default class RelationGraph {
this.dependsLink = Array.from(new Set(this.dependsLink)) this.dependsLink = Array.from(new Set(this.dependsLink))
// 隐藏节点 // 隐藏节点
this.SVG.selectAll('circle') this.SVG.selectAll('.node')
.filter((d) => this.dependsNode.indexOf(d.index) == -1) .filter((d) => this.dependsNode.indexOf(d.index) == -1)
.transition() .transition()
.style('opacity', 0.2) .style('opacity', 0.2)
...@@ -663,7 +768,7 @@ export default class RelationGraph { ...@@ -663,7 +768,7 @@ export default class RelationGraph {
} else { } else {
// 取消高亮 // 取消高亮
// 恢复隐藏的线 // 恢复隐藏的线
this.SVG.selectAll('circle').transition().style('opacity', 1) this.SVG.selectAll('.node').transition().style('opacity', 1)
// 恢复隐藏的线 // 恢复隐藏的线
this.SVG.selectAll('.edge').transition().style('opacity', 1) this.SVG.selectAll('.edge').transition().style('opacity', 1)
...@@ -676,7 +781,7 @@ export default class RelationGraph { ...@@ -676,7 +781,7 @@ export default class RelationGraph {
highlightLinks(obj) { highlightLinks(obj) {
if (obj) { if (obj) {
// 隐藏节点 // 隐藏节点
this.SVG.selectAll('circle') this.SVG.selectAll('.node')
.filter((d) => { .filter((d) => {
const sourceIndex = this.config.nodes.findIndex( const sourceIndex = this.config.nodes.findIndex(
(e) => e.id == obj.source.id (e) => e.id == obj.source.id
...@@ -695,7 +800,7 @@ export default class RelationGraph { ...@@ -695,7 +800,7 @@ export default class RelationGraph {
.transition() .transition()
.style('opacity', 0.1) .style('opacity', 0.1)
} else { } else {
this.SVG.selectAll('circle').transition().style('opacity', 1) this.SVG.selectAll('.node').transition().style('opacity', 1)
this.SVG.selectAll('.edge').transition().style('opacity', 1) this.SVG.selectAll('.edge').transition().style('opacity', 1)
} }
} }
...@@ -703,7 +808,7 @@ export default class RelationGraph { ...@@ -703,7 +808,7 @@ export default class RelationGraph {
setHighlights(key) { setHighlights(key) {
this.curHighlightKey = key this.curHighlightKey = key
this.config.isHighLight = false this.config.isHighLight = false
this.SVG.selectAll('circle').transition().style('opacity', 1) this.SVG.selectAll('.node').transition().style('opacity', 1)
this.SVG.selectAll('.edge').transition().style('opacity', 1) this.SVG.selectAll('.edge').transition().style('opacity', 1)
this.dependsNode = [] this.dependsNode = []
this.dependsLink = [] this.dependsLink = []
...@@ -733,7 +838,7 @@ export default class RelationGraph { ...@@ -733,7 +838,7 @@ export default class RelationGraph {
}) })
// 隐藏节点 // 隐藏节点
this.SVG.selectAll('circle') this.SVG.selectAll('.node')
.filter((d) => this.dependsNode.indexOf(d.index) < 0) .filter((d) => this.dependsNode.indexOf(d.index) < 0)
.transition() .transition()
.style('opacity', 0.2) .style('opacity', 0.2)
......
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