From d87c256a957a6a5c3b40eaf9c52ec68f2fc22c97 Mon Sep 17 00:00:00 2001
From: cuiqian2004 <cuiqian2004@163.com>
Date: 星期五, 12 九月 2025 16:23:42 +0800
Subject: [PATCH] test

---
 pages/map/js/ctx.js | 3065 +++++++++++++++++++++++++++++++++++++++++++++++++++-------
 1 files changed, 2,681 insertions(+), 384 deletions(-)

diff --git a/pages/map/js/ctx.js b/pages/map/js/ctx.js
index 14b32ad..d1b6b1b 100644
--- a/pages/map/js/ctx.js
+++ b/pages/map/js/ctx.js
@@ -5,8 +5,25 @@
 import {
 	Base64
 } from '../../../comm/base64.js';
+import {
+	hasSelfIntersection
+} from '../../../comm/line.js';
+import {
+	hexToRGBA,
+} from '../../../comm/utils.js';
+import {
+	throttle
+} from 'lodash-es';
+import {
+	debounce
+} from "lodash-es";
+import {
+	Result
+} from "ant-design-vue";
+
 // import okIcon from '../../../static/images/confirm.svg';
 // import cancelIcon from '../../../static/images/remove.svg';
+
 export default {
 	data() {
 		return {
@@ -30,7 +47,19 @@
 			agvObj: null,
 			initFlag: false,
 			editMode: false,
-
+			objEditing: null,
+			onOjectMoving: debounce(this.objectMoving, 200),
+			mapInfo: {
+				proportion: 1,
+				img_proportion: 1,
+				max_x: 1,
+				max_y: 1,
+				min_x: 0,
+				min_y: 0,
+				img_x: 1,
+				img_y: 1
+			},
+			pressObjTimer: 0
 
 		}
 	},
@@ -65,7 +94,7 @@
 					transparentCorners: false,
 					cornerStyle: 'circle',
 					borderScaleFactor: 2,
-					padding: 10,
+					padding: 5,
 
 				});
 				this.canvasId = `canvas_${uuidv4()}`
@@ -82,8 +111,11 @@
 					stopContextMenu: true, // 绂佹闀挎寜鑿滃崟
 					fireRightClick: true,
 					fireMiddleClick: true,
-					targetFindTolerance: 15, // 澧炲ぇ瑙︽懜瀹瑰樊
-					isTouchSupported: true
+					targetFindTolerance: 10, // 澧炲ぇ瑙︽懜瀹瑰樊
+					isTouchSupported: true,
+					enableRetinaScaling: true,
+					renderOnAddRemove: false,
+					imageSmoothingEnabled: true
 				})
 				this.canvas.clear()
 				this.eleWidth = cantainerEl.clientWidth
@@ -99,7 +131,7 @@
 					height: this.eleHeight,
 					selectable: false,
 					hasControls: false,
-					fill: "#FFFFFF"
+					fill: "#FFFFFF20"
 				})
 				this.canvas.add(this.workSpace)
 				this.patchFabricForUniApp(this.canvas)
@@ -135,61 +167,17 @@
 				return this;
 			};
 		},
-		createDeleteControl(obj) {
-			// 鍒涘缓鍒犻櫎鎸夐挳鐨勫浘鐗囧厓绱�-			const _this = this
-			const deleteIcon =
-				"data:image/svg+xml,%3C%3Fxml version='1.0' encoding='utf-8'%3F%3E%3C!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'%3E%3Csvg version='1.1' id='Ebene_1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' width='595.275px' height='595.275px' viewBox='200 215 230 470' xml:space='preserve'%3E%3Ccircle style='fill:%23F44336;' cx='299.76' cy='439.067' r='218.516'/%3E%3Cg%3E%3Crect x='267.162' y='307.978' transform='matrix(0.7071 -0.7071 0.7071 0.7071 -222.6202 340.6915)' style='fill:white;' width='65.545' height='262.18'/%3E%3Crect x='266.988' y='308.153' transform='matrix(0.7071 0.7071 -0.7071 0.7071 398.3889 -83.3116)' style='fill:white;' width='65.544' height='262.179'/%3E%3C/g%3E%3C/svg%3E";
-			const deleteImg = document.createElement('img');
-			deleteImg.src = deleteIcon;
-			// 娣诲姞鑷畾涔夊垹闄ゆ帶浠跺埌鎵�湁瀵硅薄鐨勫師鍨嬩笂
-			//	console.log("controls", JSON.stringify(fabric.Object.prototype))
-			obj.controls.deleteControl = new fabric.Control({
-				x: 0.5,
-				y: 0,
-				offsetY: 0,
-				offsetX: 36,
-				mouseDownHandler: _this.deleteObject, // 瑙︽懜浜嬩欢澶勭悊鍣�-				render: (ctx, left, top, styleOverride, fabricObject) => {
-
-					if (fabricObject.lockRemove) {
-						return;
-					}
-					if (fabricObject.canvas?.skipTargetFind) {
-						return;
-					}
-					const size = this.cornerSize || 36;
-					// drawImg(ctx, left, top, imgDelete, 24, 24, fabricObject.angle);
-					ctx.save();
-					ctx.translate(left, top);
-					ctx.drawImage(deleteImg, -size / 2, -size / 2, size, size);
-					ctx.restore();
-				},
-				cornerSize: 36,
-				withConnection: false,
-				actionName: 'delete', // 鍔ㄤ綔鍚嶇О
-				pointerStyle: 'pointer' // 鎸囬拡鏍峰紡
-			});
-		},
-		deleteObject(eventData, transform, x, y) {
-			console.log("")
-			const target = transform.target;
-			const canvas = target.canvas;
-
-			canvas.remove(target);
-			canvas.requestRenderAll();
-		},
 		editCancelObject(eventData, transform, x, y) {
 			const target = transform.target;
 			const canvas = target.canvas;
 			this.$ownerInstance.callMethod('receiveRenderData', {
 				method: "edit_finish",
 				cmd: "cancel",
+				type: target.eleType,
+				data: target.data,
 				station: target.data,
 			});
-			target.set({
-				lockEdit: true
-			})
+
 			this.canvas.requestRenderAll()
 		},
 		editOkObject(eventData, transform, x, y) {
@@ -198,83 +186,176 @@
 			this.$ownerInstance.callMethod('receiveRenderData', {
 				method: "edit_finish",
 				cmd: "ok",
+				type: target.eleType,
+				data: target.data,
 				station: target.data,
 			});
-			target.set({
-				lockEdit: true
-			})
 			this.canvas.requestRenderAll()
+		},
+
+		closeOkCancelControl() {
+			let list = this.canvas.getObjects() || []
+			list = list.filter((a) => a.eleType == "cmd")
+			list.forEach((obj) => {
+				this.canvas.remove(obj)
+			})
+			this.objEditing = null
+
 		},
 		createOkCancelControl(obj) {
 			// 鍒涘缓鍒犻櫎鎸夐挳鐨勫浘鐗囧厓绱�-			const _this = this
-			const cancelImg = document.createElement('img');
-			cancelImg.src = "static/images/remove.svg"
-			const okImg = document.createElement('img');
-			okImg.src = "static/images/confirm.svg"
-			// 娣诲姞鑷畾涔夊垹闄ゆ帶浠跺埌鎵�湁瀵硅薄鐨勫師鍨嬩笂
-			//	console.log("controls", JSON.stringify(fabric.Object.prototype))
-			obj.controls.cancelControl = new fabric.Control({
-				x: -0.5,
-				y: 0,
-				offsetY: 0,
-				offsetX: -32,
-				mouseDownHandler: _this.editCancelObject, // 瑙︽懜浜嬩欢澶勭悊鍣�-				render: (ctx, left, top, styleOverride, fabricObject) => {
+			var scale = this.canvas.getZoom()
+			if (scale < 1) {
+				scale = 1
+			}
+			var scale2 = 1
+			var offset = 50
 
-					if (fabricObject.lockEdit) {
-						return;
-					}
-					if (fabricObject.canvas?.skipTargetFind) {
-						return;
-					}
-					const size = this.cornerSize || 32;
-					// drawImg(ctx, left, top, imgDelete, 24, 24, fabricObject.angle);
-					ctx.save();
-					ctx.translate(left, top);
-					ctx.drawImage(cancelImg, -size / 2, -size / 2, size, size);
-					ctx.restore();
-				},
-				cornerSize: 32,
-				withConnection: false,
-				actionName: 'delete', // 鍔ㄤ綔鍚嶇О
-				pointerStyle: 'pointer' // 鎸囬拡鏍峰紡
-			});
-			obj.controls.okControl = new fabric.Control({
-				x: 0.5,
-				y: 0,
-				offsetY: 0,
-				offsetX: 32,
-				mouseDownHandler: _this.editOkObject, // 瑙︽懜浜嬩欢澶勭悊鍣�-				render: (ctx, left, top, styleOverride, fabricObject) => {
+			//if (obj.eleType == "station" || obj.eleType == "edit_teaching") {
+			scale2 = 1 / scale
 
-					if (fabricObject.lockEdit) {
-						return;
-					}
-					if (fabricObject.canvas?.skipTargetFind) {
-						return;
-					}
-					const size = this.cornerSize || 32;
-					// drawImg(ctx, left, top, imgDelete, 24, 24, fabricObject.angle);
-					ctx.save();
-					ctx.translate(left, top);
-					ctx.drawImage(okImg, -size / 2, -size / 2, size, size);
-					ctx.restore();
-				},
-				cornerSize: 32,
-				withConnection: false,
-				actionName: 'delete', // 鍔ㄤ綔鍚嶇О
-				pointerStyle: 'pointer' // 鎸囬拡鏍峰紡
+			//}
+			offset = 50 / scale
+			this.objEditing = obj
+			let list = this.canvas.getObjects() || []
+			list = list.filter((a) => a.eleType == "cmd")
+			if (list.length > 0) {
+				let left = obj.left - offset
+				let top = obj.top + obj.height / 2
+				if (obj.originX == "center") {
+					left = obj.left - obj.width / 2 - offset
+				}
+				if (obj.originX == "center") {
+					top = obj.top
+				}
+
+				let curIndex = list.findIndex((a) => a.id == "cancel")
+				if (curIndex > -1) {
+					list[curIndex].set({
+						left,
+						top,
+						mainObj: obj,
+						scaleX: scale2,
+						scaleY: scale2
+					})
+					list[curIndex].setCoords()
+					this.canvas.bringObjectToFront(list[curIndex]);
+				}
+				left = obj.left + obj.width + offset
+				if (obj.originX == "center") {
+					left = obj.left + obj.width / 2 + offset
+				}
+				curIndex = list.findIndex((a) => a.id == "ok")
+				if (curIndex > -1) {
+					list[curIndex].set({
+						left,
+						top,
+						mainObj: obj,
+						scaleX: scale2,
+						scaleY: scale2
+					})
+					list[curIndex].setCoords()
+					this.canvas.bringObjectToFront(list[curIndex]);
+				}
+				return
+			}
+			let objList = []
+			let ellipse = new fabric.Ellipse({
+				left: -15,
+				top: -15,
+				rx: 15,
+				ry: 15,
+				stroke: "#F5222D",
+				strokeWidth: 1,
+				fill: "#F5222D",
+
 			});
+			objList.push(ellipse)
+			let line = new fabric.Line([-6, -6, 6, 6], {
+				stroke: "white",
+				strokeWidth: 3,
+			});
+			objList.push(line)
+			line = new fabric.Line([6, -6, -6, 6], {
+				stroke: "white",
+				strokeWidth: 3,
+			});
+			objList.push(line)
+			let left = obj.left - offset
+			let top = obj.top + obj.height / 2
+			if (obj.originX == "center") {
+				left = obj.left - obj.width / 2 - offset
+			}
+			if (obj.originY == "center") {
+				top = obj.top
+			}
+			let objGroup = new fabric.Group(objList, {
+				id: `cancel`,
+				eleType: "cmd",
+				left,
+				top,
+				width: 30,
+				height: 30,
+				originX: "center",
+				originY: "center",
+				lockRotation: true,
+				lockScalingX: true,
+				lockScalingY: true,
+				lockMovementX: true,
+				lockMovementY: true,
+				mainObj: obj,
+				scaleX: scale2,
+				scaleY: scale2
+			});
+			this.canvas.add(objGroup)
+
+			objList = []
+			ellipse = new fabric.Ellipse({
+				left: -15,
+				top: -15,
+				rx: 15,
+				ry: 15,
+				stroke: "#52C41A",
+				strokeWidth: 1,
+				fill: "#52C41A",
+			});
+			objList.push(ellipse)
+			line = new fabric.Line([-8, -2, -2, 5], {
+				stroke: "white",
+				strokeWidth: 3,
+			});
+			objList.push(line)
+			line = new fabric.Line([-3, 6, 9, -5], {
+				stroke: "white",
+				strokeWidth: 3,
+			});
+			objList.push(line)
+			left = obj.left + obj.width + offset
+			if (obj.originX == "center") {
+				left = obj.left + obj.width / 2 + offset
+			}
+			objGroup = new fabric.Group(objList, {
+				id: `ok`,
+				eleType: "cmd",
+				left,
+				top,
+				width: 30,
+				height: 30,
+				originX: "center",
+				originY: "center",
+				lockRotation: true,
+				lockScalingX: true,
+				lockScalingY: true,
+				lockMovementX: true,
+				lockMovementY: true,
+				scaleX: scale2,
+				scaleY: scale2,
+				mainObj: obj
+			});
+			this.canvas.add(objGroup)
 
 		},
