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,
......
This diff is collapsed.
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