Selaa lähdekoodia

sop修改为可以选择人员配置的功能编辑和查看基本也好了

pm 11 kuukautta sitten
vanhempi
sitoutus
61c4b83dfe

+ 8 - 1
src/api/mes/sop/sopindex.js

@@ -17,7 +17,14 @@ export function addinsertIsSop(data) {
         data: data
     })
 }
-
+// 新增sop 开启人员选择功能 获取人员选项
+export function SopUser(query) {
+  return request({
+    url: '/system/role/authUser/allocatedListByRoleKey',
+    method: 'get',
+    params: query
+  })
+}
 // 修改sop信息 /dev-api/iscs/sop/updateIsSop
 export function updateIsSop(data) {
     return request({

+ 1 - 1
src/api/mes/wa/workarea.js

@@ -31,7 +31,7 @@ export function listAllWorkshop(){
   })
 }
 // 获取区域msp解析数据
-export function getMapData(query,query2,query3) {
+export function getMapDataApi(query,query2,query3) {
   return request({
     url: `/iscs/workarea/selectIsWorkareaMapById?workareaId=${query}&sopId=${query2}&ticketId=${query3}`,
     method: 'get',

+ 131 - 168
src/components/separationPoint/index.vue

@@ -34,25 +34,27 @@ export default {
     'getMapData': {
       handler(newval) {
         if (newval) {
-          this.$nextTick(()=>{
+          this.$nextTick(() => {
             this.initKonva()
           })
 
         }
       }
-    },
+    }
 
   },
   mounted() {
     this.initKonva()
     console.log(this.points, 'points')
-    console.log(this.getMapData, 'Mapdata')
-    console.log(
-      this.getSelectSopPoints,
-      this.getSopEdit,
-      this.getSopLook,
-      'getSopEdit - getSelectSopPoints'
-    )
+
+    // console.log(
+    //   this.getSelectSopPoints,
+    //   this.getSopEdit,
+    //   this.getSopLook,
+    //   this.getMapData,
+    //   'getSopEdit - getSelectSopPoints'
+    // )
+
   },
   computed: {
     ...mapGetters('sopSelectPoints', [
@@ -132,41 +134,51 @@ export default {
 
       this.layer.draw()
     },
-
-
     renderGrid(imageSrc) {
+      this.selectedStates = {}; // 用对象来存储选中状态,键为文字内容
+      this.rects = {};
+      this.texts = {};
+      this.redrects = {};
+      this.redtexts = {};
+      this.selectedText = [];
+
+      let positions;
+
+      // 判断getMapData是否已为数组
+      if (Array.isArray(this.getMapData)) {
+        // 如果是数组,则直接使用
+        positions = this.getMapData;
+      } else {
+        // 如果不是数组,则尝试将其从JSON字符串转换为数组或其他对象
+        try {
+          positions = JSON.parse(this.getMapData);
+        } catch (e) {
+          console.error("Error parsing getMapData:", e);
+          // 可以在这里处理错误情况,例如将positions设置为空数组等
+          positions = [];
+        }
+      }
 
-      this.selectedStates = {} // 用对象来存储选中状态,键为文字内容
-      this.rects = {}
-      this.texts = {}
-      this.redrects = {}
-      this.redtexts = {}
-      this.selectedText = []
-
-      // const positions = JSON.parse(this.points.map);
-      // console.log(this.getMapData,'getMapData')
-      const positions=JSON.parse(this.getMapData)
+      console.log(positions, 'positions');
       // 检查 this.getSelectSopPoints 是否有内容
-      const isLocked = this.getSelectSopPoints.length > 0
+      const isLocked = this.getSelectSopPoints.length > 0;
 
       // 添加或移除全局点击事件监听器 this.getSopEdit这是vuex里判断是否可以选择隔离点的操作
-      if (isLocked && this.getSopEdit == true) {
+      if (isLocked && this.getSopEdit === true) {
         this.layer.on('click', (e) => {
-          e.cancelBubble = true // 阻止事件冒泡
-          // e.stopPropagation(); // 阻止事件传播
-        })
+          e.cancelBubble = true; // 阻止事件冒泡
+        });
       } else {
-        this.layer.off('click') // 移除全局点击事件监听器
+        this.layer.off('click'); // 移除全局点击事件监听器
       }
 
       positions.forEach((pos, index) => {
-        const x = pos.col * 50 // 每个单元格宽度为50
-        const y = pos.row * 50 // 每个单元格高度为50
-        const labelText = pos.pointName // 对应的文字
+        const x = pos.col * 50; // 每个单元格宽度为50
+        const y = pos.row * 50; // 每个单元格高度为50
+        const labelText = pos.pointName; // 对应的文字
 
-        const point = new Image()
-        point.src = pos.pointIcon
-        // point.src = imageSrc;
+        const point = new Image();
+        point.src = pos.pointIcon;
         point.onload = () => {
           const knovaImage = new Konva.Image({
             x: x,
@@ -175,81 +187,78 @@ export default {
             width: 50,
             height: 50,
             draggable: false
-          })
-          // 添加点击事件,仅当 getSopEdit 为 true 时才允许点击a
-          if (this.getSopEdit == true && this.getSopLook == false) {
+          });
+
+          // 添加点击事件,仅当 getSopEdit 为 true 时才允许点击
+          if (this.getSopEdit === true && this.getSopLook === false) {
             knovaImage.on('click', () => {
               // 切换选中状态,基于文本内容
-              this.selectedStates[labelText] = !this.selectedStates[labelText]
+              this.selectedStates[labelText] = !this.selectedStates[labelText];
 
               if (this.selectedStates[labelText]) {
                 // 选中状态,显示红色矩形和文字,切换为选中的图片
-                this.rects[labelText].visible(false)
-                this.texts[labelText].visible(false)
-                this.redrects[labelText].visible(true)
-                this.redtexts[labelText].visible(true)
+                this.rects[labelText].visible(false);
+                this.texts[labelText].visible(false);
+                this.redrects[labelText].visible(true);
+                this.redtexts[labelText].visible(true);
 
-                const selectedImage = new Image()
-                selectedImage.src = require('@/assets/images/localSetSelect.jpg')
+                const selectedImage = new Image();
+                selectedImage.src = require('@/assets/images/localSetSelect.jpg');
                 selectedImage.onload = () => {
-                  knovaImage.image(selectedImage) // 更新图像
-                  this.layer.draw() // 更新图层
-                }
+                  knovaImage.image(selectedImage); // 更新图像
+                  this.layer.draw(); // 更新图层
+                };
 
                 // 获取隔离点信息,并将选中的 labelText 推入数组
-
                 this.$nextTick(() => {
                   if (
                     this.$route.query.sopId !== null ||
                     this.$route.query.ticketId !== null
                   ) {
-                    this.selectedText.push({
+                    const newItem = {
                       pointName: pos.pointName,
                       pointId: pos.pointId,
                       pointType: pos.pointTypeName,
-                      powerType: pos.powerTypeName
-                    })
-                    this.$emit('selection-changed', this.selectedText)
-                    // selectIsIsolationPointById(pos.pointId).then((res) => {
-                    //   this.selectedText.push({
-                    //     pointName: res.data.pointName,
-                    //     pointId: res.data.pointId,
-                    //     pointType: res.data.pointType,
-                    //     powerType: res.data.powerType,
-                    //   });
-                    //   console.log(this.selectedText, "$emit");
-                    //   this.$emit("selection-changed", this.selectedText);
-                    // });
+                      powerType: pos.powerTypeName,
+                      prePointId: pos.prePointId,
+                    };
+
+                    // 去重处理
+                    if (!this.selectedText.some(item => item.pointId === newItem.pointId)) {
+                      this.selectedText.push(newItem);
+                    }
+
+                    console.log(this.selectedText, 'selectedText');
+                    this.$emit('selection-changed', this.selectedText);
                   }
-                })
+                });
               } else {
-                // 取消选中状态,恢复普通矩形和文字,切换为未选中的图片
-                this.rects[labelText].visible(true)
-                this.texts[labelText].visible(true)
-                this.redrects[labelText].visible(false)
-                this.redtexts[labelText].visible(false)
-
-                const normalImage = new Image()
-                normalImage.src = pos.pointIcon
-                // normalImage.src = imageSrc; // 未选中的默认图片路径
+                // 取消选中状态,恢复普通矩形和文字
+                this.rects[labelText].visible(true);
+                this.texts[labelText].visible(true);
+                this.redrects[labelText].visible(false);
+                this.redtexts[labelText].visible(false);
+
+                const normalImage = new Image();
+                normalImage.src = imageSrc; // 未选中的默认图片路径
                 normalImage.onload = () => {
-                  knovaImage.image(normalImage) // 更新图像
-                  this.layer.draw() // 更新图层
-                }
+                  knovaImage.image(normalImage); // 更新图像
+                  this.layer.draw(); // 更新图层
+                };
 
                 // 从选中数组中移除该项
                 this.selectedText = this.selectedText.filter(
                   (item) => item.pointName !== labelText
-                )
+                );
               }
 
               // 确保图层重新渲染
-              this.layer.draw()
-              this.$emit('selection-changed', this.selectedText)
-            })
+              this.layer.draw();
+              this.$emit('selection-changed', this.selectedText);
+            });
           }
 
-          this.layer.add(knovaImage)
+          this.layer.add(knovaImage);
 
           // 普通矩形
           const rect = new Konva.Rect({
@@ -261,9 +270,9 @@ export default {
             stroke: 'red',
             strokeWidth: 2,
             fill: 'white'
-          })
-          this.layer.add(rect)
-          this.rects[labelText] = rect // 用文字作为键存储
+          });
+          this.layer.add(rect);
+          this.rects[labelText] = rect; // 用文字作为键存储
 
           // 普通文字
           const text = new Konva.Text({
@@ -273,9 +282,9 @@ export default {
             text: labelText,
             fontFamily: 'Calibri',
             fill: 'red'
-          })
-          this.layer.add(text)
-          this.texts[labelText] = text // 用文字作为键存储
+          });
+          this.layer.add(text);
+          this.texts[labelText] = text; // 用文字作为键存储
 
           // 红色矩形(初始隐藏)
           const redrect = new Konva.Rect({
@@ -288,9 +297,9 @@ export default {
             strokeWidth: 2,
             fill: 'red',
             visible: false
-          })
-          this.layer.add(redrect)
-          this.redrects[labelText] = redrect // 用文字作为键存储
+          });
+          this.layer.add(redrect);
+          this.redrects[labelText] = redrect; // 用文字作为键存储
 
           // 红色文字(初始隐藏)
           const redtext = new Konva.Text({
@@ -301,104 +310,58 @@ export default {
             fontFamily: 'Calibri',
             fill: 'white',
             visible: false
-          })
-          this.layer.add(redtext)
-          this.redtexts[labelText] = redtext // 用文字作为键存储
+          });
+          this.layer.add(redtext);
+          this.redtexts[labelText] = redtext; // 用文字作为键存储
 
           // 检查 this.getSelectSopPoints 是否包含当前点的 pointId
           if (pos.state) {
+            console.log('选中的隔离点渲染',pos)
             // 设置为选中状态
-            this.selectedStates[labelText] = true
-            this.rects[labelText].visible(false)
-            this.texts[labelText].visible(false)
-            this.redrects[labelText].visible(true)
-            this.redtexts[labelText].visible(true)
+            this.selectedStates[labelText] = true;
+            this.rects[labelText].visible(false);
+            this.texts[labelText].visible(false);
+            this.redrects[labelText].visible(true);
+            this.redtexts[labelText].visible(true);
 
             // 切换图片为选中状态的图片
-            const selectedImage = new Image()
-            selectedImage.src = require('@/assets/images/localSetSelect.jpg') // 选中的图片路径
+            const selectedImage = new Image();
+            selectedImage.src = require('@/assets/images/localSetSelect.jpg'); // 选中的图片路径
             selectedImage.onload = () => {
-              knovaImage.image(selectedImage) // 更新图像
-              this.layer.draw() // 更新图层
-            }
+              knovaImage.image(selectedImage); // 更新图像
+              this.layer.draw(); // 更新图层
+            };
 
             // 将选中的 labelText 推入数组
-            this.selectedText.push({
+            const newItem = {
               pointName: pos.pointName,
               pointId: pos.pointId,
               pointType: pos.pointTypeName,
-              powerType: pos.powerTypeName
-            })
+              powerType: pos.powerTypeName,
+              prePointId: pos.prePointId,
+            };
+
+            // 去重处理
+            if (!this.selectedText.some(item => item.pointId === newItem.pointId)) {
+              this.selectedText.push(newItem);
+            }
           } else {
             // 设置为未选中状态
-            this.selectedStates[labelText] = false
-            this.rects[labelText].visible(true)
-            this.texts[labelText].visible(true)
-            this.redrects[labelText].visible(false)
-            this.redtexts[labelText].visible(false)
+            this.selectedStates[labelText] = false;
+            this.rects[labelText].visible(true);
+            this.texts[labelText].visible(true);
+            this.redrects[labelText].visible(false);
+            this.redtexts[labelText].visible(false);
           }
 
           // 触发父组件的 selection-changed 事件
