更新保存代码

This commit is contained in:
2025-12-24 15:19:57 +08:00
parent ee5ed42a0d
commit c4f4babe1d
8 changed files with 2363 additions and 59 deletions

View File

@@ -0,0 +1,76 @@
import request from '../utils/request'
//注册
export function register(data) {
return request({
url: `/lease/auth/register`,
method: 'post',
data
})
}
//登录
export function getLogin(data) {
return request({
url: `/lease/auth/login`,
method: 'post',
data
})
}
//退出登录
export function getLogout(data) {
return request({
url: `/lease/auth/logout`,
method: 'post',
data
})
}
//发送登录验证码
export function sendLoginCode(data) {
return request({
url: `/lease/auth/sendLoginCode`,
method: 'post',
data
})
}
//发送注册验证码
export function sendEmailCode(data) {
return request({
url: `/lease/auth/sendRegisterCode`,
method: 'post',
data
})
}
//发送修改密码验证码
export function sendUpdatePwdCode(data) {
return request({
url: `/lease/auth/sendUpdatePwdCode`,
method: 'post',
data
})
}
//修改密码
export function updatePassword(data) {
return request({
url: `/lease/auth/updatePassword`,
method: 'post',
data
})
}

View File

@@ -2,6 +2,8 @@
<div class="header-container"> <div class="header-container">
<!-- 顶部导航栏 --> <!-- 顶部导航栏 -->
<nav class="navbar"> <nav class="navbar">
<!-- 左侧导航按钮 -->
<div class="nav-left">
<router-link <router-link
v-for="nav in navigation" v-for="nav in navigation"
:key="nav.path" :key="nav.path"
@@ -14,6 +16,33 @@
<span class="nav-text">{{ nav.name }}</span> <span class="nav-text">{{ nav.name }}</span>
<span v-if="nav.path === '/cart'" class="cart-count">({{ cartItemCount }})</span> <span v-if="nav.path === '/cart'" class="cart-count">({{ cartItemCount }})</span>
</router-link> </router-link>
</div>
<!-- 右侧用户登录状态 -->
<div class="nav-right">
<!-- 未登录显示注册/登录按钮 -->
<div v-if="!isLoggedIn" class="auth-buttons">
<button class="auth-btn register-btn" @click="goToRegister">
注册
</button>
<button class="auth-btn login-btn" @click="goToLogin">
登录
</button>
</div>
<!-- 已登录显示用户邮箱和退出按钮 -->
<div v-else class="user-info">
<span class="user-email">{{ userEmail }}</span>
<el-button
type="text"
size="small"
class="logout-btn"
@click="handleLogout"
>
退出
</el-button>
</div>
</div>
</nav> </nav>
<!-- 面包屑导航 --> <!-- 面包屑导航 -->
@@ -44,16 +73,25 @@ export default {
cart: [], cart: [],
// 服务端购物车数量(统计 productMachineDtoList 的总条数) // 服务端购物车数量(统计 productMachineDtoList 的总条数)
cartServerCount: 0, cartServerCount: 0,
navigation: mainNavigation navigation: mainNavigation,
// 用户邮箱
userEmail: ''
} }
}, },
computed: { computed: {
// 计算购物车数量
cartItemCount() { cartItemCount() {
// 只使用服务端数量,避免与本地缓存数量不一致 // 只使用服务端数量,避免与本地缓存数量不一致
return Number.isFinite(this.cartServerCount) ? this.cartServerCount : 0 return Number.isFinite(this.cartServerCount) ? this.cartServerCount : 0
}, },
// 计算面包屑导航
breadcrumbs() { breadcrumbs() {
return getBreadcrumb(this.$route.path) return getBreadcrumb(this.$route.path)
},
// 判断是否已登录检查localStorage中是否有token
isLoggedIn() {
const token = localStorage.getItem('token')
return !!token // 有token就是已登录没有就是未登录
} }
}, },
watch: {}, watch: {},
@@ -65,6 +103,8 @@ export default {
this.loadServerCartCount() this.loadServerCartCount()
// 监听应用内购物车更新事件 // 监听应用内购物车更新事件
window.addEventListener('cart-updated', this.handleCartUpdated) window.addEventListener('cart-updated', this.handleCartUpdated)
// 加载用户信息(邮箱)
this.loadUserEmail()
}, },
beforeDestroy() { beforeDestroy() {
window.removeEventListener('storage', this.handleStorageChange) window.removeEventListener('storage', this.handleStorageChange)
@@ -125,9 +165,58 @@ export default {
// 无显式数量则主动刷新 // 无显式数量则主动刷新
this.loadServerCartCount() this.loadServerCartCount()
}, },
/**
* 跳转到登录页
*/
goToLogin() {
this.$router.push('/login')
},
/**
* 跳转到注册页
*/
goToRegister() {
this.$router.push('/register')
},
/**
* 加载用户邮箱
* 从localStorage读取用户信息获取邮箱
*/
loadUserEmail() {
try {
// 从localStorage读取用户信息
const userInfoStr = localStorage.getItem('userInfo')
if (userInfoStr) {
const userInfo = JSON.parse(userInfoStr)
// 获取邮箱,如果没有邮箱就显示用户名
this.userEmail = userInfo.email || userInfo.username || '用户'
}
} catch (e) {
console.error('读取用户信息失败:', e)
this.userEmail = ''
}
},
/**
* 退出登录
* 清除所有登录信息,跳转到登录页
*/
handleLogout() { handleLogout() {
// 清除localStorage中的所有登录信息
localStorage.removeItem('token')
localStorage.removeItem('userInfo')
localStorage.removeItem('leasEmail')
localStorage.removeItem('userId')
localStorage.removeItem('username')
// 清空购物车
this.user = null this.user = null
this.cart = [] this.cart = []
this.userEmail = ''
// 提示用户
this.$message.success('退出登录成功')
// 跳转到登录页
this.$router.push('/login')
}, },
getBreadcrumbPath(index) { getBreadcrumbPath(index) {
const paths = ['/productList', '/cart', '/checkout'] const paths = ['/productList', '/cart', '/checkout']
@@ -144,14 +233,30 @@ export default {
width: 100%; width: 100%;
} }
/* 导航栏布局:导航按钮在中间,登录状态在右边 */
.navbar { .navbar {
display: flex; display: flex;
justify-content: center; justify-content: center; /* 主内容居中 */
gap: 24px; align-items: center;
background: #fff; background: #fff;
border-bottom: 1px solid #eee; border-bottom: 1px solid #eee;
padding: 16px 0; padding: 16px 32px;
margin-bottom: 16px; margin-bottom: 16px;
position: relative; /* 让右侧元素可以绝对定位 */
}
/* 左侧导航按钮区域(实际在中间显示) */
.nav-left {
display: flex;
gap: 24px;
}
/* 右侧用户登录区域(绝对定位到右边) */
.nav-right {
display: flex;
align-items: center;
position: absolute;
right: 32px; /* 距离右边32px */
} }
.nav-btn { .nav-btn {
@@ -171,14 +276,19 @@ export default {
position: relative; position: relative;
} }
/* 导航按钮悬停效果 */
.nav-btn:hover { .nav-btn:hover {
background: #f8f9fa; background: #f5f7ff;
color: #667eea;
transform: translateY(-2px); transform: translateY(-2px);
} }
/* 导航按钮激活状态 - 紫色渐变,跟登录按钮一样 */
.nav-btn.active { .nav-btn.active {
background: #42b983; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #fff; color: #fff;
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
transform: translateY(-2px);
} }
.nav-icon { .nav-icon {
@@ -233,12 +343,87 @@ export default {
color: #ccc; color: #ccc;
} }
/* 响应式设计 */ /* 未登录:注册/登录按钮样式 */
.auth-buttons {
display: flex;
gap: 12px;
}
.auth-btn {
padding: 8px 20px;
border-radius: 6px;
font-size: 14px;
font-weight: 600;
text-decoration: none;
transition: all 0.3s ease;
cursor: pointer;
}
/* 注册按钮 - 白色背景 */
.register-btn {
color: #667eea;
border: 1px solid #667eea;
background: white;
cursor: pointer;
}
.register-btn:hover {
background: #f5f7ff;
}
/* 登录按钮 - 紫色背景 */
.login-btn {
color: white;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border: none;
cursor: pointer;
}
.login-btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
}
/* 已登录:用户信息样式 */
.user-info {
display: flex;
align-items: center;
gap: 16px;
padding: 8px 16px;
/* 去掉灰色背景,更简洁 */
}
/* 用户邮箱显示 */
.user-email {
color: #2c3e50;
font-size: 14px;
font-weight: 600;
}
/* 退出按钮样式 */
.logout-btn {
color: #e74c3c;
font-size: 14px;
padding: 4px 12px;
}
.logout-btn:hover {
color: #c0392b;
background: #fee;
}
/* 响应式设计 - 手机端适配 */
@media (max-width: 768px) { @media (max-width: 768px) {
.navbar { .navbar {
flex-direction: column; flex-direction: column;
gap: 12px; gap: 16px;
padding: 12px 0; padding: 12px 16px;
}
.nav-left {
flex-direction: column;
width: 100%;
gap: 8px;
} }
.nav-btn { .nav-btn {
@@ -247,6 +432,25 @@ export default {
padding: 16px 20px; padding: 16px 20px;
} }
.nav-right {
width: 100%;
justify-content: center;
}
.auth-buttons {
width: 100%;
}
.auth-btn {
flex: 1;
text-align: center;
}
.user-info {
width: 100%;
justify-content: center;
}
.breadcrumb { .breadcrumb {
margin: 0 12px 16px 12px; margin: 0 12px 16px 12px;
padding: 8px 16px; padding: 8px 16px;

View File

@@ -3,6 +3,40 @@
* @description 定义所有电商页面的路由配置 * @description 定义所有电商页面的路由配置
*/ */
// 认证相关路由独立布局无Header
export const authRoutes = [
{
path: '/login',
name: 'Login',
component: () => import('../views/auth/login.vue'),
meta: {
title: '用户登录',
description: '登录到您的账户',
requiresAuth: false // 不需要登录即可访问
}
},
{
path: '/register',
name: 'Register',
component: () => import('../views/auth/register.vue'),
meta: {
title: '用户注册',
description: '创建新账户',
requiresAuth: false
}
},
{
path: '/reset-password',
name: 'ResetPassword',
component: () => import('../views/auth/reset-password.vue'),
meta: {
title: '重置密码',
description: '重置您的账户密码',
requiresAuth: false
}
}
]
// 商品相关路由 // 商品相关路由
export const productRoutes = [ export const productRoutes = [
{ {
@@ -100,13 +134,9 @@ export const accountRoutes = [
} }
}, },
{ {
// 兼容旧入口:收款记录 -> 卖家资金流水收款tab
path: 'receipt-record', path: 'receipt-record',
name: 'accountReceiptRecord', name: 'accountReceiptRecord',
redirect: (to) => ({ component: () => import('../views/account/receiptRecord.vue'),
path: '/account/seller-funds-flow',
query: { ...(to && to.query ? to.query : {}), tab: 'receipt' }
}),
meta: { meta: {
title: '收款记录', title: '收款记录',
description: '卖家收款流水记录', description: '卖家收款流水记录',
@@ -114,29 +144,15 @@ export const accountRoutes = [
} }
}, },
{ {
// 兼容旧入口:提现记录 -> 卖家资金流水提现tab
path: 'withdraw-record', path: 'withdraw-record',
name: 'accountWithdrawRecord', name: 'accountWithdrawRecord',
redirect: (to) => ({ component: () => import('../views/account/withdrawRecord.vue'),
path: '/account/seller-funds-flow',
query: { ...(to && to.query ? to.query : {}), tab: 'withdraw' }
}),
meta: { meta: {
title: '提现记录', title: '提现记录',
description: '卖家提现流水记录', description: '卖家提现流水记录',
allAuthority: ['all'] allAuthority: ['all']
} }
}, },
{
path: 'seller-funds-flow',
name: 'accountSellerFundsFlow',
component: () => import('../views/account/sellerFundsFlow.vue'),
meta: {
title: '资金流水',
description: '卖家收款/提现记录切换查看',
allAuthority: ['all']
}
},
{ {
path: 'shop-new', path: 'shop-new',
name: 'accountShopNew', name: 'accountShopNew',
@@ -197,16 +213,6 @@ export const accountRoutes = [
allAuthority: ['all'] allAuthority: ['all']
} }
}, },
{
path: 'purchased-machine-detail/:id',
name: 'purchasedMachineDetail',
component: () => import('../views/account/purchasedMachineDetail.vue'),
meta: {
title: '已购商品详情',
description: '查看已购买商品的详细信息',
allAuthority: ['all']
}
},
{ {
path: 'funds-flow', path: 'funds-flow',
name: 'accountFundsFlow', name: 'accountFundsFlow',
@@ -281,6 +287,10 @@ export const childrenRoutes = [
// 主路由配置 // 主路由配置
export const mainRoutes = [ export const mainRoutes = [
// 认证路由独立布局无Header
...authRoutes,
// 主应用路由带Layout
{ {
path: '/', path: '/',
name: 'Home', name: 'Home',
@@ -288,6 +298,7 @@ export const mainRoutes = [
redirect: '/productList', redirect: '/productList',
children: childrenRoutes children: childrenRoutes
}, },
// 404页面重定向到商品列表 // 404页面重定向到商品列表
{ {
path: '*', path: '*',

View File

@@ -295,14 +295,22 @@ export default {
padding: 6px 10px; padding: 6px 10px;
color: #2c3e50; color: #2c3e50;
cursor: pointer; cursor: pointer;
transition: all 0.3s ease;
} }
/* 激活状态 - 紫色渐变 */
.role-button.active { .role-button.active {
background: #42b983; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-color: #42b983; border-color: #667eea;
color: #fff; color: #fff;
box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);
}
.role-button:hover {
background: #f5f7ff;
color: #667eea;
border-color: #667eea;
} }
.role-button:focus { .role-button:focus {
outline: 2px solid #42b98333; outline: 2px solid rgba(102, 126, 234, 0.2);
outline-offset: 2px; outline-offset: 2px;
} }
/* 用户信息卡片:置于导航最前,展示邮箱与首字母头像 */ /* 用户信息卡片:置于导航最前,展示邮箱与首字母头像 */
@@ -318,17 +326,19 @@ export default {
border-radius: 8px; border-radius: 8px;
margin-bottom: 4px; margin-bottom: 4px;
} }
/* 头像也改成紫色渐变 */
.avatar { .avatar {
width: 36px; width: 36px;
height: 36px; height: 36px;
border-radius: 50%; border-radius: 50%;
background: linear-gradient(135deg, #42b983, #67c23a); background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #fff; color: #fff;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
font-weight: 700; font-weight: 700;
font-size: 14px; font-size: 14px;
box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);
} }
.user-email { .user-email {
font-size: 14px; font-size: 14px;
@@ -342,14 +352,18 @@ export default {
color: #2c3e50; color: #2c3e50;
text-decoration: none; text-decoration: none;
border-radius: 6px; border-radius: 6px;
transition: background 0.2s; transition: all 0.3s ease;
} }
/* 悬停效果 - 淡紫色 */
.side-link:hover { .side-link:hover {
background: #f6f8fa; background: #f5f7ff;
color: #667eea;
} }
/* 激活状态 - 紫色渐变 */
.side-link.active { .side-link.active {
background: #42b983; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: #fff; color: #fff;
box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);
} }
.content { .content {

View File

@@ -1160,7 +1160,7 @@ export default {
<style scoped> <style scoped>
/* 钱包容器 */ /* 钱包容器 */
.wallet-container { .wallet-container {
max-width: 800px; width: 70vw;
margin: 0 auto; margin: 0 auto;
padding: 20px; padding: 20px;
} }

View File

@@ -0,0 +1,596 @@
<template>
<div class="auth-container">
<!-- 背景装饰 -->
<div class="auth-background">
<div class="bg-circle circle-1"></div>
<div class="bg-circle circle-2"></div>
<div class="bg-circle circle-3"></div>
</div>
<!-- 登录表单卡片 -->
<div class="auth-card">
<!-- 关闭按钮 - 点击返回商城 -->
<div class="close-btn" @click="goToShop" title="返回商城">
<i class="el-icon-close"></i>
</div>
<!-- Logo和标题 -->
<div class="auth-header">
<h1 class="auth-title">欢迎登录</h1>
<p class="auth-subtitle">Power Leasing - 算力租赁平台</p>
</div>
<!-- 登录表单 -->
<el-form
ref="loginForm"
:model="loginForm"
:rules="loginRules"
class="auth-form"
@submit.native.prevent="handleLogin"
>
<!-- 邮箱输入框 -->
<el-form-item prop="email">
<el-input
v-model="loginForm.email"
placeholder="请输入邮箱"
prefix-icon="el-icon-message"
size="large"
clearable
@keyup.enter.native="handleLogin"
>
</el-input>
</el-form-item>
<!-- 密码输入框 -->
<el-form-item prop="password">
<el-input
v-model="loginForm.password"
type="password"
placeholder="请输入密码"
prefix-icon="el-icon-lock"
size="large"
show-password
clearable
@keyup.enter.native="handleLogin"
>
</el-input>
</el-form-item>
<!-- 验证码输入框 -->
<el-form-item prop="code">
<div class="code-input-wrapper">
<el-input
v-model="loginForm.code"
placeholder="请输入邮箱验证码"
prefix-icon="el-icon-key"
size="large"
maxlength="6"
clearable
@keyup.enter.native="handleLogin"
>
</el-input>
<el-button
type="primary"
size="large"
class="send-code-btn"
:disabled="countdown > 0"
:loading="sendingCode"
@click="handleSendCode"
>
{{ countdown > 0 ? `${countdown}秒后重试` : '发送验证码' }}
</el-button>
</div>
</el-form-item>
<!-- 记住密码和忘记密码 -->
<div class="auth-options">
<el-checkbox v-model="rememberMe">记住密码</el-checkbox>
<router-link to="/reset-password" class="link-text">
忘记密码
</router-link>
</div>
<!-- 登录按钮 -->
<el-form-item>
<el-button
type="primary"
size="large"
class="auth-submit-btn"
:loading="loading"
@click="handleLogin"
>
{{ loading ? '登录中...' : '登录' }}
</el-button>
</el-form-item>
<!-- 注册链接 -->
<div class="auth-footer">
<span class="footer-text">还没有账号</span>
<router-link to="/register" class="link-text link-primary">
立即注册
</router-link>
</div>
</el-form>
</div>
</div>
</template>
<script>
import { getLogin, sendLoginCode } from '@/api/user'
export default {
name: 'LoginPage',
data() {
/**
* 邮箱格式验证
*/
const validateEmail = (rule, value, callback) => {
if (!value) {
callback(new Error('请输入邮箱'))
return
}
const emailReg = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
if (!emailReg.test(value)) {
callback(new Error('请输入有效的邮箱地址'))
return
}
callback()
}
/**
* 密码格式验证
*/
const validatePassword = (rule, value, callback) => {
if (!value) {
callback(new Error('请输入密码'))
return
}
if (value.length < 6) {
callback(new Error('密码长度不能少于6位'))
return
}
callback()
}
return {
// 登录表单数据
loginForm: {
email: '',
password: '',
code: ''
},
// 表单验证规则
loginRules: {
email: [
{ required: true, validator: validateEmail, trigger: 'blur' }
],
password: [
{ required: true, validator: validatePassword, trigger: 'blur' }
],
code: [
{ required: true, message: '请输入验证码', trigger: 'blur' },
{ min: 6, max: 6, message: '验证码为6位数字', trigger: 'blur' }
]
},
// 记住密码
rememberMe: false,
// 加载状态
loading: false,
sendingCode: false,
// 验证码倒计时
countdown: 0,
timer: null
}
},
mounted() {
// 从localStorage读取记住的密码
const savedEmail = localStorage.getItem('rememberedEmail')
const savedPassword = localStorage.getItem('rememberedPassword')
if (savedEmail && savedPassword) {
this.loginForm.email = savedEmail
this.loginForm.password = savedPassword
this.rememberMe = true
}
},
beforeDestroy() {
// 清理定时器
if (this.timer) {
clearInterval(this.timer)
this.timer = null
}
},
methods: {
/**
* 返回商城页面
* 先检查是否已经在商城页面,避免重复跳转警告
*/
goToShop() {
// 如果当前不在商城页面,才跳转
if (this.$route.path !== '/productList') {
this.$router.push('/productList')
} else {
// 已经在商城页面,直接返回上一页
this.$router.go(-1)
}
},
/**
* 发送邮箱验证码
*/
async handleSendCode() {
// 先验证邮箱
try {
await this.$refs.loginForm.validateField('email')
} catch (error) {
return
}
this.sendingCode = true
try {
// 发送登录验证码接口参数email邮箱
const res = await sendLoginCode({
email: this.loginForm.email
})
if (res && res.code === 200) {
this.$message.success(res.msg || '验证码已发送,请查收邮箱')
// 开发环境显示验证码(方便测试)
if (process.env.NODE_ENV === 'development' && res.data && res.data.code) {
this.$message.info(`测试验证码:${res.data.code}`)
}
// 开始倒计时
this.startCountdown()
} else {
this.$message.error(res.msg || '发送验证码失败')
}
} catch (error) {
console.error('发送验证码失败:', error)
this.$message.error('发送验证码失败,请重试')
} finally {
this.sendingCode = false
}
},
/**
* 开始倒计时
*/
startCountdown() {
this.countdown = 60
this.timer = setInterval(() => {
this.countdown--
if (this.countdown <= 0) {
clearInterval(this.timer)
this.timer = null
}
}, 1000)
},
/**
* 处理登录
*/
async handleLogin() {
// 表单验证
try {
await this.$refs.loginForm.validate()
} catch (error) {
return
}
this.loading = true
try {
// 登录接口参数email邮箱、password密码、code验证码
const res = await getLogin({
email: this.loginForm.email,
password: this.loginForm.password,
code: this.loginForm.code
})
if (res && res.code === 200) {
// 保存token到localStorage
const token = res.data.token
localStorage.setItem('token', JSON.stringify(token))
// 保存用户信息
if (res.data.userInfo) {
localStorage.setItem('userInfo', JSON.stringify(res.data.userInfo))
}
// 记住密码
if (this.rememberMe) {
localStorage.setItem('rememberedEmail', this.loginForm.email)
localStorage.setItem('rememberedPassword', this.loginForm.password)
} else {
localStorage.removeItem('rememberedEmail')
localStorage.removeItem('rememberedPassword')
}
this.$message.success('登录成功')
// 跳转到首页或者来源页面
const redirect = this.$route.query.redirect || '/productList'
this.$router.push(redirect)
} else {
this.$message.error(res.msg || '登录失败')
}
} catch (error) {
console.error('登录失败:', error)
this.$message.error('登录失败,请重试')
} finally {
this.loading = false
}
}
}
}
</script>
<style scoped>
/* 容器样式 - 全屏背景 */
.auth-container {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 20px;
position: relative;
overflow: hidden;
}
/* 背景装饰圆圈 */
.auth-background {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
z-index: 0;
}
.bg-circle {
position: absolute;
border-radius: 50%;
background: rgba(255, 255, 255, 0.1);
animation: float 20s infinite ease-in-out;
}
.circle-1 {
width: 300px;
height: 300px;
top: -100px;
left: -100px;
animation-delay: 0s;
}
.circle-2 {
width: 200px;
height: 200px;
bottom: -50px;
right: -50px;
animation-delay: 5s;
}
.circle-3 {
width: 150px;
height: 150px;
top: 50%;
left: 50%;
animation-delay: 10s;
}
@keyframes float {
0%, 100% {
transform: translate(0, 0) scale(1);
}
50% {
transform: translate(30px, -30px) scale(1.1);
}
}
/* 登录卡片样式 */
.auth-card {
background: white;
border-radius: 16px;
padding: 48px;
width: 100%;
max-width: 450px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
position: relative;
z-index: 1;
animation: slideUp 0.6s ease-out;
}
/* 关闭按钮样式 */
.close-btn {
position: absolute;
top: 20px;
right: 20px;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
background: #f5f5f5;
color: #999;
cursor: pointer;
transition: all 0.3s ease;
z-index: 10;
}
.close-btn:hover {
background: #667eea;
color: white;
transform: rotate(90deg);
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* 头部样式 */
.auth-header {
text-align: center;
margin-bottom: 32px;
}
.auth-title {
font-size: 28px;
font-weight: 700;
color: #333;
margin: 0 0 8px 0;
}
.auth-subtitle {
font-size: 14px;
color: #999;
margin: 0;
}
/* 表单样式 */
.auth-form {
margin-top: 24px;
}
.auth-form .el-form-item {
margin-bottom: 24px;
}
/* 验证码输入框组合样式 */
.code-input-wrapper {
display: flex;
gap: 12px;
align-items: center;
}
.code-input-wrapper .el-input {
flex: 1;
}
.send-code-btn {
flex-shrink: 0;
min-width: 120px;
height: 40px;
}
/* 选项行样式 */
.auth-options {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
}
/* 提交按钮样式 */
.auth-submit-btn {
width: 100%;
height: 44px;
font-size: 16px;
font-weight: 600;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border: none;
border-radius: 8px;
transition: all 0.3s ease;
}
.auth-submit-btn:hover {
transform: translateY(-2px);
box-shadow: 0 8px 16px rgba(102, 126, 234, 0.4);
}
/* 页脚样式 */
.auth-footer {
text-align: center;
margin-top: 24px;
}
.footer-text {
color: #666;
font-size: 14px;
margin-right: 8px;
}
.link-text {
color: #667eea;
font-size: 14px;
text-decoration: none;
transition: color 0.3s ease;
}
.link-text:hover {
color: #764ba2;
text-decoration: underline;
}
.link-primary {
font-weight: 600;
}
/* 响应式设计 */
@media (max-width: 768px) {
.auth-card {
padding: 32px 24px;
max-width: 100%;
}
.auth-title {
font-size: 24px;
}
.code-input-wrapper {
flex-direction: column;
}
.send-code-btn {
width: 100%;
min-width: auto;
}
}
/* Element UI组件样式覆盖 */
.auth-form >>> .el-input__inner {
height: 44px;
line-height: 44px;
border-radius: 8px;
border: 1px solid #e0e0e0;
transition: all 0.3s ease;
}
.auth-form >>> .el-input__inner:focus {
border-color: #667eea;
box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.1);
}
.auth-form >>> .el-input__prefix {
display: flex;
align-items: center;
color: #999;
}
.auth-form >>> .el-checkbox__label {
color: #666;
font-size: 14px;
}
</style>

View File

@@ -0,0 +1,734 @@
<template>
<div class="auth-container">
<!-- 背景装饰 -->
<div class="auth-background">
<div class="bg-circle circle-1"></div>
<div class="bg-circle circle-2"></div>
<div class="bg-circle circle-3"></div>
</div>
<!-- 注册表单卡片 -->
<div class="auth-card">
<!-- 关闭按钮 - 点击返回商城 -->
<div class="close-btn" @click="goToShop" title="返回商城">
<i class="el-icon-close"></i>
</div>
<!-- Logo和标题 -->
<div class="auth-header">
<h1 class="auth-title">欢迎注册</h1>
<p class="auth-subtitle">创建您的 Power Leasing 账号</p>
</div>
<!-- 注册表单 -->
<el-form
ref="registerForm"
:model="registerForm"
:rules="registerRules"
class="auth-form"
@submit.native.prevent="handleRegister"
>
<!-- 邮箱输入框 -->
<el-form-item prop="email">
<el-input
v-model="registerForm.email"
placeholder="请输入邮箱"
prefix-icon="el-icon-message"
size="large"
clearable
>
</el-input>
</el-form-item>
<!-- 验证码输入框 -->
<el-form-item prop="code">
<div class="code-input-wrapper">
<el-input
v-model="registerForm.code"
placeholder="请输入邮箱验证码"
prefix-icon="el-icon-key"
size="large"
maxlength="6"
clearable
>
</el-input>
<el-button
type="primary"
size="large"
class="send-code-btn"
:disabled="countdown > 0"
:loading="sendingCode"
@click="handleSendCode"
>
{{ countdown > 0 ? `${countdown}秒后重试` : '发送验证码' }}
</el-button>
</div>
</el-form-item>
<!-- 密码输入框 -->
<el-form-item prop="password">
<el-input
v-model="registerForm.password"
type="password"
placeholder="请输入密码至少6位"
prefix-icon="el-icon-lock"
size="large"
show-password
clearable
>
</el-input>
<!-- 密码强度提示 -->
<div v-if="registerForm.password" class="password-strength">
<span class="strength-label">密码强度</span>
<span :class="['strength-bar', passwordStrengthClass]">
{{ passwordStrengthText }}
</span>
</div>
</el-form-item>
<!-- 确认密码输入框 -->
<el-form-item prop="confirmPassword">
<el-input
v-model="registerForm.confirmPassword"
type="password"
placeholder="请再次输入密码"
prefix-icon="el-icon-lock"
size="large"
show-password
clearable
@keyup.enter.native="handleRegister"
>
</el-input>
</el-form-item>
<!-- 用户协议 -->
<el-form-item prop="agree">
<el-checkbox v-model="registerForm.agree">
我已阅读并同意
<a href="#" class="link-text" @click.prevent>用户协议</a>
<a href="#" class="link-text" @click.prevent>隐私政策</a>
</el-checkbox>
</el-form-item>
<!-- 注册按钮 -->
<el-form-item>
<el-button
type="primary"
size="large"
class="auth-submit-btn"
:loading="loading"
@click="handleRegister"
>
{{ loading ? '注册中...' : '立即注册' }}
</el-button>
</el-form-item>
<!-- 登录链接 -->
<div class="auth-footer">
<span class="footer-text">已有账号</span>
<router-link to="/login" class="link-text link-primary">
立即登录
</router-link>
</div>
</el-form>
</div>
</div>
</template>
<script>
import { register, sendEmailCode } from '../../api/user'
import { rsaEncrypt, rsaEncryptSync } from '@/utils/rsaEncrypt'
export default {
name: 'RegisterPage',
data() {
/**
* 邮箱格式验证
*/
const validateEmail = (rule, value, callback) => {
if (!value) {
callback(new Error('请输入邮箱'))
return
}
const emailReg = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$/;
if (!emailReg.test(value)) {
callback(new Error('请输入有效的邮箱地址'))
return
}
callback()
}
/**
* 密码格式验证
*/
const validatePassword = (rule, value, callback) => {
if (!value) {
callback(new Error('请输入密码'))
return
}
if (value.length < 6) {
callback(new Error('密码长度不能少于6位'))
return
}
if (value.length > 20) {
callback(new Error('密码长度不能超过20位'))
return
}
callback()
}
/**
* 确认密码验证
*/
const validateConfirmPassword = (rule, value, callback) => {
if (!value) {
callback(new Error('请再次输入密码'))
return
}
if (value !== this.registerForm.password) {
callback(new Error('两次输入的密码不一致'))
return
}
callback()
}
/**
* 用户协议验证
*/
const validateAgree = (rule, value, callback) => {
if (!value) {
callback(new Error('请阅读并同意用户协议'))
return
}
callback()
}
return {
// 注册表单数据
registerForm: {
email: '',
code: '',
password: '',
confirmPassword: '',
agree: false
},
// 表单验证规则
registerRules: {
email: [
{ required: true, validator: validateEmail, trigger: 'blur' }
],
code: [
{ required: true, message: '请输入验证码', trigger: 'blur' },
{ min: 6, max: 6, message: '验证码为6位数字', trigger: 'blur' }
],
password: [
{ required: true, validator: validatePassword, trigger: 'blur' }
],
confirmPassword: [
{ required: true, validator: validateConfirmPassword, trigger: 'blur' }
],
agree: [
{ required: true, validator: validateAgree, trigger: 'change' }
]
},
// 加载状态
loading: false,
sendingCode: false,
// 验证码倒计时
countdown: 0,
timer: null
}
},
computed: {
/**
* 计算密码强度
*/
passwordStrength() {
const pwd = this.registerForm.password
if (!pwd) return 0
let strength = 0
// 长度加分
if (pwd.length >= 6) strength += 1
if (pwd.length >= 10) strength += 1
// 包含数字
if (/\d/.test(pwd)) strength += 1
// 包含小写字母
if (/[a-z]/.test(pwd)) strength += 1
// 包含大写字母
if (/[A-Z]/.test(pwd)) strength += 1
// 包含特殊字符
if (/[^a-zA-Z0-9]/.test(pwd)) strength += 1
return strength
},
/**
* 密码强度文本
*/
passwordStrengthText() {
const strength = this.passwordStrength
if (strength <= 2) return '弱'
if (strength <= 4) return '中'
return '强'
},
/**
* 密码强度样式类名
*/
passwordStrengthClass() {
const strength = this.passwordStrength
if (strength <= 2) return 'weak'
if (strength <= 4) return 'medium'
return 'strong'
}
},
beforeDestroy() {
// 清理定时器
if (this.timer) {
clearInterval(this.timer)
this.timer = null
}
},
methods: {
/**
* 返回商城页面
* 先检查是否已经在商城页面,避免重复跳转警告
*/
goToShop() {
// 如果当前不在商城页面,才跳转
if (this.$route.path !== '/productList') {
this.$router.push('/productList')
} else {
// 已经在商城页面,直接返回上一页
this.$router.go(-1)
}
},
/**
* 发送邮箱验证码
*/
async handleSendCode() {
// 先验证邮箱
try {
await this.$refs.registerForm.validateField('email')
} catch (error) {
return
}
this.sendingCode = true
try {
// 发送验证码接口参数email邮箱
const res = await sendEmailCode({
email: this.registerForm.email
})
if (res && res.code === 200) {
this.$message.success(res.msg || '验证码已发送,请查收邮箱')
// 开发环境显示验证码(方便测试)
if (process.env.NODE_ENV === 'development' && res.data && res.data.code) {
this.$message.info(`测试验证码:${res.data.code}`)
}
// 开始倒计时
this.startCountdown()
} else {
this.$message.error(res.msg || '发送验证码失败')
}
} catch (error) {
console.error('发送验证码失败:', error)
this.$message.error('发送验证码失败,请重试')
} finally {
this.sendingCode = false
}
},
/**
* 开始倒计时
*/
startCountdown() {
this.countdown = 60
this.timer = setInterval(() => {
this.countdown--
if (this.countdown <= 0) {
clearInterval(this.timer)
this.timer = null
}
}, 1000)
},
/**
* 处理注册
*/
async handleRegister() {
// 表单验证
try {
await this.$refs.registerForm.validate()
} catch (error) {
return
}
this.loading = true
try {
// 密码RSA加密优先使用同步方法失败则使用异步方法
const passwordPlain = this.registerForm.password
let encryptedPassword = passwordPlain
// 尝试同步加密
const syncEncrypted = rsaEncryptSync(passwordPlain)
if (syncEncrypted) {
encryptedPassword = syncEncrypted
} else {
// 同步加密失败,尝试异步加密
const asyncEncrypted = await rsaEncrypt(passwordPlain)
if (asyncEncrypted) {
encryptedPassword = asyncEncrypted
} else {
this.$message.error('密码加密失败,请重试')
this.loading = false
return
}
}
// 注册接口参数:
// - code: 邮箱验证码
// - password: RSA加密后的密码
// - userEmail: 邮箱
const res = await register({
code: this.registerForm.code,
password: encryptedPassword,
userEmail: this.registerForm.email
})
if (res && res.code === 200) {
this.$message.success('注册成功,请登录')
// 跳转到登录页,并带上邮箱
this.$router.push({
path: '/login',
query: {
email: this.registerForm.email
}
})
} else {
this.$message.error(res.msg || '注册失败')
}
} catch (error) {
console.error('注册失败:', error)
this.$message.error('注册失败,请重试')
} finally {
this.loading = false
}
}
}
}
</script>
<style scoped>
/* 容器样式 - 全屏背景 */
.auth-container {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 20px;
position: relative;
overflow: hidden;
}
/* 背景装饰圆圈 */
.auth-background {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
z-index: 0;
}
.bg-circle {
position: absolute;
border-radius: 50%;
background: rgba(255, 255, 255, 0.1);
animation: float 20s infinite ease-in-out;
}
.circle-1 {
width: 300px;
height: 300px;
top: -100px;
left: -100px;
animation-delay: 0s;
}
.circle-2 {
width: 200px;
height: 200px;
bottom: -50px;
right: -50px;
animation-delay: 5s;
}
.circle-3 {
width: 150px;
height: 150px;
top: 50%;
left: 50%;
animation-delay: 10s;
}
@keyframes float {
0%, 100% {
transform: translate(0, 0) scale(1);
}
50% {
transform: translate(30px, -30px) scale(1.1);
}
}
/* 注册卡片样式 */
.auth-card {
background: white;
border-radius: 16px;
padding: 48px;
width: 100%;
max-width: 450px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
position: relative;
z-index: 1;
animation: slideUp 0.6s ease-out;
}
/* 关闭按钮样式 */
.close-btn {
position: absolute;
top: 20px;
right: 20px;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
background: #f5f5f5;
color: #999;
cursor: pointer;
transition: all 0.3s ease;
z-index: 10;
}
.close-btn:hover {
background: #667eea;
color: white;
transform: rotate(90deg);
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* 头部样式 */
.auth-header {
text-align: center;
margin-bottom: 32px;
}
.auth-title {
font-size: 28px;
font-weight: 700;
color: #333;
margin: 0 0 8px 0;
}
.auth-subtitle {
font-size: 14px;
color: #999;
margin: 0;
}
/* 表单样式 */
.auth-form {
margin-top: 24px;
}
.auth-form .el-form-item {
margin-bottom: 20px;
}
/* 验证码输入框组合样式 */
.code-input-wrapper {
display: flex;
gap: 12px;
align-items: center;
}
.code-input-wrapper .el-input {
flex: 1;
}
.send-code-btn {
flex-shrink: 0;
min-width: 120px;
height: 40px;
}
/* 密码强度提示 */
.password-strength {
display: flex;
align-items: center;
margin-top: 8px;
font-size: 12px;
}
.strength-label {
color: #666;
margin-right: 8px;
}
.strength-bar {
padding: 2px 12px;
border-radius: 4px;
font-weight: 600;
}
.strength-bar.weak {
background: #fff1f0;
color: #ff4d4f;
}
.strength-bar.medium {
background: #fff7e6;
color: #fa8c16;
}
.strength-bar.strong {
background: #f6ffed;
color: #52c41a;
}
/* 提交按钮样式 */
.auth-submit-btn {
width: 100%;
height: 44px;
font-size: 16px;
font-weight: 600;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border: none;
border-radius: 8px;
transition: all 0.3s ease;
}
.auth-submit-btn:hover {
transform: translateY(-2px);
box-shadow: 0 8px 16px rgba(102, 126, 234, 0.4);
}
/* 页脚样式 */
.auth-footer {
text-align: center;
margin-top: 24px;
}
.footer-text {
color: #666;
font-size: 14px;
margin-right: 8px;
}
.link-text {
color: #667eea;
font-size: 14px;
text-decoration: none;
transition: color 0.3s ease;
}
.link-text:hover {
color: #764ba2;
text-decoration: underline;
}
.link-primary {
font-weight: 600;
}
/* 响应式设计 */
@media (max-width: 768px) {
.auth-card {
padding: 32px 24px;
max-width: 100%;
}
.auth-title {
font-size: 24px;
}
.code-input-wrapper {
flex-direction: column;
}
.send-code-btn {
width: 100%;
min-width: auto;
}
}
/* Element UI组件样式覆盖 */
.auth-form >>> .el-input__inner {
height: 44px;
line-height: 44px;
border-radius: 8px;
border: 1px solid #e0e0e0;
transition: all 0.3s ease;
}
.auth-form >>> .el-input__inner:focus {
border-color: #667eea;
box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.1);
}
.auth-form >>> .el-input__prefix {
display: flex;
align-items: center;
color: #999;
}
.auth-form >>> .el-checkbox__label {
color: #666;
font-size: 14px;
}
.auth-form >>> .el-checkbox__input.is-checked .el-checkbox__inner {
background-color: #667eea;
border-color: #667eea;
}
.auth-form >>> .el-checkbox__input.is-checked + .el-checkbox__label {
color: #667eea;
}
</style>

View File

@@ -0,0 +1,669 @@
<template>
<div class="auth-container">
<!-- 背景装饰 -->
<div class="auth-background">
<div class="bg-circle circle-1"></div>
<div class="bg-circle circle-2"></div>
<div class="bg-circle circle-3"></div>
</div>
<!-- 重置密码表单卡片 -->
<div class="auth-card">
<!-- 关闭按钮 - 点击返回商城 -->
<div class="close-btn" @click="goToShop" title="返回商城">
<i class="el-icon-close"></i>
</div>
<!-- Logo和标题 -->
<div class="auth-header">
<h1 class="auth-title">重置密码</h1>
<p class="auth-subtitle">通过邮箱验证码重置您的密码</p>
</div>
<!-- 重置密码表单 -->
<el-form
ref="resetForm"
:model="resetForm"
:rules="resetRules"
class="auth-form"
@submit.native.prevent="handleReset"
>
<!-- 邮箱输入框 -->
<el-form-item prop="email">
<el-input
v-model="resetForm.email"
placeholder="请输入注册时的邮箱"
prefix-icon="el-icon-message"
size="large"
clearable
>
</el-input>
</el-form-item>
<!-- 验证码输入框 -->
<el-form-item prop="code">
<div class="code-input-wrapper">
<el-input
v-model="resetForm.code"
placeholder="请输入邮箱验证码"
prefix-icon="el-icon-key"
size="large"
maxlength="6"
clearable
>
</el-input>
<el-button
type="primary"
size="large"
class="send-code-btn"
:disabled="countdown > 0"
:loading="sendingCode"
@click="handleSendCode"
>
{{ countdown > 0 ? `${countdown}秒后重试` : '发送验证码' }}
</el-button>
</div>
</el-form-item>
<!-- 新密码输入框 -->
<el-form-item prop="password">
<el-input
v-model="resetForm.password"
type="password"
placeholder="请输入新密码至少6位"
prefix-icon="el-icon-lock"
size="large"
show-password
clearable
>
</el-input>
<!-- 密码强度提示 -->
<div v-if="resetForm.password" class="password-strength">
<span class="strength-label">密码强度</span>
<span :class="['strength-bar', passwordStrengthClass]">
{{ passwordStrengthText }}
</span>
</div>
</el-form-item>
<!-- 确认新密码输入框 -->
<el-form-item prop="confirmPassword">
<el-input
v-model="resetForm.confirmPassword"
type="password"
placeholder="请再次输入新密码"
prefix-icon="el-icon-lock"
size="large"
show-password
clearable
@keyup.enter.native="handleReset"
>
</el-input>
</el-form-item>
<!-- 重置按钮 -->
<el-form-item>
<el-button
type="primary"
size="large"
class="auth-submit-btn"
:loading="loading"
@click="handleReset"
>
{{ loading ? '重置中...' : '确认重置' }}
</el-button>
</el-form-item>
<!-- 返回登录链接 -->
<div class="auth-footer">
<router-link to="/login" class="link-text link-primary">
<i class="el-icon-back"></i> 返回登录
</router-link>
</div>
</el-form>
</div>
</div>
</template>
<script>
import { updatePassword, sendUpdatePwdCode } from '@/api/user'
export default {
name: 'ResetPasswordPage',
data() {
/**
* 邮箱格式验证
*/
const validateEmail = (rule, value, callback) => {
if (!value) {
callback(new Error('请输入邮箱'))
return
}
const emailReg = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
if (!emailReg.test(value)) {
callback(new Error('请输入有效的邮箱地址'))
return
}
callback()
}
/**
* 密码格式验证
*/
const validatePassword = (rule, value, callback) => {
if (!value) {
callback(new Error('请输入新密码'))
return
}
if (value.length < 6) {
callback(new Error('密码长度不能少于6位'))
return
}
if (value.length > 20) {
callback(new Error('密码长度不能超过20位'))
return
}
callback()
}
/**
* 确认密码验证
*/
const validateConfirmPassword = (rule, value, callback) => {
if (!value) {
callback(new Error('请再次输入新密码'))
return
}
if (value !== this.resetForm.password) {
callback(new Error('两次输入的密码不一致'))
return
}
callback()
}
return {
// 重置密码表单数据
resetForm: {
email: '',
code: '',
password: '',
confirmPassword: ''
},
// 表单验证规则
resetRules: {
email: [
{ required: true, validator: validateEmail, trigger: 'blur' }
],
code: [
{ required: true, message: '请输入验证码', trigger: 'blur' },
{ min: 6, max: 6, message: '验证码为6位数字', trigger: 'blur' }
],
password: [
{ required: true, validator: validatePassword, trigger: 'blur' }
],
confirmPassword: [
{ required: true, validator: validateConfirmPassword, trigger: 'blur' }
]
},
// 加载状态
loading: false,
sendingCode: false,
// 验证码倒计时
countdown: 0,
timer: null
}
},
computed: {
/**
* 计算密码强度
*/
passwordStrength() {
const pwd = this.resetForm.password
if (!pwd) return 0
let strength = 0
// 长度加分
if (pwd.length >= 6) strength += 1
if (pwd.length >= 10) strength += 1
// 包含数字
if (/\d/.test(pwd)) strength += 1
// 包含小写字母
if (/[a-z]/.test(pwd)) strength += 1
// 包含大写字母
if (/[A-Z]/.test(pwd)) strength += 1
// 包含特殊字符
if (/[^a-zA-Z0-9]/.test(pwd)) strength += 1
return strength
},
/**
* 密码强度文本
*/
passwordStrengthText() {
const strength = this.passwordStrength
if (strength <= 2) return '弱'
if (strength <= 4) return '中'
return '强'
},
/**
* 密码强度样式类名
*/
passwordStrengthClass() {
const strength = this.passwordStrength
if (strength <= 2) return 'weak'
if (strength <= 4) return 'medium'
return 'strong'
}
},
beforeDestroy() {
// 清理定时器
if (this.timer) {
clearInterval(this.timer)
this.timer = null
}
},
methods: {
/**
* 返回商城页面
* 先检查是否已经在商城页面,避免重复跳转警告
*/
goToShop() {
// 如果当前不在商城页面,才跳转
if (this.$route.path !== '/productList') {
this.$router.push('/productList')
} else {
// 已经在商城页面,直接返回上一页
this.$router.go(-1)
}
},
/**
* 发送邮箱验证码
*/
async handleSendCode() {
// 先验证邮箱
try {
await this.$refs.resetForm.validateField('email')
} catch (error) {
return
}
this.sendingCode = true
try {
// 发送重置密码验证码接口参数email邮箱
const res = await sendUpdatePwdCode({
email: this.resetForm.email
})
if (res && res.code === 200) {
this.$message.success(res.msg || '验证码已发送,请查收邮箱')
// 开发环境显示验证码(方便测试)
if (process.env.NODE_ENV === 'development' && res.data && res.data.code) {
this.$message.info(`测试验证码:${res.data.code}`)
}
// 开始倒计时
this.startCountdown()
} else {
this.$message.error(res.msg || '发送验证码失败')
}
} catch (error) {
console.error('发送验证码失败:', error)
this.$message.error('发送验证码失败,请重试')
} finally {
this.sendingCode = false
}
},
/**
* 开始倒计时
*/
startCountdown() {
this.countdown = 60
this.timer = setInterval(() => {
this.countdown--
if (this.countdown <= 0) {
clearInterval(this.timer)
this.timer = null
}
}, 1000)
},
/**
* 处理重置密码
*/
async handleReset() {
// 表单验证
try {
await this.$refs.resetForm.validate()
} catch (error) {
return
}
this.loading = true
try {
// 修改密码接口参数email邮箱、code验证码、password新密码
const res = await updatePassword({
email: this.resetForm.email,
code: this.resetForm.code,
password: this.resetForm.password
})
if (res && res.code === 200) {
this.$message.success(res.msg || '密码重置成功,请使用新密码登录')
// 延迟跳转到登录页,并带上邮箱
setTimeout(() => {
this.$router.push({
path: '/login',
query: {
email: this.resetForm.email
}
})
}, 1500)
} else {
this.$message.error(res.msg || '密码重置失败')
}
} catch (error) {
console.error('密码重置失败:', error)
this.$message.error('密码重置失败,请重试')
} finally {
this.loading = false
}
}
}
}
</script>
<style scoped>
/* 容器样式 - 全屏背景 */
.auth-container {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 20px;
position: relative;
overflow: hidden;
}
/* 背景装饰圆圈 */
.auth-background {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
z-index: 0;
}
.bg-circle {
position: absolute;
border-radius: 50%;
background: rgba(255, 255, 255, 0.1);
animation: float 20s infinite ease-in-out;
}
.circle-1 {
width: 300px;
height: 300px;
top: -100px;
left: -100px;
animation-delay: 0s;
}
.circle-2 {
width: 200px;
height: 200px;
bottom: -50px;
right: -50px;
animation-delay: 5s;
}
.circle-3 {
width: 150px;
height: 150px;
top: 50%;
left: 50%;
animation-delay: 10s;
}
@keyframes float {
0%, 100% {
transform: translate(0, 0) scale(1);
}
50% {
transform: translate(30px, -30px) scale(1.1);
}
}
/* 重置密码卡片样式 */
.auth-card {
background: white;
border-radius: 16px;
padding: 48px;
width: 100%;
max-width: 450px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
position: relative;
z-index: 1;
animation: slideUp 0.6s ease-out;
}
/* 关闭按钮样式 */
.close-btn {
position: absolute;
top: 20px;
right: 20px;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
background: #f5f5f5;
color: #999;
cursor: pointer;
transition: all 0.3s ease;
z-index: 10;
}
.close-btn:hover {
background: #667eea;
color: white;
transform: rotate(90deg);
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* 头部样式 */
.auth-header {
text-align: center;
margin-bottom: 32px;
}
.auth-title {
font-size: 28px;
font-weight: 700;
color: #333;
margin: 0 0 8px 0;
}
.auth-subtitle {
font-size: 14px;
color: #999;
margin: 0;
}
/* 表单样式 */
.auth-form {
margin-top: 24px;
}
.auth-form .el-form-item {
margin-bottom: 20px;
}
/* 验证码输入框组合样式 */
.code-input-wrapper {
display: flex;
gap: 12px;
align-items: center;
}
.code-input-wrapper .el-input {
flex: 1;
}
.send-code-btn {
flex-shrink: 0;
min-width: 120px;
height: 40px;
}
/* 密码强度提示 */
.password-strength {
display: flex;
align-items: center;
margin-top: 8px;
font-size: 12px;
}
.strength-label {
color: #666;
margin-right: 8px;
}
.strength-bar {
padding: 2px 12px;
border-radius: 4px;
font-weight: 600;
}
.strength-bar.weak {
background: #fff1f0;
color: #ff4d4f;
}
.strength-bar.medium {
background: #fff7e6;
color: #fa8c16;
}
.strength-bar.strong {
background: #f6ffed;
color: #52c41a;
}
/* 提交按钮样式 */
.auth-submit-btn {
width: 100%;
height: 44px;
font-size: 16px;
font-weight: 600;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border: none;
border-radius: 8px;
transition: all 0.3s ease;
}
.auth-submit-btn:hover {
transform: translateY(-2px);
box-shadow: 0 8px 16px rgba(102, 126, 234, 0.4);
}
/* 页脚样式 */
.auth-footer {
text-align: center;
margin-top: 24px;
}
.link-text {
color: #667eea;
font-size: 14px;
text-decoration: none;
transition: color 0.3s ease;
display: inline-flex;
align-items: center;
gap: 4px;
}
.link-text:hover {
color: #764ba2;
text-decoration: underline;
}
.link-primary {
font-weight: 600;
}
/* 响应式设计 */
@media (max-width: 768px) {
.auth-card {
padding: 32px 24px;
max-width: 100%;
}
.auth-title {
font-size: 24px;
}
.code-input-wrapper {
flex-direction: column;
}
.send-code-btn {
width: 100%;
min-width: auto;
}
}
/* Element UI组件样式覆盖 */
.auth-form >>> .el-input__inner {
height: 44px;
line-height: 44px;
border-radius: 8px;
border: 1px solid #e0e0e0;
transition: all 0.3s ease;
}
.auth-form >>> .el-input__inner:focus {
border-color: #667eea;
box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.1);
}
.auth-form >>> .el-input__prefix {
display: flex;
align-items: center;
color: #999;
}
</style>