新增钉钉 Stream SDK 依赖,支持无 HTTP 上下文的后台线程显式传入 token 进行审批回调。同时,完善了 MES 审批台账功能,新增审批记录同步、批量发起审批时的门禁与台账写入逻辑,增强了系统的审批流管理能力。

This commit is contained in:
geht
2026-06-05 10:44:30 +08:00
parent 4785c55e52
commit fc4e3211ad
73 changed files with 5225 additions and 240 deletions

View File

@@ -0,0 +1,38 @@
import { defHttp } from '/@/utils/http/axios';
const BASE = '/xslmes/dingTplBind';
export const getMenuTree = () => defHttp.get({ url: `${BASE}/menuTree` });
export const getTplList = () => defHttp.get({ url: `${BASE}/tplList` });
export const getBizFields = (bizCode: string) =>
defHttp.get({ url: `${BASE}/bizFields`, params: { bizCode } });
export const getDetailSlots = (bizCode: string) =>
defHttp.get({ url: `${BASE}/detailSlots`, params: { bizCode } });
export const getDetailFields = (bizCode: string, detailProperty: string, slotKind = 'LIST') =>
defHttp.get({ url: `${BASE}/detailFields`, params: { bizCode, detailProperty, slotKind } });
export const getBindList = () => defHttp.get({ url: `${BASE}/list` });
export const getBindingByRoute = (routePath: string) =>
defHttp.get({ url: `${BASE}/bindingByRoute`, params: { routePath } }, { errorMessageMode: 'none' });
export const getByBizCode = (bizCode: string) =>
defHttp.get({ url: `${BASE}/getByBizCode`, params: { bizCode } });
export const saveBind = (data: {
bizCode: string;
bizName?: string;
templateId: string;
fieldMappingJson: string;
}) => defHttp.post({ url: `${BASE}/save`, data });
export const deleteBind = (id: string) =>
defHttp.delete({ url: `${BASE}/delete`, params: { id } });
/** 复用现有接口:拉取钉钉模板表单字段(含 dingFields */
export const getTemplateDetail = (id: string) =>
defHttp.get({ url: '/xslmes/mesXslDingProcessTpl/getTemplateDetail', params: { id } });

File diff suppressed because it is too large Load Diff

View File