-		addObject(obj) {
-			obj.set({
-				lockEdit: true
-			})
-			this.canvas.add(obj)
-			this.createOkCancelControl(obj)
-		},
+
 		canvasEventListener() {
 			const _this = this
 
@@ -299,10 +380,11 @@
 			_this.canvas.on("selection:updated", function(e) {
 				console.log("selection:updated", e);
 				const activeObj = _this.canvas.getActiveObject();
+
 				activeObj.set({
 					borderColor: '#1890ff',
 					borderScaleFactor: 3,
-					padding: 5,
+					padding: 3,
 					borderDashArray: [5, 5], //5px 瀹炵嚎鍜�px 闂撮殧
 				});
 				_this.canvas.requestRenderAll();
@@ -311,7 +393,7 @@
 
 			});
 			_this.canvas.on("selection:cleared", function(e) {
-				console.log("selection:cleared", e);
+				//	console.log("selection:cleared", e);
 
 				//_this.selectionChangeCanvas();
 			});
@@ -321,88 +403,191 @@
 					e.target.isRemoved = true;
 				}
 			});
+			_this.canvas.on("transform", function(e) {
+				_this.onScaleChange()
+			});
+
 			_this.canvas.on("object:modified", function(e) {
 				// console.log("object:modified", e.target);
-				if (e?.target.eleType == "station") {
-					const obj = e.target
-					obj.data.x = obj.left
-					obj.data.y = obj.top
-					_this.$ownerInstance.callMethod('receiveRenderData', {
-						method: "update_station",
-						station: obj.data,
 
-					});
-				}
+
 				// _this.resizetCanvas();
 			});
 			_this.canvas.on("object:moving", function(e) {
-				// console.log("object:modified", e.target);
+				console.log("object:moving", e.target);
+				_this.onOjectMoving(e.target)
 
 			});
 
-			var pressObjTimer
+
 			cantainerEl.addEventListener('touchstart', function(e) {
-				console.log('touchstart:', e);
+				//		console.log('touchstart:', e.touches.length);
 				e.preventDefault(); // 闃绘榛樿琛屼负
 				_this.canvas.fire('touch:start', {
 					e: 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;
+						_this.lastPosX = touch.clientX;
+						_this.lastPosY = touch.clientY;
+
 						const list = _this.canvas.getActiveObjects()
 						if (list.length === 1) {
-							pressObjTimer = setTimeout(function() {
-								console.log("edit_station", list[0].eleType)
-								if (list[0].eleType == "station") {
-									_this.setAllObjectSelectable(false)
+							if (!_this.objEditing) {
+								this.pressObjTimer = setTimeout(function() {
 
-									list[0].set({
-										selectable: true
-									})
 									const zoom = _this.canvas.getZoom();
-									let deltaX = list[0].left * zoom
-									let deltaY = list[0].top * zoom
 									const vpt = _this.canvas.viewportTransform;
+									if (list[0].eleType == "station") {
+										_this.setAllObjectSelectable(false)
 
-									_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 * zoom,
-											height: list[0].height * zoom
-										}
-									});
+										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)
 								}
-							}, 3000); //
+								_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) {
 				//	_this.canvas._onMouseMove(e);
+				//	console.log('touchmove:', e.touches.length);
 				e.preventDefault(); // 闃绘榛樿琛屼负
 				// 澶勭悊绉诲姩
-				if (!_this.canvas.getActiveObject()) {
+				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) {
@@ -410,83 +595,526 @@
 					}
 				} else {
 					if (e.touches.length === 1) {
-						const touch = e.touches[0]
-						const deltaX = touch.clientX - this.lastPosX;
-						const deltaY = touch.clientY - this.lastPosY;
-						if (Math.abs(deltaX) > 20)
-							clearTimeout(pressObjTimer);
+						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);
 					}
 				}
 			});
 
 			cantainerEl.addEventListener('touchend', function(e) {
 				//	_this.canvas._onMouseUp(e);
+				//	console.log('touchend:');
 				e.preventDefault(); // 闃绘榛樿琛屼负
 				_this.touchPoint = {
 					x: 0,
 					y: 0
 				};
-				if (!_this.canvas.getActiveObject()) {
+				const activeObj = _this.canvas.getActiveObject()
+				if (!activeObj) {
 					// 澶勭悊缁撴潫浜嬩欢
 					if (e.touches.length === 0) {
 						_this.handleTouchEnd();
 					}
-					if (_this.editObject) {
-						_this.canvas.setActiveObject(_this.editObject)
-					}
+					// if (_this.editObject) {
+					// 	_this.canvas.setActiveObject(_this.editObject)
+					// }
 				} else {
-					clearTimeout(pressObjTimer);
+					// if (activeObj.lockMovementX) {
+					// 	_this.canvas.discardActiveObject();
+					// }
 				}
-
-
+				if (this.pressObjTimer) {
+					clearTimeout(this.pressObjTimer);
+					this.pressObjTimer = null
+				}
 			});
 			cantainerEl.addEventListener('touchcancel', function(e) {
-				if (_this.canvas.getActiveObject()) {
-					// 澶勭悊缁撴潫浜嬩欢
-					clearTimeout(pressObjTimer);
-				} else {
-					if (_this.editObject) {
-						_this.canvas.setActiveObject(_this.editObject)
-					}
+				//	console.log('touchcancel:');
+				if (this.pressObjTimer) {
+					clearTimeout(this.pressObjTimer);
+					this.pressObjTimer = null
 				}
+				// const activeObj = _this.canvas.getActiveObject()
+				// if (activeObj) {
+				// 	if (activeObj.lockMovementX) {
+				// 		_this.canvas.discardActiveObject();
+				// 	}
+				// }
 			})
 
 		},
-		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: ""
-					});
+		// 璁$畻鐐瑰埌绾挎鐨勮窛绂�+		pointToSegmentDistance(px, py, x1, y1, x2, y2) {
+			const A = px - x1;
+			const B = py - y1;
+			const C = x2 - x1;
+			const D = y2 - y1;
+
+			const dot = A * C + B * D;
+			const lenSq = C * C + D * D;
+			let param = -1;
+			if (lenSq !== 0) param = dot / lenSq;
+
+			let xx, yy;
+			if (param < 0) {
+				xx = x1;
+				yy = y1;
+			} else if (param > 1) {
+				xx = x2;
+				yy = y2;
+			} else {
+				xx = x1 + param * C;
+				yy = y1 + param * D;
+			}
+
+			const dx = px - xx;
+			const dy = py - yy;
+			return Math.sqrt(dx * dx + dy * dy);
+		},
+
+		// 鑾峰彇 Path 鐨勬墍鏈夌嚎娈�+		getPathSegments(path) {
+			const segs = [];
+			const pts = path.path;
+			let lastX = 0,
+				lastY = 0;
+
+			for (let i = 0; i < pts.length; i++) {
+				const cmd = pts[i];
+				if (cmd[0] === 'M') {
+					lastX = cmd[1];
+					lastY = cmd[2];
+				} else if (cmd[0] === 'L') {
+					const x = cmd[1];
+					const y = cmd[2];
+					segs.push([lastX, lastY, x, y]);
+					lastX = x;
+					lastY = y;
+				}
+			}
+			return segs;
+		},
+
+		// 鍒ゆ柇鐐规槸鍚﹀湪 Path 鎴�Line 鐨勮竟妗嗕笂
+		isPointOnStroke(obj, pointer, tolerance = 5) {
+			if (obj instanceof fabric.Line) {
+				const dist = this.pointToSegmentDistance(
+					pointer.x, pointer.y,
+					obj.x1, obj.y1, obj.x2, obj.y2
+				);
+				return dist <= tolerance;
+			}
+
+			if (obj instanceof fabric.Path) {
+				const segs = this.getPathSegments(obj);
+				for (const [x1, y1, x2, y2] of segs) {
+					const dist = this.pointToSegmentDistance(pointer.x, pointer.y, x1, y1, x2, y2);
+					if (dist <= tolerance) return true;
+				}
+			}
+
+			return false;
+		},
+
+		pointerSelectObject(e) {
+			const pointer = this.canvas.getPointer(e);
+			const objects = this.canvas.getObjects();
+
+			objects.splice(0, 1);
+			this.canvas.discardActiveObject()
+
+			let pointerList = []
+			let pointerList2 = []
+			for (let i = objects.length - 1; i >= 0; i--) {
+				const obj = objects[i];
+
+				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)
+							pointerList.unshift(obj)
+
+						}
+					} else {
+						const isHit = obj.containsPoint(pointer);
+						if (isHit) {
+							console.log(i, obj.eleType)
+							pointerList.unshift(obj)
+
+						}
+					}
 				}
 
-			} else {
-				_this.$ownerInstance.callMethod('receiveRenderData', {
-					method: "selected_change",
-					type: ""
-				});
 			}
