Переглянути джерело

feat: 更新FAQ组件和国际化内容

- 修改FAQ组件,优化加载动画和搜索功能,提升用户体验。
- 增加搜索框和分类选择功能,支持更灵活的FAQ查询。
- 更新国际化文件,添加多语言支持的搜索相关内容,确保用户在不同语言下的使用体验一致。
- 调整样式,提升页面视觉效果,确保在不同设备上的适配性。
master
lizhuang 1 тиждень тому
джерело
коміт
228f39d487
6 змінених файлів з 1275 додано та 482 видалено
  1. 227
    175
      components/FaqContent.vue
  2. 59
    2
      i18n/locales/en.ts
  3. 59
    2
      i18n/locales/ja.ts
  4. 59
    2
      i18n/locales/zh.ts
  5. 126
    68
      pages/support/faq.vue
  6. 745
    233
      pages/support/index.vue

+ 227
- 175
components/FaqContent.vue Переглянути файл

@@ -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>

+ 59
- 2
i18n/locales/en.ts Переглянути файл

@@ -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",

+ 59
- 2
i18n/locales/ja.ts Переглянути файл

@@ -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",

+ 59
- 2
i18n/locales/zh.ts Переглянути файл

@@ -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: "关于我们",

+ 126
- 68
pages/support/faq.vue Переглянути файл

@@ -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>

+ 745
- 233
pages/support/index.vue
Різницю між файлами не показано, бо вона завелика
Переглянути файл


Завантаження…
Відмінити
Зберегти