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')