+			for (let i = pointerList.length - 1; i >= 0; i--) {
+				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();
+						return
+					}
+					pointerList2.unshift(obj)
+				} else {
+					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();
+					return
+				}
+				pointerList = pointerList2
+				pointerList2 = []
+				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();
+						return
+					}
+					pointerList2.unshift(obj)
+				}
+			}
+			if (pointerList2.length > 0) {
+				if (pointerList2.length == 1) {
+					const obj = pointerList2[i];
+					this.canvas.discardActiveObject()
+					this.canvas.setActiveObject(obj);
+					this.canvas.requestRenderAll();
+					return
+				}
+				pointerList = pointerList2
+				pointerList2 = []
+				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();
+						return
+					}
+					pointerList2.unshift(obj)
+				}
+			}
+			if (pointerList2.length > 0) {
+				if (pointerList2.length == 1) {
+					const obj = pointerList2[i];
+					this.canvas.discardActiveObject()
+					this.canvas.setActiveObject(obj);
+					this.canvas.requestRenderAll();
+					return
+				}
+				pointerList = pointerList2
+				pointerList2 = []
+				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();
+						return
+					}
+					pointerList2.unshift(obj)
+				}
+			}
+			if (pointerList2.length > 0) {
+
+				const obj = pointerList2[pointerList2.length - 1];
+				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
+			if (!target)
+				return
+			if (target?.eleType == "station") {
+				const obj = target
+				obj.data.x = _this.getActualXFromImg(obj.left)
+				obj.data.y = _this.getActualYFromImg(obj.top)
+				const vpt = _this.canvas.viewportTransform;
+				const zoom = this.canvas.getZoom();
+				let deltaX = obj.left * zoom
+				let deltaY = obj.top * zoom
+
+				_this.$ownerInstance.callMethod('receiveRenderData', {
+					method: "update_station",
+					station: obj.data,
+					view: {
+						x: vpt[4] + deltaX,
+						y: vpt[5] + deltaY,
+					}
+
+				});
+				_this.createOkCancelControl(obj)
+				_this.canvas.renderAll()
+			} else if (target?.eleType == "edit_teaching") {
+
+				_this.updateEditTeachingPath(target)
+
+			} else if (target.eleType == "virtual_wall" || target.eleType == "region") {
+				const data = target.data
+
+				const offX = target.left - target.oldLeft
+				const offY = target.top - target.oldTop
+				console.log("path", offX, offY, data.path)
+
+				data.path.forEach((pt) => {
+					pt.x += _this.getActualSizeFromImg(offX)
+					pt.y -= _this.getActualSizeFromImg(offY)
+				})
+				console.log("path2", data.path)
+				if (target.eleType == "virtual_wall")
+					_this.updateVirtualWall(target, data)
+				else
+					_this.updateRegion(target, data)
+
+				_this.canvas.renderAll()
+
+			} else if (target?.eleType == "wall_pt") {
+				const data = target.mainObj?.data
+				if (!data)
+					return
+				let pt = data.path[0]
+				let id = `${data.id}_${ pt.x}_${pt.y}`
+				console.log("wall_pt", target.id, id, pt, data.path)
+				if (target.id == id) {
+					data.path[0] = {
+						x: _this.getActualXFromImg(target.left),
+						y: _this.getActualYFromImg(target.top)
+					}
+				} else {
+					pt = data.path[1]
+
+					id = `${data.id}_${ pt.x}_${pt.y}`
+					console.log("wall_pt", id, pt)
+					if (target.id == id) {
+						data.path[1] = {
+
+							x: _this.getActualXFromImg(target.left),
+							y: _this.getActualYFromImg(target.top)
+						}
+					}
+				}
+
+				_this.updateVirtualWall(target.mainObj, data)
+
+				_this.canvas.renderAll()
+			} else if (target?.eleType == "region_pt") {
+				const data = target.mainObj?.data
+				if (!data)
+					return
+				const curIndex = data.path.findIndex((pt) => `${data.id}_${ pt.x}_${pt.y}` == target.id)
+				if (curIndex > -1) {
+					let polygon = []
+					data.path.forEach((pt, index) => {
+						if (curIndex == index) {
+							polygon.push([_this.getActualXFromImg(target.left), _this.getActualYFromImg(target
+								.top)])
+						} else {
+							polygon.push([pt.x, pt.y])
+						}
+					})
+					polygon.push(polygon[0])
+					if (hasSelfIntersection(polygon)) {
+
+						_this.showToast("杩涜鍖哄拰鍙鍖哄繀椤绘槸闂悎鍥惧舰")
+						target.set({
+							left: _this.getXOnImg(data.path[curIndex].x),
+							top: _this.getYOnImg(data.path[curIndex].y)
+						})
+						target.setCoords()
+					} else {
+						data.path[curIndex] = {
+							x: _this.getActualXFromImg(target.left),
+							y: _this.getActualYFromImg(target.top)
+						}
+						_this.updateRegion(target.mainObj, data)
+
+
+
+					}
+					_this.canvas.renderAll()
+				}
+			} else if (target?.eleType == "edit_teaching_pt") {
+
+				let left = target.mainObj.left
+				let top = target.mainObj.top
+				let width = target.mainObj.width
+				let height = target.mainObj.height
+
+				if (target.id == `edit_teaching_pt_0`) {
+					if (target.left > target.mainObj.left + target.mainObj.width - 10) {
+						target.set({
+							left: target.mainObj.left + target.mainObj.width - 10
+						})
+					}
+					width += left - target.left
+					left = target.left
+					if (target.top > target.mainObj.top + target.mainObj.height - 10) {
+						target.set({
+							top: target.mainObj.top + target.mainObj.height - 10
+						})
+					}
+					height += top - target.top
+					top = target.top
+
+
+				} else if (target.id == `edit_teaching_pt_1`) {
+					if (target.left > target.mainObj.left + target.mainObj.width - 10) {
+						target.set({
+							left: target.mainObj.left + target.mainObj.width - 10
+						})
+					}
+					width += left - target.left
+					left = target.left
+					if (target.top < target.mainObj.top + 10) {
+						target.set({
+							top: target.mainObj.top + 10
+						})
+					}
+					height = target.top - top
+				} else if (target.id == `edit_teaching_pt_2`) {
+					if (target.left < target.mainObj.left + 10) {
+						target.set({
+							left: target.mainObj.left + 10
+						})
+					}
+					width = target.left - left
+					if (target.top < target.mainObj.top + 10) {
+						target.set({
+							top: target.mainObj.top + 10
+						})
+					}
+					height = target.top - top
+				} else if (target.id == `edit_teaching_pt_3`) {
+					if (target.left < target.mainObj.left + 10) {
+						target.set({
+							left: target.mainObj.left + 10
+						})
+					}
+					width = target.left - left
+					if (target.top > target.mainObj.top + target.mainObj.height - 10) {
+						target.set({
+							top: target.mainObj.top + target.mainObj.height - 10
+						})
+					}
+					height += top - target.top
+					top = target.top
+				}
+
+				target.mainObj.set({
+					left,
+					top,
+					height,
+					width
+				})
+				target.mainObj.setCoords()
+				this.updateEditTeachingPath(target.mainObj)
+
+			}
+
+		},
+
+		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)
@@ -553,6 +1181,18 @@
 
 			});
 		},
+		// 灏�Base64 杞负 Blob锛屽啀鐢熸垚 URL
+		base64ToBlob(base64, mime) {
+			const byteString = atob(base64.split(',')[1]);
+			const ab = new ArrayBuffer(byteString.length);
+			const ia = new Uint8Array(ab);
+			for (let i = 0; i < byteString.length; i++) {
+				ia[i] = byteString.charCodeAt(i);
+			}
+			return new Blob([ab], {
+				type: mime
+			});
+		},
 		async loadBase64ImageWithProgress(data, maxSize = 2048) {
 			const _this = this
 			return new Promise((resolve, reject) => {
@@ -561,6 +1201,10 @@
 				if (base64Image.indexOf("data:image/png;base64,") < 0) {
 					base64Image = "data:image/png;base64," + data
 				}
+				const blob = this.base64ToBlob(base64Image, 'image/png');
+				const url = URL.createObjectURL(blob);
+
+
 				_this.$ownerInstance.callMethod('receiveRenderData', {
 					method: "set_backgroud_progress",
 					type: "start",
@@ -592,6 +1236,8 @@
 					}
 				};
 				img.onload = () => {
+					// 鐢ㄥ畬閲婃斁鍐呭瓨
+					URL.revokeObjectURL(url);
 					const percent = Math.min(this.progressPercent + 10, 95);
 					_this.$ownerInstance.callMethod('receiveRenderData', {
 						method: "set_backgroud_progress",
@@ -625,7 +1271,8 @@
 					reject(new Error('鍥剧墖鍔犺浇澶辫触'));
 				};
 				// 寮�鍔犺浇
-				img.src = base64Image;
+				//img.src = base64Image;
+				img.src = url;
 			});
 		},
 
@@ -638,16 +1285,30 @@
 			}
 			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)
+			}
+		},
 		setBackground(info) {
 			const _this = this
 
-			if (!this.canvas)
+			if (!this.canvas) {
+				_this.$ownerInstance.callMethod('receiveRenderData', {
+					method: "set_backgroud_progress",
+					type: "error",
+				});
 				return
-			this.canvas.clear()
-			this.canvas.selectionColor = 'rgba(100, 200, 255, 0.3)'; // 閫変腑鑳屾櫙鑹�-			this.canvas.selectionBorderColor = '#1890ff'; // 杈规棰滆壊
-			this.canvas.selectionLineWidth = 3; // 杈规瀹藉害
+			}
+
+
+			// this.canvas.clear()
+			// 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
@@ -655,92 +1316,115 @@
 			console.log("client", this.eleWidth, this.eleHeight)
 			this.canvas.setWidth(this.eleWidth);
 			this.canvas.setHeight(this.eleHeight);
-
-			//console.log("setBackground", JSON.stringify(info))
-
+			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,
+			}
 			return new Promise((resolve, reject) => {
-				// if (svgUrl) {
-				// 	fabric.loadSVGFromURL(svgUrl).then(({
-				// 		objects,
-				// 		options
-				// 	}) => {
-				// 		const workSpace = fabric.util.groupSVGElements(objects, options);
-				// 		workSpace.set({
-				// 			id: "workspace",
-				// 			eleType: "workspace",
-				// 			left: 0,
-				// 			top: 0,
-				// 			selectable: false,
-				// 			hasControls: false,
 
-
-				// 		});
-				// 		_this.canvas.add(workSpace)
-				// 		if (_this.workSpace) {
-				// 			_this.canvas.remove(_this.workSpace)
-				// 		}
-				// 		_this.workSpace = workSpace
-				// 		//	_this.auto()
-				// 		resolve()
-				// 	});
-				// } else if (imgUrl) {
 				if (info.filedata) {
 					_this.loadBase64ImageWithProgress(info.filedata).then((img) => {
 						//console.warn('setBackground', JSON.stringify(img));
 						if (img) {
+							if (_this.mapInfo.img_x == 1) {
+								_this.mapInfo.img_x = img.width
+								_this.mapInfo.max_x = _this.mapInfo.img_proportion * _this.mapInfo
+									.img_x + _this.mapInfo.min_x
+							}
+							if (_this.mapInfo.img_y == 1) {
+								_this.mapInfo.img_y = img.height
+								_this.mapInfo.max_y = _this.mapInfo.img_proportion * _this.mapInfo
+									.img_y + _this.mapInfo.min_y
+							}
 							img.set({
-								id: "workspace",
-								eleType: "workspace",
 								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,
+								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)",
+							})
+
+
+							console.log(rect.width, _this.mapInfo.img_x, img.width)
+
+							let wsGroup = new fabric.Group([img, rect], {
+								id: "workspace",
+								eleType: "workspace",
 								selectable: false,
 								hasControls: false,
+								left: 0,
+								top: 0,
+								width: _this.mapInfo.img_x,
+								height: _this.mapInfo.img_y,
+
 							});
-							_this.canvas.add(img)
+							_this.canvas.add(wsGroup)
 							if (_this.workSpace) {
 								_this.canvas.remove(_this.workSpace)
 							}
-							_this.workSpace = img
+							_this.workSpace = wsGroup
 
 						}
 						//_this.checkMemoryUsage()
 						resolve()
 
 					}).catch((err) => {
+						_this.$ownerInstance.callMethod('receiveRenderData', {
+							method: "set_backgroud_progress",
+							type: "error",
+						});
 						resolve()
 					})
 
 				} else {
+					_this.$ownerInstance.callMethod('receiveRenderData', {
+						method: "set_backgroud_progress",
+						type: "end",
+					});
 					resolve()
 				}
-				// fabric.Image.fromURL(imgUrl).then((img) => {
-				// 	// 璁剧疆鑳屾櫙鍥惧儚鐨勫睘鎬�-				// 	img.set({
-				// 		id: "workspace",
-				// 		eleType: "workspace",
-				// 		left: 0,
-				// 		top: 0,
-				// 		selectable: false,
-				// 		hasControls: false,
-				// 	});
 
-				// 	_this.canvas.add(img)
-				// 	if (_this.workSpace) {
-				// 		_this.canvas.remove(_this.workSpace)
-				// 	}
-				// 	_this.workSpace = img
-				// 	//_this.auto()
-				// 	resolve()
-				// })
-
-				//}
 			})
 		},
