fix: 修复表单校验回调弃用与 BPMN 告警

- 将 web-antd、web-antdv-next、web-ele 中的表单 validator 从 callback 改为 Promise

- 为 BPMN viewer 的 xml 增加空值兜底,避免 undefined prop warning

- 移除流程详情用户选择弹窗的无效 title 透传

- 调整 simple-bpm-viewer 函数声明顺序,避免 immediate watch 初始化时报错
This commit is contained in:
YunaiV
2026-07-05 01:02:49 +08:00
parent 9f990e0ab3
commit d75e4ac32d
28 changed files with 313 additions and 372 deletions

View File

@@ -112,41 +112,37 @@ export namespace ThingModelApi {
/** 生成「必填 + 数字」类校验器:拼到 size / length / 枚举值上 */ /** 生成「必填 + 数字」类校验器:拼到 size / length / 枚举值上 */
function buildRequiredNumberValidator(label: string) { function buildRequiredNumberValidator(label: string) {
return (_rule: any, value: any, callback: any) => { return (_rule: any, value: any) => {
if (isEmpty(value)) { if (isEmpty(value)) {
callback(new Error(`${label}不能为空`)); return Promise.reject(new Error(`${label}不能为空`));
return;
} }
if (Number.isNaN(Number(value))) { if (Number.isNaN(Number(value))) {
callback(new Error(`${label}必须是数字`)); return Promise.reject(new Error(`${label}必须是数字`));
return;
} }
callback(); return Promise.resolve();
}; };
} }
/** 生成「标识符样式」名称校验器:开头需为中文 / 英文 / 数字,整体仅允许中文、英文、数字、下划线、短划线,长度 ≤ 20 */ /** 生成「标识符样式」名称校验器:开头需为中文 / 英文 / 数字,整体仅允许中文、英文、数字、下划线、短划线,长度 ≤ 20 */
export function buildIdentifierLikeNameValidator(label: string) { export function buildIdentifierLikeNameValidator(label: string) {
return (_rule: any, value: string, callback: any) => { return (_rule: any, value: string) => {
if (isEmpty(value)) { if (isEmpty(value)) {
callback(new Error(`${label}不能为空`)); return Promise.reject(new Error(`${label}不能为空`));
return;
} }
if (!/^[一-龥A-Za-z0-9]/.test(value)) { if (!/^[一-龥A-Za-z0-9]/.test(value)) {
callback(new Error(`${label}必须以中文、英文字母或数字开头`)); return Promise.reject(
return; new Error(`${label}必须以中文、英文字母或数字开头`),
);
} }
if (!/^[一-龥A-Za-z0-9][\w一-龥-]*$/.test(value)) { if (!/^[一-龥A-Za-z0-9][\w一-龥-]*$/.test(value)) {
callback( return Promise.reject(
new Error(`${label}只能包含中文、英文字母、数字、下划线和短划线`), new Error(`${label}只能包含中文、英文字母、数字、下划线和短划线`),
); );
return;
} }
if (value.length > 20) { if (value.length > 20) {
callback(new Error(`${label}长度不能超过 20 个字符`)); return Promise.reject(new Error(`${label}长度不能超过 20 个字符`));
return;
} }
callback(); return Promise.resolve();
}; };
} }
@@ -170,7 +166,7 @@ export const ThingModelFormRules: Record<string, Rule[]> = {
trigger: 'blur', trigger: 'blur',
}, },
{ {
validator: (_rule: any, value: string, callback: any) => { validator: (_rule: any, value: string) => {
const reservedKeywords = [ const reservedKeywords = [
'set', 'set',
'get', 'get',
@@ -181,18 +177,16 @@ export const ThingModelFormRules: Record<string, Rule[]> = {
'value', 'value',
]; ];
if (reservedKeywords.includes(value)) { if (reservedKeywords.includes(value)) {
callback( return Promise.reject(
new Error( new Error(
'set, get, post, property, event, time, value 是系统保留字段,不能用于标识符定义', 'set, get, post, property, event, time, value 是系统保留字段,不能用于标识符定义',
), ),
); );
return;
} }
if (/^\d+$/.test(value)) { if (/^\d+$/.test(value)) {
callback(new Error('标识符不能是纯数字')); return Promise.reject(new Error('标识符不能是纯数字'));
return;
} }
callback(); return Promise.resolve();
}, },
trigger: 'blur', trigger: 'blur',
}, },

View File

@@ -15,8 +15,8 @@ import '../theme/index.scss';
const props = defineProps({ const props = defineProps({
xml: { xml: {
default: '',
type: String, type: String,
required: true,
}, },
view: { view: {
type: Object, type: Object,
@@ -185,12 +185,12 @@ const onSelectElement = (element: any) => {
}; };
/** 初始化 BPMN 视图 */ /** 初始化 BPMN 视图 */
const importXML = async (xml: string) => { const importXML = async (xml?: string) => {
// 清空流程图 // 清空流程图
clearViewer(); clearViewer();
// 初始化流程图 // 初始化流程图
if (xml !== null && xml !== '') { if (xml) {
try { try {
bpmnViewer.value = new BpmnViewer({ bpmnViewer.value = new BpmnViewer({
additionalModules: [MoveCanvasModule], additionalModules: [MoveCanvasModule],
@@ -303,7 +303,7 @@ const setProcessStatus = (view: any) => {
watch( watch(
() => props.xml, () => props.xml,
(newXml) => { (newXml) => {
importXML(newXml); importXML(newXml || '');
}, },
{ immediate: true }, { immediate: true },
); );
@@ -318,7 +318,7 @@ watch(
/** mounted初始化 */ /** mounted初始化 */
onMounted(() => { onMounted(() => {
importXML(props.xml); importXML(props.xml || '');
setProcessStatus(props.view); setProcessStatus(props.view);
}); });

View File

@@ -68,20 +68,18 @@ const rules: Record<string, Rule[]> = {
key: [ key: [
{ required: true, message: '流程标识不能为空', trigger: 'blur' }, { required: true, message: '流程标识不能为空', trigger: 'blur' },
{ {
validator: (_rule: any, value: string, callback: any) => { validator: (_rule: any, value: string) => {
if (!value) { if (!value) {
callback(); return Promise.resolve();
return;
} }
if (!/^[a-z_][-\w.$]*$/i.test(value)) { if (!/^[a-z_][-\w.$]*$/i.test(value)) {
callback( return Promise.reject(
new Error( new Error(
'只能包含字母、数字、下划线、连字符和点号,且必须以字母或下划线开头', '只能包含字母、数字、下划线、连字符和点号,且必须以字母或下划线开头',
), ),
); );
return;
} }
callback(); return Promise.resolve();
}, },
trigger: 'blur', trigger: 'blur',
}, },

View File

@@ -67,7 +67,7 @@ const startUserSelectTasks = ref<UserTask[]>([]);
const startUserSelectAssignees = ref<Record<string, number[]>>({}); const startUserSelectAssignees = ref<Record<string, number[]>>({});
const tempStartUserSelectAssignees = ref<Record<string, number[]>>({}); const tempStartUserSelectAssignees = ref<Record<string, number[]>>({});
const bpmnXML = ref<string | undefined>(undefined); const bpmnXML = ref('');
const simpleJson = ref<string | undefined>(undefined); const simpleJson = ref<string | undefined>(undefined);
const activeTab = ref('form'); const activeTab = ref('form');
@@ -158,7 +158,7 @@ async function initProcessInfo(row: any, formVariables?: any) {
const processDefinitionDetail: BpmProcessDefinitionApi.ProcessDefinition = const processDefinitionDetail: BpmProcessDefinitionApi.ProcessDefinition =
await getProcessDefinition(row.id); await getProcessDefinition(row.id);
if (processDefinitionDetail) { if (processDefinitionDetail) {
bpmnXML.value = processDefinitionDetail.bpmnXml; bpmnXML.value = processDefinitionDetail.bpmnXml || '';
simpleJson.value = processDefinitionDetail.simpleModel; simpleJson.value = processDefinitionDetail.simpleModel;
} }
// 情况二:业务表单 // 情况二:业务表单

View File

@@ -9,7 +9,7 @@ const props = withDefaults(
defineProps<{ defineProps<{
bpmnXml?: string; bpmnXml?: string;
loading?: boolean; // 是否加载中 loading?: boolean; // 是否加载中
modelView?: object; modelView?: Record<string, any>;
}>(), }>(),
{ {
loading: false, loading: false,
@@ -29,8 +29,10 @@ watch(
async (newModelView) => { async (newModelView) => {
// 加载最新 // 加载最新
if (newModelView) { if (newModelView) {
// @ts-expect-error: viewer instance type is broader than local ref typing view.value = {
view.value = newModelView; ...newModelView,
bpmnXml: newModelView.bpmnXml || '',
};
} }
}, },
{ immediate: true }, { immediate: true },
@@ -40,7 +42,7 @@ watch(
watch( watch(
() => props.bpmnXml, () => props.bpmnXml,
(value) => { (value) => {
view.value.bpmnXml = value; view.value.bpmnXml = value || '';
}, },
); );
</script> </script>
@@ -52,7 +54,7 @@ watch(
> >
<MyProcessViewer <MyProcessViewer
key="processViewer" key="processViewer"
:xml="view.bpmnXml" :xml="view.bpmnXml || ''"
:view="view" :view="view"
class="h-full min-h-[500px] w-full" class="h-full min-h-[500px] w-full"
/> />

View File

@@ -26,57 +26,14 @@ const simpleModel = ref<any>({});
const tasks = ref([]); // 用户任务 const tasks = ref([]); // 用户任务
const processInstance = ref(); // 流程实例 const processInstance = ref(); // 流程实例
/** 监控模型视图 包括任务列表、进行中的活动节点编号等 */ function setSimpleModelNodeTaskStatus(
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 = (
simpleModel: SimpleFlowNode | undefined, simpleModel: SimpleFlowNode | undefined,
processStatus: number, processStatus: number,
rejectedTaskActivityIds: string[], rejectedTaskActivityIds: string[],
unfinishedTaskActivityIds: string[], unfinishedTaskActivityIds: string[],
finishedActivityIds: string[], finishedActivityIds: string[],
finishedSequenceFlowActivityIds: string[], finishedSequenceFlowActivityIds: string[],
) => { ) {
if (!simpleModel) { if (!simpleModel) {
return; return;
} }
@@ -166,7 +123,50 @@ const setSimpleModelNodeTaskStatus = (
finishedActivityIds, finishedActivityIds,
finishedSequenceFlowActivityIds, 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);
}
},
);
</script> </script>
<template> <template>
<div v-loading="loading"> <div v-loading="loading">

View File

@@ -482,7 +482,6 @@ defineExpose({ setCustomApproveUsers, batchSetCustomApproveUsers });
class="w-3/5" class="w-3/5"
v-model:value="selectedUsers" v-model:value="selectedUsers"
:multiple="true" :multiple="true"
title="选择用户"
@confirm="handleUserSelectConfirm" @confirm="handleUserSelectConfirm"
@closed="handleUserSelectClosed" @closed="handleUserSelectClosed"
@cancel="handleUserSelectCancel" @cancel="handleUserSelectCancel"

View File

@@ -122,23 +122,21 @@ function normalizeFormData(result: any): RuleSceneApi.SceneRule {
} }
/** 触发器校验 */ /** 触发器校验 */
function validateTriggers(_rule: any, value: any, callback: any) { function validateTriggers(_rule: any, value: any) {
const error = validateSceneRuleTriggers(value); const error = validateSceneRuleTriggers(value);
if (error) { if (error) {
callback(new Error(error)); return Promise.reject(new Error(error));
return;
} }
callback(); return Promise.resolve();
} }
/** 执行器校验 */ /** 执行器校验 */
function validateActions(_rule: any, value: any, callback: any) { function validateActions(_rule: any, value: any) {
const error = validateSceneRuleActions(value); const error = validateSceneRuleActions(value);
if (error) { if (error) {
callback(new Error(error)); return Promise.reject(new Error(error));
return;
} }
callback(); return Promise.resolve();
} }
const formRules = reactive({ const formRules = reactive({

View File

@@ -35,51 +35,44 @@ function deleteEnum(index: number) {
} }
/** 校验单项枚举值:必填、数字、不重复 */ /** 校验单项枚举值:必填、数字、不重复 */
function validateEnumValue(_rule: any, value: any, callback: any) { function validateEnumValue(_rule: any, value: any) {
if (isEmpty(value)) { if (isEmpty(value)) {
callback(new Error('枚举值不能为空')); return Promise.reject(new Error('枚举值不能为空'));
return;
} }
if (Number.isNaN(Number(value))) { if (Number.isNaN(Number(value))) {
callback(new Error('枚举值必须是数字')); return Promise.reject(new Error('枚举值必须是数字'));
return;
} }
const sameCount = dataSpecsList.value.filter( const sameCount = dataSpecsList.value.filter(
(it) => it.value === value, (it) => it.value === value,
).length; ).length;
if (sameCount > 1) { if (sameCount > 1) {
callback(new Error('枚举值不能重复')); return Promise.reject(new Error('枚举值不能重复'));
return;
} }
callback(); return Promise.resolve();
} }
/** 校验整个枚举列表:非空、无空项、无非法数字、无重复 */ /** 校验整个枚举列表:非空、无空项、无非法数字、无重复 */
function validateEnumList(_rule: any, _value: any, callback: any) { function validateEnumList(_rule: any, _value: any) {
if (isEmpty(dataSpecsList.value)) { if (isEmpty(dataSpecsList.value)) {
callback(new Error('请至少添加一个枚举项')); return Promise.reject(new Error('请至少添加一个枚举项'));
return;
} }
const hasEmpty = dataSpecsList.value.some( const hasEmpty = dataSpecsList.value.some(
(item) => isEmpty(item.value) || isEmpty(item.name), (item) => isEmpty(item.value) || isEmpty(item.name),
); );
if (hasEmpty) { if (hasEmpty) {
callback(new Error('存在未填写的枚举值或描述')); return Promise.reject(new Error('存在未填写的枚举值或描述'));
return;
} }
const hasInvalidNumber = dataSpecsList.value.some((item) => const hasInvalidNumber = dataSpecsList.value.some((item) =>
Number.isNaN(Number(item.value)), Number.isNaN(Number(item.value)),
); );
if (hasInvalidNumber) { if (hasInvalidNumber) {
callback(new Error('存在非数字的枚举值')); return Promise.reject(new Error('存在非数字的枚举值'));
return;
} }
const values = dataSpecsList.value.map((item) => item.value); const values = dataSpecsList.value.map((item) => item.value);
if (new Set(values).size !== values.length) { if (new Set(values).size !== values.length) {
callback(new Error('存在重复的枚举值')); return Promise.reject(new Error('存在重复的枚举值'));
return;
} }
callback(); return Promise.resolve();
} }
</script> </script>

View File

@@ -36,53 +36,46 @@ function unitChange(unitSpecs: any) {
} }
/** 校验最小值 */ /** 校验最小值 */
function validateMin(_rule: any, _value: any, callback: any) { function validateMin(_rule: any, _value: any) {
const min = Number(dataSpecs.value.min); const min = Number(dataSpecs.value.min);
const max = Number(dataSpecs.value.max); const max = Number(dataSpecs.value.max);
if (Number.isNaN(min)) { if (Number.isNaN(min)) {
callback(new Error('请输入有效的数值')); return Promise.reject(new Error('请输入有效的数值'));
return;
} }
if (!Number.isNaN(max) && min >= max) { if (!Number.isNaN(max) && min >= max) {
callback(new Error('最小值必须小于最大值')); return Promise.reject(new Error('最小值必须小于最大值'));
return;
} }
callback(); return Promise.resolve();
} }
/** 校验最大值 */ /** 校验最大值 */
function validateMax(_rule: any, _value: any, callback: any) { function validateMax(_rule: any, _value: any) {
const min = Number(dataSpecs.value.min); const min = Number(dataSpecs.value.min);
const max = Number(dataSpecs.value.max); const max = Number(dataSpecs.value.max);
if (Number.isNaN(max)) { if (Number.isNaN(max)) {
callback(new Error('请输入有效的数值')); return Promise.reject(new Error('请输入有效的数值'));
return;
} }
if (!Number.isNaN(min) && max <= min) { if (!Number.isNaN(min) && max <= min) {
callback(new Error('最大值必须大于最小值')); return Promise.reject(new Error('最大值必须大于最小值'));
return;
} }
callback(); return Promise.resolve();
} }
/** 校验步长 */ /** 校验步长 */
function validateStep(_rule: any, _value: any, callback: any) { function validateStep(_rule: any, _value: any) {
const step = Number(dataSpecs.value.step); const step = Number(dataSpecs.value.step);
if (Number.isNaN(step)) { if (Number.isNaN(step)) {
callback(new Error('请输入有效的数值')); return Promise.reject(new Error('请输入有效的数值'));
return;
} }
if (step <= 0) { if (step <= 0) {
callback(new Error('步长必须大于 0')); return Promise.reject(new Error('步长必须大于 0'));
return;
} }
const min = Number(dataSpecs.value.min); const min = Number(dataSpecs.value.min);
const max = Number(dataSpecs.value.max); const max = Number(dataSpecs.value.max);
if (!Number.isNaN(min) && !Number.isNaN(max) && step > max - min) { if (!Number.isNaN(min) && !Number.isNaN(max) && step > max - min) {
callback(new Error('步长不能大于最大值与最小值的差值')); return Promise.reject(new Error('步长不能大于最大值与最小值的差值'));
return;
} }
callback(); return Promise.resolve();
} }
</script> </script>

View File

@@ -33,12 +33,11 @@ const structFormRef = ref();
const formData = ref<any>(buildEmptyFormData()); const formData = ref<any>(buildEmptyFormData());
/** 校验结构体属性对象非空 */ /** 校验结构体属性对象非空 */
function validateStructSpecsList(_rule: any, _value: any, callback: any) { function validateStructSpecsList(_rule: any, _value: any) {
if (isEmpty(dataSpecsList.value)) { if (isEmpty(dataSpecsList.value)) {
callback(new Error('请至少添加一个结构体属性对象')); return Promise.reject(new Error('请至少添加一个结构体属性对象'));
return;
} }
callback(); return Promise.resolve();
} }
const [Modal, modalApi] = useVbenModal({ const [Modal, modalApi] = useVbenModal({

View File

@@ -114,41 +114,37 @@ export namespace ThingModelApi {
/** 生成「必填 + 数字」类校验器:拼到 size / length / 枚举值上 */ /** 生成「必填 + 数字」类校验器:拼到 size / length / 枚举值上 */
function buildRequiredNumberValidator(label: string) { function buildRequiredNumberValidator(label: string) {
return (_rule: any, value: any, callback: any) => { return (_rule: any, value: any) => {
if (isEmpty(value)) { if (isEmpty(value)) {
callback(new Error(`${label}不能为空`)); return Promise.reject(new Error(`${label}不能为空`));
return;
} }
if (Number.isNaN(Number(value))) { if (Number.isNaN(Number(value))) {
callback(new Error(`${label}必须是数字`)); return Promise.reject(new Error(`${label}必须是数字`));
return;
} }
callback(); return Promise.resolve();
}; };
} }
/** 生成「标识符样式」名称校验器:开头需为中文 / 英文 / 数字,整体仅允许中文、英文、数字、下划线、短划线,长度 ≤ 20 */ /** 生成「标识符样式」名称校验器:开头需为中文 / 英文 / 数字,整体仅允许中文、英文、数字、下划线、短划线,长度 ≤ 20 */
export function buildIdentifierLikeNameValidator(label: string) { export function buildIdentifierLikeNameValidator(label: string) {
return (_rule: any, value: string, callback: any) => { return (_rule: any, value: string) => {
if (isEmpty(value)) { if (isEmpty(value)) {
callback(new Error(`${label}不能为空`)); return Promise.reject(new Error(`${label}不能为空`));
return;
} }
if (!/^[一-龥A-Za-z0-9]/.test(value)) { if (!/^[一-龥A-Za-z0-9]/.test(value)) {
callback(new Error(`${label}必须以中文、英文字母或数字开头`)); return Promise.reject(
return; new Error(`${label}必须以中文、英文字母或数字开头`),
);
} }
if (!/^[一-龥A-Za-z0-9][\w一-龥-]*$/.test(value)) { if (!/^[一-龥A-Za-z0-9][\w一-龥-]*$/.test(value)) {
callback( return Promise.reject(
new Error(`${label}只能包含中文、英文字母、数字、下划线和短划线`), new Error(`${label}只能包含中文、英文字母、数字、下划线和短划线`),
); );
return;
} }
if (value.length > 20) { if (value.length > 20) {
callback(new Error(`${label}长度不能超过 20 个字符`)); return Promise.reject(new Error(`${label}长度不能超过 20 个字符`));
return;
} }
callback(); return Promise.resolve();
}; };
} }
@@ -172,7 +168,7 @@ export const ThingModelFormRules: Record<string, FormItemRule[]> = {
trigger: 'blur', trigger: 'blur',
}, },
{ {
validator: (_rule: any, value: string, callback: any) => { validator: (_rule: any, value: string) => {
const reservedKeywords = [ const reservedKeywords = [
'set', 'set',
'get', 'get',
@@ -183,18 +179,16 @@ export const ThingModelFormRules: Record<string, FormItemRule[]> = {
'value', 'value',
]; ];
if (reservedKeywords.includes(value)) { if (reservedKeywords.includes(value)) {
callback( return Promise.reject(
new Error( new Error(
'set, get, post, property, event, time, value 是系统保留字段,不能用于标识符定义', 'set, get, post, property, event, time, value 是系统保留字段,不能用于标识符定义',
), ),
); );
return;
} }
if (/^\d+$/.test(value)) { if (/^\d+$/.test(value)) {
callback(new Error('标识符不能是纯数字')); return Promise.reject(new Error('标识符不能是纯数字'));
return;
} }
callback(); return Promise.resolve();
}, },
trigger: 'blur', trigger: 'blur',
}, },

View File

@@ -25,8 +25,8 @@ import '../theme/index.scss';
const props = defineProps({ const props = defineProps({
xml: { xml: {
default: '',
type: String, type: String,
required: true,
}, },
view: { view: {
type: Object, type: Object,
@@ -269,12 +269,12 @@ const onSelectElement = (element: any) => {
}; };
/** 初始化 BPMN 视图 */ /** 初始化 BPMN 视图 */
const importXML = async (xml: string) => { const importXML = async (xml?: string) => {
// 清空流程图 // 清空流程图
clearViewer(); clearViewer();
// 初始化流程图 // 初始化流程图
if (xml !== null && xml !== '') { if (xml) {
try { try {
bpmnViewer.value = new BpmnViewer({ bpmnViewer.value = new BpmnViewer({
additionalModules: [MoveCanvasModule], additionalModules: [MoveCanvasModule],
@@ -387,7 +387,7 @@ const setProcessStatus = (view: any) => {
watch( watch(
() => props.xml, () => props.xml,
(newXml) => { (newXml) => {
importXML(newXml); importXML(newXml || '');
}, },
{ immediate: true }, { immediate: true },
); );
@@ -402,7 +402,7 @@ watch(
/** mounted初始化 */ /** mounted初始化 */
onMounted(() => { onMounted(() => {
importXML(props.xml); importXML(props.xml || '');
setProcessStatus(props.view); setProcessStatus(props.view);
}); });

View File

@@ -76,7 +76,7 @@ const startUserSelectTasks = ref<UserTask[]>([]);
const startUserSelectAssignees = ref<Record<string, number[]>>({}); const startUserSelectAssignees = ref<Record<string, number[]>>({});
const tempStartUserSelectAssignees = ref<Record<string, number[]>>({}); const tempStartUserSelectAssignees = ref<Record<string, number[]>>({});
const bpmnXML = ref<string | undefined>(undefined); const bpmnXML = ref('');
const simpleJson = ref<string | undefined>(undefined); const simpleJson = ref<string | undefined>(undefined);
const activeTab = ref('form'); const activeTab = ref('form');
@@ -167,7 +167,7 @@ async function initProcessInfo(row: any, formVariables?: any) {
const processDefinitionDetail: BpmProcessDefinitionApi.ProcessDefinition = const processDefinitionDetail: BpmProcessDefinitionApi.ProcessDefinition =
await getProcessDefinition(row.id); await getProcessDefinition(row.id);
if (processDefinitionDetail) { if (processDefinitionDetail) {
bpmnXML.value = processDefinitionDetail.bpmnXml; bpmnXML.value = processDefinitionDetail.bpmnXml || '';
simpleJson.value = processDefinitionDetail.simpleModel; simpleJson.value = processDefinitionDetail.simpleModel;
} }
// 情况二:业务表单 // 情况二:业务表单

View File

@@ -9,7 +9,7 @@ const props = withDefaults(
defineProps<{ defineProps<{
bpmnXml?: string; bpmnXml?: string;
loading?: boolean; // 是否加载中 loading?: boolean; // 是否加载中
modelView?: object; modelView?: Record<string, any>;
}>(), }>(),
{ {
loading: false, loading: false,
@@ -29,8 +29,10 @@ watch(
async (newModelView) => { async (newModelView) => {
// 加载最新 // 加载最新
if (newModelView) { if (newModelView) {
// @ts-expect-error: viewer instance type is broader than local ref typing view.value = {
view.value = newModelView; ...newModelView,
bpmnXml: newModelView.bpmnXml || '',
};
} }
}, },
{ immediate: true }, { immediate: true },
@@ -40,7 +42,7 @@ watch(
watch( watch(
() => props.bpmnXml, () => props.bpmnXml,
(value) => { (value) => {
view.value.bpmnXml = value; view.value.bpmnXml = value || '';
}, },
); );
</script> </script>
@@ -52,7 +54,7 @@ watch(
> >
<MyProcessViewer <MyProcessViewer
key="processViewer" key="processViewer"
:xml="view.bpmnXml" :xml="view.bpmnXml || ''"
:view="view" :view="view"
class="h-full min-h-[500px] w-full" class="h-full min-h-[500px] w-full"
/> />

View File

@@ -26,57 +26,14 @@ const simpleModel = ref<any>({});
const tasks = ref([]); // 用户任务 const tasks = ref([]); // 用户任务
const processInstance = ref(); // 流程实例 const processInstance = ref(); // 流程实例
/** 监控模型视图 包括任务列表、进行中的活动节点编号等 */ function setSimpleModelNodeTaskStatus(
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 = (
simpleModel: SimpleFlowNode | undefined, simpleModel: SimpleFlowNode | undefined,
processStatus: number, processStatus: number,
rejectedTaskActivityIds: string[], rejectedTaskActivityIds: string[],
unfinishedTaskActivityIds: string[], unfinishedTaskActivityIds: string[],
finishedActivityIds: string[], finishedActivityIds: string[],
finishedSequenceFlowActivityIds: string[], finishedSequenceFlowActivityIds: string[],
) => { ) {
if (!simpleModel) { if (!simpleModel) {
return; return;
} }
@@ -166,7 +123,50 @@ const setSimpleModelNodeTaskStatus = (
finishedActivityIds, finishedActivityIds,
finishedSequenceFlowActivityIds, 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);
}
},
);
</script> </script>
<template> <template>
<div v-loading="loading"> <div v-loading="loading">

View File

@@ -14,13 +14,7 @@ import {
import { IconifyIcon } from '@vben/icons'; import { IconifyIcon } from '@vben/icons';
import { formatDateTime, isEmpty } from '@vben/utils'; import { formatDateTime, isEmpty } from '@vben/utils';
import { import { Avatar, Button, Timeline, TimelineItem, Tooltip } from 'antdv-next';
Avatar,
Button,
Timeline,
TimelineItem,
Tooltip,
} from 'antdv-next';
import { UserSelectModal } from '#/views/system/user/components'; import { UserSelectModal } from '#/views/system/user/components';
@@ -488,7 +482,6 @@ defineExpose({ setCustomApproveUsers, batchSetCustomApproveUsers });
class="w-3/5" class="w-3/5"
v-model:value="selectedUsers" v-model:value="selectedUsers"
:multiple="true" :multiple="true"
title="选择用户"
@confirm="handleUserSelectConfirm" @confirm="handleUserSelectConfirm"
@closed="handleUserSelectClosed" @closed="handleUserSelectClosed"
@cancel="handleUserSelectCancel" @cancel="handleUserSelectCancel"

View File

@@ -111,42 +111,38 @@ export namespace ThingModelApi {
} }
/** 生成「必填 + 数字」类校验器:拼到 size / length / 枚举值上 */ /** 生成「必填 + 数字」类校验器:拼到 size / length / 枚举值上 */
function buildRequiredNumberValidator(label: string) { function buildRequiredNumberValidator(label: string): any {
return (_rule: any, value: any, callback: any) => { return (_rule: any, value: any) => {
if (isEmpty(value)) { if (isEmpty(value)) {
callback(new Error(`${label}不能为空`)); return Promise.reject(new Error(`${label}不能为空`));
return;
} }
if (Number.isNaN(Number(value))) { if (Number.isNaN(Number(value))) {
callback(new Error(`${label}必须是数字`)); return Promise.reject(new Error(`${label}必须是数字`));
return;
} }
callback(); return Promise.resolve();
}; };
} }
/** 生成「标识符样式」名称校验器:开头需为中文 / 英文 / 数字,整体仅允许中文、英文、数字、下划线、短划线,长度 ≤ 20 */ /** 生成「标识符样式」名称校验器:开头需为中文 / 英文 / 数字,整体仅允许中文、英文、数字、下划线、短划线,长度 ≤ 20 */
export function buildIdentifierLikeNameValidator(label: string) { export function buildIdentifierLikeNameValidator(label: string): any {
return (_rule: any, value: string, callback: any) => { return (_rule: any, value: string) => {
if (isEmpty(value)) { if (isEmpty(value)) {
callback(new Error(`${label}不能为空`)); return Promise.reject(new Error(`${label}不能为空`));
return;
} }
if (!/^[一-龥A-Za-z0-9]/.test(value)) { if (!/^[一-龥A-Za-z0-9]/.test(value)) {
callback(new Error(`${label}必须以中文、英文字母或数字开头`)); return Promise.reject(
return; new Error(`${label}必须以中文、英文字母或数字开头`),
);
} }
if (!/^[一-龥A-Za-z0-9][\w一-龥-]*$/.test(value)) { if (!/^[一-龥A-Za-z0-9][\w一-龥-]*$/.test(value)) {
callback( return Promise.reject(
new Error(`${label}只能包含中文、英文字母、数字、下划线和短划线`), new Error(`${label}只能包含中文、英文字母、数字、下划线和短划线`),
); );
return;
} }
if (value.length > 20) { if (value.length > 20) {
callback(new Error(`${label}长度不能超过 20 个字符`)); return Promise.reject(new Error(`${label}长度不能超过 20 个字符`));
return;
} }
callback(); return Promise.resolve();
}; };
} }
@@ -170,7 +166,7 @@ export const ThingModelFormRules: Record<string, FormItemRule[]> = {
trigger: 'blur', trigger: 'blur',
}, },
{ {
validator: (_rule: any, value: string, callback: any) => { validator: ((_rule: any, value: string) => {
const reservedKeywords = [ const reservedKeywords = [
'set', 'set',
'get', 'get',
@@ -181,19 +177,17 @@ export const ThingModelFormRules: Record<string, FormItemRule[]> = {
'value', 'value',
]; ];
if (reservedKeywords.includes(value)) { if (reservedKeywords.includes(value)) {
callback( return Promise.reject(
new Error( new Error(
'set, get, post, property, event, time, value 是系统保留字段,不能用于标识符定义', 'set, get, post, property, event, time, value 是系统保留字段,不能用于标识符定义',
), ),
); );
return;
} }
if (/^\d+$/.test(value)) { if (/^\d+$/.test(value)) {
callback(new Error('标识符不能是纯数字')); return Promise.reject(new Error('标识符不能是纯数字'));
return;
} }
callback(); return Promise.resolve();
}, }) as any,
trigger: 'blur', trigger: 'blur',
}, },
], ],

View File

@@ -22,8 +22,8 @@ import '../theme/index.scss';
const props = defineProps({ const props = defineProps({
xml: { xml: {
default: '',
type: String, type: String,
required: true,
}, },
view: { view: {
type: Object, type: Object,
@@ -192,12 +192,12 @@ const onSelectElement = (element: any) => {
}; };
/** 初始化 BPMN 视图 */ /** 初始化 BPMN 视图 */
const importXML = async (xml: string) => { const importXML = async (xml?: string) => {
// 清空流程图 // 清空流程图
clearViewer(); clearViewer();
// 初始化流程图 // 初始化流程图
if (xml !== null && xml !== '') { if (xml) {
try { try {
bpmnViewer.value = new BpmnViewer({ bpmnViewer.value = new BpmnViewer({
additionalModules: [MoveCanvasModule], additionalModules: [MoveCanvasModule],
@@ -310,7 +310,7 @@ const setProcessStatus = (view: any) => {
watch( watch(
() => props.xml, () => props.xml,
(newXml) => { (newXml) => {
importXML(newXml); importXML(newXml || '');
}, },
{ immediate: true }, { immediate: true },
); );
@@ -325,7 +325,7 @@ watch(
/** mounted初始化 */ /** mounted初始化 */
onMounted(() => { onMounted(() => {
importXML(props.xml); importXML(props.xml || '');
setProcessStatus(props.view); setProcessStatus(props.view);
}); });

View File

@@ -70,21 +70,19 @@ const rules: Record<string, FormItemRule[]> = {
key: [ key: [
{ required: true, message: '流程标识不能为空', trigger: 'blur' }, { required: true, message: '流程标识不能为空', trigger: 'blur' },
{ {
validator: (_rule: any, value: string, callback: any) => { validator: ((_rule: any, value: string) => {
if (!value) { if (!value) {
callback(); return Promise.resolve();
return;
} }
if (!/^[a-z_][-\w.$]*$/i.test(value)) { if (!/^[a-z_][-\w.$]*$/i.test(value)) {
callback( return Promise.reject(
new Error( new Error(
'只能包含字母、数字、下划线、连字符和点号,且必须以字母或下划线开头', '只能包含字母、数字、下划线、连字符和点号,且必须以字母或下划线开头',
), ),
); );
return;
} }
callback(); return Promise.resolve();
}, }) as any,
trigger: 'blur', trigger: 'blur',
}, },
], ],

View File

@@ -75,7 +75,7 @@ const startUserSelectTasks = ref<UserTask[]>([]);
const startUserSelectAssignees = ref<Record<string, number[]>>({}); const startUserSelectAssignees = ref<Record<string, number[]>>({});
const tempStartUserSelectAssignees = ref<Record<string, number[]>>({}); const tempStartUserSelectAssignees = ref<Record<string, number[]>>({});
const bpmnXML = ref<string | undefined>(undefined); const bpmnXML = ref('');
const simpleJson = ref<string | undefined>(undefined); const simpleJson = ref<string | undefined>(undefined);
// @ts-expect-error unused // @ts-expect-error unused
@@ -168,7 +168,7 @@ async function initProcessInfo(row: any, formVariables?: any) {
const processDefinitionDetail: BpmProcessDefinitionApi.ProcessDefinition = const processDefinitionDetail: BpmProcessDefinitionApi.ProcessDefinition =
await getProcessDefinition(row.id); await getProcessDefinition(row.id);
if (processDefinitionDetail) { if (processDefinitionDetail) {
bpmnXML.value = processDefinitionDetail.bpmnXml; bpmnXML.value = processDefinitionDetail.bpmnXml || '';
simpleJson.value = processDefinitionDetail.simpleModel; simpleJson.value = processDefinitionDetail.simpleModel;
} }
// 情况二:业务表单 // 情况二:业务表单

View File

@@ -9,7 +9,7 @@ const props = withDefaults(
defineProps<{ defineProps<{
bpmnXml?: string; bpmnXml?: string;
loading?: boolean; // 是否加载中 loading?: boolean; // 是否加载中
modelView?: object; modelView?: Record<string, any>;
}>(), }>(),
{ {
loading: false, loading: false,
@@ -29,8 +29,10 @@ watch(
async (newModelView) => { async (newModelView) => {
// 加载最新 // 加载最新
if (newModelView) { if (newModelView) {
// @ts-expect-error: viewer instance type is broader than local ref typing view.value = {
view.value = newModelView; ...newModelView,
bpmnXml: newModelView.bpmnXml || '',
};
} }
}, },
{ immediate: true }, { immediate: true },
@@ -40,7 +42,7 @@ watch(
watch( watch(
() => props.bpmnXml, () => props.bpmnXml,
(value) => { (value) => {
view.value.bpmnXml = value; view.value.bpmnXml = value || '';
}, },
); );
</script> </script>
@@ -52,7 +54,7 @@ watch(
> >
<MyProcessViewer <MyProcessViewer
key="processViewer" key="processViewer"
:xml="view.bpmnXml" :xml="view.bpmnXml || ''"
:view="view" :view="view"
class="h-full min-h-[500px] w-full" class="h-full min-h-[500px] w-full"
/> />

View File

@@ -26,57 +26,14 @@ const simpleModel = ref<any>({});
const tasks = ref([]); // 用户任务 const tasks = ref([]); // 用户任务
const processInstance = ref(); // 流程实例 const processInstance = ref(); // 流程实例
/** 监控模型视图 包括任务列表、进行中的活动节点编号等 */ function setSimpleModelNodeTaskStatus(
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 = (
simpleModel: SimpleFlowNode | undefined, simpleModel: SimpleFlowNode | undefined,
processStatus: number, processStatus: number,
rejectedTaskActivityIds: string[], rejectedTaskActivityIds: string[],
unfinishedTaskActivityIds: string[], unfinishedTaskActivityIds: string[],
finishedActivityIds: string[], finishedActivityIds: string[],
finishedSequenceFlowActivityIds: string[], finishedSequenceFlowActivityIds: string[],
) => { ) {
if (!simpleModel) { if (!simpleModel) {
return; return;
} }
@@ -166,7 +123,50 @@ const setSimpleModelNodeTaskStatus = (
finishedActivityIds, finishedActivityIds,
finishedSequenceFlowActivityIds, 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);
}
},
);
</script> </script>
<template> <template>
<div v-loading="loading"> <div v-loading="loading">

View File

@@ -486,7 +486,6 @@ defineExpose({ setCustomApproveUsers, batchSetCustomApproveUsers });
class="w-3/5" class="w-3/5"
v-model="selectedUsers" v-model="selectedUsers"
:multiple="true" :multiple="true"
title="选择用户"
@confirm="handleUserSelectConfirm" @confirm="handleUserSelectConfirm"
@closed="handleUserSelectClosed" @closed="handleUserSelectClosed"
@cancel="handleUserSelectCancel" @cancel="handleUserSelectCancel"

View File

@@ -122,23 +122,21 @@ function normalizeFormData(result: any): RuleSceneApi.SceneRule {
} }
/** 触发器校验 */ /** 触发器校验 */
function validateTriggers(_rule: any, value: any, callback: any) { function validateTriggers(_rule: any, value: any) {
const error = validateSceneRuleTriggers(value); const error = validateSceneRuleTriggers(value);
if (error) { if (error) {
callback(new Error(error)); return Promise.reject(new Error(error));
return;
} }
callback(); return Promise.resolve();
} }
/** 执行器校验 */ /** 执行器校验 */
function validateActions(_rule: any, value: any, callback: any) { function validateActions(_rule: any, value: any) {
const error = validateSceneRuleActions(value); const error = validateSceneRuleActions(value);
if (error) { if (error) {
callback(new Error(error)); return Promise.reject(new Error(error));
return;
} }
callback(); return Promise.resolve();
} }
const formRules = reactive({ const formRules = reactive({

View File

@@ -35,52 +35,45 @@ function deleteEnum(index: number) {
} }
/** 校验单项枚举值:必填、数字、不重复 */ /** 校验单项枚举值:必填、数字、不重复 */
function validateEnumValue(_rule: any, value: any, callback: any) { const validateEnumValue: any = (_rule: any, value: any) => {
if (isEmpty(value)) { if (isEmpty(value)) {
callback(new Error('枚举值不能为空')); return Promise.reject(new Error('枚举值不能为空'));
return;
} }
if (Number.isNaN(Number(value))) { if (Number.isNaN(Number(value))) {
callback(new Error('枚举值必须是数字')); return Promise.reject(new Error('枚举值必须是数字'));
return;
} }
const sameCount = dataSpecsList.value.filter( const sameCount = dataSpecsList.value.filter(
(it) => it.value === value, (it) => it.value === value,
).length; ).length;
if (sameCount > 1) { if (sameCount > 1) {
callback(new Error('枚举值不能重复')); return Promise.reject(new Error('枚举值不能重复'));
return;
} }
callback(); return Promise.resolve();
} };
/** 校验整个枚举列表:非空、无空项、无非法数字、无重复 */ /** 校验整个枚举列表:非空、无空项、无非法数字、无重复 */
function validateEnumList(_rule: any, _value: any, callback: any) { const validateEnumList: any = (_rule: any, _value: any) => {
if (isEmpty(dataSpecsList.value)) { if (isEmpty(dataSpecsList.value)) {
callback(new Error('请至少添加一个枚举项')); return Promise.reject(new Error('请至少添加一个枚举项'));
return;
} }
const hasEmpty = dataSpecsList.value.some( const hasEmpty = dataSpecsList.value.some(
(item) => isEmpty(item.value) || isEmpty(item.name), (item) => isEmpty(item.value) || isEmpty(item.name),
); );
if (hasEmpty) { if (hasEmpty) {
callback(new Error('存在未填写的枚举值或描述')); return Promise.reject(new Error('存在未填写的枚举值或描述'));
return;
} }
const hasInvalidNumber = dataSpecsList.value.some((item) => const hasInvalidNumber = dataSpecsList.value.some((item) =>
Number.isNaN(Number(item.value)), Number.isNaN(Number(item.value)),
); );
if (hasInvalidNumber) { if (hasInvalidNumber) {
callback(new Error('存在非数字的枚举值')); return Promise.reject(new Error('存在非数字的枚举值'));
return;
} }
const values = dataSpecsList.value.map((item) => item.value); const values = dataSpecsList.value.map((item) => item.value);
if (new Set(values).size !== values.length) { if (new Set(values).size !== values.length) {
callback(new Error('存在重复的枚举值')); return Promise.reject(new Error('存在重复的枚举值'));
return;
} }
callback(); return Promise.resolve();
} };
</script> </script>
<template> <template>

View File

@@ -29,54 +29,47 @@ function unitChange(unitSpecs: any) {
} }
/** 校验最小值 */ /** 校验最小值 */
function validateMin(_rule: any, _value: any, callback: any) { const validateMin: any = (_rule: any, _value: any) => {
const min = Number(dataSpecs.value.min); const min = Number(dataSpecs.value.min);
const max = Number(dataSpecs.value.max); const max = Number(dataSpecs.value.max);
if (Number.isNaN(min)) { if (Number.isNaN(min)) {
callback(new Error('请输入有效的数值')); return Promise.reject(new Error('请输入有效的数值'));
return;
} }
if (!Number.isNaN(max) && min >= max) { if (!Number.isNaN(max) && min >= max) {
callback(new Error('最小值必须小于最大值')); return Promise.reject(new Error('最小值必须小于最大值'));
return;
} }
callback(); return Promise.resolve();
} };
/** 校验最大值 */ /** 校验最大值 */
function validateMax(_rule: any, _value: any, callback: any) { const validateMax: any = (_rule: any, _value: any) => {
const min = Number(dataSpecs.value.min); const min = Number(dataSpecs.value.min);
const max = Number(dataSpecs.value.max); const max = Number(dataSpecs.value.max);
if (Number.isNaN(max)) { if (Number.isNaN(max)) {
callback(new Error('请输入有效的数值')); return Promise.reject(new Error('请输入有效的数值'));
return;
} }
if (!Number.isNaN(min) && max <= min) { if (!Number.isNaN(min) && max <= min) {
callback(new Error('最大值必须大于最小值')); return Promise.reject(new Error('最大值必须大于最小值'));
return;
} }
callback(); return Promise.resolve();
} };
/** 校验步长 */ /** 校验步长 */
function validateStep(_rule: any, _value: any, callback: any) { const validateStep: any = (_rule: any, _value: any) => {
const step = Number(dataSpecs.value.step); const step = Number(dataSpecs.value.step);
if (Number.isNaN(step)) { if (Number.isNaN(step)) {
callback(new Error('请输入有效的数值')); return Promise.reject(new Error('请输入有效的数值'));
return;
} }
if (step <= 0) { if (step <= 0) {
callback(new Error('步长必须大于 0')); return Promise.reject(new Error('步长必须大于 0'));
return;
} }
const min = Number(dataSpecs.value.min); const min = Number(dataSpecs.value.min);
const max = Number(dataSpecs.value.max); const max = Number(dataSpecs.value.max);
if (!Number.isNaN(min) && !Number.isNaN(max) && step > max - min) { if (!Number.isNaN(min) && !Number.isNaN(max) && step > max - min) {
callback(new Error('步长不能大于最大值与最小值的差值')); return Promise.reject(new Error('步长不能大于最大值与最小值的差值'));
return;
} }
callback(); return Promise.resolve();
} };
</script> </script>
<template> <template>

View File

@@ -33,13 +33,12 @@ const structFormRef = ref();
const formData = ref<any>(buildEmptyFormData()); const formData = ref<any>(buildEmptyFormData());
/** 校验结构体属性对象非空 */ /** 校验结构体属性对象非空 */
function validateStructSpecsList(_rule: any, _value: any, callback: any) { const validateStructSpecsList: any = (_rule: any, _value: any) => {
if (isEmpty(dataSpecsList.value)) { if (isEmpty(dataSpecsList.value)) {
callback(new Error('请至少添加一个结构体属性对象')); return Promise.reject(new Error('请至少添加一个结构体属性对象'));
return;
} }
callback(); return Promise.resolve();
} };
const [Modal, modalApi] = useVbenModal({ const [Modal, modalApi] = useVbenModal({
async onConfirm() { async onConfirm() {