| | |
| | | --> |
| | | <div @click.stop="$parent.$parent.onEditer(index)" class="drag optional" :class="{selected: index === selectedIndex}"> |
| | | <div class="supplier" :style="{background: item.style.background}"> |
| | | <div class="supplier-time" v-for="(supplier, index) in item.data" :key="index"> |
| | | <!-- 筛选栏 --> |
| | | <div class="supplier-set" v-if="item.params.is_sort||(item.params.category==0&&item.params.is_category)||item.params.is_filter" > |
| | | <div class="filter-item" v-if="item.params.is_sort" > |
| | | <span class="filter-text">{{ sortOptions[currentSort] ? sortOptions[currentSort].text : '智能排序' }}</span> |
| | | <span class="filter-icon" :class="{ 'rotate': showSortDropdown }">▼</span> |
| | | </div> |
| | | <div class="filter-item" v-if="item.params.category==0&&item.params.is_category" > |
| | | <span class="filter-text">{{ categoryOptions[currentCategory] ? categoryOptions[currentCategory].name : '分类' }}</span> |
| | | <span class="filter-icon" :class="{ 'rotate': showCategoryDropdown }">▼</span> |
| | | </div> |
| | | <div class="filter-item" v-if="item.params.is_filter" > |
| | | <span class="filter-text">筛选</span> |
| | | <span class="filter-badge" v-if="hasActiveFilters">●</span> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 排序下拉面板 --> |
| | | <div class="sort-dropdown" v-if="showSortDropdown" @click.stop> |
| | | <div class="sort-option" v-for="(option, idx) in sortOptions" :key="idx" @click.stop="selectSort(idx)"> |
| | | <span class="sort-option-text" :class="{ 'active': currentSort === idx }">{{ option.text }}</span> |
| | | <span class="sort-option-check" v-if="currentSort === idx">✓</span> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 分类下拉面板 --> |
| | | <div class="category-dropdown" v-if="showCategoryDropdown" @click.stop> |
| | | <div class="sort-option" v-for="(option, idx) in categoryOptions" :key="idx" @click.stop="selectCategory(idx)"> |
| | | <span class="sort-option-text" :class="{ 'active': currentCategory === idx }">{{ option.name }}</span> |
| | | <span class="sort-option-check" v-if="currentCategory === idx">✓</span> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 遮罩层 --> |
| | | <div class="mask" v-if="showSortDropdown || showCategoryDropdown || showFilterPanel" @click.stop="closeAllDropdowns"></div> |
| | | |
| | | <!-- 筛选面板 --> |
| | | <div class="filter-panel" v-if="showFilterPanel" @click.stop> |
| | | <div class="filter-panel-header"> |
| | | <span class="filter-panel-title">筛选</span> |
| | | <span class="filter-reset" @click="resetFilters">重置</span> |
| | | </div> |
| | | |
| | | <div class="filter-section"> |
| | | <div class="filter-section-title">距离范围</div> |
| | | <div class="filter-tags"> |
| | | <div class="filter-tag" v-for="(distItem, idx) in distanceOptions" :key="idx" |
| | | :class="{ 'active': selectedDistance === idx }" @click.stop="selectDistance(idx)"> |
| | | {{ distItem.label }} |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="filter-section"> |
| | | <div class="filter-section-title">人均价格</div> |
| | | <div class="filter-tags"> |
| | | <div class="filter-tag" v-for="(priceItem, idx) in priceOptions" :key="idx" |
| | | :class="{ 'active': selectedPrice === idx }" @click.stop="selectPrice(idx)"> |
| | | {{ priceItem.label }} |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="filter-section"> |
| | | <div class="filter-section-title">服务评分</div> |
| | | <div class="filter-tags"> |
| | | <div class="filter-tag" v-for="(scoreItem, idx) in scoreOptions" :key="idx" |
| | | :class="{ 'active': selectedScore === idx }" @click.stop="selectScore(idx)"> |
| | | {{ scoreItem.label }} |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="filter-section"> |
| | | <div class="filter-section-title">优惠类型</div> |
| | | <div class="filter-tags"> |
| | | <div class="filter-tag" :class="{ 'active': showCouponOnly }" @click.stop="toggleCouponOnly"> |
| | | 仅看有券 |
| | | </div> |
| | | <div class="filter-tag" :class="{ 'active': showTopRanked }" @click.stop="toggleTopRanked"> |
| | | 好评榜前10 |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="filter-panel-footer"> |
| | | <div class="filter-confirm-btn" @click.stop="applyFilters">确定</div> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="supplier-time" v-for="(supplier, idx) in filteredData" :key="idx"> |
| | | <div class="supplier-data"> |
| | | <div class="supplier-name"> |
| | | 商户名称 |
| | |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 空状态 --> |
| | | <div class="empty-state" v-if="filteredData.length === 0"> |
| | | <span class="empty-text">暂无符合条件的商户</span> |
| | | </div> |
| | | |
| | | <div class="btn-edit-del"> |
| | | <div class="btn-del" @click.stop="$parent.$parent.onDeleleItem(index)">删除</div> |
| | | </div> |
| | |
| | | <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: [] |
| | | }; |
| | | }, |
| | | created() { |
| | | console.log(this.item) |
| | | }, |
| | | props: ['item', 'index', 'selectedIndex'], |
| | | methods: {} |
| | | computed: { |
| | | filteredData() { |
| | | let data = [...(this.item.data || [])]; |
| | | |
| | | // 分类筛选 |
| | | if (this.categoryOptions[this.currentCategory] && this.categoryOptions[this.currentCategory].category_id > 0) { |
| | | const categoryId = this.categoryOptions[this.currentCategory].category_id; |
| | | data = data.filter(supplier => supplier.category_id === categoryId); |
| | | } |
| | | |
| | | // 筛选条件 |
| | | data = data.filter(supplier => { |
| | | // 距离筛选 |
| | | if (this.selectedDistance >= 0) { |
| | | const distance = this.parseDistance(supplier.distance); |
| | | if (distance > this.distanceOptions[this.selectedDistance].value) { |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | // 价格筛选 |
| | | if (this.selectedPrice >= 0) { |
| | | const price = supplier.average_price || 0; |
| | | const range = this.priceOptions[this.selectedPrice].value; |
| | | if (range.max === -1) { |
| | | if (price < range.min) return false; |
| | | } else { |
| | | if (price < range.min || price >= range.max) return false; |
| | | } |
| | | } |
| | | |
| | | // 评分筛选 |
| | | if (this.selectedScore >= 0) { |
| | | const score = parseFloat(supplier.server_score) || 0; |
| | | if (score < this.scoreOptions[this.selectedScore].value) { |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | // 仅看有券 |
| | | if (this.showCouponOnly && (!supplier.max_reduce_price || supplier.max_reduce_price <= 0)) { |
| | | return false; |
| | | } |
| | | |
| | | // 好评榜前10 |
| | | if (this.showTopRanked && (!supplier.ranking || supplier.ranking > 10)) { |
| | | return false; |
| | | } |
| | | |
| | | return true; |
| | | }); |
| | | |
| | | // 排序 |
| | | const sortType = this.sortOptions[this.currentSort] ? this.sortOptions[this.currentSort].value : 'smart'; |
| | | data.sort((a, b) => { |
| | | switch (sortType) { |
| | | case 'distance': |
| | | return this.parseDistance(a.distance) - this.parseDistance(b.distance); |
| | | case 'score': |
| | | return parseFloat(b.server_score) - parseFloat(a.server_score); |
| | | case 'price_low': |
| | | return (a.average_price || 0) - (b.average_price || 0); |
| | | case 'price_high': |
| | | return (b.average_price || 0) - (a.average_price || 0); |
| | | default: |
| | | return 0; |
| | | } |
| | | }); |
| | | |
| | | return data; |
| | | }, |
| | | hasActiveFilters() { |
| | | return this.currentCategory > 0 || |
| | | this.selectedDistance >= 0 || |
| | | this.selectedPrice >= 0 || |
| | | this.selectedScore >= 0 || |
| | | this.showCouponOnly || |
| | | this.showTopRanked; |
| | | } |
| | | }, |
| | | methods: { |
| | | // 获取筛选条件 |
| | | getFilterCondition() { |
| | | this.$http.get('/supplier/index/getGroupBuyCondition').then(res => { |
| | | this.sortOptions = res.data.sortOptions || []; |
| | | this.categoryOptions = res.data.category || []; |
| | | this.distanceOptions = res.data.distanceOptions || []; |
| | | this.priceOptions = res.data.priceOptions || []; |
| | | this.scoreOptions = res.data.scoreOptions || []; |
| | | }).catch(err => { |
| | | console.error('获取筛选条件失败', err); |
| | | }); |
| | | }, |
| | | 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; |
| | | }, |
| | | selectSort(index) { |
| | | this.currentSort = index; |
| | | this.showSortDropdown = false; |
| | | }, |
| | | 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; |
| | | }, |
| | | parseDistance(distanceStr) { |
| | | if (!distanceStr) return 99999; |
| | | const match = distanceStr.match(/(\d+\.?\d*)/); |
| | | return match ? parseFloat(match[1]) : 99999; |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | |
| | | .supplier{ |
| | | padding: 12px; |
| | | } |
| | | |
| | | /* 筛选栏 */ |
| | | .supplier-set{ |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | gap: 10px; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .filter-item { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 5px; |
| | | padding: 6px 12px; |
| | | background: #f8f8f8; |
| | | border-radius: 15px; |
| | | font-size: 14px; |
| | | color: #333; |
| | | cursor: pointer; |
| | | border-left: none; |
| | | width: auto; |
| | | text-align: center; |
| | | } |
| | | |
| | | .filter-text { |
| | | font-size: 14px; |
| | | color: #333; |
| | | } |
| | | |
| | | .filter-icon { |
| | | font-size: 10px; |
| | | color: #999; |
| | | transition: transform 0.3s; |
| | | } |
| | | |
| | | .filter-icon.rotate { |
| | | transform: rotate(180deg); |
| | | } |
| | | |
| | | .filter-badge { |
| | | width: 8px; |
| | | height: 8px; |
| | | background: #c73a4e; |
| | | border-radius: 50%; |
| | | font-size: 0; |
| | | } |
| | | |
| | | /* 排序下拉面板 */ |
| | | .sort-dropdown, |
| | | .category-dropdown { |
| | | position: absolute; |
| | | top: 60px; |
| | | z-index: 200; |
| | | width: 150px; |
| | | background: #ffffff; |
| | | border-radius: 8px; |
| | | box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .sort-dropdown { |
| | | left: 12px; |
| | | } |
| | | |
| | | .category-dropdown { |
| | | left: 172px; |
| | | } |
| | | |
| | | .sort-option { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding: 12px 15px; |
| | | border-bottom: 1px solid #f5f5f5; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .sort-option:last-child { |
| | | border-bottom: none; |
| | | } |
| | | |
| | | .sort-option:hover { |
| | | background: #f8f8f8; |
| | | } |
| | | |
| | | .sort-option-text { |
| | | font-size: 14px; |
| | | color: #333; |
| | | } |
| | | |
| | | .sort-option-text.active { |
| | | color: #c73a4e; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .sort-option-check { |
| | | color: #c73a4e; |
| | | font-size: 14px; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | /* 遮罩层 */ |
| | | .mask { |
| | | position: absolute; |
| | | top: 0; |
| | | left: 0; |
| | | right: 0; |
| | | bottom: 0; |
| | | background: rgba(0, 0, 0, 0.5); |
| | | z-index: 150; |
| | | } |
| | | |
| | | /* 筛选面板 */ |
| | | .filter-panel { |
| | | position: absolute; |
| | | bottom: 0; |
| | | left: 0; |
| | | right: 0; |
| | | z-index: 200; |
| | | background: #ffffff; |
| | | border-radius: 16px 16px 0 0; |
| | | max-height: 400px; |
| | | overflow-y: auto; |
| | | } |
| | | |
| | | .filter-panel-header { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding: 15px 12px; |
| | | border-bottom: 1px solid #f5f5f5; |
| | | } |
| | | |
| | | .filter-panel-title { |
| | | font-size: 16px; |
| | | font-weight: bold; |
| | | color: #333; |
| | | } |
| | | |
| | | .filter-reset { |
| | | font-size: 14px; |
| | | color: #999; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .filter-section { |
| | | padding: 15px 12px; |
| | | border-bottom: 1px solid #f5f5f5; |
| | | } |
| | | |
| | | .filter-section-title { |
| | | font-size: 14px; |
| | | font-weight: bold; |
| | | color: #333; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .filter-tags { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .filter-tag { |
| | | padding: 8px 16px; |
| | | background: #f8f8f8; |
| | | border-radius: 4px; |
| | | font-size: 13px; |
| | | color: #666; |
| | | white-space: nowrap; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .filter-tag.active { |
| | | background: #fff5f6; |
| | | color: #c73a4e; |
| | | border: 1px solid #c73a4e; |
| | | } |
| | | |
| | | .filter-panel-footer { |
| | | padding: 12px; |
| | | border-top: 1px solid #f5f5f5; |
| | | } |
| | | |
| | | .filter-confirm-btn { |
| | | width: 100%; |
| | | height: 44px; |
| | | background: linear-gradient(to right, #c73a4e, #e74c3c); |
| | | border-radius: 22px; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | color: #ffffff; |
| | | font-size: 16px; |
| | | font-weight: bold; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .filter-confirm-btn:hover { |
| | | opacity: 0.9; |
| | | } |
| | | |
| | | /* 空状态 */ |
| | | .empty-state { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | padding: 50px 0; |
| | | } |
| | | |
| | | .empty-text { |
| | | font-size: 14px; |
| | | color: #999; |
| | | } |
| | | .supplier-time{ |
| | | background: #ffff; |
| | | margin-bottom: 10px; |