detail.vue 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620
  1. <template>
  2. <div class="app-container">
  3. <div class="title-container">
  4. <breadcrumb id="breadcrumb-container" class="breadcrumb-container"/>
  5. </div>
  6. <y-detail-page-layout @save="saveFlow" :edit-status="true" v-loading="vLoading" element-loading-text="处理中。。。" >
  7. <div style="padding-top: 30px;">
  8. <el-form ref="workflow" :model="workflow" class="form-container">
  9. <div class="createPost-main-container">
  10. <div class="postInfo-container">
  11. <div style="padding-bottom:20px">
  12. <el-divider content-position="left">
  13. <h3 class="title">
  14. <div class="avatar-wrapper icon-title">flow</div>
  15. <div class="icon-info">工作流</div>
  16. </h3>
  17. </el-divider>
  18. <div style="padding-left:80px;padding-top:20px">
  19. <el-row>
  20. <el-col :xs="24" :sm="12" :lg="6" :span="6">
  21. <el-form-item
  22. label="名称:"
  23. prop="name"
  24. :rules="{required: true, message: '工作流名称不能为空', trigger: 'blur'}"
  25. label-width="120px"
  26. class="postInfo-container-item"
  27. >
  28. <el-select
  29. v-model="workflow.name"
  30. placeholder="若未找到工作流名称,请联系管理员。"
  31. clearable
  32. filterable
  33. class="filter-item"
  34. style="width: 100%"
  35. @change="selectFlow"
  36. >
  37. <el-option v-for="(item, index) in flows" :key="index" :label="item.name" :value="item.name"/>
  38. </el-select>
  39. </el-form-item>
  40. </el-col>
  41. <el-col :xs="24" :sm="12" :lg="6" :span="6">
  42. <el-form-item
  43. label="编码:"
  44. prop="code"
  45. :rules="{required: true, message: '工作流编码不能为空', trigger: 'blur'}"
  46. label-width="120px"
  47. class="postInfo-container-item"
  48. >
  49. <el-input :value="workflow.code" class="filter-item" disabled readonly/>
  50. </el-form-item>
  51. </el-col>
  52. <el-col :xs="24" :sm="12" :lg="6" :span="6">
  53. <el-form-item
  54. label="状态:"
  55. prop="state"
  56. label-width="120px"
  57. class="postInfo-container-item"
  58. >
  59. <el-switch
  60. style="display: block;margin-top:4px"
  61. v-model="workflow.state"
  62. active-color="#13ce66"
  63. inactive-color="#ff4949"
  64. active-text="启用"
  65. inactive-text="禁用"
  66. active-value="false" inactive-value="true"
  67. :width=50>
  68. </el-switch>
  69. </el-form-item>
  70. </el-col>
  71. </el-row>
  72. </div>
  73. </div>
  74. <div style="padding-bottom:20px">
  75. <el-divider content-position="left">
  76. <h3 class="title">
  77. <div class="avatar-wrapper icon-title" style="background:rgba(255,175,41,1)">node</div>
  78. <div class="icon-info">
  79. 流程节点
  80. </div>
  81. </h3>
  82. </el-divider>
  83. <div>
  84. <el-button :type="editing==false?'info':'success'" round style="float:right;margin-right:20px" @click="toEditNoe">编辑节点</el-button>
  85. </div>
  86. <div class="node-area" >
  87. <div class="node-seq">
  88. <WrokflowNode name="开始" state="START" />
  89. <div class="add-icom" v-if="editing">
  90. <img src="../../../assets/images/add.png" alt="" class="add-node-icom" @click="openDialog(null)"/>
  91. </div>
  92. </div>
  93. <div class="node-seq" v-for="(n,index) in nodes" :key="index">
  94. <WrokflowNode :key="index" :name="n.name" :state="editing==true?'EDITING':''" @openCurrentNodeDialog="openCurrentNodeDialog(n)" />
  95. <div class="add-icom" v-if="editing">
  96. <img src="../../../assets/images/add.png" alt="" class="add-node-icom" @click="openDialog(n)"/>
  97. </div>
  98. </div>
  99. <div class="node-seq">
  100. <WrokflowNode name="结束" state="END" :last=true />
  101. </div>
  102. </div>
  103. </div>
  104. </div>
  105. </div>
  106. </el-form>
  107. </div>
  108. </y-detail-page-layout>
  109. <el-dialog width="700px" :visible.sync="dialogFormVisible"
  110. custom-class="editNodeDialog" :before-close="cleanDialog">
  111. <el-form ref="nodeForm" :model="editNode" label-position="right" label-width="110px"
  112. style="width: 520px; margin-left:50px;">
  113. <el-form-item label="流程节点:" prop="name" :rules="{ required: true, message: '请填写节点名称', trigger: 'blur' }">
  114. <el-select
  115. v-model="editNode.code"
  116. placeholder="请选择"
  117. clearable
  118. filterable
  119. class="filter-item"
  120. style="width: 100%"
  121. >
  122. <el-option v-for="(item, index) in allNodes" :key="index" :label="item.name" :value="item.code"/>
  123. </el-select>
  124. </el-form-item>
  125. <el-form-item label="节点类型:" prop="type" :rules="{ required: true, message: '请选择节点类型', trigger: 'blur' }">
  126. <el-select
  127. v-model="editNode.type"
  128. placeholder="请选择"
  129. clearable
  130. filterable
  131. class="filter-item"
  132. style="width: 100%"
  133. >
  134. <el-option key="1" label="任务节点" value="TASK"/>
  135. <el-option key="2" label="状态节点" value="STATE"/>
  136. </el-select>
  137. </el-form-item>
  138. <el-form-item label="校验业务数据:" prop="verify" >
  139. <el-select
  140. v-model="editNode.verify"
  141. placeholder="请选择"
  142. clearable
  143. filterable
  144. class="filter-item"
  145. style="width: 100%"
  146. >
  147. <el-option key="1" label="评估对象价值校验(大中型)" value="CHECK_PRODUCTION_CHECK"/>
  148. <el-option key="2" label="出具产品类型校验(大中型)" value="CHECK_PRODUCTION_TYPE"/>
  149. <el-option key="3" label="产品已收款校验(大中型)" value="CHECK_PRODUCTION_FUND"/>
  150. <el-option key="4" label="报价反馈节点校验(个贷)" value="CHECK_FEEDBACK"/>
  151. <el-option key="5" label="出具产品类型校验(个贷)" value="CHECK_PERSONAL_PRODUCTION_TYPE"/>
  152. <el-option key="6" label="客户类型校验(个贷)" value="CHECK_PERSONAL_CLIENT_TYPE"/>
  153. <el-option key="7" label="出具产品类型校验(资产)" value="CHECK_ASSET_PRODUCTION_TYPE"/>
  154. <el-option key="8" label="报告四审校验(资产)" value="CHECK_ASSET_FOURTH_CHECK_REPORT"/>
  155. <el-option key="9" label="产品认领实收款校验(资产)" value="CHECK_ASSET_ORDER_FUND"/>
  156. <el-option key="10" label="土地报告备案校验(大中型)" value="CHECK_LAND_REPORT_IS_RECORD"/>
  157. </el-select>
  158. </el-form-item>
  159. <el-form-item label="节点任务:" prop="tasks">
  160. <el-checkbox-group v-model="editNode.taskItems" :disabled = "editNode.type === null || editNode.type ==='STATE'" >
  161. <el-checkbox v-for="(t,index) in taksEnums" :key="index" :label="t.description" name="tasks" @change="addToTasks(t)">{{t.description}}
  162. </el-checkbox>
  163. </el-checkbox-group>
  164. </el-form-item>
  165. <el-form-item :label="t.description+':'" :prop="t.description" v-for="(t,index) in editNode.tasks" :key="index">
  166. <el-select
  167. v-model="editNode.tasks[index].businessHandle"
  168. placeholder="业务操作"
  169. class="filter-item"
  170. style="width: 27%"
  171. >
  172. <el-option label="需要业务操作" :value=true />
  173. <el-option label="不需要业务操作" :value=false />
  174. </el-select>
  175. <el-select
  176. v-model="editNode.tasks[index].name"
  177. placeholder="处理人类型"
  178. clearable
  179. filterable
  180. class="filter-item"
  181. style="width: 27% ;margin-left:10px"
  182. @change="getOptions(editNode.tasks[index].name,editNode.tasks[index])"
  183. >
  184. <el-option v-for="(permission, index) in permissionEnum" :key="index" :label="permission.name" :value="permission.code"/>
  185. </el-select>
  186. <el-select
  187. v-if="editNode.tasks[index].name === 'EMPLOYEE'"
  188. v-model="editNode.tasks[index].powerId"
  189. placeholder="请选择员工"
  190. clearable
  191. filterable
  192. class="filter-item"
  193. style="width: 41%;margin-left:10px"
  194. >
  195. <el-option v-for="(user, index) in allUser" :key="index" :label="user.name" :value="user.id"/>
  196. </el-select>
  197. <el-select
  198. v-if="editNode.tasks[index].name === 'POST'"
  199. v-model="editNode.tasks[index].powerId"
  200. placeholder="请选择岗位"
  201. clearable
  202. filterable
  203. class="filter-item"
  204. style="width: 41%;margin-left:10px"
  205. >
  206. <el-option v-for="(post, index) in allPost" :key="index" :label="post.name" :value="post.id"/>
  207. </el-select>
  208. </el-form-item>
  209. <el-form-item label="限时完成:" prop="deadline">
  210. <el-input
  211. v-model.number="editNode.deadlineNumber"
  212. placeholder="请输入内容"
  213. >
  214. <el-select slot="append" v-model="editNode.deadlineType" style="width: 130px;">
  215. <el-option label="小时" value="HOUR" ></el-option>
  216. <el-option label="分钟" value="MINUTE"></el-option>
  217. </el-select>
  218. </el-input>
  219. </el-form-item>
  220. <el-form-item label="可退回:" >
  221. <el-switch v-model="editNode.reversible" />
  222. <div style="float:right;margin-top:-10px;margin-left:3px">
  223. <el-alert
  224. title="当前节点的审批人可以将流程退回到上一个节点。"
  225. type="info"
  226. :closable="false"
  227. show-icon>
  228. </el-alert>
  229. </div>
  230. </el-form-item>
  231. <el-form-item label="可重置:" >
  232. <el-switch v-model="editNode.restartable" />
  233. <div style="float:right;margin-top:-10px;margin-left:3px">
  234. <el-alert
  235. title="当前节点的审批人可以将流程退回到第一个节点。"
  236. type="info"
  237. :closable="false"
  238. show-icon>
  239. </el-alert>
  240. </div>
  241. </el-form-item>
  242. <el-form-item label="可跳过:" >
  243. <el-switch v-model="editNode.skippable" />
  244. <div style="float:right;margin-top:-10px;margin-right:12px">
  245. <el-alert
  246. title="当前节点允许审批人不处理,进入下一个节点。"
  247. type="info"
  248. :closable="false"
  249. show-icon>
  250. </el-alert>
  251. </div>
  252. </el-form-item>
  253. <el-form-item label="可终止:" >
  254. <el-switch v-model="editNode.terminable" />
  255. <div style="float:right;margin-top:-10px;margin-right:78px">
  256. <el-alert
  257. title="当前节点的审批人可以将流程终止。"
  258. type="info"
  259. :closable="false"
  260. show-icon>
  261. </el-alert>
  262. </div>
  263. </el-form-item>
  264. <el-form-item label="任务描述:">
  265. <el-input
  266. v-model="editNode.info" type="textarea"
  267. placeholder="请输入内容"
  268. />
  269. </el-form-item>
  270. </el-form>
  271. <div slot="footer" style="text-align:center;">
  272. <el-button v-if="editNode.id" type="danger" @click="delNode" style="width:520px;margin-bottom:10px" :disabled="!editing">删除</el-button>
  273. <el-button type="success" @click="saveNode" style="width:520px" :disabled="!editing">保存</el-button>
  274. </div>
  275. </el-dialog>
  276. </div>
  277. </template>
  278. <script>
  279. import Breadcrumb from '@/components/Breadcrumb';
  280. import WrokflowNode from '@/components/workflowNode'
  281. export default {
  282. name: 'workflowDetail',
  283. components: {
  284. Breadcrumb,
  285. WrokflowNode
  286. },
  287. data() {
  288. return {
  289. dialogFormVisible:false,
  290. vLoading: false,
  291. editing:false,
  292. postForm:{},
  293. nodes:[
  294. {
  295. flowId:null,
  296. priviousNodeId:null,
  297. code:null,
  298. type:null,
  299. name:"",
  300. tasks:[],
  301. deadlineNumber:null,
  302. deadlineType:null,
  303. deadline:null,
  304. taskItems:[]
  305. }
  306. ],
  307. allNodes:[],
  308. editNode:{
  309. flowId:null,
  310. priviousNodeId:null,
  311. code:null,
  312. type:null,
  313. tasks:[],
  314. deadlineNumber:null,
  315. deadlineType:"MINUTE",
  316. deadline:null,
  317. taskItems:[],
  318. info:null,
  319. name: null
  320. },
  321. taksEnums:[
  322. {
  323. description:null,
  324. taskType:null,
  325. businessHandle:null
  326. }
  327. ],
  328. workflow:{
  329. id:null,
  330. code:null,
  331. state:null
  332. },
  333. permissionEnum:[
  334. {
  335. name:null,
  336. code:null
  337. }
  338. ],
  339. allUser:[],
  340. allPost:[],
  341. flows:[]
  342. }
  343. },
  344. created() {
  345. this.workflow = this.$route.query;
  346. this.$route.query.back = "/setting/workflow/list";
  347. this.getNodeList();
  348. this.getFlowsEnum();
  349. },
  350. methods: {
  351. openDialog(node){
  352. this.getNodeEnum();
  353. this.getAllTaskEnum();
  354. this.getAllPermissionEnum();
  355. if (node!=null && node.id!=null){
  356. this.editNode.priviousNodeId = node.id
  357. }
  358. this.dialogFormVisible = true;
  359. },
  360. openCurrentNodeDialog(node){
  361. this.getNodeEnum();
  362. this.getAllTaskEnum();
  363. this.getAllPermissionEnum();
  364. this.getOptions("POST",null);
  365. this.getOptions("EMPLOYEE",null)
  366. this.editNode = node;
  367. this.dialogFormVisible = true;
  368. },
  369. getFlowsEnum(){
  370. this.$api.workflow.enum().then(res=>{
  371. if (res.code === 200){
  372. this.flows = res.data;
  373. }
  374. })
  375. },
  376. getNodeEnum(){
  377. this.$api.workNode.enum().then(res=>{
  378. if (res.code === 200){
  379. this.allNodes = res.data;
  380. }
  381. })
  382. },
  383. getAllTaskEnum(){
  384. this.$api.workNodeTask.enum().then(res=>{
  385. if (res.code === 200){
  386. this.taksEnums = res.data;
  387. }
  388. })
  389. },
  390. getAllPermissionEnum(){
  391. this.$api.workNodeTask.permissionEnum().then(res=>{
  392. if (res.code === 200){
  393. this.permissionEnum = res.data;
  394. }
  395. })
  396. },
  397. toEditNoe(){
  398. if (!this.workflow.id){
  399. this.$notify.error({
  400. title: '错误',
  401. message: '请先保存工作流名称和编码。'
  402. });
  403. return ;
  404. }
  405. this.editing = !this.editing;
  406. },
  407. saveFlow(){
  408. let workflow = this.workflow;
  409. this.$refs.workflow.validate(valid => {
  410. if (valid){
  411. if (workflow.id!=null){
  412. this.$api.workflow.edit(workflow).then(res=>{
  413. if (res.code === 200 && res.data ){
  414. this.$notify.success({
  415. title: '成功',
  416. message: '工作流修改成功。'
  417. });
  418. }else{
  419. this.$notify.error({
  420. title: '失败',
  421. message: '工作流修改失败,请稍后再试。'
  422. });
  423. }
  424. })
  425. }else{
  426. this.$api.workflow.add(workflow).then(res=>{
  427. if (res.code === 200 && res.data !=null ){
  428. this.workflow.id = res.data;
  429. this.$notify.success({
  430. title: '成功',
  431. message: '工作流保存成功,请继续添加节点。'
  432. });
  433. }else{
  434. this.$notify.error({
  435. title: '失败',
  436. message: '工作流保存失败,请稍后再试。'
  437. });
  438. }
  439. })
  440. }
  441. }
  442. })
  443. },
  444. saveNode(){
  445. if ( this.workflow.id && this.workflow.code){
  446. this.editNode.flowId = this.workflow.id;
  447. const node = this.allNodes.find(node => node.code === this.editNode.code);
  448. this.editNode.name = node.name;
  449. this.editNode.businessType = this.workflow.code;
  450. if (this.editNode.deadlineNumber && this.editNode.deadlineType==="HOUR"){
  451. this.editNode.deadline = this.editNode.deadlineNumber * 60 *60 *1000;
  452. }
  453. if (this.editNode.deadlineNumber && this.editNode.deadlineType==="MINUTE"){
  454. this.editNode.deadline = this.editNode.deadlineNumber *60 *1000;
  455. }
  456. if (this.editNode.id){
  457. this.$api.workNode.edit(this.editNode).then(res=>{
  458. if (res.code === 200 && res.data){
  459. this.$notify.success({
  460. title: '成功',
  461. message: '节点修改成功。'
  462. });
  463. this.getNodeList();
  464. this.dialogFormVisible = false;
  465. }
  466. })
  467. }else{
  468. this.$api.workNode.add(this.editNode).then(res=>{
  469. if (res.code === 200 && res.data){
  470. this.$notify.success({
  471. title: '成功',
  472. message: '节点保存成功。'
  473. });
  474. this.getNodeList();
  475. this.dialogFormVisible = false;
  476. }
  477. })
  478. }
  479. }
  480. },
  481. getOptions(val,val1){
  482. if (val === 'EMPLOYEE'){
  483. this.$api.user.simpleAll().then(res=>{
  484. if (res.code === 200){
  485. this.allUser = res.data;
  486. return;
  487. }
  488. })
  489. }
  490. if (val === "POST"){
  491. this.$api.post.xSimpleAll().then(res=>{
  492. if (res.code === 200){
  493. this.allPost = res.data;
  494. return;
  495. }
  496. })
  497. }
  498. if (val1){
  499. if (val1.powerId){
  500. val1.powerId = null;
  501. }
  502. let permission = JSON.parse(val1.handlerPermission);
  503. if(permission.powerId){
  504. permission.powerId = null;
  505. val1.handlerPermission = JSON.stringify(permission);
  506. };
  507. }
  508. },
  509. cleanDialog(done){
  510. this.editNode = {
  511. flowId:null,
  512. priviousNodeId:null,
  513. code:null,
  514. type:null,
  515. tasks:[],
  516. deadlineNumber:null,
  517. deadlineType:"MINUTE",
  518. deadline:null,
  519. taskItems:[]}
  520. done();
  521. },
  522. addToTasks(t){
  523. let taskItems = this.editNode.tasks.map(item=>item.taskType)
  524. if (!taskItems.includes(t.taskType)){
  525. this.editNode.tasks.push(t)
  526. }else{
  527. let index = 0;
  528. let flag = false;
  529. for ( let i in this.editNode.tasks){
  530. if (this.editNode.tasks[i].taskType === t.taskType){
  531. index = i;
  532. flag = true
  533. break;
  534. }else {
  535. flag = false
  536. }
  537. }
  538. if (flag) {
  539. this.editNode.tasks.splice(index, 1);
  540. }
  541. }
  542. },
  543. getNodeList(){
  544. const flowId = this.workflow.id;
  545. this.$api.workNode.flow(flowId).then(res=>{
  546. if (res.code === 200){
  547. const nodes = res.data;
  548. for (let node in nodes){
  549. let tasks = nodes[node].tasks;
  550. let taskItems = tasks.map(item=>item.description);
  551. nodes[node].taskItems = taskItems;
  552. }
  553. this.nodes = nodes;
  554. }
  555. })
  556. },
  557. delNode(){
  558. if (this.editNode.id){
  559. this.$api.workNode.delete(this.editNode.id).then(res=>{
  560. if (res.code === 200 && res.data){
  561. this.$notify.success({
  562. title: '成功',
  563. message: '节点删除成功。'
  564. });
  565. this.getNodeList();
  566. this.dialogFormVisible = false;
  567. }
  568. })
  569. }
  570. },
  571. selectFlow(){
  572. if (this.workflow.name){
  573. for (let i in this.flows){
  574. if (this.flows[i].name === this.workflow.name){
  575. this.workflow.code = this.flows[i].code
  576. break;
  577. }
  578. }
  579. }
  580. }
  581. },
  582. }
  583. </script>
  584. <style lang="scss" scoped>
  585. .node-area{
  586. padding-left:130px;
  587. padding-top:40px;
  588. width:100%;
  589. height:850px;
  590. }
  591. .add-icom :hover{
  592. cursor:pointer;
  593. }
  594. .add-node-icom{
  595. width:50px;
  596. height:50px;
  597. margin-top:65px;
  598. margin-left:10px;
  599. }
  600. /deep/.editNodeDialog {
  601. border-radius: 20px;
  602. }
  603. .node-seq{
  604. display:flex;
  605. float:left
  606. }
  607. </style>