增强审批流管理能力,新增审批环节的 stageKey 区分关键环节与过路审批节点,完善钉钉回调日志记录,停用部分 HTTP 回调接口,改由集成方案驱动审批流,优化审批注册中心的查询逻辑。

This commit is contained in:
geht
2026-06-05 19:05:48 +08:00
parent fc4e3211ad
commit 1d0b4c9fbb
95 changed files with 8385 additions and 457 deletions

View File

@@ -0,0 +1,195 @@
<template>
<a-drawer v-model:open="visible" :title="`动作管理 — ${planName}`" width="900" destroy-on-close @close="visible = false">
<div style="margin-bottom: 12px">
<a-button type="primary" size="small" @click="handleAddAction">
<template #icon><PlusOutlined /></template>
添加动作
</a-button>
</div>
<a-table :dataSource="actions" :columns="tableColumns" :loading="loading" :pagination="false" row-key="id" size="small" bordered>
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'enabled'">
<a-tag :color="record.enabled ? 'green' : 'default'">{{ record.enabled ? '启用' : '停用' }}</a-tag>
</template>
<template v-if="column.dataIndex === 'actionType'">
<a-tag color="blue">{{ actionTypeLabel(record.actionType) }}</a-tag>
</template>
<template v-if="column.dataIndex === 'onFail'">
<a-tag :color="record.onFail === 'stop' ? 'orange' : 'default'">{{ record.onFail === 'stop' ? '终止' : '继续' }}</a-tag>
</template>
<template v-if="column.dataIndex === 'summary'">
<span style="font-size: 12px; color: #666">{{ formatActionSummary(record) }}</span>
</template>
<template v-if="column.key === 'operation'">
<a-space>
<a-button size="small" @click="handleEditAction(record)">编辑</a-button>
<a-popconfirm title="确认删除该动作?" @confirm="handleDeleteAction(record)">
<a-button size="small" danger>删除</a-button>
</a-popconfirm>
</a-space>
</template>
</template>
</a-table>
<!-- update-begin---author:GHT ---date:2026-06-05 forXSLMES-20260605-K8R2动作管理复用可视化编辑器,与向导添加动作一致 -->
<VisualActionEditor ref="visualEditorRef" @success="handleActionSaved" />
<!-- update-end---author:GHT ---date:2026-06-05 forXSLMES-20260605-K8R2动作管理复用可视化编辑器 -->
</a-drawer>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { PlusOutlined } from '@ant-design/icons-vue';
import { useMessage } from '/@/hooks/web/useMessage';
import {
listActions,
saveAction,
editAction,
deleteAction,
getTableColumns,
listBizDocRegistry,
getRegistryByTable,
} from '../MesXslIntegrationPlan.api';
import VisualActionEditor from './VisualActionEditor.vue';
const STAGE_LABELS: Record<string, string> = { proofread: '校对', audit: '审核', approve: '批准' };
const ACTION_TYPE_LABELS: Record<string, string> = {
REGISTRY_STAGE_SYNC: '审批环节同步',
REGISTRY_STAGE_REVERT: '审批环节回退',
SQL_UPDATE: 'SQL更新',
};
const { createMessage } = useMessage();
const visible = ref(false);
const loading = ref(false);
const planId = ref('');
const planName = ref('');
const planRecord = ref<Recordable>({});
const actions = ref<any[]>([]);
const visualEditorRef = ref();
const bizDocList = ref<any[]>([]);
const sourceColumns = ref<any[]>([]);
const sourceRegistry = ref<any>(null);
const tableColumns = [
{ title: '顺序', dataIndex: 'execOrder', width: 60, align: 'center' },
{ title: '动作名称', dataIndex: 'actionName', width: 140 },
{ title: '类型', dataIndex: 'actionType', width: 120 },
{ title: '配置摘要', dataIndex: 'summary', ellipsis: true },
{ title: '失败策略', dataIndex: 'onFail', width: 90 },
{ title: '启用', dataIndex: 'enabled', width: 70 },
{ title: '操作', key: 'operation', width: 130, align: 'center' },
];
function actionTypeLabel(type: string) {
return ACTION_TYPE_LABELS[type] || type || '-';
}
function formatActionSummary(record: Recordable) {
if (record.actionType === 'REGISTRY_STAGE_SYNC' || record.actionType === 'REGISTRY_STAGE_REVERT') {
try {
const cfg = JSON.parse(record.actionConfig || '{}');
if (record.actionType === 'REGISTRY_STAGE_SYNC') {
const stage = cfg.registryStage?.stage || cfg.stage;
const from = cfg.registryStage?.expectedFrom || cfg.expectedFrom;
return `环节→${STAGE_LABELS[stage] || stage || '?'}${from ? `,前置=${from}` : ''}`;
}
const target = cfg.registryStage?.targetStage || cfg.targetStage || 'compile';
return `回退→${target}`;
} catch {
return record.actionConfig || '-';
}
}
return record.sqlTemplate || '-';
}
async function loadEditorContext() {
const sourceTable = planRecord.value.sourceTable;
if (!sourceTable) return;
try {
const [cols, registryRes, docsRes] = await Promise.all([
getTableColumns(sourceTable),
getRegistryByTable(sourceTable),
listBizDocRegistry(),
]);
sourceColumns.value = (cols as any) || [];
sourceRegistry.value = registryRes || null;
bizDocList.value = (docsRes as any)?.records || (Array.isArray(docsRes) ? docsRes : []);
} catch {
sourceColumns.value = [];
sourceRegistry.value = null;
bizDocList.value = [];
}
}
async function open(plan: Recordable) {
planId.value = plan.id;
planName.value = plan.planName;
planRecord.value = plan;
visible.value = true;
await Promise.all([loadActions(), loadEditorContext()]);
}
async function loadActions() {
loading.value = true;
try {
actions.value = (await listActions(planId.value)) || [];
} finally {
loading.value = false;
}
}
function openVisualEditor(action?: Recordable) {
if (!planRecord.value.sourceTable) {
createMessage.warning('方案未配置触发业务表');
return;
}
visualEditorRef.value?.open({
sourceTable: planRecord.value.sourceTable,
sourceColumns: sourceColumns.value,
bizDocList: bizDocList.value,
sourceRegistry: sourceRegistry.value,
action: action
? { ...action, planId: planId.value }
: { planId: planId.value, execOrder: actions.value.length + 1 },
execOrder: action?.execOrder ?? actions.value.length + 1,
});
}
function handleAddAction() {
openVisualEditor();
}
function handleEditAction(record: Recordable) {
openVisualEditor(record);
}
async function handleDeleteAction(record: Recordable) {
await deleteAction({ id: record.id }, loadActions);
}
async function handleActionSaved(actionData: Recordable) {
const payload = {
...actionData,
planId: planId.value,
enabled: actionData.enabled !== false && actionData.enabled !== 0 ? 1 : 0,
};
try {
if (actionData.id) {
await editAction(payload);
} else {
await saveAction(payload);
}
createMessage.success('保存成功');
await loadActions();
} catch (e: any) {
createMessage.error(e?.message || '保存失败');
}
}
defineExpose({ open });
</script>