钉钉审批功能完善、混炼示方新增是否附加料

This commit is contained in:
geht
2026-06-10 15:41:02 +08:00
parent de48bd2324
commit 39a9bd83f1
37 changed files with 2461 additions and 166 deletions

View File

@@ -9,10 +9,18 @@ enum Api {
edit = '/xslmes/mesXslBizDocRegistry/edit',
deleteOne = '/xslmes/mesXslBizDocRegistry/delete',
deleteBatch = '/xslmes/mesXslBizDocRegistry/deleteBatch',
dbTables = '/xslmes/mesXslBizDocRegistry/dbTables',
}
export const list = (params) => defHttp.get({ url: Api.list, params });
/** 当前库物理表(审批注册中心下拉) */
export const listDbTables = (keyword?: string) =>
defHttp.get<{ value: string; label: string; comment?: string }[]>({
url: Api.dbTables,
params: keyword ? { keyword } : {},
});
export const saveOrUpdate = (params, isUpdate) =>
isUpdate ? defHttp.put({ url: Api.edit, params }) : defHttp.post({ url: Api.save, params });

View File

@@ -1,4 +1,5 @@
import { BasicColumn, FormSchema } from '/@/components/Table';
import { listDbTables } from './MesXslBizDocRegistry.api';
const STAGE_DICT = 'mes_xsl_approval_stage';
@@ -36,9 +37,16 @@ export const formSchema: FormSchema[] = [
{
label: '物理表名',
field: 'tableName',
component: 'Input',
componentProps: { placeholder: '数据库表名,如 mes_xsl_mixer_ps_compile' },
dynamicRules: () => [{ required: true, message: '请输入物理表名!' }],
component: 'ApiSelect',
componentProps: {
api: listDbTables,
showSearch: true,
placeholder: '请选择数据库物理表,可输入关键字筛选',
labelField: 'label',
valueField: 'value',
immediate: true,
},
dynamicRules: () => [{ required: true, message: '请选择物理表名!' }],
},
{
label: '中文名称',

View File

@@ -19,6 +19,8 @@ enum Api {
registryByTable = '/xslmes/mesXslIntegrationPlan/registryByTable',
previewDefaultFromFlow = '/xslmes/mesXslIntegrationPlan/previewDefaultFromFlow',
generateDefaultFromFlow = '/xslmes/mesXslIntegrationPlan/generateDefaultFromFlow',
generateForNode = '/xslmes/mesXslIntegrationPlan/generateForNode',
queryById = '/xslmes/mesXslIntegrationPlan/queryById',
}
export const list = (params) => defHttp.get({ url: Api.list, params });
@@ -49,6 +51,18 @@ export const generateDefaultFromFlow = (params: {
nodeBindings?: Array<{ nodeId: string; stage?: string | null }>;
}) => defHttp.post<any>({ url: Api.generateDefaultFromFlow, params });
/** 流程设计器:为单个节点生成集成方案 */
export const generateForNode = (params: {
sourceTable: string;
flowId: string;
nodeId: string;
stageKey: string;
flowConfig?: string;
overwriteDraft?: boolean;
}) => defHttp.post<any>({ url: Api.generateForNode, params });
export const queryPlanById = (id: string) => defHttp.get<any>({ url: Api.queryById, params: { id } });
// 动作管理
export const listActions = (planId) => defHttp.get({ url: Api.actionList, params: { planId } });
export const saveAction = (params) => defHttp.post({ url: Api.actionAdd, params });

View File

@@ -105,6 +105,15 @@
return record.actionConfig || '-';
}
}
if (record.actionType === 'SQL_UPDATE') {
try {
const cfg = JSON.parse(record.actionConfig || '{}');
const base = record.sqlTemplate || '-';
return cfg.syncTrace ? `${base};痕迹同步:是` : base;
} catch {
return record.sqlTemplate || '-';
}
}
return record.sqlTemplate || '-';
}
@@ -192,5 +201,14 @@
}
}
defineExpose({ open });
defineExpose({ open, openAndEditFirstAction });
async function openAndEditFirstAction(plan: Recordable) {
await open(plan);
if (actions.value.length > 0) {
openVisualEditor(actions.value[0]);
} else {
openVisualEditor();
}
}
</script>

View File

