|
@@ -0,0 +1,338 @@
|
|
|
|
+<template>
|
|
|
|
+ <el-dialog v-model="state.dialogVisible" draggable title="甄别审批" ref="dialogRef" width="50%" append-to-body destroy-on-close @close="close">
|
|
|
|
+ <el-form label-width="110px" :model="state.infoForm" class="show-info-form" v-loading="state.loading">
|
|
|
|
+ <p class="border-title">申请信息</p>
|
|
|
|
+ <el-row :gutter="10">
|
|
|
|
+ <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
|
|
|
|
+ <el-form-item label="工单编号"> {{ state.infoForm?.no }} </el-form-item>
|
|
|
|
+ </el-col>
|
|
|
|
+ <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
|
|
|
|
+ <el-form-item label="工单标题"> {{ state.infoForm?.visit?.order?.title }} </el-form-item>
|
|
|
|
+ </el-col>
|
|
|
|
+ <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
|
|
|
|
+ <el-form-item label="申请人">{{ state.infoForm?.creatorName }} </el-form-item>
|
|
|
|
+ </el-col>
|
|
|
|
+ <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
|
|
|
|
+ <el-form-item label="申请部门">{{ state.infoForm?.creatorOrgName }} </el-form-item>
|
|
|
|
+ </el-col>
|
|
|
|
+ <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
|
|
|
|
+ <el-form-item label="申请时间">{{ formatDate(state.infoForm?.creationTime, 'YYYY-mm-dd HH:MM:SS') }} </el-form-item>
|
|
|
|
+ </el-col>
|
|
|
|
+ <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
|
|
|
|
+ <el-form-item label="申请类型"> {{ state.infoForm?.typeDicName }} </el-form-item>
|
|
|
|
+ </el-col>
|
|
|
|
+ <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
|
|
|
|
+ <el-form-item label="发起甄别耗时"> {{ state.infoForm?.timeConsuminText }} </el-form-item>
|
|
|
|
+ </el-col>
|
|
|
|
+ <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
|
|
|
|
+ <el-form-item label="是否省甄别">{{ state.infoForm?.isProvince ? '是' : '否' }} </el-form-item>
|
|
|
|
+ </el-col>
|
|
|
|
+ <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
|
|
|
|
+ <el-form-item label="申请理由" class="formatted-text">{{ state.infoForm?.content }} </el-form-item>
|
|
|
|
+ </el-col>
|
|
|
|
+ <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
|
|
|
|
+ <el-form-item label="附件"
|
|
|
|
+ ><annex-list name="甄别申请附件" v-model="state.infoForm.files" readonly classify="甄别申请附件" />
|
|
|
|
+ </el-form-item>
|
|
|
|
+ </el-col>
|
|
|
|
+ </el-row>
|
|
|
|
+ </el-form>
|
|
|
|
+ <p class="border-title mt10 mb10">甄别审核明细</p>
|
|
|
|
+ <vxe-table :expand-config="{ trigger: 'row', padding: true }" border :data="tableData" :loading="state.loading">
|
|
|
|
+ <vxe-column type="seq" width="70"></vxe-column>
|
|
|
|
+ <vxe-column type="expand" width="60">
|
|
|
|
+ <template #content="{ row }">
|
|
|
|
+ <div class="mb5">审核意见:{{ row.opinion }}</div>
|
|
|
|
+ <annex-list name="附件" readonly businessId="" classify="查看附件" v-model="row.files" />
|
|
|
|
+ </template>
|
|
|
|
+ </vxe-column>
|
|
|
|
+ <vxe-column field="name" title="节点"></vxe-column>
|
|
|
|
+ <vxe-column field="handlerName" title="审批人"></vxe-column>
|
|
|
|
+ <vxe-column field="handleTime" title="审批时间" width="160">
|
|
|
|
+ <template #default="{ row }">{{ formatDate(row.handleTime, 'YYYY-mm-dd HH:MM:SS') }}</template>
|
|
|
|
+ </vxe-column>
|
|
|
|
+ <vxe-column field="traceTypeText" title="状态"></vxe-column>
|
|
|
|
+ </vxe-table>
|
|
|
|
+ <p class="border-title mt20 mb10">审核意见</p>
|
|
|
|
+ <el-form :model="state.ruleForm" label-width="110px" ref="ruleFormRef" v-loading="state.loading">
|
|
|
|
+ <el-row :gutter="10">
|
|
|
|
+ <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
|
|
|
|
+ <el-form-item label="审批结果" prop="isPass" :rules="[{ required: true, message: '请选择审批结果', trigger: 'change' }]">
|
|
|
|
+ <el-radio-group v-model="state.ruleForm.isPass">
|
|
|
|
+ <el-radio :value="true">同意</el-radio>
|
|
|
|
+ <el-radio :value="false">不同意</el-radio>
|
|
|
|
+ </el-radio-group>
|
|
|
|
+ </el-form-item>
|
|
|
|
+ </el-col>
|
|
|
|
+ <template v-if="state.ruleForm.isPass">
|
|
|
|
+ <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
|
|
|
|
+ <el-form-item label="下一环节" prop="nextStepCode" :rules="[{ required: true, message: '请选择下一环节', trigger: 'change' }]">
|
|
|
|
+ <el-select v-model="state.ruleForm.nextStepCode" placeholder="请选择下一环节" class="w100" @change="selectNextStep">
|
|
|
|
+ <el-option v-for="item in state.nextStepOptions" :key="item.key" :label="item.value" :value="item.key" />
|
|
|
|
+ </el-select>
|
|
|
|
+ </el-form-item>
|
|
|
|
+ </el-col>
|
|
|
|
+ <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" v-if="showHandlers">
|
|
|
|
+ <el-form-item
|
|
|
|
+ label="办理对象"
|
|
|
|
+ prop="nextHandlers"
|
|
|
|
+ :rules="[{ required: nextHandlersRequired, message: '请选择办理对象', trigger: 'change' }]"
|
|
|
|
+ >
|
|
|
|
+ <el-select-v2
|
|
|
|
+ v-model="state.ruleForm.nextHandlers"
|
|
|
|
+ :options="state.handlerOptions"
|
|
|
|
+ placeholder="请选择办理对象"
|
|
|
|
+ class="w100"
|
|
|
|
+ multiple
|
|
|
|
+ clearable
|
|
|
|
+ collapse-tags
|
|
|
|
+ collapse-tags-tooltip
|
|
|
|
+ filterable
|
|
|
|
+ value-key="key"
|
|
|
|
+ @change="selectHandlers"
|
|
|
|
+ :multiple-limit="multipleLimit"
|
|
|
|
+ />
|
|
|
|
+ </el-form-item>
|
|
|
|
+ </el-col>
|
|
|
|
+ </template>
|
|
|
|
+ <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
|
|
|
|
+ <el-form-item label="" prop="isSms">
|
|
|
|
+ <el-checkbox v-model="state.ruleForm.isSms" label="短信通知" />
|
|
|
|
+ </el-form-item>
|
|
|
|
+ </el-col>
|
|
|
|
+ <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
|
|
|
|
+ <el-form-item label="审批意见" prop="opinion" :rules="[{ required: true, message: '请填写审批意见', trigger: 'blur' }]">
|
|
|
|
+ <common-advice
|
|
|
|
+ @chooseAdvice="chooseAdvice"
|
|
|
|
+ v-model="state.ruleForm.opinion"
|
|
|
|
+ placeholder="请填写审批意见"
|
|
|
|
+ :loading="state.loading"
|
|
|
|
+ :commonEnum="commonEnum.Discriminate"
|
|
|
|
+ :maxlength="AppConfigInfo.handleOpinionWordLimit"
|
|
|
|
+ />
|
|
|
|
+ </el-form-item>
|
|
|
|
+ </el-col>
|
|
|
|
+ <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
|
|
|
|
+ <el-form-item label="附件" :rules="[{ required: false, message: '请上传附件', trigger: 'change' }]">
|
|
|
|
+ <annex-list name="甄别附件" classify="甄别上传" v-model:format="handleFiles" />
|
|
|
|
+ </el-form-item>
|
|
|
|
+ </el-col>
|
|
|
|
+ </el-row>
|
|
|
|
+ </el-form>
|
|
|
|
+ <template #footer>
|
|
|
|
+ <span class="dialog-footer">
|
|
|
|
+ <el-button @click="closeDialog" class="default-button">取 消</el-button>
|
|
|
|
+ <el-button type="primary" @click="onSubmit(ruleFormRef)" :loading="state.loading">确定</el-button>
|
|
|
|
+ </span>
|
|
|
|
+ </template>
|
|
|
|
+ </el-dialog>
|
|
|
|
+</template>
|
|
|
|
+<script setup lang="ts" name="discernDetail">
|
|
|
|
+import { computed, defineAsyncComponent, reactive, ref } from 'vue';
|
|
|
|
+import { formatDate } from '@/utils/formatTime';
|
|
|
|
+import { discernApproveParams, screenDetail } from '@/api/business/discern';
|
|
|
|
+import { transformFile } from '@/utils/tools';
|
|
|
|
+import { ElMessage, FormInstance } from 'element-plus';
|
|
|
|
+import { workflowNext, workflowReject, workflowTraces } from '@/api/system/workflow';
|
|
|
|
+import { commonEnum } from '@/utils/constants';
|
|
|
|
+import { useAppConfig } from '@/stores/appConfig';
|
|
|
|
+import { storeToRefs } from 'pinia';
|
|
|
|
+import other from '@/utils/other';
|
|
|
|
+
|
|
|
|
+const AnnexList = defineAsyncComponent(() => import('@/components/AnnexList/index.vue')); // 附件列表
|
|
|
|
+const CommonAdvice = defineAsyncComponent(() => import('@/components/CommonAdvice/index.vue')); // 常用意见
|
|
|
|
+
|
|
|
|
+const appConfigStore = useAppConfig();
|
|
|
|
+const { AppConfigInfo } = storeToRefs(appConfigStore); // 系统配置信息
|
|
|
|
+const emit = defineEmits(['updateList']); // 定义事件
|
|
|
|
+// 定义变量内容
|
|
|
|
+const state = reactive<any>({
|
|
|
|
+ dialogVisible: false, // 是否显示弹窗
|
|
|
|
+ loading: false, // 是否显示加载
|
|
|
|
+ infoForm: {},
|
|
|
|
+ ruleForm: {
|
|
|
|
+ isPass: true,
|
|
|
|
+ nextHandlers: [],
|
|
|
|
+ isSms: false,
|
|
|
|
+ opinion: '', // 意见
|
|
|
|
+ nextStepCode: '', // 下一节点
|
|
|
|
+ nextStepName: '', // 下一节点名称
|
|
|
|
+ backToCountersignEnd: false,
|
|
|
|
+ stepId: null,
|
|
|
|
+ workflowId: null,
|
|
|
|
+ },
|
|
|
|
+ handlerOptions: [],
|
|
|
|
+ nextStepOptions: [],
|
|
|
|
+});
|
|
|
|
+
|
|
|
|
+const tableData = ref<EmptyArrayType>([]);
|
|
|
|
+/*
|
|
|
|
+ * @params row 工单详情
|
|
|
|
+ * @description 打开弹窗
|
|
|
|
+ * */
|
|
|
|
+const openDialog = async (row: any) => {
|
|
|
|
+ state.dialogVisible = true;
|
|
|
|
+ state.loading = true;
|
|
|
|
+ try {
|
|
|
|
+ const { result } = await screenDetail(row.id);
|
|
|
|
+ state.infoForm = result ?? {};
|
|
|
|
+ state.infoForm.files = transformFile(state.infoForm.files);
|
|
|
|
+ state.ruleForm.workflowId = row.workflowId;
|
|
|
|
+ await getWorkflow(result.workflowId);
|
|
|
|
+ await selectNextStepOptions(result.workflowId);
|
|
|
|
+ state.loading = false;
|
|
|
|
+ } catch (e) {
|
|
|
|
+ console.log(e);
|
|
|
|
+ state.loading = false;
|
|
|
|
+ }
|
|
|
|
+};
|
|
|
|
+const formatTraces = (val: any) => {
|
|
|
|
+ if (!val || !val.length) return [];
|
|
|
|
+ val.forEach((item: any) => {
|
|
|
|
+ item.files = transformFile(item.files);
|
|
|
|
+ });
|
|
|
|
+ return val;
|
|
|
|
+};
|
|
|
|
+// 获取流程
|
|
|
|
+const getWorkflow = async (workflowId: string) => {
|
|
|
|
+ try {
|
|
|
|
+ // 查询审核记录
|
|
|
|
+ const { result } = await workflowTraces(workflowId);
|
|
|
|
+ tableData.value = formatTraces(result?.traces);
|
|
|
|
+ } catch (e) {
|
|
|
|
+ console.log(e);
|
|
|
|
+ }
|
|
|
|
+};
|
|
|
|
+const nextHandlersRequired = ref<Boolean>(false); // 办理对象是否必填
|
|
|
|
+const canStartCountersign = ref<Boolean>(false); // 是否可以会签
|
|
|
|
+const selectNext = ref<any>({});
|
|
|
|
+// 查询流程办理节点
|
|
|
|
+const selectNextStepOptions = async (workflowId: string) => {
|
|
|
|
+ try {
|
|
|
|
+ const { result } = await discernApproveParams(workflowId); //获取甄别审批流程参数
|
|
|
|
+ state.nextStepOptions = result.steps; //办理对象选择内容
|
|
|
|
+ canStartCountersign.value = result.canStartCountersign ?? false; // 是否可以发起会签
|
|
|
|
+ if (state.nextStepOptions.length === 1) {
|
|
|
|
+ // 下一节点是否只有一个 默认选中第一个
|
|
|
|
+ setTimeout(() => {
|
|
|
|
+ state.ruleForm.nextStepCode = state.nextStepOptions[0].key; // 下一节点code
|
|
|
|
+ state.ruleForm.nextStepName = state.nextStepOptions[0].value; // 下一节点name
|
|
|
|
+ state.ruleForm.backToCountersignEnd = state.nextStepOptions[0].backToCountersignEnd ?? false; // 是否回到会签结束节点
|
|
|
|
+ }, 100);
|
|
|
|
+ selectNextStep(state.nextStepOptions[0].key); // 查询流程下一节点参数
|
|
|
|
+ } else {
|
|
|
|
+ state.ruleForm.nextStepCode = '';
|
|
|
|
+ state.ruleForm.nextStepName = '';
|
|
|
|
+ }
|
|
|
|
+ state.ruleForm.stepId = result.stepId;
|
|
|
|
+ state.loading = false;
|
|
|
|
+ } catch (e) {
|
|
|
|
+ console.log(e);
|
|
|
|
+ }
|
|
|
|
+};
|
|
|
|
+const selectNextStep = (val: any) => {
|
|
|
|
+ ruleFormRef.value?.resetFields('nextHandlers');
|
|
|
|
+ const next = state.nextStepOptions.find((item: any) => item.key === val);
|
|
|
|
+ selectNext.value = next;
|
|
|
|
+ const items = next.items; //获取下一节点
|
|
|
|
+ state.ruleForm.nextStepName = next.value; // 下一节点name
|
|
|
|
+ state.ruleForm.handlerType = next.handlerType;
|
|
|
|
+ state.ruleForm.businessType = next.businessType;
|
|
|
|
+ state.ruleForm.flowDirection = next.flowDirection;
|
|
|
|
+ state.ruleForm.backToCountersignEnd = next.backToCountersignEnd ?? false; // 是否回到会签结束节点
|
|
|
|
+ state.handlerOptions = items ?? [];
|
|
|
|
+ state.handlerOptions = state.handlerOptions.map((item: any) => {
|
|
|
|
+ return {
|
|
|
|
+ value: {
|
|
|
|
+ ...item,
|
|
|
|
+ },
|
|
|
|
+ label: item.value,
|
|
|
|
+ };
|
|
|
|
+ });
|
|
|
|
+ // 办理对象是否必填
|
|
|
|
+ nextHandlersRequired.value = ![0].includes(next.handlerType);
|
|
|
|
+
|
|
|
|
+ if (items.length === 1) {
|
|
|
|
+ // 如果办理对象只有一个默认选中
|
|
|
|
+ state.ruleForm.nextHandlers = [items[0]];
|
|
|
|
+ }
|
|
|
|
+};
|
|
|
|
+// 选择办理对象
|
|
|
|
+const selectHandlers = () => {
|
|
|
|
+ if (state.ruleForm.nextHandlers.length <= 1) {
|
|
|
|
+ // 如果只有一个办理对象就不需要发起会签
|
|
|
|
+ state.ruleForm.isStartCountersign = false;
|
|
|
|
+ }
|
|
|
|
+};
|
|
|
|
+// 是否展示办理对象 (结束节点不展示: next.stepType===2 表示为结束节点)
|
|
|
|
+const showHandlers = computed(() => {
|
|
|
|
+ const next = state.nextStepOptions.find((item: any) => item.key === state.ruleForm.nextStepCode);
|
|
|
|
+ if (!next) return true;
|
|
|
|
+ // 话务部到派单 派单组到一级部门 也不需要展示办理对象
|
|
|
|
+ return next?.stepType !== 2;
|
|
|
|
+});
|
|
|
|
+
|
|
|
|
+// 办理对象是否能够选择多个(可以发起会签可以选择多个,不能发起会签只能选择一个) 加个判断 选择的下一环节必须是部门(会签必须是选择的部门)
|
|
|
|
+const multipleLimit = computed(() => {
|
|
|
|
+ return canStartCountersign.value && selectNext.value.businessType === 2 ? 0 : 1;
|
|
|
|
+});
|
|
|
|
+// 选择常用意见 填入填写框 办理
|
|
|
|
+const chooseAdvice = (item: any) => {
|
|
|
|
+ state.ruleForm.opinion += item.content;
|
|
|
|
+};
|
|
|
|
+// 关闭弹窗
|
|
|
|
+const closeDialog = () => {
|
|
|
|
+ state.dialogVisible = false;
|
|
|
|
+};
|
|
|
|
+const close = () => {
|
|
|
|
+ ruleFormRef.value?.resetFields();
|
|
|
|
+ ruleFormRef.value?.resetFields();
|
|
|
|
+};
|
|
|
|
+// 提交
|
|
|
|
+const afterSubmit = (emitType?: 'updateList', showMessage?: boolean, message?: string) => {
|
|
|
|
+ state.loading = false;
|
|
|
|
+ closeDialog();
|
|
|
|
+ const msg = message ?? '甄别审批成功';
|
|
|
|
+ if (showMessage) ElMessage.success(msg);
|
|
|
|
+ if (emitType) emit(emitType);
|
|
|
|
+};
|
|
|
|
+const ruleFormRef = ref<RefType>();
|
|
|
|
+const handleFiles = ref<EmptyArrayType>([]); // 附件
|
|
|
|
+const onSubmit = (formEl: FormInstance | undefined) => {
|
|
|
|
+ if (!formEl) return;
|
|
|
|
+ formEl.validate((valid: boolean) => {
|
|
|
|
+ if (!valid) return;
|
|
|
|
+ state.loading = true;
|
|
|
|
+ let submitObj = other.deepClone(state.ruleForm);
|
|
|
|
+ if (state.ruleForm.isPass) {
|
|
|
|
+ // 审批通过 下一步
|
|
|
|
+ workflowNext({ ...submitObj, reviewResult: 1, files: handleFiles.value })
|
|
|
|
+ .then(() => {
|
|
|
|
+ afterSubmit('updateList', true);
|
|
|
|
+ })
|
|
|
|
+ .catch(() => {
|
|
|
|
+ afterSubmit('updateList');
|
|
|
|
+ });
|
|
|
|
+ } else {
|
|
|
|
+ // 审批拒绝
|
|
|
|
+ const requestDiscernAudit = {
|
|
|
|
+ opinion: state.ruleForm.opinion,
|
|
|
|
+ workflowId: state.workflowId,
|
|
|
|
+ files: handleFiles.value,
|
|
|
|
+ stepId: state.ruleForm.stepId,
|
|
|
|
+ };
|
|
|
|
+ workflowReject(requestDiscernAudit)
|
|
|
|
+ .then(() => {
|
|
|
|
+ afterSubmit('updateList', true);
|
|
|
|
+ })
|
|
|
|
+ .catch(() => {
|
|
|
|
+ afterSubmit('updateList');
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+};
|
|
|
|
+defineExpose({
|
|
|
|
+ openDialog,
|
|
|
|
+ closeDialog,
|
|
|
|
+});
|
|
|
|
+</script>
|