198 lines
4.5 KiB
Vue
198 lines
4.5 KiB
Vue
<script setup lang="ts">
|
||
import { ref, watch } from 'vue'
|
||
import { ElMessage } from 'element-plus'
|
||
import { AppButton, PasswordStrength, SmsCodeInput } from '@/components'
|
||
|
||
interface Props {
|
||
/** 绑定手机号(脱敏展示) */
|
||
phone: string
|
||
}
|
||
|
||
const props = defineProps<Props>()
|
||
|
||
const visible = defineModel<boolean>('visible', { default: false })
|
||
|
||
const emit = defineEmits<{
|
||
submit: [payload: { code: string; password: string; confirm: string }]
|
||
}>()
|
||
|
||
const code = ref('')
|
||
const password = ref('')
|
||
const confirm = ref('')
|
||
const agree = ref(false)
|
||
|
||
watch(visible, (open) => {
|
||
if (!open) return
|
||
code.value = ''
|
||
password.value = ''
|
||
confirm.value = ''
|
||
agree.value = false
|
||
})
|
||
|
||
function onGetCode(): void {
|
||
ElMessage.success('验证码已发送')
|
||
}
|
||
|
||
function onSubmit(): void {
|
||
if (!code.value) {
|
||
ElMessage.warning('请输入验证码')
|
||
return
|
||
}
|
||
if (!password.value) {
|
||
ElMessage.warning('请输入新密码')
|
||
return
|
||
}
|
||
if (!agree.value) {
|
||
ElMessage.warning('请先阅读并同意《用户协议》和《隐私政策》')
|
||
return
|
||
}
|
||
emit('submit', {
|
||
code: code.value,
|
||
password: password.value,
|
||
confirm: confirm.value,
|
||
})
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<Teleport to="body">
|
||
<div v-if="visible" class="change-pwd-dialog" @click.self="visible = false">
|
||
<div class="change-pwd-dialog__panel">
|
||
<h3 class="change-pwd-dialog__title">
|
||
修改密码
|
||
</h3>
|
||
|
||
<div class="change-pwd-dialog__field">
|
||
<label class="change-pwd-dialog__label">手机号</label>
|
||
<input
|
||
class="change-pwd-dialog__input"
|
||
type="text"
|
||
:value="props.phone"
|
||
readonly
|
||
>
|
||
</div>
|
||
|
||
<div class="change-pwd-dialog__field">
|
||
<label class="change-pwd-dialog__label">验证码</label>
|
||
<SmsCodeInput v-model:code="code" @get-code="onGetCode" />
|
||
</div>
|
||
|
||
<div class="change-pwd-dialog__field">
|
||
<label class="change-pwd-dialog__label">设置密码</label>
|
||
<input
|
||
v-model="password"
|
||
class="change-pwd-dialog__input"
|
||
type="password"
|
||
placeholder="请输入密码(6-20位)"
|
||
maxlength="20"
|
||
>
|
||
<PasswordStrength :password="password" />
|
||
</div>
|
||
|
||
<div class="change-pwd-dialog__field">
|
||
<label class="change-pwd-dialog__label">确认密码</label>
|
||
<input
|
||
v-model="confirm"
|
||
class="change-pwd-dialog__input"
|
||
type="password"
|
||
placeholder="请再次输入密码"
|
||
maxlength="20"
|
||
@keyup.enter="onSubmit"
|
||
>
|
||
</div>
|
||
|
||
<label class="change-pwd-dialog__agree">
|
||
<input v-model="agree" type="checkbox">
|
||
<span>我已阅读并同意《用户协议》和《隐私政策》</span>
|
||
</label>
|
||
|
||
<div class="change-pwd-dialog__footer">
|
||
<AppButton type="default" @click="visible = false">
|
||
取消
|
||
</AppButton>
|
||
<AppButton type="primary" @click="onSubmit">
|
||
确认修改
|
||
</AppButton>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</Teleport>
|
||
</template>
|
||
|
||
<style lang="less" scoped>
|
||
.change-pwd-dialog {
|
||
position: fixed;
|
||
inset: 0;
|
||
z-index: 1000;
|
||
.flex(row, center);
|
||
background: rgba(0, 0, 0, 0.45);
|
||
|
||
&__panel {
|
||
width: 440px;
|
||
max-width: calc(100vw - 32px);
|
||
padding: @spacing-lg;
|
||
border-radius: @radius-lg;
|
||
background: @white;
|
||
box-shadow: @shadow-lg;
|
||
.flex(column, flex-start);
|
||
gap: @spacing-base;
|
||
}
|
||
|
||
&__title {
|
||
font-size: @font-size-lg;
|
||
font-weight: 600;
|
||
color: @heading-color;
|
||
}
|
||
|
||
&__field {
|
||
.flex(column, flex-start);
|
||
gap: @spacing-xs;
|
||
width: 100%;
|
||
}
|
||
|
||
&__label {
|
||
font-size: @font-size-base;
|
||
color: @heading-color;
|
||
}
|
||
|
||
&__input {
|
||
width: 100%;
|
||
height: 40px;
|
||
padding: 0 @spacing-base;
|
||
border: 1px solid @border-color;
|
||
border-radius: @radius-base;
|
||
background: @bg-color-light;
|
||
font-size: @font-size-base;
|
||
outline: none;
|
||
transition: border-color 0.2s;
|
||
|
||
&:focus {
|
||
border-color: @primary-color;
|
||
}
|
||
|
||
&:read-only {
|
||
color: @text-color-secondary;
|
||
cursor: not-allowed;
|
||
}
|
||
}
|
||
|
||
&__agree {
|
||
.flex(row, flex-start, center);
|
||
gap: @spacing-sm;
|
||
font-size: @font-size-sm;
|
||
color: @text-color;
|
||
cursor: pointer;
|
||
|
||
input {
|
||
accent-color: @primary-color;
|
||
}
|
||
}
|
||
|
||
&__footer {
|
||
.flex(row, flex-end);
|
||
gap: @spacing-sm;
|
||
width: 100%;
|
||
}
|
||
}
|
||
</style>
|