Industrial-image-management.../src/views/user/components/VerifyModel.vue

292 lines
7.8 KiB
Vue
Raw Normal View History

2025-06-27 19:54:42 +08:00
<template>
<a-modal
v-model:visible="visible" :title="title" :mask-closable="false" :esc-to-close="false"
:width="width >= 500 ? 500 : '100%'" draggable @before-ok="save" @ok="saveAfter" @close="reset"
>
<GiForm ref="formRef" v-model="form" :columns="columns">
<template #captcha>
<a-input v-model="form.captcha" placeholder="请输入验证码" :max-length="6" allow-clear style="flex: 1 1" />
<a-button
class="captcha-btn" :loading="captchaLoading" :disabled="captchaDisable" size="large"
@click="onCaptcha"
>
{{ captchaBtnName }}
</a-button>
</template>
</GiForm>
<Verify
ref="VerifyRef" :captcha-type="captchaType" :mode="captchaMode"
:img-size="{ width: '330px', height: '155px' }" @success="getCaptcha"
/>
</a-modal>
</template>
<script setup lang="ts">
import { useWindowSize } from '@vueuse/core'
import { Message } from '@arco-design/web-vue'
import NProgress from 'nprogress'
import { type BehaviorCaptchaReq, getEmailCaptcha, getSmsCaptcha, updateUserEmail, updateUserPassword, updateUserPhone } from '@/apis'
import { encryptByRsa } from '@/utils/encrypt'
import { useUserStore } from '@/stores'
import { type ColumnItem, GiForm } from '@/components/GiForm'
import { useResetReactive } from '@/hooks'
import * as Regexp from '@/utils/regexp'
import modalErrorWrapper from '@/utils/modal-error-wrapper'
import router from '@/router'
const { width } = useWindowSize()
const userStore = useUserStore()
const userInfo = computed(() => userStore.userInfo)
const verifyType = ref()
const title = computed(
() => `修改${verifyType.value === 'phone' ? '手机号' : verifyType.value === 'email' ? '邮箱' : '密码'}`,
)
const formRef = ref<InstanceType<typeof GiForm>>()
const [form, resetForm] = useResetReactive({
phone: '',
email: '',
captcha: '',
oldPassword: '',
newPassword: '',
rePassword: '',
})
const columns: ColumnItem[] = reactive([
{
label: '手机号',
field: 'phone',
type: 'input',
span: 24,
props: {
showWordLimit: false,
},
rules: [
{ required: true, message: '请输入手机号' },
{ match: Regexp.Phone, message: '请输入正确的手机号' },
],
hide: () => {
return verifyType.value !== 'phone'
},
},
{
label: '邮箱',
field: 'email',
type: 'input',
span: 24,
rules: [
{ required: true, message: '请输入邮箱' },
{ match: Regexp.Email, message: '请输入正确的邮箱' },
],
hide: () => {
return verifyType.value !== 'email'
},
},
{
label: '验证码',
field: 'captcha',
type: 'input',
span: 24,
rules: [{ required: true, message: '请输入验证码' }],
hide: () => {
return !['phone', 'email'].includes(verifyType.value)
},
},
{
label: '当前密码',
field: 'oldPassword',
type: 'input-password',
span: 24,
rules: [{ required: true, message: '请输入当前密码' }],
hide: () => {
return !userInfo.value.pwdResetTime
},
},
{
label: '新密码',
field: 'newPassword',
type: 'input-password',
span: 24,
rules: [
{ required: true, message: '请输入新密码' },
{
validator: (value, callback) => {
if (value === form.oldPassword) {
callback('新密码不能与当前密码相同')
} else {
callback()
}
},
},
],
hide: () => {
return verifyType.value !== 'password'
},
},
{
label: '确认新密码',
field: 'rePassword',
type: 'input-password',
span: 24,
props: {
placeholder: '请再次输入新密码',
},
rules: [
{ required: true, message: '请再次输入新密码' },
{
validator: (value, callback) => {
if (value !== form.newPassword) {
callback('两次输入的密码不一致')
} else {
callback()
}
},
},
],
hide: () => {
return verifyType.value !== 'password'
},
},
])
const VerifyRef = ref<InstanceType<any>>()
const captchaType = ref('blockPuzzle')
const captchaMode = ref('pop')
const captchaLoading = ref(false)
// 弹出行为验证码
const onCaptcha = async () => {
if (captchaLoading.value) return
const isInvalid = await formRef.value?.formRef?.validateField(verifyType.value === 'phone' ? 'phone' : 'email')
if (isInvalid) return
// 重置行为参数
VerifyRef.value.instance.refresh()
VerifyRef.value.show()
}
const captchaTimer = ref()
const captchaTime = ref(60)
const captchaBtnName = ref('获取验证码')
const captchaDisable = ref(false)
// 重置验证码
const resetCaptcha = () => {
window.clearInterval(captchaTimer.value)
captchaTime.value = 60
captchaBtnName.value = '获取验证码'
captchaDisable.value = false
}
// 重置
const reset = () => {
formRef.value?.formRef?.resetFields()
resetForm()
resetCaptcha()
}
// 获取验证码
const getCaptcha = async (captchaReq: BehaviorCaptchaReq) => {
// 发送验证码
try {
captchaLoading.value = true
captchaBtnName.value = '发送中...'
if (verifyType.value === 'phone') {
await getSmsCaptcha(form.phone, captchaReq)
} else if (verifyType.value === 'email') {
await getEmailCaptcha(form.email, captchaReq)
}
captchaLoading.value = false
captchaDisable.value = true
captchaBtnName.value = `获取验证码(${(captchaTime.value -= 1)}s)`
Message.success('发送成功')
// Message.success('仅提供效果演示,实际使用请查看代码取消相关注释')
captchaTimer.value = window.setInterval(() => {
captchaTime.value -= 1
captchaBtnName.value = `获取验证码(${captchaTime.value}s)`
if (captchaTime.value <= 0) {
resetCaptcha()
}
}, 1000)
} catch (error) {
resetCaptcha()
} finally {
captchaLoading.value = false
}
}
// 保存
const save = async () => {
const isInvalid = await formRef.value?.formRef?.validate()
if (isInvalid) return false
try {
if (verifyType.value === 'phone') {
await updateUserPhone({
phone: form.phone,
captcha: form.captcha,
oldPassword: encryptByRsa(form.oldPassword) as string,
})
Message.success('修改成功')
} else if (verifyType.value === 'email') {
await updateUserEmail({
email: form.email,
captcha: form.captcha,
oldPassword: encryptByRsa(form.oldPassword) as string,
})
Message.success('修改成功')
} else if (verifyType.value === 'password') {
if (form.newPassword !== form.rePassword) {
Message.error('两次新密码不一致')
return false
}
if (form.newPassword === form.oldPassword) {
Message.error('新密码与旧密码不能相同')
return false
}
await updateUserPassword({
oldPassword: encryptByRsa(form.oldPassword) || '',
newPassword: encryptByRsa(form.newPassword) || '',
})
}
return true
} catch (error) {
return false
}
}
const saveAfter = async () => {
if (verifyType.value === 'password') {
modalErrorWrapper({
title: '提示',
content: '密码修改成功! 请保存好新密码,并使用新密码重新登录',
maskClosable: false,
escToClose: false,
okText: '重新登录',
async onOk() {
NProgress.done()
const userStore = useUserStore()
await userStore.logoutCallBack()
await router.replace('/login')
},
})
} else {
// 修改成功后,重新获取用户信息
await userStore.getInfo()
}
}
const visible = ref(false)
// 打开弹框
const open = (type: string) => {
verifyType.value = type
visible.value = true
}
defineExpose({ open })
</script>
<style scoped lang="scss">
.captcha-btn {
margin-left: 12px;
min-width: 98px;
border-radius: 4px;
}
</style>