TaskForm.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586
  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('TestActivitiLeaveForm')" :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" v-if="!procInsId || taskId">
  18. <u--form :model="auditForm" labelWidth="130px" class="u-form" labelPosition="left" :rules="rules" ref="auditForm">
  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('TestActivitiLeaveForm')){
  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. if (this.status === 'start') {
  155. // 读取启动表单配置
  156. let data = await formService.getStartFormData({processDefinitionId: this.procDefId})
  157. this.setData(data, 'start')
  158. } else {
  159. // 读取任务表单配置
  160. let data = await formService.getTaskFormData({taskId: this.taskId})
  161. this.setData(data, 'audit')
  162. }
  163. }
  164. }
  165. // 读取按钮配置
  166. if (this.status === 'start') {
  167. this.buttons = [{code: '_flow_start', name: '启动', isHide: '0'}]
  168. } else if (this.procDefKey && this.taskDefKey) {
  169. // 读取按钮
  170. taskDefExtensionService.queryByDefIdAndTaskId({
  171. processDefId: this.procDefKey,
  172. taskDefId: this.taskDefKey
  173. }).then((data) => {
  174. this.buttons = data.flowButtonList
  175. })
  176. }
  177. // 读取历史任务列表
  178. taskService.historicTaskList(this.procInsId).then((data) => {
  179. this.historicTaskList = data.reverse()
  180. })
  181. },
  182. components:{
  183. userSelect,
  184. userSelectDialog,
  185. TestActivitiLeaveForm,
  186. TaskBackNodes,
  187. PreviewForm
  188. },
  189. data() {
  190. return {
  191. flow: null,
  192. tabIndex: 0,
  193. form: null,
  194. formType: '',
  195. formUrl: '',
  196. taskSelectedTab: 'frist',
  197. historicTaskList: [],
  198. procDefId: '',
  199. procInsId: '',
  200. formReadOnly: false,
  201. procDefKey: '',
  202. taskId: '',
  203. formData: [],
  204. taskDefKey: '',
  205. status: '',
  206. title: '',
  207. businessId: '',
  208. buttons: [],
  209. isCC: false,
  210. isAssign: false,
  211. colors: [ '#3c9cff', '#f56c6c', '#5ac725', '#f9ae3d', '#89c152',
  212. '#c38cc1', '#448aca', '#73d1f1', '#ffb34b', '#f18080',
  213. '#88a867', '#bfbf39', '#94d554', '#f19ec2', '#afaae4',
  214. '#86cefa', '#98d1ee', '#72dcdc', '#9acdcb', '#77b1cc', '#80a7dc'
  215. ],
  216. auditForm: {
  217. message: '',
  218. type: '',
  219. status: '',
  220. userIds: null,
  221. assignee: null
  222. },
  223. rules: {
  224. 'title': [
  225. {
  226. required: true,
  227. message: '流程标题',
  228. trigger: ['blur', 'change']
  229. }
  230. ],
  231. 'messsage': [
  232. {
  233. required: true,
  234. message: '审批内容',
  235. trigger: ['blur', 'change']
  236. }
  237. ]
  238. }
  239. }
  240. },
  241. methods:{
  242. tabSelect (index) {
  243. this.tabIndex = index
  244. },
  245. // 为任务表单赋值
  246. setData (taskFormData, status) {
  247. this.formData.forEach((input)=>{
  248. let item = taskFormData.filter((item)=>{
  249. if(input.model === item.id){
  250. return true
  251. }else{
  252. return false
  253. }
  254. })[0]
  255. if(item){
  256. if(status === 'start'){
  257. this.isFixParam(input)
  258. if(this.isObjectValue(input)){
  259. if(input.options.defaultValue && typeof input.options.defaultValue=== 'string'){
  260. input.value = JSON.parse(input.options.defaultValue)
  261. }else{
  262. input.value = input.options.defaultValue
  263. }
  264. }else{
  265. input.value = input.options.defaultValue || ''
  266. }
  267. }else{
  268. if(this.isObjectValue(input)){
  269. if(item.value && typeof item.value=== 'string'){
  270. input.value = JSON.parse(item.value)
  271. }else {
  272. input.value = item.value
  273. }
  274. }else{
  275. input.value = item.value
  276. }
  277. }
  278. input.readable = item.readable
  279. input.writable = item.writable
  280. }else{
  281. input.readable = false
  282. }
  283. })
  284. },
  285. // 默认参数赋值替换 ${user.name}...
  286. isFixParam (input) {
  287. if(/^[$][{].*[}]$/.test(input.options.defaultValue)){
  288. let params = input.options.defaultValue.substring(2, input.options.defaultValue.length-1)
  289. if(params === "new Date()"){
  290. input.options.defaultValue = moment(new Date()).format('YYYY-MM-DD HH:mm:ss')
  291. }else{
  292. let value = {user: this.$store.state.user.userInfo}
  293. params.split('.').forEach((param)=>{
  294. value = value?.[param]
  295. })
  296. input.options.defaultValue = value
  297. }
  298. }
  299. },
  300. // 判断数据类型是否是非String类型
  301. isObjectValue (input) {
  302. if(input.type === 'checkbox' ||
  303. input.type === 'slider' ||
  304. input.type === 'switch' ||
  305. input.type === 'rate' ||
  306. input.type === 'fileupload' ||
  307. input.type === 'imgupload' ||
  308. input.type === 'select' && input.options.multiple ){
  309. return true
  310. }
  311. return false
  312. },
  313. // 抄送
  314. cc (procInsId) {
  315. if (this.isCC && this.auditForm.userIds) {
  316. flowCopyService.save({
  317. userIds: this.auditForm.userIds,
  318. procDefId: this.procDefId,
  319. procInsId: procInsId,
  320. procDefName: '',
  321. procInsName: this.title,
  322. taskName: ''
  323. })
  324. }
  325. },
  326. // 暂存草稿
  327. save () {
  328. },
  329. // 启动流程
  330. start (vars) {
  331. if (this.formType === '2') { // 外置表单启动
  332. this.$refs.form.saveForm((businessTable, businessId) => {
  333. taskService.start({
  334. procDefKey: this.procDefKey,
  335. businessTable: businessTable,
  336. businessId: businessId,
  337. ...vars,
  338. title: this.title,
  339. assignee: this.auditForm.assignee
  340. }).then((data) => {
  341. uni.showToast({ title: "启动成功", icon: "success" });
  342. uni.navigateTo({
  343. url: '/pages/workbench/task/TodoList'
  344. })
  345. this.cc(data)
  346. })
  347. })
  348. } else { //动态表单启动
  349. this.$refs.form.submitStartFormData({
  350. processDefinitionId: this.procDefId,
  351. ...vars,
  352. title: this.title,
  353. assignee: this.auditForm.assignee
  354. }, (data) => {
  355. uni.showToast({ title: "启动成功", icon: "success" });
  356. uni.navigateTo({
  357. url: '/pages/workbench/task/TodoList'
  358. })
  359. this.cc(data)
  360. })
  361. }
  362. },
  363. // 同意
  364. agree () {
  365. this.commit() // 同意
  366. },
  367. // 驳回
  368. reject () {
  369. uni.showModal({
  370. title: '提示',
  371. content: '确定驳回流程吗?',
  372. success: (res) => {
  373. if (res.confirm) {
  374. uni.showLoading()
  375. taskService.backNodes(this.taskId).then((data) => {
  376. let backNodes = data
  377. if (backNodes.length > 0) {
  378. let backTaskDefKey = backNodes[backNodes.length - 1].taskDefKey
  379. this.back(backTaskDefKey)
  380. }
  381. })
  382. } else if (res.cancel) {
  383. uni.hideLoading()
  384. }
  385. }
  386. });
  387. },
  388. // 驳回到任意节点
  389. turnBack () {
  390. this.$refs.taskBackNodes.init(this.taskId)
  391. },
  392. // 回退到任意节点
  393. back (backTaskDefKey) {
  394. taskService.back({
  395. taskId: this.taskId,
  396. backTaskDefKey: backTaskDefKey,
  397. ...this.auditForm
  398. }).then((data) => {
  399. uni.showToast({ title: "驳回成功", icon: "success" });
  400. uni.navigateTo({
  401. url: '/pages/workbench/task/TodoList'
  402. })
  403. this.cc(data)
  404. })
  405. },
  406. // 加签
  407. addMultiInstance () {
  408. // this.$refs.addSignTaskUserSelectDialog.showModal()
  409. },
  410. selectUsersToAddSignTask (users) {
  411. let userIds = users.map((user) => {
  412. return user.id
  413. }).join(',')
  414. taskService.addSignTask({taskId: this.taskId, userIds: JSON.stringify(userIds), message: '', flag: false}).then((data) => {
  415. uni.showToast({ title: data, icon: "success" });
  416. })
  417. },
  418. // 减签
  419. delMultiInstance () {
  420. },
  421. // 转办
  422. transfer () {
  423. this.$refs.transferUserSelectDialog.showModal()
  424. },
  425. selectUsersToTransferTask (userId) {
  426. alert(userId)
  427. if(!userId){
  428. uni.showToast({ title: '没有选择任何用户!', icon: "none" });
  429. return
  430. }
  431. taskService.transfer(this.taskId, userId).then((data) => {
  432. uni.showToast({ title: data, icon: "success" });
  433. uni.navigateTo({
  434. url: '/pages/workbench/task/TodoList'
  435. })
  436. })
  437. },
  438. // 委托
  439. delegate () {
  440. this.$refs.delegateUserSelectDialog.showModal()
  441. },
  442. selectUsersToDelateTask (userId) {
  443. if(!userId){
  444. uni.showToast({ title: '没有选择任何用户!', icon: "none" });
  445. return
  446. }
  447. taskService.delegate(this.taskId, userId).then((data) => {
  448. uni.showToast({ title: data, icon: "success" });
  449. uni.navigateTo({
  450. url: '/pages/workbench/task/TodoList'
  451. })
  452. })
  453. },
  454. // 终止
  455. stop () {
  456. uni.showLoading()
  457. uni.showModal({
  458. title: '提示',
  459. content: '确定终止流程吗?',
  460. success: (res) => {
  461. if (res.confirm) {
  462. processService.stop(this.procInsId, this.auditForm.message).then((data) => {
  463. uni.showToast({ title: data, icon: "success" });
  464. uni.navigateTo({
  465. url: '/pages/workbench/task/TodoList'
  466. })
  467. })
  468. } else if (res.cancel) {
  469. uni.hideLoading()
  470. }
  471. }
  472. });
  473. },
  474. // 打印
  475. print () {
  476. },
  477. // 自定义按钮提交
  478. commit (vars) {
  479. //定义表单规则
  480. var rule = [
  481. {name:"message", checkType : "notnull", checkRule:"", errorMsg:"审批意见不能为空!"}
  482. ];
  483. //进行表单检查
  484. var formData = this.auditForm;
  485. var checkRes = graceChecker.check(formData, rule);
  486. if(!checkRes){
  487. uni.showToast({ title: graceChecker.error, icon: "none" });
  488. return;
  489. }
  490. if (this.formType === '2') { //外置表单审批
  491. this.$refs.form.saveForm((businessTable, businessId) => {
  492. taskService.audit({
  493. taskId: this.taskId,
  494. taskDefKey: this.taskDefKey,
  495. procInsId: this.procInsId,
  496. procDefId: this.procDefId,
  497. vars: vars,
  498. comment: this.auditForm,
  499. assignee: this.auditForm.assignee
  500. }).then((data) => {
  501. uni.showToast({ title: "审批成功", icon: "success" });
  502. uni.navigateTo({
  503. url: '/pages/workbench/task/TodoList'
  504. })
  505. this.cc(data)
  506. })
  507. })
  508. } else { // 动态表单启动
  509. this.$refs.form.submitTaskFormData(vars, this.procInsId, this.taskId, this.auditForm.assignee, this.auditForm, (data) => {
  510. uni.showToast({ title: "启动成功", icon: "success" });
  511. uni.navigateTo({
  512. url: '/pages/workbench/task/TodoList'
  513. })
  514. //抄送
  515. this.cc(data)
  516. })
  517. }
  518. },
  519. submit (currentBtn, buttons) {
  520. let vars = {} // 存储流程变量
  521. // 把当前操作对应的自定义按钮(以_flow_开头的是系统按钮,排除在外)的编码,存储为对应的流程变量,值设置为true,其余自定义按钮编码对应的流程变量值为false。
  522. buttons.forEach((btn) => {
  523. if (btn.code && !btn.code.startsWith('_flow_')) {
  524. vars[btn.code] = false
  525. }
  526. })
  527. if (currentBtn.code && !currentBtn.code.startsWith('_flow_')) {
  528. vars[currentBtn.code] = true
  529. }
  530. vars.title = this.title // 标题
  531. vars.assignee = this.auditForm.assignee // 指定的下一步骤处理人
  532. this.auditForm.type = currentBtn.code // 提交类型
  533. this.auditForm.status = currentBtn.name // 按钮文字
  534. switch (currentBtn.code) {
  535. case '_flow_start': // 自动流程
  536. this.start(vars)
  537. break
  538. case '_flow_save': // 保存草稿
  539. this.save()
  540. break
  541. case '_flow_agree': // 同意
  542. this.agree()
  543. break
  544. case '_flow_reject': // 驳回
  545. this.reject()
  546. break
  547. case '_flow_back': // 驳回到任意步骤
  548. this.turnBack()
  549. break
  550. case '_flow_add_multi_instance': // 加签
  551. this.addMultiInstance()
  552. break
  553. case '_flow_del_multi_instance': // 减签
  554. this.delMultiInstance()
  555. break
  556. case '_flow_transfer': // 转办
  557. this.transfer()
  558. break
  559. case '_flow_delegate':// 外派
  560. this.delegate()
  561. break
  562. case '_flow_stop':// 终止
  563. this.stop()
  564. break
  565. case '_flow_print':// 打印
  566. this.print()
  567. break
  568. default:
  569. this.commit(vars) // 自定义按钮提交
  570. }
  571. }
  572. }
  573. }
  574. </script>