- 修改FAQ组件,优化加载动画和搜索功能,提升用户体验。 - 增加搜索框和分类选择功能,支持更灵活的FAQ查询。 - 更新国际化文件,添加多语言支持的搜索相关内容,确保用户在不同语言下的使用体验一致。 - 调整样式,提升页面视觉效果,确保在不同设备上的适配性。master
@@ -1,108 +1,174 @@ | |||
<template> | |||
<div class="faq-content"> | |||
<div v-if="isLoading" class="flex justify-center py-12"> | |||
<div class="animate-spin h-8 w-8 border-2 border-blue-400 border-t-transparent rounded-full"></div> | |||
<div class="animate-spin h-8 w-8 border-2 border-cyan-400 border-t-transparent rounded-full"></div> | |||
</div> | |||
<div v-else class="p-8"> | |||
<!-- 分类和搜索区域 --> | |||
<div class="mb-8 space-y-6"> | |||
<!-- 分类选择 --> | |||
<div class="flex flex-wrap gap-2"> | |||
<button | |||
v-for="category in categoriesList" | |||
:key="category" | |||
@click="handleCategoryFilter(category)" | |||
class="px-4 py-2 rounded-md text-sm font-medium transition-colors duration-200 border" | |||
:class="{ | |||
'bg-blue-600 text-white border-blue-600': selectedCategory === category, | |||
'bg-gray-700 text-gray-300 border-gray-600 hover:bg-gray-600': selectedCategory !== category, | |||
}" | |||
> | |||
{{ category }} | |||
</button> | |||
<div v-else class="space-y-8"> | |||
<!-- 搜索和筛选区域 --> | |||
<div class="rounded-xl p-6"> | |||
<div class="text-center mb-6"> | |||
<h2 class="text-xl font-bold text-white mb-2">{{ t("support.faq.searchTitle") }}</h2> | |||
<p class="text-gray-400 text-sm">{{ t("support.faq.searchDescription") }}</p> | |||
</div> | |||
<!-- 搜索框 --> | |||
<div class="relative max-w-md"> | |||
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none"> | |||
<svg class="h-5 w-5 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path> | |||
<div class="relative max-w-xl mx-auto mb-6"> | |||
<div class="absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none"> | |||
<svg class="h-5 w-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" /> | |||
</svg> | |||
</div> | |||
<input | |||
v-model="searchTerm" | |||
type="search" | |||
:placeholder="t('faq.searchPlaceholder')" | |||
class="w-full pl-10 pr-10 py-3 border border-gray-600 rounded-md text-white bg-gray-700 placeholder-gray-400 focus:border-blue-500 focus:ring-1 focus:ring-blue-500 focus:outline-none" | |||
type="text" | |||
class="w-full pl-12 pr-12 py-3 bg-zinc-900 border border-zinc-600 rounded-lg text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-cyan-400/50 focus:border-cyan-400 transition-all duration-200" | |||
:placeholder="t('support.faq.searchPlaceholder')" | |||
/> | |||
<button | |||
v-if="searchTerm" | |||
@click="clearSearch" | |||
class="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-500 hover:text-gray-300" | |||
class="absolute inset-y-0 right-0 pr-4 flex items-center text-gray-400 hover:text-cyan-400 transition-colors duration-200" | |||
> | |||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path> | |||
<svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" /> | |||
</svg> | |||
</button> | |||
</div> | |||
<!-- 分类选择 --> | |||
<div> | |||
<div class="flex flex-wrap justify-center gap-3"> | |||
<button | |||
v-for="category in categoriesList" | |||
:key="category" | |||
@click="handleCategoryFilter(category)" | |||
:class="[ | |||
'px-4 py-2 rounded-lg font-medium text-sm transition-all duration-200 border', | |||
selectedCategory === category | |||
? 'bg-cyan-400 text-zinc-900 border-cyan-400 shadow-lg shadow-cyan-400/25' | |||
: 'bg-zinc-900 text-gray-300 border-zinc-600 hover:bg-zinc-800 hover:border-cyan-400/40 hover:text-white' | |||
]" | |||
> | |||
{{ category }} | |||
</button> | |||
</div> | |||
</div> | |||
</div> | |||
<!-- 搜索结果统计 --> | |||
<div v-if="filteredFaqs.length > 0" class="flex items-center justify-between px-2"> | |||
<p class="text-gray-400 text-sm"> | |||
{{ locale === 'zh' ? '找到' : locale === 'en' ? 'Found' : '見つかりました' }} <span class="text-cyan-400 font-semibold">{{ filteredFaqs.length }}</span> {{ t("support.faq.questionCount") }} | |||
</p> | |||
<div class="text-xs text-gray-500"> | |||
{{ selectedCategory !== categoriesList[0] ? `${locale === 'zh' ? '分类' : locale === 'en' ? 'Category' : 'カテゴリー'}: ${selectedCategory}` : (locale === 'zh' ? '显示全部分类' : locale === 'en' ? 'Show all categories' : '全てのカテゴリーを表示') }} | |||
</div> | |||
</div> | |||
<!-- FAQ列表 --> | |||
<div class="space-y-3"> | |||
<div class="space-y-4"> | |||
<div v-if="filteredFaqs.length === 0" class="text-center py-16"> | |||
<div class="w-16 h-16 bg-zinc-800 rounded-full flex items-center justify-center mx-auto mb-4"> | |||
<svg class="w-8 h-8 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.172 16.172a4 4 0 015.656 0M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" /> | |||
</svg> | |||
</div> | |||
<h3 class="text-lg font-semibold text-white mb-2">{{ t("support.faq.noResults") }}</h3> | |||
<p class="text-gray-400 mb-6">{{ t("support.faq.noResultsDesc") }}</p> | |||
<button | |||
@click="resetFilters" | |||
class="px-6 py-2 bg-cyan-400/20 text-cyan-400 rounded-lg hover:bg-cyan-400/30 transition-colors duration-200" | |||
> | |||
{{ locale === 'zh' ? '重置筛选条件' : locale === 'en' ? 'Reset Filters' : 'フィルターをリセット' }} | |||
</button> | |||
</div> | |||
<div | |||
v-for="faq in filteredFaqs" | |||
:key="generateFaqKey(faq)" | |||
class="border border-gray-600 rounded-lg overflow-hidden" | |||
class="bg-zinc-900 border border-zinc-700 rounded-lg overflow-hidden hover:border-cyan-400/30 transition-all duration-200" | |||
> | |||
<div | |||
class="flex items-center justify-between p-6 cursor-pointer hover:bg-gray-700 transition-colors duration-200" | |||
<button | |||
@click="toggleFaq(faq)" | |||
class="w-full px-6 py-5 text-left focus:outline-none focus:ring-2 focus:ring-cyan-400/30 focus:ring-inset" | |||
> | |||
<div class="text-white font-medium flex-1 text-left"> | |||
<template | |||
v-for="(part, i) in highlightKeyword(faq.title)" | |||
:key="i" | |||
> | |||
<span v-if="typeof part === 'string'">{{ part }}</span> | |||
<component v-else :is="part"></component> | |||
</template> | |||
</div> | |||
<div | |||
class="text-gray-400 transition-transform duration-200 ml-4 flex-shrink-0" | |||
:class="{ 'rotate-180': isFaqExpanded(faq) }" | |||
> | |||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path> | |||
</svg> | |||
<div class="flex items-center justify-between"> | |||
<div class="flex-1 pr-4"> | |||
<div class="flex items-center mb-2"> | |||
<span class="px-2 py-1 bg-cyan-400/20 text-cyan-400 text-xs font-medium rounded-full mr-3"> | |||
{{ faq.category }} | |||
</span> | |||
</div> | |||
<h3 class="text-lg font-semibold text-white group-hover:text-cyan-300 transition-colors text-left"> | |||
<template | |||
v-for="(part, i) in highlightKeyword(faq.title)" | |||
:key="i" | |||
> | |||
<span v-if="typeof part === 'string'">{{ part }}</span> | |||
<component v-else :is="part"></component> | |||
</template> | |||
</h3> | |||
</div> | |||
<div class="flex-shrink-0 ml-4"> | |||
<svg | |||
:class="[ | |||
'w-5 h-5 text-gray-400 transition-transform duration-200', | |||
isFaqExpanded(faq) ? 'rotate-180' : '' | |||
]" | |||
fill="none" | |||
stroke="currentColor" | |||
viewBox="0 0 24 24" | |||
> | |||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" /> | |||
</svg> | |||
</div> | |||
</div> | |||
</div> | |||
</button> | |||
<div | |||
v-if="isFaqExpanded(faq)" | |||
class="px-6 pb-6 border-t border-gray-600 bg-gray-750" | |||
<transition | |||
enter-active-class="transition-all duration-300 ease-out" | |||
enter-from-class="opacity-0 max-h-0" | |||
enter-to-class="opacity-100 max-h-screen" | |||
leave-active-class="transition-all duration-300 ease-in" | |||
leave-from-class="opacity-100 max-h-screen" | |||
leave-to-class="opacity-0 max-h-0" | |||
> | |||
<div class="pt-6"> | |||
<ContentRenderer | |||
class="prose prose-invert prose-sm max-w-none" | |||
:value="{ body: faq.content }" | |||
v-highlight="searchTerm.trim()" | |||
/> | |||
<div v-if="isFaqExpanded(faq)" class="px-6 pb-6 border-t border-zinc-700"> | |||
<div class="pt-6"> | |||
<ContentRenderer | |||
class="prose prose-invert prose-sm max-w-none" | |||
:value="{ body: faq.content }" | |||
v-highlight="searchTerm.trim()" | |||
/> | |||
<!-- 有用性评价 --> | |||
<div class="mt-8 pt-6 border-t border-zinc-700/50"> | |||
<div class="flex items-center justify-between"> | |||
<p class="text-sm text-gray-400">{{ t("support.faq.helpful") }}</p> | |||
<div class="flex items-center space-x-4"> | |||
<button | |||
class="flex items-center px-3 py-1 text-sm text-gray-400 hover:text-green-400 transition-colors duration-200" | |||
> | |||
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 10h4.764a2 2 0 011.789 2.894l-3.5 7A2 2 0 0115.263 21h-4.017c-.163 0-.326-.02-.485-.06L7 20M7 20V8m0 12v-5a2 2 0 012-2h2.5" /> | |||
</svg> | |||
{{ t("support.faq.yes") }} | |||
</button> | |||
<button | |||
class="flex items-center px-3 py-1 text-sm text-gray-400 hover:text-red-400 transition-colors duration-200" | |||
> | |||
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14H5.236a2 2 0 01-1.789-2.894l3.5-7A2 2 0 018.736 3h4.018c.163 0 .326.02.485.60L17 4m0 0v12m0-12l-5 2v8" /> | |||
</svg> | |||
{{ t("support.faq.no") }} | |||
</button> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
<div | |||
v-if="filteredFaqs.length === 0" | |||
class="text-center text-gray-400 py-16" | |||
> | |||
<div class="mb-4"> | |||
<svg class="mx-auto h-12 w-12 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path> | |||
</svg> | |||
</div> | |||
<p class="text-lg font-medium text-white mb-2">{{ t("faq.noResults") }}</p> | |||
<p class="text-sm text-gray-500">尝试调整搜索条件或选择不同的分类</p> | |||
</transition> | |||
</div> | |||
</div> | |||
</div> | |||
@@ -178,12 +244,7 @@ watchEffect(() => { | |||
if (faqData.value) { | |||
isLoading.value = true; | |||
try { | |||
const allOption: string = | |||
locale.value === "en" | |||
? "All" | |||
: locale.value === "zh" | |||
? "全部" | |||
: "すべて"; | |||
const allOption: string = t("support.faq.categoryAll"); | |||
const uniqueCategories = [ | |||
...new Set(faqData.value.map((faq: FAQ) => faq.category)), | |||
@@ -211,12 +272,7 @@ const filteredFaqs = computed(() => { | |||
let filtered = [...faqData.value]; | |||
// 按分类过滤 | |||
const allOption: string = | |||
locale.value === "en" | |||
? "All" | |||
: locale.value === "zh" | |||
? "全部" | |||
: "すべて"; | |||
const allOption: string = t("support.faq.categoryAll"); | |||
if (selectedCategory.value && selectedCategory.value !== allOption) { | |||
filtered = filtered.filter((faq) => faq.category === selectedCategory.value); | |||
@@ -275,6 +331,15 @@ const clearSearch = (): void => { | |||
searchTerm.value = ""; | |||
}; | |||
/** | |||
* 重置所有筛选条件 | |||
*/ | |||
const resetFilters = (): void => { | |||
searchTerm.value = ""; | |||
selectedCategory.value = categoriesList.value[0]; | |||
expandedFaqKeys.value.clear(); | |||
}; | |||
/** | |||
* 高亮关键词 | |||
*/ | |||
@@ -323,31 +388,33 @@ const highlightText = (el: HTMLElement, keyword: string): void => { | |||
let node: Text | null; | |||
while ((node = walker.nextNode() as Text)) { | |||
nodes.push(node); | |||
if (node.textContent && node.textContent.trim()) { | |||
nodes.push(node); | |||
} | |||
} | |||
nodes.forEach((textNode) => { | |||
const parent = textNode.parentNode; | |||
if (!parent || parent.nodeName === "MARK") return; | |||
if (!parent) return; | |||
const text = textNode.textContent || ""; | |||
const regex = new RegExp(`(${keyword.trim()})`, "gi"); | |||
const regex = new RegExp(`(${keyword})`, "gi"); | |||
if (regex.test(text)) { | |||
const fragment = document.createDocumentFragment(); | |||
const parts = text.split(regex); | |||
parts.forEach((part) => { | |||
if (part.toLowerCase() === keyword.trim().toLowerCase()) { | |||
if (part.toLowerCase() === keyword.toLowerCase()) { | |||
const mark = document.createElement("mark"); | |||
mark.className = "bg-yellow-500/30 text-yellow-200 px-1 rounded"; | |||
mark.textContent = part; | |||
fragment.appendChild(mark); | |||
} else if (part) { | |||
} else { | |||
fragment.appendChild(document.createTextNode(part)); | |||
} | |||
}); | |||
parent.replaceChild(fragment, textNode); | |||
} | |||
}); | |||
@@ -355,118 +422,103 @@ const highlightText = (el: HTMLElement, keyword: string): void => { | |||
</script> | |||
<style scoped> | |||
/* 过渡效果 */ | |||
.transition-colors { | |||
transition-property: color, background-color, border-color; | |||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); | |||
} | |||
.transition-transform { | |||
transition-property: transform; | |||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); | |||
} | |||
.duration-200 { | |||
transition-duration: 200ms; | |||
.line-clamp-2 { | |||
display: -webkit-box; | |||
-webkit-line-clamp: 2; | |||
-webkit-box-orient: vertical; | |||
overflow: hidden; | |||
} | |||
/* 悬停效果 */ | |||
.hover\:bg-gray-700:hover { | |||
background-color: #374151; | |||
.prose { | |||
max-width: none; | |||
} | |||
.hover\:bg-gray-600:hover { | |||
background-color: #4b5563; | |||
.prose h1, | |||
.prose h2, | |||
.prose h3, | |||
.prose h4, | |||
.prose h5, | |||
.prose h6 { | |||
color: #ffffff; | |||
margin-top: 1.5em; | |||
margin-bottom: 0.5em; | |||
} | |||
.hover\:text-gray-300:hover { | |||
color: #d1d5db; | |||
.prose p { | |||
color: #d4d4d8; | |||
line-height: 1.7; | |||
margin-bottom: 1em; | |||
} | |||
/* 焦点样式 */ | |||
input:focus { | |||
box-shadow: 0 0 0 1px #3b82f6; | |||
.prose ul, | |||
.prose ol { | |||
color: #d4d4d8; | |||
margin-bottom: 1em; | |||
} | |||
/* 高亮样式 */ | |||
mark { | |||
background-color: rgba(234, 179, 8, 0.3); | |||
color: #fef3c7; | |||
padding: 2px 4px; | |||
border-radius: 4px; | |||
.prose li { | |||
margin-bottom: 0.5em; | |||
} | |||
/* Prose样式覆盖 - 深色主题 */ | |||
:deep(.prose) { | |||
color: #d1d5db; | |||
max-width: none; | |||
.prose code { | |||
background-color: #27272a; | |||
color: #22d3ee; | |||
padding: 0.2em 0.4em; | |||
border-radius: 0.25rem; | |||
font-size: 0.875em; | |||
} | |||
:deep(.prose h1), | |||
:deep(.prose h2), | |||
:deep(.prose h3), | |||
:deep(.prose h4), | |||
:deep(.prose h5), | |||
:deep(.prose h6) { | |||
color: #f9fafb; | |||
font-weight: 600; | |||
.prose pre { | |||
background-color: #18181b; | |||
color: #d4d4d8; | |||
padding: 1rem; | |||
border-radius: 0.5rem; | |||
overflow-x: auto; | |||
margin-bottom: 1em; | |||
} | |||
:deep(.prose p) { | |||
margin-top: 1rem; | |||
margin-bottom: 1rem; | |||
line-height: 1.625; | |||
color: #d1d5db; | |||
.prose a { | |||
color: #22d3ee; | |||
text-decoration: underline; | |||
} | |||
:deep(.prose ul), | |||
:deep(.prose ol) { | |||
margin-top: 1rem; | |||
margin-bottom: 1rem; | |||
.prose a:hover { | |||
color: #06b6d4; | |||
} | |||
:deep(.prose li) { | |||
margin-top: 0.5rem; | |||
margin-bottom: 0.5rem; | |||
color: #d1d5db; | |||
.prose blockquote { | |||
border-left: 4px solid #22d3ee; | |||
padding-left: 1rem; | |||
margin-left: 0; | |||
font-style: italic; | |||
color: #a1a1aa; | |||
} | |||
:deep(.prose strong) { | |||
color: #f9fafb; | |||
.prose strong { | |||
color: #ffffff; | |||
font-weight: 600; | |||
} | |||
:deep(.prose code) { | |||
color: #f9fafb; | |||
background-color: #374151; | |||
padding: 0.125rem 0.25rem; | |||
border-radius: 0.25rem; | |||
font-size: 0.875em; | |||
} | |||
:deep(.prose a) { | |||
color: #60a5fa; | |||
text-decoration: underline; | |||
.prose table { | |||
width: 100%; | |||
border-collapse: collapse; | |||
margin-bottom: 1em; | |||
} | |||
:deep(.prose a:hover) { | |||
color: #93c5fd; | |||
.prose th, | |||
.prose td { | |||
border: 1px solid #3f3f46; | |||
padding: 0.5rem; | |||
text-align: left; | |||
} | |||
/* 自定义gray-750背景色 */ | |||
.bg-gray-750 { | |||
background-color: #2d3748; | |||
.prose th { | |||
background-color: #27272a; | |||
color: #ffffff; | |||
font-weight: 600; | |||
} | |||
/* 响应式优化 */ | |||
@media (max-width: 768px) { | |||
.flex-wrap { | |||
gap: 0.5rem; | |||
} | |||
.px-6 { | |||
padding-left: 1rem; | |||
padding-right: 1rem; | |||
} | |||
.prose td { | |||
color: #d4d4d8; | |||
} | |||
</style> |
@@ -200,7 +200,22 @@ export default { | |||
comingSoon: "Coming Soon", | |||
faq: { | |||
title: "FAQ", | |||
description: "Browse the most common product questions and answers" | |||
description: "Browse the most common product questions and answers", | |||
searchTitle: "Knowledge Base Search", | |||
searchDescription: "Quickly find the answers you need", | |||
searchPlaceholder: "Enter keywords to search for questions...", | |||
searchButton: "Search", | |||
categoryAll: "All Categories", | |||
noResults: "No questions found", | |||
noResultsDesc: "Please try different keywords or select a different category", | |||
loadingResults: "Searching...", | |||
questionCount: "questions", | |||
coverageRate: "coverage", | |||
availability: "24/7 access", | |||
helpful: "Was this answer helpful?", | |||
yes: "Yes", | |||
no: "No", | |||
thankYou: "Thank you for your feedback!" | |||
}, | |||
contact: { | |||
title: "Contact Us", | |||
@@ -222,7 +237,49 @@ export default { | |||
title: "Live Chat", | |||
description: "Chat with our technical experts in real-time" | |||
}, | |||
backToSupport: "Back to Support" | |||
backToSupport: "Back to Support", | |||
stats: { | |||
support247: "24/7 Support", | |||
responseTime: "< 2H Response", | |||
resolutionRate: "95% Resolution", | |||
languageSupport: "3+ Languages" | |||
}, | |||
services: { | |||
mainTitle: "Main Support Services", | |||
extendedTitle: "Extended Services", | |||
extendedSubtitle: "More professional technical resources and tools", | |||
faqRecommended: "Recommended • Instant Answers", | |||
contactPriority: "Professional • Human Service", | |||
faqFeatures: { | |||
coverage: "Covers 90% common technical issues", | |||
search: "Smart search and category filtering", | |||
maintenance: "Regularly updated and maintained" | |||
}, | |||
contactFeatures: { | |||
engineer: "Direct access to technical engineers", | |||
response: "2-hour response guarantee", | |||
solution: "Personalized solutions" | |||
}, | |||
viewFaq: "Browse FAQ", | |||
contactSupport: "Contact Support" | |||
}, | |||
team: { | |||
title: "Professional Technical Support Team", | |||
description: "Our professional technical support team consists of experienced engineers dedicated to providing high-quality technical services and solutions.", | |||
fastResponse: { | |||
title: "Fast Response", | |||
description: "Reply within 2 hours on business days" | |||
}, | |||
professional: { | |||
title: "Professional Guarantee", | |||
description: "Certified technical engineer team" | |||
}, | |||
efficient: { | |||
title: "Efficient Solutions", | |||
description: "95% issues resolved in one go" | |||
}, | |||
viewFaq: "View FAQ" | |||
} | |||
}, | |||
about: { | |||
title: "About Us", |
@@ -192,7 +192,22 @@ export default { | |||
comingSoon: "近日公開", | |||
faq: { | |||
title: "FAQ", | |||
description: "最も一般的な製品の質問と回答をご覧ください" | |||
description: "最も一般的な製品の質問と回答をご覧ください", | |||
searchTitle: "ナレッジベース検索", | |||
searchDescription: "必要な回答を素早く見つけます", | |||
searchPlaceholder: "質問のキーワードを入力してください...", | |||
searchButton: "検索", | |||
categoryAll: "全カテゴリー", | |||
noResults: "関連する質問が見つかりません", | |||
noResultsDesc: "他のキーワードを試すか、別のカテゴリーを選択してください", | |||
loadingResults: "検索中...", | |||
questionCount: "件の質問", | |||
coverageRate: "カバー率", | |||
availability: "24時間アクセス", | |||
helpful: "この回答は役に立ちましたか?", | |||
yes: "はい", | |||
no: "いいえ", | |||
thankYou: "フィードバックありがとうございます!" | |||
}, | |||
contact: { | |||
title: "お問い合わせ", | |||
@@ -214,7 +229,49 @@ export default { | |||
title: "ライブチャット", | |||
description: "技術専門家とリアルタイムでチャット" | |||
}, | |||
backToSupport: "技術サポートに戻る" | |||
backToSupport: "技術サポートに戻る", | |||
stats: { | |||
support247: "24/7 サポート", | |||
responseTime: "< 2時間対応", | |||
resolutionRate: "95% 解決率", | |||
languageSupport: "3+ 言語対応" | |||
}, | |||
services: { | |||
mainTitle: "主要サポートサービス", | |||
extendedTitle: "拡張サービス", | |||
extendedSubtitle: "より多くの専門技術リソースとツール", | |||
faqRecommended: "推奨 • 即座に回答", | |||
contactPriority: "専門サポート • 人的サービス", | |||
faqFeatures: { | |||
coverage: "90% の一般的な技術問題をカバー", | |||
search: "スマート検索とカテゴリーフィルタリング", | |||
maintenance: "定期的な更新とメンテナンス" | |||
}, | |||
contactFeatures: { | |||
engineer: "専門技術エンジニアとの直接対応", | |||
response: "2時間以内の対応保証", | |||
solution: "個別ソリューション" | |||
}, | |||
viewFaq: "FAQ を見る", | |||
contactSupport: "サポートに問い合わせ" | |||
}, | |||
team: { | |||
title: "専門技術サポートチーム", | |||
description: "私たちの専門技術サポートチームは、経験豊富なエンジニアで構成され、高品質な技術サービスとソリューションの提供に取り組んでいます。", | |||
fastResponse: { | |||
title: "迅速な対応", | |||
description: "営業日2時間以内に回答" | |||
}, | |||
professional: { | |||
title: "専門保証", | |||
description: "認定技術エンジニアチーム" | |||
}, | |||
efficient: { | |||
title: "効率的な解決", | |||
description: "95% の問題を一度で解決" | |||
}, | |||
viewFaq: "FAQ を見る" | |||
} | |||
}, | |||
about: { | |||
title: "当社について - Hanye", |
@@ -189,7 +189,22 @@ export default { | |||
comingSoon: "即将上线", | |||
faq: { | |||
title: "常见问题", | |||
description: "查看最常见的产品问题和解答" | |||
description: "查看最常见的产品问题和解答", | |||
searchTitle: "知识库搜索", | |||
searchDescription: "快速找到您需要的答案", | |||
searchPlaceholder: "请输入您要搜索的问题关键词...", | |||
searchButton: "搜索", | |||
categoryAll: "全部分类", | |||
noResults: "没有找到相关问题", | |||
noResultsDesc: "请尝试使用其他关键词或选择不同的分类", | |||
loadingResults: "正在搜索...", | |||
questionCount: "个问题", | |||
coverageRate: "覆盖率", | |||
availability: "全天候访问", | |||
helpful: "这个回答对您有帮助吗?", | |||
yes: "是", | |||
no: "否", | |||
thankYou: "感谢您的反馈!" | |||
}, | |||
contact: { | |||
title: "联系我们", | |||
@@ -211,7 +226,49 @@ export default { | |||
title: "在线客服", | |||
description: "与我们的技术专家实时对话" | |||
}, | |||
backToSupport: "返回技术支持" | |||
backToSupport: "返回技术支持", | |||
stats: { | |||
support247: "24/7 技术支持", | |||
responseTime: "< 2小时响应时间", | |||
resolutionRate: "95% 问题解决率", | |||
languageSupport: "3+ 语言支持" | |||
}, | |||
services: { | |||
mainTitle: "主要支持服务", | |||
extendedTitle: "扩展服务", | |||
extendedSubtitle: "更多专业技术资源与工具", | |||
faqRecommended: "推荐首选 • 即时解答", | |||
contactPriority: "专业支持 • 人工服务", | |||
faqFeatures: { | |||
coverage: "覆盖 90% 常见技术问题", | |||
search: "智能搜索与分类过滤", | |||
maintenance: "定期更新维护" | |||
}, | |||
contactFeatures: { | |||
engineer: "专业技术工程师对接", | |||
response: "2小时内响应保证", | |||
solution: "个性化解决方案" | |||
}, | |||
viewFaq: "浏览常见问题", | |||
contactSupport: "联系技术支持" | |||
}, | |||
team: { | |||
title: "专业技术支持团队", | |||
description: "我们的专业技术支持团队由经验丰富的工程师组成,致力于为您提供高质量的技术服务和解决方案。", | |||
fastResponse: { | |||
title: "快速响应", | |||
description: "工作日2小时内回复" | |||
}, | |||
professional: { | |||
title: "专业保障", | |||
description: "认证技术工程师团队" | |||
}, | |||
efficient: { | |||
title: "高效解决", | |||
description: "95%问题一次性解决" | |||
}, | |||
viewFaq: "查看常见问题" | |||
} | |||
}, | |||
about: { | |||
title: "关于我们", |
@@ -1,97 +1,155 @@ | |||
<template> | |||
<div class="min-h-screen bg-gray-900"> | |||
<div> | |||
<div class="w-full h-[55px] sm:h-[72px]"></div> | |||
<ErrorBoundary :error="error"> | |||
<div v-if="isLoading" class="flex justify-center py-16"> | |||
<div class="animate-spin h-8 w-8 border-2 border-blue-400 border-t-transparent rounded-full"></div> | |||
<div class="animate-spin h-8 w-8 border-2 border-cyan-400 border-t-transparent rounded-full"></div> | |||
</div> | |||
<div v-else> | |||
<div v-else class="pb-16"> | |||
<!-- 面包屑导航 --> | |||
<div class="bg-gray-800 border-b border-gray-700"> | |||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4"> | |||
<nav class="flex items-center space-x-2 text-sm text-gray-400"> | |||
<nuxt-link | |||
:to="`${homepagePath}/`" | |||
class="hover:text-blue-400 transition-colors duration-200" | |||
> | |||
{{ t("common.home") }} | |||
</nuxt-link> | |||
<svg class="w-4 h-4 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path> | |||
</svg> | |||
<nuxt-link | |||
:to="`${homepagePath}/support`" | |||
class="hover:text-blue-400 transition-colors duration-200" | |||
> | |||
{{ t("support.title") }} | |||
</nuxt-link> | |||
<svg class="w-4 h-4 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path> | |||
</svg> | |||
<span class="text-gray-200 font-medium">{{ t("support.faq.title") }}</span> | |||
</nav> | |||
<div class="max-w-full xl:px-8 lg:px-6 md:px-4 px-4 mt-6 mb-8"> | |||
<div class="max-w-screen-2xl mx-auto"> | |||
<nuxt-link | |||
:to="`${homepagePath}/`" | |||
class="text-white/60 text-base font-normal hover:text-white transition-colors duration-300" | |||
> | |||
{{ t("common.home") }} | |||
</nuxt-link> | |||
<span class="text-white/60 text-base font-normal px-2"> / </span> | |||
<nuxt-link | |||
:to="`${homepagePath}/support`" | |||
class="text-white/60 text-base font-normal hover:text-white transition-colors duration-300" | |||
> | |||
{{ t("support.title") }} | |||
</nuxt-link> | |||
<span class="text-white/60 text-base font-normal px-2"> / </span> | |||
<span class="text-white text-base font-normal"> | |||
{{ t("support.faq.title") }} | |||
</span> | |||
</div> | |||
</div> | |||
<!-- 页面头部 --> | |||
<div class="bg-gray-800"> | |||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-16"> | |||
<div class="max-w-full mb-16 xl:px-8 lg:px-6 md:px-4 px-4"> | |||
<div class="max-w-screen-2xl mx-auto"> | |||
<div class="text-center"> | |||
<div class="flex items-center justify-center mb-6"> | |||
<div class="w-12 h-12 bg-blue-500/20 text-blue-400 rounded-lg flex items-center justify-center mr-4"> | |||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path> | |||
</svg> | |||
</div> | |||
<h1 class="text-3xl md:text-4xl font-bold text-white"> | |||
{{ t("support.faq.title") }} | |||
</h1> | |||
<div class="inline-flex items-center px-4 py-2 bg-cyan-400/10 border border-cyan-400/30 rounded-full mb-6"> | |||
<svg class="w-5 h-5 text-cyan-400 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path> | |||
</svg> | |||
<span class="text-cyan-400 font-medium text-sm">FAQ CENTER</span> | |||
</div> | |||
<p class="text-xl text-gray-300 max-w-3xl mx-auto"> | |||
<h1 class="text-4xl md:text-5xl font-bold text-white mb-6"> | |||
{{ t("support.faq.title") }} | |||
</h1> | |||
<p class="text-xl text-gray-300 max-w-4xl mx-auto leading-relaxed mb-8"> | |||
{{ t("support.faq.description") }} | |||
</p> | |||
<!-- 快速统计 --> | |||
<div class="grid grid-cols-3 md:grid-cols-3 gap-6 max-w-2xl mx-auto"> | |||
<div class="text-center"> | |||
<div class="text-2xl font-bold text-cyan-400 mb-1">90%</div> | |||
<div class="text-xs text-gray-400">{{ t("support.faq.coverageRate") }}</div> | |||
</div> | |||
<div class="text-center"> | |||
<div class="text-2xl font-bold text-cyan-400 mb-1">24/7</div> | |||
<div class="text-xs text-gray-400">{{ t("support.faq.availability") }}</div> | |||
</div> | |||
<div class="text-center"> | |||
<div class="text-2xl font-bold text-cyan-400 mb-1">{{ locale === 'zh' ? '即时' : locale === 'en' ? 'Instant' : 'インスタント' }}</div> | |||
<div class="text-xs text-gray-400">{{ locale === 'zh' ? '获取答案' : locale === 'en' ? 'Get Answers' : '回答を取得' }}</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
<!-- FAQ内容区域 --> | |||
<div class="bg-gray-900"> | |||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-16"> | |||
<div class="bg-gray-800 rounded-lg shadow-lg border border-gray-700 overflow-hidden"> | |||
<div class="max-w-full xl:px-8 lg:px-6 md:px-4 px-4"> | |||
<div class="max-w-screen-2xl mx-auto"> | |||
<div class="bg-gradient-to-br from-zinc-900 to-zinc-800/80 rounded-xl p-8 md:p-10"> | |||
<FaqContent /> | |||
</div> | |||
</div> | |||
</div> | |||
<!-- 底部操作区域 --> | |||
<div class="bg-gray-800 border-t border-gray-700"> | |||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8"> | |||
<div class="flex flex-col sm:flex-row items-center justify-between"> | |||
<div class="mb-4 sm:mb-0"> | |||
<p class="text-gray-400 text-sm"> | |||
没有找到您需要的答案?我们的技术支持团队随时为您提供帮助。 | |||
<!-- 底部帮助区域 --> | |||
<div class="max-w-full xl:px-8 lg:px-6 md:px-4 px-4 mt-16"> | |||
<div class="max-w-screen-2xl mx-auto"> | |||
<div class="bg-gradient-to-r from-zinc-900 via-zinc-800/90 to-zinc-900 border border-zinc-700 rounded-xl p-8 md:p-10 text-center relative overflow-hidden"> | |||
<div class="absolute inset-0 bg-grid-pattern opacity-5"></div> | |||
<div class="relative z-10"> | |||
<h2 class="text-2xl md:text-3xl font-bold text-white mb-4"> | |||
{{ locale === 'zh' ? '未找到您需要的答案?' : locale === 'en' ? 'Can\'t find what you\'re looking for?' : 'お探しの回答が見つかりませんか?' }} | |||
</h2> | |||
<p class="text-gray-300 mb-8 max-w-2xl mx-auto text-lg leading-relaxed"> | |||
{{ locale === 'zh' ? '我们的技术支持专家随时准备为您提供个性化的帮助和专业解答。' : locale === 'en' ? 'Our technical support experts are ready to provide personalized help and professional answers.' : '私たちの技術サポート専門家が、個別のヘルプと専門的な回答を提供する準備ができています。' }} | |||
</p> | |||
</div> | |||
<div class="flex flex-col sm:flex-row gap-3"> | |||
<nuxt-link | |||
:to="`${homepagePath}/support`" | |||
class="inline-flex items-center px-4 py-2 border border-gray-600 text-sm font-medium rounded-md text-gray-300 bg-gray-700 hover:bg-gray-600 transition-colors duration-200" | |||
> | |||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"></path> | |||
</svg> | |||
{{ t("support.backToSupport") }} | |||
</nuxt-link> | |||
<nuxt-link | |||
:to="`${homepagePath}/contact`" | |||
class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 transition-colors duration-200" | |||
> | |||
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 4.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"></path> | |||
</svg> | |||
联系技术支持 | |||
</nuxt-link> | |||
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-10"> | |||
<div class="text-center"> | |||
<div class="w-12 h-12 bg-cyan-400/20 rounded-lg flex items-center justify-center mx-auto mb-3"> | |||
<svg class="w-6 h-6 text-cyan-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 4.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"></path> | |||
</svg> | |||
</div> | |||
<h3 class="font-semibold text-white mb-2"> | |||
{{ locale === 'zh' ? '邮件支持' : locale === 'en' ? 'Email Support' : 'メールサポート' }} | |||
</h3> | |||
<p class="text-gray-400 text-sm"> | |||
{{ locale === 'zh' ? '详细问题描述' : locale === 'en' ? 'Detailed problem description' : '詳細な問題の説明' }} | |||
</p> | |||
</div> | |||
<div class="text-center"> | |||
<div class="w-12 h-12 bg-cyan-400/20 rounded-lg flex items-center justify-center mx-auto mb-3"> | |||
<svg class="w-6 h-6 text-cyan-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"></path> | |||
</svg> | |||
</div> | |||
<h3 class="font-semibold text-white mb-2"> | |||
{{ locale === 'zh' ? '在线客服' : locale === 'en' ? 'Live Chat' : 'ライブチャット' }} | |||
</h3> | |||
<p class="text-gray-400 text-sm"> | |||
{{ locale === 'zh' ? '即时沟通解答' : locale === 'en' ? 'Instant communication' : 'インスタントコミュニケーション' }} | |||
</p> | |||
</div> | |||
<div class="text-center"> | |||
<div class="w-12 h-12 bg-cyan-400/20 rounded-lg flex items-center justify-center mx-auto mb-3"> | |||
<svg class="w-6 h-6 text-cyan-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z"></path> | |||
</svg> | |||
</div> | |||
<h3 class="font-semibold text-white mb-2"> | |||
{{ locale === 'zh' ? '电话支持' : locale === 'en' ? 'Phone Support' : '電話サポート' }} | |||
</h3> | |||
<p class="text-gray-400 text-sm"> | |||
{{ locale === 'zh' ? '直接语音交流' : locale === 'en' ? 'Direct voice communication' : '直接音声通信' }} | |||
</p> | |||
</div> | |||
</div> | |||
<div class="flex flex-col sm:flex-row gap-4 justify-center"> | |||
<nuxt-link | |||
:to="`${homepagePath}/contact`" | |||
class="inline-flex items-center px-8 py-4 bg-cyan-400 text-zinc-900 font-semibold rounded-lg hover:bg-cyan-500 transition-colors duration-200" | |||
> | |||
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 8l7.89 4.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"></path> | |||
</svg> | |||
{{ t("support.services.contactSupport") }} | |||
</nuxt-link> | |||
<nuxt-link | |||
:to="`${homepagePath}/support`" | |||
class="inline-flex items-center px-8 py-4 border border-zinc-600 text-white font-semibold rounded-lg hover:bg-zinc-800 hover:border-cyan-400/40 transition-all duration-200" | |||
> | |||
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18.364 5.636l-3.536 3.536m0 5.656l3.536 3.536M9.172 9.172L5.636 5.636m3.536 9.192L5.636 18.364M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-5 0a4 4 0 11-8 0 4 4 0 018 0z"></path> | |||
</svg> | |||
{{ locale === 'zh' ? '浏览支持中心' : locale === 'en' ? 'Browse Support Center' : 'サポートセンターを見る' }} | |||
</nuxt-link> | |||
</div> | |||
</div> | |||
</div> | |||
</div> |