handle.vue 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624
  1. <template>
  2. <div class="app-container">
  3. <div v-if="isLoadingStatus" />
  4. <div v-else>
  5. <el-card class="box-card">
  6. <div class="text item">
  7. <el-steps v-if="currentNode.clazz !== undefined && currentNode.clazz !== null && currentNode.clazz !== ''" :active="activeIndex" finish-status="success">
  8. <template v-for="(item, index) in nodeStepList">
  9. <el-step
  10. v-if="item.isHideNode === false ||
  11. item.isHideNode === undefined ||
  12. item.isHideNode == null ||
  13. item.id === processStructureValue.workOrder.current_state"
  14. :key="index"
  15. :title="item.label"
  16. />
  17. </template>
  18. </el-steps>
  19. <div v-else>
  20. <el-alert
  21. show-icon
  22. title="未找到当前工单流程信息,请确认当前工单绑定的流程是否存在。"
  23. type="warning"
  24. />
  25. </div>
  26. </div>
  27. </el-card>
  28. <el-alert
  29. v-if="activeIndex !== nodeStepList.length && processStructureValue.workOrder.is_end===1"
  30. style="margin-top: 15px"
  31. :title="alertMessage"
  32. type="error"
  33. :closable="false"
  34. />
  35. <el-card class="box-card" style="margin-top: 15px">
  36. <div slot="header" class="clearfix">
  37. <span>公共信息</span>
  38. </div>
  39. <div class="text item">
  40. <el-form label-width="100px">
  41. <el-row>
  42. <el-col :span="12">
  43. <el-form-item label="标题:" style="margin-bottom: 5px">
  44. <span>{{ processStructureValue.workOrder.title }}</span>
  45. </el-form-item>
  46. </el-col>
  47. <el-col :span="12">
  48. <el-form-item label="优先级:" style="margin-bottom: 0">
  49. <span v-if="processStructureValue.workOrder.priority===2">
  50. <el-tag type="warning">紧急</el-tag>
  51. </span>
  52. <span v-else-if="processStructureValue.workOrder.priority===3">
  53. <el-tag type="danger">非常紧急</el-tag>
  54. </span>
  55. <span v-else>
  56. <el-tag type="success">一般</el-tag>
  57. </span>
  58. </el-form-item>
  59. </el-col>
  60. </el-row>
  61. </el-form>
  62. </div>
  63. </el-card>
  64. <el-card class="box-card" style="margin-top: 15px;">
  65. <div slot="header" class="clearfix">
  66. <span>表单信息</span>
  67. <!-- {{ (currentNode.hideTpls!==undefined &&
  68. currentNode.hideTpls!==null &&
  69. currentNode.hideTpls.indexOf(tplItem.form_structure.id)!==-1) ||
  70. (currentNode.writeTpls===undefined ||
  71. currentNode.writeTpls===null ||
  72. currentNode.writeTpls.indexOf(tplItem.form_structure.id)===-1)||
  73. (isActiveProcessing && currentNode.activeOrder)? true: false}} -->
  74. </div>
  75. <div class="text item">
  76. <template v-for="(tplItem, tplIndex) in processStructureValue.tpls">
  77. <fm-generate-form
  78. v-show="currentNode.hideTpls===undefined ||
  79. currentNode.hideTpls===null ||
  80. currentNode.hideTpls.indexOf(tplItem.form_structure.id)===-1"
  81. :key="tplIndex"
  82. :ref="'generateForm-'+tplItem.id"
  83. :preview="true"
  84. :remote="remoteFunc"
  85. :value="tplItem.form_data"
  86. :data="tplItem.form_structure"
  87. :organ-list="organList"
  88. />
  89. </template>
  90. </div>
  91. <div v-if="processStructureValue.userAuthority && is_end == 0">
  92. <hr style="background-color: #d9d9d9; border:0; height:1px; margin-bottom: 15px">
  93. <el-form ref="dataFrom" label-position="left" :model="dataList" label-width="150">
  94. <el-form-item label="备注信息" prop="remarks" :rules="[{ required: true, message: '请输入备注信息', trigger: 'blur' }]">
  95. <el-input
  96. v-model="dataList.remarks"
  97. type="textarea"
  98. placeholder="请输入备注信息"
  99. maxlength="200"
  100. :autosize="{ minRows: 3, maxRows: 99}"
  101. show-word-limit
  102. />
  103. </el-form-item>
  104. <div class="text item" style="text-align: center;margin-top:18px">
  105. <el-button
  106. v-for="(item, index) in btn_group"
  107. :key="index"
  108. :type="item.className"
  109. @click="submitAction(item)" >{{ item.labelShow }}</el-button>
  110. <!-- 拒绝按钮内置 -->
  111. <el-button
  112. v-if="endNodeDetail.id"
  113. type="danger"
  114. @click="submitAction(endNodeDetail)" >{{ endNodeDetail.label }}</el-button>
  115. <!-- <div
  116. v-if="isActiveProcessing && currentNode.activeOrder"
  117. >
  118. <el-button
  119. v-permisaction="['process:list:handle:active']"
  120. type="primary"
  121. @click="activeOrderActive"
  122. >
  123. 主动接单
  124. </el-button>
  125. </div>
  126. <div v-else> -->
  127. <!-- <template v-for="(item, index) in processStructureValue.edges">
  128. <el-button
  129. v-if="processStructureValue.workOrder.is_end===0 && item.source===currentNode.id"
  130. :key="index"
  131. type="primary"
  132. @click="submitAction(item)"
  133. >
  134. {{ item.label }}
  135. </el-button>
  136. </template> -->
  137. <!-- </div> -->
  138. </div>
  139. </el-form>
  140. </div>
  141. </el-card>
  142. <el-card class="box-card" style="margin-top: 15px">
  143. <div slot="header" class="clearfix">
  144. <span>工单流转历史</span>
  145. </div>
  146. <div class="text item">
  147. <el-table
  148. :data="circulationHistoryList"
  149. border
  150. style="width: 100%"
  151. >
  152. <el-table-column
  153. prop="state"
  154. label="节点"
  155. />
  156. <el-table-column
  157. prop="circulation"
  158. label="流转"
  159. />
  160. <el-table-column
  161. prop="processor"
  162. label="处理人"
  163. />
  164. <el-table-column
  165. prop="create_time"
  166. label="处理时间"
  167. />
  168. <el-table-column
  169. prop="remarks"
  170. label="备注"
  171. />
  172. </el-table>
  173. </div>
  174. </el-card>
  175. </div>
  176. </div>
  177. </template>
  178. <script>
  179. import Vue from 'vue'
  180. import {
  181. GenerateForm
  182. } from '@/components/VueFormMaking'
  183. import 'form-making/dist/FormMaking.css'
  184. Vue.component(GenerateForm.name, GenerateForm)
  185. import {
  186. processStructure,
  187. handleWorkOrder,
  188. activeOrder,
  189. asyncPlayLog,
  190. queryUserInfo,
  191. queryAllOrgan
  192. } from '@/api/process/work-order'
  193. import store from '@/store'
  194. import { getInfo } from '@/api/user'
  195. import { listUser } from '@/api/system/sysuser'
  196. import { mapGetters } from 'vuex'
  197. export default {
  198. data() {
  199. return {
  200. isLoadingStatus: true,
  201. currentNode: {
  202. hideTpls: null,
  203. writeTpls: null
  204. },
  205. isActiveProcessing: false,
  206. tpls: [],
  207. organList: [],
  208. dataList: {
  209. remarks: '', // 备注信息
  210. },
  211. alertMessage: '',
  212. nodeStepList: [],
  213. circulationHistoryList: [],
  214. activeIndex: 0,
  215. processStructureValue: {
  216. workOrder: { title: '' }
  217. },
  218. endNodeDetail: {}, // 结束结节信息
  219. ruleForm: {
  220. title: '',
  221. process: '',
  222. classify: '',
  223. state_id: '',
  224. state: '',
  225. source_state: '',
  226. processor: '',
  227. process_method: '',
  228. tpls: [],
  229. tasks: []
  230. },
  231. userIds: null,
  232. tenantId: 1,
  233. btn_group: [],
  234. is_end: 0, // 是否结束
  235. ownerApply: false, // 是否是自己提交的申请
  236. remoteFunc: {
  237. // 获取用户列表
  238. userList(resolve) {
  239. listUser({
  240. pageSize: 999999
  241. }).then(response => {
  242. const options = response.data.list
  243. resolve(options)
  244. })
  245. }
  246. }
  247. }
  248. },
  249. computed: {
  250. ...mapGetters([
  251. 'userId'
  252. ])
  253. },
  254. async created() {
  255. await this.getUserInfo()
  256. await this.getAllOrgan()
  257. await this.getProcessNodeList()
  258. // 获取用户信息
  259. try {
  260. let user = await getInfo()
  261. this.userInfo = user.data
  262. console.log(this.processStructureValue.workOrder.creator, user.data.userId)
  263. this.ownerApply = this.processStructureValue.workOrder.creator == user.data.userId ? true : false
  264. } catch {
  265. //
  266. }
  267. },
  268. methods: {
  269. async getUserInfo() {
  270. await queryUserInfo().then(res => {
  271. console.log(res)
  272. if(res.code == 200) {
  273. this.userIds = res.data.id
  274. this.tenantId = res.data.tenantId
  275. } else {
  276. this.$message.error(res.data)
  277. }
  278. })
  279. },
  280. async getProcessNodeList() {
  281. await processStructure({
  282. processId: this.$route.query.processId,
  283. workOrderId: this.$route.query.workOrderId,
  284. userId: this.userIds
  285. }).then(response => {
  286. let tempData = response.data.tpls;
  287. console.log(response);
  288. // 获取对应模板中,下拉框的key, value
  289. let selectList = this.getSelectValueObject(tempData);
  290. console.log(selectList);
  291. // 获取对应模板中,需要隐藏的字段
  292. let hiddenFormList = this.getSelectValueObject(
  293. tempData,
  294. "hiddenForm",
  295. selectList
  296. );
  297. tempData.forEach((temp, index) => {
  298. let tempList = temp.form_structure.list || [];
  299. tempList.forEach(item => {
  300. if (hiddenFormList[index].length > 0) {
  301. if (item.type != "text" && !item.options.relationStatus) {
  302. item.hidden = true;
  303. } else {
  304. item.hidden = false;
  305. }
  306. // item.hidden = false
  307. if (hiddenFormList[index].includes(item.model)) {
  308. item.hidden = false;
  309. }
  310. } else {
  311. item.hidden = false;
  312. }
  313. // 子表单
  314. if (item.type == "subform") {
  315. let childList = item.columns || [];
  316. let subFormStatus = true;
  317. childList.forEach(child => {
  318. let childList = child.list || [];
  319. childList.forEach(c => {
  320. if (hiddenFormList[index].length > 0) {
  321. if (c.type != "text" && !c.options.relationStatus) {
  322. c.hidden = true;
  323. } else {
  324. c.hidden = false;
  325. subFormStatus = false;
  326. }
  327. if (hiddenFormList[index].includes(c.model)) {
  328. c.hidden = false;
  329. subFormStatus = false;
  330. }
  331. } else {
  332. c.hidden = false;
  333. subFormStatus = false;
  334. }
  335. });
  336. });
  337. item.hidden = subFormStatus;
  338. }
  339. });
  340. });
  341. this.isActiveProcessing = false
  342. this.processStructureValue = response.data
  343. this.circulationHistoryList = this.processStructureValue.circulationHistory
  344. // 获取当前展示节点列表
  345. this.nodeStepList = []
  346. if(this.processStructureValue.nodes) {
  347. for (var i = 0; i < this.processStructureValue.nodes.length; i++) {
  348. if (this.processStructureValue.nodes[i].id === this.processStructureValue.workOrder.current_state) {
  349. // 当前节点
  350. this.nodeStepList.push(this.processStructureValue.nodes[i])
  351. this.activeIndex = this.nodeStepList.length - 1
  352. if (i + 1 === this.processStructureValue.nodes.length) {
  353. this.activeIndex = this.nodeStepList.length
  354. }
  355. this.currentNode = this.processStructureValue.nodes[i]
  356. } else if (!this.processStructureValue.nodes[i].isHideNode) {
  357. // 非隐藏节点
  358. this.nodeStepList.push(this.processStructureValue.nodes[i])
  359. }
  360. }
  361. }
  362. // 如果回退到初始节点则可编辑。
  363. if (this.activeIndex === 0 && this.currentNode.clazz === 'start') {
  364. this.currentNode.writeTpls = []
  365. for (var tplTmp of this.processStructureValue.tpls) {
  366. this.currentNode.writeTpls.push(tplTmp.form_structure.id)
  367. }
  368. }
  369. // 判断是否需要主动处理
  370. for (var stateValue of this.processStructureValue.workOrder.state) {
  371. if (this.processStructureValue.workOrder.current_state === stateValue.id && stateValue.processor.length > 1) {
  372. this.isActiveProcessing = true
  373. break
  374. }
  375. }
  376. const nodes = this.processStructureValue.nodes
  377. for (var i = 0; i < nodes.length; i++) {
  378. // 判断节点里面是否有结束节点,而且一个流程里面只能有一个结束结点,
  379. if(nodes[i].clazz == 'end') {
  380. this.endNodeDetail = JSON.parse(JSON.stringify(nodes[i]))
  381. this.endNodeDetail.target = nodes[i].id
  382. this.endNodeDetail.flowProperties = 0 // 拒绝属性
  383. this.endNodeDetail.label = '拒绝'
  384. }
  385. }
  386. let psv = response.data.edges || []
  387. let btn_group = []
  388. psv.forEach(item => {
  389. // 过滤其它类型的操作
  390. if(this.processStructureValue.workOrder.is_end===0 && item.source===this.currentNode.id && item.flowProperties == 1) {
  391. if(item.flowProperties == 1) {
  392. item.className = 'primary'
  393. item.labelShow = '同意'
  394. } else if(item.flowProperties == 0) {
  395. item.className = 'danger'
  396. } else if(item.flowProperties == 2) {
  397. item.className = 'primary'
  398. }
  399. btn_group.push(item)
  400. } else {
  401. item.className = 'primary'
  402. }
  403. })
  404. this.btn_group = btn_group
  405. this.isLoadingStatus = false
  406. this.getAlertMessage()
  407. })
  408. },
  409. getSelectValueObject(tpls, type = "value", tplValues = []) {
  410. const tempData = tpls || [];
  411. let selectList = [];
  412. tempData.forEach((temp, index) => {
  413. let tempList = temp.form_structure.list || [];
  414. let tempSelectList = tplValues[index] || [];
  415. let listArray = [];
  416. tempList.forEach(list => {
  417. if (list.type == "select") {
  418. if (type == "value") {
  419. const result = this.getFormDataDetail(temp.form_data, list.model);
  420. if (result.status) {
  421. listArray.push(result);
  422. }
  423. } else {
  424. let selectOptions = [];
  425. let selectValue = [];
  426. tempSelectList.forEach(tsl => {
  427. if (tsl.model == list.model) {
  428. selectOptions = list.options.options || [];
  429. selectValue = tsl.value || [];
  430. }
  431. });
  432. selectOptions.forEach(so => {
  433. if (selectValue.includes(so.value)) {
  434. let tempRo = so.relationOptions || [];
  435. listArray.push(...tempRo);
  436. }
  437. });
  438. }
  439. }
  440. if (list.type == "subform") {
  441. let childList = list.columns || [];
  442. childList.forEach(child => {
  443. let childList = child.list || [];
  444. childList.forEach(c => {
  445. if (c.type == "select") {
  446. if (type == "value") {
  447. const originObj = JSON.parse(JSON.stringify(c));
  448. const result = this.getFormDataDetail(
  449. temp.form_data,
  450. originObj.model
  451. );
  452. if (result.status) {
  453. listArray.push(result);
  454. }
  455. } else {
  456. let selectOptions = [];
  457. let selectValue = [];
  458. tempSelectList.forEach(tsl => {
  459. if (tsl.model == c.model) {
  460. selectOptions = c.options.options || [];
  461. selectValue = tsl.value || [];
  462. }
  463. });
  464. selectOptions.forEach(so => {
  465. if (selectValue.includes(so.value)) {
  466. let tempRo = so.relationOptions || [];
  467. listArray.push(...tempRo);
  468. }
  469. });
  470. }
  471. }
  472. });
  473. });
  474. }
  475. });
  476. selectList.push(listArray);
  477. });
  478. return selectList;
  479. },
  480. // 获取对应元素的值
  481. getFormDataDetail(formData, model) {
  482. let modelStatus = {
  483. status: false,
  484. value: null
  485. };
  486. for (let data in formData) {
  487. if (typeof formData[data] == "object") {
  488. // 没有子表单里面有子表单
  489. for (let child in formData[data]) {
  490. if (child == model) {
  491. modelStatus = {
  492. status: true,
  493. model: child,
  494. value: formData[data][child]
  495. ? formData[data][child].split(",")
  496. : []
  497. };
  498. }
  499. }
  500. } else {
  501. if (data == model) {
  502. modelStatus = {
  503. status: true,
  504. model: data,
  505. value: formData[data] ? formData[data].split(",") : []
  506. };
  507. }
  508. }
  509. }
  510. return modelStatus;
  511. },
  512. submitAction(item) {
  513. var promiseList = []
  514. this.tpls = []
  515. for (var tpl of this.processStructureValue.tpls) {
  516. this.tpls.push({
  517. tplDataId: tpl.id,
  518. tplId: tpl.form_structure.id
  519. })
  520. promiseList.push(this.$refs['generateForm-' + tpl.id][0].getData())
  521. }
  522. this.$refs['dataFrom'].validate(_ => {
  523. if(_) {
  524. Promise.all(promiseList).then(values => {
  525. for (var tplDataIndex in this.tpls) {
  526. this.tpls[tplDataIndex].tplValue = values[tplDataIndex]
  527. }
  528. let fileList = []
  529. this.tpls && this.tpls.forEach(tpl => {
  530. for(let val in tpl.tplValue) {
  531. if(val.indexOf('file') != -1) {
  532. const file = tpl.tplValue[val] || []
  533. file.forEach(item => {
  534. fileList.push(item.url)
  535. })
  536. }
  537. }
  538. })
  539. handleWorkOrder({
  540. tasks: this.processStructureValue.process.task,
  541. source_state: this.processStructureValue.workOrder.current_state,
  542. target_state: item.target,
  543. circulation: item.label,
  544. flow_properties: item.flowProperties === undefined ? 2 : parseInt(item.flowProperties),
  545. work_order_id: parseInt(this.$route.query.workOrderId),
  546. remarks: this.dataList.remarks,
  547. tpls: this.tpls
  548. }).then(async (response) => {
  549. if (response.code === 200) {
  550. // this.$router.push({ name: 'upcoming' })
  551. await asyncPlayLog({ workOrderId: parseInt(this.$route.query.workOrderId), fileUrl: fileList.join(',') })
  552. // window.location.reload()
  553. this.getProcessNodeList()
  554. await store.dispatch('user/initUserInfo')
  555. }
  556. })
  557. })
  558. }
  559. })
  560. },
  561. // 获取提示消息
  562. getAlertMessage() {
  563. if (this.processStructureValue.workOrder.is_end === 1) {
  564. this.alertMessage = '当前工单已结束。'
  565. }
  566. },
  567. activeOrderActive() {
  568. var jsonData = [{
  569. id: this.nodeStepList[this.activeIndex].id,
  570. label: this.nodeStepList[this.activeIndex].label,
  571. process_method: 'person',
  572. processor: [this.userId]
  573. }]
  574. activeOrder(jsonData, this.$route.query.workOrderId).then(() => {
  575. this.getProcessNodeList()
  576. })
  577. },
  578. async getAllOrgan() {
  579. await queryAllOrgan({ tenantId: this.tenantId }).then(res => {
  580. if (res.code == 200) {
  581. const result = res.data;
  582. const filterOrganId = [36, 39, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 52, 54, 55, 56]
  583. let tempOrgan = []
  584. // 过滤不会显示的分部
  585. result.forEach(item => {
  586. if (!filterOrganId.includes(item.id)) {
  587. tempOrgan.push(item)
  588. }
  589. })
  590. this.organList = tempOrgan
  591. }
  592. })
  593. }
  594. }
  595. }
  596. </script>
  597. <style lang="scss" scoped>
  598. /deep/ .el-step__title {
  599. font-size: 14px;
  600. line-height: 1.3;
  601. width: 80%;
  602. padding-top: 10px;
  603. }
  604. </style>