更新保存代码
This commit is contained in:
76
power_leasing/src/api/user.js
Normal file
76
power_leasing/src/api/user.js
Normal 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
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
<div class="header-container">
|
||||
<!-- 顶部导航栏 -->
|
||||
<nav class="navbar">
|
||||
<!-- 左侧:导航按钮 -->
|
||||
<div class="nav-left">
|
||||
<router-link
|
||||
v-for="nav in navigation"
|
||||
:key="nav.path"
|
||||
@@ -14,6 +16,33 @@
|
||||
<span class="nav-text">{{ nav.name }}</span>
|
||||
<span v-if="nav.path === '/cart'" class="cart-count">({{ cartItemCount }})</span>
|
||||
</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>
|
||||
|
||||
<!-- 面包屑导航 -->
|
||||
@@ -44,16 +73,25 @@ export default {
|
||||
cart: [],
|
||||
// 服务端购物车数量(统计 productMachineDtoList 的总条数)
|
||||
cartServerCount: 0,
|
||||
navigation: mainNavigation
|
||||
navigation: mainNavigation,
|
||||
// 用户邮箱
|
||||
userEmail: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 计算购物车数量
|
||||
cartItemCount() {
|
||||
// 只使用服务端数量,避免与本地缓存数量不一致
|
||||
return Number.isFinite(this.cartServerCount) ? this.cartServerCount : 0
|
||||
},
|
||||
// 计算面包屑导航
|
||||
breadcrumbs() {
|
||||
return getBreadcrumb(this.$route.path)
|
||||
},
|
||||
// 判断是否已登录(检查localStorage中是否有token)
|
||||
isLoggedIn() {
|
||||
const token = localStorage.getItem('token')
|
||||
return !!token // 有token就是已登录,没有就是未登录
|
||||
}
|
||||
},
|
||||
watch: {},
|
||||
@@ -65,6 +103,8 @@ export default {
|
||||
this.loadServerCartCount()
|
||||
// 监听应用内购物车更新事件
|
||||
window.addEventListener('cart-updated', this.handleCartUpdated)
|
||||
// 加载用户信息(邮箱)
|
||||
this.loadUserEmail()
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('storage', this.handleStorageChange)
|
||||
@@ -125,9 +165,58 @@ export default {
|
||||
// 无显式数量则主动刷新
|
||||
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() {
|
||||
// 清除localStorage中的所有登录信息
|
||||
localStorage.removeItem('token')
|
||||
localStorage.removeItem('userInfo')
|
||||
localStorage.removeItem('leasEmail')
|
||||
localStorage.removeItem('userId')
|
||||
localStorage.removeItem('username')
|
||||
|
||||
// 清空购物车
|
||||
this.user = null
|
||||
this.cart = []
|
||||
this.userEmail = ''
|
||||
|
||||
// 提示用户
|
||||
this.$message.success('退出登录成功')
|
||||
|
||||
// 跳转到登录页
|
||||
this.$router.push('/login')
|
||||
},
|
||||
getBreadcrumbPath(index) {
|
||||
const paths = ['/productList', '/cart', '/checkout']
|
||||
@@ -144,14 +233,30 @@ export default {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 导航栏布局:导航按钮在中间,登录状态在右边 */
|
||||
.navbar {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 24px;
|
||||
justify-content: center; /* 主内容居中 */
|
||||
align-items: center;
|
||||
background: #fff;
|
||||
border-bottom: 1px solid #eee;
|
||||
padding: 16px 0;
|
||||
padding: 16px 32px;
|
||||
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 {
|
||||
@@ -171,14 +276,19 @@ export default {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* 导航按钮悬停效果 */
|
||||
.nav-btn:hover {
|
||||
background: #f8f9fa;
|
||||
background: #f5f7ff;
|
||||
color: #667eea;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
/* 导航按钮激活状态 - 紫色渐变,跟登录按钮一样 */
|
||||
.nav-btn.active {
|
||||
background: #42b983;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: #fff;
|
||||
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.nav-icon {
|
||||
@@ -233,12 +343,87 @@ export default {
|
||||
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) {
|
||||
.navbar {
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
padding: 12px 0;
|
||||
gap: 16px;
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
.nav-left {
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
@@ -247,6 +432,25 @@ export default {
|
||||
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 {
|
||||
margin: 0 12px 16px 12px;
|
||||
padding: 8px 16px;
|
||||
|
||||
@@ -3,6 +3,40 @@
|
||||
* @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 = [
|
||||
{
|
||||
@@ -100,13 +134,9 @@ export const accountRoutes = [
|
||||
}
|
||||
},
|
||||
{
|
||||
// 兼容旧入口:收款记录 -> 卖家资金流水(收款tab)
|
||||
path: 'receipt-record',
|
||||
name: 'accountReceiptRecord',
|
||||
redirect: (to) => ({
|
||||
path: '/account/seller-funds-flow',
|
||||
query: { ...(to && to.query ? to.query : {}), tab: 'receipt' }
|
||||
}),
|
||||
component: () => import('../views/account/receiptRecord.vue'),
|
||||
meta: {
|
||||
title: '收款记录',
|
||||
description: '卖家收款流水记录',
|
||||
@@ -114,29 +144,15 @@ export const accountRoutes = [
|
||||
}
|
||||
},
|
||||
{
|
||||
// 兼容旧入口:提现记录 -> 卖家资金流水(提现tab)
|
||||
path: 'withdraw-record',
|
||||
name: 'accountWithdrawRecord',
|
||||
redirect: (to) => ({
|
||||
path: '/account/seller-funds-flow',
|
||||
query: { ...(to && to.query ? to.query : {}), tab: 'withdraw' }
|
||||
}),
|
||||
component: () => import('../views/account/withdrawRecord.vue'),
|
||||
meta: {
|
||||
title: '提现记录',
|
||||
description: '卖家提现流水记录',
|
||||
allAuthority: ['all']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'seller-funds-flow',
|
||||
name: 'accountSellerFundsFlow',
|
||||
component: () => import('../views/account/sellerFundsFlow.vue'),
|
||||
meta: {
|
||||
title: '资金流水',
|
||||
description: '卖家收款/提现记录切换查看',
|
||||
allAuthority: ['all']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'shop-new',
|
||||
name: 'accountShopNew',
|
||||
@@ -197,16 +213,6 @@ export const accountRoutes = [
|
||||
allAuthority: ['all']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'purchased-machine-detail/:id',
|
||||
name: 'purchasedMachineDetail',
|
||||
component: () => import('../views/account/purchasedMachineDetail.vue'),
|
||||
meta: {
|
||||
title: '已购商品详情',
|
||||
description: '查看已购买商品的详细信息',
|
||||
allAuthority: ['all']
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'funds-flow',
|
||||
name: 'accountFundsFlow',
|
||||
@@ -281,6 +287,10 @@ export const childrenRoutes = [
|
||||
|
||||
// 主路由配置
|
||||
export const mainRoutes = [
|
||||
// 认证路由(独立布局,无Header)
|
||||
...authRoutes,
|
||||
|
||||
// 主应用路由(带Layout)
|
||||
{
|
||||
path: '/',
|
||||
name: 'Home',
|
||||
@@ -288,6 +298,7 @@ export const mainRoutes = [
|
||||
redirect: '/productList',
|
||||
children: childrenRoutes
|
||||
},
|
||||
|
||||
// 404页面重定向到商品列表
|
||||
{
|
||||
path: '*',
|
||||
|
||||
@@ -295,14 +295,22 @@ export default {
|
||||
padding: 6px 10px;
|
||||
color: #2c3e50;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
/* 激活状态 - 紫色渐变 */
|
||||
.role-button.active {
|
||||
background: #42b983;
|
||||
border-color: #42b983;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
border-color: #667eea;
|
||||
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 {
|
||||
outline: 2px solid #42b98333;
|
||||
outline: 2px solid rgba(102, 126, 234, 0.2);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
/* 用户信息卡片:置于导航最前,展示邮箱与首字母头像 */
|
||||
@@ -318,17 +326,19 @@ export default {
|
||||
border-radius: 8px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
/* 头像也改成紫色渐变 */
|
||||
.avatar {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, #42b983, #67c23a);
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 700;
|
||||
font-size: 14px;
|
||||
box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);
|
||||
}
|
||||
.user-email {
|
||||
font-size: 14px;
|
||||
@@ -342,14 +352,18 @@ export default {
|
||||
color: #2c3e50;
|
||||
text-decoration: none;
|
||||
border-radius: 6px;
|
||||
transition: background 0.2s;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
/* 悬停效果 - 淡紫色 */
|
||||
.side-link:hover {
|
||||
background: #f6f8fa;
|
||||
background: #f5f7ff;
|
||||
color: #667eea;
|
||||
}
|
||||
/* 激活状态 - 紫色渐变 */
|
||||
.side-link.active {
|
||||
background: #42b983;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: #fff;
|
||||
box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);
|
||||
}
|
||||
|
||||
.content {
|
||||
|
||||
@@ -1160,7 +1160,7 @@ export default {
|
||||
<style scoped>
|
||||
/* 钱包容器 */
|
||||
.wallet-container {
|
||||
max-width: 800px;
|
||||
width: 70vw;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
596
power_leasing/src/views/auth/login.vue
Normal file
596
power_leasing/src/views/auth/login.vue
Normal 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>
|
||||
|
||||
|
||||
734
power_leasing/src/views/auth/register.vue
Normal file
734
power_leasing/src/views/auth/register.vue
Normal 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>
|
||||
|
||||
669
power_leasing/src/views/auth/reset-password.vue
Normal file
669
power_leasing/src/views/auth/reset-password.vue
Normal 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>
|
||||
|
||||
Reference in New Issue
Block a user