feat 搭建架构骨架
This commit is contained in:
229
src/views/profile/cpns/BindContactDialog.vue
Normal file
229
src/views/profile/cpns/BindContactDialog.vue
Normal file
@@ -0,0 +1,229 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { AppButton, SmsCodeInput } from '@/components'
|
||||
|
||||
export type BindContactMode =
|
||||
| 'phone-bind'
|
||||
| 'phone-rebind'
|
||||
| 'email-bind'
|
||||
| 'email-rebind'
|
||||
| 'identity'
|
||||
|
||||
interface Props {
|
||||
mode: BindContactMode
|
||||
hintPhone?: string
|
||||
hintEmail?: string
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
hintPhone: '',
|
||||
hintEmail: '',
|
||||
})
|
||||
|
||||
const visible = defineModel<boolean>('visible', { default: false })
|
||||
|
||||
const emit = defineEmits<{ submit: [payload: { contact: string; code: string }] }>()
|
||||
|
||||
const contact = ref('')
|
||||
const code = ref('')
|
||||
const agree = ref(false)
|
||||
|
||||
const isEmailMode = computed(() => props.mode === 'email-bind' || props.mode === 'email-rebind')
|
||||
const isIdentity = computed(() => props.mode === 'identity')
|
||||
|
||||
const title = computed<string>(() => {
|
||||
const map: Record<BindContactMode, string> = {
|
||||
'phone-bind': '绑定手机号',
|
||||
'phone-rebind': '验证并换绑',
|
||||
'email-bind': '绑定邮箱',
|
||||
'email-rebind': '更换绑定邮箱',
|
||||
identity: '安全身份验证',
|
||||
}
|
||||
return map[props.mode]
|
||||
})
|
||||
|
||||
const fieldLabel = computed<string>(() => {
|
||||
const map: Record<BindContactMode, string> = {
|
||||
'phone-bind': '手机号',
|
||||
'phone-rebind': '新手机号',
|
||||
'email-bind': '邮箱',
|
||||
'email-rebind': '新邮箱',
|
||||
identity: '手机号',
|
||||
}
|
||||
return map[props.mode]
|
||||
})
|
||||
|
||||
const submitText = computed<string>(() => {
|
||||
const map: Record<BindContactMode, string> = {
|
||||
'phone-bind': '验证并绑定',
|
||||
'phone-rebind': '验证并换绑',
|
||||
'email-bind': '确认修改',
|
||||
'email-rebind': '确认修改',
|
||||
identity: '验证',
|
||||
}
|
||||
return map[props.mode]
|
||||
})
|
||||
|
||||
const contactPlaceholder = computed<string>(() => {
|
||||
if (isEmailMode.value) return props.hintEmail || '请输入邮箱'
|
||||
return props.mode === 'phone-rebind' ? '请输入新手机号' : '请输入手机号'
|
||||
})
|
||||
|
||||
const identityDesc = computed(() =>
|
||||
isIdentity.value
|
||||
? `安全身份验证:为确保账号安全,请完成身份验证后再进行账号操作,发送验证码至 ${props.hintPhone}`
|
||||
: '',
|
||||
)
|
||||
|
||||
watch(visible, (open) => {
|
||||
if (!open) return
|
||||
contact.value = isIdentity.value ? props.hintPhone : ''
|
||||
code.value = ''
|
||||
agree.value = false
|
||||
})
|
||||
|
||||
function onGetCode(): void {
|
||||
ElMessage.success('验证码已发送')
|
||||
}
|
||||
|
||||
function onSubmit(): void {
|
||||
if (!contact.value.trim()) {
|
||||
ElMessage.warning(isEmailMode.value ? '请输入邮箱' : '请输入手机号')
|
||||
return
|
||||
}
|
||||
if (!code.value) {
|
||||
ElMessage.warning('请输入验证码')
|
||||
return
|
||||
}
|
||||
if (!isIdentity.value && !agree.value) {
|
||||
ElMessage.warning('请先阅读并同意《用户协议》和《隐私政策》')
|
||||
return
|
||||
}
|
||||
emit('submit', { contact: contact.value.trim(), code: code.value })
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Teleport to="body">
|
||||
<div v-if="visible" class="bind-dialog" @click.self="visible = false">
|
||||
<div class="bind-dialog__panel">
|
||||
<h3 class="bind-dialog__title">
|
||||
{{ title }}
|
||||
</h3>
|
||||
<p v-if="identityDesc" class="bind-dialog__desc text-secondary">
|
||||
{{ identityDesc }}
|
||||
</p>
|
||||
|
||||
<div class="bind-dialog__field">
|
||||
<label class="bind-dialog__label">{{ fieldLabel }}</label>
|
||||
<input
|
||||
v-model="contact"
|
||||
class="bind-dialog__input"
|
||||
type="text"
|
||||
:placeholder="contactPlaceholder"
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="bind-dialog__field">
|
||||
<label class="bind-dialog__label">验证码</label>
|
||||
<SmsCodeInput v-model:code="code" @get-code="onGetCode" />
|
||||
</div>
|
||||
|
||||
<label v-if="!isIdentity" class="bind-dialog__agree">
|
||||
<input v-model="agree" type="checkbox">
|
||||
<span>我已阅读并同意《用户协议》和《隐私政策》</span>
|
||||
</label>
|
||||
|
||||
<div class="bind-dialog__footer">
|
||||
<AppButton type="default" @click="visible = false">
|
||||
取消
|
||||
</AppButton>
|
||||
<AppButton type="primary" @click="onSubmit">
|
||||
{{ submitText }}
|
||||
</AppButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Teleport>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.bind-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;
|
||||
}
|
||||
|
||||
&__desc {
|
||||
padding: @spacing-sm @spacing-base;
|
||||
border-radius: @radius-base;
|
||||
background: @bg-color-light;
|
||||
font-size: @font-size-sm;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
&__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;
|
||||
}
|
||||
}
|
||||
|
||||
&__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>
|
||||
Reference in New Issue
Block a user