From 77d24ecb542b7672a79827ef498747f056db9e01 Mon Sep 17 00:00:00 2001 From: fanpeng <438123081@qq.com> Date: Thu, 5 Jun 2025 11:52:12 +0800 Subject: [PATCH 1/5] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E6=B3=A8=E5=86=8C=E5=92=8C=E5=AF=86=E7=A0=81=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E5=8A=9F=E8=83=BD=EF=BC=8C=E6=9B=B4=E6=96=B0=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2=E8=B7=AF=E7=94=B1=E5=92=8C=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App.vue | 2 +- api/system.js | 12 + api/user.js | 18 + components/verify/utils/ase.js | 15 + components/verify/utils/request.js | 43 + components/verify/verify.vue | 476 +++++++++++ .../verify/verifySlider/verifySlider.vue | 781 ++++++++++++++++++ constant/reg.js | 3 + package-lock.json | 7 + package.json | 1 + pages.json | 45 +- pages/main/home.vue | 6 + pages/user/countryList.vue | 97 +++ pages/user/forgotPassword.vue | 5 + pages/user/login.vue | 170 ++++ pages/user/register.vue | 317 +++++++ pages/user/safeVerify.vue | 52 ++ pages/user/userInfo.vue | 32 +- stores/basic.js | 55 +- stores/user.js | 68 +- 20 files changed, 2161 insertions(+), 44 deletions(-) create mode 100644 api/system.js create mode 100644 components/verify/utils/ase.js create mode 100644 components/verify/utils/request.js create mode 100644 components/verify/verify.vue create mode 100644 components/verify/verifySlider/verifySlider.vue create mode 100644 constant/reg.js create mode 100644 pages/user/countryList.vue create mode 100644 pages/user/forgotPassword.vue create mode 100644 pages/user/login.vue create mode 100644 pages/user/register.vue create mode 100644 pages/user/safeVerify.vue diff --git a/App.vue b/App.vue index 092e580..830976a 100644 --- a/App.vue +++ b/App.vue @@ -29,7 +29,7 @@ if (this.envVersion === 'trial') { return 'XHJ' } - return 'XHJ' + return 'DEV' } }, computed: { diff --git a/api/system.js b/api/system.js new file mode 100644 index 0000000..c350145 --- /dev/null +++ b/api/system.js @@ -0,0 +1,12 @@ +import request from '../utils/request' + +// system 系统模块 + +// 获取国家列表 +export function getCountryListRequest(data) { + return request({ + url: '/system/listCountry', + method: 'POST', + data + }) +} diff --git a/api/user.js b/api/user.js index 2ea0a3e..c7ed31e 100644 --- a/api/user.js +++ b/api/user.js @@ -65,6 +65,24 @@ export function loginRequest(data) { }) } +// 密码登录 +export function passwordLoginRequest(data) { + return request({ + url: '/user/register', + method: 'POST', + data + }) +} + +// 注册 +export function registerRequest(data) { + return request({ + url: '/user/login', + method: 'POST', + data + }) +} + // 注册 export function phoneLoginRequest(data) { return request({ diff --git a/components/verify/utils/ase.js b/components/verify/utils/ase.js new file mode 100644 index 0000000..aee9d8f --- /dev/null +++ b/components/verify/utils/ase.js @@ -0,0 +1,15 @@ +import CryptoJS from 'crypto-js' + +/** + * @word 要加密的内容 + * @keyWord String 服务器随机返回的关键字 + * */ +export function aesEncrypt(word, keyWord = 'XwKsGlMcdPMEhR1B') { + let key = CryptoJS.enc.Utf8.parse(keyWord) + let src = CryptoJS.enc.Utf8.parse(word) + let encrypted = CryptoJS.AES.encrypt(src, key, { + mode: CryptoJS.mode.ECB, + padding: CryptoJS.pad.Pkcs7 + }) + return encrypted.toString() +} diff --git a/components/verify/utils/request.js b/components/verify/utils/request.js new file mode 100644 index 0000000..c6c6f88 --- /dev/null +++ b/components/verify/utils/request.js @@ -0,0 +1,43 @@ +import env from '@/config/env' + +export const myRequest = async (option = {}) => { + const envConfig = getApp().globalData.getEnvConfig() + const baseConfig = env[envConfig] + + console.log('=== 请求入参 ===') + console.log('URL:', baseConfig.baseUrl + option.url) + console.log('Data:', JSON.stringify(option.data || {}, null, 2)) + console.log('Method:', option.method || 'GET') + console.log('================') + + return new Promise((resolve, reject) => { + uni.request({ + url: baseConfig.baseUrl + option.url, + data: option.data || {}, + method: option.method || 'GET', + timeout: 30000, + header: { + 'content-type': 'application/json', + ...option.header + }, + success: result => { + console.log('=== 请求出参 ===') + console.log('StatusCode:', result.statusCode) + console.log('Response:', JSON.stringify(result.data, null, 2)) + console.log('================') + + if (result.statusCode === 200) { + resolve(result) + } else { + reject(new Error(`HTTP ${result.statusCode}: ${result.errMsg || 'Request failed'}`)) + } + }, + fail: error => { + console.log('=== 请求失败 ===') + console.log('Error:', error) + console.log('================') + reject(new Error(error.errMsg || 'Network request failed')) + } + }) + }) +} diff --git a/components/verify/verify.vue b/components/verify/verify.vue new file mode 100644 index 0000000..f9d33b4 --- /dev/null +++ b/components/verify/verify.vue @@ -0,0 +1,476 @@ + + + diff --git a/components/verify/verifySlider/verifySlider.vue b/components/verify/verifySlider/verifySlider.vue new file mode 100644 index 0000000..f1e9332 --- /dev/null +++ b/components/verify/verifySlider/verifySlider.vue @@ -0,0 +1,781 @@ + + + diff --git a/constant/reg.js b/constant/reg.js new file mode 100644 index 0000000..9b2424c --- /dev/null +++ b/constant/reg.js @@ -0,0 +1,3 @@ +export const PHONE_REG = /^\d{7,12}$/ +export const EMAIL_REG = /^[\w.-]+@([\w-]+\.)+[\w-]{2,4}$/ +export const PASSWORD_REG = /^(?!\d+$)(?![a-z]+$)(?![^0-9a-z]+$).{8,20}$/i diff --git a/package-lock.json b/package-lock.json index 3e19e4e..c5dbe72 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,6 +5,7 @@ "packages": { "": { "dependencies": { + "crypto-js": "^4.2.0", "pinia": "^2.2.0", "pinia-plugin-unistorage": "^0.1.2", "uview-plus": "^3.3.12" @@ -3106,6 +3107,12 @@ "node": ">= 8" } }, + "node_modules/crypto-js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", + "license": "MIT" + }, "node_modules/css-functions-list": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.2.3.tgz", diff --git a/package.json b/package.json index f7c9d92..e055ba6 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,6 @@ { "dependencies": { + "crypto-js": "^4.2.0", "pinia": "^2.2.0", "pinia-plugin-unistorage": "^0.1.2", "uview-plus": "^3.3.12" diff --git a/pages.json b/pages.json index 81ff30f..b06cfb4 100644 --- a/pages.json +++ b/pages.json @@ -112,6 +112,36 @@ "style": { "navigationBarTitleText": "验证邮箱" } + }, + { + "path": "login", + "style": { + "navigationBarTitleText": "登录" + } + }, + { + "path": "register", + "style": { + "navigationBarTitleText": "注册" + } + }, + { + "path": "forgotPassword", + "style": { + "navigationBarTitleText": "忘记密码" + } + }, + { + "path": "countryList", + "style": { + "navigationBarTitleText": "你所在的国家/地区" + } + }, + { + "path": "safeVerify", + "style": { + "navigationBarTitleText": "安全验证" + } } ] }, @@ -593,22 +623,13 @@ ], "preloadRule": { "pages/main/home": { - "packages": [ - "pages/others", - "pages/addDevice", - "pages/p2p" - ] + "packages": ["pages/others", "pages/addDevice", "pages/p2p"] }, "pages/main/lockDetail": { - "packages": [ - "pages/feature", - "pages/setting" - ] + "packages": ["pages/feature", "pages/setting"] }, "pages/main/mine": { - "packages": [ - "pages/user" - ] + "packages": ["pages/user"] } }, "globalStyle": { diff --git a/pages/main/home.vue b/pages/main/home.vue index 9fe1cd9..7f5777d 100644 --- a/pages/main/home.vue +++ b/pages/main/home.vue @@ -408,6 +408,12 @@ homeLogin() { const that = this return new Promise(resolve => { + // #ifdef APP-PLUS + that.routeJump({ + name: 'login', + type: 'reLaunch' + }) + // #endif // #ifdef MP-WEIXIN uni.login({ provider: 'weixin', diff --git a/pages/user/countryList.vue b/pages/user/countryList.vue new file mode 100644 index 0000000..b01e087 --- /dev/null +++ b/pages/user/countryList.vue @@ -0,0 +1,97 @@ + + + diff --git a/pages/user/forgotPassword.vue b/pages/user/forgotPassword.vue new file mode 100644 index 0000000..1e8e1a1 --- /dev/null +++ b/pages/user/forgotPassword.vue @@ -0,0 +1,5 @@ + + + diff --git a/pages/user/login.vue b/pages/user/login.vue new file mode 100644 index 0000000..7d1838d --- /dev/null +++ b/pages/user/login.vue @@ -0,0 +1,170 @@ + + + diff --git a/pages/user/register.vue b/pages/user/register.vue new file mode 100644 index 0000000..741e8ee --- /dev/null +++ b/pages/user/register.vue @@ -0,0 +1,317 @@ + + + diff --git a/pages/user/safeVerify.vue b/pages/user/safeVerify.vue new file mode 100644 index 0000000..8c95797 --- /dev/null +++ b/pages/user/safeVerify.vue @@ -0,0 +1,52 @@ + + + + + + + diff --git a/pages/user/userInfo.vue b/pages/user/userInfo.vue index ac35d08..d69cfe3 100644 --- a/pages/user/userInfo.vue +++ b/pages/user/userInfo.vue @@ -246,47 +246,47 @@ diff --git a/stores/basic.js b/stores/basic.js index 552a78a..300a5a0 100644 --- a/stores/basic.js +++ b/stores/basic.js @@ -406,11 +406,36 @@ const pages = [ name: 'videoEdit', path: '/pages/p2p/videoEdit', tabBar: false + }, + { + name: 'login', + path: '/pages/user/login', + tabBar: false + }, + { + name: 'register', + path: '/pages/user/register', + tabBar: false + }, + { + name: 'forgotPassword', + path: '/pages/user/forgotPassword', + tabBar: false + }, + { + name: 'countryList', + path: '/pages/user/countryList', + tabBar: false + }, + { + name: 'safeVerify', + path: '/pages/user/safeVerify', + tabBar: false } ] export const useBasicStore = defineStore('basic', { - state () { + state() { return { // 设备信息 deviceInfo: null, @@ -424,7 +449,7 @@ export const useBasicStore = defineStore('basic', { // 路由跳转 /* data 入参 name string页面名称 type string跳转方式 params object传递参数 delta number返回页面数 * 具体入参查看文档 https://www.uviewui.com/js/route.html */ - routeJump (data) { + routeJump(data) { const page = pages.find(page => { return page.name === data.name }) @@ -453,10 +478,10 @@ export const useBasicStore = defineStore('basic', { } }, // 获取当前网络状态 - getNetworkType () { + getNetworkType() { return new Promise(resolve => { uni.getNetworkType({ - success (res) { + success(res) { if (res.networkType === 'none') { uni.showToast({ title: '网络访问失败,请检查网络是否正常', @@ -467,14 +492,14 @@ export const useBasicStore = defineStore('basic', { } resolve(true) }, - fail () { + fail() { resolve(false) } }) }) }, // 获取设备信息 - getDeviceInfo () { + getDeviceInfo() { const that = this return new Promise(resolve => { if (that.deviceInfo?.model) { @@ -482,29 +507,31 @@ export const useBasicStore = defineStore('basic', { return } uni.getSystemInfo({ - success (res) { + success(res) { that.deviceInfo = res resolve(that.deviceInfo) }, - fail () { + fail() { resolve({}) } }) }) }, // 获取胶囊信息 - getButtonInfo () { + getButtonInfo() { return new Promise(resolve => { + // #ifdef MP-WEIXIN if (this.buttonInfo?.top) { resolve(this.buttonInfo) return } this.buttonInfo = uni.getMenuButtonBoundingClientRect() + // #endif resolve(this.buttonInfo) }) }, // 计算字符串长度 - calculateStringTotalWidth (str) { + calculateStringTotalWidth(str) { let totalWidth = 0 // 遍历字符串中的每一个字符 for (let i = 0; i < str.length; i++) { @@ -521,10 +548,10 @@ export const useBasicStore = defineStore('basic', { return totalWidth }, // 回退页面并弹出toast提示 - backAndToast (message, delta = 1) { + backAndToast(message, delta = 1) { uni.navigateBack({ delta, - complete () { + complete() { setTimeout(() => { uni.showToast({ title: message, @@ -535,7 +562,7 @@ export const useBasicStore = defineStore('basic', { }) }, // 分享跳转 - shareJump (data = this.shareConfig) { + shareJump(data = this.shareConfig) { if (data.path) { const target = data.path.split('/') if (target.length > 1) { @@ -563,7 +590,7 @@ export const useBasicStore = defineStore('basic', { } this.shareConfig = data - function getParams (params) { + function getParams(params) { let paramStr = '' Object.keys(params).forEach(item => { if (paramStr === '') { diff --git a/stores/user.js b/stores/user.js index 8675e85..7fd0131 100644 --- a/stores/user.js +++ b/stores/user.js @@ -2,10 +2,17 @@ * @description 用户信息数据持久化 */ import { defineStore } from 'pinia' -import { getUserInfoRequest, getWebUrlRequest, loginRequest, phoneLoginRequest } from '@/api/user' +import { + getUserInfoRequest, + getWebUrlRequest, + loginRequest, + phoneLoginRequest, + registerRequest +} from '@/api/user' import { useLockStore } from '@/stores/lock' import { setStorage, getStorage } from '@/utils/storage' import { useNotificationStore } from '@/stores/notification' +import { useBasicStore } from '@/stores/basic' export const useUserStore = defineStore('user', { state() { @@ -34,6 +41,65 @@ export const useUserStore = defineStore('user', { } return code }, + async register(params) { + const { account, password, receiverType, countryCode, verificationCode } = params + const $basic = useBasicStore() + const deviceInfo = await $basic.getDeviceInfo() + const info = { + deviceBrand: deviceInfo.deviceBrand, + deviceId: deviceInfo.deviceId, + deviceModel: deviceInfo.deviceModel, + model: deviceInfo.model, + system: deviceInfo.system + } + const { code, data, message } = await registerRequest({ + receiverType, + countryCode, + account, + password, + verificationCode, + platId: 2, + deviceInfo: info + }) + if (code === 0) { + setStorage('token', data.accessToken) + return true + } + uni.showToast({ + title: message, + icon: 'none' + }) + return false + }, + async passwordLogin(params) { + const { username, password, countryCode } = params + const $basic = useBasicStore() + const deviceInfo = await $basic.getDeviceInfo() + const info = { + deviceBrand: deviceInfo.deviceBrand, + deviceId: deviceInfo.deviceId, + deviceModel: deviceInfo.deviceModel, + model: deviceInfo.model, + system: deviceInfo.system + } + const { code, data, message } = await registerRequest({ + username, + password, + countryCode, + loginType: 1, + platId: 2, + deviceInfo: info + }) + if (code === 0) { + setStorage('token', data.accessToken) + return true + } + uni.showToast({ + title: message, + icon: 'none' + }) + return false + }, async phoneLogin(params) { const { iv, encryptedData, code: js_code } = params const openid = await getStorage('openid') From 34e3a49d53f96ff9eb9d8033bccd90afa9eca207 Mon Sep 17 00:00:00 2001 From: fanpeng <438123081@qq.com> Date: Thu, 5 Jun 2025 16:22:56 +0800 Subject: [PATCH 2/5] =?UTF-8?q?feat:=20=E5=AE=8C=E6=88=90=E6=B3=A8?= =?UTF-8?q?=E5=86=8C=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/user.js | 2 +- .../verify/verifySlider/verifySlider.vue | 23 +++++++++++-------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/api/user.js b/api/user.js index c7ed31e..17802c1 100644 --- a/api/user.js +++ b/api/user.js @@ -77,7 +77,7 @@ export function passwordLoginRequest(data) { // 注册 export function registerRequest(data) { return request({ - url: '/user/login', + url: '/user/register', method: 'POST', data }) diff --git a/components/verify/verifySlider/verifySlider.vue b/components/verify/verifySlider/verifySlider.vue index f1e9332..3752ffb 100644 --- a/components/verify/verifySlider/verifySlider.vue +++ b/components/verify/verifySlider/verifySlider.vue @@ -27,7 +27,7 @@ :style="{ width: imgSize.width, height: '40px', 'line-height': '40px' }" class="verify-bar-area" > - + {{ text }} - + {{ finishText }} 您已授权 - 您已拒绝授权,请去设置中 - - 打开语音、视频通话提醒开关 + + 授权失败 + 请保证设备一直处于在线状态 + + 重试 + - - 打开设置 + + 您已拒绝授权,请去设置中 + + 打开语音、视频通话提醒开关 + + + 打开设置 + @@ -52,57 +64,76 @@ const pending = ref(false) - onShow(() => { + const weChatTicketsFlag = ref(false) + + onShow(async () => { uni.showLoading({ title: '加载中...' }) - wx.getDeviceVoIPList({ - async success(res) { - list.value = res.list - if (res.list.length > 0) { - const result = await getInfo() - if (result.code === 0) { - const data = list.value.find(item => item.sn === result.data.WXIoTDeviceInfo.SN) - if (data) { - if (data.status === 1) { - if (reject.value) { - passthrough({ - request_method: 'POST', - request_uri: '/api/v1/tencentYun/reportWechatAuthSuccess', - post_args: { - lockId: $bluetooth.currentLockInfo.lockId, - wxOpenid: getStorage('openid'), - wxDeviceSn: result.data.WXIoTDeviceInfo.SNTicket - } - }) - } - reject.value = false - } else if (data.status === 0) { - reject.value = true - } - isAuthorized.value = true - requestFinish.value = true - uni.hideLoading() - } else { - requestFinish.value = true - uni.hideLoading() - } - } else { - uni.showToast({ - title: result.message, - icon: 'none' - }) - requestFinish.value = true - uni.hideLoading() - } - } else { - requestFinish.value = true - uni.hideLoading() - } + + const resultList = await Promise.all([getDeviceVoIPList(), getAuthResult()]) + + list.value = resultList[0] + const authResult = resultList[1] + + const data = list.value.find(item => item.sn === authResult.data.wxDeviceSn) + + if (authResult?.code === 0 && authResult?.data?.wxOpenid === getStorage('openid') && data) { + isAuthorized.value = true + requestFinish.value = true + if (data.status === 1) { + reject.value = false + } else if (data.status === 0) { + reject.value = true + weChatTicketsFlag.value = true } - }) + uni.hideLoading() + } else { + requestFinish.value = true + uni.hideLoading() + } }) + const getDeviceVoIPList = async () => { + return new Promise(resolve => { + wx.getDeviceVoIPList({ + success: res => { + resolve(res.list) + } + }) + }) + } + + const retry = async () => { + if (pending.value) return + uni.showLoading({ + title: '授权中...' + }) + pending.value = true + const ticketsResult = await weChatTickets() + + pending.value = false + if (ticketsResult?.code === 0) { + passthrough({ + request_method: 'POST', + request_uri: '/api/v1/tencentYun/reportWechatAuthSuccess', + post_args: { + lockId: $bluetooth.currentLockInfo.lockId, + wxOpenid: getStorage('openid'), + wxDeviceSn: ticketsResult.data.wxDeviceSn + } + }) + uni.hideLoading() + isAuthorized.value = true + } else { + uni.hideLoading() + uni.showToast({ + title: '授权失败', + icon: 'none' + }) + } + } + const openSetting = () => { uni.openSetting({ success: res => { @@ -111,6 +142,18 @@ }) } + const getAuthResult = async () => { + const result = await passthrough({ + request_method: 'GET', + request_uri: '/api/v1/tencentYun/checkWechatAuth', + post_args: { + lockId: $bluetooth.currentLockInfo.lockId, + wxOpenid: getStorage('openid') + } + }) + return result + } + const getInfo = async () => { const result = await passthrough({ request_method: 'GET', @@ -122,6 +165,29 @@ return result } + const weChatTickets = async (snTicket, sn) => { + let ticket = snTicket + let snResult = sn + if (!snTicket) { + const result = await getInfo() + ticket = result.data.WXIoTDeviceInfo.SNTicket + snResult = result.data.WXIoTDeviceInfo.SN + } + const result = await $bluetooth.weChatTickets({ + keyId: $bluetooth.currentLockInfo.keyId.toString(), + uid: $bluetooth.currentLockInfo.uid.toString(), + openId: getStorage('openid'), + snTicket: ticket + }) + + return { + code: result.code, + data: { + wxDeviceSn: snResult + } + } + } + const handleAuthorize = async () => { if (pending.value) return pending.value = true @@ -136,22 +202,39 @@ modelId: result.data.WXIoTDeviceInfo.ModelId, deviceName: await env[await getApp().globalData.getEnvConfig()].appName, async success() { - isAuthorized.value = true - uni.hideLoading() - pending.value = false - passthrough({ - request_method: 'POST', - request_uri: '/api/v1/tencentYun/reportWechatAuthSuccess', - post_args: { - lockId: $bluetooth.currentLockInfo.lockId, - wxOpenid: getStorage('openid'), - wxDeviceSn: result.data.WXIoTDeviceInfo.SNTicket - } - }) - uni.showToast({ - title: '授权成功', - icon: 'none' - }) + const ticketsResult = await weChatTickets( + result.data.WXIoTDeviceInfo.SNTicket, + result.data.WXIoTDeviceInfo.SN + ) + + if (ticketsResult?.code === 0) { + isAuthorized.value = true + uni.hideLoading() + pending.value = false + passthrough({ + request_method: 'POST', + request_uri: '/api/v1/tencentYun/reportWechatAuthSuccess', + post_args: { + lockId: $bluetooth.currentLockInfo.lockId, + wxOpenid: getStorage('openid'), + wxDeviceSn: result.data.WXIoTDeviceInfo.SN + } + }) + uni.showToast({ + title: '授权成功', + icon: 'none' + }) + } else { + isAuthorized.value = true + uni.hideLoading() + pending.value = false + reject.value = true + weChatTicketsFlag.value = true + uni.showToast({ + title: '授权失败', + icon: 'none' + }) + } }, fail() { isAuthorized.value = true diff --git a/stores/bluetooth.js b/stores/bluetooth.js index 4484d7a..dad8566 100644 --- a/stores/bluetooth.js +++ b/stores/bluetooth.js @@ -116,7 +116,9 @@ const subCmdIds = { // 修改设置 updateSetting: 70, // 修改设置带参数 - updateSettingWithParams: 72 + updateSettingWithParams: 72, + // 微信票据 + weChatTickets: 55 } export const useBluetoothStore = defineStore('ble', { @@ -482,6 +484,21 @@ export const useBluetoothStore = defineStore('ble', { } }) break + case subCmdIds.weChatTickets: + that.updateCurrentLockInfo({ + ...that.currentLockInfo, + token: decrypted.slice(5, 9) + }) + if (decrypted[2] === 0) { + characteristicValueCallback({ + code: decrypted[9] + }) + } else { + characteristicValueCallback({ + code: decrypted[2] + }) + } + break case subCmdIds.registerCard: case subCmdIds.registerRemote: case subCmdIds.registerPalmVein: @@ -2861,6 +2878,107 @@ export const useBluetoothStore = defineStore('ble', { return this.getResult(this.setLockPassword, data) }, + // 微信票据 + async weChatTickets(data) { + // 确认蓝牙状态正常 + if ( + this.bluetoothStatus !== 0 && + this.currentLockInfo.transportType !== transportType.TRANSPORT_TENCENT_YUN + ) { + console.log('写入未执行', this.bluetoothStatus) + this.getBluetoothStatus() + return { + code: -1 + } + } + + // 确认设备连接正常 + if ( + !this.currentLockInfo.connected && + this.currentLockInfo.transportType !== transportType.TRANSPORT_TENCENT_YUN + ) { + const searchResult = await this.searchAndConnectDevice() + if (searchResult.code !== 0) { + return searchResult + } + this.updateCurrentLockInfo({ + ...this.currentLockInfo, + deviceId: searchResult.data.deviceId + }) + console.log('设备ID:', this.currentLockInfo.deviceId) + const result = await this.connectBluetoothDevice() + console.log('连接结果', result) + if (!result) { + return { + code: -1 + } + } + } + + // 检查是否已添加为用户 + const checkResult = await this.checkLockUser() + if (!checkResult) { + return { + code: -1 + } + } + + const { keyId, uid, openId, snTicket } = data + const length = 2 + 1 + 1 + 40 + 20 + 4 + 2 + openId.length + 2 + snTicket.length + 1 + 16 + const headArray = this.createPackageHeader(3, length) + const contentArray = new Uint8Array(length) + + contentArray[0] = cmdIds.expandCmd / 256 + contentArray[1] = cmdIds.expandCmd % 256 + + contentArray[2] = subCmdIds.weChatTickets + + contentArray[3] = length - 3 + + for (let i = 0; i < keyId.length; i++) { + contentArray[i + 4] = keyId.charCodeAt(i) + } + for (let i = 0; i < uid.length; i++) { + contentArray[i + 44] = uid.charCodeAt(i) + } + + contentArray.set(this.currentLockInfo.token || new Uint8Array([0, 0, 0, 0]), 64) + + contentArray[68] = openId.length / 256 + contentArray[69] = openId.length % 256 + + for (let i = 0; i < openId.length; i++) { + contentArray[i + 70] = openId.charCodeAt(i) + } + + contentArray[70 + openId.length] = snTicket.length / 256 + contentArray[71 + openId.length] = snTicket.length % 256 + + for (let i = 0; i < snTicket.length; i++) { + contentArray[i + 72 + openId.length] = snTicket.charCodeAt(i) + } + + contentArray[72 + openId.length + snTicket.length] = 16 + + const md5Array = this.md5Encrypte( + keyId + uid, + this.currentLockInfo.token || new Uint8Array([0, 0, 0, 0]), + this.currentLockInfo.signKey + ) + + contentArray.set(md5Array, 73 + openId.length + snTicket.length) + + const cebArray = sm4.encrypt(contentArray, this.currentLockInfo.commKey, { + mode: 'ecb', + output: 'array' + }) + + const packageArray = this.createPackageEnd(headArray, cebArray) + + this.transportMessage(packageArray) + + return this.getResult(this.weChatTickets, data) + }, parseTimeToList(timeString) { let timeList = [0, 0, 0, 0] From 26039dc8d792f5cd05f64762cccf22aa7d411e92 Mon Sep 17 00:00:00 2001 From: fanpeng <438123081@qq.com> Date: Fri, 6 Jun 2025 09:38:24 +0800 Subject: [PATCH 4/5] =?UTF-8?q?feat:=20=E5=9C=A8sdk=E4=B8=AD=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E8=8E=B7=E5=8F=96=E5=A5=97=E9=A4=90=E5=9C=B0=E5=9D=80?= =?UTF-8?q?=E5=92=8C=E4=B8=8A=E6=8A=A5=E5=A2=9E=E5=80=BC=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E8=B4=AD=E4=B9=B0=E8=AF=B7=E6=B1=82=E7=9A=84=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=EF=BC=8C=E5=B9=B6=E5=9C=A8=E8=A7=86=E9=A2=91=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=E4=B8=AD=E9=9B=86=E6=88=90=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/sdk.js | 18 ++++++++++++++++++ pages/p2p/videoLog.vue | 25 +++++++++++++++++-------- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/api/sdk.js b/api/sdk.js index c4dc3f4..210c5a4 100644 --- a/api/sdk.js +++ b/api/sdk.js @@ -28,3 +28,21 @@ export function deleteVideo(data) { data }) } + +// 获取套餐地址 +export function getPackageUrl(data) { + return request({ + url: '/v2/service/getPackageUrl', + method: 'POST', + data + }) +} + +// 上报增值服务购买请求 +export function reportBuyRequest(data) { + return request({ + url: '/service/reportBuyRequest', + method: 'POST', + data + }) +} diff --git a/pages/p2p/videoLog.vue b/pages/p2p/videoLog.vue index 4cff9e5..c004a54 100644 --- a/pages/p2p/videoLog.vue +++ b/pages/p2p/videoLog.vue @@ -113,7 +113,7 @@ import { onMounted, ref, computed } from 'vue' import { onShow } from '@dcloudio/uni-app' import { useBluetoothStore } from '@/stores/bluetooth' - import { passthrough } from '@/api/sdk' + import { passthrough, getPackageUrl, reportBuyRequest } from '@/api/sdk' import { useBasicStore } from '@/stores/basic' const $bluetooth = useBluetoothStore() @@ -157,13 +157,22 @@ }) } - const toWebview = () => { - $basic.routeJump({ - name: 'webview', - params: { - url: encodeURIComponent('https://www.baidu.com') - } - }) + const toWebview = async () => { + const result = await Promise.all([ + getPackageUrl({}), + reportBuyRequest({ type: 'cloud_storage', lockId: $bluetooth.currentLockInfo.lockId }) + ]) + + const urls = result[0].data + + if (urls?.cloud_storage) { + $basic.routeJump({ + name: 'webview', + params: { + url: encodeURIComponent(urls.cloud_storage) + } + }) + } } const resetLog = async () => { From 8b542a4d9cc1260159c00dbf7eabbb198c1bf840 Mon Sep 17 00:00:00 2001 From: fanpeng <438123081@qq.com> Date: Fri, 6 Jun 2025 13:50:48 +0800 Subject: [PATCH 5/5] =?UTF-8?q?feat:=20=E5=AE=8C=E6=88=90=E5=BF=98?= =?UTF-8?q?=E8=AE=B0=E5=AF=86=E7=A0=81=E7=9B=B8=E5=85=B3=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/user.js | 11 +- pages/user/forgotPassword.vue | 238 +++++++++++++++++++++++++++++++++- pages/user/register.vue | 4 +- stores/user.js | 5 +- 4 files changed, 251 insertions(+), 7 deletions(-) diff --git a/api/user.js b/api/user.js index 17802c1..c927885 100644 --- a/api/user.js +++ b/api/user.js @@ -68,7 +68,7 @@ export function loginRequest(data) { // 密码登录 export function passwordLoginRequest(data) { return request({ - url: '/user/register', + url: '/user/login', method: 'POST', data }) @@ -83,6 +83,15 @@ export function registerRequest(data) { }) } +// 重置密码 +export function resetPasswordRequest(data) { + return request({ + url: '/user/resetPassword', + method: 'POST', + data + }) +} + // 注册 export function phoneLoginRequest(data) { return request({ diff --git a/pages/user/forgotPassword.vue b/pages/user/forgotPassword.vue index 1e8e1a1..5ee60d9 100644 --- a/pages/user/forgotPassword.vue +++ b/pages/user/forgotPassword.vue @@ -1,5 +1,239 @@ - + diff --git a/pages/user/register.vue b/pages/user/register.vue index 741e8ee..cd0aff3 100644 --- a/pages/user/register.vue +++ b/pages/user/register.vue @@ -51,7 +51,7 @@ padding: '0', height: '80rpx' }" - placeholder="请输入内容" + placeholder="请输入密码" type="password" border="bottom" clearable @@ -95,7 +95,7 @@ height: '80rpx' }" placeholder="请输入验证码" - type="password" + type="number" border="bottom" clearable v-model="code" diff --git a/stores/user.js b/stores/user.js index 7fd0131..20ec8d2 100644 --- a/stores/user.js +++ b/stores/user.js @@ -7,7 +7,8 @@ import { getWebUrlRequest, loginRequest, phoneLoginRequest, - registerRequest + registerRequest, + passwordLoginRequest } from '@/api/user' import { useLockStore } from '@/stores/lock' import { setStorage, getStorage } from '@/utils/storage' @@ -82,7 +83,7 @@ export const useUserStore = defineStore('user', { model: deviceInfo.model, system: deviceInfo.system } - const { code, data, message } = await registerRequest({ + const { code, data, message } = await passwordLoginRequest({ username, password, countryCode,