From d943150e59c04dfa2cdfaeb3f414034b4ac8ddde Mon Sep 17 00:00:00 2001 From: hanyuqing <1106611654@qq.com> Date: Tue, 6 Jan 2026 13:04:16 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B7=A5=E5=85=B7=E9=A1=B5=E9=9D=A2=E6=A0=B7?= =?UTF-8?q?=E5=BC=8F=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vue/src/system/GraphStyle.vue | 370 +++++++++++++++++++++++++++++++----------- 1 file changed, 273 insertions(+), 97 deletions(-) diff --git a/vue/src/system/GraphStyle.vue b/vue/src/system/GraphStyle.vue index dfa2345..82027c3 100644 --- a/vue/src/system/GraphStyle.vue +++ b/vue/src/system/GraphStyle.vue @@ -15,14 +15,11 @@
- - {{ tag }} + {{ tag }}
@@ -71,7 +68,13 @@
- +
@@ -90,7 +93,13 @@
- +
@@ -143,7 +152,13 @@
- +
@@ -294,24 +309,16 @@ import {ElMessageBox, ElMessage} from 'element-plus'; import {markRaw} from 'vue'; const tagToLabelMap = { - '疾病': 'Disease', '症状': 'Symptom', '病因': 'Cause', '药品': 'Drug', '科室': 'Department', '检查': 'Check' + '疾病': 'Disease', '症状': 'Symptom', '病因': 'Cause', '药品': 'Drug', '科室': 'Department', '检查': 'Check','其他':'Other' }; const INITIAL_FILL_MAP = { - 'Disease': '#EF4444', - 'Drug': '#91cc75', - 'Symptom': '#fac858', - 'Check': '#336eee', - 'Cause': '#59d1d4', - 'Department': '#59d1d4', + 'Disease': '#EF4444', 'Drug': '#91cc75', 'Symptom': '#fac858', 'Check': '#336eee', + 'Cause': '#59d1d4', 'Department': '#59d1d4', 'Other': '#59d1d4' }; const INITIAL_STROKE_MAP = { - 'Disease': '#B91C1C', - 'Drug': '#047857', - 'Symptom': '#B45309', - 'Check': '#1D4ED8', - 'Cause': '#40999b', - 'Department': '#40999b', + 'Disease': '#B91C1C', 'Drug': '#047857', 'Symptom': '#B45309', 'Check': '#1D4ED8', + 'Cause': '#40999b', 'Department': '#40999b', 'Other': '#40999b' }; export default { @@ -319,7 +326,7 @@ export default { components: {Menu}, data() { return { - activeTags: '', + activeTags: '疾病', styleGroups: [], existingGroups: [], activeCollapseNames: [], @@ -336,6 +343,7 @@ export default { 'Department': this.getInitialTagParams('Department'), 'DiseaseSite': this.getInitialTagParams('DiseaseSite'), 'Check': this.getInitialTagParams('Check'), + 'Other': this.getInitialTagParams('Other'), }, nodeShowLabel: true, nodeFontFamily: 'Microsoft YaHei, sans-serif', @@ -353,7 +361,7 @@ export default { edgeFontColor: '#666666', edgeType: 'line', edgeLineWidth: 2, - edgeStroke: '#b6b2b2', + edgeStroke: '#EF4444', // 初始同步节点颜色 defaultData: { nodes: [ {id: "node1", data: {name: "霍乱", label: "Disease"}}, @@ -384,13 +392,34 @@ export default { } }, watch: { + // 新增:监听方案勾选,同步勾选其下的所有画布 + checkedGroupIds(newGroupIds) { + // 遍历所有方案 + this.styleGroups.forEach(group => { + const isGroupChecked = newGroupIds.includes(group.id); + const childIds = group.configs.map(c => c.id); + + if (isGroupChecked) { + // 如果方案被勾选,将子画布 ID 全部加入 checkedConfigIds (去重) + this.checkedConfigIds = Array.from(new Set([...this.checkedConfigIds, ...childIds])); + } else { + // 如果方案取消勾选,将子画布 ID 从 checkedConfigIds 中移除 + this.checkedConfigIds = this.checkedConfigIds.filter(id => !childIds.includes(id)); + } + }); + }, + // 监听节点填充色,强制同步线条色 + nodeFill(newVal) { + this.edgeStroke = newVal; + }, nodeShowLabel: 'syncAndRefresh', nodeFontFamily: 'syncAndRefresh', nodeFontSize: 'syncAndRefresh', nodeFontColor: 'syncAndRefresh', nodeShape: 'syncAndRefresh', nodeSize: 'syncAndRefresh', - nodeFill: 'syncAndRefresh', nodeStroke: 'syncAndRefresh', nodeLineWidth: 'syncAndRefresh', + nodeStroke: 'syncAndRefresh', nodeLineWidth: 'syncAndRefresh', edgeShowLabel: 'syncAndRefresh', edgeEndArrow: 'syncAndRefresh', edgeFontFamily: 'syncAndRefresh', edgeFontSize: 'syncAndRefresh', edgeFontColor: 'syncAndRefresh', edgeType: 'syncAndRefresh', edgeLineWidth: 'syncAndRefresh', edgeStroke: 'syncAndRefresh' }, + created() { this._graph = null; this._nodeLabelMap = new Map(); @@ -412,21 +441,47 @@ export default { window.removeEventListener('resize', this.handleResize); }, methods: { - getStaticColor(tag) { - const label = tagToLabelMap[tag]; - if (label === 'Disease') return '#EF4444'; - if (label === 'Drug') return '#91cc75'; - if (label === 'Symptom') return '#fac858'; - if (label === 'Check') return '#336eee'; - return '#59d1d4'; + validateNodeSize(event) { + const inputVal = event.target.value; + const val = parseInt(inputVal); + if (isNaN(val) || val < 30 || val > 100) { + ElMessage({ message: `节点尺寸请输入 30 到 100 之间的数字`, type: 'warning', duration: 1500 }); + event.target.value = this.nodeSize; + return; + } + this.nodeSize = val; + this.syncAndRefresh(); + }, + validateNodeLineWidth(event) { + const inputVal = event.target.value; + const val = parseInt(inputVal); + if (isNaN(val) || val < 1 || val > 5) { + ElMessage({ message: `边框尺寸请输入 1 到 5 之间的数字`, type: 'warning', duration: 1500 }); + event.target.value = this.nodeLineWidth; + return; + } + this.nodeLineWidth = val; + this.syncAndRefresh(); + }, + validateEdgeLineWidth(event) { + const inputVal = event.target.value; + const val = parseInt(inputVal); + if (isNaN(val) || val < 1 || val > 5) { + ElMessage({ message: `线条粗细请输入 1 到 5 之间的数字`, type: 'warning', duration: 1500 }); + event.target.value = this.edgeLineWidth; + return; + } + this.edgeLineWidth = val; + this.syncAndRefresh(); }, getInitialTagParams(label) { + const fill = INITIAL_FILL_MAP[label] || '#59d1d4'; return { nodeShowLabel: true, nodeFontFamily: 'Microsoft YaHei, sans-serif', nodeFontSize: 12, nodeFontColor: '#ffffff', - nodeShape: 'circle', nodeSize: 60, nodeFill: INITIAL_FILL_MAP[label] || '#59d1d4', + nodeShape: 'circle', nodeSize: 60, nodeFill: fill, nodeStroke: INITIAL_STROKE_MAP[label] || '#40999b', nodeLineWidth: 2, edgeShowLabel: true, edgeEndArrow: true, edgeFontFamily: 'Microsoft YaHei, sans-serif', edgeFontSize: 10, edgeFontColor: '#666666', edgeType: 'line', - edgeLineWidth: 2, edgeStroke: '#b6b2b2' + edgeLineWidth: 2, edgeStroke: fill // 初始线条颜色同步填充色 }; }, handleTagClick(tag) { @@ -474,6 +529,14 @@ export default { } }); }); + + const hexToRgba = (hex, opacity) => { + if (!hex) return 'rgba(182, 178, 178, 0.5)'; + if (hex.startsWith('rgba')) return hex; + let r = parseInt(hex.slice(1, 3), 16), g = parseInt(hex.slice(3, 5), 16), b = parseInt(hex.slice(5, 7), 16); + return `rgba(${r}, ${g}, ${b}, ${opacity})`; + }; + const nodes = this.defaultData.nodes.map(node => { const labelEn = node.data?.label || ''; const s = labelToAppliedConfigMap[labelEn] || this.tagStyles[labelEn]; @@ -488,35 +551,32 @@ export default { } }; }); - let edgeS = this; - if (this.usingConfigIds.length > 0) { - const lastAppliedId = this.usingConfigIds[this.usingConfigIds.length - 1]; - this.styleGroups.forEach(g => { - const found = g.configs.find(c => c.id === lastAppliedId); - if (found) edgeS = found.styles; - }); - } - const hexToRgba = (hex, opacity) => { - if (!hex) return 'rgba(182, 178, 178, 0.5)'; - let r = parseInt(hex.slice(1, 3), 16), g = parseInt(hex.slice(3, 5), 16), b = parseInt(hex.slice(5, 7), 16); - return `rgba(${r}, ${g}, ${b}, ${opacity})`; - }; + const edges = this.defaultData.edges.map(edge => { - const s = edgeS; const sLabel = this._nodeLabelMap.get(edge.source); - const sourceNodeStyles = labelToAppliedConfigMap[sLabel] || this.tagStyles[sLabel]; - const dynamicStroke = hexToRgba(sourceNodeStyles?.nodeFill, 0.5); + // 获取源节点对应的样式(可能是方案里的,也可能是实时调整的) + const s = labelToAppliedConfigMap[sLabel] || this.tagStyles[sLabel] || this; + + // 线条颜色直接取该标签配置下的 edgeStroke + const strokeColor = hexToRgba(s.edgeStroke, 0.6); + return { ...edge, type: s.edgeType || 'line', style: { - stroke: dynamicStroke, lineWidth: this.safeNum(s.edgeLineWidth, 2), endArrow: s.edgeEndArrow, + stroke: strokeColor, + lineWidth: this.safeNum(s.edgeLineWidth, 2), + endArrow: s.edgeEndArrow, labelText: s.edgeShowLabel ? (edge.data?.relationship?.properties?.label || '') : '', - labelFill: s.edgeFontColor || '#666', labelFontSize: this.safeNum(s.edgeFontSize, 10), - labelFontFamily: s.edgeFontFamily || 'Microsoft YaHei', labelBackground: true, - labelBackgroundFill: '#fff', labelBackgroundOpacity: 0.7 + labelFill: s.edgeFontColor || '#666', + labelFontSize: this.safeNum(s.edgeFontSize, 10), + labelFontFamily: s.edgeFontFamily || 'Microsoft YaHei', + labelBackground: true, + labelBackgroundFill: '#fff', + labelBackgroundOpacity: 0.7 } }; }); + this._graph.setData({nodes, edges}); this._graph.render(); }, @@ -533,7 +593,6 @@ export default { ...conf, styles: typeof conf.styles === 'string' ? JSON.parse(conf.styles) : conf.styles })) })); - if (this.styleGroups.length > 0 && this.activeCollapseNames.length === 0) this.activeCollapseNames = [this.styleGroups[0].id]; } } catch (err) { console.error("加载配置失败:", err); @@ -583,7 +642,9 @@ export default { }, handleSaveClick() { this.fetchGroupNames(); - this.saveForm.canvas_name = `样式_${new Date().toLocaleTimeString([], {hour: '2-digit', minute: '2-digit'})}`; + + // 使用 Date.now() 获取当前 13 位毫秒时间戳 + this.saveForm.canvas_name = Date.now().toString(); this.saveDialogVisible = true; }, async confirmSave() { @@ -615,8 +676,7 @@ export default { this.fetchConfigs(); this.updateAllElements(); } - } catch (err) { - } + } catch (err) {} }, async deleteGroup(groupId) { try { @@ -626,19 +686,27 @@ export default { this.fetchConfigs(); this.updateAllElements(); } - } catch (err) { - } + } catch (err) {} }, async handleUnifiedBatchDelete() { try { - await ElMessageBox.confirm(`确定执行批量删除吗?`); + await ElMessageBox.confirm( + '确定执行批量删除吗?', + '批量删除', // 这里添加标题 + { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning', + // 确保标题和内容对齐 + distinguishCancelAndClose: true, + } + ); for (const gid of this.checkedGroupIds) await deleteGraphStyleGroup(gid); if (this.checkedConfigIds.length > 0) await batchDeleteGraphStyle({ids: this.checkedConfigIds}); this.clearSelection(); this.fetchConfigs(); this.updateAllElements(); - } catch (e) { - } + } catch (e) {} }, clearSelection() { this.checkedConfigIds = []; @@ -652,6 +720,44 @@ export default { \ No newline at end of file + +/* 统一修改所有弹窗和消息框的标题与取消按钮 */ +:deep(.el-dialog__title), +:deep(.el-message-box__title) { + color: #000000 !important; + font-weight: 600 !important; +} + +:deep(.el-dialog__header), +:deep(.el-message-box__header) { + text-align: left !important; +} + +:deep(.el-dialog__footer .el-button:first-child), +:deep(.el-message-box__btns .el-button:first-child) { + background-color: #e6e6e6 !important; + border-color: #e6e6e6 !important; + color: #333 !important; +} +:deep(.el-message-box__btns .el-button--primary) { + background-color: #1559f3 !important; + border-color: #1559f3 !important; +} + + +