index.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. <template>
  2. <div id="container" ref="container"></div>
  3. </template>
  4. <script>
  5. import Konva from 'konva'
  6. import {
  7. selectIsIsolationPointById,
  8. getIsIsolationPointPage
  9. } from '@/api/mes/spm/segregationPoint'
  10. import { mapGetters } from 'vuex'
  11. export default {
  12. name: 'KonvaExample',
  13. props: {
  14. points: {
  15. type: Object,
  16. default: null
  17. }
  18. },
  19. data() {
  20. return {
  21. stage: null,
  22. layer: null,
  23. selectedStates: [], // 用于存储每个元素的选中状态
  24. selectedText: [], // 用于存储未选中的元素文本
  25. rects: [], //白色rect合集
  26. texts: [], //白色text合集
  27. bgrects:[],//白色底部
  28. redrects: [], //红色rect合集
  29. redtexts: [], //白色text合集
  30. }
  31. },
  32. watch: {
  33. 'getMapData': {
  34. handler(newval) {
  35. if (newval) {
  36. this.$nextTick(()=>{
  37. this.initKonva()
  38. })
  39. }
  40. }
  41. },
  42. },
  43. created() {
  44. this.initKonva()
  45. },
  46. mounted() {
  47. // this.initKonva() //暂时先注释掉这里 为了避免多次重复渲染
  48. console.log(this.points, 'points')
  49. console.log(
  50. this.getSelectSopPoints,
  51. this.getSopEdit,
  52. this.getSopLook,
  53. 'getSopEdit - getSelectSopPoints'
  54. )
  55. },
  56. computed: {
  57. ...mapGetters('sopSelectPoints', [
  58. 'getSelectSopPoints',
  59. 'getSopEdit',
  60. 'getSopLook',
  61. 'getMapData'
  62. ])
  63. },
  64. methods: {
  65. initKonva() {
  66. // 创建舞台
  67. this.stage = new Konva.Stage({
  68. container: this.$refs.container, // 容器元素
  69. width: 1270,
  70. height: 830
  71. })
  72. // 创建图层
  73. this.layer = new Konva.Layer()
  74. // 创建底图
  75. const bgImage = new Image()
  76. bgImage.src = require('@/assets/images/table.png')
  77. bgImage.onload = () => {
  78. const knovaImage = new Konva.Image({
  79. x: 330,
  80. y: 10,
  81. image: bgImage,
  82. width: 500,
  83. height: 790,
  84. draggable: false
  85. })
  86. this.layer.add(knovaImage)
  87. this.layer.draw()
  88. }
  89. // 绘制无限网格
  90. this.drawGrid(50, 50, '#e0e0e0') // 每个单元格50x50,浅灰色网格
  91. // 渲染数据
  92. const imageSrc = require('@/assets/images/localSetIcon.jpg') // 图片路径
  93. this.renderGrid(imageSrc, 6, 3, 450, 100, 120, 100, 50, 50, 60, 25)
  94. // 将图层添加到舞台
  95. this.stage.add(this.layer)
  96. this.layer.draw()
  97. // 禁止舞台拖拽
  98. this.stage.draggable(false)
  99. },
  100. // 绘制无限网格
  101. drawGrid(cellWidth, cellHeight, gridColor) {
  102. const width = 1270
  103. const height = 830
  104. // 绘制竖线
  105. for (let i = 0; i <= width; i += cellWidth) {
  106. const verticalLine = new Konva.Line({
  107. points: [i, 0, i, height],
  108. stroke: gridColor,
  109. strokeWidth: 1
  110. })
  111. this.layer.add(verticalLine)
  112. }
  113. // 绘制横线
  114. for (let j = 0; j <= height; j += cellHeight) {
  115. const horizontalLine = new Konva.Line({
  116. points: [0, j, width, j],
  117. stroke: gridColor,
  118. strokeWidth: 1
  119. })
  120. this.layer.add(horizontalLine)
  121. }
  122. this.layer.draw()
  123. },
  124. renderGrid(imageSrc) {
  125. this.selectedStates = {} // 用对象来存储选中状态,键为文字内容
  126. this.rects = {}
  127. this.texts = {}
  128. this.bgrects={}
  129. this.redrects = {}
  130. this.redtexts = {}
  131. this.selectedText = []
  132. // const positions = JSON.parse(this.points.map);
  133. console.log(this.getMapData,',apData')
  134. let positions;
  135. // 判断getMapData是否已为数组
  136. if (Array.isArray(this.getMapData)) {
  137. // 如果是数组,则直接使用
  138. positions = this.getMapData;
  139. } else {
  140. // 如果不是数组,则尝试将其从JSON字符串转换为数组或其他对象
  141. try {
  142. positions = JSON.parse(this.getMapData);
  143. } catch (e) {
  144. console.error("Error parsing getMapData:", e);
  145. // 可以在这里处理错误情况,例如将positions设置为空数组等
  146. positions = [];
  147. }
  148. }
  149. // 检查 this.getSelectSopPoints 是否有内容
  150. const isLocked = this.getSelectSopPoints.length > 0
  151. // 添加或移除全局点击事件监听器 this.getSopEdit这是vuex里判断是否可以选择隔离点的操作
  152. if (isLocked && this.getSopEdit == true) {
  153. this.layer.on('click', (e) => {
  154. e.cancelBubble = true // 阻止事件冒泡
  155. // e.stopPropagation(); // 阻止事件传播
  156. })
  157. } else {
  158. this.layer.off('click') // 移除全局点击事件监听器
  159. }
  160. positions.forEach((pos, index) => {
  161. const x = pos.col * 50 // 每个单元格宽度为50
  162. const y = pos.row * 50 // 每个单元格高度为50
  163. const labelText = pos.pointName // 对应的文字
  164. const point = new Image()
  165. point.src = pos.pointIcon
  166. // point.src = imageSrc;
  167. point.onload = () => {
  168. const knovaImage = new Konva.Image({
  169. x: x,
  170. y: y,
  171. image: point,
  172. width: 50,
  173. height: 50,
  174. draggable: false
  175. })
  176. // 添加点击事件,仅当 getSopEdit 为 true 时才允许点击a
  177. if (this.getSopEdit == true && this.getSopLook == false) {
  178. knovaImage.on('click', () => {
  179. // 切换选中状态,基于文本内容
  180. this.selectedStates[labelText] = !this.selectedStates[labelText]
  181. if (this.selectedStates[labelText]) {
  182. // 选中状态,显示红色矩形和文字,切换为选中的图片
  183. this.rects[labelText].visible(true)
  184. this.texts[labelText].visible(true)
  185. this.redrects[labelText].visible(true)
  186. this.redtexts[labelText].visible(true)
  187. const selectedImage = new Image()
  188. selectedImage.src = pos.pointIcon
  189. selectedImage.onload = () => {
  190. knovaImage.image(selectedImage) // 更新图像
  191. this.layer.draw() // 更新图层
  192. }
  193. // 获取隔离点信息,并将选中的 labelText 推入数组
  194. this.$nextTick(() => {
  195. if (
  196. this.$route.query.sopId !== null ||
  197. this.$route.query.ticketId !== null
  198. ) {
  199. this.selectedText.push({
  200. pointName: pos.pointName,
  201. pointId: pos.pointId,
  202. pointType: pos.pointTypeName,
  203. powerType: pos.powerTypeName,
  204. prePointId:pos.prePointId
  205. })
  206. this.$emit('selection-changed', this.selectedText)
  207. }
  208. })
  209. } else {
  210. // 取消选中状态,恢复普通矩形和文字,切换为未选中的图片
  211. this.rects[labelText].visible(true)
  212. this.texts[labelText].visible(true)
  213. this.redrects[labelText].visible(false)
  214. this.redtexts[labelText].visible(false)
  215. const normalImage = new Image()
  216. normalImage.src = pos.pointIcon
  217. // normalImage.src = imageSrc; // 未选中的默认图片路径
  218. normalImage.onload = () => {
  219. knovaImage.image(normalImage) // 更新图像
  220. this.layer.draw() // 更新图层
  221. }
  222. // 从选中数组中移除该项
  223. this.selectedText = this.selectedText.filter(
  224. (item) => item.pointName !== labelText
  225. )
  226. }
  227. // 确保图层重新渲染
  228. this.layer.draw()
  229. this.$emit('selection-changed', this.selectedText)
  230. })
  231. }
  232. // 底部白色背景
  233. const bgrect=new Konva.Rect({
  234. x: x - 6,
  235. y: y - 5,
  236. width: 62,
  237. height: 80,
  238. cornerRadius: 5,
  239. stroke: 'white',
  240. strokeWidth: 2,
  241. fill: 'white'
  242. })
  243. this.layer.add(bgrect)
  244. this.bgrects[labelText] = bgrect // 用文字作为键存储
  245. // 普通矩形
  246. const rect = new Konva.Rect({
  247. x: x + 0,
  248. y: y - 1,
  249. width: 50,
  250. height: 72,
  251. cornerRadius: 5,
  252. stroke: 'red',
  253. strokeWidth: 2,
  254. fill: 'white'
  255. })
  256. this.layer.add(rect)
  257. this.rects[labelText] = rect // 用文字作为键存储
  258. // 先加底部白色 再添加图片
  259. this.layer.add(knovaImage)
  260. // // 普通矩形
  261. // const rect = new Konva.Rect({
  262. // x: x ,
  263. // y: y + 45,
  264. // width: 50,
  265. // height: 25,
  266. // cornerRadius: 0,
  267. // stroke: 'red',
  268. // strokeWidth: 2,
  269. // fill: 'white'
  270. // })
  271. // this.layer.add(rect)
  272. // this.rects[labelText] = rect // 用文字作为键存储
  273. //
  274. // // 普通文字
  275. // const text = new Konva.Text({
  276. // x: x + 8,
  277. // y: y + 50,
  278. // fontSize: 20,
  279. // text: labelText,
  280. // fontFamily: 'Calibri',
  281. // fill: 'red'
  282. // })
  283. // this.layer.add(text)
  284. // this.texts[labelText] = text // 用文字作为键存储
  285. // // 红色矩形(初始隐藏)
  286. // const redrect = new Konva.Rect({
  287. // x: x - 3,
  288. // y: y + 55,
  289. // width: 60,
  290. // height: 25,
  291. // cornerRadius: 10,
  292. // stroke: 'red',
  293. // strokeWidth: 2,
  294. // fill: 'red',
  295. // visible: false
  296. // })
  297. // this.layer.add(redrect)
  298. // this.redrects[labelText] = redrect // 用文字作为键存储
  299. //
  300. // // 红色文字(初始隐藏)
  301. // const redtext = new Konva.Text({
  302. // x: x + 12,
  303. // y: y + 60,
  304. // fontSize: 20,
  305. // text: labelText,
  306. // fontFamily: 'Calibri',
  307. // fill: 'white',
  308. // visible: false
  309. // })
  310. // this.layer.add(redtext)
  311. // this.redtexts[labelText] = redtext // 用文字作为键存储
  312. // 普通文字
  313. const text = new Konva.Text({
  314. x: x + 8,
  315. y: y + 50,
  316. fontSize: 20,
  317. text: labelText,
  318. fontFamily: 'Calibri',
  319. fill: 'red'
  320. })
  321. this.layer.add(text)
  322. this.texts[labelText] = text // 用文字作为键存储
  323. // 覆盖层(表示选中状态)
  324. const redrect = new Konva.Rect({
  325. x: x - 7,
  326. y: y - 6,
  327. width: 64.1,
  328. height: 82,
  329. cornerRadius: 5,
  330. fill: 'rgba(97, 97, 97, 0.5)', // 半透明灰色
  331. visible: false, // 初始状态隐藏
  332. listening:false
  333. });
  334. this.layer.add(redrect)
  335. this.redrects[labelText] = redrect // 用文字作为键存储
  336. // 创建对号文本
  337. const redtext = new Konva.Text({
  338. x: x - 5 + 42 / 2, // 水平居中
  339. y: y + 62 / 2, // 垂直居中
  340. fontSize: 24, // 根据需要调整字体大小
  341. text: '✔',
  342. fontFamily: 'Arial',
  343. fill: 'white',
  344. align: 'center',
  345. verticalAlign: 'middle',
  346. visible: false,//初始隐藏状态
  347. listening:false,
  348. });
  349. this.layer.add(redtext);
  350. this.redtexts[labelText] = redtext; // 用文字作为键存储
  351. // 检查 this.getSelectSopPoints 是否包含当前点的 pointId
  352. if (pos.state) {
  353. console.log('我走这里了-------')
  354. // 设置为选中状态
  355. this.selectedStates[labelText] = true
  356. this.rects[labelText].visible(true)
  357. this.texts[labelText].visible(true)
  358. this.redrects[labelText].visible(true)
  359. this.redtexts[labelText].visible(true)
  360. // 切换图片为选中状态的图片
  361. const selectedImage = new Image()
  362. selectedImage.src = pos.pointIcon // 选中的图片路径
  363. selectedImage.onload = () => {
  364. knovaImage.image(selectedImage) // 更新图像
  365. this.layer.draw() // 更新图层
  366. }
  367. // 将选中的 labelText 推入数组
  368. this.selectedText.push({
  369. pointName: pos.pointName,
  370. pointId: pos.pointId,
  371. pointType: pos.pointTypeName,
  372. powerType: pos.powerTypeName,
  373. prePointId:pos.prePointId
  374. })
  375. } else {
  376. // 设置为未选中状态
  377. this.selectedStates[labelText] = false
  378. this.rects[labelText].visible(true)
  379. this.texts[labelText].visible(true)
  380. this.redrects[labelText].visible(false)
  381. this.redtexts[labelText].visible(false)
  382. }
  383. // 触发父组件的 selection-changed 事件
  384. this.$emit('selection-changed', this.selectedText)
  385. this.layer.draw()
  386. }
  387. })
  388. }
  389. // methods结束
  390. }
  391. }
  392. </script>
  393. <style scoped lang="scss">
  394. #container {
  395. width: 100%;
  396. height: 100%;
  397. }
  398. </style>