1426 lines
31 KiB
Vue
1426 lines
31 KiB
Vue
<template>
|
|
<view class="review-container page-container-with-bg">
|
|
<!-- 搜索栏 -->
|
|
<view class="search-section">
|
|
<view class="search-wrapper">
|
|
<view class="search-input-container">
|
|
<text class="search-icon">🔍</text>
|
|
<input
|
|
class="search-input"
|
|
v-model="searchKeyword"
|
|
placeholder="搜索产品名称或品牌..."
|
|
@input="onSearch"
|
|
@confirm="onSearch"
|
|
/>
|
|
<view class="search-clear" v-if="searchKeyword" @click="clearSearch">
|
|
<text class="clear-icon">✕</text>
|
|
</view>
|
|
</view>
|
|
<view class="filter-btn" @click="showFilterModal">
|
|
<text class="filter-icon">🔧</text>
|
|
<text class="filter-text">筛选</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
|
|
|
|
<!-- 筛选面板 -->
|
|
<view class="filter-panel" v-if="showFilter">
|
|
<!-- 宠物类型筛选 -->
|
|
<view class="filter-section">
|
|
<view class="filter-title">
|
|
<text class="title-text">🐾 宠物类型</text>
|
|
</view>
|
|
<view class="type-filters">
|
|
<view
|
|
class="type-filter"
|
|
:class="{ active: currentPetType === 'all' }"
|
|
@click="setPetType('all')"
|
|
>
|
|
<text class="type-text">全部</text>
|
|
</view>
|
|
<view
|
|
class="type-filter"
|
|
v-for="type in petTypes"
|
|
:key="type.value"
|
|
:class="{ active: currentPetType === type.value }"
|
|
@click="setPetType(type.value)"
|
|
>
|
|
<text class="type-icon">{{ type.icon }}</text>
|
|
<text class="type-text">{{ type.label }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 产品分类筛选 -->
|
|
<view class="filter-section">
|
|
<view class="filter-title">
|
|
<text class="title-text">🏷️ 产品分类</text>
|
|
</view>
|
|
<view class="category-filters">
|
|
<view
|
|
class="category-filter"
|
|
:class="{ active: currentCategory === 'all' }"
|
|
@click="setCategory('all')"
|
|
>
|
|
<text class="category-text">全部</text>
|
|
</view>
|
|
<view
|
|
class="category-filter"
|
|
v-for="category in productCategories"
|
|
:key="category.value"
|
|
:class="{ active: currentCategory === category.value }"
|
|
@click="setCategory(category.value)"
|
|
>
|
|
<text class="category-text">{{ category.label }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 品牌筛选 -->
|
|
<view class="filter-section">
|
|
<view class="filter-title">
|
|
<text class="title-text">🏪 品牌筛选</text>
|
|
</view>
|
|
<view class="brand-filters">
|
|
<view
|
|
class="brand-filter"
|
|
v-for="brand in brands"
|
|
:key="brand"
|
|
:class="{ active: selectedBrands.includes(brand) }"
|
|
@click="toggleBrand(brand)"
|
|
>
|
|
<text class="brand-text">{{ brand }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 价格区间 -->
|
|
<view class="filter-section">
|
|
<view class="filter-title">
|
|
<text class="title-text">💰 价格区间</text>
|
|
</view>
|
|
<view class="price-filters">
|
|
<view
|
|
class="price-filter"
|
|
v-for="price in priceRanges"
|
|
:key="price.value"
|
|
:class="{ active: selectedPriceRange === price.value }"
|
|
@click="setPriceRange(price.value)"
|
|
>
|
|
<text class="price-text">{{ price.label }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 其他条件 -->
|
|
<view class="filter-section">
|
|
<view class="filter-title">
|
|
<text class="title-text">⚙️ 其他条件</text>
|
|
</view>
|
|
<view class="other-filters">
|
|
<view class="filter-row">
|
|
<text class="filter-label">排序:</text>
|
|
<view class="sort-filters">
|
|
<view
|
|
class="sort-filter"
|
|
:class="{ active: currentSort === 'rating' }"
|
|
@click="setSort('rating')"
|
|
>
|
|
<text class="sort-text">评分</text>
|
|
</view>
|
|
<view
|
|
class="sort-filter"
|
|
:class="{ active: currentSort === 'price' }"
|
|
@click="setSort('price')"
|
|
>
|
|
<text class="sort-text">价格</text>
|
|
</view>
|
|
<view
|
|
class="sort-filter"
|
|
:class="{ active: currentSort === 'hot' }"
|
|
@click="setSort('hot')"
|
|
>
|
|
<text class="sort-text">热度</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
<view class="filter-row">
|
|
<text class="filter-label">视图:</text>
|
|
<view class="view-filters">
|
|
<view
|
|
class="view-filter"
|
|
:class="{ active: viewMode === 'grid' }"
|
|
@click="setViewMode('grid')"
|
|
>
|
|
<text class="view-text">网格</text>
|
|
</view>
|
|
<view
|
|
class="view-filter"
|
|
:class="{ active: viewMode === 'list' }"
|
|
@click="setViewMode('list')"
|
|
>
|
|
<text class="view-text">列表</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 筛选操作按钮 -->
|
|
<view class="filter-actions">
|
|
<view class="filter-reset" @click="resetFilters">
|
|
<text class="reset-text">重置</text>
|
|
</view>
|
|
<view class="filter-apply" @click="applyFilter">
|
|
<text class="apply-text">应用筛选</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
<!-- 产品列表 -->
|
|
<view class="products-section">
|
|
<view class="products-grid" :class="viewMode" v-if="filteredProducts.length > 0">
|
|
<view
|
|
class="product-card"
|
|
v-for="product in filteredProducts"
|
|
:key="product.id"
|
|
@click="viewProductDetail(product)"
|
|
>
|
|
<view class="product-image">
|
|
<image class="product-img" :src="product.image" mode="aspectFill" />
|
|
<view class="product-badge" v-if="product.badge">
|
|
<text class="badge-text">{{ product.badge }}</text>
|
|
</view>
|
|
<view class="product-favorite" @click.stop="toggleFavorite(product)">
|
|
<text class="favorite-icon" :class="{ active: product.isFavorite }">♥</text>
|
|
</view>
|
|
</view>
|
|
<view class="product-info">
|
|
<view class="product-basic">
|
|
<text class="product-name">{{ product.name }}</text>
|
|
<view class="product-brand-tags">
|
|
<text class="product-brand">{{ product.brand }}</text>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="product-rating">
|
|
<view class="rating-stars">
|
|
<u-icon
|
|
v-for="i in 5"
|
|
:key="i"
|
|
name="star-fill"
|
|
:color="i <= product.rating ? '#FFD700' : '#DDDDDD'"
|
|
size="14"
|
|
></u-icon>
|
|
</view>
|
|
<text class="rating-score">{{ product.rating }}</text>
|
|
<text class="rating-count">({{ product.reviewCount }})</text>
|
|
</view>
|
|
|
|
<view class="product-price">
|
|
<text class="price-range">¥{{ product.priceRange }}</text>
|
|
</view>
|
|
|
|
<view class="product-tags" v-if="viewMode === 'list'">
|
|
<text
|
|
class="product-tag"
|
|
v-for="tag in product.tags"
|
|
:key="tag"
|
|
>{{ tag }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
<view class="empty-products" v-else>
|
|
<view class="empty-icon">🔍</view>
|
|
<view class="empty-text">暂无相关产品评测</view>
|
|
<view class="empty-desc">试试调整筛选条件或搜索其他关键词</view>
|
|
</view>
|
|
</view>
|
|
|
|
|
|
|
|
<!-- 筛选面板 -->
|
|
<view class="filter-panel" v-if="showFilter">
|
|
<!-- 宠物类型筛选 -->
|
|
<view class="filter-section">
|
|
<view class="filter-title">
|
|
<text class="title-text">🐾 宠物类型</text>
|
|
</view>
|
|
<view class="type-filters">
|
|
<view
|
|
class="type-filter"
|
|
:class="{ active: currentPetType === 'all' }"
|
|
@click="setPetType('all')"
|
|
>
|
|
<text class="type-text">全部</text>
|
|
</view>
|
|
<view
|
|
class="type-filter"
|
|
v-for="type in petTypes"
|
|
:key="type.value"
|
|
:class="{ active: currentPetType === type.value }"
|
|
@click="setPetType(type.value)"
|
|
>
|
|
<text class="type-icon">{{ type.icon }}</text>
|
|
<text class="type-text">{{ type.label }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 产品分类筛选 -->
|
|
<view class="filter-section">
|
|
<view class="filter-title">
|
|
<text class="title-text">🏷️ 产品分类</text>
|
|
</view>
|
|
<view class="category-filters">
|
|
<view
|
|
class="category-filter"
|
|
:class="{ active: currentCategory === 'all' }"
|
|
@click="setCategory('all')"
|
|
>
|
|
<text class="category-text">全部</text>
|
|
</view>
|
|
<view
|
|
class="category-filter"
|
|
v-for="category in productCategories"
|
|
:key="category.value"
|
|
:class="{ active: currentCategory === category.value }"
|
|
@click="setCategory(category.value)"
|
|
>
|
|
<text class="category-text">{{ category.label }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 品牌筛选 -->
|
|
<view class="filter-section">
|
|
<view class="filter-title">
|
|
<text class="title-text">🏪 品牌筛选</text>
|
|
</view>
|
|
<view class="brand-filters">
|
|
<view
|
|
class="brand-filter"
|
|
v-for="brand in brands"
|
|
:key="brand"
|
|
:class="{ active: selectedBrands.includes(brand) }"
|
|
@click="toggleBrand(brand)"
|
|
>
|
|
<text class="brand-text">{{ brand }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 价格区间 -->
|
|
<view class="filter-section">
|
|
<view class="filter-title">
|
|
<text class="title-text">💰 价格区间</text>
|
|
</view>
|
|
<view class="price-filters">
|
|
<view
|
|
class="price-filter"
|
|
v-for="price in priceRanges"
|
|
:key="price.value"
|
|
:class="{ active: selectedPriceRange === price.value }"
|
|
@click="setPriceRange(price.value)"
|
|
>
|
|
<text class="price-text">{{ price.label }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 其他条件 -->
|
|
<view class="filter-section">
|
|
<view class="filter-title">
|
|
<text class="title-text">⚙️ 其他条件</text>
|
|
</view>
|
|
<view class="other-filters">
|
|
<view class="filter-row">
|
|
<text class="filter-label">排序:</text>
|
|
<view class="sort-filters">
|
|
<view
|
|
class="sort-filter"
|
|
:class="{ active: currentSort === 'rating' }"
|
|
@click="setSort('rating')"
|
|
>
|
|
<text class="sort-text">评分</text>
|
|
</view>
|
|
<view
|
|
class="sort-filter"
|
|
:class="{ active: currentSort === 'price' }"
|
|
@click="setSort('price')"
|
|
>
|
|
<text class="sort-text">价格</text>
|
|
</view>
|
|
<view
|
|
class="sort-filter"
|
|
:class="{ active: currentSort === 'hot' }"
|
|
@click="setSort('hot')"
|
|
>
|
|
<text class="sort-text">热度</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
<view class="filter-row">
|
|
<text class="filter-label">视图:</text>
|
|
<view class="view-filters">
|
|
<view
|
|
class="view-filter"
|
|
:class="{ active: viewMode === 'grid' }"
|
|
@click="setViewMode('grid')"
|
|
>
|
|
<text class="view-text">网格</text>
|
|
</view>
|
|
<view
|
|
class="view-filter"
|
|
:class="{ active: viewMode === 'list' }"
|
|
@click="setViewMode('list')"
|
|
>
|
|
<text class="view-text">列表</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 筛选操作按钮 -->
|
|
<view class="filter-actions">
|
|
<view class="filter-reset" @click="resetFilters">
|
|
<text class="reset-text">重置</text>
|
|
</view>
|
|
<view class="filter-apply" @click="applyFilter">
|
|
<text class="apply-text">应用筛选</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
import { reactive, ref, onMounted, computed } from 'vue'
|
|
|
|
export default {
|
|
name: 'ReviewPage',
|
|
setup() {
|
|
// 响应式数据
|
|
const searchKeyword = ref('')
|
|
const currentPetType = ref('all')
|
|
const currentCategory = ref('all')
|
|
const currentSort = ref('rating')
|
|
const viewMode = ref('grid')
|
|
const showFilter = ref(false)
|
|
const selectedBrands = ref([])
|
|
const selectedPriceRange = ref('all')
|
|
|
|
const productsList = ref([])
|
|
const favoritesList = ref([])
|
|
|
|
// 筛选选项数据
|
|
const petTypes = ref([
|
|
{ value: 'cat', label: '猫咪', icon: '🐱' },
|
|
{ value: 'dog', label: '狗狗', icon: '🐶' }
|
|
])
|
|
|
|
const productCategories = ref([
|
|
{ value: 'food', label: '主粮' },
|
|
{ value: 'snack', label: '零食' },
|
|
{ value: 'litter', label: '猫砂' },
|
|
{ value: 'toy', label: '玩具' },
|
|
{ value: 'supplies', label: '用品' },
|
|
{ value: 'health', label: '保健品' }
|
|
])
|
|
|
|
const brands = ref([
|
|
'皇家', '希尔斯', '渴望', '蓝氏', '冠能', '爱肯拿', '纽翠斯', '百利', '素力高', '网易严选'
|
|
])
|
|
|
|
const priceRanges = ref([
|
|
{ value: 'all', label: '不限' },
|
|
{ value: '0-50', label: '50元以下' },
|
|
{ value: '50-100', label: '50-100元' },
|
|
{ value: '100-200', label: '100-200元' },
|
|
{ value: '200-500', label: '200-500元' },
|
|
{ value: '500+', label: '500元以上' }
|
|
])
|
|
|
|
// 计算属性
|
|
const filteredProducts = computed(() => {
|
|
let filtered = productsList.value
|
|
|
|
// 搜索筛选
|
|
if (searchKeyword.value) {
|
|
const keyword = searchKeyword.value.toLowerCase()
|
|
filtered = filtered.filter(product =>
|
|
product.name.toLowerCase().includes(keyword) ||
|
|
product.brand.toLowerCase().includes(keyword)
|
|
)
|
|
}
|
|
|
|
// 宠物类型筛选
|
|
if (currentPetType.value !== 'all') {
|
|
filtered = filtered.filter(product => product.petType === currentPetType.value)
|
|
}
|
|
|
|
// 产品类别筛选
|
|
if (currentCategory.value !== 'all') {
|
|
filtered = filtered.filter(product => product.category === currentCategory.value)
|
|
}
|
|
|
|
// 品牌筛选
|
|
if (selectedBrands.value.length > 0) {
|
|
filtered = filtered.filter(product => selectedBrands.value.includes(product.brand))
|
|
}
|
|
|
|
// 价格筛选
|
|
if (selectedPriceRange.value !== 'all') {
|
|
filtered = filtered.filter(product => {
|
|
const price = parseFloat(product.priceRange.split('-')[0])
|
|
switch (selectedPriceRange.value) {
|
|
case '0-50':
|
|
return price < 50
|
|
case '50-100':
|
|
return price >= 50 && price < 100
|
|
case '100-200':
|
|
return price >= 100 && price < 200
|
|
case '200-500':
|
|
return price >= 200 && price < 500
|
|
case '500+':
|
|
return price >= 500
|
|
default:
|
|
return true
|
|
}
|
|
})
|
|
}
|
|
|
|
// 排序
|
|
filtered.sort((a, b) => {
|
|
switch (currentSort.value) {
|
|
case 'rating':
|
|
return b.rating - a.rating
|
|
case 'price':
|
|
const priceA = parseFloat(a.priceRange.split('-')[0])
|
|
const priceB = parseFloat(b.priceRange.split('-')[0])
|
|
return priceA - priceB
|
|
case 'hot':
|
|
return b.reviewCount - a.reviewCount
|
|
default:
|
|
return 0
|
|
}
|
|
})
|
|
|
|
return filtered
|
|
})
|
|
|
|
// 生命周期
|
|
onMounted(() => {
|
|
loadProducts()
|
|
loadFavorites()
|
|
})
|
|
|
|
// 方法定义
|
|
const loadProducts = () => {
|
|
// 模拟产品数据
|
|
const mockProducts = [
|
|
{
|
|
id: 'p1',
|
|
name: '成猫粮 室内猫配方',
|
|
brand: '皇家',
|
|
category: 'food',
|
|
petType: 'cat',
|
|
rating: 4.8,
|
|
reviewCount: 1256,
|
|
priceRange: '89-156',
|
|
image: 'https://images.unsplash.com/photo-1589924691995-400dc9ecc119?w=300&h=300&fit=crop',
|
|
badge: '热销',
|
|
tags: ['室内猫', '营养均衡', '毛球护理'],
|
|
isFavorite: false
|
|
},
|
|
{
|
|
id: 'p2',
|
|
name: '幼犬粮 小型犬专用',
|
|
brand: '希尔斯',
|
|
category: 'food',
|
|
petType: 'dog',
|
|
rating: 4.7,
|
|
reviewCount: 892,
|
|
priceRange: '128-268',
|
|
image: 'https://images.unsplash.com/photo-1551717743-49959800b1f6?w=300&h=300&fit=crop',
|
|
badge: '新品',
|
|
tags: ['小型犬', '幼犬', '易消化'],
|
|
isFavorite: false
|
|
},
|
|
{
|
|
id: 'p3',
|
|
name: '无尘豆腐猫砂',
|
|
brand: '网易严选',
|
|
category: 'litter',
|
|
petType: 'cat',
|
|
rating: 4.6,
|
|
reviewCount: 2341,
|
|
priceRange: '45-78',
|
|
image: 'https://images.unsplash.com/photo-1601758228041-f3b2795255f1?w=300&h=300&fit=crop',
|
|
badge: '',
|
|
tags: ['无尘', '结团好', '除臭'],
|
|
isFavorite: false
|
|
},
|
|
{
|
|
id: 'p4',
|
|
name: '冻干鸡肉粒',
|
|
brand: '蓝氏',
|
|
category: 'snack',
|
|
petType: 'cat',
|
|
rating: 4.9,
|
|
reviewCount: 567,
|
|
priceRange: '35-68',
|
|
image: 'https://images.unsplash.com/photo-1583337130417-3346a1be7dee?w=300&h=300&fit=crop',
|
|
badge: '推荐',
|
|
tags: ['冻干', '高蛋白', '训练奖励'],
|
|
isFavorite: false
|
|
},
|
|
{
|
|
id: 'p5',
|
|
name: '智能逗猫棒',
|
|
brand: '小佩',
|
|
category: 'toy',
|
|
petType: 'cat',
|
|
rating: 4.5,
|
|
reviewCount: 423,
|
|
priceRange: '89-129',
|
|
image: 'https://images.unsplash.com/photo-1545249390-6bdfa286032f?w=300&h=300&fit=crop',
|
|
badge: '',
|
|
tags: ['智能', '自动', '互动'],
|
|
isFavorite: false
|
|
},
|
|
{
|
|
id: 'p6',
|
|
name: '关节保健软糖',
|
|
brand: '渴望',
|
|
category: 'health',
|
|
petType: 'dog',
|
|
rating: 4.4,
|
|
reviewCount: 234,
|
|
priceRange: '156-298',
|
|
image: 'https://images.unsplash.com/photo-1587300003388-59208cc962cb?w=300&h=300&fit=crop',
|
|
badge: '',
|
|
tags: ['关节保健', '老年犬', '软糖'],
|
|
isFavorite: false
|
|
},
|
|
{
|
|
id: 'p7',
|
|
name: '天然猫粮 无谷配方',
|
|
brand: '渴望',
|
|
category: 'food',
|
|
petType: 'cat',
|
|
rating: 4.9,
|
|
reviewCount: 1834,
|
|
priceRange: '200-350',
|
|
image: 'https://images.unsplash.com/photo-1548767797-d8c844163c4c?w=300&h=300&fit=crop',
|
|
badge: '高端',
|
|
tags: ['无谷物', '天然', '高蛋白'],
|
|
isFavorite: false
|
|
},
|
|
{
|
|
id: 'p8',
|
|
name: '宠物湿巾 杀菌除味',
|
|
brand: '怡亲',
|
|
category: 'health',
|
|
petType: 'all',
|
|
rating: 4.3,
|
|
reviewCount: 756,
|
|
priceRange: '25-45',
|
|
image: 'https://images.unsplash.com/photo-1560807707-8cc77767d783?w=300&h=300&fit=crop',
|
|
badge: '',
|
|
tags: ['杀菌', '除味', '便携'],
|
|
isFavorite: false
|
|
},
|
|
{
|
|
id: 'p9',
|
|
name: '狗狗咬胶 磨牙棒',
|
|
brand: '好主人',
|
|
category: 'toy',
|
|
petType: 'dog',
|
|
rating: 4.6,
|
|
reviewCount: 1123,
|
|
priceRange: '15-35',
|
|
image: 'https://images.unsplash.com/photo-1601758125946-6ec2ef64daf8?w=300&h=300&fit=crop',
|
|
badge: '',
|
|
tags: ['磨牙', '清洁牙齿', '天然'],
|
|
isFavorite: false
|
|
},
|
|
{
|
|
id: 'p10',
|
|
name: '猫咪营养膏 增肥补钙',
|
|
brand: '卫仕',
|
|
category: 'health',
|
|
petType: 'cat',
|
|
rating: 4.7,
|
|
reviewCount: 892,
|
|
priceRange: '45-89',
|
|
image: 'https://images.unsplash.com/photo-1574158622682-e40e69881006?w=300&h=300&fit=crop',
|
|
badge: '营养',
|
|
tags: ['营养膏', '增肥', '补钙'],
|
|
isFavorite: false
|
|
}
|
|
]
|
|
|
|
productsList.value = mockProducts
|
|
uni.setStorageSync('reviewProducts', mockProducts)
|
|
}
|
|
|
|
const loadFavorites = () => {
|
|
try {
|
|
const saved = uni.getStorageSync('reviewFavorites') || []
|
|
favoritesList.value = saved
|
|
|
|
// 更新产品收藏状态
|
|
productsList.value.forEach(product => {
|
|
product.isFavorite = favoritesList.value.includes(product.id)
|
|
})
|
|
} catch (error) {
|
|
console.error('加载收藏失败:', error)
|
|
}
|
|
}
|
|
|
|
const saveFavorites = () => {
|
|
uni.setStorageSync('reviewFavorites', favoritesList.value)
|
|
}
|
|
|
|
const onSearch = () => {
|
|
// 搜索逻辑已在计算属性中处理
|
|
}
|
|
|
|
const clearSearch = () => {
|
|
searchKeyword.value = ''
|
|
}
|
|
|
|
const setPetType = (type) => {
|
|
currentPetType.value = type
|
|
}
|
|
|
|
const setCategory = (category) => {
|
|
currentCategory.value = category
|
|
}
|
|
|
|
const setSort = (sort) => {
|
|
currentSort.value = sort
|
|
}
|
|
|
|
const setViewMode = (mode) => {
|
|
viewMode.value = mode
|
|
}
|
|
|
|
const showFilterModal = () => {
|
|
showFilter.value = true
|
|
}
|
|
|
|
const toggleBrand = (brand) => {
|
|
const index = selectedBrands.value.indexOf(brand)
|
|
if (index > -1) {
|
|
selectedBrands.value.splice(index, 1)
|
|
} else {
|
|
selectedBrands.value.push(brand)
|
|
}
|
|
}
|
|
|
|
const setPriceRange = (range) => {
|
|
selectedPriceRange.value = range
|
|
}
|
|
|
|
const applyFilter = () => {
|
|
showFilter.value = false
|
|
}
|
|
|
|
const resetFilters = () => {
|
|
currentPetType.value = 'all'
|
|
currentCategory.value = 'all'
|
|
currentSort.value = 'rating'
|
|
selectedBrands.value = []
|
|
selectedPriceRange.value = 'all'
|
|
viewMode.value = 'grid'
|
|
}
|
|
|
|
const toggleFavorite = (product) => {
|
|
const index = favoritesList.value.indexOf(product.id)
|
|
if (index > -1) {
|
|
favoritesList.value.splice(index, 1)
|
|
product.isFavorite = false
|
|
} else {
|
|
favoritesList.value.push(product.id)
|
|
product.isFavorite = true
|
|
}
|
|
saveFavorites()
|
|
|
|
uni.showToast({
|
|
title: product.isFavorite ? '已收藏' : '已取消收藏',
|
|
icon: 'success'
|
|
})
|
|
}
|
|
|
|
const viewProductDetail = (product) => {
|
|
// 保存浏览历史
|
|
let history = uni.getStorageSync('reviewHistory') || []
|
|
const existIndex = history.findIndex(item => item.id === product.id)
|
|
if (existIndex > -1) {
|
|
history.splice(existIndex, 1)
|
|
}
|
|
history.unshift({
|
|
...product,
|
|
viewTime: new Date().toISOString()
|
|
})
|
|
if (history.length > 50) {
|
|
history = history.slice(0, 50)
|
|
}
|
|
uni.setStorageSync('reviewHistory', history)
|
|
|
|
// 跳转到详情页
|
|
uni.navigateTo({
|
|
url: `/pages/review/detail?id=${product.id}`,
|
|
fail: () => {
|
|
uni.showToast({
|
|
title: '详情页开发中',
|
|
icon: 'none'
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
return {
|
|
searchKeyword,
|
|
currentPetType,
|
|
currentCategory,
|
|
currentSort,
|
|
viewMode,
|
|
showFilter,
|
|
selectedBrands,
|
|
selectedPriceRange,
|
|
petTypes,
|
|
productCategories,
|
|
brands,
|
|
priceRanges,
|
|
filteredProducts,
|
|
onSearch,
|
|
clearSearch,
|
|
setPetType,
|
|
setCategory,
|
|
setSort,
|
|
setViewMode,
|
|
showFilterModal,
|
|
toggleBrand,
|
|
setPriceRange,
|
|
applyFilter,
|
|
resetFilters,
|
|
toggleFavorite,
|
|
viewProductDetail
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.review-container {
|
|
padding-bottom: 60rpx;
|
|
}
|
|
|
|
/* 搜索栏 */
|
|
.search-section {
|
|
padding: 24rpx 20rpx;
|
|
|
|
.search-wrapper {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 16rpx;
|
|
|
|
.search-input-container {
|
|
flex: 1;
|
|
position: relative;
|
|
background: rgba(255, 255, 255, 0.95);
|
|
border: 2rpx solid rgba(255, 255, 255, 0.8);
|
|
border-radius: 24rpx;
|
|
padding: 0 20rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
|
|
|
|
.search-icon {
|
|
font-size: 24rpx;
|
|
margin-right: 12rpx;
|
|
color: #666;
|
|
}
|
|
|
|
.search-input {
|
|
flex: 1;
|
|
height: 72rpx;
|
|
font-size: 24rpx;
|
|
color: #333;
|
|
background: transparent;
|
|
border: none;
|
|
outline: none;
|
|
|
|
&::placeholder {
|
|
color: #999;
|
|
}
|
|
}
|
|
|
|
.search-clear {
|
|
width: 32rpx;
|
|
height: 32rpx;
|
|
background: rgba(0, 0, 0, 0.1);
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin-left: 12rpx;
|
|
transition: all 0.3s ease;
|
|
|
|
&:active {
|
|
background: rgba(0, 0, 0, 0.2);
|
|
}
|
|
|
|
.clear-icon {
|
|
font-size: 16rpx;
|
|
color: #666;
|
|
}
|
|
}
|
|
}
|
|
|
|
.filter-btn {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8rpx;
|
|
background: rgba(255, 255, 255, 0.95);
|
|
border: 2rpx solid rgba(255, 255, 255, 0.8);
|
|
border-radius: 16rpx;
|
|
padding: 16rpx 20rpx;
|
|
transition: all 0.3s ease;
|
|
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.1);
|
|
|
|
&:active {
|
|
transform: scale(0.95);
|
|
background: rgba(255, 255, 255, 0.8);
|
|
border-color: rgba(255, 255, 255, 0.9);
|
|
}
|
|
|
|
.filter-icon {
|
|
font-size: 20rpx;
|
|
color: #666;
|
|
}
|
|
|
|
.filter-text {
|
|
font-size: 22rpx;
|
|
color: #666;
|
|
font-weight: 500;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* 产品列表 */
|
|
.products-section {
|
|
margin: 0 20rpx;
|
|
|
|
.products-grid {
|
|
&.grid {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr;
|
|
gap: 16rpx;
|
|
}
|
|
|
|
&.list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 16rpx;
|
|
}
|
|
|
|
.product-card {
|
|
background: rgba(255, 255, 255, 0.95);
|
|
backdrop-filter: blur(20rpx);
|
|
border-radius: 12rpx;
|
|
padding: 12rpx;
|
|
box-shadow: 0 4rpx 16rpx rgba(255, 138, 128, 0.1);
|
|
transition: all 0.3s ease;
|
|
|
|
&:active {
|
|
transform: scale(0.98);
|
|
}
|
|
|
|
.product-image {
|
|
position: relative;
|
|
width: 100%;
|
|
height: 280rpx;
|
|
border-radius: 12rpx;
|
|
overflow: hidden;
|
|
margin-bottom: 12rpx;
|
|
|
|
.product-img {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
}
|
|
|
|
.product-badge {
|
|
position: absolute;
|
|
top: 12rpx;
|
|
left: 12rpx;
|
|
padding: 4rpx 12rpx;
|
|
background: linear-gradient(135deg, #FF8A80, #FFB6C1);
|
|
border-radius: 12rpx;
|
|
|
|
.badge-text {
|
|
font-size: 20rpx;
|
|
color: white;
|
|
font-weight: 500;
|
|
}
|
|
}
|
|
|
|
.product-favorite {
|
|
position: absolute;
|
|
top: 12rpx;
|
|
right: 12rpx;
|
|
width: 40rpx;
|
|
height: 40rpx;
|
|
background: rgba(255, 255, 255, 0.9);
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
|
|
.favorite-icon {
|
|
font-size: 24rpx;
|
|
color: #CCCCCC;
|
|
transition: all 0.3s ease;
|
|
|
|
&.active {
|
|
color: #FF8A80;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.product-info {
|
|
|
|
.product-basic {
|
|
margin-bottom: 8rpx;
|
|
|
|
.product-name {
|
|
font-size: 30rpx;
|
|
font-weight: bold;
|
|
color: #333333;
|
|
margin-bottom: 6rpx;
|
|
display: block;
|
|
line-height: 1.2;
|
|
padding: 6rpx 0;
|
|
}
|
|
|
|
.product-brand-tags {
|
|
display: flex;
|
|
gap: 6rpx;
|
|
flex-wrap: wrap;
|
|
|
|
.product-brand {
|
|
background: rgba(100, 181, 246, 0.15);
|
|
border-radius: 8rpx;
|
|
padding: 2rpx 8rpx;
|
|
font-size: 18rpx;
|
|
color: #42A5F5;
|
|
font-weight: 500;
|
|
}
|
|
}
|
|
}
|
|
|
|
.product-rating {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 4rpx;
|
|
margin-bottom: 8rpx;
|
|
|
|
.rating-stars {
|
|
display: flex;
|
|
gap: 2rpx;
|
|
align-items: center;
|
|
}
|
|
|
|
.rating-score {
|
|
font-size: 18rpx;
|
|
color: #666666;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.rating-count {
|
|
font-size: 18rpx;
|
|
color: #666666;
|
|
}
|
|
}
|
|
|
|
.product-price {
|
|
margin-bottom: 8rpx;
|
|
|
|
.price-range {
|
|
font-size: 24rpx;
|
|
color: #FF8A80;
|
|
font-weight: 600;
|
|
}
|
|
}
|
|
|
|
.product-tags {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 6rpx;
|
|
|
|
.product-tag {
|
|
padding: 2rpx 8rpx;
|
|
background: rgba(255, 138, 128, 0.1);
|
|
border-radius: 8rpx;
|
|
font-size: 18rpx;
|
|
color: #FF8A80;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
&.list .product-card {
|
|
.product-image {
|
|
height: 240rpx;
|
|
|
|
.product-img {
|
|
height: 100%;
|
|
}
|
|
}
|
|
|
|
.product-info {
|
|
.product-name {
|
|
-webkit-line-clamp: 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 空状态 */
|
|
.empty-products {
|
|
text-align: center;
|
|
padding: 80rpx 40rpx;
|
|
background: rgba(255, 255, 255, 0.95);
|
|
backdrop-filter: blur(20rpx);
|
|
border-radius: 24rpx;
|
|
box-shadow: 0 8rpx 32rpx rgba(255, 138, 128, 0.2);
|
|
|
|
.empty-icon {
|
|
font-size: 80rpx;
|
|
margin-bottom: 24rpx;
|
|
opacity: 0.5;
|
|
}
|
|
|
|
.empty-text {
|
|
font-size: 28rpx;
|
|
color: #666666;
|
|
margin-bottom: 12rpx;
|
|
font-weight: 500;
|
|
}
|
|
|
|
.empty-desc {
|
|
font-size: 24rpx;
|
|
color: #999999;
|
|
line-height: 1.5;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* 筛选面板 */
|
|
.filter-panel {
|
|
background: rgba(255, 255, 255, 0.95);
|
|
backdrop-filter: blur(20rpx);
|
|
padding: 24rpx 32rpx;
|
|
border-bottom: 1rpx solid rgba(255, 255, 255, 0.3);
|
|
position: relative;
|
|
z-index: 10;
|
|
width: 100%;
|
|
box-sizing: border-box;
|
|
|
|
.filter-section {
|
|
margin-bottom: 32rpx;
|
|
|
|
&:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.filter-title {
|
|
margin-bottom: 16rpx;
|
|
|
|
.title-text {
|
|
font-size: 28rpx;
|
|
font-weight: 600;
|
|
color: #333333;
|
|
}
|
|
}
|
|
}
|
|
|
|
.type-filters {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 12rpx;
|
|
|
|
.type-filter {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 12rpx 20rpx;
|
|
border: 2rpx solid rgba(255, 138, 128, 0.2);
|
|
border-radius: 20rpx;
|
|
background: rgba(255, 255, 255, 0.8);
|
|
|
|
&.active {
|
|
border-color: #FF8A80;
|
|
background: linear-gradient(135deg, #FF8A80, #FFB6C1);
|
|
|
|
.type-text,
|
|
.type-icon {
|
|
color: white;
|
|
font-weight: 600;
|
|
}
|
|
}
|
|
|
|
.type-icon {
|
|
font-size: 24rpx;
|
|
margin-right: 6rpx;
|
|
color: #666666;
|
|
}
|
|
|
|
.type-text {
|
|
font-size: 24rpx;
|
|
color: #666666;
|
|
}
|
|
}
|
|
}
|
|
|
|
.category-filters {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 12rpx;
|
|
|
|
.category-filter {
|
|
padding: 12rpx 20rpx;
|
|
border: 2rpx solid rgba(255, 138, 128, 0.2);
|
|
border-radius: 20rpx;
|
|
background: rgba(255, 255, 255, 0.8);
|
|
|
|
&.active {
|
|
border-color: #FF8A80;
|
|
background: linear-gradient(135deg, #FF8A80, #FFB6C1);
|
|
|
|
.category-text {
|
|
color: white;
|
|
font-weight: 600;
|
|
}
|
|
}
|
|
|
|
.category-text {
|
|
font-size: 24rpx;
|
|
color: #666666;
|
|
}
|
|
}
|
|
}
|
|
|
|
.brand-filters {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 12rpx;
|
|
|
|
.brand-filter {
|
|
padding: 12rpx 20rpx;
|
|
border: 2rpx solid rgba(255, 138, 128, 0.2);
|
|
border-radius: 20rpx;
|
|
background: rgba(255, 255, 255, 0.8);
|
|
|
|
&.active {
|
|
border-color: #FF8A80;
|
|
background: linear-gradient(135deg, #FF8A80, #FFB6C1);
|
|
|
|
.brand-text {
|
|
color: white;
|
|
font-weight: 600;
|
|
}
|
|
}
|
|
|
|
.brand-text {
|
|
font-size: 24rpx;
|
|
color: #666666;
|
|
}
|
|
}
|
|
}
|
|
|
|
.price-filters {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 12rpx;
|
|
|
|
.price-filter {
|
|
padding: 12rpx 20rpx;
|
|
border: 2rpx solid rgba(255, 138, 128, 0.2);
|
|
border-radius: 20rpx;
|
|
background: rgba(255, 255, 255, 0.8);
|
|
|
|
&.active {
|
|
border-color: #FF8A80;
|
|
background: linear-gradient(135deg, #FF8A80, #FFB6C1);
|
|
|
|
.price-text {
|
|
color: white;
|
|
font-weight: 600;
|
|
}
|
|
}
|
|
|
|
.price-text {
|
|
font-size: 24rpx;
|
|
color: #666666;
|
|
}
|
|
}
|
|
}
|
|
|
|
.other-filters {
|
|
.filter-row {
|
|
display: flex;
|
|
align-items: center;
|
|
margin-bottom: 16rpx;
|
|
|
|
&:last-child {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.filter-label {
|
|
font-size: 24rpx;
|
|
color: #666666;
|
|
margin-right: 16rpx;
|
|
min-width: 80rpx;
|
|
}
|
|
}
|
|
|
|
.sort-filters,
|
|
.view-filters {
|
|
display: flex;
|
|
gap: 12rpx;
|
|
|
|
.sort-filter,
|
|
.view-filter {
|
|
padding: 8rpx 16rpx;
|
|
border: 2rpx solid rgba(255, 138, 128, 0.2);
|
|
border-radius: 16rpx;
|
|
background: rgba(255, 255, 255, 0.8);
|
|
|
|
&.active {
|
|
border-color: #FF8A80;
|
|
background: linear-gradient(135deg, #FF8A80, #FFB6C1);
|
|
|
|
.sort-text,
|
|
.view-text {
|
|
color: white;
|
|
font-weight: 600;
|
|
}
|
|
}
|
|
|
|
.sort-text,
|
|
.view-text {
|
|
font-size: 22rpx;
|
|
color: #666666;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.filter-actions {
|
|
display: flex;
|
|
gap: 16rpx;
|
|
margin-top: 32rpx;
|
|
padding-top: 24rpx;
|
|
border-top: 1rpx solid rgba(255, 138, 128, 0.1);
|
|
|
|
.filter-reset,
|
|
.filter-apply {
|
|
flex: 1;
|
|
padding: 16rpx 24rpx;
|
|
border-radius: 24rpx;
|
|
text-align: center;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.filter-reset {
|
|
background: rgba(255, 255, 255, 0.8);
|
|
border: 2rpx solid rgba(255, 138, 128, 0.3);
|
|
|
|
&:active {
|
|
background: rgba(255, 255, 255, 0.6);
|
|
}
|
|
|
|
.reset-text {
|
|
font-size: 28rpx;
|
|
color: #666666;
|
|
}
|
|
}
|
|
|
|
.filter-apply {
|
|
background: linear-gradient(135deg, #FF8A80, #FFB6C1);
|
|
border: 2rpx solid #FF8A80;
|
|
|
|
&:active {
|
|
transform: scale(0.98);
|
|
}
|
|
|
|
.apply-text {
|
|
font-size: 28rpx;
|
|
color: white;
|
|
font-weight: 600;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 响应式设计 */
|
|
@media (max-width: 375px) {
|
|
.review-container {
|
|
.search-section,
|
|
.filter-section,
|
|
.toolbar-section,
|
|
.products-section {
|
|
margin-left: 20rpx;
|
|
margin-right: 20rpx;
|
|
}
|
|
|
|
.products-grid.grid {
|
|
gap: 12rpx;
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
|
|
/* 动画效果 */
|
|
@keyframes fadeIn {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateY(20rpx);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateY(0);
|
|
}
|
|
}
|
|
|
|
/* 移除全局动画效果以避免筛选面板闪烁 */
|
|
|
|
/* 交互反馈 */
|
|
.filter-item:active,
|
|
.category-item:active,
|
|
.sort-item:active,
|
|
.toggle-item:active,
|
|
.brand-item:active,
|
|
.price-item:active {
|
|
transform: scale(0.95);
|
|
}
|
|
|
|
.product-favorite:active {
|
|
transform: scale(0.8);
|
|
}
|
|
</style>
|