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/task/infos/task-log-item.vue |    2 
 fonts/fonticon/fonticon.css        |    8 
 pages/map/edit.vue                 |    2 
 pages/teaching/index.vue           |   44 
 fonts/fonticon/fonts/fonticon.eot  |    0 
 pages/task/update.vue              |    6 
 pages/map/scene.vue                |  589 ++
 comm/utils.js                      |   26 
 pages/my/log.vue                   |   78 
 pages/map/teaching.vue             | 1957 ++++++++
 comm/wifi/ios.js                   |    2 
 pages/station/index.vue            |    6 
 fonts/fonticon/fonts/fonticon.ttf  |    0 
 pages/index/connect.vue            |   25 
 comm/line.js                       |   74 
 pages/map/js/ctx.js                | 3065 +++++++++++-
 api/request.js                     |   33 
 pages/map/task.vue                 |    2 
 pages/map/index2.vue               |  383 +
 pages/task/map-task.vue            |    2 
 pages/teaching/list.vue            |    4 
 pages/task/list.vue                |  159 
 pages/task/add.vue                 |    8 
 api/vehicle.js                     |  289 +
 images/che.png                     |    0 
 pages/index/detail.vue             |    4 
 /dev/null                          |    0 
 pages/task/infos/task-item.vue     |  223 
 pages/map/infos/scene-create.vue   |  342 +
 pages/task/log-list.vue            |   21 
 pages/map/index.vue                | 2574 ++++++++++
 pages/index/index.vue              |   91 
 App.vue                            |   17 
 fonts/fonticon/fonts/fonticon.woff |    0 
 pages/map/js/ctx-mini.js           | 3512 +++++++++++++++
 pages/index/backup.vue             |  413 +
 pages.json                         |   25 
 static/images/van.svg              |   16 
 manifest.json                      |    6 
 images/van.svg                     |   16 
 40 files changed, 13,283 insertions(+), 741 deletions(-)

diff --git a/App.vue b/App.vue
index 61d819a..83571f9 100644
--- a/App.vue
+++ b/App.vue
@@ -1,10 +1,21 @@
 <script>
