mobile/components/diy/groupBuy/groupBuy.vue
@@ -1,77 +1,404 @@
<template>
   <view class="diy-groupbuy" :style="{ background: itemData.style.background }">
      <view class="groupbuy-item" v-for="(supplier, index) in itemData.data" :key="index">
         <view class="supplier-data" @click="gotoSupplier(supplier.supplier_id)">
            <!-- 商户名称 -->
            <view class="supplier-name">
               {{ supplier.supplier_name || '商户名称' }}
            </view>
      <!-- 顶部筛选栏 -->
      <view class="filter-bar" v-if="itemData.params.is_sort||(itemData.params.category==0&&itemData.params.is_category)||itemData.params.is_filter">
         <view class="filter-item" v-if="itemData.params.is_sort"  @click="toggleSort">
            <text class="filter-text">{{ sortOptions[currentSort] ? sortOptions[currentSort].text : '智能排序' }}</text>
            <text class="filter-icon" :class="{ 'rotate': showSortDropdown }">▼</text>
         </view>
         <view class="filter-item" v-if="itemData.params.category==0&&itemData.params.is_category"  @click="toggleCategory">
            <text class="filter-text">{{ currentCategory ? categoryOptions[currentCategory].name : '分类' }}</text>
            <text class="filter-icon" :class="{ 'rotate': showCategoryDropdown }">▼</text>
         </view>
         <view class="filter-item" v-if="itemData.params.is_filter" @click="toggleFilter">
            <text class="filter-text">筛选</text>
            <text class="filter-badge" v-if="hasActiveFilters">●</text>
         </view>
      </view>
            <!-- 商户详情 -->
            <view class="supplier-detail" >
               <view class="supplier-detail-left">
                  <image v-if="itemData.style.server_score_image" :src="itemData.style.server_score_image" class="supplier-detail-img" mode="aspectFill"></image>
                  <view class="supplier-detail-score">{{ supplier.server_score }} {{ supplier.server_score_text }}</view>
                  <view class="supplier-detail-comment" v-if="supplier.comment > 0">{{ supplier.comment }}条评论</view>
                  <view class="supplier-detail-price" v-if="supplier.average_price > 0">¥{{ supplier.average_price }}/人</view>
               </view>
               <view class="supplier-detail-right">
                  <view class="supplier-detail-distance">{{ supplier.distance }}</view>
               </view>
            </view>
      <!-- 排序下拉面板 -->
      <view class="sort-dropdown" v-if="showSortDropdown" @click.stop>
         <view class="sort-option" v-for="(option, index) in sortOptions" :key="index" @click="selectSort(index)">
            <text class="sort-option-text" :class="{ 'active': currentSort === index }">{{ option.text }}</text>
            <text class="sort-option-check" v-if="currentSort === index">✓</text>
         </view>
      </view>
            <!-- 优惠信息 -->
            <view class="supplier-detail-discount">
               <view class="discount-left" v-if="supplier.max_reduce_price && supplier.max_reduce_price > 0">
                  <view class="discount-left-text">超值券</view>
                  <view class="discount-left-price">最高减{{ supplier.max_reduce_price }}元</view>
               </view>
               <view class="discount-right" v-if="supplier.ranking && supplier.ranking <= 10 && supplier.ranking < 999">
                  好评榜第{{ supplier.ranking }}名
      <!-- 分类下拉面板 -->
      <view class="category-dropdown" :class="{ 'align-first': !itemData.params.is_sort }" v-if="showCategoryDropdown" @click.stop>
         <view class="sort-option" v-for="(option, index) in categoryOptions" :key="index" @click="selectCategory(index)">
            <text class="sort-option-text" :class="{ 'active': currentCategory === index }">{{ option.name }}</text>
            <text class="sort-option-check" v-if="currentCategory === index">✓</text>
         </view>
      </view>
      <!-- 遮罩层 -->
      <view class="mask" v-if="showSortDropdown || showCategoryDropdown || showFilterPanel" @click="closeAllDropdowns"></view>
      <!-- 筛选面板 -->
      <view class="filter-panel" v-if="showFilterPanel" @click.stop>
         <view class="filter-panel-header">
            <text class="filter-panel-title">筛选</text>
            <text class="filter-reset" @click="resetFilters">重置</text>
         </view>
         <view class="filter-section">
            <view class="filter-section-title">距离范围</view>
            <view class="filter-tags">
               <view class="filter-tag" v-for="(item, index) in distanceOptions" :key="index"
                  :class="{ 'active': selectedDistance === index }" @click="selectDistance(index)">
                  {{ item.label }}
               </view>
            </view>
         </view>
         <!-- 商品列表 -->
         <scroll-view class="supplier-product" scroll-x="true" show-scrollbar="false">
            <view class="product-item" v-for="(product, productKey) in supplier.productList" :key="productKey"
               @click="gotoDetail(product.product_id)">
               <!-- 商品图片 -->
               <view class="product-img">
                  <image :src="product.product_image" mode="aspectFill"></image>
                  <!-- 优惠券标签 -->
                  <view class="product-discount" v-if="product.reduce_price > 0">
                     <view class="discount-type">超值券</view>
                     <view class="discount-price">减{{ product.reduce_price }}元</view>
         <view class="filter-section">
            <view class="filter-section-title">人均价格</view>
            <view class="filter-tags">
               <view class="filter-tag" v-for="(item, index) in priceOptions" :key="index"
                  :class="{ 'active': selectedPrice === index }" @click="selectPrice(index)">
                  {{ item.label }}
               </view>
            </view>
         </view>
         <view class="filter-section">
            <view class="filter-section-title">服务评分</view>
            <view class="filter-tags">
               <view class="filter-tag" v-for="(item, index) in scoreOptions" :key="index"
                  :class="{ 'active': selectedScore === index }" @click="selectScore(index)">
                  {{ item.label }}
               </view>
            </view>
         </view>
         <view class="filter-section">
            <view class="filter-section-title">优惠类型</view>
            <view class="filter-tags">
               <view class="filter-tag" :class="{ 'active': showCouponOnly }" @click="toggleCouponOnly">
                  仅看有券
               </view>
               <view class="filter-tag" :class="{ 'active': showTopRanked }" @click="toggleTopRanked">
                  好评榜前10
               </view>
            </view>
         </view>
         <view class="filter-panel-footer">
            <view class="filter-confirm-btn" @click="applyFilters">确定</view>
         </view>
      </view>
      <!-- 团购列表容器 -->
      <scroll-view
         class="groupbuy-scroll"
         scroll-y="true"
         @scrolltolower="loadMore"
         lower-threshold="100"
      >
         <!-- 团购列表 -->
         <view class="groupbuy-item" v-for="(supplier, index) in listData" :key="index">
            <view class="supplier-data" @click="gotoSupplier(supplier.shop_supplier_id)">
               <!-- 商户名称 -->
               <view class="supplier-name">
                  {{ supplier.supplier_name || '商户名称' }}
               </view>
               <!-- 商户详情 -->
               <view class="supplier-detail" >
                  <view class="supplier-detail-left">
                     <image v-if="itemData.style.server_score_image" :src="itemData.style.server_score_image" class="supplier-detail-img" mode="aspectFill"></image>
                     <view class="supplier-detail-score">{{ supplier.server_score }} {{ supplier.server_score_text }}</view>
                     <view class="supplier-detail-comment" v-if="supplier.comment_count > 0">{{ supplier.comment_count }}条评论</view>
                     <view class="supplier-detail-price" v-if="supplier.average_price > 0">¥{{ supplier.average_price }}/人</view>
                  </view>
                  <view class="supplier-detail-right">
                     <view class="supplier-detail-distance">{{ supplier.distance }}</view>
                  </view>
               </view>
               <!-- 商品名称 -->
               <view class="product-name">
                  {{ product.product_name }}
               </view>
               <!-- 商品价格和购买按钮 -->
               <view class="product-price">
                  <view class="price">¥{{ product.product_price }}</view>
                  <view class="buy-btn">抢购</view>
               <!-- 优惠信息 -->
               <view class="supplier-detail-discount">
                  <view class="discount-left" v-if="supplier.max_reduce_price && supplier.max_reduce_price > 0">
                     <view class="discount-left-text">超值券</view>
                     <view class="discount-left-price">最高减{{ supplier.max_reduce_price }}元</view>
                  </view>
                  <view class="discount-right" v-if="supplier.ranking && supplier.ranking <= 10 && supplier.ranking < 999">
                     好评榜第{{ supplier.ranking }}名
                  </view>
               </view>
            </view>
         </scroll-view>
      </view>
            <!-- 商品列表 -->
            <scroll-view class="supplier-product" scroll-x="true" show-scrollbar="false">
               <view class="product-item" v-for="(product, productKey) in supplier.productList" :key="productKey"
                  @click="gotoDetail(product.product_id)">
                  <!-- 商品图片 -->
                  <view class="product-img">
                     <image :src="product.product_image" mode="aspectFill"></image>
                     <!-- 优惠券标签 -->
                     <view class="product-discount" v-if="product.reduce_price > 0">
                        <view class="discount-type">超值券</view>
                        <view class="discount-price">减{{ product.reduce_price }}元</view>
                     </view>
                  </view>
                  <!-- 商品名称 -->
                  <view class="product-name">
                     {{ product.product_name }}
                  </view>
                  <!-- 商品价格和购买按钮 -->
                  <view class="product-price">
                     <view class="price">¥{{ product.product_price }}</view>
                     <view class="buy-btn">抢购</view>
                  </view>
               </view>
            </scroll-view>
         </view>
         <!-- 加载状态 -->
         <view class="load-more" v-if="listData.length > 0">
            <view v-if="isLoadingMore" class="loading-text">加载中...</view>
            <view v-else-if="!hasMore" class="loading-text">没有更多了</view>
         </view>
         <!-- 空状态 -->
         <view class="empty-state" v-if="listData.length === 0 && !isLoading">
            <text class="empty-text">暂无符合条件的商户</text>
         </view>
      </scroll-view>
   </view>
