Discern-edit.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. <template>
  2. <el-dialog
  3. v-model="state.dialogVisible"
  4. draggable
  5. :title="state.dialogTitle"
  6. ref="dialogRef"
  7. @mouseup="mouseup"
  8. :style="'transform: ' + state.transform + ';'"
  9. append-to-body
  10. destroy-on-close
  11. :close-on-click-modal="false"
  12. @close="close"
  13. >
  14. <div v-loading="state.loading">
  15. <el-form :model="state.discernForm" label-width="110px" ref="discernFormRef">
  16. <el-row :gutter="10">
  17. <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
  18. <el-form-item label="工单编码"> {{ state.orderDetail.no }} </el-form-item>
  19. </el-col>
  20. <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
  21. <el-form-item label="工单标题"> {{ state.orderDetail.order?.title }} </el-form-item>
  22. </el-col>
  23. <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
  24. <el-form-item label="申请人"> {{ state.orderDetail.creatorName }} </el-form-item>
  25. </el-col>
  26. <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
  27. <el-form-item label="申请部门"> {{ state.orderDetail.creatorOrgName }} </el-form-item>
  28. </el-col>
  29. <el-col>
  30. <el-form-item label="申请时间"> {{ formatDate(state.orderDetail.creationTime, 'YYYY-mm-dd HH:MM:SS') }} </el-form-item>
  31. </el-col>
  32. <!-- 非退回流程都需要选择 -->
  33. <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
  34. <el-form-item label="下一环节" prop="nextStepCode" :rules="[{ required: true, message: '请选择下一环节', trigger: 'change' }]">
  35. <el-select v-model="state.discernForm.nextStepCode" placeholder="请选择下一环节" class="w100" @change="selectNextStep">
  36. <el-option v-for="item in state.nextStepOptions" :key="item.key" :label="item.value" :value="item.key" />
  37. </el-select>
  38. </el-form-item>
  39. </el-col>
  40. <!-- 非退回流程都需要选择并且如果选择了结束节点就不需要选择办理对象 -->
  41. <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" v-if="showHandlers">
  42. <el-form-item label="办理对象" prop="nextHandlers" :rules="[{ required: nextHandlersRequired, message: '请选择办理对象', trigger: 'change' }]">
  43. <el-select-v2
  44. v-model="state.discernForm.nextHandlers"
  45. :options="state.handlerOptions"
  46. placeholder="请选择办理对象"
  47. class="w100"
  48. multiple
  49. clearable
  50. collapse-tags
  51. collapse-tags-tooltip
  52. filterable
  53. value-key="key"
  54. @change="selectHandlers"
  55. :multiple-limit="multipleLimit"
  56. />
  57. </el-form-item>
  58. </el-col>
  59. <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
  60. <el-form-item label="申请类型" prop="type" :rules="[{ required: true, message: '请选择申请类型', trigger: 'change' }]">
  61. <el-select v-model="state.discernForm.type" placeholder="请选择申请类型" class="w100" value-key="dicDataValue">
  62. <el-option v-for="item in screenTypeOptions" :value="item" :key="item.dicDataValue" :label="item.dicDataName" />
  63. </el-select>
  64. </el-form-item>
  65. </el-col>
  66. <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
  67. <el-form-item label="" prop="isSms">
  68. <el-checkbox v-model="state.discernForm.isSms" label="短信通知" />
  69. </el-form-item>
  70. </el-col>
  71. <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
  72. <el-form-item label="申请理由" prop="content" :rules="[{ required: true, message: '请填写甄别申请理由', trigger: 'blur' }]">
  73. <common-advice
  74. @chooseAdvice="chooseAdviceDiscern"
  75. v-model="state.discernForm.content"
  76. placeholder="请填写甄别申请理由"
  77. :loading="state.loading"
  78. :commonEnum="commonEnum.Discriminate"
  79. :maxlength="AppConfigInfo.handleOpinionWordLimit"
  80. />
  81. </el-form-item>
  82. </el-col>
  83. <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
  84. <el-form-item label="附件">
  85. <annex-list
  86. name="甄别附件"
  87. ref="discernAnnexListRef"
  88. v-model:format="handleFilesDiscern"
  89. :businessId="state.orderDetail.orderId"
  90. v-model="state.discernForm.files"
  91. classify="甄别上传"
  92. />
  93. </el-form-item>
  94. </el-col>
  95. </el-row>
  96. </el-form>
  97. </div>
  98. <template #footer>
  99. <span class="dialog-footer">
  100. <el-button @click="closeDialog" class="default-button">取 消</el-button>
  101. <el-button type="primary" @click="onSubmit(discernFormRef)" :loading="state.loading">确定</el-button>
  102. </span>
  103. </template>
  104. </el-dialog>
  105. </template>
  106. <script setup lang="ts">
  107. import { computed, defineAsyncComponent, nextTick, reactive, ref, watch } from 'vue';
  108. import { ElMessage, FormInstance } from 'element-plus';
  109. import other from '@/utils/other';
  110. import { commonEnum } from '@/utils/constants';
  111. import { discernApproveParams, discernUpdate, screenBaseData, screenDetail, workflowDiscernParams } from '@/api/business/discern';
  112. import { transformFile } from '@/utils/tools';
  113. import { formatDate } from '@/utils/formatTime';
  114. import { useAppConfig } from '@/stores/appConfig';
  115. import { storeToRefs } from 'pinia';
  116. // 引入组件
  117. const CommonAdvice = defineAsyncComponent(() => import('@/components/CommonAdvice/index.vue')); // 常用意见
  118. const AnnexList = defineAsyncComponent(() => import('@/components/AnnexList/index.vue')); // 附件列表
  119. // 定义子组件向父组件传值/事件
  120. const emit = defineEmits(['orderProcessSuccess', 'orderProcessFailed']);
  121. // 定义变量内容
  122. const state = reactive<any>({
  123. dialogVisible: false, // 弹窗显示隐藏
  124. discernForm: {
  125. //流程表单
  126. content: '', // 甄别理由
  127. nextStepCode: '', // 下一节点
  128. nextStepName: '', // 下一节点名称
  129. backToCountersignEnd: false, // 是否回到会签结束节点
  130. nextHandlers: [], // 下一节点办理对象
  131. nextMainHandler: '', // 主办人
  132. isSms: false, // 是否短信通知
  133. isStartCountersign: false, // 是否发起会签
  134. stepId: '',
  135. },
  136. nextStepOptions: [], // 下一节点
  137. handlerOptions: [], // 办理对象
  138. transform: 'translate(0px, 0px)', // 滚动条位置
  139. loading: false, // 提交按钮loading
  140. workflowId: '', // 流程id
  141. handlerClassifies: [], //撤回办理对象
  142. handleId: '', // 流程处理ID
  143. dialogTitle: '甄别编辑', // 弹窗标题
  144. annexName: '', // 附件标题
  145. inputPlaceholder: '', // 意见提示
  146. orderDetail: {}, // 工单详情
  147. });
  148. const screenTypeOptions = ref<EmptyArrayType>([]); // 甄别类型
  149. const appConfigStore = useAppConfig();
  150. const { AppConfigInfo } = storeToRefs(appConfigStore); // 系统配置信息
  151. // 打开弹窗
  152. const openDialog = async (val: any) => {
  153. state.dialogVisible = true;
  154. state.loading = true;
  155. try {
  156. if (val.workflowId) {
  157. state.workflowId = val.workflowId;
  158. // 如果有流程ID说明已经开启过流程需要获取办理流程的参数
  159. const [workflowDiscernResponse, responseDiscern] = await Promise.all([discernApproveParams(val.workflowId), screenBaseData()]); //甄别审批参数
  160. screenTypeOptions.value = responseDiscern.result?.screenType ?? []; // 甄别理由
  161. handleResult(workflowDiscernResponse);
  162. } else {
  163. const [workflowDiscernResponse, responseDiscern] = await Promise.all([workflowDiscernParams(), screenBaseData()]); //获取开启流程参数
  164. screenTypeOptions.value = responseDiscern.result?.screenType ?? []; // 甄别理由
  165. handleResult(workflowDiscernResponse);
  166. }
  167. const { result } = await screenDetail(val.id);
  168. state.discernForm.content = result.content ?? {};
  169. state.discernForm.type = {
  170. dicDataValue: result.typeDicId,
  171. dicDataName: result.typeDicName,
  172. };
  173. state.discernForm.screenType = val.screenType ?? null;
  174. state.orderDetail = result;
  175. state.discernForm.files = transformFile(result.files);
  176. state.loading = false;
  177. } finally {
  178. state.loading = false;
  179. }
  180. };
  181. const canStartCountersign = ref<boolean>(false); // 是否可以发起会签
  182. const isMainHandlerShow = ref<boolean>(false); // 是否展示主办人
  183. const handleResult = (res: any) => {
  184. state.nextStepOptions = res.result.steps; //办理对象选择内容
  185. canStartCountersign.value = res.result.canStartCountersign ?? false; // 是否可以发起会签
  186. isMainHandlerShow.value = res.result.isMainHandlerShow ?? false; // 是否展示主办人
  187. if (state.nextStepOptions.length === 1) {
  188. // 下一节点是否只有一个 默认选中第一个
  189. setTimeout(() => {
  190. state.discernForm.nextStepCode = state.nextStepOptions[0].key; // 下一节点code
  191. state.discernForm.nextStepName = state.nextStepOptions[0].value; // 下一节点name
  192. state.discernForm.backToCountersignEnd = state.nextStepOptions[0].backToCountersignEnd ?? false; // 是否回到会签结束节点
  193. }, 0);
  194. selectNextStep(state.nextStepOptions[0].key); // 查询流程下一节点参数
  195. } else {
  196. state.discernForm.nextStepCode = '';
  197. state.discernForm.nextStepName = '';
  198. }
  199. state.discernForm.stepId = res.result.stepId;
  200. state.loading = false;
  201. };
  202. const discernFormRef = ref<RefType>(); //甄别申请表单组件
  203. // 流程选择下一环节
  204. const nextHandlersRequired = ref<Boolean>(false);
  205. const selectNextStep = (val: any) => {
  206. discernFormRef.value?.resetFields('nextHandlers');
  207. const next = state.nextStepOptions.find((item: any) => item.key === val);
  208. const items = next.items; //获取下一节点
  209. state.discernForm.nextStepName = next.value; // 下一节点name
  210. state.discernForm.backToCountersignEnd = next.backToCountersignEnd ?? false; // 是否回到会签结束节点
  211. state.discernForm.handlerType = next.handlerType;
  212. state.discernForm.businessType = next.businessType;
  213. state.discernForm.flowDirection = next.flowDirection;
  214. state.handlerOptions = items ?? [];
  215. state.handlerOptions = state.handlerOptions.map((item: any) => {
  216. return {
  217. value: {
  218. ...item,
  219. },
  220. label: item.value,
  221. };
  222. });
  223. // 办理对象是否必填
  224. // nextHandlersRequired.value = ![0].includes(next.handlerType) && !showStepsArr.includes(state.processType);
  225. nextHandlersRequired.value = ![0].includes(next.handlerType);
  226. if (items.length === 1) {
  227. // 如果办理对象只有一个默认选中
  228. state.discernForm.nextHandlers = [items[0]];
  229. }
  230. };
  231. // 办理对象是否能够选择多个(可以发起会签可以选择多个,不能发起会签只能选择一个)
  232. const multipleLimit = computed(() => {
  233. return canStartCountersign.value ? 0 : 1;
  234. });
  235. // 是否展示办理对象 (只有结束节点不展示 next.stepType===2 表示为结束节点)
  236. const showHandlers = computed(() => {
  237. const next = state.nextStepOptions.find((item: any) => item.key === state.discernForm.nextStepCode);
  238. if (!next) return true;
  239. return next.stepType !== 2;
  240. });
  241. // 选择办理对象
  242. const selectHandlers = () => {
  243. if (state.discernForm.nextHandlers.length <= 1) {
  244. // 如果只有一个办理对象就不需要发起会签
  245. state.discernForm.isStartCountersign = false;
  246. }
  247. };
  248. // 设置抽屉
  249. const dialogRef = ref<RefType>();
  250. const mouseup = () => {
  251. state.transform = dialogRef.value.dialogContentRef.$el.style.transform;
  252. };
  253. // 关闭弹窗
  254. const closeDialog = () => {
  255. state.dialogVisible = false;
  256. };
  257. // 重置表单方法
  258. const restForm = (formEl: FormInstance | undefined) => {
  259. if (!formEl) return;
  260. formEl.resetFields();
  261. formEl.clearValidate();
  262. state.discernForm.files = [];
  263. };
  264. // 选择常用意见 填入填写框 甄别
  265. const chooseAdviceDiscern = (item: any) => {
  266. state.discernForm.content += item.content;
  267. };
  268. const afterSubmit = (emitType?: 'orderProcessSuccess' | 'orderProcessFailed', showMessage?: boolean, message?: string) => {
  269. state.loading = false;
  270. closeDialog();
  271. const msg = message ?? '操作成功';
  272. if (showMessage) ElMessage.success(msg);
  273. if (emitType) emit(emitType);
  274. };
  275. const close = () => {
  276. restForm(discernFormRef.value);
  277. };
  278. // 提交
  279. const handleFilesDiscern = ref<EmptyArrayType>([]); // 甄别附件
  280. const onSubmit = (formEl: FormInstance | undefined) => {
  281. if (!formEl) return;
  282. formEl.validate((valid: boolean) => {
  283. if (!valid) return;
  284. state.loading = true;
  285. const requestDiscern = {
  286. data: {
  287. id: state.orderDetail.id,
  288. no: state.orderDetail.no,
  289. visitId: state.orderDetail.visitId,
  290. visitDetailId: state.orderDetail.visitDetailId,
  291. orderId: state.orderDetail.orderId,
  292. typeDicId: state.discernForm.type.dicDataValue,
  293. typeDicName: state.discernForm.type.dicDataName,
  294. content: state.discernForm.content,
  295. screenType: state.discernForm.screenType,
  296. files: handleFilesDiscern.value,
  297. workflowId: state.workflowId,
  298. },
  299. nextWorkflow: {
  300. nextStepCode: state.discernForm.nextStepCode,
  301. nextStepName: state.discernForm.nextStepName,
  302. backToCountersignEnd: state.discernForm.backToCountersignEnd,
  303. nextHandlers: state.discernForm.nextHandlers,
  304. flowDirection: state.discernForm.flowDirection,
  305. handlerType: state.discernForm.handlerType,
  306. stepType: state.discernForm.stepType,
  307. businessType: state.discernForm.businessType,
  308. isSms: state.discernForm.isSms,
  309. opinion: state.discernForm.content,
  310. files: handleFilesDiscern.value,
  311. stepId: state.discernForm.stepId,
  312. screenType: state.discernForm.screenType,
  313. workflowId: state.workflowId,
  314. },
  315. };
  316. discernUpdate(requestDiscern)
  317. .then(() => {
  318. afterSubmit('orderProcessSuccess', true, '甄别申请成功');
  319. })
  320. .catch(() => {
  321. afterSubmit('orderProcessFailed');
  322. });
  323. });
  324. };
  325. // 暴露变量
  326. defineExpose({
  327. openDialog,
  328. closeDialog,
  329. });
  330. </script>