|
|
|
@ -467,8 +467,14 @@ export default { |
|
|
|
methods: { |
|
|
|
handleEditConfig(item) { |
|
|
|
if (this.saveTimer) clearTimeout(this.saveTimer); |
|
|
|
this.isInitialEcho = true; |
|
|
|
|
|
|
|
// 如果点击的是当前正在编辑的配置,则退出编辑状态 |
|
|
|
if (this.editingConfigId === item.id) { |
|
|
|
this.exitEditingMode(); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
this.isInitialEcho = true; |
|
|
|
this.editingConfigId = item.id; |
|
|
|
this.editingConfigLabel = item.current_label; |
|
|
|
this.activeTags = item.current_label; |
|
|
|
@ -479,6 +485,32 @@ export default { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// 将配置应用到控制面板变量 |
|
|
|
this.applyStylesToPanel(s); |
|
|
|
|
|
|
|
const labelEn = tagToLabelMap[item.current_label]; |
|
|
|
if (labelEn) this.tagStyles[labelEn] = JSON.parse(JSON.stringify(s)); |
|
|
|
|
|
|
|
// 确保该配置在 usingConfigIds 中 |
|
|
|
if (!this.usingConfigIds.includes(item.id)) { |
|
|
|
this.styleGroups.forEach(g => { |
|
|
|
g.configs.forEach(c => { |
|
|
|
if (this.usingConfigIds.includes(c.id) && c.current_label === item.current_label) { |
|
|
|
this.usingConfigIds = this.usingConfigIds.filter(id => id !== c.id); |
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
this.usingConfigIds.push(item.id); |
|
|
|
} |
|
|
|
|
|
|
|
this.updateAllElements(); |
|
|
|
this.$nextTick(() => { |
|
|
|
setTimeout(() => { this.isInitialEcho = false; }, 100); |
|
|
|
}); |
|
|
|
}, |
|
|
|
|
|
|
|
// 抽离:将样式对象应用到左侧面板变量 |
|
|
|
applyStylesToPanel(s) { |
|
|
|
this.nodeShowLabel = s.nodeShowLabel; |
|
|
|
this.nodeFontFamily = s.nodeFontFamily; |
|
|
|
this.nodeFontSize = s.nodeFontSize; |
|
|
|
@ -496,27 +528,28 @@ export default { |
|
|
|
this.edgeType = s.edgeType; |
|
|
|
this.edgeLineWidth = s.edgeLineWidth; |
|
|
|
this.edgeStroke = s.edgeStroke; |
|
|
|
}, |
|
|
|
|
|
|
|
const labelEn = tagToLabelMap[item.current_label]; |
|
|
|
if (labelEn) this.tagStyles[labelEn] = JSON.parse(JSON.stringify(s)); |
|
|
|
// 退出编辑模式:恢复控制栏到初始默认值 |
|
|
|
exitEditingMode() { |
|
|
|
this.editingConfigId = null; |
|
|
|
this.editingConfigLabel = ''; |
|
|
|
|
|
|
|
if (!this.usingConfigIds.includes(item.id)) { |
|
|
|
this.styleGroups.forEach(g => { |
|
|
|
g.configs.forEach(c => { |
|
|
|
if (this.usingConfigIds.includes(c.id) && c.current_label === item.current_label) { |
|
|
|
this.usingConfigIds = this.usingConfigIds.filter(id => id !== c.id); |
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
this.usingConfigIds.push(item.id); |
|
|
|
} |
|
|
|
// 获取当前标签对应的系统初始默认样式 |
|
|
|
const labelEn = tagToLabelMap[this.activeTags]; |
|
|
|
const defaultStyle = this.getInitialTagParams(labelEn); |
|
|
|
|
|
|
|
this.isInitialEcho = true; |
|
|
|
// 1. 恢复控制面板UI |
|
|
|
this.applyStylesToPanel(defaultStyle); |
|
|
|
// 2. 恢复本地缓存(使图谱在该标签下也回归默认预览色) |
|
|
|
this.tagStyles[labelEn] = JSON.parse(JSON.stringify(defaultStyle)); |
|
|
|
|
|
|
|
this.updateAllElements(); |
|
|
|
this.$nextTick(() => { |
|
|
|
setTimeout(() => { |
|
|
|
this.isInitialEcho = false; |
|
|
|
}, 100); |
|
|
|
this.isInitialEcho = false; |
|
|
|
this.updateAllElements(); |
|
|
|
}); |
|
|
|
ElMessage.info("已退出编辑模式,控制栏已恢复默认"); |
|
|
|
}, |
|
|
|
|
|
|
|
syncAndRefresh() { |
|
|
|
@ -534,9 +567,11 @@ export default { |
|
|
|
edgeLineWidth: this.edgeLineWidth, edgeStroke: this.edgeStroke |
|
|
|
}; |
|
|
|
|
|
|
|
// 实时预览更新 |
|
|
|
this.tagStyles[labelEn] = currentStyle; |
|
|
|
this.updateAllElements(); |
|
|
|
|
|
|
|
// 只有在编辑模式下才同步到数据库 |
|
|
|
if (this.editingConfigId) { |
|
|
|
if (this.saveTimer) clearTimeout(this.saveTimer); |
|
|
|
const currentEditId = this.editingConfigId; |
|
|
|
@ -598,12 +633,12 @@ export default { |
|
|
|
} |
|
|
|
this.edgeLineWidth = val; |
|
|
|
}, |
|
|
|
getInitialTagParams(label) { |
|
|
|
const fill = INITIAL_FILL_MAP[label] || '#59d1d4'; |
|
|
|
getInitialTagParams(labelEn) { |
|
|
|
const fill = INITIAL_FILL_MAP[labelEn] || '#59d1d4'; |
|
|
|
return { |
|
|
|
nodeShowLabel: true, nodeFontFamily: 'Microsoft YaHei, sans-serif', nodeFontSize: 12, nodeFontColor: '#ffffff', |
|
|
|
nodeShape: 'circle', nodeSize: 60, nodeFill: fill, |
|
|
|
nodeStroke: INITIAL_STROKE_MAP[label] || '#40999b', nodeLineWidth: 2, edgeShowLabel: true, edgeEndArrow: true, |
|
|
|
nodeStroke: INITIAL_STROKE_MAP[labelEn] || '#40999b', nodeLineWidth: 2, edgeShowLabel: true, edgeEndArrow: true, |
|
|
|
edgeFontFamily: 'Microsoft YaHei, sans-serif', edgeFontSize: 10, edgeFontColor: '#666666', edgeType: 'line', |
|
|
|
edgeLineWidth: 2, edgeStroke: fill |
|
|
|
}; |
|
|
|
@ -628,10 +663,17 @@ export default { |
|
|
|
performTagSwitch(tag) { |
|
|
|
this.activeTags = tag; |
|
|
|
const labelEn = tagToLabelMap[tag]; |
|
|
|
const style = this.tagStyles[labelEn]; |
|
|
|
|
|
|
|
// 如果不在编辑模式,切换标签时让控制栏显示该标签的默认样式 |
|
|
|
const style = this.editingConfigId ? this.tagStyles[labelEn] : this.getInitialTagParams(labelEn); |
|
|
|
|
|
|
|
if (style) { |
|
|
|
this.isInitialEcho = true; |
|
|
|
Object.assign(this, style); |
|
|
|
this.applyStylesToPanel(style); |
|
|
|
// 如果是退出编辑后的切换,也要更新预览缓存 |
|
|
|
if (!this.editingConfigId) { |
|
|
|
this.tagStyles[labelEn] = JSON.parse(JSON.stringify(style)); |
|
|
|
} |
|
|
|
this.$nextTick(() => { this.isInitialEcho = false; }); |
|
|
|
} |
|
|
|
this.updateAllElements(); |
|
|
|
@ -762,7 +804,7 @@ export default { |
|
|
|
const currentActiveConf = activeGroup.configs.find(c => c.current_label === this.activeTags); |
|
|
|
if (currentActiveConf) { |
|
|
|
this.isInitialEcho = true; |
|
|
|
Object.assign(this, currentActiveConf.styles); |
|
|
|
this.applyStylesToPanel(currentActiveConf.styles); |
|
|
|
this.editingConfigId = currentActiveConf.id; |
|
|
|
this.editingConfigLabel = currentActiveConf.current_label; |
|
|
|
this.$nextTick(() => { this.isInitialEcho = false; }); |
|
|
|
@ -783,8 +825,7 @@ export default { |
|
|
|
if (idx > -1) { |
|
|
|
this.usingConfigIds.splice(idx, 1); |
|
|
|
if (this.editingConfigId === item.id) { |
|
|
|
this.editingConfigId = null; |
|
|
|
this.editingConfigLabel = ''; |
|
|
|
this.exitEditingMode(); // 退出应用的同时退出编辑 |
|
|
|
} |
|
|
|
} else { |
|
|
|
this.handleEditConfig(item); |
|
|
|
@ -792,59 +833,36 @@ export default { |
|
|
|
this.updateAllElements(); |
|
|
|
}, |
|
|
|
|
|
|
|
// 修改 validateGroupConstraint 函数 |
|
|
|
validateGroupConstraint(groupName, labelName, excludeId = null) { |
|
|
|
const group = this.styleGroups.find(g => g.group_name === groupName); |
|
|
|
if (!group) return true; |
|
|
|
|
|
|
|
// 1. 检查标签是否重复 |
|
|
|
const isLabelExist = group.configs.some(c => c.current_label === labelName && c.id !== excludeId); |
|
|
|
if (isLabelExist) { |
|
|
|
// 使用 alert 而不是 confirm,因为它不需要处理“取消”逻辑,且必须加 .catch() 规避报错 |
|
|
|
ElMessageBox.alert( |
|
|
|
`方案【${groupName}】中已存在【${labelName}】标签的配置,请先删除旧配置或选择其他方案。`, |
|
|
|
'校验失败', |
|
|
|
{ type: 'error' } |
|
|
|
).catch(() => {}); // 捕获关闭弹窗时的异常 |
|
|
|
ElMessageBox.alert(`方案【${groupName}】中已存在【${labelName}】标签的配置,请先删除旧配置或选择其他方案。`, '校验失败', { type: 'error' }).catch(() => {}); |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
// 2. 检查总量是否已达5个 |
|
|
|
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 true; |
|
|
|
}, |
|
|
|
|
|
|
|
async moveConfigToGroup(config, targetGroup) { |
|
|
|
// 移动时的校验 |
|
|
|
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 { |
|
|
|
const payload = { |
|
|
|
id: config.id, |
|
|
|
canvas_name: config.canvas_name, |
|
|
|
group_name: targetGroup.group_name, |
|
|
|
current_label: config.current_label, |
|
|
|
styles: config.styles, |
|
|
|
is_auto_save: false |
|
|
|
id: config.id, canvas_name: config.canvas_name, group_name: targetGroup.group_name, |
|
|
|
current_label: config.current_label, styles: config.styles, is_auto_save: false |
|
|
|
}; |
|
|
|
const res = await saveGraphStyle(payload); |
|
|
|
if (res.code === 200) { |
|
|
|
ElMessage.success(`已移动至【${targetGroup.group_name}】`); |
|
|
|
await this.fetchConfigs(); |
|
|
|
} |
|
|
|
} catch (err) { |
|
|
|
ElMessage.error("操作失败"); |
|
|
|
} |
|
|
|
} catch (err) { ElMessage.error("操作失败"); } |
|
|
|
}, |
|
|
|
|
|
|
|
handleSaveClick() { |
|
|
|
@ -854,22 +872,12 @@ export default { |
|
|
|
}, |
|
|
|
|
|
|
|
async confirmSave() { |
|
|
|
if (!this.saveForm.group_name?.trim() || !this.saveForm.canvas_name?.trim()) { |
|
|
|
return ElMessage.warning("请完善名称"); |
|
|
|
} |
|
|
|
|
|
|
|
// 保存时的校验 |
|
|
|
if (!this.validateGroupConstraint(this.saveForm.group_name.trim(), this.activeTags)) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (!this.saveForm.group_name?.trim() || !this.saveForm.canvas_name?.trim()) return ElMessage.warning("请完善名称"); |
|
|
|
if (!this.validateGroupConstraint(this.saveForm.group_name.trim(), this.activeTags)) return; |
|
|
|
const labelEn = tagToLabelMap[this.activeTags]; |
|
|
|
const payload = { |
|
|
|
canvas_name: this.saveForm.canvas_name.trim(), |
|
|
|
group_name: this.saveForm.group_name.trim(), |
|
|
|
current_label: this.activeTags, |
|
|
|
styles: { ...this.tagStyles[labelEn] }, |
|
|
|
is_auto_save: false |
|
|
|
canvas_name: this.saveForm.canvas_name.trim(), group_name: this.saveForm.group_name.trim(), |
|
|
|
current_label: this.activeTags, styles: { ...this.tagStyles[labelEn] }, is_auto_save: false |
|
|
|
}; |
|
|
|
const res = await saveGraphStyle(payload); |
|
|
|
if (res.code === 200) { |
|
|
|
@ -878,95 +886,73 @@ export default { |
|
|
|
await this.fetchConfigs(); |
|
|
|
} |
|
|
|
}, |
|
|
|
// =========================================================================== |
|
|
|
|
|
|
|
async applyWholeGroup(group) { |
|
|
|
if (this.saveTimer) clearTimeout(this.saveTimer); |
|
|
|
this.isInitialEcho = true; |
|
|
|
this.editingConfigId = null; |
|
|
|
this.editingConfigLabel = ''; |
|
|
|
|
|
|
|
try { |
|
|
|
const REQUIRED_TAGS = ['疾病', '症状', '药品', '检查', '其他']; |
|
|
|
const currentLabels = group.configs.map(conf => conf.current_label); |
|
|
|
const hasTags = new Set(currentLabels); |
|
|
|
const missingTags = REQUIRED_TAGS.filter(tag => !hasTags.has(tag)); |
|
|
|
|
|
|
|
const missingTags = REQUIRED_TAGS.filter(tag => !new Set(currentLabels).has(tag)); |
|
|
|
if (missingTags.length > 0) { |
|
|
|
this.isInitialEcho = false; |
|
|
|
return ElMessageBox.alert( |
|
|
|
`该方案配置不完整,无法应用。<br/>缺失:<b style="color: #f56c6c">${missingTags.join('、')}</b>`, |
|
|
|
'提示', { dangerouslyUseHTMLString: true, type: 'warning' } |
|
|
|
); |
|
|
|
return ElMessageBox.alert(`该方案配置不完整,缺失:${missingTags.join('、')}`, '提示', { type: 'warning' }).catch(() => {}); |
|
|
|
} |
|
|
|
|
|
|
|
this.usingConfigIds = group.configs.map(c => c.id); |
|
|
|
const res = await applyGraphStyleGroup(group.id); |
|
|
|
if (res.code === 200) { |
|
|
|
await this.fetchConfigs(); |
|
|
|
if (group.configs.length > 0) { |
|
|
|
this.handleEditConfig(group.configs[0]); |
|
|
|
} |
|
|
|
// 应用全案后,默认开启第一个配置的编辑模式 |
|
|
|
if (group.configs.length > 0) this.handleEditConfig(group.configs[0]); |
|
|
|
ElMessage.success(`已应用方案【${group.group_name}】`); |
|
|
|
} |
|
|
|
} catch (err) { |
|
|
|
this.isInitialEcho = false; |
|
|
|
ElMessage.error("切换失败"); |
|
|
|
} |
|
|
|
} catch (err) { this.isInitialEcho = false; ElMessage.error("切换失败"); } |
|
|
|
}, |
|
|
|
|
|
|
|
resetStyle() { |
|
|
|
const labelEn = tagToLabelMap[this.activeTags]; |
|
|
|
const initial = this.getInitialTagParams(labelEn); |
|
|
|
this.tagStyles[labelEn] = initial; |
|
|
|
this.isInitialEcho = true; |
|
|
|
Object.assign(this, initial); |
|
|
|
this.applyStylesToPanel(initial); |
|
|
|
this.$nextTick(() => { |
|
|
|
this.isInitialEcho = false; |
|
|
|
this.syncAndRefresh(); |
|
|
|
}); |
|
|
|
}, |
|
|
|
|
|
|
|
async deleteSingleConfig(id) { |
|
|
|
if (this.usingConfigIds.includes(id)) return ElMessage.error("应用中无法删除"); |
|
|
|
try { |
|
|
|
await ElMessageBox.confirm('确定删除吗?', '提示'); |
|
|
|
const res = await deleteGraphStyle(id); |
|
|
|
if (res.code === 200) { |
|
|
|
ElMessage.success("删除成功"); |
|
|
|
this.fetchConfigs(); |
|
|
|
} |
|
|
|
if (res.code === 200) { ElMessage.success("删除成功"); this.fetchConfigs(); } |
|
|
|
} catch (err) { } |
|
|
|
}, |
|
|
|
|
|
|
|
async deleteGroup(groupId) { |
|
|
|
const group = this.styleGroups.find(g => g.id === groupId); |
|
|
|
if (!group || group.is_active) return ElMessage.error("应用中无法删除"); |
|
|
|
try { |
|
|
|
await ElMessageBox.confirm('确定删除全案吗?', '提示'); |
|
|
|
const res = await deleteGraphStyleGroup(groupId); |
|
|
|
if (res.code === 200) { |
|
|
|
ElMessage.success("已删除"); |
|
|
|
this.fetchConfigs(); |
|
|
|
} |
|
|
|
if (res.code === 200) { ElMessage.success("已删除"); this.fetchConfigs(); } |
|
|
|
} catch (err) { } |
|
|
|
}, |
|
|
|
|
|
|
|
async handleUnifiedBatchDelete() { |
|
|
|
if (this.checkedConfigIds.length === 0 && this.checkedGroupIds.length === 0) return; |
|
|
|
try { |
|
|
|
await ElMessageBox.confirm('确定批量删除吗?', '提示'); |
|
|
|
if (this.checkedGroupIds.length > 0) { |
|
|
|
for (const gid of this.checkedGroupIds) await deleteGraphStyleGroup(gid); |
|
|
|
} |
|
|
|
if (this.checkedConfigIds.length > 0) { |
|
|
|
await batchDeleteGraphStyle({ ids: this.checkedConfigIds }); |
|
|
|
} |
|
|
|
ElMessage.success("成功"); |
|
|
|
this.clearSelection(); |
|
|
|
this.fetchConfigs(); |
|
|
|
if (this.checkedGroupIds.length > 0) { for (const gid of this.checkedGroupIds) await deleteGraphStyleGroup(gid); } |
|
|
|
if (this.checkedConfigIds.length > 0) { await batchDeleteGraphStyle({ ids: this.checkedConfigIds }); } |
|
|
|
ElMessage.success("成功"); this.clearSelection(); this.fetchConfigs(); |
|
|
|
} catch (e) { } |
|
|
|
}, |
|
|
|
clearSelection() { |
|
|
|
this.checkedConfigIds = []; |
|
|
|
this.checkedGroupIds = []; |
|
|
|
}, |
|
|
|
|
|
|
|
clearSelection() { this.checkedConfigIds = []; this.checkedGroupIds = []; }, |
|
|
|
handleResize() { |
|
|
|
if (this._graph && this.$refs.graphContainer) { |
|
|
|
this._graph.setSize(this.$refs.graphContainer.clientWidth, this.$refs.graphContainer.clientHeight); |
|
|
|
|