sms、邮件数据库配置
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
<template>
|
||||
|
||||
<div class="account-settings">
|
||||
<div class="account-settings" v-loading="loading">
|
||||
<div class="settings-section">
|
||||
<h3>修改密码</h3>
|
||||
<el-form :model="passwordForm" :rules="passwordRules" ref="passwordFormRef" label-width="120px">
|
||||
@@ -30,10 +29,11 @@
|
||||
<i class="icon">📱</i>
|
||||
<div>
|
||||
<h4>手机绑定</h4>
|
||||
<p>已绑定手机:138****8888</p>
|
||||
<p v-if="userInfo.phone">已绑定手机:{{ maskPhone(userInfo.phone) }}</p>
|
||||
<p v-else class="not-bind">未绑定</p>
|
||||
</div>
|
||||
</div>
|
||||
<el-button size="small">修改</el-button>
|
||||
<el-button size="small" @click="showPhoneDialog">{{ userInfo.phone ? '修改' : '绑定' }}</el-button>
|
||||
</div>
|
||||
|
||||
<div class="security-item">
|
||||
@@ -41,23 +41,78 @@
|
||||
<i class="icon">✉️</i>
|
||||
<div>
|
||||
<h4>邮箱绑定</h4>
|
||||
<p>已绑定邮箱:user@example.com</p>
|
||||
<p v-if="userInfo.email">已绑定邮箱:{{ maskEmail(userInfo.email) }}</p>
|
||||
<p v-else class="not-bind">未绑定</p>
|
||||
</div>
|
||||
</div>
|
||||
<el-button size="small">修改</el-button>
|
||||
<el-button size="small" @click="showEmailDialog">{{ userInfo.email ? '修改' : '绑定' }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 手机号绑定弹窗 -->
|
||||
<el-dialog v-model="phoneDialogVisible" title="手机号绑定" width="500px">
|
||||
<el-form :model="phoneForm" :rules="phoneRules" ref="phoneFormRef" label-width="100px">
|
||||
<el-form-item label="手机号" prop="phone">
|
||||
<el-input v-model="phoneForm.phone" placeholder="请输入手机号" />
|
||||
</el-form-item>
|
||||
<el-form-item label="验证码" prop="code">
|
||||
<div class="code-input-wrapper">
|
||||
<el-input v-model="phoneForm.code" placeholder="请输入验证码" />
|
||||
<el-button
|
||||
:disabled="phoneCounting"
|
||||
@click="sendPhoneCode"
|
||||
>
|
||||
{{ phoneCounting ? `${phoneCountdown}秒后重试` : '发送验证码' }}
|
||||
</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="phoneDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleBindPhone" :loading="phoneBinding">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 邮箱绑定弹窗 -->
|
||||
<el-dialog v-model="emailDialogVisible" title="邮箱绑定" width="500px">
|
||||
<el-form :model="emailForm" :rules="emailRules" ref="emailFormRef" label-width="100px">
|
||||
<el-form-item label="邮箱" prop="email">
|
||||
<el-input v-model="emailForm.email" placeholder="请输入邮箱" />
|
||||
</el-form-item>
|
||||
<el-form-item label="验证码" prop="code">
|
||||
<div class="code-input-wrapper">
|
||||
<el-input v-model="emailForm.code" placeholder="请输入验证码" />
|
||||
<el-button
|
||||
:disabled="emailCounting"
|
||||
@click="sendEmailCode"
|
||||
>
|
||||
{{ emailCounting ? `${emailCountdown}秒后重试` : '发送验证码' }}
|
||||
</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="emailDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleBindEmail" :loading="emailBinding">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { ElForm, ElFormItem, ElInput, ElButton, ElMessage, type FormInstance, type FormRules } from 'element-plus';
|
||||
import { UserCenterLayout } from '@/views/user/user-center';
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { ElMessage, type FormInstance, type FormRules } from 'element-plus';
|
||||
import { userProfileApi } from '@/apis/usercenter';
|
||||
import { authApi } from '@/apis/system/auth';
|
||||
import type { UserVO } from '@/types';
|
||||
const loading = ref(false);
|
||||
const passwordFormRef = ref<FormInstance>();
|
||||
const phoneFormRef = ref<FormInstance>();
|
||||
const emailFormRef = ref<FormInstance>();
|
||||
|
||||
// 用户信息
|
||||
const userInfo = ref<UserVO>({});
|
||||
|
||||
const passwordForm = ref({
|
||||
oldPassword: '',
|
||||
@@ -88,18 +143,283 @@ const passwordRules: FormRules = {
|
||||
]
|
||||
};
|
||||
|
||||
// 手机号绑定
|
||||
const phoneDialogVisible = ref(false);
|
||||
const phoneBinding = ref(false);
|
||||
const phoneCounting = ref(false);
|
||||
const phoneCountdown = ref(60);
|
||||
const phoneForm = ref({
|
||||
phone: '',
|
||||
code: ''
|
||||
});
|
||||
|
||||
const phoneRules: FormRules = {
|
||||
phone: [
|
||||
{ required: true, message: '请输入手机号', trigger: 'blur' },
|
||||
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
|
||||
],
|
||||
code: [
|
||||
{ required: true, message: '请输入验证码', trigger: 'blur' }
|
||||
]
|
||||
};
|
||||
|
||||
// 邮箱绑定
|
||||
const emailDialogVisible = ref(false);
|
||||
const emailBinding = ref(false);
|
||||
const emailCounting = ref(false);
|
||||
const emailCountdown = ref(60);
|
||||
const emailForm = ref({
|
||||
email: '',
|
||||
code: ''
|
||||
});
|
||||
|
||||
const emailRules: FormRules = {
|
||||
email: [
|
||||
{ required: true, message: '请输入邮箱', trigger: 'blur' },
|
||||
{ type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur' }
|
||||
],
|
||||
code: [
|
||||
{ required: true, message: '请输入验证码', trigger: 'blur' }
|
||||
]
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
loadUserInfo();
|
||||
});
|
||||
|
||||
/**
|
||||
* 加载用户信息
|
||||
*/
|
||||
async function loadUserInfo() {
|
||||
loading.value = true;
|
||||
try {
|
||||
const result = await userProfileApi.getUserProfile();
|
||||
if (result.code === 200 && result.data) {
|
||||
userInfo.value = result.data;
|
||||
} else {
|
||||
ElMessage.error(result.message || '获取用户信息失败');
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('加载用户信息失败:', error);
|
||||
ElMessage.error('加载用户信息失败: ' + (error.message || '未知错误'));
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改密码
|
||||
*/
|
||||
async function handleChangePassword() {
|
||||
if (!passwordFormRef.value) return;
|
||||
|
||||
try {
|
||||
await passwordFormRef.value.validate();
|
||||
// TODO: 调用修改密码API
|
||||
ElMessage.success('密码修改成功');
|
||||
passwordFormRef.value.resetFields();
|
||||
} catch (error) {
|
||||
console.error('表单验证失败', error);
|
||||
const result = await userProfileApi.changePassword(
|
||||
passwordForm.value.oldPassword,
|
||||
passwordForm.value.newPassword
|
||||
);
|
||||
|
||||
if (result.code === 200) {
|
||||
ElMessage.success('密码修改成功');
|
||||
passwordFormRef.value.resetFields();
|
||||
} else {
|
||||
ElMessage.error(result.message || '密码修改失败');
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('密码修改失败:', error);
|
||||
ElMessage.error('密码修改失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示手机号绑定弹窗
|
||||
*/
|
||||
function showPhoneDialog() {
|
||||
phoneForm.value = {
|
||||
phone: userInfo.value.phone || '',
|
||||
code: ''
|
||||
};
|
||||
phoneDialogVisible.value = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送手机验证码
|
||||
*/
|
||||
async function sendPhoneCode() {
|
||||
if (!phoneForm.value.phone) {
|
||||
ElMessage.warning('请先输入手机号');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!/^1[3-9]\d{9}$/.test(phoneForm.value.phone)) {
|
||||
ElMessage.warning('请输入正确的手机号');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await authApi.sendSmsCode(phoneForm.value.phone);
|
||||
if (result.code === 200) {
|
||||
ElMessage.success('验证码已发送');
|
||||
startPhoneCountdown();
|
||||
} else {
|
||||
ElMessage.error(result.message || '发送失败');
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('发送验证码失败:', error);
|
||||
ElMessage.error('发送验证码失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始手机验证码倒计时
|
||||
*/
|
||||
function startPhoneCountdown() {
|
||||
phoneCounting.value = true;
|
||||
phoneCountdown.value = 60;
|
||||
const timer = setInterval(() => {
|
||||
phoneCountdown.value--;
|
||||
if (phoneCountdown.value <= 0) {
|
||||
clearInterval(timer);
|
||||
phoneCounting.value = false;
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定手机号
|
||||
*/
|
||||
async function handleBindPhone() {
|
||||
if (!phoneFormRef.value) return;
|
||||
|
||||
try {
|
||||
await phoneFormRef.value.validate();
|
||||
phoneBinding.value = true;
|
||||
|
||||
const result = await userProfileApi.bindPhone(
|
||||
phoneForm.value.phone,
|
||||
phoneForm.value.code
|
||||
);
|
||||
|
||||
if (result.code === 200) {
|
||||
ElMessage.success('手机号绑定成功');
|
||||
phoneDialogVisible.value = false;
|
||||
await loadUserInfo();
|
||||
} else {
|
||||
ElMessage.error(result.message || '绑定失败');
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('绑定手机号失败:', error);
|
||||
ElMessage.error('绑定失败');
|
||||
} finally {
|
||||
phoneBinding.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示邮箱绑定弹窗
|
||||
*/
|
||||
function showEmailDialog() {
|
||||
emailForm.value = {
|
||||
email: userInfo.value.email || '',
|
||||
code: ''
|
||||
};
|
||||
emailDialogVisible.value = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送邮箱验证码
|
||||
*/
|
||||
async function sendEmailCode() {
|
||||
if (!emailForm.value.email) {
|
||||
ElMessage.warning('请先输入邮箱');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(emailForm.value.email)) {
|
||||
ElMessage.warning('请输入正确的邮箱地址');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await authApi.sendEmailCode(emailForm.value.email);
|
||||
if (result.code === 200) {
|
||||
ElMessage.success('验证码已发送');
|
||||
startEmailCountdown();
|
||||
} else {
|
||||
ElMessage.error(result.message || '发送失败');
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('发送验证码失败:', error);
|
||||
ElMessage.error('发送验证码失败');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始邮箱验证码倒计时
|
||||
*/
|
||||
function startEmailCountdown() {
|
||||
emailCounting.value = true;
|
||||
emailCountdown.value = 60;
|
||||
const timer = setInterval(() => {
|
||||
emailCountdown.value--;
|
||||
if (emailCountdown.value <= 0) {
|
||||
clearInterval(timer);
|
||||
emailCounting.value = false;
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定邮箱
|
||||
*/
|
||||
async function handleBindEmail() {
|
||||
if (!emailFormRef.value) return;
|
||||
|
||||
try {
|
||||
await emailFormRef.value.validate();
|
||||
emailBinding.value = true;
|
||||
|
||||
const result = await userProfileApi.bindEmail(
|
||||
emailForm.value.email,
|
||||
emailForm.value.code
|
||||
);
|
||||
|
||||
if (result.code === 200) {
|
||||
ElMessage.success('邮箱绑定成功');
|
||||
emailDialogVisible.value = false;
|
||||
await loadUserInfo();
|
||||
} else {
|
||||
ElMessage.error(result.message || '绑定失败');
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('绑定邮箱失败:', error);
|
||||
ElMessage.error('绑定失败');
|
||||
} finally {
|
||||
emailBinding.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 脱敏手机号
|
||||
*/
|
||||
function maskPhone(phone: string): string {
|
||||
if (!phone || phone.length < 11) return phone;
|
||||
return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2');
|
||||
}
|
||||
|
||||
/**
|
||||
* 脱敏邮箱
|
||||
*/
|
||||
function maskEmail(email: string): string {
|
||||
if (!email) return email;
|
||||
const [username, domain] = email.split('@');
|
||||
if (username.length <= 3) {
|
||||
return email;
|
||||
}
|
||||
const maskedUsername = username.substring(0, 2) + '****' + username.substring(username.length - 1);
|
||||
return maskedUsername + '@' + domain;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@@ -157,6 +477,23 @@ async function handleChangePassword() {
|
||||
p {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
|
||||
&.not-bind {
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.code-input-wrapper {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
|
||||
.el-input {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.el-button {
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user