+
 		getAutoScale() {
 			// 鎸夌収瀹藉害
 
-			const eleWidth = this.eleWidth
-			const eleHeight = this.eleHeight
+			const eleWidth = this.eleWidth - 20
+			const eleHeight = this.eleHeight - 200
 			if (!this.workSpace)
 				return 1
 			const width = this.workSpace.width
@@ -771,12 +1455,12 @@
 				scale: scale
 			});
 			if (!this.workSpace) return;
-			this.setCenterFromObject(this.workSpace);
+			// this.setCenterFromObject(this.workSpace);
 			// 瓒呭嚭鐢诲竷涓嶅睍绀�-			_this.workSpace.clone().then((cloned) => {
-				_this.canvas.clipPath = cloned;
-				_this.canvas.requestRenderAll();
-			});
+			// _this.workSpace.clone().then((cloned) => {
+			// 	_this.canvas.clipPath = cloned;
+			// 	_this.canvas.requestRenderAll();
+			// });
 		},
 		setDrawingType(type, svg) {
 			if (svg) {
@@ -819,7 +1503,7 @@
 				x: touch.clientX,
 				y: touch.clientY
 			};
-			console.log('鍗曠偣瑙︽懜寮�');
+			//	console.log('鍗曠偣瑙︽懜寮�');
 
 			let activeObj = this.canvas.getActiveObject();
 			if (!activeObj) {
@@ -828,6 +1512,7 @@
 					this.isDragging = true;
 					this.lastPosX = touch.clientX;
 					this.lastPosY = touch.clientY;
+
 				} else {
 					this.isDragging = false;
 					this.isDrawing = true;
@@ -849,19 +1534,45 @@
 			// 澶氱偣瑙︽懜鍒濆閫昏緫
 		},
 		handleSingleTouchMove(touch) {
-			console.log('鍗曠偣绉诲姩', touch.clientX, touch.clientY);
+			//console.log('鍗曠偣绉诲姩', touch.clientX, touch.clientY,this.lastPosX,this.lastPosY);
 			if (this.isDragging) {
 				const deltaX = touch.clientX - this.lastPosX;
 				const deltaY = touch.clientY - this.lastPosY;
-				// 绉诲姩瑙嗗彛
-				console.log('relativePan', deltaX, deltaY);
 
+				if (Math.abs(deltaX) > 5 || Math.abs(deltaY) > 5) {
+					if (this.pressObjTimer) {
+						clearTimeout(this.pressObjTimer);
+						this.pressObjTimer = null
+					}
+				}
+				// 绉诲姩瑙嗗彛
+				//	console.log('relativePan', deltaX, deltaY);
+				const vpt = this.canvas.viewportTransform;
 				this.canvas.relativePan(new fabric.Point(deltaX, deltaY));
+				if (this.objEditing) {
+					if (this.objEditing.eleType == "station") {
+						const zoom = this.canvas.getZoom();
+						let deltaX2 = this.objEditing.left * zoom
+						let deltaY2 = this.objEditing.top * zoom
+						this.$ownerInstance.callMethod('receiveRenderData', {
+							method: "update_station",
+							station: this.objEditing.data,
+							view: {
+								x: vpt[4] - deltaX + deltaX2,
+								y: vpt[5] - deltaY + deltaY2,
+							}
+
+						});
+					}
+				}
+
+
 				this.lastPosX = touch.clientX;
 				this.lastPosY = touch.clientY;
+				this.canvas.renderAll()
 			} else if (this.isDrawing) {
 				const vpt = this.canvas.viewportTransform;
-				console.log("viewportTransform", vpt[4], vpt[5])
+				//	console.log("viewportTransform", vpt[4], vpt[5])
 				let startX = this.touchStartPoint.x - vpt[4]
 				let startY = this.touchStartPoint.y - vpt[5]
 				let endX = touch.clientX - vpt[4]
@@ -872,7 +1583,7 @@
 				startY /= scale
 				endX /= scale
 				endY /= scale
-				console.log("viewportTransform", startX, startY, endX, endY)
+				//	console.log("viewportTransform", startX, startY, endX, endY)
 				const left =
 					endX > startX ?
 					startX :
@@ -1017,7 +1728,7 @@
 
 						this.drawingObj = line
 					}
-					this.canvas.requestRenderAll();
+					// this.canvas.requestRenderAll();
 				}
 
 
@@ -1033,7 +1744,7 @@
 			const _this = this
 			const touch1 = touches[0];
 			const touch2 = touches[1];
-			console.log('澶氱偣绉诲姩', touch1.identifier, touch2.identifier);
+			//console.log('澶氱偣绉诲姩', touch1.identifier, touch2.identifier);
 
 			const distance = Math.sqrt(
 				Math.pow(touch2.clientX - touch1.clientX, 2) +
@@ -1062,59 +1773,132 @@
 			} else if (scale == scaleAuto) {
 				return
 			}
-			console.log(scale, scaleAuto)
+			//console.log(scale, scaleAuto)
 			this.setZoomAuto(scale, center)
 
-			console.log('澶氱偣绉诲姩 - 璺濈:', distance, '瑙掑害:', angle);
+			//	console.log('澶氱偣绉诲姩 - 璺濈:', distance, '瑙掑害:', angle);
 			// 澶氱偣绉诲姩閫昏緫
 		},
 		handleTouchEnd() {
-			console.log('鎵�湁瑙︽懜缁撴潫');
+			//	console.log('鎵�湁瑙︽懜缁撴潫');
 			this.isDrawing = false
 			this.drawingObj = undefined
 			this.initialDistance = null;
 			// 瑙︽懜缁撴潫閫昏緫
 		},
