feat: 优化节点图标加载逻辑,添加图标加载失败处理及稳定的 action 图标映射

This commit is contained in:
zhanghao 2026-09-23 11:36:00 +08:00
parent 92540f594d
commit 8c3647b300
2 changed files with 52 additions and 1 deletions

View File

@ -7,7 +7,7 @@
<div class="node-title__main">
<div class="node-title__identity">
<div class="node-title__icon">
<img v-if="props.icon" :src="props.icon" alt="" />
<img v-if="displayedIcon" :src="displayedIcon" alt="" @error="handleIconLoadError" />
<el-icon v-else><Operation /></el-icon>
</div>
@ -76,6 +76,7 @@ import { computed, nextTick, onMounted, ref, watch } from "vue";
import { CloseBold, Delete, EditPen, FolderRemove, Operation, Select, VideoPlay } from "@element-plus/icons-vue";
import { ElMessageBox } from "element-plus";
import { emitter } from "@/utils/eventBus";
import { getFlowNodeIconByAction } from "../config";
const props = defineProps({
icon: { type: String, default: "" },
@ -90,7 +91,9 @@ const emits = defineEmits(["setNodeName"]);
const isEditing = ref(false);
const localName = ref("");
const nameInputRef = ref();
const displayedIcon = ref("");
const isSelectArea = computed(() => props.nodeType === "selectArea");
const configuredIcon = computed(() => getFlowNodeIconByAction(props.nodeProperties?.action));
const flowContextActions = new Set([
"NONE",
"START",
@ -113,6 +116,32 @@ const typeLabel = computed(() => {
return { EDGE: "设备", LLM: "模型", AE: "评估" }[props.nodeType] || props.nodeType;
});
/**
* 重置节点当前展示的图标。
*
* 实现思路:内置节点优先根据稳定的 action 使用当前生产包中的图标,避免浏览器先请求
* 后端流程数据里保存的开发地址或旧构建地址;无法识别的扩展节点继续使用后端自定义图标。
*/
const resetDisplayedIcon = () => {
displayedIcon.value = configuredIcon.value || props.icon || "";
};
watch([() => props.icon, configuredIcon], resetDisplayedIcon, { immediate: true });
/**
* 处理节点图标加载失败。
*
* 实现思路:扩展节点的自定义 URL 失效时尝试 action 对应的内置图标;如果当前展示的
* 已经是内置图标或没有可匹配图标,则使用 Element Plus 通用图标,避免破图占位。
*/
const handleIconLoadError = () => {
if (configuredIcon.value && displayedIcon.value !== configuredIcon.value) {
displayedIcon.value = configuredIcon.value;
return;
}
displayedIcon.value = "";
};
/**
* 处理 sourceName 对应的数据或交互;作用范围仅限当前组件或模块。
*/

View File

@ -977,3 +977,25 @@ function handler(params) {
}]
},
]
// 流程数据会长期保存,而 Vite 生成的图标地址只对当前构建有效。
// 这里用稳定的 action 建立索引,使旧流程或后端缺失 icon 时可以恢复为当前版本的内置图标。
const flowNodeIconByAction = new Map(
collapseList.flatMap((group) => group.nodeList)
.filter((node) => node.action && node.icon)
.map((node) => [String(node.action).trim().toUpperCase(), node.icon]),
)
/**
* 根据流程节点 action 获取当前版本的内置图标。
*
* 实现思路:action 是流程协议中的稳定标识,比构建生成的资源 URL 更适合跨版本匹配;
* 未识别的 action 返回空字符串,由调用方继续使用后端自定义图标或通用占位图标。
*
* @param {*} action 流程节点的动作标识
* @returns {string} 当前构建对应的图标地址,未匹配时返回空字符串
*/
export const getFlowNodeIconByAction = (action) => {
const normalizedAction = String(action || '').trim().toUpperCase()
return normalizedAction ? flowNodeIconByAction.get(normalizedAction) || '' : ''
}