aaa
doum
2026-06-08 cf2da3b2a63840888815c6a81cbd7948faf93533
h5/pages/index_3/index.vue
@@ -236,22 +236,41 @@
                                 <text>{{ sub.name }}</text>
                              </view>
                           </view>
                           <view class="expand_search_wrap">
                           <view class="expand_search_wrap" :style="suggestDropdownTheme">
                              <view class="expand_search" :style="searchBarStyle">
                                 <image class="search_icon" src="@/static/ic_search@2x.png" mode="aspectFit"></image>
                                 <input type="text" v-model="row.modelVal" placeholder="搜索商品名称/型号" @input="searchModel(row)" />
                                 <view class="search_divider"></view>
                                 <input type="text" v-model="row.modelVal" placeholder="搜索商品名称/型号" placeholder-class="expand_search_placeholder" @input="searchModel(row)" />
                              </view>
                              <view class="model_suggest_list" v-if="hasModelKeyword(row) && modelOptions(row).length">
                                 <view
                                    class="model_suggest_item"
                                    v-for="(g, gi) in modelOptions(row)"
                                    :key="g.id || gi"
                                    @click.stop="onClickSound(() => selectGoodsFromSearch(index, g))"
                                    @mouseenter="playHoverSound">
                                    {{ g.name }}
                              <view class="model_suggest_panel" v-if="hasModelKeyword(row) && modelOptions(row).length">
                                 <view class="model_suggest_list">
                                    <view
                                       class="model_suggest_item"
                                       v-for="(g, gi) in modelOptions(row)"
                                       :key="g.id || gi"
                                       @click.stop="onClickSound(() => selectGoodsFromSearch(index, g))"
                                       @mouseenter="playHoverSound">
                                       <view class="model_suggest_thumb">
                                          <image v-if="suggestGoodsImg(g)" :src="suggestGoodsImg(g)" mode="aspectFit"></image>
                                          <view v-else class="model_suggest_thumb_ph"></view>
                                       </view>
                                       <view class="model_suggest_body">
                                          <text class="model_suggest_name">{{ g.name }}</text>
                                          <text class="model_suggest_brand" v-if="g.brandName">{{ g.brandName }}</text>
                                       </view>
                                       <view class="model_suggest_meta">
                                          <text class="model_suggest_price" v-if="g.price != null && g.price !== ''">¥{{ g.price }}</text>
                                          <text class="model_suggest_zd" v-if="g.zdPrice != null && g.zdPrice !== ''">旗舰 ¥{{ g.zdPrice }}</text>
                                       </view>
                                    </view>
                                 </view>
                                 <view class="model_suggest_footer">
                                    <text>共 {{ modelOptions(row).length }} 件可选</text>
                                 </view>
                              </view>
                              <view class="model_suggest_empty" v-else-if="hasModelKeyword(row)">暂无匹配商品</view>
                              <view class="model_suggest_empty" v-else-if="hasModelKeyword(row)">
                                 <text class="model_suggest_empty_text">未找到「{{ (row.modelVal || '').trim() }}」相关商品</text>
                              </view>
                           </view>
                        </view>
                     </view>
@@ -266,12 +285,12 @@
               <view class="product_grid">
                  <view
                     class="product_card"
                     v-for="(item, pi) in displayProducts"
                     v-for="(item, pi) in visibleProducts"
                     :key="item.id || pi"
                     @click="onClickSound(() => clickProduct(item))"
                     @mouseenter="playHoverSound">
                     <view class="product_card_img">
                        <image :src="productImg(item)" mode="aspectFit"></image>
                        <image :src="productImg(item)" mode="aspectFit" lazy-load></image>
                     </view>
                     <view class="product_card_info">
                        <text class="product_brand">{{ item.brandName || '' }}</text>
