diff --git a/api/check.js b/api/check.js new file mode 100644 index 0000000..215667c --- /dev/null +++ b/api/check.js @@ -0,0 +1,12 @@ +import request from '../utils/request' + +// check 检查模块 + +// 获取服务器时间 +export function getServerDatetime(data) { + return request({ + url: '/check/getServerDatetime', + method: 'POST', + data + }) +} diff --git a/api/geocode.js b/api/geocode.js new file mode 100644 index 0000000..dd85388 --- /dev/null +++ b/api/geocode.js @@ -0,0 +1,12 @@ +import request from '../utils/request' + +// geocode 地理编码模块 + +// 获取地址信息 +export function getGeocodeAddress(data) { + return request({ + url: '/geocode/getAddress', + method: 'POST', + data + }) +} diff --git a/api/lock.js b/api/lock.js index d1f39b7..9931b27 100644 --- a/api/lock.js +++ b/api/lock.js @@ -10,3 +10,12 @@ export function getLockListRequest(data) { data }) } + +// 绑定锁管理员 +export function bindLockAdmin(data) { + return request({ + url: '/v2/lock/bindAdmin', + method: 'POST', + data + }) +} diff --git a/manifest.json b/manifest.json index 5330fd3..35562d9 100644 --- a/manifest.json +++ b/manifest.json @@ -5,49 +5,6 @@ "versionName" : "1.0.0", "versionCode" : "100", "transformPx" : false, - /* 5+App特有相关 */ - "app-plus" : { - "usingComponents" : true, - "nvueStyleCompiler" : "uni-app", - "compilerVersion" : 3, - "splashscreen" : { - "alwaysShowBeforeRender" : true, - "waiting" : true, - "autoclose" : true, - "delay" : 0 - }, - /* 模块配置 */ - "modules" : {}, - /* 应用发布信息 */ - "distribute" : { - /* android打包配置 */ - "android" : { - "permissions" : [ - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "", - "" - ] - }, - /* ios打包配置 */ - "ios" : {}, - /* SDK配置 */ - "sdkConfigs" : {} - } - }, - /* 快应用特有相关 */ - "quickapp" : {}, /* 小程序特有相关 */ "mp-weixin" : { "appid" : "wx9829a39e65550757", @@ -55,20 +12,17 @@ "urlCheck" : true, "minified" : true }, + "permission": { + "scope.bluetooth": { + "desc": "蓝牙将用于控制和管理您的智能门锁" + }, + "scope.userLocation": { + "desc": "获取您的位置信息将用于智能门锁的位置服务" + } + }, + "requiredPrivateInfos": ["getLocation"], "usingComponents" : true, "lazyCodeLoading" : "requiredComponents" }, - "mp-alipay" : { - "usingComponents" : true - }, - "mp-baidu" : { - "usingComponents" : true - }, - "mp-toutiao" : { - "usingComponents" : true - }, - "uniStatistics" : { - "enable" : false - }, "vueVersion" : "3" } diff --git a/pages.json b/pages.json index 9c0ec72..cba01fd 100644 --- a/pages.json +++ b/pages.json @@ -17,6 +17,16 @@ "navigationStyle": "default" } }, + { + "path": "pages/selectAddress/selectAddress", + "style": { + "navigationBarTitleText": "锁地址", + "navigationBarTextStyle": "white", + "navigationBarBackgroundColor": "#63b8af", + "navigationStyle": "default", + "disableScroll": true + } + }, { "path": "pages/mine/mine", "style": { @@ -110,10 +120,20 @@ "navigationStyle": "default" } }, + { + "path": "pages/bindLock/bindLock", + "style": { + "navigationBarTitleText": "添加锁", + "navigationBarTextStyle": "white", + "navigationBarBackgroundColor": "#63b8af", + "navigationStyle": "default" + } + }, { "path": "pages/searchDevice/searchDevice", "style": { - "navigationBarTitleText": "搜索", + "disableScroll": true, + "navigationBarTitleText": "附近设备", "navigationBarTextStyle": "white", "navigationBarBackgroundColor": "#63b8af", "navigationStyle": "default" diff --git a/pages/bindLock/bindLock.vue b/pages/bindLock/bindLock.vue new file mode 100644 index 0000000..d88b2d3 --- /dev/null +++ b/pages/bindLock/bindLock.vue @@ -0,0 +1,186 @@ + + + + + + + diff --git a/pages/home/home.vue b/pages/home/home.vue index d96a461..57f6010 100644 --- a/pages/home/home.vue +++ b/pages/home/home.vue @@ -2,8 +2,8 @@ - - + 填加锁时,手机必须在锁旁边 @@ -32,9 +33,9 @@ {{getRole(lock.userType)}} - {{lock.lockName}} + {{lock.lockAlias}} - {{ getTimeLimit(lock.keyType) }} + {{ getTimeLimit(lock.keyType) }} {{ timeFormat(lock.startDate, 'yyyy-mm-dd h:M') }} {{ timeFormat(lock.endDate, 'yyyy-mm-dd h:M ') + getTimeLimit(lock.keyType) }} @@ -45,7 +46,7 @@ - @@ -63,6 +64,7 @@ import { useBluetoothStore } from '@/stores/bluetooth' import { useBasicStore } from '@/stores/basic' import { mapState, mapActions } from 'pinia' + import { getServerDatetime } from '@/api/check' export default { data() { @@ -74,7 +76,8 @@ }, refresherTriggered: false, focus: false, - penging: true + penging: true, + deviceInfo: null } }, computed: { @@ -86,6 +89,8 @@ uni.showLoading({ title: '加载中' }) + this.deviceInfo = await this.getDeviceInfo() + console.log(this.deviceInfo) const token = uni.getStorageSync('token') if(token) { this.updateLoginStatus(true) @@ -102,7 +107,7 @@ ...mapActions(useUserStore, ['updateUserInfo', 'updateLoginStatus', 'login']), ...mapActions(useLockStore, ['getLockList', 'getRole', 'getTimeLimit']), ...mapActions(useBluetoothStore, ['getBluetoothStatus', 'initAndListenBluetooth', 'updateCurrentLockInfo', 'checkSetting']), - ...mapActions(useBasicStore, ['routeJump']), + ...mapActions(useBasicStore, ['routeJump', 'getDeviceInfo']), async nextPage() { if(this.lockList.length < this.lockTotal) { this.search.pageNo++ @@ -116,6 +121,10 @@ this.refresherTriggered = true this.search.pageNo = 1 await this.getLockList(this.search) + uni.showToast({ + title: '刷新成功', + icon: 'none' + }) this.refresherTriggered = false }, async changeSearch(data) { diff --git a/pages/index/index.vue b/pages/index/index.vue index c4ed272..dc0afa9 100644 --- a/pages/index/index.vue +++ b/pages/index/index.vue @@ -164,12 +164,12 @@ console.log('添加用户返回', addUserCode) }, getLockStatusResult() { - const timnestamp = parseInt(new Date().getTime() / 1000) + const timestamp = parseInt(new Date().getTime() / 1000) this.getLockStatus({ name: this.currentLockInfo.name, uid: this.authUid, - nowTime: timnestamp, - localTime: timnestamp + nowTime: timestamp, + localTime: timestamp }) } } diff --git a/pages/searchDevice/searchDevice.vue b/pages/searchDevice/searchDevice.vue index 608c2be..2cc7af6 100644 --- a/pages/searchDevice/searchDevice.vue +++ b/pages/searchDevice/searchDevice.vue @@ -1,17 +1,161 @@ + + diff --git a/pages/selectAddress/selectAddress.vue b/pages/selectAddress/selectAddress.vue new file mode 100644 index 0000000..516ade8 --- /dev/null +++ b/pages/selectAddress/selectAddress.vue @@ -0,0 +1,256 @@ + + + + + + + diff --git a/static/images/icon_add.png b/static/images/icon_add.png index d0f9a4d..937c30b 100644 Binary files a/static/images/icon_add.png and b/static/images/icon_add.png differ diff --git a/static/images/icon_add_round.png b/static/images/icon_add_round.png new file mode 100644 index 0000000..d0f9a4d Binary files /dev/null and b/static/images/icon_add_round.png differ diff --git a/static/images/icon_address.png b/static/images/icon_address.png new file mode 100644 index 0000000..d2ddd5e Binary files /dev/null and b/static/images/icon_address.png differ diff --git a/static/images/icon_door_lock.png b/static/images/icon_door_lock.png new file mode 100755 index 0000000..72dd4b9 Binary files /dev/null and b/static/images/icon_door_lock.png differ diff --git a/stores/basic.js b/stores/basic.js index 43e57e0..daa6e11 100644 --- a/stores/basic.js +++ b/stores/basic.js @@ -61,6 +61,16 @@ const pages = [ name: 'searchDevice', path: '/pages/searchDevice/searchDevice', tabBar: false + }, + { + name: 'selectAddress', + path: '/pages/selectAddress/selectAddress', + tabBar: false + }, + { + name: 'bindLock', + path: '/pages/bindLock/bindLock', + tabBar: false } ] @@ -120,5 +130,22 @@ export const useBasicStore = defineStore('basic', { return }) }, + // 计算字符串长度 + calculateStringTotalWidth(str) { + let totalWidth = 0 + // 遍历字符串中的每一个字符 + for (let i = 0; i < str.length; i++) { + const char = str[i] + // 判断当前字符是全角字符还是半角字符 + if (/[\uFF01-\uFF60\uFFE0-\uFFE6\u4E00-\u9FFF\u3000-\u303F]/.test(char)) { + // 全角字符宽度为 2 + totalWidth += 2 + } else { + // 半角字符宽度为 1 + totalWidth += 1 + } + } + return totalWidth + } } }) diff --git a/stores/bluetooth.js b/stores/bluetooth.js index 541e2a7..2e67edf 100644 --- a/stores/bluetooth.js +++ b/stores/bluetooth.js @@ -5,6 +5,7 @@ import { defineStore } from 'pinia' import crc from 'crc' import { sm4 } from 'sm-crypto' import { md5 } from 'js-md5' +import { getServerDatetime } from '@/api/check' // 定时器 let timer @@ -58,10 +59,32 @@ export const useBluetoothStore = defineStore('ble', { // 消息序号 messageCount: 1, // 是否初始化蓝牙 - isInitBluetooth: false + isInitBluetooth: false, + // 服务器时间 + serverTimestamp: 0, + // 设备keyID + keyId: '0' } }, actions: { + // 二进制转字符串 + uint8ArrayToString(uint8Array) { + let str = '' + for (let i = 0; i < uint8Array.length; i++) { + if (uint8Array[i] !== 0) { + str += String.fromCharCode(uint8Array[i]); + } + } + return str + }, + // 更新服务端时间戳 + async updateServerTimestamp() { + const { code, data, message } = await getServerDatetime({}) + if(code === 0) { + this.serverTimestamp = parseInt(data.date / 1000) + } + return { code, data, message } + }, // 初始化并监听 async initAndListenBluetooth() { // 初始化蓝牙 @@ -168,7 +191,7 @@ export const useBluetoothStore = defineStore('ble', { if(binaryData[14] === 0) { that.updateCurrentLockInfo({ ...that.currentLockInfo, - publicKey: binaryData.slice(15, 31) + publicKey: [...binaryData.slice(15, 31)] }) } characteristicValueCallback({ @@ -191,7 +214,8 @@ export const useBluetoothStore = defineStore('ble', { that.updateCurrentLockInfo({ ...that.currentLockInfo, commKey: decrypted.slice(3, 19), - signKey: decrypted.slice(19, 35) + signKey: decrypted.slice(19, 35), + pwdTimestamp: this.arrayToTimestamp(decrypted.slice(35, 39)) * 1000 }) console.log('commKey', Array.from(that.currentLockInfo.commKey)) console.log('signKey', Array.from(that.currentLockInfo.signKey)) @@ -211,28 +235,28 @@ export const useBluetoothStore = defineStore('ble', { case cmdIds.getLockStatus: if (decrypted[2] === 0) { const lockConfig = { - vendor: decrypted.slice(3, 23), + vendor: that.uint8ArrayToString(decrypted.slice(3, 23)), product: decrypted[23], - model: decrypted.slice(24, 44), - fwVersion: decrypted.slice(44, 64), - hwVersion: decrypted.slice(64, 84), - serialNum0: decrypted.slice(84, 100), - serialNum1: decrypted.slice(100, 116), - btDeviceName: decrypted.slice(116, 132), - battRemCap: decrypted[132], - battRemCapStandby: decrypted[133], - restoreCounter: decrypted.slice(134, 136), - restoreDate: decrypted.slice(136, 140), - icPartNo: decrypted.slice(140, 150), - inDate: decrypted.slice(150, 154), - mac: decrypted.slice(154, 174), - featurevalueLength: decrypted[174], - featureValue: decrypted.slice(175, 175 + decrypted[174]), - featureEnValLength: decrypted[175 + decrypted[174]], - featureEnVal: decrypted.slice(176 + decrypted[174], 176 + decrypted[174] + decrypted[175 + decrypted[174]]), + model: that.uint8ArrayToString(decrypted.slice(24, 44)), + fwVersion: that.uint8ArrayToString(decrypted.slice(44, 64)), + hwVersion: that.uint8ArrayToString(decrypted.slice(64, 84)), + serialNum0: that.uint8ArrayToString(decrypted.slice(84, 100)), + serialNum1: that.uint8ArrayToString(decrypted.slice(100, 116)), + btDeviceName: that.uint8ArrayToString(decrypted.slice(116, 132)), + electricQuantity: decrypted[132], + electricQuantityStandby: decrypted[133], + restoreCount: decrypted[134] * 256 + decrypted[135], + restoreDate: this.arrayToTimestamp(decrypted.slice(136, 140)), + icPartNo: that.uint8ArrayToString(decrypted.slice(140, 150)), + indate: this.arrayToTimestamp(decrypted.slice(150, 154)), + mac: that.uint8ArrayToString(decrypted.slice(154, 174)), + timezoneOffset: new Date().getTimezoneOffset() * 60 } that.updateCurrentLockInfo({ ...that.currentLockInfo, + featureValue: that.uint8ArrayToString(decrypted.slice(175, 175 + decrypted[174])), + featureSettingValue: that.uint8ArrayToString(decrypted.slice(176 + decrypted[174], 176 + decrypted[174] + decrypted[175 + decrypted[174]])), + featureSettingParams: Array.from(decrypted.slice(176 + decrypted[174] + decrypted[175 + decrypted[174]])), lockConfig }) console.log('获取锁状态成功', that.currentLockInfo.lockConfig) @@ -244,7 +268,8 @@ export const useBluetoothStore = defineStore('ble', { case cmdIds.addUser: that.updateCurrentLockInfo({ ...that.currentLockInfo, - token: decrypted.slice(42,46) + token: decrypted.slice(42,46), + lockUserNo: decrypted[47] * 256 + decrypted[48] }) characteristicValueCallback({ code: decrypted[46] @@ -769,10 +794,15 @@ export const useBluetoothStore = defineStore('ble', { array[3] = (timestamp & 0xff) return array }, + // 二进制转时间戳 + arrayToTimestamp(array) { + const timestamp = (array[0] << 24) | (array[1] << 16) | (array[2] << 8) | array[3] + return timestamp >>> 0 + }, // 添加用户 async addLockUser(data) { const { name, authUid, uid, keyId, openMode, keyType, startDate, expireDate, useCountLimit, isRound, weekRound, - startHour, startMin, endHour, endMin, role, password, publicKey, commKey } = data + startHour, startMin, endHour, endMin, role, password } = data const length = 2 + 40 + 20 + 40 + 20 + 1 + 1 + 4 + 4 + 2 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 20 + 4 + 1 + 16 const headArray = this.createPackageHeader(3, length) const conentArray = new Uint8Array(length) @@ -825,7 +855,7 @@ export const useBluetoothStore = defineStore('ble', { conentArray.set(md5Array, 166) - const cebArray = sm4.encrypt(conentArray, commKey, { mode: 'ecb', output: 'array' }) + const cebArray = sm4.encrypt(conentArray, this.currentLockInfo.commKey, { mode: 'ecb', output: 'array' }) const packageArray = this.createPackageEnd(headArray, cebArray) await this.writeBLECharacteristicValue(packageArray) diff --git a/stores/user.js b/stores/user.js index 5aa4988..e89fd61 100644 --- a/stores/user.js +++ b/stores/user.js @@ -22,7 +22,7 @@ export const useUserStore = defineStore('user', { this.isLogin = status }, async login() { - uni.setStorageSync('token', '3021|MZv7iEf0NwjCPSGx4QWs37zOjeVN3GrSJ2v7D56L7db1fcc5') + uni.setStorageSync('token', '3023|pZ1aoVlMKJBTBTGWlsZPpbLvxc8txcHbrJx2ljrf49c7efe0') const { code, data } = await getUserInfoRequest() await useLockStore().getLockList({ pageNo: 1, diff --git a/utils/request.js b/utils/request.js index f7567ae..4cea802 100644 --- a/utils/request.js +++ b/utils/request.js @@ -74,6 +74,7 @@ const request = (config) => { code: res?.data?.errorCode, res: res?.data?.data, token: header?.authorization || '', + message: res?.data?.errorMsg, duration: new Date().getTime() - timestamp }) }