diff --git a/vue/src/system/GraphStyle.vue b/vue/src/system/GraphStyle.vue index f4e43c3..1c502f3 100644 --- a/vue/src/system/GraphStyle.vue +++ b/vue/src/system/GraphStyle.vue @@ -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( - `该方案配置不完整,无法应用。
缺失:${missingTags.join('、')}`, - '提示', { 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);