feat: 增加忘记密码

zcsf
时启龙 2025-10-13 17:45:49 +08:00
parent ce41faacc5
commit edfe5a56e2
3 changed files with 123 additions and 14 deletions

3
components.d.ts vendored
View File

@ -26,6 +26,9 @@ declare module 'vue' {
ASwitch: typeof import('ant-design-vue/es')['Switch']
ATextarea: typeof import('ant-design-vue/es')['Textarea']
ATooltip: typeof import('ant-design-vue/es')['Tooltip']
ElButton: typeof import('element-plus/es')['ElButton']
ElDialog: typeof import('element-plus/es')['ElDialog']
ElInput: typeof import('element-plus/es')['ElInput']
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
Empty: typeof import('./src/components/empty/Empty.vue')['default']
ImageCropper: typeof import('./src/components/image-cropper/index.vue')['default']

View File

@ -21,12 +21,12 @@ export function logout() {
}
export function getConfig(params) {
return request.get('/sms/v1/logo', {
params: wrapperParams(params),
params: wrapperParams(params)
})
}
export function getDict(data) {
return request.get('/dict/children', {
params: wrapperParams(data),
params: wrapperParams(data)
})
}
export function getSysconf() {
@ -48,7 +48,7 @@ export function getPortal(params) {
}
export function getServiceQuota(tenantId, data) {
return request.get(`/cos/v1/tenants/${tenantId}/quotas`, {
params: wrapperParams(data),
params: wrapperParams(data)
})
}
export function replaceToken(params) {
@ -69,7 +69,7 @@ export function getSystemTreeConfigs(params) {
// 更新系统配置信息
export function updateSystemConfigs(params) {
return request.put('/sms/v1/system-configs', params, {
headers: { 'Content-Type': 'multipart/form-data', BsmAjaxHeader: true, options: { noSeri: true } },
headers: { 'Content-Type': 'multipart/form-data', BsmAjaxHeader: true, options: { noSeri: true } }
})
}
// 测试连接
@ -79,3 +79,8 @@ export function testLinkApi(category) {
export function getVertifyCode(params) {
return request.post('/sms/v1/users/code', wrapperParams(params))
}
// 忘记密码
export function forgetPassword(params) {
return request.post('/sms/v1/users/forget', params)
}

View File

@ -42,13 +42,42 @@
<a-switch v-model:checked="remember"></a-switch>
<span class="m-l-xs">记住密码</span>
</span>
<a :href="`mailto:${configs.helpInformationLink}`" type="text" class="text-info pull-right help-info" :title="configs.helpInformationContent">{{ configs.helpInformationContent }}</a>
<a :href="`mailto:${configs.helpInformationLink}`" type="text" class="text-info help-info" :title="configs.helpInformationContent">{{ configs.helpInformationContent }}</a>
<a type="text" class="m-l text-info forgot-password" @click="openForgotPasswordDialog"></a>
</div>
</a-form-item>
<a-button class="login-btn" type="primary" size="medium" :loading="loading" @click="handleLogin"></a-button>
</a-form>
</div>
<div class="copyright-info">{{ configs.copyrightInformation }}</div>
<!-- 忘记密码弹窗 -->
<a-modal title="忘记密码" v-model:visible="showForgotPasswordDialog" width="600px" :confirm-loading="forgotPasswordLoading" @ok="handleForgotPassword" :close-on-click-modal="false">
<a-form :model="forgotPasswordForm" ref="forgotPasswordFormRef" label-position="left" :labelCol="{ style: { width: '80px' } }">
<a-form-item label="用户名" name="account" :rules="[{ ...required, message: '请输入用户名' }]">
<a-input class="w-lg" v-model:value="forgotPasswordForm.account" placeholder="请输入用户名"></a-input>
</a-form-item>
<a-form-item label="新密码" name="password" :rules="[{ ...required, message: '请输入密码' }]">
<a-input class="w-lg" v-model:value="forgotPasswordForm.password" type="password" placeholder="请输入新密码" show-password></a-input>
</a-form-item>
<a-form-item label="确认密码" name="confirmPassword" :rules="[{ ...required, message: '请输入密码' }]">
<a-input class="w-lg" v-model:value="forgotPasswordForm.confirmPassword" type="password" placeholder="请再次输入新密码" show-password></a-input>
</a-form-item>
<a-form-item label="验证码" name="verificationCode" :rules="[{ ...required, message: '请输入验证码' }]">
<div class="verification-code-input">
<a-input class="w-lg m-r" v-model:value="forgotPasswordForm.verificationCode" placeholder="请输入验证码"></a-input>
<a-button type="primary" :disabled="codeCountdown > 0" @click="getVerificationCode" :loading="codeLoading">
{{ codeCountdown > 0 ? `${codeCountdown}s后重新获取` : '获取验证码' }}
</a-button>
</div>
</a-form-item>
</a-form>
<template #footer>
<div class="dialog-footer">
<a-button @click="showForgotPasswordDialog = false">取消</a-button>
<a-button type="primary" :loading="forgotPasswordLoading" @click="handleForgotPassword"></a-button>
</div>
</template>
</a-modal>
</div>
</template>
@ -61,8 +90,8 @@ import { useStore } from 'vuex'
import { useRouter, useRoute } from 'vue-router'
import setLoginData from './tools'
import { required } from '@/validate'
import { getVertifyCode } from '@/services'
import { message } from 'ant-design-vue'
import { getVertifyCode, forgetPassword } from '@/services'
import { message, Modal } from 'ant-design-vue'
export default {
components: { UserOutlined, LockOutlined },
@ -78,11 +107,23 @@ export default {
verifyLoading: false,
countdown: 0,
isCountingDown: false,
needSmsVerify: true
needSmsVerify: true,
showForgotPasswordDialog: false,
forgotPasswordForm: {
account: '',
password: '',
confirmPassword: '',
verificationCode: ''
},
forgotPasswordLoading: false,
codeCountdown: 0,
codeLoading: false
})
const store = useStore()
const pwdRule = computed(() => store.state.app.systemConfig.pwdStrength + ',pswNoSpace' + ',required')
const configs = computed(() => store.getters.pageConfig)
const loginFormRef = ref(null)
const forgotPasswordFormRef = ref(null)
let countdownTimer = null
//
@ -122,12 +163,15 @@ export default {
const route = useRoute()
// accountneedSmsVerify
watch(() => state.loginForm.account, (newAccount) => {
if (!newAccount.trim()) {
state.needSmsVerify = true
resetCountdown() //
watch(
() => state.loginForm.account,
(newAccount) => {
if (!newAccount.trim()) {
state.needSmsVerify = true
resetCountdown() //
}
}
})
)
const getVertify = () => {
if (state.isCountingDown) return //
@ -209,6 +253,58 @@ export default {
state.capsTooltip = false
}
}
function getVerificationCode() {
if (!state.forgotPasswordForm.account) {
message.error('请先输入用户名')
return
}
state.codeLoading = true
getVertifyCode({ account: state.forgotPasswordForm.account })
.then((res) => {
if (res.success) {
message.success('验证码已发送')
state.codeCountdown = 60
const timer = setInterval(() => {
state.codeCountdown--
if (state.codeCountdown <= 0) {
clearInterval(timer)
}
}, 1000)
}
})
.finally(() => {
state.codeLoading = false
})
}
function openForgotPasswordDialog() {
//
state.forgotPasswordForm.account = state.loginForm.account
state.showForgotPasswordDialog = true
}
function handleForgotPassword() {
forgotPasswordFormRef.value.validate().then(() => {
const { account, password, confirmPassword, verificationCode } = state.forgotPasswordForm
if (confirmPassword !== password) return message.error('两次输入的密码不相同')
state.forgotPasswordLoading = true
forgetPassword({
account,
password: encrypt(password),
code: verificationCode
})
.then((res) => {
if (res.success) {
message.success(res.message)
state.showForgotPasswordDialog = false
}
})
.finally(() => {
state.forgotPasswordLoading = false
})
})
}
return {
...toRefs(state),
required,
@ -218,7 +314,12 @@ export default {
checkCapslock,
getVertify,
startCountdown,
resetCountdown
resetCountdown,
forgotPasswordFormRef,
getVerificationCode,
openForgotPasswordDialog,
handleForgotPassword,
pwdRule
}
}
}