461 lines
11 KiB
Vue
461 lines
11 KiB
Vue
<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>
|
||
|