DrawDesigns.vue 27 KB

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