-          this.$emit('selection-changed', this.selectedText)
-
-          // if (this.getSelectSopPoints.includes(pos.pointId.toString())) {
-          //   // 设置为选中状态
-          //   this.selectedStates[labelText] = true;
-          //   this.rects[labelText].visible(false);
-          //   this.texts[labelText].visible(false);
-          //   this.redrects[labelText].visible(true);
-          //   this.redtexts[labelText].visible(true);
-          //
-          //   // 切换图片为选中状态的图片
-          //   const selectedImage = new Image();
-          //   selectedImage.src = require("@/assets/images/localSetSelect.jpg"); // 选中的图片路径
-          //   selectedImage.onload = () => {
-          //     knovaImage.image(selectedImage); // 更新图像
-          //     this.layer.draw(); // 更新图层
-          //   };
-          //   // 将选中的 labelText 推入数组
-          //   this.$nextTick(() => {
-          //     getIsIsolationPointPage({ current: 1, size: 100 })
-          //       .then((res) => {
-          //         const allPoints = res.data.records; // 假设返回的数据结构是 { records: [点数据] }
-          //
-          //         // 根据 pos.pointId 查找对应的 pointType 和 powerType
-          //         const pointInfo = allPoints.find(
-          //           (point) => point.pointId == pos.pointId
-          //         );
-          //
-          //         if (pointInfo) {
-          //           this.selectedText.push({
-          //             pointName: labelText,
-          //             pointId: pos.pointId,
-          //             pointType: pointInfo.pointType,
-          //             powerType: pointInfo.powerType,
-          //           });
-          //         } else {
-          //           // 如果没有找到对应的点信息,可以处理这种情况
-          //           console.warn(`未找到 pointId 为 ${pos.pointId} 的点信息`);
-          //           this.selectedText.push({
-          //             pointName: labelText,
-          //             pointId: pos.pointId,
-          //             pointType: "",
-          //             powerType: "",
-          //           });
-          //         }
-          //         // console.log(this.selectedText,'默认拿到的points')
-          //         // 触发父组件的 selection-changed 事件
-          //         this.$emit("selection-changed", this.selectedText);
-          //       })
-          //       .catch((error) => {
-          //         console.error("获取隔离点信息失败", error);
-          //       });
-          //   });
-          // }
-
-          this.layer.draw()
-
+          this.$emit('selection-changed', this.selectedText);
+          this.layer.draw();
         }
