MapData.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  1. <template>
  2. <div class="mapdata">
  3. <div id="container" ref="container" style="width:1200px"></div>
  4. <div class="left">
  5. <el-card class="box-card">
  6. <div slot="header" class="clearfix">
  7. <span style="font-size: 18px">锁定站信息展示</span>
  8. <!-- <span-->
  9. <!-- style="-->
  10. <!-- float: right;-->
  11. <!-- padding: 1px 0;-->
  12. <!-- font-size: 22px;-->
  13. <!-- cursor: pointer;-->
  14. <!-- "-->
  15. <!-- type="text"-->
  16. <!-- @click="goBack"-->
  17. <!-- >×</span-->
  18. <!-- >-->
  19. </div>
  20. <div style="height: 100%;padding-bottom:10px">
  21. <el-input v-model="value" type="textarea" :rows="26" disabled></el-input>
  22. </div>
  23. <div class="bottombtn" style="width:100%;height: 35px;padding: 10px 0 0">
  24. <el-button v-no-more-click @click="save" type="primary" icon="el-icon-edit"
  25. style="float: left; height: 33px; line-height: 2px"
  26. >保存
  27. </el-button>
  28. <!-- <el-button v-no-more-click @click="close" type="primary" icon="el-icon-close"-->
  29. <!-- style="float: left; height: 33px; line-height: 2px;"-->
  30. <!-- >关闭-->
  31. <!-- </el-button>-->
  32. <!-- <el-button v-no-more-click @click="reset" type="primary" icon="el-icon-refresh"-->
  33. <!-- style="float: left; height: 33px; line-height: 2px;"-->
  34. <!-- >重置-->
  35. <!-- </el-button>-->
  36. <!-- <el-button v-no-more-click @click="view" type="primary" icon="el-icon-view"-->
  37. <!-- style="float: left; height: 33px; line-height: 2px"-->
  38. <!-- >预览-->
  39. <!-- </el-button>-->
  40. </div>
  41. </el-card>
  42. </div>
  43. </div>
  44. </template>
  45. <script>
  46. import Konva from 'konva'
  47. import { selectLotoMapById, selectIsLotoStationById, updateLoto } from '@/api/mes/lotoStation/lotoStation'
  48. import { getTechnologyInfo, saveMachineryPoints } from '@/api/system/machinery'
  49. import { selectIsMapById } from '@/api/system/mapconfig'
  50. export default {
  51. name: 'KonvaExample',
  52. props: {
  53. machineryId: {
  54. type: String,
  55. default: ''
  56. }
  57. },
  58. data() {
  59. return {
  60. stage: null,
  61. layer: null,
  62. backgroundLayer:null,//背景图层
  63. gridLayer:null,//网格图层
  64. selectedStates: [], // 用于存储每个元素的选中状态
  65. selectedText: [], // 用于存储未选中的元素文本
  66. rects: [], // 白色r ect合集
  67. texts: [], // 白色text合集
  68. redrects: [], // 红色rect合集
  69. redtexts: [], // 白色text合集
  70. value: '',
  71. form: {},//拿到单个数据
  72. orignData: null,//原始数据
  73. pointIdList: [],//选中的隔离点
  74. selectPoints: [],//回显之前选中的隔离点
  75. imageUrl: '',//获取底图
  76. width: '',//底图宽
  77. height: '',//底图高
  78. x: '',//底图横坐标
  79. y: '',//底图纵坐标
  80. pointList:null,//接口给的点位信息
  81. }
  82. },
  83. mounted() {
  84. this.$nextTick(() => {
  85. this.selectIsLotoStationById()
  86. })
  87. },
  88. methods: {
  89. goBack() {
  90. this.$router.push('/technology/technologyList')
  91. },
  92. close() {
  93. this.$router.push('/technology/technologyList')
  94. },
  95. selectIsLotoStationById() {
  96. const machineryId = this.machineryId
  97. getTechnologyInfo(machineryId).then((response) => {
  98. const lotoId = response.data.lotoId
  99. const sopId = ''
  100. const ticketId = ''
  101. selectLotoMapById(lotoId, sopId, ticketId).then(response => {
  102. console.log(response, '电柜预览接口调用')
  103. this.form.map = response.data
  104. if (response.data) {
  105. try {
  106. this.value = JSON.stringify(response.data, null, 4)
  107. this.orignData = this.value
  108. this.initKonva()
  109. } catch (err) {
  110. }
  111. }
  112. })
  113. selectIsLotoStationById(lotoId).then((response) => {
  114. console.log(response, '电柜信息')
  115. this.form = response.data
  116. // 获取不同底图 如地图或者柜子
  117. selectIsMapById(response.data.mapId).then((response) => {
  118. console.log(response, '获取底图')
  119. this.imageUrl = response.data.imageUrl
  120. this.width = response.data.width
  121. this.height = response.data.height
  122. this.x = response.data.x
  123. this.y = response.data.y
  124. this.pointList=response.data.pointList
  125. this.initKonva()
  126. })
  127. })
  128. })
  129. // 设备工艺详情
  130. getTechnologyInfo(this.$route.query.machineryId).then(response => {
  131. console.log(response, '设备/工艺详情')
  132. this.selectPoints = response.data.pointIdList
  133. this.initKonva()
  134. })
  135. },
  136. // 重置
  137. reset() {
  138. this.value = this.orignData
  139. this.initKonva() // 重新初始化 Konva
  140. },
  141. // 预览
  142. view() {
  143. if (this.isJson(this.value)) {
  144. this.form.map = this.value
  145. this.initKonva()
  146. } else {
  147. this.$message({
  148. type: 'error',
  149. message: '地图数据格式不正确,请输入有效的 JSON 格式!'
  150. })
  151. }
  152. },
  153. save() {
  154. this.$confirm('请确认是否保存修改内容', '提示', {
  155. confirmButtonText: '确定',
  156. cancelButtonText: '取消',
  157. type: 'warning'
  158. }).then(() => {
  159. // 校验 this.value 是否为有效的 JSON
  160. if (this.isJson(this.value)) {
  161. const machineryId = this.$route.query.machineryId
  162. const formData = {
  163. machineryId: machineryId,
  164. pointIdList: this.pointIdList
  165. }
  166. console.log(this.form, formData, 'map')
  167. saveMachineryPoints(formData).then(response => {
  168. console.log(response, '修改设备/工艺地图')
  169. // this.$router.push('/technology/technologyList')
  170. this.$message({
  171. type: 'success',
  172. message: '保存成功!'
  173. })
  174. })
  175. } else {
  176. this.$message({
  177. type: 'error',
  178. message: '地图数据格式不正确,请输入有效的 JSON 格式!'
  179. })
  180. }
  181. }).catch(() => {
  182. // 取消操作
  183. })
  184. },
  185. // 校验字符串是否为有效的 JSON
  186. isJson(str) {
  187. try {
  188. JSON.parse(str)
  189. return true
  190. } catch (e) {
  191. return false
  192. }
  193. },
  194. initKonva() {
  195. // 创建舞台
  196. this.stage = new Konva.Stage({
  197. container: this.$refs.container, // 容器元素
  198. width: 1200,
  199. height: 860
  200. });
  201. // 创建网格图层(最底层)
  202. this.gridLayer = new Konva.Layer();
  203. this.stage.add(this.gridLayer);
  204. // 先绘制网格(不变)
  205. this.drawGrid(50, 50, '#e0e0e0', this.gridLayer);
  206. // 创建底图图层(第二层)
  207. this.backgroundLayer = new Konva.Layer();
  208. this.stage.add(this.backgroundLayer);
  209. // 加载底图
  210. const bgImage = new Image();
  211. bgImage.src = this.imageUrl;
  212. bgImage.onload = () => {
  213. const konvaImage = new Konva.Image({
  214. x: this.x || 0,
  215. y: this.y || 0,
  216. image: bgImage,
  217. width: this.width || 1200,
  218. height: this.height || 860,
  219. draggable: false
  220. });
  221. // 添加底图到底图图层
  222. this.backgroundLayer.add(konvaImage);
  223. this.backgroundLayer.draw();
  224. };
  225. // 创建主图层(第三层)用于存放隔离点
  226. this.layer = new Konva.Layer();
  227. this.stage.add(this.layer);
  228. // 渲染隔离点等数据
  229. const imageSrc = require('@/assets/images/localSetIcon.jpg'); // 图片路径
  230. this.renderGrid(imageSrc, 6, 3, 450, 100, 120, 100, 50, 50, 60, 25);
  231. this.layer.draw();
  232. // 禁止舞台拖拽
  233. this.stage.draggable(false);
  234. },
  235. // 绘制无限网格
  236. drawGrid(cellWidth, cellHeight, gridColor, layer) {
  237. const width = 1200;
  238. const height = 860;
  239. // 绘制竖线
  240. for (let i = 0; i <= width; i += cellWidth) {
  241. const verticalLine = new Konva.Line({
  242. points: [i, 0, i, height],
  243. stroke: gridColor,
  244. strokeWidth: 1
  245. });
  246. layer.add(verticalLine);
  247. }
  248. // 绘制横线
  249. for (let j = 0; j <= height; j += cellHeight) {
  250. const horizontalLine = new Konva.Line({
  251. points: [0, j, width, j],
  252. stroke: gridColor,
  253. strokeWidth: 1
  254. });
  255. layer.add(horizontalLine);
  256. }
  257. layer.draw();
  258. },
  259. renderGrid(imageSrc) {
  260. this.selectedStates = [] // 用数组来存储选中状态
  261. this.rects = {}
  262. this.texts = {}
  263. this.bgrects = {}
  264. this.redrects = {}
  265. this.redtexts = {}
  266. this.selectedText = []
  267. this.pointIdList = [] // 初始化选中的点ID列表
  268. // const positions = JSON.parse(this.value)
  269. const positions=this.pointList
  270. positions.forEach((pos, index) => {
  271. const x = pos.x * 50 // 每个单元格宽度为50
  272. const y = pos.y * 50 // 每个单元格高度为50
  273. const labelText = pos.entityName // 对应的文字
  274. const point = new Image()
  275. point.src = pos.pointIcon
  276. point.onload = () => {
  277. const knovaImage = new Konva.Image({
  278. x: x,
  279. y: y,
  280. image: point,
  281. width: 50,
  282. height: 50,
  283. draggable: false
  284. })
  285. // 添加点击事件监听器
  286. knovaImage.on('click', () => {
  287. const isCurrentlySelected = this.redrects[labelText].visible()
  288. if (isCurrentlySelected) {
  289. // 如果当前已选中,则取消选中
  290. this.redrects[labelText].visible(false)
  291. this.redtexts[labelText].visible(false)
  292. this.pointIdList = this.pointIdList.filter(id => id !== pos.entityId) // 移除ID
  293. } else {
  294. // 如果当前未选中,则选中
  295. this.redrects[labelText].visible(true)
  296. this.redtexts[labelText].visible(true)
  297. this.pointIdList.push(pos.entityId) // 添加ID
  298. }
  299. this.layer.batchDraw() // 更新图层显示
  300. })
  301. // 底部白色背景
  302. const bgrect = new Konva.Rect({
  303. x: x - 2,
  304. y: y - 5,
  305. width: 50,
  306. height: 78,
  307. cornerRadius: 5,
  308. stroke: 'white',
  309. strokeWidth: 2,
  310. fill: 'white'
  311. })
  312. this.layer.add(bgrect)
  313. this.bgrects[labelText] = bgrect // 用文字作为键存储
  314. // 普通矩形红色边框
  315. const rect = new Konva.Rect({
  316. x: x + 1,
  317. y: y - 1,
  318. width: 45,
  319. height: 70,
  320. cornerRadius: 5,
  321. stroke: 'red',
  322. strokeWidth: 2,
  323. fill: 'white'
  324. })
  325. this.layer.add(rect)
  326. this.rects[labelText] = rect // 用文字作为键存储
  327. // 先加底部白色 再添加图片
  328. this.layer.add(knovaImage)
  329. // 普通文字
  330. const text = new Konva.Text({
  331. x: x + 8,
  332. y: y + 50,
  333. fontSize: 17,
  334. text: labelText,
  335. fontFamily: 'Calibri',
  336. fill: 'red'
  337. })
  338. this.layer.add(text)
  339. this.texts[labelText] = text // 用文字作为键存储
  340. // 覆盖层(表示选中状态)
  341. const redrect = new Konva.Rect({
  342. x: x - 3,
  343. y: y - 6,
  344. width: 51,
  345. height: 80,
  346. cornerRadius: 5,
  347. fill: 'rgba(97, 97, 97, 0.5)', // 半透明灰色
  348. visible: false, // 初始状态隐藏
  349. listening: false
  350. })
  351. this.layer.add(redrect)
  352. this.redrects[labelText] = redrect // 用文字作为键存储
  353. // 创建对号文本
  354. const redtext = new Konva.Text({
  355. x: x - 8 + 42 / 2, // 水平居中
  356. y: y + 50 / 2, // 垂直居中
  357. fontSize: 24, // 根据需要调整字体大小
  358. text: '✔',
  359. fontFamily: 'Arial',
  360. fill: 'white',
  361. align: 'center',
  362. verticalAlign: 'middle',
  363. visible: false, // 初始隐藏状态
  364. listening: false
  365. })
  366. this.layer.add(redtext)
  367. this.redtexts[labelText] = redtext // 用文字作为键存储
  368. // 检查 selectPoints 是否存在并且不为空
  369. if (Array.isArray(this.selectPoints) && this.selectPoints.length > 0) {
  370. if (this.selectPoints.includes(pos.entityId)) {
  371. // 设置选中状态
  372. this.redrects[labelText].visible(true)
  373. this.redtexts[labelText].visible(true)
  374. this.pointIdList.push(pos.entityId) // 添加ID
  375. }
  376. }
  377. this.layer.draw()
  378. }
  379. })
  380. }
  381. }
  382. }
  383. </script>
  384. <style scoped lang="scss">
  385. #container {
  386. width: 100%;
  387. height: 100%;
  388. }
  389. .mapdata {
  390. width: 100%;
  391. height: 100%;
  392. display: flex;
  393. }
  394. .left {
  395. flex: 1;
  396. display: flex;
  397. flex-direction: column;
  398. }
  399. </style>