Files
yxe-mp/pages/cart/index.vue
T
2026-09-29 15:17:13 +08:00

340 lines
7.0 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>
<view class="cart-page">
<view class="page-hero">
<AppNavbar class="page-navbar" title="购物车"/>
<view class="intro-card">
<view class="card-title">
<view class="title-bar"></view>
<text>购物车</text>
</view>
<text class="intro-text">按商家分单结算,蓝色主色适配竖版,支持多件商品数量调整与勾选。</text>
<view class="hero-tags">
<view class="chip">分商家结算</view>
<view class="chip">蓝白风格</view>
<view class="chip">竖版适配</view>
</view>
</view>
</view>
<view class="card vendor-card" v-for="(vendor, vIdx) in cartVendors" :key="vendor.name">
<view class="vendor-head">
<label class="check">
<checkbox :checked="vendor.selected" color="#2563eb" @tap.stop="toggleVendor(vIdx)" />
<text class="vendor-name">{{ vendor.name }}</text>
</label>
<view class="freight">运费:¥{{ vendor.freight }}</view>
</view>
<view class="item" v-for="(item, idx) in vendor.items" :key="item.id">
<label class="check">
<checkbox :checked="item.selected" color="#2563eb" @tap.stop="toggleItem(vIdx, idx)" />
</label>
<view class="item-info">
<view class="item-name">{{ item.name }}</view>
<view class="item-meta">规格 {{ item.specs }}</view>
<view class="price-row">
<view class="price">¥{{ item.price }}</view>
<view class="origin">¥{{ item.origin }}</view>
</view>
</view>
<view class="qty">
<view class="btn" @tap="changeQty(vIdx, idx, -1)">-</view>
<view class="num">{{ item.qty }}</view>
<view class="btn" @tap="changeQty(vIdx, idx, 1)">+</view>
</view>
</view>
</view>
<view class="summary card">
<view class="summary-left">
<view class="section-title">结算信息</view>
<view class="sum-line">已选 {{ selectedCount }} 件,商品金额 ¥{{ goodsTotal }}</view>
<view class="sum-line">预估运费 ¥{{ freightTotal }},合计 ¥{{ totalPrice }}</view>
</view>
<view class="primary-btn go-checkout" @tap="goCheckout">去结算</view>
</view>
</view>
</template>
<script>
import AppNavbar from "@/components/AppNavbar.vue";
export default {
components: {AppNavbar},
data() {
return {
cartVendors: [{
name: '华润三九',
freight: 8,
selected: true,
items: [{
id: 1,
name: '三九感冒灵颗粒',
specs: '10袋/盒',
price: 29.9,
origin: 39.9,
qty: 1,
selected: true
}, {
id: 2,
name: '感冒软胶囊',
specs: '24粒/盒',
price: 32,
origin: 45,
qty: 2,
selected: true
}]
}, {
name: '华润江中',
freight: 6,
selected: true,
items: [{
id: 3,
name: '健胃消食片',
specs: '48片/盒',
price: 36.8,
origin: 46,
qty: 2,
selected: true
}]
}]
}
},
computed: {
selectedCount() {
return this.cartVendors.reduce((sum, v) => {
v.items.forEach(i => {
if (i.selected) sum += i.qty
})
return sum
}, 0)
},
goodsTotal() {
return this.cartVendors.reduce((sum, v) => {
v.items.forEach(i => {
if (i.selected) sum += i.qty * i.price
})
return sum
}, 0).toFixed(2)
},
freightTotal() {
return this.cartVendors.reduce((sum, v) => {
const hasSelected = v.items.some(i => i.selected)
return sum + (hasSelected ? v.freight : 0)
}, 0).toFixed(2)
},
totalPrice() {
return (parseFloat(this.goodsTotal) + parseFloat(this.freightTotal)).toFixed(2)
}
},
methods: {
changeQty(vIdx, idx, delta) {
const item = this.cartVendors[vIdx].items[idx]
const next = item.qty + delta
if (next < 1) return
this.cartVendors[vIdx].items[idx].qty = next
this.cartVendors = [...this.cartVendors]
},
toggleItem(vIdx, idx) {
const item = this.cartVendors[vIdx].items[idx]
item.selected = !item.selected
this.syncVendorSelect(vIdx)
this.cartVendors = [...this.cartVendors]
},
toggleVendor(vIdx) {
const vendor = this.cartVendors[vIdx]
const next = !vendor.selected
vendor.selected = next
vendor.items.forEach(i => {
i.selected = next
})
this.cartVendors = [...this.cartVendors]
},
syncVendorSelect(vIdx) {
const vendor = this.cartVendors[vIdx]
vendor.selected = vendor.items.every(i => i.selected)
},
goCheckout() {
uni.navigateTo({
url: `/pages/order/checkout?total=${this.totalPrice}`
})
}
}
}
</script>
<style lang="scss" scoped>
.cart-page {
min-height: 100vh;
padding: 0 20rpx 40rpx;
background: #f5f5f5;
}
.page-hero {
padding: 30rpx 6rpx 18rpx;
background: url('https://yaodongxiao.oss-cn-guangzhou.aliyuncs.com/20260126/462bfa04cf0eff8698884c38b9d17519.png') no-repeat
top center / 100% auto;
}
.page-navbar {
padding: 0 6rpx;
}
.intro-card {
margin-top: 18rpx;
padding: 20rpx 18rpx;
border-radius: 20rpx;
background: rgba(255, 255, 255, 0.9);
box-shadow: 0 10rpx 20rpx rgba(0, 0, 0, 0.08);
}
.card-title {
display: flex;
align-items: center;
gap: 12rpx;
font-size: 28rpx;
font-weight: 700;
color: #1f2937;
}
.title-bar {
width: 6rpx;
height: 30rpx;
border-radius: 999rpx;
background: #2fc6a7;
}
.intro-text {
margin-top: 12rpx;
font-size: 26rpx;
color: #6b7280;
line-height: 1.6;
}
.hero-tags {
display: flex;
gap: 12rpx;
margin-top: 12rpx;
}
.vendor-card {
margin-top: 18rpx;
}
.vendor-head {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10rpx;
}
.check {
display: flex;
align-items: center;
gap: 8rpx;
}
.vendor-name {
font-weight: 700;
color: var(--text-main);
}
.freight {
color: var(--text-sub);
font-size: 24rpx;
}
.item {
display: flex;
gap: 12rpx;
align-items: center;
padding: 12rpx 0;
border-bottom: 1rpx solid #edf1f7;
}
.item:last-child {
border-bottom: none;
}
.item-info {
flex: 1;
}
.item-name {
font-weight: 700;
color: var(--text-main);
}
.item-meta {
color: var(--text-sub);
font-size: 24rpx;
margin: 4rpx 0;
}
.price-row {
display: flex;
gap: 10rpx;
align-items: baseline;
}
.price {
color: #ff3b30;
font-weight: 800;
font-size: 30rpx;
}
.origin {
text-decoration: line-through;
color: #a1aabc;
font-size: 24rpx;
}
.qty {
display: flex;
align-items: center;
gap: 8rpx;
background: #f4f7fb;
border-radius: 12rpx;
padding: 6rpx 10rpx;
}
.btn {
width: 40rpx;
height: 40rpx;
border-radius: 12rpx;
background: #e9f1ff;
color: var(--brand-primary);
text-align: center;
line-height: 40rpx;
font-weight: 800;
}
.num {
min-width: 34rpx;
text-align: center;
color: var(--text-main);
font-weight: 700;
}
.summary {
margin-top: 18rpx;
display: flex;
justify-content: space-between;
align-items: center;
}
.summary-left {
flex: 1;
}
.sum-line {
color: var(--text-sub);
margin-top: 6rpx;
}
.go-checkout {
padding: 14rpx 24rpx;
margin-left: 12rpx;
}
</style>