+
+		getXOnImg(x) {
+			const mapX = x * this.mapInfo.proportion
+			const imgX = (mapX - this.mapInfo.min_x) / this.mapInfo.img_proportion
+			return imgX
+		},
+		getYOnImg(y) {
+			const mapY = y * this.mapInfo.proportion
+			const imgY = (this.mapInfo.max_y - mapY) / this.mapInfo.img_proportion
+			return imgY
+		},
+		getSizeOnImg(size) {
+			const imgSize = size * this.mapInfo.proportion / this.mapInfo.img_proportion
+			return imgSize
+		},
+		getActualXFromImg(x) {
+			const mapX = x * this.mapInfo.img_proportion + this.mapInfo.min_x
+			const actualX = mapX / this.mapInfo.proportion
+			return actualX
+		},
+		getActualYFromImg(y) {
+			const mapY = this.mapInfo.max_y - y * this.mapInfo.img_proportion
+			const actualY = mapY / this.mapInfo.proportion
+			return actualY
+		},
+		getActualSizeFromImg(size) {
+			const actualSize = size * this.mapInfo.img_proportion / this.mapInfo.proportion
+			return actualSize
+		},
+		onScaleChange() {
+			var scale = this.canvas.getZoom()
+			if (scale < 1) {
+				scale = 1
+			}
+			const scale2 = 1 / scale
+
+			let list = this.canvas.getObjects()
+			const filter = ["agv", "current_teaching", "edit_teaching", "edit_teaching_pt", "cmd", "station",
+				"station_mark", "station_tip"
+			]
+			list = list.filter((a) => filter.includes(a.eleType))
+			list.forEach((obj) => {
+				if (obj.eleType == "edit_teaching") {
+					obj.set({
+						strokeWidth: 2 * scale2
+					})
+				} else {
+					obj.set({
+						scaleX: scale2,
+						scaleY: scale2
+					})
+				}
+				if (obj.eleType == "station" || obj.eleType == "edit_teaching") {
+					const tipObj = obj.tipObj
+					if (tipObj) {
+						tipObj.set({
+							left: obj.left,
+							top: obj.top - (obj.height / 2 + tipObj
+								.height / 2) * scale2
+						})
+						tipObj.setCoords()
+					}
+				}
+				if (obj.eleType == "cmd") {
+					const obj2 = obj.mainObj
+					if (obj2?.eleType == "station") {
+						if (obj.id == `cancel`) {
+							obj.set({
+								left: obj2.left - (obj2.width / 2 + 50) * scale2,
+							})
+							obj.setCoords()
+						} else if (obj.id == `ok`) {
+							obj.set({
+								left: obj2.left + (obj2.width / 2 + 50) * scale2,
+							})
+							obj.setCoords()
+						}
+
+
+
+					}
+				}
+
+			})
+			if (this.objEditing) {
+				this.createOkCancelControl(this.objEditing)
+			}
+			this.canvas.renderAll()
+
+		},
+
 		addStation(info) {
 			const _this = this
+
 			return new Promise((resolve) => {
 				const zoom = _this.canvas.getZoom();
 				let svg = "static/images/station.svg"
-				// if (info.angle > 0) {
-				// 	if (info.angle / 3.14 <= 0.25) {
-				// 		svg = "static/images/station2.svg"
-				// 	} else if (info.angle / 3.14 <= 0.50) {
-				// 		svg = "static/images/station3.svg"
-				// 	} else if (info.angle / 3.14 <= 0.75) {
-				// 		svg = "static/images/station4.svg"
-				// 	} else if (info.angle / 3.14 <= 1) {
-				// 		svg = "static/images/station5.svg"
-				// 	}
-				// } else if (info.angle < 0) {
-				// 	if (info.angle / 3.14 < -0.75) {
-				// 		svg = "static/images/station5.svg"
-				// 	} else if (info.angle / 3.14 < -0.5) {
-				// 		svg = "static/images/station6.svg"
-				// 	} else if (info.angle / 3.14 < -0.25) {
-				// 		svg = "static/images/station7.svg"
-				// 	} else if (info.angle / 3.14 < 0) {
-				// 		svg = "static/images/station8.svg"
-				// 	}
-				// }
 
-				//	const scale = this.getAutoScale()
-				const left = info.x // * scale
-				const top = info.y //* scale
-				const angle = info.angle * 180 / 3.14
-				console.log("addStation", svg, info)
+				const left = _this.getXOnImg(info.x) // * scale
+				const top = _this.getYOnImg(info.y) //* scale
+				const angle = info.angle * 180 / Math.PI
 				fabric.loadSVGFromURL(svg).then(
 					({
 						objects,
 						options
 					}) => {
-
-						const obj = fabric.util.groupSVGElements(objects, options);
-						obj.set({
+						var scale = this.canvas.getZoom()
+						if (scale < 1) {
+							scale = 1
+						}
+						const scale2 = 1 / scale
+						const objGroup = fabric.util.groupSVGElements(objects, options);
+						objGroup.set({
 							id: `station_${new Date().getTime()}`,
 							eleType: "station",
 							left,
@@ -1123,6 +1907,10 @@
 							data: info,
 							originX: "center",
 							originY: "center",
+							// width: options.width,
+							// height: options.height,
+							// viewBoxWidth: options.viewBoxWidth || options.width,
+							// viewBoxHeight: options.viewBoxHeight || options.height,
 							hasControls: this.editMode,
 							lockRotation: true,
 							lockScalingX: true,
@@ -1130,21 +1918,589 @@
 							lockMovementX: true,
 							lockMovementY: true,
 							canSelect: true,
-							lockEdit: true
+							lockEdit: true,
+							scaleX: scale2,
+							scaleY: scale2,
+
+						})
+						_this.canvas.add(objGroup)
+
+						const objectText = new fabric.Text(`${info.name}`, {
+							id: `station_tip${new Date().getTime()}`,
+							eleType: "station_tip",
+							left: objGroup.left,
+							top: objGroup.top - 20,
+							fontSize: 14,
+							fontFamily: "Microsoft YaHei", //
+							fill: "#000", //
+							stroke: "#000", //
+							textBaseline: "alphabetic", // Correct value
+							originX: "center",
+							originY: "center",
+							hasControls: false,
+							lockRotation: true,
+							lockScalingX: true,
+							lockScalingY: true,
+							lockMovementX: true,
+							lockMovementY: true,
+							canSelect: false,
+							selectable: false,
+
+							scaleX: scale2,
+							scaleY: scale2,
 						});
-						_this.addObject(obj)
+						objGroup.set({
+							tipObj: objectText
+						})
+						objectText.set({
+							left: objGroup.left,
+							top: objGroup.top - (objGroup.height / 2 + objectText
+								.height / 2) * scale2,
+						})
+						_this.canvas.add(objectText)
+						_this.canvas.bringObjectToFront(objGroup);
 						resolve()
 					}
 				)
 			})
 		},
+		setMarkStation(obj, mark) {
+			console.log("setMarkStation", obj.id, obj.mark, mark)
+			if (mark) {
+				if (obj.mark) {
+					return
+				}
+				var scale = this.canvas.getZoom()
+				if (scale < 1) {
+					scale = 1
+				}
+				const scale2 = 1 / scale
+				let ellipse = new fabric.Ellipse({
+					id: `station_mark${new Date().getTime()}`,
+					eleType: "station_mark",
+					left: obj.left,
+					top: obj.top,
+					rx: 20,
+					ry: 20,
+					stroke: "#ff7f23",
+					strokeWidth: 2,
+					fill: "#ffffff00",
+					originX: "center",
+					originY: "center",
+					lockRotation: true,
+					lockScalingX: true,
+					lockScalingY: true,
+					scaleX: scale2,
+					scaleY: scale2,
+				});
+				obj.set({
+					mark: true,
+					markObj: ellipse,
+				})
+				obj.add(ellipse)
+
+			} else {
+				if (obj.mark) {
+					obj.remove(obj.markObj)
+					delete obj.markObj
+					obj.set({
+						mark: false,
+					})
+				}
+				return
+			}
+
+		},
+		updateCurrentTeaching(teachingData) {
+			var posArr = teachingData.pos_list || []
+			const pos_list = []
+			if (this.curTeachingObj) {
+				this.canvas.remove(this.curTeachingObj)
+				this.curTeachingObj = null
+			}
+
+			posArr.forEach((item) => {
+				const curIndex = pos_list.findIndex((item2) => item2.x === item.x && item2.y === item.y)
+				if (curIndex < 0) {
+					pos_list.push(item)
+				}
+			})
+			let path2 = ""
+			const theta = 20;
+			let headlen = 10;
+			var main_road = teachingData.main_road || 0
+
+			const len = pos_list.length
+			let fromX = 0,
+				fromY = 0,
+				toX = 0,
+				toY = 0;
+			for (let index = 0; index < len; index++) {
+				const pt = pos_list[index]
+				let pt2 = {
+					x: this.getXOnImg(pt.x),
+					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}`
+
+							// 璁$畻鍚勮搴﹀拰瀵瑰簲鐨凱2,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}`
+					}
+				} else {
+					path2 = `M${pt2.x} ${pt2.y}`
+				}
+				fromX = pt2.x
+				fromY = pt2.y
+			}
+			let strokeWidth = 1
+			let stroke = "#95DE64"
+
+			if (main_road == 1) {
+				stroke = "#69C0FF"
+			}
+			const objPath = new fabric.Path(
+				path2, {
+					id: "current_teaching",
+					eleType: "current_teaching",
+					stroke: stroke,
+					strokeWidth,
+					// strokeDashArray: [5, 3],
+					// strokeLineCap: 'butt',
+					fill: "#ffffff00",
+					hasControls: false,
+					lockRotation: true,
+					lockScalingX: true,
+					lockScalingY: true,
+					lockMovementX: true,
+					lockMovementY: true,
+					selectable: false,
+					opacity: 1,
+				})
+			this.canvas.add(objPath)
+			this.curTeachingObj = objPath
+		},
+		addTeachingPath(teachingData, id, type) {
+
+			var posArr = teachingData.pos_list || []
+			const pos_list = []
+			posArr.forEach((item) => {
+				const curIndex = pos_list.findIndex((item2) => item2.x === item.x && item2.y === item.y)
+				if (curIndex < 0) {
+					pos_list.push(item)
+				}
+			})
+
+			console.log(posArr.length, pos_list.length)
+			let path2 = ""
+
+			const theta = 20;
+			let headlen = 10;
+			var main_road = 1
+
+			const len = pos_list.length
+			let fromX = 0,
+				fromY = 0,
+				toX = 0,
+				toY = 0;
+
+
+			for (let index = 0; index < len; index++) {
+				const pt = pos_list[index]
+				let pt2 = {
+					x: this.getXOnImg(pt.x),
+					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}`
+
+							// 璁$畻鍚勮搴﹀拰瀵瑰簲鐨凱2,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}`
+					}
+				} else {
+					main_road = pt.main_road
+					if (main_road == 1) {
+						headlen = 15
+					}
+					path2 = `M${pt2.x} ${pt2.y}`
+				}
+				fromX = pt2.x
+				fromY = pt2.y
+
+
+			}
+			//console.log(path2)
+			// path2 += " Z"
+			let strokeWidth = 1
+			let stroke = "#95DE64"
+			if (type == "station_teaching") {
+				stroke = "#69C0FF"
+			} else {
+				if (main_road == 1) {
+					stroke = "#69C0FF"
+				}
+			}
+			let list = this.canvas.getObjects() || []
+			list = list.filter((a) => a.eleType == "station_teaching" || a.eleType == "public_teaching")
+			let lenTeaching = list.length
+			if (main_road == 1) {
+				list = list.filter((a) => a.eleType == "public_teaching" && a.mainRoad == 1)
+				lenTeaching = list.length
+			}
+
+			let ptList = []
+			const objPath = new fabric.Path(
+				path2, {
+					id: id,
+					eleType: type,
+					stroke: stroke,
+					strokeWidth,
+					// strokeDashArray: [5, 3],
+					// strokeLineCap: 'butt',
+					fill: "#ffffff00",
+					hasControls: false,
+					lockRotation: true,
+					lockScalingX: true,
+					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.moveObjectTo(objPath, lenTeaching + 1);
+
+			return objPath
+		},
+		isObjectFullyContained(outerObj, innerObj) {
+			const outer = outerObj.getBoundingRect(true, true);
+			const inner = innerObj.getBoundingRect(true, true);
+
+			return (
+				outer.left <= inner.left &&
+				outer.top <= inner.top &&
+				outer.left + outer.width >= inner.left + inner.width &&
+				outer.top + outer.height >= inner.top + inner.height
+			);
+		},
+
+		compareOverlap(bottomObj, topObj) {
+			const bottomRect = bottomObj.getBoundingRect(true, true);
+			const topRect = topObj.getBoundingRect(true, true);
+			const maxLeft = Math.min(bottomRect.left + bottomRect.width, topObj.width + topObj.left)
+			const maxTop = Math.min(bottomRect.top + bottomRect.height, topObj.height + topObj.top)
+			const minRight = Math.min(bottomRect.left + bottomRect.width, topObj.width + topObj.left)
+			const minBottom = Math.min(bottomRect.top + bottomRect.height, topObj.height + topObj.top)
+			if (minRight <= maxLeft || minBottom <= maxTop) return true; // 鏃犻噸鍙�+			const bottomArea = bottomRect.width * bottomRect.height;
+			const topArea = topRect.width * topRect.height;
+			const interArea = (minRight - maxLeft) * (minBottom - maxTop)
+			if (bottomArea - interArea > topArea - -interArea) {
+				return true; //搴曢儴鏈噸鍙犻潰绉ぇ
+			}
+			return false;
+
+		},
+		showTeachingPath(show) {
+
+			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: '',
+					hasControls: show,
+					selectable: show,
+				})
+			}
+
+
+		},
+		showEditTeachingPath(teachingMode) {
+
+			let list = this.canvas.getObjects() || []
+			let eleType = ""
+			let id = ""
+			if (teachingMode.mode == "Public") {
+				eleType = "public_teaching"
+				id = `public_teaching_${teachingMode.name}`
+			} else if (teachingMode.mode == "Stations") {
+				eleType = "station_teaching"
+				id = `station_teaching_${teachingMode.src_dst}`
+			} else {
+				return
+			}
+
+			let objTeaching;
+			for (let i2 in list) {
+				const obj = list[i2]
+				if (obj.eleType == eleType && obj.id == id) {
+					obj.set({
+						opacity: 1,
+						selectable: false
+					})
+					objTeaching = obj
+				} else {
+					obj.set({
+						opacity: 0.5
+					})
+				}
+			}
+
+			if (objTeaching) {
+				var scale = this.canvas.getZoom()
+				if (scale < 1) {
+					scale = 1
+				}
+				const scale2 = 1 / scale
+
+				this.canvas.discardActiveObject();
+
+				const width = (objTeaching.width > 200 ? 200 : objTeaching.width < 100 ? 100 : objTeaching
+					.width) * scale2
+				const height = (objTeaching.height > 200 ? 200 : objTeaching.height < 100 ? 100 : objTeaching
+					.height) * scale2
+
+				const pt = teachingMode.point
+				let left = objTeaching.left - 10
+				let top = objTeaching.top - 10
+				if (pt) {
+					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",
+					left,
+					top,
+					width,
+					height,
+					stroke: "#ff4d4f",
+					strokeWidth: 2 * scale2,
+					strokeDashArray: [5, 3],
+					strokeLineCap: 'butt',
+					fill: "#ff4d4f20",
+					hasControls: true,
+					lockRotation: true,
+					lockScalingX: true,
+					lockScalingY: true,
+					mainObj: objTeaching
+				})
+
+				this.canvas.add(rect)
+				this.createOkCancelControl(rect)
+				const ptList = []
+				const path = [{
+					x: rect.left,
+					y: rect.top
+				}, {
+					x: rect.left,
+					y: rect.top + rect.height
+				}, {
+					x: rect.left + rect.width,
+					y: rect.top + rect.height
+				}, {
+					x: rect.left + rect.width,
+					y: rect.top
+				}]
+				path.forEach((pt, index) => {
+					let ellipse = new fabric.Ellipse({
+						id: `edit_teaching_pt_${index}`,
+						eleType: "edit_teaching_pt",
+						left: pt.x,
+						top: pt.y,
+						rx: 10,
+						ry: 10,
+						stroke: "#ff4d4f",
+						strokeWidth: 1,
+						fill: "#ff4d4f",
+						originX: "center",
+						originY: "center",
+						lockRotation: true,
+						lockScalingX: true,
+						lockScalingY: true,
+						scaleX: scale2,
+						scaleY: scale2,
+						mainObj: rect
+					});
+					this.canvas.add(ellipse)
+					ptList.push(ellipse)
+				})
+
+				rect.set({
+					ptObjs: ptList,
+					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) {
+					const obj = list[i2]
+					obj.set({
+						opacity: 1
+					})
+				}
+			}
+
+		},
+		updateEditTeachingPath(obj) {
+			const ptObjs = obj.ptObjs || []
+			ptObjs[0].set({
+				left: obj.left,
+				top: obj.top
+			})
+			ptObjs[0].setCoords()
+			ptObjs[1].set({
+				left: obj.left,
+				top: obj.top + obj.height
+			})
+			ptObjs[1].setCoords()
+			ptObjs[2].set({
+				left: obj.left + obj.width,
+				top: obj.top + obj.height
+			})
+			ptObjs[2].setCoords()
+			ptObjs[3].set({
+				left: obj.left + obj.width,
+				top: obj.top
+			})
+			ptObjs[3].setCoords()
+			this.createOkCancelControl(obj)
+			this.canvas.requestRenderAll();
+		},
 		updateAgv(info) {
 			const _this = this
 			return new Promise((resolve) => {
 				//	const scale = this.getAutoScale()
-				const left = info.x // * scale
-				const top = info.y // * scale
-				const angle = info.angle * 180 / 3.14
+				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({
 						left,
@@ -1163,6 +2519,11 @@
 							options
 						}) => {
 
+							var scale = this.canvas.getZoom()
+							if (scale < 1) {
+								scale = 1
+							}
+							const scale2 = 1 / scale
 							const obj = fabric.util.groupSVGElements(objects, options);
 
 							obj.set({
@@ -1182,11 +2543,14 @@
 								lockMovementX: true,
 								lockMovementY: true,
 								selectable: false,
+								scaleX: scale2,
+								scaleY: scale2,
 								lockEdit: true
 
 							});
 							console.log("agv", JSON.stringify(info))
 							_this.canvas.add(obj)
+							//	_this.canvas.bringObjectToFront(obj);
 							_this.agvObj = obj
 							resolve()
 						}
@@ -1195,6 +2559,684 @@
 
 			})
 		},