-      })
-    }
+      });
+    },
+
+
 
     //     methods结束
   }

+ 420 - 0
src/components/separationPoint/index1.vue

@@ -0,0 +1,420 @@
+<template>
+  <div id="container" ref="container"></div>
+</template>
+
+<script>
+import Konva from 'konva'
+import {
+  selectIsIsolationPointById,
+  getIsIsolationPointPage
+} from '@/api/mes/spm/segregationPoint'
+import { mapGetters } from 'vuex'
+
+export default {
+  name: 'KonvaExample',
+  props: {
+    points: {
+      type: Object,
+      default: null
+    }
+  },
+  data() {
+    return {
+      stage: null,
+      layer: null,
+      selectedStates: [], // 用于存储每个元素的选中状态
+      selectedText: [], // 用于存储未选中的元素文本
+      rects: [], //白色rect合集
+      texts: [], //白色text合集
+      redrects: [], //红色rect合集
+      redtexts: [] //白色text合集
+    }
+  },
+  watch: {
+    'getMapData': {
+      handler(newval) {
+        if (newval) {
+          // this.$nextTick(()=>{
+          //   this.initKonva()
+          // })
+
+        }
+      }
+    },
+
+  },
+  mounted() {
+    // this.initKonva()
+    // console.log(this.points, 'points')
+    // console.log(this.getMapData, 'Mapdata')
+    // console.log(
+    //   this.getSelectSopPoints,
+    //   this.getSopEdit,
+    //   this.getSopLook,
+    //   'getSopEdit - getSelectSopPoints'
+    // )
+  },
+  computed: {
+    ...mapGetters('sopSelectPoints', [
+      'getSelectSopPoints',
+      'getSopEdit',
+      'getSopLook',
+      'getMapData'
+    ])
+  },
+  methods: {
+    initKonva() {
+      // 创建舞台
+      this.stage = new Konva.Stage({
+        container: this.$refs.container, // 容器元素
+        width: 1270,
+        height: 830
+      })
+
+      // 创建图层
+      this.layer = new Konva.Layer()
+
+      // 创建底图
+      const bgImage = new Image()
+      bgImage.src = require('@/assets/images/table.png')
+      bgImage.onload = () => {
+        const knovaImage = new Konva.Image({
+          x: 330,
+          y: 10,
+          image: bgImage,
+          width: 500,
+          height: 790,
+          draggable: false
+        })
+        this.layer.add(knovaImage)
+        this.layer.draw()
+      }
+
+      // 绘制无限网格
+      this.drawGrid(50, 50, '#e0e0e0') // 每个单元格50x50,浅灰色网格
+
+      // 渲染数据
+      const imageSrc = require('@/assets/images/localSetIcon.jpg') // 图片路径
+      this.renderGrid(imageSrc, 6, 3, 450, 100, 120, 100, 50, 50, 60, 25)
+
+      // 将图层添加到舞台
+      this.stage.add(this.layer)
+      this.layer.draw()
+
+      // 禁止舞台拖拽
+      this.stage.draggable(false)
+    },
+
+    // 绘制无限网格
+    drawGrid(cellWidth, cellHeight, gridColor) {
+      const width = 1270
+      const height = 830
+
+      // 绘制竖线
+      for (let i = 0; i <= width; i += cellWidth) {
+        const verticalLine = new Konva.Line({
+          points: [i, 0, i, height],
+          stroke: gridColor,
+          strokeWidth: 1
+        })
+        this.layer.add(verticalLine)
+      }
+
+      // 绘制横线
+      for (let j = 0; j <= height; j += cellHeight) {
+        const horizontalLine = new Konva.Line({
+          points: [0, j, width, j],
+          stroke: gridColor,
+          strokeWidth: 1
+        })
+        this.layer.add(horizontalLine)
+      }
+
+      this.layer.draw()
+    },
+
+
+    renderGrid(imageSrc) {
+
+      this.selectedStates = {} // 用对象来存储选中状态,键为文字内容
+      this.rects = {}
+      this.texts = {}
+      this.redrects = {}
+      this.redtexts = {}
+      this.selectedText = []
+
+      // const positions = JSON.parse(this.points.map);
+      // console.log(this.getMapData,'getMapData')
+      // const positions=JSON.parse(this.getMapData)
+      const positions=null
+      // 检查 this.getSelectSopPoints 是否有内容
+      const isLocked = this.getSelectSopPoints.length > 0
+
+      // 添加或移除全局点击事件监听器 this.getSopEdit这是vuex里判断是否可以选择隔离点的操作
+      if (isLocked && this.getSopEdit == true) {
+        this.layer.on('click', (e) => {
+          e.cancelBubble = true // 阻止事件冒泡
+          // e.stopPropagation(); // 阻止事件传播
+        })
+      } else {
+        this.layer.off('click') // 移除全局点击事件监听器
+      }
+
+      positions.forEach((pos, index) => {
+        const x = pos.col * 50 // 每个单元格宽度为50
+        const y = pos.row * 50 // 每个单元格高度为50
+        const labelText = pos.pointName // 对应的文字
+
+        const point = new Image()
+        point.src = pos.pointIcon
+        point.onload = () => {
+          const knovaImage = new Konva.Image({
+            x: x,
+            y: y,
+            image: point,
+            width: 50,
+            height: 50,
+            draggable: false
+          })
+          // 添加点击事件,仅当 getSopEdit 为 true 时才允许点击a
+          if (this.getSopEdit == true && this.getSopLook == false) {
+            knovaImage.on('click', () => {
+              // 切换选中状态,基于文本内容
+              this.selectedStates[labelText] = !this.selectedStates[labelText]
+
+              if (this.selectedStates[labelText]) {
+                // 选中状态,显示红色矩形和文字,切换为选中的图片
+                this.rects[labelText].visible(false)
+                this.texts[labelText].visible(false)
+                this.redrects[labelText].visible(true)
+                this.redtexts[labelText].visible(true)
+
+                const selectedImage = new Image()
+                selectedImage.src = pos.pointIcon
+                selectedImage.onload = () => {
+                  knovaImage.image(selectedImage) // 更新图像
+
+                  this.layer.draw() // 更新图层
+                }
+
+                // 获取隔离点信息,并将选中的 labelText 推入数组
+
+                this.$nextTick(() => {
+                  if (
+                    this.$route.query.sopId !== null ||
+                    this.$route.query.ticketId !== null
+                  ) {
+                    this.selectedText.push({
+                      pointName: pos.pointName,
+                      pointId: pos.pointId,
+                      pointType: pos.pointTypeName,
+                      powerType: pos.powerTypeName
+                    })
+                    this.$emit('selection-changed', this.selectedText)
+
+                  }
+                })
+              } else {
+                // 取消选中状态,恢复普通矩形和文字,
+                this.rects[labelText].visible(true)
+                this.texts[labelText].visible(true)
+                this.redrects[labelText].visible(false)
+                this.redtexts[labelText].visible(false)
+
+                const normalImage = new Image()
+                normalImage.src = pos.pointIcon
+                // normalImage.src = imageSrc; // 未选中的默认图片路径
+                normalImage.onload = () => {
+                  knovaImage.image(normalImage) // 更新图像
+
+
+                  this.layer.draw() // 更新图层
+                }
+
+                // 从选中数组中移除该项
+                this.selectedText = this.selectedText.filter(
+                  (item) => item.pointName !== labelText
+                )
+              }
+
+              // 确保图层重新渲染
+              this.layer.draw()
+              this.$emit('selection-changed', this.selectedText)
+            })
+          }
+
+          this.layer.add(knovaImage)
+          // const border=new Konva.Rect({
+          //   x: x - 9 ,
+          //   y: y ,
+          //   width: 70,
+          //   height: 85,
+          //   cornerRadius: 10,
+          //   stroke: 'black',
+          //   strokeWidth: 2,
+          //   fill: 'transparent',
+          //
+          // })
+          // this.layer.add(border)
+          // this.rects[labelText] = border // 用文字作为键存储
+          // 普通矩形
+          const rect = new Konva.Rect({
+            x: x - 3,
+            y: y + 55,
+            width: 60,
+            height: 25,
+            cornerRadius: 10,
+            stroke: 'red',
+            strokeWidth: 2,
+
+            fill: 'white'
+          })
+          this.layer.add(rect)
+          this.rects[labelText] = rect // 用文字作为键存储
+
+          // 普通文字
+          const text = new Konva.Text({
+            x: x + 12,
+            y: y + 60,
+            fontSize: 20,
+            text: labelText,
+            fontFamily: 'Calibri',
+            fill: 'red'
+          })
+          this.layer.add(text)
+          this.texts[labelText] = text // 用文字作为键存储
+
+          // 红色矩形(初始隐藏)
+          const redrect = new Konva.Rect({
+            x: x - 3,
+            y: y + 55,
+            width: 60,
+            height: 25,
+            cornerRadius: 10,
+            stroke: 'red',
+            strokeWidth: 2,
+            fill: 'red',
+            visible: false
+          })
+          this.layer.add(redrect)
+          this.redrects[labelText] = redrect // 用文字作为键存储
+
+          // 红色文字(初始隐藏)
+          const redtext = new Konva.Text({
+            x: x + 12,
+            y: y + 60,
+            fontSize: 20,
+            text: labelText,
+            fontFamily: 'Calibri',
+            fill: 'white',
+            visible: false
+          })
+          this.layer.add(redtext)
+          this.redtexts[labelText] = redtext // 用文字作为键存储
+
+          // 检查 this.getSelectSopPoints 是否包含当前点的 pointId
+          if (pos.state) {
+            // 设置为选中状态
+            this.selectedStates[labelText] = true
+            this.rects[labelText].visible(false)
+            this.texts[labelText].visible(false)
+            this.redrects[labelText].visible(true)
+            this.redtexts[labelText].visible(true)
+
+            // 切换图片为选中状态的图片
+            const selectedImage = new Image()
+            selectedImage.src = require('@/assets/images/localSetSelect.jpg') // 选中的图片路径
+            selectedImage.onload = () => {
+              knovaImage.image(selectedImage) // 更新图像
+              this.layer.draw() // 更新图层
+            }
+
+            // 将选中的 labelText 推入数组
+            this.selectedText.push({
+              pointName: pos.pointName,
+              pointId: pos.pointId,
+              pointType: pos.pointTypeName,
+              powerType: pos.powerTypeName
+            })
+          } else {
+            // 设置为未选中状态
+            this.selectedStates[labelText] = false
+            this.rects[labelText].visible(true)
+            this.texts[labelText].visible(true)
+            this.redrects[labelText].visible(false)
+            this.redtexts[labelText].visible(false)
+          }
+
+          // 触发父组件的 selection-changed 事件
+          this.$emit('selection-changed', this.selectedText)
+
+          // if (this.getSelectSopPoints.includes(pos.pointId.toString())) {
+          //   // 设置为选中状态
+          //   this.selectedStates[labelText] = true;
+          //   this.rects[labelText].visible(false);
+          //   this.texts[labelText].visible(false);
+          //   this.redrects[labelText].visible(true);
+          //   this.redtexts[labelText].visible(true);
+          //
+          //   // 切换图片为选中状态的图片
+          //   const selectedImage = new Image();
+          //   selectedImage.src = require("@/assets/images/localSetSelect.jpg"); // 选中的图片路径
+          //   selectedImage.onload = () => {
+          //     knovaImage.image(selectedImage); // 更新图像
+          //     this.layer.draw(); // 更新图层
+          //   };
+          //   // 将选中的 labelText 推入数组
+          //   this.$nextTick(() => {
+          //     getIsIsolationPointPage({ current: 1, size: 100 })
+          //       .then((res) => {
+          //         const allPoints = res.data.records; // 假设返回的数据结构是 { records: [点数据] }
+          //
+          //         // 根据 pos.pointId 查找对应的 pointType 和 powerType
+          //         const pointInfo = allPoints.find(
+          //           (point) => point.pointId == pos.pointId
+          //         );
+          //
+          //         if (pointInfo) {
+          //           this.selectedText.push({
+          //             pointName: labelText,
+          //             pointId: pos.pointId,
+          //             pointType: pointInfo.pointType,
+          //             powerType: pointInfo.powerType,
+          //           });
+          //         } else {
+          //           // 如果没有找到对应的点信息,可以处理这种情况
+          //           console.warn(`未找到 pointId 为 ${pos.pointId} 的点信息`);
+          //           this.selectedText.push({
+          //             pointName: labelText,
+          //             pointId: pos.pointId,
+          //             pointType: "",
+          //             powerType: "",
+          //           });
+          //         }
+          //         // console.log(this.selectedText,'默认拿到的points')
+          //         // 触发父组件的 selection-changed 事件
+          //         this.$emit("selection-changed", this.selectedText);
+          //       })
+          //       .catch((error) => {
+          //         console.error("获取隔离点信息失败", error);
+          //       });
+          //   });
+          // }
+
+          this.layer.draw()
+
+        }
+      })
+    }
+
+    //     methods结束
+  }
+}
+</script>
+
+<style scoped lang="scss">
+#container {
+  width: 100%;
+  height: 100%;
+}
+</style>