-
 	export default {
 		globalData: {
 			platform: "",
 			deviceModel: "",
-			osVersion: 0
+			osVersion: 0,
+			sceneList: [],
+			curScene: {
+				scene_id: "",
+				proportion: 1,
+				img_proportion: 1,
+				max_x: 1,
+				max_y: 1,
+				min_x: 0,
+				min_y: 0,
+				img_x: 1,
+				img_y: 1,
+			}
 		},
 		onLaunch: function() {
 			console.log('App Launch')
@@ -21,6 +32,7 @@
 <style>
 	@import '@/fonts/fonticon/fonticon.css';
 	@import 'ant-design-vue/dist/reset.css';
+
 	/*姣忎釜椤甸潰鍏叡css */
 	page {
 		background-color: #fff;
@@ -48,6 +60,7 @@
 		white-space: nowrap;
 		text-overflow: ellipsis;
 	}
+
 	.uni-navbar-btn-text {
 		font-size: 32rpx;
 	}
diff --git a/api/request.js b/api/request.js
index 54ac8d1..dd77289 100644
--- a/api/request.js
+++ b/api/request.js
@@ -14,17 +14,22 @@
 		const list = session.getValue("request_log") || []
 
 		const res = typeof item.data == 'string' ? item.data : JSON.stringify(item.data)
-		if (res.length > 100 * 1024) {
-			const maxData = session.getValue("request_log_max_data") || {}
-			const key = `data${new Date().getTime()}`
-			maxData[key] = item.data
-			item.data_key = key
-			delete item.data
-			session.setValue("request_log_max_data", maxData)
+		if (res.length > 64 * 1024) {
+			// const maxData = session.getValue("request_log_max_data") || {}
+			// const key = `data${new Date().getTime()}`
+			// maxData[key] = item.data
+			// item.data_key = key
+			// delete item.data
+			// session.setValue("request_log_max_data", maxData)
+			 item.data  = res.length
+		}
+		if(item.url.indexOf("get_agv_state") > 0)
+		{
+			return
 		}
 		list.unshift(item)
 		if (list.length > 1000) {
-			const oldItem = list.shift()
+			const oldItem = list.pop()
 			if (oldItem.data_key) {
 				const maxData = session.getValue("request_log_max_data") || {}
 				delete maxData[oldItem.data_key]
@@ -41,7 +46,11 @@
 		};
 		options.method = options.method || this.common.method;
 		options.dataType = options.dataType || this.common.dataType;
-		console.log("url", options.url, options.data)
+		
+		if(options.url.indexOf("get_agv_state") < 0)
+		{
+			console.log("url", options.url, options.data)
+		}
 		return new Promise((resolve, reject) => {
 			const app = getApp()
 			uni.request({
@@ -51,7 +60,11 @@
 				method: options.method,
 				dataType: options.dataType,
 				success: (result) => {
-					console.log("result", result)
+					if(options.url.indexOf("get_agv_state") < 0)
+					{
+						console.log("result", result)
+					}
+					
 					const now = new Date()
 					const date = `${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}`
 					if (result.statusCode != 200) {
diff --git a/api/vehicle.js b/api/vehicle.js
index 37f4b36..f98fa91 100644
--- a/api/vehicle.js
+++ b/api/vehicle.js
@@ -130,36 +130,6 @@
 	})
 
 }
-/**
- * POST 5000/api/add_station
- * 鑾峰彇杞﹁締鍦板浘鍒楄〃
- * @param 
- *  stationID, //绔欏彴ID
- *	name, //绔欏彴鍚�- * @returns 
- */
-
-export const mapInfo = (ip) => {
-	// var header = {
-	// 	"Content-Type": "application/json;charset=UTF-8"
-	// };
-	// var url = `http://${ip}:4405/api/shell/version`;
-	// return http.request({
-	// 	method: "GET",
-	// 	url,
-	// 	header,
-	// })
-	return new Promise((resolve, reject) => {
-		return resolve(
-			[{
-				name: "鍦板浘1",
-				img: 'static/map.png',
-			}]
-		)
-	})
-
-
-}
 
 /**
  * POST 5000/api/add_station
@@ -236,7 +206,7 @@
 		data: {
 			stationID,
 			name,
-			x: Number(x),
+			x:Number(x),
 			y: Number(y),
 			angle
 		}
@@ -583,6 +553,26 @@
 }
 
 /**
+ * GET 5000/api/get_current_teaching_data
+ * 鑾峰彇璁板綍鐨勭ず鏁欐暟鎹�+ * @param 
+ * @returns 
+ */
+export const getCurrentTeachingData = (ip) => {
+	var header = {
+		"Content-Type": "application/json;charset=UTF-8"
+	};
+	var url = `http://${ip}:5000/api/get_current_teaching_data/`;
+	return http.request({
+		method: "GET",
+		url,
+		header,
+
+	})
+}
+
+
+/**
  * POST 5000/api/teaching_mode_flag
  * 寮�/鍋滄绀烘暀
  * @param 
@@ -591,6 +581,7 @@
  * name 绀烘暀鍚嶇О
  * teaching_flag # 1 寮�绀烘暀锛�0 鍋滄绀烘暀
  * main_road 1,# 1涓昏矾锛�鏀矾
+ * "bidirection": "1", # 1涓哄弻鍚戯紝0涓哄崟鍚戯紝鍋滄绀烘暀鐨勬椂鍊欎紶鍊煎嵆鍙紙 鍗�teaching_flag": 0锛�  * @returns 
  */
 export const teachingModeFlag = (ip, param) => {
@@ -603,6 +594,7 @@
 		name,
 		teaching_flag,
 		main_road,
+		bidirection
 	} = param
 
 	var url = `http://${ip}:5000/api/teaching_mode_flag/`;
@@ -615,7 +607,8 @@
 			src_dst,
 			name,
 			teaching_flag,
-			main_road
+			main_road,
+			bidirection
 		}
 
 	})
@@ -648,6 +641,29 @@
 }
 
 /**
+ * POST 5000/api/delete_teaching_mode_data
+ * 鍒犻櫎绀烘暀鏁版嵁
+ * @param 
+ * polygon_points [(0, 0), (0, 3), (3, 3), (3, 0)] #鍒犻櫎鍖哄煙鐨勫洓涓偣
+ * @returns 
+ */
+export const delTeachingModeData = (ip, polygonPoints) => {
+	var header = {
+		"Content-Type": "application/json;charset=UTF-8"
+	};
+	var url = `http://${ip}:5000/api/delete_teaching_mode_data/`;
+	return http.request({
+		method: "POST",
+		url,
+		header,
+		data: {
+			polygon_points: polygonPoints
+		}
+
+	})
+}
+
+/**
  * POST 5000/api/check_agv_location_distance_error
  * 鍒ゆ柇搴撲綅鍜岃溅璺濈鏄惁杩囧ぇ
  * @param 
@@ -668,4 +684,213 @@
 		}
 
 	})
+}
+
+/**
+ * POST 5000/api/create_scene
+ * 鍒涘缓/缁撴潫鍦烘櫙
+ * @param 
+ * scene_id  鍦烘櫙鍚嶇О
+ * scene_flag  1寮�鍦烘櫙锛�缁撴潫鍦烘櫙
+ * @returns 
+ */
+
+export const createScene = (ip, scene_id, scene_flag) => {
+	var header = {
+		"Content-Type": "application/json;charset=UTF-8"
+	};
+	var url = `http://${ip}:5000/api/create_scene/`;
+	return http.request({
+		method: "POST",
+		url,
+		header,
+		data: {
+			scene_id,
+			scene_flag
+		}
+
+	})
+}
+
+/**
+ * POST 5000/api/add_map
+ * 寮�鍦板浘鎿�+ * @param 
+ * scene_id  鍦烘櫙鍚嶇О
+ * map_type   #鏂板浘create鎵╁睍extend
+ * @returns 
+ */
+
+export const addMap = (ip, scene_id, map_type="create") => {
+	var header = {
+		"Content-Type": "application/json;charset=UTF-8"
+	};
+	var url = `http://${ip}:5000/api/add_map/`;
+	return http.request({
+		method: "POST",
+		url,
+		header,
+		data: {
+			scene_id,
+			map_type
+		}
+
+	})
+}
+
+/**
+ * POST 5000/api/stop_map
+ *缁撴潫鍦板浘鎿�+ * @param 
+ * scene_id  鍦烘櫙鍚嶇О
+ * @returns 
+ */
+
+export const stopMap = (ip, scene_id) => {
+	var header = {
+		"Content-Type": "application/json;charset=UTF-8"
+	};
+	var url = `http://${ip}:5000/api/stop_map/`;
+	return http.request({
+		method: "POST",
+		url,
+		header,
+		data: {
+			scene_id,
+		}
+
+	})
+}
+/**
+ * GET 6000/api/laser_data
+ * 杩斿洖婵�厜瀹炴椂鏁版嵁鍙婂簳鍥�+ * @param 
+ *  map_id  鍦板浘鏍囪瘑
+ * @returns 
+ */
+
+export const getMapLaserData = (ip,mapId) => {
+	var header = {
+		"Content-Type": "application/json;charset=UTF-8"
+	};
+	var url = `http://${ip}:5000/api/laser_data?map_id=${mapId?mapId:-1}`;
+	return http.request({
+		method: "GET",
+		url,
+		header,
+	})
+}
+
+/**
+ * POST 5000/api/update_scene_id
+ * 淇敼鍦烘櫙鍚嶇О
+ * @param 
+ * old_scene_id  闇�淇敼鍦烘櫙鍚嶇О
+ * new_scene_id  淇敼鍚庣殑鍦烘櫙鍚嶇О
+ * @returns 
+ */
+
+export const updateScene = (ip, old_scene_id, new_scene_id) => {
+	var header = {
+		"Content-Type": "application/json;charset=UTF-8"
+	};
+	var url = `http://${ip}:5000/api/update_scene_id/`;
+	return http.request({
+		method: "POST",
+		url,
+		header,
+		data: {
+			old_scene_id,
+			new_scene_id
+		}
+
+	})
+}
+
+/**
+ * POST 5000/api/delete_scene
+ * 鍒犻櫎鍦烘櫙
+ * @param 
+ * scene_id  鍦烘櫙鍚嶇О
+ * @returns 
+ */
+
+export const delScene = (ip, scene_id) => {
+	var header = {
+		"Content-Type": "application/json;charset=UTF-8"
+	};
+	var url = `http://${ip}:5000/api/delete_scene/`;
+	return http.request({
+		method: "POST",
+		url,
+		header,
+		data: {
+			scene_id,
+		}
+
+	})
+}
+
+/**
+ * POST 5000/api/handoff_scene
+ * 鍒囨崲鍦烘櫙
+ * @param 
+ * old_scene_id  褰撳墠鍦烘櫙鍚嶇О
+ * new_scene_id  闇�鍒囨崲鐨勫満鏅悕绉�+ * @returns 
+ */
+
+export const handoffScene = (ip, old_scene_id, new_scene_id) => {
+	var header = {
+		"Content-Type": "application/json;charset=UTF-8"
+	};
+	var url = `http://${ip}:5000/api/handoff_scene/`;
+	return http.request({
+		method: "POST",
+		url,
+		header,
+		data: {
+			old_scene_id,
+			new_scene_id
+		}
+
+	})
+}
+/**
+ * GET 5000/api/save_db_data
+ * 鑾峰彇DB鏁版嵁
+ * @param 
+ * @returns 
+ */
+
+export const getDBData = (ip) => {
+	var header = {
+		"Content-Type": "application/json;charset=UTF-8"
+	};
+	var url = `http://${ip}:5000/api/get_db_data/`;
+	return http.request({
+		method: "GET",
+		url,
+		header,
+	})
+}
+/**
+ * POST 5000/api/save_db_data
+ * 淇濆瓨DB鏁版嵁
+ * @param 
+ * @returns 
+ */
+
+export const saveDBData = (ip, data) => {
+	var header = {
+		"Content-Type": "application/json;charset=UTF-8"
+	};
+	var url = `http://${ip}:5000/api/save_db_data/`;
+	return http.request({
+		method: "POST",
+		url,
+		header,
+		data: {data}
+
+	})
 }
\ No newline at end of file
diff --git a/comm/line.js b/comm/line.js
new file mode 100644
index 0000000..261103a
--- /dev/null
+++ b/comm/line.js
@@ -0,0 +1,74 @@
+/**
+ * 妫�煡涓ゆ潯绾挎鏄惁鐩镐氦
+ * @param {Array} a1 绾挎1璧风偣 [x, y]
+ * @param {Array} a2 绾挎1缁堢偣 [x, y]
+ * @param {Array} b1 绾挎2璧风偣 [x, y]
+ * @param {Array} b2 绾挎2缁堢偣 [x, y]
+ * @returns {boolean} 鏄惁鐩镐氦
+ */
+function doLinesIntersect(a1, a2, b1, b2) {
+	// 璁$畻鍚戦噺鍙夌Н
+	const ccw = (p1, p2, p3) => (p2[0] - p1[0]) * (p3[1] - p1[1]) - (p2[1] - p1[1]) * (p3[0] - p1[0]);
+
+	// 妫�煡绾挎 a1a2 鍜�b1b2 鏄惁璺ㄧ珛
+	const ccw1 = ccw(a1, a2, b1);
+	const ccw2 = ccw(a1, a2, b2);
+	const ccw3 = ccw(b1, b2, a1);
+	const ccw4 = ccw(b1, b2, a2);
+
+	// 璺ㄧ珛瀹為獙 + 鍏辩嚎妫�煡
+	if (
+		(ccw1 * ccw2 < 0 && ccw3 * ccw4 < 0) || // 涓�埇鎯呭喌锛氳法绔�+		(ccw1 === 0 && isPointOnSegment(a1, a2, b1)) || // 鍏辩嚎鎯呭喌
+		(ccw2 === 0 && isPointOnSegment(a1, a2, b2)) ||
+		(ccw3 === 0 && isPointOnSegment(b1, b2, a1)) ||
+		(ccw4 === 0 && isPointOnSegment(b1, b2, a2))
+	) {
+		//console.log("doLinesIntersect ccw", ccw1, ccw2, ccw3, ccw4)
+		return true;
+	}
+	return false;
+}
+
+/**
+ * 妫�煡鐐规槸鍚﹀湪绾挎涓婏紙鍏辩嚎鏃朵娇鐢級
+ */
+function isPointOnSegment(p1, p2, p) {
+	const minX = Math.min(p1[0], p2[0]);
+	const maxX = Math.max(p1[0], p2[0]);
+	const minY = Math.min(p1[1], p2[1]);
+	const maxY = Math.max(p1[1], p2[1]);
+	return (
+		p[0] >= minX && p[0] <= maxX &&
+		p[1] >= minY && p[1] <= maxY
+	);
+}
+
+/**
+ * 妫�煡璺緞鏄惁鑷浉浜�+ * @param {Array} points 璺緞鐐规暟缁�[[x1, y1], [x2, y2], ...]
+ * @returns {boolean} 鏄惁鑷浉浜�+ */
+export function hasSelfIntersection(points) {
+	const len = points.length -1
+	if (len < 3) return false; // 鑷冲皯 4 涓偣鎵嶅彲鑳借嚜鐩镐氦
+
+	for (let i = 0; i < len; i++) {
+		//console.log("point", i, points[i])
+		for (let j = i + 2; j < len; j++) {
+			// 璺宠繃鐩搁偦绾挎锛堢浉閭荤嚎娈靛彧浼氬湪绔偣鐩镐氦锛屼笉绠楄嚜鐩镐氦锛�+			//console.log("point2", j, points[j])
+			if (i === (j + 1) % len|| j === (i + 1) % len) continue;
+
+			const a1 = points[i];
+			const a2 = points[i + 1];
+			const b1 = points[j];
+			const b2 = points[j + 1];
+		//	console.log("doLinesIntersect", a1, a2, b1, b2)
+			if (doLinesIntersect(a1, a2, b1, b2)) {
+				return true;
+			}
+		}
+	}
+	return false;
+}
\ No newline at end of file
diff --git a/comm/utils.js b/comm/utils.js
index 9e3ec1a..c3df92c 100644
--- a/comm/utils.js
+++ b/comm/utils.js
@@ -1,11 +1,12 @@
-
 var SESSION_SUFFIX = "diniu_app_"
-export function showModal(message, title = '鎻愮ず', iscancel = true) {
+export function showModal(message, title = '鎻愮ず', iscancel = true, confirmText = "鏄�, cancelText = "鍚�) {
 	return new Promise((resolve) => {
 		uni.showModal({
 			title: title,
 			content: message,
 			showCancel: iscancel,
+			confirmText,
+			cancelText,
 			success: function(res) {
 				if (res.confirm) {
 					resolve(true)
@@ -23,6 +24,20 @@
 		icon: icon ? icon : 'none'
 	})
 }
+export function showInfo(ex){
+	if( !ex)
+		return
+	 let tip =ex
+	 console.log(ex);
+	if( typeof ex !== "string" )
+	{
+		let exStr = JSON.stringify(ex)
+		if (exStr == "{}")
+			exStr = ex
+		tip = typeof ex.errMsg == "string" ? ex.errMsg :typeof ex.message == "string" ? ex.message: exStr
+	}
+	plus.nativeUI.toast(tip , {duration:"short"});
+}
 export function showLoading(title) {
 
 
@@ -32,6 +47,13 @@
 	})
 
 }
+export function hexToRGBA(hex, alpha) {
+	const r = parseInt(hex.slice(1, 3), 16);
+	const g = parseInt(hex.slice(3, 5), 16);
+	const b = parseInt(hex.slice(5, 7), 16);
+
+	return `rgba(${r}, ${g}, ${b}, ${alpha})`;
+}
 
 export function hideLoading() {
 
diff --git a/comm/wifi/ios.js b/comm/wifi/ios.js
index a8c4ea9..5f6e48c 100644
--- a/comm/wifi/ios.js
+++ b/comm/wifi/ios.js
@@ -13,6 +13,6 @@
 	},
 	getConnectionWifi() {
 
-		return ""
+		return {}
 	},
 }
\ No newline at end of file
diff --git a/fonts/fonticon/fonticon.css b/fonts/fonticon/fonticon.css
index 752b95c..df353f6 100644
--- a/fonts/fonticon/fonticon.css
+++ b/fonts/fonticon/fonticon.css
@@ -143,4 +143,12 @@
 
 .user-avatar:before {
 	content: "\ea21";
+}
+
+.zone:before {
+	content: "\ea22";
+}
+
+.expand:before {
+	content: "\ea23";
 }
\ No newline at end of file
diff --git a/fonts/fonticon/fonts/fonticon.eot b/fonts/fonticon/fonts/fonticon.eot
index 7a4f789..7bfa445 100644
--- a/fonts/fonticon/fonts/fonticon.eot
+++ b/fonts/fonticon/fonts/fonticon.eot
Binary files differ
diff --git a/fonts/fonticon/fonts/fonticon.ttf b/fonts/fonticon/fonts/fonticon.ttf
index ad7fccb..9c8b3b8 100644
--- a/fonts/fonticon/fonts/fonticon.ttf
+++ b/fonts/fonticon/fonts/fonticon.ttf
Binary files differ
diff --git a/fonts/fonticon/fonts/fonticon.woff b/fonts/fonticon/fonts/fonticon.woff
index 92ac957..0833553 100644
--- a/fonts/fonticon/fonts/fonticon.woff
+++ b/fonts/fonticon/fonts/fonticon.woff
Binary files differ
diff --git a/images/che.png b/images/che.png
new file mode 100644
index 0000000..8e998ce
--- /dev/null
+++ b/images/che.png
Binary files differ
diff --git a/images/image 11.png b/images/image 11.png
deleted file mode 100644
index 697cbd9..0000000
--- a/images/image 11.png
+++ /dev/null
Binary files differ
diff --git a/images/image 23.png b/images/image 23.png
deleted file mode 100644
index 32bf0bc..0000000
--- a/images/image 23.png
+++ /dev/null
Binary files differ
diff --git a/images/van.svg b/images/van.svg
index 356c12c..35d8433 100644
--- a/images/van.svg
+++ b/images/van.svg
@@ -1,10 +1,10 @@
 <svg width="40" height="84" viewBox="0 0 40 84" fill="none" xmlns="http://www.w3.org/2000/svg">
-<rect x="18" y="74" width="4" height="10" fill="#434343"/>
-<rect x="34" y="80" width="4" height="28" rx="2" transform="rotate(90 34 80)" fill="#434343"/>
-<rect x="4" width="12" height="62" rx="6" fill="#434343"/>
-<rect x="24" width="12" height="62" rx="6" fill="#434343"/>
-<rect x="8" y="8" width="4" height="8" rx="2" fill="#FA8C16"/>
-<rect x="28" y="8" width="4" height="8" rx="2" fill="#FA8C16"/>
-<rect y="48" width="40" height="28" rx="4" fill="#FA8C16"/>
-<circle cx="32" cy="56" r="4" fill="#434343"/>
+<rect x="22" y="10" width="4" height="10" transform="rotate(-180 22 10)" fill="#434343"/>
+<rect x="6" y="4" width="4" height="28" rx="2" transform="rotate(-90 6 4)" fill="#434343"/>
+<rect x="36" y="84" width="12" height="62" rx="6" transform="rotate(-180 36 84)" fill="#8A98A8"/>
+<rect x="16" y="84" width="12" height="62" rx="6" transform="rotate(-180 16 84)" fill="#8A98A8"/>
+<rect x="32" y="76" width="4" height="8" rx="2" transform="rotate(-180 32 76)" fill="#FA8C16"/>
+<rect x="12" y="76" width="4" height="8" rx="2" transform="rotate(-180 12 76)" fill="#FA8C16"/>
+<rect x="40" y="36" width="40" height="28" rx="4" transform="rotate(-180 40 36)" fill="#284962"/>
+<circle cx="8" cy="28" r="4" transform="rotate(-180 8 28)" fill="#FF4D4F"/>
 </svg>
diff --git a/manifest.json b/manifest.json
index 9655901..3f2ec68 100644
--- a/manifest.json
+++ b/manifest.json
@@ -1,9 +1,9 @@
 {
-    "name" : "鍦扮墰App",
+    "name" : "ES-GO",
     "appid" : "__UNI__C988375",
     "description" : "",
-    "versionName" : "1.1.2",
-    "versionCode" : 112,
+    "versionName" : "1.2.6",
+    "versionCode" : 126,
     "transformPx" : false,
     /* 5+App鐗规湁鐩稿叧 */
     "app-plus" : {
diff --git a/pages.json b/pages.json
index 54fdc2c..cf34dcc 100644
--- a/pages.json
+++ b/pages.json
@@ -45,6 +45,12 @@
 			}
 		},
 		{
+			"path": "pages/index/backup",
+			"style": {
+				"navigationBarTitleText": "鍦烘櫙澶囦唤"
+			}
+		},
+		{
 			"path": "pages/my/index",
 			"style": {
 				"navigationBarTitleText": "鎴戠殑"
@@ -89,7 +95,11 @@
 		{
 			"path": "pages/map/index",
 			"style": {
-				"navigationBarTitleText": "鍦板浘"
+				"navigationStyle": "custom", //鑷畾涔夋爣棰樻爮
+				"app-plus": {
+					"titleNView": false, //绂佺敤鍘熺敓瀵艰埅鏍�+					"softinputMode": "adjustResize"
+				}
 			}
 		},
 		{
@@ -102,6 +112,12 @@
 			"path": "pages/map/edit",
 			"style": {
 				"navigationBarTitleText": "缂栬緫鍦板浘"
+			}
+		},
+		{
+			"path": "pages/map/teaching",
+			"style": {
+				"navigationBarTitleText": "璺緞绀烘暀"
 			}
 		},
 
@@ -164,6 +180,13 @@
 			"style": {
 				"navigationBarTitleText": "绀烘暀璺嚎鍒楄〃"
 			}
+		},
+
+		{
+			"path": "pages/map/scene",
+			"style": {
+				"navigationBarTitleText": "鍦烘櫙鏋勫缓"
+			}
 		}
 
 	],
diff --git a/pages/index/backup.vue b/pages/index/backup.vue
new file mode 100644
index 0000000..6ca1706
--- /dev/null
+++ b/pages/index/backup.vue
@@ -0,0 +1,413 @@
+<template>
+	<view class="pages-backup">
+		<view class="switch-type">
+			<view class="switch-button-group">
+				<view class="switch-button" v-for="(page,index) in pageList" :key="page.name"
+					:class="curPageIndex ==index?'switch-button-checked':''" @click="onSelectPage(index)">
+					{{page.name}}
+				</view>
+
+			</view>
+		</view>
+		<swiper circular indicator-dots class="swiper" :current="curPageIndex" @change="changePageSwiper">
+			<swiper-item class="swiper-item">
+				<view class="list">
+					<view class="list-tip">{{pageList[0].title}}</view>
+					<view class="list-view" v-if="pageList[0].list.length > 0">
+						<view class="list-item" v-for="(item,index) in pageList[0].list" :key="index"
+							@click="clickSceneItem(index)">
+							<view class="list-name">{{item}}</view>
+
+							<uni-icons v-if="sceneIndex === index" type="checkmarkempty" size="20"
+								color="#1890FF"></uni-icons>
+						</view>
+					</view>
+					<view class="list-no-content" v-else>
+						<!-- <image class="img" src="/images/icon-park-outline_attention.svg" alt=" 鍥剧墖" mode="aspectFit" />-->
+						<uni-icons color="#ccc" type="info" size="128"></uni-icons>
+						<view class="space">娌℃湁鎵惧埌绗﹀悎鏉′欢鐨勫満鏅�/view>
+					</view>
+					<view class="list-tip">璇烽�鎷╀笂浼犲埌鎵嬫満绔殑鍦烘櫙</view>
+					<view>
+
+						<a-button :disabled="loading|| sceneIndex < 0" type="primary" class="button"
+							@click="clickSaveToLocal">涓婁紶鑷虫墜鏈虹</a-button>
+					</view>
+				</view>
+
+			</swiper-item>
+			<swiper-item class="swiper-item">
+				<view class="list">
+					<view class="ist-tip">{{pageList[1].title}}</view>
+					<view class="list-view" v-if="pageList[1].list.length > 0">
+						<view class="list-item" v-for="(item,index) in pageList[1].list" :key="index"
+							@click="clickSceneItem2(index)">
+							<view class="list-name">{{item.name}}</view>
+
+							<uni-icons v-if="sceneIndex2 === index" type="checkmarkempty" size="20"
+								color="#1890FF"></uni-icons>
+						</view>
+					</view>
+					<view class="list-no-content" v-else>
+						<!-- <image class="img" src="/images/icon-park-outline_attention.svg" alt=" 鍥剧墖" mode="aspectFit" />-->
+						<uni-icons color="#ccc" type="info" size="128"></uni-icons>
+						<view class="space">娌℃湁鎵惧埌绗﹀悎鏉′欢鐨勫満鏅�/view>
+					</view>
+					<view class="list-tip">璇烽�鎷╀笅杞藉埌杞﹁締绔殑鍦烘櫙</view>
+					<view>
+						<a-button :disabled="loading|| sceneIndex2< 0" type="primary" class="button"
+							@click="clickSaveToVehicle">涓嬭浇鑷宠溅杈嗙</a-button>
+					</view>
+				</view>
+
+			</swiper-item>
+
+		</swiper>
+
+	</view>
+</template>
+
+<script>
+	import {
+		session,
+		showToast,
+		showModal,
+		showInfo
+	} from "@/comm/utils.js"
+	import {
+		getAllScene,
+		handoffScene,
+		saveDBData,
+		getDBData
+	} from "@/api/vehicle.js"
+	import {
+		Button
+	} from 'antd-mobile-vue-next'
+	export default {
+		name: "PagesBackup",
+		components: {
+			'a-button': Button
+		},
+		data() {
+			return {
+				loading: false,
+				sceneIndex: -1,
+				sceneIndex2: -1,
+				curPageIndex: 0,
+				pageList: [{
+					name: "涓婁紶鍦烘櫙",
+					title: "ES-GO1鍦烘櫙鍒楄〃",
+					list: []
+				}, {
+					name: "涓嬭浇鍦烘櫙",
+					title: "鎵嬫満绔繚瀛樼殑鍦烘櫙鍒楄〃",
+					list: []
+				}]
+			}
+		},
+		computed: {},
+		onLoad(option) {
+			this.ip = option.ip || ""
+			this.loadData()
+		},
+		methods: {
+			setData(obj) {
+				let that = this;
+				let keys = [];
+				let val, data;
+
+				Object.keys(obj).forEach(function(key) {
+					keys = key.split(".");
+					val = obj[key];
+					data = that.$data;
+					keys.forEach(function(key2, index) {
+						if (index + 1 == keys.length) {
+							that.$set(data, key2, val);
+						} else {
+							if (!data[key2]) {
+								that.$set(data, key2, {});
+							}
+						}
+						data = data[key2];
+					});
+				});
+			},
+			async loadData() {
+				try {
+					this.pageList[0].list = await this.loadScene()
+					this.pageList[1].list = await this.loadLocalScene()
+					
+					console.log(this.pageList[1].list)
+				} catch (ex) {
+					this.showError(ex)
+				}
+			},
+			async loadScene() {
+				try {
+					const res = await getAllScene(this.ip) || []
+					const list = res?.sceneList || []
+					return list
+				} catch (ex) {
+					this.showError(ex)
+					return []
+				}
+			},
+			async loadLocalScene() {
+				try {
+					const list = session.getValue("scene_db") || []
+					return list
+				} catch (ex) {
+					this.showError(ex)
+					return []
+				}
+			},
+			changePageSwiper(evt) {
+				if (this.loading)
+					return
+				let index = evt.target.current || evt.detail.current;
+				this.setData({
+					curPageIndex: index
+				})
+			},
+
+			onSelectPage(index) {
+				if (this.loading)
+					return
+				this.setData({
+					curPageIndex: index
+				})
+			},
+			clickSceneItem(index) {
+				if (this.loading)
+					return
+				this.setData({
+					sceneIndex: index
+				})
+			},
+			clickSceneItem2(index) {
+				if (this.loading)
+					return
+				this.setData({
+					sceneIndex2: index
+				})
+			},
+
+			clickSaveToLocal() {
+				const _this = this
+				let list = this.pageList[1].list
+				if (list.length > 0) {
+					showModal(`涓婁紶璇ュ満鏅細鏇挎崲鎺夊綋鍓嶅凡涓婁紶鐨勫満鏅痐, "鏄惁瑕佽鐩栧満鏅紵").then(async (res) => {
+						if (res) {
+							_this.saveToLocal(this.sceneIndex)
+						}
+					})
+					
+					return
+				}
+				_this.saveToLocal(this.sceneIndex)
+			},
+			async saveToLocal(index) {
+				try {
+					this.setData({
+						loading: true
+					})
+					const scene = this.pageList[0].list[index]
+					await handoffScene(this.ip, "", scene)
+					session.setValue("scene_db", [])
+					const res = await getDBData(this.ip)
+					const list = [{
+						name: scene,
+						data: res ||[] 
+					}]
+					session.setValue("scene_db", list)
+					this.pageList[1].list = list
+					this.sceneIndex2 = -1
+					this.setData({
+						loading: false
+					})
+				} catch (ex) {
+					showInfo(ex)
+					this.setData({
+						loading: false
+					})
+
+				}
+
+			},
+			clickSaveToVehicle() {
+				const _this = this
+				const scene = this.pageList[1].list[this.sceneIndex2].name
+				let list = this.pageList[0].list
+				const curIndex = list.findIndex((a) => a == scene)
+				console.log(curIndex, scene,list)
+				if (curIndex > -1) {
+					showModal(`涓嬭浇璇ュ満鏅細鏇挎崲鎺夊凡涓嬭浇鐨勫悓鍚嶅満鏅�`, "鏄惁瑕佽鐩栧満鏅紵").then(async (res) => {
+						if (res) {
+							_this.saveToVehicle(this.sceneIndex2)
+						}
+					})
+					
+					return
+				}
+				_this.saveToVehicle(this.sceneIndex2)
+			},
+			async saveToVehicle(index) {
+				try {
+					this.setData({
+						loading: true
+					})
+					console.log(this.pageList[1].list[index])
+					const data = this.pageList[1].list[index].data
+					const scene = this.pageList[1].list[index].name
+					await saveDBData(this.ip, data)
+					this.pageList[0].list.push(scene)
+					this.setData({
+						loading: false
+					})
+				} catch (ex) {
+					showInfo(ex)
+					this.setData({
+						loading: false
+					})
+				}
+
+			},
+
+			showError(ex) {
+				let exStr = JSON.stringify(ex)
+				if (exStr == "{}")
+					exStr = ex
+				let tip = typeof ex.msg == "string" ? ex.msg : exStr
+				showModal(tip, "閿欒", false, "纭畾")
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.pages-backup {
+		display: flex;
+		width: 750rpx;
+		height: 100vh;
+		flex-direction: column;
+		background-color: #F5F5F5;
+
+		.switch-type {
+			width: calc(100% - 40rpx);
+			margin: 20rpx  20rpx 10rpx 20rpx;
+			align-items: center;
+			vertical-align: middle;
+
+			.switch-button-group {
+				height: 32px;
+				border: 1px solid #dfdfdf;
+				border-radius: 15rpx;
+				background-color: #dfdfdf;
+				display: flex;
+				flex: row;
+
+				.switch-button {
+					flex: 1;
+					border-radius: 15rpx;
+					color: #000000A5;
+					height: 32px;
+					line-height: 32px;
+					text-align: center;
+				}
+
+				.switch-button-checked {
+					box-shadow: 0px 2px 8px 0px #0000000C;
+					background-color: #fff;
+					color: #262626;
+				}
+
+			}
+
+		}
+
+		.swiper {
+
+			width: calc(100% - 20rpx);
+			margin: 10rpx;
+
+			display: flex;
+			flex: 1;
+		}
+
+		.list {
+			width: 100%;
+			height: 100%;
+			display: flex;
+			flex-direction: column;
+			padding: 0 10rpx;
+
+			.list-tip {
+				width: 100%;
+				padding: 15rpx;
+				color: #888;
+			}
+				
+			.list-view {
+				width: 100%;
+				//flex: 1;
+				border-radius: 10rpx;
+				overflow: auto;
+				background-color: #fff;
+				max-height:500rpx ;
+				.list-item {
+
+					border-bottom: 1px solid #ddd;
+					display: flex;
+					flex-direction: row;
+					padding:20rpx;
+
+					.list-name {
+						flex: 1;
+						margin-right: 20rpx;
+						font-size: 32rpx;
+						font-weight: 700;
+						padding:10rpx 0;
+					}
+				}
+
+				.list-item:last-child {
+					border-bottom: 0;
+					/* 鍙充笅瑙�*/
+				}
+			}
+
+			.list-no-content {
+				flex: 1;
+				margin-top: 50px;
+				padding: 20rpx 40rpx;
+				align-items: center;
+				text-align: center;
+				display: flex;
+				flex-direction: column;
+				font-size: 30rpx;
+				font-weight: 400;
+
+				.img {
+					width: 212rpx;
+					height: 212rpx;
+					color: #ddd;
+				}
+
+				.space {
+					margin-top: 20rpx;
+				}
+
+			}
+
+			.am-button {
+				border-radius: 90rpx;
+				height: 90rpx;
+				line-height: 60rpx;
+			}
+
+		}
+
+
+
+	}
+</style>
\ No newline at end of file
diff --git a/pages/index/connect.vue b/pages/index/connect.vue
index 9fcc16b..f7ff7a2 100644
--- a/pages/index/connect.vue
+++ b/pages/index/connect.vue
@@ -37,7 +37,7 @@
 		</view>
 		<view v-if="connectState == 3" class="button-group">
 			<a-button type="primary" plain="true" class="button" @click="clickTry">
-				{{reconnectFlag ? "閲嶆柊杩炴帴WiFi":"鍐嶈瘯"}}
+				{{reconnectFlag ? "閲嶆柊杩炴帴WiFi":"閲嶈瘯"}}
 			</a-button>
 			<a-button  type="ghost" class="button" @click="clickCancel">鍙栨秷
 			</a-button>
@@ -181,11 +181,11 @@
 							const result = res.result || ""
 							const arCode = result.split(";")
 							if (arCode.length != 3) {
-								showModal("鏃犳晥鐨勪簩缁寸爜锛�, "鎻愮ず")
+								this.showError("鏃犳晥鐨勪簩缁寸爜锛�)
 								return
 							}
 							if (!arCode[0].trim() || !arCode[0].trim()) {
-								showModal("鏃犳晥鐨勪簩缁寸爜锛�, "鎻愮ず")
+						this.showError("鏃犳晥鐨勪簩缁寸爜锛�)
 								return
 							}
 							that.ip = arCode[0]
@@ -219,7 +219,7 @@
 				}
 				this.connectState = 1
 
-				this.checkConnectVehicle()
+				this.checkConnectVehicle(60)
 			},
 			clickStudyMore() {
 				showToast("鍚庣画鍙戝紑涓�)
@@ -241,7 +241,7 @@
 								androidWifi.connectWifi(this.wifiSID,
 									this.wifiPassword
 								)
-								this.checkConnectVehicle()
+								this.checkConnectVehicle(60)
 								// 
 							} else {
 								if (first) {
@@ -261,7 +261,7 @@
 						}
 						this.connectState = 1
 
-						this.checkConnectVehicle()
+						this.checkConnectVehicle(60)
 
 					}
 				} else {
@@ -285,9 +285,11 @@
 			async connectVehicle() {
 				try {
 					const info = await mtBattery(this.ip)
+					console.log(this.ip)
 					this.connectState = 2
 					this.connectedSuccess()
 				} catch (ex) {
+					console.log("connectVehicle faile",this.ip,ex)
 					this.connectState = 3
 					// this.showError(ex)
 				}
@@ -336,8 +338,13 @@
 					})
 				}, 1000)
 			},
-			checkConnectVehicle() {
+			checkConnectVehicle(sec) {
 				if (this.unloadFlag) {
+					return
+				}
+				if(sec <= 0)
+				{
+					this.connectState = 3
 					return
 				}
 				checkIpLinkSuccess(this.ip).then((res) => {
@@ -349,7 +356,7 @@
 					}
 				}).catch((ex) => {
 					setTimeout(() => {
-						this.checkConnectVehicle()
+						this.checkConnectVehicle(sec- 7)
 					}, 2000)
 				})
 			},
@@ -358,7 +365,7 @@
 				if (exStr == "{}")
 					exStr = ex
 				let tip = typeof ex.msg == "string" ? ex.msg : exStr
-				showModal(tip, "閿欒", false)
+				showModal(tip, "閿欒", false,"纭畾")
 			},
 
 		}
diff --git a/pages/index/detail.vue b/pages/index/detail.vue
index 94db021..0413f43 100644
--- a/pages/index/detail.vue
+++ b/pages/index/detail.vue
@@ -133,7 +133,7 @@
 				}
 			},
 			clickDelete() {
-				showModal(`纭畾瑕佸垹闄よ澶団�${ this.vehicleInfo.name}鈥濆悧`, "璀﹀憡").then((res) => {
+				showModal(`纭畾瑕佸垹闄よ澶団�${ this.vehicleInfo.name}鈥濆悧`, "璀﹀憡",true,"纭畾","鍙栨秷").then((res) => {
 					if (res) {
 						const eventChannel = this.getOpenerEventChannel();
 						eventChannel.emit('delete_vehicle', this.vehicleInfo);
@@ -169,7 +169,7 @@
 				if (exStr == "{}")
 					exStr = ex
 				let tip = typeof ex.msg == "string" ? ex.msg : exStr
-				showModal(tip, "閿欒", false)
+				showModal(tip, "閿欒", false,"纭畾")
 			},
 
 		}
diff --git a/pages/index/index.vue b/pages/index/index.vue
index d3e179b..63dadb8 100644
--- a/pages/index/index.vue
+++ b/pages/index/index.vue
@@ -42,20 +42,20 @@
 					</view>
 				</view>
 				<image class="vehicle-img " :class="page.link_status ?'':'gray-image'" mode="aspectFit"
-					src="/images/image 11.png" alt="鍥剧墖" />
+					src="/images/che.png" alt="鍥剧墖" />
 				<view v-if="page.link_status" class="img-button-group">
-					<view type="primary" class="img-text-button" @click="clickToTask(index,page)">
-						<a-button class="img-button" color='primary' disabled>
-							<text class="ico task-list" />
-						</a-button>
-						<text>浠诲姟鍒楄〃</text>
-					</view>
 					<view type="primary" plain="true" class="img-text-button" @click="clickToMap(index,page)">
 						<a-button class="img-button" color='primary'>
 							<text class="ico map" />
 						</a-button>
-						<text>杩涘叆鍦板浘</text>
+						<text>鍦烘櫙鏋勫缓</text>
 
+					</view>
+					<view type="primary" class="img-text-button" @click="clickToTask(page)">
+						<a-button class="img-button" color='primary'>
+							<text class="ico task-list" />
+						</a-button>
+						<text>浠诲姟璁板綍</text>
 					</view>
 				</view>
 				<view v-else class="unlink-content">
@@ -126,6 +126,7 @@
 		showModal
 	} from "@/comm/utils.js"
 	import {
+		getAllScene,
 		mtBattery,
 		getAgvState,
 		checkIpLinkSuccess,
@@ -285,7 +286,7 @@
 
 			async loadVehicleBattery() {
 				try {
-					
+
 					const list = this.pageList
 					if (this.currentPage < list.length && this.currentPage > -1) {
 						const page = list[this.currentPage]
@@ -459,12 +460,7 @@
 				this.inputPopupValue = ""
 				this.$refs.refPopupInput.open()
 			},
-			clickToTask(index, page) {
-				showToast("璇ュ姛鑳借繕鍦ㄥ紑鍙戜腑")
-				// uni.navigateTo({
-				// 	url: `/pages/task/index?ip=${page.ip}`
-				// })
-			},
+
 			clickMore() {
 				if (this.currentPage < this.pageList.length) {
 					this.$refs.refPopupMenu.open("top")
@@ -504,19 +500,48 @@
 					}
 				})
 			},
+			async clickToMap(index, page) {
+				try {
 
-			clickToMap(index, page) {
-				const _this = this
+					const _this = this
+					// const res = await getAllScene(page.ip) || []
+					// const list = res?.sceneList || []
+					// if (list.length === 0) {
+					// 	uni.navigateTo({
+					// 		url: `/pages/scene/index?ip=${page.ip}`,
+					// 		events: {
+					// 			// 涓烘寚瀹氫簨浠舵坊鍔犱竴涓洃鍚櫒锛岃幏鍙栬鎵撳紑椤甸潰浼犻�鍒板綋鍓嶉〉闈㈢殑鏁版嵁
+					// 			finish: function() {
+					// 				_this.clickToMap(index, page)
+					// 			},
+					// 		}
+					// 	})
+					// 	return
+					// }
+					// getApp().globalData.sceneList = list
+
+					uni.navigateTo({
+						url: `/pages/map/index?ip=${page.ip}`,
+						events: {
+							// 涓烘寚瀹氫簨浠舵坊鍔犱竴涓洃鍚櫒锛岃幏鍙栬鎵撳紑椤甸潰浼犻�鍒板綋鍓嶉〉闈㈢殑鏁版嵁
+							check_connect: function(flag) {
+								page.link_status = flag
+								console.log(flag)
+								_this.checkPageConnected(index)
+							},
+						}
+					})
+				} catch (ex) {
+					this.showError(ex)
+				}
+
+			},
+			 clickToTask( page) {
+
 				uni.navigateTo({
-					url: `/pages/map/index?ip=${page.ip}`,
-					events: {
-						// 涓烘寚瀹氫簨浠舵坊鍔犱竴涓洃鍚櫒锛岃幏鍙栬鎵撳紑椤甸潰浼犻�鍒板綋鍓嶉〉闈㈢殑鏁版嵁
-						check_connect: function(flag) {
-							page.link_status = flag
-							_this.checkPageConnected(index)
-						},
-					}
+					url: `/pages/task/log-list?ip=${page.ip}`
 				})
+
 			},
 			isValidIP(ip) {
 				// IPv4 鍦板潃姝e垯琛ㄨ揪寮�@@ -569,7 +594,7 @@
 
 				} else if (item.text == "鍒犻櫎") {
 					const page = this.pageList[this.currentPage]
-					showModal(`纭畾瑕佸垹闄よ澶団�${ page.name}鈥濆悧`, "璀﹀憡").then((res) => {
+					showModal(`纭畾瑕佸垹闄よ澶団�${ page.name}鈥濆悧`, "璀﹀憡", true, "纭畾", "鍙栨秷").then((res) => {
 						if (res) {
 							const list = this.pageList
 							const curPage = this.currentPage
@@ -586,6 +611,12 @@
 
 						}
 					})
+				} else if (item.text == "澶囦唤") {
+					const page = this.pageList[this.currentPage]
+					uni.navigateTo({
+						
+						url: `/pages/index/backup?ip=${page.ip}`
+					})
 				}
 			},
 			showError(ex) {
@@ -593,7 +624,7 @@
 				if (exStr == "{}")
 					exStr = ex
 				let tip = typeof ex.msg == "string" ? ex.msg : exStr
-				showModal(tip, "閿欒", false)
+				showModal(tip, "閿欒", false, "纭畾")
 			},
 			closeMenu() {
 				this.$refs.refPopupMenu.close()
@@ -767,10 +798,10 @@
 
 		.vehicle-img {
 			margin-top: 20rpx;
-			margin-left: calc(50% - 280rpx);
+			margin-left: calc(50% - 325rpx);
 			display: block;
-			width: 560rpx;
-			height: 560rpx;
+			width: 650rpx;
+			height: 692rpx;
 
 		}
 
diff --git a/pages/map/edit.vue b/pages/map/edit.vue
index a6943d2..a88ebd4 100644
--- a/pages/map/edit.vue
+++ b/pages/map/edit.vue
@@ -148,7 +148,7 @@
 				if (exStr == "{}")
 					exStr = ex
 				let tip = typeof ex.msg == "string" ? ex.msg : exStr
-				showModal(tip, "閿欒", false)
+				showModal(tip, "閿欒", false,"纭畾")
 			},
 
 
diff --git a/pages/map/index.vue b/pages/map/index.vue
index 8a606f2..98e8b78 100644
--- a/pages/map/index.vue
+++ b/pages/map/index.vue
@@ -1,73 +1,489 @@
 <template>
 	<view class="pages-map">
+
 		<view class="view-content">
+			<uni-nav-bar :fixed="true" status-bar right-text="" left-text="" :leftWidth="24" :rightWidth="28"
+				:title="navigationBarTitle">
+				<view class="uni-navbar-container-inner">
+					<text class="uni-nav-bar-text">{{navigationBarTitle }}</text>
+					<image class="icon" v-if="sceneList.length > 1 && !mapOperationType" src="/images/Vector.svg"
+						alt="SVG 鍥剧墖" @click="clickShowMenu" />
+				</view>
+				<template v-slot:left>
+					<view>
+						<uni-icons type="left" size="24" @click="clickBack"></uni-icons>
+					</view>
+
+				</template>
+				<template v-slot:right>
+					<view v-if="sceneList.length > 0 && !mapOperationType">
+						<a @click="clickMore">
+							<uni-icons class="uni-panel-bar-icon" type="more-filled" size="24"></uni-icons>
+						</a>
+					</view>
+
+				</template>
+			</uni-nav-bar>
 			<view class="no-content" v-if="unlinked">
 				<image class="img" src="/images/image 25.png" alt=" 鍥剧墖" mode="aspectFit" />
 				<view class="title">杞﹁締杩炴帴澶辫触</view>
 				<view class="space">璇锋鏌ヤ綘鐨勭綉缁滆缃垨閲嶆柊鍔犺浇</view>
 			</view>
-			<view class="map-content" v-show="!unlinked">
-				<view class="text-button-group">
-					<a-button type="primary" class="button" @click="clickStationList">
-						绔欑偣鍒楄〃
-					</a-button>
-					<a-button type="primary" class="button" @click="clickTeachingList">
-						绀烘暀璺嚎鍒楄〃
-					</a-button>
+			<!-- 	<SceneCreateInfo v-else-if="mapOperationType =='scene_create'" class="map-content" :ip="vehicleIp"
+				v-model:opSceneType="opSceneType" @create-ok="onCreateSceneOk"></SceneCreateInfo> -->
+			<view class="map-content" v-show="mapOperationType !='scene_create'">
+				<view class="content">
+					<view class="fabric" :message="ctxDataStr" :change:message="ctx.receiveMsg" id="canvasMap"></view>
+					<view class="loading-overlay" v-if="bgLoading">
+						<view class="loading-content">
+							<view class="loading-spinner"></view>
+							<text>鍔犺浇涓�.. {{ bgProgressPercent }}%</text>
+						</view>
+					</view>
+					<view class="position-site" v-if="mapOperationStatus =='pos' " @click="clickPositionStation">
+						<text class="ico my-location-rounded"></text>
+
+						鑾峰彇鎼繍杞︿綅缃拰鏈濆悜
+					</view>
+					<view class="position" v-else @click="clickVehiclePosition">
+						<text class="ico my-location-rounded"></text>
+						<!-- <image class="img" src="/images/material-symbols_my-location-rounded.svg" alt="SVG 鍥剧墖" /> -->
+					</view>
+					<view v-if="this.mapOperationType ==''" class="teaching-path-show"
+						:class="showTeachingPathFlag?'selected':''" @click="clickShowTeachingPath">
+						<text class="ico layer"></text>
+
+					</view>
+					<view class="station-info"
+						:style="{'left':(stationViewPos.x - 120)+ 'px','top':(stationViewPos.y + stationViewPos.height/2 + 10)+'px'}"
+						v-if="mapOperationType =='edit_station'">
+
+						<view class=" left">
+							<view class="line">绔欑偣鍚嶇О锛�+							</view>
+							<view class="line"> 鍧愭爣(x,y)锛�+							</view>
+							<view class="line">鏈濆悜锛�+							</view>
+
+						</view>
+						<view class="right">
+							<view class="line"> {{stationEdit.name}}
+							</view>
+							<view class="line">
+								{{ Math.floor( stationEdit.x*1000)/1000}},{{ Math.floor( stationEdit.y*1000)/1000}}
+							</view>
+							<view class="line"> {{ Math.floor(180* stationEdit.angle/Math.PI)}}掳
+							</view>
+
+						</view>
+					</view>
+
 				</view>
 			</view>
 		</view>
-		<view class="bottom" v-if="!unlinked">
+		<view class="bottom">
+			<view class="bottom-content" v-if="mapOperationType =='edit_scene_name'">
+				<view class="tip">璇疯緭鍏ュ満鏅悕绉�/view>
+				<view class="name-input">
+					<input ref="refInputName" :focus="true" placeholder="璇疯緭鍏ュ満鏅悕绉� :value="sceneInputName"
+						@input="onInputName"></input>
+					<uni-icons class="clear" color="#ccc" type="clear" size="20" v-if="sceneInputName"
+						@click="clickClearName"></uni-icons>
+				</view>
+				<view class="text-button-group">
+					<a-button type="primary" class="button" :disabled="loading || sceneInputName.trim() == ''"
+						@click="clickNameOK">纭</a-button>
+					<a-button type="ghost" class="button" :disabled="loading" @click="clickNameCancel">鍙栨秷</a-button>
+				</view>
+			</view>
 
-			<view class="bottom-content">
+			<template
+				v-else-if="mapOperationType =='teaching_add_station' || mapOperationType =='add_station' || mapOperationType =='edit_station'">
+				<view class="bottom-content" v-if="mapOperationStatus =='pos' ">
+					<view class="tip">璋冭妭浣嶇疆</view>
+					<view class="row-group">
+						<view class="coordinate">
+							<text class="name">妯潗鏍�</text>
+							<input ref="refInputX" class="number-input" type="number" :value="stationEdit.x"
+								@input="onInputStationX" :maxlength="4"></input>
+							<uni-icons class="clear" color="#ccc" type="clear" size="20"
+								v-if="stationEdit.x && stationEdit.x!='0'" @click="clickClearStationX"></uni-icons>
+						</view>
+						<view class="coordinate">
+							<text class="name">绔栧潗鏍�</text>
+							<input ref="refInputX" class="number-input" type="number" :value="stationEdit.y"
+								@input="onInputStationY" :maxlength="4"></input>
+							<uni-icons class="clear" color="#ccc" type="clear" size="20"
+								v-if="stationEdit.y&& stationEdit.y!='0'" @click="clickClearStationY"></uni-icons>
+						</view>
+					</view>
+
+					<view class="tip">璋冭妭鏈濆悜</view>
+					<view class="angle-group">
+						<image class="img-angle" :src="angleSvg" alt="SVG 鍥剧墖" />
+						<image class="img-angle-pos" src="/images/Frame 153.svg" alt="SVG 鍥剧墖"
+							@touchstart="handleAngleTouchStart" @touchmove="handleAngleTouchMove" />
+					</view>
+				</view>
+				<view class="bottom-content" v-else>
+					<view class="tip">璇疯緭鍏ョ珯鐐瑰悕绉�/view>
+					<view class="name-input">
+						<input ref="refInputName" :focus="true" placeholder="杈撳叆绔欑偣鍚嶇О" :value="stationEdit.name"
+							@input="onInputStationName"></input>
+						<uni-icons class="clear" color="#ccc" type="clear" size="20" v-if="stationEdit.name"
+							@click="clickClearStationName"></uni-icons>
+					</view>
+					<view class="text-button-group">
+						<a-button class="button" :disabled="loading" @click="clickStationNameCancel">鍙栨秷</a-button>
+						<a-button type="primary" class="button" :disabled="loading || stationEdit.name.trim() == ''"
+							@click="clickStationNameOK">纭畾</a-button>
+					</view>
+				</view>
+
+			</template>
+			<view class="bottom-content" v-else-if="mapOperationType =='regiona_planning'">
+				<view class="img-button-group">
+					<view fill="none" class="button" @click="clickManualPlan">
+						<text class="ico conversion-path"></text>
+						<view class="text">浜哄伐瑙勫垝</view>
+					</view>
+
+					<view type="text" class="button " @click="clickVehicleTrajectoryPlan">
+						<text class="ico conversion-path"></text>
+						<view class="text ">杞﹁締杞ㄨ抗瑙勫垝</view>
+					</view>
+				</view>
+			</view>
+			<view class="bottom-content" v-else-if="mapOperationType =='vehicle_trajectory_planning'">
+				<template v-if="mapOperationStatus=='feasible_region' || mapOperationStatus=='prohibition_region'">
+					<view class="tip">鍖哄煙瑙勫垝</view>
+					<view>鍙寜浣忓悇绔偣璋冩暣铏氭嫙澧欎綅缃紝鎸夈�+銆戞寜閽彲娣诲姞椤剁偣锛屽尯鍩熼渶涓洪棴鍚堝浘褰�/view>
+				</template>
+				<view class="img-button-group" v-else>
+					<view fill="none" class="button" @click="clickPlanFeasibleRegion">
+						<!-- <text class="ico conversion-path"></text> -->
+						<uni-icons class="img" type="checkmarkempty" size="36" color="#1890FF"></uni-icons>
+						<view class="text">鍙鍖�/view>
+					</view>
+
+					<view type="text" class="button " @click="clickPlanProhibitionRegion">
+						<text class="ico placeholder-bold"></text>
+						<view class="text ">绂佽鍖�/view>
+					</view>
+				</view>
+			</view>
+
+			<view class="bottom-content" v-else-if="mapOperationType =='manual_planning'">
+				<template v-if="mapOperationStatus=='feasible_region' || mapOperationStatus=='prohibition_region'">
+					<view class="tip">鍖哄煙瑙勫垝</view>
+					<view>鍙寜浣忓悇绔偣璋冩暣铏氭嫙澧欎綅缃紝鎸夈�+銆戞寜閽彲娣诲姞椤剁偣锛屽尯鍩熼渶涓洪棴鍚堝浘褰�/view>
+				</template>
+				<template v-else-if="mapOperationStatus=='virtual_wall'">
+					<view class="tip">鍖哄煙瑙勫垝</view>
+					<view>鍙寜浣忎袱绔偣璋冩暣铏氭嫙澧欎綅缃�/view>
+
+				</template>
+				<view class="img-button-group" v-else>
+					<view fill="none" class="button" @click="clickPlanFeasibleRegion">
+						<!-- 	<text class="ico conversion-path"></text> -->
+						<uni-icons class="img" type="checkmarkempty" size="36" color="#1890FF"></uni-icons>
+						<view class="text">鍙鍖�/view>
+					</view>
+
+					<view type="text" class="button " @click="clickPlanProhibitionRegion">
+						<text class="ico placeholder-bold"></text>
+
+						<view class="text ">绂佽鍖�/view>
+					</view>
+					<view type="text" class="button " @click="clickPlanVirtualWall">
+						<text class="dashed-line"></text>
+						<view class="text ">铏氭嫙澧�/view>
+					</view>
+				</view>
+
+
+			</view>
+			<view class="bottom-content" v-else-if="mapOperationType =='scene_create'"></view>
+			<template v-else-if="mapOperationType =='public_teaching' ">
+				<view class="bottom-content" v-if="mapOperationStatus =='teaching'">
+					<view class="tip">璺緞璁板綍涓�..</view>
+					<view v-if="mapOperationType =='public_teaching'">
+						<view>姝e湪璁板綍鎼繍杞﹁杩涜矾寰勶紝鍙殢鏃跺垏鎹富璺�鏀矾绀烘暀锛屽畬鎴愬悗鐐规寜閽互缁撴潫绀烘暀銆傚湪绀烘暀杩囩▼涓彲浠ユ坊鍔犵珯鐐广�</view>
+						<view class="switch-type">
+							<view class="switch-button-group">
+								<view class="switch-button"
+									:class="teachingModeCur.main_road ==1?'switch-button-checked':''"
+									@click="onTeachingModeMainRoad(1)">涓昏矾绀烘暀</view>
+								<view class="switch-button "
+									:class="teachingModeCur.main_road ==0?'switch-button-checked':''"
+									@click="onTeachingModeMainRoad(0)">鏀矾绀烘暀</view>
+							</view>
+						</view>
+					</view>
+
+					<view v-else>
+						姝e湪璁板綍鎼繍杞﹁杩涜矾寰勶紝瀹屾垚鍚庣偣鎸夐挳浠ョ粨鏉熺ず鏁�+					</view>
+					<view class="text-button-group">
+						<a-button v-if="mapOperationType =='public_teaching'" type="ghost" class="button"
+							@click="clickAddStation">娣诲姞绔欑偣</a-button>
+						<a-button type="primary" class="button" @click="clickTeachingEnd">缁撴潫璁板綍</a-button>
+					</view>
+				</view>
+				<view class="bottom-content" v-else-if="mapOperationStatus =='end'">
+					<view class="tip">璺緞璁板綍瀹屾垚</view>
+					<view>
+						瑕佸皢璇ユ璺緞淇濆瓨涓虹ず鏁欒矾寰勫悧锛�+					</view>
+					<view class="text-button-group">
+						<a-button type="primary" class="button" :disabled="loading"
+							@click="clickTeachingSave">淇濆瓨涓虹ず鏁欒矾寰�/a-button>
+						<a-button type="ghost" class="button" :disabled="loading"
+							@click="clickTeachingReset">閲嶆柊璁板綍</a-button>
+					</view>
+				</view>
+				<view class="bottom-content" v-else-if="mapOperationStatus =='save'">
+					<view class="tip">绀烘暀瀹屾垚</view>
+					<view>
+						宸插皢璺緞淇濆瓨涓虹ず鏁欒矾寰�+					</view>
+					<view class="text-button-group">
+						<a-button type="primary" class="button" :disabled="loading"
+							@click="clickTeachingFinish">瀹屾垚</a-button>
+					</view>
+				</view>
+				<view class="bottom-content" v-else>
+					<view class="tip">鍗冲皢寮�鍏叡绀烘暀</view>
+					<view>
+						璇烽�鎷╄杩涜涓昏矾杩樻槸鏀矾绀烘暀锛岀偣銆愬紑濮嬭褰曘�鍚庡皢璁板綍鎼繍杞︾殑琛岃繘璺嚎浣滀负鍏叡绀烘暀璺嚎銆傚紑濮嬭褰曞悗鍙殢鏃跺垏鎹富璺�鏀矾绀烘暀銆�+					</view>
+					<view class="text-button-group">
+						<view class="switch-type">
+							<view class="switch-button-group">
+								<view class="switch-button"
+									:class="teachingModeCur.main_road ==1?'switch-button-checked':''"
+									@click="onTeachingModeMainRoad(1)">涓昏矾绀烘暀</view>
+								<view class="switch-button "
+									:class="teachingModeCur.main_road ==0?'switch-button-checked':''"
+									@click="onTeachingModeMainRoad(0)">鏀矾绀烘暀</view>
+							</view>
+						</view>
+						<a-button type="primary" class="button" :disabled="loading"
+							@click="clickTeachingStart">寮�璁板綍</a-button>
+					</view>
+				</view>
+
+			</template>
+			<view class="bottom-content" v-else-if="mapOperationType =='edit_map'">
+				<view class="img-button-group">
+					<view fill="none" class="button" @click="clickRename">
+						<text class="ico rename"></text>
+						<view class="text">閲嶅懡鍚�/view>
+					</view>
+
+					<view type="text" class="button " @click="clickRegionaPlan">
+						<text class="ico zone"></text>
+						<view class="text">鍖哄煙瑙勫垝</view>
+					</view>
+					<view fill="none" class="button" @click="clickExtendMap">
+						<text class="ico expand"></text>
+						<view class="text">鎵╁睍鍦板浘</view>
+					</view>
+					<!-- <view type="text" class="button" @click="clickMapEdit">
+						<text class="ico edit-line"></text>
+						<view class="text"> 缂栬緫鍦板浘</view>
+					</view> -->
+					<view type="text" class="button" @click="clickDelete">
+						<text class="ico delete-outline"></text>
+						<view class="text">鍒犻櫎鍦烘櫙</view>
+					</view>
+				</view>
+
+			</view>
+			<view class="bottom-content" v-else>
 				<view class="img-button-group">
 					<view fill="none" class="button" @click="clickMapStation">
 						<text class="ico location1"></text>
-						<view class="text"> 娣诲姞绔欑偣</view>
+						<view class="text">娣诲姞绔欑偣</view>
 					</view>
+					<view fill="none" class="button" @click="clickTeaching">
+						<text class="ico teach"></text>
+						<view class="text">璺緞绀烘暀</view>
+					</view>
+					<!-- <view type="text" class="button " @click="clickRegionaPlan">
+						<text class="ico layer"></text>
+						<view class="text">鍖哄煙瑙勫垝</view>
+					</view> -->
 					<view type="text" class="button" @click="clickMapEdit">
 						<text class="ico edit-line"></text>
 						<view class="text"> 缂栬緫鍦板浘</view>
 					</view>
 					<view type="text" class="button" @click="clickMapTask">
 						<text class="ico task-list"></text>
-						<view class="text">杞﹁締浠诲姟</view>
-
+						<view class="text">浠诲姟璁剧疆</view>
 					</view>
 				</view>
-
 			</view>
 		</view>
+		<view>
+			<uni-popup ref="refPopupMenu" background-color="transparent" maskBackgroundColor="rgba(0, 0, 0, 0.2)">
+				<view class="popup-content">
+					<view class="popup-content-menu "
+						:style="{'margin-top':menuPopup.top+'rpx','margin-left':menuPopup.left+'rpx'}">
+						<view class="popup-content-menu-item" v-for="(item,index) in menuPopup.list" :key="index"
+							@click="menuItemChange(item,menuPopup.type)">
+							{{item}}
+						</view>
+					</view>
+				</view>
+			</uni-popup>
+			<uni-popup ref="refPopupOperateStation" background-color="transparent"
+				maskBackgroundColor="rgba(0, 0, 0, 0.2)" @click="closePopuBtn">
+				<view class="popup-content">
+					<view class="popup-content-btn">
+						<view class="img-button-group">
+							<view fill="none" class="button" @click.stop="clickStationDelete">
+								<text class="ico delete-outline"></text>
+								<view class="text"> 鍒犻櫎绔欑偣</view>
+							</view>
+							<view type="text" class="button" @click.stop="clickStationPostion">
+								<text class="ico edit-line"></text>
+								<view class="text">璋冩暣浣嶇疆鏈濆悜</view>
+							</view>
+							<view type="text" class="button" @click.stop="clickStationRename">
+								<text class="ico rename"></text>
+								<view class="text">閲嶅懡鍚�/view>
 
+							</view>
+						</view>
+					</view>
+
+				</view>
+			</uni-popup>
+			<uni-popup ref="refPopupOperateTeaching" background-color="transparent"
+				maskBackgroundColor="rgba(0, 0, 0, 0.2)">
+				<view class="popup-content">
+					<view class="popup-content-btn">
+						<view class="img-button-group">
+
+							<view fill="none" class="button" @click.stop="clickTeachingEdit">
+								<text class="ico edit-line"></text>
+								<view class="text">缂栬緫</view>
+							</view>
+							<view fill="none" class="button" @click.stop="clickTeachingDelete">
+								<text class="ico red delete-outline "></text>
+								<view class="text">鍒犻櫎</view>
+							</view>
+
+						</view>
+					</view>
+
+				</view>
+			</uni-popup>
+
+		</view>
 	</view>
 </template>
+<script src="./js/ctx.js" module="ctx" lang="renderjs"></script>
+
+
 <script>
 	import {
 		showToast,
-		showModal
+		showModal,
+		session,
 	} from "@/comm/utils.js"
 	// import OIFabric from "@/components/oi-fabric/index.vue"
 	import {
 		Button
 	} from 'antd-mobile-vue-next'
+	import SceneCreateInfo from './infos/scene-create.vue'
+
 
 	import {
+		getAllScene,
+		getMapUrl,
+		handoffScene,
+		updateScene,
+		delScene,
+		stations,
 		getAgvState,
+		addStation,
+		updateStation,
+		delStation,
+		getTeachingMode,
+		teachingModeFlag,
+		getCurrentTeachingData,
+		delTeachingMode,
+		delTeachingModeData,
+		checkAgvLocationDistanceError,
 	} from "@/api/vehicle.js"
 	export default {
 		name: "PagesMap",
 		components: {
-			'a-button': Button
+			'a-button': Button,
+			SceneCreateInfo
 		},
 		data() {
 			return {
+				loading: false,
 				navigationBarTitle: "",
+				opSceneType: "",
 				vehicleIp: "",
+				sceneId: "",
+				mapShowAgv: false,
+				menuPopup: {
+					type: "",
+					list: []
+				},
+				sceneList: [],
+				ctxDataStr: "[]",
+				mapOperationType: "",
+				mapOperationStatus: "",
+				mapOperationStatus: "",
+				teachingMode: {},
+				teachingModeCur: {},
+
+				stationViewPos: {
+					x: 0,
+					y: 0,
+					width: 0,
+					height: 0
+				},
+				stationEdit: {
+					stationID: "",
+					name: "",
+					x: 0,
+					y: 0,
+					angle: 0
+				},
+				sceneInputName: "",
+				showClearName: false,
 				stationList: [],
-				unlinked: true
+				angleSvg: "/static/images/angle0.svg",
+				windowWidth: 375,
+				bgProgressPercent: 0,
+				bgLoading: false,
+				isMapEdit: false,
+				wallList: [],
+				regionList: [],
+				unlinked: false,
+				showTeachingPathFlag: false,
+				curMapInfo: {
+					proportion: 1,
+					img_proportion: 1,
+					max_x: 1,
+					max_y: 1,
+					min_x: 0,
+					min_y: 0,
+					img_x: 1,
+					img_y: 1
+				},
 			}
 		},
 		computed: {
+
 			getMaxStationNo() {
 				let num = 0
 				this.stationList.forEach((item) => {
@@ -79,14 +495,50 @@
 			},
 		},
 		watch: {
-
+			mapOperationType(val) {
+				let name = this.sceneId || "鍦板浘"
+				if (val == "add_station" || val == "teaching_add_station") {
+					name = "娣诲姞绔欑偣"
+				} else if (val == "edit_station") {
+					name = "缂栬緫绔欑偣"
+				} else if (val == "scene_create") {
+					name = "鏋勫缓鍦烘櫙"
+				} else if (val == "edit_map") {
+					name = "缂栬緫鍦板浘"
+				} else if (val == "manual_planning") {
+					name = "浜哄伐瑙勫垝"
+				} else if (val == "vehicle_trajectory_planning") {
+					name = "杞﹁締杞ㄨ抗瑙勫垝"
+				} else if (val == "public_teaching") {
+					name = "璺緞绀烘暀"
+				}
+				this.setData({
+					navigationBarTitle: name
+				})
+			}
 		},
 		onLoad(option) {
 			const _this = this
+			console.log(option)
 			this.vehicleIp = option.ip || ""
+			this.isPageVisible = true
+			uni.getSystemInfo({
+				success(e) {
+					_this.windowWidth = e.windowWidth
+				},
 
+			})
 			this.loadData()
 
+		},
+		onShow() {
+			this.isPageVisible = true
+		},
+		onHide() {
+			this.isPageVisible = false
+		},
+		onUnload() {
+			this.isPageVisible = false
 		},
 		methods: {
 			setData(obj) {
@@ -112,58 +564,1660 @@
 			},
 			async loadData() {
 				try {
-					const info = await getAgvState(this.vehicleIp)
-					this.setData({
-						unlinked: false
-					})
+					await this.loadSceneList()
+					if (this.sceneList.length > 0) {
+						this.mapOperationType = ''
+						const scene = this.sceneList[0]
+						this.changeMap(scene)
+					}
+					this.refreshAgvPosition()
 				} catch (ex) {
 					this.setData({
 						unlinked: true
 					})
-
+					this.showError(ex)
 				}
 			},
+
 			clickShowMenu() {
+				this.menuPopup = {
+					type: "scene",
+					list: this.sceneList,
+					left: 225,
+					top: 140
+				}
 				this.$refs.refPopupMenu.open("top")
 			},
-			clickBack() {
-				uni.navigateBack({
-					delta: 1
-				})
+			clickMore() {
+				this.menuPopup = {
+					type: "menu",
+					list: ["鏋勫缓鍦烘櫙", "鍦板浘鎵╁睍", "閲嶅懡鍚�, "鍒犻櫎"],
+					left: 450,
+					top: 140
+				}
+				this.$refs.refPopupMenu.open("top")
+			},
+
+			async clickBack() {
+				if (this.mapOperationType == "scene_create") {
+					if (this.opSceneType == "scan") {
+						showModal("宸叉瀯寤哄満鏅皢浼氳鍒犻櫎", "鏄惁涓柇鍦烘櫙鏋勫缓锛�).then((res) => {
+							if (res) {
+								if (this.sceneList.length == 0) {
+									const eventChannel = this.getOpenerEventChannel();
+									eventChannel.emit('check_connect', !this.unlinked);
+									uni.navigateBack({
+										delta: 1
+									})
+								} else {
+									this.opSceneType = ""
+									this.mapOperationType = ""
+								}
+
+							}
+						})
+					} else {
+						if (this.opSceneType != "") {
+							await this.loadSceneList()
+							if (this.sceneList.length == 0) {
+
+								const eventChannel = this.getOpenerEventChannel();
+								eventChannel.emit('check_connect', !this.unlinked);
+								uni.navigateBack({
+									delta: 1
+								})
+							}
+						} else {
+							const eventChannel = this.getOpenerEventChannel();
+							eventChannel.emit('check_connect', !this.unlinked);
+							uni.navigateBack({
+								delta: 1
+							})
+						}
+					}
+				} else if (this.mapOperationType == "edit_map") {
+					this.mapOperationType = ""
+				} else if (this.mapOperationType == "regiona_planning") {
+					this.mapOperationType = "edit_map"
+				} else if (this.mapOperationType == "vehicle_trajectory_planning") {
+					if (this.mapOperationStatus == "feasible_region" || this.mapOperationStatus ==
+						"prohibition_region") {
+
+						showModal("姝e湪杩涜鍖哄煙瑙勫垝銆�, "鏄惁瑕侀�鍑哄尯鍩熻鍒掞紵").then((res) => {
+							if (res) {
+								this.mapOperationStatus = ""
+							}
+						})
+
+					} else {
+						this.mapOperationType = "regiona_planning"
+					}
+					return
+				} else if (this.mapOperationType == "manual_planning") {
+					if (this.mapOperationStatus == "virtual_wall" ||
+						this.mapOperationStatus == "feasible_region" ||
+						this.mapOperationStatus == "prohibition_region") {
+						showModal("姝e湪杩涜鍖哄煙瑙勫垝銆�, "鏄惁瑕侀�鍑哄尯鍩熻鍒掞紵").then((res) => {
+							if (res) {
+								if (this.mapOperationStatus == "virtual_wall") {
+									this.ctxDataStr = JSON.stringify([{
+										method: "remove_wall",
+										param: [this.wallEdit]
+									}])
+									this.wallEdit = {}
+								} else {
+									this.ctxDataStr = JSON.stringify([{
+										method: "remove_region",
+										param: [this.regionEdit]
+									}])
+									this.regionEdit = {}
+								}
+								this.mapOperationStatus = ""
+							}
+						})
+					} else {
+						this.mapOperationType = "regiona_planning"
+					}
+					return
+				} else if (this.mapOperationType == "edit_station") {
+					this.mapOperationType = ""
+					if (this.mapOperationStatus == "pos") {
+						this.ctxDataStr = JSON.stringify([{
+							method: "edit_station_pos",
+							param: undefined
+						}])
+					}
+					this.$refs.refPopupOperateStation.open("bottom")
+				} else if (this.mapOperationType == "add_station") {
+					this.mapOperationType = ""
+					if (this.stationEdit.stationID) {
+						this.ctxDataStr = JSON.stringify([{
+							method: "remove_station",
+							param: [this.stationEdit]
+						}, {
+							method: "set_selectable",
+							param: true
+						}])
+					}
+
+
+				} else if (this.mapOperationType == "teaching_add_station") {
+					this.mapOperationType = "public_teaching"
+					this.mapOperationStatus = "teaching"
+					if (this.stationEdit.stationID) {
+						this.ctxDataStr = JSON.stringify([{
+							method: "remove_station",
+							param: [this.stationEdit]
+						}])
+					}
+
+
+				} else if (this.mapOperationType == "public_teaching") {
+
+
+					if (this.mapOperationStatus) {
+
+						showModal("宸茶褰曠殑璺緞灏嗕細琚垹闄ゃ�", "鏄惁瑕侀�鍑虹ず鏁欙紵").then(async (res) => {
+							if (res) {
+								if (mapOperationStatus == 'end' || mapOperationStatus == 'save') {
+									try {
+										await delTeachingMode(this.vehicleIp, [_this.teachingModeCur])
+									} catch (ex) {
+										this.showError(ex)
+									}
+
+								}
+								this.ctxDataStr = JSON.stringify([{
+									method: "teaching_finish",
+								}])
+
+								this.mapOperationType = ""
+							}
+						})
+					} else {
+						this.ctxDataStr = JSON.stringify([{
+							method: "teaching_finish",
+						}])
+
+						this.mapOperationType = ""
+					}
+
+				} else {
+					// const eventChannel = this.getOpenerEventChannel();
+					// eventChannel.emit('udapte_station', this.unlinked);
+					// uni.navigateBack({
+					// 	delta: 1
+					// })
+					const eventChannel = this.getOpenerEventChannel();
+					eventChannel.emit('check_connect', !this.unlinked);
+					uni.navigateBack({
+						delta: 1
+					})
+				}
 
 			},
-			clickStationList() {
-				uni.navigateTo({
-					url: `/pages/station/index?ip=${this.vehicleIp}`
-				})
+			async onCreateSceneOk(name) {
+				this.opSceneType = ""
+				this.mapOperationType = ""
+				await this.loadSceneList()
+				if (this.sceneList.length == 1) {
+					this.changeMap(this.sceneList[0])
+				} else if (this.sceneList.length > 1) {
+					this.changeMap(name)
+				}
 			},
-			clickTeachingList() {
-				uni.navigateTo({
-					url: `/pages/teaching/list?ip=${this.vehicleIp}`
+			clickStartConstructScene() {
+
+			},
+			clickDownloadScene() {
+
+			},
+			menuItemChange(item, type) {
+				const _this = this
+				this.$refs.refPopupMenu.close()
+				if (type == "scene") {
+					this.changeMap(item)
+				} else if (type == "menu") {
+					if (item == "鏋勫缓鍦烘櫙") {
+						// this.mapOperationType = 'scene_create'
+						// this.opSceneType = 'add_name'
+						uni.navigateTo({
+							url: `/pages/map/scene?ip=${this.vehicleIp}`,
+							events: {
+								// 涓烘寚瀹氫簨浠舵坊鍔犱竴涓洃鍚櫒锛岃幏鍙栬鎵撳紑椤甸潰浼犻�鍒板綋鍓嶉〉闈㈢殑鏁版嵁
+								create_finish: function(data) {
+									_this.onCreateSceneOk(data)
+								},
+							}
+						})
+					} else if (item == "鍦板浘鎵╁睍") {
+						this.clickExtendMap()
+					} else if (item == "閲嶅懡鍚�) {
+						this.clickRename()
+					} else if (item == "鍒犻櫎") {
+						this.clickDelete()
+					}
+				}
+
+
+			},
+			async loadAgvState() {
+				try {
+					const info = await getAgvState(this.vehicleIp)
+					return info
+				} catch (ex) {
+					this.showError(ex)
+					return {}
+				}
+			},
+			async loadCurrentTeachingData() {
+				try {
+					const paths = await getCurrentTeachingData(this.vehicleIp) || []
+					return paths
+				} catch (ex) {
+					this.showError(ex)
+					return []
+				}
+			},
+
+			async loadStations() {
+				try {
+					const info = await stations(this.vehicleIp)
+					return info.station_list || []
+				} catch (ex) {
+					this.showError(ex)
+					return []
+				}
+			},
+			async loadMapInfo(id) {
+				try {
+					const info = await getMapUrl(this.vehicleIp, id)
+					return info
+				} catch (ex) {
+					this.showError(ex)
+					return {}
+				}
+			},
+			async changeMap(id) {
+
+				this.mapShowAgv = false
+				this.setData({
+					sceneId: id,
+					navigationBarTitle: id || "鍦板浘"
 				})
+				try {
+					uni.showLoading({
+						title: "鍒囨崲鍦烘櫙涓�
+					})
+					this.setData({
+						unlinked: false
+					})
+					await handoffScene(this.vehicleIp, "", id)
+				} catch (ex) {
+					this.setData({
+						unlinked: true
+					})
+					return
+				} finally {
+					uni.hideLoading()
+				}
+				try {
+					this.setData({
+						bgProgressPercent: 0,
+					})
+					const infoAgv = await this.loadAgvState()
+					const stationLst = await this.loadStations()
+
+					const infoMap = await this.loadMapInfo(id)
+					this.curMapInfo = {
+						proportion: infoMap.proportion || 1,
+						img_proportion: infoMap.img_proportion || 1,
+						max_x: infoMap.max_x || 1,
+						max_y: infoMap.max_y || 1,
+						min_x: infoMap.min_x || 0,
+						min_y: infoMap.min_y || 0,
+						img_x: infoMap.img_x || 1,
+						img_y: infoMap.img_y || 1
+					}
+					// getApp().globalData.curScene = infoMap
+					this.setData({
+						bgProgressPercent: 30,
+						bgLoading: infoMap.filedata ? true : false
+					})
+					this.stationList = stationLst
+					this.teachingMode = await this.loadTeachingMode()
+					this.ctxDataStr = JSON.stringify([{
+							method: "init",
+							param: {
+								editMode: true
+							}
+						}, {
+							method: "background",
+							param: infoMap
+						},
+						{
+							method: "update_agv_state",
+							param: infoAgv
+						},
+						{
+							method: "add_station",
+							param: stationLst
+						},
+						{
+							method: "public_teaching_path",
+							param: this.teachingMode.Public || []
+
+						},
+						{
+							method: "station_teaching_path",
+							param: this.teachingMode.Stations || []
+
+						},
+						{
+							method: "move_canvas",
+							param: {
+								x: infoAgv.x,
+								y: infoAgv.y
+							}
+						}
+					])
+					this.mapShowAgv = true
+				} catch (ex) {
+					this.setData({
+						bgProgressPercent: 0,
+						bgLoading: false
+					})
+					this.showError(ex)
+				}
+			},
+
+			receiveRenderData(param) {
+				console.log('鎺ユ敹鍒拌鍥惧眰鐨勬暟鎹細', param);
+				if (param.method == "set_backgroud_progress") {
+					if (param.type == "start") {
+						this.setData({
+							bgProgressPercent: 50,
+							bgLoading: true
+						})
+					} else if (param.type == "progress") {
+						this.setData({
+							bgProgressPercent: 50 + (param.percent || 0) / 2
+						})
+					} else if (param.type == "end") {
+						this.setData({
+							bgProgressPercent: 100,
+							bgLoading: true
+						})
+						setTimeout(() => {
+							this.setData({
+								bgLoading: false
+							})
+						}, 500)
+
+					} else if (param.type == "error") {
+						this.setData({
+							bgProgressPercent: 0,
+							bgLoading: false
+						})
+					}
+
+				} else if (param.method == "edit_station") {
+					this.stationEdit = param.station
+					this.stationViewPos = {
+						x: param.view?.x || 0,
+						y: param.view?.y || 0,
+						width: param.view?.width || 0,
+						height: param.view?.height || 0,
+					}
+					this.mapOperationType = ""
+
+					this.$refs.refPopupOperateStation.open("bottom")
+
+				} else if (param.method == "update_station") {
+					this.stationEdit.stationID = param.station?.stationID
+					this.stationEdit.angle = param.station.angle
+					this.stationEdit.x = param.station.x
+					this.stationEdit.y = param.station.y
+					this.stationViewPos.x = param.view?.x || 0
+					this.stationViewPos.y = param.view?.y || 0
+				} else if (param.method == "edit_finish") {
+
+					if (param.cmd == "ok") {
+
+						if (param.type == "station") {
+							this.mapOperationStatus = ""
+							this.stationEdit.stationID = param.data?.stationID
+							this.stationEdit.name = param.data?.name
+							this.stationEdit.angle = param.data?.angle
+							this.stationEdit.x = param.data?.x
+							this.stationEdit.y = param.data?.y
+
+							const angle = this.getStantardAngle(this.stationEdit.angle * 180 / Math.PI)
+
+							this.angleSvg = `/static/images/angle${angle}.svg`
+							this.clickStationPositonOk()
+						} else if (param.type == "edit_teaching") {
+							// console.log(param.type,JSON.stringify(param.data))
+							this.removeTeachingModeData(param.data)
+							this.ctxDataStr = JSON.stringify([])
+							this.mapOperationType = ""
+						} else if (param.type == "virtual_wall") {
+							this.wallEdit = param.data
+							this.wallList.push(param.data)
+							this.mapOperationStatus = ""
+
+						} else if (param.type == "region") {
+							this.regionEdit = param.data
+							this.regionList.push(param.data)
+							this.mapOperationStatus = ""
+						}
+
+					} else if (param.cmd == "cancel") {
+
+						if (param.type == "edit_teaching") {
+							this.ctxDataStr = JSON.stringify([])
+							this.mapOperationType = ""
+						} else if (this.mapOperationType == "manual_planning") {
+							if (this.mapOperationStatus == "virtual_wall") {
+								this.ctxDataStr = JSON.stringify([{
+									method: "remove_wall",
+									param: [this.wallEdit]
+								}])
+								this.wallEdit = {}
+							} else {
+								this.ctxDataStr = JSON.stringify([{
+									method: "remove_region",
+									param: [this.regionEdit]
+								}])
+								this.regionEdit = {}
+							}
+							this.mapOperationStatus = ""
+						} else if (this.mapOperationType == "teaching_add_station") {
+							this.ctxDataStr = JSON.stringify([{
+								method: "remove_station",
+								param: [{
+									stationID: this.stationEdit.stationID
+								}],
+							}])
+							this.stationEdit.stationID = ""
+							this.mapOperationType = "public_teaching"
+							this.mapOperationStatus = "teaching"
+
+						} else if (this.mapOperationType == "edit_station") {
+							this.mapOperationType = ""
+							this.mapOperationStatus = ""
+							if (this.mapOperationStatus == "pos") {
+								this.ctxDataStr = JSON.stringify([{
+									method: "edit_station_pos",
+									param: undefined
+								}])
+							}
+							this.$refs.refPopupOperateStation.open("bottom")
+						} else if (this.mapOperationType == "add_station") {
+							this.mapOperationType = ""
+							this.mapOperationStatus = ""
+							this.ctxDataStr = JSON.stringify([{
+								method: "remove_station",
+								param: [this.stationEdit]
+							}, {
+								method: "set_selectable",
+								param: true
+							}])
+						}
+
+					}
+				} else if (param.method == "select_teaching_path") {
+					console.log("point", param.point)
+					if (param.type == "station") {
+
+						this.selectTeachingMode = {
+							mode: "Stations",
+							name: param.data?.name || "",
+							src_dst: param.data?.src_dst || "",
+							point: param.point
+						}
+
+					} else if (param.type == "public") {
+						this.selectTeachingMode = {
+							mode: "Public",
+							name: param.data?.name || "",
+							point: param.point
+						}
+					}
+
+					this.$refs.refPopupOperateTeaching.open("bottom")
+				}
+
 			},
 			clickMapStation() {
-				uni.navigateTo({
-					url: `/pages/station/index?ip=${this.vehicleIp}&isAdd=1`
-				})
+				this.stationEdit = {
+					stationID: "",
+					name: "",
+					x: 0,
+					y: 0,
+					angle: 0,
+				}
+				this.mapOperationType = "add_station"
+				this.mapOperationStatus = ""
+			},
+			clickAddStation() {
+				this.stationEdit = {
+					stationID: "",
+					name: "",
+					x: 0,
+					y: 0,
+					angle: 0,
+				}
+				this.mapOperationType = "teaching_add_station"
+				this.mapOperationStatus = ""
 			},
 			clickMapEdit() {
+
+				this.mapOperationType = "edit_map"
+			},
+			clickExtendMap() {
 				uni.navigateTo({
-					url: `/pages/map/edit?ip=${this.vehicleIp}`
+					url: `/pages/map/scene?ip=${this.vehicleIp}&opType=extend&sceneId=${this.sceneId}`,
+					events: {
+						// 涓烘寚瀹氫簨浠舵坊鍔犱竴涓洃鍚櫒锛岃幏鍙栬鎵撳紑椤甸潰浼犻�鍒板綋鍓嶉〉闈㈢殑鏁版嵁
+						create_finish: function(data) {
+							_this.onCreateSceneOk(data)
+						},
+					}
 				})
 			},
 			clickMapTask() {
+
 				uni.navigateTo({
-					url: `/pages/map/task?ip=${this.vehicleIp}`
+					url: `/pages/task/list?ip=${this.vehicleIp}`
 				})
+			},
+			clickTeaching() {
+				this.mapOperationStatus = ""
+				this.teachingModeCur = {
+					mode: "Public",
+					main_road: 1,
+				}
+				this.mapOperationType = "public_teaching"
+				this.ctxDataStr = JSON.stringify([{
+					method: "set_selectable",
+					param: false,
+				}, {
+					method: "public_teaching",
+				}])
+
+				// uni.navigateTo({
+				// 	url: `/pages/map/teaching?ip=${this.vehicleIp}&sceneId=${this.sceneId}&list=${JSON.stringify(this.sceneList)}`
+				// })
+			},
+			clickRegionaPlan() {
+				this.mapOperationType = "regiona_planning"
+			},
+			clickRename() {
+				this.sceneInputName = this.sceneId
+				this.mapOperationType = 'edit_scene_name'
+			},
+			async loadSceneList() {
+				try {
+					uni.showLoading({
+						title: "鍔犺浇鍦烘櫙涓�
+					})
+					const _this = this
+					const res = await getAllScene(this.vehicleIp) || []
+					const list = res?.sceneList || []
+					this.setData({
+						unlinked: false
+					})
+					getApp().globalData.sceneList = list
+					this.sceneList = list
+					if (list.length === 0) {
+						this.opSceneType = ''
+						// this.mapOperationType = 'scene_create'
+						// this.setData({
+						// 	sceneId: "",
+						// 	navigationBarTitle: "鍦板浘"
+						// })
+						uni.navigateTo({
+							url: `/pages/map/scene?ip=${this.vehicleIp}&opType=create`,
+							events: {
+								// 涓烘寚瀹氫簨浠舵坊鍔犱竴涓洃鍚櫒锛岃幏鍙栬鎵撳紑椤甸潰浼犻�鍒板綋鍓嶉〉闈㈢殑鏁版嵁
+								create_finish: function(data) {
+									_this.onCreateSceneOk(data)
+								},
+							}
+						})
+						return
+					}
+
+				} catch (ex) {
+					this.showError(ex)
+				} finally {
+					uni.hideLoading()
+				}
+			},
+			clickDelete() {
+				showModal(`鍒犻櫎鍦烘櫙[${this.sceneId}]浼氭妸鍦烘櫙瀵瑰簲鐨勫湴鍥句换鍔′俊鎭兘浼氬垹闄わ紝鏄惁纭鍒犻櫎锛焋, "鍒犻櫎鍦烘櫙").then(async (res) => {
+					if (res) {
+						try {
+							uni.showLoading({
+								title: "鍒犻櫎鍦烘櫙涓�
+							})
+							this.mapShowAgv = false
+							await delScene(this.vehicleIp, this.sceneId)
+							await this.loadSceneList()
+							if (this.sceneList.length > 0)
+								this.changeMap(this.sceneList[0])
+						} catch (ex) {
+							this.showError(ex)
+						} finally {
+							uni.hideLoading()
+						}
+					}
+
+
+
+				})
+			},
+			clickManualPlan() {
+				this.mapOperationType = "manual_planning"
+				this.mapOperationStatus = ""
+			},
+			clickVehicleTrajectoryPlan() {
+				this.mapOperationType = "vehicle_trajectory_planning"
+				this.mapOperationStatus = ""
+			},
+			async clickPlanFeasibleRegion() {
+				try {
+					this.mapOperationStatus = "feasible_region"
+					if (this.mapOperationType == "manual_planning") {
+						const agv = await this.loadAgvState()
+
+						const offset = 0.1 * this.curMapInfo.img_proportion
+						this.regionEdit = {
+
+							id: `region_${new Date().getTime()}`,
+							color: "#71d13c",
+							type: "feasible_region",
+							path: [{
+									x: agv.x - offset,
+									y: agv.y - offset
+								}, {
+									x: agv.x - offset,
+									y: agv.y + offset
+								}, {
+									x: agv.x + offset,
+									y: agv.y + offset
+								},
+								{
+									x: agv.x + offset,
+									y: agv.y - offset
+								}
+							]
+
+						}
+						this.ctxDataStr = JSON.stringify([{
+								method: "update_agv_state",
+								param: agv
+							}, {
+								method: "add_region",
+								param: [this.regionEdit]
+							},
+							{
+								method: "move_canvas",
+								param: {
+									x: agv.x,
+									y: agv.y
+								}
+							}
+						])
+					}
+				} catch (ex) {
+					this.showError(ex)
+				}
+			},
+			async clickPlanProhibitionRegion() {
+				try {
+					this.mapOperationStatus = "prohibition_region"
+					if (this.mapOperationType == "manual_planning") {
+						const agv = await this.loadAgvState()
+						const offset = 0.1 * this.curMapInfo.img_proportion
+						this.regionEdit = {
+							id: `region_${new Date().getTime()}`,
+							color: "#ff4d4f",
+							type: "prohibition_region",
+							path: [{
+									x: agv.x - offset,
+									y: agv.y - offset
+								}, {
+									x: agv.x - offset,
+									y: agv.y + offset
+								}, {
+									x: agv.x + offset,
+									y: agv.y + offset
+								},
+								{
+									x: agv.x + offset,
+									y: agv.y - offset
+								}
+							]
+						}
+						this.ctxDataStr = JSON.stringify([{
+								method: "update_agv_state",
+								param: agv
+							}, {
+								method: "add_region",
+								param: [this.regionEdit]
+							},
+							{
+								method: "move_canvas",
+								param: {
+									x: agv.x,
+									y: agv.y
+								}
+							}
+						])
+					}
+				} catch (ex) {
+					this.showError(ex)
+				}
+			},
+
+			async clickPlanVirtualWall() {
+				try {
+					const agv = await this.loadAgvState()
+					const offset = 0.1 * this.curMapInfo.img_proportion
+
+					this.mapOperationStatus = "virtual_wall"
+					this.wallEdit = {
+						id: `wall_${new Date().getTime()}`,
+						color: "#ff4d4f",
+						type: "virtual_wall",
+						path: [{
+								x: agv.x,
+								y: agv.y - offset
+							},
+							{
+								x: agv.x + offset,
+								y: agv.y + offset
+							}
+						]
+					}
+					this.ctxDataStr = JSON.stringify([{
+							method: "update_agv_state",
+							param: agv
+						}, {
+							method: "add_wall",
+							param: [this.wallEdit]
+						},
+						{
+							method: "move_canvas",
+							param: {
+								x: agv.x,
+								y: agv.y
+							}
+						}
+					])
+				} catch (ex) {
+					this.showError(ex)
+				}
+
+			},
+			clickNameCancel() {
+				this.mapOperationType = ''
+			},
+			async clickNameOK() {
+				try {
+					uni.showLoading({
+						title: "鏇存柊鍦烘櫙鍚嶇О"
+					})
+					this.loading = true
+					const name = this.sceneInputName.trim()
+					if (!name) {
+						showToast("鍦烘櫙鍚嶇О涓嶈兘涓虹┖锛�)
+						return
+					}
+					console.log(name, this.sceneId)
+					if (name == this.sceneId) {
+						showToast("鍦烘櫙鍚嶇О鏈彉鍖栵紒")
+						return
+					}
+					this.mapOperationType = ""
+					await updateScene(this.vehicleIp, this.sceneId, name)
+					this.sceneId = name
+					showToast("鍦烘櫙閲嶅懡鍚嶆垚鍔燂紒")
+				} catch (ex) {
+					this.mapOperationType = "edit_scene_name"
+					this.showError(ex)
+				} finally {
+					uni.hideLoading()
+					this.loading = false
+				}
+
+			},
+
+			clickClearName() {
+				this.showClearName = false
+				this.sceneInputName = ""
+			},
+			onInputName(event) {
+				this.sceneInputName = event.detail.value.trim();
+				if (this.sceneInputName)
+					this.showClearName = true
+				else
+					this.showClearName = false
+			},
+
+			closePopuBtn() {
+
+				this.ctxDataStr = JSON.stringify([{
+					method: "set_selectable",
+					param: true
+				}])
+				this.mapOperationType = ""
+			},
+			clickStationDelete() {
+				const _this = this
+				showModal("璇ョ珯鐐瑰凡缁戝畾浠诲姟锛屽垹闄ょ珯鐐瑰悗缁戝畾鐨勪换鍔′細鍋滄骞跺垹闄�, "鏄惁纭鍒犻櫎锛�).then((res) => {
+					if (res) {
+						this.stationDelete(this.stationEdit)
+					}
+				})
+			},
+			async stationAdd(item) {
+				try {
+					uni.showLoading({
+						title: "姝e湪鏂板缓绔欑偣"
+					})
+					await addStation(this.vehicleIp, item)
+					await updateStation(this.vehicleIp, item)
+					this.stationList.push({
+						stationID: item.stationID,
+						name: item.name,
+						angle: item.angle,
+						x: item.y,
+						y: item.x
+					})
+
+
+				} catch (ex) {
+					this.showError(ex)
+				} finally {
+					uni.hideLoading()
+				}
+			},
+			async stationUpdate(item) {
+				try {
+					uni.showLoading({
+						title: "姝e湪鏇存柊绔欑偣"
+					})
+					await updateStation(this.vehicleIp, item)
+					const curIndex = this.stationList.findIndex((a) => a.stationID ==
+						item
+						.stationID)
+					if (curIndex > -1) {
+						const station = this.stationList[curIndex]
+						station.name = item.name
+						station.angle = item.angle
+						station.x = item.y
+						station.y = item.x
+					}
+
+				} catch (ex) {
+					this.showError(ex)
+				} finally {
+					uni.hideLoading()
+				}
+			},
+			async clickStationPositonOk() {
+				try {
+
+					if (this.mapOperationType == "add_station") {
+						this.stationAdd(this.stationEdit)
+					} else if (this.mapOperationType == "teaching_add_station") {
+						this.stationAdd(this.stationEdit)
+						this.mapOperationType = "public_teaching"
+						this.mapOperationStatus = "teaching"
+						return
+					} else if (this.mapOperationType == "edit_station") {
+						this.stationUpdate(this.stationEdit)
+					}
+					this.ctxDataStr = JSON.stringify([{
+						method: "set_selectable",
+						param: true
+					}])
+					this.mapOperationType = ''
+				} catch (ex) {
+					this.showError(ex)
+				}
+			},
+			async stationDelete(item) {
+				try {
+					uni.showLoading({
+						title: "姝e湪鍒犻櫎绔欑偣"
+					})
+					this.$refs.refPopupOperateStation.close()
+					await delStation(this.vehicleIp, [item.stationID])
+
+					const curIndex = this.stationList.findIndex((a) => a
+						.stationID == item.stationID)
+					if (curIndex > -1) {
+						this.stationList.splice(curIndex, 1)
+					}
+
+					this.mapOperationType = ""
+					this.ctxDataStr = JSON.stringify([{
+						method: "remove_station",
+						param: [item]
+					}, {
+						method: "set_selectable",
+						param: true
+					}])
+
+				} catch (ex) {
+					this.showError(ex)
+				} finally {
+					uni.hideLoading()
+				}
+			},
+			clickStationPostion() {
+				this.mapOperationType = "edit_station"
+				this.mapOperationStatus = "pos"
+
+
+				this.$refs.refPopupOperateStation.close()
+				this.ctxDataStr = JSON.stringify([{
+					method: "edit_station_pos",
+					param: this.stationEdit
+				}])
+			},
+			clickStationRename() {
+				this.mapOperationType = "edit_station"
+				this.mapOperationStatus = ""
+				this.$refs.refPopupOperateStation.close()
+			},
+			clickStationNameCancel() {
+
+				if (this.mapOperationType == "edit_station") {
+					this.ctxDataStr = JSON.stringify([{
+						method: "set_selectable",
+						param: true
+					}])
+					this.mapOperationType = ""
+					this.$refs.refPopupOperateStation.open("bottom")
+				} else if (this.mapOperationType == "add_station") {
+					this.mapOperationType = ""
+					this.ctxDataStr = JSON.stringify([{
+						method: "set_selectable",
+						param: true
+					}])
+
+				} else if (this.mapOperationType == "teaching_add_station") {
+					this.mapOperationType = "public_teaching"
+					this.mapOperationStatus = "teaching"
+				}
+
+			},
+			async clickStationNameOK() {
+				try {
+					this.loading = true
+					const name = this.stationEdit.name.trim()
+					if (!name) {
+						showToast("绔欑偣鍚嶇О杩樻湭杈撳叆")
+						return
+					}
+					if (this.mapOperationType == 'add_station' || this
+						.mapOperationType == "teaching_add_station") {
+						const agv = await this.loadAgvState()
+						this.stationEdit = {
+							stationID: this.getMaxStationNo + 1,
+							name: name,
+							x: agv.x || 0,
+							y: agv.y || 0,
+							angle: agv.angle || 0,
+						}
+						const angle = this.getStantardAngle(this
+							.stationEdit.angle * 180 / Math.PI)
+
+						this.angleSvg = `/static/images/angle${angle}.svg`
+						this.ctxDataStr = JSON.stringify([{
+							method: "update_agv_state",
+							param: agv
+						}, {
+							method: "update_station",
+							param: [this.stationEdit]
+						}, {
+							method: "edit_station_pos",
+							param: this.stationEdit
+						}, {
+							method: "move_canvas",
+							param: {
+								x: agv.x,
+								y: agv.y
+							}
+						}])
+						this.mapOperationStatus = "pos"
+					} else if (this.mapOperationType ==
+						'edit_station') {
+						this.stationEdit.name = name
+						await updateStation(this.vehicleIp, this
+							.stationEdit)
+						this.ctxDataStr = JSON.stringify([{
+							method: "update_station",
+							param: [this.stationEdit]
+						}, {
+							method: "set_selectable",
+							param: true
+						}])
+						this.mapOperationStatus = "pos"
+						this.$refs.refPopupOperateStation.open("bottom")
+					}
+
+				} catch (ex) {
+					this.showError(ex)
+				} finally {
+					this.loading = false
+				}
+
+			},
+
+			clickClearStationName() {
+				this.stationEdit.name = ""
+				this.showClearName = false
+			},
+			onInputStationName(event) {
+				this.stationEdit.name = event.detail.value.trim();
+				if (this.stationEdit.name)
+					this.showClearName = true
+				else
+					this.showClearName = false
+			},
+			onInputStationX(event) {
+				console.log(event)
+				this.stationEdit.x = Number(event.detail.value);
+				console.log(this.stationEdit.x)
+				this.ctxDataStr = JSON.stringify([{
+					method: "update_station",
+					param: [this.stationEdit]
+				}, {
+					method: "move_canvas",
+					param: {
+						x: this.stationEdit.x,
+						y: this.stationEdit.y
+					}
+				}])
+			},
+
+			onInputStationY(event) {
+				this.stationEdit.y = Number(event.detail.value);
+				console.log(this.stationEdit.y)
+				this.ctxDataStr = JSON.stringify([{
+					method: "update_station",
+					param: [this.stationEdit]
+				}, {
+					method: "move_canvas",
+					param: {
+						x: this.stationEdit.x,
+						y: this.stationEdit.y
+					}
+				}])
+			},
+			clickClearStationX() {
+				this.stationEdit.x = 0
+				this.ctxDataStr = JSON.stringify([{
+					method: "update_station",
+					param: [this.stationEdit]
+				}, {
+					method: "move_canvas",
+					param: {
+						x: this.stationEdit.x,
+						y: this.stationEdit.y
+					}
+				}])
+			},
+			clickClearStationY() {
+				this.stationEdit.y = 0
+				this.ctxDataStr = JSON.stringify([{
+					method: "update_station",
+					param: [this.stationEdit]
+				}, {
+					method: "move_canvas",
+					param: {
+						x: this.stationEdit.x,
+						y: this.stationEdit.y
+					}
+				}])
+			},
+			getStantardAngle(angle) {
+				let resAngle = 0
+				if (angle < -157.5) {
+					resAngle = 180
+				} else if (angle < -112.5) {
+					resAngle = -135
+				} else if (angle < -67.5) {
+					resAngle = -90
+				} else if (angle < -22.5) {
+					resAngle = -45
+				} else if (angle < 22.5) {
+					resAngle = 0
+				} else if (angle < 67.5) {
+					resAngle = 45
+				} else if (angle < 112.5) {
+					resAngle = 90
+				} else if (angle < 157.5) {
+					resAngle = 135
+				} else if (angle <= 180) {
+					resAngle = 180
+				} else {
+					resAngle = 0
+				}
+				return resAngle
+
+			},
+
+			touchAngleChange(e) {
+				let angle = 0
+
+				const ptX = e.touches[0].clientX
+
+				const offX = ptX // ptX - e.target.offsetLeft
+				const offW = 300 / 16
+				const centerPt = this.windowWidth / 2
+				if (offX < centerPt - 8 * offW) {
+					return
+				} else if (offX < centerPt - 7 * offW) {
+					angle = 180
+				} else if (offX < centerPt - 5 * offW) {
+					angle = -135
+				} else if (offX < centerPt - 3 * offW) {
+					angle = -90
+				} else if (offX <= centerPt - offW) {
+					angle = -45
+				} else if (offX < centerPt + offW) {
+					angle = 0
+				} else if (offX < centerPt + 3 * offW) {
+					angle = 45
+				} else if (offX < centerPt + 5 * offW) {
+					angle = 90
+				} else if (offX < centerPt + 7 * offW) {
+					angle = 135
+				} else if (offX <= centerPt + 8 * offW) {
+					angle = 180
+				} else {
+					return
+				}
+				this.stationEdit.angle = angle * Math.PI / 180
+
+
+				this.angleSvg = `/static/images/angle${angle}.svg`
+				this.ctxDataStr = JSON.stringify([{
+					method: "update_station",
+					param: [this.stationEdit]
+				}])
+			},
+			handleAngleTouchStart(e) {
+				//	console.log("handleAngleTouchStart", e)
+				this.touchAngleChange(e)
+
+			},
+			handleAngleTouchMove(e) {
+				//	console.log("handleAngleTouchMove", e)
+				this.touchAngleChange(e)
+			},
+
+			async clickVehiclePosition() {
+				try {
+					const infoAgv = await this.loadAgvState()
+					this.ctxDataStr = JSON.stringify([{
+						method: "update_agv_state",
+						param: infoAgv
+					}, {
+						method: "move_canvas",
+						param: {
+							x: infoAgv.x,
+							y: infoAgv.y
+						}
+					}])
+				} catch (ex) {
+					this.showError(ex)
+				}
+			},
+			async clickPositionStation() {
+				try {
+					const infoAgv = await this.loadAgvState()
+					this.stationEdit.x = infoAgv.x
+					this.stationEdit.y = infoAgv.y
+					this.stationEdit.angle = infoAgv.angle
+					const angle = this.getStantardAngle(this
+						.stationEdit.angle * 180 / Math.PI)
+					console.log(this.stationEdit.angle, angle, this
+						.angleSvg)
+					this.angleSvg =
+						`/static/images/angle${angle}.svg`
+					this.ctxDataStr = JSON.stringify([{
+							method: "update_agv_state",
+							param: infoAgv
+						}, {
+							method: "update_station",
+							param: [this.stationEdit]
+						},
+						{
+							method: "move_canvas",
+							param: {
+								x: infoAgv.x,
+								y: infoAgv.y
+							}
+						}
+
+
+					])
+				} catch (ex) {
+					this.showError(ex)
+				}
+			},
+			async loadTeachingMode() {
+				try {
+					const {
+						data
+					} = await getTeachingMode(this.vehicleIp)
+					return data || {
+						Public: [],
+						Stations: []
+					}
+
+				} catch (ex) {
+					this.showError(ex)
+					return {
+						Public: [],
+						Stations: []
+					}
+				}
+			},
+			async reloadTeachingMode() {
+				try {
+					this.teachingMode = await this.loadTeachingMode()
+					this.ctxDataStr = JSON.stringify([{
+							method: "public_teaching_path",
+							param: this.teachingMode.Public || []
+						},
+						{
+							method: "station_teaching_path",
+							param: this.teachingMode.Stations || []
+						}, {
+							method: "show_teaching_path",
+							param: {
+								show: this.showTeachingPathFlag
+							}
+						}
+					])
+
+				} catch (ex) {
+					this.showError(ex)
+
+				}
+			},
+
+			async clickShowTeachingPath() {
+				try {
+					if (this.showTeachingPathFlag) {
+						this.ctxDataStr = JSON.stringify([{
+							method: "show_teaching_path",
+							param: {
+								show: false
+							}
+						}])
+						this.showTeachingPathFlag = false
+						return
+					}
+					this.showTeachingPathFlag = true
+					//this.teachingMode = await this.loadTeachingMode()
+					this.ctxDataStr = JSON.stringify([
+						// {
+						// 	method: "clear_teaching_path",
+
+						// },
+						// {
+						// 	method: "public_teaching_path",
+						// 	param: this.teachingMode
+						// 		.Public || []
+						// },
+						// {
+						// 	method: "station_teaching_path",
+						// 	param: this.teachingMode
+						// 		.Stations || []
+						// }, 
+						{
+							method: "show_teaching_path",
+							param: {
+								show: true
+							}
+						}
+					])
+				} catch (ex) {
+					this.showError(ex)
+				}
+			},
+			async refreshAgvPosition() {
+				try {
+					if (this.isPageVisible && this.mapShowAgv) {
+						const agv = await this.loadAgvState()
+
+						const listCtrData = [{
+							method: "update_agv_state",
+							param: agv
+						}]
+
+						if (this.mapOperationType === "public_teaching" && this.mapOperationStatus ===
+							"teaching") {
+							const list = await this.loadCurrentTeachingData()
+							listCtrData.push({
+								method: "update_current_teaching",
+								param: {
+									main_road: this.teachingModeCur.main_road,
+									pos_list: list
+								}
+							})
+						}
+						this.ctxDataStr = JSON.stringify(listCtrData)
+					}
+				} catch (ex) {
+					this.showError(ex)
+				} finally {
+					// 鏃犺鎴愬姛澶辫触锛� 绉掑悗鍐嶆潵
+					setTimeout(this.refreshAgvPosition, 1000);
+				}
+
+			},
+
+			askTeachingBiDirection(teachingMode) {
+				showModal("閫夋嫨褰撳墠绀烘暀璺嚎绫诲瀷?", "绀烘暀缁撴潫", true, "鍙屽悜璺嚎",
+					"鍗曞悜璺嚎").then((res) => {
+					if (res) {
+						teachingMode.bidirection = "1"
+					} else {
+						teachingMode.bidirection = "0"
+					}
+					teachingMode.teaching_flag = 0
+					this.finishTeaching(teachingMode)
+				})
+
+			},
+			async finishTeaching(teachingMode) {
+				try {
+					uni.showLoading({
+						title: "绀烘暀缁撴潫"
+					})
+					await teachingModeFlag(this.vehicleIp,
+						teachingMode)
+
+					this.mapOperationStatus = "end"
+				} catch (ex) {
+					this.showError(ex)
+				} finally {
+					uni.hideLoading()
+				}
+			},
+
+			async clickTeachingStart() {
+
+				const _this = this
+				try {
+					uni.showLoading({
+						title: "绀烘暀寮�"
+					})
+					this.loading = false
+					this.mapOperationStatus = "teaching"
+					await _this.teachingStart("Public")
+
+				} catch (ex) {
+					this.showError(ex)
+				} finally {
+					this.loading = false
+					uni.hideLoading()
+				}
+			},
+			clickTeachingEnd() {
+				if (this.mapOperationType ==
+					"station_teaching") {
+					this.teachingEnd("Stations")
+				} else {
+
+					this.teachingEnd("Public")
+				}
+
+			},
+			clickTeachingReset() {
+				const _this = this
+				showModal("宸茶褰曠殑璺緞灏嗕細琚垹闄ゃ�", "鏄惁瑕侀噸鏂拌褰曪紵")
+					.then(async (res) => {
+						if (res) {
+							try {
+								this.loading = true
+								await delTeachingMode(_this.vehicleIp, [_this.teachingModeCur])
+								_this.mapOperationStatus = ""
+
+							} catch (ex) {
+								this.showError(ex)
+							} finally {
+								this.loading = false
+							}
+						} else {
+
+							_this.mapOperationStatus =
+								"save"
+
+						}
+					})
+
+			},
+			async clickTeachingSave() {
+
+				this.mapOperationStatus = "save"
+				try {} catch (ex) {
+					this.showError(ex)
+				}
+
+			},
+			clickTeachingFinish() {
+				this.mapOperationType = ""
+				this.ctxDataStr = JSON.stringify([{
+					method: "teaching_finish",
+				}])
+				this.reloadTeachingMode()
+
+			},
+			async teachingStart(mode) {
+				try {
+					/*if (mode == "Stations") {
+						const res = await checkAgvLocationDistanceError(this.vehicleIp, this.startStationID)
+						if (res?.error) {
+							this.calibratioStationType = "start"
+							this.$refs.refPopupCalibration.open()
+						} else {
+							// const name =
+							// 	`${ this.stationName(this.startStationID)}_${ this.stationName(this.endStationID)}`
+
+							this.teachingModeCur = {
+								mode: "Stations",
+								src_dst: `${this.startStationID}_${this.endStationID}`,
+								name: "",
+								teaching_flag: 1,
+							}
+							this.ctxDataStr = JSON.stringify([{
+								method: "set_selectable",
+								param: false,
+							}])
+							const res2 = await teachingModeFlag(this.vehicleIp, this.teachingModeCur)
+							if (res2?.name)
+								this.teachingModeCur.name = res2.name
+						}
+					} else {*/
+
+					const main_road = this
+						.teachingModeCur
+						.main_road
+					this.teachingModeCur = {
+						mode: "Public",
+						src_dst: ``,
+						name: "",
+						teaching_flag: 1,
+						main_road
+					}
+					const res2 =
+						await teachingModeFlag(
+							this.vehicleIp,
+							this.teachingModeCur)
+					if (res2?.name) {
+						this.teachingModeCur.name = res2.name
+					}
+
+				} catch (ex) {
+					this.showError(ex)
+				}
+
+			},
+			async teachingEnd(mode) {
+				try {
+					this.loading = true
+					// if (mode == "Stations") {
+					// 	const res = await checkAgvLocationDistanceError(this.vehicleIp, this.endStationID)
+					// 	if (res.error) {
+					// 		this.calibratioStationType = "end"
+					// 		this.$refs.refPopupCalibration.open()
+					// 	} else {
+					// 		this.askTeachingBiDirection(this.teachingModeCur)
+					// 	}
+					// } else {
+					// 	this.askTeachingBiDirection(this.teachingModeCur)
+					// }
+					this.askTeachingBiDirection(
+						this
+						.teachingModeCur
+					)
+				} catch (ex) {
+					this.showError(ex)
+				} finally {
+					this.loading = false
+				}
+
+			},
+			async onTeachingModeMainRoad(
+				val) {
+				try {
+					if (this
+						.mapOperationStatus
+					) {
+
+						if (this
+							.teachingModeCur
+							.main_road ==
+							val) {
+							return
+						}
+						this.teachingModeCur
+							.main_road =
+							val
+						this.teachingModeCur
+							.teaching_flag =
+							1
+						this.teachingModeCur
+							.mode =
+							"Public"
+						const res =
+							await teachingModeFlag(
+								this
+								.vehicleIp,
+								this
+								.teachingModeCur
+							)
+						if (val == 0)
+							showToast(
+								"宸插皢璇ヨ矾娈佃矾寰勪繚瀛樹负涓昏矾绀烘暀璺嚎"
+							)
+						else
+							showToast(
+								"宸插皢璇ヨ矾娈佃矾寰勪繚瀛樹负鏀矾绀烘暀璺嚎"
+							)
+						if (res?.name)
+							this
+							.teachingModeCur
+							.name = res
+							.name
+					} else {
+						this.teachingModeCur
+							.main_road =
+							val
+					}
+
+				} catch (ex) {
+					this.showError(ex)
+				}
+			},
+			clickTeachingDelete() {
+				this.$refs
+					.refPopupOperateTeaching
+					.close()
+				this.removeTeachingMode(
+					this
+					.selectTeachingMode
+				)
+			},
+
+			clickTeachingEdit() {
+				this.$refs
+					.refPopupOperateTeaching
+					.close()
+				this.mapOperationType =
+					"edit_teaching"
+				this.ctxDataStr = JSON
+					.stringify([{
+						method: "edit_teaching",
+						param: this.selectTeachingMode,
+					}])
+			},
+
+			async removeTeachingMode(item) {
+				try {
+					uni.showLoading({
+						title: "鍒犻櫎鍦烘櫙涓�
+					})
+					await delTeachingMode(this.vehicleIp, [item])
+					this.ctxDataStr =
+						JSON.stringify(
+							[{
+								method: "remove_teaching_path",
+								param: item,
+							}])
+				} catch (ex) {
+					this.showError(
+						ex)
+				} finally {
+					uni.hideLoading()
+				}
+			},
+			async removeTeachingModeData(data) {
+				try {
+					uni.showLoading({
+						title: "鍒犻櫎鍦烘櫙鏁版嵁涓�
+					})
+					await delTeachingModeData(this.vehicleIp, data)
+					this.reloadTeachingMode()
+				} catch (ex) {
+					this.showError(
+						ex)
+				} finally {
+					uni.hideLoading()
+				}
 			},
 
 			showError(ex) {
 				let exStr = JSON.stringify(ex)
 				if (exStr == "{}")
 					exStr = ex
-				let tip = typeof ex.msg == "string" ? ex.msg : exStr
-				showModal(tip, "閿欒", false)
+				let tip = typeof ex.msg == "string" ? ex.msg : typeof ex.errMsg == "string" ? ex.errMsg : exStr
+				showModal(tip,
+					"閿欒",
+					false,
+					"纭畾")
 			},
 
 
@@ -208,6 +2262,7 @@
 			left: 0;
 			right: 0;
 			bottom: 0;
+			background-color: #ddd;
 			display: flex;
 			flex-direction: column;
 		}
@@ -217,6 +2272,174 @@
 			display: flex;
 			flex: 1;
 			margin: auto;
+			flex-direction: column;
+			align-items: center;
+			justify-content: center;
+
+			.loading-overlay {
+				position: fixed;
+				top: 0;
+				left: 0;
+				right: 0;
+				bottom: 0;
+				background-color: rgba(0, 0, 0, 0.5);
+				display: flex;
+				justify-content: center;
+				align-items: center;
+				z-index: 999;
+
+				.loading-content {
+					background-color: #fff;
+					padding: 60rpx 100rpx;
+					border-radius: 20rpx;
+					text-align: center;
+				}
+
+				.loading-spinner {
+					width: 120rpx;
+					height: 120rpx;
+					border: 20rpx solid #f3f3f3;
+					border-top: 20rpx solid #1890FF;
+					border-radius: 50%;
+					animation: spin 1s linear infinite;
+					margin: 0 auto 40rpx;
+				}
+
+				@keyframes spin {
+					0% {
+						transform: rotate(0deg);
+					}
+
+					100% {
+						transform: rotate(360deg);
+					}
+				}
+
+			}
+
+
+			.content {
+				overflow: auto;
+				position: absolute;
+				left: 0;
+				right: 0;
+				bottom: 0;
+				top: 0;
+
+				.fabric {
+					width: 100%;
+					height: 100%;
+					background-color: #fff;
+				}
+			}
+
+			.position {
+				position: absolute;
+				right: 50rpx;
+				bottom: 500rpx;
+
+				.img {
+					width: 65rpx;
+					height: 65rpx;
+				}
+
+				.ico {
+					font-size: 65rpx;
+					color: #1890FF;
+				}
+			}
+
+			.position-site {
+				display: flex;
+				position: absolute;
+				margin: 2rpx;
+				right: 40rpx;
+				bottom: 500rpx;
+				flex-direction: row;
+				padding: 10rpx;
+				border-radius: 10rpx;
+				background-color: #fff;
+				border: 2rpx solid #1890FF;
+				justify-content: center;
+				align-items: center;
+				color: #1890FF;
+				font-size: 32rpx;
+
+				.img {
+					width: 40rpx;
+					height: 40rpx;
+					margin: 0 10rpx;
+				}
+
+				.ico {
+					font-size: 40rpx;
+					color: #1890FF;
+				}
+			}
+
+			.station-info {
+				display: flex;
+				position: absolute;
+				flex-direction: row;
+				border-radius: 10rpx;
+				background-color: #fff;
+				border: 1px solid #1890FF;
+				font-size: 15px;
+
+				.left {
+					display: flex;
+					flex-direction: column;
+					width: 180rpx;
+					color: #aaa;
+
+					.line {
+						padding: 15rpx 5rpx 15rpx 10rpx;
+					}
+				}
+
+				.right {
+					display: flex;
+					flex-direction: column;
+
+					.line {
+						padding: 15rpx 10rpx 15rpx 5rpx;
+						text-align: right;
+					}
+				}
+			}
+
+			.teaching-path-show {
+				position: absolute;
+				right: 50rpx;
+				top: 200rpx;
+				padding: 12rpx;
+				border: 1px solid #1890FF;
+				border-radius: 36rpx;
+				width: 72rpx;
+				height: 72rpx;
+				background-color: #fff;
+
+				.ico {
+					font-size: 40rpx;
+					color: #1890FF;
+				}
+			}
+
+			.selected {
+				background-color: #1890FF20;
+			}
+
+			.station-edit-cancel {
+				position: absolute;
+				color: #F5222D;
+				font-size: 60rpx;
+			}
+
+			.station-edit-ok {
+				position: absolute;
+				color: #52C41A;
+				font-size: 60rpx;
+			}
 
 			.text-button-group {
 				display: flex;
@@ -225,14 +2448,12 @@
 				align-items: center;
 				flex-direction: column;
 				font-size: 30rpx !important;
-				background-color: #eee;
 
 				.button {
 					margin-top: 20rpx;
 					width: 375rpx;
 				}
 			}
-
 
 		}
 
@@ -245,6 +2466,7 @@
 			flex-direction: column;
 			font-size: 30rpx;
 			font-weight: 400;
+			background-color: #000fff;
 
 			.title {
 				font-size: 40rpx;
@@ -271,51 +2493,188 @@
 				padding-bottom: 10rpx;
 				background-color: #fff;
 				border-radius: 10rpx;
-			}
 
 
+				.title {
+					font-size: 40rpx;
+					margin-bottom: 10rpx;
+				}
 
-		}
+				.tip {
+					color: #888;
+					margin: 10rpx;
+					text-align: left;
+				}
 
-		.img-button-group {
-			display: flex;
-			//width: 100%;
-			flex-direction: row;
-			border-radius: 10px;
-			border: 1px solid #fff;
-			font-size: 30rpx !important;
-			justify-content: space-around;
-
-			.button {
-				margin: 10rpx 20rpx;
-
-				height: 144rpx !important;
-				border: 0;
-				display: flex;
-				flex-direction: column;
-				background-color: #00000000;
-
-				.img {
-					margin: auto;
-					width: 72rpx;
-					height: 72rpx;
+				.row-group {
+					display: flex;
+					flex-direction: row;
+					margin: 10rpx 0;
 
 				}
 
-				.ico {
-					margin: auto;
-					font-size: 72rpx;
+				.angle-group {
+					padding: 10rpx;
+					display: flex;
+					flex-direction: column;
+					justify-content: center;
+					align-items: center;
+
+					.img-angle {
+						width: 128rpx;
+						height: 42rpx;
+						margin-bottom: 10rpx;
+					}
+
+					.img-angle-pos {
+						width: 300px;
+						height: 64rpx;
+						margin: 10rpx;
+					}
+
+				}
+
+				.coordinate {
+					flex: 1;
+					display: flex;
+					flex-direction: row;
+					align-items: center;
+					// .name{
+					// 	padding: 10rpx 0;
+
+					// }
+				}
+
+				.number-input {
+					flex: 1;
+					background-color: #fff;
+					padding: 10rpx;
+					border-radius: 8rpx;
 					color: #1890FF;
 				}
 
-				.text {
+
+				.name-input {
+					width: calc(100% - 20rpx);
+					margin-bottom: 10rpx;
+					background-color: #fff;
+					padding: 10rpx;
+					border-radius: 8rpx;
+					display: flex;
+					flex-direction: row;
+					color: #1890FF;
+
+					input {
+						flex: 1;
+					}
+				}
+			}
+
+			.img-button-group {
+				display: flex;
+				//width: 100%;
+				flex-direction: row;
+				border-radius: 10px;
+				border: 1px solid #fff;
+				font-size: 30rpx !important;
+				justify-content: space-around;
+
+				.button {
+					margin: 10rpx 10rpx;
+
+					height: 144rpx !important;
+					border: 0;
+					display: flex;
+					flex-direction: column;
+					background-color: #00000000;
+
+					.img {
+						margin: auto;
+						width: 72rpx;
+						height: 72rpx;
+
+					}
+
+					.dashed-line {
+						width: 72rpx;
+						height: 72rpx;
+						border-left: 8rpx dashed #1890FF;
+						/* 璁剧疆宸︿晶杈规涓鸿櫄绾�*/
+						transform: rotate(45deg);
+						/* 鏃嬭浆45搴�*/
+						transform-origin: bottom left;
+						/* 璁剧疆鏃嬭浆鐨勫熀鐐�*/
+					}
+
+					.ico {
+						margin: auto;
+						font-size: 72rpx;
+						color: #1890FF;
+					}
+
+					.text {
+						margin: auto;
+
+					}
+				}
+
+			}
+
+			.text-button-group {
+				display: flex;
+				width: 100%;
+				justify-content: center;
+				align-items: center;
+				flex-direction: column;
+				font-size: 30rpx !important;
+
+				.button {
 					margin: auto;
+					margin-top: 20rpx;
+					width: calc(100% - 10rpx);
+				}
+			}
+
+			.switch-type {
+				width: 100%;
+				align-items: center;
+				vertical-align: middle;
+
+				.switch-button-group {
+					width: 96%;
+					height: 32px;
+					border: 1px solid #dfdfdf;
+					border-radius: 16px;
+					background-color: #dfdfdf;
+					display: flex;
+					flex: row;
+
+					.switch-button {
+						flex: 1;
+						border-radius: 16px;
+						color: #000000A5;
+						height: 32px;
+						line-height: 32px;
+						text-align: center;
+					}
+
+					.switch-button-checked {
+						box-shadow: 0px 2px 8px 0px #0000000C;
+						background-color: #fff;
+						color: #262626;
+					}
 
 				}
+
 			}
 
 		}
 
+
+
+		.red {
+			color: red !important;
+		}
 
 		.popup-content {
 			display: flex;
@@ -325,15 +2684,15 @@
 		}
 
 		.popup-content-menu {
-			margin-top: 75px;
-			margin-left: 120px;
-			width: 150px;
+			margin-top: 140rpx;
+			margin-left: 225rpx;
+			width: 300rpx;
 			align-items: center;
 			justify-content: center;
 			flex-direction: column;
 			background-color: #fff;
 			border-radius: 5px;
-			border: 1px solid gray;
+			border: 1px solid #aaa;
 		}
 
 		.popup-content-menu-item {
@@ -341,16 +2700,69 @@
 			flex-wrap: nowrap;
 			flex-direction: row !important;
 			align-items: center;
-			padding: 4px 8px;
+			padding: 8px;
 			font-size: 16px;
+			border-bottom: 1px solid #aaa;
 
-			.img {
-				width: 20px;
-				height: 20px;
-				margin: 5px;
-			}
 		}
 
+		.popup-content-menu-item:last-child {
+			border-bottom: 0;
+		}
+
+		.popup-content-menu-item:hover {
+			background-color: #eee;
+		}
+
+		.popup-content-btn {
+			margin-bottom: 20rpx;
+			margin-left: 50rpx;
+			width: 600rpx;
+			background-color: #fff;
+			border-radius: 5px;
+			display: flex;
+
+			.img-button-group {
+				display: flex;
+				//width: 100%;
+				flex-direction: row;
+				border-radius: 10px;
+				border: 1px solid #fff;
+				font-size: 30rpx !important;
+
+				margin: auto;
+
+				.button {
+					margin: 10rpx 20rpx;
+
+					height: 144rpx !important;
+					border: 0;
+					display: flex;
+					flex-direction: column;
+					background-color: #00000000;
+
+					.img {
+						margin: auto;
+						width: 72rpx;
+						height: 72rpx;
+
+					}
+
+					.ico {
+						margin: auto;
+						font-size: 72rpx;
+						color: #1890FF;
+					}
+
+					.text {
+						margin: auto;
+
+					}
+				}
+
+			}
+
+		}
 
 
 	}
diff --git a/pages/map/index2.vue b/pages/map/index2.vue
new file mode 100644
index 0000000..3d67348
--- /dev/null
+++ b/pages/map/index2.vue
@@ -0,0 +1,383 @@
+<template>
+	<view class="pages-map">
+		<view class="view-content">
+			<view class="no-content" v-if="unlinked">
+				<image class="img" src="/images/image 25.png" alt=" 鍥剧墖" mode="aspectFit" />
+				<view class="title">杞﹁締杩炴帴澶辫触</view>
+				<view class="space">璇锋鏌ヤ綘鐨勭綉缁滆缃垨閲嶆柊鍔犺浇</view>
+			</view>
+			<view class="map-content" v-else-if="sceneList.length ==0">
+				<image class="img" src="/images/image 25.png" alt=" 鍥剧墖" mode="aspectFit" />
+				<view class="space">娌℃湁鎵惧埌濡備綍鏉′欢鐨勫湴鍥�/view>
+				<view class="text-button-group">
+					<a-button type="primary" class="button" @click="clickStartConstructScene">
+						寮�鏋勫缓
+					</a-button>
+					<a-button type="primary" class="button" @click="clickDownloadScene">
+						涓嬭浇鍦烘櫙
+					</a-button>
+				</view>
+
+			</view>
+			<view class="map-content" v-else>
+				<view class="text-button-group">
+					<a-button type="primary" class="button" @click="clickStationList">
+						绔欑偣鍒楄〃
+					</a-button>
+					<a-button type="primary" class="button" @click="clickTeachingList">
+						绀烘暀璺嚎鍒楄〃
+					</a-button>
+				</view>
+
+			</view>
+		</view>
+		<view class="bottom" v-if="sceneList.length > 0">
+
+			<view class="bottom-content">
+				<view class="img-button-group">
+					<view fill="none" class="button" @click="clickMapStation">
+						<text class="ico location1"></text>
+						<view class="text"> 娣诲姞绔欑偣</view>
+					</view>
+					<view type="text" class="button" @click="clickMapEdit">
+						<text class="ico edit-line"></text>
+						<view class="text"> 缂栬緫鍦板浘</view>
+					</view>
+					<view type="text" class="button" @click="clickMapTask">
+						<text class="ico task-list"></text>
+						<view class="text">杞﹁締浠诲姟</view>
+
+					</view>
+				</view>
+
+			</view>
+		</view>
+
+	</view>
+</template>
+<script>
+	import {
+		showToast,
+		showModal
+	} from "@/comm/utils.js"
+	// import OIFabric from "@/components/oi-fabric/index.vue"
+	import {
+		Button
+	} from 'antd-mobile-vue-next'
+
+	import {
+		getAgvState,
+		getAllScene
+	} from "@/api/vehicle.js"
+	export default {
+		name: "PagesMap",
+		components: {
+			'a-button': Button
+		},
+		data() {
+			return {
+				navigationBarTitle: "",
+				vehicleIp: "",
+				stationList: [],
+				sceneList: [{name:"鍦板浘1"}],
+				unlinked: true
+			}
+		},
+		computed: {
+			getMaxStationNo() {
+				let num = 0
+				this.stationList.forEach((item) => {
+					if (num < item.stationID) {
+						num = item.stationID
+					}
+				})
+				return num
+			},
+		},
+		watch: {
+
+		},
+		onLoad(option) {
+			const _this = this
+			this.vehicleIp = option.ip || ""
+
+			this.loadData()
+
+		},
+		methods: {
+			setData(obj) {
+				let that = this;
+				let keys = [];
+				let val, data;
+
+				Object.keys(obj).forEach(function(key) {
+					keys = key.split(".");
+					val = obj[key];
+					data = that.$data;
+					keys.forEach(function(key2, index) {
+						if (index + 1 == keys.length) {
+							that.$set(data, key2, val);
+						} else {
+							if (!data[key2]) {
+								that.$set(data, key2, {});
+							}
+						}
+						data = data[key2];
+					});
+				});
+			},
+			async loadData() {
+				try {
+					const res = await getAllScene(this.vehicleIp)
+					//this.sceneList = res.sceneList || []
+					this.setData({
+						unlinked: false
+					})
+				} catch (ex) {
+					this.setData({
+						unlinked: true
+					})
+
+				}
+			},
+			clickShowMenu() {
+				this.$refs.refPopupMenu.open("top")
+			},
+			clickBack() {
+				uni.navigateBack({
+					delta: 1
+				})
+
+			},
+			clickStationList() {
+				uni.navigateTo({
+					url: `/pages/station/index?ip=${this.vehicleIp}`
+				})
+			},
+			clickTeachingList() {
+				uni.navigateTo({
+					url: `/pages/teaching/list?ip=${this.vehicleIp}`
+				})
+			},
+			clickMapStation() {
+				uni.navigateTo({
+					url: `/pages/station/index?ip=${this.vehicleIp}&isAdd=1`
+				})
+			},
+			clickMapEdit() {
+				uni.navigateTo({
+					url: `/pages/map/edit?ip=${this.vehicleIp}`
+				})
+			},
+			clickMapTask() {
+				uni.navigateTo({
+					url: `/pages/map/task?ip=${this.vehicleIp}`
+				})
+			},
+			clickStartConstructScene() {
+				uni.navigateTo({
+					url: `/pages/scene/index?ip=${this.vehicleIp}`
+				})
+			},
+			clickDownloadScene() {
+
+			},
+			showError(ex) {
+				let exStr = JSON.stringify(ex)
+				if (exStr == "{}")
+					exStr = ex
+				let tip = typeof ex.msg == "string" ? ex.msg : exStr
+				showModal(tip, "閿欒", false,"纭畾")
+			},
+
+
+		}
+	}
+</script>
+
+<style lang="scss">
+	.pages-map {
+		display: flex;
+		width: 750rpx;
+		height: 100vh;
+		background-color: #fff;
+		position: relative;
+
+		.uni-navbar-container-inner {
+			display: flex;
+			flex: 1;
+			flex-direction: row;
+			align-items: center;
+			justify-content: center;
+			font-size: 40rpx;
+			overflow: hidden;
+			font-weight: 700;
+			color: #333;
+
+			.icon {
+				width: 30rpx;
+				height: 16rpx;
+			}
+		}
+
+		.uni-nav-bar-text {
+			overflow: hidden;
+			white-space: nowrap;
+			text-overflow: ellipsis;
+		}
+
+		.view-content {
+			position: absolute;
+			top: 0;
+			left: 0;
+			right: 0;
+			bottom: 0;
+			display: flex;
+			flex-direction: column;
+		}
+
+		.map-content {
+			width: 100%;
+			display: flex;
+			flex: 1;
+			margin: auto;
+			flex-direction: column;
+			align-items: center;
+			justify-content: center;
+
+			.text-button-group {
+				display: flex;
+				width: 100%;
+				justify-content: center;
+				align-items: center;
+				flex-direction: column;
+				font-size: 30rpx !important;
+
+				.button {
+					margin-top: 20rpx;
+					width: 375rpx;
+				}
+			}
+
+
+		}
+
+		.no-content {
+			margin-top: 50px;
+			padding: 20rpx 40rpx;
+			align-items: center;
+			text-align: center;
+			display: flex;
+			flex-direction: column;
+			font-size: 30rpx;
+			font-weight: 400;
+
+			.title {
+				font-size: 40rpx;
+				margin-bottom: 10rpx;
+			}
+
+
+		}
+
+		.bottom {
+			position: fixed;
+			left: 50rpx;
+			right: 50rpx;
+			bottom: 20rpx;
+			display: flex;
+
+			.bottom-content {
+				display: flex;
+				flex-direction: column;
+				width: 100%;
+				// justify-content: center;
+				// align-items: center;
+				padding: 0 10rpx;
+				padding-bottom: 10rpx;
+				background-color: #fff;
+				border-radius: 10rpx;
+			}
+
+
+
+		}
+
+		.img-button-group {
+			display: flex;
+			//width: 100%;
+			flex-direction: row;
+			border-radius: 10px;
+			border: 1px solid #fff;
+			font-size: 30rpx !important;
+			justify-content: space-around;
+
+			.button {
+				margin: 10rpx 20rpx;
+
+				height: 144rpx !important;
+				border: 0;
+				display: flex;
+				flex-direction: column;
+				background-color: #00000000;
+
+				.img {
+					margin: auto;
+					width: 72rpx;
+					height: 72rpx;
+
+				}
+
+				.ico {
+					margin: auto;
+					font-size: 72rpx;
+					color: #1890FF;
+				}
+
+				.text {
+					margin: auto;
+
+				}
+			}
+
+		}
+
+
+		.popup-content {
+			display: flex;
+			justify-content: center;
+			flex-direction: column;
+			background-color: transparent;
+		}
+
+		.popup-content-menu {
+			margin-top: 75px;
+			margin-left: 120px;
+			width: 150px;
+			align-items: center;
+			justify-content: center;
+			flex-direction: column;
+			background-color: #fff;
+			border-radius: 5px;
+			border: 1px solid gray;
+		}
+
+		.popup-content-menu-item {
+			display: flex;
+			flex-wrap: nowrap;
+			flex-direction: row !important;
+			align-items: center;
+			padding: 4px 8px;
+			font-size: 16px;
+
+			.img {
+				width: 20px;
+				height: 20px;
+				margin: 5px;
+			}
+		}
+
+
+
+	}
+</style>
\ No newline at end of file
diff --git a/pages/map/infos/scene-create.vue b/pages/map/infos/scene-create.vue
new file mode 100644
index 0000000..793bc09
--- /dev/null
+++ b/pages/map/infos/scene-create.vue
@@ -0,0 +1,342 @@
+<template>
+	<view class="pages-map-scene-create">
+		<view class="scene-content" v-if="opSceneType =='' ">
+			<image class="img" src="/images/image 25.png" alt=" 鍥剧墖" mode="aspectFit" />
+			<view class="space">娌℃湁鎵惧埌绗﹀悎鏉′欢鐨勫湴鍥�/view>
+			<view class="text-button-group">
+				<a-button type="primary" class="button" @click="clickStartConstructScene">
+					寮�鏋勫缓
+				</a-button>
+				<a-button type="primary" class="button" @click="clickDownloadScene" disabled>
+					涓嬭浇鍦烘櫙
+				</a-button>
+			</view>
+
+		</view>
+		<view class="bottom">
+			<view class="bottom-content" v-if="opSceneType =='add_name' ">
+				<view class="tip">璇疯緭鍏ュ満鏅悕绉�/view>
+				<view class="name-input">
+					<input ref="refInputName" :focus="true" placeholder="璇疯緭鍏ュ満鏅悕绉� :value="sceneName"
+						@input="onInputName"></input>
+					<uni-icons class="clear" color="#ccc" type="clear" size="20" v-if="showClearName"
+						@click="clickClearName"></uni-icons>
+				</view>
+				<view class="text-button-group">
+					<a-button type="primary" class="button" :disabled="sceneName.trim() == ''"
+						@click="clickNameOK">纭</a-button>
+					<a-button type="ghost" class="button" @click="clickNameCancel">鍙栨秷</a-button>
+
+				</view>
+			</view>
+			<view class="bottom-content" v-else-if="opSceneType =='scan'">
+				<view class="tip">鍦烘櫙鏋勫缓涓�/view>
+				<view>
+					璇锋搷浣滄惉杩愯溅鎵弿鍦板浘瑕嗙洊鐨勫尯鍩�+				</view>
+
+				<view class="text-button-group">
+					<a-button type="primary" class="button" @click="clickScanFinish">鎵弿瀹屾垚</a-button>
+				</view>
+			</view>
+			<view class="bottom-content" v-else-if="opSceneType =='finish'">
+				<view class="tip">鍦烘櫙鏋勫缓瀹屾垚</view>
+				<view>
+					宸叉垚鍔熸瀯寤衡�{{sceneName}}鈥�+				</view>
+
+				<view class="text-button-group">
+					<a-button type="primary" class="button" @click="clickFinish">鏋勫缓瀹屾垚</a-button>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+<script>
+	import {
+		showToast,
+		showModal,
+		session,
+	} from "@/comm/utils.js"
+	import {
+		Button
+	} from 'antd-mobile-vue-next'
+
+	import {
+		createScene,
+		addMap,
+		stopMap,
+		//getAgvState,
+		getMapLaserData
+
+	} from "@/api/vehicle.js"
+	export default {
+		name: "PagesMapSceneCreate",
+		components: {
+			'a-button': Button
+		},
+		props: {
+			ip: {
+				type: String,
+				default () {
+					return ''
+				}
+			},
+			opSceneType: {
+				type: String,
+				default () {
+					return ''
+				}
+			},
+		},
+		data() {
+			return {
+				windowWidth: 375,
+				sceneName: "",
+				showClearName: false,
+
+			}
+		},
+		computed: {
+
+		},
+		mounted() {
+			const _this = this
+			uni.getSystemInfo({
+				success(e) {
+					_this.windowWidth = e.windowWidth
+				},
+
+			})
+			this.loadData()
+
+		},
+		methods: {
+			setData(obj) {
+				let that = this;
+				let keys = [];
+				let val, data;
+
+				Object.keys(obj).forEach(function(key) {
+					keys = key.split(".");
+					val = obj[key];
+					data = that.$data;
+					keys.forEach(function(key2, index) {
+						if (index + 1 == keys.length) {
+							that.$set(data, key2, val);
+						} else {
+							if (!data[key2]) {
+								that.$set(data, key2, {});
+							}
+						}
+						data = data[key2];
+					});
+				});
+			},
+			async loadData() {
+				try {
+
+
+				} catch (ex) {
+
+					this.showError(ex)
+				}
+			},
+			async loadMapLaserData() {
+				try {
+					const info = await getMapLaserData(this.ip)
+					return info
+				} catch (ex) {
+					this.showError(ex)
+					return {}
+				}
+			},
+
+
+
+			clickStartConstructScene() {
+				this.$emit('update:opSceneType', "add_name");
+			},
+			clickDownloadScene() {
+				showToast("鏈疄鐜�)
+			},
+
+			clickNameCancel() {
+				uni.navigateBack({
+					delta: 1, //杩斿洖灞傛暟锛�鍒欎笂涓婇〉
+				})
+			},
+			async clickNameOK() {
+				try {
+					const name = this.sceneName.trim()
+					if (!name) {
+						showToast("绔欑偣鍚嶇О杩樻湭杈撳叆")
+						return
+					}
+					this.sceneName = name
+					await createScene(this.ip, name, 1)
+					this.$emit('update:opSceneType', "scan");
+				} catch (ex) {
+					this.showError(ex)
+				}
+
+			},
+			clickClearName() {
+				this.sceneName = ""
+				this.showClearName = false
+			},
+			onInputName(event) {
+				this.sceneName = event.detail.value;
+				if (this.sceneName)
+					this.showClearName = true
+				else
+					this.showClearName = false
+			},
+			async clickScanFinish() {
+				try {
+					this.$emit('update:opSceneType', "finish");
+					uni.setNavigationBarTitle({
+						title: this.sceneName
+					})
+					await createScene(this.ip, this.sceneName, 0)
+				} catch (ex) {
+
+					showModal("璇锋鏌ヨ溅杈嗚繛鎺ワ紝骞堕噸鏂板紑濮嬫瀯寤哄満鏅�, "鍦烘櫙鏋勫缓澶辫触", false, "纭畾").then((res) => {
+						this.$emit('update:opSceneType', "add_name");
+					})
+				}
+
+			},
+			clickFinish() {
+				this.$emit('create-ok', this.sceneName);
+				this.sceneName = ""
+			},
+			showError(ex) {
+				console.log(ex)
+				let exStr = JSON.stringify(ex)
+				if (exStr == "{}")
+					exStr = ex
+				let tip = typeof ex.msg == "string" ? ex.msg : exStr
+				showModal(tip, "閿欒", false, "纭畾")
+			},
+
+
+		}
+	}
+</script>
+
+<style lang="scss">
+	.pages-map-scene-create {
+		display: flex;
+		width: 100%;
+		height: 100%;
+		position: relative;
+		background-color: #fff;
+		.scene-content {
+			width: 100%;
+			display: flex;
+			flex: 1;
+			flex-direction: column;
+			margin: auto;
+			align-items: center;
+			justify-content: center;
+
+			.text-button-group {
+				display: flex;
+				width: 100%;
+				justify-content: center;
+				align-items: center;
+				flex-direction: column;
+				font-size: 30rpx !important;
+
+				.button {
+					margin-top: 20rpx;
+					width: 375rpx;
+				}
+			}
+
+		}
+
+
+		.bottom {
+			position: fixed;
+			left: 50rpx;
+			right: 50rpx;
+			bottom: 20rpx;
+			display: flex;
+
+			.bottom-content {
+				display: flex;
+				flex-direction: column;
+				width: 100%;
+				// justify-content: center;
+				// align-items: center;
+				padding: 0 10rpx;
+				padding-bottom: 10rpx;
+				background-color: #fff;
+				border-radius: 10rpx;
+				//background-color: #eee;
+
+				.title {
+					font-size: 40rpx;
+					margin-bottom: 10rpx;
+				}
+
+				.tip {
+					color: #888;
+					margin: 10rpx;
+					text-align: left;
+				}
+
+
+				.disabled {
+					color: #ccc !important;
+				}
+
+				.name-input {
+					width: calc(100% - 20rpx);
+					margin-bottom: 10rpx;
+					background-color: #fff;
+					padding: 10rpx;
+					border-radius: 8rpx;
+					display: flex;
+					flex-direction: row;
+					color: #1890FF;
+
+					input {
+						flex: 1;
+					}
+				}
+			}
+
+
+			.text-button-group {
+				display: flex;
+				width: 100%;
+				justify-content: center;
+				align-items: center;
+				flex-direction: column;
+				font-size: 30rpx !important;
+
+				.button {
+					margin: auto;
+					margin-top: 20rpx;
+					width: calc(100% - 10rpx);
+				}
+
+				.am-button {
+					border-radius: 30px;
+				}
+
+				.am-button-ghost {
+					border: 1px solid #1677ff !important;
+
+				}
+			}
+
+
+		}
+
+
+	}
+</style>
\ No newline at end of file
diff --git a/pages/map/js/ctx-mini.js b/pages/map/js/ctx-mini.js
new file mode 100644
index 0000000..a07a0b5
--- /dev/null
+++ b/pages/map/js/ctx-mini.js
@@ -0,0 +1,3512 @@
+import * as fabric from "fabric" //'../../../components/fabric';
+import {
+	v4 as uuidv4
+} from 'uuid';
+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 {
+			vehicleIp: "",
+			canvasId: "",
+			canvas: null,
+			eleWidth: 0,
+			eleHeight: 0,
+			touchStartPoint: {
+				x: 0,
+				y: 0
+			},
+			isDrawing: false,
+			drawType: "",
+			drawSvg: "",
+			initialDistance: null,
+			initialZoom: 1,
+			isDragging: false,
+			lastPosX: 0,
+			lastPosY: 0,
+			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
+
+		}
+	},
+	mounted() {
+		console.log("ctx mounted")
+		this.init()
+	},
+	methods: {
+		async init(ip) {
+			try {
+				this.agvObj = null
+				const _this = this
+				fabric.Object.prototype.setControlsVisibility({
+					mt: false, // 涓棿涓�+					mb: false, // 涓棿涓�+					ml: false, // 涓棿宸�+					mr: false, // 涓棿鍙�+					mtr: false, // 鏃嬭浆鎺у埗
+
+					tl: false, //鏄惁鏄剧ず涓棿妯潬
+					bl: false, //鏄惁鏄剧ず涓棿妯潬
+					tr: false, //鏄惁鏄剧ず涓棿妯潬
+					br: false, //鏄惁鏄剧ず涓棿妯潬
+				})
+				// 鍏ㄥ眬鏍峰紡璋冩暣
+				fabric.Object.prototype.set({
+					borderColor: '#1890FF',
+					cornerColor: '#fff',
+					cornerStrokeColor: '#1890FF',
+					cornerSize: 36,
+					//	touchCornerSize: 48,
+					transparentCorners: false,
+					cornerStyle: 'circle',
+					borderScaleFactor: 2,
+					padding: 5,
+
+				});
+				this.canvasId = `canvas_${uuidv4()}`
+				const cantainerEl = document.getElementById("canvasMap")
+				let canvas = document.getElementById(this.canvasId)
+				canvas = document.createElement("canvas")
+				canvas.setAttribute("id", this.canvasId)
+				canvas.setAttribute("type", "2d")
+				cantainerEl.appendChild(canvas)
+				this.canvas = new fabric.Canvas(this.canvasId, {
+					allowTouchScrolling: true, // 鍏佽瑙︽懜婊氬姩
+					selection: true,
+					isTouchDevice: true, // 鍏抽敭閰嶇疆
+					stopContextMenu: true, // 绂佹闀挎寜鑿滃崟
+					fireRightClick: true,
+					fireMiddleClick: true,
+					targetFindTolerance: 10, // 澧炲ぇ瑙︽懜瀹瑰樊
+					isTouchSupported: true,
+					enableRetinaScaling: true,
+					renderOnAddRemove: false,
+					imageSmoothingEnabled: true
+				})
+				this.canvas.clear()
+				this.eleWidth = cantainerEl.clientWidth
+				this.eleHeight = cantainerEl.clientHeight
+				this.canvas.setWidth(this.eleWidth);
+				this.canvas.setHeight(this.eleHeight);
+				this.workSpace = new fabric.Rect({
+					id: "workspace",
+					eleType: "workspace",
+					left: 0,
+					top: 0,
+					width: this.eleWidth,
+					height: this.eleHeight,
+					selectable: false,
+					hasControls: false,
+					fill: "#FFFFFF"
+				})
+				this.canvas.add(this.workSpace)
+				this.patchFabricForUniApp(this.canvas)
+				this.initFlag = true
+				await this.$nextTick();
+				this.canvasEventListener()
+
+			} catch (ex) {
+				this.showError(ex)
+			}
+		},
+		patchFabricForUniApp(canvas) {
+			// 淇濆瓨鍘熷鏂规硶
+			const originalSetActiveObject = canvas.setActiveObject.bind(canvas);
+
+			// 閲嶅啓setActiveObject
+			canvas.setActiveObject = function(object, e) {
+				originalSetActiveObject(object, e);
+				if (object) {
+					object.canvas = this;
+					this._activeObject = object;
+				}
+			};
+
+			// 纭繚瀵硅薄鍒濆鍖栨椂璁剧疆姝g‘鐨刢anvas寮曠敤
+			fabric.Object.prototype.initialize = function(options) {
+				const originalInit = fabric.util.getOriginalMethod(this, 'initialize');
+				originalInit.call(this, options);
+
+				if (this.canvas && this.canvas._activeObject === this) {
+					this.canvas._activeObject = this;
+				}
+				return this;
+			};
+		},
+		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,
+			});
+
+			this.canvas.requestRenderAll()
+		},
+		editOkObject(eventData, transform, x, y) {
+			const target = transform.target;
+			const canvas = target.canvas;
+			this.$ownerInstance.callMethod('receiveRenderData', {
+				method: "edit_finish",
+				cmd: "ok",
+				type: target.eleType,
+				data: target.data,
+				station: target.data,
+			});
+			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) {
+			// 鍒涘缓鍒犻櫎鎸夐挳鐨勫浘鐗囧厓绱�+			var scale = this.canvas.getZoom()
+			if (scale < 1) {
+				scale = 1
+			}
+			var scale2 = 1
+			var offset = 50
+
+			//if (obj.eleType == "station" || obj.eleType == "edit_teaching") {
+			scale2 = 1 / scale
+
+			//}
+			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)
+
+		},
+
+		canvasEventListener() {
+			const _this = this
+
+			const cantainerEl = document.getElementById("canvasMap")
+			_this.canvas.on('touch:gesture', function(e) {
+				console.log("touch:gesture", e);
+				// 澶勭悊鎵嬪娍鎿嶄綔
+			});
+			_this.canvas.on("selection:created", function(e) {
+				const activeObj = _this.canvas.getActiveObject();
+				activeObj.set({
+					borderColor: '#1890ff',
+					borderScaleFactor: 3,
+					padding: 5,
+					borderDashArray: [5, 5], //5px 瀹炵嚎鍜�px 闂撮殧
+				});
+				_this.canvas.requestRenderAll();
+
+				_this.onSelectionChanage()
+				//_this.selectionChangeCanvas();
+			});
+			_this.canvas.on("selection:updated", function(e) {
+				console.log("selection:updated", e);
+				const activeObj = _this.canvas.getActiveObject();
+
+				activeObj.set({
+					borderColor: '#1890ff',
+					borderScaleFactor: 3,
+					padding: 3,
+					borderDashArray: [5, 5], //5px 瀹炵嚎鍜�px 闂撮殧
+				});
+				_this.canvas.requestRenderAll();
+				_this.onSelectionChanage()
+
+
+			});
+			_this.canvas.on("selection:cleared", function(e) {
+				//	console.log("selection:cleared", e);
+
+				//_this.selectionChangeCanvas();
+			});
+			_this.canvas.on("object:removed", function(e) {
+				// console.log("object:removed", e.target);
+				if (e.target) {
+					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);
+
+
+				// _this.resizetCanvas();
+			});
+			_this.canvas.on("object:moving", function(e) {
+				console.log("object:moving", e.target);
+				_this.onOjectMoving(e.target)
+
+			});
+
+
+			cantainerEl.addEventListener('touchstart', function(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;
+
+						const list = _this.canvas.getActiveObjects()
+						if (list.length === 1) {
+							if (!_this.objEditing) {
+								this.pressObjTimer = setTimeout(function() {
+
+									const zoom = _this.canvas.getZoom();
+									const vpt = _this.canvas.viewportTransform;
+									if (list[0].eleType == "station") {
+										_this.setAllObjectSelectable(false)
+
+										list[0].set({
+											selectable: true
+										})
+
+										let deltaX = list[0].left * zoom
+										let deltaY = list[0].top * zoom
+										const scale = zoom < 1 ? zoom : 1
+										_this.$ownerInstance.callMethod('receiveRenderData', {
+											method: "edit_station",
+											station: list[0].data,
+											view: {
+												x: vpt[4] + deltaX,
+												y: vpt[5] + deltaY,
+												// x: e.touches[0].clientX,
+												// y: e.touches[0].clientY,
+												width: list[0].width * scale,
+												height: list[0].height * scale
+											}
+										});
+									} else if (list[0].eleType == "public_teaching") {
+										const pt = _this.canvas.getPointer(touch); // 鈫�鍏抽敭
+										// 2. pointer 灏辨槸鐢诲竷鍧愭爣
+
+										_this.$ownerInstance.callMethod('receiveRenderData', {
+											method: "select_teaching_path",
+											data: list[0].data,
+											type: "public",
+											point: pt
+
+
+										});
+									} else if (list[0].eleType == "station_teaching") {
+										_this.$ownerInstance.callMethod('receiveRenderData', {
+											method: "select_teaching_path",
+											data: list[0].data,
+											type: "station",
+											point: pt
+										});
+									}
+								}, 1000); //
+							}
+							let activeObj = list[0]
+							if (activeObj.eleType == "region_pt_add") {
+								const data = activeObj.mainObj?.data
+
+								data.path.push({
+									x: _this.getActualXFromImg(activeObj.left),
+									y: _this.getActualYFromImg(activeObj.top)
+								})
+								_this.updateRegion(activeObj.mainObj, data)
+							} else if (activeObj.eleType == "cmd") {
+								let data = activeObj.mainObj.data
+								if (activeObj.mainObj.eleType == "edit_teaching") {
+
+									let left = _this.getActualXFromImg(activeObj.left)
+									let top = _this.getActualYFromImg(activeObj.top)
+									let right = _this.getActualXFromImg(activeObj.left + activeObj.width)
+									let bottom = _this.getActualYFromImg(activeObj.top + activeObj.height)
+									data = [
+										[left, top],
+										[left, bottom],
+										[right, bottom],
+										[right, top]
+									]
+									console.log(data)
+								}
+								_this.$ownerInstance.callMethod('receiveRenderData', {
+									method: "edit_finish",
+									cmd: activeObj.id,
+									type: activeObj.mainObj.eleType,
+									data: data,
+								});
+								if (activeObj.id == "ok") {
+									if (activeObj.mainObj.eleType == "region") {
+										_this.addRegionFinish(activeObj.mainObj)
+									} else if (activeObj.mainObj.eleType == "virtual_wall") {
+										_this.addVirtualWallFinish(activeObj.mainObj)
+									}
+
+								}
+								if (activeObj.mainObj.eleType == "edit_teaching") {
+									let list = _this.canvas.getObjects() || []
+									for (let i2 in list) {
+										const obj = list[i2]
+										obj.set({
+											opacity: 1
+										})
+									}
+									if (activeObj.mainObj?.mainObj) {
+										activeObj.mainObj.mainObj.set({
+											selectable: true
+										})
+									}
+									const ptObjs = activeObj.mainObj.ptObjs || []
+									_this.canvas.remove(activeObj.mainObj)
+									for (let i2 in ptObjs) {
+										const obj = ptObjs[i2]
+										_this.canvas.remove(obj)
+									}
+								}
+								if (activeObj.mainObj.eleType == "station") {
+
+									_this.setAllObjectSelectable(true)
+									activeObj.mainObj.tipObj.set({
+										left: activeObj.mainObj.left,
+										top: activeObj.mainObj.top - activeObj.mainObj.height / 2 -
+											activeObj.mainObj
+											.tipObj.height / 2,
+										visible: true
+									})
+									activeObj.mainObj.tipObj.setCoords()
+								}
+								_this.closeOkCancelControl()
+
+								_this.canvas.requestRenderAll()
+							}
+							// else if (activeObj.eleType == "station") {
+							// 	_this.$ownerInstance.callMethod('receiveRenderData', {
+							// 		method: "select_station",
+							// 		data: activeObj.data,
+							// 		select: activeObj.mark ? false : true
+							// 	});
+							// }
+						}
+					} else if (e.touches.length >= 2) {
+						_this.handleMultiTouch(e.touches);
+					}
+				}
+			});
+
+			cantainerEl.addEventListener('touchmove', function(e) {
+				//	_this.canvas._onMouseMove(e);
+				//	console.log('touchmove:', e.touches.length);
+				e.preventDefault(); // 闃绘榛樿琛屼负
+				// 澶勭悊绉诲姩
+				const list = _this.canvas.getActiveObjects()
+				if (list.length == 0) {
+					if (e.touches.length === 1) {
+						_this.handleSingleTouchMove(e.touches[0]);
+					} else if (e.touches.length >= 2) {
+						_this.handleMultiTouchMove(e.touches);
+					}
+				} else {
+					if (e.touches.length === 1) {
+						if (list.length > 1 || list[0].lockMovementX) {
+							if (list[0].lockMovementX) {
+								_this.canvas.discardActiveObject();
+							}
+							_this.handleSingleTouchMove(e.touches[0]);
+						} else {
+							const touch = e.touches[0]
+							const deltaX = touch.clientX - this.lastPosX;
+							const deltaY = touch.clientY - this.lastPosY;
+							if (Math.abs(deltaX) > 20)
+								clearTimeout(this.pressObjTimer);
+						}
+
+					} else if (e.touches.length >= 2) {
+						_this.canvas.discardActiveObject();
+						_this.handleMultiTouchMove(e.touches);
+					}
+				}
+			});
+
+			cantainerEl.addEventListener('touchend', function(e) {
+				//	_this.canvas._onMouseUp(e);
+				//	console.log('touchend:');
+				e.preventDefault(); // 闃绘榛樿琛屼负
+				_this.touchPoint = {
+					x: 0,
+					y: 0
+				};
+				const activeObj = _this.canvas.getActiveObject()
+				if (!activeObj) {
+					// 澶勭悊缁撴潫浜嬩欢
+					if (e.touches.length === 0) {
+						_this.handleTouchEnd();
+					}
+					// if (_this.editObject) {
+					// 	_this.canvas.setActiveObject(_this.editObject)
+					// }
+				} else {
+					// if (activeObj.lockMovementX) {
+					// 	_this.canvas.discardActiveObject();
+					// }
+				}
+				if (this.pressObjTimer) {
+					clearTimeout(this.pressObjTimer);
+					this.pressObjTimer = null
+				}
+			});
+			cantainerEl.addEventListener('touchcancel', function(e) {
+				//	console.log('touchcancel:');
+				if (this.pressObjTimer) {
+					clearTimeout(this.pressObjTimer);
+					this.pressObjTimer = null
+				}
+				// const activeObj = _this.canvas.getActiveObject()
+				// if (activeObj) {
+				// 	if (activeObj.lockMovementX) {
+				// 		_this.canvas.discardActiveObject();
+				// 	}
+				// }
+			})
+
+		},
+		// 璁$畻鐐瑰埌绾挎鐨勮窛绂�+		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()
+			console.log("pointer", pointer.x, pointer.y, objects.length)
+			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)) {
+							pointerList.push(obj)
+					
+						}
+					} else {
+						const isHit = obj.containsPoint(pointer);
+						if (isHit) {
+							pointerList2.push(obj)
+					
+						}
+					}
+				}
+
+			}
+			if (pointerList2.length > 0) {
+				const obj = pointerList2[0];
+				console.log('鍛戒腑瀵硅薄锛�, obj.eleType);
+				this.canvas.discardActiveObject()
+				this.canvas.setActiveObject(obj);
+				this.canvas.requestRenderAll();
+				return
+			}
+			if (pointerList.length > 0) {
+				const obj = pointerList[0];
+				console.log('鍛戒腑瀵硅薄2锛�, obj.eleType);
+				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)
+			return new Promise((resolve) => {
+				const img = new Image();
+				img.onload = () => {
+					// 妫�煡灏哄鏄惁瓒呭嚭闄愬埗
+					const scale = Math.min(
+						maxSize / Math.max(img.width, img.height),
+						1
+					);
+					resolve(new fabric.Image(img, {
+						scaleX: scale,
+						scaleY: scale
+					}));
+				};
+				img.onerror = () => {
+					console.error('鍥剧墖鍔犺浇澶辫触');
+					resolve(null);
+				};
+				img.src = url;
+			});
+		},
+		safeLoadImageData(data, maxSize = 2048) {
+			//console.log("safeLoadImageData")
+			const _this = this
+			return new Promise((resolve) => {
+				let base64Image = data
+				if (base64Image.indexOf("data:image/png;base64,") < 0) {
+					base64Image = "data:image/png;base64," + data
+				}
+				// var img = new fabric.Image();
+				// img.setSrc(base64Image, function() {
+				// 	console.log("img", JSON.stringify(img))
+				// 	const scale = Math.min(
+				// 		maxSize / Math.max(img.width, img.height),
+				// 		1
+				// 	);
+				// 	img.set({
+				// 		scaleX: scale,
+				// 		scaleY: scale
+				// 	})
+				// 	resolve(img)
+				// });
+				fabric.Image.fromURL(base64Image, {
+					crossOrigin: 'anonymous' // 閲嶈锛氳缃法鍩�+				}).then((img) => {
+
+					//console.log("img",JSON.stringify(img))
+					const scale = Math.min(
+						maxSize / Math.max(img.width, img.height),
+						1
+					);
+					img.set({
+						scaleX: scale,
+						scaleY: scale,
+					})
+					resolve(img)
+				}).catch((err) => {
+					console.error("鍥剧墖鍔犺浇澶辫触", err)
+					_this.showError("鍥剧墖鍔犺浇澶辫触")
+					resolve(null);
+				})
+
+			});
+		},
+		// 灏�Base64 杞负 Blob锛屽啀鐢熸垚 URL
+		base64ToBlob(base64, mime) {
+		  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) => {
+				// 鍒涘缓涓存椂Image瀵硅薄鐩戞帶鍔犺浇杩涘害
+				var base64Image = data
+				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",
+				});
+				const img = new Image();
+				img.onloadstart = () => {
+					_this.$ownerInstance.callMethod('receiveRenderData', {
+						method: "set_backgroud_progress",
+						type: "progress",
+						percent: 5
+					});
+				};
+				img.onprogress = (e) => {
+					if (e.lengthComputable) {
+						const percent = Math.min(99, Math.round((e.loaded / e.total) * 100));
+						_this.$ownerInstance.callMethod('receiveRenderData', {
+							method: "set_backgroud_progress",
+							type: "progress",
+							percent
+						});
+					} else {
+						// 鏃犳硶璁$畻杩涘害鏃剁殑妯℃嫙杩涘害
+						const percent = Math.min(this.progressPercent + 10, 95);
+						_this.$ownerInstance.callMethod('receiveRenderData', {
+							method: "set_backgroud_progress",
+							type: "progress",
+							percent
+						});
+					}
+				};
+				img.onload = () => {
+					// 鐢ㄥ畬閲婃斁鍐呭瓨
+					URL.revokeObjectURL(url);
+					const percent = Math.min(this.progressPercent + 10, 95);
+					_this.$ownerInstance.callMethod('receiveRenderData', {
+						method: "set_backgroud_progress",
+						type: "progress",
+						percent: 100
+					});
+					// 杞崲涓篎abric鍥惧儚
+					const scale = Math.min(
+						maxSize / Math.max(img.width, img.height),
+						1
+					);
+					const fabricImg = new fabric.Image(img, {
+						left: 0,
+						top: 0,
+						scaleX: scale,
+						scaleY: scale,
+					});
+					_this.$ownerInstance.callMethod('receiveRenderData', {
+						method: "set_backgroud_progress",
+						type: "end",
+					});
+					resolve(fabricImg);
+				};
+
+				img.onerror = (err) => {
+					_this.$ownerInstance.callMethod('receiveRenderData', {
+						method: "set_backgroud_progress",
+						type: "error",
+					});
+					console.error("鍥剧墖鍔犺浇澶辫触", err)
+					reject(new Error('鍥剧墖鍔犺浇澶辫触'));
+				};
+				// 寮�鍔犺浇
+				//img.src = base64Image;
+				img.src = url;
+			});
+		},
+
+		// 鐩戞帶鍐呭瓨浣跨敤鎯呭喌
+		checkMemoryUsage() {
+			if (window.performance && window.performance.memory) {
+				const usedMB = window.performance.memory.usedJSHeapSize / (1024 * 1024);
+				console.log(`鍐呭瓨浣跨敤: ${usedMB.toFixed(2)}MB`);
+				return usedMB;
+			}
+			return null;
+		},
+
+		setBackground(info) {
+			const _this = this
+
+			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.agvObj = null
+			const cantainerEl = document.getElementById("canvasMap")
+			this.eleWidth = cantainerEl.clientWidth
+			this.eleHeight = cantainerEl.clientHeight
+			console.log("client", this.eleWidth, this.eleHeight)
+			this.canvas.setWidth(this.eleWidth);
+			this.canvas.setHeight(this.eleHeight);
+			this.mapInfo = {
+				proportion: info.proportion || 1,
+				img_proportion: info.img_proportion || 1,
+				max_x: info.max_x || 1,
+				max_y: info.max_y || 1,
+				min_x: info.min_x || 0,
+				min_y: info.min_y || 0,
+				img_x: info.img_x || 1,
+				img_y: info.img_y || 1,
+			}
+			return new Promise((resolve, reject) => {
+
+				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
+							}
+
+							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)",
+							})
+
+							img.set({
+								// id: "workspace",
+								// eleType: "workspace",
+								left: 0,
+								top: 0,
+								// selectable: false,
+								// hasControls: false,
+							});
+							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(wsGroup)
+							if (_this.workSpace) {
+								_this.canvas.remove(_this.workSpace)
+							}
+							_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()
+				}
+
+			})
+		},
+		getAutoScale() {
+			// 鎸夌収瀹藉害
+
+			const eleWidth = this.eleWidth - 20
+			const eleHeight = this.eleHeight - 200
+			if (!this.workSpace)
+				return 1
+			const width = this.workSpace.width
+			const height = this.workSpace.height
+			if (eleWidth / eleHeight < width / height) {
+				return eleWidth / width;
+			} // 鎸夌収瀹藉害缂╂斁
+			return eleHeight / height;
+		},
+
+		auto() {
+			const scale = this.getAutoScale()
+			this.setZoomAuto((98 * scale) / 100);
+		},
+		one() {
+			this.setZoomAuto(1, ); //
+			this.canvas.requestRenderAll();
+		},
+		setZoomAuto(scale, zoomPoint) {
+			const _this = this
+			let center = this.canvas.getCenter();
+			if (zoomPoint) {
+				this.canvas.zoomToPoint(new fabric.Point(zoomPoint.x, zoomPoint.y), scale);
+			} else {
+				this.canvas.zoomToPoint(new fabric.Point(center.left, center.top), scale);
+			}
+			this.canvas.fire("transform", {
+				scale: scale
+			});
+			if (!this.workSpace) return;
+			// this.setCenterFromObject(this.workSpace);
+			// 瓒呭嚭鐢诲竷涓嶅睍绀�+			// _this.workSpace.clone().then((cloned) => {
+			// 	_this.canvas.clipPath = cloned;
+			// 	_this.canvas.requestRenderAll();
+			// });
+		},
+		setDrawingType(type, svg) {
+			if (svg) {
+				if (type == "svg") {
+					this.drawType = "svg"
+					this.drawSvg = svg
+					return
+				}
+			}
+			if (type == "line") {
+				this.drawType = "line"
+			} else {
+				this.drawType = ""
+			}
+		},
+		deleteSelected() {
+			const list = this.canvas.getActiveObjects()
+			list.map((item) => this.canvas.remove(item));
+			this.canvas.requestRenderAll();
+			this.canvas.discardActiveObject();
+			this.canvas.fire("object:modified");
+
+		},
+		/**
+		 * 璁剧疆鐢诲竷涓績鍒版寚瀹氬璞′腑蹇冪偣涓�+		 * @param {Object} obj 鎸囧畾鐨勫璞�+		 */
+		setCenterFromObject(obj) {
+			const objCenter = obj.getCenterPoint();
+			const viewportTransform = this.canvas.viewportTransform;
+			if (this.canvas.width === undefined || this.canvas.height === undefined || !viewportTransform)
+				return;
+			viewportTransform[4] = this.canvas.width / 2 - objCenter.x * viewportTransform[0];
+			viewportTransform[5] = this.canvas.height / 2 - objCenter.y * viewportTransform[3];
+			this.canvas.setViewportTransform(viewportTransform);
+			this.canvas.renderAll();
+		},
+		handleSingleTouch(touch) {
+			this.touchStartPoint = {
+				x: touch.clientX,
+				y: touch.clientY
+			};
+			//	console.log('鍗曠偣瑙︽懜寮�');
+
+			let activeObj = this.canvas.getActiveObject();
+			if (!activeObj) {
+				if (!this.drawType) {
+					this.isDrawing = false;
+					this.isDragging = true;
+					this.lastPosX = touch.clientX;
+					this.lastPosY = touch.clientY;
+
+				} else {
+					this.isDragging = false;
+					this.isDrawing = true;
+
+				}
+			}
+
+			// 鍗曠偣瑙︽懜閫昏緫
+		},
+		handleMultiTouch(touches) {
+			const _this = this
+			const touch1 = touches[0];
+			const touch2 = touches[1];
+			this.initialDistance = Math.sqrt(
+				Math.pow(touch2.clientX - touch1.clientX, 2) +
+				Math.pow(touch2.clientY - touch1.clientY, 2)
+			);
+			this.initialZoom = this.canvas.getZoom();
+			// 澶氱偣瑙︽懜鍒濆閫昏緫
+		},
+		handleSingleTouchMove(touch) {
+			//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;
+
+				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])
+				let startX = this.touchStartPoint.x - vpt[4]
+				let startY = this.touchStartPoint.y - vpt[5]
+				let endX = touch.clientX - vpt[4]
+				let endY = touch.clientY - vpt[5]
+
+				const scale = this.canvas.getZoom();
+				startX /= scale
+				startY /= scale
+				endX /= scale
+				endY /= scale
+				//	console.log("viewportTransform", startX, startY, endX, endY)
+				const left =
+					endX > startX ?
+					startX :
+					endX;
+				const top =
+					endY > startY ?
+					startY :
+					endY;
+				let width = Math.abs(endX - startX)
+				let height = Math.abs(endY - startY)
+
+
+				if (this.drawType == "rect") {
+					if (this.drawingObj) {
+						this.drawingObj.set({
+							left,
+							top,
+							width,
+							height
+						})
+						this.drawingObj.setCoords()
+					} else {
+						const rect = new fabric.Rect({
+							id: `${new Date().getTime()}`,
+							eleType: "rect",
+							left,
+							top,
+							width,
+							height,
+							stroke: "#333",
+							strokeWidth: 1,
+							fill: "#fff",
+							lockEdit: true
+						});
+						this.canvas.add(rect);
+						this.drawingObj = rect
+					}
+					this.canvas.requestRenderAll();
+				} else if (this.drawType == "ellipse") {
+					if (this.drawingObj) {
+						this.drawingObj.set({
+							left,
+							top,
+							rx: width / 2,
+							ry: height / 2,
+						})
+						this.drawingObj.setCoords()
+					} else {
+						const ellipse = new fabric.Ellipse({
+							id: `${new Date().getTime()}`,
+							eleType: "ellipse",
+							left,
+							top,
+							rx: width / 2,
+							ry: height / 2,
+							stroke: "#333",
+							strokeWidth: 1,
+							fill: "#fff",
+							lockEdit: true
+						});
+						this.canvas.add(ellipse);
+						this.drawingObj = ellipse
+					}
+					this.canvas.requestRenderAll();
+				} else if (this.drawType == "line") {
+					if (this.drawingObj) {
+						this.drawingObj.set({
+							left,
+							top,
+							x1: startX,
+							y1: startY,
+							x2: endX,
+							y2: endY,
+						})
+						this.drawingObj.setCoords()
+					} else {
+						const line = new fabric.Line([startX, startY, endX,
+							endY
+						], {
+							id: `${new Date().getTime()}`,
+							eleType: "line",
+							stroke: "#333",
+							strokeWidth: 5,
+							lockEdit: true
+						});
+						this.canvas.add(line);
+						this.drawingObj = line
+					}
+					this.canvas.requestRenderAll();
+				} else if (this.drawType == "arrow") {
+					if (this.drawingObj) {
+						this.setArrowPath(this.drawingObj, startX, startY, endX, endY);
+						this.drawingObj.setCoords()
+					} else {
+						let path = this.getArrowPath(
+							startX, startY, endX, endY,
+							30, 40
+						)
+						const line = new fabric.Path(path, {
+							id: `${new Date().getTime()}`,
+							eleType: "arrow",
+							stroke: "#333",
+							strokeWidth: 5,
+							lockEdit: true
+						});
+						this.canvas.add(line);
+						this.drawingObj = line
+					}
+					this.canvas.requestRenderAll();
+				} else if (this.drawType == "svg") {
+					if (this.drawingObj) {
+						this.drawingObj.set({
+							left,
+							top,
+							width,
+							height,
+						});
+						this.drawingObj.setCoords()
+					} else {
+						const _this = this
+						fabric.loadSVGFromURL(this.drawSvg).then(({
+							objects,
+							options
+						}) => {
+							const objGroup = fabric.util.groupSVGElements(objects, options);
+							objGroup.set({
+								id: `${new Date().getTime()}`,
+								eleType: "svg",
+								left,
+								top,
+								width,
+								height,
+								viewBoxWidth: options.viewBoxWidth || options.width,
+								viewBoxHeight: options.viewBoxHeight || options.height,
+								lockEdit: true
+							});
+							_this.canvas.add(objGroup)
+
+						});
+
+
+
+						this.drawingObj = line
+					}
+					// this.canvas.requestRenderAll();
+				}
+
+
+			}
+
+			// 鍗曠偣绉诲姩閫昏緫
+		},
+		handleMultiTouchMove(touches) {
+			// 璁$畻涓ょ偣闂磋窛绂伙紙缂╂斁锛�+			if (!this.initialDistance) {
+				return
+			}
+			const _this = this
+			const touch1 = touches[0];
+			const touch2 = touches[1];
+			//console.log('澶氱偣绉诲姩', touch1.identifier, touch2.identifier);
+
+			const distance = Math.sqrt(
+				Math.pow(touch2.clientX - touch1.clientX, 2) +
+				Math.pow(touch2.clientY - touch1.clientY, 2)
+			);
+
+			// 璁$畻鏃嬭浆瑙掑害
+			const angle = Math.atan2(
+				touch2.clientY - touch1.clientY,
+				touch2.clientX - touch1.clientX
+			) * 180 / Math.PI;
+
+			// 璁$畻涓績鐐�+			const center = {
+				x: (touch1.clientX + touch2.clientX) / 2,
+				y: (touch1.clientY + touch2.clientY) / 2
+			};
+
+			// 璁$畻缂╂斁姣斾緥 (闄愬埗鍦�.1-10鍊嶄箣闂�
+			let scale = Math.min(Math.max(
+				this.initialZoom * (distance / this.initialDistance),
+				0.1), 10);
+			let scaleAuto = this.getAutoScale()
+			if (scale < scaleAuto) {
+				scale = scaleAuto
+			} else if (scale == scaleAuto) {
+				return
+			}
+			//console.log(scale, scaleAuto)
+			this.setZoomAuto(scale, center)
+
+			//	console.log('澶氱偣绉诲姩 - 璺濈:', distance, '瑙掑害:', angle);
+			// 澶氱偣绉诲姩閫昏緫
+		},
+		handleTouchEnd() {
+			//	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"
+
+				const left = _this.getXOnImg(info.x) // * scale
+				const top = _this.getYOnImg(info.y) //* scale
+				const angle = info.angle * 180 / 3.14
+				console.log("addStation", svg, info)
+				fabric.loadSVGFromURL(svg).then(
+					({
+						objects,
+						options
+					}) => {
+						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,
+							top,
+							angle,
+							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,
+							lockScalingY: true,
+							lockMovementX: true,
+							lockMovementY: true,
+							canSelect: 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,
+						});
+						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 = _this.getXOnImg(info.x) // * scale
+				const top = _this.getYOnImg(info.y) //* scale
+				const angle = info.angle * 180 / 3.14
+				if (this.agvObj) {
+					this.agvObj.set({
+						left,
+						top,
+						angle,
+						data: info
+					});
+					this.agvObj.setCoords()
+					_this.canvas.requestRenderAll();
+					resolve()
+				} else {
+					const zoom = _this.canvas.getZoom();
+					fabric.loadSVGFromURL("static/images/van.svg").then(
+						({
+							objects,
+							options
+						}) => {
+
+							var scale = this.canvas.getZoom()
+							if (scale < 1) {
+								scale = 1
+							}
+							const scale2 = 1 / scale
+							const obj = fabric.util.groupSVGElements(objects, options);
+
+							obj.set({
+								id: "agv",
+								eleType: "agv",
+								left,
+								top,
+								angle,
+								data: info,
+								originX: "center",
+								originY: "center",
+								//	scale: 1 / zoom,
+								hasControls: false,
+								lockRotation: true,
+								lockScalingX: true,
+								lockScalingY: true,
+								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()
+						}
+					)
+				}
+
+			})
+		},
+
+
+		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)
+		},
+		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) {
+				if (obj.canSelect) {
+					if (!obj.flag) {
+						flag = true
+					}
+					obj.set({
+						selectable: selectable,
+						lockEdit: true
+					})
+
+				}
+
+			});
+			if (flag) {
+				this.canvas.requestRenderAll()
+			}
+		},
+		receiveMsg(newValue, oldValue) {
+			if (typeof newValue == "undefined")
+				return;
+			const _this = this
+			//console.log("receiveMsg",_this.initFlag)
+			setTimeout(() => {
+				if (_this.initFlag) {
+					_this.handleMsg(newValue, oldValue)
+				} else {
+					_this.receiveMsg(newValue, oldValue)
+				}
+			}, 500)
+
+		},
+		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]
+
+					if (item.method == "init") {
+						if (item.param?.editMode) {
+							_this.editMode = true
+						} else {
+							_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)
+					} else if (item.method == "update_current_teaching") {
+						const info = item.param || []
+						await _this.updateCurrentTeaching(info)
+					} else if (item.method == "move_canvas") {
+						const info2 = item.param || {}
+
+						const pt = {
+							x: this.getXOnImg(info2.x),
+							y: this.getYOnImg(info2.y)
+						}
+						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)
+							if (curIndex < 0) {
+								await _this.addStation(station)
+							}
+						}
+					} else if (item.method == "update_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)
+							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 scale = this.getAutoScale()
+								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)
+									}
+
+								}
+							}
+
+						}
+
+					} 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)
+							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 == "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() || []
+						list = list.filter((a) => a.eleType == "station")
+						_this.setAllObjectSelectable(false)
+						const curIndex = list.findIndex((a) => a.data.stationID == station.stationID)
+						if (curIndex > -1) {
+							console.log("edit_station_pos", curIndex, list[curIndex])
+							if (_this.editObject != list[curIndex]) {
+								if (_this.editObject) {
+									_this.editObject.set({
+										lockMovementX: true,
+										lockMovementY: true,
+									})
+								}
+								_this.editObject = list[curIndex]
+								_this.editObject.set({
+									lockMovementX: false,
+									lockMovementY: false,
+								})
+							}
+							list[curIndex].set({
+								selectable: true,
+								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)
+							_this.setAllObjectSelectable(true)
+						else
+							_this.setAllObjectSelectable(false)
+						if (_this.editObject) {
+							_this.editObject.set({
+								lockMovementX: true,
+								lockMovementY: true,
+							})
+							_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])
+							}
+						}
+					}
+				}
+				_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
+			}
+			let exStr = JSON.stringify(ex)
+			if (exStr == "{}")
+				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
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
diff --git a/pages/map/scene.vue b/pages/map/scene.vue
new file mode 100644
index 0000000..4da13fa
--- /dev/null
+++ b/pages/map/scene.vue
@@ -0,0 +1,589 @@
+<template>
+	<view class="pages-scene">
+		<view class="map-content" v-if="opSceneType =='' ">
+			<image class="img" src="/images/image 25.png" alt=" 鍥剧墖" mode="aspectFit" />
+			<view class="space">娌℃湁鎵惧埌绗﹀悎鏉′欢鐨勫湴鍥�/view>
+
+			<view class="text-button-group">
+				<a-button type="primary" class="button" @click="clickStartConstructScene">
+					寮�鏋勫缓
+				</a-button>
+				<a-button type="primary" class="button" :disabled="loading && localSceneList.length == 0"
+					@click="clickDownloadScene" disabled>
+					涓嬭浇鍦烘櫙
+				</a-button>
+			</view>
+
+		</view>
+		<view class="content" v-show="opSceneType =='scan'">
+			<view class="fabric" :message="ctxDataStr" :change:message="ctx.receiveMsg" id="canvasMap"></view>
+		</view>
+		<view class="bottom">
+			<view class="bottom-content" v-if="opSceneType =='add_name' ">
+				<view class="tip">璇疯緭鍏ュ満鏅悕绉�/view>
+				<view class="name-input">
+					<input ref="refInputName" :focus="true" placeholder="璇疯緭鍏ュ満鏅悕绉� :value="sceneName"
+						@input="onInputName"></input>
+					<uni-icons class="clear" color="#ccc" type="clear" size="20" v-if="showClearName"
+						@click="clickClearName"></uni-icons>
+				</view>
+				<view class="text-button-group">
+					<a-button type="primary" class="button" :disabled="loading || sceneName.trim() == ''"
+						@click="clickNameOK">纭</a-button>
+					<a-button type="ghost" class="button" :disabled="loading" @click="clickNameCancel">鍙栨秷</a-button>
+
+				</view>
+			</view>
+			<view class="bottom-content" v-else-if="opSceneType =='scan'">
+				<view class="tip">鍦烘櫙鏋勫缓涓�/view>
+				<view>
+					璇锋搷浣滄惉杩愯溅鎵弿鍦板浘瑕嗙洊鐨勫尯鍩�+				</view>
+
+				<view class="text-button-group">
+					<a-button type="primary" class="button" :disabled="loading" @click="clickScanFinish">鎵弿瀹屾垚</a-button>
+				</view>
+			</view>
+			<view class="bottom-content" v-else-if="opSceneType =='finish'">
+				<view class="tip">鍦烘櫙鏋勫缓瀹屾垚</view>
+				<view>
+					宸叉垚鍔熸瀯寤衡�{{sceneName}}鈥�+				</view>
+
+				<view class="text-button-group">
+					<a-button type="primary" class="button" :disabled="loading" @click="clickFinish">鏋勫缓瀹屾垚</a-button>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+<script src="./js/ctx.js" module="ctx" lang="renderjs"></script>
+
+<script>
+	import {
+		showToast,
+		showModal,
+		session,
+	} from "@/comm/utils.js"
+	import {
+		Button
+	} from 'antd-mobile-vue-next'
+
+	import {
+		createScene,
+		addMap,
+		stopMap,
+		//getAgvState,
+		getMapLaserData,
+		saveDBData,
+
+	} from "@/api/vehicle.js"
+	export default {
+		name: "PagesScene",
+		components: {
+			'a-button': Button
+		},
+		data() {
+			return {
+				opType: "",
+				loading: false,
+				ip: "",
+				opSceneType: "",
+				windowWidth: 375,
+				sceneName: "",
+				showClearName: false,
+				mapId: "",
+				ctxDataStr: "[]",
+				localSceneList: []
+
+			}
+		},
+		computed: {
+
+		},
+		watch: {
+
+		},
+		onLoad(option) {
+			const _this = this
+			this.ip = option.ip || ""
+			uni.getSystemInfo({
+				success(e) {
+					_this.windowWidth = e.windowWidth
+				},
+
+			})
+			this.sceneName = option.sceneId || ""
+			this.opType = option.opType || ""
+			this.opSceneType = ""
+			this.loadData()
+
+		},
+		onUnload() {
+
+		},
+		onBackPress() {
+
+			if (this.opSceneType == "scan") {
+
+				showModal("宸叉瀯寤哄満鏅皢浼氳鍒犻櫎", "鏄惁涓柇鍦烘櫙鏋勫缓锛�).then((res) => {
+					if (res) {
+						this.opSceneType = ""
+						if (this.opType != "") {
+							uni.navigateBack({
+								delta: 1, //杩斿洖灞傛暟锛�鍒欎笂涓婇〉
+							})
+						}
+
+					}
+				})
+				return true
+
+			} else {
+				return false
+			}
+
+		},
+		methods: {
+			setData(obj) {
+				let that = this;
+				let keys = [];
+				let val, data;
+
+				Object.keys(obj).forEach(function(key) {
+					keys = key.split(".");
+					val = obj[key];
+					data = that.$data;
+					keys.forEach(function(key2, index) {
+						if (index + 1 == keys.length) {
+							that.$set(data, key2, val);
+						} else {
+							if (!data[key2]) {
+								that.$set(data, key2, {});
+							}
+						}
+						data = data[key2];
+					});
+				});
+			},
+			async loadData() {
+				try {
+
+					if (this.opType == "") {
+						this.localSceneList = await this.loadLocalScene()
+					} else {
+						if (this.opType == "create") {
+							this.opSceneType = "add_name"
+						}
+						if (this.opType == "extend") {
+							await addMap(this.ip, this.sceneName, "extend")
+							this.opSceneType = 'scan'
+						}
+					}
+
+					this.refreshMapLaserData()
+
+
+				} catch (ex) {
+
+					this.showError(ex)
+				}
+			},
+			receiveRenderData(param) {
+				console.log('鎺ユ敹鍒拌鍥惧眰鐨勬暟鎹細', param);
+				if (param.method == "set_backgroud_progress") {
+					if (param.type == "start") {
+						this.setData({
+							bgProgressPercent: 50,
+							bgLoading: true
+						})
+					} else if (param.type == "progress") {
+						this.setData({
+							bgProgressPercent: 50 + (param.percent || 0) / 2
+						})
+					} else if (param.type == "end") {
+						this.setData({
+							bgProgressPercent: 100,
+							bgLoading: true
+						})
+						setTimeout(() => {
+							this.setData({
+								bgLoading: false
+							})
+						}, 500)
+
+					} else if (param.type == "error") {
+						this.setData({
+							bgProgressPercent: 0,
+							bgLoading: false
+						})
+					}
+
+				}
+			},
+
+			async loadLocalScene() {
+				try {
+					const list = session.getValue("scene_db") || []
+					return list
+				} catch (ex) {
+					this.showError(ex)
+					return []
+				}
+			},
+			clickStartConstructScene() {
+				this.opSceneType = "add_name"
+			},
+			async clickDownloadScene() {
+				try {
+					this.loading = true
+					uni.showLoading({
+						title: "涓嬭浇鍦烘櫙涓�
+					})
+					const data = this.localSceneList[0].data
+					await saveDBData(this.ip, data)
+					showToast("涓嬭浇鍦烘櫙鎴愬姛")
+					this.opSceneType = ""
+					const eventChannel = this.getOpenerEventChannel();
+					eventChannel.emit('create_finish', this.sceneName);
+					uni.navigateBack({
+						delta: 1, //杩斿洖灞傛暟锛�鍒欎笂涓婇〉
+					})
+				} catch (ex) {
+					this.showError(ex)
+				} finally {
+					this.loading = false
+					uni.hideLoading()
+				}
+
+
+			},
+
+			clickNameCancel() {
+				uni.navigateBack({
+					delta: 1, //杩斿洖灞傛暟锛�鍒欎笂涓婇〉
+				})
+			},
+			async clickNameOK() {
+				try {
+					this.loading = true
+					uni.showLoading({
+						title: "寮�鏋勫缓鍦烘櫙"
+					})
+					const name = this.sceneName.trim()
+					if (!name) {
+						showToast("绔欑偣鍚嶇О杩樻湭杈撳叆")
+						return
+					}
+
+					this.sceneName = name
+					await createScene(this.ip, name, 1)
+					await addMap(this.ip, name)
+					uni.showLoading({
+						title: "寮�鎵弿鍦板浘"
+					})
+					this.opSceneType = 'scan'
+
+
+				} catch (ex) {
+					this.showError(ex)
+					this.opSceneType = 'add_name'
+				} finally {
+					this.loading = false
+					uni.hideLoading()
+				}
+
+			},
+			clickClearName() {
+				this.sceneName = ""
+				this.showClearName = false
+			},
+			onInputName(event) {
+				this.sceneName = event.detail.value;
+				if (this.sceneName)
+					this.showClearName = true
+				else
+					this.showClearName = false
+			},
+			async clickScanFinish() {
+				try {
+					this.loading = true
+					uni.showLoading({
+						title: "鍦板浘鎵弿缁撴潫"
+					})
+					this.opSceneType = "finish"
+					uni.setNavigationBarTitle({
+						title: this.sceneName
+					})
+					await stopMap(this.ip, this.sceneName)
+					if (this.opType == "extend") {
+						this.opSceneType = ""
+						const eventChannel = this.getOpenerEventChannel();
+						eventChannel.emit('create_finish', this.sceneName);
+						uni.navigateBack({
+							delta: 1, //杩斿洖灞傛暟锛�鍒欎笂涓婇〉
+						})
+					}
+
+				} catch (ex) {
+
+					showModal("璇锋鏌ヨ溅杈嗚繛鎺ワ紝骞堕噸鏂板紑濮嬫瀯寤哄満鏅�, "鍦烘櫙鏋勫缓澶辫触", false, "纭畾").then((res) => {
+						this.opSceneType = ''
+						if (this.opType != "") {
+							uni.navigateBack({
+								delta: 1, //杩斿洖灞傛暟锛�鍒欎笂涓婇〉
+							})
+						}
+					})
+				} finally {
+					this.loading = false
+					uni.hideLoading()
+				}
+
+			},
+			async clickFinish() {
+				try {
+
+					this.loading = true
+					uni.showLoading({
+						title: "缁撴潫鍦烘櫙鏋勫缓"
+					})
+					this.opSceneType = ""
+					await createScene(this.ip, this.sceneName, 0)
+					const eventChannel = this.getOpenerEventChannel();
+					eventChannel.emit('create_finish', this.sceneName);
+					uni.navigateBack({
+						delta: 1, //杩斿洖灞傛暟锛�鍒欎笂涓婇〉
+					})
+				} catch (ex) {
+					showModal("璇锋鏌ヨ溅杈嗚繛鎺ワ紝骞堕噸鏂板紑濮嬫瀯寤哄満鏅�, "鍦烘櫙鏋勫缓澶辫触", false, "纭畾").then((res) => {
+						this.opSceneType = ''
+						if (this.opType != "") {
+							uni.navigateBack({
+								delta: 1, //杩斿洖灞傛暟锛�鍒欎笂涓婇〉
+							})
+						}
+					})
+				} finally {
+					this.loading = false
+					uni.hideLoading()
+				}
+
+
+			},
+			async loadMapLaserData() {
+				try {
+					const info = await getMapLaserData(this.ip, this.mapId)
+					return info
+				} catch (ex) {
+					this.showError(ex)
+					return {}
+				}
+			},
+			async refreshMapLaserData() {
+				try {
+					if (this.opSceneType === "scan") {
+
+						const data = await this.loadMapLaserData()
+
+						const listCtrData = []
+						if (data.base_map?.image_base64) {
+
+							this.mapId = data.base_map.map_id
+							listCtrData.push({
+								method: "background",
+								param: {
+									proportion: 1,
+									img_proportion: 1,
+									max_x: data.base_map.width,
+									max_y: data.base_map.height,
+									min_x: 0,
+									min_y: 0,
+									img_x: data.base_map.width,
+									img_y: data.base_map.height,
+									filedata: data.base_map.image_base64
+								}
+							})
+							listCtrData.push({
+								method: "agv_laser",
+								param: data.robot_pose
+							})
+							listCtrData.push({
+								method: "point_cloud",
+								param: data.point_cloud
+							})
+							listCtrData.push({
+								method: "move_canvas",
+								param: {
+									x: data.robot_pose.x,
+									y: data.robot_pose.y
+								}
+							})
+
+						} else {
+							listCtrData.push({
+								method: "agv_laser",
+								param: data.robot_pose
+							})
+							listCtrData.push({
+								method: "point_cloud",
+								param: data.point_cloud
+							})
+						}
+						this.ctxDataStr = JSON.stringify(listCtrData)
+					}
+				} catch (ex) {
+					this.showError(ex)
+				} finally {
+					// 鏃犺鎴愬姛澶辫触锛� 绉掑悗鍐嶆潵
+					setTimeout(this.refreshMapLaserData, 1000);
+				}
+			},
+
+			showError(ex) {
+				console.log(ex)
+				let exStr = JSON.stringify(ex)
+				if (exStr == "{}")
+					exStr = ex
+				let tip = typeof ex.msg == "string" ? ex.msg : exStr
+				showModal(tip, "閿欒", false, "纭畾")
+			},
+
+
+		}
+	}
+</script>
+
+<style lang="scss">
+	.pages-scene {
+		display: flex;
+		width: 750rpx;
+		height: 100vh;
+		position: relative;
+
+		.content {
+			overflow: auto;
+			position: absolute;
+			left: 0;
+			right: 0;
+			bottom: 0;
+			top: 0;
+
+			.fabric {
+				width: 100%;
+				height: 100%;
+				background-color: #fff;
+			}
+
+
+		}
+
+		.map-content {
+			width: 100%;
+			display: flex;
+			flex: 1;
+			flex-direction: column;
+			margin: auto;
+			align-items: center;
+			justify-content: center;
+
+
+			.text-button-group {
+				display: flex;
+				width: 100%;
+				justify-content: center;
+				align-items: center;
+				flex-direction: column;
+				font-size: 30rpx !important;
+
+				.button {
+					margin-top: 20rpx;
+					width: 375rpx;
+				}
+			}
+
+		}
+
+
+		.bottom {
+			position: fixed;
+			left: 50rpx;
+			right: 50rpx;
+			bottom: 20rpx;
+			display: flex;
+
+			.bottom-content {
+				display: flex;
+				flex-direction: column;
+				width: 100%;
+				// justify-content: center;
+				// align-items: center;
+				padding: 0 10rpx;
+				padding-bottom: 10rpx;
+				background-color: #fff;
+				border-radius: 10rpx;
+				//background-color: #eee;
+
+				.title {
+					font-size: 40rpx;
+					margin-bottom: 10rpx;
+				}
+
+				.tip {
+					color: #888;
+					margin: 10rpx;
+					text-align: left;
+				}
+
+
+				.disabled {
+					color: #ccc !important;
+				}
+
+				.name-input {
+					width: calc(100% - 20rpx);
+					margin-bottom: 10rpx;
+					background-color: #fff;
+					padding: 10rpx;
+					border-radius: 8rpx;
+					display: flex;
+					flex-direction: row;
+					color: #1890FF;
+
+					input {
+						flex: 1;
+					}
+				}
+			}
+
+
+			.text-button-group {
+				display: flex;
+				width: 100%;
+				justify-content: center;
+				align-items: center;
+				flex-direction: column;
+				font-size: 30rpx !important;
+
+				.button {
+					margin: auto;
+					margin-top: 20rpx;
+					width: calc(100% - 10rpx);
+				}
+
+				.am-button {
+					border-radius: 30px;
+				}
+
+				.am-button-ghost {
+					border: 1px solid #1677ff !important;
+
+				}
+			}
+
+
+		}
+
+
+	}
+</style>
\ No newline at end of file
diff --git a/pages/map/task.vue b/pages/map/task.vue
index a8c0a67..df03da6 100644
--- a/pages/map/task.vue
+++ b/pages/map/task.vue
@@ -143,7 +143,7 @@
 				if (exStr == "{}")
 					exStr = ex
 				let tip = typeof ex.msg == "string" ? ex.msg : exStr
