国外MOSE官网
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

278 lines
8.6 KiB

<script setup lang="ts">
import { useI18n } from 'vue-i18n';
import { ref, computed, onMounted, watch } from 'vue';
import { Icon } from '@iconify/vue';
import { queryQuestionList, type QuestionItem } from '@/api/modules';
// 声明全局变量
declare global {
interface Window {
Typed: any;
}
}
const { t } = useI18n();
// 搜索关键词
const searchQuery = ref('');
// 问题列表
const questions = ref<QuestionItem[]>([]);
const loading = ref(true);
// 监听搜索关键词变化
let searchTimeout: number | null = null;
watch(searchQuery, (newValue: string) => {
// 清除之前的定时器
if (searchTimeout) {
clearTimeout(searchTimeout);
}
// 设置新的定时器,实现防抖
searchTimeout = setTimeout(() => {
loadQuestions();
}, 500) as unknown as number;
});
// 处理搜索按钮点击
const handleSearch = () => {
if (searchTimeout) {
clearTimeout(searchTimeout);
}
loadQuestions();
};
// 加载问题数据
const loadQuestions = async () => {
try {
loading.value = true;
const data = await queryQuestionList({
pageSize: 50,
pageNo: 1,
title: searchQuery.value || undefined // 添加title参数
});
questions.value = data;
} catch (error) {
console.error('加载常见问题数据失败:', error);
} finally {
loading.value = false;
}
};
// 过滤后的问题列表
const filteredQuestions = computed(() => {
let result = questions.value;
// 按搜索关键词过滤
if (searchQuery.value) {
const query = searchQuery.value.toLowerCase();
result = result.filter(q =>
q.question.toLowerCase().includes(query) ||
q.answer.toLowerCase().includes(query)
);
}
return result;
});
// 展开/折叠问题
const expandedQuestions = ref<string[]>([]);
const toggleQuestion = (id: string) => {
const index = expandedQuestions.value.indexOf(id);
if (index === -1) {
expandedQuestions.value.push(id);
} else {
expandedQuestions.value.splice(index, 1);
}
};
const isExpanded = (id: string) => {
return expandedQuestions.value.includes(id);
};
// 打字机效果
const typedElement = ref<HTMLElement | null>(null);
let typed: any = null;
onMounted(() => {
loadQuestions();
// 初始化打字机效果
if (typedElement.value && window.Typed) {
typed = new window.Typed(typedElement.value, {
strings: [
t('faq.hero.subtitle'),
'有任何疑问?我们随时为您解答',
'探索 MOSE 的常见问题',
'快速找到您需要的答案'
],
typeSpeed: 50,
backSpeed: 30,
backDelay: 1500,
loop: true
});
}
});
</script>
<template>
<div class="bg-background min-h-screen">
<!-- Hero Section -->
<section class="relative py-32 px-6 md:px-12 lg:px-24 bg-background-dark overflow-hidden">
<div class="container mx-auto relative z-10">
<div class="max-w-3xl mx-auto text-center">
<h1 class="text-4xl md:text-5xl lg:text-6xl font-bold text-text mb-6 wow animate__animated animate__fadeInDown animate__duration-fast">
{{ t('faq.hero.title') }}
</h1>
<p class="text-lg md:text-xl text-text-secondary mb-8">
<span ref="typedElement"></span>
</p>
</div>
</div>
<!-- Background Decoration -->
<div class="absolute top-0 left-0 w-full h-full overflow-hidden opacity-10">
<div class="absolute -top-24 -left-24 w-64 h-64 rounded-full bg-accent blur-3xl wow animate__animated animate__pulse animate__infinite"></div>
<div class="absolute top-1/2 right-0 w-80 h-80 rounded-full bg-primary blur-3xl wow animate__animated animate__pulse animate__infinite animate__delay-sm"></div>
<div class="absolute -bottom-24 left-1/3 w-72 h-72 rounded-full bg-secondary blur-3xl wow animate__animated animate__pulse animate__infinite animate__delay-md"></div>
</div>
</section>
<!-- Search and Filter Section -->
<section class="py-12 px-6 md:px-12 lg:px-24">
<div class="container mx-auto">
<div class="max-w-3xl mx-auto mb-12">
<div class="relative mb-8 search-container">
<input
type="text"
v-model="searchQuery"
:placeholder="t('faq.search.placeholder')"
class="w-full py-4 px-6 pr-12 bg-background-light text-text rounded-xl focus:outline-none focus:ring-2 focus:ring-primary"
@keyup.enter="handleSearch"
/>
<div
class="absolute right-4 top-1/2 transform -translate-y-1/2 text-text-secondary hover:text-primary transition-colors btn-hover-scale cursor-pointer"
@click="handleSearch"
>
<Icon icon="carbon:search" width="24" height="24" />
</div>
</div>
</div>
<!-- 加载中状态 -->
<div v-if="loading" class="flex justify-center items-center py-16 max-w-3xl mx-auto">
<div class="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-primary"></div>
</div>
<!-- FAQ Questions -->
<div v-else class="max-w-3xl mx-auto">
<div v-if="filteredQuestions.length === 0" class="text-center py-8 text-text-secondary wow animate__animated animate__fadeIn">
{{ t('faq.search.noresults') }}
</div>
<div v-else class="space-y-4">
<div
v-for="question in filteredQuestions"
:key="question.id"
class="bg-background-light rounded-xl overflow-hidden wow animate__animated animate__fadeIn"
>
<button
@click="toggleQuestion(question.id)"
class="w-full px-6 py-4 flex justify-between items-center text-left hover:bg-background-light/80 transition-colors"
>
<h3 class="text-lg font-medium text-text">{{ question.question }}</h3>
<Icon
:icon="isExpanded(question.id) ? 'carbon:chevron-up' : 'carbon:chevron-down'"
class="h-5 w-5 text-text-secondary transition-transform duration-300"
width="20"
height="20"
/>
</button>
<div
v-show="isExpanded(question.id)"
class="px-6 py-4 border-t border-background"
>
<p class="text-text-secondary whitespace-pre-line" v-html="question.answer"></p>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- More Questions Section -->
<section class="py-16 px-6 md:px-12 lg:px-24 bg-background-light">
<div class="container mx-auto">
<div class="max-w-3xl mx-auto text-center">
<h2 class="text-2xl font-bold text-text mb-4">
{{ t('faq.more.title') || '还有更多问题?' }}
</h2>
<div class="flex flex-col sm:flex-row justify-center gap-4 mt-8">
<router-link
to="/contact"
class="px-8 py-3 bg-primary text-text rounded-lg hover:bg-primary-dark transition-colors duration-300 shadow-button btn-hover-glow flex items-center justify-center gap-2"
>
<Icon icon="carbon:email" width="20" height="20" />
{{ t('faq.more.contact') || '联系我们' }}
</router-link>
<router-link
to="/community"
class="px-8 py-3 bg-transparent border border-primary-light text-primary-light rounded-lg hover:bg-primary-light hover:bg-opacity-10 transition-colors duration-300 btn-hover-shadow flex items-center justify-center gap-2"
>
<Icon icon="carbon:group" width="20" height="20" />
{{ t('faq.more.community') || '加入社区' }}
</router-link>
</div>
</div>
</div>
</section>
</div>
</template>
<style scoped>
button {
outline: none;
}
.btn-hover-float {
transition: transform 0.3s ease;
}
.btn-hover-float:hover {
transform: translateY(-3px);
}
.btn-hover-glow {
position: relative;
overflow: hidden;
}
.btn-hover-glow::after {
content: '';
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: radial-gradient(circle, rgba(255,255,255,0.2) 0%, rgba(255,255,255,0) 70%);
opacity: 0;
transform: scale(0.5);
transition: opacity 0.3s ease, transform 0.3s ease;
}
.btn-hover-glow:hover::after {
opacity: 1;
transform: scale(1);
}
.btn-hover-shadow {
transition: box-shadow 0.3s ease;
}
.btn-hover-shadow:hover {
box-shadow: 0 4px 12px rgba(var(--primary-light-rgb), 0.15);
}
</style>