approvalManage.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629
  1. <template>
  2. <div class="approvalset-wrap">
  3. <div class="left-wrap">
  4. <div class="item" :class="typeName==='客户审批'?'active':null" @click="handleChangeType('客户审批')">客户审批</div>
  5. <div class="item" :class="typeName==='合同审批'?'active':null" @click="handleChangeType('合同审批')">合同审批</div>
  6. <div class="item" :class="typeName==='用印审批'?'active':null" @click="handleChangeType('用印审批')">用印审批</div>
  7. </div>
  8. <div class="content-wrap">
  9. <div class="top-type">
  10. <span class="item" :class="topTypeActive===item.id?'active':null" v-for="item in topTypeArr" :key="item.id" @click="handleTopTypeChange(item.id)">{{item.name}}</span>
  11. </div>
  12. <div class="step-wrap">
  13. <div>
  14. <div class="step-item-box" v-for="(step,stepIndex) in processList" :key="step.name">
  15. <div class="step-line">
  16. <h2>{{step.name}}</h2>
  17. </div>
  18. <div class="step-content">
  19. <!-- 审批人 -->
  20. <div>
  21. <block v-for="(auser,index) in step.approvalUser" :key="auser.user_type">
  22. <block v-if="auser.user_type==='user'">
  23. <span class="user-box" v-for="(item,index2) in auser.user" :key="item.AdminId">
  24. {{item.userName}}
  25. <img class="delete-icon" src="../../assets/img/icons/delete-icon.png" alt="" @click="handleDelete({type:'approve',userType:'user',stepIndex,index,index2})">
  26. </span>
  27. </block>
  28. <block v-if="auser.user_type==='role'">
  29. <!-- <span class="user-box" style="margin-right:0"> -->
  30. <span class="user-box">
  31. {{auser.user[0].RoleName}}:
  32. <span v-for="(item,itemindex) in auser.user" :key="item.AdminId">{{item.userName}}<span v-if="itemindex<auser.user.length-1">、</span></span>
  33. <img class="delete-icon" src="../../assets/img/icons/delete-icon.png" alt="" @click="handleDelete({type:'approve',userType:'role',stepIndex,index})">
  34. </span>
  35. <!-- :<span class="user-name" v-for="item in auser.user" :key="item.AdminId">{{item.userName}}</span> -->
  36. </block>
  37. </block>
  38. <span class="add-btn" @click="handleShowAdd('approve',stepIndex)">+添加用户</span>
  39. </div>
  40. <!-- 抄送人 -->
  41. <div style="margin-top:24px">
  42. <block v-for="(cuser,index) in step.copyUser" :key="cuser.user_type">
  43. <block v-if="cuser.user_type==='user'">
  44. <span class="user-box" v-for="(item,index2) in cuser.user" :key="item.AdminId">
  45. {{item.userName}}
  46. <img class="delete-icon" src="../../assets/img/icons/delete-icon.png" alt="" @click="handleDelete({type:'copy',userType:'user',stepIndex,index,index2})">
  47. </span>
  48. </block>
  49. <block v-if="cuser.user_type==='role'">
  50. <!-- <span class="user-box" style="margin-right:0"> -->
  51. <span class="user-box">
  52. {{cuser.user[0].RoleName}}:
  53. <span v-for="(item,itemindex) in cuser.user" :key="item.AdminId">{{item.userName}}<span v-if="itemindex<cuser.user.length-1">、</span></span>
  54. <img class="delete-icon" src="../../assets/img/icons/delete-icon.png" alt="" @click="handleDelete({type:'copy',userType:'role',stepIndex,index})">
  55. </span>
  56. <!-- :<span class="user-name" v-for="item in cuser.user" :key="item.AdminId">{{item.userName}}</span> -->
  57. </block>
  58. </block>
  59. <span class="add-btn" @click="handleShowAdd('copy',stepIndex)">+添加抄送人</span>
  60. </div>
  61. </div>
  62. </div>
  63. </div>
  64. </div>
  65. <div class="bts-box">
  66. <el-button type="primary" @click="handleSave">保存</el-button>
  67. <el-button type="primary" plain @click="handleCancelSave">取消</el-button>
  68. </div>
  69. </div>
  70. <!-- 添加审批人/抄送人 弹窗 -->
  71. <el-dialog width="560px" :visible.sync="show" :modal-append-to-body="false" @close="show=false">
  72. <div slot="title">
  73. <img width="15" src="../../assets/img/icons/add_icon.png" alt="" style="position:relative;top:2px;margin-right:10px">
  74. <span>{{addType==='approve'?'添加审批人':'添加抄送人'}}</span>
  75. </div>
  76. <div class="add-wrap">
  77. <el-autocomplete
  78. class="search-box"
  79. v-model="searchVal"
  80. :fetch-suggestions="searchList"
  81. placeholder="搜索用户名称"
  82. @select="handleSelect"
  83. ></el-autocomplete>
  84. <div style="text-align:center;margin:70px 0 30px 0">
  85. <el-button type="primary" @click="handleConfirmUser">确定</el-button>
  86. <el-button type="primary" plain @click="show=false">取消</el-button>
  87. </div>
  88. </div>
  89. </el-dialog>
  90. </div>
  91. </template>
  92. <script>
  93. import {departInterence} from '@/api/api.js'
  94. const config={
  95. '客户审批':[{id:1,name:'FICC部门'},{id:2,name:'权益部门'}],
  96. '合同审批':[{id:3,name:'FICC部门'},{id:4,name:'权益部门'}],
  97. '用印审批':[{id:5,name:'合同章/电子合同章'},{id:6,name:'公章/法人章'}]
  98. }
  99. export default {
  100. data () {
  101. return {
  102. typeName:"客户审批",
  103. topTypeArr:config['客户审批'],
  104. topTypeActive:config['客户审批'][0].id,
  105. processList:[
  106. {name:"一级审批",approvalUser:[],copyUser:[]},
  107. {name:'二级审批',approvalUser:[],copyUser:[]},
  108. {name:'三级审批',approvalUser:[],copyUser:[]}
  109. ],//此处写死三级 目前需求固定页面展示三级 允许只配置一个节点
  110. show:false,
  111. searchVal:'',
  112. addType:'',//添加人类型 approve 审批 copy 抄送
  113. temUser:null,
  114. stepIndex:null,
  115. }
  116. },
  117. created () {
  118. this.getApprovalDetail()
  119. },
  120. methods: {
  121. handleCancelSave(){
  122. this.getApprovalDetail()
  123. },
  124. // 保存配置
  125. async handleSave(){
  126. let temarr=[]
  127. let copyArr=JSON.parse(JSON.stringify(this.processList))
  128. copyArr.forEach(item=>{
  129. if(item.approvalUser.length>0){
  130. let obj={
  131. NodeType:'check',
  132. User:[]
  133. }
  134. let arr=[]
  135. arr=item.approvalUser.filter(item=> item.user.length>0)
  136. obj.User=arr.map(item2=>{
  137. item2.user=item2.user.map(item3=>{
  138. return item3.AdminId
  139. })
  140. return {
  141. user_type:item2.user_type,
  142. user:[...new Set(item2.user)].join(',')
  143. }
  144. })
  145. if(obj.User.length>0){
  146. temarr.push(obj)
  147. }
  148. }
  149. if(item.copyUser.length>0){
  150. let obj={
  151. NodeType:'cc',
  152. User:[]
  153. }
  154. let arr=[]
  155. arr=item.copyUser.filter(item=> item.user.length>0)
  156. obj.User=arr.map(item2=>{
  157. item2.user=item2.user.map(item3=>{
  158. return item3.AdminId
  159. })
  160. return {
  161. user_type:item2.user_type,
  162. user:[...new Set(item2.user)].join(',')
  163. }
  164. })
  165. if(obj.User.length>0){
  166. temarr.push(obj)
  167. }
  168. }
  169. })
  170. const res=await departInterence.approvalFlowSave({
  171. FlowId:Number(this.topTypeActive),
  172. NodeList:temarr
  173. })
  174. if(res.Ret===200){
  175. this.$message.success('保存成功')
  176. this.getApprovalDetail()
  177. }
  178. },
  179. /**
  180. * 删除用户/组织
  181. * type:类型 approve审批人 copy抄送人
  182. * userType:用户类型 role组织 user个人
  183. * stepIndex:当前是操作第几级审批的序号
  184. * index:最外层序号
  185. * index2:内部删除指定用户序号
  186. */
  187. handleDelete({type,userType,stepIndex,index,index2}){
  188. // 删除审批人
  189. if(type==='approve'){
  190. if(userType==='role'){
  191. this.processList[stepIndex].approvalUser.splice(index,1)
  192. }
  193. if(userType==='user'){
  194. this.processList[stepIndex].approvalUser[index].user.splice(index2,1)
  195. }
  196. }
  197. // 删除抄送人
  198. if(type==='copy'){
  199. if(userType==='role'){
  200. this.processList[stepIndex].copyUser.splice(index,1)
  201. }
  202. if(userType==='user'){
  203. this.processList[stepIndex].copyUser[index].user.splice(index2,1)
  204. }
  205. }
  206. },
  207. // 切换左侧类型
  208. handleChangeType(e){
  209. this.typeName=e
  210. this.topTypeArr=config[e]
  211. this.handleTopTypeChange(config[e][0].id)
  212. },
  213. //显示添加弹窗
  214. handleShowAdd(e,index){
  215. this.addType=e
  216. this.searchVal=''
  217. this.stepIndex=index
  218. this.temUser=null
  219. this.show=true
  220. },
  221. //切换顶部类型
  222. handleTopTypeChange(e){
  223. this.topTypeActive=e
  224. this.getApprovalDetail()
  225. },
  226. //搜索用户
  227. async searchList(queryVal,cb){
  228. cb([])
  229. if(!queryVal) return
  230. const res=await departInterence.approvalUserSearch({Keyword:queryVal})
  231. if(res.Ret===200){
  232. let arr1=[],arr2=[]
  233. if(res.Data.AdminList){
  234. arr1=res.Data.AdminList.map(item=>{
  235. return {
  236. value:item.RealName,
  237. AdminId:item.AdminId,
  238. RoleName:item.RoleName,
  239. type:'user'
  240. }
  241. })
  242. }
  243. if(res.Data.RoleList){
  244. arr2=res.Data.RoleList.map(item=>{
  245. return {
  246. value:item.RoleName,
  247. AdminId:item.RoleId,
  248. RoleName:item.RoleType,
  249. type:'role'
  250. }
  251. })
  252. }
  253. cb([...arr1,...arr2])
  254. }
  255. },
  256. handleConfirmUser(){
  257. // 判断是否存在该用户
  258. if(!this.temUser||!this.temUser.AdminId||!this.searchVal){
  259. let str='请添加审批人'
  260. if(this.addType==='copy'){
  261. str='请添加抄送人'
  262. }
  263. this.$message.warning(str)
  264. return
  265. }
  266. let temarr=[]
  267. if(this.addType==='approve'){
  268. this.processList[this.stepIndex].approvalUser.forEach(item=>{
  269. temarr.push(...item.user)
  270. })
  271. }else{
  272. this.processList[this.stepIndex].copyUser.forEach(item=>{
  273. temarr.push(...item.user)
  274. })
  275. }
  276. let flag=temarr.some(item=>item.AdminId==this.temUser.AdminId)
  277. if(flag){
  278. this.$message.warning('该用户已存在')
  279. return
  280. }
  281. // 添加到审批节点
  282. if(this.addType==='approve'){
  283. if(this.temUser.type==='user'){
  284. let tagindex=-1
  285. this.processList[this.stepIndex].approvalUser.forEach((item,index)=>{
  286. if(item.user_type==='user'){
  287. tagindex=index
  288. }
  289. })
  290. if(tagindex>-1){
  291. this.processList[this.stepIndex].approvalUser[tagindex].user.push({
  292. AdminId:this.temUser.AdminId,
  293. RoleName:this.temUser.RoleName,
  294. userName:this.temUser.value
  295. })
  296. }else{
  297. this.processList[this.stepIndex].approvalUser.push({
  298. user:[{
  299. AdminId:this.temUser.AdminId,
  300. RoleName:this.temUser.RoleName,
  301. userName:this.temUser.value
  302. }],
  303. user_type:"user"
  304. })
  305. }
  306. }else{
  307. let tag=false
  308. this.processList[this.stepIndex].approvalUser.forEach(item=>{
  309. if(item.user_type==='role'&&item.user[0].AdminId==this.temUser.AdminId){
  310. tag=true
  311. }
  312. })
  313. if(tag){
  314. this.$message.warning('该组织已存在')
  315. return
  316. }
  317. this.processList[this.stepIndex].approvalUser.push({
  318. user:this.temUser.value,
  319. user_type:"role"
  320. })
  321. }
  322. }else{
  323. if(this.temUser.type==='user'){
  324. let tagindex=-1
  325. this.processList[this.stepIndex].copyUser.forEach((item,index)=>{
  326. if(item.user_type==='user'){
  327. tagindex=index
  328. }
  329. })
  330. if(tagindex>-1){
  331. this.processList[this.stepIndex].copyUser[tagindex].user.push({
  332. AdminId:this.temUser.AdminId,
  333. RoleName:this.temUser.RoleName,
  334. userName:this.temUser.value
  335. })
  336. }else{
  337. this.processList[this.stepIndex].copyUser.push({
  338. user:[{
  339. AdminId:this.temUser.AdminId,
  340. RoleName:this.temUser.RoleName,
  341. userName:this.temUser.value
  342. }],
  343. user_type:"user"
  344. })
  345. }
  346. }else{
  347. let tag=false
  348. this.processList[this.stepIndex].copyUser.forEach(item=>{
  349. if(item.user_type==='role'&&item.user[0].AdminId==this.temUser.AdminId){
  350. tag=true
  351. }
  352. })
  353. if(tag){
  354. this.$message.warning('该组织已存在')
  355. return
  356. }
  357. this.processList[this.stepIndex].copyUser.push({
  358. user:this.temUser.value,
  359. user_type:"role"
  360. })
  361. }
  362. }
  363. this.show=false
  364. },
  365. //选择搜索到的用户
  366. async handleSelect(e){
  367. if(e.type==='user'){
  368. this.temUser=e
  369. }else{
  370. const res=await departInterence.getUserList({RoleId:e.AdminId,PageSize:1000})
  371. if(res.Ret===200){
  372. let arr=res.Data.List&&res.Data.List.map(item=>{
  373. return {
  374. AdminId:e.AdminId,
  375. RoleName:e.RoleName,
  376. userName:item.RealName
  377. }
  378. })
  379. this.temUser={
  380. value:arr,
  381. AdminId:e.AdminId,
  382. RoleName:e.RoleName,
  383. type:'role'
  384. }
  385. }
  386. }
  387. },
  388. // 流程详情
  389. async getApprovalDetail(){
  390. this.processList=[
  391. {name:"一级审批",approvalUser:[],copyUser:[]},
  392. {name:'二级审批',approvalUser:[],copyUser:[]},
  393. {name:'三级审批',approvalUser:[],copyUser:[]}
  394. ]
  395. const res=await departInterence.approvalFlowDetail({FlowId :this.topTypeActive})
  396. if(res.Ret===200){
  397. let tag=-1
  398. let obj={name:'',approvalUser:[],copyUser:[]}
  399. res.Data.NodeList.forEach(item=>{
  400. item.User.forEach(item2=>{
  401. let userarr=item2.user.split(',')
  402. let temarr=[]
  403. userarr.forEach(item3=>{
  404. item.UserList.forEach(item4=>{
  405. if(item2.user_type==='role'){
  406. if(item4.RoleId==item3){
  407. temarr=[...temarr,{AdminId:item3,userName:item4.Name,RoleName:item4.RoleName}]
  408. }
  409. }else{
  410. if(item4.AdminId==item3){
  411. temarr=[...temarr,{AdminId:item3,userName:item4.Name,RoleName:item4.RoleName}]
  412. }
  413. }
  414. })
  415. })
  416. item2.user=temarr
  417. })
  418. if(item.NodeType==='check'){
  419. tag++
  420. obj={name:`${tag}级审批`,approvalUser:[],copyUser:[]}
  421. obj.approvalUser=[...obj.approvalUser,...item.User]
  422. }else{
  423. obj.copyUser=[...obj.copyUser,...item.User]
  424. }
  425. // arr[tag]=obj
  426. this.processList[tag].approvalUser=obj.approvalUser
  427. this.processList[tag].copyUser=obj.copyUser
  428. })
  429. }
  430. }
  431. }
  432. }
  433. </script>
  434. <style lang="scss">
  435. .add-wrap{
  436. .el-input{
  437. width: 100%;
  438. }
  439. }
  440. </style>
  441. <style lang="scss" scoped>
  442. div{
  443. box-sizing: border-box;
  444. }
  445. .approvalset-wrap{
  446. display: flex;
  447. }
  448. .left-wrap{
  449. width: 210px;
  450. background: #fff;
  451. // border: 1px solid #ececec;
  452. border-radius: 4px;
  453. // box-shadow: 0 3px 6px rgba(0, 0, 0, 0.05);
  454. padding: 30px 0 30px 30px;
  455. margin-right: 20px;
  456. .item{
  457. line-height: 53px;
  458. color: #409EFF;
  459. font-size: 14px;
  460. padding-left: 48px;
  461. font-weight: bold;
  462. cursor: pointer;
  463. }
  464. .active{
  465. background-color: #409EFF;
  466. border-radius: 27px 0px 0px 27px;
  467. color: #fff;
  468. position: relative;
  469. &::before{
  470. content: '';
  471. display: block;
  472. width: 10px;
  473. height: 10px;
  474. border-radius: 50%;
  475. position: absolute;
  476. left: 20px;
  477. top: 50%;
  478. transform: translateY(-50%);
  479. background-color: #fff;
  480. }
  481. }
  482. }
  483. .content-wrap{
  484. flex: 1;
  485. .top-type{
  486. .item{
  487. display: inline-block;
  488. font-size: 16px;
  489. font-weight: bold;
  490. width: 216px;
  491. text-align: center;
  492. line-height: 60px;
  493. cursor: pointer;
  494. }
  495. .active{
  496. color: #409EFF;
  497. background-color: #fff;
  498. }
  499. }
  500. .step-wrap{
  501. background-color: #fff;
  502. padding: 60px 70px;
  503. min-height: calc(100vh - 350px);
  504. .step-item-box{
  505. display: flex;
  506. .step-line{
  507. font-size: 16px;
  508. padding-left: 30px;
  509. margin-right: 120px;
  510. position: relative;
  511. &::before{
  512. position: absolute;
  513. left: 0;
  514. top: 5px;
  515. content: '';
  516. display: inline-block;
  517. width: 16px;
  518. height: 16px;
  519. background-color: #E1E1E1;
  520. border-radius: 50%;
  521. }
  522. &::after{
  523. content: '';
  524. display: inline-block;
  525. width: 1px;
  526. height: 100%;
  527. background-color: #E1E1E1;
  528. position: absolute;
  529. left: 8px;
  530. top: 5px;
  531. }
  532. }
  533. .step-content{
  534. font-size: 14px;
  535. margin-bottom: 60px;
  536. .user-box{
  537. display: inline-block;
  538. padding: 9px 24px;
  539. background-color: #E0EEFD;
  540. border-radius: 4px;
  541. color: #2D8CF0;
  542. margin-right: 24px;
  543. margin-bottom: 24px;
  544. position: relative;
  545. .delete-icon{
  546. position: absolute;
  547. top: -7px;
  548. right: -7px;
  549. width: 14px;
  550. height: 14px;
  551. cursor: pointer;
  552. }
  553. }
  554. .user-name{
  555. font-size: 14px;
  556. &::after{
  557. content: '、';
  558. }
  559. }
  560. .user-name:last-child::after{
  561. content: '';
  562. margin-right: 20px;
  563. }
  564. .add-btn{
  565. color: #409EFF;
  566. font-size: 16px;
  567. cursor: pointer;
  568. // margin-left: 20px;
  569. }
  570. }
  571. }
  572. .step-item-box:last-child{
  573. .step-line::after{
  574. width: 0;
  575. }
  576. }
  577. }
  578. .bts-box{
  579. background-color: #fff;
  580. text-align: center;
  581. padding: 50px 0;
  582. }
  583. }
  584. .add-wrap{
  585. /deep/ .el-input{
  586. width: 100% !important;
  587. }
  588. .search-box{
  589. width: 500px;
  590. }
  591. }
  592. </style>