340 lines
7.0 KiB
Vue
340 lines
7.0 KiB
Vue
<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>
|