Files
webs/power_leasing/src/components/header.vue
2025-12-24 15:19:57 +08:00

461 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="header-container">
<!-- 顶部导航栏 -->
<nav class="navbar">
<!-- 左侧导航按钮 -->
<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>
</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'
import { getGoodsListV2 } from '../api/shoppingCart'
export default {
name: 'Header',
data() {
return {
user: null,
cart: [],
// 服务端购物车数量(统计 productMachineDtoList 的总条数)
cartServerCount: 0,
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: {},
mounted() {
this.loadCart()
// 监听购物车变化
window.addEventListener('storage', this.handleStorageChange)
// 首次加载服务端购物车数量
this.loadServerCartCount()
// 监听应用内购物车更新事件
window.addEventListener('cart-updated', this.handleCartUpdated)
// 加载用户信息(邮箱)
this.loadUserEmail()
},
beforeDestroy() {
window.removeEventListener('storage', this.handleStorageChange)
window.removeEventListener('cart-updated', this.handleCartUpdated)
},
methods: {
loadCart() {
this.cart = readCart()
},
/**
* 加载服务器购物车数量
* 根据新接口结构res.rows[].cartMachineInfoDtoList.length 累加
*/
async loadServerCartCount() {
try {
const res = await getGoodsListV2()
let total = 0
// 新接口结构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)
}
this.cartServerCount = Number.isFinite(total) ? total : 0
} catch (e) {
// 忽略错误,保持当前显示
console.error('加载购物车数量失败:', e)
}
},
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()
},
/**
* 跳转到登录页
*/
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']
if (index === 0) return '/productList'
if (index < paths.length) return paths[index - 1]
return '/productList'
}
}
}
</script>
<style scoped>
.header-container {
width: 100%;
}
/* 导航栏布局:导航按钮在中间,登录状态在右边 */
.navbar {
display: flex;
justify-content: center; /* 主内容居中 */
align-items: center;
background: #fff;
border-bottom: 1px solid #eee;
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 {
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;
}
/* 导航按钮悬停效果 */
.nav-btn:hover {
background: #f5f7ff;
color: #667eea;
transform: translateY(-2px);
}
/* 导航按钮激活状态 - 紫色渐变,跟登录按钮一样 */
.nav-btn.active {
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 {
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;
}
/* 未登录:注册/登录按钮样式 */
.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: 16px;
padding: 12px 16px;
}
.nav-left {
flex-direction: column;
width: 100%;
gap: 8px;
}
.nav-btn {
width: 100%;
justify-content: center;
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;
font-size: 12px;
}
}
</style>