|
|
@@ -4,9 +4,12 @@ import { ref, computed } from 'vue'
|
|
|
import { useUserStore } from '@/stores/user'
|
|
|
import { useCartStore } from '@/stores/cart'
|
|
|
import { formatYuan } from '@/utils/format'
|
|
|
-import { IMG_DEFAULT_PRODUCT, IMG_EMPTY } from '@/utils/staticAsset'
|
|
|
+import { IMG_DEFAULT_PRODUCT, IMG_EMPTY, staticAssetUrl } from '@/utils/staticAsset'
|
|
|
import { request, parseResponseJson } from '@/utils/request'
|
|
|
import ProductDetailOverlay from '@/components/client/product-detail-overlay.vue'
|
|
|
+import ClientTabBar from '@/components/client/client-tab-bar.vue'
|
|
|
+import ClientSkeleton from '@/components/client/client-skeleton.vue'
|
|
|
+import ClientImageLoader from '@/components/client/client-image-loader.vue'
|
|
|
|
|
|
// Store
|
|
|
const userStore = useUserStore()
|
|
|
@@ -62,6 +65,9 @@ const groupedCartLines = computed(() => {
|
|
|
return groups
|
|
|
})
|
|
|
|
|
|
+const ICON_COORDINATE = staticAssetUrl('coordinate.svg')
|
|
|
+const ICON_DELETE = staticAssetUrl('delete.svg')
|
|
|
+
|
|
|
async function loadCartList(reset) {
|
|
|
if (cartListLoading.value) return
|
|
|
const page = reset ? 1 : nextCartPage.value
|
|
|
@@ -152,7 +158,7 @@ async function loadAddresses() {
|
|
|
const res = await request({
|
|
|
url: '/mini/client/addresses',
|
|
|
method: 'GET',
|
|
|
- data: { page: 1, pageSize: 500 }
|
|
|
+ data: { page: 1, pageSize: 100 }
|
|
|
})
|
|
|
const body = parseResponseJson(res)
|
|
|
if (body.code !== 200) {
|
|
|
@@ -338,13 +344,18 @@ onShow(loadCart)
|
|
|
</script>
|
|
|
|
|
|
<template>
|
|
|
- <view class="cart-root">
|
|
|
+ <view class="cart-root client-theme">
|
|
|
<view class="cart-page">
|
|
|
+ <!-- 对齐 sanqiang-client-cart-ui-demo.html .top -->
|
|
|
+ <view class="cart-hero">
|
|
|
+ <text class="cart-hero-title">购物车</text>
|
|
|
+ <text class="cart-hero-sub">支持一键结算</text>
|
|
|
+ </view>
|
|
|
<!-- 顶部固定:收货地址(不参与商品区滚动) -->
|
|
|
<view class="cart-top">
|
|
|
<view class="address-card">
|
|
|
<view class="addr-left">
|
|
|
- <text class="addr-icon">📍</text>
|
|
|
+ <image class="addr-icon" :src="ICON_COORDINATE" mode="aspectFit" />
|
|
|
</view>
|
|
|
<view class="addr-main">
|
|
|
<view class="addr-top">
|
|
|
@@ -373,71 +384,100 @@ onShow(loadCart)
|
|
|
:lower-threshold="80"
|
|
|
@scrolltolower="onCartScrollToLower"
|
|
|
>
|
|
|
- <view v-if="!cartStore.isEmpty" class="cart-items-inner">
|
|
|
+ <view v-if="cartListLoading && cartLines.length === 0" class="cart-items-inner">
|
|
|
+ <ClientSkeleton variant="list" :rows="3" />
|
|
|
+ </view>
|
|
|
+ <view v-else-if="!cartStore.isEmpty" class="cart-items-inner">
|
|
|
<view v-for="g in groupedCartLines" :key="g.key" class="category-group">
|
|
|
<view class="category-header">
|
|
|
<text class="category-name">{{ g.title }}</text>
|
|
|
</view>
|
|
|
|
|
|
- <view v-for="item in g.items" :key="item.id" class="cart-item">
|
|
|
+ <view
|
|
|
+ v-for="item in g.items"
|
|
|
+ :key="item.id"
|
|
|
+ class="cart-item"
|
|
|
+ hover-class="cart-item--pressed"
|
|
|
+ :hover-stay-time="100"
|
|
|
+ >
|
|
|
<view class="item-check" @click.stop="toggleSelect(item.id)">
|
|
|
<view class="circle" :class="{ checked: selectedIds.has(item.id) }" />
|
|
|
</view>
|
|
|
- <view class="item-image" @click="goProductView(item.productId, item.quantity)">
|
|
|
- <image :src="item.imageUrl || IMG_DEFAULT_PRODUCT" mode="aspectFill" />
|
|
|
- </view>
|
|
|
-
|
|
|
- <view class="item-info" @click="goProductView(item.productId, item.quantity)">
|
|
|
- <text class="item-name">{{ item.productName }}</text>
|
|
|
- <text class="item-unit">{{ formatMeasureText(item.measureValue, item.unitName) }}</text>
|
|
|
- <text class="item-price">¥{{ formatPrice(item.price) }}</text>
|
|
|
- </view>
|
|
|
-
|
|
|
- <view class="item-right-actions">
|
|
|
- <view class="item-delete" @click.stop="removeItem(item.id)">
|
|
|
- <text>🗑️</text>
|
|
|
+ <view class="item-card">
|
|
|
+ <view class="item-image sq-product-thumb sq-product-thumb--md" @click="goProductView(item.productId, item.quantity)">
|
|
|
+ <ClientImageLoader :src="item.imageUrl || IMG_DEFAULT_PRODUCT" mode="aspectFill" radius="20rpx" />
|
|
|
</view>
|
|
|
- <view class="quantity-control" @click.stop>
|
|
|
- <view class="qty-btn" @click.stop="updateQuantity(item.id, -1)">
|
|
|
- <text>-</text>
|
|
|
+
|
|
|
+ <view class="item-body">
|
|
|
+ <view class="item-title-row">
|
|
|
+ <text class="item-name" @click="goProductView(item.productId, item.quantity)">{{ item.productName }}</text>
|
|
|
+ <view class="item-delete" @click.stop="removeItem(item.id)">
|
|
|
+ <image class="item-delete-icon" :src="ICON_DELETE" mode="aspectFit" />
|
|
|
+ </view>
|
|
|
</view>
|
|
|
- <text class="qty-num">{{ item.quantity }}</text>
|
|
|
- <view class="qty-btn" @click.stop="updateQuantity(item.id, 1)">
|
|
|
- <text>+</text>
|
|
|
+ <text class="item-unit" @click="goProductView(item.productId, item.quantity)">
|
|
|
+ {{ formatMeasureText(item.measureValue, item.unitName) }}
|
|
|
+ </text>
|
|
|
+ <view class="item-line">
|
|
|
+ <text class="item-price">¥{{ formatPrice(item.price) }}</text>
|
|
|
+ <view class="line-actions">
|
|
|
+ <view class="quantity-control" @click.stop>
|
|
|
+ <view
|
|
|
+ class="qty-btn"
|
|
|
+ hover-class="qty-btn--pressed"
|
|
|
+ :hover-stay-time="100"
|
|
|
+ @click.stop="updateQuantity(item.id, -1)"
|
|
|
+ >
|
|
|
+ <text class="qty-symbol">-</text>
|
|
|
+ </view>
|
|
|
+ <text class="qty-num">{{ item.quantity }}</text>
|
|
|
+ <view
|
|
|
+ class="qty-btn"
|
|
|
+ hover-class="qty-btn--pressed"
|
|
|
+ :hover-stay-time="100"
|
|
|
+ @click.stop="updateQuantity(item.id, 1)"
|
|
|
+ >
|
|
|
+ <text class="qty-symbol">+</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
- <view v-if="cartListLoading && cartLines.length === 0" class="list-footer-tip">加载中...</view>
|
|
|
- <view v-else-if="cartListHasMore" class="list-footer-tip">上拉加载更多</view>
|
|
|
+ <view v-if="cartListHasMore" class="list-footer-tip">上拉加载更多</view>
|
|
|
<view v-else-if="cartLines.length > 0" class="list-footer-tip muted">没有更多了</view>
|
|
|
</view>
|
|
|
|
|
|
<view v-else class="empty-state">
|
|
|
<image class="empty-image" :src="IMG_EMPTY" mode="aspectFit" />
|
|
|
<text class="empty-text">购物车是空的</text>
|
|
|
- <button class="empty-btn" type="default" @click="goHome">去逛逛</button>
|
|
|
+ <van-button class="empty-btn" round type="primary" color="#0a9f6d" @click="goHome">去逛逛</van-button>
|
|
|
</view>
|
|
|
</scroll-view>
|
|
|
</view>
|
|
|
|
|
|
<!-- 结算栏移出 cart-page,避免 overflow:hidden 裁切;bottom 与 window-bottom 对齐 -->
|
|
|
<view v-if="!cartStore.isEmpty" class="checkout-bar">
|
|
|
- <view class="bar-left">
|
|
|
- <view class="select-all" @click="toggleSelectAll">
|
|
|
- <view class="circle" :class="{ checked: isAllSelected }" />
|
|
|
- <text class="select-all-text">全选</text>
|
|
|
+ <view class="checkout-inner">
|
|
|
+ <view class="bar-left">
|
|
|
+ <view class="select-all" @click="toggleSelectAll">
|
|
|
+ <view class="circle" :class="{ checked: isAllSelected }" />
|
|
|
+ <text class="select-all-text">全选</text>
|
|
|
+ </view>
|
|
|
+ <view class="total-block">
|
|
|
+ <text class="total-muted">已选 {{ selectedCount }} 件商品</text>
|
|
|
+ <text class="total-strong">¥{{ formatPrice(selectedAmount) }}</text>
|
|
|
+ </view>
|
|
|
</view>
|
|
|
- <view class="total-info">
|
|
|
- <text class="total-label">合计(元):</text>
|
|
|
- <text class="total-price">¥{{ formatPrice(selectedAmount) }}</text>
|
|
|
- </view>
|
|
|
- </view>
|
|
|
|
|
|
- <view class="checkout-actions">
|
|
|
- <button class="checkout-btn" type="default" @click="goCheckout">结算({{ selectedCount }})</button>
|
|
|
+ <view class="checkout-actions">
|
|
|
+ <view class="cart-settle" @click="goCheckout">
|
|
|
+ <text>去结算</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
@@ -447,6 +487,9 @@ onShow(loadCart)
|
|
|
:initial-cart-qty="overlayInitialCartQty"
|
|
|
@close="closeProductOverlay"
|
|
|
/>
|
|
|
+ <!-- #ifdef H5 -->
|
|
|
+ <ClientTabBar active="cart" />
|
|
|
+ <!-- #endif -->
|
|
|
</view>
|
|
|
</template>
|
|
|
|
|
|
@@ -454,7 +497,7 @@ onShow(loadCart)
|
|
|
page {
|
|
|
height: 100%;
|
|
|
overflow: hidden;
|
|
|
- background-color: #f5f5f5;
|
|
|
+ background-color: #f5f8f7;
|
|
|
}
|
|
|
</style>
|
|
|
|
|
|
@@ -477,7 +520,7 @@ page {
|
|
|
z-index: 1;
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
- background: #f5f5f5;
|
|
|
+ background: var(--c-bg);
|
|
|
overflow: hidden;
|
|
|
box-sizing: border-box;
|
|
|
}
|
|
|
@@ -490,16 +533,41 @@ page {
|
|
|
min-height: 0;
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
- background: #f5f5f5;
|
|
|
+ background: var(--c-bg);
|
|
|
overflow: hidden;
|
|
|
box-sizing: border-box;
|
|
|
}
|
|
|
/* #endif */
|
|
|
|
|
|
-.cart-top {
|
|
|
+.cart-hero {
|
|
|
+ flex-shrink: 0;
|
|
|
+ background: linear-gradient(145deg, var(--c-brand-green), var(--c-brand-green-2));
|
|
|
+ color: #ffffff;
|
|
|
+ padding: calc(24rpx + env(safe-area-inset-top, 0px)) 32rpx 28rpx;
|
|
|
+ border-bottom-left-radius: 36rpx;
|
|
|
+ border-bottom-right-radius: 36rpx;
|
|
|
+ box-shadow: var(--c-shadow);
|
|
|
+}
|
|
|
+
|
|
|
+.cart-hero-title {
|
|
|
+ display: block;
|
|
|
+ font-size: 36rpx;
|
|
|
+ font-weight: 700;
|
|
|
+ line-height: 1.25;
|
|
|
+ margin-bottom: 12rpx;
|
|
|
+}
|
|
|
+
|
|
|
+.cart-hero-sub {
|
|
|
+ display: block;
|
|
|
+ font-size: 24rpx;
|
|
|
+ opacity: 0.9;
|
|
|
+ line-height: 1.35;
|
|
|
+}
|
|
|
+
|
|
|
+ .cart-top {
|
|
|
flex-shrink: 0;
|
|
|
flex-grow: 0;
|
|
|
- padding: 20rpx 20rpx 0;
|
|
|
+ padding: 24rpx 32rpx 0;
|
|
|
box-sizing: border-box;
|
|
|
position: relative;
|
|
|
z-index: 2;
|
|
|
@@ -517,7 +585,7 @@ page {
|
|
|
}
|
|
|
|
|
|
.cart-items-inner {
|
|
|
- padding: 0 20rpx 20rpx;
|
|
|
+ padding: 0 32rpx 24rpx;
|
|
|
padding-bottom: calc(24rpx + 120rpx + constant(safe-area-inset-bottom));
|
|
|
padding-bottom: calc(24rpx + 120rpx + env(safe-area-inset-bottom));
|
|
|
box-sizing: border-box;
|
|
|
@@ -536,11 +604,12 @@ page {
|
|
|
|
|
|
.address-card {
|
|
|
display: flex;
|
|
|
- background: #ffffff;
|
|
|
- border-radius: 12rpx;
|
|
|
+ background: var(--c-card);
|
|
|
+ border-radius: var(--c-radius-card);
|
|
|
padding: 22rpx 20rpx;
|
|
|
margin-bottom: 18rpx;
|
|
|
- box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
|
|
|
+ border: 1rpx solid var(--c-border);
|
|
|
+ box-shadow: none;
|
|
|
box-sizing: border-box;
|
|
|
|
|
|
.addr-left {
|
|
|
@@ -549,7 +618,8 @@ page {
|
|
|
justify-content: center;
|
|
|
padding-top: 4rpx;
|
|
|
.addr-icon {
|
|
|
- font-size: 34rpx;
|
|
|
+ width: 34rpx;
|
|
|
+ height: 34rpx;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -565,12 +635,12 @@ page {
|
|
|
margin-bottom: 10rpx;
|
|
|
.addr-title {
|
|
|
font-size: 28rpx;
|
|
|
- color: #303133;
|
|
|
- font-weight: 600;
|
|
|
+ color: var(--c-text);
|
|
|
+ font-weight: 700;
|
|
|
}
|
|
|
.addr-edit {
|
|
|
font-size: 24rpx;
|
|
|
- color: #07c160;
|
|
|
+ color: var(--c-brand-green);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -585,18 +655,18 @@ page {
|
|
|
|
|
|
.addr-person-name {
|
|
|
font-size: 26rpx;
|
|
|
- color: #303133;
|
|
|
+ color: var(--c-text);
|
|
|
}
|
|
|
|
|
|
.addr-phone {
|
|
|
font-size: 26rpx;
|
|
|
- color: #303133;
|
|
|
+ color: var(--c-text);
|
|
|
}
|
|
|
|
|
|
.addr-detail {
|
|
|
display: block;
|
|
|
font-size: 24rpx;
|
|
|
- color: #606266;
|
|
|
+ color: var(--c-muted);
|
|
|
line-height: 1.4;
|
|
|
word-break: break-all;
|
|
|
}
|
|
|
@@ -606,7 +676,7 @@ page {
|
|
|
padding: 12rpx 0 6rpx;
|
|
|
.addr-empty-text {
|
|
|
font-size: 26rpx;
|
|
|
- color: #909399;
|
|
|
+ color: var(--c-muted);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -621,157 +691,245 @@ page {
|
|
|
position: relative;
|
|
|
|
|
|
&.checked {
|
|
|
- border-color: #07c160;
|
|
|
- background: #07c160;
|
|
|
+ border-color: var(--c-brand-green);
|
|
|
+ background: var(--c-brand-green);
|
|
|
}
|
|
|
|
|
|
&.checked::after {
|
|
|
content: '';
|
|
|
position: absolute;
|
|
|
- left: 11rpx;
|
|
|
- top: 6rpx;
|
|
|
+ left: 50%;
|
|
|
+ top: 50%;
|
|
|
width: 8rpx;
|
|
|
height: 16rpx;
|
|
|
border-right: 4rpx solid #fff;
|
|
|
border-bottom: 4rpx solid #fff;
|
|
|
- transform: rotate(45deg);
|
|
|
+ transform: translate(-50%, -58%) rotate(45deg);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// 分类分组
|
|
|
+// 分类分组(对齐 demo .store)
|
|
|
.category-group {
|
|
|
- margin-bottom: 24rpx;
|
|
|
- background: #ffffff;
|
|
|
- border-radius: 12rpx;
|
|
|
- padding: 0 20rpx;
|
|
|
- box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
|
|
|
+ margin-bottom: 20rpx;
|
|
|
+ background: var(--c-card);
|
|
|
+ border-radius: 28rpx;
|
|
|
+ padding: 0 24rpx;
|
|
|
+ border: 1rpx solid var(--c-border);
|
|
|
+ box-shadow: none;
|
|
|
box-sizing: border-box;
|
|
|
|
|
|
.category-header {
|
|
|
- padding: 18rpx 0;
|
|
|
- border-bottom: 1rpx solid #e4e7ed;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ padding: 20rpx 0 16rpx;
|
|
|
+ border-bottom: 1rpx solid #edf5f1;
|
|
|
|
|
|
.category-name {
|
|
|
font-size: 28rpx;
|
|
|
font-weight: 600;
|
|
|
- color: #303133;
|
|
|
+ color: var(--c-text);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// 购物车项
|
|
|
+// 购物车项(对齐 demo .item 网格 84px + 文案 + .line)
|
|
|
.cart-item {
|
|
|
- display: flex;
|
|
|
- align-items: flex-start;
|
|
|
- padding: 20rpx 0;
|
|
|
- border-bottom: 1rpx solid #f0f2f5;
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: 48rpx minmax(0, 1fr);
|
|
|
+ align-items: center;
|
|
|
+ column-gap: 10rpx;
|
|
|
+ padding: 16rpx 0;
|
|
|
+ border-top: 1rpx solid #edf5f1;
|
|
|
|
|
|
- &:last-child {
|
|
|
- border-bottom: none;
|
|
|
+ &:first-of-type {
|
|
|
+ border-top: none;
|
|
|
}
|
|
|
|
|
|
.item-check {
|
|
|
- width: 54rpx;
|
|
|
+ width: 48rpx;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
- margin-right: 10rpx;
|
|
|
+ align-self: center;
|
|
|
}
|
|
|
|
|
|
- .item-image {
|
|
|
- width: 160rpx;
|
|
|
- height: 160rpx;
|
|
|
- border-radius: 8rpx;
|
|
|
+ .item-card {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: 168rpx minmax(0, 1fr);
|
|
|
+ gap: min(16rpx, 2.5vw);
|
|
|
+ align-items: stretch;
|
|
|
+ min-height: 168rpx;
|
|
|
+ height: auto;
|
|
|
+ background: #fff;
|
|
|
+ border: 1rpx solid var(--c-border);
|
|
|
+ border-radius: var(--c-radius-md);
|
|
|
overflow: hidden;
|
|
|
- margin-right: 20rpx;
|
|
|
- background: #f5f7fa;
|
|
|
+ box-sizing: border-box;
|
|
|
+ }
|
|
|
|
|
|
+ .item-image {
|
|
|
+ width: 168rpx;
|
|
|
+ height: 168rpx;
|
|
|
+ min-width: 168rpx;
|
|
|
+ min-height: 168rpx;
|
|
|
+ max-width: 168rpx;
|
|
|
+ max-height: 168rpx;
|
|
|
+ flex-shrink: 0;
|
|
|
+ align-self: center;
|
|
|
+ border-radius: 20rpx;
|
|
|
+ overflow: hidden;
|
|
|
+ background: linear-gradient(145deg, #f4fbf8, #e4f6ef);
|
|
|
image {
|
|
|
+ display: block;
|
|
|
width: 100%;
|
|
|
height: 100%;
|
|
|
+ object-fit: cover;
|
|
|
+ border-radius: inherit;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- .item-info {
|
|
|
- flex: 1;
|
|
|
+ .item-body {
|
|
|
+ min-height: 0;
|
|
|
min-width: 0;
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
justify-content: flex-start;
|
|
|
-
|
|
|
- .item-name {
|
|
|
- display: block;
|
|
|
- font-size: 28rpx;
|
|
|
- color: #303133;
|
|
|
- line-height: 1.45;
|
|
|
- word-break: break-word;
|
|
|
- overflow-wrap: break-word;
|
|
|
- }
|
|
|
-
|
|
|
- .item-unit {
|
|
|
- font-size: 24rpx;
|
|
|
- color: #909399;
|
|
|
- margin-top: 8rpx;
|
|
|
- }
|
|
|
-
|
|
|
- .item-price {
|
|
|
- margin-top: 16rpx;
|
|
|
- font-size: 32rpx;
|
|
|
- color: #07c160;
|
|
|
- font-weight: bold;
|
|
|
- }
|
|
|
+ padding: 10rpx 12rpx 14rpx 0;
|
|
|
+ box-sizing: border-box;
|
|
|
}
|
|
|
|
|
|
- .item-right-actions {
|
|
|
+ .item-title-row {
|
|
|
display: flex;
|
|
|
- flex-direction: column;
|
|
|
- align-items: flex-end;
|
|
|
+ align-items: flex-start;
|
|
|
justify-content: space-between;
|
|
|
+ gap: 12rpx;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* demo .name:13px */
|
|
|
+ .item-name {
|
|
|
+ flex: 1 1 auto;
|
|
|
+ min-width: 0;
|
|
|
+ max-width: calc(100% - 72rpx);
|
|
|
+ font-size: clamp(22rpx, 3.4vw, 26rpx);
|
|
|
+ color: var(--c-text);
|
|
|
+ line-height: 1.35;
|
|
|
+ min-height: calc(2 * 1.35em);
|
|
|
+ display: -webkit-box;
|
|
|
+ -webkit-box-orient: vertical;
|
|
|
+ -webkit-line-clamp: 2;
|
|
|
+ overflow: hidden;
|
|
|
+ word-break: break-word;
|
|
|
+ }
|
|
|
+
|
|
|
+ .item-delete {
|
|
|
flex-shrink: 0;
|
|
|
- align-self: stretch;
|
|
|
- min-height: 160rpx;
|
|
|
- padding-left: 12rpx;
|
|
|
+ min-width: 64rpx;
|
|
|
+ height: 44rpx;
|
|
|
+ padding: 10rpx 16rpx 0 6rpx;
|
|
|
+ display: inline-flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: flex-end;
|
|
|
box-sizing: border-box;
|
|
|
}
|
|
|
|
|
|
- .item-delete {
|
|
|
- padding: 4rpx 0 0;
|
|
|
+ .item-delete-icon {
|
|
|
+ width: 36rpx;
|
|
|
+ height: 36rpx;
|
|
|
+ }
|
|
|
|
|
|
- text {
|
|
|
- font-size: 32rpx;
|
|
|
- }
|
|
|
+ /* demo .spec:11px */
|
|
|
+ .item-unit {
|
|
|
+ display: block;
|
|
|
+ margin: 6rpx 0 12rpx;
|
|
|
+ font-size: clamp(20rpx, 3vw, 22rpx);
|
|
|
+ color: var(--c-muted);
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ white-space: nowrap;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* demo .line:与首页一致,价格可省略 + 操作区固定列,避免窄屏重叠 */
|
|
|
+ .item-line {
|
|
|
+ margin-top: auto;
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: minmax(0, 1fr) auto;
|
|
|
+ align-items: center;
|
|
|
+ column-gap: 12rpx;
|
|
|
+ min-width: 0;
|
|
|
+ padding-bottom: 6rpx;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* demo .price:16px danger */
|
|
|
+ .item-price {
|
|
|
+ font-size: clamp(24rpx, 3.8vw, 32rpx);
|
|
|
+ color: var(--c-danger);
|
|
|
+ font-weight: 700;
|
|
|
+ min-width: 0;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ white-space: nowrap;
|
|
|
+ }
|
|
|
+
|
|
|
+ .line-actions {
|
|
|
+ flex-shrink: 0;
|
|
|
+ padding-right: 12rpx;
|
|
|
}
|
|
|
|
|
|
.quantity-control {
|
|
|
- display: flex;
|
|
|
+ display: inline-flex;
|
|
|
align-items: center;
|
|
|
- margin-top: auto;
|
|
|
+ gap: 10rpx;
|
|
|
+ min-width: 176rpx;
|
|
|
+ justify-content: space-between;
|
|
|
|
|
|
.qty-btn {
|
|
|
- width: 48rpx;
|
|
|
- height: 48rpx;
|
|
|
- border: 2rpx solid #e4e7ed;
|
|
|
- border-radius: 8rpx;
|
|
|
+ width: clamp(32rpx, 6.5vw, 42rpx);
|
|
|
+ height: clamp(32rpx, 6.5vw, 42rpx);
|
|
|
+ border: 1rpx solid #b9decf;
|
|
|
+ border-radius: 999rpx;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
- font-size: 28rpx;
|
|
|
- color: #303133;
|
|
|
+ color: var(--c-brand-green);
|
|
|
+ background: #ffffff;
|
|
|
+ box-sizing: border-box;
|
|
|
+ line-height: 1;
|
|
|
+ padding: 0;
|
|
|
|
|
|
&:active {
|
|
|
- background: #f5f5f5;
|
|
|
+ background: var(--c-brand-green-soft);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ .qty-symbol {
|
|
|
+ line-height: 1;
|
|
|
+ font-size: clamp(18rpx, 3.2vw, 24rpx);
|
|
|
+ font-weight: 700;
|
|
|
+ display: block;
|
|
|
+ transform: translateY(-1rpx);
|
|
|
+ }
|
|
|
+
|
|
|
.qty-num {
|
|
|
- min-width: 60rpx;
|
|
|
+ min-width: 56rpx;
|
|
|
+ max-width: 72rpx;
|
|
|
text-align: center;
|
|
|
- font-size: 28rpx;
|
|
|
- color: #303133;
|
|
|
+ font-size: clamp(18rpx, 3vw, 24rpx);
|
|
|
+ color: var(--c-text);
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+.cart-item--pressed {
|
|
|
+ opacity: 0.96;
|
|
|
+}
|
|
|
+
|
|
|
+.qty-btn--pressed {
|
|
|
+ opacity: 0.88;
|
|
|
+}
|
|
|
+
|
|
|
// 空状态(在 scroll-view 内,避免整页滚动)
|
|
|
.empty-state {
|
|
|
display: flex;
|
|
|
@@ -805,102 +963,126 @@ page {
|
|
|
justify-content: center;
|
|
|
border: none !important;
|
|
|
box-shadow: none !important;
|
|
|
- background: #07c160 !important;
|
|
|
- color: #ffffff !important;
|
|
|
- line-height: 1;
|
|
|
}
|
|
|
|
|
|
- .empty-btn::after {
|
|
|
+ .empty-btn:deep(.van-button) {
|
|
|
border: none !important;
|
|
|
+ box-shadow: none !important;
|
|
|
+ background: var(--c-brand-green) !important;
|
|
|
+ color: #ffffff !important;
|
|
|
+ border-color: var(--c-brand-green) !important;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// 底部结算栏
|
|
|
+// 底部结算栏(对齐 demo .bar / .total / .btn)
|
|
|
.checkout-bar {
|
|
|
position: fixed;
|
|
|
/* #ifdef H5 */
|
|
|
- bottom: var(--window-bottom, calc(50px + env(safe-area-inset-bottom, 0px)));
|
|
|
+ bottom: calc(var(--window-bottom, calc(50px + env(safe-area-inset-bottom, 0px))) + 44rpx);
|
|
|
z-index: 10010;
|
|
|
/* #endif */
|
|
|
/* #ifndef H5 */
|
|
|
- bottom: 0;
|
|
|
+ bottom: calc(100rpx + constant(safe-area-inset-bottom));
|
|
|
+ bottom: calc(100rpx + env(safe-area-inset-bottom, 0px));
|
|
|
z-index: 10010;
|
|
|
/* #endif */
|
|
|
- left: 0;
|
|
|
- right: 0;
|
|
|
- height: 100rpx;
|
|
|
+ left: 50%;
|
|
|
+ transform: translateX(-50%);
|
|
|
+ width: 100%;
|
|
|
+ max-width: 840rpx;
|
|
|
+ padding: 0 24rpx;
|
|
|
+ min-height: 96rpx;
|
|
|
box-sizing: border-box;
|
|
|
- background: #ffffff;
|
|
|
+ background: transparent;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
- justify-content: space-between;
|
|
|
- padding: 0 30rpx;
|
|
|
- padding-bottom: constant(safe-area-inset-bottom);
|
|
|
- padding-bottom: env(safe-area-inset-bottom);
|
|
|
- box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
|
|
|
+ justify-content: center;
|
|
|
+ padding-bottom: calc(6rpx + constant(safe-area-inset-bottom));
|
|
|
+ padding-bottom: calc(6rpx + env(safe-area-inset-bottom, 0px));
|
|
|
overflow: visible;
|
|
|
+}
|
|
|
+
|
|
|
+.checkout-inner {
|
|
|
+ min-height: 96rpx;
|
|
|
+ border-radius: 36rpx;
|
|
|
+ background: linear-gradient(145deg, #ffffff, #f5fbf8);
|
|
|
+ border: 1rpx solid #d9ebe3;
|
|
|
+ color: var(--c-text);
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ width: 100%;
|
|
|
+ padding: 12rpx;
|
|
|
+ box-shadow: 0 16rpx 36rpx rgba(10, 159, 109, 0.12);
|
|
|
+ box-sizing: border-box;
|
|
|
|
|
|
.bar-left {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
- gap: 20rpx;
|
|
|
+ gap: 16rpx;
|
|
|
min-width: 0;
|
|
|
+ flex: 1;
|
|
|
}
|
|
|
|
|
|
.select-all {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
gap: 12rpx;
|
|
|
- padding: 10rpx 8rpx;
|
|
|
+ padding: 8rpx 4rpx;
|
|
|
+ flex-shrink: 0;
|
|
|
.select-all-text {
|
|
|
- font-size: 26rpx;
|
|
|
- color: #606266;
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: var(--c-muted);
|
|
|
white-space: nowrap;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- .total-info {
|
|
|
- min-width: 0;
|
|
|
+ .total-block {
|
|
|
display: flex;
|
|
|
- align-items: center;
|
|
|
- gap: 6rpx;
|
|
|
- .total-label {
|
|
|
- font-size: 26rpx;
|
|
|
- color: #606266;
|
|
|
- line-height: 1;
|
|
|
- }
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: flex-start;
|
|
|
+ min-width: 0;
|
|
|
+ }
|
|
|
|
|
|
- .total-price {
|
|
|
- font-size: 40rpx;
|
|
|
- color: #07c160;
|
|
|
- font-weight: bold;
|
|
|
- line-height: 1;
|
|
|
- }
|
|
|
+ .total-muted {
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: var(--c-muted);
|
|
|
+ line-height: 1.3;
|
|
|
+ }
|
|
|
+
|
|
|
+ .total-strong {
|
|
|
+ display: block;
|
|
|
+ margin-top: 4rpx;
|
|
|
+ font-size: 40rpx;
|
|
|
+ color: var(--c-danger);
|
|
|
+ font-weight: 700;
|
|
|
+ line-height: 1.1;
|
|
|
}
|
|
|
|
|
|
.checkout-actions {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
+ flex-shrink: 0;
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
- .checkout-btn {
|
|
|
- padding: 0 50rpx;
|
|
|
- height: 72rpx;
|
|
|
- border-radius: 36rpx;
|
|
|
- display: flex;
|
|
|
- align-items: center;
|
|
|
- justify-content: center;
|
|
|
- font-size: 28rpx;
|
|
|
- font-weight: 500;
|
|
|
- border: none !important;
|
|
|
- box-shadow: none !important;
|
|
|
- background: #07c160 !important;
|
|
|
- color: #ffffff !important;
|
|
|
- line-height: 1;
|
|
|
- }
|
|
|
+/* 与首页底部去结算按钮保持一致 */
|
|
|
+.cart-settle {
|
|
|
+ flex-shrink: 0;
|
|
|
+ min-height: 72rpx;
|
|
|
+ padding: 0 32rpx;
|
|
|
+ border-radius: 28rpx;
|
|
|
+ background: linear-gradient(145deg, var(--c-barley-yellow), #e4b458);
|
|
|
+ color: #fff;
|
|
|
+ display: inline-flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ font-size: 28rpx;
|
|
|
+ font-weight: 700;
|
|
|
+ box-shadow: inset 0 -2rpx 0 rgba(0, 0, 0, 0.1);
|
|
|
|
|
|
- .checkout-btn::after {
|
|
|
- border: none !important;
|
|
|
- }
|
|
|
+ text {
|
|
|
+ color: #fff;
|
|
|
}
|
|
|
}
|
|
|
</style>
|