@@ -279,8 +298,20 @@
                        <text class="product_price_label">指导价 ¥ {{ item.price || '' }}</text>
                     </view>
                  </view>
                  <view class="product_card placeholder" v-if="!displayProducts.length">
                  <view class="product_card placeholder" v-if="!visibleProducts.length && !productLoadingMore">
                     <text>暂无商品</text>
                  </view>
               </view>
               <view class="product_list_footer" v-if="productTotalCount > 0">
                  <view class="product_load_hint" v-if="productLoadingMore">
                     <view class="product_load_spinner"></view>
                     <text>加载中...</text>
                  </view>
                  <view class="product_load_hint" v-else-if="hasMoreProducts">
                     <text>已显示 {{ visibleProducts.length }} / {{ productTotalCount }},继续上滑加载更多</text>
                  </view>
                  <view class="product_load_hint product_load_hint--done" v-else>
                     <text>已加载全部 {{ productTotalCount }} 件商品</text>
                  </view>
               </view>
            </view>
@@ -481,6 +512,7 @@
const DEFAULT_MAIN = '#E8DCC8'
const DEFAULT_HEADER = '#4A3728'
const DEFAULT_ROW = '#FFFFFF'
const PRODUCT_PAGE_SIZE = 30
export default {
   components: { search, bigImg },
@@ -526,6 +558,8 @@
         shopPageData: [],
         shopPageDataSou: [],
         productPageNo: 1,
         productLoadingMore: false,
         shopPageBrand: { id: '', name: '' },
         brandData: [],
         title: '',
@@ -585,7 +619,26 @@
         return { background: t.rowBg || DEFAULT_ROW }
      },
      dropdownStyle() {
         return { background: this.themeStyle.background }
         const { background, color } = this.themeStyle
         const accent = this.accentColor || '#FFD88A'
         return {
            background,
            color,
            '--picker-accent': accent,
            '--picker-hover-bg': 'rgba(255, 255, 255, 0.14)',
            '--picker-active-bg': this.hexToRgba(accent, 0.28),
            '--picker-divider': 'rgba(255, 255, 255, 0.12)'
         }
      },
      suggestDropdownTheme() {
         const { background } = this.themeStyle
         const accent = this.accentColor || '#FFD88A'
         return {
            '--suggest-theme': background,
            '--suggest-accent': accent,
            '--suggest-accent-soft': this.hexToRgba(accent, 0.22),
            '--suggest-theme-soft': this.hexToRgba(background, 0.08)
         }
      },
      searchBarStyle() {
         const s = (this.configuration && this.configuration.search) || {}
@@ -659,6 +712,16 @@
      displayProducts() {
         return Array.isArray(this.shopPageDataSou) ? this.shopPageDataSou : []
      },
      productTotalCount() {
         return this.displayProducts.length
      },
      visibleProducts() {
         const end = this.productPageNo * PRODUCT_PAGE_SIZE
         return this.displayProducts.slice(0, end)
      },
      hasMoreProducts() {
         return this.visibleProducts.length < this.productTotalCount
      },
      productListCategoryImg() {
         const row = this.activeRowIndex >= 0 ? this.shopList[this.activeRowIndex] : null
         return (row && row.categoryImgurl) ? row.categoryImgurl : ''
@@ -708,6 +771,33 @@
      this.sessionCreateTime = new Date()
      this.loadAllData()
   },
   onReachBottom() {
      this.tryLoadMoreProducts()
   },
   mounted() {
      if (typeof window !== 'undefined') {
         this._onProductScroll = () => {
            if (this.status !== 1 || !this.hasMoreProducts) return
            const doc = document.documentElement
            const scrollTop = doc.scrollTop || document.body.scrollTop || 0
            const viewHeight = doc.clientHeight || window.innerHeight
            const scrollHeight = doc.scrollHeight || document.body.scrollHeight
            if (scrollTop + viewHeight >= scrollHeight - 100) {
               this.tryLoadMoreProducts()
            }
         }
         window.addEventListener('scroll', this._onProductScroll, { passive: true })
      }
   },
   beforeDestroy() {
      if (typeof window !== 'undefined' && this._onProductScroll) {
         window.removeEventListener('scroll', this._onProductScroll)
      }
   },
   methods: {
      playHoverSound,
      playClickSound,
@@ -992,6 +1082,11 @@
         return row.modelSearchList || []
      },
      suggestGoodsImg(g) {
         if (!g || !g.imgurl) return ''
         return (g.prefixUrl || '') + g.imgurl
      },
      selectGoodsFromSearch(index, goods) {
         this.applyGoodsToRow(index, goods)
         this.hoverRowIndex = -1
@@ -1104,6 +1199,7 @@
         if (!row) {
            this.shopPageData = []
            this.shopPageDataSou = []
            this.resetProductPagination()
            return
         }
         const list = filterGoods(this.allGoods, {
@@ -1136,9 +1232,24 @@
         const kw = (this.title || '').trim()
         if (!kw) {
            this.shopPageDataSou = source.slice()
            return
         } else {
            this.shopPageDataSou = source.filter(item => matchKeyword(item, kw))
         }
         this.shopPageDataSou = source.filter(item => matchKeyword(item, kw))
         this.resetProductPagination()
      },
      resetProductPagination() {
         this.productPageNo = 1
         this.productLoadingMore = false
      },
      tryLoadMoreProducts() {
         if (this.status !== 1 || !this.hasMoreProducts || this.productLoadingMore) return
         this.productLoadingMore = true
         this.$nextTick(() => {
            this.productPageNo += 1
            this.productLoadingMore = false
         })
      },
      clickProduct(item) {
@@ -1423,6 +1534,7 @@
         this.hoverRowIndex = -1
         this.resetPkState()
         this.title = ''
         this.resetProductPagination()
         this.shopPageData = []
         this.shopPageDataSou = []
         this.loadOrderNo()
@@ -1437,6 +1549,7 @@
            this.status = 0
            this.title = ''
            this.showBrandPicker = false
            this.resetProductPagination()
            this.shopPageData = []
            this.shopPageDataSou = []
            this.activeRowIndex = -1
@@ -1935,41 +2048,76 @@
   }
}
.home_top_section .category_picker {
   position: absolute;
   top: calc(100% + 2px);
   left: -26px;
   min-width: 168px;
   z-index: 20;
   max-height: 240px;
   overflow-y: auto;
   border-radius: 0 0 6px 6px;
   padding: 6px 0;
   box-shadow: 0 8px 20px rgba(0, 0, 0, 0.22);
.home_top_section    .category_picker {
      position: absolute;
      top: calc(100% + 6px);
      left: -26px;
      min-width: 188px;
      z-index: 20;
      max-height: 260px;
      overflow-x: hidden;
      overflow-y: auto;
   border-radius: 12px;
   padding: 0;
   box-shadow: 0 12px 32px rgba(61, 46, 34, 0.28);
   border: 1px solid rgba(255, 255, 255, 0.14);
   backdrop-filter: blur(8px);
   &::-webkit-scrollbar {
      width: 4px;
   }
   &::-webkit-scrollbar-thumb {
      background: rgba(255, 255, 255, 0.28);
      border-radius: 999px;
   }
   &::before {
      content: '';
      display: block;
      height: 3px;
      background: var(--picker-accent, #FFD88A);
      border-radius: 12px 12px 0 0;
   }
   .category_picker_item {
      display: flex;
      align-items: center;
      gap: 10px;
      padding: 10px 12px;
      color: #fff;
      padding: 11px 14px;
      color: inherit;
      cursor: pointer;
      border-radius: 0;
      border-bottom: 1px solid var(--picker-divider, rgba(255, 255, 255, 0.12));
      transition: background 0.15s;
      &:last-child {
         border-bottom: none;
      }
      &:hover {
         background: rgba(255, 255, 255, 0.1);
         background: var(--picker-hover-bg, rgba(255, 255, 255, 0.14));
      }
      image {
         width: 28px;
         height: 28px;
         width: 30px;
         height: 30px;
         border-radius: 6px;
         background: rgba(255, 255, 255, 0.12);
         padding: 2px;
         box-sizing: border-box;
      }
      text {
         font-size: 13px;
         font-weight: 500;
         line-height: 1.3;
      }
   }
   .category_picker_empty {
      padding: 16px;
      padding: 20px 16px;
      text-align: center;
      color: rgba(255, 255, 255, 0.7);
      color: rgba(255, 255, 255, 0.72);
      font-size: 13px;
   }
}
@@ -2098,26 +2246,71 @@
   .brand_picker {
      position: absolute;
      top: calc(100% + 4px);
      top: calc(100% + 6px);
      right: 0;
      min-width: 120px;
      max-height: 220px;
      min-width: 136px;
      max-height: 240px;
      overflow-x: hidden;
      overflow-y: auto;
      border-radius: 8px;
      padding: 6px 0;
      box-shadow: 0 8px 20px rgba(0, 0, 0, 0.22);
      border-radius: 12px;
      padding: 0;
      box-shadow: 0 12px 32px rgba(61, 46, 34, 0.28);
      border: 1px solid rgba(255, 255, 255, 0.14);
      z-index: 20;
      backdrop-filter: blur(8px);
      &::-webkit-scrollbar {
         width: 4px;
      }
      &::-webkit-scrollbar-thumb {
         background: rgba(255, 255, 255, 0.28);
         border-radius: 999px;
      }
      &::before {
         content: '';
         display: block;
         height: 3px;
         background: var(--picker-accent, #FFD88A);
         border-radius: 12px 12px 0 0;
      }
      .brand_picker_item {
         padding: 10px 14px;
         padding: 11px 16px;
         font-size: 13px;
         color: #fff;
         font-weight: 500;
         color: inherit;
         cursor: pointer;
         white-space: nowrap;
         border-bottom: 1px solid var(--picker-divider, rgba(255, 255, 255, 0.12));
         transition: background 0.15s;
         &:hover,
         &:last-child {
            border-bottom: none;
         }
         &:hover {
            background: var(--picker-hover-bg, rgba(255, 255, 255, 0.14));
         }
         &.active {
            background: rgba(255, 255, 255, 0.12);
            background: var(--picker-active-bg, rgba(255, 216, 138, 0.28));
            font-weight: 600;
            position: relative;
            padding-left: 22px;
            &::before {
               content: '';
               position: absolute;
               left: 10px;
               top: 50%;
               transform: translateY(-50%);
               width: 4px;
               height: 4px;
               border-radius: 50%;
               background: var(--picker-accent, #FFD88A);
            }
         }
      }
   }
@@ -2431,6 +2624,14 @@
      gap: 10px;
      border-radius: 24px;
      padding: 10px 16px;
      border: 1px solid rgba(74, 55, 40, 0.1);
      box-shadow: 0 2px 12px rgba(74, 55, 40, 0.08);
      transition: box-shadow 0.2s, border-color 0.2s;
      &:focus-within {
         border-color: var(--suggest-theme, rgba(74, 55, 40, 0.22));
         box-shadow: 0 4px 16px var(--suggest-theme-soft, rgba(74, 55, 40, 0.1));
      }
      .search_icon {
         width: 14px;
@@ -2441,6 +2642,13 @@
         filter: $search-icon-filter;
      }
      .search_divider {
         width: 1px;
         height: 16px;
         background: rgba(74, 55, 40, 0.12);
         flex-shrink: 0;
      }
      input {
         flex: 1;
         font-size: 14px;
@@ -2449,52 +2657,172 @@
      }
   }
   .model_suggest_empty {
   .expand_search_placeholder {
      color: rgba(74, 55, 40, 0.38);
      font-size: 14px;
   }
   .model_suggest_panel {
      position: absolute;
      top: calc(100% + 4px);
      top: calc(100% + 6px);
      left: 0;
      right: 0;
      margin-top: 0;
      padding: 10px 14px;
      font-size: 13px;
      color: #999;
      text-align: center;
      padding: 8px;
      border-radius: 16px;
      background: #fff;
      border-radius: 8px;
      box-shadow: 0 8px 20px rgba(74, 55, 40, 0.16);
      border: 1px solid rgba(74, 55, 40, 0.08);
      box-shadow:
         0 10px 28px rgba(61, 46, 34, 0.14),
         0 2px 8px rgba(61, 46, 34, 0.06);
      z-index: 20;
   }
   .model_suggest_list {
      position: absolute;
      top: calc(100% + 4px);
      left: 0;
      right: 0;
      margin-top: 0;
      max-height: 160px;
      max-height: 204px;
      overflow-y: auto;
      border-radius: 8px;
      background: #fff;
      box-shadow: 0 8px 20px rgba(74, 55, 40, 0.16);
      border: 1px solid rgba(74, 55, 40, 0.08);
      z-index: 20;
      display: flex;
      flex-direction: column;
      gap: 6px;
      padding: 2px;
      &::-webkit-scrollbar {
         width: 4px;
      }
      &::-webkit-scrollbar-thumb {
         background: rgba(74, 55, 40, 0.18);
         border-radius: 999px;
      }
   }
   .model_suggest_item {
      padding: 10px 14px;
      font-size: 13px;
      color: $brown;
      display: flex;
      align-items: center;
      gap: 10px;
      padding: 8px 10px;
      border-radius: 12px;
      background: $panel-bg;
      border: 1px solid rgba(74, 55, 40, 0.05);
      cursor: pointer;
      border-bottom: 1px solid rgba(74, 55, 40, 0.06);
      &:last-child {
         border-bottom: none;
      }
      transition: background 0.15s, border-color 0.15s, box-shadow 0.15s, transform 0.15s;
      &:hover {
         background: rgba(255, 255, 255, 0.6);
         background: #fff;
         border-color: var(--suggest-accent, rgba(255, 216, 138, 0.85));
         box-shadow: 0 3px 10px var(--suggest-accent-soft, rgba(255, 216, 138, 0.28));
         transform: translateY(-1px);
      }
      &:active {
         transform: translateY(0);
      }
   }
   .model_suggest_thumb {
      width: 38px;
      height: 38px;
      flex-shrink: 0;
      border-radius: 10px;
      background: #fff;
      border: 1px solid rgba(74, 55, 40, 0.06);
      display: flex;
      align-items: center;
      justify-content: center;
      overflow: hidden;
      image {
         width: 34px;
         height: 34px;
      }
   }
   .model_suggest_thumb_ph {
      width: 18px;
      height: 18px;
      border-radius: 4px;
      background: rgba(74, 55, 40, 0.08);
   }
   .model_suggest_body {
      flex: 1;
      min-width: 0;
      display: flex;
      flex-direction: column;
      gap: 3px;
   }
   .model_suggest_name {
      font-size: 13px;
      font-weight: 600;
      color: $brown;
      line-height: 1.3;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
   }
   .model_suggest_brand {
      font-size: 11px;
      color: rgba(74, 55, 40, 0.42);
      line-height: 1.2;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
   }
   .model_suggest_meta {
      flex-shrink: 0;
      display: flex;
      flex-direction: column;
      align-items: flex-end;
      gap: 2px;
      min-width: 64px;
   }
   .model_suggest_price {
      font-size: 13px;
      font-weight: 700;
      color: $orange;
      line-height: 1.2;
   }
   .model_suggest_zd {
      font-size: 10px;
      color: rgba(74, 55, 40, 0.4);
      line-height: 1.2;
      white-space: nowrap;
   }
   .model_suggest_footer {
      margin-top: 6px;
      padding-top: 6px;
      border-top: 1px solid rgba(74, 55, 40, 0.06);
      text-align: center;
      text {
         font-size: 11px;
         color: rgba(74, 55, 40, 0.38);
      }
   }
   .model_suggest_empty {
      position: absolute;
      top: calc(100% + 6px);
      left: 0;
      right: 0;
      padding: 16px 14px;
      text-align: center;
      background: #fff;
      border-radius: 14px;
      box-shadow: 0 8px 20px rgba(61, 46, 34, 0.1);
      border: 1px dashed rgba(74, 55, 40, 0.12);
      z-index: 20;
   }
   .model_suggest_empty_text {
      font-size: 12px;
      color: rgba(74, 55, 40, 0.45);
      line-height: 1.5;
   }
   .col {
@@ -2636,6 +2964,41 @@
         justify-content: center;
      }
   }
   .product_list_footer {
      padding: 16px 12px 8px;
   }
   .product_load_hint {
      display: flex;
      align-items: center;
      justify-content: center;
      gap: 8px;
      padding: 10px 12px;
      border-radius: 10px;
      background: rgba(255, 255, 255, 0.72);
      border: 1px solid rgba(74, 55, 40, 0.08);
      text {
         font-size: 12px;
         color: rgba(74, 55, 40, 0.55);
         line-height: 1.4;
      }
      &--done text {
         color: rgba(74, 55, 40, 0.42);
      }
   }
   .product_load_spinner {
      width: 14px;
      height: 14px;
      border: 2px solid rgba(74, 55, 40, 0.15);
      border-top-color: $brown;
      border-radius: 50%;
      animation: spin 0.8s linear infinite;
      flex-shrink: 0;
   }
}
.pk_page {