+ 110 - 138
src/components/separationPoint/workshop.vue

@@ -14,17 +14,19 @@
             @input="handleInputChange"
           />
         </div>
-          <div class="head-container">
-            <el-tree
-              :data="deptOptions"
-              :props="defaultProps"
-              :expand-on-click-node="false"
-              :filter-node-method="filterNode"
-              ref="tree"
-              default-expand-all
-              @node-click="handleNodeClick"
-            />
-          </div>
+        <div class="head-container">
+          <el-tree
+            :data="deptOptions"
+            :props="defaultProps"
+            :expand-on-click-node="false"
+            :filter-node-method="filterNode"
+            ref="tree"
+            node-key="id"
+            default-expand-all
+            @node-click="handleNodeClick"
+            highlight-current
+          />
+        </div>
       </el-col>
       <el-col :span="4" :xs="24">
         <div class="middle">
@@ -38,7 +40,7 @@
 
 <script>
 import { listAllWorkshop } from '@/api/mes/md/workshop'
-import { getIsWorkareaList, getMapData } from '@/api/mes/wa/workarea'
+import { getIsWorkareaList, getMapDataApi } from '@/api/mes/wa/workarea'
 import { selectIsSopById } from '@/api/mes/sop/sopindex'
 import { mapActions, mapGetters } from 'vuex'
 import { listWorkarea } from '@/api/mes/wa/workarea'
