From 2af5f043b60c1f7ac38ecccc8f5bf44743134325 Mon Sep 17 00:00:00 2001
From: cuiqian2004 <cuiqian2004@163.com>
Date: 星期五, 12 十二月 2025 18:08:00 +0800
Subject: [PATCH] test

---
 fonts/fonticon/fonticon.css                                                            |    3 
 pages/map/scene.vue                                                                    |  125 
 comm/utils.js                                                                          |   56 
 uni_modules/uni-icons/changelog.md                                                     |    6 
 pages/my/log.vue                                                                       |   26 
 pages/map/teaching.vue                                                                 |   63 
 pages/my/help-feedback.vue                                                             |   37 
 pages/station/index.vue                                                                |  122 
 pages/map/infos/pallet-size.vue                                                        |  175 
 pages/my/language.vue                                                                  |    9 
 api/version.js                                                                         |  219 
 api/request.js                                                                         |   30 
 package.json                                                                           |    1 
 pages/map/task.vue                                                                     |   19 
 uni_modules/uni-icons/components/uni-icons/uni-icons.vue                               |    6 
 components/calendar/infos/date-item.vue                                                |  134 
 pages/version/app-update.vue                                                           |  313 +
 pages/task/map-task.vue                                                                |   22 
 package-lock.json                                                                      |    9 
 api/vehicle.js                                                                         |  496 +
 comm/extend.js                                                                         |  446 +
 pages/task/log-list.vue                                                                |   43 
 App.vue                                                                                |   18 
 pages/login/forgot-password.vue                                                        |   12 
 comm/calendarLunar.js                                                                  |  556 ++
 components/cmd-progress/index.vue                                                      |  558 ++
 pages/my/instruction.vue                                                               |    7 
 components/calendar/index.vue                                                          |  238 
 pages/station/delete.vue                                                               |   34 
 uni_modules/uni-datetime-picker/changelog.md                                           |  173 
 comm/utils-android.js                                                                  |   25 
 uni_modules/uni-datetime-picker/package.json                                           |  107 
 uni_modules/uni-datetime-picker/components/uni-datetime-picker/uni-datetime-picker.vue | 1068 ++++
 pages/task/infos/task-log-item.vue                                                     |   18 
 pages/teaching/index.vue                                                               |   59 
 fonts/fonticon/fonts/fonticon.eot                                                      |    0 
 pages/task/update.vue                                                                  |  133 
 pages/version/car-program-download.vue                                                 |  347 +
 locale/en.json                                                                         |  474 +
 fonts/fonticon/fonts/fonticon.ttf                                                      |    0 
 pages/index/connect.vue                                                                |   74 
 pages/my/log-detail.vue                                                                |   65 
 pages/my/index.vue                                                                     |   51 
 pages/map/js/ctx.js                                                                    |  229 
 uni_modules/uni-datetime-picker/readme.md                                              |   21 
 locale/index.js                                                                        |   23 
 uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar-item.vue       |  177 
 uni_modules/uni-icons/components/uni-icons/uni-icons.uvue                              |  158 
 .hbuilderx/launch.json                                                                 |   13 
 pages/login/index.vue                                                                  |  367 +
 uni_modules/uni-icons/package.json                                                     |  110 
 pages/login/register.vue                                                               |   13 
 pages/teaching/list.vue                                                                |   29 
 pages/task/list.vue                                                                    |   88 
 pages/task/add.vue                                                                     |  128 
 components/calendar/infos/date.vue                                                     |  732 ++
 pages/error/index.vue                                                                  |  591 ++
 uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hans.json       |   22 
 pages/index/detail.vue                                                                 |   50 
 pages/my/email.vue                                                                     |  321 +
 comm/calendarUtil.js                                                                   |  440 +
 /dev/null                                                                              |    8 
 uni_modules/uni-datetime-picker/components/uni-datetime-picker/util.js                 |  421 +
 pages/task/infos/task-item.vue                                                         |   87 
 uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/en.json            |   22 
 pages/map/index.vue                                                                    |  712 +-
 locale/zh-Hans.json                                                                    |  487 +
 pages/index/index.vue                                                                  |  393 +
 pages/version/car-program-upload.vue                                                   |  399 +
 pages/my/log_upload.vue                                                                |  296 +
 fonts/fonticon/fonts/fonticon.woff                                                     |    0 
 pages/map/js/ctx-mini.js                                                               |    4 
 locale/zh-Hant.json                                                                    |  475 +
 pages/index/backup.vue                                                                 |  266 
 uni_modules/uni-datetime-picker/components/uni-datetime-picker/time-picker.vue         |  940 +++
 pages/map/infos/scene-rename.vue                                                       |  152 
 pages.json                                                                             |   91 
 uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar.vue            |  947 +++
 pages/error/infos/log-item.vue                                                         |  229 
 uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hant.json       |   22 
 comm/utils-ios.js                                                                      |   22 
 manifest.json                                                                          |   10 
 uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/index.js           |    8 
 main.js                                                                                |   35 
 pages/task/index.vue                                                                   |   12 
 85 files changed, 14,466 insertions(+), 1,461 deletions(-)

diff --git a/.hbuilderx/launch.json b/.hbuilderx/launch.json
index 7e4d039..a7d43a9 100644
--- a/.hbuilderx/launch.json
+++ b/.hbuilderx/launch.json
@@ -10,12 +10,11 @@
             "playground" : "standard",
             "type" : "uni-app:app-android"
         },
-	{
-		"app-plus" : 
-		{
-			"launchtype" : "local"
-		},
-		"type" : "uniCloud"
-	}
+        {
+            "app-plus" : {
+                "launchtype" : "local"
+            },
+            "type" : "uniCloud"
+        }
     ]
 }
diff --git a/App.vue b/App.vue
index e6f487c..94d7456 100644
--- a/App.vue
+++ b/App.vue
@@ -1,4 +1,8 @@
 <script>
+	import {
+		showToast
+	} from './comm/utils'
+
 	export default {
 		globalData: {
 			platform: "",
@@ -16,13 +20,25 @@
 				img_x: 1,
 				img_y: 1,
 			},
-			withLog:false
+			withLog: false,
+			pageBigData: {},
+			updateUrl: "http://39.170.81.174:5000" //"http://116.204.82.126/mobox3",
+
+
 		},
 		onLaunch: function() {
 			console.log('App Launch')
+			// #ifdef APP-PLUS
+			plus.runtime.getProperty(plus.runtime.appid, (info) => {
+				// console.log(info);
+				_this.version = info.version;
+				_this.checkAppVersion(_this.version)
+			});
+			//#endif
 		},
 		onShow: function() {
 			console.log('App Show')
+
 		},
 		onHide: function() {
 			console.log('App Hide')
diff --git a/api/request.js b/api/request.js
index 421f7cc..7f74ded 100644
--- a/api/request.js
+++ b/api/request.js
@@ -14,15 +14,24 @@
 	apiCount: {
 		agv_state: 0,
 		laser_data: 0,
-		current_teaching:0
+		current_teaching: 0
+	},
+
+	parseJson(str) {
+		try {
+			const obj = JSON.parse(str)
+			return obj
+		} catch (ex) {
+			return undefined
+		}
 	},
 	addLog(item) {
 		if (!getApp().globalData.withLog) {
 			return
 		}
 		if (item.statusCode == 200) {
-			const ret = JSON.parse(item.data)
-			if (ret.code == 0) {
+			const ret = this.parseJson(item.data)
+			if (ret?.code === 0) {
 				if (item.url.indexOf("get_agv_state") > 0) {
 
 					if (this.apiCount.agv_state % 20 != 0) {
@@ -32,14 +41,14 @@
 					this.apiCount.agv_state = 1;
 				}
 				if (item.url.indexOf("get_current_teaching_data") > 0) {
-				
+
 					if (this.apiCount.current_teaching % 20 != 0) {
 						this.apiCount.current_teaching++;
 						return
 					}
 					this.apiCount.current_teaching = 1;
 				}
-				
+
 				// if (item.url.indexOf("laser_data") > 0) {
 
 				// 	if (ret.data?.base_map?.image_base64) {
@@ -84,6 +93,7 @@
 		}
 		const list = session.getValue("request_log") || []
 
+
 		list.unshift(item)
 
 		if (list.length > 512) {
@@ -105,8 +115,8 @@
 		options.method = options.method || this.common.method;
 		options.dataType = options.dataType || this.common.dataType;
 
-		if (options.url.indexOf("get_agv_state") < 0 && options.url.indexOf("laser_data") < 0
-		 && options.url.indexOf("taskGroupStatus") < 0 &&options.url.indexOf("get_current_teaching_data") < 0) {
+		if (options.url.indexOf("get_agv_state") < 0 && options.url.indexOf("laser_data") < 0 &&
+			options.url.indexOf("taskGroupStatus") < 0 && options.url.indexOf("get_current_teaching_data") < 0) {
 			console.log("url", options.url, options.data)
 		}
 		return new Promise((resolve, reject) => {
@@ -118,8 +128,10 @@
 				method: options.method,
 				dataType: options.dataType,
 				success: (result) => {
-					if (options.url.indexOf("get_agv_state") < 0 && options.url.indexOf("laser_data") < 0 
-							&& options.url.indexOf("taskGroupStatus") < 0 &&options.url.indexOf("get_current_teaching_data") < 0) { //
+					if (options.url.indexOf("get_agv_state") < 0 && options.url.indexOf(
+							"laser_data") < 0 &&
+						options.url.indexOf("taskGroupStatus") < 0 && options.url.indexOf(
+							"get_current_teaching_data") < 0) { //
 						console.log("result", result)
 					}
 
diff --git a/api/vehicle.js b/api/vehicle.js
index 47f4d43..b40c756 100644
--- a/api/vehicle.js
+++ b/api/vehicle.js
@@ -4,10 +4,11 @@
 } from "@/comm/utils.js"
 
 const addLog = (item) => {
-	const list = session.getValue("request_log") || []
-	list.unshift(item)
-	session.setValue("request_log", list)
+	http.addLog(item)
 }
+
+
+
 // 鍒ゆ柇ip鏄惁鍙互杩炴帴涓� export const checkIpLinkSuccess = (ip) => {
 	var url = `http://${ip}:4405/api/mt/battery`;
@@ -75,6 +76,310 @@
 
 }
 /**
+ * GET 4405/api/shell/version
+ * 鑾峰彇杞欢鐗堟湰淇℃伅锛�+ * @param 
+ * @returns 
+ */
+export const getShellVersion = (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,
+	})
+}
+/**
+ * POST 4405/api/shell/version
+ * 鏇存柊杞欢鐗堟湰
+ * @param 
+ * @returns 
+ */
+
+export const uploadShellVersion = async (ip, path) => {
+
+	var header = {
+		'Content-Type': 'application/octet-stream'
+	};
+
+	var url = `http://${ip}:4405/api/shell/version`;
+	const buf = await FilePath.readArrayBuffer({
+		path: filePath
+	});
+	const flag = 3
+	const flagBuf = new ArrayBuffer(4);
+	const view = new DataView(flagBuf);
+	view.setUint32(0, flag, false); // 澶х
+	// 2.3 鎷兼帴 POST 浣擄細flag(4B) + 鍘嬬缉鍖�+	const body = new ArrayBuffer(4 + buf.byteLength);
+	const whole = new Uint8Array(body);
+	whole.set(new Uint8Array(flagBuf), 0);
+	whole.set(new Uint8Array(buf), 4);
+	return http.request({
+		method: "POST",
+		url,
+		header,
+		dataType: 'arraybuffer', // 鍏抽敭锛氳 data 鎸変簩杩涘埗璧�+		data: body,
+	})
+}
+export const shellUpgrade = async (ip, filePath, cbSuccess, cbProgress, cbFail) => {
+
+	var url = `http://${ip}:4405/api/shell/upgrade`;
+	const uploadTask = uni.uploadFile({
+		url,
+		name: "upload",
+		filePath: filePath,
+		formData: {
+			'upgrade_flag': 3
+		},
+		success: (result) => {
+			console.log(result)
+			const now = new Date()
+			const date = `${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}`
+			if (result.statusCode != 200) {
+				http.addLog({
+					date,
+					method: "POST",
+					url,
+					param: {
+						upload: filePath,
+						upgrade_flag: 3
+					},
+					statusCode: result.statusCode,
+					data: ""
+				})
+				if (result.statusCode == 404) {
+					cbFail("鍦板潃涓嶅锛岃妫�煡璇ュ湴鍧�細" + url);
+				} else
+					cbFail("璁块棶澶辫触锛岀姸鎬佺爜锛� + result.statusCode);
+			} else {
+				var ret = result.data
+				if (typeof ret == 'string') {
+					try {
+						try {
+							try {
+								ret = JSON.parse(ret.replace(/\\"/g, '"'));
+							} catch (ex) {
+								ret = JSON.parse(ret.replace(/\\/g, '\\\\'))
+							}
+						} catch (ex) {
+							ret = JSON.parse(ret.replace(/\\"/g, "'").replace(
+								/[\r\n]/g,
+								'<br>').replace(/[\t]/g, '    '));
+						}
+					} catch (ex) {
+						cbFail('灏嗐�json string銆戣浆鎹负銆恓son object銆戝け璐�);
+					}
+				}
+				http.addLog({
+					date,
+					method: "POST",
+					url,
+					param: {
+						upload: filePath,
+						upgrade_flag: 3
+					},
+					statusCode: result.statusCode,
+					data: JSON.stringify(ret)
+				})
+				if (ret.code == 0) {
+
+					cbSuccess(ret.data);
+				} else {
+					cbFail(ret.msg || ret.message || "");
+				}
+
+			}
+		},
+		fail: (result) => {
+			console.log(result)
+			cbFail(result);
+		},
+	})
+	uploadTask.onProgressUpdate((res) => {
+		cbProgress(res, uploadTask)
+	});
+}
+
+/**
+ * GET 4405/api/error/list
+ * 鑾峰彇瀹炴椂閿欒
+ * @param 
+ * @returns 
+ */
+
+export const getErrorList = (ip) => {
+	var header = {
+		"Content-Type": "application/json;charset=UTF-8"
+	};
+	var url = `http://${ip}:4405/api/error/list?_timestamp=${new Date().getTime()}`;
+	return http.request({
+		method: "GET",
+		url,
+		header,
+	})
+}
+
+
+/**
+ * POST 4405/api/mt/clear_fault
+ * 娓呴櫎瀹炴椂閿欒
+ * @param 
+ * @returns 
+ */
+
+export const clearFault = (ip) => {
+	var header = {
+		"Content-Type": "application/json;charset=UTF-8"
+	};
+	var url = `http://${ip}:4405/api/mt/clear_fault`;
+	return http.request({
+		method: "POST",
+		url,
+		header,
+	})
+}
+
+/**
+ * GET 4405/api/error/history
+ * 鑾峰彇鍘嗗彶閿欒
+ * @param 
+ * @returns 
+ */
+
+export const getErrorHistory = (ip) => {
+	var header = {
+		"Content-Type": "application/json;charset=UTF-8"
+	};
+	var url = `http://${ip}:4405/api/error/history?_timestamp=${new Date().getTime()}`;
+	return http.request({
+		method: "GET",
+		url,
+		header,
+	})
+}
+/**
+ * GET 4405/api/error/download_error
+ * 涓嬭浇鍘嗗彶閿欒鏃ュ織锛�+ * @param 
+ * @returns 
+ */
+
+
+export const downloadError = (ip, path, cbSuccess, cbProgress, cbFail) => {
+	var url = `http://${ip}:4405/api/error/download_error?path=${encodeURI(path)}`;
+	console.log(url)
+	// var header = {
+	// 	"Content-Type": "application/json;charset=UTF-8"
+	// };
+	// return http.request({
+	// 	method: "GET",
+	// 	url,
+	// 	header,
+	// })
+	// return
+	const downloadTask = uni.downloadFile({
+		url: url,
+		header: {
+			'content-type': 'application/octet-stream',
+		},
+		success: (result) => {
+			console.log("downloadFile", result)
+			const now = new Date()
+			const date = `${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}`
+			if (result.statusCode === 200) {
+				http.addLog({
+					date,
+					method: `GET`,
+					url: url,
+					param: {},
+					statusCode: result.statusCode,
+					data: result
+				})
+				cbSuccess(result.tempFilePath)
+			} else {
+
+				http.addLog({
+					date,
+					method: `GET`,
+					url: url,
+					param: {},
+					statusCode: result.statusCode,
+					data: result.data
+				})
+				cbFail(result.data || '涓嬭浇鏈煡閿欒!');
+			}
+		},
+		fail: (result) => {
+			console.log("downloadTaskFile fail", result)
+			const now = new Date()
+			const date = `${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}`
+			http.addLog({
+				date,
+				method: `GET`,
+				url: url,
+				param: {},
+				statusCode: -1,
+				data: result
+			})
+			cbFail('涓嬭浇鏂囦欢澶辫触!');
+		}
+	})
+	downloadTask.onProgressUpdate((res) => {
+
+		cbProgress(res, downloadTask)
+	});
+	return downloadTask
+}
+
+/**
+ * POST 4405/api/mt/error/tar_log
+ * 鎵撳寘涓嬭浇鏃ュ織
+ * @param 
+ * @returns 
+ */
+
+export const tarErrorLog = (ip, start_time, end_time) => {
+	var header = {
+		"Content-Type": "application/json;charset=UTF-8"
+	};
+	var url = `http://${ip}:4405/api/error/tar_log`;
+	return http.request({
+		method: "POST",
+		url,
+		header,
+		data: {
+			start_time,
+			end_time
+		}
+	})
+}
+
+/**
+ * GET 4405/api/error/query_task_status
+ * 鏌ヨ鎵撳寘鍚庣殑鏃ュ織鐩綍(鏌ュ埌鍚庝娇鐢�405/api/error/download_error鎺ュ彛涓嬭浇)
+ * @param 
+ * @returns 
+ */
+
+export const queryErrorTaskStatus = (ip, task_list) => {
+	var header = {
+		"Content-Type": "application/json;charset=UTF-8"
+	};
+	var url =
+		`http://${ip}:4405/api/error/query_task_status?_timestamp=${new Date().getTime()}&task_list=${encodeURI(JSON.stringify(task_list))}`;
+	return http.request({
+		method: "GET",
+		url,
+		header,
+	})
+}
+/**
  * GET 5000/api/get_agv_state
  * 鏌ヨ杞﹁締淇℃伅
  * @param 
@@ -89,7 +394,7 @@
 		method: "GET",
 		url,
 		header,
-		
+
 	})
 
 
@@ -111,7 +416,7 @@
 		url,
 		header,
 		timeout: 5000,
-		
+
 	})
 
 }
@@ -209,7 +514,7 @@
 		data: {
 			stationID,
 			name,
-			x:Number(x),
+			x: Number(x),
 			y: Number(y),
 			angle
 		}
@@ -702,7 +1007,7 @@
  * @returns 
  */
 export const updateSplitTeachingData = (ip, param) => {
-	
+
 	const {
 		edge_name,
 		name,
@@ -734,8 +1039,8 @@
  * name # 蹇呬紶
  * @returns 
  */
-export const deleteSplitTeachingData = (ip, edge_name,name) => {
-	
+export const deleteSplitTeachingData = (ip, edge_name, name) => {
+
 	var header = {
 		"Content-Type": "application/json;charset=UTF-8"
 	};
@@ -810,7 +1115,7 @@
  * @returns 
  */
 
-export const addMap = (ip, scene_id, map_type="create") => {
+export const addMap = (ip, scene_id, map_type = "create") => {
 	var header = {
 		"Content-Type": "application/json;charset=UTF-8"
 	};
@@ -858,7 +1163,7 @@
  * @returns 
  */
 
-export const getMapLaserData = (ip,mapId) => {
+export const getMapLaserData = (ip, mapId) => {
 	var header = {
 		"Content-Type": "application/json;charset=UTF-8"
 	};
@@ -1000,10 +1305,50 @@
 		method: "POST",
 		url,
 		header,
-		data: {data}
+		data: {
+			data
+		}
 
 	})
 }
+
+/**
+ * GET 5000/api/save_db_data_status
+ * 鑾峰彇DB鏁版嵁淇濆瓨鐘舵�锛�+ * @param 
+ * @returns  1淇濆瓨涓�2淇濆瓨鎴愬姛 3淇濆瓨澶辫触
+ */
+
+export const getSaveDBDataStatus = (ip) => {
+	var header = {
+		"Content-Type": "application/json;charset=UTF-8"
+	};
+	var url = `http://${ip}:5000/api/save_db_data_status/`;
+	return http.request({
+		method: "GET",
+		url,
+		header,
+	})
+}
+/**
+ * GET 5000/api/cancel_save_db_data
+ * 鍙栨秷淇濆瓨DB鏁版嵁锛�+ * @param 
+ * @returns 
+ */
+
+export const cancelSaveDBData = (ip) => {
+	var header = {
+		"Content-Type": "application/json;charset=UTF-8"
+	};
+	var url = `http://${ip}:5000/api/cancel_save_db_data/`;
+	return http.request({
+		method: "GET",
+		url,
+		header,
+	})
+}
+
 
 
 /**
@@ -1041,47 +1386,92 @@
 		method: "POST",
 		url,
 		header,
-		data: {status}
+		data: {
+			status
+		}
 
 	})
 }
- 
- 
- /**
-  * GET 5000/api/check_mapserver_is_ok
-  * 鏋勫浘绋嬪簭鏄惁鍑嗗鍒颁綅
-  * @param 
-  * @returns 
-  */
- 
- export const checkMapServerIsOk = (ip) => {
- 	var header = {
- 		"Content-Type": "application/json;charset=UTF-8"
- 	};
- 	var url = `http://${ip}:5000/api/check_mapserver_is_ok/`;
- 	return http.request({
- 		method: "GET",
- 		url,
- 		header,
- 	})
- }
- 
- /**
-  * GET 5000/api/check_save_map_is_ok
-  * 缁撴潫鏋勫浘鏄惁淇濆瓨瀹屾垚
-  * @param 
-  * @returns 
-  */
- 
- export const checkSaveMapIsOk = (ip) => {
- 	var header = {
- 		"Content-Type": "application/json;charset=UTF-8"
- 	};
- 	var url = `http://${ip}:5000/api/check_save_map_is_ok/`;
- 	return http.request({
- 		method: "GET",
- 		url,
- 		header,
- 	})
- }
- 
+
+
+/**
+ * GET 5000/api/check_mapserver_is_ok
+ * 鏋勫浘绋嬪簭鏄惁鍑嗗鍒颁綅
+ * @param 
+ * @returns 
+ */
+
+export const checkMapServerIsOk = (ip) => {
+	var header = {
+		"Content-Type": "application/json;charset=UTF-8"
+	};
+	var url = `http://${ip}:5000/api/check_mapserver_is_ok/`;
+	return http.request({
+		method: "GET",
+		url,
+		header,
+	})
+}
+
+/**
+ * GET 5000/api/check_save_map_is_ok
+ * 缁撴潫鏋勫浘鏄惁淇濆瓨瀹屾垚
+ * @param 
+ * @returns 
+ */
+
+export const checkSaveMapIsOk = (ip) => {
+	var header = {
+		"Content-Type": "application/json;charset=UTF-8"
+	};
+	var url = `http://${ip}:5000/api/check_save_map_is_ok/`;
+	return http.request({
+		method: "GET",
+		url,
+		header,
+	})
+}
+
+/**
+ * GET 5000/api/get_pallet_size/
+ * 鑾峰彇鎵樼洏灏哄锛�+ * @param 
+ * @returns 
+ */
+
+export const getPalletSize = (ip) => {
+	var header = {
+		"Content-Type": "application/json;charset=UTF-8"
+	};
+	var url = `http://${ip}:5000/api/get_pallet_size/`;
+	return http.request({
+		method: "GET",
+		url,
+		header,
+	})
+}
+
+
+/**
+ * POST 5000/api/save_pallet_size/
+ * 璁剧疆鎵樼洏灏哄锛�
+ * @param  length 2,#闀垮害/鍗曚綅m
+ * @param  width 2,#闀垮害/鍗曚綅m
+ * @returns 
+ */
+export const savePalletSize = (ip, length, width) => {
+	var header = {
+		"Content-Type": "application/json;charset=UTF-8"
+	};
+	var url = `http://${ip}:5000/api/save_pallet_size/`;
+	return http.request({
+		method: "POST",
+		url,
+		header,
+		data: {
+			length,
+			width
+		}
+
+	})
+}
\ No newline at end of file
diff --git a/api/version.js b/api/version.js
new file mode 100644
index 0000000..153b149
--- /dev/null
+++ b/api/version.js
@@ -0,0 +1,219 @@
+import http from './request.js';
+import {
+	session,
+} from "@/comm/utils.js"
+
+
+export const getVersion = (type) => {
+
+	return new Promise((resolve, reject) => {
+		const updateUrl = getApp().globalData.updateUrl || ""
+		const rootUrl = updateUrl.trim()
+		if (!rootUrl) {
+			resolve({})
+			return
+		}
+		var url = rootUrl + '/esgo/version.json';
+
+		uni.request({
+			url: url, // 鎸囧畾JS鏂囦欢鐨勫畬鏁磋矾寰�+			method: 'GET',
+			success(res) {
+				if (res && res.statusCode === 200) {
+					var ret = res.data
+					if (typeof ret == 'string') {
+						try {
+							try {
+								ret = JSON.parse(ret.replace(/\\"/g, '"'));
+							} catch (ex) {
+								ret = JSON.parse(ret.replace(/\\/g, '\\\\'))
+							}
+						} catch (ex) {
+							console.log(ex)
+						}
+					}
+					//鑾峰彇褰撳墠鐗堟湰鍙�+					let verInfo = ret[type]; //鏇存柊鍖呭悕绉�+					if (!verInfo) {
+						resolve({})
+					}
+					// let versionInfo = verName.split('_');
+					// let verNum = versionInfo[2].replace(".apk", ""); //鐗堟湰缂栫爜
+					// let verCode = versionInfo[2].replace(".apk", "").replace(".", "").replace(".", ""); //鏈嶅姟绔増鏈彿
+					if (verInfo.file_name) {
+						verInfo.dld_url = rootUrl + "/esgo/" + verInfo.file_name
+					}
+
+					resolve(verInfo);
+				} else {
+					// return reject("璁块棶澶辫触锛岀姸鎬佺爜锛� + res.statusCode);
+					resolve({})
+				}
+
+			},
+			fail: (err) => {
+				return reject(err);
+			},
+		})
+	})
+}
+export const getAppVersion = async () => {
+	//return getVersion("app")
+	const rootUrl = getApp().globalData.updateUrl || ""
+	var header = {
+		"Content-Type": "application/json;charset=UTF-8"
+	};
+	var url = `${rootUrl}/api/list_files/`;
+	try {
+		return await http.request({
+			method: "POST",
+			url,
+			header,
+			data: {
+				is_app: 1
+			}
+		})
+	} catch (ex) {
+		console.log("getAppVersion", ex)
+	}
+
+}
+export const getServerVersion = async() => {
+	//return getVersion("server")
+	const rootUrl = getApp().globalData.updateUrl || ""
+	var header = {
+		"Content-Type": "application/json;charset=UTF-8"
+	};
+	var url = `${rootUrl}/api/list_files/`;
+	try {
+		return await http.request({
+			method: "POST",
+			url,
+			header,
+			data: {
+				is_app: 0
+			}
+		})
+	} catch (ex) {
+		console.log("getServerVersion", ex)
+	}
+
+}
+
+/**
+ * GET api/download/
+ * 涓嬭浇鏇存柊鍖咃紙鏈嶅姟鍣ㄦ帴鍙o級
+ * @param 
+ * @returns 
+ */
+
+export const downloadUpdatePackage = (fileName, cbSuccess, cbProgress, cbFail) => {
+	const rootUrl = getApp().globalData.updateUrl || ""
+	var url = `${rootUrl}/api/download/?file_name=${encodeURI(fileName)}`;
+	console.log(url)
+	const downloadTask = uni.downloadFile({
+		url: url,
+		header: {
+			'content-type': 'application/octet-stream',
+		},
+		success: (result) => {
+			console.log("downloadFile", result)
+			const now = new Date()
+			const date = `${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}`
+			if (result.statusCode === 200) {
+				http.addLog({
+					date,
+					method: `GET`,
+					url: url,
+					param: {},
+					statusCode: result.statusCode,
+					data: result
+				})
+				cbSuccess(result.tempFilePath)
+			} else {
+
+				http.addLog({
+					date,
+					method: `GET`,
+					url: url,
+					param: {},
+					statusCode: result.statusCode,
+					data: result.data
+				})
+				cbFail(result.data || '涓嬭浇鏈煡閿欒!');
+			}
+		},
+		fail: (result) => {
+			console.log("downloadTaskFile fail", result)
+			const now = new Date()
+			const date = `${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}`
+			http.addLog({
+				date,
+				method: `GET`,
+				url: url,
+				param: {},
+				statusCode: -1,
+				data: result
+			})
+			cbFail('涓嬭浇鏂囦欢澶辫触!');
+		}
+	})
+	downloadTask.onProgressUpdate((res) => {
+
+		cbProgress(res, downloadTask)
+	});
+	return downloadTask
+}
+/**
+ * GET api/upload/
+ * 涓婁紶鏃ュ織鍖咃紙 鏈嶅姟鍣ㄦ帴鍙o級锛�+ * @param 
+ * @returns 
+ */
+
+export const uploadLogPackage = (filePath, cbSuccess, cbProgress, cbFail) => {
+	const rootUrl = getApp().globalData.updateUrl || ""
+	var url = `${rootUrl}/api/upload/`;
+	console.log("uploadLogPackage", url, filePath)
+	const uploadTask = uni.uploadFile({
+		url,
+		name: "file",
+		filePath: filePath,
+		success: (result) => {
+			const now = new Date()
+			const date = `${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}`
+			console.log("uploadFile", result)
+			http.addLog({
+				date,
+				method: `POST`,
+				url: url,
+				param: {
+					file: filePath
+				},
+				statusCode: 200,
+				data: result
+			})
+			cbSuccess()
+		},
+		fail: (result) => {
+			console.log(result)
+			const now = new Date()
+			const date = `${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}`
+			http.addLog({
+				date,
+				method: `GET`,
+				url: url,
+				param: {
+					file: filePath
+				},
+				statusCode: -1,
+				data: result
+			})
+			cbFail('涓婁紶鏂囦欢澶辫触!');
+		},
+	})
+	uploadTask.onProgressUpdate((res) => {
+		console.log(res)
+		cbProgress(res, uploadTask)
+	});
+}
\ No newline at end of file
diff --git a/comm/calendarLunar.js b/comm/calendarLunar.js
new file mode 100644
index 0000000..fdbe1b5
--- /dev/null
+++ b/comm/calendarLunar.js
@@ -0,0 +1,556 @@
+/**
+* @1900-2100鍖洪棿鍐呯殑鍏巻銆佸啘鍘嗕簰杞�+* @charset UTF-8
+* @github  https://github.com/jjonline/calendar.js
+* @Author  Jea鏉�JJonline@JJonline.Cn)
+* @Time    2014-7-21
+* @Time    2016-8-13 Fixed 2033hex銆丄ttribution Annals
+* @Time    2016-9-25 Fixed lunar LeapMonth Param Bug
+* @Time    2017-7-24 Fixed use getTerm Func Param Error.use solar year,NOT lunar year
+* @Version 1.0.3
+* @鍏巻杞啘鍘嗭細calendar.solar2lunar(1987,11,01); //[you can ignore params of prefix 0]
+* @鍐滃巻杞叕鍘嗭細calendar.lunar2solar(1987,09,10); //[you can ignore params of prefix 0]
+*/
+/* eslint-disable */
+var calendar = {
+
+  /**
+      * 鍐滃巻1900-2100鐨勬鼎澶у皬淇℃伅琛�+      * @Array Of Property
+      * @return Hex
+      */
+  lunarInfo: [0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2, // 1900-1909
+    0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977, // 1910-1919
+    0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, // 1920-1929
+    0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950, // 1930-1939
+    0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, // 1940-1949
+    0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0, // 1950-1959
+    0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, // 1960-1969
+    0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6, // 1970-1979
+    0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570, // 1980-1989
+    0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x05ac0, 0x0ab60, 0x096d5, 0x092e0, // 1990-1999
+    0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5, // 2000-2009
+    0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, // 2010-2019
+    0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530, // 2020-2029
+    0x05aa0, 0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, // 2030-2039
+    0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0, // 2040-2049
+    /** Add By JJonline@JJonline.Cn**/
+    0x14b63, 0x09370, 0x049f8, 0x04970, 0x064b0, 0x168a6, 0x0ea50, 0x06b20, 0x1a6c4, 0x0aae0, // 2050-2059
+    0x0a2e0, 0x0d2e3, 0x0c960, 0x0d557, 0x0d4a0, 0x0da50, 0x05d55, 0x056a0, 0x0a6d0, 0x055d4, // 2060-2069
+    0x052d0, 0x0a9b8, 0x0a950, 0x0b4a0, 0x0b6a6, 0x0ad50, 0x055a0, 0x0aba4, 0x0a5b0, 0x052b0, // 2070-2079
+    0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160, // 2080-2089
+    0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252, // 2090-2099
+    0x0d520], // 2100
+
+  /**
+      * 鍏巻姣忎釜鏈堜唤鐨勫ぉ鏁版櫘閫氳〃
+      * @Array Of Property
+      * @return Number
+      */
+  solarMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
+
+  /**
+      * 澶╁共鍦版敮涔嬪ぉ骞查�鏌ヨ〃
+      * @Array Of Property trans["鐢�,"涔�,"涓�,"涓�,"鎴�,"宸�,"搴�,"杈�,"澹�,"鐧�]
+      * @return Cn string
+      */
+  Gan: ['\u7532', '\u4e59', '\u4e19', '\u4e01', '\u620a', '\u5df1', '\u5e9a', '\u8f9b', '\u58ec', '\u7678'],
+
+  /**
+      * 澶╁共鍦版敮涔嬪湴鏀�鏌ヨ〃
+      * @Array Of Property
+      * @trans["瀛�,"涓�,"瀵�,"鍗�,"杈�,"宸�,"鍗�,"鏈�,"鐢�,"閰�,"鎴�,"浜�]
+      * @return Cn string
+      */
+  Zhi: ['\u5b50', '\u4e11', '\u5bc5', '\u536f', '\u8fb0', '\u5df3', '\u5348', '\u672a', '\u7533', '\u9149', '\u620c', '\u4ea5'],
+
+  /**
+      * 澶╁共鍦版敮涔嬪湴鏀�鏌ヨ〃<=>鐢熻倴
+      * @Array Of Property
+      * @trans["榧�,"鐗�,"铏�,"鍏�,"榫�,"铔�,"椹�,"缇�,"鐚�,"楦�,"鐙�,"鐚�]
+      * @return Cn string
+      */
+  Animals: ['\u9f20', '\u725b', '\u864e', '\u5154', '\u9f99', '\u86c7', '\u9a6c', '\u7f8a', '\u7334', '\u9e21', '\u72d7', '\u732a'],
+
+  /**
+      * 24鑺傛皵閫熸煡琛�+      * @Array Of Property
+      * @trans["灏忓瘨","澶у瘨","绔嬫槬","闆ㄦ按","鎯婅洶","鏄ュ垎","娓呮槑","璋烽洦","绔嬪","灏忔弧","鑺掔","澶忚嚦","灏忔殤","澶ф殤","绔嬬","澶勬殤","鐧介湶","绉嬪垎","瀵掗湶","闇滈檷","绔嬪啲","灏忛洩","澶ч洩","鍐嚦"]
+      * @return Cn string
+      */
+  solarTerm: ['\u5c0f\u5bd2', '\u5927\u5bd2', '\u7acb\u6625', '\u96e8\u6c34', '\u60ca\u86f0', '\u6625\u5206', '\u6e05\u660e', '\u8c37\u96e8', '\u7acb\u590f', '\u5c0f\u6ee1', '\u8292\u79cd', '\u590f\u81f3', '\u5c0f\u6691', '\u5927\u6691', '\u7acb\u79cb', '\u5904\u6691', '\u767d\u9732', '\u79cb\u5206', '\u5bd2\u9732', '\u971c\u964d', '\u7acb\u51ac', '\u5c0f\u96ea', '\u5927\u96ea', '\u51ac\u81f3'],
+
+  /**
+      * 1900-2100鍚勫勾鐨�4鑺傛皵鏃ユ湡閫熸煡琛�+      * @Array Of Property
+      * @return 0x string For splice
+      */
+  sTermInfo: ['9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f',
+    '97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+    '97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f',
+    'b027097bd097c36b0b6fc9274c91aa', '9778397bd19801ec9210c965cc920e', '97b6b97bd19801ec95f8c965cc920f',
+    '97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2', '9778397bd197c36c9210c9274c91aa',
+    '97b6b97bd19801ec95f8c965cc920e', '97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2',
+    '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec95f8c965cc920e', '97bcf97c3598082c95f8e1cfcc920f',
+    '97bd097bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+    '97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f',
+    '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+    '97bcf97c359801ec95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd097bd07f595b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9210c8dc2', '9778397bd19801ec9210c9274c920e', '97b6b97bd19801ec95f8c965cc920f',
+    '97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e',
+    '97b6b97bd19801ec95f8c965cc920f', '97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2',
+    '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bd07f1487f595b0b0bc920fb0722',
+    '7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+    '97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f531b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+    '97bcf7f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
+    '9778397bd097c36b0b6fc9210c91aa', '97b6b97bd197c36c9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e',
+    '97b6b7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2',
+    '9778397bd097c36b0b70c9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b7f0e47f531b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
+    '9778397bd097c36b0b6fc9210c91aa', '97b6b7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '977837f0e37f149b0723b0787b0721',
+    '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c35b0b6fc9210c8dc2',
+    '977837f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc9210c8dc2', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '977837f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
+    '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
+    '977837f0e37f14998082b0723b06bd', '7f07e7f0e37f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b0721',
+    '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f595b0b0bb0b6fb0722', '7f0e37f0e37f14898082b0723b02d5',
+    '7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f531b0b0bb0b6fb0722',
+    '7f0e37f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e37f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35',
+    '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f149b0723b0787b0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0723b06bd',
+    '7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', '7f0e37f0e366aa89801eb072297c35',
+    '7ec967f0e37f14998082b0723b06bd', '7f07e7f0e37f14998083b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
+    '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14898082b0723b02d5', '7f07e7f0e37f14998082b0787b0721',
+    '7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66aa89801e9808297c35', '665f67f0e37f14898082b0723b02d5',
+    '7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66a449801e9808297c35',
+    '665f67f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e36665b66a449801e9808297c35', '665f67f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721', '7f0e26665b66a449801e9808297c35', '665f67f0e37f1489801eb072297c35',
+    '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722'],
+
+  /**
+      * 鏁板瓧杞腑鏂囬�鏌ヨ〃
+      * @Array Of Property
+      * @trans ['鏃�,'涓�,'浜�,'涓�,'鍥�,'浜�,'鍏�,'涓�,'鍏�,'涔�,'鍗�]
+      * @return Cn string
+      */
+  nStr1: ['\u65e5', '\u4e00', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341'],
+
+  /**
+      * 鏃ユ湡杞啘鍘嗙О鍛奸�鏌ヨ〃
+      * @Array Of Property
+      * @trans ['鍒�,'鍗�,'寤�,'鍗�]
+      * @return Cn string
+      */
+  nStr2: ['\u521d', '\u5341', '\u5eff', '\u5345'],
+
+  /**
+      * 鏈堜唤杞啘鍘嗙О鍛奸�鏌ヨ〃
+      * @Array Of Property
+      * @trans ['姝�,'涓�,'浜�,'涓�,'鍥�,'浜�,'鍏�,'涓�,'鍏�,'涔�,'鍗�,'鍐�,'鑵�]
+      * @return Cn string
+      */
+  nStr3: ['\u6b63', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341', '\u51ac', '\u814a'],
+
+  /**
+      * 杩斿洖鍐滃巻y骞翠竴鏁村勾鐨勬�澶╂暟
+      * @param lunar Year
+      * @return Number
+      * @eg:var count = calendar.lYearDays(1987) ;//count=387
+      */
+  lYearDays: function (y) {
+    var i; var sum = 348
+    for (i = 0x8000; i > 0x8; i >>= 1) { sum += (this.lunarInfo[y - 1900] & i) ? 1 : 0 }
+    return (sum + this.leapDays(y))
+  },
+
+  /**
+      * 杩斿洖鍐滃巻y骞撮棸鏈堟槸鍝釜鏈堬紱鑻骞存病鏈夐棸鏈�鍒欒繑鍥�
+      * @param lunar Year
+      * @return Number (0-12)
+      * @eg:var leapMonth = calendar.leapMonth(1987) ;//leapMonth=6
+      */
+  leapMonth: function (y) { // 闂板瓧缂栫爜 \u95f0
+    return (this.lunarInfo[y - 1900] & 0xf)
+  },
+
+  /**
+      * 杩斿洖鍐滃巻y骞撮棸鏈堢殑澶╂暟 鑻ヨ骞存病鏈夐棸鏈堝垯杩斿洖0
+      * @param lunar Year
+      * @return Number (0銆�9銆�0)
+      * @eg:var leapMonthDay = calendar.leapDays(1987) ;//leapMonthDay=29
+      */
+  leapDays: function (y) {
+    if (this.leapMonth(y)) {
+      return ((this.lunarInfo[y - 1900] & 0x10000) ? 30 : 29)
+    }
+    return (0)
+  },
+
+  /**
+      * 杩斿洖鍐滃巻y骞磎鏈堬紙闈為棸鏈堬級鐨勬�澶╂暟锛岃绠梞涓洪棸鏈堟椂鐨勫ぉ鏁拌浣跨敤leapDays鏂规硶
+      * @param lunar Year
+      * @return Number (-1銆�9銆�0)
+      * @eg:var MonthDay = calendar.monthDays(1987,9) ;//MonthDay=29
+      */
+  monthDays: function (y, m) {
+    if (m > 12 || m < 1) { return -1 }// 鏈堜唤鍙傛暟浠�鑷�2锛屽弬鏁伴敊璇繑鍥�1
+    return ((this.lunarInfo[y - 1900] & (0x10000 >> m)) ? 30 : 29)
+  },
+
+  /**
+      * 杩斿洖鍏巻(!)y骞磎鏈堢殑澶╂暟
+      * @param solar Year
+      * @return Number (-1銆�8銆�9銆�0銆�1)
+      * @eg:var solarMonthDay = calendar.leapDays(1987) ;//solarMonthDay=30
+      */
+  solarDays: function (y, m) {
+    if (m > 12 || m < 1) { return -1 } // 鑻ュ弬鏁伴敊璇�杩斿洖-1
+    var ms = m - 1
+    if (ms == 1) { // 2鏈堜唤鐨勯棸骞宠寰嬫祴绠楀悗纭杩斿洖28鎴�9
+      return (((y % 4 == 0) && (y % 100 != 0) || (y % 400 == 0)) ? 29 : 28)
+    } else {
+      return (this.solarMonth[ms])
+    }
+  },
+
+  /**
+     * 鍐滃巻骞翠唤杞崲涓哄共鏀邯骞�+     * @param  lYear 鍐滃巻骞寸殑骞翠唤鏁�+     * @return Cn string
+     */
+  toGanZhiYear: function (lYear) {
+    var ganKey = (lYear - 3) % 10
+    var zhiKey = (lYear - 3) % 12
+    if (ganKey == 0) ganKey = 10// 濡傛灉浣欐暟涓�鍒欎负鏈�悗涓�釜澶╁共
+    if (zhiKey == 0) zhiKey = 12// 濡傛灉浣欐暟涓�鍒欎负鏈�悗涓�釜鍦版敮
+    return this.Gan[ganKey - 1] + this.Zhi[zhiKey - 1]
+  },
+
+  /**
+     * 鍏巻鏈堛�鏃ュ垽鏂墍灞炴槦搴�+     * @param  cMonth [description]
+     * @param  cDay [description]
+     * @return Cn string
+     */
+  toAstro: function (cMonth, cDay) {
+    var s = '\u9b54\u7faf\u6c34\u74f6\u53cc\u9c7c\u767d\u7f8a\u91d1\u725b\u53cc\u5b50\u5de8\u87f9\u72ee\u5b50\u5904\u5973\u5929\u79e4\u5929\u874e\u5c04\u624b\u9b54\u7faf'
+    var arr = [20, 19, 21, 21, 21, 22, 23, 23, 23, 23, 22, 22]
+    return s.substr(cMonth * 2 - (cDay < arr[cMonth - 1] ? 2 : 0), 2) + '\u5ea7'// 搴�+  },
+
+  /**
+      * 浼犲叆offset鍋忕Щ閲忚繑鍥炲共鏀�+      * @param offset 鐩稿鐢插瓙鐨勫亸绉婚噺
+      * @return Cn string
+      */
+  toGanZhi: function (offset) {
+    return this.Gan[offset % 10] + this.Zhi[offset % 12]
+  },
+
+  /**
+      * 浼犲叆鍏巻(!)y骞磋幏寰楄骞寸n涓妭姘旂殑鍏巻鏃ユ湡
+      * @param y鍏巻骞�1900-2100)锛沶浜屽崄鍥涜妭姘斾腑鐨勭鍑犱釜鑺傛皵(1~24)锛涗粠n=1(灏忓瘨)绠楄捣
+      * @return day Number
+      * @eg:var _24 = calendar.getTerm(1987,3) ;//_24=4;鎰忓嵆1987骞�鏈�鏃ョ珛鏄�+      */
+  getTerm: function (y, n) {
+    if (y < 1900 || y > 2100) { return -1 }
+    if (n < 1 || n > 24) { return -1 }
+    var _table = this.sTermInfo[y - 1900]
+    var _info = [
+      parseInt('0x' + _table.substr(0, 5)).toString(),
+      parseInt('0x' + _table.substr(5, 5)).toString(),
+      parseInt('0x' + _table.substr(10, 5)).toString(),
+      parseInt('0x' + _table.substr(15, 5)).toString(),
+      parseInt('0x' + _table.substr(20, 5)).toString(),
+      parseInt('0x' + _table.substr(25, 5)).toString()
+    ]
+    var _calday = [
+      _info[0].substr(0, 1),
+      _info[0].substr(1, 2),
+      _info[0].substr(3, 1),
+      _info[0].substr(4, 2),
+
+      _info[1].substr(0, 1),
+      _info[1].substr(1, 2),
+      _info[1].substr(3, 1),
+      _info[1].substr(4, 2),
+
+      _info[2].substr(0, 1),
+      _info[2].substr(1, 2),
+      _info[2].substr(3, 1),
+      _info[2].substr(4, 2),
+
+      _info[3].substr(0, 1),
+      _info[3].substr(1, 2),
+      _info[3].substr(3, 1),
+      _info[3].substr(4, 2),
+
+      _info[4].substr(0, 1),
+      _info[4].substr(1, 2),
+      _info[4].substr(3, 1),
+      _info[4].substr(4, 2),
+
+      _info[5].substr(0, 1),
+      _info[5].substr(1, 2),
+      _info[5].substr(3, 1),
+      _info[5].substr(4, 2)
+    ]
+    return parseInt(_calday[n - 1])
+  },
+
+  /**
+      * 浼犲叆鍐滃巻鏁板瓧鏈堜唤杩斿洖姹夎閫氫織琛ㄧず娉�+      * @param lunar month
+      * @return Cn string
+      * @eg:var cnMonth = calendar.toChinaMonth(12) ;//cnMonth='鑵婃湀'
+      */
+  toChinaMonth: function (m) { // 鏈�=> \u6708
+    if (m > 12 || m < 1) { return -1 } // 鑻ュ弬鏁伴敊璇�杩斿洖-1
+    var s = this.nStr3[m - 1]
+    s += '\u6708'// 鍔犱笂鏈堝瓧
+    return s
+  },
+
+  /**
+      * 浼犲叆鍐滃巻鏃ユ湡鏁板瓧杩斿洖姹夊瓧琛ㄧず娉�+      * @param lunar day
+      * @return Cn string
+      * @eg:var cnDay = calendar.toChinaDay(21) ;//cnMonth='寤夸竴'
+      */
+  toChinaDay: function (d) { // 鏃�=> \u65e5
+    var s
+    switch (d) {
+      case 10:
+        s = '\u521d\u5341'; break
+      case 20:
+        s = '\u4e8c\u5341'; break
+        break
+      case 30:
+        s = '\u4e09\u5341'; break
+        break
+      default :
+        s = this.nStr2[Math.floor(d / 10)]
+        s += this.nStr1[d % 10]
+    }
+    return (s)
+  },
+  /**
+      * 浼犲叆鏄熸湡鏁板瓧杩斿洖姹夊瓧琛ㄧず娉�+      * @param week
+      * @return Cn string
+      * @eg:var cnDay = calendar.toChinaWeek(2) ;//cnweek='浜�
+      */
+  toChinaWeek: function (w) { // 鏃�=> \u65e5
+    var s
+    s = this.nStr1[w]
+    return (s)
+  },
+  /**
+      * 骞翠唤杞敓鑲朳!浠呰兘澶ц嚧杞崲] => 绮剧‘鍒掑垎鐢熻倴鍒嗙晫绾挎槸鈥滅珛鏄モ�
+      * @param y year
+      * @return Cn string
+      * @eg:var animal = calendar.getAnimal(1987) ;//animal='鍏�
+      */
+  getAnimal: function (y) {
+    return this.Animals[(y - 4) % 12]
+  },
+
+  /**
+      * 浼犲叆闃冲巻骞存湀鏃ヨ幏寰楄缁嗙殑鍏巻銆佸啘鍘唎bject淇℃伅 <=>JSON
+      * @param y  solar year
+      * @param m  solar month
+      * @param d  solar day
+      * @return JSON object
+      * @eg:console.log(calendar.solar2lunar(1987,11,01));
+      */
+  solar2lunar: function (y, m, d) { // 鍙傛暟鍖洪棿1900.1.31~2100.12.31
+    // 骞翠唤闄愬畾銆佷笂闄�+    if (y < 1900 || y > 2100) {
+      return -1// undefined杞崲涓烘暟瀛楀彉涓篘aN
+    }
+    // 鍏巻浼犲弬鏈�笅闄�+    if (y == 1900 && m == 1 && d < 31) {
+      return -1
+    }
+    // 鏈紶鍙� 鑾峰緱褰撳ぉ
+    if (!y) {
+      var objDate = new Date()
+    } else {
+      var objDate = new Date(y, parseInt(m) - 1, d)
+    }
+    var i; var leap = 0; var temp = 0
+    // 淇ymd鍙傛暟
+    var y = objDate.getFullYear()
+    var m = objDate.getMonth() + 1
+    var d = objDate.getDate()
+    var offset = (Date.UTC(objDate.getFullYear(), objDate.getMonth(), objDate.getDate()) - Date.UTC(1900, 0, 31)) / 86400000
+    for (i = 1900; i < 2101 && offset > 0; i++) {
+      temp = this.lYearDays(i)
+      offset -= temp
+    }
+    if (offset < 0) {
+      offset += temp; i--
+    }
+
+    // 鏄惁浠婂ぉ
+    var isTodayObj = new Date()
+    var isToday = false
+    if (isTodayObj.getFullYear() == y && isTodayObj.getMonth() + 1 == m && isTodayObj.getDate() == d) {
+      isToday = true
+    }
+    // 鏄熸湡鍑�+    var nWeek = objDate.getDay()
+    var cWeek = this.nStr1[nWeek]
+    // 鏁板瓧琛ㄧず鍛ㄥ嚑椤哄簲澶╂湞鍛ㄤ竴寮�鐨勬儻渚�+    if (nWeek == 0) {
+      nWeek = 7
+    }
+    // 鍐滃巻骞�+    var year = i
+    var leap = this.leapMonth(i) // 闂板摢涓湀
+    var isLeap = false
+
+    // 鏁堥獙闂版湀
+    for (i = 1; i < 13 && offset > 0; i++) {
+      // 闂版湀
+      if (leap > 0 && i == (leap + 1) && isLeap == false) {
+        --i
+        isLeap = true; temp = this.leapDays(year) // 璁$畻鍐滃巻闂版湀澶╂暟
+      } else {
+        temp = this.monthDays(year, i)// 璁$畻鍐滃巻鏅�鏈堝ぉ鏁�+      }
+      // 瑙i櫎闂版湀
+      if (isLeap == true && i == (leap + 1)) { isLeap = false }
+      offset -= temp
+    }
+    // 闂版湀瀵艰嚧鏁扮粍涓嬫爣閲嶅彔鍙栧弽
+    if (offset == 0 && leap > 0 && i == leap + 1) {
+      if (isLeap) {
+        isLeap = false
+      } else {
+        isLeap = true; --i
+      }
+    }
+    if (offset < 0) {
+      offset += temp; --i
+    }
+    // 鍐滃巻鏈�+    var month = i
+    // 鍐滃巻鏃�+    var day = offset + 1
+    // 澶╁共鍦版敮澶勭悊
+    var sm = m - 1
+    var gzY = this.toGanZhiYear(year)
+
+    // 褰撴湀鐨勪袱涓妭姘�+    // bugfix-2017-7-24 11:03:38 use lunar Year Param `y` Not `year`
+    var firstNode = this.getTerm(y, (m * 2 - 1))// 杩斿洖褰撴湀銆岃妭銆嶄负鍑犳棩寮�
+    var secondNode = this.getTerm(y, (m * 2))// 杩斿洖褰撴湀銆岃妭銆嶄负鍑犳棩寮�
+
+    // 渚濇嵁12鑺傛皵淇骞叉敮鏈�+    var gzM = this.toGanZhi((y - 1900) * 12 + m + 11)
+    if (d >= firstNode) {
+      gzM = this.toGanZhi((y - 1900) * 12 + m + 12)
+    }
+
+    // 浼犲叆鐨勬棩鏈熺殑鑺傛皵涓庡惁
+    var isTerm = false
+    var Term = null
+    if (firstNode == d) {
+      isTerm = true
+      Term = this.solarTerm[m * 2 - 2]
+    }
+    if (secondNode == d) {
+      isTerm = true
+      Term = this.solarTerm[m * 2 - 1]
+    }
+    // 鏃ユ煴 褰撴湀涓�棩涓�1900/1/1 鐩稿樊澶╂暟
+    var dayCyclical = Date.UTC(y, sm, 1, 0, 0, 0, 0) / 86400000 + 25567 + 10
+    var gzD = this.toGanZhi(dayCyclical + d - 1)
+    // 璇ユ棩鏈熸墍灞炵殑鏄熷骇
+    var astro = this.toAstro(m, d)
+
+    return { 'lYear': year, 'lMonth': month, 'lDay': day, 'Animal': this.getAnimal(year), 'IMonthCn': (isLeap ? '\u95f0' : '') + this.toChinaMonth(month), 'IDayCn': this.toChinaDay(day), 'cYear': y, 'cMonth': m, 'cDay': d, 'gzYear': gzY, 'gzMonth': gzM, 'gzDay': gzD, 'isToday': isToday, 'isLeap': isLeap, 'nWeek': nWeek, 'ncWeek': '\u661f\u671f' + cWeek, 'isTerm': isTerm, 'Term': Term, 'astro': astro }
+  },
+
+  /**
+      * 浼犲叆鍐滃巻骞存湀鏃ヤ互鍙婁紶鍏ョ殑鏈堜唤鏄惁闂版湀鑾峰緱璇︾粏鐨勫叕鍘嗐�鍐滃巻object淇℃伅 <=>JSON
+      * @param y  lunar year
+      * @param m  lunar month
+      * @param d  lunar day
+      * @param isLeapMonth  lunar month is leap or not.[濡傛灉鏄啘鍘嗛棸鏈堢鍥涗釜鍙傛暟璧嬪�true鍗冲彲]
+      * @return JSON object
+      * @eg:console.log(calendar.lunar2solar(1987,9,10));
+      */
+  lunar2solar: function (y, m, d, isLeapMonth) { // 鍙傛暟鍖洪棿1900.1.31~2100.12.1
+    var isLeapMonth = !!isLeapMonth
+    var leapOffset = 0
+    var leapMonth = this.leapMonth(y)
+    var leapDay = this.leapDays(y)
+    if (isLeapMonth && (leapMonth != m)) { return -1 }// 浼犲弬瑕佹眰璁$畻璇ラ棸鏈堝叕鍘�浣嗚骞村緱鍑虹殑闂版湀涓庝紶鍙傜殑鏈堜唤骞朵笉鍚�+    if (y == 2100 && m == 12 && d > 1 || y == 1900 && m == 1 && d < 31) { return -1 }// 瓒呭嚭浜嗘渶澶ф瀬闄愬�
+    var day = this.monthDays(y, m)
+    var _day = day
+    // bugFix 2016-9-25
+    // if month is leap, _day use leapDays method
+    if (isLeapMonth) {
+      _day = this.leapDays(y, m)
+    }
+    if (y < 1900 || y > 2100 || d > _day) { return -1 }// 鍙傛暟鍚堟硶鎬ф晥楠�+
+    // 璁$畻鍐滃巻鐨勬椂闂村樊
+    var offset = 0
+    for (var i = 1900; i < y; i++) {
+      offset += this.lYearDays(i)
+    }
+    var leap = 0; var isAdd = false
+    for (var i = 1; i < m; i++) {
+      leap = this.leapMonth(y)
+      if (!isAdd) { // 澶勭悊闂版湀
+        if (leap <= i && leap > 0) {
+          offset += this.leapDays(y); isAdd = true
+        }
+      }
+      offset += this.monthDays(y, i)
+    }
+    // 杞崲闂版湀鍐滃巻 闇�ˉ鍏呰骞撮棸鏈堢殑鍓嶄竴涓湀鐨勬椂宸�+    if (isLeapMonth) { offset += day }
+    // 1900骞村啘鍘嗘鏈堜竴鏃ョ殑鍏巻鏃堕棿涓�900骞�鏈�0鏃�鏃�鍒�绉�璇ユ椂闂翠篃鏄湰鍐滃巻鐨勬渶寮�璧峰鐐�
+    var stmap = Date.UTC(1900, 1, 30, 0, 0, 0)
+    var calObj = new Date((offset + d - 31) * 86400000 + stmap)
+    var cY = calObj.getUTCFullYear()
+    var cM = calObj.getUTCMonth() + 1
+    var cD = calObj.getUTCDate()
+
+    return this.solar2lunar(cY, cM, cD)
+  }
+}
+
+export default calendar
diff --git a/comm/calendarUtil.js b/comm/calendarUtil.js
new file mode 100644
index 0000000..9e90147
--- /dev/null
+++ b/comm/calendarUtil.js
@@ -0,0 +1,440 @@
+import CalendarLunar from './calendarLunar.js'
+
+class CalendarUtil {
+	constructor({
+		date,
+		selected,
+		startDate,
+		endDate,
+		range
+	} = {}) {
+		// 褰撳墠鏃ユ湡
+		this.date = this.getDate(new Date()) // 褰撳墠鍒濆叆鏃ユ湡
+		// 鎵撶偣淇℃伅
+		this.selected = selected || [];
+		// 鑼冨洿寮�
+		this.startDate = startDate
+		// 鑼冨洿缁撴潫
+		this.endDate = endDate
+		this.range = range
+		// 姣忓懆鏃ユ湡
+		this.weeks = []
+		// this._getWeek(this.date.fullDate)
+	}
+	/**
+	 * 璁剧疆鏃ユ湡
+	 * @param {Object} date
+	 */
+	setDate(date) {
+		this.selectDate = this.getDate(date)
+		this._getWeek(this.selectDate.fullDate)
+		this._getPrevWeek(this.selectDate.fullDate)
+		this._getNextWeek(this.selectDate.fullDate)
+	}
+
+
+	/**
+	 * 閲嶇疆寮�鏃ユ湡
+	 */
+	resetSatrtDate(startDate) {
+		// 鑼冨洿寮�
+		this.startDate = startDate
+
+	}
+
+	/**
+	 * 閲嶇疆缁撴潫鏃ユ湡
+	 */
+	resetEndDate(endDate) {
+		// 鑼冨洿缁撴潫
+		this.endDate = endDate
+	}
+
+
+	/**
+	 * 鑾峰彇浠绘剰鏃堕棿
+	 */
+	getDate(date, AddDayCount = 0, str = 'day') {
+		if (!date) {
+			date = new Date()
+		}
+		if (typeof date !== 'object') {
+			date = date.replace(/-/g, '/')
+		}
+		const dd = new Date(date)
+		switch (str) {
+			case 'day':
+				dd.setDate(dd.getDate() + AddDayCount) // 鑾峰彇AddDayCount澶╁悗鐨勬棩鏈�+				break
+			case 'month':
+				if (dd.getDate() > 28) {
+					let dd2 = new Date(date)
+					dd2.setMonth(dd2.getMonth() + AddDayCount) // 鑾峰彇AddDayCount澶╁悗鐨勬棩鏈�+					
+					if(dd2.getDate() < dd.getDate())
+					{
+						dd.setDate(28)
+						dd.setMonth(dd.getMonth() + AddDayCount) // 鑾峰彇AddDayCount澶╁悗鐨勬棩鏈�+					}
+					else
+					{
+						dd.setMonth(dd.getMonth() + AddDayCount) // 鑾峰彇AddDayCount澶╁悗鐨勬棩鏈�+					}
+				} else {
+					dd.setMonth(dd.getMonth() + AddDayCount) // 鑾峰彇AddDayCount澶╁悗鐨勬棩鏈�+				}
+				break
+			case 'year':
+				dd.setFullYear(dd.getFullYear() + AddDayCount) // 鑾峰彇AddDayCount澶╁悗鐨勬棩鏈�+				break
+		}
+		const y = dd.getFullYear()
+		const m = dd.getMonth() + 1 < 10 ? '0' + (dd.getMonth() + 1) : dd.getMonth() + 1 // 鑾峰彇褰撳墠鏈堜唤鐨勬棩鏈燂紝涓嶈冻10琛�
+		const d = dd.getDate() < 10 ? '0' + dd.getDate() : dd.getDate() // 鑾峰彇褰撳墠鍑犲彿锛屼笉瓒�0琛�
+		return {
+			fullDate: `${y}-${m}-${d}`,
+			year: y,
+			month: m,
+			date: d,
+			day: dd.getDay()
+		}
+	}
+
+
+	/**
+	 * 鑾峰彇涓婃湀鍓╀綑澶╂暟
+	 */
+	_getLastMonthDays(firstDay, full) {
+		let dateArr = []
+		const before= this.getDate(full.fullDate, -1, 'month')
+		//console.log("_getLastMonthDays",full, before)
+		for (let i = firstDay; i > 0; i--) {
+			const beforeDate = new Date(full.year, full.month - 1, -i + 1).getDate()
+			let nowDate = before.year + '-' + before.month+ '-' + beforeDate
+			//console.log("_getLastMonthDays",beforeDate, nowDate )
+			let data = {
+				fullDate: nowDate,
+				year:before.year,
+				date: beforeDate,
+				month:  Number(before.month),
+				lunar: this.getlunar(before.year, Number(before.month), beforeDate),
+				disable: true,
+			}
+			// 鑾峰彇鎵撶偣淇℃伅
+			let info = this.selected && this.selected.find((item) => {
+				//console.log("_currentMonthDys info",item )
+				if (this.dateEqual(nowDate, item.date)) {
+					//console.log("_currentMonthDys info res",i )
+					return item
+				}
+			})
+			if (info) {
+				data.extraInfo = info
+			}	
+			//console.log("_getLastMonthDays",data)
+			dateArr.push(data)
+			
+		}
+		return dateArr
+	}
+	/**
+	 * 鑾峰彇鏈湀澶╂暟
+	 */
+	_currentMonthDys(dateData, full) {
+		let dateArr = []
+		let fullDate = this.date.fullDate
+		let weekStart =((full.date +35) - full.day) % 7 
+		//console.log("_currentMonthDys", weekStart,full )
+		for (let i = 1; i <= dateData; i++) {
+			let isinfo = false
+			let nowDate = full.year + '-' + (full.month < 10 ?
+				full.month : full.month) + '-' + (i < 10 ?
+				'0' + i : i)
+			// 鏄惁浠婂ぉ
+			let isDay = fullDate === nowDate
+			let isWeekend = ((i+7 - weekStart +1)%7) < 2 ? true: false
+			// 鑾峰彇鎵撶偣淇℃伅
+			let info = this.selected && this.selected.find((item) => {
+				//console.log("_currentMonthDys info",item )
+				if (this.dateEqual(nowDate, item.date)) {
+					//console.log("_currentMonthDys info res",i )
+					return item
+				}
+			})
+
+			// 鏃ユ湡绂佺敤
+			let disableBefore = true
+			let disableAfter = true
+			if (this.startDate) {
+				// let dateCompBefore = this.dateCompare(this.startDate, fullDate)
+				// disableBefore = this.dateCompare(dateCompBefore ? this.startDate : fullDate, nowDate)
+				disableBefore = this.dateCompare(this.startDate, nowDate)
+			}
+
+			if (this.endDate) {
+				// let dateCompAfter = this.dateCompare(fullDate, this.endDate)
+				// disableAfter = this.dateCompare(nowDate, dateCompAfter ? this.endDate : fullDate)
+				disableAfter = this.dateCompare(nowDate, this.endDate)
+			}
+			let checked = false
+			
+			let data = {
+				fullDate: nowDate,
+				year: full.year,
+				date: i,
+				month: full.month,
+				lunar: this.getlunar(full.year, full.month, i),
+				disable: !(disableBefore && disableAfter),
+				isDay,
+				isWeekend,
+				//solarTerm:jq?jq:""
+			}
+			if (info) {
+				data.extraInfo = info
+			}
+
+			dateArr.push(data)
+		}
+		return dateArr
+	}
+	/**
+	 * 鑾峰彇涓嬫湀澶╂暟
+	 */
+	_getNextMonthDays(surplus, full) {
+		let dateArr = []
+		const nextDate = this.getDate(full.fullDate, +1, 'month')
+		//console.log("_getNextMonthDays",full,nextDate )
+		for (let i = 1; i < surplus + 1; i++) {
+			let nowDate = nextDate.year + '-' +nextDate.month + '-' + (i < 10 ?
+				'0' + i : i)
+			//console.log("_getNextMonthDays",i, nowDate )
+			
+			let data = {
+				fullDate: nowDate,
+				year:nextDate.year,
+				date: i,
+				month: nextDate.month,
+				lunar: this.getlunar(nextDate.year, nextDate.month, i),
+				disable: true,
+			}
+			// 鑾峰彇鎵撶偣淇℃伅
+			let info = this.selected && this.selected.find((item) => {
+				//console.log("_currentMonthDys info",item )
+				if (this.dateEqual(nowDate, item.date)) {
+					//console.log("_currentMonthDys info res",i )
+					return item
+				}
+			})
+			if (info) {
+				data.extraInfo = info
+			}
+			//console.log("_getNextMonthDays",data)
+			dateArr.push(data)
+		
+		}
+		return dateArr
+	}
+
+	/**
+	 * 鑾峰彇褰撳墠鏃ユ湡璇︽儏
+	 * @param {Object} date
+	 */
+	getInfo(date) {
+		if (!date) {
+			date = new Date()
+		}
+		const dateInfo = this.canlender.find(item => item.fullDate === this.getDate(date).fullDate)
+		return dateInfo
+	}
+
+	/**
+	 * 姣旇緝鏃堕棿澶у皬
+	 */
+	dateCompare(startDate, endDate) {
+		// 璁$畻鎴鏃堕棿
+		startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
+		// 璁$畻璇︾粏椤圭殑鎴鏃堕棿
+		endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
+		if (startDate <= endDate) {
+			return true
+		} else {
+			return false
+		}
+	}
+
+	/**
+	 * 姣旇緝鏃堕棿鏄惁鐩哥瓑
+	 */
+	dateEqual(before, after) {
+		// 璁$畻鎴鏃堕棿
+		before = new Date(before.replace('-', '/').replace('-', '/'))
+		// 璁$畻璇︾粏椤圭殑鎴鏃堕棿
+		after = new Date(after.replace('-', '/').replace('-', '/'))
+		if (before.getTime() - after.getTime() === 0) {
+			return true
+		} else {
+			return false
+		}
+	}
+
+
+	/**
+	 * 鑾峰彇鏃ユ湡鑼冨洿鍐呮墍鏈夋棩鏈�+	 * @param {Object} begin
+	 * @param {Object} end
+	 */
+	geDateAll(begin, end) {
+		var arr = []
+		var ab = begin.split('-')
+		var ae = end.split('-')
+		var db = new Date()
+		db.setFullYear(ab[0], ab[1] - 1, ab[2])
+		var de = new Date()
+		de.setFullYear(ae[0], ae[1] - 1, ae[2])
+		var unixDb = db.getTime() - 24 * 60 * 60 * 1000
+		var unixDe = de.getTime() - 24 * 60 * 60 * 1000
+		for (var k = unixDb; k <= unixDe;) {
+			k = k + 24 * 60 * 60 * 1000
+			arr.push(this.getDate(new Date(parseInt(k))).fullDate)
+		}
+		return arr
+	}
+	/**
+	 * 璁$畻闃村巻鏃ユ湡鏄剧ず
+	 */
+	getlunar(year, month, date) {
+		return CalendarLunar.solar2lunar(year, month, date)
+	}
+	/**
+	 * 鑾峰彇褰撴湀绗竴澶�+	 */
+	getfirstday(dateData) {
+		const {
+			fullDate,
+			year,
+			month,
+			date,
+			day
+		} = this.getDate(dateData)
+		let nowDate = year+ '-' +month + '-01'
+		//console.log("getfirstday:" + nowDate)
+		return this.getDate(nowDate)
+	}
+	/**
+	 * 璁剧疆鎵撶偣
+	 */
+	setSelectInfo(data, value) {
+		this.selected = value
+		this._getWeek(data)
+	}
+	/**
+	 * 鑾峰彇姣忓懆鏁版嵁
+	 * @param {Object} dateData
+	 */
+	_getWeek(dateData) {
+		const {
+			fullDate,
+			year,
+			month,
+			date,
+			day
+		} = this.getDate(dateData)
+		//console.log("_getWeeks",dateData,fullDate)
+		let firstDay = new Date(year, month - 1, 1).getDay()
+		let currentDay = new Date(year, month, 0).getDate()
+		let dates = {
+			lastMonthDays: this._getLastMonthDays(firstDay, this.getDate(dateData)), // 涓婁釜鏈堟湯灏惧嚑澶�+			currentMonthDys: this._currentMonthDys(currentDay, this.getDate(dateData)), // 鏈湀澶╂暟
+			nextMonthDays: [], // 涓嬩釜鏈堝紑濮嬪嚑澶�+			weeks: []
+		}
+		let canlender = []
+		let surplus = 42 - (dates.lastMonthDays.length + dates.currentMonthDys.length)
+		if(surplus >= 7)
+			surplus-= 7
+		dates.nextMonthDays = this._getNextMonthDays(surplus, this.getDate(dateData))
+		canlender = canlender.concat(dates.lastMonthDays, dates.currentMonthDys, dates.nextMonthDays)
+		let weeks = []
+		// 鎷兼帴鏁扮粍  涓婁釜鏈堝紑濮嬪嚑澶�+ 鏈湀澶╂暟+ 涓嬩釜鏈堝紑濮嬪嚑澶�+		for (let i = 0; i < canlender.length; i++) {
+			if (i % 7 === 0) {
+				weeks[parseInt(i / 7)] = new Array(7)
+			}
+			weeks[parseInt(i / 7)][i % 7] = canlender[i]
+		}
+		this.canlender = canlender
+		//console.log("_getWeek", weeks )
+		this.weeks = weeks
+	}
+	_getPrevWeek(dateData) {
+		
+		const {
+			fullDate,
+			year,
+			month,
+			date,
+			day
+		} = this.getDate(dateData, -1, 'month')
+		//console.log("_getPrevWeek",dateData,fullDate)
+		let firstDay = new Date(year, month - 1, 1).getDay()
+		let currentDay = new Date(year, month, 0).getDate()
+		let dates = {
+			lastMonthDays: this._getLastMonthDays(firstDay, this.getDate(dateData, -1, 'month')), // 涓婁釜鏈堟湯灏惧嚑澶�+			currentMonthDys: this._currentMonthDys(currentDay, this.getDate(dateData, -1, 'month')), // 鏈湀澶╂暟
+			nextMonthDays: [], // 涓嬩釜鏈堝紑濮嬪嚑澶�+			weeks: []
+		}
+		let canlender = []
+		let surplus = 42 - (dates.lastMonthDays.length + dates.currentMonthDys.length)
+		if(surplus >= 7)
+			surplus-= 7
+		dates.nextMonthDays = this._getNextMonthDays(surplus, this.getDate(dateData, -1, 'month'))
+		canlender = canlender.concat(dates.lastMonthDays, dates.currentMonthDys, dates.nextMonthDays)
+		let weeks = []
+		// 鎷兼帴鏁扮粍  涓婁釜鏈堝紑濮嬪嚑澶�+ 鏈湀澶╂暟+ 涓嬩釜鏈堝紑濮嬪嚑澶�+		for (let i = 0; i < canlender.length; i++) {
+			if (i % 7 === 0) {
+				weeks[parseInt(i / 7)] = new Array(7)
+			}
+			weeks[parseInt(i / 7)][i % 7] = canlender[i]
+		}
+		//console.log("_getWeek", weeks )
+		this.weeks_prev = weeks
+	}
+	_getNextWeek(dateData) {
+		const {
+			fullDate,
+			year,
+			month,
+			date,
+			day
+		} = this.getDate(dateData, +1, 'month')
+		//console.log("_getNextWeek",dateData,fullDate)
+		let firstDay = new Date(year, month - 1, 1).getDay()
+		let currentDay = new Date(year, month, 0).getDate()
+		let dates = {
+			lastMonthDays: this._getLastMonthDays(firstDay, this.getDate(dateData, +1, 'month')), // 涓婁釜鏈堟湯灏惧嚑澶�+			currentMonthDys: this._currentMonthDys(currentDay, this.getDate(dateData, +1, 'month')), // 鏈湀澶╂暟
+			nextMonthDays: [], // 涓嬩釜鏈堝紑濮嬪嚑澶�+			weeks: []
+		}
+		let canlender = []
+		let surplus = 42 - (dates.lastMonthDays.length + dates.currentMonthDys.length)
+		if(surplus >= 7)
+			surplus-= 7
+		dates.nextMonthDays = this._getNextMonthDays(surplus, this.getDate(dateData, +1, 'month'))
+		canlender = canlender.concat(dates.lastMonthDays, dates.currentMonthDys, dates.nextMonthDays)
+		let weeks = []
+		// 鎷兼帴鏁扮粍  涓婁釜鏈堝紑濮嬪嚑澶�+ 鏈湀澶╂暟+ 涓嬩釜鏈堝紑濮嬪嚑澶�+		for (let i = 0; i < canlender.length; i++) {
+			if (i % 7 === 0) {
+				weeks[parseInt(i / 7)] = new Array(7)
+			}
+			weeks[parseInt(i / 7)][i % 7] = canlender[i]
+		}
+		this.weeks_next = weeks
+	}
+}
+
+
+export default CalendarUtil
diff --git a/comm/extend.js b/comm/extend.js
new file mode 100644
index 0000000..bf320cf
--- /dev/null
+++ b/comm/extend.js
@@ -0,0 +1,446 @@
+var dateUtils = {
+	UNITS: {
+		'骞�: 31557600000,
+		'鏈�: 2629800000,
+		'澶�: 86400000,
+		'灏忔椂': 3600000,
+		'鍒嗛挓': 60000,
+		'绉�: 1000
+	},
+	humanize: function(milliseconds) {
+		var humanize = '';
+		mui.each(this.UNITS, function(unit, value) {
+			if (milliseconds >= value) {
+				humanize = Math.floor(milliseconds / value) + unit + '鍓�;
+				return false;
+			}
+			return true;
+		});
+		return humanize; // || '鍒氬垰';
+	},
+	format: function(dateStr, notime) {
+		var date = this.parse(dateStr)
+		return this.openinfo_formatter_ttime(dateStr, 'notime');
+		/* var diff = Date.now() - date.getTime();
+		if (diff < this.UNITS['澶�]) {
+			return this.humanize(diff);
+		} else
+			return this.openinfo_formatter_ttime(dateStr, notime)
+		var _format = function (number) {
+			return (number < 10 ? ('0' + number) : number);
+		};
+		return date.getFullYear() + '/' + _format(date.getMonth() + 1) + '/' + _format(date.getDate()) + '-' + _format(
+			date.getHours()) + ':' + _format(date.getMinutes()); */
+	},
+	parse: function(str) { //灏�yyyy-mm-dd HH:MM:ss"鏍煎紡鐨勫瓧绗︿覆锛岃浆鍖栦负涓�釜Date瀵硅薄
+		var a = str.split(/[^0-9]/);
+		if (a.length == 1)
+			return new Date(a[0], 1, 1, 0, 0, 1);
+		else if (a.length == 2)
+			return new Date(a[0], a[1] - 1, 1, 0, 0, 1);
+		else if (a.length == 3)
+			return new Date(a[0], a[1] - 1, a[2], 0, 0, 1);
+		else if (a.length == 4)
+			return new Date(a[0], a[1] - 1, a[2], a[3], 0, 1);
+		else if (a.length == 5)
+			return new Date(a[0], a[1] - 1, a[2], a[3], a[4], 1);
+		else if (a.length >= 6)
+			return new Date(a[0], a[1] - 1, a[2], a[3], a[4], a[5]);
+		else
+			return new Date();
+	},
+	openinfo_formatter_ttime: function(cellvalue, nowhat) {
+		var cv_o = cellvalue.split(' ')[0];
+		var cv_t = cellvalue.split(' ')[1];
+		if (nowhat) {
+			cv_t = "";
+		}
+		var date = new Date();
+		var time = cv_o;
+		var h_m = "";
+		if (cv_t)
+			h_m = cv_t.split(':')[0] + ':' + cv_t.split(':')[1];
+		if (cv_o.split('-').length == 3)
+			if (date.getFullYear() == cv_o.split('-')[0]) {
+				time = " " + cv_o.split('-')[1] + '-' + cv_o.split('-')[2];
+				if (date.getMonth() + 1 == cv_o.split('-')[1]) {
+					if (date.getDate() == cv_o.split('-')[2])
+						time = "浠婂ぉ";
+					if (date.getDate() - 1 == cv_o.split('-')[2])
+						time = '鏄ㄥぉ';
+					if (date.getDate() - 2 == cv_o.split('-')[2])
+						time = '鍓嶅ぉ';
+				}
+			} else {
+				time = cv_o.split('-')[1] + '-' + cv_o.split('-')[2];
+				if (date.getFullYear() - 1 == cv_o.split('-')[0]) {
+					//time = time + " 鍘诲勾";
+				}
+				if (date.getFullYear() - 2 == cv_o.split('-')[0]) {
+					//time = time + " 鍓嶅勾";
+				}
+				time = cv_o.split('-')[0] + '-' + time;
+			}
+		return time + (h_m ? (" " + h_m) : "");
+	},
+	getDate: function(type) {
+		const date = new Date();
+
+		let year = date.getFullYear();
+		let month = date.getMonth() + 1;
+		let day = date.getDate();
+
+		if (type === 'start') {
+			year = year - 10;
+		} else if (type === 'end') {
+			year = year + 10;
+		}
+		month = month > 9 ? month : '0' + month;;
+		day = day > 9 ? day : '0' + day;
+
+		return `${year}-${month}-${day}`;
+	},
+	getShortDate: function(dateStr) {
+		var date = new Date()
+		if (dateStr) {
+			dateStr = dateStr.trim()
+
+			if (dateStr != "")
+				date = this.parse(dateStr)
+		}
+		//console.log("getShortDate2",dateStr,date)
+		let year = date.getFullYear();
+		let month = date.getMonth() + 1;
+		return `${year}骞�{month}鏈坄;
+	},
+	toDateTimeString: function(date) {
+		let year = date.getFullYear();
+		let month = date.getMonth() + 1;
+		let day = date.getDate();
+		let hour = date.getHours();
+		let minute = date.getMinutes();
+		let second = date.getSeconds();
+		month = month > 9 ? month : '0' + month;;
+		day = day > 9 ? day : '0' + day;
+		hour = hour > 9 ? hour : '0' + hour;
+		minute = minute > 9 ? minute : '0' + minute;
+		second = second > 9 ? second : '0' + second;
+		return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
+	},
+	toDateString: function(date) {
+		let year = date.getFullYear();
+		let month = date.getMonth() + 1;
+		let day = date.getDate();
+		month = month > 9 ? month : '0' + month;;
+		day = day > 9 ? day : '0' + day;
+		return `${year}-${month}-${day}`;
+	},
+	toTimeString: function(date) {
+		let hour = date.getHours();
+		let minute = date.getMinutes();
+		let second = date.getSeconds();
+		hour = hour > 9 ? hour : '0' + hour;
+		minute = minute > 9 ? minute : '0' + minute;
+		second = second > 9 ? second : '0' + second;
+		return `${hour}:${minute}:${second}`;
+	},
+	toShortTimeString: function(date) {
+		let hour = date.getHours();
+		let minute = date.getMinutes();
+		hour = hour > 9 ? hour : '0' + hour;
+		minute = minute > 9 ? minute : '0' + minute;
+		return `${hour}:${minute}`;
+	},
+
+	toShortDateTimeString: function(date) {
+		let month = date.getMonth() + 1;
+		let day = date.getDate();
+		month = month > 9 ? month : '0' + month;;
+		day = day > 9 ? day : '0' + day;
+		let hour = date.getHours();
+		let minute = date.getMinutes();
+		hour = hour > 9 ? hour : '0' + hour;
+		minute = minute > 9 ? minute : '0' + minute;
+		return `${month}-${day} ${hour}:${minute}`;
+	},
+	formatDate: function(date, fmt) {
+		// 澶勭悊骞翠唤
+		if (/(y+)/.test(fmt)) {
+			fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length));
+		}
+		// 瀹氫箟鏈堛�鏃ャ�鏃躲�鍒嗐�绉掔殑鍖归厤瑙勫垯
+		let o = {
+			'M+': date.getMonth() + 1, // 鏈堜唤
+			'd+': date.getDate(), // 鏃�+			'h+': date.getHours(), // 灏忔椂
+			'm+': date.getMinutes(), // 鍒嗛挓
+			's+': date.getSeconds() // 绉�+		};
+
+		// 閬嶅巻瀵硅薄锛屽姩鎬佺敓鎴愭鍒欒〃杈惧紡鏉ユ浛鎹㈡牸寮忓瓧绗︿覆
+		for (let k in o) {
+			if (new RegExp(`(${k})`).test(fmt)) {
+				let str = o[k] + '';
+				// 鏍规嵁鏍煎紡绗︾殑闀垮害鍐冲畾鏄惁琛ラ浂
+				fmt = fmt.replace(RegExp.$1, RegExp.$1.length === 1 ? str : ('00' + str).substr(str.length));
+			}
+		}
+		return fmt;
+	},
+}
+var fileUtils = {
+	isPictureType: function(fileName) {
+		if (".tiff.pjp.jfif.bmp.gif.svg.png.xbm.dib.jxl.jpeg.svgz.jpg.webp.ico.tif.pjpeg.avif.image".indexOf(
+				fileName
+				.split('.').pop().toLowerCase()) < 0) //鍥剧墖
+		{
+			return false
+		}
+		return true
+	},
+	isDocumentType: function(fileName) {
+		//#ifdef MP-WEIXIN
+		if (".doc.xls.ppt.pdf.docx.xlsx.pptx".indexOf(fileName.split('.').pop().toLowerCase()) < 0) //鏂囨。
+		{
+			return false
+		}
+		return true
+		//#endif
+		//#ifdef MP-DINGTALK
+		if (".pdf".indexOf(fileName.split('.').pop().toLowerCase()) < 0) //鏂囨。
+		{
+			return false
+		}
+		return true
+		//#endif
+		return false
+
+	},
+	isVideoType: function(fileName) {
+		if (".mp4.webm.ogg.video".indexOf(fileName.split('.').pop().toLowerCase()) < 0) //瑙嗛
+		{
+			return false
+		}
+		return true
+	},
+	isAudioType: function(fileName) {
+		if (".mp3.audio".indexOf(fileName.split('.').pop().toLowerCase()) < 0) //瑙嗛
+		{
+			return false
+		}
+		return true
+	},
+	isCanPreviewType: function(fileName) {
+		if (this.isDocumentType(fileName) || this.isPictureType(fileName) || this.isVideoType(fileName) || this
+			.isAudioType(fileName)) //瑙嗛
+		{
+			return true
+		}
+		return false
+	},
+	getTypeBgColor: function(fileName) {
+		let color = '#ccc'
+		if (this.isPictureType(fileName)) {
+			color = '#e5e5e5'
+		} else if (this.isVideoType(fileName)) {
+			color = '#456476'
+		} else if (this.isAudioType(fileName)) {
+			color = '#f0f0f0'
+		} else if (".doc.docx".indexOf(fileName.split('.').pop().toLowerCase()) > -1) //鏂囨。
+		{
+			color = '#059fff'
+		} else if (".xls.xlsx".indexOf(fileName.split('.').pop().toLowerCase()) > -1) //鏂囨。
+		{
+			color = '#16b251'
+		} else if (".ppt.pptx".indexOf(fileName.split('.').pop().toLowerCase()) > -1) //鏂囨。
+		{
+			color = '#f9661d'
+		} else if (".pdf".indexOf(fileName.split('.').pop().toLowerCase()) > -1) //鏂囨。
+		{
+			color = '#fa493d'
+		}
+		return color
+	},
+	getFileSizeStr(size) {
+		let sizeStr
+		if (size > 1024 * 1024)
+			sizeStr = Math.round(size / (1024 * 1024)) + ' MB'
+		else if (size > 1024)
+			sizeStr = Math.round(size / (1024)) + ' KB'
+		else
+			sizeStr = (size) + ' B'
+		return sizeStr
+	},
+	moveToLocal(tempPath, dirName, fileName) {
+		return new Promise((resolve, reject) => {
+			plus.io.resolveLocalFileSystemURL(
+				tempPath,
+				(entry) => {
+					plus.io.resolveLocalFileSystemURL(
+						'_doc/',
+						(dirEntry) => {
+							dirEntry.getDirectory(
+								dirName, {
+									create: true
+								},
+								(subDir) => {
+									// 宸ュ叿锛氭彃鍏ュ悗缂�埌鏂囦欢鍚�+									function insertSuffix(name, suffix) {
+										const dot = name.lastIndexOf('.');
+										if (dot === -1) return name + suffix;
+										return name.slice(0, dot) + suffix + name.slice(dot);
+									}
+
+									function tryName(name, idx = 0) {
+										const realName =
+											idx === 0 ? name : insertSuffix(name, `(${idx})`);
+										subDir.getFile(
+											realName, {
+												create: false
+											}, // 鍙鏌ワ紝涓嶅垱寤�+											() => {
+												tryName(name, idx + 1) // 宸插瓨鍦紝缁х画璇�+											},
+											() => {
+												// 涓嶅瓨鍦紝鍙互鐢ㄤ簡
+												doMove(realName);
+											}
+										);
+									}
+									// 鐪熸鎵ц move
+									function doMove(finalName) {
+										plus.io.resolveLocalFileSystemURL(
+											tempPath,
+											(tempEntry) => {
+												tempEntry.moveTo(
+													subDir,
+													finalName,
+													(newEntry) => resolve(newEntry
+														.fullPath),
+													(e) => reject(e)
+												);
+											},
+											(e) => reject(e)
+										);
+									}
+									tryName(fileName);
+								},
+								(e) => reject(e)
+							);
+						},
+						(e) => reject(e)
+					);
+				},
+				(e) => reject(e)
+			);
+		});
+	},
+
+
+	listSavedFiles(dirName) {
+		return new Promise((resolve, reject) => {
+			plus.io.resolveLocalFileSystemURL(
+				`_doc/${dirName}/`,
+				(dirEntry) => {
+					const reader = dirEntry.createReader();
+					reader.readEntries(
+						(entries) => {
+							const files = entries
+								.filter((e) => e.isFile)
+								.map((e) => ({
+									name: e.name,
+									fullPath: e.fullPath,
+								}));
+							resolve(files);
+						},
+						(e) => reject(e)
+					);
+				},
+				(e) => reject(e)
+			);
+		});
+	},
+	clearSavedFiles(dirName) {
+		return new Promise((resolve, reject) => {
+			plus.io.resolveLocalFileSystemURL(
+				`_doc/${dirName}/`,
+				(dirEntry) => {
+					const reader = dirEntry.createReader();
+					reader.readEntries(
+						(entries) => {
+							entries.forEach((entry) => {
+								entry.remove();
+							})
+							resolve();
+						},
+						(e) => reject(e)
+					);
+				},
+				(e) => reject(e)
+			);
+		});
+	},
+
+	deleteSavedFile(dirName, fileName) {
+		return new Promise((resolve, reject) => {
+			plus.io.resolveLocalFileSystemURL(
+				`_doc/${dirName}/${fileName}`,
+				(entry) => {
+					entry.remove(
+						() => resolve(), // 鍒犻櫎鎴愬姛
+						(e) => reject(e) // 鍒犻櫎澶辫触
+					);
+				},
+				(e) => reject(e) // 鏂囦欢涓嶅瓨鍦ㄦ垨璺緞閿欒
+			);
+		})
+	},
+
+	getSavedFileSize(filePath) {
+		return new Promise((resolve, reject) => {
+			plus.io.getFileInfo({
+				filePath,
+				success: (info) => {
+					resolve(info.size || 0)
+
+				},
+				fail:()=>{
+					resolve(0)
+				}
+			});
+		})
+	},
+
+}
+
+var taskstate = {
+	'鏈': 'fs-mail_close',
+	'宸茶': 'fs-mail_open',
+	'鎵ц': 'fs-run_man',
+	'閫�洖': 'fs-goback',
+	'瀹屾垚': 'fs-flag',
+	'鎻愪氦': 'fs-TaskSubmission',
+	'鎼佺疆': 'fs-pause',
+	'鍑洪敊': 'fs-wrong',
+	'缁堟': 'fs-Due',
+	'褰掓。': 'fs-project_doc',
+	'璁″垝': 'fs-project_manage',
+	'绛夊緟': 'fs-wait',
+	'缂栬緫': 'fs-PRJ_edit',
+	'瀹℃牳': 'fs-Audit',
+	'鍒犻櫎': 'fs-delete_solid_circle',
+	'鎾ゅ洖': 'fs-return_key'
+};
+var eventstate = {
+	'闂': 'fs-question2',
+	'閿欒': 'fs-bug',
+	'闇�眰': 'fs-CirclPlus',
+	'寤鸿': 'fs-suggest',
+	'璁ㄨ': 'fs-discuss',
+}
+export default {
+	dateUtils,
+	fileUtils,
+	taskstate,
+	eventstate
+}
\ No newline at end of file
diff --git a/comm/utils-android.js b/comm/utils-android.js
new file mode 100644
index 0000000..31dc430
--- /dev/null
+++ b/comm/utils-android.js
@@ -0,0 +1,25 @@
+export const sendEmail = (filePath) => {
+	console.log("sendEmail",filePath)
+	const main = plus.android.runtimeMainActivity();
+	const Intent = plus.android.importClass('android.content.Intent');
+	const Uri = plus.android.importClass('android.net.Uri');
+	const File = plus.android.importClass('java.io.File');
+
+	const emailIntent = new Intent(Intent.ACTION_SEND);
+	emailIntent.setType('*/*'); // 浠呴偖浠跺鎴风
+	emailIntent.putExtra(Intent.EXTRA_SUBJECT, '鏁版嵁鎶ュ憡');
+	emailIntent.putExtra(Intent.EXTRA_TEXT, '璇﹁闄勪欢');
+
+	const file = new File(filePath);
+	const uri = Uri.fromFile(file);
+	emailIntent.putExtra(Intent.EXTRA_STREAM, uri);
+	emailIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+	try {
+		main.startActivity(Intent.createChooser(emailIntent, '鍙戦�閭欢'));
+		console.log("sendEmail success")
+	} catch (e) {
+			console.log("sendEmail fail")
+		plus.nativeUI.toast('鏈壘鍒伴偖浠跺鎴风');
+	}
+}
\ No newline at end of file
diff --git a/comm/utils-ios.js b/comm/utils-ios.js
new file mode 100644
index 0000000..02c93f7
--- /dev/null
+++ b/comm/utils-ios.js
@@ -0,0 +1,22 @@
+export const sendEmail = (filePath) => {
+	plus.ios.importClass('MFMailComposeViewController');
+	const mailClass = plus.ios.newObject('MFMailComposeViewController');
+	if (!mailClass.canSendMail()) {
+		plus.nativeUI.toast('鏈厤缃偖浠惰处鎴�);
+		return;
+	}
+	// 璁剧疆涓婚/姝f枃
+	mailClass.setSubject('鏁版嵁鎶ュ憡');
+	mailClass.setMessageBodyIsHTML('璇﹁闄勪欢', false);
+
+	// 娣诲姞闄勪欢
+	const fileManager = plus.ios.newObject('NSFileManager').defaultManager();
+	const data = fileManager.contentsAtPath(filePath);
+	const fileName = filePath.split('/').pop();
+	mailClass.addAttachmentDataMimeTypeFileName(data, 'text/plain', fileName);
+
+	// 寮瑰嚭鍘熺敓閭欢绐楀彛
+	const app = plus.ios.importClass('UIApplication').sharedApplication();
+	const rootVC = app.keyWindow().rootViewController();
+	rootVC.presentViewControllerAnimatedCompletion(mailClass, true, null);
+}
\ No newline at end of file
diff --git a/comm/utils.js b/comm/utils.js
index 8e8692a..421a8dd 100644
--- a/comm/utils.js
+++ b/comm/utils.js
@@ -1,10 +1,24 @@
-var SESSION_SUFFIX = "diniu_app_"
-export function showModal(message, title = '鎻愮ず', iscancel = true, confirmText = "鏄�, cancelText = "鍚�) {
+import {
+	sendEmail as sendEmailAndroid
+} from './utils-android.js';
+import {
+	sendEmail as sendEmailiOS
+} from './utils-ios.js';
+
+const SESSION_SUFFIX = "diniu_app_"
+
+export function showModal({
+	content,
+	title = '',
+	showCancel = true,
+	confirmText,
+	cancelText
+}) {
 	return new Promise((resolve) => {
 		uni.showModal({
-			title: title,
-			content: message,
-			showCancel: iscancel,
+			title,
+			content,
+			showCancel,
 			confirmText,
 			cancelText,
 			success: function(res) {
@@ -56,13 +70,13 @@
 	// 	verticalAlign:"center"
 	// });
 	return uni.showModal({
-		title: "鎻愮ず",
+		// title: "鎻愮ず",
 		content: tip,
 		showCancel: false
 	});
 }
 
-export function showError(ex, title = "閿欒") {
+export function showError(ex, title = "") {
 	if (!ex)
 		return
 	let tip = ex
@@ -73,7 +87,7 @@
 		tip = typeof ex.errMsg == "string" ? ex.errMsg : typeof ex.msg == "string" ? ex.msg : typeof ex.message ==
 			"string" ? ex.message : exStr
 	}
-	console.log(ex,tip)
+	console.log(ex, tip)
 	//plus.nativeUI.alert(tip,title);
 	return uni.showModal({
 		title: title || "",
@@ -101,6 +115,32 @@
 
 	uni.hideLoading()
 }
+// 1. 鎶婁换鎰忔暟鎹啓鎴�txt 鏂囦欢
+export function saveAndSendEmail(data, fileName = 'report.txt') {
+	const fullPath = `_esgo/${fileName}`; // 娌欑洅鍐呰矾寰�+	plus.io.requestFileSystem(plus.io.PRIVATE_DOC, fs => {
+		fs.root.getFile(fullPath, {
+			create: true
+		}, fileEntry => {
+			fileEntry.createWriter(writer => {
+				writer.onwrite = () => {
+					// 鍐欏畬绔嬪嵆鍙戦偖浠�+					sendEmailWithAttach(fileEntry.fullPath);
+				};
+				writer.write(data); // data 鍙互鏄瓧绗︿覆/JSON
+			});
+		});
+	});
+}
+//甯﹂檮浠惰皟璧风郴缁熼偖浠跺鎴风
+export function sendEmailWithAttach(absPath) {
+	const platform = uni.getSystemInfoSync().platform;
+	if (platform === 'android') {
+		sendEmailAndroid(absPath);
+	} else if (platform === 'ios') {
+		sendEmailiOS(absPath);
+	}
+}
 
 let session = {
 	setValue(key, value) {
diff --git a/components/calendar/index.vue b/components/calendar/index.vue
new file mode 100644
index 0000000..3e7bbc4
--- /dev/null
+++ b/components/calendar/index.vue
@@ -0,0 +1,238 @@
+<template>
+	<view class="uni-calendar">
+		<view class="uni-panel-header">
+			<view class="uni-panel-header-child-item" @click="clickToday">浠婂ぉ</view>
+			<view class="uni-panel-header-child-date">
+				<picker mode="date" fields="month" :start="startDateCalendar" :end="endDateCalendar"
+					@change="bindDateChange">
+					<view>
+						<text>{{monthText}}</text>
+						<text style="padding: 5px;" class="fs-ArrowDown" />
+					</view>
+				</picker>
+			</view>
+			<view class="uni-panel-header-child-item" @click="clickCancel">鍙栨秷</view>
+			<view class="uni-panel-header-child-item" @click="clickConfirm">纭畾</view>
+		</view>
+		<view class="uni-panel-content">
+			<view class="uni-panel-week">
+				<text class="uni-panel-week-item">鏃�/text>
+				<text class="uni-panel-week-item">涓�/text>
+				<text class="uni-panel-week-item">浜�/text>
+				<text class="uni-panel-week-item">涓�/text>
+				<text class="uni-panel-week-item">鍥�/text>
+				<text class="uni-panel-week-item">浜�/text>
+				<text class="uni-panel-week-item">鍏�/text>
+			</view>
+			<view class="uni-panel-calenda">
+				<calendarDate ref="calendar" class="calendar-date" :canWeekView="false" :lunar="showLunar"
+					@change="changeCalendar" @monthSwitch="monthSwitchCalendar"></calendarDate>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import TaskInit from "@/comm/extend.js"
+	import CalendarLunar from '@/comm/calendarLunar.js'
+	import calendarDate from './infos/date.vue'
+	export default {
+		name: "calendar",
+		components: {
+			calendarDate
+		},
+		props: {
+			showLunar: {
+				type: Boolean,
+				default: false
+			},
+
+		},
+
+		emits: ['close', 'confirm', 'change', 'monthSwitch'],
+		data() {
+			return {
+				dateCalendar: new Date(),
+				selCalendar: {},
+				startDateCalendar: TaskInit.dateUtils.getDate('start'),
+				endDateCalendar: TaskInit.dateUtils.getDate('end'),
+			}
+		},
+		computed: {
+			monthText() {
+				return TaskInit.dateUtils.getShortDate(TaskInit.dateUtils.toDateString(this.dateCalendar))
+			},
+			dayText() {
+
+				let curDate = new Date()
+				let fullDate = this.selCalendar.fulldate
+				let month = curDate.getMonth() + 1
+				let day = curDate.getDate()
+				let nowDate = curDate.getFullYear() + '-' + (month < 10 ?
+					'0' + month : month) + '-' + (day < 10 ?
+					'0' + day : day)
+				//console.log("dayText",nowDate,fullDate)	
+				// 鏄惁浠婂ぉ
+				let isDay = fullDate === nowDate
+				if (isDay)
+					return "浠婂ぉ"
+				else
+					return this.selCalendar.month + '-' + this.selCalendar.date
+			},
+		},
+		methods: {
+			setData: function(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];
+					});
+				});
+			},
+			bindDateChange(e) {
+				console.log("bindDateChange", e.detail)
+				this.setData({
+					dateCalendar: TaskInit.dateUtils.parse(e.detail.value)
+				})
+				this.$refs.calendar.init(this.dateCalendar)
+				// console.log("bindDateChange2", this.dateCalendar)
+			},
+			changeCalendar(e) {
+				// console.log("changeCalendar", e)
+				var _this = this
+
+				_this.setData({
+					selCalendar: e,
+				})
+				this.$emit('change', {
+					date: this.selCalendar
+				})
+			},
+			monthSwitchCalendar(e) {
+				// console.log("monthSwitchCalendar", e)
+				var _this = this
+				const yearOld = _this.dateCalendar.getFullYear()
+				_this.setData({
+					dateCalendar: new Date(e.year, e.month - 1, 1)
+				})
+				this.$emit('monthSwitch', {
+					date: this.dateCalendar
+				})
+
+				// console.log("monthSwitchCalendar", _this.dateCalendar)
+			},
+			clickToday() {
+				this.$refs.calendar.backtoday()
+			},
+			clickCancel() {
+				this.$emit('close')
+			},
+			clickConfirm() {
+				if (this.selCalendar.fulldate)
+					this.$emit('confirm', this.selCalendar)
+				else {
+					let curDate = new Date()
+					let month = curDate.getMonth() + 1
+					let day = curDate.getDate()
+					let nowDate = curDate.getFullYear() + '-' + (month < 10 ?
+						'0' + month : month) + '-' + (day < 10 ?
+						'0' + day : day)
+					this.$emit('confirm', {
+						fulldate: nowDate
+					})
+				}
+
+			},
+			
+
+		},
+		mounted() {},
+	}
+</script>
+
+<style lang="scss" scoped>
+	.uni-calendar {
+		display: flex;
+		width: 100%;
+		height: 312px;
+		padding: 8px 2px 12px 2px;
+		flex-direction: column;
+
+		.uni-panel-header {
+			height: 40px;
+			padding: 0 3%;
+			display: flex;
+			flex-wrap: no-wrap;
+			flex-direction: row;
+			align-items: center;
+			color: #000;
+			font-size: 16px;
+			/* justify-content: space-between; */
+		}
+
+		.uni-panel-header-child-item {
+			display: flex;
+			padding: 0px 8px;
+			font-size: 16px;
+			color: #007aff;
+		}
+
+		.uni-panel-header-child-date {
+			display: flex;
+			flex: 1;
+			justify-content: center;
+		}
+
+		.clearfix {
+			content: " ";
+			box-sizing: border-box;
+		}
+
+		.uni-panel-content {
+			padding-top: 5px;
+			height: calc(100% - 48px);
+		}
+
+		.uni-panel-week {
+			width: 94%;
+			padding: 0 3%;
+			display: flex;
+			height: 32px;
+		}
+
+		.uni-panel-week-item {
+			display: flex;
+			flex: auto;
+			justify-content: center;
+			align-items: center;
+			font-size: 14px;
+			color: #606060;
+		}
+
+		.uni-panel-calendar {
+			width: 100%;
+			display: flex;
+			flex-direction: column;
+			height: calc(100% - 32px);
+		}
+
+		.calendar-date {
+			width: 94%;
+			padding: 2px 3%;
+			background-color: #fff;
+		}
+	}
+</style>
\ No newline at end of file
diff --git a/components/calendar/infos/date-item.vue b/components/calendar/infos/date-item.vue
new file mode 100644
index 0000000..6b38f1a
--- /dev/null
+++ b/components/calendar/infos/date-item.vue
@@ -0,0 +1,134 @@
+<template>
+	<view class="uni-calendar-item__weeks-box" @click="choiceDate(weeks)"><!--  -->
+		<view class="uni-calendar-item__weeks-box-item" :class="{
+			'uni-calendar-item--disable':weeks.disable && !retract,
+			'uni-calendar-item--isDay': weeks.isDay,
+			'uni-calendar-item--checked':(calendar.fullDate === weeks.fullDate) ,
+			}">
+			<text class="uni-calendar-item__weeks-box-text" :class="{
+				'uni-calendar-item--isDay-text': weeks.isDay || weeks.isWeekend,
+				'uni-calendar-item--checked-text':(calendar.fullDate === weeks.fullDate) ,
+				'uni-calendar-item--disable':weeks.disable && !retract,
+				}">{{weeks.date}}</text>
+			<text class="uni-calendar-item__weeks-lunar-text" :class="{
+				'uni-calendar-item--isDay-text':weeks.isDay || weeks.isWeekend,
+				'uni-calendar-item--checked-text':(calendar.fullDate === weeks.fullDate) ,
+				'uni-calendar-item--disable':weeks.disable && !retract,
+				}">{{weeks.isDay ? todayText : (lunar?( ( weeks.lunar.Term ? weeks.lunar.Term:(weeks.lunar.IDayCn === '鍒濅竴'?weeks.lunar.IMonthCn:weeks.lunar.IDayCn))):'')}}</text>
+			<text v-if="weeks.extraInfo&&weeks.extraInfo.info" class="uni-calendar-item--extra-info" :class="{
+				'uni-calendar-item--isDay2':(calendar.fullDate === weeks.fullDate) }"></text>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		name: "calendarDateItem",
+		emits:['change'],
+		props: {
+			weeks: {
+				type: Object,
+				default () {
+					return {}
+				}
+			},
+			calendar: {
+				type: Object,
+				default: () => {
+					return {}
+				}
+			},
+			lunar: {
+				type: Boolean,
+				default: false
+			},
+			retract: {
+				type: Boolean,
+				retract: true
+			},
+		},
+		watch: {
+			lunar(newVal) {
+				console.log("calendar-date-item lunar",newVal)
+			},
+		},
+		computed: {
+			todayText() {
+				return "浠婂ぉ"
+			},
+		},
+		methods: {
+			choiceDate(weeks) {
+				this.$emit('change', weeks)
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.uni-calendar-item__weeks-box {
+		flex: 1;
+		display: flex;
+		padding: 2px 2px;
+	}
+
+	.uni-calendar-item__weeks-box-text {
+		margin-top: 2px;
+		font-size: 32rpx;
+		color: black;
+	}
+
+	.uni-calendar-item__weeks-lunar-text {
+		font-size: 20rpx;
+		color: gray;
+	}
+	.uni-calendar-item__weeks-box-item {
+		position: relative;
+		flex: 1;
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		height: 90rpx;
+	}
+	.uni-calendar-item--disable {
+		//background-color: rgba(249, 249, 249,160);
+		color: gray;
+		opacity: 0.8;
+	}
+
+	.uni-calendar-item--isDay-text {
+		color:  #00aaff;
+	}
+
+	.uni-calendar-item--isDay {
+		border: 1px solid #00c1f140;
+		background-color: #00c3f840;
+		border-radius: 25px;
+	}
+	.uni-calendar-item--isDay2 {
+		background-color: #fff !important;
+	}
+	.uni-calendar-item--extra {
+		color:  #00aaff;
+		opacity: 0.8;
+	}
+	.uni-calendar-item--extra-info {
+		background-color: #adadad;
+		border-radius: 2px;
+		position: absolute;
+		bottom: 2px;
+		width: 5px;
+		height: 5px;
+	}
+	.uni-calendar-item--checked {
+		border: 1px solid #00aaff;
+		background-color: #00aaff;
+		border-radius: 25px;
+		color: #fff;
+		
+	}
+	.uni-calendar-item--checked-text {
+		color:  #fff;
+	}
+
+</style>
diff --git a/components/calendar/infos/date.vue b/components/calendar/infos/date.vue
new file mode 100644
index 0000000..1738be0
--- /dev/null
+++ b/components/calendar/infos/date.vue
@@ -0,0 +1,732 @@
+<template>
+	<view class="uni-calendar-date">
+		<swiper class="uni-panel-swiper" :style="{height:weeks_height}" circular current="currentSwiper"
+			@change="changeSwiper">
+			<swiper-item>
+				<view class="uni-table-col">
+					<view class="uni-table-row"
+						:style="(!retract || weekIndex == to_prev_week_index)? 'display: flex;': 'display: none;'"
+						v-for="(item,weekIndex) in weeks_prev_co" :key="weekIndex">
+						<view class="uni-calendar-weeks-item" v-for="(days,weeksIndex) in item" :key="weeksIndex">
+							<dateCalendarItem class="uni-calendar-weeks-day" :weeks="days" :calendar="calendar"
+								:retract="retract" :lunar="lunar" @change="choiceDate"></dateCalendarItem>
+						</view>
+					</view>
+				</view>
+			</swiper-item>
+			<swiper-item>
+				<view class="uni-table-col">
+					<view class="uni-table-row"
+						:style="(!retract || weekIndex == to_week_index)? 'display: flex;': 'display: none;'"
+						v-for="(item,weekIndex) in weeks" :key="weekIndex">
+						<view class="uni-calendar-weeks-item" v-for="(days,weeksIndex) in item" :key="weeksIndex">
+							<dateCalendarItem class="uni-calendar-weeks-day" :weeks="days" :calendar="calendar"
+								:retract="retract" :lunar="lunar" @change="choiceDate"></dateCalendarItem>
+						</view>
+					</view>
+				</view>
+			</swiper-item>
+			<swiper-item>
+				<view class="uni-table-col">
+					<view class="uni-table-row"
+						:style=" (!retract || weekIndex == to_next_week_index)? 'display: flex;': 'display: none;'"
+						v-for="(item,weekIndex) in weeks_next_co" :key="weekIndex">
+						<view class="uni-calendar-weeks-item" v-for="(days,weeksIndex) in item" :key="weeksIndex">
+							<dateCalendarItem class="uni-calendar-weeks-day" :weeks="days" :calendar="calendar"
+								:retract="retract" :lunar="lunar" @change="choiceDate"></dateCalendarItem>
+						</view>
+					</view>
+				</view>
+			</swiper-item>
+		</swiper>
+		<view class="uni-table-row" v-if="canWeekView">
+			<view class="uni-panel-calendar-arrow stretch fs-ArrowDown" :class="{'uni-panel-calendar-arrow2':retract}"
+				@click="clickRetract"></view>
+		</view>
+	</view>
+
+</template>
+
+<script>
+	import CalendarUtil from "@/comm/calendarUtil.js"
+	import dateCalendarItem from './date-item.vue'
+	export default {
+		name: "calendarDate",
+		components: {
+			dateCalendarItem
+		},
+		emits: ['change', 'monthSwitch', 'weekSwitch'],
+		props: {
+			date: {
+				type: String,
+				default: ''
+			},
+			lunar: {
+				type: Boolean,
+				default: false
+			},
+			canWeekView: {
+				type: Boolean,
+				default: true
+			},
+			viewWeek: {
+				type: Boolean,
+				default: true
+			},
+			selected: {
+				type: Array,
+				default () {
+					return []
+				}
+			},
+		},
+		data() {
+			return {
+				currentSwiper: 0,
+				weeks: [],
+				weeks_prev: [],
+				weeks_prev_week: [],
+				weeks_next: [],
+				weeks_next_week: [],
+				calendar: {},
+				nowDate: '',
+				retract: true,
+				to_week_index: 0,
+				to_prev_week_index: 0,
+				to_next_week_index: 0,
+
+			}
+		},
+		computed: {
+			weeks_prev_co() {
+				return this.retract ? this.weeks_prev_week : this.weeks_prev
+			},
+			weeks_next_co() {
+				return this.retract ? this.weeks_next_week : this.weeks_next
+			},
+			weeks_height() {
+				if (this.retract)
+					return '100rpx'
+				else {
+					if (this.currentSwiper == 1) {
+						return (this.weeks.length * 100) + 'rpx'
+					} else if (this.currentSwiper == 2) {
+						return (this.weeks_next.length * 100) + 'rpx'
+					} else {
+						return (this.weeks_prev.length * 100) + 'rpx'
+					}
+				}
+			},
+		},
+		watch: {
+			date(newVal) {
+				this.init(newVal)
+			},
+			selected(newVal) {
+				//console.log("calendar-date selected",newVal )
+				this.cale.setSelectInfo(this.nowDate.fullDate, newVal)
+				this.updateWeeks()
+				this.updateWeek()
+				//console.log("calendar-date week",this.weeks_prev )
+			},
+			canWeekView(newVal) {
+				//	console.log("canWeekView",newVal )
+				if (!newVal) {
+					this.retract = false
+					this.updateWeeks()
+				}
+			},
+			lunar(newVal) {
+				//console.log("calendar-date lunar",newVal)
+			},
+
+		},
+		created() {
+			// 鑾峰彇鏃ュ巻鏂规硶瀹炰緥
+			this.cale = new CalendarUtil({
+				// date: new Date(),
+				selected: this.selected,
+				startDate: this.startDate,
+				endDate: this.endDate,
+				range: this.range,
+			})
+			// 閫変腑鏌愪竴澶�+			// this.cale.setDate(this.date)
+			this.init(this.date)
+			// this.setDay
+		},
+		mounted() {
+			if (!this.canWeekView) {
+				this.retract = false
+			} else {
+				this.retract = this.viewWeek
+			}
+		},
+		methods: {
+			setData: function(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];
+					});
+				});
+			},
+			updateWeeks(weekIndex = -1) {
+				//console.log("updateWeeks weekIndex",weekIndex)
+				if (weekIndex < 0) {
+					if (this.currentSwiper == 1) {
+						weekIndex = this.to_week_index
+					} else if (this.currentSwiper == 2) {
+						weekIndex = this.to_next_week_index
+					} else {
+						weekIndex = this.to_prev_week_index
+					}
+				}
+				if (this.currentSwiper == 1) {
+					this.setData({
+						to_week_index: weekIndex,
+						weeks_prev: this.cale.weeks_prev,
+						weeks: this.cale.weeks,
+						weeks_next: this.cale.weeks_next,
+					})
+				} else if (this.currentSwiper == 2) {
+					this.setData({
+						to_next_week_index: weekIndex,
+						weeks_prev: this.cale.weeks_next,
+						weeks: this.cale.weeks_prev,
+						weeks_next: this.cale.weeks,
+					})
+				} else {
+					this.setData({
+						to_prev_week_index: weekIndex,
+						weeks_prev: this.cale.weeks,
+						weeks: this.cale.weeks_next,
+						weeks_next: this.cale.weeks_prev,
+					})
+				}
+				// console.log("updateWeeks weeks",this.cale.weeks)
+				// console.log("updateWeeks weeks_prev",this.cale.weeks_prev)
+				// console.log("updateWeeks weeks_next",this.cale.weeks_next)
+			},
+			updateWeek(weekIndex = -1) {
+				//console.log("updateWeek weekIndex",weekIndex)
+				if (weekIndex < 0) {
+					if (this.currentSwiper == 1) {
+						weekIndex = this.to_week_index
+					} else if (this.currentSwiper == 2) {
+						weekIndex = this.to_next_week_index
+					} else {
+						weekIndex = this.to_prev_week_index
+					}
+				}
+
+				if (this.retract) {
+
+					if (weekIndex < 0) {
+						if (this.currentSwiper == 1) {
+							weekIndex = this.to_week_index
+						} else if (this.currentSwiper == 2) {
+							weekIndex = this.to_next_week_index
+						} else {
+							weekIndex = this.to_prev_week_index
+						}
+					}
+
+					let prev_week_index = 1
+					let next_week_index = 1
+					let week = []
+					week = this.cale.weeks
+					let prev_week = []
+					prev_week = this.cale.weeks_prev
+					let next_week = []
+					next_week = this.cale.weeks_next
+					//console.log("updateWeek2 weekIndex",weekIndex,week.length)
+					if (weekIndex > 0) {
+						prev_week_index = weekIndex - 1
+						prev_week = this.cale.weeks
+					} else {
+						prev_week = this.cale.weeks_prev
+						let first = week[0][0]
+						let firstDay = this.cale.getDate(first.fullDate)
+						//console.log("updateWeek2 firstDay",firstDay)
+						//prev_week_index = this.weeks_prev_week.length  -1
+						if (firstDay.date == 1)
+							prev_week_index = this.weeks_prev_week.length - 1
+						else
+							prev_week_index = this.weeks_prev_week.length - 2
+					}
+					if (weekIndex > week.length - 2) {
+						next_week = this.cale.weeks_next
+						let first = next_week[0][0]
+						let firstDay = this.cale.getDate(first.fullDate)
+						//console.log("updateWeek2 firstDay",firstDay)
+						//next_week_index =0
+						if (firstDay.date == 1)
+							next_week_index = 0
+						else
+							next_week_index = 1
+					} else {
+						next_week_index = weekIndex + 1
+						next_week = this.cale.weeks
+					}
+
+					if (this.currentSwiper == 1) {
+						this.to_week_index = weekIndex
+						this.to_prev_week_index = prev_week_index
+						this.to_next_week_index = next_week_index
+						this.weeks = week
+						this.weeks_prev_week = prev_week
+						this.weeks_next_week = next_week
+					} else if (this.currentSwiper == 2) {
+						this.to_next_week_index = weekIndex
+						this.to_week_index = prev_week_index
+						this.to_prev_week_index = next_week_index
+						this.weeks = prev_week
+						this.weeks_prev_week = next_week
+						this.weeks_next_week = week
+					} else {
+						this.to_prev_week_index = weekIndex
+						this.to_next_week_index = prev_week_index
+						this.to_week_index = next_week_index
+						this.weeks = next_week
+						this.weeks_prev_week = week
+						this.weeks_next_week = prev_week
+					}
+					//console.log("updateWeek3 weekIndex",weekIndex,prev_week_index,next_week_index)
+				} else {
+					if (this.currentSwiper == 1) {
+						this.to_week_index = weekIndex
+					} else if (this.currentSwiper == 2) {
+						this.to_next_week_index = weekIndex
+					} else {
+						this.to_prev_week_index = weekIndex
+					}
+				}
+
+			},
+			/**
+			 * 鍒濆鍖栨棩鏈熸樉绀�+			 * @param {Object} date
+			 */
+			init(date) {
+				this.cale.setDate(date)
+				this.nowDate = this.calendar = this.cale.getInfo(date)
+
+				let weekIndex = 0
+				let curDate = this.cale.date
+				//console.log("init nowDate:" , this.nowDate,curDate)
+				if (curDate.date > curDate.day + 1) {
+					let weekPos = (curDate.date - curDate.day - 1) % 7
+					if (weekPos > 0)
+						weekIndex = 1
+					weekIndex += (curDate.date - curDate.day - weekPos - 1) / 7
+				}
+				//console.log("init:" , weekIndex,this.retract)
+				this.updateWeeks(weekIndex)
+				this.updateWeek(weekIndex)
+				this.change()
+				//console.log("init2:" , this.to_prev_week_index)
+			},
+			/**
+			 * 鍙樺寲瑙﹀彂
+			 */
+			change() {
+				this.setEmit('change')
+			},
+			/**
+			 * 閫夋嫨鏈堜唤瑙﹀彂
+			 */
+			monthSwitch() {
+				let {
+					year,
+					month
+				} = this.nowDate
+				this.$emit('monthSwitch', {
+					year,
+					month: Number(month)
+				})
+			},
+			/**
+			 * 閫夋嫨鍛ㄨЕ鍙�+			 */
+			weekSwitch() {
+				let {
+					year,
+					month,
+					date,
+					day
+				} = this.calendar
+				this.$emit('weekSwitch', {
+					year,
+					month: Number(month),
+					date: Number(date),
+					day
+				})
+			},
+
+			/**
+			 * 娲惧彂浜嬩欢
+			 * @param {Object} name
+			 */
+			setEmit(name) {
+				let {
+					year,
+					month,
+					date,
+					fullDate,
+					lunar,
+					extraInfo
+				} = this.calendar
+				this.$emit(name, {
+					year,
+					month,
+					date,
+					fulldate: fullDate,
+					lunar,
+					extraInfo: extraInfo || {}
+				})
+			},
+			clickRetract() {
+				this.retract = !this.retract;
+				//console.log("clickRetract",this.retract)
+				if (this.retract) {
+					let weekIndex = 0
+					let curDate = this.cale.getDate(this.calendar.fullDate)
+					if (curDate.date > curDate.day + 1) {
+						let weekPos = (curDate.date - curDate.day - 1) % 7
+						if (weekPos > 0)
+							weekIndex = 1
+						weekIndex += (curDate.date - curDate.day - weekPos - 1) / 7
+					}
+					//console.log("clickRetract",curDate,weekIndex)
+					this.updateWeek(weekIndex)
+				} else
+					this.updateWeeks()
+			},
+			/**
+			 * 閫夋嫨澶╄Е鍙�+			 * @param {Object} weeks
+			 */
+			choiceDate(weeks) {
+				if (weeks.disable) {
+					//console.log("choiceDate",weeks)
+					this.setDate(weeks.fullDate)
+					this.nowDate = this.calendar = this.cale.getInfo(weeks.fullDate)
+					this.monthSwitch()
+					let weekIndex = 0
+					let curDate = this.cale.getDate(weeks.fullDate)
+					//console.log("choiceDate2:",weeks)
+					if (curDate.date > curDate.day + 1) {
+						let weekPos = (curDate.date - curDate.day - 1) % 7
+						if (weekPos > 0)
+							weekIndex = 1
+						weekIndex += (curDate.date - curDate.day - weekPos - 1) / 7
+					}
+					//console.log("choiceDate weekIndex",weekIndex)
+					this.updateWeek(weekIndex)
+					this.change()
+					return;
+				}
+				//console.log("choiceDate:",weeks)
+				this.calendar = weeks
+				let weekIndex = 0
+				let curDate = this.cale.getDate(weeks.fullDate)
+				//console.log("choiceDate2:",weeks)
+				if (curDate.date > curDate.day + 1) {
+					let weekPos = (curDate.date - curDate.day - 1) % 7
+					if (weekPos > 0)
+						weekIndex = 1
+					weekIndex += (curDate.date - curDate.day - weekPos - 1) / 7
+				}
+				//console.log("choiceDate weekIndex",weekIndex)
+				this.updateWeek(weekIndex)
+				this.change()
+				/* if (weeks.disable){
+					console.log("choiceDate",weeks)
+					this.setDate(weeks.fullDate)
+					this.nowDate = this.calendar = this.cale.getInfo(weeks.fullDate)
+					this.monthSwitch()
+					this.change()
+					return;
+				}
+				console.log("choiceDate",weeks)
+				this.calendar = weeks
+				this.weeks = this.cale.weeks
+				this.change() */
+			},
+			/**
+			 * 閫夋嫨澶╄Е鍙�+			 * @param {Object} days
+			 * @param {Number} weekIndex
+			 */
+			clickDate(days, weekIndex) {
+				//console.log("choiceDate",days,weekIndex)
+				if (days.disable) {
+					//console.log("choiceDate",days)
+					this.setDate(days.fullDate)
+					this.nowDate = this.calendar = this.cale.getInfo(days.fullDate)
+					this.monthSwitch()
+					this.change()
+					return;
+				}
+				//console.log("choiceDate",days)
+				this.calendar = days
+				this.updateWeek(weekIndex)
+				this.change()
+			},
+			/**
+			 * 鍥炲埌浠婂ぉ
+			 */
+			backtoday() {
+				//console.log(this.cale.getDate(new Date()).fullDate);
+				let date = this.cale.getDate(new Date()).fullDate
+				// this.cale.setDate(date)
+				this.init(date)
+				this.monthSwitch()
+			},
+			/**
+			 * 涓婁釜鏈�+			 */
+			prev() {
+				if (this.retract) {
+					let bChanged = false
+					if (this.currentSwiper == 1) {
+						if (this.to_week_index == 0)
+							bChanged = true
+					} else if (this.currentSwiper == 2) {
+						if (this.to_next_week_index == 0)
+							bChanged = true
+					} else {
+						if (this.to_prev_week_index == 0)
+							bChanged = true
+					}
+					let oldMonth = this.calendar.month
+					this.calendar = this.cale.getDate(this.calendar.fullDate, -7, 'day')
+					//console.log("prev Month",oldMonth,this.calendar )
+					if (oldMonth != this.calendar.month) {
+						const preDate = this.cale.getDate(this.nowDate.fullDate, -1, 'month').fullDate
+						//console.log("prev",preDate)
+						this.setDate(preDate)
+						this.monthSwitch()
+					} else
+						this.updateWeek()
+					this.change()
+				} else {
+					const preDate = this.cale.getDate(this.nowDate.fullDate, -1, 'month').fullDate
+					//console.log("prev",preDate)
+					this.setDate(preDate)
+					this.monthSwitch()
+				}
+
+			},
+			/**
+			 * 涓嬩釜鏈�+			 */
+			next() {
+				if (this.retract) {
+					let weekIndex = 0
+					if (this.currentSwiper == 1) {
+
+						weekIndex = this.to_week_index
+					} else if (this.currentSwiper == 2) {
+						weekIndex = this.to_next_week_index
+					} else {
+						weekIndex = this.to_prev_week_index
+					}
+					//console.log("next",weekIndex)
+					let lastCalendar = this.calendar
+					this.calendar = this.cale.getDate(this.calendar.fullDate, +7, 'day')
+					let firstDay = this.cale.getfirstday(this.calendar.fullDate)
+					if (lastCalendar.month != this.calendar.month) {
+						const nextDate = this.cale.getDate(this.nowDate.fullDate, +1, 'month').fullDate
+						//console.log("next",nextDate)
+						this.setDate(nextDate)
+						this.monthSwitch()
+					} else
+						this.updateWeek()
+					this.change()
+				} else {
+					const nextDate = this.cale.getDate(this.nowDate.fullDate, +1, 'month').fullDate
+					//console.log("next",nextDate)
+					this.setDate(nextDate)
+					this.monthSwitch()
+				}
+			},
+			/**
+			 * 璁剧疆鏃ユ湡
+			 * @param {Object} date
+			 */
+			setDate(date) {
+
+				this.cale.setDate(date)
+				let curDate = this.cale.date
+				this.nowDate = this.cale.getInfo(date)
+				//this.nowDate = this.calendar = this.cale.getInfo(date)		
+				//this.calendar = this.nowDate 
+				let curDate2 = this.cale.getDate(this.calendar.fullDate)
+				let weekIndex = 0
+				//console.log("setDate",this.nowDate,curDate,curDate2)
+
+
+				if (this.nowDate.year == curDate.year && curDate.month == this.nowDate.month) {
+					let curDate2 = curDate
+					if (curDate2.date > curDate2.day + 1) {
+						let weekPos = (curDate2.date - curDate2.day - 1) % 7
+						if (weekPos > 0)
+							weekIndex = 1
+						weekIndex += (curDate2.date - curDate2.day - weekPos - 1) / 7
+					}
+					//console.log("setDate2",weekIndex,curDate2)
+					if (this.retract) {
+						this.updateWeeks(weekIndex)
+						this.updateWeek()
+					} else {
+						this.updateWeeks()
+					}
+					this.calendar = curDate
+					this.change()
+				} else {
+					if (curDate2.date > curDate2.day + 1) {
+						let weekPos = (curDate2.date - curDate2.day - 1) % 7
+						if (weekPos > 0)
+							weekIndex = 1
+						weekIndex += (curDate2.date - curDate2.day - weekPos - 1) / 7
+					}
+					//console.log("setDate3",weekIndex,curDate2)
+					if (this.retract) {
+						this.calendar = this.cale.getfirstday(date)
+						weekIndex = 0
+						this.updateWeeks(weekIndex)
+						this.updateWeek()
+
+					} else {
+						this.updateWeeks()
+						this.calendar = this.cale.getfirstday(date)
+					}
+					this.change()
+				}
+			},
+			changeSwiper(evt) {
+				//console.log("changeSwiper1:",this.currentSwiper)
+				let current = evt.target.current || evt.detail.current;
+				let primary_current = this.currentSwiper
+				this.currentSwiper = current;
+				//console.log("changeSwiper2:",current)
+				if (primary_current - current == -1 || primary_current - current == 2) {
+					this.next()
+				} else {
+					this.prev()
+				}
+				/* if(index ==0 )
+				{
+					if(this.currentSwiper == 2)
+					{
+						this.next()
+					}
+					else{
+						this.prev()
+					}
+				}
+				else if(index ==2 )
+				{
+					if(this.currentSwiper == 0)
+					{
+						this.prev()
+					}
+					else{
+						this.next()
+					}
+				}
+				else{
+					
+					if(this.currentSwiper < index)
+					{
+						this.next()
+					}
+					else
+						this.prev()
+				}
+				this.currentSwiper = index */
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.uni-calendar-date {
+		width: 100%;
+		display: flex;
+		position: relative;
+		flex-direction: column;
+		background-color: #fff;
+
+		.uni-panel-swiper {
+			margin: 0 auto;
+			position: relative;
+			width: 100%;
+			height: 100%;
+			z-index: 1
+		}
+
+		.uni-table-col {
+			display: flex;
+			flex-direction: column;
+		}
+
+		.uni-table-row {
+			position: relative;
+			width: 100%;
+			display: flex;
+			flex-direction: row;
+		}
+
+		.uni-calendar-weeks-item {
+			display: flex;
+			// flex: auto;
+			flex: 1;
+			justify-content: center;
+			align-items: center;
+			font-size: 32rpx;
+		}
+
+		.uni-calendar-weeks-day {
+			flex: 1;
+			display: flex;
+			/* 		flex-direction: column;
+		justify-content: center;
+		align-items: center; */
+			height: 100rpx;
+			/* 		border-bottom-color: #F5F5F5;
+		border-bottom-style: solid;
+		border-bottom-width: 1px; */
+		}
+
+		.uni-calendar-weeks-day-text {
+			font-size: 14px;
+		}
+
+		.uni-panel-calendar-arrow {
+			padding: 2px;
+			margin-left: 350rpx;
+			color: #808080;
+			transform: scale(2, 1);
+		}
+
+		.uni-panel-calendar-arrow2 {
+			transform: rotate(180deg) scale(2, 1);
+		}
+	}
+</style>
\ No newline at end of file
diff --git a/components/cmd-progress/index.vue b/components/cmd-progress/index.vue
new file mode 100644
index 0000000..2ec29d2
--- /dev/null
+++ b/components/cmd-progress/index.vue
@@ -0,0 +1,558 @@
+<template>
+  <view class="cmd-progress cmd-progress-default" :class="setStatusClass">
+    <block v-if="type == 'circle' || type == 'dashboard'">
+      <view class="cmd-progress cmd-progress-default" :class="setStatusClass">
+        <view class="cmd-progress-inner" :style="setCircleStyle">
+          <!-- 缁樺埗鍦�start -->
+          <!-- #ifdef H5 -->
+          <svg viewBox="0 0 100 100" class="cmd-progress-circle">
+            <path :d="setCirclePath" stroke="#f3f3f3" :stroke-linecap="strokeShape" :stroke-width="strokeWidth"
+              fill-opacity="0" class="cmd-progress-circle-trail" :style="setCircleTrailStyle"></path>
+            <path :d="setCirclePath" :stroke-linecap="strokeShape" :stroke-width="strokeWidth" fill-opacity="0" class="cmd-progress-circle-path"
+              :style="setCirclePathStyle"></path>
+          </svg>
+          <!-- #endif -->
+          <!-- #ifndef H5 -->
+          <text :style="setCircle"></text>
+          <!-- #endif -->
+          <!-- 缁樺埗鍦�end -->
+          <!-- 鐘舵�鏂囨湰 start -->
+          <block v-if="showInfo">
+            <text class="cmd-progress-text" :title="setFormat">
+              <block v-if="status != 'success' && status != 'exception' && setProgress < 100">{{setFormat}}</block>
+              <!-- #ifdef H5 -->
+              <svg v-if="status == 'exception'" viewBox="64 64 896 896" data-icon="close" width="1em" height="1em" fill="currentColor"
+                aria-hidden="true">
+                <path d="M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 0 0 203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z"></path>
+              </svg>
+              <svg v-if="status == 'success' || setProgress == 100" viewBox="64 64 896 896" data-icon="check" width="1em"
+                height="1em" fill="currentColor" aria-hidden="true" :style="{'color': strokeColor ? strokeColor : ''}">
+                <path d="M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 0 0-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z"></path>
+              </svg>
+              <!-- #endif -->
+              <!-- #ifndef H5 -->
+              <text v-if="status == 'exception' || status == 'success' || setProgress == 100" :style="setCircleIcon"></text>
+              <!-- #endif -->
+            </text>
+          </block>
+          <!-- 鐘舵�鏂囨湰 end -->
+        </view>
+      </view>
+    </block>
+
+    <block v-if="type == 'line'">
+      <!-- 杩涘害鏉�start -->
+      <view class="cmd-progress-outer">
+        <view class="cmd-progress-inner" :style="{'border-radius': strokeShape == 'square' ? 0 : '100px'}">
+          <view class="cmd-progress-bg" :style="setLineStyle"></view>
+          <view v-if="successPercent" class="cmd-progress-success-bg" :style="setLineSuccessStyle"></view>
+        </view>
+      </view>
+      <!-- 杩涘害鏉�end -->
+      <!-- 杩涘害鏉℃槸鍚︽樉绀轰俊鎭�start -->
+      <block v-if="showInfo">
+        <text class="cmd-progress-text" :title="setFormat">
+          <block v-if="status != 'success' && status != 'exception' && setProgress < 100">{{setFormat}}</block>
+          <!-- #ifdef H5 -->
+          <svg v-if="status == 'exception'" viewBox="64 64 896 896" data-icon="close-circle" width="1em" height="1em"
+            fill="currentColor" aria-hidden="true">
+            <path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 0 1-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z"></path>
+          </svg>
+          <svg v-if="status == 'success' || setProgress == 100" viewBox="64 64 896 896" data-icon="check-circle" width="1em"
+            height="1em" fill="currentColor" aria-hidden="true" :style="{'color': strokeColor ? strokeColor : ''}">
+            <path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm193.5 301.7l-210.6 292a31.8 31.8 0 0 1-51.7 0L318.5 484.9c-3.8-5.3 0-12.7 6.5-12.7h46.9c10.2 0 19.9 4.9 25.9 13.3l71.2 98.8 157.2-218c6-8.3 15.6-13.3 25.9-13.3H699c6.5 0 10.3 7.4 6.5 12.7z"></path>
+          </svg>
+          <!-- #endif -->
+          <!-- #ifndef H5 -->
+          <text v-if="status == 'exception' || status == 'success' || setProgress == 100" :style="setLineStatusIcon"></text>
+          <!-- #endif -->
+        </text>
+      </block>
+      <!-- 杩涘害鏉℃槸鍚︽樉绀轰俊鎭�end -->
+    </block>
+  </view>
+</template>
+
+<script>
+  /**  
+   * 杩涘害鏉$粍浠� 
+   * @description 鏄剧ず涓�釜鎿嶄綔瀹屾垚鐨勭櫨鍒嗘瘮鏃讹紝涓虹敤鎴锋樉绀鸿鎿嶄綔鐨勫綋鍓嶈繘搴﹀拰鐘舵�銆� 
+   * @tutorial https://ext.dcloud.net.cn/plugin?id=259  
+   * @property {String} type 杩涘害绫诲瀷 - 绾垮瀷锛歭ine銆佸渾鍦堝舰锛歝ircle銆佷华琛ㄧ洏锛歞ashboard锛岄粯璁ょ嚎鍨嬶細line  
+   * @property {Number} percent 杩涘害鐧惧垎姣斿� - 鏄剧ず鑼冨洿0-100 锛屽彲鑳芥暟姣旇緝澶у氨闇�鑷繁杞垚鐧惧垎姣旂殑鍊� 
+   * @property {Number} success-percent 杩涘害宸插畬鎴愮殑鐧惧垎鍑�- 浠呮敮鎸佽繘搴︾嚎鍨嬶細line  
+   * @property {String} status 杩涘害鐘舵� - 娑屽姩锛歛ctive锛堜粎鏀寔绾垮瀷锛歭ine锛夈�姝e父锛歯ormal銆佸畬鎴愶細success銆佸け璐ワ細exception锛岄粯璁ゆ甯革細normal  
+   * @property {Boolean} show-info 杩涘害鐘舵�淇℃伅 - 鏄惁鏄剧ず杩涘害鏁板�鎴栫姸鎬佸浘鏍囷紝榛樿true  
+   * @property {Number} stroke-width 杩涘害绾挎潯鐨勫搴�- 寤鸿鍦ㄦ潯绾跨殑瀹藉害鑼冨洿锛�-50锛屼笌杩涘害鏉℃樉绀哄搴︽湁鍏筹紝榛樿8  
+   * @property {String} stroke-color 杩涘害绾挎潯鐨勯鑹�- 娓愬彉鑹蹭粎鏀寔绾垮瀷锛歭ine  
+   * @property {String} stroke-shape 杩涘害绾挎潯涓ょ鐨勫舰鐘�- 鍦嗭細round銆佹柟鍧楃洿瑙掞細square锛岄粯璁ゅ渾锛歳ound  
+   * @property {Number} width 杩涘害鐢诲竷瀹藉害 - 浠呮敮鎸佸渾鍦堝舰锛歝ircle銆佷华琛ㄧ洏锛歞ashboard锛岄粯璁�0  
+   * @property {String} gap-degree 杩涘害鍦嗗舰缂哄彛瑙掑害 - 鍙彇鍊�0 ~ 360,浠呮敮鎸佸渾鍦堝舰锛歝ircle銆佷华琛ㄧ洏锛歞ashboard  
+   * @property {String} gap-position 杩涘害鍦嗗舰缂哄彛浣嶇疆 - 鍙彇鍊�top', 'bottom', 'left', 'right',浠呮敮鎸佸渾鍦堝舰锛歝ircle銆佷华琛ㄧ洏锛歞ashboard  
+   * @example <cmd-progress :percent="30"></cmd-progress>  
+   */
+  export default {
+    name: 'cmd-progress',
+
+    props: {
+      /**
+       * 绫诲瀷榛樿锛歭ine锛屽彲閫�line circle dashboard
+       */
+      type: {
+        validator: val => {
+          return ['line', 'circle', 'dashboard'].includes(val);
+        },
+        default: 'line'
+      },
+      /**
+       * 鐧惧垎姣�+       */
+      percent: {
+        type: Number,
+        default: 0
+      },
+      /**
+       * 宸插畬鎴愮殑鍒嗘鐧惧垎锛屼粎鏀寔绫诲瀷line
+       */
+      successPercent: {
+        type: Number,
+        default: 0
+      },
+      /**
+       * 鏄惁鏄剧ず杩涘害鏁板�鎴栫姸鎬佸浘鏍�+       */
+      showInfo: {
+        type: Boolean,
+        default: true
+      },
+      /**
+       * 杩涘害鐘舵�锛屽彲閫夛細normal success exception 锛坅ctive浠呮敮鎸佺被鍨媗ine
+       */
+      status: {
+        validator: val => {
+          return ['normal', 'success', 'exception', 'active'].includes(val);
+        },
+        default: 'normal'
+      },
+      /**
+       * 鏉$嚎鐨勫搴�-50锛屼笌width鏈夊叧
+       */
+      strokeWidth: {
+        type: Number,
+        default: 6
+      },
+      /**
+       * 鏉$嚎鐨勯鑹诧紝娓愬彉鑹蹭粎鏀寔绫诲瀷line
+       */
+      strokeColor: {
+        type: String,
+        default: ''
+      },
+      /**
+       * 鏉$嚎涓ょ鐨勫舰鐘�鍙�锛�round', 'square'
+       */
+      strokeShape: {
+        validator: val => {
+          return ['round', 'square'].includes(val);
+        },
+        default: 'round'
+      },
+      /**
+       * 鍦嗗舰杩涘害鏉$敾甯冨搴�鏀寔绫诲瀷circle dashboard
+       */
+      width: {
+        type: Number,
+        default: 80
+      },
+      /**
+       * 鍦嗗舰杩涘害鏉$己鍙h搴︼紝鍙彇鍊�0 ~ 360,鏀寔绫诲瀷circle dashboard
+       */
+      gapDegree: {
+        type: Number,
+        default: 0
+      },
+      /**
+       * 鍦嗗舰杩涘害鏉$己鍙d綅缃�鍙彇鍊�top', 'bottom', 'left', 'right' ,鏀寔绫诲瀷circle dashboard
+       */
+      gapPosition: {
+        validator: val => {
+          return ['top', 'bottom', 'left', 'right'].includes(val);
+        },
+        default: 'top'
+      }
+    },
+
+    computed: {
+      /**
+       * 濡傛灉闇�鑷畾涔夋牸寮忓氨鍦ㄨ繖鏀�+       */
+      setFormat() {
+        return `${this.setProgress}%`;
+      },
+      /**
+       * 璁剧疆鏄剧ず杩涘害鍊硷紝绂佹灏忎簬0鍜岃秴杩�00
+       */
+      setProgress() {
+        let percent = this.percent;
+        if (!this.percent || this.percent < 0) {
+          percent = 0;
+        } else if (this.percent >= 100) {
+          percent = 100;
+        }
+        return percent;
+      },
+      /**
+       * 杩涘害鍦坰vg澶у皬
+       */
+      setCircleStyle() {
+        return `width: ${this.width}px;
+				height: ${this.width}px;
+				fontSize: ${this.width * 0.15 + 6}px;`
+      },
+      /**
+       * 鍦堝簳鑹�+       */
+      setCircleTrailStyle() {
+        const radius = 50 - this.strokeWidth / 2;
+        const len = Math.PI * 2 * radius;
+        const gapDeg = this.gapDegree || (this.type === 'dashboard' && 75);
+        return `stroke-dasharray: ${len - (gapDeg||0)}px, ${len}px;
+				stroke-dashoffset: -${(gapDeg||0) / 2}px;
+				transition: stroke-dashoffset 0.3s ease 0s, stroke-dasharray 0.3s ease 0s, stroke 0.3s;`
+      },
+      /**
+       * 鍦堣繘搴�+       */
+      setCirclePathStyle() {
+        const radius = 50 - this.strokeWidth / 2;
+        const len = Math.PI * 2 * radius;
+        const gapDeg = this.gapDegree || (this.type === 'dashboard' && 75);
+        return `stroke: ${this.strokeColor};
+				stroke-dasharray: ${(this.setProgress / 100) * (len - (gapDeg||0))}px, ${len}px;
+				stroke-dashoffset: -${(gapDeg||0) / 2}px;
+				transition: stroke-dashoffset 0.3s ease 0s, stroke-dasharray 0.3s ease 0s, stroke 0.3s, stroke-width 0.06s ease 0.3s;`
+      },
+      /**
+       * 缁樺埗鍦�+       */
+      setCirclePath() {
+        const radius = 50 - this.strokeWidth / 2;
+        let beginPositionX = 0;
+        let beginPositionY = -radius;
+        let endPositionX = 0;
+        let endPositionY = -2 * radius;
+        const gapPos = (this.type === 'dashboard' && 'bottom') || this.gapPosition || 'top';
+        switch (gapPos) {
+          case 'left':
+            beginPositionX = -radius;
+            beginPositionY = 0;
+            endPositionX = 2 * radius;
+            endPositionY = 0;
+            break;
+          case 'right':
+            beginPositionX = radius;
+            beginPositionY = 0;
+            endPositionX = -2 * radius;
+            endPositionY = 0;
+            break;
+          case 'bottom':
+            beginPositionY = radius;
+            endPositionY = 2 * radius;
+            break;
+          default:
+            break;
+        }
+        return `M 50,50 m ${beginPositionX},${beginPositionY} a ${radius},${radius} 0 1 1 ${endPositionX},${-endPositionY} a ${radius},${radius} 0 1 1 ${-endPositionX},${endPositionY}`;
+      },
+      // #ifndef H5
+      /**
+       * 闈濰5绔紝缁樺埗杩涘害鍦坰vg杞琤ase URL
+       */
+      setCircle() {
+        const radius = 50 - this.strokeWidth / 2;
+        const len = Math.PI * 2 * radius;
+        const gapDeg = this.gapDegree || (this.type === 'dashboard' && 75);
+        let currentColor = '#108ee9'
+        // 寮傚父杩涘害
+        if (this.status == 'exception') {
+          currentColor = '#f5222d'
+        }
+        // 瀹屾垚杩涘害
+        if (this.status == 'success' || this.setProgress >= 100 || this.strokeColor) {
+          currentColor = this.strokeColor || '#52c41a'
+        }
+        let svgToBase =
+          `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100' class='cmd-progress-circle'%3E%3Cpath d='${this.setCirclePath}' stroke='%23f3f3f3' stroke-linecap='${this.strokeShape}' stroke-width='${this.strokeWidth}' fill-opacity='0' class='cmd-progress-circle-trail' style='stroke-dasharray: ${len - (gapDeg||0)}px, ${len}px;stroke-dashoffset: -${(gapDeg||0) / 2}px;transition: stroke-dashoffset 0.3s ease 0s, stroke-dasharray 0.3s ease 0s, stroke 0.3s;'%3E%3C/path%3E%3Cpath  d='${this.setCirclePath}' stroke-linecap='${this.strokeShape}' stroke-width='${this.strokeWidth}' fill-opacity='0' class='cmd-progress-circle-path' style='stroke: ${escape(currentColor)};stroke-dasharray: ${(this.setProgress / 100) * (len - (gapDeg||0))}px, ${len}px;stroke-dashoffset: -${(gapDeg||0) / 2}px;transition: stroke-dashoffset 0.3s ease 0s, stroke-dasharray 0.3s ease 0s, stroke 0.3s, stroke-width 0.06s ease 0.3s;'%3E%3C/path%3E%3C/svg%3E`
+        return `background-image: url("${svgToBase}");
+				background-size: cover;
+				display: inline-block;
+				${this.setCircleStyle}`;
+      },
+      /**
+       * 璁剧疆杩涘害鍦堢姸鎬佸浘鏍�+       */
+      setCircleIcon() {
+        let currentColor = '#108ee9'
+        let svgToBase = ''
+        // 寮傚父杩涘害
+        if (this.status == 'exception') {
+          currentColor = '#f5222d'
+          svgToBase =
+            `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='64 64 896 896' data-icon='close' width='1em' height='1em' fill='${escape(currentColor)}' aria-hidden='true'%3E %3Cpath d='M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 0 0 203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z'%3E%3C/path%3E %3C/svg%3E`;
+        }
+        // 瀹屾垚杩涘害
+        if (this.status == 'success' || this.setProgress >= 100) {
+          currentColor = this.strokeColor || '#52c41a'
+          svgToBase =
+            `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='64 64 896 896' data-icon='check' width='1em' height='1em' fill='${escape(currentColor)}' aria-hidden='true'%3E %3Cpath d='M912 190h-69.9c-9.8 0-19.1 4.5-25.1 12.2L404.7 724.5 207 474a32 32 0 0 0-25.1-12.2H112c-6.7 0-10.4 7.7-6.3 12.9l273.9 347c12.8 16.2 37.4 16.2 50.3 0l488.4-618.9c4.1-5.1.4-12.8-6.3-12.8z'%3E%3C/path%3E %3C/svg%3E`;
+        }
+        return `background-image: url("${svgToBase}");
+				background-size: cover;
+				display: inline-block;
+				width: 1em;
+				height: 1em;`;
+      },
+      // #endif
+      /**
+       * 璁剧疆杩涘害鏉℃牱寮�+       */
+      setLineStyle() {
+        return `width: ${this.setProgress}%;
+				height: ${this.strokeWidth}px;
+				background: ${this.strokeColor};
+				border-radius: ${this.strokeShape === 'square' ? 0 : '100px'};`;
+      },
+      /**
+       * 璁剧疆宸插畬鎴愬垎娈佃繘搴�+       */
+      setLineSuccessStyle() {
+        let successPercent = this.successPercent;
+        if (!this.successPercent || this.successPercent < 0 || this.setProgress < this.successPercent) {
+          successPercent = 0;
+        } else if (this.successPercent >= 100) {
+          successPercent = 100;
+        }
+        return `width: ${successPercent}%;
+				height: ${this.strokeWidth}px;
+				border-radius: ${this.strokeShape === 'square' ? 0 : '100px'};`;
+      },
+      // #ifndef H5
+      /**
+       * 璁剧疆杩涘害鏉$姸鎬佸浘鏍�+       */
+      setLineStatusIcon() {
+        let currentColor = '#108ee9'
+        let svgToBase = ''
+        // 寮傚父杩涘害
+        if (this.status == 'exception') {
+          currentColor = '#f5222d'
+          svgToBase =
+            `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='64 64 896 896' data-icon='close-circle' width='1em' height='1em' fill='${escape(currentColor)}' aria-hidden='true'%3E %3Cpath d='M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 0 1-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z'%3E%3C/path%3E %3C/svg%3E`;
+        }
+        // 瀹屾垚杩涘害
+        if (this.status == 'success' || this.setProgress >= 100) {
+          currentColor = this.strokeColor || '#52c41a'
+          svgToBase =
+            `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='64 64 896 896' data-icon='check-circle' width='1em' height='1em' fill='${escape(currentColor)}' aria-hidden='true'%3E %3Cpath d='M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm193.5 301.7l-210.6 292a31.8 31.8 0 0 1-51.7 0L318.5 484.9c-3.8-5.3 0-12.7 6.5-12.7h46.9c10.2 0 19.9 4.9 25.9 13.3l71.2 98.8 157.2-218c6-8.3 15.6-13.3 25.9-13.3H699c6.5 0 10.3 7.4 6.5 12.7z'%3E%3C/path%3E %3C/svg%3E`;
+        }
+        return `background-image: url("${svgToBase}");
+				background-size: cover;
+				display: inline-block;
+				width: 1em;
+				height: 1em;`;
+      },
+      // #endif
+      /**
+       * 鐘舵�鏍峰紡
+       */
+      setStatusClass() {
+        let statusClass = [];
+        // 寮傚父杩涘害
+        if (this.status == 'exception') {
+          statusClass.push('cmd-progress-status-exception')
+        }
+        // 瀹屾垚杩涘害
+        if (this.status == 'success' || this.setProgress >= 100) {
+          statusClass.push('cmd-progress-status-success')
+        }
+        // 娲诲姩杩涘害鏉�+        if (this.status == 'active') {
+          statusClass.push('cmd-progress-status-active')
+        }
+        // 鏄惁鏄剧ず淇℃伅
+        if (this.showInfo) {
+          statusClass.push('cmd-progress-show-info')
+        }
+        // 杩涘害鏉$被鍨�+        if (this.type === 'line') {
+          statusClass.push('cmd-progress-line')
+        }
+        // 杩涘害鍦堛�浠〃鐩樼被鍨�+        if (this.type === 'circle' || this.type === 'dashboard') {
+          statusClass.push('cmd-progress-circle')
+        }
+        statusClass.push('cmd-progress-status-normal')
+        return statusClass;
+      }
+    }
+  }
+</script>
+
+<style>
+  .cmd-progress { 
+    box-sizing: border-box;
+    margin: 0;
+    padding: 0;
+    list-style: none;
+    display: inline-block;
+  }
+
+  .cmd-progress-line {
+    width: 100%;
+    font-size: 28upx;
+    position: relative;
+    display: flex;
+    flex-direction: row;
+    justify-content: center;
+    align-items: center;
+  }
+
+  .cmd-progress-outer {
+    display: inline-block;
+    width: 100%;
+    margin-right: 0;
+    padding-right: 0;
+  }
+
+  .cmd-progress-show-info .cmd-progress-outer {
+    flex: 1;
+  }
+
+  .cmd-progress-inner {
+    display: inline-block;
+    width: 100%;
+    background-color: #f5f5f5;
+    border-radius: 200upx;
+    vertical-align: middle;
+    position: relative;
+  }
+
+  .cmd-progress-circle-trail {
+    stroke: #f5f5f5;
+  }
+
+  .cmd-progress-circle-path {
+    stroke: #1890ff;
+    animation: appear 0.3s;
+  }
+
+  .cmd-progress-success-bg,
+  .cmd-progress-bg {
+    background-color: #1890ff;
+    transition: all 0.4s cubic-bezier(0.08, 0.82, 0.17, 1) 0s;
+    position: relative;
+  }
+
+  .cmd-progress-success-bg {
+    background-color: #52c41a;
+    position: absolute;
+    top: 0;
+    left: 0;
+  }
+
+  .cmd-progress-text {
+    word-break: normal;
+    width: 60upx;
+    text-align: left;
+    margin-left: 16upx;
+    vertical-align: middle;
+    display: inline-block;
+    white-space: nowrap;
+    color: rgba(0, 0, 0, 0.45);
+    line-height: 1;
+  }
+
+  .cmd-progress-status-active .cmd-progress-bg:before {
+    content: "";
+    opacity: 0;
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    background: #fff;
+    border-radius: 20upx;
+    -webkit-animation: cmd-progress-active 2.4s cubic-bezier(0.23, 1, 0.32, 1) infinite;
+    animation: cmd-progress-active 2.4s cubic-bezier(0.23, 1, 0.32, 1) infinite;
+  }
+
+  .cmd-progress-status-exception .cmd-progress-bg {
+    background-color: #f5222d;
+  }
+
+  .cmd-progress-status-exception .cmd-progress-text {
+    color: #f5222d;
+  }
+
+  .cmd-progress-status-exception .cmd-progress-circle-path {
+    stroke: #f5222d;
+  }
+
+  .cmd-progress-status-success .cmd-progress-bg {
+    background-color: #52c41a;
+  }
+
+  .cmd-progress-status-success .cmd-progress-text {
+    color: #52c41a;
+  }
+
+  .cmd-progress-status-success .cmd-progress-circle-path {
+    stroke: #52c41a;
+  }
+
+  .cmd-progress-circle .cmd-progress-inner {
+    position: relative;
+    line-height: 1;
+    background-color: transparent;
+  }
+
+  .cmd-progress-circle .cmd-progress-text {
+    display: block;
+    position: absolute;
+    width: 100%;
+    text-align: center;
+    line-height: 1;
+    top: 50%;
+    -webkit-transform: translateY(-50%);
+    transform: translateY(-50%);
+    left: 0;
+    margin: 0;
+    color: rgba(0, 0, 0, 0.65);
+    white-space: normal;
+  }
+
+  .cmd-progress-circle .cmd-progress-status-exception .cmd-progress-text {
+    color: #f5222d;
+  }
+
+  .cmd-progress-circle .cmd-progress-status-success .cmd-progress-text {
+    color: #52c41a;
+  }
+
+  @keyframes cmd-progress-active {
+    0% {
+      opacity: 0.1;
+      width: 0;
+    }
+
+    20% {
+      opacity: 0.5;
+      width: 0;
+    }
+
+    100% {
+      opacity: 0;
+      width: 100%;
+    }
+  }
+</style>
diff --git a/fonts/fonticon/fonticon.css b/fonts/fonticon/fonticon.css
index df353f6..ad78f9d 100644
--- a/fonts/fonticon/fonticon.css
+++ b/fonts/fonticon/fonticon.css
@@ -151,4 +151,7 @@
 
 .expand:before {
 	content: "\ea23";
+}
+.dimensions:before {
+	content: "\ea24";
 }
\ No newline at end of file
diff --git a/fonts/fonticon/fonts/fonticon.eot b/fonts/fonticon/fonts/fonticon.eot
index 7bfa445..c18d9d6 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 9c8b3b8..b7c946a 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 0833553..389dccc 100644
--- a/fonts/fonticon/fonts/fonticon.woff
+++ b/fonts/fonticon/fonts/fonticon.woff
Binary files differ
diff --git a/locale/en.json b/locale/en.json
new file mode 100644
index 0000000..2533f90
--- /dev/null
+++ b/locale/en.json
@@ -0,0 +1,474 @@
+{
+
+	"page.comma": ",",
+	"page.question_mark": "?",
+	"page.exclamation_mark": "!",
+	"page.full_stop": ".",
+	"page.colon": ":",
+	"page.quotation_mark_left": " '",
+	"page.quotation_mark_right": "' ",
+	"page.double_quotation_mark_left": " \"",
+	"page.double_quotation_mark_right": "\" ",
+	"page.confirm": "Confirm",
+	"page.close": "Close",
+	"page.ok": "OK",
+	"page.cancel": "Cancel",
+	"page.save": "Save",
+	"page.yes": "Yes",
+	"page.no": "No",
+	"page.add": "Add",
+	"page.add2": "Add",
+	"page.new": "New",
+	"page.modify": "Modify",
+	"page.edit": "Edit",
+	"page.update": "Update",
+	"page.discarded": "Discarded",
+	"page.delete": "Delete",
+	"page.remove": "Remove",
+	"page.create": "Create",
+	"page.browse": "Browse",
+	"page.import": "Import",
+	"page.export": "Export",
+	"page.disabled": "Disabled",
+	"page.enabled": "Enabled",
+	"page.not_enabled": "Not Enabled",
+	"page.execute": "Execute",
+	"page.query": "Query",
+	"page.clear": "Clear",
+	"page.set": "Set",
+	"page.config": "Config",
+	"page.reset": "Reset",
+	"page.susccess": "Success",
+	"page.fail": "Fail",
+	"page.tip": "Tip",
+	"page.error": "Error",
+	"page.warning": "Warning",
+	"page.inquire": "Inquire",
+	"page.login_name": "Login name",
+	"page.account": "Account",
+	"page.user_name": "Username",
+	"page.password": "Password",
+	"page.name": "Name",
+	"page.input_login": "Please enter your account",
+	"page.input_password": "Please input your password",
+	"page.remember_password": "Remember password",
+	"page.login": "Login",
+	"page.start_login": "Starting login",
+	"page.language": "Language",
+	"page.version": "Version",
+	"page.loading": "loading...",
+	"page.refresh": "Refresh",
+	"page.home_page": "home page",
+	"page.coding_disk": "Coding disk",
+	"page.inventory": "Inventory",
+	"page.sorting": "Sorting",
+	"page.view": "View",
+	"page.unrealized": "Unrealized",
+	"page.select": "Select",
+	"page.please": "please",
+	"page.port": "Port",
+
+	"page.account_not_exist": "Account does not exist",
+	"page.password_error": "Incorrect password",
+
+	"page.parameter": "Parameter",
+	"page.result": "Result",
+	"page.my": "My",
+	"page.share": "Share",
+	"page.backup": "Backup",
+	"page.tutorial": "Tutorial",
+	"page.rename": "Rename",
+	"page.coordinates": "Coordinates",
+	"page.orientation": "Orientation",
+	"page.angle": "Angle",
+	"page.state": "State",
+	"page.idle": "Idle",
+	"page.at_work": "Working",
+	"page.fault": "Fault",
+	"page.retry": "Retry",
+	"page.model": "Model",
+	"page.unknown": "Unknown",
+	"page.wait": "Wait",
+	"page.second": "Second",
+	"page.minute": "min",
+
+	"page.properties": "properties",
+	"page.select_all": "Select All",
+
+	"page.phone_number": "Phone number",
+	"page.input_phone_number": "Enter phone number",
+
+
+	"page.exit_login": "Log out",
+	"page.registered_user": "Registered user",
+	"page.forgot_password": "Forgot password",
+
+	"page.tab_vehicle": "Vehicle",
+	"page.tab_my": "My",
+	"page.multilingual_settings": "Multilingual settings",
+	"page.app_version": "App version",
+	"page.language_switch": "Language switch",
+	"page.clear_cache": "Clear cache",
+	"page.help_and_feedback": "Help & feedback",
+	"page.clear_cache_success": "Cache cleared successfully",
+	"page.remote_assistance": "Remote assistance",
+	"page.log_upload": "Log upload",
+	"page.check_the_manual": "View manual",
+	"page.interface_log": "Interface log",
+	"page.record_interface_log": "Record interface log",
+	"page.video_instruction": "Video instruction",
+	"page.input_interface_name": "Enter interface name",
+
+	"page.error_code": "Error code",
+	"page.error_msg": "Error message",
+	"page.version_update": "Version update",
+	"page.check_update": "Check for updates",
+	"page.updatable_version": "Updatable version",
+	"page.version_info": "Version Description",
+	"page.current_version": "Current version",
+	"page.is_latest_version": "Already the latest version",
+	"page.update_immediately": "Update now",
+	"page.download_immediately": "Download Now",
+	"page.failed_vehicle_connection": "Unable to connect to device",
+	"page.check_network_or_reload": "Please check your connection or retry.",
+	"page.obtain_positon_and_orientation_of_vehicle": "Device position and orientation",
+
+	"page.station_name": "Station name",
+	"page.offline": "Offline",
+	"page.scene_construction": "Scene construction",
+	"page.scene_construction_in_progress": "Scene constructing",
+
+	"page.scenes": "Scenes",
+	"page.tasks": "Tasks",
+	"page.task_history": "Task history",
+	"page.connecting": "Connecting",
+	"page.connection_success": "Connection successful",
+	"page.connection_failed": "Connection failed",
+	"page.connection_preparation": "Connect the device",
+	"page.unable_connect_device": "Unable to connect device",
+	"page.connect_device": "Connect the device",
+
+
+	"page.vehicle_offline_to_reconnect": "The device is offline, please reconnect.",
+	"page.reconnect": "Reconnect",
+	"page.scan_add_device": "Scan to add device",
+	"page.manual_add_device": "Manually add device",
+	"page.add_device": "Add device",
+	"page.input_vehicle_ip": "Please enter device IP address.",
+	"page.invalid_qr_code": "Invalid QR code",
+	"page.only_3_vehicles_to_added": "Max 3 vehicles allowed",
+	"page.vehicle_ip_has_added": "This IP already added",
+
+	"page.scan_device_code_to_connect": "Scan the QR code on the device to connect",
+
+	"page.ip_address": "IP address",
+	"page.invalid_ip": "Invalid IP",
+	"page.ask_delete_device": "Are you sure you want to remove the device?",
+	"page.check_vehicle_not_paired_other_terminals": "Please ensure the device is on and not connected with other cellphones.",
+	"page.connect_phone_to_this_ip_wifi": "Please connect your phone to a Wi-Fi network starting with \"{0}\"",
+
+	"page.after_connect_return_this": "After connected, please return to this app",
+
+	"page.reconnect_wifi": "Reconnect Wi-Fi",
+	"page.more": "More",
+	"page.switch_scan_connection": "Switch to scan QR code",
+	"page.go_connect": "Go to connect",
+	"page.vehicle_connection": "Vehicle connection",
+	"page.ask_join_wlan": "Join銆恵0}銆憈o Wi-Fi銆恵1}銆�",
+	"page.join": "Join",
+	"page.scene_backup": "Backup",
+	"page.no_scene_matching_criteria": "No scenes found.",
+	"page.select_scene_to_upload_mobile": "Select the scene to upload to cellphone.",
+	"page.upload_to_mobile": "Upload to cellphone",
+	"page.select_scene_to_download_vehicle": "Select the scene to download to device.",
+	"page.download_to_vehicle": "Download to device",
+	"page.upload_scenes": "Upload scenes",
+	"page.scene_list": "scene list",
+	"page.download_scenes": "Download scenes",
+	"page.scene_list_on_cellphone": "Scene list on cellphone",
+	"page.confirm_replace_scene": "Confirm replace scene?",
+	"page.uploading_will_replace_currently_uploaded_scene": "Uploading this scene will replace the currently uploaded scene.",
+	"page.downloading_will_replace_currently_downloaded_scene": "Downloading this scene will replace the currently downloaded scene.",
+	"page.ask_cover_scene": "Overwrite scene?",
+	"page.upload_success": "Uploaded successful",
+	"page.download_success": "Download successful",
+	"page.scene_uploading": "Uploading scene",
+	"page.scene_downloading": "Downloading scene",
+	"page.ask_exit_page": "Do you want to exit?",
+	"page.car_version": "Version",
+	"page.authorization_validity_period": "Auth validity",
+	"page.wifi_address": "WiFi address",
+	"page.remove_vehicle": "Remove vehicle",
+	"page.errors": "Errors",
+
+	"page.error_report_info": "Error info",
+	"page.current_errors": "Current errors",
+	"page.history_errors": "History errors",
+	"page.download_logs": "Download logs",
+	"page.download_log": "Download log",
+	"page.clear_errors": "Clear errors",
+	"page.log_time_range": "Log time range",
+	"page.start_time": "Start time",
+	"page.create_time": "Created",
+	"page.end_time": "End time",
+	"page.select_start_time": "Select start time",
+	"page.select_end_time": "Select end time",
+	"page.module_id": "Module ID",
+	"page.refresh_failed_retry": "Refresh failed, please retry.",
+	"page.current_no_error_messages": "No error reported.",
+	"page.download_logs_fail": "Download failed, please retry.",
+	"page.downloading_log": "Downloading log",
+	"page.package_download_logs": "Packaging logs",
+	"page.download_log_fail_not_get_filepath": "Download log failed, no log file information returned",
+	"page.log_downloaded_ask_redownloaded": "The log file has already been downloaded. Do you want to download it again?",
+	"page.clear_download_logs": "Clear local logs",
+	"page.email_send": "Send email",
+	"page.send": "Send",
+	"page.delete_scene": "Delete map",
+	"page.deleting_scene": "Deleting scene",
+	"page.pallet_size": "Pallet size",
+	"page.map": "Map",
+	"page.extend_map": "Extend map",
+	"page.regiona_plan": "Region plan",
+
+	"page.length": "Length",
+	"page.width": "Width",
+	"page.input_please": "Please enter",
+	"page.navigation": "Navigation",
+	"page.auto_unload": "Auto unload",
+
+	"page.failed_save_file": "Failed to save downloaded file",
+
+	"page.build_scene": "Build scene",
+
+	"page.input_scene_name": "Enter scene name",
+	"page.update_scene_name": "Rename scene",
+	"page.scene_name_cannot_empty": "Scene name required",
+	"page.scene_name_not_changed": "Name unchanged",
+	"page.update_scene_name_success": "Renamed",
+	"page.switching_scene": "Switching scene",
+	"page.delete_scene_will_be_deleted_map_and_tasks": "Deleting scene removes its map & tasks",
+	"page.ask_deleting_scene": "Delete scene?",
+	"page.scene_extend_completed": "Scene extension done",
+	"page.scene_construction_completed": "Scene construction done",
+
+
+
+	"page.add_station": "Add station",
+	"page.edit_station": "Edit station",
+	"page.creating_station": "Creating station",
+	"page.updating_station": "Updating station",
+	"page.deleting_station": "Deleting station",
+
+	"page.adjust_position_orientation": "Adjust position & orientation",
+	"page.adjust_position": "Adjust position",
+	"page.adjust_orientation": "Adjust orientation",
+
+
+	"page.x_axis": "x-axis",
+	"page.y_axis": "y-axis",
+
+	"page.input_station_name": "Enter station name",
+
+
+	"page.manual_plan": "Manual planning",
+
+	"page.vehicle_trajectory_plan": "Vehicle path plan",
+
+	"page.feasible_region": "Feasible region",
+	"page.prohibition_region": "Prohibited region",
+	"page.virtual_wall": "Virtual wall",
+	"page.endpoint_adjust_position_to_closed_shape": "Drag endpoints to adjust wall; tap + to add vertices; area must be closed",
+	"page.endpoint_adjust_virtual_wall_position": "Drag endpoints to adjust virtual wall",
+
+	"page.regional_planning": "Regional planning in progress",
+	"page.ask_exit_regional_planning": "Exit regional planning?",
+
+	"page.task_set": "Tasks",
+	"page.set_task": "Set task",
+
+	"page.edit_map": "Edit map",
+	"page.path_teaching": "Teaching",
+	"page.in_path_record": "Recording path",
+	"page.default": "Default",
+	"page.intelligent": "Auto",
+	"page.bidirectional": "Two-way",
+	"page.unidirectional": "Unidirectional",
+	"page.main_road": "Main road",
+	"page.branch_road": "Branch road",
+
+
+	"page.any_time_switch_teaching_modes": "Switch teaching mode anytime",
+	"page.recording_path_of_vehicle_can_add_station": "Recording vehicle path; add stations during teaching",
+
+
+	"page.start_teaching": "Start teaching",
+	"page.end_teaching": "Finish teaching",
+	"page.re_record": "Re-record",
+	"page.path_recording_completed": "Path recorded",
+	"page.recording_path_of_vehicle_click_btn_to_finish": "Recording path; click button to finish teaching",
+	"page.ask_save_this_path": "Save this path as teaching path?",
+	"page.save_as_teaching_path": "Save as teaching path",
+	"page.teaching_completed": "Teaching completed",
+	"page.path_saved_as_teaching_path": "Path saved as teaching path",
+	"page.complete": "Complete",
+	"page.public_teaching_to_begin": "Teaching starting...",
+	"page.public_teaching_start_tip_select_teaching_mode": "Please select a teaching mode and press "
+	Start teaching ", the moving route of the device will be recorded as the teaching route. You can switch teaching modes at any time during teaching.",
+
+	"page.batch_delete": "Batch delete",
+	"page.teaching_update": "Update teaching",
+	"page.teaching_updating": "Updating teaching",
+	"page.teaching_update_success": "Teaching updated",
+	"page.teaching_deleting": "Deleting teaching",
+	"page.teaching_delete_success": "Teaching deleted",
+	"page.teaching_data_deleting": "Deleting teaching data",
+	"page.ask_deleting_teaching": "Confirm deleting path?",
+	"page.deleted_teaching_cannot_recovered": "Once deleted, it cannot be recovered.",
+	"page.ask_exit_teaching": "Confirm exit teaching?",
+
+	"page.recorded_path_will_be_deleted": "Recorded paths will be deleted.",
+	"page.teaching_end": "Teaching ended",
+	"page.teaching_start": "Teaching started",
+	"page.ask_re_recording": "Re-record?",
+	"page.edit_path": "Edit path",
+	"page.delete_path": "Delete path",
+
+	"page.teaching_switched_bidirectional_mode": "Switched to tow-way mode",
+	"page.teaching_switched_intelligent_mode": "Switched to auto mode",
+	"page.teaching_switched_default_mode": "Switched to default mode",
+	"page.Hold_adjust_position_be_closed_shape_to_delete": "Hold down each vertex to adjust its position. The area must be a closed shape. Press OK button to delete the path within the box.",
+
+
+	"page.start_extending": "Start extension",
+	"page.start_building": "Start building",
+	"page.no_map_found": "No matching map found",
+	"page.scan_completed": "Scan completed",
+	"page.extend_completed": "Extension completed",
+	"page.construction_completed": "Construction completed",
+	"page.re_extend": "Re-extend",
+	"page.rebuild": "Rebuild",
+
+	"page.scene_construction_service_not_start": "Scene construction service: not started",
+	"page.scene_construction_service_starting": "Scene construction service: starting",
+	"page.scene_composition_saving": "Saving scene composition",
+	"page.scene_composition_save_success": "Scene composition saved",
+	"page.scene_composition_save_failed": "Scene composition save failed",
+	"page.ask_interrupt_scene_construction": "Re-record?",
+	"page.built_scene_will_be_deleted": "Built scene will be deleted",
+
+	"page.loading_base_map_failed": "Base map load failed",
+	"page.downloading_scene": "Downloading scene",
+	"page.download_scene_success": "Scene downloaded",
+	"page.start_scanning_map": "Start map scan",
+	"page.end_scanning_map": "Map scan ended",
+	"page.scene_construction_failed": "Scene construction failed",
+	"page.check_vehicle_connection_and_restart_building_scene": "Check vehicle connection and restart scene building",
+
+	"page.target_point": "Target",
+	"page.not_started": "Not started",
+	"page.mandatory_completion": "Force complete",
+	"page.abnormal_termination": "Abnormal end",
+	"page.canceled": "Canceled",
+	"page.completed": "Completed",
+	"page.in_progress": "In progress",
+
+	"page.task_completed": "Task completed",
+	"page.task_exception": "Task exception",
+	"page.task_properties": "properties",
+
+	"page.input_task_name": "Enter name",
+	"page.select_properties": "Select properties",
+	"page.fixed": "Fixed",
+	"page.temporary": "Temporary",
+	"page.number_repetitions": "Repetitions",
+	"page.button_number": "Button No.",
+	"page.task_route": "Path info",
+	"page.wait_time": "Wait time",
+
+	"page.add_target_point": "Add target",
+
+	"page.new_task": "New task",
+	"page.update_task": "Update task",
+	"page.select_task_properties": "Select properties",
+	"page.select_button_number": "Select button No.",
+
+	"page.adding_task": "Adding task",
+	"page.updating_task": "Updating task",
+	"page.target_point_no_operation_type": "Target point operation type not selected",
+	"page.at_least_one_target_point_added": "At least one target point required",
+	"page.ask_exit_task_edit": "Confirm exit editing?",
+	"page.current_edited_content_will_be_deleted": "The currently edited content will be deleted.",
+	"page.operation": "Operation",
+	"page.task_action": "Action",
+
+	"page.cumulative_duration": "Cumulative duration",
+	"page.accumulated_mileage": "Accumulated mileage",
+	"page.cumulative_count": "Cumulative count",
+	"page.fixed_task": "Fixed task",
+	"page.temporary_task": "Temporary task",
+	"page.not_task_found": "No task found",
+	"page.task_terminated_success": "Task terminated",
+	"page.skip_sub_task_success": "Sub-task skipped",
+	"page.no_sub_task_found_not_started_or_executing": "No not-started or running sub-task found",
+	"page.history_task_will_be_retained": "	The history of this task will be retained.",
+	"page.no_task_history_found": "No task history found",
+	"page.downloading": "Downloading",
+	"page.refresh_success": "Refresh successful",
+	"page.station_list": "Station list",
+	"page.delete_station": "Remove station",
+	"page.delete_selected_station": "This station has been linked to tasks. Removing the station will stop and delete the linked tasks.",
+	"page.ask_confirm_delete": "Confirm detete?",
+	"page.ask_confirm_remove": "Confirm remove?",
+	"page.delete_success": "Delete successfully",
+	"page.delete_station_success": "Station deleted successfully",
+	"page.no_selected_stations": "No station selected",
+	"page.modify_success_restart_device": "Modified successfully, please restart the vehicle",
+	"page.input_content_only_numbers_or_signs": "The input can only contain numbers and signs. ",
+
+	"page.input_content_only_numbers_and_signs_and_decimal": "The input can only contain numbers,plus or minus signs, and decimal points.",
+	"page.repeat_once": "Repeat once",
+	"page.repeat_twice": "Repeat twice",
+	"page.repeat_times": "Repeat {0} times",
+	"page.time_number": "{0} out of {1}",
+	"page.the_st_suffix": "st",
+	"page.the_nd_suffix": "nd",
+	"page.the_rd_suffix": "rd",
+	"page.the_th_suffix": "th",
+
+	"page.email_service": "Email Service",
+	"page.email_port": "Email port",
+	"page.sender_email": "Sender Email",
+	"page.authorization_code": "Authorization Code",
+	"page.receiver_email": "Receiver Email",
+	"page.email_subject": "Email Subject",
+	"page.attachment": "Attachments",
+
+	"page.input_email_service": "Please enter email service",
+	"page.input_email_port": "Please enter port",
+	"page.input_sender_email": "Please enter sender email",
+	"page.input_authorization_code": "Please enter authorization code",
+	"page.input_receiver_email": "Please enter receiver email",
+	"page.input_email_subject": "Please enter Emai Subject",
+	"page.input_email_content": "Please enter Email Content",
+
+	"page.email_sending": "Email is being sent",
+	"page.email_send_success": "Email sent successfully",
+	"page.email_send_fail": "Email sending failed",
+	"page.error_log_package": "	Error log package",
+	"page.tip_app_update": "Discovering a new version of the app",
+	"page.tip_car_update": "Discovered in car version",
+	"page.ask_download": "Do I need to download it?",
+	"page.ask_update": "Do you need to update?",
+	"page.update_fail": "Update failed",
+	"page.update_success": "Update successful",
+	"page.saved_success": "Update successful",
+	"page.saved_fail": "Update failed",
+	"page.saving": "Saving",
+	"page.no_station": "There is no station yet",
+	"page.upload": "Upload",
+	"page.upload_fail": "Upload failed",
+		"page.upload_finish": "Upload completed"
+
+
+}
\ No newline at end of file
diff --git a/locale/index.js b/locale/index.js
new file mode 100644
index 0000000..ef05364
--- /dev/null
+++ b/locale/index.js
@@ -0,0 +1,23 @@
+import customZhCn from './zh-Hans.json'
+import customZhTw from './zh-Hant.json'
+import customEnUs from './en.json'
+// const messages = {
+//     'zh-CN': customZhCn,
+//     'zh-TW': customZhTw,
+//     'en-US': customEnUs,
+//     'es-ES':customESes
+// }
+export default {
+	'zh-Hans': customZhCn,
+	'zh-Hant': customZhTw,
+	'en': customEnUs,
+}
+
+// const i18n = new VueI18n({
+//   locale: 'en-US', // set default locale
+//   messages, // set locale messages
+// })
+
+// export const setup = (app) => {
+// 	// app.use(i18n)
+// }
\ No newline at end of file
diff --git a/locale/zh-Hans.json b/locale/zh-Hans.json
new file mode 100644
index 0000000..0c7acf8
--- /dev/null
+++ b/locale/zh-Hans.json
@@ -0,0 +1,487 @@
+{
+	"page.comma": "锛�,
+	"page.question_mark": "锛�,
+	"page.exclamation_mark": "锛�,
+	"page.full_stop": "銆�,
+	"page.colon": "锛�,
+	"page.quotation_mark_left": "鈥�,
+	"page.quotation_mark_right": "鈥�,
+	"page.double_quotation_mark_left": "鈥�,
+	"page.double_quotation_mark_right": "鈥�,
+	"page.confirm": "纭",
+	"page.close": "鍏抽棴",
+	"page.ok": "纭畾",
+	"page.cancel": "鍙栨秷",
+	"page.save": "淇濆瓨",
+	"page.yes": "鏄�,
+	"page.no": "鍚�,
+	"page.add": "娣诲姞",
+	"page.add2": "鏂板",
+	"page.new": "鏂板缓",
+	"page.modify": "淇敼",
+	"page.edit": "缂栬緫",
+	"page.update": "鏇存柊",
+	"page.discarded": "浣滃簾",
+	"page.delete": "鍒犻櫎",
+	"page.remove": "鍒犻櫎",
+	"page.create": "鍒涘缓",
+	"page.browse": "娴忚",
+	"page.import": "瀵煎叆",
+	"page.export": "瀵煎嚭",
+	"page.disabled": "绂佺敤",
+	"page.enabled": "鍚敤",
+	"page.not_enabled": "鏈惎鐢�,
+	"page.execute": "鎵ц",
+	"page.query": "鏌ヨ",
+	"page.clear": "娓呯┖",
+	"page.set": "璁剧疆",
+	"page.config": "閰嶇疆",
+	"page.reset": "閲嶇疆",
+	"page.susccess": "鎴愬姛",
+	"page.fail": "澶辫触",
+	"page.tip": "鎻愮ず",
+	"page.error": "閿欒",
+	"page.warning": "璀﹀憡",
+	"page.inquire": "璇㈤棶",
+	"page.login_name": "璐﹀彿",
+	"page.account": "璐﹀彿",
+	"page.user_name": "濮撳悕",
+	"page.password": "瀵嗙爜",
+	"page.name": "鍚嶇О",
+	"page.input_login": "璇疯緭鍏ヨ处鍙�,
+	"page.input_password": "璇疯緭鍏ュ瘑鐮�,
+	"page.remember_password": "璁颁綇瀵嗙爜",
+	"page.login": "鐧诲綍",
+	"page.start_login": "寮�鐧诲綍",
+	"page.language": "璇█",
+	"page.version": "鐗堟湰鍙�,
+	"page.loading": "鍔犺浇涓�..",
+	"page.refresh": "鍒锋柊",
+	"page.home_page": "棣栭〉",
+	"page.coding_disk": "鐮佺洏",
+	"page.inventory": "鐩樼偣",
+	"page.sorting": "鍒嗘嫞",
+	"page.view": "娴忚",
+	"page.unrealized": "鏈疄鐜�,
+	"page.select": "璇烽�鎷�,
+	"page.please": "璇�,
+	"page.port": "绔彛",
+
+	"page.parameter": "鍙傛暟",
+	"page.result": "缁撴灉",
+	"page.my": "鎴戠殑",
+	"page.share": "鍏变韩",
+	"page.backup": "澶囦唤",
+	"page.tutorial": "鏁欑▼",
+	"page.rename": "閲嶅懡鍚�,
+	"page.coordinates": "鍧愭爣",
+	"page.orientation": "鏈濆悜",
+	"page.angle": "瑙掑害",
+	"page.state": "鐘舵�",
+	"page.idle": "绌洪棽涓�,
+	"page.at_work": "宸ヤ綔涓�,
+	"page.fault": "鏁呴殰",
+	"page.retry": "閲嶈瘯",
+	"page.model": "鍨嬪彿",
+	"page.unknown": "鏈煡",
+	"page.wait": "绛夊緟",
+	"page.second": "绉�,
+	"page.minute": "鍒嗛挓",
+
+	"page.properties": "灞炴�",
+	"page.select_all": "鍏ㄩ�",
+
+
+	"page.account_not_exist": "璐﹀彿涓嶅瓨鍦�,
+	"page.password_error": "瀵嗙爜閿欒",
+
+	"page.phone_number": "鎵嬫満鍙�,
+	"page.input_phone_number": "杈撳叆鎵嬫満鍙�,
+
+
+
+	"page.exit_login": "閫�嚭鐧诲綍",
+	"page.registered_user": "娉ㄥ唽鐢ㄦ埛",
+	"page.forgot_password": "蹇樿瀵嗙爜",
+
+	"page.tab_vehicle": "杞﹁締",
+	"page.tab_my": "鎴戠殑",
+	"page.multilingual_settings": "澶氳瑷�缃�,
+
+
+	"page.app_version": "App鐗堟湰",
+	"page.language_switch": "璇█鍒囨崲",
+	"page.clear_cache": "娓呴櫎缂撳瓨",
+	"page.help_and_feedback": "甯姪涓庡弽棣�,
+	"page.clear_cache_success": "娓呴櫎缂撳瓨鎴愬姛",
+	"page.remote_assistance": "杩滅▼鍗忓姪",
+	"page.log_upload": "鏃ュ織涓婁紶",
+	"page.check_the_manual": "鏌ョ湅璇存槑涔�,
+	"page.interface_log": "鎺ュ彛鏃ュ織",
+	"page.record_interface_log": "璁板綍鎺ュ彛鏃ュ織",
+	"page.video_instruction": "瑙嗛璇存槑",
+	"page.input_interface_name": "璇疯緭鍏ユ帴鍙e悕绉�,
+
+	"page.error_code": "閿欒鐮�,
+	"page.error_msg": "閿欒淇℃伅",
+	"page.version_update": "鐗堟湰鏇存柊",
+	"page.check_update": "妫�煡鏇存柊",
+	"page.updatable_version": "鍙洿鏂扮増鏈�,
+	"page.version_info": "鐗堟湰璇存槑",
+	"page.current_version": "褰撳墠鐗堟湰涓�,
+	"page.is_latest_version": "褰撳墠涓烘渶鏂扮増鏈�,
+	"page.update_immediately": "绔嬪嵆鏇存柊",
+	"page.download_immediately": "绔嬪嵆涓嬭浇",
+	"page.failed_vehicle_connection": "杞﹁締杩炴帴澶辫触",
+	"page.check_network_or_reload": "璇锋鏌ヤ綘鐨勭綉缁滆缃垨閲嶆柊鍔犺浇",
+	"page.obtain_positon_and_orientation_of_vehicle": "鑾峰彇鎼繍杞︿綅缃拰鏈濆悜",
+
+	"page.station_name": "绔欑偣鍚嶇О",
+	"page.offline": "宸茬绾�,
+	"page.scene_construction": "鍦烘櫙鏋勫缓",
+	"page.scene_construction_in_progress": "鍦烘櫙鏋勫缓涓�,
+
+	"page.scenes": "鍦烘櫙鏋勫缓",
+	"page.tasks": "浠诲姟璁板綍",
+	"page.task_history": "浠诲姟璁板綍",
+	"page.connecting": "杩炴帴涓�,
+	"page.connection_success": "杩炴帴鎴愬姛",
+	"page.connection_failed": "杩炴帴澶辫触",
+	"page.connection_preparation": "杩炴帴鍑嗗",
+	"page.unable_connect_device": "鏃犳硶杩炴帴璁惧",
+	"page.connect_device": "杩炴帴璁惧",
+
+
+	"page.vehicle_offline_to_reconnect": "杞﹁締宸茬绾匡紝璇烽噸鏂拌繛鎺�,
+	"page.reconnect": "閲嶆柊杩炴帴",
+	"page.scan_add_device": "鎵弿娣诲姞璁惧",
+	"page.manual_add_device": "鎵嬪姩娣诲姞璁惧",
+	"page.add_device": "娣诲姞璁惧",
+	"page.input_vehicle_ip": "璇疯緭鍏ヨ溅杈咺P",
+	"page.invalid_qr_code": "鏃犳晥鐨勪簩缁寸爜",
+	"page.only_3_vehicles_to_added": " 鏈�鍙厑璁告坊鍔�杈嗚溅",
+	"page.vehicle_ip_has_added": "璇P鐨勮澶囧凡娣诲姞",
+
+	"page.scan_device_code_to_connect": "灏嗘満韬簩缁寸爜鏀惧叆妗嗗唴浠ヨ繛鎺ヨ澶�,
+
+	"page.ip_address": "IP鍦板潃",
+	"page.invalid_ip": "IP鏃犳晥",
+
+	"page.ask_delete_device": "纭畾瑕佸垹闄よ璁惧鍚楋紵",
+	"page.check_vehicle_not_paired_other_terminals": "璇风‘淇濊溅杈嗗紑鏈轰笖鏈笌鍏朵粬缁堢閰嶅",
+	"page.connect_phone_to_this_ip_wifi": "璇峰皢鎵嬫満杩炴帴鍒扳�{0}鈥濆紑澶寸殑WiFi缃戠粶",
+	"page.after_connect_return_this": "杩炴帴鍚庡洖鍒版湰搴旂敤",
+
+	"page.reconnect_wifi": "閲嶆柊杩炴帴WiFi",
+	"page.more": "瀛︿範鏇村",
+	"page.switch_scan_connection": "鍒囨崲鎵爜杩炴帴",
+	"page.go_connect": "鍘昏繛鎺�,
+	"page.vehicle_connection": "杞﹁締杩炴帴",
+	"page.ask_join_wlan": "銆恵0}銆戞兂鍔犲叆鏃犵嚎灞�煙缃戙�{1}銆戝悧锛�,
+	"page.join": "Join",
+	"page.scene_backup": "鍦烘櫙澶囦唤",
+	"page.no_scene_matching_criteria": "娌℃湁鎵惧埌绗﹀悎鏉′欢鐨勫満鏅�,
+	"page.select_scene_to_upload_mobile": "璇烽�鎷╀笂浼犲埌鎵嬫満绔殑鍦烘櫙",
+	"page.upload_to_mobile": "涓婁紶鑷虫墜鏈虹",
+	"page.select_scene_to_download_vehicle": "璇烽�鎷╀笅杞藉埌杞﹁締绔殑鍦烘櫙",
+	"page.download_to_vehicle": "涓嬭浇鑷宠溅杈嗙",
+	"page.upload_scenes": "涓婁紶鍦烘櫙",
+	"page.scene_list": "鍦烘櫙鍒楄〃",
+	"page.download_scenes": "涓嬭浇鍦烘櫙",
+	"page.scene_list_on_cellphone": "鎵嬫満绔繚瀛樼殑鍦烘櫙鍒楄〃",
+	"page.confirm_replace_scene": "纭鏇挎崲鍦烘櫙?",
+	"page.uploading_will_replace_currently_uploaded_scene": "涓婁紶璇ュ満鏅細鏇挎崲鎺夊綋鍓嶅凡涓婁紶鐨勫満鏅�",
+	"page.downloading_will_replace_currently_downloaded_scene": "涓嬭浇璇ュ満鏅細鏇挎崲鎺夊凡涓嬭浇鐨勫悓鍚嶅満鏅�",
+	"page.ask_cover_scene": "鏄惁瑕佽鐩栧満鏅�,
+	"page.upload_success": "涓婁紶鎴愬姛",
+	"page.download_success": "涓嬭浇鎴愬姛",
+	"page.scene_uploading": "姝e湪涓婁紶鍦烘櫙",
+	"page.scene_downloading": "姝e湪涓嬭浇鍦烘櫙",
+	"page.ask_exit_page": "鏄惁瑕侀�鍑猴紵",
+
+
+	"page.car_version": "杞﹁浇鐗堟湰",
+	"page.authorization_validity_period": "鎺堟潈鏈夋晥鏈�,
+	"page.wifi_address": "鏃犵嚎灞�煙缃戝湴鍧�,
+	"page.remove_vehicle": "鍒犻櫎杞﹁締",
+	"page.errors": "鎶ラ敊",
+
+	"page.error_report_info": "鎶ラ敊淇℃伅",
+	"page.current_errors": "瀹炴椂閿欒",
+	"page.history_errors": "鍘嗗彶閿欒",
+	"page.download_logs": "涓嬭浇鏃ュ織",
+	"page.download_log": "涓嬭浇鏃ュ織",
+
+
+	"page.clear_errors": "娓呴敊",
+	"page.log_time_range": "鏃ュ織鏃堕棿鑼冨洿",
+	"page.start_time": "寮�鏃堕棿",
+	"page.create_time": "鍒涘缓鏃堕棿",
+	"page.end_time": "缁撴潫鏃堕棿",
+	"page.select_start_time": "閫夋嫨寮�鏃堕棿",
+	"page.select_end_time": "閫夋嫨缁撴潫鏃堕棿",
+	"page.module_id": "妯″潡id",
+	"page.refresh_failed_retry": "鍒锋柊澶辫触锛岃閲嶈瘯",
+	"page.current_no_error_messages": "褰撳墠鏃犳姤閿欎俊鎭�,
+	"page.download_logs_fail": "涓嬭浇鏃ュ織澶辫触",
+	"page.downloading_log": "涓嬭浇鏃ュ織涓�,
+	"page.package_download_logs": "鎵撳寘涓嬭浇鏃ュ織涓�,
+	"page.download_log_fail_not_get_filepath": "涓嬭浇鏃ュ織澶辫触锛屾湭杩斿洖鏃ュ織鏂囦欢淇℃伅",
+	"page.log_downloaded_ask_redownloaded": "鏃ュ織鏂囦欢宸茬粡涓嬭浇锛屾槸鍚﹂噸鏂颁笅杞斤紵",
+	"page.clear_download_logs": "娓呯┖鏈湴鏃ュ織",
+	"page.email_send": "閭欢鍙戦�",
+	"page.send": "鍙戦�",
+	"page.delete_scene": "鍒犻櫎鍦烘櫙",
+	"page.deleting_scene": "姝e湪鍒犻櫎鍦烘櫙",
+	"page.pallet_size": "杞藉叿灏哄",
+	"page.map": "鍦板浘",
+	"page.extend_map": "鎵╁睍鍦板浘",
+	"page.regiona_plan": "鍖哄煙瑙勫垝",
+
+	"page.length": "闀垮害",
+	"page.width": "瀹藉害",
+	"page.input_please": "璇疯緭鍏�,
+	"page.navigation": "瀵艰埅",
+	"page.auto_unload": "鍗歌揣",
+
+	"page.failed_save_file": "淇濆瓨涓嬭浇鏂囦欢澶辫触",
+
+	"page.build_scene": "鏋勫缓鍦烘櫙",
+
+	"page.input_scene_name": "璇疯緭鍏ュ満鏅悕绉�,
+	"page.update_scene_name": "淇敼鍦烘櫙鍚嶇О",
+	"page.scene_name_cannot_empty": "鍦烘櫙鍚嶇О涓嶈兘涓虹┖",
+	"page.scene_name_not_changed": "鍦烘櫙鍚嶇О鏈彉鍖�,
+	"page.update_scene_name_success": "淇敼鍦烘櫙鍚嶇О鎴愬姛",
+	"page.switching_scene": "姝e湪鍒囨崲鍦烘櫙",
+	"page.delete_scene_will_be_deleted_map_and_tasks": "鍒犻櫎鍦烘櫙浼氭妸鍦烘櫙瀵瑰簲鐨勫湴鍥句换鍔′俊鎭兘浼氬垹闄�,
+	"page.ask_deleting_scene": "纭鍒犻櫎鍦烘櫙锛�,
+	"page.scene_extend_completed": "鍦烘櫙鎵╁睍缁撴潫",
+	"page.scene_construction_completed": "鍦烘櫙鏋勫缓缁撴潫",
+
+
+
+	"page.add_station": "娣诲姞绔欑偣",
+	"page.edit_station": "缂栬緫绔欑偣",
+	"page.creating_station": "姝e湪鏂板缓绔欑偣",
+	"page.updating_station": "姝e湪鏇存柊绔欑偣",
+	"page.deleting_station": "姝e湪鍒犻櫎绔欑偣",
+
+	"page.adjust_position_orientation": "璋冩暣浣嶇疆鏈濆悜",
+	"page.adjust_position": "璋冭妭浣嶇疆",
+	"page.adjust_orientation": "璋冭妭鏈濆悜",
+
+
+	"page.x_axis": "妯潗鏍�,
+	"page.y_axis": "绔栧潗鏍�,
+
+	"page.input_station_name": "璇疯緭鍏ョ珯鐐瑰悕绉�,
+
+
+	"page.manual_plan": "浜哄伐瑙勫垝",
+
+	"page.vehicle_trajectory_plan": "杞﹁締杞ㄨ抗瑙勫垝",
+
+	"page.feasible_region": "鍙鍖�,
+	"page.prohibition_region": "绂佽鍖�,
+	"page.virtual_wall": "铏氭嫙澧�,
+	"page.endpoint_adjust_position_to_closed_shape": "鍙寜浣忓悇绔偣璋冩暣铏氭嫙澧欎綅缃紝鎸夈�+銆戞寜閽彲娣诲姞椤剁偣锛屽尯鍩熼渶涓洪棴鍚堝浘褰�,
+	"page.endpoint_adjust_virtual_wall_position": "鍙寜浣忎袱绔偣璋冩暣铏氭嫙澧欎綅缃�,
+
+	"page.regional_planning": "姝e湪杩涜鍖哄煙瑙勫垝",
+	"page.ask_exit_regional_planning": "鏄惁瑕侀�鍑哄尯鍩熻鍒掞紵",
+
+	"page.task_set": "浠诲姟璁剧疆",
+	"page.set_task": "璁剧疆浠诲姟",
+
+	"page.edit_map": "缂栬緫鍦板浘",
+	"page.path_teaching": "璺緞绀烘暀",
+	"page.in_path_record": "璺緞璁板綍涓�,
+	"page.default": "榛樿",
+	"page.intelligent": "鏅鸿兘",
+	"page.bidirectional": "鍙屽悜",
+	"page.unidirectional": "鍗曞悜",
+	"page.main_road": "涓昏矾",
+	"page.branch_road": "鏀矾",
+
+
+	"page.any_time_switch_teaching_modes": "鍙殢鏃跺垏鎹㈢ず鏁欐ā寮�,
+	"page.recording_path_of_vehicle_can_add_station": "姝e湪璁板綍鎼繍杞﹁杩涜矾寰勶紝瀹屾垚鍚庣偣鎸夐挳浠ョ粨鏉熺ず鏁欍�鍦ㄧず鏁欒繃绋嬩腑鍙互娣诲姞绔欑偣銆�,
+
+
+	"page.start_teaching": "寮�绀烘暀",
+	"page.end_teaching": "缁撴潫绀烘暀",
+	"page.re_record": "閲嶆柊绀烘暀",
+	"page.path_recording_completed": "璺緞璁板綍瀹屾垚",
+	"page.recording_path_of_vehicle_click_btn_to_finish": "姝e湪璁板綍鎼繍杞﹁杩涜矾寰勶紝瀹屾垚鍚庣偣鎸夐挳浠ョ粨鏉熺ず鏁�,
+	"page.ask_save_this_path": "瑕佸皢璇ユ璺緞淇濆瓨涓虹ず鏁欒矾寰勫悧锛�,
+	"page.save_as_teaching_path": "淇濆瓨涓虹ず鏁欒矾寰�,
+	"page.teaching_completed": "绀烘暀瀹屾垚",
+	"page.path_saved_as_teaching_path": "宸插皢璺緞淇濆瓨涓虹ず鏁欒矾寰�,
+	"page.complete": "瀹屾垚",
+	"page.public_teaching_to_begin": "鍗冲皢寮�绀烘暀",
+	"page.public_teaching_start_tip_select_teaching_mode": "璇烽�鎷╃ず鏁欐ā寮忥紝鐐广�寮�璁板綍銆戝悗灏嗚褰曟惉杩愯溅鐨勮杩涜矾绾夸綔涓哄叕鍏辩ず鏁欒矾绾裤�寮�璁板綍鍚庡彲闅忔椂鍒囨崲绀烘暀妯″紡銆�,
+
+	"page.batch_delete": "鎵归噺鍒犻櫎",
+	"page.teaching_update": "绀烘暀鏇存柊",
+	"page.teaching_updating": "绀烘暀鏇存柊涓�,
+	"page.teaching_update_success": "鏇存柊绀烘暀鎴愬姛",
+	"page.teaching_deleting": "绀烘暀鍒犻櫎涓�,
+	"page.teaching_delete_success": "鍒犻櫎绀烘暀鎴愬姛",
+	"page.teaching_data_deleting": "姝e湪鍒犻櫎绀烘暀鏁版嵁",
+	"page.ask_deleting_teaching": "纭鍒犻櫎绀烘暀锛�,
+	"page.deleted_teaching_cannot_recovered": "涓�棪鍒犻櫎锛屽氨鏃犳硶鎭㈠",
+
+
+
+	"page.ask_exit_teaching": "鏄惁瑕侀�鍑虹ず鏁欙紵",
+
+	"page.recorded_path_will_be_deleted": "宸茶褰曠殑璺緞灏嗕細琚垹闄�,
+	"page.teaching_end": "绀烘暀缁撴潫",
+	"page.teaching_start": "绀烘暀寮�",
+	"page.ask_re_recording": "鏄惁瑕侀噸鏂拌褰曪紵",
+	"page.edit_path": "缂栬緫璺緞",
+	"page.delete_path": "鍒犻櫎璺緞",
+	"page.teaching_switched_bidirectional_mode": "宸插皢绀烘暀鍒囨崲鎴愬弻鍚戞ā寮�,
+	"page.teaching_switched_intelligent_mode": "宸插皢绀烘暀鍒囨崲鎴愭櫤鑳芥ā寮�,
+	"page.teaching_switched_default_mode": "宸插皢绀烘暀鍒囨崲鎴愰粯璁ゆā寮�,
+	"page.Hold_adjust_position_be_closed_shape_to_delete": "鎸変綇姣忎釜椤剁偣浠ヨ皟鏁村叾浣嶇疆銆傝鍖哄煙蹇呴』鏄皝闂舰鐘躲�鎸夆�纭畾鈥濇寜閽垹闄ゆ鍐呯殑璺緞銆�,
+
+
+	"page.start_extending": "寮�鎵╁睍",
+	"page.start_building": "寮�鏋勫缓",
+	"page.no_map_found": "娌℃湁鎵惧埌绗﹀悎鏉′欢鐨勫湴鍥�,
+	"page.scan_completed": "鎵弿瀹屾垚",
+	"page.extend_completed": "鎵╁睍瀹屾垚",
+	"page.construction_completed": "鏋勫缓瀹屾垚",
+	"page.re_extend": "閲嶆柊鎵╁睍",
+	"page.rebuild": "閲嶆柊鏋勫缓",
+
+	"page.scene_construction_service_not_start": "鍦烘櫙鏋勫缓鏈嶅姟:鏈惎鍔�,
+	"page.scene_construction_service_starting": "鍦烘櫙鏋勫缓鏈嶅姟:鍚姩涓�,
+	"page.scene_composition_saving": "鍦烘櫙鏋勫浘淇濆瓨涓�,
+	"page.scene_composition_save_success": "鍦烘櫙鏋勫浘淇濆瓨鎴愬姛",
+	"page.scene_composition_save_failed": "鍦烘櫙鏋勫浘淇濆瓨澶辫触",
+	"page.ask_interrupt_scene_construction": "鏄惁瑕侀噸鏂拌褰曪紵",
+	"page.built_scene_will_be_deleted": "宸叉瀯寤哄満鏅皢浼氳鍒犻櫎",
+
+	"page.loading_base_map_failed": "鍔犺浇搴曞浘澶辫触",
+	"page.downloading_scene": "涓嬭浇鍦烘櫙涓�,
+	"page.download_scene_success": "涓嬭浇鍦烘櫙鎴愬姛",
+	"page.start_scanning_map": "寮�鎵弿鍦板浘",
+	"page.end_scanning_map": "鍦板浘鎵弿缁撴潫",
+	"page.scene_construction_failed": "鍦烘櫙鏋勫缓澶辫触",
+	"page.check_vehicle_connection_and_restart_building_scene": "璇锋鏌ヨ溅杈嗚繛鎺ワ紝骞堕噸鏂板紑濮嬫瀯寤哄満鏅�,
+
+	"page.target_point": "鐩爣鐐�,
+	"page.not_started": "鏈紑濮�,
+	"page.mandatory_completion": "寮哄埗瀹屾垚",
+	"page.abnormal_termination": "寮傚父缁撴潫",
+	"page.canceled": "宸插彇娑�,
+	"page.completed": "宸插畬鎴�,
+	"page.in_progress": "鎵ц涓�,
+
+	"page.task_completed": "浠诲姟瀹屾垚",
+	"page.task_exception": "浠诲姟寮傚父",
+	"page.task_properties": "浠诲姟灞炴�",
+
+	"page.input_task_name": "璇疯緭鍏ヤ换鍔″悕绉�,
+	"page.select_properties": "閫夋嫨灞炴�",
+	"page.fixed": "鍥哄畾",
+	"page.temporary": "涓存椂",
+	"page.number_repetitions": "閲嶅娆℃暟",
+	"page.button_number": "鎸夐挳鍙�,
+	"page.task_route": "浠诲姟璺嚎",
+	"page.wait_time": "绛夊緟鏃堕棿",
+
+	"page.add_target_point": "娣诲姞鐩爣鐐�,
+
+	"page.new_task": "鏂板浠诲姟",
+	"page.update_task": "鏇存柊浠诲姟",
+	"page.select_task_properties": "璇烽�鎷╁睘鎬�,
+	"page.select_button_number": "璇烽�鎷╂寜閽彿",
+
+	"page.adding_task": "鏂板浠诲姟涓�,
+	"page.updating_task": "鏇存柊浠诲姟鏂板浠诲姟涓�,
+	"page.target_point_no_operation_type": "鐩爣鐐规湭閫夋嫨鎿嶄綔绫诲瀷",
+	"page.at_least_one_target_point_added": "蹇呴』娣诲姞鑷冲皯涓�釜鐩爣鐐�,
+	"page.ask_exit_task_edit": "纭畾瑕侀�鍑虹紪杈戝悧锛�,
+	"page.current_edited_content_will_be_deleted": "褰撳墠缂栬緫鐨勫唴瀹瑰皢浼氳鍒犻櫎",
+	"page.operation": "鎿嶄綔",
+	"page.task_action": "鎿嶄綔",
+
+	"page.cumulative_duration": "绱鏃堕暱",
+	"page.accumulated_mileage": "绱閲岀▼",
+	"page.cumulative_count": "绱娆℃暟",
+	"page.fixed_task": "鍥哄畾浠诲姟",
+	"page.temporary_task": "涓存椂浠诲姟",
+	"page.not_task_found": "娌℃湁鎵惧埌绗﹀悎鏉′欢鐨勪换鍔�,
+	"page.task_terminated_success": "缁堟浠诲姟鎴愬姛",
+	"page.skip_sub_task_success": "璺宠繃瀛愪换鍔℃垚鍔�,
+	"page.no_sub_task_found_not_started_or_executing": "鏈壘鍒版湭寮�鎴栨鍦ㄦ墽琛岀殑瀛愪换鍔�,
+	"page.history_task_will_be_retained": "灏嗕繚鐣欐浠诲姟鐨勫巻鍙茶褰曘�",
+	"page.no_task_history_found": "娌℃湁鎵惧埌绗﹀悎鏉′欢鐨勪换鍔¤褰�,
+	"page.downloading": "涓嬭浇涓�,
+	"page.refresh_success": "Refresh successful",
+	"page.station_list": "绔欑偣鍒楄〃",
+
+	"page.delete_station": "鍒犻櫎绔欑偣",
+	"page.delete_selected_station": "璇ョ珯鐐瑰彲鑳藉凡缁戝畾浠诲姟锛屽垹闄ょ珯鐐瑰悗缁戝畾鐨勪换鍔′細鍋滄骞跺垹闄ゃ�",
+	"page.ask_confirm_delete": "纭鍒犻櫎?",
+	"page.ask_confirm_remove": "纭鍒犻櫎?",
+	"page.delete_success": "鍒犻櫎鎴愬姛",
+	"page.delete_station_success": "鍒犻櫎绔欑偣鎴愬姛",
+	"page.no_selected_stations": "鏈�鎷╃珯鐐�,
+	"page.modify_success_restart_device": "淇敼鎴愬姛锛岃閲嶅惎杞﹁締",
+	"page.input_content_only_numbers_or_signs": "杈撳叆鐨勫唴瀹瑰彧鑳藉寘鍚暟瀛楀拰姝h礋鍙�,
+	"page.input_content_only_numbers_and_signs_and_decimal": "杈撳叆鐨勫唴瀹瑰彧鑳藉寘鍚暟瀛椼�灏忔暟鐐瑰拰姝h礋鍙�",
+
+	"page.repeat_once": "閲嶅1娆�,
+	"page.repeat_twice": "閲嶅2娆�,
+	"page.repeat_times": "閲嶅{0}娆�,
+	"page.time_number": "绗瑊0}/{1}娆�,
+	"page.the_st_suffix": "",
+	"page.the_nd_suffix": "",
+	"page.the_rd_suffix": "",
+	"page.the_th_suffix": "",
+
+
+	"page.email_service": "閭鏈嶅姟",
+	"page.email_port": "閭绔彛",
+	"page.sender_email": "鍙戜欢绠�,
+	"page.authorization_code": "鎺堟潈鐮�,
+	"page.receiver_email": "鏀堕偖绠�,
+	"page.email_subject": "閭欢鏍囬",
+	"page.attachment": "闄勪欢",
+	"page.input_email_service": "璇峰~鍐欓偖绠辨湇鍔�,
+	"page.input_email_port": "璇峰~鍐欑鍙�,
+	"page.input_sender_email": "璇峰~鍐欏彂浠剁",
+	"page.input_authorization_code": "璇峰~鍐欐巿鏉冪爜",
+	"page.input_receiver_email": "璇峰~鍐欐敹閭",
+	"page.input_email_subject": "璇疯緭鍏ラ偖浠舵爣棰�,
+
+	"page.input_email_content": "璇疯緭鍏ラ偖浠跺唴瀹�,
+
+
+	"page.email_sending": "閭欢鍙戦�涓�,
+	"page.email_send_success": "閭欢鍙戦�鎴愬姛",
+	"page.email_send_fail": "閭欢鍙戦�澶辫触",
+	"page.error_log_package": "閿欒鏃ュ織鍖�,
+	"page.tip_app_update": "鍙戠幇App鏂扮増鏈�,
+	"page.tip_car_update": "鍙戠幇杞﹁浇鐗堟湰",
+
+	"page.ask_download": "鏄惁闇�涓嬭浇锛�,
+
+	"page.ask_update": "鏄惁闇�鏇存柊锛�,
+	"page.update_fail": "鏇存柊澶辫触",
+	"page.update_success": "鏇存柊鎴愬姛",
+	"page.saved_success": "淇濆瓨鎴愬姛",
+	"page.saved_fail": "淇濆瓨澶辫触",
+	"page.saving": "淇濆瓨涓�,
+	"page.no_station": "杩樻病鏈夌珯鐐�,
+	"page.upload": "涓婁紶",
+	"page.upload_fail": "涓婁紶澶辫触",
+	"page.upload_finish": "涓婁紶缁撴潫"
+
+
+}
\ No newline at end of file
diff --git a/locale/zh-Hant.json b/locale/zh-Hant.json
new file mode 100644
index 0000000..17886d2
--- /dev/null
+++ b/locale/zh-Hant.json
@@ -0,0 +1,475 @@
+{
+	"page.comma": "锛�,
+	"page.question_mark": "锛�,
+	"page.exclamation_mark": "锛�,
+	"page.full_stop": "銆�,
+	"page.colon": "锛�,
+	"page.quotation_mark_left": "鈥�,
+	"page.quotation_mark_right": "鈥�,
+	"page.double_quotation_mark_left": "鈥�,
+	"page.double_quotation_mark_right": "鈥�,
+	"page.confirm": "纰鸿獚",
+	"page.close": "闂滈枆",
+	"page.ok": "纰哄畾",
+	"page.cancel": "鍙栨秷",
+	"page.save": "淇濆瓨",
+	"page.yes": "鏄�,
+	"page.no": "鍚�,
+	"page.add": "娣诲姞",
+	"page.add2": "鏂板",
+	"page.new": "鏂板缓",
+	"page.modify": "淇敼",
+	"page.edit": "绶ㄨ集",
+	"page.update": "鏇存柊",
+	"page.discarded": "浣滃虎",
+	"page.delete": "鍒櫎",
+	"page.remove": "鍒犻櫎",
+	"page.create": "鍓靛缓",
+	"page.browse": "鐎忚",
+	"page.import": "灏庡叆",
+	"page.export": "灏庡嚭",
+	"page.disabled": "绂佺敤",
+	"page.enabled": "鍟熺敤",
+	"page.not_enabled": "鏈暉鐢�,
+	"page.execute": "鍩疯",
+	"page.query": "鏌ヨ",
+	"page.clear": "娓呯┖",
+	"page.set": "瑷疆",
+	"page.config": "閰嶇疆",
+	"page.reset": "閲嶇疆",
+	"page.susccess": "鎴愬姛",
+	"page.fail": "澶辨晽",
+
+	"page.tip": "鎻愮ず",
+	"page.error": "閷",
+	"page.warning": "璀﹀憡",
+	"page.inquire": "瑭㈠晱",
+	"page.login_name": "鐧婚寗鍚�,
+	"page.account": "甯宠櫉",
+	"page.user_name": "鐢ㄦ埗鍚�,
+	"page.password": "瀵嗙⒓",
+	"page.name": "鍚嶇ū",
+	"page.input_login": "璜嬭几鍏ュ赋铏�,
+	"page.input_password": "璜嬭几鍏ュ瘑纰�,
+	"page.remember_password": "瑷樹綇瀵嗙⒓",
+	"page.login": "鐧诲叆",
+	"page.start_login": "闁嬪鐧诲叆",
+	"page.language": "瑾炶█",
+	"page.version": "鐗堟湰铏�,
+	"page.loading": "鍔犺級涓�..",
+	"page.refresh": "鍒锋柊",
+	"page.home_page": "棣栭爜",
+	"page.coding_disk": "纰肩洡",
+	"page.inventory": "鐩ら粸",
+	"page.sorting": "鍒嗘弨",
+	"page.view": "鐎忚",
+	"page.unrealized": "鏈鐝�,
+	"page.select": "閫夋嫨",
+	"page.please": "璇�,
+	"page.port": "鍩�,
+
+	"page.account_not_exist": "璩櫉涓嶅瓨鍦�,
+	"page.password_error": "瀵嗙⒓閷",
+
+	"page.parameter": "鍙冩暩",
+	"page.result": "绲愭灉",
+	"page.my": "鎴戠殑",
+	"page.share": "鍏变韩",
+	"page.backup": "鍌欎唤",
+	"page.tutorial": "鏁欑▼",
+	"page.rename": "閲嶆柊鍛藉悕",
+	"page.coordinates": "搴ф",
+	"page.orientation": "鏈濆悜",
+	"page.angle": "瑙掑害",
+	"page.state": "鐙�厠",
+	"page.idle": "闁掔疆涓�,
+	"page.at_work": "宸ヤ綔涓�,
+	"page.fault": "鏁呴殰",
+	"page.retry": "閲嶈│",
+	"page.model": "鍨嬭櫉",
+	"page.unknown": "鏈煡",
+	"page.wait": "绛夊緟",
+	"page.second": "绉�,
+	"page.minute": "鍒嗛悩",
+
+	"page.properties": "灞�",
+	"page.select_all": "鍏ㄩ伕",
+
+	"page.phone_number": "鎵嬫铏�,
+	"page.input_phone_number": "杓稿叆鎵嬫铏�,
+
+
+	"page.exit_login": "閫�嚭鐧诲叆",
+	"page.registered_user": "瑷诲唺鐢ㄦ埗",
+	"page.forgot_password": "蹇樿瀵嗙⒓",
+
+	"page.tab_vehicle": "杌婅紱",
+	"page.tab_my": "鎴戠殑",
+	"page.multilingual_settings": "澶氳獮瑷�ō瀹�,
+
+	"page.app_version": "App鐗堟湰",
+	"page.language_switch": "瑾炶█鍒囨彌",
+	"page.clear_cache": "娓呴櫎蹇彇",
+	"page.help_and_feedback": "骞姪鑸囧弽楗�,
+	"page.clear_cache_success": "娓呴櫎蹇彇鎴愬姛",
+	"page.remote_assistance": "閬犵鍗斿姪",
+	"page.log_upload": "鏃ヨ獙涓婂偝",
+	"page.check_the_manual": "鏌ョ湅瑾槑鏇�,
+	"page.interface_log": "浠嬮潰鏃ヨ獙",
+	"page.record_interface_log": "瑷橀寗浠嬮潰鏃ヨ獙",
+	"page.video_instruction": "褰辩墖瑾槑",
+	"page.input_interface_name": "璜嬭几鍏ヤ粙闈㈠悕绋�,
+
+	"page.error_code": "閷纰�,
+	"page.error_msg": "閷璩囪▕",
+	"page.version_update": "鐗堟湰鏇存柊",
+	"page.check_update": "妾㈡煡鏇存柊",
+	"page.updatable_version": "鍙洿鏂扮増鏈�,
+	"page.version_info": "鐗堟湰璇存槑",
+	"page.current_version": "鐣跺墠鐗堟湰鐐�,
+	"page.is_latest_version": "鐣跺墠鐐烘渶鏂扮増鏈�,
+	"page.update_immediately": "绔嬪嵆鏇存柊",
+	"page.download_immediately": "绔嬪嵆涓嬭級",
+	"page.failed_vehicle_connection": "杌婅紱閫g窔澶辨晽",
+	"page.check_network_or_reload": "璜嬫鏌ヤ綘鐨勭恫璺ō瀹氭垨閲嶆柊杓夊叆",
+	"page.obtain_positon_and_orientation_of_vehicle": "鐛插彇鎼亱杌婁綅缃垏鏈濆悜",
+
+	"page.station_name": "绔欓粸鍚嶇ū",
+	"page.offline": "宸查洟绶�,
+	"page.scene_construction": "鍫存櫙妲嬪缓",
+	"page.scene_construction_in_progress": "鍫存櫙妲嬪缓涓�,
+
+	"page.scenes": "鍦烘櫙鏋勫缓",
+	"page.tasks": "浠诲姟璁板綍",
+	"page.task_history": "浠诲嫏瑷橀寗",
+	"page.connecting": "閫g窔涓�,
+	"page.connection_success": "閫g窔鎴愬姛",
+	"page.connection_failed": "閫g窔澶辨晽",
+	"page.connection_preparation": "閫g窔婧栧倷",
+	"page.unable_connect_device": "鐒℃硶閫g窔瑷倷",
+	"page.connect_device": "閫g窔瑷倷",
+
+
+	"page.vehicle_offline_to_reconnect": "杌婅紱宸查洟绶氾紝璜嬮噸鏂伴�绶�,
+	"page.reconnect": "閲嶆柊閫g窔",
+	"page.scan_add_device": "鎺冩弿鏂板瑷倷",
+	"page.manual_add_device": "鎵嬪嫊鏂板瑷倷",
+	"page.add_device": "鏂板瑷倷",
+	"page.input_vehicle_ip": "璜嬭几鍏ヨ粖杓汭P",
+	"page.invalid_qr_code": "鐒℃晥鐨凲R纰�,
+	"page.only_3_vehicles_to_added": " 鏈�鍙厑瑷辨柊澧�杓涜粖",
+	"page.vehicle_ip_has_added": "瑭睮P鐨勮ō鍌欏凡鏂板",
+	"page.scan_device_code_to_connect": "灏囨韬簩缍⒓鏀惧叆妗嗗収浠ラ�鎺ヨō鍌�,
+
+	"page.ip_address": "IP浣嶅潃",
+	"page.invalid_ip": "IP鐒℃晥",
+
+	"page.ask_delete_device": "纰哄畾瑕佸埅闄よ┎瑷倷鍡庯紵",
+	"page.check_vehicle_not_paired_other_terminals": "璜嬬⒑淇濊粖杓涢枊姗熶笖鏈垏鍏朵粬绲傜閰嶅皪",
+	"page.connect_phone_to_this_ip_wifi": "璜嬪皣鎵嬫閫g窔鍒扳�{0}鈥濋枊闋殑WiFii缍茶矾",
+
+	"page.after_connect_return_this": "閫g窔寰屽洖鍒版湰鎳夌敤",
+
+	"page.reconnect_wifi": "閲嶆柊閫g窔WiFi",
+	"page.more": "瀛哥繏鏇村",
+	"page.switch_scan_connection": "鍒囨彌鎺冪⒓閫g窔",
+	"page.go_connect": "鍘婚�绶�,
+	"page.vehicle_connection": "杌婅紱閫g窔",
+	"page.ask_join_wlan": "銆恵0}銆戞兂鍔犲叆鐒$窔鍗�煙缍茶矾銆恵1}銆戝棊锛�,
+	"page.join": "鍔犲叆",
+	"page.scene_backup": "鍫存櫙鍌欎唤",
+	"page.no_scene_matching_criteria": "娌掓湁鎵惧埌绗﹀悎姊濅欢鐨勫牬鏅�,
+	"page.select_scene_to_upload_mobile": "璜嬮伕鎿囦笂鍌冲埌鎵嬫绔殑鍫存櫙",
+	"page.upload_to_mobile": "涓婂偝鑷虫墜姗熺",
+	"page.select_scene_to_download_vehicle": "璜嬮伕鎿囦笅杓夊埌杌婅紱绔殑鍫存櫙",
+	"page.download_to_vehicle": "涓嬭級鑷宠粖杓涚",
+	"page.upload_scenes": "涓婂偝鍫存櫙",
+	"page.scene_list": "鍫存櫙鍒楄〃",
+	"page.download_scenes": "涓嬭級鍫存櫙",
+	"page.scene_list_on_cellphone": "鎵嬫绔劜瀛樼殑鍫存櫙鍒楄〃",
+	"page.confirm_replace_scene": "纰鸿獚鏇挎彌鍫存櫙?",
+	"page.uploading_will_replace_currently_uploaded_scene": "涓婂偝瑭插牬鏅渻鏇挎彌鎺夌暥鍓嶅凡涓婂偝鐨勫牬鏅�",
+	"page.downloading_will_replace_currently_downloaded_scene": "涓嬭級瑭插牬鏅渻鏇挎彌鎺夊凡涓嬭級鐨勫悓鍚嶅牬鏅�",
+	"page.ask_cover_scene": "鏄惁瑕佽钃嬪牬鏅�,
+	"page.upload_success": "涓婂偝鎴愬姛",
+	"page.download_success": "涓嬭級鎴愬姛",
+	"page.scene_uploading": "姝e湪涓婂偝鍫存櫙",
+	"page.scene_downloading": "姝e湪涓嬭級鍫存櫙",
+	"page.ask_exit_page": "鏄惁瑕侀�鍑猴紵",
+	"page.car_version": "杌婅級鐗堟湰",
+	"page.authorization_validity_period": "鎺堟瑠鏈夋晥鏈�,
+	"page.wifi_address": "鐒$窔鍗�煙缍茶矾浣嶅潃",
+	"page.remove_vehicle": "鍒櫎杌婅紱",
+	"page.errors": "閷",
+
+	"page.error_report_info": "閷鍥炲牨璩囪▕",
+	"page.current_errors": "鍗虫檪閷",
+	"page.history_errors": "姝峰彶閷",
+	"page.download_logs": "涓嬭級鏃ヨ獙",
+	"page.download_log": "涓嬭級鏃ヨ獙",
+	"page.clear_errors": "娓呴尟",
+	"page.log_time_range": "鏃ヨ獙鏅傞枔绡勫湇",
+	"page.start_time": "闁嬪鏅傞枔",
+	"page.create_time": "寤虹珛鏅傞枔",
+	"page.end_time": "绲愭潫鏅傞枔",
+	"page.select_start_time": "閬告搰闁嬪鏅傞枔",
+	"page.select_end_time": "閬告搰绲愭潫鏅傞枔",
+	"page.module_id": "妯$祫id",
+	"page.refresh_failed_retry": "閲嶆柊鏁寸悊澶辨晽锛岃珛閲嶈│",
+	"page.current_no_error_messages": "鐣跺墠鐒¢尟瑾よ▕鎭�,
+	"page.download_logs_fail": "涓嬭級鏃ヨ獙澶辨晽",
+	"page.downloading_log": "涓嬭級鏃ヨ獙涓�,
+	"page.package_download_logs": "鎵撳寘涓嬭級鏃ヨ獙涓�,
+	"page.download_log_fail_not_get_filepath": "涓嬭級鏃ヨ獙澶辨晽锛屾湭杩斿洖鏃ヨ獙妾旇硣瑷�,
+	"page.log_downloaded_ask_redownloaded": "鏃ヨ獙妾斿凡缍撲笅杓夛紝鏄惁閲嶆柊涓嬭級锛�,
+	"page.clear_download_logs": "娓呯┖鏈湴鏃ヨ獙",
+	"page.email_send": "閮典欢瀵勯�",
+	"page.send": "瀵勯�",
+	"page.delete_scene": "鍒櫎鍫存櫙",
+	"page.deleting_scene": "姝e湪鍒櫎鍫存櫙",
+	"page.pallet_size": "杓夊叿灏哄",
+	"page.map": "鍦板湒",
+	"page.extend_map": "鎿村厖鍦板湒",
+	"page.regiona_plan": "鍗�煙瑕忓妰",
+
+	"page.length": "闀峰害",
+	"page.width": "瀵害",
+	"page.input_please": "璜嬭几鍏�,
+	"page.navigation": "灏庤埅",
+	"page.auto_unload": "鍗歌波",
+
+	"page.failed_save_file": "淇濆瓨涓嬭級妾旀澶辨晽",
+
+	"page.build_scene": "寤烘鍫存櫙",
+
+	"page.input_scene_name": "璜嬭几鍏ュ牬鏅悕绋�,
+	"page.update_scene_name": "淇敼鍫存櫙鍚嶇ū",
+	"page.scene_name_cannot_empty": "鍫存櫙鍚嶇ū涓嶈兘鐐虹┖",
+	"page.scene_name_not_changed": "鍫存櫙鍚嶇ū鏈畩鏇�,
+	"page.update_scene_name_success": "淇敼鍫存櫙鍚嶇ū鎴愬姛",
+	"page.switching_scene": "姝e湪鍒囨彌鍫存櫙",
+	"page.delete_scene_will_be_deleted_map_and_tasks": "鍒櫎鍫存櫙鏈冩妸鍫存櫙灏嶆噳鐨勫湴鍦栦换鍕欒硣瑷婇兘鏈冨埅闄�,
+	"page.ask_deleting_scene": "纰鸿獚鍒櫎鍫存櫙锛�,
+	"page.scene_extend_completed": "鍫存櫙鎿村厖绲愭潫",
+	"page.scene_construction_completed": "鍫存櫙寤烘绲愭潫",
+
+
+
+	"page.add_station": "鏂板绔欓粸",
+	"page.edit_station": "绶ㄨ集绔欓粸",
+	"page.creating_station": "姝e湪鏂板缓绔欓粸",
+	"page.updating_station": "姝e湪鏇存柊绔欓粸",
+	"page.deleting_station": "姝e湪鍒櫎绔欓粸",
+
+	"page.adjust_position_orientation": "瑾挎暣浣嶇疆鏈濆悜",
+	"page.adjust_position": "瑾跨瘈浣嶇疆",
+	"page.adjust_orientation": "瑾跨瘈鏈濆悜",
+
+
+	"page.x_axis": "姗骇妯�,
+	"page.y_axis": "绺卞骇妯�,
+
+	"page.input_station_name": "璜嬭几鍏ョ珯榛炲悕绋�,
+
+
+	"page.manual_plan": "浜哄伐瑕忓妰",
+
+	"page.vehicle_trajectory_plan": "杌婅紱杌岃贰瑕忓妰",
+
+	"page.feasible_region": "鍙鍗�,
+	"page.prohibition_region": "绂佽鍗�,
+	"page.virtual_wall": "铏涙摤鐗�,
+	"page.endpoint_adjust_position_to_closed_shape": "鍙寜浣忓悇绔粸瑾挎暣铏涙摤鐗嗕綅缃紝鎸夈�+銆戞寜閳曞彲鏂板闋傞粸锛屽崁鍩熼渶鐐洪枆鍚堝湒褰�,
+	"page.endpoint_adjust_virtual_wall_position": "鍙寜浣忓叐绔粸瑾挎暣铏涙摤鐗嗕綅缃�,
+
+	"page.regional_planning": "姝e湪閫茶鍗�煙瑕忓妰",
+	"page.ask_exit_regional_planning": "鏄惁瑕侀�鍑哄崁鍩熻鍔冿紵",
+
+	"page.task_set": "浠诲嫏瑷畾",
+	"page.set_task": "瑷畾浠诲嫏",
+
+	"page.edit_map": "绶ㄨ集鍦板湒",
+	"page.path_teaching": "璺緫绀烘暀",
+	"page.in_path_record": "璺緫瑷橀寗涓�,
+	"page.default": "闋愯ō",
+	"page.intelligent": "鏅烘収",
+	"page.bidirectional": "闆欏悜",
+	"page.unidirectional": "鍠悜",
+	"page.main_road": "涓昏矾",
+	"page.branch_road": "鏀矾",
+
+
+	"page.any_time_switch_teaching_modes": "鍙毃鏅傚垏鎻涚ず鏁欐ā寮�,
+	"page.recording_path_of_vehicle_can_add_station": "姝e湪瑷橀寗鎼亱杌婅閫茶矾寰戯紝瀹屾垚寰岄粸鎸夐垥浠ョ祼鏉熺ず鏁欍�鍦ㄧず鏁欓亷绋嬩腑鍙互鏂板绔欓粸銆�,
+
+
+	"page.start_teaching": "闁嬪绀烘暀",
+	"page.end_teaching": "绲愭潫绀烘暀",
+	"page.re_record": "閲嶆柊绀烘暀",
+	"page.path_recording_completed": "璺緫瑷橀寗瀹屾垚",
+	"page.recording_path_of_vehicle_click_btn_to_finish": "姝e湪瑷橀寗鎼亱杌婅閫茶矾寰戯紝瀹屾垚寰岄粸鎸夐垥浠ョ祼鏉熺ず鏁�,
+	"page.ask_save_this_path": "瑕佸皣瑭叉璺緫鍎插瓨鐐虹ず鏁欒矾寰戝棊锛�,
+	"page.save_as_teaching_path": "鍎插瓨鐐虹ず鏁欒矾寰�,
+	"page.teaching_completed": "绀烘暀瀹屾垚",
+	"page.path_saved_as_teaching_path": "宸插皣璺緫鍎插瓨鐐虹ず鏁欒矾寰�,
+	"page.complete": "瀹屾垚",
+	"page.public_teaching_to_begin": "鍗冲皣闁嬪绀烘暀",
+	"page.public_teaching_start_tip_select_teaching_mode": "璜嬮伕鎿囩ず鏁欐ā寮忥紝榛炪�闁嬪瑷橀寗銆戝緦灏囪閷勬惉閬嬭粖鐨勮閫茶矾绶氫綔鐐哄叕鍏辩ず鏁欒矾绶氥�闁嬪瑷橀寗寰屽彲闅ㄦ檪鍒囨彌绀烘暀妯″紡銆�,
+
+	"page.batch_delete": "鎵规鍒櫎",
+	"page.teaching_update": "绀烘暀鏇存柊",
+	"page.teaching_updating": "绀烘暀鏇存柊涓�,
+	"page.teaching_update_success": "鏇存柊绀烘暀鎴愬姛",
+	"page.teaching_deleting": "绀烘暀鍒櫎涓�,
+	"page.teaching_delete_success": "鍒櫎绀烘暀鎴愬姛",
+	"page.teaching_data_deleting": "姝e湪鍒櫎绀烘暀璩囨枡",
+	"page.ask_deleting_teaching": "纰鸿獚鍒櫎绀烘暀锛�,
+	"page.deleted_teaching_cannot_recovered": "涓�棪鍒犻櫎锛屽氨鐒℃硶鎭㈠京",
+	"page.ask_exit_teaching": "纭閫�嚭绀烘暀锛�,
+
+	"page.recorded_path_will_be_deleted": "宸茶閷勭殑璺緫灏囨渻琚埅闄�,
+	"page.teaching_end": "绀烘暀绲愭潫",
+	"page.teaching_start": "绀烘暀闁嬪",
+	"page.ask_re_recording": "鏄惁瑕侀噸鏂拌閷勶紵",
+	"page.edit_path": "绶ㄨ集璺緫",
+	"page.delete_path": "鍒犻櫎璺緫",
+
+	"page.teaching_switched_bidirectional_mode": "宸插皣绀烘暀鍒囨彌鎴愰洐鍚戞ā寮�,
+	"page.teaching_switched_intelligent_mode": "宸插皣绀烘暀鍒囨彌鎴愭櫤鎱фā寮�,
+	"page.teaching_switched_default_mode": "宸插皣绀烘暀鍒囨彌鎴愰爯瑷ā寮�,
+	"page.Hold_adjust_position_be_closed_shape_to_delete": "鎸変綇姣忓�闋傞粸浠ヨ鏁村叾浣嶇疆銆�瑭插崁鍩熷繀闋堟槸灏侀枆褰㈢媭銆�鎸夆�纰哄畾鈥濇寜閳曞垹闄ゆ鍏х殑璺緫銆�,
+
+	"page.start_extending": "闁嬪鎿村厖",
+	"page.start_building": "闁嬪寤烘",
+	"page.no_map_found": "娌掓湁鎵惧埌绗﹀悎姊濅欢鐨勫湴鍦�,
+	"page.scan_completed": "鎺冩弿瀹屾垚",
+	"page.extend_completed": "鎿村厖瀹屾垚",
+	"page.construction_completed": "寤烘瀹屾垚",
+	"page.re_extend": "閲嶆柊鎿村厖",
+	"page.rebuild": "閲嶆柊寤烘",
+
+	"page.scene_construction_service_not_start": "鍫存櫙寤烘鏈嶅嫏:鏈暉鍕�,
+	"page.scene_construction_service_starting": "鍫存櫙寤烘鏈嶅嫏:鍟熷嫊涓�,
+	"page.scene_composition_saving": "鍫存櫙妲嬪湒鍎插瓨涓�,
+	"page.scene_composition_save_success": "鍫存櫙妲嬪湒鍎插瓨鎴愬姛",
+	"page.scene_composition_save_failed": "鍫存櫙妲嬪湒鍎插瓨澶辨晽",
+	"page.ask_interrupt_scene_construction": "鏄惁瑕侀噸鏂拌閷勶紵",
+	"page.built_scene_will_be_deleted": "宸插缓妲嬪牬鏅皣鏈冭鍒櫎",
+
+	"page.loading_base_map_failed": "杓夊叆搴曞湒澶辨晽",
+	"page.downloading_scene": "涓嬭級鍫存櫙涓�,
+	"page.download_scene_success": "涓嬭級鍫存櫙鎴愬姛",
+	"page.start_scanning_map": "闁嬪鎺冩弿鍦板湒",
+	"page.end_scanning_map": "鍦板湒鎺冩弿绲愭潫",
+	"page.scene_construction_failed": "鍫存櫙寤烘澶辨晽",
+	"page.check_vehicle_connection_and_restart_building_scene": "璜嬫鏌ヨ粖杓涢�绶氾紝涓﹂噸鏂伴枊濮嬪缓妲嬪牬鏅�,
+
+	"page.target_point": "鐩榛�,
+	"page.not_started": "鏈枊濮�,
+	"page.mandatory_completion": "寮峰埗瀹屾垚",
+	"page.abnormal_termination": "鐣板父绲愭潫",
+	"page.canceled": "宸插彇娑�,
+	"page.completed": "宸插畬鎴�,
+	"page.in_progress": "鍩疯涓�,
+
+	"page.task_completed": "浠诲嫏瀹屾垚",
+	"page.task_exception": "浠诲嫏鐣板父",
+	"page.task_properties": "浠诲嫏灞�",
+
+	"page.input_task_name": "璜嬭几鍏ヤ换鍕欏悕绋�,
+	"page.select_properties": "閬告搰灞�",
+	"page.fixed": "鍥哄畾",
+	"page.temporary": "鑷ㄦ檪",
+	"page.number_repetitions": "閲嶈娆℃暩",
+	"page.button_number": "鎸夐垥铏�,
+	"page.task_route": "浠诲嫏璺窔",
+	"page.wait_time": "绛夊緟鏅傞枔",
+
+	"page.add_target_point": "鏂板鐩榛�,
+
+	"page.new_task": "鏂板浠诲嫏",
+	"page.update_task": "鏇存柊浠诲嫏",
+	"page.select_task_properties": "璜嬮伕鎿囧爆鎬�,
+	"page.select_button_number": "璜嬮伕鎿囨寜閳曡櫉",
+
+	"page.adding_task": "鏂板浠诲嫏涓�,
+	"page.updating_task": "鏇存柊浠诲嫏鏂板浠诲嫏涓�,
+	"page.target_point_no_operation_type": "鐩榛炴湭閬告搰鎿嶄綔椤炲瀷",
+	"page.at_least_one_target_point_added": "蹇呴爤鏂板鑷冲皯涓��鐩榛�,
+	"page.ask_exit_task_edit": "纰哄畾瑕侀�鍑虹法杓棊锛�,
+	"page.current_edited_content_will_be_deleted": "鐣跺墠绶ㄨ集鐨勫収瀹瑰皣鏈冭鍒犻櫎",
+	"page.operation": "鎿嶄綔",
+	"page.task_action": "鎿嶄綔",
+
+	"page.cumulative_duration": "绱▓鏅傞暦",
+	"page.accumulated_mileage": "绱▓閲岀▼",
+	"page.cumulative_count": "绱▓娆℃暩",
+	"page.fixed_task": "鍥哄畾浠诲嫏",
+	"page.temporary_task": "鑷ㄦ檪浠诲嫏",
+	"page.not_task_found": "娌掓湁鎵惧埌绗﹀悎姊濅欢鐨勪换鍕�,
+	"page.task_terminated_success": "绲傛浠诲嫏鎴愬姛",
+	"page.skip_sub_task_success": "璺抽亷瀛愪换鍕欐垚鍔�,
+	"page.no_sub_task_found_not_started_or_executing": "鏈壘鍒版湭闁嬪鎴栨鍦ㄥ煼琛岀殑瀛愪换鍕�,
+	"page.history_task_will_be_retained": "灏囦繚鐣欐浠诲嫏鐨勬鍙茶閷勩�",
+	"page.no_task_history_found": "娌掓湁鎵惧埌绗﹀悎姊濅欢鐨勪换鍕欒閷�,
+
+	"page.downloading": "涓嬭級涓�,
+	"page.refresh_success": "Refresh successful",
+	"page.station_list": "缍茬珯娓呭柈",
+	"page.delete_station": "鍒犻櫎缍茬珯",
+	"page.delete_selected_station": "瑭茬恫绔欏彲鑳藉凡缍佸畾浠诲嫏锛屽垹闄ょ恫绔欏緦缍佸畾鐨勪换鍕欐渻鍋滄涓﹀垹闄ゃ�",
+	"page.ask_confirm_delete": "纰鸿獚鍒犻櫎?",
+	"page.ask_confirm_remove": "纰鸿獚鍒犻櫎?",
+	"page.delete_success": "鍒犻櫎鎴愬姛",
+
+	"page.delete_station_success": "鍒犻櫎缍茬珯鎴愬姛",
+	"page.no_selected_stations": "鏈伕鎿囩恫绔�,
+	"page.modify_success_restart_device": "淇敼鎴愬姛锛岃珛閲嶅晸杌婅紱",
+	"page.input_content_only_numbers_or_signs": "杓稿叆鐨勫収瀹瑰彧鑳藉寘鍚暩浣嶅拰姝h矤铏�,
+	"page.input_content_only_numbers_and_signs_and_decimal": "杓稿叆鐨勫収瀹瑰彧鑳藉寘鍚暩浣嶃�灏忔暟鐐瑰拰姝h矤铏�,
+	"page.repeat_once": "閲嶈1娆�,
+	"page.repeat_twice": "閲嶈2娆�,
+	"page.repeat_times": "閲嶈{0}娆�,
+	"page.time_number": "绗瑊0}/{1}娆�,
+	"page.the_st_suffix": "",
+	"page.the_nd_suffix": "",
+	"page.the_rd_suffix": "",
+	"page.the_th_suffix": "",
+
+	"page.email_service": "閮电鏈嶅嫏",
+	"page.email_port": "閮电鍩�,
+	"page.sender_email": "瀵勪欢鍖�,
+	"page.authorization_code": "鎺堟瑠纰�,
+	"page.receiver_email": "鏀堕兊绠�,
+	"page.email_subject": "閮典欢妯欓",
+	"page.attachment": "闄勪欢",
+
+	"page.input_email_service": "璜嬪~瀵兊绠辨湇鍕�,
+	"page.input_email_port": "璜嬪~瀵煚",
+	"page.input_sender_email": "璜嬪~瀵瘎浠跺專",
+	"page.input_authorization_code": "璜嬪~瀵巿娆婄⒓",
+	"page.input_receiver_email": "璜嬪~瀵敹閮电",
+	"page.input_email_subject": "璜嬭几鍏ラ兊浠舵椤�,
+	"page.input_email_content": "璜嬭几鍏ラ兊浠跺収瀹�,
+
+	"page.email_sending": "閮典欢鐧奸�涓�,
+	"page.email_send_success": "閮典欢鐧奸�鎴愬姛",
+	"page.email_send_fail": "閮典欢鐧奸�澶辨晽",
+	"page.error_log_package": "閷鏃ヨ獙鍖�,
+	"tip_app_update": "鐧肩従App鏂扮増鏈�,
+	"page.tip_car_update": "鐧肩従杌婅級鐗堟湰",
+
+	"page.ask_download": "鏄惁闇�涓嬭級锛�,
+	"page.ask_update": "鏄惁闇�鏇存柊锛�,
+	"page.update_fail": "鏇存柊澶辨晽",
+	"page.update_success": "鏇存柊鎴愬姛",
+	"page.saved_success": "淇濆瓨鎴愬姛",
+	"page.saved_fail": "淇濆瓨澶辨晽",
+	"page.saving": "淇濆瓨涓�,
+	"page.no_station": "閭勬矑鏈夌恫绔�,
+	"page.upload": "涓婂偝",
+	"page.upload_fail": "涓婂偝澶辨晽",
+
+"page.upload_finish": "涓婂偝绲愭潫"
+
+}
\ No newline at end of file
diff --git a/main.js b/main.js
index c1a55cd..d139c77 100644
--- a/main.js
+++ b/main.js
@@ -1,26 +1,43 @@
 import App from './App'
-
+// i18n
+import messages from './locale'
+const i18nConfig = {
+	locale: uni.getLocale(),
+	messages
+}
 // #ifndef VUE3
 import Vue from 'vue'
 import './uni.promisify.adaptor'
+import VueI18n from 'vue-i18n'
 Vue.config.productionTip = false
+const i18n = new VueI18n(i18nConfig)
 App.mpType = 'app'
 const app = new Vue({
-  ...App
+	...App,
+	i18n,
 })
 app.$mount()
 // #endif
 
 // #ifdef VUE3
 
-import { createSSRApp } from 'vue'
-import {Button} from 'antd-mobile-vue-next'
+import {
+	createSSRApp
+} from 'vue'
+import {
+	Button
+} from 'antd-mobile-vue-next'
+import {
+	createI18n
+} from 'vue-i18n'
 
+const i18n = new createI18n(i18nConfig)
 export function createApp() {
-  const app = createSSRApp(App)
-app.use(Button)
-  return {
-    app
-  }
+	const app = createSSRApp(App)
+	app.use(i18n)
+	app.use(Button)
+	return {
+		app
+	}
 }
 // #endif
\ No newline at end of file
diff --git a/manifest.json b/manifest.json
index 6e2ede5..f31e55b 100644
--- a/manifest.json
+++ b/manifest.json
@@ -2,8 +2,8 @@
     "name" : "ES-GO",
     "appid" : "__UNI__C988375",
     "description" : "",
-    "versionName" : "1.3.9",
-    "versionCode" : 139,
+    "versionName" : "251212_V2.0",
+    "versionCode" : 1160,
     "transformPx" : false,
     /* 5+App鐗规湁鐩稿叧 */
     "app-plus" : {
@@ -39,7 +39,8 @@
                     "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
                     "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
                     "<uses-feature android:name=\"android.hardware.camera\"/>",
-                    "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
+                    "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>",
+                    "<uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"/>"
                 ]
             },
             /* ios鎵撳寘閰嶇疆 */
@@ -50,7 +51,8 @@
             "sdkConfigs" : {
                 "ad" : {}
             }
-        }
+        },
+        "nativePlugins" : {}
     },
     /* 蹇簲鐢ㄧ壒鏈夌浉鍏�*/
     "quickapp" : {},
diff --git a/package-lock.json b/package-lock.json
index 4e73299..628b704 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,5 +1,5 @@
 {
-  "name": "diniu",
+  "name": "鍦扮墰App",
   "lockfileVersion": 3,
   "requires": true,
   "packages": {
@@ -7,6 +7,7 @@
       "dependencies": {
         "ant-design-vue": "^4.0.0-rc.6",
         "antd-mobile-vue-next": "^0.1.0-16",
+        "dayjs": "^1.11.19",
         "fabric": "^6.6.7",
         "rxjs": "^7.8.2",
         "uuid": "^11.1.0"
@@ -1337,9 +1338,9 @@
       }
     },
     "node_modules/dayjs": {
-      "version": "1.11.18",
-      "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.18.tgz",
-      "integrity": "sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA==",
+      "version": "1.11.19",
+      "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.19.tgz",
+      "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==",
       "license": "MIT"
     },
     "node_modules/debounce": {
diff --git a/package.json b/package.json
index 4e35f38..9ab1c71 100644
--- a/package.json
+++ b/package.json
@@ -2,6 +2,7 @@
   "dependencies": {
     "ant-design-vue": "^4.0.0-rc.6",
     "antd-mobile-vue-next": "^0.1.0-16",
+    "dayjs": "^1.11.19",
     "fabric": "^6.6.7",
     "rxjs": "^7.8.2",
     "uuid": "^11.1.0"
diff --git a/pages.json b/pages.json
index cc782b2..4aca9d2 100644
--- a/pages.json
+++ b/pages.json
@@ -3,19 +3,19 @@
 		{
 			"path": "pages/login/index",
 			"style": {
-				"navigationBarTitleText": "鐧诲綍"
+				"navigationBarTitleText": "%page.login%"
 			}
 		},
 		{
 			"path": "pages/login/register",
 			"style": {
-				"navigationBarTitleText": "娉ㄥ唽鐢ㄦ埛"
+				"navigationBarTitleText": "%page.registered_user%"
 			}
 		},
 		{
 			"path": "pages/login/forgot-password",
 			"style": {
-				"navigationBarTitleText": "蹇樿瀵嗙爜"
+				"navigationBarTitleText": "%page.forgot_password%"
 			}
 		},
 		{
@@ -31,7 +31,7 @@
 		{
 			"path": "pages/index/connect",
 			"style": {
-				"navigationBarTitleText": "杞﹁締杩炴帴"
+				"navigationBarTitleText": ""
 			}
 		},
 
@@ -47,49 +47,73 @@
 		{
 			"path": "pages/index/backup",
 			"style": {
-				"navigationBarTitleText": "鍦烘櫙澶囦唤"
+				"navigationBarTitleText": "%page.scene_backup%"
 			}
 		},
 		{
 			"path": "pages/my/index",
 			"style": {
-				"navigationBarTitleText": "鎴戠殑"
+				"navigationBarTitleText": "%page.my%"
 			}
 		},
 		{
 			"path": "pages/my/language",
 			"style": {
-				"navigationBarTitleText": "澶氳瑷�缃�
+				"navigationBarTitleText": "%page.multilingual_settings%"
 			}
 		},
 		{
-			"path": "pages/my/version-update",
+			"path": "pages/version/app-update",
 			"style": {
-				"navigationBarTitleText": "鐗堟湰鏇存柊"
+				"navigationBarTitleText": "%page.version_update%"
+			}
+		},
+		{
+			"path": "pages/version/car-program-download",
+			"style": {
+				"navigationBarTitleText": "%page.version_update%"
+			}
+		},
+		{
+			"path": "pages/version/car-program-upload",
+			"style": {
+				"navigationBarTitleText": "%page.car_version%"
 			}
 		},
 		{
 			"path": "pages/my/help-feedback",
 			"style": {
-				"navigationBarTitleText": "甯姪涓庡弽棣�
+				"navigationBarTitleText": "%page.help_and_feedback%"
 			}
 		},
 		{
 			"path": "pages/my/instruction",
 			"style": {
-				"navigationBarTitleText": "鏌ョ湅璇存槑涔�
+				"navigationBarTitleText": "%page.check_the_manual%"
 			}
 		},
 		{
 			"path": "pages/my/log",
 			"style": {
-				"navigationBarTitleText": "鎺ュ彛鏃ュ織"
+				"navigationBarTitleText": "%page.interface_log%"
 			}
 		},
 		{
 			"path": "pages/my/log-detail",
 			"style": {
-				"navigationBarTitleText": "鎺ュ彛鏃ュ織璇︽儏"
+				"navigationBarTitleText": "%page.interface_log%"
+			}
+		},
+		{
+			"path": "pages/my/log_upload",
+			"style": {
+				"navigationBarTitleText": "%page.log_upload%"
+			}
+		},
+		{
+			"path": "pages/my/email",
+			"style": {
+				"navigationBarTitleText": "%page.email_send%"
 			}
 		},
 		{
@@ -105,27 +129,27 @@
 		{
 			"path": "pages/map/task",
 			"style": {
-				"navigationBarTitleText": "鍦板浘"
+				"navigationBarTitleText": "%page.map%"
 			}
 		},
 
 		{
 			"path": "pages/map/teaching",
 			"style": {
-				"navigationBarTitleText": "璺緞绀烘暀"
+				"navigationBarTitleText": "%page.path_teaching%"
 			}
 		},
 
 		{
 			"path": "pages/task/index",
 			"style": {
-				"navigationBarTitleText": "浠诲姟"
+				"navigationBarTitleText": "%page.tasks%"
 			}
 		},
 		{
 			"path": "pages/task/list",
 			"style": {
-				"navigationBarTitleText": "璁剧疆浠诲姟"
+				"navigationBarTitleText": "%page.set_task%"
 			}
 		},
 		{
@@ -149,44 +173,39 @@
 		{
 			"path": "pages/task/log-list",
 			"style": {
-				"navigationBarTitleText": "浠诲姟璁板綍"
+				"navigationBarTitleText": "%page.task_history%"
 			}
 		},
 		{
 			"path": "pages/task/map-task",
 			"style": {
-				"navigationBarTitleText": "浠诲姟"
+				"navigationBarTitleText": "%page.tasks%"
 			}
 		},
 		{
 			"path": "pages/station/index",
 			"style": {
-				"navigationBarTitleText": "绔欑偣鍒楄〃"
+				"navigationBarTitleText": "%page.station_list%"
 			}
 		},
 		{
 			"path": "pages/station/delete",
 			"style": {
-				"navigationBarTitleText": "鎵归噺鍒犻櫎绔欑偣"
-			}
-		},
-		{
-			"path": "pages/teaching/index",
-			"style": {
-				"navigationBarTitleText": "绀烘暀璺嚎鍒楄〃"
-			}
-		},
-		{
-			"path": "pages/teaching/list",
-			"style": {
-				"navigationBarTitleText": "绀烘暀璺嚎鍒楄〃"
+				"navigationBarTitleText": "%page.delete_station%"
 			}
 		},
 
 		{
 			"path": "pages/map/scene",
 			"style": {
-				"navigationBarTitleText": "鍦烘櫙鏋勫缓"
+				"navigationBarTitleText": "%page.scene_construction%"
+			}
+		},
+		{
+			"path": "pages/error/index",
+			"style": {
+				"navigationBarTitleText": "%page.errors%"
+				// "enablePullDownRefresh": true
 			}
 		}
 
@@ -211,14 +230,14 @@
 		"backgroundColor": "#FFFFFF",
 		"list": [{
 				"pagePath": "pages/index/index",
-				"text": "杞﹁締",
+				"text": "%page.tab_vehicle%",
 				"iconPath": "/static/tabbar/mdi_hand-truck2.png",
 				"selectedIconPath": "/static/tabbar/mdi_hand-truck.png"
 			},
 
 			{
 				"pagePath": "pages/my/index",
-				"text": "鎴戠殑",
+				"text": "%page.tab_my%",
 				"iconPath": "/static/tabbar/ooui_user-avatar2.png",
 				"selectedIconPath": "/static/tabbar/ooui_user-avatar.png"
 			}
diff --git a/pages/error/index.vue b/pages/error/index.vue
new file mode 100644
index 0000000..28a664f
--- /dev/null
+++ b/pages/error/index.vue
@@ -0,0 +1,591 @@
+<template>
+	<view class="pages-error">
+		<view class="switch-type">
+			<view class="switch-button-group">
+				<view class="switch-button" v-for="(page,index) in pageList" :key="page"
+					:class="curPageIndex ==index?'switch-button-checked':''" @click="onSelectPage(index)">
+					{{page}}
+				</view>
+
+			</view>
+		</view>
+		<swiper circular indicator-dots class="swiper" :current="curPageIndex" @change="changePageSwiper">
+			<swiper-item class="swiper-item">
+
+				<scroll-view class="list" enable-flex scroll-y refresher-enabled="true" :refresher-triggered="triggered"
+					:scroll-top="scrollTop" :refresher-threshold="100" @refresherpulling="onPulling"
+					@refresherrefresh="onRefresh" @refresherrestore="onRestore" @refresherabort="onAbort"
+					@scroll="scrollView">
+					<view class="list-tip"> {{translate('current_errors')}}</view>
+					<template v-if="errorList.length > 0">
+						<view class="list-view">
+							<view class="list-item" v-for="(item,index) in errorList" :key="index">
+								<LogItemView :logData="item" :isRuntime="true" :language='language'></LogItemView>
+
+							</view>
+						</view>
+						<view class="button-line"> <a-button type="primary" class="button" @click="clickClearError">
+								{{translate('clear_errors')}}
+							</a-button></view>
+					</template>
+					<view class="list-tip center" v-else>{{translate('current_no_error_messages')}}</view>
+
+					<view class="list-tip">{{translate('history_errors')}}</view>
+					<view class="list-view" v-if="errorHistory.length > 0">
+						<view class="list-item" v-for="(item,index) in errorHistory" :key="index">
+							<LogItemView :logData="item" :language='language' :downloadProgress="downloadProgress"
+								:downloading="downloadingFile== item.file_path"
+								:waiting="downloadList.includes(item.file_path)" @click-download="clickDownload">
+							</LogItemView>
+						</view>
+					</view>
+					<view class="list-tip center" v-else>{{translate('current_no_error_messages')}}</view>
+
+				</scroll-view>
+
+
+			</swiper-item>
+			<swiper-item class="swiper-item">
+				<view class="list">
+					<view class="list-tip">{{translate('log_time_range')}}</view>
+					<view class="list-line">
+						<view class="label">{{translate("start_time")}}:</view>
+						<view class="value">
+							<uni-datetime-picker type="datetime" v-model="start_time" :disabled="downloading">
+								{{start_time?start_time:translate('select_start_time')}}<uni-icons class="icon"
+									type="right" size="20" color="#888"></uni-icons></uni-datetime-picker>
+
+						</view>
+
+					</view>
+					<view class="list-line">
+						<view class="label">{{translate("end_time")}}:</view>
+						<view class="value">
+							<uni-datetime-picker type="datetime" v-model="end_time" :disabled="downloading">
+								{{end_time?end_time:translate('select_end_time')}}<uni-icons class="icon" type="right"
+									size="20" color="#888"></uni-icons></uni-datetime-picker>
+						</view>
+					</view>
+					<template v-if="!downloading">
+						<view class="button-line"> <a-button type="primary" class="button" @click="clickDownloadLog">
+								{{translate('download_log')}}
+							</a-button></view>
+
+					</template>
+
+					<view class="download-progress" v-else>
+
+						<view class="progress"> <progress :percent="downloadProgress" activeColor="#10AEFF"
+								stroke-width="3" />
+						</view>
+						<view class="tip">{{downloadingTip }}</view>
+
+					</view>
+				</view>
+			</swiper-item>
+
+		</swiper>
+	</view>
+</template>
+
+<script>
+	import {
+		showToast,
+		showModal,
+		showError,
+		showInfo
+	} from "@/comm/utils.js"
+	import TaskInit from "@/comm/extend.js"
+
+	import {
+		getErrorList,
+		getErrorHistory,
+		clearFault,
+		downloadError,
+		tarErrorLog,
+		queryErrorTaskStatus
+	} from "@/api/vehicle.js"
+	import LogItemView from "./infos/log-item.vue"
+
+	import {
+		Button
+	} from 'antd-mobile-vue-next'
+	import dayjs from "dayjs"
+
+	export default {
+		name: "PagesError",
+		components: {
+
+			'a-button': Button,
+			LogItemView,
+		},
+		data() {
+			return {
+				downloading: false,
+				downloadProgress: 0,
+				downloadingTip: "",
+				downloadingFile: "",
+				downloadList: [],
+				ip: "",
+				sceneId: "",
+				errorList: [],
+				errorHistory: [],
+				curPageIndex: 0,
+				pageList: [this.translate('errors'), this.translate('download_logs')],
+				language: "zh",
+				isPageVisible: false,
+				start_time: "",
+				end_time: "",
+				triggered: false,
+				scrollTop: 0,
+				old: {
+					triggered: 0,
+					scrollTop: 0
+				},
+				fileList: []
+			}
+		},
+		computed: {
+
+		},
+
+		onLoad(option) {
+			this.ip = option.ip || ""
+			uni.setNavigationBarTitle({
+				title: this.translate("errors")
+			})
+			const curLang = uni.getLocale()
+			if (curLang == "en") {
+				this.language = ""
+			}
+			this.isPageVisible = true
+			this.loadData()
+		},
+		onShow() {
+			this.isPageVisible = true
+
+
+		},
+		onHide() {
+			this.isPageVisible = 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 {
+					const _this = this
+					this.errorHistory = await this.loadErrorHistory()
+					this.errorList = await this.loadErrorList()
+					this.scrollTop = this.old.scrollTop
+
+					this.$nextTick(function() {
+						_this.scrollTop = 1
+					});
+					try {
+						const list = await TaskInit.fileUtils.listSavedFiles("download")
+						this.fileList = list.map((a) => a.name)
+					} catch (ex) {
+						console.log(ex)
+					}
+					
+				} catch (ex) {
+					showError(ex, this.translate('error'))
+				}
+			},
+
+			async loadErrorHistory() {
+				try {
+					const list = await getErrorHistory(this.ip)
+					list.forEach((item) => {
+						const path = item.file_path
+						item.file_path = path.trim()
+					})
+					return list || []
+				} catch (ex) {
+					showError(ex, this.translate('error'))
+
+					return []
+				}
+			},
+			async loadErrorList() {
+				try {
+					const list = await getErrorList(this.ip)
+					return list || []
+				} catch (ex) {
+					showError(ex, this.translate('error'))
+
+					return []
+				}
+			},
+			async timerLoadErrorList() {
+				try {
+					if (this.isPageVisible)
+						this.errorList = await this.loadErrorList()
+				} catch (ex) {
+					showToast(ex)
+				} finally {
+					setTimeout(this.timerLoadErrorList, 10 * 1000);
+				}
+			},
+			onSelectPage(index) {
+				if (this.downloading)
+					return
+				this.setData({
+					curPageIndex: index
+				})
+			},
+			changePageSwiper(evt) {
+				if (this.downloading)
+					return
+				let index = evt.target.current || evt.detail.current;
+				this.setData({
+					curPageIndex: index
+				})
+			},
+			async clickClearError() {
+				try {
+
+					await clearFault(this.ip)
+					this.loadData()
+				} catch (ex) {
+					showError(ex)
+				}
+			},
+			clickDownload(item) {
+				var pathArr = item.file_path.split("/")
+				if (pathArr.length === 1) {
+					pathArr = item.file_path.split("\\")
+				}
+				const fileName = pathArr.pop()
+				console.log(fileName, this.fileList)
+				if (this.fileList.includes(fileName)) {
+					showModal({
+						title: "",
+						content: this.translate("log_downloaded_ask_redownloaded"),
+						confirmText: this.translate("yes"),
+						cancelText: this.translate("no")
+					}).then((res) => {
+						TaskInit.fileUtils.deleteSavedFile("download",fileName)
+						if (this.downloadingFile) {
+							this.downloadList.push(item.file_path)
+						} else
+							this.downloadErrorLog(item.file_path)
+					})
+				} else {
+					if (this.downloadingFile) {
+						this.downloadList.push(item.file_path)
+					} else
+						this.downloadErrorLog(item.file_path)
+				}
+
+			},
+			async clickDownloadLog() {
+				try {
+					this.downloadingTip = this.translate("package_download_logs")
+					this.downloadProgress = 0
+					this.downloading = true
+					const startTime = this.start_time ? TaskInit.dateUtils.formatDate(new Date(this.start_time),
+						"yyyyMMdd_hhmmss") : ""
+					const endTime = this.start_time ? TaskInit.dateUtils.formatDate(new Date(this.end_time),
+						"yyyyMMdd_hhmmss") : ""
+					const res = await tarErrorLog(this.ip, startTime, endTime)
+					if (res) {
+						const list = await queryErrorTaskStatus(this.ip, [res]) || []
+						if (list.length > 0) {
+							const item = list[0]
+							const path = item.path || ""
+							if (!path.trim()) {
+								showToast(this.translate("download_log_fail_not_get_filepath"))
+								return
+							}
+							if (this.downloadingFile) {
+								this.downloadList.push(path)
+							} else
+								this.downloadErrorLog(path)
+
+						} else {
+							this.downloading = false
+							showError(this.translate('download_logs_fail'))
+						}
+					} else {
+						this.downloading = false
+						showError(this.translate('download_logs_fail'))
+					}
+				} catch (ex) {
+					showError(ex)
+					this.downloading = false
+				} finally {
+
+
+				}
+			},
+
+			downloadErrorLog(path) {
+				const _this = this
+				this.downloading = true
+				this.downloadProgress = 0
+				this.downloadingFile = path
+				this.downloadingTip = `${this.translate("downloading_log")}锛�{this.downloadProgress}%`
+				downloadError(this.ip, path, (temppath) => {
+						console.log("涓嬭浇鎴愬姛锛�, temppath)
+						var pathArr = temppath.split("/")
+						if (pathArr.length === 1) {
+							pathArr = temppath.split("\\")
+						}
+						const fileName = pathArr.pop()
+						_this.downloading = false
+						_this.downloadProgress = 100
+
+						TaskInit.fileUtils.moveToLocal(temppath,"download", fileName).then((res) => {
+							console.log("淇濆瓨鎴愬姛锛�, res)
+							this.fileList.push(fileName)
+							showToast(_this.translate("download_success"))
+
+						}).catch((err) => {
+							console.log("淇濆瓨澶辫触锛�, err)
+							showToast(_this.translate("failed_save_file"))
+
+						}).finally(() => {
+							_this.downloadNextLogFile()
+						})
+
+
+					},
+					(docProgress, downloadHandle) => {
+						console.log("涓嬭浇杩涘害锛�, docProgress)
+						_this.setData({
+							downloadProgress: docProgress.progress,
+							downloadingTip: `${this.translate("downloading_log")}锛�{docProgress.progress}%`
+						})
+					},
+					(err) => {
+						_this.downloading = false
+						_this.downloadNextLogFile()
+						showError(err)
+					})
+
+			},
+			downloadNextLogFile() {
+				if (this.downloadList.length > 0) {
+					const path = this.downloadList.shift()
+					this.downloadErrorLog(path)
+				} else {
+					this.downloadingFile = ""
+				}
+
+			},
+			scrollView(e) { //nvue鏆備笉鏀寔婊氬姩鐩戝惉锛屽彲鐢╞indingx浠f浛
+				//console.log("scrollView锛� + e.scrollTop);
+				this.old.scrollTop = e.detail.scrollTop
+			},
+
+			onPulling(e) {
+
+				this.old.triggered = e.detail.triggered
+				console.log("onPulling", this.old.triggered)
+			},
+			async onRefresh() {
+				try {
+					const _this = this
+
+					this.errorHistory = await getErrorHistory(this.ip) || []
+					this.errorList = await getErrorList(this.ip) || []
+
+					this.triggered = this.old.triggered
+					this.$nextTick(function() {
+						_this.triggered = false
+					});
+					showToast(this.translate("refresh_success"))
+					this.$forceUpdate();
+				} catch (ex) {
+					showToast(this.translate("refresh_failed_retry"))
+				}
+			},
+
+			onRestore() {
+				//	console.log("onRestore");
+				this.triggered = 'restore'; // 闇�閲嶇疆
+			},
+			onAbort() {
+				//	console.log("onAbort");
+			},
+
+			translate(t) {
+				if (typeof this.$t == "function") return this.$t(`page.${t}`)
+				else return t;
+			},
+
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.pages-error {
+		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;
+			overflow: auto;
+
+
+			.list-tip {
+				width: 100%;
+				padding: 15rpx;
+				color: #888;
+				font-size: 25rpx;
+			}
+
+			.center {
+				text-align: center;
+			}
+
+			.list-view {
+				width: 100%;
+				//flex: 1;
+				border-radius: 10rpx;
+				background-color: #fff;
+
+				.list-item {
+
+					border-bottom: 1px solid #ddd;
+					display: flex;
+					flex-direction: row;
+					padding: 10rpx;
+				}
+
+				.list-item:last-child {
+					border-bottom: 0;
+					/* 鍙充笅瑙�*/
+				}
+
+			}
+
+			.list-line {
+				display: flex;
+				flex-direction: row;
+
+				.label {
+					font-size: 32rpx;
+					flex: 1;
+				}
+
+				.value {
+					font-size: 32rpx;
+					color: #888;
+				}
+
+				.icon {
+					margin: 5rpx;
+				}
+
+			}
+
+			.button-line {
+				margin: 20rpx;
+
+				.button {
+					margin: auto;
+					width: 500rpx !important;
+					border-radius: 40rpx;
+					height: 80rpx;
+					line-height: 50rpx;
+				}
+			}
+
+
+			.download-progress {
+				display: flex;
+				flex-direction: column;
+				width: 100%;
+
+				.progress {
+					width: 100%;
+
+				}
+
+				.tip {
+					margin-top: 10rpx;
+					padding: 10rpx;
+					text-align: center;
+				}
+			}
+
+
+
+		}
+
+
+
+	}
+</style>
\ No newline at end of file
diff --git a/pages/error/infos/log-item.vue b/pages/error/infos/log-item.vue
new file mode 100644
index 0000000..3f61682
--- /dev/null
+++ b/pages/error/infos/log-item.vue
@@ -0,0 +1,229 @@
+<template>
+	<view class="pages-error-log-item">
+		<view class="item-content">
+			<view class="content">
+				<view class="line">
+					<view class="title"> {{description}}</view>
+				</view>
+				<view class="line">
+					<view class="time gray">
+						{{translate('create_time')+":"+logData.happen_time}}
+					</view>
+					<view class="gray">
+						{{translate('module_id')+":"+logData.module_code}}
+					</view>
+				</view>
+				<view class="line">
+					<view class="time gray">
+						{{translate('end_time')+":"+logData.latest_happen_time}}
+					</view>
+					<view class="gray">
+						{{translate('error_code')+":"+logData.error_code}}
+					</view>
+				</view>
+			</view>
+
+
+			<view class="action-button" v-if="!isRuntime ">
+				<button type="primary" v-if="!waiting && logData.file_path" :plain="true" class="button" @click.stop="clickDownload">
+					<uni-icons type="download" size="20" color="#1890FF"></uni-icons>
+				</button>
+				<view v-else> {{downloading? translate("downloading") : translate("wait")}}</view>
+			</view>
+		</view>
+		<view class="progress" v-if="waiting"> <progress :percent="downloadProgress" activeColor="#10AEFF"
+				stroke-width="3" />
+		</view>
+	</view>
+</template>
+
+<script>
+	import {
+		showToast,
+		showModal
+	} from "@/comm/utils.js"
+	import {
+		Button
+	} from 'antd-mobile-vue-next'
+	export default {
+		name: "PagesErrorLogItem",
+		components: {
+			'a-button': Button
+		},
+		props: {
+			logData: {
+				type: Object,
+				default () {
+					return {};
+				}
+			},
+			isRuntime: {
+				type: Boolean,
+				default () {
+					return false;
+				}
+			},
+			language: {
+				type: String,
+				default () {
+					return "";
+				}
+			},
+			downloading: {
+				type: Boolean,
+				default () {
+					return false;
+				}
+			},
+			waiting: {
+				type: Boolean,
+				default () {
+					return false;
+				}
+			},
+			downloadProgress: {
+				type: Number,
+				default () {
+					return 0;
+				}
+			},
+		},
+
+		data() {
+			return {}
+		},
+		computed: {
+
+			description() {
+				if (this.language) {
+					const desc = this.logData["description_" + this.language]
+					if (desc) {
+						return desc
+					}
+				}
+				return this.logData.description
+			}
+
+		},
+
+		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];
+					});
+				});
+			},
+			clickDownload() {
+				this.$emit('click-download', this.logData)
+			},
+
+			translate(t) {
+				if (typeof this.$t == "function") return this.$t(`page.${t}`)
+				else return t;
+			},
+		}
+	}
+</script>
+
+<style lang="less" scoped>
+	.pages-error-log-item {
+
+		width: 100%;
+		display: flex;
+		flex-direction: column;
+
+		.progress {
+			width: 100%;
+
+		}
+
+		.item-content {
+			width: 100%;
+			display: flex;
+			flex-direction: row;
+			align-items: center;
+		}
+
+		.active {
+			background-color: #1890FF;
+			color: #fff;
+		}
+
+		.content {
+			display: flex;
+			flex-direction: column;
+			flex: 1;
+			min-width: 0;
+			margin: 10rpx;
+
+			.line {
+				margin: 5rpx 0;
+				width: 100%;
+				display: flex;
+				flex-direction: row;
+
+				.title {
+					font-size: 36rpx;
+					font-weight: 600;
+					word-wrap: break-word;
+					word-break: break-all;
+				}
+
+				.time {
+					flex: 1;
+				}
+
+				.gray {
+					color: #aaa;
+					font-size: 24rpx;
+				}
+			}
+		}
+
+		.action-button {
+			display: flex;
+			flex-direction: row;
+			margin-left: 10rpx;
+
+			.button {
+				display: flex;
+				margin: 10rpx;
+				border-radius: 50%;
+				width: 72rpx;
+				height: 72rpx;
+				justify-content: center;
+				align-items: center;
+
+				.ico {
+					font-size: 30rpx;
+					color: #1890FF;
+				}
+
+
+
+			}
+
+			.disabled {
+				color: gray !important;
+			}
+
+
+		}
+
+	}
+</style>
\ No newline at end of file
diff --git a/pages/index/backup.vue b/pages/index/backup.vue
index acdc864..241ef59 100644
--- a/pages/index/backup.vue
+++ b/pages/index/backup.vue
@@ -25,14 +25,20 @@
 					<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 class="space">{{translate('no_scene_matching_criteria')}}</view>
 					</view>
-					<view class="list-tip">璇烽�鎷╀笂浼犲埌鎵嬫満绔殑鍦烘櫙</view>
+					<view class="list-tip">{{translate('select_scene_to_upload_mobile')}}</view>
 					<view>
 
-						<a-button :disabled="loading|| sceneIndex < 0" type="primary" class="button"
-							@click="clickSaveToLocal">涓婁紶鑷虫墜鏈虹</a-button>
+						<a-button v-if="!loading" :disabled=" sceneIndex < 0" type="primary" class="button"
+							@click="clickSaveToLocal">{{translate('upload_to_mobile')}}</a-button>
+
 					</view>
+					<view v-if="loading && loadingPage== 0" class="list-progress">
+						{{translate("scene_uploading") }}...
+						<view class="spinner"></view>
+					</view>
+
 				</view>
 
 			</swiper-item>
@@ -51,13 +57,19 @@
 					<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 class="space">{{translate('no_scene_matching_criteria')}}</view>
 					</view>
-					<view class="list-tip">璇烽�鎷╀笅杞藉埌杞﹁締绔殑鍦烘櫙</view>
+					<view class="list-tip">{{translate('select_scene_to_download_vehicle')}}</view>
 					<view>
-						<a-button :disabled="loading|| sceneIndex2< 0" type="primary" class="button"
-							@click="clickSaveToVehicle">涓嬭浇鑷宠溅杈嗙</a-button>
+						<a-button v-if="!loading" :disabled="sceneIndex2< 0" type="primary" class="button"
+							@click="clickSaveToVehicle">{{translate('download_to_vehicle')}}</a-button>
+
 					</view>
+					<view v-if="loading && loadingPage== 1" class="list-progress">
+						{{translate("scene_downloading") }}...{{saveDBDataStatusStr}}
+						<view class="spinner"></view>
+					</view>
+
 				</view>
 
 			</swiper-item>
@@ -79,11 +91,17 @@
 		getAllScene,
 		handoffScene,
 		saveDBData,
-		getDBData
+		getDBData,
+		getSaveDBDataStatus,
+		cancelSaveDBData
+
 	} from "@/api/vehicle.js"
 	import {
 		Button
 	} from 'antd-mobile-vue-next'
+	import {
+		async
+	} from "rxjs"
 	export default {
 		name: "PagesBackup",
 		components: {
@@ -95,21 +113,76 @@
 				sceneIndex: -1,
 				sceneIndex2: -1,
 				curPageIndex: 0,
+				loadingPage: 0,
+				opType: "",
 				pageList: [{
-					name: "涓婁紶鍦烘櫙",
-					title: "ES-GO1鍦烘櫙鍒楄〃",
+					name: this.translate('upload_scenes'),
+					title: `ES-GO1 ${this.translate('scene_list')}`,
 					list: []
 				}, {
-					name: "涓嬭浇鍦烘櫙",
-					title: "鎵嬫満绔繚瀛樼殑鍦烘櫙鍒楄〃",
+					name: this.translate('download_scenes'),
+					title: this.translate('scene_list_on_cellphone'),
 					list: []
-				}]
+				}],
+				saveDBDataStatus: 0,
 			}
 		},
-		computed: {},
+		computed: {
+
+			saveDBDataStatusStr() {
+				if (this.saveDBDataStatus == 2) {
+					return this.translate("saved_success")
+				} else if (this.saveDBDataStatus == 3) {
+					return this.translate("saved_fail")
+				} else {
+					return this.translate("saving")
+				}
+			}
+		},
 		onLoad(option) {
 			this.ip = option.ip || ""
+			this.opType = option.opType || ""
+			if (this.opType == "download") {
+				this.curPageIndex = 1
+			}
+
+			uni.setNavigationBarTitle({
+				title: this.translate('scene_backup')
+			})
 			this.loadData()
+		},
+		onBackPress() {
+
+			const _this = this
+			if (this.loading) {
+				showModal({
+					title: this.translate("ask_exit_page"),
+					content: this.loadingPage == 1 ? `${this.translate('scene_downloading')}` :
+						`${this.translate('scene_uploading')}`,
+					confirmText: this.translate('yes'),
+					cancelText: this.translate("no"),
+				}).then(async (res) => {
+					if (res) {
+
+						await _this.cancelSaveDBData()
+						_this.saveDBDataStatus = 3
+						_this.setData({
+							loading: false
+						})
+						uni.navigateBack({
+							delta: 1, //杩斿洖灞傛暟锛�鍒欎笂涓婇〉
+						})
+					}
+
+				})
+				return true
+			}
+			if (this.opType == "download") {
+				uni.reLaunch({
+					url: "/pages/index/index"
+				})
+				return true
+			}
 		},
 		methods: {
 			setData(obj) {
@@ -137,10 +210,10 @@
 				try {
 					this.pageList[0].list = await this.loadScene()
 					this.pageList[1].list = await this.loadLocalScene()
-					
+
 					console.log(this.pageList[1].list)
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 			},
 			async loadScene() {
@@ -149,7 +222,7 @@
 					const list = res?.sceneList || []
 					return list
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 					return []
 				}
 			},
@@ -158,7 +231,7 @@
 					const list = session.getValue("scene_db") || []
 					return list
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 					return []
 				}
 			},
@@ -197,12 +270,17 @@
 				const _this = this
 				let list = this.pageList[1].list
 				if (list.length > 0) {
-					showModal(`涓婁紶璇ュ満鏅細鏇挎崲鎺夊綋鍓嶅凡涓婁紶鐨勫満鏅痐, "鏄惁瑕佽鐩栧満鏅紵").then(async (res) => {
+					showModal({
+						title: this.translate('confirm_replace_scene'),
+						content: this.translate('uploading_will_replace_currently_uploaded_scene'),
+						confirmText: this.translate("confirm"),
+						cancelText: this.translate("cancel"),
+					}).then(async (res) => {
 						if (res) {
 							_this.saveToLocal(this.sceneIndex)
 						}
 					})
-					
+
 					return
 				}
 				_this.saveToLocal(this.sceneIndex)
@@ -210,32 +288,33 @@
 			async saveToLocal(index) {
 				try {
 					this.setData({
-						loading: true
+						loading: true,
+						loadingPage: 0,
 					})
-					uni.showLoading({
-						title:"姝e湪涓婁紶鍦烘櫙"
-					})
+
+					// uni.showLoading({
+					// 	title: `${this.translate('upload_scenes')}...`
+					// })
 					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 ||[] 
+						data: res || []
 					}]
 					session.setValue("scene_db", list)
 					this.pageList[1].list = list
 					this.sceneIndex2 = -1
-					showToast(`涓婁紶鍦烘櫙[${scene}]鎴愬姛`)
+					showToast(this.translate('upload_success'))
 				} catch (ex) {
-					showError(ex)
-				
-				}
-				finally {
+					showError(ex, this.translate('error'))
+
+				} finally {
 					this.setData({
 						loading: false
 					})
-					uni.hideLoading()
+					// uni.hideLoading()
 				}
 
 			},
@@ -244,43 +323,94 @@
 				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)
+				console.log(curIndex, scene, list)
 				if (curIndex > -1) {
-					showModal(`涓嬭浇璇ュ満鏅細鏇挎崲鎺夊凡涓嬭浇鐨勫悓鍚嶅満鏅�`, "鏄惁瑕佽鐩栧満鏅紵").then(async (res) => {
+					showModal({
+						title: this.translate('confirm_replace_scene'),
+						content: this.translate('downloading_will_replace_currently_downloaded_scene'),
+						confirmText: this.translate("confirm"),
+						cancelText: this.translate("cancel"),
+					}).then(async (res) => {
 						if (res) {
 							_this.saveToVehicle(this.sceneIndex2)
 						}
 					})
-					
+
 					return
 				}
 				_this.saveToVehicle(this.sceneIndex2)
 			},
+			async cancelSaveDBData() {
+				try {
+					await cancelSaveDBData(this.ip)
+				} catch (ex) {
+					showToast(ex)
+				}
+			},
+			async checkSaveDBDataStatus() {
+				try {
+
+					if (this.saveDBDataStatus === 2 || this.saveDBDataStatus === 3) {
+						this.setData({
+							loading: false
+						})
+						if (this.saveDBDataStatus === 2) {
+							const scene = this.pageList[1].list[this.sceneIndex2]?.name
+							this.pageList[0].list.push(scene)
+							showToast(this.translate('download_success'))
+						}
+						return
+					}
+					this.saveDBDataStatus = await getSaveDBDataStatus(this.ip) || 0
+
+					if (this.saveDBDataStatus === 2 || this.saveDBDataStatus === 3) {
+						this.setData({
+							loading: false
+						})
+						if (this.saveDBDataStatus === 2) {
+							const scene = this.pageList[1].list[this.sceneIndex2]?.name
+							this.pageList[0].list.push(scene)
+							showToast(this.translate('download_success'))
+						} else {
+							showToast(`${this.translate('update_fail')}, ${this.translate('saved_fail')} `)
+						}
+						return
+					}
+					setTimeout(this.checkSaveDBDataStatus, 1000);
+				} catch (ex) {
+					showToast(ex)
+					setTimeout(this.checkSaveDBDataStatus, 1000);
+				}
+			},
+
 			async saveToVehicle(index) {
 				try {
 					this.setData({
-						loading: true
+						loading: true,
+						loadingPage: 1,
 					})
-					uni.showLoading({
-						title:"姝e湪涓嬭浇鍦烘櫙"
-					})
-					
+					// uni.showLoading({
+					// 	title: `${this.translate('download_scenes')}...`
+					// })
+
 					const data = this.pageList[1].list[index].data
 					const scene = this.pageList[1].list[index].name
+					this.saveDBDataStatus = 1
 					await saveDBData(this.ip, data)
-					this.pageList[0].list.push(scene)
-					showToast(`涓嬭浇鍦烘櫙[${scene}]鎴愬姛`)
-					
+					this.checkSaveDBDataStatus()
+
+
 				} catch (ex) {
-					showError(ex)
-				}
-				finally {
 					this.setData({
 						loading: false
 					})
-					uni.hideLoading()
+					showError(ex, this.translate('error'))
 				}
 
+			},
+			translate(t) {
+				if (typeof this.$t == "function") return this.$t(`page.${t}`)
+				else return t;
 			},
 
 
@@ -298,7 +428,7 @@
 
 		.switch-type {
 			width: calc(100% - 40rpx);
-			margin: 20rpx  20rpx 10rpx 20rpx;
+			margin: 20rpx 20rpx 10rpx 20rpx;
 			align-items: center;
 			vertical-align: middle;
 
@@ -345,32 +475,40 @@
 			flex-direction: column;
 			padding: 0 10rpx;
 
+			.list-progress {
+				width: 100%;
+				text-align: center;
+				margin: 20rpx;
+			}
+
 			.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 ;
+				max-height: 500rpx;
+
 				.list-item {
 
 					border-bottom: 1px solid #ddd;
 					display: flex;
 					flex-direction: row;
-					padding:20rpx;
+					padding: 20rpx;
 
 					.list-name {
 						flex: 1;
 						margin-right: 20rpx;
 						font-size: 32rpx;
 						font-weight: 700;
-						padding:10rpx 0;
+						padding: 10rpx 0;
 					}
 				}
 
@@ -404,13 +542,33 @@
 			}
 
 			.am-button {
-				border-radius: 90rpx;
-				height: 90rpx;
+				border-radius: 40rpx;
+				height: 80rpx;
 				line-height: 60rpx;
 			}
 
 		}
 
+		/* CSS鍔ㄧ敾绀轰緥锛氭棆杞殑鍦嗙幆 */
+		.spinner {
+			width: 100rpx;
+			height: 100rpx;
+			border: 4px solid #f3f3f3;
+			border-top: 4px solid #007aff;
+			border-radius: 50%;
+			animation: spin 1s linear infinite;
+			margin: 20rpx auto;
+		}
+
+		@keyframes spin {
+			0% {
+				transform: rotate(0deg);
+			}
+
+			100% {
+				transform: rotate(360deg);
+			}
+		}
 
 
 	}
diff --git a/pages/index/connect.vue b/pages/index/connect.vue
index 9fddb1c..4809a1f 100644
--- a/pages/index/connect.vue
+++ b/pages/index/connect.vue
@@ -2,51 +2,51 @@
 	<view class="pages-vehicle-connect">
 
 		<view class="content" v-if="connectState == 1">
-			<view class="title">杩炴帴涓�/view>
+			<view class="title">{{translate('connecting')}}</view>
 			<view class="content2">
 				<view class="auto-circle"></view>
-				<view>杩炴帴涓�..</view>
+				<view>{{translate('connecting')}}...</view>
 			</view>
 		</view>
 		<view class="content" v-else-if="connectState == 2">
-			<view class="title">杩炴帴鎴愬姛</view>
+			<view class="title">{{translate('connection_success')}}</view>
 			<view class="content2">
-				<image class="img" src="/images/Frame_178.svg" alt=" 鍥剧墖" />
-				<view>銆恵{connectedDevice}}銆戣繛鎺ユ垚鍔�/view>
+				<image class="img" src="/images/Frame_178.svg" alt=" picture" />
+				<view>銆恵{connectedDevice}}銆憑{translate('connection_success')}}</view>
 			</view>
 		</view>
 		<view class="content" v-else-if="connectState == 3">
-			<view class="title">杩炴帴澶辫触</view>
+			<view class="title">{{translate('connection_failed')}}</view>
 			<view class="content2">
-				<image class="img" src="/images/Frame_179.svg" alt=" 鍥剧墖" />
-				<view class="title">鏃犳硶杩炴帴璁惧</view>
-				<view>璇风‘淇濊溅杈嗗紑鏈轰笖鏈笌鍏朵粬缁堢閰嶅</view>
+				<image class="img" src="/images/Frame_179.svg" alt=" picture" />
+				<view class="title">{{translate('unable_connect_device')}}</view>
+				<view>{{translate('check_vehicle_not_paired_other_terminals')}}</view>
 			</view>
 		</view>
 		<view class="content" v-else-if="connectState == 4">
-			<view class="title">杩炴帴鍑嗗</view>
+			<view class="title">{{translate('connection_preparation')}}</view>
 			<view class="content2">
 
-				<view>1銆佽灏嗘墜鏈鸿繛鎺ュ埌鈥榹{ip}}鈥欑殑Wifi缃戠粶</view>
-				<view>2銆佽繛鎺ュ悗鍥炲埌鏈簲鐢�/view>
-				<image class="img-2" src="/images/Frame_180.svg" alt=" 鍥剧墖" />
+				<view>1銆亄{translate("connect_phone_to_this_ip_wifi",[ip]) }}</view>
+				<view>2銆亄{translate('after_connect_return_this')}}</view>
+				<image class="img-2" src="/images/Frame_180.svg" alt=" picture" />
 			</view>
 		</view>
 		<view class="content" v-else>
-			<view class="title">杩炴帴鍑嗗</view>
+			<view class="title">{{translate('connection_preparation')}}</view>
 		</view>
 		<view v-if="connectState == 3" class="button-group">
 			<a-button type="primary" plain="true" class="button" @click="clickTry">
-				{{reconnectFlag ? "閲嶆柊杩炴帴WiFi":"閲嶈瘯"}}
+				{{reconnectFlag ? translate('reconnect_wifi'):translate('retry')}}
 			</a-button>
-			<a-button  type="ghost" class="button" @click="clickCancel">鍙栨秷
+			<a-button type="ghost" class="button" @click="clickCancel">{{translate('cancel')}}
 			</a-button>
-			<view class="link-text"> <a @click="clickStudyMore">瀛︿範鏇村...</a> </view>
+			<view class="link-text"> <a @click="clickStudyMore">{{translate('more')}}...</a> </view>
 		</view>
 		<view v-if="connectState == 4" class="button-group">
-			<a-button type="primary" plain="true" class="button" @click="clickLinkWifi">鍘昏繛鎺�+			<a-button type="primary" plain="true" class="button" @click="clickLinkWifi">{{translate('go_connect')}}
 			</a-button>
-			<a-button  type="ghost" class="button" @click="clickTry">鍒囨崲鎵爜杩炴帴
+			<a-button type="ghost" class="button" @click="clickTry">{{translate('switch_scan_connection')}}
 			</a-button>
 		</view>
 	</view>
@@ -96,6 +96,7 @@
 			this.wifiPassword = option.passwpord || ""
 			this.reconnectFlag = option.reconnect ? true : false
 			this.unloadFlag = false
+
 			this.loadData()
 		},
 		onUnload() {
@@ -145,7 +146,7 @@
 
 
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 			},
 			checkConnectStatus() {
@@ -183,11 +184,11 @@
 							const result = res.result || ""
 							const arCode = result.split(";")
 							if (arCode.length != 3) {
-								showInfo("鏃犳晥鐨勪簩缁寸爜锛�)
+								showToast(this.translate('invalid_qr_code'))
 								return
 							}
 							if (!arCode[0].trim() || !arCode[0].trim()) {
-						showInfo("鏃犳晥鐨勪簩缁寸爜锛�)
+								showToast(this.translate('invalid_qr_code'))
 								return
 							}
 							that.ip = arCode[0]
@@ -236,7 +237,12 @@
 				console.log("connectWifi", ssid)
 				if (`"${this.wifiSID}"` != ssid && `${this.wifiSID}` != ssid) {
 					if (platform == "android" && osVersion < 10) {
-						showModal(`銆�{app.globalData.deviceModel}銆戞兂鍔犲叆鏃犵嚎灞�煙缃戙�${this.wifiSID}銆戝悧锛焋, "璇㈤棶").then((res) => {
+						showModal({
+								content: `${this.translate('ask_join_wlan',[this.this.ip,this.wifiSID])}`,
+								confirmText: this.translate('join'),
+								cancelText: this.translate("cancel"),
+							}
+						).then((res) => {
 							if (res) {
 								this.connectState = 1
 
@@ -291,9 +297,9 @@
 					this.connectState = 2
 					this.connectedSuccess()
 				} catch (ex) {
-					console.log("connectVehicle faile",this.ip,ex)
+					console.log("connectVehicle faile", this.ip, ex)
 					this.connectState = 3
-					// showError(ex)
+					// showError(ex,this.translate('error'))
 				}
 			},
 
@@ -344,8 +350,7 @@
 				if (this.unloadFlag) {
 					return
 				}
-				if(sec <= 0)
-				{
+				if (sec <= 0) {
 					this.connectState = 3
 					return
 				}
@@ -358,9 +363,20 @@
 					}
 				}).catch((ex) => {
 					setTimeout(() => {
-						this.checkConnectVehicle(sec- 7)
+						this.checkConnectVehicle(sec - 7)
 					}, 2000)
 				})
+			},
+			translate(t, values) {
+				if (typeof this.$t == "function") {
+					const message = this.$t(`page.${t}`)
+					if (values) {
+						return message.replace(/{(\d+)}/g, (match, index) => {
+							const value = values[index]
+							return value !== undefined ? value : match
+						})
+					} else return message
+				} else return t;
 			},
 		}
 	}
@@ -438,7 +454,7 @@
 
 			.button {
 				margin: 20rpx !important;
-				width: 320rpx !important;
+				width: 400rpx !important;
 				border-radius: 4rpx;
 			}
 		}
diff --git a/pages/index/detail.vue b/pages/index/detail.vue
index c615aa2..a9b3e14 100644
--- a/pages/index/detail.vue
+++ b/pages/index/detail.vue
@@ -1,22 +1,22 @@
 <template>
 	<view class="pages-vehicle-detail">
-		<uni-nav-bar :fixed="true" status-bar right-text="" left-text="" leftWidth="72rpx" rightWidth="72rpx"
-			:title="navigationBarTitle">
+		<uni-nav-bar :fixed="true" status-bar :title="navigationBarTitle" @clickLeft="clickCancel"
+			@clickRight="clickSave">
 			<view class="uni-navbar-container-inner">
 				<text class="uni-nav-bar-text">{{navigationBarTitle }}</text>
 			</view>
 			<template v-slot:right>
-				<view class="uni-navbar-btn-text" @click="clickSave">
-					<a  class="uni-nav-bar-right-text">
-						淇濆瓨
+				<view class="uni-navbar-btn-text">
+					<a class="uni-nav-bar-right-text">
+						{{translate('save')}}
 					</a>
 				</view>
 
 			</template>
 			<template v-slot:left>
-				<view class="uni-navbar-btn-text" @click="clickCancel">
-					<a  class="uni-nav-bar-left-text">
-						鍙栨秷
+				<view class="uni-navbar-btn-text">
+					<a class="uni-nav-bar-left-text">
+						{{translate('cancel')}}
 					</a>
 				</view>
 
@@ -25,25 +25,25 @@
 		<view class="content">
 			<view class="group">
 				<view class="item line">
-					<view>鍚嶇О锛�/view>
-					<input class="input" v-model="vehicleName"/>
+					<view>{{translate('name')}}锛�/view>
+					<input class="input" v-model="vehicleName" />
 				</view>
 				<view class="item line">
-					<view>ip鍦板潃锛�/view>
+					<view>{{translate('ip_address')}}锛�/view>
 					<view class="text">{{vehicleInfo.ip }} </view>
 				</view>
 				<view class="item line">
-					<view>鍨嬪彿锛�/view>
+					<view>{{translate('model')}}锛�/view>
 					<view class="text">{{vehicleInfo.model_number|| ""}}</view>
 				</view>
 				<view class="item line">
-					<view>杞﹁浇鐗堟湰锛�/view>
+					<view>{{translate('car_version')}}锛�/view>
 					<view class="text">{{car_version|| ""}}</view>
 				</view>
-				<view class="item line">
-					<view>鎺堟潈鏈夋晥鏈燂細</view>
+				<!-- <view class="item line">
+					<view>{{translate('authorization_validity_period')}}锛�/view>
 					<view class="text">{{vehicleInfo.authorization_period|| ""}}</view>
-				</view>
+				</view> -->
 				<!-- <view class="item" @click="clickUpgrade">
 					<view>鍥轰欢鐗堟湰锛�/view>
 					<view class="text2">{{firmware_version|| ""}}</view>
@@ -54,7 +54,7 @@
 			</view>
 			<view class="group">
 				<view class="item line">
-					<view>鏃犵嚎灞�煙缃戝湴鍧�細</view>
+					<view>{{translate('wifi_address')}}锛�/view>
 					<!-- 	<input class="input" v-model="vehicleInfo.wifi.mac"> </input> -->
 					<view class="text">{{vehicleInfo.wifi?.mac|| ""}}</view>
 				</view>
@@ -63,7 +63,7 @@
 					<view class="text">{{vehicleInfo.bluetooth|| ""}}</view>
 				</view> -->
 			</view>
-			<a-button  type="ghost" class="button" @click="clickDelete">鍒犻櫎杞﹁締</a-button>
+			<a-button type="ghost" class="button" @click="clickDelete">{{translate('remove_vehicle')}}</a-button>
 		</view>
 
 	</view>
@@ -131,14 +131,18 @@
 					const info = await shellVersion(this.vehicleIp)
 					this.car_version = info.software_version
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 			},
 			clickDelete() {
-				showModal(`纭畾瑕佸垹闄よ澶団�${ this.vehicleInfo.name}鈥濆悧`, "璀﹀憡",true,"纭畾","鍙栨秷").then((res) => {
+				showModal({
+					content: `${this.translate('ask_delete_device')}`,
+					confirmText: this.translate('remove'),
+					cancelText: this.translate("cancel"),
+				}).then((res) => {
 					if (res) {
 						const eventChannel = this.getOpenerEventChannel();
-						eventChannel.emit('delete_vehicle', this.vehicleInfo);
+						eventChannel.emit('remove_vehicle', this.vehicleInfo);
 						uni.navigateBack({
 							delta: 1, //杩斿洖灞傛暟锛�鍒欎笂涓婇〉
 						})
@@ -166,6 +170,10 @@
 					delta: 1
 				})
 			},
+			translate(t) {
+				if (typeof this.$t == "function") return this.$t(`page.${t}`)
+				else return t;
+			},
 
 
 		}
diff --git a/pages/index/index.vue b/pages/index/index.vue
index e3c22da..f5a63d3 100644
--- a/pages/index/index.vue
+++ b/pages/index/index.vue
@@ -1,6 +1,6 @@
 <template>
 	<view class="pages-main">
-		<uni-nav-bar :fixed="true" status-bar right-text="" left-text="" :leftWidth="0" rightWidth="72px"
+		<uni-nav-bar :fixed="true" status-bar right-text="" left-text="" :leftWidth="0" rightWidth="150rpx"
 			:title="navigationBarTitle">
 			<view class="uni-navbar-container-inner">
 				<text class="uni-nav-bar-text">{{navigationBarTitle }}</text>
@@ -20,82 +20,91 @@
 		<swiper v-if="pageList.length > 0" circular indicator-dots class="swiper" :current="currentPage"
 			@change="changeSwiper">
 			<swiper-item v-for="(page,index) in pageList" class="swiper-item">
-			
-					<view class="vehicle-header">
-						<view class="link-view">
-							<view v-if="page.link_status">
-								<!-- <image class="img-battery-charging" src="/images/bx_battery3.svg" alt="SVG 鍥剧墖" /> -->
-								<image class="img-battery-charging" src="/images/bx_battery2.svg" alt="SVG 鍥剧墖" />
-								<image class="img-battery-soc" :style="{width:(page.soc*0.4)+'rpx'}"
-									src="/images/bx_battery4.svg" alt="SVG 鍥剧墖" />
 
-							</view>
-							<view v-else>
-								<image class="img-battery" src="/images/bx_battery1.svg" alt="SVG 鍥剧墖" />
-							</view>
-							<template v-if="page.link_status">
-								<view class="soc-text">{{page.soc}}%</view>
-								<view class="status-text">{{getAgvStateText(page)}}</view>
-							</template>
+				<view class="vehicle-header">
+					<view class="link-view">
+						<view v-if="page.link_status">
+							<!-- <image class="img-battery-charging" src="/images/bx_battery3.svg" alt="SVG" /> -->
+							<image class="img-battery-charging" src="/images/bx_battery2.svg" alt="SVG" />
+							<image class="img-battery-soc" :style="{width:(page.soc*0.4)+'rpx'}"
+								src="/images/bx_battery4.svg" alt="SVG" />
 
-
-							<view v-else class="gray-text">宸茬绾�/view>
 						</view>
+						<view v-else>
+							<image class="img-battery" src="/images/bx_battery1.svg" alt="SVG" />
+						</view>
+						<template v-if="page.link_status">
+							<view class="soc-text">{{page.soc}}%</view>
+							<view class="status-text">{{getAgvStateText(page)}}</view>
+						</template>
+
+
+						<view v-else class="gray-text">{{translate('offline')}}</view>
 					</view>
-					<image class="vehicle-img " :class="page.link_status ?'':'gray-image'" mode="aspectFit"
-						src="/images/che.png" alt="鍥剧墖" />
-					<view v-if="page.link_status" class="img-button-group">
-						<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>
+				</view>
+				<image class="vehicle-img " :class="page.link_status ?'':'gray-image'" mode="aspectFit"
+					src="/images/che.png" alt="picture" />
+				<view v-if="page.link_status" class="img-button-group">
+					<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>{{translate('scenes')}}</text>
 
-						</view>
-						<view type="primary" class="img-text-button" @click="clickToTask(page)">
+					</view>
+					<navigator :url="`/pages/task/log-list?ip=${page.ip}`" hover-class="other-navigator-hover">
+						<view type="primary" class="img-text-button">
 							<a-button class="img-button" color='primary'>
 								<text class="ico task-list" />
 							</a-button>
-							<text>浠诲姟璁板綍</text>
+							<text>{{translate('tasks')}}</text>
 						</view>
-					</view>
-					<view v-else class="unlink-content">
-						<view class="content2" v-if="page.conntecting">
-							<view class="auto-circle"></view>
+					</navigator>
 
-							<view class="text">杩炴帴涓�..</view>
-						</view>
-						<view class="content2" v-else>
-							<view class="text"> 杞﹁締宸茬绾匡紝璇烽噸鏂拌繛鎺�/view>
-							<a-button type="primary" class="button" @click="clickRelink">閲嶆柊杩炴帴
-							</a-button>
-						</view>
 
+				</view>
+				<view v-else class="unlink-content">
+					<view class="content2" v-if="page.conntecting">
+						<view class="auto-circle"></view>
+
+						<view class="text">{{translate('connecting')}}...</view>
 					</view>
-				
+					<view class="content2" v-else>
+						<view class="text">{{translate('vehicle_offline_to_reconnect')}}</view>
+						<a-button type="primary" class="button" @click="clickRelink">{{translate('reconnect')}}
+						</a-button>
+					</view>
+
+				</view>
+
 			</swiper-item>
-				<swiper-item class="swiper-item">
+			<swiper-item class="swiper-item">
 
-				<image class="title-img gray-image" src="/images/image_15.png" alt="鍥剧墖" />
+				<image class="title-img gray-image" src="/images/image_15.png" alt="picture" />
 				<view class="button-group">
-					<a-button type="primary" class="button" @click="clickScanCode">鎵弿娣诲姞璁惧</a-button>
-					<a-button type="ghost" class="button" @click="clickManualAdd">鎵嬪姩娣诲姞璁惧</a-button>
+					<a-button type="primary" class="button"
+						@click="clickScanCode">{{translate('scan_add_device')}}</a-button>
+					<a-button type="ghost" class="button"
+						@click="clickManualAdd">{{translate('manual_add_device')}}</a-button>
 				</view>
 			</swiper-item>
 		</swiper>
 		<view v-else class="no-page items-center">
 			<view class="content ">
-				<image class="title-img gray-image" src="/images/image_15.png" alt="鍥剧墖" />
+				<image class="title-img gray-image" src="/images/image_15.png" alt="picture" />
 				<view class="button-group">
-					<a-button type="primary" class="button" @click="clickScanCode">鎵弿娣诲姞璁惧</a-button>
-					<a-button type="ghost" class="button" @click="clickManualAdd">鎵嬪姩娣诲姞璁惧</a-button>
+					<a-button type="primary" class="button"
+						@click="clickScanCode">{{translate('scan_add_device')}}</a-button>
+					<a-button type="ghost" class="button"
+						@click="clickManualAdd">{{translate('manual_add_device')}}</a-button>
 				</view>
 			</view>
 		</view>
 		<view>
 			<uni-popup ref="refPopupInput" type="dialog">
-				<uni-popup-dialog mode="input" title="杈撳叆杞﹁締IP" :value="inputPopupValue" placeholder="杞﹁締IP"
-					:beforeClose="true" @confirm="dialogInputConfirm" @close="dialogInputClose"></uni-popup-dialog>
+				<uni-popup-dialog mode="input" :title="translate('connect_device')" :value="inputPopupValue"
+					:placeholder="translate('input_vehicle_ip')" :beforeClose="true" @confirm="dialogInputConfirm"
+					@close="dialogInputClose"></uni-popup-dialog>
 			</uni-popup>
 			<!-- 		<uni-popup ref="refPopupMessage" type="dialog">
 				<uni-popup-dialog type="info" title="" :content="inputPopupValue" 
@@ -107,7 +116,9 @@
 						<a-button class="popup-content-menu-item" v-for="(item,index) in menuList" :key="index"
 							@click="menuItemChange(item)" :style="{'color':item.color}">
 							<view class="text">{{item.text}}</view>
-							<text class="ico" :class="item.ico" />
+							<view v-if="item.id=='update' && newCarVersion" class="new" />
+							<view class="ico" :class="item.ico" />
+							<uni-icons :type="item.ico" size="20"></uni-icons>
 						</a-button>
 					</view>
 				</view>
@@ -125,20 +136,20 @@
 		showToast,
 		showModal,
 		showError,
-		showInfo
+		showInfo,
 	} from "@/comm/utils.js"
+	import TaskInit from "@/comm/extend.js"
 	import {
 		getAllScene,
 		mtBattery,
 		getAgvState,
 		checkIpLinkSuccess,
+		getShellVersion
 	} from "@/api/vehicle.js"
+
 	import {
 		Button
 	} from 'antd-mobile-vue-next'
-	import {
-		async
-	} from "rxjs";
 	export default {
 		name: "PagesMain",
 		components: {
@@ -152,30 +163,50 @@
 				timerBatteryId: 0,
 				inputPopupValue: "",
 				menuList: [{
-					ico: "edit-line",
-					text: "缂栬緫",
-					color: "black"
-				}, {
-					ico: "update",
-					text: "鏇存柊",
-					color: "black"
-				}, {
-					ico: "book",
-					text: "鏁欑▼",
-					color: "black"
-				}, {
-					ico: "share",
-					text: "鍏变韩",
-					color: "black"
-				}, {
-					ico: "copy",
-					text: "澶囦唤",
-					color: "black"
-				}, {
-					ico: "delete-outline",
-					text: "鍒犻櫎",
-					color: "red"
-				}]
+						id: "edit",
+						ico: "edit-line",
+						text: this.translate('edit'),
+						color: "black"
+					},
+					{
+						id: "update",
+						ico: "update",
+						text: this.translate('update'),
+						color: "black"
+					},
+					{
+						id: "error",
+						ico: "info",
+						text: this.translate('errors'),
+						color: "black",
+
+
+					},
+					// {
+					//	id: "tutorial",
+					// 	ico: "book",
+					// 	text: this.translate('tutorial'),
+					// 	color: "black"
+					// },
+					// {
+					//	id: "share",
+					// 	ico: "share",
+					// 	text: this.translate('share'),
+					// 	color: "black"
+					// },
+					{
+						id: "backup",
+						ico: "copy",
+						text: this.translate('backup'),
+						color: "black"
+					},
+					{
+						id: "remove",
+						ico: "delete-outline",
+						text: this.translate('remove'),
+						color: "red"
+					}
+				]
 
 			}
 		},
@@ -201,14 +232,18 @@
 		onLoad(option) {
 			const ip = option.connectedIp || ""
 			this.loadData(ip)
-			console.log("hide")
 		},
 		computed: {
 			tabList() {
 				return [...this.pageList, {
 					ip: ""
 				}]
+			},
+			newCarVersion() {
+				const page = this.pageList[this.currentPage]
+				return page?.newVer || false
 			}
+
 		},
 
 		methods: {
@@ -236,7 +271,7 @@
 			async loadData(ip) {
 
 				try {
-
+					this.loadCarSaveVersion()
 					let list = session.getValue("vehicles") || []
 
 					list.forEach((page) => {
@@ -252,7 +287,7 @@
 						if (this.pageList.length > 0)
 							this.navigationBarTitle = this.pageList[0].name
 						else
-							this.navigationBarTitle = "娣诲姞璁惧"
+							this.navigationBarTitle = ""
 						for (let i in list) {
 							const page = list[i]
 							const battery = await this.loadMTBattery(page.ip)
@@ -260,6 +295,7 @@
 								page.soc = battery
 								page.status = await this.loadAgvState(page.ip)
 								page.link_status = true
+								page.newVer = await this.checkServerVersion(page.ip)
 								this.setData({
 									currentPage: i
 								})
@@ -280,6 +316,7 @@
 							page.soc = battery
 							page.link_status = true
 							page.status = await this.loadAgvState(page.ip)
+							page.newVer = await this.checkServerVersion(page.ip)
 						} else {
 							page.link_status = false
 						}
@@ -289,7 +326,7 @@
 					}
 				} catch (ex) {
 
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 			},
 
@@ -303,6 +340,7 @@
 						if (battery > -1) {
 							page.soc = battery
 							page.status = await this.loadAgvState(page.ip)
+							page.newVer = await this.checkServerVersion(page.ip)
 							page.link_status = true
 
 						} else {
@@ -315,7 +353,7 @@
 					// this.pageList = [...list]
 
 				} catch (ex) {
-					// showError(ex)
+					// showError(ex,this.translate('error'))
 					showToast(ex)
 				}
 
@@ -349,13 +387,13 @@
 			getAgvStateText(page) {
 				let text = ""
 				if (page.status == 1) {
-					text = "绌洪棽涓�
+					text = this.translate('idle')
 				} else if (page.status == 2) {
-					text = "宸ヤ綔涓�
+					text = this.translate('at_work')
 				} else if (page.status == 3) {
-					text = "鏁呴殰"
+					text = this.translate('fault')
 				} else {
-					text = `鐘舵�${page.status}`
+					text = `${this.translate('state')}:${page.status}`
 				}
 				return text
 			},
@@ -368,6 +406,7 @@
 						page.soc = battery
 						page.link_status = true
 						page.status = await this.loadAgvState(page.ip)
+						page.newVer = await this.checkServerVersion(page.ip)
 					} else {
 						page.link_status = false
 					}
@@ -385,7 +424,7 @@
 					currentPage: index
 				})
 				if (this.pageList.length == this.currentPage)
-					this.navigationBarTitle = "娣诲姞璁惧"
+					this.navigationBarTitle = ""
 				else
 					this.navigationBarTitle = this.pageList[this.currentPage].name
 				this.loadVehicleBattery()
@@ -416,13 +455,14 @@
 							page.soc = battery
 							page.link_status = true
 							page.status = await this.loadAgvState(page.ip)
+							page.newVer = await this.checkServerVersion(page.ip)
 							this.setData({
 								pageList: list
 							})
 						}
 					}
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 			},
 			async checkConnectSuccess(ip) {
@@ -445,25 +485,28 @@
 						const result = res.result || ""
 						const arCode = result.split(";")
 						if (arCode.length != 3) {
-							showToast("鏃犳晥鐨勪簩缁寸爜锛�)
+							showToast(this.translate("invalid_qr_code"))
 							return
 						}
 						let ip = arCode[0].trim()
 						if (!ip || !arCode[0].trim()) {
-							showToast("鏃犳晥鐨勪簩缁寸爜锛�)
+							showToast(this.translate("invalid_qr_code"))
 							return
 						}
 						const curIndex = that.pageList.findIndex((a) => a.ip == ip)
 						if (curIndex < 0) {
 							if (that.pageList.length === 3) {
-								showToast("瀵瑰鍙厑璁告坊鍔�杈嗚溅锛�)
+								showToast(this.translate("only_3_vehicles_to_added"))
 								return
 							}
 							uni.navigateTo({
 								url: `/pages/index/connect?ip=${ ip}&sid=${ arCode[1]}&password=${ arCode[2]}`
 							})
 						} else {
-							showToast("涓嶅厑璁告坊鍔犵浉鍚孖P鐨勮澶囷紒")
+							showToast(this.translate('vehicle_ip_has_added'))
+							this.setData({
+								currentPage: curIndex
+							})
 						}
 					},
 				})
@@ -471,6 +514,18 @@
 			},
 			clickManualAdd() {
 				this.inputPopupValue = ""
+				// const _this = this
+				// uni.showModal({
+				// 	title: this.translate('connect_device'),
+				// 	showCancel: true,
+				// 	editable: true,
+				// 	placeholderText:this.translate('input_vehicle_ip'),
+				// 	success: function(res) {
+				// 		if (res.confirm) {
+				// 			_this.dialogInputConfirm(res.content)
+				// 		}
+				// 	}
+				// });
 				this.$refs.refPopupInput.open()
 			},
 
@@ -486,7 +541,7 @@
 					url: `/pages/index/detail?param=${JSON.stringify(page)}`,
 					events: {
 						// 涓烘寚瀹氫簨浠舵坊鍔犱竴涓洃鍚櫒锛岃幏鍙栬鎵撳紑椤甸潰浼犻�鍒板綋鍓嶉〉闈㈢殑鏁版嵁
-						delete_vehicle: function(data) {
+						remove_vehicle: function(data) {
 							const list = that.pageList
 							const index = that.currentPage
 							list.splice(index, 1)
@@ -500,7 +555,7 @@
 							// else
 							// 	that.navigationBarTitle = "娣诲姞璁惧"
 							uni.reLaunch({
-								url:"/pages/indde/index"
+								url: "/pages/indde/index"
 							})
 						},
 						update_vehicle: function(data) {
@@ -518,7 +573,7 @@
 			},
 			async clickToMap(index, page) {
 				try {
-console.log("clicktomap")
+					console.log("clicktomap")
 					const _this = this
 					const res = await getAllScene(page.ip) || []
 					const list = res?.sceneList || []
@@ -551,15 +606,8 @@
 						}
 					})
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
-
-			},
-			clickToTask(page) {
-
-				uni.navigateTo({
-					url: `/pages/task/log-list?ip=${page.ip}`
-				})
 
 			},
 			isValidIP(ip) {
@@ -574,12 +622,12 @@
 
 				let text = val.trim()
 				if (!text) {
-					showToast("璇疯緭鍏ヨ溅杈咺P锛�)
+					showToast(this.translate('input_vehicle_ip'))
 					this.$refs.refPopupInput.open()
 					return
 				}
 				if (!this.isValidIP(text)) {
-					showToast("IP鏃犳晥锛�)
+					showToast(this.translate('invalid_ip'))
 					this.$refs.refPopupInput.open()
 					return
 				}
@@ -590,14 +638,17 @@
 				const curIndex = this.pageList.findIndex((a) => a.ip == text)
 				if (curIndex < 0) {
 					if (this.pageList.length === 3) {
-						showToast("瀵瑰鍙厑璁告坊鍔�杈嗚溅锛�)
+						showToast(this.translate('only_3_vehicles_to_added'))
 						return
 					}
 					uni.navigateTo({
 						url: `/pages/index/connect?ip=${text}`
 					})
 				} else {
-					showToast("涓嶅厑璁告坊鍔犵浉鍚孖P鐨勮澶囷紒")
+					showToast(this.translate('vehicle_ip_has_added'))
+					this.setData({
+						currentPage: curIndex
+					})
 					return
 				}
 
@@ -607,13 +658,18 @@
 				this.$refs.refPopupInput.close()
 			},
 			menuItemChange(item) {
-				if (item.text == "缂栬緫") {
+				if (item.id == 'edit') {
 					const page = this.pageList[this.currentPage]
 					this.clickInfo(page)
 
-				} else if (item.text == "鍒犻櫎") {
+				} else if (item.id == "remove") {
 					const page = this.pageList[this.currentPage]
-					showModal(`纭畾瑕佸垹闄よ澶団�${ page.name}鈥濆悧`, "璀﹀憡", true, "纭畾", "鍙栨秷").then((res) => {
+					showModal({
+						content: `${this.translate('ask_delete_device')}`,
+						confirmText: this.translate('remove'),
+						cancelText: this.translate("cancel"),
+
+					}).then((res) => {
 						if (res) {
 							const list = this.pageList
 							let curPage = this.currentPage
@@ -635,17 +691,107 @@
 
 						}
 					})
-				} else if (item.text == "澶囦唤") {
+				} else if (item.id == "backup") {
 					const page = this.pageList[this.currentPage]
 					uni.navigateTo({
 
 						url: `/pages/index/backup?ip=${page.ip}`
 					})
+				} else if (item.id == "error") {
+					const page = this.pageList[this.currentPage]
+					uni.navigateTo({
+
+						url: `/pages/error/index?ip=${page.ip}`
+					})
+				} else if (item.id == "update") {
+					const page = this.pageList[this.currentPage]
+					uni.navigateTo({
+						url: `/pages/version/car-program-upload?ip=${page.ip}`
+					})
 				}
 			},
+			async loadCarSaveVersion() {
+				try {
+					var verName = ""
+					var fileList = []
+					try {
+						fileList = await TaskInit.fileUtils.listSavedFiles('car_version')
+					} catch (ex) {
+						console.log(ex)
+					}
+
+					fileList.forEach((ele) => {
+						if (verName) {
+							if (verName < ele.name) {
+								verName = ele.name
+							}
+						} else {
+							verName = ele.name
+						}
+					})
+
+					this.localCarVersion = this.getVersionFromFileName(verName)
+				} catch (ex) {
+					this.localCarVersion = {
+						date: "",
+						ver: ""
+					}
+				}
+			},
+			getVersionFromFileName(fileName) {
+				if (!fileName.trim()) {
+					return {
+						date:"" ,
+						ver: ""
+					}
+				}
+				let versionInfos = fileName.trim().split('_');
+				let date = versionInfos[1]
+				let version2 = versionInfos[2]
+				let versionInfos2 = version2.split('.');
+				versionInfos2.pop()
+				return {
+					date,
+					ver: versionInfos2.join(".")
+				}
+			},
+			async checkServerVersion(ip) {
+				try {
+
+					if (!this.localCarVersion?.ver) {
+						return false
+					}
+					const verInfo = await getShellVersion(ip)
+					console.log(verInfo)
+
+					if (verInfo?.software_version) {
+
+						const isVer = `${this.localCarVersion?.date ||""}_${this.localCarVersion?.ver ||""}` > verInfo
+							.software_version
+						return isVer
+					}
+
+				} catch (ex) {
+					return false
+				}
+				return false
+
+			},
+
 			closeMenu() {
 				this.$refs.refPopupMenu.close()
-			}
+			},
+			translate(t, values) {
+				if (typeof this.$t == "function") {
+					const message = this.$t(`page.${t}`)
+					if (values) {
+						return message.replace(/{(\d+)}/g, (match, index) => {
+							const value = values[index]
+							return value !== undefined ? value : match
+						})
+					} else return message
+				} else return t;
+			},
 		}
 	}
 </script>
@@ -884,7 +1030,7 @@
 		.button {
 			margin: auto;
 			margin-top: 20rpx;
-			width: 320rpx !important;
+			width: 400rpx !important;
 			border-radius: 4rpx;
 		}
 
@@ -913,18 +1059,21 @@
 			display: flex;
 			flex-wrap: nowrap;
 			flex-direction: row !important;
-			align-items: center;
-			padding: 8rpx 16rpx;
+			// align-items: center;
+			padding: 10rpx 20rpx;
 			font-size: 32rpx;
 
 			.text {
 				flex: 1;
+				text-align: left;
 			}
 
-			.img {
-				width: 40rpx;
-				height: 40rpx;
-				margin: 10rpx;
+			.new {
+				background-color: red;
+				width: 10rpx;
+				height: 10rpx;
+				margin: 5rpx 10rpx;
+				border-radius: 5rpx;
 			}
 		}
 
diff --git a/pages/login/forgot-password.vue b/pages/login/forgot-password.vue
index 599b55e..ec22a54 100644
--- a/pages/login/forgot-password.vue
+++ b/pages/login/forgot-password.vue
@@ -3,16 +3,16 @@
 		<view class="uni-panel-content items-center">
 			<view class="login-content">
 				<view class="justify-center">
-					<image class="title-img" src="/images/earth.svg" alt="svg 鍥剧墖" />
+					<image class="title-img" src="/images/earth.svg" alt="svg " />
 				</view>
 				<view class="login-form">
 					<view class="login-form-item ">
-						<image class="icon" src="/images/mobile.svg" alt="svg 鍥剧墖" />
+						<image class="icon" src="/images/mobile.svg" alt="svg " />
 						<input class="input" :focus="formData.accountFocus" name="account" comfirm-type="done"
 							type="text" v-model="form.account" placeholder="杈撳叆鎵嬫満鍙风爜" />
 					</view>
 					<view class="login-form-item ">
-						<image class="icon" src="/images/chat.svg" alt="svg 鍥剧墖" />
+						<image class="icon" src="/images/chat.svg" alt="svg " />
 						<input class="input" comfirm-type="done" v-model="form.mobileCode" placeholder="鐭俊楠岃瘉鐮� />
 						<view v-if="disabledSendCode" class="disabled">{{sendCodeCountdown}}绉掑悗閲嶆柊鍙戦�</view>
 						<a v-else @click="clickSendVerifyCode">鍙戦�楠岃瘉鐮�/a>
@@ -108,7 +108,7 @@
 
 			clickOk() {
 				uni.reLaunch({
-					url: "/pages/login/index"
+					url: "/pages/login/index?flag=1"
 				})
 			},
 			clickSendVerifyCode() {
@@ -132,6 +132,10 @@
 					}
 				}, 1000)
 			},
+			translate(t) {
+				if (typeof this.$t == "function") return this.$t(`page.${t}`)
+				else return t;
+			},
 
 		}
 	}
diff --git a/pages/login/index.vue b/pages/login/index.vue
index 108311f..b47187d 100644
--- a/pages/login/index.vue
+++ b/pages/login/index.vue
@@ -7,18 +7,18 @@
 					<!-- <image class="title-img" src="/images/earth.svg" alt=" 鍥剧墖" /> -->
 
 				</view>
-				<text class="title">璇风櫥褰�/text>
+				<text class="title">{{translate('start_login')}}</text>
 				<view class="login-form">
 					<view class="login-form-item line">
-						<text class="label">鐢ㄦ埛鍚�/text>
+						<text class="label">{{translate('account')}}</text>
 						<input class="input" :focus="formData.accountFocus" comfirm-type="done" type="text"
-							v-model="form.account" placeholder="鎵嬫満鍙风爜" />
+							v-model="form.account" :placeholder="translate('input_login')" />
 					</view>
 
 					<view class="login-form-item line">
-						<text class="label">瀵嗙爜</text>
+						<text class="label">{{translate('password')}}</text>
 						<input class="input" :focus="formData.passwordFocus" :password="hidePassContent"
-							comfirm-type="done" v-model="form.password" placeholder="鐧诲綍瀵嗙爜" />
+							comfirm-type="done" v-model="form.password" :placeholder="translate('input_password')" />
 						<uni-icons :type="hidePassContent?'eye-slash':'eye'" size="28"
 							@click="clickHidePassContent"></uni-icons>
 					</view>
@@ -26,7 +26,7 @@
 						<checkbox-group @change="handleSavePwdChange">
 							<label>
 								<checkbox value="1" :checked="savePsw" />
-								<text>璁颁綇瀵嗙爜</text>
+								<text>{{translate('remember_password')}}</text>
 							</label>
 						</checkbox-group>
 
@@ -34,7 +34,8 @@
 						<!-- 	<a @click="clickForgotPassword">蹇樿瀵嗙爜</a> -->
 					</view>
 					<view class="button-group">
-						<a-button :disabled="disabledOk" type="primary" class="button" @click="clickLogin">鐧诲綍</a-button>
+						<a-button :disabled="disabledOk" type="primary" class="button"
+							@click="clickLogin">{{translate('login')}}</a-button>
 						<!-- <a-button class="button" type="ghost"  @click="clickNewAccount">鍒涘缓璐﹀彿</a-button> -->
 					</view>
 				</view>
@@ -42,26 +43,67 @@
 			</view>
 		</view>
 		<view class="bottom">
+			<a class="language" @click="clickLanguage">{{translate("language")}}</a>
 			<view class="version">
-				鐗堟湰锛歿{version}}
+				{{translate("version")}}锛歿{version.date}}_{{version.ver}}
 			</view>
 		</view>
+		<!-- 鐗堟湰鍗囩骇寮圭獥寮� -->
+		<uni-popup ref="promotion" type="center">
+			<view class="promotion" style="width: 700rpx;padding:20rpx 40rpx; background: white;font-size: 36rpx;">
+
+				<view class="operates">
+					<view v-if="showUpdateBtns" style="text-align: center;padding:20rpx;font-weight: 700;">
+						{{translate('ask_update') }}
+					</view>
+					<view style="text-align: center;padding-top:20rpx;">
+						{{carUpdateFlag ? translate('tip_car_update') : translate('tip_app_update')  }}锛歿{newVersion.date}}_{{newVersion.ver}}
+					</view>
+					<view  v-if="newVersion.info" style="padding:20rpx;font-weight: 600;">
+						{{translate("version_info")}}锛歿{newVersion.info}}
+					</view>
+					
+					<template v-if="showUpdateBtns">
+						<text @click="onCancel"
+							style="margin-top:30rpx; display: inline-block;color: #7cd0f8;width: 50%; text-align: center;">{{translate('cancel')}}</text>
+						<text @click="onUpdate"
+							style="margin-top:30rpx;display: inline-block;color: #7cd0f8;width: 50%; text-align: center;">{{translate('confirm')}}</text>
+					</template>
+				</view>
+				<view class="operates" v-if=" !showUpdateBtns">
+					<cmdProgress :percent="percentage" stroke-color="linear-gradient(to right, #ef32d9, #89fffd)">
+					</cmdProgress>
+				</view>
+			</view>
+		</uni-popup>
 	</view>
 </template>
 
 <script>
+	import cmdProgress from "@/components/cmd-progress/index.vue"
 	import {
 		session,
 		showToast,
-		showModal
+		showModal,
+		showError
 	} from "@/comm/utils.js"
+	import TaskInit from "@/comm/extend.js"
 	import {
 		Button
 	} from 'antd-mobile-vue-next'
+	import {
+		getAppVersion,
+		getServerVersion,
+		downloadUpdatePackage
+	} from "@/api/version.js"
+
+
+
 	export default {
 		name: "PagesLogin",
 		components: {
-			'a-button': Button
+			'a-button': Button,
+			cmdProgress
 		},
 		data() {
 
@@ -76,7 +118,19 @@
 				},
 				hidePassContent: true,
 				savePsw: false,
-				version: "1.0.0",
+				version: {
+					date: "",
+					ver: ""
+				},
+				newVersion: {
+					date: "",
+					ver: "",
+					info:""
+				},
+				carUpdateFlag: false,
+				vesironFileName: "", //瀹夊崜app涓嬭浇閾炬帴
+				percentage: 0, //涓嬭浇杩涘害
+				showUpdateBtns: true,
 			}
 		},
 		computed: {
@@ -92,7 +146,11 @@
 				return false
 			}
 		},
-		onLoad() {
+		onLoad(option) {
+			const _this = this
+			uni.setNavigationBarTitle({
+				title: this.translate('login')
+			})
 
 			uni.getSystemInfo({
 				success(e) {
@@ -105,7 +163,7 @@
 
 			})
 			const info = session.getValue("login_info")
-			console.log(info)
+
 			this.setData({
 				form: {
 					account: info?.id || '',
@@ -117,7 +175,22 @@
 			// #ifdef APP-PLUS
 			plus.runtime.getProperty(plus.runtime.appid, (info) => {
 				// console.log(info);
-				this.version = info.version;
+				const verStr = info.version || "";
+				const arVer = verStr.split("_")
+				if (arVer.length > 1) {
+					_this.version = {
+						date: arVer[0],
+						ver: arVer[1]
+					}
+				} else if (arVer.length == 1) {
+					_this.version = {
+						date: "",
+						ver: arVer[0]
+					}
+				}
+				if (!option.flag)
+					_this.checkAppVersion(_this.version)
+
 			});
 			//#endif
 
@@ -144,6 +217,13 @@
 					});
 				});
 			},
+
+			clickLanguage() {
+				uni.navigateTo({
+					url: "/pages/my/language"
+				})
+
+			},
 			clickHidePassContent() {
 				this.hidePassContent = !this.hidePassContent
 			},
@@ -156,45 +236,211 @@
 				}
 
 			},
-			clickLogin() {
-				let account = this.form.account.trim()
-				if (!account) {
-					showToast("璇疯緭鍏ユ墜鏈哄彿")
-					return
-				}
-				let password = this.form.password.trim()
-				if (!password) {
-					showToast("璇疯緭鍏ュ瘑鐮�)
-					return
-				}
-				if (account != "sa") {
-					showToast("鎵嬫満鍙蜂笉瀛樺湪")
-					return
-				}
-				if (password != "0000") {
-					showToast("瀵嗙爜閿欒")
-					return
-				}
-				if (this.savePsw) {
+			onUpdate() {
+				this.showUpdateBtns = false;
 
-					session.setValue("login_info", {
-						id: account,
-						password,
-						savePsw: true,
-					})
-				} else {
-					session.setValue("login_info", {
-						id: account,
-						password: ""
-					})
+
+				this.updateAPP(this.vesironFileName, this.newVersion)
+			},
+			onCancel() {
+				this.$refs.promotion.close();
+				if (!this.carUpdateFlag) {
+					setTimeout(() => {
+						this.checkServerVersion()
+					}, 500)
 				}
-				getApp().globalData.withLog  = 	session.getValue("write_log") ? true:false
-				uni.reLaunch({
-					url: "/pages/index/index"
+
+			},
+			updateAPP(fileName, verInfo) {
+				let _this = this;
+				let sys = uni.getSystemInfoSync().platform //妫�煡绯荤粺
+
+				downloadUpdatePackage(fileName, (path) => {
+					if (_this.carUpdateFlag) {
+						var pathArr = fileName.split(".")
+						const fileExt = pathArr.pop()
+						const fileName2 = `EasyGO_${verInfo.date}_${verInfo.ver}.${fileExt}`
+						TaskInit.fileUtils.clearSavedFiles("car_version")
+						TaskInit.fileUtils.moveToLocal(path, "car_version", fileName2).then((
+							res) => {
+							console.log("淇濆瓨鎴愬姛锛�, res)
+							showToast(this.translate('download_success'));
+						}).catch((err) => {
+							console.log("淇濆瓨澶辫触锛�, err)
+							showToast(_this.translate("failed_save_file"))
+						})
+					} else {
+						plus.runtime.openFile(path); //閫夋嫨杞欢鎵撳紑鏂囦欢
+						showToast(this.translate('update_success'));
+					}
+					_this.$refs.promotion.close();
+
+				}, (res) => {
+					console.log('涓嬭浇杩涘害' + res.progress);
+					_this.percentage = res.progress
+					if (_this.percentage == 100) {
+						_this.$refs.promotion.close();
+					}
+				}, (err) => {
+					_this.$refs.promotion.close();
+					showToast(`${_this.translate('update_fail') },${err}`);
 				})
-				// uni.navigateTo({
-				// 	url: `/pages/index/connect?ip=192.168.1.2&sid=HH&password=HH888888`
-				// })
+
+			},
+			async checkAppVersion(version) {
+				try {
+					const listVer = await getAppVersion() || []
+					const appVer = listVer[0] || {}
+					this.vesironFileName = appVer.file_name || ""
+					if (this.vesironFileName) {
+						var isver = this.compareVersion({
+							date: appVer.file_time,
+							ver: appVer.version
+						}, version);
+
+						if (isver) {
+							this.carUpdateFlag = false
+							this.newVersion = {
+								date: appVer.file_time,
+								ver: appVer.version,
+								info: appVer.info,
+							}
+							this.$refs.promotion.open();
+						} else {
+							this.checkServerVersion()
+						}
+					} else {
+						this.checkServerVersion()
+					}
+				} catch (ex) {
+					console.log(ex)
+				}
+
+			},
+			getVersionFromFileName(fileName) {
+				if (!fileName.trim()) {
+					return {
+						date: "",
+						ver: ""
+					}
+				}
+				let versionInfos = fileName.trim().split('_');
+				let date = versionInfos[1]
+				let version2 = versionInfos[2]
+				let versionInfos2 = version2.split('.');
+				versionInfos2.pop()
+				return {
+					date,
+					ver: versionInfos2.join(".")
+				}
+			},
+			compareVersion(curV, reqV) {
+
+				if (curV.date == reqV.date) {
+					return curV.ver > reqV.ver
+				}
+				return curV.date > reqV.date
+			},
+			async checkServerVersion() {
+				try {
+					const listVer = await getServerVersion() || []
+					const svrVer = listVer[0] || {}
+					this.vesironFileName = svrVer.file_name || ""
+					if (this.vesironFileName) {
+						var oldVerName = ""
+						var fileList = []
+						try {
+							fileList = await TaskInit.fileUtils.listSavedFiles('car_version')
+						} catch (ex) {
+							console.log(ex)
+						}
+						fileList.forEach((ele) => {
+							if (oldVerName) {
+								if (oldVerName < ele.name) {
+									oldVerName = ele.name
+								}
+							} else {
+								oldVerName = ele.name
+							}
+						})
+						console.log(oldVerName)
+						if (oldVerName) {
+							const oldVer = this.getVersionFromFileName(oldVerName)
+							const isVer = this.compareVersion({
+								date: svrVer.file_time,
+								ver: svrVer.version
+							}, oldVer);
+							if (isVer || !oldVer) {
+								this.carUpdateFlag = true
+								this.newVersion = {
+									date: svrVer.file_time,
+									ver: svrVer.version,
+										info: svrVer.info,
+								}
+								this.$refs.promotion.open();
+							}
+						} else {
+							this.carUpdateFlag = true
+							this.newVersion = {
+								date: svrVer.file_time,
+								ver: svrVer.version,
+								info: svrVer.info,
+							}
+							this.$refs.promotion.open();
+						}
+
+					}
+				} catch (ex) {
+
+					console.log(ex)
+				}
+
+			},
+
+			async clickLogin() {
+
+				try {
+					let account = this.form.account.trim()
+					if (!account) {
+						showToast(this.translate("input_login"))
+						return
+					}
+					let password = this.form.password.trim()
+					if (!password) {
+						showToast(this.translate("input_password"))
+						return
+					}
+					if (account != "sa") {
+						showToast(this.translate("account_not_exist"))
+						return
+					}
+					if (password != "0000") {
+						showToast(this.translate("password_error"))
+						return
+					}
+					if (this.savePsw) {
+
+						session.setValue("login_info", {
+							id: account,
+							password,
+							savePsw: true,
+						})
+					} else {
+						session.setValue("login_info", {
+							id: account,
+							password: ""
+						})
+					}
+					getApp().globalData.withLog = session.getValue("write_log") ? true : false
+					uni.reLaunch({
+						url: "/pages/index/index"
+					})
+					// uni.navigateTo({
+					// 	url: `/pages/index/connect?ip=192.168.1.2&sid=HH&password=HH888888`
+					// })
+				} catch (ex) {
+					showError(ex)
+				}
 			},
 			clickNewAccount() {
 				uni.navigateTo({
@@ -205,8 +451,13 @@
 				uni.navigateTo({
 					url: "/pages/login/forgot-password"
 				})
-			}
-		}
+			},
+			translate(t) {
+				if (typeof this.$t == "function") return this.$t(`page.${t}`)
+				else return t;
+			},
+		},
+
 	}
 </script>
 
@@ -233,6 +484,13 @@
 				color: #888;
 				flex-direction: row;
 				display: flex;
+			}
+
+			.language {
+				float: left;
+				display: flex;
+				text-decoration: underline;
+				/* 涓嬪垝绾�*/
 			}
 		}
 
@@ -275,7 +533,7 @@
 			margin: 20rpx 10rpx;
 			font-size: 28rpx !important;
 			flex-direction: row;
-			
+
 
 			.label {
 				text-align: right;
@@ -289,7 +547,8 @@
 			}
 
 		}
-		.line{
+
+		.line {
 			border-bottom: 2rpx solid #ddd;
 		}
 
diff --git a/pages/login/register.vue b/pages/login/register.vue
index ffac145..8d596cf 100644
--- a/pages/login/register.vue
+++ b/pages/login/register.vue
@@ -3,13 +3,13 @@
 		<view class="uni-panel-content items-center">
 			<view class="login-content">
 				<view class="justify-center">
-					<image class="title-img" src="/images/earth.svg" alt="svg 鍥剧墖" />
+					<image class="title-img" src="/images/earth.svg" alt="svg " />
 				</view>
 				<view class="login-form">
 					<view class="login-form-item ">
-						<image class="icon" src="/images/mobile.svg" alt="svg 鍥剧墖" />
+						<image class="icon" src="/images/mobile.svg" alt="svg " />
 						<input class="input" :focus="formData.accountFocus" name="account" comfirm-type="done"
-							type="text" v-model="form.account" placeholder="杈撳叆鎵嬫満鍙风爜" />
+							type="text" v-model="form.account" :placeholder="杈撳叆鎵嬫満鍙风爜" />
 					</view>
 					<view class="login-form-item ">
 						<image class="icon" src="/images/chat.svg" alt="svg 鍥剧墖" />
@@ -117,7 +117,7 @@
 					return
 				}
 				uni.reLaunch({
-					url: "/pages/login/index"
+					url: "/pages/login/index?flag=1"
 				})
 			},
 			clickSendVerifyCode() {
@@ -144,7 +144,10 @@
 				}, 1000)
 			},
 			clickUserAgreement() {},
-
+			translate(t) {
+				if (typeof this.$t == "function") return this.$t(`page.${t}`)
+				else return t;
+			},
 
 		}
 	}
diff --git a/pages/map/index.vue b/pages/map/index.vue
index 7c03354..899a0db 100644
--- a/pages/map/index.vue
+++ b/pages/map/index.vue
@@ -7,7 +7,7 @@
 				<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" />
+						alt="SVG" @click="clickShowMenu" />
 				</view>
 				<template v-slot:left>
 					<view @click="clickBack">
@@ -25,13 +25,11 @@
 				</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>
+				<image class="img" src="/images/image_25.png" alt=" picture" mode="aspectFit" />
+				<view class="title">{{translate('failed_vehicle_connection')}}</view>
+				<view class="space">{{translate('check_network_or_reload')}}</view>
 			</view>
-			<!-- 	<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="map-content">
 				<view class="content">
 					<view ref="canvasCtx" class="fabric" id="canvasMap" :message="ctxDataStr"
 						:change:message="ctx.receiveMsg">
@@ -39,17 +37,16 @@
 					<view class="loading-overlay" v-if="bgLoading">
 						<view class="loading-content">
 							<view class="loading-spinner"></view>
-							<text>鍔犺浇涓�.. {{ bgProgressPercent }}%</text>
+							<text>{{translate('loading')}}... {{ bgProgressPercent }}%</text>
 						</view>
 					</view>
 					<view class="position-site" v-if="mapOperationStatus =='pos' " @click="clickPositionStation">
 						<text class="ico my-location-rounded"></text>
-
-						鑾峰彇鎼繍杞︿綅缃拰鏈濆悜
+						{{translate('obtain_positon_and_orientation_of_vehicle')}}
 					</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 鍥剧墖" /> -->
+						<!-- <image class="img" src="/images/material-symbols_my-location-rounded.svg" alt="SVG picture" /> -->
 					</view>
 					<view v-if="this.mapOperationType ==''" class="teaching-path-show"
 						:class="showTeachingPathFlag?'selected':''" @click="clickShowTeachingPath">
@@ -61,11 +58,11 @@
 						v-if="mapOperationType =='edit_station'">
 
 						<view class=" left">
-							<view class="line">绔欑偣鍚嶇О锛�+							<view class="line">{{translate('station_name')}}锛� 							</view>
-							<view class="line"> 鍧愭爣(x,y)锛�+							<view class="line"> {{translate('coordinates')}}(x,y)锛� 							</view>
-							<view class="line">鏈濆悜锛�+							<view class="line">{{translate('angle')}}锛� 							</view>
 
 						</view>
@@ -85,201 +82,193 @@
 			</view>
 		</view>
 		<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" />
-					<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>
+			<SceneRenameInfo class="bottom-content" :ip="vehicleIp" :sceneName="sceneId"
+				v-if="mapOperationType =='edit_scene_name'" @finish="onFinishRenameScene" />
+			<PalletSizeInfo class="bottom-content" v-else-if="mapOperationType =='set_pallet_size'" :ip="vehicleIp">
+			</PalletSizeInfo>
 
 			<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="tip">{{translate("adjust_position")}}</view>
 					<view class="row-group">
 						<view class="coordinate">
-							<text class="name">妯潗鏍�</text>
+							<text class="name">{{translate("x_axis")}}:</text>
 							<input ref="refInputX" class="number-input" type="number" :value="stationEdit.x"
-								@input="onInputStationX" :maxlength="4" />
+								@blur="onInputStationX" :maxlength="8" />
 							<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" />
+							<text class="name">{{translate("y_axis")}}:</text>
+							<input ref="refInputY" class="number-input" type="number" :value="stationEdit.y"
+								@blur="onInputStationY" :maxlength="8" />
 							<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="tip">{{translate('adjust_orientation')}}</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 鍥剧墖"
+						<image class="img-angle" :src="angleSvg" alt="SVG picture" />
+						<image class="img-angle-pos" src="/images/Frame_153.svg" alt="SVG picture"
 							@touchstart="handleAngleTouchStart" @touchmove="handleAngleTouchMove" />
 					</view>
 				</view>
 				<view class="bottom-content" v-else>
-					<view class="tip">璇疯緭鍏ョ珯鐐瑰悕绉�/view>
+					<view class="tip">{{translate("input_station_name")}}</view>
 					<view class="name-input">
-						<input ref="refInputName" :focus="true" placeholder="杈撳叆绔欑偣鍚嶇О" :value="stationEdit.name"
-							@input="onInputStationName" />
+						<input ref="refInputName" :focus="true" :placeholder="translate('input_station_name')"
+							:value="stationEdit.name" @input="onInputStationName" />
 						<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 class="button" :disabled="loading"
+							@click="clickStationNameCancel">{{translate('cancel')}}</a-button>
 						<a-button type="primary" class="button" :disabled="loading || stationEdit.name.trim() == ''"
-							@click="clickStationNameOK">纭畾</a-button>
+							@click="clickStationNameOK">{{translate('confirm')}}</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 class="text">{{translate('manual_plan')}}</view>
 					</view>
 
 					<view type="text" class="button " @click="clickVehicleTrajectoryPlan">
 						<text class="ico conversion-path"></text>
-						<view class="text ">杞﹁締杞ㄨ抗瑙勫垝</view>
+						<view class="text ">{{translate('vehicle_trajectory_plan')}}</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>
+					<view class="tip">{{translate("regiona_plan")}}</view>
+					<view>{{translate("endpoint_adjust_position_to_closed_shape")}}</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 class="text">{{translate("feasible_region")}}</view>
 					</view>
 
 					<view type="text" class="button " @click="clickPlanProhibitionRegion">
 						<text class="ico placeholder-bold"></text>
-						<view class="text ">绂佽鍖�/view>
+						<view class="text ">{{translate("prohibition_region")}}</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>
+					<view class="tip">{{translate("regiona_plan")}}</view>
+					<view>{{translate("endpoint_adjust_position_to_closed_shape")}}</view>
 				</template>
 				<template v-else-if="mapOperationStatus=='virtual_wall'">
-					<view class="tip">鍖哄煙瑙勫垝</view>
-					<view>鍙寜浣忎袱绔偣璋冩暣铏氭嫙澧欎綅缃�/view>
+					<view class="tip">{{translate("regiona_plan")}}</view>
+					<view>{{translate("endpoint_adjust_virtual_wall_position")}}</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 class="text">{{translate("feasible_region")}}</view>
 					</view>
 
 					<view type="text" class="button " @click="clickPlanProhibitionRegion">
 						<text class="ico placeholder-bold"></text>
 
-						<view class="text ">绂佽鍖�/view>
+						<view class="text ">{{translate("prohibition_region")}}</view>
 					</view>
 					<view type="text" class="button " @click="clickPlanVirtualWall">
 						<text class="dashed-line"></text>
-						<view class="text ">铏氭嫙澧�/view>
+						<view class="text ">{{translate("virtual_wall")}}</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 class="tip">{{translate("in_path_record")}}...</view>
 					<view v-if="mapOperationType =='public_teaching'">
 						<!-- 涓昏矾/鏀矾 -->
-						<view>姝e湪璁板綍鎼繍杞﹁杩涜矾寰勶紝瀹屾垚鍚庣偣鎸夐挳浠ョ粨鏉熺ず鏁欍�鍦ㄧず鏁欒繃绋嬩腑鍙互娣诲姞绔欑偣銆�/view>
-						<view class="switch-type">鍙殢鏃跺垏鎹㈢ず鏁欐ā寮�+						<view>{{translate("recording_path_of_vehicle_can_add_station")}}</view>
+						<view class="switch-type">{{translate("any_time_switch_teaching_modes")}}
 							<view class="switch-button-group">
 								<view class="switch-button"
 									:class="teachingModeCur.mode_type ==0?'switch-button-checked':''"
-									@click="onTeachingModeType(0)">榛樿</view>
+									@click="onTeachingModeType(0)">{{translate("default")}}</view>
 								<view class="switch-button "
 									:class="teachingModeCur.mode_type ==1?'switch-button-checked':''"
-									@click="onTeachingModeType(1)">鍙屽悜</view>
+									@click="onTeachingModeType(1)">{{translate("bidirectional")}}</view>
 								<view class="switch-button "
 									:class="teachingModeCur.mode_type ==2?'switch-button-checked':''"
-									@click="onTeachingModeType(2)">鏅鸿兘</view>
+									@click="onTeachingModeType(2)">{{translate("intelligent")}}</view>
 							</view>
 						</view>
 					</view>
 
 					<view v-else>
-						姝e湪璁板綍鎼繍杞﹁杩涜矾寰勶紝瀹屾垚鍚庣偣鎸夐挳浠ョ粨鏉熺ず鏁�+						{{translate("recording_path_of_vehicle_click_btn_to_finish")}}
 					</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>
+							@click="clickAddStation">{{translate("add_station")}}</a-button>
+						<a-button type="primary" class="button" @click="clickTeachingEnd">{{translate("end_teaching")}}</a-button>
 					</view>
 				</view>
 				<view class="bottom-content" v-else-if="mapOperationStatus =='end'">
-					<view class="tip">璺緞璁板綍瀹屾垚</view>
+					<view class="tip">{{translate("path_recording_completed")}}</view>
 					<view>
-						瑕佸皢璇ユ璺緞淇濆瓨涓虹ず鏁欒矾寰勫悧锛�+						{{translate("ask_save_this_path")}}
 					</view>
 					<view class="text-button-group">
 						<a-button type="primary" class="button" :disabled="loading"
-							@click="clickTeachingSave">淇濆瓨涓虹ず鏁欒矾寰�/a-button>
+							@click="clickTeachingSave">{{translate("save_as_teaching_path")}}</a-button>
 						<a-button type="ghost" class="button" :disabled="loading"
-							@click="clickTeachingReset">閲嶆柊璁板綍</a-button>
+							@click="clickTeachingReset">{{translate("re_record")}}</a-button>
 					</view>
 				</view>
 				<view class="bottom-content" v-else-if="mapOperationStatus =='save'">
-					<view class="tip">绀烘暀瀹屾垚</view>
+					<view class="tip">{{translate("teaching_completed")}}</view>
 					<view>
-						宸插皢璺緞淇濆瓨涓虹ず鏁欒矾寰�+						{{translate("path_saved_as_teaching_path")}}
 					</view>
 					<view class="text-button-group">
 						<a-button type="primary" class="button" :disabled="loading"
-							@click="clickTeachingFinish">瀹屾垚</a-button>
+							@click="clickTeachingFinish">{{translate("complete")}}</a-button>
 					</view>
 				</view>
 				<view class="bottom-content" v-else>
-					<view class="tip">鍗冲皢寮�鍏叡绀烘暀</view>
+					<view class="tip">{{translate("public_teaching_to_begin")}}</view>
 					<view>
-						璇烽�鎷╄杩涜涓昏矾杩樻槸鏀矾绀烘暀锛岀偣銆愬紑濮嬭褰曘�鍚庡皢璁板綍鎼繍杞︾殑琛岃繘璺嚎浣滀负鍏叡绀烘暀璺嚎銆傚紑濮嬭褰曞悗鍙殢鏃跺垏鎹㈢ず鏁欐ā寮忋�
+						{{translate("public_teaching_start_tip_select_teaching_mode")}}
 					</view>
 					<view class="text-button-group">
 						<view class="switch-type">
 							<view class="switch-button-group">
 								<view class="switch-button"
 									:class="teachingModeCur.mode_type ==0?'switch-button-checked':''"
-									@click="onTeachingModeType(0)">榛樿</view>
+									@click="onTeachingModeType(0)">{{translate("default")}}</view>
 								<view class="switch-button "
 									:class="teachingModeCur.mode_type ==1?'switch-button-checked':''"
-									@click="onTeachingModeType(1)">鍙屽悜</view>
+									@click="onTeachingModeType(1)">{{translate("bidirectional")}}</view>
 								<view class="switch-button "
 									:class="teachingModeCur.mode_type ==2?'switch-button-checked':''"
-									@click="onTeachingModeType(2)">鏅鸿兘</view>
+									@click="onTeachingModeType(2)">{{translate("intelligent")}}</view>
 							</view>
 						</view>
 						<a-button type="primary" class="button" :disabled="loading"
-							@click="clickTeachingStart">寮�璁板綍</a-button>
+							@click="clickTeachingStart">{{translate("start_teaching")}}</a-button>
 					</view>
 				</view>
 
@@ -288,24 +277,28 @@
 				<view class="img-button-group">
 					<view fill="none" class="button" @click="clickRename">
 						<text class="ico rename"></text>
-						<view class="text">閲嶅懡鍚�/view>
+						<view class="text">{{translate("rename")}}</view>
 					</view>
 
-					<view type="text" class="button " @click="clickRegionaPlan">
+					<!-- <view type="text" class="button " @click="clickRegionaPlan">
 						<text class="ico zone"></text>
-						<view class="text">鍖哄煙瑙勫垝</view>
+						<view class="text">{{translate("regiona_plan")}}</view>
+					</view> -->
+					<view type="text" class="button " @click="clickPalletSize">
+						<text class="ico dimensions"></text>
+						<view class="text">{{translate('pallet_size')}}</view>
 					</view>
 					<view fill="none" class="button" @click="clickExtendMap">
 						<text class="ico expand"></text>
-						<view class="text">鎵╁睍鍦板浘</view>
+						<view class="text">{{translate("extend_map")}}</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>
+						<text class="ico red delete-outline"></text>
+						<view class="text">{{translate('delete_scene')}}</view>
 					</view>
 				</view>
 
@@ -314,11 +307,11 @@
 				<view class="img-button-group">
 					<view fill="none" class="button" @click="clickMapStation">
 						<text class="ico location1"></text>
-						<view class="text">娣诲姞绔欑偣</view>
+						<view class="text">{{translate("add_station")}}</view>
 					</view>
 					<view fill="none" class="button" @click="clickTeaching">
 						<text class="ico teach"></text>
-						<view class="text">璺緞绀烘暀</view>
+						<view class="text">{{translate("path_teaching")}}</view>
 					</view>
 					<!-- <view type="text" class="button " @click="clickRegionaPlan">
 						<text class="ico layer"></text>
@@ -326,11 +319,11 @@
 					</view> -->
 					<view type="text" class="button" @click="clickMapEdit">
 						<text class="ico edit-line"></text>
-						<view class="text"> 缂栬緫鍦板浘</view>
+						<view class="text"> {{translate("edit_map")}}</view>
 					</view>
 					<view type="text" class="button" @click="clickMapTask">
 						<text class="ico task-list"></text>
-						<view class="text">浠诲姟璁剧疆</view>
+						<view class="text">{{translate("task_set")}}</view>
 					</view>
 				</view>
 			</view>
@@ -354,15 +347,15 @@
 						<view class="img-button-group">
 							<view fill="none" class="button" @click.stop="clickStationDelete">
 								<text class="ico delete-outline"></text>
-								<view class="text"> 鎵归噺鍒犻櫎</view>
+								<view class="text"> {{translate("batch_delete")}}</view>
 							</view>
 							<view type="text" class="button" @click.stop="clickStationPostion">
 								<text class="ico edit-line"></text>
-								<view class="text">璋冩暣浣嶇疆鏈濆悜</view>
+								<view class="text">{{translate("adjust_position")}}</view>
 							</view>
 							<view type="text" class="button" @click.stop="clickStationRename">
 								<text class="ico rename"></text>
-								<view class="text">閲嶅懡鍚�/view>
+								<view class="text">{{translate("rename")}}</view>
 
 							</view>
 						</view>
@@ -378,11 +371,11 @@
 
 							<view fill="none" class="button" @click.stop="clickTeachingEdit">
 								<text class="ico edit-line"></text>
-								<view class="text">缂栬緫</view>
+								<view class="text">{{translate('edit_path')}}</view>
 							</view>
 							<view fill="none" class="button" @click.stop="clickTeachingDelete">
 								<text class="ico red delete-outline "></text>
-								<view class="text">鍒犻櫎</view>
+								<view class="text">{{translate('delete_path')}}</view>
 							</view>
 
 						</view>
@@ -391,17 +384,20 @@
 				</view>
 			</uni-popup>
 			<uni-popup ref="refPopupTeachingUpdate" type="dialog">
-				<uni-popup-dialog type="info" cancelText="鍙栨秷" confirmText="纭畾" title="绀烘暀鏇存柊"
-					@confirm="dialogTeachingUpdateConfirm" @close="dialogTeachingUpdateClose">
+				<uni-popup-dialog type="info" :cancelText="translate('cancel')" :confirmText="translate('ok')"
+					:title="translate('teaching_update')" @confirm="dialogTeachingUpdateConfirm"
+					@close="dialogTeachingUpdateClose">
 
 					<view class="popup-dialog-content">
 						<view class="popup-content-item">
 							<radio-group @change="radioTeachinMainRoadChange">
 								<label class="radio">
-									<radio :value="1" :checked="selectTeachingMode.main_road == 1" />涓昏矾
+									<radio :value="1" :checked="selectTeachingMode.main_road == 1" />
+									{{translate("main_road")}}
 								</label>
 								<label class="radio">
-									<radio :value="0" :checked="selectTeachingMode.main_road != 1" />鏀矾
+									<radio :value="0" :checked="selectTeachingMode.main_road != 1" />
+									{{translate("branch_road")}}
 								</label>
 							</radio-group>
 
@@ -410,10 +406,12 @@
 						<view class="popup-content-item">
 							<radio-group @change="radioTeachinBidirectionChange">
 								<label class="radio">
-									<radio :value="1" :checked="selectTeachingMode.bidirection == 1" />鍙屽悜
+									<radio :value="1" :checked="selectTeachingMode.bidirection == 1" />
+									{{translate("bidirectional")}}
 								</label>
 								<label class="radio">
-									<radio :value="0" :checked="selectTeachingMode.bidirection != 1" />鍗曞悜
+									<radio :value="0" :checked="selectTeachingMode.bidirection != 1" />
+									{{translate("unidirectional")}}
 								</label>
 							</radio-group>
 
@@ -433,18 +431,22 @@
 		ref
 	} from "vue";
 	import {
+		Base64
+	} from '@/comm/Base64.js';
+	import {
 		showToast,
 		showModal,
 		session,
 		showError,
-		showInfo
+		showInfo,
+
 	} 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 PalletSizeInfo from './infos/pallet-size.vue'
+	import SceneRenameInfo from './infos/scene-rename.vue'
 
 	import {
 		getAllScene,
@@ -471,7 +473,8 @@
 		name: "PagesMap",
 		components: {
 			'a-button': Button,
-			SceneCreateInfo
+			PalletSizeInfo,
+			SceneRenameInfo
 		},
 		data() {
 			return {
@@ -507,8 +510,7 @@
 					y: 0,
 					angle: 0
 				},
-				sceneInputName: "",
-				showClearName: false,
+
 				stationList: [],
 				angleSvg: "/static/images/angle0.svg",
 				windowWidth: 375,
@@ -534,6 +536,8 @@
 				positioningAgv: false,
 				isPageVisible: true,
 				destroyFlag: false,
+
+
 			}
 		},
 		computed: {
@@ -550,21 +554,21 @@
 		},
 		watch: {
 			mapOperationType(val) {
-				let name = this.sceneId || "鍦板浘"
+				let name = this.sceneId || this.translate("map")
 				if (val == "add_station" || val == "teaching_add_station") {
-					name = "娣诲姞绔欑偣"
+					name = this.translate("add_station")
 				} else if (val == "edit_station") {
-					name = "缂栬緫绔欑偣"
-				} else if (val == "scene_create") {
-					name = "鏋勫缓鍦烘櫙"
+					name = this.translate("edit_station")
 				} else if (val == "edit_map") {
-					name = "缂栬緫鍦板浘"
+					name = this.translate("edit_map")
 				} else if (val == "manual_planning") {
-					name = "浜哄伐瑙勫垝"
+					name = this.translate("manual_plan")
 				} else if (val == "vehicle_trajectory_planning") {
-					name = "杞﹁締杞ㄨ抗瑙勫垝"
+					name = this.translate("vehicle_trajectory_plan")
 				} else if (val == "public_teaching") {
-					name = "璺緞绀烘暀"
+					name = this.translate("path_teaching")
+				} else if (val == "set_pallet_size") {
+					name = this.translate("pallet_size")
 				}
 				this.setData({
 					navigationBarTitle: name
@@ -652,7 +656,7 @@
 					this.setData({
 						unlinked: true
 					})
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 			},
 
@@ -672,7 +676,9 @@
 			clickMore() {
 				this.menuPopup = {
 					type: "menu",
-					list: ["鏋勫缓鍦烘櫙", "鍦板浘鎵╁睍", "閲嶅懡鍚�, "鍒犻櫎"],
+					list: [this.translate("build_scene"), this.translate("extend_map"), this.translate("rename"), this
+						.translate("delete")
+					],
 					left: 450,
 					top: 140
 				}
@@ -680,43 +686,7 @@
 			},
 
 			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") {
+				if (this.mapOperationType == "edit_map") {
 					this.mapOperationType = ""
 				} else if (this.mapOperationType == "regiona_planning") {
 					this.mapOperationType = "edit_map"
@@ -724,11 +694,18 @@
 					if (this.mapOperationStatus == "feasible_region" || this.mapOperationStatus ==
 						"prohibition_region") {
 
-						showModal("姝e湪杩涜鍖哄煙瑙勫垝銆�, "鏄惁瑕侀�鍑哄尯鍩熻鍒掞紵").then((res) => {
-							if (res) {
-								this.mapOperationStatus = ""
-							}
-						})
+						showModal({
+								title: this.translate("ask_exit_regional_planning"),
+								content: `${this.translate('regional_planning')}`,
+								confirmText: this.translate('yes'),
+								cancelText: this.translate("no"),
+
+							})
+							.then((res) => {
+								if (res) {
+									this.mapOperationStatus = ""
+								}
+							})
 
 					} else {
 						this.mapOperationType = "regiona_planning"
@@ -738,24 +715,30 @@
 					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 = {}
+						showModal({
+								title: this.translate("ask_exit_regional_planning"),
+								content: `${this.translate('regional_planning')}`,
+								confirmText: this.translate('yes'),
+								cancelText: this.translate("no"),
+							})
+							.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 = ""
 								}
-								this.mapOperationStatus = ""
-							}
-						})
+							})
 					} else {
 						this.mapOperationType = "regiona_planning"
 					}
@@ -798,32 +781,38 @@
 
 					if (this.mapOperationStatus) {
 
-						showModal("宸茶褰曠殑璺緞灏嗕細琚垹闄ゃ�", "鏄惁瑕侀�鍑虹ず鏁欙紵").then(async (res) => {
-							if (res) {
-								const listDataStr = []
-								if (this.mapOperationStatus == 'end' || this.mapOperationStatus ==
-									'save') {
-									try {
-										await delTeachingMode(this.vehicleIp, [this.teachingModeCur])
+						showModal({
+								title: this.translate("ask_exit_teaching"),
+								content: `${this.translate('recorded_path_will_be_deleted')}`,
+								confirmText: this.translate('yes'),
+								cancelText: this.translate("no"),
+							})
+							.then(async (res) => {
+								if (res) {
+									const listDataStr = []
+									if (this.mapOperationStatus == 'end' || this.mapOperationStatus ==
+										'save') {
+										try {
+											await delTeachingMode(this.vehicleIp, [this.teachingModeCur])
 
-										listDataStr.push({
-											method: "remove_teaching_path",
-											param: {
-												name: this.teachingModeCur.name,
-												mode: "Public"
-											},
-										})
-									} catch (ex) {
-										showError(ex)
+											listDataStr.push({
+												method: "remove_teaching_path",
+												param: {
+													name: this.teachingModeCur.name,
+													mode: "Public"
+												},
+											})
+										} catch (ex) {
+											showError(ex, this.translate('error'))
+										}
 									}
+									listDataStr.push({
+										method: "teaching_finish",
+									})
+									this.ctxDataStr = JSON.stringify(listDataStr)
+									this.mapOperationType = ""
 								}
-								listDataStr.push({
-									method: "teaching_finish",
-								})
-								this.ctxDataStr = JSON.stringify(listDataStr)
-								this.mapOperationType = ""
-							}
-						})
+							})
 					} else {
 						this.ctxDataStr = JSON.stringify([{
 							method: "teaching_finish",
@@ -864,9 +853,7 @@
 				if (type == "scene") {
 					this.changeMap(item)
 				} else if (type == "menu") {
-					if (item == "鏋勫缓鍦烘櫙") {
-						// this.mapOperationType = 'scene_create'
-						// this.opSceneType = 'add_name'
+					if (item == this.translate("build_scene")) {
 						uni.navigateTo({
 							url: `/pages/map/scene?ip=${_this.vehicleIp}&opType=create`,
 							events: {
@@ -876,11 +863,11 @@
 								},
 							}
 						})
-					} else if (item == "鍦板浘鎵╁睍") {
+					} else if (item == this.translate("extend_map")) {
 						this.clickExtendMap()
-					} else if (item == "閲嶅懡鍚�) {
+					} else if (item == this.translate('rename')) {
 						this.clickRename()
-					} else if (item == "鍒犻櫎") {
+					} else if (item == this.translate('delete')) {
 						this.clickDelete()
 					}
 				}
@@ -909,7 +896,7 @@
 					const info = await stations(this.vehicleIp)
 					return info.station_list || []
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 					return []
 				}
 			},
@@ -918,7 +905,7 @@
 					const info = await getMapUrl(this.vehicleIp, id)
 					return info
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 					return {}
 				}
 			},
@@ -927,11 +914,11 @@
 				this.mapShowAgv = false
 				this.setData({
 					sceneId: id,
-					navigationBarTitle: id || "鍦板浘"
+					navigationBarTitle: id || this.translate("map")
 				})
 				try {
 					uni.showLoading({
-						title: "鍒囨崲鍦烘櫙涓�
+						title: this.translate("switching_scene")
 					})
 					this.setData({
 						unlinked: false
@@ -968,6 +955,7 @@
 						bgProgressPercent: 30,
 						bgLoading: infoMap.filedata ? true : false
 					})
+
 					this.stationList = stationLst
 					this.teachingMode = await this.loadTeachingMode()
 					this.ctxDataStr = JSON.stringify([{
@@ -1024,7 +1012,7 @@
 						bgProgressPercent: 0,
 						bgLoading: false
 					})
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 			},
 
@@ -1067,9 +1055,11 @@
 
 				} else if (param.method == "edit_station") {
 					this.stationEdit = param.station
+					this.stationEdit.x = Math.round(Number(param.station.x || 0) * 1000) / 1000
+					this.stationEdit.y = Math.round(Number(param.station.y || 0) * 1000) / 1000
 					this.stationViewPos = {
-						x: param.view?.x || 0,
-						y: param.view?.y || 0,
+						x: Math.round(Number(param.view?.x || 0) * 1000) / 1000,
+						y: Math.round(Number(param.view?.y || 0) * 1000) / 1000,
 						width: param.view?.width || 0,
 						height: param.view?.height || 0,
 					}
@@ -1083,8 +1073,8 @@
 				} 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.stationEdit.x = Math.round(Number(param.station.x || 0) * 1000) / 1000
+					this.stationEdit.y = Math.round(Number(param.station.y || 0) * 1000) / 1000
 					this.stationViewPos.x = param.view?.x || 0
 					this.stationViewPos.y = param.view?.y || 0
 				} else if (param.method == "edit_finish") {
@@ -1096,8 +1086,8 @@
 							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
+							this.stationEdit.x = Math.round(Number(param.data?.x || 0) * 1000) / 1000
+							this.stationEdit.y = Math.round(Number(param.data?.y || 0) * 1000) / 1000
 
 							const angle = this.getStantardAngle(this.stationEdit.angle * 180 / Math.PI)
 
@@ -1235,6 +1225,9 @@
 
 				this.mapOperationType = "edit_map"
 			},
+			clickPalletSize() {
+				this.mapOperationType = "set_pallet_size"
+			},
 			clickExtendMap() {
 				const _this = this
 				uni.navigateTo({
@@ -1273,7 +1266,6 @@
 				this.mapOperationType = "regiona_planning"
 			},
 			clickRename() {
-				this.sceneInputName = this.sceneId
 				this.mapOperationType = 'edit_scene_name'
 			},
 			async loadSceneList() {
@@ -1290,20 +1282,7 @@
 					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}`,
-						// 	events: {
-						// 		// 涓烘寚瀹氫簨浠舵坊鍔犱竴涓洃鍚櫒锛岃幏鍙栬鎵撳紑椤甸潰浼犻�鍒板綋鍓嶉〉闈㈢殑鏁版嵁
-						// 		create_finish: function(data) {
-						// 			_this.onCreateSceneOk(data)
-						// 		},
-						// 	}
-						// })
+
 						uni.navigateBack({
 							delta: 1, //杩斿洖灞傛暟锛�鍒欎笂涓婇〉
 						})
@@ -1311,33 +1290,32 @@
 					}
 
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 			},
 			clickDelete() {
 				const _this = this
-				showModal(`鍒犻櫎鍦烘櫙[${this.sceneId}]浼氭妸鍦烘櫙瀵瑰簲鐨勫湴鍥句换鍔′俊鎭兘浼氬垹闄わ紝鏄惁纭鍒犻櫎锛焋, "鍒犻櫎鍦烘櫙").then(async (res) => {
+				
+				
+				showModal({
+					title: this.translate("ask_deleting_scene"),
+					content: `${this.translate('delete_scene_will_be_deleted_map_and_tasks')}`,
+					confirmText: this.translate('yes'),
+					cancelText: this.translate("no"),
+				}).then(async (res) => {
 					if (res) {
 						try {
 							uni.showLoading({
-								title: "鍒犻櫎鍦烘櫙涓�
+								title: this.translate("deleting_scene")
 							})
 							this.mapShowAgv = false
 							await delScene(this.vehicleIp, this.sceneId)
 							await this.loadSceneList()
 							if (this.sceneList.length > 0)
 								this.changeMap(this.sceneList[0])
-							// else {
-							// 	_this.mapOperationType = ""
-							// 	setTimeout(() => {
-							// 		uni.navigateBack({
-							// 			delta: 1, //杩斿洖灞傛暟锛�鍒欎笂涓婇〉
-							// 		})
-							// 	}, 500)
 
-							// }
 						} catch (ex) {
-							showError(ex)
+							showError(ex, this.translate('error'))
 						} finally {
 							uni.hideLoading()
 						}
@@ -1401,7 +1379,7 @@
 						])
 					}
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 			},
 			async clickPlanProhibitionRegion() {
@@ -1447,7 +1425,7 @@
 						])
 					}
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 			},
 
@@ -1487,34 +1465,14 @@
 						}
 					])
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 
 			},
-			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
-					}
+			onFinishRenameScene(name) {
+
+				if (name) {
 					const curIndex = this.sceneList.findIndex((param) => param == this.sceneId)
-					this.mapOperationType = ""
-					await updateScene(this.vehicleIp, this.sceneId, name)
-
-
 					this.sceneId = name
 					if (curIndex > -1) {
 						this.sceneList[curIndex] = name
@@ -1522,28 +1480,10 @@
 					this.setData({
 						navigationBarTitle: name
 					})
-					showToast("鍦烘櫙閲嶅懡鍚嶆垚鍔燂紒")
-				} catch (ex) {
-					this.mapOperationType = "edit_scene_name"
-					showError(ex)
-				} finally {
-					uni.hideLoading()
-					this.loading = false
 				}
-
+				this.mapOperationType = ""
 			},
 
-			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() {
 
@@ -1574,18 +1514,12 @@
 						}
 					}
 				})
-				//this.stationDelete(this.stationEdit)
-				// const _this = this
-				// showModal("璇ョ珯鐐瑰凡缁戝畾浠诲姟锛屽垹闄ょ珯鐐瑰悗缁戝畾鐨勪换鍔′細鍋滄骞跺垹闄�, "鏄惁纭鍒犻櫎锛�).then((res) => {
-				// 	if (res) {
 
-				// 	}
-				// })
 			},
 			async stationAdd(item) {
 				try {
 					uni.showLoading({
-						title: "姝e湪鏂板缓绔欑偣"
+						title: this.translate("creating_station")
 					})
 					await addStation(this.vehicleIp, item)
 					await updateStation(this.vehicleIp, item)
@@ -1599,7 +1533,7 @@
 
 
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				} finally {
 					uni.hideLoading()
 				}
@@ -1607,7 +1541,7 @@
 			async stationUpdate(item) {
 				try {
 					uni.showLoading({
-						title: "姝e湪鏇存柊绔欑偣"
+						title: this.translate("updating_station")
 					})
 					await updateStation(this.vehicleIp, item)
 					const curIndex = this.stationList.findIndex((a) => a.stationID ==
@@ -1622,7 +1556,7 @@
 					}
 
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				} finally {
 					uni.hideLoading()
 				}
@@ -1647,13 +1581,13 @@
 					}])
 					this.mapOperationType = ''
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 			},
 			async stationDelete(item) {
 				try {
 					uni.showLoading({
-						title: "姝e湪鍒犻櫎绔欑偣"
+						title: this.translate("deleting_station")
 					})
 					this.$refs.refPopupOperateStation.close()
 					await delStation(this.vehicleIp, [item.stationID])
@@ -1674,7 +1608,7 @@
 					}])
 
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				} finally {
 					uni.hideLoading()
 				}
@@ -1722,7 +1656,7 @@
 
 					const name = this.stationEdit.name.trim()
 					if (!name) {
-						showToast("绔欑偣鍚嶇О杩樻湭杈撳叆")
+						showToast(this.translate("input_station_name"))
 						return
 					}
 					this.loading = true
@@ -1731,8 +1665,8 @@
 						this.stationEdit = {
 							stationID: this.getMaxStationNo + 1,
 							name: name,
-							x: agv.x || 0,
-							y: agv.y || 0,
+							x: Math.round(Number(agv.x || 0) * 1000) / 1000,
+							y: Math.round(Number(agv.y || 0) * 1000) / 1000,
 							angle: agv.angle || 0,
 						}
 						const angle = this.getStantardAngle(this
@@ -1773,7 +1707,7 @@
 					}
 
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				} finally {
 					this.loading = false
 				}
@@ -1791,8 +1725,22 @@
 				else
 					this.showClearName = false
 			},
+			validateNumber(input) {
+				// 甯哥敤姝e垯妯″紡
+				const patterns = {
+					integer: /^-?\d+$/, // 鏁存暟锛屽寘鎷礋鏁�[citation:6]
+					positiveInt: /^[1-9]\d*$/, // 姝f暣鏁�[citation:7]
+					decimal: /^-?\d*\.?\d+$/, // 灏忔暟 (鏀寔鍍�".1" 杩欐牱鐨勬牸寮�
+					decimalFixed: /^-?\d*\.?\d{0,3}$/, // 鏈�涓や綅灏忔暟 [citation:5][citation:8]
+				};
+				// 閫夋嫨闇�鐨勬ā寮忚繘琛屾祴璇曪紝渚嬪浣跨敤 decimal
+				return patterns.decimalFixed.test(input);
+			},
 			onInputStationX(event) {
-				console.log(event)
+				if (!this.validateNumber(event.detail.value)) {
+					showToast(this.translate("input_content_only_numbers_and_signs_and_decimal"))
+					return
+				}
 				this.stationEdit.x = Number(event.detail.value);
 				console.log(this.stationEdit.x)
 				this.ctxDataStr = JSON.stringify([{
@@ -1808,6 +1756,10 @@
 			},
 
 			onInputStationY(event) {
+				if (!this.validateNumber(event.detail.value)) {
+					showToast(this.translate("input_content_only_numbers_and_signs_and_decimal"))
+					return
+				}
 				this.stationEdit.y = Number(event.detail.value);
 				console.log(this.stationEdit.y)
 				this.ctxDataStr = JSON.stringify([{
@@ -1821,6 +1773,8 @@
 					}
 				}])
 			},
+
+
 			clickClearStationX() {
 				this.stationEdit.x = 0
 				this.ctxDataStr = JSON.stringify([{
@@ -1937,14 +1891,14 @@
 						}
 					}])
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 			},
 			async clickPositionStation() {
 				try {
 					const infoAgv = await this.loadAgvState()
-					this.stationEdit.x = infoAgv.x
-					this.stationEdit.y = infoAgv.y
+					this.stationEdit.x = Math.round(Number(infoAgv.x) * 1000) / 1000;
+					this.stationEdit.y = Math.round(Number(infoAgv.y) * 1000) / 1000;
 					this.stationEdit.angle = infoAgv.angle
 					const angle = this.getStantardAngle(this
 						.stationEdit.angle * 180 / Math.PI)
@@ -1970,7 +1924,7 @@
 
 					])
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 			},
 			async loadTeachingMode() {
@@ -1984,7 +1938,7 @@
 					}
 
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 					return {
 						Public: [],
 						Stations: []
@@ -2022,7 +1976,7 @@
 					this.ctxDataStr = JSON.stringify(list)
 
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 
 				}
 			},
@@ -2064,7 +2018,7 @@
 						}
 					])
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 			},
 			async refreshAgvPosition() {
@@ -2110,8 +2064,9 @@
 								const stationAdd = []
 								for (let i in publicNew) {
 									const item = publicNew[i]
-									const curIndex = publicOld.findIndex((a) => a.name == item.name && a.edge_name == item.edge_name )
-									
+									const curIndex = publicOld.findIndex((a) => a.name == item.name && a.edge_name ==
+										item.edge_name)
+
 									if (curIndex < 0) {
 										publicAdd.push(item)
 										publicOld.push(item)
@@ -2185,7 +2140,7 @@
 					}
 					//setTimeout(this.refreshAgvPosition, 1000);
 				} catch (ex) {
-					// showError(ex).then((res) => {
+					// showError(ex,this.translate('error')).then((res) => {
 
 					// })
 					showToast(ex)
@@ -2234,29 +2189,16 @@
 					return []
 				}
 			},
-			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: "绀烘暀缁撴潫"
+						title: this.translate("teaching_end")
 					})
 					await teachingModeFlag(this.vehicleIp, teachingMode)
 
 					this.mapOperationStatus = "end"
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				} finally {
 					uni.hideLoading()
 				}
@@ -2266,14 +2208,14 @@
 				const _this = this
 				try {
 					uni.showLoading({
-						title: "绀烘暀寮�"
+						title: this.translate("teaching_start")
 					})
 					this.loading = false
 					this.mapOperationStatus = "teaching"
 					await _this.teachingStart("Public")
 
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				} finally {
 					this.loading = false
 					uni.hideLoading()
@@ -2291,7 +2233,12 @@
 			},
 			clickTeachingReset() {
 				const _this = this
-				showModal("宸茶褰曠殑璺緞灏嗕細琚垹闄ゃ�", "鏄惁瑕侀噸鏂拌褰曪紵")
+				showModal({
+						title: this.translate("ask_re_recording"),
+						content: `${this.translate('recorded_path_will_be_deleted')}`,
+						confirmText: this.translate('yes'),
+						cancelText: this.translate("no"),
+					})
 					.then(async (res) => {
 						if (res) {
 							try {
@@ -2310,7 +2257,7 @@
 
 
 							} catch (ex) {
-								showError(ex)
+								showError(ex, this.translate('error'))
 							} finally {
 								this.loading = false
 							}
@@ -2326,7 +2273,7 @@
 
 				this.mapOperationStatus = "save"
 				try {} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 
 			},
@@ -2338,30 +2285,6 @@
 			},
 			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 mode_type = this
 						.teachingModeCur
@@ -2382,32 +2305,17 @@
 					}
 
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 
 			},
 			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
-					// )
 					this.teachingModeCur.teaching_flag = 0
 					await this.finishTeaching(this.teachingModeCur)
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				} finally {
 					this.loading = false
 				}
@@ -2430,11 +2338,11 @@
 						const res = await teachingModeFlag(this.vehicleIp, this
 							.teachingModeCur)
 						if (val == 1)
-							showToast("宸插皢绀烘暀鍒囨崲鎴愬弻鍚戞ā寮�)
+							showToast(this.translate("teaching_switched_bidirectional_mode"))
 						else if (val == 2)
-							showToast("宸插皢绀烘暀鍒囨崲鎴愭櫤鑳芥ā寮�)
+							showToast(this.translate("teaching_switched_intelligent_mode"))
 						else
-							showToast("宸插皢绀烘暀鍒囨崲鎴愰粯璁ゆā寮�)
+							showToast(this.translate("teaching_switched_default_mode"))
 						/*if (val == 0)
 							showToast(
 								"宸插皢璇ヨ矾娈佃矾寰勪繚瀛樹负涓昏矾绀烘暀璺嚎"
@@ -2470,17 +2378,26 @@
 					}
 
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 			},
 			clickTeachingDelete() {
 				this.$refs
 					.refPopupOperateTeaching
 					.close()
-				this.removeTeachingMode(
-					this
-					.selectTeachingMode
-				)
+				const _this = this
+				showModal({
+						title: this.translate("ask_deleting_teaching"),
+						content: `${this.translate('deleted_teaching_cannot_recovered')}`,
+						confirmText: this.translate('delete'),
+						cancelText: this.translate("cancel"),
+
+					})
+					.then((res) => {
+						if (res) {
+							_this.removeTeachingMode(_this.selectTeachingMode)
+						}
+					})
 			},
 
 			clickTeachingEdit() {
@@ -2504,7 +2421,7 @@
 			async dialogTeachingUpdateConfirm() {
 				try {
 					uni.showLoading({
-						title: "鏇存柊绀烘暀涓�
+						title: this.translate("teaching_updating")
 					})
 					const item = this.selectTeachingMode
 					await updateSplitTeachingData(this.vehicleIp, item)
@@ -2515,10 +2432,9 @@
 								param: item,
 							}])
 					this.selectTeachingMode = {}
-					showToast("鏇存柊绀烘暀鎴愬姛")
+					showToast(this.translate("teaching_update_success"))
 				} catch (ex) {
-					showError(
-						ex)
+					showError(ex, this.translate('error'))
 				} finally {
 					uni.hideLoading()
 				}
@@ -2531,7 +2447,7 @@
 			async removeTeachingMode(item) {
 				try {
 					uni.showLoading({
-						title: "鍒犻櫎绀烘暀涓�
+						title: this.translate("teaching_deleting")
 					})
 					await deleteSplitTeachingData(this.vehicleIp, item
 						.edge_name, item.name)
@@ -2542,10 +2458,9 @@
 								method: "remove_teaching_path",
 								param: item,
 							}])
-					showToast("鍒犻櫎绀烘暀鎴愬姛")
+					showToast(this.translate("teaching_delete_success"))
 				} catch (ex) {
-					showError(
-						ex)
+					showError(ex, this.translate('error'))
 				} finally {
 					uni.hideLoading()
 				}
@@ -2553,16 +2468,20 @@
 			async removeTeachingModeData(data) {
 				try {
 					uni.showLoading({
-						title: "鍒犻櫎绀烘暀鏁版嵁涓�
+						title: this.translate("teaching_data_deleting")
 					})
 					await delTeachingModeData(this.vehicleIp, data)
-					showToast("鍒犻櫎绀烘暀鎴愬姛")
+					showToast(this.translate("teaching_delete_success"))
 					this.reloadTeachingMode()
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				} finally {
 					uni.hideLoading()
 				}
+			},
+			translate(t) {
+				if (typeof this.$t == "function") return this.$t(`page.${t}`)
+				else return t;
 			},
 
 		}
@@ -2733,7 +2652,7 @@
 				.left {
 					display: flex;
 					flex-direction: column;
-					width: 180rpx;
+					width: 240rpx;
 					color: #aaa;
 
 					.line {
@@ -2958,6 +2877,7 @@
 
 					.text {
 						margin: auto;
+						white-space: nowrap;
 
 					}
 				}
diff --git a/pages/map/infos/pallet-size.vue b/pages/map/infos/pallet-size.vue
new file mode 100644
index 0000000..4e915aa
--- /dev/null
+++ b/pages/map/infos/pallet-size.vue
@@ -0,0 +1,175 @@
+<template>
+	<view class="pages-map-pallet-size">
+		<view class="tip">{{translate("pallet_size")}}</view>
+		<view class="row">
+			<view class="width-50">
+				<text class="attr">{{translate("length")}}:</text>
+				<input class=" val " type="number" :value="palletLength" @blur="onBlurPalletLength"
+					@confirm="onConfirmPalletLength" :maxlength="10" :placeholder="translate('input_please')" />
+				cm
+			</view>
+			<view class="width-50">
+				<text class="attr">{{translate("width")}}:</text>
+				<input class=" val " type="number" :value="palletWidth" @blur="onBlurPalletWidth"
+					@confirm="onConfirmPalletWidth" :maxlength="10" :placeholder="translate('input_please')" />
+
+				cm
+			</view>
+		</view>
+	</view>
+</template>
+<script>
+	import {
+		showToast,
+		showModal,
+		session,
+		showError,
+		showInfo
+	} from "@/comm/utils.js"
+	import {
+		Button
+	} from 'antd-mobile-vue-next'
+
+	import {
+		savePalletSize,
+		getPalletSize,
+
+	} from "@/api/vehicle.js"
+	export default {
+		name: "PagesMapPalletSize",
+		components: {
+			'a-button': Button
+		},
+		props: {
+			ip: {
+				type: String,
+				default () {
+					return ''
+				}
+			},
+		},
+		data() {
+			return {
+				palletLength: "200",
+				palletWidth: "1000"
+			}
+		},
+		computed: {
+
+		},
+
+		mounted() {
+			this.loadData()
+
+		},
+		methods: {
+			async loadData() {
+				try {
+					const res = await getPalletSize(this.ip)
+					this.palletLength = res?.length ? Math.floor(res.length * 100) : "200"
+					this.palletWidth = res?.width ? Math.floor(res.width * 100) : "1000"
+				} catch (ex) {
+
+					showError(ex, this.translate('error'))
+				}
+			},
+
+			validateNumber(input) {
+				// 甯哥敤姝e垯妯″紡
+				const patterns = {
+					integer: /^-?\d+$/, // 鏁存暟锛屽寘鎷礋鏁�[citation:6]
+					positiveInt: /^[1-9]\d*$/, // 姝f暣鏁�[citation:7]
+					decimal: /^-?\d*\.?\d+$/, // 灏忔暟 (鏀寔鍍�".1" 杩欐牱鐨勬牸寮�
+					decimalFixed: /^-?\d*\.?\d{0,3}$/, // 鏈�涓や綅灏忔暟 [citation:5][citation:8]
+				};
+				// 閫夋嫨闇�鐨勬ā寮忚繘琛屾祴璇曪紝渚嬪浣跨敤 decimal
+				return patterns.integer.test(input);
+			},
+			async savePalletSize() {
+				try {
+					if (!this.validateNumber(this.palletLength)) {
+						console.log("input_content_only_numbers_or_signs")
+						showError(this.translate("input_content_only_numbers_or_signs"))
+						return
+					}
+					if (!this.validateNumber(this.palletWidth)) {
+						console.log("input_content_only_numbers_or_signs")
+						showError(this.translate("input_content_only_numbers_or_signs"))
+						return
+					}
+					const len = Number(this.palletLength) / 100
+					const width = Number(this.palletWidth) / 100
+					await savePalletSize(this.ip, len, width)
+					console.log("modify_success_restart_device", this.palletLength, this.palletWidth)
+					showToast(this.translate("modify_success_restart_device"))
+				} catch (ex) {
+					showError(ex, this.translate('error'))
+				}
+			},
+			onBlurPalletLength(event) {
+				this.palletLength = event.detail.value
+				this.savePalletSize()
+			},
+			onConfirmPalletLength(event) {
+				this.palletLength = event.detail.value
+				this.savePalletSize()
+			},
+
+			onBlurPalletWidth(event) {
+				this.palletWidth = event.detail.value
+
+				this.savePalletSize()
+
+			},
+			onConfirmPalletWidth(event) {
+				this.palletWidth = event.detail.value
+				this.savePalletSize()
+			},
+			translate(t) {
+				if (typeof this.$t == "function") return this.$t(`page.${t}`)
+				else return t;
+			},
+
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.pages-map-pallet-size {
+		.tip {
+			color: #888;
+			margin: 10rpx;
+			text-align: left;
+		}
+
+		.row {
+			margin: 5px;
+			display: flex;
+			flex-direction: row;
+
+			.width-50 {
+				width: calc(50% - 10rpx);
+				display: flex;
+				flex-direction: row;
+				padding-left: 10rpx;
+			}
+
+			.attr {
+				padding-right: 10rpx;
+			}
+
+			.val {
+				flex: 1;
+				padding-left: 10rpx;
+				color: #2d8cf0;
+				background-color: #fff;
+				border-bottom: 1px solid #ccc;
+
+			}
+		}
+
+
+
+
+	}
+</style>
\ No newline at end of file
diff --git a/pages/map/infos/scene-create.vue b/pages/map/infos/scene-create.vue
deleted file mode 100644
index 629e98a..0000000
--- a/pages/map/infos/scene-create.vue
+++ /dev/null
@@ -1,345 +0,0 @@
-<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"/>
-					<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,
-		showError,
-		showInfo
-	} 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) {
-
-					showError(ex)
-				}
-			},
-			async loadMapLaserData() {
-				try {
-					const info = await getMapLaserData(this.ip)
-					return info
-				} catch (ex) {
-					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) {
-					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/infos/scene-rename.vue b/pages/map/infos/scene-rename.vue
new file mode 100644
index 0000000..41b6c05
--- /dev/null
+++ b/pages/map/infos/scene-rename.vue
@@ -0,0 +1,152 @@
+<template>
+	<view class="pages-map-scene-rename">
+		<view class="tip">{{translate('input_scene_name')}}</view>
+		<view class="name-input">
+			<input ref="refInputName" :focus="true" :placeholder="translate('input_scene_name')" :value="inputName"
+				@input="onInputName" />
+			<uni-icons class="clear" color="#ccc" type="clear" size="20" v-if="inputName"
+				@click="clickClearName"></uni-icons>
+		</view>
+		<view class="text-button-group">
+			<a-button type="primary" class="button" :disabled="loading || inputName.trim() == ''"
+				@click="clickNameOK">{{translate('confirm')}} </a-button>
+			<a-button type="ghost" class="button" :disabled="loading"
+				@click="clickNameCancel">{{translate('cancel')}}</a-button>
+		</view>
+	</view>
+</template>
+<script>
+	import {
+		showToast,
+		showModal,
+		session,
+		showError,
+		showInfo
+	} from "@/comm/utils.js"
+	import {
+		Button
+	} from 'antd-mobile-vue-next'
+
+	import {
+		updateScene
+
+	} from "@/api/vehicle.js"
+	export default {
+		name: "PagesMapSceneRename",
+		components: {
+			'a-button': Button
+		},
+		props: {
+			ip: {
+				type: String,
+				default () {
+					return ''
+				}
+			},
+			sceneName: {
+				type: String,
+				default () {
+					return ''
+				}
+			},
+		},
+		data() {
+			return {
+				loading: false,
+				inputName: "",
+			}
+		},
+
+
+		watch: {
+			sceneName(val) {
+				this.inputName = val
+			}
+		},
+		methods: {
+			clickNameCancel() {
+				this.$emit("finish")
+			},
+			async clickNameOK() {
+				try {
+					uni.showLoading({
+						title: this.translate("update_scene_name")
+					})
+					this.loading = true
+					const name = this.inputName.trim()
+					if (!name) {
+						showToast(this.translate("scene_name_cannot_empty"))
+						return
+					}
+
+					if (name == this.sceneName) {
+						showToast(this.translate("scene_name_not_changed"))
+						return
+					}
+					await updateScene(this.ip, this.sceneName, name)
+					showToast(this.translate("update_scene_name_success"))
+					this.$emit("finish",name)
+				} catch (ex) {
+					showError(ex, this.translate('error'))
+				} finally {
+					uni.hideLoading()
+					this.loading = false
+				}
+
+			},
+
+			clickClearName() {
+				this.inputName = ""
+			},
+			onInputName(event) {
+				this.inputName = event.detail.value.trim();
+
+			},
+			translate(t) {
+				if (typeof this.$t == "function") return this.$t(`page.${t}`)
+				else return t;
+			},
+
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.pages-map-scene-rename {
+		.tip {
+			color: #888;
+			margin: 10rpx;
+			text-align: left;
+		}
+
+		.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);
+			}
+		}
+	}
+</style>
\ No newline at end of file
diff --git a/pages/map/js/ctx-mini.js b/pages/map/js/ctx-mini.js
index e5014a9..b433235 100644
--- a/pages/map/js/ctx-mini.js
+++ b/pages/map/js/ctx-mini.js
@@ -3477,7 +3477,7 @@
 			if (type == "string") {
 				let tip = ex
 				console.log(ex)
-				plus.nativeUI.alert(tip, undefined, "閿欒");
+				plus.nativeUI.alert(tip, undefined, this.translate('error'));
 				return
 			}
 			let exStr = JSON.stringify(ex)
@@ -3486,7 +3486,7 @@
 
 			let tip = typeof ex.msg == "string" ? ex.msg : exStr
 			console.log(tip)
-			plus.nativeUI.alert(tip, undefined, "閿欒");
+			plus.nativeUI.alert(tip, undefined, this.translate('error'));
 		},
 		showToast(ex) {
 			const type = typeof ex
diff --git a/pages/map/js/ctx.js b/pages/map/js/ctx.js
index aeca5a2..b7f3f50 100644
--- a/pages/map/js/ctx.js
+++ b/pages/map/js/ctx.js
@@ -115,14 +115,26 @@
 					stopContextMenu: true, // 绂佹闀挎寜鑿滃崟
 					fireRightClick: true,
 					fireMiddleClick: true,
-					targetFindTolerance: 10, // 澧炲ぇ瑙︽懜瀹瑰樊
 					isTouchSupported: true,
 					enableRetinaScaling: true,
 					renderOnAddRemove: false,
-					imageSmoothingEnabled: true
+					imageSmoothingEnabled: true,
+					perPixelTargetFind: true, // 鐐瑰嚮鍥惧舰鑷韩鎵嶉�涓�+					targetFindTolerance: 5 // 澧炲ぇ瑙︽懜瀹瑰樊
 				})
 
+				const originalShouldClearSelection = this.canvas._shouldClearSelection;
 
+				// 閲嶅啓鏂规硶
+				this.canvas._shouldClearSelection = function(e) {
+					// 鍙湁褰撶偣鍑诲湪瀵硅薄涓婏紝骞朵笖鏄垜浠湡鏈涚殑鏉′欢锛堝杈规锛夋椂锛屾墠鎵ц榛樿鐨勬竻闄ら�杈�+					// 鐐瑰嚮绌虹櫧澶勭洿鎺ヨ繑鍥�false锛屼笉娓呴櫎閫夋嫨
+					if (e.target && e.subTargets) {
+						// 杩欓噷鍙互娣诲姞鏇寸簿缁嗙殑鍒ゆ柇锛屼緥濡傜粨鍚堝悗闈㈢殑杈规鍒ゆ柇
+						return originalShouldClearSelection.call(this, e);
+					}
+					return false;
+				};
 				this.canvas.clear()
 				this.eleWidth = cantainerEl.clientWidth
 				this.eleHeight = cantainerEl.clientHeight
@@ -445,7 +457,24 @@
 				_this.onOjectMoving(e.target)
 
 			});
+			_this.canvas.on("mouse:down", function(opt) {
+				console.log("mouse:down", opt);
+				// 濡傛灉鐐瑰嚮鐨勬槸绌虹櫧澶勶紙娌℃湁鐩爣瀵硅薄锛�+				if (!opt.target) {
+					// 鍙栨秷褰撳墠閫変腑
 
+					_this.canvas.discardActiveObject();
+					// 閲嶆柊娓叉煋鐢诲竷
+					_this.canvas.renderAll();
+				} else {
+					if (!opt.target.selectable) {
+						_this.canvas.discardActiveObject();
+						// 閲嶆柊娓叉煋鐢诲竷
+						_this.canvas.renderAll();
+					}
+					console.log("mouse:down", opt.target.eleType);
+				}
+			});
 
 			cantainerEl.addEventListener('touchstart', function(e) {
 				//		console.log('touchstart:', e.touches.length);
@@ -508,7 +537,7 @@
 		},
 		canvasTouchStart(e) {
 			const _this = this
-			_this.pointerSelectObject(e)
+			//	_this.pointerSelectObject(e)
 			if (!_this.canvas.getActiveObject()) {
 				// 鏍规嵁瑙︽懜鐐规暟閲忓垽鏂氦浜掔被鍨� 				if (e.touches.length === 1) {
@@ -791,6 +820,33 @@
 			const objActive = this.canvas.getActiveObject()
 			let pointerList = []
 			let pointerList2 = []
+			return
+			if (objActive instanceof fabric.Group) {
+
+				const ptr = pointer
+				// 璁$畻鐩稿浜�Group 鐨勬湰鍦板潗鏍�+				const localX = ptr.x - objActive.left;
+				const localY = ptr.y - objActive.top;
+				// 鍒ゆ柇杩欎釜鏈湴鍧愭爣鏄惁鈥滅湡鐨勨�鐐逛腑浜嗘煇涓瓙瀵硅薄
+				let hit = false;
+				objActive._objects.forEach((obj) => {
+					if (obj.selectable) {
+						if (this.isPointOnStroke(obj, pointer, 8)) {
+							hit = true;
+						}
+
+					}
+				});
+				console.log(hit)
+				// 濡傛灉宸茬粡閫変腑锛屼絾杩欎竴娆℃病鐐逛腑浠讳綍瀛愬璞�鈫�鍙栨秷閫変腑
+				if (!hit) {
+					this.canvas.discardActiveObject();
+					this.canvas.requestRenderAll();
+
+				}
+			}
+
+
 			for (let i = objects.length - 1; i >= 0; i--) {
 				const obj = objects[i];
 
@@ -1268,7 +1324,7 @@
 			}
 		},
 		clearObjects() {
-			
+
 			if (!this.canvas) return;
 			this.canvas.discardActiveObject()
 			const objects = this.canvas.getObjects()
@@ -1862,12 +1918,20 @@
 			const scale2 = 1 / scale
 
 			let list = this.canvas.getObjects()
-			const filter = ["agv", "current_teaching", "edit_teaching", "edit_teaching_pt", "cmd", "station",
+			const filter = ["agv", "public_teaching", "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") {
+				if (obj.eleType == "public_teaching") {
+					// const subObjs = obj.getObjects()
+					// subObjs.forEach((obj2) => {
+					// 	obj2.set({
+					// 		strokeWidth: obj2.strokeOriginWidth * scale2
+					// 	})
+					// })
+				} else if (obj.eleType == "edit_teaching") {
 					obj.set({
 						strokeWidth: 2 * scale2
 					})
@@ -1877,7 +1941,7 @@
 						scaleY: scale2
 					})
 				}
-				if (obj.eleType == "station" || obj.eleType == "edit_teaching") {
+				 if (obj.eleType == "station" || obj.eleType == "edit_teaching") {
 					const tipObj = obj.tipObj
 					if (tipObj) {
 						tipObj.set({
@@ -1887,8 +1951,7 @@
 						})
 						tipObj.setCoords()
 					}
-				}
-				if (obj.eleType == "cmd") {
+				} else if (obj.eleType == "cmd") {
 					const obj2 = obj.mainObj
 					if (obj2?.eleType == "station") {
 						if (obj.id == `cancel`) {
@@ -2122,57 +2185,126 @@
 					pos_list.push(item)
 				}
 			})
+			let teachingGroup = []
 			let path2 = ""
-
-			const theta = 20;
+			let pathArrow = ""
+			const theta = 30;
 			let headlen = 10;
-			var main_road = teachingData.main_road 
+			var main_road = teachingData.main_road
 
 			const len = pos_list.length
 			let fromX = 0,
 				fromY = 0,
 				toX = 0,
 				toY = 0;
+			let right = 0
+			let bottom = 0
+			let left = this.mapInfo.img_x
+			let top = this.mapInfo.img_y
 
-
+			var scale = this.canvas.getZoom()
+			if (scale < 1) {
+				scale = 1
+			}
+			const scale2 = 1 / scale
 			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 (fromX == pt2.x && fromY == pt2.y) {
+					pos_list.splice(index, 1)
+					index--;
+					continue;
+				}
 				if (index > 0) {
-					
+					if (type == "public_teaching") {
+						const offset = index % 50
+						if (offset > 20 && offset < 41) {
+							if (offset == 21) {
+
+								pathArrow = `M${pt2.x} ${pt2.y}`
+							} else
+								pathArrow += ` L${pt2.x} ${pt2.y}`
+							if (offset == 40) {
+								const pt = pos_list[index - 2]
+								fromX = this.getXOnImg(pt.x)
+								fromY = this.getYOnImg(pt.y)
+								//	console.log(fromX,fromY, this.getXOnImg(pt.x),this.getYOnImg(pt.y))
+								toX = pt2.x
+								toY = 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;
+								pathArrow += " M " + arrowX + " " + arrowY;
+								pathArrow += " L " + toX + " " + toY;
+								arrowX = toX + botX;
+								arrowY = toY + botY;
+								pathArrow += " L " + arrowX + " " + arrowY;
+
+								const objArrow = new fabric.Path(
+									pathArrow, {
+										stroke: "#fff",
+										strokeWidth:2,// * scale2,
+										strokeOriginWidth:2,
+										fill: "#ffffff00",
+										selectable: false,
+
+									})
+								teachingGroup.push(objArrow)
+							}
+						}
+					}
 					path2 += ` L${pt2.x} ${pt2.y}`
 				} else {
 					if (main_road === undefined)
 						main_road = pt.main_road
-				
+
 					path2 = `M${pt2.x} ${pt2.y}`
 				}
+				// if (left > pt2.x) {
+				// 	left = pt2.x
+				// }
+				// if (top > pt2.y) {
+				// 	top = pt2.y
+				// }
+				// if (right < pt2.x) {
+				// 	right = pt2.x
+				// }
+				// if (bottom < pt2.y) {
+				// 	bottom = pt2.y
+				// }
 				fromX = pt2.x
 				fromY = pt2.y
 
-
 			}
-
 			// console.log("addTeachingPath",path2)
 			// path2 += " Z"
-			let strokeWidth = 1.5
+			let strokeWidth = 8
 			let stroke = "#69C0FF"
 			if (type == "station_teaching") {
-				stroke = "#69C0FF"
+				stroke = "#69C0FF80"
 			} else {
 				if (main_road == 1) {
 					//stroke = "#69C0FF"
 					if (teachingData.bidirection == 1) {
-						stroke = "#ffaa00"
+						stroke = "#ffaa0080"
 					}
-					strokeWidth = 3
+					strokeWidth = 12
 
 				} else {
+					strokeWidth = 8
 					if (teachingData.bidirection == 1) {
-						stroke = "#FF00FF"
+						stroke = "#FF00FF80"
 					}
 				}
 			}
@@ -2185,15 +2317,29 @@
 				lenTeaching = list.length
 			}
 
+
 			let ptList = []
-			const objPath = new fabric.Path(
+			const obj = new fabric.Path(
 				path2, {
 					id: id,
-					eleType: type,
 					stroke: stroke,
-					strokeWidth,
+					strokeWidth:strokeWidth,// * scale2,
+					strokeOriginWidth:strokeWidth,
 					// strokeDashArray: [5, 3],
 					// strokeLineCap: 'butt',
+					// originX: "center",
+					// originY: "center",
+					selectable: true,
+					fill: "#ffffff00",
+				})
+			// this.canvas.add(obj)
+			teachingGroup.unshift(obj)
+			const objPath = new fabric.Group(
+				teachingGroup, {
+					id: id,
+					eleType: type,
+					// stroke: stroke,
+					// strokeWidth,
 					fill: "#ffffff00",
 					hasControls: false,
 					lockRotation: true,
@@ -2201,7 +2347,12 @@
 					lockScalingY: true,
 					lockMovementX: true,
 					lockMovementY: true,
-
+					// perPixelTargetFind: true,
+					// left: left - strokeWidth / 2,
+					// top: top - strokeWidth / 2,
+					// width: right - left + strokeWidth,
+					// height: bottom - top + strokeWidth,
+					
 					mainRoad: main_road,
 					data: teachingData
 				})
@@ -2990,8 +3141,11 @@
 				y: this.getYOnImg(param.y)
 			}
 			if (this.objAgvLaser) {
-				this.objAgvLaser.set({stroke: "#00ff00",	rx: 2,
-				ry:2})
+				this.objAgvLaser.set({
+					stroke: "#00ff00",
+					rx: 2,
+					ry: 2
+				})
 			}
 			let ellipse = new fabric.Ellipse({
 				id: "agv_laser",
@@ -3026,7 +3180,7 @@
 				selectable: false,
 				hasControls: false,
 			});
-			
+
 			this.objAgvLaserLine = line
 			this.canvas.add(line)
 		},
@@ -3054,13 +3208,13 @@
 					originX: "center",
 					originY: "center",
 				});
-				
+
 				objs.push(ellipse)
-				if (left > pt2.x-2) {
-					left = pt2.x-2
+				if (left > pt2.x - 2) {
+					left = pt2.x - 2
 				}
-				if (top > pt2.y-2) {
-					top = pt2.y-2
+				if (top > pt2.y - 2) {
+					top = pt2.y - 2
 				}
 				if (right < pt2.x + 2) {
 					right = pt2.x + 2
@@ -3615,9 +3769,6 @@
 						const curIndex = list.findIndex((a) => a.id == id)
 						if (curIndex > -1) {
 							const obj = list[curIndex]
-
-
-
 							let strokeWidth = 1.5
 							let stroke = "#69C0FF"
 
@@ -3731,7 +3882,7 @@
 			if (type == "string") {
 				let tip = ex
 				console.log(ex)
-				plus.nativeUI.alert(tip, undefined, "閿欒");
+				plus.nativeUI.alert(tip, undefined);
 				return
 			}
 			let exStr = JSON.stringify(ex)
@@ -3740,7 +3891,7 @@
 
 			let tip = typeof ex.msg == "string" ? ex.msg : exStr
 			console.log(tip)
-			plus.nativeUI.alert(tip, undefined, "閿欒");
+			plus.nativeUI.alert(tip, undefined);
 		},
 		showToast(ex) {
 			const type = typeof ex
diff --git a/pages/map/scene.vue b/pages/map/scene.vue
index c787b5d..c0f2929 100644
--- a/pages/map/scene.vue
+++ b/pages/map/scene.vue
@@ -1,20 +1,22 @@
 <template>
 	<view class="pages-scene">
 		<view class="map-content" v-if="opSceneType =='' ">
-			<image v-if="opType != 'extend'" class="img" src="/images/image_25.png" alt=" 鍥剧墖" mode="aspectFit" />
-			<image v-else class="img" :src="extendBase64Img" alt=" 鍥剧墖" mode="aspectFit" />
-			<view v-if="opType != 'extend'" class="space">娌℃湁鎵惧埌绗﹀悎鏉′欢鐨勫湴鍥�/view>
+			<image v-if="opType != 'extend'" class="img" src="/images/image_25.png" alt=" picture" mode="aspectFit" />
+			<image v-else class="img" :src="extendBase64Img" alt=" picture" mode="aspectFit" />
+			<view v-if="opType != 'extend'" class="space">{{translate("no_map_found")}}</view>
 			<!-- <view class="loading-view">{{mapserverIsOk?"鏋勫浘绋嬪簭鍑嗗灏辩华":"绛夊緟鏋勫浘绋嬪簭灏辩华..."}}
 				<view v-if="!mapserverIsOk" class="auto-circle"></view>:disabled="!mapserverIsOk" 
 			</view> -->
 			<view class="text-button-group">
 				<a-button type="primary" class="button" @click="clickStartConstructScene">
-					{{opType == 'extend'?"寮�鎵╁睍":'寮�鏋勫缓'}}
+					{{opType == 'extend'?translate("start_extending"):translate("start_building")}}
 				</a-button>
-				<a-button v-if="opType != 'extend'" type="primary" class="button"
-					:disabled="loading && localSceneList.length == 0" @click="clickDownloadScene" disabled>
-					涓嬭浇鍦烘櫙
-				</a-button>
+				<navigator :url="`/pages/index/backup?ip=${ip}&opType=download`" hover-class="other-navigator-hover">
+					<a-button v-if="opType != 'extend'" type="primary" class="button"
+						:disabled="loading && localSceneList.length == 0">
+						{{translate("download_scenes")}}
+					</a-button>
+				</navigator>
 			</view>
 
 		</view>
@@ -26,33 +28,36 @@
 		</view>
 		<view class="bottom">
 			<view class="bottom-content" v-if="opSceneType =='add_name' ">
-				<view class="tip">璇疯緭鍏ュ満鏅悕绉�/view>
+				<view class="tip">{{translate('input_scene_name')}}</view>
 				<view class="name-input">
-					<input ref="refInputName" :focus="true" placeholder="璇疯緭鍏ュ満鏅悕绉� :value="sceneName"
-						@input="onInputName" />
+					<input ref="refInputName" :focus="true" :placeholder="translate('input_scene_name')"
+						:value="sceneName" @input="onInputName" />
 					<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>
+						@click="clickNameOK">{{translate("confirm")}}</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 class="tip">{{translate("scene_construction_in_progress")}}</view>
 				<view>
 					<!-- 璇锋搷浣滄惉杩愯溅鎵弿鍦板浘瑕嗙洊鐨勫尯鍩�-->
 					{{mapCreatePhase}}
 				</view>
 
 				<view class="text-button-group">
-					<a-button type="primary" class="button" :disabled="loading" @click="clickScanFinish">鎵弿瀹屾垚</a-button>
+					<a-button type="primary" class="button" :disabled="loading"
+						@click="clickScanFinish">{{translate("scan_completed")}}</a-button>
 				</view>
 			</view>
 			<view class="bottom-content" v-else-if="opSceneType =='finish'">
-				<view class="tip"> 鈥渰{sceneName}}鈥漿{opType == "extend"?"鍦烘櫙鎵╁睍缁撴潫"+"":"鍦烘櫙鏋勫缓缁撴潫"}}</view>
+				<view class="tip">
+					鈥渰{sceneName}}鈥漿{opType == "extend"?translate("scene_extend_completed"):translate("scene_construction_completed")}}
+				</view>
 				<!-- 	<view> 宸叉垚鍔熸瀯寤衡�{{sceneName}}鈥�/view> -->
 				<view class="loading-view">
 					{{mapCreatePhase}}
@@ -60,9 +65,9 @@
 				</view>
 				<view class="text-button-group">
 					<a-button type="primary" class="button" v-if="mapServerPhase === 6"
-						@click="clickRecreate">{{opType == "extend"?"閲嶇幇鎵╁睍":"閲嶇幇鏋勫缓"}}</a-button>
+						@click="clickRecreate">{{opType == "extend"?translate('re_extend'):translate("rebuild")}}</a-button>
 					<a-button type="primary" class="button" v-else :disabled=" mapServerPhase !==5 || loading"
-						@click="clickFinish">{{opType == "extend"?"鎵╁睍瀹屾垚":"鏋勫缓瀹屾垚"}}</a-button>
+						@click="clickFinish">{{opType == "extend"?translate("extend_completed"):translate("construction_completed")}}</a-button>
 				</view>
 			</view>
 		</view>
@@ -127,17 +132,17 @@
 		computed: {
 			mapCreatePhase() {
 				if (this.mapServerPhase == 1) {
-					return "鍦烘櫙鏋勫缓鏈嶅姟锛氭湭鍚姩"
+					return this.translate("scene_construction_service_not_start")
 				} else if (this.mapServerPhase == 2) {
-					return "鍦烘櫙鏋勫缓鏈嶅姟锛氬惎鍔ㄤ腑"
+					return this.translate("scene_construction_service_starting")
 				} else if (this.mapServerPhase == 3) {
-					return "鍦烘櫙鏋勫缓涓�
+					return this.translate("scene_construction_in_progress")
 				} else if (this.mapServerPhase == 4) {
-					return "鍦烘櫙鏋勫浘淇濆瓨涓�
+					return this.translate("scene_composition_saving")
 				} else if (this.mapServerPhase == 5) {
-					return "鍦烘櫙鏋勫浘淇濆瓨鎴愬姛"
+					return this.translate("scene_composition_save_success")
 				} else if (this.mapServerPhase == 6) {
-					return "鍦烘櫙鏋勫浘淇濆瓨澶辫触"
+					return this.translate("scene_composition_save_failed")
 				} else
 					return this.mapServerPhase
 			}
@@ -168,7 +173,12 @@
 
 			if (this.opSceneType == "scan") {
 
-				showModal("宸叉瀯寤哄満鏅皢浼氳鍒犻櫎", "鏄惁涓柇鍦烘櫙鏋勫缓锛�).then((res) => {
+				showModal({
+					title: this.translate("ask_interrupt_scene_construction"),
+					content: `${this.translate('built_scene_will_be_deleted')}`,
+					confirmText: this.translate('yes'),
+					cancelText: this.translate("no"),
+				}).then((res) => {
 					if (res) {
 						this.opSceneType = ""
 						if (this.opType != "") {
@@ -263,7 +273,7 @@
 
 				} catch (ex) {
 
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 			},
 			async loadMapInfo(id) {
@@ -280,8 +290,7 @@
 
 					const res = await getMapServerPhase(this.ip)
 					this.mapServerPhase = res || 1
-					if(this.opSceneType === "finish")
-					{
+					if (this.opSceneType === "finish") {
 						if (this.mapServerPhase === 5 || this.mapServerPhase === 6) {
 							this.saveMapIsOk = true
 							return
@@ -333,14 +342,14 @@
 						})
 						const now = new Date()
 						const date = `${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}`
-						showToast("鍔犺浇搴曞浘澶辫触")
+						showToast(this.translate("loading_base_map_failed"))
 						const log = {
 							date,
 							method: `POST`,
 							url: "app/log/load_img",
 							param: param.msg,
 							statusCode: 100,
-							data: "鍔犺浇搴曞浘澶辫触"
+							data: this.translate("loading_base_map_failed")
 						}
 						const listLog = session.getValue("request_log") || []
 						listLog.unshift(log)
@@ -362,7 +371,7 @@
 					const list = session.getValue("scene_db") || []
 					return list
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 					return []
 				}
 			},
@@ -378,26 +387,30 @@
 						this.opSceneType = "add_name"
 					}
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 			},
 			async clickDownloadScene() {
 				try {
-					this.loading = true
-					uni.showLoading({
-						title: "涓嬭浇鍦烘櫙涓�
+					uni.navigateTo({
+						url: `/pages/index/backup?ip=${this.ip}&opType=download`,
+
 					})
-					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, //杩斿洖灞傛暟锛�鍒欎笂涓婇〉
-					})
+					// this.loading = true
+					// uni.showLoading({
+					// 	title: this.translate("downloading_scene")
+					// })
+					// const data = this.localSceneList[0].data
+					// await saveDBData(this.ip, data)
+					// showToast(this.translate("download_scene_success"))
+					// this.opSceneType = ""
+					// const eventChannel = this.getOpenerEventChannel();
+					// eventChannel.emit('create_finish', this.sceneName);
+					// uni.navigateBack({
+					// 	delta: 1, //杩斿洖灞傛暟锛�鍒欎笂涓婇〉
+					// })
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				} finally {
 					this.loading = false
 					uni.hideLoading()
@@ -415,11 +428,11 @@
 				try {
 					this.loading = true
 					uni.showLoading({
-						title: "寮�鏋勫缓鍦烘櫙"
+						title: this.translate("start_building")
 					})
 					const name = this.sceneName.trim()
 					if (!name) {
-						showToast("绔欑偣鍚嶇О杩樻湭杈撳叆")
+						showToast(this.translate("input_scene_name"))
 						return
 					}
 
@@ -428,14 +441,14 @@
 					await addMap(this.ip, name)
 					this.checkMapServerPhase()
 					uni.showLoading({
-						title: "寮�鎵弿鍦板浘"
+						title: this.translate("start_scanning_map")
 					})
 					this.mapId = ""
 					this.opSceneType = 'scan'
 
 
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 					this.opSceneType = 'add_name'
 				} finally {
 					this.loading = false
@@ -458,7 +471,7 @@
 				try {
 					this.loading = true
 					uni.showLoading({
-						title: "鍦板浘鎵弿缁撴潫"
+						title: this.translate("end_scanning_map")
 					})
 					this.opSceneType = "finish"
 					uni.setNavigationBarTitle({
@@ -479,7 +492,12 @@
 
 				} catch (ex) {
 					console.log(ex)
-					showModal("璇锋鏌ヨ溅杈嗚繛鎺ワ紝骞堕噸鏂板紑濮嬫瀯寤哄満鏅�, "鍦烘櫙鏋勫缓澶辫触", false, "纭畾").then((res) => {
+					showModal({
+						title: this.translate("scene_construction_failed"),
+						content: `${this.translate('check_vehicle_connection_and_restart_building_scene')}`,
+						confirmText: this.translate('ok'),
+						showCancel: false
+					}).then((res) => {
 						this.opSceneType = ''
 
 					})
@@ -494,7 +512,7 @@
 
 					this.loading = true
 					uni.showLoading({
-						title: "缁撴潫鍦烘櫙鏋勫缓"
+						title: this.translate("scene_construction_completed")
 					})
 					this.opSceneType = ""
 					const eventChannel = this.getOpenerEventChannel();
@@ -641,7 +659,10 @@
 
 			},
 
-
+			translate(t) {
+				if (typeof this.$t == "function") return this.$t(`page.${t}`)
+				else return t;
+			},
 		}
 	}
 </script>
diff --git a/pages/map/task.vue b/pages/map/task.vue
index a5bcc03..d77571a 100644
--- a/pages/map/task.vue
+++ b/pages/map/task.vue
@@ -2,8 +2,8 @@
 	<view class="pages-map-task">
 		<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 class="title">{{translate("failed_vehicle_connection")}}</view>
+			<view class="space">{{translate("check_network_or_reload")}}</view>
 		</view>
 		<view class="map-content" v-else>
 			<view class="content">
@@ -13,12 +13,12 @@
 				<view class="button-group">
 					<view type="text" class="button" @click="clickTaskSet">
 						<uni-icons class="img" type="gear-filled" size="40" color="#1890FF"></uni-icons>
-						<view class="text"> 璁剧疆浠诲姟</view>
+						<view class="text"> {{translate("task_set")}}</view>
 
 					</view>
 					<view type="text" class="button" @click="clickTaskRecord">
 						<text class="ico task-list" />
-						<view class="text"> 浠诲姟璁板綍</view>
+						<view class="text"> {{translate("task_history")}}</view>
 						
 						
 
@@ -54,7 +54,7 @@
 		data() {
 			return {
 				vehicleIp: "",
-				navigationBarTitle: "鍦板浘浠诲姟",
+				navigationBarTitle: this.translate("task_set"),
 				unlinked: false
 			}
 		},
@@ -117,7 +117,7 @@
 					const info = await getAgvState(this.vehicleIp)
 					return info
 				} catch (ex) {
-					showError(ex)
+					showError(ex,this.translate('error'))
 					return {}
 				}
 			},
@@ -126,7 +126,7 @@
 					const info = await stations(this.vehicleIp)
 					return info.station_list || []
 				} catch (ex) {
-					showError(ex)
+					showError(ex,this.translate('error'))
 					return []
 				}
 			},
@@ -140,7 +140,10 @@
 					url: `/pages/task/log-list?ip=${this.vehicleIp}`
 				})
 			},
-
+			translate(t) {
+				if (typeof this.$t == "function") return this.$t(`page.${t}`)
+				else return t;
+			},
 
 		}
 	}
diff --git a/pages/map/teaching.vue b/pages/map/teaching.vue
index 7f11824..ff3e789 100644
--- a/pages/map/teaching.vue
+++ b/pages/map/teaching.vue
@@ -127,9 +127,9 @@
 						@click="clickClearStationName"></uni-icons>
 				</view>
 				<view class="text-button-group">
-					<a-button class="button" @click="clickStationNameCancel">鍙栨秷</a-button>
+					<a-button class="button" @click="clickStationNameCancel">{{translate('cancel')}}</a-button>
 					<a-button type="primary" class="button" :disabled="stationEdit.name.trim() == ''"
-						@click="clickStationNameOK">纭畾</a-button>
+						@click="clickStationNameOK">{{translate('ok')}}</a-button>
 				</view>
 			</view>
 			<view class="bottom-content" v-else-if="mapOperationType =='add_station_pos'">
@@ -214,11 +214,11 @@
 						<view class="img-button-group">
 							<view fill="none" class="button" @click.stop="clickTeachingEdit">
 								<text class="ico edit-line"></text>
-								<view class="text">缂栬緫</view>
+								<view class="text">{{translate('edit')}}</view>
 							</view>
 							<view fill="none" class="button" @click.stop="clickTeachingDelete">
 								<text class="ico red delete-outline "></text>
-								<view class="text">鍒犻櫎</view>
+								<view class="text">{{translate('delete')}}</view>
 							</view>
 
 						</view>
@@ -480,7 +480,7 @@
 						bgProgressPercent: 0,
 						bgLoading: false
 					})
-					showError(ex)
+					showError(ex,this.translate('error'))
 				}
 			},
 			async loadAgvState() {
@@ -488,7 +488,7 @@
 					const info = await getAgvState(this.vehicleIp)
 					return info
 				} catch (ex) {
-					showError(ex)
+					showError(ex,this.translate('error'))
 					return {}
 				}
 			},
@@ -497,7 +497,7 @@
 					const info = await stations(this.vehicleIp)
 					return info.station_list || []
 				} catch (ex) {
-					showError(ex)
+					showError(ex,this.translate('error'))
 					return []
 				}
 			},
@@ -506,7 +506,7 @@
 					const info = await getMapUrl(this.vehicleIp, id)
 					return info
 				} catch (ex) {
-					showError(ex)
+					showError(ex,this.translate('error'))
 					return {}
 				}
 			},
@@ -521,7 +521,7 @@
 					}
 
 				} catch (ex) {
-					showError(ex)
+					showError(ex,this.translate('error'))
 					return {
 						Public: [],
 						Stations: []
@@ -542,7 +542,7 @@
 					])
 
 				} catch (ex) {
-					showError(ex)
+					showError(ex,this.translate('error'))
 
 				}
 			},
@@ -703,7 +703,7 @@
 						param: item,
 					}])
 				} catch (ex) {
-					showError(ex)
+					showError(ex,this.translate('error'))
 				}
 			},
 			async removeTeachingModeData(data) {
@@ -711,7 +711,7 @@
 					await delTeachingModeData(this.vehicleIp, data)
 					this.reloadTeachingMode()
 				} catch (ex) {
-					showError(ex)
+					showError(ex,this.translate('error'))
 				}
 			},
 			clickPublicTeaching() {
@@ -753,7 +753,7 @@
 					}])
 					this.mapOperationType = 'public_teaching'
 				} catch (ex) {
-					showError(ex)
+					showError(ex,this.translate('error'))
 				}
 			},
 			async stationAdd(item) {
@@ -770,7 +770,7 @@
 
 
 				} catch (ex) {
-					showError(ex)
+					showError(ex,this.translate('error'))
 				}
 			},
 			async teachingStart(mode) {
@@ -814,7 +814,7 @@
 					}
 
 				} catch (ex) {
-					showError(ex)
+					showError(ex,this.translate('error'))
 				}
 
 			},
@@ -832,7 +832,7 @@
 						this.askTeachingBiDirection(this.teachingModeCur)
 					}
 				} catch (ex) {
-					showError(ex)
+					showError(ex,this.translate('error'))
 				}
 
 			},
@@ -858,7 +858,7 @@
 					}
 
 				} catch (ex) {
-					showError(ex)
+					showError(ex,this.translate('error'))
 				}
 			},
 
@@ -872,7 +872,7 @@
 					}])
 
 				} catch (ex) {
-					showError(ex)
+					showError(ex,this.translate('error'))
 				}
 			},
 
@@ -902,7 +902,7 @@
 										stationTeaching.splice(curIndex, 1)
 										_this.teachingStart("Stations")
 									} catch (ex) {
-										showError(ex)
+										showError(ex,this.translate('error'))
 									}
 
 								} else {
@@ -927,7 +927,7 @@
 					}
 
 				} catch (ex) {
-					showError(ex)
+					showError(ex,this.translate('error'))
 				}
 			},
 			clickTeachingEnd() {
@@ -950,7 +950,7 @@
 							_this.reloadTeachingMode()
 
 						} catch (ex) {
-							showError(ex)
+							showError(ex,this.translate('error'))
 						}
 					} else {
 						_this.teachingStatus = "save"
@@ -962,7 +962,7 @@
 
 				this.teachingStatus = "save"
 				try {} catch (ex) {
-					showError(ex)
+					showError(ex,this.translate('error'))
 				}
 
 			},
@@ -1147,7 +1147,7 @@
 
 					}
 				} catch (ex) {
-					showError(ex)
+					showError(ex,this.translate('error'))
 				}
 			},
 
@@ -1174,7 +1174,7 @@
 
 					}
 				} catch (ex) {
-					showError(ex)
+					showError(ex,this.translate('error'))
 				}
 			},
 			askTeachingBiDirection(teachingMode) {
@@ -1195,7 +1195,7 @@
 					this.reloadTeachingMode()
 					this.teachingStatus = "end"
 				} catch (ex) {
-					showError(ex)
+					showError(ex,this.translate('error'))
 				}
 			},
 			clickBackTeaching() {
@@ -1245,7 +1245,7 @@
 					}
 
 				} catch (ex) {
-					showError(ex)
+					showError(ex,this.translate('error'))
 				}
 
 			},
@@ -1408,7 +1408,7 @@
 						}
 					}])
 				} catch (ex) {
-					showError(ex)
+					showError(ex,this.translate('error'))
 				}
 			},
 			async clickShowTeachingPath() {
@@ -1441,7 +1441,7 @@
 					])
 					console.log(this.ctxDataStr)
 				} catch (ex) {
-					showError(ex)
+					showError(ex,this.translate('error'))
 				}
 			},
 			async clickPositionStation() {
@@ -1471,10 +1471,13 @@
 
 					])
 				} catch (ex) {
-					showError(ex)
+					showError(ex,this.translate('error'))
 				}
 			},
-
+			translate(t) {
+				if (typeof this.$t == "function") return this.$t(`page.${t}`)
+				else return t;
+			},
 
 		}
 	}
diff --git a/pages/my/email.vue b/pages/my/email.vue
new file mode 100644
index 0000000..ddb6122
--- /dev/null
+++ b/pages/my/email.vue
@@ -0,0 +1,321 @@
+<template>
+	<view class="pages-my-log-email">
+		<view class="content">
+			<view class="group">
+				<view class="item">
+					<view>{{translate("email_service")}}</view>
+					<input class="right-input" :placeholder="translate('input_email_service')" v-model="form.host" />
+				</view>
+				<view class="item">
+					<view>{{translate("email_port")}}</view>
+					<input class="right-input" :placeholder="translate('input_email_port')" v-model="form.port" />
+				</view>
+				<view class="item">
+					<view>{{translate("sender_email")}}</view>
+					<input class="right-input" :placeholder="translate('input_sender_email')"
+						v-model="form.yourEmail" />
+				</view>
+				<view class="item">
+					<view>{{translate("authorization_code")}}</view>
+					<input class="right-input" password :placeholder="translate('input_authorization_code')"
+						v-model="form.yourPwd" />
+				</view>
+
+				<view class="item">
+					<view>{{translate("receiver_email")}}</view>
+					<input class="right-input" :placeholder="translate('input_receiver_email')"
+						v-model="form.toEmail" />
+				</view>
+				<view class="item">
+					<view>{{translate("email_subject")}}</view>
+					<input class="right-input" :placeholder="translate('input_email_subject')" v-model="form.subject" />
+				</view>
+				<view class="item">
+					<textarea class="oi-content" auto-height fixed name="msgBody" comfirm-type="done"
+						show-confirm-bar="{{false}}" type="text" v-model="form.msgBody"
+						:placeholder="translate('input_email_content')+'...'" :maxlength="-1" />
+				</view>
+
+			</view>
+
+			<view class="title">
+				{{translate("attachment")}}
+			</view>
+
+
+			<view class="group">
+				<view class="item" v-for="(item,index) in form.attachment" :key="item">
+					{{index+ 1}}銆亄{item}}
+				</view>
+			</view>
+		</view>
+
+		<view class="button-line"> <a-button type="primary" class="button" @click="sendEmail">
+				{{translate('send')}}
+			</a-button></view>
+	</view>
+</template>
+
+<script>
+	// 鑾峰彇 module
+	const emailModule = uni.requireNativePlugin('EmailPlugin');
+	import {
+		session,
+		showToast,
+		showModal,
+		showError
+	} from "@/comm/utils.js"
+	import {
+		Button
+	} from 'antd-mobile-vue-next'
+	export default {
+		name: "PagesMyLogEmail",
+		components: {
+			'a-button': Button
+		},
+		data() {
+			return {
+				form: {
+					host: "",
+					port: "",
+					yourEmail: "",
+					yourPwd: "",
+					toEmail: "",
+					subject: '',
+					msgBody: '',
+					attachment: []
+
+				},
+				configureSMTPFlag: false
+			}
+		},
+		onLoad(option) {
+			const attachments = JSON.parse(option.attachment || "") || []
+			const info = session.getValue("log_email") || {}
+
+			this.form = {
+				host: info.host || "smtp.163.com",
+				port: info.port || "465",
+				yourEmail: info.yourEmail ||"cuiqian2004@163.com",
+				yourPwd: info.yourPwd || "QWfCFwa54a6L66Xr",
+				toEmail: info.toEmail || "nancy0606@qq.com",
+				subject: "ES-GO:" + this.translate("error_log_package"),
+				msgBody: '',
+				attachment: attachments
+			}
+		},
+		methods: {
+			configureSMTP() {
+				// 閰嶇疆SMTP鏈嶅姟鍣�+				return new Promise((resolve, reject) => {
+					if (!this.form.host.trim()) {
+
+						return reject(this.translate("input_email_service"))
+					}
+					if (!this.form.port.trim()) {
+						return reject(this.translate("input_email_port"))
+					}
+					if (!this.form.yourEmail.trim()) {
+						return reject(this.translate("input_sender_email"))
+					}
+					if (!this.form.yourPwd.trim()) {
+						return reject(this.translate("input_authorization_code"))
+					}
+					emailModule.configureSMTP({
+						host: this.form.host,
+						port: this.form.port,
+						username: this.form.yourEmail,
+						password: this.form.yourPwd,
+						enableSSL: true,
+						fromAlias: 'mobox'
+					}, (result) => {
+
+						if (result.success) {
+							resolve()
+
+						} else {
+							reject(result.error)
+						}
+					});
+				})
+			},
+			async sendEmail() {
+				try {
+					// 闈欓粯鍙戦�閭欢
+					const _this = this
+					uni.showLoading({
+						title: this.translate("email_sending")
+					})
+					console.log('闈欓粯鍙戦�閭欢');
+					if (!this.configureSMTPFlag) {
+						await this.configureSMTP()
+						this.configureSMTPFlag = true
+
+						const info = {
+							host: this.form.host,
+							port: this.form.port,
+							yourEmail: this.form.yourEmail,
+							yourPwd: this.form.yourPwd,
+							toEmail: this.form.toEmail,
+						}
+						session.setValue("log_email", info)
+					}
+
+					const info = session.getValue("log_email") || {}
+					if (!this.form.toEmail.trim()) {
+						uni.hideLoading()
+						uni.showToast({
+							title: this.translate("input_receiver_email"),
+							icon: 'warning'
+						});
+						return
+					}
+					var subject = this.form.subject
+					if (!subject.trim()) {
+						subject = "ES-GO Error"
+					}
+					const attachs = this.form.attachment.map((ele) => ele)
+					// this.form.attachment.forEach((ele) => {
+					// 	attachs.push(ele)
+					// })
+
+					// [
+					// 	"/storage/emulated/0/Android/data/uni.EasyGo/apps/__UNI__C988375/doc/download/test.zip"
+					// ]
+					console.log(attachs)
+					emailModule.sendSilentEmail({
+						to: this.form.toEmail,
+						subject: subject,
+						body: this.form.msgBody,
+						attachments: attachs
+					}, (result) => {
+						console.log('鍙戦�缁撴潫', result);
+						_this.loading = true
+						if (result.success) {
+							session.setValue("log_email", info)
+							uni.hideLoading()
+							showModal({
+								title: "",
+								content: this.translate("email_send_success"),
+								confirmText: this.translate("ok")
+							}).then(() => {
+								uni.navigateBack({
+									delta: 1, //杩斿洖灞傛暟锛�鍒欎笂涓婇〉
+								})
+							})
+
+						} else {
+							showError(`${this.translate("email_send_fail")},${result.error}}`)
+						}
+					});
+				} catch (ex) {
+					showError(ex)
+					uni.hideLoading()
+					this.loading = true
+				}
+			},
+			translate(t) {
+				if (typeof this.$t == "function") return this.$t(`page.${t}`)
+				else return t;
+			},
+		}
+	}
+</script>
+<style lang="less">
+	.pages-my-log-email {
+		display: flex;
+		width: 750rpx;
+		height: 100vh;
+		flex-direction: column;
+		background-color: #F8F8F8;
+
+		.content {
+			width: 750rpx;
+			display: flex;
+			flex-direction: column;
+			flex: 1;
+			overflow-y: auto;
+
+			.title {
+				margin: 10rpx 30rpx;
+			}
+
+			.group {
+				width: calc(100% - 60rpx);
+				// border: 1px solid #ccc;
+				border-radius: 20rpx;
+				margin: 10rpx 20rpx;
+				padding: 0 10rpx;
+				display: flex;
+				flex-direction: column;
+				background-color: #fff;
+				font-size: 30rpx;
+
+				.item {
+					width: calc(100% - 20rpx);
+					padding: 20rpx 10rpx;
+					display: flex;
+					flex-direction: row;
+					align-items: center;
+					word-wrap: break-word;
+					word-break: break-all;
+					border-bottom: 1px solid #ddd;
+
+					.right {
+						flex: 1;
+						text-align: right;
+						color: #888;
+						padding-right: 5px;
+
+					}
+
+					.right-input {
+						text-align: right;
+						flex: 1;
+						color: #1890FF;
+					}
+
+					.text {
+						flex: 1;
+						color: #888;
+
+					}
+
+					.icon {
+						color: #888;
+					}
+
+					a {
+						margin-left: 20rpx;
+					}
+
+					.input {
+						margin-left: 20rpx;
+						margin-right: 10rpx;
+						width: 75rpx;
+					}
+
+				}
+				.item:last-child {
+					border-bottom: 0;
+
+				}
+
+			}
+
+		}
+
+		.button-line {
+			margin: 20rpx;
+
+			.button {
+				margin: auto;
+				width: 500rpx !important;
+				border-radius: 40rpx;
+				height: 80rpx;
+				line-height: 50rpx;
+			}
+		}
+
+	}
+</style>
\ No newline at end of file
diff --git a/pages/my/help-feedback.vue b/pages/my/help-feedback.vue
index cae0300..fce927f 100644
--- a/pages/my/help-feedback.vue
+++ b/pages/my/help-feedback.vue
@@ -2,30 +2,31 @@
 	<view class="pages-my">
 		<view class="group">
 			<view class="item line">
-				<view>杩滅▼鍗忓姪锛�/view>
+				<view>{{translate('remote_assistance')}}</view>
 				<view class="right"></view>
 				<a @click="clickRemoteAssistance">
 					<uni-icons class="icon" type="right" size="24" color="#888"></uni-icons>
 				</a>
 			</view>
 			<view class="item line">
-				<view>鏃ュ織涓婁紶锛�/view>
+				<view>{{translate('log_upload')}}</view>
 				<view class="right"></view>
 				<a @click="clickUploadLog">
 					<uni-icons class="icon" type="right" size="24" color="#888"></uni-icons>
 				</a>
 			</view>
-			<view class="item line">
-				<view>鏌ョ湅璇存槑涔︼細</view>
+			<!-- <view class="item line">
+				<view>{{translate('check_the_manual')}}</view>
 				<view class="right"></view>
 				<a @click="clickViewInstruction">
 					<uni-icons class="icon" type="right" size="24" color="#888"></uni-icons>
 				</a>
-			</view>
+			</view> -->
 			<view class="item line">
-				<view>鎺ュ彛鏃ュ織锛�/view>
+				<view>{{translate('interface_log')}}</view>
 
-				<switch :checked="withLog" style="transform:scale(0.7)" @change="switchChangeLog"/>{{withLog?"璁板綍鎺ュ彛鏃ュ織":"涓嶈褰曟帴鍙f棩蹇�}}
+				<switch :checked="withLog" style="transform:scale(0.7)" @change="switchChangeLog" />
+				{{translate('record_interface_log')}}
 				<view class="right"></view>
 				<a @click="clickApiLog">
 					<uni-icons class="icon" type="right" size="24" color="#888"></uni-icons>
@@ -54,7 +55,9 @@
 			}
 		},
 		onLoad() {
-
+			uni.setNavigationBarTitle({
+				title:this.translate('help_and_feedback')
+			})
 		},
 		computed: {
 
@@ -62,15 +65,17 @@
 		methods: {
 			switchChangeLog(e) {
 				this.withLog = e.detail.value
-				getApp().globalData.withLog =this.withLog
-				session.setValue("write_log",this.withLog ? 1:0) 
+				getApp().globalData.withLog = this.withLog
+				session.setValue("write_log", this.withLog ? 1 : 0)
 			},
 
 			clickRemoteAssistance() {
-				showToast("鏆傛湭瀹炵幇")
+				showToast(this.translate('unrealized'))
 			},
 			clickUploadLog() {
-				showToast("鏆傛湭瀹炵幇")
+				uni.navigateTo({
+					url: "/pages/my/log_upload"
+				})
 			},
 			clickViewInstruction() {
 				uni.navigateTo({
@@ -82,10 +87,10 @@
 					url: "/pages/my/log"
 				})
 			},
-
-
-
-
+			translate(t) {
+				if (typeof this.$t == "function") return this.$t(`page.${t}`)
+				else return t;
+			},
 		}
 	}
 </script>
diff --git a/pages/my/index.vue b/pages/my/index.vue
index d3cf4c8..a5825bb 100644
--- a/pages/my/index.vue
+++ b/pages/my/index.vue
@@ -1,17 +1,17 @@
 <template>
 	<view class="pages-my">
 		<view class="group">
-			<view class="item line"  @click="clickAppVersion">
-				<view>App鐗堟湰锛�/view>
+			<view class="item line" @click="clickAppVersion">
+				<view>{{translate('app_version')}}</view>
 				<view class="right">{{app_version}}</view>
 				<a>
 					<uni-icons class="icon" type="right" size="24" color="#888"></uni-icons>
 				</a>
 			</view>
 			<view class="item line" @click="clickLanguage">
-				<view>璇█鍒囨崲锛�/view>
+				<view>{{translate('language_switch')}}</view>
 				<view class="right">{{languageStr}}</view>
-				<a >
+				<a>
 					<uni-icons class="icon" type="right" size="24" color="#888"></uni-icons>
 				</a>
 			</view>
@@ -32,14 +32,14 @@
 			</view>
 		 -->
 			<view class="item line" @click="clickClearCache">
-				<view>娓呴櫎缂撳瓨锛�/view>
+				<view>{{translate('clear_cache')}}</view>
 				<view class="right">{{cacheSizeStr}}</view>
-				<a >
+				<a>
 					<uni-icons class="icon" type="right" size="24" color="#888"></uni-icons>
 				</a>
 			</view>
-			<view class="item line"  @click="clickHelp">
-				<view>甯姪涓庡弽棣堬細</view>
+			<view class="item line" @click="clickHelp">
+				<view>{{translate('help_and_feedback')}}</view>
 				<view class="right"></view>
 				<a>
 					<uni-icons class="icon" type="right" size="24" color="#888"></uni-icons>
@@ -57,7 +57,7 @@
 		<view class="button-group">
 			<!-- 	<button class="button" @click="clickModifyPassword">淇敼瀵嗙爜</button>
 			<button class="button" @click="clickChangeAccount">鍒囨崲璐﹀彿</button> -->
-			<a-button class="button" type="ghost" @click="clickExitAccount">閫�嚭鐧诲綍</a-button>
+			<a-button class="button" type="ghost" @click="clickExitAccount">{{translate('exit_login')}}</a-button>
 		</view>
 		<view>
 			<uni-popup ref="refPopupMenu" background-color="transparent" maskBackgroundColor="rgba(0, 0, 0, 0.2)">
@@ -100,7 +100,7 @@
 					name: "涓枃绠�綋"
 				}, {
 					value: "zh-Hant",
-					name: "涓枃绻佷綋"
+					name: " 涓枃绻侀珨"
 				}, {
 					value: "en",
 					name: "English"
@@ -109,7 +109,9 @@
 		},
 		onLoad() {
 			// this.loadData()
-
+			uni.setNavigationBarTitle({
+				title:this.translate('my')
+			})	
 		},
 		computed: {
 			cacheSizeStr() {
@@ -182,14 +184,9 @@
 						_this.setData({
 							cache_size: res.currentSize
 						})
-						console.log('褰撳墠缂撳瓨澶у皬锛�, res, 'KB');
 					},
-					fail: (err) => {
-						console.error('鑾峰彇缂撳瓨淇℃伅澶辫触锛�, err);
-					},
-					complete: () => {
-						console.log('缂撳瓨淇℃伅鑾峰彇瀹屾垚');
-					},
+					fail: (err) => {},
+					complete: () => {},
 				});
 			},
 			bindLanguageChange(e) {
@@ -203,7 +200,7 @@
 			},
 			clickAppVersion() {
 				uni.navigateTo({
-					url: "/pages/my/version-update"
+					url: "/pages/version/app-update"
 				})
 			},
 			clickPrivacyPolicy() {
@@ -214,7 +211,10 @@
 			},
 			clickLanguage() {
 
-				this.$refs.refPopupMenu.open("right")
+				uni.navigateTo({
+					url: "/pages/my/language"
+				})
+				//this.$refs.refPopupMenu.open("right")
 			},
 			clickClearCache() {
 				try {
@@ -223,7 +223,7 @@
 					uni.clearStorageSync();
 					session.setValue("vehicles", list)
 					session.setValue("login_info", info)
-					showToast("娓呴櫎缂撳瓨鎴愬姛锛�)
+					showToast(this.translate('clear_cache_success'))
 					this.loadCache()
 				} catch (e) {
 					// error
@@ -237,7 +237,7 @@
 			clickUserAgreement() {
 
 			},
-	
+
 			clickModifyPassword() {
 
 			},
@@ -246,10 +246,13 @@
 			},
 			clickExitAccount() {
 				uni.reLaunch({
-					url: "/pages/login/index"
+					url: "/pages/login/index?flag=1"
 				})
 			},
-
+			translate(t) {
+				if (typeof this.$t == "function") return this.$t(`page.${t}`)
+				else return t;
+			},
 		}
 	}
 </script>
diff --git a/pages/my/instruction.vue b/pages/my/instruction.vue
index a5be107..48833bd 100644
--- a/pages/my/instruction.vue
+++ b/pages/my/instruction.vue
@@ -1,7 +1,7 @@
 <template>
 	<view class="pages-my-instruction">
 		<view class="content">
-			<view class="title">瑙嗛璇存槑</view>
+			<view class="title">{{translate('video_instruction')}}</view>
 			<view class="vidio">
 				<video></video>
 				
@@ -60,7 +60,10 @@
 			clickViewInstruction() {
 
 			},
-
+			translate(t) {
+				if (typeof this.$t == "function") return this.$t(`page.${t}`)
+				else return t;
+			},
 
 		}
 	}
diff --git a/pages/my/language.vue b/pages/my/language.vue
index d4dcf05..3a446c3 100644
--- a/pages/my/language.vue
+++ b/pages/my/language.vue
@@ -15,7 +15,7 @@
 					name: "涓枃绠�綋"
 				}, {
 					value: "zh-Hant",
-					name: "涓枃绻佷綋"
+					name: " 涓枃绻侀珨"
 				}, {
 					value: "en",
 					name: "English"
@@ -32,8 +32,8 @@
 				// this.$i18n.locale =lang;
 
 			},
-			translateSys(t) {
-				if (typeof this.$t == "function") return this.$t(`sys.${t}`)
+			translate(t) {
+				if (typeof this.$t == "function") return this.$t(`page.${t}`)
 				else return t;
 			},
 		},
@@ -41,6 +41,9 @@
 
 			this.curLang = uni.getLocale()
 			this.oldLang = this.curLang
+			uni.setNavigationBarTitle({
+				title:this.translate('language')
+			})
 		},
 		onUnload() {
 			if (this.oldLang != this.curLang) {
diff --git a/pages/my/log-detail.vue b/pages/my/log-detail.vue
index 3b5d184..4a43ab1 100644
--- a/pages/my/log-detail.vue
+++ b/pages/my/log-detail.vue
@@ -1,26 +1,26 @@
 <template>
 	<view class="pages-my-log-detail">
-		<view class="title"> {{'鎺ュ彛 '+ info.date}}
+		<view class="title"> {{ info.date+" " +info.method }}
 		</view>
-		<view class="content">{{info.method + " " + info.url}}
+		<view class="content">{{ info.url}}
 		</view>
 		<template v-if="info.method == 'POST'">
-			<view class="title">鍙傛暟
+			<view class="title">{{translate('parameter')}}
 			</view>
 			<textarea class="param" disabled :value="paramText"></textarea>
 		</template>
-		<template v-if="info.statusCode!=200">
-			<view class="title">閿欒鐮�+		<template v-if="isError">
+			<view class="title">{{translate('error_code')}}
 			</view>
 			<view class="content error">{{info.statusCode}}
 			</view>
-			<view class="title">閿欒淇℃伅
+			<view class="title">{{translate('error_msg')}}
 			</view>
 			<textarea class="content error" disabled :value="result"></textarea>
-			
+
 		</template>
 		<template v-else>
-			<view class="title">缁撴灉
+			<view class="title">{{translate('result')}}
 			</view>
 			<textarea class="result" disabled :value="result"></textarea>
 			<!-- <view class="result">{{result}}
@@ -32,6 +32,9 @@
 	import {
 		session,
 	} from "@/comm/utils.js"
+	import {
+		Base64
+	} from '@/comm/Base64.js';
 	export default {
 		name: "PagesMyLogDetail",
 		data() {
@@ -41,18 +44,25 @@
 			}
 		},
 		onLoad(option) {
-			this.info = JSON.parse(option.info) || {}
-			console.log(	this.info)
-			if(this.info.data_key)
-			{
+			this.info = getApp().globalData.pageBigData || {}
+			getApp().globalData.pageBigData = {}
+			if (this.info.data_key) {
 				const maxData = session.getValue("request_log_max_data") || {}
 				this.info.data = maxData[this.info.data_key]
 			}
+			const list = (this.info.url || "").split("/")
+			let title = ""
+			for (let i in list) {
+				if (list[i] == "api") {
+					title = "api"
+				} else
+					title += "/" + list[i]
+			}
 			uni.setNavigationBarTitle({
-				title: this.info.method || "鎺ュ彛鏃ュ織璇︽儏"
+				title: title || "鎺ュ彛鏃ュ織璇︽儏"
 			})
-			
-			
+
+
 		},
 		computed: {
 			paramText() {
@@ -62,10 +72,31 @@
 			result() {
 				const res = this.info.data
 				return typeof res == 'string' ? res : JSON.stringify(res)
+			},
+			isError() {
+				if (this.info.statusCode == 200) {
+					const res = typeof this.info.data == 'string' ? this.parseJson(this.info.data) : this.info.data
+					if (res?.code)
+						return true
+					return false
+				}
+				return true
 			}
 		},
 		methods: {
-
+			parseJson(str) {
+				try {
+					const obj = JSON.parse(str)
+					return obj
+				} catch (ex) {
+					console.log(ex)
+					return str
+				}
+			},
+			translate(t) {
+				if (typeof this.$t == "function") return this.$t(`page.${t}`)
+				else return t;
+			},
 		}
 	}
 </script>
@@ -79,7 +110,7 @@
 		background-color: #F5F5F5;
 
 		.title {
-			margin: 5rpx;
+			margin: 10rpx;
 			font-size: 32rpx;
 			font-weight: 700;
 		}
diff --git a/pages/my/log.vue b/pages/my/log.vue
index 3a85e49..0cbaa52 100644
--- a/pages/my/log.vue
+++ b/pages/my/log.vue
@@ -2,13 +2,13 @@
 	<view class="pages-my-log">
 		<view class="top">
 			<view class="input">
-				<input placeholder="璇疯緭鍏ユ煡鎵炬帴鍙RL" v-model="keyMethod"/>
+				<input :placeholder="translate('input_interface_name')" v-model="keyMethod" />
 				<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>
+				<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>
@@ -30,6 +30,9 @@
 </template>
 <script>
 	import {
+		Base64
+	} from '@/comm/Base64.js';
+	import {
 		session,
 		showToast,
 		showModal
@@ -49,12 +52,19 @@
 			}
 		},
 		onLoad() {
-			this.list = session.getValue("request_log") || []
+			const list = session.getValue("request_log") || []
+			this.list = list.map((item) => {
+				item.url = decodeURI(item.url)
+				return item
+			})
+
 		},
 		methods: {
 			clickDetail(item) {
+				
+				getApp().globalData.pageBigData = item
 				uni.navigateTo({
-					url: `/pages/my/log-detail?info=${JSON.stringify(item)}`
+					url: `/pages/my/log-detail`
 				})
 			},
 			clickClearLog() {
@@ -68,14 +78,17 @@
 				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 = ""
 			},
-
+			translate(t) {
+				if (typeof this.$t == "function") return this.$t(`page.${t}`)
+				else return t;
+			},
 
 		}
 	}
@@ -143,6 +156,7 @@
 				display: flex;
 				flex-direction: row;
 				background-color: #fff;
+
 				input {
 					flex: 1;
 				}
diff --git a/pages/my/log_upload.vue b/pages/my/log_upload.vue
new file mode 100644
index 0000000..05b96e1
--- /dev/null
+++ b/pages/my/log_upload.vue
@@ -0,0 +1,296 @@
+<template>
+	<view class="pages-my-log-upload">
+		<view class="list">
+			<checkbox-group @change="checkboxChange">
+				<label class="group" v-for="item in fileList" :key="item.name">
+					<view>
+						<checkbox :value="item.name" :checked="item.checked" />
+					</view>
+					<view class="name">{{item.name}}</view>
+					<template v-if="item.result"> <uni-icons v-if="item.result==1" type="checkmarkempty" size="20"
+							color="#55aa00"></uni-icons>
+						<uni-icons v-if="item.result ==2" type="info" size="20" color="#ff0000"></uni-icons>
+					</template>
+
+
+					<progress v-if="uploadingItem.fileName== item.name" :percent="uploadingItem.progress"
+						activeColor="#10AEFF" stroke-width="3" />
+				</label>
+
+			</checkbox-group>
+		</view>
+		<view class="button-line">
+
+			<checkbox :checked="checkeAll" @click="clickCheckeAll">{{translate("select_all")}}</checkbox>
+			<!-- 	<a-button
+				type="primary" class="button" @click="clickLogSendEmail">
+				{{translate('email_send')}}
+			</a-button> -->
+			<a-button type="primary" class="button" :loading="uploadingFlag" @click="clickLogSend">
+				{{translate('upload')}}
+			</a-button>
+			<a class="btn" @click="clickLogClear">
+				<uni-icons type="trash" size="28" :color="uploadingFlag?'gray':'#1890FF'"></uni-icons>
+			</a>
+		</view>
+	</view>
+</template>
+<script>
+	import {
+		showToast,
+		showModal,
+		showError,
+		showInfo
+	} from "@/comm/utils.js"
+	import TaskInit from "@/comm/extend.js"
+
+	import {
+		Button
+	} from 'antd-mobile-vue-next'
+	import {
+		uploadLogPackage
+	} from "@/api/version.js"
+	export default {
+		name: "PagesMyLogUpload",
+		components: {
+			'a-button': Button
+		},
+		data() {
+			return {
+				fileList: [],
+				host: "smtp.163.com",
+				port: "465",
+				yourEmail: "cuiqian2004@163.com",
+				yourPwd: "QWfCFwa54a6L66Xr",
+				toEmail: "nancy0606@qq.com",
+				checkeAll: false,
+				uploadList: [],
+				uploadingFlag: false,
+				uploadingItem: {
+					progress: 0,
+					fileName: ""
+				}
+			}
+		},
+		onLoad() {
+
+			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.fileList = await TaskInit.fileUtils.listSavedFiles('download')
+					this.fileList.forEach((a) => {
+						a.checked = false
+					})
+				} catch (ex) {
+					console.log(ex)
+				}
+			},
+			clickCheckeAll() {
+				this.checkeAll = !this.checkeAll
+				var items = this.fileList
+				for (var i = 0, lenI = items.length; i < lenI; ++i) {
+					const item = items[i]
+					this.$set(item, 'checked', this.checkeAll)
+				}
+			},
+			checkboxChange(e) {
+				var items = this.fileList,
+					values = e.detail.value;
+				for (var i = 0, lenI = items.length; i < lenI; ++i) {
+					const item = items[i]
+					console.log(item)
+					if (values.includes(item.name)) {
+						this.$set(item, 'checked', true)
+					} else {
+						this.$set(item, 'checked', false)
+					}
+				}
+			},
+			async clickLogClear() {
+
+				try {
+					var flag = false
+					for (let i in this.fileList) {
+						const item = this.fileList[i]
+						if (item.checked) {
+							await TaskInit.fileUtils.deleteSavedFile("download", item.name)
+							flag = true
+						}
+					}
+					if (flag) {
+						try {
+							this.fileList = await TaskInit.fileUtils.listSavedFiles()
+						} catch (ex) {
+							console.log(ex)
+						}
+						showToast(this.translate("delete_success"))
+					}
+				} catch (ex) {
+					showError(ex)
+				}
+
+			},
+			clickLogSendEmail() {
+
+				const list = []
+				for (let i in this.fileList) {
+					const item = this.fileList[i]
+					if (item.checked) {
+						list.push(item.fullPath)
+					}
+				}
+				if (list.length > 0) {
+					uni.navigateTo({
+						url: "/pages/my/email?attachment=" + JSON.stringify(list)
+					})
+				}
+
+			},
+			clickLogSend() {
+
+				const list = []
+				this.uploadList = []
+				for (let i in this.fileList) {
+					const item = this.fileList[i]
+					if (item.checked) {
+						list.push(item)
+					}
+					item.result = 0
+				}
+
+				if (list.length > 0) {
+					this.uploadList = list
+					this.uploadLogs()
+				}
+
+			},
+			uploadLogs() {
+				const _this = this
+				if (this.uploadList.length > 0) {
+					console.log("uploadList", this.uploadList.length)
+					const item = this.uploadList.shift()
+					this.setData({
+						uploadingFlag: true,
+						uploadingItem: {
+							progress: 0,
+							fileName: item.name
+						}
+					})
+					uploadLogPackage(item.fullPath, () => {
+						const curItem = this.fileList.find((a) => a.name == item.name)
+						if (curItem) {
+							curItem.result = 1;
+						}
+						_this.uploadLogs()
+					}, (res) => {
+						_this.uploadingItem.progress = res.progress
+					}, (err) => {
+						showToast(`${_this.translate("upload_fail")}:${item.name},${err}`)
+						const curItem = this.fileList.find((a) => a.name == item.name)
+						if (curItem) {
+							curItem.result = 2;
+						}
+						_this.uploadLogs()
+					})
+				} else {
+					this.setData({
+						uploadingFlag: false,
+						uploadingItem: {
+							progress: 0,
+							fileName: ""
+						}
+					})
+					showToast(_this.translate("upload_finish"))
+				}
+
+			},
+			translate(t) {
+				if (typeof this.$t == "function") return this.$t(`page.${t}`)
+				else return t;
+			},
+
+		}
+	}
+</script>
+
+<style lang="less">
+	.pages-my-log-upload {
+		display: flex;
+		width: 750rpx;
+		height: 100vh;
+		flex-direction: column;
+		background-color: #F5F5F5;
+
+		.list {
+			flex: 1;
+			overflow-y: auto;
+			display: flex;
+			flex-direction: column;
+			width: 750rpx;
+		}
+
+		.group {
+			width: calc(100% - 20rpx);
+			padding: 15rpx 10rpx;
+			display: flex;
+			flex-direction: row;
+			margin: 10rpx 10rpx;
+			background-color: #fff;
+			border-radius: 10rpx;
+			font-size: 30rpx;
+			position: relative;
+
+			.progress {
+				position: absolute;
+				bottom: 0;
+				left: 0;
+				right: 0;
+			}
+
+			.name {
+				flex: 1;
+			}
+		}
+
+		.button-line {
+			margin: 20rpx;
+			display: flex;
+			flex-direction: row;
+			align-items: center;
+			/* 浜ゅ弶杞�= 鍨傜洿鏂瑰悜灞呬腑 */
+		}
+
+		.button {
+			width: 360rpx !important;
+			border-radius: 40rpx;
+			height: 80rpx;
+			line-height: 50rpx;
+			margin: 20rpx;
+		}
+
+	}
+</style>
\ No newline at end of file
diff --git a/pages/my/version-update.vue b/pages/my/version-update.vue
deleted file mode 100644
index c579404..0000000
--- a/pages/my/version-update.vue
+++ /dev/null
@@ -1,207 +0,0 @@
-<template>
-	<view class="pages-my-version-update">
-		<view class="group">
-			<view class="item">
-				<view>鑷姩鏇存柊</view>
-				<view class="right"></view>
-				<switch :checked="autoUpdate" @change="switchChangeAutoUpdate" />
-			</view>
-
-		</view>
-		<view class="tip">
-			寮�惎鍚庯紝璁惧灏嗗湪2:00-6:00鑷姩鏇存柊杞欢鐗堟湰
-		</view>
-		<view class="group" v-if="newPack.size > 0">
-			<view class="item big-text ">
-				鍙洿鏂扮増鏈瑊{newPack.version}}
-			</view>
-			<view class="item gray-text">
-				{{newVersionSize}}
-			</view>
-			<view class="item">
-				鏈夋洿鏂扮殑鐗堟湰{{newPack.version}}
-			</view>
-			<view class="item">
-				褰撳墠鐗堟湰涓簕{appVersion}}
-			</view>
-		</view>
-		<view class="no-version gray-text" v-else>
-			<uni-icons type="upload-filled" size="150" color="#888"></uni-icons>
-			褰撳墠涓烘渶鏂扮増鏈瑊{appVersion}}
-		</view>
-
-		<view class="button-group" v-if="newPack.size > 0">
-			<a-button type="primary" class="button" @click="clickNowUpdate">鐜板湪鏇存柊</a-button>
-		</view>
-	</view>
-</template>
-<script>
-	import {
-		session,
-		showToast,
-		showModal
-	} from "@/comm/utils.js"
-	import {
-		Button
-	} from 'antd-mobile-vue-next'
-	export default {
-		name: "PagesMyVersionUpdate",
-		components: {
-			'a-button': Button
-		},
-		data() {
-			return {
-				autoUpdate:false,
-				appVersion: "1.0.0",
-				newPack:{}
-				// newPack: {
-				// 	version: "1.0.0",
-				// 	size: 200 * 1024 * 1024,
-				// }
-			}
-		},
-		onLoad() {
-			this.loadData()
-
-		},
-		computed: {
-			newVersionSize() {
-				const packSize = this.newPack?.size || 0
-				let size = 0
-				if (packSize < 1024) {
-					size = Math.round(packSize * 100)
-					return `${size/100} KB`
-				} else if (packSize < 1024 * 1024) {
-					size = Math.round(packSize * 100 / 1024)
-					return `${size/100} MB`
-				} else {
-					size = Math.round(packSize * 100 / (1024 * 1024 * 1024))
-					return `${size/100} GB`
-				}
-			}
-
-		},
-		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];
-					});
-				});
-			},
-			loadData() {
-				
-					this.autoUpdate = session.getValue("auto_update") ? true :false
-				const _this = this
-				// #ifdef APP-PLUS
-				plus.runtime.getProperty(plus.runtime.appid, (info) => {
-					// console.log(info);
-					_this.setData({
-						appVersion: info.version
-					})
-				});
-				//#endif
-			},
-
-
-			switchChangeAutoUpdate(e) {
-				this.autoUpdate = e.detail.value
-				session.setValue("auto_update", this.autoUpdate ? 1 :0)
-			},
-
-			clickNowUpdate() {
-			
-			},
-
-		}
-	}
-</script>
-
-<style lang="scss">
-	.pages-my-version-update {
-		display: flex;
-		width: 750rpx;
-		height: 100vh;
-		flex-direction: column;
-
-		.group {
-			width: calc(100% - 40rpx);
-			border: 2rpx solid #ccc;
-			background-color: #fff;
-			border-radius: 20rpx;
-			margin: 20rpx;
-			padding: 0 10rpx;
-			display: flex;
-			flex-direction: column;
-
-			.item {
-				width: 100%;
-				padding: 20rpx 10rpx;
-				display: flex;
-				flex-direction: row;
-				align-items: center;
-
-				.right {
-					flex: 1;
-					text-align: right;
-					color: #888;
-				}
-			}
-
-
-		}
-		.tip{
-			color: #888;
-			padding: 10rpx;
-			margin-left: 50rpx;
-		}
-		.gray-text {
-			color: #888;
-		}
-
-		.big-text {
-			font-size: 40rpx;
-		}
-		.no-version{
-			margin: auto;
-			display: flex;
-			flex-direction: column;
-		}
-
-		.button-group {
-			display: flex;
-			justify-content: center;
-			align-items: center;
-			flex-direction: column;
-			font-size: 30rpx !important;
-
-			.am-button {
-				border-radius: 30px;
-			}
-
-		}
-
-		.button {
-			margin: auto;
-			margin-top: 20rpx;
-			border-radius: 4rpx;
-			width: 500rpx;
-		}
-
-
-	}
-</style>
\ No newline at end of file
diff --git a/pages/station/delete.vue b/pages/station/delete.vue
index c63dab1..bfb84c8 100644
--- a/pages/station/delete.vue
+++ b/pages/station/delete.vue
@@ -10,17 +10,17 @@
 
 						<view class="item-title">{{item.name}}</view>
 						<view class="item-text">
-							瑙掑害锛歿{Math.round(item.angle *180/3.14)}}锛屽潗鏍囷紙{{Math.round(Number(item.x)*100)/100}},{{Math.round(Number(item.y)*100)/100}})
+							{{translate("angle")}}锛歿{Math.round(item.angle *180/3.14)}}锛寋{translate("coordinates")}}:锛坽{Math.round(Number(item.x)*100)/100}},{{Math.round(Number(item.y)*100)/100}})
 						</view>
 					</view>
 				</label>
 			</checkbox-group>
 		</view>
 		<view class="bottom">
-			<checkbox :checked="checkedAll" @click="onSelectAll">鍏ㄩ�</checkbox>
+			<checkbox :checked="checkedAll" @click="onSelectAll">{{translate("select_all")}}</checkbox>
 			<view class="button-group">
 
-				<a-button type="primary" class="button" @click="clickDelelte">鍒犻櫎</a-button>
+				<a-button type="primary" class="button" @click="clickDelelte">{{translate("delete")}}</a-button>
 			</view>
 
 
@@ -65,7 +65,7 @@
 		onBackPress() {
 			const eventChannel = this.getOpenerEventChannel();
 			eventChannel.emit('delete_finish', this.listDel);
-			
+
 		},
 		methods: {
 			async loadData() {
@@ -75,7 +75,7 @@
 
 				} catch (ex) {
 
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 			},
 			async loadStations() {
@@ -83,7 +83,7 @@
 					const info = await stations(this.ip)
 					return info.station_list || []
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 					return []
 				}
 			},
@@ -122,10 +122,15 @@
 
 				}
 				if (list.length === 0) {
-					showInfo("鏈�鎷╃珯鐐癸紒")
+					showToast(this.translate("no_selected_stations"))
 					return
 				}
-				showModal("鍒犻櫎閫夋嫨鐨勭珯鐐�, "鏄惁纭鍒犻櫎锛�).then((res) => {
+				showModal({
+					title: this.translate("ask_confirm_remove"),
+					content: `${this.translate('delete_selected_station')}`,
+					confirmText: this.translate('remove'),
+					cancelText: this.translate('cancel'),
+				}).then((res) => {
 					if (res) {
 						_this.stationDelete(list)
 					}
@@ -136,22 +141,25 @@
 			async stationDelete(list) {
 				try {
 					uni.showLoading({
-						title: "姝e湪鍒犻櫎绔欑偣"
+						title: this.translate("deleting_station")
 					})
 
 					await delStation(this.ip, list)
 					this.stationList = await this.loadStations() || []
 
 					this.listDel.push(...list)
-					showToast("鍒犻櫎绔欑偣鎴愬姛")
+					showToast(this.translate("delete_station_success"))
 				} catch (ex) {
-					
-					showError(ex)
+
+					showError(ex, this.translate('error'))
 				} finally {
 					uni.hideLoading()
 				}
 			},
-
+			translate(t) {
+				if (typeof this.$t == "function") return this.$t(`page.${t}`)
+				else return t;
+			},
 		}
 	}
 </script>
diff --git a/pages/station/index.vue b/pages/station/index.vue
index c22a196..c967451 100644
--- a/pages/station/index.vue
+++ b/pages/station/index.vue
@@ -5,7 +5,7 @@
 				<view class="position-site">
 					<a-button type="ghost" class="button" @click="clickPositionStation">
 						<text class="ico my-location-rounded" />
-						<text class="text">鑾峰彇鎼繍杞︿綅缃拰鏈濆悜</text>
+						<text class="text">{{translate("obtain_positon_and_orientation_of_vehicle")}}</text>
 					</a-button>
 				</view>
 			</view>
@@ -21,17 +21,17 @@
 						@touchcancel="onTouchCancelStation">
 						<view class="item-title">{{item.name}}</view>
 						<view class="item-text">
-							瑙掑害锛歿{Math.round(item.angle *180/3.14)}}锛屽潗鏍囷紙{{Math.round(Number(item.x)*100)/100}},{{Math.round(Number(item.y)*100)/100}})
+							{{translate("angle")}}锛歿{Math.round(item.angle *180/3.14)}}锛寋{translate("coordinates")}}锛坽{Math.round(Number(item.x)*100)/100}},{{Math.round(Number(item.y)*100)/100}})
 						</view>
 						<template v-slot:right>
-							<view class="btn-del" @click="clickDelStation(item)">鍒犻櫎</view>
+							<view class="btn-del" @click="clickDelStation(item)">{{translate("delete")}}</view>
 						</template>
 					</uni-swipe-action-item>
 				</uni-swipe-action>
 			</view>
 			<view class="list-no-content" v-else>
 				<uni-icons color="#ccc" type="info" size="128"></uni-icons>
-				<view class="space">杩樻病鏈夌珯鐐�/view>
+				<view class="space">{{translate("no_station")}}</view>
 			</view>
 			<view class="position-add" v-if="opStationType ==''" @click="clickAddStation" @touchstart='btnAddTouchStart'
 				@touchmove='btnAddTouchMove'
@@ -45,13 +45,13 @@
 				<view class="tip">璇疯緭鍏ョ珯鐐瑰悕绉�/view>
 				<view class="name-input">
 					<input ref="refInputName" :focus="true" placeholder="杈撳叆绔欑偣鍚嶇О" :value="stationEdit.name"
-						@input="onInputName"/>
+						@input="onInputName" />
 					<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="ghost" class="button" @click="clickNameCancel">鍙栨秷</a-button>
-					<a-button type="primary" class="button" @click="clickNameOK">纭畾</a-button>
+					<a-button type="ghost" class="button" @click="clickNameCancel">{{translate('cancel')}}</a-button>
+					<a-button type="primary" class="button" @click="clickNameOK">{{translate('ok')}}</a-button>
 				</view>
 			</view>
 			<view class="bottom-content" v-else-if=" opStationType =='edit_pos'">
@@ -60,12 +60,12 @@
 					<view class="coordinate">
 						<text class="name">妯潗鏍�</text>
 						<input ref="refInputX" class="number-input" type="number" :value="stationEdit.x"
-							@input="onInputX"/>
+							@input="onInputX" />
 					</view>
 					<view class="coordinate">
 						<text class="name">绔栧潗鏍�</text>
 						<input ref="refInputX" class="number-input" type="number" :value="stationEdit.y"
-							@input="onInputY"/>
+							@input="onInputY" />
 					</view>
 				</view>
 
@@ -175,7 +175,7 @@
 			this.onlyAdd = option.isAdd ? true : false
 
 			uni.setNavigationBarTitle({
-				title: "绔欑偣鍒楄〃"
+				title: this.translate("station_list")
 			})
 			uni.getSystemInfo({
 				success(e) {
@@ -186,7 +186,7 @@
 			if (this.onlyAdd) {
 				this.opStationType = "edit_name"
 				uni.setNavigationBarTitle({
-					title: "娣诲姞绔欑偣"
+					title: this.translate("add_station")
 				})
 			}
 
@@ -205,7 +205,7 @@
 				if (this.opStationType == "edit_name" || this.opStationType == "edit_pos") {
 					this.opStationType = ""
 					uni.setNavigationBarTitle({
-						title: "绔欑偣鍒楄〃"
+						title: this.translate("station_list")
 					})
 					this.$refs.refPopupOperateStation.open("bottom")
 
@@ -260,7 +260,7 @@
 
 				} catch (ex) {
 
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 			},
 			async loadAgvState() {
@@ -268,7 +268,7 @@
 					const info = await getAgvState(this.ip)
 					return info
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 					return {}
 				}
 			},
@@ -277,7 +277,7 @@
 					const info = await stations(this.ip)
 					return info.station_list || []
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 					return []
 				}
 			},
@@ -293,7 +293,7 @@
 				}
 				this.opStationType = "edit_name"
 				uni.setNavigationBarTitle({
-					title: "娣诲姞绔欑偣"
+					title:  this.translate("add_station")
 				})
 
 			},
@@ -361,7 +361,12 @@
 				this.pressStationTimer = null;
 			},
 			clickDelStation(item) {
-				showModal("纭鍒犻櫎绔欑偣", "璀﹀憡",true,"纭畾","鍙栨秷").then((res) => {
+				showModal({
+					title: this.translate("ask_confirm_remove"),
+					content: `${this.translate('delete_selected_station')}`,
+					confirmText: this.translate('remove'),
+					cancelText: this.translate('cancel'),
+				}).then((res) => {
 					if (res) {
 						this.deleteStation(item)
 					}
@@ -388,13 +393,18 @@
 					}
 
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 
 			},
 			clickStationDelete() {
 				const _this = this
-				showModal("纭鍒犻櫎绔欑偣", "璀﹀憡",true,"纭畾","鍙栨秷").then((res) => {
+				showModal({
+					title: this.translate("ask_confirm_remove"),
+					content: `${this.translate('delete_selected_station')}`,
+					confirmText: this.translate('remove'),
+					cancelText: this.translate('cancel'),
+				}).then((res) => {
 					if (res) {
 						_this.deleteStation(this.stationEdit)
 						_this.$refs.refPopupOperateStation.close()
@@ -408,7 +418,7 @@
 
 				this.opStationType = "edit_pos"
 				uni.setNavigationBarTitle({
-					title: "璋冩暣绔欑偣浣嶇疆鍜屾湞鍚�
+					title: this.translate("adjust_position_orientation")
 				})
 				this.$refs.refPopupOperateStation.close()
 			},
@@ -416,7 +426,7 @@
 				this.isEdit = true
 				this.opStationType = "edit_name"
 				uni.setNavigationBarTitle({
-					title: "绔欑偣閲嶅懡鍚�
+					title: this.translate("rename")
 				})
 				this.$refs.refPopupOperateStation.close()
 			},
@@ -435,7 +445,7 @@
 
 
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 			},
 			async stationUpdate(item) {
@@ -451,7 +461,7 @@
 					}
 
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 			},
 
@@ -465,7 +475,7 @@
 				if (this.isEdit) {
 					this.opStationType = ""
 					uni.setNavigationBarTitle({
-						title: "绔欑偣鍒楄〃"
+						title: this.translate("station_list")
 					})
 					this.$refs.refPopupOperateStation.open("bottom")
 				} else {
@@ -476,7 +486,7 @@
 				try {
 					const name = this.stationEdit.name.trim()
 					if (!name) {
-						showToast("绔欑偣鍚嶇О杩樻湭杈撳叆")
+						showToast(this.translate("input_station_name"))
 						return
 					}
 					if (this.isEdit) {
@@ -484,7 +494,7 @@
 						await this.stationUpdate(this.stationEdit)
 						this.opStationType = ""
 						uni.setNavigationBarTitle({
-							title: "绔欑偣鍒楄〃"
+							title: this.translate("station_list")
 						})
 						this.$refs.refPopupOperateStation.open("bottom")
 					} else {
@@ -499,7 +509,7 @@
 					}
 
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 
 			},
@@ -519,7 +529,7 @@
 						await this.stationUpdate(this.stationEdit)
 						this.opStationType = ""
 						uni.setNavigationBarTitle({
-							title: "绔欑偣鍒楄〃"
+							title: this.translate("station_list")
 						})
 						this.$refs.refPopupOperateStation.open("bottom")
 					} else {
@@ -529,7 +539,7 @@
 					}
 				} catch (ex) {
 
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 			},
 			clickClearName() {
@@ -543,44 +553,33 @@
 				else
 					this.showClearName = false
 			},
+			validateNumber(input) {
+				// 甯哥敤姝e垯妯″紡
+				const patterns = {
+					integer: /^-?\d+$/, // 鏁存暟锛屽寘鎷礋鏁�[citation:6]
+					positiveInt: /^[1-9]\d*$/, // 姝f暣鏁�[citation:7]
+					decimal: /^-?\d*\.?\d+$/, // 灏忔暟 (鏀寔鍍�".1" 杩欐牱鐨勬牸寮�
+					decimalFixed: /^-?\d*\.?\d{0,3}$/, // 鏈�涓や綅灏忔暟 [citation:5][citation:8]
+				};
+				// 閫夋嫨闇�鐨勬ā寮忚繘琛屾祴璇曪紝渚嬪浣跨敤 decimal
+				return patterns.decimalFixed.test(input);
+			},
 			onInputX(event) {
-				const strictRegex = /^[-+]?(\d+\.?\d*|\.\d+)$/;
 				let num = event.detail.value
-				if (!strictRegex.test(num)) {
-					showToast("杈撳叆鍐呭鍙兘鍖呭惈鏁板瓧銆佹璐熷彿鍜屽皬鏁扮偣")
+				if (!this.validateNumber(num)) {
+					showToast(this.translate("input_content_only_numbers_and_signs_and_decimal"))
 					return
-				} else {
-					if (Math.round(Number(num)) === Number(num)) {
-						if (!Number.isSafeInteger(Number(num))) {
-							showToast("杈撳叆鏁板瓧瓒呭嚭鏁板�瀹夊叏鑼冨洿")
-						}
-					} else {
-						if (!Number.isSafeInteger(Math.round(Number(num)))) {
-							showToast("杈撳叆鏁板瓧瓒呭嚭鏁板�瀹夊叏鑼冨洿")
-						}
-					}
 				}
-				this.stationEdit.x = event.detail.value
+				this.stationEdit.x = Number(event.detail.value)
 
 			},
 			onInputY(event) {
-				const strictRegex = /^[-+]?(\d+\.?\d*|\.\d+)$/;
 				let num = event.detail.value
-				if (!strictRegex.test(num)) {
-					showToast("杈撳叆鍐呭鍙兘鍖呭惈鏁板瓧銆佹璐熷彿鍜屽皬鏁扮偣")
-				} else {
-					if (Math.round(Number(num)) === Number(num)) {
-						if (!Number.isSafeInteger(Number(num))) {
-							showToast("杈撳叆鏁板瓧瓒呭嚭鏁板�瀹夊叏鑼冨洿")
-						}
-					} else {
-						if (!Number.isSafeInteger(Math.round(Number(num)))) {
-							showToast("杈撳叆鏁板瓧瓒呭嚭鏁板�瀹夊叏鑼冨洿")
-						}
-					}
-
+				if (!this.validateNumber(num)) {
+					showToast(this.translate("input_content_only_numbers_and_signs_and_decimal"))
+					return
 				}
-				this.stationEdit.y = event.detail.value
+				this.stationEdit.y = Number(event.detail.value)
 
 			},
 			getStantardAngle(angle) {
@@ -666,11 +665,14 @@
 					this.angleSvg = `/static/images/angle${angle}.svg`
 					this.stationEdit.angle = angle * 3.14 / 180
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 			},
-		
 
+			translate(t) {
+				if (typeof this.$t == "function") return this.$t(`page.${t}`)
+				else return t;
+			},
 		}
 	}
 </script>
diff --git a/pages/task/add.vue b/pages/task/add.vue
index 88067f0..65bfb07 100644
--- a/pages/task/add.vue
+++ b/pages/task/add.vue
@@ -1,65 +1,68 @@
 <template>
 	<view class="pages-task-add">
-		<uni-nav-bar :fixed="true" status-bar right-text="" left-text="" leftWidth="72rpx" rightWidth="72rpx"
-			:title="navigationBarTitle">
+		<uni-nav-bar :fixed="true" status-bar  
+			:title="navigationBarTitle" @clickLeft="clickCancel" @clickRight="clickSave">
 			<view class="uni-navbar-container-inner">
 				<text class="uni-nav-bar-text">{{navigationBarTitle }}</text>
 			</view>
 			<template v-slot:right>
 				<view class="uni-navbar-btn-text">
-					<a @click="clickSave" class="uni-nav-bar-right-text">
-						淇濆瓨
+					<a class="uni-nav-bar-right-text">
+						{{translate('save')}}
 					</a>
 				</view>
 			</template>
 			<template v-slot:left>
 				<view class="uni-navbar-btn-text">
-					<a @click="clickCancel" class="uni-nav-bar-left-text">
-						鍙栨秷
+					<a   class="uni-nav-bar-left-text">
+						{{translate('cancel')}}
 					</a>
 				</view>
 
-			</template>
+			</template> 
 		</uni-nav-bar>
 		<view class="content">
-			<view class="header">浠诲姟灞炴�</view>
+			<view class="header">{{translate("task_properties")}}</view>
 			<view class="group">
 				<view class="item">
-					<view>鍚嶇О锛�/view>
-					<input class="right-input" placeholder="杈撳叆浠诲姟鍚嶇О" v-model="info.taskGroupName" />
+					<view>{{translate("name")}}</view>
+					<input class="right-input" :placeholder="translate('input_task_name')"
+						v-model="info.taskGroupName" />
 				</view>
 				<view class="item">
-					<view>灞炴�锛�/view>
-					<view class="right" v-if="info.tasktype > 0">{{info.tasktype ==1 ? '鍥哄畾':'涓存椂'}}</view>
-					<view class="right" v-else>閫夋嫨灞炴�</view>
+					<view>{{translate("properties")}}</view>
+					<view class="right" v-if="info.tasktype > 0">
+						{{info.tasktype ==1 ? translate("fixed"):translate("temporary")}}
+					</view>
+					<view class="right" v-else>{{translate("select_properties")}}</view>
 					<a @click="clickType">
 						<uni-icons class="icon" type="right" size="20"></uni-icons>
 					</a>
 				</view>
 				<view class="item" v-if="info.tasktype == 1">
-					<view>閲嶅娆℃暟锛�/view>
+					<view>{{translate("number_repetitions")}}</view>
 					<input class="right-input" type="number" :value="info.cycleTime" :maxlength="4"
 						@input="onInputCycleTime" />
 				</view>
 				<view class="item">
-					<view>鎸夐挳鍙凤細</view>
-					<view class="right">{{info.taskButton ?info.taskButton:""}}</view>
+					<view>{{translate("button_number")}}</view>
+					<view class="right">{{info.taskButton ?info.taskButton:translate("select_button_number")}}</view>
 					<a @click="clickTaskButton">
 						<uni-icons class="icon" type="right" size="20"></uni-icons>
 					</a>
 				</view>
 			</view>
-			<view class="header">浠诲姟璺嚎</view>
+			<view class="header">{{translate("task_route")}}</view>
 			<view class="group">
 
 				<uni-swipe-action class="list">
 					<uni-swipe-action-item class="list-item" v-for="(item,index) in pathwayList" :key="index"
 						:auto-close="true">
 						<view class="item">
-							<view>鐩爣鐐箋{index+1}}锛�/view>
+							<view>{{translate("target_point") + " "}}{{index+1}}锛�/view>
 							<view class="right"></view>
 							<a @click.stop="(e)=>{ this.clickPathwayPoint(e,index,'stationID')}">
-								{{item.stationID ?stationName(item.stationID) : "鐩爣鐐�}}
+								{{item.stationID ?stationName(item.stationID) :translate("target_point")}}
 
 							</a>
 							<a @click.stop="(e)=>{ clickPathwayPoint(e,index,'actionType')}">
@@ -67,21 +70,21 @@
 							</a>
 							<template class="time" v-if="item.actionType === 3">
 								<a @click.stop="()=>{item.wait = 0}" v-if="item.wait===undefined">
-									绛夊緟鏃堕棿
+									{{translate("wait_time")}}
 								</a>
 								<input v-else class="input" type="number" v-model="item.wait" :maxlength="4" />
 							</template>
 							<span v-if="item.actionType === 3 && item.wait!==undefined">
-								绉�+								{{translate("second")}}
 							</span>
 						</view>
 						<template v-slot:right>
-							<view class="btn-del" @click="clickDelPathWay(index)">鍒犻櫎</view>
+							<view class="btn-del" @click="clickDelPathWay(index)">{{translate("delete")}}</view>
 						</template>
 					</uni-swipe-action-item>
 				</uni-swipe-action>
 				<view class="item">
-					<a-button class="button" @click="clickAddPathway">+ 娣诲姞鐩爣鐐�+					<a-button class="button" @click="clickAddPathway">{{"+  "+translate("add_target_point")}}
 					</a-button>
 				</view>
 			</view>
@@ -130,7 +133,7 @@
 			return {
 				indicatorStyle: `height: 75rpx;`,
 				ip: '',
-				navigationBarTitle: '鏂板浠诲姟',
+				navigationBarTitle: this.translate("new_task"),
 				info: {
 					taskGroupName: "",
 					cycleTime: 1,
@@ -148,18 +151,22 @@
 
 
 				actionList: [{
-					name: "瀵艰埅",
-					type: 1,
-				}, {
-					name: "鍙栬揣",
-					type: 2,
-				}, {
-					name: "鍗歌揣",
-					type: 3,
-				}, {
-					name: "浜哄伐",
-					type: 4,
-				}]
+						name: this.translate("navigation"),
+						type: 1,
+					},
+					// {
+					// 	name: "鍙栬揣",
+					// 	type: 2,
+					// }, 
+					{
+						name: this.translate("auto_unload"),
+						type: 3,
+					},
+					// {
+					// 	name: "浜哄伐",
+					// 	type: 4,
+					// },
+				],
 			}
 		},
 		computed: {
@@ -168,7 +175,7 @@
 
 		onLoad(option) {
 			const _this = this
-			this.navigationBarTitle = option.title || "鏂板浠诲姟"
+			this.navigationBarTitle = option.title || this.translate("new_task")
 			this.ip = option.ip || ""
 			uni.getSystemInfo({
 				success(e) {
@@ -208,26 +215,26 @@
 					const info = await stations(this.ip)
 					this.stationList = info.station_list || []
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 			},
 			async clickSave() {
 				try {
 					const name = this.info.taskGroupName.trim()
 					if (!name) {
-						showToast("鏈緭鍏ヤ换鍔″悕绉帮紒")
+						showToast(this.translate("input_task_name"))
 						return
 					}
 					if (!this.info.tasktype) {
-						showToast("鏈�鎷╀换鍔″睘鎬э紒")
+						showToast(this.translate("select_task_properties"))
 						return
 					}
 					if (!this.info.taskButton) {
-						showToast("鏈�鎷╂寜閽彿锛�)
+						showToast(this.translate("select_button_number"))
 						return
 					}
 					uni.showLoading({
-						title: "浠诲姟鏂板涓�
+						title: this.translate("adding_task")
 					})
 					const taskList = []
 					const task = {
@@ -244,7 +251,7 @@
 						const item = this.pathwayList[i]
 						if (item.stationID) {
 							if (!item.actionType) {
-								showToast("鐩爣鐐规湭閫夋嫨鎿嶄綔绫诲瀷锛�)
+								showToast(this.translate("target_point_no_operation_type"))
 								return
 							}
 							task.taskList.push({
@@ -266,7 +273,7 @@
 					}
 
 					if (taskList.length == 0) {
-						showToast("蹇呴』娣诲姞鑷冲皯涓�釜鐩爣鐐癸紒")
+						showToast(this.translate("at_least_one_target_point_added"))
 						return
 					}
 					await addTask(this.ip, task)
@@ -278,7 +285,7 @@
 						delta: 1, //杩斿洖灞傛暟锛�鍒欎笂涓婇〉
 					})
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				} finally {
 					uni.hideLoading()
 				}
@@ -302,7 +309,14 @@
 				}
 				if (flag) {
 
-					showModal(`褰撳墠缂栬緫鐨勫唴瀹瑰皢涓嶄細琚繚瀛榒, "纭畾瑕侀�鍑虹紪杈戝悧?", true, "纭畾", "鍙栨秷").then((res) => {
+					showModal(
+					{
+						title: this.translate("ask_exit_task_edit"),
+						content: `${this.translate('current_edited_content_will_be_deleted')}`,
+						confirmText: this.translate('ok'),
+						cancelText: this.translate('cancel'),
+					}).then((
+						res) => {
 						if (res) {
 							uni.navigateBack({
 								delta: 1, //杩斿洖灞傛暟锛�鍒欎笂涓婇〉
@@ -353,10 +367,10 @@
 					if (curIndex > -1) {
 						return this.actionList[curIndex].name
 					} else {
-						return "鎿嶄綔"
+						return this.translate("task_action")
 					}
 				} else {
-					return "鎿嶄綔"
+					return this.translate("task_action")
 				}
 			},
 
@@ -430,10 +444,12 @@
 				this.pickerView = {
 					type: "type",
 					list: [{
-						name: "鍥哄畾"
-					}, {
-						name: "涓存椂"
-					}],
+						name: this.translate("fixed")
+					},
+					// {
+					// 	name:this.translate("temporary")
+					// },
+					],
 					value: [this.info.tasktype - 1],
 					top: e.target.offsetTop + 30,
 					right: 0,
@@ -461,7 +477,7 @@
 				if (this.pathwayList.length > 1) {
 					this.pathwayList.splice(index, 1)
 				} else {
-					showToast("蹇呴』鏈変竴涓洰鏍囩偣")
+					showToast(this.translate("at_least_one_target_point_added"))
 				}
 			},
 			onInputCycleTime(event) {
@@ -490,7 +506,10 @@
 			closeMenu() {
 				this.$refs.refPopupMenu.close()
 			},
-
+			translate(t) {
+				if (typeof this.$t == "function") return this.$t(`page.${t}`)
+				else return t;
+			},
 
 		}
 	}
@@ -546,6 +565,7 @@
 				.right-input {
 					text-align: right;
 					flex: 1;
+					color: #1890FF;
 				}
 
 				.text {
diff --git a/pages/task/index.vue b/pages/task/index.vue
index 00fa805..4d96bf7 100644
--- a/pages/task/index.vue
+++ b/pages/task/index.vue
@@ -3,15 +3,15 @@
 		<view class="header">
 			<view class="item">
 				<view class="title">{{accumulated_duration}}<text class="text">min</text></view>
-				<view class="text">绱鏃堕暱</view>
+				<view class="text">{{translate("cumulative_duration")}}</view>
 			</view>
 			<view class="item">
 				<view class="title">{{accumulated_mileage}}<text class="text">m</text></view>
-				<view class="text">绱閲岀▼</view>
+				<view class="text">{{translate("accumulated_mileage")}}</view>
 			</view>
 			<view class="item">
 				<view class="title">{{cumulative_number}}</view>
-				<view class="text">绱娆℃暟</view>
+				<view class="text">{{translate("cumulative_count")}}</view>
 			</view>
 		</view>
 		<view class="list">
@@ -96,7 +96,11 @@
 				uni.navigateTo({
 					url: "/pages/task/map-task"
 				})
-			}
+			},
+			translate(t) {
+				if (typeof this.$t == "function") return this.$t(`page.${t}`)
+				else return t;
+			},
 
 		}
 	}
diff --git a/pages/task/infos/task-item.vue b/pages/task/infos/task-item.vue
index 72fd1ae..91e32aa 100644
--- a/pages/task/infos/task-item.vue
+++ b/pages/task/infos/task-item.vue
@@ -44,7 +44,7 @@
 		<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">{{translate("target_point" ) + " "+(index+ 1)}}</text>
 				<text class="text">{{getDestText(item)}}</text>
 				<text class="text">{{getActionText(item)}}</text>
 			</view>
@@ -105,13 +105,37 @@
 			},
 			taskCycleTime() {
 
-				if (this.taskIsRun)
-					return `绗�{this.taskStatus?.curCycleNumber || 0}/${this.taskStatus?.cycleTotalNumber || 0}娆
-
-				else
-					return `閲嶅${this.taskData.cycleTime || 0}娆
-
-
+				if (this.taskIsRun) {
+					const time = this.taskStatus?.curCycleNumber || 0
+					let timeStr = `${time}${this.translate("the_th_suffix")}`
+					if (time % 10 == 1) {
+						timeStr = `${time}${this.translate("the_st_suffix")}`
+					} else if (time % 10 == 2) {
+						timeStr = `${time}${this.translate("the_nd_suffix")}`
+					} else if (time % 10 == 3) {
+						timeStr = `${time}${this.translate("the_rd_suffix")}`
+					}
+					return this.translate("time_number", [timeStr,
+						`${this.taskStatus?.cycleTotalNumber || 0}`
+					])
+				} else {
+					const time = this.taskData.cycleTime || 0
+					let timeStr = ""
+					if(time == 1)
+					{
+						timeStr = this.translate("repeat_once")
+					}
+					else if(time == 2)
+					{
+						timeStr = this.translate("repeat_twice")
+					}
+					else
+					{
+						timeStr = this.translate("repeat_times", [`${time}`])
+					}
+		
+					return timeStr
+				}
 			},
 			taskIsRun() {
 				if (this.taskStatus?.taskGroupID == this.taskData.taskGroupID) {
@@ -197,19 +221,19 @@
 			},
 
 			getStatusText(status) {
-				var statusText = "鏈煡"
+				var statusText = this.translate("unknown")
 				if (!status) {
-					statusText = "鏈紑濮�
+					statusText = this.translate("not_started")
 				} else if (status === 5) {
-					statusText = "寮哄埗瀹屾垚"
+					statusText = this.translate("mandatory_completion")
 				} else if (status === 4) {
-					statusText = "寮傚父缁撴潫"
+					statusText = this.translate("abnormal_termination")
 				} else if (status === 3) {
-					statusText = "宸插彇娑�
+					statusText = this.translate("canceled")
 				} else if (status === 2) {
-					statusText = "宸插畬鎴�
+					statusText = this.translate("completed")
 				} else if (status === 1) {
-					statusText = "鎵ц涓�
+					statusText = this.translate("in_progress")
 				}
 				return statusText
 			},
@@ -224,21 +248,35 @@
 				const index = this.taskData.taskList.findIndex((a) => a.taskID == item.taskID)
 				if (index > -1) {
 					const task = this.taskData.taskList[index]
-					var actionText = "鏈煡"
+					var actionText = this.translate("unknown")
 					if (task.actionType === 1) {
-						actionText = "瀵艰埅"
-					} else if (task.actionType === 2) {
-						actionText = "鍙栬揣"
-					} else if (task.actionType === 3) {
-						actionText = `鍗歌揣 绛夊緟${task.wait || 0}绉抈
-					} else if (task.actionType === 4) {
-						actionText = "浜哄伐"
+						actionText = this.translate("navigation")
 					}
+					// else if (task.actionType === 2) {
+					// 	actionText = "鍙栬揣"
+					// } 
+					else if (task.actionType === 3) {
+						actionText =
+							`${this.translate("auto_unload")}:${this.translate("wait")}:${task.wait || 0}${this.translate("second")}`
+					}
+					// else if (task.actionType === 4) {
+					// 	actionText = "浜哄伐"
+					// }
 					return actionText
 				}
 				return ""
 			},
-
+			translate(t, values) {
+				if (typeof this.$t == "function") {
+					const message = this.$t(`page.${t}`)
+					if (values) {
+						return message.replace(/{(\d+)}/g, (match, index) => {
+							const value = values[index]
+							return value !== undefined ? value : match
+						})
+					} else return message
+				} else return t;
+			},
 		}
 	}
 </script>
@@ -268,6 +306,7 @@
 				border-bottom: 1px solid #ddd;
 				display: flex;
 				flex-direction: row;
+
 				.text {
 					color: gray;
 					padding: 10rpx;
diff --git a/pages/task/infos/task-log-item.vue b/pages/task/infos/task-log-item.vue
index adcd347..df3b586 100644
--- a/pages/task/infos/task-log-item.vue
+++ b/pages/task/infos/task-log-item.vue
@@ -10,7 +10,7 @@
 				</view>
 			</view>
 			<view class="line"><text class="text">{{ startTime}} - {{endTime}} </text>
-				<text class="text">{{Math.ceil((taskData.end_time- taskData.start_time) / (60* 1000)) }}min</text>
+				<text class="text">{{Math.ceil((taskData.end_time- taskData.start_time) / (60* 1000)) }}{{translate("minute")}}</text>
 			</view>
 		</view>
 	</view>
@@ -41,7 +41,7 @@
 		computed: {
 			startTime() {
 				const date = new Date(Number(this.taskData.start_time))
-				return`${date.getHours()}:${date.getMinutes()}`;
+				return `${date.getHours()}:${date.getMinutes()}`;
 			},
 			endTime() {
 				const date = new Date(Number(this.taskData.end_time))
@@ -50,9 +50,9 @@
 			taskStatusText() {
 				let statusText = ""
 				if (this.taskData.task_status == "finish") {
-					statusText = "浠诲姟瀹屾垚"
+					statusText = this.translate("task_completed")
 				} else if (this.taskData.task_status == "cancel") {
-					statusText = "浠诲姟寮傚父"
+					statusText = this.translate("task_exception")
 				} else {
 					statusText = this.taskData.task_status
 				}
@@ -61,9 +61,9 @@
 			taskStatusText() {
 				let statusText = ""
 				if (this.taskData.task_status == "finish") {
-					statusText = "浠诲姟瀹屾垚"
+					statusText =  this.translate("task_completed")
 				} else if (this.taskData.task_status == "cancel") {
-					statusText = "浠诲姟寮傚父"
+					statusText =this.translate("task_exception")
 				} else {
 					statusText = this.taskData.task_status
 				}
@@ -106,7 +106,11 @@
 			},
 			clickTask() {
 				this.$emit('click-item', this.taskData)
-			}
+			},
+			translate(t) {
+				if (typeof this.$t == "function") return this.$t(`page.${t}`)
+				else return t;
+			},
 
 		}
 	}
diff --git a/pages/task/list.vue b/pages/task/list.vue
index e7d9573..561dba3 100644
--- a/pages/task/list.vue
+++ b/pages/task/list.vue
@@ -1,7 +1,7 @@
 <template>
 	<view class="pages-task-list">
 		<view class="list-content" v-if="list.length > 0">
-			<view class="list-header" v-if="fixedList.length > 0">鍥哄畾浠诲姟</view>
+			<view class="list-header" v-if="fixedList.length > 0">{{translate("fixed_task")}}</view>
 			<uni-swipe-action class="list">
 				<uni-swipe-action-item class="list-item" v-for="(item,index) in fixedList" :key="index"
 					:auto-close="true" :disabled="taskStatus.taskGroupID == item.taskGroupID">
@@ -11,11 +11,11 @@
 						@click-repeat="clickRepeatTask">
 					</TaskItemView>
 					<template v-slot:right>
-						<view class="btn-del" @click="clickDelSetTask(item)">鍒犻櫎</view>
+						<view class="btn-del" @click="clickDelSetTask(item)">{{translate("delete")}}</view>
 					</template>
 				</uni-swipe-action-item>
 			</uni-swipe-action>
-			<view class="list-header" v-if="tempList.length > 0">涓存椂浠诲姟</view>
+			<view class="list-header" v-if="tempList.length > 0">{{translate("temporary_task")}}</view>
 			<uni-swipe-action class="list">
 				<uni-swipe-action-item class="list-item" v-for="(item,index) in tempList" :key="index"
 					:auto-close="true" :disabled="taskStatus.taskGroupID == item.taskGroupID">
@@ -24,16 +24,16 @@
 						@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>
+						<view class="btn-del" @click="clickDelSetTask(item)">{{translate("delete")}}</view>
 					</template>
 				</uni-swipe-action-item>
 			</uni-swipe-action>
 		</view>
 		<view class="list-no-content" v-else>
 			<uni-icons color="#ccc" type="info" size="128"></uni-icons>
-			<view class="space">娌℃湁鎵惧埌绗﹀悎鏉′欢鐨勪换鍔�/view>
+			<view class="space">{{translate("not_task_found")}}</view>
 		</view>
-		<view class="position-add" @click="clickAddTask"  @touchstart='btnAddTouchStart' @touchmove='btnAddTouchMove'
+		<view class="position-add" @click="clickAddTask" @touchstart='btnAddTouchStart' @touchmove='btnAddTouchMove'
 			:style="{transform:`translate(${btnAddInfo.x}px,${btnAddInfo.y}px) scale(1)`}">
 			<uni-icons class="img" type="plus-filled" size="80" color="#1890FF"></uni-icons>
 		</view>
@@ -60,17 +60,15 @@
 	import {
 		v4 as uuidv4
 	} from 'uuid';
-	import {
-		filter
-	} from "rxjs";
+
 	export default {
-		name: "PagesTasklist",
+		name: "PagesTaskList",
 		components: {
 			TaskItemView
 		},
 		data() {
 			return {
-				loading:false,
+				loading: false,
 				ip: "",
 				sceneId: "",
 				list: [],
@@ -80,7 +78,7 @@
 					[]
 				], // 姣忎釜椤圭洰鐨勫亸绉婚噺
 				options: [{
-					text: '鍒犻櫎',
+					text: this.translate('delete'),
 					style: {
 						backgroundColor: '#F56C6C'
 					}
@@ -107,10 +105,10 @@
 			this.ip = option.ip || ""
 			this.sceneId = option.sceneId || ""
 			this.isPageVisible = true
-			this.$nextTick(()=>{
+			this.$nextTick(() => {
 				this.loadData()
 			})
-			
+
 		},
 		onShow() {
 			this.isPageVisible = true
@@ -164,34 +162,38 @@
 
 				} catch (ex) {
 
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 			},
 			async loadTaskList() {
 				try {
+					uni.showLoading({
+						title: this.translate("loading")
+					})
 					const res = await tasks(this.ip)
 					const list = res?.data || []
 					this.list = list
-					showToast(`瑁呰浇浜�{list.length}浠诲姟`)
 				} catch (ex) {
 
-					showError(ex)
+					showError(ex, this.translate('error'))
+				} finally {
+					uni.hideLoading()
 				}
-				
+
 			},
 
 			async timerCheckTaskGroupStatus() {
 				try {
 					if (this.isPageVisible)
 						await this.checkTaskGroupStatus()
-						// setTimeout(this.timerCheckTaskGroupStatus, 1000);
+					// setTimeout(this.timerCheckTaskGroupStatus, 1000);
 				} catch (ex) {
 					showToast(ex)
-					// showError(ex).then((res)=>{
+					// showError(ex,this.translate('error')).then((res)=>{
 					// 	setTimeout(this.timerCheckTaskGroupStatus, 1000);
 					// })
 				} finally {
-						setTimeout(this.timerCheckTaskGroupStatus, 1000);
+					setTimeout(this.timerCheckTaskGroupStatus, 1000);
 				}
 			},
 			async checkTaskGroupStatus() {
@@ -231,9 +233,9 @@
 			},
 			clickAddTask() {
 				const _this = this
-				
+
 				uni.navigateTo({
-					url: `/pages/task/add?ip=${this.ip}&title=鏂板浠诲姟`,
+					url: `/pages/task/add?ip=${this.ip}&title=${this.translate("new_task")}`,
 					events: {
 						// 涓烘寚瀹氫簨浠舵坊鍔犱竴涓洃鍚櫒锛岃幏鍙栬鎵撳紑椤甸潰浼犻�鍒板綋鍓嶉〉闈㈢殑鏁版嵁
 						add_task: function(data) {
@@ -291,7 +293,7 @@
 					return
 				}
 				uni.navigateTo({
-					url: `/pages/task/update?ip=${this.ip}&title=鏇存柊浠诲姟&task=${JSON.stringify(item)}`,
+					url: `/pages/task/update?ip=${this.ip}&title=${this.translate("update_task")}&task=${JSON.stringify(item)}`,
 					events: {
 						// 涓烘寚瀹氫簨浠舵坊鍔犱竴涓洃鍚櫒锛岃幏鍙栬鎵撳紑椤甸潰浼犻�鍒板綋鍓嶉〉闈㈢殑鏁版嵁
 						update_task: function(data) {
@@ -317,16 +319,16 @@
 					this.setTaskGroupStatus(cmdID)
 
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 			},
 
 			async clickStopTask(item) {
 				try {
 					await addTaskGroupCmd(this.ip, item.taskGroupID, this.taskStatus.taskGroupCmdID, 2)
-					showToast("缁堟浠诲姟鎴愬姛")
+					showToast(this.translate("task_terminated_success"))
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 			},
 
@@ -350,14 +352,13 @@
 							this.taskStatus.taskGroupCmdID,
 							taskList
 						)
-
-						showToast("璺宠繃瀛愪换鍔℃垚鍔�)
+						showToast(this.translate("skip_sub_task_success"))
 
 					} else {
-						showToast("鏈壘鍒版湭寮�鎴栨鍦ㄦ墽琛岀殑瀛愪换鍔�)
+						showToast(this.translate("no_sub_task_found_not_started_or_executing"))
 					}
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 			},
 
@@ -368,12 +369,18 @@
 
 			async clickPauseTask(item) {},
 			clickDelSetTask(item) {
-				showModal("纭鍒犻櫎浠诲姟璁剧疆", "璀﹀憡", true, "纭畾", "鍙栨秷").then((res) => {
-					if (res) {
-						this.deleteTask(item)
+				showModal({
+						title: this.translate('ask_confirm_delete'),
+						content: this.translate('history_task_will_be_retained'),
+						confirmText: this.translate('delete'),
+						cancelText: this.translate('cancel'),
+					})
+					.then((res) => {
+						if (res) {
+							this.deleteTask(item)
 
-					}
-				})
+						}
+					})
 			},
 			async deleteTask(item) {
 				try {
@@ -384,19 +391,22 @@
 					const index = list.findIndex((a) =>
 						a.taskGroupID == item.taskGroupID
 					)
-					showToast("鍒犻櫎浠诲姟鎴愬姛")
+					showToast(this.translate("delete_success"))
 					if (index < 0)
 						return
 					list.splice(index, 1)
 					this.list = [...list]
 
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 
 			},
 
-
+			translate(t) {
+				if (typeof this.$t == "function") return this.$t(`page.${t}`)
+				else return t;
+			},
 		}
 	}
 </script>
diff --git a/pages/task/log-list.vue b/pages/task/log-list.vue
index b1c6003..b222595 100644
--- a/pages/task/log-list.vue
+++ b/pages/task/log-list.vue
@@ -3,17 +3,17 @@
 		<view class="list-content" v-if="list.length > 0">
 			<view class="header">
 				<view class="item">
-					<view class="title">{{totalDuration}}<text class="text">min</text></view>
-					<view class="text">绱鏃堕暱</view>
+					<view class="title">{{totalDuration}}<text class="text">{{translate("minute")}}</text></view>
+					<view class="text">{{translate("cumulative_duration")}}</view>
 				</view>
 
 				<view class="item">
 					<view class="title">{{totalCount}}</view>
-					<view class="text">绱娆℃暟</view>
+					<view class="text">{{translate("cumulative_count")}}</view>
 				</view>
 			</view>
 			<view class="list">
-				<!-- <view class="task-header" v-if="fixedList.length > 0">鍥哄畾浠诲姟</view> -->
+				<!-- <view class="task-header" v-if="fixedList.length > 0">{{translate("fixed_task")}</view> -->
 				<template v-for="(group) in taskList" :key="group.date">
 					<view class="task-header">{{group.date}}</view>
 					<view class="task-list-view">
@@ -22,7 +22,7 @@
 						</TaskLogItemView>
 					</view>
 				</template>
-				<!-- <view class="task-header" v-if="tempList.length > 0">涓存椂浠诲姟</view>
+				<!-- <view class="task-header" v-if="tempList.length > 0">{{translate("temporary_task")}</view>
 				<template v-for="(group) in tempList" :key="group.date">
 					<view class="task-header">{{group.date}}</view>
 					<view class="task-list-view">
@@ -37,7 +37,7 @@
 		<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 class="space">{{translate("no_task_history_found")}}</view>
 		</view>
 	</view>
 </template>
@@ -69,8 +69,9 @@
 			taskList() {
 				const listRes = []
 				this.list.forEach((ele) => {
-					const dateStart = new Date(Number(ele.start_time))
-					const date = `${dateStart.getMonth()}-${dateStart.getDay()}`;
+					const dateStart = new Date(parseInt(ele.start_time))
+				
+					const date = `${dateStart.getMonth() +1}-${dateStart.getDate()}`;
 					const curIndex = listRes.findIndex((a) => a.date == date)
 					if (curIndex < 0) {
 						let group = {
@@ -121,13 +122,14 @@
 				// this.list.forEach((item) => {
 				// 	d += item.duration || 0
 				// })
+				
 				this.list.forEach((item) => {
-					d +=( item.end_time -  item.start_time)
+					d += Math.ceil((item.end_time- item.start_time) / (60* 1000)) //(item.end_time - item.start_time)
 				})
-				return Math.ceil(d / (60 * 1000))
+				return d//Math.ceil(d / (60 * 1000))
 			},
 			totalCount() {
-				 let cnt =this.list.length
+				let cnt = this.list.length
 				// this.list.forEach((item) => {
 				// 	cnt += item.list.length
 				// })
@@ -165,12 +167,15 @@
 				try {
 					this.list = await this.loadTaskLog()
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 			},
 			async loadTaskLog() {
 				try {
-					
+
+					uni.showLoading({
+						title: this.translate("loading")
+					})
 					const now = new Date();
 					const oneWeekAgo = new Date();
 					const endTimeStamp = `${now.getTime()}`
@@ -182,13 +187,13 @@
 					list.sort((a, b) => {
 						return a.end_time < b.end_time ? 1 : -1
 					});
-				
-					showToast(`瑁呰浇浜�{list.length}璁板綍`)
 					return list
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 					return []
-				} 
+				} finally {
+					uni.hideLoading()
+				}
 			},
 
 			clickTask(item) {
@@ -196,6 +201,10 @@
 				// 	url: "/pages/task/index"
 				// })
 			},
+			translate(t) {
+				if (typeof this.$t == "function") return this.$t(`page.${t}`)
+				else return t;
+			},
 
 		}
 	}
diff --git a/pages/task/map-task.vue b/pages/task/map-task.vue
index 58776c7..df448cd 100644
--- a/pages/task/map-task.vue
+++ b/pages/task/map-task.vue
@@ -238,7 +238,12 @@
 				})
 			},
 			clickDelSetTask(item) {
-				showModal("纭鍒犻櫎浠诲姟璁剧疆", "璀﹀憡",true,"纭畾","鍙栨秷").then((res) => {
+				showModal({
+					title: this.translate('ask_confirm_delete'),
+					content: `${this.translate('history_task_will_be_retained')}`,
+					confirmText: this.translate('delete'),
+					cancelText: this.translate('cancel'),
+				}).then((res) => {
 					if (res) {
 						const list = this.setList
 						const index = list.findIndex((a) => {
@@ -269,23 +274,21 @@
 						[]
 					]
 					this.deleteX[root][index] = old; // 閲嶇疆鍋忕Щ閲�-				} else
-				{
+				} else {
 					this.deleteX = [
 						[],
 						[]
 					]
 					this.deleteX[root][index] = 0; // 閲嶇疆鍋忕Щ閲� 				}
-				
+
 			},
 			touchMove(event, root, index) {
 				const moveX = event.touches[0].pageX - this.startX;
 				if (moveX < 0) {
 					this.deleteX[root][index] = moveX; // 宸︽粦鏃舵洿鏂板亸绉婚噺
-				}
-				else if (moveX > 0) {
-					this.deleteX[root][index] =0
+				} else if (moveX > 0) {
+					this.deleteX[root][index] = 0
 				}
 			},
 			touchEnd(event, root, index) {
@@ -295,7 +298,10 @@
 					this.deleteX[root][index] = 0; // 鍚﹀垯鎭㈠鍘熶綅
 				}
 			},
-
+			translate(t) {
+				if (typeof this.$t == "function") return this.$t(`page.${t}`)
+				else return t;
+			},
 
 		}
 	}
diff --git a/pages/task/update.vue b/pages/task/update.vue
index d40a729..bafd66b 100644
--- a/pages/task/update.vue
+++ b/pages/task/update.vue
@@ -1,64 +1,67 @@
 <template>
 	<view class="pages-task-update">
-		<uni-nav-bar :fixed="true" status-bar right-text="" left-text="" leftWidth="72rpx" rightWidth="72rpx"
-			:title="navigationBarTitle">
+		<uni-nav-bar :fixed="true" status-bar :title="navigationBarTitle" @clickLeft="clickCancel"
+			@clickRight="clickSave">
 			<view class="uni-navbar-container-inner">
 				<text class="uni-nav-bar-text">{{navigationBarTitle }}</text>
 			</view>
 			<template v-slot:right>
-				<view class="uni-navbar-btn-text" @click="clickSave">
+				<view class="uni-navbar-btn-text">
 					<a class="uni-nav-bar-right-text">
-						淇濆瓨
+						{{translate('save')}}
 					</a>
 				</view>
 
 			</template>
 			<template v-slot:left>
 				<view class="uni-navbar-btn-text">
-					<a @click="clickCancel" class="uni-nav-bar-left-text">
-						鍙栨秷
+					<a class="uni-nav-bar-left-text">
+						{{translate('cancel')}}
 					</a>
 				</view>
 
 			</template>
 		</uni-nav-bar>
 		<view class="content">
-			<view class="header">浠诲姟灞炴�</view>
+			<view class="header">{{translate("task_properties")}}</view>
 			<view class="group">
 				<view class="item">
-					<view>鍚嶇О锛�/view>
-					<input class="right-input" placeholder="杈撳叆浠诲姟鍚嶇О" v-model="form.taskGroupName" />
+					<view>{{translate("name")}}</view>
+					<input class="right-input" :placeholder="translate('input_task_name')"
+						v-model="form.taskGroupName" />
 				</view>
 				<view class="item">
-					<view>灞炴�锛�/view>
-					<view class="right" v-if="form.tasktype > 0">{{form.tasktype ==1 ? '鍥哄畾':'涓存椂'}}</view>
-					<view class="right" v-else>閫夋嫨灞炴�</view>
+					<view>{{translate("properties")}}</view>
+					<view class="right" v-if="form.tasktype > 0">
+						{{form.tasktype ==1 ? translate("fixed"):translate("temporary")}}
+					</view>
+					<view class="right" v-else>{{translate("select_properties")}}</view>
 					<a @click="clickType">
 						<uni-icons class="icon" type="right" size="20"></uni-icons>
 					</a>
 				</view>
 				<view class="item" v-if="form.tasktype == 1">
-					<view>閲嶅娆℃暟锛�/view>
+					<view>{{translate("number_repetitions")}}</view>
 					<input class="right-input" type="number" :value="form.cycleTime" :maxlength="4"
 						@input="onInputCycleTime" />
 				</view>
 				<view class="item">
-					<view>鎸夐挳鍙凤細</view>
-					<view class="right">{{form.taskButton ?form.taskButton:""}}</view>
+					<view>{{translate("button_number")}}</view>
+					<view class="right">{{form.taskButton ?form.taskButton:translate("select_button_number")}}</view>
 					<a @click="clickTaskButton">
 						<uni-icons class="icon" type="right" size="20"></uni-icons>
 					</a>
 				</view>
 			</view>
-			<view class="header">浠诲姟璺嚎</view>
+			<view class="header">{{translate("task_route")}}</view>
 			<view class="group">
 				<view class="list">
 					<view class="list-item" v-for="(item,index) in pathwayList" :key="index" :auto-close="true">
 						<view class="item">
-							<view>鐩爣鐐箋{index+1}}锛�/view>
+							<view>{{translate("target_point") + " "}}{{index+1}}锛�/view>
 							<view class="right"></view>
 							<a @click.stop="(e)=>{ this.clickPathwayPoint(e,index,'stationID')}">
-								{{item.dest?.stationID ?item.dest?.name : "鐩爣鐐�}}
+								{{item.dest?.stationID ?item.dest?.name : translate("target_point")}}
 
 							</a>
 							<a @click.stop="(e)=>{ clickPathwayPoint(e,index,'actionType')}">
@@ -66,12 +69,12 @@
 							</a>
 							<template class="time" v-if="item.actionType === 3">
 								<a @click.stop="()=>{item.wait = 0}" v-if="item.wait===undefined">
-									绛夊緟鏃堕棿
+									{{translate("wait_time")}}
 								</a>
 								<input v-else class="input" type="number" v-model="item.wait" :maxlength="4" />
 							</template>
 							<span v-if="item.actionType === 3 && item.wait!==undefined">
-								绉�+								{{translate("second")}}
 							</span>
 						</view>
 					</view>
@@ -123,7 +126,7 @@
 			return {
 				indicatorStyle: `height: 75rpx;`,
 				ip: '',
-				navigationBarTitle: '鏇存柊浠诲姟',
+				navigationBarTitle: this.translate("update_task"),
 				info: {
 
 				},
@@ -132,7 +135,7 @@
 					taskGroupName: "",
 					cycleTime: 1,
 					taskButton: 0,
-					tasktype: 0,
+					tasktype: 1,
 					taskList: []
 				},
 				pathwayList: [],
@@ -146,18 +149,22 @@
 				windowSize: {},
 				isEditContent: false,
 				actionList: [{
-					name: "瀵艰埅",
-					type: 1,
-				}, {
-					name: "鍙栬揣",
-					type: 2,
-				}, {
-					name: "鍗歌揣",
-					type: 3,
-				}, {
-					name: "浜哄伐",
-					type: 4,
-				}]
+						name: this.translate("navigation"),
+						type: 1,
+					},
+					// {
+					// 	name: "鍙栬揣",
+					// 	type: 2,
+					// }, 
+					{
+						name: this.translate("auto_unload"),
+						type: 3,
+					},
+					// {
+					// 	name: "浜哄伐",
+					// 	type: 4,
+					// },
+				],
 			}
 		},
 		computed: {
@@ -167,7 +174,7 @@
 		onLoad(option) {
 
 			const _this = this
-			this.navigationBarTitle = option.title || "鏇存柊浠诲姟"
+			this.navigationBarTitle = option.title || this.translate("update_task")
 			this.ip = option.ip || ""
 			if (option.task) {
 				const info = JSON.parse(option.task)
@@ -186,7 +193,7 @@
 					this.pathwayList.push({
 						actionType: item.actionType,
 						dest: item.dest || {},
-						wait:  parseInt(item.wait) || 0,
+						wait: parseInt(item.wait) || 0,
 						taskID: item.taskID,
 						taskStartTimeStamp: item.taskStartTimeStamp,
 					})
@@ -232,22 +239,22 @@
 					const info = await stations(this.ip)
 					this.stationList = info.station_list || []
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 			},
 			async clickSave() {
 				try {
 					const name = this.form.taskGroupName.trim()
 					if (!name) {
-						showToast("鏈緭鍏ヤ换鍔″悕绉帮紒")
+						showToast(this.translate("input_task_name"))
 						return
 					}
 					if (!this.form.tasktype) {
-						showToast("鏈�鎷╀换鍔″睘鎬э紒")
+						showToast(this.translate("select_task_properties"))
 						return
 					}
 					uni.showLoading({
-						title: "浠诲姟鏇存柊涓�
+						title: this.translate("updating_task")
 					})
 					const task = {
 						taskGroupID: this.form.taskGroupID,
@@ -263,7 +270,7 @@
 						const item2 = this.info.taskList[i]
 						const item = this.pathwayList[i]
 						let flag = false
-						
+
 						if (item?.actionType != item2.actionType) {
 							flag = true
 						} else if (item?.dest?.stationID != item2.dest?.stationID) {
@@ -305,7 +312,7 @@
 						delta: 1, //杩斿洖灞傛暟锛�鍒欎笂涓婇〉
 					})
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				} finally {
 					uni.hideLoading()
 				}
@@ -343,7 +350,13 @@
 
 
 				if (flag) {
-					showModal(`褰撳墠缂栬緫鐨勫唴瀹瑰皢涓嶄細琚繚瀛榒, "纭畾瑕侀�鍑虹紪杈戝悧?", true, "纭畾", "鍙栨秷").then((res) => {
+					showModal({
+						title: this.translate(
+							"ask_exit_task_edit"),
+						content: `${this.translate('current_edited_content_will_be_deleted')}`,
+						confirmText: this.translate('ok'),
+						cancelText: this.translate('cancel'),
+					}).then((res) => {
 						if (res) {
 							uni.navigateBack({
 								delta: 1, //杩斿洖灞傛暟锛�鍒欎笂涓婇〉
@@ -396,10 +409,10 @@
 					if (curIndex > -1) {
 						return this.actionList[curIndex].name
 					} else {
-						return "鎿嶄綔"
+						return this.translate("task_action")
 					}
 				} else {
-					return "鎿嶄綔"
+					return this.translate("task_action")
 				}
 			},
 
@@ -471,10 +484,12 @@
 				this.pickerView = {
 					type: "type",
 					list: [{
-						name: "鍥哄畾"
-					}, {
-						name: "涓存椂"
-					}],
+							name: this.translate("fixed")
+						},
+						// {
+						// 	name: this.translate("temporary")
+						// },
+					],
 					value: [this.form.tasktype - 1],
 					top: e.target.offsetTop + 30,
 					right: 0,
@@ -507,7 +522,7 @@
 				try {
 					if (this.pathwayList.length == 1) {
 
-						showToast("蹇呴』鏈変竴涓洰鏍囩偣")
+						showToast(this.translate("at_least_one_target_point_added"))
 						return
 					}
 					if (item.taskID) {
@@ -528,7 +543,7 @@
 					})
 
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 
 			},
@@ -559,7 +574,10 @@
 			closeMenu() {
 				this.$refs.refPopupMenu.close()
 			},
-
+			translate(t) {
+				if (typeof this.$t == "function") return this.$t(`page.${t}`)
+				else return t;
+			},
 
 		}
 	}
@@ -609,6 +627,7 @@
 					padding-right: 5px;
 
 				}
+
 				.right-input {
 					text-align: right;
 					flex: 1;
@@ -628,11 +647,11 @@
 					margin-left: 20rpx;
 				}
 
-					.input {
-									margin-left: 20rpx;
-									width: 75rpx;
-									margin-right: 10rpx;
-								}
+				.input {
+					margin-left: 20rpx;
+					width: 75rpx;
+					margin-right: 10rpx;
+				}
 			}
 
 			.btn-del {
diff --git a/pages/teaching/index.vue b/pages/teaching/index.vue
index d412b91..6cae10d 100644
--- a/pages/teaching/index.vue
+++ b/pages/teaching/index.vue
@@ -183,7 +183,12 @@
 			} else if (this.mapOperationType == "public_teaching" || this.mapOperationType == "station_teaching") {
 
 				if (this.teachingStatus) {
-					showModal("宸茶褰曠殑璺緞灏嗕細琚垹闄ゃ�", "鏄惁瑕侀�鍑虹ず鏁欙紵").then((res) => {
+					showModal({
+						title: `${this.translate('ask_exit_teaching')}`,
+						content: `${this.translate('recorded_path_will_be_deleted')}`,
+						confirmText: this.translate('yes'),
+						cancelText: this.translate('no'),
+					}).then((res) => {
 						if (res) {
 							this.mapOperationType = ""
 						}
@@ -286,7 +291,7 @@
 
 				} catch (ex) {
 
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 			},
 			async loadAgvState() {
@@ -294,7 +299,7 @@
 					const info = await getAgvState(this.ip)
 					return info
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 					return {}
 				}
 			},
@@ -303,7 +308,7 @@
 					const info = await stations(this.ip)
 					return info.station_list || []
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 					return []
 				}
 			},
@@ -369,7 +374,7 @@
 					}
 
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 
 			},
@@ -388,7 +393,7 @@
 						this.askTeachingBiDirection(this.teachingModeCur)
 					}
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 
 			},
@@ -407,7 +412,7 @@
 					}
 
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 			},
 			async clickTeachingStart() {
@@ -422,7 +427,12 @@
 
 						const curIndex = stationTeaching.findIndex((a) => a.src_dst == srcDst)
 						if (curIndex > -1) {
-							showModal("璇ョ珯鐐归棿宸叉湁绀烘暀璺緞锛岄噸鏂拌褰曚細瑕嗙洊涔嬪墠鐨勮矾寰勩�", "鏄惁瑕侀噸鏂拌褰曪紵").then(async (res) => {
+							showModal({
+								title: "鏄惁瑕侀噸鏂拌褰曪紵",
+								content: "璇ョ珯鐐归棿宸叉湁绀烘暀璺緞锛岄噸鏂拌褰曚細瑕嗙洊涔嬪墠鐨勮矾寰勩�",
+								confirmText: this.translate('yes'),
+								cancelText: this.translate('no'),
+							}).then(async (res) => {
 								if (res) {
 									try {
 										_this.teachingStatus = "teaching"
@@ -435,7 +445,7 @@
 										stationTeaching.splice(curIndex, 1)
 										_this.teachingStart("Stations")
 									} catch (ex) {
-										showError(ex)
+										showError(ex, this.translate('error'))
 									}
 
 								} else {
@@ -454,7 +464,7 @@
 					}
 
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 			},
 			clickTeachingEnd() {
@@ -467,7 +477,12 @@
 			},
 			clickTeachingReset() {
 				const _this = this
-				showModal("宸茶褰曠殑璺緞灏嗕細琚垹闄ゃ�", "鏄惁瑕侀噸鏂拌褰曪紵").then(async (res) => {
+				showModal({
+					title: `${this.translate('ask_exit_teaching')}`,
+					content: `${this.translate('recorded_path_will_be_deleted')}`,
+					confirmText: this.translate('yes'),
+					cancelText: this.translate('no'),
+				}).then(async (res) => {
 					if (res) {
 						try {
 							await delTeachingMode(_this.ip, [_this.teachingModeCur])
@@ -475,7 +490,7 @@
 							_this.teachingMode = await getTeachingMode(_this.ip)
 
 						} catch (ex) {
-							showError(ex)
+							showError(ex, this.translate('error'))
 						}
 					} else {
 						_this.teachingStatus = "save"
@@ -487,7 +502,7 @@
 
 				this.teachingStatus = "save"
 				try {} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 
 			},
@@ -639,7 +654,7 @@
 
 					}
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 			},
 			async clickNoCalibration() {
@@ -664,11 +679,16 @@
 
 					}
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 			},
 			askTeachingBiDirection(teachingMode) {
-				showModal("鏄惁灏嗗綋鍓嶇ず鏁欒矾绾胯缃负鍙屽悜?", "绀烘暀缁撴潫").then((res) => {
+				showModal({
+					title: "绀烘暀缁撴潫",
+					content: "鏄惁灏嗗綋鍓嶇ず鏁欒矾绾胯缃负鍙屽悜?",
+					confirmText: this.translate('yes'),
+					cancelText: this.translate('no'),
+				}).then((res) => {
 					if (res) {
 						teachingMode.bidirection = "1"
 					} else {
@@ -685,14 +705,17 @@
 					this.teachingMode = await getTeachingMode(this.ip)
 					this.teachingStatus = "end"
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 			},
 			clickBackTeaching() {
 				this.$refs.refPopupCalibration.close()
 				this.teachingStatus = ""
 			},
-
+			translate(t) {
+				if (typeof this.$t == "function") return this.$t(`page.${t}`)
+				else return t;
+			},
 		}
 	}
 </script>
diff --git a/pages/teaching/list.vue b/pages/teaching/list.vue
index 7d62a86..9230d0c 100644
--- a/pages/teaching/list.vue
+++ b/pages/teaching/list.vue
@@ -10,7 +10,8 @@
 						<view class="item-title">{{item.name}}</view>
 						<view class="item-text">{{getTeachingSrcDst(item)}}</view>
 						<template v-slot:right>
-							<view class="btn-del" @click="clickDelTeachingMode('Public',item)">鍒犻櫎</view>
+							<view class="btn-del" @click="clickDelTeachingMode('Public',item)">{{translate("delete")}}
+							</view>
 						</template>
 					</uni-swipe-action-item>
 					<uni-swipe-action-item class="list-item"
@@ -20,7 +21,8 @@
 						<view class="item-title">{{item.name}}</view>
 						<view class="item-text">{{getTeachingSrcDst(item)}}</view>
 						<template v-slot:right>
-							<view class="btn-del" @click="clickDelTeachingMode('Stations',item)">鍒犻櫎</view>
+							<view class="btn-del" @click="clickDelTeachingMode('Stations',item)">{{translate("delete")}}
+							</view>
 						</template>
 					</uni-swipe-action-item>
 				</uni-swipe-action>
@@ -131,7 +133,7 @@
 
 				} catch (ex) {
 
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 			},
 
@@ -143,7 +145,7 @@
 					this.teachingPublic = data?.Public || []
 					this.teachingStation = data?.Stations || []
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 			},
 			clickAddTeaching() {
@@ -219,7 +221,8 @@
 				const list = item.pos_list || []
 				let tip = ""
 				if (list.length > 1) {
-					tip = `(${Number(list[0].x).toFixed(2)},${Number(list[0].y).toFixed(2)}) (${Number(list[1].x).toFixed(2)},${Number(list[1].y).toFixed(2)})`
+					tip =
+						`(${Number(list[0].x).toFixed(2)},${Number(list[0].y).toFixed(2)}) (${Number(list[1].x).toFixed(2)},${Number(list[1].y).toFixed(2)})`
 				}
 				if (list.length == 1) {
 					tip = `(${Number(list[0].x).toFixed(2)},${Number(list[0].y).toFixed(2)})`
@@ -227,7 +230,12 @@
 				return tip
 			},
 			clickDelTeachingMode(mode, item) {
-				showModal("纭鍒犻櫎绀烘暀", "璀﹀憡",true,"纭畾","鍙栨秷").then((res) => {
+				showModal({
+					title: "",
+					content: `${this.translate('ask_deleting_teaching')}`,
+					confirmText: this.translate('ok'),
+					cancelText: this.translate('cancel'),
+				}).then((res) => {
 					if (res) {
 						this.deleteTeachingMode(mode, item)
 					}
@@ -261,11 +269,14 @@
 					}
 
 				} catch (ex) {
-					showError(ex)
+					showError(ex, this.translate('error'))
 				}
 
 			},
-
+			translate(t) {
+				if (typeof this.$t == "function") return this.$t(`page.${t}`)
+				else return t;
+			},
 
 
 		}
@@ -363,7 +374,7 @@
 
 					.item-title {
 						font-size: 32rpx;
-						padding:  10rpx 20rpx;
+						padding: 10rpx 20rpx;
 					}
 
 
diff --git a/pages/vehicle/index.vue b/pages/vehicle/index.vue
deleted file mode 100644
index 02409e3..0000000
--- a/pages/vehicle/index.vue
+++ /dev/null
@@ -1,8 +0,0 @@
-<template>
-</template>
-
-<script>
-</script>
-
-<style>
-</style>
\ No newline at end of file
diff --git a/pages/version/app-update.vue b/pages/version/app-update.vue
new file mode 100644
index 0000000..8535134
--- /dev/null
+++ b/pages/version/app-update.vue
@@ -0,0 +1,313 @@
+<template>
+	<view class="pages-version-app-update">
+		<view>
+			<a-button type="primary" class="max-button" :disabled="updating"
+				@click="clickCheckUpdate">{{translate('check_update')}}</a-button>
+
+		</view>
+		<view class="group" v-if="newPack.version">
+			<view class="item big-text ">
+				{{translate('updatable_version')}}:{{newPack.fileTime}}_{{newPack.version}}
+			</view>
+			<view class="item gray-text" v-if="newPack.size">
+				{{newPack.size}}
+			</view>
+			<view class="item">
+				{{translate('version_info')}}:{{newPack.info}}
+			</view>
+			<view class="item">
+				{{translate('current_version')}}:{{appVersion.date}}_{{appVersion.ver}}
+			</view>
+		</view>
+		<view class="no-version gray-text" v-else>
+			<uni-icons type="upload-filled" size="150" color="#888"></uni-icons>
+			{{ translate('current_version')}}:{{appVersion.date}}_{{appVersion.ver}}
+		</view>
+
+		<view class="button-group" v-if="newPack.version">
+			<a-button type="primary" class="button" :disabled="updating"
+				@click="clickNowUpdate">{{translate('update_immediately')}}</a-button>
+			<cmdProgress v-if="updating" :percent="percentage"
+				stroke-color="linear-gradient(to right, #ef32d9, #89fffd)">
+			</cmdProgress>
+		</view>
+
+	</view>
+</template>
+<script>
+	import {
+		session,
+		showToast,
+		showModal,
+		showError
+	} from "@/comm/utils.js"
+	import {
+		Button
+	} from 'antd-mobile-vue-next'
+	import {
+		getAppVersion,
+		getServerVersion
+	} from "@/api/version.js"
+	import cmdProgress from "@/components/cmd-progress/index.vue"
+	export default {
+		name: "PagesVersionAppUpdate",
+		components: {
+			'a-button': Button,
+			cmdProgress
+		},
+		data() {
+			return {
+				appVersion: {
+					date: "",
+					ver: ""
+				},
+				newPack: {
+					version: "",
+					fileName: "",
+					size: "",
+					info: "",
+					fileTime: "",
+				},
+				percentage: 0, //涓嬭浇杩涘害
+				updating: false
+
+			}
+		},
+		onLoad(option) {
+
+
+			uni.setNavigationBarTitle({
+				title: this.translate('version_update')
+			})
+			this.loadData()
+
+		},
+		computed: {
+			newVersionSize() {
+				const packSize = this.newPack?.size || 0
+				let size = 0
+				if (packSize < 1024 * 1024) {
+					size = Math.round(packSize * 100 / 1024)
+					return `${size/100} KB`
+				} else if (packSize < 1024 * 1024 * 1024) {
+					size = Math.round(packSize * 100 / (1024 * 1024))
+					return `${size/100} MB`
+				} else {
+					size = Math.round(packSize * 100 / (1024 * 1024 * 1024))
+					return `${size/100} GB`
+				}
+			}
+
+		},
+		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];
+					});
+				});
+			},
+			loadData() {
+				const _this = this
+				// #ifdef APP-PLUS
+				plus.runtime.getProperty(plus.runtime.appid, (info) => {
+					// console.log(info);
+					const verStr = info.version || "";
+					const arVer = verStr.split("_")
+					if (arVer.length > 1) {
+						_this.setData({
+							appVersion: {
+								date: arVer[0],
+								ver: arVer[1]
+							}
+						})
+					} else if (arVer.length == 1) {
+						_this.setData({
+							appVersion: {
+								date: "",
+								ver: arVer[0]
+							}
+						})
+
+					}
+				});
+				//#endif
+			},
+			updateAPP(fileName, ver) {
+				let _this = this;
+				let sys = uni.getSystemInfoSync().platform //妫�煡绯荤粺
+
+				downloadUpdatePackage(fileName, (path) => {
+					_this.updating = false
+					plus.runtime.openFile(result.tempFilePath); //閫夋嫨杞欢鎵撳紑鏂囦欢
+
+				}, (res) => {
+					console.log('涓嬭浇杩涘害' + res.progress);
+					_this.percentage = res.progress
+				}, (err) => {
+					showToast(this.translate('update_fail'));
+					_this.updating = false
+				})
+
+			},
+			async checkAppVersion(version) {
+				try {
+					const listVer = await getAppVersion() || []
+					const appVer = listVer[0] || {}
+					const fileName = appVer.file_name || ""
+					if (fileName) {
+						var isver = this.compareVersion({
+							date: appVer.file_time,
+							ver: appVer.version
+						}, version);
+						if (isver) {
+							this.newPack.version = appVer.version
+							this.newPack.fileName = fileName
+							this.newPack.fileTime = appVer.file_time
+							this.newPack.size = appVer.size || ""
+							this.newPack.info = appVer.info
+						} else {
+							this.newPack = {
+								version: "",
+								fileName: "",
+								size: "",
+								info: "",
+								fileTime: "",
+
+							}
+							showToast(this.translate("is_latest_version"))
+							console.log('褰撳墠宸叉槸鏈�柊鐗堟湰')
+						}
+					}
+				} catch (ex) {
+					showError(ex)
+					this.newPack = {
+						version: "",
+						fileName: "",
+						size: "",
+						info: "",
+						fileTime: "",
+					}
+				}
+			},
+			compareVersion(curV, reqV) {
+				if (curV.date == reqV.date) {
+					return curV.ver > reqV.ver
+				}
+				return curV.date > reqV.date
+			},
+			clickCheckUpdate() {
+				this.checkAppVersion(this.appVersion)
+			},
+
+			clickNowUpdate() {
+				this.updateAPP(this.newPack.fileName)
+			},
+			translate(t) {
+				if (typeof this.$t == "function") return this.$t(`page.${t}`)
+				else return t;
+			},
+		}
+	}
+</script>
+
+<style lang="scss">
+	.pages-version-app-update {
+		display: flex;
+		width: 750rpx;
+		height: 100vh;
+		flex-direction: column;
+
+		.group {
+			width: calc(100% - 40rpx);
+			// border: 2rpx solid #ccc;
+			// background-color: #fff;
+			border-radius: 20rpx;
+			margin: 20rpx;
+			padding: 0 20rpx;
+			display: flex;
+			flex-direction: column;
+
+			.item {
+				width: 100%;
+				padding: 20rpx 10rpx;
+				display: flex;
+				flex-direction: row;
+				align-items: center;
+
+				.right {
+					flex: 1;
+					text-align: right;
+					color: #888;
+				}
+			}
+
+
+		}
+
+		.tip {
+			color: #888;
+			padding: 10rpx;
+			margin-left: 50rpx;
+		}
+
+		.gray-text {
+			color: #888;
+		}
+
+		.big-text {
+			font-size: 40rpx;
+		}
+
+		.no-version {
+			margin: auto;
+			display: flex;
+			flex-direction: column;
+		}
+
+		.button-group {
+			display: flex;
+			justify-content: center;
+			align-items: center;
+			flex-direction: column;
+			font-size: 30rpx !important;
+
+
+		}
+
+		.max-button {
+			margin: auto;
+			margin-top: 20rpx;
+			margin-bottom: 20rpx;
+			border-radius: 40rpx;
+			height: 80rpx;
+			line-height: 60rpx;
+			width: 600rpx;
+		}
+
+		.button {
+			margin: auto;
+			margin-top: 20rpx;
+			border-radius: 10rpx;
+			height: 50rpx;
+			line-height: 32rpx;
+			width: 400rpx;
+		}
+
+
+	}
+</style>
\ No newline at end of file
diff --git a/pages/version/car-program-download.vue b/pages/version/car-program-download.vue
new file mode 100644
index 0000000..ac2c505
--- /dev/null
+++ b/pages/version/car-program-download.vue
@@ -0,0 +1,347 @@
+<template>
+	<view class="pages-version-car-program-download">
+		<view>
+			<a-button type="primary" class="max-button" :disabled="downloading"
+				@click="clickCheckUpdate">{{translate('check_update')}}</a-button>
+
+		</view>
+
+		<view class="group" v-if="newPack.version">
+			<view class="item big-text ">
+				{{translate('updatable_version')}}:{{newPack.file_time}}.{{newPack.version}}
+			</view>
+			<view class="item gray-text">
+				{{newPack.size}}
+			</view>
+
+			<view class="item">
+				{{translate('current_version')}}:{{svrVersion.date}}_{{svrVersion.ver}}
+			</view>
+		</view>
+		<view class="no-version gray-text" v-else>
+			<uni-icons type="upload-filled" size="150" color="#888"></uni-icons>
+			{{translate('current_version')}}:{{svrVersion.date}}_{{svrVersion.ver}}
+		</view>
+
+		<view class="button-group" v-if="newPack.version">
+			<a-button type="primary" class="button" :disabled="downloading"
+				@click="clickNowUpdate">{{translate('download_immediately')}}</a-button>
+			<cmdProgress v-if="downloading" :percent="percentage"
+				stroke-color="linear-gradient(to right, #ef32d9, #89fffd)">
+			</cmdProgress>
+		</view>
+
+	</view>
+</template>
+<script>
+	import {
+		session,
+		showToast,
+		showModal
+	} from "@/comm/utils.js"
+	import {
+		Button
+	} from 'antd-mobile-vue-next'
+	import {
+		getServerVersion,
+		downloadUpdatePackage
+	} from "@/api/version.js"
+	import TaskInit from "@/comm/extend.js"
+	import cmdProgress from "@/components/cmd-progress/index.vue"
+	export default {
+		name: "PagesVersionCarProgramDownload",
+		components: {
+			'a-button': Button,
+			cmdProgress
+		},
+		data() {
+			return {
+				ip: "",
+				svrVersion: {
+					date: "",
+					ver: ""
+				},
+				newPack: {
+					version: "",
+					fileName: "",
+					size: "",
+					info: "",
+					fileTime: "",
+				},
+				percentage: 0, //涓嬭浇杩涘害
+				downloading: false
+
+			}
+		},
+		onLoad(option) {
+
+			this.ip = option.ip || ""
+			uni.setNavigationBarTitle({
+				title: this.translate('version_update')
+			})
+			this.loadData()
+
+		},
+		computed: {
+			newVersionSize() {
+				const packSize = this.newPack?.size || 0
+				let size = 0
+				if (packSize < 1024) {
+					size = Math.round(packSize * 100)
+					return `${size/100} KB`
+				} else if (packSize < 1024 * 1024) {
+					size = Math.round(packSize * 100 / 1024)
+					return `${size/100} MB`
+				} else {
+					size = Math.round(packSize * 100 / (1024 * 1024 * 1024))
+					return `${size/100} GB`
+				}
+			}
+
+		},
+		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];
+					});
+				});
+			},
+			loadData() {
+				const _this = this
+				// #ifdef APP-PLUS
+				plus.runtime.getProperty(plus.runtime.appid, (info) => {
+					// console.log(info);
+					const verStr = info.version || "";
+					const arVer = verStr.split("_")
+					if (arVer.length > 1) {
+						_this.setData({
+							svrVersion: {
+								date: arVer[0],
+								ver: arVer[1]
+							}
+						})
+					} else if (arVer.length == 1) {
+						_this.setData({
+							svrVersion: {
+								date: "",
+								ver: arVer[0]
+							}
+						})
+					
+					}
+
+
+				});
+				//#endif
+			},
+			updateAPP(fileName, ver) {
+				let _this = this;
+				let sys = uni.getSystemInfoSync().platform //妫�煡绯荤粺
+
+				downloadUpdatePackage(fileName, (path) => {
+					var pathArr = fileName.split(".")
+					const fileExt = pathArr.pop()
+					const fileName2 = `EasyGO_${ver}.${fileExt}`
+					TaskInit.fileUtils.clearSavedFiles("car_version")
+					TaskInit.fileUtils.moveToLocal(path, "car_version", fileName2).then((
+						res) => {
+						console.log("淇濆瓨鎴愬姛锛�, res)
+						showToast(this.translate('download_success'));
+					}).catch((err) => {
+						console.log("淇濆瓨澶辫触锛�, err)
+						showToast(_this.translate("failed_save_file"))
+					})
+					_this.downloading = false
+				}, (res) => {
+					console.log('涓嬭浇杩涘害' + res.progress);
+					_this.percentage = res.progress
+				}, (err) => {
+					showToast(this.translate('update_fail'));
+					_this.downloading = false
+				})
+			},
+
+			async checkAppVersion(version) {
+				try {
+
+					const listVer = await getServerVersion() || []
+					const svrVer = listVer[0] || {}
+					console.log("downloadUrl", svrVer)
+
+					const fileName = svrVer.file_name || ""
+					if (fileName) {
+						if (!svrVer.version) return
+							const newVer = `${svrVer.version }`
+						var isver = this.compareVersion(svrVer.version || "", version);
+						if (isver) {
+							this.newPack.version = svrVer.version
+							this.newPack.fileName = fileName
+							this.newPack.fileTime = svrVer.file_time
+							this.newPack.size = svrVer.size || ""
+							this.newPack.info =appVer.info
+						} else {
+							this.newPack = {
+								version: "",
+								fileName: "",
+								size: "",
+								info: "",
+								fileTime: "",
+							}
+							showToast(this.translate("is_latest_version"))
+							console.log('褰撳墠宸叉槸鏈�柊鐗堟湰')
+						}
+					}
+				} catch (ex) {
+					showError(ex)
+					this.newPack = {
+						version: "",
+						fileName: "",
+						size: "",
+						info: "",
+						fileTime: "",
+					}
+				}
+			},
+			compareVersion(curV, reqV) {
+				return curV > reqV
+				var arr1 = curV.toString().split('.');
+				var arr2 = reqV.toString().split('.');
+				//灏嗕袱涓増鏈彿鎷嗘垚鏁板瓧
+				var minL = Math.min(arr1.length, arr2.length);
+				var pos = 0; //褰撳墠姣旇緝浣�+				var diff = 0; //褰撳墠涓轰綅姣旇緝鏄惁鐩哥瓑
+				var flag = false;
+				//閫愪釜姣旇緝濡傛灉褰撳墠浣嶇浉绛夊垯缁х画姣旇緝涓嬩竴浣�+				while (pos < minL) {
+					diff = parseInt(arr1[pos]) - parseInt(arr2[pos]);
+					if (diff == 0) {
+						pos++;
+						continue;
+					} else if (diff > 0) {
+						flag = true;
+						break;
+					} else {
+						flag = false;
+						break;
+					}
+				}
+				return flag;
+			},
+
+			clickCheckUpdate(e) {
+
+				this.checkAppVersion(this.svrVersion)
+
+			},
+
+			clickNowUpdate() {
+				this.updateAPP(this.newPack.fileName)
+			},
+			translate(t) {
+				if (typeof this.$t == "function") return this.$t(`page.${t}`)
+				else return t;
+			},
+		}
+	}
+</script>
+
+<style lang="scss">
+	.pages-version-car-program-download {
+		display: flex;
+		width: 750rpx;
+		height: 100vh;
+		flex-direction: column;
+
+		.group {
+			width: calc(100% - 40rpx);
+			// border: 2rpx solid #ccc;
+			// background-color: #fff;
+			border-radius: 20rpx;
+			margin: 20rpx;
+			padding: 0 20rpx;
+			display: flex;
+			flex-direction: column;
+
+			.item {
+				width: 100%;
+				padding: 20rpx 10rpx;
+				display: flex;
+				flex-direction: row;
+				align-items: center;
+
+				.right {
+					flex: 1;
+					text-align: right;
+					color: #888;
+				}
+			}
+
+
+		}
+
+		.tip {
+			color: #888;
+			padding: 10rpx;
+			margin-left: 50rpx;
+		}
+
+		.gray-text {
+			color: #888;
+		}
+
+		.big-text {
+			font-size: 40rpx;
+		}
+
+		.no-version {
+			margin: auto;
+			display: flex;
+			flex-direction: column;
+		}
+
+		.button-group {
+			display: flex;
+			justify-content: center;
+			align-items: center;
+			flex-direction: column;
+			font-size: 30rpx !important;
+
+
+		}
+
+		.max-button {
+			margin: auto;
+			margin-top: 20rpx;
+			margin-bottom: 20rpx;
+			border-radius: 40rpx;
+			height: 80rpx;
+			line-height: 60rpx;
+			width: 600rpx;
+		}
+
+		.button {
+			margin: auto;
+			margin-top: 20rpx;
+			border-radius: 10rpx;
+			height: 50rpx;
+			line-height: 32rpx;
+			width: 400rpx;
+		}
+
+	}
+</style>
\ No newline at end of file
diff --git a/pages/version/car-program-upload.vue b/pages/version/car-program-upload.vue
new file mode 100644
index 0000000..8176d58
--- /dev/null
+++ b/pages/version/car-program-upload.vue
@@ -0,0 +1,399 @@
+<template>
+	<view class="pages-version-car-program-upload">
+		<view>
+			<a-button type="primary" class="max-button" :disabled="updating"
+				@click="clickCheckUpdate">{{translate('check_update')}}</a-button>
+
+		</view>
+		<view class="group" v-if="newPack.version">
+			<view class="item big-text ">
+				{{translate('updatable_version')}}:{{newPack.version}}
+			</view>
+			<view class="item gray-text">
+				{{newVersionSize}}
+			</view>
+			<view class="item">
+				{{translate('current_version')}}:{{shellVersion}}
+			</view>
+		</view>
+		<view class="no-version gray-text" v-else>
+			<uni-icons type="upload-filled" size="150" color="#888"></uni-icons>
+			{{ translate('current_version')}}:{{shellVersion}}
+		</view>
+		<!-- 		<template v-if="verFileList.length  >0">
+			<view class="group ">
+				{{translate('current_version')}}:{{shellVersion}}
+			</view>
+			<view class="group big-text">
+				{{translate('updatable_version')}}:
+			</view>
+		</template>
+
+		<view class="list">
+			<label class="item" v-for="item in verFileList" :key="item.version" @click="clickVersionItem(item)">
+				<view class="name" :class="item.isNew ? '':'disabled'">{{item.version}}</view>
+				<uni-icons v-if="newPack.version == item.version" type="checkmarkempty" size="20"
+					color="#0055ff"></uni-icons>
+				<progress v-if="updating && newPack.version == item.version" class="progress" :percent="percentage"
+					backgroundColor="#fff" activeColor="#10AEFF" stroke-width="3" />
+			</label>
+		</view>
+		<view class="no-version gray-text" v-if="verFileList.length == 0">
+			<uni-icons type="upload-filled" size="150" color="#888"></uni-icons>
+			{{translate('current_version')}}:{{shellVersion}}
+		</view> -->
+		<view class="button-group" v-if="newPack.version">
+			<a-button type="primary" class="button" :disabled="updating"
+				@click="clickNowUpdate">{{translate('update_immediately')}}</a-button>
+
+		</view>
+
+	</view>
+</template>
+<script>
+	import {
+		session,
+		showToast,
+		showModal,
+		showError
+	} from "@/comm/utils.js"
+	import TaskInit from "@/comm/extend.js"
+	import {
+		Button
+	} from 'antd-mobile-vue-next'
+	import {
+		getShellVersion,
+		shellUpgrade
+	} from "@/api/vehicle.js"
+	import cmdProgress from "@/components/cmd-progress/index.vue"
+	export default {
+		name: "PagesVersionCarProgramUpload",
+		components: {
+			'a-button': Button,
+			cmdProgress
+		},
+		data() {
+			return {
+				ip: "",
+				shellVersion: "",
+				newPack: {
+					version: "",
+					filePath: "",
+					size: 0
+				},
+				percentage: 0, //涓嬭浇杩涘害
+				updating: false,
+				verFileList: []
+			}
+		},
+		onLoad(option) {
+
+			this.ip = option.ip || ""
+			uni.setNavigationBarTitle({
+				title: this.translate('version_update')
+			})
+			this.loadData()
+
+		},
+		computed: {
+			newVersionSize() {
+				const packSize = this.newPack?.size || 0
+				console.log(packSize)
+				let size = 0
+				if (packSize < 1024 * 1024) {
+					size = Math.round(packSize * 100 / 1024)
+					return `${size/100} KB`
+				} else if (packSize < 1024 * 1024 * 1024) {
+					size = Math.round(packSize * 100 / (1024 * 1024))
+					return `${size/100} MB`
+				} else {
+					size = Math.round(packSize * 100 / (1024 * 1024 * 1024))
+					return `${size/100} GB`
+				}
+			}
+
+		},
+		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() {
+				this.checkVersion()
+
+			},
+			getVersionFromFileName(fileName) {
+				if (!fileName.trim()) {
+					return {
+						date: "",
+						ver: ""
+					}
+				}
+				let versionInfos = fileName.trim().split('_');
+				let date = versionInfos[1]
+				let version2 = versionInfos[2]
+				let versionInfos2 = version2.split('.');
+				versionInfos2.pop()
+				return {
+					date,
+					ver: versionInfos2.join(".")
+				}
+			},
+			async loadServerVersionFile() {
+				try {
+					var verName = ""
+					var verPath = ""
+					var fileList = []
+					var listVer = []
+					try {
+						fileList = await TaskInit.fileUtils.listSavedFiles('car_version')
+					} catch (ex) {
+						console.log(ex)
+					}
+					this.verFileList = []
+					let versionInfos = this.shellVersion.trim().split('_');
+					let oldDate = ""
+					let oldVer = ""
+					if (versionInfos.length == 2) {
+						oldDate = versionInfos[0]
+						oldVer = versionInfos[1]
+					}
+					for (let i in fileList) {
+						const ele = fileList[i]
+						const newVer = this.getVersionFromFileName(ele.name)
+						const curVer = `${newVer.date ||""}_${newVer.ver ||""}`
+						const isVer = this.compareVersion(newVer, {
+							date: oldDate,
+							ver: oldVer
+						});
+						if (isVer) {
+							ele.isNew = true
+							ele.size = await TaskInit.fileUtils.getSavedFileSize(ele.fullPath)
+						}
+						ele.version = curVer
+						listVer.push(ele)
+					}
+					listVer.sort((a, b) => {
+						return a.version > b.version
+					});
+					this.verFileList = listVer
+					if (listVer.length > 0) {
+						const verItem = listVer[0]
+						if (verItem.isNew) {
+							this.newPack = {
+								version: verItem.version,
+								filePath: verItem.fullPath,
+								size: verItem.size,
+							}
+
+						}
+
+					}
+
+				} catch (ex) {
+
+				}
+			},
+			compareVersion(curV, reqV) {
+
+				if (curV.date == reqV.date) {
+					return curV.ver > reqV.ver
+				}
+				return parseInt(curV.date) > parseInt(reqV.date)
+			},
+			async updateShellVersion(filePath) {
+				const _this = this;
+				this.updating = true
+				shellUpgrade(this.ip, filePath, (res) => {
+
+					_this.updating = false
+					_this.shellVersion = _this.newPack.version
+					this.newPack = {
+						version: "",
+						filePath: "",
+						size: 0
+					}
+					this.loadServerVersionFile();
+					showToast(this.translate('update_success'));
+				}, (res) => {
+					_this.percentage = res.progress
+				}, (err) => {
+					showToast(this.translate('update_fail'));
+					_this.updating = false
+				})
+			},
+			async checkVersion(version) {
+				try {
+					this.newPack = {
+						version: "",
+						filePath: "",
+						size: 0
+					}
+					const verInfo = await getShellVersion(this.ip)
+					this.shellVersion = verInfo?.software_version || ""
+					await this.loadServerVersionFile();
+
+				} catch (ex) {
+					showError(ex)
+				}
+			},
+			clickVersionItem(item) {
+				if (item.isNew && !this.updating) {
+					this.newPack = {
+						version: item.version,
+						filePath: item.fullPath,
+						size: item.size
+					}
+				}
+
+			},
+			clickCheckUpdate(e) {
+				this.checkVersion()
+			},
+
+			clickNowUpdate() {
+				this.updateShellVersion(this.newPack.filePath)
+			},
+			translate(t) {
+				if (typeof this.$t == "function") return this.$t(`page.${t}`)
+				else return t;
+			},
+		}
+	}
+</script>
+
+<style lang="scss">
+	.pages-version-car-program-upload {
+		display: flex;
+		width: 750rpx;
+		height: 100vh;
+		flex-direction: column;
+
+		.list {
+			overflow-y: auto;
+			display: flex;
+			flex-direction: column;
+			width: calc(100% - 20rpx);
+			margin: 10rpx;
+			padding: 0 20rpx;
+			max-height: 60vh;
+
+			.item {
+				width: calc(100% - 20rpx);
+				padding: 20rpx;
+				display: flex;
+				flex-direction: row;
+				margin: 10rpx 10rpx;
+				background-color: #fff;
+				border-radius: 10rpx;
+				position: relative;
+
+				.progress {
+					position: absolute;
+					bottom: 5rpx;
+					left: 0;
+					right: 0;
+				}
+
+				.name {
+					flex: 1;
+				}
+
+			}
+
+			.disabled {
+				color: gray;
+			}
+
+
+		}
+
+		.group {
+			width: calc(100% - 40rpx);
+			// border: 2rpx solid #ccc;
+			// background-color: #fff;
+			border-radius: 20rpx;
+			margin: 20rpx;
+			padding: 0 20rpx;
+			display: flex;
+			flex-direction: column;
+
+			.item {
+				width: 100%;
+				padding: 20rpx;
+				display: flex;
+				flex-direction: row;
+				align-items: center;
+
+			}
+
+
+		}
+
+		.tip {
+			color: #888;
+			padding: 10rpx;
+			margin-left: 50rpx;
+		}
+
+		.gray-text {
+			color: #888;
+		}
+
+		.big-text {
+			font-size: 40rpx;
+		}
+
+		.no-version {
+			margin: auto;
+			display: flex;
+			flex-direction: column;
+		}
+
+		.button-group {
+			display: flex;
+			justify-content: center;
+			align-items: center;
+			flex-direction: column;
+			font-size: 30rpx !important;
+
+		}
+
+		.max-button {
+			margin: auto;
+			margin-top: 20rpx;
+			margin-bottom: 20rpx;
+			border-radius: 40rpx;
+			height: 80rpx;
+			line-height: 60rpx;
+			width: 600rpx;
+		}
+
+		.button {
+			margin: auto;
+			margin-top: 20rpx;
+			border-radius: 10rpx;
+			height: 56rpx;
+			line-height: 32rpx;
+			width: 400rpx;
+		}
+
+	}
+</style>
\ No newline at end of file
diff --git a/uni_modules/uni-datetime-picker/changelog.md b/uni_modules/uni-datetime-picker/changelog.md
new file mode 100644
index 0000000..1eee75e
--- /dev/null
+++ b/uni_modules/uni-datetime-picker/changelog.md
@@ -0,0 +1,173 @@
+## 2.2.42锛�025-08-20锛�+- 淇 datetime-picker 灏忕▼搴忔牱寮忚鍛�+## 2.2.41锛�025-08-20锛�+- 淇 uni-datetime-picker缁勪欢鏃堕棿瀵规瘮鎶ラ敊闂
+## 2.2.40锛�025-04-14锛�+- 淇 缁戝畾瀛楃涓插�鐨勬椂锛屾棩鍘嗛潰鏉块�涓姸鎬佹湭閲嶇疆鍒伴粯璁ゅ�鐨勯棶棰�+## 2.2.39锛�025-04-14锛�+- 淇 鍦�iOS 寰俊灏忕▼搴忎笂type='daterange'鏃讹紝浼犲叆'YYYY-MM-DD'鏍煎紡涓嶇敓鏁堢殑闂
+
+## 2.2.38锛�024-10-15锛�+- 淇 寰俊灏忕▼搴忎腑鐨刧etSystemInfo璀﹀憡
+## 2.2.35锛�024-09-21锛�+- 淇 娌℃湁閫変腑鏃ユ湡鏃剁偣鍑荤‘瀹氱洿鎺ユ姤閿欑殑Bug [璇︽儏](https://ask.dcloud.net.cn/question/198168)
+## 2.2.34锛�024-04-24锛�+- 鏂板 鏃ユ湡鐐瑰嚮浜嬩欢锛屽湪鐐瑰嚮鏃ユ湡鏃朵細瑙﹀彂璇ヤ簨浠躲�
+## 2.2.33锛�024-04-15锛�+- 淇 鎶栭煶灏忕▼搴忎簨浠朵紶閫掑け鏁坆ug
+## 2.2.32锛�024-02-20锛�+- 淇 鏃ュ巻鐨刢lose浜嬩欢瑙﹀彂寮傚父鐨刡ug [璇︽儏](https://github.com/dcloudio/uni-ui/issues/844)
+## 2.2.31锛�024-02-20锛�+- 淇 h5骞冲彴 鍙宠竟鏃ュ巻鐨勬湀浠介粯璁�1鐨刡ug [璇︽儏](https://github.com/dcloudio/uni-ui/issues/841)
+## 2.2.30锛�024-01-31锛�+- 淇 闅愯棌鈥滅鈥濇椂锛屽湪IOS15鍙婁互涓嬬増鏈椂鍑虹幇 缁撴潫鏃堕棿鍦ㄥ紑濮嬫椂闂翠箣鍓�鐨刡ug [璇︽儏](https://github.com/dcloudio/uni-ui/issues/788)
+## 2.2.29锛�024-01-20锛�+- 鏂板 show浜嬩欢锛屽脊绐楀脊鍑烘椂瑙﹀彂璇ヤ簨浠�[璇︽儏](https://github.com/dcloudio/uni-app/issues/4694)
+## 2.2.28锛�024-01-18锛�+- 鍘婚櫎 noChange浜嬩欢锛屽綋杩涜鏃ユ湡鑼冨洿閫夋嫨鏃讹紝鑻ュ彧閫変簡涓�ぉ锛屽垯寮�缁撴潫鏃ユ湡閮戒负鍚屼竴澶�[璇︽儏](https://github.com/dcloudio/uni-ui/issues/815)
+## 2.2.27锛�024-01-10锛�+- 浼樺寲 澧炲姞noChange浜嬩欢锛屽綋杩涜鏃ユ湡鑼冨洿閫夋嫨鏃讹紝鑻ユ湁绌哄�锛屽垯瑙﹀彂璇ヤ簨浠�[璇︽儏](https://github.com/dcloudio/uni-ui/issues/815)
+## 2.2.26锛�024-01-08锛�+- 淇 瀛楄妭灏忕▼搴忔椂闂撮�鎷╄寖鍥村櫒澶辨晥闂 [璇︽儏](https://github.com/dcloudio/uni-ui/issues/834)
+## 2.2.25锛�023-10-18锛�+- 淇 PC绔垵娆′慨鏀规椂闂达紝寮�鏃堕棿鏈洿鏂扮殑Bug [璇︽儏](https://github.com/dcloudio/uni-ui/issues/737)
+## 2.2.24锛�023-06-02锛�+- 淇 閮ㄥ垎鎯呭喌淇敼鏃堕棿锛屽紑濮嬨�缁撴潫鏃堕棿鏄剧ず寮傚父鐨凚ug [璇︽儏](https://ask.dcloud.net.cn/question/171146)
+- 浼樺寲 褰撳墠鏈堝彲浠ラ�鎷╀笂鏈堛�涓嬫湀鐨勬棩鏈熺殑Bug
+## 2.2.23锛�023-05-02锛�+- 淇 閮ㄥ垎鎯呭喌淇敼鏃堕棿锛屽紑濮嬫椂闂存湭鏇存柊鐨凚ug [璇︽儏](https://github.com/dcloudio/uni-ui/issues/737)
+- 淇 閮ㄥ垎骞冲彴鍙婅澶囩涓�鐐瑰嚮鏃犳硶鏄剧ず寮规鐨凚ug
+- 淇 ios 鏃ユ湡鏍煎紡鏈ˉ闆舵樉绀哄強浣跨敤寮傚父鐨凚ug [璇︽儏](https://ask.dcloud.net.cn/question/162979)
+## 2.2.22锛�023-03-30锛�+- 淇 鏃ュ巻 picker 淇敼骞存湀鍚庯紝鑷姩閫変腑褰撴湀1鏃ョ殑Bug [璇︽儏](https://ask.dcloud.net.cn/question/165937)
+- 淇 灏忕▼搴忕 浣庣増鏈�ios NaN鐨凚ug [璇︽儏](https://ask.dcloud.net.cn/question/162979)
+## 2.2.21锛�023-02-20锛�+- 淇 firefox 娴忚鍣ㄦ樉绀哄尯鍩熺偣鍑绘棤娉曟媺璧锋棩鍘嗗脊妗嗙殑Bug [璇︽儏](https://ask.dcloud.net.cn/question/163362)
+## 2.2.20锛�023-02-17锛�+- 浼樺寲 鍊间负绌轰緷鐒堕�涓綋澶╅棶棰�+- 浼樺寲 鎻愪緵 default-value 灞炴�鏀寔閰嶇疆閫夋嫨鍣ㄦ墦寮�椂榛樿鏄剧ず鐨勬椂闂�+- 浼樺寲 闈炶寖鍥撮�鎷╂湭閫夋嫨鏃ユ湡鏃堕棿锛岀偣鍑荤‘璁ゆ寜閽�涓綋鍓嶆棩鏈熸椂闂�+- 浼樺寲 瀛楄妭灏忕▼搴忔棩鏈熸椂闂磋寖鍥撮�鎷╋紝搴曢儴鏃ユ湡鎹㈣鐨凚ug
+## 2.2.19锛�023-02-09锛�+- 淇 2.2.18 寮曡捣鑼冨洿閫夋嫨閰嶇疆 end 閫夋嫨鏃犳晥鐨凚ug [璇︽儏](https://github.com/dcloudio/uni-ui/issues/686)
+## 2.2.18锛�023-02-08锛�+- 淇 绉诲姩绔寖鍥撮�鎷ヽhange浜嬩欢瑙﹀彂寮傚父鐨凚ug [璇︽儏](https://github.com/dcloudio/uni-ui/issues/684)
+- 浼樺寲 PC绔緭鍏ユ棩鏈熸牸寮忛敊璇椂杩斿洖褰撳墠鏃ユ湡鏃堕棿
+- 浼樺寲 PC绔緭鍏ユ棩鏈熸椂闂磋秴鍑�start銆乪nd 闄愬埗鐨凚ug
+- 浼樺寲 绉诲姩绔棩鏈熸椂闂磋寖鍥寸敤娉曟椂闂村睍绀轰笉瀹屾暣闂
+## 2.2.17锛�023-02-04锛�+- 淇 灏忕▼搴忕缁戝畾 Date 绫诲瀷鎶ラ敊鐨凚ug [璇︽儏](https://github.com/dcloudio/uni-ui/issues/679)
+- 淇 vue3 time-picker 鏃犳硶鏄剧ず缁戝畾鏃跺垎绉掔殑Bug
+## 2.2.16锛�023-02-02锛�+- 淇 瀛楄妭灏忕▼搴忔姤閿欑殑Bug
+## 2.2.15锛�023-02-02锛�+- 淇 鏌愪簺鎯呭喌鍒囨崲鏈堜唤閿欒鐨凚ug
+## 2.2.14锛�023-01-30锛�+- 淇 鏌愪簺鎯呭喌鍒囨崲鏈堜唤閿欒鐨凚ug [璇︽儏](https://ask.dcloud.net.cn/question/162033)
+## 2.2.13锛�023-01-10锛�+- 淇 澶氭鍔犺浇缁勪欢閫犳垚鍐呭瓨鍗犵敤鐨凚ug
+## 2.2.12锛�022-12-01锛�+- 淇 vue3 涓�i18n 鍥介檯鍖栧垵濮嬪�涓嶆纭殑Bug
+## 2.2.11锛�022-09-19锛�+- 淇 鏀粯瀹濆皬绋嬪簭鏍峰紡閿欎贡鐨凚ug [璇︽儏](https://github.com/dcloudio/uni-app/issues/3861)
+## 2.2.10锛�022-09-19锛�+- 淇 鍙嶅悜閫夋嫨鏃ユ湡鑼冨洿锛屾棩鏈熸樉绀哄紓甯哥殑Bug [璇︽儏](https://ask.dcloud.net.cn/question/153401?item_id=212892&rf=false)
+## 2.2.9锛�022-09-16锛�+- 鍙互浣跨敤 uni-scss 鎺у埗涓婚鑹�+## 2.2.8锛�022-09-08锛�+- 淇 close浜嬩欢鏃犳晥鐨凚ug
+## 2.2.7锛�022-09-05锛�+- 淇 绉诲姩绔�maskClick 鏃犳晥鐨凚ug [璇︽儏](https://ask.dcloud.net.cn/question/140824)
+## 2.2.6锛�022-06-30锛�+- 浼樺寲 缁勪欢鏍峰紡锛岃皟鏁翠簡缁勪欢鍥炬爣澶у皬銆侀珮搴︺�棰滆壊绛夛紝涓巙ni-ui椋庢牸淇濇寔涓�嚧
+## 2.2.5锛�022-06-24锛�+- 淇 鏃ュ巻椤堕儴骞存湀鍙婂簳閮ㄧ‘璁ゆ湭鍥介檯鍖栫殑Bug
+## 2.2.4锛�022-03-31锛�+- 淇 Vue3 涓嬪姩鎬佽祴鍊�鍗曢�绫诲瀷鏈搷搴旂殑Bug
+## 2.2.3锛�022-03-28锛�+- 淇 Vue3 涓嬪姩鎬佽祴鍊兼湭鍝嶅簲鐨凚ug
+## 2.2.2锛�021-12-10锛�+- 淇 clear-icon 灞炴�鍦ㄥ皬绋嬪簭骞冲彴涓嶇敓鏁堢殑Bug
+## 2.2.1锛�021-12-10锛�+- 淇 鏃ユ湡鑼冨洿閫夊湪灏忕▼搴忓钩鍙帮紝蹇呴』澶氱偣鍑讳竴娆℃墠鑳藉彇娑堥�涓姸鎬佺殑Bug
+## 2.2.0锛�021-11-19锛�+- 浼樺寲 缁勪欢UI锛屽苟鎻愪緵璁捐璧勬簮 [璇︽儏](https://uniapp.dcloud.io/component/uniui/resource)
+- 鏂囨。杩佺Щ [https://uniapp.dcloud.io/component/uniui/uni-datetime-picker](https://uniapp.dcloud.io/component/uniui/uni-datetime-picker)
+## 2.1.5锛�021-11-09锛�+- 鏂板 鎻愪緵缁勪欢璁捐璧勬簮锛岀粍浠舵牱寮忚皟鏁�+## 2.1.4锛�021-09-10锛�+- 淇 hide-second 鍦ㄧЩ鍔ㄧ鐨凚ug
+- 淇 鍗曢�璧嬮粯璁ゅ�鏃讹紝璧嬪�鏃ユ湡鏈珮浜殑Bug
+- 淇 璧嬮粯璁ゅ�鏃讹紝绉诲姩绔湭姝g‘鏄剧ず鏃堕棿鐨凚ug
+## 2.1.3锛�021-09-09锛�+- 鏂板 hide-second 灞炴�锛屾敮鎸佸彧浣跨敤鏃跺垎锛岄殣钘忕
+## 2.1.2锛�021-09-03锛�+- 浼樺寲 鍙栨秷閫変腑鏃讹紙鑼冨洿閫夛級鐩存帴寮�涓嬩竴娆¢�鎷� 閬垮厤澶氱偣涓�
+- 浼樺寲 绉诲姩绔敮鎸佹竻闄ゆ寜閽紝鍚屾椂鏀寔閫氳繃 ref 璋冪敤缁勪欢鐨�clear 鏂规硶
+- 浼樺寲 璋冩暣瀛楀彿澶у皬锛岀編鍖栨棩鍘嗙晫闈�+- 淇 鍥犲浗闄呭寲瀵艰嚧鐨�placeholder 澶辨晥鐨凚ug
+## 2.1.1锛�021-08-24锛�+- 鏂板 鏀寔鍥介檯鍖�+- 浼樺寲 鑼冨洿閫夋嫨鍣ㄥ湪 pc 绔繃瀹界殑闂
+## 2.1.0锛�021-08-09锛�+- 鏂板 閫傞厤 vue3
+## 2.0.19锛�021-08-09锛�+- 鏂板 鏀寔浣滀负 uni-forms 瀛愮粍浠剁浉鍏冲姛鑳�+- 淇 鍦�uni-forms 涓娇鐢ㄦ椂锛岄�鎷╂椂闂存姤 NAN 閿欒鐨凚ug
+## 2.0.18锛�021-08-05锛�+- 淇 type 灞炴�鍔ㄦ�璧嬪�鏃犳晥鐨凚ug
+- 淇 鈥樼‘璁も�鎸夐挳琚�tabbar 閬洊 bug
+- 淇 缁勪欢鏈祴鍊兼椂鑼冨洿閫夊乏銆佸彸鏃ュ巻鐩稿悓鐨凚ug
+## 2.0.17锛�021-08-04锛�+- 淇 鑼冨洿閫夋湭姝g‘鏄剧ず褰撳墠鍊肩殑Bug
+- 淇 h5 骞冲彴锛堢Щ鍔ㄧ锛夋姤閿�'cale' of undefined 鐨凚ug
+## 2.0.16锛�021-07-21锛�+- 鏂板 return-type 灞炴�鏀寔杩斿洖 date 鏃ユ湡瀵硅薄
+## 2.0.15锛�021-07-14锛�+- 淇 鍗曢�鏃ユ湡绫诲瀷锛屽垵濮嬭祴鍊煎悗涓嶅湪褰撳墠鏃ュ巻鐨凚ug
+- 鏂板 clearIcon 灞炴�锛屾樉绀烘鐨勬竻绌烘寜閽彲閰嶇疆鏄剧ず闅愯棌锛堜粎 pc 鏈夋晥锛�+- 浼樺寲 绉诲姩绔Щ闄ゆ樉绀烘鐨勬竻绌烘寜閽紝鏃犲疄闄呯敤閫�+## 2.0.14锛�021-07-14锛�+- 淇 缁勪欢璧嬪�涓虹┖锛岀晫闈㈡湭鏇存柊鐨凚ug
+- 淇 start 鍜�end 涓嶈兘鍔ㄦ�璧嬪�鐨凚ug
+- 淇 鑼冨洿閫夌被鍨嬶紝鐢ㄦ埛閫夋嫨鍚庡啀娆¢�鎷╁彸渚ф棩鍘嗭紙缁撴潫鏃ユ湡锛夋樉绀轰笉姝g‘鐨凚ug
+## 2.0.13锛�021-07-08锛�+- 淇 鑼冨洿閫夋嫨涓嶈兘鍔ㄦ�璧嬪�鐨凚ug
+## 2.0.12锛�021-07-08锛�+- 淇 鑼冨洿閫夋嫨鐨勫垵濮嬫椂闂村湪涓�釜鏈堝唴鏃讹紝閫犳垚鏃犳硶閫夋嫨鐨刡ug
+## 2.0.11锛�021-07-08锛�+- 浼樺寲 寮瑰嚭灞傚湪瓒呭嚭瑙嗙獥杈圭紭瀹氫綅涓嶅噯纭殑闂
+## 2.0.10锛�021-07-08锛�+- 淇 鑼冨洿璧峰鐐规牱寮忕殑鑳屾櫙鑹蹭笌浠婃棩鏍峰紡鐨勫瓧浣撳墠鏅壊铻嶅悎锛屽鑷存棩鏈熷瓧浣撶湅涓嶆竻鐨凚ug
+- 浼樺寲 寮瑰嚭灞傚湪瓒呭嚭瑙嗙獥杈圭紭琚伄鐩栫殑闂
+## 2.0.9锛�021-07-07锛�+- 鏂板 maskClick 浜嬩欢
+- 淇 鐗规畩鎯呭喌鏃ュ巻 rpx 甯冨眬閿欒鐨凚ug锛宺px -> px
+- 淇 鑼冨洿閫夋嫨鏃舵竻绌鸿繑鍥炲�涓嶅悎鐞嗙殑bug锛孾'', ''] -> []
+## 2.0.8锛�021-07-07锛�+- 鏂板 鏃ユ湡鏃堕棿鏄剧ず妗嗘敮鎸佹彃妲�+## 2.0.7锛�021-07-01锛�+- 浼樺寲 娣诲姞 uni-icons 渚濊禆
+## 2.0.6锛�021-05-22锛�+- 淇 鍥炬爣鍦ㄥ皬绋嬪簭涓婁笉鏄剧ず鐨凚ug
+- 浼樺寲 閲嶅懡鍚嶅紩鐢ㄧ粍浠讹紝閬垮厤娼滃湪缁勪欢鍛藉悕鍐茬獊
+## 2.0.5锛�021-05-20锛�+- 浼樺寲 浠g爜鐩綍鎵佸钩鍖�+## 2.0.4锛�021-05-12锛�+- 鏂板 缁勪欢绀轰緥鍦板潃
+## 2.0.3锛�021-05-10锛�+- 淇 ios 涓嬩笉璇嗗埆 '-' 鏃ユ湡鏍煎紡鐨凚ug
+- 浼樺寲 pc 涓嬪脊鍑哄眰娣诲姞杈规鍜岄槾褰�+## 2.0.2锛�021-05-08锛�+- 淇 鍦�admin 涓幏鍙栧脊鍑哄眰瀹氫綅閿欒鐨刡ug
+## 2.0.1锛�021-05-08锛�+- 淇 type 灞炴�鍚戜笅鍏煎锛岄粯璁ゅ�浠�date 鍙樻洿涓�datetime
+## 2.0.0锛�021-04-30锛�+- 鏀寔鏃ュ巻褰㈠紡鐨勬棩鏈�鏃堕棿鐨勮寖鍥撮�鎷�+ > 娉ㄦ剰锛氭鐗堟湰涓嶅悜鍚庡吋瀹癸紝涓嶅啀鏀寔鍗曠嫭鏃堕棿閫夋嫨锛坱ype=time锛夊強鐩稿叧鐨�hide-second 灞炴�锛堟椂闂撮�鍙娇鐢ㄥ唴缃粍浠�picker锛�+## 1.0.6锛�021-03-18锛�+- 鏂板 hide-second 灞炴�锛屾椂闂存敮鎸佷粎閫夋嫨鏃躲�鍒�+- 淇 閫夋嫨璺熸樉绀虹殑鏃ユ湡涓嶄竴鏍风殑Bug
+- 淇 chang浜嬩欢瑙﹀彂2娆$殑Bug
+- 淇 鍒嗐�绉�end 鑼冨洿閿欒鐨凚ug
+- 浼樺寲 鏇村ソ鐨�nvue 閫傞厤
diff --git a/uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar-item.vue b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar-item.vue
new file mode 100644
index 0000000..dba9887
--- /dev/null
+++ b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar-item.vue
@@ -0,0 +1,177 @@
+<template>
+	<view class="uni-calendar-item__weeks-box" :class="{
+		'uni-calendar-item--disable':weeks.disable,
+		'uni-calendar-item--before-checked-x':weeks.beforeMultiple,
+		'uni-calendar-item--multiple': weeks.multiple,
+		'uni-calendar-item--after-checked-x':weeks.afterMultiple,
+		}" @click="choiceDate(weeks)" @mouseenter="handleMousemove(weeks)">
+		<view class="uni-calendar-item__weeks-box-item" :class="{
+				'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && (calendar.userChecked || !checkHover),
+				'uni-calendar-item--checked-range-text': checkHover,
+				'uni-calendar-item--before-checked':weeks.beforeMultiple,
+				'uni-calendar-item--multiple': weeks.multiple,
+				'uni-calendar-item--after-checked':weeks.afterMultiple,
+				'uni-calendar-item--disable':weeks.disable,
+				}">
+			<text v-if="selected && weeks.extraInfo" class="uni-calendar-item__weeks-box-circle"></text>
+			<text class="uni-calendar-item__weeks-box-text uni-calendar-item__weeks-box-text-disable uni-calendar-item--checked-text">{{weeks.date}}</text>
+		</view>
+		<view :class="{'uni-calendar-item--today': weeks.isToday}"></view>
+	</view>
+</template>
+
+<script>
+	export default {
+		props: {
+			weeks: {
+				type: Object,
+				default () {
+					return {}
+				}
+			},
+			calendar: {
+				type: Object,
+				default: () => {
+					return {}
+				}
+			},
+			selected: {
+				type: Array,
+				default: () => {
+					return []
+				}
+			},
+			checkHover: {
+				type: Boolean,
+				default: false
+			}
+		},
+		methods: {
+			choiceDate(weeks) {
+				this.$emit('change', weeks)
+			},
+			handleMousemove(weeks) {
+				this.$emit('handleMouse', weeks)
+			}
+		}
+	}
+</script>
+
+<style lang="scss" >
+	$uni-primary: #007aff !default;
+
+	.uni-calendar-item__weeks-box {
+		flex: 1;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: column;
+		justify-content: center;
+		align-items: center;
+		margin: 1px 0;
+		position: relative;
+	}
+
+	.uni-calendar-item__weeks-box-text {
+		font-size: 14px;
+		// font-family: Lato-Bold, Lato;
+		font-weight: bold;
+		color: darken($color: $uni-primary, $amount: 40%);
+	}
+
+	.uni-calendar-item__weeks-box-item {
+		position: relative;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: column;
+		justify-content: center;
+		align-items: center;
+		width: 40px;
+		height: 40px;
+		/* #ifdef H5 */
+		cursor: pointer;
+		/* #endif */
+	}
+
+
+	.uni-calendar-item__weeks-box-circle {
+		position: absolute;
+		top: 5px;
+		right: 5px;
+		width: 8px;
+		height: 8px;
+		border-radius: 8px;
+		background-color: #dd524d;
+
+	}
+
+	.uni-calendar-item__weeks-box .uni-calendar-item--disable {
+		cursor: default;
+	}
+
+	.uni-calendar-item--disable .uni-calendar-item__weeks-box-text-disable {
+		color: #D1D1D1;
+	}
+
+	.uni-calendar-item--today {
+		position: absolute;
+		top: 10px;
+		right: 17%;
+		background-color: #dd524d;
+		width:6px;
+		height: 6px;
+		border-radius: 50%;
+	}
+
+	.uni-calendar-item--extra {
+		color: #dd524d;
+		opacity: 0.8;
+	}
+
+	.uni-calendar-item__weeks-box .uni-calendar-item--checked {
+		background-color: $uni-primary;
+		border-radius: 50%;
+		box-sizing: border-box;
+		border: 3px solid #fff;
+	}
+
+	.uni-calendar-item--checked .uni-calendar-item--checked-text {
+		color: #fff;
+	}
+
+	.uni-calendar-item--multiple .uni-calendar-item--checked-range-text {
+		color: #333;
+	}
+
+	.uni-calendar-item--multiple {
+		background-color:  #F6F7FC;
+		// color: #fff;
+	}
+
+	.uni-calendar-item--multiple .uni-calendar-item--before-checked,
+	.uni-calendar-item--multiple .uni-calendar-item--after-checked {
+		background-color: $uni-primary;
+		border-radius: 50%;
+		box-sizing: border-box;
+		border: 3px solid #F6F7FC;
+	}
+
+	.uni-calendar-item--before-checked .uni-calendar-item--checked-text,
+	.uni-calendar-item--after-checked .uni-calendar-item--checked-text {
+		color: #fff;
+	}
+
+	.uni-calendar-item--before-checked-x {
+		border-top-left-radius: 50px;
+		border-bottom-left-radius: 50px;
+		box-sizing: border-box;
+		background-color: #F6F7FC;
+	}
+
+	.uni-calendar-item--after-checked-x {
+		border-top-right-radius: 50px;
+		border-bottom-right-radius: 50px;
+		background-color: #F6F7FC;
+	}
+</style>
diff --git a/uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar.vue b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar.vue
new file mode 100644
index 0000000..0f9e121
--- /dev/null
+++ b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar.vue
@@ -0,0 +1,947 @@
+<template>
+	<view class="uni-calendar" @mouseleave="leaveCale">
+
+		<view v-if="!insert && show" class="uni-calendar__mask" :class="{'uni-calendar--mask-show':aniMaskShow}"
+			@click="maskClick"></view>
+
+		<view v-if="insert || show" class="uni-calendar__content"
+			:class="{'uni-calendar--fixed':!insert,'uni-calendar--ani-show':aniMaskShow, 'uni-calendar__content-mobile': aniMaskShow}">
+			<view class="uni-calendar__header" :class="{'uni-calendar__header-mobile' :!insert}">
+
+				<view class="uni-calendar__header-btn-box" @click.stop="changeMonth('pre')">
+					<view class="uni-calendar__header-btn uni-calendar--left"></view>
+				</view>
+
+				<picker mode="date" :value="date" fields="month" @change="bindDateChange">
+					<text
+						class="uni-calendar__header-text">{{ (nowDate.year||'') + yearText + ( nowDate.month||'') + monthText}}</text>
+				</picker>
+
+				<view class="uni-calendar__header-btn-box" @click.stop="changeMonth('next')">
+					<view class="uni-calendar__header-btn uni-calendar--right"></view>
+				</view>
+
+				<view v-if="!insert" class="dialog-close" @click="maskClick">
+					<view class="dialog-close-plus" data-id="close"></view>
+					<view class="dialog-close-plus dialog-close-rotate" data-id="close"></view>
+				</view>
+			</view>
+			<view class="uni-calendar__box">
+
+				<view v-if="showMonth" class="uni-calendar__box-bg">
+					<text class="uni-calendar__box-bg-text">{{nowDate.month}}</text>
+				</view>
+
+				<view class="uni-calendar__weeks" style="padding-bottom: 7px;">
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">{{SUNText}}</text>
+					</view>
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">{{MONText}}</text>
+					</view>
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">{{TUEText}}</text>
+					</view>
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">{{WEDText}}</text>
+					</view>
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">{{THUText}}</text>
+					</view>
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">{{FRIText}}</text>
+					</view>
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">{{SATText}}</text>
+					</view>
+				</view>
+
+				<view class="uni-calendar__weeks" v-for="(item,weekIndex) in weeks" :key="weekIndex">
+					<view class="uni-calendar__weeks-item" v-for="(weeks,weeksIndex) in item" :key="weeksIndex">
+						<calendar-item class="uni-calendar-item--hook" :weeks="weeks" :calendar="calendar" :selected="selected"
+							:checkHover="range" @change="choiceDate" @handleMouse="handleMouse">
+						</calendar-item>
+					</view>
+				</view>
+			</view>
+
+			<view v-if="!insert && !range && hasTime" class="uni-date-changed uni-calendar--fixed-top"
+				style="padding: 0 80px;">
+				<view class="uni-date-changed--time-date">{{tempSingleDate ? tempSingleDate : selectDateText}}</view>
+				<time-picker type="time" :start="timepickerStartTime" :end="timepickerEndTime" v-model="time"
+					:disabled="!tempSingleDate" :border="false" :hide-second="hideSecond" class="time-picker-style">
+				</time-picker>
+			</view>
+
+			<view v-if="!insert && range && hasTime" class="uni-date-changed uni-calendar--fixed-top">
+				<view class="uni-date-changed--time-start">
+					<view class="uni-date-changed--time-date">{{tempRange.before ? tempRange.before : startDateText}}
+					</view>
+					<time-picker type="time" :start="timepickerStartTime" v-model="timeRange.startTime" :border="false"
+						:hide-second="hideSecond" :disabled="!tempRange.before" class="time-picker-style">
+					</time-picker>
+				</view>
+				<view style="line-height: 50px;">
+					<uni-icons type="arrowthinright" color="#999"></uni-icons>
+				</view>
+				<view class="uni-date-changed--time-end">
+					<view class="uni-date-changed--time-date">{{tempRange.after ? tempRange.after : endDateText}}</view>
+					<time-picker type="time" :end="timepickerEndTime" v-model="timeRange.endTime" :border="false"
+						:hide-second="hideSecond" :disabled="!tempRange.after" class="time-picker-style">
+					</time-picker>
+				</view>
+			</view>
+
+			<view v-if="!insert" class="uni-date-changed uni-date-btn--ok">
+				<view class="uni-datetime-picker--btn" @click="confirm">{{confirmText}}</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {
+		Calendar,
+		getDate,
+		getTime
+	} from './util.js';
+	import calendarItem from './calendar-item.vue'
+	import timePicker from './time-picker.vue'
+
+	import {
+		initVueI18n
+	} from '@dcloudio/uni-i18n'
+	import i18nMessages from './i18n/index.js'
+	const {
+		t
+	} = initVueI18n(i18nMessages)
+
+	/**
+	 * Calendar 鏃ュ巻
+	 * @description 鏃ュ巻缁勪欢鍙互鏌ョ湅鏃ユ湡锛岄�鎷╀换鎰忚寖鍥村唴鐨勬棩鏈燂紝鎵撶偣鎿嶄綔銆傚父鐢ㄥ満鏅锛氶厭搴楁棩鏈熼璁€�鐏溅鏈虹エ閫夋嫨璐拱鏃ユ湡銆佷笂涓嬬彮鎵撳崱绛�+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=56
+	 * @property {String} date 鑷畾涔夊綋鍓嶆椂闂达紝榛樿涓轰粖澶�+	 * @property {String} startDate 鏃ユ湡閫夋嫨鑼冨洿-寮�鏃ユ湡
+	 * @property {String} endDate 鏃ユ湡閫夋嫨鑼冨洿-缁撴潫鏃ユ湡
+	 * @property {Boolean} range 鑼冨洿閫夋嫨
+	 * @property {Boolean} insert = [true|false] 鎻掑叆妯″紡,榛樿涓篺alse
+	 * 	@value true 寮圭獥妯″紡
+	 * 	@value false 鎻掑叆妯″紡
+	 * @property {Boolean} clearDate = [true|false] 寮圭獥妯″紡鏄惁娓呯┖涓婃閫夋嫨鍐呭
+	 * @property {Array} selected 鎵撶偣锛屾湡寰呮牸寮廩{date: '2019-06-27', info: '绛惧埌', data: { custom: '鑷畾涔変俊鎭�, name: '鑷畾涔夋秷鎭ご',xxx:xxx... }}]
+	 * @property {Boolean} showMonth 鏄惁閫夋嫨鏈堜唤涓鸿儗鏅�+	 * @property {[String} defaultValue 閫夋嫨鍣ㄦ墦寮�椂榛樿鏄剧ず鐨勬椂闂�+	 * @event {Function} change 鏃ユ湡鏀瑰彉锛宍insert :ture` 鏃剁敓鏁�+	 * @event {Function} confirm 纭閫夋嫨`insert :false` 鏃剁敓鏁�+	 * @event {Function} monthSwitch 鍒囨崲鏈堜唤鏃惰Е鍙�+	 * @example <uni-calendar :insert="true" :start-date="'2019-3-2'":end-date="'2019-5-20'"@change="change" />
+	 */
+	export default {
+		components: {
+			calendarItem,
+			timePicker
+		},
+
+		options: {
+			// #ifdef MP-TOUTIAO
+			virtualHost: false,
+			// #endif
+			// #ifndef MP-TOUTIAO
+			virtualHost: true
+			// #endif
+		},
+		props: {
+			date: {
+				type: String,
+				default: ''
+			},
+			defTime: {
+				type: [String, Object],
+				default: ''
+			},
+			selectableTimes: {
+				type: [Object],
+				default () {
+					return {}
+				}
+			},
+			selected: {
+				type: Array,
+				default () {
+					return []
+				}
+			},
+			startDate: {
+				type: String,
+				default: ''
+			},
+			endDate: {
+				type: String,
+				default: ''
+			},
+			startPlaceholder: {
+				type: String,
+				default: ''
+			},
+			endPlaceholder: {
+				type: String,
+				default: ''
+			},
+			range: {
+				type: Boolean,
+				default: false
+			},
+			hasTime: {
+				type: Boolean,
+				default: false
+			},
+			insert: {
+				type: Boolean,
+				default: true
+			},
+			showMonth: {
+				type: Boolean,
+				default: true
+			},
+			clearDate: {
+				type: Boolean,
+				default: true
+			},
+			checkHover: {
+				type: Boolean,
+				default: true
+			},
+			hideSecond: {
+				type: [Boolean],
+				default: false
+			},
+			pleStatus: {
+				type: Object,
+				default () {
+					return {
+						before: '',
+						after: '',
+						data: [],
+						fulldate: ''
+					}
+				}
+			},
+			defaultValue: {
+				type: [String, Object, Array],
+				default: ''
+			}
+		},
+		data() {
+			return {
+				show: false,
+				weeks: [],
+				calendar: {},
+				nowDate: {},
+				aniMaskShow: false,
+				firstEnter: true,
+				time: '',
+				timeRange: {
+					startTime: '',
+					endTime: ''
+				},
+				tempSingleDate: '',
+				tempRange: {
+					before: '',
+					after: ''
+				}
+			}
+		},
+		watch: {
+			date: {
+				immediate: true,
+				handler(newVal) {
+					if (!this.range) {
+						this.tempSingleDate = newVal
+						setTimeout(() => {
+							this.init(newVal)
+						}, 100)
+					}
+				}
+			},
+			defTime: {
+				immediate: true,
+				handler(newVal) {
+					if (!this.range) {
+						this.time = newVal
+					} else {
+						this.timeRange.startTime = newVal.start
+						this.timeRange.endTime = newVal.end
+					}
+				}
+			},
+			startDate(val) {
+				// 瀛楄妭灏忕▼搴�watch 鏃╀簬 created
+				if (!this.cale) {
+					return
+				}
+				this.cale.setStartDate(val)
+				this.cale.setDate(this.nowDate.fullDate)
+				this.weeks = this.cale.weeks
+			},
+			endDate(val) {
+				// 瀛楄妭灏忕▼搴�watch 鏃╀簬 created
+				if (!this.cale) {
+					return
+				}
+				this.cale.setEndDate(val)
+				this.cale.setDate(this.nowDate.fullDate)
+				this.weeks = this.cale.weeks
+			},
+			selected(newVal) {
+				// 瀛楄妭灏忕▼搴�watch 鏃╀簬 created
+				if (!this.cale) {
+					return
+				}
+				this.cale.setSelectInfo(this.nowDate.fullDate, newVal)
+				this.weeks = this.cale.weeks
+			},
+			pleStatus: {
+				immediate: true,
+				handler(newVal) {
+					const {
+						before,
+						after,
+						fulldate,
+						which
+					} = newVal
+					this.tempRange.before = before
+					this.tempRange.after = after
+					setTimeout(() => {
+						if (fulldate) {
+							this.cale.setHoverMultiple(fulldate)
+							if (before && after) {
+								this.cale.lastHover = true
+								if (this.rangeWithinMonth(after, before)) return
+								this.setDate(before)
+							} else {
+								this.cale.setMultiple(fulldate)
+								this.setDate(this.nowDate.fullDate)
+								this.calendar.fullDate = ''
+								this.cale.lastHover = false
+							}
+						} else {
+							// 瀛楄妭灏忕▼搴�watch 鏃╀簬 created
+							if (!this.cale) {
+								return
+							}
+
+							this.cale.setDefaultMultiple(before, after)
+							if (which === 'left' && before) {
+								this.setDate(before)
+								this.weeks = this.cale.weeks
+							} else if (after) {
+								this.setDate(after)
+								this.weeks = this.cale.weeks
+							}
+							this.cale.lastHover = true
+						}
+					}, 16)
+				}
+			}
+		},
+		computed: {
+			timepickerStartTime() {
+				const activeDate = this.range ? this.tempRange.before : this.calendar.fullDate
+				return activeDate === this.startDate ? this.selectableTimes.start : ''
+			},
+			timepickerEndTime() {
+				const activeDate = this.range ? this.tempRange.after : this.calendar.fullDate
+				return activeDate === this.endDate ? this.selectableTimes.end : ''
+			},
+			/**
+			 * for i18n
+			 */
+			selectDateText() {
+				return t("uni-datetime-picker.selectDate")
+			},
+			startDateText() {
+				return this.startPlaceholder || t("uni-datetime-picker.startDate")
+			},
+			endDateText() {
+				return this.endPlaceholder || t("uni-datetime-picker.endDate")
+			},
+			okText() {
+				return t("uni-datetime-picker.ok")
+			},
+			yearText() {
+				return t("uni-datetime-picker.year")
+			},
+			monthText() {
+				return t("uni-datetime-picker.month")
+			},
+			MONText() {
+				return t("uni-calender.MON")
+			},
+			TUEText() {
+				return t("uni-calender.TUE")
+			},
+			WEDText() {
+				return t("uni-calender.WED")
+			},
+			THUText() {
+				return t("uni-calender.THU")
+			},
+			FRIText() {
+				return t("uni-calender.FRI")
+			},
+			SATText() {
+				return t("uni-calender.SAT")
+			},
+			SUNText() {
+				return t("uni-calender.SUN")
+			},
+			confirmText() {
+				return t("uni-calender.confirm")
+			},
+		},
+		created() {
+			// 鑾峰彇鏃ュ巻鏂规硶瀹炰緥
+			this.cale = new Calendar({
+				selected: this.selected,
+				startDate: this.startDate,
+				endDate: this.endDate,
+				range: this.range,
+			})
+			// 閫変腑鏌愪竴澶�+			this.init(this.date)
+		},
+		methods: {
+			leaveCale() {
+				this.firstEnter = true
+			},
+			handleMouse(weeks) {
+				if (weeks.disable) return
+				if (this.cale.lastHover) return
+				let {
+					before,
+					after
+				} = this.cale.multipleStatus
+				if (!before) return
+				this.calendar = weeks
+				// 璁剧疆鑼冨洿閫�+				this.cale.setHoverMultiple(this.calendar.fullDate)
+				this.weeks = this.cale.weeks
+				// hover鏃讹紝杩涘叆涓�釜鏃ュ巻锛屾洿鏂板彟涓�釜
+				if (this.firstEnter) {
+					this.$emit('firstEnterCale', this.cale.multipleStatus)
+					this.firstEnter = false
+				}
+			},
+			rangeWithinMonth(A, B) {
+				const [yearA, monthA] = A.split('-')
+				const [yearB, monthB] = B.split('-')
+				return yearA === yearB && monthA === monthB
+			},
+			// 钂欑増鐐瑰嚮浜嬩欢
+			maskClick() {
+				this.close()
+				this.$emit('maskClose')
+			},
+
+			clearCalender() {
+				if (this.range) {
+					this.timeRange.startTime = ''
+					this.timeRange.endTime = ''
+					this.tempRange.before = ''
+					this.tempRange.after = ''
+					this.cale.multipleStatus.before = ''
+					this.cale.multipleStatus.after = ''
+					this.cale.multipleStatus.data = []
+					this.cale.lastHover = false
+				} else {
+					this.time = ''
+					this.tempSingleDate = ''
+				}
+				this.calendar.fullDate = ''
+				this.setDate(new Date())
+			},
+
+			bindDateChange(e) {
+				const value = e.detail.value + '-1'
+				this.setDate(value)
+			},
+			/**
+			 * 鍒濆鍖栨棩鏈熸樉绀�+			 * @param {Object} date
+			 */
+			init(date) {
+				// 瀛楄妭灏忕▼搴�watch 鏃╀簬 created
+				if (!this.cale) {
+					return
+				}
+				this.cale.setDate(date || new Date())
+				this.weeks = this.cale.weeks
+				this.nowDate = this.cale.getInfo(date)
+				this.calendar = {
+					...this.nowDate
+				}
+				if (!date) {
+					// 浼樺寲date涓虹┖榛樿涓嶉�涓粖澶�+					this.calendar.fullDate = ''
+					if (this.defaultValue && !this.range) {
+						// 鏆傛椂鍙敮鎸佺Щ鍔ㄧ闈炶寖鍥撮�鎷�+						const defaultDate = new Date(this.defaultValue)
+						const fullDate = getDate(defaultDate)
+						const year = defaultDate.getFullYear()
+						const month = defaultDate.getMonth() + 1
+						const date = defaultDate.getDate()
+						const day = defaultDate.getDay()
+						this.calendar = {
+								fullDate,
+								year,
+								month,
+								date,
+								day
+							},
+							this.tempSingleDate = fullDate
+						this.time = getTime(defaultDate, this.hideSecond)
+					}
+				}
+			},
+			/**
+			 * 鎵撳紑鏃ュ巻寮圭獥
+			 */
+			open() {
+				// 寮圭獥妯″紡骞朵笖娓呯悊鏁版嵁
+				if (this.clearDate && !this.insert) {
+					this.cale.cleanMultipleStatus()
+					this.init(this.date)
+				}
+				this.show = true
+				this.$nextTick(() => {
+					setTimeout(() => {
+						this.aniMaskShow = true
+					}, 50)
+				})
+			},
+			/**
+			 * 鍏抽棴鏃ュ巻寮圭獥
+			 */
+			close() {
+				this.aniMaskShow = false
+				this.$nextTick(() => {
+					setTimeout(() => {
+						this.show = false
+						this.$emit('close')
+					}, 300)
+				})
+			},
+			/**
+			 * 纭鎸夐挳
+			 */
+			confirm() {
+				this.setEmit('confirm')
+				this.close()
+			},
+			/**
+			 * 鍙樺寲瑙﹀彂
+			 */
+			change(isSingleChange) {
+				if (!this.insert && !isSingleChange) return
+				this.setEmit('change')
+			},
+			/**
+			 * 閫夋嫨鏈堜唤瑙﹀彂
+			 */
+			monthSwitch() {
+				let {
+					year,
+					month
+				} = this.nowDate
+				this.$emit('monthSwitch', {
+					year,
+					month: Number(month)
+				})
+			},
+			/**
+			 * 娲惧彂浜嬩欢
+			 * @param {Object} name
+			 */
+			setEmit(name) {
+				if (!this.range) {
+					if (!this.calendar.fullDate) {
+						this.calendar = this.cale.getInfo(new Date())
+						this.tempSingleDate = this.calendar.fullDate
+					}
+					if (this.hasTime && !this.time) {
+						this.time = getTime(new Date(), this.hideSecond)
+					}
+				}
+				let {
+					year,
+					month,
+					date,
+					fullDate,
+					extraInfo
+				} = this.calendar
+				this.$emit(name, {
+					range: this.cale.multipleStatus,
+					year,
+					month,
+					date,
+					time: this.time,
+					timeRange: this.timeRange,
+					fulldate: fullDate,
+					extraInfo: extraInfo || {}
+				})
+			},
+			/**
+			 * 閫夋嫨澶╄Е鍙�+			 * @param {Object} weeks
+			 */
+			choiceDate(weeks) {
+				if (weeks.disable) return
+				this.calendar = weeks
+				this.calendar.userChecked = true
+				// 璁剧疆澶氶�
+				this.cale.setMultiple(this.calendar.fullDate, true)
+				this.weeks = this.cale.weeks
+				this.tempSingleDate = this.calendar.fullDate
+				const beforeDate = new Date(this.cale.multipleStatus.before).getTime()
+				const afterDate = new Date(this.cale.multipleStatus.after).getTime()
+				if (beforeDate > afterDate && afterDate) {
+					this.tempRange.before = this.cale.multipleStatus.after
+					this.tempRange.after = this.cale.multipleStatus.before
+				} else {
+					this.tempRange.before = this.cale.multipleStatus.before
+					this.tempRange.after = this.cale.multipleStatus.after
+				}
+				this.change(true)
+			},
+			changeMonth(type) {
+				let newDate
+				if (type === 'pre') {
+					newDate = this.cale.getPreMonthObj(this.nowDate.fullDate).fullDate
+				} else if (type === 'next') {
+					newDate = this.cale.getNextMonthObj(this.nowDate.fullDate).fullDate
+				}
+
+				this.setDate(newDate)
+				this.monthSwitch()
+			},
+			/**
+			 * 璁剧疆鏃ユ湡
+			 * @param {Object} date
+			 */
+			setDate(date) {
+				this.cale.setDate(date)
+				this.weeks = this.cale.weeks
+				this.nowDate = this.cale.getInfo(date)
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	$uni-primary: #007aff !default;
+
+	.uni-calendar {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: column;
+	}
+
+	.uni-calendar__mask {
+		position: fixed;
+		bottom: 0;
+		top: 0;
+		left: 0;
+		right: 0;
+		background-color: rgba(0, 0, 0, 0.4);
+		transition-property: opacity;
+		transition-duration: 0.3s;
+		opacity: 0;
+		/* #ifndef APP-NVUE */
+		z-index: 99;
+		/* #endif */
+	}
+
+	.uni-calendar--mask-show {
+		opacity: 1
+	}
+
+	.uni-calendar--fixed {
+		position: fixed;
+		bottom: calc(var(--window-bottom));
+		left: 0;
+		right: 0;
+		transition-property: transform;
+		transition-duration: 0.3s;
+		transform: translateY(460px);
+		/* #ifndef APP-NVUE */
+		z-index: 99;
+		/* #endif */
+	}
+
+	.uni-calendar--ani-show {
+		transform: translateY(0);
+	}
+
+	.uni-calendar__content {
+		background-color: #fff;
+	}
+
+	.uni-calendar__content-mobile {
+		border-top-left-radius: 10px;
+		border-top-right-radius: 10px;
+		box-shadow: 0px 0px 5px 3px rgba(0, 0, 0, 0.1);
+	}
+
+	.uni-calendar__header {
+		position: relative;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		justify-content: center;
+		align-items: center;
+		height: 50px;
+	}
+
+	.uni-calendar__header-mobile {
+		padding: 10px;
+		padding-bottom: 0;
+	}
+
+	.uni-calendar--fixed-top {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		justify-content: space-between;
+		border-top-color: rgba(0, 0, 0, 0.4);
+		border-top-style: solid;
+		border-top-width: 1px;
+	}
+
+	.uni-calendar--fixed-width {
+		width: 50px;
+	}
+
+	.uni-calendar__backtoday {
+		position: absolute;
+		right: 0;
+		top: 25rpx;
+		padding: 0 5px;
+		padding-left: 10px;
+		height: 25px;
+		line-height: 25px;
+		font-size: 12px;
+		border-top-left-radius: 25px;
+		border-bottom-left-radius: 25px;
+		color: #fff;
+		background-color: #f1f1f1;
+	}
+
+	.uni-calendar__header-text {
+		text-align: center;
+		width: 100px;
+		font-size: 15px;
+		color: #666;
+	}
+
+	.uni-calendar__button-text {
+		text-align: center;
+		width: 100px;
+		font-size: 14px;
+		color: $uni-primary;
+		/* #ifndef APP-NVUE */
+		letter-spacing: 3px;
+		/* #endif */
+	}
+
+	.uni-calendar__header-btn-box {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		align-items: center;
+		justify-content: center;
+		width: 50px;
+		height: 50px;
+	}
+
+	.uni-calendar__header-btn {
+		width: 9px;
+		height: 9px;
+		border-left-color: #808080;
+		border-left-style: solid;
+		border-left-width: 1px;
+		border-top-color: #555555;
+		border-top-style: solid;
+		border-top-width: 1px;
+	}
+
+	.uni-calendar--left {
+		transform: rotate(-45deg);
+	}
+
+	.uni-calendar--right {
+		transform: rotate(135deg);
+	}
+
+
+	.uni-calendar__weeks {
+		position: relative;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+	}
+
+	.uni-calendar__weeks-item {
+		flex: 1;
+	}
+
+	.uni-calendar__weeks-day {
+		flex: 1;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: column;
+		justify-content: center;
+		align-items: center;
+		height: 40px;
+		border-bottom-color: #F5F5F5;
+		border-bottom-style: solid;
+		border-bottom-width: 1px;
+	}
+
+	.uni-calendar__weeks-day-text {
+		font-size: 12px;
+		color: #B2B2B2;
+	}
+
+	.uni-calendar__box {
+		position: relative;
+		// padding: 0 10px;
+		padding-bottom: 7px;
+	}
+
+	.uni-calendar__box-bg {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		justify-content: center;
+		align-items: center;
+		position: absolute;
+		top: 0;
+		left: 0;
+		right: 0;
+		bottom: 0;
+	}
+
+	.uni-calendar__box-bg-text {
+		font-size: 200px;
+		font-weight: bold;
+		color: #999;
+		opacity: 0.1;
+		text-align: center;
+		/* #ifndef APP-NVUE */
+		line-height: 1;
+		/* #endif */
+	}
+
+	.uni-date-changed {
+		padding: 0 10px;
+		// line-height: 50px;
+		text-align: center;
+		color: #333;
+		border-top-color: #DCDCDC;
+		;
+		border-top-style: solid;
+		border-top-width: 1px;
+		flex: 1;
+	}
+
+	.uni-date-btn--ok {
+		padding: 20px 15px;
+	}
+
+	.uni-date-changed--time-start {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		align-items: center;
+	}
+
+	.uni-date-changed--time-end {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		align-items: center;
+	}
+
+	.uni-date-changed--time-date {
+		color: #999;
+		line-height: 50px;
+		/* #ifdef MP-TOUTIAO */
+		font-size: 16px;
+		/* #endif */
+		margin-right: 5px;
+		// opacity: 0.6;
+	}
+
+	.time-picker-style {
+		// width: 62px;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		justify-content: center;
+		align-items: center
+	}
+
+	.mr-10 {
+		margin-right: 10px;
+	}
+
+	.dialog-close {
+		position: absolute;
+		top: 0;
+		right: 0;
+		bottom: 0;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		align-items: center;
+		padding: 0 25px;
+		margin-top: 10px;
+	}
+
+	.dialog-close-plus {
+		width: 16px;
+		height: 2px;
+		background-color: #737987;
+		border-radius: 2px;
+		transform: rotate(45deg);
+	}
+
+	.dialog-close-rotate {
+		position: absolute;
+		transform: rotate(-45deg);
+	}
+
+	.uni-datetime-picker--btn {
+		border-radius: 100px;
+		height: 40px;
+		line-height: 40px;
+		background-color: $uni-primary;
+		color: #fff;
+		font-size: 16px;
+		letter-spacing: 2px;
+	}
+
+	/* #ifndef APP-NVUE */
+	.uni-datetime-picker--btn:active {
+		opacity: 0.7;
+	}
+
+	/* #endif */
+</style>
diff --git a/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/en.json b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/en.json
new file mode 100644
index 0000000..024f22f
--- /dev/null
+++ b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/en.json
@@ -0,0 +1,22 @@
+{
+	"uni-datetime-picker.selectDate": "select date",
+	"uni-datetime-picker.selectTime": "select time",
+	"uni-datetime-picker.selectDateTime": "select date and time",
+	"uni-datetime-picker.startDate": "start date",
+	"uni-datetime-picker.endDate": "end date",
+	"uni-datetime-picker.startTime": "start time",
+	"uni-datetime-picker.endTime": "end time",
+	"uni-datetime-picker.ok": "ok",
+	"uni-datetime-picker.clear": "clear",
+	"uni-datetime-picker.cancel": "cancel",
+	"uni-datetime-picker.year": "-",
+	"uni-datetime-picker.month": "",
+	"uni-calender.MON": "MON",
+	"uni-calender.TUE": "TUE",
+	"uni-calender.WED": "WED",
+	"uni-calender.THU": "THU",
+	"uni-calender.FRI": "FRI",
+	"uni-calender.SAT": "SAT",
+	"uni-calender.SUN": "SUN",
+	"uni-calender.confirm": "confirm"
+}
diff --git a/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/index.js b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/index.js
new file mode 100644
index 0000000..de7509c
--- /dev/null
+++ b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/index.js
@@ -0,0 +1,8 @@
+import en from './en.json'
+import zhHans from './zh-Hans.json'
+import zhHant from './zh-Hant.json'
+export default {
+	en,
+	'zh-Hans': zhHans,
+	'zh-Hant': zhHant
+}
diff --git a/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hans.json b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hans.json
new file mode 100644
index 0000000..d2df5e7
--- /dev/null
+++ b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hans.json
@@ -0,0 +1,22 @@
+{
+	"uni-datetime-picker.selectDate": "閫夋嫨鏃ユ湡",
+	"uni-datetime-picker.selectTime": "閫夋嫨鏃堕棿",
+	"uni-datetime-picker.selectDateTime": "閫夋嫨鏃ユ湡鏃堕棿",
+	"uni-datetime-picker.startDate": "寮�鏃ユ湡",
+	"uni-datetime-picker.endDate": "缁撴潫鏃ユ湡",
+	"uni-datetime-picker.startTime": "寮�鏃堕棿",
+	"uni-datetime-picker.endTime": "缁撴潫鏃堕棿",
+	"uni-datetime-picker.ok": "纭畾",
+	"uni-datetime-picker.clear": "娓呴櫎",
+	"uni-datetime-picker.cancel": "鍙栨秷",
+	"uni-datetime-picker.year": "骞�,
+	"uni-datetime-picker.month": "鏈�,
+	"uni-calender.SUN": "鏃�,
+	"uni-calender.MON": "涓�,
+	"uni-calender.TUE": "浜�,
+	"uni-calender.WED": "涓�,
+	"uni-calender.THU": "鍥�,
+	"uni-calender.FRI": "浜�,
+	"uni-calender.SAT": "鍏�,
+	"uni-calender.confirm": "纭"
+}
\ No newline at end of file
diff --git a/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hant.json b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hant.json
new file mode 100644
index 0000000..d23fa3c
--- /dev/null
+++ b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hant.json
@@ -0,0 +1,22 @@
+{
+  "uni-datetime-picker.selectDate": "閬告搰鏃ユ湡",
+  "uni-datetime-picker.selectTime": "閬告搰鏅傞枔",
+  "uni-datetime-picker.selectDateTime": "閬告搰鏃ユ湡鏅傞枔",
+  "uni-datetime-picker.startDate": "闁嬪鏃ユ湡",
+  "uni-datetime-picker.endDate": "绲愭潫鏃ユ湡",
+  "uni-datetime-picker.startTime": "闁嬪鏃堕棿",
+  "uni-datetime-picker.endTime": "绲愭潫鏃堕棿",
+  "uni-datetime-picker.ok": "纰哄畾",
+  "uni-datetime-picker.clear": "娓呴櫎",
+  "uni-datetime-picker.cancel": "鍙栨秷",
+  "uni-datetime-picker.year": "骞�,
+  "uni-datetime-picker.month": "鏈�,
+  "uni-calender.SUN": "鏃�,
+  "uni-calender.MON": "涓�,
+  "uni-calender.TUE": "浜�,
+  "uni-calender.WED": "涓�,
+  "uni-calender.THU": "鍥�,
+  "uni-calender.FRI": "浜�,
+  "uni-calender.SAT": "鍏�,
+  "uni-calender.confirm": "纰鸿獚"
+}
\ No newline at end of file
diff --git a/uni_modules/uni-datetime-picker/components/uni-datetime-picker/time-picker.vue b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/time-picker.vue
new file mode 100644
index 0000000..1817692
--- /dev/null
+++ b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/time-picker.vue
@@ -0,0 +1,940 @@
+<template>
+	<view class="uni-datetime-picker">
+		<view @click="initTimePicker">
+			<slot>
+				<view class="uni-datetime-picker-timebox-pointer"
+					:class="{'uni-datetime-picker-disabled': disabled, 'uni-datetime-picker-timebox': border}">
+					<text class="uni-datetime-picker-text">{{time}}</text>
+					<view v-if="!time" class="uni-datetime-picker-time">
+						<text class="uni-datetime-picker-text">{{selectTimeText}}</text>
+					</view>
+				</view>
+			</slot>
+		</view>
+		<view v-if="visible" id="mask" class="uni-datetime-picker-mask" @click="tiggerTimePicker"></view>
+		<view v-if="visible" class="uni-datetime-picker-popup" :class="[dateShow && timeShow ? '' : 'fix-nvue-height']"
+			:style="fixNvueBug">
+			<view class="uni-title">
+				<text class="uni-datetime-picker-text">{{selectTimeText}}</text>
+			</view>
+			<view v-if="dateShow" class="uni-datetime-picker__container-box">
+				<picker-view class="uni-datetime-picker-view" :indicator-style="indicatorStyle" :value="ymd"
+					@change="bindDateChange">
+					<picker-view-column>
+						<view class="uni-datetime-picker-item" v-for="(item,index) in years" :key="index">
+							<text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
+						</view>
+					</picker-view-column>
+					<picker-view-column>
+						<view class="uni-datetime-picker-item" v-for="(item,index) in months" :key="index">
+							<text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
+						</view>
+					</picker-view-column>
+					<picker-view-column>
+						<view class="uni-datetime-picker-item" v-for="(item,index) in days" :key="index">
+							<text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
+						</view>
+					</picker-view-column>
+				</picker-view>
+				<!-- 鍏煎 nvue 涓嶆敮鎸佷吉绫�-->
+				<text class="uni-datetime-picker-sign sign-left">-</text>
+				<text class="uni-datetime-picker-sign sign-right">-</text>
+			</view>
+			<view v-if="timeShow" class="uni-datetime-picker__container-box">
+				<picker-view class="uni-datetime-picker-view" :class="[hideSecond ? 'time-hide-second' : '']"
+					:indicator-style="indicatorStyle" :value="hms" @change="bindTimeChange">
+					<picker-view-column>
+						<view class="uni-datetime-picker-item" v-for="(item,index) in hours" :key="index">
+							<text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
+						</view>
+					</picker-view-column>
+					<picker-view-column>
+						<view class="uni-datetime-picker-item" v-for="(item,index) in minutes" :key="index">
+							<text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
+						</view>
+					</picker-view-column>
+					<picker-view-column v-if="!hideSecond">
+						<view class="uni-datetime-picker-item" v-for="(item,index) in seconds" :key="index">
+							<text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
+						</view>
+					</picker-view-column>
+				</picker-view>
+				<!-- 鍏煎 nvue 涓嶆敮鎸佷吉绫�-->
+				<text class="uni-datetime-picker-sign" :class="[hideSecond ? 'sign-center' : 'sign-left']">:</text>
+				<text v-if="!hideSecond" class="uni-datetime-picker-sign sign-right">:</text>
+			</view>
+			<view class="uni-datetime-picker-btn">
+				<view @click="clearTime">
+					<text class="uni-datetime-picker-btn-text">{{clearText}}</text>
+				</view>
+				<view class="uni-datetime-picker-btn-group">
+					<view class="uni-datetime-picker-cancel" @click="tiggerTimePicker">
+						<text class="uni-datetime-picker-btn-text">{{cancelText}}</text>
+					</view>
+					<view @click="setTime">
+						<text class="uni-datetime-picker-btn-text">{{okText}}</text>
+					</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import {
+		initVueI18n
+	} from '@dcloudio/uni-i18n'
+	import i18nMessages from './i18n/index.js'
+	const {
+		t
+	} = initVueI18n(i18nMessages)
+	import {
+		fixIosDateFormat
+	} from './util'
+
+	/**
+	 * DatetimePicker 鏃堕棿閫夋嫨鍣�+	 * @description 鍙互鍚屾椂閫夋嫨鏃ユ湡鍜屾椂闂寸殑閫夋嫨鍣�+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=xxx
+	 * @property {String} type = [datetime | date | time] 鏄剧ず妯″紡
+	 * @property {Boolean} multiple = [true|false] 鏄惁澶氶�
+	 * @property {String|Number} value 榛樿鍊�+	 * @property {String|Number} start 璧峰鏃ユ湡鎴栨椂闂�+	 * @property {String|Number} end 璧峰鏃ユ湡鎴栨椂闂�+	 * @property {String} return-type = [timestamp | string]
+	 * @event {Function} change  閫変腑鍙戠敓鍙樺寲瑙﹀彂
+	 */
+
+	export default {
+		name: 'UniDatetimePicker',
+		data() {
+			return {
+				indicatorStyle: `height: 50px;`,
+				visible: false,
+				fixNvueBug: {},
+				dateShow: true,
+				timeShow: true,
+				title: '鏃ユ湡鍜屾椂闂�,
+				// 杈撳叆妗嗗綋鍓嶆椂闂�+				time: '',
+				// 褰撳墠鐨勫勾鏈堟棩鏃跺垎绉�+				year: 1920,
+				month: 0,
+				day: 0,
+				hour: 0,
+				minute: 0,
+				second: 0,
+				// 璧峰鏃堕棿
+				startYear: 1920,
+				startMonth: 1,
+				startDay: 1,
+				startHour: 0,
+				startMinute: 0,
+				startSecond: 0,
+				// 缁撴潫鏃堕棿
+				endYear: 2120,
+				endMonth: 12,
+				endDay: 31,
+				endHour: 23,
+				endMinute: 59,
+				endSecond: 59,
+			}
+		},
+		options: {
+			// #ifdef MP-TOUTIAO
+			virtualHost: false,
+			// #endif
+			// #ifndef MP-TOUTIAO
+			virtualHost: true
+			// #endif
+		},
+		props: {
+			type: {
+				type: String,
+				default: 'datetime'
+			},
+			value: {
+				type: [String, Number],
+				default: ''
+			},
+			modelValue: {
+				type: [String, Number],
+				default: ''
+			},
+			start: {
+				type: [Number, String],
+				default: ''
+			},
+			end: {
+				type: [Number, String],
+				default: ''
+			},
+			returnType: {
+				type: String,
+				default: 'string'
+			},
+			disabled: {
+				type: [Boolean, String],
+				default: false
+			},
+			border: {
+				type: [Boolean, String],
+				default: true
+			},
+			hideSecond: {
+				type: [Boolean, String],
+				default: false
+			}
+		},
+		watch: {
+			// #ifndef VUE3
+			value: {
+				handler(newVal) {
+					if (newVal) {
+						this.parseValue(fixIosDateFormat(newVal))
+						this.initTime(false)
+					} else {
+						this.time = ''
+						this.parseValue(Date.now())
+					}
+				},
+				immediate: true
+			},
+			// #endif
+			// #ifdef VUE3
+			modelValue: {
+				handler(newVal) {
+					if (newVal) {
+						this.parseValue(fixIosDateFormat(newVal))
+						this.initTime(false)
+					} else {
+						this.time = ''
+						this.parseValue(Date.now())
+					}
+				},
+				immediate: true
+			},
+			// #endif
+			type: {
+				handler(newValue) {
+					if (newValue === 'date') {
+						this.dateShow = true
+						this.timeShow = false
+						this.title = '鏃ユ湡'
+					} else if (newValue === 'time') {
+						this.dateShow = false
+						this.timeShow = true
+						this.title = '鏃堕棿'
+					} else {
+						this.dateShow = true
+						this.timeShow = true
+						this.title = '鏃ユ湡鍜屾椂闂�
+					}
+				},
+				immediate: true
+			},
+			start: {
+				handler(newVal) {
+					this.parseDatetimeRange(fixIosDateFormat(newVal), 'start')
+				},
+				immediate: true
+			},
+			end: {
+				handler(newVal) {
+					this.parseDatetimeRange(fixIosDateFormat(newVal), 'end')
+				},
+				immediate: true
+			},
+
+			// 鏈堛�鏃ャ�鏃躲�鍒嗐�绉掑彲閫夎寖鍥村彉鍖栧悗锛屾鏌ュ綋鍓嶅�鏄惁鍦ㄨ寖鍥村唴锛屼笉鍦ㄥ垯褰撳墠鍊奸噸缃负鍙�鑼冨洿绗竴椤�+			months(newVal) {
+				this.checkValue('month', this.month, newVal)
+			},
+			days(newVal) {
+				this.checkValue('day', this.day, newVal)
+			},
+			hours(newVal) {
+				this.checkValue('hour', this.hour, newVal)
+			},
+			minutes(newVal) {
+				this.checkValue('minute', this.minute, newVal)
+			},
+			seconds(newVal) {
+				this.checkValue('second', this.second, newVal)
+			}
+		},
+		computed: {
+			// 褰撳墠骞淬�鏈堛�鏃ャ�鏃躲�鍒嗐�绉掗�鎷╄寖鍥�+			years() {
+				return this.getCurrentRange('year')
+			},
+
+			months() {
+				return this.getCurrentRange('month')
+			},
+
+			days() {
+				return this.getCurrentRange('day')
+			},
+
+			hours() {
+				return this.getCurrentRange('hour')
+			},
+
+			minutes() {
+				return this.getCurrentRange('minute')
+			},
+
+			seconds() {
+				return this.getCurrentRange('second')
+			},
+
+			// picker 褰撳墠鍊兼暟缁�+			ymd() {
+				return [this.year - this.minYear, this.month - this.minMonth, this.day - this.minDay]
+			},
+			hms() {
+				return [this.hour - this.minHour, this.minute - this.minMinute, this.second - this.minSecond]
+			},
+
+			// 褰撳墠 date 鏄�start
+			currentDateIsStart() {
+				return this.year === this.startYear && this.month === this.startMonth && this.day === this.startDay
+			},
+
+			// 褰撳墠 date 鏄�end
+			currentDateIsEnd() {
+				return this.year === this.endYear && this.month === this.endMonth && this.day === this.endDay
+			},
+
+			// 褰撳墠骞淬�鏈堛�鏃ャ�鏃躲�鍒嗐�绉掔殑鏈�皬鍊煎拰鏈�ぇ鍊�+			minYear() {
+				return this.startYear
+			},
+			maxYear() {
+				return this.endYear
+			},
+			minMonth() {
+				if (this.year === this.startYear) {
+					return this.startMonth
+				} else {
+					return 1
+				}
+			},
+			maxMonth() {
+				if (this.year === this.endYear) {
+					return this.endMonth
+				} else {
+					return 12
+				}
+			},
+			minDay() {
+				if (this.year === this.startYear && this.month === this.startMonth) {
+					return this.startDay
+				} else {
+					return 1
+				}
+			},
+			maxDay() {
+				if (this.year === this.endYear && this.month === this.endMonth) {
+					return this.endDay
+				} else {
+					return this.daysInMonth(this.year, this.month)
+				}
+			},
+			minHour() {
+				if (this.type === 'datetime') {
+					if (this.currentDateIsStart) {
+						return this.startHour
+					} else {
+						return 0
+					}
+				}
+				if (this.type === 'time') {
+					return this.startHour
+				}
+			},
+			maxHour() {
+				if (this.type === 'datetime') {
+					if (this.currentDateIsEnd) {
+						return this.endHour
+					} else {
+						return 23
+					}
+				}
+				if (this.type === 'time') {
+					return this.endHour
+				}
+			},
+			minMinute() {
+				if (this.type === 'datetime') {
+					if (this.currentDateIsStart && this.hour === this.startHour) {
+						return this.startMinute
+					} else {
+						return 0
+					}
+				}
+				if (this.type === 'time') {
+					if (this.hour === this.startHour) {
+						return this.startMinute
+					} else {
+						return 0
+					}
+				}
+			},
+			maxMinute() {
+				if (this.type === 'datetime') {
+					if (this.currentDateIsEnd && this.hour === this.endHour) {
+						return this.endMinute
+					} else {
+						return 59
+					}
+				}
+				if (this.type === 'time') {
+					if (this.hour === this.endHour) {
+						return this.endMinute
+					} else {
+						return 59
+					}
+				}
+			},
+			minSecond() {
+				if (this.type === 'datetime') {
+					if (this.currentDateIsStart && this.hour === this.startHour && this.minute === this.startMinute) {
+						return this.startSecond
+					} else {
+						return 0
+					}
+				}
+				if (this.type === 'time') {
+					if (this.hour === this.startHour && this.minute === this.startMinute) {
+						return this.startSecond
+					} else {
+						return 0
+					}
+				}
+			},
+			maxSecond() {
+				if (this.type === 'datetime') {
+					if (this.currentDateIsEnd && this.hour === this.endHour && this.minute === this.endMinute) {
+						return this.endSecond
+					} else {
+						return 59
+					}
+				}
+				if (this.type === 'time') {
+					if (this.hour === this.endHour && this.minute === this.endMinute) {
+						return this.endSecond
+					} else {
+						return 59
+					}
+				}
+			},
+
+			/**
+			 * for i18n
+			 */
+			selectTimeText() {
+				return t("uni-datetime-picker.selectTime")
+			},
+			okText() {
+				return t("uni-datetime-picker.ok")
+			},
+			clearText() {
+				return t("uni-datetime-picker.clear")
+			},
+			cancelText() {
+				return t("uni-datetime-picker.cancel")
+			}
+		},
+
+		mounted() {
+			// #ifdef APP-NVUE
+			const res = uni.getSystemInfoSync();
+			this.fixNvueBug = {
+				top: res.windowHeight / 2,
+				left: res.windowWidth / 2
+			}
+			// #endif
+		},
+
+		methods: {
+			/**
+			 * @param {Object} item
+			 * 灏忎簬 10 鍦ㄥ墠闈㈠姞涓�0
+			 */
+
+			lessThanTen(item) {
+				return item < 10 ? '0' + item : item
+			},
+
+			/**
+			 * 瑙f瀽鏃跺垎绉掑瓧绗︿覆锛屼緥濡傦細00:00:00
+			 * @param {String} timeString
+			 */
+			parseTimeType(timeString) {
+				if (timeString) {
+					let timeArr = timeString.split(':')
+					this.hour = Number(timeArr[0])
+					this.minute = Number(timeArr[1])
+					this.second = Number(timeArr[2])
+				}
+			},
+
+			/**
+			 * 瑙f瀽閫夋嫨鍣ㄥ垵濮嬪�锛岀被鍨嬪彲浠ユ槸瀛楃涓层�鏃堕棿鎴筹紝渚嬪锛�000-10-02銆�08:30:00'銆�1610695109000
+			 * @param {String | Number} datetime
+			 */
+			initPickerValue(datetime) {
+				let defaultValue = null
+				if (datetime) {
+					defaultValue = this.compareValueWithStartAndEnd(datetime, this.start, this.end)
+				} else {
+					defaultValue = Date.now()
+					defaultValue = this.compareValueWithStartAndEnd(defaultValue, this.start, this.end)
+				}
+				this.parseValue(defaultValue)
+			},
+
+			/**
+			 * 鍒濆鍊艰鍒欙細
+			 * - 鐢ㄦ埛璁剧疆鍒濆鍊�value
+			 * 	- 璁剧疆浜嗚捣濮嬫椂闂�start銆佺粓姝㈡椂闂�end锛屽苟 start < value < end锛屽垵濮嬪�涓�value锛�鍚﹀垯鍒濆鍊间负 start
+			 * 	- 鍙缃簡璧峰鏃堕棿 start锛屽苟 start < value锛屽垵濮嬪�涓�value锛屽惁鍒欏垵濮嬪�涓�start
+			 * 	- 鍙缃簡缁堟鏃堕棿 end锛屽苟 value < end锛屽垵濮嬪�涓�value锛屽惁鍒欏垵濮嬪�涓�end
+			 * 	- 鏃犺捣濮嬬粓姝㈡椂闂达紝鍒欏垵濮嬪�涓�value
+			 * - 鏃犲垵濮嬪� value锛屽垯鍒濆鍊间负褰撳墠鏈湴鏃堕棿 Date.now()
+			 * @param {Object} value
+			 * @param {Object} dateBase
+			 */
+			compareValueWithStartAndEnd(value, start, end) {
+				let winner = null
+				value = this.superTimeStamp(value)
+				start = this.superTimeStamp(start)
+				end = this.superTimeStamp(end)
+
+				if (start && end) {
+					if (value < start) {
+						winner = new Date(start)
+					} else if (value > end) {
+						winner = new Date(end)
+					} else {
+						winner = new Date(value)
+					}
+				} else if (start && !end) {
+					winner = start <= value ? new Date(value) : new Date(start)
+				} else if (!start && end) {
+					winner = value <= end ? new Date(value) : new Date(end)
+				} else {
+					winner = new Date(value)
+				}
+
+				return winner
+			},
+
+			/**
+			 * 杞崲涓哄彲姣旇緝鐨勬椂闂存埑锛屾帴鍙楁棩鏈熴�鏃跺垎绉掋�鏃堕棿鎴�+			 * @param {Object} value
+			 */
+			superTimeStamp(value) {
+				let dateBase = ''
+				if (this.type === 'time' && value && typeof value === 'string') {
+					const now = new Date()
+					const year = now.getFullYear()
+					const month = now.getMonth() + 1
+					const day = now.getDate()
+					dateBase = year + '/' + month + '/' + day + ' '
+				}
+				if (Number(value)) {
+					value = parseInt(value)
+					dateBase = 0
+				}
+				return this.createTimeStamp(dateBase + value)
+			},
+
+			/**
+			 * 瑙f瀽榛樿鍊�value锛屽瓧绗︿覆銆佹椂闂存埑
+			 * @param {Object} defaultTime
+			 */
+			parseValue(value) {
+				if (!value) {
+					return
+				}
+				if (this.type === 'time' && typeof value === "string") {
+					this.parseTimeType(value)
+				} else {
+					let defaultDate = null
+					defaultDate = new Date(value)
+					if (this.type !== 'time') {
+						this.year = defaultDate.getFullYear()
+						this.month = defaultDate.getMonth() + 1
+						this.day = defaultDate.getDate()
+					}
+					if (this.type !== 'date') {
+						this.hour = defaultDate.getHours()
+						this.minute = defaultDate.getMinutes()
+						this.second = defaultDate.getSeconds()
+					}
+				}
+				if (this.hideSecond) {
+					this.second = 0
+				}
+			},
+
+			/**
+			 * 瑙f瀽鍙�鎷╂椂闂磋寖鍥�start銆乪nd锛屽勾鏈堟棩瀛楃涓层�鏃堕棿鎴�+			 * @param {Object} defaultTime
+			 */
+			parseDatetimeRange(point, pointType) {
+				// 鏃堕棿涓虹┖锛屽垯閲嶇疆涓哄垵濮嬪�
+				if (!point) {
+					if (pointType === 'start') {
+						this.startYear = 1920
+						this.startMonth = 1
+						this.startDay = 1
+						this.startHour = 0
+						this.startMinute = 0
+						this.startSecond = 0
+					}
+					if (pointType === 'end') {
+						this.endYear = 2120
+						this.endMonth = 12
+						this.endDay = 31
+						this.endHour = 23
+						this.endMinute = 59
+						this.endSecond = 59
+					}
+					return
+				}
+				if (this.type === 'time') {
+					const pointArr = point.split(':')
+					this[pointType + 'Hour'] = Number(pointArr[0])
+					this[pointType + 'Minute'] = Number(pointArr[1])
+					this[pointType + 'Second'] = Number(pointArr[2])
+				} else {
+					if (!point) {
+						pointType === 'start' ? this.startYear = this.year - 60 : this.endYear = this.year + 60
+						return
+					}
+					if (Number(point)) {
+						point = parseInt(point)
+					}
+					// datetime 鐨�end 娌℃湁鏃跺垎绉� 鍒欎笉闄愬埗
+					const hasTime = /[0-9]:[0-9]/
+					if (this.type === 'datetime' && pointType === 'end' && typeof point === 'string' && !hasTime.test(
+							point)) {
+						point = point + ' 23:59:59'
+					}
+					const pointDate = new Date(point)
+					this[pointType + 'Year'] = pointDate.getFullYear()
+					this[pointType + 'Month'] = pointDate.getMonth() + 1
+					this[pointType + 'Day'] = pointDate.getDate()
+					if (this.type === 'datetime') {
+						this[pointType + 'Hour'] = pointDate.getHours()
+						this[pointType + 'Minute'] = pointDate.getMinutes()
+						this[pointType + 'Second'] = pointDate.getSeconds()
+					}
+				}
+			},
+
+			// 鑾峰彇 骞淬�鏈堛�鏃ャ�鏃躲�鍒嗐�绉�褰撳墠鍙�鑼冨洿
+			getCurrentRange(value) {
+				const range = []
+				for (let i = this['min' + this.capitalize(value)]; i <= this['max' + this.capitalize(value)]; i++) {
+					range.push(i)
+				}
+				return range
+			},
+
+			// 瀛楃涓查瀛楁瘝澶у啓
+			capitalize(str) {
+				return str.charAt(0).toUpperCase() + str.slice(1)
+			},
+
+			// 妫�煡褰撳墠鍊兼槸鍚﹀湪鑼冨洿鍐咃紝涓嶅湪鍒欏綋鍓嶅�閲嶇疆涓哄彲閫夎寖鍥寸涓�」
+			checkValue(name, value, values) {
+				if (values.indexOf(value) === -1) {
+					this[name] = values[0]
+				}
+			},
+
+			// 姣忎釜鏈堢殑瀹為檯澶╂暟
+			daysInMonth(year, month) { // Use 1 for January, 2 for February, etc.
+				return new Date(year, month, 0).getDate();
+			},
+
+			/**
+			 * 鐢熸垚鏃堕棿鎴�+			 * @param {Object} time
+			 */
+			createTimeStamp(time) {
+				if (!time) return
+				if (typeof time === "number") {
+					return time
+				} else {
+					time = time.replace(/-/g, '/')
+					if (this.type === 'date') {
+						time = time + ' ' + '00:00:00'
+					}
+					return Date.parse(time)
+				}
+			},
+
+			/**
+			 * 鐢熸垚鏃ユ湡鎴栨椂闂寸殑瀛楃涓�+			 */
+			createDomSting() {
+				const yymmdd = this.year +
+					'-' +
+					this.lessThanTen(this.month) +
+					'-' +
+					this.lessThanTen(this.day)
+
+				let hhmmss = this.lessThanTen(this.hour) +
+					':' +
+					this.lessThanTen(this.minute)
+
+				if (!this.hideSecond) {
+					hhmmss = hhmmss + ':' + this.lessThanTen(this.second)
+				}
+
+				if (this.type === 'date') {
+					return yymmdd
+				} else if (this.type === 'time') {
+					return hhmmss
+				} else {
+					return yymmdd + ' ' + hhmmss
+				}
+			},
+
+			/**
+			 * 鍒濆鍖栬繑鍥炲�锛屽苟鎶涘嚭 change 浜嬩欢
+			 */
+			initTime(emit = true) {
+				this.time = this.createDomSting()
+				if (!emit) return
+				if (this.returnType === 'timestamp' && this.type !== 'time') {
+					this.$emit('change', this.createTimeStamp(this.time))
+					this.$emit('input', this.createTimeStamp(this.time))
+					this.$emit('update:modelValue', this.createTimeStamp(this.time))
+				} else {
+					this.$emit('change', this.time)
+					this.$emit('input', this.time)
+					this.$emit('update:modelValue', this.time)
+				}
+			},
+
+			/**
+			 * 鐢ㄦ埛閫夋嫨鏃ユ湡鎴栨椂闂存洿鏂�data
+			 * @param {Object} e
+			 */
+			bindDateChange(e) {
+				const val = e.detail.value
+				this.year = this.years[val[0]]
+				this.month = this.months[val[1]]
+				this.day = this.days[val[2]]
+			},
+			bindTimeChange(e) {
+				const val = e.detail.value
+				this.hour = this.hours[val[0]]
+				this.minute = this.minutes[val[1]]
+				this.second = this.seconds[val[2]]
+			},
+
+			/**
+			 * 鍒濆鍖栧脊鍑哄眰
+			 */
+			initTimePicker() {
+				if (this.disabled) return
+				const value = fixIosDateFormat(this.time)
+				this.initPickerValue(value)
+				this.visible = !this.visible
+			},
+
+			/**
+			 * 瑙﹀彂鎴栧叧闂脊妗�+			 */
+			tiggerTimePicker(e) {
+				this.visible = !this.visible
+			},
+
+			/**
+			 * 鐢ㄦ埛鐐瑰嚮鈥滄竻绌衡�鎸夐挳锛屾竻绌哄綋鍓嶅�
+			 */
+			clearTime() {
+				this.time = ''
+				this.$emit('change', this.time)
+				this.$emit('input', this.time)
+				this.$emit('update:modelValue', this.time)
+				this.tiggerTimePicker()
+			},
+
+			/**
+			 * 鐢ㄦ埛鐐瑰嚮鈥滅‘瀹氣�鎸夐挳
+			 */
+			setTime() {
+				this.initTime()
+				this.tiggerTimePicker()
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	$uni-primary: #007aff !default;
+
+	.uni-datetime-picker {
+		/* #ifndef APP-NVUE */
+		/* width: 100%; */
+		/* #endif */
+	}
+
+	.uni-datetime-picker-view {
+		height: 130px;
+		width: 270px;
+		/* #ifndef APP-NVUE */
+		cursor: pointer;
+		/* #endif */
+	}
+
+	.uni-datetime-picker-item {
+		height: 50px;
+		line-height: 50px;
+		text-align: center;
+		font-size: 14px;
+	}
+
+	.uni-datetime-picker-btn {
+		margin-top: 60px;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		cursor: pointer;
+		/* #endif */
+		flex-direction: row;
+		justify-content: space-between;
+	}
+
+	.uni-datetime-picker-btn-text {
+		font-size: 14px;
+		color: $uni-primary;
+	}
+
+	.uni-datetime-picker-btn-group {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+	}
+
+	.uni-datetime-picker-cancel {
+		margin-right: 30px;
+	}
+
+	.uni-datetime-picker-mask {
+		position: fixed;
+		bottom: 0px;
+		top: 0px;
+		left: 0px;
+		right: 0px;
+		background-color: rgba(0, 0, 0, 0.4);
+		transition-duration: 0.3s;
+		z-index: 998;
+	}
+
+	.uni-datetime-picker-popup {
+		border-radius: 8px;
+		padding: 30px;
+		width: 270px;
+		/* #ifdef APP-NVUE */
+		height: 500px;
+		/* #endif */
+		/* #ifdef APP-NVUE */
+		width: 330px;
+		/* #endif */
+		background-color: #fff;
+		position: fixed;
+		top: 50%;
+		left: 50%;
+		transform: translate(-50%, -50%);
+		transition-duration: 0.3s;
+		z-index: 999;
+	}
+
+	.fix-nvue-height {
+		/* #ifdef APP-NVUE */
+		height: 330px;
+		/* #endif */
+	}
+
+	.uni-datetime-picker-time {
+		color: grey;
+	}
+
+	.uni-datetime-picker-column {
+		height: 50px;
+	}
+
+	.uni-datetime-picker-timebox {
+
+		border: 1px solid #E5E5E5;
+		border-radius: 5px;
+		padding: 7px 10px;
+		/* #ifndef APP-NVUE */
+		box-sizing: border-box;
+		cursor: pointer;
+		/* #endif */
+	}
+
+	.uni-datetime-picker-timebox-pointer {
+		/* #ifndef APP-NVUE */
+		cursor: pointer;
+		/* #endif */
+	}
+
+
+	.uni-datetime-picker-disabled {
+		opacity: 0.4;
+		/* #ifdef H5 */
+		cursor: not-allowed !important;
+		/* #endif */
+	}
+
+	.uni-datetime-picker-text {
+		font-size: 14px;
+		line-height: 50px
+	}
+
+	.uni-datetime-picker-sign {
+		position: absolute;
+		top: 53px;
+		/* 鍑忔帀 10px 鐨勫厓绱犻珮搴︼紝鍏煎nvue */
+		color: #999;
+		/* #ifdef APP-NVUE */
+		font-size: 16px;
+		/* #endif */
+	}
+
+	.sign-left {
+		left: 86px;
+	}
+
+	.sign-right {
+		right: 86px;
+	}
+
+	.sign-center {
+		left: 135px;
+	}
+
+	.uni-datetime-picker__container-box {
+		position: relative;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		margin-top: 40px;
+	}
+
+	.time-hide-second {
+		width: 180px;
+	}
+</style>
diff --git a/uni_modules/uni-datetime-picker/components/uni-datetime-picker/uni-datetime-picker.vue b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/uni-datetime-picker.vue
new file mode 100644
index 0000000..021300f
--- /dev/null
+++ b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/uni-datetime-picker.vue
@@ -0,0 +1,1068 @@
+<template>
+	<view class="uni-date">
+		<view class="uni-date-editor" @click="show">
+			<slot>
+				<view class="uni-date-editor--x"
+					:class="{'uni-date-editor--x__disabled': disabled,'uni-date-x--border': border}">
+					<view v-if="!isRange" class="uni-date-x uni-date-single">
+						<uni-icons class="icon-calendar" type="calendar" color="#c0c4cc" size="22"></uni-icons>
+						<view class="uni-date__x-input">{{ displayValue || singlePlaceholderText }}</view>
+					</view>
+
+					<view v-else class="uni-date-x uni-date-range">
+						<uni-icons class="icon-calendar" type="calendar" color="#c0c4cc" size="22"></uni-icons>
+						<view class="uni-date__x-input text-center">{{ displayRangeValue.startDate || startPlaceholderText }}</view>
+
+						<view class="range-separator">{{rangeSeparator}}</view>
+
+						<view class="uni-date__x-input text-center">{{ displayRangeValue.endDate || endPlaceholderText }}</view>
+					</view>
+
+					<view v-if="showClearIcon" class="uni-date__icon-clear" @click.stop="clear">
+						<uni-icons type="clear" color="#c0c4cc" size="22"></uni-icons>
+					</view>
+				</view>
+			</slot>
+		</view>
+
+		<view v-show="pickerVisible" class="uni-date-mask--pc" @click="close"></view>
+
+		<view v-if="!isPhone" v-show="pickerVisible" ref="datePicker" class="uni-date-picker__container">
+			<view v-if="!isRange" class="uni-date-single--x" :style="pickerPositionStyle">
+				<view class="uni-popper__arrow"></view>
+
+				<view v-if="hasTime" class="uni-date-changed popup-x-header">
+					<input class="uni-date__input text-center" type="text" v-model="inputDate" :placeholder="selectDateText" />
+
+					<time-picker type="time" v-model="pickerTime" :border="false" :disabled="!inputDate"
+						:start="timepickerStartTime" :end="timepickerEndTime" :hideSecond="hideSecond" style="width: 100%;">
+						<input class="uni-date__input text-center" type="text" v-model="pickerTime" :placeholder="selectTimeText"
+							:disabled="!inputDate" />
+					</time-picker>
+				</view>
+
+				<Calendar ref="pcSingle" :showMonth="false" :start-date="calendarRange.startDate"
+					:end-date="calendarRange.endDate" :date="calendarDate" @change="singleChange" :default-value="defaultValue"
+					style="padding: 0 8px;" />
+
+				<view v-if="hasTime" class="popup-x-footer">
+					<text class="confirm-text" @click="confirmSingleChange">{{okText}}</text>
+				</view>
+			</view>
+
+			<view v-else class="uni-date-range--x" :style="pickerPositionStyle">
+				<view class="uni-popper__arrow"></view>
+				<view v-if="hasTime" class="popup-x-header uni-date-changed">
+					<view class="popup-x-header--datetime">
+						<input class="uni-date__input uni-date-range__input" type="text" v-model="tempRange.startDate"
+							:placeholder="startDateText" />
+
+						<time-picker type="time" v-model="tempRange.startTime" :start="timepickerStartTime" :border="false"
+							:disabled="!tempRange.startDate" :hideSecond="hideSecond">
+							<input class="uni-date__input uni-date-range__input" type="text" v-model="tempRange.startTime"
+								:placeholder="startTimeText" :disabled="!tempRange.startDate" />
+						</time-picker>
+					</view>
+
+					<uni-icons type="arrowthinright" color="#999" style="line-height: 40px;"></uni-icons>
+
+					<view class="popup-x-header--datetime">
+						<input class="uni-date__input uni-date-range__input" type="text" v-model="tempRange.endDate"
+							:placeholder="endDateText" />
+
+						<time-picker type="time" v-model="tempRange.endTime" :end="timepickerEndTime" :border="false"
+							:disabled="!tempRange.endDate" :hideSecond="hideSecond">
+							<input class="uni-date__input uni-date-range__input" type="text" v-model="tempRange.endTime"
+								:placeholder="endTimeText" :disabled="!tempRange.endDate" />
+						</time-picker>
+					</view>
+				</view>
+
+				<view class="popup-x-body">
+					<Calendar ref="left" :showMonth="false" :start-date="calendarRange.startDate"
+						:end-date="calendarRange.endDate" :range="true" :pleStatus="endMultipleStatus" @change="leftChange"
+						@firstEnterCale="updateRightCale" style="padding: 0 8px;"/>
+					<Calendar ref="right" :showMonth="false" :start-date="calendarRange.startDate"
+						:end-date="calendarRange.endDate" :range="true" @change="rightChange" :pleStatus="startMultipleStatus"
+						@firstEnterCale="updateLeftCale" style="padding: 0 8px;border-left: 1px solid #F1F1F1;" />
+				</view>
+
+				<view v-if="hasTime" class="popup-x-footer">
+					<text @click="clear">{{clearText}}</text>
+					<text class="confirm-text" @click="confirmRangeChange">{{okText}}</text>
+				</view>
+			</view>
+		</view>
+
+		<Calendar v-if="isPhone" ref="mobile" :clearDate="false" :date="calendarDate" :defTime="mobileCalendarTime"
+			:start-date="calendarRange.startDate" :end-date="calendarRange.endDate" :selectableTimes="mobSelectableTime"
+			:startPlaceholder="startPlaceholder" :endPlaceholder="endPlaceholder" :default-value="defaultValue"
+			:pleStatus="endMultipleStatus" :showMonth="false" :range="isRange" :hasTime="hasTime" :insert="false"
+			:hideSecond="hideSecond" @confirm="mobileChange" @maskClose="close" @change="calendarClick"/>
+	</view>
+</template>
+<script>
+	/**
+	 * DatetimePicker 鏃堕棿閫夋嫨鍣�+	 * @description 鍚屾椂鏀寔 PC 鍜岀Щ鍔ㄧ浣跨敤鏃ュ巻閫夋嫨鏃ユ湡鍜屾棩鏈熻寖鍥�+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=3962
+	 * @property {String} type 閫夋嫨鍣ㄧ被鍨�+	 * @property {String|Number|Array|Date} value 缁戝畾鍊�+	 * @property {String} placeholder 鍗曢�鎷╂椂鐨勫崰浣嶅唴瀹�+	 * @property {String} start 璧峰鏃堕棿
+	 * @property {String} end 缁堟鏃堕棿
+	 * @property {String} start-placeholder 鑼冨洿閫夋嫨鏃跺紑濮嬫棩鏈熺殑鍗犱綅鍐呭
+	 * @property {String} end-placeholder 鑼冨洿閫夋嫨鏃剁粨鏉熸棩鏈熺殑鍗犱綅鍐呭
+	 * @property {String} range-separator 閫夋嫨鑼冨洿鏃剁殑鍒嗛殧绗�+	 * @property {Boolean} border = [true|false] 鏄惁鏈夎竟妗�+	 * @property {Boolean} disabled = [true|false] 鏄惁绂佺敤
+	 * @property {Boolean} clearIcon = [true|false] 鏄惁鏄剧ず娓呴櫎鎸夐挳锛堜粎PC绔�鐢級
+	 * @property {[String} defaultValue 閫夋嫨鍣ㄦ墦寮�椂榛樿鏄剧ず鐨勬椂闂�+	 * @event {Function} change 纭畾鏃ユ湡鏃惰Е鍙戠殑浜嬩欢
+	 * @event {Function} maskClick 鐐瑰嚮閬僵灞傝Е鍙戠殑浜嬩欢
+	 * @event {Function} show 鎵撳紑寮瑰嚭灞�+	 * @event {Function} close 鍏抽棴寮瑰嚭灞�+	 * @event {Function} clear 娓呴櫎涓婃閫変腑鐨勭姸鎬佸拰鍊�+	 **/
+	import Calendar from './calendar.vue'
+	import TimePicker from './time-picker.vue'
+	import {
+		initVueI18n
+	} from '@dcloudio/uni-i18n'
+	import i18nMessages from './i18n/index.js'
+	import {
+		getDateTime,
+		getDate,
+		getTime,
+		getDefaultSecond,
+		dateCompare,
+		checkDate,
+		fixIosDateFormat
+	} from './util'
+
+	export default {
+		name: 'UniDatetimePicker',
+
+		options: {
+			// #ifdef MP-TOUTIAO
+			virtualHost: false,
+			// #endif
+			// #ifndef MP-TOUTIAO
+			virtualHost: true
+			// #endif
+		},
+		components: {
+			Calendar,
+			TimePicker
+		},
+		data() {
+			return {
+				isRange: false,
+				hasTime: false,
+				displayValue: '',
+				inputDate: '',
+				calendarDate: '',
+				pickerTime: '',
+				calendarRange: {
+					startDate: '',
+					startTime: '',
+					endDate: '',
+					endTime: ''
+				},
+				displayRangeValue: {
+					startDate: '',
+					endDate: '',
+				},
+				tempRange: {
+					startDate: '',
+					startTime: '',
+					endDate: '',
+					endTime: ''
+				},
+				// 宸﹀彸鏃ュ巻鍚屾鏁版嵁
+				startMultipleStatus: {
+					before: '',
+					after: '',
+					data: [],
+					fulldate: ''
+				},
+				endMultipleStatus: {
+					before: '',
+					after: '',
+					data: [],
+					fulldate: ''
+				},
+				pickerVisible: false,
+				pickerPositionStyle: null,
+				isEmitValue: false,
+				isPhone: false,
+				isFirstShow: true,
+				i18nT: () => {}
+			}
+		},
+		props: {
+			type: {
+				type: String,
+				default: 'datetime'
+			},
+			value: {
+				type: [String, Number, Array, Date],
+				default: ''
+			},
+			modelValue: {
+				type: [String, Number, Array, Date],
+				default: ''
+			},
+			start: {
+				type: [Number, String],
+				default: ''
+			},
+			end: {
+				type: [Number, String],
+				default: ''
+			},
+			returnType: {
+				type: String,
+				default: 'string'
+			},
+			placeholder: {
+				type: String,
+				default: ''
+			},
+			startPlaceholder: {
+				type: String,
+				default: ''
+			},
+			endPlaceholder: {
+				type: String,
+				default: ''
+			},
+			rangeSeparator: {
+				type: String,
+				default: '-'
+			},
+			border: {
+				type: [Boolean],
+				default: true
+			},
+			disabled: {
+				type: [Boolean],
+				default: false
+			},
+			clearIcon: {
+				type: [Boolean],
+				default: true
+			},
+			hideSecond: {
+				type: [Boolean],
+				default: false
+			},
+			defaultValue: {
+				type: [String, Object, Array],
+				default: ''
+			}
+		},
+		watch: {
+			type: {
+				immediate: true,
+				handler(newVal) {
+					this.hasTime = newVal.indexOf('time') !== -1
+					this.isRange = newVal.indexOf('range') !== -1
+				}
+			},
+			// #ifndef VUE3
+			value: {
+				immediate: true,
+				handler(newVal) {
+					if (this.isEmitValue) {
+						this.isEmitValue = false
+						return
+					}
+					this.initPicker(newVal)
+				}
+			},
+			// #endif
+			// #ifdef VUE3
+			modelValue: {
+				immediate: true,
+				handler(newVal) {
+					if (this.isEmitValue) {
+						this.isEmitValue = false
+						return
+					}
+					this.initPicker(newVal)
+				}
+			},
+			// #endif
+			start: {
+				immediate: true,
+				handler(newVal) {
+					if (!newVal) return
+					this.calendarRange.startDate = getDate(newVal)
+					if (this.hasTime) {
+						this.calendarRange.startTime = getTime(newVal)
+					}
+				}
+			},
+			end: {
+				immediate: true,
+				handler(newVal) {
+					if (!newVal) return
+					this.calendarRange.endDate = getDate(newVal)
+					if (this.hasTime) {
+						this.calendarRange.endTime = getTime(newVal, this.hideSecond)
+					}
+				}
+			},
+		},
+		computed: {
+			timepickerStartTime() {
+				const activeDate = this.isRange ? this.tempRange.startDate : this.inputDate
+				return activeDate === this.calendarRange.startDate ? this.calendarRange.startTime : ''
+			},
+			timepickerEndTime() {
+				const activeDate = this.isRange ? this.tempRange.endDate : this.inputDate
+				return activeDate === this.calendarRange.endDate ? this.calendarRange.endTime : ''
+			},
+			mobileCalendarTime() {
+				const timeRange = {
+					start: this.tempRange.startTime,
+					end: this.tempRange.endTime
+				}
+				return this.isRange ? timeRange : this.pickerTime
+			},
+			mobSelectableTime() {
+				return {
+					start: this.calendarRange.startTime,
+					end: this.calendarRange.endTime
+				}
+			},
+			datePopupWidth() {
+				// todo
+				return this.isRange ? 653 : 301
+			},
+
+			/**
+			 * for i18n
+			 */
+			singlePlaceholderText() {
+				return this.placeholder || (this.type === 'date' ? this.selectDateText : this.selectDateTimeText)
+			},
+			startPlaceholderText() {
+				return this.startPlaceholder || this.startDateText
+			},
+			endPlaceholderText() {
+				return this.endPlaceholder || this.endDateText
+			},
+			selectDateText() {
+				return this.i18nT("uni-datetime-picker.selectDate")
+			},
+			selectDateTimeText() {
+				return this.i18nT("uni-datetime-picker.selectDateTime")
+			},
+			selectTimeText() {
+				return this.i18nT("uni-datetime-picker.selectTime")
+			},
+			startDateText() {
+				return this.startPlaceholder || this.i18nT("uni-datetime-picker.startDate")
+			},
+			startTimeText() {
+				return this.i18nT("uni-datetime-picker.startTime")
+			},
+			endDateText() {
+				return this.endPlaceholder || this.i18nT("uni-datetime-picker.endDate")
+			},
+			endTimeText() {
+				return this.i18nT("uni-datetime-picker.endTime")
+			},
+			okText() {
+				return this.i18nT("uni-datetime-picker.ok")
+			},
+			clearText() {
+				return this.i18nT("uni-datetime-picker.clear")
+			},
+			showClearIcon() {
+				return this.clearIcon && !this.disabled && (this.displayValue || (this.displayRangeValue.startDate && this
+					.displayRangeValue.endDate))
+			}
+		},
+		created() {
+			this.initI18nT()
+			this.platform()
+		},
+		methods: {
+			initI18nT() {
+				const vueI18n = initVueI18n(i18nMessages)
+				this.i18nT = vueI18n.t
+			},
+			initPicker(newVal) {
+				if ((!newVal && !this.defaultValue) || Array.isArray(newVal) && !newVal.length) {
+					this.$nextTick(() => {
+						this.clear(false)
+					})
+					return
+				}
+
+				if (!Array.isArray(newVal) && !this.isRange) {
+					if (newVal) {
+						this.displayValue = this.inputDate = this.calendarDate = getDate(newVal)
+						if (this.hasTime) {
+							this.pickerTime = getTime(newVal, this.hideSecond)
+							this.displayValue = `${this.displayValue} ${this.pickerTime}`
+						}
+					} else if (this.defaultValue) {
+						this.inputDate = this.calendarDate = getDate(this.defaultValue)
+						if (this.hasTime) {
+							this.pickerTime = getTime(this.defaultValue, this.hideSecond)
+						}
+					}
+				} else {
+					const [before, after] = newVal
+					if (!before && !after) return
+					const beforeDate = getDate(before)
+					const beforeTime = getTime(before, this.hideSecond)
+
+					const afterDate = getDate(after)
+					const afterTime = getTime(after, this.hideSecond)
+					const startDate = beforeDate
+					const endDate = afterDate
+					this.displayRangeValue.startDate = this.tempRange.startDate = startDate
+					this.displayRangeValue.endDate = this.tempRange.endDate = endDate
+
+					if (this.hasTime) {
+						this.displayRangeValue.startDate = `${beforeDate} ${beforeTime}`
+						this.displayRangeValue.endDate = `${afterDate} ${afterTime}`
+						this.tempRange.startTime = beforeTime
+						this.tempRange.endTime = afterTime
+					}
+					const defaultRange = {
+						before: beforeDate,
+						after: afterDate
+					}
+					this.startMultipleStatus = Object.assign({}, this.startMultipleStatus, defaultRange, {
+						which: 'right'
+					})
+					this.endMultipleStatus = Object.assign({}, this.endMultipleStatus, defaultRange, {
+						which: 'left'
+					})
+				}
+			},
+			updateLeftCale(e) {
+				const left = this.$refs.left
+				// 璁剧疆鑼冨洿閫�+				left.cale.setHoverMultiple(e.after)
+				left.setDate(this.$refs.left.nowDate.fullDate)
+			},
+			updateRightCale(e) {
+				const right = this.$refs.right
+				// 璁剧疆鑼冨洿閫�+				right.cale.setHoverMultiple(e.after)
+				right.setDate(this.$refs.right.nowDate.fullDate)
+			},
+			platform() {
+				if (typeof navigator !== "undefined") {
+					this.isPhone = navigator.userAgent.toLowerCase().indexOf('mobile') !== -1
+					return
+				}
+				// #ifdef MP-WEIXIN
+				const {
+					windowWidth
+				} = uni.getWindowInfo()
+				// #endif
+				// #ifndef MP-WEIXIN
+				const {
+					windowWidth
+				} = uni.getSystemInfoSync()
+				// #endif
+				this.isPhone = windowWidth <= 500
+				this.windowWidth = windowWidth
+			},
+			show() {
+				this.$emit("show")
+				if (this.disabled) {
+					return
+				}
+				this.platform()
+				if (this.isPhone) {
+					setTimeout(() => {
+						this.$refs.mobile.open()
+					}, 0);
+					return
+				}
+				this.pickerPositionStyle = {
+					top: '10px'
+				}
+				const dateEditor = uni.createSelectorQuery().in(this).select(".uni-date-editor")
+				dateEditor.boundingClientRect(rect => {
+					if (this.windowWidth - rect.left < this.datePopupWidth) {
+						this.pickerPositionStyle.right = 0
+					}
+				}).exec()
+				setTimeout(() => {
+					this.pickerVisible = !this.pickerVisible
+					if (!this.isPhone && this.isRange && this.isFirstShow) {
+						this.isFirstShow = false
+						const {
+							startDate,
+							endDate
+						} = this.calendarRange
+						if (startDate && endDate) {
+							if (this.diffDate(startDate, endDate) < 30) {
+								this.$refs.right.changeMonth('pre')
+							}
+						} else {
+							// this.$refs.right.changeMonth('next')
+							if (this.isPhone) {
+								this.$refs.right.cale.lastHover = false;
+							}
+						}
+					}
+
+				}, 50)
+			},
+			close() {
+				setTimeout(() => {
+					this.pickerVisible = false
+					this.$emit('maskClick', this.value)
+					this.$refs.mobile && this.$refs.mobile.close()
+				}, 20)
+			},
+			setEmit(value) {
+				if (this.returnType === "timestamp" || this.returnType === "date") {
+					if (!Array.isArray(value)) {
+						if (!this.hasTime) {
+							value = value + ' ' + '00:00:00'
+						}
+						value = this.createTimestamp(value)
+						if (this.returnType === "date") {
+							value = new Date(value)
+						}
+					} else {
+						if (!this.hasTime) {
+							value[0] = value[0] + ' ' + '00:00:00'
+							value[1] = value[1] + ' ' + '00:00:00'
+						}
+						value[0] = this.createTimestamp(value[0])
+						value[1] = this.createTimestamp(value[1])
+						if (this.returnType === "date") {
+							value[0] = new Date(value[0])
+							value[1] = new Date(value[1])
+						}
+					}
+				}
+
+				this.$emit('update:modelValue', value)
+				this.$emit('input', value)
+				this.$emit('change', value)
+				this.isEmitValue = true
+			},
+			createTimestamp(date) {
+				date = fixIosDateFormat(date)
+				return Date.parse(new Date(date))
+			},
+			singleChange(e) {
+				this.calendarDate = this.inputDate = e.fulldate
+				if (this.hasTime) return
+				this.confirmSingleChange()
+			},
+			confirmSingleChange() {
+				if (!checkDate(this.inputDate)) {
+					const now = new Date()
+					this.calendarDate = this.inputDate = getDate(now)
+					this.pickerTime = getTime(now, this.hideSecond)
+				}
+
+				let startLaterInputDate = false
+				let startDate, startTime
+				if (this.start) {
+					let startString = this.start
+					if (typeof this.start === 'number') {
+						startString = getDateTime(this.start, this.hideSecond)
+					}
+					[startDate, startTime] = startString.split(' ')
+					if (this.start && !dateCompare(startDate, this.inputDate)) {
+						startLaterInputDate = true
+						this.inputDate = startDate
+					}
+				}
+
+				let endEarlierInputDate = false
+				let endDate, endTime
+				if (this.end) {
+					let endString = this.end
+					if (typeof this.end === 'number') {
+						endString = getDateTime(this.end, this.hideSecond)
+					}
+					[endDate, endTime] = endString.split(' ')
+					if (this.end && !dateCompare(this.inputDate, endDate)) {
+						endEarlierInputDate = true
+						this.inputDate = endDate
+					}
+				}
+				if (this.hasTime) {
+					if (startLaterInputDate) {
+						this.pickerTime = startTime || getDefaultSecond(this.hideSecond)
+					}
+					if (endEarlierInputDate) {
+						this.pickerTime = endTime || getDefaultSecond(this.hideSecond)
+					}
+					if (!this.pickerTime) {
+						this.pickerTime = getTime(Date.now(), this.hideSecond)
+					}
+					this.displayValue = `${this.inputDate} ${this.pickerTime}`
+				} else {
+					this.displayValue = this.inputDate
+				}
+				this.setEmit(this.displayValue)
+				this.pickerVisible = false
+			},
+			leftChange(e) {
+				const {
+					before,
+					after
+				} = e.range
+				this.rangeChange(before, after)
+				const obj = {
+					before: e.range.before,
+					after: e.range.after,
+					data: e.range.data,
+					fulldate: e.fulldate
+				}
+				this.startMultipleStatus = Object.assign({}, this.startMultipleStatus, obj)
+				this.$emit('calendarClick', e)
+			},
+			rightChange(e) {
+				const {
+					before,
+					after
+				} = e.range
+				this.rangeChange(before, after)
+				const obj = {
+					before: e.range.before,
+					after: e.range.after,
+					data: e.range.data,
+					fulldate: e.fulldate
+				}
+				this.endMultipleStatus = Object.assign({}, this.endMultipleStatus, obj)
+				this.$emit('calendarClick', e)
+			},
+			mobileChange(e) {
+				if (this.isRange) {
+					const {
+						before,
+						after
+					} = e.range
+					if (!before) {
+						return;
+					}
+
+					this.handleStartAndEnd(before, after, true)
+					if (this.hasTime) {
+						const {
+							startTime,
+							endTime
+						} = e.timeRange
+						this.tempRange.startTime = startTime
+						this.tempRange.endTime = endTime
+					}
+					this.confirmRangeChange()
+				} else {
+					if (this.hasTime) {
+						this.displayValue = e.fulldate + ' ' + e.time
+					} else {
+						this.displayValue = e.fulldate
+					}
+					this.setEmit(this.displayValue)
+					this.calendarDate = this.displayValue;
+				}
+				this.$refs.mobile.close()
+			},
+			rangeChange(before, after) {
+				if (!(before && after)) return
+				this.handleStartAndEnd(before, after, true)
+				if (this.hasTime) return
+				this.confirmRangeChange()
+			},
+			confirmRangeChange() {
+				if (!this.tempRange.startDate || !this.tempRange.endDate) {
+					this.pickerVisible = false
+					return
+				}
+				if (!checkDate(this.tempRange.startDate)) {
+					this.tempRange.startDate = getDate(Date.now())
+				}
+				if (!checkDate(this.tempRange.endDate)) {
+					this.tempRange.endDate = getDate(Date.now())
+				}
+
+				let start, end
+
+				let startDateLaterRangeStartDate = false
+				let startDateLaterRangeEndDate = false
+				let startDate, startTime
+
+				let compareStartDateString = this.tempRange.startDate
+				let compareEndDateString = this.tempRange.endDate
+				if (this.hasTime) {
+					compareStartDateString = `${this.tempRange.startDate} ${this.tempRange.startTime}`
+					compareEndDateString = `${this.tempRange.endDate} ${this.tempRange.endTime}`
+				}
+
+				if (this.start) {
+					let startString = this.start
+					if (typeof this.start === 'number') {
+						startString = getDateTime(this.start, this.hideSecond)
+					}
+					[startDate, startTime] = startString.split(' ')
+					if (this.start && !dateCompare(this.start, compareStartDateString)) {
+						startDateLaterRangeStartDate = true
+						this.tempRange.startDate = startDate
+					}
+					if (this.start && !dateCompare(this.start, compareEndDateString)) {
+						startDateLaterRangeEndDate = true
+						this.tempRange.endDate = startDate
+					}
+				}
+				let endDateEarlierRangeStartDate = false
+				let endDateEarlierRangeEndDate = false
+				let endDate, endTime
+				if (this.end) {
+					let endString = this.end
+					if (typeof this.end === 'number') {
+						endString = getDateTime(this.end, this.hideSecond)
+					}
+					[endDate, endTime] = endString.split(' ')
+
+					if (this.end && !dateCompare(compareStartDateString, this.end)) {
+						endDateEarlierRangeStartDate = true
+						this.tempRange.startDate = endDate
+					}
+					if (this.end && !dateCompare(compareEndDateString, this.end)) {
+						endDateEarlierRangeEndDate = true
+						this.tempRange.endDate = endDate
+					}
+				}
+				if (!this.hasTime) {
+					start = this.displayRangeValue.startDate = this.tempRange.startDate
+					end = this.displayRangeValue.endDate = this.tempRange.endDate
+				} else {
+					if (startDateLaterRangeStartDate) {
+						this.tempRange.startTime = startTime || getDefaultSecond(this.hideSecond)
+					} else if (endDateEarlierRangeStartDate) {
+						this.tempRange.startTime = endTime || getDefaultSecond(this.hideSecond)
+					}
+					if (!this.tempRange.startTime) {
+						this.tempRange.startTime = getTime(Date.now(), this.hideSecond)
+					}
+
+					if (startDateLaterRangeEndDate) {
+						this.tempRange.endTime = startTime || getDefaultSecond(this.hideSecond)
+					} else if (endDateEarlierRangeEndDate) {
+						this.tempRange.endTime = endTime || getDefaultSecond(this.hideSecond)
+					}
+					if (!this.tempRange.endTime) {
+						this.tempRange.endTime = getTime(Date.now(), this.hideSecond)
+					}
+					start = this.displayRangeValue.startDate = `${this.tempRange.startDate} ${this.tempRange.startTime}`
+					end = this.displayRangeValue.endDate = `${this.tempRange.endDate} ${this.tempRange.endTime}`
+				}
+				if (!dateCompare(start, end)) {
+					[start, end] = [end, start]
+				}
+				this.displayRangeValue.startDate = start
+				this.displayRangeValue.endDate = end
+				const displayRange = [start, end]
+				this.setEmit(displayRange)
+				this.pickerVisible = false
+			},
+			handleStartAndEnd(before, after, temp = false) {
+				if (!before) return
+				if (!after) after = before;
+				const type = temp ? 'tempRange' : 'range'
+				const isStartEarlierEnd = dateCompare(before, after)
+				this[type].startDate = isStartEarlierEnd ? before : after
+				this[type].endDate = isStartEarlierEnd ? after : before
+			},
+			/**
+			 * 姣旇緝鏃堕棿澶у皬
+			 */
+			dateCompare(startDate, endDate) {
+				// 璁$畻鎴鏃堕棿
+				startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
+				// 璁$畻璇︾粏椤圭殑鎴鏃堕棿
+				endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
+				return startDate <= endDate
+			},
+
+			/**
+			 * 姣旇緝鏃堕棿宸�+			 */
+			diffDate(startDate, endDate) {
+				// 璁$畻鎴鏃堕棿
+				startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
+				// 璁$畻璇︾粏椤圭殑鎴鏃堕棿
+				endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
+				const diff = (endDate - startDate) / (24 * 60 * 60 * 1000)
+				return Math.abs(diff)
+			},
+
+			clear(needEmit = true) {
+				if (!this.isRange) {
+					this.displayValue = ''
+					this.inputDate = ''
+					this.pickerTime = ''
+					if (this.isPhone) {
+						this.$refs.mobile && this.$refs.mobile.clearCalender()
+					} else {
+						this.$refs.pcSingle && this.$refs.pcSingle.clearCalender()
+					}
+					if (needEmit) {
+						this.$emit('change', '')
+						this.$emit('input', '')
+						this.$emit('update:modelValue', '')
+					}
+				} else {
+					this.displayRangeValue.startDate = ''
+					this.displayRangeValue.endDate = ''
+					this.tempRange.startDate = ''
+					this.tempRange.startTime = ''
+					this.tempRange.endDate = ''
+					this.tempRange.endTime = ''
+					if (this.isPhone) {
+						this.$refs.mobile && this.$refs.mobile.clearCalender()
+					} else {
+						this.$refs.left && this.$refs.left.clearCalender()
+						this.$refs.right && this.$refs.right.clearCalender()
+						this.$refs.right && this.$refs.right.changeMonth('next')
+					}
+					if (needEmit) {
+						this.$emit('change', [])
+						this.$emit('input', [])
+						this.$emit('update:modelValue', [])
+					}
+				}
+			},
+
+			calendarClick(e) {
+				this.$emit('calendarClick', e)
+			}
+		}
+	}
+</script>
+
+<style lang="scss">
+	$uni-primary: #007aff !default;
+
+	.uni-date {
+		width: 100%;
+		flex: 1;
+	}
+
+	.uni-date-x {
+		display: flex;
+		flex-direction: row;
+		align-items: center;
+		justify-content: center;
+		border-radius: 4px;
+		background-color: #fff;
+		color: #666;
+		font-size: 14px;
+		flex: 1;
+
+		.icon-calendar {
+			padding-left: 3px;
+		}
+
+		.range-separator {
+			height: 35px;
+			/* #ifndef MP */
+			padding: 0 2px;
+			/* #endif */
+			line-height: 35px;
+		}
+	}
+
+	.uni-date-x--border {
+		box-sizing: border-box;
+		border-radius: 4px;
+		border: 1px solid #e5e5e5;
+	}
+
+	.uni-date-editor--x {
+		display: flex;
+		align-items: center;
+		position: relative;
+	}
+
+	.uni-date-editor--x .uni-date__icon-clear {
+		padding-right: 3px;
+		display: flex;
+		align-items: center;
+		/* #ifdef H5 */
+		cursor: pointer;
+		/* #endif */
+	}
+
+	.uni-date__x-input {
+		width: auto;
+		height: 35px;
+		/* #ifndef MP */
+		padding-left: 5px;
+		/* #endif */
+		position: relative;
+		flex: 1;
+		line-height: 35px;
+		font-size: 14px;
+		overflow: hidden;
+	}
+
+	.text-center {
+		text-align: center;
+	}
+
+	.uni-date__input {
+		height: 40px;
+		width: 100%;
+		line-height: 40px;
+		font-size: 14px;
+	}
+
+	.uni-date-range__input {
+		text-align: center;
+		max-width: 142px;
+	}
+
+	.uni-date-picker__container {
+		position: relative;
+	}
+
+	.uni-date-mask--pc {
+		position: fixed;
+		bottom: 0px;
+		top: 0px;
+		left: 0px;
+		right: 0px;
+		background-color: rgba(0, 0, 0, 0);
+		transition-duration: 0.3s;
+		z-index: 996;
+	}
+
+	.uni-date-single--x {
+		background-color: #fff;
+		position: absolute;
+		top: 0;
+		z-index: 999;
+		border: 1px solid #EBEEF5;
+		box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+		border-radius: 4px;
+	}
+
+	.uni-date-range--x {
+		background-color: #fff;
+		position: absolute;
+		top: 0;
+		z-index: 999;
+		border: 1px solid #EBEEF5;
+		box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+		border-radius: 4px;
+	}
+
+	.uni-date-editor--x__disabled {
+		opacity: 0.4;
+		cursor: default;
+	}
+
+	.uni-date-editor--logo {
+		width: 16px;
+		height: 16px;
+		vertical-align: middle;
+	}
+
+	/* 娣诲姞鏃堕棿 */
+	.popup-x-header {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+	}
+
+	.popup-x-header--datetime {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		flex: 1;
+	}
+
+	.popup-x-body {
+		display: flex;
+	}
+
+	.popup-x-footer {
+		padding: 0 15px;
+		border-top-color: #F1F1F1;
+		border-top-style: solid;
+		border-top-width: 1px;
+		line-height: 40px;
+		text-align: right;
+		color: #666;
+	}
+
+	.popup-x-footer text:hover {
+		color: $uni-primary;
+		cursor: pointer;
+		opacity: 0.8;
+	}
+
+	.popup-x-footer .confirm-text {
+		margin-left: 20px;
+		color: $uni-primary;
+	}
+
+	.uni-date-changed {
+		text-align: center;
+		color: #333;
+		border-bottom-color: #F1F1F1;
+		border-bottom-style: solid;
+		border-bottom-width: 1px;
+	}
+
+	.uni-date-changed .uni-date-changed--time {
+		flex: 1;
+	}
+
+	.uni-date-changed--time-date {
+		color: #333;
+		opacity: 0.6;
+	}
+
+	.mr-50 {
+		margin-right: 50px;
+	}
+
+	/* picker 寮瑰嚭灞傞�鐢ㄧ殑鎸囩ず灏忎笁瑙� todo锛氭墿灞曡嚦涓婁笅宸﹀彸鏂瑰悜瀹氫綅 */
+	.uni-popper__arrow,
+	.uni-popper__arrow::after {
+		position: absolute;
+		display: block;
+		width: 0;
+		height: 0;
+		border: 6px solid transparent;
+		border-top-width: 0;
+	}
+
+	.uni-popper__arrow {
+		filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03));
+		top: -6px;
+		left: 10%;
+		margin-right: 3px;
+		border-bottom-color: #EBEEF5;
+	}
+
+	.uni-popper__arrow::after {
+		content: " ";
+		top: 1px;
+		margin-left: -6px;
+		border-bottom-color: #fff;
+	}
+</style>
diff --git a/uni_modules/uni-datetime-picker/components/uni-datetime-picker/util.js b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/util.js
new file mode 100644
index 0000000..00888d2
--- /dev/null
+++ b/uni_modules/uni-datetime-picker/components/uni-datetime-picker/util.js
@@ -0,0 +1,421 @@
+class Calendar {
+	constructor({
+		selected,
+		startDate,
+		endDate,
+		range,
+	} = {}) {
+		// 褰撳墠鏃ユ湡
+		this.date = this.getDateObj(new Date()) // 褰撳墠鍒濆叆鏃ユ湡
+		// 鎵撶偣淇℃伅
+		this.selected = selected || [];
+		// 璧峰鏃堕棿
+		this.startDate = startDate
+		// 缁堟鏃堕棿
+		this.endDate = endDate
+		// 鏄惁鑼冨洿閫夋嫨
+		this.range = range
+		// 澶氶�鐘舵�
+		this.cleanMultipleStatus()
+		// 姣忓懆鏃ユ湡
+		this.weeks = {}
+		this.lastHover = false
+	}
+	/**
+	 * 璁剧疆鏃ユ湡
+	 * @param {Object} date
+	 */
+	setDate(date) {
+		const selectDate = this.getDateObj(date)
+		this.getWeeks(selectDate.fullDate)
+	}
+
+	/**
+	 * 娓呯悊澶氶�鐘舵�
+	 */
+	cleanMultipleStatus() {
+		this.multipleStatus = {
+			before: '',
+			after: '',
+			data: []
+		}
+	}
+
+	setStartDate(startDate) {
+		this.startDate = startDate
+	}
+
+	setEndDate(endDate) {
+		this.endDate = endDate
+	}
+
+	getPreMonthObj(date) {
+		date = fixIosDateFormat(date)
+		date = new Date(date)
+
+		const oldMonth = date.getMonth()
+		date.setMonth(oldMonth - 1)
+		const newMonth = date.getMonth()
+		if (oldMonth !== 0 && newMonth - oldMonth === 0) {
+			date.setMonth(newMonth - 1)
+		}
+		return this.getDateObj(date)
+	}
+	getNextMonthObj(date) {
+		date = fixIosDateFormat(date)
+		date = new Date(date)
+
+		const oldMonth = date.getMonth()
+		date.setMonth(oldMonth + 1)
+		const newMonth = date.getMonth()
+		if (newMonth - oldMonth > 1) {
+			date.setMonth(newMonth - 1)
+		}
+		return this.getDateObj(date)
+	}
+
+	/**
+	 * 鑾峰彇鎸囧畾鏍煎紡Date瀵硅薄
+	 */
+	getDateObj(date) {
+		date = fixIosDateFormat(date)
+		date = new Date(date)
+
+		return {
+			fullDate: getDate(date),
+			year: date.getFullYear(),
+			month: addZero(date.getMonth() + 1),
+			date: addZero(date.getDate()),
+			day: date.getDay()
+		}
+	}
+
+	/**
+	 * 鑾峰彇涓婁竴涓湀鏃ユ湡闆嗗悎
+	 */
+	getPreMonthDays(amount, dateObj) {
+		const result = []
+		for (let i = amount - 1; i >= 0; i--) {
+			const month = dateObj.month - 1
+			result.push({
+				date: new Date(dateObj.year, month, -i).getDate(),
+				month,
+				disable: true
+			})
+		}
+		return result
+	}
+	/**
+	 * 鑾峰彇鏈湀鏃ユ湡闆嗗悎
+	 */
+	getCurrentMonthDays(amount, dateObj) {
+		const result = []
+		const fullDate = this.date.fullDate
+		for (let i = 1; i <= amount; i++) {
+			const currentDate = `${dateObj.year}-${dateObj.month}-${addZero(i)}`
+			const isToday = fullDate === currentDate
+			// 鑾峰彇鎵撶偣淇℃伅
+			const info = this.selected && this.selected.find((item) => {
+				if (this.dateEqual(currentDate, item.date)) {
+					return item
+				}
+			})
+
+			// 鏃ユ湡绂佺敤
+			let disableBefore = true
+			let disableAfter = true
+			if (this.startDate) {
+				disableBefore = dateCompare(this.startDate, currentDate)
+			}
+
+			if (this.endDate) {
+				disableAfter = dateCompare(currentDate, this.endDate)
+			}
+
+			let multiples = this.multipleStatus.data
+			let multiplesStatus = -1
+			if (this.range && multiples) {
+				multiplesStatus = multiples.findIndex((item) => {
+					return this.dateEqual(item, currentDate)
+				})
+			}
+			const checked = multiplesStatus !== -1
+
+			result.push({
+				fullDate: currentDate,
+				year: dateObj.year,
+				date: i,
+				multiple: this.range ? checked : false,
+				beforeMultiple: this.isLogicBefore(currentDate, this.multipleStatus.before, this.multipleStatus.after),
+				afterMultiple: this.isLogicAfter(currentDate, this.multipleStatus.before, this.multipleStatus.after),
+				month: dateObj.month,
+				disable: (this.startDate && !dateCompare(this.startDate, currentDate)) || (this.endDate && !dateCompare(
+					currentDate, this.endDate)),
+				isToday,
+				userChecked: false,
+				extraInfo: info
+			})
+		}
+		return result
+	}
+	/**
+	 * 鑾峰彇涓嬩竴涓湀鏃ユ湡闆嗗悎
+	 */
+	_getNextMonthDays(amount, dateObj) {
+		const result = []
+		const month = dateObj.month + 1
+		for (let i = 1; i <= amount; i++) {
+			result.push({
+				date: i,
+				month,
+				disable: true
+			})
+		}
+		return result
+	}
+
+	/**
+	 * 鑾峰彇褰撳墠鏃ユ湡璇︽儏
+	 * @param {Object} date
+	 */
+	getInfo(date) {
+		if (!date) {
+			date = new Date()
+		}
+		const res = this.calendar.find(item => item.fullDate === this.getDateObj(date).fullDate)
+		return res ? res : this.getDateObj(date)
+	}
+
+	/**
+	 * 姣旇緝鏃堕棿鏄惁鐩哥瓑
+	 */
+	dateEqual(before, after) {
+		before = new Date(fixIosDateFormat(before))
+		after = new Date(fixIosDateFormat(after))
+		return before.valueOf() === after.valueOf()
+	}
+
+	/**
+	 *  姣旇緝鐪熷疄璧峰鏃ユ湡
+	 */
+
+	isLogicBefore(currentDate, before, after) {
+		let logicBefore = before
+		if (before && after) {
+			logicBefore = dateCompare(before, after) ? before : after
+		}
+		return this.dateEqual(logicBefore, currentDate)
+	}
+
+	isLogicAfter(currentDate, before, after) {
+		let logicAfter = after
+		if (before && after) {
+			logicAfter = dateCompare(before, after) ? after : before
+		}
+		return this.dateEqual(logicAfter, currentDate)
+	}
+
+	/**
+	 * 鑾峰彇鏃ユ湡鑼冨洿鍐呮墍鏈夋棩鏈�+	 * @param {Object} begin
+	 * @param {Object} end
+	 */
+	geDateAll(begin, end) {
+		var arr = []
+		var ab = begin.split('-')
+		var ae = end.split('-')
+		var db = new Date()
+		db.setFullYear(ab[0], ab[1] - 1, ab[2])
+		var de = new Date()
+		de.setFullYear(ae[0], ae[1] - 1, ae[2])
+		var unixDb = db.getTime() - 24 * 60 * 60 * 1000
+		var unixDe = de.getTime() - 24 * 60 * 60 * 1000
+		for (var k = unixDb; k <= unixDe;) {
+			k = k + 24 * 60 * 60 * 1000
+			arr.push(this.getDateObj(new Date(parseInt(k))).fullDate)
+		}
+		return arr
+	}
+
+	/**
+	 *  鑾峰彇澶氶�鐘舵�
+	 */
+	setMultiple(fullDate) {
+		if (!this.range) return
+
+		let {
+			before,
+			after
+		} = this.multipleStatus
+		if (before && after) {
+			if (!this.lastHover) {
+				this.lastHover = true
+				return
+			}
+			this.multipleStatus.before = fullDate
+			this.multipleStatus.after = ''
+			this.multipleStatus.data = []
+			this.multipleStatus.fulldate = ''
+			this.lastHover = false
+		} else {
+			if (!before) {
+				this.multipleStatus.before = fullDate
+				this.multipleStatus.after = undefined;
+				this.lastHover = false
+			} else {
+				this.multipleStatus.after = fullDate
+				if (dateCompare(this.multipleStatus.before, this.multipleStatus.after)) {
+					this.multipleStatus.data = this.geDateAll(this.multipleStatus.before, this.multipleStatus
+						.after);
+				} else {
+					this.multipleStatus.data = this.geDateAll(this.multipleStatus.after, this.multipleStatus
+						.before);
+				}
+				this.lastHover = true
+			}
+		}
+		this.getWeeks(fullDate)
+	}
+
+	/**
+	 *  榧犳爣 hover 鏇存柊澶氶�鐘舵�
+	 */
+	setHoverMultiple(fullDate) {
+		//鎶栭煶灏忕▼搴忕偣鍑讳細瑙﹀彂hover浜嬩欢锛岄渶瑕侀伩鍏嶄竴涓�+		// #ifndef MP-TOUTIAO
+		if (!this.range || this.lastHover) return
+		const {
+			before
+		} = this.multipleStatus
+
+		if (!before) {
+			this.multipleStatus.before = fullDate
+		} else {
+			this.multipleStatus.after = fullDate
+			if (dateCompare(this.multipleStatus.before, this.multipleStatus.after)) {
+				this.multipleStatus.data = this.geDateAll(this.multipleStatus.before, this.multipleStatus.after);
+			} else {
+				this.multipleStatus.data = this.geDateAll(this.multipleStatus.after, this.multipleStatus.before);
+			}
+		}
+		this.getWeeks(fullDate)
+		// #endif
+
+	}
+
+	/**
+	 * 鏇存柊榛樿鍊煎閫夌姸鎬�+	 */
+	setDefaultMultiple(before, after) {
+		this.multipleStatus.before = before
+		this.multipleStatus.after = after
+		if (before && after) {
+			if (dateCompare(before, after)) {
+				this.multipleStatus.data = this.geDateAll(before, after);
+				this.getWeeks(after)
+			} else {
+				this.multipleStatus.data = this.geDateAll(after, before);
+				this.getWeeks(before)
+			}
+		}
+	}
+
+	/**
+	 * 鑾峰彇姣忓懆鏁版嵁
+	 * @param {Object} dateData
+	 */
+	getWeeks(dateData) {
+		const {
+			year,
+			month,
+		} = this.getDateObj(dateData)
+
+		const preMonthDayAmount = new Date(year, month - 1, 1).getDay()
+		const preMonthDays = this.getPreMonthDays(preMonthDayAmount, this.getDateObj(dateData))
+
+		const currentMonthDayAmount = new Date(year, month, 0).getDate()
+		const currentMonthDays = this.getCurrentMonthDays(currentMonthDayAmount, this.getDateObj(dateData))
+
+		const nextMonthDayAmount = 42 - preMonthDayAmount - currentMonthDayAmount
+		const nextMonthDays = this._getNextMonthDays(nextMonthDayAmount, this.getDateObj(dateData))
+
+		const calendarDays = [...preMonthDays, ...currentMonthDays, ...nextMonthDays]
+
+		const weeks = new Array(6)
+		for (let i = 0; i < calendarDays.length; i++) {
+			const index = Math.floor(i / 7)
+			if (!weeks[index]) {
+				weeks[index] = new Array(7)
+			}
+			weeks[index][i % 7] = calendarDays[i]
+		}
+
+		this.calendar = calendarDays
+		this.weeks = weeks
+	}
+}
+
+function getDateTime(date, hideSecond) {
+	return `${getDate(date)} ${getTime(date, hideSecond)}`
+}
+
+function getDate(date) {
+	date = fixIosDateFormat(date)
+	date = new Date(date)
+	const year = date.getFullYear()
+	const month = date.getMonth() + 1
+	const day = date.getDate()
+	return `${year}-${addZero(month)}-${addZero(day)}`
+}
+
+function getTime(date, hideSecond) {
+	date = fixIosDateFormat(date)
+	date = new Date(date)
+	const hour = date.getHours()
+	const minute = date.getMinutes()
+	const second = date.getSeconds()
+	return hideSecond ? `${addZero(hour)}:${addZero(minute)}` : `${addZero(hour)}:${addZero(minute)}:${addZero(second)}`
+}
+
+function addZero(num) {
+	if (num < 10) {
+		num = `0${num}`
+	}
+	return num
+}
+
+function getDefaultSecond(hideSecond) {
+	return hideSecond ? '00:00' : '00:00:00'
+}
+
+function dateCompare(startDate, endDate) {
+	startDate = new Date(fixIosDateFormat(typeof startDate === 'string' ? startDate.trim() : startDate))
+	endDate = new Date(fixIosDateFormat(typeof endDate === 'string' ? endDate.trim() : endDate))
+	return startDate <= endDate
+}
+
+function checkDate(date) {
+	const dateReg = /((19|20)\d{2})(-|\/)\d{1,2}(-|\/)\d{1,2}/g
+	return date.match(dateReg)
+}
+//ios浣庣増鏈�5鍙婁互涓嬶紝鏃犳硶鍖归厤 娌℃湁 鈥欑鈥�鏃剁殑鎯呭喌锛屾墍浠ラ渶瑕佸湪鏈熬 绉�鍔犱笂 闂彿
+const dateTimeReg = /^\d{4}-(0?[1-9]|1[012])-(0?[1-9]|[12][0-9]|3[01])( [0-5]?[0-9]:[0-5]?[0-9](:[0-5]?[0-9])?)?$/;
+
+function fixIosDateFormat(value) {
+	if (typeof value === 'string' && dateTimeReg.test(value)) {
+		value = value.replace(/-/g, '/')
+	}
+	return value
+}
+
+export {
+	Calendar,
+	getDateTime,
+	getDate,
+	getTime,
+	addZero,
+	getDefaultSecond,
+	dateCompare,
+	checkDate,
+	fixIosDateFormat
+}
diff --git a/uni_modules/uni-datetime-picker/package.json b/uni_modules/uni-datetime-picker/package.json
new file mode 100644
index 0000000..55b8155
--- /dev/null
+++ b/uni_modules/uni-datetime-picker/package.json
@@ -0,0 +1,107 @@
+{
+  "id": "uni-datetime-picker",
+  "displayName": "uni-datetime-picker 鏃ユ湡閫夋嫨鍣�,
+  "version": "2.2.42",
+  "description": "uni-datetime-picker 鏃ユ湡鏃堕棿閫夋嫨鍣紝鏀寔鏃ュ巻锛屾敮鎸佽寖鍥撮�鎷�,
+  "keywords": [
+    "uni-datetime-picker",
+    "uni-ui",
+    "uniui",
+    "鏃ユ湡鏃堕棿閫夋嫨鍣�,
+    "鏃ユ湡鏃堕棿"
+],
+  "repository": "https://github.com/dcloudio/uni-ui",
+  "engines": {
+    "HBuilderX": "",
+    "uni-app": "^4.07",
+    "uni-app-x": ""
+  },
+  "directories": {
+    "example": "../../temps/example_temps"
+  },
+  "dcloudext": {
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+      "ads": "鏃�,
+      "data": "鏃�,
+      "permissions": "鏃�
+    },
+    "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
+    "type": "component-vue",
+    "darkmode": "x",
+    "i18n": "x",
+    "widescreen": "x"
+  },
+  "uni_modules": {
+    "dependencies": [
+      "uni-scss",
+      "uni-icons"
+    ],
+    "encrypt": [],
+    "platforms": {
+      "cloud": {
+        "tcb": "x",
+        "aliyun": "x",
+        "alipay": "x"
+      },
+      "client": {
+        "uni-app": {
+          "vue": {
+            "vue2": "鈭�,
+            "vue3": "鈭�
+          },
+          "web": {
+            "safari": "鈭�,
+            "chrome": "鈭�
+          },
+          "app": {
+            "vue": "鈭�,
+            "nvue": "鈭�,
+            "android": "鈭�,
+            "ios": "鈭�,
+            "harmony": "鈭�
+          },
+          "mp": {
+            "weixin": "鈭�,
+            "alipay": "鈭�,
+            "toutiao": "鈭�,
+            "baidu": "鈭�,
+            "kuaishou": "-",
+            "jd": "-",
+            "harmony": "-",
+            "qq": "鈭�,
+            "lark": "-"
+          },
+          "quickapp": {
+            "huawei": "鈭�,
+            "union": "鈭�
+          }
+        },
+        "uni-app-x": {
+          "web": {
+            "safari": "-",
+            "chrome": "-"
+          },
+          "app": {
+            "android": "-",
+            "ios": "-",
+            "harmony": "-"
+          },
+          "mp": {
+            "weixin": "-"
+          }
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/uni_modules/uni-datetime-picker/readme.md b/uni_modules/uni-datetime-picker/readme.md
new file mode 100644
index 0000000..162fbef
--- /dev/null
+++ b/uni_modules/uni-datetime-picker/readme.md
@@ -0,0 +1,21 @@
+
+
+> `閲嶈閫氱煡锛氱粍浠跺崌绾ф洿鏂�2.0.0 鍚庯紝鏀寔鏃ユ湡+鏃堕棿鑼冨洿閫夋嫨锛岀粍浠�ui 灏嗕娇鐢ㄦ棩鍘嗛�鎷╂棩鏈燂紝ui 鍙樺寲杈冨ぇ锛屽悓鏃舵敮鎸�PC 鍜�绉诲姩绔�姝ょ増鏈笉鍚戝悗鍏煎锛屼笉鍐嶆敮鎸佸崟鐙殑鏃堕棿閫夋嫨锛坱ype=time锛夊強鐩稿叧鐨�hide-second 灞炴�锛堟椂闂撮�鍙娇鐢ㄥ唴缃粍浠�picker锛夈�鑻ヤ粛闇�娇鐢ㄦ棫鐗堟湰锛屽彲鍦ㄦ彃浠跺競鍦轰笅杞�闈瀠ni_modules鐗堟湰*锛屾棫鐗堟湰灏嗕笉鍐嶇淮鎶
+
+## DatetimePicker 鏃堕棿閫夋嫨鍣�+
+> **缁勪欢鍚嶏細uni-datetime-picker**
+> 浠g爜鍧楋細 `uDatetimePicker`
+
+
+璇ョ粍浠剁殑浼樺娍鏄紝鏀寔**鏃堕棿鎴�*杈撳叆鍜岃緭鍑猴紙璧峰鏃堕棿銆佺粓姝㈡椂闂翠篃鏀寔鏃堕棿鎴筹級锛屽彲**鍚屾椂閫夋嫨**鏃ユ湡鍜屾椂闂淬�
+
+鑻ュ彧鏄渶瑕佸崟鐙�鎷╂棩鏈熷拰鏃堕棿锛屼笉闇�鏃堕棿鎴宠緭鍏ュ拰杈撳嚭锛屽彲浣跨敤鍘熺敓鐨�picker 缁勪欢銆�+
+**_鐐瑰嚮 picker 榛樿鍊艰鍒欙細_**
+
+- 鑻ヨ缃垵濮嬪� value, 浼氭樉绀哄湪 picker 鏄剧ず妗嗕腑
+- 鑻ユ棤鍒濆鍊�value锛屽垯鍒濆鍊�value 涓哄綋鍓嶆湰鍦版椂闂�Date.now()锛�浣嗕笉浼氭樉绀哄湪 picker 鏄剧ず妗嗕腑
+
+### [鏌ョ湅鏂囨。](https://uniapp.dcloud.io/component/uniui/uni-datetime-picker)
+#### 濡備娇鐢ㄨ繃绋嬩腑鏈変换浣曢棶棰橈紝鎴栬�鎮ㄥuni-ui鏈変竴浜涘ソ鐨勫缓璁紝娆㈣繋鍔犲叆 uni-ui 浜ゆ祦缇わ細871950839 
\ No newline at end of file
diff --git a/uni_modules/uni-icons/changelog.md b/uni_modules/uni-icons/changelog.md
index 0261131..62e7682 100644
--- a/uni_modules/uni-icons/changelog.md
+++ b/uni_modules/uni-icons/changelog.md
@@ -1,5 +1,7 @@
-## 2.0.10锛�024-06-07锛�-- 浼樺寲 uni-app x 涓紝size 灞炴�鐨勭被鍨�+## 2.0.12锛�025-08-26锛�+- 浼樺寲 uni-app x 涓�size 绫诲瀷闂
+## 2.0.11锛�025-08-18锛�+- 淇 鍥炬爣鐐瑰嚮浜嬩欢杩斿洖
 ## 2.0.9锛�024-01-12锛� fix: 淇鍥炬爣澶у皬榛樿鍊奸敊璇殑闂
 ## 2.0.8锛�023-12-14锛�diff --git a/uni_modules/uni-icons/components/uni-icons/uni-icons.uvue b/uni_modules/uni-icons/components/uni-icons/uni-icons.uvue
index 8740559..53eb2ea 100644
--- a/uni_modules/uni-icons/components/uni-icons/uni-icons.uvue
+++ b/uni_modules/uni-icons/components/uni-icons/uni-icons.uvue
@@ -1,91 +1,91 @@
 <template>
-  <text class="uni-icons" :style="styleObj">
-    <slot>{{unicode}}</slot>
-  </text>
+	<text class="uni-icons" :style="styleObj">
+		<slot>{{unicode}}</slot>
+	</text>
 </template>
 
 <script>
-  import { fontData, IconsDataItem } from './uniicons_file'
+	import { fontData, IconsDataItem } from './uniicons_file'
 
-  /**
-   * Icons 鍥炬爣
-   * @description 鐢ㄤ簬灞曠ず icon 鍥炬爣
-   * @tutorial https://ext.dcloud.net.cn/plugin?id=28
-   * @property {Number,String} size 鍥炬爣澶у皬
-   * @property {String} type 鍥炬爣鍥炬锛屽弬鑰冪ず渚�-   * @property {String} color 鍥炬爣棰滆壊
-   * @property {String} customPrefix 鑷畾涔夊浘鏍�-   * @event {Function} click 鐐瑰嚮 Icon 瑙﹀彂浜嬩欢
-   */
-  export default {
-    name: "uni-icons",
-    props: {
-      type: {
-        type: String,
-        default: ''
-      },
-      color: {
-        type: String,
-        default: '#333333'
-      },
-      size: {
+	/**
+	 * Icons 鍥炬爣
+	 * @description 鐢ㄤ簬灞曠ず icon 鍥炬爣
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=28
+	 * @property {Number} size 鍥炬爣澶у皬
+	 * @property {String} type 鍥炬爣鍥炬锛屽弬鑰冪ず渚�+	 * @property {String} color 鍥炬爣棰滆壊
+	 * @property {String} customPrefix 鑷畾涔夊浘鏍�+	 * @event {Function} click 鐐瑰嚮 Icon 瑙﹀彂浜嬩欢
+	 */
+	export default {
+		name: "uni-icons",
+		props: {
+			type: {
+				type: String,
+				default: ''
+			},
+			color: {
+				type: String,
+				default: '#333333'
+			},
+			size: {
         type: [Number, String],
         default: 16
-      },
-      fontFamily: {
-        type: String,
-        default: ''
-      }
-    },
-    data() {
-      return {};
-    },
-    computed: {
-      unicode() : string {
-        let codes = fontData.find((item : IconsDataItem) : boolean => { return item.font_class == this.type })
-        if (codes !== null) {
-          return codes.unicode
-        }
-        return ''
-      },
-      iconSize() : string {
-        const size = this.size
-        if (typeof size == 'string') {
-          const reg = /^[0-9]*$/g
-          return reg.test(size as string) ? '' + size + 'px' : '' + size;
-          // return '' + this.size
-        }
-        return this.getFontSize(size as number)
-      },
-      styleObj() : UTSJSONObject {
-        if (this.fontFamily !== '') {
-          return { color: this.color, fontSize: this.iconSize, fontFamily: this.fontFamily }
-        }
-        return { color: this.color, fontSize: this.iconSize }
-      }
-    },
-    created() { },
-    methods: {
-      /**
-       * 瀛椾綋澶у皬
-       */
-      getFontSize(size : number) : string {
-        return size + 'px';
-      },
-    },
-  }
+			},
+			fontFamily: {
+				type: String,
+				default: ''
+			}
+		},
+		data() {
+			return {};
+		},
+		computed: {
+			unicode() : string {
+				let codes = fontData.find((item : IconsDataItem) : boolean => { return item.font_class == this.type })
+				if (codes !== null) {
+					return codes.unicode
+				}
+				return ''
+			},
+			iconSize() : string {
+				const size = this.size
+				if (typeof size == 'string') {
+				  const reg = /^[0-9]*$/g
+				  return reg.test(size as string) ? '' + size + 'px' : '' + size;
+				  // return '' + this.size
+				}
+				return this.getFontSize(size as number)
+			},
+			styleObj() : UTSJSONObject {
+				if (this.fontFamily !== '') {
+					return { color: this.color, fontSize: this.iconSize, fontFamily: this.fontFamily }
+				}
+				return { color: this.color, fontSize: this.iconSize }
+			}
+		},
+		created() { },
+		methods: {
+			/**
+			 * 瀛椾綋澶у皬
+			 */
+			getFontSize(size : number) : string {
+				return size + 'px';
+			},
+		},
+	}
 </script>
 
 <style scoped>
-  @font-face {
-    font-family: UniIconsFontFamily;
-    src: url('./uniicons.ttf');
-  }
+	@font-face {
+		font-family: UniIconsFontFamily;
+		src: url('./uniicons.ttf');
+	}
 
-  .uni-icons {
-    font-family: UniIconsFontFamily;
-    font-size: 18px;
-    font-style: normal;
-    color: #333;
-  }
+	.uni-icons {
+		font-family: UniIconsFontFamily;
+		font-size: 18px;
+		font-style: normal;
+		color: #333;
+	}
 </style>
diff --git a/uni_modules/uni-icons/components/uni-icons/uni-icons.vue b/uni_modules/uni-icons/components/uni-icons/uni-icons.vue
index 7da5356..1bd3d5e 100644
--- a/uni_modules/uni-icons/components/uni-icons/uni-icons.vue
+++ b/uni_modules/uni-icons/components/uni-icons/uni-icons.vue
@@ -85,8 +85,8 @@
 			}
 		},
 		methods: {
-			_onClick() {
-				this.$emit('click')
+			_onClick(e) {
+				this.$emit('click', e)
 			}
 		}
 	}
@@ -107,4 +107,4 @@
 		text-decoration: none;
 		text-align: center;
 	}
-</style>
+</style>
\ No newline at end of file
diff --git a/uni_modules/uni-icons/package.json b/uni_modules/uni-icons/package.json
index 6b681b4..60e45f0 100644
--- a/uni_modules/uni-icons/package.json
+++ b/uni_modules/uni-icons/package.json
@@ -1,7 +1,7 @@
 {
   "id": "uni-icons",
   "displayName": "uni-icons 鍥炬爣",
-  "version": "2.0.10",
+  "version": "2.0.12",
   "description": "鍥炬爣缁勪欢锛岀敤浜庡睍绀虹Щ鍔ㄧ甯歌鐨勫浘鏍囷紝鍙嚜瀹氫箟棰滆壊銆佸ぇ灏忋�",
   "keywords": [
     "uni-ui",
@@ -11,12 +11,14 @@
 ],
   "repository": "https://github.com/dcloudio/uni-ui",
   "engines": {
-    "HBuilderX": "^3.2.14"
+    "HBuilderX": "^3.2.14",
+    "uni-app": "^4.08",
+    "uni-app-x": "^4.61"
   },
   "directories": {
     "example": "../../temps/example_temps"
   },
-"dcloudext": {
+  "dcloudext": {
     "sale": {
       "regular": {
         "price": "0.00"
@@ -34,56 +36,76 @@
       "permissions": "鏃�
     },
     "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui",
-    "type": "component-vue"
+    "type": "component-vue",
+    "darkmode": "x",
+    "i18n": "x",
+    "widescreen": "x"
   },
   "uni_modules": {
-    "dependencies": ["uni-scss"],
+    "dependencies": [
+      "uni-scss"
+    ],
     "encrypt": [],
     "platforms": {
       "cloud": {
-        "tcb": "y",
-        "aliyun": "y",
-        "alipay": "n"
+        "tcb": "x",
+        "aliyun": "x",
+        "alipay": "x"
       },
       "client": {
-        "App": {
-          "app-vue": "y",
-          "app-nvue": "y",
-          "app-uvue": "y"
+        "uni-app": {
+          "vue": {
+            "vue2": "鈭�,
+            "vue3": "鈭�
+          },
+          "web": {
+            "safari": "鈭�,
+            "chrome": "鈭�
+          },
+          "app": {
+            "vue": "鈭�,
+            "nvue": "-",
+            "android": {
+                "extVersion": "",
+                "minVersion": "29"
+            },
+            "ios": "鈭�,
+            "harmony": "鈭�
+          },
+          "mp": {
+            "weixin": "鈭�,
+            "alipay": "鈭�,
+            "toutiao": "鈭�,
+            "baidu": "鈭�,
+            "kuaishou": "-",
+            "jd": "-",
+            "harmony": "-",
+            "qq": "鈭�,
+            "lark": "-"
+          },
+          "quickapp": {
+            "huawei": "鈭�,
+            "union": "鈭�
+          }
         },
-        "H5-mobile": {
-          "Safari": "y",
-          "Android Browser": "y",
-          "寰俊娴忚鍣�Android)": "y",
-          "QQ娴忚鍣�Android)": "y"
-        },
-        "H5-pc": {
-          "Chrome": "y",
-          "IE": "y",
-          "Edge": "y",
-          "Firefox": "y",
-          "Safari": "y"
-        },
-        "灏忕▼搴�: {
-          "寰俊": "y",
-          "闃块噷": "y",
-          "鐧惧害": "y",
-          "瀛楄妭璺冲姩": "y",
-          "QQ": "y",
-					"閽夐拤": "y",
-					"蹇墜": "y",
-					"椋炰功": "y",
-					"浜笢": "y"
-        },
-        "蹇簲鐢�: {
-          "鍗庝负": "y",
-          "鑱旂洘": "y"
-        },
-        "Vue": {
-            "vue2": "y",
-            "vue3": "y"
+        "uni-app-x": {
+          "web": {
+            "safari": "鈭�,
+            "chrome": "鈭�
+          },
+          "app": {
+            "android": {
+                "extVersion": "",
+                "minVersion": "29"
+            },
+            "ios": "鈭�,
+            "harmony": "鈭�
+          },
+          "mp": {
+            "weixin": "鈭�
+          }
         }
       }
     }
   }
-}
+}
\ No newline at end of file

--
Gitblit v1.9.1