+
+
+		addVirtualWallShow(info) {
+			const path = info.path || []
+			if (path.length != 2)
+				return
+
+			const line = new fabric.Line([this.getXOnImg(path[0].x), this.getYOnImg(path[0].y), this
+				.getXOnImg(
+					path[1]
+					.x), this.getYOnImg(path[1].y)
+			], {
+				id: `${info.id}`,
+				eleType: "virtual_wall",
+				stroke: info.color || "#ff4d4f",
+				strokeWidth: 3,
+				strokeDashArray: [5, 3],
+				strokeLineCap: 'butt',
+				hasControls: false,
+				lockRotation: true,
+				lockScalingX: true,
+				lockScalingY: true,
+				lockMovementX: true,
+				lockMovementY: true,
+				selectable: true,
+				data: info
+			});
+			return line
+		},
+
+		addVirtualWall(info) {
+			const path = info.path || []
+			if (path.length != 2)
+				return
+			const line = new fabric.Line([this.getXOnImg(path[0].x), this.getYOnImg(path[0].y), this
+				.getXOnImg(
+					path[1]
+					.x), this.getYOnImg(path[1].y)
+			], {
+				id: `${info.id}`,
+				eleType: "virtual_wall",
+				stroke: info.color || "#ff4d4f",
+				strokeWidth: 3,
+				strokeDashArray: [5, 3],
+				strokeLineCap: 'butt',
+				hasControls: false,
+				lockRotation: true,
+				lockScalingX: true,
+				lockScalingY: true,
+				selectable: true,
+
+				data: info
+			});
+			this.canvas.add(line)
+			let ptImg = {
+				x: this.getXOnImg(path[0].x),
+				y: this.getYOnImg(path[0].y)
+			}
+			let pt = {
+				x: path[0].x,
+				y: path[0].y
+			}
+			let ellipse = new fabric.Ellipse({
+				id: `${info.id}_${ pt.x}_${pt.y}`,
+				eleType: "wall_pt",
+				left: ptImg.x,
+				top: ptImg.y,
+				rx: 10,
+				ry: 10,
+				stroke: info.color || "#ff4d4f",
+				strokeWidth: 1,
+				fill: info.color || "#ff4d4f",
+				originX: "center",
+				originY: "center",
+				lockEdit: true,
+				lockRotation: true,
+				lockScalingX: true,
+				lockScalingY: true,
+				mainObj: line
+			});
+			this.canvas.add(ellipse)
+			ptImg = {
+				x: this.getXOnImg(path[1].x),
+				y: this.getYOnImg(path[1].y)
+			}
+			pt = {
+				x: path[1].x,
+				y: path[1].y
+			}
+			let ellipse2 = new fabric.Ellipse({
+				id: `${info.id}_${ pt.x}_${pt.y}`,
+				eleType: "wall_pt",
+				left: ptImg.x,
+				top: ptImg.y,
+				rx: 10,
+				ry: 10,
+				stroke: info.color || "#ff4d4f",
+				strokeWidth: 1,
+				fill: info.color || "#ff4d4f",
+				originX: "center",
+				originY: "center",
+				lockEdit: true,
+				lockRotation: true,
+				lockScalingX: true,
+				lockScalingY: true,
+				mainObj: line
+			});
+			this.canvas.add(ellipse2)
+			line.set({
+				ptObj1: ellipse,
+				ptObj2: ellipse2
+			})
+
+			this.createOkCancelControl(line)
+			this.editObject = line
+			line.set({
+				oldLeft: line.left,
+				oldTop: line.top,
+			})
+			return line
+		},
+		updateVirtualWall(obj, info) {
+			const path = info.path || []
+			if (path.length != 2)
+				return
+			let ptImg1 = {
+				x: this.getXOnImg(path[0].x),
+				y: this.getYOnImg(path[0].y)
+			}
+			let ptImg2 = {
+				x: this.getXOnImg(path[1].x),
+				y: this.getYOnImg(path[1].y)
+			}
+			let pt1 = {
+				x: path[0].x,
+				y: path[0].y
+			}
+			let pt2 = {
+				x: path[1].x,
+				y: path[1].y
+			}
+			const left = ptImg2.x > ptImg1.x ? ptImg1.x : ptImg2.x;
+			const top = ptImg2.y > ptImg1.y ? ptImg1.y : ptImg2.y;
+			obj.set({
+				left: left,
+				top: top,
+				x1: ptImg1.x,
+				y1: ptImg1.y,
+				x2: ptImg2.x,
+				y2: ptImg2.y,
+				data: info,
+			});
+			obj.setCoords()
+			obj.ptObj1.set({
+				id: `${info.id}_${ pt1.x}_${pt1.y}`,
+				left: ptImg1.x,
+				top: ptImg1.y,
+
+			})
+			obj.ptObj2.set({
+				id: `${info.id}_${ pt2.x}_${pt2.y}`,
+				left: ptImg2.x,
+				top: ptImg2.y,
+
+			})
+			obj.ptObj1.setCoords()
+			obj.ptObj2.setCoords()
+
+			this.createOkCancelControl(obj)
+			this.editObject = obj
+			obj.set({
+				oldLeft: obj.left,
+				oldTop: obj.top,
+			})
+		},
+		addVirtualWallFinish(obj) {
+			this.canvas.remove(obj.ptObj1)
+			this.canvas.remove(obj.ptObj2)
+			delete obj.ptObj1
+			delete obj.ptObj2
+			this.canvas.sendObjectToBack(obj);
+			this.canvas.moveObjectTo(obj, 1);
+			obj.set({
+				lockMovementX: true,
+				lockMovementY: true,
+				selectable: false
+			})
+
+		},
+		removeVirtualWall(obj) {
+			this.closeOkCancelControl()
+			if (obj.ptObj1)
+				this.canvas.remove(obj.ptObj1)
+			if (obj.ptObj1)
+				this.canvas.remove(obj.ptObj2)
+			this.canvas.remove(obj)
+		},
+		addRegionShow(info) {
+			const _this = this
+			const path = info.path || []
+			let path2 = ""
+			path.forEach((pt, index) => {
+
+				let pt2 = {
+					x: this.getXOnImg(pt.x),
+					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 {
+					path2 = `M${pt2.x} ${pt2.y}`
+				}
+			})
+			path2 += " Z"
+			let ptList = []
+			let fillColor = info.color || "#ff4538"
+
+			const objPath = new fabric.Path(
+				path2, {
+					id: `${info.id}`,
+					eleType: "region",
+					stroke: info.color || "#ff4d4f",
+					strokeWidth: 3,
+					strokeDashArray: [5, 3],
+					strokeLineCap: 'butt',
+					fill: hexToRGBA(fillColor, 0.2),
+					hasControls: false,
+					lockRotation: true,
+					lockScalingX: true,
+					lockScalingY: true,
+					lockMovementX: true,
+					lockMovementY: true,
+					selectable: false,
+					data: info
+				})
+			this.canvas.add(objPath)
+			return objPath
+		},
+
+		addRegion(info) {
+			const _this = this
+			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)
+				}
+				if (index > 0) {
+					path2 += ` L${pt2.x} ${pt2.y}`
+				} else {
+					path2 = `M${pt2.x} ${pt2.y}`
+				}
+			})
+			path2 += " Z"
+			let ptList = []
+			const objPath = new fabric.Path(
+				path2, {
+					id: `${info.id}`,
+					eleType: "region",
+					stroke: info.color || "#ff4d4f",
+					strokeWidth: 3,
+					strokeDashArray: [5, 3],
+					strokeLineCap: 'butt',
+					fill: "rgba(255,255,255,0)",
+					hasControls: false,
+					lockRotation: true,
+					lockScalingX: true,
+					lockScalingY: true,
+					data: info
+				})
+			this.canvas.add(objPath)
+			path.forEach((pt, index) => {
+				let pt2 = {
+					x: this.getXOnImg(pt.x),
+					y: this.getYOnImg(pt.y)
+				}
+				let ellipse = new fabric.Ellipse({
+					id: `${info.id}_${ pt.x}_${pt.y}`,
+					eleType: "region_pt",
+					left: pt.x,
+					top: pt.y,
+					rx: 10,
+					ry: 10,
+					stroke: info.color || "#ff4d4f",
+					strokeWidth: 1,
+					fill: info.color || "#ff4d4f",
+					originX: "center",
+					originY: "center",
+					lockRotation: true,
+					lockScalingX: true,
+					lockScalingY: true,
+					mainObj: objPath
+				});
+				this.canvas.add(ellipse)
+				ptList.push(ellipse)
+
+			})
+			const ptLast = path[0]
+			const ptLast2 = path[path.length - 1]
+
+			const objAddList = []
+			let ellipse = new fabric.Ellipse({
+				left: -10,
+				top: -10,
+				rx: 10,
+				ry: 10,
+				stroke: info.color || "#ff4d4f",
+				strokeWidth: 1,
+				fill: info.color || "#ff4d4f",
+			});
+			objAddList.push(ellipse)
+			let line = new fabric.Line([-6, 0, 6, 0], {
+				stroke: "#FEFEFE",
+				strokeWidth: 2,
+			});
+			objAddList.push(line)
+			line = new fabric.Line([0, -6, 0, 6], {
+				stroke: "#FEFEFE",
+				strokeWidth: 2,
+			});
+			objAddList.push(line)
+			let objGroup = new fabric.Group(objAddList, {
+				id: `${info.id}_add`,
+				eleType: "region_pt_add",
+				left: ptLast2.x + (ptLast.x - ptLast2.x) / 2,
+				top: ptLast2.y + (ptLast.y - ptLast2.y) / 2,
+				width: 20,
+				height: 20,
+				originX: "center",
+				originY: "center",
+				lockRotation: true,
+				lockScalingX: true,
+				lockScalingY: true,
+				lockMovementX: true,
+				lockMovementY: true,
+				mainObj: objPath
+			});
+			this.canvas.add(objGroup)
+
+			objPath.set({
+				ptObjs: ptList,
+				ptAddObj: objGroup,
+				oldLeft: objPath.left,
+				oldTop: objPath.top,
+			})
+			this.createOkCancelControl(objPath)
+			this.editObject = objPath
+			return objPath
+
+		},
+
+		updateRegion(obj, info) {
+			const path = info.path || []
+			let path2 = ""
+			path.forEach((pt, index) => {
+				let pt2 = {
+					x: this.getXOnImg(pt.x),
+					y: this.getYOnImg(pt.y)
+				}
+				if (index > 0) {
+					path2 += ` L${pt2.x} ${pt2.y}`
+				} else {
+					path2 = `M${pt2.x} ${pt2.y}`
+				}
+			})
+			path2 += " Z"
+
+			const objList = obj.ptObjs || []
+			const objAdd = obj.ptAddObj
+
+			let listObj = this.canvas.getObjects() || []
+			const curIndex = listObj.findIndex((a) => a.eleType == "region" && a.id == obj.id)
+			let oldObj
+			if (curIndex > -1) {
+				oldObj = listObj[curIndex]
+			}
+			let objNew = new fabric.Path(
+				path2, {
+					id: `${info.id}`,
+					eleType: "region",
+					stroke: info.color || "#ff4d4f",
+					strokeWidth: 3,
+					strokeDashArray: [5, 3],
+					strokeLineCap: 'butt',
+					fill: "rgba(255,255,255,0)",
+					hasControls: false,
+					lockRotation: true,
+					lockScalingX: true,
+					lockScalingY: true,
+
+					data: info,
+					ptObjs: objList,
+					ptAddObj: objAdd
+				})
+			this.canvas.add(objNew)
+			// console.log("remove", curIndex, obj.id, obj)
+			if (oldObj) {
+				this.canvas.remove(oldObj)
+			}
+			if (objList.length > path.length) {
+				for (let i = path.length; i < objList.length; i++) {
+					this.canvas.remove(objList[i])
+				}
+			} else if (objList.length == path.length) {
+				for (let i = 0; i < objList.length; i++) {
+					const pt = path[i]
+					let pt2 = {
+						x: this.getXOnImg(pt.x),
+						y: this.getYOnImg(pt.y)
+					}
+					objList[i].set({
+						id: `${info.id}_${ pt.x}_${pt.y}`,
+						left: pt2.x,
+						top: pt2.y,
+						mainObj: objNew
+					})
+
+					this.canvas.bringObjectToFront(objList[i]);
+					objList[i].setCoords()
+				}
+			} else {
+				for (let i = objList.length; i < path.length; i++) {
+					const pt = path[i]
+					let pt2 = {
+						x: this.getXOnImg(pt.x),
+						y: this.getYOnImg(pt.y)
+					}
+					let ellipse = new fabric.Ellipse({
+						id: `${info.id}_${ pt.x}_${pt.y}`,
+						eleType: "region_pt",
+						left: pt2.x,
+						top: pt2.y,
+						rx: 10,
+						ry: 10,
+						stroke: info.color || "#ff4d4f",
+						strokeWidth: 1,
+						fill: info.color || "#ff4d4f",
+						originX: "center",
+						originY: "center",
+						lockRotation: true,
+						lockScalingX: true,
+						lockScalingY: true,
+						mainObj: objNew
+					});
+					this.canvas.add(ellipse)
+					objList.push(ellipse)
+				}
+			}
+			const ptLast = {
+				x: this.getXOnImg(path[0].x),
+				y: this.getYOnImg(path[0].y)
+			}
+			const ptLast2 = {
+				x: this.getXOnImg(path[path.length - 1].x),
+				y: this.getYOnImg(path[path.length - 1].y)
+			}
+			objNew.ptAddObj.set({
+				left: ptLast2.x + (ptLast.x - ptLast2.x) / 2,
+				top: ptLast2.y + (ptLast.y - ptLast2.y) / 2,
+				mainObj: objNew
+			})
+			objNew.set({
+				oldLeft: objNew.left,
+				oldTop: objNew.top,
+			})
+			this.canvas.bringObjectToFront(objNew.ptAddObj);
+			objNew.ptAddObj.setCoords()
+			let list = this.canvas.getObjects() || []
+			list = list.filter((a) => a.eleType == "cmd")
+			list.forEach((obj) => {
+				obj.mainObj = objNew
+			})
+			this.editObject = objNew
+			this.createOkCancelControl(objNew)
+		},
+		addRegionFinish(obj) {
+			const objList = obj.ptObjs || []
+
+
+			for (let i = 0; i < objList.length; i++) {
+				this.canvas.remove(objList[i])
+			}
+			let color = obj.data.color || "#ff4d4f"
+			obj.set({
+				fill: hexToRGBA(color, 0.2)
+			})
+			this.canvas.remove(obj.ptAddObj)
+
+			delete obj.ptObjs
+			delete obj.ptAddObj
+			this.canvas.sendObjectToBack(obj);
+			this.canvas.moveObjectTo(obj, 1);
+			obj.set({
+				lockMovementX: true,
+				lockMovementY: true,
+				selectable: false
+			})
+		},
+		removeRegion(obj) {
+			this.closeOkCancelControl()
+			const objList = obj.ptObjs || []
+			for (let i = 0; i < objList.length; i++) {
+				this.canvas.remove(objList[i])
+			}
+			if (obj.ptAddObj)
+				this.canvas.remove(obj.ptAddObj)
+			this.canvas.remove(obj)
+		},
+		updateAgvLaser(param) {
+			const angle = param.angle - Math.PI / 2 // * 180 / Math.PI
+			const pt = {
+				x: this.getXOnImg(param.x),
+				y: this.getYOnImg(param.y)
+			}
+			let ellipse = new fabric.Ellipse({
+				id: "agv_laser",
+				eleType: "agv_laser",
+				left: pt.x,
+				top: pt.y,
+				rx: 2,
+				ry: 2,
+				stroke: "#00aa00",
+				strokeWidth: 1,
+				fill: "#00aa00",
+				originX: "center",
+				originY: "center",
+				selectable: false,
+				hasControls: true,
+				lockRotation: true,
+				lockScalingX: true,
+				lockScalingY: true,
+				lockMovementX: true,
+				lockMovementY: true,
+			});
+			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)
+			}
+			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,
+			});
+			this.objAgvLaser = line
+			this.canvas.add(line)
+		},
+		updateLaserPoint(param) {
+
+			let list2 = this.canvas.getObjects() || []
+			list2 = list2.filter((a) => a.eleType == "laser_point")
+			for (let i in list2) {
+				const obj = list2[i]
+				obj.set({
+					fill: "#000",
+				})
+			}
+			const list = param.xy || []
+			for (let i in list) {
+				const pt = list[i]
+				const pt2 = {
+					x: this.getXOnImg(pt[0]),
+					y: this.getYOnImg(pt[1])
+				}
+				const point = new fabric.Rect({
+					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,
+				});
+				// 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)
+			}
+
+		},
+
+		ensurePointVisible(pt) {
+			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
+				} 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 - 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
+				} else {
+					newPanY = pt.y * zoom - this.eleHeight / 2
+				}
+			}
+
+			// 鍙湁鍦ㄩ渶瑕佹椂鎵嶅钩绉�+			if (newPanX !== vpt[4] || newPanY !== vpt[5]) {
+				this.canvas.absolutePan({
+					x: newPanX,
+					y: newPanY
+				});
+			}
+		},
+
 		setAllObjectSelectable(selectable) {
 			let flag = false
 			this.canvas.forEachObject(function(obj) {
@@ -1225,13 +3267,13 @@
 				} else {
 					_this.receiveMsg(newValue, oldValue)
 				}
-			}, 500)
+			}, 100)
 
 		},
 		async handleMsg(newValue, oldValue) {
-
 			const _this = this
 			try {
+				//console.log("handleMsg", newValue)
 				var data = JSON.parse(newValue);
 				for (var i = 0; i < data.length; i++) {
 					const item = data[i]
@@ -1243,47 +3285,32 @@
 							_this.editMode = false
 						}
 					}
-
 					if (item.method == "background") {
 						await _this.setBackground(item.param)
 					} else if (item.method == "update_agv_state") {
 						const info = item.param || {}
 						await _this.updateAgv(info)
-						// const workSpaceWidth = this.workSpace.width
-						// const workSpaceHeight = this.workSpace.height
-
-
+					} else if (item.method == "update_current_teaching") {
+						const info = item.param || []
+						await _this.updateCurrentTeaching(info)
 					} else if (item.method == "move_canvas") {
-						const info = item.param || {}
-						//	const scale = this.getAutoScale()
-						const zoom = this.canvas.getZoom();
-						const eleHeight = this.eleHeight - 150
+						const info2 = item.param || {}
 
-						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)
+						const pt = {
+							x: this.getXOnImg(info2.x),
+							y: this.getYOnImg(info2.y)
 						}
-						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));
+						this.ensurePointVisible(pt)
+
+
 
 					} else if (item.method == "add_station") {
 						const stationList = item.param || []
 						let list = _this.canvas.getObjects() || []
 						for (let i2 in stationList) {
 							const station = stationList[i2]
-							const curIndex = list.findIndex((a) => a.data?.stationID == station.stationID)
+							const curIndex = list.findIndex((a) => a.data?.stationID == station
+								.stationID)
 							if (curIndex < 0) {
 								await _this.addStation(station)
 							}
@@ -1294,28 +3321,42 @@
 						list = list.filter((a) => a.eleType == "station")
 						for (let i2 in stationList) {
 							const station = stationList[i2]
-							const curIndex = list.findIndex((a) => a.data.stationID == station.stationID)
+							const curIndex = list.findIndex((a) => a.data.stationID == station
+								.stationID)
 							if (curIndex < 0) {
 								await _this.addStation(station)
 							} else {
 								//	_this.canvas.remove(list[curIndex])
 								const curStationObj = list[curIndex]
-								const angle = station.angle * 180 / 3.14
+								const angle = station.angle * 180 / Math.PI
 								//const scale = this.getAutoScale()
-								const left = station.x //* scale
-								const top = station.y //* scale
-								console.log("update_station", curIndex, left,
-									top,
-									angle)
-								if (curStationObj.left != left || top != station.y || curStationObj.angle !=
-									angle) {
+								const left = this.getXOnImg(station.x) //* scale
+								const top = this.getYOnImg(station.y) //* scale
+								if (curStationObj.left != left || top != station.y ||
+									curStationObj.angle != angle) {
 									curStationObj.set({
 										left,
 										top,
 										angle,
 										data: station
 									})
+
+									curStationObj.tipObj.set({
+										text: `${station.name}`,
+										left: curStationObj.left,
+										top: curStationObj.top - curStationObj.height / 2 -
+											curStationObj
+											.tipObj.height / 2,
+									})
+									curStationObj.tipObj.setCoords()
 									curStationObj.setCoords()
+									if (_this.editObject == curStationObj) {
+										_this.editObject.tipObj.set({
+											visible: false
+										})
+										this.createOkCancelControl(curStationObj)
+									}
+
 								}
 							}
 
@@ -1324,46 +3365,39 @@
 					} else if (item.method == "remove_station") {
 						const stationList = item.param || []
 						let list = _this.canvas.getObjects() || []
+
 						list = list.filter((a) => a.eleType == "station")
 						for (let i2 in stationList) {
 							const station = stationList[i2]
-							const curIndex = list.findIndex((a) => a.data.stationID == station.stationID)
+							const curIndex = list.findIndex((a) => a.data.stationID == station
+								.stationID)
 							if (curIndex > -1) {
+								const tipObj = list[curIndex].tipObj
+								_this.closeOkCancelControl()
+								console.log("remove_station", list[curIndex])
 								_this.canvas.remove(list[curIndex])
+								if (tipObj) {
+									_this.canvas.remove(tipObj)
+
+								}
+
 							}
 						}
 
-					} else if (item.method == "select_station") {
-						const station = item.param || {}
-						let list = _this.canvas.getObjects() || []
-						list = list.filter((a) => a.eleType == "station")
-						_this.setAllObjectSelectable(false)
-
-						let listSel = []
-						const curIndex = list.findIndex((a) => a.data.stationID == station.stationID)
-						if (curIndex > -1) {
-							console.log("select_station", curIndex, list[curIndex])
-							listSel.push(list[curIndex])
-
-							list[curIndex].set({
-								selectable: true
-							})
-							_this.canvas.discardActiveObject();
-							// let sel = new fabric.ActiveSelection(listSel, {
-							// 	canvas: _this.canvas,
-							// });
-							_this.canvas.setActiveObject(list[curIndex]);
-						}
 					} else if (item.method == "edit_station_pos") {
 						const station = item.param || undefined
+
 						if (!station) {
 							if (_this.editObject) {
 								_this.editObject.set({
 									lockMovementX: true,
 									lockMovementY: true,
 								})
+
 								_this.editObject = null
 							}
+
+							_this.closeOkCancelControl()
 							continue
 						}
 						let list = _this.canvas.getObjects() || []
@@ -1387,9 +3421,187 @@
 							}
 							list[curIndex].set({
 								selectable: true,
-								lockEdit: false
+								lockEdit: false,
+								lockMovementX: false,
+								lockMovementY: false,
 							})
+							_this.editObject.tipObj.set({
+								left: _this.editObject.left,
+								top: _this.editObject.top - _this.editObject.height / 2 - _this
+									.editObject
+									.tipObj.height / 2,
+								visible: false
+							})
+							_this.createOkCancelControl(_this.editObject)
 						}
+
+					} else if (item.method == "mark_station") {
+						const stationIdList = item.param || []
+						let list2 = _this.canvas.getObjects() || []
+						let list = list2.filter((a) => a.eleType == "station")
+						const flag = stationIdList.length == 2
+
+						_this.showTeachingPath(_this.showTeachPathFlag ? true : false)
+						let objStation1
+						let objStation2
+						for (let i2 in list) {
+							const obj = list[i2]
+							const curIndex = stationIdList.findIndex((a) => a == obj.data.stationID)
+							if (curIndex > -1) {
+								await _this.setMarkStation(obj, true)
+								obj.set({
+									selectable: true,
+									opacity: 1
+								})
+								if (!objStation1)
+									objStation1 = obj
+								else
+									objStation2 = obj
+
+							} else {
+								await _this.setMarkStation(obj, false)
+								if (flag) {
+									obj.set({
+										selectable: false,
+										opacity: 0.5
+									})
+								} else {
+									obj.set({
+										selectable: true,
+										opacity: 1
+									})
+								}
+							}
+						}
+						list = list2.filter((a) => a.eleType == "station_teaching")
+						if (objStation1 && objStation2) {
+							const id =
+								`station_teaching_${objStation1.data.stationID}_${objStation2.data.stationID}`
+							const curIndex = list.findIndex((a) => a.id == id)
+							if (curIndex > -1) {
+								list[curIndex].set({
+									opacity: 1,
+									strokeDashArray: [3, 2],
+									strokeLineCap: 'butt',
+								})
+							}
+						}
+
+					} else if (item.method == "station_teaching") {
+						let list = _this.canvas.getObjects() || []
+						list.forEach((obj) => {
+							if (obj.eleType == "public_teaching" || obj.eleType ==
+								"station_teaching") {
+								obj.set({
+									hasControls: false,
+									selectable: false,
+								})
+							} else if (obj.eleType == "station" || obj.eleType ==
+								"station_tip") {
+								obj.set({
+									selectable: true,
+									opacity: 1
+								})
+							} else {
+								obj.set({
+									opacity: 0.5
+								})
+							}
+						})
+					} else if (item.method == "public_teaching") {
+						let list = _this.canvas.getObjects() || []
+						list.forEach((obj) => {
+							if (obj.eleType == "public_teaching" || obj.eleType ==
+								"station_teaching") {
+								obj.set({
+									hasControls: false,
+									selectable: false,
+								})
+							} else if (obj.eleType == "agv")
+								obj.set({
+									opacity: 1
+								})
+							else
+								obj.set({
+									opacity: 0.5
+								})
+						})
+					} else if (item.method == "teaching_finish") {
+						if (this.curTeachingObj) {
+							this.canvas.remove(this.curTeachingObj)
+							this.curTeachingObj = null
+						}
+						let list = _this.canvas.getObjects() || []
+						for (let i2 in list) {
+							const obj = list[i2]
+							obj.set({
+								selectable: 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") {
+						let list = _this.canvas.getObjects() || []
+						list = list.filter((a) => a.eleType == "public_teaching")
+						for (let i2 in list) {
+							this.canvas.remove(list[i2])
+						}
+						const teachingPathList = item.param || []
+						for (let i2 in teachingPathList) {
+							const teachingPath = teachingPathList[i2]
+							const id = `public_teaching_${teachingPath.name}`
+							await this.addTeachingPath(teachingPath, id, "public_teaching")
+						}
+					} 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 || []
+						for (let i2 in teachingPathList) {
+							const teachingPath = teachingPathList[i2]
+
+							const id = `station_teaching_${teachingPath.src_dst}`
+
+							await this.addTeachingPath(teachingPath, id, "station_teaching")
+						}
+					} else if (item.method == "show_teaching_path") {
+						_this.showTeachPathFlag = item.param.show
+						_this.showTeachingPath(item.param.show)
+
+					} else if (item.method == "remove_teaching_path") {
+						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 curIndex = list.findIndex((a) => a.id == id)
+
+							if (curIndex > -1) {
+								const obj = list[curIndex]
+								console.log(item.method, curIndex, id, obj)
+								_this.canvas.remove(obj)
+							}
+						} else if (item.param.mode == "Stations") {
+							list = list.filter((a) => a.eleType == "station_teaching")
+							const id = `station_teaching_${item.param.src_dst}`
+							const curIndex = list.findIndex((a) => a.id == id)
+							if (curIndex > -1) {
+
+								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 == "set_selectable") {
 						if (item.param)
@@ -1403,18 +3615,83 @@
 							})
 							_this.editObject = null
 						}
-					}
+					} else if (item.method == "add_wall") {
+						const wallList = item.param || []
+						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 || []
+						let list = _this.canvas.getObjects() || []
+						list = list.filter((a) => a.eleType == "virtual_wall")
+						for (let i2 in wallList) {
+							const wall = wallList[i2]
+							const curIndex = list.findIndex((a) => a.id == wall.id)
+							if (curIndex < 0) {
+								await _this.addVirtualWallShow(wall)
+							}
+						}
+					} else if (item.method == "add_region") {
+						const regionList = item.param || []
+						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 || []
+						let list = _this.canvas.getObjects() || []
+						list = list.filter((a) => a.eleType == "region")
+						for (let i2 in regionList) {
+							const region = regionList[i2]
+							const curIndex = list.findIndex((a) => a.id == region.id)
+							if (curIndex < 0) {
+								await _this.addRegionShow(region)
+							}
+						}
+
+					} else if (item.method == "remove_wall") {
+						const wallList = item.param || []
+						let list = _this.canvas.getObjects() || []
+						list = list.filter((a) => a.eleType == "virtual_wall")
+						for (let i2 in wallList) {
+							const wall = wallList[i2]
+							const curIndex = list.findIndex((a) => a.id == wall.id)
+							if (curIndex > -1) {
+								_this.removeVirtualWall(list[curIndex])
+							}
+						}
+					} else if (item.method == "remove_region") {
+						const regionList = item.param || []
+						let list = _this.canvas.getObjects() || []
+						list = list.filter((a) => a.eleType == "region")
+						for (let i2 in regionList) {
+							const region = regionList[i2]
+							const curIndex = list.findIndex((a) => a.id == region.id)
+							if (curIndex > -1) {
+								_this.removeRegion(list[curIndex])
+							}
+						}
+					} else if (item.method == "agv_laser") {
+						this.updateAgvLaser(item.param || {})
+					} else if (item.method == "point_cloud") {
+						this.updateLaserPoint(item.param || {})
+					}
 				}
 				_this.canvas.renderAll()
 			} catch (ex) {
 				console.log(ex)
+				this.showError(ex)
 			}
 		},
 		showError(ex) {
 			const type = typeof ex
 			if (type == "string") {
 				let tip = ex
+				console.log(ex)
 				plus.nativeUI.alert(tip, undefined, "閿欒");
 				return
 			}
@@ -1423,8 +3700,28 @@
 				exStr = ex
 
 			let tip = typeof ex.msg == "string" ? ex.msg : exStr
+			console.log(tip)
 			plus.nativeUI.alert(tip, undefined, "閿欒");
 		},
+		showToast(ex) {
+			const type = typeof ex
+			if (type == "string") {
+				let tip = ex
+				console.log(ex)
+				plus.nativeUI.toast(tip); // undefined, "閿欒"
+				return
+			}
+			let exStr = JSON.stringify(ex)
+			if (exStr == "{}")
+				exStr = ex
+
+			let tip = typeof ex.msg == "string" ? ex.msg : exStr
+			console.log(tip)
+			plus.nativeUI.toast(tip, {
+				duration: 'long',
+				verticalAlign: "center"
+			}); // undefined, "閿欒"
+		},
 
 	},
 }
\ No newline at end of file

--
Gitblit v1.9.1