Prechádzať zdrojové kódy

style(sqxp-wx): 客户端页面与 miniprogram 对齐模板与样式

从 miniprogram 同步客户端相关页面的 template 与 style,保留 sqxp-wx 脚本与请求逻辑;配送员/供应商端页面模板样式已与参考工程一致。

Made-with: Cursor
zlwu 6 mesiacov pred
rodič
commit
0dd77d6699

+ 7 - 1
sqxp-wx/components/client/product-detail-overlay.vue

@@ -172,6 +172,8 @@
   </view>
 </template>
 
+
+
 <script setup>
 import { computed, ref, watch } from 'vue'
 import ClientTabBar from '@/components/client/client-tab-bar.vue'
@@ -404,6 +406,8 @@ watch(
 )
 </script>
 
+
+
 <style lang="scss" scoped>
 .overlay-root {
   position: fixed;
@@ -425,10 +429,12 @@ watch(
   flex-direction: column;
 }
 
+/* 为底部自定义 Tab 留出空间,避免白面板盖住导航栏区域 */
 .overlay-panel--with-tab {
   bottom: calc(88rpx + env(safe-area-inset-bottom, 0px));
 }
 
+/* 有底部 Tab 时,面板已整体上移,body 不再额外加底部留白,避免出现透明空层 */
 .overlay-panel--with-tab .overlay-body {
   padding-bottom: 0;
 }
@@ -824,6 +830,7 @@ watch(
   }
 }
 
+/* 最后一个内容块不再额外留底部分隔,避免尾部出现空白条 */
 .overlay-scroll-inner > view:last-child {
   border-bottom: 0;
 }
@@ -921,4 +928,3 @@ watch(
   }
 }
 </style>
-

+ 99 - 102
sqxp-wx/pages/client/address/address.vue

@@ -1,3 +1,99 @@
+<template>
+  <view class="page client-theme client-subpage">
+    <view v-if="loading" class="state">
+      <ClientSkeleton variant="list" :rows="4" />
+    </view>
+    <template v-else>
+      <view class="client-subpage-hero">
+        <text class="client-subpage-hero__title">收货地址</text>
+        <text class="client-subpage-hero__sub">管理配送地址,保障送货上门</text>
+      </view>
+      <view v-if="list.length === 0" class="empty-wrap">
+        <image class="empty-image" :src="IMG_EMPTY" mode="aspectFit" />
+        <text class="empty-text">暂无收货地址</text>
+        <text class="empty-hint">添加后便于配送员联系与送货</text>
+        <button class="add-btn" type="default" @click="goAdd">新增收货地址</button>
+      </view>
+      <view v-else class="list-shell">
+        <!-- #ifdef H5 -->
+        <view class="list-scroll list-scroll--h5" @scroll.passive="onH5ListScroll">
+          <view class="list-inner">
+            <view v-for="item in list" :key="item.id" class="card" hover-class="card--pressed" :hover-stay-time="100">
+              <view class="card-main" @click="goEdit(item)">
+                <view class="card-top">
+                  <text class="name">{{ item.contactPerson }}</text>
+                  <PhoneDialInline
+                    class="phone-wrap"
+                    :phone="item.contactPhone"
+                    color="#0a9f6d"
+                    hide-icon
+                  />
+                  <view v-if="item.isDefault" class="tag-default">默认</view>
+                </view>
+                <text class="addr-line">{{ fullRegion(item) }}{{ item.detailAddress }}</text>
+              </view>
+              <view class="card-actions">
+                <text
+                  v-if="!item.isDefault"
+                  class="act"
+                  @click.stop="handleSetDefault(item.id)"
+                >设为默认</text>
+                <text class="act" @click.stop="goEdit(item)">编辑</text>
+                <text class="act danger" @click.stop="handleDelete(item)">删除</text>
+              </view>
+            </view>
+            <view v-if="loadingMore" class="list-footer-tip">加载中...</view>
+            <view v-else-if="!hasMore && list.length > 0" class="list-footer-tip muted">没有更多了</view>
+          </view>
+        </view>
+        <!-- #endif -->
+        <!-- #ifndef H5 -->
+        <scroll-view
+          scroll-y
+          class="list-scroll"
+          :show-scrollbar="true"
+          :enable-flex="true"
+          :lower-threshold="100"
+          @scrolltolower="onScrollToLower"
+        >
+          <view class="list-inner">
+            <view v-for="item in list" :key="item.id" class="card" hover-class="card--pressed" :hover-stay-time="100">
+              <view class="card-main" @click="goEdit(item)">
+                <view class="card-top">
+                  <text class="name">{{ item.contactPerson }}</text>
+                  <PhoneDialInline
+                    class="phone-wrap"
+                    :phone="item.contactPhone"
+                    color="#0a9f6d"
+                    hide-icon
+                  />
+                  <view v-if="item.isDefault" class="tag-default">默认</view>
+                </view>
+                <text class="addr-line">{{ fullRegion(item) }}{{ item.detailAddress }}</text>
+              </view>
+              <view class="card-actions">
+                <text
+                  v-if="!item.isDefault"
+                  class="act"
+                  @click.stop="handleSetDefault(item.id)"
+                >设为默认</text>
+                <text class="act" @click.stop="goEdit(item)">编辑</text>
+                <text class="act danger" @click.stop="handleDelete(item)">删除</text>
+              </view>
+            </view>
+            <view v-if="loadingMore" class="list-footer-tip">加载中...</view>
+            <view v-else-if="!hasMore && list.length > 0" class="list-footer-tip muted">没有更多了</view>
+          </view>
+        </scroll-view>
+        <!-- #endif -->
+        <view class="footer-bar">
+          <button class="add-btn-bottom" type="default" @click="goAdd">新增收货地址</button>
+        </view>
+      </view>
+    </template>
+
+
+
 <script setup>
 import { onShow } from '@dcloudio/uni-app'
 import { ref } from 'vue'
@@ -151,101 +247,7 @@ onShow(() => {
 })
 </script>
 
