index.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  1. <template>
  2. <div class="FlowChartComponent">
  3. <!-- <Alert show-icon closable v-if="currentSteps === 1">双击空白区域即可直接新增一个中间节点,双击节点可编辑</Alert> -->
  4. <div class="chartContent">
  5. <div id="paletteDiv" v-show="currentSteps === 1"></div>
  6. <div id="goChart" v-show="currentSteps === 1">
  7. </div>
  8. <div id="showChart" v-show="currentSteps === 2">
  9. </div>
  10. </div>
  11. <!-- 全局侧滑模块 -->
  12. <Drawer
  13. v-model="drawerObject.show"
  14. transfer
  15. :title="drawerObject.title"
  16. :width="472"
  17. :mask-closable="false"
  18. :closable="false"
  19. scrollable
  20. >
  21. <components
  22. v-if="drawerObject.show"
  23. :configData="drawerObject.configData"
  24. :data="drawerObject.configData"
  25. :is="drawerObject.component"
  26. :TABLE_ID="drawerObject.TABLE_ID"
  27. :node="drawerObject.node"
  28. :status="drawerObject.status"
  29. :rejectedNodes="rejectedNodes"
  30. :assignedNodes="assignedNodes"
  31. :readOnly="readOnly"
  32. :moduleType="data.moduleType"
  33. @closeDrawer="closeDrawer"
  34. >
  35. </components>
  36. </Drawer>
  37. </div>
  38. </template>
  39. <script>
  40. import '@/utils/go' import FlowDesinger from '@/utils/flow-desinger'; import startNodeInfo from '@/components/startNodeInfo' import endNodeInfo from '@/components/endNodeInfo' import linkInfo from '@/components/linkInfo' import ProcessNodeConfig from '@/components/ProcessNodeConfig' import DynamicProcessNodeConfig from '@/components/DynamicProcessNodeConfig' import ServiceNodeConfig from '@/components/ServiceNodeConfig' import FlowDisplay from '@/utils/flow-display';
  41. export default {
  42. components:{
  43. startNodeInfo, endNodeInfo, linkInfo, ProcessNodeConfig, DynamicProcessNodeConfig, ServiceNodeConfig
  44. },
  45. props:{
  46. editable:{ //是否可编辑
  47. type:Boolean,
  48. default:true
  49. },
  50. fresh:{
  51. type:Boolean,
  52. default:false
  53. },
  54. currentSteps:{
  55. type:Number,
  56. default:null
  57. },
  58. noFreshFlag:{
  59. type:Boolean,
  60. default:false
  61. },
  62. readOnly:{
  63. type:Boolean,
  64. default:false
  65. },
  66. data:{
  67. type:Object,
  68. default () {
  69. return {
  70. }
  71. }
  72. }
  73. },
  74. data () {
  75. return {
  76. myDesigner:null, //画布实例
  77. drawerObject:{
  78. show:false
  79. },
  80. nodeMsg:{}, //存放节点数据
  81. pathMsg:{}, //存放路径数据
  82. rejectedNodes:[], //驳回节点
  83. assignedNodes: [], //指派节点
  84. }
  85. },
  86. activated () {
  87. if(!this.noFreshFlag){
  88. this.init()
  89. }
  90. },
  91. deactivated () { //keep-alive 组件停用时调用
  92. if(this.currentSteps === 1 && this.myDesigner){
  93. this.data.guiStyle = JSON.parse(this.myDesigner.getFlowData())
  94. }
  95. },
  96. methods:{
  97. init () {
  98. if(!Array.isArray(this.data.nodeMsg)){
  99. let nodeMsg = []
  100. Object.keys(this.data.nodeMsg).map(item => {
  101. nodeMsg.push(this.data.nodeMsg[item])
  102. return item
  103. })
  104. this.data.nodeMsg = nodeMsg
  105. let pathMsg = []
  106. Object.keys(this.data.pathMsg).map(item => {
  107. pathMsg.push(this.data.pathMsg[item])
  108. return item
  109. })
  110. this.data.pathMsg = pathMsg
  111. }
  112. this.dataProcessing()
  113. if(this.currentSteps === 1){
  114. // 流程图设计器
  115. if(!this.myDesigner){
  116. this.myDesigner= new FlowDesinger('goChart',{
  117. showEditNode:this.showEditNode, //节点双击编辑
  118. SelectionDeleted:this.SelectionDeleted, //删除事件
  119. LinkDrawn:this.LinkDrawn, //线的生成
  120. externalobjectsdropped:this.externalobjectsdropped, //节点生成
  121. LinkRelinked: this.LinkRelinked //连线修改
  122. });
  123. }
  124. this.initToolbar = this.myDesigner.initToolbar('paletteDiv',this.data.moduleType)
  125. this.myDesigner.displayFlow(JSON.parse(JSON.stringify(this.data.guiStyle)));// 在设计面板中显示流程图
  126. this.data.myDisplay = this.myDesigner.diagram
  127. }else{
  128. if(this.myDisplay){
  129. this.myDisplay.loadFlow(JSON.parse(JSON.stringify(this.data.guiStyle)))
  130. }else{
  131. this.myDisplay = new FlowDisplay('showChart',{
  132. showEditNode:this.showEditNode, //节点双击编辑
  133. });
  134. this.myDisplay.loadFlow(JSON.parse(JSON.stringify(this.data.guiStyle)))
  135. }
  136. // this.data.myDisplay = this.myDisplay.diagram
  137. }
  138. },
  139. compare (property) {
  140. return function(a,b) {
  141. var value1 = a[property];
  142. var value2 = b[property];
  143. return value1 - value2
  144. }
  145. },
  146. dataProcessing () { //处理后端返回节点数据和路径数据
  147. if(this.data.nodeMsg){
  148. this.data.nodeMsg.forEach(item => {
  149. this.nodeMsg[item.key] = item
  150. if(item.type === 2){ //结束节点对数据排序,区分执行程序还是抄送人
  151. this.nodeMsg[item.key].actionConfig.sort(this.compare('handleType'))
  152. }
  153. })
  154. this.data.nodeMsg = this.nodeMsg
  155. }
  156. if(this.data.pathMsg){
  157. this.data.pathMsg.forEach(item => {
  158. this.pathMsg[item.key] = item
  159. })
  160. this.data.pathMsg = this.pathMsg
  161. }
  162. this.data.removePath = []
  163. this.data.removeNode = []
  164. },
  165. showEditNode (node) { //双击编辑
  166. // node.findNodesOutOf()
  167. if(node.data.type === 1){
  168. let defaultObj = {
  169. conditionType: 0,
  170. ruleList: [],
  171. priority: null,
  172. defaultPriority: null,
  173. modifyId: null,
  174. id:null,
  175. triggerBt:[],
  176. visibleBt:[]
  177. }
  178. if(!node.data.id){
  179. this.nodeMsg[node.data.key] = Object.assign(defaultObj,this.nodeMsg[node.data.key])
  180. }
  181. this.drawerObject = {
  182. show: true,
  183. component: 'startNodeInfo',
  184. configData: this.nodeMsg[node.data.key],
  185. title:'开始节点配置'
  186. }
  187. }
  188. if(node.data.type === 2){
  189. let defaultObj = {
  190. id:null,
  191. manualConfig:null,
  192. actionConfig:[]
  193. }
  194. if(!node.data.id){
  195. this.nodeMsg[node.data.key] = Object.assign(defaultObj,this.nodeMsg[node.data.key])
  196. }
  197. this.drawerObject = {
  198. show: true,
  199. component: 'endNodeInfo',
  200. configData: this.nodeMsg[node.data.key],
  201. title:'结束节点配置'
  202. }
  203. }
  204. // 审批节点和操作节点用同一个配置界面,根据参数控制展示项
  205. if(node.data.category === "Approval" || node.data.category === "Operation"){
  206. this.rejectedNodes = []
  207. this.assignedNodes = []
  208. this.findNodesInto(node)
  209. this.findNodesOutOf(node)
  210. // 默认节点配置
  211. let defaultObj = {
  212. actServiceS:[],
  213. actionConfig:[],
  214. approvelList:[],
  215. backId:null,
  216. id:null,
  217. manualConfig:0,
  218. modifiableField:[],
  219. moduleId:null,
  220. name:'中间节点',
  221. nodeType:0,
  222. ruleList:[],
  223. inevitable: 1, //节点必经
  224. approveCondition: 0, //审批条件
  225. assignBack: 0, //驳回操作
  226. whetherBack: 0, //可为驳回节点
  227. nodeCancle: 0, //作废操作
  228. assignNext: 1, //指派流转节点
  229. assignNode: 1, //节点是否是必需指派
  230. assignApprover: 1, //是否动态指派操作人
  231. visibleBt:[], //可见按钮
  232. nodeBack: 0, //驳回按钮开关
  233. nodeAgree: 0, //同意按钮
  234. }
  235. if(this.data.moduleType === 0){
  236. defaultObj = {
  237. actServiceS:[],
  238. actionConfig:[],
  239. approvelList:[],
  240. backId:null,
  241. id:null,
  242. manualConfig:0,
  243. modifiableField:'',
  244. modifiableFieldName:'',
  245. moduleId:null,
  246. name:'审批节点',
  247. nodeType:0,
  248. ruleList:[]
  249. }
  250. }
  251. if(!node.data.id){
  252. this.nodeMsg[node.data.key] = Object.assign(defaultObj,this.nodeMsg[node.data.key])
  253. }
  254. this.drawerObject = {
  255. show: true,
  256. component: this.data.moduleType === 0?'ProcessNodeConfig':'DynamicProcessNodeConfig',
  257. configData: this.nodeMsg[node.data.key],
  258. node: node,
  259. title:node.data.category === "Approval"?'审批节点配置':'操作节点'
  260. }
  261. }
  262. // 服务节点
  263. if(node.data.category === 'Service'){
  264. let defaultObj = {
  265. id:null,
  266. actionConfig:[{
  267. "id":null, //新增不传,修改传
  268. "handleValue":null, //具体的关于请求状态,url之类的json
  269. "handleType":null, //51:rest服务实现,52:消息系统
  270. "extraMsg":null //相关参数
  271. }]
  272. }
  273. if(!node.data.id){
  274. this.nodeMsg[node.data.key] = Object.assign(defaultObj,this.nodeMsg[node.data.key])
  275. }
  276. this.drawerObject = {
  277. show: true,
  278. component: 'ServiceNodeConfig',
  279. configData: this.nodeMsg[node.data.key],
  280. title:'服务节点配置'
  281. }
  282. }
  283. if(node.data.from !== undefined){
  284. if(this.nodeMsg[node.data.from].category === 'Service' && this.data.moduleType === 1){
  285. return
  286. }
  287. // 默认连线配置
  288. let defaultObj = {
  289. endNode:null,
  290. moduleId:null,
  291. name:null,
  292. ruleList:[],
  293. sources:null,
  294. startNode:null,
  295. status:null
  296. }
  297. if(!node.data.moduleId){
  298. this.pathMsg[node.data.key] = Object.assign(defaultObj,this.pathMsg[node.data.key])
  299. }
  300. let tableConfig = [{
  301. ID: this.nodeMsg[node.data.from].nodeFormId,
  302. value: this.nodeMsg[node.data.from].nodeForm
  303. }]
  304. if(this.nodeMsg[node.data.from].category === 'Start'){
  305. tableConfig = [{
  306. ID: this.nodeMsg[node.data.from].businessType,
  307. value: this.nodeMsg[node.data.from].businessTypeName
  308. }]
  309. }
  310. this.drawerObject = {
  311. show: true,
  312. component: 'linkInfo',
  313. configData: this.pathMsg[node.data.key],
  314. TABLE_ID: this.data.moduleType === 1?tableConfig:this.data.businessType,
  315. title:'连接线配置'
  316. }
  317. }
  318. },
  319. findNodesInto (node) { //查找父节点
  320. let parents = node.findNodesInto()
  321. if(parents.count > 0){
  322. node.findNodesInto().map(item => {
  323. this.rejectedNodes.push({
  324. value:item.data.key,
  325. label:item.data.text
  326. })
  327. this.findNodesInto(item)
  328. })
  329. }else{
  330. return
  331. }
  332. },
  333. findNodesOutOf (node) { //查找子节点
  334. let childrens = node.findNodesOutOf()
  335. if(childrens.count > 0){
  336. node.findNodesOutOf().map(item => {
  337. if(item.data.key != -2){
  338. this.assignedNodes.push({
  339. value:item.data.key,
  340. label:item.data.text
  341. })
  342. this.findNodesOutOf(item)
  343. }
  344. })
  345. }else{
  346. return
  347. }
  348. },
  349. SelectionDeleted (node) { //删除节点或者线
  350. if(node.fromPort !== undefined){ //线的删除
  351. delete this.pathMsg[node.key]
  352. if(node.id){ //已保存的线
  353. this.data.removePath.push(node.id)
  354. }
  355. }else{ //节点删除
  356. delete this.nodeMsg[node.key]
  357. if(node.id){
  358. this.data.removeNode.push({
  359. id:node.id,
  360. type:node.type
  361. })
  362. }
  363. }
  364. },
  365. LinkDrawn (node) { //连线生成
  366. if(!this.judgeLoop()){
  367. this.$Modal.fcWarning({
  368. title:'警告',
  369. content:'流程图中存在回路,请重新设置!!',
  370. mask:true
  371. })
  372. this.data.guiStyle = JSON.parse(this.myDesigner.getFlowData())
  373. let linkDataArray = this.data.guiStyle.linkDataArray
  374. linkDataArray = linkDataArray.filter(item => {
  375. return item.key !== node.key
  376. })
  377. this.data.guiStyle.linkDataArray = linkDataArray
  378. this.init()
  379. // reject()
  380. return
  381. }else{
  382. this.pathMsg[node.key] = node
  383. this.pathMsg[node.key].ruleList = []
  384. }
  385. },
  386. LinkRelinked (node) { //连线修改
  387. this.pathMsg[node.key].from = node.from
  388. this.pathMsg[node.key].to = node.to
  389. this.pathMsg[node.key].fromPort = node.fromPort
  390. this.pathMsg[node.key].toPort = node.toPort
  391. },
  392. judgeLoop () { //判断是否存在闭环 true为没有闭环 false有闭环
  393. // 获取所有的节点
  394. let data = JSON.parse(this.myDesigner.getFlowData())
  395. const edges = data.linkDataArray?data.linkDataArray:JSON.parse(data).linkDataArray;
  396. const nodes = [];
  397. const list = {}; // 邻接表
  398. const queue = []; // 入度为0的节点集合
  399. const indegree = {};
  400. edges.forEach(e => {
  401. const { from, to } = e;
  402. if (!nodes.includes(from)) {
  403. nodes.push(from);
  404. }
  405. if (!nodes.includes(to)) {
  406. nodes.push(to);
  407. }
  408. addEdge(from, to);
  409. });
  410. const V = nodes.length;
  411. nodes.forEach(node => {
  412. if (!indegree[node]) indegree[node] = 0;
  413. if (!list[node]) list[node] = [];
  414. });
  415. function addEdge(source, target) {
  416. if (!list[source]) list[source] = [];
  417. if (!indegree[target]) indegree[target] = 0;
  418. list[source].push(target);
  419. indegree[target] += 1;
  420. }
  421. function sort() {
  422. Object.keys(indegree).forEach(id => {
  423. if (indegree[id] === 0) {
  424. queue.push(id);
  425. }
  426. });
  427. let count = 0;
  428. while (queue.length) {
  429. ++count;
  430. const currentNode = queue.pop();
  431. const nodeTargets = list[currentNode];
  432. for (let i = 0; i < nodeTargets.length; i++) {
  433. const target = nodeTargets[i];
  434. indegree[target] -= 1;
  435. if (indegree[target] === 0) {
  436. queue.push(target);
  437. }
  438. }
  439. }
  440. // false 没有输出全部顶点,有向图中有回路
  441. return !(count < V);
  442. }
  443. return sort();
  444. },
  445. externalobjectsdropped (node) { //节点生成
  446. if(node.category === 'Service' || node.category === 'Subprocesses'){
  447. this.nodeMsg[node.key] = node
  448. this.nodeMsg[node.key].name = node.text
  449. return
  450. }
  451. this.nodeMsg[node.key] = node
  452. this.nodeMsg[node.key].ruleList = []
  453. this.nodeMsg[node.key].actServiceS =[
  454. {
  455. status:1,
  456. handleValue:null,
  457. handleType:11
  458. },
  459. {
  460. status:1,
  461. handleValue:null,
  462. handleType:20
  463. }
  464. ]
  465. this.nodeMsg[node.key].actionConfig =[]
  466. this.nodeMsg[node.key].approvelList = []
  467. this.nodeMsg[node.key].approverStyle = 0
  468. this.nodeMsg[node.key].name = node.text
  469. },
  470. closeDrawer () { //关闭策划块
  471. this.drawerObject = {
  472. show: false
  473. }
  474. }
  475. }
  476. }
  477. </script>
  478. <style lang="less" >
  479. .FlowChartComponent{
  480. width: 100%;
  481. height: 100%;
  482. overflow: hidden;
  483. display: flex;
  484. position: relative;
  485. flex-direction: column;
  486. .burgeon-alert{
  487. margin-bottom: 0;
  488. }
  489. .chartContent{
  490. flex: 1;
  491. display: flex;
  492. }
  493. #paletteDiv{
  494. padding: 5px;
  495. width: 160px;
  496. height: 100%;
  497. display: inline-block;
  498. border-right: 1px solid #dcdee2;
  499. }
  500. canvas{
  501. outline: none;
  502. }
  503. #goChart,#showChart{
  504. flex-grow: 1;
  505. flex: 1;
  506. // border: solid 1px black
  507. }
  508. }
  509. </style>