Browse Source

Merge branch 'mh' of http://124.70.32.114:3100/hanyuqing/KGPython into hanyuqing

# Conflicts:
#	vue/src/system/GraphDemo.vue
#	vue/src/system/GraphStyle.vue
hanyuqing
hanyuqing 3 months ago
parent
commit
3657863262
  1. 4
      vue/src/components/GraphToolbar.vue
  2. 233
      vue/src/system/GraphDemo.vue
  3. 208
      vue/src/system/GraphQA.vue
  4. 132
      vue/src/system/GraphStyle.vue

4
vue/src/components/GraphToolbar.vue

@ -270,12 +270,12 @@ export default {
const url = URL.createObjectURL(blob); const url = URL.createObjectURL(blob);
const link = document.createElement('a'); const link = document.createElement('a');
link.href = url; link.href = url;
link.download = `医疗图谱导出_${Date.now()}.svg`; link.download = `医疗图谱_${Date.now()}.svg`;
document.body.appendChild(link); document.body.appendChild(link);
link.click(); link.click();
document.body.removeChild(link); document.body.removeChild(link);
URL.revokeObjectURL(url); URL.revokeObjectURL(url);
this.$message.success('高精度矢量图导出成功'); this.$message.success('图导出成功');
} catch (err) { } catch (err) {
this.$message.error('导出失败'); this.$message.error('导出失败');
} }

233
vue/src/system/GraphDemo.vue

