Files
webs/power_leasing/src/components/header.vue
2025-09-26 16:40:38 +08:00

267 lines
6.3 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">
<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'
import { getGoodsList } from '../api/shoppingCart'
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()
},
async loadServerCartCount() {
try {
const res = await getGoodsList()
// 统一提取 rows/数组
const primary = Array.isArray(res && res.rows)
? res.rows
: Array.isArray(res && res.data && res.data.rows)
? res.data.rows
: Array.isArray(res && res.data)
? res.data
: (Array.isArray(res) ? res : [])
let groups = []
if (Array.isArray(primary) && primary.length) {
// 情况Ashop -> shoppingCartInfoDtoList -> productMachineDtoList
if (Array.isArray(primary[0] && primary[0].shoppingCartInfoDtoList)) {
primary.forEach(shop => {
if (Array.isArray(shop && shop.shoppingCartInfoDtoList)) {
groups.push(...shop.shoppingCartInfoDtoList)
}
})
} else {
// 情况B直接就是商品分组数组
groups = primary
}
} else if (Array.isArray(res && res.shoppingCartInfoDtoList)) {
// 情况C返回对象直接有 shoppingCartInfoDtoList
groups = res.shoppingCartInfoDtoList
}
let total = 0
if (groups.length) {
total = groups.reduce((sum, g) => sum + (Array.isArray(g && g.productMachineDtoList) ? g.productMachineDtoList.length : 0), 0)
} else if (Array.isArray(res && res.productMachineDtoList)) {
// 情况D根对象直接是机器列表
total = res.productMachineDtoList.length
}
this.cartServerCount = Number.isFinite(total) ? total : 0
} catch (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()
},
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>