consumer模块完成度95%,优化安卓端界面和小程序测试
This commit is contained in:
@@ -3,30 +3,30 @@
|
||||
<view class="checkout-page">
|
||||
<scroll-view class="checkout-content" direction="vertical">
|
||||
<!-- 收货地址 -->
|
||||
<view class="address-section" @click="selectAddress">
|
||||
<view class="section-card address-section" @click="selectAddress">
|
||||
<view class="address-icon-wrapper">
|
||||
<text class="location-icon">📍</text>
|
||||
</view>
|
||||
<view v-if="selectedAddress" class="address-info">
|
||||
<view class="address-header">
|
||||
<text class="recipient">{{ selectedAddress!!.recipient_name }}</text>
|
||||
<text class="phone">{{ selectedAddress!!.phone }}</text>
|
||||
<view v-if="selectedAddress!!.is_default" class="default-tag">
|
||||
<text class="tag-text">默认</text>
|
||||
</view>
|
||||
</view>
|
||||
<text class="address-detail">{{ getFullAddress(selectedAddress!!) }}</text>
|
||||
<text class="recipient">{{ selectedAddress!!.recipient_name }}</text>
|
||||
<text class="phone">{{ selectedAddress!!.phone }}</text>
|
||||
<view v-if="selectedAddress!!.is_default" class="default-tag">
|
||||
<text class="tag-text">默认</text>
|
||||
</view>
|
||||
</view>
|
||||
<text class="address-detail">{{ getFullAddress(selectedAddress!!) }}</text>
|
||||
</view>
|
||||
<view v-else class="no-address">
|
||||
<text class="no-address-text">请选择收货地址</text>
|
||||
<text class="no-address-arrow">›</text>
|
||||
</view>
|
||||
<view class="address-arrow-wrapper">
|
||||
<text class="address-arrow">›</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 商品列表 (按店铺分组) -->
|
||||
<view class="products-section">
|
||||
<!-- 调试信息 -->
|
||||
<view v-if="checkoutItems.length > 0" class="debug-info">
|
||||
<text class="debug-text">共 {{ checkoutItems.length }} 件商品</text>
|
||||
</view>
|
||||
|
||||
<view class="section-card products-section">
|
||||
<view v-if="shopGroups.length > 0">
|
||||
<view v-for="group in shopGroups" :key="group.shopId" class="shop-group">
|
||||
<view class="shop-header">
|
||||
@@ -34,27 +34,25 @@
|
||||
<text class="shop-name">{{ group.shopName }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 商品列表 -->
|
||||
<view v-for="item in group.items" :key="item.id" class="product-item">
|
||||
<image class="product-image" :src="item.product_image" mode="aspectFill" />
|
||||
<view class="product-info">
|
||||
<text class="product-name">{{ item.product_name }}</text>
|
||||
<text v-if="item.sku_specifications" class="product-spec">{{ formatSpecs(item.sku_specifications) }}</text>
|
||||
<view class="product-bottom">
|
||||
<view class="product-name-row">
|
||||
<text class="product-name">{{ item.product_name }}</text>
|
||||
<text class="product-price">¥{{ item.price }}</text>
|
||||
</view>
|
||||
<view class="product-spec-row">
|
||||
<text v-if="item.sku_specifications" class="product-spec">{{ formatSpecs(item.sku_specifications) }}</text>
|
||||
<text class="product-quantity">×{{ item.quantity }}</text>
|
||||
</view>
|
||||
<!-- 商品小计移至图片右侧 -->
|
||||
<view class="item-subtotal-row">
|
||||
<text class="item-subtotal-label">小计:</text>
|
||||
<text class="item-subtotal-price">¥{{ (item.price * item.quantity).toFixed(2) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 店铺小计 -->
|
||||
<view class="shop-subtotal">
|
||||
<text class="subtotal-label">配送方式</text>
|
||||
<text class="subtotal-value">快递 免邮</text>
|
||||
</view>
|
||||
<view class="shop-subtotal">
|
||||
<text class="subtotal-text">小计: </text>
|
||||
<text class="subtotal-price">¥{{ getGroupTotal(group) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else class="no-products">
|
||||
@@ -63,70 +61,76 @@
|
||||
</view>
|
||||
|
||||
<!-- 配送方式 -->
|
||||
<view class="delivery-section">
|
||||
<text class="section-title">配送方式</text>
|
||||
<view class="delivery-options">
|
||||
<view v-for="option in deliveryOptions"
|
||||
:key="option.id"
|
||||
:class="['delivery-option', { selected: selectedDelivery === option.id }]"
|
||||
@click="selectDelivery(option)">
|
||||
<text class="option-name">{{ option.name }}</text>
|
||||
<text class="option-price">¥{{ option.price }}</text>
|
||||
<text v-if="selectedDelivery === option.id" class="option-selected">✓</text>
|
||||
<view class="section-card delivery-section">
|
||||
<view class="delivery-row">
|
||||
<text class="section-title">配送方式</text>
|
||||
<view class="delivery-selector">
|
||||
<view v-for="option in deliveryOptions"
|
||||
:key="option.id"
|
||||
:class="['delivery-pill', { selected: selectedDelivery === option.id }]"
|
||||
@click="selectDelivery(option)">
|
||||
<text class="pill-name">{{ option.name }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="delivery-detail" v-if="selectedDelivery">
|
||||
<text class="detail-desc">{{ deliveryOptions.find(opt => opt.id === selectedDelivery)?.description }}</text>
|
||||
<text class="detail-price">费用: ¥{{ deliveryOptions.find(opt => opt.id === selectedDelivery)?.price.toFixed(2) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 优惠券 -->
|
||||
<view class="coupon-section" @click="selectCoupon">
|
||||
<text class="section-title">优惠券</text>
|
||||
<view class="coupon-info">
|
||||
<text v-if="selectedCoupon != null" class="coupon-selected">{{ selectedCoupon.template?.name ?? '已选择优惠券 (¥' + (selectedCoupon.template?.discount_value ?? 0) + ')' }}</text>
|
||||
<text v-else class="coupon-placeholder">选择优惠券</text>
|
||||
<text class="coupon-arrow">›</text>
|
||||
<view class="section-card coupon-section" @click="selectCoupon">
|
||||
<view class="coupon-row">
|
||||
<text class="section-title">优惠券</text>
|
||||
<view class="coupon-right-content">
|
||||
<text v-if="selectedCoupon != null" class="coupon-selected-name">{{ selectedCoupon.template?.name ?? '已选择优惠券' }}</text>
|
||||
<text v-else class="coupon-placeholder">暂无可用优惠券</text>
|
||||
<text class="arrow-icon">›</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 买家留言 -->
|
||||
<view class="remark-section">
|
||||
<text class="section-title">买家留言</text>
|
||||
<textarea class="remark-input"
|
||||
v-model="remark"
|
||||
placeholder="选填,请先和商家协商一致"
|
||||
maxlength="100" />
|
||||
<view class="section-card remark-section">
|
||||
<view class="remark-row">
|
||||
<text class="section-title">买家留言</text>
|
||||
<input class="remark-input-compact"
|
||||
v-model="remark"
|
||||
placeholder="选填,给商家留言"
|
||||
maxlength="100" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 价格明细 -->
|
||||
<view class="price-section">
|
||||
<text class="section-title">价格明细</text>
|
||||
<view class="price-detail">
|
||||
<view class="price-row">
|
||||
<text class="price-label">商品总价</text>
|
||||
<text class="price-value">¥{{ totalAmount.toFixed(2) }}</text>
|
||||
<view class="section-card price-section">
|
||||
<view class="price-grid">
|
||||
<view class="price-item-inline">
|
||||
<text class="price-item-label">商品</text>
|
||||
<text class="price-item-value">¥{{ totalAmount.toFixed(2) }}</text>
|
||||
</view>
|
||||
<view class="price-row">
|
||||
<text class="price-label">运费</text>
|
||||
<text class="price-value">+¥{{ deliveryFee.toFixed(2) }}</text>
|
||||
<view class="price-item-inline">
|
||||
<text class="price-item-label">运费</text>
|
||||
<text class="price-item-value">+¥{{ deliveryFee.toFixed(2) }}</text>
|
||||
</view>
|
||||
<view v-if="discountAmount > 0" class="price-row">
|
||||
<text class="price-label">优惠减免</text>
|
||||
<text class="price-value discount">-¥{{ discountAmount.toFixed(2) }}</text>
|
||||
</view>
|
||||
<view class="price-row total">
|
||||
<text class="price-label">应付金额</text>
|
||||
<text class="price-value total-price">¥{{ actualAmount.toFixed(2) }}</text>
|
||||
<view v-if="discountAmount > 0" class="price-item-inline">
|
||||
<text class="price-item-label">优惠</text>
|
||||
<text class="price-item-value discount-text">-¥{{ discountAmount.toFixed(2) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="safe-area-bottom"></view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 底部结算栏 -->
|
||||
<view class="bottom-bar">
|
||||
<view class="price-summary">
|
||||
<text class="summary-label">合计:</text>
|
||||
<text class="summary-price">¥{{ actualAmount.toFixed(2) }}</text>
|
||||
<view class="footer-action-bar">
|
||||
<view class="footer-left">
|
||||
<text class="footer-total-label">合计:</text>
|
||||
<text class="footer-currency">¥</text>
|
||||
<text class="footer-price">{{ actualAmount.toFixed(2) }}</text>
|
||||
</view>
|
||||
<button class="submit-btn" @click="submitOrder">提交订单</button>
|
||||
<button class="footer-submit-btn" @click="submitOrder">提交订单</button>
|
||||
</view>
|
||||
|
||||
<!-- 地址选择弹窗 -->
|
||||
@@ -414,10 +418,10 @@ function getObjectKeys(obj: object): string[] {
|
||||
const checkoutItems = ref<Array<CheckoutItemType>>([])
|
||||
const selectedAddress = ref<AddressItem | null>(null)
|
||||
const deliveryOptions = ref<Array<DeliveryOptionType>>([
|
||||
{ id: 'standard', name: '快递配送', price: 8.00, description: '1-3天送达' },
|
||||
{ id: 'express', name: '加急配送', price: 15.00, description: '当天送达' }
|
||||
{ id: 'express', name: '物流快递', price: 8.00, description: '普通快递配送' },
|
||||
{ id: 'local', name: '同城配送', price: 15.00, description: '同城极速上门' }
|
||||
])
|
||||
const selectedDelivery = ref<string>('standard')
|
||||
const selectedDelivery = ref<string>('express')
|
||||
const selectedCoupon = ref<UserCouponType | null>(null)
|
||||
const remark = ref<string>('')
|
||||
const showAddressPopup = ref<boolean>(false)
|
||||
@@ -1427,15 +1431,17 @@ const goToLogin = () => {
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: #f5f5f5;
|
||||
background-color: #f8f8f8;
|
||||
overflow: hidden;
|
||||
align-items: center; /* PC端居中显示 */
|
||||
}
|
||||
/* 顶部栏 */
|
||||
.checkout-header {
|
||||
background-color: #ffffff;
|
||||
padding: 15px;
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
flex-shrink: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
@@ -1448,83 +1454,474 @@ const goToLogin = () => {
|
||||
.checkout-content {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
max-width: 800px; /* 限制PC端内容宽度 */
|
||||
min-height: 0;
|
||||
background-color: #f5f5f5;
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
|
||||
/* 卡片容器 */
|
||||
.no-products {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 30px 0;
|
||||
}
|
||||
.no-products-text { font-size: 14px; color: #999999; }
|
||||
|
||||
.section-card {
|
||||
background-color: #ffffff;
|
||||
margin: 12px;
|
||||
padding: 18px;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.02);
|
||||
}
|
||||
|
||||
/* 自适应适配 */
|
||||
@media screen and (min-width: 768px) {
|
||||
.section-card {
|
||||
margin: 16px 0;
|
||||
}
|
||||
.delivery-options-grid {
|
||||
display: flex;
|
||||
flex-direction: row !important;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
}
|
||||
.delivery-card {
|
||||
flex: 1;
|
||||
min-width: 280px;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
/* 底部结算栏在大屏居中并限宽 */
|
||||
.footer-action-bar {
|
||||
max-width: 800px;
|
||||
left: 50% !important;
|
||||
right: auto !important;
|
||||
transform: translateX(-50%);
|
||||
border-radius: 16px 16px 0 0;
|
||||
box-shadow: 0 -4px 16px rgba(0,0,0,0.05);
|
||||
}
|
||||
}
|
||||
|
||||
.address-section {
|
||||
margin-top: 12px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 16px;
|
||||
position: relative;
|
||||
background-color: #ffffff;
|
||||
margin-bottom: 10px;
|
||||
padding: 20px 15px;
|
||||
}
|
||||
|
||||
.address-icon-wrapper {
|
||||
margin-right: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.address-info { flex: 1; }
|
||||
.address-header { display: flex; align-items: center; margin-bottom: 10px; }
|
||||
.recipient { font-size: 16px; font-weight: bold; color: #333333; margin-right: 15px; }
|
||||
.phone { font-size: 14px; color: #666666; margin-right: 10px; }
|
||||
.default-tag { background-color: #ff4757; padding: 2px 8px; border-radius: 10px; }
|
||||
.tag-text { color: #ffffff; font-size: 12px; }
|
||||
.address-detail { font-size: 14px; color: #333333; line-height: 1.4; }
|
||||
.no-address { flex: 1; display: flex; justify-content: space-between; align-items: center; }
|
||||
.location-icon {
|
||||
font-size: 24px;
|
||||
color: #ff5000;
|
||||
}
|
||||
|
||||
.address-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.address-header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.recipient { font-size: 17px; font-weight: bold; color: #333333; margin-right: 12px; }
|
||||
.phone { font-size: 14px; color: #666666; margin-right: 8px; }
|
||||
.default-tag { background-color: #fff0eb; border: 0.5px solid #ff5000; padding: 0 6px; border-radius: 4px; }
|
||||
.tag-text { color: #ff5000; font-size: 11px; }
|
||||
|
||||
.address-detail {
|
||||
font-size: 13px;
|
||||
color: #666;
|
||||
line-height: 1.5;
|
||||
lines: 2;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.address-arrow-wrapper {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.address-arrow {
|
||||
color: #ccc;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.no-address { flex: 1; display: flex; align-items: center; }
|
||||
.no-address-text { font-size: 16px; color: #999999; }
|
||||
.no-address-arrow { color: #999999; font-size: 18px; }
|
||||
|
||||
.products-section { background-color: #ffffff; margin-bottom: 10px; padding: 0 15px; }
|
||||
.debug-info { background-color: #f8f9fa; padding: 10px 15px; border-bottom: 1px solid #e5e5e5; margin-bottom: 10px; }
|
||||
.debug-text { font-size: 12px; color: #666; text-align: center; }
|
||||
.shop-group { background-color: #fff; margin: 10px 0; border-radius: 12px; padding: 10px; }
|
||||
.shop-header { display: flex; flex-direction: row; align-items: center; padding-bottom: 10px; border-bottom: 1px solid #f0f0f0; }
|
||||
.shop-icon { font-size: 18px; margin-right: 8px; }
|
||||
.shop-name { font-size: 16px; font-weight: bold; color: #333; }
|
||||
.shop-subtotal { display: flex; justify-content: flex-end; align-items: center; padding-top: 10px; margin-top: 5px; border-top: 1px dashed #f0f0f0; font-size: 14px; }
|
||||
.subtotal-label { color: #666; margin-right: 10px; }
|
||||
.subtotal-value { color: #333; }
|
||||
.subtotal-text { color: #333; margin-right: 5px; }
|
||||
.subtotal-price { color: #ff4757; font-weight: bold; font-size: 16px; }
|
||||
.products-section { padding: 0; }
|
||||
.debug-info { padding: 10px 15px; border-bottom: 1px solid #f5f5f5; margin-bottom: 10px; }
|
||||
.debug-text { font-size: 12px; color: #999; text-align: center; }
|
||||
.shop-group { background-color: #fff; padding: 0; }
|
||||
.shop-header { display: flex; flex-direction: row; align-items: center; padding: 5px 0 12px; }
|
||||
.shop-icon { font-size: 17px; margin-right: 6px; }
|
||||
.shop-name { font-size: 15px; font-weight: bold; color: #333; }
|
||||
.shop-subtotal { display: flex; justify-content: flex-end; align-items: center; padding: 12px 0 5px; margin-top: 5px; border-top: 1px dashed #f0f0f0; }
|
||||
.subtotal-label { color: #888; margin-right: 8px; font-size: 13px; }
|
||||
.subtotal-value { color: #666; font-size: 13px; }
|
||||
.subtotal-text { color: #333; margin-right: 5px; font-size: 14px; }
|
||||
.subtotal-price { color: #ff5000; font-weight: bold; font-size: 16px; }
|
||||
|
||||
.product-item { display: flex; padding: 15px 0; border-bottom: 1px solid #f5f5f5; }
|
||||
.product-item:last-child { border-bottom: none; }
|
||||
.product-image { width: 80px; height: 80px; border-radius: 5px; margin-right: 15px; }
|
||||
.product-item { display: flex; flex-direction: row; padding: 12px 0; }
|
||||
.product-image { width: 85px; height: 85px; border-radius: 8px; margin-right: 12px; background-color: #f8f8f8; }
|
||||
.product-info { flex: 1; display: flex; flex-direction: column; justify-content: space-between; }
|
||||
.product-name { font-size: 14px; color: #333333; line-height: 1.4; margin-bottom: 5px; lines: 2; text-overflow: ellipsis; overflow: hidden; }
|
||||
.product-spec { font-size: 12px; color: #999999; margin-bottom: 10px; }
|
||||
.product-bottom { display: flex; justify-content: space-between; align-items: center; }
|
||||
.product-price { font-size: 16px; color: #ff4757; font-weight: bold; }
|
||||
.product-quantity { font-size: 14px; color: #666666; }
|
||||
.product-name-row { display: flex; flex-direction: row; justify-content: space-between; align-items: flex-start; }
|
||||
.product-name { flex: 1; font-size: 14px; color: #333333; line-height: 1.4; lines: 2; text-overflow: ellipsis; overflow: hidden; margin-right: 12px; }
|
||||
.product-price { font-size: 15px; color: #333; font-weight: normal; }
|
||||
.product-spec-row { display: flex; flex-direction: row; justify-content: space-between; align-items: center; margin-bottom: 4px; }
|
||||
.product-spec { font-size: 12px; color: #999999; background-color: #f7f7f7; padding: 2px 6px; border-radius: 4px; }
|
||||
.product-quantity { font-size: 12px; color: #999999; }
|
||||
|
||||
.delivery-section, .coupon-section, .remark-section, .price-section { background-color: #ffffff; margin-bottom: 10px; padding: 15px; }
|
||||
.section-title { font-size: 16px; font-weight: bold; color: #333333; margin-bottom: 15px; }
|
||||
/* 配送方式重构 */
|
||||
.delivery-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.delivery-options { display: flex; flex-direction: column; }
|
||||
.delivery-option { display: flex; align-items: center; justify-content: space-between; padding: 15px; border: 1px solid #e5e5e5; border-radius: 8px; margin-bottom: 10px; }
|
||||
.delivery-option:last-child { margin-bottom: 0; }
|
||||
.delivery-option.selected { border-color: #007aff; background-color: #f0f8ff; }
|
||||
.option-name { font-size: 14px; color: #333333; }
|
||||
.option-price { font-size: 14px; color: #ff4757; font-weight: bold; }
|
||||
.option-selected { color: #007aff; font-size: 16px; }
|
||||
.delivery-selector {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.coupon-info { display: flex; align-items: center; justify-content: space-between; padding: 15px; border: 1px solid #e5e5e5; border-radius: 8px; }
|
||||
.coupon-selected { font-size: 14px; color: #007aff; }
|
||||
.coupon-placeholder { font-size: 14px; color: #999999; }
|
||||
.coupon-arrow { color: #999999; font-size: 16px; }
|
||||
.delivery-pill {
|
||||
padding: 4px 12px;
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 16px;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.remark-input { width: 100%; min-height: 40px; padding: 10px; border: 1px solid #e5e5e5; border-radius: 8px; font-size: 14px; color: #333333; }
|
||||
.delivery-pill.selected {
|
||||
background-color: #fff9f6;
|
||||
border-color: #ff5000;
|
||||
}
|
||||
|
||||
.price-detail { padding: 15px; background-color: #f8f9fa; border-radius: 8px; }
|
||||
.price-row { display: flex; justify-content: space-between; align-items: center; padding: 8px 0; }
|
||||
.price-row.total { border-top: 1px solid #e5e5e5; margin-top: 8px; padding-top: 15px; }
|
||||
.price-label { font-size: 14px; color: #666666; }
|
||||
.price-value { font-size: 14px; color: #333333; }
|
||||
.price-value.discount { color: #4caf50; }
|
||||
.price-value.total-price { font-size: 18px; color: #ff4757; font-weight: bold; }
|
||||
.pill-name {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.bottom-bar { background-color: #ffffff; padding: 15px; border-top: 1px solid #e5e5e5; display: flex; align-items: center; justify-content: space-between; }
|
||||
.price-summary { display: flex; align-items: flex-end; }
|
||||
.summary-label { font-size: 14px; color: #333333; margin-right: 5px; }
|
||||
.summary-price { font-size: 20px; color: #ff4757; font-weight: bold; }
|
||||
.submit-btn { background-color: #007aff; color: #ffffff; padding: 0 40px; height: 45px; border-radius: 22.5px; font-size: 16px; font-weight: bold; border: none; }
|
||||
.delivery-pill.selected .pill-name {
|
||||
color: #ff5000;
|
||||
}
|
||||
|
||||
.delivery-detail {
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
padding-top: 8px;
|
||||
border-top: 0.5px solid #f9f9f9;
|
||||
}
|
||||
|
||||
.detail-desc {
|
||||
font-size: 11px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.detail-price {
|
||||
font-size: 11px;
|
||||
color: #ff5000;
|
||||
}
|
||||
|
||||
/* 优惠券重构 */
|
||||
.coupon-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.coupon-right-content {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* 留言重构 */
|
||||
.remark-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.remark-input-compact {
|
||||
flex: 1;
|
||||
margin-left: 15px;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
/* 价格明细横向排列 */
|
||||
.price-grid {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.price-item-inline {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.price-item-label {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.price-item-value {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.item-subtotal-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
margin-top: 4px;
|
||||
}
|
||||
.item-subtotal-label {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
margin-right: 4px;
|
||||
}
|
||||
.item-subtotal-price {
|
||||
font-size: 14px;
|
||||
color: #ff5000;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* 配送方式网格 */
|
||||
.delivery-options-grid {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.delivery-card {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 15px;
|
||||
border: 1px solid #f0f0f0;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 10px;
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
.delivery-card.selected {
|
||||
border-color: #ff5000;
|
||||
background-color: #fff9f6;
|
||||
}
|
||||
|
||||
.option-main {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.option-name {
|
||||
font-size: 15px;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
margin-bottom: 4px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.option-desc {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.option-side {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.option-price {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.select-icon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
background-color: #ff5000;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.check-mark {
|
||||
color: #ffffff;
|
||||
font-size: 12px;
|
||||
margin-top: -1px;
|
||||
}
|
||||
|
||||
/* 优惠券样式重构 */
|
||||
.coupon-content {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.coupon-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.coupon-tag {
|
||||
background-color: #ff5000;
|
||||
color: #fff;
|
||||
font-size: 10px;
|
||||
padding: 1px 4px;
|
||||
border-radius: 2px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.coupon-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.coupon-selected-name {
|
||||
font-size: 14px;
|
||||
color: #ff5000;
|
||||
}
|
||||
|
||||
.coupon-placeholder {
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.arrow-icon {
|
||||
font-size: 18px;
|
||||
color: #ccc;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
/* 留言输入 */
|
||||
.remark-input-new {
|
||||
width: 100%;
|
||||
background-color: #f9f9f9;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
font-size: 14px;
|
||||
min-height: 48px;
|
||||
}
|
||||
|
||||
/* 价格明细列表 */
|
||||
.price-list {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.price-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 6px 0;
|
||||
}
|
||||
|
||||
.price-item-label {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.price-item-value {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.discount-text {
|
||||
color: #ff5000;
|
||||
}
|
||||
|
||||
.section-title { font-size: 15px; font-weight: bold; color: #333333; }
|
||||
|
||||
/* 底部操作栏 */
|
||||
.footer-action-bar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 60px;
|
||||
background-color: #ffffff;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 16px;
|
||||
padding-bottom: constant(safe-area-inset-bottom);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
border-top: 1px solid #f0f0f0;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.footer-left {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.footer-total-label {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.footer-currency {
|
||||
font-size: 14px;
|
||||
color: #ff5000;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.footer-price {
|
||||
font-size: 22px;
|
||||
color: #ff5000;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.footer-submit-btn {
|
||||
background-color: #ff5000;
|
||||
color: #ffffff;
|
||||
padding: 0 28px;
|
||||
height: 42px;
|
||||
line-height: 42px;
|
||||
border-radius: 21px;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
border: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.safe-area-bottom {
|
||||
height: 100px; /* 留出底部操作栏和安全区的空间 */
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 弹窗样式 */
|
||||
.address-popup-mask, .address-form-mask, .confirm-popup-mask { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0, 0, 0, 0.5); z-index: 9998; }
|
||||
|
||||
@@ -72,5 +72,48 @@
|
||||
* **日志记录**:接入 **Sentry** 或 **阿里云日志服务**,记录生产环境中的所有前端报错(尤其是支付环节)。
|
||||
* **压力测试**:上线前需进行接口压力测试,确保在促销活动期间数据库连接数不会爆满。
|
||||
|
||||
---
|
||||
|
||||
## 6. 无真机调试指南 (iOS & 鸿蒙调试方案)
|
||||
|
||||
针对您目前没有 iOS 和鸿蒙(HarmonyOS Next)实机的情况,请务必执行以下**模拟调试流程**,以确保上线后 90% 以上的兼容性。
|
||||
|
||||
### A. iOS (苹果) 远程与模拟调试
|
||||
1. **微信开发者工具“模拟器” (最简便)**:
|
||||
* 在工具底部左侧选择 `iPhone 13/14 Pro`。
|
||||
* **重点检查**: 底部 Tabbar 是否被 iPhone 的 Home Indicator (底部黑条) 遮挡。如果是,请在 CSS 中使用 `padding-bottom: constant(safe-area-inset-bottom); padding-bottom: env(safe-area-inset-bottom);`。
|
||||
2. **Chrome 浏览器 iOS 仿真 (针对 H5 端)**:
|
||||
* 将项目运行为 H5,开启 Chrome Console (F12)。
|
||||
* 选择 `iPhone` 设备仿真,手动测试 `v-if` 在不同屏幕高度下的布局抖动。
|
||||
3. **免费云真机调试 (推荐)**:
|
||||
* **腾讯云 WeTest / 阿里云移动测试**: 注册后通常有免费试用额度(如 30-60 分钟)。
|
||||
* **操作**: 上传打包好的 APK (Android) 或在微信内测环境下选择 iOS 云真机,远程控制真机屏幕进行滑屏测试。
|
||||
|
||||
### B. 鸿蒙 (HarmonyOS Next) 深度兼容
|
||||
由于鸿蒙系统现在进入了“纯血”阶段,与 Android 差异变大:
|
||||
1. **华为开发者联盟“云调试” (官方推荐)**:
|
||||
* **访问**: [华为远程真机调试](https://developer.huawei.com/consumer/cn/service/jsservicestu/appdebug.html)。
|
||||
* **步骤**:
|
||||
1. 注册并完成实名认证。
|
||||
2. 选择“远程真机”。
|
||||
3. 申请免费使用的 **Mate 60 / Pura 70 (HarmonyOS Next 预览版)**。
|
||||
* **调试重点**: 鸿蒙系统对 Webview 的内核限制较多,务必通过云真机测试“结算页”和“支付弹窗”是否能正常拉起。
|
||||
2. **DevEco Studio 远程模拟器**:
|
||||
* 安装华为官开发的 IDE `DevEco Studio`。
|
||||
* 使用内置的 `Device Manager` 启动 `Remote Emulator`。这比本地模拟器对电脑配置要求低,且渲染效果最接近真机。
|
||||
|
||||
### C. 关键代码兼容性“避坑”建议 (无真机必做)
|
||||
* **日期处理**: 严禁使用 `new Date("2024-03-10")`,在 iOS 上会返回 `Invalid Date`。**必须**统一替换为 `new Date("2024/03/10")` 或 `new Date(2024, 2, 10)`。
|
||||
* **图片显示**: iOS 微信小程序对 WebP 格式支持有限,生产环境生产建议优先使用 **JPG/PNG** 或确保 CDN 开启了自适应转换。
|
||||
* **输入框**: iOS 下 `input` 的 `focus` 自动拉起键盘可能会造成页面整体上移或错位,务必通过云真机确认“下单备注”等输入区域。
|
||||
|
||||
---
|
||||
## 7. 微信小程序上线必改项 (Final Checklist)
|
||||
|
||||
1. **正式 AppID**: 修改 `manifest.json` 中的 `mp-weixin.appid` 为正式 ID。
|
||||
2. **域名白名单**: 在微信后台添加 Supabase API 域名、图片 CDN 域名、物流查询域名。
|
||||
3. **用户隐私政策**: 获取头像、位置等接口前,必须在微信后台配置隐私协议,且在 App 内部提供可见入口。
|
||||
4. **打包优化**: 检查 `unpackage/dist/build/mp-weixin` 的大小,如超过 2MB,必须启用“分包加载”。
|
||||
|
||||
---
|
||||
*生成日期:2026-03-10*
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
<view class="shop-header" @click="goToShop">
|
||||
<text class="shop-icon">🏪</text>
|
||||
<text class="shop-name">{{ shopName }}</text>
|
||||
<text class="arrow-right">></text>
|
||||
<text class="arrow-right">›</text>
|
||||
</view>
|
||||
<view v-for="item in orderItems" :key="item.id" class="product-item" @click="goToProduct(item.product_id)">
|
||||
<image :src="item.image_url != null && item.image_url != '' ? item.image_url : '/static/default-product.png'" class="product-image" mode="aspectFill"/>
|
||||
|
||||
@@ -16,12 +16,14 @@
|
||||
<view class="product-info">
|
||||
<view class="price-section">
|
||||
<text class="current-price">¥{{ product.price }}</text>
|
||||
<text v-if="memberPrice > 0 && memberPrice < product.price" class="member-price-tag">会员价 ¥{{ memberPrice }}</text>
|
||||
<!-- 会员价功能已禁用 -->
|
||||
<!-- <text v-if="memberPrice > 0 && memberPrice < product.price" class="member-price-tag">会员价 ¥{{ memberPrice }}</text> -->
|
||||
<text v-if="product.original_price" class="original-price">¥{{ product.original_price }}</text>
|
||||
</view>
|
||||
<view v-if="memberDiscount > 0" class="member-discount-row">
|
||||
<!-- 会员专享折扣功能已禁用 -->
|
||||
<!-- <view v-if="memberDiscount > 0" class="member-discount-row">
|
||||
<text class="member-discount-text">会员专享 {{ memberDiscount }}折优惠</text>
|
||||
</view>
|
||||
</view> -->
|
||||
<text class="product-name">{{ product.name }}</text>
|
||||
<text class="sales-info">已售{{ product.sales }}件 · 库存{{ product.stock }}件</text>
|
||||
</view>
|
||||
@@ -69,7 +71,7 @@
|
||||
<view class="detail-cell spec-section" @click="showSpecModal" v-if="productSkus.length > 0">
|
||||
<text class="cell-label">规格</text>
|
||||
<view class="cell-content">
|
||||
<text class="spec-selected">{{ selectedSpec ?? '请选择规格' }}</text>
|
||||
<text class="spec-selected">{{ selectedSpec != '' ? selectedSpec : '请选择规格' }}</text>
|
||||
</view>
|
||||
<text class="cell-arrow">❯</text>
|
||||
</view>
|
||||
@@ -135,23 +137,54 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 规格选择弹窗 -->
|
||||
<!-- 规格选择弹窗 (京东风格) -->
|
||||
<view v-if="showSpec" class="spec-modal" @click="hideSpecModal">
|
||||
<view class="spec-content" @click.stop>
|
||||
<view class="spec-header">
|
||||
<text class="spec-title">选择规格</text>
|
||||
<text class="close-btn" @click="hideSpecModal">×</text>
|
||||
<view class="spec-header-jd">
|
||||
<image :src="getSelectedSkuImage()" class="spec-product-img" mode="aspectFill" />
|
||||
<view class="spec-info-jd">
|
||||
<view class="spec-price-row">
|
||||
<text class="price-symbol">¥</text>
|
||||
<text class="price-value">{{ getSelectedSkuPrice() }}</text>
|
||||
</view>
|
||||
<text class="spec-stock-jd">库存: {{ getSelectedSkuStock() }}件</text>
|
||||
<text class="spec-choosed-jd">已选: {{ selectedSpec != '' ? selectedSpec : '请选择规格' }}</text>
|
||||
</view>
|
||||
<text class="close-btn-jd" @click="hideSpecModal">×</text>
|
||||
</view>
|
||||
<scroll-view class="spec-list" direction="vertical">
|
||||
<view v-for="sku in productSkus" :key="sku.id"
|
||||
class="spec-item"
|
||||
:class="{ active: selectedSkuId === sku.id }"
|
||||
@click="selectSku(sku)">
|
||||
<text class="spec-name">{{ getSkuSpecText(sku) }}</text>
|
||||
<text class="spec-price">¥{{ sku.price }}</text>
|
||||
<text class="spec-stock">库存{{ sku.stock }}</text>
|
||||
|
||||
<scroll-view class="spec-list-jd" scroll-y="true">
|
||||
<view class="spec-group">
|
||||
<text class="group-title">规格</text>
|
||||
<view class="group-tags">
|
||||
<view v-for="sku in productSkus" :key="sku.id"
|
||||
class="spec-tag"
|
||||
:class="{ active: selectedSkuId === sku.id }"
|
||||
@click="selectSku(sku)">
|
||||
<text class="tag-text">{{ getSkuSpecText(sku) }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 数量选择移入弹窗内容 -->
|
||||
<view class="spec-quantity-row">
|
||||
<text class="group-title">数量</text>
|
||||
<view class="quantity-selector-jd">
|
||||
<view class="q-btn" @click="decreaseQuantity">
|
||||
<text class="q-btn-text">-</text>
|
||||
</view>
|
||||
<input class="q-input" type="number" :value="quantity.toString()" @input="validateQuantity" />
|
||||
<view class="q-btn" @click="increaseQuantity">
|
||||
<text class="q-btn-text">+</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<view class="spec-footer-jd">
|
||||
<button class="footer-btn cart" @click="addToCart">加入购物车</button>
|
||||
<button class="footer-btn buy" @click="buyNow">立即购买</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -692,7 +725,33 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
uni.hideLoading()
|
||||
},
|
||||
|
||||
getSelectedSkuImage(): string {
|
||||
if (this.selectedSkuId != '') {
|
||||
const sku = this.productSkus.find(s => s.id === this.selectedSkuId)
|
||||
if (sku != null && sku.image_url != null && sku.image_url != '') {
|
||||
return sku.image_url as string
|
||||
}
|
||||
}
|
||||
return this.product.images.length > 0 ? this.product.images[0] : '/static/default-product.png'
|
||||
},
|
||||
|
||||
getSelectedSkuPrice(): string {
|
||||
if (this.selectedSkuId != '') {
|
||||
const sku = this.productSkus.find(s => s.id === this.selectedSkuId)
|
||||
if (sku != null) return sku.price.toFixed(2)
|
||||
}
|
||||
return this.product.price.toFixed(2)
|
||||
},
|
||||
|
||||
getSelectedSkuStock(): number {
|
||||
if (this.selectedSkuId != '') {
|
||||
const sku = this.productSkus.find(s => s.id === this.selectedSkuId)
|
||||
this.showSpecModal()
|
||||
if (sku != null) return sku.stock
|
||||
}
|
||||
return this.product.stock
|
||||
if (success === true) {
|
||||
uni.showToast({ title: '领取成功', icon: 'success' })
|
||||
} else {
|
||||
@@ -723,7 +782,6 @@ export default {
|
||||
selectSku(sku: ProductSkuType) {
|
||||
this.selectedSkuId = sku.id
|
||||
this.selectedSpec = this.getSkuSpecText(sku)
|
||||
this.hideSpecModal()
|
||||
},
|
||||
|
||||
getSkuSpecText(sku: ProductSkuType): string {
|
||||
@@ -746,6 +804,7 @@ export default {
|
||||
|
||||
async addToCart() {
|
||||
if (this.productSkus.length > 0 && (this.selectedSkuId == null || this.selectedSkuId === '')) {
|
||||
this.showSpecModal()
|
||||
uni.showToast({
|
||||
title: '请选择规格',
|
||||
icon: 'none'
|
||||
@@ -766,6 +825,7 @@ export default {
|
||||
|
||||
if (success === true) {
|
||||
uni.showToast({ title: '已添加到购物车', icon: 'success' })
|
||||
this.hideSpecModal()
|
||||
} else {
|
||||
console.error('添加购物车返回失败')
|
||||
uni.showToast({ title: '添加失败,请登录重试', icon: 'none' })
|
||||
@@ -779,6 +839,7 @@ export default {
|
||||
|
||||
buyNow() {
|
||||
if (this.productSkus.length > 0 && (this.selectedSkuId == null || this.selectedSkuId === '')) {
|
||||
this.showSpecModal()
|
||||
uni.showToast({
|
||||
title: '请选择规格',
|
||||
icon: 'none'
|
||||
@@ -1375,70 +1436,200 @@ export default {
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
background-color: rgba(0, 0, 0, 0.6);
|
||||
display: flex;
|
||||
justify-content: flex-end; /* UVUE 推荐用 flex 布局对齐 */
|
||||
justify-content: flex-end;
|
||||
flex-direction: column;
|
||||
z-index: 999;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.spec-content {
|
||||
background-color: #fff;
|
||||
width: 100%;
|
||||
border-radius: 20rpx 20rpx 0 0;
|
||||
border-radius: 24rpx 24rpx 0 0;
|
||||
padding: 30rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-height: 1000rpx;
|
||||
max-height: 80vh;
|
||||
}
|
||||
|
||||
.spec-header {
|
||||
.spec-header-jd {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 30rpx;
|
||||
padding-bottom: 20rpx;
|
||||
border-bottom: 1rpx solid #eee;
|
||||
flex-direction: row;
|
||||
position: relative;
|
||||
padding-bottom: 30rpx;
|
||||
border-bottom: 1rpx solid #f2f2f2;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.spec-title {
|
||||
font-size: 32rpx;
|
||||
.spec-product-img {
|
||||
width: 180rpx;
|
||||
height: 180rpx;
|
||||
border-radius: 12rpx;
|
||||
margin-top: -60rpx;
|
||||
background-color: #fff;
|
||||
border: 4rpx solid #fff;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.1);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.spec-info-jd {
|
||||
flex: 1;
|
||||
margin-left: 24rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-end;
|
||||
padding-bottom: 10rpx;
|
||||
}
|
||||
|
||||
.spec-price-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: baseline;
|
||||
color: #fa2c19;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.price-symbol {
|
||||
font-size: 24rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.price-value {
|
||||
font-size: 36rpx;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.spec-stock-jd {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.spec-choosed-jd {
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.spec-list {
|
||||
flex: 1;
|
||||
.close-btn-jd {
|
||||
font-size: 48rpx;
|
||||
color: #999;
|
||||
position: absolute;
|
||||
right: -10rpx;
|
||||
top: -10rpx;
|
||||
padding: 10rpx;
|
||||
}
|
||||
|
||||
.spec-item {
|
||||
.spec-list-jd {
|
||||
flex: 1;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
.spec-group {
|
||||
padding: 30rpx 0;
|
||||
}
|
||||
|
||||
.group-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 24rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.group-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.spec-tag {
|
||||
background-color: #f6f6f6;
|
||||
padding: 16rpx 32rpx;
|
||||
border-radius: 40rpx;
|
||||
margin-right: 20rpx;
|
||||
margin-bottom: 20rpx;
|
||||
border: 2rpx solid #f6f6f6;
|
||||
}
|
||||
|
||||
.spec-tag.active {
|
||||
background-color: #fcedeb;
|
||||
border-color: #fa2c19;
|
||||
}
|
||||
|
||||
.spec-tag.active .tag-text {
|
||||
color: #fa2c19;
|
||||
}
|
||||
|
||||
.tag-text {
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.spec-quantity-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 30rpx 0;
|
||||
}
|
||||
|
||||
.quantity-selector-jd {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
background-color: #f6f6f6;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.q-btn {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 25rpx 0;
|
||||
border-bottom: 1rpx solid #f5f5f5;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.spec-item.active {
|
||||
background-color: #fff3e0;
|
||||
.q-btn-text {
|
||||
font-size: 36rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.spec-name {
|
||||
flex: 1;
|
||||
.q-input {
|
||||
width: 80rpx;
|
||||
text-align: center;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.spec-price {
|
||||
font-size: 26rpx;
|
||||
color: #ff5000;
|
||||
margin-right: 20rpx;
|
||||
.spec-footer-jd {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: 20rpx 0 10rpx;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.spec-stock {
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
width: 100rpx;
|
||||
text-align: right;
|
||||
.footer-btn {
|
||||
flex: 1;
|
||||
height: 80rpx;
|
||||
line-height: 80rpx;
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
border-radius: 40rpx;
|
||||
margin: 0 10rpx;
|
||||
border: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.footer-btn.cart {
|
||||
background: linear-gradient(90deg, #ffba0d, #ffc30d);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.footer-btn.buy {
|
||||
background: linear-gradient(90deg, #f2140c, #f2270c);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* 功能主治样式 */
|
||||
|
||||
@@ -48,13 +48,10 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 主内容区域 -->
|
||||
<scroll-view
|
||||
<!-- 主内容区域:改为普通 view,由页面整体滚动 -->
|
||||
<view
|
||||
v-else
|
||||
direction="vertical"
|
||||
class="main-content"
|
||||
:style="{ height: scrollHeight + 'px' }"
|
||||
@scrolltolower="loadMore"
|
||||
class="main-content"
|
||||
>
|
||||
<!-- 初始状态(无搜索词) -->
|
||||
<view v-if="searchKeyword == '' && showResults == false">
|
||||
@@ -233,12 +230,13 @@
|
||||
|
||||
<!-- 底部安全区域 -->
|
||||
<view class="safe-area"></view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { ref, reactive, onMounted, computed } from 'vue'
|
||||
import { onPageScroll, onReachBottom } from '@dcloudio/uni-app'
|
||||
import { supabaseService } from '@/utils/supabaseService.uts'
|
||||
import type { Product } from '@/utils/supabaseService.uts'
|
||||
|
||||
@@ -251,6 +249,7 @@ const loading = ref(false)
|
||||
const hasMore = ref(true)
|
||||
const isError = ref(false) // 错误状态控制
|
||||
const autoFocus = ref(true)
|
||||
|
||||
const activeSort = ref('default') // 当前排序方式: default, sales, price
|
||||
const priceSortAsc = ref(false) // 价格排序是否为升序
|
||||
|
||||
@@ -474,6 +473,8 @@ const performSearch = async (): Promise<void> => {
|
||||
return
|
||||
}
|
||||
|
||||
console.log('Search execution started for keyword:', keyword)
|
||||
|
||||
let sortBy = 'sales'
|
||||
let ascending = false
|
||||
if (activeSort.value === 'price') {
|
||||
@@ -484,7 +485,9 @@ const performSearch = async (): Promise<void> => {
|
||||
}
|
||||
|
||||
try {
|
||||
console.log('Calling searchProducts with params:', keyword, currentPage.value, sortBy, ascending)
|
||||
const prodResp = await supabaseService.searchProducts(keyword, currentPage.value, 20, sortBy, ascending)
|
||||
console.log('searchProducts response received:', prodResp.data != null ? prodResp.data.length : 0, 'items')
|
||||
|
||||
let shopList: Array<ShopResultType> = []
|
||||
if (currentPage.value === 1 && activeSort.value === 'default') {
|
||||
@@ -539,7 +542,7 @@ const performSearch = async (): Promise<void> => {
|
||||
|
||||
hasMore.value = prodResp.hasmore
|
||||
} catch(e) {
|
||||
console.error('Search failed', e)
|
||||
console.error('Search failed detailed error:', e)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
@@ -691,19 +694,18 @@ const loadMore = async (): Promise<void> => {
|
||||
} else if (activeSort.value === 'default') {
|
||||
sortBy = 'default'
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await supabaseService.searchProducts(keyword, currentPage.value, 20, sortBy, ascending)
|
||||
const respData = response.data != null ? response.data : []
|
||||
for (let i: number = 0; i < respData.length; i++) {
|
||||
const p = respData[i] as UTSJSONObject
|
||||
const p = respData[i] as Product
|
||||
let tag = ''
|
||||
const tagsRaw = p.get('tags')
|
||||
const tagsRaw = p.tags
|
||||
if (tagsRaw != null) {
|
||||
try {
|
||||
const tagsStr = p.getString('tags')
|
||||
const tagsStr = p.tags
|
||||
if (tagsStr != null) {
|
||||
const tags = JSON.parse(tagsStr)
|
||||
const tags = JSON.parse(tagsStr as string)
|
||||
if (Array.isArray(tags) && tags.length > 0) {
|
||||
const firstTag = tags[0]
|
||||
tag = firstTag != null ? (firstTag as string) : ''
|
||||
@@ -713,14 +715,14 @@ const loadMore = async (): Promise<void> => {
|
||||
}
|
||||
|
||||
const searchItem: SearchResultType = {
|
||||
id: p.getString('id') ?? '',
|
||||
name: p.getString('name') ?? '',
|
||||
image: p.getString('main_image_url') ?? '/static/default.jpg',
|
||||
price: p.getNumber('base_price') ?? 0,
|
||||
specification: p.getString('specification') ?? '标准规格',
|
||||
id: p.id ?? '',
|
||||
name: p.name ?? '',
|
||||
image: p.main_image_url ?? '/static/default.jpg',
|
||||
price: p.base_price ?? 0,
|
||||
specification: p.specification ?? '标准规格',
|
||||
tag: tag,
|
||||
sales: p.getNumber('sale_count') ?? 0,
|
||||
merchant_id: p.getString('merchant_id') ?? ''
|
||||
sales: p.sale_count ?? 0,
|
||||
merchant_id: p.merchant_id ?? ''
|
||||
}
|
||||
searchResults.value.push(searchItem)
|
||||
}
|
||||
@@ -733,6 +735,12 @@ const loadMore = async (): Promise<void> => {
|
||||
}
|
||||
}
|
||||
|
||||
onReachBottom(() => {
|
||||
if (showResults.value) {
|
||||
loadMore()
|
||||
}
|
||||
})
|
||||
|
||||
const refreshGuessList = () => {
|
||||
uni.showLoading({ title: '刷新中' })
|
||||
setTimeout(() => {
|
||||
@@ -835,6 +843,7 @@ const goBack = () => {
|
||||
background-color: #f5f5f5;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh; /* 确保背景色覆盖全屏 */
|
||||
}
|
||||
|
||||
/* 店铺搜索结果 */
|
||||
@@ -913,6 +922,7 @@ const goBack = () => {
|
||||
/* #ifdef APP-PLUS */
|
||||
padding-top: 0; /* 在App端由style动态控制 */
|
||||
/* #endif */
|
||||
flex-shrink: 0; /* 禁止头部被压缩 */
|
||||
}
|
||||
|
||||
.search-bar-container {
|
||||
@@ -920,7 +930,7 @@ const goBack = () => {
|
||||
flex-direction: row; /* UVUE 必须显式设置 row */
|
||||
align-items: center;
|
||||
padding: 10px 16px;
|
||||
width: 100%; /* 确保占满宽度 */
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.back-btn {
|
||||
@@ -932,6 +942,7 @@ const goBack = () => {
|
||||
width: 32px; /* 固定宽度防止压缩 */
|
||||
height: 32px;
|
||||
margin-right: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.back-icon {
|
||||
@@ -1194,16 +1205,30 @@ const goBack = () => {
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
width: 48%;
|
||||
width: 48%; /* 手机端 2列 */
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
/* 猜测列表响应式,参照 index.uvue 的 hot-products */
|
||||
@media screen and (min-width: 769px) {
|
||||
.guess-item {
|
||||
width: 32%; /* 平板 3列 */
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1025px) {
|
||||
.guess-item {
|
||||
width: 23%; /* 电脑 4列 */
|
||||
}
|
||||
}
|
||||
|
||||
.guess-img {
|
||||
width: 100%;
|
||||
height: 170px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 8px;
|
||||
background: #f5f5f5;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.guess-name {
|
||||
@@ -1272,110 +1297,86 @@ const goBack = () => {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* 搜索结果 */
|
||||
.search-results {
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.results-header {
|
||||
display: flex;
|
||||
flex-direction: row; /* UVUE 显式设置 row */
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 12px;
|
||||
flex-wrap: wrap; /* 允许换行以适应小屏 */
|
||||
padding: 10px 12px;
|
||||
background-color: #fff;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.results-title {
|
||||
font-size: 15px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.filter-tabs {
|
||||
display: flex;
|
||||
flex-direction: row; /* UVUE 显式设置 row */
|
||||
flex: 1; /* 自适应填充剩余空间 */
|
||||
justify-content: flex-end; /* 靠右对齐 */
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.filter-tab {
|
||||
font-size: 13px;
|
||||
color: #666;
|
||||
padding: 4px 8px; /* 增加点击区域 */
|
||||
margin-left: 16px;
|
||||
padding: 8px 12px;
|
||||
border-radius: 20px;
|
||||
border: 1px solid #e0e0e0;
|
||||
transition: all 0.2s ease;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.filter-tab.active {
|
||||
color: #4CAF50;
|
||||
font-weight: bold; /* REPLACED 500 */
|
||||
background: #ff5000;
|
||||
color: white;
|
||||
border-color: #ff5000;
|
||||
}
|
||||
|
||||
.filter-tab:hover {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
/* 搜索结果列表 */
|
||||
.search-results {
|
||||
padding-bottom: 20px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start; /* 核心修复:确保内容不居中缩进 */
|
||||
}
|
||||
|
||||
.results-header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 10px 12px;
|
||||
background-color: #fff;
|
||||
margin-bottom: 2px;
|
||||
width: 100%; /* 核心修复:确保标题栏撑满宽度 */
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.results-list {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
/* 响应式布局 */
|
||||
/* 平板设备 (768px以上) */
|
||||
@media screen and (min-width: 768px) {
|
||||
.results-list {
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.result-item {
|
||||
width: 32%;
|
||||
}
|
||||
|
||||
.guess-item {
|
||||
width: 24%;
|
||||
}
|
||||
}
|
||||
|
||||
/* 桌面设备 (1024px以上) */
|
||||
@media screen and (min-width: 1024px) {
|
||||
.results-list {
|
||||
padding: 0 24px;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.result-item {
|
||||
width: 19%;
|
||||
}
|
||||
|
||||
.guess-item {
|
||||
width: 16%;
|
||||
}
|
||||
|
||||
.product-image {
|
||||
height: 160px;
|
||||
}
|
||||
|
||||
.guess-grid {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
/* 大屏幕 (1440px以上) */
|
||||
@media screen and (min-width: 1440px) {
|
||||
.result-item {
|
||||
width: 16%;
|
||||
}
|
||||
|
||||
.guess-item {
|
||||
width: 12%;
|
||||
}
|
||||
justify-content: flex-start;
|
||||
padding: 10px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
margin-top: 5px;
|
||||
background-color: #fff; /* 为列表添加背景色 */
|
||||
}
|
||||
|
||||
.result-item {
|
||||
@@ -1384,16 +1385,43 @@ const goBack = () => {
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
width: 48%;
|
||||
/* width: calc(50% - 20px); 手机端一行2个 */
|
||||
width: 48%;
|
||||
margin-bottom: 12px;
|
||||
margin-right: 2%;
|
||||
border: 1px solid #f0f0f0; /* 添加微弱边框增加层次感 */
|
||||
}
|
||||
|
||||
/* 电脑端响应式覆盖 - 强制拉伸宽度 */
|
||||
@media screen and (min-width: 1025px) {
|
||||
.main-content {
|
||||
width: 1200px; /* 改为固定宽度或更大百分比 */
|
||||
max-width: 95%;
|
||||
margin: 0 auto;
|
||||
padding: 20px 32px;
|
||||
}
|
||||
|
||||
.result-item {
|
||||
width: 23%; /* 4列布局 */
|
||||
margin-right: 2%;
|
||||
}
|
||||
}
|
||||
|
||||
/* 大桌面端 (1400px以上) */
|
||||
@media screen and (min-width: 1400px) {
|
||||
.result-item {
|
||||
width: 23%; /* 保持一行4个,或者根据需要调整为 18% (一行5个) */
|
||||
}
|
||||
}
|
||||
|
||||
.product-image {
|
||||
width: 100%;
|
||||
height: 170px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 8px;
|
||||
background: #f5f5f5;
|
||||
height: 170px; /* 与主页一致 */
|
||||
/* aspect-ratio: 1 / 1; */
|
||||
object-fit: cover;
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.product-name {
|
||||
|
||||
@@ -45,9 +45,17 @@
|
||||
|
||||
<!-- 商品列表 -->
|
||||
<view class="product-section">
|
||||
<view class="section-title">全部商品</view>
|
||||
<view class="product-grid">
|
||||
<view v-for="product in products" :key="product.id" class="product-item" @click="goToProduct(product.id)">
|
||||
<view class="results-header">
|
||||
<text class="results-title">全部商品</text>
|
||||
<view class="filter-tabs">
|
||||
<text class="filter-tab active">综合</text>
|
||||
<text class="filter-tab">销量</text>
|
||||
<text class="filter-tab">价格</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="results-list">
|
||||
<view v-for="product in products" :key="product.id" class="result-item" @click="goToProduct(product.id)">
|
||||
<image :src="product.images[0]" class="product-image" mode="aspectFill" />
|
||||
<text class="product-name" :lines="2">{{ product.name }}</text>
|
||||
<view class="product-bottom">
|
||||
@@ -529,63 +537,63 @@ const goToProduct = (id: string) => {
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center; /* 使 PC 端内容居中 */
|
||||
}
|
||||
|
||||
.shop-banner {
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
height: 200px; /* PC 端稍微加高一点 */
|
||||
height: 150px;
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
.shop-info-card {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
align-items: flex-start;
|
||||
padding: 0 15px;
|
||||
margin-top: -30px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.shop-logo {
|
||||
width: 80px; /* PC 端稍微加大 */
|
||||
height: 80px;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 8px;
|
||||
border: 2px solid #fff;
|
||||
background-color: #fff;
|
||||
margin-right: 15px;
|
||||
margin-right: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.shop-basic-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-top: 30px;
|
||||
padding-top: 35px;
|
||||
}
|
||||
|
||||
.shop-name {
|
||||
font-size: 22px; /* PC 端字体加大 */
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 8px;
|
||||
margin-bottom: 6px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.shop-stats {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
font-size: 14px;
|
||||
font-size: 11px;
|
||||
color: #666;
|
||||
margin-right: 15px;
|
||||
background-color: #f0f0f0;
|
||||
padding: 4px 10px;
|
||||
margin-right: 8px;
|
||||
background-color: #f5f5f5;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
@@ -593,22 +601,23 @@ const goToProduct = (id: string) => {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding-top: 30px;
|
||||
padding-top: 40px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
border-radius: 20px;
|
||||
margin-left: 15px;
|
||||
border-radius: 17px;
|
||||
margin-left: 8px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 8px 24px; /* PC 端按钮加大 */
|
||||
padding: 4px 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.action-text {
|
||||
font-size: 14px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.chat-btn {
|
||||
@@ -623,6 +632,7 @@ const goToProduct = (id: string) => {
|
||||
.follow-btn {
|
||||
background-color: #ff5000;
|
||||
border: 1px solid #ff5000;
|
||||
min-width: 60px;
|
||||
}
|
||||
|
||||
.follow-btn .action-text {
|
||||
@@ -634,12 +644,70 @@ const goToProduct = (id: string) => {
|
||||
}
|
||||
|
||||
.shop-desc {
|
||||
color: #666;
|
||||
padding: 15px 15px 0;
|
||||
line-height: 1.6;
|
||||
color: #999;
|
||||
padding: 10px 15px 0;
|
||||
line-height: 1.5;
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
box-sizing: border-box;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
/* PC 端响应式覆盖 */
|
||||
@media screen and (min-width: 1025px) {
|
||||
.shop-header {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.shop-banner {
|
||||
height: 300px;
|
||||
max-width: 1200px;
|
||||
}
|
||||
|
||||
.shop-info-card {
|
||||
max-width: 1200px;
|
||||
margin-top: -40px;
|
||||
}
|
||||
|
||||
.shop-logo {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.shop-basic-info {
|
||||
padding-top: 45px;
|
||||
}
|
||||
|
||||
.shop-name {
|
||||
font-size: 24px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.shop-stats .stat-item {
|
||||
font-size: 14px;
|
||||
padding: 6px 15px;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.shop-actions {
|
||||
padding-top: 50px;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
padding: 8px 24px;
|
||||
margin-left: 15px;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.action-text {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.shop-desc {
|
||||
max-width: 1200px;
|
||||
font-size: 14px;
|
||||
padding: 15px 15px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Coupon Styles */
|
||||
@@ -708,67 +776,121 @@ const goToProduct = (id: string) => {
|
||||
}
|
||||
|
||||
.product-section {
|
||||
padding: 20px;
|
||||
padding: 20px 0;
|
||||
width: 100%;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 20px;
|
||||
padding-left: 10px;
|
||||
border-left: 5px solid #ff5000;
|
||||
}
|
||||
|
||||
.product-grid {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.product-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
width: calc(50% - 10px); /* 默认两列 */
|
||||
margin-right: 20px;
|
||||
margin-bottom: 20px;
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.product-item:nth-child(2n) {
|
||||
margin-right: 0;
|
||||
.results-header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 10px 12px;
|
||||
background-color: #fff;
|
||||
margin-bottom: 2px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.product-item:hover {
|
||||
.results-title {
|
||||
font-size: 15px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
padding-left: 10px;
|
||||
border-left: 5px solid #ff5000;
|
||||
}
|
||||
|
||||
.filter-tabs {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.filter-tab {
|
||||
font-size: 13px;
|
||||
color: #666;
|
||||
padding: 8px 12px;
|
||||
border-radius: 20px;
|
||||
border: 1px solid #e0e0e0;
|
||||
transition: all 0.2s ease;
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.filter-tab.active {
|
||||
background: #ff5000;
|
||||
color: white;
|
||||
border-color: #ff5000;
|
||||
}
|
||||
|
||||
.results-list {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-start;
|
||||
padding: 10px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
margin-top: 5px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.result-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
width: 48% !important;
|
||||
margin-bottom: 12px;
|
||||
margin-right: 0 !important;
|
||||
border: 1px solid #f0f0f0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.result-item:nth-child(2n-1) {
|
||||
margin-right: 4% !important;
|
||||
}
|
||||
|
||||
.result-item:nth-child(2n) {
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
|
||||
.result-item:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
.product-image {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
background: #f5f5f5;
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
height: 170px;
|
||||
object-fit: cover;
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.product-name {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
margin: 10px 0;
|
||||
line-height: 1.4;
|
||||
height: 40px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
padding: 0 10px;
|
||||
font-size: 13px;
|
||||
color: #333;
|
||||
margin-bottom: 5px;
|
||||
line-height: 1.4;
|
||||
height: 36px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
padding: 0 8px;
|
||||
}
|
||||
|
||||
.product-bottom {
|
||||
@@ -776,20 +898,20 @@ const goToProduct = (id: string) => {
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 10px 12px;
|
||||
padding: 0 8px 8px;
|
||||
}
|
||||
|
||||
.product-price {
|
||||
font-size: 18px;
|
||||
font-size: 15px;
|
||||
color: #ff5000;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.product-add-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
background-color: #ff5000;
|
||||
border-radius: 14px;
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -797,50 +919,50 @@ const goToProduct = (id: string) => {
|
||||
|
||||
.add-icon {
|
||||
color: #fff;
|
||||
font-size: 18px;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* PC/Tablet Responsive */
|
||||
@media (min-width: 768px) {
|
||||
.product-item {
|
||||
width: calc(33.33% - 14px) !important;
|
||||
}
|
||||
.product-item:nth-child(2n) {
|
||||
margin-right: 20px !important;
|
||||
}
|
||||
.product-item:nth-child(3n) {
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
/* 电脑端响应式覆盖 */
|
||||
@media screen and (min-width: 1025px) {
|
||||
.product-section {
|
||||
max-width: 95%;
|
||||
width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.result-item {
|
||||
width: 23%;
|
||||
margin-right: 2% !important;
|
||||
}
|
||||
|
||||
.result-item:nth-child(2n) {
|
||||
margin-right: 2% !important;
|
||||
}
|
||||
|
||||
.result-item:nth-child(4n) {
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.product-item {
|
||||
width: calc(20% - 16px) !important; /* 五列 */
|
||||
}
|
||||
.product-item:nth-child(3n) {
|
||||
margin-right: 20px !important;
|
||||
}
|
||||
.product-item:nth-child(5n) {
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
|
||||
/* 大桌面端 (1400px以上) */
|
||||
@media screen and (min-width: 1400px) {
|
||||
.result-item {
|
||||
width: 23.5%;
|
||||
}
|
||||
}
|
||||
|
||||
.shop-banner {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1025px) {
|
||||
.shop-banner {
|
||||
height: 300px; /* 大屏加宽 Banner */
|
||||
border-radius: 0 0 20px 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1440px) {
|
||||
.product-item {
|
||||
width: calc(16.66% - 17px) !important; /* 六列 */
|
||||
}
|
||||
.product-item:nth-child(5n) {
|
||||
margin-right: 20px !important;
|
||||
}
|
||||
.product-item:nth-child(6n) {
|
||||
margin-right: 0 !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user