MapData.vue 12 KB

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