综合办公系统
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

index.vue 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524
  1. <template>
  2. <div class="app-container">
  3. <el-card class="box-card">
  4. <div slot="header" class="clearfix">
  5. <span class="el-icon-document">待办任务</span>
  6. <el-tag style="margin-left:10px">发起人:{{ startUser }}</el-tag>
  7. <el-tag>任务节点:{{ taskName }}</el-tag>
  8. <el-button style="float: right;" size="mini" type="danger" @click="goBack">关闭</el-button>
  9. </div>
  10. <el-tabs tab-position="top" v-model="activeName" @tab-click="handleClick">
  11. <!--表单信息-->
  12. <el-tab-pane label="表单信息" name="1">
  13. <ScTable :tableForm="tableForm" @submit="handleComplete"></ScTable>
  14. <el-row type="flex" justify="center" v-if="taskName != '员工填报'">
  15. <el-button v-if="!formKeyExist" icon="el-icon-edit-outline" type="success" size="mini"
  16. @click="handleComplete">审核通过
  17. </el-button>
  18. <el-button icon="el-icon-refresh-left" type="warning" size="mini" @click="handleReturn">退回</el-button>
  19. <!-- <el-button icon="el-icon-circle-close" type="danger" size="mini" @click="handleReject">驳回</el-button> -->
  20. </el-row>
  21. </el-tab-pane>
  22. <!--流程流转记录-->
  23. <el-tab-pane label="流转记录" name="2">
  24. <!--flowRecordList-->
  25. <el-col :span="16" :offset="4">
  26. <div class="block">
  27. <el-timeline>
  28. <el-timeline-item v-for="(item, index ) in flowRecordList" :key="index" :icon="setIcon(item.finishTime)"
  29. :color="setColor(item.finishTime)">
  30. <p style="font-weight: 700">{{ item.taskName }}</p>
  31. <el-card :body-style="{ padding: '10px' }">
  32. <el-descriptions class="margin-top" :column="1" size="small" border>
  33. <el-descriptions-item v-if="item.assigneeName" label-class-name="my-label">
  34. <template slot="label"><i class="el-icon-user"></i>办理人</template>
  35. {{ item.assigneeName }}
  36. <el-tag type="info" size="mini">{{ item.deptName }}</el-tag>
  37. </el-descriptions-item>
  38. <el-descriptions-item v-if="item.candidate" label-class-name="my-label">
  39. <template slot="label"><i class="el-icon-user"></i>候选办理</template>
  40. {{ item.candidate }}
  41. </el-descriptions-item>
  42. <el-descriptions-item label-class-name="my-label">
  43. <template slot="label"><i class="el-icon-date"></i>接收时间</template>
  44. {{ item.createTime }}
  45. </el-descriptions-item>
  46. <el-descriptions-item v-if="item.finishTime" label-class-name="my-label">
  47. <template slot="label"><i class="el-icon-date"></i>处理时间</template>
  48. {{ item.finishTime }}
  49. </el-descriptions-item>
  50. <el-descriptions-item v-if="item.duration" label-class-name="my-label">
  51. <template slot="label"><i class="el-icon-time"></i>耗时</template>
  52. {{ item.duration }}
  53. </el-descriptions-item>
  54. <el-descriptions-item v-if="item.comment" label-class-name="my-label">
  55. <template slot="label"><i class="el-icon-tickets"></i>处理意见</template>
  56. {{ item.comment }}
  57. </el-descriptions-item>
  58. </el-descriptions>
  59. </el-card>
  60. </el-timeline-item>
  61. </el-timeline>
  62. </div>
  63. </el-col>
  64. </el-tab-pane>
  65. <!--流程图-->
  66. <el-tab-pane label="流程图" name="3">
  67. <flow :flowData="flowData" />
  68. </el-tab-pane>
  69. </el-tabs>
  70. <!--审批任务-->
  71. <el-dialog :title="completeTitle" :visible.sync="completeOpen" width="60%" append-to-body>
  72. <el-form ref="taskForm" :model="taskForm">
  73. <el-form-item prop="targetKey">
  74. <flow-user v-if="checkSendUser" :checkType="checkType" @handleUserSelect="handleUserSelect"></flow-user>
  75. <flow-role v-if="checkSendRole" @handleRoleSelect="handleRoleSelect"></flow-role>
  76. </el-form-item>
  77. <!-- <el-form-item label="处理意见" label-width="80px" prop="comment"
  78. :rules="[{ required: true, message: '请输入处理意见', trigger: 'blur' }]">
  79. <el-input type="textarea" v-model="taskForm.comment" placeholder="请输入处理意见" />
  80. </el-form-item> -->
  81. </el-form>
  82. <span slot="footer" class="dialog-footer">
  83. <el-button @click="completeOpen = false">取 消</el-button>
  84. <el-button type="primary" @click="taskComplete">确 定</el-button>
  85. </span>
  86. </el-dialog>
  87. <!--退回流程-->
  88. <el-dialog :title="returnTitle" :visible.sync="returnOpen" width="40%" append-to-body>
  89. <el-form ref="taskForm" :model="taskForm" label-width="80px">
  90. <el-form-item label="退回节点" prop="targetKey">
  91. <el-radio-group v-model="taskForm.targetKey">
  92. <el-radio-button v-for="item in returnTaskList" :key="item.id" :label="item.id">{{ item.name }}
  93. </el-radio-button>
  94. </el-radio-group>
  95. </el-form-item>
  96. <el-form-item label="退回意见" prop="comment" :rules="[{ required: true, message: '请输入意见', trigger: 'blur' }]">
  97. <el-input style="width: 50%" type="textarea" v-model="taskForm.comment" placeholder="请输入意见" />
  98. </el-form-item>
  99. </el-form>
  100. <span slot="footer" class="dialog-footer">
  101. <el-button @click="returnOpen = false">取 消</el-button>
  102. <el-button type="primary" @click="taskReturn">确 定</el-button>
  103. </span>
  104. </el-dialog>
  105. <!--驳回流程-->
  106. <el-dialog :title="rejectTitle" :visible.sync="rejectOpen" width="60%" append-to-body>
  107. <el-form ref="taskForm" :model="taskForm">
  108. <el-form-item prop="targetKey">
  109. <flow-user v-if="checkSendUser" :checkType="checkType" @handleUserSelect="handleUserSelect"></flow-user>
  110. </el-form-item>
  111. <el-form-item label="驳回意见" prop="comment" :rules="[{ required: true, message: '请输入意见', trigger: 'blur' }]">
  112. <el-input style="width: 50%" type="textarea" v-model="taskForm.comment" placeholder="请输入意见" />
  113. </el-form-item>
  114. </el-form>
  115. <span slot="footer" class="dialog-footer">
  116. <el-button @click="rejectOpen = false">取 消</el-button>
  117. <el-button type="primary" @click="taskReject">确 定</el-button>
  118. </span>
  119. </el-dialog>
  120. </el-card>
  121. </div>
  122. </template>
  123. <script>
  124. import { flowRecord } from "@/api/flowable/finished";
  125. import FlowUser from '@/components/flow/User'
  126. import FlowRole from '@/components/flow/Role'
  127. import Parser from '@/components/parser/Parser'
  128. import { getProcessVariables, flowXmlAndNode, definitionStart } from "@/api/flowable/definition";
  129. import {
  130. complete,
  131. rejectTask,
  132. returnList,
  133. returnTask,
  134. getNextFlowNode,
  135. delegate,
  136. flowTaskForm,
  137. } from "@/api/flowable/todo";
  138. import flow from '@/views/flowable/task/todo/detail/flow'
  139. import "@riophae/vue-treeselect/dist/vue-treeselect.css";
  140. import { listUser } from "@/api/system/user";
  141. import { getAssess, modifyAssess, submitAssess } from '@/api/oa/assess/assess';
  142. import ScTable from "../../../form/scTable.vue";
  143. export default {
  144. name: "Record",
  145. components: {
  146. Parser,
  147. flow,
  148. FlowUser,
  149. FlowRole,
  150. ScTable
  151. },
  152. props: {},
  153. data() {
  154. return {
  155. // 模型xml数据
  156. xmlData: "",
  157. flowData: {},
  158. activeName: '1',
  159. // 部门名称
  160. deptName: undefined,
  161. // 部门树选项
  162. // 用户表格数据
  163. userList: null,
  164. defaultProps: {
  165. children: "children",
  166. label: "label"
  167. },
  168. // 查询参数
  169. queryParams: {
  170. deptId: undefined
  171. },
  172. // 遮罩层
  173. loading: true,
  174. flowRecordList: [], // 流程流转数据
  175. formConfCopy: {},
  176. src: null,
  177. rules: {}, // 表单校验
  178. variablesForm: {}, // 流程变量数据
  179. taskForm: {
  180. returnTaskShow: false, // 是否展示回退表单
  181. delegateTaskShow: false, // 是否展示回退表单
  182. defaultTaskShow: true, // 默认处理
  183. comment: "", // 意见内容
  184. procInsId: "", // 流程实例编号
  185. instanceId: "", // 流程实例编号
  186. deployId: "", // 流程定义编号
  187. taskId: "",// 流程任务编号
  188. procDefId: "", // 流程编号
  189. targetKey: "",
  190. variables: {
  191. variables: {}
  192. },
  193. },
  194. assignee: null,
  195. formConf: {}, // 默认表单数据
  196. variables: [], // 流程变量数据
  197. variablesData: {}, // 流程变量数据
  198. returnTaskList: [], // 回退列表数据
  199. completeTitle: null,
  200. completeOpen: false,
  201. returnTitle: null,
  202. returnOpen: false,
  203. rejectOpen: false,
  204. rejectTitle: null,
  205. userData: [],
  206. checkSendUser: false, // 是否展示人员选择模块
  207. checkSendRole: false,// 是否展示角色选择模块
  208. checkType: 'single', // 选择类型
  209. taskName: null, // 任务节点
  210. startUser: null, // 发起人信息,
  211. multiInstanceVars: '', // 会签节点
  212. formKeyExist: false, // 当前节点是否存在表单
  213. // 表单信息
  214. tableForm: {}
  215. };
  216. },
  217. created() {
  218. if (this.$route.query) {
  219. this.taskName = this.$route.query.taskName;
  220. this.startUser = this.$route.query.startUser;
  221. this.taskForm.deployId = this.$route.query.deployId;
  222. this.taskForm.taskId = this.$route.query.taskId;
  223. this.taskForm.procInsId = this.$route.query.procInsId;
  224. this.taskForm.executionId = this.$route.query.executionId;
  225. this.taskForm.instanceId = this.$route.query.procInsId;
  226. this.taskForm.formId = this.$route.query.formId;
  227. // 流程任务获取变量信息
  228. if (this.taskForm.taskId) {
  229. this.processVariables(this.taskForm.taskId)
  230. this.getFlowTaskForm(this.taskForm.taskId)
  231. this.getAssessByAssessId(this.taskForm.formId)
  232. }
  233. this.getFlowRecordList(this.taskForm.procInsId, this.taskForm.deployId);
  234. }
  235. },
  236. methods: {
  237. handleClick(tab, event) {
  238. if (tab.name === '3') {
  239. flowXmlAndNode({ procInsId: this.taskForm.procInsId, deployId: this.taskForm.deployId }).then(res => {
  240. this.flowData = res.data;
  241. })
  242. }
  243. },
  244. setIcon(val) {
  245. if (val) {
  246. return "el-icon-check";
  247. } else {
  248. return "el-icon-time";
  249. }
  250. },
  251. setColor(val) {
  252. if (val) {
  253. return "#2bc418";
  254. } else {
  255. return "#b3bdbb";
  256. }
  257. },
  258. // 用户信息选中数据
  259. handleUserSelect(selection) {
  260. if (selection) {
  261. if (selection instanceof Array) {
  262. const selectVal = selection.map(item => item.userId);
  263. if (this.multiInstanceVars) {
  264. this.$set(this.taskForm.variables, this.multiInstanceVars, selectVal);
  265. } else {
  266. this.$set(this.taskForm.variables, "approval", selectVal.join(','));
  267. }
  268. } else {
  269. this.$set(this.taskForm.variables, "approval", selection.userId.toString());
  270. }
  271. }
  272. },
  273. // 角色信息选中数据
  274. handleRoleSelect(selection) {
  275. if (selection) {
  276. if (selection instanceof Array) {
  277. const selectVal = selection.map(item => item.roleId);
  278. this.$set(this.taskForm.variables, "approval", selectVal.join(','));
  279. } else {
  280. this.$set(this.taskForm.variables, "approval", selection);
  281. }
  282. }
  283. },
  284. /** 流程流转记录 */
  285. getFlowRecordList(procInsId, deployId) {
  286. const that = this
  287. const params = { procInsId: procInsId, deployId: deployId }
  288. flowRecord(params).then(res => {
  289. that.flowRecordList = res.data.flowList;
  290. }).catch(res => {
  291. this.goBack();
  292. })
  293. },
  294. fillFormData(form, data) {
  295. form.fields.forEach(item => {
  296. const val = data[item.__vModel__]
  297. if (val) {
  298. item.__config__.defaultValue = val
  299. }
  300. })
  301. },
  302. /** 获取流程变量内容 */
  303. processVariables(taskId) {
  304. if (taskId) {
  305. // 提交流程申请时填写的表单存入了流程变量中后续任务处理时需要展示
  306. getProcessVariables(taskId).then(res => {
  307. // this.variablesData = res.data.variables;
  308. });
  309. }
  310. },
  311. /** 流程节点表单 */
  312. getFlowTaskForm(taskId) {
  313. if (taskId) {
  314. // 提交流程申请时填写的表单存入了流程变量中后续任务处理时需要展示
  315. flowTaskForm({ taskId: taskId }).then(res => {
  316. this.variablesData = res.data.formData;
  317. this.taskForm.variables = res.data.formData;
  318. this.formKeyExist = res.data.formKeyExist;
  319. });
  320. }
  321. },
  322. // 获取需要处理的表单表格信息
  323. getAssessByAssessId(formId) {
  324. getAssess(formId).then(res => {
  325. let data = res.data;
  326. for (let d in data) {
  327. if (d.includes('role')) {
  328. data[d] = data[d].split(',')
  329. }
  330. if (d.includes('familiar')) {
  331. data[d] = parseInt(data[d])
  332. }
  333. }
  334. this.tableForm = data;
  335. this.tableForm.taskId = this.$route.query.taskId;
  336. this.tableForm.taskName = this.$route.query.taskName;
  337. })
  338. },
  339. /** 加载审批任务弹框 */
  340. handleComplete() {
  341. // this.completeOpen = true;
  342. // this.completeTitle = "流程审批";
  343. // this.submitForm(null);
  344. let formData = new FormData();
  345. let form = JSON.stringify(this.tableForm);
  346. formData.append("form", form);
  347. this.submitForm(formData);
  348. },
  349. /** 用户审批任务 */
  350. taskComplete() {
  351. if (!this.taskForm.variables && this.checkSendUser) {
  352. this.$modal.msgError("请选择流程接收人员!");
  353. return;
  354. }
  355. if (!this.taskForm.variables && this.checkSendRole) {
  356. this.$modal.msgError("请选择流程接收角色组!");
  357. return;
  358. }
  359. // if (!this.taskForm.comment) {
  360. // this.$modal.msgError("请输入审批意见!");
  361. // return;
  362. // }
  363. if (this.taskForm && this.formKeyExist) {
  364. // 表单是否禁用
  365. this.taskForm.formData.formData.disabled = true;
  366. // 是否显示按钮
  367. this.taskForm.formData.formData.formBtns = false;
  368. this.taskForm.variables = Object.assign({}, this.taskForm.variables, this.taskForm.formData.valData);
  369. this.taskForm.variables.variables = this.taskForm.formData.formData;
  370. complete(this.taskForm).then(response => {
  371. this.$modal.msgSuccess(response.msg);
  372. this.goBack();
  373. });
  374. } else {
  375. // 流程设计人员类型配置为固定人员接收任务时,直接提交任务到下一步
  376. complete(this.taskForm).then(response => {
  377. this.$modal.msgSuccess(response.msg);
  378. this.goBack();
  379. });
  380. }
  381. },
  382. /** 委派任务 */
  383. handleDelegate() {
  384. this.taskForm.delegateTaskShow = true;
  385. this.taskForm.defaultTaskShow = false;
  386. },
  387. handleAssign() {
  388. },
  389. /** 返回页面 */
  390. goBack() {
  391. // 关闭当前标签页并返回上个页面
  392. const obj = { path: "/task/todo", query: { t: Date.now() } };
  393. this.$tab.closeOpenPage(obj);
  394. },
  395. /** 驳回任务 */
  396. handleReject() {
  397. this.rejectOpen = true;
  398. this.rejectTitle = "驳回流程";
  399. },
  400. /** 驳回任务 */
  401. taskReject() {
  402. this.$refs["taskForm"].validate(valid => {
  403. if (valid) {
  404. rejectTask(this.taskForm).then(res => {
  405. this.$modal.msgSuccess(res.msg);
  406. this.goBack();
  407. });
  408. }
  409. });
  410. },
  411. /** 可退回任务列表 */
  412. handleReturn() {
  413. this.returnOpen = true;
  414. this.returnTitle = "退回流程";
  415. returnList(this.taskForm).then(res => {
  416. this.returnTaskList = res.data;
  417. this.taskForm.variables = null;
  418. })
  419. },
  420. /** 提交退回任务 */
  421. taskReturn() {
  422. this.$refs["taskForm"].validate(valid => {
  423. if (valid) {
  424. returnTask(this.taskForm).then(res => {
  425. this.$modal.msgSuccess(res.msg);
  426. this.goBack()
  427. });
  428. }
  429. });
  430. },
  431. /** 取消回退任务按钮 */
  432. cancelTask() {
  433. this.taskForm.returnTaskShow = false;
  434. this.taskForm.defaultTaskShow = true;
  435. this.returnTaskList = [];
  436. },
  437. /** 委派任务 */
  438. submitDeleteTask() {
  439. this.$refs["taskForm"].validate(valid => {
  440. if (valid) {
  441. delegate(this.taskForm).then(response => {
  442. this.$modal.msgSuccess(response.msg);
  443. this.goBack();
  444. });
  445. }
  446. });
  447. },
  448. /** 取消回退任务按钮 */
  449. cancelDelegateTask() {
  450. this.taskForm.delegateTaskShow = false;
  451. this.taskForm.defaultTaskShow = true;
  452. this.returnTaskList = [];
  453. },
  454. /** 申请流程表单数据提交 */
  455. submitForm(formData) {
  456. modifyAssess(formData).then(res => {
  457. });
  458. if (this.taskForm.variables === null) {
  459. this.taskForm.variables = {};
  460. }
  461. // 根据当前任务或者流程设计配置的下一步节点 todo 暂时未涉及到考虑网关、表达式和多节点情况
  462. const params = { taskId: this.taskForm.taskId }
  463. getNextFlowNode(params).then(res => {
  464. const data = res.data;
  465. this.taskForm.formData = formData;
  466. if (data) {
  467. if (data.dataType === 'dynamic') {
  468. if (data.type === 'assignee') { // 指定人员
  469. this.checkSendUser = true;
  470. this.checkType = "single";
  471. } else if (data.type === 'candidateUsers') { // 候选人员(多个)
  472. this.checkSendUser = true;
  473. this.checkType = "multiple";
  474. } else if (data.type === 'candidateGroups') { // 指定组(所属角色接收任务)
  475. this.checkSendRole = true;
  476. } else { // 会签
  477. // 流程设计指定的 elementVariable 作为会签人员列表
  478. this.multiInstanceVars = data.vars;
  479. this.checkSendUser = true;
  480. this.checkType = "multiple";
  481. }
  482. }
  483. }
  484. this.completeOpen = true;
  485. this.completeTitle = "选择下一节点审核人";
  486. })
  487. },
  488. },
  489. };
  490. </script>
  491. <style lang="scss" scoped>
  492. .test-form {
  493. margin: 15px auto;
  494. width: 800px;
  495. padding: 15px;
  496. }
  497. .clearfix:before,
  498. .clearfix:after {
  499. display: table;
  500. content: "";
  501. }
  502. .clearfix:after {
  503. clear: both
  504. }
  505. .box-card {
  506. width: 100%;
  507. margin-bottom: 20px;
  508. }
  509. .el-tag+.el-tag {
  510. margin-left: 10px;
  511. }
  512. .my-label {
  513. background: #E1F3D8;
  514. }
  515. </style>