web登录注册修改

This commit is contained in:
2025-12-24 12:06:59 +08:00
parent 1b80fda0d7
commit 3d1e19030a
20 changed files with 662 additions and 252 deletions

View File

@@ -26,15 +26,30 @@
</div>
</div>
<!-- 重置密码方式切换 -->
<div class="reset-type-tabs">
<!-- 不支持密码找回提示 -->
<div v-if="!canResetPassword" class="reset-disabled-notice">
<div class="notice-icon">
<el-icon :size="48" color="#909399"><WarningFilled /></el-icon>
</div>
<h3 class="notice-title">不支持密码找回</h3>
<p class="notice-text">系统管理员尚未开启手机号或邮箱验证码找回功能</p>
<p class="notice-text">请联系管理员重置密码</p>
<el-button type="primary" @click="$router.push('/login')" class="back-button">返回登录</el-button>
</div>
<!-- 重置密码表单 -->
<template v-else>
<!-- 重置密码方式切换 -->
<div class="reset-type-tabs" v-if="showResetTypeTabs">
<div
v-if="smsLoginEnabled"
:class="['tab-item', { active: resetType === 'phone' }]"
@click="switchResetType('phone')"
>
手机号重置
</div>
<div
v-if="emailLoginEnabled"
:class="['tab-item', { active: resetType === 'email' }]"
@click="switchResetType('email')"
>
@@ -140,6 +155,7 @@
</el-button>
</el-form-item>
</el-form>
</template>
</div>
<!-- 底部信息 -->
@@ -156,9 +172,11 @@
</template>
<script setup lang="ts">
import { ref, reactive, onUnmounted } from 'vue';
import { ref, reactive, onBeforeUnmount, onUnmounted, computed, watch } from 'vue';
import { useRouter } from 'vue-router';
import { useStore } from 'vuex';
import { ElMessage, type FormInstance, type FormRules } from 'element-plus';
import { WarningFilled } from '@element-plus/icons-vue';
import { authApi } from '@/apis/system/auth';
// 响应式引用
@@ -171,9 +189,35 @@ let emailTimer: number | null = null;
// Composition API
const router = useRouter();
const store = useStore();
// 获取系统配置
const smsLoginEnabled = computed(() => store.getters['system/smsLoginEnabled']);
const emailLoginEnabled = computed(() => store.getters['system/emailLoginEnabled']);
// 是否支持密码找回
const canResetPassword = computed(() => smsLoginEnabled.value || emailLoginEnabled.value);
// 是否显示重置方式切换标签
const showResetTypeTabs = computed(() => smsLoginEnabled.value && emailLoginEnabled.value);
// 重置方式phone-手机号email-邮箱
const resetType = ref<'phone' | 'email'>('phone');
const getDefaultResetType = (): 'phone' | 'email' => {
if (!smsLoginEnabled.value && emailLoginEnabled.value) {
return 'email';
}
return 'phone';
};
const resetType = ref<'phone' | 'email'>(getDefaultResetType());
// 监听配置变化
watch([smsLoginEnabled, emailLoginEnabled], () => {
if (resetType.value === 'phone' && !smsLoginEnabled.value && emailLoginEnabled.value) {
resetType.value = 'email';
} else if (resetType.value === 'email' && !emailLoginEnabled.value && smsLoginEnabled.value) {
resetType.value = 'phone';
}
});
// 表单数据
const forgotForm = reactive({
@@ -690,9 +734,40 @@ onUnmounted(() => {
line-height: 2;
color: #D9D9D9;
text-align: center;
padding-bottom: 0;
}
}
.reset-disabled-notice {
text-align: center;
padding: 60px 20px;
.notice-icon {
margin-bottom: 24px;
}
.notice-title {
font-size: 20px;
font-weight: 600;
color: #303133;
margin: 0 0 16px 0;
}
.notice-text {
font-size: 14px;
color: #606266;
margin: 8px 0;
line-height: 1.6;
}
.back-button {
margin-top: 32px;
width: 200px;
background-color: #C62828;
border: none;
}
}
// Element Plus 组件样式覆盖
:deep(.el-form-item) {
margin-bottom: 16px;

View File

@@ -27,23 +27,38 @@
<h2 class="forgot-password-title">找回密码</h2>
</div>
<!-- 重置密码方式切换 -->
<div class="reset-type-tabs">
<div
:class="['tab-item', { active: resetType === 'phone' }]"
@click="switchResetType('phone')"
>
手机号重置
</div>
<div
:class="['tab-item', { active: resetType === 'email' }]"
@click="switchResetType('email')"
>
邮箱重置
<!-- 不支持密码找回提示 -->
<div v-if="!canResetPassword" class="reset-disabled-notice">
<div class="notice-icon">
<el-icon :size="48" color="#909399"><WarningFilled /></el-icon>
</div>
<h3 class="notice-title">不支持密码找回</h3>
<p class="notice-text">系统管理员尚未开启手机号或邮箱验证码找回功能</p>
<p class="notice-text">请联系管理员重置密码</p>
<el-button type="primary" @click="goToLogin" class="back-button">返回登录</el-button>
</div>
<!-- 忘记密码表单 -->
<!-- 重置密码表单 -->
<template v-else>
<!-- 重置密码方式切换 -->
<div class="reset-type-tabs" v-if="showResetTypeTabs">
<div
v-if="smsLoginEnabled"
:class="['tab-item', { active: resetType === 'phone' }]"
@click="switchResetType('phone')"
>
手机号重置
</div>
<div
v-if="emailLoginEnabled"
:class="['tab-item', { active: resetType === 'email' }]"
@click="switchResetType('email')"
>
邮箱重置
</div>
</div>
<!-- 忘记密码表单 -->
<el-form
ref="forgotFormRef"
:model="forgotForm"
@@ -139,6 +154,7 @@
</el-button>
</el-form-item>
</el-form>
</template>
</div>
<!-- 底部信息 -->
@@ -155,9 +171,11 @@
</template>
<script setup lang="ts">
import { ref, reactive } from 'vue';
import { ref, reactive, onBeforeUnmount, computed, watch } from 'vue';
import { useRouter } from 'vue-router';
import { useStore } from 'vuex';
import { ElMessage, type FormInstance, type FormRules } from 'element-plus';
import { WarningFilled } from '@element-plus/icons-vue';
import { authApi } from '@/apis/system/auth';
// 响应式引用
@@ -170,9 +188,36 @@ let emailTimer: number | null = null;
// Composition API
const router = useRouter();
const store = useStore();
// 获取系统配置
const smsLoginEnabled = computed(() => store.getters['system/smsLoginEnabled']);
const emailLoginEnabled = computed(() => store.getters['system/emailLoginEnabled']);
// 是否支持密码找回(至少一种方式启用)
const canResetPassword = computed(() => smsLoginEnabled.value || emailLoginEnabled.value);
// 是否显示重置方式切换标签(两种方式都启用)
const showResetTypeTabs = computed(() => smsLoginEnabled.value && emailLoginEnabled.value);
// 重置方式phone-手机号email-邮箱
const resetType = ref<'phone' | 'email'>('phone');
// 根据配置设置默认值
const getDefaultResetType = (): 'phone' | 'email' => {
if (!smsLoginEnabled.value && emailLoginEnabled.value) {
return 'email';
}
return 'phone';
};
const resetType = ref<'phone' | 'email'>(getDefaultResetType());
// 监听配置变化,自动调整重置方式
watch([smsLoginEnabled, emailLoginEnabled], () => {
if (resetType.value === 'phone' && !smsLoginEnabled.value && emailLoginEnabled.value) {
resetType.value = 'email';
} else if (resetType.value === 'email' && !emailLoginEnabled.value && smsLoginEnabled.value) {
resetType.value = 'phone';
}
});
// 表单数据
const forgotForm = reactive({
@@ -747,4 +792,34 @@ onUnmounted(() => {
margin: 0;
}
}
.reset-disabled-notice {
text-align: center;
padding: 60px 20px;
.notice-icon {
margin-bottom: 24px;
}
.notice-title {
font-size: 20px;
font-weight: 600;
color: #303133;
margin: 0 0 16px 0;
}
.notice-text {
font-size: 14px;
color: #606266;
margin: 8px 0;
line-height: 1.6;
}
.back-button {
margin-top: 32px;
width: 200px;
background-color: #C62828;
border: none;
}
}
</style>

View File

@@ -35,6 +35,7 @@
密码登录
</div>
<div
v-if="showCaptchaLoginTab"
:class="['tab-item', { active: loginMode === 'captcha' }]"
@click="switchLoginMode('captcha')"
>
@@ -74,14 +75,16 @@
<!-- 验证码登录模式 -->
<template v-else>
<!-- 登录方式选择 -->
<div class="captcha-type-tabs">
<div class="captcha-type-tabs" v-if="showCaptchaTypeTabs">
<div
v-if="smsLoginEnabled"
:class="['captcha-tab-item', { active: captchaType === 'phone' }]"
@click="switchCaptchaType('phone')"
>
手机号
</div>
<div
v-if="emailLoginEnabled"
:class="['captcha-tab-item', { active: captchaType === 'email' }]"
@click="switchCaptchaType('email')"
>
@@ -196,7 +199,7 @@
</template>
<script setup lang="ts">
import { ref, reactive, onMounted, computed } from 'vue';
import { ref, reactive, onMounted, computed, watch } from 'vue';
import { useRouter, useRoute } from 'vue-router';
import { useStore } from 'vuex';
import { ElMessage, type FormInstance, type FormRules } from 'element-plus';
@@ -210,19 +213,45 @@ const loginLoading = ref(false);
const showCaptcha = ref(false);
const captchaImage = ref('');
// Composition API
const router = useRouter();
const route = useRoute();
const store = useStore();
// 获取系统配置
const smsLoginEnabled = computed(() => store.getters['system/smsLoginEnabled']);
const emailLoginEnabled = computed(() => store.getters['system/emailLoginEnabled']);
// 是否显示验证码登录选项卡
const showCaptchaLoginTab = computed(() => smsLoginEnabled.value || emailLoginEnabled.value);
// 是否显示验证码类型切换选项卡
const showCaptchaTypeTabs = computed(() => smsLoginEnabled.value && emailLoginEnabled.value);
// 登录模式password-密码登录captcha-验证码登录
const loginMode = ref<'password' | 'captcha'>('password');
// 验证码类型phone-手机号email-邮箱
const captchaType = ref<'phone' | 'email'>('phone');
const getDefaultCaptchaType = (): 'phone' | 'email' => {
if (!smsLoginEnabled.value && emailLoginEnabled.value) {
return 'email';
}
return 'phone';
};
const captchaType = ref<'phone' | 'email'>(getDefaultCaptchaType());
// 倒计时
const smsCountdown = ref(0);
const emailCountdown = ref(0);
// Composition API
const router = useRouter();
const route = useRoute();
const store = useStore();
// 监听配置变化
watch([smsLoginEnabled, emailLoginEnabled], () => {
if (captchaType.value === 'phone' && !smsLoginEnabled.value && emailLoginEnabled.value) {
captchaType.value = 'email';
} else if (captchaType.value === 'email' && !emailLoginEnabled.value && smsLoginEnabled.value) {
captchaType.value = 'phone';
}
});
// 表单数据
const loginForm = reactive<LoginParam>({

View File

@@ -35,6 +35,7 @@
密码登录
</div>
<div
v-if="showCaptchaLoginTab"
:class="['tab-item', { active: loginMode === 'captcha' }]"
@click="switchLoginMode('captcha')"
>
@@ -74,14 +75,16 @@
<!-- 验证码登录模式 -->
<template v-else>
<!-- 登录方式选择 -->
<div class="captcha-type-tabs">
<div class="captcha-type-tabs" v-if="showCaptchaTypeTabs">
<div
v-if="smsLoginEnabled"
:class="['captcha-tab-item', { active: captchaType === 'phone' }]"
@click="switchCaptchaType('phone')"
>
手机号
</div>
<div
v-if="emailLoginEnabled"
:class="['captcha-tab-item', { active: captchaType === 'email' }]"
@click="switchCaptchaType('email')"
>
@@ -196,7 +199,7 @@
</template>
<script setup lang="ts">
import { ref, reactive, onMounted, computed } from 'vue';
import { ref, reactive, onMounted, computed, watch } from 'vue';
import { useRouter, useRoute } from 'vue-router';
import { useStore } from 'vuex';
import { ElMessage, type FormInstance, type FormRules } from 'element-plus';
@@ -210,19 +213,39 @@ const loginLoading = ref(false);
const showCaptcha = ref(false);
const captchaImage = ref('');
// Composition API
const router = useRouter();
const route = useRoute();
const store = useStore();
// 获取系统配置(必须先定义,因为后续初始化会用到)
const smsLoginEnabled = computed(() => store.getters['system/smsLoginEnabled']);
const emailLoginEnabled = computed(() => store.getters['system/emailLoginEnabled']);
// 登录模式password-密码登录captcha-验证码登录
const loginMode = ref<'password' | 'captcha'>('password');
// 验证码类型phone-手机号email-邮箱
const captchaType = ref<'phone' | 'email'>('phone');
// 根据配置设置默认值
const getDefaultCaptchaType = (): 'phone' | 'email' => {
// 如果只启用了邮箱登录,默认使用邮箱
if (!smsLoginEnabled.value && emailLoginEnabled.value) {
return 'email';
}
// 其他情况默认使用手机号
return 'phone';
};
const captchaType = ref<'phone' | 'email'>(getDefaultCaptchaType());
// 倒计时
const smsCountdown = ref(0);
const emailCountdown = ref(0);
// Composition API
const router = useRouter();
const route = useRoute();
const store = useStore();
// 是否显示验证码登录选项卡(至少一个验证码登录方式启用)
const showCaptchaLoginTab = computed(() => smsLoginEnabled.value || emailLoginEnabled.value);
// 是否显示验证码类型切换选项卡(两种验证码方式都启用)
const showCaptchaTypeTabs = computed(() => smsLoginEnabled.value && emailLoginEnabled.value);
// 表单数据
const loginForm = reactive<LoginParam>({
@@ -334,15 +357,22 @@ const switchCaptchaType = (type: 'phone' | 'email') => {
if (captchaType.value === type) return;
captchaType.value = type;
// 清空相关表单数据和验证
loginFormRef.value?.clearValidate();
loginForm.phone = '';
loginForm.email = '';
loginForm.captcha = '';
loginForm.captchaId = '';
loginFormRef.value?.clearValidate();
};
// 监听配置变化,动态调整验证码类型
watch([smsLoginEnabled, emailLoginEnabled], () => {
// 如果当前选中的验证码类型未启用,切换到可用的类型
if (captchaType.value === 'phone' && !smsLoginEnabled.value && emailLoginEnabled.value) {
captchaType.value = 'email';
} else if (captchaType.value === 'email' && !emailLoginEnabled.value && smsLoginEnabled.value) {
captchaType.value = 'phone';
}
});
// 发送短信验证码
const handleSendSmsCode = async () => {
// 验证手机号

View File

@@ -36,6 +36,7 @@
用户名
</div>
<div
v-if="smsLoginEnabled"
class="tab-item"
:class="{ active: registerType === RegisterType.PHONE }"
@click="switchRegisterType(RegisterType.PHONE)"
@@ -43,6 +44,7 @@
手机号
</div>
<div
v-if="emailLoginEnabled"
class="tab-item"
:class="{ active: registerType === RegisterType.EMAIL }"
@click="switchRegisterType(RegisterType.EMAIL)"
@@ -183,8 +185,9 @@
</template>
<script setup lang="ts">
import { ref, reactive, computed, onUnmounted } from 'vue';
import { ref, reactive, computed, onUnmounted, watch } from 'vue';
import { useRouter } from 'vue-router';
import { useStore } from 'vuex';
import { ElMessage, type FormInstance, type FormRules } from 'element-plus';
import type { RegisterParam } from '@/types';
import { RegisterType } from '@/types';
@@ -200,10 +203,24 @@ let emailTimer: number | null = null;
// Composition API
const router = useRouter();
const store = useStore();
// 获取系统配置
const smsLoginEnabled = computed(() => store.getters['system/smsLoginEnabled']);
const emailLoginEnabled = computed(() => store.getters['system/emailLoginEnabled']);
// 注册方式
const registerType = ref<RegisterType>(RegisterType.USERNAME);
// 监听配置变化
watch([smsLoginEnabled, emailLoginEnabled], () => {
if (registerType.value === RegisterType.PHONE && !smsLoginEnabled.value) {
registerType.value = RegisterType.USERNAME;
} else if (registerType.value === RegisterType.EMAIL && !emailLoginEnabled.value) {
registerType.value = RegisterType.USERNAME;
}
});
// 表单数据
const registerForm = reactive<RegisterParam>({
registerType: RegisterType.USERNAME,

View File

@@ -36,6 +36,7 @@
用户名
</div>
<div
v-if="smsLoginEnabled"
class="tab-item"
:class="{ active: registerType === RegisterType.PHONE }"
@click="switchRegisterType(RegisterType.PHONE)"
@@ -43,6 +44,7 @@
手机号
</div>
<div
v-if="emailLoginEnabled"
class="tab-item"
:class="{ active: registerType === RegisterType.EMAIL }"
@click="switchRegisterType(RegisterType.EMAIL)"
@@ -190,8 +192,9 @@
</template>
<script setup lang="ts">
import { ref, reactive, computed } from 'vue';
import { ref, reactive, computed, watch } from 'vue';
import { useRouter } from 'vue-router';
import { useStore } from 'vuex';
import { ElMessage, type FormInstance, type FormRules } from 'element-plus';
import type { RegisterParam } from '@/types';
import { RegisterType } from '@/types';
@@ -207,10 +210,25 @@ let emailTimer: number | null = null;
// Composition API
const router = useRouter();
const store = useStore();
// 获取系统配置
const smsLoginEnabled = computed(() => store.getters['system/smsLoginEnabled']);
const emailLoginEnabled = computed(() => store.getters['system/emailLoginEnabled']);
// 注册方式
const registerType = ref<RegisterType>(RegisterType.USERNAME);
// 监听配置变化,自动调整注册方式
watch([smsLoginEnabled, emailLoginEnabled], () => {
// 如果当前选中的注册方式未启用,切换到用户名注册
if (registerType.value === RegisterType.PHONE && !smsLoginEnabled.value) {
registerType.value = RegisterType.USERNAME;
} else if (registerType.value === RegisterType.EMAIL && !emailLoginEnabled.value) {
registerType.value = RegisterType.USERNAME;
}
});
// 表单数据
const registerForm = reactive<RegisterParam>({
registerType: RegisterType.USERNAME,