-<template>
-  <view class="page client-theme client-subpage">
-    <view v-if="loading" class="state">
-      <ClientSkeleton variant="list" :rows="4" />
-    </view>
-    <template v-else>
-      <view class="client-subpage-hero">
-        <text class="client-subpage-hero__title">收货地址</text>
-        <text class="client-subpage-hero__sub">管理配送地址,保障送货上门</text>
-      </view>
-      <view v-if="list.length === 0" class="empty-wrap">
-        <image class="empty-image" :src="IMG_EMPTY" mode="aspectFit" />
-        <text class="empty-text">暂无收货地址</text>
-        <text class="empty-hint">添加后便于配送员联系与送货</text>
-        <button class="add-btn" type="default" @click="goAdd">新增收货地址</button>
-      </view>
-      <view v-else class="list-shell">
-        <!-- #ifdef H5 -->
-        <view class="list-scroll list-scroll--h5" @scroll.passive="onH5ListScroll">
-          <view class="list-inner">
-            <view v-for="item in list" :key="item.id" class="card" hover-class="card--pressed" :hover-stay-time="100">
-              <view class="card-main" @click="goEdit(item)">
-                <view class="card-top">
-                  <text class="name">{{ item.contactPerson }}</text>
-                  <PhoneDialInline
-                    class="phone-wrap"
-                    :phone="item.contactPhone"
-                    color="#0a9f6d"
-                    hide-icon
-                  />
-                  <view v-if="item.isDefault" class="tag-default">默认</view>
-                </view>
-                <text class="addr-line">{{ fullRegion(item) }}{{ item.detailAddress }}</text>
-              </view>
-              <view class="card-actions">
-                <text
-                  v-if="!item.isDefault"
-                  class="act"
-                  @click.stop="handleSetDefault(item.id)"
-                >设为默认</text>
-                <text class="act" @click.stop="goEdit(item)">编辑</text>
-                <text class="act danger" @click.stop="handleDelete(item)">删除</text>
-              </view>
-            </view>
-            <view v-if="loadingMore" class="list-footer-tip">加载中...</view>
-            <view v-else-if="!hasMore && list.length > 0" class="list-footer-tip muted">没有更多了</view>
-          </view>
-        </view>
-        <!-- #endif -->
-        <!-- #ifndef H5 -->
-        <scroll-view
-          scroll-y
-          class="list-scroll"
-          :show-scrollbar="true"
-          :enable-flex="true"
-          :lower-threshold="100"
-          @scrolltolower="onScrollToLower"
-        >
-          <view class="list-inner">
-            <view v-for="item in list" :key="item.id" class="card" hover-class="card--pressed" :hover-stay-time="100">
-              <view class="card-main" @click="goEdit(item)">
-                <view class="card-top">
-                  <text class="name">{{ item.contactPerson }}</text>
-                  <PhoneDialInline
-                    class="phone-wrap"
-                    :phone="item.contactPhone"
-                    color="#0a9f6d"
-                    hide-icon
-                  />
-                  <view v-if="item.isDefault" class="tag-default">默认</view>
-                </view>
-                <text class="addr-line">{{ fullRegion(item) }}{{ item.detailAddress }}</text>
-              </view>
-              <view class="card-actions">
-                <text
-                  v-if="!item.isDefault"
-                  class="act"
-                  @click.stop="handleSetDefault(item.id)"
-                >设为默认</text>
-                <text class="act" @click.stop="goEdit(item)">编辑</text>
-                <text class="act danger" @click.stop="handleDelete(item)">删除</text>
-              </view>
-            </view>
-            <view v-if="loadingMore" class="list-footer-tip">加载中...</view>
-            <view v-else-if="!hasMore && list.length > 0" class="list-footer-tip muted">没有更多了</view>
-          </view>
-        </scroll-view>
-        <!-- #endif -->
-        <view class="footer-bar">
-          <button class="add-btn-bottom" type="default" @click="goAdd">新增收货地址</button>
-        </view>
-      </view>
-    </template>
-  </view>
-</template>
+
 
 <style lang="scss">
 page {
@@ -292,14 +294,14 @@ page {
 
   .empty-text {
     font-size: 30rpx;
-    color: #303133;
+    color: var(--c-text);
     margin-bottom: 8rpx;
     text-align: center;
   }
 
   .empty-hint {
     font-size: 24rpx;
-    color: #909399;
+    color: var(--c-muted);
     margin-bottom: 28rpx;
     text-align: center;
   }
@@ -324,11 +326,6 @@ page {
   margin: 0;
 }
 
-.add-btn::after,
-.add-btn-bottom::after {
-  border: none;
-}
-
 .list-shell {
   flex: 1;
   min-height: 0;

+ 106 - 106
sqxp-wx/pages/client/address/select.vue

@@ -1,3 +1,100 @@
+<template>
+  <view class="page client-theme client-subpage">
+    <view v-if="loading && list.length === 0" class="state">加载中...</view>
+    <template v-else>
+      <view class="client-subpage-hero">
+        <text class="client-subpage-hero__title">选择收货地址</text>
+        <text class="client-subpage-hero__sub">点击下方地址完成选择,或新增地址</text>
+      </view>
+      <view v-if="list.length === 0" class="empty-wrap">
+        <image class="empty-image" :src="IMG_EMPTY" mode="aspectFit" />
+        <text class="empty-text">暂无收货地址</text>
+        <text class="empty-hint">添加后便于配送员联系与送货</text>
+      </view>
+
+      <view v-else class="list-shell">
+        <!-- #ifdef H5 -->
+        <view class="list-scroll list-scroll--h5" @scroll.passive="onH5ListScroll">
+          <view class="list-inner">
+            <radio-group :value="selectedAddressId" @change="onAddressChange">
+              <label v-for="item in list" :key="item.id" class="card" hover-class="card--pressed" :hover-stay-time="100">
+                <view class="card-main">
+                  <view class="radio-col">
+                    <radio :value="item.id" :color="'#0a9f6d'" />
+                  </view>
+                  <view class="card-top">
+                    <text class="name">{{ item.contactPerson }}</text>
+                    <text class="phone">{{ item.contactPhone }}</text>
+                    <view v-if="item.isDefault" class="tag-default">默认</view>
+                  </view>
+                  <text class="addr-line">{{ fullRegion(item) }}{{ item.detailAddress }}</text>
+                </view>
+                <view class="card-actions" @click.stop>
+                  <text
+                    v-if="!item.isDefault"
+                    class="act"
+                    @click.stop="handleSetDefault(item.id)"
+                  >设为默认</text>
+                  <text class="act" @click.stop="goEdit(item)">编辑</text>
+                  <text class="act danger" @click.stop="handleDelete(item)">删除</text>
+                </view>
+              </label>
+            </radio-group>
+            <view v-if="loadingMore" class="list-footer-tip">加载中...</view>
+            <view v-else-if="!hasMore && list.length > 0" class="list-footer-tip muted">没有更多了</view>
+          </view>
+        </view>
+        <!-- #endif -->
+
+        <!-- #ifndef H5 -->
+        <scroll-view
+          scroll-y
+          class="list-scroll"
+          :show-scrollbar="true"
+          :lower-threshold="100"
+          @scrolltolower="onScrollToLower"
+        >
+          <view class="list-inner">
+            <radio-group :value="selectedAddressId" @change="onAddressChange">
+              <label v-for="item in list" :key="item.id" class="card" hover-class="card--pressed" :hover-stay-time="100">
+                <view class="card-main">
+                  <view class="radio-col">
+                    <radio :value="item.id" :color="'#0a9f6d'" />
+                  </view>
+                  <view class="card-top">
+                    <text class="name">{{ item.contactPerson }}</text>
+                    <text class="phone">{{ item.contactPhone }}</text>
+                    <view v-if="item.isDefault" class="tag-default">默认</view>
+                  </view>
+                  <text class="addr-line">{{ fullRegion(item) }}{{ item.detailAddress }}</text>
+                </view>
+                <view class="card-actions" @click.stop>
+                  <text
+                    v-if="!item.isDefault"
+                    class="act"
+                    @click.stop="handleSetDefault(item.id)"
+                  >设为默认</text>
+                  <text class="act" @click.stop="goEdit(item)">编辑</text>
+                  <text class="act danger" @click.stop="handleDelete(item)">删除</text>
+                </view>
+              </label>
+            </radio-group>
+            <view v-if="loadingMore" class="list-footer-tip">加载中...</view>
+            <view v-else-if="!hasMore && list.length > 0" class="list-footer-tip muted">没有更多了</view>
+          </view>
+        </scroll-view>
+        <!-- #endif -->
+      </view>
+
+      <view class="footer-bar">
+        <van-button class="add-btn-bottom" block round type="primary" color="#d6a343" @click="goAdd">
+          新增收货地址
+        </van-button>
+      </view>
+    </template>
+
+
+
 <script setup>
 import { onShow } from '@dcloudio/uni-app'
 import { ref } from 'vue'
@@ -173,100 +270,7 @@ onShow(() => {
 })
 </script>
 
-<template>
-  <view class="page client-theme client-subpage">
-    <view v-if="loading && list.length === 0" class="state">加载中...</view>
-    <template v-else>
-      <view class="client-subpage-hero">
-        <text class="client-subpage-hero__title">选择收货地址</text>
-        <text class="client-subpage-hero__sub">点击下方地址完成选择,或新增地址</text>
-      </view>
-      <view v-if="list.length === 0" class="empty-wrap">
-        <image class="empty-image" :src="IMG_EMPTY" mode="aspectFit" />
-        <text class="empty-text">暂无收货地址</text>
-        <text class="empty-hint">添加后便于配送员联系与送货</text>
-      </view>
-
-      <view v-else class="list-shell">
-        <!-- #ifdef H5 -->
-        <view class="list-scroll list-scroll--h5" @scroll.passive="onH5ListScroll">
-          <view class="list-inner">
-            <radio-group :value="selectedAddressId" @change="onAddressChange">
-              <label v-for="item in list" :key="item.id" class="card" hover-class="card--pressed" :hover-stay-time="100">
-                <view class="card-main">
-                  <view class="radio-col">
-                    <radio :value="item.id" :color="'#0a9f6d'" />
-                  </view>
-                  <view class="card-top">
-                    <text class="name">{{ item.contactPerson }}</text>
-                    <text class="phone">{{ item.contactPhone }}</text>
-                    <view v-if="item.isDefault" class="tag-default">默认</view>
-                  </view>
-                  <text class="addr-line">{{ fullRegion(item) }}{{ item.detailAddress }}</text>
-                </view>
-                <view class="card-actions" @click.stop>
-                  <text
-                    v-if="!item.isDefault"
-                    class="act"
-                    @click.stop="handleSetDefault(item.id)"
-                  >设为默认</text>
-                  <text class="act" @click.stop="goEdit(item)">编辑</text>
-                  <text class="act danger" @click.stop="handleDelete(item)">删除</text>
-                </view>
-              </label>
-            </radio-group>
-            <view v-if="loadingMore" class="list-footer-tip">加载中...</view>
-            <view v-else-if="!hasMore && list.length > 0" class="list-footer-tip muted">没有更多了</view>
-          </view>
-        </view>
-        <!-- #endif -->
 
-        <!-- #ifndef H5 -->
-        <scroll-view
-          scroll-y
-          class="list-scroll"
-          :show-scrollbar="true"
-          :lower-threshold="100"
-          @scrolltolower="onScrollToLower"
-        >
-          <view class="list-inner">
-            <radio-group :value="selectedAddressId" @change="onAddressChange">
-              <label v-for="item in list" :key="item.id" class="card" hover-class="card--pressed" :hover-stay-time="100">
-                <view class="card-main">
-                  <view class="radio-col">
-                    <radio :value="item.id" :color="'#0a9f6d'" />
-                  </view>
-                  <view class="card-top">
-                    <text class="name">{{ item.contactPerson }}</text>
-                    <text class="phone">{{ item.contactPhone }}</text>
-                    <view v-if="item.isDefault" class="tag-default">默认</view>
-                  </view>
-                  <text class="addr-line">{{ fullRegion(item) }}{{ item.detailAddress }}</text>
-                </view>
-                <view class="card-actions" @click.stop>
-                  <text
-                    v-if="!item.isDefault"
-                    class="act"
-                    @click.stop="handleSetDefault(item.id)"
-                  >设为默认</text>
-                  <text class="act" @click.stop="goEdit(item)">编辑</text>
-                  <text class="act danger" @click.stop="handleDelete(item)">删除</text>
-                </view>
-              </label>
-            </radio-group>
-            <view v-if="loadingMore" class="list-footer-tip">加载中...</view>
-            <view v-else-if="!hasMore && list.length > 0" class="list-footer-tip muted">没有更多了</view>
-          </view>
-        </scroll-view>
-        <!-- #endif -->
-      </view>
-
-      <view class="footer-bar">
-        <button class="add-btn-bottom" type="default" @click="goAdd">新增收货地址</button>
-      </view>
-    </template>
-  </view>
-</template>
 
 <style lang="scss">
 page {
@@ -345,6 +349,10 @@ page {
 }
 /* #endif */
 
+.card--pressed {
+  opacity: 0.94;
+}
+
 .list-inner {
   padding: 24rpx 32rpx 8rpx;
   padding-bottom: calc(24rpx + 120rpx + constant(safe-area-inset-bottom));
@@ -380,20 +388,16 @@ page {
 
 .add-btn-bottom {
   width: 100%;
-  max-width: none;
   height: 88rpx;
-  line-height: 88rpx;
-  background: linear-gradient(145deg, var(--c-barley-yellow), #e4b458);
-  color: #ffffff;
   border-radius: 44rpx;
   font-size: 30rpx;
-  border: none;
-  font-weight: 700;
-  margin: 0;
+  border: none !important;
+  box-shadow: none !important;
 }
 
-.add-btn-bottom::after {
-  border: none;
+.add-btn-bottom :deep(.van-button) {
+  border: none !important;
+  box-shadow: none !important;
 }
 
 .card {
@@ -406,10 +410,6 @@ page {
   border: 1rpx solid var(--c-border);
 }
 
-.card--pressed {
-  opacity: 0.94;
-}
-
 .card-main {
   padding: 28rpx 24rpx 20rpx;
   display: grid;

+ 143 - 150
sqxp-wx/pages/client/cart/cart.vue

@@ -1,3 +1,140 @@
+<template>
+  <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">
+          <image class="addr-icon" :src="ICON_COORDINATE" mode="aspectFit" />
+        </view>
+        <view class="addr-main">
+          <view class="addr-top">
+            <text class="addr-title">收货地址</text>
+            <text class="addr-edit" @click.stop="goSelectAddress">修改 ›</text>
+          </view>
+          <view v-if="selectedAddress" class="addr-content">
+            <view class="addr-person-row">
+              <text class="addr-person-name">{{ selectedAddress.contactPerson }}</text>
+              <text class="addr-phone">{{ selectedAddress.contactPhone }}</text>
+            </view>
+            <text class="addr-detail">{{ fullAddress(selectedAddress) }}</text>
+          </view>
+          <view v-else class="addr-empty">
+            <text class="addr-empty-text">请先添加收货地址</text>
+          </view>
+        </view>
+      </view>
+    </view>
+
+    <!-- 仅商品列表区域纵向滚动 + 分页加载 -->
+    <scroll-view
+      class="cart-items-scroll"
+      scroll-y
+      :show-scrollbar="true"
+      :lower-threshold="80"
+      @scrolltolower="onCartScrollToLower"
+    >
+      <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" 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-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="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="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="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>
+        <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="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="checkout-actions">
+          <view class="cart-settle" @click="goCheckout">
+            <text>去结算</text>
+          </view>
+        </view>
+      </view>
+    </view>
+
+    <ProductDetailOverlay
+      :show="showProductOverlay"
+      :product-id="overlayProductId"
+      :initial-cart-qty="overlayInitialCartQty"
+      @close="closeProductOverlay"
+    />
+    <!-- #ifdef H5 -->
+    <ClientTabBar active="cart" />
+    <!-- #endif -->
+  </view>
+</template>
+
+
+
 <script setup>
 import { onShow } from '@dcloudio/uni-app'
 import { ref, computed } from 'vue'
@@ -343,155 +480,7 @@ async function loadCart() {
 onShow(loadCart)
 </script>
 
-<template>
-  <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">
-          <image class="addr-icon" :src="ICON_COORDINATE" mode="aspectFit" />
-        </view>
-        <view class="addr-main">
-          <view class="addr-top">
-            <text class="addr-title">收货地址</text>
-            <text class="addr-edit" @click.stop="goSelectAddress">修改 ›</text>
-          </view>
-          <view v-if="selectedAddress" class="addr-content">
-            <view class="addr-person-row">
-              <text class="addr-person-name">{{ selectedAddress.contactPerson }}</text>
-              <text class="addr-phone">{{ selectedAddress.contactPhone }}</text>
-            </view>
-            <text class="addr-detail">{{ fullAddress(selectedAddress) }}</text>
-          </view>
-          <view v-else class="addr-empty">
-            <text class="addr-empty-text">请先添加收货地址</text>
-          </view>
-        </view>
-      </view>
-    </view>
-
-    <!-- 仅商品列表区域纵向滚动 + 分页加载 -->
-    <scroll-view
-      class="cart-items-scroll"
-      scroll-y
-      :show-scrollbar="true"
-      :lower-threshold="80"
-      @scrolltolower="onCartScrollToLower"
-    >
-      <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"
-            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-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="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="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="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>
-        <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="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="checkout-actions">
-          <view class="cart-settle" @click="goCheckout">
-            <text>去结算</text>
-          </view>
-        </view>
-      </view>
-    </view>
-
-    <ProductDetailOverlay
-      :show="showProductOverlay"
-      :product-id="overlayProductId"
-      :initial-cart-qty="overlayInitialCartQty"
-      @close="closeProductOverlay"
-    />
-    <!-- #ifdef H5 -->
-    <ClientTabBar active="cart" />
-    <!-- #endif -->
-  </view>
-</template>
 
 <style lang="scss">
 page {
@@ -539,6 +528,7 @@ page {
 }
 /* #endif */
 
+/* 对齐 sanqiang-client-cart-ui-demo.html .top */
 .cart-hero {
   flex-shrink: 0;
   background: linear-gradient(145deg, var(--c-brand-green), var(--c-brand-green-2));
@@ -564,7 +554,7 @@ page {
   line-height: 1.35;
 }
 
- .cart-top {
+.cart-top {
   flex-shrink: 0;
   flex-grow: 0;
   padding: 24rpx 32rpx 0;
@@ -831,6 +821,7 @@ page {
     align-items: center;
     justify-content: flex-end;
     box-sizing: border-box;
+
   }
 
   .item-delete-icon {
@@ -883,6 +874,7 @@ page {
     min-width: 176rpx;
     justify-content: space-between;
 
+    /* demo .qbtn:30px → 略缩小与首页操作区一致 */
     .qty-btn {
       width: clamp(32rpx, 6.5vw, 42rpx);
       height: clamp(32rpx, 6.5vw, 42rpx);
@@ -998,7 +990,7 @@ page {
   align-items: center;
   justify-content: center;
   padding-bottom: calc(6rpx + constant(safe-area-inset-bottom));
-  padding-bottom: calc(6rpx + env(safe-area-inset-bottom, 0px));
+  padding-bottom: calc(6rpx + env(safe-area-inset-bottom));
   overflow: visible;
 }
 
@@ -1030,6 +1022,7 @@ page {
     gap: 12rpx;
     padding: 8rpx 4rpx;
     flex-shrink: 0;
+
     .select-all-text {
       font-size: 24rpx;
       color: var(--c-muted);

+ 236 - 199
sqxp-wx/pages/client/home/home.vue

@@ -1,3 +1,194 @@
+<template>
+  <!-- 购物车条勿放在 inquiry-page 内:父级 overflow:hidden 会裁切角标;且 bottom 须与 tabBar 对齐 -->
+  <view class="home-root client-theme">
+  <view class="inquiry-page">
+    <view v-if="isLoading" class="state">
+      <ClientSkeleton variant="hero-list" :rows="3" />
+    </view>
+    <view v-else class="content-wrap">
+      <!-- 顶栏:仅渐变背景 + 配送行 + 搜索条(与 sanqiang-client-ui-demo.html .top 一致) -->
+      <view class="filter-top-wrap">
+        <view class="top-row">
+          <text class="top-row-left">{{ deliveryLineLeft }}</text>
+          <text class="top-row-brand">叁强鲜配</text>
+        </view>
+        <view class="search-bar">
+          <input
+            v-model="keyword"
+            class="search-input"
+            type="text"
+            placeholder="搜索商品、品牌、一级/二级分类"
+            placeholder-class="search-placeholder"
+            confirm-type="search"
+            @confirm="onSearchClick"
+          />
+          <view v-if="keyword" class="search-clear" @click.stop="keyword = ''">×</view>
+          <text class="search-action" @click="onSearchClick">搜索</text>
+        </view>
+      </view>
+
+      <!-- 一级分类及以下在灰底区域,独立白卡片(与 demo .section + .first-level 一致) -->
+      <view class="category-section">
+        <view class="section-head">
+          <text class="section-title">商品分类</text>
+        </view>
+
+        <view class="first-category-wrap">
+          <swiper
+            class="first-category-swiper"
+            :current="firstCategorySwiperIndex"
+            :circular="false"
+            :indicator-dots="false"
+            @change="onFirstCategorySwiperChange"
+          >
+            <swiper-item v-for="(page, pageIdx) in firstCategoryPages" :key="`category-page-${pageIdx}`">
+              <view class="first-category-grid">
+                <view
+                  v-for="cat in page"
+                  :key="cat.id"
+                  class="first-category-card"
+                  :class="{ active: selectedFirstCategoryId === cat.id }"
+                  hover-class="first-category-card--hover"
+                  :hover-stay-time="120"
+                  @click="handleFirstCategoryClick(cat.id)"
+                >
+                  <ClientImageLoader
+                    class="first-category-image"
+                    :src="cat.imageUrl || DEFAULT_PLACEHOLDER_IMAGE_URL"
+                    mode="aspectFill"
+                    radius="24rpx"
+                  />
+                  <text class="first-category-name">{{ cat.name }}</text>
+                </view>
+              </view>
+            </swiper-item>
+          </swiper>
+        </view>
+
+        <view v-if="firstCategoryPages.length > 0" class="first-level-indicator">
+          <view
+            v-for="(_, idx) in firstCategoryPages"
+            :key="`dot-${idx}`"
+            class="indicator-dot"
+            :class="{ active: firstCategorySwiperIndex === idx }"
+            @click="goFirstCategorySwiperPage(idx)"
+          />
+        </view>
+      </view>
+
+      <view class="product-layout">
+        <view class="product-body">
+          <scroll-view
+            scroll-y
+            class="category-pane"
+            :show-scrollbar="true"
+            :lower-threshold="60"
+            @scrolltolower="onCategoryScrollToLower"
+          >
+            <view class="category-scroll-inner">
+              <view
+                v-for="cat in displaySidebarRows"
+                :key="cat.id"
+                class="category-item"
+                :class="{ active: selectedSecondCategoryId === cat.id }"
+                hover-class="category-item--hover"
+                :hover-stay-time="120"
+                @click="selectedSecondCategoryId = cat.id"
+              >
+                {{ cat.name }}
+              </view>
+              <view v-if="sidebarLoading && sidebarRows.length === 0" class="pane-footer-tip">加载中...</view>
+              <view v-else-if="sidebarHasMore" class="pane-footer-tip">上拉加载更多分类</view>
+            </view>
+          </scroll-view>
+
+          <view class="goods-pane">
+            <scroll-view
+              scroll-y
+              class="goods-scroll"
+              :show-scrollbar="true"
+              :lower-threshold="80"
+              @scrolltolower="onGoodsScrollToLower"
+            >
+              <view class="goods-scroll-inner">
+                <view
+                  v-for="product in products"
+                  :key="product.id"
+                  class="goods-card"
+                  hover-class="goods-card--pressed"
+                  :hover-stay-time="120"
+                  @click="goProductView(product.id)"
+                >
+                  <ClientImageLoader
+                    class="goods-image"
+                    :src="product.imageUrl || IMG_DEFAULT_PRODUCT"
+                    mode="aspectFill"
+                    radius="16rpx"
+                  />
+                  <view class="goods-main">
+                    <text class="goods-name">{{ product.name || '-' }}</text>
+                    <text class="goods-spec">{{ formatMeasureText(product.measureValue, product.unitName) }}</text>
+                    <view class="bottom-row">
+                      <text class="goods-price">¥{{ formatPrice(product.price || 0) }}</text>
+                      <view class="bottom-row-actions">
+                        <view v-if="getCartQuantity(product.id) === 0" class="add-btn" @click.stop="addToCart(product)">
+                          <text class="add-btn-plus">+</text>
+                        </view>
+                        <view v-else class="quantity-control" @click.stop>
+                          <view class="qty-btn" @click.stop="updateQuantity(product.id, -1)">
+                            <text>-</text>
+                          </view>
+                          <text class="qty-num">{{ getCartQuantity(product.id) }}</text>
+                          <view class="qty-btn" @click.stop="updateQuantity(product.id, 1)">
+                            <text>+</text>
+                          </view>
+                        </view>
+                      </view>
+                    </view>
+                  </view>
+                </view>
+
+                <view v-if="products.length === 0 && !productsLoadingMore" class="empty-list-tip">暂无匹配商品</view>
+                <view v-if="productsLoadingMore" class="pane-footer-tip">加载中...</view>
+                <view v-else-if="products.length > 0 && !productHasMore" class="pane-footer-tip">没有更多了</view>
+              </view>
+            </scroll-view>
+          </view>
+        </view>
+      </view>
+    </view>
+  </view>
+
+  <view class="cart-bar" @click="goToCart">
+    <view class="cart-inner">
+      <view class="cart-left">
+        <view class="cart-count">
+          <text>{{ cartStore.totalCount > 99 ? '99+' : cartStore.totalCount }}</text>
+        </view>
+        <view class="cart-text-col">
+          <text class="cart-items-label">已选商品</text>
+          <text class="cart-total-money">¥{{ formatPrice(cartStore.totalAmount) }}</text>
+        </view>
+      </view>
+      <view class="cart-settle">
+        <text>去结算</text>
+      </view>
+    </view>
+  </view>
+
+  <ProductDetailOverlay
+    :show="showProductOverlay"
+    :product-id="overlayProductId"
+    @close="closeProductOverlay"
+  />
+  <!-- #ifdef H5 -->
+  <ClientTabBar active="home" />
+  <!-- #endif -->
+  </view>
+</template>
+
+
+
 <script setup>
 import { ref, computed, onMounted, watch } from 'vue'
 import { onShow } from '@dcloudio/uni-app'
@@ -391,190 +582,7 @@ onShow(() => {
 })
 </script>
 
-<template>
-  <!-- 购物车条勿放在 inquiry-page 内:父级 overflow:hidden 会裁切角标;且 bottom 须与 tabBar 对齐 -->
-  <view class="home-root client-theme">
-    <view class="inquiry-page">
-      <view v-if="isLoading" class="state">
-        <ClientSkeleton variant="hero-list" :rows="3" />
-      </view>
-      <view v-else class="content-wrap">
-        <!-- 顶栏:仅渐变背景 + 配送行 + 搜索条(与 demo 一致) -->
-        <view class="filter-top-wrap">
-          <view class="top-row">
-            <text class="top-row-left">{{ deliveryLineLeft }}</text>
-            <text class="top-row-brand">叁强鲜配</text>
-          </view>
-          <view class="search-bar">
-            <input
-              v-model="keyword"
-              class="search-input"
-              type="text"
-              placeholder="搜索商品、品牌、一级/二级分类"
-              placeholder-class="search-placeholder"
-              confirm-type="search"
-              @confirm="onSearchClick"
-            />
-            <view v-if="keyword" class="search-clear" @click.stop="keyword = ''">×</view>
-            <text class="search-action" @click="onSearchClick">搜索</text>
-          </view>
-        </view>
-
-        <!-- 一级分类及以下在灰底区域,独立白卡片(与 demo 一致) -->
-        <view class="category-section">
-          <view class="section-head">
-            <text class="section-title">商品分类</text>
-          </view>
-
-          <view class="first-category-wrap">
-            <swiper
-              class="first-category-swiper"
-              :current="firstCategorySwiperIndex"
-              :circular="false"
-              :indicator-dots="false"
-              @change="onFirstCategorySwiperChange"
-            >
-              <swiper-item v-for="(page, pageIdx) in firstCategoryPages" :key="`category-page-${pageIdx}`">
-                <view class="first-category-grid">
-                  <view
-                    v-for="cat in page"
-                    :key="cat.id"
-                    class="first-category-card"
-                    :class="{ active: selectedFirstCategoryId === cat.id }"
-                    hover-class="first-category-card--hover"
-                    :hover-stay-time="120"
-                    @click="handleFirstCategoryClick(cat.id)"
-                  >
-                    <ClientImageLoader
-                      class="first-category-image"
-                      :src="cat.imageUrl || DEFAULT_PLACEHOLDER_IMAGE_URL"
-                      mode="aspectFill"
-                      radius="24rpx"
-                    />
-                    <text class="first-category-name">{{ cat.name }}</text>
-                  </view>
-                </view>
-              </swiper-item>
-            </swiper>
-          </view>
-
-          <view v-if="firstCategoryPages.length > 0" class="first-level-indicator">
-            <view
-              v-for="(_, idx) in firstCategoryPages"
-              :key="`dot-${idx}`"
-              class="indicator-dot"
-              :class="{ active: firstCategorySwiperIndex === idx }"
-              @click="goFirstCategorySwiperPage(idx)"
-            />
-          </view>
-        </view>
-
-        <view class="product-layout">
-          <view class="product-body">
-            <scroll-view
-              scroll-y
-              class="category-pane"
-              :show-scrollbar="true"
-              :lower-threshold="60"
-              @scrolltolower="onCategoryScrollToLower"
-            >
-              <view class="category-scroll-inner">
-                <view
-                  v-for="cat in displaySidebarRows"
-                  :key="cat.id"
-                  class="category-item"
-                  :class="{ active: selectedSecondCategoryId === cat.id }"
-                  hover-class="category-item--hover"
-                  :hover-stay-time="120"
-                  @click="selectedSecondCategoryId = cat.id"
-                >
-                  {{ cat.name }}
-                </view>
-                <view v-if="sidebarLoading && sidebarRows.length === 0" class="pane-footer-tip">加载中...</view>
-                <view v-else-if="sidebarHasMore" class="pane-footer-tip">上拉加载更多分类</view>
-              </view>
-            </scroll-view>
-
-            <view class="goods-pane">
-              <scroll-view
-                scroll-y
-                class="goods-scroll"
-                :show-scrollbar="true"
-                :lower-threshold="80"
-                @scrolltolower="onGoodsScrollToLower"
-              >
-                <view class="goods-scroll-inner">
-                  <view
-                    v-for="product in products"
-                    :key="product.id"
-                    class="goods-card"
-                    hover-class="goods-card--pressed"
-                    :hover-stay-time="120"
-                    @click="goProductView(product.id)"
-                  >
-                    <ClientImageLoader
-                      class="goods-image"
-                      :src="product.imageUrl || IMG_DEFAULT_PRODUCT"
-                      mode="aspectFill"
-                      radius="16rpx"
-                    />
-                    <view class="goods-main">
-                      <text class="goods-name">{{ product.name || '-' }}</text>
-                      <text class="goods-spec">{{ formatMeasureText(product.measureValue, product.unitName) }}</text>
-                      <view class="bottom-row">
-                        <text class="goods-price">¥{{ formatPrice(product.price || 0) }}</text>
-                        <view class="bottom-row-actions">
-                          <view v-if="getCartQuantity(product.id) === 0" class="add-btn" @click.stop="addToCart(product)">
-                            <text class="add-btn-plus">+</text>
-                          </view>
-                          <view v-else class="quantity-control" @click.stop>
-                            <view class="qty-btn" @click.stop="updateQuantity(product.id, -1)">
-                              <text>-</text>
-                            </view>
-                            <text class="qty-num">{{ getCartQuantity(product.id) }}</text>
-                            <view class="qty-btn" @click.stop="updateQuantity(product.id, 1)">
-                              <text>+</text>
-                            </view>
-                          </view>
-                        </view>
-                      </view>
-                    </view>
-                  </view>
-
-                  <view v-if="products.length === 0 && !productsLoadingMore" class="empty-list-tip">暂无匹配商品</view>
-                  <view v-if="productsLoadingMore" class="pane-footer-tip">加载中...</view>
-                  <view v-else-if="products.length > 0 && !productHasMore" class="pane-footer-tip">没有更多了</view>
-                </view>
-              </scroll-view>
-            </view>
-          </view>
-        </view>
-      </view>
-    </view>
 
-    <view class="cart-bar" @click="goToCart">
-      <view class="cart-inner">
-        <view class="cart-left">
-          <view class="cart-count">
-            <text>{{ cartStore.totalCount > 99 ? '99+' : cartStore.totalCount }}</text>
-          </view>
-          <view class="cart-text-col">
-            <text class="cart-items-label">已选商品</text>
-            <text class="cart-total-money">¥{{ formatPrice(cartStore.totalAmount) }}</text>
-          </view>
-        </view>
-        <view class="cart-settle">
-          <text>去结算</text>
-        </view>
-      </view>
-    </view>
-
-    <ProductDetailOverlay :show="showProductOverlay" :product-id="overlayProductId" @close="closeProductOverlay" />
-    <!-- #ifdef H5 -->
-    <ClientTabBar active="home" />
-    <!-- #endif -->
-  </view>
-</template>
 
 <style lang="scss">
 /* 仅影响当前页:高度随 tab 内容区,禁止页面级纵向滚动(不写全局 body overflow,避免污染其它路由) */
@@ -584,6 +592,7 @@ page {
   background-color: #f5f8f7;
 }
 
+/* input placeholder-class 在部分端需非 scoped 才能命中 */
 .search-placeholder {
   color: #8aa39a;
 }
@@ -648,12 +657,14 @@ page {
 .content-wrap {
   flex: 1;
   min-height: 0;
+  /* demo .section 水平 16px → 32rpx */
   padding: 0 32rpx;
   display: flex;
   flex-direction: column;
   box-sizing: border-box;
 }
 
+/* 对齐 mobile-ui/sanqiang-client-ui-demo.html .top */
 .filter-top-wrap {
   flex-shrink: 0;
   background: linear-gradient(
@@ -662,18 +673,18 @@ page {
     var(--c-brand-green-2) 70%,
     var(--c-barley-yellow) 160%
   );
+  box-shadow: var(--c-shadow);
   border-bottom-left-radius: 44rpx;
   border-bottom-right-radius: 44rpx;
   padding: 28rpx 32rpx 32rpx;
   margin: 0 -32rpx 0;
-  box-shadow: var(--c-shadow);
 }
 
 .top-row {
   display: flex;
   align-items: center;
   justify-content: space-between;
-  color: #fff;
+  color: #ffffff;
   font-size: 28rpx;
   line-height: 1.35;
   margin-bottom: 20rpx;
@@ -715,7 +726,10 @@ page {
   color: var(--c-text);
   background: transparent;
   border: none;
-  box-sizing: border-box;
+}
+
+.search-placeholder {
+  color: #8aa39a;
 }
 
 .search-clear {
@@ -736,12 +750,15 @@ page {
 .search-action {
   flex-shrink: 0;
   padding: 0 16rpx 0 8rpx;
-  color: var(--c-brand-green);
   font-size: 24rpx;
+  color: var(--c-brand-green);
   font-weight: 600;
 }
 
+/* demo .section 内:标题 + 一级分类白卡片 */
 .category-section {
+  flex-shrink: 0;
+  /* demo .section margin 12px 0 顶 */
   margin-top: 24rpx;
 }
 
@@ -753,6 +770,7 @@ page {
 }
 
 .section-title {
+  /* demo h3 17px */
   font-size: 34rpx;
   font-weight: 600;
   color: var(--c-text);
@@ -768,6 +786,7 @@ page {
 }
 
 .first-category-swiper {
+  /* 收紧一级分类区高度,避免短文案卡片出现大块空白 */
   height: 336rpx;
 }
 
@@ -782,6 +801,10 @@ page {
   align-content: start;
 }
 
+.first-category-grid--single {
+  min-height: auto;
+}
+
 .first-category-card {
   display: flex;
   flex-direction: column;
@@ -808,6 +831,7 @@ page {
 }
 
 .first-category-image {
+  /* demo .first-chip::before 42×42,圆角 12px */
   width: 84rpx;
   height: 84rpx;
   border-radius: 24rpx;
@@ -820,9 +844,11 @@ page {
 }
 
 .first-category-name {
+  /* demo .first-chip gap 8px + font 11px */
   margin-top: 8rpx;
   width: 100%;
   font-size: 22rpx;
+  line-height: 1.2;
   color: #4f665d;
   text-align: center;
   overflow: hidden;
@@ -834,16 +860,6 @@ page {
   color: var(--c-brand-green);
 }
 
-.product-layout {
-  flex: 1;
-  min-height: 0;
-  margin-top: 20rpx;
-  margin-left: -32rpx;
-  margin-right: -32rpx;
-  width: auto;
-  align-self: stretch;
-}
-
 .first-level-indicator {
   display: flex;
   justify-content: center;
@@ -865,10 +881,23 @@ page {
   background: linear-gradient(145deg, var(--c-brand-green-2), var(--c-brand-green));
 }
 
+.product-layout {
+  flex: 1;
+  min-height: 0;
+  /* demo .category-layout margin-top 10px */
+  margin-top: 20rpx;
+  /* 二级分类 + 商品区通栏:抵消 content-wrap 左右 32rpx,商品列更宽 */
+  margin-left: -32rpx;
+  margin-right: -32rpx;
+  width: auto;
+  align-self: stretch;
+}
+
 .product-body {
   display: flex;
   height: 100%;
   min-height: 0;
+  /* demo .category-layout min-height 440px */
   min-height: 880rpx;
   border: 1rpx solid var(--c-border);
   border-radius: var(--c-radius-lg);
@@ -877,6 +906,7 @@ page {
 }
 
 .category-pane {
+  /* demo grid 92px 侧栏 */
   width: 184rpx;
   flex-shrink: 0;
   height: 100%;
@@ -920,6 +950,7 @@ page {
   flex: 1;
   min-width: 0;
   background: #ffffff;
+  /* 对齐设计图 goods-area padding:10px */
   padding: 16rpx 0 0;
   display: flex;
   flex-direction: column;
@@ -935,6 +966,7 @@ page {
   height: 100%;
 }
 
+/* 内层包裹:各端对 scroll-view 直接 padding 支持不一致,用内层垫高更稳 */
 .goods-scroll-inner {
   padding-left: 20rpx;
   padding-right: 20rpx;
@@ -958,6 +990,7 @@ page {
 
 .goods-card {
   display: grid;
+  /* 窄屏:缩略图列随屏宽比例收缩;宽屏仍接近 demo 约 92px/侧 */
   grid-template-columns: minmax(112rpx, 36%) minmax(0, 1fr);
   gap: min(16rpx, 2.5vw);
   align-items: stretch;
@@ -1018,6 +1051,7 @@ page {
 }
 
 .bottom-row {
+  /* 栅格:左侧价格区可收缩省略,右侧操作区独占一列,避免 flex 与 text 组件叠在一起 */
   display: grid;
   grid-template-columns: minmax(0, 1fr) auto;
   align-items: center;
@@ -1064,6 +1098,7 @@ page {
   line-height: 1;
 }
 
+/* 「+」在圆内几何居中:抵消 text 默认行高/基线导致的视觉偏移 */
 .add-btn-plus {
   line-height: 1;
   font-size: inherit;
@@ -1106,16 +1141,18 @@ page {
   text-overflow: ellipsis;
 }
 
+// 底部购物车:对齐 demo .cart-bar / .cart-inner / .settle
 .cart-bar {
   position: fixed;
+  left: 50%;
+  transform: translateX(-50%);
   z-index: 10010;
   width: 100%;
   max-width: 840rpx;
   padding: 0 24rpx;
   box-sizing: border-box;
   overflow: visible;
-  left: 50%;
-  transform: translateX(-50%);
+  /* demo .cart-bar bottom:72px — 在 tab 之上留出空隙;与 uni --window-bottom 叠加 */
   /* #ifdef H5 */
   bottom: calc(var(--window-bottom, calc(50px + env(safe-area-inset-bottom, 0px))) + 44rpx);
   /* #endif */

+ 53 - 49
sqxp-wx/pages/client/invoice/invoice.vue

@@ -1,3 +1,56 @@
+<template>
+  <view class="page client-theme client-subpage">
+    <view class="client-subpage-hero invoice-hero">
+      <text class="client-subpage-hero__title">发票管理</text>
+      <text class="client-subpage-hero__sub">申请开票与查看开票记录</text>
+      <button class="apply-btn" @click="openApply">申请开票</button>
+    </view>
+
+    <scroll-view scroll-y class="client-subpage-scroll invoice-scroll" :show-scrollbar="true">
+      <view class="client-subpage-body invoice-body">
+        <view v-if="loading && list.length === 0" class="hint">加载中...</view>
+        <view v-else-if="list.length === 0" class="empty">暂无发票记录</view>
+
+        <view v-for="row in list" :key="row.id" class="card" hover-class="card--pressed" :hover-stay-time="100">
+      <view class="row">
+        <text class="no">{{ row.invoiceNo }}</text>
+        <text class="st">{{ statusText[row.status] || row.status }}</text>
+      </view>
+      <text class="sub">订单金额(元) ¥{{ formatYuan(row.invoiceAmount) }}</text>
+      <text class="sub">抬头 {{ row.title }}</text>
+      <text class="time">{{ row.createAt }}</text>
+        </view>
+      </view>
+    </scroll-view>
+
+    <view v-if="showApply" class="mask" @click.self="showApply = false">
+      <view class="dialog" @click.stop>
+        <text class="dlg-title">申请开票</text>
+        <picker
+          v-if="availOrders.length"
+          mode="selector"
+          :range="availOrders"
+          range-key="orderNo"
+          :value="pickerIndex"
+          @change="onPickerChange"
+        >
+          <view class="picker">
+            订单:{{ availOrders[pickerIndex]?.orderNo || '选择订单' }}
+          </view>
+        </picker>
+        <text v-else class="hint">暂无可开票订单(需已完成且无发票)</text>
+        <input v-model="applyTitle" class="input" placeholder="发票抬头,默认「个人」" />
+        <view class="dlg-actions">
+          <button class="btn plain" @click="showApply = false">取消</button>
+          <button class="btn primary" @click="submitApply">提交</button>
+        </view>
+      </view>
+    </view>
+  </view>
+</template>
+
+
+
 <script setup>
 import { ref, onMounted } from 'vue'
 import { useUserStore } from '@/stores/user'
@@ -115,56 +168,7 @@ onMounted(() => {
 })
 </script>
 
-<template>
-  <view class="page client-theme client-subpage">
-    <view class="client-subpage-hero invoice-hero">
-      <text class="client-subpage-hero__title">发票管理</text>
-      <text class="client-subpage-hero__sub">申请开票与查看开票记录</text>
-      <button class="apply-btn" @click="openApply">申请开票</button>
-    </view>
 
-    <scroll-view scroll-y class="client-subpage-scroll invoice-scroll" :show-scrollbar="true">
-      <view class="client-subpage-body invoice-body">
-        <view v-if="loading && list.length === 0" class="hint">加载中...</view>
-        <view v-else-if="list.length === 0" class="empty">暂无发票记录</view>
-
-        <view v-for="row in list" :key="row.id" class="card" hover-class="card--pressed" :hover-stay-time="100">
-          <view class="row">
-            <text class="no">{{ row.invoiceNo }}</text>
-            <text class="st">{{ statusText[row.status] || row.status }}</text>
-          </view>
-          <text class="sub">订单金额(元) ¥{{ formatYuan(row.invoiceAmount) }}</text>
-          <text class="sub">抬头 {{ row.title }}</text>
-          <text class="time">{{ row.createAt }}</text>
-        </view>
-      </view>
-    </scroll-view>
-
-    <view v-if="showApply" class="mask" @click.self="showApply = false">
-      <view class="dialog" @click.stop>
-        <text class="dlg-title">申请开票</text>
-        <picker
-          v-if="availOrders.length"
-          mode="selector"
-          :range="availOrders"
-          range-key="orderNo"
-          :value="pickerIndex"
-          @change="onPickerChange"
-        >
-          <view class="picker">
-            订单:{{ availOrders[pickerIndex]?.orderNo || '选择订单' }}
-          </view>
-        </picker>
-        <text v-else class="hint">暂无可开票订单(需已完成且无发票)</text>
-        <input v-model="applyTitle" class="input" placeholder="发票抬头,默认「个人」" />
-        <view class="dlg-actions">
-          <button class="btn plain" @click="showApply = false">取消</button>
-          <button class="btn primary" @click="submitApply">提交</button>
-        </view>
-      </view>
-    </view>
-  </view>
-</template>
 
 <style lang="scss" scoped>
 .page {

+ 136 - 126
sqxp-wx/pages/client/order/create/create.vue

@@ -1,3 +1,135 @@
+<template>
+  <view class="page client-theme client-subpage">
+    <view v-if="pageLoading" class="page-loading-wrap">
+      <ClientSkeleton variant="detail" :rows="3" />
+    </view>
+    <template v-else>
+    <view class="client-subpage-hero create-hero">
+      <text class="client-subpage-hero__title">确认订单</text>
+      <text class="client-subpage-hero__sub">核对商品、地址与支付方式</text>
+    </view>
+    <scroll-view scroll-y class="create-scroll" :show-scrollbar="true" :enable-flex="true">
+      <view class="create-body">
+    <view class="address-card">
+      <view class="addr-left">
+        <image class="addr-icon" :src="ICON_COORDINATE" mode="aspectFit" />
+      </view>
+      <view class="addr-main">
+        <view class="addr-top">
+          <text class="addr-title">收货地址</text>
+          <text class="addr-edit" @click.stop="goSelectAddress">修改 ›</text>
+        </view>
+        <view v-if="selectedAddress" class="addr-content">
+          <view class="addr-person-row">
+            <text class="addr-person-name">{{ selectedAddress.contactPerson }}</text>
+            <text class="addr-phone">{{ selectedAddress.contactPhone }}</text>
+          </view>
+          <text class="addr-detail">{{ fullAddress(selectedAddress) }}</text>
+        </view>
+        <view v-else class="addr-empty">
+          <text class="addr-empty-text">请先添加收货地址</text>
+        </view>
+      </view>
+    </view>
+    <view class="address-card">
+      <view class="addr-main">
+        <view class="addr-top">
+          <text class="addr-title">支付方式</text>
+        </view>
+        <radio-group class="pay-group" @change="onPaymentMethodChange">
+          <label class="pay-row">
+            <text class="pay-text">线上支付(30分钟未支付,自动取消)</text>
+            <radio value="online" :checked="paymentMethod === 'online'" />
+          </label>
+          <label class="pay-row">
+            <text class="pay-text">线下支付</text>
+            <radio value="offline" :checked="paymentMethod === 'offline'" />
+          </label>
+        </radio-group>
+      </view>
+    </view>
+
+    <view class="section">
+      <view class="section-title"><text>商品明细</text></view>
+
+      <view v-if="cartItemsForOrder.length === 0" class="empty-box">
+        <text>购物车为空</text>
+      </view>
+
+      <view v-else>
+        <view v-for="item in cartItemsForOrder" :key="item.id" class="cart-item order-line">
+          <view class="item-image sq-product-thumb sq-product-thumb--sm" @click="goProductView(item.productId)">
+            <ClientImageLoader :src="item.imageUrl || IMG_DEFAULT_PRODUCT" mode="aspectFill" radius="20rpx" />
+          </view>
+
+          <view class="item-info" @click="goProductView(item.productId)">
+            <text class="item-name">{{ item.productName }}</text>
+            <text class="item-unit">{{ formatMeasureText(item.measureValue, item.unitName) }}</text>
+
+            <view class="item-bottom">
+              <text class="item-price">¥{{ formatPrice(item.price) }}</text>
+              <view class="quantity-control">
+                <view class="qty-btn" @click.stop="updateQuantity(item.id, -1)">
+                  <text>-</text>
+                </view>
+                <text class="qty-num">{{ item.quantity }}</text>
+                <view class="qty-btn" @click.stop="updateQuantity(item.id, 1)">
+                  <text>+</text>
+                </view>
+              </view>
+            </view>
+          </view>
+
+          <view class="item-delete" @click.stop="removeItem(item.id)">
+            <image class="item-delete-icon" :src="ICON_DELETE" mode="aspectFit" />
+          </view>
+        </view>
+
+        <view class="total-row">
+          <text>合计(元)</text>
+          <text class="total">¥{{ totalText }}</text>
+        </view>
+      </view>
+    </view>
+
+    <view class="section">
+      <view class="section-title"><text>备注</text></view>
+      <textarea
+        v-model="remark"
+        class="remark"
+        placeholder="选填:配送说明等"
+        maxlength="200"
+      />
+    </view>
+      </view>
+    </scroll-view>
+
+    <view class="footer">
+      <view class="footer-sum">
+        <text class="label">应付(元)</text>
+        <text class="price">¥{{ totalText }}</text>
+      </view>
+      <van-button
+        class="submit-btn"
+        round
+        type="primary"
+        :disabled="submitting || !selectedAddressId || cartItemsForOrder.length === 0"
+        @click="submitOrder"
+      >
+        {{ submitting ? '提交中...' : '提交订单' }}
+      </van-button>
+    </view>
+
+    <ProductDetailOverlay
+      :show="showProductOverlay"
+      :product-id="overlayProductId"
+      bottom-mode="cart"
+      @close="closeProductOverlay"
+    />
+    </template>
+
+
+
 <script setup>
 import { ref, computed, onMounted } from 'vue'
 import { onLoad, onShow } from '@dcloudio/uni-app'
@@ -258,129 +390,7 @@ onLoad((options) => {
 })
 </script>
 
-<template>
-  <view class="page client-theme client-subpage">
-    <view v-if="pageLoading" class="page-loading-wrap">
-      <ClientSkeleton variant="detail" :rows="3" />
-    </view>
-    <template v-else>
-      <view class="client-subpage-hero create-hero">
-        <text class="client-subpage-hero__title">确认订单</text>
-        <text class="client-subpage-hero__sub">核对商品、地址与支付方式</text>
-      </view>
-
-      <scroll-view scroll-y class="create-scroll" :show-scrollbar="true" :enable-flex="true">
-        <view class="create-body">
-          <view class="address-card">
-            <view class="addr-left">
-              <image class="addr-icon" :src="ICON_COORDINATE" mode="aspectFit" />
-            </view>
-            <view class="addr-main">
-              <view class="addr-top">
-                <text class="addr-title">收货地址</text>
-                <text class="addr-edit" @click.stop="goSelectAddress">修改 ›</text>
-              </view>
-              <view v-if="selectedAddress" class="addr-content">
-                <view class="addr-person-row">
-                  <text class="addr-person-name">{{ selectedAddress.contactPerson }}</text>
-                  <text class="addr-phone">{{ selectedAddress.contactPhone }}</text>
-                </view>
-                <text class="addr-detail">{{ fullAddress(selectedAddress) }}</text>
-              </view>
-              <view v-else class="addr-empty">
-                <text class="addr-empty-text">请先添加收货地址</text>
-              </view>
-            </view>
-          </view>
-
-          <view class="address-card">
-            <view class="addr-main">
-              <view class="addr-top">
-                <text class="addr-title">支付方式</text>
-              </view>
-              <radio-group class="pay-group" @change="onPaymentMethodChange">
-                <label class="pay-row">
-                  <text class="pay-text">线上支付(30分钟未支付,自动取消)</text>
-                  <radio value="online" :checked="paymentMethod === 'online'" />
-                </label>
-                <label class="pay-row">
-                  <text class="pay-text">线下支付</text>
-                  <radio value="offline" :checked="paymentMethod === 'offline'" />
-                </label>
-              </radio-group>
-            </view>
-          </view>
-
-          <view class="section">
-            <view class="section-title"><text>商品明细</text></view>
 
-            <view v-if="cartItemsForOrder.length === 0" class="empty-box">
-              <text>购物车为空</text>
-            </view>
-
-            <view v-else>
-              <view v-for="item in cartItemsForOrder" :key="item.id" class="cart-item order-line">
-                <view class="item-image sq-product-thumb sq-product-thumb--sm" @click="goProductView(item.productId)">
-                  <ClientImageLoader :src="item.imageUrl || IMG_DEFAULT_PRODUCT" mode="aspectFill" radius="20rpx" />
-                </view>
-
-                <view class="item-info" @click="goProductView(item.productId)">
-                  <text class="item-name">{{ item.productName }}</text>
-                  <text class="item-unit">{{ formatMeasureText(item.measureValue, item.unitName) }}</text>
-
-                  <view class="item-bottom">
-                    <text class="item-price">¥{{ formatPrice(item.price) }}</text>
-                    <view class="quantity-control" @click.stop>
-                      <view class="qty-btn" @click.stop="updateQuantity(item.id, -1)">
-                        <text>-</text>
-                      </view>
-                      <text class="qty-num">{{ item.quantity }}</text>
-                      <view class="qty-btn" @click.stop="updateQuantity(item.id, 1)">
-                        <text>+</text>
-                      </view>
-                    </view>
-                  </view>
-                </view>
-
-                <view class="item-delete" @click.stop="removeItem(item.id)">
-                  <image class="item-delete-icon" :src="ICON_DELETE" mode="aspectFit" />
-                </view>
-              </view>
-
-              <view class="total-row">
-                <text>合计(元)</text>
-                <text class="total">¥{{ totalText }}</text>
-              </view>
-            </view>
-          </view>
-
-          <view class="section">
-            <view class="section-title"><text>备注</text></view>
-            <textarea v-model="remark" class="remark" placeholder="选填:配送说明等" maxlength="200" />
-          </view>
-        </view>
-      </scroll-view>
-
-      <view class="footer">
-        <view class="footer-sum">
-          <text class="label">应付(元)</text>
-          <text class="price">¥{{ totalText }}</text>
-        </view>
-        <van-button
-          class="submit-btn"
-          round
-          type="primary"
-          :disabled="submitting || !selectedAddressId || cartItemsForOrder.length === 0"
-          @click="submitOrder"
-        >
-          {{ submitting ? '提交中...' : '提交订单' }}
-        </van-button>
-      </view>
-
-      <ProductDetailOverlay :show="showProductOverlay" :product-id="overlayProductId" bottom-mode="cart" @close="closeProductOverlay" />
-    </template>
-  </view>
-</template>
 
 <style lang="scss" scoped>
 .page {
@@ -515,7 +525,7 @@ onLoad((options) => {
   display: flex;
   padding: 20rpx 0;
   position: relative;
-  border-bottom: 1rpx solid #f0f2f5;
+  border-bottom: 1rpx solid rgba(220, 235, 228, 0.65);
 
   &:last-child {
     border-bottom: none;
@@ -634,7 +644,7 @@ onLoad((options) => {
   display: flex;
   align-items: flex-start;
   padding: 16rpx 0;
-  border-bottom: 1rpx solid #f5f5f5;
+  border-bottom: 1rpx solid rgba(220, 235, 228, 0.65);
 }
 .addr-info {
   flex: 1;
@@ -655,11 +665,11 @@ onLoad((options) => {
   display: flex;
   justify-content: space-between;
   padding: 16rpx 0;
-  border-bottom: 1rpx solid #f5f5f5;
+  border-bottom: 1rpx solid rgba(220, 235, 228, 0.65);
 }
 .line-main .name {
   font-size: 28rpx;
-  color: #303133;
+  color: var(--c-text);
   display: block;
 }
 .line-main .sub {

+ 140 - 117
sqxp-wx/pages/client/order/order.vue

@@ -1,3 +1,126 @@
+<template>
+  <view class="order-page client-theme">
+    <view class="order-top">
+      <text class="order-page-title">我的订单</text>
+      <scroll-view scroll-x class="order-tabs-scroll" :show-scrollbar="false" :enable-flex="true">
+        <view class="order-tabs-inner">
+          <view
+            v-for="tab in TAB_KEYS"
+            :key="tab"
+            class="tab-item"
+            :class="{ active: activeTab === tab }"
+            hover-class="tab-item--hover"
+            :hover-stay-time="100"
+            @click="switchTab(tab)"
+          >
+            <text>
+              {{
+                tab === 'all'
+                  ? '全部'
+                  : tab === 'pending_payment'
+                    ? '待支付'
+                    : tab === 'pending_confirm'
+                      ? '待收货'
+                      : tab === 'completed'
+                        ? '已完成'
+                        : '已取消'
+              }}
+            </text>
+          </view>
+        </view>
+      </scroll-view>
+    </view>
+
+    <scroll-view
+      class="order-list"
+      scroll-y
+      :enable-flex="true"
+      :lower-threshold="100"
+      @scrolltolower="onScrollToLower"
+    >
+      <ClientSkeleton
+        v-if="activeTabState.loading && activeTabState.list.length === 0"
+        variant="list"
+        :rows="3"
+      />
+      <view
+        v-else
+        v-for="order in activeTabState.list"
+        :key="order.id"
+        class="order-card"
+        hover-class="order-card--pressed"
+        :hover-stay-time="120"
+        @click="goToDetail(order.id)"
+      >
+        <view class="order-header">
+          <text class="order-no">订单号:{{ order.orderNo }}</text>
+          <view :class="['status-tag', 'status-' + getOrderStatusClass(order)]">
+            <text>{{ getOrderStatusText(order) }}</text>
+          </view>
+        </view>
+
+        <!-- 不用横向 scroll-view:H5 嵌套「纵 scroll-view + 横 scroll-view」时 uni 会写 scrollLeft,DOM 未就绪会报错 -->
+        <view v-if="(order.items || []).length" class="product-scroll">
+          <view class="product-scroll-inner">
+            <view
+              v-for="item in (order.items || [])"
+              :key="item.id"
+              class="product-card"
+            >
+              <ClientImageLoader
+                class="product-image"
+                :src="getItemImageUrl(item)"
+                mode="aspectFill"
+                radius="20rpx"
+              />
+              <view class="product-meta">
+                <text class="product-name">{{ item.productName }}</text>
+                <text class="product-qty">x{{ item.quantity }}</text>
+              </view>
+            </view>
+          </view>
+        </view>
+
+        <view class="order-footer">
+          <text class="order-time">{{ formatDate(order.createAt) }}</text>
+          <view class="order-amount">
+            <text class="amount-count">共 {{ getOrderTotalQuantity(order) }} 件</text>
+            <text class="sum-line">
+              合计:<text class="sum-value">¥{{ formatPrice(order.totalAmount) }}</text>
+            </text>
+          </view>
+        </view>
+      </view>
+
+      <view v-if="activeTabState.list.length === 0 && !activeTabState.loading" class="empty-state">
+        <image class="empty-image" :src="IMG_EMPTY" mode="aspectFit" />
+        <text class="empty-text">暂无订单</text>
+      </view>
+
+      <view v-if="activeTabState.loading && activeTabState.list.length > 0" class="loading-more">
+        <text>加载中...</text>
+      </view>
+      <view
+        v-else-if="
+          activeTabState.initialLoaded &&
+          activeTabState.total > 0 &&
+          (activeTabState.totalPages > 0
+            ? activeTabState.page >= activeTabState.totalPages
+            : activeTabState.list.length >= activeTabState.total)
+        "
+        class="nomore"
+      >
+        <text>没有更多了</text>
+      </view>
+    </scroll-view>
+    <!-- #ifdef H5 -->
+    <ClientTabBar active="order" />
+    <!-- #endif -->
+  </view>
+</template>
+
+
+
 <script setup>
 import { computed, reactive, ref } from 'vue'
 import { onShow } from '@dcloudio/uni-app'
@@ -228,121 +351,12 @@ onShow(() => {
 })
 </script>
 
-<template>
-  <view class="order-page client-theme">
-    <view class="order-top">
-      <text class="order-page-title">我的订单</text>
-      <scroll-view scroll-x class="order-tabs-scroll" :show-scrollbar="false" :enable-flex="true">
-        <view class="order-tabs-inner">
-          <view
-            v-for="tab in TAB_KEYS"
-            :key="tab"
-            class="tab-item"
-            :class="{ active: activeTab === tab }"
-            hover-class="tab-item--hover"
-            :hover-stay-time="100"
-            @click="switchTab(tab)"
-          >
-            <text>
-              {{ tab === 'all' ? '全部' : tab === 'pending_payment' ? '待支付' : tab === 'pending_confirm' ? '待收货' : tab === 'completed' ? '已完成' : '已取消' }}
-            </text>
-          </view>
-        </view>
-      </scroll-view>
-    </view>
-
-    <scroll-view
-      class="order-list"
-      scroll-y
-      :enable-flex="true"
-      :lower-threshold="100"
-      @scrolltolower="onScrollToLower"
-    >
-      <ClientSkeleton
-        v-if="activeTabState.loading && activeTabState.list.length === 0"
-        variant="list"
-        :rows="3"
-      />
-      <view
-        v-else
-        v-for="order in activeTabState.list"
-        :key="order.id"
-        class="order-card"
-        hover-class="order-card--pressed"
-        :hover-stay-time="120"
-        @click="goToDetail(order.id)"
-      >
-        <view class="order-header">
-          <text class="order-no">订单号:{{ order.orderNo }}</text>
-          <view :class="['status-tag', 'status-' + getOrderStatusClass(order)]">
-            <text>{{ getOrderStatusText(order) }}</text>
-          </view>
-        </view>
 
-        <!-- 不用横向 scroll-view:H5 嵌套「纵 scroll-view + 横 scroll-view」时 uni 会写 scrollLeft,DOM 未就绪会报错 -->
-        <view v-if="(order.items || []).length" class="product-scroll">
-          <view class="product-scroll-inner">
-            <view
-              v-for="item in (order.items || [])"
-              :key="item.id"
-              class="product-card"
-            >
-              <ClientImageLoader
-                class="product-image"
-                :src="getItemImageUrl(item)"
-                mode="aspectFill"
-                radius="20rpx"
-              />
-              <view class="product-meta">
-                <text class="product-name">{{ item.productName }}</text>
-                <text class="product-qty">x{{ item.quantity }}</text>
-              </view>
-            </view>
-          </view>
-        </view>
-
-        <view class="order-footer">
-          <text class="order-time">{{ formatDate(order.createAt) }}</text>
-          <view class="order-amount">
-            <text class="amount-count">共 {{ getOrderTotalQuantity(order) }} 件</text>
-            <text class="sum-line">
-              合计:<text class="sum-value">¥{{ formatPrice(order.totalAmount) }}</text>
-            </text>
-          </view>
-        </view>
-      </view>
-
-      <view v-if="activeTabState.list.length === 0 && !activeTabState.loading" class="empty-state">
-        <image class="empty-image" :src="IMG_EMPTY" mode="aspectFit" />
-        <text class="empty-text">暂无订单</text>
-      </view>
-
-      <view v-if="activeTabState.loading && activeTabState.list.length > 0" class="loading-more">
-        <text>加载中...</text>
-      </view>
-      <view
-        v-else-if="
-          activeTabState.initialLoaded &&
-          activeTabState.total > 0 &&
-          (activeTabState.totalPages > 0
-            ? activeTabState.page >= activeTabState.totalPages
-            : activeTabState.list.length >= activeTabState.total)
-        "
-        class="nomore"
-      >
-        <text>没有更多了</text>
-      </view>
-    </scroll-view>
-    <!-- #ifdef H5 -->
-    <ClientTabBar active="order" />
-    <!-- #endif -->
-  </view>
-</template>
 
 <style lang="scss" scoped>
 .order-page {
-  height: 100vh;
-  min-height: 100vh;
+  height: 100%;
+  min-height: 0;
   display: flex;
   flex-direction: column;
   background: var(--c-bg);
@@ -363,8 +377,9 @@ onShow(() => {
   display: block;
   font-size: 36rpx;
   font-weight: 700;
-  color: #fff;
+  color: #ffffff;
   margin-bottom: 20rpx;
+  line-height: 1.25;
 }
 
 .order-tabs-scroll {
@@ -377,6 +392,7 @@ onShow(() => {
   align-items: center;
   gap: 16rpx;
   padding: 4rpx 0 2rpx;
+  box-sizing: border-box;
 }
 
 .tab-item {
@@ -391,12 +407,13 @@ onShow(() => {
   border: 1rpx solid rgba(255, 255, 255, 0.35);
   border-radius: 34rpx;
   box-sizing: border-box;
+  transition: opacity 0.15s ease;
 
   &.active {
-    background: #fff;
+    background: #ffffff;
     color: var(--c-brand-green);
     font-weight: 600;
-    border-color: #fff;
+    border-color: #ffffff;
   }
 }
 
@@ -406,7 +423,9 @@ onShow(() => {
 
 .order-list {
   flex: 1;
+  flex-basis: 0;
   min-height: 0;
+  height: 0;
   padding: 24rpx 32rpx 32rpx;
   width: 100%;
   box-sizing: border-box;
@@ -423,6 +442,7 @@ onShow(() => {
   width: 100%;
   max-width: 100%;
   box-sizing: border-box;
+  transition: opacity 0.15s ease;
 
   .order-header {
     display: flex;
@@ -434,8 +454,8 @@ onShow(() => {
       flex: 1;
       min-width: 0;
       font-size: 26rpx;
-      color: var(--c-text);
       font-weight: 600;
+      color: var(--c-text);
       overflow: hidden;
       text-overflow: ellipsis;
       white-space: nowrap;
@@ -467,7 +487,7 @@ onShow(() => {
 
       &.status-info {
         background: #f4f4f5;
-        color: #909399;
+        color: var(--c-muted);
       }
     }
   }
@@ -524,7 +544,7 @@ onShow(() => {
       flex: 1;
       min-width: 0;
       font-size: 24rpx;
-      color: #fff;
+      color: #ffffff;
       overflow: hidden;
       text-overflow: ellipsis;
       white-space: nowrap;
@@ -548,6 +568,8 @@ onShow(() => {
     .order-time {
       font-size: 22rpx;
       color: var(--c-muted);
+      line-height: 1.45;
+      max-width: 62%;
     }
 
     .order-amount {
@@ -603,6 +625,7 @@ onShow(() => {
   }
 }
 
+.loading-state,
 .loading-more,
 .nomore {
   display: flex;

+ 49 - 45
sqxp-wx/pages/client/order/pay/pay.vue

@@ -1,3 +1,48 @@
+<template>
+  <view class="page client-theme client-subpage">
+    <view class="client-subpage-hero pay-hero">
+      <text class="client-subpage-hero__title">订单支付</text>
+      <text class="client-subpage-hero__sub">请尽快完成支付,超时订单将取消</text>
+    </view>
+    <view class="client-subpage-body pay-body">
+    <view v-if="showCreateSuccessBanner" class="success-banner">
+      <text class="success-icon">✅</text>
+      <view class="success-text">
+        <text class="success-title">订单下单成功</text>
+      </view>
+    </view>
+
+    <view v-if="loading" class="loading">
+      <ClientSkeleton variant="detail" :rows="2" />
+    </view>
+    <view v-else class="card">
+      <text class="label">订单号</text>
+      <text class="value">{{ orderNo || '—' }}</text>
+      <text class="label mt">下单时间</text>
+      <text class="value">{{ formatDateTime(createAt) || '—' }}</text>
+      <text class="label mt">应付金额(元)</text>
+      <text class="amount">¥{{ amountText }}</text>
+      <text class="status">状态:{{ statusText }}</text>
+    </view>
+    <view class="actions">
+      <van-button
+        v-if="paymentMethod === 'online'"
+        round
+        class="btn secondary"
+        :disabled="!canPay || paying"
+        :loading="canPay && paying"
+        @click="requestPay"
+      >
+        微信支付
+      </van-button>
+      <van-button round class="btn primary" type="primary" @click="goContinueShopping">继续购物</van-button>
+    </view>
+    </view>
+  </view>
+</template>
+
+
+
 <script setup>
 import { ref, computed } from 'vue'
 import { onLoad } from '@dcloudio/uni-app'
@@ -194,48 +239,7 @@ onLoad((query) => {
 })
 </script>
 
-<template>
-  <view class="page client-theme client-subpage">
-    <view class="client-subpage-hero pay-hero">
-      <text class="client-subpage-hero__title">订单支付</text>
-      <text class="client-subpage-hero__sub">请尽快完成支付,超时订单将取消</text>
-    </view>
-    <view class="client-subpage-body pay-body">
-      <view v-if="showCreateSuccessBanner" class="success-banner">
-        <text class="success-icon">✅</text>
-        <view class="success-text">
-          <text class="success-title">订单下单成功</text>
-        </view>
-      </view>
 
-      <view v-if="loading" class="loading">
-        <ClientSkeleton variant="detail" :rows="2" />
-      </view>
-      <view v-else class="card">
-        <text class="label">订单号</text>
-        <text class="value">{{ orderNo || '—' }}</text>
-        <text class="label mt">下单时间</text>
-        <text class="value">{{ formatDateTime(createAt) || '—' }}</text>
-        <text class="label mt">应付金额(元)</text>
-        <text class="amount">¥{{ amountText }}</text>
-        <text class="status">状态:{{ statusText }}</text>
-      </view>
-      <view class="actions">
-        <van-button
-          v-if="paymentMethod === 'online'"
-          round
-          class="btn secondary"
-          :disabled="!canPay || paying"
-          :loading="canPay && paying"
-          @click="requestPay"
-        >
-          微信支付
-        </van-button>
-        <van-button round class="btn primary" type="primary" @click="goContinueShopping">继续购物</van-button>
-      </view>
-    </view>
-  </view>
-</template>
 
 <style lang="scss" scoped>
 .page {
@@ -312,7 +316,7 @@ onLoad((query) => {
   display: block;
 }
 .pay-btn {
-  background: #07c160;
+  background: var(--c-brand-green);
   color: #fff;
   border-radius: 44rpx;
   font-size: 30rpx;
@@ -331,7 +335,7 @@ onLoad((query) => {
 }
 .btn.primary {
   background: linear-gradient(145deg, var(--c-barley-yellow), #e4b458);
-  color: #ffffff;
+  color: #fff;
 }
 .btn.secondary {
   background: #ffffff;
@@ -340,12 +344,12 @@ onLoad((query) => {
   box-shadow: 0 2rpx 10rpx rgba(10, 159, 109, 0.1);
 }
 
-::deep(.btn.van-button) {
+:deep(.btn.van-button) {
   border: none !important;
   box-shadow: none !important;
 }
 
-::deep(.btn.primary.van-button--primary) {
+:deep(.btn.primary.van-button--primary) {
   background: linear-gradient(145deg, var(--c-barley-yellow), #e4b458) !important;
   color: #ffffff !important;
   border-color: #d6a343 !important;

+ 256 - 241
sqxp-wx/pages/client/product/detail.vue

@@ -4,183 +4,193 @@
       <ClientSkeleton variant="detail" :rows="3" />
     </view>
     <template v-else>
-      <view class="detail-fixed-top">
-        <swiper
-          v-if="bannerList.length > 0"
-          class="banner-swiper"
-          :indicator-dots="bannerList.length > 1"
-          :autoplay="bannerList.length > 0"
-          :interval="3000"
-          :circular="bannerList.length > 1"
-        >
-          <swiper-item v-for="(banner, index) in bannerList" :key="index">
-            <ClientImageLoader
-              v-if="banner.mediaType === 'image'"
-              :src="banner.fileUrl"
-              mode="aspectFill"
-              class="banner-image"
-            />
-            <video v-else :src="banner.fileUrl" controls class="banner-video" />
-          </swiper-item>
-        </swiper>
-        <view v-else class="banner-empty">
-          <text class="banner-empty-text">暂无轮播图</text>
+    <view class="detail-fixed-top">
+    <!-- 轮播图 -->
+    <swiper
+      v-if="bannerList.length > 0"
+      class="banner-swiper"
+      :indicator-dots="bannerList.length > 1"
+      :autoplay="bannerList.length > 0"
+      :interval="3000"
+      :circular="bannerList.length > 1"
+    >
+      <swiper-item v-for="(banner, index) in bannerList" :key="index">
+        <ClientImageLoader
+          v-if="banner.mediaType === 'image'"
+          :src="banner.fileUrl"
+          mode="aspectFill"
+          class="banner-image"
+        />
+        <video v-else :src="banner.fileUrl" controls class="banner-video" />
+      </swiper-item>
+    </swiper>
+    <view v-else class="banner-empty">
+      <text class="banner-empty-text">暂无轮播图</text>
+    </view>
+
+    <!-- 商品基本信息 -->
+    <view class="product-info">
+      <view class="price-row">
+        <text class="price">¥{{ formatPrice(product?.price) }}</text>
+        <text class="unit">/{{ product?.unitName }}</text>
+      </view>
+      <view class="name-row">
+        <text class="name">{{ product?.name }}</text>
+      </view>
+      <view class="sku-row" v-if="product?.skuCode">
+        <text class="sku-label">SKU:</text>
+        <text class="sku-code">{{ product.skuCode }}</text>
+      </view>
+    </view>
+    </view>
+
+    <scroll-view scroll-y class="detail-scroll-area" :show-scrollbar="true">
+      <view class="detail-scroll-inner">
+    <!-- 商品参数 -->
+    <view class="params-section" v-if="parameterValues.length > 0">
+      <view class="section-title">商品参数</view>
+      <view class="params-list">
+        <view v-for="param in parameterValues" :key="param.parameterId" class="param-item">
+          <text class="param-name">{{ param.parameterName }}</text>
+          <text class="param-value">{{ param.paramValue }}</text>
         </view>
+      </view>
+    </view>
 
-        <view class="product-info">
-          <view class="price-row">
-            <text class="price">¥{{ formatPrice(product?.price) }}</text>
-            <text class="unit">/{{ product?.unitName }}</text>
-          </view>
-          <view class="name-row">
-            <text class="name">{{ product?.name }}</text>
-          </view>
-          <view class="sku-row" v-if="product?.skuCode">
-            <text class="sku-label">SKU:</text>
-            <text class="sku-code">{{ product.skuCode }}</text>
-          </view>
+    <!-- 商品详情(富文本) -->
+    <view class="detail-section" v-if="detailContent">
+      <view class="section-title">商品详情</view>
+      <view class="rich-content">
+        <rich-text :nodes="detailContent"></rich-text>
+      </view>
+    </view>
+
+    <!-- 详情图片/视频(运营端配置的详情媒体,展示在富文本下方) -->
+    <view class="detail-section" v-if="detailMediaList.length > 0">
+      <view class="section-title">详情图片/视频</view>
+      <view class="detail-media-list">
+        <view v-for="(m, idx) in detailMediaList" :key="m.id || `dm-${idx}`" class="detail-media-item">
+          <ClientImageLoader
+            v-if="m.contentType === 'image'"
+            :src="m.fileUrl"
+            mode="widthFix"
+            class="detail-media-img"
+          />
+          <video v-else :src="m.fileUrl" :poster="m.thumbnailUrl || ''" controls class="detail-media-video" />
+          <text v-if="m.description" class="detail-media-desc">{{ m.description }}</text>
         </view>
       </view>
+    </view>
 
-      <scroll-view scroll-y class="detail-scroll-area" :show-scrollbar="true">
-        <view class="detail-scroll-inner">
-          <view class="params-section" v-if="parameterValues.length > 0">
-            <view class="section-title">商品参数</view>
-            <view class="params-list">
-              <view v-for="param in parameterValues" :key="param.parameterId" class="param-item">
-                <text class="param-name">{{ param.parameterName }}</text>
-                <text class="param-value">{{ param.paramValue }}</text>
-              </view>
-            </view>
-          </view>
+    <!-- 商品评价 -->
+    <view class="review-section">
+      <view class="section-title" @click="goToReviewList">
+        <text>商品评价</text>
+        <view class="more-btn">
+          <text class="count">({{ reviewStats?.totalCount || 0 }})</text>
+          <text class="arrow">›</text>
+        </view>
+      </view>
 
-          <view class="detail-section" v-if="detailContent">
-            <view class="section-title">商品详情</view>
-            <view class="rich-content">
-              <rich-text :nodes="detailContent"></rich-text>
+      <!-- 评分概览 -->
+      <view class="review-overview" v-if="reviewStats">
+        <view class="avg-rating">
+          <text class="score">{{ reviewStats.avgRating?.toFixed(1) }}</text>
+          <text class="label">分</text>
+        </view>
+        <view class="rating-distribution">
+          <view v-for="i in 5" :key="i" class="rating-row">
+            <text class="star">{{ 6 - i }}星</text>
+            <view class="bar-wrapper">
+              <view class="bar" :style="{ width: getBarWidth(6 - i) }"></view>
             </view>
           </view>
+        </view>
+      </view>
 
-          <view class="detail-section" v-if="detailMediaList.length > 0">
-            <view class="section-title">详情图片/视频</view>
-            <view class="detail-media-list">
-              <view v-for="(m, idx) in detailMediaList" :key="m.id || `dm-${idx}`" class="detail-media-item">
-                <ClientImageLoader
-                  v-if="m.contentType === 'image'"
-                  :src="m.fileUrl"
-                  mode="widthFix"
-                  class="detail-media-img"
-                />
-                <video v-else :src="m.fileUrl" :poster="m.thumbnailUrl || ''" controls class="detail-media-video" />
-                <text v-if="m.description" class="detail-media-desc">{{ m.description }}</text>
-              </view>
+      <!-- 评价列表(最新3条) -->
+      <view class="review-list" v-if="reviewList.length > 0">
+        <view v-for="review in reviewList.slice(0, 3)" :key="review.id" class="review-item">
+          <view class="review-header">
+            <view class="user-info">
+              <text class="username">{{ review.isAnonymous ? '匿名用户' : review.userName }}</text>
+              <van-rate :value="review.rating" readonly size="12" />
             </view>
           </view>
-
-          <view class="review-section">
-            <view class="section-title" @click="goToReviewList">
-              <text>商品评价</text>
-              <view class="more-btn">
-                <text class="count">({{ reviewStats?.totalCount || 0 }})</text>
-                <text class="arrow">›</text>
-              </view>
-            </view>
-
-            <view class="review-overview" v-if="reviewStats">
-              <view class="avg-rating">
-                <text class="score">{{ reviewStats.avgRating?.toFixed(1) }}</text>
-                <text class="label">分</text>
-              </view>
-              <view class="rating-distribution">
-                <view v-for="i in 5" :key="i" class="rating-row">
-                  <text class="star">{{ 6 - i }}星</text>
-                  <view class="bar-wrapper">
-                    <view class="bar" :style="{ width: getBarWidth(6 - i) }"></view>
-                  </view>
-                </view>
-              </view>
-            </view>
-
-            <view class="review-list" v-if="reviewList.length > 0">
-              <view v-for="review in reviewList.slice(0, 3)" :key="review.id" class="review-item">
-                <view class="review-header">
-                  <view class="user-info">
-                    <text class="username">{{ review.isAnonymous ? '匿名用户' : review.userName }}</text>
-                    <van-rate :value="review.rating" readonly size="12" />
-                  </view>
-                </view>
-                <view class="review-content">
-                  <text v-if="review.ratingTags" class="tags">{{ formatTags(review.ratingTags) }}</text>
-                  <text class="content">{{ review.content }}</text>
-                </view>
-                <view class="review-media" v-if="review.mediaList?.length">
-                  <ClientImageLoader
-                    v-for="media in review.mediaList.slice(0, 3)"
-                    :key="media.id"
-                    :src="media.fileUrl"
-                    mode="aspectFill"
-                    class="media-thumb"
-                  />
-                </view>
-                <view class="review-footer">
-                  <text class="time">{{ formatTime(review.createAt) }}</text>
-                  <view class="like-btn">
-                    <text>👍</text>
-                    <text>{{ review.likeCount || 0 }}</text>
-                  </view>
-                </view>
-              </view>
-            </view>
-            <view v-else class="no-review">
-              <text>暂无评价</text>
-            </view>
+          <view class="review-content">
+            <text v-if="review.ratingTags" class="tags">{{ formatTags(review.ratingTags) }}</text>
+            <text class="content">{{ review.content }}</text>
           </view>
-        </view>
-      </scroll-view>
-
-      <view class="bottom-bar">
-        <view class="action-icons">
-          <view class="icon-item" hover-class="icon-item--pressed" :hover-stay-time="100" @click="goToHome">
-            <text class="icon">🏠</text>
-            <text class="icon-text">首页</text>
+          <view class="review-media" v-if="review.mediaList?.length">
+            <ClientImageLoader
+              v-for="media in review.mediaList.slice(0, 3)"
+              :key="media.id"
+              :src="media.fileUrl"
+              mode="aspectFill"
+              class="media-thumb"
+            />
           </view>
-          <view class="icon-item" hover-class="icon-item--pressed" :hover-stay-time="100" @click="goToCart">
-            <text class="icon">🛒</text>
-            <text class="icon-text">购物车</text>
-            <view v-if="cartCount > 0" class="badge">{{ cartCount > 99 ? '99+' : cartCount }}</view>
+          <view class="review-footer">
+            <text class="time">{{ formatTime(review.createAt) }}</text>
+            <view class="like-btn">
+              <text>👍</text>
+              <text>{{ review.likeCount || 0 }}</text>
+            </view>
           </view>
         </view>
-        <view class="action-btns">
-          <view class="add-cart-btn" hover-class="bar-btn--pressed" :hover-stay-time="100" @click="handleAddToCart">加入购物车</view>
-          <view class="buy-btn" hover-class="bar-btn--pressed" :hover-stay-time="100" @click="handleBuyNow">立即购买</view>
+      </view>
+      <view v-else class="no-review">
+        <text>暂无评价</text>
+      </view>
+    </view>
+      </view>
+    </scroll-view>
+
+    <!-- 底部操作栏 -->
+    <view class="bottom-bar">
+      <view class="action-icons">
+        <view class="icon-item" hover-class="icon-item--pressed" :hover-stay-time="100" @click="goToHome">
+          <text class="icon">🏠</text>
+          <text class="icon-text">首页</text>
+        </view>
+        <view class="icon-item" hover-class="icon-item--pressed" :hover-stay-time="100" @click="goToCart">
+          <text class="icon">🛒</text>
+          <text class="icon-text">购物车</text>
+          <view v-if="cartCount > 0" class="badge">{{ cartCount > 99 ? '99+' : cartCount }}</view>
         </view>
       </view>
+      <view class="action-btns">
+        <view class="add-cart-btn" hover-class="bar-btn--pressed" :hover-stay-time="100" @click="handleAddToCart">加入购物车</view>
+        <view class="buy-btn" hover-class="bar-btn--pressed" :hover-stay-time="100" @click="handleBuyNow">立即购买</view>
+      </view>
+    </view>
 
-      <ClientTabBar :active="tabBarActive" />
+    <!-- 数量选择弹窗 -->
+    <ClientTabBar :active="tabBarActive" />
 
-      <van-popup :show="showQuantityPopup" position="bottom" round @update:show="showQuantityPopup = $event">
-        <view class="quantity-popup">
-          <view class="product-row">
-            <ClientImageLoader :src="product?.imageUrl || ''" class="popup-image" />
-            <view class="popup-info">
-              <text class="popup-name">{{ product?.name }}</text>
-              <text class="popup-price">¥{{ formatPrice(product?.price) }}</text>
-            </view>
+    <van-popup v-model:show="showQuantityPopup" position="bottom" round>
+      <view class="quantity-popup">
+        <view class="product-row">
+          <ClientImageLoader :src="product?.imageUrl || ''" class="popup-image" />
+          <view class="popup-info">
+            <text class="popup-name">{{ product?.name }}</text>
+            <text class="popup-price">¥{{ formatPrice(product?.price) }}</text>
           </view>
-          <view class="quantity-row">
-            <text class="quantity-label">购买数量</text>
-            <view class="quantity-control">
-              <view class="qty-btn" @click="decreaseQty">-</view>
-              <input v-model="quantity" type="number" class="qty-input" />
-              <view class="qty-btn" @click="increaseQty">+</view>
-            </view>
+        </view>
+        <view class="quantity-row">
+          <text class="quantity-label">购买数量</text>
+          <view class="quantity-control">
+            <view class="qty-btn" @click="decreaseQty">-</view>
+            <input v-model="quantity" type="number" class="qty-input" />
+            <view class="qty-btn" @click="increaseQty">+</view>
           </view>
-          <view class="confirm-btn" @click="confirmAction">{{ actionType === 'cart' ? '加入购物车' : '立即购买' }}</view>
         </view>
-      </van-popup>
+        <view class="confirm-btn" @click="confirmAction">{{ actionType === 'cart' ? '加入购物车' : '立即购买' }}</view>
+      </view>
+    </van-popup>
     </template>
-  </view>
-</template>
+
+
 
 <script setup>
 import { ref, onMounted } from 'vue'
@@ -363,6 +373,8 @@ onMounted(() => {
 })
 </script>
 
+
+
 <style lang="scss" scoped>
 .product-detail-page {
   height: 100vh;
@@ -395,6 +407,7 @@ onMounted(() => {
   padding-bottom: 40rpx;
 }
 
+/* 对齐 sanqiang-client-product-detail-ui-demo.html:banner 区边距与圆角 */
 .banner-swiper {
   width: auto;
   height: 440rpx;
@@ -405,8 +418,7 @@ onMounted(() => {
   background: linear-gradient(145deg, #f4fbf8, #dff2e9);
   box-sizing: border-box;
 
-  .banner-image,
-  .banner-video {
+  .banner-image, .banner-video {
     width: 100%;
     height: 100%;
   }
@@ -509,32 +521,33 @@ onMounted(() => {
 
   .params-list {
     padding: 8rpx 24rpx 24rpx;
-  }
 
-  .param-item {
-    display: grid;
-    grid-template-columns: 184rpx 1fr;
-    gap: 16rpx;
-    align-items: start;
-    padding: 16rpx 0;
-    border-top: 1rpx solid #edf5f1;
-    box-sizing: border-box;
+    .param-item {
+      display: grid;
+      grid-template-columns: 184rpx 1fr;
+      gap: 16rpx;
+      align-items: start;
+      padding: 16rpx 0;
+      border-top: 1rpx solid #edf5f1;
+      box-sizing: border-box;
 
-    &:first-child {
-      border-top: none;
-      padding-top: 8rpx;
-    }
+      &:first-child {
+        border-top: none;
+        padding-top: 8rpx;
+      }
 
-    .param-name {
-      font-size: 26rpx;
-      color: var(--c-muted);
-    }
+      .param-name {
+        width: auto;
+        font-size: 26rpx;
+        color: var(--c-muted);
+      }
 
-    .param-value {
-      min-width: 0;
-      font-size: 26rpx;
-      color: var(--c-text);
-      word-break: break-word;
+      .param-value {
+        min-width: 0;
+        font-size: 26rpx;
+        color: var(--c-text);
+        word-break: break-word;
+      }
     }
   }
 }
@@ -600,16 +613,18 @@ onMounted(() => {
   overflow: hidden;
   box-sizing: border-box;
 
-  .more-btn {
-    display: flex;
-    align-items: center;
-    font-size: 24rpx;
-    color: var(--c-muted);
-    font-weight: 400;
+  .section-title {
+    .more-btn {
+      display: flex;
+      align-items: center;
+      font-size: 24rpx;
+      color: var(--c-muted);
+      font-weight: 400;
 
-    .arrow {
-      margin-left: 8rpx;
-      font-size: 28rpx;
+      .arrow {
+        margin-left: 8rpx;
+        font-size: 28rpx;
+      }
     }
   }
 
@@ -752,6 +767,7 @@ onMounted(() => {
   }
 }
 
+/* 对齐 demo .bar:顶部分割 + 图标区 + 双按钮 */
 .bottom-bar {
   position: fixed;
   bottom: calc(100rpx + env(safe-area-inset-bottom, 0px));
@@ -764,7 +780,7 @@ onMounted(() => {
   padding: 20rpx 24rpx;
   padding-bottom: calc(20rpx + env(safe-area-inset-bottom, 0px));
   box-sizing: border-box;
-  background: #ffffff;
+  background: #fff;
   border-top: 1rpx solid var(--c-border);
   gap: 16rpx;
 
@@ -817,8 +833,7 @@ onMounted(() => {
     gap: 16rpx;
     min-width: 0;
 
-    .add-cart-btn,
-    .buy-btn {
+    .add-cart-btn, .buy-btn {
       flex: 1;
       min-height: 84rpx;
       border-radius: 42rpx;
@@ -855,33 +870,33 @@ onMounted(() => {
   .product-row {
     display: flex;
     margin-bottom: 30rpx;
-  }
 
-  .popup-image {
-    width: 160rpx;
-    height: 160rpx;
-    border-radius: 12rpx;
-    background: #f5f7fa;
-  }
+    .popup-image {
+      width: 160rpx;
+      height: 160rpx;
+      border-radius: 12rpx;
+      background: #f5f7fa;
+    }
 
-  .popup-info {
-    flex: 1;
-    margin-left: 20rpx;
-    display: flex;
-    flex-direction: column;
-    justify-content: center;
-  }
+    .popup-info {
+      flex: 1;
+      margin-left: 20rpx;
+      display: flex;
+      flex-direction: column;
+      justify-content: center;
 
-  .popup-name {
-    font-size: 28rpx;
-    color: var(--c-text);
-    margin-bottom: 12rpx;
-  }
+      .popup-name {
+        font-size: 28rpx;
+        color: var(--c-text);
+        margin-bottom: 12rpx;
+      }
 
-  .popup-price {
-    font-size: 36rpx;
-    color: var(--c-danger);
-    font-weight: 700;
+      .popup-price {
+        font-size: 36rpx;
+        color: var(--c-danger);
+        font-weight: bold;
+      }
+    }
   }
 
   .quantity-row {
@@ -890,38 +905,38 @@ onMounted(() => {
     align-items: center;
     padding: 20rpx 0;
     border-top: 1rpx solid #ebeef5;
-  }
 
-  .quantity-label {
-    font-size: 28rpx;
-    color: var(--c-text);
-  }
+    .quantity-label {
+      font-size: 28rpx;
+      color: var(--c-text);
+    }
 
-  .quantity-control {
-    display: flex;
-    align-items: center;
-  }
+    .quantity-control {
+      display: flex;
+      align-items: center;
 
-  .qty-btn {
-    width: 60rpx;
-    height: 60rpx;
-    background: #f5f7fa;
-    border-radius: 8rpx;
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    font-size: 32rpx;
-    color: #606266;
-  }
+      .qty-btn {
+        width: 60rpx;
+        height: 60rpx;
+        background: #f5f7fa;
+        border-radius: 8rpx;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        font-size: 32rpx;
+        color: #606266;
+      }
 
-  .qty-input {
-    width: 80rpx;
-    height: 60rpx;
-    text-align: center;
-    font-size: 28rpx;
-    background: #f5f7fa;
-    margin: 0 12rpx;
-    border-radius: 8rpx;
+      .qty-input {
+        width: 80rpx;
+        height: 60rpx;
+        text-align: center;
+        font-size: 28rpx;
+        background: #f5f7fa;
+        margin: 0 12rpx;
+        border-radius: 8rpx;
+      }
+    }
   }
 
   .confirm-btn {

+ 63 - 60
sqxp-wx/pages/client/product/view.vue

@@ -34,7 +34,7 @@
 
         <view class="product-info">
           <view class="price-row">
-            <text class="price">¥{{ formatPrice(product.price || 0) }}</text>
+            <text class="price">¥{{ formatPrice(product.price) }}</text>
             <text class="unit">/{{ formatMeasureText(product.measureValue, product.unitName) }}</text>
           </view>
           <text class="name">{{ product.name }}</text>
@@ -116,6 +116,8 @@
   </view>
 </template>
 
+
+
 <script setup>
 import { ref } from 'vue'
 import { onLoad } from '@dcloudio/uni-app'
@@ -250,6 +252,8 @@ onLoad((options) => {
 })
 </script>
 
+
+
 <style lang="scss" scoped>
 .product-view-page {
   height: 100vh;
@@ -301,8 +305,7 @@ onLoad((options) => {
   height: 560rpx;
   background: #fff;
 
-  .banner-image,
-  .banner-video {
+  .banner-image, .banner-video {
     width: 100%;
     height: 100%;
   }
@@ -524,8 +527,7 @@ onLoad((options) => {
   margin-right: 10rpx;
 }
 
-.add-cart-btn,
-.buy-btn {
+.add-cart-btn, .buy-btn {
   flex: 1;
   display: flex;
   align-items: center;
@@ -549,33 +551,33 @@ onLoad((options) => {
   .product-row {
     display: flex;
     margin-bottom: 30rpx;
-  }
-
-  .popup-image {
-    width: 160rpx;
-    height: 160rpx;
-    border-radius: 12rpx;
-    background: #f5f7fa;
-  }
 
-  .popup-info {
-    flex: 1;
-    margin-left: 20rpx;
-    display: flex;
-    flex-direction: column;
-    justify-content: center;
-  }
-
-  .popup-name {
-    font-size: 28rpx;
-    color: #303133;
-    margin-bottom: 12rpx;
-  }
+    .popup-image {
+      width: 160rpx;
+      height: 160rpx;
+      border-radius: 12rpx;
+      background: #f5f7fa;
+    }
 
-  .popup-price {
-    font-size: 36rpx;
-    color: var(--c-danger);
-    font-weight: 700;
+    .popup-info {
+      flex: 1;
+      margin-left: 20rpx;
+      display: flex;
+      flex-direction: column;
+      justify-content: center;
+
+      .popup-name {
+        font-size: 28rpx;
+        color: #303133;
+        margin-bottom: 12rpx;
+      }
+
+      .popup-price {
+        font-size: 36rpx;
+        color: var(--c-danger);
+        font-weight: 700;
+      }
+    }
   }
 
   .quantity-row {
@@ -584,38 +586,38 @@ onLoad((options) => {
     align-items: center;
     padding: 20rpx 0;
     border-top: 1rpx solid #ebeef5;
-  }
-
-  .quantity-label {
-    font-size: 28rpx;
-    color: #303133;
-  }
-
-  .quantity-control {
-    display: flex;
-    align-items: center;
-  }
 
-  .qty-btn {
-    width: 60rpx;
-    height: 60rpx;
-    background: #f5f7fa;
-    border-radius: 8rpx;
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    font-size: 32rpx;
-    color: #606266;
-  }
+    .quantity-label {
+      font-size: 28rpx;
+      color: #303133;
+    }
 
-  .qty-input {
-    width: 90rpx;
-    height: 60rpx;
-    text-align: center;
-    font-size: 28rpx;
-    background: #f5f7fa;
-    margin: 0 12rpx;
-    border-radius: 8rpx;
+    .quantity-control {
+      display: flex;
+      align-items: center;
+
+      .qty-btn {
+        width: 60rpx;
+        height: 60rpx;
+        background: #f5f7fa;
+        border-radius: 8rpx;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        font-size: 32rpx;
+        color: #606266;
+      }
+
+      .qty-input {
+        width: 90rpx;
+        height: 60rpx;
+        text-align: center;
+        font-size: 28rpx;
+        background: #f5f7fa;
+        margin: 0 12rpx;
+        border-radius: 8rpx;
+      }
+    }
   }
 
   .confirm-btn {
@@ -632,6 +634,7 @@ onLoad((options) => {
 </style>
 
 <style lang="scss">
+/* 与首页一致:禁止页面级滚动,仅中间 scroll-view 滚动 */
 page {
   height: 100%;
   overflow: hidden;

+ 96 - 85
sqxp-wx/pages/client/profile/profile.vue

@@ -1,3 +1,98 @@
+<template>
+  <view class="profile-page client-theme">
+    <view v-if="profileLoading" class="profile-loading-wrap">
+      <ClientSkeleton variant="hero-list" :rows="2" />
+    </view>
+    <scroll-view v-else scroll-y class="profile-scroll" :show-scrollbar="true">
+      <!-- 对齐 sanqiang-client-profile-ui-demo.html .header -->
+      <view class="profile-header">
+        <view class="user-row">
+          <view class="avatar">
+            <ClientImageLoader class="avatar-img" :src="avatarDisplayUrl" mode="aspectFill" radius="50%" />
+          </view>
+          <view class="user-text">
+            <text class="user-name">{{ userStore.userInfo?.realName || '用户' }}</text>
+            <text class="user-sub">
+              叁强会员 · {{ userStore.userInfo?.regionName?.trim() || '客户' }}
+            </text>
+          </view>
+        </view>
+        <view class="user-stats">
+          <view class="stat-item" hover-class="stat-item--pressed" :hover-stay-time="100" @click="goOrders">
+            <text class="stat-value">{{ dashPending }}</text>
+            <text class="stat-label">待支付</text>
+          </view>
+          <view class="stat-item" hover-class="stat-item--pressed" :hover-stay-time="100" @click="goOrders">
+            <text class="stat-value">{{ dashDeliver }}</text>
+            <text class="stat-label">配送中</text>
+          </view>
+          <view class="stat-item" hover-class="stat-item--pressed" :hover-stay-time="100" @click="goOrders">
+            <text class="stat-value">{{ dashDone }}</text>
+            <text class="stat-label">已完成</text>
+          </view>
+        </view>
+      </view>
+
+      <view class="profile-section">
+        <view class="menu-card">
+          <view
+            v-for="(item, index) in menuItems"
+            :key="index"
+            class="menu-row"
+            hover-class="menu-row--pressed"
+            :hover-stay-time="100"
+            @click="goToMenu(item.path)"
+          >
+            <view class="menu-left">
+              <view class="menu-ico-wrap" :class="{ 'menu-ico-wrap--bg': item.withBg }">
+                <image class="menu-ico-img" :src="item.icon" mode="aspectFit" />
+              </view>
+              <text class="menu-text">{{ item.text }}</text>
+            </view>
+            <text class="menu-arrow">›</text>
+          </view>
+          <view class="menu-row" hover-class="menu-row--pressed" :hover-stay-time="100" @click="goToMenu('__service__')">
+            <view class="menu-left">
+              <view class="menu-ico-wrap menu-ico-wrap--bg">
+                <image class="menu-ico-img" :src="ICON_SERVICE" mode="aspectFit" />
+              </view>
+              <text class="menu-text">联系客服</text>
+            </view>
+            <image class="menu-arrow-icon" :src="ICON_OUTGOING_CALL" mode="aspectFit" />
+          </view>
+        </view>
+
+        <view class="logout-btn" hover-class="logout-btn--pressed" :hover-stay-time="120" @click="handleLogout">
+          <text>退出登录</text>
+        </view>
+      </view>
+    </scroll-view>
+    <!-- #ifdef H5 -->
+    <ClientTabBar active="profile" />
+    <!-- #endif -->
+
+    <view
+      v-if="showLogoutDialog"
+      class="logout-dialog-mask"
+      @click="closeLogoutDialog"
+      @touchmove.stop.prevent
+    >
+      <view class="logout-dialog" @click.stop>
+        <view class="logout-dialog-title">退出登录</view>
+        <view class="logout-dialog-content">退出后需要重新登录,确认要退出当前账号吗?</view>
+        <view class="logout-dialog-actions">
+          <view class="logout-action logout-action-cancel" @click="closeLogoutDialog">取消</view>
+          <view class="logout-action logout-action-confirm" @click="confirmLogout">
+            {{ logoutSubmitting ? '退出中...' : '退出登录' }}
+          </view>
+        </view>
+      </view>
+    </view>
+  </view>
+</template>
+
+
+
 <script setup>
 import { computed, onMounted, ref } from 'vue'
 import { onShow } from '@dcloudio/uni-app'
@@ -135,92 +230,7 @@ onShow(() => {
 })
 </script>
 
-<template>
-  <view class="profile-page client-theme">
-    <view v-if="profileLoading" class="profile-loading-wrap">
-      <ClientSkeleton variant="hero-list" :rows="2" />
-    </view>
-    <scroll-view v-else scroll-y class="profile-scroll" :show-scrollbar="true">
-      <view class="profile-header">
-        <view class="user-row">
-          <view class="avatar">
-            <ClientImageLoader class="avatar-img" :src="avatarDisplayUrl" mode="aspectFill" radius="50%" />
-          </view>
-          <view class="user-text">
-            <text class="user-name">{{ userStore.userInfo?.realName || '用户' }}</text>
-            <text class="user-sub">叁强会员 · {{ userStore.userInfo?.regionName?.trim() || '客户' }}</text>
-          </view>
-        </view>
-        <view class="user-stats">
-          <view class="stat-item" hover-class="stat-item--pressed" :hover-stay-time="100" @click="goOrders">
-            <text class="stat-value">{{ dashPending }}</text>
-            <text class="stat-label">待支付</text>
-          </view>
-          <view class="stat-item" hover-class="stat-item--pressed" :hover-stay-time="100" @click="goOrders">
-            <text class="stat-value">{{ dashDeliver }}</text>
-            <text class="stat-label">配送中</text>
-          </view>
-          <view class="stat-item" hover-class="stat-item--pressed" :hover-stay-time="100" @click="goOrders">
-            <text class="stat-value">{{ dashDone }}</text>
-            <text class="stat-label">已完成</text>
-          </view>
-        </view>
-      </view>
-
-      <view class="profile-section">
-        <view class="menu-card">
-          <view
-            v-for="(item, index) in menuItems"
-            :key="index"
-            class="menu-row"
-            hover-class="menu-row--pressed"
-            :hover-stay-time="100"
-            @click="goToMenu(item.path)"
-          >
-            <view class="menu-left">
-              <view class="menu-ico-wrap" :class="{ 'menu-ico-wrap--bg': item.withBg }">
-                <image class="menu-ico-img" :src="item.icon" mode="aspectFit" />
-              </view>
-              <text class="menu-text">{{ item.text }}</text>
-            </view>
-            <text class="menu-arrow">›</text>
-          </view>
-
-          <view class="menu-row" hover-class="menu-row--pressed" :hover-stay-time="100" @click="goToMenu('__service__')">
-            <view class="menu-left">
-              <view class="menu-ico-wrap menu-ico-wrap--bg">
-                <image class="menu-ico-img" :src="ICON_SERVICE" mode="aspectFit" />
-              </view>
-              <text class="menu-text">联系客服</text>
-            </view>
-            <image class="menu-arrow-icon" :src="ICON_OUTGOING_CALL" mode="aspectFit" />
-          </view>
-        </view>
-
-        <view class="logout-btn" hover-class="logout-btn--pressed" :hover-stay-time="120" @click="handleLogout">
-          <text>退出登录</text>
-        </view>
-      </view>
-    </scroll-view>
-
-    <!-- #ifdef H5 -->
-    <ClientTabBar active="profile" />
-    <!-- #endif -->
 
-    <view v-if="showLogoutDialog" class="logout-dialog-mask" @click="closeLogoutDialog" @touchmove.stop.prevent>
-      <view class="logout-dialog" @click.stop>
-        <view class="logout-dialog-title">退出登录</view>
-        <view class="logout-dialog-content">退出后需要重新登录,确认要退出当前账号吗?</view>
-        <view class="logout-dialog-actions">
-          <view class="logout-action logout-action-cancel" @click="closeLogoutDialog">取消</view>
-          <view class="logout-action logout-action-confirm" @click="confirmLogout">
-            {{ logoutSubmitting ? '退出中...' : '退出登录' }}
-          </view>
-        </view>
-      </view>
-    </view>
-  </view>
-</template>
 
 <style lang="scss" scoped>
 .profile-loading-wrap {
@@ -245,6 +255,7 @@ onShow(() => {
   box-sizing: border-box;
 }
 
+/* 对齐 sanqiang-client-profile-ui-demo.html .header */
 .profile-header {
   background: linear-gradient(145deg, var(--c-brand-green), var(--c-brand-green-2));
   color: #ffffff;

+ 103 - 93
sqxp-wx/pages/client/review/create.vue

@@ -6,113 +6,123 @@
     </view>
     <scroll-view scroll-y class="client-subpage-scroll create-review-scroll" :show-scrollbar="true">
       <view class="client-subpage-body create-review-body">
-        <view class="product-card">
-          <ClientImageLoader
-            class="product-image"
-            :src="product?.imageUrl || IMG_DEFAULT_PRODUCT"
-            mode="aspectFill"
-            radius="12rpx"
-          />
-          <view class="product-info">
-            <text class="product-name">{{ product?.name || '' }}</text>
-            <text class="product-unit">{{ product?.unitName }}</text>
-            <text class="product-price">¥{{ formatPrice(product?.price || 0) }}</text>
-          </view>
-        </view>
+    <!-- 商品信息 -->
+    <view class="product-card">
+      <ClientImageLoader class="product-image" :src="product?.imageUrl || IMG_DEFAULT_PRODUCT" mode="aspectFill" radius="12rpx" />
+      <view class="product-info">
+        <text class="product-name">{{ product?.name || '' }}</text>
+        <text class="product-unit">{{ product?.unitName }}</text>
+        <text class="product-price">¥{{ formatPrice(product?.price || 0) }}</text>
+      </view>
+    </view>
 
-        <view class="rating-section">
-          <view class="section-title">商品评分</view>
-          <view class="rating-stars">
-            <view
-              v-for="i in 5"
-              :key="i"
-              class="star-item"
-              :class="{ active: i <= rating }"
-              @click="rating = i"
-            >
-              <text class="star-icon">{{ i <= rating ? '⭐' : '☆' }}</text>
-            </view>
-          </view>
-          <view class="rating-label">
-            <text v-if="rating === 5">非常满意</text>
-            <text v-else-if="rating === 4">满意</text>
-            <text v-else-if="rating === 3">一般</text>
-            <text v-else-if="rating === 2">较差</text>
-            <text v-else-if="rating === 1">很差</text>
-            <text v-else>请选择评分</text>
-          </view>
+    <!-- 评分 -->
+    <view class="rating-section">
+      <view class="section-title">商品评分</view>
+      <view class="rating-stars">
+        <view
+          v-for="i in 5"
+          :key="i"
+          class="star-item"
+          :class="{ active: i <= rating }"
+          @click="rating = i"
+        >
+          <text class="star-icon">{{ i <= rating ? '⭐' : '☆' }}</text>
         </view>
+      </view>
+      <view class="rating-label">
+        <text v-if="rating === 5">非常满意</text>
+        <text v-else-if="rating === 4">满意</text>
+        <text v-else-if="rating === 3">一般</text>
+        <text v-else-if="rating === 2">较差</text>
+        <text v-else-if="rating === 1">很差</text>
+        <text v-else>请选择评分</text>
+      </view>
+    </view>
 
-        <view class="tags-section">
-          <view class="section-title">选择标签(可选)</view>
-          <view class="tags-list">
-            <view
-              v-for="tag in availableTags"
-              :key="tag"
-              class="tag-item"
-              :class="{ active: selectedTags.includes(tag) }"
-              @click="toggleTag(tag)"
-            >
-              {{ tag }}
-            </view>
-          </view>
+    <!-- 评价标签 -->
+    <view class="tags-section">
+      <view class="section-title">选择标签(可选)</view>
+      <view class="tags-list">
+        <view
+          v-for="tag in availableTags"
+          :key="tag"
+          class="tag-item"
+          :class="{ active: selectedTags.includes(tag) }"
+          @click="toggleTag(tag)"
+        >
+          {{ tag }}
         </view>
+      </view>
+    </view>
 
-        <view class="content-section">
-          <view class="section-title">评价内容</view>
-          <view class="content-input">
-            <textarea
-              v-model="content"
-              placeholder="请输入评价内容(至少10个字)..."
-              :maxlength="500"
-              class="textarea"
-            />
-            <view class="word-count">{{ content.length }}/500</view>
-          </view>
-        </view>
+    <!-- 评价内容 -->
+    <view class="content-section">
+      <view class="section-title">评价内容</view>
+      <view class="content-input">
+        <textarea
+          v-model="content"
+          placeholder="请输入评价内容(至少10个字)..."
+          :maxlength="500"
+          class="textarea"
+        />
+        <view class="word-count">{{ content.length }}/500</view>
+      </view>
+    </view>
 
-        <view class="media-section">
-          <view class="section-title">上传图片/视频(可选)</view>
-          <view class="media-list">
-            <view v-for="(item, index) in mediaList" :key="index" class="media-item">
-              <ClientImageLoader
-                v-if="item.type === 'image'"
-                :src="item.url"
-                mode="aspectFill"
-                class="media-image"
-                @click="previewImage(index)"
-              />
-              <video v-else :src="item.url" controls @click="previewVideo(index)" />
-              <view class="media-delete" @click="removeMedia(index)">
-                <image class="media-delete-icon" :src="ICON_DELETE" mode="aspectFit" />
-              </view>
-            </view>
-            <view v-if="mediaList.length < 9" class="media-add" @click="chooseMedia">
-              <text class="add-icon">+</text>
-              <text class="add-text">添加</text>
-            </view>
+    <!-- 上传图片/视频 -->
+    <view class="media-section">
+      <view class="section-title">上传图片/视频(可选)</view>
+      <view class="media-list">
+        <view
+          v-for="(item, index) in mediaList"
+          :key="index"
+          class="media-item"
+        >
+          <ClientImageLoader
+            v-if="item.type === 'image'"
+            :src="item.url"
+            mode="aspectFill"
+            class="media-image"
+            @click="previewImage(index)"
+          />
+          <video v-else :src="item.url" controls @click="previewVideo(index)" />
+          <view class="media-delete" @click="removeMedia(index)">
+            <image class="media-delete-icon" :src="ICON_DELETE" mode="aspectFit" />
           </view>
-          <view class="media-tip">最多上传9张图片或1个视频</view>
         </view>
-
-        <view class="anonymous-section">
-          <checkbox-group @change="onAnonymousChange">
-            <label class="anonymous-label">
-              <checkbox :checked="isAnonymous" value="1" color="#0a9f6d" />
-              <text>匿名评价</text>
-            </label>
-          </checkbox-group>
-          <text class="anonymous-tip">匿名后用户名称显示为"匿名用户"</text>
+        <view v-if="mediaList.length < 9" class="media-add" @click="chooseMedia">
+          <text class="add-icon">+</text>
+          <text class="add-text">添加</text>
         </view>
       </view>
+      <view class="media-tip">最多上传9张图片或1个视频</view>
+    </view>
+
+    <!-- 匿名评价 -->
+    <view class="anonymous-section">
+      <checkbox-group @change="onAnonymousChange">
+        <label class="anonymous-label">
+          <checkbox :checked="isAnonymous" value="1" color="#0a9f6d" />
+          <text>匿名评价</text>
+        </label>
+      </checkbox-group>
+      <text class="anonymous-tip">匿名后用户名称显示为"匿名用户"</text>
+    </view>
+      </view>
     </scroll-view>
 
+    <!-- 提交按钮 -->
     <view class="submit-section">
-      <button class="submit-btn" :disabled="!canSubmit" @click="handleSubmit">提交评价</button>
+      <button class="submit-btn" :disabled="!canSubmit" @click="handleSubmit">
+        提交评价
+      </button>
     </view>
   </view>
 </template>
 
+
+
 <script setup>
 import { ref, computed } from 'vue'
 import { onLoad } from '@dcloudio/uni-app'
@@ -318,6 +328,8 @@ onLoad((options) => {
 })
 </script>
 
+
+
 <style lang="scss" scoped>
 .create-review-page {
   min-height: 100vh;
@@ -495,9 +507,7 @@ onLoad((options) => {
       border-radius: 12rpx;
       overflow: hidden;
 
-      image,
-      video,
-      .media-image {
+      image, video, .media-image {
         width: 100%;
         height: 100%;
       }

+ 97 - 97
sqxp-wx/pages/client/review/list.vue

@@ -11,115 +11,113 @@
       @scrolltolower="loadMore"
     >
       <view class="client-subpage-body review-list-body">
-        <ClientSkeleton v-if="initialLoading" variant="list" :rows="3" />
-        <template v-else>
-          <view class="product-card">
-            <ClientImageLoader
-              class="product-image"
-              :src="product?.imageUrl || IMG_DEFAULT_PRODUCT"
-              mode="aspectFill"
-              radius="8rpx"
-            />
-            <view class="product-info">
-              <text class="product-name">{{ product?.name || '' }}</text>
-              <text class="product-price">¥{{ formatPrice(product?.price || 0) }}</text>
-            </view>
-          </view>
+    <ClientSkeleton v-if="initialLoading" variant="list" :rows="3" />
+    <template v-else>
+    <!-- 商品信息卡片 -->
+    <view class="product-card">
+      <ClientImageLoader class="product-image" :src="product?.imageUrl || IMG_DEFAULT_PRODUCT" mode="aspectFill" radius="8rpx" />
+      <view class="product-info">
+        <text class="product-name">{{ product?.name || '' }}</text>
+        <text class="product-price">¥{{ formatPrice(product?.price || 0) }}</text>
+      </view>
+    </view>
 
-          <view class="review-stats">
-            <view class="avg-rating">
-              <text class="score">{{ reviewStats?.avgRating?.toFixed(1) || '0.0' }}</text>
-              <text class="label">分</text>
-            </view>
-            <view class="rating-bars">
-              <view v-for="i in 5" :key="i" class="rating-bar-item">
-                <text class="star-num">{{ 6 - i }}星</text>
-                <view class="bar-wrapper">
-                  <view class="bar" :style="{ width: getBarWidth(6 - i) }"></view>
-                </view>
-                <text class="count">{{ reviewStats?.ratingDistribution?.[String(6 - i)] || 0 }}</text>
-              </view>
-            </view>
+    <!-- 评价统计 -->
+    <view class="review-stats">
+      <view class="avg-rating">
+        <text class="score">{{ reviewStats?.avgRating?.toFixed(1) || '0.0' }}</text>
+        <text class="label">分</text>
+      </view>
+      <view class="rating-bars">
+        <view v-for="i in 5" :key="i" class="rating-bar-item">
+          <text class="star-num">{{ 6 - i }}星</text>
+          <view class="bar-wrapper">
+            <view class="bar" :style="{ width: getBarWidth(6 - i) }"></view>
           </view>
+          <text class="count">{{ reviewStats?.ratingDistribution?.[String(6 - i)] || 0 }}</text>
+        </view>
+      </view>
+    </view>
 
-          <view class="filter-tabs">
-            <view
-              v-for="tab in tabs"
-              :key="tab.key"
-              class="tab-item"
-              :class="{ active: currentTab === tab.key }"
-              @click="switchTab(tab.key)"
-            >
-              <text>{{ tab.label }}</text>
-              <text v-if="tab.count !== undefined" class="tab-count">{{ tab.count }}</text>
+    <!-- 筛选标签 -->
+    <view class="filter-tabs">
+      <view
+        v-for="tab in tabs"
+        :key="tab.key"
+        class="tab-item"
+        :class="{ active: currentTab === tab.key }"
+        @click="switchTab(tab.key)"
+      >
+        <text>{{ tab.label }}</text>
+        <text v-if="tab.count !== undefined" class="tab-count">{{ tab.count }}</text>
+      </view>
+    </view>
+
+    <!-- 评价列表 -->
+      <view v-for="review in reviewList" :key="review.id" class="review-item" hover-class="review-item--pressed" :hover-stay-time="100">
+        <!-- 用户信息 -->
+        <view class="review-header">
+          <view class="user-info">
+            <view class="avatar">{{ getAvatarText(review.userName) }}</view>
+            <view class="user-detail">
+              <text class="username">{{ review.isAnonymous ? '匿名用户' : review.userName }}</text>
+              <van-rate :value="review.rating" readonly size="12" />
             </view>
           </view>
+          <text class="review-time">{{ formatTime(review.createAt) }}</text>
+        </view>
+
+        <!-- 评价内容 -->
+        <view class="review-content">
+          <text v-if="review.ratingTags" class="tags">{{ formatTags(review.ratingTags) }}</text>
+          <text class="content-text">{{ review.content }}</text>
+        </view>
 
+        <!-- 媒体 -->
+        <view v-if="review.mediaList?.length" class="review-media">
           <view
-            v-for="review in reviewList"
-            :key="review.id"
-            class="review-item"
-            hover-class="review-item--pressed"
-            :hover-stay-time="100"
+            v-for="media in review.mediaList"
+            :key="media.id"
+            class="media-item"
+            @click="previewImage(media.fileUrl)"
           >
-            <view class="review-header">
-              <view class="user-info">
-                <view class="avatar">{{ getAvatarText(review.userName) }}</view>
-                <view class="user-detail">
-                  <text class="username">{{ review.isAnonymous ? '匿名用户' : review.userName }}</text>
-                  <van-rate :value="review.rating" readonly size="12" />
-                </view>
-              </view>
-              <text class="review-time">{{ formatTime(review.createAt) }}</text>
-            </view>
-
-            <view class="review-content">
-              <text v-if="review.ratingTags" class="tags">{{ formatTags(review.ratingTags) }}</text>
-              <text class="content-text">{{ review.content }}</text>
-            </view>
-
-            <view v-if="review.mediaList?.length" class="review-media">
-              <view
-                v-for="media in review.mediaList"
-                :key="media.id"
-                class="media-item"
-                @click="previewImage(media.fileUrl)"
-              >
-                <ClientImageLoader v-if="media.mediaType === 'image'" :src="media.fileUrl" mode="aspectFill" class="media-image" />
-                <video v-else :src="media.fileUrl" controls />
-              </view>
-            </view>
-
-            <view v-if="review.replyContent" class="seller-reply">
-              <text class="reply-label">商家回复:</text>
-              <text class="reply-text">{{ review.replyContent }}</text>
-              <text v-if="review.replyTime" class="reply-time">{{ formatTime(review.replyTime) }}</text>
-            </view>
-
-            <view class="review-actions">
-              <view class="like-btn" @click="handleLike(review)">
-                <text class="icon">👍</text>
-                <text class="count">{{ review.likeCount || 0 }}</text>
-              </view>
-            </view>
-          </view>
-
-          <view v-if="loading" class="loading-more">
-            <van-loading size="20px">加载中...</van-loading>
+            <ClientImageLoader v-if="media.mediaType === 'image'" :src="media.fileUrl" mode="aspectFill" class="media-image" />
+            <video v-else :src="media.fileUrl" controls />
           </view>
-          <view v-else-if="noMore" class="no-more">
-            <text>没有更多了</text>
+        </view>
+
+        <!-- 商家回复 -->
+        <view v-if="review.replyContent" class="seller-reply">
+          <text class="reply-label">商家回复:</text>
+          <text class="reply-text">{{ review.replyContent }}</text>
+          <text v-if="review.replyTime" class="reply-time">{{ formatTime(review.replyTime) }}</text>
+        </view>
+
+        <!-- 点赞 -->
+        <view class="review-actions">
+          <view class="like-btn" @click="handleLike(review)">
+            <text class="icon">👍</text>
+            <text class="count">{{ review.likeCount || 0 }}</text>
           </view>
+        </view>
+      </view>
 
-          <view v-if="!loading && reviewList.length === 0" class="empty-state">
-            <image class="empty-image" :src="IMG_EMPTY" mode="aspectFit" />
-            <text class="empty-text">暂无评价</text>
-          </view>
-        </template>
+      <!-- 加载更多 -->
+      <view v-if="loading" class="loading-more">
+        <van-loading size="20px">加载中...</van-loading>
+      </view>
+      <view v-else-if="noMore" class="no-more">
+        <text>没有更多了</text>
       </view>
-    </scroll-view>
-  </view>
-</template>
+
+    <!-- 空状态 -->
+    <view v-if="!loading && reviewList.length === 0" class="empty-state">
+      <image class="empty-image" :src="IMG_EMPTY" mode="aspectFit" />
+      <text class="empty-text">暂无评价</text>
+    </view>
+    </template>
+
+
 
 <script setup>
 import { ref, computed, onMounted } from 'vue'
@@ -291,6 +289,8 @@ onMounted(async () => {
 })
 </script>
 
+
+
 <style lang="scss" scoped>
 .review-list-page {
   min-height: 100vh;

+ 26 - 27
sqxp-wx/pages/client/settlement/settlement.vue

@@ -1,3 +1,28 @@
+<template>
+  <view class="page client-theme client-subpage">
+    <view class="client-subpage-hero settlement-hero">
+      <text class="client-subpage-hero__title">结算记录</text>
+      <text class="client-subpage-hero__sub">累计已结算(元)</text>
+      <text class="sum-val">¥{{ formatYuan(totalSettled) }}</text>
+    </view>
+    <scroll-view scroll-y class="client-subpage-scroll settlement-scroll" :show-scrollbar="true">
+      <view class="client-subpage-body settlement-body">
+        <view v-if="loading" class="hint">加载中...</view>
+        <view v-else-if="list.length === 0" class="hint">暂无结算记录</view>
+        <view v-for="row in list" :key="row.id" class="card" hover-class="card--pressed" :hover-stay-time="100">
+      <view class="line">
+        <text class="order">订单 {{ row.orderNo || row.id.slice(0, 8) }}</text>
+        <text class="amt">¥{{ formatYuan(row.amount) }}</text>
+      </view>
+      <text class="sub">{{ row.settlementMethod || '—' }} · {{ row.settlementTime || row.createAt }}</text>
+        </view>
+      </view>
+    </scroll-view>
+  </view>
+</template>
+
+
+
 <script setup>
 import { ref, onMounted } from 'vue'
 import { useUserStore } from '@/stores/user'
@@ -53,28 +78,7 @@ onMounted(() => {
 })
 </script>
 
-<template>
-  <view class="page client-theme client-subpage">
-    <view class="client-subpage-hero settlement-hero">
-      <text class="client-subpage-hero__title">结算记录</text>
-      <text class="client-subpage-hero__sub">累计已结算(元)</text>
-      <text class="sum-val">¥{{ formatYuan(totalSettled) }}</text>
-    </view>
-    <scroll-view scroll-y class="client-subpage-scroll settlement-scroll" :show-scrollbar="true">
-      <view class="client-subpage-body settlement-body">
-        <view v-if="loading" class="hint">加载中...</view>
-        <view v-else-if="list.length === 0" class="hint">暂无结算记录</view>
-        <view v-for="row in list" :key="row.id" class="card" hover-class="card--pressed" :hover-stay-time="100">
-          <view class="line">
-            <text class="order">订单 {{ row.orderNo || row.id.slice(0, 8) }}</text>
-            <text class="amt">¥{{ formatYuan(row.amount) }}</text>
-          </view>
-          <text class="sub">{{ row.settlementMethod || '—' }} · {{ row.settlementTime || row.createAt }}</text>
-        </view>
-      </view>
-    </scroll-view>
-  </view>
-</template>
+
 
 <style lang="scss" scoped>
 .page {
@@ -100,11 +104,6 @@ onMounted(() => {
 .card--pressed {
   opacity: 0.94;
 }
-
-.sum-val {
-  font-size: 44rpx;
-  font-weight: bold;
-}
 .hint {
   text-align: center;
   color: var(--c-muted);