UserTask.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. <template>
  2. <el-form label-width="100px">
  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="
  134. userTaskForm.candidateStrategy == CandidateStrategy.MULTI_LEVEL_DEPT_LEADER ||
  135. userTaskForm.candidateStrategy == CandidateStrategy.START_USER_DEPT_LEADER ||
  136. userTaskForm.candidateStrategy == CandidateStrategy.START_USER_MULTI_LEVEL_DEPT_LEADER ||
  137. userTaskForm.candidateStrategy == CandidateStrategy.FORM_DEPT_LEADER
  138. "
  139. :label="deptLevelLabel!"
  140. prop="deptLevel"
  141. span="24"
  142. >
  143. <el-select v-model="deptLevel" clearable>
  144. <el-option
  145. v-for="(item, index) in MULTI_LEVEL_DEPT"
  146. :key="index"
  147. :label="item.label"
  148. :value="item.value"
  149. />
  150. </el-select>
  151. </el-form-item>
  152. <el-form-item
  153. v-if="userTaskForm.candidateStrategy === CandidateStrategy.EXPRESSION"
  154. label="流程表达式"
  155. prop="candidateParam"
  156. >
  157. <el-input
  158. type="textarea"
  159. v-model="userTaskForm.candidateParam[0]"
  160. clearable
  161. style="width: 72%"
  162. @change="updateElementTask"
  163. />
  164. <el-button class="ml-5px" size="small" type="success" @click="openProcessExpressionDialog"
  165. >选择表达式</el-button
  166. >
  167. <!-- 选择弹窗 -->
  168. <ProcessExpressionDialog ref="processExpressionDialogRef" @select="selectProcessExpression" />
  169. </el-form-item>
  170. </el-form>
  171. </template>
  172. <script lang="ts" setup>
  173. import {
  174. CANDIDATE_STRATEGY,
  175. CandidateStrategy,
  176. FieldPermissionType,
  177. MULTI_LEVEL_DEPT
  178. } from '@/components/SimpleProcessDesignerV2/src/consts'
  179. import { defaultProps, handleTree } from '@/utils/tree'
  180. import * as RoleApi from '@/api/system/role'
  181. import * as DeptApi from '@/api/system/dept'
  182. import * as PostApi from '@/api/system/post'
  183. import * as UserApi from '@/api/system/user'
  184. import * as UserGroupApi from '@/api/bpm/userGroup'
  185. import ProcessExpressionDialog from './ProcessExpressionDialog.vue'
  186. import { ProcessExpressionVO } from '@/api/bpm/processExpression'
  187. import { useFormFieldsPermission } from '@/components/SimpleProcessDesignerV2/src/node'
  188. defineOptions({ name: 'UserTask' })
  189. const props = defineProps({
  190. id: String,
  191. type: String
  192. })
  193. const prefix = inject('prefix')
  194. const userTaskForm = ref({
  195. candidateStrategy: undefined, // 分配规则
  196. candidateParam: [] // 分配选项
  197. })
  198. const bpmnElement = ref()
  199. const bpmnInstances = () => (window as any)?.bpmnInstances
  200. const roleOptions = ref<RoleApi.RoleVO[]>([]) // 角色列表
  201. const deptTreeOptions = ref() // 部门树
  202. const postOptions = ref<PostApi.PostVO[]>([]) // 岗位列表
  203. const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
  204. const userGroupOptions = ref<UserGroupApi.UserGroupVO[]>([]) // 用户组列表
  205. // 表单内用户字段
  206. const { formFieldOptions } = useFormFieldsPermission(FieldPermissionType.READ)
  207. // 表单内用户字段选项, 必须是必填和用户选择器
  208. const userFieldOnFormOptions = computed(() => {
  209. return formFieldOptions.filter((item) => item.type === 'UserSelect')
  210. })
  211. const deptLevel = ref(1)
  212. const deptLevelLabel = computed(() => {
  213. let label = '部门负责人来源'
  214. if (userTaskForm.value.candidateStrategy == CandidateStrategy.MULTI_LEVEL_DEPT_LEADER) {
  215. label = label + '(指定部门向上)'
  216. } else if (userTaskForm.value.candidateStrategy == CandidateStrategy.FORM_DEPT_LEADER) {
  217. label = label + '(表单内部门向上)'
  218. } else {
  219. label = label + '(发起人部门向上)'
  220. }
  221. return label
  222. })
  223. const otherExtensions = ref()
  224. const resetTaskForm = () => {
  225. const businessObject = bpmnElement.value.businessObject
  226. if (!businessObject) {
  227. return
  228. }
  229. const extensionElements =
  230. businessObject?.extensionElements ??
  231. bpmnInstances().moddle.create('bpmn:ExtensionElements', { values: [] })
  232. userTaskForm.value.candidateStrategy = extensionElements.values?.filter(
  233. (ex) => ex.$type === `${prefix}:CandidateStrategy`
  234. )?.[0]?.value
  235. const candidateParamStr = extensionElements.values?.filter(
  236. (ex) => ex.$type === `${prefix}:CandidateParam`
  237. )?.[0]?.value
  238. if (candidateParamStr && candidateParamStr.length > 0) {
  239. if (userTaskForm.value.candidateStrategy === CandidateStrategy.EXPRESSION) {
  240. // 特殊:流程表达式,只有一个 input 输入框
  241. userTaskForm.value.candidateParam = [candidateParamStr]
  242. } else if (userTaskForm.value.candidateStrategy == CandidateStrategy.MULTI_LEVEL_DEPT_LEADER) {
  243. // 特殊:多级不部门负责人,需要通过'|'分割
  244. userTaskForm.value.candidateParam = candidateParamStr
  245. .split('|')[0]
  246. .split(',')
  247. .map((item) => {
  248. // 如果数字超出了最大安全整数范围,则将其作为字符串处理
  249. let num = Number(item)
  250. return num > Number.MAX_SAFE_INTEGER || num < -Number.MAX_SAFE_INTEGER ? item : num
  251. })
  252. deptLevel.value = +candidateParamStr.split('|')[1]
  253. } else {
  254. userTaskForm.value.candidateParam = candidateParamStr.split(',').map((item) => {
  255. // 如果数字超出了最大安全整数范围,则将其作为字符串处理
  256. let num = Number(item)
  257. return num > Number.MAX_SAFE_INTEGER || num < -Number.MAX_SAFE_INTEGER ? item : num
  258. })
  259. }
  260. } else {
  261. userTaskForm.value.candidateParam = []
  262. }
  263. otherExtensions.value =
  264. extensionElements.values?.filter(
  265. (ex) => ex.$type !== `${prefix}:CandidateStrategy` && ex.$type !== `${prefix}:CandidateParam`
  266. ) ?? []
  267. // 改用通过extensionElements来存储数据
  268. return
  269. if (businessObject.candidateStrategy != undefined) {
  270. userTaskForm.value.candidateStrategy = parseInt(businessObject.candidateStrategy) as any
  271. } else {
  272. userTaskForm.value.candidateStrategy = undefined
  273. }
  274. if (businessObject.candidateParam && businessObject.candidateParam.length > 0) {
  275. if (userTaskForm.value.candidateStrategy === 60) {
  276. // 特殊:流程表达式,只有一个 input 输入框
  277. userTaskForm.value.candidateParam = [businessObject.candidateParam]
  278. } else {
  279. userTaskForm.value.candidateParam = businessObject.candidateParam
  280. .split(',')
  281. .map((item) => item)
  282. }
  283. } else {
  284. userTaskForm.value.candidateParam = []
  285. }
  286. }
  287. /** 更新 candidateStrategy 字段时,需要清空 candidateParam,并触发 bpmn 图更新 */
  288. const changeCandidateStrategy = () => {
  289. userTaskForm.value.candidateParam = []
  290. deptLevel.value = 1
  291. if (userTaskForm.value.candidateStrategy === CandidateStrategy.FORM_USER) {
  292. // 特殊处理表单内用户字段,当只有发起人选项时应选中发起人
  293. if (!userFieldOnFormOptions.value || userFieldOnFormOptions.value.length <= 1) {
  294. userTaskForm.value.candidateStrategy = CandidateStrategy.START_USER
  295. }
  296. }
  297. updateElementTask()
  298. }
  299. /** 选中某个 options 时候,更新 bpmn 图 */
  300. const updateElementTask = () => {
  301. let candidateParam =
  302. userTaskForm.value.candidateParam instanceof Array
  303. ? userTaskForm.value.candidateParam.join(',')
  304. : userTaskForm.value.candidateParam
  305. // 特殊处理多级部门情况
  306. if (userTaskForm.value.candidateStrategy == CandidateStrategy.MULTI_LEVEL_DEPT_LEADER) {
  307. candidateParam += '|' + deptLevel.value
  308. }
  309. const extensions = bpmnInstances().moddle.create('bpmn:ExtensionElements', {
  310. values: [
  311. ...otherExtensions.value,
  312. bpmnInstances().moddle.create(`${prefix}:CandidateStrategy`, {
  313. value: userTaskForm.value.candidateStrategy
  314. }),
  315. bpmnInstances().moddle.create(`${prefix}:CandidateParam`, {
  316. value: candidateParam
  317. })
  318. ]
  319. })
  320. bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
  321. extensionElements: extensions
  322. })
  323. // 改用通过extensionElements来存储数据
  324. return
  325. bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
  326. candidateStrategy: userTaskForm.value.candidateStrategy,
  327. candidateParam: userTaskForm.value.candidateParam.join(',')
  328. })
  329. }
  330. // 打开监听器弹窗
  331. const processExpressionDialogRef = ref()
  332. const openProcessExpressionDialog = async () => {
  333. processExpressionDialogRef.value.open()
  334. }
  335. const selectProcessExpression = (expression: ProcessExpressionVO) => {
  336. userTaskForm.value.candidateParam = [expression.expression]
  337. updateElementTask()
  338. }
  339. const handleFormUserChange = (e) => {
  340. if (e === 'PROCESS_START_USER_ID') {
  341. userTaskForm.value.candidateParam = []
  342. userTaskForm.value.candidateStrategy = CandidateStrategy.START_USER
  343. }
  344. updateElementTask()
  345. }
  346. watch(
  347. () => props.id,
  348. () => {
  349. bpmnElement.value = bpmnInstances().bpmnElement
  350. nextTick(() => {
  351. resetTaskForm()
  352. })
  353. },
  354. { immediate: true }
  355. )
  356. onMounted(async () => {
  357. // 获得角色列表
  358. roleOptions.value = await RoleApi.getSimpleRoleList()
  359. // 获得部门列表
  360. const deptOptions = await DeptApi.getSimpleDeptList()
  361. deptTreeOptions.value = handleTree(deptOptions, 'id')
  362. // 获得岗位列表
  363. postOptions.value = await PostApi.getSimplePostList()
  364. // 获得用户列表
  365. userOptions.value = await UserApi.getSimpleUserList()
  366. // 获得用户组列表
  367. userGroupOptions.value = await UserGroupApi.getUserGroupSimpleList()
  368. })
  369. onBeforeUnmount(() => {
  370. bpmnElement.value = null
  371. })
  372. </script>