@@ -77,49 +79,41 @@ export default {
       },
       // 工作区域名称 树形结构
       workareaName: undefined,
-      treeSelectMap:null,//单节点点击传递map
+      treeSelectMap: null,//单节点点击传递map
       queryParams: {
         current: 1,
         size: -1
+
       }
     }
   },
   watch: {
-    'form.workShop': {
-      handler(newVal) {
-        if (newVal) {
-          // this.imgsrc = localStorage.getItem("img");
-        }
-      }
-    },
-
     // 监听父组件传递的数据
     sopProps: {
       handler(newVal, oldVal) {
-        console.log('sopProps 发生变化', newVal)
-        this.form.workShop = newVal[0].workshopId
-        this.form.producLine = newVal[0].workareaId
-
-        // 确保 handleselect 完成后再调用 handleselectProductLine
-        this.handleselect(newVal[0].workshopId)
-          .then(() => {
-            this.handleselectProductLine(newVal[0].workareaId)
-          })
-          .catch((error) => {
-            console.error('处理车间下拉失败', error)
-          })
-        // 这里是为了拿到sop模板里默认的隔离点选中的数据
-        selectIsSopById(newVal[0].sopId).then((res) => {
-          console.log(res, 'sop')
-          const selectSopPoints = res.data.pointDetailVOList.map((item) => {
-            return item.pointId
+        if(newVal){
+          console.log('sopProps 发生变化', newVal)
+
+          if(newVal[0].workareaId){
+            this.workareaName = newVal[0].workareaId  //新工作区域所返回
+            this.handleselectProductLine(newVal[0].workareaId);
+          }
+
+          this.getTreeselect()
+          // 这里是为了拿到sop模板里默认的隔离点选中的数据
+          selectIsSopById(newVal[0].sopId).then((res) => {
+            console.log(res, 'sop')
+            const selectSopPoints = res.data.pointDetailVOList.map((item) => {
+              return item.pointId
+            })
+            // 调用 Vuex action 来存储 selectSopPoints
+            this.setSelectSopPoints(selectSopPoints)
+
+            this.setSopEdit(true)
+            // console.log(selectSopPoints, 'sop_selectePoints')
           })
-          // 调用 Vuex action 来存储 selectSopPoints
-          this.setSelectSopPoints(selectSopPoints)
+        }
 
-          this.setSopEdit(true)
-          console.log(selectSopPoints, 'sop_selectePoints')
-        })
       },
       immediate: true // 立即执行一次,确保在组件初始化时也能捕获到 jobProps 的值
     },
@@ -131,7 +125,7 @@ export default {
           this.form.workShop = []
           this.form.producLine = ''
           this.form.sopId = ''
-          this.form.t
+
         } else {
           // 在这里处理 jobProps 变化后的逻辑
           this.form.workShop = newVal[0].workshopId
@@ -177,11 +171,11 @@ export default {
   },
 
   mounted() {
-    this.getworkshopList()
     this.getTreeselect()//获取工作区域下拉
+
   },
   computed: {
-    ...mapGetters('sopSelectPoints', ['getSopLook'])
+    ...mapGetters('sopSelectPoints', ['getSopLook','getMapData'])
   },
   methods: {
     ...mapActions('sopSelectPoints', [
@@ -191,56 +185,6 @@ export default {
       'setMapData'
     ]),
 
-    // 获取车间列表
-    getworkshopList() {
-      listAllWorkshop().then((response) => {
-        console.log(response, '车间信息')
-        this.workShopOptions = response.data.map((item) => {
-          return {
-            label: item.workshopName,
-            value: item.workshopId,
-            key: item.workshopCode,
-            attr1: item.attr1
-          }
-        })
-      })
-    },
-
-    // 单选车间下拉
-    handleselect(value) {
-      return new Promise((resolve, reject) => {
-        getIsWorkareaList(value)
-          .then((response) => {
-            this.producLineOptions = response.data.map((item) => {
-              return {
-                label: item.workareaName,
-                value: item.workareaId,
-                key: item.workareaCode,
-                map: item.map
-              }
-            })
-
-            // 根据选择的车间更新 imgsrc
-            const selectedWorkshop = this.workShopOptions.find(
-              (item) => item.value === value
-            )
-            localStorage.setItem('img', selectedWorkshop.attr1)
-
-            if (selectedWorkshop) {
-              this.imgsrc = selectedWorkshop.attr1
-            } else {
-              this.imgsrc = '' // 如果没有找到,清空 imgsrc
-            }
-
-            console.log(this.producLineOptions, 'this.producLineOptions')
-            resolve() // 异步操作成功后 resolve
-          })
-          .catch((error) => {
-            console.error('获取区域下拉列表失败', error)
-            reject(error) // 异步操作失败后 reject
-          })
-      })
-    },
     // 区域下拉
     handleselectProductLine(val) {
       console.log(val, '区域下拉')
@@ -271,7 +215,7 @@ export default {
         data1 = this.form.sopId
       }
 
-      getMapData(data, data1, data2)
+      getMapDataApi(data, data1, data2)
         .then((res) => {
           console.log(res, '获取区域map的解析数据')
           const mapdata = res.data
@@ -281,36 +225,64 @@ export default {
           console.error('获取区域map数据失败', error)
         })
 
-      //   利用组件逆传递 selectProductLine方法传递数据给兄弟组件
-      const selectedOption = this.producLineOptions.filter((item) => {
-        return item.value == val
-      })
-      const selectworkShop = this.workShopOptions.filter((item) => {
-        return item.value == this.form.workShop
-      })
-      // 这里两个赋值是为了方式页面切换数据丢失
-
-      console.log(selectworkShop, selectedOption, 'workshop-components')
-      // 触发自定义事件,传递 selectedOption[0] 数据给父组件
-      console.log(selectedOption, 'selectedOption[0]', this.producLineOptions)
-      this.$emit('product-line-selected', selectedOption[0])
-      this.$emit('work-shop-selected', selectworkShop[0])
+      // //   利用组件逆传递 selectProductLine方法传递数据给兄弟组件
+      // const selectedOption = this.producLineOptions.filter((item) => {
+      //   return item.value == val
+      // })
+      // const selectworkShop = this.workShopOptions.filter((item) => {
+      //   return item.value == this.form.workShop
+      // })
+      // // 这里两个赋值是为了方式页面切换数据丢失
+      //
+      // console.log(selectworkShop, selectedOption, 'workshop-components')
+      // // 触发自定义事件,传递 selectedOption[0] 数据给父组件
+      // console.log(selectedOption, 'selectedOption[0]', this.producLineOptions)
+      // this.$emit('product-line-selected', selectedOption[0])
+      // this.$emit('work-shop-selected', selectworkShop[0])
     },
 
     /** 查询工作区域下拉树结构 */
     getTreeselect() {
       listWorkarea(this.queryParams).then(response => {
-        console.log(response.data.records, '工作区域下拉树形结构');
+        // console.log(response.data.records, '工作区域下拉树形结构');
         // 转换为树形结构
-        this.deptOptions = this.transformToTree(response.data.records);
-        console.log(this.deptOptions, 'deptOptions');
-      });
-    },
+        this.deptOptions = this.transformToTree(response.data.records)
+        // console.log(this.deptOptions, 'deptOptions')
+
+        // 使用递归函数查找匹配的节点
+
+          console.log(this.workareaName,'name')
+          const selectedTreeNode = this.findNodeById(this.deptOptions, this.workareaName)
+          // 调用 handleNodeClick 方法
+          if (selectedTreeNode) {
+            this.handleNodeClick(selectedTreeNode)
+          } else {
+            console.log('未找到匹配的节点')
+          }
+
 
+      })
+    },
+    // 深层次遍历
+    findNodeById(nodes, targetId) {
+      for (let i = 0; i < nodes.length; i++) {
+        const node = nodes[i]
+        if (node.id === targetId) {
+          return node
+        }
+        if (node.children && node.children.length > 0) {
+          const foundNode = this.findNodeById(node.children, targetId)
+          if (foundNode) {
+            return foundNode
+          }
+        }
+      }
+      return null
+    },
     /** 转换数据为树形结构 */
     transformToTree(records) {
-      const recordMap = {}; // 创建一个 Map 以存储所有记录
-      const tree = []; // 最终返回的树形结构
+      const recordMap = {} // 创建一个 Map 以存储所有记录
+      const tree = [] // 最终返回的树形结构
 
       // 初始化所有记录到 Map
       records.forEach(record => {
@@ -318,47 +290,47 @@ export default {
           id: record.workareaId,
           label: record.workareaName,
           map: record.map,
-          mapImg:record.mapImg,
+          mapImg: record.mapImg,
           children: []
-        };
-      });
+        }
+      })
 
       // 遍历记录并构建树
       records.forEach(record => {
-        const parentId = record.parentId;
+        const parentId = record.parentId
 
-        if (parentId === "0") {
+        if (parentId === '0') {
           // 如果是顶层节点,直接添加到树中
-          tree.push(recordMap[record.workareaId]);
+          tree.push(recordMap[record.workareaId])
         } else if (recordMap[parentId]) {
           // 如果有父节点,则将当前节点加入父节点的 children 中
-          recordMap[parentId].children.push(recordMap[record.workareaId]);
+          recordMap[parentId].children.push(recordMap[record.workareaId])
         }
-      });
+      })
 
-      return tree;
+      return tree
     },
 
     // 树节点筛选逻辑
     filterNode(value, data) {
-      if (!value) return true; // 如果没有输入值,显示所有节点
-      return data.label.indexOf(value) !== -1; // 判断节点 label 是否包含输入值
+      if (!value) return true // 如果没有输入值,显示所有节点
+      return data.label.indexOf(value) !== -1 // 判断节点 label 是否包含输入值
     },
     // 监听输入框变化
     handleInputChange() {
-      this.$refs.tree.filter(this.workareaName); // 调用树的 filter 方法
+      this.$refs.tree.filter(this.workareaName) // 调用树的 filter 方法
     },
     // 节点单击事件
     handleNodeClick(data) {
-      console.log(data,'单节点点击')
-      this.treeSelectMap=data
-      // vuex来传递map给选择隔离点
+      console.log(data, '单节点点击')
+      this.treeSelectMap = data
       this.setMapData(data.map)
       // 传递车间地图
-      this.imgsrc=data.mapImg
-      this.workareaName = data.label;
-      this.handleInputChange();
-    },
+      this.imgsrc = data.mapImg
+      this.workareaName = data.label
+      this.handleInputChange()
+      this.$emit('work-shop-selected', data)
+    }
   }
 }
 </script>