@@ -177,6 +177,21 @@
</div>
</div>
<!-- update-begin---author:GHT ---date:2026-06-10 for关联表痕迹同步关联表动作可选同步主表审批痕迹 -->
<div
v-if="vc.visualType === 'STATUS_MODIFY' || vc.visualType === 'DATA_SYNC'"
style="background: #f6ffed; border: 1px solid #b7eb8f; border-radius: 6px; padding: 12px 14px; margin-bottom: 16px"
>
<a-checkbox v-model:checked="vc.syncTrace">同步主表审批痕迹到目标表</a-checkbox>
<div style="font-size: 12px; color: #666; margin-top: 8px; line-height: 1.6">
开启后环节通过时将主表当前审批人/时间写入目标表 <code>mes_xsl_approval_trace</code>
驳回时将按新状态清空对应环节痕迹
<span v-if="vc.visualType === 'DATA_SYNC'" style="color: #fa8c16">驳回清空仅对状态修改动作生效</span>
目标表须已在审批注册中心启用对应环节
</div>
</div>
<!-- update-end---author:GHT ---date:2026-06-10 for关联表痕迹同步关联表动作可选同步主表审批痕迹 -->
<!-- ============ 状态修改全新设计 ============ -->
<template v-if="vc.visualType === 'STATUS_MODIFY'">
@@ -341,6 +356,8 @@
statusConfig: StatusConfig;
fieldMappings: FieldMapping[];
registryStage?: RegistryStageConfig;
/** 是否将主表审批痕迹同步到目标表 */
syncTrace?: boolean;
}
const emit = defineEmits<{ success: [action: any] }>();
@@ -431,6 +448,7 @@
statusConfig: defaultStatusConfig(),
fieldMappings: [],
registryStage: defaultRegistryStage(),
syncTrace: false,
});
/** 兼容 Flyway 扁平格式stage/expectedFrom 在顶层与向导嵌套格式registryStage 对象) */

View File

@@ -0,0 +1,82 @@
import { queryByBiz } from './MesXslApprovalTrace.api';
/** 配合示方业务表(与审批注册中心 table_name 一致) */
export const FORMULA_SPEC_BIZ_TABLE = 'mes_xsl_formula_spec';
/** 混炼示方业务表 */
export const MIXING_SPEC_BIZ_TABLE = 'mes_xsl_mixing_spec';
const TRACE_FIELD_KEYS = [
'traceProofreadBy',
'traceProofreadTime',
'traceAuditBy',
'traceAuditTime',
'traceApproveBy',
'traceApproveTime',
] as const;
/** 从列表/详情记录中提取已注入的痕迹字段 */
export function pickTraceFields(record?: Recordable | null): Recordable {
const out: Recordable = {};
if (!record) return out;
for (const key of TRACE_FIELD_KEYS) {
if (record[key] != null && record[key] !== '') {
out[key] = record[key];
}
}
return out;
}
/** 将痕迹表实体字段映射为列表注入格式 traceProofreadBy 等 */
export function applyTraceEntityToRecord(record: Recordable, trace?: Recordable | null): Recordable {
if (!record) return record;
const merged = { ...record, ...pickTraceFields(record) };
if (!trace) return merged;
return {
...merged,
traceProofreadBy: trace.proofreadBy ?? merged.traceProofreadBy,
traceProofreadTime: trace.proofreadTime ?? merged.traceProofreadTime,
traceAuditBy: trace.auditBy ?? merged.traceAuditBy,
traceAuditTime: trace.auditTime ?? merged.traceAuditTime,
traceApproveBy: trace.approveBy ?? merged.traceApproveBy,
traceApproveTime: trace.approveTime ?? merged.traceApproveTime,
};
}
export function hasTraceWorkflowInfo(record?: Recordable | null): boolean {
return !!(
record?.traceProofreadBy ||
record?.traceProofreadTime ||
record?.traceAuditBy ||
record?.traceAuditTime ||
record?.traceApproveBy ||
record?.traceApproveTime
);
}
export function normalizeApiRecord(mainRaw: unknown): Recordable {
const raw = mainRaw as Recordable;
if (raw?.id != null) return raw;
return (raw as any)?.result ?? raw ?? {};
}
/** 加载业务主表并合并痕迹(列表注入 / queryById 增强 / queryByBiz 兜底) */
export async function loadRecordWithTrace(
id: string,
bizTable: string,
fetchById: (params: { id: string }) => Promise<unknown>,
listRecord?: Recordable,
): Promise<Recordable> {
const mainRaw = await fetchById({ id });
let record = normalizeApiRecord(mainRaw);
record = applyTraceEntityToRecord(record, pickTraceFields(listRecord));
if (!hasTraceWorkflowInfo(record)) {
try {
const trace = await queryByBiz({ bizTable, bizDataId: id });
record = applyTraceEntityToRecord(record, trace);
} catch {
// 无痕迹或无权查询时忽略
}
}
return record;
}