index.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663
  1. <template>
  2. <svg
  3. id="svgContent"
  4. :style="{cursor: this.currentEvent === 'move_graph' ? 'grabbing' : 'grab'}"
  5. xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="100%"
  6. height="1029" data-spm-anchor-id="TODO.11007039.0.i6.12b64a9bcbXQmm"
  7. @mousedown="svgMouseDown"
  8. @mousemove="dragIng($event)"
  9. @mouseleave="atMouseOut"
  10. @mouseup="dragEnd($event)">
  11. <g :transform="` translate(${svg_left}, ${svg_top}) scale(${svgScale})`">
  12. <!-- 节点主体 -->
  13. <g
  14. v-for="(item, i) in DataAll.nodes"
  15. :key="'_' + i" class="svgEach"
  16. :transform="`translate(${item.pos_x}, ${item.pos_y})`"
  17. @contextmenu="r_click_nodes($event, i)"
  18. @dblclick="focusInput($event.path[0])"
  19. @mousedown="dragPre($event, i, item)">
  20. <main-body
  21. :item="item"
  22. :i="i"
  23. :choice="choice"
  24. :currentEvent="currentEvent"
  25. @nodesPersonalEvent="nodesPersonalEvent"
  26. @changeNodeName="changeNodeName"
  27. @linkEnd="linkEnd"
  28. @linkPre="linkPre"
  29. ></main-body>
  30. </g>
  31. <Arrow v-for="(each, n) in DataAll.edges" :key="'____' + n" :DataAll="DataAll" @delEdge="delEdge" :each="each"
  32. :index="n"/>
  33. <SimulateArrow v-if="currentEvent === 'dragLink'" :dragLink="dragLink"/>
  34. <SimulateSelArea v-if="['sel_area', 'sel_area_ing'].indexOf(currentEvent) !== -1"
  35. :simulate_sel_area="simulate_sel_area"/>
  36. </g>
  37. <EditArea @editNodeDetails="editNodeDetails" :isEditAreaShow="is_edit_area" @nodesPersonalEvent="nodesPersonalEvent"
  38. @delNode="delNode" @changePort="changePort" @close_click_nodes="close_click_nodes"/>
  39. <Control @changeModelRunningStatus="changeModelRunningStatus" @sizeInit="sizeInit" @sizeExpend="sizeExpend"
  40. @sizeShrink="sizeShrink" @sel_area="sel_area" :modelRunningStatus="modelRunningStatus"
  41. :currentEvent="currentEvent"/>
  42. </svg>
  43. </template>
  44. <script>
  45. import Arrow from "./arrow.vue";
  46. import SimulateArrow from "./simulateArrow.vue";
  47. import SimulateFrame from "./simulateFrame.vue";
  48. import EditArea from "./editArea.vue";
  49. import Control from "./control.vue";
  50. import SimulateSelArea from "./simulateSelArea.vue";
  51. import mainBody from './mainBody.vue'
  52. export default {
  53. name: 'DAGBoard',
  54. props: {
  55. DataAll: {
  56. type: Object,
  57. default: () => []
  58. }
  59. },
  60. computed: {
  61. svgScale() {
  62. return this.svg_scale || !!sessionStorage['svgScale'] ? sessionStorage['svgScale'] : 1
  63. }
  64. },
  65. created() {
  66. this.$nextTick(() => {
  67. this.setMouseWheelEvent()
  68. })
  69. },
  70. mounted() {
  71. sessionStorage["svg_left"] = 0;
  72. sessionStorage["svg_top"] = 0;
  73. },
  74. methods: {
  75. startActive() {
  76. // 激活图像状态变更
  77. let step = this.step
  78. if (step === this.historyList.length || step > this.historyList.length || !this.modelRunningStatus) return false
  79. this.activeGraph(step)
  80. if (this.nextStep) {
  81. clearTimeout(this.nextStep)
  82. }
  83. this.nextStep = setTimeout(() => {
  84. this.step++
  85. this.startActive()
  86. }, (this.historyList[step + 1].time - this.historyList[step].time) * 1000)
  87. },
  88. atMouseOut() {
  89. // 鼠标移出
  90. this.currentEvent = null
  91. },
  92. /**
  93. * 事件分发器
  94. */
  95. dragPre(e, i, item) {
  96. // 准备拖动节点
  97. this.$parent.dragPre2(item) //将点击获取到的元素传给父组件
  98. this.multipleSelectNodes = JSON.parse(JSON.stringify(this.choice))
  99. if (this.multipleSelectNodes && this.multipleSelectNodes.paneNode.length) {
  100. this.initMultiplePosition = JSON.parse(JSON.stringify(this.DataAll.nodes))
  101. }
  102. this.setInitRect(); // 工具类 初始化dom坐标
  103. this.currentEvent = "dragPane"; // 修正行为
  104. sessionStorage['offsetX'] = e.offsetX
  105. sessionStorage['offsetY'] = e.offsetY
  106. this.choice.index = i; // 当前选择的接点
  107. this.timeStamp = e.timeStamp;
  108. this.selPaneNode(item.id);
  109. this.setDragFramePosition(e);
  110. e.preventDefault();
  111. e.stopPropagation();
  112. e.cancelBubble = true;
  113. },
  114. dragIng(e) {
  115. // 事件发放器 根据currentEvent来执行系列事件
  116. switch (this.currentEvent) {
  117. case 'dragPane':
  118. this.paneDragIng(e);
  119. break;
  120. // case 'PaneDraging':
  121. // this.setDragFramePosition(e); // 触发节点拖动
  122. // break;
  123. case 'dragLink':
  124. this.setDragLinkPostion(e); // 触发连线拖动
  125. break;
  126. case 'sel_area_ing':
  127. this.setSelAreaPostion(e); // 触发框选
  128. break;
  129. case 'move_graph':
  130. this.graphMoveIng(e);
  131. break;
  132. default:
  133. () => {
  134. }
  135. }
  136. },
  137. dragEnd(e) {
  138. // 拖动结束
  139. switch (this.currentEvent) {
  140. // case 'PaneDraging': this.paneDragEnd(e); // 触发节点拖动结束
  141. // break;
  142. case 'sel_area_ing':
  143. this.getSelNodes(this.simulate_sel_area);
  144. break;
  145. default:
  146. () => {
  147. }
  148. }
  149. this.currentEvent = null;
  150. },
  151. svgMouseDown(e) {
  152. // svg鼠标按下触发事件分发
  153. this.setInitRect();
  154. if (this.currentEvent === "sel_area") {
  155. this.selAreaStart(e);
  156. } else {
  157. // 那就拖动画布
  158. this.currentEvent = "move_graph";
  159. this.graphMovePre(e);
  160. }
  161. },
  162. /**
  163. * 连线系列事件
  164. */
  165. linkPre(e, i, nth) {
  166. // 开始连线
  167. this.setInitRect();
  168. this.currentEvent = "dragLink";
  169. this.choice = Object.assign({}, this.choice, {index: i, point: nth});
  170. this.setDragLinkPostion(e, true);
  171. e.preventDefault();
  172. e.stopPropagation();
  173. },
  174. linkEnd(i, nth) {
  175. // 连线结束 i, 目标点序号 nth 出发点 choice.index 出发点序号 choice.point
  176. if (this.currentEvent === "dragLink") {
  177. let params = {
  178. desp: {
  179. src_node_id: this.DataAll.nodes[this.choice.index].id,
  180. src_output_idx: this.choice.point,
  181. dst_node_id: this.DataAll.nodes[i].id,
  182. dst_input_idx: nth
  183. }
  184. };
  185. const that = this
  186. this.addEdge(params, that);
  187. }
  188. this.currentEvent = null;
  189. },
  190. /**
  191. * svg画板缩放行为
  192. */
  193. sizeInit() {
  194. this.changeSize("init"); // 回归到默认倍数
  195. this.svg_left = 0; // 回归到默认位置
  196. this.svg_top = 0;
  197. sessionStorage["svg_left"] = 0;
  198. sessionStorage["svg_top"] = 0;
  199. },
  200. sizeExpend() {
  201. this.changeSize("expend"); // 画板放大0.1
  202. },
  203. sizeShrink() {
  204. this.changeSize("shrink"); // 画板缩小0.1
  205. },
  206. onMouseWheel(e) { // 鼠标滚动或mac触摸板可以改变size
  207. if (!e) return false
  208. let multiple = (e.wheelDelta / 10)
  209. if (this.canMouseWheelUse && (multiple * multiple) > 1) {
  210. multiple > 0
  211. ? this.sizeExpend()
  212. : this.sizeShrink()
  213. this.canMouseWheelUse = false
  214. setTimeout(() => { // 节流
  215. this.canMouseWheelUse = true
  216. }, 50)
  217. }
  218. },
  219. setMouseWheelEvent() { // 绑定鼠标滚轮事件
  220. const addEvent = (obj, xEvent, fn) => {
  221. if (obj.attachEvent) {
  222. obj.attachEvent('on' + xEvent, fn);
  223. } else {
  224. obj.addEventListener(xEvent, fn, false);
  225. }
  226. }
  227. var oDiv = document.getElementById('svgContent');
  228. // 当滚轮事件发生时,执行onMouseWheel这个函数
  229. addEvent(oDiv, 'mousewheel', this.onMouseWheel);
  230. addEvent(oDiv, 'DOMMouseScroll', this.onMouseWheel);
  231. },
  232. /**
  233. * 节点事件 单选 框选 拖动
  234. */
  235. sel_area() {
  236. this.currentEvent = "sel_area";
  237. this.simulate_sel_area = {
  238. left: 0,
  239. top: 0,
  240. width: 0,
  241. height: 0
  242. };
  243. },
  244. paneDragIng(e) {
  245. let offsetX = sessionStorage['offsetX'] || 0
  246. let offsetY = sessionStorage['offsetY'] || 0
  247. const x =
  248. (e.x - this.initPos.left - (sessionStorage["svg_left"] || 0)) /
  249. this.svgScale - 30 - offsetX
  250. const y =
  251. (e.y - this.initPos.top - (sessionStorage["svg_top"] || 0)) /
  252. this.svgScale - offsetY;
  253. let params = {
  254. model_id: sessionStorage["newGraph"],
  255. id: this.DataAll.nodes[this.choice.index].id,
  256. pos_x: x,
  257. pos_y: y
  258. };
  259. if (this.multipleMoveNode && this.multipleSelectNodes.paneNode.length > 1) {
  260. this.multipleMoveNode(params)
  261. } else {
  262. this.moveNode(params);
  263. }
  264. },
  265. paneDragEnd(e) {
  266. // 节点拖动结束
  267. this.multipleSelectNodes = {}
  268. this.initMultiplePosition = null
  269. this.dragFrame = {dragFrame: false, posX: 0, posY: 0};
  270. const x =
  271. (e.x - this.initPos.left - (sessionStorage["svg_left"] || 0)) /
  272. this.svgScale -
  273. 90;
  274. const y =
  275. (e.y - this.initPos.top - (sessionStorage["svg_top"] || 0)) /
  276. this.svgScale -
  277. 15;
  278. let params = {
  279. model_id: sessionStorage["newGraph"],
  280. id: this.DataAll.nodes[this.choice.index].id,
  281. pos_x: x,
  282. pos_y: y
  283. };
  284. },
  285. selPaneNode(id) {
  286. // 单选节点
  287. this.choice.paneNode.length = [];
  288. if (id) {
  289. this.choice.paneNode.push(id);
  290. }
  291. },
  292. selAreaStart(e) {
  293. // 框选节点开始
  294. this.currentEvent = "sel_area_ing";
  295. const x =
  296. (e.x - this.initPos.left - (sessionStorage["svg_left"] || 0)) /
  297. this.svgScale;
  298. const y =
  299. (e.y - this.initPos.top - (sessionStorage["svg_top"] || 0)) /
  300. this.svgScale;
  301. this.simulate_sel_area = {
  302. left: x,
  303. top: y,
  304. width: 0,
  305. height: 0
  306. };
  307. },
  308. setSelAreaPostion(e) {
  309. // 框选节点ing
  310. const x =
  311. (e.x - this.initPos.left - (sessionStorage["svg_left"] || 0)) /
  312. this.svgScale;
  313. const y =
  314. (e.y - this.initPos.top - (sessionStorage["svg_top"] || 0)) /
  315. this.svgScale;
  316. const width = x - this.simulate_sel_area.left;
  317. const height = y - this.simulate_sel_area.top;
  318. this.simulate_sel_area.width = width;
  319. this.simulate_sel_area.height = height;
  320. },
  321. getSelNodes(postions) {
  322. // 选取框选的节点
  323. const {left, top, width, height} = postions;
  324. this.choice.paneNode.length = 0;
  325. this.DataAll.nodes.forEach(item => {
  326. if (
  327. item.pos_x > left &&
  328. item.pos_x < left + width &&
  329. item.pos_y > top &&
  330. item.pos_y < top + height
  331. ) {
  332. // set the select nodes into this.data.choice
  333. this.choice.paneNode.push(item.id);
  334. }
  335. });
  336. this.simulate_sel_area = {
  337. // 触发框选结束
  338. left: 0,
  339. top: 0,
  340. width: 0,
  341. height: 0
  342. };
  343. },
  344. focusInput(el) { // 双击选中input
  345. if (el) {
  346. el.focus()
  347. }
  348. },
  349. /**
  350. * 画布拖动
  351. */
  352. graphMovePre(e) {
  353. const {x, y} = e;
  354. this.svg_trans_init = {x, y};
  355. this.svg_trans_pre = {x: this.svg_left, y: this.svg_top};
  356. },
  357. graphMoveIng(e) {
  358. const {x, y} = this.svg_trans_init;
  359. this.svg_left = e.x - x + this.svg_trans_pre.x;
  360. this.svg_top = e.y - y + this.svg_trans_pre.y;
  361. sessionStorage["svg_left"] = this.svg_left;
  362. sessionStorage["svg_top"] = this.svg_top;
  363. },
  364. /**
  365. * 模态框类
  366. */
  367. setDragFramePosition(e) {
  368. // 节点拖拽模态
  369. const x = e.x - this.initPos.left - (sessionStorage["svg_left"] || 0);
  370. const y = e.y - this.initPos.top - (sessionStorage["svg_top"] || 0);
  371. this.dragFrame = {
  372. posX: x / this.svgScale - 90,
  373. posY: y / this.svgScale - 15
  374. };
  375. },
  376. setDragLinkPostion(e, init) {
  377. // 节点连线模态
  378. const x =
  379. (e.x - this.initPos.left - (sessionStorage["svg_left"] || 0)) /
  380. this.svgScale;
  381. const y =
  382. (e.y - this.initPos.top - (sessionStorage["svg_top"] || 0)) /
  383. this.svgScale;
  384. if (init) {
  385. this.dragLink = Object.assign({}, this.dragLink, {
  386. fromX: x,
  387. fromY: y
  388. });
  389. }
  390. this.dragLink = Object.assign({}, this.dragLink, {toX: x, toY: y});
  391. },
  392. close_click_nodes() {
  393. // 关闭模态
  394. this.is_edit_area = {value: false, x: -9999, y: -9999};
  395. },
  396. r_click_nodes(e, i) {
  397. // 节点右键模态
  398. this.setInitRect();
  399. const id = this.DataAll.nodes[i].id;
  400. const detail = this.DataAll.nodes[i].detail || null
  401. const rightClickEvent = this.DataAll.nodes[i].rightClickEvent || null
  402. const x = e.x - this.initPos.left;
  403. const y = e.y - this.initPos.top;
  404. this.is_edit_area = {
  405. value: true,
  406. x,
  407. y,
  408. id,
  409. detail,
  410. rightClickEvent
  411. };
  412. e.stopPropagation();
  413. e.cancelBubble = true;
  414. e.preventDefault();
  415. },
  416. /**
  417. * 工具类
  418. */
  419. setInitRect() {
  420. // 矫正svg组件坐标
  421. let {left, top} = document
  422. .getElementById("svgContent")
  423. .getBoundingClientRect();
  424. this.initPos = {left, top};
  425. },
  426. /**
  427. * 执行&暂停模型训练模拟
  428. */
  429. changeModelRunningStatus(status) {
  430. this.$emit('updateDAG', this.DataAll, 'startRunning')
  431. },
  432. /**
  433. * 数据层逻辑
  434. */
  435. // 模型激活
  436. activeGraph: ({commit, state}, step) => {
  437. // const action = state.historyList[step].action
  438. // commit('ACTIVE_DATA', action)
  439. },
  440. // 模型暂停
  441. stopGraph: ({commit, state}) => {
  442. // commit('STOP_DATA')
  443. },
  444. addEdge: (value, that) => {
  445. // 增加边
  446. let _DataAll = that.DataAll
  447. _DataAll.edges.push({
  448. ...value.desp,
  449. id: _DataAll.edges.length + 10
  450. })
  451. that.$emit('updateDAG', _DataAll, 'addEdge')
  452. },
  453. delEdge({id}) {
  454. // 删除边
  455. console.log('id', id)
  456. let _edges = []
  457. this.DataAll.edges.forEach((item, i) => {
  458. if (item.id !== id) {
  459. _edges.push(item)
  460. }
  461. })
  462. this.DataAll.edges = _edges
  463. this.$emit('updateDAG', this.DataAll, 'delEdge')
  464. },
  465. moveNode(params) {
  466. // 移动点的位置
  467. const {id, pos_x, pos_y} = params
  468. let _DataAll = this.DataAll
  469. _DataAll.nodes.forEach((item, i) => {
  470. if (item.id === params.id) {
  471. item.pos_x = params.pos_x
  472. item.pos_y = params.pos_y
  473. }
  474. })
  475. this.$emit('updateDAG', _DataAll, 'moveNode')
  476. },
  477. multipleMoveNode(params) {
  478. // 同时移动多个点
  479. const {id, pos_x, pos_y} = params
  480. let x = 0
  481. let y = 0
  482. let _initMultiplePosition = this.initMultiplePosition
  483. let _DataAll = this.DataAll
  484. _initMultiplePosition.map(item => {
  485. if (item.id === id) {
  486. x = pos_x - item.pos_x
  487. y = pos_y - item.pos_y
  488. }
  489. })
  490. _initMultiplePosition.forEach(item => {
  491. if (this.multipleSelectNodes.paneNode.indexOf(item.id) > -1) {
  492. item.pos_x += x
  493. item.pos_y += y
  494. }
  495. })
  496. _DataAll.nodes = _initMultiplePosition
  497. this.$emit('updateDAG', _DataAll, 'moveNode')
  498. },
  499. addNode: (params) => {
  500. // 增加节点
  501. let _nodes = this.DataAll.nodes
  502. _nodes.push({
  503. ...params.desp,
  504. id: this.DataAll.nodes.length + 100,
  505. in_ports: [0],
  506. out_ports: [0]
  507. })
  508. this.$emit('updateDag', this.DataAll, 'addNode')
  509. },
  510. delNode({model_id, id}) {
  511. // 删除节点
  512. let _edges = []
  513. let _nodes = []
  514. this.DataAll.edges.forEach(item => {
  515. if (item.dst_node_id !== id && item.src_node_id !== id) {
  516. _edges.push(item)
  517. }
  518. })
  519. this.DataAll.nodes.forEach(item => {
  520. if (item.id !== id) {
  521. _nodes.push(item)
  522. }
  523. })
  524. this.DataAll.edges = _edges
  525. this.DataAll.nodes = _nodes
  526. this.$emit('updateDAG', this.DataAll, 'delNode')
  527. },
  528. changeSize(action) {
  529. // 改变size
  530. let svgScale = typeof sessionStorage['svgScale'] === 'string' ? Number(sessionStorage['svgScale']) : 1
  531. let _width = window.innerWidth
  532. let _height = window.innerHeight
  533. switch (action) {
  534. case 'shrink':
  535. svgScale -= 0.05;
  536. this.svg_left = sessionStorage['svg_left'] = this.svg_left + _width * 0.01
  537. this.svg_top = sessionStorage['svg_top'] = this.svg_top + _height * 0.01
  538. break;
  539. case 'expend':
  540. svgScale += 0.05;
  541. this.svg_top = sessionStorage['svg_top'] = this.svg_top - _height * 0.01
  542. this.svg_left = sessionStorage['svg_left'] = this.svg_left - _width * 0.01
  543. break;
  544. case 'init':
  545. svgScale = 1
  546. break;
  547. default:
  548. () => ''
  549. }
  550. this.svg_scale = sessionStorage['svgScale'] = svgScale
  551. },
  552. changeNodeName(params) {
  553. // 改变节点名称
  554. this.DataAll.nodes.forEach(item => {
  555. if (item.id === params.id) {
  556. item.name = params.name
  557. }
  558. })
  559. this.$emit('updateDAG', this.DataAll, 'changeNodeName')
  560. },
  561. changePort(action, id) {
  562. this.DataAll.nodes.forEach(item => {
  563. if (item.id === id) {
  564. item[action] ? item[action].push(item[action].length) : item[action] = ['0']
  565. }
  566. })
  567. this.$emit('updateDAG', this.DataAll, 'changePort')
  568. },
  569. editNodeDetails(value) {
  570. // 抛出待编辑内容
  571. this.$emit('editNodeDetails', value)
  572. },
  573. nodesPersonalEvent(eventName, id) {
  574. this.$emit('doSthPersonal', eventName, id)
  575. }
  576. },
  577. data() {
  578. return {
  579. svg_scale: null,
  580. choice: {
  581. paneNode: [], // 选取的节点下标组
  582. index: -1,
  583. point: -1 // 选取的点数的下标
  584. },
  585. dragFrame: {
  586. // 节点模态
  587. posX: 9999,
  588. posY: 9999
  589. },
  590. dragLink: {
  591. // 连线模态
  592. fromX: 0,
  593. fromY: 0,
  594. toX: 0,
  595. toY: 0
  596. },
  597. currentEvent: null, // 当前执行的方法
  598. initPos: {
  599. // 初始化svg dom位置
  600. left: -1,
  601. top: -1
  602. },
  603. timeStamp: "",
  604. is_edit_area: {
  605. value: false,
  606. x: -9999,
  607. y: -9999
  608. }, // 是否在编辑节点
  609. simulate_sel_area: {
  610. // 框选节点
  611. left: 10,
  612. top: 50,
  613. width: 0,
  614. height: 0
  615. },
  616. svg_left: 0,
  617. svg_top: 0,
  618. svg_trans_init: {
  619. x: 0,
  620. y: 0
  621. },
  622. canMouseWheelUse: true,
  623. step: 0, // 模型训练计步
  624. modelRunningStatus: false,
  625. nextStep: null,
  626. multipleSelectNodes: {},
  627. initMultiplePosition: {}
  628. };
  629. },
  630. components: {
  631. Arrow,
  632. SimulateArrow,
  633. // SimulateFrame,
  634. EditArea,
  635. Control,
  636. SimulateSelArea,
  637. mainBody
  638. }
  639. };
  640. </script>
  641. <style scoped>
  642. .svgEach {
  643. position: relative;
  644. }
  645. .connector {
  646. stroke: hsla(0, 0%, 50%, 0.6);
  647. stroke-width: 2px;
  648. fill: none;
  649. }
  650. .simulate_sel_area {
  651. border: 3px dashed blue;
  652. position: absolute;
  653. }
  654. </style>