From d75e4ac32db7a8fadfe545240f5f6c738274054d Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 5 Jul 2026 01:02:49 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E8=A1=A8=E5=8D=95?= =?UTF-8?q?=E6=A0=A1=E9=AA=8C=E5=9B=9E=E8=B0=83=E5=BC=83=E7=94=A8=E4=B8=8E?= =?UTF-8?q?=20BPMN=20=E5=91=8A=E8=AD=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将 web-antd、web-antdv-next、web-ele 中的表单 validator 从 callback 改为 Promise - 为 BPMN viewer 的 xml 增加空值兜底,避免 undefined prop warning - 移除流程详情用户选择弹窗的无效 title 透传 - 调整 simple-bpm-viewer 函数声明顺序,避免 immediate watch 初始化时报错 --- apps/web-antd/src/api/iot/thingmodel/index.ts | 38 ++++---- .../package/designer/ProcessViewer.vue | 10 +- .../bpm/model/form/modules/basic-info.vue | 10 +- .../processInstance/create/modules/form.vue | 4 +- .../detail/modules/bpm-viewer.vue | 12 ++- .../detail/modules/simple-bpm-viewer.vue | 92 +++++++++---------- .../detail/modules/time-line.vue | 1 - .../src/views/iot/rule/scene/modules/form.vue | 14 ++- .../thingmodel/modules/data-specs/enum.vue | 29 +++--- .../thingmodel/modules/data-specs/number.vue | 33 +++---- .../thingmodel/modules/data-specs/struct.vue | 7 +- .../src/api/iot/thingmodel/index.ts | 38 ++++---- .../package/designer/ProcessViewer.vue | 10 +- .../processInstance/create/modules/form.vue | 4 +- .../detail/modules/bpm-viewer.vue | 12 ++- .../detail/modules/simple-bpm-viewer.vue | 92 +++++++++---------- .../detail/modules/time-line.vue | 9 +- apps/web-ele/src/api/iot/thingmodel/index.ts | 44 ++++----- .../package/designer/ProcessViewer.vue | 10 +- .../bpm/model/form/modules/basic-info.vue | 12 +-- .../processInstance/create/modules/form.vue | 4 +- .../detail/modules/bpm-viewer.vue | 12 ++- .../detail/modules/simple-bpm-viewer.vue | 92 +++++++++---------- .../detail/modules/time-line.vue | 1 - .../src/views/iot/rule/scene/modules/form.vue | 14 ++- .../thingmodel/modules/data-specs/enum.vue | 33 +++---- .../thingmodel/modules/data-specs/number.vue | 39 ++++---- .../thingmodel/modules/data-specs/struct.vue | 9 +- 28 files changed, 313 insertions(+), 372 deletions(-) diff --git a/apps/web-antd/src/api/iot/thingmodel/index.ts b/apps/web-antd/src/api/iot/thingmodel/index.ts index 0ccf1f069..8e5c7ff43 100644 --- a/apps/web-antd/src/api/iot/thingmodel/index.ts +++ b/apps/web-antd/src/api/iot/thingmodel/index.ts @@ -112,41 +112,37 @@ export namespace ThingModelApi { /** 生成「必填 + 数字」类校验器:拼到 size / length / 枚举值上 */ function buildRequiredNumberValidator(label: string) { - return (_rule: any, value: any, callback: any) => { + return (_rule: any, value: any) => { if (isEmpty(value)) { - callback(new Error(`${label}不能为空`)); - return; + return Promise.reject(new Error(`${label}不能为空`)); } if (Number.isNaN(Number(value))) { - callback(new Error(`${label}必须是数字`)); - return; + return Promise.reject(new Error(`${label}必须是数字`)); } - callback(); + return Promise.resolve(); }; } /** 生成「标识符样式」名称校验器:开头需为中文 / 英文 / 数字,整体仅允许中文、英文、数字、下划线、短划线,长度 ≤ 20 */ export function buildIdentifierLikeNameValidator(label: string) { - return (_rule: any, value: string, callback: any) => { + return (_rule: any, value: string) => { if (isEmpty(value)) { - callback(new Error(`${label}不能为空`)); - return; + return Promise.reject(new Error(`${label}不能为空`)); } if (!/^[一-龥A-Za-z0-9]/.test(value)) { - callback(new Error(`${label}必须以中文、英文字母或数字开头`)); - return; + return Promise.reject( + new Error(`${label}必须以中文、英文字母或数字开头`), + ); } if (!/^[一-龥A-Za-z0-9][\w一-龥-]*$/.test(value)) { - callback( + return Promise.reject( new Error(`${label}只能包含中文、英文字母、数字、下划线和短划线`), ); - return; } if (value.length > 20) { - callback(new Error(`${label}长度不能超过 20 个字符`)); - return; + return Promise.reject(new Error(`${label}长度不能超过 20 个字符`)); } - callback(); + return Promise.resolve(); }; } @@ -170,7 +166,7 @@ export const ThingModelFormRules: Record = { trigger: 'blur', }, { - validator: (_rule: any, value: string, callback: any) => { + validator: (_rule: any, value: string) => { const reservedKeywords = [ 'set', 'get', @@ -181,18 +177,16 @@ export const ThingModelFormRules: Record = { 'value', ]; if (reservedKeywords.includes(value)) { - callback( + return Promise.reject( new Error( 'set, get, post, property, event, time, value 是系统保留字段,不能用于标识符定义', ), ); - return; } if (/^\d+$/.test(value)) { - callback(new Error('标识符不能是纯数字')); - return; + return Promise.reject(new Error('标识符不能是纯数字')); } - callback(); + return Promise.resolve(); }, trigger: 'blur', }, diff --git a/apps/web-antd/src/views/bpm/components/bpmn-process-designer/package/designer/ProcessViewer.vue b/apps/web-antd/src/views/bpm/components/bpmn-process-designer/package/designer/ProcessViewer.vue index 6696087ae..209cff686 100644 --- a/apps/web-antd/src/views/bpm/components/bpmn-process-designer/package/designer/ProcessViewer.vue +++ b/apps/web-antd/src/views/bpm/components/bpmn-process-designer/package/designer/ProcessViewer.vue @@ -15,8 +15,8 @@ import '../theme/index.scss'; const props = defineProps({ xml: { + default: '', type: String, - required: true, }, view: { type: Object, @@ -185,12 +185,12 @@ const onSelectElement = (element: any) => { }; /** 初始化 BPMN 视图 */ -const importXML = async (xml: string) => { +const importXML = async (xml?: string) => { // 清空流程图 clearViewer(); // 初始化流程图 - if (xml !== null && xml !== '') { + if (xml) { try { bpmnViewer.value = new BpmnViewer({ additionalModules: [MoveCanvasModule], @@ -303,7 +303,7 @@ const setProcessStatus = (view: any) => { watch( () => props.xml, (newXml) => { - importXML(newXml); + importXML(newXml || ''); }, { immediate: true }, ); @@ -318,7 +318,7 @@ watch( /** mounted:初始化 */ onMounted(() => { - importXML(props.xml); + importXML(props.xml || ''); setProcessStatus(props.view); }); diff --git a/apps/web-antd/src/views/bpm/model/form/modules/basic-info.vue b/apps/web-antd/src/views/bpm/model/form/modules/basic-info.vue index a297e253e..7116a3632 100644 --- a/apps/web-antd/src/views/bpm/model/form/modules/basic-info.vue +++ b/apps/web-antd/src/views/bpm/model/form/modules/basic-info.vue @@ -68,20 +68,18 @@ const rules: Record = { key: [ { required: true, message: '流程标识不能为空', trigger: 'blur' }, { - validator: (_rule: any, value: string, callback: any) => { + validator: (_rule: any, value: string) => { if (!value) { - callback(); - return; + return Promise.resolve(); } if (!/^[a-z_][-\w.$]*$/i.test(value)) { - callback( + return Promise.reject( new Error( '只能包含字母、数字、下划线、连字符和点号,且必须以字母或下划线开头', ), ); - return; } - callback(); + return Promise.resolve(); }, trigger: 'blur', }, diff --git a/apps/web-antd/src/views/bpm/processInstance/create/modules/form.vue b/apps/web-antd/src/views/bpm/processInstance/create/modules/form.vue index d72c04122..eb619126e 100644 --- a/apps/web-antd/src/views/bpm/processInstance/create/modules/form.vue +++ b/apps/web-antd/src/views/bpm/processInstance/create/modules/form.vue @@ -67,7 +67,7 @@ const startUserSelectTasks = ref([]); const startUserSelectAssignees = ref>({}); const tempStartUserSelectAssignees = ref>({}); -const bpmnXML = ref(undefined); +const bpmnXML = ref(''); const simpleJson = ref(undefined); const activeTab = ref('form'); @@ -158,7 +158,7 @@ async function initProcessInfo(row: any, formVariables?: any) { const processDefinitionDetail: BpmProcessDefinitionApi.ProcessDefinition = await getProcessDefinition(row.id); if (processDefinitionDetail) { - bpmnXML.value = processDefinitionDetail.bpmnXml; + bpmnXML.value = processDefinitionDetail.bpmnXml || ''; simpleJson.value = processDefinitionDetail.simpleModel; } // 情况二:业务表单 diff --git a/apps/web-antd/src/views/bpm/processInstance/detail/modules/bpm-viewer.vue b/apps/web-antd/src/views/bpm/processInstance/detail/modules/bpm-viewer.vue index 3f86e68f3..36440ca1c 100644 --- a/apps/web-antd/src/views/bpm/processInstance/detail/modules/bpm-viewer.vue +++ b/apps/web-antd/src/views/bpm/processInstance/detail/modules/bpm-viewer.vue @@ -9,7 +9,7 @@ const props = withDefaults( defineProps<{ bpmnXml?: string; loading?: boolean; // 是否加载中 - modelView?: object; + modelView?: Record; }>(), { loading: false, @@ -29,8 +29,10 @@ watch( async (newModelView) => { // 加载最新 if (newModelView) { - // @ts-expect-error: viewer instance type is broader than local ref typing - view.value = newModelView; + view.value = { + ...newModelView, + bpmnXml: newModelView.bpmnXml || '', + }; } }, { immediate: true }, @@ -40,7 +42,7 @@ watch( watch( () => props.bpmnXml, (value) => { - view.value.bpmnXml = value; + view.value.bpmnXml = value || ''; }, ); @@ -52,7 +54,7 @@ watch( > diff --git a/apps/web-antd/src/views/bpm/processInstance/detail/modules/simple-bpm-viewer.vue b/apps/web-antd/src/views/bpm/processInstance/detail/modules/simple-bpm-viewer.vue index 6e4c65982..1985b2d45 100644 --- a/apps/web-antd/src/views/bpm/processInstance/detail/modules/simple-bpm-viewer.vue +++ b/apps/web-antd/src/views/bpm/processInstance/detail/modules/simple-bpm-viewer.vue @@ -26,57 +26,14 @@ const simpleModel = ref({}); const tasks = ref([]); // 用户任务 const processInstance = ref(); // 流程实例 -/** 监控模型视图 包括任务列表、进行中的活动节点编号等 */ -watch( - () => props.modelView, - async (newModelView) => { - if (newModelView) { - tasks.value = newModelView.tasks; - processInstance.value = newModelView.processInstance; - // 已经拒绝的活动节点编号集合,只包括 UserTask - const rejectedTaskActivityIds: string[] = - newModelView.rejectedTaskActivityIds; - // 进行中的活动节点编号集合, 只包括 UserTask - const unfinishedTaskActivityIds: string[] = - newModelView.unfinishedTaskActivityIds; - // 已经完成的活动节点编号集合, 包括 UserTask、Gateway 等 - const finishedActivityIds: string[] = - newModelView.finishedTaskActivityIds; - // 已经完成的连线节点编号集合,只包括 SequenceFlow - const finishedSequenceFlowActivityIds: string[] = - newModelView.finishedSequenceFlowActivityIds; - setSimpleModelNodeTaskStatus( - newModelView.simpleModel, - newModelView.processInstance?.status, - rejectedTaskActivityIds, - unfinishedTaskActivityIds, - finishedActivityIds, - finishedSequenceFlowActivityIds, - ); - simpleModel.value = newModelView.simpleModel || {}; - } - }, - { immediate: true }, -); - -/** 监控模型结构数据 */ -watch( - () => props.simpleJson, - async (value) => { - if (value) { - simpleModel.value = JSON.parse(value); - } - }, -); - -const setSimpleModelNodeTaskStatus = ( +function setSimpleModelNodeTaskStatus( simpleModel: SimpleFlowNode | undefined, processStatus: number, rejectedTaskActivityIds: string[], unfinishedTaskActivityIds: string[], finishedActivityIds: string[], finishedSequenceFlowActivityIds: string[], -) => { +) { if (!simpleModel) { return; } @@ -166,7 +123,50 @@ const setSimpleModelNodeTaskStatus = ( finishedActivityIds, finishedSequenceFlowActivityIds, ); -}; +} + +/** 监控模型视图 包括任务列表、进行中的活动节点编号等 */ +watch( + () => props.modelView, + async (newModelView) => { + if (newModelView) { + tasks.value = newModelView.tasks; + processInstance.value = newModelView.processInstance; + // 已经拒绝的活动节点编号集合,只包括 UserTask + const rejectedTaskActivityIds: string[] = + newModelView.rejectedTaskActivityIds; + // 进行中的活动节点编号集合, 只包括 UserTask + const unfinishedTaskActivityIds: string[] = + newModelView.unfinishedTaskActivityIds; + // 已经完成的活动节点编号集合, 包括 UserTask、Gateway 等 + const finishedActivityIds: string[] = + newModelView.finishedTaskActivityIds; + // 已经完成的连线节点编号集合,只包括 SequenceFlow + const finishedSequenceFlowActivityIds: string[] = + newModelView.finishedSequenceFlowActivityIds; + setSimpleModelNodeTaskStatus( + newModelView.simpleModel, + newModelView.processInstance?.status, + rejectedTaskActivityIds, + unfinishedTaskActivityIds, + finishedActivityIds, + finishedSequenceFlowActivityIds, + ); + simpleModel.value = newModelView.simpleModel || {}; + } + }, + { immediate: true }, +); + +/** 监控模型结构数据 */ +watch( + () => props.simpleJson, + async (value) => { + if (value) { + simpleModel.value = JSON.parse(value); + } + }, +);