feat: 添加用户注册和密码登录功能,更新页面路由和样式
This commit is contained in:
parent
a38e1bd4e6
commit
77d24ecb54
2
App.vue
2
App.vue
@ -29,7 +29,7 @@
|
||||
if (this.envVersion === 'trial') {
|
||||
return 'XHJ'
|
||||
}
|
||||
return 'XHJ'
|
||||
return 'DEV'
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
||||
12
api/system.js
Normal file
12
api/system.js
Normal file
@ -0,0 +1,12 @@
|
||||
import request from '../utils/request'
|
||||
|
||||
// system 系统模块
|
||||
|
||||
// 获取国家列表
|
||||
export function getCountryListRequest(data) {
|
||||
return request({
|
||||
url: '/system/listCountry',
|
||||
method: 'POST',
|
||||
data
|
||||
})
|
||||
}
|
||||
18
api/user.js
18
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({
|
||||
|
||||
15
components/verify/utils/ase.js
Normal file
15
components/verify/utils/ase.js
Normal file
@ -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()
|
||||
}
|
||||
43
components/verify/utils/request.js
Normal file
43
components/verify/utils/request.js
Normal file
@ -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'))
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
476
components/verify/verify.vue
Normal file
476
components/verify/verify.vue
Normal file
File diff suppressed because one or more lines are too long
781
components/verify/verifySlider/verifySlider.vue
Normal file
781
components/verify/verifySlider/verifySlider.vue
Normal file
File diff suppressed because one or more lines are too long
3
constant/reg.js
Normal file
3
constant/reg.js
Normal file
@ -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
|
||||
7
package-lock.json
generated
7
package-lock.json
generated
@ -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",
|
||||
|
||||
@ -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"
|
||||
|
||||
45
pages.json
45
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": {
|
||||
|
||||
@ -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',
|
||||
|
||||
97
pages/user/countryList.vue
Normal file
97
pages/user/countryList.vue
Normal file
@ -0,0 +1,97 @@
|
||||
<template>
|
||||
<view class="flex flex-col" style="height: 100vh">
|
||||
<view class="flex-shrink-0 p-4">
|
||||
<up-input
|
||||
v-model="searchKeyword"
|
||||
placeholder="搜索"
|
||||
:customStyle="{
|
||||
padding: '0 16rpx',
|
||||
height: '80rpx',
|
||||
backgroundColor: '#f4f4f4'
|
||||
}"
|
||||
border="surround"
|
||||
clearable
|
||||
@input="onSearch"
|
||||
></up-input>
|
||||
</view>
|
||||
|
||||
<view class="flex-1" style="overflow: hidden">
|
||||
<up-index-list :index-list="indexList" :sticky="true">
|
||||
<up-index-item
|
||||
v-for="(item, index) in countryList"
|
||||
:key="index"
|
||||
v-show="getFilteredGroup(item).length > 0"
|
||||
>
|
||||
<up-index-anchor :text="indexList[index]"></up-index-anchor>
|
||||
<view
|
||||
v-for="country in getFilteredGroup(item)"
|
||||
:key="country.id"
|
||||
class="px-4 py-2.5 border-b border-gray-2"
|
||||
@click="handleCountryClick(country)"
|
||||
>
|
||||
{{ country.name }}
|
||||
</view>
|
||||
</up-index-item>
|
||||
<view style="padding-bottom: calc(300rpx + env(safe-area-inset-bottom))"></view>
|
||||
</up-index-list>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, getCurrentInstance } from 'vue'
|
||||
import { getCountryListRequest } from '@/api/system'
|
||||
|
||||
const instance = getCurrentInstance().proxy
|
||||
const eventChannel = instance.getOpenerEventChannel()
|
||||
|
||||
const indexList = ref(['★', ...Array.from({ length: 26 }, (_, i) => String.fromCharCode(65 + i))])
|
||||
|
||||
const countryList = ref(new Array(27).fill().map(() => []))
|
||||
const searchKeyword = ref('')
|
||||
|
||||
const getFilteredGroup = group => {
|
||||
if (!searchKeyword.value) return group
|
||||
const keyword = searchKeyword.value.toLowerCase()
|
||||
return group.filter(country => country.name.toLowerCase().includes(keyword))
|
||||
}
|
||||
|
||||
const onSearch = () => {}
|
||||
|
||||
const getCountryList = async () => {
|
||||
uni.showLoading({ title: '加载中...' })
|
||||
try {
|
||||
const { code, data, message } = await getCountryListRequest()
|
||||
if (code === 0) {
|
||||
const newGroups = data
|
||||
.filter(item => !indexList.value.includes(item.group))
|
||||
.map(item => item.group)
|
||||
.filter((group, index, arr) => arr.indexOf(group) === index)
|
||||
|
||||
indexList.value = [...indexList.value, ...newGroups]
|
||||
countryList.value = new Array(indexList.value.length).fill().map(() => [])
|
||||
|
||||
if (data.length > 0) countryList.value[0].push(data[0])
|
||||
|
||||
data.forEach(item => {
|
||||
const groupIndex = indexList.value.indexOf(item.group)
|
||||
if (groupIndex !== -1) countryList.value[groupIndex].push(item)
|
||||
})
|
||||
} else {
|
||||
uni.showToast({ title: message, icon: 'none' })
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
uni.showToast({ title: '获取国家列表失败', icon: 'none' })
|
||||
} finally {
|
||||
uni.hideLoading()
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => getCountryList())
|
||||
|
||||
const handleCountryClick = country => {
|
||||
eventChannel.emit('country', country)
|
||||
uni.navigateBack()
|
||||
}
|
||||
</script>
|
||||
5
pages/user/forgotPassword.vue
Normal file
5
pages/user/forgotPassword.vue
Normal file
@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<view>忘记密码</view>
|
||||
</template>
|
||||
|
||||
<script setup></script>
|
||||
170
pages/user/login.vue
Normal file
170
pages/user/login.vue
Normal file
@ -0,0 +1,170 @@
|
||||
<template>
|
||||
<view>
|
||||
<view class="mx-4 mt-10 text-base">
|
||||
<view class="font-bold text-2xl">欢迎使用星星锁</view>
|
||||
<view class="mt-4 flex items-center w-full" @click="toJump('countryList')">
|
||||
<view class="w-200">国家/地区</view>
|
||||
<view class="text-#63b8af">{{ country.name }} +{{ country.code }}</view>
|
||||
</view>
|
||||
<view class="mt-4">
|
||||
<up-input
|
||||
fontSize="32rpx"
|
||||
:placeholderStyle="{
|
||||
color: '#333333',
|
||||
fontSize: '32rpx'
|
||||
}"
|
||||
placeholder="请输入手机号或邮箱"
|
||||
:customStyle="{
|
||||
padding: '0',
|
||||
height: '80rpx'
|
||||
}"
|
||||
border="bottom"
|
||||
clearable
|
||||
v-model="username"
|
||||
:maxlength="50"
|
||||
@change="handleUsernameInput"
|
||||
></up-input>
|
||||
</view>
|
||||
<view class="mt-4">
|
||||
<up-input
|
||||
fontSize="32rpx"
|
||||
:placeholderStyle="{
|
||||
color: '#333333',
|
||||
fontSize: '32rpx'
|
||||
}"
|
||||
:customStyle="{
|
||||
padding: '0',
|
||||
height: '80rpx'
|
||||
}"
|
||||
placeholder="请输入内容"
|
||||
type="password"
|
||||
border="bottom"
|
||||
clearable
|
||||
v-model="password"
|
||||
:maxlength="20"
|
||||
@change="handlePasswordInput"
|
||||
></up-input>
|
||||
</view>
|
||||
<view class="mt-4 flex items-center text-sm" @click="agreeAgreement">
|
||||
<image
|
||||
class="w-35 h-35"
|
||||
v-if="select"
|
||||
src="https://oss-lock.xhjcn.ltd/mp/icon_select.png"
|
||||
></image>
|
||||
<image
|
||||
v-else
|
||||
class="w-35 h-35"
|
||||
src="https://oss-lock.xhjcn.ltd/mp/icon_not_select.png"
|
||||
></image>
|
||||
<view class="ml-2">
|
||||
我已阅读并同意
|
||||
<span class="text-#63b8af" @click="toWebview('userAgreement')">《用户协议》 </span>
|
||||
<span class="text-#63b8af" @click="toWebview('privacy')">《隐私协议》</span>
|
||||
</view>
|
||||
</view>
|
||||
<view
|
||||
class="mt-4 w-full rounded-full h-88 text-center leading-[88rpx] text-white"
|
||||
:class="[canLogin ? 'bg-#63b8af' : 'bg-#9d9da1']"
|
||||
@click="login"
|
||||
>
|
||||
登录
|
||||
</view>
|
||||
<view class="mt-4 mx-2 flex items-center justify-between">
|
||||
<view class="text-#63b8af" @click="toJump('forgotPassword')">忘记密码</view>
|
||||
<view class="text-#63b8af" @click="toJump('register')">注册</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import { useBasicStore } from '@/stores/basic'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { PHONE_REG, EMAIL_REG, PASSWORD_REG } from '@/constant/reg'
|
||||
|
||||
const $basic = useBasicStore()
|
||||
const $user = useUserStore()
|
||||
|
||||
const country = ref({
|
||||
countryId: 0,
|
||||
name: '中国',
|
||||
code: '86',
|
||||
flag: 'https://lock.xhjcn.ltd/storage/country-flags/86.png',
|
||||
abbreviation: 'CN',
|
||||
group: 'Z'
|
||||
})
|
||||
const username = ref('')
|
||||
const password = ref('')
|
||||
|
||||
const select = ref(false)
|
||||
|
||||
const isValidUsername = computed(() => {
|
||||
if (!username.value) return false
|
||||
return PHONE_REG.test(username.value) || EMAIL_REG.test(username.value)
|
||||
})
|
||||
|
||||
const isValidPassword = computed(() => {
|
||||
if (!password.value) return false
|
||||
return PASSWORD_REG.test(password.value)
|
||||
})
|
||||
|
||||
const canLogin = computed(() => {
|
||||
return isValidUsername.value && isValidPassword.value
|
||||
})
|
||||
|
||||
const handleUsernameInput = text => {
|
||||
username.value = text
|
||||
}
|
||||
const handlePasswordInput = text => {
|
||||
password.value = text
|
||||
}
|
||||
|
||||
const agreeAgreement = () => {
|
||||
select.value = !select.value
|
||||
}
|
||||
|
||||
const toJump = path => {
|
||||
$basic.routeJump({
|
||||
name: path,
|
||||
events: {
|
||||
country(data) {
|
||||
country.value = data
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const toWebview = type => {
|
||||
$basic.routeJump({
|
||||
name: 'webview',
|
||||
params: {
|
||||
type
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const login = async () => {
|
||||
if (!canLogin.value) {
|
||||
return
|
||||
}
|
||||
if (!select.value) {
|
||||
uni.showToast({
|
||||
title: '请先同意用户协议及隐私协议',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
const res = await $user.passwordLogin({
|
||||
username: username.value,
|
||||
password: password.value,
|
||||
countryCode: country.value.code
|
||||
})
|
||||
if (res) {
|
||||
$basic.routeJump({
|
||||
name: 'home',
|
||||
type: 'reLaunch'
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
317
pages/user/register.vue
Normal file
317
pages/user/register.vue
Normal file
@ -0,0 +1,317 @@
|
||||
<template>
|
||||
<view>
|
||||
<up-tabs
|
||||
:list="list"
|
||||
@click="click"
|
||||
class="mx-4"
|
||||
lineWidth="300rpx"
|
||||
lineColor="#63b8af"
|
||||
:activeStyle="{
|
||||
color: '#63b8af',
|
||||
fontWeight: 'bold',
|
||||
transform: 'scale(1.05)'
|
||||
}"
|
||||
itemStyle="width: 343rpx;height: 108rpx;font-size: 32rpx"
|
||||
></up-tabs>
|
||||
<view class="mx-6 mt-10">
|
||||
<view class="mt-4 flex items-center w-full" @click="toJump('countryList')">
|
||||
<view class="w-200">国家/地区</view>
|
||||
<view class="text-#63b8af">
|
||||
{{ country.name }}
|
||||
<span v-if="type === 1">+{{ country.code }}</span>
|
||||
</view>
|
||||
</view>
|
||||
<view class="mt-4">
|
||||
<up-input
|
||||
:placeholder="type === 1 ? '请输入手机号' : '请输入邮箱'"
|
||||
fontSize="32rpx"
|
||||
:placeholderStyle="{
|
||||
color: '#333333',
|
||||
fontSize: '32rpx'
|
||||
}"
|
||||
:customStyle="{
|
||||
padding: '0',
|
||||
height: '80rpx'
|
||||
}"
|
||||
border="bottom"
|
||||
clearable
|
||||
v-model="username"
|
||||
:maxlength="50"
|
||||
@change="handleUsernameInput"
|
||||
></up-input>
|
||||
</view>
|
||||
<view class="mt-4">
|
||||
<up-input
|
||||
fontSize="32rpx"
|
||||
:placeholderStyle="{
|
||||
color: '#333333',
|
||||
fontSize: '32rpx'
|
||||
}"
|
||||
:customStyle="{
|
||||
padding: '0',
|
||||
height: '80rpx'
|
||||
}"
|
||||
placeholder="请输入内容"
|
||||
type="password"
|
||||
border="bottom"
|
||||
clearable
|
||||
v-model="password"
|
||||
:maxlength="20"
|
||||
@change="handlePasswordInput"
|
||||
></up-input>
|
||||
<view class="text-#999999 text-sm mt-1">
|
||||
密码必须是8-20位,至少包括数字/字母/符号中的2种
|
||||
</view>
|
||||
</view>
|
||||
<view class="mt-4">
|
||||
<up-input
|
||||
fontSize="32rpx"
|
||||
:placeholderStyle="{
|
||||
color: '#333333',
|
||||
fontSize: '32rpx'
|
||||
}"
|
||||
:customStyle="{
|
||||
padding: '0',
|
||||
height: '80rpx'
|
||||
}"
|
||||
placeholder="确认密码"
|
||||
type="password"
|
||||
border="bottom"
|
||||
clearable
|
||||
v-model="confirmPassword"
|
||||
:maxlength="20"
|
||||
@change="handleConfirmPasswordInput"
|
||||
></up-input>
|
||||
</view>
|
||||
<view class="mt-4 flex items-center">
|
||||
<up-input
|
||||
fontSize="32rpx"
|
||||
:placeholderStyle="{
|
||||
color: '#333333',
|
||||
fontSize: '32rpx'
|
||||
}"
|
||||
:customStyle="{
|
||||
padding: '0',
|
||||
height: '80rpx'
|
||||
}"
|
||||
placeholder="请输入验证码"
|
||||
type="password"
|
||||
border="bottom"
|
||||
clearable
|
||||
v-model="code"
|
||||
:maxlength="20"
|
||||
@change="handleCodeInput"
|
||||
>
|
||||
<template #suffix>
|
||||
<up-button @tap="getCode" :disabled="!canGetCode" color="#63b8af">
|
||||
{{ tips }}
|
||||
</up-button>
|
||||
<up-code
|
||||
:seconds="seconds"
|
||||
ref="uCodeRef"
|
||||
changeText="X秒后重新获取"
|
||||
endText="获取验证码"
|
||||
@change="codeChange"
|
||||
></up-code
|
||||
></template>
|
||||
</up-input>
|
||||
</view>
|
||||
|
||||
<view class="my-6 flex items-center text-sm" @click="agreeAgreement">
|
||||
<image
|
||||
class="w-35 h-35"
|
||||
v-if="select"
|
||||
src="https://oss-lock.xhjcn.ltd/mp/icon_select.png"
|
||||
></image>
|
||||
<image
|
||||
v-else
|
||||
class="w-35 h-35"
|
||||
src="https://oss-lock.xhjcn.ltd/mp/icon_not_select.png"
|
||||
></image>
|
||||
<view class="ml-2">
|
||||
我已阅读并同意
|
||||
<span class="text-#63b8af" @click="toWebview('userAgreement')">《用户协议》 </span>
|
||||
<span class="text-#63b8af" @click="toWebview('privacy')">《隐私协议》</span>
|
||||
</view>
|
||||
</view>
|
||||
<view
|
||||
class="w-full rounded-full h-88 text-center leading-[88rpx] text-white"
|
||||
:class="[canRegister ? 'bg-#63b8af' : 'bg-#9d9da1']"
|
||||
@click="register"
|
||||
>
|
||||
注册
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { useBasicStore } from '@/stores/basic'
|
||||
import { PHONE_REG, EMAIL_REG, PASSWORD_REG } from '@/constant/reg'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
|
||||
const $basic = useBasicStore()
|
||||
const $user = useUserStore()
|
||||
|
||||
const list = ref([
|
||||
{ name: '手机', receiverType: 1 },
|
||||
{ name: '邮箱', receiverType: 2 }
|
||||
])
|
||||
|
||||
const type = ref(1)
|
||||
const username = ref('')
|
||||
const password = ref('')
|
||||
const confirmPassword = ref('')
|
||||
const code = ref('')
|
||||
|
||||
const tips = ref('')
|
||||
const seconds = ref(120)
|
||||
const uCodeRef = ref(null)
|
||||
const select = ref(false)
|
||||
|
||||
onMounted(async () => {
|
||||
const res = await $basic.getDeviceInfo()
|
||||
if (res.language !== 'zh-CN') {
|
||||
list.value.reverse()
|
||||
type.value = 2
|
||||
}
|
||||
})
|
||||
|
||||
const codeChange = text => {
|
||||
tips.value = text
|
||||
}
|
||||
|
||||
const getCode = () => {
|
||||
if (!uCodeRef.value.canGetCode) {
|
||||
return
|
||||
}
|
||||
const params = {
|
||||
account: username.value,
|
||||
codeType: 1,
|
||||
channel: type.value,
|
||||
countryCode: country.value.code
|
||||
}
|
||||
$basic.routeJump({
|
||||
name: 'safeVerify',
|
||||
events: {
|
||||
successEvent() {
|
||||
uCodeRef.value.start()
|
||||
}
|
||||
},
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
const country = ref({
|
||||
countryId: 0,
|
||||
name: '中国',
|
||||
code: '86',
|
||||
flag: 'https://lock.xhjcn.ltd/storage/country-flags/86.png',
|
||||
abbreviation: 'CN',
|
||||
group: 'Z'
|
||||
})
|
||||
|
||||
const isValidUsername = computed(() => {
|
||||
if (!username.value) return false
|
||||
if (type.value === 1) {
|
||||
return PHONE_REG.test(username.value)
|
||||
}
|
||||
return EMAIL_REG.test(username.value)
|
||||
})
|
||||
|
||||
const isValidPassword = computed(() => {
|
||||
if (!password.value) return false
|
||||
return PASSWORD_REG.test(password.value)
|
||||
})
|
||||
|
||||
const isValidConfirmPassword = computed(() => {
|
||||
if (!confirmPassword.value) return false
|
||||
return confirmPassword.value === password.value
|
||||
})
|
||||
|
||||
const isValidCode = computed(() => {
|
||||
if (!code.value) return false
|
||||
return code.value.length === 6
|
||||
})
|
||||
|
||||
const canGetCode = computed(() => {
|
||||
return isValidUsername.value
|
||||
})
|
||||
|
||||
const canRegister = computed(() => {
|
||||
return (
|
||||
isValidUsername.value &&
|
||||
isValidPassword.value &&
|
||||
isValidConfirmPassword.value &&
|
||||
isValidCode.value
|
||||
)
|
||||
})
|
||||
|
||||
const agreeAgreement = () => {
|
||||
select.value = !select.value
|
||||
}
|
||||
|
||||
const register = async () => {
|
||||
if (!canRegister.value) {
|
||||
return
|
||||
}
|
||||
if (!select.value) {
|
||||
uni.showToast({
|
||||
title: '请先同意用户协议及隐私协议',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
const res = await $user.register({
|
||||
account: username.value,
|
||||
password: password.value,
|
||||
receiverType: type.value,
|
||||
countryCode: country.value.code,
|
||||
verificationCode: code.value
|
||||
})
|
||||
if (res) {
|
||||
$basic.routeJump({
|
||||
name: 'home',
|
||||
type: 'reLaunch'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const click = item => {
|
||||
type.value = item.receiverType
|
||||
}
|
||||
|
||||
const handleUsernameInput = text => {
|
||||
username.value = text
|
||||
}
|
||||
const handlePasswordInput = text => {
|
||||
password.value = text
|
||||
}
|
||||
const handleConfirmPasswordInput = text => {
|
||||
confirmPassword.value = text
|
||||
}
|
||||
const handleCodeInput = text => {
|
||||
code.value = text
|
||||
}
|
||||
|
||||
const toJump = path => {
|
||||
$basic.routeJump({
|
||||
name: path,
|
||||
events: {
|
||||
country(data) {
|
||||
country.value = data
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const toWebview = type => {
|
||||
$basic.routeJump({
|
||||
name: 'webview',
|
||||
params: {
|
||||
type
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
52
pages/user/safeVerify.vue
Normal file
52
pages/user/safeVerify.vue
Normal file
@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<view>
|
||||
<view class="main">
|
||||
<view v-if="params" class="content">
|
||||
<verify
|
||||
:imgSize="{ width: '330px', height: '155px' }"
|
||||
:mode="'fixed'"
|
||||
:params="params"
|
||||
captchaType="blockPuzzle"
|
||||
@success="success"
|
||||
></verify>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { getCurrentInstance, ref } from 'vue'
|
||||
import verify from '@/components/verify/verify.vue'
|
||||
|
||||
const instance = getCurrentInstance().proxy
|
||||
const eventChannel = instance.getOpenerEventChannel()
|
||||
|
||||
const params = ref(null)
|
||||
|
||||
onLoad(async options => {
|
||||
params.value = options
|
||||
})
|
||||
|
||||
const success = result => {
|
||||
eventChannel.emit('successEvent', result || {})
|
||||
uni.navigateBack()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background: $uni-bg-color;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.main {
|
||||
margin-top: 32rpx;
|
||||
text-align: center;
|
||||
|
||||
.content {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -246,47 +246,47 @@
|
||||
|
||||
<style scoped lang="scss">
|
||||
.view {
|
||||
margin-top: 32rpx;
|
||||
border-radius: 32rpx;
|
||||
width: 710rpx;
|
||||
margin-top: 32rpx;
|
||||
margin-left: 20rpx;
|
||||
background: #ffffff;
|
||||
border-radius: 32rpx;
|
||||
}
|
||||
|
||||
.view-button {
|
||||
padding: 0 20rpx 0 40rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
color: #292826;
|
||||
justify-content: space-between;
|
||||
padding: 0 20rpx 0 40rpx;
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
line-height: 80rpx;
|
||||
color: #292826;
|
||||
}
|
||||
|
||||
.info {
|
||||
text-align: right;
|
||||
width: 400rpx;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
margin-right: 20rpx;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
text-align: right;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.name-info {
|
||||
width: 520rpx;
|
||||
margin-right: 20rpx;
|
||||
overflow: hidden;
|
||||
line-height: 40rpx;
|
||||
text-align: right;
|
||||
width: 520rpx;
|
||||
overflow: hidden;
|
||||
word-break: break-all;
|
||||
margin-right: 20rpx;
|
||||
}
|
||||
|
||||
.red-dot {
|
||||
margin-right: 20rpx;
|
||||
background: #ec433c;
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
margin-right: 20rpx;
|
||||
background: #ec433c;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
@ -304,9 +304,9 @@
|
||||
.avatar {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
border-radius: 50%;
|
||||
margin-right: 20rpx;
|
||||
margin-top: 20rpx;
|
||||
margin-right: 20rpx;
|
||||
margin-bottom: 20rpx;
|
||||
border-radius: 50%;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -406,6 +406,31 @@ 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
|
||||
}
|
||||
]
|
||||
|
||||
@ -495,11 +520,13 @@ export const useBasicStore = defineStore('basic', {
|
||||
// 获取胶囊信息
|
||||
getButtonInfo() {
|
||||
return new Promise(resolve => {
|
||||
// #ifdef MP-WEIXIN
|
||||
if (this.buttonInfo?.top) {
|
||||
resolve(this.buttonInfo)
|
||||
return
|
||||
}
|
||||
this.buttonInfo = uni.getMenuButtonBoundingClientRect()
|
||||
// #endif
|
||||
resolve(this.buttonInfo)
|
||||
})
|
||||
},
|
||||
|
||||
@ -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')
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user