Files
inkreach-uni/pages/products/products.vue
T
2026-08-20 18:29:49 +08:00

761 lines
25 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<!-- 产品列表页 - 印美达 Inkreach -->
<view class="products-page">
<!-- 搜索栏 -->
<view class="search-bar">
<view class="search-input-wrap">
<text class="search-icon">🔍</text>
<input class="search-input" placeholder="搜索商品" placeholder-class="search-placeholder" @input="onSearchInput" :value="searchKeyword" />
</view>
<view class="filter-btn" @tap="toggleFilter">
<text class="filter-icon"></text>
<text class="filter-text">筛选</text>
</view>
</view>
<!-- 国家选择条 -->
<scroll-view class="country-bar" :scroll-x="true" :enhanced="true" :show-scrollbar="false">
<view :class="'country-item ' + (selectedCountry === item.id ? 'active' : '')" @tap="selectCountry" :data-id="item.id" v-for="(item, index) in countries" :key="index">
<text class="country-flag">{{ item.flag }}</text>
<text class="country-name">{{ item.name }}</text>
</view>
</scroll-view>
<!-- 已选筛选标签 -->
<view class="active-filters" v-if="activeFilters.length > 0">
<view class="active-filter-tag" v-for="(item, index) in activeFilters" :key="index">
<text>{{ item.label }}</text>
<text class="filter-remove" @tap="removeFilter" :data-key="item.key"></text>
</view>
<view class="clear-all" @tap="clearAllFilters">清除全部</view>
</view>
<!-- 主体内容侧边栏 + 产品列表 -->
<view class="content-body">
<!-- 左侧分类侧边栏 -->
<scroll-view class="sidebar" :scroll-y="true">
<view
:class="'sidebar-item ' + (activeCategory === item.id ? 'active' : '')"
@tap="selectCategory"
:data-id="item.id"
v-for="(item, index) in categories"
:key="index"
>
<view class="sidebar-indicator" v-if="activeCategory === item.id"></view>
<text>{{ item.name }}</text>
</view>
<!-- 促销Banner -->
<view class="sidebar-promo">
<text class="promo-title">0元拿样</text>
<text class="promo-desc">全国包邮</text>
</view>
</scroll-view>
<!-- 右侧产品列表 -->
<scroll-view class="product-list-area" :scroll-y="true" @scrolltolower="loadMore">
<!-- 子分类 -->
<view class="sub-categories" v-if="subCategories.length > 0">
<view
:class="'sub-cat-item ' + (activeSubCategory === item.id ? 'active' : '')"
@tap="selectSubCategory"
:data-id="item.id"
v-for="(item, index) in subCategories"
:key="index"
>
{{ item.name }}
</view>
</view>
<!-- 产品网格 -->
<view class="product-grid">
<view class="product-card" @tap="goDetail" :data-id="item.id" v-for="(item, index) in products" :key="index">
<view class="product-img" :style="'background: ' + item.bgColor + ';'">
<text class="product-emoji">{{ item.emoji }}</text>
<view class="product-badges">
<view class="product-badge" :style="'background: ' + tag.bg + '; color: ' + tag.color + ';'" v-for="(tag, index1) in item.tags" :key="index1">
{{ tag.text }}
</view>
</view>
</view>
<view class="product-info">
<text class="product-name">{{ item.name }}</text>
<view class="product-bottom">
<text class="product-price">
¥{{ item.price }}
<text class="product-price-unit"></text>
</text>
</view>
</view>
</view>
</view>
<!-- 分页 -->
<view class="pagination">
<text class="page-info">显示 {{ pagination.start }}-{{ pagination.end }} {{ pagination.total }} 个产品</text>
<view class="page-controls">
<view :class="'page-btn ' + (pagination.current === 1 ? 'disabled' : '')" @tap="changePage" data-action="prev">上一页</view>
<view
:class="'page-num ' + (pagination.current === item ? 'active' : '')"
@tap="goToPage"
:data-page="item"
v-for="(item, index) in pageNumbers"
:key="index"
>
{{ item }}
</view>
<view :class="'page-btn ' + (pagination.current === pagination.totalPages ? 'disabled' : '')" @tap="changePage" data-action="next">下一页</view>
</view>
<view class="page-size">
<text>{{ pagination.pageSize }}/</text>
</view>
</view>
<!-- 加载更多提示 -->
<view class="loading-more" v-if="loadingMore">
<text>加载中...</text>
</view>
</scroll-view>
</view>
<!-- 筛选弹窗 -->
<view :class="'filter-overlay ' + (showFilter ? 'show' : '')" @tap="closeFilter"></view>
<view :class="'filter-drawer ' + (showFilter ? 'show' : '')">
<view class="filter-drawer-header">
<text class="filter-drawer-title">筛选条件</text>
<text class="filter-drawer-close" @tap="closeFilter"></text>
</view>
<scroll-view class="filter-drawer-body" :scroll-y="true">
<!-- 工艺 -->
<view class="filter-group">
<text class="filter-group-title">工艺</text>
<view class="filter-options">
<view
:class="'filter-option ' + (selectedCrafts.indexOf(item) > -1 ? 'selected' : '')"
@tap="toggleCraft"
:data-value="item"
v-for="(item, index) in craftOptions"
:key="index"
>
{{ item }}
</view>
</view>
</view>
<!-- 材质 -->
<view class="filter-group">
<text class="filter-group-title">材质</text>
<view class="filter-options">
<view
:class="'filter-option ' + (selectedMaterials.indexOf(item) > -1 ? 'selected' : '')"
@tap="toggleMaterial"
:data-value="item"
v-for="(item, index) in materialOptions"
:key="index"
>
{{ item }}
</view>
</view>
</view>
<!-- 价格区间 -->
<view class="filter-group">
<text class="filter-group-title">价格区间</text>
<view class="filter-price-range">
<input class="price-input" placeholder="最低价" type="number" />
<text class="price-divider">-</text>
<input class="price-input" placeholder="最高价" type="number" />
</view>
</view>
</scroll-view>
<view class="filter-drawer-footer">
<view class="filter-reset" @tap="resetFilters">重置</view>
<view class="filter-confirm" @tap="confirmFilters">确定</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
searchKeyword: '',
selectedCountry: 'us',
countries: [
{
id: 'us',
name: '美国',
flag: '🇺🇸'
},
{
id: 'uk',
name: '英国',
flag: '🇬🇧'
},
{
id: 'jp',
name: '日本',
flag: '🇯🇵'
},
{
id: 'mx',
name: '墨西哥',
flag: '🇲🇽'
},
{
id: 'br',
name: '巴西',
flag: '🇧🇷'
},
{
id: 'ae',
name: '中东',
flag: '🇦🇪'
},
{
id: 'pl',
name: '波兰',
flag: '🇵🇱'
},
{
id: 'es',
name: '西班牙',
flag: '🇪🇸'
},
{
id: 'de',
name: '德国',
flag: '🇩🇪'
},
{
id: 'it',
name: '意大利',
flag: '🇮🇹'
},
{
id: 'fr',
name: '法国',
flag: '🇫🇷'
},
{
id: 'ca',
name: '加拿大',
flag: '🇨🇦'
},
{
id: 'au',
name: '澳大利亚',
flag: '🇦🇺'
},
{
id: 'kr',
name: '韩国',
flag: '🇰🇷'
}
],
activeCategory: 'all',
categories: [
{
id: 'all',
name: '全部商品'
},
{
id: 'men',
name: '男士服装'
},
{
id: 'women',
name: '女士服装'
},
{
id: 'kids',
name: '儿童服装'
},
{
id: 'home',
name: '家居配饰'
},
{
id: 'bag',
name: '包包配饰'
}
],
subCategories: [],
activeSubCategory: '',
activeFilters: [
{
key: 'country',
label: '美国'
},
{
key: 'craft',
label: '烫画'
},
{
key: 'material',
label: '棉麻'
}
],
showFilter: false,
selectedCrafts: ['烫画'],
selectedMaterials: ['棉麻'],
craftOptions: ['烫画', '刺绣', '丝印', '数码直喷', '热转印'],
materialOptions: ['棉麻', '纯棉', '涤纶', '混纺', '牛奶丝'],
pagination: {
start: 1,
end: 10,
total: 96,
current: 1,
pageSize: 10,
totalPages: 10
},
pageNumbers: [1, 2, 3, 4, 5],
loadingMore: false,
products: [
{
id: 1,
name: '180G牛奶丝T恤 DG120',
price: '28.00',
bgColor: '#F5F5F5',
emoji: '👕',
tags: [
{
text: '可定制',
bg: '#FFF0E5',
color: '#FF6800'
},
{
text: '现货',
bg: '#E5F5FF',
color: '#1890FF'
}
]
},
{
id: 2,
name: '纯棉连帽卫衣 HD200',
price: '59.00',
bgColor: '#E8E8E8',
emoji: '🧥',
tags: [
{
text: '可定制',
bg: '#FFF0E5',
color: '#FF6800'
},
{
text: '快返',
bg: '#F0FFE5',
color: '#52C41A'
}
]
},
{
id: 3,
name: '260G重装T恤 TS260',
price: '35.00',
bgColor: '#F0F0F0',
emoji: '👕',
tags: [
{
text: '可定制',
bg: '#FFF0E5',
color: '#FF6800'
}
]
},
{
id: 4,
name: '精梳棉Polo衫 PL100',
price: '45.00',
bgColor: '#F8F8F8',
emoji: '👔',
tags: [
{
text: '可定制',
bg: '#FFF0E5',
color: '#FF6800'
},
{
text: '现货',
bg: '#E5F5FF',
color: '#1890FF'
}
]
},
{
id: 5,
name: '抓绒卫裤 PT150',
price: '42.00',
bgColor: '#F5F5F5',
emoji: '👖',
tags: [
{
text: '快返',
bg: '#F0FFE5',
color: '#52C41A'
}
]
},
{
id: 6,
name: '运动速干T恤 ST200',
price: '32.00',
bgColor: '#E8E8E8',
emoji: '👕',
tags: [
{
text: '可定制',
bg: '#FFF0E5',
color: '#FF6800'
}
]
},
{
id: 7,
name: '纯棉背包 BP300',
price: '25.00',
bgColor: '#F0F0F0',
emoji: '🎒',
tags: [
{
text: '可定制',
bg: '#FFF0E5',
color: '#FF6800'
},
{
text: '现货',
bg: '#E5F5FF',
color: '#1890FF'
}
]
},
{
id: 8,
name: '儿童纯棉T恤 KD100',
price: '22.00',
bgColor: '#F8F8F8',
emoji: '👶',
tags: [
{
text: '可定制',
bg: '#FFF0E5',
color: '#FF6800'
}
]
},
{
id: 9,
name: '帆布托特包 BG150',
price: '15.00',
bgColor: '#F5F5F5',
emoji: '👜',
tags: [
{
text: '可定制',
bg: '#FFF0E5',
color: '#FF6800'
},
{
text: '快返',
bg: '#F0FFE5',
color: '#52C41A'
}
]
},
{
id: 10,
name: '针织开衫 CR180',
price: '55.00',
bgColor: '#E8E8E8',
emoji: '🧶',
tags: [
{
text: '可定制',
bg: '#FFF0E5',
color: '#FF6800'
}
]
}
],
tag: {
bg: '',
color: '',
text: ''
}
};
},
onLoad() {
this.loadSubCategories('all');
},
methods: {
loadSubCategories(catId) {
const subCatMap = {
all: [],
men: [
{
id: 'tops',
name: '上装'
},
{
id: 'hoodie',
name: '卫衣'
},
{
id: 'tshirt',
name: 'T恤'
},
{
id: 'shirt',
name: '衬衫'
},
{
id: 'pants',
name: '裤装'
},
{
id: 'underwear',
name: '内衣'
}
],
women: [
{
id: 'dress',
name: '连衣裙'
},
{
id: 'tops',
name: '上装'
},
{
id: 'bottoms',
name: '下装'
}
],
kids: [
{
id: 'baby',
name: '婴儿装'
},
{
id: 'kids',
name: '儿童装'
}
],
home: [
{
id: 'blanket',
name: '毛毯'
},
{
id: 'pillow',
name: '抱枕'
}
],
bag: [
{
id: 'tote',
name: '托特包'
},
{
id: 'backpack',
name: '背包'
}
]
};
this.setData({
subCategories: subCatMap[catId] || [],
activeSubCategory: subCatMap[catId]?.[0]?.id || ''
});
},
onSearchInput(e) {
this.setData({
searchKeyword: e.detail.value
});
},
selectCountry(e) {
const id = e.currentTarget.dataset.id;
const country = this.countries.find((c) => c.id === id);
const filters = this.activeFilters.filter((f) => f.key !== 'country');
filters.push({
key: 'country',
label: country.name
});
this.setData({
selectedCountry: id,
activeFilters: filters
});
},
selectCategory(e) {
const id = e.currentTarget.dataset.id;
this.setData({
activeCategory: id
});
this.loadSubCategories(id);
},
selectSubCategory(e) {
this.setData({
activeSubCategory: e.currentTarget.dataset.id
});
},
toggleFilter() {
this.setData({
showFilter: !this.showFilter
});
},
closeFilter() {
this.setData({
showFilter: false
});
},
toggleCraft(e) {
const value = e.currentTarget.dataset.value;
let crafts = [...this.selectedCrafts];
const idx = crafts.indexOf(value);
if (idx > -1) {
crafts.splice(idx, 1);
} else {
crafts.push(value);
}
this.setData({
selectedCrafts: crafts
});
},
toggleMaterial(e) {
const value = e.currentTarget.dataset.value;
let materials = [...this.selectedMaterials];
const idx = materials.indexOf(value);
if (idx > -1) {
materials.splice(idx, 1);
} else {
materials.push(value);
}
this.setData({
selectedMaterials: materials
});
},
removeFilter(e) {
const key = e.currentTarget.dataset.key;
const filters = this.activeFilters.filter((f) => f.key !== key);
this.setData({
activeFilters: filters
});
},
clearAllFilters() {
this.setData({
activeFilters: []
});
},
resetFilters() {
this.setData({
selectedCrafts: [],
selectedMaterials: []
});
},
confirmFilters() {
const filters = [];
if (this.selectedCrafts.length > 0) {
filters.push({
key: 'craft',
label: this.selectedCrafts.join(',')
});
}
if (this.selectedMaterials.length > 0) {
filters.push({
key: 'material',
label: this.selectedMaterials.join(',')
});
}
this.setData({
activeFilters: filters,
showFilter: false
});
},
goDetail(e) {
const id = e.currentTarget.dataset.id;
uni.navigateTo({
url: `/pages/product-detail/product-detail?id=${id}`
});
},
changePage(e) {
const action = e.currentTarget.dataset.action;
let current = this.pagination.current;
if (action === 'prev' && current > 1) {
current--;
}
if (action === 'next' && current < this.pagination.totalPages) {
current++;
}
this.updatePagination(current);
},
goToPage(e) {
this.updatePagination(e.currentTarget.dataset.page);
},
updatePagination(current) {
const total = this.pagination.total;
const pageSize = this.pagination.pageSize;
const totalPages = Math.ceil(total / pageSize);
let pageNumbers = [];
for (let i = Math.max(1, current - 2); i <= Math.min(totalPages, current + 2); i++) {
pageNumbers.push(i);
}
this.setData({
pagination: {
...this.pagination,
current,
start: (current - 1) * pageSize + 1,
end: Math.min(current * pageSize, total),
totalPages
},
pageNumbers
});
},
loadMore() {
if (this.loadingMore) {
return;
}
this.setData({
loadingMore: true
});
setTimeout(() => {
this.setData({
loadingMore: false
});
}, 1000);
}
}
};
</script>
<style>
@import './products.css';
</style>