UserTaskNodeConfig.vue 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981
  1. <template>
  2. <el-drawer
  3. :append-to-body="true"
  4. v-model="settingVisible"
  5. :show-close="false"
  6. :size="580"
  7. :before-close="saveConfig"
  8. class="justify-start"
  9. >
  10. <template #header>
  11. <div class="config-header">
  12. <input
  13. v-if="showInput"
  14. type="text"
  15. class="config-editable-input"
  16. @blur="blurEvent()"
  17. v-mountedFocus
  18. v-model="nodeName"
  19. :placeholder="nodeName"
  20. />
  21. <div v-else class="node-name">
  22. {{ nodeName }}
  23. <Icon class="ml-1" icon="ep:edit-pen" :size="16" @click="clickIcon()" />
  24. </div>
  25. <div class="divide-line"></div>
  26. </div>
  27. </template>
  28. <div class="flex flex-items-center mb-3">
  29. <span class="font-size-16px mr-3">审批类型 :</span>
  30. <el-radio-group v-model="approveType">
  31. <el-radio
  32. v-for="(item, index) in APPROVE_TYPE"
  33. :key="index"
  34. :value="item.value"
  35. :label="item.value"
  36. >
  37. {{ item.label }}
  38. </el-radio>
  39. </el-radio-group>
  40. </div>
  41. <el-tabs type="border-card" v-model="activeTabName" v-if="approveType === ApproveType.USER">
  42. <el-tab-pane label="审批人" name="user">
  43. <div>
  44. <el-form ref="formRef" :model="configForm" label-position="top" :rules="formRules">
  45. <el-form-item label="审批人设置" prop="candidateStrategy">
  46. <el-radio-group
  47. v-model="configForm.candidateStrategy"
  48. @change="changeCandidateStrategy"
  49. >
  50. <el-row>
  51. <el-col v-for="(dict, index) in CANDIDATE_STRATEGY" :key="index" :span="8">
  52. <el-radio :value="dict.value" :label="dict.value">
  53. {{ dict.label }}
  54. </el-radio>
  55. </el-col>
  56. </el-row>
  57. </el-radio-group>
  58. </el-form-item>
  59. <el-form-item
  60. v-if="configForm.candidateStrategy == CandidateStrategy.ROLE"
  61. label="指定角色"
  62. prop="roleIds"
  63. >
  64. <el-select v-model="configForm.roleIds" clearable multiple style="width: 100%">
  65. <el-option
  66. v-for="item in roleOptions"
  67. :key="item.id"
  68. :label="item.name"
  69. :value="item.id"
  70. />
  71. </el-select>
  72. </el-form-item>
  73. <el-form-item
  74. v-if="
  75. configForm.candidateStrategy == CandidateStrategy.DEPT_MEMBER ||
  76. configForm.candidateStrategy == CandidateStrategy.DEPT_LEADER ||
  77. configForm.candidateStrategy == CandidateStrategy.MULTI_LEVEL_DEPT_LEADER
  78. "
  79. label="指定部门"
  80. prop="deptIds"
  81. span="24"
  82. >
  83. <el-tree-select
  84. ref="treeRef"
  85. v-model="configForm.deptIds"
  86. :data="deptTreeOptions"
  87. :props="defaultProps"
  88. empty-text="加载中,请稍后"
  89. multiple
  90. node-key="id"
  91. :check-strictly="true"
  92. style="width: 100%"
  93. show-checkbox
  94. />
  95. </el-form-item>
  96. <el-form-item
  97. v-if="configForm.candidateStrategy == CandidateStrategy.POST"
  98. label="指定岗位"
  99. prop="postIds"
  100. span="24"
  101. >
  102. <el-select v-model="configForm.postIds" clearable multiple style="width: 100%">
  103. <el-option
  104. v-for="item in postOptions"
  105. :key="item.id"
  106. :label="item.name"
  107. :value="item.id!"
  108. />
  109. </el-select>
  110. </el-form-item>
  111. <el-form-item
  112. v-if="configForm.candidateStrategy == CandidateStrategy.USER"
  113. label="指定用户"
  114. prop="userIds"
  115. span="24"
  116. >
  117. <el-select v-model="configForm.userIds" clearable multiple style="width: 100%">
  118. <el-option
  119. v-for="item in userOptions"
  120. :key="item.id"
  121. :label="item.nickname"
  122. :value="item.id"
  123. />
  124. </el-select>
  125. </el-form-item>
  126. <el-form-item
  127. v-if="configForm.candidateStrategy === CandidateStrategy.USER_GROUP"
  128. label="指定用户组"
  129. prop="userGroups"
  130. >
  131. <el-select v-model="configForm.userGroups" clearable multiple style="width: 100%">
  132. <el-option
  133. v-for="item in userGroupOptions"
  134. :key="item.id"
  135. :label="item.name"
  136. :value="item.id"
  137. />
  138. </el-select>
  139. </el-form-item>
  140. <el-form-item
  141. v-if="configForm.candidateStrategy === CandidateStrategy.FORM_USER"
  142. label="表单内用户字段"
  143. prop="formUser"
  144. >
  145. <el-select v-model="configForm.formUser" clearable style="width: 100%">
  146. <el-option
  147. v-for="(item, idx) in userFieldOnFormOptions"
  148. :key="idx"
  149. :label="item.title"
  150. :value="item.field"
  151. :disabled="!item.required"
  152. />
  153. </el-select>
  154. </el-form-item>
  155. <el-form-item
  156. v-if="configForm.candidateStrategy === CandidateStrategy.FORM_DEPT_LEADER"
  157. label="表单内部门字段"
  158. prop="formDept"
  159. >
  160. <el-select v-model="configForm.formDept" clearable style="width: 100%">
  161. <el-option
  162. v-for="(item, idx) in deptFieldOnFormOptions"
  163. :key="idx"
  164. :label="item.title"
  165. :value="item.field"
  166. :disabled="!item.required"
  167. />
  168. </el-select>
  169. </el-form-item>
  170. <el-form-item
  171. v-if="
  172. configForm.candidateStrategy == CandidateStrategy.MULTI_LEVEL_DEPT_LEADER ||
  173. configForm.candidateStrategy == CandidateStrategy.START_USER_DEPT_LEADER ||
  174. configForm.candidateStrategy ==
  175. CandidateStrategy.START_USER_MULTI_LEVEL_DEPT_LEADER ||
  176. configForm.candidateStrategy == CandidateStrategy.FORM_DEPT_LEADER
  177. "
  178. :label="deptLevelLabel!"
  179. prop="deptLevel"
  180. span="24"
  181. >
  182. <el-select v-model="configForm.deptLevel" clearable>
  183. <el-option
  184. v-for="(item, index) in MULTI_LEVEL_DEPT"
  185. :key="index"
  186. :label="item.label"
  187. :value="item.value"
  188. />
  189. </el-select>
  190. </el-form-item>
  191. <!-- TODO @jason:后续要支持选择已经存好的表达式 -->
  192. <el-form-item
  193. v-if="configForm.candidateStrategy === CandidateStrategy.EXPRESSION"
  194. label="流程表达式"
  195. prop="expression"
  196. >
  197. <el-input
  198. type="textarea"
  199. v-model="configForm.expression"
  200. clearable
  201. style="width: 100%"
  202. />
  203. </el-form-item>
  204. <el-form-item label="多人审批方式" prop="approveMethod">
  205. <el-radio-group v-model="configForm.approveMethod" @change="approveMethodChanged">
  206. <div class="flex-col">
  207. <div
  208. v-for="(item, index) in APPROVE_METHODS"
  209. :key="index"
  210. class="flex items-center"
  211. >
  212. <el-radio :value="item.value" :label="item.value">
  213. {{ item.label }}
  214. </el-radio>
  215. <el-form-item prop="approveRatio">
  216. <el-input-number
  217. v-model="configForm.approveRatio"
  218. :min="10"
  219. :max="100"
  220. :step="10"
  221. size="small"
  222. v-if="
  223. item.value === ApproveMethodType.APPROVE_BY_RATIO &&
  224. configForm.approveMethod === ApproveMethodType.APPROVE_BY_RATIO
  225. "
  226. />
  227. </el-form-item>
  228. </div>
  229. </div>
  230. </el-radio-group>
  231. </el-form-item>
  232. <el-divider content-position="left">审批人拒绝时</el-divider>
  233. <el-form-item prop="rejectHandlerType">
  234. <el-radio-group v-model="configForm.rejectHandlerType">
  235. <div class="flex-col">
  236. <div v-for="(item, index) in REJECT_HANDLER_TYPES" :key="index">
  237. <el-radio :key="item.value" :value="item.value" :label="item.label" />
  238. </div>
  239. </div>
  240. </el-radio-group>
  241. </el-form-item>
  242. <el-form-item
  243. v-if="configForm.rejectHandlerType == RejectHandlerType.RETURN_USER_TASK"
  244. label="驳回节点"
  245. prop="returnNodeId"
  246. >
  247. <el-select v-model="configForm.returnNodeId" clearable style="width: 100%">
  248. <el-option
  249. v-for="item in returnTaskList"
  250. :key="item.id"
  251. :label="item.name"
  252. :value="item.id"
  253. />
  254. </el-select>
  255. </el-form-item>
  256. <el-divider content-position="left">审批人超时未处理时</el-divider>
  257. <el-form-item label="启用开关" prop="timeoutHandlerEnable">
  258. <el-switch
  259. v-model="configForm.timeoutHandlerEnable"
  260. active-text="开启"
  261. inactive-text="关闭"
  262. @change="timeoutHandlerChange"
  263. />
  264. </el-form-item>
  265. <el-form-item
  266. label="执行动作"
  267. prop="timeoutHandlerType"
  268. v-if="configForm.timeoutHandlerEnable"
  269. >
  270. <el-radio-group
  271. v-model="configForm.timeoutHandlerType"
  272. @change="timeoutHandlerTypeChanged"
  273. >
  274. <el-radio-button
  275. v-for="item in TIMEOUT_HANDLER_TYPES"
  276. :key="item.value"
  277. :value="item.value"
  278. :label="item.label"
  279. />
  280. </el-radio-group>
  281. </el-form-item>
  282. <el-form-item label="超时时间设置" v-if="configForm.timeoutHandlerEnable">
  283. <span class="mr-2">当超过</span>
  284. <el-form-item prop="timeDuration">
  285. <el-input-number
  286. class="mr-2"
  287. :style="{ width: '100px' }"
  288. v-model="configForm.timeDuration"
  289. :min="1"
  290. controls-position="right"
  291. />
  292. </el-form-item>
  293. <el-select
  294. v-model="timeUnit"
  295. class="mr-2"
  296. :style="{ width: '100px' }"
  297. @change="timeUnitChange"
  298. >
  299. <el-option
  300. v-for="item in TIME_UNIT_TYPES"
  301. :key="item.value"
  302. :label="item.label"
  303. :value="item.value"
  304. />
  305. </el-select>
  306. 未处理
  307. </el-form-item>
  308. <el-form-item
  309. label="最大提醒次数"
  310. prop="maxRemindCount"
  311. v-if="configForm.timeoutHandlerEnable && configForm.timeoutHandlerType === 1"
  312. >
  313. <el-input-number v-model="configForm.maxRemindCount" :min="1" :max="10" />
  314. </el-form-item>
  315. <el-divider content-position="left">审批人为空时</el-divider>
  316. <el-form-item prop="assignEmptyHandlerType">
  317. <el-radio-group v-model="configForm.assignEmptyHandlerType">
  318. <div class="flex-col">
  319. <div v-for="(item, index) in ASSIGN_EMPTY_HANDLER_TYPES" :key="index">
  320. <el-radio :key="item.value" :value="item.value" :label="item.label" />
  321. </div>
  322. </div>
  323. </el-radio-group>
  324. </el-form-item>
  325. <el-form-item
  326. v-if="configForm.assignEmptyHandlerType == AssignEmptyHandlerType.ASSIGN_USER"
  327. label="指定用户"
  328. prop="assignEmptyHandlerUserIds"
  329. span="24"
  330. >
  331. <el-select
  332. v-model="configForm.assignEmptyHandlerUserIds"
  333. clearable
  334. multiple
  335. style="width: 100%"
  336. >
  337. <el-option
  338. v-for="item in userOptions"
  339. :key="item.id"
  340. :label="item.nickname"
  341. :value="item.id"
  342. />
  343. </el-select>
  344. </el-form-item>
  345. <el-divider content-position="left">审批人与提交人为同一人时</el-divider>
  346. <el-form-item prop="assignStartUserHandlerType">
  347. <el-radio-group v-model="configForm.assignStartUserHandlerType">
  348. <div class="flex-col">
  349. <div v-for="(item, index) in ASSIGN_START_USER_HANDLER_TYPES" :key="index">
  350. <el-radio :key="item.value" :value="item.value" :label="item.label" />
  351. </div>
  352. </div>
  353. </el-radio-group>
  354. </el-form-item>
  355. <el-divider content-position="left">是否需要签名</el-divider>
  356. <el-form-item prop="signEnable">
  357. <el-switch v-model="configForm.signEnable" active-text="是" inactive-text="否" />
  358. </el-form-item>
  359. <el-divider content-position="left">审批意见</el-divider>
  360. <el-form-item prop="reasonRequire">
  361. <el-switch v-model="configForm.reasonRequire" active-text="必填" inactive-text="非必填" />
  362. </el-form-item>
  363. </el-form>
  364. </div>
  365. </el-tab-pane>
  366. <el-tab-pane label="操作按钮设置" name="buttons">
  367. <div class="button-setting-pane">
  368. <div class="button-setting-desc">操作按钮</div>
  369. <div class="button-setting-title">
  370. <div class="button-title-label">操作按钮</div>
  371. <div class="pl-4 button-title-label">显示名称</div>
  372. <div class="button-title-label">启用</div>
  373. </div>
  374. <div class="button-setting-item" v-for="(item, index) in buttonsSetting" :key="index">
  375. <div class="button-setting-item-label"> {{ OPERATION_BUTTON_NAME.get(item.id) }} </div>
  376. <div class="button-setting-item-label">
  377. <input
  378. type="text"
  379. class="editable-title-input"
  380. @blur="btnDisplayNameBlurEvent(index)"
  381. v-mountedFocus
  382. v-model="item.displayName"
  383. :placeholder="item.displayName"
  384. v-if="btnDisplayNameEdit[index]"
  385. />
  386. <el-button v-else text @click="changeBtnDisplayName(index)"
  387. >{{ item.displayName }} &nbsp;<Icon icon="ep:edit"
  388. /></el-button>
  389. </div>
  390. <div class="button-setting-item-label">
  391. <el-switch v-model="item.enable" />
  392. </div>
  393. </div>
  394. </div>
  395. </el-tab-pane>
  396. <el-tab-pane label="表单字段权限" name="fields" v-if="formType === 10">
  397. <div class="field-setting-pane">
  398. <div class="field-setting-desc">字段权限</div>
  399. <div class="field-permit-title">
  400. <div class="setting-title-label first-title"> 字段名称 </div>
  401. <div class="other-titles">
  402. <span class="setting-title-label">只读</span>
  403. <span class="setting-title-label">可编辑</span>
  404. <span class="setting-title-label">隐藏</span>
  405. </div>
  406. </div>
  407. <div
  408. class="field-setting-item"
  409. v-for="(item, index) in fieldsPermissionConfig"
  410. :key="index"
  411. >
  412. <div class="field-setting-item-label"> {{ item.title }} </div>
  413. <el-radio-group class="field-setting-item-group" v-model="item.permission">
  414. <div class="item-radio-wrap">
  415. <el-radio
  416. :value="FieldPermissionType.READ"
  417. size="large"
  418. :label="FieldPermissionType.READ"
  419. ><span></span
  420. ></el-radio>
  421. </div>
  422. <div class="item-radio-wrap">
  423. <el-radio
  424. :value="FieldPermissionType.WRITE"
  425. size="large"
  426. :label="FieldPermissionType.WRITE"
  427. ><span></span
  428. ></el-radio>
  429. </div>
  430. <div class="item-radio-wrap">
  431. <el-radio
  432. :value="FieldPermissionType.NONE"
  433. size="large"
  434. :label="FieldPermissionType.NONE"
  435. ><span></span
  436. ></el-radio>
  437. </div>
  438. </el-radio-group>
  439. </div>
  440. </div>
  441. </el-tab-pane>
  442. <el-tab-pane label="监听器" name="listener">
  443. <UserTaskListener ref="userTaskListenerRef" v-model="configForm" :form-field-options="formFieldOptions" />
  444. </el-tab-pane>
  445. </el-tabs>
  446. <template #footer>
  447. <el-divider />
  448. <div>
  449. <el-button type="primary" @click="saveConfig">确 定</el-button>
  450. <el-button @click="closeDrawer">取 消</el-button>
  451. </div>
  452. </template>
  453. </el-drawer>
  454. </template>
  455. <script setup lang="ts">
  456. import {
  457. SimpleFlowNode,
  458. APPROVE_TYPE,
  459. ApproveType,
  460. APPROVE_METHODS,
  461. CandidateStrategy,
  462. NodeType,
  463. ApproveMethodType,
  464. TimeUnitType,
  465. RejectHandlerType,
  466. TIMEOUT_HANDLER_TYPES,
  467. TIME_UNIT_TYPES,
  468. REJECT_HANDLER_TYPES,
  469. DEFAULT_BUTTON_SETTING,
  470. OPERATION_BUTTON_NAME,
  471. ButtonSetting,
  472. MULTI_LEVEL_DEPT,
  473. CANDIDATE_STRATEGY,
  474. ASSIGN_START_USER_HANDLER_TYPES,
  475. TimeoutHandlerType,
  476. ASSIGN_EMPTY_HANDLER_TYPES,
  477. AssignEmptyHandlerType,
  478. FieldPermissionType,
  479. ProcessVariableEnum
  480. } from '../consts'
  481. import {
  482. useWatchNode,
  483. useNodeName,
  484. useFormFieldsPermission,
  485. useNodeForm,
  486. UserTaskFormType,
  487. useDrawer
  488. } from '../node'
  489. import { defaultProps } from '@/utils/tree'
  490. import { cloneDeep } from 'lodash-es'
  491. import { convertTimeUnit, getApproveTypeText } from '../utils'
  492. import UserTaskListener from './components/UserTaskListener.vue'
  493. defineOptions({
  494. name: 'UserTaskNodeConfig'
  495. })
  496. const props = defineProps({
  497. flowNode: {
  498. type: Object as () => SimpleFlowNode,
  499. required: true
  500. }
  501. })
  502. const emits = defineEmits<{
  503. 'find:returnTaskNodes': [nodeList: SimpleFlowNode[]]
  504. }>()
  505. const deptLevelLabel = computed(() => {
  506. let label = '部门负责人来源'
  507. if (configForm.value.candidateStrategy == CandidateStrategy.MULTI_LEVEL_DEPT_LEADER) {
  508. label = label + '(指定部门向上)'
  509. } else if (configForm.value.candidateStrategy == CandidateStrategy.FORM_DEPT_LEADER) {
  510. label = label + '(表单内部门向上)'
  511. } else {
  512. label = label + '(发起人部门向上)'
  513. }
  514. return label
  515. })
  516. // 监控节点的变化
  517. const currentNode = useWatchNode(props)
  518. // 抽屉配置
  519. const { settingVisible, closeDrawer, openDrawer } = useDrawer()
  520. // 节点名称配置
  521. const { nodeName, showInput, clickIcon, blurEvent } = useNodeName(NodeType.USER_TASK_NODE)
  522. // 激活的 Tab 标签页
  523. const activeTabName = ref('user')
  524. // 表单字段权限设置
  525. const { formType, fieldsPermissionConfig, formFieldOptions, getNodeConfigFormFields } =
  526. useFormFieldsPermission(FieldPermissionType.READ)
  527. // 表单内用户字段选项, 必须是必填和用户选择器
  528. const userFieldOnFormOptions = computed(() => {
  529. // 固定添加发起人 ID 字段
  530. formFieldOptions.unshift({
  531. field: ProcessVariableEnum.START_USER_ID,
  532. title: '发起人',
  533. type: 'UserSelect',
  534. required: true
  535. })
  536. return formFieldOptions.filter((item) => item.type === 'UserSelect')
  537. })
  538. // 表单内部门字段选项, 必须是必填和部门选择器
  539. const deptFieldOnFormOptions = computed(() => {
  540. return formFieldOptions.filter((item) => item.type === 'DeptSelect')
  541. })
  542. // 操作按钮设置
  543. const { buttonsSetting, btnDisplayNameEdit, changeBtnDisplayName, btnDisplayNameBlurEvent } =
  544. useButtonsSetting()
  545. const approveType = ref(ApproveType.USER)
  546. // 审批人表单设置
  547. const formRef = ref() // 表单 Ref
  548. // 表单校验规则
  549. const formRules = reactive({
  550. candidateStrategy: [{ required: true, message: '审批人设置不能为空', trigger: 'change' }],
  551. userIds: [{ required: true, message: '用户不能为空', trigger: 'change' }],
  552. roleIds: [{ required: true, message: '角色不能为空', trigger: 'change' }],
  553. deptIds: [{ required: true, message: '部门不能为空', trigger: 'change' }],
  554. userGroups: [{ required: true, message: '用户组不能为空', trigger: 'change' }],
  555. formUser: [{ required: true, message: '表单内用户字段不能为空', trigger: 'change' }],
  556. formDept: [{ required: true, message: '表单内部门字段不能为空', trigger: 'change' }],
  557. postIds: [{ required: true, message: '岗位不能为空', trigger: 'change' }],
  558. expression: [{ required: true, message: '流程表达式不能为空', trigger: 'blur' }],
  559. approveMethod: [{ required: true, message: '多人审批方式不能为空', trigger: 'change' }],
  560. approveRatio: [{ required: true, message: '通过比例不能为空', trigger: 'blur' }],
  561. returnNodeId: [{ required: true, message: '驳回节点不能为空', trigger: 'change' }],
  562. timeoutHandlerEnable: [{ required: true }],
  563. timeoutHandlerType: [{ required: true }],
  564. timeDuration: [{ required: true, message: '超时时间不能为空', trigger: 'blur' }],
  565. maxRemindCount: [{ required: true, message: '提醒次数不能为空', trigger: 'blur' }],
  566. assignEmptyHandlerType: [{ required: true }],
  567. assignEmptyHandlerUserIds: [{ required: true, message: '用户不能为空', trigger: 'change' }],
  568. assignStartUserHandlerType: [{ required: true }]
  569. })
  570. const {
  571. configForm: tempConfigForm,
  572. roleOptions,
  573. postOptions,
  574. userOptions,
  575. userGroupOptions,
  576. deptTreeOptions,
  577. handleCandidateParam,
  578. parseCandidateParam,
  579. getShowText
  580. } = useNodeForm(NodeType.USER_TASK_NODE)
  581. const configForm = tempConfigForm as Ref<UserTaskFormType>
  582. // 改变审批人设置策略
  583. const changeCandidateStrategy = () => {
  584. configForm.value.userIds = []
  585. configForm.value.deptIds = []
  586. configForm.value.roleIds = []
  587. configForm.value.postIds = []
  588. configForm.value.userGroups = []
  589. configForm.value.deptLevel = 1
  590. configForm.value.formUser = ''
  591. configForm.value.formDept = ''
  592. configForm.value.approveMethod = ApproveMethodType.SEQUENTIAL_APPROVE
  593. }
  594. // 审批方式改变
  595. const approveMethodChanged = () => {
  596. configForm.value.rejectHandlerType = RejectHandlerType.FINISH_PROCESS
  597. if (configForm.value.approveMethod === ApproveMethodType.APPROVE_BY_RATIO) {
  598. configForm.value.approveRatio = 100
  599. }
  600. formRef.value.clearValidate('approveRatio')
  601. }
  602. // 审批拒绝 可退回的节点
  603. const returnTaskList = ref<SimpleFlowNode[]>([])
  604. // 审批人超时未处理设置
  605. const {
  606. timeoutHandlerChange,
  607. cTimeoutType,
  608. timeoutHandlerTypeChanged,
  609. timeUnit,
  610. timeUnitChange,
  611. isoTimeDuration,
  612. cTimeoutMaxRemindCount
  613. } = useTimeoutHandler()
  614. const userTaskListenerRef = ref()
  615. // 保存配置
  616. const saveConfig = async () => {
  617. // activeTabName.value = 'user'
  618. // 设置审批节点名称
  619. currentNode.value.name = nodeName.value!
  620. // 设置审批类型
  621. currentNode.value.approveType = approveType.value
  622. // 如果不是人工审批。返回
  623. if (approveType.value !== ApproveType.USER) {
  624. currentNode.value.showText = getApproveTypeText(approveType.value)
  625. settingVisible.value = false
  626. return true
  627. }
  628. if (!formRef) return false
  629. if (!userTaskListenerRef) return false
  630. const valid = (await formRef.value.validate()) && (await userTaskListenerRef.value.validate())
  631. if (!valid) return false
  632. const showText = getShowText()
  633. if (!showText) return false
  634. currentNode.value.candidateStrategy = configForm.value.candidateStrategy
  635. // 处理 candidateParam 参数
  636. currentNode.value.candidateParam = handleCandidateParam()
  637. // 设置审批方式
  638. currentNode.value.approveMethod = configForm.value.approveMethod
  639. if (configForm.value.approveMethod === ApproveMethodType.APPROVE_BY_RATIO) {
  640. currentNode.value.approveRatio = configForm.value.approveRatio
  641. }
  642. // 设置拒绝处理
  643. currentNode.value.rejectHandler = {
  644. type: configForm.value.rejectHandlerType!,
  645. returnNodeId: configForm.value.returnNodeId
  646. }
  647. // 设置超时处理
  648. currentNode.value.timeoutHandler = {
  649. enable: configForm.value.timeoutHandlerEnable!,
  650. type: cTimeoutType.value,
  651. timeDuration: isoTimeDuration.value,
  652. maxRemindCount: cTimeoutMaxRemindCount.value
  653. }
  654. // 设置审批人为空时
  655. currentNode.value.assignEmptyHandler = {
  656. type: configForm.value.assignEmptyHandlerType!,
  657. userIds:
  658. configForm.value.assignEmptyHandlerType === AssignEmptyHandlerType.ASSIGN_USER
  659. ? configForm.value.assignEmptyHandlerUserIds
  660. : undefined
  661. }
  662. // 设置审批人与发起人相同时
  663. currentNode.value.assignStartUserHandlerType = configForm.value.assignStartUserHandlerType
  664. // 设置表单权限
  665. currentNode.value.fieldsPermission = fieldsPermissionConfig.value
  666. // 设置按钮权限
  667. currentNode.value.buttonsSetting = buttonsSetting.value
  668. // 创建任务监听器
  669. currentNode.value.taskCreateListener = {
  670. enable: configForm.value.taskCreateListenerEnable ?? false,
  671. path: configForm.value.taskCreateListenerPath,
  672. header: configForm.value.taskCreateListener?.header,
  673. body: configForm.value.taskCreateListener?.body
  674. }
  675. // 指派任务监听器
  676. currentNode.value.taskAssignListener = {
  677. enable: configForm.value.taskAssignListenerEnable ?? false,
  678. path: configForm.value.taskAssignListenerPath,
  679. header: configForm.value.taskAssignListener?.header,
  680. body: configForm.value.taskAssignListener?.body
  681. }
  682. // 完成任务监听器
  683. currentNode.value.taskCompleteListener = {
  684. enable: configForm.value.taskCompleteListenerEnable ?? false,
  685. path: configForm.value.taskCompleteListenerPath,
  686. header: configForm.value.taskCompleteListener?.header,
  687. body: configForm.value.taskCompleteListener?.body
  688. }
  689. // 签名
  690. currentNode.value.signEnable = configForm.value.signEnable
  691. // 审批意见
  692. currentNode.value.reasonRequire = configForm.value.reasonRequire
  693. currentNode.value.showText = showText
  694. settingVisible.value = false
  695. return true
  696. }
  697. // 显示审批节点配置, 由父组件传过来
  698. const showUserTaskNodeConfig = (node: SimpleFlowNode) => {
  699. nodeName.value = node.name
  700. // 1 审批类型
  701. approveType.value = node.approveType ? node.approveType : ApproveType.USER
  702. // 如果审批类型不是人工审批返回
  703. if (approveType.value !== ApproveType.USER) {
  704. return
  705. }
  706. //2.1 审批人设置
  707. configForm.value.candidateStrategy = node.candidateStrategy!
  708. // 解析候选人参数
  709. parseCandidateParam(node.candidateStrategy!, node?.candidateParam)
  710. // 2.2 设置审批方式
  711. configForm.value.approveMethod = node.approveMethod!
  712. if (node.approveMethod == ApproveMethodType.APPROVE_BY_RATIO) {
  713. configForm.value.approveRatio = node.approveRatio!
  714. }
  715. // 2.3 设置审批拒绝处理
  716. configForm.value.rejectHandlerType = node.rejectHandler!.type
  717. configForm.value.returnNodeId = node.rejectHandler?.returnNodeId
  718. const matchNodeList = []
  719. emits('find:returnTaskNodes', matchNodeList)
  720. returnTaskList.value = matchNodeList
  721. // 2.4 设置审批超时处理
  722. configForm.value.timeoutHandlerEnable = node.timeoutHandler!.enable
  723. if (node.timeoutHandler?.enable && node.timeoutHandler?.timeDuration) {
  724. const strTimeDuration = node.timeoutHandler.timeDuration
  725. let parseTime = strTimeDuration.slice(2, strTimeDuration.length - 1)
  726. let parseTimeUnit = strTimeDuration.slice(strTimeDuration.length - 1)
  727. configForm.value.timeDuration = parseInt(parseTime)
  728. timeUnit.value = convertTimeUnit(parseTimeUnit)
  729. }
  730. configForm.value.timeoutHandlerType = node.timeoutHandler?.type
  731. configForm.value.maxRemindCount = node.timeoutHandler?.maxRemindCount
  732. // 2.5 设置审批人为空时
  733. configForm.value.assignEmptyHandlerType = node.assignEmptyHandler?.type
  734. configForm.value.assignEmptyHandlerUserIds = node.assignEmptyHandler?.userIds
  735. // 2.6 设置用户任务的审批人与发起人相同时
  736. configForm.value.assignStartUserHandlerType = node.assignStartUserHandlerType
  737. // 3. 操作按钮设置
  738. buttonsSetting.value = cloneDeep(node.buttonsSetting) || DEFAULT_BUTTON_SETTING
  739. // 4. 表单字段权限配置
  740. getNodeConfigFormFields(node.fieldsPermission)
  741. // 5. 监听器
  742. // 5.1 创建任务
  743. configForm.value.taskCreateListenerEnable = node.taskCreateListener!.enable
  744. configForm.value.taskCreateListenerPath = node.taskCreateListener!.path
  745. configForm.value.taskCreateListener = {
  746. header: node.taskCreateListener?.header ?? [],
  747. body: node.taskCreateListener?.body ?? []
  748. }
  749. // 5.2 指派任务
  750. configForm.value.taskAssignListenerEnable = node.taskAssignListener!.enable
  751. configForm.value.taskAssignListenerPath = node.taskAssignListener!.path
  752. configForm.value.taskAssignListener = {
  753. header: node.taskAssignListener?.header ?? [],
  754. body: node.taskAssignListener?.body ?? []
  755. }
  756. // 5.3 完成任务
  757. configForm.value.taskCompleteListenerEnable = node.taskCompleteListener!.enable
  758. configForm.value.taskCompleteListenerPath = node.taskCompleteListener!.path
  759. configForm.value.taskCompleteListener = {
  760. header: node.taskCompleteListener?.header ?? [],
  761. body: node.taskCompleteListener?.body ?? []
  762. }
  763. // 6. 签名
  764. configForm.value.signEnable = node?.signEnable ?? false
  765. // 7. 审批意见
  766. configForm.value.reasonRequire = node?.reasonRequire ?? false
  767. }
  768. defineExpose({ openDrawer, showUserTaskNodeConfig }) // 暴露方法给父组件
  769. /**
  770. * @description 操作按钮设置
  771. */
  772. function useButtonsSetting() {
  773. const buttonsSetting = ref<ButtonSetting[]>()
  774. // 操作按钮显示名称可编辑
  775. const btnDisplayNameEdit = ref<boolean[]>([])
  776. const changeBtnDisplayName = (index: number) => {
  777. btnDisplayNameEdit.value[index] = true
  778. }
  779. const btnDisplayNameBlurEvent = (index: number) => {
  780. btnDisplayNameEdit.value[index] = false
  781. const buttonItem = buttonsSetting.value![index]
  782. buttonItem.displayName = buttonItem.displayName || OPERATION_BUTTON_NAME.get(buttonItem.id)!
  783. }
  784. return {
  785. buttonsSetting,
  786. btnDisplayNameEdit,
  787. changeBtnDisplayName,
  788. btnDisplayNameBlurEvent
  789. }
  790. }
  791. /**
  792. * @description 审批人超时未处理配置
  793. */
  794. function useTimeoutHandler() {
  795. // 时间单位
  796. const timeUnit = ref(TimeUnitType.HOUR)
  797. // 超时开关改变
  798. const timeoutHandlerChange = () => {
  799. if (configForm.value.timeoutHandlerEnable) {
  800. timeUnit.value = 2
  801. configForm.value.timeDuration = 6
  802. configForm.value.timeoutHandlerType = 1
  803. configForm.value.maxRemindCount = 1
  804. }
  805. }
  806. // 超时执行的动作
  807. const cTimeoutType = computed(() => {
  808. if (!configForm.value.timeoutHandlerEnable) {
  809. return undefined
  810. }
  811. return configForm.value.timeoutHandlerType
  812. })
  813. // 超时处理动作改变
  814. const timeoutHandlerTypeChanged = () => {
  815. if (configForm.value.timeoutHandlerType === TimeoutHandlerType.REMINDER) {
  816. configForm.value.maxRemindCount = 1 // 超时提醒次数,默认为1
  817. }
  818. }
  819. // 时间单位改变
  820. const timeUnitChange = () => {
  821. // 分钟,默认是 60 分钟
  822. if (timeUnit.value === TimeUnitType.MINUTE) {
  823. configForm.value.timeDuration = 60
  824. }
  825. // 小时,默认是 6 个小时
  826. if (timeUnit.value === TimeUnitType.HOUR) {
  827. configForm.value.timeDuration = 6
  828. }
  829. // 天, 默认 1天
  830. if (timeUnit.value === TimeUnitType.DAY) {
  831. configForm.value.timeDuration = 1
  832. }
  833. }
  834. // 超时时间的 ISO 表示
  835. const isoTimeDuration = computed(() => {
  836. if (!configForm.value.timeoutHandlerEnable) {
  837. return undefined
  838. }
  839. let strTimeDuration = 'PT'
  840. if (timeUnit.value === TimeUnitType.MINUTE) {
  841. strTimeDuration += configForm.value.timeDuration + 'M'
  842. }
  843. if (timeUnit.value === TimeUnitType.HOUR) {
  844. strTimeDuration += configForm.value.timeDuration + 'H'
  845. }
  846. if (timeUnit.value === TimeUnitType.DAY) {
  847. strTimeDuration += configForm.value.timeDuration + 'D'
  848. }
  849. return strTimeDuration
  850. })
  851. // 超时最大提醒次数
  852. const cTimeoutMaxRemindCount = computed(() => {
  853. if (!configForm.value.timeoutHandlerEnable) {
  854. return undefined
  855. }
  856. if (configForm.value.timeoutHandlerType !== TimeoutHandlerType.REMINDER) {
  857. return undefined
  858. }
  859. return configForm.value.maxRemindCount
  860. })
  861. return {
  862. timeoutHandlerChange,
  863. cTimeoutType,
  864. timeoutHandlerTypeChanged,
  865. timeUnit,
  866. timeUnitChange,
  867. isoTimeDuration,
  868. cTimeoutMaxRemindCount
  869. }
  870. }
  871. </script>
  872. <style lang="scss" scoped>
  873. .button-setting-pane {
  874. display: flex;
  875. flex-direction: column;
  876. font-size: 14px;
  877. .button-setting-desc {
  878. padding-right: 8px;
  879. margin-bottom: 16px;
  880. font-size: 16px;
  881. font-weight: 700;
  882. }
  883. .button-setting-title {
  884. display: flex;
  885. justify-content: space-between;
  886. align-items: center;
  887. height: 45px;
  888. padding-left: 12px;
  889. background-color: #f8fafc0a;
  890. border: 1px solid #1f38581a;
  891. & > :first-child {
  892. width: 100px !important;
  893. text-align: left !important;
  894. }
  895. & > :last-child {
  896. text-align: center !important;
  897. }
  898. .button-title-label {
  899. width: 150px;
  900. font-size: 13px;
  901. font-weight: 700;
  902. color: #000;
  903. text-align: left;
  904. }
  905. }
  906. .button-setting-item {
  907. align-items: center;
  908. display: flex;
  909. justify-content: space-between;
  910. height: 38px;
  911. padding-left: 12px;
  912. border: 1px solid #1f38581a;
  913. border-top: 0;
  914. & > :first-child {
  915. width: 100px !important;
  916. }
  917. & > :last-child {
  918. text-align: center !important;
  919. }
  920. .button-setting-item-label {
  921. width: 150px;
  922. overflow: hidden;
  923. text-align: left;
  924. text-overflow: ellipsis;
  925. white-space: nowrap;
  926. }
  927. .editable-title-input {
  928. height: 24px;
  929. max-width: 130px;
  930. margin-left: 4px;
  931. line-height: 24px;
  932. border: 1px solid #d9d9d9;
  933. border-radius: 4px;
  934. transition: all 0.3s;
  935. &:focus {
  936. border-color: #40a9ff;
  937. outline: 0;
  938. box-shadow: 0 0 0 2px rgb(24 144 255 / 20%);
  939. }
  940. }
  941. }
  942. }
  943. </style>