mirror of
https://gitee.com/bootx/dax-pay-ui.git
synced 2025-09-04 19:28:05 +00:00
feat 开放平台绑定功能优化, 用户注册
This commit is contained in:
47
src/api/sys/login.ts
Normal file
47
src/api/sys/login.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { defHttp } from '/@/utils/http/axios'
|
||||
import { LoginParams } from './model/userModel'
|
||||
import { Result } from '/#/axios'
|
||||
|
||||
/**
|
||||
* 登录接口 返回token
|
||||
*/
|
||||
export function login(params: LoginParams) {
|
||||
return defHttp.post<Result<string>>({
|
||||
url: '/token/login',
|
||||
params,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取微信扫码登录二维码
|
||||
*/
|
||||
export function applyQrCode() {
|
||||
return defHttp.get<Result<WeChatLoginQrCode>>({
|
||||
url: `/token/wechat/qr/applyQrCode`,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取扫码状态
|
||||
*/
|
||||
export function getQrStatus(qrCodeKey) {
|
||||
return defHttp.get<Result<string>>({
|
||||
url: `/token/wechat/qr/getStatus`,
|
||||
params: { qrCodeKey },
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 退出
|
||||
*/
|
||||
export function doLogout() {
|
||||
return defHttp.post({ url: '/token/logout' })
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录二维码
|
||||
*/
|
||||
export interface WeChatLoginQrCode {
|
||||
qrCodeKey: string
|
||||
qrCodeUrl: string
|
||||
}
|
@@ -1,17 +1,7 @@
|
||||
import { defHttp } from '/@/utils/http/axios'
|
||||
import { LoginParams, GetUserInfoModel } from './model/userModel'
|
||||
import { GetUserInfoModel } from './model/userModel'
|
||||
import { Result } from '/#/axios'
|
||||
|
||||
/**
|
||||
* 登录接口 返回token
|
||||
*/
|
||||
export function loginApi(params: LoginParams) {
|
||||
return defHttp.post<Result<string>>({
|
||||
url: '/token/login',
|
||||
params,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录后获取用户信息
|
||||
*/
|
||||
@@ -20,8 +10,21 @@ export function getUserInfo() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 退出
|
||||
* 注册
|
||||
*/
|
||||
export function doLogout() {
|
||||
return defHttp.post({ url: '/token/logout' })
|
||||
export function register(obj) {
|
||||
return defHttp.post({
|
||||
url: `/user/register`,
|
||||
data: obj,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置密码
|
||||
*/
|
||||
export function forgetPasswordByPhone(obj) {
|
||||
return defHttp.post({
|
||||
url: `/user/forgetPasswordByPhone`,
|
||||
data: obj,
|
||||
})
|
||||
}
|
||||
|
@@ -28,7 +28,7 @@
|
||||
|
||||
const { currentCount, isStart, start, reset } = useCountdown(props.count)
|
||||
const { t } = useI18n()
|
||||
|
||||
// 优化
|
||||
const getButtonText = computed(() => {
|
||||
return !unref(isStart) ? t('component.countdown.normalText') : t('component.countdown.sendText', [unref(currentCount)])
|
||||
})
|
||||
|
@@ -98,12 +98,13 @@ export function createPermissionGuard(router: Router) {
|
||||
return
|
||||
}
|
||||
//TODO 添加 websocket连接.
|
||||
console.log(`路由守卫`)
|
||||
|
||||
// 重载菜单
|
||||
console.log('重载菜单')
|
||||
const routes = await permissionStore.buildRoutesAction()
|
||||
|
||||
// 初始化字典
|
||||
console.log('初始化字典')
|
||||
await useDictStore.initDict()
|
||||
|
||||
routes.forEach((route) => {
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import type { RouteRecordRaw } from 'vue-router'
|
||||
import type { App } from 'vue'
|
||||
|
||||
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||
import { createRouter, createWebHashHistory, createWebHistory } from "vue-router";
|
||||
import { basicRoutes } from './routes'
|
||||
|
||||
// 白名单应该包含基本静态路由
|
||||
@@ -17,7 +17,7 @@ getRouteNames(basicRoutes)
|
||||
// 创建一个可以被 Vue 应用程序使用的路由实例
|
||||
export const router = createRouter({
|
||||
// 创建一个 hash 历史记录。
|
||||
history: createWebHashHistory(import.meta.env.VITE_PUBLIC_PATH),
|
||||
history: createWebHistory(import.meta.env.VITE_PUBLIC_PATH),
|
||||
// 应该添加到路由的初始路由列表。
|
||||
routes: basicRoutes as unknown as RouteRecordRaw[],
|
||||
// 是否应该禁止尾部斜杠。默认为假
|
||||
|
@@ -17,7 +17,7 @@ export const useDictStore = defineStore({
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
async initDict() {
|
||||
initDict() {
|
||||
findAll().then(({ data }) => {
|
||||
this.dict = data.map((o) => {
|
||||
return {
|
||||
|
@@ -6,9 +6,8 @@ import { RoleEnum } from '/@/enums/roleEnum'
|
||||
import { PageEnum } from '/@/enums/pageEnum'
|
||||
import { ROLES_KEY, TOKEN_KEY, USER_INFO_KEY } from '/@/enums/cacheEnum'
|
||||
import { getAuthCache, setAuthCache } from '/@/utils/auth'
|
||||
import { GetUserInfoModel, LoginParams } from '/@/api/sys/model/userModel'
|
||||
import { doLogout, getUserInfo, loginApi } from '/@/api/sys/user'
|
||||
import { useI18n } from '/@/hooks/web/useI18n'
|
||||
import { LoginParams } from '/@/api/sys/model/userModel'
|
||||
import { doLogout, login } from '/@/api/sys/login'
|
||||
import { useMessage } from '/@/hooks/web/useMessage'
|
||||
import { router } from '/@/router'
|
||||
import { usePermissionStore } from '/@/store/modules/permission'
|
||||
@@ -16,6 +15,8 @@ import { RouteRecordRaw } from 'vue-router'
|
||||
import { PAGE_NOT_FOUND_ROUTE } from '/@/router/routes/basic'
|
||||
import { h } from 'vue'
|
||||
import { getFilePreviewUrlPrefix } from '/@/api/common/FileUpload'
|
||||
// @ts-ignore
|
||||
import { getUserInfo } from '/@/api/sys/user'
|
||||
|
||||
interface UserState {
|
||||
userInfo: Nullable<UserInfo>
|
||||
@@ -89,7 +90,7 @@ export const useUserStore = defineStore({
|
||||
*/
|
||||
async login(params: LoginParams) {
|
||||
try {
|
||||
const { data: token } = await loginApi(params)
|
||||
const { data: token } = await login(params)
|
||||
// 保存token
|
||||
this.setToken(token)
|
||||
await this.afterLoginAction(true)
|
||||
|
@@ -18,20 +18,6 @@ export interface UserDetails {
|
||||
clientIdList?: string[]
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录后用户信息
|
||||
*/
|
||||
export interface GetUserInfoModel {
|
||||
// 用户id
|
||||
userId: number
|
||||
// 名称
|
||||
name: string
|
||||
// 账号
|
||||
username: string
|
||||
// 头像
|
||||
avatar: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户基础消息
|
||||
*/
|
||||
|
@@ -56,24 +56,25 @@
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-spin>
|
||||
<we-chat-qr-bind ref="weChatQrBind" @bind="bindWeChatCallback" />
|
||||
</CollapseContainer>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { List } from 'ant-design-vue'
|
||||
import { defineComponent, onMounted } from 'vue'
|
||||
import { onMounted } from 'vue'
|
||||
import { CollapseContainer } from '/@/components/Container'
|
||||
import {} from 'OpenIdLoginType'
|
||||
import { $ref } from 'vue/macros'
|
||||
import { DING_TALK, WE_CHAT, WE_CHAT_OPEN, QQ, WE_COM } from '/@/views/login/third/OpenIdLoginType'
|
||||
import { bindThird, getThirdBindInfo, unbindThird } from '/@/views/account/account.api'
|
||||
import { UserThirdBindInfo } from '/@/views/account/accountModel'
|
||||
import { useMessage } from '/@/hooks/web/useMessage'
|
||||
import { getAppEnvConfig } from "/@/utils/env";
|
||||
import { getAppEnvConfig } from '/@/utils/env'
|
||||
import WeChatQrBind from './WeChatQrBind.vue'
|
||||
|
||||
const { createMessage, createConfirm } = useMessage()
|
||||
|
||||
let loading = $ref(false)
|
||||
let currentLoginType = $ref()
|
||||
let weChatQrBind = $ref<any>()
|
||||
let bindInfo = $ref({
|
||||
weChat: {},
|
||||
weChatOpen: {},
|
||||
@@ -130,6 +131,27 @@
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信公众平台绑定
|
||||
*/
|
||||
function bindWeChat() {
|
||||
weChatQrBind.init()
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信绑定回调
|
||||
*/
|
||||
function bindWeChatCallback(from) {
|
||||
loading = true
|
||||
bindThird(from)
|
||||
.then(() => {
|
||||
createMessage.success('绑定成功')
|
||||
})
|
||||
.finally(() => {
|
||||
init()
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 解绑账号
|
||||
*/
|
||||
|
@@ -1,5 +1,101 @@
|
||||
<template> </template>
|
||||
<template>
|
||||
<basic-modal
|
||||
title="扫码绑定"
|
||||
:footer="null"
|
||||
:can-fullscreen="false"
|
||||
v-bind="$attrs"
|
||||
:width="250"
|
||||
:height="250"
|
||||
:visible="visible"
|
||||
@cancel="handleCancel"
|
||||
>
|
||||
<a-spin :spinning="loading">
|
||||
<qr-code :value="qrCodeUrl" />
|
||||
</a-spin>
|
||||
</basic-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup></script>
|
||||
<script lang="ts" setup>
|
||||
import BasicModal from '/@/components/Modal/src/BasicModal.vue'
|
||||
import { $ref } from 'vue/macros'
|
||||
import { QrCode } from '/@/components/Qrcode'
|
||||
import { applyQrCode, getQrStatus } from '/@/api/sys/login'
|
||||
import { onBeforeUnmount, reactive } from 'vue'
|
||||
import { WE_CHAT } from '/@/views/login/third/OpenIdLoginType'
|
||||
|
||||
let visible = $ref(false)
|
||||
let loading = $ref(false)
|
||||
let qrCodeUrl = $ref('请稍等')
|
||||
let interval: any
|
||||
|
||||
const emits = defineEmits(['bind'])
|
||||
|
||||
const form = reactive({
|
||||
client: '',
|
||||
loginType: WE_CHAT,
|
||||
authCode: '',
|
||||
})
|
||||
|
||||
function init() {
|
||||
visible = true
|
||||
getApplyQrCode()
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取
|
||||
*/
|
||||
function getApplyQrCode() {
|
||||
loading = true
|
||||
applyQrCode().then((res) => {
|
||||
form.authCode = res.data.qrCodeKey
|
||||
qrCodeUrl = res.data.qrCodeUrl
|
||||
loading = false
|
||||
checkQrScanStatus()
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 定时查询扫码情况
|
||||
*/
|
||||
function checkQrScanStatus() {
|
||||
interval = setInterval(() => {
|
||||
getQrStatus(form.authCode).then(({ data }) => {
|
||||
// 成功 进行绑定
|
||||
if (data === 'ok') {
|
||||
bindWeChat()
|
||||
} else if (data === 'expired') {
|
||||
clearInterval(interval)
|
||||
interval = null
|
||||
getApplyQrCode()
|
||||
}
|
||||
})
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定
|
||||
*/
|
||||
function bindWeChat() {
|
||||
emits('bind', {
|
||||
authCode: form.authCode,
|
||||
clientCode: form.loginType,
|
||||
})
|
||||
handleCancel()
|
||||
}
|
||||
/**
|
||||
* 关闭
|
||||
*/
|
||||
function handleCancel() {
|
||||
clearInterval(interval)
|
||||
interval = null
|
||||
visible = false
|
||||
}
|
||||
onBeforeUnmount(() => {
|
||||
handleCancel()
|
||||
})
|
||||
defineExpose({
|
||||
init,
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
|
@@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<basic-modal
|
||||
title="邮箱绑定"
|
||||
v-bind="$attrs"
|
||||
:loading="confirmLoading"
|
||||
:width="640"
|
||||
title="邮箱绑定"
|
||||
:visible="visible"
|
||||
@ok="handleOk"
|
||||
@cancel="handleCancel"
|
||||
@@ -17,10 +17,11 @@
|
||||
:label-col="labelCol"
|
||||
:wrapper-col="wrapperCol"
|
||||
>
|
||||
<a-form-item label="邮箱号" name="email">
|
||||
<a-form-item validateFirst label="邮箱号" name="email">
|
||||
<a-input v-model:value="form.email" placeholder="邮箱" />
|
||||
</a-form-item>
|
||||
<a-form-item label="验证码" name="captcha">
|
||||
<a-form-item validateFirst label="验证码" name="captcha">
|
||||
<count-down-input v-model:value="form.captcha" :send-code-api="sendEmailCaptcha" :count="120">获取验证码</count-down-input>
|
||||
<a-input :maxLength="8" placeholder="验证码" v-model:value="form.captcha">
|
||||
<template #addonAfter>
|
||||
<a-button size="small" type="link" :disabled="state.newCaptcha" href="javascript:" @click="sendEmailCaptcha">
|
||||
@@ -44,6 +45,7 @@
|
||||
import { useMessage } from '/@/hooks/web/useMessage'
|
||||
import { validateEmail } from '/@/utils/validate'
|
||||
import { bindEmail } from '/@/views/account/account.api'
|
||||
import CountDownInput from "/@/components/CountDown/src/CountdownInput.vue";
|
||||
|
||||
const emits = defineEmits(['ok'])
|
||||
const { visible, confirmLoading, modalWidth, labelCol, wrapperCol, handleCancel } = useFormEdit()
|
||||
@@ -95,9 +97,6 @@
|
||||
*/
|
||||
function validateEmailRule() {
|
||||
const { email } = form
|
||||
if (!email) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
const { msg, result } = validateEmail(email)
|
||||
return result ? Promise.resolve() : Promise.reject(msg)
|
||||
}
|
||||
@@ -106,14 +105,6 @@
|
||||
*/
|
||||
async function validateBindEmail() {
|
||||
const { email } = form
|
||||
if (!email) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
const { msg, result } = validateEmail(email)
|
||||
// 邮箱号验证
|
||||
if (!result) {
|
||||
return Promise.reject(msg)
|
||||
}
|
||||
const { data } = await existsEmail(email)
|
||||
return data ? Promise.reject('邮箱已被使用') : Promise.resolve()
|
||||
}
|
||||
@@ -122,9 +113,6 @@
|
||||
*/
|
||||
async function validateCaptcha() {
|
||||
const { captcha } = form
|
||||
if (!captcha) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
const { data } = await validateEmailChangeCaptcha(form.email, captcha)
|
||||
return data ? Promise.resolve() : Promise.reject('验证码错误')
|
||||
}
|
||||
|
@@ -5,30 +5,25 @@
|
||||
<a-step title="验证邮箱" />
|
||||
<a-step title="绑定新邮箱" />
|
||||
</a-steps>
|
||||
<a-form ref="formRef" :model="form" :rules="rules" :label-col="labelCol" :wrapper-col="wrapperCol">
|
||||
<a-form-item label="邮箱" v-show="currentTab === 0">
|
||||
<a-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
:validate-trigger="['blur', 'change']"
|
||||
:label-col="labelCol"
|
||||
:wrapper-col="wrapperCol"
|
||||
>
|
||||
<a-form-item label="邮箱" v-show="currentTab === 0" validateFirst>
|
||||
<span>{{ email }}</span>
|
||||
</a-form-item>
|
||||
<a-form-item label="验证码" name="oldCaptcha" v-show="currentTab === 0">
|
||||
<a-input :maxLength="8" placeholder="验证码" v-model:value="form.oldCaptcha">
|
||||
<template #addonAfter>
|
||||
<a-button size="small" type="link" :disabled="state.oldCaptcha" href="javascript:" @click="sendOldEmailCaptcha">
|
||||
{{ (!state.oldCaptcha && '获取验证码') || '请等待 ' + (state.oldCaptchaTime + ' s') }}
|
||||
</a-button>
|
||||
</template>
|
||||
</a-input>
|
||||
<a-form-item validate-first label="验证码" name="oldCaptcha" v-show="currentTab === 0">
|
||||
<count-down-input v-model:value="form.oldCaptcha" :send-code-api="sendOldEmailCaptcha" :count="120">获取验证码1</count-down-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="邮箱" name="email" v-show="currentTab === 1">
|
||||
<a-form-item validate-first label="邮箱" name="email" v-show="currentTab === 1">
|
||||
<a-input v-model:value="form.email" placeholder="邮箱" />
|
||||
</a-form-item>
|
||||
<a-form-item label="验证码" name="newCaptcha" v-show="currentTab === 1">
|
||||
<a-input :maxLength="8" placeholder="验证码" v-model:value="form.newCaptcha">
|
||||
<template #addonAfter>
|
||||
<a-button size="small" type="link" :disabled="state.newCaptcha" href="javascript:" @click="sendNewEmailCaptcha">
|
||||
{{ (!state.newCaptcha && '获取验证码') || '请等待 ' + (state.newCaptchaTime + ' s') }}
|
||||
</a-button>
|
||||
</template>
|
||||
</a-input>
|
||||
<a-form-item validate-first label="验证码" name="newCaptcha" v-show="currentTab === 1">
|
||||
<count-down-input v-model:value="form.newCaptcha" :send-code-api="sendNewEmailCaptcha" :count="120">获取验证码1</count-down-input>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-spin>
|
||||
@@ -44,7 +39,7 @@
|
||||
import BasicModal from '/@/components/Modal/src/BasicModal.vue'
|
||||
import useFormEdit from '/@/hooks/bootx/useFormEdit'
|
||||
import { $ref } from 'vue/macros'
|
||||
import { computed, nextTick, reactive } from 'vue'
|
||||
import { computed, nextTick } from 'vue'
|
||||
import { FormInstance, Rule } from 'ant-design-vue/lib/form'
|
||||
import { useMessage } from '/@/hooks/web/useMessage'
|
||||
import { validateEmail, validateMobile } from '/@/utils/validate'
|
||||
@@ -56,6 +51,7 @@
|
||||
validateEmailChangeCaptcha,
|
||||
} from '/@/api/sys/userAssist'
|
||||
import { updateEmail } from '/@/views/account/account.api'
|
||||
import CountDownInput from '/@/components/CountDown/src/CountdownInput.vue'
|
||||
|
||||
const props = defineProps({
|
||||
email: String,
|
||||
@@ -74,7 +70,7 @@
|
||||
return {
|
||||
email: [
|
||||
{ required: currentTab === 1, message: '请输入新邮箱!' },
|
||||
{ validator: validateEmailRule, trigger: 'change' },
|
||||
{ validator: validateEmailRule },
|
||||
{ validator: validateNewEmail, trigger: 'blur' },
|
||||
],
|
||||
oldCaptcha: [
|
||||
@@ -87,12 +83,6 @@
|
||||
],
|
||||
} as Record<string, Rule[]>
|
||||
})
|
||||
const state = reactive({
|
||||
oldCaptcha: false,
|
||||
newCaptcha: false,
|
||||
oldCaptchaTime: 120,
|
||||
newCaptchaTime: 120,
|
||||
})
|
||||
const formRef = $ref<FormInstance>()
|
||||
|
||||
function init() {
|
||||
@@ -121,7 +111,7 @@
|
||||
*/
|
||||
function validateEmailRule() {
|
||||
const { email } = form
|
||||
if (currentTab !== 1 || !email) {
|
||||
if (currentTab !== 1) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
const { msg, result } = validateEmail(email)
|
||||
@@ -135,11 +125,6 @@
|
||||
if (currentTab !== 1) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
const { msg, result } = validateMobile(email)
|
||||
// 邮箱验证
|
||||
if (!result) {
|
||||
return Promise.reject(msg)
|
||||
}
|
||||
const { data } = await existsEmail(email)
|
||||
return data ? Promise.reject('邮箱已被使用') : Promise.resolve()
|
||||
}
|
||||
@@ -148,9 +133,6 @@
|
||||
*/
|
||||
async function validateOldCaptcha() {
|
||||
const { oldCaptcha } = form
|
||||
if (!oldCaptcha) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
const { data } = await validateCurrentEmailChangeCaptcha(oldCaptcha)
|
||||
return data ? Promise.resolve() : Promise.reject('验证码错误')
|
||||
}
|
||||
@@ -168,36 +150,30 @@
|
||||
/**
|
||||
* 发送验证码 旧
|
||||
*/
|
||||
function sendOldEmailCaptcha() {
|
||||
sendCurrentEmailChangeCaptcha().then(() => {
|
||||
createMessage.success('发送验证码成功')
|
||||
state.oldCaptcha = true
|
||||
const interval = window.setInterval(() => {
|
||||
if (state.oldCaptchaTime-- <= 0) {
|
||||
state.oldCaptchaTime = 120
|
||||
state.oldCaptcha = false
|
||||
window.clearInterval(interval)
|
||||
}
|
||||
}, 1000)
|
||||
})
|
||||
async function sendOldEmailCaptcha() {
|
||||
try {
|
||||
await sendCurrentEmailChangeCaptcha().then(() => {
|
||||
createMessage.success('发送验证码成功')
|
||||
})
|
||||
} catch (e) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
/**
|
||||
* 发送验证码 新手机
|
||||
*/
|
||||
function sendNewEmailCaptcha() {
|
||||
formRef.validateFields('email').then(async () => {
|
||||
sendEmailChangeCaptcha(form.email).then(() => {
|
||||
createMessage.success('发送验证码成功')
|
||||
state.newCaptcha = true
|
||||
const interval = window.setInterval(() => {
|
||||
if (state.newCaptchaTime-- <= 0) {
|
||||
state.newCaptchaTime = 120
|
||||
state.newCaptcha = false
|
||||
window.clearInterval(interval)
|
||||
}
|
||||
}, 1000)
|
||||
async function sendNewEmailCaptcha() {
|
||||
try {
|
||||
await formRef.validateFields('email').then(async () => {
|
||||
sendEmailChangeCaptcha(form.email).then(() => {
|
||||
createMessage.success('发送验证码成功')
|
||||
})
|
||||
})
|
||||
})
|
||||
} catch (e) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -2,6 +2,7 @@
|
||||
<basic-modal v-bind="$attrs" :loading="confirmLoading" :width="640" title="密码修改" :visible="visible" @cancel="handleCancel">
|
||||
<a-form
|
||||
ref="formRef"
|
||||
validateFirst
|
||||
:validate-trigger="['blur', 'change']"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
|
@@ -11,6 +11,7 @@
|
||||
<a-spin :spinning="confirmLoading">
|
||||
<a-form
|
||||
ref="formRef"
|
||||
validateFirst
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
:validate-trigger="['blur', 'change']"
|
||||
@@ -21,13 +22,7 @@
|
||||
<a-input v-model:value="form.phone" placeholder="手机号" />
|
||||
</a-form-item>
|
||||
<a-form-item label="验证码" name="captcha">
|
||||
<a-input :maxLength="8" placeholder="验证码" v-model:value="form.captcha">
|
||||
<template #addonAfter>
|
||||
<a-button size="small" type="link" :disabled="state.captcha" href="javascript:" @click="sendPhoneCaptcha">
|
||||
{{ (!state.captcha && '获取验证码') || '请等待 ' + (state.captchaTime + ' s') }}
|
||||
</a-button>
|
||||
</template>
|
||||
</a-input>
|
||||
<count-down-input v-model:value="form.captcha" :send-code-api="sendPhoneCaptcha" :count="120">获取验证码</count-down-input>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-spin>
|
||||
@@ -44,6 +39,7 @@
|
||||
import { useMessage } from '/@/hooks/web/useMessage'
|
||||
import { validateMobile } from '/@/utils/validate'
|
||||
import { bindPhone } from '/@/views/account/account.api'
|
||||
import CountDownInput from '/@/components/CountDown/src/CountdownInput.vue'
|
||||
|
||||
const emits = defineEmits(['ok'])
|
||||
const { visible, confirmLoading, modalWidth, labelCol, wrapperCol, handleCancel } = useFormEdit()
|
||||
@@ -64,10 +60,6 @@
|
||||
],
|
||||
} as Record<string, Rule[]>
|
||||
|
||||
const state = reactive({
|
||||
captcha: false,
|
||||
captchaTime: 120,
|
||||
})
|
||||
const formRef = $ref<FormInstance>()
|
||||
function init() {
|
||||
visible.value = true
|
||||
@@ -94,9 +86,6 @@
|
||||
*/
|
||||
function validatePhone() {
|
||||
const { phone } = form
|
||||
if (!phone) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
const { msg, result } = validateMobile(phone)
|
||||
return result ? Promise.resolve() : Promise.reject(msg)
|
||||
}
|
||||
@@ -105,14 +94,6 @@
|
||||
*/
|
||||
async function validateBindPhone() {
|
||||
const { phone } = form
|
||||
if (!phone) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
const { msg, result } = validateMobile(phone)
|
||||
// 手机号验证
|
||||
if (!result) {
|
||||
return Promise.reject(msg)
|
||||
}
|
||||
const { data } = await existsPhone(phone)
|
||||
return data ? Promise.reject('手机号已被使用') : Promise.resolve()
|
||||
}
|
||||
@@ -121,29 +102,23 @@
|
||||
*/
|
||||
async function validateCaptcha() {
|
||||
const { captcha } = form
|
||||
if (!captcha) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
const { data } = await validatePhoneChangeCaptcha(form.phone, captcha)
|
||||
return data ? Promise.resolve() : Promise.reject('验证码错误')
|
||||
}
|
||||
/**
|
||||
* 发送验证码 绑定邮箱
|
||||
*/
|
||||
function sendPhoneCaptcha() {
|
||||
formRef.validateFields('phone').then(async () => {
|
||||
sendPhoneChangeCaptcha(form.phone).then(() => {
|
||||
createMessage.success('发送验证码成功')
|
||||
state.captcha = true
|
||||
const interval = window.setInterval(() => {
|
||||
if (state.captchaTime-- <= 0) {
|
||||
state.captchaTime = 120
|
||||
state.captcha = false
|
||||
window.clearInterval(interval)
|
||||
}
|
||||
}, 1000)
|
||||
async function sendPhoneCaptcha() {
|
||||
try {
|
||||
await formRef.validateFields('phone').then(async () => {
|
||||
sendPhoneChangeCaptcha(form.phone).then(() => {
|
||||
createMessage.success('发送验证码成功')
|
||||
})
|
||||
})
|
||||
})
|
||||
} catch (e) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
defineExpose({ init })
|
||||
</script>
|
||||
|
@@ -1,34 +1,30 @@
|
||||
<template>
|
||||
<basic-modal v-bind="$attrs" :width="640" title="密码修改" :loading="confirmLoading" :visible="visible" @cancel="handleCancel">
|
||||
<basic-modal v-bind="$attrs" :width="640" title="手机号修改" :loading="confirmLoading" :visible="visible" @cancel="handleCancel">
|
||||
<a-spin :spinning="confirmLoading">
|
||||
<a-steps class="steps" :current="currentTab">
|
||||
<a-step title="验证手机" />
|
||||
<a-step title="绑定新手机" />
|
||||
</a-steps>
|
||||
<a-form ref="formRef" :model="form" :rules="rules" :label-col="labelCol" :wrapper-col="wrapperCol">
|
||||
<a-form
|
||||
ref="formRef"
|
||||
validateFirst
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
:validate-trigger="['blur', 'change']"
|
||||
:label-col="labelCol"
|
||||
:wrapper-col="wrapperCol"
|
||||
>
|
||||
<a-form-item label="手机号" v-show="currentTab === 0">
|
||||
<span>{{ phone }}</span>
|
||||
</a-form-item>
|
||||
<a-form-item label="验证码" name="oldCaptcha" v-show="currentTab === 0">
|
||||
<a-input :maxLength="8" placeholder="验证码" v-model:value="form.oldCaptcha">
|
||||
<template #addonAfter>
|
||||
<a-button size="small" type="link" :disabled="state.oldCaptcha" href="javascript:" @click="sendOldPhoneCaptcha">
|
||||
{{ (!state.oldCaptcha && '获取验证码') || '请等待 ' + (state.oldCaptchaTime + ' s') }}
|
||||
</a-button>
|
||||
</template>
|
||||
</a-input>
|
||||
<a-form-item validateFirst label="验证码" name="oldCaptcha" v-show="currentTab === 0">
|
||||
<count-down-input v-model:value="form.oldCaptcha" :send-code-api="sendOldPhoneCaptcha" :count="120">获取验证码</count-down-input>
|
||||
</a-form-item>
|
||||
<a-form-item label="手机号" name="phone" v-show="currentTab === 1">
|
||||
<a-form-item validateFirst label="手机号" name="phone" v-show="currentTab === 1">
|
||||
<a-input v-model:value="form.phone" placeholder="手机号" />
|
||||
</a-form-item>
|
||||
<a-form-item label="验证码" name="newCaptcha" v-show="currentTab === 1">
|
||||
<a-input :maxLength="8" placeholder="验证码" v-model:value="form.newCaptcha">
|
||||
<template #addonAfter>
|
||||
<a-button size="small" type="link" :disabled="state.newCaptcha" href="javascript:" @click="sendNewPhoneCaptcha">
|
||||
{{ (!state.newCaptcha && '获取验证码') || '请等待 ' + (state.newCaptchaTime + ' s') }}
|
||||
</a-button>
|
||||
</template>
|
||||
</a-input>
|
||||
<a-form-item validateFirst label="验证码" name="newCaptcha" v-show="currentTab === 1">
|
||||
<count-down-input v-model:value="form.newCaptcha" :send-code-api="sendNewPhoneCaptcha" :count="120">获取验证码</count-down-input>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-spin>
|
||||
@@ -44,7 +40,7 @@
|
||||
import BasicModal from '/@/components/Modal/src/BasicModal.vue'
|
||||
import useFormEdit from '/@/hooks/bootx/useFormEdit'
|
||||
import { $ref } from 'vue/macros'
|
||||
import { computed, nextTick, reactive } from 'vue'
|
||||
import { computed, nextTick } from 'vue'
|
||||
import { FormInstance, Rule } from 'ant-design-vue/lib/form'
|
||||
import {
|
||||
existsPhone,
|
||||
@@ -56,6 +52,7 @@
|
||||
import { useMessage } from '/@/hooks/web/useMessage'
|
||||
import { validateMobile } from '/@/utils/validate'
|
||||
import { updatePhone } from '/@/views/account/account.api'
|
||||
import CountDownInput from '/@/components/CountDown/src/CountdownInput.vue'
|
||||
|
||||
const props = defineProps({
|
||||
phone: String,
|
||||
@@ -64,7 +61,7 @@
|
||||
const emits = defineEmits(['ok'])
|
||||
const { visible, confirmLoading, modalWidth, labelCol, wrapperCol, handleCancel } = useFormEdit()
|
||||
const { createMessage, createConfirm } = useMessage()
|
||||
let currentTab = $ref(0)
|
||||
let currentTab = $ref(1)
|
||||
let form = $ref({
|
||||
phone: '',
|
||||
oldCaptcha: '',
|
||||
@@ -87,12 +84,6 @@
|
||||
],
|
||||
} as Record<string, Rule[]>
|
||||
})
|
||||
const state = reactive({
|
||||
oldCaptcha: false,
|
||||
newCaptcha: false,
|
||||
oldCaptchaTime: 120,
|
||||
newCaptchaTime: 120,
|
||||
})
|
||||
const formRef = $ref<FormInstance>()
|
||||
|
||||
function init() {
|
||||
@@ -131,14 +122,9 @@
|
||||
*/
|
||||
async function validateNewPhone() {
|
||||
const { phone } = form
|
||||
if (currentTab !== 1) {
|
||||
if (!(currentTab === 1 && phone)) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
const { msg, result } = validateMobile(phone)
|
||||
// 手机号验证
|
||||
if (!result) {
|
||||
return Promise.reject(msg)
|
||||
}
|
||||
const { data } = await existsPhone(phone)
|
||||
return data ? Promise.reject('手机号已被使用') : Promise.resolve()
|
||||
}
|
||||
@@ -147,9 +133,6 @@
|
||||
*/
|
||||
async function validateOldCaptcha() {
|
||||
const { oldCaptcha } = form
|
||||
if (!oldCaptcha) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
const { data } = await validateCurrentPhoneChangeCaptcha(oldCaptcha)
|
||||
return data ? Promise.resolve() : Promise.reject('验证码错误')
|
||||
}
|
||||
@@ -158,45 +141,35 @@
|
||||
*/
|
||||
async function validateNewCaptcha() {
|
||||
const { newCaptcha } = form
|
||||
if (!(currentTab === 1 && newCaptcha)) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
const { data } = await validatePhoneChangeCaptcha(form.phone, newCaptcha)
|
||||
return data ? Promise.resolve() : Promise.reject('验证码错误')
|
||||
}
|
||||
/**
|
||||
* 发送验证码 旧
|
||||
*/
|
||||
function sendOldPhoneCaptcha() {
|
||||
sendCurrentPhoneChangeCaptcha().then(() => {
|
||||
createMessage.success('发送验证码成功')
|
||||
state.oldCaptcha = true
|
||||
const interval = window.setInterval(() => {
|
||||
if (state.oldCaptchaTime-- <= 0) {
|
||||
state.oldCaptchaTime = 120
|
||||
state.oldCaptcha = false
|
||||
window.clearInterval(interval)
|
||||
}
|
||||
}, 1000)
|
||||
})
|
||||
async function sendOldPhoneCaptcha() {
|
||||
try {
|
||||
await sendCurrentPhoneChangeCaptcha().then(() => {
|
||||
createMessage.success('发送验证码成功')
|
||||
})
|
||||
} catch (e) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
/**
|
||||
* 发送验证码 新手机
|
||||
*/
|
||||
function sendNewPhoneCaptcha() {
|
||||
formRef.validateFields('phone').then(async () => {
|
||||
sendPhoneChangeCaptcha(form.phone).then(() => {
|
||||
async function sendNewPhoneCaptcha() {
|
||||
try {
|
||||
await formRef.validateFields('phone')
|
||||
await sendPhoneChangeCaptcha(form.phone).then(() => {
|
||||
createMessage.success('发送验证码成功')
|
||||
state.newCaptcha = true
|
||||
const interval = window.setInterval(() => {
|
||||
if (state.newCaptchaTime-- <= 0) {
|
||||
state.newCaptchaTime = 120
|
||||
state.newCaptcha = false
|
||||
window.clearInterval(interval)
|
||||
}
|
||||
}, 1000)
|
||||
})
|
||||
})
|
||||
} catch (e) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
/**
|
||||
* 下一步
|
||||
|
@@ -30,38 +30,6 @@ export const settingList = [
|
||||
name: '账号绑定',
|
||||
component: AccountBind,
|
||||
},
|
||||
{
|
||||
key: '4',
|
||||
name: '新消息通知',
|
||||
component: MsgNotify,
|
||||
},
|
||||
]
|
||||
|
||||
export const accountBindList: ListItem[] = [
|
||||
{
|
||||
key: '1',
|
||||
title: '绑定淘宝',
|
||||
description: '当前未绑定淘宝账号',
|
||||
extra: '绑定',
|
||||
avatar: 'ri:taobao-fill',
|
||||
color: '#ff4000',
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
title: '绑定支付宝',
|
||||
description: '当前未绑定支付宝账号',
|
||||
extra: '绑定',
|
||||
avatar: 'fa-brands:alipay',
|
||||
color: '#2eabff',
|
||||
},
|
||||
{
|
||||
key: '3',
|
||||
title: '绑定钉钉',
|
||||
description: '当前未绑定钉钉账号',
|
||||
extra: '绑定',
|
||||
avatar: 'ri:dingding-fill',
|
||||
color: '#2eabff',
|
||||
},
|
||||
]
|
||||
|
||||
// 新消息通知 list
|
||||
|
@@ -28,9 +28,13 @@
|
||||
>
|
||||
<!-- 账号密码登录 -->
|
||||
<LoginForm />
|
||||
<!-- 找回密码 -->
|
||||
<ForgetPasswordForm />
|
||||
<!-- 注册 -->
|
||||
<RegisterForm />
|
||||
<!-- 手机登录 -->
|
||||
<MobileForm />
|
||||
<!-- 扫码登录 -->
|
||||
<QrCodeForm />
|
||||
</div>
|
||||
</div>
|
||||
@@ -43,8 +47,8 @@
|
||||
import { AppLogo } from '/@/components/Application'
|
||||
import { AppDarkModeToggle } from '/@/components/Application'
|
||||
import LoginForm from './LoginForm.vue'
|
||||
import ForgetPasswordForm from './ForgetPasswordForm.vue'
|
||||
import RegisterForm from './RegisterForm.vue'
|
||||
import ForgetPasswordForm from './user/ForgetPasswordForm.vue'
|
||||
import RegisterForm from './user/RegisterForm.vue'
|
||||
import MobileForm from './MobileForm.vue'
|
||||
import QrCodeForm from './QrCodeForm.vue'
|
||||
import { useGlobSetting } from '/@/hooks/setting'
|
||||
|
@@ -86,7 +86,7 @@
|
||||
import { FormInstance, Rule } from 'ant-design-vue/lib/form'
|
||||
import { LoginParams } from '/@/api/sys/model/userModel'
|
||||
|
||||
const { notification, createErrorModal } = useMessage()
|
||||
const { notification } = useMessage()
|
||||
const { prefixCls } = useDesign('login')
|
||||
// 用户信息存储
|
||||
const userStore = useUserStore()
|
||||
@@ -157,8 +157,8 @@
|
||||
* 登录处理
|
||||
*/
|
||||
async function handleLogin() {
|
||||
const data = await formRef.validate()
|
||||
try {
|
||||
await formRef.validate()
|
||||
loading = true
|
||||
const token = await userStore.login(form)
|
||||
if (token) {
|
||||
|
@@ -5,20 +5,17 @@
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { computed, unref } from 'vue'
|
||||
import { useI18n } from '/@/hooks/web/useI18n'
|
||||
import { LoginStateEnum, useLoginState } from './useLogin'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const { getLoginState } = useLoginState()
|
||||
|
||||
const getFormTitle = computed(() => {
|
||||
const titleObj = {
|
||||
[LoginStateEnum.RESET_PASSWORD]: t('sys.login.forgetFormTitle'),
|
||||
[LoginStateEnum.LOGIN]: t('sys.login.signInFormTitle'),
|
||||
[LoginStateEnum.REGISTER]: t('sys.login.signUpFormTitle'),
|
||||
[LoginStateEnum.MOBILE]: t('sys.login.mobileSignInFormTitle'),
|
||||
[LoginStateEnum.QR_CODE]: t('sys.login.qrSignInFormTitle'),
|
||||
[LoginStateEnum.RESET_PASSWORD]: '重置密码',
|
||||
[LoginStateEnum.LOGIN]: '登录',
|
||||
[LoginStateEnum.REGISTER]: '注册',
|
||||
[LoginStateEnum.MOBILE]: '手机登录',
|
||||
[LoginStateEnum.QR_CODE]: '二维码登录',
|
||||
}
|
||||
return titleObj[unref(getLoginState)]
|
||||
})
|
||||
|
@@ -1,78 +0,0 @@
|
||||
<template>
|
||||
<template v-if="getShow">
|
||||
<LoginFormTitle class="enter-x" />
|
||||
<Form class="p-4 enter-x" :model="formData" :rules="getFormRules" ref="formRef">
|
||||
<FormItem name="account" class="enter-x">
|
||||
<Input class="fix-auto-fill" size="large" v-model:value="formData.account" :placeholder="t('sys.login.userName')" />
|
||||
</FormItem>
|
||||
<FormItem name="mobile" class="enter-x">
|
||||
<Input size="large" v-model:value="formData.mobile" :placeholder="t('sys.login.mobile')" class="fix-auto-fill" />
|
||||
</FormItem>
|
||||
<FormItem name="sms" class="enter-x">
|
||||
<CountdownInput size="large" class="fix-auto-fill" v-model:value="formData.sms" :placeholder="t('sys.login.smsCode')" />
|
||||
</FormItem>
|
||||
<FormItem name="password" class="enter-x">
|
||||
<StrengthMeter size="large" v-model:value="formData.password" :placeholder="t('sys.login.password')" />
|
||||
</FormItem>
|
||||
<FormItem name="confirmPassword" class="enter-x">
|
||||
<InputPassword
|
||||
size="large"
|
||||
visibilityToggle
|
||||
v-model:value="formData.confirmPassword"
|
||||
:placeholder="t('sys.login.confirmPassword')"
|
||||
/>
|
||||
</FormItem>
|
||||
|
||||
<FormItem class="enter-x" name="policy">
|
||||
<!-- No logic, you need to deal with it yourself -->
|
||||
<Checkbox v-model:checked="formData.policy" size="small">
|
||||
{{ t('sys.login.policy') }}
|
||||
</Checkbox>
|
||||
</FormItem>
|
||||
|
||||
<Button type="primary" class="enter-x" size="large" block @click="handleRegister" :loading="loading">
|
||||
{{ t('sys.login.registerButton') }}
|
||||
</Button>
|
||||
<Button size="large" block class="mt-4 enter-x" @click="handleBackLogin">
|
||||
{{ t('sys.login.backSignIn') }}
|
||||
</Button>
|
||||
</Form>
|
||||
</template>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref, unref, computed } from 'vue'
|
||||
import LoginFormTitle from './LoginFormTitle.vue'
|
||||
import { Form, Input, Button, Checkbox } from 'ant-design-vue'
|
||||
import { StrengthMeter } from '/@/components/StrengthMeter'
|
||||
import { CountdownInput } from '/@/components/CountDown'
|
||||
import { useI18n } from '/@/hooks/web/useI18n'
|
||||
import { useLoginState, useFormRules, useFormValid, LoginStateEnum } from './useLogin'
|
||||
|
||||
const FormItem = Form.Item
|
||||
const InputPassword = Input.Password
|
||||
const { t } = useI18n()
|
||||
const { handleBackLogin, getLoginState } = useLoginState()
|
||||
|
||||
const formRef = ref()
|
||||
const loading = ref(false)
|
||||
|
||||
const formData = reactive({
|
||||
account: '',
|
||||
password: '',
|
||||
confirmPassword: '',
|
||||
mobile: '',
|
||||
sms: '',
|
||||
policy: false,
|
||||
})
|
||||
|
||||
const { getFormRules } = useFormRules(formData)
|
||||
const { validForm } = useFormValid(formRef)
|
||||
|
||||
const getShow = computed(() => unref(getLoginState) === LoginStateEnum.REGISTER)
|
||||
|
||||
async function handleRegister() {
|
||||
const data = await validForm()
|
||||
if (!data) return
|
||||
console.log(data)
|
||||
}
|
||||
</script>
|
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<template v-if="getShow">
|
||||
<LoginFormTitle class="enter-x" />
|
||||
<Form class="p-4 enter-x" :model="formData" :rules="getFormRules" ref="formRef">
|
||||
<login-form-title class="enter-x" />
|
||||
<a-form class="p-4 enter-x" :model="formData" :rules="getFormRules" ref="formRef">
|
||||
<FormItem name="account" class="enter-x">
|
||||
<Input size="large" v-model:value="formData.account" :placeholder="t('sys.login.userName')" />
|
||||
</FormItem>
|
||||
@@ -21,16 +21,16 @@
|
||||
{{ t('sys.login.backSignIn') }}
|
||||
</Button>
|
||||
</FormItem>
|
||||
</Form>
|
||||
</a-form>
|
||||
</template>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref, computed, unref } from 'vue'
|
||||
import LoginFormTitle from './LoginFormTitle.vue'
|
||||
import LoginFormTitle from '../LoginFormTitle.vue'
|
||||
import { Form, Input, Button } from 'ant-design-vue'
|
||||
import { CountdownInput } from '/@/components/CountDown'
|
||||
import { useI18n } from '/@/hooks/web/useI18n'
|
||||
import { useLoginState, useFormRules, LoginStateEnum } from './useLogin'
|
||||
import { useLoginState, useFormRules, LoginStateEnum } from '../useLogin'
|
||||
|
||||
const FormItem = Form.Item
|
||||
const { t } = useI18n()
|
126
src/views/login/user/RegisterForm.vue
Normal file
126
src/views/login/user/RegisterForm.vue
Normal file
@@ -0,0 +1,126 @@
|
||||
<template>
|
||||
<template v-if="getShow">
|
||||
<login-form-title class="enter-x" />
|
||||
<a-spin :spinning="loading">
|
||||
<a-form class="p-4 enter-x" :model="form" :validate-trigger="['blur', 'change']" :rules="rules" ref="formRef">
|
||||
<a-form-item validate-first name="username" class="enter-x">
|
||||
<a-input class="fix-auto-fill" size="large" v-model:value="form.username" placeholder="账号" />
|
||||
</a-form-item>
|
||||
<a-form-item validate-first name="password" class="enter-x">
|
||||
<strength-meter size="large" v-model:value="form.password" placeholder="密码" />
|
||||
</a-form-item>
|
||||
<a-form-item validate-first name="confirmPassword" class="enter-x">
|
||||
<a-input-password size="large" visibilityToggle v-model:value="form.confirmPassword" placeholder="确认密码" />
|
||||
</a-form-item>
|
||||
<a-row :span="12" class="enter-x">
|
||||
<a-col :span="16">
|
||||
<a-form-item name="captcha" class="enter-x">
|
||||
<a-input size="large" placeholder="验证码" v-model:value="form.captcha" style="min-width: 100px" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="8" style="text-align: right">
|
||||
<a-form-item :style="{ 'text-align': 'right', 'margin-left': '20px' }" class="enter-x">
|
||||
<img style="margin-top: 2px" :src="captchaData" @click="getCaptcha" alt="验证码" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-form>
|
||||
</a-spin>
|
||||
<a-button type="primary" class="enter-x" size="large" block @click="handleSubmit" :loading="loading"> 注册 </a-button>
|
||||
<a-button size="large" block class="mt-4 enter-x" @click="handleBackLogin"> 返回 </a-button>
|
||||
</template>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref, unref, computed, onMounted } from 'vue'
|
||||
import LoginFormTitle from '../LoginFormTitle.vue'
|
||||
import { StrengthMeter } from '/@/components/StrengthMeter'
|
||||
import { useLoginState, LoginStateEnum } from '../useLogin'
|
||||
import { $ref } from 'vue/macros'
|
||||
import { FormInstance, Rule } from 'ant-design-vue/lib/form'
|
||||
import { imgCaptcha } from '/@/api/common/Captcha'
|
||||
import { existsUsername } from '/@/api/sys/userAssist'
|
||||
import { register } from '/@/api/sys/user'
|
||||
import { useMessage } from '/@/hooks/web/useMessage'
|
||||
|
||||
const { handleBackLogin, getLoginState } = useLoginState()
|
||||
const getShow = computed(() => unref(getLoginState) === LoginStateEnum.REGISTER)
|
||||
|
||||
const { createMessage } = useMessage()
|
||||
|
||||
const formRef = $ref<FormInstance>()
|
||||
let loading = $ref(false)
|
||||
let captchaData = $ref('')
|
||||
let confirmDirty = $ref(false)
|
||||
const form = reactive({
|
||||
username: '',
|
||||
password: '',
|
||||
confirmPassword: '',
|
||||
captchaKey: '',
|
||||
captcha: '',
|
||||
})
|
||||
|
||||
const rules = {
|
||||
username: [{ required: true, message: '请输入登录账号!' }, { validator: checkUsername }],
|
||||
password: [
|
||||
{ required: true, message: '请输入登录密码!' },
|
||||
{ validator: validateToNextPassword, trigger: 'change' },
|
||||
],
|
||||
confirmPassword: [{ required: true, message: '请重新输入登录密码!' }, { validator: compareToFirstPassword }],
|
||||
captcha: [{ required: true, message: '请输入验证码!' }],
|
||||
} as Record<string, Rule[]>
|
||||
|
||||
onMounted(() => {
|
||||
getCaptcha()
|
||||
})
|
||||
|
||||
/**
|
||||
* 获取验证码
|
||||
*/
|
||||
function getCaptcha() {
|
||||
imgCaptcha().then(({ data }) => {
|
||||
captchaData = data.captchaData
|
||||
form.captchaKey = data.captchaKey
|
||||
})
|
||||
}
|
||||
/**
|
||||
* 用户名检查
|
||||
*/
|
||||
async function checkUsername() {
|
||||
const { data } = await existsUsername(form.username)
|
||||
return data ? Promise.reject('用户名已存在!') : Promise.resolve()
|
||||
}
|
||||
/**
|
||||
* 密码检查
|
||||
*/
|
||||
function validateToNextPassword() {
|
||||
if (confirmDirty) {
|
||||
formRef.validateFields(['confirmPassword'])
|
||||
}
|
||||
return Promise.resolve()
|
||||
}
|
||||
function compareToFirstPassword() {
|
||||
if (form.confirmPassword !== form.password) {
|
||||
confirmDirty = true
|
||||
return Promise.reject('密码不一致')
|
||||
} else {
|
||||
return Promise.resolve()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册
|
||||
*/
|
||||
async function handleSubmit() {
|
||||
await formRef.validate()
|
||||
loading = true
|
||||
try {
|
||||
await register(form).then(() => {
|
||||
createMessage.success('注册成功')
|
||||
handleBackLogin()
|
||||
})
|
||||
formRef.resetFields()
|
||||
} finally {
|
||||
loading = false
|
||||
}
|
||||
}
|
||||
</script>
|
@@ -53,7 +53,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, Ref, onMounted } from 'vue'
|
||||
import { ref, Ref, onMounted, onBeforeUnmount } from 'vue'
|
||||
import { useECharts } from '/@/hooks/web/useECharts'
|
||||
import { getRedisInfo } from '/@/views/modules/monitor/redis/RedisInfoMonitor.api'
|
||||
import { $ref } from 'vue/macros'
|
||||
@@ -128,6 +128,11 @@
|
||||
}, 1000 * 5)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
clearInterval(interval)
|
||||
interval = null
|
||||
})
|
||||
|
||||
/**
|
||||
* 获取Redis服务信息
|
||||
*/
|
||||
|
@@ -139,7 +139,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, onUnmounted } from 'vue'
|
||||
import { onBeforeUnmount, onMounted, onUnmounted } from 'vue'
|
||||
import { getSystemInfo } from './SystemInfo.api'
|
||||
import { $ref } from 'vue/macros'
|
||||
|
||||
@@ -169,7 +169,7 @@
|
||||
})
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
onBeforeUnmount(() => {
|
||||
clearInterval(interval)
|
||||
interval = null
|
||||
})
|
||||
|
Reference in New Issue
Block a user