Terminate-apply.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. <template>
  2. <el-dialog
  3. v-model="state.dialogVisible"
  4. draggable
  5. title="终止申请"
  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.terminateForm" label-width="110px" ref="terminateFormRef">
  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.title }} </el-form-item>
  22. </el-col>
  23. <!-- 非退回流程都需要选择 -->
  24. <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
  25. <el-form-item label="下一环节" prop="nextStepCode" :rules="[{ required: true, message: '请选择下一环节', trigger: 'change' }]">
  26. <el-select v-model="state.terminateForm.nextStepCode" placeholder="请选择下一环节" class="w100" @change="selectNextStep">
  27. <el-option v-for="item in state.nextStepOptions" :key="item.key" :label="item.value" :value="item.key" />
  28. </el-select>
  29. </el-form-item>
  30. </el-col>
  31. <!-- 非退回流程都需要选择并且如果选择了结束节点就不需要选择办理对象 -->
  32. <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" v-if="showHandlers">
  33. <el-form-item label="办理对象" prop="nextHandlers" :rules="[{ required: false, message: '请选择办理对象', trigger: 'change' }]">
  34. <el-select-v2
  35. v-model="state.terminateForm.nextHandlers"
  36. :options="state.handlerOptions"
  37. placeholder="请选择办理对象"
  38. class="w100"
  39. multiple
  40. clearable
  41. collapse-tags
  42. collapse-tags-tooltip
  43. filterable
  44. value-key="key"
  45. @change="selectHandlers"
  46. :multiple-limit="multipleLimit"
  47. />
  48. </el-form-item>
  49. </el-col>
  50. <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12">
  51. <el-form-item label="" prop="isSms">
  52. <el-checkbox v-model="state.terminateForm.isSms" label="短信通知" />
  53. </el-form-item>
  54. </el-col>
  55. <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
  56. <el-form-item label="终止理由" prop="content" :rules="[{ required: true, message: '请填写终止申请理由', trigger: 'blur' }]">
  57. <common-advice
  58. @chooseAdvice="chooseAdviceTerminate"
  59. v-model="state.terminateForm.content"
  60. placeholder="请填写终止申请理由"
  61. :loading="state.loading"
  62. :commonEnum="commonEnum.OrderCirculation"
  63. />
  64. </el-form-item>
  65. </el-col>
  66. <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
  67. <el-form-item label="附件">
  68. <annex-list
  69. name="终止附件"
  70. ref="terminateAnnexListRef"
  71. v-model:format="handleFilesTerminate"
  72. :businessId="state.orderDetail.id"
  73. classify="终止上传"
  74. />
  75. </el-form-item>
  76. </el-col>
  77. </el-row>
  78. </el-form>
  79. </div>
  80. <template #footer>
  81. <span class="dialog-footer">
  82. <el-button @click="closeDialog" class="default-button">取 消</el-button>
  83. <el-button type="primary" @click="onSubmit(terminateFormRef)" :loading="state.loading">确定</el-button>
  84. </span>
  85. </template>
  86. </el-dialog>
  87. </template>
  88. <script setup lang="ts">
  89. import { computed, defineAsyncComponent, reactive, ref, watch } from 'vue';
  90. import { ElMessage, FormInstance } from 'element-plus';
  91. import other from '@/utils/other';
  92. import { commonEnum } from '@/utils/constants';
  93. import { terminateStartFlow, terminateStartFlowParams } from '@/api/business/terminate';
  94. // 引入组件
  95. const CommonAdvice = defineAsyncComponent(() => import('@/components/CommonAdvice/index.vue')); // 常用意见
  96. const AnnexList = defineAsyncComponent(() => import('@/components/AnnexList/index.vue')); // 附件列表
  97. // 定义子组件向父组件传值/事件
  98. const emit = defineEmits(['orderProcessSuccess', 'orderProcessFailed']);
  99. // 定义变量内容
  100. const state = reactive<any>({
  101. dialogVisible: false, // 弹窗显示隐藏
  102. terminateForm: {
  103. //流程表单
  104. content: '', // 意见
  105. nextStepCode: '', // 下一节点
  106. nextStepName: '', // 下一节点名称
  107. backToCountersignEnd: false, // 是否回到会签结束节点
  108. nextHandlers: [], // 下一节点办理对象
  109. isSms: false, // 是否短信通知
  110. isStartCountersign: false, // 是否发起会签
  111. },
  112. nextStepOptions: [], // 下一节点
  113. handlerOptions: [], // 办理对象
  114. transform: 'translate(0px, 0px)', // 滚动条位置
  115. loading: false, // 提交按钮loading
  116. orderDetail: {}, // 工单详情
  117. });
  118. // 打开弹窗
  119. const openDialog = async (val: any) => {
  120. console.log(val);
  121. state.dialogVisible = true;
  122. state.loading = true;
  123. try {
  124. const [workflowTerminateResponse] = await Promise.all([terminateStartFlowParams()]); //获取开启流程参数
  125. handleResult(workflowTerminateResponse);
  126. const { orderDetail } = val;
  127. state.orderDetail = orderDetail ?? {}; // 工单详情
  128. state.loading = false;
  129. } catch (e) {
  130. console.log(e, '111');
  131. state.loading = false;
  132. state.dialogVisible = false;
  133. }
  134. };
  135. const canStartCountersign = ref<boolean>(false); // 是否可以发起会签
  136. const isMainHandlerShow = ref<boolean>(false); // 是否展示主办人
  137. const handleResult = (res: any) => {
  138. state.nextStepOptions = res.result.steps; //办理对象选择内容
  139. canStartCountersign.value = res.result.canStartCountersign ?? false; // 是否可以发起会签
  140. isMainHandlerShow.value = res.result.isMainHandlerShow ?? false; // 是否展示主办人
  141. if (state.nextStepOptions.length === 1) {
  142. // 下一节点是否只有一个 默认选中第一个
  143. setTimeout(() => {
  144. state.terminateForm.nextStepCode = state.nextStepOptions[0].key; // 下一节点code
  145. state.terminateForm.nextStepName = state.nextStepOptions[0].value; // 下一节点name
  146. state.terminateForm.backToCountersignEnd = state.nextStepOptions[0].backToCountersignEnd ?? false; // 是否回到会签结束节点
  147. }, 0);
  148. selectNextStep(state.nextStepOptions[0].key); // 查询流程下一节点参数
  149. } else {
  150. state.terminateForm.nextStepCode = '';
  151. state.terminateForm.nextStepName = '';
  152. }
  153. state.terminateForm.stepId = res.result.stepId;
  154. state.loading = false;
  155. };
  156. // 流程选择下一环节
  157. const selectNextStep = (val: any) => {
  158. terminateFormRef.value?.resetFields('nextHandlers');
  159. const next = state.nextStepOptions.find((item: any) => item.key === val);
  160. const items = next.items; //获取下一节点
  161. state.terminateForm.nextStepName = next.value; // 下一节点name
  162. state.terminateForm.backToCountersignEnd = next.backToCountersignEnd ?? false; // 是否回到会签结束节点
  163. state.terminateForm.handlerType = next.handlerType;
  164. state.terminateForm.businessType = next.businessType;
  165. state.terminateForm.flowDirection = next.flowDirection;
  166. state.handlerOptions = items ?? [];
  167. state.handlerOptions = state.handlerOptions.map((item: any) => {
  168. return {
  169. value: {
  170. ...item,
  171. },
  172. label: item.value,
  173. };
  174. });
  175. if (items.length === 1) {
  176. // 如果办理对象只有一个默认选中
  177. state.terminateForm.nextHandlers = [items[0]];
  178. }
  179. };
  180. // 办理对象是否能够选择多个(可以发起会签可以选择多个,不能发起会签只能选择一个)
  181. const multipleLimit = computed(() => {
  182. return canStartCountersign.value ? 0 : 1;
  183. });
  184. watch(
  185. () => state.terminateForm.nextHandlers, // 监听办理对象 多个办理对象自动发起会签
  186. (val) => {
  187. state.terminateForm.isStartCountersign = val.length > 1;
  188. }
  189. );
  190. // 是否展示办理对象 (只有结束节点不展示 next.stepType===2 表示为结束节点)
  191. const showHandlers = computed(() => {
  192. const next = state.nextStepOptions.find((item: any) => item.key === state.terminateForm.nextStepCode);
  193. if (!next) return true;
  194. return next.stepType !== 2;
  195. });
  196. // 选择办理对象
  197. const selectHandlers = () => {
  198. if (state.terminateForm.nextHandlers.length <= 1) {
  199. // 如果只有一个办理对象就不需要发起会签
  200. state.terminateForm.isStartCountersign = false;
  201. }
  202. };
  203. // 设置抽屉
  204. const dialogRef = ref<RefType>();
  205. const mouseup = () => {
  206. state.transform = dialogRef.value.dialogContentRef.$el.style.transform;
  207. };
  208. // 关闭弹窗
  209. const closeDialog = () => {
  210. state.dialogVisible = false;
  211. };
  212. // 重置表单方法
  213. const restForm = (formEl: FormInstance | undefined) => {
  214. if (!formEl) return;
  215. formEl.resetFields();
  216. formEl.clearValidate();
  217. state.terminateForm.files = [];
  218. };
  219. // 选择常用意见 填入填写框 终止
  220. const chooseAdviceTerminate = (item: any) => {
  221. state.terminateForm.content += item.content;
  222. };
  223. const afterSubmit = (emitType?: 'orderProcessSuccess' | 'orderProcessFailed', showMessage?: boolean, message?: string) => {
  224. state.loading = false;
  225. closeDialog();
  226. const msg = message ?? '操作成功';
  227. if (showMessage) ElMessage.success(msg);
  228. if (emitType) emit(emitType);
  229. };
  230. const terminateFormRef = ref<RefType>();
  231. const close = () => {
  232. restForm(terminateFormRef.value);
  233. };
  234. // 提交
  235. const handleFilesTerminate = ref<EmptyArrayType>([]); // 终止附件
  236. const onSubmit = (formEl: FormInstance | undefined) => {
  237. if (!formEl) return;
  238. formEl.validate((valid: boolean) => {
  239. if (!valid) return;
  240. state.loading = true;
  241. let submitObj = other.deepClone(state.terminateForm);
  242. if (submitObj.nextHandlers && submitObj.nextHandlers.length) {
  243. if (submitObj.nextHandlers.length === 1) {
  244. submitObj.nextMainHandler = submitObj.nextHandlers[0].key;
  245. }
  246. }
  247. const requestTerminate = {
  248. data: {
  249. no: state.orderDetail.no,
  250. orderId: state.orderDetail.id,
  251. content: state.terminateForm.content,
  252. files: handleFilesTerminate.value,
  253. },
  254. workflow: {
  255. nextStepCode: state.terminateForm.nextStepCode,
  256. nextStepName: state.terminateForm.nextStepName,
  257. backToCountersignEnd: state.terminateForm.backToCountersignEnd,
  258. nextHandlers: state.terminateForm.nextHandlers,
  259. flowDirection: state.terminateForm.flowDirection,
  260. handlerType: state.terminateForm.handlerType,
  261. stepType: state.terminateForm.stepType,
  262. businessType: state.terminateForm.businessType,
  263. isSms: state.terminateForm.isSms,
  264. files: handleFilesTerminate.value,
  265. opinion: state.terminateForm.content,
  266. },
  267. };
  268. terminateStartFlow(requestTerminate)
  269. .then(() => {
  270. afterSubmit('orderProcessSuccess', true, '终止申请成功');
  271. })
  272. .catch(() => {
  273. afterSubmit('orderProcessFailed');
  274. });
  275. });
  276. };
  277. // 暴露变量
  278. defineExpose({
  279. openDialog,
  280. closeDialog,
  281. });
  282. </script>