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

257 lines
5.9 KiB
Vue
Raw Normal View History

2025-09-26 16:40:38 +08:00
<template>
<div class="header-container">
<!-- 顶部导航栏 -->
<nav class="navbar">
<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>
</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,
navigation: mainNavigation
}
},
computed: {
cartItemCount() {
// 只使用服务端数量,避免与本地缓存数量不一致
return Number.isFinite(this.cartServerCount) ? this.cartServerCount : 0
},
breadcrumbs() {
return getBreadcrumb(this.$route.path)
}
},
watch: {},
mounted() {
this.loadCart()
// 监听购物车变化
window.addEventListener('storage', this.handleStorageChange)
// 首次加载服务端购物车数量
this.loadServerCartCount()
// 监听应用内购物车更新事件
window.addEventListener('cart-updated', this.handleCartUpdated)
},
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()
},
handleLogout() {
this.user = null
this.cart = []
},
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;
gap: 24px;
background: #fff;
border-bottom: 1px solid #eee;
padding: 16px 0;
margin-bottom: 16px;
}
.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: #f8f9fa;
transform: translateY(-2px);
}
.nav-btn.active {
background: #42b983;
color: #fff;
}
.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;
}
/* 响应式设计 */
@media (max-width: 768px) {
.navbar {
flex-direction: column;
gap: 12px;
padding: 12px 0;
}
.nav-btn {
width: 100%;
justify-content: center;
padding: 16px 20px;
}
.breadcrumb {
margin: 0 12px 16px 12px;
padding: 8px 16px;
font-size: 12px;
}
}
</style>