@@ -15,6 +15,7 @@ enum Api {
batchImport = '/xslmes/mesXslDingProcessTpl/batchImport',
getTemplateDetail = '/xslmes/mesXslDingProcessTpl/getTemplateDetail',
saveFieldMapping = '/xslmes/mesXslDingProcessTpl/saveFieldMapping',
addNewTemplate = '/xslmes/mesXslDingProcessTpl/addNewTemplate',
createDingTemplate = '/xslmes/mesXslDingProcessTpl/createDingTemplate',
updateDingTemplate = '/xslmes/mesXslDingProcessTpl/updateDingTemplate',
launchApproval = '/xslmes/mesXslDingProcessTpl/launchApproval',
@@ -46,6 +47,10 @@ export const batchDelete = (params, handleSuccess) => {
export const saveOrUpdate = (params, isUpdate) =>
defHttp.post({ url: isUpdate ? Api.edit : Api.save, params }, { successMessageMode: 'none' });
/** 新增审批模板草稿(返回含 id 的完整记录) */
export const addNewTemplate = (params) =>
defHttp.post({ url: Api.addNewTemplate, params }, { successMessageMode: 'none' });
export const syncFromDingtalk = () => defHttp.get({ url: Api.syncFromDingtalk }, { successMessageMode: 'none' });
export const batchImport = (params) => defHttp.post({ url: Api.batchImport, params }, { successMessageMode: 'none' });

View File

@@ -51,8 +51,9 @@ export const formSchema: FormSchema[] = [
label: 'processCode',
field: 'processCode',
component: 'Input',
componentProps: { placeholder: '请输入钉钉processCode如 PROC-XXXXXXXX-...' },
dynamicRules: () => [{ required: true, message: '请输入processCode!' }],
componentProps: {
placeholder: '钉钉返回的 processCode;「新增审批模板」流程中可留空,设计器创建钉钉模板后自动回填',
},
},
{
label: '业务类型标识',

View File

@@ -2,8 +2,18 @@
<div>
<BasicTable @register="registerTable" :rowSelection="rowSelection">
<template #tableTitle>
<a-button type="primary" v-auth="'xslmes:mes_xsl_ding_process_tpl:add'" preIcon="ant-design:plus-outlined" @click="handleAdd">
新增
<!--update-begin---author:GHT ---date:2026-06-04 forMESToDing审批配置新增审批模板创建草稿+打开设计器-->
<a-button
type="primary"
v-auth="'xslmes:mes_xsl_ding_process_tpl:add'"
preIcon="ant-design:dingtalk-outlined"
@click="handleAddNewTemplate"
>
新增审批模板
</a-button>
<!--update-end---author:GHT ---date:2026-06-04 forMESToDing审批配置新增审批模板创建草稿+打开设计器-->
<a-button v-auth="'xslmes:mes_xsl_ding_process_tpl:add'" preIcon="ant-design:plus-outlined" @click="handleAdd">
快速录入
</a-button>
<a-button type="primary" v-auth="'xslmes:mes_xsl_ding_process_tpl:exportXls'" preIcon="ant-design:export-outlined" @click="onExportXls">
导出
@@ -88,6 +98,7 @@
<!--update-begin---author:GHT ---date:2026-06-03 forMESToDing审批配置表单设计器-->
<DingTplDesigner ref="designerRef" @success="handleSuccess" />
<DingTplCreateModal ref="createModalRef" @success="onNewTemplateCreated" />
<!--update-end---author:GHT ---date:2026-06-03 forMESToDing审批配置表单设计器-->
<!--update-begin---author:GHT ---date:2026-06-03 forMESToDing审批配置手动填表发起钉钉审批-->
@@ -119,7 +130,8 @@
>
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'imported'">
<a-tag :color="record.imported ? 'green' : 'default'">{{ record.imported ? '已导入' : '未导入' }}</a-tag>
<a-tag v-if="record.linkDraft" color="orange">待回填本地</a-tag>
<a-tag v-else :color="record.imported ? 'green' : 'default'">{{ record.imported ? '已导入' : '未导入' }}</a-tag>
</template>
</template>
</a-table>
@@ -138,6 +150,7 @@
import Icon from '/@/components/Icon';
import MesXslDingProcessTplModal from './components/MesXslDingProcessTplModal.vue';
import DingTplDesigner from './components/DingTplDesigner.vue';
import DingTplCreateModal from './components/DingTplCreateModal.vue';
//update-begin---author:GHT ---date:2026-06-03 for【MESToDing审批配置】手动填表发起钉钉审批
import DingApprovalLaunchModal from './components/DingApprovalLaunchModal.vue';
//update-end---author:GHT ---date:2026-06-03 for【MESToDing审批配置】手动填表发起钉钉审批
@@ -184,6 +197,19 @@
openModal(true, { isUpdate: false, showFooter: true });
}
const createModalRef = ref();
function handleAddNewTemplate() {
createModalRef.value?.open();
}
function onNewTemplateCreated({ record, openDesigner }: { record: Recordable; openDesigner: boolean }) {
reload();
if (openDesigner && record?.id) {
designerRef.value?.open(record);
}
}
function handleEdit(record: Recordable) {
openModal(true, { record, isUpdate: true, showFooter: true });
}
@@ -221,10 +247,20 @@
}
function getDropDownAction(record) {
return [
const actions: any[] = [
{ label: '详情', onClick: handleDetail.bind(null, record) },
//update-begin---author:GHT ---date:2026-06-03 for【MESToDing审批配置】新增设计模板入口
{ label: '设计模板', onClick: handleDesignTemplate.bind(null, record), icon: 'ant-design:layout-outlined' },
];
if (!record.processCode) {
actions.push({
label: '创建钉钉模板',
icon: 'ant-design:dingtalk-outlined',
onClick: handleDesignTemplate.bind(null, record),
auth: 'xslmes:mes_xsl_ding_process_tpl:edit',
});
}
actions.push(
{ label: '查看钉钉字段', onClick: handleShowDingSchema.bind(null, record), icon: 'ant-design:dingtalk-outlined' },
//update-end---author:GHT ---date:2026-06-03 for【MESToDing审批配置】新增设计模板入口
{
@@ -232,7 +268,8 @@
popConfirm: { title: '是否确认删除', confirm: handleDelete.bind(null, record), placement: 'topLeft' },
auth: 'xslmes:mes_xsl_ding_process_tpl:delete',
},
];
);
return actions;
}
// ===== 手动填表发起钉钉审批 =====
@@ -306,7 +343,9 @@
try {
const data = await syncFromDingtalk();
syncList.value = data || [];
syncSelectedKeys.value = (syncList.value as any[]).filter((r) => !r.imported).map((r) => r.processCode);
syncSelectedKeys.value = (syncList.value as any[])
.filter((r) => !r.imported || r.linkDraft)
.map((r) => r.processCode);
} catch (e: any) {
createMessage.error(e?.message || '从钉钉同步失败');
syncVisible.value = false;

View File

@@ -0,0 +1,110 @@
<!--
新增审批模板创建本地草稿并可选打开表单设计器在设计器中推送到钉钉
@author GHT
@date 2026-06-04 forMESToDing审批配置新增审批模板入口
-->
<template>
<a-modal
v-model:open="visible"
title="新增审批模板"
width="520px"
:confirmLoading="loading"
okText="创建并设计表单"
cancelText="取消"
destroy-on-close
@ok="handleSubmit"
@cancel="visible = false"
>
<a-alert
type="info"
show-icon
style="margin-bottom: 16px"
message="将先在 MES 创建模板配置随后在表单设计器中添加字段并点击「创建钉钉模板」推送到钉钉processCode 由钉钉返回)。"
/>
<a-form ref="formRef" :model="formState" :rules="rules" layout="vertical">
<a-form-item label="模板名称" name="tplName">
<a-input v-model:value="formState.tplName" placeholder="如密炼PS编制审批" allow-clear />
</a-form-item>
<a-form-item label="业务类型标识" name="bizType">
<a-input v-model:value="formState.bizType" placeholder="供审批流关联,如 mixer_ps" allow-clear />
</a-form-item>
<a-form-item label="备注" name="remark">
<a-textarea v-model:value="formState.remark" :rows="2" placeholder="可选" />
</a-form-item>
<a-form-item name="openDesigner">
<a-checkbox v-model:checked="formState.openDesigner">创建成功后打开表单设计器</a-checkbox>
</a-form-item>
</a-form>
</a-modal>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue';
import type { FormInstance } from 'ant-design-vue';
import { useMessage } from '/@/hooks/web/useMessage';
import { addNewTemplate } from '../MesXslDingProcessTpl.api';
const emit = defineEmits<{
(e: 'success', payload: { record: Recordable; openDesigner: boolean }): void;
}>();
const { createMessage } = useMessage();
const visible = ref(false);
const loading = ref(false);
const formRef = ref<FormInstance>();
const formState = reactive({
tplName: '',
bizType: '',
remark: '',
openDesigner: true,
});
const rules = {
tplName: [{ required: true, message: '请输入模板名称', trigger: 'blur' }],
};
function resetForm() {
formState.tplName = '';
formState.bizType = '';
formState.remark = '';
formState.openDesigner = true;
formRef.value?.clearValidate();
}
function open() {
resetForm();
visible.value = true;
}
async function handleSubmit() {
try {
await formRef.value?.validate();
} catch {
return;
}
loading.value = true;
try {
const record: any = await addNewTemplate({
tplName: formState.tplName.trim(),
bizType: formState.bizType?.trim() || undefined,
remark: formState.remark?.trim() || undefined,
status: '1',
sortNo: 0,
});
if (!record?.id) {
createMessage.error('创建失败:未返回模板 ID');
return;
}
createMessage.success('审批模板已创建');
visible.value = false;
emit('success', { record, openDesigner: formState.openDesigner });
} catch (e: any) {
createMessage.error(e?.message || '创建失败');
} finally {
loading.value = false;
}
}
defineExpose({ open });
</script>

View File

@@ -747,7 +747,11 @@
try {
await saveFieldMapping({ id: tplData.value.id, formFields: serializeComponents() });
const res: any = await createDingTemplate({ id: tplData.value.id });
const processCode = res?.processCode || res?.result?.processCode;
const processCode = res?.processCode;
if (!processCode) {
createMessage.warning('钉钉已创建,但未拿到 processCode请稍后使用「从钉钉同步」回填');
return;
}
createMessage.success('钉钉模板创建成功processCode=' + processCode);
if (processCode) tplData.value = { ...tplData.value, processCode };
emit('success');