From c71e806d1966a83aad55880c8a456358182a82be Mon Sep 17 00:00:00 2001
From: lefengyang <lefengyang@tencent.com>
Date: Sun, 09 Aug 2026 01:36:45 +0800
Subject: [PATCH] uniapp

---
 ledApp/static/logo.png                          |    0 
 ledApp/uni.promisify.adaptor.js                 |   13 
 ledApp/pages/addDevice/addDevice0.vue           |  487 ++
 ledApp/main.js                                  |   30 
 ledApp/static/img/btn_1.png                     |    0 
 ledApp/pages/invite/invite.vue                  |   96 
 ledApp/pages/policy/policy.vue                  |   63 
 ledApp/pages/personal/personal.vue              |  300 +
 ledApp/static/img/ico_3.png                     |    0 
 ledApp/static/img/logo.png                      |    0 
 ledApp/static/img/ico_head.png                  |    0 
 ledApp/uni.scss                                 |   76 
 ledApp/static/img/ico_m1.png                    |    0 
 ledApp/static/img/ico_1.png                     |    0 
 ledApp/pages/addTemplate/addTemplate copy 4.vue | 1056 +++++
 ledApp/static/img/ico_m3a.png                   |    0 
 ledApp/pages/addTemplate/addTemplate copy.vue   |  348 +
 ledApp/static/img/ico_2.png                     |    0 
 ledApp/pages/addTemplate/addTemplate copy 3.vue | 1056 +++++
 ledApp/pages/template/template.vue              |  183 
 ledApp/api/api.js                               |   60 
 ledApp/static/img/ico_m3.png                    |    0 
 ledApp/pages/animation/animation copy.vue       |  533 ++
 ledApp/pages/addTemplate/addTemplate copy 2.vue |  548 ++
 ledApp/pages/animation/animation.vue            |  624 +++
 ledApp/pages/my/my.vue                          |  176 
 ledApp/static/img/ico_m1a.png                   |    0 
 ledApp/pages.json                               |  122 
 ledApp/pages/addTemplate/addTemplate copy 1.vue |  548 ++
 ledApp/static/img/bg_1.png                      |    0 
 ledApp/pages/index/index.vue                    | 1353 +++++++
 ledApp/static/img/ico_m2.png                    |    0 
 ledApp/App.vue                                  |   39 
 ledApp/manifest.json                            |  146 
 ledApp/pages/set/set.vue                        |  129 
 ledApp/static/img/bg_2.jpg                      |    0 
 ledApp/pages/login/login.vue                    |  248 +
 ledApp/pages/feedback/feedback.vue              |  111 
 ledApp/static/img/bg_2.png                      |    0 
 ledApp/static/img/vip_head.jpg                  |    0 
 ledApp/pages/addTemplate/addTemplate.vue        | 1062 +++++
 ledApp/util/request.js                          |   70 
 ledApp/pages/about/about.vue                    |   59 
 ledApp/pages/register/register.vue              |  264 +
 ledApp/static/img/ph_1.png                      |    0 
 ledApp/index.html                               |   20 
 ledApp/static/img/bg_4.png                      |    0 
 ledApp/static/img/bg_3.png                      |    0 
 ledApp/static/img/ico_m2a.png                   |    0 
 ledApp/pages/addDevice/addDevice.vue            | 1622 ++++++++
 50 files changed, 11,442 insertions(+), 0 deletions(-)

diff --git a/ledApp/App.vue b/ledApp/App.vue
new file mode 100644
index 0000000..8ec0899
--- /dev/null
+++ b/ledApp/App.vue
@@ -0,0 +1,39 @@
+<script>
+	export default {
+		onLaunch: function() {
+			console.log('App Launch')
+
+			if(uni.getStorageSync('token')){
+				console.log('已登陆------------')
+				// uni.switchTab({
+				// 	url: '/pages/index/index'
+				// });
+			}
+			else{
+				console.log('未登陆------------')
+				// uni.reLaunch({
+				// 	url: '/pages/login/login'
+				// });
+			}
+			uni.onBLECharacteristicValueChange(res => {
+				uni.$emit('BLECharacteristic', res)
+			})
+			
+			
+		},
+		onShow: function() {
+			console.log('App Show')
+		},
+		onHide: function() {
+			console.log('App Hide')
+		}
+	}
+</script>
+
+<style>
+	/*每个页面公共css */
+	page {
+		font-family: "PingFang SC", "Helvetica Neue", Helvetica, Arial, sans-serif;
+	
+	}
+</style>
diff --git a/ledApp/api/api.js b/ledApp/api/api.js
new file mode 100644
index 0000000..8a32d56
--- /dev/null
+++ b/ledApp/api/api.js
@@ -0,0 +1,60 @@
+// post请求,获取菜单
+export const postMenu = (params, config = {}) => uni.$uv.http.post('/ebapi/public_api/index', params, config)
+
+// get请求,获取菜单,注意:get请求的配置等,都在第二个参数中,详见前面解释
+export const getMenu = (data) => uni.$uv.http.get('/ebapi/public_api/index', data)
+
+// 登录 
+export const login = (data) => uni.$uv.http.post("/login", data);
+
+// 退出登录
+export const logout = (data) => uni.$uv.http.post("/logout", data);
+
+// 注册 
+export const register = (data) => uni.$uv.http.post("/mobile/register", data);
+
+// 注销账户
+export const unRegistry = () => uni.$uv.http.post("/unRegistry");
+
+// 获取用户信息
+export const userInfo = (data) => uni.$uv.http.get('/getInfo', data)
+
+// 修改编辑用户信息
+export const userProfile = (data) => uni.$uv.http.put("/system/user/profile", data);
+
+// 新建设备
+export const equipmentAdd = (data) => uni.$uv.http.post("/system/equipment", data);
+
+// 设备列表
+export const equipmentList = (data) => uni.$uv.http.get('/system/equipment/list', {params:data})
+
+// 删除列表
+export const equipmentDel = (id) => uni.$uv.http.delete("/system/equipment/"+id);
+
+
+// 模板列表
+export const templeteList = (data) => uni.$uv.http.get('/system/templete/list', {params:data})
+
+// 新增模板
+export const templeteAdd = (data) => uni.$uv.http.post("/system/templete", data);
+
+// 删除模版
+export const templeteDel = (id) => uni.$uv.http.delete("/system/templete/"+id);
+
+// 隐私协议/关于我们 1/2
+export const content = (id) => uni.$uv.http.get('/system/content/'+id)
+
+// 意见反馈
+export const feekback = (data) => uni.$uv.http.post("/system/opinion", data);
+
+// 客服电话
+export const configPhone = () => uni.$uv.http.get('/system/config/configKey/biz.customer.phone')
+
+// app下载
+export const appDownload = () => uni.$uv.http.get('/system/config/configKey/biz.appDownload')
+
+// 模版详情
+export const templeteInfo = (id) => uni.$uv.http.get("/system/templete/"+id);
+
+// 修改模板
+export const templeteEdit = (data) => uni.$uv.http.put("/system/templete", data);
diff --git a/ledApp/index.html b/ledApp/index.html
new file mode 100644
index 0000000..c3ff205
--- /dev/null
+++ b/ledApp/index.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <script>
+      var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
+        CSS.supports('top: constant(a)'))
+      document.write(
+        '<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
+        (coverSupport ? ', viewport-fit=cover' : '') + '" />')
+    </script>
+    <title></title>
+    <!--preload-links-->
+    <!--app-context-->
+  </head>
+  <body>
+    <div id="app"><!--app-html--></div>
+    <script type="module" src="/main.js"></script>
+  </body>
+</html>
diff --git a/ledApp/main.js b/ledApp/main.js
new file mode 100644
index 0000000..d7a00b0
--- /dev/null
+++ b/ledApp/main.js
@@ -0,0 +1,30 @@
+import App from './App'
+import uvUI from '@/uni_modules/uv-ui-tools'
+import { Request } from '@/util/request'
+
+// #ifndef VUE3
+import Vue from 'vue'
+import './uni.promisify.adaptor'
+Vue.config.productionTip = false
+Vue.use(uvUI);
+App.mpType = 'app'
+const app = new Vue({
+	...App
+})
+app.$mount()
+Request(app)
+// #endif
+
+// #ifdef VUE3
+import {
+	createSSRApp
+} from 'vue'
+export function createApp() {
+	const app = createSSRApp(App)
+	app.use(uvUI);
+	 Request(app)
+	return {
+		app
+	}
+}
+// #endif
\ No newline at end of file
diff --git a/ledApp/manifest.json b/ledApp/manifest.json
new file mode 100644
index 0000000..7863e26
--- /dev/null
+++ b/ledApp/manifest.json
@@ -0,0 +1,146 @@
+{
+    "name" : "ledApp",
+    "appid" : "__UNI__8C00436",
+    "description" : "",
+    "versionName" : "1.0.6",
+    "versionCode" : 106,
+    "transformPx" : false,
+    /* 5+App特有相关 */
+    "app-plus" : {
+        "usingComponents" : true,
+        "nvueStyleCompiler" : "uni-app",
+        "compilerVersion" : 3,
+        "splashscreen" : {
+            "alwaysShowBeforeRender" : true,
+            "waiting" : true,
+            "autoclose" : true,
+            "delay" : 0
+        },
+        "screenOrientation" : [ "portrait-primary", "landscape-primary" ],
+        /* 模块配置 */
+        "modules" : {
+            "Barcode" : {},
+            "Geolocation" : {},
+            "Bluetooth" : {},
+            "Camera" : {}
+        },
+        /* 应用发布信息 */
+        "distribute" : {
+            /* android打包配置 */
+            "android" : {
+                "permissions" : [
+                    "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
+                    "<uses-permission android:name=\"android.permission.VIBRATE\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CAMERA\"/>",
+                    "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
+                    "<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.ACCESS_COARSE_LOCATION\" />",
+                    "<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\" />",
+                    "<uses-permission android:name=\"android.permission.BLUETOOTH_ADMIN\" />",
+                    "<uses-permission android:name=\"android.permission.BLUETOOTH\" />",
+                    "<uses-permission android:name=\"android.permission.BLUETOOTH_SCAN\" />",
+                    "<uses-permission android:name=\"android.permission.BLUETOOTH_CONNECT\" />",
+                    "<uses-permission android:name=\"android.permission.BLUETOOTH_ADVERTISE\" />"
+                ],
+                "abiFilters" : [ "armeabi-v7a", "arm64-v8a", "x86" ],
+                "minSdkVersion" : 21,
+                "targetSdkVersion" : 31
+            },
+            /* ios打包配置 */
+            "ios" : {
+                "privacyDescription" : {
+                    "NSBluetoothAlwaysUsageDescription" : "本应用需要始终使用蓝牙功能",
+                    "NSBluetoothPeripheralUsageDescription" : "需要使用蓝牙连接外设设备",
+                    "NSLocationWhenInUseUsageDescription" : "蓝牙连接外设设备需要定位服务",
+                    "NSLocationAlwaysUsageDescription" : "",
+                    "NSLocationAlwaysAndWhenInUseUsageDescription" : "蓝牙连接外设设备需要定位服务"
+                },
+                "dSYMs" : false,
+                "idfa" : false
+            },
+            /* SDK配置 */
+            "sdkConfigs" : {
+                "geolocation" : {
+                    "system" : {
+                        "__platform__" : [ "ios", "android" ]
+                    }
+                }
+            },
+            "icons" : {
+                "android" : {
+                    "hdpi" : "unpackage/res/icons/72x72.png",
+                    "xhdpi" : "unpackage/res/icons/96x96.png",
+                    "xxhdpi" : "unpackage/res/icons/144x144.png",
+                    "xxxhdpi" : "unpackage/res/icons/192x192.png"
+                },
+                "ios" : {
+                    "appstore" : "unpackage/res/icons/1024x1024.png",
+                    "ipad" : {
+                        "app" : "unpackage/res/icons/76x76.png",
+                        "app@2x" : "unpackage/res/icons/152x152.png",
+                        "notification" : "unpackage/res/icons/20x20.png",
+                        "notification@2x" : "unpackage/res/icons/40x40.png",
+                        "proapp@2x" : "unpackage/res/icons/167x167.png",
+                        "settings" : "unpackage/res/icons/29x29.png",
+                        "settings@2x" : "unpackage/res/icons/58x58.png",
+                        "spotlight" : "unpackage/res/icons/40x40.png",
+                        "spotlight@2x" : "unpackage/res/icons/80x80.png"
+                    },
+                    "iphone" : {
+                        "app@2x" : "unpackage/res/icons/120x120.png",
+                        "app@3x" : "unpackage/res/icons/180x180.png",
+                        "notification@2x" : "unpackage/res/icons/40x40.png",
+                        "notification@3x" : "unpackage/res/icons/60x60.png",
+                        "settings@2x" : "unpackage/res/icons/58x58.png",
+                        "settings@3x" : "unpackage/res/icons/87x87.png",
+                        "spotlight@2x" : "unpackage/res/icons/80x80.png",
+                        "spotlight@3x" : "unpackage/res/icons/120x120.png"
+                    }
+                }
+            }
+        }
+    },
+    /* 快应用特有相关 */
+    "quickapp" : {},
+    /* 小程序特有相关 */
+    "mp-weixin" : {
+        "appid" : "wx835fdaefed9491a4",
+        "setting" : {
+            "urlCheck" : false,
+            "minified" : true,
+            "postcss" : true,
+            "es6" : true,
+            "bluetooth" : true,
+            "request" : [ "scope.userLocation", "scope.userInfo" ]
+        },
+        "usingComponents" : true,
+        "permission" : {
+            "scope.userLocation" : {
+                "desc" : "你的位置信息将用于蓝牙设备扫描"
+            }
+        }
+    },
+    "mp-alipay" : {
+        "usingComponents" : true
+    },
+    "mp-baidu" : {
+        "usingComponents" : true
+    },
+    "mp-toutiao" : {
+        "usingComponents" : true
+    },
+    "uniStatistics" : {
+        "enable" : false
+    },
+    "vueVersion" : "3"
+}
diff --git a/ledApp/pages.json b/ledApp/pages.json
new file mode 100644
index 0000000..1be60ba
--- /dev/null
+++ b/ledApp/pages.json
@@ -0,0 +1,122 @@
+{
+	"pages": [{
+			"path": "pages/index/index",
+			"style": {
+				"navigationBarTitleText": "设备"
+			}
+		},
+		{
+			"path": "pages/login/login",
+			"style": {
+				"navigationBarTitleText": "登陆"
+			}
+		},
+		{
+			"path": "pages/my/my",
+			"style": {
+				"navigationBarTitleText": "我的"
+			}
+		},
+		{
+			"path": "pages/template/template",
+			"style": {
+				"navigationBarTitleText": "模版"
+			}
+		},
+		{
+			"path": "pages/addDevice/addDevice",
+			"style": {
+				"navigationBarTitleText": "添加设备"
+			}
+		},
+		{
+			"path": "pages/invite/invite",
+			"style": {
+				"navigationBarTitleText": ""
+			}
+		},
+		{
+			"path": "pages/about/about",
+			"style": {
+				"navigationBarTitleText": "关于我们"
+			}
+		},
+		{
+			"path": "pages/feedback/feedback",
+			"style": {
+				"navigationBarTitleText": "反馈问题"
+			}
+		},
+		{
+			"path": "pages/set/set",
+			"style": {
+				"navigationBarTitleText": "设置"
+			}
+		},
+		{
+			"path": "pages/policy/policy",
+			"style": {
+				"navigationBarTitleText": ""
+			}
+		},
+		{
+			"path": "pages/addTemplate/addTemplate",
+			"style": {
+				"navigationBarTitleText": "添加模版"
+			}
+		},
+		{
+			"path": "pages/animation/animation",
+			"style": {
+				"navigationBarTitleText": "编辑动画",
+				"orientation": "landscape"
+			}
+		},
+		{
+			"path": "pages/register/register",
+			"style": {
+				"navigationBarTitleText": ""
+			}
+		},
+
+		{
+			"path": "pages/personal/personal",
+			"style": {
+				"navigationBarTitleText": ""
+			}
+		}
+	],
+	"globalStyle": {
+		"navigationBarTextStyle": "black",
+		"navigationBarTitleText": "LEDAPP",
+		"navigationBarBackgroundColor": "#F8F8F8",
+		"backgroundColor": "#F8F8F8",
+		"navigationStyle": "custom"
+	},
+	"tabBar": {
+		"color": "#757575",
+		"selectedColor": "#3086F3",
+		"borderStyle": "black",
+		"backgroundColor": "#ffffff",
+		"list": [{
+				"pagePath": "pages/index/index",
+				"iconPath": "/static/img/ico_m1.png",
+				"selectedIconPath": "/static/img/ico_m1a.png",
+				"text": "设备"
+			},
+			{
+				"pagePath": "pages/template/template",
+				"iconPath": "/static/img/ico_m2.png",
+				"selectedIconPath": "/static/img/ico_m2a.png",
+				"text": "模版"
+			},
+			{
+				"pagePath": "pages/my/my",
+				"iconPath": "/static/img/ico_m3.png",
+				"selectedIconPath": "/static/img/ico_m3a.png",
+				"text": "我的"
+			}
+		]
+	},
+	"uniIdRouter": {}
+}
\ No newline at end of file
diff --git a/ledApp/pages/about/about.vue b/ledApp/pages/about/about.vue
new file mode 100644
index 0000000..be6b6e5
--- /dev/null
+++ b/ledApp/pages/about/about.vue
@@ -0,0 +1,59 @@
+<template>
+	<view class="page_bg">
+		<uv-navbar title="关于我们" :autoBack="true" :placeholder="true" leftIconColor="#fff" bgColor="rgba(0,0,0,0)"
+			titleStyle="color:#fff"></uv-navbar>
+
+		<view class="about">
+			<rich-text :nodes="aboutData"></rich-text>
+		</view>
+
+	</view>
+</template>
+
+<script>
+	import {
+		content
+	} from '@/api/api.js'
+	export default {
+		data() {
+			return {
+				aboutData: ""
+			}
+		},
+		onLoad() {
+			this.getContent()
+		},
+		methods: {
+			getContent() {
+				content(2).then(res => {
+					console.log(res.data)
+					this.aboutData = res.data.content.replace(/(?<=src=")\/prod-api/g, uni.$uv.http.config.baseURL)
+				})
+			}
+		}
+	}
+</script>
+<style>
+  /deep/ rich-text img {
+    max-width: 100%;
+    height: auto;
+  }
+</style>
+<style lang="scss" scoped>
+	.about {
+		margin-top: 360rpx;
+		padding: 30rpx;
+		font-weight: 400;
+		font-size: 27rpx;
+		color: rgba(0, 0, 0, 0.7);
+		line-height: 46rpx;
+		text-align: justified;
+	}
+
+	.page_bg {
+		width: 100%;
+		min-height: calc(100vh - var(--window-bottom));
+		background: url('/static/img/bg_4.png') no-repeat center top;
+		background-size: 100% auto;
+	}
+</style>
\ No newline at end of file
diff --git a/ledApp/pages/addDevice/addDevice.vue b/ledApp/pages/addDevice/addDevice.vue
new file mode 100644
index 0000000..f23b618
--- /dev/null
+++ b/ledApp/pages/addDevice/addDevice.vue
@@ -0,0 +1,1622 @@
+<template>
+	<view>
+		<uv-navbar title="添加设备" :autoBack="true" :placeholder="true" leftIconColor="#fff" bgColor="#3B74EE"
+			titleStyle="color:#fff"></uv-navbar>
+
+		<view class="page_bg" v-if="searchIng">
+			<view class="searching_ico">
+				<image src="/static/img/ico_1.png" mode="widthFix"></image>
+			</view>
+
+			<view class="searching_text" v-if="isBluetooth && isLocation">
+				<view class="name">
+					搜索中
+				</view>
+				<view class="text">
+					持续自动搜索中...
+				</view>
+			</view>
+
+			<view class="searching_text" v-if="!isBluetooth">
+				<view class="name">
+					蓝牙未开启
+				</view>
+				<view class="text">
+					请手动开启系统蓝牙
+				</view>
+			</view>
+			
+			<view class="searching_text" v-if="!isLocation">
+				<view class="name">
+					定位服务未开启
+				</view>
+				<view class="text">
+					请手动开启系统定位服务
+				</view>
+			</view>
+
+		</view>
+
+		<view class="bluetooth" v-if="!searchIng">
+			<view class="bluetooth_title">
+				<image class="ico" src="/static/img/ico_2.png" mode="widthFix"></image>
+				<text>已找到设备</text>
+			</view>
+			<view class="bluetooth_list">
+				<view class="item" v-for="(item,index) in bluetoothList" :key="index">
+					<image class="ph" src="/static/img/ph_1.png" mode="aspectFill"></image>
+					<view class="info">
+						<view class="name">
+							{{item.name}}
+						</view>
+						<view class="btn" v-if="!isLink">
+							<uv-button type="primary" shape="circle" text="连接" size="small"
+								@click="createBLEConnection(item)"></uv-button>
+						</view>
+						<!-- <view class="btn" v-if="!isLink">
+							<uv-button type="primary" shape="circle" text="连接" size="small"
+								@click="createBLEConnection(item.deviceId)"></uv-button>
+						</view> -->
+						<!-- <view class="btn">
+							<uv-button type="primary" shape="circle" text="LIST" size="small"
+								@click="commandLIST()"></uv-button>
+						</view> -->
+						<!-- <view class="btn">
+							<uv-button type="primary" shape="circle" text="播放LED" size="small"
+								@click="this.$refs.popupLED.open();"></uv-button>
+						</view> -->
+						<view class="btn" v-if="isLink">
+							<uv-button type="primary" shape="circle" text="发送LED" size="small"
+								@click="this.$refs.popupTemplete.open();"></uv-button>
+						</view>
+						<!-- <view class="btn">
+							<uv-button type="primary" shape="circle" text="获取画板数据" size="small"
+								@click="getDrawData()"></uv-button>
+						</view> -->
+						<!-- <view class="btn">
+							<uv-button type="primary" shape="circle" text="删除全部" size="small"
+								@click="commandDALL()"></uv-button>
+						</view> -->
+						<!-- <view class="btn" v-if="isLink">
+							<uv-button type="primary" shape="circle" text="调节亮度" size="small"
+								@click="this.$refs.popupBRIG.open();"></uv-button>
+						</view> -->
+
+					</view>
+				</view>
+			</view>
+		</view>
+
+
+		<!-- 设置密码弹出框 -->
+		<uv-popup ref="popupPwd" @change="changePopupPwd" mode="center" round="30rpx" :closeable="false"
+			:closeOnClickOverlay="false">
+			<view class="popup_pwd">
+				<view class="title">请输入设备绑定的密码</view>
+				<view class="cont">
+					<uv-input v-model="password" type="number" maxlength="4" placeholder="请输入4位数密码" />
+				</view>
+				<view class="btns">
+					<uv-button type="primary" :disabled="password.length !== 4" @click="confirmPwd">确定</uv-button>
+				</view>
+			</view>
+		</uv-popup>
+
+		<!-- 设置名称出框 -->
+		<uv-popup ref="popupName" @change="changePopupName" mode="center" round="30rpx" :closeable="false"
+			:closeOnClickOverlay="false">
+			<view class="popup_pwd">
+				<view class="title">请输入设备名称</view>
+				<view class="cont">
+					<uv-input v-model="deviceName" placeholder="请输入设备名称" />
+				</view>
+				<view class="btns">
+					<uv-button type="primary" :disabled="deviceName == ''" @click="confirmName">确定</uv-button>
+				</view>
+			</view>
+		</uv-popup>
+
+		<!-- 播放设备中文件列表 -->
+		<uv-popup ref="popupLED" mode="center" round="30rpx">
+			<view class="popup_pwd">
+				<view class="title">发送LED</view>
+				<view class="cont cont_led">
+					<view class="led_item" v-for="(item,index) in fileNames" :key="index">
+						<view class="name">
+							{{item.fileName}}
+						</view>
+						<!-- <view class="name">
+							{{item.nameHex}}
+						</view> -->
+						<uv-button type="primary" @click="commandDELL(item.nameHex)">删除</uv-button>
+						<uv-button type="primary" @click="commandPLAY(item.nameHex)">播放</uv-button>
+					</view>
+				</view>
+				<view class="btns">
+					<uv-button type="primary" plain @click="this.$refs.popupLED.close();">关闭</uv-button>
+				</view>
+			</view>
+		</uv-popup>
+
+		<!-- 调节亮度 -->
+		<uv-popup ref="popupBRIG" mode="center" round="30rpx">
+			<view class="popup_pwd">
+				<view class="title">调节亮度</view>
+				<view class="cont">
+					<uv-slider v-model="brightness" :min="0" :max="9" :step="1" block-size="24" block-color="#2979ff"
+						@changing="changeBrightness"></uv-slider>
+				</view>
+				<view class="btns_two">
+					<view class="btn2">
+						<uv-button type="primary" plain @click="this.$refs.popupBRIG.close();">关闭</uv-button>
+					</view>
+					<view class="btn2">
+						<uv-button type="primary" @click="commandBRIG()">确定</uv-button>
+					</view>
+				</view>
+			</view>
+		</uv-popup>
+
+		<!-- 模版列表 -->
+		<uv-popup ref="popupTemplete" mode="center" round="30rpx">
+			<view class="popup_pwd">
+				<view class="title">发送LED</view>
+				<view class="cont cont_led">
+					<view class="led_item" v-for="(item,index) in templeteData" :key="index">
+						<view class="name">
+							{{item.name}}
+						</view>
+						<view class="name">
+							{{item.screen}}
+						</view>
+						<uv-button type="primary" shape="circle" size="small"
+							@click="commandSendTemplete(item)">播放</uv-button>
+					</view>
+				</view>
+				<view class="btns">
+					<uv-button type="primary" plain @click="this.$refs.popupTemplete.close();">关闭</uv-button>
+				</view>
+			</view>
+		</uv-popup>
+
+	</view>
+</template>
+
+<script>
+	import {
+		equipmentAdd,
+		templeteList
+	} from '@/api/api.js'
+	export default {
+		data() {
+			return {
+				searchIng: true,
+				bluetoothList: [], //搜索到的蓝牙设备列表
+				deviceId: '', //设备id
+				serviceId: '', //服务uuid
+				characteristicId1: '', //特征uuid
+				characteristicId2: '',
+				characteristicId3: '',
+				showPwdPopup: false,
+				password: '', //设置的密码
+				checId: '', //智能壳的ID
+				batchNumber: '', //生产顺序号
+				fileNames: [], //文件名称列表
+				brightness: 3, //亮度
+				nameIndex: 1,
+				name: '',
+
+				ledName: '', // 名称
+				screen: '',
+				ledTotal: 3, // 总帧数
+				ledCurrent: 0, // 当前帧数
+				ledData: [],
+				isLink: false,
+				deviceName: '',
+				versions: '',
+				isBluetooth: true,
+				lastBluetooth: null,
+				isLocation: true,
+				platform: ''
+				// ledData: [
+				// 	'000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FF0000FF0000FF0000FF0000FF0000FF0000FF0000FF0000FF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
+				// 	'000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FF0000FF0000FF0000FF0000FF0000FF0000FF0000FF0000FF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
+				// 	'000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FF0000FF0000FF0000FF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
+				// ]
+			}
+		},
+		onLoad() {
+			console.log('onLoad-----')
+			
+		},
+		onShow() {
+			const systemInfo = uni.getSystemInfoSync();
+			if (systemInfo.platform === 'ios') {
+				// iOS 设备
+				this.platform = 'ios'
+			} else if (systemInfo.platform === 'android') {
+				// Android 设备
+				this.platform = 'android'
+			}
+			console.log('onShow-----')
+			this.openBluetooth()
+			this.getTempleteList()
+			//this.checkBluetoothEnabled()
+		},
+		onHide() {
+			console.log('onHide----aaa-')
+			uni.$off('BLECharacteristic')
+			this.closeBLEConnection()
+			this.closeBluetooth()
+			this.stopBluetoothDevices()
+		},
+		onUnload() {
+			uni.$off('BLECharacteristic')
+			console.log('onUnload-----aaa')
+			this.closeBLEConnection()
+			this.closeBluetooth()
+			this.stopBluetoothDevices()
+		},
+		methods: {
+			// 获取模版列表
+			getTempleteList() {
+				templeteList({
+					userId: uni.getStorageSync('userId'),
+					pageNum: 1,
+					pageSize: 10
+				}).then(res => {
+					//console.log('模版列表-------', res.rows)
+					this.templeteData = res.rows
+				})
+			},
+			// 添加设备
+			postEquipmentAdd() {
+				
+				//console.log('this.checId---', this.checId)
+				equipmentAdd({
+					userId: uni.getStorageSync('userId'),
+					code: this.platform,
+					password: this.password,
+					name: this.deviceName, //设备名称
+					checId: this.checId, //智能壳的ID
+					deviceId: this.deviceId, //设备id
+					serviceId: this.serviceId, //服务id
+					characteristicId1: this.characteristicId1, //特征id
+					characteristicId2: this.characteristicId2,
+					characteristicId3: this.characteristicId3,
+					versions: this.versions
+				}).then(res => {
+					console.log(res)
+					uni.showToast({
+						title: res.msg,
+						duration: 2000,
+						icon: 'none'
+					});
+					if (res.code == 200) {
+						this.closeBLEConnection()
+						setTimeout(() => {
+							uni.switchTab({
+								url: '/pages/index/index'
+							});
+						}, 300);
+					}
+				})
+			},
+			changeBrightness(value) {
+				//console.log('changeBrightness----', value)
+				//this.brightness = value
+			},
+			// 设置名称
+			confirmName() {
+				this.$refs.popupName.close();
+				this.commandQVER()
+				uni.showLoading({
+					title: '添加中...',
+					mask: true
+				});
+				//this.postEquipmentAdd()
+			},
+			changePopupName(e) {
+				console.log(e)
+			},
+			changePopupPwd(e) {
+				console.log(e.show)
+				if (e.show) {
+					this.password = ''
+				} else {
+					//this.closeBLEConnection()
+				}
+			},
+			// 初始化蓝牙
+			openBluetooth() {
+				uni.openBluetoothAdapter({
+					success: (res) => {
+						console.log('蓝牙初始化成功');
+						// uni.onBluetoothAdapterStateChange(function (res) {
+						//   console.log('监听蓝牙适配器状态变化事件------', res)
+						// })
+						this.isBluetooth = true
+						this.startBluetoothDevices()
+					},
+					fail: (err) => {
+						//console.log('蓝牙初始化失败----', err);
+						if (err.code == 10001) {
+							console.log('蓝牙开关未开启----', err);
+							this.isBluetooth = false
+							
+							this.lastBluetooth = false
+							uni.onBluetoothAdapterStateChange(res => {
+								console.log('蓝牙状态变化:', res.available)
+							
+								// 仅当状态从 false 变成 true 时执行一次
+								if (this.lastBluetooth == false && res.available == true) {
+									console.log('蓝牙开启,执行 openBluetooth()')
+									this.openBluetooth()
+								}
+								this.lastBluetooth = res.available
+								this.isBluetooth = res.available
+							})
+						}
+					},
+					complete: () => {
+						
+					}
+				});
+			},
+			// 关闭蓝牙
+			closeBluetooth() {
+				uni.closeBluetoothAdapter({
+					success: (res) => {
+						console.log(res)
+					}
+				})
+			},
+			//开始搜寻附近的蓝牙外围设备
+			startBluetoothDevices() {
+				const that = this
+				uni.startBluetoothDevicesDiscovery({
+					success: (res) => {
+						console.log('开始扫描设备', res);
+						that.onBluetoothDeviceFound();
+					},
+					fail: (err) => {
+						console.log('扫描失败', err);
+						if(err.code == 10016){
+							//that.isLocation = false
+							const timerLocation = setInterval(function() {
+								const systemSetting = uni.getSystemSetting()
+								that.isLocation = systemSetting.locationEnabled
+								console.log("执行一次",this.isLocation,systemSetting.locationEnabled);
+								if(that.isLocation){
+									clearInterval(timerLocation);
+									that.openBluetooth()
+								}
+							}, 500);
+						}
+					}
+				});
+			},
+			// 停止搜寻附近的蓝牙外围设备
+			stopBluetoothDevices() {
+				uni.stopBluetoothDevicesDiscovery({
+					success: (res) => {
+						console.log(res)
+					},
+					fail: (err) => {
+						console.log(err)
+					}
+				});
+			},
+			// 监听寻找到新设备的事件
+			onBluetoothDeviceFound() {
+				uni.onBluetoothDeviceFound(res => {
+					//console.log('找到新设备', res);
+					res.devices.forEach((device) => {
+						if (device.name.includes('CH9140')) {
+							this.bluetoothList.push(device);
+							console.log('找到新设备----', this.bluetoothList);
+							this.stopBluetoothDevices()
+							setTimeout(() => {
+								this.searchIng = false
+							}, 500);
+						}
+					});
+				});
+			},
+			// 连接低功耗蓝牙设备
+			createBLEConnection(item) {
+				console.log('连接de蓝牙设备--------', item)
+				const that = this
+				uni.showLoading({
+					title: '连接中...',
+					mask: true
+				});
+				that.name = item.name
+				that.deviceId = item.deviceId
+				uni.createBLEConnection({
+					deviceId: that.deviceId,
+					success: (res) => {
+						console.log('连接低功耗蓝牙设备', res);
+						setTimeout(() => {
+							//that.setBLEMTU()
+							that.getServices();
+						}, 500);
+					},
+					fail: (err) => {
+						console.log('连接失败', err);
+						this.closeBLEConnection()
+						uni.hideLoading();
+						uni.showToast({
+							title: '连接失败',
+							icon: 'none',
+							mask: true
+						});
+					}
+				});
+			},
+			// 断开与低功耗蓝牙设备的连接
+			closeBLEConnection() {
+				uni.closeBLEConnection({
+					deviceId: this.deviceId,
+					success(res) {
+						console.log(res)
+					}
+				})
+			},
+			setBLEMTU() {
+				uni.setBLEMTU({
+					deviceId: this.deviceId,
+					mtu: 80, // 最大值一般 512,蓝牙 4.2 通常支持 247
+					success: (res) => {
+						console.log('设置MTU成功:', res);
+					},
+					fail: (err) => {
+						console.error('设置MTU失败:', err);
+					}
+				});
+			},
+			// 获取蓝牙设备所有服务
+			getServices() {
+				console.log(this.deviceId)
+				uni.getBLEDeviceServices({
+					deviceId: this.deviceId,
+					success: (res) => {
+						console.log('服务列表成功', res);
+						this.serviceId = res.services.find(item => item.uuid.toLowerCase().includes('fff0'))?.uuid;
+						this.getCharacteristics();
+					},
+					fail: (err) => {
+						console.error('服务列表失败:', err);
+						uni.hideLoading();
+						uni.showToast({
+							title: '服务列表失败',
+							icon: 'none',
+							mask: true
+						});
+					}
+				});
+			},
+			// 获取蓝牙设备某个服务中所有特征值(characteristic)
+			getCharacteristics() {
+				uni.getBLEDeviceCharacteristics({
+					deviceId: this.deviceId,
+					serviceId: this.serviceId,
+					success: (res) => {
+						console.log('特征列表', res);
+						this.characteristicId1 = res.characteristics[0].uuid
+						this.characteristicId2 = res.characteristics[1].uuid
+						this.characteristicId3 = res.characteristics[2].uuid
+						this.startNotify()
+						setTimeout(() => {
+							this.setBaud()
+						}, 500);
+					}
+				});
+			},
+			// 设置波特率
+			setBaud() {
+				var that = this
+				var hexStr = "06000900009600000801009F"
+				uni.writeBLECharacteristicValue({
+					deviceId: this.deviceId,
+					serviceId: this.serviceId,
+					characteristicId: this.characteristicId3,
+					value: this.hexStringToArrayBuffer(hexStr),
+					writeType: 'write',
+					success: (res) => {
+						console.log('设置波特率成功----', res);
+						that.setBLEMTU()
+						that.commandCHEC()
+					},
+					fail: (err) => {
+						console.log('设置波特率失败----', err);
+					}
+				});
+			},
+			// 开始监听
+			startNotify() {
+				const that = this
+				uni.notifyBLECharacteristicValueChange({
+					deviceId: this.deviceId,
+					serviceId: this.serviceId,
+					characteristicId: this.characteristicId1,
+					state: true,
+					success: () => {
+						console.log('开始监听------');
+						uni.$off('BLECharacteristic');
+						uni.$on('BLECharacteristic',function(res){
+							console.log('BLECharacteristic---------------cc', res)
+							const resData = that.ab2hex(res.value);
+							const result = that.removeStartBytes(resData)
+							const resName = that.getCommandType(result);
+							console.log("回调返回数据-------add", resData);
+							console.log('result-----------add', result)
+							console.log('resName----------add', resName)
+							uni.hideLoading();
+							if (resName == 'CHEC') {
+								that.parseBLECHECResponse(result)
+							} else if (resName == 'BIND') {
+								that.$refs.popupName.open();
+							} else if (resName == 'LIST') {
+								that.parseFileList(result)
+							} else if (resName == 'BRIG') {
+							
+							} else if (resName == 'PLAY') {
+							
+							} else if (resName == 'INST') {
+								// 添加动画帧
+								that.ledCurrent++
+								if (that.ledCurrent < that.ledTotal) {
+									uni.showLoading({
+										title: '发送中' + that.ledCurrent,
+										mask: true
+									});
+									that.commandINST(that.ledData[that.ledCurrent]);
+								} else if (that.ledCurrent == that.ledTotal) {
+									uni.showLoading({
+										title: '发送结束帧',
+										mask: true
+									});
+									that.commandEnd()
+								}
+							
+							} else if (resName == 'DALL') {
+							
+							} else if (resName == 'QVER') {
+								// 查询版本信息
+								that.versions = parseInt(result.slice(-8, -6), 16);
+								that.postEquipmentAdd()
+							} else {
+								that.isLink == false
+								uni.showToast({
+									title: '返回数据错误',
+									duration: 2000,
+									icon: 'none'
+								});
+								// 关闭蓝牙设备
+								that.closeBLEConnection()
+							}
+						})
+						//uni.onBLECharacteristicValueChange(res => {
+							// const resData = that.ab2hex(res.value);
+							// const result = that.removeStartBytes(resData)
+							// const resName = that.getCommandType(result);
+							// console.log("回调返回数据-------add", resData);
+							// console.log('result-----------add', result)
+							// console.log('resName----------add', resName)
+							// uni.hideLoading();
+							// if (resName == 'CHEC') {
+							// 	that.parseBLECHECResponse(result)
+							// } else if (resName == 'BIND') {
+							// 	that.$refs.popupName.open();
+							// } else if (resName == 'LIST') {
+							// 	that.parseFileList(result)
+							// } else if (resName == 'BRIG') {
+
+							// } else if (resName == 'PLAY') {
+
+							// } else if (resName == 'INST') {
+							// 	// 添加动画帧
+							// 	that.ledCurrent++
+							// 	if (that.ledCurrent < that.ledTotal) {
+							// 		uni.showLoading({
+							// 			title: '发送中' + that.ledCurrent,
+							// 			mask: true
+							// 		});
+							// 		that.commandINST(that.ledData[that.ledCurrent]);
+							// 	} else if (that.ledCurrent == that.ledTotal) {
+							// 		uni.showLoading({
+							// 			title: '发送结束帧',
+							// 			mask: true
+							// 		});
+							// 		that.commandEnd()
+							// 	}
+
+							// } else if (resName == 'DALL') {
+
+							// } else if (resName == 'QVER') {
+							// 	// 查询版本信息
+							// 	that.versions = parseInt(result.slice(-8, -6), 16);
+							// 	that.postEquipmentAdd()
+							// } else {
+							// 	that.isLink == false
+							// 	uni.showToast({
+							// 		title: '返回数据错误',
+							// 		duration: 2000,
+							// 		icon: 'none'
+							// 	});
+							// 	// 关闭蓝牙设备
+							// 	that.closeBLEConnection()
+							// }
+						//});
+					}
+				});
+			},
+			// 判断并去除10个0x55启动字节(20个hex字符)
+			removeStartBytes(hexStr) {
+				// 10个0x55 = 20个hex字符
+				const startBytes = '55'.repeat(10); // '55555555555555555555'
+				if (hexStr.startsWith(startBytes)) {
+					return hexStr.slice(20); // 去掉前20个字符
+				}
+				return hexStr;
+			},
+			// 返回指令类型 判断
+			getCommandType(hexString) {
+				// 判断是否有 返回标志 单字节。0(0x30),工作无异常;1(0x31)工作有异常
+				// 判断是否包含返回标志 30 或 31
+				// 查找30或31的位置
+				let returnFlag = '';
+				let cmd = '';
+				let packageType = hexString.slice(2, 4);
+
+				if (packageType == '41') {
+					returnFlag = hexString.slice(12, 14);
+					const before8Chars = hexString.slice(4, 12);
+					cmd = before8Chars.match(/.{2}/g)
+						.map(h => String.fromCharCode(parseInt(h, 16)))
+						.join('');
+					return cmd;
+				} else if (packageType == '43') {
+					returnFlag = hexString.slice(28, 30);
+					const before8Chars = hexString.slice(20, 28);
+					cmd = before8Chars.match(/.{2}/g)
+						.map(h => String.fromCharCode(parseInt(h, 16)))
+						.join('');
+					return cmd;
+				}
+				console.log('returnFlag------', returnFlag)
+			},
+			// 查询智能壳ID
+			// 构建指令数据
+			commandCHEC() {
+				//555555555555555555557e41434845430095977e
+				// 构建指令数据
+				const startBytes = new Array(10).fill(0x55); // 10个启动字节
+				const frameHeader = 0x7E; // 帧头
+				const packageType = 0x41; // 包类型
+				const command = [0x43, 0x48, 0x45, 0x43]; // "CHEC"的ASCII码
+				const reserved = 0x00; // 保留字节
+
+				// 组合数据
+				const data = [
+					...startBytes,
+					frameHeader,
+					packageType,
+					...command,
+					reserved
+				];
+
+				// 计算CRC校验码(从包头段开始计算)
+				const crc = this.calcCRC16(data.slice(11));
+
+				// 添加CRC和帧尾
+				data.push((crc >> 8) & 0xFF); // CRC高8位
+				data.push(crc & 0xFF); // CRC低8位
+				data.push(frameHeader); // 帧尾
+				console.log('CHEC data----AAAA--------', this.hexArrayToHexString(data))
+
+				const buffer = new Uint8Array(data).buffer;
+				setTimeout(() => {
+					this.writeBLECharacteristicValue(buffer)
+				}, 1000);
+			},
+			// 加密绑定号 发送绑定号
+			commandBIND() {
+				console.log('checId---', this.checId)
+				// 59 5C 37 39 71 4D 56 65 5A 37 12 18 07 06 2A 18 36 1E 1E 06
+				// 7e 41 42 49 4e 44 6d 71 41 45 95 4e 61 78 9e 39 49 12 15 13 71 42 94 50 50 17 7c c9 7e
+				//帧头+0x41+ BIND +加密绑定号+CRC校验码+帧尾
+				// 构建指令数据
+				const startBytes = new Array(10).fill(0x55); // 10个启动字节
+				const frameHeader = 0x7E; // 帧头
+				const packageType = 0x41; // 包类型
+				const command = [0x42, 0x49, 0x4E, 0x44]; // "BIND"的ASCII码
+				const reserved = this.replaceScreenSizeInID(this.checId, this.password); // 密码
+				console.log('reserved----111', reserved)
+				const reservedId = this.encryptSmartShellID(reserved)
+				console.log('reservedid--222', reservedId)
+				// 组合数据
+				const data = [
+					...startBytes,
+					frameHeader,
+					packageType,
+					...command,
+					...this.hexStringToArray(reservedId)
+				];
+
+				// 计算CRC校验码(从包头段开始计算)
+				const crc = this.calcCRC16(data.slice(11));
+
+				// 添加CRC和帧尾
+				data.push((crc >> 8) & 0xFF); // CRC高8位
+				data.push(crc & 0xFF); // CRC低8位
+				data.push(frameHeader); // 帧尾
+				console.log('BIND data----BBBB--------', this.hexArrayToHexString(data))
+
+				const buffer = new Uint8Array(data).buffer;
+				setTimeout(() => {
+					this.writeBLECharacteristicValue(buffer)
+				}, 1000);
+			},
+			//关屏指令(BLAC)
+			commandBLAC() {
+				// 构建指令数据
+				const startBytes = new Array(10).fill(0x55); // 10个启动字节
+				const frameHeader = 0x7E; // 帧头
+				const packageType = 0x41; // 包类型
+				const command = [0x42, 0x49, 0x4E, 0x44]; // "BLAC"的ASCII码
+
+				// 组合数据
+				const data = [
+					...startBytes,
+					frameHeader,
+					packageType,
+					...command
+				];
+
+				// 计算CRC校验码(从包头段开始计算)
+				const crc = this.calcCRC16(data.slice(11));
+
+				// 添加CRC和帧尾
+				data.push((crc >> 8) & 0xFF); // CRC高8位
+				data.push(crc & 0xFF); // CRC低8位
+				data.push(frameHeader); // 帧尾
+				console.log('data----', data)
+
+				const buffer = new Uint8Array(data).buffer;
+				setTimeout(() => {
+					this.writeBLECharacteristicValue(buffer)
+				}, 1000);
+			},
+			// 查询版本信息
+			commandQVER() {
+				// 构建指令数据
+				const startBytes = new Array(10).fill(0x55); // 10个启动字节
+				const frameHeader = 0x7E; // 帧头
+				const packageType = 0x41; // 包类型
+				const command = [0x51, 0x56, 0x45, 0x52]; // "QVER"的ASCII码
+
+				// 组合数据
+				const data = [
+					...startBytes,
+					frameHeader,
+					packageType,
+					...command
+				];
+
+				// 计算CRC校验码(从包头段开始计算)
+				const crc = this.calcCRC16(data.slice(11));
+
+				// 添加CRC和帧尾
+				data.push((crc >> 8) & 0xFF); // CRC高8位
+				data.push(crc & 0xFF); // CRC低8位
+				data.push(frameHeader); // 帧尾
+				console.log('QVER----', data)
+
+				const buffer = new Uint8Array(data).buffer;
+				setTimeout(() => {
+					this.writeBLECharacteristicValue(buffer)
+				}, 1000);
+			},
+			//查询智能壳已存信息序列(LIST)
+			commandLIST() {
+				//1.手机发指令:启动字节+帧头+包头段(0x43+简易绑定号+屏大小)+LIST+信息属性+CRC校验码+帧尾。
+				//2.智能壳指令:启动字节+帧头+包头段+LIST+返回标志+加密地址+文件数+[信息名1+信息名2+...+信息名n]+CRC校验码+帧尾。
+				// 构建指令数据
+				const startBytes = new Array(10).fill(0x55); // 10个启动字节
+				const frameHeader = 0x7E; // 帧头
+				const packageType = 0x43; // 包类型
+				// 使用新的简易绑定号生成函数
+				const simpleBindId = this.generateSimpleBindId();
+
+				// 屏大小 (2 bytes): 从ID中获取
+				const screenSize = this.hexStringToArray(this.checId.slice(20, 24));
+				const packageHeader = [packageType, ...simpleBindId, ...screenSize];
+				const command = [0x4C, 0x49, 0x53, 0x54]; // "LIST"的ASCII码
+				const infoAttribute = 0x00; // 信息属性: 0=一般信息
+				// 组合数据
+				const data = [
+					...startBytes,
+					frameHeader,
+					...packageHeader,
+					...command,
+					infoAttribute
+				];
+
+				// 计算CRC校验码(从包头段开始计算)
+				const crc = this.calcCRC16(data.slice(11));
+
+				// 添加CRC和帧尾
+				data.push((crc >> 8) & 0xFF); // CRC高8位
+				data.push(crc & 0xFF); // CRC低8位
+				data.push(frameHeader); // 帧尾
+				console.log('LIST data----', this.hexArrayToHexString(data))
+
+				const buffer = new Uint8Array(data).buffer;
+				setTimeout(() => {
+					this.writeBLECharacteristicValue(buffer)
+				}, 1000);
+			},
+			//指定节目显示(PLAY)
+			commandPLAY(nameHex) {
+				console.log('nameHex----', nameHex, typeof nameHex)
+				//1.手机发指令:启动字节+帧头+0x41+ PLAY +信息属性+文件名+CRC校验码+帧尾。
+				//2.智能壳指令:启动字节+帧头+0x41+ PLAY +返回标志+CRC校验码+帧尾
+				// 构建指令数据
+				const startBytes = new Array(10).fill(0x55); // 10个启动字节
+				const frameHeader = 0x7E; // 帧头
+				const packageType = 0x41; // 包类型
+				const command = [0x50, 0x4C, 0x41, 0x59]; // "PLAY"的ASCII码
+				const infoAttribute = 0x00; // 信息属性: 0=一般信息
+				//const fileName = [0x42, 0x48] //文件名称
+				const fileName = this.hexStringToArray(nameHex)
+				// 组合数据
+				const data = [
+					...startBytes,
+					frameHeader,
+					packageType,
+					...command,
+					infoAttribute,
+					...fileName
+				];
+
+				// 计算CRC校验码(从包头段开始计算)
+				const crc = this.calcCRC16(data.slice(11));
+
+				// 添加CRC和帧尾
+				data.push((crc >> 8) & 0xFF); // CRC高8位
+				data.push(crc & 0xFF); // CRC低8位
+				data.push(frameHeader); // 帧尾
+				console.log('PLAY data----', data)
+
+				const buffer = new Uint8Array(data).buffer;
+				setTimeout(() => {
+					this.writeBLECharacteristicValue(buffer)
+				}, 1000);
+			},
+			// 调节亮度
+			commandBRIG() {
+				console.log('brightness----', this.brightness)
+				//1.手机发指令:启动字节+帧头+0x41+ BRIG +亮度+CRC校验码+帧尾。
+				//2.智能壳回指令:启动字节+帧头+0x41+ BRIG +返回标志+CRC校验码+帧尾。
+				// 构建指令数据
+				const startBytes = new Array(10).fill(0x55); // 10个启动字节
+				const frameHeader = 0x7E; // 帧头
+				const packageType = 0x41; // 包类型
+				const command = [0x42, 0x52, 0x49, 0x47]; // "BRIG"的ASCII码
+
+				//亮度:单字节,0,1,2,3。。。ASIC码
+				const brightness = this.stringToASCIIByteArray(this.brightness.toString())
+				//const brightness = 0x32
+
+				// 组合数据
+				const data = [
+					...startBytes,
+					frameHeader,
+					packageType,
+					...command,
+					brightness
+				];
+				// 计算CRC校验码(从包头段开始计算)
+				const crc = this.calcCRC16(data.slice(11));
+				// 添加CRC和帧尾
+				data.push((crc >> 8) & 0xFF); // CRC高8位
+				data.push(crc & 0xFF); // CRC低8位
+				data.push(frameHeader); // 帧尾
+				console.log('BRIG data----', data)
+				const buffer = new Uint8Array(data).buffer;
+				setTimeout(() => {
+					this.writeBLECharacteristicValue(buffer)
+				}, 1000);
+
+			},
+			commandSendTemplete(item) {
+				console.log('--------------', item)
+				uni.showLoading({
+					title: '发送中...',
+					mask: true
+				});
+				const name = 'A' + this.nameIndex++
+				this.ledName = name
+				this.screen = item.screen
+				this.ledTotal = item.fpsTotal
+				this.ledCurrent = 0
+				this.ledData = JSON.parse(item.fpsData)
+				this.commandINST(this.ledData[this.ledCurrent]);
+			},
+			// 获取画板数据
+			getDrawData() {
+				uni.showLoading({
+					title: '发送中...',
+					mask: true
+				});
+				const name = 'A' + this.nameIndex++
+				this.ledName = name
+				console.log('name--------', name)
+				// uni.getStorage({
+				// 	key: 'pixelData',
+				// 	success: (res) => {
+				// 		this.commandINST(res.data)
+				// 	}
+				// });
+				this.commandINST(this.ledData[this.ledCurrent]);
+			},
+			//添加信息指令(INST)
+			commandINST(hexString) {
+				// 1.手机发指令:启动+帧头+包头段(0x43+简易绑定号+屏大小)+INST+信息属性+文件名+总帧数+当前帧*+块描述+块数据+CRC校验码+帧尾
+				//注意:当前帧如果是0xff,则表示是结束帧,后面四个字节,第一个字节(表示速度),第二个字节(播放次数,0表示一直播放),后面二个字节保留;其他数字表示表示当前是第几帧。
+				//2.智能壳指令:启动+帧头+包头段+INST+返回标志+加密地址+CRC校验码+帧尾
+				// 构建指令数据
+				const startBytes = new Array(10).fill(0x55); // 10个启动字节
+				const frameHeader = 0x7E; // 帧头
+				const packageType = 0x43; // 包类型
+				// 使用新的简易绑定号生成函数
+				const simpleBindId = this.generateSimpleBindId();
+
+				// 屏大小 (2 bytes): 从ID中获取
+				const screenSize = this.hexStringToArray(this.checId.slice(20, 24));
+				const packageHeader = [packageType, ...simpleBindId, ...screenSize];
+				const command = [0x49, 0x4E, 0x53, 0x54]; // "INST"的ASCII码
+				const infoAttribute = 0x02; // 信息属性: 0=一般信息
+
+				const fileName = this.stringToASCIIByteArray(this.ledName) //文件名称
+				//console.log('fileName----', this.ledName)
+				// 总帧数
+				const totalFrame = this.hexStringToArray(this.ledTotal.toString(16).padStart(2, '0')
+					.toUpperCase()) //[0x01]; //this.hexStringToArray('01')
+				//console.log('totalFrame----', totalFrame)
+				// 当前帧
+				const currentFrame = this.hexStringToArray(this.ledCurrent.toString(16).padStart(2, '0')
+					.toUpperCase()) //0x00
+
+				// 块描述
+				const tblockDesc = 0x00
+
+				// 块数据
+				const blockDescription = this.hexStringToArray(hexString)
+				//const blockString = "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FF0000FF000000000000000000000000000000000000000000000000000000000000FF0000000000000000000000000000000000000000000000FF0000000000000000FF0000FF00000000000000000000000000000000FF0000FF000000000000000000FF0000000000FF0000FF00FF0000FF0000FF000000000000000000FF00FF0000FF000000000000000000FF00000000000000000000FF000000FF0000FF0000FF0000FF0000000000FF0000000000000000000000FF0000000000000000000000FF0000FF0000000000000000FF0000FF0000000000000000000000000000FF00FF000000000000000000000000FF00FF0000FF000000000000000000FF00000000000000FF0000FF0000FF0000FF0000FF0000000000000000FF0000FF000000000000000000000000FF0000000000FF0000000000000000000000000000000000000000FF0000000000000000000000000000000000FF0000000000000000000000000000000000FF00000000000000000000000000FF0000FF000000FF0000FF0000000000000000000000FF00000000000000FF0000FF000000FF00FF0000FF000000000000FF0000000000000000000000000000000000000000000000FF0000000000FF0000000000000000000000FF0000000000000000000000000000000000000000FF0000000000000000FF0000000000000000FF0000000000000000FF00000000000000FF0000FF0000FF0000FF000000000000000000FF00000000000000FF0000FF0000FF0000FF0000FF000000000000000000000000000000000000FF0000FF0000000000000000FF0000000000000000FF0000000000000000FF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FF00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
+				//const blockDescription = this.hexStringToArray(blockString)
+
+				// 组合数据
+				const data = [
+					...startBytes,
+					frameHeader,
+					...packageHeader,
+					...command,
+					infoAttribute,
+					...fileName,
+					...totalFrame,
+					...currentFrame,
+					tblockDesc,
+					...blockDescription
+				];
+				// 计算CRC校验码(从包头段开始计算)
+				const crc = this.calcCRC16(data.slice(11));
+				// 添加CRC和帧尾
+				data.push((crc >> 8) & 0xFF); // CRC高8位
+				data.push(crc & 0xFF); // CRC低8位
+				data.push(frameHeader); // 帧尾
+				console.log(this.ledCurrent, '----INST data----', this.hexArrayToHexString(data))
+				//const buffer = new Uint8Array(data).buffer;
+				setTimeout(() => {
+					//this.writeBLECharacteristicValue(buffer)
+					this.sendBLEDataInChunks(
+						this.hexArrayToHexString(data)
+					)
+				}, 1000);
+			},
+			// 发送结束针
+			commandEnd() {
+				const startBytes = new Array(10).fill(0x55); // 10个启动字节
+				const frameHeader = 0x7E; // 帧头
+				const packageType = 0x43; // 包类型
+				// 使用新的简易绑定号生成函数
+				const simpleBindId = this.generateSimpleBindId();
+
+				// 屏大小 (2 bytes): 从ID中获取
+				const screenSize = this.hexStringToArray(this.checId.slice(20, 24));
+				const packageHeader = [packageType, ...simpleBindId, ...screenSize];
+				const command = [0x49, 0x4E, 0x53, 0x54]; // "INST"的ASCII码
+				const infoAttribute = 0x02; // 信息属性: 0=一般信息
+
+				const fileName = this.stringToASCIIByteArray(this.ledName) //文件名称
+				//console.log('fileName----', this.ledName)
+				// 总帧数
+				const totalFrame = this.hexStringToArray(this.ledTotal.toString(16).padStart(2, '0')
+					.toUpperCase()) //[0x01]; //this.hexStringToArray('01')
+				//console.log('totalFrame----', totalFrame)
+
+				//结束幕:
+				const ffdata = this.hexStringToArray('ff10000000')
+				const endData = [
+					...startBytes,
+					frameHeader,
+					...packageHeader,
+					...command,
+					infoAttribute,
+					...fileName,
+					...totalFrame,
+					...ffdata
+				]
+				// 计算CRC校验码(从包头段开始计算)
+				const endcrc = this.calcCRC16(endData.slice(11));
+
+				// 添加CRC和帧尾
+				endData.push((endcrc >> 8) & 0xFF); // CRC高8位
+				endData.push(endcrc & 0xFF); // CRC低8位
+				endData.push(frameHeader); // 帧尾
+				console.log('INST endData----', this.hexArrayToHexString(endData))
+
+				const endbuffer = new Uint8Array(endData).buffer;
+				setTimeout(() => {
+					this.writeBLECharacteristicValue(endbuffer)
+				}, 1000);
+			},
+			async sendBLEDataInChunks(hexStr) {
+				const maxBytes = 60
+				const interval = 50
+				const chunks = this.splitHexData(hexStr, maxBytes)
+
+				console.log(`📦 总共需要发送 ${chunks.length} 包,每包 ${maxBytes} 字节,间隔 ${interval}ms`)
+
+				for (let i = 0; i < chunks.length; i++) {
+					const buffer = this.hexToBuffer(chunks[i])
+					try {
+						await new Promise((resolve, reject) => {
+							uni.writeBLECharacteristicValue({
+								deviceId: this.deviceId,
+								serviceId: this.serviceId,
+								characteristicId: this.characteristicId2,
+								value: buffer,
+								success: (res) => {
+									console.log(`✅ 第 ${i + 1} 包发送成功`, res)
+									resolve()
+								},
+								fail: (err) => {
+									console.error(`❌ 第 ${i + 1} 包发送失败`, err)
+									reject(err)
+								}
+							})
+						})
+					} catch (err) {
+						console.error(`🚫 发送中断,第 ${i + 1} 包发送失败`)
+						break
+					}
+					// 延迟下一包发送
+					await new Promise(r => setTimeout(r, interval))
+				}
+				console.log("🎉 数据发送完成")
+				// setTimeout(() => {
+				// 	uni.showLoading({
+				// 		title: '发送中...',
+				// 		mask: true
+				// 	});
+				// 	this.commandEnd()
+				// }, 500)
+			},
+			// 删除单个
+			commandDELL(nameHex) {
+				const startBytes = new Array(10).fill(0x55); // 10个启动字节
+				const frameHeader = 0x7E; // 帧头
+				const packageType = 0x43; // 包类型
+				// 使用新的简易绑定号生成函数
+				const simpleBindId = this.generateSimpleBindId();
+
+				// 屏大小 (2 bytes): 从ID中获取
+				const screenSize = this.hexStringToArray(this.checId.slice(20, 24));
+				const packageHeader = [packageType, ...simpleBindId, ...screenSize];
+				const command = [0x44, 0x45, 0x4C, 0x4C]; // "DELL"的ASCII码
+				const infoAttribute = 0x00; // 信息属性: 0=一般信息
+				//const fileName = [0x42, 0x48] //文件名称
+				const fileName = this.hexStringToArray(nameHex)
+				// 组合数据
+				const data = [
+					...startBytes,
+					frameHeader,
+					...packageHeader,
+					...command,
+					infoAttribute,
+					...fileName
+				];
+
+				// 计算CRC校验码(从包头段开始计算)
+				const crc = this.calcCRC16(data.slice(11));
+
+				// 添加CRC和帧尾
+				data.push((crc >> 8) & 0xFF); // CRC高8位
+				data.push(crc & 0xFF); // CRC低8位
+				data.push(frameHeader); // 帧尾
+				console.log('DELL data----', this.hexArrayToHexString(data))
+
+				const buffer = new Uint8Array(data).buffer;
+				setTimeout(() => {
+					this.writeBLECharacteristicValue(buffer)
+				}, 1000);
+			},
+			// 删除全部
+			commandDALL() {
+				const startBytes = new Array(10).fill(0x55); // 10个启动字节
+				const frameHeader = 0x7E; // 帧头
+				const packageType = 0x43; // 包类型
+				// 使用新的简易绑定号生成函数
+				const simpleBindId = this.generateSimpleBindId();
+
+				// 屏大小 (2 bytes): 从ID中获取
+				const screenSize = this.hexStringToArray(this.checId.slice(20, 24));
+				const packageHeader = [packageType, ...simpleBindId, ...screenSize];
+				const command = [0x44, 0x41, 0x4C, 0x4C]; // "DALL"的ASCII码
+				// 组合数据
+				const data = [
+					...startBytes,
+					frameHeader,
+					...packageHeader,
+					...command
+				];
+
+				// 计算CRC校验码(从包头段开始计算)
+				const crc = this.calcCRC16(data.slice(11));
+
+				// 添加CRC和帧尾
+				data.push((crc >> 8) & 0xFF); // CRC高8位
+				data.push(crc & 0xFF); // CRC低8位
+				data.push(frameHeader); // 帧尾
+				console.log('DALL data----', this.hexArrayToHexString(data))
+
+				const buffer = new Uint8Array(data).buffer;
+				setTimeout(() => {
+					this.writeBLECharacteristicValue(buffer)
+				}, 1000);
+			},
+
+			// 生成简易绑定号
+			generateSimpleBindId() {
+				if (!this.checId || !this.password) {
+					//console.error("生成简易绑定号失败:checId或password不可用");
+					uni.hideLoading();
+					uni.showToast({
+						title: '请先连接设备',
+						icon: 'none',
+						mask: true
+					});
+					return;
+				}
+				// 从checId中提取生产顺序号(最后4字节,即8个字符)
+				const productionNumber = this.checId.slice(-8); // 生产顺序号
+				// 生产顺序号+密码
+				const simpleBindData = productionNumber + this.password;
+				// 用加密数字加密
+				const encryptedSimpleBind = this.encryptSmartShellID(simpleBindData);
+				// 取前6字节作为简易绑定号
+				const simpleBindId = this.hexStringToArray(encryptedSimpleBind.slice(0, 12));
+				//console.log('简易绑定号生成----', simpleBindId);
+				return simpleBindId;
+			},
+
+			//向低功耗蓝牙设备特征值中写入二进制数据
+			writeBLECharacteristicValue(buffer) {
+				const that = this
+				uni.writeBLECharacteristicValue({
+					deviceId: this.deviceId,
+					serviceId: this.serviceId,
+					characteristicId: this.characteristicId2,
+					value: buffer,
+					writeType: 'write',
+					success: (res) => {
+						console.log('指令发送成功2----', res);
+					},
+					fail: (err) => {
+						console.log('指令发送失败2----', err);
+						uni.hideLoading();
+						uni.showToast({
+							title: '指令发送失败',
+							icon: 'none',
+							mask: true
+						});
+					}
+				});
+			},
+			//向低功耗蓝牙设备特征值中写入二进制数据2222222
+			writeBLEValue(buffer) {
+				const that = this
+				uni.writeBLECharacteristicValue({
+					deviceId: this.deviceId,
+					serviceId: this.serviceId,
+					characteristicId: this.characteristicId3,
+					value: buffer,
+					writeType: 'write',
+					success: (res) => {
+						console.log('指令发送成功----3', res);
+
+					},
+					fail: (err) => {
+						console.log('指令发送失败----3', err);
+					}
+				});
+			},
+			// 将十六进制字符串转为 ArrayBuffer
+			hexToBuffer(hexStr) {
+				const typedArray = new Uint8Array(hexStr.match(/[\da-f]{2}/gi).map(h => parseInt(h, 16)))
+				return typedArray.buffer
+			},
+
+			// 将大十六进制数据切分为每段 maxBytes 大小(单位:字节)
+			splitHexData(hexStr, maxBytes = 100) {
+				const result = []
+				for (let i = 0; i < hexStr.length; i += maxBytes * 2) {
+					result.push(hexStr.slice(i, i + maxBytes * 2))
+				}
+				return result
+			},
+
+			// 获取文件名称文件数量
+			parseFileList(hexString) {
+				// 文件数占1字节(2个十六进制字符
+				const fileCountHex = hexString.slice(32, 34);
+				const fileCount = parseInt(fileCountHex, 16);
+				console.log('文件数:', fileCount);
+
+				// 获取文件名称列表
+				const fileNames = [];
+				let nameStartIndex = 34; // 文件数后开始
+
+				for (let i = 0; i < fileCount; i++) {
+					// 每个信息名占2个字节(4个十六进制字符)
+					const nameHex = hexString.slice(nameStartIndex, nameStartIndex + 4);
+					const nameBytes = nameHex.match(/.{2}/g);
+					if (nameBytes && nameBytes.length === 2) {
+						// 将两个字节转换为ASCII字符
+						const char1 = String.fromCharCode(parseInt(nameBytes[0], 16));
+						const char2 = String.fromCharCode(parseInt(nameBytes[1], 16));
+						const fileName = char1 + char2;
+						fileNames.push({
+							fileName: fileName,
+							nameHex: nameHex
+						});
+					}
+
+					nameStartIndex += 4; // 移动到下一个文件名
+				}
+				console.log('文件名称列表:', fileNames);
+				this.fileNames = fileNames
+			},
+			// 指令ASCII码转换
+			stringToASCIIByteArray(str) {
+				return str.split('').map(char => char.charCodeAt(0));
+			},
+			//16进制数组 转 16进制字符串
+			hexArrayToHexString(hexArray) {
+				return hexArray.map(byte => byte.toString(16).padStart(2, '0')).join('');
+			},
+			// 16进制字符串转16进制数组
+			hexStringToArray(hexStr) {
+				if (!hexStr) return [];
+				return hexStr.match(/.{2}/g).map(byte => parseInt(byte, 16));
+			},
+			// 16进制数据转二进数据
+			hexStringToArrayBuffer(hexStr) {
+				const bytes = new Uint8Array(hexStr.match(/.{2}/g).map(byte => parseInt(byte, 16)));
+				return bytes.buffer;
+			},
+			// 二进数据转16进制数据
+			ab2hex(buffer) {
+				const hexArr = Array.prototype.map.call(
+					new Uint8Array(buffer),
+					function(bit) {
+						return ('00' + bit.toString(16)).slice(-2)
+					}
+				)
+				return hexArr.join('')
+			},
+			calcCRC16(data) {
+				// 创建查表
+				const table = this.buildCRC16Table();
+				let crc = 0;
+				// 计算CRC
+				for (let i = 0; i < data.length; i++) {
+					crc = ((crc << 8) & 0xFF00) ^ table[((crc >> 8) ^ data[i]) & 0xFF];
+				}
+				return crc & 0xFFFF;
+			},
+			buildCRC16Table() {
+				const table = new Array(256);
+				const poly = 0x1021; // CRC-CCITT多项式
+				for (let i = 0; i < 256; i++) {
+					let nData = i << 8;
+					let nAccum = 0;
+					for (let j = 0; j < 8; j++) {
+						if ((nData ^ nAccum) & 0x8000) {
+							nAccum = (nAccum << 1) ^ poly;
+						} else {
+							nAccum <<= 1;
+						}
+						nData <<= 1;
+					}
+					table[i] = nAccum & 0xFFFF;
+				}
+				return table;
+			},
+
+			// 设置密码
+			confirmPwd() {
+				console.log("✅ 用户密码:", this.password);
+				this.$refs.popupPwd.close();
+				this.commandBIND()
+				uni.showLoading({
+					title: '绑定中...',
+					mask: true
+				});
+			},
+			// 密码转16进制数组
+			passwordToHexArray(pwd) {
+				if (!/^\d{4}$/.test(pwd)) throw new Error('密码必须是4位数字');
+				return [
+					parseInt(pwd.slice(0, 2), 10),
+					parseInt(pwd.slice(2, 4), 10)
+				];
+			},
+			// 密码置换掉ID号中"屏的大小",加密下发
+			replaceScreenSizeInID(originalHexString, password4Digit) {
+				// 密码4个数字,每二个合起来一个字节,如3472变成 0x34 0x72
+				const high = password4Digit.slice(0, 2); // "34"
+				const low = password4Digit.slice(2, 4); // "72"
+
+				// 替换屏幕大小字段(第 10、11 个字节,即第20-23个字符)
+				const before = originalHexString.slice(0, 20);
+				const after = originalHexString.slice(24);
+
+				return before + high + low + after;
+			},
+			// 提取智能壳加密ID
+			parseBLECHECResponse(hexStr) {
+				const encryptedID = hexStr.slice(14, -6);
+				console.log('加密id-------', typeof encryptedID, encryptedID);
+				if(!encryptedID){
+					// uni.showToast({
+					// 	title: '请设置手电筒模式并在手电筒闪烁时点击确认',
+					// 	icon: 'none',
+					// 	mask: true,
+					// 	duration: 2000
+					// });
+					this.closeBLEConnection()
+					//this.openBluetooth()
+					uni.showModal({
+						title: '提示',
+						content: '请将设备设置手电筒模式并在手电筒闪烁时点击确认',
+						showCancel: false,
+						confirmText: '关闭',
+						success: function (res) {
+							if (res.confirm) {
+								console.log('用户点击确定');
+							}
+						}
+					});
+					return
+				}
+				const decryptId = this.decryptSmartShellID(encryptedID)
+				console.log('解密id-------', typeof decryptId, decryptId);
+				this.checId = decryptId
+				this.$refs.popupPwd.open();
+			},
+			// 解密函数
+			decryptSmartShellID(encryptedHex) {
+				const key = '2021101236011119680215001413714294505017';
+				const decrypted = [];
+				for (let i = 0; i < 20; i++) {
+					const byteHex = encryptedHex.substr(i * 2, 2);
+					const encryptedByte = parseInt(byteHex, 16);
+					const keyHex = key.substr(i * 2, 2)
+					const keyByte = parseInt(keyHex, 16);
+					const decryptedByte = (encryptedByte - keyByte);
+					decrypted.push(decryptedByte.toString(16).padStart(2, '0').toUpperCase());
+				}
+				return decrypted.join('');
+			},
+			// 加密函数
+			encryptSmartShellID(encryptedHex) {
+				const key = '2021101236011119680215001413714294505017';
+				const decrypted = [];
+				for (let i = 0; i < 20; i++) {
+					const byteHex = encryptedHex.substr(i * 2, 2);
+					const encryptedByte = parseInt(byteHex, 16);
+					const keyHex = key.substr(i * 2, 2)
+					const keyByte = parseInt(keyHex, 16);
+					const decryptedByte = (encryptedByte + keyByte);
+					decrypted.push(decryptedByte.toString(16).padStart(2, '0').toUpperCase());
+				}
+				return decrypted.join('');
+			},
+
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.btns_two {
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		padding: 0 40rpx;
+		margin-top: 50rpx;
+
+		.btn2 {
+			width: 47%;
+		}
+	}
+
+	.cont_led {
+		max-height: 500rpx;
+		overflow-y: auto;
+	}
+
+	.popup_pwd {
+		width: 660rpx;
+		box-sizing: border-box;
+		padding: 0 50rpx 50rpx;
+
+		.led_item {
+			display: flex;
+			align-items: center;
+			justify-content: space-between;
+			padding: 10rpx 0;
+
+			.name {
+				font-size: 30rpx;
+				color: #000000;
+				line-height: 50rpx;
+				width: 100%;
+				box-sizing: border-box;
+				padding-right: 40rpx;
+			}
+
+			.btn {
+				flex-shrink: 0;
+				width: 120rpx;
+			}
+		}
+
+		.title {
+			text-align: center;
+			font-weight: 500;
+			font-size: 32rpx;
+			color: rgba(0, 0, 0, 0.9);
+			line-height: 42rpx;
+			padding: 50rpx 0 40rpx;
+		}
+
+		.btns {
+			margin-top: 40rpx;
+		}
+	}
+
+	.bluetooth {
+		padding: 20rpx 30rpx 0;
+
+		.bluetooth_list {
+			.item {
+				background: linear-gradient(180deg, #FFFFFF 0%, rgba(255, 255, 255, 0.5) 100%);
+				box-shadow: 8rpx 8rpx 23rpx 0rpx rgba(67, 147, 248, 0.2), -8rpx -8rpx 23rpx 0rpx rgba(255, 255, 255, 0.4);
+				border-radius: 20rpx;
+				border: 2rpx solid #FFFFFF;
+				padding: 22rpx;
+				display: flex;
+				align-items: stretch;
+				justify-content: space-between;
+				margin-top: 24rpx;
+
+				.ph {
+					display: block;
+					flex-shrink: 0;
+					width: 230rpx;
+					height: 170rpx;
+					border-radius: 20rpx;
+					margin-right: 24rpx;
+				}
+
+				.info {
+					width: 100%;
+					display: flex;
+					flex-direction: column;
+					justify-content: space-between;
+
+					.name {
+						font-weight: 500;
+						font-size: 27rpx;
+						color: #000000;
+						line-height: 50rpx;
+					}
+
+					.btn {
+						width: 200rpx;
+						margin-top: 20rpx;
+					}
+				}
+			}
+		}
+
+		.bluetooth_title {
+			display: flex;
+			align-items: center;
+			justify-content: space-between;
+			margin-top: 20rpx;
+
+			text {
+				display: block;
+				width: 100%;
+				font-weight: 500;
+				font-size: 34rpx;
+				color: rgba(0, 0, 0, 0.7);
+				line-height: 38rpx;
+			}
+
+			.ico {
+				flex-shrink: 0;
+				width: 40rpx;
+				margin-right: 10rpx;
+			}
+		}
+	}
+
+	.searching_text {
+		text-align: center;
+		background: url('/static/img/bg_3.png') no-repeat center top;
+		background-size: 100% auto;
+		box-sizing: border-box;
+		padding-top: 140rpx;
+		margin-top: -100rpx;
+		height: 300rpx;
+
+		.name {
+			font-weight: 500;
+			font-size: 36rpx;
+			color: #000000;
+			line-height: 34rpx;
+		}
+
+		.text {
+			font-size: 26rpx;
+			color: rgba(0, 0, 0, 0.5);
+			line-height: 34rpx;
+			margin-top: 30rpx;
+		}
+	}
+
+	.searching_ico {
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		padding-top: 100rpx;
+	}
+
+	.page_bg {
+		width: 100%;
+		background: linear-gradient(3deg, #72E5FF 0%, #3B74EE 98%, #3B74EE 100%);
+	}
+
+	.page_bg1 {
+		width: 100%;
+		min-height: calc(100vh - var(--window-bottom));
+		background: url('/static/img/bg_2.jpg') no-repeat center top;
+		background-size: 100% auto;
+	}
+</style>
\ No newline at end of file
diff --git a/ledApp/pages/addDevice/addDevice0.vue b/ledApp/pages/addDevice/addDevice0.vue
new file mode 100644
index 0000000..65f8b60
--- /dev/null
+++ b/ledApp/pages/addDevice/addDevice0.vue
@@ -0,0 +1,487 @@
+<template>
+	<view>
+		<uv-navbar title="添加设备" :autoBack="true" :placeholder="true" leftIconColor="#fff" bgColor="#3B74EE"
+			titleStyle="color:#fff"></uv-navbar>
+
+		<view class="page_bg">
+			<view class="searching_ico" @click="getBluetoothDevices">
+				<image src="/static/img/ico_1.png" mode="widthFix"></image>
+			</view>
+
+			<view class="searching_text">
+				<view class="name">
+					搜索中
+				</view>
+				<view class="text">
+					持续自动搜索中...
+				</view>
+			</view>
+		</view>
+
+		<view class="bluetooth" v-if="bluetoothList.length != 0">
+			<view class="bluetooth_title">
+				<image class="ico" src="/static/img/ico_2.png" mode="widthFix"></image>
+				<text>已找到设备</text>
+			</view>
+			<view class="bluetooth_list">
+				<view class="item" v-for="(item,index) in bluetoothList" :key="index">
+					<image class="ph" src="/static/img/ph_1.png" mode="aspectFill"></image>
+					<view class="info">
+						<view class="name">
+							{{item.device.name}}
+						</view>
+						<view class="btn">
+							<uv-button type="primary" shape="circle" text="连接" size="small"
+								@click="createBLEConnection(item.device.address)"></uv-button>
+
+						</view>
+
+					</view>
+				</view>
+			</view>
+		</view>
+
+	</view>
+</template>
+
+<script>
+	import {
+		BleLib
+	} from '@/uni_modules/android-ble'
+	var lib = new BleLib()
+
+	export default {
+		data() {
+			return {
+				searchIng: true,
+				bluetoothList: [],
+				deviceId: '', //设备id
+				serviceId: '', //服务uuid
+				characteristicId: '', //特征uuid
+				characteristicId1: '',
+				characteristicId2: '',
+				characteristicId3: '',
+				bluetooth: false
+			}
+		},
+		onLoad() {
+
+		},
+		onShow() {
+			this.bluetooth = lib.isEnabled();
+			if (this.bluetooth) {
+				console.log('蓝牙开启', this.bluetooth)
+				this.getBluetoothDevices()
+			} else {
+				console.log('蓝牙关闭', this.bluetooth)
+			}
+		},
+		onUnload() {
+			//lib.close();
+		},
+		methods: {
+			// 蓝牙扫描
+			getBluetoothDevices() {
+				console.log('蓝牙扫描----')
+				const that = this
+				var isconnect = lib.isConnected();
+				console.log('蓝牙是否连接----',isconnect)
+				if(isconnect){
+					console.log('蓝牙是否连接----',isconnect)
+					var device = lib.getConnectMac();
+					console.log('device----',device)
+				}
+				lib.startScanBleDevice(15000, function(res) {
+					if (res.data.device.name === 'CH9140BLE2U') {
+						that.bluetoothList.push(res.data);
+						console.log(res.data)
+					}
+				})
+			},
+			// 连接蓝牙
+			createBLEConnection(address) {
+				console.log('address-----',address)
+				const that = this
+				const isconnect = lib.isConnected();
+				console.log('蓝牙是否连接----',isconnect)
+				// if(isconnect){
+				// 	const device = lib.getConnectMac();
+				// 	console.log('获取已连接设备id----',device)
+				// 	that.commandCHEC()
+				// }
+				// else{
+					lib.connect(address, false, function(res) {
+						console.log('连接', res)
+						// type==0 表示连接成功
+						if (res.type == 0) {
+							// 扫描蓝牙与特征值
+							lib.scanServices(function(resSevice) {
+								console.log('特征值--------', resSevice)
+								that.characteristicId1 = resSevice.data[2].characteristics[0].uuid;
+								that.characteristicId2 = resSevice.data[2].characteristics[1].uuid;
+								that.characteristicId3 = resSevice.data[2].characteristics[2].uuid;
+								that.onNotityReadBleData()
+								setTimeout(() => {
+									console.log("延迟执行这段代码");
+									that.setBaud()
+									//that.commandCHEC()
+								}, 2000);
+							});
+							// lib.setMtu(512, function(resMtu) {
+							// 	console.log('设置mtu(仅安卓)', resMtu)
+							// })
+							
+
+						} else {
+							if(res.message == 'bluetooth is connected'){
+								//that.commandCHEC()
+							}
+							else{
+								uni.showToast({
+									title: '蓝牙连接失败',
+									duration: 2000,
+									icon: 'none'
+								});
+							}
+						}
+					})
+				// }
+			},
+			// 开启服务消息通知
+			onNotityReadBleData() {
+				const that = this
+				console.log(that.characteristicId1)
+				lib.onNotityReadBleData(lib.getSericUUID(),
+					that.characteristicId1,
+					true,
+					function(res) {
+						console.log('开启服务消息通知', res.data)
+						
+						uni.$uv.debounce(that.extractEncryptedIDWithCheck(res.data), 2000)
+					}
+				)
+			},
+			// 构建指令数据
+			commandCHEC() {
+				//555555555555555555557e41434845430095977e
+				// 构建指令数据
+				const startBytes = new Array(10).fill(0x55); // 10个启动字节
+				const frameHeader = 0x7E; // 帧头
+				const packageType = 0x41; // 包类型
+				const command = [0x43, 0x48, 0x45, 0x43]; // "CHEC"的ASCII码
+				const reserved = 0x00; // 保留字节
+
+				// 组合数据
+				const data = [
+					...startBytes,
+					frameHeader,
+					packageType,
+					...command,
+					reserved
+				];
+
+				// 计算CRC校验码(从包头段开始计算)
+				const crc = this.calcCRC16(data.slice(11));
+
+				// 添加CRC和帧尾
+				data.push((crc >> 8) & 0xFF); // CRC高8位
+				data.push(crc & 0xFF); // CRC低8位
+				data.push(frameHeader); // 帧尾
+
+				setTimeout(() => {
+					this.writeDataToBle(data)
+				}, 2000);
+			},
+			// 设置波特率
+			setBaud() {
+				var that = this
+				var d = "06000900009600000801009F"
+				lib.writeStringDataToBle(
+					lib.getSericUUID(),
+					that.characteristicId2,
+					d,
+					function(res) {
+						console.log('设置波特率', res)
+						that.commandCHEC()
+					}
+				)
+			},
+			
+			// 发送写入指令
+			writeDataToBle(b) {
+				var that = this
+				lib.writeDataToBle(
+					lib.getSericUUID(),
+					that.characteristicId2,
+					b,
+					function(res) {
+						console.log('发送16进制数组', res)
+						
+					}
+				)
+			},
+			writeStringDataToBle() {
+				var that = this
+				var d = "555555555555555555557e41434845430095977e"
+				lib.writeStringDataToBle(
+					lib.getSericUUID(),
+					that.characteristicId2,
+					d,
+					function(res) {
+						console.log('发送16进制字符数据', res)
+					}
+				)
+			},
+			// 16进制数据转二位数据
+			hexStringToArrayBuffer(hexStr) {
+				const bytes = new Uint8Array(hexStr.match(/.{2}/g).map(byte => parseInt(byte, 16)));
+				return bytes.buffer;
+			},
+			// 二位数据转16进制数据
+			ab2hex(buffer) {
+				const hexArr = Array.prototype.map.call(
+					new Uint8Array(buffer),
+					function(bit) {
+						return ('00' + bit.toString(16)).slice(-2)
+					}
+				)
+				return hexArr.join('')
+			},
+			calcCRC16(data) {
+				// 创建查表
+				const table = this.buildCRC16Table();
+				let crc = 0;
+
+				// 计算CRC
+				for (let i = 0; i < data.length; i++) {
+					crc = ((crc << 8) & 0xFF00) ^ table[((crc >> 8) ^ data[i]) & 0xFF];
+				}
+
+				return crc & 0xFFFF;
+			},
+			buildCRC16Table() {
+				const table = new Array(256);
+				const poly = 0x1021; // CRC-CCITT多项式
+
+				for (let i = 0; i < 256; i++) {
+					let nData = i << 8;
+					let nAccum = 0;
+
+					for (let j = 0; j < 8; j++) {
+						if ((nData ^ nAccum) & 0x8000) {
+							nAccum = (nAccum << 1) ^ poly;
+						} else {
+							nAccum <<= 1;
+						}
+						nData <<= 1;
+					}
+					table[i] = nAccum & 0xFFFF;
+				}
+				return table;
+			},
+			// 判断返回数据是否正确 并提取加密id
+			extractEncryptedIDWithCheck(buffer) {
+				const hexArr = Array.prototype.map.call(
+					new Uint8Array(buffer),
+					function(bit) {
+						return ('00' + bit.toString(16)).slice(-2)
+					}
+				)
+				const responseHex = hexArr.join('').slice(20)
+				console.log('responseHex---',responseHex)
+				// 1. 提取返回标志位置(第11字节,即下标:30~31)
+				const returnFlagHex = responseHex.substring(12, 14).toUpperCase();
+				console.log('returnFlagHex---',returnFlagHex)
+				if (returnFlagHex === "30") {
+					// 正常,提取加密ID(从下标 32 开始,取 40 位
+					const encryptedID = responseHex.substring(14, 55).toUpperCase();
+					console.log('encryptedID---',encryptedID)
+					uni.showToast({
+						title: encryptedID,
+						duration: 2000,
+						icon: 'none'
+					});
+				} else if (returnFlagHex === "31") {
+					// 异常,无加密ID
+					uni.showToast({
+						title: '智能壳工作异常,返回标志0x31',
+						duration: 2000,
+						icon: 'none'
+					});
+					lib.close();
+				} else {
+					// 未知标志
+					uni.showToast({
+						title: '智能壳工作异常,未知返回标志',
+						duration: 2000,
+						icon: 'none'
+					});
+					lib.close();
+				}
+			},
+			// 解密函数
+			decryptSmartShellID(encryptedHex) {
+				if (encryptedHex.length !== 40) {
+					console.error('加密ID长度必须为40个十六进制字符(20字节)');
+					return null;
+				}
+
+				const key = [
+					0x14, 0x15, 0x0A, 0x0C, 0x24, 0x01, 0x0B, 0x13, 0x44, 0x02,
+					0x0F, 0x00, 0x0E, 0x0D, 0x47, 0x2A, 0x5E, 0x32, 0x32, 0x11
+				];
+
+				const decrypted = [];
+
+				for (let i = 0; i < 20; i++) {
+					const byteHex = encryptedHex.substr(i * 2, 2);
+					const encryptedByte = parseInt(byteHex, 16);
+					const decryptedByte = (encryptedByte - key[i] + 256) % 256;
+					decrypted.push(decryptedByte.toString(16).padStart(2, '0').toUpperCase());
+				}
+
+				return decrypted.join(' ');
+			},
+			// 加密函数
+			encryptSmartShellID(originalHex) {
+				if (originalHex.length !== 40) {
+					console.error('原始ID长度必须为40位十六进制字符串(20字节)');
+					return null;
+				}
+
+				// 加密密钥(20 字节)
+				const key = [
+					0x14, 0x15, 0x0A, 0x0C, 0x24, 0x01, 0x0B, 0x13, 0x44, 0x02,
+					0x0F, 0x00, 0x0E, 0x0D, 0x47, 0x2A, 0x5E, 0x32, 0x32, 0x11
+				];
+
+				const encrypted = [];
+
+				for (let i = 0; i < 20; i++) {
+					const byteHex = originalHex.substr(i * 2, 2);
+					const originalByte = parseInt(byteHex, 16);
+					const encryptedByte = (originalByte + key[i]) % 256; // 忽略进位
+					encrypted.push(encryptedByte.toString(16).padStart(2, '0').toUpperCase());
+				}
+
+				return encrypted.join('');
+			}
+
+
+
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.bluetooth {
+		padding: 0 30rpx;
+
+		.bluetooth_list {
+			.item {
+				background: linear-gradient(180deg, #FFFFFF 0%, rgba(255, 255, 255, 0.5) 100%);
+				box-shadow: 8rpx 8rpx 23rpx 0rpx rgba(67, 147, 248, 0.1), -8rpx -8rpx 23rpx 0rpx rgba(255, 255, 255, 0.4);
+				border-radius: 20rpx;
+				border: 2rpx solid #FFFFFF;
+				padding: 22rpx;
+				display: flex;
+				align-items: stretch;
+				justify-content: space-between;
+				margin-top: 24rpx;
+
+				.ph {
+					display: block;
+					flex-shrink: 0;
+					width: 230rpx;
+					height: 170rpx;
+					border-radius: 20rpx;
+					margin-right: 24rpx;
+				}
+
+				.info {
+					width: 100%;
+					display: flex;
+					flex-direction: column;
+					justify-content: space-between;
+
+					.name {
+						font-weight: 500;
+						font-size: 27rpx;
+						color: #000000;
+						line-height: 50rpx;
+					}
+
+					.btn {
+						width: 160rpx;
+						margin-top: 10rpx;
+					}
+				}
+			}
+		}
+
+		.bluetooth_title {
+			display: flex;
+			align-items: center;
+			justify-content: space-between;
+			margin-top: 20rpx;
+
+			text {
+				display: block;
+				width: 100%;
+				font-weight: 500;
+				font-size: 34rpx;
+				color: rgba(0, 0, 0, 0.7);
+				line-height: 38rpx;
+			}
+
+			.ico {
+				flex-shrink: 0;
+				width: 40rpx;
+				margin-right: 10rpx;
+			}
+		}
+	}
+
+	.searching_text {
+		text-align: center;
+		background: url('/static/img/bg_3.png') no-repeat center top;
+		background-size: 100% auto;
+		box-sizing: border-box;
+		padding-top: 140rpx;
+		margin-top: -100rpx;
+		height: 300rpx;
+
+		.name {
+			font-weight: 500;
+			font-size: 36rpx;
+			color: #000000;
+			line-height: 34rpx;
+		}
+
+		.text {
+			font-size: 26rpx;
+			color: rgba(0, 0, 0, 0.5);
+			line-height: 34rpx;
+			margin-top: 30rpx;
+		}
+	}
+
+	.searching_ico {
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		padding-top: 100rpx;
+	}
+
+	.page_bg {
+		width: 100%;
+		background: linear-gradient(3deg, #72E5FF 0%, #3B74EE 98%, #3B74EE 100%);
+	}
+
+	.page_bg1 {
+		width: 100%;
+		min-height: calc(100vh - var(--window-bottom));
+		background: url('/static/img/bg_2.jpg') no-repeat center top;
+		background-size: 100% auto;
+	}
+</style>
\ No newline at end of file
diff --git a/ledApp/pages/addTemplate/addTemplate copy 1.vue b/ledApp/pages/addTemplate/addTemplate copy 1.vue
new file mode 100644
index 0000000..d90c1a9
--- /dev/null
+++ b/ledApp/pages/addTemplate/addTemplate copy 1.vue
@@ -0,0 +1,548 @@
+<template>
+	<view class="page_bg">
+		<uv-navbar title="新增模版" :autoBack="true" :placeholder="true" leftIconColor="#fff" bgColor="rgba(0,0,0,0)"
+			titleStyle="color:#fff"></uv-navbar>
+
+		<view class="temp">
+			<view class="temp_item">
+				<view class="title">
+					模版名称
+				</view>
+				<view class="item">
+					<uv-input placeholder="请输入名称" border="none" v-model="tempName" fontSize="27rpx"
+						customStyle="background: #ECEFF6;padding:18rpx 20rpx;border-radius: 10rpx;"></uv-input>
+				</view>
+			</view>
+			<view class="temp_item">
+				<view class="title">
+					封面图片
+				</view>
+				<view class="item item_pic">
+					<uv-upload :fileList="fileList1" name="1" :maxCount="1" width="200rpx" height="200rpx"
+						:previewFullImage="false" @afterRead="afterRead" @delete="deletePic">
+					</uv-upload>
+				</view>
+			</view>
+			<view class="temp_item">
+				<view class="title">
+					屏幕规格
+				</view>
+				<view class="item">
+					<uv-radio-group v-model="specVal" placement="column" @change="changeSpec">
+						<uv-radio class="spec_radio" v-for="(item, index) in specList" :key="index" :label="item.name"
+							:name="item.name">
+						</uv-radio>
+					</uv-radio-group>
+				</view>
+			</view>
+			<view class="temp_item">
+				<view class="title">
+					动画方式
+				</view>
+				<view class="item">
+					<uv-radio-group v-model="modeVal" placement="column" @change="changeMode">
+						<uv-radio class="spec_radio" v-for="(item, index) in modeList" :key="index" :label="item.name"
+							:name="item.val">
+						</uv-radio>
+					</uv-radio-group>
+				</view>
+			</view>
+
+			<view class="temp_item">
+				<view class="title">
+					动画帧数
+				</view>
+				<view class="item" @click="clickTotal">
+					<uv-input placeholder="请输入" readonly border="none" v-model="total" suffixIcon="arrow-right"
+						suffixIconStyle="color: #bbb" fontSize="26rpx"
+						customStyle="background: #ECEFF6;padding:18rpx 20rpx;border-radius: 10rpx;"></uv-input>
+				</view>
+			</view>
+			<view class="temp_item">
+				<view class="title">
+					动画速度
+				</view>
+				<view class="item" @click="clickSpeed">
+					<uv-input placeholder="请输入" readonly border="none" v-model="speed" suffixIcon="arrow-right"
+						suffixIconStyle="color: #bbb" fontSize="26rpx"
+						customStyle="background: #ECEFF6;padding:18rpx 20rpx;border-radius: 10rpx;"></uv-input>
+				</view>
+			</view>
+			<!-- <view class="temp_item" v-if="modeVal == 1">
+				<view class="title">
+					循环次数
+				</view>
+				<view class="item">
+					<uv-input placeholder="请输入" readonly border="none" v-model="cycle" suffixIcon="arrow-right"
+						suffixIconStyle="color: #bbb" fontSize="26rpx" customStyle="background: #ECEFF6;padding:18rpx 20rpx;border-radius: 10rpx;"></uv-input>
+				</view>
+			</view> -->
+
+			<view class="temp_item1" v-for="(item,index) in total" :key="index">
+				<view class="title">
+					动画第{{item}}帧
+				</view>
+				<view class="item">
+					<uv-button text="编辑" @click="clickGoAnim(index)" shape="circle" size="small"
+						color="linear-gradient( 142deg, #6FD2FF 0%, #268DFF 100%)"></uv-button>
+				</view>
+			</view>
+
+		</view>
+
+
+		<!-- <view class="temp_btn" @click="clickGoAnim">
+			编辑动画
+		</view> -->
+
+		<view class="footer_none"></view>
+		<view class="footer_btn">
+			<uv-button text="保存" color="linear-gradient( 142deg, #6FD2FF 0%, #268DFF 100%)" shape="circle"
+				@click="clickTempleteAdd()"></uv-button>
+			<uv-button text="测试" color="linear-gradient( 142deg, #6FD2FF 0%, #268DFF 100%)" shape="circle"
+				style="margin-top: 30rpx;" @click="clickTempleteAdd2()"></uv-button>
+		</view>
+
+		<!-- 动画帧数 -->
+		<uv-picker ref="pickerTotal" :columns="columnsTotal" @confirm="confirmTotal"></uv-picker>
+
+		<!-- 动画速度 -->
+		<uv-picker ref="pickerSpeed" :columns="columnsSpeed" @confirm="confirmSpeed"></uv-picker>
+
+
+	</view>
+</template>
+
+<script>
+	import {
+		templeteAdd
+	} from '@/api/api.js'
+	export default {
+		data() {
+			return {
+				tempName: '',
+				pic: '',
+				specList: [{
+					name: '12*24'
+				}, {
+					name: '16*24'
+				}, {
+					name: '18*24'
+				}],
+				specVal: '12*24',
+				specWidth: 24,
+				specHeight: 12,
+				modeList: [{
+						name: '单个动画帧',
+						val: 0
+					},
+					{
+						name: '动画帧切换',
+						val: 1
+					}
+				],
+				modeVal: 0,
+				columnsTotal: [
+					[2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
+						28, 29, 30
+					]
+				],
+				total: 1, //动画总帧数
+				columnsSpeed: [
+					[10, 20, 30, 40, 50, 60, 70, 80, 90]
+				],
+				speed: 10,
+				cycle: 1,
+				fpsData: [],
+				fpsIndex: 1, //第几帧
+				fpsIds: [],
+
+				baseURL: '',
+				fileList1: [],
+				animationData: [],
+				fpsTotal: 1
+			}
+		},
+		onLoad() {
+			this.baseURL = uni.$uv.http.config.baseURL
+		},
+		onShow() {
+			console.log('------onShow')
+			//const fpsParams = uni.getStorageSync('fpsParams');
+			// if (fpsParams) {
+			// 	console.log('收到回传参数:', fpsParams);
+			// 	this.fpsData[fpsParams.fpsIndex] = fpsParams.data;
+			// 	uni.removeStorageSync('fpsParams');
+			// }
+			const that = this
+			uni.getStorage({
+				key: 'fpsParams',
+				success: (res) => {
+					console.log('获取成功:', res.data);
+					that.fpsData[res.data.fpsIndex] = res.data.data;
+				},
+				fail: (err) => {
+					console.error('获取失败:', err);
+				}
+			});
+		},
+		methods: {
+			// 屏幕规格
+			changeSpec(e) {
+				const ratio = e.split('*');
+				this.specHeight = Number(ratio[0]);
+				this.specWidth = Number(ratio[1]);
+			},
+			// 偶数行数据反转
+			toSnakeData(pixelData, cols = 24) {
+				const rows = []
+				for (let i = 0; i < pixelData.length; i += cols) {
+					let row = pixelData.slice(i, i + cols)
+					if ((rows.length + 1) % 2 === 0) {
+						row.reverse() // 偶数行反转
+					}
+					rows.push(row)
+				}
+				return rows
+			},
+			// 二维数组转字符串
+			mergeRowsToString(rows) {
+				const rowStrings = rows.map(r => r.join('')) // 每行合并
+				return rowStrings.join('') // 所有行合并
+			},
+			// 生成一帧动画其他帧数据
+			generateFrames(data, frameCount = 24) {
+				const cols = this.specWidth; // 每行 24 个
+				const rows = this.specHeight; // 共 12 行
+				const frames = [];
+				// 拆分成 12 行
+				let grid = [];
+				for (let r = 0; r < rows; r++) {
+					grid.push(data.slice(r * cols, (r + 1) * cols));
+				}
+				// 生成帧
+				for (let f = 0; f < frameCount; f++) {
+					let frame = [];
+					// 每行左移 f 次
+					for (let r = 0; r < rows; r++) {
+						let row = grid[r].slice(); // 拷贝一行
+						let shifted = row.slice(f % cols).concat(row.slice(0, f % cols));
+						frame = frame.concat(shifted);
+					}
+					frames.push(frame);
+				}
+				return frames;
+			},
+			clickTempleteAdd2() {
+
+				uni.showLoading({
+					title: '添加中',
+					mask: true
+				});
+
+				templeteAdd({
+					name: '',
+					userId: uni.getStorageSync('userId'),
+					screen: '', //屏幕
+					fpsTotal: '', //总帧数
+					speed: '', //速度
+					pic: '',
+					fpsData: ''
+				}).then(res => {
+					console.log('添加动画模版', res)
+					uni.hideLoading();
+					uni.navigateBack({
+						delta: 1
+					});
+				})
+
+			},
+			clickTempleteAdd() {
+				if (!this.tempName) {
+					uni.showToast({
+						title: '请输入模版名称',
+						duration: 2000,
+						icon: 'none'
+					});
+					return
+				}
+				if (this.fileList1.length == 0) {
+					uni.showToast({
+						title: '请上传封面图片',
+						duration: 2000,
+						icon: 'none'
+					});
+					return
+				}
+				if (this.fpsData.length == 0) {
+					uni.showToast({
+						title: '请编辑动画帧',
+						duration: 2000,
+						icon: 'none'
+					});
+					return
+				}
+				//console.log('fpsData------------', this.fpsData)
+				// 循环校验每一帧是否完整
+				// let missingIndex = -1;
+				// for (let i = 0; i < this.total; i++) {
+				// 	if (this.fpsData[i] === null || this.fpsData[i] === undefined) {
+				// 		missingIndex = i;
+				// 		break;
+				// 	}
+				// }
+				// if (missingIndex !== -1) {
+				// 	uni.showToast({
+				// 		title: '请编辑第' + (missingIndex + 1) + '帧',
+				// 		duration: 2000,
+				// 		icon: 'none'
+				// 	});
+				// 	return;
+				// }
+
+				uni.showLoading({
+					title: '添加中',
+					mask: true
+				});
+				// 动画方式
+				if (this.modeVal == 0) { //一帧动画
+					let frames = this.generateFrames(this.fpsData[0], 24);
+					const parsedData = frames.map(item => {
+						return this.toSnakeData(item, this.specWidth)
+					});
+					this.animationData = parsedData.map(item => {
+						return this.mergeRowsToString(item)
+					});
+					this.fpsTotal = 24
+				} else if (this.modeVal == 1) {
+					const parsedData = this.fpsData.map(item => {
+						return this.toSnakeData(item, this.specWidth)
+					});
+					this.animationData = parsedData.map(item => {
+						return this.mergeRowsToString(item)
+					});
+					this.fpsTotal = this.total
+				}
+
+				// setTimeout(() => {
+
+				// }, 1000)
+				const addData = {
+					name: this.tempName,
+					userId: uni.getStorageSync('userId'),
+					screen: this.specVal, //屏幕
+					fpsTotal: this.fpsTotal, //总帧数
+					speed: this.speed, //速度
+					pic: this.fileList1[0].url,
+					fpsData: JSON.stringify(this.animationData)
+				}
+				console.log('addData-----', addData)
+				templeteAdd(addData).then(res => {
+					console.log('添加动画模版', res)
+					uni.hideLoading();
+					if (res.code == 200) {
+						uni.navigateBack({
+							delta: 1
+						});
+					} else {
+						uni.showToast({
+							title: res.msg,
+							duration: 1500,
+							icon: 'none',
+							mask: true
+						});
+					}
+				})
+
+			},
+			clickSpeed() {
+				this.$refs.pickerSpeed.open();
+			},
+			confirmSpeed(e) {
+				console.log('confirmSpeed', e.value[0]);
+				this.speed = e.value[0]
+			},
+			clickTotal() {
+				if (this.total != 1) {
+					this.$refs.pickerTotal.open();
+				}
+			},
+			confirmTotal(e) {
+				console.log('confirmTotal', e.value[0]);
+				this.total = e.value[0]
+				// this.fpsData = []
+				// for (let i = 0; i < this.total; i++) {
+				// 	this.fpsData.push(null);
+				// }
+				this.adjustFpsData()
+				console.log('fpsData------------', this.fpsData)
+			},
+			changeMode(e) {
+				this.fpsData = []
+				if (e == 0) {
+					this.total = 1
+				} else {
+					this.total = 2
+				}
+				this.adjustFpsData()
+			},
+			clickGoAnim(index) {
+				uni.navigateTo({
+					url: '/pages/animation/animation?fps=' + index
+				})
+			},
+			adjustFpsData() {
+				// 如果 fpsData 太长 → 截取
+				if (this.fpsData.length > this.total) {
+					this.fpsData = this.fpsData.slice(0, this.total);
+				}
+				// 如果 fpsData 太短 → 补齐 null
+				else if (this.fpsData.length < this.total) {
+					const missing = this.total - this.fpsData.length;
+					this.fpsData = this.fpsData.concat(new Array(missing).fill(null));
+				}
+			},
+			// 删除图片
+			deletePic(event) {
+				this[`fileList${event.name}`].splice(event.index, 1)
+			},
+			// 新增图片
+			async afterRead(event) {
+				console.log('event---', event)
+				// 当设置 multiple 为 true 时, file 为数组格式,否则为对象格式
+				let lists = [].concat(event.file)
+				let fileListLen = this[`fileList${event.name}`].length
+				lists.map((item) => {
+					this[`fileList${event.name}`].push({
+						...item,
+						status: 'uploading',
+						message: '上传中'
+					})
+				})
+				for (let i = 0; i < lists.length; i++) {
+					const result = await this.uploadFilePromise(lists[i].url)
+					this.avatar = result
+					let item = this[`fileList${event.name}`][fileListLen]
+					this[`fileList${event.name}`].splice(fileListLen, 1, Object.assign(item, {
+						status: 'success',
+						message: '',
+						url: result
+					}))
+					fileListLen++
+				}
+			},
+			uploadFilePromise(url) {
+				return new Promise((resolve, reject) => {
+					let a = uni.uploadFile({
+						url: this.baseURL + '/common/uploadFileByAnny', // 仅为示例,非真实的接口地址
+						filePath: url,
+						name: 'file',
+						success: (res) => {
+							console.log('图片-----', JSON.parse(res.data).fileName)
+							setTimeout(() => {
+								resolve(JSON.parse(res.data).fileName)
+							}, 1000)
+						}
+					});
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.footer_none {
+		height: 200rpx;
+	}
+
+	.footer_btn {
+		position: fixed;
+		left: 0;
+		right: 0;
+		bottom: 0;
+		background: #fff;
+		padding: 30rpx 40rpx;
+	}
+
+	.temp_btn {
+		position: fixed;
+		left: 30rpx;
+		bottom: 60rpx;
+		right: 30rpx;
+		box-sizing: border-box;
+		height: 96rpx;
+		background: linear-gradient(142deg, #6FD2FF 0%, #268DFF 100%);
+		border-radius: 385rpx 385rpx 385rpx 385rpx;
+		font-size: 30rpx;
+		color: #FFFFFF;
+		line-height: 96rpx;
+		text-align: center;
+	}
+
+	.spec_radio {
+		margin-top: 30rpx;
+
+		&:first-child {
+			margin-top: 10rpx;
+		}
+	}
+
+	.temp {
+		padding: 0 30rpx;
+
+		.temp_item1 {
+			display: flex;
+			align-items: center;
+			justify-content: space-between;
+			background: linear-gradient(180deg, #FFFFFF 0%, rgba(255, 255, 255, 0.5) 100%);
+			box-shadow: 8rpx 8rpx 23rpx 0rpx rgba(67, 147, 248, 0.1), -8rpx -8rpx 23rpx 0rpx rgba(255, 255, 255, 0.4);
+			border-radius: 23rpx 23rpx 23rpx 23rpx;
+			border: 2rpx solid #FFFFFF;
+			padding: 30rpx;
+			box-sizing: border-box;
+			margin-top: 24rpx;
+
+			.title {
+				flex-shrink: 0;
+				font-size: 30rpx;
+				color: rgba(0, 0, 0, 0.9);
+				line-height: 40rpx;
+
+			}
+
+			.item {}
+		}
+
+		.temp_item {
+			background: linear-gradient(180deg, #FFFFFF 0%, rgba(255, 255, 255, 0.5) 100%);
+			box-shadow: 8rpx 8rpx 23rpx 0rpx rgba(67, 147, 248, 0.1), -8rpx -8rpx 23rpx 0rpx rgba(255, 255, 255, 0.4);
+			border-radius: 23rpx 23rpx 23rpx 23rpx;
+			border: 2rpx solid #FFFFFF;
+			padding: 30rpx;
+			box-sizing: border-box;
+			margin-top: 24rpx;
+
+			.title {
+				flex-shrink: 0;
+				font-size: 30rpx;
+				color: rgba(0, 0, 0, 0.9);
+				line-height: 40rpx;
+			}
+
+			.item {
+				margin-top: 20rpx;
+
+				&.item_pic {
+					margin-top: 25rpx;
+				}
+			}
+		}
+	}
+
+	.page_bg {
+		width: 100%;
+		min-height: calc(100vh - var(--window-bottom));
+		background: url('/static/img/bg_2.jpg') no-repeat center top;
+		background-size: 100% auto;
+	}
+</style>
\ No newline at end of file
diff --git a/ledApp/pages/addTemplate/addTemplate copy 2.vue b/ledApp/pages/addTemplate/addTemplate copy 2.vue
new file mode 100644
index 0000000..d90c1a9
--- /dev/null
+++ b/ledApp/pages/addTemplate/addTemplate copy 2.vue
@@ -0,0 +1,548 @@
+<template>
+	<view class="page_bg">
+		<uv-navbar title="新增模版" :autoBack="true" :placeholder="true" leftIconColor="#fff" bgColor="rgba(0,0,0,0)"
+			titleStyle="color:#fff"></uv-navbar>
+
+		<view class="temp">
+			<view class="temp_item">
+				<view class="title">
+					模版名称
+				</view>
+				<view class="item">
+					<uv-input placeholder="请输入名称" border="none" v-model="tempName" fontSize="27rpx"
+						customStyle="background: #ECEFF6;padding:18rpx 20rpx;border-radius: 10rpx;"></uv-input>
+				</view>
+			</view>
+			<view class="temp_item">
+				<view class="title">
+					封面图片
+				</view>
+				<view class="item item_pic">
+					<uv-upload :fileList="fileList1" name="1" :maxCount="1" width="200rpx" height="200rpx"
+						:previewFullImage="false" @afterRead="afterRead" @delete="deletePic">
+					</uv-upload>
+				</view>
+			</view>
+			<view class="temp_item">
+				<view class="title">
+					屏幕规格
+				</view>
+				<view class="item">
+					<uv-radio-group v-model="specVal" placement="column" @change="changeSpec">
+						<uv-radio class="spec_radio" v-for="(item, index) in specList" :key="index" :label="item.name"
+							:name="item.name">
+						</uv-radio>
+					</uv-radio-group>
+				</view>
+			</view>
+			<view class="temp_item">
+				<view class="title">
+					动画方式
+				</view>
+				<view class="item">
+					<uv-radio-group v-model="modeVal" placement="column" @change="changeMode">
+						<uv-radio class="spec_radio" v-for="(item, index) in modeList" :key="index" :label="item.name"
+							:name="item.val">
+						</uv-radio>
+					</uv-radio-group>
+				</view>
+			</view>
+
+			<view class="temp_item">
+				<view class="title">
+					动画帧数
+				</view>
+				<view class="item" @click="clickTotal">
+					<uv-input placeholder="请输入" readonly border="none" v-model="total" suffixIcon="arrow-right"
+						suffixIconStyle="color: #bbb" fontSize="26rpx"
+						customStyle="background: #ECEFF6;padding:18rpx 20rpx;border-radius: 10rpx;"></uv-input>
+				</view>
+			</view>
+			<view class="temp_item">
+				<view class="title">
+					动画速度
+				</view>
+				<view class="item" @click="clickSpeed">
+					<uv-input placeholder="请输入" readonly border="none" v-model="speed" suffixIcon="arrow-right"
+						suffixIconStyle="color: #bbb" fontSize="26rpx"
+						customStyle="background: #ECEFF6;padding:18rpx 20rpx;border-radius: 10rpx;"></uv-input>
+				</view>
+			</view>
+			<!-- <view class="temp_item" v-if="modeVal == 1">
+				<view class="title">
+					循环次数
+				</view>
+				<view class="item">
+					<uv-input placeholder="请输入" readonly border="none" v-model="cycle" suffixIcon="arrow-right"
+						suffixIconStyle="color: #bbb" fontSize="26rpx" customStyle="background: #ECEFF6;padding:18rpx 20rpx;border-radius: 10rpx;"></uv-input>
+				</view>
+			</view> -->
+
+			<view class="temp_item1" v-for="(item,index) in total" :key="index">
+				<view class="title">
+					动画第{{item}}帧
+				</view>
+				<view class="item">
+					<uv-button text="编辑" @click="clickGoAnim(index)" shape="circle" size="small"
+						color="linear-gradient( 142deg, #6FD2FF 0%, #268DFF 100%)"></uv-button>
+				</view>
+			</view>
+
+		</view>
+
+
+		<!-- <view class="temp_btn" @click="clickGoAnim">
+			编辑动画
+		</view> -->
+
+		<view class="footer_none"></view>
+		<view class="footer_btn">
+			<uv-button text="保存" color="linear-gradient( 142deg, #6FD2FF 0%, #268DFF 100%)" shape="circle"
+				@click="clickTempleteAdd()"></uv-button>
+			<uv-button text="测试" color="linear-gradient( 142deg, #6FD2FF 0%, #268DFF 100%)" shape="circle"
+				style="margin-top: 30rpx;" @click="clickTempleteAdd2()"></uv-button>
+		</view>
+
+		<!-- 动画帧数 -->
+		<uv-picker ref="pickerTotal" :columns="columnsTotal" @confirm="confirmTotal"></uv-picker>
+
+		<!-- 动画速度 -->
+		<uv-picker ref="pickerSpeed" :columns="columnsSpeed" @confirm="confirmSpeed"></uv-picker>
+
+
+	</view>
+</template>
+
+<script>
+	import {
+		templeteAdd
+	} from '@/api/api.js'
+	export default {
+		data() {
+			return {
+				tempName: '',
+				pic: '',
+				specList: [{
+					name: '12*24'
+				}, {
+					name: '16*24'
+				}, {
+					name: '18*24'
+				}],
+				specVal: '12*24',
+				specWidth: 24,
+				specHeight: 12,
+				modeList: [{
+						name: '单个动画帧',
+						val: 0
+					},
+					{
+						name: '动画帧切换',
+						val: 1
+					}
+				],
+				modeVal: 0,
+				columnsTotal: [
+					[2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
+						28, 29, 30
+					]
+				],
+				total: 1, //动画总帧数
+				columnsSpeed: [
+					[10, 20, 30, 40, 50, 60, 70, 80, 90]
+				],
+				speed: 10,
+				cycle: 1,
+				fpsData: [],
+				fpsIndex: 1, //第几帧
+				fpsIds: [],
+
+				baseURL: '',
+				fileList1: [],
+				animationData: [],
+				fpsTotal: 1
+			}
+		},
+		onLoad() {
+			this.baseURL = uni.$uv.http.config.baseURL
+		},
+		onShow() {
+			console.log('------onShow')
+			//const fpsParams = uni.getStorageSync('fpsParams');
+			// if (fpsParams) {
+			// 	console.log('收到回传参数:', fpsParams);
+			// 	this.fpsData[fpsParams.fpsIndex] = fpsParams.data;
+			// 	uni.removeStorageSync('fpsParams');
+			// }
+			const that = this
+			uni.getStorage({
+				key: 'fpsParams',
+				success: (res) => {
+					console.log('获取成功:', res.data);
+					that.fpsData[res.data.fpsIndex] = res.data.data;
+				},
+				fail: (err) => {
+					console.error('获取失败:', err);
+				}
+			});
+		},
+		methods: {
+			// 屏幕规格
+			changeSpec(e) {
+				const ratio = e.split('*');
+				this.specHeight = Number(ratio[0]);
+				this.specWidth = Number(ratio[1]);
+			},
+			// 偶数行数据反转
+			toSnakeData(pixelData, cols = 24) {
+				const rows = []
+				for (let i = 0; i < pixelData.length; i += cols) {
+					let row = pixelData.slice(i, i + cols)
+					if ((rows.length + 1) % 2 === 0) {
+						row.reverse() // 偶数行反转
+					}
+					rows.push(row)
+				}
+				return rows
+			},
+			// 二维数组转字符串
+			mergeRowsToString(rows) {
+				const rowStrings = rows.map(r => r.join('')) // 每行合并
+				return rowStrings.join('') // 所有行合并
+			},
+			// 生成一帧动画其他帧数据
+			generateFrames(data, frameCount = 24) {
+				const cols = this.specWidth; // 每行 24 个
+				const rows = this.specHeight; // 共 12 行
+				const frames = [];
+				// 拆分成 12 行
+				let grid = [];
+				for (let r = 0; r < rows; r++) {
+					grid.push(data.slice(r * cols, (r + 1) * cols));
+				}
+				// 生成帧
+				for (let f = 0; f < frameCount; f++) {
+					let frame = [];
+					// 每行左移 f 次
+					for (let r = 0; r < rows; r++) {
+						let row = grid[r].slice(); // 拷贝一行
+						let shifted = row.slice(f % cols).concat(row.slice(0, f % cols));
+						frame = frame.concat(shifted);
+					}
+					frames.push(frame);
+				}
+				return frames;
+			},
+			clickTempleteAdd2() {
+
+				uni.showLoading({
+					title: '添加中',
+					mask: true
+				});
+
+				templeteAdd({
+					name: '',
+					userId: uni.getStorageSync('userId'),
+					screen: '', //屏幕
+					fpsTotal: '', //总帧数
+					speed: '', //速度
+					pic: '',
+					fpsData: ''
+				}).then(res => {
+					console.log('添加动画模版', res)
+					uni.hideLoading();
+					uni.navigateBack({
+						delta: 1
+					});
+				})
+
+			},
+			clickTempleteAdd() {
+				if (!this.tempName) {
+					uni.showToast({
+						title: '请输入模版名称',
+						duration: 2000,
+						icon: 'none'
+					});
+					return
+				}
+				if (this.fileList1.length == 0) {
+					uni.showToast({
+						title: '请上传封面图片',
+						duration: 2000,
+						icon: 'none'
+					});
+					return
+				}
+				if (this.fpsData.length == 0) {
+					uni.showToast({
+						title: '请编辑动画帧',
+						duration: 2000,
+						icon: 'none'
+					});
+					return
+				}
+				//console.log('fpsData------------', this.fpsData)
+				// 循环校验每一帧是否完整
+				// let missingIndex = -1;
+				// for (let i = 0; i < this.total; i++) {
+				// 	if (this.fpsData[i] === null || this.fpsData[i] === undefined) {
+				// 		missingIndex = i;
+				// 		break;
+				// 	}
+				// }
+				// if (missingIndex !== -1) {
+				// 	uni.showToast({
+				// 		title: '请编辑第' + (missingIndex + 1) + '帧',
+				// 		duration: 2000,
+				// 		icon: 'none'
+				// 	});
+				// 	return;
+				// }
+
+				uni.showLoading({
+					title: '添加中',
+					mask: true
+				});
+				// 动画方式
+				if (this.modeVal == 0) { //一帧动画
+					let frames = this.generateFrames(this.fpsData[0], 24);
+					const parsedData = frames.map(item => {
+						return this.toSnakeData(item, this.specWidth)
+					});
+					this.animationData = parsedData.map(item => {
+						return this.mergeRowsToString(item)
+					});
+					this.fpsTotal = 24
+				} else if (this.modeVal == 1) {
+					const parsedData = this.fpsData.map(item => {
+						return this.toSnakeData(item, this.specWidth)
+					});
+					this.animationData = parsedData.map(item => {
+						return this.mergeRowsToString(item)
+					});
+					this.fpsTotal = this.total
+				}
+
+				// setTimeout(() => {
+
+				// }, 1000)
+				const addData = {
+					name: this.tempName,
+					userId: uni.getStorageSync('userId'),
+					screen: this.specVal, //屏幕
+					fpsTotal: this.fpsTotal, //总帧数
+					speed: this.speed, //速度
+					pic: this.fileList1[0].url,
+					fpsData: JSON.stringify(this.animationData)
+				}
+				console.log('addData-----', addData)
+				templeteAdd(addData).then(res => {
+					console.log('添加动画模版', res)
+					uni.hideLoading();
+					if (res.code == 200) {
+						uni.navigateBack({
+							delta: 1
+						});
+					} else {
+						uni.showToast({
+							title: res.msg,
+							duration: 1500,
+							icon: 'none',
+							mask: true
+						});
+					}
+				})
+
+			},
+			clickSpeed() {
+				this.$refs.pickerSpeed.open();
+			},
+			confirmSpeed(e) {
+				console.log('confirmSpeed', e.value[0]);
+				this.speed = e.value[0]
+			},
+			clickTotal() {
+				if (this.total != 1) {
+					this.$refs.pickerTotal.open();
+				}
+			},
+			confirmTotal(e) {
+				console.log('confirmTotal', e.value[0]);
+				this.total = e.value[0]
+				// this.fpsData = []
+				// for (let i = 0; i < this.total; i++) {
+				// 	this.fpsData.push(null);
+				// }
+				this.adjustFpsData()
+				console.log('fpsData------------', this.fpsData)
+			},
+			changeMode(e) {
+				this.fpsData = []
+				if (e == 0) {
+					this.total = 1
+				} else {
+					this.total = 2
+				}
+				this.adjustFpsData()
+			},
+			clickGoAnim(index) {
+				uni.navigateTo({
+					url: '/pages/animation/animation?fps=' + index
+				})
+			},
+			adjustFpsData() {
+				// 如果 fpsData 太长 → 截取
+				if (this.fpsData.length > this.total) {
+					this.fpsData = this.fpsData.slice(0, this.total);
+				}
+				// 如果 fpsData 太短 → 补齐 null
+				else if (this.fpsData.length < this.total) {
+					const missing = this.total - this.fpsData.length;
+					this.fpsData = this.fpsData.concat(new Array(missing).fill(null));
+				}
+			},
+			// 删除图片
+			deletePic(event) {
+				this[`fileList${event.name}`].splice(event.index, 1)
+			},
+			// 新增图片
+			async afterRead(event) {
+				console.log('event---', event)
+				// 当设置 multiple 为 true 时, file 为数组格式,否则为对象格式
+				let lists = [].concat(event.file)
+				let fileListLen = this[`fileList${event.name}`].length
+				lists.map((item) => {
+					this[`fileList${event.name}`].push({
+						...item,
+						status: 'uploading',
+						message: '上传中'
+					})
+				})
+				for (let i = 0; i < lists.length; i++) {
+					const result = await this.uploadFilePromise(lists[i].url)
+					this.avatar = result
+					let item = this[`fileList${event.name}`][fileListLen]
+					this[`fileList${event.name}`].splice(fileListLen, 1, Object.assign(item, {
+						status: 'success',
+						message: '',
+						url: result
+					}))
+					fileListLen++
+				}
+			},
+			uploadFilePromise(url) {
+				return new Promise((resolve, reject) => {
+					let a = uni.uploadFile({
+						url: this.baseURL + '/common/uploadFileByAnny', // 仅为示例,非真实的接口地址
+						filePath: url,
+						name: 'file',
+						success: (res) => {
+							console.log('图片-----', JSON.parse(res.data).fileName)
+							setTimeout(() => {
+								resolve(JSON.parse(res.data).fileName)
+							}, 1000)
+						}
+					});
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.footer_none {
+		height: 200rpx;
+	}
+
+	.footer_btn {
+		position: fixed;
+		left: 0;
+		right: 0;
+		bottom: 0;
+		background: #fff;
+		padding: 30rpx 40rpx;
+	}
+
+	.temp_btn {
+		position: fixed;
+		left: 30rpx;
+		bottom: 60rpx;
+		right: 30rpx;
+		box-sizing: border-box;
+		height: 96rpx;
+		background: linear-gradient(142deg, #6FD2FF 0%, #268DFF 100%);
+		border-radius: 385rpx 385rpx 385rpx 385rpx;
+		font-size: 30rpx;
+		color: #FFFFFF;
+		line-height: 96rpx;
+		text-align: center;
+	}
+
+	.spec_radio {
+		margin-top: 30rpx;
+
+		&:first-child {
+			margin-top: 10rpx;
+		}
+	}
+
+	.temp {
+		padding: 0 30rpx;
+
+		.temp_item1 {
+			display: flex;
+			align-items: center;
+			justify-content: space-between;
+			background: linear-gradient(180deg, #FFFFFF 0%, rgba(255, 255, 255, 0.5) 100%);
+			box-shadow: 8rpx 8rpx 23rpx 0rpx rgba(67, 147, 248, 0.1), -8rpx -8rpx 23rpx 0rpx rgba(255, 255, 255, 0.4);
+			border-radius: 23rpx 23rpx 23rpx 23rpx;
+			border: 2rpx solid #FFFFFF;
+			padding: 30rpx;
+			box-sizing: border-box;
+			margin-top: 24rpx;
+
+			.title {
+				flex-shrink: 0;
+				font-size: 30rpx;
+				color: rgba(0, 0, 0, 0.9);
+				line-height: 40rpx;
+
+			}
+
+			.item {}
+		}
+
+		.temp_item {
+			background: linear-gradient(180deg, #FFFFFF 0%, rgba(255, 255, 255, 0.5) 100%);
+			box-shadow: 8rpx 8rpx 23rpx 0rpx rgba(67, 147, 248, 0.1), -8rpx -8rpx 23rpx 0rpx rgba(255, 255, 255, 0.4);
+			border-radius: 23rpx 23rpx 23rpx 23rpx;
+			border: 2rpx solid #FFFFFF;
+			padding: 30rpx;
+			box-sizing: border-box;
+			margin-top: 24rpx;
+
+			.title {
+				flex-shrink: 0;
+				font-size: 30rpx;
+				color: rgba(0, 0, 0, 0.9);
+				line-height: 40rpx;
+			}
+
+			.item {
+				margin-top: 20rpx;
+
+				&.item_pic {
+					margin-top: 25rpx;
+				}
+			}
+		}
+	}
+
+	.page_bg {
+		width: 100%;
+		min-height: calc(100vh - var(--window-bottom));
+		background: url('/static/img/bg_2.jpg') no-repeat center top;
+		background-size: 100% auto;
+	}
+</style>
\ No newline at end of file
diff --git a/ledApp/pages/addTemplate/addTemplate copy 3.vue b/ledApp/pages/addTemplate/addTemplate copy 3.vue
new file mode 100644
index 0000000..dc6ca6d
--- /dev/null
+++ b/ledApp/pages/addTemplate/addTemplate copy 3.vue
@@ -0,0 +1,1056 @@
+<template>
+	<view class="page_bg">
+		
+		<uv-navbar :title="titleName" :autoBack="true" :placeholder="true" leftIconColor="#fff" bgColor="rgba(0,0,0,0)" titleStyle="color:#fff"></uv-navbar>
+		
+		<view class="temp">
+			<view class="temp_item">
+				<view class="title">
+					模版名称
+				</view>
+				<view class="item">
+					<uv-input placeholder="请输入名称" border="none" v-model="tempName" fontSize="27rpx"
+						customStyle="background: #ECEFF6;padding:18rpx 20rpx;border-radius: 10rpx;"></uv-input>
+				</view>
+			</view>
+			<view class="temp_item">
+				<view class="title">
+					封面图片
+				</view>
+				<view class="item item_pic">
+					<uv-upload :fileList="fileList1" name="1" :maxCount="1" width="200rpx" height="200rpx"
+						:previewFullImage="false" @afterRead="afterRead" @delete="deletePic">
+						<!-- <image v-if="fileList1.length != 0"
+							:src="baseURL + fileList1[0]?.url" 
+							mode="aspectFill" 
+							style="width: 200rpx;height: 200rpx;"
+						></image>
+						<view class="item_pic_btn" v-else>
+							<uv-icon name="camera-fill" color="#ccc" size="60rpx"></uv-icon>
+						</view> -->
+					</uv-upload>
+				</view>
+			</view>
+			<view class="temp_item">
+				<view class="title">
+					屏幕规格
+				</view>
+				<view class="item">
+					<uv-radio-group v-model="specVal" placement="column" @change="changeSpec">
+						<uv-radio class="spec_radio" v-for="(item, index) in specList" :key="index" :label="item.name"
+							:name="item.name">
+						</uv-radio>
+					</uv-radio-group>
+				</view>
+			</view>
+			<view class="temp_item">
+				<view class="title">
+					动画方式
+				</view>
+				<view class="item">
+					<uv-radio-group v-model="modeVal" placement="column" @change="changeMode">
+						<uv-radio class="spec_radio" v-for="(item, index) in modeList" :key="index" :label="item.name"
+							:name="item.val">
+						</uv-radio>
+					</uv-radio-group>
+				</view>
+			</view>
+
+			<view class="temp_item">
+				<view class="title">
+					动画帧数
+				</view>
+				<view class="item" @click="clickTotal">
+					<uv-input placeholder="请输入" readonly border="none" v-model="total" suffixIcon="arrow-right"
+						suffixIconStyle="color: #bbb" fontSize="26rpx"
+						customStyle="background: #ECEFF6;padding:18rpx 20rpx;border-radius: 10rpx;"></uv-input>
+				</view>
+			</view>
+			<view class="temp_item">
+				<view class="title">
+					动画速度
+				</view>
+				<view class="item" @click="clickSpeed">
+					<uv-input placeholder="请输入" readonly border="none" v-model="speed" suffixIcon="arrow-right"
+						suffixIconStyle="color: #bbb" fontSize="26rpx"
+						customStyle="background: #ECEFF6;padding:18rpx 20rpx;border-radius: 10rpx;"></uv-input>
+				</view>
+			</view>
+			<!-- <view class="temp_item" v-if="modeVal == 1">
+				<view class="title">
+					循环次数
+				</view>
+				<view class="item">
+					<uv-input placeholder="请输入" readonly border="none" v-model="cycle" suffixIcon="arrow-right"
+						suffixIconStyle="color: #bbb" fontSize="26rpx" customStyle="background: #ECEFF6;padding:18rpx 20rpx;border-radius: 10rpx;"></uv-input>
+				</view>
+			</view> -->
+			<view class="temp_item1" v-for="(item,index) in total" :key="index">
+				<view class="title">
+					动画第{{item}}帧
+				</view>
+				<view class="item">
+					<uv-button text="编辑" @click="popupAnimationOpen(index)" shape="circle" size="small" color="linear-gradient( 142deg, #6FD2FF 0%, #268DFF 100%)"></uv-button>
+				</view>
+			</view>
+		</view>
+
+		<view class="footer_none"></view>
+		<view class="footer_btn">
+			<uv-button text="保存" color="linear-gradient( 142deg, #6FD2FF 0%, #268DFF 100%)" shape="circle" @click="clickTempleteAdd()"></uv-button>
+		</view>
+
+		<!-- 动画帧数 -->
+		<uv-picker ref="pickerTotal" :columns="columnsTotal" @confirm="confirmTotal"></uv-picker>
+
+		<!-- 动画速度 -->
+		<uv-picker ref="pickerSpeed" :columns="columnsSpeed" @confirm="confirmSpeed"></uv-picker>
+		
+		<!-- 动画弹出框 -->
+		<uv-popup ref="popupAnimation" mode="center" @change="changeAnimation" :safeAreaInsetTop="false" :safeAreaInsetBottom="false" :zoom="false" duration="0">
+			<view class="animation_pixel">
+				<view class="back_btn" @click="popupAnimationClose">
+					<uv-icon name="arrow-leftward" color="#333" size="30"></uv-icon>
+				</view>
+				<!-- 动画按钮 -->
+				<view class="controls">
+					<uv-button type="primary" :plain="true" text="分辨率" @click="changeRatio()"></uv-button>
+					<uv-button type="primary" :plain="true" text="颜色" @click="changeColor()"></uv-button>
+					<uv-button type="primary" :plain="true" text="添加文字" @click="addText()"></uv-button>
+					<uv-button type="primary" :plain="true" text="清除" @click="clear()"></uv-button>
+					<uv-button type="primary" :plain="true" text="保存" @click="clickSaveData"></uv-button>
+				</view>
+				
+				<!-- 文字生成图形 -->
+				<canvas canvas-id="myCanvas" id="myCanvas" class="canvas" :style="{ width: canvasWidth + 'px', height: canvasHeight + 'px' }"></canvas>
+
+				<view class="pixel_grid" @touchstart.prevent="handleTouchStart" @touchmove.prevent="handleTouchMove"
+					@touchend.prevent="handleTouchEnd" @touchcancel.prevent="handleTouchEnd" 
+					:style="{
+						gridTemplateRows: `repeat(${height}, 1fr)`,
+						gridTemplateColumns: `repeat(${width}, 1fr)`
+					}">
+					<view class="pixel_cell" v-for="(cell, cellIndex) in pixelData" :key="cellIndex">
+						<view class="pixel_cell_item" :style="{ backgroundColor: cell }"></view>
+					</view>
+				</view>
+			</view>
+		</uv-popup>
+		
+		<!-- 分辨率 -->
+		<uv-picker ref="pickerRatio" :columns="columnsRatio" @confirm="confirmRatio"></uv-picker>
+		
+		<!-- 颜色 -->
+		<uv-popup ref="pickerColor" mode="bottom" round="10" :closeable="true">
+			<view class="color_list">
+				<view class="color_item" v-for="(item, index) in colorList" :class="{ hover: color === item }" :key="index" :style="{ backgroundColor: item }" @click="clickColor(item)"></view>
+			</view>
+		</uv-popup>
+		
+	</view>
+</template>
+
+<script>
+	import {
+		templeteAdd,
+		templeteInfo,
+		templeteEdit
+	} from '@/api/api.js'
+	let ctx = null
+	export default {
+		data() {
+			return {
+				tempName: '',
+				pic: '',
+				specList: [{
+					name: '12*24'
+				}, {
+					name: '16*24'
+				}, {
+					name: '18*24'
+				}],
+				specVal: '12*24',
+				specWidth: 24,
+				specHeight: 12,
+				modeList: [{
+						name: '单个动画帧',
+						val: 0
+					},
+					{
+						name: '动画帧切换',
+						val: 1
+					}
+				],
+				modeVal: 0,
+				columnsTotal: [
+					[2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
+						28, 29, 30
+					]
+				],
+				total: 1, //动画总帧数
+				columnsSpeed: [
+					[10, 20, 30, 40, 50, 60, 70, 80, 90]
+				],
+				speed: 10,
+				cycle: 1,
+				fpsData: [],
+				fpsIndex: 1, //第几帧
+				fpsIds: [],
+
+				baseURL: '',
+				fileList1: [],
+				animationData: [],
+				fpsTotal: 1,
+				
+				
+				
+				// 模版动画
+				pixelData: [],
+				canvasData: [],
+				canvasWidth: 240,
+				canvasHeight: 120,
+				width: 24,
+				height: 12,
+				color: '#FF0000',
+				gridRect: null, // 存储像素网格的位置信息
+				lastCell: null, // 防止重复绘制
+				columnsRatio: [
+					['12*24', '16*32', '24*48', '32*64', '40*80']
+				],
+				// columnsRatio: [
+				// 	['12*24']
+				// ],
+				colorList: [
+					'#FF0000',
+					'#00FF00',
+					'#0000FF',
+					'#FFFF00',
+					'#FFA500',
+					'#800080',
+					'#00FFFF',
+					'#FFC0CB'
+				],
+				fpsIndex: 0,
+				fpsList: [],
+				tId: '',
+				tempInfo:{},
+				titleName: '新增模版'
+				
+			}
+		},
+		onLoad(opt) {
+			this.baseURL = uni.$uv.http.config.baseURL
+			if(opt.id){
+				this.tId = opt.id
+				this.getTempleteInfo()
+				this.titleName = '编辑模版'
+			}
+			else{
+				this.titleName = '新增模版'
+			}
+		},
+		onShow() {
+			
+		},
+		onHide() {
+			this.$refs.popupAnimation.close();
+			if (typeof plus !== 'undefined') {
+				plus.screen.lockOrientation('portrait')
+			}
+		},
+		onUnload() {
+			if (typeof plus !== 'undefined') {
+				plus.screen.lockOrientation('portrait')
+			}
+		},
+		methods: {
+			// 获取编辑详情
+			getTempleteInfo(){
+				templeteInfo(this.tId).then(res=>{
+					console.log('获取编辑详情-------',res)
+					if(res.code == 200){
+						this.tempInfo = res.data
+						this.tempName = this.tempInfo.name
+						this.fileList1.push({
+							url: this.baseURL + this.tempInfo.pic
+						})
+						
+						this.specVal = this.tempInfo.screen
+						this.modeVal = this.tempInfo.mode
+						
+						this.total = this.tempInfo.cycle
+						this.fpsTotal = this.tempInfo.fpsTotal
+						this.speed = this.tempInfo.speed
+						
+						this.fpsList = JSON.parse(this.tempInfo.fpsList)
+						
+					}
+					else{
+						uni.showToast({
+							title: res.msg,
+							duration: 2000,
+							icon: 'none'
+						});
+					}
+				})
+			},
+			// 添加模版提交接口
+			clickTempleteAdd() {
+				
+				if (!this.tempName) {
+					uni.showToast({
+						title: '请输入模版名称',
+						duration: 2000,
+						icon: 'none'
+					});
+					return
+				}
+				if (this.fileList1.length == 0) {
+					uni.showToast({
+						title: '请上传封面图片',
+						duration: 2000,
+						icon: 'none'
+					});
+					return
+				}
+				
+				if (this.fpsList.length == 0) {
+					uni.showToast({
+						title: '请编辑动画帧',
+						duration: 2000,
+						icon: 'none'
+					});
+					return
+				} else {
+					for (let i = 0; i < this.fpsList.length; i++) {
+					    if (this.fpsList[i] === null) {
+							uni.showToast({
+								title: `请编辑第 ${i+1} 个动画帧`,
+								duration: 2000,
+								icon: 'none'
+							});
+							return
+					    }
+					}
+				}
+				console.log('this.fpsList=========',this.fpsList)
+				
+				uni.showLoading({
+					title: '添加中',
+					mask: true
+				});
+				
+				
+				// 遍历二维数组
+				this.fpsData = this.fpsList.map(inner =>
+				  inner.map(item => {
+				    if (item === "") {
+				      return "000000"; // 空的改成000000
+				    } else if (item.includes("#")) {
+				      return item.replace("#", ""); // 去掉#
+				    } else {
+				      return item; // 保持原样
+				    }
+				  })
+				);
+				
+				console.log('this.fpsData=========',this.fpsData)
+				
+				// 动画方式
+				if (this.modeVal == 0) { //一帧动画
+					let frames = this.generateFrames(this.fpsData[0], 24);
+					const parsedData = frames.map(item => {
+						return this.toSnakeData(item, this.specWidth)
+					});
+					this.animationData = parsedData.map(item => {
+						return this.mergeRowsToString(item)
+					});
+					this.fpsTotal = 24
+				} else if (this.modeVal == 1) {
+					console.log('modeVal--------1')
+					const parsedData = this.fpsData.map(item => {
+						return this.toSnakeData(item, this.specWidth)
+					});
+					console.log('modeVal--------1',parsedData)
+					this.animationData = parsedData.map(item => {
+						return this.mergeRowsToString(item)
+					});
+					this.fpsTotal = this.total
+				}
+				console.log('animationData--------', this.animationData)
+				
+				// 判断this.fileList1[0].url里是否有 this.baseURL
+				if (this.fileList1[0].url.includes(this.baseURL)) {
+					this.fileList1[0].url = this.fileList1[0].url.replace(this.baseURL, "");
+				}
+				
+				console.log('url----',this.fileList1[0].url);
+				
+				// 判断如果有id就编辑
+				if(this.tId){
+					const putData = {
+						id: this.tId,
+						name: this.tempName,
+						userId: uni.getStorageSync('userId'),
+						screen: this.specVal, //屏幕
+						mode: this.modeVal,
+						cycle: this.total,
+						fpsTotal: this.fpsTotal, //总帧数
+						speed: this.speed, //速度
+						pic: this.fileList1[0].url,
+						fpsData: JSON.stringify(this.animationData),
+						fpsList: JSON.stringify(this.fpsList)
+					}
+					console.log('putData-----', putData)
+					templeteEdit(putData).then(res => {
+						console.log('编辑动画模版', res)
+						uni.hideLoading();
+						if (res.code == 200) {
+							uni.navigateBack({
+								delta: 1
+							});
+						} else {
+							uni.showToast({
+								title: res.msg,
+								duration: 1500,
+								icon: 'none',
+								mask: true
+							});
+						}
+					})
+				}
+				else{
+					const addData = {
+						name: this.tempName,
+						userId: uni.getStorageSync('userId'),
+						screen: this.specVal, //屏幕
+						mode: this.modeVal,
+						cycle: this.total,
+						fpsTotal: this.fpsTotal, //总帧数
+						speed: this.speed, //速度
+						pic: this.fileList1[0].url,
+						fpsData: JSON.stringify(this.animationData),
+						fpsList: JSON.stringify(this.fpsList)
+					}
+					console.log('addData-----', addData)
+					templeteAdd(addData).then(res => {
+						console.log('添加动画模版', res)
+						uni.hideLoading();
+						if (res.code == 200) {
+							uni.navigateBack({
+								delta: 1
+							});
+						} else {
+							uni.showToast({
+								title: res.msg,
+								duration: 1500,
+								icon: 'none',
+								mask: true
+							});
+						}
+					})
+				}
+				
+			},
+			// 保存动画
+			clickSaveData() {
+				
+				this.fpsList[this.fpsIndex] = this.pixelData;
+				
+				
+				
+				
+				this.$refs.popupAnimation.close();
+			},
+			changeColor() {
+				this.$refs.pickerColor.open();
+			},
+			clickColor(item) {
+				console.log('confirmColor---', item);
+				this.color = item;
+				this.$refs.pickerColor.close();
+			},
+			changeRatio() {
+				this.$refs.pickerRatio.open();
+			},
+			// 分辨率
+			confirmRatio(e) {
+				console.log('confirmRatio---', e.value[0]);
+				const ratio = e.value[0].split('*');
+				console.log('ratio---', ratio[0]);
+				this.height = Number(ratio[0]);
+				this.width = Number(ratio[1]);
+				this.canvasWidth = this.width * 10;
+				this.canvasHeight = this.height * 10;
+				// 重新初始化一维数组
+				this.pixelData = new Array(this.height * this.width).fill('')
+				console.log('pixelData---', this.pixelData);
+			},
+			// 预先获取像素网格的位置信息
+			getGridRect(cb) {
+				if (this.gridRect) {
+					cb(this.gridRect)
+				} else {
+					uni.createSelectorQuery().in(this).select('.pixel_grid').boundingClientRect(rect => {
+						this.gridRect = rect
+						cb(rect)
+					}).exec()
+				}
+			},
+			// 计算触摸点对应的格子
+			getCellByTouch(touch, cb) {
+				this.getGridRect(rect => {
+					if (!rect) return
+					const x = touch.clientX - rect.left
+					const y = touch.clientY - rect.top
+					const cellWidth = rect.width / this.width
+					const cellHeight = rect.height / this.height
+					const colIndex = Math.floor(x / cellWidth)
+					const rowIndex = Math.floor(y / cellHeight)
+					if (
+						colIndex >= 0 && colIndex < this.width &&
+						rowIndex >= 0 && rowIndex < this.height
+					) {
+						cb({
+							rowIndex,
+							colIndex
+						})
+					}
+				})
+			},
+			handleTouchStart(e) {
+				// 阻止默认行为
+				e.preventDefault && e.preventDefault();
+			
+				if (!e.touches.length) return
+				this.getCellByTouch(e.touches[0], cell => {
+					this.lastCell = cell
+					this.handleClick(cell.rowIndex, cell.colIndex)
+				})
+			},
+			handleTouchMove(e) {
+				// 阻止默认行为
+				e.preventDefault && e.preventDefault();
+			
+				if (!e.touches.length) return
+				this.getCellByTouch(e.touches[0], cell => {
+					// 只在格子变化时才触发
+					if (!this.lastCell || this.lastCell.rowIndex !== cell.rowIndex || this.lastCell.colIndex !==
+						cell.colIndex) {
+						this.lastCell = cell
+						this.handleClick(cell.rowIndex, cell.colIndex)
+					}
+				})
+			},
+			handleTouchEnd() {
+				this.lastCell = null
+			},
+			handleClick(rowIndex, colIndex) {
+				console.log(rowIndex, colIndex)
+				// 计算一维数组的索引
+				const index = rowIndex * this.width + colIndex
+				// 如果当前像素有颜色,则清除;如果没有颜色,则添加当前选择的颜色
+				const currentColor = this.pixelData[index]
+				const newColor = currentColor ? '' : this.color
+				this.$set(this.pixelData, index, newColor)
+			},
+			clear() {
+				ctx && ctx.clearRect(0, 0, 1000, 1000)
+				// 将所有像素点设置为空字符串
+				this.pixelData = new Array(this.height * this.width).fill('')
+			},
+			// 模版动画弹出框
+			changeAnimation(e){
+				//console.log('打开关闭模版动画弹窗触发---',e)
+				if(e.show){
+					if (typeof plus !== 'undefined') {
+						plus.screen.lockOrientation('landscape-primary')
+					}
+				}
+				else{
+					if (typeof plus !== 'undefined') {
+						plus.screen.lockOrientation('portrait')
+					}
+				}
+			},
+			// 打开模版动画
+			popupAnimationOpen(index){
+				this.$refs.popupAnimation.open();
+				this.fpsIndex = index
+				
+				if(this.fpsList[this.fpsIndex] == null){
+					console.log('第一次编辑-----null')
+					this.pixelData = new Array(this.height * this.width).fill('')
+				}else{
+					console.log('已编辑过-----')
+					this.pixelData = this.fpsList[this.fpsIndex]
+				}
+			},
+			// 关闭模版动画
+			popupAnimationClose(){
+				this.$refs.popupAnimation.close();
+				this.pixelData = new Array(this.height * this.width).fill('')
+			},
+			// 屏幕规格
+			changeSpec(e) {
+				const ratio = e.split('*');
+				this.specHeight = Number(ratio[0]);
+				this.specWidth = Number(ratio[1]);
+			},
+			// 偶数行数据反转
+			toSnakeData(pixelData, cols = 24) {
+				const rows = []
+				for (let i = 0; i < pixelData.length; i += cols) {
+					let row = pixelData.slice(i, i + cols)
+					if ((rows.length + 1) % 2 === 0) {
+						row.reverse() // 偶数行反转
+					}
+					rows.push(row)
+				}
+				return rows
+			},
+			// 二维数组转字符串
+			mergeRowsToString(rows) {
+				const rowStrings = rows.map(r => r.join('')) // 每行合并
+				return rowStrings.join('') // 所有行合并
+			},
+			// 生成一帧动画其他帧数据
+			generateFrames(data, frameCount = 24) {
+				const cols = this.specWidth; // 每行 24 个
+				const rows = this.specHeight; // 共 12 行
+				const frames = [];
+				// 拆分成 12 行
+				let grid = [];
+				for (let r = 0; r < rows; r++) {
+					grid.push(data.slice(r * cols, (r + 1) * cols));
+				}
+				// 生成帧
+				for (let f = 0; f < frameCount; f++) {
+					let frame = [];
+					// 每行左移 f 次
+					for (let r = 0; r < rows; r++) {
+						let row = grid[r].slice(); // 拷贝一行
+						let shifted = row.slice(f % cols).concat(row.slice(0, f % cols));
+						frame = frame.concat(shifted);
+					}
+					frames.push(frame);
+				}
+				return frames;
+			},
+			
+			clickSpeed() {
+				this.$refs.pickerSpeed.open();
+			},
+			confirmSpeed(e) {
+				console.log('confirmSpeed', e.value[0]);
+				this.speed = e.value[0]
+			},
+			clickTotal() {
+				if (this.total != 1) {
+					this.$refs.pickerTotal.open();
+				}
+			},
+			confirmTotal(e) {
+				console.log('confirmTotal', e.value[0]);
+				this.total = e.value[0]
+				this.adjustFpsData()
+			},
+			changeMode(e) {
+				this.fpsList = []
+				this.fpsData = []
+				if (e == 0) {
+					this.total = 1
+				} else {
+					this.total = 2
+				}
+				this.adjustFpsData()
+			},
+			adjustFpsData() {
+				// 如果 fpsData 太长 → 截取
+				if (this.fpsList.length > this.total) {
+					this.fpsList = this.fpsList.slice(0, this.total);
+				}
+				// 如果 fpsData 太短 → 补齐 null
+				else if (this.fpsList.length < this.total) {
+					const missing = this.total - this.fpsList.length;
+					this.fpsList = this.fpsList.concat(new Array(missing).fill(null));
+				}
+				
+				// 如果 fpsList 太长 → 截取
+				if (this.fpsData.length > this.total) {
+					this.fpsData = this.fpsData.slice(0, this.total);
+				}
+				// 如果 fpsList 太短 → 补齐 null
+				else if (this.fpsData.length < this.total) {
+					const missing = this.total - this.fpsData.length;
+					this.fpsData = this.fpsData.concat(new Array(missing).fill(null));
+				}
+			},
+			// 删除图片
+			deletePic(event) {
+				this[`fileList${event.name}`].splice(event.index, 1)
+			},
+			// 新增图片
+			async afterRead(event) {
+				console.log('event---', event)
+				// 当设置 multiple 为 true 时, file 为数组格式,否则为对象格式
+				let lists = [].concat(event.file)
+				let fileListLen = this[`fileList${event.name}`].length
+				lists.map((item) => {
+					this[`fileList${event.name}`].push({
+						...item,
+						status: 'uploading',
+						message: '上传中'
+					})
+				})
+				for (let i = 0; i < lists.length; i++) {
+					const result = await this.uploadFilePromise(lists[i].url)
+					this.avatar = result
+					let item = this[`fileList${event.name}`][fileListLen]
+					this[`fileList${event.name}`].splice(fileListLen, 1, Object.assign(item, {
+						status: 'success',
+						message: '',
+						url: result
+					}))
+					fileListLen++
+				}
+			},
+			uploadFilePromise(url) {
+				return new Promise((resolve, reject) => {
+					let a = uni.uploadFile({
+						url: this.baseURL + '/common/uploadFileByAnny', // 仅为示例,非真实的接口地址
+						filePath: url,
+						name: 'file',
+						success: (res) => {
+							console.log('图片-----', JSON.parse(res.data))
+							setTimeout(() => {
+								resolve(JSON.parse(res.data).fileName)
+							}, 1000)
+						}
+					});
+				})
+			},
+			// 生成点阵数据
+			generateDotMatrix(char) {
+				return new Promise((resolve, reject) => {
+			
+					// 使用 uni-app 的 canvas context
+					ctx = uni.createCanvasContext('myCanvas', this);
+			
+					// 设置字体样式
+					ctx.setFontSize(this.canvasHeight * 0.9);
+					ctx.setFillStyle('#000')
+					ctx.setTextAlign('center')
+					ctx.setTextBaseline('middle')
+					// 绘制文字
+					// 计算文字位置
+					// 水平居中
+					const x = this.canvasWidth / 2;
+					// 垂直居中,稍微向上偏移一点
+					const y = this.canvasHeight / 2;
+			
+					// 绘制文字
+					ctx.fillText(char, x, y);
+			
+					// 绘制到画布
+					ctx.draw(false, () => {
+						// 获取画布数据
+						uni.canvasGetImageData({
+							canvasId: 'myCanvas',
+							x: 0,
+							y: 0,
+							width: this.canvasWidth,
+							height: this.canvasHeight,
+							success: (res) => {
+								console.log(res)
+								const imageData = res.data;
+								// 生成一维数组
+								const pixelArray = new Array(this.height * this.width).fill('')
+			
+								// 计算每个格子的像素大小
+								const cellWidth = this.canvasWidth / this.width;
+								const cellHeight = this.canvasHeight / this.height;
+			
+								// 生成一维数组
+								for (let y = 0; y < this.height; y++) {
+									for (let x = 0; x < this.width; x++) {
+										let blackPixels = 0;
+										let totalPixels = 0;
+			
+										// 对每个格子进行采样
+										for (let py = 0; py < cellHeight; py++) {
+											for (let px = 0; px < cellWidth; px++) {
+												const pixelY = Math.floor(y * cellHeight + py);
+												const pixelX = Math.floor(x * cellWidth + px);
+			
+												const idx = (pixelY * this.canvasWidth + pixelX) * 4;
+												const r = imageData[idx];
+												const g = imageData[idx + 1];
+												const b = imageData[idx + 2];
+												const a = imageData[idx + 3];
+			
+												// 计算亮度
+												const brightness = 0.3 * r + 0.59 * g + 0.11 * b;
+			
+												// 如果亮度小于阈值且透明度大于阈值,则认为是黑色
+												if (brightness < 128 && a > 128) {
+													blackPixels++;
+												}
+												totalPixels++;
+											}
+										}
+			
+										// 如果黑色像素占比超过阈值,则设置为当前选择的颜色
+										const isBlack = blackPixels / totalPixels > 0.3;
+										const arrayIndex = y * this.width + x;
+										pixelArray[arrayIndex] = isBlack ? this.color : ''; // 改为颜色值或空字符串
+									}
+								}
+			
+								resolve(pixelArray);
+							},
+							fail: (err) => {
+								reject(err);
+							}
+						});
+					});
+				});
+			},
+			
+			// 修改添加文字方法
+			async addText() {
+				uni.showModal({
+					title: '添加文字',
+					content: '',
+					editable: true,
+					placeholderText: '请输入文字',
+					success: async (res) => {
+						if (res.confirm) {
+							const text = res.content;
+							if (text.length > 0) {
+								try {
+									// 生成点阵数据
+									const dotMatrix = await this.generateDotMatrix(text);
+									console.log('点阵数据:', dotMatrix);
+									this.pixelData = dotMatrix
+								} catch (error) {
+									console.error('生成点阵失败:', error);
+									uni.showToast({
+										title: '生成点阵失败',
+										icon: 'none'
+									});
+								}
+							} else {
+								uni.showToast({
+									title: '请输入文字',
+									icon: 'none'
+								});
+							}
+						}
+					}
+				});
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	page {
+	  -webkit-text-size-adjust: 100%;
+	  text-size-adjust: 100%;
+	}
+	.item_pic_btn{
+		width: 200rpx;
+		height: 200rpx;
+		background: #eee;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		margin-bottom: 15rpx;
+	}
+	.color_list {
+		width: 100%;
+		display: flex;
+		justify-content: center;
+		flex-wrap: wrap;
+		gap: 30rpx;
+		box-sizing: border-box;
+		padding: 50rpx;
+	
+		.color_item {
+			width: 30rpx;
+			height: 30rpx;
+			border-radius: 5rpx;
+		}
+	}
+	
+	.controls {
+		position: absolute;
+		bottom: 10px;
+		left: 50px;
+		display: flex;
+		gap: 20px;
+		z-index: 9;
+	}
+	.animation_pixel{
+		width: 100vw;
+		height: 100vh;
+		position: relative;
+		.back_btn{
+			position: absolute;
+			left: 30px;
+			top: 20px;
+			z-index: 9999;
+		}
+		.canvas {
+			margin: 100rpx auto 0;
+			background: #ccc;
+			position: absolute;
+			z-index: 1;
+			left: -9999px;
+			top: -9999px;
+		}
+		.pixel_grid {
+			position: fixed;
+			left: 0;
+			top: var(--status-bar-height);
+			right: 0;
+			bottom: 0;
+			display: grid;
+			gap: 1px;
+			box-sizing: border-box;
+			background: #eee;
+			touch-action: none;
+			-webkit-touch-callout: none;
+			-webkit-user-select: none;
+			user-select: none;
+		
+			.pixel_cell {
+				display: flex;
+				align-items: center;
+				justify-content: center;
+				box-sizing: border-box;
+				background: #fff;
+		
+				.pixel_cell_item {
+					width: 50%;
+					height: 50%;
+					aspect-ratio: 1/1;
+					font-size: 10rpx;
+				}
+			}
+		}
+		
+	}
+	// -------------
+	.footer_none {
+		height: 200rpx;
+	}
+
+	.footer_btn {
+		position: fixed;
+		left: 0;
+		right: 0;
+		bottom: 0;
+		background: #fff;
+		padding: 30rpx 40rpx;
+	}
+	.footer_btn2 {
+		position: fixed;
+		left: 0;
+		right: 0;
+		bottom: 0;
+		background: #fff;
+		padding: 30rpx 40rpx;
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		box-shadow: 0 0 10rpx rgba(0, 0, 0, 0.1);
+	}
+
+	.temp_btn {
+		position: fixed;
+		left: 30rpx;
+		bottom: 60rpx;
+		right: 30rpx;
+		box-sizing: border-box;
+		height: 96rpx;
+		background: linear-gradient(142deg, #6FD2FF 0%, #268DFF 100%);
+		border-radius: 385rpx 385rpx 385rpx 385rpx;
+		font-size: 30rpx;
+		color: #FFFFFF;
+		line-height: 96rpx;
+		text-align: center;
+	}
+
+	.spec_radio {
+		margin-top: 30rpx;
+
+		&:first-child {
+			margin-top: 10rpx;
+		}
+	}
+
+	.temp {
+		padding: 0 30rpx;
+
+		.temp_item1 {
+			display: flex;
+			align-items: center;
+			justify-content: space-between;
+			background: linear-gradient(180deg, #FFFFFF 0%, rgba(255, 255, 255, 0.5) 100%);
+			box-shadow: 8rpx 8rpx 23rpx 0rpx rgba(67, 147, 248, 0.1), -8rpx -8rpx 23rpx 0rpx rgba(255, 255, 255, 0.4);
+			border-radius: 23rpx 23rpx 23rpx 23rpx;
+			border: 2rpx solid #FFFFFF;
+			padding: 30rpx;
+			box-sizing: border-box;
+			margin-top: 24rpx;
+
+			.title {
+				flex-shrink: 0;
+				font-size: 30rpx;
+				color: rgba(0, 0, 0, 0.9);
+				line-height: 40rpx;
+
+			}
+
+			.item {}
+		}
+
+		.temp_item {
+			display: flex;
+			align-items: flex-start;
+			justify-content: flex-start;
+			flex-direction: column;
+			background: linear-gradient(180deg, #FFFFFF 0%, rgba(255, 255, 255, 0.5) 100%);
+			box-shadow: 8rpx 8rpx 23rpx 0rpx rgba(67, 147, 248, 0.1), -8rpx -8rpx 23rpx 0rpx rgba(255, 255, 255, 0.4);
+			border-radius: 23rpx 23rpx 23rpx 23rpx;
+			border: 2rpx solid #FFFFFF;
+			padding: 30rpx;
+			box-sizing: border-box;
+			margin-top: 24rpx;
+
+			.title {
+				flex-shrink: 0;
+				font-size: 30rpx;
+				color: rgba(0, 0, 0, 0.9);
+				line-height: 40rpx;
+			}
+
+			.item {
+				margin-top: 20rpx;
+				width: 100%;
+				&.item_pic {
+					margin-top: 25rpx;
+				}
+			}
+		}
+	}
+
+	.page_bg {
+		width: 100%;
+		min-height: calc(100vh - var(--window-bottom));
+		background: url('/static/img/bg_2.jpg') no-repeat center top;
+		background-size: 100% auto;
+	}
+</style>
\ No newline at end of file
diff --git a/ledApp/pages/addTemplate/addTemplate copy 4.vue b/ledApp/pages/addTemplate/addTemplate copy 4.vue
new file mode 100644
index 0000000..dc6ca6d
--- /dev/null
+++ b/ledApp/pages/addTemplate/addTemplate copy 4.vue
@@ -0,0 +1,1056 @@
+<template>
+	<view class="page_bg">
+		
+		<uv-navbar :title="titleName" :autoBack="true" :placeholder="true" leftIconColor="#fff" bgColor="rgba(0,0,0,0)" titleStyle="color:#fff"></uv-navbar>
+		
+		<view class="temp">
+			<view class="temp_item">
+				<view class="title">
+					模版名称
+				</view>
+				<view class="item">
+					<uv-input placeholder="请输入名称" border="none" v-model="tempName" fontSize="27rpx"
+						customStyle="background: #ECEFF6;padding:18rpx 20rpx;border-radius: 10rpx;"></uv-input>
+				</view>
+			</view>
+			<view class="temp_item">
+				<view class="title">
+					封面图片
+				</view>
+				<view class="item item_pic">
+					<uv-upload :fileList="fileList1" name="1" :maxCount="1" width="200rpx" height="200rpx"
+						:previewFullImage="false" @afterRead="afterRead" @delete="deletePic">
+						<!-- <image v-if="fileList1.length != 0"
+							:src="baseURL + fileList1[0]?.url" 
+							mode="aspectFill" 
+							style="width: 200rpx;height: 200rpx;"
+						></image>
+						<view class="item_pic_btn" v-else>
+							<uv-icon name="camera-fill" color="#ccc" size="60rpx"></uv-icon>
+						</view> -->
+					</uv-upload>
+				</view>
+			</view>
+			<view class="temp_item">
+				<view class="title">
+					屏幕规格
+				</view>
+				<view class="item">
+					<uv-radio-group v-model="specVal" placement="column" @change="changeSpec">
+						<uv-radio class="spec_radio" v-for="(item, index) in specList" :key="index" :label="item.name"
+							:name="item.name">
+						</uv-radio>
+					</uv-radio-group>
+				</view>
+			</view>
+			<view class="temp_item">
+				<view class="title">
+					动画方式
+				</view>
+				<view class="item">
+					<uv-radio-group v-model="modeVal" placement="column" @change="changeMode">
+						<uv-radio class="spec_radio" v-for="(item, index) in modeList" :key="index" :label="item.name"
+							:name="item.val">
+						</uv-radio>
+					</uv-radio-group>
+				</view>
+			</view>
+
+			<view class="temp_item">
+				<view class="title">
+					动画帧数
+				</view>
+				<view class="item" @click="clickTotal">
+					<uv-input placeholder="请输入" readonly border="none" v-model="total" suffixIcon="arrow-right"
+						suffixIconStyle="color: #bbb" fontSize="26rpx"
+						customStyle="background: #ECEFF6;padding:18rpx 20rpx;border-radius: 10rpx;"></uv-input>
+				</view>
+			</view>
+			<view class="temp_item">
+				<view class="title">
+					动画速度
+				</view>
+				<view class="item" @click="clickSpeed">
+					<uv-input placeholder="请输入" readonly border="none" v-model="speed" suffixIcon="arrow-right"
+						suffixIconStyle="color: #bbb" fontSize="26rpx"
+						customStyle="background: #ECEFF6;padding:18rpx 20rpx;border-radius: 10rpx;"></uv-input>
+				</view>
+			</view>
+			<!-- <view class="temp_item" v-if="modeVal == 1">
+				<view class="title">
+					循环次数
+				</view>
+				<view class="item">
+					<uv-input placeholder="请输入" readonly border="none" v-model="cycle" suffixIcon="arrow-right"
+						suffixIconStyle="color: #bbb" fontSize="26rpx" customStyle="background: #ECEFF6;padding:18rpx 20rpx;border-radius: 10rpx;"></uv-input>
+				</view>
+			</view> -->
+			<view class="temp_item1" v-for="(item,index) in total" :key="index">
+				<view class="title">
+					动画第{{item}}帧
+				</view>
+				<view class="item">
+					<uv-button text="编辑" @click="popupAnimationOpen(index)" shape="circle" size="small" color="linear-gradient( 142deg, #6FD2FF 0%, #268DFF 100%)"></uv-button>
+				</view>
+			</view>
+		</view>
+
+		<view class="footer_none"></view>
+		<view class="footer_btn">
+			<uv-button text="保存" color="linear-gradient( 142deg, #6FD2FF 0%, #268DFF 100%)" shape="circle" @click="clickTempleteAdd()"></uv-button>
+		</view>
+
+		<!-- 动画帧数 -->
+		<uv-picker ref="pickerTotal" :columns="columnsTotal" @confirm="confirmTotal"></uv-picker>
+
+		<!-- 动画速度 -->
+		<uv-picker ref="pickerSpeed" :columns="columnsSpeed" @confirm="confirmSpeed"></uv-picker>
+		
+		<!-- 动画弹出框 -->
+		<uv-popup ref="popupAnimation" mode="center" @change="changeAnimation" :safeAreaInsetTop="false" :safeAreaInsetBottom="false" :zoom="false" duration="0">
+			<view class="animation_pixel">
+				<view class="back_btn" @click="popupAnimationClose">
+					<uv-icon name="arrow-leftward" color="#333" size="30"></uv-icon>
+				</view>
+				<!-- 动画按钮 -->
+				<view class="controls">
+					<uv-button type="primary" :plain="true" text="分辨率" @click="changeRatio()"></uv-button>
+					<uv-button type="primary" :plain="true" text="颜色" @click="changeColor()"></uv-button>
+					<uv-button type="primary" :plain="true" text="添加文字" @click="addText()"></uv-button>
+					<uv-button type="primary" :plain="true" text="清除" @click="clear()"></uv-button>
+					<uv-button type="primary" :plain="true" text="保存" @click="clickSaveData"></uv-button>
+				</view>
+				
+				<!-- 文字生成图形 -->
+				<canvas canvas-id="myCanvas" id="myCanvas" class="canvas" :style="{ width: canvasWidth + 'px', height: canvasHeight + 'px' }"></canvas>
+
+				<view class="pixel_grid" @touchstart.prevent="handleTouchStart" @touchmove.prevent="handleTouchMove"
+					@touchend.prevent="handleTouchEnd" @touchcancel.prevent="handleTouchEnd" 
+					:style="{
+						gridTemplateRows: `repeat(${height}, 1fr)`,
+						gridTemplateColumns: `repeat(${width}, 1fr)`
+					}">
+					<view class="pixel_cell" v-for="(cell, cellIndex) in pixelData" :key="cellIndex">
+						<view class="pixel_cell_item" :style="{ backgroundColor: cell }"></view>
+					</view>
+				</view>
+			</view>
+		</uv-popup>
+		
+		<!-- 分辨率 -->
+		<uv-picker ref="pickerRatio" :columns="columnsRatio" @confirm="confirmRatio"></uv-picker>
+		
+		<!-- 颜色 -->
+		<uv-popup ref="pickerColor" mode="bottom" round="10" :closeable="true">
+			<view class="color_list">
+				<view class="color_item" v-for="(item, index) in colorList" :class="{ hover: color === item }" :key="index" :style="{ backgroundColor: item }" @click="clickColor(item)"></view>
+			</view>
+		</uv-popup>
+		
+	</view>
+</template>
+
+<script>
+	import {
+		templeteAdd,
+		templeteInfo,
+		templeteEdit
+	} from '@/api/api.js'
+	let ctx = null
+	export default {
+		data() {
+			return {
+				tempName: '',
+				pic: '',
+				specList: [{
+					name: '12*24'
+				}, {
+					name: '16*24'
+				}, {
+					name: '18*24'
+				}],
+				specVal: '12*24',
+				specWidth: 24,
+				specHeight: 12,
+				modeList: [{
+						name: '单个动画帧',
+						val: 0
+					},
+					{
+						name: '动画帧切换',
+						val: 1
+					}
+				],
+				modeVal: 0,
+				columnsTotal: [
+					[2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
+						28, 29, 30
+					]
+				],
+				total: 1, //动画总帧数
+				columnsSpeed: [
+					[10, 20, 30, 40, 50, 60, 70, 80, 90]
+				],
+				speed: 10,
+				cycle: 1,
+				fpsData: [],
+				fpsIndex: 1, //第几帧
+				fpsIds: [],
+
+				baseURL: '',
+				fileList1: [],
+				animationData: [],
+				fpsTotal: 1,
+				
+				
+				
+				// 模版动画
+				pixelData: [],
+				canvasData: [],
+				canvasWidth: 240,
+				canvasHeight: 120,
+				width: 24,
+				height: 12,
+				color: '#FF0000',
+				gridRect: null, // 存储像素网格的位置信息
+				lastCell: null, // 防止重复绘制
+				columnsRatio: [
+					['12*24', '16*32', '24*48', '32*64', '40*80']
+				],
+				// columnsRatio: [
+				// 	['12*24']
+				// ],
+				colorList: [
+					'#FF0000',
+					'#00FF00',
+					'#0000FF',
+					'#FFFF00',
+					'#FFA500',
+					'#800080',
+					'#00FFFF',
+					'#FFC0CB'
+				],
+				fpsIndex: 0,
+				fpsList: [],
+				tId: '',
+				tempInfo:{},
+				titleName: '新增模版'
+				
+			}
+		},
+		onLoad(opt) {
+			this.baseURL = uni.$uv.http.config.baseURL
+			if(opt.id){
+				this.tId = opt.id
+				this.getTempleteInfo()
+				this.titleName = '编辑模版'
+			}
+			else{
+				this.titleName = '新增模版'
+			}
+		},
+		onShow() {
+			
+		},
+		onHide() {
+			this.$refs.popupAnimation.close();
+			if (typeof plus !== 'undefined') {
+				plus.screen.lockOrientation('portrait')
+			}
+		},
+		onUnload() {
+			if (typeof plus !== 'undefined') {
+				plus.screen.lockOrientation('portrait')
+			}
+		},
+		methods: {
+			// 获取编辑详情
+			getTempleteInfo(){
+				templeteInfo(this.tId).then(res=>{
+					console.log('获取编辑详情-------',res)
+					if(res.code == 200){
+						this.tempInfo = res.data
+						this.tempName = this.tempInfo.name
+						this.fileList1.push({
+							url: this.baseURL + this.tempInfo.pic
+						})
+						
+						this.specVal = this.tempInfo.screen
+						this.modeVal = this.tempInfo.mode
+						
+						this.total = this.tempInfo.cycle
+						this.fpsTotal = this.tempInfo.fpsTotal
+						this.speed = this.tempInfo.speed
+						
+						this.fpsList = JSON.parse(this.tempInfo.fpsList)
+						
+					}
+					else{
+						uni.showToast({
+							title: res.msg,
+							duration: 2000,
+							icon: 'none'
+						});
+					}
+				})
+			},
+			// 添加模版提交接口
+			clickTempleteAdd() {
+				
+				if (!this.tempName) {
+					uni.showToast({
+						title: '请输入模版名称',
+						duration: 2000,
+						icon: 'none'
+					});
+					return
+				}
+				if (this.fileList1.length == 0) {
+					uni.showToast({
+						title: '请上传封面图片',
+						duration: 2000,
+						icon: 'none'
+					});
+					return
+				}
+				
+				if (this.fpsList.length == 0) {
+					uni.showToast({
+						title: '请编辑动画帧',
+						duration: 2000,
+						icon: 'none'
+					});
+					return
+				} else {
+					for (let i = 0; i < this.fpsList.length; i++) {
+					    if (this.fpsList[i] === null) {
+							uni.showToast({
+								title: `请编辑第 ${i+1} 个动画帧`,
+								duration: 2000,
+								icon: 'none'
+							});
+							return
+					    }
+					}
+				}
+				console.log('this.fpsList=========',this.fpsList)
+				
+				uni.showLoading({
+					title: '添加中',
+					mask: true
+				});
+				
+				
+				// 遍历二维数组
+				this.fpsData = this.fpsList.map(inner =>
+				  inner.map(item => {
+				    if (item === "") {
+				      return "000000"; // 空的改成000000
+				    } else if (item.includes("#")) {
+				      return item.replace("#", ""); // 去掉#
+				    } else {
+				      return item; // 保持原样
+				    }
+				  })
+				);
+				
+				console.log('this.fpsData=========',this.fpsData)
+				
+				// 动画方式
+				if (this.modeVal == 0) { //一帧动画
+					let frames = this.generateFrames(this.fpsData[0], 24);
+					const parsedData = frames.map(item => {
+						return this.toSnakeData(item, this.specWidth)
+					});
+					this.animationData = parsedData.map(item => {
+						return this.mergeRowsToString(item)
+					});
+					this.fpsTotal = 24
+				} else if (this.modeVal == 1) {
+					console.log('modeVal--------1')
+					const parsedData = this.fpsData.map(item => {
+						return this.toSnakeData(item, this.specWidth)
+					});
+					console.log('modeVal--------1',parsedData)
+					this.animationData = parsedData.map(item => {
+						return this.mergeRowsToString(item)
+					});
+					this.fpsTotal = this.total
+				}
+				console.log('animationData--------', this.animationData)
+				
+				// 判断this.fileList1[0].url里是否有 this.baseURL
+				if (this.fileList1[0].url.includes(this.baseURL)) {
+					this.fileList1[0].url = this.fileList1[0].url.replace(this.baseURL, "");
+				}
+				
+				console.log('url----',this.fileList1[0].url);
+				
+				// 判断如果有id就编辑
+				if(this.tId){
+					const putData = {
+						id: this.tId,
+						name: this.tempName,
+						userId: uni.getStorageSync('userId'),
+						screen: this.specVal, //屏幕
+						mode: this.modeVal,
+						cycle: this.total,
+						fpsTotal: this.fpsTotal, //总帧数
+						speed: this.speed, //速度
+						pic: this.fileList1[0].url,
+						fpsData: JSON.stringify(this.animationData),
+						fpsList: JSON.stringify(this.fpsList)
+					}
+					console.log('putData-----', putData)
+					templeteEdit(putData).then(res => {
+						console.log('编辑动画模版', res)
+						uni.hideLoading();
+						if (res.code == 200) {
+							uni.navigateBack({
+								delta: 1
+							});
+						} else {
+							uni.showToast({
+								title: res.msg,
+								duration: 1500,
+								icon: 'none',
+								mask: true
+							});
+						}
+					})
+				}
+				else{
+					const addData = {
+						name: this.tempName,
+						userId: uni.getStorageSync('userId'),
+						screen: this.specVal, //屏幕
+						mode: this.modeVal,
+						cycle: this.total,
+						fpsTotal: this.fpsTotal, //总帧数
+						speed: this.speed, //速度
+						pic: this.fileList1[0].url,
+						fpsData: JSON.stringify(this.animationData),
+						fpsList: JSON.stringify(this.fpsList)
+					}
+					console.log('addData-----', addData)
+					templeteAdd(addData).then(res => {
+						console.log('添加动画模版', res)
+						uni.hideLoading();
+						if (res.code == 200) {
+							uni.navigateBack({
+								delta: 1
+							});
+						} else {
+							uni.showToast({
+								title: res.msg,
+								duration: 1500,
+								icon: 'none',
+								mask: true
+							});
+						}
+					})
+				}
+				
+			},
+			// 保存动画
+			clickSaveData() {
+				
+				this.fpsList[this.fpsIndex] = this.pixelData;
+				
+				
+				
+				
+				this.$refs.popupAnimation.close();
+			},
+			changeColor() {
+				this.$refs.pickerColor.open();
+			},
+			clickColor(item) {
+				console.log('confirmColor---', item);
+				this.color = item;
+				this.$refs.pickerColor.close();
+			},
+			changeRatio() {
+				this.$refs.pickerRatio.open();
+			},
+			// 分辨率
+			confirmRatio(e) {
+				console.log('confirmRatio---', e.value[0]);
+				const ratio = e.value[0].split('*');
+				console.log('ratio---', ratio[0]);
+				this.height = Number(ratio[0]);
+				this.width = Number(ratio[1]);
+				this.canvasWidth = this.width * 10;
+				this.canvasHeight = this.height * 10;
+				// 重新初始化一维数组
+				this.pixelData = new Array(this.height * this.width).fill('')
+				console.log('pixelData---', this.pixelData);
+			},
+			// 预先获取像素网格的位置信息
+			getGridRect(cb) {
+				if (this.gridRect) {
+					cb(this.gridRect)
+				} else {
+					uni.createSelectorQuery().in(this).select('.pixel_grid').boundingClientRect(rect => {
+						this.gridRect = rect
+						cb(rect)
+					}).exec()
+				}
+			},
+			// 计算触摸点对应的格子
+			getCellByTouch(touch, cb) {
+				this.getGridRect(rect => {
+					if (!rect) return
+					const x = touch.clientX - rect.left
+					const y = touch.clientY - rect.top
+					const cellWidth = rect.width / this.width
+					const cellHeight = rect.height / this.height
+					const colIndex = Math.floor(x / cellWidth)
+					const rowIndex = Math.floor(y / cellHeight)
+					if (
+						colIndex >= 0 && colIndex < this.width &&
+						rowIndex >= 0 && rowIndex < this.height
+					) {
+						cb({
+							rowIndex,
+							colIndex
+						})
+					}
+				})
+			},
+			handleTouchStart(e) {
+				// 阻止默认行为
+				e.preventDefault && e.preventDefault();
+			
+				if (!e.touches.length) return
+				this.getCellByTouch(e.touches[0], cell => {
+					this.lastCell = cell
+					this.handleClick(cell.rowIndex, cell.colIndex)
+				})
+			},
+			handleTouchMove(e) {
+				// 阻止默认行为
+				e.preventDefault && e.preventDefault();
+			
+				if (!e.touches.length) return
+				this.getCellByTouch(e.touches[0], cell => {
+					// 只在格子变化时才触发
+					if (!this.lastCell || this.lastCell.rowIndex !== cell.rowIndex || this.lastCell.colIndex !==
+						cell.colIndex) {
+						this.lastCell = cell
+						this.handleClick(cell.rowIndex, cell.colIndex)
+					}
+				})
+			},
+			handleTouchEnd() {
+				this.lastCell = null
+			},
+			handleClick(rowIndex, colIndex) {
+				console.log(rowIndex, colIndex)
+				// 计算一维数组的索引
+				const index = rowIndex * this.width + colIndex
+				// 如果当前像素有颜色,则清除;如果没有颜色,则添加当前选择的颜色
+				const currentColor = this.pixelData[index]
+				const newColor = currentColor ? '' : this.color
+				this.$set(this.pixelData, index, newColor)
+			},
+			clear() {
+				ctx && ctx.clearRect(0, 0, 1000, 1000)
+				// 将所有像素点设置为空字符串
+				this.pixelData = new Array(this.height * this.width).fill('')
+			},
+			// 模版动画弹出框
+			changeAnimation(e){
+				//console.log('打开关闭模版动画弹窗触发---',e)
+				if(e.show){
+					if (typeof plus !== 'undefined') {
+						plus.screen.lockOrientation('landscape-primary')
+					}
+				}
+				else{
+					if (typeof plus !== 'undefined') {
+						plus.screen.lockOrientation('portrait')
+					}
+				}
+			},
+			// 打开模版动画
+			popupAnimationOpen(index){
+				this.$refs.popupAnimation.open();
+				this.fpsIndex = index
+				
+				if(this.fpsList[this.fpsIndex] == null){
+					console.log('第一次编辑-----null')
+					this.pixelData = new Array(this.height * this.width).fill('')
+				}else{
+					console.log('已编辑过-----')
+					this.pixelData = this.fpsList[this.fpsIndex]
+				}
+			},
+			// 关闭模版动画
+			popupAnimationClose(){
+				this.$refs.popupAnimation.close();
+				this.pixelData = new Array(this.height * this.width).fill('')
+			},
+			// 屏幕规格
+			changeSpec(e) {
+				const ratio = e.split('*');
+				this.specHeight = Number(ratio[0]);
+				this.specWidth = Number(ratio[1]);
+			},
+			// 偶数行数据反转
+			toSnakeData(pixelData, cols = 24) {
+				const rows = []
+				for (let i = 0; i < pixelData.length; i += cols) {
+					let row = pixelData.slice(i, i + cols)
+					if ((rows.length + 1) % 2 === 0) {
+						row.reverse() // 偶数行反转
+					}
+					rows.push(row)
+				}
+				return rows
+			},
+			// 二维数组转字符串
+			mergeRowsToString(rows) {
+				const rowStrings = rows.map(r => r.join('')) // 每行合并
+				return rowStrings.join('') // 所有行合并
+			},
+			// 生成一帧动画其他帧数据
+			generateFrames(data, frameCount = 24) {
+				const cols = this.specWidth; // 每行 24 个
+				const rows = this.specHeight; // 共 12 行
+				const frames = [];
+				// 拆分成 12 行
+				let grid = [];
+				for (let r = 0; r < rows; r++) {
+					grid.push(data.slice(r * cols, (r + 1) * cols));
+				}
+				// 生成帧
+				for (let f = 0; f < frameCount; f++) {
+					let frame = [];
+					// 每行左移 f 次
+					for (let r = 0; r < rows; r++) {
+						let row = grid[r].slice(); // 拷贝一行
+						let shifted = row.slice(f % cols).concat(row.slice(0, f % cols));
+						frame = frame.concat(shifted);
+					}
+					frames.push(frame);
+				}
+				return frames;
+			},
+			
+			clickSpeed() {
+				this.$refs.pickerSpeed.open();
+			},
+			confirmSpeed(e) {
+				console.log('confirmSpeed', e.value[0]);
+				this.speed = e.value[0]
+			},
+			clickTotal() {
+				if (this.total != 1) {
+					this.$refs.pickerTotal.open();
+				}
+			},
+			confirmTotal(e) {
+				console.log('confirmTotal', e.value[0]);
+				this.total = e.value[0]
+				this.adjustFpsData()
+			},
+			changeMode(e) {
+				this.fpsList = []
+				this.fpsData = []
+				if (e == 0) {
+					this.total = 1
+				} else {
+					this.total = 2
+				}
+				this.adjustFpsData()
+			},
+			adjustFpsData() {
+				// 如果 fpsData 太长 → 截取
+				if (this.fpsList.length > this.total) {
+					this.fpsList = this.fpsList.slice(0, this.total);
+				}
+				// 如果 fpsData 太短 → 补齐 null
+				else if (this.fpsList.length < this.total) {
+					const missing = this.total - this.fpsList.length;
+					this.fpsList = this.fpsList.concat(new Array(missing).fill(null));
+				}
+				
+				// 如果 fpsList 太长 → 截取
+				if (this.fpsData.length > this.total) {
+					this.fpsData = this.fpsData.slice(0, this.total);
+				}
+				// 如果 fpsList 太短 → 补齐 null
+				else if (this.fpsData.length < this.total) {
+					const missing = this.total - this.fpsData.length;
+					this.fpsData = this.fpsData.concat(new Array(missing).fill(null));
+				}
+			},
+			// 删除图片
+			deletePic(event) {
+				this[`fileList${event.name}`].splice(event.index, 1)
+			},
+			// 新增图片
+			async afterRead(event) {
+				console.log('event---', event)
+				// 当设置 multiple 为 true 时, file 为数组格式,否则为对象格式
+				let lists = [].concat(event.file)
+				let fileListLen = this[`fileList${event.name}`].length
+				lists.map((item) => {
+					this[`fileList${event.name}`].push({
+						...item,
+						status: 'uploading',
+						message: '上传中'
+					})
+				})
+				for (let i = 0; i < lists.length; i++) {
+					const result = await this.uploadFilePromise(lists[i].url)
+					this.avatar = result
+					let item = this[`fileList${event.name}`][fileListLen]
+					this[`fileList${event.name}`].splice(fileListLen, 1, Object.assign(item, {
+						status: 'success',
+						message: '',
+						url: result
+					}))
+					fileListLen++
+				}
+			},
+			uploadFilePromise(url) {
+				return new Promise((resolve, reject) => {
+					let a = uni.uploadFile({
+						url: this.baseURL + '/common/uploadFileByAnny', // 仅为示例,非真实的接口地址
+						filePath: url,
+						name: 'file',
+						success: (res) => {
+							console.log('图片-----', JSON.parse(res.data))
+							setTimeout(() => {
+								resolve(JSON.parse(res.data).fileName)
+							}, 1000)
+						}
+					});
+				})
+			},
+			// 生成点阵数据
+			generateDotMatrix(char) {
+				return new Promise((resolve, reject) => {
+			
+					// 使用 uni-app 的 canvas context
+					ctx = uni.createCanvasContext('myCanvas', this);
+			
+					// 设置字体样式
+					ctx.setFontSize(this.canvasHeight * 0.9);
+					ctx.setFillStyle('#000')
+					ctx.setTextAlign('center')
+					ctx.setTextBaseline('middle')
+					// 绘制文字
+					// 计算文字位置
+					// 水平居中
+					const x = this.canvasWidth / 2;
+					// 垂直居中,稍微向上偏移一点
+					const y = this.canvasHeight / 2;
+			
+					// 绘制文字
+					ctx.fillText(char, x, y);
+			
+					// 绘制到画布
+					ctx.draw(false, () => {
+						// 获取画布数据
+						uni.canvasGetImageData({
+							canvasId: 'myCanvas',
+							x: 0,
+							y: 0,
+							width: this.canvasWidth,
+							height: this.canvasHeight,
+							success: (res) => {
+								console.log(res)
+								const imageData = res.data;
+								// 生成一维数组
+								const pixelArray = new Array(this.height * this.width).fill('')
+			
+								// 计算每个格子的像素大小
+								const cellWidth = this.canvasWidth / this.width;
+								const cellHeight = this.canvasHeight / this.height;
+			
+								// 生成一维数组
+								for (let y = 0; y < this.height; y++) {
+									for (let x = 0; x < this.width; x++) {
+										let blackPixels = 0;
+										let totalPixels = 0;
+			
+										// 对每个格子进行采样
+										for (let py = 0; py < cellHeight; py++) {
+											for (let px = 0; px < cellWidth; px++) {
+												const pixelY = Math.floor(y * cellHeight + py);
+												const pixelX = Math.floor(x * cellWidth + px);
+			
+												const idx = (pixelY * this.canvasWidth + pixelX) * 4;
+												const r = imageData[idx];
+												const g = imageData[idx + 1];
+												const b = imageData[idx + 2];
+												const a = imageData[idx + 3];
+			
+												// 计算亮度
+												const brightness = 0.3 * r + 0.59 * g + 0.11 * b;
+			
+												// 如果亮度小于阈值且透明度大于阈值,则认为是黑色
+												if (brightness < 128 && a > 128) {
+													blackPixels++;
+												}
+												totalPixels++;
+											}
+										}
+			
+										// 如果黑色像素占比超过阈值,则设置为当前选择的颜色
+										const isBlack = blackPixels / totalPixels > 0.3;
+										const arrayIndex = y * this.width + x;
+										pixelArray[arrayIndex] = isBlack ? this.color : ''; // 改为颜色值或空字符串
+									}
+								}
+			
+								resolve(pixelArray);
+							},
+							fail: (err) => {
+								reject(err);
+							}
+						});
+					});
+				});
+			},
+			
+			// 修改添加文字方法
+			async addText() {
+				uni.showModal({
+					title: '添加文字',
+					content: '',
+					editable: true,
+					placeholderText: '请输入文字',
+					success: async (res) => {
+						if (res.confirm) {
+							const text = res.content;
+							if (text.length > 0) {
+								try {
+									// 生成点阵数据
+									const dotMatrix = await this.generateDotMatrix(text);
+									console.log('点阵数据:', dotMatrix);
+									this.pixelData = dotMatrix
+								} catch (error) {
+									console.error('生成点阵失败:', error);
+									uni.showToast({
+										title: '生成点阵失败',
+										icon: 'none'
+									});
+								}
+							} else {
+								uni.showToast({
+									title: '请输入文字',
+									icon: 'none'
+								});
+							}
+						}
+					}
+				});
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	page {
+	  -webkit-text-size-adjust: 100%;
+	  text-size-adjust: 100%;
+	}
+	.item_pic_btn{
+		width: 200rpx;
+		height: 200rpx;
+		background: #eee;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		margin-bottom: 15rpx;
+	}
+	.color_list {
+		width: 100%;
+		display: flex;
+		justify-content: center;
+		flex-wrap: wrap;
+		gap: 30rpx;
+		box-sizing: border-box;
+		padding: 50rpx;
+	
+		.color_item {
+			width: 30rpx;
+			height: 30rpx;
+			border-radius: 5rpx;
+		}
+	}
+	
+	.controls {
+		position: absolute;
+		bottom: 10px;
+		left: 50px;
+		display: flex;
+		gap: 20px;
+		z-index: 9;
+	}
+	.animation_pixel{
+		width: 100vw;
+		height: 100vh;
+		position: relative;
+		.back_btn{
+			position: absolute;
+			left: 30px;
+			top: 20px;
+			z-index: 9999;
+		}
+		.canvas {
+			margin: 100rpx auto 0;
+			background: #ccc;
+			position: absolute;
+			z-index: 1;
+			left: -9999px;
+			top: -9999px;
+		}
+		.pixel_grid {
+			position: fixed;
+			left: 0;
+			top: var(--status-bar-height);
+			right: 0;
+			bottom: 0;
+			display: grid;
+			gap: 1px;
+			box-sizing: border-box;
+			background: #eee;
+			touch-action: none;
+			-webkit-touch-callout: none;
+			-webkit-user-select: none;
+			user-select: none;
+		
+			.pixel_cell {
+				display: flex;
+				align-items: center;
+				justify-content: center;
+				box-sizing: border-box;
+				background: #fff;
+		
+				.pixel_cell_item {
+					width: 50%;
+					height: 50%;
+					aspect-ratio: 1/1;
+					font-size: 10rpx;
+				}
+			}
+		}
+		
+	}
+	// -------------
+	.footer_none {
+		height: 200rpx;
+	}
+
+	.footer_btn {
+		position: fixed;
+		left: 0;
+		right: 0;
+		bottom: 0;
+		background: #fff;
+		padding: 30rpx 40rpx;
+	}
+	.footer_btn2 {
+		position: fixed;
+		left: 0;
+		right: 0;
+		bottom: 0;
+		background: #fff;
+		padding: 30rpx 40rpx;
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		box-shadow: 0 0 10rpx rgba(0, 0, 0, 0.1);
+	}
+
+	.temp_btn {
+		position: fixed;
+		left: 30rpx;
+		bottom: 60rpx;
+		right: 30rpx;
+		box-sizing: border-box;
+		height: 96rpx;
+		background: linear-gradient(142deg, #6FD2FF 0%, #268DFF 100%);
+		border-radius: 385rpx 385rpx 385rpx 385rpx;
+		font-size: 30rpx;
+		color: #FFFFFF;
+		line-height: 96rpx;
+		text-align: center;
+	}
+
+	.spec_radio {
+		margin-top: 30rpx;
+
+		&:first-child {
+			margin-top: 10rpx;
+		}
+	}
+
+	.temp {
+		padding: 0 30rpx;
+
+		.temp_item1 {
+			display: flex;
+			align-items: center;
+			justify-content: space-between;
+			background: linear-gradient(180deg, #FFFFFF 0%, rgba(255, 255, 255, 0.5) 100%);
+			box-shadow: 8rpx 8rpx 23rpx 0rpx rgba(67, 147, 248, 0.1), -8rpx -8rpx 23rpx 0rpx rgba(255, 255, 255, 0.4);
+			border-radius: 23rpx 23rpx 23rpx 23rpx;
+			border: 2rpx solid #FFFFFF;
+			padding: 30rpx;
+			box-sizing: border-box;
+			margin-top: 24rpx;
+
+			.title {
+				flex-shrink: 0;
+				font-size: 30rpx;
+				color: rgba(0, 0, 0, 0.9);
+				line-height: 40rpx;
+
+			}
+
+			.item {}
+		}
+
+		.temp_item {
+			display: flex;
+			align-items: flex-start;
+			justify-content: flex-start;
+			flex-direction: column;
+			background: linear-gradient(180deg, #FFFFFF 0%, rgba(255, 255, 255, 0.5) 100%);
+			box-shadow: 8rpx 8rpx 23rpx 0rpx rgba(67, 147, 248, 0.1), -8rpx -8rpx 23rpx 0rpx rgba(255, 255, 255, 0.4);
+			border-radius: 23rpx 23rpx 23rpx 23rpx;
+			border: 2rpx solid #FFFFFF;
+			padding: 30rpx;
+			box-sizing: border-box;
+			margin-top: 24rpx;
+
+			.title {
+				flex-shrink: 0;
+				font-size: 30rpx;
+				color: rgba(0, 0, 0, 0.9);
+				line-height: 40rpx;
+			}
+
+			.item {
+				margin-top: 20rpx;
+				width: 100%;
+				&.item_pic {
+					margin-top: 25rpx;
+				}
+			}
+		}
+	}
+
+	.page_bg {
+		width: 100%;
+		min-height: calc(100vh - var(--window-bottom));
+		background: url('/static/img/bg_2.jpg') no-repeat center top;
+		background-size: 100% auto;
+	}
+</style>
\ No newline at end of file
diff --git a/ledApp/pages/addTemplate/addTemplate copy.vue b/ledApp/pages/addTemplate/addTemplate copy.vue
new file mode 100644
index 0000000..5778ff6
--- /dev/null
+++ b/ledApp/pages/addTemplate/addTemplate copy.vue
@@ -0,0 +1,348 @@
+<template>
+	<view class="page_bg">
+		<uv-navbar title="新增模版" :autoBack="true" :placeholder="true" leftIconColor="#fff" bgColor="rgba(0,0,0,0)" titleStyle="color:#fff"></uv-navbar>
+		
+		<view class="temp">
+			<view class="temp_item">
+				<view class="title">
+					模版名称
+				</view>
+				<view class="item">
+					<uv-input placeholder="请输入名称" border="none" v-model="tempName" fontSize="27rpx" customStyle="background: #ECEFF6;padding:18rpx 20rpx;border-radius: 10rpx;"></uv-input>
+				</view>
+			</view>
+			<view class="temp_item">
+				<view class="title">
+					屏幕规格
+				</view>
+				<view class="item">
+					<uv-radio-group v-model="specVal" placement="column">
+						<uv-radio 
+							class="spec_radio"
+							v-for="(item, index) in specList" 
+							:key="index" 
+							:label="item.name" 
+							:name="item.name">
+						</uv-radio>
+					</uv-radio-group>
+				</view>
+			</view>
+			<view class="temp_item">
+				<view class="title">
+					动画方式
+				</view>
+				<view class="item">
+					<uv-radio-group v-model="modeVal" placement="column"  @change="changeMode">
+						<uv-radio 
+							class="spec_radio"
+							v-for="(item, index) in modeList" 
+							:key="index" 
+							:label="item.name" 
+							:name="item.val">
+						</uv-radio>
+					</uv-radio-group>
+				</view>
+			</view>
+
+			<view class="temp_item">
+				<view class="title">
+					动画帧数
+				</view>
+				<view class="item" @click="clickTotal">
+					<uv-input placeholder="请输入" readonly border="none" v-model="total" suffixIcon="arrow-right"
+						suffixIconStyle="color: #bbb" fontSize="26rpx" customStyle="background: #ECEFF6;padding:18rpx 20rpx;border-radius: 10rpx;"></uv-input>
+				</view>
+			</view>
+			<view class="temp_item" v-if="modeVal == 1">
+				<view class="title">
+					动画速度
+				</view>
+				<view class="item" @click="clickSpeed">
+					<uv-input placeholder="请输入" readonly border="none" v-model="speed" suffixIcon="arrow-right"
+						suffixIconStyle="color: #bbb" fontSize="26rpx" customStyle="background: #ECEFF6;padding:18rpx 20rpx;border-radius: 10rpx;"></uv-input>
+				</view>
+			</view>
+			<!-- <view class="temp_item" v-if="modeVal == 1">
+				<view class="title">
+					循环次数
+				</view>
+				<view class="item">
+					<uv-input placeholder="请输入" readonly border="none" v-model="cycle" suffixIcon="arrow-right"
+						suffixIconStyle="color: #bbb" fontSize="26rpx" customStyle="background: #ECEFF6;padding:18rpx 20rpx;border-radius: 10rpx;"></uv-input>
+				</view>
+			</view> -->
+			
+			<view class="temp_item1" v-for="(item,index) in total" :key="index">
+				<view class="title">
+					动画第{{item}}帧
+				</view>
+				<view class="item">
+					<uv-button text="编辑" @click="clickGoAnim(item)" shape="circle" size="small" color="linear-gradient( 142deg, #6FD2FF 0%, #268DFF 100%)"></uv-button>
+				</view>
+			</view>
+
+		</view>
+		
+		
+		<!-- <view class="temp_btn" @click="clickGoAnim">
+			编辑动画
+		</view> -->
+		
+		<view class="footer_none"></view>
+		<view class="footer_btn">
+			<uv-button text="保存" color="linear-gradient( 142deg, #6FD2FF 0%, #268DFF 100%)" shape="circle" @click="clickTempleteAdd()"></uv-button>
+		</view>
+		
+		<!-- 动画帧数 -->
+		<uv-picker ref="pickerTotal" :columns="columnsTotal" @confirm="confirmTotal"></uv-picker>
+		
+		<!-- 动画速度 -->
+		<uv-picker ref="pickerSpeed" :columns="columnsSpeed" @confirm="confirmSpeed"></uv-picker>
+		
+	</view>
+</template>
+
+<script>
+	import { templeteAdd } from '@/api/api.js'
+	export default {
+		data() {
+			return {
+				tempName: '',
+				specList: [{
+					name: '12*24'
+				}, {
+					name: '16*24'
+				}, {
+					name: '18*24'
+				}],
+				specVal: '12*24',
+				modeList: [
+					{
+						name: '单个动画帧',
+						val: 0
+					},
+					{
+						name: '动画帧切换',
+						val: 1
+					}
+				],
+				modeVal: 0,
+				columnsTotal: [
+					[2,3,4,5,6,7,8,9]
+				],
+				total: 1,
+				columnsSpeed: [
+					[10,20,30,40,50,60,70,80,90]
+				],
+				speed: 10,
+				cycle: 1,
+				fpsData: [],
+				fpsIndex: 1,
+				fpsIds: []
+			}
+		},
+		onLoad() {
+
+		},
+		onShow() {
+			console.log('------onShow')
+			const fpsParams = uni.getStorageSync('fpsParams');
+			// if (fpsParams) {
+			// 	console.log('收到回传参数:', fpsParams);
+			// 	this.fpsIndex = fpsParams.fpsIndex+1
+			// 	this.fpsData[fpsParams.fpsIndex] = fpsParams.data;
+			// 	this.fpsIds.push(fpsParams.fpsIndex)
+			// 	console.log(this.fpsIndex, this.fpsData);
+			// 	uni.removeStorageSync('fpsParams'); // 清理掉
+			// }
+			if (fpsParams) {
+			    console.log('收到回传参数:', fpsParams);
+			    // 用 splice 代替下标赋值
+				this.fpsIndex = fpsParams.fpsIndex+1
+			    this.fpsData.splice(fpsParams.fpsIndex, 1, fpsParams.data);
+				
+			    if (!this.fpsIds.includes(fpsParams.fpsIndex + 1)) {
+					this.fpsIds.push(fpsParams.fpsIndex + 1);
+			    }
+			    console.log(this.fpsIndex, this.fpsData);
+			    uni.removeStorageSync('fpsParams'); // 清理掉
+			}
+		},
+		methods: {
+			clickTempleteAdd(){
+				if(!this.tempName){
+					uni.showToast({
+						title: '请输入模版名称',
+						duration: 2000,
+						icon: 'none'
+					});
+					return
+				}
+				if(this.fpsIds.length == 0){
+					uni.showToast({
+						title: '请编辑动画帧',
+						duration: 2000,
+						icon: 'none'
+					});
+					return
+				}
+				let totalArr = new Array(this.total).fill(0).map((_, i) => i + 1);
+				let diffBA = totalArr.filter(v => !this.fpsIds.includes(v));
+				console.log(totalArr, diffBA)
+				if(diffBA.length != 0){
+					uni.showToast({
+						title: '请编辑第'+ Math.min(...diffBA) +'帧',
+						duration: 2000,
+						icon: 'none'
+					});
+					return
+				}
+				
+				templeteAdd({
+					name: this.tempName,
+					userId: uni.getStorageSync('userId'),
+					screen: this.specVal, //屏幕
+					fpsTotal: this.total, //总帧数
+					speed: this.speed, //速度
+					
+					fpsData: JSON.stringify(this.fpsData)
+				}).then(res=>{
+					console.log('添加动画模版',res)
+					uni.showToast({
+						title: res.msg,
+						duration: 2000,
+						icon: 'none'
+					});
+					if(res.code == 200){
+						uni.navigateBack({
+							delta: 1
+						});
+					}
+				})
+			},
+			clickSpeed(){
+				this.$refs.pickerSpeed.open();
+			},
+			confirmSpeed(e) {
+				console.log('confirmSpeed', e.value[0]);
+				this.speed = e.value[0]
+			},
+			clickTotal(){
+				if(this.total != 1){
+					this.$refs.pickerTotal.open();
+				}
+			},
+			confirmTotal(e) {
+				console.log('confirmTotal', e.value[0]);
+				this.total = e.value[0]
+			},
+			changeMode(e){
+				if(e == 0){
+					this.total = 1
+				}
+				else{
+					this.total = 2
+				}
+				this.fpsIndex = 1
+				this.fpsData = []
+			},
+			clickGoAnim(index){
+				console.log(this.fpsIndex, index)
+				// if(this.fpsIndex > index){
+				// 	uni.showToast({
+				// 		title: '请先编辑第'+this.fpsIndex+'帧',
+				// 		duration: 2000,
+				// 		icon: 'none'
+				// 	});
+				// 	return
+				// }
+				uni.navigateTo({
+					url: '/pages/animation/animation?fps='+index
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+.footer_none{
+	height: 200rpx;
+}
+.footer_btn{
+	position: fixed;
+	left: 0;
+	right: 0;
+	bottom: 0;
+	background: #fff;
+	padding:30rpx 40rpx;
+}
+.temp_btn{
+	position: fixed;
+	left: 30rpx;
+	bottom: 60rpx;
+	right: 30rpx;
+	box-sizing: border-box;
+	height: 96rpx;
+	background: linear-gradient( 142deg, #6FD2FF 0%, #268DFF 100%);
+	border-radius: 385rpx 385rpx 385rpx 385rpx;
+	font-size: 30rpx;
+	color: #FFFFFF;
+	line-height: 96rpx;
+	text-align: center;
+}
+.spec_radio{
+	margin-top: 30rpx;
+	&:first-child{
+		margin-top: 10rpx;
+	}
+}
+.temp{
+	padding:0 30rpx;
+	.temp_item1{
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		background: linear-gradient( 180deg, #FFFFFF 0%, rgba(255,255,255,0.5) 100%);
+		box-shadow: 8rpx 8rpx 23rpx 0rpx rgba(67,147,248,0.1), -8rpx -8rpx 23rpx 0rpx rgba(255,255,255,0.4);
+		border-radius: 23rpx 23rpx 23rpx 23rpx;
+		border: 2rpx solid #FFFFFF;
+		padding:30rpx;
+		box-sizing: border-box;
+		margin-top: 24rpx;
+		.title{
+			flex-shrink: 0;
+			font-size: 30rpx;
+			color: rgba(0,0,0,0.9);
+			line-height: 40rpx;
+			
+		}
+		.item{
+			
+		}
+	}
+	.temp_item{
+		background: linear-gradient( 180deg, #FFFFFF 0%, rgba(255,255,255,0.5) 100%);
+		box-shadow: 8rpx 8rpx 23rpx 0rpx rgba(67,147,248,0.1), -8rpx -8rpx 23rpx 0rpx rgba(255,255,255,0.4);
+		border-radius: 23rpx 23rpx 23rpx 23rpx;
+		border: 2rpx solid #FFFFFF;
+		padding:30rpx;
+		box-sizing: border-box;
+		margin-top: 24rpx;
+		.title{
+			flex-shrink: 0;
+			font-size: 30rpx;
+			color: rgba(0,0,0,0.9);
+			line-height: 40rpx;
+		}
+		.item{
+			margin-top: 15rpx;
+		}
+	}
+}
+.page_bg{
+	width: 100%;
+	min-height: calc(100vh - var(--window-bottom));
+	background:url('/static/img/bg_2.jpg') no-repeat center top;
+	background-size: 100% auto;
+}
+</style>
diff --git a/ledApp/pages/addTemplate/addTemplate.vue b/ledApp/pages/addTemplate/addTemplate.vue
new file mode 100644
index 0000000..0a208de
--- /dev/null
+++ b/ledApp/pages/addTemplate/addTemplate.vue
@@ -0,0 +1,1062 @@
+<template>
+	<view class="page_bg">
+		
+		<uv-navbar :title="titleName" :autoBack="true" :placeholder="true" leftIconColor="#fff" bgColor="rgba(0,0,0,0)" titleStyle="color:#fff"></uv-navbar>
+		
+		<view class="temp">
+			<view class="temp_item">
+				<view class="title">
+					模版名称
+				</view>
+				<view class="item">
+					<uv-input placeholder="请输入名称" border="none" v-model="tempName" fontSize="27rpx"
+						customStyle="background: #ECEFF6;padding:18rpx 20rpx;border-radius: 10rpx;"></uv-input>
+				</view>
+			</view>
+			<view class="temp_item">
+				<view class="title">
+					封面图片
+				</view>
+				<view class="item item_pic">
+					<uv-upload :fileList="fileList1" name="1" :maxCount="1" width="200rpx" height="200rpx"
+						:previewFullImage="false" @afterRead="afterRead" @delete="deletePic">
+						<!-- <image v-if="fileList1.length != 0"
+							:src="baseURL + fileList1[0]?.url" 
+							mode="aspectFill" 
+							style="width: 200rpx;height: 200rpx;"
+						></image>
+						<view class="item_pic_btn" v-else>
+							<uv-icon name="camera-fill" color="#ccc" size="60rpx"></uv-icon>
+						</view> -->
+					</uv-upload>
+				</view>
+			</view>
+			<view class="temp_item">
+				<view class="title">
+					屏幕规格
+				</view>
+				<view class="item">
+					<uv-radio-group v-model="specVal" placement="column" @change="changeSpec" labelSize="26rpx">
+						<uv-radio class="spec_radio" v-for="(item, index) in specList" :key="index" :label="item.name"
+							:name="item.name">
+						</uv-radio>
+					</uv-radio-group>
+				</view>
+			</view>
+			<view class="temp_item">
+				<view class="title">
+					动画方式
+				</view>
+				<view class="item">
+					<uv-radio-group v-model="modeVal" placement="column" @change="changeMode" labelSize="26rpx">
+						<uv-radio class="spec_radio" v-for="(item, index) in modeList" :key="index" :label="item.name"
+							:name="item.val">
+						</uv-radio>
+					</uv-radio-group>
+				</view>
+			</view>
+
+			<view class="temp_item">
+				<view class="title">
+					动画帧数
+				</view>
+				<view class="item" @click="clickTotal">
+					<uv-input placeholder="请输入" readonly border="none" v-model="total" suffixIcon="arrow-right"
+						suffixIconStyle="color: #bbb" fontSize="26rpx"
+						customStyle="background: #ECEFF6;padding:18rpx 20rpx;border-radius: 10rpx;"></uv-input>
+				</view>
+			</view>
+			<view class="temp_item">
+				<view class="title">
+					动画速度
+				</view>
+				<view class="item" @click="clickSpeed">
+					<uv-input placeholder="请输入" readonly border="none" v-model="speed" suffixIcon="arrow-right"
+						suffixIconStyle="color: #bbb" fontSize="26rpx"
+						customStyle="background: #ECEFF6;padding:18rpx 20rpx;border-radius: 10rpx;"></uv-input>
+				</view>
+			</view>
+			<!-- <view class="temp_item" v-if="modeVal == 1">
+				<view class="title">
+					循环次数
+				</view>
+				<view class="item">
+					<uv-input placeholder="请输入" readonly border="none" v-model="cycle" suffixIcon="arrow-right"
+						suffixIconStyle="color: #bbb" fontSize="26rpx" customStyle="background: #ECEFF6;padding:18rpx 20rpx;border-radius: 10rpx;"></uv-input>
+				</view>
+			</view> -->
+			<view class="temp_item1" v-for="(item,index) in total" :key="index">
+				<view class="title">
+					动画第{{item}}帧
+				</view>
+				<view class="item">
+					<uv-button text="编辑" @click="popupAnimationOpen(index)" shape="circle" size="small" color="linear-gradient( 142deg, #6FD2FF 0%, #268DFF 100%)"></uv-button>
+				</view>
+			</view>
+		</view>
+
+		<view class="footer_none"></view>
+		<view class="footer_btn">
+			<uv-button text="保存" color="linear-gradient( 142deg, #6FD2FF 0%, #268DFF 100%)" shape="circle" @click="clickTempleteAdd()"></uv-button>
+		</view>
+
+		<!-- 动画帧数 -->
+		<uv-picker ref="pickerTotal" :columns="columnsTotal" @confirm="confirmTotal"></uv-picker>
+
+		<!-- 动画速度 -->
+		<uv-picker ref="pickerSpeed" :columns="columnsSpeed" @confirm="confirmSpeed"></uv-picker>
+		
+		<!-- 动画弹出框 -->
+		<uv-popup ref="popupAnimation" mode="center" @change="changeAnimation" :safeAreaInsetTop="false" :safeAreaInsetBottom="false" :zoom="false" duration="0">
+			<view class="animation_pixel">
+				<view class="back_btn" @click="popupAnimationClose">
+					<uv-icon name="arrow-leftward" color="#333" size="30"></uv-icon>
+				</view>
+				<!-- 动画按钮 -->
+				<view class="controls">
+					<uv-button type="primary" :plain="true" text="分辨率" @click="changeRatio()"></uv-button>
+					<uv-button type="primary" :plain="true" text="颜色" @click="changeColor()"></uv-button>
+					<uv-button type="primary" :plain="true" text="添加文字" @click="addText()"></uv-button>
+					<uv-button type="primary" :plain="true" text="清除" @click="clear()"></uv-button>
+					<uv-button type="primary" :plain="true" text="保存" @click="clickSaveData"></uv-button>
+				</view>
+				
+				<!-- 文字生成图形 -->
+				<canvas canvas-id="myCanvas" id="myCanvas" class="canvas" :style="{ width: canvasWidth + 'px', height: canvasHeight + 'px' }"></canvas>
+
+				<view class="pixel_grid" @touchstart.prevent="handleTouchStart" @touchmove.prevent="handleTouchMove"
+					@touchend.prevent="handleTouchEnd" @touchcancel.prevent="handleTouchEnd" 
+					:style="{
+						gridTemplateRows: `repeat(${height}, 1fr)`,
+						gridTemplateColumns: `repeat(${width}, 1fr)`
+					}">
+					<view class="pixel_cell" v-for="(cell, cellIndex) in pixelData" :key="cellIndex">
+						<view class="pixel_cell_item" :style="{ backgroundColor: cell }"></view>
+					</view>
+				</view>
+			</view>
+		</uv-popup>
+		
+		<!-- 分辨率 -->
+		<uv-picker ref="pickerRatio" :columns="columnsRatio" @confirm="confirmRatio"></uv-picker>
+		
+		<!-- 颜色 -->
+		<uv-popup ref="pickerColor" mode="bottom" round="10" :closeable="true">
+			<view class="color_list">
+				<view class="color_item" v-for="(item, index) in colorList" :class="{ hover: color === item }" :key="index" :style="{ backgroundColor: item }" @click="clickColor(item)"></view>
+			</view>
+		</uv-popup>
+		
+	</view>
+</template>
+
+<script>
+	import {
+		templeteAdd,
+		templeteInfo,
+		templeteEdit
+	} from '@/api/api.js'
+	let ctx = null
+	export default {
+		data() {
+			return {
+				tempName: '',
+				pic: '',
+				specList: [{
+					name: '12*24'
+				}, {
+					name: '16*24'
+				}, {
+					name: '18*24'
+				}],
+				specVal: '12*24',
+				specWidth: 24,
+				specHeight: 12,
+				modeList: [{
+						name: '单个动画帧',
+						val: 0
+					},
+					{
+						name: '动画帧切换',
+						val: 1
+					}
+				],
+				modeVal: 0,
+				columnsTotal: [
+					[2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
+						28, 29, 30
+					]
+				],
+				total: 1, //动画总帧数
+				columnsSpeed: [
+					[10, 20, 30, 40, 50, 60, 70, 80, 90]
+				],
+				speed: 10,
+				cycle: 1,
+				fpsData: [],
+				fpsIndex: 1, //第几帧
+				fpsIds: [],
+
+				baseURL: '',
+				fileList1: [],
+				animationData: [],
+				fpsTotal: 1,
+				
+				
+				
+				// 模版动画
+				pixelData: [],
+				canvasData: [],
+				canvasWidth: 240,
+				canvasHeight: 120,
+				width: 24,
+				height: 12,
+				color: '#FF0000',
+				gridRect: null, // 存储像素网格的位置信息
+				lastCell: null, // 防止重复绘制
+				columnsRatio: [
+					['12*24', '16*32', '24*48', '32*64', '40*80']
+				],
+				// columnsRatio: [
+				// 	['12*24']
+				// ],
+				colorList: [
+					'#FF0000',
+					'#00FF00',
+					'#0000FF',
+					'#FFFF00',
+					'#FFA500',
+					'#800080',
+					'#00FFFF',
+					'#FFC0CB'
+				],
+				fpsIndex: 0,
+				fpsList: [],
+				tId: '',
+				tempInfo:{},
+				titleName: '新增模版'
+				
+			}
+		},
+		onLoad(opt) {
+			this.baseURL = uni.$uv.http.config.baseURL
+			if(opt.id){
+				this.tId = opt.id
+				this.getTempleteInfo()
+				this.titleName = '编辑模版'
+			}
+			else{
+				this.titleName = '新增模版'
+			}
+		},
+		onShow() {
+			
+		},
+		onHide() {
+			this.$refs.popupAnimation.close();
+			if (typeof plus !== 'undefined') {
+				plus.screen.lockOrientation('portrait')
+			}
+		},
+		onUnload() {
+			if (typeof plus !== 'undefined') {
+				plus.screen.lockOrientation('portrait')
+			}
+		},
+		methods: {
+			// 跳转到编辑动画页面
+			goToAnimaiton(index){
+				this.fpsIndex = index
+				uni.navigateTo({
+				  url: '/pages/animation/animation?index='+index
+				})
+			},
+			// 获取编辑详情
+			getTempleteInfo(){
+				templeteInfo(this.tId).then(res=>{
+					console.log('获取编辑详情-------',res)
+					if(res.code == 200){
+						this.tempInfo = res.data
+						this.tempName = this.tempInfo.name
+						this.fileList1.push({
+							url: this.baseURL + this.tempInfo.pic
+						})
+						
+						this.specVal = this.tempInfo.screen
+						this.modeVal = this.tempInfo.mode
+						
+						this.total = this.tempInfo.cycle
+						this.fpsTotal = this.tempInfo.fpsTotal
+						this.speed = this.tempInfo.speed
+						
+						this.fpsList = JSON.parse(this.tempInfo.fpsList)
+						
+					}
+					else{
+						uni.showToast({
+							title: res.msg,
+							duration: 2000,
+							icon: 'none'
+						});
+					}
+				})
+			},
+			// 添加模版提交接口
+			clickTempleteAdd() {
+				
+				if (!this.tempName) {
+					uni.showToast({
+						title: '请输入模版名称',
+						duration: 2000,
+						icon: 'none'
+					});
+					return
+				}
+				if (this.fileList1.length == 0) {
+					uni.showToast({
+						title: '请上传封面图片',
+						duration: 2000,
+						icon: 'none'
+					});
+					return
+				}
+				
+				if (this.fpsList.length == 0) {
+					uni.showToast({
+						title: '请编辑动画帧',
+						duration: 2000,
+						icon: 'none'
+					});
+					return
+				} else {
+					for (let i = 0; i < this.fpsList.length; i++) {
+					    if (this.fpsList[i] === null) {
+							uni.showToast({
+								title: `请编辑第 ${i+1} 个动画帧`,
+								duration: 2000,
+								icon: 'none'
+							});
+							return
+					    }
+					}
+				}
+				console.log('this.fpsList=========',this.fpsList)
+				
+				uni.showLoading({
+					title: '添加中',
+					mask: true
+				});
+				
+				
+				// 遍历二维数组
+				this.fpsData = this.fpsList.map(inner =>
+				  inner.map(item => {
+				    if (item === "") {
+				      return "000000"; // 空的改成000000
+				    } else if (item.includes("#")) {
+				      return item.replace("#", ""); // 去掉#
+				    } else {
+				      return item; // 保持原样
+				    }
+				  })
+				);
+				
+				console.log('this.fpsData=========',this.fpsData)
+				
+				// 动画方式
+				if (this.modeVal == 0) { //一帧动画
+					let frames = this.generateFrames(this.fpsData[0], 24);
+					const parsedData = frames.map(item => {
+						return this.toSnakeData(item, this.specWidth)
+					});
+					this.animationData = parsedData.map(item => {
+						return this.mergeRowsToString(item)
+					});
+					this.fpsTotal = 24
+				} else if (this.modeVal == 1) {
+					console.log('modeVal--------1')
+					const parsedData = this.fpsData.map(item => {
+						return this.toSnakeData(item, this.specWidth)
+					});
+					console.log('modeVal--------1',parsedData)
+					this.animationData = parsedData.map(item => {
+						return this.mergeRowsToString(item)
+					});
+					this.fpsTotal = this.total
+				}
+				console.log('animationData--------', this.animationData)
+				
+				// 判断this.fileList1[0].url里是否有 this.baseURL
+				if (this.fileList1[0].url.includes(this.baseURL)) {
+					this.fileList1[0].url = this.fileList1[0].url.replace(this.baseURL, "");
+				}
+				
+				console.log('url----',this.fileList1[0].url);
+				
+				// 判断如果有id就编辑
+				if(this.tId){
+					const putData = {
+						id: this.tId,
+						name: this.tempName,
+						userId: uni.getStorageSync('userId'),
+						screen: this.specVal, //屏幕
+						mode: this.modeVal,
+						cycle: this.total,
+						fpsTotal: this.fpsTotal, //总帧数
+						speed: this.speed, //速度
+						pic: this.fileList1[0].url,
+						fpsData: JSON.stringify(this.animationData),
+						fpsList: JSON.stringify(this.fpsList)
+					}
+					console.log('putData-----', putData)
+					templeteEdit(putData).then(res => {
+						console.log('编辑动画模版', res)
+						uni.hideLoading();
+						if (res.code == 200) {
+							uni.navigateBack({
+								delta: 1
+							});
+						} else {
+							uni.showToast({
+								title: res.msg,
+								duration: 1500,
+								icon: 'none',
+								mask: true
+							});
+						}
+					})
+				}
+				else{
+					const addData = {
+						name: this.tempName,
+						userId: uni.getStorageSync('userId'),
+						screen: this.specVal, //屏幕
+						mode: this.modeVal,
+						cycle: this.total,
+						fpsTotal: this.fpsTotal, //总帧数
+						speed: this.speed, //速度
+						pic: this.fileList1[0].url,
+						fpsData: JSON.stringify(this.animationData),
+						fpsList: JSON.stringify(this.fpsList)
+					}
+					console.log('addData-----', addData)
+					templeteAdd(addData).then(res => {
+						console.log('添加动画模版', res)
+						uni.hideLoading();
+						if (res.code == 200) {
+							uni.navigateBack({
+								delta: 1
+							});
+						} else {
+							uni.showToast({
+								title: res.msg,
+								duration: 1500,
+								icon: 'none',
+								mask: true
+							});
+						}
+					})
+				}
+				
+			},
+			// 保存动画
+			clickSaveData() {
+				this.fpsList[this.fpsIndex] = this.pixelData;
+				this.$refs.popupAnimation.close();
+			},
+			changeColor() {
+				this.$refs.pickerColor.open();
+			},
+			clickColor(item) {
+				console.log('confirmColor---', item);
+				this.color = item;
+				this.$refs.pickerColor.close();
+			},
+			changeRatio() {
+				this.$refs.pickerRatio.open();
+			},
+			// 分辨率
+			confirmRatio(e) {
+				console.log('confirmRatio---', e.value[0]);
+				const ratio = e.value[0].split('*');
+				console.log('ratio---', ratio[0]);
+				this.height = Number(ratio[0]);
+				this.width = Number(ratio[1]);
+				this.canvasWidth = this.width * 10;
+				this.canvasHeight = this.height * 10;
+				// 重新初始化一维数组
+				this.pixelData = new Array(this.height * this.width).fill('')
+				console.log('pixelData---', this.pixelData);
+			},
+			// 预先获取像素网格的位置信息
+			getGridRect(cb) {
+				if (this.gridRect) {
+					cb(this.gridRect)
+				} else {
+					uni.createSelectorQuery().in(this).select('.pixel_grid').boundingClientRect(rect => {
+						this.gridRect = rect
+						cb(rect)
+					}).exec()
+				}
+			},
+			// 计算触摸点对应的格子
+			getCellByTouch(touch, cb) {
+				this.getGridRect(rect => {
+					if (!rect) return
+					const x = touch.clientX - rect.left
+					const y = touch.clientY - rect.top
+					const cellWidth = rect.width / this.width
+					const cellHeight = rect.height / this.height
+					const colIndex = Math.floor(x / cellWidth)
+					const rowIndex = Math.floor(y / cellHeight)
+					if (
+						colIndex >= 0 && colIndex < this.width &&
+						rowIndex >= 0 && rowIndex < this.height
+					) {
+						cb({
+							rowIndex,
+							colIndex
+						})
+					}
+				})
+			},
+			handleTouchStart(e) {
+				// 阻止默认行为
+				e.preventDefault && e.preventDefault();
+			
+				if (!e.touches.length) return
+				this.getCellByTouch(e.touches[0], cell => {
+					this.lastCell = cell
+					this.handleClick(cell.rowIndex, cell.colIndex)
+				})
+			},
+			handleTouchMove(e) {
+				// 阻止默认行为
+				e.preventDefault && e.preventDefault();
+			
+				if (!e.touches.length) return
+				this.getCellByTouch(e.touches[0], cell => {
+					// 只在格子变化时才触发
+					if (!this.lastCell || this.lastCell.rowIndex !== cell.rowIndex || this.lastCell.colIndex !==
+						cell.colIndex) {
+						this.lastCell = cell
+						this.handleClick(cell.rowIndex, cell.colIndex)
+					}
+				})
+			},
+			handleTouchEnd() {
+				this.lastCell = null
+			},
+			handleClick(rowIndex, colIndex) {
+				console.log(rowIndex, colIndex)
+				// 计算一维数组的索引
+				const index = rowIndex * this.width + colIndex
+				// 如果当前像素有颜色,则清除;如果没有颜色,则添加当前选择的颜色
+				const currentColor = this.pixelData[index]
+				const newColor = currentColor ? '' : this.color
+				this.$set(this.pixelData, index, newColor)
+			},
+			clear() {
+				ctx && ctx.clearRect(0, 0, 1000, 1000)
+				// 将所有像素点设置为空字符串
+				this.pixelData = new Array(this.height * this.width).fill('')
+			},
+			// 模版动画弹出框
+			changeAnimation(e){
+				//console.log('打开关闭模版动画弹窗触发---',e)
+				// ios设备不支持弹出框横屏切换
+				if(e.show){
+					if (typeof plus !== 'undefined') {
+						plus.screen.lockOrientation('landscape-primary')
+					}
+				}
+				else{
+					if (typeof plus !== 'undefined') {
+						plus.screen.lockOrientation('portrait-primary')
+					}
+				}
+			},
+			// 打开模版动画
+			popupAnimationOpen(index){
+				this.$refs.popupAnimation.open();
+				this.fpsIndex = index
+				
+				if(this.fpsList[this.fpsIndex] == null){
+					console.log('第一次编辑-----null')
+					this.pixelData = new Array(this.height * this.width).fill('')
+				}else{
+					console.log('已编辑过-----')
+					this.pixelData = this.fpsList[this.fpsIndex]
+				}
+			},
+			// 关闭模版动画
+			popupAnimationClose(){
+				this.$refs.popupAnimation.close();
+				this.pixelData = new Array(this.height * this.width).fill('')
+			},
+			// 屏幕规格
+			changeSpec(e) {
+				const ratio = e.split('*');
+				this.specHeight = Number(ratio[0]);
+				this.specWidth = Number(ratio[1]);
+			},
+			// 偶数行数据反转
+			toSnakeData(pixelData, cols = 24) {
+				const rows = []
+				for (let i = 0; i < pixelData.length; i += cols) {
+					let row = pixelData.slice(i, i + cols)
+					if ((rows.length + 1) % 2 === 0) {
+						row.reverse() // 偶数行反转
+					}
+					rows.push(row)
+				}
+				return rows
+			},
+			// 二维数组转字符串
+			mergeRowsToString(rows) {
+				const rowStrings = rows.map(r => r.join('')) // 每行合并
+				return rowStrings.join('') // 所有行合并
+			},
+			// 生成一帧动画其他帧数据
+			generateFrames(data, frameCount = 24) {
+				const cols = this.specWidth; // 每行 24 个
+				const rows = this.specHeight; // 共 12 行
+				const frames = [];
+				// 拆分成 12 行
+				let grid = [];
+				for (let r = 0; r < rows; r++) {
+					grid.push(data.slice(r * cols, (r + 1) * cols));
+				}
+				// 生成帧
+				for (let f = 0; f < frameCount; f++) {
+					let frame = [];
+					// 每行左移 f 次
+					for (let r = 0; r < rows; r++) {
+						let row = grid[r].slice(); // 拷贝一行
+						let shifted = row.slice(f % cols).concat(row.slice(0, f % cols));
+						frame = frame.concat(shifted);
+					}
+					frames.push(frame);
+				}
+				return frames;
+			},
+			
+			clickSpeed() {
+				this.$refs.pickerSpeed.open();
+			},
+			confirmSpeed(e) {
+				console.log('confirmSpeed', e.value[0]);
+				this.speed = e.value[0]
+			},
+			clickTotal() {
+				if (this.total != 1) {
+					this.$refs.pickerTotal.open();
+				}
+			},
+			confirmTotal(e) {
+				console.log('confirmTotal', e.value[0]);
+				this.total = e.value[0]
+				this.adjustFpsData()
+			},
+			changeMode(e) {
+				this.fpsList = []
+				this.fpsData = []
+				if (e == 0) {
+					this.total = 1
+				} else {
+					this.total = 2
+				}
+				this.adjustFpsData()
+			},
+			adjustFpsData() {
+				// 如果 fpsData 太长 → 截取
+				if (this.fpsList.length > this.total) {
+					this.fpsList = this.fpsList.slice(0, this.total);
+				}
+				// 如果 fpsData 太短 → 补齐 null
+				else if (this.fpsList.length < this.total) {
+					const missing = this.total - this.fpsList.length;
+					this.fpsList = this.fpsList.concat(new Array(missing).fill(null));
+				}
+				
+				// 如果 fpsList 太长 → 截取
+				if (this.fpsData.length > this.total) {
+					this.fpsData = this.fpsData.slice(0, this.total);
+				}
+				// 如果 fpsList 太短 → 补齐 null
+				else if (this.fpsData.length < this.total) {
+					const missing = this.total - this.fpsData.length;
+					this.fpsData = this.fpsData.concat(new Array(missing).fill(null));
+				}
+			},
+			// 删除图片
+			deletePic(event) {
+				this[`fileList${event.name}`].splice(event.index, 1)
+			},
+			// 新增图片
+			async afterRead(event) {
+				console.log('event---', event)
+				// 当设置 multiple 为 true 时, file 为数组格式,否则为对象格式
+				let lists = [].concat(event.file)
+				let fileListLen = this[`fileList${event.name}`].length
+				lists.map((item) => {
+					this[`fileList${event.name}`].push({
+						...item,
+						status: 'uploading',
+						message: '上传中'
+					})
+				})
+				for (let i = 0; i < lists.length; i++) {
+					const result = await this.uploadFilePromise(lists[i].url)
+					this.avatar = result
+					let item = this[`fileList${event.name}`][fileListLen]
+					this[`fileList${event.name}`].splice(fileListLen, 1, Object.assign(item, {
+						status: 'success',
+						message: '',
+						url: result
+					}))
+					fileListLen++
+				}
+			},
+			uploadFilePromise(url) {
+				return new Promise((resolve, reject) => {
+					let a = uni.uploadFile({
+						url: this.baseURL + '/common/uploadFileByAnny', // 仅为示例,非真实的接口地址
+						filePath: url,
+						name: 'file',
+						success: (res) => {
+							console.log('图片-----', JSON.parse(res.data))
+							setTimeout(() => {
+								resolve(JSON.parse(res.data).fileName)
+							}, 1000)
+						}
+					});
+				})
+			},
+			// 生成点阵数据
+			generateDotMatrix(char) {
+				return new Promise((resolve, reject) => {
+			
+					// 使用 uni-app 的 canvas context
+					ctx = uni.createCanvasContext('myCanvas', this);
+			
+					// 设置字体样式
+					ctx.setFontSize(this.canvasHeight * 0.9);
+					ctx.setFillStyle('#000')
+					ctx.setTextAlign('center')
+					ctx.setTextBaseline('middle')
+					// 绘制文字
+					// 计算文字位置
+					// 水平居中
+					const x = this.canvasWidth / 2;
+					// 垂直居中,稍微向上偏移一点
+					const y = this.canvasHeight / 2;
+			
+					// 绘制文字
+					ctx.fillText(char, x, y);
+			
+					// 绘制到画布
+					ctx.draw(false, () => {
+						// 获取画布数据
+						uni.canvasGetImageData({
+							canvasId: 'myCanvas',
+							x: 0,
+							y: 0,
+							width: this.canvasWidth,
+							height: this.canvasHeight,
+							success: (res) => {
+								console.log(res)
+								const imageData = res.data;
+								// 生成一维数组
+								const pixelArray = new Array(this.height * this.width).fill('')
+			
+								// 计算每个格子的像素大小
+								const cellWidth = this.canvasWidth / this.width;
+								const cellHeight = this.canvasHeight / this.height;
+			
+								// 生成一维数组
+								for (let y = 0; y < this.height; y++) {
+									for (let x = 0; x < this.width; x++) {
+										let blackPixels = 0;
+										let totalPixels = 0;
+			
+										// 对每个格子进行采样
+										for (let py = 0; py < cellHeight; py++) {
+											for (let px = 0; px < cellWidth; px++) {
+												const pixelY = Math.floor(y * cellHeight + py);
+												const pixelX = Math.floor(x * cellWidth + px);
+			
+												const idx = (pixelY * this.canvasWidth + pixelX) * 4;
+												const r = imageData[idx];
+												const g = imageData[idx + 1];
+												const b = imageData[idx + 2];
+												const a = imageData[idx + 3];
+			
+												// 计算亮度
+												const brightness = 0.3 * r + 0.59 * g + 0.11 * b;
+			
+												// 如果亮度小于阈值且透明度大于阈值,则认为是黑色
+												if (brightness < 128 && a > 128) {
+													blackPixels++;
+												}
+												totalPixels++;
+											}
+										}
+			
+										// 如果黑色像素占比超过阈值,则设置为当前选择的颜色
+										const isBlack = blackPixels / totalPixels > 0.3;
+										const arrayIndex = y * this.width + x;
+										pixelArray[arrayIndex] = isBlack ? this.color : ''; // 改为颜色值或空字符串
+									}
+								}
+			
+								resolve(pixelArray);
+							},
+							fail: (err) => {
+								reject(err);
+							}
+						});
+					});
+				});
+			},
+			
+			// 修改添加文字方法
+			async addText() {
+				uni.showModal({
+					title: '添加文字',
+					content: '',
+					editable: true,
+					placeholderText: '请输入文字',
+					success: async (res) => {
+						if (res.confirm) {
+							const text = res.content;
+							if (text.length > 0) {
+								try {
+									// 生成点阵数据
+									const dotMatrix = await this.generateDotMatrix(text);
+									console.log('点阵数据:', dotMatrix);
+									this.pixelData = dotMatrix
+								} catch (error) {
+									console.error('生成点阵失败:', error);
+									uni.showToast({
+										title: '生成点阵失败',
+										icon: 'none'
+									});
+								}
+							} else {
+								uni.showToast({
+									title: '请输入文字',
+									icon: 'none'
+								});
+							}
+						}
+					}
+				});
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	page {
+	  -webkit-text-size-adjust: 100%;
+	  text-size-adjust: 100%;
+	}
+	.item_pic_btn{
+		width: 200rpx;
+		height: 200rpx;
+		background: #eee;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		margin-bottom: 15rpx;
+	}
+	.color_list {
+		width: 100%;
+		display: flex;
+		justify-content: center;
+		flex-wrap: wrap;
+		gap: 30rpx;
+		box-sizing: border-box;
+		padding: 50rpx;
+	
+		.color_item {
+			width: 30rpx;
+			height: 30rpx;
+			border-radius: 5rpx;
+		}
+	}
+	
+	.controls {
+		position: absolute;
+		bottom: 10px;
+		left: 50px;
+		display: flex;
+		gap: 20px;
+		z-index: 9;
+	}
+	.animation_pixel{
+		width: 100vw;
+		height: 100vh;
+		position: relative;
+		.back_btn{
+			position: absolute;
+			left: 30px;
+			top: 20px;
+			z-index: 9999;
+		}
+		.canvas {
+			margin: 100rpx auto 0;
+			background: #ccc;
+			position: absolute;
+			z-index: 1;
+			left: -9999px;
+			top: -9999px;
+		}
+		.pixel_grid {
+			position: fixed;
+			left: 0;
+			top: var(--status-bar-height);
+			right: 0;
+			bottom: 0;
+			display: grid;
+			gap: 1px;
+			box-sizing: border-box;
+			background: #eee;
+			touch-action: none;
+			-webkit-touch-callout: none;
+			-webkit-user-select: none;
+			user-select: none;
+		
+			.pixel_cell {
+				display: flex;
+				align-items: center;
+				justify-content: center;
+				box-sizing: border-box;
+				background: #fff;
+		
+				.pixel_cell_item {
+					width: 50%;
+					height: 50%;
+					aspect-ratio: 1/1;
+					font-size: 10rpx;
+				}
+			}
+		}
+		
+	}
+	// -------------
+	.footer_none {
+		height: 200rpx;
+	}
+
+	.footer_btn {
+		position: fixed;
+		left: 0;
+		right: 0;
+		bottom: 0;
+		background: #fff;
+		padding: 30rpx 40rpx;
+	}
+	.footer_btn2 {
+		position: fixed;
+		left: 0;
+		right: 0;
+		bottom: 0;
+		background: #fff;
+		padding: 30rpx 40rpx;
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		box-shadow: 0 0 10rpx rgba(0, 0, 0, 0.1);
+	}
+
+	.temp_btn {
+		position: fixed;
+		left: 30rpx;
+		bottom: 60rpx;
+		right: 30rpx;
+		box-sizing: border-box;
+		height: 96rpx;
+		background: linear-gradient(142deg, #6FD2FF 0%, #268DFF 100%);
+		border-radius: 385rpx 385rpx 385rpx 385rpx;
+		font-size: 30rpx;
+		color: #FFFFFF;
+		line-height: 96rpx;
+		text-align: center;
+	}
+
+	.spec_radio {
+		margin-top: 30rpx;
+
+		&:first-child {
+			margin-top: 10rpx;
+		}
+	}
+
+	.temp {
+		padding: 0 30rpx;
+
+		.temp_item1 {
+			display: flex;
+			align-items: center;
+			justify-content: space-between;
+			background: linear-gradient(180deg, #FFFFFF 0%, rgba(255, 255, 255, 0.5) 100%);
+			box-shadow: 8rpx 8rpx 23rpx 0rpx rgba(67, 147, 248, 0.1), -8rpx -8rpx 23rpx 0rpx rgba(255, 255, 255, 0.4);
+			border-radius: 23rpx 23rpx 23rpx 23rpx;
+			border: 2rpx solid #FFFFFF;
+			padding: 30rpx;
+			box-sizing: border-box;
+			margin-top: 24rpx;
+
+			.title {
+				flex-shrink: 0;
+				font-size: 30rpx;
+				color: rgba(0, 0, 0, 0.9);
+				line-height: 40rpx;
+
+			}
+
+			.item {}
+		}
+
+		.temp_item {
+			display: flex;
+			align-items: flex-start;
+			justify-content: flex-start;
+			flex-direction: column;
+			background: linear-gradient(180deg, #FFFFFF 0%, rgba(255, 255, 255, 0.5) 100%);
+			box-shadow: 8rpx 8rpx 23rpx 0rpx rgba(67, 147, 248, 0.1), -8rpx -8rpx 23rpx 0rpx rgba(255, 255, 255, 0.4);
+			border-radius: 23rpx 23rpx 23rpx 23rpx;
+			border: 2rpx solid #FFFFFF;
+			padding: 30rpx;
+			box-sizing: border-box;
+			margin-top: 24rpx;
+
+			.title {
+				flex-shrink: 0;
+				font-size: 30rpx;
+				color: rgba(0, 0, 0, 0.9);
+				line-height: 40rpx;
+			}
+
+			.item {
+				margin-top: 20rpx;
+				width: 100%;
+				font-size: 30rpx;
+				display: flex;
+				flex-direction: column;
+				&.item_pic {
+					margin-top: 25rpx;
+				}
+			}
+		}
+	}
+
+	.page_bg {
+		width: 100%;
+		min-height: calc(100vh - var(--window-bottom));
+		background: url('/static/img/bg_2.jpg') no-repeat center top;
+		background-size: 100% auto;
+	}
+</style>
\ No newline at end of file
diff --git a/ledApp/pages/animation/animation copy.vue b/ledApp/pages/animation/animation copy.vue
new file mode 100644
index 0000000..15ccbff
--- /dev/null
+++ b/ledApp/pages/animation/animation copy.vue
@@ -0,0 +1,533 @@
+<template>
+	<view>
+		<!-- <uv-navbar title="动画编辑" :autoBack="true" bgColor="rgba(0,0,0,0)" :placeholder="false"></uv-navbar> -->
+		<view class="back_btn" @click="clickBack">
+			<uv-icon name="arrow-leftward" color="#333" size="26rpx"></uv-icon>
+		</view>
+		<view class="controls">
+			<uv-button type="primary" :plain="true" text="分辨率" @click="changeRatio()"></uv-button>
+			<uv-button type="primary" :plain="true" text="颜色" @click="changeColor()"></uv-button>
+
+			<uv-button type="primary" :plain="true" text="添加文字" @click="addText()"></uv-button>
+			<uv-button type="primary" :plain="true" text="清除" @click="clear()"></uv-button>
+
+			<uv-button type="primary" :plain="true" text="保存" @click="clickSaveData"></uv-button>
+		</view>
+
+		<!-- 文字生成图形 -->
+		<canvas canvas-id="myCanvas" id="myCanvas" class="canvas" :style="{ width: canvasWidth + 'px', height: canvasHeight + 'px' }"></canvas>
+
+		<!-- 修改像素网格显示 -->
+		<!-- <view class="pixel-grid" @touchstart.prevent="handleTouchStart" @touchmove.prevent="handleTouchMove">
+			<view class="pixel-row" v-for="(row, rowIndex) in Math.ceil(pixelData.length / width)" :key="rowIndex">
+				<view class="pixel-cell" v-for="(item, colIndex) in width" :key="colIndex">
+					<view v-if="pixelData[rowIndex * width + colIndex]" class="pixel-cell-item" :style="{ backgroundColor: pixelData[rowIndex * width + colIndex] }">
+						{{rowIndex,colIndex}}
+					</view>
+				</view>
+			</view>
+		</view> -->
+
+		<view class="pixel_grid" @touchstart.prevent="handleTouchStart" @touchmove.prevent="handleTouchMove"
+			@touchend.prevent="handleTouchEnd" @touchcancel.prevent="handleTouchEnd" 
+			:style="{
+				gridTemplateRows: `repeat(${height}, 1fr)`,
+				gridTemplateColumns: `repeat(${width}, 1fr)`
+			}">
+			<view class="pixel_cell" v-for="(cell, cellIndex) in pixelData" :key="cellIndex">
+				<view class="pixel_cell_item" :style="{ backgroundColor: cell }">
+
+				</view>
+			</view>
+		</view>
+
+		<!-- 分辨率 -->
+		<uv-picker ref="pickerRatio" :columns="columnsRatio" @confirm="confirmRatio"></uv-picker>
+
+		<!-- 颜色 -->
+		<uv-popup ref="pickerColor" mode="bottom" round="10" :closeable="true">
+			<view class="color_list">
+				<view class="color_item" v-for="(item, index) in colorList" :class="{ hover: color === item }"
+					:key="index" :style="{ backgroundColor: item }" @click="clickColor(item)"></view>
+			</view>
+		</uv-popup>
+
+		<!-- <uv-pick-color ref="pickerColor" @confirm="confirm"></uv-pick-color> -->
+
+	</view>
+</template>
+
+<script>
+	let ctx = null
+
+	export default {
+		data() {
+			return {
+				pixelData: [],
+				canvasData: [],
+				canvasWidth: 240,
+				canvasHeight: 120,
+				width: 24,
+				height: 12,
+				color: '#FF0000',
+				gridRect: null, // 存储像素网格的位置信息
+				lastCell: null, // 防止重复绘制
+				columnsRatio: [
+					['12*24', '16*32', '24*48', '32*64', '40*80']
+				],
+				// columnsRatio: [
+				// 	['12*24']
+				// ],
+				colorList: [
+					'#FF0000',
+					'#00FF00',
+					'#0000FF',
+					'#FFFF00',
+					'#FFA500',
+					'#800080',
+					'#00FFFF',
+					'#FFC0CB'
+				],
+				fpsIndex: 0
+			}
+		},
+		onLoad(opt) {
+			// App端强制横屏
+			if (typeof plus !== 'undefined') {
+				plus.screen.lockOrientation('landscape-primary')
+			}
+			if (opt.fps) {
+				this.fpsIndex = opt.fps
+			}
+
+		},
+		onShow() {
+			// 改为一维数组初始化
+			this.pixelData = new Array(this.height * this.width).fill('')
+		},
+		onHide() {
+			console.log('离开页面恢复竖屏------onHide')
+			// 离开页面恢复竖屏
+			if (typeof plus !== 'undefined') {
+				plus.screen.lockOrientation('portrait')
+			}
+		},
+		onUnload() {
+			console.log('离开页面恢复竖屏------onUnload')
+			// 离开页面恢复竖屏
+			if (typeof plus !== 'undefined') {
+				plus.screen.lockOrientation('portrait')
+			}
+		},
+		methods: {
+			clickBack() {
+				uni.navigateBack({
+					delta: 1
+				});
+			},
+			clickSaveData() {
+				const parsed = this.pixelData.map(item => {
+					if (item) {
+						return item.replace("#", "");
+					} else {
+						return "000000";
+					}
+				});
+				console.log('parsed-------------', parsed.join(''))
+				//const snakeRows = this.toSnakeData(parsed, this.width)
+				//console.log('snakeRows-------------', snakeRows)
+				//const snakeString = this.mergeRowsToString(snakeRows)
+				//console.log('snakeString-------------', snakeString)
+
+				// uni.setStorageSync('fpsParams', {
+				// 	shouldRefresh: true,
+				// 	fpsIndex: Number(this.fpsIndex),
+				// 	//data: snakeString
+				// 	data: parsed
+				// });
+
+				uni.setStorage({
+					key: 'fpsParams',
+					data: {
+						shouldRefresh: true,
+						fpsIndex: Number(this.fpsIndex),
+						data: parsed
+					},
+					success: () => {
+						console.log('存储成功');
+						uni.navigateBack({
+							delta: 1,
+						});
+					},
+					fail: (err) => {
+						console.error('存储失败:', err);
+					}
+				});
+
+				
+			},
+			// 偶数行数据反转
+			toSnakeData(pixelData, cols = 24) {
+				const rows = []
+				for (let i = 0; i < pixelData.length; i += cols) {
+					let row = pixelData.slice(i, i + cols)
+					if ((rows.length + 1) % 2 === 0) {
+						row.reverse() // 偶数行反转
+					}
+					rows.push(row)
+				}
+				return rows
+			},
+			// 二维数组转字符串
+			mergeRowsToString(rows) {
+				const rowStrings = rows.map(r => r.join('')) // 每行合并
+				return rowStrings.join('') // 所有行合并
+			},
+			getData() {
+				uni.switchTab({
+					url: '/pages/template/template',
+					success() {
+						console.log('跳转成功');
+					},
+					fail(err) {
+						console.error('跳转失败:', err);
+					}
+				});
+			},
+			changeColor() {
+				this.$refs.pickerColor.open();
+			},
+			clickColor(item) {
+				console.log('confirmColor---', item);
+				this.color = item;
+				this.$refs.pickerColor.close();
+			},
+			changeRatio() {
+				this.$refs.pickerRatio.open();
+			},
+			// 分辨率
+			confirmRatio(e) {
+				console.log('confirmRatio---', e.value[0]);
+				const ratio = e.value[0].split('*');
+				console.log('ratio---', ratio[0]);
+				this.height = Number(ratio[0]);
+				this.width = Number(ratio[1]);
+				this.canvasWidth = this.width * 10;
+				this.canvasHeight = this.height * 10;
+				// 重新初始化一维数组
+				this.pixelData = new Array(this.height * this.width).fill('')
+				console.log('pixelData---', this.pixelData);
+			},
+			// 预先获取像素网格的位置信息
+			getGridRect(cb) {
+				if (this.gridRect) {
+					cb(this.gridRect)
+				} else {
+					uni.createSelectorQuery().in(this).select('.pixel_grid').boundingClientRect(rect => {
+						this.gridRect = rect
+						cb(rect)
+					}).exec()
+				}
+			},
+			// 计算触摸点对应的格子
+			getCellByTouch(touch, cb) {
+				this.getGridRect(rect => {
+					if (!rect) return
+					const x = touch.clientX - rect.left
+					const y = touch.clientY - rect.top
+					const cellWidth = rect.width / this.width
+					const cellHeight = rect.height / this.height
+					const colIndex = Math.floor(x / cellWidth)
+					const rowIndex = Math.floor(y / cellHeight)
+					if (
+						colIndex >= 0 && colIndex < this.width &&
+						rowIndex >= 0 && rowIndex < this.height
+					) {
+						cb({
+							rowIndex,
+							colIndex
+						})
+					}
+				})
+			},
+			handleTouchStart(e) {
+				// 阻止默认行为
+				e.preventDefault && e.preventDefault();
+
+				if (!e.touches.length) return
+				this.getCellByTouch(e.touches[0], cell => {
+					this.lastCell = cell
+					this.handleClick(cell.rowIndex, cell.colIndex)
+				})
+			},
+			handleTouchMove(e) {
+				// 阻止默认行为
+				e.preventDefault && e.preventDefault();
+
+				if (!e.touches.length) return
+				this.getCellByTouch(e.touches[0], cell => {
+					// 只在格子变化时才触发
+					if (!this.lastCell || this.lastCell.rowIndex !== cell.rowIndex || this.lastCell.colIndex !==
+						cell.colIndex) {
+						this.lastCell = cell
+						this.handleClick(cell.rowIndex, cell.colIndex)
+					}
+				})
+			},
+			handleTouchEnd() {
+				this.lastCell = null
+			},
+			handleClick(rowIndex, colIndex) {
+				console.log(rowIndex, colIndex)
+				// 计算一维数组的索引
+				const index = rowIndex * this.width + colIndex
+				// 如果当前像素有颜色,则清除;如果没有颜色,则添加当前选择的颜色
+				const currentColor = this.pixelData[index]
+				const newColor = currentColor ? '' : this.color
+				this.$set(this.pixelData, index, newColor)
+			},
+			clear() {
+				ctx && ctx.clearRect(0, 0, 1000, 1000)
+				// 将所有像素点设置为空字符串
+				this.pixelData = new Array(this.height * this.width).fill('')
+			},
+			// 生成点阵数据
+			generateDotMatrix(char) {
+				return new Promise((resolve, reject) => {
+
+					// 使用 uni-app 的 canvas context
+					ctx = uni.createCanvasContext('myCanvas', this);
+
+					// 设置字体样式
+					ctx.setFontSize(this.canvasHeight * 0.9);
+					ctx.setFillStyle('#000')
+					ctx.setTextAlign('center')
+					ctx.setTextBaseline('middle')
+					// 绘制文字
+					// 计算文字位置
+					// 水平居中
+					const x = this.canvasWidth / 2;
+					// 垂直居中,稍微向上偏移一点
+					const y = this.canvasHeight / 2;
+
+					// 绘制文字
+					ctx.fillText(char, x, y);
+
+					// 绘制到画布
+					ctx.draw(false, () => {
+						// 获取画布数据
+						uni.canvasGetImageData({
+							canvasId: 'myCanvas',
+							x: 0,
+							y: 0,
+							width: this.canvasWidth,
+							height: this.canvasHeight,
+							success: (res) => {
+								console.log(res)
+								const imageData = res.data;
+								// 生成一维数组
+								const pixelArray = new Array(this.height * this.width).fill('')
+
+								// 计算每个格子的像素大小
+								const cellWidth = this.canvasWidth / this.width;
+								const cellHeight = this.canvasHeight / this.height;
+
+								// 生成一维数组
+								for (let y = 0; y < this.height; y++) {
+									for (let x = 0; x < this.width; x++) {
+										let blackPixels = 0;
+										let totalPixels = 0;
+
+										// 对每个格子进行采样
+										for (let py = 0; py < cellHeight; py++) {
+											for (let px = 0; px < cellWidth; px++) {
+												const pixelY = Math.floor(y * cellHeight + py);
+												const pixelX = Math.floor(x * cellWidth + px);
+
+												const idx = (pixelY * this.canvasWidth +
+													pixelX) * 4;
+												const r = imageData[idx];
+												const g = imageData[idx + 1];
+												const b = imageData[idx + 2];
+												const a = imageData[idx + 3];
+
+												// 计算亮度
+												const brightness = 0.3 * r + 0.59 * g + 0.11 *
+													b;
+
+												// 如果亮度小于阈值且透明度大于阈值,则认为是黑色
+												if (brightness < 128 && a > 128) {
+													blackPixels++;
+												}
+												totalPixels++;
+											}
+										}
+
+										// 如果黑色像素占比超过阈值,则设置为当前选择的颜色
+										const isBlack = blackPixels / totalPixels > 0.3;
+										const arrayIndex = y * this.width + x;
+										pixelArray[arrayIndex] = isBlack ? this.color :
+											''; // 改为颜色值或空字符串
+									}
+								}
+
+								resolve(pixelArray);
+							},
+							fail: (err) => {
+								reject(err);
+							}
+						});
+					});
+				});
+			},
+
+			// 修改添加文字方法
+			async addText() {
+				uni.showModal({
+					title: '添加文字',
+					content: '',
+					editable: true,
+					placeholderText: '请输入文字',
+					success: async (res) => {
+						if (res.confirm) {
+							const text = res.content;
+							if (text.length > 0) {
+								try {
+									// 生成点阵数据
+									const dotMatrix = await this.generateDotMatrix(text);
+									console.log('点阵数据:', dotMatrix);
+									this.pixelData = dotMatrix
+								} catch (error) {
+									console.error('生成点阵失败:', error);
+									uni.showToast({
+										title: '生成点阵失败',
+										icon: 'none'
+									});
+								}
+							} else {
+								uni.showToast({
+									title: '请输入文字',
+									icon: 'none'
+								});
+							}
+						}
+					}
+				});
+			},
+			
+			
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.back_btn {
+		position: fixed;
+		left: 10rpx;
+		top: 26rpx;
+		z-index: 9999;
+	}
+
+	.pixel_grid {
+		position: fixed;
+		left: 0;
+		top: var(--status-bar-height);
+		right: 0;
+		bottom: 0;
+		display: grid;
+		gap: 1px;
+		box-sizing: border-box;
+		background: #eee;
+		touch-action: none;
+		-webkit-touch-callout: none;
+		-webkit-user-select: none;
+		user-select: none;
+
+		.pixel_cell {
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			box-sizing: border-box;
+			background: #fff;
+
+			.pixel_cell_item {
+				width: 50%;
+				height: 50%;
+				aspect-ratio: 1/1;
+				font-size: 10rpx;
+			}
+		}
+	}
+
+	.color_list {
+		width: 100%;
+		display: flex;
+		justify-content: center;
+		flex-wrap: wrap;
+		gap: 30rpx;
+		box-sizing: border-box;
+		padding: 50rpx;
+
+		.color_item {
+			width: 30rpx;
+			height: 30rpx;
+			border-radius: 5rpx;
+		}
+	}
+
+	.controls {
+		position: absolute;
+		bottom: 15rpx;
+		left: 20rpx;
+		display: flex;
+		gap: 20rpx;
+		z-index: 9;
+	}
+
+	.canvas {
+		margin: 100rpx auto 0;
+		background: #ccc;
+		position: absolute;
+		z-index: 1;
+		left: -9999px;
+		top: -9999px;
+	}
+
+	.pixel-grid {
+		position: fixed;
+		left: 0;
+		top: 0;
+		right: 0;
+		bottom: 0;
+		display: flex;
+		flex-direction: column;
+		z-index: 8;
+		background: #fff;
+		touch-action: none;
+		-webkit-touch-callout: none;
+		-webkit-user-select: none;
+		user-select: none;
+
+		.pixel-row {
+			display: flex;
+			flex: 1;
+
+			.pixel-cell {
+				flex: 1;
+				display: flex;
+				justify-content: center;
+				align-items: center;
+				border: 1rpx solid #eee;
+				box-sizing: border-box;
+
+				.pixel-cell-item {
+					width: 50%;
+					aspect-ratio: 1/1;
+					color: #333 !important;
+					;
+					font-size: 10rpx;
+				}
+			}
+		}
+	}
+</style>
\ No newline at end of file
diff --git a/ledApp/pages/animation/animation.vue b/ledApp/pages/animation/animation.vue
new file mode 100644
index 0000000..280e105
--- /dev/null
+++ b/ledApp/pages/animation/animation.vue
@@ -0,0 +1,624 @@
+<template>
+	<view>
+		<view class="animation_pixel">
+			<view class="back_btn" @click="popupAnimationClose">
+				<uv-icon name="arrow-leftward" color="#333" size="30"></uv-icon>
+			</view>
+			<!-- 动画按钮 -->
+			<view class="controls">
+				<uv-button type="primary" :plain="true" text="分辨率" @click="changeRatio()"></uv-button>
+				<uv-button type="primary" :plain="true" text="颜色" @click="changeColor()"></uv-button>
+				<uv-button type="primary" :plain="true" text="添加文字" @click="addText()"></uv-button>
+				<uv-button type="primary" :plain="true" text="清除" @click="clear()"></uv-button>
+				<uv-button type="primary" :plain="true" text="保存" @click="clickSaveData"></uv-button>
+			</view>
+
+			<!-- 文字生成图形 -->
+			<canvas canvas-id="myCanvas" id="myCanvas" class="canvas"
+				:style="{ width: canvasWidth + 'px', height: canvasHeight + 'px' }"></canvas>
+
+			<view class="pixel_grid" @touchstart.prevent="handleTouchStart" @touchmove.prevent="handleTouchMove"
+				@touchend.prevent="handleTouchEnd" @touchcancel.prevent="handleTouchEnd" :style="{
+					gridTemplateRows: `repeat(${height}, 1fr)`,
+					gridTemplateColumns: `repeat(${width}, 1fr)`
+				}">
+				<view class="pixel_cell" v-for="(cell, cellIndex) in pixelData" :key="cellIndex">
+					<view class="pixel_cell_item" :style="{ backgroundColor: cell }"></view>
+				</view>
+			</view>
+		</view>
+
+		<!-- 分辨率 -->
+		<uv-picker ref="pickerRatio" :columns="columnsRatio" @confirm="confirmRatio"></uv-picker>
+
+		<!-- 颜色 -->
+		<uv-popup ref="pickerColor" mode="bottom" round="10" :closeable="true">
+			<view class="color_list">
+				<view class="color_item" v-for="(item, index) in colorList" :class="{ hover: color === item }"
+					:key="index" :style="{ backgroundColor: item }" @click="clickColor(item)"></view>
+			</view>
+		</uv-popup>
+
+	</view>
+</template>
+
+<script>
+	let ctx = null
+	export default {
+		data() {
+			return {
+				tempName: '',
+				pic: '',
+				specList: [{
+					name: '12*24'
+				}, {
+					name: '16*24'
+				}, {
+					name: '18*24'
+				}],
+				specVal: '12*24',
+				specWidth: 24,
+				specHeight: 12,
+				modeList: [{
+						name: '单个动画帧',
+						val: 0
+					},
+					{
+						name: '动画帧切换',
+						val: 1
+					}
+				],
+				modeVal: 0,
+				columnsTotal: [
+					[2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
+						28, 29, 30
+					]
+				],
+				total: 1, //动画总帧数
+				columnsSpeed: [
+					[10, 20, 30, 40, 50, 60, 70, 80, 90]
+				],
+				speed: 10,
+				cycle: 1,
+				fpsData: [],
+				fpsIndex: 1, //第几帧
+				fpsIds: [],
+
+				baseURL: '',
+				fileList1: [],
+				animationData: [],
+				fpsTotal: 1,
+
+
+
+				// 模版动画
+				pixelData: [],
+				canvasData: [],
+				canvasWidth: 240,
+				canvasHeight: 120,
+				width: 24,
+				height: 12,
+				color: '#FF0000',
+				gridRect: null, // 存储像素网格的位置信息
+				lastCell: null, // 防止重复绘制
+				columnsRatio: [
+					['12*24', '16*32', '24*48', '32*64', '40*80']
+				],
+				// columnsRatio: [
+				// 	['12*24']
+				// ],
+				colorList: [
+					'#FF0000',
+					'#00FF00',
+					'#0000FF',
+					'#FFFF00',
+					'#FFA500',
+					'#800080',
+					'#00FFFF',
+					'#FFC0CB'
+				],
+				fpsIndex: 0,
+				fpsList: [],
+				tId: '',
+				tempInfo: {},
+				titleName: '新增模版',
+				
+
+			}
+		},
+		onLoad(opt) {
+			this.baseURL = uni.$uv.http.config.baseURL
+			if (opt.index) {
+				this.fpsIndex = opt.index
+				console.log('fpsIndex--', this.fpsIndex)
+			}
+		},
+		onShow() {
+			this.pixelData = new Array(this.height * this.width).fill('')
+		},
+		onHide() {
+			
+		},
+		onUnload() {
+			
+		},
+		methods: {
+			// 保存动画
+			clickSaveData() {
+				this.fpsList[this.fpsIndex] = this.pixelData;
+				this.$refs.popupAnimation.close();
+			},
+			changeColor() {
+				this.$refs.pickerColor.open();
+			},
+			clickColor(item) {
+				console.log('confirmColor---', item);
+				this.color = item;
+				this.$refs.pickerColor.close();
+			},
+			changeRatio() {
+				this.$refs.pickerRatio.open();
+			},
+			// 分辨率
+			confirmRatio(e) {
+				console.log('confirmRatio---', e.value[0]);
+				const ratio = e.value[0].split('*');
+				console.log('ratio---', ratio[0]);
+				this.height = Number(ratio[0]);
+				this.width = Number(ratio[1]);
+				this.canvasWidth = this.width * 10;
+				this.canvasHeight = this.height * 10;
+				// 重新初始化一维数组
+				this.pixelData = new Array(this.height * this.width).fill('')
+				console.log('pixelData---', this.pixelData);
+			},
+			// 预先获取像素网格的位置信息
+			getGridRect(cb) {
+				if (this.gridRect) {
+					cb(this.gridRect)
+				} else {
+					uni.createSelectorQuery().in(this).select('.pixel_grid').boundingClientRect(rect => {
+						this.gridRect = rect
+						cb(rect)
+					}).exec()
+				}
+			},
+			// 计算触摸点对应的格子
+			getCellByTouch(touch, cb) {
+				this.getGridRect(rect => {
+					if (!rect) return
+					const x = touch.clientX - rect.left
+					const y = touch.clientY - rect.top
+					const cellWidth = rect.width / this.width
+					const cellHeight = rect.height / this.height
+					const colIndex = Math.floor(x / cellWidth)
+					const rowIndex = Math.floor(y / cellHeight)
+					if (
+						colIndex >= 0 && colIndex < this.width &&
+						rowIndex >= 0 && rowIndex < this.height
+					) {
+						cb({
+							rowIndex,
+							colIndex
+						})
+					}
+				})
+			},
+			handleTouchStart(e) {
+				// 阻止默认行为
+				e.preventDefault && e.preventDefault();
+
+				if (!e.touches.length) return
+				this.getCellByTouch(e.touches[0], cell => {
+					this.lastCell = cell
+					this.handleClick(cell.rowIndex, cell.colIndex)
+				})
+			},
+			handleTouchMove(e) {
+				// 阻止默认行为
+				e.preventDefault && e.preventDefault();
+
+				if (!e.touches.length) return
+				this.getCellByTouch(e.touches[0], cell => {
+					// 只在格子变化时才触发
+					if (!this.lastCell || this.lastCell.rowIndex !== cell.rowIndex || this.lastCell.colIndex !==
+						cell.colIndex) {
+						this.lastCell = cell
+						this.handleClick(cell.rowIndex, cell.colIndex)
+					}
+				})
+			},
+			handleTouchEnd() {
+				this.lastCell = null
+			},
+			handleClick(rowIndex, colIndex) {
+				console.log(rowIndex, colIndex)
+				// 计算一维数组的索引
+				const index = rowIndex * this.width + colIndex
+				// 如果当前像素有颜色,则清除;如果没有颜色,则添加当前选择的颜色
+				const currentColor = this.pixelData[index]
+				const newColor = currentColor ? '' : this.color
+				this.$set(this.pixelData, index, newColor)
+			},
+			clear() {
+				ctx && ctx.clearRect(0, 0, 1000, 1000)
+				// 将所有像素点设置为空字符串
+				this.pixelData = new Array(this.height * this.width).fill('')
+			},
+			// 模版动画弹出框
+			changeAnimation(e) {
+				//console.log('打开关闭模版动画弹窗触发---',e)
+				// ios设备不支持弹出框横屏切换
+				// if(e.show){
+				// 	if (typeof plus !== 'undefined') {
+				// 		plus.screen.lockOrientation('landscape-primary')
+				// 	}
+				// }
+				// else{
+				// 	if (typeof plus !== 'undefined') {
+				// 		plus.screen.lockOrientation('portrait')
+				// 	}
+				// }
+			},
+			// 打开模版动画
+			popupAnimationOpen(index) {
+				this.fpsIndex = index
+				if (this.fpsList[this.fpsIndex] == null) {
+					console.log('第一次编辑-----null')
+					this.pixelData = new Array(this.height * this.width).fill('')
+				} else {
+					console.log('已编辑过-----')
+					this.pixelData = this.fpsList[this.fpsIndex]
+				}
+			},
+			// 关闭模版动画
+			popupAnimationClose() {
+				this.$refs.popupAnimation.close();
+				this.pixelData = new Array(this.height * this.width).fill('')
+			},
+			// 屏幕规格
+			changeSpec(e) {
+				const ratio = e.split('*');
+				this.specHeight = Number(ratio[0]);
+				this.specWidth = Number(ratio[1]);
+			},
+			// 偶数行数据反转
+			toSnakeData(pixelData, cols = 24) {
+				const rows = []
+				for (let i = 0; i < pixelData.length; i += cols) {
+					let row = pixelData.slice(i, i + cols)
+					if ((rows.length + 1) % 2 === 0) {
+						row.reverse() // 偶数行反转
+					}
+					rows.push(row)
+				}
+				return rows
+			},
+			// 二维数组转字符串
+			mergeRowsToString(rows) {
+				const rowStrings = rows.map(r => r.join('')) // 每行合并
+				return rowStrings.join('') // 所有行合并
+			},
+			// 生成一帧动画其他帧数据
+			generateFrames(data, frameCount = 12) {
+				const cols = this.specWidth; // 每行 24 个
+				const rows = this.specHeight; // 共 12 行
+				const frames = [];
+				// 拆分成 12 行
+				let grid = [];
+				for (let r = 0; r < rows; r++) {
+					grid.push(data.slice(r * cols, (r + 1) * cols));
+				}
+				// 生成帧
+				for (let f = 0; f < frameCount; f++) {
+					let frame = [];
+					// 每行左移 f 次
+					for (let r = 0; r < rows; r++) {
+						let row = grid[r].slice(); // 拷贝一行
+						let shifted = row.slice(f % cols).concat(row.slice(0, f % cols));
+						frame = frame.concat(shifted);
+					}
+					frames.push(frame);
+				}
+				return frames;
+			},
+
+			clickSpeed() {
+				this.$refs.pickerSpeed.open();
+			},
+			confirmSpeed(e) {
+				console.log('confirmSpeed', e.value[0]);
+				this.speed = e.value[0]
+			},
+			clickTotal() {
+				if (this.total != 1) {
+					this.$refs.pickerTotal.open();
+				}
+			},
+			confirmTotal(e) {
+				console.log('confirmTotal', e.value[0]);
+				this.total = e.value[0]
+				this.adjustFpsData()
+			},
+			changeMode(e) {
+				this.fpsList = []
+				this.fpsData = []
+				if (e == 0) {
+					this.total = 1
+				} else {
+					this.total = 2
+				}
+				this.adjustFpsData()
+			},
+			adjustFpsData() {
+				// 如果 fpsData 太长 → 截取
+				if (this.fpsList.length > this.total) {
+					this.fpsList = this.fpsList.slice(0, this.total);
+				}
+				// 如果 fpsData 太短 → 补齐 null
+				else if (this.fpsList.length < this.total) {
+					const missing = this.total - this.fpsList.length;
+					this.fpsList = this.fpsList.concat(new Array(missing).fill(null));
+				}
+
+				// 如果 fpsList 太长 → 截取
+				if (this.fpsData.length > this.total) {
+					this.fpsData = this.fpsData.slice(0, this.total);
+				}
+				// 如果 fpsList 太短 → 补齐 null
+				else if (this.fpsData.length < this.total) {
+					const missing = this.total - this.fpsData.length;
+					this.fpsData = this.fpsData.concat(new Array(missing).fill(null));
+				}
+			},
+			// 删除图片
+			deletePic(event) {
+				this[`fileList${event.name}`].splice(event.index, 1)
+			},
+			// 新增图片
+			async afterRead(event) {
+				console.log('event---', event)
+				// 当设置 multiple 为 true 时, file 为数组格式,否则为对象格式
+				let lists = [].concat(event.file)
+				let fileListLen = this[`fileList${event.name}`].length
+				lists.map((item) => {
+					this[`fileList${event.name}`].push({
+						...item,
+						status: 'uploading',
+						message: '上传中'
+					})
+				})
+				for (let i = 0; i < lists.length; i++) {
+					const result = await this.uploadFilePromise(lists[i].url)
+					this.avatar = result
+					let item = this[`fileList${event.name}`][fileListLen]
+					this[`fileList${event.name}`].splice(fileListLen, 1, Object.assign(item, {
+						status: 'success',
+						message: '',
+						url: result
+					}))
+					fileListLen++
+				}
+			},
+			uploadFilePromise(url) {
+				return new Promise((resolve, reject) => {
+					let a = uni.uploadFile({
+						url: this.baseURL + '/common/uploadFileByAnny', // 仅为示例,非真实的接口地址
+						filePath: url,
+						name: 'file',
+						success: (res) => {
+							console.log('图片-----', JSON.parse(res.data))
+							setTimeout(() => {
+								resolve(JSON.parse(res.data).fileName)
+							}, 1000)
+						}
+					});
+				})
+			},
+			// 生成点阵数据
+			generateDotMatrix(char) {
+				return new Promise((resolve, reject) => {
+
+					// 使用 uni-app 的 canvas context
+					ctx = uni.createCanvasContext('myCanvas', this);
+
+					// 设置字体样式
+					ctx.setFontSize(this.canvasHeight * 0.9);
+					ctx.setFillStyle('#000')
+					ctx.setTextAlign('center')
+					ctx.setTextBaseline('middle')
+					// 绘制文字
+					// 计算文字位置
+					// 水平居中
+					const x = this.canvasWidth / 2;
+					// 垂直居中,稍微向上偏移一点
+					const y = this.canvasHeight / 2;
+
+					// 绘制文字
+					ctx.fillText(char, x, y);
+
+					// 绘制到画布
+					ctx.draw(false, () => {
+						// 获取画布数据
+						uni.canvasGetImageData({
+							canvasId: 'myCanvas',
+							x: 0,
+							y: 0,
+							width: this.canvasWidth,
+							height: this.canvasHeight,
+							success: (res) => {
+								console.log(res)
+								const imageData = res.data;
+								// 生成一维数组
+								const pixelArray = new Array(this.height * this.width).fill('')
+
+								// 计算每个格子的像素大小
+								const cellWidth = this.canvasWidth / this.width;
+								const cellHeight = this.canvasHeight / this.height;
+
+								// 生成一维数组
+								for (let y = 0; y < this.height; y++) {
+									for (let x = 0; x < this.width; x++) {
+										let blackPixels = 0;
+										let totalPixels = 0;
+
+										// 对每个格子进行采样
+										for (let py = 0; py < cellHeight; py++) {
+											for (let px = 0; px < cellWidth; px++) {
+												const pixelY = Math.floor(y * cellHeight + py);
+												const pixelX = Math.floor(x * cellWidth + px);
+
+												const idx = (pixelY * this.canvasWidth +
+													pixelX) * 4;
+												const r = imageData[idx];
+												const g = imageData[idx + 1];
+												const b = imageData[idx + 2];
+												const a = imageData[idx + 3];
+
+												// 计算亮度
+												const brightness = 0.3 * r + 0.59 * g + 0.11 *
+													b;
+
+												// 如果亮度小于阈值且透明度大于阈值,则认为是黑色
+												if (brightness < 128 && a > 128) {
+													blackPixels++;
+												}
+												totalPixels++;
+											}
+										}
+
+										// 如果黑色像素占比超过阈值,则设置为当前选择的颜色
+										const isBlack = blackPixels / totalPixels > 0.3;
+										const arrayIndex = y * this.width + x;
+										pixelArray[arrayIndex] = isBlack ? this.color :
+										''; // 改为颜色值或空字符串
+									}
+								}
+
+								resolve(pixelArray);
+							},
+							fail: (err) => {
+								reject(err);
+							}
+						});
+					});
+				});
+			},
+
+			// 修改添加文字方法
+			async addText() {
+				uni.showModal({
+					title: '添加文字',
+					content: '',
+					editable: true,
+					placeholderText: '请输入文字',
+					success: async (res) => {
+						if (res.confirm) {
+							const text = res.content;
+							if (text.length > 0) {
+								try {
+									// 生成点阵数据
+									const dotMatrix = await this.generateDotMatrix(text);
+									console.log('点阵数据:', dotMatrix);
+									this.pixelData = dotMatrix
+								} catch (error) {
+									console.error('生成点阵失败:', error);
+									uni.showToast({
+										title: '生成点阵失败',
+										icon: 'none'
+									});
+								}
+							} else {
+								uni.showToast({
+									title: '请输入文字',
+									icon: 'none'
+								});
+							}
+						}
+					}
+				});
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+.color_list {
+	width: 100%;
+	display: flex;
+	justify-content: center;
+	flex-wrap: wrap;
+	gap: 30rpx;
+	box-sizing: border-box;
+	padding: 50rpx;
+
+	.color_item {
+		width: 30rpx;
+		height: 30rpx;
+		border-radius: 5rpx;
+	}
+}
+
+.controls {
+	position: absolute;
+	bottom: 10px;
+	left: 50px;
+	display: flex;
+	gap: 20px;
+	z-index: 9;
+}
+
+.animation_pixel {
+	width: 100vw;
+	height: 100vh;
+	position: relative;
+
+	.back_btn {
+		position: absolute;
+		left: 30px;
+		top: 20px;
+		z-index: 9999;
+	}
+
+	.canvas {
+		margin: 100rpx auto 0;
+		background: #ccc;
+		position: absolute;
+		z-index: 1;
+		left: -9999px;
+		top: -9999px;
+	}
+
+	.pixel_grid {
+		position: fixed;
+		left: 0;
+		top: var(--status-bar-height);
+		right: 0;
+		bottom: 0;
+		display: grid;
+		gap: 1px;
+		box-sizing: border-box;
+		background: #eee;
+		touch-action: none;
+		-webkit-touch-callout: none;
+		-webkit-user-select: none;
+		user-select: none;
+
+		.pixel_cell {
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			box-sizing: border-box;
+			background: #fff;
+
+			.pixel_cell_item {
+				width: 50%;
+				height: 50%;
+				aspect-ratio: 1/1;
+				font-size: 10rpx;
+			}
+		}
+	}
+
+}
+</style>
\ No newline at end of file
diff --git a/ledApp/pages/feedback/feedback.vue b/ledApp/pages/feedback/feedback.vue
new file mode 100644
index 0000000..ab2cf71
--- /dev/null
+++ b/ledApp/pages/feedback/feedback.vue
@@ -0,0 +1,111 @@
+<template>
+	<view class="page_bg">
+		<uv-navbar title="意见反馈" :autoBack="true" :placeholder="true" leftIconColor="#fff" bgColor="rgba(0,0,0,0)" titleStyle="color:#fff"></uv-navbar>
+		
+		<view class="feedback">
+			<uv-textarea v-model="content" placeholder="请输入反馈内容" height="400rpx" customStyle="background:#ECEFF6" textStyle="font-size:27rpx"></uv-textarea>
+		</view>
+		
+		<view class="feedback_btn" @click="clickFeedback">
+			提交反馈
+		</view>
+		
+		<view class="foot_tips">
+			客服电话:{{phone}}
+		</view>
+		
+	</view>
+</template>
+
+<script>
+	import { feekback,configPhone } from '@/api/api.js'
+	export default {
+		data() {
+			return {
+				content: '',
+				userId: '',
+				phone: ''
+			}
+		},
+		onLoad() {
+			this.userId = uni.getStorageSync('userId');
+			this.getConfigPhone()
+		},
+		methods: {
+			getConfigPhone(){
+				configPhone().then(res=>{
+					console.log(res.msg)
+					this.phone = res.msg
+				})
+			},
+			clickFeedback(){
+				if(!this.content || this.content.trim() == ''){
+					uni.showToast({
+						title: '请输入反馈内容',
+						duration: 2000,
+						icon: 'none'
+					});
+					this.content = ''
+					return
+				}
+				feekback({
+					content: this.content,
+					userId: this.userId
+				}).then(res=>{
+					console.log(res)
+					uni.showToast({
+						title: res.msg,
+						duration: 2000,
+						icon: 'none'
+					});
+					if(res.code == 200){
+						uni.navigateBack({
+							delta: 1
+						});
+					}
+				})
+				
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+.foot_tips{
+	position: fixed;
+	left: 0;
+	bottom: 100rpx;
+	right: 0;
+	text-align: center;
+	font-size: 30rpx;
+	color: rgba(0,0,0,0.7);
+	line-height: 38rpx;
+}
+.feedback_btn{
+	width: 423rpx;
+	height: 96rpx;
+	background: linear-gradient( 142deg, #6FD2FF 0%, #268DFF 100%);
+	box-shadow: 0rpx 12rpx 27rpx 0rpx rgba(56,151,243,0.38);
+	border-radius: 385rpx 385rpx 385rpx 385rpx;
+	font-size: 30rpx;
+	color: #FFFFFF;
+	line-height: 96rpx;
+	text-align: center;
+	margin: 60rpx auto 0;
+}
+.feedback{
+	margin: 20rpx 30rpx 0;
+	background: linear-gradient( 180deg, #FFFFFF 0%, rgba(255,255,255,0.5) 100%);
+	box-shadow: 8rpx 8rpx 23rpx 0rpx rgba(67,147,248,0.1), -8rpx -8rpx 23rpx 0rpx rgba(255,255,255,0.4);
+	border-radius: 23rpx 23rpx 23rpx 23rpx;
+	border: 2rpx solid #FFFFFF;
+	box-sizing: border-box;
+	padding:30rpx;
+}
+.page_bg{
+	width: 100%;
+	min-height: calc(100vh - var(--window-bottom));
+	background:url('/static/img/bg_2.jpg') no-repeat center top;
+	background-size: 100% auto;
+}
+</style>
diff --git a/ledApp/pages/index/index.vue b/ledApp/pages/index/index.vue
new file mode 100644
index 0000000..2c326cc
--- /dev/null
+++ b/ledApp/pages/index/index.vue
@@ -0,0 +1,1353 @@
+<template>
+	<view :class="deviceList.length == 0?'page_bg':'page_bg1'">
+		<uv-navbar title="我的设备" :placeholder="true" leftIcon="" bgColor="rgba(0,0,0,0)" titleStyle="color:#fff"></uv-navbar>
+
+		<view class="hi" v-if="deviceList.length == 0">
+			HI,你来了~
+		</view>
+		<view class="add_device" v-if="deviceList.length == 0">
+			<navigator url="/pages/addDevice/addDevice" hover-class="none">
+				<image class="ico_add" src="/static/img/btn_1.png" mode="widthFix"></image>
+				<view class="name">
+					添加设备
+				</view>
+			</navigator>
+		</view>
+
+		<view class="device_list" v-if="deviceList.length != 0">
+			<view class="device_item" v-for="(item,index) in deviceList" :key="index">
+				<view class="item">
+					<image class="ph" src="/static/img/ph_1.png" mode="aspectFill"></image>
+					<view class="info">
+						<view class="name">
+							{{item.name}}
+						</view>
+						<view class="version">
+							v {{item.versions}}
+						</view>
+						<view class="power">
+							<view v-if="devKwh && dId == item.id">
+								电量{{devKwh * 10}}%
+							</view>
+						</view>
+						
+						<view class="state" v-if="dId != item.id">
+							未连接
+						</view>
+						
+						<view class="state green" v-if="dId == item.id">
+							已连接
+						</view>
+						<view class="btn" v-if="dId != item.id">
+							<uv-button type="primary" shape="circle" text="连接" size="small"
+								@click="createBLEConnection(item,index)"></uv-button>
+							<view class="btn_none"></view>
+							<uv-button type="primary" shape="circle" text="删除" size="small"
+								@click="clickEquipmentDel(item.id, index)"></uv-button>
+						</view>
+						<view class="btn" v-if="dId == item.id">
+							<uv-button type="primary" shape="circle" text="关闭" size="small"
+								@click="clickCloseBLEConnection(index)"></uv-button>
+						</view>
+
+						<!-- <view class="btn">
+							<uv-button type="primary" shape="circle" text="设备升级" size="small"></uv-button>
+						</view> -->
+					</view>
+				</view>
+				<view class="item_btn" v-if="dId == item.id">
+					<uv-button type="primary" shape="circle" text="调节亮度" size="small" :plain="true"
+						@click="this.$refs.popupBRIG.open();"></uv-button>
+					<uv-button type="primary" shape="circle" text="发送LED" size="small" :plain="true"
+						@click="getTempleteList"></uv-button>
+				</view>
+			</view>
+			<!-- <view class="device_item">
+				<view class="item">
+					<image class="ph" src="/static/img/ph_1.png" mode="aspectFill"></image>
+					<view class="info">
+						<view class="name">
+							设备名称占位文字
+						</view>
+						<view class="version">
+							v2.21
+						</view>
+						<view class="power">
+							
+						</view>
+						<view class="state">
+							未连接
+						</view>
+						<view class="btn">
+							<uv-button type="primary" shape="circle" text="设备升级" size="small"></uv-button>
+						</view>
+					</view>
+				</view>
+			</view> -->
+
+			<navigator class="add_device_small" url="/pages/addDevice/addDevice" hover-class="none"
+				:render-link="false">
+				<image class="ico_add" src="/static/img/btn_1.png" mode="widthFix"></image>
+				<view class="name">
+					添加设备
+				</view>
+			</navigator>
+
+		</view>
+
+
+		<!-- 调节亮度 -->
+		<uv-popup ref="popupBRIG" mode="center" round="30rpx">
+			<view class="popup_pwd">
+				<view class="title">调节亮度</view>
+				<view class="cont">
+					<uv-slider v-model="brightness" :min="0" :max="9" :step="1" block-size="24"
+						block-color="#2979ff"></uv-slider>
+				</view>
+				<view class="btns_two">
+					<view class="btn2">
+						<uv-button type="primary" plain @click="this.$refs.popupBRIG.close();">关闭</uv-button>
+					</view>
+					<view class="btn2">
+						<uv-button type="primary" @click="commandBRIG()">确定</uv-button>
+					</view>
+				</view>
+			</view>
+		</uv-popup>
+
+		<!-- 模版列表 -->
+		<uv-popup ref="popupTemplete" mode="center" round="30rpx">
+			<view class="popup_pwd">
+				<view class="title">发送LED</view>
+				<view class="cont cont_led">
+
+					<view class="led_item" v-for="(item,index) in templeteData" :key="index">
+						<view class="led_item_pic">
+							<uv-image :src="baseURL+item.pic" width="160rpx" height="120rpx" :radius="5"></uv-image>
+						</view>
+						<view class="led_item_text">
+							<view class="name">
+								{{item.name}}
+							</view>
+							<view class="text">
+								{{item.screen}}
+							</view>
+						</view>
+						<uv-button type="primary" shape="circle" size="small" @click="commandSendTemplate(item)">播放</uv-button>
+					</view>
+
+				</view>
+				<view class="btns">
+					<uv-button type="primary" plain @click="this.$refs.popupTemplete.close();">关闭</uv-button>
+				</view>
+			</view>
+		</uv-popup>
+
+	</view>
+</template>
+
+<script>
+	import {
+		equipmentList,
+		equipmentDel,
+		templeteList
+	} from '@/api/api.js'
+	export default {
+		data() {
+			return {
+				searchIng: true,
+				userId: '',
+				deviceList: [],
+
+				bluetoothList: [], //搜索到的蓝牙设备列表
+				deviceId: '', //设备id
+				serviceId: '', //服务uuid
+				characteristicId1: '', //特征uuid
+				characteristicId2: '',
+				characteristicId3: '',
+				showPwdPopup: false,
+				password: '', //设置的密码
+				checId: '', //智能壳的ID
+				batchNumber: '', //生产顺序号
+				fileNames: [], //文件名称列表
+				brightness: 3, //亮度
+				nameIndex: 1,
+				name: '',
+
+				ledName: '', // 名称
+				screen: '',
+				ledTotal: 3, // 总帧数
+				ledCurrent: 0, // 当前帧数
+				ledData: [],
+				ledSpeed: '', //速度
+				isLink: false,
+				listIndex: 0,
+				templeteData: [],
+				baseURL: '',
+				dId: '' ,//已连接的设备id,
+				devKwh: '',
+				platform: ''
+			}
+		},
+		onLoad() {
+			//uni.setStorageSync('dId', '');
+			this.userId = uni.getStorageSync('userId');
+			this.baseURL = uni.$uv.http.config.baseURL
+			const systemInfo = uni.getSystemInfoSync();
+			if (systemInfo.platform === 'ios') {
+				// iOS 设备
+				this.platform = 'ios'
+			} else if (systemInfo.platform === 'android') {
+				// Android 设备
+				this.platform = 'android'
+			}
+			else{
+				this.platform = ''
+			}
+		},
+		onShow() {
+			this.getEquipmentList()
+			this.openBluetooth()
+			
+			const that = this
+			this.userId = uni.getStorageSync('userId');
+			
+			this.dId = uni.getStorageSync('dId');
+			if(this.dId){
+				this.deviceId = uni.getStorageSync('deviceId');
+				this.serviceId = uni.getStorageSync('serviceId');
+				this.characteristicId1 = uni.getStorageSync('characteristicId1');
+				this.characteristicId2 = uni.getStorageSync('characteristicId2');
+				this.characteristicId3 = uni.getStorageSync('characteristicId3');
+				this.checId = uni.getStorageSync('checId');
+				this.password = uni.getStorageSync('password');
+				this.startNotify()
+			}
+			
+			console.log('dId---',this.dId)
+			
+			//this.getTempleteList()
+		},
+		onHide() {
+			console.log('onHide-----')
+			uni.$off('BLECharacteristic')
+			
+			uni.setStorageSync('dId', this.dId);
+			uni.setStorageSync('deviceId', this.deviceId);
+			uni.setStorageSync('serviceId', this.serviceId);
+			uni.setStorageSync('characteristicId1', this.characteristicId1);
+			uni.setStorageSync('characteristicId2', this.characteristicId2);
+			uni.setStorageSync('characteristicId3', this.characteristicId3);
+			uni.setStorageSync('checId', this.checId);
+			uni.setStorageSync('password', this.password);
+			
+			//this.clickCloseBLEConnection()
+			
+			//this.closeBluetooth()
+			//this.closeBLEConnection()
+		},
+		onUnload() {
+			console.log('onUnload-----')
+			uni.$off('BLECharacteristic')
+			
+			//this.closeBluetooth()
+			//this.closeBLEConnection()
+		},
+		methods: {
+			// 获取模版列表
+			getTempleteList() {
+				uni.showLoading({
+					title: '加载中...',
+					mask: true
+				});
+				templeteList({
+					userId: this.userId,
+					pageNum: 1,
+					pageSize: 99
+				}).then(res => {
+					console.log('模版列表-------', res.rows)
+					uni.hideLoading();
+					this.templeteData = res.rows
+					if (this.templeteData.length != 0) {
+						this.$refs.popupTemplete.open();
+					} else {
+						uni.showToast({
+							title: '暂无数据,请先添加模版!',
+							icon: 'none',
+							mask: true
+						});
+					}
+
+				})
+			},
+			// 删除设备
+			clickEquipmentDel(id, index) {
+				uni.showModal({
+					title: '提示',
+					content: '删除后无法恢复,确定删除吗?',
+					success: (resa) => {
+						if (resa.confirm) {
+							console.log('用户点击确定');
+							equipmentDel(id).then(res=>{
+								console.log(res)
+								uni.showToast({
+									title: res.msg,
+									duration: 2000,
+									icon: 'none'
+								});
+								if(res.code == 200){
+									//this.getTempleteList()
+									this.deviceList.splice(index, 1)
+								}
+							})
+						} else if (resa.cancel) {
+							console.log('用户点击取消');
+						}
+					}
+				});
+			},
+			// 设备列表
+			getEquipmentList() {
+				this.deviceList = []
+				equipmentList({
+					userId: this.userId,
+					code: this.platform,
+					pageNum: 1,
+					pageSize: 99
+				}).then(res => {
+					console.log('设备列表------', res.rows)
+					this.deviceList = res.rows
+				})
+			},
+			// 初始化蓝牙
+			openBluetooth() {
+				uni.openBluetoothAdapter({
+					success: (res) => {
+						console.log('蓝牙初始化成功');
+						this.onBLEStateChange()
+					},
+					fail: (err) => {
+						console.log('蓝牙初始化失败', err);
+					}
+				});
+			},
+			// 关闭蓝牙
+			closeBluetooth() {
+				uni.closeBluetoothAdapter({
+					success: (res) => {
+						console.log(res)
+					}
+				})
+			},
+			// 断开与低功耗蓝牙设备的连接
+			closeBLEConnection() {
+				const that = this
+				uni.closeBLEConnection({
+					deviceId: this.deviceId,
+					success(res) {
+						console.log('断开与低功耗蓝牙设备的连接', res)
+						that.dId = ''
+						uni.setStorageSync('dId', '');
+					}
+				})
+			},
+			// 关闭连接
+			clickCloseBLEConnection() {
+				const that = this
+				console.log('that.deviceId-----', that.deviceId)
+				uni.closeBLEConnection({
+					deviceId: that.deviceId,
+					success(res) {
+						console.log('关闭连接', res)
+						// 关闭连接清空已连接的设备id
+						that.dId = ''
+						uni.setStorageSync('dId', '');
+						//that.isLink = false
+						//that.deviceList[that.listIndex].isLink = false
+					}
+				})
+			},
+			// 发送LED
+			commandSendTemplate(item) {
+				console.log('--------------', item);
+
+				uni.showLoading({
+					title: '发送中...',
+					mask: true
+				});
+
+				const name = 'A' + this.nameIndex++;
+				this.ledName = name;
+				this.screen = item.screen;
+				this.ledTotal = item.fpsTotal;
+				this.ledSpeed = item.speed
+				this.ledCurrent = 0;
+				
+				try {
+					this.ledData = JSON.parse(item.fpsData);
+					console.log('this.ledData------',this.ledData)
+				} catch (e) {
+					uni.hideLoading();
+					console.error('fpsData 解析失败:', e);
+					uni.showToast({
+						title: 'fpsData 解析失败',
+						duration: 2000
+					});
+					return;
+				}
+
+				if (this.ledData.length > 0) {
+					this.commandINST(this.ledData[this.ledCurrent]);
+				} else {
+					uni.hideLoading();
+					uni.showToast({
+						title: '无可发送的帧数据',
+						duration: 2000
+					});
+					console.warn('无可发送的帧数据');
+				}
+			},
+			// 连接
+			createBLEConnection(item, index) {
+				const that = this
+				if (this.dId) {
+					uni.showToast({
+						title: '请先关闭已连接的设备',
+						icon: 'none',
+						mask: true
+					});
+					return
+				}
+				this.deviceId = item.deviceId
+				this.serviceId = item.serviceId
+				this.characteristicId1 = item.characteristicId1
+				this.characteristicId2 = item.characteristicId2
+				this.characteristicId3 = item.characteristicId3
+				this.checId = item.checId
+				this.password = item.password
+				
+				
+				uni.showLoading({
+					title: '连接中...',
+					mask: true
+				});
+				uni.createBLEConnection({
+					deviceId: this.deviceId,
+					success: (res) => {
+						console.log('连接低功耗蓝牙设备', res);
+						this.startNotify()
+						// setTimeout(() => {
+						// 	that.setBaud()
+						// 	that.setBLEMTU()
+						// }, 500);
+						this.dId = item.id
+						
+						setTimeout(() => {
+							this.commandPOWR()
+						}, 1500);
+					},
+					fail: (err) => {
+						//console.log('连接失败', err);
+						this.closeBLEConnection()
+						uni.hideLoading();
+						uni.showToast({
+							title: '连接失败',
+							icon: 'none',
+							mask: true
+						});
+					}
+				});
+			},
+			// 设置波特率
+			setBaud() {
+				var that = this
+				var hexStr = "06000900009600000801009F"
+				uni.writeBLECharacteristicValue({
+					deviceId: this.deviceId,
+					serviceId: this.serviceId,
+					characteristicId: this.characteristicId3,
+					value: this.hexStringToArrayBuffer(hexStr),
+					writeType: 'write',
+					success: (res) => {
+						console.log('设置波特率成功----', res);
+						//that.commandBIND()
+						uni.hideLoading();
+					},
+					fail: (err) => {
+						console.log('设置波特率失败----', err);
+						uni.hideLoading();
+						uni.showToast({
+							title: '设置波特率失败',
+							icon: 'none',
+							mask: true
+						});
+					}
+				});
+			},
+			// 设置蓝牙最大传输单元
+			setBLEMTU() {
+				var that = this
+				uni.setBLEMTU({
+					deviceId: this.deviceId,
+					mtu: 80, // 最大值一般 512,蓝牙 4.2 通常支持 247
+					success: (res) => {
+						console.log('设置MTU成功:', res);
+						uni.hideLoading();
+					},
+					fail: (err) => {
+						console.error('设置MTU失败:', err);
+						uni.hideLoading();
+						uni.showToast({
+							title: '设置MTU失败',
+							icon: 'none',
+							mask: true
+						});
+					}
+				});
+			},
+			commandCHEC() {
+				//555555555555555555557e41434845430095977e
+				// 构建指令数据
+				const startBytes = new Array(10).fill(0x55); // 10个启动字节
+				const frameHeader = 0x7E; // 帧头
+				const packageType = 0x41; // 包类型
+				const command = [0x43, 0x48, 0x45, 0x43]; // "CHEC"的ASCII码
+				const reserved = 0x00; // 保留字节
+
+				// 组合数据
+				const data = [
+					...startBytes,
+					frameHeader,
+					packageType,
+					...command,
+					reserved
+				];
+
+				// 计算CRC校验码(从包头段开始计算)
+				const crc = this.calcCRC16(data.slice(11));
+
+				// 添加CRC和帧尾
+				data.push((crc >> 8) & 0xFF); // CRC高8位
+				data.push(crc & 0xFF); // CRC低8位
+				data.push(frameHeader); // 帧尾
+				console.log('CHEC data----AAAA--------', this.hexArrayToHexString(data))
+
+				const buffer = new Uint8Array(data).buffer;
+				setTimeout(() => {
+					this.writeBLECharacteristicValue(buffer)
+				}, 500);
+			},
+			// 加密绑定号 发送绑定号
+			commandBIND() {
+				// uni.showToast({
+				// 	title: '发送绑定号',
+				// 	icon: 'none',
+				// 	mask: true
+				// });
+				//console.log('checId---', this.checId)
+				// 59 5C 37 39 71 4D 56 65 5A 37 12 18 07 06 2A 18 36 1E 1E 06
+				// 7e 41 42 49 4e 44 6d 71 41 45 95 4e 61 78 9e 39 49 12 15 13 71 42 94 50 50 17 7c c9 7e
+				//帧头+0x41+ BIND +加密绑定号+CRC校验码+帧尾
+				// 构建指令数据
+				const startBytes = new Array(10).fill(0x55); // 10个启动字节
+				const frameHeader = 0x7E; // 帧头
+				const packageType = 0x41; // 包类型
+				const command = [0x42, 0x49, 0x4E, 0x44]; // "BIND"的ASCII码
+				const reserved = this.replaceScreenSizeInID(this.checId, this.password); // 密码
+				//console.log('reserved----111', reserved)
+				const reservedId = this.encryptSmartShellID(reserved)
+				//console.log('reservedid--222', reservedId)
+				// 组合数据
+				const data = [
+					...startBytes,
+					frameHeader,
+					packageType,
+					...command,
+					...this.hexStringToArray(reservedId)
+				];
+
+				// 计算CRC校验码(从包头段开始计算)
+				const crc = this.calcCRC16(data.slice(11));
+
+				// 添加CRC和帧尾
+				data.push((crc >> 8) & 0xFF); // CRC高8位
+				data.push(crc & 0xFF); // CRC低8位
+				data.push(frameHeader); // 帧尾
+				//console.log('BIND data----BBBB--------', this.hexArrayToHexString(data))
+
+				const buffer = new Uint8Array(data).buffer;
+				setTimeout(() => {
+					this.writeBLECharacteristicValue(buffer)
+				}, 500);
+			},
+
+			//向低功耗蓝牙设备特征值中写入二进制数据
+			writeBLECharacteristicValue(buffer) {
+				const that = this
+				uni.writeBLECharacteristicValue({
+					deviceId: this.deviceId,
+					serviceId: this.serviceId,
+					characteristicId: this.characteristicId2,
+					value: buffer,
+					writeType: 'write',
+					success: (res) => {
+						//console.log('写入指令发送成功----', res);
+						uni.hideLoading();
+					},
+					fail: (err) => {
+						//console.log('写入指令发送失败----', err);
+						uni.hideLoading();
+						uni.showToast({
+							title: '写入指令发送失败',
+							icon: 'none',
+							mask: true
+						});
+					}
+				});
+			},
+			// 调节亮度
+			commandBRIG() {
+				console.log('brightness----', this.brightness)
+				//1.手机发指令:启动字节+帧头+0x41+ BRIG +亮度+CRC校验码+帧尾。
+				//2.智能壳回指令:启动字节+帧头+0x41+ BRIG +返回标志+CRC校验码+帧尾。
+				// 构建指令数据
+				const startBytes = new Array(10).fill(0x55); // 10个启动字节
+				const frameHeader = 0x7E; // 帧头
+				const packageType = 0x41; // 包类型
+				const command = [0x42, 0x52, 0x49, 0x47]; // "BRIG"的ASCII码
+
+				//亮度:单字节,0,1,2,3。。。ASIC码
+				const brightness = this.stringToASCIIByteArray(this.brightness.toString())
+				//const brightness = 0x32
+
+				// 组合数据
+				const data = [
+					...startBytes,
+					frameHeader,
+					packageType,
+					...command,
+					brightness
+				];
+				// 计算CRC校验码(从包头段开始计算)
+				const crc = this.calcCRC16(data.slice(11));
+				// 添加CRC和帧尾
+				data.push((crc >> 8) & 0xFF); // CRC高8位
+				data.push(crc & 0xFF); // CRC低8位
+				data.push(frameHeader); // 帧尾
+				console.log('BRIG data----', data)
+				const buffer = new Uint8Array(data).buffer;
+				setTimeout(() => {
+					this.writeBLECharacteristicValue(buffer)
+				}, 500);
+
+			},
+			//添加信息指令(INST)
+			commandINST(hexString) {
+				// 1.手机发指令:启动+帧头+包头段(0x43+简易绑定号+屏大小)+INST+信息属性+文件名+总帧数+当前帧*+块描述+块数据+CRC校验码+帧尾
+				//注意:当前帧如果是0xff,则表示是结束帧,后面四个字节,第一个字节(表示速度),第二个字节(播放次数,0表示一直播放),后面二个字节保留;其他数字表示表示当前是第几帧。
+				//2.智能壳指令:启动+帧头+包头段+INST+返回标志+加密地址+CRC校验码+帧尾
+				// 构建指令数据
+				const startBytes = new Array(10).fill(0x55); // 10个启动字节
+				const frameHeader = 0x7E; // 帧头
+				const packageType = 0x43; // 包类型
+				// 使用新的简易绑定号生成函数
+				const simpleBindId = this.generateSimpleBindId();
+
+				// 屏大小 (2 bytes): 从ID中获取
+				//const screenSize = this.hexStringToArray(this.checId.slice(20, 24));
+				//console.log('屏大小ID----',this.checId)
+				//console.log('屏大小----',screenSize,this.checId.slice(20, 24))
+				const screenSize = [0x0C,0x18]
+				const packageHeader = [packageType, ...simpleBindId, ...screenSize];
+				const command = [0x49, 0x4E, 0x53, 0x54]; // "INST"的ASCII码
+				const infoAttribute = 0x02; // 信息属性: 0=一般信息
+
+				const fileName = this.stringToASCIIByteArray(this.ledName) //文件名称
+				//console.log('fileName----', this.ledName)
+				// 总帧数
+				const totalFrame = this.hexStringToArray(this.ledTotal.toString(16).padStart(2, '0')
+					.toUpperCase()) //[0x01]; //this.hexStringToArray('01')
+				//console.log('totalFrame----', totalFrame)
+				// 当前帧
+				const currentFrame = this.hexStringToArray(this.ledCurrent.toString(16).padStart(2, '0').toUpperCase()) //0x00
+
+				// 块描述
+				const tblockDesc = 0x00
+
+				// 块数据
+				const blockDescription = this.hexStringToArray(hexString)
+				//const blockString = "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FF0000FF000000000000000000000000000000000000000000000000000000000000FF0000000000000000000000000000000000000000000000FF0000000000000000FF0000FF00000000000000000000000000000000FF0000FF000000000000000000FF0000000000FF0000FF00FF0000FF0000FF000000000000000000FF00FF0000FF000000000000000000FF00000000000000000000FF000000FF0000FF0000FF0000FF0000000000FF0000000000000000000000FF0000000000000000000000FF0000FF0000000000000000FF0000FF0000000000000000000000000000FF00FF000000000000000000000000FF00FF0000FF000000000000000000FF00000000000000FF0000FF0000FF0000FF0000FF0000000000000000FF0000FF000000000000000000000000FF0000000000FF0000000000000000000000000000000000000000FF0000000000000000000000000000000000FF0000000000000000000000000000000000FF00000000000000000000000000FF0000FF000000FF0000FF0000000000000000000000FF00000000000000FF0000FF000000FF00FF0000FF000000000000FF0000000000000000000000000000000000000000000000FF0000000000FF0000000000000000000000FF0000000000000000000000000000000000000000FF0000000000000000FF0000000000000000FF0000000000000000FF00000000000000FF0000FF0000FF0000FF000000000000000000FF00000000000000FF0000FF0000FF0000FF0000FF000000000000000000000000000000000000FF0000FF0000000000000000FF0000000000000000FF0000000000000000FF0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FF00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
+				//const blockDescription = this.hexStringToArray(blockString)
+
+				// 组合数据
+				const data = [
+					...startBytes,
+					frameHeader,
+					...packageHeader,
+					...command,
+					infoAttribute,
+					...fileName,
+					...totalFrame,
+					...currentFrame,
+					tblockDesc,
+					...blockDescription
+				];
+				// 计算CRC校验码(从包头段开始计算)
+				const crc = this.calcCRC16(data.slice(11));
+				// 添加CRC和帧尾
+				data.push((crc >> 8) & 0xFF); // CRC高8位
+				data.push(crc & 0xFF); // CRC低8位
+				data.push(frameHeader); // 帧尾
+				//console.log(this.ledCurrent, '----INST data----', this.hexArrayToHexString(data))
+				//const buffer = new Uint8Array(data).buffer;
+				console.log('第几帧-------',this.ledCurrent)
+				setTimeout(() => {
+					//this.writeBLECharacteristicValue(buffer)
+					this.sendBLEDataInChunks(
+						this.hexArrayToHexString(data)
+					)
+				}, 50);
+			},
+			// 发送结束针
+			commandEnd() {
+				const startBytes = new Array(10).fill(0x55); // 10个启动字节
+				const frameHeader = 0x7E; // 帧头
+				const packageType = 0x43; // 包类型
+				// 使用新的简易绑定号生成函数
+				const simpleBindId = this.generateSimpleBindId();
+
+				// 屏大小 (2 bytes): 从ID中获取
+				const screenSize = this.hexStringToArray(this.checId.slice(20, 24));
+				const packageHeader = [packageType, ...simpleBindId, ...screenSize];
+				const command = [0x49, 0x4E, 0x53, 0x54]; // "INST"的ASCII码
+				const infoAttribute = 0x02; // 信息属性: 0=一般信息
+
+				const fileName = this.stringToASCIIByteArray(this.ledName) //文件名称
+				//console.log('fileName----', this.ledName)
+				// 总帧数
+				const totalFrame = this.hexStringToArray(this.ledTotal.toString(16).padStart(2, '0').toUpperCase()) //[0x01]; //this.hexStringToArray('01')
+				//console.log('totalFrame----', totalFrame)
+
+				//结束幕:
+				const ffdata = this.hexStringToArray('ff')
+				const speedData = this.hexStringToArray(this.ledSpeed.toString(16).padStart(2, '0').toUpperCase())
+				
+				console.log('speedData------',speedData)
+				const ffend = this.hexStringToArray('000000')
+				
+				const endData = [
+					...startBytes,
+					frameHeader,
+					...packageHeader,
+					...command,
+					infoAttribute,
+					...fileName,
+					...totalFrame,
+					...ffdata,
+					...speedData,
+					...ffend
+				]
+				// 计算CRC校验码(从包头段开始计算)
+				const endcrc = this.calcCRC16(endData.slice(11));
+
+				// 添加CRC和帧尾
+				endData.push((endcrc >> 8) & 0xFF); // CRC高8位
+				endData.push(endcrc & 0xFF); // CRC低8位
+				endData.push(frameHeader); // 帧尾
+				//console.log('INST endData----', this.hexArrayToHexString(endData))
+
+				const endbuffer = new Uint8Array(endData).buffer;
+				setTimeout(() => {
+					this.writeBLECharacteristicValue(endbuffer)
+				}, 50);
+			},
+			async sendBLEDataInChunks(hexStr) {
+				const maxBytes = 76
+				const interval = 20
+				const chunks = this.splitHexData(hexStr, maxBytes)
+
+				//console.log(`📦 总共需要发送 ${chunks.length} 包,每包 ${maxBytes} 字节,间隔 ${interval}ms`)
+
+				for (let i = 0; i < chunks.length; i++) {
+					const buffer = this.hexToBuffer(chunks[i])
+					try {
+						await new Promise((resolve, reject) => {
+							uni.writeBLECharacteristicValue({
+								deviceId: this.deviceId,
+								serviceId: this.serviceId,
+								characteristicId: this.characteristicId2,
+								value: buffer,
+								success: (res) => {
+									//console.log(`✅ 第 ${i + 1} 包发送成功`, res)
+									resolve()
+								},
+								fail: (err) => {
+									//console.error(`❌ 第 ${i + 1} 包发送失败`, err)
+									uni.hideLoading();
+									reject(err)
+								}
+							})
+						})
+					} catch (err) {
+						console.error(`🚫 发送中断,第 ${i + 1} 包发送失败`)
+						uni.hideLoading();
+						break
+					}
+					// 延迟下一包发送
+					await new Promise(r => setTimeout(r, interval))
+				}
+				//console.log("🎉 数据发送完成")
+			},
+			// 读取电量
+			commandPOWR() {
+				console.log('读取电量POWR----', )
+				// 构建指令数据
+				const startBytes = new Array(10).fill(0x55); // 10个启动字节
+				const frameHeader = 0x7E; // 帧头
+				const packageType = 0x41; // 包类型
+				const command = [0x50, 0x4F, 0x57, 0x52]; // "POWR"的ASCII码
+
+				// 组合数据
+				const data = [
+					...startBytes,
+					frameHeader,
+					packageType,
+					...command
+				];
+
+				// 计算CRC校验码(从包头段开始计算)
+				const crc = this.calcCRC16(data.slice(11));
+
+				// 添加CRC和帧尾
+				data.push((crc >> 8) & 0xFF); // CRC高8位
+				data.push(crc & 0xFF); // CRC低8位
+				data.push(frameHeader); // 帧尾
+				//console.log('POWR----', data)
+
+				const buffer = new Uint8Array(data).buffer;
+				setTimeout(() => {
+					this.writeBLECharacteristicValue(buffer)
+				}, 500);
+			},
+			// 监听蓝牙连接状态
+			onBLEStateChange(){
+				const that = this
+				uni.onBLEConnectionStateChange(res => {
+					//console.log('监听蓝牙连接状态------------',res)
+					if (!res.connected) {
+						// uni.showToast({
+						// 	title: '蓝牙设备已断开',
+						// 	icon: 'none',
+						// 	mask: true
+						// });
+						that.dId = ''
+						uni.setStorageSync('dId', '');
+						//console.log('蓝牙设备已断开------------',res)
+						//that.isLink = false
+						// for(let i = 0;i<that.deviceList.length;i++){
+						// 	if(that.deviceList[i].isLink){
+						// 		that.deviceList[i].isLink = false
+						// 	}
+						// }
+					}
+				})
+			},
+			// 开始监听
+			startNotify() {
+				const that = this
+				uni.notifyBLECharacteristicValueChange({
+					deviceId: that.deviceId,
+					serviceId: that.serviceId,
+					characteristicId: that.characteristicId1,
+					state: true,
+					success: () => {
+						console.log('开始监听------');
+						setTimeout(() => {
+							that.setBaud()
+							if (uni.getSystemInfoSync().platform === 'android') {
+								that.setBLEMTU();
+							}
+							else{
+								uni.hideLoading();
+							}
+							
+						}, 500);
+						
+						uni.$off('BLECharacteristic');
+						uni.$on('BLECharacteristic', function(res) {
+							
+							//console.log('BLECharacteristic---------------index', res)
+							const resData = that.ab2hex(res.value);
+							const result = that.removeStartBytes(resData)
+							const resName = that.getCommandType(result);
+							//console.log("回调返回数据-------", resData);
+							//console.log('result-----------', result)
+							//console.log('resName----------', resName)
+							//uni.hideLoading();
+							if (resName == 'CHEC') {
+
+							} else if (resName == 'BIND') {
+								//that.isLink = true;
+								//that.deviceList[that.listIndex].isLink = true;
+								//that.commandPOWR()
+							} else if (resName == 'LIST') {
+								//that.parseFileList(result)
+							} else if (resName == 'BRIG') {
+
+							} else if (resName == 'PLAY') {
+
+							} else if (resName == 'INST') {
+								// 添加动画帧
+								that.ledCurrent++
+								if (that.ledCurrent < that.ledTotal) {
+									// uni.showLoading({
+									// 	title: '发送中',// + that.ledCurrent,
+									// 	mask: true
+									// });
+									that.commandINST(that.ledData[that.ledCurrent]);
+								} else if (that.ledCurrent == that.ledTotal) {
+									// uni.showLoading({
+									// 	title: '发送结束帧',
+									// 	mask: true
+									// });
+									that.commandEnd()
+								}
+
+							} else if (resName == 'DALL') {
+
+							} else if (resName == 'POWR') {
+								// 查询电量
+								let capacityHex = result.slice(-8, -6); // "0a"
+								let capacity = parseInt(capacityHex, 16); // 转十进制
+								that.devKwh = capacity
+							} else {
+								//that.isLink == false
+								uni.showToast({
+									title: '返回数据错误',
+									duration: 2000,
+									icon: 'none'
+								});
+								// 关闭蓝牙设备
+								that.closeBLEConnection()
+							}
+						})
+					},
+					fail: (err) => {
+						//console.log('监听失败----', err);
+						uni.hideLoading();
+						// uni.showToast({
+						// 	title: '监听失败',
+						// 	icon: 'none',
+						// 	mask: true
+						// });
+						that.dId = ''
+						uni.setStorageSync('dId', '');
+					}
+				});
+			},
+			// 生成简易绑定号
+			generateSimpleBindId() {
+				if (!this.checId || !this.password) {
+					//console.error("生成简易绑定号失败:checId或password不可用");
+					uni.hideLoading();
+					uni.showToast({
+						title: '请先连接设备',
+						icon: 'none',
+						mask: true
+					});
+					return;
+				}
+				// 从checId中提取生产顺序号(最后4字节,即8个字符)
+				const productionNumber = this.checId.slice(-8); // 生产顺序号
+				// 生产顺序号+密码
+				const simpleBindData = productionNumber + this.password;
+				// 用加密数字加密
+				const encryptedSimpleBind = this.encryptSmartShellID(simpleBindData);
+				// 取前6字节作为简易绑定号
+				const simpleBindId = this.hexStringToArray(encryptedSimpleBind.slice(0, 12));
+				//console.log('简易绑定号生成----', simpleBindId);
+				return simpleBindId;
+			},
+			// 判断并去除10个0x55启动字节(20个hex字符)
+			removeStartBytes(hexStr) {
+				// 10个0x55 = 20个hex字符
+				const startBytes = '55'.repeat(10); // '55555555555555555555'
+				if (hexStr.startsWith(startBytes)) {
+					return hexStr.slice(20); // 去掉前20个字符
+				}
+				return hexStr;
+			},
+			// 返回指令类型 判断
+			getCommandType(hexString) {
+				// 判断是否有 返回标志 单字节。0(0x30),工作无异常;1(0x31)工作有异常
+				// 判断是否包含返回标志 30 或 31
+				// 查找30或31的位置
+				let returnFlag = '';
+				let cmd = '';
+				let packageType = hexString.slice(2, 4);
+
+				if (packageType == '41') {
+					returnFlag = hexString.slice(12, 14);
+					const before8Chars = hexString.slice(4, 12);
+					cmd = before8Chars.match(/.{2}/g)
+						.map(h => String.fromCharCode(parseInt(h, 16)))
+						.join('');
+					return cmd;
+				} else if (packageType == '43') {
+					returnFlag = hexString.slice(28, 30);
+					const before8Chars = hexString.slice(20, 28);
+					cmd = before8Chars.match(/.{2}/g)
+						.map(h => String.fromCharCode(parseInt(h, 16)))
+						.join('');
+					return cmd;
+				}
+				//console.log('returnFlag------', returnFlag)
+			},
+			// 将十六进制字符串转为 ArrayBuffer
+			hexToBuffer(hexStr) {
+				const typedArray = new Uint8Array(hexStr.match(/[\da-f]{2}/gi).map(h => parseInt(h, 16)))
+				return typedArray.buffer
+			},
+			// 将大十六进制数据切分为每段 maxBytes 大小(单位:字节)
+			splitHexData(hexStr, maxBytes = 100) {
+				const result = []
+				for (let i = 0; i < hexStr.length; i += maxBytes * 2) {
+					result.push(hexStr.slice(i, i + maxBytes * 2))
+				}
+				return result
+			},
+			// 指令ASCII码转换
+			stringToASCIIByteArray(str) {
+				return str.split('').map(char => char.charCodeAt(0));
+			},
+			//16进制数组 转 16进制字符串
+			hexArrayToHexString(hexArray) {
+				return hexArray.map(byte => byte.toString(16).padStart(2, '0')).join('');
+			},
+			// 16进制字符串转16进制数组
+			hexStringToArray(hexStr) {
+				if (!hexStr) return [];
+				return hexStr.match(/.{2}/g).map(byte => parseInt(byte, 16));
+			},
+			// 16进制数据转二进数据
+			hexStringToArrayBuffer(hexStr) {
+				const bytes = new Uint8Array(hexStr.match(/.{2}/g).map(byte => parseInt(byte, 16)));
+				return bytes.buffer;
+			},
+			// 二进数据转16进制数据
+			ab2hex(buffer) {
+				const hexArr = Array.prototype.map.call(
+					new Uint8Array(buffer),
+					function(bit) {
+						return ('00' + bit.toString(16)).slice(-2)
+					}
+				)
+				return hexArr.join('')
+			},
+			calcCRC16(data) {
+				// 创建查表
+				const table = this.buildCRC16Table();
+				let crc = 0;
+				// 计算CRC
+				for (let i = 0; i < data.length; i++) {
+					crc = ((crc << 8) & 0xFF00) ^ table[((crc >> 8) ^ data[i]) & 0xFF];
+				}
+				return crc & 0xFFFF;
+			},
+			buildCRC16Table() {
+				const table = new Array(256);
+				const poly = 0x1021; // CRC-CCITT多项式
+				for (let i = 0; i < 256; i++) {
+					let nData = i << 8;
+					let nAccum = 0;
+					for (let j = 0; j < 8; j++) {
+						if ((nData ^ nAccum) & 0x8000) {
+							nAccum = (nAccum << 1) ^ poly;
+						} else {
+							nAccum <<= 1;
+						}
+						nData <<= 1;
+					}
+					table[i] = nAccum & 0xFFFF;
+				}
+				return table;
+			},
+			// 密码置换掉ID号中"屏的大小",加密下发
+			replaceScreenSizeInID(originalHexString, password4Digit) {
+				// 密码4个数字,每二个合起来一个字节,如3472变成 0x34 0x72
+				const high = password4Digit.slice(0, 2); // "34"
+				const low = password4Digit.slice(2, 4); // "72"
+
+				// 替换屏幕大小字段(第 10、11 个字节,即第20-23个字符)
+				const before = originalHexString.slice(0, 20);
+				const after = originalHexString.slice(24);
+
+				return before + high + low + after;
+			},
+			// 解密函数
+			decryptSmartShellID(encryptedHex) {
+				const key = '2021101236011119680215001413714294505017';
+				const decrypted = [];
+				for (let i = 0; i < 20; i++) {
+					const byteHex = encryptedHex.substr(i * 2, 2);
+					const encryptedByte = parseInt(byteHex, 16);
+					const keyHex = key.substr(i * 2, 2)
+					const keyByte = parseInt(keyHex, 16);
+					const decryptedByte = (encryptedByte - keyByte);
+					decrypted.push(decryptedByte.toString(16).padStart(2, '0').toUpperCase());
+				}
+				return decrypted.join('');
+			},
+			// 加密函数
+			encryptSmartShellID(encryptedHex) {
+				const key = '2021101236011119680215001413714294505017';
+				const decrypted = [];
+				for (let i = 0; i < 20; i++) {
+					const byteHex = encryptedHex.substr(i * 2, 2);
+					const encryptedByte = parseInt(byteHex, 16);
+					const keyHex = key.substr(i * 2, 2)
+					const keyByte = parseInt(keyHex, 16);
+					const decryptedByte = (encryptedByte + keyByte);
+					decrypted.push(decryptedByte.toString(16).padStart(2, '0').toUpperCase());
+				}
+				return decrypted.join('');
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.btns_two {
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		padding: 0 40rpx;
+		margin-top: 50rpx;
+
+		.btn2 {
+			width: 47%;
+		}
+	}
+
+	.cont_led {
+		max-height: 700rpx;
+		overflow-y: auto;
+	}
+
+	.popup_pwd {
+		width: 680rpx;
+		box-sizing: border-box;
+		padding: 0 40rpx 50rpx;
+
+		.led_item {
+			display: flex;
+			align-items: center;
+			justify-content: space-between;
+			padding: 15rpx 0;
+			.led_item_text{
+				width: 100%;
+				padding:0 20rpx;
+			}
+			.name {
+				font-size: 30rpx;
+				color: #333;
+				line-height: 50rpx;
+			}
+			.text{
+				font-size: 28rpx;
+				color: #666;
+				line-height: 50rpx;
+			}
+
+			.btn {
+				flex-shrink: 0;
+				width: 120rpx;
+			}
+		}
+
+		.title {
+			text-align: center;
+			font-weight: 500;
+			font-size: 32rpx;
+			color: rgba(0, 0, 0, 0.9);
+			line-height: 42rpx;
+			padding: 50rpx 0 40rpx;
+		}
+
+		.btns {
+			margin-top: 40rpx;
+		}
+	}
+
+	.device_list {
+		padding: 0 30rpx;
+
+		.device_item {
+			background: linear-gradient(180deg, #FFFFFF 0%, rgba(255, 255, 255, 0.5) 100%);
+			box-shadow: 8rpx 8rpx 23rpx 0rpx rgba(67, 147, 248, 0.1), -8rpx -8rpx 23rpx 0rpx rgba(255, 255, 255, 0.4);
+			border-radius: 20rpx;
+			border: 2rpx solid #FFFFFF;
+			padding: 22rpx;
+			margin-top: 24rpx;
+			position: relative;
+
+			.item_btn {
+				display: flex;
+				align-items: center;
+				justify-content: flex-end;
+				margin-top: 20rpx;
+				border-top: 1rpx dashed #ddd;
+				padding-top: 22rpx;
+				gap: 20rpx;
+			}
+
+			.item {
+				display: flex;
+				align-items: stretch;
+				justify-content: space-between;
+				position: relative;
+			}
+
+			.ph {
+				display: block;
+				flex-shrink: 0;
+				width: 230rpx;
+				height: 170rpx;
+				border-radius: 20rpx;
+				margin-right: 24rpx;
+			}
+
+			.info {
+				width: 100%;
+				display: flex;
+				flex-direction: column;
+				justify-content: space-between;
+				position: relative;
+
+				.name {
+					font-weight: 500;
+					font-size: 27rpx;
+					color: #000000;
+					line-height: 50rpx;
+					box-sizing: border-box;
+					padding-right: 80rpx;
+				}
+
+				.version {
+					font-size: 26rpx;
+					color: rgba(0, 0, 0, 0.7);
+					line-height: 40rpx;
+				}
+
+				.power {
+					font-size: 26rpx;
+					color: rgba(0, 0, 0, 0.7);
+					line-height: 40rpx;
+					height: 40rpx;
+				}
+
+				.state {
+					position: absolute;
+					top: 10rpx;
+					right: 0;
+					font-size: 22rpx;
+					color: rgba(0, 0, 0, 0.7);
+					line-height: 38rpx;
+
+					&.green {
+						color: #2BA471;
+					}
+				}
+
+				.btn {
+					position: absolute;
+					right: 0;
+					bottom: 0;
+					display: flex;
+					align-items: center;
+					justify-content: end;
+
+					.btn_none {
+						flex-shrink: 0;
+						width: 20rpx;
+					}
+				}
+			}
+		}
+	}
+
+	.add_device_small {
+		margin: 30rpx auto 0;
+		background: rgba(255, 255, 255, 0.95) url('/static/img/bg_1.png') no-repeat center bottom;
+		background-size: 100% auto;
+		box-shadow: 8rpx 8rpx 23rpx 0rpx rgba(67, 147, 248, 0.2), -8rpx -8rpx 23rpx 0rpx rgba(255, 255, 255, 0.4);
+		border-radius: 23rpx 23rpx 23rpx 23rpx;
+		border: 2rpx solid #FFFFFF;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		padding: 20rpx 30rpx 10rpx;
+
+		.ico_add {
+			width: 100rpx;
+		}
+
+		.name {
+			font-weight: 400;
+			font-size: 31rpx;
+			color: rgba(0, 0, 0, 0.7);
+			line-height: 40rpx;
+			margin-bottom: 10rpx;
+			text-align: center;
+		}
+	}
+
+	.add_device {
+		margin: 60rpx auto 0;
+		width: 688rpx;
+		height: 319rpx;
+		background: rgba(255, 255, 255, 0.95) url('/static/img/bg_1.png') no-repeat center bottom;
+		background-size: 100% auto;
+		border-radius: 23rpx 23rpx 23rpx 23rpx;
+		border: 2rpx solid #FFFFFF;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		flex-direction: column;
+
+		.ico_add {
+			width: 220rpx;
+		}
+
+		.name {
+			font-weight: 400;
+			font-size: 31rpx;
+			color: rgba(0, 0, 0, 0.7);
+			line-height: 40rpx;
+			margin-bottom: 10rpx;
+			text-align: center;
+		}
+	}
+
+	.hi {
+		text-align: center;
+		color: #fff;
+		font-weight: 500;
+		font-size: 42rpx;
+		line-height: 42rpx;
+		padding: 40rpx 0 0;
+	}
+
+	.page_bg {
+		width: 100%;
+		min-height: calc(100vh - var(--window-bottom));
+		background: linear-gradient(3deg, rgba(114, 229, 255, 0) 14%, rgba(114, 229, 255, 0.2) 33%, rgba(114, 229, 255, 0.4) 48%, #72E5FF 62%, #3B74EE 100%);
+	}
+
+	.page_bg1 {
+		width: 100%;
+		min-height: calc(100vh - var(--window-bottom));
+		background: url('/static/img/bg_2.jpg') no-repeat center top;
+		background-size: 100% auto;
+	}
+</style>
\ No newline at end of file
diff --git a/ledApp/pages/invite/invite.vue b/ledApp/pages/invite/invite.vue
new file mode 100644
index 0000000..cbd2614
--- /dev/null
+++ b/ledApp/pages/invite/invite.vue
@@ -0,0 +1,96 @@
+<template>
+	<view class="page_bg">
+		<uv-navbar title="邀请好友" :autoBack="true" :placeholder="true" leftIconColor="#fff" bgColor="rgba(0,0,0,0)"
+			titleStyle="color:#fff"></uv-navbar>
+
+		<view class="invite">
+			<view class="name">
+				app下载链接
+			</view>
+			<view class="link">
+				{{appDown}}
+			</view>
+			<view class="btn">
+				<uv-button type="primary" text="复制" :plain="true" size="small" @click="copyText"></uv-button>
+			</view>
+		</view>
+
+
+	</view>
+</template>
+
+<script>
+	import {
+		appDownload
+	} from '@/api/api.js'
+	export default {
+		data() {
+			return {
+				appDown: ''
+			}
+		},
+		onLoad() {
+			this.getAppDownload()
+		},
+		methods: {
+			getAppDownload() {
+				appDownload().then(res => {
+					console.log(res)
+					this.appDown = res.msg
+				})
+			},
+			copyText() {
+				uni.setClipboardData({
+					data: this.appDown,
+					success: () => {
+						uni.showToast({
+							title: '复制成功',
+							icon: 'none'
+						})
+					}
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.invite {
+		margin: 30rpx;
+		background: linear-gradient(180deg, #FFFFFF 0%, rgba(255, 255, 255, 0.5) 100%);
+		box-shadow: 8rpx 8rpx 23rpx 0rpx rgba(67, 147, 248, 0.1), -8rpx -8rpx 23rpx 0rpx rgba(255, 255, 255, 0.4);
+		border-radius: 20rpx;
+		border: 2rpx solid #FFFFFF;
+		box-sizing: border-box;
+		padding: 30rpx;
+		position: relative;
+
+		.name {
+			font-weight: 500;
+			font-size: 27rpx;
+			color: rgba(0, 0, 0, 0.9);
+			line-height: 46rpx;
+		}
+
+		.link {
+			font-size: 27rpx;
+			color: rgba(0, 0, 0, 0.7);
+			line-height: 46rpx;
+			width: 500%;
+			margin-top: 20rpx;
+		}
+
+		.btn {
+			position: absolute;
+			right: 30rpx;
+			bottom: 30rpx;
+		}
+	}
+
+	.page_bg {
+		width: 100%;
+		min-height: calc(100vh - var(--window-bottom));
+		background: url('/static/img/bg_2.jpg') no-repeat center top;
+		background-size: 100% auto;
+	}
+</style>
\ No newline at end of file
diff --git a/ledApp/pages/login/login.vue b/ledApp/pages/login/login.vue
new file mode 100644
index 0000000..785824e
--- /dev/null
+++ b/ledApp/pages/login/login.vue
@@ -0,0 +1,248 @@
+<template>
+	<view class="page_bg">
+		<uv-navbar title=" " :placeholder="true" leftIcon="" bgColor="rgba(0,0,0,0)"
+			titleStyle="color:#fff"></uv-navbar>
+		
+		<view class="login_logo">
+			<image class="ph" src="/static/img/logo.png" mode="widthFix"></image>
+			<view class="name">		
+				欢迎,请登录
+			</view>
+		</view>
+		
+		<view class="register">
+			<view class="item">
+				<uv-input placeholder="请输入手机号/邮箱" border="none" v-model="userName"></uv-input>
+			</view>
+			<view class="item">
+				<uv-input placeholder="请输入密码" :password="isEye" border="none" v-model="password"></uv-input>
+				<view class="eye" @click="isEye = !isEye">
+					<uv-icon v-if="isEye" name="eye-off" color="#666" size="40rpx"></uv-icon>
+					<uv-icon v-else name="eye" color="#666" size="40rpx"></uv-icon>
+				</view>
+			</view>
+			<view class="item_login">
+				<navigator class="link" url="/pages/register/register" hover-class="none">
+					还没有账号?去注册
+				</navigator>
+			</view>
+			<view class="item_agree">
+				<uv-checkbox-group v-model="agree" size="28rpx" labelSize="28rpx">
+					<uv-checkbox activeColor="#268DFF" name="agree" label="我已阅读并同意" labelColor="#666"></uv-checkbox>
+					<navigator class="link" url="/pages/policy/policy?id=1" hover-class="none">《用户协议》</navigator>
+					和
+					<navigator class="link" url="/pages/policy/policy?id=2" hover-class="none">《隐私政策》</navigator>
+				</uv-checkbox-group>
+			</view>
+			<view class="item_btn">
+				<uv-button text="登录" color="linear-gradient( 142deg, #6FD2FF 0%, #268DFF 100%)" shape="circle" size="large" @click="clickLogin()"></uv-button>
+			</view>
+		</view>
+		
+		<!-- 协议同意弹出框 -->
+		<uv-popup ref="popupAgree" round="15" :safeAreaInsetBottom="false">
+			<view class="popup_agree">
+				<view class="title">
+					请阅读并同意以下条款
+				</view>
+				<view class="text">
+					我已阅读并同意
+					<navigator class="link" url="/myPages/agreement/agreement" hover-class="none">《服务协议》</navigator>和
+					<navigator class="link" url="/myPages/privacy/privacy" hover-class="none">《隐私政策》</navigator>
+				</view>
+				<view class="btn">
+					<view class="btn_item">
+						<uv-button type="primary" shape="circle" text="取消" :plain="true" :custom-style="customStyle" @click="clickAgreeClose"></uv-button>
+					</view>
+					<view class="btn_item">
+						<uv-button type="primary" shape="circle" text="同意" color="#268DFF" @click="clickAgreeOk"></uv-button>
+					</view>
+				</view>
+			</view>
+		</uv-popup>
+		
+		
+	</view>
+</template>
+
+<script>
+	import { login,userInfo } from '@/api/api.js'
+	export default {
+		data() {
+			return {
+				userName: '18646195977',
+				password: '123456',
+				agree: [],
+				isEye: true
+			}
+		},
+		onLoad() {
+
+		},
+		methods: {
+			clickAgreeClose(){
+				this.$refs.popupAgree.close();
+			},
+			clickAgreeOk(){
+				this.agree = ['agree']
+				this.$refs.popupAgree.close();
+			},
+			clickLogin(){
+				if(!this.userName){
+					uni.showToast({
+						title: '请输入账号',
+						duration: 2000,
+						icon: 'none'
+					});
+					return
+				}
+				if(!this.password){
+					uni.showToast({
+						title: '请输入密码',
+						duration: 2000,
+						icon: 'none'
+					});
+					return
+				}
+				if(this.agree.length == 0){
+					this.$refs.popupAgree.open();
+					return
+				}
+				uni.showLoading({
+					title: '登陆中'
+				});
+				login({
+					username: this.userName,
+					password: this.password
+				}).then(res=>{
+					console.log('登陆----',res)
+					if(res.code == 200){
+						uni.setStorageSync('token', res.token)
+						userInfo().then(res=>{
+							uni.setStorageSync('userId', res.user.userId)
+							uni.hideLoading();
+							uni.reLaunch({
+								url: '/pages/index/index'
+							});
+						})
+					}
+					else{
+						uni.hideLoading();
+						uni.showToast({
+							title: res.msg,
+							duration: 2000,
+							icon: 'none'
+						});
+					}
+				})
+			},
+			changeAgree(e){
+				//console.log(e.detail.value.length)
+				this.agree = e.detail.value.length
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	.popup_agree{
+		width: 650rpx;
+		box-sizing: border-box;
+		padding:50rpx 40rpx;
+		.title{
+			text-align: center;
+			font-size: 34rpx;
+			color:#000;
+			font-weight: bold;
+			line-height: 50rpx;
+		}
+		.text{
+			color: #333;
+			font-size: 28rpx;
+			line-height: 40rpx;
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			padding:30rpx 0 50rpx;
+			.link{
+				color: #3086F3;
+				font-size: 28rpx;
+				line-height: 40rpx;
+			}
+		}
+		.btn{
+			display: flex;
+			align-items: center;
+			justify-content: space-between;
+			.btn_item{
+				width: 47%;
+			}
+		}
+	}
+	.register{
+		padding:0 60rpx;
+		.item_agree{
+			display: flex;
+			align-items: center;
+			font-size: 27rpx;
+			color: rgba(0,0,0,0.85);
+			line-height: 30rpx;
+			margin-top: 80rpx;
+			.link{
+				font-size: 27rpx;
+				color: #3086F3;
+			}
+			.item_agree_info{
+				display: flex;
+				align-items: center;
+			}
+		}
+		.item_btn{
+			margin-top: 40rpx;
+		}
+		.item{
+			margin-top: 46rpx;
+			height: 100rpx;
+			background: #ECEFF6;
+			border-radius: 23rpx 23rpx 23rpx 23rpx;
+			display: flex;
+			align-items: center;
+			justify-content: space-between;
+			box-sizing: border-box;
+			padding:0 20rpx;
+		}
+		.item_login{
+			padding:20rpx 0;
+			.link{
+				font-size: 27rpx;
+				color: #3086F3;
+				line-height: 40rpx;
+			}
+		}
+	}
+	.login_logo{
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		flex-direction: column;
+		padding-top: 100rpx;
+		.ph{
+			width: 200rpx;
+			height: 200rpx;
+			flex-shrink: 0;
+		}
+		.name{
+			font-weight: 600;
+			font-size: 38rpx;
+			color: rgba(0,0,0,0.65);
+			line-height: 50rpx;
+			margin-top: 30rpx;
+		}
+	}
+	.page_bg {
+		width: 100%;
+		min-height: calc(100vh - var(--window-bottom));
+		background: url('/static/img/bg_2.jpg') no-repeat center top;
+		background-size: 100% auto;
+	}
+</style>
diff --git a/ledApp/pages/my/my.vue b/ledApp/pages/my/my.vue
new file mode 100644
index 0000000..114460b
--- /dev/null
+++ b/ledApp/pages/my/my.vue
@@ -0,0 +1,176 @@
+<template>
+	<view class="page_bg">
+		<uv-navbar title=" " :placeholder="true" leftIcon="" bgColor="rgba(0,0,0,0)" titleStyle="color:#fff"></uv-navbar>
+		
+		<view class="my_info">
+			<image class="ph" :src="baseURL + user.avatar" mode="aspectFill"></image>
+			<view class="info">
+				<view class="name">
+					{{user.nickName}}
+				</view>
+				<view class="text">
+					微信号: {{user.wx||'-'}}
+				</view>
+			</view>
+		</view>
+		
+		<view class="my_nav">
+			<navigator class="item" url="/pages/personal/personal" hover-class="none" :render-link="false">
+				<view class="name">
+					用户信息编辑
+				</view>
+				<uv-icon name="arrow-right" color="rgba(0, 0, 0, 0.40)" size="36rpx"></uv-icon>
+			</navigator>
+			<navigator class="item" url="/pages/invite/invite" hover-class="none" :render-link="false">
+				<view class="name">
+					邀请好友
+				</view>
+				<uv-icon name="arrow-right" color="rgba(0, 0, 0, 0.40)" size="36rpx"></uv-icon>
+			</navigator>
+			<navigator class="item" url="/pages/about/about" hover-class="none" :render-link="false">
+				<view class="name">
+					关于我们
+				</view>
+				<uv-icon name="arrow-right" color="rgba(0, 0, 0, 0.40)" size="36rpx"></uv-icon>
+			</navigator>
+			<navigator class="item" url="/pages/feedback/feedback" hover-class="none" :render-link="false">
+				<view class="name">
+					意见反馈
+				</view>
+				<uv-icon name="arrow-right" color="rgba(0, 0, 0, 0.40)" size="36rpx"></uv-icon>
+			</navigator>
+			<navigator class="item" url="/pages/set/set" hover-class="none" :render-link="false">
+				<view class="name">
+					设置
+				</view>
+				<uv-icon name="arrow-right" color="rgba(0, 0, 0, 0.40)" size="36rpx"></uv-icon>
+			</navigator>
+		</view>
+		
+		<view class="logout">
+			<uv-button type="primary" :hairline="true" :plain="true" text="退出登录" shape="circle" color="#999" @click="postLogout"></uv-button>
+		</view>
+		
+	</view>
+</template>
+
+<script>
+	import { userInfo,logout } from '@/api/api.js'
+	export default {
+		data() {
+			return {
+				baseURL: '',
+				user: {}
+			}
+		},
+		onLoad() {
+			
+		},
+		onShow() {
+			this.baseURL = uni.$uv.http.config.baseURL
+			this.getUserInfo()
+		},
+		methods: {
+			postLogout(){
+				uni.showModal({
+					title: '提示',
+					content: '确定退出登录吗?',
+					success: function (res) {
+						if (res.confirm) {
+							console.log('用户点击确定');
+							logout().then(res=>{
+								console.log('退出----',res)
+								uni.showToast({
+									title: res.msg,
+									duration: 2000,
+									icon: 'none'
+								});
+								uni.removeStorageSync('token');
+								uni.removeStorageSync('userId');
+								uni.reLaunch({
+									url: '/pages/login/login'
+								});
+							})
+						} else if (res.cancel) {
+							console.log('用户点击取消');
+						}
+					}
+				});
+				
+			},
+			getUserInfo(){
+				userInfo().then(res=>{
+					console.log('用户信息----',res)
+					this.user = res.user
+					uni.setStorageSync('userId', res.user.userId)
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+.logout{
+	width: 380rpx;
+	margin: 60rpx auto 0;
+}
+.my_nav{
+	margin-top: 70rpx;
+	padding:0 30rpx;
+	.item{
+		width: 100%;
+		height: 120rpx;
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		box-sizing: border-box;
+		border-bottom: 1rpx solid #ddd;
+		box-sizing: border-box;
+		padding-right: 10rpx;
+		.name{
+			font-size: 30rpx;
+			color: rgba(0,0,0,0.9);
+			line-height: 46rpx;
+		}
+	}
+}
+.my_info{
+	display: flex;
+	align-items: center;
+	justify-content: space-between;
+	padding:20rpx 30rpx 0;
+	.info{
+		width: 100%;
+		.name{
+			font-weight: 500;
+			font-size: 35rpx;
+			color: rgba(0,0,0,0.9);
+			line-height: 48rpx;
+		}
+		.text{
+			font-weight: 400;
+			font-size: 27rpx;
+			color: rgba(0,0,0,0.7);
+			line-height: 46rpx;
+			margin-top: 10rpx;
+		}
+	}
+	.ph{
+		flex-shrink: 0;
+		width: 140rpx;
+		height: 140rpx;
+		border-radius: 50%;
+		border:4rpx solid #fff;
+		margin-right: 30rpx;
+	}
+}
+.page_bg{
+	width: 100%;
+	min-height: calc(100vh - var(--window-bottom));
+	background:url('/static/img/bg_2.jpg') no-repeat center top;
+	background-size: 100% auto;
+}
+</style>
diff --git a/ledApp/pages/personal/personal.vue b/ledApp/pages/personal/personal.vue
new file mode 100644
index 0000000..772472b
--- /dev/null
+++ b/ledApp/pages/personal/personal.vue
@@ -0,0 +1,300 @@
+<template>
+	<view class="page_bg">
+		<uv-navbar title="个人资料" :autoBack="true" :placeholder="true" bgColor="rgba(0,0,0,0)" titleStyle="color:#fff" leftIconColor="#fff"></uv-navbar>
+		
+		<view class="head_ph">
+			<uv-upload :fileList="fileList1" name="1" :maxCount="1" width="200rpx" height="200rpx" :previewFullImage="false"
+				@afterRead="afterRead" @delete="deletePic">
+				<!-- <image class="ico_head" :src="baseURL+avatar" mode="widthFix"
+					style="width: 200rpx;height: 200rpx;"></image> -->
+			</uv-upload>
+			<!-- <image class="camera" src="/static/img/ico_3.png" mode="widthFix"></image> -->
+		</view>
+		
+		<view class="user_info">
+			<view class="item">
+				<view class="name">
+					电话
+				</view>
+				<view class="info">
+					<uv-input placeholder="请输入电话" border="none" v-model="phonenumber" :clearable="true" ></uv-input>
+				</view>
+			</view>
+			<view class="item">
+				<view class="name">
+					密码
+				</view>
+				<view class="info_eye">
+					<uv-input placeholder="请输入密码" :password="isEye" border="none" v-model="password" :clearable="true"></uv-input>
+					<view class="eye" @click="isEye = !isEye">
+						<uv-icon v-if="isEye" name="eye-off" color="#666" size="40rpx"></uv-icon>
+						<uv-icon v-else name="eye" color="#666" size="40rpx"></uv-icon>
+					</view>
+				</view>
+			</view>
+			<view class="item">
+				<view class="name">
+					昵称
+				</view>
+				<view class="info">
+					<uv-input placeholder="请输入昵称" border="none" v-model="nickName" :clearable="true"></uv-input>
+				</view>
+			</view>
+			<view class="item">
+				<view class="name">
+					微信号
+				</view>
+				<view class="info">
+					<uv-input placeholder="请输入微信号" border="none" v-model="wx" :clearable="true"></uv-input>
+				</view>
+			</view>
+			
+		</view>
+		
+		<view class="footer_btn">
+			<uv-button text="确认修改" color="linear-gradient( 142deg, #6FD2FF 0%, #268DFF 100%)" shape="circle" size="large" @click="postUserProfile()"></uv-button>
+		</view>
+		
+		
+		
+	</view>
+</template>
+
+<script>
+	import { userInfo, userProfile } from '@/api/api.js'
+	export default {
+		data() {
+			return {
+				userId: '',
+				userName: '',
+				password: '',
+				nickName: '',
+				wx: '',
+				avatar: '',
+				phonenumber: '',
+				fileList1: [],
+				baseURL: '',
+				agree: 0,
+				isEye: true
+			}
+		},
+		onLoad() {
+			this.baseURL = uni.$uv.http.config.baseURL
+			this.getUserInfo()
+		},
+		methods: {
+			postUserProfile(){
+				if(this.fileList1.length == 0){
+					uni.showToast({
+						title: '请上传头像',
+						duration: 2000,
+						icon: 'none'
+					});
+					return
+				}
+				// if(!this.avatar){
+				// 	uni.showToast({
+				// 		title: '请上传头像',
+				// 		duration: 2000,
+				// 		icon: 'none'
+				// 	});
+				// 	return
+				// }
+				if(!this.phonenumber){
+					uni.showToast({
+						title: '请输入手机号码',
+						duration: 2000,
+						icon: 'none'
+					});
+					return
+				}
+				if (!/^1[3-9]\d{9}$/.test(this.phonenumber)) {
+					uni.showToast({
+						title: '手机号码格式不正确',
+						icon: 'none'
+					});
+					return
+				}
+				if(!this.password){
+					uni.showToast({
+						title: '请输入密码',
+						duration: 2000,
+						icon: 'none'
+					});
+					return
+				}
+				if(!this.nickName){
+					uni.showToast({
+						title: '请输入昵称',
+						duration: 2000,
+						icon: 'none'
+					});
+					return
+				}
+				if(!this.wx){
+					uni.showToast({
+						title: '请输入微信号',
+						duration: 2000,
+						icon: 'none'
+					});
+					return
+				}
+				
+				userProfile({
+					userId: this.userId,
+					phonenumber: this.phonenumber,
+					password: this.password,
+					nickName: this.nickName,
+					wx: this.wx,
+					avatar: this.avatar
+				}).then(res=>{
+					console.log('修改用户信息---',res)
+					uni.showToast({
+						title: res.msg,
+						duration: 2000,
+						icon: 'none'
+					});
+					this.getUserInfo()
+					if(res.code == 200){
+						uni.navigateBack({
+							delta: 1
+						});
+					}
+				})
+			},
+			getUserInfo(){
+				userInfo().then(res=>{
+					console.log(res.user)
+					this.fileList1 = []
+					this.userId = res.user.userId
+					this.phonenumber = res.user.phonenumber
+					this.password = res.user.password
+					this.nickName = res.user.nickName
+					this.wx = res.user.wx
+					this.avatar = res.user.avatar
+					this.fileList1.push({url:this.baseURL+res.user.avatar})
+				})
+			},
+			// 删除图片
+			deletePic(event) {
+				this[`fileList${event.name}`].splice(event.index, 1)
+			},
+			// 新增图片
+			async afterRead(event) {
+				console.log('event---',event)
+				// 当设置 multiple 为 true 时, file 为数组格式,否则为对象格式
+				let lists = [].concat(event.file)
+				let fileListLen = this[`fileList${event.name}`].length
+				lists.map((item) => {
+					this[`fileList${event.name}`].push({
+						...item,
+						status: 'uploading',
+						message: '上传中'
+					})
+				})
+				for (let i = 0; i < lists.length; i++) {
+					const result = await this.uploadFilePromise(lists[i].url)
+					this.avatar = result
+					let item = this[`fileList${event.name}`][fileListLen]
+					this[`fileList${event.name}`].splice(fileListLen, 1, Object.assign(item, {
+						status: 'success',
+						message: '',
+						url: result
+					}))
+					fileListLen++
+				}
+			},
+			uploadFilePromise(url) {
+				return new Promise((resolve, reject) => {
+					let a = uni.uploadFile({
+						url: this.baseURL+'/common/uploadFileByAnny', // 仅为示例,非真实的接口地址
+						filePath: url,
+						name: 'file',
+						success: (res) => {
+							console.log('图片-----',JSON.parse(res.data).fileName)
+							setTimeout(() => {
+								resolve(JSON.parse(res.data).fileName)
+							}, 1000)
+						}
+					});
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+::v-deep .uv-upload__wrap__preview,::v-deep .uv-upload__button{
+	margin: 0 !important;
+}
+::v-deep .uv-upload__wrap__preview__image{
+	border-radius: 50%;
+}
+.footer_btn{
+	position: fixed;
+	left:0;
+	right: 0;
+	bottom: 0;
+	padding:35rpx;
+}
+.user_info{
+	.item{
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		border-bottom: 1rpx solid #ddd;
+		padding:0 35rpx;
+		height: 120rpx;
+		.name{
+			width: 140rpx;
+			flex-shrink: 0;
+			font-size: 28rpx;
+			color: rgba(0,0,0,0.9);
+			line-height: 46rpx;
+		}
+		.info{
+			width: 100%;
+		}
+		.info_eye{
+			width: 100%;
+			display: flex;
+			align-items: center;
+			justify-content: space-between;
+			.eye{
+				flex-shrink: 0;
+				margin-left: 20rpx;
+			}
+		}
+	}
+}
+.head_ph {
+	flex-shrink: 0;
+	width: 200rpx;
+	margin: 50rpx auto;
+	position: relative;
+	.camera{
+		position: absolute;
+		right: 0;
+		bottom: 0;
+		width: 50rpx;
+		z-index: 9;
+	}
+	.name{
+		text-align: center;
+		font-size: 28rpx;
+		line-height: 40rpx;
+		margin-top: 20rpx;
+	}
+	.ico_head {
+		width: 200rpx;
+		height: 200rpx;
+		border-radius: 50%;
+	}
+}
+.page_bg{
+	width: 100%;
+	min-height: calc(100vh - var(--window-bottom));
+	background:url('/static/img/bg_2.jpg') no-repeat center top;
+	background-size: 100% auto;
+}
+</style>
diff --git a/ledApp/pages/policy/policy.vue b/ledApp/pages/policy/policy.vue
new file mode 100644
index 0000000..d6e8a2d
--- /dev/null
+++ b/ledApp/pages/policy/policy.vue
@@ -0,0 +1,63 @@
+<template>
+	<view class="page_bg">
+		<uv-navbar :title="title" :autoBack="true" :placeholder="true" leftIconColor="#fff" bgColor="rgba(0,0,0,0)" titleStyle="color:#fff"></uv-navbar>
+		
+		<view class="policy" v-if="id == 1">
+			请仔细阅读以下条款,为获得聚合包装提供的服务,服务使用人(以下称“用户”)应当审慎阅读、充分理解各条款内容,并按照页面上的提示完成全部的注册程序。用户在进行注册程序过程中点击“同意”按钮即表示用户完全接受本协议项下的全部条款。
+			请仔细阅读以下条款,为获得聚合包装提供的服务,服务使用人(以下称“用户”)应当审慎阅读、充分理解各条款内容,并按照页面上的提示完成全部的注册程序。用户在进行注册程序过程中点击“同意”按钮即表示用户完全接受本协议项下的全部条款。
+		</view>
+		
+		<view class="policy" v-if="id == 2">
+			<uv-parse :content="infoData"></uv-parse>
+		</view>
+		
+	</view>
+</template>
+
+<script>
+	import { content } from '@/api/api.js'
+	export default {
+		data() {
+			return {
+				infoData: '',
+				id: 0,
+				title: ''
+			}
+		},
+		onLoad(opt) {
+			if(opt.id){
+				this.id = opt.id
+				const titleMap = {
+				    1: '用户协议',
+				    2: '隐私协议'
+				}
+				this.title = titleMap[this.id]
+			}
+			this.getContent()
+		},
+		methods: {
+			getContent(){
+				content(1).then(res=>{
+					console.log(res.data.content)
+					this.infoData = res.data.content
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+.page_bg{
+	width: 100%;
+	min-height: calc(100vh - var(--window-bottom));
+	background:url('/static/img/bg_2.jpg') no-repeat center top;
+	background-size: 100% auto;
+}
+.policy{
+	padding:20rpx 30rpx;
+	font-size: 28rpx;
+	line-height: 40rpx;
+	background: #fff;
+	min-height: calc(100vh - var(--window-bottom));
+}
+</style>
diff --git a/ledApp/pages/register/register.vue b/ledApp/pages/register/register.vue
new file mode 100644
index 0000000..f513087
--- /dev/null
+++ b/ledApp/pages/register/register.vue
@@ -0,0 +1,264 @@
+<template>
+	<view class="page_bg">
+		<uv-navbar title=" " :placeholder="true" :autoBack="true" bgColor="rgba(0,0,0,0)"
+			titleStyle="color:#fff"></uv-navbar>
+
+		<view class="head_ph">
+			<uv-upload :fileList="fileList1" name="1" :maxCount="1" width="200rpx" height="200rpx"
+				@afterRead="afterRead" @delete="deletePic">
+				<image class="ico_head" src="/static/img/ico_head.png" mode="widthFix"
+					style="width: 200rpx;height: 200rpx;"></image>
+			</uv-upload>
+			<view class="name">
+				上传头像
+			</view>
+		</view>
+
+		<view class="register">
+			<view class="item">
+				<uv-input placeholder="请输入手机号/邮箱" border="none" v-model="userName"></uv-input>
+			</view>
+			<view class="item">
+				<uv-input placeholder="请输入密码" :password="isEye" border="none" v-model="password"></uv-input>
+				<view class="eye" @click="isEye = !isEye">
+					<uv-icon v-if="isEye" name="eye-off" color="#666" size="40rpx"></uv-icon>
+					<uv-icon v-else name="eye" color="#666" size="40rpx"></uv-icon>
+				</view>
+			</view>
+			<view class="item">
+				<uv-input placeholder="请输入昵称" border="none" v-model="nickName"></uv-input>
+			</view>
+			<view class="item">
+				<uv-input placeholder="请输入微信号" border="none" v-model="wx"></uv-input>
+			</view>
+
+			<view class="item_login">
+				<navigator class="link" url="/pages/login/login" hover-class="none">
+					已有账号?去登录
+				</navigator>
+			</view>
+			<view class="item_agree">
+				<checkbox-group @change="changeAgree">
+					<label class="item_agree_info">
+						<checkbox value="agree" :checked="false" style="transform:scale(0.75)" ></checkbox>
+						同意本应用
+						<navigator class="link" url="/pages/policy/policy?id=1" hover-class="none">《用户协议》</navigator>
+						和
+						<navigator class="link" url="/pages/policy/policy?id=2" hover-class="none">《隐私政策》</navigator>
+					</label>
+				</checkbox-group>
+			</view>
+			<view class="item_btn">
+				<uv-button text="注册" color="linear-gradient( 142deg, #6FD2FF 0%, #268DFF 100%)" shape="circle" size="large" @click="clickRegister()"></uv-button>
+			</view>
+		</view>
+
+	</view>
+</template>
+
+<script>
+	import { register } from '@/api/api.js'
+	export default {
+		data() {
+			return {
+				userName: '',
+				password: '',
+				nickName: '',
+				wx: '',
+				avatar: '',
+				fileList1: [],
+				baseURL: '',
+				agree: 0,
+				isEye: true
+			}
+		},
+		onLoad() {
+			this.baseURL = uni.$uv.http.config.baseURL
+			console.log(this.baseURL)
+			
+		},
+		methods: {
+			clickRegister(){
+				if(this.fileList1.length == 0){
+					uni.showToast({
+						title: '请上传头像',
+						duration: 2000,
+						icon: 'none'
+					});
+					return
+				}
+				if(!this.userName){
+					uni.showToast({
+						title: '请输入账号',
+						duration: 2000,
+						icon: 'none'
+					});
+					return
+				}
+				if(!this.password){
+					uni.showToast({
+						title: '请输入密码',
+						duration: 2000,
+						icon: 'none'
+					});
+					return
+				}
+				if(!this.nickName){
+					uni.showToast({
+						title: '请输入昵称',
+						duration: 2000,
+						icon: 'none'
+					});
+					return
+				}
+				if(!this.agree){
+					uni.showToast({
+						title: '请勾选用户协议和隐私政策',
+						duration: 2000,
+						icon: 'none'
+					});
+					return
+				}
+				
+				register({
+					userName: this.userName,
+					password: this.password,
+					nickName: this.nickName,
+					wx: this.wx,
+					avatar: this.fileList1[0].url,
+				}).then(res=>{
+					console.log('注册----',res)
+					uni.showToast({
+						title: res.msg,
+						duration: 2000,
+						icon: 'none'
+					});
+					if(res.code == 200){
+						uni.reLaunch({
+							url: '/pages/login/login'
+						});
+					}
+					else{
+						
+					}
+				})
+			},
+			changeAgree(e){
+				//console.log(e.detail.value.length)
+				this.agree = e.detail.value.length
+			},
+			// 删除图片
+			deletePic(event) {
+				this[`fileList${event.name}`].splice(event.index, 1)
+			},
+			// 新增图片
+			async afterRead(event) {
+				console.log('event---',event)
+				// 当设置 multiple 为 true 时, file 为数组格式,否则为对象格式
+				let lists = [].concat(event.file)
+				let fileListLen = this[`fileList${event.name}`].length
+				lists.map((item) => {
+					this[`fileList${event.name}`].push({
+						...item,
+						status: 'uploading',
+						message: '上传中'
+					})
+				})
+				for (let i = 0; i < lists.length; i++) {
+					const result = await this.uploadFilePromise(lists[i].url)
+					let item = this[`fileList${event.name}`][fileListLen]
+					this[`fileList${event.name}`].splice(fileListLen, 1, Object.assign(item, {
+						status: 'success',
+						message: '',
+						url: result
+					}))
+					fileListLen++
+				}
+			},
+			uploadFilePromise(url) {
+				return new Promise((resolve, reject) => {
+					let a = uni.uploadFile({
+						url: this.baseURL+'/common/uploadFileByAnny', // 仅为示例,非真实的接口地址
+						filePath: url,
+						name: 'file',
+						success: (res) => {
+							console.log('图片-----',JSON.parse(res.data).fileName)
+							setTimeout(() => {
+								resolve(JSON.parse(res.data).fileName)
+							}, 1000)
+						}
+					});
+				})
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	
+	.register{
+		padding:0 60rpx;
+		.item_agree{
+			display: flex;
+			align-items: center;
+			font-size: 27rpx;
+			color: rgba(0,0,0,0.85);
+			line-height: 30rpx;
+			margin-top: 80rpx;
+			.link{
+				font-size: 27rpx;
+				color: #3086F3;
+			}
+			.item_agree_info{
+				display: flex;
+				align-items: center;
+			}
+		}
+		.item_btn{
+			margin-top: 40rpx;
+		}
+		.item{
+			margin-top: 46rpx;
+			height: 100rpx;
+			background: #ECEFF6;
+			border-radius: 23rpx 23rpx 23rpx 23rpx;
+			display: flex;
+			align-items: center;
+			justify-content: space-between;
+			box-sizing: border-box;
+			padding:0 20rpx;
+		}
+		.item_login{
+			padding:20rpx 0;
+			.link{
+				font-size: 27rpx;
+				color: #3086F3;
+				line-height: 40rpx;
+			}
+		}
+	}
+	.head_ph {
+		flex-shrink: 0;
+		width: 200rpx;
+		margin: 50rpx auto;
+		.name{
+			text-align: center;
+			font-size: 28rpx;
+			line-height: 40rpx;
+			margin-top: 20rpx;
+		}
+		.ico_head {
+			width: 200rpx;
+			height: 200rpx;
+			border-radius: 50%;
+			
+		}
+	}
+
+	.page_bg {
+		width: 100%;
+		min-height: calc(100vh - var(--window-bottom));
+		background: url('/static/img/bg_2.jpg') no-repeat center top;
+		background-size: 100% auto;
+	}
+</style>
\ No newline at end of file
diff --git a/ledApp/pages/set/set.vue b/ledApp/pages/set/set.vue
new file mode 100644
index 0000000..9ebddc6
--- /dev/null
+++ b/ledApp/pages/set/set.vue
@@ -0,0 +1,129 @@
+<template>
+	<view class="page_bg">
+		<uv-navbar title="设置" :autoBack="true" :placeholder="true" leftIconColor="#fff" bgColor="rgba(0,0,0,0)" titleStyle="color:#fff"></uv-navbar>
+		
+		<view class="set_nav">
+			<navigator class="item" url="/pages/policy/policy?id=1" hover-class="none" :render-link="false">
+				<view class="name">
+					用户协议
+				</view>
+				<uv-icon name="arrow-right" color="rgba(0, 0, 0, 0.40)" size="36rpx"></uv-icon>
+			</navigator>
+			<navigator class="item" url="/pages/policy/policy?id=2" hover-class="none" :render-link="false">
+				<view class="name">
+					隐私政策
+				</view>
+				<uv-icon name="arrow-right" color="rgba(0, 0, 0, 0.40)" size="36rpx"></uv-icon>
+			</navigator>
+			<navigator class="item" url="#" hover-class="none" :render-link="false">
+				<view class="name">
+					APP升级
+				</view>
+				<uv-icon name="arrow-right" color="rgba(0, 0, 0, 0.40)" size="36rpx"></uv-icon>
+			</navigator>
+		</view>
+		<view class="set_btn" @click="postUnRegistry">
+			注销用户
+		</view>
+		
+		<view class="foot_tips">
+			APP版本:V1.2
+		</view>
+		
+	</view>
+</template>
+
+<script>
+	import { unRegistry } from '@/api/api.js'
+	export default {
+		data() {
+			return {
+				
+			}
+		},
+		onLoad() {
+
+		},
+		methods: {
+			postUnRegistry(){
+				uni.showModal({
+					title: '提示',
+					content: '确定注销用户吗?',
+					success: function (res) {
+						if (res.confirm) {
+							console.log('用户点击确定');
+							unRegistry().then(res=>{
+								console.log('注销----',res)
+								uni.showToast({
+									title: res.msg,
+									duration: 2000,
+									icon: 'none'
+								});
+								uni.removeStorageSync('token');
+								uni.removeStorageSync('userId');
+								uni.reLaunch({
+									url: '/pages/login/login'
+								});
+							})
+						} else if (res.cancel) {
+							console.log('用户点击取消');
+						}
+					}
+				});
+				
+			},
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+.set_btn{
+	width: 386rpx;
+	font-size: 27rpx;
+	color: rgba(0,0,0,0.9);
+	line-height: 96rpx;
+	height: 96rpx;
+	background: #FFFFFF;
+	border-radius: 385rpx 385rpx 385rpx 385rpx;
+	border: 2rpx solid rgba(0,0,0,0.15);
+	text-align: center;
+	margin: 60rpx auto;
+}
+.set_nav{
+	padding:0 30rpx;
+	.item{
+		height: 138rpx;
+		background: linear-gradient( 180deg, #FFFFFF 0%, rgba(255,255,255,0.5) 100%);
+		box-shadow: 8rpx 8rpx 23rpx 0rpx rgba(67,147,248,0.1), -8rpx -8rpx 23rpx 0rpx rgba(255,255,255,0.4);
+		border-radius: 23rpx 23rpx 23rpx 23rpx;
+		border: 2rpx solid #FFFFFF;
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		padding:0 30rpx;
+		margin-top: 24rpx;
+		.name{
+			font-weight: 500;
+			font-size: 27rpx;
+			color: rgba(0,0,0,0.9);
+			line-height: 46rpx;
+		}
+	}
+}
+.foot_tips{
+	position: fixed;
+	left: 0;
+	bottom: 100rpx;
+	right: 0;
+	text-align: center;
+	font-size: 30rpx;
+	color: rgba(0,0,0,0.7);
+	line-height: 38rpx;
+}
+.page_bg{
+	width: 100%;
+	min-height: calc(100vh - var(--window-bottom));
+	background:url('/static/img/bg_2.jpg') no-repeat center top;
+	background-size: 100% auto;
+}
+</style>
diff --git a/ledApp/pages/template/template.vue b/ledApp/pages/template/template.vue
new file mode 100644
index 0000000..735921d
--- /dev/null
+++ b/ledApp/pages/template/template.vue
@@ -0,0 +1,183 @@
+<template>
+	<view class="page_bg">
+		<uv-navbar title="模版" :placeholder="true" leftIcon="" bgColor="rgba(0,0,0,0)" titleStyle="color:#fff"></uv-navbar>
+		
+		<view class="temp_add" @click="gotoTemp">
+			<uv-icon name="plus" color="#3086F3" size="30rpx"></uv-icon>
+			<view class="name">
+				新增动画
+			</view>
+		</view>
+		
+		<view class="temp_list" v-if="listData.length > 0">
+			<view class="item" v-for="(item,index) in listData" :key="index">
+				<view class="pic" >
+					<uv-image :src="baseURL+item.pic" width="200rpx" height="150rpx" :radius="5"></uv-image>
+				</view>
+				<view class="info">
+					<view class="name">
+						{{item.name}}
+					</view>
+					<view class="text">
+						{{item.screen}}
+					</view>
+				</view>
+				<view class="btn">
+					<uv-button type="primary" :plain="true" shape="circle" text="编辑" size="small" @click="clickTempleteEdit(item.id)"></uv-button>
+					<uv-button type="primary" :plain="true" shape="circle" text="删除" size="small" custom-style="marginTop:20rpx" @click="clickTempleteDel(item.id)"></uv-button>
+				</view>
+				<!-- <view class="btn">
+					<uv-button type="primary" shape="circle" text="编辑" size="small" @click="gotoTemp"></uv-button>
+				</view> -->
+			</view>
+		</view>
+		<uv-empty v-if="listData.length == 0" mode="list" marginTop="300rpx" iconColor="#eee" textColor="#ddd"></uv-empty>
+
+		
+	</view>
+</template>
+
+<script>
+	import { templeteList,templeteDel } from '@/api/api.js'
+	export default {
+		data() {
+			return {
+				userId: '',
+				listData: [],
+				baseURL: '',
+			}
+		},
+		onLoad() {
+			this.baseURL = uni.$uv.http.config.baseURL
+		},
+		onShow() {
+			this.userId = uni.getStorageSync('userId');
+			this.getTempleteList()
+		},
+		methods: {
+			// 编辑
+			clickTempleteEdit(id){
+				uni.navigateTo({
+					url: '/pages/addTemplate/addTemplate?id='+id
+				});
+			},
+			// 删除
+			clickTempleteDel(id){
+				uni.showModal({
+					title: '提示',
+					content: '删除后无法恢复,确定删除吗?',
+					success: (resa) => {
+						if (resa.confirm) {
+							console.log('用户点击确定');
+							templeteDel(id).then(res=>{
+								console.log(res)
+								uni.showToast({
+									title: res.msg,
+									duration: 2000,
+									icon: 'none'
+								});
+								if(res.code == 200){
+									this.getTempleteList()
+								}
+							})
+						} else if (resa.cancel) {
+							console.log('用户点击取消');
+						}
+					}
+				});
+				
+			},
+			getTempleteList(){
+				templeteList({
+					userId: this.userId,
+					pageNum: 1,
+					pageSize: 99
+				}).then(res=>{
+					console.log('模版list-----------',res)
+					if(res.code == 200){
+						this.listData = res.rows
+					}
+					else{
+						uni.showToast({
+							title: res.msg,
+							duration: 2000,
+							icon: 'none'
+						});
+					}
+				})
+			},
+			gotoTemp(){
+				uni.navigateTo({
+					url: '/pages/addTemplate/addTemplate'
+				});
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+.temp_list{
+	padding:0 30rpx;
+	.item{
+		display: flex;
+		align-items: center;
+		justify-content: space-between;
+		min-height: 160rpx;
+		background: linear-gradient( 180deg, #FFFFFF 0%, rgba(255,255,255,0.5) 100%);
+		box-shadow: 8rpx 8rpx 23rpx 0rpx rgba(67,147,248,0.1), -8rpx -8rpx 23rpx 0rpx rgba(255,255,255,0.4);
+		border-radius: 20rpx;
+		border: 2rpx solid #FFFFFF;
+		margin-bottom: 20rpx;
+		box-sizing: border-box;
+		padding:15px 30rpx;
+		.pic{
+			width: 200rpx;
+			flex-shrink: 0;
+			margin-right: 25rpx;
+		}
+		.info{
+			width: 100%;
+			.name{
+				font-weight: 500;
+				font-size: 28rpx;
+				color: rgba(0,0,0,0.9);
+				line-height: 40rpx;
+			}
+			.text{
+				font-weight: 600;
+				font-size: 27rpx;
+				color: rgba(0,0,0,0.7);
+				line-height: 40rpx;
+				margin-top: 10rpx;
+			}
+		}
+		.btn{
+			flex-shrink: 0;
+			margin-left: 20rpx;
+		}
+	}
+}
+.temp_add{
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	margin:10rpx 30rpx 20rpx;
+	height: 90rpx;
+	background: #FFFFFF;
+	box-shadow: 0rpx 12rpx 27rpx 0rpx rgba(56,151,243,0.38);
+	border-radius: 20rpx;
+	border: 2rpx solid #3086F3;
+	.name{
+		font-size: 28rpx;
+		color: #3086F3;
+		line-height: 42rpx;
+		margin-left: 6rpx;
+	}
+}
+.page_bg{
+	width: 100%;
+	height: calc(100vh - var(--window-bottom));
+	background:url('/static/img/bg_2.jpg') no-repeat center top;
+	background-size: 100% auto;
+}
+</style>
diff --git a/ledApp/static/img/bg_1.png b/ledApp/static/img/bg_1.png
new file mode 100644
index 0000000..a7329b8
--- /dev/null
+++ b/ledApp/static/img/bg_1.png
Binary files differ
diff --git a/ledApp/static/img/bg_2.jpg b/ledApp/static/img/bg_2.jpg
new file mode 100644
index 0000000..27a3b23
--- /dev/null
+++ b/ledApp/static/img/bg_2.jpg
Binary files differ
diff --git a/ledApp/static/img/bg_2.png b/ledApp/static/img/bg_2.png
new file mode 100644
index 0000000..31da1b6
--- /dev/null
+++ b/ledApp/static/img/bg_2.png
Binary files differ
diff --git a/ledApp/static/img/bg_3.png b/ledApp/static/img/bg_3.png
new file mode 100644
index 0000000..084040b
--- /dev/null
+++ b/ledApp/static/img/bg_3.png
Binary files differ
diff --git a/ledApp/static/img/bg_4.png b/ledApp/static/img/bg_4.png
new file mode 100644
index 0000000..5eac465
--- /dev/null
+++ b/ledApp/static/img/bg_4.png
Binary files differ
diff --git a/ledApp/static/img/btn_1.png b/ledApp/static/img/btn_1.png
new file mode 100644
index 0000000..b648905
--- /dev/null
+++ b/ledApp/static/img/btn_1.png
Binary files differ
diff --git a/ledApp/static/img/ico_1.png b/ledApp/static/img/ico_1.png
new file mode 100644
index 0000000..1ce8b97
--- /dev/null
+++ b/ledApp/static/img/ico_1.png
Binary files differ
diff --git a/ledApp/static/img/ico_2.png b/ledApp/static/img/ico_2.png
new file mode 100644
index 0000000..d60a16b
--- /dev/null
+++ b/ledApp/static/img/ico_2.png
Binary files differ
diff --git a/ledApp/static/img/ico_3.png b/ledApp/static/img/ico_3.png
new file mode 100644
index 0000000..2e3203b
--- /dev/null
+++ b/ledApp/static/img/ico_3.png
Binary files differ
diff --git a/ledApp/static/img/ico_head.png b/ledApp/static/img/ico_head.png
new file mode 100644
index 0000000..f2f566a
--- /dev/null
+++ b/ledApp/static/img/ico_head.png
Binary files differ
diff --git a/ledApp/static/img/ico_m1.png b/ledApp/static/img/ico_m1.png
new file mode 100644
index 0000000..1254487
--- /dev/null
+++ b/ledApp/static/img/ico_m1.png
Binary files differ
diff --git a/ledApp/static/img/ico_m1a.png b/ledApp/static/img/ico_m1a.png
new file mode 100644
index 0000000..cdc32d8
--- /dev/null
+++ b/ledApp/static/img/ico_m1a.png
Binary files differ
diff --git a/ledApp/static/img/ico_m2.png b/ledApp/static/img/ico_m2.png
new file mode 100644
index 0000000..0c51373
--- /dev/null
+++ b/ledApp/static/img/ico_m2.png
Binary files differ
diff --git a/ledApp/static/img/ico_m2a.png b/ledApp/static/img/ico_m2a.png
new file mode 100644
index 0000000..136d888
--- /dev/null
+++ b/ledApp/static/img/ico_m2a.png
Binary files differ
diff --git a/ledApp/static/img/ico_m3.png b/ledApp/static/img/ico_m3.png
new file mode 100644
index 0000000..a6f4f2b
--- /dev/null
+++ b/ledApp/static/img/ico_m3.png
Binary files differ
diff --git a/ledApp/static/img/ico_m3a.png b/ledApp/static/img/ico_m3a.png
new file mode 100644
index 0000000..c75a5ea
--- /dev/null
+++ b/ledApp/static/img/ico_m3a.png
Binary files differ
diff --git a/ledApp/static/img/logo.png b/ledApp/static/img/logo.png
new file mode 100644
index 0000000..c3666a4
--- /dev/null
+++ b/ledApp/static/img/logo.png
Binary files differ
diff --git a/ledApp/static/img/ph_1.png b/ledApp/static/img/ph_1.png
new file mode 100644
index 0000000..27fb87d
--- /dev/null
+++ b/ledApp/static/img/ph_1.png
Binary files differ
diff --git a/ledApp/static/img/vip_head.jpg b/ledApp/static/img/vip_head.jpg
new file mode 100644
index 0000000..114ffe1
--- /dev/null
+++ b/ledApp/static/img/vip_head.jpg
Binary files differ
diff --git a/ledApp/static/logo.png b/ledApp/static/logo.png
new file mode 100644
index 0000000..b5771e2
--- /dev/null
+++ b/ledApp/static/logo.png
Binary files differ
diff --git a/ledApp/uni.promisify.adaptor.js b/ledApp/uni.promisify.adaptor.js
new file mode 100644
index 0000000..5fec4f3
--- /dev/null
+++ b/ledApp/uni.promisify.adaptor.js
@@ -0,0 +1,13 @@
+uni.addInterceptor({
+  returnValue (res) {
+    if (!(!!res && (typeof res === "object" || typeof res === "function") && typeof res.then === "function")) {
+      return res;
+    }
+    return new Promise((resolve, reject) => {
+      res.then((res) => {
+        if (!res) return resolve(res) 
+        return res[0] ? reject(res[0]) : resolve(res[1])
+      });
+    });
+  },
+});
\ No newline at end of file
diff --git a/ledApp/uni.scss b/ledApp/uni.scss
new file mode 100644
index 0000000..b9249e9
--- /dev/null
+++ b/ledApp/uni.scss
@@ -0,0 +1,76 @@
+/**
+ * 这里是uni-app内置的常用样式变量
+ *
+ * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
+ * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App
+ *
+ */
+
+/**
+ * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
+ *
+ * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
+ */
+
+/* 颜色变量 */
+
+/* 行为相关颜色 */
+$uni-color-primary: #007aff;
+$uni-color-success: #4cd964;
+$uni-color-warning: #f0ad4e;
+$uni-color-error: #dd524d;
+
+/* 文字基本颜色 */
+$uni-text-color:#333;//基本色
+$uni-text-color-inverse:#fff;//反色
+$uni-text-color-grey:#999;//辅助灰色,如加载更多的提示信息
+$uni-text-color-placeholder: #808080;
+$uni-text-color-disable:#c0c0c0;
+
+/* 背景颜色 */
+$uni-bg-color:#ffffff;
+$uni-bg-color-grey:#f8f8f8;
+$uni-bg-color-hover:#f1f1f1;//点击状态颜色
+$uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色
+
+/* 边框颜色 */
+$uni-border-color:#c8c7cc;
+
+/* 尺寸变量 */
+
+/* 文字尺寸 */
+$uni-font-size-sm:12px;
+$uni-font-size-base:14px;
+$uni-font-size-lg:16px;
+
+/* 图片尺寸 */
+$uni-img-size-sm:20px;
+$uni-img-size-base:26px;
+$uni-img-size-lg:40px;
+
+/* Border Radius */
+$uni-border-radius-sm: 2px;
+$uni-border-radius-base: 3px;
+$uni-border-radius-lg: 6px;
+$uni-border-radius-circle: 50%;
+
+/* 水平间距 */
+$uni-spacing-row-sm: 5px;
+$uni-spacing-row-base: 10px;
+$uni-spacing-row-lg: 15px;
+
+/* 垂直间距 */
+$uni-spacing-col-sm: 4px;
+$uni-spacing-col-base: 8px;
+$uni-spacing-col-lg: 12px;
+
+/* 透明度 */
+$uni-opacity-disabled: 0.3; // 组件禁用态的透明度
+
+/* 文章场景相关 */
+$uni-color-title: #2C405A; // 文章标题颜色
+$uni-font-size-title:20px;
+$uni-color-subtitle: #555555; // 二级标题颜色
+$uni-font-size-subtitle:26px;
+$uni-color-paragraph: #3F536E; // 文章段落颜色
+$uni-font-size-paragraph:15px;
diff --git a/ledApp/util/request.js b/ledApp/util/request.js
new file mode 100644
index 0000000..289b402
--- /dev/null
+++ b/ledApp/util/request.js
@@ -0,0 +1,70 @@
+// 此vm参数为页面的实例,可以通过它引用vuex中的变量
+export const Request = (vm)=>{
+    // 初始化请求配置
+    uni.$uv.http.setConfig((config) => {
+        /* config 为默认全局配置*/
+        config.baseURL = 'https://shoujike.shenzhenbenwo.com/prod-api'; /* 根域名 */
+		return config
+    })
+	
+	// 请求拦截
+	uni.$uv.http.interceptors.request.use((config) => { // 可使用async await 做异步操作
+	    // 初始化请求拦截器时,会执行此方法,此时data为undefined,赋予默认{}
+	 //    config.data = config.data || {}
+		// // 根据custom参数中配置的是否需要token,添加对应的请求头
+		// if(config?.custom?.auth) {
+		// 	// 可以在此通过vm引用vuex中的变量,具体值在vm.$store.state中
+		// 	config.header.token = vm.$store.state.userInfo.token
+		// }
+		const token = uni.getStorageSync('token');
+		//console.log('token----',token)
+		if (token) {
+			config.header.Authorization = token; // 设置 Authorization 头部
+		}
+	    return config 
+	}, config => { // 可使用async await 做异步操作
+	    return Promise.reject(config)
+	})
+	
+	// 响应拦截
+	uni.$uv.http.interceptors.response.use((response) => { /* 对响应成功做点什么 可使用async await 做异步操作*/
+		//return response.data
+		const res = response.data
+		const token = uni.getStorageSync('token');
+		//console.log('拦截器---',res)
+		if(res.code == 401){ //判断token 过期 跳转重新登录
+			//ElMessage.error("请先登录!")
+			// uni.showToast({
+			// 	title: res.msg,
+			// 	duration: 2000,
+			// 	icon: 'none'
+			// });
+			uni.removeStorageSync('token');
+			uni.showToast({
+				title: '请重新登录!',
+				duration: 2000,
+				icon: 'none'
+			});
+			uni.reLaunch({
+				url: '/pages/login/login'
+			});
+			// setTimeout(() => {
+			// 	//window.location.href = '/';
+				
+			// }, 1000);
+			return
+		}
+		// if(response.code == 502){
+		// 	uni.showToast({
+		// 		title: '服务器错误,请稍后重试',
+		// 		duration: 2000,
+		// 		icon: 'none'
+		// 	});
+		// 	return
+		// }
+		return res;
+	}, (response) => { 
+		// 对响应错误做点什么 (statusCode !== 200)
+		return Promise.reject(response)
+	})
+}
\ No newline at end of file

--
Gitblit v1.10.0