DrawDesigns.vue 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667
  1. <template>
  2. <div>
  3. <div class="bigBox">
  4. <div ref="container" class="container" v-loading="loading"></div>
  5. </div>
  6. <!-- 功能模态框 -->
  7. <AbilityModal v-if="abModal" :fun="fun" :funOne="funOne" :listMap="listMap" :abModal="abModal"
  8. :modelId="modelId" :iedType="iedType" :nodeid="nodeid" @abilityBack="abilityBack">
  9. </AbilityModal>
  10. <LineDouble v-if="ldModal" :ldModal="ldModal" :modelId="modelId" :startTarget="startTarget"
  11. :endTarget="endTarget" :numCase="numCase" :startText="startText" :endText="endText" :lineType="lineType"
  12. @lineBack="lineBack">
  13. </LineDouble>
  14. <GroupModelAdd v-if="groupType" :groupType="groupType" :modelId="modelId" @groupBack="groupBack"></GroupModelAdd>
  15. </div>
  16. </template>
  17. <script>
  18. import { ref, onMounted, watch, onBeforeUnmount } from 'vue';
  19. import LogicFlow from "@logicflow/core";
  20. import { PolylineEdge, PolylineEdgeModel } from "@logicflow/core";
  21. import cid from '@/api/cid/cid'
  22. import { ElMessage } from 'element-plus';
  23. import { useRouter, useRoute } from 'vue-router';
  24. import AbilityModal from "../drawModal/AbilityModal.vue"
  25. import LineDouble from '../drawModal/LineDouble.vue';
  26. import GroupModelAdd from '../drawModal/GroupModelAdd.vue';
  27. import {
  28. DndPanel,
  29. SelectionSelect,
  30. Group,
  31. Menu,
  32. MiniMap,
  33. regeister,
  34. Snapshot,
  35. lfJson2Xml,
  36. lfXml2Json,
  37. RectResize,
  38. NodeResize,
  39. GroupNode,
  40. groupModel,
  41. } from "@logicflow/extension";
  42. import "@logicflow/core/dist/style/index.css";
  43. import "@logicflow/extension/lib/style/index.css";
  44. export default {
  45. props: {
  46. lineMenuColor: {
  47. type: String,
  48. required: true
  49. },
  50. fatX: {
  51. type: Number,
  52. required: true
  53. },
  54. fatY: {
  55. type: Number,
  56. required: true
  57. },
  58. nowLook: {
  59. type: null || Number,
  60. required: true
  61. },
  62. needObj: {
  63. type: Object,
  64. required: true
  65. },
  66. needId: {
  67. type: Number || String,
  68. required: true
  69. },
  70. needName: {
  71. type: String,
  72. required: true
  73. },
  74. coolObj: {
  75. type: Object,
  76. required: true
  77. },
  78. // 用于判断是sv还是goose
  79. svOrGoose: {
  80. type: Number,
  81. required: true,
  82. },
  83. searchModule: {
  84. type: Function,
  85. required: true
  86. },//父组件insideModule的方法
  87. cleanAll: {
  88. type: Function,
  89. required: true
  90. },//父组件insideModule的方法
  91. },
  92. setup(props, { emit }) {
  93. let router = useRouter()
  94. let route = useRoute()
  95. const container = ref();
  96. const lf = ref();
  97. let lineColor = ref('#255CE7')
  98. let copyColor = ref('')
  99. let delId = ref('')
  100. let nodeId = ref('')
  101. let copyId = ref('')
  102. let setX = ref(0)
  103. let setY = ref(0)
  104. let needMap = ref([])
  105. let coolId = ref('')
  106. let coolName = ref('')
  107. let copyObj = ref({})//整个模型属性
  108. let modelId = ref("")//模型id
  109. let abModal = ref(false)//功能模态框
  110. let iedType = ref("")//iedtype
  111. let ldModal = ref(false)//linemodel.vue开关
  112. let startTarget = ref({})//开始节点
  113. let endTarget = ref({})//结束节点
  114. let numCase = ref(1)//判断sv还是goose
  115. let startText = ref("")//开始节点名称
  116. let endText = ref("")//结束节点名称
  117. let lineType = ref("")//连接线
  118. let loading = ref(false)
  119. let nodeid = ref("")//节点的id
  120. let fun = ref()//储存父节点传来的函数
  121. let funOne = ref()//储存父节点传来的函数
  122. let groupType = ref(false)//GroupModelAdd.vue组件显示与否
  123. watch(() => props.lineMenuColor, (newVal) => {
  124. copyColor.value = newVal
  125. })
  126. watch(() => props.fatX, (newVal) => {
  127. if (newVal == 0 || newVal == '') {
  128. setX.value = 0
  129. } else {
  130. setX.value = newVal
  131. }
  132. })
  133. watch(() => props.fatY, (newVal) => {
  134. if (newVal == 0 || newVal == '') {
  135. setY.value = 0
  136. } else {
  137. setY.value = newVal
  138. }
  139. })
  140. // watch(() => props.needObj, (newVal) => {
  141. // coolObj.value = newVal
  142. // })
  143. watch(() => props.needId, (newVal) => {
  144. coolId.value = newVal
  145. })
  146. watch(() => props.needName, (newVal) => {
  147. coolName.value = newVal
  148. })
  149. watch(() => props.coolObj, (newVal) => {
  150. if (route.query.modelid != '' && route.query.modelid != null && route.query.modelid != undefined) {
  151. console.log('路由跳转watch');
  152. return
  153. } else {
  154. console.log('普通watch');
  155. loading.value = true//打开加载动画
  156. copyObj.value = newVal
  157. modelId.value = copyObj.value.id//模型id
  158. setTimeout(() => {
  159. cid.getModelInfo({ id: copyObj.value.id, pageno: 1, pagesize: 20 }).then(res => {
  160. if (res.data[0].relation_json == null || res.data[0].relation_json == '') return;
  161. const origiondata = JSON.parse(res.data[0].relation_json)
  162. lf.value.render(origiondata);//渲染模型图
  163. loading.value = false//关闭加载动画
  164. for (let index = 0; index < origiondata.edges.length; index++) {
  165. const element = origiondata.edges[index];
  166. if (element.properties != null && element.properties["issv"] === 'SV') {
  167. let lineStyle = lf.value.getEdgeModelById(element.id)//.getEdgeStyle();
  168. // console.log(lineStyle)
  169. lineStyle.setProperties({ svEdge: { stroke: 'orange' } })
  170. }
  171. }
  172. })
  173. }, 2000);
  174. }
  175. }, {
  176. deep: true
  177. })
  178. watch(() => props.svOrGoose, (newVal) => {
  179. numCase.value = newVal
  180. })
  181. function blue() {
  182. lf.value.setTheme({
  183. baseEdge: {
  184. stroke: '#255CE7',
  185. strokeWidth: 2,
  186. },
  187. });//设置连接线为蓝色
  188. }
  189. function orange() {
  190. lf.value.setTheme({
  191. svEdge: {
  192. stroke: 'orange',
  193. strokeWidth: 2,
  194. },
  195. });//设置连接线为橙色
  196. }
  197. function delLine() {
  198. if (delId.value) {
  199. lf.value.deleteEdge(delId.value)//删除选择的连接线
  200. }
  201. if (nodeId.value) {
  202. lf.value.deleteNode(nodeId.value)//删除节点
  203. }
  204. }
  205. function saveLine() {
  206. // lf.value.getSnapshot()//保存为图片
  207. console.log(JSON.stringify(lf.value.getGraphData()))
  208. const xml = lfJson2Xml(lf.value.getGraphData());
  209. const data = JSON.stringify(lf.value.getGraphData())
  210. if (modelId.value != undefined && modelId.value != null && modelId.value != '') {
  211. cid.saveMap(
  212. {
  213. id: modelId.value - 0,
  214. relation_json: data,
  215. }
  216. ).then(res => {
  217. if (res.code == 0) {
  218. ElMessage({
  219. type: "success",
  220. message: "保存成功!",
  221. duration: 2000,
  222. })
  223. } else {
  224. ElMessage({
  225. message: res.msg,
  226. type: "error"
  227. })
  228. }
  229. })
  230. } else {
  231. cid.saveMap(
  232. {
  233. id: copyObj.value.id - 0,
  234. relation_json: data,
  235. }
  236. ).then(res => {
  237. if (res.code == 0) {
  238. ElMessage({
  239. type: "success",
  240. message: "保存成功!",
  241. duration: 2000,
  242. })
  243. } else {
  244. ElMessage({
  245. message: res.msg,
  246. type: "error"
  247. })
  248. }
  249. })
  250. }
  251. }
  252. function cleanMap() {
  253. lf.value.clearData()
  254. }
  255. function textColor() {
  256. if (lineColor.value == "#255CE7") {
  257. lf.value.setTheme({
  258. svEdge: {
  259. stroke: "orange",
  260. strokeWidth: 2,
  261. },
  262. });
  263. lineColor.value = 'orange'
  264. } else {
  265. lf.value.setTheme({
  266. baseEdge: {
  267. stroke: "#255CE7",
  268. strokeWidth: 2,
  269. },
  270. });
  271. lineColor.value = '#255CE7'
  272. }
  273. }
  274. function momal() {
  275. lf.value.graphModel.moveNode2Coordinate(copyId.value, setX.value - 0, setY.value - 0, true);
  276. }
  277. function lastRender() {
  278. loading.value = true
  279. modelId.value = route.query.modelid
  280. cid.getModelInfo({ id: modelId.value - 0, pageno: 1, pagesize: 20 }).then(res => {
  281. if (res.data[0].relation_json == null || res.data[0].relation_json == '') return;
  282. const origiondata = JSON.parse(res.data[0].relation_json)
  283. lf.value.render(origiondata);
  284. loading.value = false
  285. for (let index = 0; index < origiondata.edges.length; index++) {
  286. const element = origiondata.edges[index];
  287. if (element.properties != null && element.properties["issv"] === 'SV') {
  288. let lineStyle = lf.value.getEdgeModelById(element.id)
  289. lineStyle.setProperties({ svEdge: { stroke: 'orange' } })
  290. }
  291. }
  292. }, { immediate: true })
  293. }
  294. function listMap() {
  295. fun.value = props.searchModule
  296. funOne.value = props.cleanAll
  297. coolId.value = props.needId
  298. coolName.value = props.needName//模型名称
  299. copyObj.value = props.coolObj//需要的模型信息对象
  300. numCase.value = props.svOrGoose//判断sv或者goose类型
  301. copyColor.value = props.lineMenuColor//颜色
  302. setTimeout(() => {
  303. console.log('定时器执行');
  304. cid.getModelInfo({ id: copyObj.value.id ? copyObj.value.id : route.query.modelid - 0, pageno: 1, pagesize: 20 }).then(res => {
  305. emit("drawCor", res.data)
  306. const filteredArray = res.data[0].ied_type.filter(param => param !== null)
  307. let groups = {
  308. type: "my-group",
  309. label: "分组",
  310. id: "952",
  311. resizable: true,//手动调整大小
  312. foldable: true,//展开收起
  313. children: [],
  314. width: 5,
  315. height: 5,
  316. isShowAnchor: true,//锚点
  317. }
  318. needMap.value = filteredArray.map(item => {
  319. return {
  320. type: 'rect',
  321. text: item.name,
  322. label: item.name,
  323. icon: '',
  324. id: item.id,
  325. properties: {
  326. ied_type: item.code,
  327. id: item.id
  328. },
  329. }
  330. })
  331. needMap.value.push(groups)
  332. lf.value = new LogicFlow({
  333. // 通过选项指定了渲染的容器和需要显示网格
  334. container: container.value,//需要显示画布的容器ref
  335. // grid: {
  336. // size: 10,
  337. // visible: false,
  338. // type: "mesh",
  339. // config: {
  340. // color: "#ababab",
  341. // thickness: 1,
  342. // },
  343. // },//网格,
  344. grid: true,
  345. plugins: [DndPanel, SelectionSelect, Group,GroupNode, Menu, MiniMap, Snapshot, RectResize, NodeResize],//全局加载的组件
  346. keyboard: {
  347. enabled: true
  348. },
  349. snapline: true,//辅助线
  350. edgeTextDraggable: true,//连接线文本可以拖拽
  351. })
  352. class SvEdge extends PolylineEdge { }
  353. // 节点Model
  354. class SvEdgeModel extends PolylineEdgeModel {
  355. getEdgeStyle() {
  356. const stl = super.getEdgeStyle()
  357. stl.stroke = 'orange'
  358. return stl
  359. }
  360. }
  361. class GooseEdge extends PolylineEdge { }
  362. // 节点Model
  363. class GooseEdgeModel extends PolylineEdgeModel {
  364. getEdgeStyle() {
  365. const stl = super.getEdgeStyle()
  366. stl.stroke = '#255CE7'
  367. return stl
  368. }
  369. }
  370. class MyGroup extends GroupNode.view {
  371. }
  372. class MyGroupModel extends GroupNode.model {
  373. initNodeData(data) {
  374. data.text= ""//{"value":"未命名组","x":10,"y":5,"editable":true}
  375. data.id=new Date().getTime();
  376. super.initNodeData(data);
  377. //this.isRestrict = true;
  378. this.resizable = true;
  379. this.foldable = true;
  380. this.width = 200;
  381. this.height = 150;
  382. this.foldedWidth = 100;
  383. this.foldedHeight = 100;
  384. this.isShowAnchor = true;
  385. }
  386. getNodeStyle() {
  387. const stl = super.getNodeStyle()
  388. stl.strokeDasharray= '4 4'
  389. return stl
  390. }
  391. getAnchorStyle(anchorInfo) {
  392. const style = super.getAnchorStyle(anchorInfo);
  393. style.stroke = "rgb(24, 125, 255)";
  394. style.r = 5;
  395. style.hover.r = 8;
  396. style.hover.fill = "rgb(24, 125, 255)";
  397. style.hover.stroke = "rgb(24, 125, 255)";
  398. return style;
  399. }
  400. getTextStyle() {
  401. const style = super.getTextStyle();
  402. style.fontSize = 12;
  403. style.color="#666"
  404. return style;
  405. }
  406. }
  407. lf.value.register({
  408. type: "svEdge",
  409. view: SvEdge,
  410. model: SvEdgeModel,
  411. })
  412. lf.value.register({
  413. type: "baseEdge",
  414. view: GooseEdge,
  415. model: GooseEdgeModel,
  416. })
  417. lf.value.register({
  418. type: "my-group",
  419. model: MyGroupModel,
  420. view: MyGroup,
  421. });
  422. lf.value.setTheme({//设置画布
  423. rect: {
  424. fill: "#FFFFFF",
  425. stroke: "#255CE7",
  426. strokeWidth: 2,
  427. },//放置的元素
  428. snapline: {
  429. stroke: 'black', // 对齐线颜色
  430. strokeWidth: 1, // 对齐线宽度
  431. },
  432. edgeText: {
  433. textWidth: 100,
  434. overflowMode: "default",
  435. fontSize: 18,
  436. background: {
  437. fill: "#FFFFFF",
  438. },
  439. },//连接线w文字样式
  440. outline: {
  441. fill: "transparent",
  442. stroke: "#949494",
  443. strokeDasharray: "3,3",
  444. hover: {
  445. stroke: "#949494",
  446. },
  447. },
  448. anchor: {
  449. stroke: "#000000",
  450. fill: "#FFFFFF",
  451. r: 4,
  452. hover: {
  453. fill: "#949494",
  454. fillOpacity: 0.5,
  455. stroke: "#949494",
  456. r: 10,
  457. },
  458. },//锚点样式
  459. baseEdge: {
  460. stroke: "#255CE7",
  461. strokeWidth: 2,
  462. },//连接线颜色
  463. svEdge: {
  464. stroke: "orange",
  465. strokeWidth: 1,
  466. },//连接线颜色
  467. nodeText: {
  468. color: "#255CE7",
  469. overflowMode: "autoWrap",
  470. lineHeight: 1.2,
  471. fontSize: 12,
  472. },//节点内文字样式
  473. });
  474. lf.value.on("edge:click", function (data, e, position) {
  475. delId.value = data.data.id
  476. // if (copyColor.value == '#255CE7') {
  477. // lf.value.graphModel.updateText(data.data.id, "GOOSE");
  478. // } else if (copyColor.value == 'orange') {
  479. // lf.value.graphModel.updateText(data.data.id, "SV");
  480. // }
  481. })
  482. lf.value.on("edge:add", function (data, e, position) {
  483. if (copyColor.value == '#255CE7') {
  484. blue()
  485. lf.value.changeEdgeType(data.data.id, "baseEdge")
  486. lf.value.setProperties(data.data.id, { 'issv': 'GOOSE' })
  487. }
  488. if (copyColor.value == 'orange') {
  489. orange()
  490. lf.value.changeEdgeType(data.data.id, "svEdge")
  491. lf.value.setProperties(data.data.id, { 'issv': 'SV' })
  492. }
  493. })
  494. lf.value.on('node:click', function (data, e, position) {//传送坐标轴参数到父组件
  495. copyId.value = data.data.id
  496. nodeId.value = data.data.id
  497. setX.value = data.data.x
  498. setY.value = data.data.y
  499. emit("backxy", setX.value, setY.value)
  500. })
  501. // lf.value.on('anchor:drop', function (data, e, position) {//添加线上文字
  502. // if (numCase.value == 0) {
  503. // lf.value.graphModel.updateText(data.edgeModel.id, "SV");
  504. // } else if (numCase.value == 1) {
  505. // lf.value.graphModel.updateText(data.edgeModel.id, "GOOSE");
  506. // }
  507. // })
  508. lf.value.on('node:dbclick', function (data, e, position) {//双击打开弹窗
  509. console.log(data, 'datas');
  510. iedType.value = data.data.properties.ied_type
  511. nodeid.value = data.data.properties.id
  512. abModal.value = true
  513. })
  514. lf.value.on('edge:dbclick', function (data, e, position) {//双击连接线
  515. lineType.value = data.data.properties.issv
  516. let start = lf.value.getNodeModelById(data.data.sourceNodeId)//获取连线开始节点
  517. let end = lf.value.getNodeModelById(data.data.targetNodeId)//获取连线结尾节点
  518. startText.value = start.text.value//开始文本
  519. endText.value = end.text.value//结束文本
  520. startTarget.value = start.properties
  521. endTarget.value = end.properties
  522. console.log(copyColor.value, 'copy');
  523. console.log(data, 'data');
  524. // if (copyColor.value == '#255CE7') {
  525. // lf.value.graphModel.updateText(data.data.id, "GOOSE");
  526. // } else if (copyColor.value == 'orange') {
  527. // lf.value.graphModel.updateText(data.data.id, "SV");
  528. // }
  529. ldModal.value = true
  530. })
  531. lf.value.on('node:mousemove', function (data, e) {//传递坐标轴参数到父组件
  532. setX.value = data.data.x
  533. setY.value = data.data.y
  534. console.log(data.data, 'move');
  535. emit("backxy", setX.value, setY.value)
  536. })
  537. // console.log(lf.value.graphModel,'sss');
  538. lf.value.on('edge:click', function (data, e, position) {//解决点击连接线问题
  539. console.log(data, 'data2');
  540. if (copyColor.value == '#255CE7') {
  541. blue()
  542. lf.value.changeEdgeType(data.data.id, "baseEdge")
  543. lf.value.setProperties(data.data.id, { 'issv': 'GOOSE' })
  544. }
  545. if (copyColor.value == 'orange') {
  546. orange()
  547. lf.value.changeEdgeType(data.data.id, "svEdge")
  548. lf.value.setProperties(data.data.id, { 'issv': 'SV' })
  549. }
  550. })
  551. //当将节点托入画布后触发事件
  552. lf.value.on('node:dnd-add',function (nodedata){
  553. console.log(nodedata,'node');
  554. if(nodedata.data.type=='my-group'){
  555. let nodeid=nodedata.data.id
  556. // 添加的是分组节点,需要打开一个窗口让用户设置该节点的ied_type并保存到properties中
  557. let ied_type="user set iedtype"
  558. lf.value.getNodeModelById(nodeid).setProperties({
  559. ied_type: ied_type,
  560. id:-99 //分组的装置类型id固定为0
  561. });
  562. groupType.value = true
  563. }
  564. })
  565. // lf.value.register(logicFlows)
  566. lf.value.extension.dndPanel.setPatternItems(needMap.value);
  567. lf.value.render();
  568. console.log('执行了渲染');
  569. if (route.query.modelid != '' && route.query.modelid != null && route.query.modelid != undefined) {
  570. lastRender()
  571. }
  572. setTimeout(() => {
  573. loading.value = false
  574. }, 1000)
  575. })
  576. }, 1000);
  577. }
  578. onMounted(() => {
  579. listMap()
  580. })
  581. function abilityBack(data) {
  582. abModal.value = data
  583. }
  584. function lineBack(data) {
  585. ldModal.value = data
  586. }
  587. // GroupModelAdd.vue返回状态
  588. function groupBack(data){
  589. groupType.value = data
  590. }
  591. onBeforeUnmount(() => {
  592. // lf.value.remove()
  593. // coolObj.value = {}
  594. router.push("/home/setting")
  595. })
  596. return {
  597. container,
  598. lf,
  599. textColor,
  600. lineColor,
  601. blue,
  602. orange,
  603. delLine,
  604. saveLine,
  605. cleanMap,
  606. nodeId,
  607. copyColor,
  608. copyId,
  609. setX,
  610. setY,
  611. momal,
  612. needMap,
  613. coolId,
  614. coolName,
  615. copyObj,//模型整个数据
  616. modelId,//模型id
  617. abModal,//功能模态框
  618. iedType,//模型属性
  619. abilityBack,//abilitymodal.vue返回模态框开关状态
  620. ldModal,//lineDouble.vue模态框开关
  621. startTarget,//开始节点
  622. endTarget,//结束节点
  623. lineBack,//LineDouble.vue返回模态框开关状态
  624. numCase,//判断是sv还是goose,0为sv,1为goose
  625. startText,//开始节点名称
  626. endText,//结束节点名称
  627. lineType,//连接线
  628. lastRender,//路由跳转的渲染
  629. loading,
  630. nodeid,//节点的id
  631. fun,//储存父节点传来的函数
  632. listMap,//组件初始化
  633. funOne,//储存父节点传来的函数
  634. groupType,//GroupModelAdd.vue组件显示与否
  635. groupBack,// GroupModelAdd.vue返回状态
  636. }
  637. },
  638. components: {
  639. AbilityModal,//功能模态框
  640. LineDouble,//连接线模态框
  641. GroupModelAdd,//分组专属模态框
  642. }
  643. }
  644. </script>
  645. <style scoped>
  646. .container {
  647. width: 100%;
  648. height: calc(100vh - 18rem);
  649. /* overflow: auto; */
  650. }
  651. :deep(.lf-element-text) {
  652. color: black;
  653. }
  654. /* :deep(.lf-dndpanel){
  655. w
  656. } */
  657. /* :deep(.lf-basic-shape) {
  658. width: 150px;
  659. height: 150px;
  660. } */
  661. </style>