新增钉钉 Stream SDK 依赖,支持无 HTTP 上下文的后台线程显式传入 token 进行审批回调。同时,完善了 MES 审批台账功能,新增审批记录同步、批量发起审批时的门禁与台账写入逻辑,增强了系统的审批流管理能力。
This commit is contained in:
31
jeecgboot-vue3/src/views/approval/gate/approvalGate.api.ts
Normal file
31
jeecgboot-vue3/src/views/approval/gate/approvalGate.api.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
|
||||
enum Api {
|
||||
canLaunch = '/xslmes/approvalGate/canLaunch',
|
||||
canLaunchBatch = '/xslmes/approvalGate/canLaunchBatch',
|
||||
history = '/xslmes/approvalGate/history',
|
||||
}
|
||||
|
||||
export interface ApprovalGateVo {
|
||||
allowed?: boolean;
|
||||
reason?: string;
|
||||
bizTable?: string;
|
||||
bizDataId?: string;
|
||||
latestRecordId?: string;
|
||||
latestStatus?: string;
|
||||
latestChannel?: string;
|
||||
latestChannelText?: string;
|
||||
latestStatusText?: string;
|
||||
}
|
||||
|
||||
/** 检查是否允许发起审批 */
|
||||
export const checkCanLaunch = (params: { bizTable: string; bizDataId: string }) =>
|
||||
defHttp.get<ApprovalGateVo>({ url: Api.canLaunch, params });
|
||||
|
||||
/** 批量检查是否允许发起审批 */
|
||||
export const checkCanLaunchBatch = (params: { bizTable: string; bizDataIds: string[] }) =>
|
||||
defHttp.post<ApprovalGateVo[]>({ url: Api.canLaunchBatch, params });
|
||||
|
||||
/** 查询业务单据审批台账历史 */
|
||||
export const getApprovalHistory = (params: { bizTable: string; bizDataId: string }) =>
|
||||
defHttp.get({ url: Api.history, params });
|
||||
@@ -64,6 +64,19 @@ export const thirdAppFormSchema: FormSchema[] = [
|
||||
unCheckedValue: 0
|
||||
},
|
||||
defaultValue: 1
|
||||
},{
|
||||
label: 'Stream事件推送',
|
||||
field: 'streamEnabled',
|
||||
component: 'Switch',
|
||||
ifShow: ({ values }) => values.thirdType === 'dingtalk',
|
||||
componentProps: {
|
||||
checkedChildren: '已启用',
|
||||
checkedValue: 1,
|
||||
unCheckedChildren: '未启用',
|
||||
unCheckedValue: 0,
|
||||
},
|
||||
helpMessage: '开启后,此应用的 AppKey/AppSecret 将用于接收钉钉 Stream 事件推送(审批结果等)。同一企业只需开启一个',
|
||||
defaultValue: 0,
|
||||
},{
|
||||
label: '租户id',
|
||||
field: 'tenantId',
|
||||
|
||||
@@ -45,6 +45,14 @@
|
||||
<a-input-password v-model:value="appConfigData.clientSecret" readonly />
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-flow">
|
||||
<div class="base-title">Stream推送</div>
|
||||
<div class="base-message" style="display:flex;align-items:center;height:50px;">
|
||||
<a-tag :color="appConfigData.streamEnabled === 1 ? 'green' : 'default'">
|
||||
{{ appConfigData.streamEnabled === 1 ? '已设为Stream主配置' : '未启用' }}
|
||||
</a-tag>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-top: 20px; width: 100%; text-align: right">
|
||||
<a-button @click="dingEditClick">编辑</a-button>
|
||||
<a-button v-if="appConfigData.id" @click="cancelBindClick" danger style="margin-left: 10px">取消绑定</a-button>
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import { defHttp } from '/@/utils/http/axios';
|
||||
|
||||
enum Api {
|
||||
list = '/xslmes/mesXslApprovalRecord/list',
|
||||
queryById = '/xslmes/mesXslApprovalRecord/queryById',
|
||||
exportXls = '/xslmes/mesXslApprovalRecord/exportXls',
|
||||
}
|
||||
|
||||
export const getExportUrl = Api.exportXls;
|
||||
|
||||
export const list = (params) => defHttp.get({ url: Api.list, params });
|
||||
|
||||
export const queryById = (params: { id: string }) => defHttp.get({ url: Api.queryById, params });
|
||||
@@ -0,0 +1,78 @@
|
||||
import { BasicColumn, FormSchema } from '/@/components/Table';
|
||||
|
||||
export const columns: BasicColumn[] = [
|
||||
{ title: '业务单据', align: 'center', dataIndex: 'bizTableName', width: 120, ellipsis: true },
|
||||
{ title: '业务标题', align: 'center', dataIndex: 'bizTitle', width: 180, ellipsis: true },
|
||||
{ title: '审批通道', align: 'center', dataIndex: 'channel_dictText', width: 100 },
|
||||
{ title: '状态', align: 'center', dataIndex: 'status_dictText', width: 100 },
|
||||
{ title: '发起次数', align: 'center', dataIndex: 'launchNo', width: 80 },
|
||||
{ title: '发起人', align: 'center', dataIndex: 'applyUserName', width: 100 },
|
||||
{
|
||||
title: '发起时间',
|
||||
align: 'center',
|
||||
dataIndex: 'applyTime',
|
||||
width: 165,
|
||||
customRender: ({ text }) => (!text ? '' : String(text).length > 19 ? String(text).substring(0, 19) : text),
|
||||
},
|
||||
{
|
||||
title: '办结时间',
|
||||
align: 'center',
|
||||
dataIndex: 'finishTime',
|
||||
width: 165,
|
||||
customRender: ({ text }) => (!text ? '' : String(text).length > 19 ? String(text).substring(0, 19) : text),
|
||||
},
|
||||
{ title: 'MES审批流', align: 'center', dataIndex: 'flowName', width: 140, ellipsis: true },
|
||||
{ title: '钉钉模板', align: 'center', dataIndex: 'templateName', width: 140, ellipsis: true },
|
||||
{ title: '外部实例ID', align: 'center', dataIndex: 'externalInstanceId', width: 160, ellipsis: true },
|
||||
{ title: '备注', align: 'center', dataIndex: 'remark', width: 160, ellipsis: true },
|
||||
];
|
||||
|
||||
export const searchFormSchema: FormSchema[] = [
|
||||
{ label: '业务单据', field: 'bizTableName', component: 'Input', colProps: { span: 6 } },
|
||||
{ label: '业务标题', field: 'bizTitle', component: 'Input', colProps: { span: 6 } },
|
||||
{
|
||||
label: '审批通道',
|
||||
field: 'channel',
|
||||
component: 'JDictSelectTag',
|
||||
componentProps: { dictCode: 'mes_xsl_approval_channel', placeholder: '请选择' },
|
||||
colProps: { span: 6 },
|
||||
},
|
||||
{
|
||||
label: '状态',
|
||||
field: 'status',
|
||||
component: 'JDictSelectTag',
|
||||
componentProps: { dictCode: 'mes_xsl_approval_record_status', placeholder: '请选择' },
|
||||
colProps: { span: 6 },
|
||||
},
|
||||
{ label: '发起人', field: 'applyUserName', component: 'Input', colProps: { span: 6 } },
|
||||
];
|
||||
|
||||
export const detailFormSchema: FormSchema[] = [
|
||||
{ label: '', field: 'id', component: 'Input', show: false },
|
||||
{ label: '业务单据', field: 'bizTableName', component: 'Input', componentProps: { readonly: true } },
|
||||
{ label: '业务表名', field: 'bizTable', component: 'Input', componentProps: { readonly: true } },
|
||||
{ label: '业务数据ID', field: 'bizDataId', component: 'Input', componentProps: { readonly: true } },
|
||||
{ label: '业务标题', field: 'bizTitle', component: 'Input', componentProps: { readonly: true } },
|
||||
{ label: '业务编码', field: 'bizCode', component: 'Input', componentProps: { readonly: true } },
|
||||
{
|
||||
label: '审批通道',
|
||||
field: 'channel',
|
||||
component: 'JDictSelectTag',
|
||||
componentProps: { dictCode: 'mes_xsl_approval_channel', disabled: true },
|
||||
},
|
||||
{
|
||||
label: '状态',
|
||||
field: 'status',
|
||||
component: 'JDictSelectTag',
|
||||
componentProps: { dictCode: 'mes_xsl_approval_record_status', disabled: true },
|
||||
},
|
||||
{ label: '发起次数', field: 'launchNo', component: 'InputNumber', componentProps: { disabled: true, style: { width: '100%' } } },
|
||||
{ label: '发起人账号', field: 'applyUser', component: 'Input', componentProps: { readonly: true } },
|
||||
{ label: '发起人姓名', field: 'applyUserName', component: 'Input', componentProps: { readonly: true } },
|
||||
{ label: '发起时间', field: 'applyTime', component: 'Input', componentProps: { readonly: true } },
|
||||
{ label: '办结时间', field: 'finishTime', component: 'Input', componentProps: { readonly: true } },
|
||||
{ label: 'MES审批流', field: 'flowName', component: 'Input', componentProps: { readonly: true } },
|
||||
{ label: '钉钉模板', field: 'templateName', component: 'Input', componentProps: { readonly: true } },
|
||||
{ label: '外部实例ID', field: 'externalInstanceId', component: 'Input', componentProps: { readonly: true } },
|
||||
{ label: '备注', field: 'remark', component: 'InputTextArea', componentProps: { rows: 3, readonly: true } },
|
||||
];
|
||||
@@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<div>
|
||||
<BasicTable @register="registerTable">
|
||||
<template #tableTitle>
|
||||
<a-button
|
||||
type="primary"
|
||||
v-auth="'xslmes:mes_xsl_approval_record:exportXls'"
|
||||
preIcon="ant-design:export-outlined"
|
||||
@click="onExportXls"
|
||||
>
|
||||
导出
|
||||
</a-button>
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<TableAction
|
||||
:actions="[
|
||||
{
|
||||
label: '详情',
|
||||
onClick: handleDetail.bind(null, record),
|
||||
auth: 'xslmes:mes_xsl_approval_record:list',
|
||||
},
|
||||
]"
|
||||
/>
|
||||
</template>
|
||||
</BasicTable>
|
||||
<MesXslApprovalRecordDetailModal @register="registerModal" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="xslmes-mesXslApprovalRecord" setup>
|
||||
import { BasicTable, TableAction } from '/@/components/Table';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
import { useListPage } from '/@/hooks/system/useListPage';
|
||||
import MesXslApprovalRecordDetailModal from './components/MesXslApprovalRecordDetailModal.vue';
|
||||
import { columns, searchFormSchema } from './MesXslApprovalRecord.data';
|
||||
import { list, getExportUrl } from './MesXslApprovalRecord.api';
|
||||
|
||||
const [registerModal, { openModal }] = useModal();
|
||||
|
||||
const { tableContext, onExportXls } = useListPage({
|
||||
tableProps: {
|
||||
title: '审批台账',
|
||||
api: list,
|
||||
columns,
|
||||
canResize: true,
|
||||
formConfig: {
|
||||
schemas: searchFormSchema,
|
||||
labelWidth: 100,
|
||||
autoSubmitOnEnter: true,
|
||||
showAdvancedButton: true,
|
||||
},
|
||||
actionColumn: {
|
||||
width: 100,
|
||||
fixed: 'right',
|
||||
},
|
||||
},
|
||||
exportConfig: {
|
||||
name: 'MES审批台账',
|
||||
url: getExportUrl,
|
||||
},
|
||||
});
|
||||
|
||||
const [registerTable] = tableContext;
|
||||
|
||||
function handleDetail(record: Recordable) {
|
||||
openModal(true, { record });
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,33 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" title="审批台账详情" width="720px" :showOkBtn="false" cancelText="关闭">
|
||||
<BasicForm @register="registerForm" />
|
||||
</BasicModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, unref } from 'vue';
|
||||
import { BasicModal, useModalInner } from '/@/components/Modal';
|
||||
import { BasicForm, useForm } from '/@/components/Form';
|
||||
import { detailFormSchema } from '../MesXslApprovalRecord.data';
|
||||
import { queryById } from '../MesXslApprovalRecord.api';
|
||||
|
||||
const [registerForm, { setFieldsValue, resetFields }] = useForm({
|
||||
labelWidth: 110,
|
||||
schemas: detailFormSchema,
|
||||
showActionButtonGroup: false,
|
||||
baseColProps: { span: 24 },
|
||||
});
|
||||
|
||||
const recordId = ref('');
|
||||
|
||||
const [registerModal, { setModalProps }] = useModalInner(async (data) => {
|
||||
await resetFields();
|
||||
setModalProps({ confirmLoading: false });
|
||||
recordId.value = data?.record?.id || '';
|
||||
if (!unref(recordId)) {
|
||||
return;
|
||||
}
|
||||
const res = await queryById({ id: unref(recordId) });
|
||||
await setFieldsValue({ ...res });
|
||||
});
|
||||
</script>
|
||||
@@ -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 } });
|
||||
1132
jeecgboot-vue3/src/views/xslmes/dingtalk/dingTplBind/index.vue
Normal file
1132
jeecgboot-vue3/src/views/xslmes/dingtalk/dingTplBind/index.vue
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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' });
|
||||
|
||||
@@ -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: '业务类型标识',
|
||||
|
||||
@@ -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 for:【MESToDing审批配置】新增审批模板(创建草稿+打开设计器)-->
|
||||
<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 for:【MESToDing审批配置】新增审批模板(创建草稿+打开设计器)-->
|
||||
<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 for:【MESToDing审批配置】表单设计器-->
|
||||
<DingTplDesigner ref="designerRef" @success="handleSuccess" />
|
||||
<DingTplCreateModal ref="createModalRef" @success="onNewTemplateCreated" />
|
||||
<!--update-end---author:GHT ---date:2026-06-03 for:【MESToDing审批配置】表单设计器-->
|
||||
|
||||
<!--update-begin---author:GHT ---date:2026-06-03 for:【MESToDing审批配置】手动填表发起钉钉审批-->
|
||||
@@ -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;
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
<!--
|
||||
新增审批模板:创建本地草稿并可选打开表单设计器,在设计器中推送到钉钉
|
||||
@author GHT
|
||||
@date 2026-06-04 for:【MESToDing审批配置】新增审批模板入口
|
||||
-->
|
||||
<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>
|
||||
@@ -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');
|
||||
|
||||
@@ -12,7 +12,6 @@ enum Api {
|
||||
proofread = '/xslmes/mesXslMixerPsCompile/proofread',
|
||||
audit = '/xslmes/mesXslMixerPsCompile/audit',
|
||||
approve = '/xslmes/mesXslMixerPsCompile/approve',
|
||||
ddApprovalTest = '/xslmes/mesXslMixerPsCompile/ddApprovalTest',
|
||||
}
|
||||
|
||||
export const list = (params) => defHttp.get({ url: Api.list, params });
|
||||
@@ -38,5 +37,3 @@ export const proofread = (params: { ids: string }) => defHttp.post({ url: Api.pr
|
||||
export const audit = (params: { ids: string }) => defHttp.post({ url: Api.audit, params }, { joinParamsToUrl: true });
|
||||
|
||||
export const approve = (params: { ids: string }) => defHttp.post({ url: Api.approve, params }, { joinParamsToUrl: true });
|
||||
|
||||
export const ddApprovalTest = () => defHttp.post({ url: Api.ddApprovalTest }, { successMessageMode: 'none' });
|
||||
|
||||
@@ -65,11 +65,6 @@
|
||||
</a-button>
|
||||
</a-dropdown>
|
||||
<super-query :config="superQueryConfig" @search="handleSuperQuery" />
|
||||
<!--update-begin---author:GHT ---date:2026-06-03 for:【钉钉PROC-GENERIC可行性验证】测试按钮-->
|
||||
<a-button :loading="ddTestLoading" preIcon="ant-design:dingtalk-outlined" @click="handleDdApprovalTest">
|
||||
钉钉审批测试
|
||||
</a-button>
|
||||
<!--update-end---author:GHT ---date:2026-06-03 for:【钉钉PROC-GENERIC可行性验证】测试按钮-->
|
||||
</template>
|
||||
<template #action="{ record }">
|
||||
<TableAction
|
||||
@@ -86,19 +81,11 @@
|
||||
</template>
|
||||
</BasicTable>
|
||||
<MesXslMixerPsCompileModal @register="registerModal" @success="handleSuccess" />
|
||||
|
||||
<!--update-begin---author:GHT ---date:2026-06-03 for:【钉钉PROC-GENERIC可行性验证】结果弹窗-->
|
||||
<a-modal v-model:open="ddTestVisible" title="钉钉审批测试结果" width="680px" :footer="null">
|
||||
<a-spin :spinning="ddTestLoading">
|
||||
<pre style="max-height: 520px; overflow: auto; font-size: 12px; background: #f6f8fa; padding: 14px; border-radius: 6px; white-space: pre-wrap; word-break: break-all">{{ ddTestResult }}</pre>
|
||||
</a-spin>
|
||||
</a-modal>
|
||||
<!--update-end---author:GHT ---date:2026-06-03 for:【钉钉PROC-GENERIC可行性验证】结果弹窗-->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="xslmes-mesXslMixerPsCompile" setup>
|
||||
import { computed, reactive, ref } from 'vue';
|
||||
import { computed, reactive } from 'vue';
|
||||
import { Modal } from 'ant-design-vue';
|
||||
import { BasicTable, TableAction } from '/@/components/Table';
|
||||
import { useModal } from '/@/components/Modal';
|
||||
@@ -116,7 +103,6 @@
|
||||
proofread,
|
||||
audit,
|
||||
approve,
|
||||
ddApprovalTest,
|
||||
} from './MesXslMixerPsCompile.api';
|
||||
|
||||
const { createMessage } = useMessage();
|
||||
@@ -242,26 +228,6 @@
|
||||
selectedRowKeys.value = [];
|
||||
}
|
||||
|
||||
//update-begin---author:GHT ---date:2026-06-03 for:【钉钉PROC-GENERIC可行性验证】测试逻辑
|
||||
const ddTestVisible = ref(false);
|
||||
const ddTestLoading = ref(false);
|
||||
const ddTestResult = ref('');
|
||||
|
||||
async function handleDdApprovalTest() {
|
||||
ddTestVisible.value = true;
|
||||
ddTestLoading.value = true;
|
||||
ddTestResult.value = '正在请求,请稍候...';
|
||||
try {
|
||||
const res = await ddApprovalTest();
|
||||
ddTestResult.value = JSON.stringify(res, null, 2);
|
||||
} catch (e: any) {
|
||||
ddTestResult.value = '请求失败:\n' + (e?.message || JSON.stringify(e));
|
||||
} finally {
|
||||
ddTestLoading.value = false;
|
||||
}
|
||||
}
|
||||
//update-end---author:GHT ---date:2026-06-03 for:【钉钉PROC-GENERIC可行性验证】测试逻辑
|
||||
|
||||
function getDropDownAction(record: Recordable) {
|
||||
return [
|
||||
{ label: '详情', onClick: handleDetail.bind(null, record) },
|
||||
|
||||
Reference in New Issue
Block a user