|
|
|
|
|
|
|
|
v-if="selectedCategory" |
|
|
v-if="selectedCategory" |
|
|
@click="clearCategory" |
|
|
@click="clearCategory" |
|
|
class="flex items-center gap-1 px-1.5 py-0.5 sm:px-2 sm:py-1 md:px-2.5 md:py-1.5 lg:px-3 lg:py-2 text-xs sm:text-sm md:text-base text-white/60 hover:text-white bg-zinc-800/50 hover:bg-zinc-700/50 rounded-lg transition-all duration-300 active:scale-95" |
|
|
class="flex items-center gap-1 px-1.5 py-0.5 sm:px-2 sm:py-1 md:px-2.5 md:py-1.5 lg:px-3 lg:py-2 text-xs sm:text-sm md:text-base text-white/60 hover:text-white bg-zinc-800/50 hover:bg-zinc-700/50 rounded-lg transition-all duration-300 active:scale-95" |
|
|
|
|
|
v-show="false" |
|
|
> |
|
|
> |
|
|
<svg |
|
|
<svg |
|
|
xmlns="http://www.w3.org/2000/svg" |
|
|
xmlns="http://www.w3.org/2000/svg" |
|
|
|
|
|
|
|
|
{{ category }} |
|
|
{{ category }} |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<div |
|
|
|
|
|
v-if="getCategoryTags(category)?.length > 0 && selectedCategory" |
|
|
|
|
|
class="flex flex-wrap gap-4" |
|
|
|
|
|
> |
|
|
|
|
|
<span |
|
|
|
|
|
@click="selectTag('all')" |
|
|
|
|
|
class="text-white text-sm md:text-lg font-normal px-4 py-2 bg-zinc-800/50 rounded-lg cursor-pointer hover:bg-zinc-700/50 transition-all duration-300" |
|
|
|
|
|
:class="{ |
|
|
|
|
|
'!bg-cyan-400 text-zinc-900 font-medium': |
|
|
|
|
|
selectedTag === 'all', |
|
|
|
|
|
}" |
|
|
|
|
|
> |
|
|
|
|
|
{{ t("common.all") }} |
|
|
|
|
|
</span> |
|
|
|
|
|
<span |
|
|
|
|
|
v-for="tag in getCategoryTags(category)" |
|
|
|
|
|
:key="tag" |
|
|
|
|
|
@click="selectTag(tag)" |
|
|
|
|
|
class="text-white text-sm md:text-lg font-normal px-4 py-2 bg-zinc-800/50 rounded-lg cursor-pointer hover:bg-zinc-700/50 transition-all duration-300" |
|
|
|
|
|
:class="{ |
|
|
|
|
|
'!bg-cyan-400 text-zinc-900 font-medium': |
|
|
|
|
|
selectedTag === tag, |
|
|
|
|
|
}" |
|
|
|
|
|
> |
|
|
|
|
|
{{ tag }} |
|
|
|
|
|
</span> |
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
<!-- 添加系列分组 --> |
|
|
<!-- 添加系列分组 --> |
|
|
<template |
|
|
<template |
|
|
v-for="[seriesName, seriesProducts] in Array.from( |
|
|
v-for="[seriesName, seriesProducts] in Array.from( |
|
|
|
|
|
|
|
|
(c) => c.title === category |
|
|
(c) => c.title === category |
|
|
); |
|
|
); |
|
|
return ( |
|
|
return ( |
|
|
categoryObj && p.categoryId === categoryObj.id |
|
|
|
|
|
|
|
|
categoryObj && |
|
|
|
|
|
p.categoryId === categoryObj.id && |
|
|
|
|
|
(selectedTag === 'all' || p.tag === selectedTag) |
|
|
); |
|
|
); |
|
|
}) |
|
|
}) |
|
|
" |
|
|
" |
|
|
|
|
|
|
|
|
); |
|
|
); |
|
|
return ( |
|
|
return ( |
|
|
categoryObj && |
|
|
categoryObj && |
|
|
p.categoryId === categoryObj.id |
|
|
|
|
|
|
|
|
p.categoryId === categoryObj.id && |
|
|
|
|
|
(selectedTag === 'all' || |
|
|
|
|
|
p.tag === selectedTag) |
|
|
); |
|
|
); |
|
|
})" |
|
|
})" |
|
|
:key="product.id" |
|
|
:key="product.id" |
|
|
|
|
|
|
|
|
return ( |
|
|
return ( |
|
|
categoryObj && |
|
|
categoryObj && |
|
|
p.categoryId === categoryObj.id && |
|
|
p.categoryId === categoryObj.id && |
|
|
(!p.series || p.series.length === 0) |
|
|
|
|
|
|
|
|
(!p.series || p.series.length === 0) && |
|
|
|
|
|
(selectedTag === 'all' || p.tag === selectedTag) |
|
|
); |
|
|
); |
|
|
}).length > 0 |
|
|
}).length > 0 |
|
|
" |
|
|
" |
|
|
|
|
|
|
|
|
return ( |
|
|
return ( |
|
|
categoryObj && |
|
|
categoryObj && |
|
|
p.categoryId === categoryObj.id && |
|
|
p.categoryId === categoryObj.id && |
|
|
(!p.series || p.series.length === 0) |
|
|
|
|
|
|
|
|
(!p.series || p.series.length === 0) && |
|
|
|
|
|
(selectedTag === 'all' || p.tag === selectedTag) |
|
|
); |
|
|
); |
|
|
})" |
|
|
})" |
|
|
:key="product.id" |
|
|
:key="product.id" |
|
|
|
|
|
|
|
|
summary: string; |
|
|
summary: string; |
|
|
gallery: string[]; |
|
|
gallery: string[]; |
|
|
sort: number; |
|
|
sort: number; |
|
|
|
|
|
tag: string; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
interface Category { |
|
|
interface Category { |
|
|
|
|
|
|
|
|
summary: string; |
|
|
summary: string; |
|
|
capacities: string[]; |
|
|
capacities: string[]; |
|
|
sort: number; |
|
|
sort: number; |
|
|
|
|
|
tags: string[]; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 分页配置 |
|
|
// 分页配置 |
|
|
|
|
|
|
|
|
const usages = ref<string[]>([]); |
|
|
const usages = ref<string[]>([]); |
|
|
const selectedCategory = ref(route.query.category?.toString() || ""); |
|
|
const selectedCategory = ref(route.query.category?.toString() || ""); |
|
|
const selectedUsage = ref(route.query.usage?.toString() || ""); |
|
|
const selectedUsage = ref(route.query.usage?.toString() || ""); |
|
|
|
|
|
const selectedTag = ref(route.query.tag?.toString() || "all"); |
|
|
|
|
|
|
|
|
|
|
|
const getCategoryTags = (category: string) => { |
|
|
|
|
|
const categoryObj = allCategories.value.find((c) => c.title === category); |
|
|
|
|
|
return categoryObj?.tags || []; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
// 使用缓存优化数据加载 |
|
|
// 使用缓存优化数据加载 |
|
|
const { data: productData } = await useAsyncData("products", async () => { |
|
|
const { data: productData } = await useAsyncData("products", async () => { |
|
|
|
|
|
|
|
|
summary: meta.summary || "", |
|
|
summary: meta.summary || "", |
|
|
gallery: meta.gallery || [], |
|
|
gallery: meta.gallery || [], |
|
|
sort: meta.sort || 0, |
|
|
sort: meta.sort || 0, |
|
|
|
|
|
tag: meta.tag || "", |
|
|
}; |
|
|
}; |
|
|
}) |
|
|
}) |
|
|
.sort((a, b) => a.sort - b.sort); |
|
|
.sort((a, b) => a.sort - b.sort); |
|
|
|
|
|
|
|
|
summary: meta.summary || "", |
|
|
summary: meta.summary || "", |
|
|
capacities: meta.capacities || [], |
|
|
capacities: meta.capacities || [], |
|
|
sort: meta.sort || 0, |
|
|
sort: meta.sort || 0, |
|
|
|
|
|
tags: meta.tags || [], |
|
|
}; |
|
|
}; |
|
|
}) |
|
|
}) |
|
|
.sort((a, b) => a.sort - b.sort); |
|
|
.sort((a, b) => a.sort - b.sort); |
|
|
|
|
|
|
|
|
* 处理分类筛选 |
|
|
* 处理分类筛选 |
|
|
*/ |
|
|
*/ |
|
|
function handleCategoryFilter(category: string) { |
|
|
function handleCategoryFilter(category: string) { |
|
|
selectedCategory.value = selectedCategory.value === category ? "" : category; |
|
|
|
|
|
|
|
|
// 不能取消选择 |
|
|
|
|
|
selectedCategory.value = category; |
|
|
|
|
|
// 切换分类时,重置tag选择为'all' |
|
|
|
|
|
selectedTag.value = "all"; |
|
|
currentPage.value = 1; // 重置页码 |
|
|
currentPage.value = 1; // 重置页码 |
|
|
filteredProducts.value = paginatedProducts.value; |
|
|
filteredProducts.value = paginatedProducts.value; |
|
|
|
|
|
|
|
|
|
|
|
// 更新路由,移除tag参数 |
|
|
|
|
|
router.push({ |
|
|
|
|
|
query: { |
|
|
|
|
|
...route.query, |
|
|
|
|
|
category: category, |
|
|
|
|
|
tag: undefined, // 清除tag参数 |
|
|
|
|
|
page: currentPage.value > 1 ? currentPage.value.toString() : undefined |
|
|
|
|
|
} |
|
|
|
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
|
|
|
|
|
|
filteredProducts.value = paginatedProducts.value; |
|
|
filteredProducts.value = paginatedProducts.value; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 监听分类变化 |
|
|
|
|
|
|
|
|
// 监听分类变化 - 注释掉这个监听,防止干扰直接路由更新 |
|
|
|
|
|
/* |
|
|
watch(selectedCategory, (newValue) => { |
|
|
watch(selectedCategory, (newValue) => { |
|
|
debouncedRouterPush({ |
|
|
debouncedRouterPush({ |
|
|
...route.query, |
|
|
...route.query, |
|
|
category: newValue || undefined, |
|
|
category: newValue || undefined, |
|
|
}); |
|
|
}); |
|
|
}); |
|
|
}); |
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
// 监听用途变化 |
|
|
// 监听用途变化 |
|
|
watch(selectedUsage, (newValue) => { |
|
|
watch(selectedUsage, (newValue) => { |
|
|
|
|
|
|
|
|
(newQuery) => { |
|
|
(newQuery) => { |
|
|
selectedCategory.value = newQuery.category?.toString() || ""; |
|
|
selectedCategory.value = newQuery.category?.toString() || ""; |
|
|
selectedUsage.value = newQuery.usage?.toString() || ""; |
|
|
selectedUsage.value = newQuery.usage?.toString() || ""; |
|
|
|
|
|
selectedTag.value = newQuery.tag?.toString() || "all"; |
|
|
const page = parseInt(newQuery.page?.toString() || "1"); |
|
|
const page = parseInt(newQuery.page?.toString() || "1"); |
|
|
if (page !== currentPage.value) { |
|
|
if (page !== currentPage.value) { |
|
|
currentPage.value = page; |
|
|
currentPage.value = page; |
|
|
|
|
|
|
|
|
const resetFilters = () => { |
|
|
const resetFilters = () => { |
|
|
selectedCategory.value = ""; |
|
|
selectedCategory.value = ""; |
|
|
selectedUsage.value = ""; |
|
|
selectedUsage.value = ""; |
|
|
|
|
|
selectedTag.value = "all"; |
|
|
currentPage.value = 1; |
|
|
currentPage.value = 1; |
|
|
debouncedRouterPush({}); |
|
|
debouncedRouterPush({}); |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
// 清除分类筛选 |
|
|
|
|
|
const clearCategory = () => { |
|
|
|
|
|
|
|
|
// 清除分类筛选 (已经隐藏按钮) |
|
|
|
|
|
function clearCategory() { |
|
|
selectedCategory.value = ""; |
|
|
selectedCategory.value = ""; |
|
|
currentPage.value = 1; |
|
|
currentPage.value = 1; |
|
|
filteredProducts.value = paginatedProducts.value; |
|
|
filteredProducts.value = paginatedProducts.value; |
|
|
|
|
|
|
|
|
...route.query, |
|
|
...route.query, |
|
|
category: undefined, |
|
|
category: undefined, |
|
|
}); |
|
|
}); |
|
|
}; |
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// 清除用途筛选 |
|
|
// 清除用途筛选 |
|
|
const clearUsage = () => { |
|
|
const clearUsage = () => { |
|
|
|
|
|
|
|
|
}); |
|
|
}); |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// 选择标签进行筛选 |
|
|
|
|
|
function selectTag(tag: string) { |
|
|
|
|
|
selectedTag.value = tag; |
|
|
|
|
|
|
|
|
|
|
|
// 更新路由查询参数 |
|
|
|
|
|
router.push({ |
|
|
|
|
|
query: { |
|
|
|
|
|
...route.query, |
|
|
|
|
|
tag: tag === "all" ? undefined : tag |
|
|
|
|
|
} |
|
|
|
|
|
}); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// 组件卸载时清理资源 |
|
|
// 组件卸载时清理资源 |
|
|
onBeforeUnmount(() => { |
|
|
onBeforeUnmount(() => { |
|
|
allProducts.value = []; |
|
|
allProducts.value = []; |