@ -139,7 +139,6 @@
v-if="_graph" v-if="_graph"
:graph="_graph" :graph="_graph"
class="toolbar-position" class="toolbar-position"
style="position: absolute; top: 40px; left: 750px; z-index: 1000; width: auto;"
/> />
<div ref="graphContainer" class="graph-container" id="container"></div> <div ref="graphContainer" class="graph-container" id="container"></div>
</div> </div>
@ -161,16 +160,15 @@ import {
} from "@/api/graph" } from "@/api/graph"
import {Graph, Tooltip} from '@antv/g6'; import {Graph, Tooltip} from '@antv/g6';
import Menu from "@/components/Menu.vue"; import Menu from "@/components/Menu.vue";
import {a} from "vue-router/dist/devtools-EWN81iOl.mjs";
import GraphToolbar from "@/components/GraphToolbar.vue"; import GraphToolbar from "@/components/GraphToolbar.vue";
import { markRaw } from 'vue';
import {a} from "vue-router/dist/devtools-EWN81iOl.mjs";
import Fuse from 'fuse.js'; import Fuse from 'fuse.js';
import {ElMessage} from "element-plus";
import {getGraphStyleActive} from "@/api/style"; import {getGraphStyleActive} from "@/api/style";
export default { export default {
name: 'Display', name: 'Display',
components: {Menu, GraphToolbar}, components: {Menu,GraphToolbar},
data() { data() {
return { return {
_graph: null, _graph: null,
@ -212,41 +210,41 @@ export default {
children: 'children', children: 'children',
label: 'title' // el-tree label: 'title' // el-tree
}, },
typeRadio: "Disease", typeRadio:"Disease",
DiseaseRadio: "ICD10", DiseaseRadio:"ICD10",
drugTree: [], drugTree:[],
diseaseDepartTree: [], diseaseDepartTree:[],
diseaseICD10Tree: [], diseaseICD10Tree:[],
diseaseSZMTree: [], diseaseSZMTree:[],
checkTree: [], checkTree:[],
legendItems: [ legendItems: [
{key: 'Disease', label: '疾病', color: '#EF4444'}, { key: 'Disease', label: '疾病', color: '#EF4444' },
{key: 'Drug', label: '药品', color: '#91cc75'}, { key: 'Drug', label: '药品', color: '#91cc75' },
{key: 'Check', label: '检查', color: '#336eee'}, { key: 'Check', label: '检查', color: '#336eee' },
{key: 'Symptom', label: '症状', color: '#fac858'}, { key: 'Symptom', label: '症状', color: '#fac858' },
{key: 'Other', label: '其他', color: '#59d1d4'} { key: 'Other', label: '其他', color: '#59d1d4' }
], ],
visibleCategories: new Set(), // visibleCategories: new Set(), //
diseaseCount: 0, diseaseCount:0,
drugCount: 0, drugCount:0,
checkCount: 0, checkCount:0,
originalNodeStyles: new Map(), // originalNodeStyles: new Map(), //
originalEdgeStyles: new Map(), // originalEdgeStyles: new Map(), //
searchResults: { searchResults: {
nodes: [], nodes: [],
edges: [] edges: []
}, },
searchKeyword: "", searchKeyword:"",
drugSubjectTree: [], drugSubjectTree:[],
DrugRadio: "Subject", DrugRadio:"Subject",
isDropdownOpen: false, isDropdownOpen: false,
typeOptions: [ typeOptions: [
{value: 'Disease', label: '疾病'}, { value: 'Disease', label: '疾病' },
{value: 'Drug', label: '药品'}, { value: 'Drug', label: '药品' },
{value: 'Check', label: '检查'} { value: 'Check', label: '检查' }
], ],
configs: [], configs:[],
parsedStyles: {}, parsedStyles:{},
enToZhLabelMap: { enToZhLabelMap: {
Disease: '疾病', Disease: '疾病',
Drug: '药品', Drug: '药品',
@ -325,7 +323,7 @@ export default {
this.loadDrugTreeData() this.loadDrugTreeData()
this.loadCheckTreeData() this.loadCheckTreeData()
this.loadDrugSubjectTreeData() this.loadDrugSubjectTreeData()
this.treeData = this.diseaseICD10Tree this.treeData=this.diseaseICD10Tree
await this.$nextTick(); await this.$nextTick();
try { try {
await this.getDefault() await this.getDefault()
@ -367,7 +365,7 @@ export default {
return { return {
...edge, ...edge,
id: edge.data?.relationship?.id || edge.id, id: edge.data?.relationship?.id || edge.id,
type: styleConf.edgeType || this.edgeType, type: styleConf.edgeType ||this.edgeType,
style: { style: {
endArrow: styleConf.edgeEndArrow !== undefined ? styleConf.edgeEndArrow : true, endArrow: styleConf.edgeEndArrow !== undefined ? styleConf.edgeEndArrow : true,
stroke: styleConf.edgeStroke || this.edgeStroke, stroke: styleConf.edgeStroke || this.edgeStroke,
@ -443,7 +441,7 @@ export default {
}, },
beforeUnmount() { beforeUnmount() {
if (this._graph != null) { if (this._graph!=null){
this._graph.stopLayout(); this._graph.stopLayout();
this.clearGraphState(); this.clearGraphState();
this._graph.destroy() this._graph.destroy()
@ -482,7 +480,7 @@ export default {
return {}; return {};
} }
}, },
async getDefault() { async getDefault(){
const response = await getGraphStyleActive(); const response = await getGraphStyleActive();
const data = response.data; const data = response.data;
if (!Array.isArray(data) || data.length === 0) { if (!Array.isArray(data) || data.length === 0) {
@ -650,7 +648,7 @@ export default {
// 4 // 4
const firstMatch = nodeIds[0] || edgeIds[0]; const firstMatch = nodeIds[0] || edgeIds[0];
if (firstMatch && (this._graph.hasNode(firstMatch) || this._graph.hasEdge(firstMatch))) { if (firstMatch && (this._graph.hasNode(firstMatch) || this._graph.hasEdge(firstMatch))) {
this._graph.focusElement(firstMatch, {animation: true, duration: 600}); this._graph.focusElement(firstMatch, { animation: true, duration: 600 });
} }
}, },
clearSearchHighlight() { clearSearchHighlight() {
@ -664,26 +662,26 @@ export default {
this._graph.setElementState(id, 'normal', true); this._graph.setElementState(id, 'normal', true);
}); });
}, },
getCount() { getCount(){
getCount().then(res => { getCount().then(res=>{
this.diseaseCount = res.Disease this.diseaseCount=res.Disease
this.drugCount = res.Drug this.drugCount=res.Drug
this.checkCount = res.Check this.checkCount=res.Check
console.log(res) console.log(res)
}) })
}, },
buildCategoryIndex() { buildCategoryIndex() {
const index = {}; const index = {};
if (this._graph != null) { if (this._graph!=null){
const nodes = this._graph.getNodeData() // const nodes = this._graph.getNodeData() //
nodes.forEach(node => { nodes.forEach(node => {
console.log(node.data.label) console.log(node.data.label)
const category = node.data.label; // label const category = node.data.label; // label
if (category == 'Drug' || category == 'Symptom' || if(category=='Drug'||category=='Symptom'||
category == 'Disease' || category == 'Check') { category=='Disease'||category=='Check'){
if (!index[category]) index[category] = []; if (!index[category]) index[category] = [];
index[category].push(node.id); index[category].push(node.id);
} else { }else{
if (!index["Other"]) index["Other"] = []; if (!index["Other"]) index["Other"] = [];
index["Other"].push(node.id); index["Other"].push(node.id);
} }
@ -693,7 +691,7 @@ export default {
}, },
// //
toggleCategory(key) { toggleCategory (key){
if (this.visibleCategories.has(key)) { if (this.visibleCategories.has(key)) {
this.visibleCategories.delete(key) this.visibleCategories.delete(key)
} else { } else {
@ -748,28 +746,28 @@ export default {
} }
}, },
changeTree() { changeTree(){
if (this.typeRadio == "Disease") { if(this.typeRadio=="Disease"){
if (this.DiseaseRadio == "ICD10") { if(this.DiseaseRadio=="ICD10") {
this.treeData = this.diseaseICD10Tree this.treeData=this.diseaseICD10Tree
} }
if (this.DiseaseRadio == "Department") { if(this.DiseaseRadio=="Department") {
this.treeData = this.diseaseDepartTree this.treeData=this.diseaseDepartTree
} }
if (this.DiseaseRadio == "SZM") { if(this.DiseaseRadio=="SZM") {
this.treeData = this.diseaseSZMTree this.treeData=this.diseaseSZMTree
} }
} }
if (this.typeRadio == "Drug") { if(this.typeRadio=="Drug") {
if (this.DrugRadio == "Subject") { if(this.DrugRadio=="Subject") {
this.treeData = this.drugSubjectTree this.treeData=this.drugSubjectTree
} }
if (this.DrugRadio == "SZM") { if(this.DrugRadio=="SZM") {
this.treeData = this.drugTree this.treeData=this.drugTree
} }
} }
if (this.typeRadio == "Check") { if(this.typeRadio=="Check") {
this.treeData = this.checkTree this.treeData=this.checkTree
} }
}, },
async loadDiseaseICD10TreeData() { async loadDiseaseICD10TreeData() {
@ -829,15 +827,15 @@ export default {
const response = await getGraph(data); // Promise const response = await getGraph(data); // Promise
this.formatData(response) this.formatData(response)
} }
if (data.level == "category" || if(data.level=="category"||
data.level == "subcategory" || data.level=="subcategory"||
data.level == "diagnosis") { data.level=="diagnosis"){
data.type = "Disease" data.type="Disease"
const response = await getGraph(data); // Promise const response = await getGraph(data); // Promise
this.formatData(response) this.formatData(response)
} }
if (data.type === "Disease") { if(data.type === "Disease"){
data.type = "Disease" data.type="Disease"
const response = await getGraph(data); // Promise const response = await getGraph(data); // Promise
this.formatData(response) this.formatData(response)
} }
@ -853,21 +851,21 @@ export default {
// 1. // 1.
this._graph.getNodeData().forEach(node => { this._graph.getNodeData().forEach(node => {
this._graph.setElementState(node.id, []); this._graph.setElementState(node.id,[]);
}); });
this._graph.getEdgeData().forEach(edge => { this._graph.getEdgeData().forEach(edge => {
this._graph.setElementState(edge.id, []); this._graph.setElementState(edge.id,[]);
}); });
// 2. pending // 2. pending
// clearTimeout // clearTimeout
}, },
formatData(data) { formatData(data){
if (!this._graph) return;
this._graph.stopLayout(); this._graph.stopLayout();
this.clearGraphState(); this.clearGraphState();
// === 1. nodeId label === // === 1. nodeId label ===
const nodeIdToEnLabel = {}; const nodeIdToEnLabel = {};
data.nodes.forEach(node => { data.nodes.forEach(node => {
@ -904,7 +902,7 @@ export default {
return { return {
...edge, ...edge,
id: edge.data?.relationship?.id || edge.id, id: edge.data?.relationship?.id || edge.id,
type: styleConf.edgeType || this.edgeType, type: styleConf.edgeType ||this.edgeType,
style: { style: {
endArrow: styleConf.edgeEndArrow !== undefined ? styleConf.edgeEndArrow : true, endArrow: styleConf.edgeEndArrow !== undefined ? styleConf.edgeEndArrow : true,
stroke: styleConf.edgeStroke || this.edgeStroke, stroke: styleConf.edgeStroke || this.edgeStroke,
@ -922,6 +920,7 @@ export default {
nodes: updatedNodes, nodes: updatedNodes,
edges: updatedEdges edges: updatedEdges
}; };
this.buildNodeLabelMap(updatedNodes); this.buildNodeLabelMap(updatedNodes);
this.updateGraph(updatedData) this.updateGraph(updatedData)
this.buildCategoryIndex(); this.buildCategoryIndex();
@ -949,7 +948,7 @@ export default {
}, },
initGraph() { initGraph() {
if (this._graph != null) { if (this._graph!=null){
this._graph.destroy() this._graph.destroy()
this._graph = null; this._graph = null;
} }
@ -960,7 +959,7 @@ export default {
console.log(this._nodeLabelMap) console.log(this._nodeLabelMap)
const container = this.$refs.graphContainer; const container = this.$refs.graphContainer;
if (!container) return; if (!container) return;
if (container != null) { if (container!=null){
const width = container.clientWidth || 800; const width = container.clientWidth || 800;
const height = container.clientHeight || 600; const height = container.clientHeight || 600;
console.log(width) console.log(width)
@ -1000,8 +999,8 @@ export default {
easing: 'ease-in-out', // easing: 'ease-in-out', //
}, },
}, },
behaviors: ['zoom-canvas', 'drag-element', behaviors: [ 'zoom-canvas', 'drag-element',
'click-select', 'focus-element', { 'click-select','focus-element', {
type: 'hover-activate', type: 'hover-activate',
degree: 1, degree: 1,
}, },
@ -1048,7 +1047,7 @@ export default {
shadowBlur: 10, shadowBlur: 10,
opacity: 1 opacity: 1
}, },
highlight: { highlight:{
stroke: '#FF5722', stroke: '#FF5722',
lineWidth: 4, lineWidth: 4,
opacity: 1 opacity: 1
@ -1056,7 +1055,7 @@ export default {
inactive: { inactive: {
opacity: 0.8 opacity: 0.8
}, },
normal: { normal:{
opacity: 1 opacity: 1
} }
@ -1104,14 +1103,14 @@ export default {
inactive: { inactive: {
opacity: 0.8 opacity: 0.8
}, },
normal: { normal:{
opacity: 1 opacity: 1
} }
}, },
}, },
data: this.defaultData, data:this.defaultData,
}); });
this.$nextTick(() => { this.$nextTick(() => {
@ -1169,10 +1168,10 @@ export default {
// }); // });
graph.on('node:click', (evt) => { graph.on('node:click', (evt) => {
const nodeItem = evt.target.id; // const nodeItem = evt.target.id; //
let node = graph.getNodeData(nodeItem).data let node=graph.getNodeData(nodeItem).data
let data = { let data={
label: node.name, label:node.name,
type: node.label type:node.label
} }
getGraph(data).then(response=>{ getGraph(data).then(response=>{
console.log(response) console.log(response)
@ -1184,7 +1183,7 @@ export default {
graph.fitCenter({ padding: 40, duration: 1000 }); graph.fitCenter({ padding: 40, duration: 1000 });
} }
}); });
this._graph = graph this._graph = markRaw(graph)
this._graph.setPlugins([ { this._graph.setPlugins([ {
type: 'tooltip', type: 'tooltip',
// tooltip // tooltip
@ -1194,7 +1193,7 @@ export default {
console.log(e) console.log(e)
const edge = items[0]; // const edge = items[0]; //
if (!edge) return ''; if (!edge) return '';
const data = items[0].data const data=items[0].data
const sourceId = edge.source; const sourceId = edge.source;
const targetId = edge.target; const targetId = edge.target;
@ -1215,11 +1214,7 @@ export default {
{ {
type: 'toolbar', type: 'toolbar',
onClick: (id) => { onClick: (id) => {
if (id === 'reset') { if (this.$refs.toolbarRef) {
// 1.
this.localResetGraph();
} else if (this.$refs.toolbarRef) {
// 2.
this.$refs.toolbarRef.handleToolbarAction(id); this.$refs.toolbarRef.handleToolbarAction(id);
} }
}, },
@ -1228,7 +1223,6 @@ export default {
{id: 'zoom-in', value: 'zoom-in', title: '放大'}, {id: 'zoom-in', value: 'zoom-in', title: '放大'},
{id: 'zoom-out', value: 'zoom-out', title: '缩小'}, {id: 'zoom-out', value: 'zoom-out', title: '缩小'},
{id: 'auto-fit', value: 'auto-fit', title: '聚焦'}, {id: 'auto-fit', value: 'auto-fit', title: '聚焦'},
{id: 'reset', value: 'reset', title: '重置'},
{id: 'export', value: 'export', title: '导出图谱'}, {id: 'export', value: 'export', title: '导出图谱'},
]; ];
}, },
@ -1236,44 +1230,6 @@ export default {
]) ])
} }
}, },
localResetGraph() {
// 1.
if (!this._graph) return;
if (!this.defaultData || !this.defaultData.nodes) {
this.$message.warning("未找到可重置的数据源");
return;
}
try {
// 2.
const canvas = this._graph.getCanvas();
if (canvas && typeof canvas.setCursor === 'function') {
canvas.setCursor('default');
}
// 3.
// DOM EventBoundary
this._graph.destroy();
this._graph = null;
// 4.
this.$nextTick(() => {
// initGraph this.defaultData
this.initGraph();
// 5. destroy
this.buildCategoryIndex();
this.$message.success("图谱已重置");
});
} catch (err) {
console.error('重置图谱失败:', err);
//
// location.reload();
}
},
updateGraph(data) { updateGraph(data) {
if (!this._graph) return if (!this._graph) return
this._graph.setData(data) this._graph.setData(data)
@ -1285,7 +1241,7 @@ export default {
const updatedNodes = this.defaultData.nodes.map(node => ({ const updatedNodes = this.defaultData.nodes.map(node => ({
...node, ...node,
type: this.nodeShape, type: this.nodeShape,
style: { style:{
size: this.nodeSize, size: this.nodeSize,
fill: this.nodeFill, fill: this.nodeFill,
stroke: this.nodeStroke, stroke: this.nodeStroke,
@ -1303,7 +1259,6 @@ export default {
this.defaultData = updatedData this.defaultData = updatedData
this.updateGraph(updatedData) this.updateGraph(updatedData)
}, },
updateAllEdges() { updateAllEdges() {
if (!this._graph) return if (!this._graph) return
const updatedEdges = this.defaultData.edges.map(edge => ({ const updatedEdges = this.defaultData.edges.map(edge => ({
@ -1474,7 +1429,6 @@ button:hover {
} }
.graph-container { .graph-container {
flex: 1;
background: #fff; background: #fff;
width: 100%; width: 100%;
height: 100%; height: 100%;
@ -1613,10 +1567,14 @@ button:hover {
flex: 1; flex: 1;
border: 1px dashed #e2e8f0; border: 1px dashed #e2e8f0;
border-radius: 12px; border-radius: 12px;
position: relative !important; /* 必须:作为工具栏参照物 */
overflow: hidden;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
color: #ccc; color: #ccc;
background: #fff;
padding: 0 !important;
} }
@ -1712,7 +1670,6 @@ button:hover {
/deep/ .radio-check .el-radio__input.is-checked+.el-radio__label { /deep/ .radio-check .el-radio__input.is-checked+.el-radio__label {
color: #1890ff; color: #1890ff;
} }
/* 自定义下拉样式 */ /* 自定义下拉样式 */
.select-container { .select-container {
position: relative; position: relative;
@ -1742,7 +1699,7 @@ button:hover {
background: rgba(255, 255, 255, 0.64); background: rgba(255, 255, 255, 0.64);
border: 1px solid #e0e0e0; border: 1px solid #e0e0e0;
border-radius: 8px; border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); box-shadow: 0 4px 12px rgba(0,0,0,0.15);
z-index: 10; z-index: 10;
min-width: 80px; min-width: 80px;
color: #000; color: #000;
@ -1759,4 +1716,16 @@ button:hover {
background-color: #f5f7fa; background-color: #f5f7fa;
} }
:deep(.custom-graph-toolbar) {
position: absolute !important; /* 脱离文档流,解决占位问题 */
top: 0 !important;
right: 0 !important;
z-index: 1000 !important;
background: rgba(255, 255, 255, 0.9) !important;
padding: 4px 8px !important;
border-radius: 8px !important;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1) !important;
border: 1px solid #ebeef5 !important
}
</style> </style>

208
vue/src/system/GraphQA.vue

@ -63,6 +63,12 @@
<!-- 右侧知识图谱 --> <!-- 右侧知识图谱 -->
<div class="knowledge-graph"> <div class="knowledge-graph">
<GraphToolbar
v-if="_graph"
:graph="_graph"
ref="toolbarRef"
class="custom-graph-toolbar"
/>
<div ref="graphContainer" class="graph-container" id="container"></div> <div ref="graphContainer" class="graph-container" id="container"></div>
</div> </div>
</div> </div>
@ -77,11 +83,12 @@ import {qaAnalyze} from "@/api/qa";
import {Graph} from "@antv/g6"; import {Graph} from "@antv/g6";
import {getGraph} from "@/api/graph"; import {getGraph} from "@/api/graph";
import {getGraphStyleActive} from "@/api/style"; import {getGraphStyleActive} from "@/api/style";
import GraphToolbar from '@/components/GraphToolbar.vue';
export default { export default {
name: 'GraghQA', name: 'GraghQA',
components: {Menu}, components: {Menu,GraphToolbar},
data() { data() {
return { return {
query:"", query:"",
@ -244,47 +251,85 @@ export default {
} }
}, },
handleSearch(){ handleSearch() {
this.isSending=true this.isSending = true
this.answers=[] this.answers = []
if (this._graph){ if (this._graph) {
this._graph.clear() this._graph.clear()
} }
let data={ let data = {
text:this.query text: this.query
} }
this.queryRecord=this.query this.queryRecord = this.query
this.query="" this.query = ""
// this.answers=[{"answer":"尿", // this.answers=[{"answer":"尿",
// "result":{"nodes":[{"id":"e0a8410b-c5ee-47d4-acbe-b7ae5f111fd4","label":"尿","type":""},{"id":"89f9498b-7a83-4361-889b-f96dfdfb802c","label":"","type":""},{"id":"03201620-ba9f-4957-b7d3-f89c5a115e37","label":"","type":""},{"id":"b0bace0a-eedc-485c-90d3-0a5378dc5556","label":"","type":""},{"id":"ffc12d7b-60e5-4ffa-a945-a0769f6e1047","label":"","type":""},{"id":"a7e94ee7-072b-456f-bc0c-354545851c38","label":"","type":""}],"edges":[{"source":"e0a8410b-c5ee-47d4-acbe-b7ae5f111fd4","target":"03201620-ba9f-4957-b7d3-f89c5a115e37","label":""},{"source":"e0a8410b-c5ee-47d4-acbe-b7ae5f111fd4","target":"b0bace0a-eedc-485c-90d3-0a5378dc5556","label":""},{"source":"e0a8410b-c5ee-47d4-acbe-b7ae5f111fd4","target":"ffc12d7b-60e5-4ffa-a945-a0769f6e1047","label":""},{"source":"e0a8410b-c5ee-47d4-acbe-b7ae5f111fd4","target":"a7e94ee7-072b-456f-bc0c-354545851c38","label":""},{"source":"e0a8410b-c5ee-47d4-acbe-b7ae5f111fd4","target":"89f9498b-7a83-4361-889b-f96dfdfb802c","label":""}]}}] // "result":{"nodes":[{"id":"e0a8410b-c5ee-47d4-acbe-b7ae5f111fd4","label":"尿","type":""},{"id":"89f9498b-7a83-4361-889b-f96dfdfb802c","label":"","type":""},{"id":"03201620-ba9f-4957-b7d3-f89c5a115e37","label":"","type":""},{"id":"b0bace0a-eedc-485c-90d3-0a5378dc5556","label":"","type":""},{"id":"ffc12d7b-60e5-4ffa-a945-a0769f6e1047","label":"","type":""},{"id":"a7e94ee7-072b-456f-bc0c-354545851c38","label":"","type":""}],"edges":[{"source":"e0a8410b-c5ee-47d4-acbe-b7ae5f111fd4","target":"03201620-ba9f-4957-b7d3-f89c5a115e37","label":""},{"source":"e0a8410b-c5ee-47d4-acbe-b7ae5f111fd4","target":"b0bace0a-eedc-485c-90d3-0a5378dc5556","label":""},{"source":"e0a8410b-c5ee-47d4-acbe-b7ae5f111fd4","target":"ffc12d7b-60e5-4ffa-a945-a0769f6e1047","label":""},{"source":"e0a8410b-c5ee-47d4-acbe-b7ae5f111fd4","target":"a7e94ee7-072b-456f-bc0c-354545851c38","label":""},{"source":"e0a8410b-c5ee-47d4-acbe-b7ae5f111fd4","target":"89f9498b-7a83-4361-889b-f96dfdfb802c","label":""}]}}]
// this.initGraph(this.answers[0].result) // this.initGraph(this.answers[0].result)
// this.formatData(this.answers[0].result) // this.formatData(this.answers[0].result)
qaAnalyze(data).then(res=>{ qaAnalyze(data).then(res => {
this.answers=res this.answers = res
if(this.answers.length>0){ if (this.answers.length > 0) {
this.initGraph(this.answers[0].result) this.initGraph(this.answers[0].result)
} }
this.isSending=false this.isSending = false
}) }).catch(err => {
console.error('接口失败,启动保底方案', err);
const mockData = {
nodes: [
{id: "node1", label: "霍乱", data: {type: "疾病"}},
{id: "node2", label: "腹泻", data: {type: "症状"}},
{id: "node3", label: "脱水", data: {type: "疾病"}},
{id: "node4", label: "呕吐", data: {type: "疾病"}},
{id: "node5", label: "霍乱弧菌", data: {type: "病因"}},
{id: "node6", label: "复方磺胺", data: {type: "药品"}},
],
edges: [
{id: "e1", source: "node1", target: "node2", data: {label: "典型症状"}},
{id: "e2", source: "node1", target: "node3", data: {label: "并发症"}},
{id: "e3", source: "node1", target: "node4", data: {label: "并发症"}},
{id: "e4", source: "node1", target: "node5", data: {label: "致病菌"}},
{id: "e5", source: "node1", target: "node6", data: {label: "推荐用药"}},
]
};
this.answers = [{answer: "连接失败,显示预览版。", result: mockData}];
this.initGraph(this.answers[0].result);
this.isSending = false;
});
}, },
formatData(data){ formatData(data){
// this._graph.stopLayout(); // this._graph.stopLayout();
// this.clearGraphState(); // this.clearGraphState();
// === 1. nodeId label === const typeMap = { '疾病': 'Disease', '药品': 'Drug', '药物': 'Drug', '症状': 'Symptom', '检查': 'Check', '病因': 'Cause' };
const nodeIdToEnLabel = {}; const getStandardLabel = (rawType) => typeMap[rawType] || rawType;
const nodeIdToData = {};
data.nodes.forEach(node => { data.nodes.forEach(node => {
nodeIdToEnLabel[node.id] = node.data.type; // e.g. "Disease" nodeIdToData[node.id] = {
enLabel: getStandardLabel(node.data.type),
rawType: node.data.type
};
}); });
// === 2. label === // === 2. label ===
const updatedNodes = data.nodes.map(node => { const updatedNodes = data.nodes.map(node => {
const enLabel = node.data.type; const enLabel = getStandardLabel(node.data.type);
const styleConf = this.parsedStyles[enLabel] || {}; const styleConf = this.parsedStyles[enLabel] || {};
// 💡 styleConf
let fColor = styleConf.nodeFill;
if (!fColor) {
if (node.data.type === '疾病') fColor = '#EF4444';
else if (node.data.type === '药品' || node.data.type === '药物') fColor = '#91cc75';
else if (node.data.type === '症状') fColor = '#fac858';
else if (node.data.type === '检查') fColor = '#336eee';
else fColor = this.nodeFill;
}
return { return {
...node, ...node,
type: styleConf.nodeShape || this.nodeShape, type: styleConf.nodeShape || this.nodeShape,
data: { ...node.data, label: enLabel, name: node.label },
style: { style: {
...node.style,
size: styleConf.nodeSize || this.nodeSize, size: styleConf.nodeSize || this.nodeSize,
fill: styleConf.nodeFill || this.nodeFill, fill: fColor,
stroke: styleConf.nodeStroke || this.nodeStroke, stroke: styleConf.nodeStroke || this.nodeStroke,
lineWidth: styleConf.nodeLineWidth || this.nodeLineWidth, lineWidth: styleConf.nodeLineWidth || this.nodeLineWidth,
label: styleConf.nodeShowLabel !== undefined ? styleConf.nodeShowLabel : true, label: styleConf.nodeShowLabel !== undefined ? styleConf.nodeShowLabel : true,
@ -297,15 +342,25 @@ export default {
// === 3. source label === // === 3. source label ===
const updatedEdges = data.edges.map(edge => { const updatedEdges = data.edges.map(edge => {
console.log(edge) const sourceInfo = nodeIdToData[edge.source];
const sourceEnLabel = nodeIdToEnLabel[edge.source]; // e.g. "Disease" const styleConf = this.parsedStyles[sourceInfo.enLabel] || {};
const styleConf = this.parsedStyles[sourceEnLabel] || {};
// 💡
let eStroke = styleConf.edgeStroke;
if (!eStroke) {
if (sourceInfo.rawType === '疾病') eStroke = 'rgba(239, 68, 68, 0.4)';
else if (sourceInfo.rawType === '药品' || sourceInfo.rawType === '药物') eStroke = 'rgba(145, 204, 117, 0.4)';
else if (sourceInfo.rawType === '症状') eStroke = 'rgba(250, 200, 88, 0.4)';
else eStroke = this.edgeStroke;
}
return { return {
...edge, ...edge,
id: edge.data?.relationship?.id || edge.id, id: edge.data?.relationship?.id || edge.id,
type: styleConf.edgeType ||this.edgeType, type: styleConf.edgeType ||this.edgeType,
data: { ...edge.data, label: edge.data?.label || "" },
style: { style: {
...edge.style,
endArrow: styleConf.edgeEndArrow !== undefined ? styleConf.edgeEndArrow : true, endArrow: styleConf.edgeEndArrow !== undefined ? styleConf.edgeEndArrow : true,
stroke: styleConf.edgeStroke || this.edgeStroke, stroke: styleConf.edgeStroke || this.edgeStroke,
lineWidth: styleConf.edgeLineWidth || this.edgeLineWidth, lineWidth: styleConf.edgeLineWidth || this.edgeLineWidth,
@ -318,41 +373,49 @@ export default {
}); });
// === 4. === // === 4. ===
let updatedData = { const pureData = JSON.parse(JSON.stringify({ nodes: updatedNodes, edges: updatedEdges }));
nodes: updatedNodes, this.updateGraph(pureData);
edges: updatedEdges
};
this.updateGraph(updatedData)
}, },
updateGraph(data) { updateGraph(data) {
if (!this._graph) return if (!this._graph) return;
this._graph.setData(data);
this._graph.setData(data) this._graph.render();
this._graph.render()
}, },
initGraph(data) { initGraph(data) {
const typeMap = { '疾病': 'Disease', '药品': 'Drug', '药物': 'Drug', '症状': 'Symptom', '检查': 'Check', '病因': 'Cause' };
const getStandardLabel = (rawType) => typeMap[rawType] || rawType;
if (this._graph!=null){ if (this._graph!=null){
this._graph.destroy() this._graph.destroy()
this._graph = null; this._graph = null;
} }
console.log(data)
// === 1. nodeId label === // === 1. nodeId label ===
const nodeIdToEnLabel = {}; const nodeIdToData = {};
data.nodes.forEach(node => { data.nodes.forEach(node => {
nodeIdToEnLabel[node.id] = node.data.type; // e.g. "Disease" nodeIdToData[node.id] = {
enLabel: getStandardLabel(node.data.type),
rawType: node.data.type
};
}); });
console.log(nodeIdToEnLabel)
// === 2. label === // === 2. label ===
const updatedNodes = data.nodes.map(node => { const updatedNodes = data.nodes.map(node => {
const enLabel = node.data.type; const enLabel = getStandardLabel(node.data.type);
const styleConf = this.parsedStyles[enLabel] || {}; const styleConf = this.parsedStyles[enLabel] || {};
let fColor = styleConf.nodeFill;
if (!fColor) {
if (node.data.type === '疾病') fColor = '#EF4444';
else if (node.data.type === '药品' || node.data.type === '药物') fColor = '#91cc75';
else if (node.data.type === '症状') fColor = '#fac858';
else if (node.data.type === '检查') fColor = '#336eee';
else fColor = this.nodeFill;
}
return { return {
...node, ...node,
type: styleConf.nodeShape || this.nodeShape, type: styleConf.nodeShape || this.nodeShape,
data: { ...node.data, label: enLabel, name: node.label },
style: { style: {
...node.style,
size: styleConf.nodeSize || this.nodeSize, size: styleConf.nodeSize || this.nodeSize,
fill: styleConf.nodeFill || this.nodeFill, fill: fColor,
stroke: styleConf.nodeStroke || this.nodeStroke, stroke: styleConf.nodeStroke || this.nodeStroke,
lineWidth: styleConf.nodeLineWidth || this.nodeLineWidth, lineWidth: styleConf.nodeLineWidth || this.nodeLineWidth,
label: styleConf.nodeShowLabel !== undefined ? styleConf.nodeShowLabel : true, label: styleConf.nodeShowLabel !== undefined ? styleConf.nodeShowLabel : true,
@ -365,17 +428,26 @@ export default {
// === 3. source label === // === 3. source label ===
const updatedEdges = data.edges.map(edge => { const updatedEdges = data.edges.map(edge => {
console.log(edge) const sourceInfo = nodeIdToData[edge.source];
const sourceEnLabel = nodeIdToEnLabel[edge.source]; // e.g. "Disease" const styleConf = this.parsedStyles[sourceInfo.enLabel] || {};
const styleConf = this.parsedStyles[sourceEnLabel] || {};
let eStroke = styleConf.edgeStroke;
if (!eStroke) {
if (sourceInfo.rawType === '疾病') eStroke = 'rgba(239, 68, 68, 0.4)';
else if (sourceInfo.rawType === '药品' || sourceInfo.rawType === '药物') eStroke = 'rgba(145, 204, 117, 0.4)';
else if (sourceInfo.rawType === '症状') eStroke = 'rgba(250, 200, 88, 0.4)';
else eStroke = this.edgeStroke;
}
return { return {
...edge, ...edge,
id: edge.data?.relationship?.id || edge.id, id: edge.data?.relationship?.id || edge.id,
type: styleConf.edgeType ||this.edgeType, type: styleConf.edgeType ||this.edgeType,
data: { ...edge.data, label: edge.data?.label || "" },
style: { style: {
...edge.style,
endArrow: styleConf.edgeEndArrow !== undefined ? styleConf.edgeEndArrow : true, endArrow: styleConf.edgeEndArrow !== undefined ? styleConf.edgeEndArrow : true,
stroke: styleConf.edgeStroke || this.edgeStroke, stroke: eStroke,
lineWidth: styleConf.edgeLineWidth || this.edgeLineWidth, lineWidth: styleConf.edgeLineWidth || this.edgeLineWidth,
label: styleConf.edgeShowLabel !== undefined ? styleConf.edgeShowLabel : false, label: styleConf.edgeShowLabel !== undefined ? styleConf.edgeShowLabel : false,
labelFontSize: styleConf.edgeFontSize || this.edgeFontSize, labelFontSize: styleConf.edgeFontSize || this.edgeFontSize,
@ -402,14 +474,27 @@ export default {
container, container,
width, width,
height, height,
plugins: [
{
type: 'toolbar',
key: 'g6-toolbar',
onClick: (id) => {
if (this.$refs.toolbarRef) this.$refs.toolbarRef.handleToolbarAction(id);
},
getItems: () => [
{ id: 'zoom-in', value: 'zoom-in', title: '放大' },
{ id: 'zoom-out', value: 'zoom-out', title: '缩小' },
{ id: 'auto-fit', value: 'auto-fit', title: '聚焦' },
{ id: 'export', value: 'export', title: '导出图谱' },
],
},
],
layout: { layout: {
type: 'force', // type: 'force', //
gravity: 0.3, // gravity: 0.3, //
repulsion: 500, // repulsion: 500, //
attraction: 20, // attraction: 20, //
preventOverlap: true // preventOverlap: true //
}, },
behaviors: [ 'zoom-canvas', 'drag-element', behaviors: [ 'zoom-canvas', 'drag-element',
'click-select','focus-element', { 'click-select','focus-element', {
@ -724,4 +809,39 @@ export default {
.dot-2 { animation-delay: -0.2s; } .dot-2 { animation-delay: -0.2s; }
.dot-3 { animation-delay: 0s; } .dot-3 { animation-delay: 0s; }
.dot-4 { animation-delay: 0.2s; } .dot-4 { animation-delay: 0.2s; }
/* 右侧图谱外层容器 */
.knowledge-graph {
position: relative !important; /* 保持相对定位,作为工具栏的参照物 */
flex: 1;
height: 100%;
background-color: white;
border-radius: 16px;
padding: 0 !important;
box-shadow: 0 4px 12px rgba(0,0,0,0.05);
overflow: hidden;
display: flex;
}
/* 图谱画布容器 */
.graph-container {
width: 100% !important;
height: 100% !important;
margin: 0 !important;
padding: 0 !important;
}
/* 工具栏浮动修饰 */
:deep(.custom-graph-toolbar) {
position: absolute !important; /* 必须绝对定位,不占物理高度 */
top: 0 !important;
right: 0 !important;
z-index: 9999 !important;
display: inline-flex !important;
background: rgba(255, 255, 255, 0.9) !important;
padding: 5px !important;
border-radius: 8px !important;
border: 1px solid #ebeef5 !important;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1) !important;
}
</style> </style>

132
vue/src/system/GraphStyle.vue

@ -53,7 +53,7 @@
<label>字体颜色</label> <label>字体颜色</label>
<div class="color-picker-border"> <div class="color-picker-border">
<el-color-picker v-model="nodeFontColor" show-alpha class="square-picker"/> <el-color-picker v-model="nodeFontColor" show-alpha class="square-picker"/>
<!-- <input v-model="nodeFontColor" type="color" class="square-picker"/>--> <!-- <input v-model="nodeFontColor" type="color" class="square-picker"/>-->
</div> </div>
</div> </div>
@ -82,7 +82,7 @@
<label>填充颜色</label> <label>填充颜色</label>
<div class="color-picker-border"> <div class="color-picker-border">
<el-color-picker v-model="nodeFill" show-alpha class="square-picker"/> <el-color-picker v-model="nodeFill" show-alpha class="square-picker"/>
<!-- <input v-model="nodeFill" type="color" class="square-picker"/>--> <!-- <input v-model="nodeFill" type="color" class="square-picker"/>-->
</div> </div>
</div> </div>
@ -90,7 +90,7 @@
<label>边框颜色</label> <label>边框颜色</label>
<div class="color-picker-border"> <div class="color-picker-border">
<el-color-picker v-model="nodeStroke" show-alpha class="square-picker"/> <el-color-picker v-model="nodeStroke" show-alpha class="square-picker"/>
<!-- <input v-model="nodeStroke" type="color" class="square-picker"/>--> <!-- <input v-model="nodeStroke" type="color" class="square-picker"/>-->
</div> </div>
</div> </div>
@ -140,7 +140,7 @@
<label>字体颜色</label> <label>字体颜色</label>
<div class="color-picker-border"> <div class="color-picker-border">
<el-color-picker v-model="edgeFontColor" class="square-picker" show-alpha /> <el-color-picker v-model="edgeFontColor" class="square-picker" show-alpha />
<!-- <input v-model="edgeFontColor" type="color" class="square-picker"/>--> <!-- <input v-model="edgeFontColor" type="color" class="square-picker"/>-->
</div> </div>
</div> </div>
@ -169,7 +169,7 @@
<label>线条颜色</label> <label>线条颜色</label>
<div class="color-picker-border"> <div class="color-picker-border">
<el-color-picker v-model="edgeStroke" class="square-picker" show-alpha /> <el-color-picker v-model="edgeStroke" class="square-picker" show-alpha />
<!-- <input v-model="edgeStroke" type="color" class="square-picker"/>--> <!-- <input v-model="edgeStroke" type="color" class="square-picker"/>-->
</div> </div>
</div> </div>
</div> </div>
@ -961,7 +961,10 @@ export default {
updateAllElements() { updateAllElements() {
if (!this._graph) return; if (!this._graph) return;
// 1.
const currentActiveLabelEn = tagToLabelMap[this.activeTags]; const currentActiveLabelEn = tagToLabelMap[this.activeTags];
// 2.
const labelToAppliedConfigMap = {}; const labelToAppliedConfigMap = {};
this.styleGroups.forEach(group => { this.styleGroups.forEach(group => {
group.configs.forEach(conf => { group.configs.forEach(conf => {
@ -972,13 +975,33 @@ export default {
}); });
}); });
const hexToRgba = (hex, opacity) => { // 3. 💡 Hex RGB
if (!hex) return 'rgba(182, 178, 178, 0.5)'; const hexToRgba = (color, opacity) => {
if (hex.startsWith('rgba')) return hex; if (!color) return `rgba(182, 178, 178, ${opacity})`;
let r = parseInt(hex.slice(1, 3), 16), g = parseInt(hex.slice(3, 5), 16), b = parseInt(hex.slice(5, 7), 16); // rgba
if (color.startsWith('rgba')) return color;
// A. rgb(116, 239, 68)
if (color.startsWith('rgb')) {
// rgb rgba
return color.replace('rgb', 'rgba').replace(')', `, ${opacity})`);
}
// B. #ffffff
if (color.startsWith('#')) {
try {
let r = parseInt(color.slice(1, 3), 16),
g = parseInt(color.slice(3, 5), 16),
b = parseInt(color.slice(5, 7), 16);
return `rgba(${r}, ${g}, ${b}, ${opacity})`; return `rgba(${r}, ${g}, ${b}, ${opacity})`;
} catch (e) {
console.error('❌ Hex 转换失败:', color);
return color;
}
}
// C.
return color;
}; };
// 4. (Nodes)
const nodes = this.defaultData.nodes.map(node => { const nodes = this.defaultData.nodes.map(node => {
const rawLabel = node.data?.label || ''; const rawLabel = node.data?.label || '';
const effectiveKey = this.getEffectiveStyleKey(rawLabel); const effectiveKey = this.getEffectiveStyleKey(rawLabel);
@ -996,46 +1019,85 @@ export default {
} }
return { return {
...node, type: s?.nodeShape || 'circle', ...node,
type: s?.nodeShape || 'circle',
style: { style: {
size: this.safeNum(s?.nodeSize, 60), fill: s?.nodeFill, stroke: s?.nodeStroke, size: this.safeNum(s?.nodeSize, 60),
lineWidth: this.safeNum(s?.nodeLineWidth, 2), labelText: s?.nodeShowLabel ? (node.data?.name || '') : '', fill: s?.nodeFill,
labelFill: s?.nodeFontColor || '#ffffff', labelFontSize: this.safeNum(s?.nodeFontSize, 12), stroke: s?.nodeStroke,
labelFontFamily: s?.nodeFontFamily || 'Microsoft YaHei', labelPlacement: 'center', labelWordWrap: true, lineWidth: this.safeNum(s?.nodeLineWidth, 2),
labelMaxWidth: '150%', labelMaxLines: 3, labelTextOverflow: 'ellipsis', labelTextAlign: 'center', labelText: s?.nodeShowLabel ? (node.data?.name || '') : '',
labelFill: s?.nodeFontColor || '#ffffff',
labelFontSize: this.safeNum(s?.nodeFontSize, 12),
labelFontFamily: s?.nodeFontFamily || 'Microsoft YaHei',
labelPlacement: 'center',
labelWordWrap: true,
labelMaxWidth: '150%',
labelMaxLines: 3,
labelTextOverflow: 'ellipsis',
labelTextAlign: 'center',
} }
}; };
}); });
const edges = this.defaultData.edges.map(edge => { // 5. (Edges)
const edges = this.defaultData.edges.map((edge, index) => {
const sRawLabel = this._nodeLabelMap.get(edge.source); const sRawLabel = this._nodeLabelMap.get(edge.source);
const effectiveKey = this.getEffectiveStyleKey(sRawLabel); const effectiveKey = this.getEffectiveStyleKey(sRawLabel);
let s; let s;
let sourceFrom = '';
if (effectiveKey === currentActiveLabelEn) { if (effectiveKey === currentActiveLabelEn) {
sourceFrom = '实时面板控制';
s = { s = {
edgeType: this.edgeType, edgeStroke: this.edgeStroke, edgeLineWidth: this.edgeLineWidth, edgeType: this.edgeType,
edgeEndArrow: this.edgeEndArrow, edgeShowLabel: this.edgeShowLabel, edgeStroke: this.edgeStroke,
edgeFontColor: this.edgeFontColor, edgeFontSize: this.edgeFontSize, edgeFontFamily: this.edgeFontFamily edgeLineWidth: this.edgeLineWidth,
edgeEndArrow: this.edgeEndArrow,
edgeShowLabel: this.edgeShowLabel,
edgeFontColor: this.edgeFontColor,
edgeFontSize: this.edgeFontSize,
edgeFontFamily: this.edgeFontFamily
}; };
} else { } else {
sourceFrom = '缓存/方案配置';
s = labelToAppliedConfigMap[effectiveKey] || this.tagStyles[effectiveKey]; s = labelToAppliedConfigMap[effectiveKey] || this.tagStyles[effectiveKey];
} }
const strokeColor = hexToRgba(s?.edgeStroke || '#EF4444', 0.6); // 使
const finalRawColor = s?.edgeStroke || this.edgeStroke || INITIAL_FILL_MAP[effectiveKey] || '#EF4444';
const strokeColor = hexToRgba(finalRawColor, 0.6);
if (index === 0) {
console.log('3. [连边抽检] 第一条边颜色详情:');
console.log(' - 原始输入:', finalRawColor);
console.log(' - 转换输出:', strokeColor);
}
return { return {
...edge, type: s?.edgeType || 'line', ...edge,
type: s?.edgeType || 'line',
style: { style: {
stroke: strokeColor, lineWidth: this.safeNum(s?.edgeLineWidth, 2), endArrow: s?.edgeEndArrow, stroke: strokeColor,
lineWidth: this.safeNum(s?.edgeLineWidth, 2),
endArrow: s?.edgeEndArrow !== undefined ? s.edgeEndArrow : true,
labelText: s?.edgeShowLabel ? (edge.data?.relationship?.properties?.label || '') : '', labelText: s?.edgeShowLabel ? (edge.data?.relationship?.properties?.label || '') : '',
labelFill: s?.edgeFontColor || '#666', labelFontSize: this.safeNum(s?.edgeFontSize, 10), labelFill: s?.edgeFontColor || '#666',
labelFontFamily: s?.edgeFontFamily || 'Microsoft YaHei', labelBackground: true, labelFontSize: this.safeNum(s?.edgeFontSize, 10),
labelBackgroundFill: '#fff', labelBackgroundOpacity: 0.7 labelFontFamily: s?.edgeFontFamily || 'Microsoft YaHei',
labelBackground: true,
labelBackgroundFill: '#fff',
labelBackgroundOpacity: 0.7
} }
}; };
}); });
this._graph.setData({nodes, edges});
// 6.
this._graph.setData({ nodes, edges });
this._graph.render(); this._graph.render();
console.log('4. 图谱渲染指令已发出');
console.groupEnd();
}, },
safeNum(val, defaultVal = 1) { safeNum(val, defaultVal = 1) {
const n = Number(val); const n = Number(val);
@ -1050,7 +1112,7 @@ export default {
...conf, ...conf,
styles: typeof conf.styles === 'string' ? JSON.parse(conf.styles) : conf.styles styles: typeof conf.styles === 'string' ? JSON.parse(conf.styles) : conf.styles
})); }));
return {...group, configs: uniqueConfigs}; return { ...group, configs: uniqueConfigs };
}); });
const activeGroup = this.styleGroups.find(g => g.is_active); const activeGroup = this.styleGroups.find(g => g.is_active);
@ -1103,27 +1165,19 @@ export default {
const isLabelExist = group.configs.some(c => c.current_label === labelName && c.id !== excludeId); const isLabelExist = group.configs.some(c => c.current_label === labelName && c.id !== excludeId);
if (isLabelExist) { if (isLabelExist) {
ElMessageBox.alert(`方案【${groupName}】中已存在【${labelName}】标签的配置。`, '校验失败', {type: 'error'}).catch(() => { ElMessageBox.alert(`方案【${groupName}】中已存在【${labelName}】标签的配置,请先删除旧配置或选择其他方案。`, '校验失败', { type: 'error' }).catch(() => {});
});
return false; return false;
} }
if (group.configs.length >= 5 && !group.configs.some(c => c.id === excludeId)) { if (group.configs.length >= 5 && !group.configs.some(c => c.id === excludeId)) {
ElMessageBox.alert(`方案【${groupName}】已满(上限5个)。`, '校验失败', {type: 'error'}).catch(() => { ElMessageBox.alert(`方案【${groupName}】的配置已满(上限5个),无法添加。`, '校验失败', { type: 'error' }).catch(() => {});
});
return false; return false;
} }
return true; return true;
}, },
async moveConfigToGroup(config, targetGroup) { async moveConfigToGroup(config, targetGroup) {
// 1.
if (config.group_id === targetGroup.id) {
return ElMessage.info("该配置已在该方案中");
}
if (!this.validateGroupConstraint(targetGroup.group_name, config.current_label, config.id)) return; if (!this.validateGroupConstraint(targetGroup.group_name, config.current_label, config.id)) return;
try { try {
// 2. Payload target_group_id // 2. Payload target_group_id
const payload = { const payload = {
@ -1186,8 +1240,7 @@ export default {
const missingTags = REQUIRED_TAGS.filter(tag => !new Set(currentLabels).has(tag)); const missingTags = REQUIRED_TAGS.filter(tag => !new Set(currentLabels).has(tag));
if (missingTags.length > 0) { if (missingTags.length > 0) {
this.isInitialEcho = false; this.isInitialEcho = false;
return ElMessageBox.alert(`该方案配置不完整,缺失:${missingTags.join('、')}`, '提示', {type: 'warning'}).catch(() => { return ElMessageBox.alert(`该方案配置不完整,缺失:${missingTags.join('、')}`, '提示', { type: 'warning' }).catch(() => {});
});
} }
this.usingConfigIds = group.configs.map(c => c.id); this.usingConfigIds = group.configs.map(c => c.id);
const res = await applyGraphStyleGroup(group.id); const res = await applyGraphStyleGroup(group.id);
@ -1774,7 +1827,6 @@ export default {
border-color: #e6e6e6 !important; border-color: #e6e6e6 !important;
color: #333 !important; color: #333 !important;
} }
:deep(.el-message-box__btns .el-button--primary) { :deep(.el-message-box__btns .el-button--primary) {
background-color: #1559f3 !important; background-color: #1559f3 !important;
border-color: #1559f3 !important; border-color: #1559f3 !important;

Loading…
Cancel
Save