-				showModal(tip, "閿欒", false)
+				showModal(tip, "閿欒", false,"纭畾")
 			},
 
 
diff --git a/pages/map/teaching.vue b/pages/map/teaching.vue
new file mode 100644
index 0000000..482b381
--- /dev/null
+++ b/pages/map/teaching.vue
@@ -0,0 +1,1957 @@
+<template>
+	<view class="pages-map-teaching">
+		<view class="view-content">
+			<view class="map-content">
+				<view class="content">
+					<view class="fabric" :message="ctxDataStr" :change:message="ctx.receiveMsg" id="canvasMap"></view>
+					<view class="loading-overlay" v-if="bgLoading">
+						<view class="loading-content">
+							<view class="loading-spinner"></view>
+							<text>鍔犺浇涓�.. {{ bgProgressPercent }}%</text>
+						</view>
+					</view>
+					<view class="position-site" v-if="mapOperationType =='add_station_pos' "
+						@click="clickPositionStation">
+						<text class="ico my-location-rounded"></text>
+
+						鑾峰彇鎼繍杞︿綅缃拰鏈濆悜
+					</view>
+					<view class="position" @click="clickVehiclePosition">
+						<text class="ico my-location-rounded"></text>
+					</view>
+					<view v-if="this.mapOperationType ==''" class="teaching-path-show"
+						:class="showTeachingPathFlag?'selected':''" @click="clickShowTeachingPath">
+						<text class="ico layer"></text>
+
+					</view>
+				</view>
+			</view>
+		</view>
+		<view class="bottom">
+			<template v-if="mapOperationType =='public_teaching' || mapOperationType =='station_teaching'">
+				<view class="bottom-content" v-if="teachingStatus =='teaching'">
+					<view class="tip">璺緞璁板綍涓�..</view>
+					<view v-if="mapOperationType =='public_teaching'">
+						<view>姝e湪璁板綍鎼繍杞﹁杩涜矾寰勶紝鍙殢鏃跺垏鎹富璺�鏀矾绀烘暀锛屽畬鎴愬悗鐐规寜閽互缁撴潫绀烘暀銆傚湪绀烘暀杩囩▼涓彲浠ユ坊鍔犵珯鐐广�</view>
+						<view class="switch-type">
+							<view class="switch-button-group">
+								<view class="switch-button"
+									:class="teachingModeCur.main_road ==1?'switch-button-checked':''"
+									@click="onTeachingModeMainRoad(1)">涓昏矾绀烘暀</view>
+								<view class="switch-button "
+									:class="teachingModeCur.main_road ==0?'switch-button-checked':''"
+									@click="onTeachingModeMainRoad(0)">鏀矾绀烘暀</view>
+							</view>
+						</view>
+					</view>
+
+					<view v-else>
+						姝e湪璁板綍鎼繍杞﹁杩涜矾寰勶紝瀹屾垚鍚庣偣鎸夐挳浠ョ粨鏉熺ず鏁�+					</view>
+					<view class="text-button-group">
+						<a-button v-if="mapOperationType =='public_teaching'" type="ghost" class="button"
+							@click="clickAddStation">娣诲姞绔欑偣</a-button>
+						<a-button type="primary" class="button" @click="clickTeachingEnd">缁撴潫璁板綍</a-button>
+					</view>
+				</view>
+				<view class="bottom-content" v-else-if="teachingStatus =='end'">
+					<view class="tip">璺緞璁板綍瀹屾垚</view>
+					<view>
+						瑕佸皢璇ユ璺緞淇濆瓨涓虹ず鏁欒矾寰勫悧锛�+					</view>
+					<view class="text-button-group">
+						<a-button type="primary" class="button" @click="clickTeachingSave">淇濆瓨涓虹ず鏁欒矾寰�/a-button>
+						<a-button type="ghost" class="button" @click="clickTeachingReset">閲嶆柊璁板綍</a-button>
+					</view>
+				</view>
+				<view class="bottom-content" v-else-if="teachingStatus =='save'">
+					<view class="tip">绀烘暀瀹屾垚</view>
+					<view>
+						宸插皢璺緞淇濆瓨涓虹ず鏁欒矾寰�+					</view>
+					<view class="text-button-group">
+						<a-button type="primary" class="button" @click="clickTeachingFinish">瀹屾垚</a-button>
+					</view>
+				</view>
+				<template v-else>
+					<view class="bottom-content" v-if=" mapOperationType =='station_teaching'">
+						<view class="tip">璇烽�鎷╃ず鏁欒矾绾跨殑璧风偣鍜岀粓鐐�/view>
+						<view class="row-group">
+							<view class="coordinate">
+								<text class="name">璧风偣:</text>
+								<a @click="clickStartStation">
+									{{startStationID ? stationName(startStationID) : "璇烽�鎷╄捣鐐�}}
+								</a>
+							</view>
+							<view class="coordinate">
+								<text class="name">缁堢偣:</text>
+								<a @click="clickEndStation">
+									{{endStationID ? stationName(endStationID) : "璇烽�鎷╃粓鐐�}}
+								</a>
+
+							</view>
+						</view>
+						<view>璇峰皢鎼繍杞︾Щ鑷宠捣鐐癸紝鐐光�寮�璁板綍鈥濆悗灏嗚溅寮�埌缁堢偣锛屾娆¤杩涜矾绾垮皢浣滀负鎵��绔欑偣闂寸殑绀烘暀璺嚎銆�/view>
+						<view class="text-button-group">
+							<a-button type="primary" :disabled="startStationID=='' || endStationID==''" class="button"
+								@click="clickTeachingStart">寮�璁板綍</a-button>
+						</view>
+					</view>
+					<view class="bottom-content" v-else>
+						<view class="tip">鍗冲皢寮�鍏叡绀烘暀</view>
+						<view>
+							璇烽�鎷╄杩涜涓昏矾杩樻槸鏀矾绀烘暀锛岀偣銆愬紑濮嬭褰曘�鍚庡皢璁板綍鎼繍杞︾殑琛岃繘璺嚎浣滀负鍏叡绀烘暀璺嚎銆傚紑濮嬭褰曞悗鍙殢鏃跺垏鎹富璺�鏀矾绀烘暀銆�+						</view>
+						<view class="text-button-group">
+							<view class="switch-type">
+								<view class="switch-button-group">
+									<view class="switch-button"
+										:class="teachingModeCur.main_road ==1?'switch-button-checked':''"
+										@click="onTeachingModeMainRoad(1)">涓昏矾绀烘暀</view>
+									<view class="switch-button "
+										:class="teachingModeCur.main_road ==0?'switch-button-checked':''"
+										@click="onTeachingModeMainRoad(0)">鏀矾绀烘暀</view>
+								</view>
+							</view>
+							<a-button type="primary" class="button" @click="clickTeachingStart">寮�璁板綍</a-button>
+						</view>
+					</view>
+				</template>
+			</template>
+			<view class="bottom-content" v-else-if="mapOperationType =='add_station_name' ">
+				<view class="tip">璇疯緭鍏ョ珯鐐瑰悕绉�/view>
+				<view class="name-input">
+					<input ref="refInputName" :focus="true" placeholder="杈撳叆绔欑偣鍚嶇О" :value="stationEdit.name"
+						@input="onInputStationName"></input>
+					<uni-icons class="clear" color="#ccc" type="clear" size="20" v-if="stationEdit.name"
+						@click="clickClearStationName"></uni-icons>
+				</view>
+				<view class="text-button-group">
+					<a-button class="button" @click="clickStationNameCancel">鍙栨秷</a-button>
+					<a-button type="primary" class="button" :disabled="stationEdit.name.trim() == ''"
+						@click="clickStationNameOK">纭畾</a-button>
+				</view>
+			</view>
+			<view class="bottom-content" v-else-if="mapOperationType =='add_station_pos'">
+				<view class="tip">璋冭妭浣嶇疆</view>
+				<view class="row-group">
+					<view class="coordinate">
+						<text class="name">妯潗鏍�</text>
+						<input ref="refInputX" class="number-input" type="number" :value="stationEdit.x"
+							@input="onInputStationX" :maxlength="4"></input>
+						<uni-icons class="clear" color="#ccc" type="clear" size="20"
+							v-if="stationEdit.x && stationEdit.x!='0'" @click="clickClearStationX"></uni-icons>
+					</view>
+					<view class="coordinate">
+						<text class="name">绔栧潗鏍�</text>
+						<input ref="refInputX" class="number-input" type="number" :value="stationEdit.y"
+							@input="onInputStationY" :maxlength="4"></input>
+						<uni-icons class="clear" color="#ccc" type="clear" size="20"
+							v-if="stationEdit.y&& stationEdit.y!='0'" @click="clickClearStationY"></uni-icons>
+					</view>
+				</view>
+
+				<view class="tip">璋冭妭鏈濆悜</view>
+				<view class="angle-group">
+					<image class="img-angle" :src="angleSvg" alt="SVG 鍥剧墖" />
+					<image class="img-angle-pos" src="/images/Frame 153.svg" alt="SVG 鍥剧墖"
+						@touchstart="handleAngleTouchStart" @touchmove="handleAngleTouchMove" />
+				</view>
+			</view>
+			<view class="bottom-content" v-else-if="mapOperationType =='edit_teaching'">姝e湪缂栬緫绀烘暀璺嚎</view>
+			<view class="bottom-content" v-else>
+				<view class="img-button-group">
+					<view fill="none" class="button" @click="clickStationTeaching">
+						<text class="ico conversion-path"></text>
+						<view class="text">绔欑偣绀烘暀</view>
+					</view>
+					<view type="text" class="button " @click="clickPublicTeaching">
+						<text class="ico edit-road-outline-rounded"></text>
+						<view class="text">鍏叡绀烘暀</view>
+					</view>
+				</view>
+			</view>
+		</view>
+		<view>
+			<uni-popup ref="refPopupPicker" background-color="transparent" maskBackgroundColor="rgba(0, 0, 0, 0.2)">
+				<view class="popup-content" @click="closePopupPicker">
+					<picker-view class="popup-content-view" :style="{'margin-left':pickerView.left +'px'}"
+						:indicator-style="indicatorStyle" :value="pickerView.value" @change="bindPickerChange">
+						<picker-view-column>
+							<view class="item" @click="clickPickerViewItem(index)"
+								v-for="(item,index) in pickerView.list" :key="index">{{item.name}}</view>
+						</picker-view-column>
+
+					</picker-view>
+				</view>
+			</uni-popup>
+			<uni-popup ref="refPopupCalibration">
+				<view class="popup-dialog">
+					<view class="popup-dialog-title">
+						<text class="uni-dialog-title-text">鏄惁瑕佹牎鍑嗙珯鐐逛綅缃紵</text>
+					</view>
+					<view class="popup-dialog-content">
+						妫�祴鍒皗{calibratioStationTypeName}}浣嶇疆鏈濆悜涓庢墍閫夌珯鐐规湁鍋忓樊锛屾槸鍚﹀皢绔欑偣浣嶇疆鏈濆悜鏍″噯鍒板綋鍓嶅�锛�+					</view>
+					<view class="popup-dialog-button-group">
+
+						<view class="popup-dialog-button" @click="clickCalibration">
+							鏍″噯
+						</view>
+						<view class="popup-dialog-button" @click="clickNoCalibration">
+							涓嶆牎鍑�+						</view>
+						<view class="popup-dialog-button" @click="clickBackTeaching">
+							鍥炲埌绀烘暀
+						</view>
+					</view>
+				</view>
+			</uni-popup>
+			<uni-popup ref="refPopupOperateTeaching" background-color="transparent"
+				maskBackgroundColor="rgba(0, 0, 0, 0.2)">
+				<view class="popup-content">
+					<view class="popup-content-btn">
+						<view class="img-button-group">
+							<view fill="none" class="button" @click.stop="clickTeachingEdit">
+								<text class="ico edit-line"></text>
+								<view class="text">缂栬緫</view>
+							</view>
+							<view fill="none" class="button" @click.stop="clickTeachingDelete">
+								<text class="ico red delete-outline "></text>
+								<view class="text">鍒犻櫎</view>
+							</view>
+
+						</view>
+					</view>
+
+				</view>
+			</uni-popup>
+
+		</view>
+	</view>
+</template>
+<script src="./js/ctx.js" module="ctx" lang="renderjs"></script>
+<script>
+	import {
+		showToast,
+		showModal
+	} from "@/comm/utils.js"
+	// import OIFabric from "@/components/oi-fabric/index.vue"
+	import {
+		Button
+	} from 'antd-mobile-vue-next'
+
+	import {
+		getMapUrl,
+		stations,
+		addStation,
+		updateStation,
+		getAgvState,
+		getTeachingMode,
+		teachingModeFlag,
+		delTeachingMode,
+		delTeachingModeData,
+		checkAgvLocationDistanceError,
+
+	} from "@/api/vehicle.js"
+	import {
+		async
+	} from "rxjs"
+	export default {
+		name: "PagesMapTeaching",
+		components: {
+			'a-button': Button
+		},
+		data() {
+			return {
+				indicatorStyle: `height: 75rpx;`,
+				navigationBarTitle: "",
+				vehicleIp: "",
+				sceneId: "",
+				ctxDataStr: "[]",
+				mapOperationType: "",
+				teachingStatus: "",
+				teachingMode: {},
+				teachingModeCur: {},
+
+				startStationID: "",
+				endStationID: "",
+				calibratioStationType: "",
+				pickerView: {
+					type: "",
+					list: [],
+					value: []
+				},
+				stationEdit: {
+					stationID: "",
+					name: "",
+					x: 0,
+					y: 0,
+					angle: 0
+				},
+				showClearName: false,
+				stationList: [],
+				angleSvg: "/static/images/angle0.svg",
+				windowWidth: 375,
+				bgProgressPercent: 0,
+				bgLoading: true,
+				showTeachingPathFlag: false
+
+			}
+		},
+		computed: {
+			getMaxStationNo() {
+				let num = 0
+				this.stationList.forEach((item) => {
+					if (num < item.stationID) {
+						num = item.stationID
+					}
+				})
+				return num
+			},
+		},
+		watch: {
+			mapOperationType(val) {
+				let name = "璺緞绀烘暀"
+				if (val == "public_teaching") {
+					name = "鍏叡绀烘暀"
+				} else if (val == "station_teaching") {
+					name = "绔欑偣绀烘暀"
+				} else if (val == "add_station_name" || val == "add_station_pos") {
+					name = "娣诲姞绔欑偣"
+				}
+				this.setData({
+					navigationBarTitle: name
+				})
+			},
+			navigationBarTitle(val) {
+				uni.setNavigationBarTitle({
+					title: val
+				})
+			},
+			calibratioStationTypeName() {
+				if (this.calibratioStationType == "start") {
+					return "璧风偣"
+				} else if (this.calibratioStationType == "end") {
+					return "缁堢偣"
+				} else {
+					return "绔欑偣"
+				}
+			},
+		},
+		onBackPress() {
+			const _this = this
+			if (this.mapOperationType == "") {
+				return false
+
+			} else if (this.mapOperationType == "edit_teaching") {
+				return false
+			} else if (this.mapOperationType == "add_station_name" || this.mapOperationType == "add_station_pos") {
+				this.mapOperationType = "public_teaching"
+				return false
+			} else if (this.mapOperationType == "public_teaching" || this.mapOperationType == "station_teaching") {
+
+
+				if (this.teachingStatus) {
+
+					showModal("宸茶褰曠殑璺緞灏嗕細琚垹闄ゃ�", "鏄惁瑕侀�鍑虹ず鏁欙紵").then((res) => {
+						if (res) {
+							this.ctxDataStr = JSON.stringify([{
+								method: "teaching_finish",
+							}])
+							if (this.mapOperationType == "public_teaching") {
+								clearInterval(this.refreshIntervalId)
+								this.refreshIntervalId = 0
+							}
+
+
+							this.mapOperationType = ""
+						}
+					})
+				} else {
+					this.ctxDataStr = JSON.stringify([{
+						method: "teaching_finish",
+					}])
+
+					this.mapOperationType = ""
+				}
+
+			}
+			if (this.mapOperationType == "add_station_name") {
+				this.mapOperationType = "public_teaching"
+			}
+			return true
+		},
+
+		onLoad(option) {
+			this.vehicleIp = option.ip || ""
+			this.sceneId = option.sceneId || ""
+			this.mapOperationType = ""
+			this.loadData()
+
+		},
+		methods: {
+			setData(obj) {
+				let that = this;
+				let keys = [];
+				let val, data;
+
+				Object.keys(obj).forEach(function(key) {
+					keys = key.split(".");
+					val = obj[key];
+					data = that.$data;
+					keys.forEach(function(key2, index) {
+						if (index + 1 == keys.length) {
+							that.$set(data, key2, val);
+						} else {
+							if (!data[key2]) {
+								that.$set(data, key2, {});
+							}
+						}
+						data = data[key2];
+					});
+				});
+			},
+
+			async loadData() {
+				try {
+					this.setData({
+						bgProgressPercent: 0,
+						bgLoading: true
+					})
+					const infoAgv = await this.loadAgvState()
+					const stationLst = await this.loadStations()
+					const infoMap = await this.loadMapInfo(this.sceneId)
+					if (!infoMap.fileData) {
+						this.setData({
+							bgProgressPercent: 0,
+							bgLoading: false
+						})
+					}
+					this.stationList = stationLst
+					this.teachingMode = await this.loadTeachingMode()
+					this.ctxDataStr = JSON.stringify([{
+							method: "init",
+							param: {
+								editMode: true
+							}
+						}, {
+							method: "background",
+							param: infoMap
+						},
+						{
+							method: "update_agv_state",
+							param: infoAgv
+						},
+						{
+							method: "add_station",
+							param: stationLst
+						},
+						{
+							method: "public_teaching_path",
+							param: this.teachingMode.Public || []
+
+						},
+						{
+							method: "station_teaching_path",
+							param: this.teachingMode.Stations || []
+
+						},
+						{
+							method: "set_selectable",
+							param: false
+						},
+
+						{
+							method: "move_canvas",
+							param: {
+								x: infoAgv.x,
+								y: infoAgv.y
+							}
+						}
+					])
+
+
+
+				} catch (ex) {
+					this.setData({
+						bgProgressPercent: 0,
+						bgLoading: false
+					})
+					this.showError(ex)
+				}
+			},
+			async loadAgvState() {
+				try {
+					const info = await getAgvState(this.vehicleIp)
+					return info
+				} catch (ex) {
+					this.showError(ex)
+					return {}
+				}
+			},
+			async loadStations() {
+				try {
+					const info = await stations(this.vehicleIp)
+					return info.station_list || []
+				} catch (ex) {
+					this.showError(ex)
+					return []
+				}
+			},
+			async loadMapInfo(id) {
+				try {
+					const info = await getMapUrl(this.vehicleIp, id)
+					return info
+				} catch (ex) {
+					this.showError(ex)
+					return {}
+				}
+			},
+			async loadTeachingMode() {
+				try {
+					const {
+						data
+					} = await getTeachingMode(this.vehicleIp)
+					return data || {
+						Public: [],
+						Stations: []
+					}
+
+				} catch (ex) {
+					this.showError(ex)
+					return {
+						Public: [],
+						Stations: []
+					}
+				}
+			},
+			async reloadTeachingMode() {
+				try {
+					this.teachingMode = await this.loadTeachingMode()
+					this.ctxDataStr = JSON.stringify([{
+							method: "public_teaching_path",
+							param: this.teachingMode.Public || []
+						},
+						{
+							method: "station_teaching_path",
+							param: this.teachingMode.Stations || []
+						}
+					])
+
+				} catch (ex) {
+					this.showError(ex)
+
+				}
+			},
+			stationName(id) {
+				const curIndex = this.stationList.findIndex((a) => a.stationID == id)
+				if (curIndex > -1) {
+					return this.stationList[curIndex].name
+				}
+				return ""
+
+			},
+			receiveRenderData(param) {
+				console.log('鎺ユ敹鍒拌鍥惧眰鐨勬暟鎹細', param);
+				if (param.method == "set_backgroud_progress") {
+					if (param.type == "start") {
+						this.setData({
+							bgProgressPercent: 0,
+							bgLoading: true
+						})
+					} else if (param.type == "progress") {
+						this.setData({
+							bgProgressPercent: param.percent || 0
+						})
+					} else if (param.type == "end") {
+						this.setData({
+							bgProgressPercent: 0,
+							bgLoading: false
+						})
+					} else if (param.type == "error") {
+						this.setData({
+							bgProgressPercent: 0,
+							bgLoading: false
+						})
+					}
+
+				} else if (param.method == "select_station") {
+					if (param.select) {
+						if (!this.startStationID) {
+							this.startStationID = param.data.stationID
+						} else if (!this.endStationID) {
+							this.endStationID = param.data.stationID
+						}
+
+						const list = []
+						if (this.startStationID)
+							list.push(this.startStationID)
+						if (this.endStationID)
+							list.push(this.endStationID)
+						this.ctxDataStr = JSON.stringify([{
+							method: "mark_station",
+							param: list
+						}])
+
+					} else {
+						if (this.startStationID == param.data.stationID) {
+							this.startStationID = ""
+						} else if (this.endStationID == param.data.stationID) {
+							this.endStationID = ""
+						}
+						const list = []
+						if (this.startStationID)
+							list.push(this.startStationID)
+						if (this.endStationID)
+							list.push(this.endStationID)
+						this.ctxDataStr = JSON.stringify([{
+							method: "mark_station",
+							param: list
+						}])
+
+					}
+
+				} else if (param.method == "update_station") {
+					this.stationEdit.stationID = param.station?.stationID
+					this.stationEdit.angle = param.station.angle
+					this.stationEdit.x = param.station.x
+					this.stationEdit.y = param.station.y
+				} else if (param.method == "edit_finish") {
+					if (param.cmd == "ok") {
+						if (param.type == "station") {
+							this.stationEdit.stationID = param.data?.stationID
+							this.stationEdit.name = param.data?.name
+							this.stationEdit.angle = param.data?.angle || 0
+							this.stationEdit.x = param.data?.x
+							this.stationEdit.y = param.data?.y
+
+							const angle = this.getStantardAngle(this.stationEdit.angle * 180 / 3.14)
+
+							this.angleSvg = `/static/images/angle${angle}.svg`
+							this.clickStationPositonOk()
+							this.refreshIntervalId = setInterval(() => {
+								this.refreshAgvPosition()
+							}, 5000)
+						} else if (param.type == "edit_teaching") {
+
+							this.removeTeachingModeData(param.data)
+							this.ctxDataStr = JSON.stringify([])
+							this.mapOperationType = ""
+						}
+					} else if (param.cmd == "cancel") {
+						if (param.type == "edit_teaching") {
+							this.ctxDataStr = JSON.stringify([])
+							this.mapOperationType = ""
+						} else {
+							this.ctxDataStr = JSON.stringify([{
+								method: "remove_station",
+								param: [{
+									stationID: this.stationEdit.stationID
+								}],
+							}])
+							this.stationEdit.stationID = ""
+							this.mapOperationType = 'public_teaching'
+							this.refreshIntervalId = setInterval(() => {
+								this.refreshAgvPosition()
+							}, 5000)
+						}
+
+					}
+				} else if (param.method == "select_teaching_path") {
+					console.log("point",param.point)
+					if (param.type == "station") {
+						
+						this.selectTeachingMode = {
+							mode: "Stations",
+							name: param.data?.name || "",
+							src_dst: param.data?.src_dst || "",
+							point: param.point
+						}
+
+					} else if (param.type == "public") {
+						this.selectTeachingMode = {
+							mode: "Public",
+							name: param.data?.name || "",
+							point: param.point
+						}
+					}
+
+					this.$refs.refPopupOperateTeaching.open("bottom")
+				}
+			},
+			clickTeachingDelete() {
+				this.$refs.refPopupOperateTeaching.close()
+				this.removeTeachingMode(this.selectTeachingMode)
+			},
+			clickTeachingEdit() {
+				this.$refs.refPopupOperateTeaching.close()
+				this.mapOperationType = "edit_teaching"
+				this.ctxDataStr = JSON.stringify([{
+					method: "edit_teaching",
+					param: this.selectTeachingMode,
+				}])
+			},
+
+			async removeTeachingMode(item) {
+				try {
+					await delTeachingMode(this.vehicleIp, [item])
+					this.ctxDataStr = JSON.stringify([{
+						method: "remove_teaching_path",
+						param: item,
+					}])
+				} catch (ex) {
+					this.showError(ex)
+				}
+			},
+			async removeTeachingModeData(data) {
+				try {
+					await delTeachingModeData(this.vehicleIp, data)
+					this.reloadTeachingMode()
+				} catch (ex) {
+					this.showError(ex)
+				}
+			},
+			clickPublicTeaching() {
+
+				this.teachingStatus = ""
+				this.teachingModeCur = {
+					mode: "Public",
+					main_road: 1,
+				}
+				this.mapOperationType = "public_teaching"
+				this.ctxDataStr = JSON.stringify([{
+					method: "set_selectable",
+					param: false,
+				}, {
+					method: "public_teaching",
+				}])
+
+
+			},
+			clickStationTeaching() {
+
+				this.startStationID = ""
+				this.endStationID = ""
+				this.teachingStatus = ""
+				this.mapOperationType = "station_teaching"
+				this.ctxDataStr = JSON.stringify([{
+					method: "station_teaching",
+				}])
+
+
+
+			},
+			async clickStationPositonOk() {
+				try {
+					await this.stationAdd(this.stationEdit)
+					this.ctxDataStr = JSON.stringify([{
+						method: "set_selectable",
+						param: false,
+					}])
+					this.mapOperationType = 'public_teaching'
+				} catch (ex) {
+					this.showError(ex)
+				}
+			},
+			async stationAdd(item) {
+				try {
+					await addStation(this.vehicleIp, item)
+					await updateStation(this.vehicleIp, item)
+					this.stationList.push({
+						stationID: item.stationID,
+						name: item.name,
+						angle: item.angle,
+						x: item.y,
+						y: item.x
+					})
+
+
+				} catch (ex) {
+					this.showError(ex)
+				}
+			},
+			async teachingStart(mode) {
+				try {
+					if (mode == "Stations") {
+						const res = await checkAgvLocationDistanceError(this.vehicleIp, this.startStationID)
+						if (res?.error) {
+							this.calibratioStationType = "start"
+							this.$refs.refPopupCalibration.open()
+						} else {
+							// const name =
+							// 	`${ this.stationName(this.startStationID)}_${ this.stationName(this.endStationID)}`
+
+							this.teachingModeCur = {
+								mode: "Stations",
+								src_dst: `${this.startStationID}_${this.endStationID}`,
+								name: "",
+								teaching_flag: 1,
+							}
+							this.ctxDataStr = JSON.stringify([{
+								method: "set_selectable",
+								param: false,
+							}])
+							const res2 = await teachingModeFlag(this.vehicleIp, this.teachingModeCur)
+							if (res2?.name)
+								this.teachingModeCur.name = res2.name
+						}
+					} else {
+						//	const name = `绀烘暀_${new Date().getTime()}`
+						const main_road = this.teachingModeCur.main_road
+						this.teachingModeCur = {
+							mode: "Public",
+							src_dst: ``,
+							name: "",
+							teaching_flag: 1,
+							main_road
+						}
+						const res2 = await teachingModeFlag(this.vehicleIp, this.teachingModeCur)
+						if (res2?.name)
+							this.teachingModeCur.name = res2.name
+					}
+
+				} catch (ex) {
+					this.showError(ex)
+				}
+
+			},
+			async teachingEnd(mode) {
+				try {
+					if (mode == "Stations") {
+						const res = await checkAgvLocationDistanceError(this.vehicleIp, this.endStationID)
+						if (res.error) {
+							this.calibratioStationType = "end"
+							this.$refs.refPopupCalibration.open()
+						} else {
+							this.askTeachingBiDirection(this.teachingModeCur)
+						}
+					} else {
+						this.askTeachingBiDirection(this.teachingModeCur)
+					}
+				} catch (ex) {
+					this.showError(ex)
+				}
+
+			},
+			async onTeachingModeMainRoad(val) {
+				try {
+					if (this.teachingStatus) {
+
+						if (this.teachingModeCur.main_road == val) {
+							return
+						}
+						this.teachingModeCur.main_road = val
+						this.teachingModeCur.teaching_flag = 1
+						this.teachingModeCur.mode = "Public"
+						const res = await teachingModeFlag(this.vehicleIp, this.teachingModeCur)
+						if (val == 0)
+							showToast("宸插皢璇ヨ矾娈佃矾寰勪繚瀛樹负涓昏矾绀烘暀璺嚎")
+						else
+							showToast("宸插皢璇ヨ矾娈佃矾寰勪繚瀛樹负鏀矾绀烘暀璺嚎")
+						if (res?.name)
+							this.teachingModeCur.name = res.name
+					} else {
+						this.teachingModeCur.main_road = val
+					}
+
+				} catch (ex) {
+					this.showError(ex)
+				}
+			},
+
+			async refreshAgvPosition() {
+				const _this = this
+				try {
+					const agv = await this.loadAgvState()
+					this.ctxDataStr = JSON.stringify([{
+						method: "update_agv_state",
+						param: agv
+					}])
+
+				} catch (ex) {
+					this.showError(ex)
+				}
+			},
+
+			async clickTeachingStart() {
+
+				const _this = this
+				try {
+					if (this.mapOperationType == "station_teaching") {
+						const stationTeaching = this.teachingMode?.Stations || []
+						const srcDst = `${this.startStationID}_${this.endStationID}`
+						const name =
+							`${ this.stationName(this.startStationID)}_${ this.stationName(this.endStationID)}`
+
+						const curIndex = stationTeaching.findIndex((a) => a.src_dst == srcDst)
+						console.log(srcDst, curIndex, stationTeaching)
+						if (curIndex > -1) {
+							showModal("璇ョ珯鐐归棿宸叉湁绀烘暀璺緞锛岄噸鏂拌褰曚細瑕嗙洊涔嬪墠鐨勮矾寰勩�", "鏄惁瑕侀噸鏂拌褰曪紵").then(async (res) => {
+								if (res) {
+									try {
+										_this.teachingStatus = "teaching"
+										await delTeachingMode(this.vehicleIp, [{
+											mode: "Stations",
+											name,
+											src_dst: srcDst
+
+										}])
+										stationTeaching.splice(curIndex, 1)
+										_this.teachingStart("Stations")
+									} catch (ex) {
+										this.showError(ex)
+									}
+
+								} else {
+
+									_this.teachingStatus = ""
+								}
+							})
+						} else {
+							_this.teachingStatus = "teaching"
+							_this.teachingStart("Stations")
+						}
+
+					} else {
+						this.teachingStatus = "teaching"
+						_this.teachingStart("Public")
+						await this.refreshAgvPosition()
+						this.refreshIntervalId = setInterval(() => {
+							this.refreshAgvPosition()
+						}, 5000)
+
+
+					}
+
+				} catch (ex) {
+					this.showError(ex)
+				}
+			},
+			clickTeachingEnd() {
+				if (this.mapOperationType == "station_teaching") {
+					this.teachingEnd("Stations")
+				} else {
+					clearInterval(this.refreshIntervalId)
+					this.refreshIntervalId = 0
+					this.teachingEnd("Public")
+				}
+
+			},
+			clickTeachingReset() {
+				const _this = this
+				showModal("宸茶褰曠殑璺緞灏嗕細琚垹闄ゃ�", "鏄惁瑕侀噸鏂拌褰曪紵").then(async (res) => {
+					if (res) {
+						try {
+							await delTeachingMode(_this.vehicleIp, [_this.teachingModeCur])
+							_this.teachingStatus = ""
+							_this.reloadTeachingMode()
+
+						} catch (ex) {
+							this.showError(ex)
+						}
+					} else {
+						_this.teachingStatus = "save"
+					}
+				})
+
+			},
+			async clickTeachingSave() {
+
+				this.teachingStatus = "save"
+				try {} catch (ex) {
+					this.showError(ex)
+				}
+
+			},
+			clickTeachingFinish() {
+				this.mapOperationType = ""
+				this.ctxDataStr = JSON.stringify([{
+					method: "teaching_finish",
+				}])
+				// const eventChannel = this.getOpenerEventChannel();
+				// eventChannel.emit('add');
+				// uni.navigateBack({
+				// 	delta: 1, //杩斿洖灞傛暟锛�鍒欎笂涓婇〉
+				// })
+			},
+			clickEndStation(e) {
+				const list = this.stationList.filter((a) => a.stationID != this.startStationID)
+				let index = -1
+
+				if (this.endStationID) {
+					index = list.findIndex((a) => a.stationID == this.endStationID)
+					if (index < 0) {
+						index = -1
+					}
+
+				} else {
+					index = -1
+				}
+				this.pickerView = {
+					type: "endStation",
+					list,
+					value: [index],
+					left: e.target.offsetLeft - 150,
+				}
+				this.$refs.refPopupPicker.open("bottom")
+			},
+			clickStartStation(e) {
+				const list = this.stationList.filter((a) => a.stationID != this.endStationID)
+				let index = -1
+
+				if (this.startStationID) {
+					index = list.findIndex((a) => a.stationID == this.startStationID)
+					if (index < 0) {
+						index = -1
+					}
+
+				} else {
+					index = -1
+				}
+				this.pickerView = {
+					type: "startStation",
+					list,
+					value: [index],
+					left: e.target.offsetLeft + 50,
+				}
+				this.$refs.refPopupPicker.open("bottom")
+			},
+			clickAddStation() {
+				// const _this = this
+				this.mapOperationType = "add_station_name"
+				clearInterval(this.refreshIntervalId)
+				this.refreshIntervalId = 0
+				// uni.navigateTo({
+				// 	url: `/pages/station/index?ip=${this.vehicleIp}&isAdd=1`,
+				// 	events: {
+				// 		// 涓烘寚瀹氫簨浠舵坊鍔犱竴涓洃鍚櫒锛岃幏鍙栬鎵撳紑椤甸潰浼犻�鍒板綋鍓嶉〉闈㈢殑鏁版嵁
+				// 		add: function(data) {
+				// 			_this.stationList.push({
+				// 				stationID: data.stationID,
+				// 				name: data.name,
+				// 				x: data.x,
+				// 				y: data.y,
+				// 				angle: data.angle
+				// 			})
+				// 		},
+				// 	}
+				// })
+			},
+			bindPickerChange(e) {
+				const val = e.detail.value
+
+				const val2 = val[0]
+
+				this.pickerView.value = [val2]
+				console.log(val2, val)
+				if (this.pickerView.type == "startStation") {
+					this.startStationID = this.pickerView.list[val2].stationID
+					const list = [this.startStationID]
+					if (this.endStationID)
+						list.push(this.endStationID)
+
+					this.ctxDataStr = JSON.stringify([{
+						method: "mark_station",
+						param: list
+					}])
+				} else if (this.pickerView.type == "endStation") {
+					this.endStationID = this.pickerView.list[val2].stationID
+					const list = [this.endStationID]
+					if (this.startStationID)
+						list.push(this.startStationID)
+
+					this.ctxDataStr = JSON.stringify([{
+						method: "mark_station",
+						param: list
+					}])
+				}
+			},
+			clickPickerViewItem(index) {
+				if (index == this.pickerView.value) {
+					this.$refs.refPopupPicker.close()
+				}
+			},
+			closePopupPicker() {
+				this.$refs.refPopupPicker.close()
+			},
+			async clickCalibration() {
+				try {
+					this.$refs.refPopupCalibration.close()
+					if (this.calibratioStationType == "start") {
+						const curIndex = list.findIndex((a) => a.stationID == this.startStationID)
+						if (curIndex > -1) {
+							const station = {}
+							station.stationID = list[curIndex].stationID
+							station.name = list[curIndex].name
+							const infoAgv = await getAgvState(this.vehicleIp)
+							station.angle = infoAgv.angle
+							station.x = infoAgv.x
+							station.y = infoAgv.y
+							await updateStation(this.vehicleIp, station)
+							showToast(`${station.name}浣嶇疆宸叉洿鏂癭)
+							list[curIndex].angle = infoAgv.angle
+							list[curIndex].x = infoAgv.x
+							list[curIndex].y = infoAgv.y
+							this.ctxDataStr = JSON.stringify([{
+									method: "update_agv_state",
+									param: infoAgv
+								},
+								{
+									method: "update_station",
+									param: [station]
+								},
+
+							])
+
+						}
+						const name =
+							`${ this.stationName(this.startStationID)}_${ this.stationName(this.endStationID)}`
+
+						this.teachingModeCur = {
+							mode: "Stations",
+							src_dst: `${this.startStationID}_${this.endStationID}`,
+							name,
+							teaching_flag: 1
+						}
+						await teachingModeFlag(this.vehicleIp, this.teachingModeCur)
+					} else if (this.calibratioStationType == "end") {
+						const curIndex = list.findIndex((a) => a.stationID == this.endStationID)
+						if (curIndex > -1) {
+							const station = {}
+							station.stationID = list[curIndex].stationID
+							station.name = list[curIndex].name
+							const infoAgv = await getAgvState(this.vehicleIp)
+							station.angle = infoAgv.angle
+							station.x = infoAgv.x
+							station.y = infoAgv.y
+							await updateStation(this.vehicleIp, station)
+							showToast(`${station.name}浣嶇疆宸叉洿鏂癭)
+							list[curIndex].angle = infoAgv.angle
+							list[curIndex].x = infoAgv.x
+							list[curIndex].y = infoAgv.y
+							this.ctxDataStr = JSON.stringify([{
+									method: "update_agv_state",
+									param: infoAgv
+								},
+								{
+									method: "update_station",
+									param: [station]
+								},
+
+							])
+						}
+						this.askTeachingBiDirection(this.teachingModeCur)
+
+					}
+				} catch (ex) {
+					this.showError(ex)
+				}
+			},
+
+			async clickNoCalibration() {
+				try {
+					this.$refs.refPopupCalibration.close()
+					if (this.calibratioStationType == "start") {
+						const name =
+							`${ this.stationName(this.startStationID)}_${ this.stationName(this.endStationID)}`
+						this.teachingModeCur = {
+							mode: "Stations",
+							src_dst: `${this.startStationID}_${this.endStationID}`,
+							name,
+							teaching_flag: 1
+						}
+						this.ctxDataStr = JSON.stringify([{
+							method: "set_selectable",
+							param: false,
+						}])
+						await teachingModeFlag(this.vehicleIp, this.teachingModeCur)
+
+					} else if (this.calibratioStationType == "end") {
+						this.askTeachingBiDirection(this.teachingModeCur)
+
+					}
+				} catch (ex) {
+					this.showError(ex)
+				}
+			},
+			askTeachingBiDirection(teachingMode) {
+				showModal("閫夋嫨褰撳墠绀烘暀璺嚎绫诲瀷?", "绀烘暀缁撴潫", true, "鍙屽悜璺嚎", "鍗曞悜璺嚎").then((res) => {
+					if (res) {
+						teachingMode.bidirection = "1"
+					} else {
+						teachingMode.bidirection = "0"
+					}
+					teachingMode.teaching_flag = 0
+					this.finishTeaching(teachingMode)
+				})
+
+			},
+			async finishTeaching(teachingMode) {
+				try {
+					await teachingModeFlag(this.vehicleIp, teachingMode)
+					this.reloadTeachingMode()
+					this.teachingStatus = "end"
+				} catch (ex) {
+					this.showError(ex)
+				}
+			},
+			clickBackTeaching() {
+				this.$refs.refPopupCalibration.close()
+				this.teachingStatus = ""
+			},
+			clickStationNameCancel() {
+				if (this.mapOperationType == "add_station_name") {
+					this.mapOperationType = "public_teaching"
+				}
+			},
+			async clickStationNameOK() {
+				try {
+					const name = this.stationEdit.name.trim()
+					if (!name) {
+						showToast("绔欑偣鍚嶇О杩樻湭杈撳叆")
+						return
+					}
+					if (this.mapOperationType == 'add_station_name') {
+						const agv = await this.loadAgvState()
+						this.stationEdit = {
+							stationID: this.getMaxStationNo + 1,
+							name: name,
+							x: agv.x || 0,
+							y: agv.y || 0,
+							angle: agv.angle || 0,
+						}
+						const angle2 = this.getStantardAngle(this.stationEdit.angle * 180 /3.14)
+						this.angleSvg = `/static/images/angle${angle2}.svg`
+						this.ctxDataStr = JSON.stringify([{
+							method: "update_agv_state",
+							param: agv
+						}, {
+							method: "update_station",
+							param: [this.stationEdit]
+						}, {
+							method: "edit_station_pos",
+							param: this.stationEdit
+						}, {
+							method: "move_canvas",
+							param: {
+								x: agv.x,
+								y: agv.y
+							}
+						}])
+						this.mapOperationType = "add_station_pos"
+					}
+
+				} catch (ex) {
+					this.showError(ex)
+				}
+
+			},
+
+			clickClearStationName() {
+				this.stationEdit.name = ""
+				this.showClearName = false
+			},
+			onInputStationName(event) {
+				this.stationEdit.name = event.detail.value.trim();
+				if (this.stationEdit.name)
+					this.showClearName = true
+				else
+					this.showClearName = false
+			},
+			onInputStationX(event) {
+				console.log(event)
+				this.stationEdit.x = Number(event.detail.value);
+				console.log(this.stationEdit.x)
+				this.ctxDataStr = JSON.stringify([{
+					method: "update_station",
+					param: [this.stationEdit]
+				}, {
+					method: "move_canvas",
+					param: {
+						x: this.stationEdit.x,
+						y: this.stationEdit.y
+					}
+				}])
+			},
+
+			onInputStationY(event) {
+				this.stationEdit.y = Number(event.detail.value);
+				console.log(this.stationEdit.y)
+				this.ctxDataStr = JSON.stringify([{
+					method: "update_station",
+					param: [this.stationEdit]
+				}, {
+					method: "move_canvas",
+					param: {
+						x: this.stationEdit.x,
+						y: this.stationEdit.y
+					}
+				}])
+			},
+			clickClearStationX() {
+				this.stationEdit.x = 0
+				this.ctxDataStr = JSON.stringify([{
+					method: "update_station",
+					param: [this.stationEdit]
+				}, {
+					method: "move_canvas",
+					param: {
+						x: this.stationEdit.x,
+						y: this.stationEdit.y
+					}
+				}])
+			},
+			clickClearStationY() {
+				this.stationEdit.y = 0
+				this.ctxDataStr = JSON.stringify([{
+					method: "update_station",
+					param: [this.stationEdit]
+				}, {
+					method: "move_canvas",
+					param: {
+						x: this.stationEdit.x,
+						y: this.stationEdit.y
+					}
+				}])
+			},
+			getStantardAngle(angle) {
+				let resAngle = 0
+				if (angle < -157.5) {
+					resAngle = 180
+				} else if (angle < -112.5) {
+					resAngle = -135
+				} else if (angle < -67.5) {
+					resAngle = -90
+				} else if (angle < -22.5) {
+					resAngle = -45
+				} else if (angle < 22.5) {
+					resAngle = 0
+				} else if (angle < 67.5) {
+					resAngle = 45
+				} else if (angle < 112.5) {
+					resAngle = 90
+				} else if (angle < 157.5) {
+					resAngle = 135
+				} else if (angle <= 180) {
+					resAngle = 180
+				} else {
+					resAngle = 0
+				}
+				return resAngle
+
+			},
+
+			touchAngleChange(e) {
+				let angle = 0
+
+				const ptX = e.touches[0].clientX
+
+				const offX = ptX // ptX - e.target.offsetLeft
+				const offW = 300 / 16
+				const centerPt = this.windowWidth / 2
+				if (offX < centerPt - 8 * offW) {
+					return
+				} else if (offX < centerPt - 7 * offW) {
+					angle = 180
+				} else if (offX < centerPt - 5 * offW) {
+					angle = -135
+				} else if (offX < centerPt - 3 * offW) {
+					angle = -90
+				} else if (offX <= centerPt - offW) {
+					angle = -45
+				} else if (offX < centerPt + offW) {
+					angle = 0
+				} else if (offX < centerPt + 3 * offW) {
+					angle = 45
+				} else if (offX < centerPt + 5 * offW) {
+					angle = 90
+				} else if (offX < centerPt + 7 * offW) {
+					angle = 135
+				} else if (offX <= centerPt + 8 * offW) {
+					angle = 180
+				} else {
+					return
+				}
+				this.stationEdit.angle = angle * 3.14 / 180
+
+
+				this.angleSvg = `/static/images/angle${angle}.svg`
+				this.ctxDataStr = JSON.stringify([{
+					method: "update_station",
+					param: [this.stationEdit]
+				}])
+			},
+			handleAngleTouchStart(e) {
+				//	console.log("handleAngleTouchStart", e)
+				this.touchAngleChange(e)
+
+			},
+			handleAngleTouchMove(e) {
+				//	console.log("handleAngleTouchMove", e)
+				this.touchAngleChange(e)
+			},
+
+			async clickVehiclePosition() {
+				try {
+					const infoAgv = await this.loadAgvState()
+					this.ctxDataStr = JSON.stringify([{
+						method: "update_agv_state",
+						param: infoAgv
+					}, {
+						method: "move_canvas",
+						param: {
+							x: infoAgv.x,
+							y: infoAgv.y
+						}
+					}])
+				} catch (ex) {
+					this.showError(ex)
+				}
+			},
+			async clickShowTeachingPath() {
+				try {
+					if (this.showTeachingPathFlag) {
+						this.ctxDataStr = JSON.stringify([{
+							method: "show_teaching_path",
+							param: {
+								show: false
+							}
+						}])
+						this.showTeachingPathFlag = false
+						return
+					}
+					this.showTeachingPathFlag = true
+					this.teachingMode = await this.loadTeachingMode()
+					this.ctxDataStr = JSON.stringify([{
+							method: "public_teaching_path",
+							param: this.teachingMode.Public || []
+						},
+						{
+							method: "station_teaching_path",
+							param: this.teachingMode.Stations || []
+						}, {
+							method: "show_teaching_path",
+							param: {
+								show: true
+							}
+						}
+					])
+					console.log(this.ctxDataStr)
+				} catch (ex) {
+					this.showError(ex)
+				}
+			},
+			async clickPositionStation() {
+				try {
+					const infoAgv = await this.loadAgvState()
+					this.stationEdit.x = infoAgv.x
+					this.stationEdit.y = infoAgv.y
+					this.stationEdit.angle = infoAgv.angle
+					const angle = this.getStantardAngle(this.stationEdit.angle * 180 /
+						3.14)
+					this.angleSvg = `/static/images/angle${angle}.svg`
+					this.ctxDataStr = JSON.stringify([{
+							method: "update_agv_state",
+							param: infoAgv
+						}, {
+							method: "update_station",
+							param: [this.stationEdit]
+						},
+						{
+							method: "move_canvas",
+							param: {
+								x: infoAgv.x,
+								y: infoAgv.y
+							}
+						}
+
+
+					])
+				} catch (ex) {
+					this.showError(ex)
+				}
+			},
+
+			showError(ex) {
+				let exStr = JSON.stringify(ex)
+				if (exStr == "{}")
+					exStr = ex
+				let tip = typeof ex.msg == "string" ? ex.msg : exStr
+				showModal(tip, "閿欒", false, "纭畾")
+			},
+
+
+		}
+	}
+</script>
+
+<style lang="scss">
+	.pages-map-teaching {
+		display: flex;
+		width: 750rpx;
+		height: 100vh;
+		background-color: #fff;
+		position: relative;
+
+		.uni-navbar-container-inner {
+			display: flex;
+			flex: 1;
+			flex-direction: row;
+			align-items: center;
+			justify-content: center;
+			font-size: 40rpx;
+			overflow: hidden;
+			font-weight: 700;
+			color: #333;
+
+			.icon {
+				width: 30rpx;
+				height: 16rpx;
+			}
+		}
+
+		.uni-nav-bar-text {
+			overflow: hidden;
+			white-space: nowrap;
+			text-overflow: ellipsis;
+		}
+
+		.view-content {
+			position: absolute;
+			top: 0;
+			left: 0;
+			right: 0;
+			bottom: 0;
+			background-color: #ddd;
+			display: flex;
+			flex-direction: column;
+		}
+
+		.map-content {
+			width: 100%;
+			display: flex;
+			flex: 1;
+			position: relative;
+			background-color: #aaa000;
+
+			.loading-overlay {
+				position: fixed;
+				top: 0;
+				left: 0;
+				right: 0;
+				bottom: 0;
+				background-color: rgba(0, 0, 0, 0.5);
+				display: flex;
+				justify-content: center;
+				align-items: center;
+				z-index: 999;
+
+				.loading-content {
+					background-color: #fff;
+					padding: 60rpx 100rpx;
+					border-radius: 20rpx;
+					text-align: center;
+				}
+
+				.loading-spinner {
+					width: 120rpx;
+					height: 120rpx;
+					border: 20rpx solid #f3f3f3;
+					border-top: 20rpx solid #1890FF;
+					border-radius: 50%;
+					animation: spin 1s linear infinite;
+					margin: 0 auto 40rpx;
+				}
+
+				@keyframes spin {
+					0% {
+						transform: rotate(0deg);
+					}
+
+					100% {
+						transform: rotate(360deg);
+					}
+				}
+
+			}
+
+
+			.content {
+				overflow: auto;
+				position: absolute;
+				left: 0;
+				right: 0;
+				bottom: 0;
+				top: 0;
+				background-color: #ee000e;
+
+				.fabric {
+					width: 100%;
+					height: 100%;
+					background-color: #fff;
+				}
+
+				.position {
+					position: absolute;
+					right: 50rpx;
+					bottom: 400rpx;
+
+
+					.ico {
+						font-size: 65rpx;
+						color: #1890FF;
+					}
+				}
+
+				.teaching-path-show {
+					position: absolute;
+					right: 50rpx;
+					top: 100rpx;
+					padding: 12rpx;
+					border: 1px solid #1890FF;
+					border-radius: 36rpx;
+					width: 72rpx;
+					height: 72rpx;
+					background-color: #fff;
+
+					.ico {
+						font-size: 40rpx;
+						color: #1890FF;
+					}
+				}
+
+				.selected {
+					background-color: #1890FF20;
+				}
+			}
+
+
+		}
+
+		.no-content {
+			margin-top: 50px;
+			padding: 20rpx 40rpx;
+			align-items: center;
+			text-align: center;
+			display: flex;
+			flex-direction: column;
+			font-size: 30rpx;
+			font-weight: 400;
+			background-color: #000fff;
+
+			.title {
+				font-size: 40rpx;
+				margin-bottom: 10rpx;
+			}
+
+
+		}
+
+		.bottom {
+			position: fixed;
+			left: 50rpx;
+			right: 50rpx;
+			bottom: 20rpx;
+			display: flex;
+
+			.bottom-content {
+				display: flex;
+				flex-direction: column;
+				width: 100%;
+				// justify-content: center;
+				// align-items: center;
+				padding: 0 10rpx;
+				padding-bottom: 10rpx;
+				background-color: #fff;
+				border-radius: 10rpx;
+				//background-color: #eee;
+
+				.title {
+					font-size: 40rpx;
+					margin-bottom: 10rpx;
+				}
+
+				.tip {
+					color: #888;
+					margin: 10rpx;
+					text-align: left;
+				}
+
+				.row-group {
+					display: flex;
+					flex-direction: row;
+					margin: 10rpx 0;
+
+				}
+
+				.disabled {
+					color: #ccc !important;
+				}
+
+				.angle-group {
+					padding: 10rpx;
+					display: flex;
+					flex-direction: column;
+					justify-content: center;
+					align-items: center;
+
+					.img-angle {
+						width: 128rpx;
+						height: 42rpx;
+						margin-bottom: 10rpx;
+					}
+
+					.img-angle-pos {
+						width: 300px;
+						height: 64rpx;
+						margin: 10rpx;
+					}
+
+				}
+
+				.coordinate {
+					flex: 1;
+					display: flex;
+					flex-direction: row;
+					align-items: center;
+					// .name{
+					// 	padding: 10rpx 0;
+
+					// }
+				}
+
+				.number-input {
+					flex: 1;
+					background-color: #fff;
+					padding: 10rpx;
+					border-radius: 8rpx;
+					color: #1890FF;
+				}
+
+
+				.name-input {
+					width: calc(100% - 20rpx);
+					margin-bottom: 10rpx;
+					background-color: #fff;
+					padding: 10rpx;
+					border-radius: 8rpx;
+					display: flex;
+					flex-direction: row;
+					color: #1890FF;
+
+					input {
+						flex: 1;
+					}
+				}
+			}
+
+			.img-button-group {
+				display: flex;
+				//width: 100%;
+				flex-direction: row;
+				border-radius: 10px;
+				border: 1px solid #fff;
+				font-size: 30rpx !important;
+				justify-content: space-around;
+
+				.button {
+					margin: 10rpx 20rpx;
+					height: 144rpx !important;
+					border: 0;
+					display: flex;
+					flex-direction: column;
+					background-color: #00000000;
+
+					.img {
+						margin: auto;
+						width: 72rpx;
+						height: 72rpx;
+
+					}
+
+					.ico {
+						margin: auto;
+						font-size: 72rpx;
+						color: #1890FF;
+					}
+
+					.text {
+						margin: auto;
+
+					}
+
+				}
+
+			}
+
+
+			.text-button-group {
+				display: flex;
+				width: 100%;
+				justify-content: center;
+				align-items: center;
+				flex-direction: column;
+				font-size: 30rpx !important;
+
+				.button {
+					margin: auto;
+					margin-top: 20rpx;
+					width: calc(100% - 10rpx);
+					border-radius: 30rpx;
+				}
+
+				.am-button {
+					border-radius: 30px;
+				}
+
+				.am-button-ghost {
+					border: 1px solid #1677ff !important;
+				}
+			}
+
+			.switch-type {
+				width: 100%;
+				align-items: center;
+				vertical-align: middle;
+
+				.switch-button-group {
+					width: 96%;
+					height: 32px;
+					border: 1px solid #dfdfdf;
+					border-radius: 16px;
+					background-color: #dfdfdf;
+					display: flex;
+					flex: row;
+
+					.switch-button {
+						flex: 1;
+						border-radius: 16px;
+						color: #000000A5;
+						height: 32px;
+						line-height: 32px;
+						text-align: center;
+					}
+
+					.switch-button-checked {
+						box-shadow: 0px 2px 8px 0px #0000000C;
+						background-color: #fff;
+						color: #262626;
+					}
+
+				}
+
+			}
+		}
+
+
+		.popup-content {
+			display: flex;
+			justify-content: center;
+			flex-direction: column;
+			background-color: transparent;
+		}
+
+		.popup-dialog {
+			width: 300px;
+			border-radius: 11px;
+			background-color: #fff;
+
+			.popup-dialog-title {
+				display: flex;
+				flex-direction: row;
+				justify-content: center;
+				padding-top: 15px;
+				font-size: 18px;
+				font-weight: 500;
+			}
+
+			.popup-dialog-content {
+				display: flex;
+				flex-direction: row;
+				justify-content: center;
+				align-items: center;
+				padding: 10px;
+			}
+
+			.popup-dialog-button-group {
+				display: flex;
+				flex-direction: column;
+
+				.popup-dialog-button {
+					display: flex;
+					flex-direction: row;
+					justify-content: center;
+					align-items: center;
+					height: 45px;
+					border-top: 1px solid #f5f5f5;
+					color: #1890FF;
+				}
+			}
+
+		}
+
+		.popup-content-view {
+			width: 150px;
+			height: 150px;
+			border-radius: 10rpx;
+			padding: 10rpx 20rpx;
+			background-color: #fff;
+
+			.item {
+				line-height: 75rpx;
+				text-align: center;
+				border: 0;
+
+			}
+		}
+
+		.popup-content-btn {
+			margin-bottom: 20rpx;
+			margin-left: 50rpx;
+			width: 600rpx;
+			background-color: #fff;
+			border-radius: 5px;
+			display: flex;
+
+			.img-button-group {
+				display: flex;
+				//width: 100%;
+				flex-direction: row;
+				border-radius: 10px;
+				border: 1px solid #fff;
+				font-size: 30rpx !important;
+				// justify-content: space-around;
+				margin: auto;
+
+				.button {
+					margin: 10rpx 50rpx;
+
+					height: 144rpx !important;
+					border: 0;
+					display: flex;
+					flex-direction: column;
+					background-color: #00000000;
+
+					.ico {
+						margin: auto;
+						font-size: 72rpx;
+						color: #1890FF;
+					}
+
+					.red {
+						color: #ff0000 !important;
+					}
+
+					.text {
+						margin: auto;
+
+					}
+				}
+
+			}
+
+		}
+
+	}
+</style>
\ No newline at end of file
diff --git a/pages/my/log.vue b/pages/my/log.vue
index a824105..958ad3f 100644
--- a/pages/my/log.vue
+++ b/pages/my/log.vue
@@ -1,5 +1,21 @@
 <template>
 	<view class="pages-my-log">
+		<view class="top">
+			<view class="input">
+				<input placeholder="璇疯緭鍏ユ煡鎵炬帴鍙RL" v-model="keyMethod"></input>
+				<uni-icons class="clear" color="#ccc" type="clear" size="20" v-if="keyMethod"
+					@click="clickClearKey"></uni-icons>
+			</view>
+
+			<a class="btn" @click="clickSearch">
+				<uni-icons type="search" size="24"  color="#1890FF" ></uni-icons>
+			</a>
+			<a class="btn" @click="clickClearLog">
+				<uni-icons type="trash" size="24" color="#1890FF"></uni-icons>
+			</a>
+
+		</view>
+
 		<view class="list">
 			<view class="group" v-for="(item,index) in list" :key="index">
 				<view class="title" :class="item.statusCode!=200 ? 'error':''">{{ item.date+" "+item.url}}
@@ -9,9 +25,7 @@
 				</a>
 			</view>
 		</view>
-		<view class="bottom">
-			<a-button class="button" @click="clickClearLog">娓呯┖</a-button>
-		</view>
+
 	</view>
 </template>
 <script>
@@ -30,8 +44,8 @@
 		},
 		data() {
 			return {
-				list: []
-
+				list: [],
+				keyMethod: ""
 			}
 		},
 		onLoad() {
@@ -46,9 +60,23 @@
 			clickClearLog() {
 				session.setValue("request_log", [])
 				session.setValue("request_log_max_data", {})
-				
+
 				this.list = []
-			}
+			},
+			clickSearch() {
+				const list = session.getValue("request_log")
+				const key = this.keyMethod.trim()
+				if (key)
+					this.list = list.filter((a) => a.url.toLowerCase().includes(key.toLowerCase()))
+			
+				else
+					this.list = list
+			},
+			clickClearKey() {
+				this.keyMethod = ""
+			},
+
+
 		}
 	}
 </script>
