Files
webs/power_leasing/src/components/header.vue

461 lines
11 KiB
Vue
Raw Normal View History

2025-09-26 16:40:38 +08:00
<template>
<div class="header-container">
<!-- 顶部导航栏 -->
<nav class="navbar">
2025-12-24 15:19:57 +08:00
<!-- 左侧导航按钮 -->
<div class="nav-left">
<router-link
v-for="nav in navigation"
:key="nav.path"
:to="nav.path"
class="nav-btn"
active-class="active"
:title="nav.description"
>
<span class="nav-icon">{{ nav.icon }}</span>
<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>
2025-09-26 16:40:38 +08:00
</nav>
<!-- 面包屑导航 -->
<!-- <div class="breadcrumb">
<router-link
v-for="(crumb, index) in breadcrumbs"
:key="index"
:to="getBreadcrumbPath(index)"
class="breadcrumb-item"
:class="{ active: index === breadcrumbs.length - 1 }"
>
{{ crumb }}
</router-link>
</div> -->
</div>
</template>
<script>
import { readCart } from '../utils/cartManager'
import { mainNavigation, getBreadcrumb } from '../utils/navigation'
2025-12-12 15:33:23 +08:00
import { getGoodsListV2 } from '../api/shoppingCart'
2025-09-26 16:40:38 +08:00
export default {
name: 'Header',
data() {
return {
user: null,
cart: [],
// 服务端购物车数量(统计 productMachineDtoList 的总条数)
cartServerCount: 0,
2025-12-24 15:19:57 +08:00
navigation: mainNavigation,
// 用户邮箱
userEmail: ''
2025-09-26 16:40:38 +08:00
}
},
computed: {
2025-12-24 15:19:57 +08:00
// 计算购物车数量
2025-09-26 16:40:38 +08:00
cartItemCount() {
// 只使用服务端数量,避免与本地缓存数量不一致
return Number.isFinite(this.cartServerCount) ? this.cartServerCount : 0
},
2025-12-24 15:19:57 +08:00
// 计算面包屑导航
2025-09-26 16:40:38 +08:00
breadcrumbs() {
return getBreadcrumb(this.$route.path)
2025-12-24 15:19:57 +08:00
},
// 判断是否已登录检查localStorage中是否有token
isLoggedIn() {
const token = localStorage.getItem('token')
return !!token // 有token就是已登录没有就是未登录
2025-09-26 16:40:38 +08:00
}
},
watch: {},
mounted() {
this.loadCart()
// 监听购物车变化
window.addEventListener('storage', this.handleStorageChange)
// 首次加载服务端购物车数量
this.loadServerCartCount()
// 监听应用内购物车更新事件
window.addEventListener('cart-updated', this.handleCartUpdated)
2025-12-24 15:19:57 +08:00
// 加载用户信息(邮箱)
this.loadUserEmail()
2025-09-26 16:40:38 +08:00
},
beforeDestroy() {
window.removeEventListener('storage', this.handleStorageChange)
window.removeEventListener('cart-updated', this.handleCartUpdated)
},
methods: {
loadCart() {
this.cart = readCart()
},
2025-12-12 15:33:23 +08:00
/**
* 加载服务器购物车数量
* 根据新接口结构res.rows[].cartMachineInfoDtoList.length 累加
*/
2025-09-26 16:40:38 +08:00
async loadServerCartCount() {
try {
2025-12-12 15:33:23 +08:00
const res = await getGoodsListV2()
2025-09-26 16:40:38 +08:00
let total = 0
2025-12-12 15:33:23 +08:00
// 新接口结构res.rows 是店铺数组,每个店铺有 cartMachineInfoDtoList
if (Array.isArray(res && res.rows)) {
total = res.rows.reduce((sum, shop) => {
const machineList = Array.isArray(shop && shop.cartMachineInfoDtoList)
? shop.cartMachineInfoDtoList
: []
return sum + machineList.length
}, 0)
} else if (Array.isArray(res && res.data && res.data.rows)) {
// 兼容:如果数据在 res.data.rows 中
total = res.data.rows.reduce((sum, shop) => {
const machineList = Array.isArray(shop && shop.cartMachineInfoDtoList)
? shop.cartMachineInfoDtoList
: []
return sum + machineList.length
}, 0)
2025-09-26 16:40:38 +08:00
}
this.cartServerCount = Number.isFinite(total) ? total : 0
} catch (e) {
// 忽略错误,保持当前显示
2025-12-12 15:33:23 +08:00
console.error('加载购物车数量失败:', e)
2025-09-26 16:40:38 +08:00
}
},
handleStorageChange(event) {
if (event.key === 'power_leasing_cart_v1') {
this.loadCart()
this.loadServerCartCount()
}
},
handleCartUpdated(event) {
// 支持事件携带数量 { detail: { count } }
try {
const next = event && event.detail && Number(event.detail.count)
if (Number.isFinite(next)) {
this.cartServerCount = next
return
}
} catch (e) { /* ignore malformed event detail */ }
// 无显式数量则主动刷新
this.loadServerCartCount()
},
2025-12-24 15:19:57 +08:00
/**
* 跳转到登录页
*/
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 = ''
}
},
/**
* 退出登录
* 清除所有登录信息跳转到登录页
*/
2025-09-26 16:40:38 +08:00
handleLogout() {
2025-12-24 15:19:57 +08:00
// 清除localStorage中的所有登录信息
localStorage.removeItem('token')
localStorage.removeItem('userInfo')
localStorage.removeItem('leasEmail')
localStorage.removeItem('userId')
localStorage.removeItem('username')
// 清空购物车
2025-09-26 16:40:38 +08:00
this.user = null
this.cart = []
2025-12-24 15:19:57 +08:00
this.userEmail = ''
// 提示用户
this.$message.success('退出登录成功')
// 跳转到登录页
this.$router.push('/login')
2025-09-26 16:40:38 +08:00
},
getBreadcrumbPath(index) {
const paths = ['/productList', '/cart', '/checkout']
if (index === 0) return '/productList'
if (index < paths.length) return paths[index - 1]
return '/productList'
}
}
}
</script>
<style scoped>
.header-container {
width: 100%;
}
2025-12-24 15:19:57 +08:00
/* 导航栏布局:导航按钮在中间,登录状态在右边 */
2025-09-26 16:40:38 +08:00
.navbar {
display: flex;
2025-12-24 15:19:57 +08:00
justify-content: center; /* 主内容居中 */
align-items: center;
2025-09-26 16:40:38 +08:00
background: #fff;
border-bottom: 1px solid #eee;
2025-12-24 15:19:57 +08:00
padding: 16px 32px;
2025-09-26 16:40:38 +08:00
margin-bottom: 16px;
2025-12-24 15:19:57 +08:00
position: relative; /* 让右侧元素可以绝对定位 */
}
/* 左侧导航按钮区域(实际在中间显示) */
.nav-left {
display: flex;
gap: 24px;
}
/* 右侧用户登录区域(绝对定位到右边) */
.nav-right {
display: flex;
align-items: center;
position: absolute;
right: 32px; /* 距离右边32px */
2025-09-26 16:40:38 +08:00
}
.nav-btn {
display: flex;
align-items: center;
gap: 8px;
background: none;
border: none;
font-size: 16px;
color: #2c3e50;
cursor: pointer;
padding: 12px 20px;
border-radius: 8px;
transition: all 0.3s ease;
text-decoration: none;
outline: none;
position: relative;
}
2025-12-24 15:19:57 +08:00
/* 导航按钮悬停效果 */
2025-09-26 16:40:38 +08:00
.nav-btn:hover {
2025-12-24 15:19:57 +08:00
background: #f5f7ff;
color: #667eea;
2025-09-26 16:40:38 +08:00
transform: translateY(-2px);
}
2025-12-24 15:19:57 +08:00
/* 导航按钮激活状态 - 紫色渐变,跟登录按钮一样 */
2025-09-26 16:40:38 +08:00
.nav-btn.active {
2025-12-24 15:19:57 +08:00
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
2025-09-26 16:40:38 +08:00
color: #fff;
2025-12-24 15:19:57 +08:00
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
transform: translateY(-2px);
2025-09-26 16:40:38 +08:00
}
.nav-icon {
font-size: 18px;
}
.nav-text {
font-weight: 600;
}
.cart-count {
background: #e74c3c;
color: white;
padding: 2px 8px;
border-radius: 12px;
font-size: 12px;
font-weight: bold;
min-width: 20px;
text-align: center;
}
/* 面包屑导航样式 */
.breadcrumb {
display: flex;
align-items: center;
gap: 8px;
padding: 12px 20px;
background: #f8f9fa;
border-radius: 8px;
margin: 0 20px 20px 20px;
font-size: 14px;
}
.breadcrumb-item {
color: #666;
text-decoration: none;
transition: color 0.3s ease;
}
.breadcrumb-item:hover {
color: #42b983;
}
.breadcrumb-item.active {
color: #2c3e50;
font-weight: 600;
}
.breadcrumb-item:not(:last-child)::after {
content: '>';
margin-left: 8px;
color: #ccc;
}
2025-12-24 15:19:57 +08:00
/* 未登录:注册/登录按钮样式 */
.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;
}
/* 响应式设计 - 手机端适配 */
2025-09-26 16:40:38 +08:00
@media (max-width: 768px) {
.navbar {
flex-direction: column;
2025-12-24 15:19:57 +08:00
gap: 16px;
padding: 12px 16px;
}
.nav-left {
flex-direction: column;
width: 100%;
gap: 8px;
2025-09-26 16:40:38 +08:00
}
.nav-btn {
width: 100%;
justify-content: center;
padding: 16px 20px;
}
2025-12-24 15:19:57 +08:00
.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;
}
2025-09-26 16:40:38 +08:00
.breadcrumb {
margin: 0 12px 16px 12px;
padding: 8px 16px;
font-size: 12px;
}
}
</style>