create.vue 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. <template>
  2. <ContentWrap :bodyStyle="{ padding: '10px 20px 0' }">
  3. <el-row :gutter="20">
  4. <el-col :span="16"
  5. ><el-form
  6. ref="formRef"
  7. v-loading="formLoading"
  8. :model="formData"
  9. :rules="formRules"
  10. label-width="80px"
  11. >
  12. <el-form-item label="请假类型" prop="type">
  13. <el-select v-model="formData.type" clearable placeholder="请选择请假类型">
  14. <el-option
  15. v-for="dict in getIntDictOptions(DICT_TYPE.BPM_OA_LEAVE_TYPE)"
  16. :key="dict.value"
  17. :label="dict.label"
  18. :value="dict.value"
  19. />
  20. </el-select>
  21. </el-form-item>
  22. <el-form-item label="开始时间" prop="startTime">
  23. <el-date-picker
  24. v-model="formData.startTime"
  25. clearable
  26. placeholder="请选择开始时间"
  27. type="datetime"
  28. value-format="x"
  29. />
  30. </el-form-item>
  31. <el-form-item label="结束时间" prop="endTime">
  32. <el-date-picker
  33. v-model="formData.endTime"
  34. clearable
  35. placeholder="请选择结束时间"
  36. type="datetime"
  37. value-format="x"
  38. />
  39. </el-form-item>
  40. <el-form-item label="原因" prop="reason">
  41. <el-input v-model="formData.reason" placeholder="请输入请假原因" type="textarea" />
  42. </el-form-item>
  43. <el-form-item>
  44. <el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
  45. </el-form-item>
  46. </el-form></el-col
  47. >
  48. <!-- 新增 ====== begin ======== -->
  49. <el-col :span="8"
  50. ><!-- 流程时间线 -->
  51. <ProcessInstanceTimeline
  52. ref="timelineRef"
  53. :activity-nodes="activityNodes"
  54. :show-status-icon="false"
  55. @select-user-confirm="selectUserConfirm"
  56. />
  57. </el-col>
  58. <!-- 新增 ====== end ======== -->
  59. </el-row>
  60. </ContentWrap>
  61. </template>
  62. <script lang="ts" setup>
  63. import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
  64. import * as LeaveApi from '@/api/bpm/leave'
  65. import { useTagsViewStore } from '@/store/modules/tagsView'
  66. import * as DefinitionApi from '@/api/bpm/definition'
  67. // 新增 ====== begin ========
  68. import ProcessInstanceTimeline from '../../processInstance/detail/ProcessInstanceTimeline.vue'
  69. import * as ProcessInstanceApi from '@/api/bpm/processInstance'
  70. import { CandidateStrategy, NodeId } from '@/components/SimpleProcessDesignerV2/src/consts'
  71. import { ApprovalNodeInfo } from '@/api/bpm/processInstance'
  72. // 新增 ====== end ========
  73. defineOptions({ name: 'BpmOALeaveCreate' })
  74. const message = useMessage() // 消息弹窗
  75. const { delView } = useTagsViewStore() // 视图操作
  76. const { push, currentRoute } = useRouter() // 路由
  77. const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
  78. const formData = ref({
  79. type: undefined,
  80. reason: undefined,
  81. startTime: undefined,
  82. endTime: undefined
  83. })
  84. const formRules = reactive({
  85. type: [{ required: true, message: '请假类型不能为空', trigger: 'blur' }],
  86. reason: [{ required: true, message: '请假原因不能为空', trigger: 'change' }],
  87. startTime: [{ required: true, message: '请假开始时间不能为空', trigger: 'change' }],
  88. endTime: [{ required: true, message: '请假结束时间不能为空', trigger: 'change' }]
  89. })
  90. const formRef = ref() // 表单 Ref
  91. // 指定审批人
  92. const processDefineKey = 'oa_leave' // 流程定义 Key
  93. const startUserSelectTasks = ref([]) // 发起人需要选择审批人的用户任务列表
  94. const startUserSelectAssignees = ref({}) // 发起人选择审批人的数据
  95. // 新增 ====== begin ========
  96. const tempStartUserSelectAssignees = ref({}) // 历史发起人选择审批人的数据,用于每次表单变更时,临时保存
  97. const activityNodes = ref<ProcessInstanceApi.ApprovalNodeInfo[]>([]) // 审批节点信息
  98. const processDefinitionId = ref('')
  99. // 新增 ====== end ========
  100. /** 提交表单 */
  101. const submitForm = async () => {
  102. // 校验表单
  103. if (!formRef) return
  104. const valid = await formRef.value.validate()
  105. if (!valid) return
  106. // 校验指定审批人
  107. if (startUserSelectTasks.value?.length > 0) {
  108. for (const userTask of startUserSelectTasks.value) {
  109. if (
  110. Array.isArray(startUserSelectAssignees.value[userTask.id]) &&
  111. startUserSelectAssignees.value[userTask.id].length === 0
  112. ) {
  113. return message.warning(`请选择${userTask.name}的审批人`)
  114. }
  115. }
  116. }
  117. // 提交请求
  118. formLoading.value = true
  119. try {
  120. const data = { ...formData.value } as unknown as LeaveApi.LeaveVO
  121. // 设置指定审批人
  122. if (startUserSelectTasks.value?.length > 0) {
  123. data.startUserSelectAssignees = startUserSelectAssignees.value
  124. }
  125. await LeaveApi.createLeave(data)
  126. message.success('发起成功')
  127. // 关闭当前 Tab
  128. delView(unref(currentRoute))
  129. await push({ name: 'BpmOALeave' })
  130. } finally {
  131. formLoading.value = false
  132. }
  133. }
  134. // 新增 ====== begin ========
  135. /** 获取审批详情 */
  136. const getApprovalDetail = async (row: any) => {
  137. try {
  138. const data = await ProcessInstanceApi.getApprovalDetail({
  139. processDefinitionId: row.id,
  140. activityId: NodeId.START_USER_NODE_ID,
  141. processVariablesStr: row.processVariablesStr // 解决 GET 无法传递对象的问题,后端 String 再转 JSON
  142. })
  143. if (!data) {
  144. message.error('查询不到审批详情信息!')
  145. return
  146. }
  147. // 获取审批节点,显示 Timeline 的数据
  148. activityNodes.value = data.activityNodes
  149. // 获取发起人自选的任务
  150. startUserSelectTasks.value = data.activityNodes?.filter(
  151. (node: ApprovalNodeInfo) => CandidateStrategy.START_USER_SELECT === node.candidateStrategy
  152. )
  153. // 恢复之前的选择审批人
  154. if (startUserSelectTasks.value?.length > 0) {
  155. for (const node of startUserSelectTasks.value) {
  156. if (
  157. tempStartUserSelectAssignees.value[node.id] &&
  158. tempStartUserSelectAssignees.value[node.id].length > 0
  159. ) {
  160. startUserSelectAssignees.value[node.id] = tempStartUserSelectAssignees.value[node.id]
  161. } else {
  162. startUserSelectAssignees.value[node.id] = []
  163. }
  164. }
  165. }
  166. } finally {
  167. }
  168. }
  169. /** 选择发起人 */
  170. const selectUserConfirm = (id: string, userList: any[]) => {
  171. startUserSelectAssignees.value[id] = userList?.map((item: any) => item.id)
  172. }
  173. // 新增 ====== end ========
  174. /** 初始化 */
  175. onMounted(async () => {
  176. const processDefinitionDetail = await DefinitionApi.getProcessDefinition(
  177. undefined,
  178. processDefineKey
  179. )
  180. if (!processDefinitionDetail) {
  181. message.error('OA 请假的流程模型未配置,请检查!')
  182. return
  183. }
  184. processDefinitionId.value = processDefinitionDetail.id
  185. startUserSelectTasks.value = processDefinitionDetail.startUserSelectTasks
  186. // 新增 ====== begin ========
  187. // 查询流程预测节点
  188. getApprovalDetail({
  189. id: processDefinitionId.value,
  190. processVariablesStr: JSON.stringify(formData.value)
  191. })
  192. // 新增 ====== end ========
  193. })
  194. // 新增 ====== begin ========
  195. /** 预测流程节点会因为输入的参数值而产生新的预测结果值,所以需重新预测一次 */
  196. watch(
  197. formData.value,
  198. (newValue, oldValue) => {
  199. if (!oldValue) {
  200. return
  201. }
  202. if (newValue && Object.keys(newValue).length > 0) {
  203. // 记录之前的节点审批人
  204. tempStartUserSelectAssignees.value = startUserSelectAssignees.value
  205. startUserSelectAssignees.value = {}
  206. // 加载最新的审批详情
  207. getApprovalDetail({
  208. id: processDefinitionId.value,
  209. processVariablesStr: JSON.stringify(newValue) // 解决 GET 无法传递对象的问题,后端 String 再转 JSON
  210. })
  211. }
  212. },
  213. {
  214. immediate: true
  215. }
  216. // 新增 ====== end ========
  217. )
  218. </script>