</template>
<script>
export default {
   data() {
      return {};
      return {
         showSortDropdown: false,
         showCategoryDropdown: false,
         showFilterPanel: false,
         currentSort: 0,
         currentCategory: 0,
         selectedDistance: -1,
         selectedPrice: -1,
         selectedScore: -1,
         showCouponOnly: false,
         showTopRanked: false,
         sortOptions: [],
         categoryOptions: [],
         distanceOptions: [],
         priceOptions: [],
         scoreOptions: [],
         listData:[],
         isLoading: false,
         isLoadingMore: false,
         hasMore: true,
         currentPage: 1,
         param:{
            longitude:0,
            latitude:0,
            list_rows:15
         }
      };
   },
   props: ['itemData'],
   computed: {
      hasActiveFilters() {
         return this.selectedDistance >= 0 ||
               this.selectedPrice >= 0 ||
               this.selectedScore >= 0 ||
               this.showCouponOnly ||
               this.showTopRanked;
      }
   },
   created() {
      console.log('团购组件数据:', this.itemData);
      this.getFilterCondition();
      this.getList();
   },
   methods: {
      // 获取筛选条件
      getFilterCondition() {
         let self = this;
         this._get('supplier.index/getGroupBuyCondition', {}, res => {
            self.sortOptions = res.data.sortOptions || [];
            self.categoryOptions = res.data.category || [];
            self.distanceOptions = res.data.distanceOptions || [];
            self.priceOptions = res.data.priceOptions || [];
            self.scoreOptions = res.data.scoreOptions || [];
         });
      },
      toggleSort() {
         this.showSortDropdown = !this.showSortDropdown;
         this.showCategoryDropdown = false;
         this.showFilterPanel = false;
      },
      toggleCategory() {
         this.showCategoryDropdown = !this.showCategoryDropdown;
         this.showSortDropdown = false;
         this.showFilterPanel = false;
      },
      toggleFilter() {
         this.showFilterPanel = !this.showFilterPanel;
         this.showSortDropdown = false;
         this.showCategoryDropdown = false;
      },
      closeAllDropdowns() {
         this.showSortDropdown = false;
         this.showCategoryDropdown = false;
         this.showFilterPanel = false;
      },
      selectCategory(index) {
         this.currentCategory = index;
         this.showCategoryDropdown = false;
         // 分类改变后重新请求数据
         this.resetAndLoad();
      },
      selectDistance(index) {
         this.selectedDistance = this.selectedDistance === index ? -1 : index;
      },
      selectPrice(index) {
         this.selectedPrice = this.selectedPrice === index ? -1 : index;
      },
      selectScore(index) {
         this.selectedScore = this.selectedScore === index ? -1 : index;
      },
      toggleCouponOnly() {
         this.showCouponOnly = !this.showCouponOnly;
      },
      toggleTopRanked() {
         this.showTopRanked = !this.showTopRanked;
      },
      resetFilters() {
         this.selectedDistance = -1;
         this.selectedPrice = -1;
         this.selectedScore = -1;
         this.showCouponOnly = false;
         this.showTopRanked = false;
      },
      applyFilters() {
         this.showFilterPanel = false;
         // 应用筛选后重新请求数据
         this.resetAndLoad();
      },
      selectSort(index) {
         this.currentSort = index;
         this.showSortDropdown = false;
         // 排序改变后重新请求数据
         this.resetAndLoad();
      },
      parseDistance(distanceStr) {
         if (!distanceStr) return 99999;
         const match = distanceStr.match(/(\d+\.?\d*)/);
         return match ? parseFloat(match[1]) : 99999;
      },
      resetAndLoad() {
         // 重置分页状态
         this.currentPage = 1;
         this.hasMore = true;
         this.listData = [];
         // 重新加载数据
         this.getList();
      },
      getList(isLoadMore = false) {
         if (this.isLoading || this.isLoadingMore) {
            return;
         }
         // 加载更多时检查是否还有更多数据
         if (isLoadMore && !this.hasMore) {
            return;
         }
         // 设置加载状态
         if (isLoadMore) {
            this.isLoadingMore = true;
         } else {
            this.isLoading = true;
         }
         let self = this;
         let param = {
            ...this.param,
            page: this.currentPage,
            longitude: uni.getStorageSync('longitude') || 0,
            latitude: uni.getStorageSync('latitude') || 0,
            sortType: this.sortOptions[this.currentSort] ? this.sortOptions[this.currentSort].value : 'smart'
         };
      if (this.itemData.params.category>0){
        param.category_id = this.itemData.params.category;
      }
      param.product_num= this.itemData.params.product_num
      // 添加分类筛选
         if (this.categoryOptions[this.currentCategory] && this.categoryOptions[this.currentCategory].category_id > 0) {
            param.category_id = this.categoryOptions[this.currentCategory].category_id;
         }
         // 添加筛选条件到参数
         if (this.selectedDistance >= 0) {
            const distanceValue = this.distanceOptions[this.selectedDistance].value;
            // 距离"不限"选项的value是-1,不传递参数
            if (distanceValue > 0) {
               param.max_distance = distanceValue;
            }
         }
         if (this.selectedPrice >= 0) {
            const range = this.priceOptions[this.selectedPrice].value;
            // 如果是"不限"选项(value=-1),不传递价格参数
            if (typeof range === 'object' && range.min !== undefined) {
               param.price_min = range.min;
               param.price_max = range.max;
            }
         }
         if (this.selectedScore >= 0) {
            const scoreValue = this.scoreOptions[this.selectedScore].value;
            // 评分"不限"选项的value是-1,不传递参数
            if (scoreValue > 0) {
               param.min_score = scoreValue;
            }
         }
         if (this.showCouponOnly) {
            param.has_coupon = 1;
         }
         if (this.showTopRanked) {
            param.top_ranked = 1;
         }
         this._get('supplier.index/getGroupBuyList', param, function(res) {
            const newData = res.data.list.data || [];
            const total = res.data.list.total || 0;
            if (isLoadMore) {
               // 加载更多,追加数据
               self.listData = [...self.listData, ...newData];
            } else {
               // 首次加载,替换数据
               self.listData = newData;
            }
            // 判断是否还有更多数据
            const loadedCount = self.listData.length;
            self.hasMore = loadedCount < total;
            // 清除加载状态
            if (isLoadMore) {
               self.isLoadingMore = false;
            } else {
               self.isLoading = false;
            }
         });
      },
      loadMore() {
         if (this.hasMore && !this.isLoadingMore && !this.isLoading) {
            this.currentPage++;
            this.getList(true);
         }
      },
      /**
       * 跳转商品详情
       */
@@ -79,8 +406,9 @@
         let url = '/pages/product/detail/detail?product_id=' + productId;
         this.gotoPage(url);
      },
        gotoSupplier(productId) {
         let url = '/pages/shop/shop?supplier_id=' + productId;
      gotoSupplier(supplierId) {
         let url = '/pages/shop/shop?shop_supplier_id=' + supplierId;
            uni.setStorageSync('shop_supplier_id',supplierId)
         this.gotoPage(url);
      }
   }
@@ -89,9 +417,225 @@
<style scoped>
.diy-groupbuy {
   position: relative;
   padding: 24rpx;
   padding-top: 0;
}
/* 列表滚动容器 */
.groupbuy-scroll {
   height: calc(100vh - 88rpx);
   width: 100%;
}
/* 加载更多状态 */
.load-more {
   padding: 30rpx 0;
   text-align: center;
}
.loading-text {
   font-size: 28rpx;
   color: #999;
}
/* 筛选栏 */
.filter-bar {
   position: sticky;
   top: 0;
   z-index: 100;
   display: flex;
   background: #ffffff;
   padding: 20rpx 24rpx;
   border-bottom: 1rpx solid #f0f0f0;
   justify-content: space-between;
}
.filter-item {
   display: flex;
   align-items: center;
   gap: 8rpx;
   padding: 10rpx 20rpx;
   margin-right: 20rpx;
   background: #f8f8f8;
   border-radius: 30rpx;
   font-size: 28rpx;
   color: #333;
}
.filter-text {
   font-size: 28rpx;
   color: #333;
}
.filter-icon {
   font-size: 20rpx;
   color: #999;
   transition: transform 0.3s;
}
.filter-icon.rotate {
   transform: rotate(180deg);
}
.filter-badge {
   width: 12rpx;
   height: 12rpx;
   background: #c73a4e;
   border-radius: 50%;
   font-size: 0;
}
/* 排序下拉面板 */
.sort-dropdown,
.category-dropdown {
   position: absolute;
   top: 88rpx;
   z-index: 200;
   width: 200rpx;
   background: #ffffff;
   border-radius: 16rpx;
   box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.15);
   overflow: hidden;
}
.sort-dropdown {
   left: 24rpx;
}
.category-dropdown {
   left: calc(24rpx + 200rpx + 20rpx);
}
.category-dropdown.align-first {
   left: 24rpx;
}
.sort-option {
   display: flex;
   justify-content: space-between;
   align-items: center;
   padding: 24rpx 30rpx;
   border-bottom: 1rpx solid #f5f5f5;
}
.sort-option:last-child {
   border-bottom: none;
}
.sort-option-text {
   font-size: 28rpx;
   color: #333;
}
.sort-option-text.active {
   color: #c73a4e;
   font-weight: bold;
}
.sort-option-check {
   color: #c73a4e;
   font-size: 28rpx;
   font-weight: bold;
}
/* 遮罩层 */
.mask {
   position: fixed;
   top: 0;
   left: 0;
   right: 0;
   bottom: 0;
   background: rgba(0, 0, 0, 0.5);
   z-index: 150;
}
/* 筛选面板 */
.filter-panel {
   position: fixed;
   bottom: 0;
   left: 0;
   right: 0;
   z-index: 200;
   background: #ffffff;
   border-radius: 32rpx 32rpx 0 0;
   max-height: 80vh;
   overflow-y: auto;
   padding-bottom: env(safe-area-inset-bottom);
}
.filter-panel-header {
   display: flex;
   justify-content: space-between;
   align-items: center;
   padding: 30rpx 24rpx;
   border-bottom: 1rpx solid #f5f5f5;
}
.filter-panel-title {
   font-size: 32rpx;
   font-weight: bold;
   color: #333;
}
.filter-reset {
   font-size: 28rpx;
   color: #999;
}
.filter-section {
   padding: 30rpx 24rpx;
   border-bottom: 1rpx solid #f5f5f5;
}
.filter-section-title {
   font-size: 28rpx;
   font-weight: bold;
   color: #333;
   margin-bottom: 20rpx;
}
.filter-tags {
   display: flex;
   flex-wrap: wrap;
   gap: 16rpx;
}
.filter-tag {
   padding: 16rpx 32rpx;
   background: #f8f8f8;
   border-radius: 8rpx;
   font-size: 26rpx;
   color: #666;
   white-space: nowrap;
}
.filter-tag.active {
   background: #fff5f6;
   color: #c73a4e;
   border: 1rpx solid #c73a4e;
}
.filter-panel-footer {
   padding: 24rpx;
   border-top: 1rpx solid #f5f5f5;
   padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
}
.filter-confirm-btn {
   width: 100%;
   height: 88rpx;
   background: linear-gradient(to right, #c73a4e, #e74c3c);
   border-radius: 44rpx;
   display: flex;
   align-items: center;
   justify-content: center;
   color: #ffffff;
   font-size: 32rpx;
   font-weight: bold;
}
/* 团购列表 */
.groupbuy-item {
   background: #ffffff;
   margin-bottom: 20rpx;
@@ -298,4 +842,18 @@
.buy-btn:active {
   opacity: 0.8;
}
/* 空状态 */
.empty-state {
   display: flex;
   flex-direction: column;
   align-items: center;
   justify-content: center;
   padding: 100rpx 0;
}
.empty-text {
   font-size: 28rpx;
   color: #999;
}
</style>