Преглед изворни кода

feat: 新增产品标签功能和多语言支持

本次提交主要进行了以下修改:
1. 在 `content.config.ts` 中为产品和类别集合新增了 `tags` 字段,以支持产品标签功能。
2. 在 `i18n/locales` 目录下的多语言文件中新增了 `all` 标签的翻译,确保多语言环境下的标签显示一致。
3. 在 `pages/products/index.vue` 中实现了标签选择功能,用户可以通过标签筛选产品,提升了用户体验。
4. 更新了相关的路由逻辑,以支持标签的选择和清除。

这些改动旨在增强产品页面的可用性和用户交互体验,同时提升多语言支持的完整性。
master
lizhuang пре 1 месец
родитељ
комит
2679beeb56
5 измењених фајлова са 89 додато и 9 уклоњено
  1. 2
    0
      content.config.ts
  2. 1
    0
      i18n/locales/en.ts
  3. 1
    0
      i18n/locales/ja.ts
  4. 1
    0
      i18n/locales/zh.ts
  5. 84
    9
      pages/products/index.vue

+ 2
- 0
content.config.ts Прегледај датотеку

summary: z.string(), summary: z.string(),
capacities: z.array(z.string()), capacities: z.array(z.string()),
sort: z.number(), sort: z.number(),
tags: z.array(z.string()),
}) })
}) })


categoryId: z.string(), // 关联类别的ID categoryId: z.string(), // 关联类别的ID
usage: z.array(z.string()), usage: z.array(z.string()),
series: z.array(z.string()), series: z.array(z.string()),
tag: z.array(z.string()),
image: z.string(), image: z.string(),
gallery: z.array(z.string()), gallery: z.array(z.string()),
summary: z.string(), summary: z.string(),

+ 1
- 0
i18n/locales/en.ts Прегледај датотеку

privacy: "Privacy Policy", privacy: "Privacy Policy",
terms: "Terms of Use", terms: "Terms of Use",
}, },
all: "All",
}, },
}, },
home: { home: {

+ 1
- 0
i18n/locales/ja.ts Прегледај датотеку

privacy: "プライバシーポリシー", privacy: "プライバシーポリシー",
terms: "利用規約", terms: "利用規約",
}, },
all: "全部",
}, },
}, },
home: { home: {

+ 1
- 0
i18n/locales/zh.ts Прегледај датотеку

description: "了解 Hanye 如何收集、使用和保护您的个人信息", description: "了解 Hanye 如何收集、使用和保护您的个人信息",
}, },
}, },
all: "全部",
}, },
home: { home: {
title: "Hanye 官网", title: "Hanye 官网",

+ 84
- 9
pages/products/index.vue Прегледај датотеку

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 = [];

Loading…
Откажи
Сачувај