cuiqian2004
2025-10-17 68ce9382090846dc3a03a057a18a7d09f30e45e5
pages/map/js/ctx.js
@@ -21,13 +21,16 @@
   Result
} from "ant-design-vue";
import {
   session,
} from "../../../comm/utils.js"
// import okIcon from '../../../static/images/confirm.svg';
// import cancelIcon from '../../../static/images/remove.svg';
export default {
   data() {
      return {
         vehicleIp: "",
         canvasId: "",
         canvas: null,
         eleWidth: 0,
@@ -59,18 +62,18 @@
            img_x: 1,
            img_y: 1
         },
         pressObjTimer: 0
         pressObjTimer: 0,
         selectable: true,
      }
   },
   mounted() {
      console.log("ctx mounted")
      this.init()
   },
   methods: {
      async init(ip) {
         try {
            this.agvObj = null
            const _this = this
            fabric.Object.prototype.setControlsVisibility({
               mt: false, // 中间上
@@ -104,6 +107,7 @@
            canvas.setAttribute("id", this.canvasId)
            canvas.setAttribute("type", "2d")
            cantainerEl.appendChild(canvas)
            this.canvas = new fabric.Canvas(this.canvasId, {
               allowTouchScrolling: true, // 允许触摸滚动
               selection: true,
@@ -117,6 +121,8 @@
               renderOnAddRemove: false,
               imageSmoothingEnabled: true
            })
            this.canvas.clear()
            this.eleWidth = cantainerEl.clientWidth
            this.eleHeight = cantainerEl.clientHeight
@@ -141,6 +147,28 @@
         } catch (ex) {
            this.showError(ex)
         }
      },
      destroyCanvas() {
         console.log("destroyCanvas")
         if (this.canvas) {
            // 2. 移除所有事件监听器
            this.removeAllEventListeners();
            // 3. 清空所有对象(分批处理避免阻塞)
            this.clearObjects();
            // 4. 销毁Fabric.js实例
            this.canvas.dispose();
            // 6. 清除所有变量引用
            this.cleanupReferences();
            this.canvas = null
            // 通知Vue层销毁完成
            this.$ownerInstance.callMethod('receiveRenderData', {
               method: "destroy_complete",
               param: true
            });
         }
      },
      patchFabricForUniApp(canvas) {
@@ -353,7 +381,9 @@
            mainObj: obj
         });
         this.canvas.add(objGroup)
         this.$ownerInstance.callMethod('receiveRenderData', {
            method: "cancel_positioning_agv",
         });
      },
      canvasEventListener() {
@@ -375,7 +405,7 @@
            _this.canvas.requestRenderAll();
            _this.onSelectionChanage()
            //_this.selectionChangeCanvas();
         });
         _this.canvas.on("selection:updated", function(e) {
            console.log("selection:updated", e);
@@ -409,9 +439,6 @@
         _this.canvas.on("object:modified", function(e) {
            // console.log("object:modified", e.target);
            // _this.resizetCanvas();
         });
         _this.canvas.on("object:moving", function(e) {
            console.log("object:moving", e.target);
@@ -423,162 +450,12 @@
         cantainerEl.addEventListener('touchstart', function(e) {
            //      console.log('touchstart:', e.touches.length);
            e.preventDefault(); // 阻止默认行为
            _this.canvas.fire('touch:start', {
               e: e
            });
            _this.canvasTouchStart(e)
            //   _this.canvas._onMouseDown(e);
            _this.pointerSelectObject(e)
            if (!_this.canvas.getActiveObject()) {
               // 根据触摸点数量判断交互类型
               if (e.touches.length === 1) {
                  _this.handleSingleTouch(e.touches[0]);
               } else if (e.touches.length >= 2) {
                  _this.handleMultiTouch(e.touches);
               }
            } else {
               if (e.touches.length === 1) {
                  const touch = e.touches[0]
                  _this.lastPosX = touch.clientX;
                  _this.lastPosY = touch.clientY;
                  const list = _this.canvas.getActiveObjects()
                  if (list.length === 1) {
                     if (!_this.objEditing) {
                        this.pressObjTimer = setTimeout(function() {
                           const zoom = _this.canvas.getZoom();
                           const vpt = _this.canvas.viewportTransform;
                           if (list[0].eleType == "station") {
                              _this.setAllObjectSelectable(false)
                              list[0].set({
                                 selectable: true
                              })
                              let deltaX = list[0].left * zoom
                              let deltaY = list[0].top * zoom
                              const scale = zoom < 1 ? zoom : 1
                              _this.$ownerInstance.callMethod('receiveRenderData', {
                                 method: "edit_station",
                                 station: list[0].data,
                                 view: {
                                    x: vpt[4] + deltaX,
                                    y: vpt[5] + deltaY,
                                    // x: e.touches[0].clientX,
                                    // y: e.touches[0].clientY,
                                    width: list[0].width * scale,
                                    height: list[0].height * scale
                                 }
                              });
                           } else if (list[0].eleType == "public_teaching") {
                              const pt = _this.canvas.getPointer(touch); // ← 关键
                              // 2. pointer 就是画布坐标
                              _this.$ownerInstance.callMethod('receiveRenderData', {
                                 method: "select_teaching_path",
                                 data: list[0].data,
                                 type: "public",
                                 point: pt
                              });
                           } else if (list[0].eleType == "station_teaching") {
                              _this.$ownerInstance.callMethod('receiveRenderData', {
                                 method: "select_teaching_path",
                                 data: list[0].data,
                                 type: "station",
                                 point: pt
                              });
                           }
                        }, 1000); //
                     }
                     let activeObj = list[0]
                     if (activeObj.eleType == "region_pt_add") {
                        const data = activeObj.mainObj?.data
                        data.path.push({
                           x: _this.getActualXFromImg(activeObj.left),
                           y: _this.getActualYFromImg(activeObj.top)
                        })
                        _this.updateRegion(activeObj.mainObj, data)
                     } else if (activeObj.eleType == "cmd") {
                        let data = activeObj.mainObj.data
                        if (activeObj.mainObj.eleType == "edit_teaching") {
                           let left = _this.getActualXFromImg(activeObj.left)
                           let top = _this.getActualYFromImg(activeObj.top)
                           let right = _this.getActualXFromImg(activeObj.left + activeObj.width)
                           let bottom = _this.getActualYFromImg(activeObj.top + activeObj.height)
                           data = [
                              [left, top],
                              [left, bottom],
                              [right, bottom],
                              [right, top]
                           ]
                           console.log(data)
                        }
                        _this.$ownerInstance.callMethod('receiveRenderData', {
                           method: "edit_finish",
                           cmd: activeObj.id,
                           type: activeObj.mainObj.eleType,
                           data: data,
                        });
                        if (activeObj.id == "ok") {
                           if (activeObj.mainObj.eleType == "region") {
                              _this.addRegionFinish(activeObj.mainObj)
                           } else if (activeObj.mainObj.eleType == "virtual_wall") {
                              _this.addVirtualWallFinish(activeObj.mainObj)
                           }
                        }
                        if (activeObj.mainObj.eleType == "edit_teaching") {
                           let list = _this.canvas.getObjects() || []
                           for (let i2 in list) {
                              const obj = list[i2]
                              obj.set({
                                 opacity: 1
                              })
                           }
                           if (activeObj.mainObj?.mainObj) {
                              activeObj.mainObj.mainObj.set({
                                 selectable: true
                              })
                           }
                           const ptObjs = activeObj.mainObj.ptObjs || []
                           _this.canvas.remove(activeObj.mainObj)
                           for (let i2 in ptObjs) {
                              const obj = ptObjs[i2]
                              _this.canvas.remove(obj)
                           }
                        }
                        if (activeObj.mainObj.eleType == "station") {
                           _this.setAllObjectSelectable(true)
                           activeObj.mainObj.tipObj.set({
                              left: activeObj.mainObj.left,
                              top: activeObj.mainObj.top - activeObj.mainObj.height / 2 -
                                 activeObj.mainObj
                                 .tipObj.height / 2,
                              visible: true
                           })
                           activeObj.mainObj.tipObj.setCoords()
                        }
                        _this.closeOkCancelControl()
                        _this.canvas.requestRenderAll()
                     }
                     // else if (activeObj.eleType == "station") {
                     //    _this.$ownerInstance.callMethod('receiveRenderData', {
                     //       method: "select_station",
                     //       data: activeObj.data,
                     //       select: activeObj.mark ? false : true
                     //    });
                     // }
                  }
               } else if (e.touches.length >= 2) {
                  _this.handleMultiTouch(e.touches);
               }
            }
         });
         cantainerEl.addEventListener('touchmove', function(e) {
@@ -586,43 +463,23 @@
            //   console.log('touchmove:', e.touches.length);
            e.preventDefault(); // 阻止默认行为
            // 处理移动
            const list = _this.canvas.getActiveObjects()
            if (list.length == 0) {
               if (e.touches.length === 1) {
                  _this.handleSingleTouchMove(e.touches[0]);
               } else if (e.touches.length >= 2) {
                  _this.handleMultiTouchMove(e.touches);
               }
            } else {
               if (e.touches.length === 1) {
                  if (list.length > 1 || list[0].lockMovementX) {
                     if (list[0].lockMovementX) {
                        _this.canvas.discardActiveObject();
                     }
                     _this.handleSingleTouchMove(e.touches[0]);
                  } else {
                     const touch = e.touches[0]
                     const deltaX = touch.clientX - this.lastPosX;
                     const deltaY = touch.clientY - this.lastPosY;
                     if (Math.abs(deltaX) > 20)
                        clearTimeout(this.pressObjTimer);
                  }
               } else if (e.touches.length >= 2) {
                  _this.canvas.discardActiveObject();
                  _this.handleMultiTouchMove(e.touches);
               }
            }
            _this.canvasTouchMove(e)
         });
         cantainerEl.addEventListener('touchend', function(e) {
            //   _this.canvas._onMouseUp(e);
            //   console.log('touchend:');
            e.preventDefault(); // 阻止默认行为
            _this.touchPoint = {
               x: 0,
               y: 0
            };
            if (_this.pressObjTimer) {
               clearTimeout(_this.pressObjTimer);
               _this.pressObjTimer = null
            }
            const activeObj = _this.canvas.getActiveObject()
            if (!activeObj) {
               // 处理结束事件
@@ -637,24 +494,220 @@
               //    _this.canvas.discardActiveObject();
               // }
            }
            if (this.pressObjTimer) {
               clearTimeout(this.pressObjTimer);
               this.pressObjTimer = null
            }
         });
         cantainerEl.addEventListener('touchcancel', function(e) {
            //   console.log('touchcancel:');
            if (this.pressObjTimer) {
               clearTimeout(this.pressObjTimer);
               this.pressObjTimer = null
            if (_this.pressObjTimer) {
               clearTimeout(_this.pressObjTimer);
               _this.pressObjTimer = null
            }
            // const activeObj = _this.canvas.getActiveObject()
            // if (activeObj) {
            //    if (activeObj.lockMovementX) {
            //       _this.canvas.discardActiveObject();
            //    }
            // }
         })
      },
      canvasTouchStart(e) {
         const _this = this
         _this.pointerSelectObject(e)
         if (!_this.canvas.getActiveObject()) {
            // 根据触摸点数量判断交互类型
            if (e.touches.length === 1) {
               _this.handleSingleTouch(e.touches[0]);
            } else if (e.touches.length >= 2) {
               _this.handleMultiTouch(e.touches);
            }
         } else {
            if (e.touches.length === 1) {
               const touch = e.touches[0]
               _this.lastPosX = touch.clientX;
               _this.lastPosY = touch.clientY;
               const list = _this.canvas.getActiveObjects()
               if (list.length === 1) {
                  if (!_this.objEditing) {
                     this.pressObjTimer = setTimeout(function() {
                        const zoom = _this.canvas.getZoom();
                        const vpt = _this.canvas.viewportTransform;
                        if (list[0].eleType == "station") {
                           _this.setAllObjectSelectable(false)
                           list[0].set({
                              selectable: true
                           })
                           let deltaX = list[0].left * zoom
                           let deltaY = list[0].top * zoom
                           const scale = zoom < 1 ? zoom : 1
                           _this.$ownerInstance.callMethod('receiveRenderData', {
                              method: "edit_station",
                              station: list[0].data,
                              view: {
                                 x: vpt[4] + deltaX,
                                 y: vpt[5] + deltaY,
                                 // x: e.touches[0].clientX,
                                 // y: e.touches[0].clientY,
                                 width: list[0].width * scale,
                                 height: list[0].height * scale
                              }
                           });
                        } else if (list[0].eleType == "public_teaching") {
                           const pt = _this.canvas.getPointer(touch); // ← 关键
                           // 2. pointer 就是画布坐标
                           _this.$ownerInstance.callMethod('receiveRenderData', {
                              method: "select_teaching_path",
                              data: list[0].data,
                              type: "public",
                              point: pt
                           });
                        } else if (list[0].eleType == "station_teaching") {
                           _this.$ownerInstance.callMethod('receiveRenderData', {
                              method: "select_teaching_path",
                              data: list[0].data,
                              type: "station",
                              point: pt
                           });
                        }
                     }, 1000); //
                  }
                  let activeObj = list[0]
                  if (activeObj.eleType == "region_pt_add") {
                     const data = activeObj.mainObj?.data
                     data.path.push({
                        x: _this.getActualXFromImg(activeObj.left) || 0,
                        y: _this.getActualYFromImg(activeObj.top) || 0
                     })
                     _this.updateRegion(activeObj.mainObj, data)
                  } else if (activeObj.eleType == "cmd") {
                     let data = activeObj.mainObj.data
                     const objCmdMain = activeObj.mainObj
                     if (objCmdMain.eleType == "edit_teaching") {
                        const left = _this.getActualXFromImg(objCmdMain.left)
                        const top = _this.getActualYFromImg(objCmdMain.top)
                        const right = _this.getActualXFromImg(objCmdMain.left + objCmdMain.width)
                        const bottom = _this.getActualYFromImg(objCmdMain.top + objCmdMain.height)
                        data = []
                        if (Number.isNaN(left) || Number.isNaN(top) || Number.isNaN(right) || Number
                           .isNaN(bottom)) {
                           const now = new Date()
                           const date = `${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}`
                           _this.$ownerInstance.callMethod('receiveRenderData', {
                              method: "show_log",
                              data: {
                                 date,
                                 method: `POST`,
                                 url: "app/log/edit_teaching",
                                 param: objCmdMain,
                                 statusCode: 100,
                                 data: _this.mapInfo
                              },
                           });
                        }
                        data.push([left, top])
                        data.push([left, bottom])
                        data.push([right, bottom])
                        data.push([right, top])
                        console.log(data)
                     }
                     _this.$ownerInstance.callMethod('receiveRenderData', {
                        method: "edit_finish",
                        cmd: activeObj.id,
                        type: objCmdMain.eleType,
                        data: data,
                     });
                     if (activeObj.id == "ok") {
                        if (objCmdMain.eleType == "region") {
                           _this.addRegionFinish(activeObj.mainObj)
                        } else if (activeObj.mainObj.eleType == "virtual_wall") {
                           _this.addVirtualWallFinish(activeObj.mainObj)
                        }
                     }
                     if (objCmdMain.eleType == "edit_teaching") {
                        let list = _this.canvas.getObjects() || []
                        for (let i2 in list) {
                           const obj = list[i2]
                           obj.set({
                              opacity: 1
                           })
                        }
                        if (objCmdMain?.mainObj) {
                           objCmdMain.mainObj.set({
                              selectable: true
                           })
                        }
                        const ptObjs = objCmdMain.ptObjs || []
                        _this.canvas.remove(objCmdMain)
                        for (let i2 in ptObjs) {
                           const obj = ptObjs[i2]
                           _this.canvas.remove(obj)
                        }
                     }
                     if (objCmdMain.eleType == "station") {
                        _this.setAllObjectSelectable(true)
                        objCmdMain.tipObj.set({
                           left: objCmdMain.left,
                           top: objCmdMain.top - objCmdMain.height / 2 -
                              objCmdMain.tipObj.height / 2,
                           visible: true
                        })
                        objCmdMain.tipObj.setCoords()
                     }
                     _this.closeOkCancelControl()
                     _this.canvas.requestRenderAll()
                  }
                  // else if (activeObj.eleType == "station") {
                  //    _this.$ownerInstance.callMethod('receiveRenderData', {
                  //       method: "select_station",
                  //       data: activeObj.data,
                  //       select: activeObj.mark ? false : true
                  //    });
                  // }
               }
            } else if (e.touches.length >= 2) {
               _this.handleMultiTouch(e.touches);
            }
         }
      },
      canvasTouchMove(e) {
         const _this = this
         const list = _this.canvas.getActiveObjects()
         if (list.length == 0) {
            if (e.touches.length === 1) {
               _this.handleSingleTouchMove(e.touches[0]);
            } else if (e.touches.length >= 2) {
               _this.handleMultiTouchMove(e.touches);
            }
         } else {
            if (e.touches.length === 1) {
               if (list.length > 1 || list[0].lockMovementX) {
                  if (list[0].lockMovementX) {
                     _this.canvas.discardActiveObject();
                  }
                  _this.handleSingleTouchMove(e.touches[0]);
               } else {
                  const touch = e.touches[0]
                  const deltaX = touch.clientX - this.lastPosX;
                  const deltaY = touch.clientY - this.lastPosY;
                  if (Math.abs(deltaX) > 20)
                     clearTimeout(this.pressObjTimer);
               }
            } else if (e.touches.length >= 2) {
               _this.canvas.discardActiveObject();
               _this.handleMultiTouchMove(e.touches);
            }
         }
      },
      // 计算点到线段的距离
@@ -735,8 +788,7 @@
         const objects = this.canvas.getObjects();
         objects.splice(0, 1);
         this.canvas.discardActiveObject()
         const objActive = this.canvas.getActiveObject()
         let pointerList = []
         let pointerList2 = []
         for (let i = objects.length - 1; i >= 0; i--) {
@@ -745,14 +797,14 @@
            if (obj.selectable && obj.opacity > 0) {
               if (obj instanceof fabric.Path || obj instanceof fabric.Line) {
                  if (this.isPointOnStroke(obj, pointer)) {
                     console.log(i, obj.eleType)
                     //   console.log(i, obj.eleType)
                     pointerList.unshift(obj)
                  }
               } else {
                  const isHit = obj.containsPoint(pointer);
                  if (isHit) {
                     console.log(i, obj.eleType)
                     //   console.log(i, obj.eleType)
                     pointerList.unshift(obj)
                  }
@@ -764,26 +816,44 @@
            const obj = pointerList[i];
            if (obj instanceof fabric.Path || obj instanceof fabric.Line) {
               if (this.isPointOnStroke(obj, pointer, 1)) {
                  this.canvas.discardActiveObject()
                  this.canvas.setActiveObject(obj);
                  this.canvas.requestRenderAll();
                  if (objActive != obj) {
                     if (this.pressObjTimer) {
                        clearTimeout(this.pressObjTimer);
                        this.pressObjTimer = null
                     }
                     this.canvas.discardActiveObject()
                     this.canvas.setActiveObject(obj);
                     this.canvas.requestRenderAll();
                  }
                  return
               }
               pointerList2.unshift(obj)
            } else {
               this.canvas.discardActiveObject()
               this.canvas.setActiveObject(obj);
               this.canvas.requestRenderAll();
               if (objActive != obj) {
                  if (this.pressObjTimer) {
                     clearTimeout(this.pressObjTimer);
                     this.pressObjTimer = null
                  }
                  this.canvas.discardActiveObject()
                  this.canvas.setActiveObject(obj);
                  this.canvas.requestRenderAll();
               }
               return
            }
         }
         if (pointerList2.length > 0) {
            if (pointerList2.length == 1) {
               const obj = pointerList2[i];
               this.canvas.discardActiveObject()
               this.canvas.setActiveObject(obj);
               this.canvas.requestRenderAll();
               const obj = pointerList2[0];
               if (objActive != obj) {
                  if (this.pressObjTimer) {
                     clearTimeout(this.pressObjTimer);
                     this.pressObjTimer = null
                  }
                  this.canvas.discardActiveObject()
                  this.canvas.setActiveObject(obj);
                  this.canvas.requestRenderAll();
               }
               return
            }
            pointerList = pointerList2
@@ -791,9 +861,15 @@
            for (let i = pointerList.length - 1; i >= 0; i--) {
               const obj = pointerList[i];
               if (this.isPointOnStroke(obj, pointer, 2)) {
                  this.canvas.discardActiveObject()
                  this.canvas.setActiveObject(obj);
                  this.canvas.requestRenderAll();
                  if (objActive != obj) {
                     if (this.pressObjTimer) {
                        clearTimeout(this.pressObjTimer);
                        this.pressObjTimer = null
                     }
                     this.canvas.discardActiveObject()
                     this.canvas.setActiveObject(obj);
                     this.canvas.requestRenderAll();
                  }
                  return
               }
               pointerList2.unshift(obj)
@@ -801,10 +877,16 @@
         }
         if (pointerList2.length > 0) {
            if (pointerList2.length == 1) {
               const obj = pointerList2[i];
               this.canvas.discardActiveObject()
               this.canvas.setActiveObject(obj);
               this.canvas.requestRenderAll();
               const obj = pointerList2[0];
               if (objActive != obj) {
                  if (this.pressObjTimer) {
                     clearTimeout(this.pressObjTimer);
                     this.pressObjTimer = null
                  }
                  this.canvas.discardActiveObject()
                  this.canvas.setActiveObject(obj);
                  this.canvas.requestRenderAll();
               }
               return
            }
            pointerList = pointerList2
@@ -812,9 +894,15 @@
            for (let i = pointerList.length - 1; i >= 0; i--) {
               const obj = pointerList[i];
               if (this.isPointOnStroke(obj, pointer, 3)) {
                  this.canvas.discardActiveObject()
                  this.canvas.setActiveObject(obj);
                  this.canvas.requestRenderAll();
                  if (objActive != obj) {
                     if (this.pressObjTimer) {
                        clearTimeout(this.pressObjTimer);
                        this.pressObjTimer = null
                     }
                     this.canvas.discardActiveObject()
                     this.canvas.setActiveObject(obj);
                     this.canvas.requestRenderAll();
                  }
                  return
               }
               pointerList2.unshift(obj)
@@ -822,10 +910,16 @@
         }
         if (pointerList2.length > 0) {
            if (pointerList2.length == 1) {
               const obj = pointerList2[i];
               this.canvas.discardActiveObject()
               this.canvas.setActiveObject(obj);
               this.canvas.requestRenderAll();
               const obj = pointerList2[0];
               if (objActive != obj) {
                  if (this.pressObjTimer) {
                     clearTimeout(this.pressObjTimer);
                     this.pressObjTimer = null
                  }
                  this.canvas.discardActiveObject()
                  this.canvas.setActiveObject(obj);
                  this.canvas.requestRenderAll();
               }
               return
            }
            pointerList = pointerList2
@@ -833,9 +927,15 @@
            for (let i = pointerList.length - 1; i >= 0; i--) {
               const obj = pointerList[i];
               if (this.isPointOnStroke(obj, pointer, 4)) {
                  this.canvas.discardActiveObject()
                  this.canvas.setActiveObject(obj);
                  this.canvas.requestRenderAll();
                  if (objActive != obj) {
                     if (this.pressObjTimer) {
                        clearTimeout(this.pressObjTimer);
                        this.pressObjTimer = null
                     }
                     this.canvas.discardActiveObject()
                     this.canvas.setActiveObject(obj);
                     this.canvas.requestRenderAll();
                  }
                  return
               }
               pointerList2.unshift(obj)
@@ -844,49 +944,17 @@
         if (pointerList2.length > 0) {
            const obj = pointerList2[pointerList2.length - 1];
            this.canvas.discardActiveObject()
            this.canvas.setActiveObject(obj);
            this.canvas.requestRenderAll();
            if (objActive != obj) {
               if (this.pressObjTimer) {
                  clearTimeout(this.pressObjTimer);
                  this.pressObjTimer = null
               }
               this.canvas.discardActiveObject()
               this.canvas.setActiveObject(obj);
               this.canvas.requestRenderAll();
            }
            return
         }
         // objects.forEach(obj => {
         //    if (obj instanceof fabric.Path || obj instanceof fabric.Line) {
         //       // 设置一个“点击容忍度”,比如 5 像素
         //       const tolerance = 5;
         //       // 临时扩大路径的点击区域
         //       const originalStrokeWidth = obj.strokeWidth;
         //       obj.strokeWidth = originalStrokeWidth + tolerance * 2;
         //       const isHit = obj.intersectsWithPointer(pointer);
         //       // 恢复原始宽度
         //       obj.strokeWidth = originalStrokeWidth;
         //       if (isHit) {
         //          pointerList.push(obj)
         //       }
         //    } else if (obj instanceof fabric.Rect || obj instanceof fabric.Ellipse) {
         //       const isHit = obj.containsPoint(pointer);
         //       if (isHit) {
         //          pointerList.push(obj)
         //       }
         //    }
         //    // else if (obj instanceof fabric.Group )
         //    // {
         //    //    const objects2 = obj.getObjects();
         //    //    const isHit = obj.containsPoint(pointer);
         //    //    if (isHit) {
         //    //       pointerList.push(obj)
         //    //    }
         //    // }
         // });
         // if (pointerList.length === 0) {
         //    return
         // }
         // const obj = pointerList.pop()
         // this.canvas.discardActiveObject()
         // this.canvas.setActiveObject(obj)
      },
      objectMoving(target) {
         const _this = this
@@ -911,7 +979,7 @@
            });
            _this.createOkCancelControl(obj)
            _this.canvas.renderAll()
            _this.canvas.requestRenderAll()
         } else if (target?.eleType == "edit_teaching") {
            _this.updateEditTeachingPath(target)
@@ -933,7 +1001,7 @@
            else
               _this.updateRegion(target, data)
            _this.canvas.renderAll()
            _this.canvas.requestRenderAll()
         } else if (target?.eleType == "wall_pt") {
            const data = target.mainObj?.data
@@ -963,7 +1031,7 @@
            _this.updateVirtualWall(target.mainObj, data)
            _this.canvas.renderAll()
            _this.canvas.requestRenderAll()
         } else if (target?.eleType == "region_pt") {
            const data = target.mainObj?.data
            if (!data)
@@ -995,10 +1063,8 @@
                  }
                  _this.updateRegion(target.mainObj, data)
               }
               _this.canvas.renderAll()
               _this.canvas.requestRenderAll()
            }
         } else if (target?.eleType == "edit_teaching_pt") {
@@ -1081,105 +1147,7 @@
      },
      onSelectionChanage() {
         const _this = this
         // const list = _this.canvas.getActiveObjects()
         // if (list.length === 1) {
         //    if (list[0].eleType == "station") {
         //       _this.$ownerInstance.callMethod('receiveRenderData', {
         //          method: "selected_change",
         //          type: list[0].eleType,
         //          param: list[0].data
         //       });
         //    } else if (list[0].eleType == "agv") {
         //       _this.$ownerInstance.callMethod('receiveRenderData', {
         //          method: "selected_change",
         //          type: list[0].eleType,
         //          param: list[0].data
         //       });
         //    } else if (list[0].eleType == "agv_line") {
         //       _this.$ownerInstance.callMethod('receiveRenderData', {
         //          method: "selected_change",
         //          type: list[0].eleType,
         //          param: list[0].data
         //       });
         //    } else {
         //       _this.$ownerInstance.callMethod('receiveRenderData', {
         //          method: "selected_change",
         //          type: ""
         //       });
         //    }
         // } else {
         //    _this.$ownerInstance.callMethod('receiveRenderData', {
         //       method: "selected_change",
         //       type: ""
         //    });
         // }
      },
      safeLoadImage(url, maxSize = 2048) {
         console.log(url)
         return new Promise((resolve) => {
            const img = new Image();
            img.onload = () => {
               // 检查尺寸是否超出限制
               const scale = Math.min(
                  maxSize / Math.max(img.width, img.height),
                  1
               );
               resolve(new fabric.Image(img, {
                  scaleX: scale,
                  scaleY: scale
               }));
            };
            img.onerror = () => {
               console.error('图片加载失败');
               resolve(null);
            };
            img.src = url;
         });
      },
      safeLoadImageData(data, maxSize = 2048) {
         //console.log("safeLoadImageData")
         const _this = this
         return new Promise((resolve) => {
            let base64Image = data
            if (base64Image.indexOf("data:image/png;base64,") < 0) {
               base64Image = "data:image/png;base64," + data
            }
            // var img = new fabric.Image();
            // img.setSrc(base64Image, function() {
            //    console.log("img", JSON.stringify(img))
            //    const scale = Math.min(
            //       maxSize / Math.max(img.width, img.height),
            //       1
            //    );
            //    img.set({
            //       scaleX: scale,
            //       scaleY: scale
            //    })
            //    resolve(img)
            // });
            fabric.Image.fromURL(base64Image, {
               crossOrigin: 'anonymous' // 重要:设置跨域
            }).then((img) => {
               //console.log("img",JSON.stringify(img))
               const scale = Math.min(
                  maxSize / Math.max(img.width, img.height),
                  1
               );
               img.set({
                  scaleX: scale,
                  scaleY: scale,
               })
               resolve(img)
            }).catch((err) => {
               console.error("图片加载失败", err)
               _this.showError("图片加载失败")
               resolve(null);
            })
         });
      },
      // 将 Base64 转为 Blob,再生成 URL
      base64ToBlob(base64, mime) {
@@ -1266,6 +1234,7 @@
               _this.$ownerInstance.callMethod('receiveRenderData', {
                  method: "set_backgroud_progress",
                  type: "error",
                  msg: err
               });
               console.error("图片加载失败", err)
               reject(new Error('图片加载失败'));
@@ -1285,14 +1254,69 @@
         }
         return null;
      },
      clearObjects() {
         const list = this.canvas.getObjects()
         list.splice(0, 1)
         for (let i in list) {
            const obj = list[i]
            this.canvas.remove(obj)
      // 移除所有事件监听器
      removeAllEventListeners() {
         if (!this.canvas) return;
         // 移除Fabric.js内置事件
         this.canvas.off();
         // 移除自定义事件监听器
         const cantainerEl = document.getElementById("canvasMap");
         if (cantainerEl) {
            cantainerEl.replaceWith(cantainerEl.cloneNode(true));
         }
      },
      clearObjects() {
         if (!this.canvas) return;
         this.canvas.discardActiveObject()
         const objects = this.canvas.getObjects()
         const batchSize = 50; // 每批删除50个对象
         for (let i = 0; i < objects.length; i += batchSize) {
            const batch = objects.slice(i, i + batchSize);
            this.canvas.remove(...batch);
         }
         this.canvas.clear();
         this.workSpace = null;
         this.agvObj = null;
         this.curTeachingObj = null;
         this.objEditing = null;
         this.editObject = null;
         this.drawingObj = null;
         this.objAgvLaser = null
         this.objAgvLaserLine = null
         if (this.pressObjTimer) {
            clearTimeout(this.pressObjTimer);
            this.pressObjTimer = null;
         }
      },
      // 清理所有引用
      cleanupReferences() {
         this.canvas = null;
         this.workSpace = null;
         this.agvObj = null;
         this.curTeachingObj = null;
         this.objEditing = null;
         this.editObject = null;
         this.drawingObj = null;
         this.mapInfo = {
            proportion: 1,
            img_proportion: 1,
            max_x: 1,
            max_y: 1,
            min_x: 0,
            min_y: 0,
            img_x: 1,
            img_y: 1
         };
         // 清除所有定时器
         if (this.pressObjTimer) {
            clearTimeout(this.pressObjTimer);
            this.pressObjTimer = null;
         }
      },
      setBackground(info) {
         const _this = this
@@ -1309,23 +1333,23 @@
         // this.canvas.selectionColor = 'rgba(100, 200, 255, 0.3)'; // 选中背景色
         // this.canvas.selectionBorderColor = '#1890ff'; // 边框颜色
         // this.canvas.selectionLineWidth = 3; // 边框宽度
         this.agvObj = null
         const cantainerEl = document.getElementById("canvasMap")
         this.eleWidth = cantainerEl.clientWidth
         this.eleHeight = cantainerEl.clientHeight
         console.log("client", this.eleWidth, this.eleHeight)
         this.canvas.setWidth(this.eleWidth);
         this.canvas.setHeight(this.eleHeight);
         this.mapInfo = {
            proportion: info.proportion || 1,
            img_proportion: info.img_proportion || 1,
            max_x: info.max_x || 1,
            max_y: info.max_y || 1,
            min_x: info.min_x || 0,
            min_y: info.min_y || 0,
            img_x: info.img_x || 1,
            img_y: info.img_y || 1,
            proportion: parseInt(info.proportion) || 1,
            img_proportion: parseInt(info.img_proportion) || 1,
            max_x: parseInt(info.max_x) || 1,
            max_y: parseInt(info.max_y) || 1,
            min_x: parseInt(info.min_x) || 0,
            min_y: parseInt(info.min_y) || 0,
            img_x: parseInt(info.img_x) || 1,
            img_y: parseInt(info.img_y) || 1,
         }
         return new Promise((resolve, reject) => {
            if (info.filedata) {
@@ -1346,25 +1370,8 @@
                        left: 0,
                        top: 0,
                     });
                     // if (_this.workSpace instanceof fabric.Group) {
                     //    const objs = _this.workSpace.getObjects()
                     //    const rect = objs[1]
                     //    _this.workSpace.remove(objs[0])
                     //    _this.workSpace.insertAt(0,img)
                     //    rect.set({
                     //       width: _this.mapInfo.img_x,
                     //       height: _this.mapInfo.img_y,
                     //    })
                     //    _this.workSpace.set({
                     //       width: _this.mapInfo.img_x,
                     //       height: _this.mapInfo.img_y,
                     //    })
                     //    resolve()
                     //    return
                     // }
                     _this.clearObjects()
                     //
                     const rect = new fabric.Rect({
                        left: 0,
                        top: 0,
@@ -1391,21 +1398,52 @@
                        height: _this.mapInfo.img_y,
                     });
                     _this.canvas.add(wsGroup)
                     if (_this.workSpace) {
                        _this.canvas.remove(_this.workSpace)
                     }
                     _this.workSpace = wsGroup
                     _this.clearObjects()
                     _this.canvas.add(wsGroup)
                     // _this.canvas.renderAll()
                     _this.workSpace = wsGroup
                     const scale = _this.eleWidth / (4 * 84)
                     _this.setZoomAuto(scale); //
                  }
                  //_this.checkMemoryUsage()
                  resolve()
               }).catch((err) => {
                  _this.$ownerInstance.callMethod('receiveRenderData', {
                     method: "set_backgroud_progress",
                     type: "error",
                  // _this.$ownerInstance.callMethod('receiveRenderData', {
                  //    method: "set_backgroud_progress",
                  //    type: "error",
                  //    msg:err
                  // });
                  const rect = new fabric.Rect({
                     left: 0,
                     top: 0,
                     width: _this.mapInfo.img_x,
                     height: _this.mapInfo.img_y,
                     stroke: "#333",
                     strokeWidth: 1,
                     strokeDashArray: [5, 5],
                     strokeLineCap: 'butt',
                     fill: "rgba(255,255,255,0)",
                  })
                  let wsGroup = new fabric.Group([rect], {
                     id: "workspace",
                     eleType: "workspace",
                     selectable: false,
                     hasControls: false,
                     left: 0,
                     top: 0,
                     width: _this.mapInfo.img_x,
                     height: _this.mapInfo.img_y,
                  });
                  _this.clearObjects()
                  _this.canvas.add(wsGroup)
                  _this.workSpace = wsGroup
                  const scale = _this.eleWidth / (4 * 84)
                  _this.setZoomAuto(scale); //
                  resolve()
               })
@@ -1426,13 +1464,15 @@
         const eleWidth = this.eleWidth - 20
         const eleHeight = this.eleHeight - 200
         if (!this.workSpace)
            return 1
            return 0.8
         const width = this.workSpace.width
         const height = this.workSpace.height
         if (eleWidth / eleHeight < width / height) {
            return eleWidth / width;
            let scale = eleWidth / width;
            return scale - scale / 10
         } // 按照宽度缩放
         return eleHeight / height;
         let scale = eleHeight / height;
         return scale - scale / 10
      },
      auto() {
@@ -1455,12 +1495,7 @@
            scale: scale
         });
         if (!this.workSpace) return;
         // this.setCenterFromObject(this.workSpace);
         // 超出画布不展示
         // _this.workSpace.clone().then((cloned) => {
         //    _this.canvas.clipPath = cloned;
         //    _this.canvas.requestRenderAll();
         // });
      },
      setDrawingType(type, svg) {
         if (svg) {
@@ -1504,7 +1539,6 @@
            y: touch.clientY
         };
         //   console.log('单点触摸开始');
         let activeObj = this.canvas.getActiveObject();
         if (!activeObj) {
            if (!this.drawType) {
@@ -1544,9 +1578,14 @@
                  clearTimeout(this.pressObjTimer);
                  this.pressObjTimer = null
               }
            }
            if (Math.abs(deltaX) > 20 || Math.abs(deltaY) > 20) {
               this.$ownerInstance.callMethod('receiveRenderData', {
                  method: "cancel_positioning_agv",
               });
            }
            // 移动视口
            //   console.log('relativePan', deltaX, deltaY);
            const vpt = this.canvas.viewportTransform;
            this.canvas.relativePan(new fabric.Point(deltaX, deltaY));
            if (this.objEditing) {
@@ -1569,7 +1608,7 @@
            this.lastPosX = touch.clientX;
            this.lastPosY = touch.clientY;
            this.canvas.renderAll()
            this.canvas.requestRenderAll()
         } else if (this.isDrawing) {
            const vpt = this.canvas.viewportTransform;
            //   console.log("viewportTransform", vpt[4], vpt[5])
@@ -1873,7 +1912,7 @@
         if (this.objEditing) {
            this.createOkCancelControl(this.objEditing)
         }
         this.canvas.renderAll()
         this.canvas.requestRenderAll()
      },
@@ -1886,7 +1925,7 @@
            const left = _this.getXOnImg(info.x) // * scale
            const top = _this.getYOnImg(info.y) //* scale
            const angle = info.angle * 180 / Math.PI
            const angle = -(info.angle * 180 / Math.PI)
            fabric.loadSVGFromURL(svg).then(
               ({
                  objects,
@@ -2018,7 +2057,8 @@
            this.canvas.remove(this.curTeachingObj)
            this.curTeachingObj = null
         }
         if (teachingData.lenght === 0)
            return
         posArr.forEach((item) => {
            const curIndex = pos_list.findIndex((item2) => item2.x === item.x && item2.y === item.y)
            if (curIndex < 0) {
@@ -2026,9 +2066,6 @@
            }
         })
         let path2 = ""
         const theta = 20;
         let headlen = 10;
         var main_road = teachingData.main_road || 0
         const len = pos_list.length
         let fromX = 0,
@@ -2042,45 +2079,18 @@
               y: this.getYOnImg(pt.y)
            }
            if (index > 0) {
               if ((index % 50 == 0 || index == len - 1 || index % 50 == 1)) {
                  if (index % 50 == 0 || index == len - 1) {
                     toX = pt2.x
                     toY = pt2.y
                     path2 += ` L${pt2.x} ${pt2.y}`
                     // 计算各角度和对应的P2,P3坐标
                     let angle = (Math.atan2(fromY - toY, fromX - toX) * 180) / Math.PI,
                        angle1 = ((angle + theta) * Math.PI) / 180,
                        angle2 = ((angle - theta) * Math.PI) / 180,
                        topX = headlen * Math.cos(angle1),
                        topY = headlen * Math.sin(angle1),
                        botX = headlen * Math.cos(angle2),
                        botY = headlen * Math.sin(angle2);
                     let arrowX = fromX - topX,
                        arrowY = fromY - topY;
                     arrowX = toX + topX;
                     arrowY = toY + topY;
                     path2 += " L " + arrowX + " " + arrowY;
                     arrowX = toX + botX;
                     arrowY = toY + botY;
                     path2 += " M " + arrowX + " " + arrowY;
                     path2 += " L " + toX + " " + toY;
                  }
               } else {
                  path2 += ` L${pt2.x} ${pt2.y}`
               }
               path2 += ` L${pt2.x} ${pt2.y}`
            } else {
               path2 = `M${pt2.x} ${pt2.y}`
            }
            fromX = pt2.x
            fromY = pt2.y
         }
         let strokeWidth = 1
         let stroke = "#95DE64"
         let strokeWidth = 5
         let stroke = "#69C0FF"
         if (main_road == 1) {
            stroke = "#69C0FF"
         }
         const objPath = new fabric.Path(
            path2, {
               id: "current_teaching",
@@ -2112,13 +2122,11 @@
               pos_list.push(item)
            }
         })
         console.log(posArr.length, pos_list.length)
         let path2 = ""
         const theta = 20;
         let headlen = 10;
         var main_road = 1
         var main_road = teachingData.main_road
         const len = pos_list.length
         let fromX = 0,
@@ -2134,77 +2142,12 @@
               y: this.getYOnImg(pt.y)
            }
            if (index > 0) {
               if ((type == "public_teaching") && (index % 50 == 0 || index == len - 1 || index % 50 == 1)) {
                  if (index % 50 == 1 || index == len - 1) {
                     if (teachingData.bidirection == 1 && index < len - 1) { //- 1
                        toY = fromY
                        toX = fromX
                        fromX = pt2.x
                        fromY = pt2.y
                        let angle = (Math.atan2(fromY - toY, fromX - toX) * 180) / Math.PI,
                           angle1 = ((angle + theta) * Math.PI) / 180,
                           angle2 = ((angle - theta) * Math.PI) / 180,
                           topX = headlen * Math.cos(angle1),
                           topY = headlen * Math.sin(angle1),
                           botX = headlen * Math.cos(angle2),
                           botY = headlen * Math.sin(angle2);
                        let arrowX = fromX - topX,
                           arrowY = fromY - topY;
                        arrowX = toX + topX;
                        arrowY = toY + topY;
                        let path3 = " L " + arrowX + " " + arrowY;
                        arrowX = toX + botX;
                        arrowY = toY + botY;
                        path3 += " M " + arrowX + " " + arrowY;
                        path3 += " L " + toX + " " + toY;
                        path2 += path3
                        fromY = toY
                        fromX = toX
                     }
                  }
                  if (index % 50 == 0 || index == len - 1) {
                     toX = pt2.x
                     toY = pt2.y
                     // if (fromX == toX && fromY == toY) {
                     //    if (index - 2 >= 0) {
                     //       const pt3 = pos_list[index - 2]
                     //       fromX = this.getXOnImg(pt3.x),
                     //          fromY = this.getYOnImg(pt3.y)
                     //    } else {
                     //       continue;
                     //    }
                     // }
                     path2 += ` L${pt2.x} ${pt2.y}`
                     // 计算各角度和对应的P2,P3坐标
                     let angle = (Math.atan2(fromY - toY, fromX - toX) * 180) / Math.PI,
                        angle1 = ((angle + theta) * Math.PI) / 180,
                        angle2 = ((angle - theta) * Math.PI) / 180,
                        topX = headlen * Math.cos(angle1),
                        topY = headlen * Math.sin(angle1),
                        botX = headlen * Math.cos(angle2),
                        botY = headlen * Math.sin(angle2);
                     let arrowX = fromX - topX,
                        arrowY = fromY - topY;
                     arrowX = toX + topX;
                     arrowY = toY + topY;
                     path2 += " L " + arrowX + " " + arrowY;
                     arrowX = toX + botX;
                     arrowY = toY + botY;
                     path2 += " M " + arrowX + " " + arrowY;
                     path2 += " L " + toX + " " + toY;
                  }
                  //   console.log(`箭头 L${pt2.x} ${pt2.y}`)
               } else {
                  //   console.log(`点 L${pt2.x} ${pt2.y} ${index} == ${len-1}`)
                  path2 += ` L${pt2.x} ${pt2.y}`
               }
               path2 += ` L${pt2.x} ${pt2.y}`
            } else {
               main_road = pt.main_road
               if (main_road == 1) {
                  headlen = 15
               }
               if (main_road === undefined)
                  main_road = pt.main_road
               path2 = `M${pt2.x} ${pt2.y}`
            }
            fromX = pt2.x
@@ -2212,17 +2155,28 @@
         }
         //console.log(path2)
         // console.log("addTeachingPath",path2)
         // path2 += " Z"
         let strokeWidth = 1
         let stroke = "#95DE64"
         let strokeWidth = 1.5
         let stroke = "#69C0FF"
         if (type == "station_teaching") {
            stroke = "#69C0FF"
         } else {
            if (main_road == 1) {
               stroke = "#69C0FF"
               //stroke = "#69C0FF"
               if (teachingData.bidirection == 1) {
                  stroke = "#ffaa00"
               }
               strokeWidth = 3
            } else {
               if (teachingData.bidirection == 1) {
                  stroke = "#FF00FF"
               }
            }
         }
         teachingData.main_road = main_road
         let list = this.canvas.getObjects() || []
         list = list.filter((a) => a.eleType == "station_teaching" || a.eleType == "public_teaching")
         let lenTeaching = list.length
@@ -2247,27 +2201,17 @@
               lockScalingY: true,
               lockMovementX: true,
               lockMovementY: true,
               selectable: false,
               opacity: 0,
               mainRoad: main_road,
               data: teachingData
            })
         this.canvas.add(objPath)
         // this.canvas.sendObjectToBack(objPath);
         // lenTeaching = 0
         // for (let i = list.length - 1; i >= 0; i--) {
         //    const obj = list[i]
         //    if (this.compareOverlap(obj, objPath)) {
         //       lenTeaching = i + 1
         //       break
         //    }
         // }
         this.canvas.sendObjectToBack(objPath);
         this.canvas.moveObjectTo(objPath, lenTeaching + 1);
         return objPath
      },
      isObjectFullyContained(outerObj, innerObj) {
         const outer = outerObj.getBoundingRect(true, true);
         const inner = innerObj.getBoundingRect(true, true);
@@ -2298,15 +2242,16 @@
      },
      showTeachingPath(show) {
         if (!show)
            this.canvas.discardActiveObject();
         let list = this.canvas.getObjects() || []
         list = list.filter((a) => a.eleType == "station_teaching" || a.eleType == "public_teaching")
         for (let i2 in list) {
            const obj = list[i2]
            obj.set({
               opacity: show ? 1 : 0,
               strokeDashArray: [],
               strokeLineCap: '',
               // strokeDashArray: [],
               // strokeLineCap: '',
               hasControls: show,
               selectable: show,
            })
@@ -2321,7 +2266,7 @@
         let id = ""
         if (teachingMode.mode == "Public") {
            eleType = "public_teaching"
            id = `public_teaching_${teachingMode.name}`
            id = `public_teaching_${teachingMode.name}_${teachingMode.edge_name}`
         } else if (teachingMode.mode == "Stations") {
            eleType = "station_teaching"
            id = `station_teaching_${teachingMode.src_dst}`
@@ -2366,10 +2311,6 @@
               left = pt.x - width / 2
               top = pt.y - height / 2
            }
            console.log(left,
               top,
               width,
               height, scale2)
            const rect = new fabric.Rect({
               id: `edit_teaching`,
               eleType: "edit_teaching",
@@ -2434,30 +2375,6 @@
               oldLeft: rect.left,
               oldTop: rect.top,
            })
            /*const zoom = this.canvas.getZoom();
            const eleHeight = this.eleHeight - 150
            const info = {
               x: rect.left + rect.width / 2,
               y: rect.top + rect.height / 2
            }
            let deltaX = info.x * zoom - this.eleWidth / 2 // * scale;
            let deltaY = info.y * zoom - eleHeight / 2 //* scale;
            const vpt = this.canvas.viewportTransform;
            const oldX = vpt[4]
            const oldY = vpt[5]
            if (deltaX + this.eleWidth > this.workSpace.width)
               deltaX = this.workSpace.width - this.eleWidth
            if (deltaY + eleHeight > this.workSpace.height)
               deltaY = this.workSpace.height - eleHeight
            if (oldX + this.eleWidth >= info.x * zoom && info.x * zoom >= oldX) {
               deltaX = -oldX
               //console.log("move_canvas X", oldX)
            }
            if (oldY + eleHeight >= info.y * zoom && info.y * zoom >= oldY) {
               //   console.log("move_canvas Y", oldY)
               deltaY = -oldY
            }
            this.canvas.absolutePan(new fabric.Point(deltaX, deltaY));*/
         } else {
            for (let i2 in list) {
@@ -2494,23 +2411,23 @@
         this.createOkCancelControl(obj)
         this.canvas.requestRenderAll();
      },
      updateAgv(info) {
      updateAgv(info, obj) {
         const _this = this
         return new Promise((resolve) => {
            //   const scale = this.getAutoScale()
            const left = _this.getXOnImg(info.x) // * scale
            const top = _this.getYOnImg(info.y) //* scale
            const angle = info.angle * 180 / Math.PI
            if (this.agvObj) {
               this.agvObj.set({
            const angle = -(info.angle * 180 / Math.PI) + 90
            if (obj) {
               obj.set({
                  left,
                  top,
                  angle,
                  data: info
               });
               this.agvObj.setCoords()
               _this.canvas.requestRenderAll();
               resolve()
               obj.setCoords()
               this.canvas.requestRenderAll();
               resolve(obj)
            } else {
               const zoom = _this.canvas.getZoom();
               fabric.loadSVGFromURL("static/images/van.svg").then(
@@ -2551,16 +2468,13 @@
                     console.log("agv", JSON.stringify(info))
                     _this.canvas.add(obj)
                     //   _this.canvas.bringObjectToFront(obj);
                     _this.agvObj = obj
                     resolve()
                     resolve(obj)
                  }
               )
            }
         })
      },
      addVirtualWallShow(info) {
         const path = info.path || []
         if (path.length != 2)
@@ -2588,7 +2502,6 @@
         });
         return line
      },
      addVirtualWall(info) {
         const path = info.path || []
         if (path.length != 2)
@@ -2767,18 +2680,7 @@
               y: this.getYOnImg(pt.y)
            }
            // if (pt.x < 10) {
            //    pt.x = 10
            // }
            // if (pt.y < 10) {
            //    pt.y = 10
            // }
            // if (pt.x > this.workSpace.width - 10) {
            //    pt.x = 10
            // }
            // if (pt.y > this.workSpace.height - 10) {
            //    pt.y = 10
            // }
            if (index > 0) {
               path2 += ` L${pt2} ${pt2.y}`
            } else {
@@ -2816,18 +2718,6 @@
         const path = info.path || []
         let path2 = ""
         path.forEach((pt, index) => {
            // if (pt.x < 10) {
            //    pt.x = 10
            // }
            // if (pt.y < 10) {
            //    pt.y = 10
            // }
            // if (pt.x > this.workSpace.width - 10) {
            //    pt.x = this.workSpace.width - 10
            // }
            // if (pt.y > this.workSpace.height - 10) {
            //    pt.y = this.workSpace.height - 10
            // }
            let pt2 = {
               x: this.getXOnImg(pt.x),
               y: this.getYOnImg(pt.y)
@@ -3094,65 +2984,130 @@
         this.canvas.remove(obj)
      },
      updateAgvLaser(param) {
         const angle = param.angle - Math.PI / 2 // * 180 / Math.PI
         const angle = -param.angle //- Math.PI / 2 // * 180 / Math.PI
         const pt = {
            x: this.getXOnImg(param.x),
            y: this.getYOnImg(param.y)
         }
         if (this.objAgvLaser) {
            this.objAgvLaser.set({stroke: "#00ff00",   rx: 2,
            ry:2})
         }
         let ellipse = new fabric.Ellipse({
            id: "agv_laser",
            eleType: "agv_laser",
            left: pt.x,
            top: pt.y,
            rx: 2,
            ry: 2,
            stroke: "#00aa00",
            rx: 3,
            ry: 3,
            stroke: "#ff0000",
            strokeWidth: 1,
            fill: "#00aa00",
            fill: "#00ff00",
            originX: "center",
            originY: "center",
            selectable: false,
            hasControls: true,
            lockRotation: true,
            lockScalingX: true,
            lockScalingY: true,
            lockMovementX: true,
            lockMovementY: true,
            hasControls: false,
         });
         this.canvas.add(ellipse)
         const offX = 20 * Math.cos(angle)
         const offY = 20 * Math.sin(angle)
         console.log("angle", param.angle, offX, offY)
         if (this.objAgvLaser) {
            this.canvas.remove(this.objAgvLaser)
         this.objAgvLaser = ellipse
         if (this.objAgvLaserLine) {
            this.canvas.remove(this.objAgvLaserLine)
         }
         const line = new fabric.Line([pt.x, pt.y, pt.x + offX,
            pt.y + offY
         ], {
            id: "agv_laser_angle",
            eleType: "agv_laser_angle",
            stroke: "#00aa00",
            strokeWidth: 2,
            lockRotation: true,
            lockScalingX: true,
            lockScalingY: true,
            lockMovementX: true,
            lockMovementY: true,
            stroke: "#00ff00",
            strokeWidth: 1,
            selectable: false,
            hasControls: false,
         });
         this.objAgvLaser = line
         this.objAgvLaserLine = line
         this.canvas.add(line)
      },
      addTrajectoryPoint(list) {
         const objs = []
         let right = 0
         let bottom = 0
         let left = this.mapInfo.img_x
         let top = this.mapInfo.img_y
         for (let i in list) {
            const pt = list[i]
            const pt2 = {
               x: this.getXOnImg(pt[0]),
               y: this.getYOnImg(pt[1])
            }
            let ellipse = new fabric.Ellipse({
               left: pt2.x,
               top: pt2.y,
               rx: 2,
               ry: 2,
               stroke: "#00ff00",
               strokeWidth: 1,
               fill: "#00ff00",
               originX: "center",
               originY: "center",
            });
            objs.push(ellipse)
            if (left > pt2.x-2) {
               left = pt2.x-2
            }
            if (top > pt2.y-2) {
               top = pt2.y-2
            }
            if (right < pt2.x + 2) {
               right = pt2.x + 2
            }
            if (bottom < pt2.y + 2) {
               bottom = pt2.y + 2
            }
         }
         const groupObj = new fabric.Group(objs, {
            id: `trajectory_point_group`,
            eleType: "trajectory_point_group",
            left,
            top,
            width: right - left,
            height: bottom - top,
            selectable: false,
            hasControls: true,
         })
         this.canvas.add(groupObj)
      },
      updateLaserPoint(param) {
         let list2 = this.canvas.getObjects() || []
         list2 = list2.filter((a) => a.eleType == "laser_point")
         list2 = list2.filter((a) => a.eleType == "laser_point_cur_group")
         for (let i in list2) {
            const obj = list2[i]
            obj.set({
               fill: "#000",
            const objGroup = list2[i]
            objGroup.set({
               eleType: "laser_point_group",
            })
            const list3 = objGroup.getObjects()
            for (let i3 in list3) {
               const obj = list3[i3]
               obj.set({
                  fill: "#0000FF",
               })
            }
         }
         const list = param.xy || []
         const list = param?.xy || []
         const objs = []
         let right = 0
         let bottom = 0
         let left = this.mapInfo.img_x
         let top = this.mapInfo.img_y
         for (let i in list) {
            const pt = list[i]
            const pt2 = {
@@ -3160,45 +3115,52 @@
               y: this.getYOnImg(pt[1])
            }
            const point = new fabric.Rect({
               id: "laser_point",
               eleType: "laser_point",
               // id: "laser_point",
               // eleType: "laser_point",
               left: pt2.x,
               top: pt2.y,
               width: 1,
               height: 1,
               fill: "#F5222D",
               originX: "center",
               originY: "center",
               selectable: false,
               hasControls: true,
               lockRotation: true,
               lockScalingX: true,
               lockScalingY: true,
               lockMovementX: true,
               lockMovementY: true,
               fill: "#ff00ff",
               originX: "left",
               originY: "top",
               // selectable: false,
               // hasControls: true,
               // lockRotation: true,
               // lockScalingX: true,
               // lockScalingY: true,
               // lockMovementX: true,
               // lockMovementY: true,
            });
            // let point = new fabric.Ellipse({
            //   id: "laser_point",
            //    eleType: "laser_point",
            //    left: pt[0],
            //    top: pt[1],
            //    rx: 2,
            //    ry: 1,
            //    strokeWidth: 1,
            //    stroke: "#F5222D",
            //    fill: "#F5222D",
            //    originX: "center",
            //    originY: "center",
            //    selectable: false,
            //    hasControls: true,
            //    lockRotation: true,
            //    lockScalingX: true,
            //    lockScalingY: true,
            //    lockMovementX: true,
            //    lockMovementY: true,
            // });
            this.canvas.add(point)
            objs.push(point)
            if (left > pt2.x) {
               left = pt2.x
            }
            if (top > pt2.y) {
               top = pt2.y
            }
            if (right < pt2.x + 1) {
               right = pt2.x + 1
            }
            if (bottom < pt2.y + 1) {
               bottom = pt2.y + 1
            }
         }
         //console.log("updateLaserPoint", list.length, left, right,top,bottom)
         const groupObj = new fabric.Group(objs, {
            id: `laser_point_group`,
            eleType: "laser_point_cur_group",
            left,
            top,
            width: right - left,
            height: bottom - top,
            // originX: "left",
            // originY: "top",
            selectable: false,
            hasControls: true,
         })
         this.canvas.add(groupObj)
      },
@@ -3206,77 +3168,98 @@
         var zoom = this.canvas.getZoom();
         var vpt = this.canvas.viewportTransform; // 当前变换矩阵
         var newPanX = vpt[4];
         var newPanY = vpt[5];
         if (pt.x * zoom < vpt[4] + 80 || pt.x * zoom > vpt[4] + this.eleWidth - 80) {
            if (pt.x * zoom - this.eleWidth / 2 < 80) {
               newPanX = -80
            } else if (pt.x * zoom > this.mapInfo.img_x * zoom - 80) {
               newPanX = this.mapInfo.img_x * zoom - this.eleWidth + 80
         var newPanX = -vpt[4];
         var newPanY = -vpt[5];
         const offWidth = pt.width || 20
         const offHeight = pt.height || 20
         //   console.log("ensurePointVisible",pt.x,pt.y,newPanX,newPanY, this.eleWidth,this.eleHeight)
         if (pt.x * zoom < -vpt[4] + offWidth || pt.x * zoom > -vpt[4] + this.eleWidth - offWidth) {
            if (pt.x * zoom - this.eleWidth / 2 < offWidth) {
               newPanX = -offWidth
            } else {
               newPanX = pt.x * zoom - this.eleWidth / 2
            }
         }
         if (pt.y * zoom < vpt[5] + 80 || pt.y * zoom > vpt[5] + this.eleHeight - 200) {
         if (pt.y * zoom < -vpt[5] + offHeight || pt.y * zoom > -vpt[5] + this.eleHeight - (120 +
               offHeight)) {
            if (pt.y * zoom - this.eleHeight / 2 < 80) {
               newPanY = -80
            } else if (pt.y * zoom > this.mapInfo.img_y * zoom - 200) {
               newPanY = this.mapInfo.img_y * zoom - this.eleHeight + 200
            if (pt.y * zoom - this.eleHeight / 2 < offHeight) {
               newPanY = -offHeight
            } else {
               newPanY = pt.y * zoom - this.eleHeight / 2
               newPanY = pt.y * zoom - (this.eleHeight - 120) / 2
            }
         }
         // 只有在需要时才平移
         if (newPanX !== vpt[4] || newPanY !== vpt[5]) {
         if (newPanX !== -vpt[4] || newPanY !== -vpt[5]) {
            this.canvas.absolutePan({
               x: newPanX,
               y: newPanY
            });
         }
      },
      ensurePointCenter(pt) {
         var zoom = this.canvas.getZoom();
         var newPanX = newPanX = pt.x * zoom - this.eleWidth / 2
         var newPanY = pt.y * zoom - (this.eleHeight - 150) / 2
         this.canvas.absolutePan({
            x: newPanX,
            y: newPanY
         });
      },
      setAllObjectSelectable(selectable) {
         let flag = false
         this.canvas.forEachObject(function(obj) {
            if (obj.canSelect) {
               if (!obj.flag) {
               if (obj.selectable != selectable) {
                  flag = true
                  obj.set({
                     selectable: selectable,
                     lockEdit: true
                  })
               }
               obj.set({
                  selectable: selectable,
                  lockEdit: true
               })
            }
         });
         if (flag) {
         if (flag)
            this.canvas.requestRenderAll()
         }
      },
      receiveMsg(newValue, oldValue) {
         if (typeof newValue == "undefined")
            return;
         const _this = this
         //console.log("receiveMsg",_this.initFlag)
         setTimeout(() => {
            if (_this.initFlag) {
               _this.handleMsg(newValue, oldValue)
            } else {
         if (!this.canvas) {
            return
         }
         if (_this.initFlag) {
            _this.handleMsg(newValue, oldValue)
         } else {
            setTimeout(() => {
               _this.receiveMsg(newValue, oldValue)
            }
         }, 100)
            }, 100)
         }
      },
      async handleMsg(newValue, oldValue) {
         const _this = this
         if (!this.canvas) {
            return
         }
         try {
            //console.log("handleMsg", newValue)
            var data = JSON.parse(newValue);
            // console.log("handleMsg", data.length)
            const destroyCommand = data.find(item => item.method === "destroy");
            if (destroyCommand) {
               this.destroyCanvas();
               return;
            }
            for (var i = 0; i < data.length; i++) {
               const item = data[i]
               if (item.method == "init") {
                  if (item.param?.editMode) {
@@ -3289,21 +3272,40 @@
                  await _this.setBackground(item.param)
               } else if (item.method == "update_agv_state") {
                  const info = item.param || {}
                  await _this.updateAgv(info)
                  const obj = _this.agvObj
                  const obj2 = await _this.updateAgv(info, obj)
                  let obj3 = _this.agvObj
                  _this.agvObj = obj2
                  if (obj && obj2 != obj) {
                     _this.canvas.remove(obj)
                     if (obj3 == obj)
                        obj3 = undefined
                  }
                  if (obj3 && obj2 != obj3) {
                     _this.canvas.remove(obj3)
                  }
               } else if (item.method == "update_current_teaching") {
                  const info = item.param || []
                  await _this.updateCurrentTeaching(info)
               } else if (item.method == "move_canvas") {
               } else if (item.method == "move_pt_visible") {
                  const info2 = item.param || {}
                  const pt = {
                     x: this.getXOnImg(info2.x),
                     y: this.getYOnImg(info2.y)
                     y: this.getYOnImg(info2.y),
                     width: 20,
                     height: 20
                  }
                  this.ensurePointVisible(pt)
               } else if (item.method == "move_pt_center") {
                  const info2 = item.param || {}
                  const pt = {
                     x: this.getXOnImg(info2.x),
                     y: this.getYOnImg(info2.y)
                  }
                  this.ensurePointCenter(pt)
               } else if (item.method == "add_station") {
                  const stationList = item.param || []
                  let list = _this.canvas.getObjects() || []
@@ -3328,7 +3330,7 @@
                     } else {
                        //   _this.canvas.remove(list[curIndex])
                        const curStationObj = list[curIndex]
                        const angle = station.angle * 180 / Math.PI
                        const angle = -station.angle * 180 / Math.PI
                        //const scale = this.getAutoScale()
                        const left = this.getXOnImg(station.x) //* scale
                        const top = this.getYOnImg(station.y) //* scale
@@ -3359,11 +3361,11 @@
                        }
                     }
                  }
               } else if (item.method == "remove_station") {
                  const stationList = item.param || []
                  this.canvas.discardActiveObject()
                  let list = _this.canvas.getObjects() || []
                  list = list.filter((a) => a.eleType == "station")
@@ -3378,9 +3380,7 @@
                        _this.canvas.remove(list[curIndex])
                        if (tipObj) {
                           _this.canvas.remove(tipObj)
                        }
                     }
                  }
@@ -3509,13 +3509,13 @@
                     }
                  })
               } else if (item.method == "public_teaching") {
                  _this.setAllObjectSelectable(false)
                  let list = _this.canvas.getObjects() || []
                  list.forEach((obj) => {
                     if (obj.eleType == "public_teaching" || obj.eleType ==
                        "station_teaching") {
                        obj.set({
                           hasControls: false,
                           selectable: false,
                           opacity: 1
                        })
                     } else if (obj.eleType == "agv")
                        obj.set({
@@ -3535,51 +3535,58 @@
                  for (let i2 in list) {
                     const obj = list[i2]
                     obj.set({
                        selectable: false,
                        selectable: obj?.canSelect ? true : false,
                        opacity: 1
                     })
                     // if (obj.eleType == "station") {
                     //    await _this.setMarkStation(obj, false)
                     // }
                  }
                  _this.showTeachingPath(_this.showTeachPathFlag ? true : false)
               } else if (item.method == "public_teaching_path") {
               } else if (item.method == "clear_teaching_path") {
                  this.canvas.discardActiveObject()
                  let list = _this.canvas.getObjects() || []
                  list = list.filter((a) => a.eleType == "public_teaching")
                  list = list.filter((a) => a.eleType == "public_teaching" || a.eleType ==
                     "station_teaching")
                  for (let i2 in list) {
                     this.canvas.remove(list[i2])
                  }
                  const teachingPathList = item.param || []
               } else if (item.method == "public_teaching_path") {
                  const teachingPathList = item.param?.list || []
                  const show = item.param?.show || false
                  for (let i2 in teachingPathList) {
                     const teachingPath = teachingPathList[i2]
                     const id = `public_teaching_${teachingPath.name}`
                     await this.addTeachingPath(teachingPath, id, "public_teaching")
                     const id = `public_teaching_${teachingPath.name}_${teachingPath.edge_name}`
                     const obj = await this.addTeachingPath(teachingPath, id, "public_teaching",
                        show)
                     obj.set({
                        selectable: show ? true : false,
                        opacity: show ? 1 : 0,
                     })
                  }
               } else if (item.method == "station_teaching_path") {
                  let list = _this.canvas.getObjects() || []
                  list = list.filter((a) => a.eleType == "station_teaching")
                  for (let i2 in list) {
                     this.canvas.remove(list[i2])
                  }
                  const teachingPathList = item.param || []
                  const show = item.param?.show || false
                  const teachingPathList = item.param?.list || []
                  for (let i2 in teachingPathList) {
                     const teachingPath = teachingPathList[i2]
                     const id = `station_teaching_${teachingPath.src_dst}`
                     await this.addTeachingPath(teachingPath, id, "station_teaching")
                     const obj = await this.addTeachingPath(teachingPath, id, "station_teaching",
                        show)
                     obj.set({
                        selectable: show ? true : false,
                        opacity: show ? 1 : 0,
                     })
                  }
               } else if (item.method == "show_teaching_path") {
                  _this.showTeachPathFlag = item.param.show
                  _this.showTeachingPath(item.param.show)
               } else if (item.method == "remove_teaching_path") {
                  this.canvas.discardActiveObject()
                  let list = _this.canvas.getObjects() || []
                  if (item.param.mode == "Public") {
                     list = list.filter((a) => a.eleType == "public_teaching")
                     const id = `public_teaching_${item.param.name}`
                     const id = `public_teaching_${item.param.name}_${item.param.edge_name}`
                     const curIndex = list.findIndex((a) => a.id == id)
                     if (curIndex > -1) {
@@ -3596,13 +3603,43 @@
                        const obj = list[curIndex]
                        this.canvas.remove(obj)
                     }
                  }
               } else if (item.method == "edit_teaching") {
                  const teachingMode = item.param
                  _this.showEditTeachingPath(teachingMode)
               } else if (item.method == "update_teaching") {
                  let list = _this.canvas.getObjects() || []
                  list = list.filter((a) => a.eleType == "public_teaching")
                  const id = `public_teaching_${item.param.name}_${item.param.edge_name}`
                  const curIndex = list.findIndex((a) => a.id == id)
                  if (curIndex > -1) {
                     const obj = list[curIndex]
                     let strokeWidth = 1.5
                     let stroke = "#69C0FF"
                     if (item.param.main_road == 1) {
                        //stroke = "#69C0FF"
                        if (item.param.bidirection == 1) {
                           stroke = "#ffaa00"
                        }
                        strokeWidth = 3
                     } else {
                        if (item.param.bidirection == 1) {
                           stroke = "#FF00FF"
                        }
                     }
                     obj.set({
                        strokeWidth,
                        stroke
                     })
                  }
               } else if (item.method == "set_selectable") {
                  if (item.param)
                     _this.setAllObjectSelectable(true)
@@ -3620,7 +3657,6 @@
                  for (let i2 in wallList) {
                     const wall = wallList[i2]
                     const obj = await _this.addVirtualWall(wall)
                  }
               } else if (item.method == "wall_list") {
                  const wallList = item.param || []
@@ -3638,8 +3674,6 @@
                  for (let i2 in regionList) {
                     const region = regionList[i2]
                     const obj = await _this.addRegion(region)
                  }
               } else if (item.method == "region_list") {
                  const regionList = item.param || []
@@ -3652,9 +3686,9 @@
                        await _this.addRegionShow(region)
                     }
                  }
               } else if (item.method == "remove_wall") {
                  const wallList = item.param || []
                  this.canvas.discardActiveObject()
                  let list = _this.canvas.getObjects() || []
                  list = list.filter((a) => a.eleType == "virtual_wall")
                  for (let i2 in wallList) {
@@ -3666,6 +3700,7 @@
                  }
               } else if (item.method == "remove_region") {
                  const regionList = item.param || []
                  this.canvas.discardActiveObject()
                  let list = _this.canvas.getObjects() || []
                  list = list.filter((a) => a.eleType == "region")
                  for (let i2 in regionList) {
@@ -3679,9 +3714,13 @@
                  this.updateAgvLaser(item.param || {})
               } else if (item.method == "point_cloud") {
                  this.updateLaserPoint(item.param || {})
               } else if (item.method == "point_trajectory") {
                  this.addTrajectoryPoint(item.param || [])
               }
            }
            _this.canvas.renderAll()
            if (_this.canvas)
               _this.canvas.renderAll()
         } catch (ex) {
            console.log(ex)
            this.showError(ex)
@@ -3722,6 +3761,5 @@
            verticalAlign: "center"
         }); // undefined, "错误"
      },
   },
   }
}