This commit is contained in:
2026-09-29 15:17:13 +08:00
commit 69e6ed8c59
113 changed files with 36564 additions and 0 deletions
+339
View File
@@ -0,0 +1,339 @@
<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>