home.vue 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. <template>
  2. <div class="flow_region">
  3. <div class="flow_left">
  4. <div class="help">
  5. <el-tooltip placement="bottom-start">
  6. <div slot="content">
  7. <ul>
  8. <li>
  9. <span>新增节点:</span
  10. ><span>从左侧拖动相应节点放到右侧画布中</span>
  11. </li>
  12. <li><span>编辑节点:</span><span>双击节点弹出编辑界面</span></li>
  13. <li>
  14. <span>删除节点:</span><span>鼠标右键节点弹出删除菜单</span>
  15. </li>
  16. <li>
  17. <span>删除连线:</span
  18. ><span
  19. >鼠标移动到连线处,当连线变成红色时鼠标左键双击弹出删除界面</span
  20. >
  21. </li>
  22. </ul>
  23. </div>
  24. <el-button style="font-size: 10px; padding: 5px">操作指南</el-button>
  25. </el-tooltip>
  26. </div>
  27. <el-scrollbar style="height: 100%">
  28. <div class="nodes-wrap">
  29. <div
  30. v-for="item in nodeTypeList"
  31. :key="item.type"
  32. :class="nodeDisabled(item) ? 'node node-disabled' : 'node'"
  33. :draggable="!nodeDisabled(item)"
  34. @dragstart="drag($event, item)"
  35. >
  36. <div class="log">
  37. <img :src="item.logImg" alt="" />
  38. </div>
  39. <div class="name">{{ item.typeName }}</div>
  40. </div>
  41. </div>
  42. </el-scrollbar>
  43. <!-- <div class="flow_operation">
  44. <el-button type="primary" @click="saveFlow" :disabled="disabled">保存</el-button>
  45. </div> -->
  46. </div>
  47. <div
  48. id="flowWrap"
  49. ref="flowWrap"
  50. class="flow-wrap"
  51. @drop="drop($event)"
  52. @dragover="allowDrop($event)"
  53. >
  54. <div id="flow">
  55. <div
  56. v-show="auxiliaryLine.isShowXLine"
  57. class="auxiliary-line-x"
  58. :style="{
  59. width: auxiliaryLinePos.width,
  60. top: auxiliaryLinePos.y + 'px',
  61. left: auxiliaryLinePos.offsetX + 'px'
  62. }"
  63. ></div>
  64. <div
  65. v-show="auxiliaryLine.isShowYLine"
  66. class="auxiliary-line-y"
  67. :style="{
  68. height: auxiliaryLinePos.height,
  69. left: auxiliaryLinePos.x + 'px',
  70. top: auxiliaryLinePos.offsetY + 'px'
  71. }"
  72. ></div>
  73. <flowNode
  74. v-for="item in data.nodeList"
  75. :id="item.id"
  76. :key="item.id"
  77. :node="item"
  78. :disabled="disabled"
  79. :selectOperator="selectOperator"
  80. @setNode="setNode"
  81. @deleteNode="deleteNode"
  82. @changeLineState="changeLineState"
  83. ></flowNode>
  84. </div>
  85. </div>
  86. </div>
  87. </template>
  88. <script>
  89. import cloneDeep from 'lodash/cloneDeep'
  90. import { jsPlumb } from 'jsplumb'
  91. import { nodeTypeList } from './config/init'
  92. import {
  93. jsplumbSetting,
  94. jsplumbConnectOptions,
  95. jsplumbSourceOptions,
  96. jsplumbTargetOptions
  97. } from './config/commonConfig'
  98. import methods from './config/methods'
  99. // import data from "./config/data.json";
  100. import flowNode from './node-item'
  101. export default {
  102. name: 'FlowEdit',
  103. components: {
  104. flowNode
  105. },
  106. props: {
  107. // 节点数据源
  108. nodeData: {
  109. type: Object,
  110. default: {
  111. nodeList: [],
  112. lineList: []
  113. }
  114. },
  115. // 是否仅查看
  116. disabled: {
  117. type: Boolean,
  118. default: false
  119. },
  120. // 是否选择操作人,当选择操作人时,其他字段不可编辑
  121. selectOperator: {
  122. type: Boolean,
  123. default: false
  124. }
  125. },
  126. data () {
  127. return {
  128. jsPlumb: null,
  129. currentItem: null,
  130. nodeTypeList: nodeTypeList,
  131. nodeTypeObj: {},
  132. data: {
  133. nodeList: [],
  134. lineList: []
  135. },
  136. selectedList: [],
  137. jsplumbSetting: jsplumbSetting,
  138. jsplumbConnectOptions: jsplumbConnectOptions,
  139. jsplumbSourceOptions: jsplumbSourceOptions,
  140. jsplumbTargetOptions: jsplumbTargetOptions,
  141. auxiliaryLine: { isShowXLine: false, isShowYLine: false }, // 对齐辅助线是否显示
  142. auxiliaryLinePos: {
  143. width: '100%',
  144. height: '100%',
  145. offsetX: 0,
  146. offsetY: 0,
  147. x: 20,
  148. y: 20
  149. },
  150. commonGrid: [5, 5], // 节点移动最小距离
  151. selectModuleFlag: false, // 多选标识
  152. rectAngle: {
  153. px: '', // 多选框绘制时的起始点横坐标
  154. py: '', // 多选框绘制时的起始点纵坐标
  155. left: 0,
  156. top: 0,
  157. height: 0,
  158. width: 0
  159. }
  160. }
  161. },
  162. watch: {
  163. nodeData (val) {
  164. this.initNode()
  165. this.fixNodesPosition()
  166. this.$nextTick(() => {
  167. this.jsPlumb.deleteEveryConnection()
  168. // this.jsPlumb.deleteEveryEndpoint();
  169. // this.deleteAllNode();
  170. this.init()
  171. })
  172. },
  173. disabled (val) {
  174. }
  175. },
  176. mounted () {
  177. console.log("mounted")
  178. this.jsPlumb = jsPlumb.getInstance()
  179. this.initNodeTypeObj()
  180. this.initNode()
  181. this.fixNodesPosition()
  182. this.$nextTick(() => {
  183. this.init()
  184. })
  185. },
  186. methods: {
  187. ...methods,
  188. // 重置
  189. resetNodeData () {
  190. this.data = {
  191. nodeList: [],
  192. lineList: []
  193. }
  194. },
  195. initNodeTypeObj () {
  196. nodeTypeList.map(v => {
  197. this.nodeTypeObj[v.type] = v
  198. })
  199. },
  200. initNode () {
  201. this.resetNodeData()
  202. let tempData = cloneDeep(this.nodeData)
  203. this.data.lineList.push(...tempData.lineList)
  204. this.data.nodeList = []
  205. tempData.nodeList.map(v => {
  206. v.logImg = this.nodeTypeObj[v.type].logImg
  207. v.log_bg_color = this.nodeTypeObj[v.type].log_bg_color
  208. this.data.nodeList.push(v)
  209. })
  210. // this.$nextTick(() => {
  211. // this.init()
  212. // })
  213. },
  214. nodeDisabled (node) {
  215. return (
  216. this.disabled ||
  217. this.selectOperator ||
  218. (this.data.nodeList.findIndex(t => t.type === 'start') > -1 &&
  219. node.type === 'start') ||
  220. (this.data.nodeList.findIndex(t => t.type === 'end') > -1 &&
  221. node.type === 'end') ||
  222. false
  223. )
  224. },
  225. // 保存流程图
  226. saveFlow () {
  227. // 去除流程图背景图片logImg和背景颜色字段log_bg_color
  228. // let data = JSON.parse(JSON.stringify(this.data))
  229. // data.nodeList = data.nodeList.map(item => {
  230. // delete item.logImg
  231. // delete item.log_bg_color
  232. // return item
  233. // })
  234. // this.$emit('saveWorkFlow', data)
  235. },
  236. getFlowData () {
  237. let data = JSON.parse(JSON.stringify(this.data))
  238. data.nodeList = data.nodeList.map(item => {
  239. delete item.logImg
  240. delete item.log_bg_color
  241. return item
  242. })
  243. return data
  244. }
  245. }
  246. }
  247. </script>
  248. <style lang="less" scoped>
  249. .flow_region {
  250. display: flex;
  251. width: 90%;
  252. height: 90%;
  253. margin: 20px auto;
  254. border: 1px solid #ccc;
  255. position: relative;
  256. text-align: center;
  257. .flow_left {
  258. border-right: 1px solid #ccc;
  259. display: flex;
  260. flex-direction: column;
  261. justify-content: space-between;
  262. .help {
  263. position: absolute;
  264. left: 150px;
  265. z-index: 99999;
  266. }
  267. }
  268. .nodes-wrap {
  269. width: 150px;
  270. height: 90%;
  271. // border-right: 1px solid #ccc;
  272. .node {
  273. display: flex;
  274. height: 40px;
  275. width: 80%;
  276. margin: 5px auto;
  277. border: 1px solid #ccc;
  278. line-height: 40px;
  279. &:hover {
  280. cursor: grab;
  281. }
  282. &:active {
  283. cursor: grabbing;
  284. }
  285. .log {
  286. width: 40px;
  287. height: 40px;
  288. }
  289. .name {
  290. width: 0;
  291. flex-grow: 1;
  292. }
  293. }
  294. .node-disabled {
  295. &:hover {
  296. cursor: default;
  297. }
  298. &:active {
  299. cursor: default;
  300. }
  301. }
  302. }
  303. .flow_operation {
  304. // height: 50px;
  305. display: flex;
  306. justify-content: center;
  307. align-items: center;
  308. border-top: 1px solid #ccc;
  309. padding: 10px 0;
  310. .el-button {
  311. width: 80%;
  312. }
  313. }
  314. .flow-wrap {
  315. height: 100%;
  316. position: relative;
  317. overflow: hidden;
  318. outline: none !important;
  319. flex-grow: 1;
  320. background-image: url("../../assets/point.png");
  321. #flow {
  322. position: relative;
  323. width: 100%;
  324. height: 100%;
  325. .auxiliary-line-x {
  326. position: absolute;
  327. border: 0.5px dashed #2ab1e8;
  328. z-index: 9999;
  329. }
  330. .auxiliary-line-y {
  331. position: absolute;
  332. border: 0.5px dashed #2ab1e8;
  333. z-index: 9999;
  334. }
  335. }
  336. }
  337. }
  338. ::v-deep.el-scrollbar .el-scrollbar__wrap {
  339. overflow-x: hidden;
  340. }
  341. </style>
  342. <style lang="less">
  343. .jtk-connector.active {
  344. z-index: 9999;
  345. path {
  346. stroke: #150042;
  347. stroke-width: 1.5;
  348. animation: ring;
  349. animation-duration: 3s;
  350. animation-timing-function: linear;
  351. animation-iteration-count: infinite;
  352. stroke-dasharray: 5;
  353. }
  354. }
  355. @keyframes ring {
  356. from {
  357. stroke-dashoffset: 50;
  358. }
  359. to {
  360. stroke-dashoffset: 0;
  361. }
  362. }
  363. </style>