|
|
|
<template>
|
|
|
|
<div class="product-grid-page">
|
|
|
|
<!-- 筛选排序区 -->
|
|
|
|
<div class="filter-bar" v-if="showFilter">
|
|
|
|
<!-- 分类弹框 -->
|
|
|
|
<el-select
|
|
|
|
v-model="selectedCategory"
|
|
|
|
placeholder="全部分类"
|
|
|
|
class="filter-select"
|
|
|
|
@change="handleCategoryChange"
|
|
|
|
>
|
|
|
|
<el-option
|
|
|
|
v-for="cat in categories"
|
|
|
|
:key="cat.value"
|
|
|
|
:label="cat.label"
|
|
|
|
:value="cat.value"
|
|
|
|
></el-option>
|
|
|
|
</el-select>
|
|
|
|
|
|
|
|
<!-- 排序选项 -->
|
|
|
|
<el-select
|
|
|
|
v-model="selectedSort"
|
|
|
|
placeholder="综合排序"
|
|
|
|
class="filter-select"
|
|
|
|
@change="handleSortChange"
|
|
|
|
>
|
|
|
|
<el-option label="综合排序" value="default"></el-option>
|
|
|
|
<el-option label="销量↑" value="sales_asc"></el-option>
|
|
|
|
<el-option label="销量↓" value="sales_desc"></el-option>
|
|
|
|
<el-option label="价格↑" value="price_asc"></el-option>
|
|
|
|
<el-option label="价格↓" value="price_desc"></el-option>
|
|
|
|
</el-select>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<!-- 搜索结果统计 -->
|
|
|
|
<div class="result-stats" v-if="type == 'search'">
|
|
|
|
<span>全部结果 ></span>
|
|
|
|
<span class="keyword" v-if="searchKeyword">"{{ searchKeyword }}"</span>
|
|
|
|
<span
|
|
|
|
v-if="
|
|
|
|
selectedCategory &&
|
|
|
|
categories.find((cat) => cat.value === selectedCategory)
|
|
|
|
"
|
|
|
|
class="category"
|
|
|
|
>
|
|
|
|
"{{ categories.find((cat) => cat.value === selectedCategory).label }}"
|
|
|
|
</span>
|
|
|
|
<span>共{{ totalProducts }}个结果</span>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<!-- 商品网格 -->
|
|
|
|
<div class="product-grid">
|
|
|
|
<div
|
|
|
|
class="product-card"
|
|
|
|
v-for="(product, index) in visibleProducts"
|
|
|
|
:key="product.id"
|
|
|
|
@click="goToDetail(product.id)"
|
|
|
|
>
|
|
|
|
<el-image
|
|
|
|
v-lazy="product.image"
|
|
|
|
:alt="product.name"
|
|
|
|
class="product-img"
|
|
|
|
lazy
|
|
|
|
fit="cover"
|
|
|
|
>
|
|
|
|
<div slot="placeholder" class="image-placeholder">
|
|
|
|
<i class="el-icon-loading"></i>
|
|
|
|
</div>
|
|
|
|
</el-image>
|
|
|
|
<div
|
|
|
|
class="tag"
|
|
|
|
v-if="product.tag"
|
|
|
|
:style="{ backgroundColor: product.tagColor }"
|
|
|
|
>
|
|
|
|
{{ product.tag }}
|
|
|
|
</div>
|
|
|
|
<div class="product-info">
|
|
|
|
<div class="product-name">{{ product.name }}</div>
|
|
|
|
<div class="price-row">
|
|
|
|
<span class="current-price">¥{{ product.price.toFixed(2) }}</span>
|
|
|
|
<span class="original-price" v-if="product.originalPrice"
|
|
|
|
>¥{{ product.originalPrice.toFixed(2) }}</span
|
|
|
|
>
|
|
|
|
</div>
|
|
|
|
<div class="sales-volume" v-if="product.sales > 0">
|
|
|
|
<i class="el-icon-shopping-cart"></i> 已售 {{ product.sales }}
|
|
|
|
</div>
|
|
|
|
<el-button
|
|
|
|
type="primary"
|
|
|
|
size="mini"
|
|
|
|
class="cart-btn"
|
|
|
|
@click.prevent="addToCart(product)"
|
|
|
|
>
|
|
|
|
加入购物车
|
|
|
|
</el-button>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div class="empty-state" v-if="visibleProducts.length === 0">
|
|
|
|
<el-empty description="暂无符合条件的商品"></el-empty>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<!-- 分页组件 -->
|
|
|
|
<div class="pagination-container" v-if="totalProducts > 0">
|
|
|
|
<el-pagination
|
|
|
|
@size-change="handleSizeChange"
|
|
|
|
@current-change="handleCurrentChange"
|
|
|
|
:current-page="currentPage"
|
|
|
|
:page-sizes="[12, 24, 36]"
|
|
|
|
:page-size="pageSize"
|
|
|
|
layout="total, sizes, prev, pager, next, jumper"
|
|
|
|
:total="totalProducts"
|
|
|
|
></el-pagination>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<script>
|
|
|
|
import { mapGetters } from "vuex";
|
|
|
|
export default {
|
|
|
|
name: "ProductGridPage",
|
|
|
|
data() {
|
|
|
|
return {
|
|
|
|
type: "",
|
|
|
|
// 搜索关键词
|
|
|
|
searchKeyword: "",
|
|
|
|
// 筛选/排序条件
|
|
|
|
selectedCategory: "",
|
|
|
|
selectedSort: "default", // 默认综合排序
|
|
|
|
showFilter: true,
|
|
|
|
// 分页控制
|
|
|
|
currentPage: 1,
|
|
|
|
pageSize: 12,
|
|
|
|
// 商品数据
|
|
|
|
products: [
|
|
|
|
{
|
|
|
|
id: 1,
|
|
|
|
name: "无线蓝牙耳机 主动降噪长续航",
|
|
|
|
image: "https://picsum.photos/id/101/300/300",
|
|
|
|
price: 359.0,
|
|
|
|
originalPrice: 499.0,
|
|
|
|
category: "digital",
|
|
|
|
tag: "限时折扣",
|
|
|
|
tagColor: "#FF6B6B",
|
|
|
|
sales: 120,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: 2,
|
|
|
|
name: "智能手表 心率监测运动计步",
|
|
|
|
image: "https://picsum.photos/id/102/300/300",
|
|
|
|
price: 259.0,
|
|
|
|
originalPrice: 329.0,
|
|
|
|
category: "digital",
|
|
|
|
tag: "新品",
|
|
|
|
tagColor: "#4ECDC4",
|
|
|
|
sales: 86,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: 3,
|
|
|
|
name: "纯棉短袖T恤 宽松休闲",
|
|
|
|
image: "https://picsum.photos/id/103/300/300",
|
|
|
|
price: 89.0,
|
|
|
|
originalPrice: 129.0,
|
|
|
|
category: "life",
|
|
|
|
sales: 320,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: 4,
|
|
|
|
name: "新鲜水果礼盒 当季混合装",
|
|
|
|
image: "https://picsum.photos/id/104/300/300",
|
|
|
|
price: 159.0,
|
|
|
|
category: "food",
|
|
|
|
tag: "热销",
|
|
|
|
tagColor: "#FF9F1C",
|
|
|
|
sales: 215,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: 5,
|
|
|
|
name: "全自动咖啡机 家用小型",
|
|
|
|
image: "https://picsum.photos/id/105/300/300",
|
|
|
|
price: 1299.0,
|
|
|
|
originalPrice: 1599.0,
|
|
|
|
category: "life",
|
|
|
|
sales: 45,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: 6,
|
|
|
|
name: "高清投影仪 家用办公两用",
|
|
|
|
image: "https://picsum.photos/id/106/300/300",
|
|
|
|
price: 2499.0,
|
|
|
|
category: "digital",
|
|
|
|
tag: "推荐",
|
|
|
|
tagColor: "#2EC4B6",
|
|
|
|
sales: 78,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: 7,
|
|
|
|
name: "进口红酒 赤霞珠干红",
|
|
|
|
image: "https://picsum.photos/id/107/300/300",
|
|
|
|
price: 199.0,
|
|
|
|
originalPrice: 258.0,
|
|
|
|
category: "food",
|
|
|
|
sales: 63,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: 8,
|
|
|
|
name: "瑜伽垫 防滑专业健身垫",
|
|
|
|
image: "https://picsum.photos/id/108/300/300",
|
|
|
|
price: 129.0,
|
|
|
|
category: "life",
|
|
|
|
sales: 156,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: 9,
|
|
|
|
name: "机械键盘 青轴游戏专用",
|
|
|
|
image: "https://picsum.photos/id/109/300/300",
|
|
|
|
price: 299.0,
|
|
|
|
originalPrice: 399.0,
|
|
|
|
category: "digital",
|
|
|
|
tag: "限时折扣",
|
|
|
|
tagColor: "#FF6B6B",
|
|
|
|
sales: 92,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: 10,
|
|
|
|
name: "有机蔬菜礼盒 新鲜配送",
|
|
|
|
image: "https://picsum.photos/id/110/300/300",
|
|
|
|
price: 89.0,
|
|
|
|
category: "food",
|
|
|
|
sales: 205,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: 11,
|
|
|
|
name: "北欧风落地灯 客厅卧室",
|
|
|
|
image: "https://picsum.photos/id/111/300/300",
|
|
|
|
price: 199.0,
|
|
|
|
category: "life",
|
|
|
|
sales: 57,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: 12,
|
|
|
|
name: "便携式充电宝 20000mAh",
|
|
|
|
image: "https://picsum.photos/id/112/300/300",
|
|
|
|
price: 129.0,
|
|
|
|
originalPrice: 169.0,
|
|
|
|
category: "digital",
|
|
|
|
sales: 310,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
// 分类选项
|
|
|
|
categories: [
|
|
|
|
{ label: "全部", value: "" },
|
|
|
|
{ label: "美食", value: "food" },
|
|
|
|
{ label: "生活", value: "life" },
|
|
|
|
{ label: "数码", value: "digital" },
|
|
|
|
],
|
|
|
|
};
|
|
|
|
},
|
|
|
|
computed: {
|
|
|
|
...mapGetters(["getSearchText"]),
|
|
|
|
// 筛选后商品
|
|
|
|
filteredProducts() {
|
|
|
|
return this.products.filter((product) => {
|
|
|
|
// 分类筛选
|
|
|
|
const categoryMatch = this.selectedCategory
|
|
|
|
? product.category === this.selectedCategory
|
|
|
|
: true;
|
|
|
|
|
|
|
|
// 关键词搜索筛选
|
|
|
|
const keywordMatch = this.searchKeyword
|
|
|
|
? product.name
|
|
|
|
.toLowerCase()
|
|
|
|
.includes(this.searchKeyword.toLowerCase())
|
|
|
|
: true;
|
|
|
|
|
|
|
|
return categoryMatch && keywordMatch;
|
|
|
|
});
|
|
|
|
},
|
|
|
|
// 排序处理
|
|
|
|
sortedProducts() {
|
|
|
|
const sorted = [...this.filteredProducts];
|
|
|
|
console.log(...this.filteredProducts, sorted);
|
|
|
|
|
|
|
|
switch (this.selectedSort) {
|
|
|
|
case "price_asc":
|
|
|
|
return sorted.sort((a, b) => a.price - b.price);
|
|
|
|
case "price_desc":
|
|
|
|
return sorted.sort((a, b) => b.price - a.price);
|
|
|
|
case "sales_asc":
|
|
|
|
return sorted.sort((a, b) => a.sales - b.sales);
|
|
|
|
case "sales_desc":
|
|
|
|
return sorted.sort((a, b) => b.sales - a.sales);
|
|
|
|
default: // 综合排序(默认按ID)
|
|
|
|
return sorted.sort((a, b) => a.id - b.id);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
// 分页后商品
|
|
|
|
visibleProducts() {
|
|
|
|
const start = (this.currentPage - 1) * this.pageSize;
|
|
|
|
const end = start + this.pageSize;
|
|
|
|
return this.sortedProducts.slice(start, end);
|
|
|
|
},
|
|
|
|
// 商品总数
|
|
|
|
totalProducts() {
|
|
|
|
return this.sortedProducts.length;
|
|
|
|
},
|
|
|
|
},
|
|
|
|
watch: {
|
|
|
|
// 监听搜索词变化
|
|
|
|
getSearchText(newVal, oldVal) {
|
|
|
|
if (newVal !== oldVal) {
|
|
|
|
this.searchKeyword = newVal;
|
|
|
|
this.fetchProducts(); // 调用接口获取数据
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
created() {
|
|
|
|
this.type = this.$route.query?.type;
|
|
|
|
this.searchKeyword = this.getSearchText;
|
|
|
|
this.fetchProducts();
|
|
|
|
},
|
|
|
|
methods: {
|
|
|
|
// 获取列表
|
|
|
|
async fetchProducts() {
|
|
|
|
console.log(this.getSearchText);
|
|
|
|
// const response = await this.$axios.get('/api/products', { params });
|
|
|
|
},
|
|
|
|
|
|
|
|
// 搜索处理
|
|
|
|
handleSearch() {
|
|
|
|
this.currentPage = 1; // 搜索后重置到第一页
|
|
|
|
},
|
|
|
|
|
|
|
|
// 分类变更处理
|
|
|
|
handleCategoryChange() {
|
|
|
|
this.currentPage = 1; // 切换分类后重置到第一页
|
|
|
|
},
|
|
|
|
|
|
|
|
// 排序变更处理
|
|
|
|
handleSortChange() {
|
|
|
|
this.currentPage = 1; // 切换排序后重置到第一页
|
|
|
|
},
|
|
|
|
|
|
|
|
// 分页大小改变
|
|
|
|
handleSizeChange(val) {
|
|
|
|
this.pageSize = val;
|
|
|
|
this.currentPage = 1;
|
|
|
|
},
|
|
|
|
|
|
|
|
// 当前页改变
|
|
|
|
handleCurrentChange(val) {
|
|
|
|
this.currentPage = val;
|
|
|
|
// 滚动到页面顶部
|
|
|
|
window.scrollTo(0, 0);
|
|
|
|
},
|
|
|
|
|
|
|
|
// 加入购物车
|
|
|
|
addToCart(product) {
|
|
|
|
this.$message.success(`${product.name} 已加入购物车`);
|
|
|
|
// 实际项目中这里会调用购物车API
|
|
|
|
},
|
|
|
|
|
|
|
|
// 跳转详情页
|
|
|
|
goToDetail(id) {
|
|
|
|
this.$router.push(`/product/${id}`);
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
</script>
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
.product-grid-page {
|
|
|
|
padding: 20px;
|
|
|
|
background-color: #f5f7fa;
|
|
|
|
max-width: 1400px;
|
|
|
|
margin: 0 auto;
|
|
|
|
|
|
|
|
// 筛选栏样式
|
|
|
|
.filter-bar {
|
|
|
|
display: flex;
|
|
|
|
gap: 15px;
|
|
|
|
margin-bottom: 20px;
|
|
|
|
flex-wrap: wrap;
|
|
|
|
align-items: center;
|
|
|
|
padding: 15px;
|
|
|
|
background-color: #fff;
|
|
|
|
border-radius: 8px;
|
|
|
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
|
|
|
|
|
|
|
.search-input {
|
|
|
|
flex: 1;
|
|
|
|
min-width: 200px;
|
|
|
|
}
|
|
|
|
|
|
|
|
.filter-select {
|
|
|
|
min-width: 140px;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 搜索结果统计样式
|
|
|
|
.result-stats {
|
|
|
|
margin: 0 0 15px 5px;
|
|
|
|
color: #666;
|
|
|
|
font-size: 14px;
|
|
|
|
padding: 5px 0;
|
|
|
|
|
|
|
|
.keyword,
|
|
|
|
.category {
|
|
|
|
color: #ff4d4f;
|
|
|
|
font-weight: 500;
|
|
|
|
margin: 0 5px;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 商品网格布局
|
|
|
|
.product-grid {
|
|
|
|
display: grid;
|
|
|
|
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
|
|
|
gap: 20px;
|
|
|
|
margin-bottom: 30px;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 商品卡片
|
|
|
|
.product-card {
|
|
|
|
background: #fff;
|
|
|
|
border-radius: 8px;
|
|
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
|
|
|
overflow: hidden;
|
|
|
|
transition: transform 0.3s, box-shadow 0.3s;
|
|
|
|
cursor: pointer;
|
|
|
|
position: relative;
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
transform: translateY(-5px);
|
|
|
|
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
|
|
|
|
}
|
|
|
|
|
|
|
|
.product-img {
|
|
|
|
width: 100%;
|
|
|
|
height: 200px;
|
|
|
|
background-color: #f5f5f5;
|
|
|
|
}
|
|
|
|
|
|
|
|
.image-placeholder {
|
|
|
|
width: 100%;
|
|
|
|
height: 200px;
|
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
|
|
|
justify-content: center;
|
|
|
|
background-color: #f5f5f5;
|
|
|
|
}
|
|
|
|
|
|
|
|
.tag {
|
|
|
|
position: absolute;
|
|
|
|
top: 10px;
|
|
|
|
left: 10px;
|
|
|
|
padding: 3px 8px;
|
|
|
|
font-size: 12px;
|
|
|
|
color: #fff;
|
|
|
|
border-radius: 4px;
|
|
|
|
z-index: 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
.product-info {
|
|
|
|
padding: 15px;
|
|
|
|
|
|
|
|
.product-name {
|
|
|
|
font-size: 14px;
|
|
|
|
color: #333;
|
|
|
|
margin-bottom: 10px;
|
|
|
|
height: 40px;
|
|
|
|
display: -webkit-box;
|
|
|
|
-webkit-line-clamp: 2;
|
|
|
|
-webkit-box-orient: vertical;
|
|
|
|
overflow: hidden;
|
|
|
|
}
|
|
|
|
|
|
|
|
.price-row {
|
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
|
|
|
.current-price {
|
|
|
|
color: #ff4d4f;
|
|
|
|
font-weight: bold;
|
|
|
|
font-size: 16px;
|
|
|
|
}
|
|
|
|
|
|
|
|
.original-price {
|
|
|
|
color: #999;
|
|
|
|
font-size: 12px;
|
|
|
|
text-decoration: line-through;
|
|
|
|
margin-left: 8px;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
.sales-volume {
|
|
|
|
font-size: 12px;
|
|
|
|
color: #666;
|
|
|
|
margin-bottom: 10px;
|
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
|
|
|
|
|
|
|
i {
|
|
|
|
font-size: 12px;
|
|
|
|
margin-right: 4px;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
.cart-btn {
|
|
|
|
width: 100%;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 空状态样式
|
|
|
|
.empty-state {
|
|
|
|
grid-column: 1 / -1;
|
|
|
|
padding: 60px 0;
|
|
|
|
text-align: center;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 分页容器
|
|
|
|
.pagination-container {
|
|
|
|
display: flex;
|
|
|
|
justify-content: center;
|
|
|
|
margin-top: 20px;
|
|
|
|
padding: 10px;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 响应式调整
|
|
|
|
@media (max-width: 768px) {
|
|
|
|
.product-grid-page {
|
|
|
|
padding: 10px;
|
|
|
|
|
|
|
|
.filter-bar {
|
|
|
|
padding: 10px;
|
|
|
|
gap: 10px;
|
|
|
|
}
|
|
|
|
|
|
|
|
.product-grid {
|
|
|
|
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
|
|
|
|
gap: 10px;
|
|
|
|
}
|
|
|
|
|
|
|
|
.product-card {
|
|
|
|
.product-img {
|
|
|
|
height: 140px;
|
|
|
|
}
|
|
|
|
|
|
|
|
.image-placeholder {
|
|
|
|
height: 140px;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
</style>
|