if (typeof Promise !== "undefined" && !Promise.prototype.finally) { Promise.prototype.finally = function(callback) { const promise = this.constructor; return this.then( (value) => promise.resolve(callback()).then(() => value), (reason) => promise.resolve(callback()).then(() => { throw reason; }) ); }; } ; if (typeof uni !== "undefined" && uni && uni.requireGlobal) { const global = uni.requireGlobal(); ArrayBuffer = global.ArrayBuffer; Int8Array = global.Int8Array; Uint8Array = global.Uint8Array; Uint8ClampedArray = global.Uint8ClampedArray; Int16Array = global.Int16Array; Uint16Array = global.Uint16Array; Int32Array = global.Int32Array; Uint32Array = global.Uint32Array; Float32Array = global.Float32Array; Float64Array = global.Float64Array; BigInt64Array = global.BigInt64Array; BigUint64Array = global.BigUint64Array; } ; if (uni.restoreGlobal) { uni.restoreGlobal(Vue, weex, plus, setTimeout, clearTimeout, setInterval, clearInterval); } (function(vue) { "use strict"; function formatAppLog(type, filename, ...args) { if (uni.__log__) { uni.__log__(type, filename, ...args); } else { console[type].apply(console, [...args, filename]); } } const _export_sfc = (sfc, props) => { const target = sfc.__vccOpts || sfc; for (const [key, val] of props) { target[key] = val; } return target; }; const _sfc_main$2 = { data() { return { foundDevices: [], isScanning: false, bluetoothEnabled: false, connectedDeviceId: "" }; }, onLoad() { this.initBluetooth(); }, onUnload() { this.stopScan(); uni.closeBluetoothAdapter(); uni.offBluetoothDeviceFound(this.onDeviceFound); }, methods: { // 初始化蓝牙适配器 initBluetooth() { uni.openBluetoothAdapter({ success: () => { this.bluetoothEnabled = true; }, fail: (err) => { this.bluetoothEnabled = false; uni.showModal({ title: "蓝牙开启失败", content: "请检查设备蓝牙是否开启", showCancel: false }); formatAppLog("error", "at pages/index/index.vue:88", "蓝牙初始化失败:", err); } }); }, // 切换扫描状态(开始/停止) toggleScan() { if (!this.bluetoothEnabled) { this.initBluetooth(); return; } if (this.isScanning) { this.stopScan(); } else { this.startScan(); } }, // 开始扫描设备 startScan() { this.isScanning = true; this.foundDevices = []; uni.startBluetoothDevicesDiscovery({ services: [], allowDuplicatesKey: false, // 不允许重复上报 success: () => { uni.showToast({ title: "开始扫描", icon: "none" }); uni.onBluetoothDeviceFound(this.onDeviceFound); }, fail: (err) => { this.isScanning = false; uni.showToast({ title: "扫描失败", icon: "none" }); formatAppLog("error", "at pages/index/index.vue:124", "扫描失败:", err); } }); setTimeout(() => { if (this.isScanning) this.stopScan(); }, 5e3); }, // 停止扫描 stopScan() { if (!this.isScanning) return; uni.stopBluetoothDevicesDiscovery({ success: () => { this.isScanning = false; if (this.foundDevices.length == 0) { uni.showToast({ title: `暂未扫描到任何设备`, icon: "none" }); return; } uni.showToast({ title: `扫描完成,发现${this.foundDevices.length}台设备`, icon: "none" }); }, fail: (err) => { formatAppLog("error", "at pages/index/index.vue:154", "停止扫描失败:", err); } }); }, // 设备发现回调(处理去重和数据格式化) onDeviceFound(res) { const devices = res.devices || []; devices.forEach((device) => { if (!device.deviceId) return; const isExist = this.foundDevices.some( (d) => d.deviceId === device.deviceId ); if (isExist) return; var is_bj = false; const advData = new Uint8Array(device.advertisData); const devicenameData = advData.slice(2, 6); const devicename = String.fromCharCode(...devicenameData); if (devicename == "dzbj") is_bj = true; this.foundDevices.push({ is_bj, name: device.name || "未知设备", deviceId: device.deviceId, rssi: device.RSSI, connected: device.deviceId === this.connectedDeviceId }); }); }, // 计算信号强度条宽度(-100dBm为0%,-30dBm为100%) getRssiWidth(rssi) { const minRssi = -100; const maxRssi = -30; let ratio = (rssi - minRssi) / (maxRssi - minRssi); ratio = Math.max(0, Math.min(1, ratio)); return `${ratio * 100}%`; }, connectDevice(device) { var that = this; this.stopScan(); uni.showLoading({ title: "连接中..." }); uni.createBLEConnection({ deviceId: device.deviceId, success(res) { that.foundDevices = that.foundDevices.map((d) => ({ ...d, connected: d.deviceId === device.deviceId })); uni.hideLoading(); uni.showToast({ title: `已连接${device.name}`, icon: "none" }); uni.navigateTo({ url: "../connect/connect?deviceId=" + device.deviceId, fail: (err) => { uni.showToast({ title: `连接失败,请稍后重试`, icon: "error" }); uni.closeBLEConnection({ deviceId: device.deviceId, success() { console("断开连接成功"); } }); } }); }, fail() { uni.hideLoading(); uni.showToast({ title: `连接失败`, icon: "error" }); } }); } } }; function _sfc_render$1(_ctx, _cache, $props, $setup, $data, $options) { return vue.openBlock(), vue.createElementBlock("view", { class: "container" }, [ vue.createElementVNode("view", { class: "title-bar" }, [ vue.createElementVNode("text", { class: "title" }, "连接设备"), vue.createElementVNode("button", { class: "scan-btn", disabled: $data.isScanning, onClick: _cache[0] || (_cache[0] = (...args) => $options.toggleScan && $options.toggleScan(...args)) }, vue.toDisplayString($data.isScanning ? "停止扫描" : "开始扫描"), 9, ["disabled"]) ]), $data.isScanning ? (vue.openBlock(), vue.createElementBlock("view", { key: 0, class: "status-tip" }, [ vue.createElementVNode("text", null, "正在扫描设备..."), vue.createElementVNode("view", { class: "loading" }) ])) : $data.foundDevices.length === 0 ? (vue.openBlock(), vue.createElementBlock("view", { key: 1, class: "status-tip" }, [ vue.createElementVNode("text", null, '未发现设备,请点击"开始扫描"') ])) : vue.createCommentVNode("v-if", true), vue.createElementVNode("view", { class: "device-list" }, [ (vue.openBlock(true), vue.createElementBlock( vue.Fragment, null, vue.renderList($data.foundDevices, (device, index) => { return vue.openBlock(), vue.createElementBlock("view", { class: "device-item", key: device.deviceId, onClick: ($event) => $options.connectDevice(device) }, [ vue.createElementVNode("view", { class: "device-name" }, [ vue.createElementVNode( "text", null, vue.toDisplayString(device.name || "未知设备"), 1 /* TEXT */ ), device.is_bj ? (vue.openBlock(), vue.createElementBlock("view", { key: 0, class: "is_bj" }, "吧唧")) : vue.createCommentVNode("v-if", true), vue.createElementVNode( "text", { class: "device-id" }, vue.toDisplayString(device.deviceId), 1 /* TEXT */ ) ]), vue.createElementVNode("view", { class: "device-rssi" }, [ vue.createElementVNode( "text", null, "信号: " + vue.toDisplayString(device.rssi) + " dBm", 1 /* TEXT */ ), vue.createElementVNode( "view", { class: "rssi-bar", style: vue.normalizeStyle({ width: $options.getRssiWidth(device.rssi) }) }, null, 4 /* STYLE */ ) ]), device.connected ? (vue.openBlock(), vue.createElementBlock("view", { key: 0, class: "device-status" }, [ vue.createElementVNode("text", { class: "connected" }, "已连接") ])) : vue.createCommentVNode("v-if", true) ], 8, ["onClick"]); }), 128 /* KEYED_FRAGMENT */ )) ]) ]); } const PagesIndexIndex = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["render", _sfc_render$1], ["__scopeId", "data-v-1cf27b2a"], ["__file", "/Users/rdzleo/Desktop/uniapp_code/pages/index/index.vue"]]); const _sfc_main$1 = { data() { return { // 图片相关 uploadedImages: [], // 已上传的图片列表 currentImageUrl: "", // 当前选中的图片 currentImageIndex: -1, // 当前选中的图片索引 imageDir: "", // 图片保存目录 // BLE设备数据 isConnected: true, // 是否连接 isScanning: false, // 是否正在扫描 batteryLevel: 0, // 电量 temperature: 0, // 温度 humidity: 0, // 湿度 faceStatus: 0, // 表盘状态 0-正常 1-更新中 2-异常 // statusPotColor:'Green', deviceId: "", imageServiceuuid: "", imageWriteuuid: "", imageEdituuid: "", isSending: false, transferProgress: 0, // 模拟数据定时器 dataTimer: null }; }, computed: { // 表盘状态文本 faceStatusText() { switch (this.faceStatus) { case 0: return "正常"; case 1: return "更新中"; case 2: return "异常"; default: return "未知"; } }, // 电池颜色(根据电量变化) batteryColor() { if (this.batteryLevel > 70) return "#52c41a"; if (this.batteryLevel > 30) return "#faad14"; return "#ff4d4f"; } }, onLoad(options) { this.deviceId = options.deviceId; this.initImageDir(); this.loadSavedImages(); this.setBleMtu(); }, onUnload() { this.stopDataSimulation(); if (this.isConnected) { this.disconnectDevice(); } }, methods: { // BLE 单次写入(内部方法) _bleWriteOnce(characteristicId, buffer, writeType) { return new Promise((resolve, reject) => { uni.writeBLECharacteristicValue({ deviceId: this.deviceId, serviceId: this.imageServiceuuid, characteristicId, value: buffer, writeType: writeType || "writeNoResponse", success: resolve, fail: reject }); }); }, // BLE 写入封装(带重试,writeNoResponse 失败自动降级为 write) async bleWrite(characteristicId, buffer) { const MAX_RETRY = 3; for (let i = 0; i < MAX_RETRY; i++) { try { await this._bleWriteOnce(characteristicId, buffer, "writeNoResponse"); return; } catch (err) { if (i < MAX_RETRY - 1) { await this.delay(20 * (i + 1)); } else { try { await this._bleWriteOnce(characteristicId, buffer, "write"); return; } catch (fallbackErr) { throw fallbackErr; } } } } }, // 延时 delay(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); }, // 读取图片文件为 ArrayBuffer readImageAsArrayBuffer(filePath) { return new Promise((resolve, reject) => { formatAppLog("log", "at pages/connect/connect.vue:266", "readImageAsArrayBuffer 开始, 路径:", filePath); plus.io.resolveLocalFileSystemURL(filePath, (fileEntry) => { formatAppLog("log", "at pages/connect/connect.vue:268", "resolveLocalFileSystemURL 成功"); fileEntry.file((file) => { formatAppLog("log", "at pages/connect/connect.vue:270", "获取File对象成功, 大小:", file.size); const reader = new plus.io.FileReader(); reader.onloadend = (e) => { formatAppLog("log", "at pages/connect/connect.vue:273", "FileReader onloadend, 有数据:", !!e.target.result); if (e.target.result) { resolve(e.target.result); } else { reject(new Error("读取文件结果为空")); } }; reader.onerror = (e) => { formatAppLog("error", "at pages/connect/connect.vue:281", "FileReader onerror:", e); reject(new Error("FileReader错误")); }; reader.readAsDataURL(file); }, (err) => { formatAppLog("error", "at pages/connect/connect.vue:286", "fileEntry.file 失败:", JSON.stringify(err)); reject(err); }); }, (err) => { formatAppLog("error", "at pages/connect/connect.vue:290", "resolveLocalFileSystemURL 失败:", JSON.stringify(err)); reject(err); }); }); }, // DataURL 转 ArrayBuffer dataURLtoArrayBuffer(dataURL) { const base64 = dataURL.split(",")[1]; const binary = atob(base64); const len = binary.length; const buffer = new ArrayBuffer(len); const view = new Uint8Array(buffer); for (let i = 0; i < len; i++) { view[i] = binary.charCodeAt(i); } return buffer; }, // 图片分包传输 async writeBleImage(filename, imageArrayBuffer) { const data = new Uint8Array(imageArrayBuffer); const len = data.length; formatAppLog("log", "at pages/connect/connect.vue:311", "开始传输图片:", filename, "大小:", len, "字节"); const header = new Uint8Array(26); header[0] = 253; for (let i = 0; i < Math.min(filename.length, 22); i++) { header[i + 1] = filename.charCodeAt(i); } header[23] = len >> 16 & 255; header[24] = len >> 8 & 255; header[25] = len & 255; await this.bleWrite(this.imageWriteuuid, header.buffer); await this.delay(50); const CHUNK_SIZE = 507; let offset = 0; let packetNo = 0; while (offset < len) { const remaining = len - offset; const chunkLen = Math.min(CHUNK_SIZE, remaining); const isEnd = offset + chunkLen >= len ? 1 : 0; const packet = new Uint8Array(2 + chunkLen); packet[0] = packetNo & 255; packet[1] = isEnd; packet.set(data.slice(offset, offset + chunkLen), 2); await this.bleWrite(this.imageWriteuuid, packet.buffer); await this.delay(5); offset += chunkLen; packetNo++; if (packetNo % 10 === 0 || isEnd) { this.transferProgress = Math.floor(offset / len * 100); uni.showLoading({ title: "传输中 " + this.transferProgress + "%", mask: true }); } } formatAppLog("log", "at pages/connect/connect.vue:355", "图片传输完成,共", packetNo, "包"); }, // 通过 16-bit UUID 在列表中查找(兼容多种格式) findByUuid16(list, uuid16) { const hex4 = uuid16.toString(16).padStart(4, "0").toLowerCase(); const fullTarget = "0000" + hex4 + "-0000-1000-8000-00805f9b34fb"; return list.find((item) => { const uuid = item.uuid.toLowerCase(); if (uuid === fullTarget) return true; if (uuid === hex4 || uuid === "0000" + hex4) return true; if (uuid.startsWith("0000" + hex4 + "-")) return true; return false; }); }, getBleService() { var that = this; uni.getBLEDeviceServices({ deviceId: that.deviceId, success(res) { formatAppLog("log", "at pages/connect/connect.vue:378", "发现服务数量:", res.services.length); res.services.forEach((s, i) => { formatAppLog("log", "at pages/connect/connect.vue:380", " 服务[" + i + "]:", s.uuid); }); const service = that.findByUuid16(res.services, 2816); if (service) { that.imageServiceuuid = service.uuid; formatAppLog("log", "at pages/connect/connect.vue:385", "图片服务已找到:", service.uuid); that.getBleChar(); } else { formatAppLog("error", "at pages/connect/connect.vue:388", "未找到图片传输服务 0x0B00,请检查上方打印的UUID格式"); uni.showToast({ title: "未找到图片服务", icon: "none" }); } }, fail() { formatAppLog("log", "at pages/connect/connect.vue:393", "获取服务Id失败"); } }); }, getBleChar() { var that = this; uni.getBLEDeviceCharacteristics({ deviceId: that.deviceId, serviceId: that.imageServiceuuid, success(res) { formatAppLog("log", "at pages/connect/connect.vue:403", "发现特征数量:", res.characteristics.length); res.characteristics.forEach((c, i) => { formatAppLog("log", "at pages/connect/connect.vue:405", " 特征[" + i + "]:", c.uuid, "属性:", JSON.stringify(c.properties)); }); const writeChar = that.findByUuid16(res.characteristics, 2817); const editChar = that.findByUuid16(res.characteristics, 2818); if (writeChar) { that.imageWriteuuid = writeChar.uuid; formatAppLog("log", "at pages/connect/connect.vue:411", "写入特征已找到:", writeChar.uuid); } if (editChar) { that.imageEdituuid = editChar.uuid; formatAppLog("log", "at pages/connect/connect.vue:415", "编辑特征已找到:", editChar.uuid); } if (!writeChar || !editChar) { formatAppLog("error", "at pages/connect/connect.vue:418", "特征发现不完整, write:", !!writeChar, "edit:", !!editChar); } }, fail() { formatAppLog("log", "at pages/connect/connect.vue:422", "获取特征Id失败"); } }); }, setBleMtu() { var that = this; uni.setBLEMTU({ deviceId: that.deviceId, mtu: 512, success() { that.isConnected = true; formatAppLog("log", "at pages/connect/connect.vue:434", "MTU设置成功"); that.getBleService(); }, fail() { formatAppLog("log", "at pages/connect/connect.vue:438", "MTU设置失败"); } }); }, // 初始化图片保存目录 initImageDir() { const docPath = plus.io.convertLocalFileSystemURL("_doc/"); this.imageDir = docPath + "watch_faces/"; plus.io.resolveLocalFileSystemURL( this.imageDir, () => { }, () => { plus.io.resolveLocalFileSystemURL("_doc/", (root) => { root.getDirectory("watch_faces", { create: true }, () => { formatAppLog("log", "at pages/connect/connect.vue:453", "目录创建成功"); }); }); } ); }, loadSavedImages() { plus.io.resolveLocalFileSystemURL(this.imageDir, (dir) => { const reader = dir.createReader(); reader.readEntries((entries) => { const images = []; entries.forEach((entry) => { if (entry.isFile && this.isImageFile(entry.name)) { images.push({ name: entry.name, url: entry.toLocalURL() }); } }); this.uploadedImages = images; if (images.length > 0) { this.currentImageUrl = images[0].url; this.currentImageIndex = 0; } }); }); }, // 判断是否为图片文件 isImageFile(filename) { const ext = filename.toLowerCase().split(".").pop(); return ["jpg", "jpeg", "png", "gif", "bmp"].includes(ext); }, // 选择图片(支持 GIF 和普通图片) chooseImage() { uni.chooseImage({ count: 1, sizeType: ["compressed"], sourceType: ["album", "camera"], crop: { quality: 100, width: 360, height: 360, resize: true }, success: (res) => { formatAppLog("log", "at pages/connect/connect.vue:520", res); const tempFilePath = res.tempFilePaths[0]; plus.io.resolveLocalFileSystemURL(tempFilePath, (fileEntry) => { fileEntry.file((file) => { const reader = new FileReader(); reader.onloadend = (e) => { this.imageBuffer = e.target.result; this.log = "图片读取成功,大小:" + this.imageBuffer.byteLength + "字节"; }; reader.readAsArrayBuffer(file); }); }); this.saveImageToLocal(tempFilePath); }, fail: (err) => { formatAppLog("error", "at pages/connect/connect.vue:551", "选择图片失败:", err); uni.showToast({ title: "选择图片失败", icon: "none" }); } }); }, // 选择GIF动图(不裁剪,保留原始动画) chooseGif() { uni.chooseImage({ count: 1, sizeType: ["original"], sourceType: ["album"], success: (res) => { formatAppLog("log", "at pages/connect/connect.vue:564", res); const tempFilePath = res.tempFilePaths[0]; if (!tempFilePath.toLowerCase().endsWith(".gif")) { uni.showToast({ title: "请选择GIF动图文件", icon: "none" }); return; } uni.getImageInfo({ src: tempFilePath, success: (imgInfo) => { formatAppLog("log", "at pages/connect/connect.vue:577", "GIF尺寸:", imgInfo.width, "×", imgInfo.height); const sizeOk = imgInfo.width === 360 && imgInfo.height === 360; const doSave = () => { plus.io.resolveLocalFileSystemURL(tempFilePath, (fileEntry) => { fileEntry.file((file) => { if (file.size > 500 * 1024) { uni.showModal({ title: "文件较大", content: `GIF文件 ${(file.size / 1024).toFixed(0)}KB,设备存储有限,建议不超过500KB。是否继续?`, success: (modalRes) => { if (modalRes.confirm) { this.saveImageToLocal(tempFilePath); } } }); } else { this.saveImageToLocal(tempFilePath); } const reader = new FileReader(); reader.onloadend = (e) => { this.imageBuffer = e.target.result; this.log = "GIF读取成功,大小:" + this.imageBuffer.byteLength + "字节"; }; reader.readAsArrayBuffer(file); }); }); }; if (!sizeOk) { uni.showModal({ title: "GIF尺寸不匹配", content: `当前GIF尺寸为 ${imgInfo.width}×${imgInfo.height},设备屏幕为 360×360。非标准尺寸的GIF播放可能不流畅且无法铺满屏幕,建议使用360×360的GIF动图。是否继续上传?`, confirmText: "继续上传", cancelText: "取消", success: (modalRes) => { if (modalRes.confirm) { doSave(); } } }); } else { doSave(); } }, fail: () => { formatAppLog("warn", "at pages/connect/connect.vue:632", "获取GIF尺寸失败,跳过检测"); plus.io.resolveLocalFileSystemURL(tempFilePath, (fileEntry) => { fileEntry.file((file) => { this.saveImageToLocal(tempFilePath); const reader = new FileReader(); reader.onloadend = (e) => { this.imageBuffer = e.target.result; this.log = "GIF读取成功,大小:" + this.imageBuffer.byteLength + "字节"; }; reader.readAsArrayBuffer(file); }); }); } }); }, fail: (err) => { formatAppLog("error", "at pages/connect/connect.vue:653", "选择GIF失败:", err); uni.showToast({ title: "选择GIF失败", icon: "none" }); } }); }, // 保存图片到本地目录 saveImageToLocal(tempPath) { const isGif = tempPath.toLowerCase().endsWith(".gif"); const fileName = `face_${Date.now()}.${isGif ? "gif" : "jpg"}`; this.imageDir + fileName; plus.io.resolveLocalFileSystemURL(tempPath, (file) => { plus.io.resolveLocalFileSystemURL(this.imageDir, (dir) => { file.copyTo(dir, fileName, (newFile) => { const imageUrl = newFile.toLocalURL(); this.uploadedImages.push({ name: fileName, url: imageUrl }); this.currentImageIndex = this.uploadedImages.length - 1; this.currentImageUrl = imageUrl; uni.showToast({ title: "图片保存成功", icon: "success" }); }, (err) => { formatAppLog("error", "at pages/connect/connect.vue:680", "保存图片失败:", err); uni.showToast({ title: "保存图片失败", icon: "none" }); }); }); }); }, // 选择轮播中的图片 selectImage(index) { this.currentImageIndex = index; this.currentImageUrl = this.uploadedImages[index].url; }, // 轮播图变化时触发 handleCarouselChange(e) { const index = e.detail.current; this.currentImageIndex = index; this.currentImageUrl = this.uploadedImages[index].url; }, // 压缩图片为JPEG格式(设备端只支持JPEG解码) compressToJpeg(srcPath) { return new Promise((resolve, reject) => { uni.compressImage({ src: srcPath, quality: 100, success: (res) => { formatAppLog("log", "at pages/connect/connect.vue:724", "JPEG压缩完成:", res.tempFilePath); resolve(res.tempFilePath); }, fail: (err) => { formatAppLog("error", "at pages/connect/connect.vue:728", "JPEG压缩失败:", err); reject(err); } }); }); }, // 设置为当前表盘(BLE 传输图片到设备,支持 JPEG 和 GIF) async setAsCurrentFace() { if (this.isSending) return; if (this.currentImageIndex < 0 || !this.imageWriteuuid) { uni.showToast({ title: "请先选择图片并等待BLE就绪", icon: "none" }); return; } const currentImage = this.uploadedImages[this.currentImageIndex]; if (!currentImage) return; this.isSending = true; this.faceStatus = 1; this.transferProgress = 0; const isGif = currentImage.name.toLowerCase().endsWith(".gif"); try { let buffer; let bleName; if (isGif) { uni.showLoading({ title: "读取GIF...", mask: true }); const dataURL = await this.readImageAsArrayBuffer(currentImage.url); buffer = this.dataURLtoArrayBuffer(dataURL); bleName = currentImage.name; formatAppLog("log", "at pages/connect/connect.vue:762", "GIF读取完成,大小:", buffer.byteLength, "字节"); if (buffer.byteLength > 200 * 1024) { const sizeKB = (buffer.byteLength / 1024).toFixed(0); uni.showLoading({ title: `传输GIF ${sizeKB}KB...`, mask: true }); } } else { uni.showLoading({ title: "压缩图片...", mask: true }); const jpegPath = await this.compressToJpeg(currentImage.url); uni.showLoading({ title: "读取图片...", mask: true }); const dataURL = await this.readImageAsArrayBuffer(jpegPath); buffer = this.dataURLtoArrayBuffer(dataURL); bleName = currentImage.name.replace(/\.\w+$/, ".jpg"); formatAppLog("log", "at pages/connect/connect.vue:778", "JPEG读取完成,大小:", buffer.byteLength, "字节"); } await this.writeBleImage(bleName, buffer); uni.hideLoading(); this.faceStatus = 0; uni.showToast({ title: "表盘已更新", icon: "success" }); uni.setStorageSync("current_watch_face", this.currentImageUrl); } catch (err) { uni.hideLoading(); this.faceStatus = 2; formatAppLog("error", "at pages/connect/connect.vue:791", "传输失败:", err); uni.showToast({ title: "传输失败: " + (err.errMsg || err.message || err), icon: "none" }); setTimeout(() => { this.faceStatus = 0; }, 3e3); } finally { this.isSending = false; } }, // 切换设备连接状态 toggleConnection() { if (this.isConnected) { this.disconnectDevice(); } else { this.connectDevice(); } }, // 连接设备 connectDevice() { var that = this; this.isScanning = true; uni.showToast({ title: "正在连接设备...", icon: "none" }); uni.createBLEConnection({ deviceId: that.deviceId, success() { that.isScanning = false; that.isConnected = true; uni.showToast({ title: "设备连接成功", icon: "success" }); that.setBleMtu(); that.startDataSimulation(); } }); }, // 断开连接 disconnectDevice() { var that = this; uni.closeBLEConnection({ deviceId: that.deviceId, success() { that.isConnected = false; that.statusPotColor = "red"; uni.showToast({ title: "已断开连接", icon: "none" }); that.stopDataSimulation(); }, fail(res) { if (res == 1e4) { uni.openBluetoothAdapter({ success() { that.isConnected = false; }, fail() { formatAppLog("log", "at pages/connect/connect.vue:844", "初始化失败"); } }); } } }); }, // 开始模拟数据更新 startDataSimulation() { if (this.dataTimer) { clearInterval(this.dataTimer); } this.batteryLevel = Math.floor(Math.random() * 30) + 70; this.temperature = Math.floor(Math.random() * 10) + 20; this.humidity = Math.floor(Math.random() * 30) + 40; this.dataTimer = setInterval(() => { if (this.batteryLevel > 1) { this.batteryLevel = Math.max(1, this.batteryLevel - Math.random() * 2); } this.temperature = Math.max(15, Math.min(35, this.temperature + (Math.random() * 2 - 1))); this.humidity = Math.max(30, Math.min(80, this.humidity + (Math.random() * 4 - 2))); }, 5e3); }, // 停止数据模拟 stopDataSimulation() { if (this.dataTimer) { clearInterval(this.dataTimer); this.dataTimer = null; } } } }; function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) { const _component_uni_icons = vue.resolveComponent("uni-icons"); return vue.openBlock(), vue.createElementBlock("view", { class: "container" }, [ vue.createElementVNode("view", { class: "nav-bar" }, [ vue.createElementVNode("text", { class: "nav-title" }, "表盘管理器") ]), vue.createElementVNode("view", { class: "content" }, [ vue.createElementVNode("view", { class: "preview-container" }, [ vue.createElementVNode("view", { class: "preview-frame" }, [ $data.currentImageUrl ? (vue.openBlock(), vue.createElementBlock("image", { key: 0, src: $data.currentImageUrl, class: "preview-image", mode: "aspectFill" }, null, 8, ["src"])) : (vue.openBlock(), vue.createElementBlock("view", { key: 1, class: "empty-preview" }, [ vue.createVNode(_component_uni_icons, { type: "image", size: "60", color: "#ccc" }), vue.createElementVNode("text", null, "请选择表盘图片") ])) ]), $data.currentImageUrl ? (vue.openBlock(), vue.createElementBlock("button", { key: 0, class: "set-btn", onClick: _cache[0] || (_cache[0] = (...args) => $options.setAsCurrentFace && $options.setAsCurrentFace(...args)), disabled: $data.isSending }, vue.toDisplayString($data.isSending ? "传输中 " + $data.transferProgress + "%" : "设置为当前表盘"), 9, ["disabled"])) : vue.createCommentVNode("v-if", true), vue.createElementVNode("view", { class: "btn-row" }, [ vue.createElementVNode("button", { class: "upload-btn", onClick: _cache[1] || (_cache[1] = (...args) => $options.chooseImage && $options.chooseImage(...args)) }, [ vue.createVNode(_component_uni_icons, { type: "camera", size: "24", color: "#fff" }), vue.createElementVNode("text", null, "上传图片") ]), vue.createElementVNode("button", { class: "upload-btn gif-btn", onClick: _cache[2] || (_cache[2] = (...args) => $options.chooseGif && $options.chooseGif(...args)) }, [ vue.createVNode(_component_uni_icons, { type: "videocam", size: "24", color: "#fff" }), vue.createElementVNode("text", null, "上传GIF") ]) ]) ]), vue.createElementVNode("view", { class: "carousel-section" }, [ vue.createElementVNode("text", { class: "section-title" }, "已上传表盘"), vue.createElementVNode("view", { class: "carousel-container" }, [ vue.createElementVNode( "swiper", { class: "card-swiper", circular: "", "previous-margin": "200rpx", "next-margin": "200rpx", onChange: _cache[3] || (_cache[3] = (...args) => $options.handleCarouselChange && $options.handleCarouselChange(...args)) }, [ (vue.openBlock(true), vue.createElementBlock( vue.Fragment, null, vue.renderList($data.uploadedImages, (img, index) => { return vue.openBlock(), vue.createElementBlock("swiper-item", { key: index }, [ vue.createElementVNode("view", { class: "card-item", onClick: ($event) => $options.selectImage(index) }, [ vue.createElementVNode("view", { class: "card-frame" }, [ vue.createElementVNode("image", { src: img.url, class: "card-image", mode: "aspectFill" }, null, 8, ["src"]), $data.currentImageIndex === index ? (vue.openBlock(), vue.createElementBlock("view", { key: 0, class: "card-overlay" }, [ vue.createVNode(_component_uni_icons, { type: "checkmark", size: "30", color: "#007aff" }) ])) : vue.createCommentVNode("v-if", true) ]) ], 8, ["onClick"]) ]); }), 128 /* KEYED_FRAGMENT */ )) ], 32 /* NEED_HYDRATION */ ), $data.uploadedImages.length === 0 ? (vue.openBlock(), vue.createElementBlock("view", { key: 0, class: "carousel-hint" }, [ vue.createElementVNode("text", null, "暂无上传图片,请点击上方上传按钮添加") ])) : vue.createCommentVNode("v-if", true) ]) ]), vue.createElementVNode("view", { class: "data-section" }, [ vue.createElementVNode("text", { class: "section-title" }, "设备状态"), vue.createElementVNode("view", { class: "data-grid" }, [ vue.createElementVNode("view", { class: "data-card" }, [ vue.createElementVNode("view", { class: "data-icon battery-icon" }, [ vue.createVNode(_component_uni_icons, { type: "battery", size: "36", color: $options.batteryColor }, null, 8, ["color"]) ]), vue.createElementVNode("view", { class: "data-info" }, [ vue.createElementVNode( "text", { class: "data-value" }, vue.toDisplayString($data.batteryLevel) + "%", 1 /* TEXT */ ), vue.createElementVNode("text", { class: "data-label" }, "电量") ]) ]), vue.createElementVNode("view", { class: "data-card" }, [ vue.createElementVNode("view", { class: "data-icon temp-icon" }, [ vue.createVNode(_component_uni_icons, { type: "thermometer", size: "36", color: "#ff7a45" }) ]), vue.createElementVNode("view", { class: "data-info" }, [ vue.createElementVNode( "text", { class: "data-value" }, vue.toDisplayString($data.temperature) + "°C", 1 /* TEXT */ ), vue.createElementVNode("text", { class: "data-label" }, "温度") ]) ]), vue.createElementVNode("view", { class: "data-card" }, [ vue.createElementVNode("view", { class: "data-icon humidity-icon" }, [ vue.createVNode(_component_uni_icons, { type: "water", size: "36", color: "#40a9ff" }) ]), vue.createElementVNode("view", { class: "data-info" }, [ vue.createElementVNode( "text", { class: "data-value" }, vue.toDisplayString($data.humidity) + "%", 1 /* TEXT */ ), vue.createElementVNode("text", { class: "data-label" }, "湿度") ]) ]), vue.createElementVNode("view", { class: "data-card" }, [ vue.createElementVNode("view", { class: "data-icon status-icon" }, [ vue.createVNode(_component_uni_icons, { type: "watch", size: "36", color: "#52c41a" }) ]), vue.createElementVNode("view", { class: "data-info" }, [ vue.createElementVNode( "text", { class: "data-value" }, vue.toDisplayString($options.faceStatusText), 1 /* TEXT */ ), vue.createElementVNode("text", { class: "data-label" }, "表盘状态") ]) ]) ]), vue.createElementVNode("view", { class: "connection-status" }, [ vue.createVNode(_component_uni_icons, { type: "bluetooth", size: "24", color: $data.isConnected ? "#52c41a" : "#ff4d4f", animation: $data.isScanning ? "spin" : "" }, null, 8, ["color", "animation"]), vue.createElementVNode( "view", { class: "status-pot", style: vue.normalizeStyle({ backgroundColor: $data.isConnected ? "Green" : "red" }) }, null, 4 /* STYLE */ ), vue.createElementVNode( "text", { class: "status-text" }, vue.toDisplayString($data.isConnected ? "已连接设备" : $data.isScanning ? "正在搜索设备..." : "未连接设备"), 1 /* TEXT */ ), vue.createElementVNode("button", { class: "connect-btn", onClick: _cache[4] || (_cache[4] = (...args) => $options.toggleConnection && $options.toggleConnection(...args)), disabled: $data.isScanning }, vue.toDisplayString($data.isConnected ? "断开连接" : "连接设备"), 9, ["disabled"]) ]) ]) ]) ]); } const PagesConnectConnect = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["render", _sfc_render], ["__scopeId", "data-v-ea8c7664"], ["__file", "/Users/rdzleo/Desktop/uniapp_code/pages/connect/connect.vue"]]); __definePage("pages/index/index", PagesIndexIndex); __definePage("pages/connect/connect", PagesConnectConnect); const _sfc_main = { onLaunch: function() { formatAppLog("log", "at App.vue:7", "App Launch"); }, onShow: function() { formatAppLog("log", "at App.vue:10", "App Show"); }, onHide: function() { formatAppLog("log", "at App.vue:13", "App Hide"); }, onExit: function() { formatAppLog("log", "at App.vue:34", "App Exit"); } }; const App = /* @__PURE__ */ _export_sfc(_sfc_main, [["__file", "/Users/rdzleo/Desktop/uniapp_code/App.vue"]]); function createApp() { const app = vue.createVueApp(App); return { app }; } const { app: __app__, Vuex: __Vuex__, Pinia: __Pinia__ } = createApp(); uni.Vuex = __Vuex__; uni.Pinia = __Pinia__; __app__.provide("__globalStyles", __uniConfig.styles); __app__._component.mpType = "app"; __app__._component.render = () => { }; __app__.mount("#app"); })(Vue);