@@ -371,7 +343,7 @@ export default {
 }
 
 .middle {
-  width: 1000px;
+  width: 990px;
   height: 850px;
   margin: 10px 0 0 60px;
   background-color: rgb(255, 255, 255);

+ 2 - 2
src/components/separationPoint/workshop copy.vue → src/components/separationPoint/workshop1.vue

@@ -116,7 +116,7 @@ export default {
     // 监听父组件传递的数据
     sopProps: {
       handler(newVal, oldVal) {
-        console.log("sopProps 发生变化", newVal);
+        // console.log("sopProps 发生变化", newVal);
         this.form.workShop = newVal[0].workshopId;
         this.form.producLine = newVal[0].workareaId;
 
@@ -151,7 +151,7 @@ export default {
           this.form.workShop=[];
           this.form.producLine=''
           this.form.sopId=''
-          this.form.t
+          this.form.tick
         }else{
           // 在这里处理 jobProps 变化后的逻辑
           this.form.workShop = newVal[0].workshopId;

+ 2 - 3
src/views/mes/job/jobm/NewOperations.vue

@@ -419,7 +419,7 @@
 
 
 <script>
-import SeparationPoint from "@/components/separationPoint/index.vue";
+import SeparationPoint from "@/components/separationPoint/index1.vue";
 import WorkShop from "@/components/separationPoint/workshop.vue";
 import {
   listJobTicket,
@@ -437,7 +437,7 @@ import { listWorkarea } from "@/api/mes/wa/workarea";
 import { getIsSopPage } from "@/api/mes/sop/sopindex";
 import { listUser } from "@/api/system/user";
 import { listDept } from "@/api/system/dept";
-import IsolationLeftVue from "@/components/separationPoint/index.vue";
+import IsolationLeftVue from "@/components/separationPoint/index1.vue";
 import { genCode } from "@/api/system/autocode/rule";
 
 export default {
@@ -453,7 +453,6 @@ export default {
       //自动生成编码
       autoGenFlag: false,
       activeName: "first",
-
       form: {
         sopId: "",
         pointIds: "",

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 661 - 227
src/views/mes/sop/sopm/NewSop.vue


+ 21 - 28
src/views/mes/sop/sopm/index.vue

@@ -34,22 +34,22 @@
           />
         </el-select>
       </el-form-item>
-      <el-form-item label="车间名称" prop="workshopId">
-        <el-select
-          v-model="queryParams.workshopId"
-          placeholder="请选择车间名称"
-          @change="workShopChange"
-        >
-          <el-option
-            v-for="item in this.workshopList"
-            :key="item.key"
-            :label="item.label"
-            :value="item.value"
-          />
-        </el-select>
-      </el-form-item>
-      <el-form-item label="区域" prop="workareaId">
-        <el-select v-model="queryParams.workareaId" placeholder="请选择区域">
+<!--      <el-form-item label="车间名称" prop="workshopId">-->
+<!--        <el-select-->
+<!--          v-model="queryParams.workshopId"-->
+<!--          placeholder="请选择车间名称"-->
+<!--          @change="workShopChange"-->
+<!--        >-->
+<!--          <el-option-->
+<!--            v-for="item in this.workshopList"-->
+<!--            :key="item.key"-->
+<!--            :label="item.label"-->
+<!--            :value="item.value"-->
+<!--          />-->
+<!--        </el-select>-->
+<!--      </el-form-item>-->
+      <el-form-item label="工作区域" prop="workareaId">
+        <el-select v-model="queryParams.workareaId" placeholder="请选择工作区域">
           <el-option
             v-for="dict in this.workareaList"
             :key="dict.value"
@@ -157,14 +157,7 @@
         v-show="this.noShow"
       /> -->
       <el-table-column label="SOP编号" align="center" prop="sopCode">
-        <!-- <template slot-scope="scope">
-          <el-button
-            type="text"
-            @click="handleView(scope.row)"
-            v-hasPermi="['mes:sop:sopm:query']"
-            >{{ scope.row.teamCode }}
-          </el-button>
-        </template> -->
+
       </el-table-column>
       <el-table-column label="SOP名称" align="center" prop="sopName" />
       <el-table-column label="状态" align="center" prop="sopStatus">
@@ -175,15 +168,15 @@
           />
         </template>
       </el-table-column>
-      <el-table-column label="车间名称" align="center" prop="workshopName">
-      </el-table-column>
+<!--      <el-table-column label="车间名称" align="center" prop="workshopName">-->
+<!--      </el-table-column>-->
 
       <el-table-column label="SOP类型" align="center" prop="sopType">
         <template slot-scope="scope">
           <dict-tag :options="dict.type.sop_type" :value="scope.row.sopType" />
         </template>
       </el-table-column>
-      <el-table-column label="区域" align="center" prop="workareaName">
+      <el-table-column label="工作区域" align="center" prop="workareaName">
       </el-table-column>
       <el-table-column label="隔离点数量" align="center" prop="pointCount">
       </el-table-column>
@@ -244,7 +237,7 @@ import {
   deleteIsSopBySopIds,
   selectIsSopById,
 } from "@/api/mes/sop/sopindex";
-import { genCode } from "@/api/system/autocode/rule";
+
 import { mapActions } from "vuex";
 export default {
   name: "Team",

+ 130 - 0
test.html

@@ -0,0 +1,130 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>Enhanced Button Selection</title>
+  <style>
+    canvas {
+      border: 1px solid #000;
+      display: block;
+      margin: 0 auto;
+    }
+  </style>
+</head>
+<body>
+<canvas id="buttonCanvas" width="480" height="360"></canvas>
+
+<script>
+  const canvas = document.getElementById('buttonCanvas');
+  const ctx = canvas.getContext('2d');
+
+  const buttonWidth = 100;
+  const buttonHeight = 100;
+  const padding = 10;
+
+  const rows = 3;
+  const cols = 4;
+
+  const images = [
+    'https://via.placeholder.com/100/FF0000/FFFFFF?text=Red',
+    'https://via.placeholder.com/100/0000FF/FFFFFF?text=Blue',
+    'https://via.placeholder.com/100/00FF00/FFFFFF?text=Green',
+    'https://via.placeholder.com/100/FFFFFF/000000?text=White',
+    'https://via.placeholder.com/100/FFAA00/FFFFFF?text=Orange',
+    'https://via.placeholder.com/100/AAAAAA/FFFFFF?text=Gray',
+    'https://via.placeholder.com/100/FF00FF/FFFFFF?text=Magenta',
+    'https://via.placeholder.com/100/00FFFF/FFFFFF?text=Cyan',
+    'https://via.placeholder.com/100/FFFF00/FFFFFF?text=Yellow',
+    'https://via.placeholder.com/100/000000/FFFFFF?text=Black',
+    'https://via.placeholder.com/100/FF7777/FFFFFF?text=Pink',
+    'https://via.placeholder.com/100/7777FF/FFFFFF?text=Purple',
+  ];
+
+  let buttons = [];
+  let selectedButtons = new Set();
+
+  function loadImages(imageUrls, callback) {
+    const loadedImages = [];
+    let imagesToLoad = imageUrls.length;
+
+    imageUrls.forEach((url, index) => {
+      const img = new Image();
+      img.src = url;
+      img.onload = () => {
+        loadedImages[index] = img;
+        imagesToLoad--;
+        if (imagesToLoad === 0) callback(loadedImages);
+      };
+    });
+  }
+
+  function createButtons(images) {
+    let x, y;
+    for (let row = 0; row < rows; row++) {
+      for (let col = 0; col < cols; col++) {
+        const index = row * cols + col;
+        x = col * (buttonWidth + padding);
+        y = row * (buttonHeight + padding);
+        buttons.push({ x, y, width: buttonWidth, height: buttonHeight, image: images[index], id: index });
+      }
+    }
+  }
+
+  function drawButtons() {
+    ctx.clearRect(0, 0, canvas.width, canvas.height);
+    buttons.forEach(button => {
+      // 绘制按钮图片
+      ctx.drawImage(button.image, button.x, button.y, button.width, button.height);
+
+      if (selectedButtons.has(button.id)) {
+        // 绘制半透明灰色遮罩
+        ctx.fillStyle = 'rgba(50, 50, 50, 0.5)';
+        ctx.fillRect(button.x, button.y, button.width, button.height);
+
+        // 绘制白色对勾图标
+        ctx.fillStyle = 'white';
+        ctx.font = '20px Arial';
+        ctx.textAlign = 'center';
+        ctx.textBaseline = 'middle';
+        ctx.fillText('✔', button.x + button.width / 2, button.y + button.height / 2);
+
+        // 添加边框
+        ctx.strokeStyle = 'black';
+        ctx.lineWidth = 5;
+        ctx.strokeRect(button.x, button.y, button.width, button.height);
+      }
+    });
+  }
+
+  function handleCanvasClick(event) {
+    const rect = canvas.getBoundingClientRect();
+    const mouseX = event.clientX - rect.left;
+    const mouseY = event.clientY - rect.top;
+
+    buttons.forEach(button => {
+      if (
+        mouseX >= button.x &&
+        mouseX <= button.x + button.width &&
+        mouseY >= button.y &&
+        mouseY <= button.y + button.height
+      ) {
+        if (selectedButtons.has(button.id)) {
+          selectedButtons.delete(button.id); // 取消选中
+        } else {
+          selectedButtons.add(button.id); // 选中按钮
+        }
+        drawButtons();
+      }
+    });
+  }
+
+  canvas.addEventListener('click', handleCanvasClick);
+
+  loadImages(images, loadedImages => {
+    createButtons(loadedImages);
+    drawButtons();
+  });
+</script>
+</body>
+</html>

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä