UserTask.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  1. <template>
  2. <el-form label-width="120px">
  3. <el-form-item label="规则类型" prop="candidateStrategy">
  4. <el-select
  5. v-model="userTaskForm.candidateStrategy"
  6. clearable
  7. style="width: 100%"
  8. @change="changeCandidateStrategy"
  9. >
  10. <el-option
  11. v-for="(dict, index) in CANDIDATE_STRATEGY"
  12. :key="index"
  13. :label="dict.label"
  14. :value="dict.value"
  15. />
  16. </el-select>
  17. </el-form-item>
  18. <el-form-item
  19. v-if="userTaskForm.candidateStrategy == CandidateStrategy.ROLE"
  20. label="指定角色"
  21. prop="candidateParam"
  22. >
  23. <el-select
  24. v-model="userTaskForm.candidateParam"
  25. clearable
  26. multiple
  27. style="width: 100%"
  28. @change="updateElementTask"
  29. >
  30. <el-option v-for="item in roleOptions" :key="item.id" :label="item.name" :value="item.id" />
  31. </el-select>
  32. </el-form-item>
  33. <el-form-item
  34. v-if="
  35. userTaskForm.candidateStrategy == CandidateStrategy.DEPT_MEMBER ||
  36. userTaskForm.candidateStrategy == CandidateStrategy.DEPT_LEADER ||
  37. userTaskForm.candidateStrategy == CandidateStrategy.MULTI_LEVEL_DEPT_LEADER
  38. "
  39. label="指定部门"
  40. prop="candidateParam"
  41. span="24"
  42. >
  43. <el-tree-select
  44. ref="treeRef"
  45. v-model="userTaskForm.candidateParam"
  46. :data="deptTreeOptions"
  47. :props="defaultProps"
  48. empty-text="加载中,请稍后"
  49. multiple
  50. node-key="id"
  51. show-checkbox
  52. @change="updateElementTask"
  53. />
  54. </el-form-item>
  55. <el-form-item
  56. v-if="userTaskForm.candidateStrategy == CandidateStrategy.POST"
  57. label="指定岗位"
  58. prop="candidateParam"
  59. span="24"
  60. >
  61. <el-select
  62. v-model="userTaskForm.candidateParam"
  63. clearable
  64. multiple
  65. style="width: 100%"
  66. @change="updateElementTask"
  67. >
  68. <el-option v-for="item in postOptions" :key="item.id" :label="item.name" :value="item.id" />
  69. </el-select>
  70. </el-form-item>
  71. <el-form-item
  72. v-if="userTaskForm.candidateStrategy == CandidateStrategy.USER"
  73. label="指定用户"
  74. prop="candidateParam"
  75. span="24"
  76. >
  77. <el-select
  78. v-model="userTaskForm.candidateParam"
  79. clearable
  80. multiple
  81. style="width: 100%"
  82. @change="updateElementTask"
  83. >
  84. <el-option
  85. v-for="item in userOptions"
  86. :key="item.id"
  87. :label="item.nickname"
  88. :value="item.id"
  89. />
  90. </el-select>
  91. </el-form-item>
  92. <el-form-item
  93. v-if="userTaskForm.candidateStrategy === CandidateStrategy.USER_GROUP"
  94. label="指定用户组"
  95. prop="candidateParam"
  96. >
  97. <el-select
  98. v-model="userTaskForm.candidateParam"
  99. clearable
  100. multiple
  101. style="width: 100%"
  102. @change="updateElementTask"
  103. >
  104. <el-option
  105. v-for="item in userGroupOptions"
  106. :key="item.id"
  107. :label="item.name"
  108. :value="item.id"
  109. />
  110. </el-select>
  111. </el-form-item>
  112. <el-form-item
  113. v-if="userTaskForm.candidateStrategy === CandidateStrategy.FORM_USER"
  114. label="表单内用户字段"
  115. prop="formUser"
  116. >
  117. <el-select
  118. v-model="userTaskForm.candidateParam"
  119. clearable
  120. style="width: 100%"
  121. @change="handleFormUserChange"
  122. >
  123. <el-option
  124. v-for="(item, idx) in userFieldOnFormOptions"
  125. :key="idx"
  126. :label="item.title"
  127. :value="item.field"
  128. :disabled="!item.required"
  129. />
  130. </el-select>
  131. </el-form-item>
  132. <el-form-item
  133. v-if="userTaskForm.candidateStrategy === CandidateStrategy.FORM_DEPT_LEADER"
  134. label="表单内部门字段"
  135. prop="formDept"
  136. >
  137. <el-select
  138. v-model="userTaskForm.candidateParam"
  139. clearable
  140. style="width: 100%"
  141. @change="updateElementTask"
  142. >
  143. <el-option
  144. v-for="(item, idx) in deptFieldOnFormOptions"
  145. :key="idx"
  146. :label="item.title"
  147. :value="item.field"
  148. :disabled="!item.required"
  149. />
  150. </el-select>
  151. </el-form-item>
  152. <el-form-item
  153. v-if="
  154. userTaskForm.candidateStrategy == CandidateStrategy.MULTI_LEVEL_DEPT_LEADER ||
  155. userTaskForm.candidateStrategy == CandidateStrategy.START_USER_DEPT_LEADER ||
  156. userTaskForm.candidateStrategy == CandidateStrategy.START_USER_MULTI_LEVEL_DEPT_LEADER ||
  157. userTaskForm.candidateStrategy == CandidateStrategy.FORM_DEPT_LEADER
  158. "
  159. :label="deptLevelLabel!"
  160. prop="deptLevel"
  161. span="24"
  162. >
  163. <el-select v-model="deptLevel" clearable @change="updateElementTask">
  164. <el-option
  165. v-for="(item, index) in MULTI_LEVEL_DEPT"
  166. :key="index"
  167. :label="item.label"
  168. :value="item.value"
  169. />
  170. </el-select>
  171. </el-form-item>
  172. <el-form-item
  173. v-if="userTaskForm.candidateStrategy === CandidateStrategy.EXPRESSION"
  174. label="流程表达式"
  175. prop="candidateParam"
  176. >
  177. <el-input
  178. type="textarea"
  179. v-model="userTaskForm.candidateParam[0]"
  180. clearable
  181. style="width: 72%"
  182. @change="updateElementTask"
  183. />
  184. <el-button class="ml-5px" size="small" type="success" @click="openProcessExpressionDialog"
  185. >选择表达式</el-button
  186. >
  187. <!-- 选择弹窗 -->
  188. <ProcessExpressionDialog ref="processExpressionDialogRef" @select="selectProcessExpression" />
  189. </el-form-item>
  190. </el-form>
  191. </template>
  192. <script lang="ts" setup>
  193. import {
  194. CANDIDATE_STRATEGY,
  195. CandidateStrategy,
  196. FieldPermissionType,
  197. MULTI_LEVEL_DEPT
  198. } from '@/components/SimpleProcessDesignerV2/src/consts'
  199. import { defaultProps, handleTree } from '@/utils/tree'
  200. import * as RoleApi from '@/api/system/role'
  201. import * as DeptApi from '@/api/system/dept'
  202. import * as PostApi from '@/api/system/post'
  203. import * as UserApi from '@/api/system/user'
  204. import * as UserGroupApi from '@/api/bpm/userGroup'
  205. import ProcessExpressionDialog from './ProcessExpressionDialog.vue'
  206. import { ProcessExpressionVO } from '@/api/bpm/processExpression'
  207. import { useFormFieldsPermission } from '@/components/SimpleProcessDesignerV2/src/node'
  208. defineOptions({ name: 'UserTask' })
  209. const props = defineProps({
  210. id: String,
  211. type: String
  212. })
  213. const prefix = inject('prefix')
  214. const userTaskForm = ref({
  215. candidateStrategy: undefined, // 分配规则
  216. candidateParam: [] // 分配选项
  217. })
  218. const bpmnElement = ref()
  219. const bpmnInstances = () => (window as any)?.bpmnInstances
  220. const roleOptions = ref<RoleApi.RoleVO[]>([]) // 角色列表
  221. const deptTreeOptions = ref() // 部门树
  222. const postOptions = ref<PostApi.PostVO[]>([]) // 岗位列表
  223. const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
  224. const userGroupOptions = ref<UserGroupApi.UserGroupVO[]>([]) // 用户组列表
  225. const { formFieldOptions } = useFormFieldsPermission(FieldPermissionType.READ)
  226. // 表单内用户字段选项, 必须是必填和用户选择器
  227. const userFieldOnFormOptions = computed(() => {
  228. return formFieldOptions.filter((item) => item.type === 'UserSelect')
  229. })
  230. // 表单内部门字段选项, 必须是必填和部门选择器
  231. const deptFieldOnFormOptions = computed(() => {
  232. return formFieldOptions.filter((item) => item.type === 'DeptSelect')
  233. })
  234. const deptLevel = ref(1)
  235. const deptLevelLabel = computed(() => {
  236. let label = '部门负责人来源'
  237. if (userTaskForm.value.candidateStrategy == CandidateStrategy.MULTI_LEVEL_DEPT_LEADER) {
  238. label = label + '(指定部门向上)'
  239. } else if (userTaskForm.value.candidateStrategy == CandidateStrategy.FORM_DEPT_LEADER) {
  240. label = label + '(表单内部门向上)'
  241. } else {
  242. label = label + '(发起人部门向上)'
  243. }
  244. return label
  245. })
  246. const otherExtensions = ref()
  247. const resetTaskForm = () => {
  248. const businessObject = bpmnElement.value.businessObject
  249. if (!businessObject) {
  250. return
  251. }
  252. const extensionElements =
  253. businessObject?.extensionElements ??
  254. bpmnInstances().moddle.create('bpmn:ExtensionElements', { values: [] })
  255. userTaskForm.value.candidateStrategy = extensionElements.values?.filter(
  256. (ex) => ex.$type === `${prefix}:CandidateStrategy`
  257. )?.[0]?.value
  258. const candidateParamStr = extensionElements.values?.filter(
  259. (ex) => ex.$type === `${prefix}:CandidateParam`
  260. )?.[0]?.value
  261. if (candidateParamStr && candidateParamStr.length > 0) {
  262. if (userTaskForm.value.candidateStrategy === CandidateStrategy.EXPRESSION) {
  263. // 特殊:流程表达式,只有一个 input 输入框
  264. userTaskForm.value.candidateParam = [candidateParamStr]
  265. } else if (userTaskForm.value.candidateStrategy == CandidateStrategy.MULTI_LEVEL_DEPT_LEADER) {
  266. // 特殊:多级不部门负责人,需要通过'|'分割
  267. userTaskForm.value.candidateParam = candidateParamStr
  268. .split('|')[0]
  269. .split(',')
  270. .map((item) => {
  271. // 如果数字超出了最大安全整数范围,则将其作为字符串处理
  272. let num = Number(item)
  273. return num > Number.MAX_SAFE_INTEGER || num < -Number.MAX_SAFE_INTEGER ? item : num
  274. })
  275. deptLevel.value = +candidateParamStr.split('|')[1]
  276. } else if (
  277. userTaskForm.value.candidateStrategy == CandidateStrategy.START_USER_DEPT_LEADER ||
  278. userTaskForm.value.candidateStrategy == CandidateStrategy.START_USER_MULTI_LEVEL_DEPT_LEADER
  279. ) {
  280. userTaskForm.value.candidateParam = +candidateParamStr
  281. deptLevel.value = +candidateParamStr
  282. } else if (userTaskForm.value.candidateStrategy == CandidateStrategy.FORM_DEPT_LEADER) {
  283. userTaskForm.value.candidateParam = candidateParamStr.split('|')[0]
  284. deptLevel.value = +candidateParamStr.split('|')[1]
  285. } else {
  286. userTaskForm.value.candidateParam = candidateParamStr.split(',').map((item) => {
  287. // 如果数字超出了最大安全整数范围,则将其作为字符串处理
  288. let num = Number(item)
  289. return num > Number.MAX_SAFE_INTEGER || num < -Number.MAX_SAFE_INTEGER ? item : num
  290. })
  291. }
  292. } else {
  293. userTaskForm.value.candidateParam = []
  294. }
  295. otherExtensions.value =
  296. extensionElements.values?.filter(
  297. (ex) => ex.$type !== `${prefix}:CandidateStrategy` && ex.$type !== `${prefix}:CandidateParam`
  298. ) ?? []
  299. // 改用通过extensionElements来存储数据
  300. return
  301. if (businessObject.candidateStrategy != undefined) {
  302. userTaskForm.value.candidateStrategy = parseInt(businessObject.candidateStrategy) as any
  303. } else {
  304. userTaskForm.value.candidateStrategy = undefined
  305. }
  306. if (businessObject.candidateParam && businessObject.candidateParam.length > 0) {
  307. if (userTaskForm.value.candidateStrategy === 60) {
  308. // 特殊:流程表达式,只有一个 input 输入框
  309. userTaskForm.value.candidateParam = [businessObject.candidateParam]
  310. } else {
  311. userTaskForm.value.candidateParam = businessObject.candidateParam
  312. .split(',')
  313. .map((item) => item)
  314. }
  315. } else {
  316. userTaskForm.value.candidateParam = []
  317. }
  318. }
  319. /** 更新 candidateStrategy 字段时,需要清空 candidateParam,并触发 bpmn 图更新 */
  320. const changeCandidateStrategy = () => {
  321. userTaskForm.value.candidateParam = []
  322. deptLevel.value = 1
  323. if (userTaskForm.value.candidateStrategy === CandidateStrategy.FORM_USER) {
  324. // 特殊处理表单内用户字段,当只有发起人选项时应选中发起人
  325. if (!userFieldOnFormOptions.value || userFieldOnFormOptions.value.length <= 1) {
  326. userTaskForm.value.candidateStrategy = CandidateStrategy.START_USER
  327. }
  328. }
  329. updateElementTask()
  330. }
  331. /** 选中某个 options 时候,更新 bpmn 图 */
  332. const updateElementTask = () => {
  333. let candidateParam =
  334. userTaskForm.value.candidateParam instanceof Array
  335. ? userTaskForm.value.candidateParam.join(',')
  336. : userTaskForm.value.candidateParam
  337. // 特殊处理多级部门情况
  338. if (
  339. userTaskForm.value.candidateStrategy == CandidateStrategy.MULTI_LEVEL_DEPT_LEADER ||
  340. userTaskForm.value.candidateStrategy == CandidateStrategy.FORM_DEPT_LEADER
  341. ) {
  342. candidateParam += '|' + deptLevel.value
  343. }
  344. // 特殊处理发起人部门负责人、发起人连续部门负责人
  345. if (
  346. userTaskForm.value.candidateStrategy == CandidateStrategy.START_USER_DEPT_LEADER ||
  347. userTaskForm.value.candidateStrategy == CandidateStrategy.START_USER_MULTI_LEVEL_DEPT_LEADER
  348. ) {
  349. candidateParam = deptLevel.value + ''
  350. }
  351. const extensions = bpmnInstances().moddle.create('bpmn:ExtensionElements', {
  352. values: [
  353. ...otherExtensions.value,
  354. bpmnInstances().moddle.create(`${prefix}:CandidateStrategy`, {
  355. value: userTaskForm.value.candidateStrategy
  356. }),
  357. bpmnInstances().moddle.create(`${prefix}:CandidateParam`, {
  358. value: candidateParam
  359. })
  360. ]
  361. })
  362. bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
  363. extensionElements: extensions
  364. })
  365. // 改用通过extensionElements来存储数据
  366. return
  367. bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
  368. candidateStrategy: userTaskForm.value.candidateStrategy,
  369. candidateParam: userTaskForm.value.candidateParam.join(',')
  370. })
  371. }
  372. // 打开监听器弹窗
  373. const processExpressionDialogRef = ref()
  374. const openProcessExpressionDialog = async () => {
  375. processExpressionDialogRef.value.open()
  376. }
  377. const selectProcessExpression = (expression: ProcessExpressionVO) => {
  378. userTaskForm.value.candidateParam = [expression.expression]
  379. updateElementTask()
  380. }
  381. const handleFormUserChange = (e) => {
  382. if (e === 'PROCESS_START_USER_ID') {
  383. userTaskForm.value.candidateParam = []
  384. userTaskForm.value.candidateStrategy = CandidateStrategy.START_USER
  385. }
  386. updateElementTask()
  387. }
  388. watch(
  389. () => props.id,
  390. () => {
  391. bpmnElement.value = bpmnInstances().bpmnElement
  392. nextTick(() => {
  393. resetTaskForm()
  394. })
  395. },
  396. { immediate: true }
  397. )
  398. onMounted(async () => {
  399. // 获得角色列表
  400. roleOptions.value = await RoleApi.getSimpleRoleList()
  401. // 获得部门列表
  402. const deptOptions = await DeptApi.getSimpleDeptList()
  403. deptTreeOptions.value = handleTree(deptOptions, 'id')
  404. // 获得岗位列表
  405. postOptions.value = await PostApi.getSimplePostList()
  406. // 获得用户列表
  407. userOptions.value = await UserApi.getSimpleUserList()
  408. // 获得用户组列表
  409. userGroupOptions.value = await UserGroupApi.getUserGroupSimpleList()
  410. })
  411. onBeforeUnmount(() => {
  412. bpmnElement.value = null
  413. })
  414. </script>