ProcessDefinitionDetail.vue 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. <template>
  2. <ContentWrap :bodyStyle="{ padding: '10px 20px 0' }">
  3. <div class="processInstance-wrap-main">
  4. <el-scrollbar>
  5. <div class="text-#878c93 h-15px">编号:{{ selectProcessDefinition.id }}</div>
  6. <el-divider class="!my-8px" />
  7. <div class="flex items-center justify-between gap-5 mb-10px h-40px">
  8. <div class="text-26px font-bold mb-5px">{{ selectProcessDefinition.name }}</div>
  9. <el-button style="float: right" type="primary" @click="handleCancel">
  10. <Icon icon="ep:delete" /> 选择其它流程
  11. </el-button>
  12. </div>
  13. <!-- 中间主要内容tab栏 -->
  14. <el-tabs v-model="activeTab">
  15. <!-- 表单信息 -->
  16. <el-tab-pane label="表单填写" name="form">
  17. <div class="form-scroll-area">
  18. <el-scrollbar>
  19. <el-row>
  20. <el-col :span="17">
  21. <form-create
  22. :rule="detailForm.rule"
  23. v-model:api="fApi"
  24. v-model="detailForm.value"
  25. :option="detailForm.option"
  26. @submit="submitForm"
  27. >
  28. <template #type-startUserSelect>
  29. <el-col :span="24">
  30. <el-card class="mb-10px">
  31. <template #header>指定审批人</template>
  32. <el-form
  33. :model="startUserSelectAssignees"
  34. :rules="startUserSelectAssigneesFormRules"
  35. ref="startUserSelectAssigneesFormRef"
  36. >
  37. <el-form-item
  38. v-for="userTask in startUserSelectTasks"
  39. :key="userTask.id"
  40. :label="`任务【${userTask.name}】`"
  41. :prop="userTask.id"
  42. >
  43. <el-select
  44. v-model="startUserSelectAssignees[userTask.id]"
  45. multiple
  46. placeholder="请选择审批人"
  47. >
  48. <el-option
  49. v-for="user in userList"
  50. :key="user.id"
  51. :label="user.nickname"
  52. :value="user.id"
  53. />
  54. </el-select>
  55. </el-form-item>
  56. </el-form>
  57. </el-card>
  58. </el-col>
  59. </template>
  60. </form-create>
  61. </el-col>
  62. <el-col :span="6" :offset="1">
  63. <!-- 流程时间线 -->
  64. <ProcessInstanceTimeline
  65. ref="timelineRef"
  66. :approve-nodes="approveNodes"
  67. :show-status-icon="false"
  68. candidateField="candidateUserList"
  69. />
  70. </el-col>
  71. </el-row>
  72. </el-scrollbar>
  73. </div>
  74. </el-tab-pane>
  75. <!-- 流程图 -->
  76. <el-tab-pane label="流程图" name="diagram">
  77. <div class="form-scroll-area">
  78. <!-- 流程图预览 -->
  79. <ProcessInstanceBpmnViewer :bpmn-xml="bpmnXML" />
  80. </div>
  81. </el-tab-pane>
  82. </el-tabs>
  83. <!-- 底部操作栏 -->
  84. <div class="b-t-solid border-t-1px border-[var(--el-border-color)]">
  85. <!-- 操作栏按钮 -->
  86. <div
  87. v-if="activeTab === 'form'"
  88. class="h-50px bottom-10 text-14px flex items-center color-#32373c dark:color-#fff font-bold btn-container"
  89. >
  90. <el-button plain type="success" @click="submitForm">
  91. <Icon icon="ep:select" />&nbsp; 发起
  92. </el-button>
  93. <el-button plain type="danger" @click="handleCancel">
  94. <Icon icon="ep:close" />&nbsp; 取消
  95. </el-button>
  96. </div>
  97. </div>
  98. </el-scrollbar>
  99. </div>
  100. </ContentWrap>
  101. </template>
  102. <script lang="ts" setup>
  103. import { setConfAndFields2 } from '@/utils/formCreate'
  104. import ProcessInstanceBpmnViewer from '../detail/ProcessInstanceBpmnViewer.vue'
  105. import ProcessInstanceTimeline from '../detail/ProcessInstanceTimeline.vue'
  106. import type { ApiAttrs } from '@form-create/element-ui/types/config'
  107. import { useTagsViewStore } from '@/store/modules/tagsView'
  108. import * as ProcessInstanceApi from '@/api/bpm/processInstance'
  109. import * as DefinitionApi from '@/api/bpm/definition'
  110. import * as UserApi from '@/api/system/user'
  111. defineOptions({ name: 'ProcessDefinitionDetail' })
  112. const props = defineProps<{
  113. selectProcessDefinition: any
  114. }>()
  115. const { push, currentRoute } = useRouter() // 路由
  116. const message = useMessage() // 消息弹窗
  117. const { delView } = useTagsViewStore() // 视图操作
  118. const detailForm: any = ref({
  119. rule: [],
  120. option: {},
  121. value: {}
  122. }) // 流程表单详情
  123. const fApi = ref<ApiAttrs>()
  124. // 指定审批人
  125. const startUserSelectAssigneesFormRef = ref() // 发起人选择审批人的表单 Ref
  126. const startUserSelectTasks: any = ref([]) // 发起人需要选择审批人的用户任务列表
  127. const startUserSelectAssignees = ref({}) // 发起人选择审批人的数据
  128. const startUserSelectAssigneesFormRules = ref({}) // 发起人选择审批人的表单 Rules
  129. const userList = ref<any[]>([]) // 用户列表
  130. const bpmnXML: any = ref(null) // BPMN 数据
  131. /** 当前的Tab */
  132. const activeTab = ref('form')
  133. const emit = defineEmits(['cancel'])
  134. // 审批节点信息
  135. const approveNodes = ref<ProcessInstanceApi.ApprovalNodeInfo[]>([])
  136. /** 设置表单信息、获取流程图数据 **/
  137. const initProcessInfo = async (row, formVariables?) => {
  138. // 重置指定审批人
  139. startUserSelectTasks.value = []
  140. startUserSelectAssignees.value = {}
  141. startUserSelectAssigneesFormRules.value = {}
  142. // 情况一:流程表单
  143. if (row.formType == 10) {
  144. // 设置表单
  145. setConfAndFields2(detailForm, row.formConf, row.formFields, formVariables)
  146. await nextTick()
  147. fApi.value?.btn.show(false) // 隐藏提交按钮
  148. // 获取流程审批信息
  149. getApprovalDetail(row)
  150. // 加载流程图
  151. const processDefinitionDetail = await DefinitionApi.getProcessDefinition(row.id)
  152. if (processDefinitionDetail) {
  153. bpmnXML.value = processDefinitionDetail.bpmnXml
  154. startUserSelectTasks.value = processDefinitionDetail.startUserSelectTasks
  155. // 设置指定审批人
  156. if (startUserSelectTasks.value?.length > 0) {
  157. detailForm.value.rule.push({
  158. type: 'startUserSelect',
  159. props: {
  160. title: '指定审批人'
  161. }
  162. })
  163. // 设置校验规则
  164. for (const userTask of startUserSelectTasks.value) {
  165. startUserSelectAssignees.value[userTask.id] = []
  166. startUserSelectAssigneesFormRules.value[userTask.id] = [
  167. { required: true, message: '请选择审批人', trigger: 'blur' }
  168. ]
  169. }
  170. // 加载用户列表
  171. userList.value = await UserApi.getSimpleUserList()
  172. }
  173. }
  174. // 情况二:业务表单
  175. } else if (row.formCustomCreatePath) {
  176. await push({
  177. path: row.formCustomCreatePath
  178. })
  179. // 这里暂时无需加载流程图,因为跳出到另外个 Tab;
  180. }
  181. }
  182. /** 获取审批详情 */
  183. const getApprovalDetail = async (row) => {
  184. try {
  185. const param = {
  186. processDefinitionId: row.id
  187. }
  188. const data = await ProcessInstanceApi.getApprovalDetail(param)
  189. if (!data) {
  190. message.error('查询不到审批详情信息!')
  191. return
  192. }
  193. // 获取审批节点,显示 Timeline 的数据
  194. approveNodes.value = data.approveNodes
  195. } finally {
  196. }
  197. }
  198. /** 提交按钮 */
  199. const submitForm = async (formData) => {
  200. if (!fApi.value || props.selectProcessDefinition) {
  201. return
  202. }
  203. // 如果有指定审批人,需要校验
  204. if (startUserSelectTasks.value?.length > 0) {
  205. await startUserSelectAssigneesFormRef.value.validate()
  206. }
  207. // 提交请求
  208. fApi.value.btn.loading(true)
  209. try {
  210. await ProcessInstanceApi.createProcessInstance({
  211. processDefinitionId: props.selectProcessDefinition.id,
  212. variables: formData || detailForm.value.value,
  213. startUserSelectAssignees: startUserSelectAssignees.value
  214. })
  215. // 提示
  216. message.success('发起流程成功')
  217. // 跳转回去
  218. delView(unref(currentRoute))
  219. await push({
  220. name: 'BpmProcessInstanceMy'
  221. })
  222. } finally {
  223. fApi.value.btn.loading(false)
  224. }
  225. }
  226. const handleCancel = () => {
  227. emit('cancel')
  228. }
  229. defineExpose({ initProcessInfo })
  230. </script>
  231. <style lang="scss" scoped>
  232. $wrap-padding-height: 20px;
  233. $wrap-margin-height: 15px;
  234. $button-height: 51px;
  235. $process-header-height: 194px;
  236. .processInstance-wrap-main {
  237. height: calc(
  238. 100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - 35px
  239. );
  240. max-height: calc(
  241. 100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - 35px
  242. );
  243. overflow: auto;
  244. .form-scroll-area {
  245. height: calc(
  246. 100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - 35px -
  247. $process-header-height - 40px
  248. );
  249. max-height: calc(
  250. 100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - 35px -
  251. $process-header-height - 40px
  252. );
  253. overflow: auto;
  254. }
  255. }
  256. .form-box {
  257. :deep(.el-card) {
  258. border: none;
  259. }
  260. }
  261. </style>