更新保存代码
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,18 +2,47 @@
|
|||||||
<div class="header-container">
|
<div class="header-container">
|
||||||
<!-- 顶部导航栏 -->
|
<!-- 顶部导航栏 -->
|
||||||
<nav class="navbar">
|
<nav class="navbar">
|
||||||
<router-link
|
<!-- 左侧:导航按钮 -->
|
||||||
v-for="nav in navigation"
|
<div class="nav-left">
|
||||||
:key="nav.path"
|
<router-link
|
||||||
:to="nav.path"
|
v-for="nav in navigation"
|
||||||
class="nav-btn"
|
:key="nav.path"
|
||||||
active-class="active"
|
:to="nav.path"
|
||||||
:title="nav.description"
|
class="nav-btn"
|
||||||
>
|
active-class="active"
|
||||||
<span class="nav-icon">{{ nav.icon }}</span>
|
:title="nav.description"
|
||||||
<span class="nav-text">{{ nav.name }}</span>
|
>
|
||||||
<span v-if="nav.path === '/cart'" class="cart-count">({{ cartItemCount }})</span>
|
<span class="nav-icon">{{ nav.icon }}</span>
|
||||||
</router-link>
|
<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>
|
</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;
|
||||||
|
|||||||
@@ -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: '*',
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
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