TaskFormEdit.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570
  1. <template>
  2. <view>
  3. <u-subsection
  4. :list="procInsId?['表单信息', '流转记录']:['表单信息']"
  5. mode="button"
  6. :fontSize="16"
  7. :current="tabIndex"
  8. @change="tabSelect"
  9. ></u-subsection>
  10. <view v-show="0 === tabIndex">
  11. <scroll-view scroll-y>
  12. <view class=" bg-white ">
  13. <TestActivitiLeaveForm v-if="formUrl.endsWith('TestActivitiAuditForm')" :formReadOnly="formReadOnly" :class="formReadOnly?'readonly':''" ref="form" :businessId="businessId"></TestActivitiLeaveForm>
  14. <!-- <component :formReadOnly="formReadOnly" :class="formReadOnly?'readonly':''" ref="form" :businessId="businessId" :is="form"></component> -->
  15. <PreviewForm :formData="formData" v-if="formType !== '2'" :processDefinitionId="procDefId" :edit="true" ref="form"></PreviewForm>
  16. </view>
  17. <view class=" bg-white margin-top">
  18. <u--form :model="inputForm" labelWidth="100px" class="u-form" labelPosition="left" :rules="rules" ref="inputForm">
  19. <u-form-item v-if="!procInsId" label="流程标题" borderBottom prop="title">
  20. <u--input v-model="title" placeholder="请输入流程标题" border="none"></u--input>
  21. </u-form-item>
  22. <u-form-item v-if="taskId" label="审批意见" borderBottom prop="message">
  23. <u--textarea v-model="auditForm.message" placeholder="请输入审批意见" border="none"></u--textarea>
  24. </u-form-item>
  25. <u-form-item label="是否抄送" borderBottom prop="isCC">
  26. <u-switch v-model="isCC" ></u-switch>
  27. </u-form-item>
  28. <u-form-item label="抄送给" v-if="isCC">
  29. <user-select v-model="auditForm.userIds" ></user-select>
  30. </u-form-item>
  31. <u-form-item label="指定下一步处理者" borderBottom prop="isAssign">
  32. <u-switch v-model="isAssign" ></u-switch>
  33. </u-form-item>
  34. <u-form-item label="指定" v-if="isAssign">
  35. <user-select v-model="auditForm.assignee" ></user-select>
  36. </u-form-item>
  37. <user-select-dialog title="选择转办用户" ref="transferUserSelectDialog" :showRadio="true" :showCheckBox="false" @doSubmit="selectUsersToTransferTask"></user-select-dialog>
  38. <user-select-dialog title="选择委派用户" ref="delegateUserSelectDialog" :showRadio="true" :showCheckBox="false" @doSubmit="selectUsersToDelateTask"></user-select-dialog>
  39. <user-select-dialog title="选择加签用户" ref="addSignTaskUserSelectDialog" @doSubmit="selectUsersToAddSignTask"></user-select-dialog>
  40. <task-back-nodes ref="taskBackNodes" @getBackTaskDefKey="back"></task-back-nodes>
  41. <view class="bottom-wrap flex">
  42. <view class="flex-sub" v-show="button.isHide === '0'"
  43. v-for="(button, index) in buttons" :key="index" >
  44. <u-button type="primary" class=" buttonBox" :color="colors[index]" @click="submit(button, buttons)" :text="button.name"></u-button>
  45. </view>
  46. </view>
  47. </u--form>
  48. </view>
  49. <u-gap height="70"></u-gap>
  50. </scroll-view>
  51. </view>
  52. <view v-show="1 === tabIndex">
  53. <view class="padding bg-white">
  54. <view class="cu-timeline" :key="index" v-for="(act, index) in historicTaskList">
  55. <view class="cu-time">{{act.histIns.startTime |formatDate('MM-DD')}}</view>
  56. <view class="cu-item text-blue">
  57. <view class="content">
  58. <view class="cu-capsule radius">
  59. <view class="cu-tag bg-cyan">{{act.histIns.activityName}}</view>
  60. <!-- <view class="cu-tag line-cyan">{{act.histIns.activityName}}</view> -->
  61. </view>
  62. <view class="margin-top">
  63. 审批人 : {{act.assigneeName}}
  64. </view>
  65. <view class="margin-top">
  66. 办理状态 :<view class="cu-tag bg-blue">{{act.comment.status}}</view>
  67. </view>
  68. <view class="margin-top">
  69. 审批意见 : {{act.comment.message}}
  70. </view>
  71. <view class="margin-top">
  72. 开始时间 : {{act.histIns.startTime |formatDate}}
  73. </view>
  74. <view class="margin-top">
  75. 结束时间 : {{act.histIns.endTime |formatDate}}
  76. </view>
  77. <view class="margin-top">
  78. 用时 : {{act.durationTime || '0秒'}}
  79. </view>
  80. </view>
  81. </view>
  82. </view>
  83. </view>
  84. </view>
  85. </view>
  86. </template>
  87. <script>
  88. import userSelect from '@/components/user-select/user-select.vue'
  89. import userSelectDialog from '@/components/user-select/user-select-dialog.vue'
  90. import PreviewForm from '../form/GenerateFlowableForm'
  91. import TaskBackNodes from './components/TaskBackNodes.vue'
  92. import TestActivitiLeaveForm from '@/pages/test/activiti/TestActivitiLeaveForm.vue'
  93. import moment from 'moment'
  94. import taskService from "@/api/flowable/taskService"
  95. import formService from "@/api/flowable/formService"
  96. import processService from "@/api/flowable/processService"
  97. import flowCopyService from "@/api/flowable/flowCopyService"
  98. import taskDefExtensionService from "@/api/flowable/taskDefExtensionService"
  99. var graceChecker = require("@/common/graceChecker.js");
  100. export default {
  101. onLoad: function (option) {
  102. this.flow = JSON.parse(decodeURIComponent(option.flow));
  103. this.procDefId = this.flow.procDefId
  104. this.procDefKey = this.flow.procDefKey
  105. this.formType = this.flow.formType
  106. this.formUrl = this.flow.formUrl
  107. this.taskId = this.flow.taskId
  108. this.taskDefKey = this.flow.taskDefKey
  109. this.status = this.flow.status
  110. this.title = this.flow.formTitle
  111. this.businessId = this.flow.businessId
  112. this.procInsId = this.flow.procInsId
  113. this.formReadOnly = this.flow.formReadOnly !== undefined && this.flow.formReadOnly !== 'false' && this.flow.formReadOnly !== false
  114. this.isCC = false
  115. this.isAssign = false
  116. this.auditForm.assignee = null
  117. this.auditForm.userIds = null
  118. this.auditForm.message = ''
  119. uni.setNavigationBarTitle({
  120. title: this.title
  121. });
  122. },
  123. async mounted () {
  124. if (this.formType === '2') { //外置表单
  125. if (this.formUrl === '/404') {
  126. this.form = null
  127. uni.showToast({ title: '没有关联流程表单!', icon: "none" });
  128. } else {
  129. // uniapp 不支持动态组件,所以通过名称匹配决定调用的表单组件
  130. if(this.formUrl.endsWith('TestActivitiAuditForm')){
  131. this.form = TestActivitiLeaveForm
  132. }else{
  133. uni.showToast({ title: '没有关联流程表单!', icon: "none" });
  134. }
  135. }
  136. } else { // 动态表单
  137. // 读取流程表单
  138. if (this.formUrl === '/404') {
  139. uni.showToast({ title: '没有关联流程表单!', icon: "none" });
  140. } else {
  141. let data = await formService.getMobileForm(this.formUrl);
  142. // 初始化动态表单
  143. data.forEach((item)=>{ //挂载 writable,readable,value 属性,是为了触发对这三个属性的监听
  144. item.writable = true
  145. item.readable = true
  146. if(this.isObjectValue(item)){
  147. item.value = null
  148. }else{
  149. item.value = ''
  150. }
  151. let input = JSON.parse(JSON.stringify(item))
  152. this.formData.push(input)
  153. })
  154. // 读取启动表单配置
  155. let data2 = await formService.getHistoryTaskFormData({
  156. processInstanceId: this.procInsId,
  157. procDefId: this.procDefId,
  158. taskDefKey: this.taskDefKey,
  159. })
  160. this.setData(data2, 'start')
  161. }
  162. }
  163. // 读取按钮配置
  164. if (this.status === 'start') {
  165. this.buttons = [{code: '_flow_start', name: '启动', isHide: '0'}]
  166. } else if (this.procDefKey && this.taskDefKey) {
  167. // 读取按钮
  168. taskDefExtensionService.queryByDefIdAndTaskId({
  169. processDefId: this.procDefKey,
  170. taskDefId: this.taskDefKey
  171. }).then((data) => {
  172. this.buttons = data.flowButtonList
  173. })
  174. }
  175. // 读取历史任务列表
  176. taskService.historicTaskList(this.procInsId).then((data) => {
  177. this.historicTaskList = data.reverse()
  178. })
  179. },
  180. components:{
  181. userSelect,
  182. userSelectDialog,
  183. TestActivitiLeaveForm,
  184. TaskBackNodes,
  185. PreviewForm
  186. },
  187. data() {
  188. return {
  189. flow: null,
  190. tabIndex: 0,
  191. form: null,
  192. formType: '',
  193. formUrl: '',
  194. taskSelectedTab: 'frist',
  195. historicTaskList: [],
  196. procDefId: '',
  197. procInsId: '',
  198. formReadOnly: false,
  199. procDefKey: '',
  200. taskId: '',
  201. formData: [],
  202. taskDefKey: '',
  203. status: '',
  204. title: '',
  205. businessId: '',
  206. buttons: [],
  207. isCC: false,
  208. isAssign: false,
  209. colors: [ '#3c9cff', '#f56c6c', '#5ac725', '#f9ae3d', '#89c152',
  210. '#c38cc1', '#448aca', '#73d1f1', '#ffb34b', '#f18080',
  211. '#88a867', '#bfbf39', '#94d554', '#f19ec2', '#afaae4',
  212. '#86cefa', '#98d1ee', '#72dcdc', '#9acdcb', '#77b1cc', '#80a7dc'
  213. ],
  214. auditForm: {
  215. message: '',
  216. type: '',
  217. status: '',
  218. userIds: null,
  219. assignee: null
  220. }
  221. }
  222. },
  223. methods:{
  224. tabSelect (index) {
  225. this.tabIndex = index
  226. },
  227. // 为任务表单赋值
  228. setData (taskFormData, status) {
  229. this.formData.forEach((input)=>{
  230. let item = taskFormData.filter((item)=>{
  231. if(input.model === item.id){
  232. return true
  233. }else{
  234. return false
  235. }
  236. })[0]
  237. if(item){
  238. if(status === 'start'){
  239. this.isFixParam(input)
  240. if(this.isObjectValue(input)){
  241. if(input.options.defaultValue && typeof input.options.defaultValue=== 'string'){
  242. input.value = JSON.parse(input.options.defaultValue)
  243. }else{
  244. input.value = input.options.defaultValue
  245. }
  246. }else{
  247. input.value = input.options.defaultValue || ''
  248. }
  249. }else{
  250. if(this.isObjectValue(input)){
  251. if(item.value && typeof item.value=== 'string'){
  252. input.value = JSON.parse(item.value)
  253. }else {
  254. input.value = item.value
  255. }
  256. }else{
  257. input.value = item.value
  258. }
  259. }
  260. input.readable = item.readable
  261. input.writable = item.writable
  262. }else{
  263. input.readable = false
  264. }
  265. })
  266. },
  267. // 默认参数赋值替换 ${user.name}...
  268. isFixParam (input) {
  269. if(/^[$][{].*[}]$/.test(input.options.defaultValue)){
  270. let params = input.options.defaultValue.substring(2, input.options.defaultValue.length-1)
  271. if(params === "new Date()"){
  272. input.options.defaultValue = moment(new Date()).format('YYYY-MM-DD HH:mm:ss')
  273. }else{
  274. let value = {user: this.$store.state.user.userInfo}
  275. params.split('.').forEach((param)=>{
  276. value = value?.[param]
  277. })
  278. input.options.defaultValue = value
  279. }
  280. }
  281. },
  282. // 判断数据类型是否是非String类型
  283. isObjectValue (input) {
  284. if(input.type === 'checkbox' ||
  285. input.type === 'slider' ||
  286. input.type === 'switch' ||
  287. input.type === 'rate' ||
  288. input.type === 'imgupload' ||
  289. input.type === 'select' && input.options.multiple ||
  290. input.type === 'fileupload'){
  291. return true
  292. }
  293. return false
  294. },
  295. // 抄送
  296. cc (procInsId) {
  297. if (this.isCC && this.auditForm.userIds) {
  298. flowCopyService.save({
  299. userIds: this.auditForm.userIds,
  300. procDefId: this.procDefId,
  301. procInsId: procInsId,
  302. procDefName: '',
  303. procInsName: this.title,
  304. taskName: ''
  305. })
  306. }
  307. },
  308. // 暂存草稿
  309. save () {
  310. },
  311. // 启动流程
  312. start (vars) {
  313. if (this.formType === '2') { // 外置表单启动
  314. this.$refs.form.saveForm((businessTable, businessId) => {
  315. taskService.start({
  316. procDefKey: this.procDefKey,
  317. businessTable: businessTable,
  318. businessId: businessId,
  319. ...vars,
  320. title: this.title,
  321. assignee: this.auditForm.assignee
  322. }).then((data) => {
  323. uni.showToast({ title: "启动成功", icon: "success" });
  324. uni.navigateTo({
  325. url: '/pages/workbench/task/TodoList'
  326. })
  327. this.cc(data)
  328. })
  329. })
  330. } else { //动态表单启动
  331. this.$refs.form.submitStartFormData({
  332. processDefinitionId: this.procDefId,
  333. ...vars,
  334. title: this.title,
  335. assignee: this.auditForm.assignee
  336. }, (data) => {
  337. uni.showToast({ title: "启动成功", icon: "success" });
  338. uni.navigateTo({
  339. url: '/pages/workbench/task/TodoList'
  340. })
  341. this.cc(data)
  342. })
  343. }
  344. },
  345. // 同意
  346. agree () {
  347. this.commit() // 同意
  348. },
  349. // 驳回
  350. reject () {
  351. uni.showLoading()
  352. uni.showModal({
  353. title: '提示',
  354. content: '确定驳回流程吗?',
  355. success: (res) => {
  356. if (res.confirm) {
  357. taskService.backNodes(this.taskId).then((data) => {
  358. let backNodes = data
  359. if (backNodes.length > 0) {
  360. let backTaskDefKey = backNodes[backNodes.length - 1].taskDefKey
  361. this.back(backTaskDefKey)
  362. }
  363. })
  364. } else if (res.cancel) {
  365. uni.hideLoading()
  366. }
  367. }
  368. });
  369. },
  370. // 驳回到任意节点
  371. turnBack () {
  372. this.$refs.taskBackNodes.init(this.taskId)
  373. },
  374. // 回退到任意节点
  375. back (backTaskDefKey) {
  376. taskService.back({
  377. taskId: this.taskId,
  378. backTaskDefKey: backTaskDefKey,
  379. ...this.auditForm
  380. }).then((data) => {
  381. uni.showToast({ title: "驳回成功", icon: "success" });
  382. uni.navigateTo({
  383. url: '/pages/workbench/task/TodoList'
  384. })
  385. this.cc(data)
  386. })
  387. },
  388. // 加签
  389. addMultiInstance () {
  390. // this.$refs.addSignTaskUserSelectDialog.showModal()
  391. },
  392. selectUsersToAddSignTask (users) {
  393. let userIds = users.map((user) => {
  394. return user.id
  395. }).join(',')
  396. taskService.addSignTask({taskId: this.taskId, userIds: JSON.stringify(userIds), message: '', flag: false}).then((data) => {
  397. uni.showToast({ title: data, icon: "success" });
  398. })
  399. },
  400. // 减签
  401. delMultiInstance () {
  402. },
  403. // 转办
  404. transfer () {
  405. this.$refs.transferUserSelectDialog.showModal()
  406. },
  407. selectUsersToTransferTask (userId) {
  408. if(!userId){
  409. uni.showToast({ title: '没有选择任何用户!', icon: "none" });
  410. return
  411. }
  412. taskService.transfer(this.taskId, userId).then((data) => {
  413. uni.showToast({ title: data, icon: "success" });
  414. uni.navigateTo({
  415. url: '/pages/workbench/task/TodoList'
  416. })
  417. })
  418. },
  419. // 委托
  420. delegate () {
  421. this.$refs.delegateUserSelectDialog.showModal()
  422. },
  423. selectUsersToDelateTask (userId) {
  424. if(!userId){
  425. uni.showToast({ title: '没有选择任何用户!', icon: "none" });
  426. return
  427. }
  428. taskService.delegate(this.taskId, userId).then((data) => {
  429. uni.showToast({ title: data, icon: "success" });
  430. uni.navigateTo({
  431. url: '/pages/workbench/task/TodoList'
  432. })
  433. })
  434. },
  435. // 终止
  436. stop () {
  437. uni.showLoading()
  438. uni.showModal({
  439. title: '提示',
  440. content: '确定终止流程吗?',
  441. success: (res) => {
  442. if (res.confirm) {
  443. processService.stop(this.procInsId, this.auditForm.message).then((data) => {
  444. uni.showToast({ title: data, icon: "success" });
  445. uni.navigateTo({
  446. url: '/pages/workbench/task/TodoList'
  447. })
  448. })
  449. } else if (res.cancel) {
  450. uni.hideLoading()
  451. }
  452. }
  453. });
  454. },
  455. // 打印
  456. print () {
  457. },
  458. // 自定义按钮提交
  459. commit (vars) {
  460. //定义表单规则
  461. var rule = [
  462. {name:"message", checkType : "notnull", checkRule:"", errorMsg:"审批意见不能为空!"}
  463. ];
  464. //进行表单检查
  465. var formData = this.auditForm;
  466. var checkRes = graceChecker.check(formData, rule);
  467. if(!checkRes){
  468. uni.showToast({ title: graceChecker.error, icon: "none" });
  469. return;
  470. }
  471. if (this.formType === '2') { //外置表单审批
  472. this.$refs.form.saveForm((businessTable, businessId) => {
  473. taskService.audit({
  474. taskId: this.taskId,
  475. taskDefKey: this.taskDefKey,
  476. procInsId: this.procInsId,
  477. procDefId: this.procDefId,
  478. vars: vars,
  479. comment: this.auditForm,
  480. assignee: this.auditForm.assignee
  481. }).then((data) => {
  482. uni.showToast({ title: "审批成功", icon: "success" });
  483. uni.navigateTo({
  484. url: '/pages/workbench/task/TodoList'
  485. })
  486. this.cc(data)
  487. })
  488. })
  489. } else { // 动态表单启动
  490. this.$refs.form.submitTaskFormData(vars, this.procInsId, this.taskId, this.auditForm.assignee, this.auditForm, (data) => {
  491. uni.showToast({ title: "启动成功", icon: "success" });
  492. uni.navigateTo({
  493. url: '/pages/workbench/task/TodoList'
  494. })
  495. //抄送
  496. this.cc(data)
  497. })
  498. }
  499. },
  500. submit (currentBtn, buttons) {
  501. let vars = {} // 存储流程变量
  502. // 把当前操作对应的自定义按钮(以_flow_开头的是系统按钮,排除在外)的编码,存储为对应的流程变量,值设置为true,其余自定义按钮编码对应的流程变量值为false。
  503. buttons.forEach((btn) => {
  504. if (btn.code && !btn.code.startsWith('_flow_')) {
  505. vars[btn.code] = false
  506. }
  507. })
  508. if (currentBtn.code && !currentBtn.code.startsWith('_flow_')) {
  509. vars[currentBtn.code] = true
  510. }
  511. vars.title = this.title // 标题
  512. vars.assignee = this.auditForm.assignee // 指定的下一步骤处理人
  513. this.auditForm.type = currentBtn.code // 提交类型
  514. this.auditForm.status = currentBtn.name // 按钮文字
  515. switch (currentBtn.code) {
  516. case '_flow_start': // 自动流程
  517. this.start(vars)
  518. break
  519. case '_flow_save': // 保存草稿
  520. this.save()
  521. break
  522. case '_flow_agree': // 同意
  523. this.agree()
  524. break
  525. case '_flow_reject': // 驳回
  526. this.reject()
  527. break
  528. case '_flow_back': // 驳回到任意步骤
  529. this.turnBack()
  530. break
  531. case '_flow_add_multi_instance': // 加签
  532. this.addMultiInstance()
  533. break
  534. case '_flow_del_multi_instance': // 减签
  535. this.delMultiInstance()
  536. break
  537. case '_flow_transfer': // 转办
  538. this.transfer()
  539. break
  540. case '_flow_delegate':// 外派
  541. this.delegate()
  542. break
  543. case '_flow_stop':// 终止
  544. this.stop()
  545. break
  546. case '_flow_print':// 打印
  547. this.print()
  548. break
  549. default:
  550. this.commit(vars) // 自定义按钮提交
  551. }
  552. }
  553. }
  554. }
  555. </script>