@@ -63,19 +91,18 @@
 
 		.list {
 			flex: 1;
-			overflow: auto;
+			overflow-y: auto;
 			display: flex;
 			flex-direction: column;
+			width: 750rpx;
 		}
 
 		.group {
-
-
-			width: calc(100% - 30rpx);
+			width: calc(100% - 20rpx);
 			padding: 10rpx 10rpx;
 			display: flex;
 			flex-direction: row;
-			margin: 5rpx;
+			margin: 5rpx 10rpx;
 			background-color: #fff;
 			border-radius: 10rpx;
 			font-size: 30rpx;
@@ -84,12 +111,14 @@
 				flex: 1;
 				display: flex;
 				flex-direction: row;
+
 				margin: 5rpx;
 				word-wrap: break-word;
 				/* 鍏佽闀垮崟璇嶆垨 URL 鍦板潃鎹㈣鍒颁笅涓� */
 				word-break: break-all;
 				/* 寮哄埗鏂囨湰鍦ㄨ秴鍑哄鍣ㄥ搴︽椂鎹㈣ */
 				white-space: normal;
+				min-height: 30prx;
 				/* 鍏佽鏂囨湰鎹㈣ */
 			}
 
@@ -100,17 +129,30 @@
 
 		}
 
-		.bottom {
+		.top {
 			margin: 10rpx;
+			width: 730rpx;
 			display: flex;
 			flex-direction: row;
-			justify-content: center;
-			align-items: center;
+			background-color: #F0F0F0;
 
-			.button {
-				width: 500rpx;
-				color: red;
+			.input {
+				flex: 1;
+				padding: 10rpx;
+				border-radius: 8rpx;
+				display: flex;
+				flex-direction: row;
+				background-color: #fff;
+				input {
+					flex: 1;
+				}
 			}
+
+			.btn {
+				padding: 5rpx;
+				margin: 0 10rpx;
+			}
+
 		}
 
 	}
diff --git a/pages/station/index.vue b/pages/station/index.vue
index 82b6154..a4e7884 100644
--- a/pages/station/index.vue
+++ b/pages/station/index.vue
@@ -359,7 +359,7 @@
 				this.pressStationTimer = null;
 			},
 			clickDelStation(item) {
-				showModal("纭鍒犻櫎绔欑偣", "璀﹀憡").then((res) => {
+				showModal("纭鍒犻櫎绔欑偣", "璀﹀憡",true,"纭畾","鍙栨秷").then((res) => {
 					if (res) {
 						this.deleteStation(item)
 					}
@@ -392,7 +392,7 @@
 			},
 			clickStationDelete() {
 				const _this = this
-				showModal("纭鍒犻櫎绔欑偣", "璀﹀憡").then((res) => {
+				showModal("纭鍒犻櫎绔欑偣", "璀﹀憡",true,"纭畾","鍙栨秷").then((res) => {
 					if (res) {
 						_this.deleteStation(this.stationEdit)
 						_this.$refs.refPopupOperateStation.close()
@@ -672,7 +672,7 @@
 				if (exStr == "{}")
 					exStr = ex
 				let tip = typeof ex.msg == "string" ? ex.msg : exStr
-				showModal(tip, "閿欒", false)
+				showModal(tip, "閿欒", false,"纭畾")
 			},
 
 
diff --git a/pages/task/add.vue b/pages/task/add.vue
index 4214475..a4e2980 100644
--- a/pages/task/add.vue
+++ b/pages/task/add.vue
@@ -37,7 +37,7 @@
 					</a>
 				</view>
 				<view class="item" v-if="info.tasktype == 1">
-					<view>寰幆娆℃暟锛�/view>
+					<view>閲嶅娆℃暟锛�/view>
 					<input class="right-input" type="number" :value="info.cycleTime" :maxlength="4"
 						@input="onInputCycleTime" />
 				</view>
@@ -280,7 +280,7 @@
 				}
 				if (flag) {
 
-					showModal(`褰撳墠缂栬緫鐨勫唴瀹瑰皢涓嶄細琚繚瀛榒, "纭畾瑕侀�鍑虹紪杈戝悧?").then((res) => {
+					showModal(`褰撳墠缂栬緫鐨勫唴瀹瑰皢涓嶄細琚繚瀛榒, "纭畾瑕侀�鍑虹紪杈戝悧?",true,"纭畾","鍙栨秷").then((res) => {
 						if (res) {
 							uni.navigateBack({
 								delta: 1, //杩斿洖灞傛暟锛�鍒欎笂涓婇〉
@@ -349,7 +349,7 @@
 				const item = this.pathwayList[index]
 				if (type == "stationID") {
 
-					const list = this.stationLst(item.stationID)
+					const list = this.stationList
 					let index2 = 0
 					if (list.length > 0) {
 						if (item.stationID) {
@@ -473,7 +473,7 @@
 				if (exStr == "{}")
 					exStr = ex
 				let tip = typeof ex.msg == "string" ? ex.msg : exStr
-				showModal(tip, "閿欒", false)
+				showModal(tip, "閿欒", false,"纭畾")
 			},
 
 		}
diff --git a/pages/task/infos/task-item.vue b/pages/task/infos/task-item.vue
index 0d9e44b..bf6b196 100644
--- a/pages/task/infos/task-item.vue
+++ b/pages/task/infos/task-item.vue
@@ -1,42 +1,54 @@
 <template>
 	<view class="pages-task-item">
-		<view class="btn-no " :class="taskIsRun?'active':'' ">
-			{{taskData.taskButton}}
-		</view>
-		<view class="content" @click="clickTask">
-			<view class="line">
-				<view class="title"> {{taskData.taskGroupName}}</view>
+		<view class="item-content">
+			<view class="btn-no " :class="taskIsRun?'active':'' " @click="clickButton">
+				{{taskData.taskButton}}
 			</view>
-			<view class="line">
-				{{taskListDesc}}
+			<view class="content" @click="clickTask">
+				<view class="line">
+					<view class="title"> {{taskData.taskGroupName}}</view>
+				</view>
+				<view class="line">
+					<view class="desc">
+						{{taskListDesc}}
+					</view>
+					<view class="cycle">
+						{{taskCycleTime}}
+					</view>
+				</view>
 			</view>
-		</view>
-		<view class="action-button" v-if="taskIsRun">
-<!-- 			<button type="primary" v-show="false" :plain="true" class="button" @click.stop="clickRepeatTask">
+			<view class="action-button" v-if="taskIsRun">
+				<!-- 			<button type="primary" v-show="false" :plain="true" class="button" @click.stop="clickRepeatTask">
 				<text class="ico repeat" />
 			</button> -->
-			<button type="primary" :plain="true" class="button" @click.stop="clickSkipTask">
-				<text class="ico skip-next-rounded" />
-			</button>
-			<button type="primary" :plain="true" class="button" @click.stop="clickStopTask">
-				<text class="ico stop" />
-			</button>
-			<!-- 			<button type="primary" v-show="false" :plain="true" class="button" @click.stop="clickPauseTask">
+				<button type="primary" :plain="true" class="button" @click.stop="clickSkipTask">
+					<text class="ico skip-next-rounded" />
+				</button>
+				<button type="primary" :plain="true" class="button" @click.stop="clickStopTask">
+					<text class="ico stop" />
+				</button>
+				<!-- 			<button type="primary" v-show="false" :plain="true" class="button" @click.stop="clickPauseTask">
 				<text class="ico pause-rounded" />
 			</button>
 			<button type="primary" v-show="false" :plain="true" class="button" @click.stop="clickPlayTask">
 				<text class="ico play-filled" />
 			</button> -->
+			</view>
+			<view class="action-button" v-else>
+				<button :type=" !taskCanRun ? 'default':'primary'" :plain="true" class="button" :disabled=" !taskCanRun"
+					@click.stop="clickPlayTask">
+					<text class="ico play-filled " :class="taskCanRun ? '':'disabled'" />
+				</button>
+			</view>
 		</view>
-		<view class="action-button" v-else>
-			<button :type=" !taskCanRun ? 'default':'primary'" :plain="true" class="button" :disabled=" !taskCanRun"
-				@click.stop="clickPlayTask">
-				<text class="ico play-filled " :class="taskCanRun ? '':'disabled'" />
-			</button>
+		<view class="item-sub" v-if="showTaskRunning && taskIsRun">
+			<view class="sub-item" v-for="(item,index) in taskStatusList" :key="item.taskID">
+				<text class="text">{{getStatusText(item.status)}}</text>
+				<text class="text">{{"鐩爣鐐�+(index+ 1)}}</text>
+				<text class="text">{{getDestText(item)}}</text>
+				<text class="text">{{getActionText(item)}}</text>
+			</view>
 		</view>
-		<!-- <view class="btn-del" :style="{ transform: `translateX(${deleteX || 0}px)` }" @click="clickDelete">
-			鍒犻櫎</view> -->
-
 	</view>
 
 </template>
@@ -73,6 +85,13 @@
 					return 0;
 				}
 			},
+			showTaskRunning: {
+				type: Boolean,
+				default () {
+					return false;
+				}
+			},
+
 		},
 		data() {
 			return {}
@@ -81,7 +100,18 @@
 			taskListDesc() {
 				let list = this.taskData.taskList || []
 				list = list.map((a) => a.dest?.name)
+
 				return list.join("-")
+			},
+			taskCycleTime() {
+
+				if (this.taskIsRun)
+					return `绗�{this.taskStatus?.curCycleNumber || 0}/${this.taskStatus?.cycleTotleNumber || 0}娆
+
+				else
+					return `閲嶅${this.taskData.cycleTime || 0}娆
+
+
 			},
 			taskIsRun() {
 				if (this.taskStatus?.taskGroupID == this.taskData.taskGroupID) {
@@ -97,6 +127,16 @@
 					return true
 				}
 			},
+
+			taskStatusList() {
+				if (this.taskIsRun) {
+					let list = this.taskStatus.taskStatusList || []
+					list = list.filter((a) => a.cycleNumber == this.taskStatus.curCycleNumber)
+					return list
+				} else {
+					return []
+				}
+			}
 
 		},
 
@@ -122,6 +162,11 @@
 					});
 				});
 			},
+			clickButton() {
+				if (this.taskIsRun)
+					this.$emit('click-btn', this.taskData)
+			},
+
 			clickTask() {
 				this.$emit('click-item', this.taskData)
 			},
@@ -151,7 +196,48 @@
 				this.$emit('click-delete', this.taskData)
 			},
 
-
+			getStatusText(status) {
+				var statusText = "鏈煡"
+				if (!status) {
+					statusText = "鏈紑濮�
+				} else if (status === 5) {
+					statusText = "寮哄埗瀹屾垚"
+				} else if (status === 4) {
+					statusText = "寮傚父缁撴潫"
+				} else if (status === 3) {
+					statusText = "宸插彇娑�
+				} else if (status === 2) {
+					statusText = "宸插畬鎴�
+				} else if (status === 1) {
+					statusText = "鎵ц涓�
+				}
+				return statusText
+			},
+			getDestText(item) {
+				const index = this.taskData.taskList.findIndex((a) => a.taskID == item.taskID)
+				if (index > -1) {
+					return this.taskData.taskList[index]?.dest?.name || ""
+				}
+				return ""
+			},
+			getActionText(item) {
+				const index = this.taskData.taskList.findIndex((a) => a.taskID == item.taskID)
+				if (index > -1) {
+					const task = this.taskData.taskList[index]
+					var actionText = "鏈煡"
+					if (task.actionType === 1) {
+						actionText = "瀵艰埅"
+					} else if (task.actionType === 2) {
+						actionText = "鍙栬揣"
+					} else if (task.actionType === 3) {
+						actionText = `鍗歌揣 绛夊緟${taskData.taskList[index]?.wait || 0}绉抈
+					} else if (task.actionType === 4) {
+						actionText = "浜哄伐"
+					}
+					return actionText
+				}
+				return ""
+			},
 
 		}
 	}
@@ -159,20 +245,49 @@
 
 <style lang="scss" scoped>
 	.pages-task-item {
+
 		width: 100%;
 		display: flex;
-		flex-direction: row;
-		position: relative;
-		align-items: center;
+		flex-direction: column;
+
+		.item-content {
+			width: 100%;
+			display: flex;
+			flex-direction: row;
+			align-items: center;
+		}
+
+		.item-sub {
+			display: flex;
+			flex-direction: column;
+			padding: 10rpx 30rpx 10rpx 50rpx;
+
+			.sub-item {
+				color: gray;
+				padding: 10rpx;
+				border-bottom: 1px solid #ddd;
+				display: flex;
+				flex-direction: row;
+				.text {
+					color: gray;
+					padding: 10rpx;
+				}
+			}
+
+			.sub-item:last-child {
+				border-bottom: 0;
+			}
+		}
 
 		.btn-no {
+			display: flex;
 			margin: 0 10rpx 0 20rpx;
 			border-radius: 25rpx;
 			background-color: #E6F7FF;
 			color: #1890FF;
 			width: 50rpx;
 			height: 50rpx;
-			line-height:54rpx ;
+			line-height: 54rpx;
 			justify-content: center;
 			align-items: center;
 			text-align: center;
@@ -184,7 +299,10 @@
 		}
 
 		.content {
+			display: flex;
+			flex-direction: column;
 			flex: 1;
+			min-width: 0;
 			padding: 5px;
 
 			.line {
@@ -196,15 +314,27 @@
 				.title {
 					font-size: 36rpx;
 					font-weight: 600;
+					min-width: 0;
+					/* 鍏抽敭锛氬厑璁告敹缂╁埌姣斿唴瀹硅繕绐�*/
+					overflow: hidden;
+					text-overflow: ellipsis;
+					white-space: nowrap;
 				}
 
-				.text {
-					padding: 0 10px;
-					font-size: 14px;
-					color: #1ABC9C;
+				.desc {
 					flex: 1;
+					color: gray;
+					min-width: 0;
+					/* 鍏抽敭锛氬厑璁告敹缂╁埌姣斿唴瀹硅繕绐�*/
+					overflow: hidden;
+					text-overflow: ellipsis;
+					white-space: nowrap;
 				}
 
+				.cycle {
+					color: gray;
+					padding: 0 10rpx;
+				}
 
 
 			}
@@ -229,34 +359,19 @@
 					color: #1890FF;
 
 				}
-				.disabled{
+
+				.disabled {
 					color: gray !important;
 				}
 
 			}
-			.disabled{
+
+			.disabled {
 				color: gray;
 			}
 
 
 		}
 
-		.btn-del {
-			position: absolute;
-			right: -80px;
-			top: 0;
-			padding: 0 8px;
-			line-height: 100px;
-			background-color: #CC001A;
-			font-weight: 700;
-			color: #fff;
-			border: none;
-			border-radius: 10px;
-			border-bottom-right-radius: 0px;
-			border-bottom-left-radius: 0px;
-			border-top-left-radius: 0px;
-			transform: translateX(100%);
-			transition: transform 0.3s;
-		}
 	}
 </style>
\ No newline at end of file
diff --git a/pages/task/infos/task-log-item.vue b/pages/task/infos/task-log-item.vue
index 5dba57c..7684d4f 100644
--- a/pages/task/infos/task-log-item.vue
+++ b/pages/task/infos/task-log-item.vue
@@ -112,6 +112,7 @@
 		flex-direction: row;
 		align-items: center;
 
+
 		.btn-no {
 			margin: 0 10rpx 0 20rpx;
 			border-radius: 25rpx;
@@ -128,6 +129,7 @@
 
 		.content {
 			flex: 1;
+			padding: 5px;
 
 			.line {
 				padding: 10rpx 0;
diff --git a/pages/task/list.vue b/pages/task/list.vue
index 0f2d992..afa7af0 100644
--- a/pages/task/list.vue
+++ b/pages/task/list.vue
@@ -3,11 +3,12 @@
 		<view class="list-content" v-if="list.length > 0">
 			<view class="list-header" v-if="fixedList.length > 0">鍥哄畾浠诲姟</view>
 			<uni-swipe-action class="list">
-				<uni-swipe-action-item :class="index== 0 ?'':'list-item'" v-for="(item,index) in fixedList" :key="index"
+				<uni-swipe-action-item class="list-item" v-for="(item,index) in fixedList" :key="index"
 					:auto-close="true" :disabled="taskStatus.taskGroupID == item.taskGroupID">
-					<TaskItemView :taskData="item" :taskStatus="taskStatus" @click-item="clickEditTask"
-						@click-play="clickPlayTask" @click-pause="clickPauseTask" @click-stop="clickStopTask"
-						@click-skip="clickSkipTask" @click-repeat="clickRepeatTask">
+					<TaskItemView :taskData="item" :taskStatus="taskStatus" :showTaskRunning="showTaskRunning"
+						@click-btn="clickTaskBtn" @click-item="clickEditTask" @click-play="clickPlayTask"
+						@click-pause="clickPauseTask" @click-stop="clickStopTask" @click-skip="clickSkipTask"
+						@click-repeat="clickRepeatTask">
 					</TaskItemView>
 					<template v-slot:right>
 						<view class="btn-del" @click="clickDelSetTask(item)">鍒犻櫎</view>
@@ -16,12 +17,12 @@
 			</uni-swipe-action>
 			<view class="list-header" v-if="tempList.length > 0">涓存椂浠诲姟</view>
 			<uni-swipe-action class="list">
-				<uni-swipe-action-item :class="index== 0 ?'':'list-item'" v-for="(item,index) in tempList" :key="index"
+				<uni-swipe-action-item class="list-item" v-for="(item,index) in tempList" :key="index"
 					:auto-close="true" :disabled="taskStatus.taskGroupID == item.taskGroupID">
-					<TaskItemView :taskData="item" :taskStatus="taskStatus" @click-item="clickEditTask"
-						@click-play="clickPlayTask" @click-pause="clickPauseTask" @click-stop="clickStopTask"
-						@click-skip="clickSkipTask" @click-repeat="clickRepeatTask">
-					</TaskItemView>
+					<TaskItemView :taskData="item" :taskStatus="taskStatus" :showTaskRunning="showTaskRunning"
+						@click-btn="clickTaskBtn" @click-item="clickEditTask" @click-play="clickPlayTask"
+						@click-pause="clickPauseTask" @click-stop="clickStopTask" @click-skip="clickSkipTask"
+						@click-repeat="clickRepeatTask"></TaskItemView>
 					<template v-slot:right>
 						<view class="btn-del" @click="clickDelSetTask(item)">鍒犻櫎</view>
 					</template>
@@ -57,6 +58,9 @@
 	import {
 		v4 as uuidv4
 	} from 'uuid';
+	import {
+		filter
+	} from "rxjs";
 	export default {
 		name: "PagesTasklist",
 		components: {
@@ -85,6 +89,7 @@
 					x: 0,
 					y: 0
 				},
+				showTaskRunning: false,
 			}
 		},
 		computed: {
@@ -98,7 +103,21 @@
 		onLoad(option) {
 			this.ip = option.ip || ""
 			this.sceneId = option.sceneId || ""
+			this.isPageVisible = true
+
 			this.loadData()
+		},
+		onShow() {
+			this.isPageVisible = true
+
+
+		},
+		onHide() {
+			this.isPageVisible = false
+
+		},
+		onUnload() {
+			this.isPageVisible = false
 		},
 		methods: {
 			setData(obj) {
@@ -135,50 +154,57 @@
 						}
 						this.btnAddInfo = btninfo
 					}
-					const res = await tasks(this.ip)
-					const list = res?.data || []
-					this.list = list
-					if (this.list.length > 0) {
-						this.timerCheckTaskGroupStatus()
-					}
+					this.loadTaskList()
+					this.timerCheckTaskGroupStatus()
+
 				} catch (ex) {
 
 					this.showError(ex)
 				}
 			},
+			async loadTaskList() {
+				try {
+					const res = await tasks(this.ip)
+					const list = res?.data || []
+					this.list = list
+				} catch (ex) {
+
+					this.showError(ex)
+				}
+			},
+
 			async timerCheckTaskGroupStatus() {
-				if (this.timerCheckStatusId) {
-					return
+				try {
+					if (this.isPageVisible)
+						await this.checkTaskGroupStatus()
+				} catch (ex) {
+					this.showError(ex)
+				} finally {
+					setTimeout(this.timerCheckTaskGroupStatus, 1000);
 				}
-				if (this.list.length == 0) {
-					return
-				}
-				await this.checkTaskGroupStatus()
-				this.timerCheckStatusId = setTimeout(() => {
-					this.timerCheckStatusId = 0
-					this.timerCheckTaskGroupStatus()
-				}, 1000)
 			},
 			async checkTaskGroupStatus() {
 				try {
-					const key = `task_cmd_id_${this.ip.replace(".","_")}`
-					const cmdID = session.getValue(key)
-					if (cmdID) {
-						const info = await taskGroupStatus(this.ip) || {}
-						if (info.taskGroupCmdID == cmdID) {
-
-							const list = info.taskStatusList || []
+					const info = await taskGroupStatus(this.ip) || {}
+					if (info.taskGroupID) {
+						if (this.list.length == 0) {
+							await this.loadTaskList()
+						}
+						const curIndex = this.list.findIndex((a) => a.taskGroupID == info.taskGroupID)
+						if (curIndex > -1) {
+							let list = info.taskStatusList || []
+							list = list.filter((a) => a.cycleNumber == info.curCycleNumber)
 							for (let i in list) {
-								if (list[i].status != 1 && list[i].status != 0) {
-									this.taskStatus = {}
+								if (list[i].status == 1 || list[i].status == 0) {
+									this.taskStatus = info
 									return
 								}
 							}
-							this.taskStatus = info
+							this.taskStatus = {}
 							return
 						}
-
 					}
+
 					this.taskStatus = {}
 
 				} catch (ex) {
@@ -199,7 +225,7 @@
 					events: {
 						// 涓烘寚瀹氫簨浠舵坊鍔犱竴涓洃鍚櫒锛岃幏鍙栬鎵撳紑椤甸潰浼犻�鍒板綋鍓嶉〉闈㈢殑鏁版嵁
 						add_task: function(data) {
-							_this.loadData()
+							_this.loadTaskList()
 						},
 					}
 				})
@@ -244,8 +270,9 @@
 
 				session.setValue('task_btn_add', this.btnAddInfo)
 			},
-
-
+			clickTaskBtn(item) {
+				this.showTaskRunning = !this.showTaskRunning
+			},
 			clickEditTask(item) {
 				const _this = this
 				if (this.taskStatus.taskGroupID == item.taskGroupID) {
@@ -285,6 +312,7 @@
 			async clickStopTask(item) {
 				try {
 					await addTaskGroupCmd(this.ip, item.taskGroupID, this.taskStatus.taskGroupCmdID, 2)
+					showToast("缁堟浠诲姟鎴愬姛")
 				} catch (ex) {
 					this.showError(ex)
 				}
@@ -294,7 +322,8 @@
 				try {
 
 
-					const taskStatusList = this.taskStatus.taskStatusList || []
+					let taskStatusList = this.taskStatus.taskStatusList || []
+					taskStatusList = taskStatusList.filter((a) => a.cycleNumber == this.taskStatus.curCycleNumber)
 					let curIdx = taskStatusList.findIndex((a) => a.status == 1)
 					if (curIdx < 0)
 						curIdx = taskStatusList.findIndex((a) => a.status == 0)
@@ -306,7 +335,7 @@
 						})
 						await cancelTask(this.ip,
 							item.taskGroupID,
-							 this.taskStatus.taskGroupCmdID,
+							this.taskStatus.taskGroupCmdID,
 							taskList
 						)
 
@@ -327,7 +356,7 @@
 
 			async clickPauseTask(item) {},
 			clickDelSetTask(item) {
-				showModal("纭鍒犻櫎浠诲姟璁剧疆", "璀﹀憡").then((res) => {
+				showModal("纭鍒犻櫎浠诲姟璁剧疆", "璀﹀憡", true, "纭畾", "鍙栨秷").then((res) => {
 					if (res) {
 						this.deleteTask(item)
 
@@ -343,7 +372,7 @@
 					const index = list.findIndex((a) =>
 						a.taskGroupID == item.taskGroupID
 					)
-					console.log(index, item, list)
+					showToast("鍒犻櫎浠诲姟鎴愬姛")
 					if (index < 0)
 						return
 					list.splice(index, 1)
@@ -360,8 +389,8 @@
 				let exStr = JSON.stringify(ex)
 				if (exStr == "{}")
 					exStr = ex
-				let tip = typeof ex.msg == "string" ? ex.msg : exStr
-				showModal(tip, "閿欒", false)
+				let tip = typeof ex.msg == "string" ? ex.msg : typeof ex.errMsg == "string" ? ex.errMsg : exStr
+				showModal(tip, "閿欒", false, "纭畾")
 			},
 		}
 	}
@@ -378,15 +407,26 @@
 		.list-content {
 			display: flex;
 			width: 100%;
-			padding: 0 10px;
+			padding: 10rpx 10px;
 			flex: 1;
 			flex-direction: column;
 			overflow-y: auto;
 			overflow-x: hidden;
 
 			.list-header {
-				margin: 10px;
+				margin: 10rpx 10px;
 				// border-bottom: 1px solid #ddd;
+			}
+
+			.btn-del {
+				// margin: 10rpx;
+				display: flex;
+				padding: 0 10rpx;
+				background-color: #FF4D4F;
+				font-weight: 700;
+				color: #fff;
+				justify-content: center;
+				align-items: center;
 			}
 
 			.list {
@@ -397,20 +437,23 @@
 
 				.list-item {
 
-					border-top: 1px solid #ddd;
+					border-bottom: 1px solid #ddd;
 				}
+
+				.list-item:first-child {
+					border-top-right-radius: 10px;
+					/* 鍙充笅瑙�*/
+				}
+
+				.list-item:last-child {
+					border-bottom: 0;
+					border-bottom-right-radius: 10px;
+					/* 鍙充笅瑙�*/
+				}
+
 			}
 
-			.btn-del {
-				margin: 10rpx;
-				display: flex;
-				padding: 0 10rpx;
-				background-color: #FF4D4F;
-				font-weight: 700;
-				color: #fff;
-				justify-content: center;
-				align-items: center;
-			}
+
 
 		}
 
diff --git a/pages/task/log-list.vue b/pages/task/log-list.vue
index 80acc20..7d2f6d4 100644
--- a/pages/task/log-list.vue
+++ b/pages/task/log-list.vue
@@ -20,7 +20,7 @@
 				<template v-for="(group) in fixedList" :key="group.date">
 					<view class="task-header">{{group.date}}</view>
 					<view class="task-list-view">
-						<TaskLogItemView v-for="(item,index) in group.list" :key="index" :taskData="item">
+						<TaskLogItemView class="list-item" v-for="(item,index) in group.list" :key="index" :taskData="item">
 						</TaskLogItemView>
 					</view>
 				</template>
@@ -28,7 +28,7 @@
 				<template v-for="(group) in tempList" :key="group.date">
 					<view class="task-header">{{group.date}}</view>
 					<view class="task-list-view">
-						<TaskLogItemView v-for="(item,index) in group.list" :key="index" :taskData="item">
+						<TaskLogItemView class="list-item" v-for="(item,index) in group.list" :key="index" :taskData="item">
 
 						</TaskLogItemView>
 					</view>
@@ -59,6 +59,7 @@
 		},
 		data() {
 			return {
+				ip:"",
 				sceneId: "",
 				list: [],
 			}
@@ -245,7 +246,7 @@
 					}]*/
 					this.list = await this.loadTaskLog()
 				} catch (ex) {
-					show.showError(ex)
+					this.showError(ex)
 				}
 			},
 			async loadTaskLog() {
@@ -302,7 +303,7 @@
 					console.log(list)
 					return list
 				} catch (ex) {
-					show.showError(ex)
+					this.showError(ex)
 					return []
 				}
 			},
@@ -317,7 +318,7 @@
 				if (exStr == "{}")
 					exStr = ex
 				let tip = typeof ex.msg == "string" ? ex.msg : exStr
-				showModal(tip, "閿欒", false)
+				showModal(tip, "閿欒", false,"纭畾")
 			},
 		}
 	}
@@ -384,8 +385,16 @@
 				.task-list-view {
 					width: 100% ;
 					border-radius: 10rpx;
-					padding: 0 10rpx;
+					// padding: 0 10rpx;
 					background-color: #fff;
+					.list-item {
+					
+						border-bottom: 1px solid #ddd;
+					}
+					.list-item:last-child {
+						border-bottom: 0;
+						/* 鍙充笅瑙�*/
+					}
 				}
 
 
diff --git a/pages/task/map-task.vue b/pages/task/map-task.vue
index 0859ffc..58776c7 100644
--- a/pages/task/map-task.vue
+++ b/pages/task/map-task.vue
@@ -238,7 +238,7 @@
 				})
 			},
 			clickDelSetTask(item) {
-				showModal("纭鍒犻櫎浠诲姟璁剧疆", "璀﹀憡").then((res) => {
+				showModal("纭鍒犻櫎浠诲姟璁剧疆", "璀﹀憡",true,"纭畾","鍙栨秷").then((res) => {
 					if (res) {
 						const list = this.setList
 						const index = list.findIndex((a) => {
diff --git a/pages/task/update.vue b/pages/task/update.vue
index dd00fe8..421d5a9 100644
--- a/pages/task/update.vue
+++ b/pages/task/update.vue
@@ -38,7 +38,7 @@
 					</a>
 				</view>
 				<view class="item" v-if="form.tasktype == 1">
-					<view>寰幆娆℃暟锛�/view>
+					<view>閲嶅娆℃暟锛�/view>
 					<input class="right-input" type="number" :value="form.cycleTime" :maxlength="4"
 						@input="onInputCycleTime" />
 				</view>
@@ -334,7 +334,7 @@
 
 
 				if (flag) {
-					showModal(`褰撳墠缂栬緫鐨勫唴瀹瑰皢涓嶄細琚繚瀛榒, "纭畾瑕侀�鍑虹紪杈戝悧?").then((res) => {
+					showModal(`褰撳墠缂栬緫鐨勫唴瀹瑰皢涓嶄細琚繚瀛榒, "纭畾瑕侀�鍑虹紪杈戝悧?",true,"纭畾","鍙栨秷").then((res) => {
 						if (res) {
 							uni.navigateBack({
 								delta: 1, //杩斿洖灞傛暟锛�鍒欎笂涓婇〉
@@ -555,7 +555,7 @@
 				if (exStr == "{}")
 					exStr = ex
 				let tip = typeof ex.msg == "string" ? ex.msg : exStr
-				showModal(tip, "閿欒", false)
+				showModal(tip, "閿欒", false,"纭畾")
 			},
 
 		}
diff --git a/pages/teaching/index.vue b/pages/teaching/index.vue
index 2b171f9..924f9a3 100644
--- a/pages/teaching/index.vue
+++ b/pages/teaching/index.vue
@@ -379,18 +379,11 @@
 							this.calibratioStationType = "end"
 							this.$refs.refPopupCalibration.open()
 						} else {
-							this.teachingModeCur.teaching_flag = 0
-							await teachingModeFlag(this.ip, this.teachingModeCur)
-							this.teachingMode = await getTeachingMode(this.ip)
-
-							this.teachingStatus = "end"
+							this.askTeachingBiDirection(this.teachingModeCur)
 
 						}
 					} else {
-						this.teachingModeCur.teaching_flag = 0
-						await teachingModeFlag(this.ip, this.teachingModeCur)
-						this.teachingMode = await getTeachingMode(this.ip)
-						this.teachingStatus = "end"
+						this.askTeachingBiDirection(this.teachingModeCur)
 					}
 				} catch (ex) {
 					this.showError(ex)
@@ -639,10 +632,8 @@
 							list[curIndex].y = infoAgv.y
 
 						}
-						this.teachingStatus = "end"
-						this.teachingModeCur.teaching_flag = 0
-						await teachingModeFlag(this.ip, this.teachingModeCur)
-						this.teachingMode = await getTeachingMode(this.ip)
+						this.askTeachingBiDirection(this.teachingModeCur)
+
 
 					}
 				} catch (ex) {
@@ -667,11 +658,30 @@
 
 					} else if (this.calibratioStationType == "end") {
 
-						this.teachingModeCur.teaching_flag = 0
-						await teachingModeFlag(this.ip, this.teachingModeCur)
-						this.teachingMode = await getTeachingMode(this.ip)
+						this.askTeachingBiDirection(this.teachingModeCur)
 
 					}
+				} catch (ex) {
+					this.showError(ex)
+				}
+			},
+			askTeachingBiDirection(teachingMode) {
+				showModal("鏄惁灏嗗綋鍓嶇ず鏁欒矾绾胯缃负鍙屽悜?", "绀烘暀缁撴潫").then((res) => {
+					if (res) {
+						teachingMode.bidirection = "1"
+					} else {
+						teachingMode.bidirection = "0"
+					}
+					teachingMode.teaching_flag = 0
+					this.finishTeaching(teachingMode)
+				})
+
+			},
+			async finishTeaching(teachingMode) {
+				try {
+					await teachingModeFlag(this.ip, teachingMode)
+					this.teachingMode = await getTeachingMode(this.ip)
+					this.teachingStatus = "end"
 				} catch (ex) {
 					this.showError(ex)
 				}
@@ -685,7 +695,7 @@
 				if (exStr == "{}")
 					exStr = ex
 				let tip = typeof ex.msg == "string" ? ex.msg : exStr
-				showModal(tip, "閿欒", false)
+				showModal(tip, "閿欒", false, "纭畾")
 			},
 
 
diff --git a/pages/teaching/list.vue b/pages/teaching/list.vue
index 35d2ef8..8894b87 100644
--- a/pages/teaching/list.vue
+++ b/pages/teaching/list.vue
@@ -225,7 +225,7 @@
 				return tip
 			},
 			clickDelTeachingMode(mode, item) {
-				showModal("纭鍒犻櫎绀烘暀", "璀﹀憡").then((res) => {
+				showModal("纭鍒犻櫎绀烘暀", "璀﹀憡",true,"纭畾","鍙栨秷").then((res) => {
 					if (res) {
 						this.deleteTeachingMode(mode, item)
 					}
@@ -269,7 +269,7 @@
 				if (exStr == "{}")
 					exStr = ex
 				let tip = typeof ex.msg == "string" ? ex.msg : exStr
-				showModal(tip, "閿欒", false)
+				showModal(tip, "閿欒", false,"纭畾")
 			},
 
 
diff --git a/static/images/van.svg b/static/images/van.svg
index 356c12c..35d8433 100644
--- a/static/images/van.svg
+++ b/static/images/van.svg
@@ -1,10 +1,10 @@
 <svg width="40" height="84" viewBox="0 0 40 84" fill="none" xmlns="http://www.w3.org/2000/svg">
-<rect x="18" y="74" width="4" height="10" fill="#434343"/>
-<rect x="34" y="80" width="4" height="28" rx="2" transform="rotate(90 34 80)" fill="#434343"/>
-<rect x="4" width="12" height="62" rx="6" fill="#434343"/>
-<rect x="24" width="12" height="62" rx="6" fill="#434343"/>
-<rect x="8" y="8" width="4" height="8" rx="2" fill="#FA8C16"/>
-<rect x="28" y="8" width="4" height="8" rx="2" fill="#FA8C16"/>
-<rect y="48" width="40" height="28" rx="4" fill="#FA8C16"/>
-<circle cx="32" cy="56" r="4" fill="#434343"/>
+<rect x="22" y="10" width="4" height="10" transform="rotate(-180 22 10)" fill="#434343"/>
+<rect x="6" y="4" width="4" height="28" rx="2" transform="rotate(-90 6 4)" fill="#434343"/>
+<rect x="36" y="84" width="12" height="62" rx="6" transform="rotate(-180 36 84)" fill="#8A98A8"/>
+<rect x="16" y="84" width="12" height="62" rx="6" transform="rotate(-180 16 84)" fill="#8A98A8"/>
+<rect x="32" y="76" width="4" height="8" rx="2" transform="rotate(-180 32 76)" fill="#FA8C16"/>
+<rect x="12" y="76" width="4" height="8" rx="2" transform="rotate(-180 12 76)" fill="#FA8C16"/>
+<rect x="40" y="36" width="40" height="28" rx="4" transform="rotate(-180 40 36)" fill="#284962"/>
+<circle cx="8" cy="28" r="4" transform="rotate(-180 8 28)" fill="#FF4D4F"/>
 </svg>
diff --git a/static/images/view1.png b/static/images/view1.png
deleted file mode 100644
index cfa3e58..0000000
--- a/static/images/view1.png
+++ /dev/null
Binary files differ

--
Gitblit v1.9.1