|
|
- <script setup lang="ts">
- import { useI18n } from 'vue-i18n';
- import { ref, onMounted, computed } from 'vue';
- import { Icon } from '@iconify/vue';
- import { useConfig } from '@/utils/config';
- import { Swiper, SwiperSlide } from 'swiper/vue';
- import { Navigation, Pagination } from 'swiper/modules';
- import 'swiper/css';
- import 'swiper/css/navigation';
- import 'swiper/css/pagination';
- import {
- queryOfficialMediaList,
- queryForumList,
- queryCommentsList,
- queryMessageList,
- queryCommunityList,
- addComments,
- type OfficialMediaItem,
- type ForumItem,
- type CommentItem,
- type MessageItem,
- type CommunityItem
- } from '@/api/modules';
-
- const { getConfigImage } = useConfig();
-
- const { t } = useI18n();
-
- // 社交媒体账号数据
- const socialMediaAccounts = ref<OfficialMediaItem[]>([]);
- const socialMediaLoading = ref(true);
-
- // 论坛信息数据
- const forumList = ref<ForumItem[]>([]);
- const forumLoading = ref(true);
-
- // 评论信息数据
- const commentsMap = ref<Record<string, CommentItem[]>>({});
- const commentsLoading = ref(true);
-
- // 信息公示数据
- const messageList = ref<MessageItem[]>([]);
- const messageLoading = ref(true);
-
- // 社区活动数据
- const communityList = ref<CommunityItem[]>([]);
- const communityLoading = ref(true);
-
- // 新留言
- const newMessage = ref({
- forumId: null as string | null,
- content: '',
- createBy: ''
- });
-
- // 是否正在提交
- const isSubmitting = ref(false);
-
- // 当前查看的帖子ID
- const currentForumId = ref<string | null>(null);
-
- // Swiper实例
- const swiperInstance = ref(null);
-
- // Swiper配置
- const swiperOptions = {
- slidesPerView: 3,
- spaceBetween: 40,
- navigation: false,
- pagination: false,
- modules: [Navigation, Pagination],
- breakpoints: {
- 320: {
- slidesPerView: 1,
- spaceBetween: 20
- },
- 768: {
- slidesPerView: 2,
- spaceBetween: 30
- },
- 1024: {
- slidesPerView: 3,
- spaceBetween: 40
- }
- }
- };
-
- // 弹窗相关数据
- const showDetailModal = ref(false);
- const selectedMessage = ref<MessageItem | null>(null);
-
- // 显示消息详情弹窗
- const showMessageDetail = (message: MessageItem) => {
- selectedMessage.value = message;
- showDetailModal.value = true;
- };
-
- // 关闭消息详情弹窗
- const closeMessageDetail = () => {
- showDetailModal.value = false;
- selectedMessage.value = null;
- };
-
- // 跳转到公告详情
- const goToMessage = (id: string) => {
- console.log('查看公告详情:', id);
- // 跳转到公告详情页面实现
- };
-
- // 格式化日期
- const formatDate = (dateString: string) => {
- if (!dateString) return '';
-
- const date = new Date(dateString);
- return new Intl.DateTimeFormat('zh-CN', {
- year: 'numeric',
- month: 'long',
- day: 'numeric'
- }).format(date);
- };
-
- // 加载社交媒体数据
- const loadSocialMedia = async () => {
- try {
- socialMediaLoading.value = true;
- const data = await queryOfficialMediaList({
- pageSize: 10,
- pageNo: 1
- });
- socialMediaAccounts.value = data;
- } catch (error) {
- console.error('加载社交媒体数据失败:', error);
- } finally {
- socialMediaLoading.value = false;
- }
- };
-
- // 加载论坛数据
- const loadForum = async () => {
- try {
- forumLoading.value = true;
- const data = await queryForumList({
- pageSize: 10,
- pageNo: 1
- });
- forumList.value = data;
-
- // 为每个论坛加载对应的评论
- await Promise.all(data.map(forum => loadComments(forum.id)));
- } catch (error) {
- console.error('加载论坛数据失败:', error);
- } finally {
- forumLoading.value = false;
- }
- };
-
- // 加载评论数据
- const loadComments = async (forumId: string) => {
- try {
- commentsLoading.value = true;
- const data = await queryCommentsList({
- forumId
- });
- commentsMap.value[forumId] = data;
- } catch (error) {
- console.error(`加载论坛${forumId}的评论数据失败:`, error);
- } finally {
- commentsLoading.value = false;
- }
- };
-
- // 加载信息公示数据
- const loadMessages = async () => {
- try {
- messageLoading.value = true;
- const data = await queryMessageList({
- pageSize: 10,
- pageNo: 1
- });
- messageList.value = data;
- } catch (error) {
- console.error('加载信息公示数据失败:', error);
- } finally {
- messageLoading.value = false;
- }
- };
-
- // 加载社区数据
- const loadCommunity = async () => {
- try {
- communityLoading.value = true;
- const data = await queryCommunityList({
- pageSize: 6,
- pageNo: 1
- });
- communityList.value = data;
- } catch (error) {
- console.error('加载社区数据失败:', error);
- } finally {
- communityLoading.value = false;
- }
- };
-
- // 提交留言
- const submitComment = async (forumId: string) => {
- if (newMessage.value.content) {
- isSubmitting.value = true;
- try {
- await addComments({
- forumId: forumId,
- content: newMessage.value.content
- });
-
- // 重新加载评论
- await loadComments(forumId);
-
- // 重置表单
- newMessage.value.createBy = '';
- newMessage.value.content = '';
- newMessage.value.forumId = null;
- } catch (error) {
- console.error('提交评论失败:', error);
- } finally {
- isSubmitting.value = false;
- }
- }
- };
-
- // 准备留言
- const prepareComment = (forumId: string) => {
- // 记录当前滚动位置
- const scrollPosition = window.scrollY;
-
- // 设置当前查看的帖子ID
- currentForumId.value = forumId;
- newMessage.value.forumId = forumId;
-
- // 使用setTimeout确保DOM更新后再恢复滚动位置
- setTimeout(() => {
- window.scrollTo({
- top: scrollPosition,
- behavior: 'auto'
- });
- }, 0);
- };
-
- // 关闭评论
- const closeComment = () => {
- // 记录当前滚动位置
- const scrollPosition = window.scrollY;
-
- // 关闭评论区
- currentForumId.value = null;
-
- // 使用setTimeout确保DOM更新后再恢复滚动位置
- setTimeout(() => {
- window.scrollTo({
- top: scrollPosition,
- behavior: 'auto'
- });
- }, 0);
- };
-
- // 获取特定论坛的评论
- const getComments = (forumId: string): CommentItem[] => {
- return commentsMap.value[forumId] || [];
- };
-
- // 计算帖子发布时间
- const getTimeAgo = (timestamp: string) => {
- const now = new Date();
- const postTime = new Date(timestamp);
- const diffTime = Math.abs(now.getTime() - postTime.getTime());
- const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));
-
- if (diffDays < 1) {
- return t('community.time.today');
- } else if (diffDays === 1) {
- return t('community.time.yesterday');
- } else if (diffDays < 7) {
- return t('community.time.days_ago', { days: diffDays });
- } else {
- return new Date(timestamp).toLocaleDateString();
- }
- };
-
- // 根据平台名称获取图标
- const getSocialIcon = (title: string) => {
- const lowerTitle = title.toLowerCase();
- if (lowerTitle.includes('twitter') || lowerTitle.includes('x')) {
- return 'mdi:twitter';
- } else if (lowerTitle.includes('telegram')) {
- return 'mdi:telegram';
- } else if (lowerTitle.includes('discord')) {
- return 'mdi:discord';
- } else if (lowerTitle.includes('medium')) {
- return 'mdi:medium';
- } else if (lowerTitle.includes('github')) {
- return 'mdi:github';
- } else if (lowerTitle.includes('reddit')) {
- return 'mdi:reddit';
- } else if (lowerTitle.includes('facebook')) {
- return 'mdi:facebook';
- } else if (lowerTitle.includes('instagram')) {
- return 'mdi:instagram';
- } else {
- return 'mdi:web';
- }
- };
-
- onMounted(async() => {
- await Promise.all([
- loadSocialMedia(),
- loadForum(),
- loadMessages(),
- loadCommunity()
- ]);
- });
- </script>
-
- <template>
- <div class="bg-background min-h-screen">
- <!-- Hero Section -->
- <section class="relative py-24 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('community.hero.title') }}
- </h1>
- <p class="text-lg md:text-xl text-text-secondary mb-8 wow animate__animated animate__fadeIn animate__delay-xs animate__duration-fast">
- {{ t('community.hero.subtitle') }}
- </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-primary-light 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-secondary 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-accent blur-3xl wow animate__animated animate__pulse animate__infinite animate__delay-md"></div>
- </div>
- </section>
-
- <!-- 官方公告 Section -->
- <section class="py-16 px-6 md:px-12 lg:px-24" :style="{ backgroundImage: `url(${getConfigImage('com_mes_bg')})` }">
- <div class="container mx-auto">
- <h2 class="text-2xl md:text-3xl font-bold text-text mb-8 text-center wow animate__animated animate__fadeInUp animate__duration-fast">
- 官方公告
- </h2>
-
- <div v-if="messageLoading" class="flex justify-center py-10">
- <div class="animate-spin rounded-full h-10 w-10 border-t-2 border-b-2 border-primary"></div>
- </div>
-
- <div v-else class="space-y-6">
- <div
- v-for="message in messageList"
- :key="message.id"
- class="flex bg-background rounded-xl overflow-hidden shadow-card hover:shadow-lg transition-all duration-300 wow animate__animated animate__fadeInUp"
- >
- <!-- 公告图片 -->
- <div class="w-1/4">
- <img :src="message.image || '/LOGO.png'" :alt="message.title" class="w-full h-full object-cover" />
- </div>
-
- <!-- 公告内容 -->
- <div class="w-3/4 p-6">
- <h3
- class="text-xl font-bold text-text mb-3 line-clamp-2 hover:text-primary hover:bg-primary/10 px-2 py-1 rounded transition-all duration-300 cursor-pointer hover:underline transform hover:scale-105"
- @click="showMessageDetail(message)"
- >
- {{ message.title }}
- </h3>
- <p class="text-text-secondary text-base mb-4 line-clamp-3">{{ message.description }}</p>
- <div class="flex items-center text-text-secondary text-sm">
- <Icon icon="carbon:time" class="mr-2" />
- <span>{{ formatDate(message.createTime) }}</span>
- </div>
- </div>
- </div>
- </div>
-
- <!-- 消息详情弹窗 -->
- <div
- v-if="showDetailModal"
- class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4"
- @click="closeMessageDetail"
- >
- <div
- class="bg-background rounded-xl max-w-2xl w-full max-h-[80vh] overflow-y-auto"
- @click.stop
- >
- <!-- 弹窗头部 -->
- <div class="p-6 border-b border-border">
- <div class="flex justify-between items-start">
- <h3 class="text-2xl font-bold text-text">{{ selectedMessage?.title }}</h3>
- <button
- @click="closeMessageDetail"
- class="text-text-secondary hover:text-text transition-colors duration-300"
- >
- <Icon icon="carbon:close" class="h-6 w-6" />
- </button>
- </div>
- <div class="flex items-center text-text-secondary text-sm mt-2">
- <Icon icon="carbon:time" class="mr-2" />
- <span>{{ formatDate(selectedMessage?.createTime) }}</span>
- </div>
- </div>
-
- <!-- 弹窗内容 -->
- <div class="p-6">
- <div v-if="selectedMessage?.image" class="mb-6">
- <img
- :src="selectedMessage.image"
- :alt="selectedMessage.title"
- class="w-full h-64 object-cover rounded-lg"
- />
- </div>
- <div class="prose prose-lg max-w-none">
- <div v-html="selectedMessage?.description"></div>
- </div>
- </div>
- </div>
- </div>
- </div>
- </section>
-
- <!-- 社区风采 Section (原社区亮点) -->
- <section class="py-16 px-6 md:px-12 lg:px-24 bg-background-light" :style="{ backgroundImage: `url(${getConfigImage('com_show_bg')})` }">
- <div class="container mx-auto">
- <h2 class="text-2xl md:text-3xl font-bold text-text mb-8 text-center wow animate__animated animate__fadeInUp animate__duration-fast">
- 社区风采
- </h2>
-
- <div v-if="communityLoading" class="flex justify-center py-10">
- <div class="animate-spin rounded-full h-10 w-10 border-t-2 border-b-2 border-primary"></div>
- </div>
-
- <div v-else class="relative">
- <div class="relative">
- <Swiper
- :slides-per-view="swiperOptions.slidesPerView"
- :space-between="swiperOptions.spaceBetween"
- :navigation="swiperOptions.navigation"
- :pagination="false"
- :modules="swiperOptions.modules"
- :breakpoints="swiperOptions.breakpoints"
- class="community-swiper"
- @swiper="swiperInstance = $event"
- >
- <SwiperSlide v-for="community in communityList" :key="community.id">
- <div class="bg-background rounded-2xl overflow-hidden shadow-xl hover:shadow-2xl transition-all duration-500 h-full transform hover:scale-105">
- <div class="flex flex-col h-full">
- <div class="relative overflow-hidden">
- <img
- :src="community.image || '/LOGO.png'"
- :alt="community.title"
- class="w-full h-56 object-cover transition-transform duration-700 hover:scale-110"
- />
- <div class="absolute inset-0 bg-gradient-to-t from-black/20 to-transparent"></div>
- </div>
- <div class="p-8 flex-1 flex flex-col">
- <h3 class="text-2xl font-bold text-text mb-4">{{ community.title }}</h3>
- <p class="text-text-secondary mb-6 flex-1 leading-relaxed">{{ community.description }}</p>
- </div>
- </div>
- </div>
- </SwiperSlide>
- </Swiper>
-
- <!-- 外部自定义导航按钮 -->
- <button
- @click="swiperInstance?.slidePrev()"
- class="absolute left-4 top-1/2 transform -translate-y-1/2 w-12 h-12 bg-white/90 hover:bg-white rounded-full shadow-lg flex items-center justify-center transition-all duration-300 z-10 group"
- >
- <Icon icon="carbon:chevron-left" class="h-6 w-6 text-primary group-hover:scale-110 transition-transform duration-300" />
- </button>
-
- <button
- @click="swiperInstance?.slideNext()"
- class="absolute right-4 top-1/2 transform -translate-y-1/2 w-12 h-12 bg-white/90 hover:bg-white rounded-full shadow-lg flex items-center justify-center transition-all duration-300 z-10 group"
- >
- <Icon icon="carbon:chevron-right" class="h-6 w-6 text-primary group-hover:scale-110 transition-transform duration-300" />
- </button>
- </div>
-
- <!-- 外部自定义分页器 -->
- <div class="flex justify-center mt-8 gap-4">
- <button
- v-for="(community, index) in communityList"
- :key="index"
- @click="swiperInstance?.slideTo(index)"
- class="w-5 h-5 rounded-full transition-all duration-300 border-2 cursor-pointer"
- :class="swiperInstance?.activeIndex === index
- ? 'bg-primary border-primary scale-125 shadow-lg'
- : 'bg-white/50 border-white/70 hover:bg-white/80'"
- ></button>
- </div>
- </div>
- </div>
- </section>
-
- <!-- 社交媒体账号 Section -->
- <section class="py-16 px-6 md:px-12 lg:px-24" :style="{ backgroundImage: `url(${getConfigImage('com_media_bg')})` }">
- <div class="container mx-auto">
- <h2 class="text-2xl md:text-3xl font-bold text-text mb-8 text-center wow animate__animated animate__fadeInUp animate__duration-fast">
- {{ t('community.social_media.title') }}
- </h2>
-
- <div v-if="socialMediaLoading" class="flex justify-center py-10">
- <div class="animate-spin rounded-full h-10 w-10 border-t-2 border-b-2 border-primary"></div>
- </div>
-
- <div v-else-if="socialMediaAccounts.length === 0" class="text-center py-10 text-text-secondary">
- {{ t('community.social_media.no_accounts') }}
- </div>
-
- <div v-else class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
- <div
- v-for="account in socialMediaAccounts"
- :key="account.id"
- class="bg-background rounded-xl p-6 shadow-card hover:shadow-lg transition-all duration-300 wow animate__animated animate__fadeInUp"
- >
- <div class="flex items-center mb-4">
- <div class="w-12 h-12 rounded-full bg-primary bg-opacity-10 flex items-center justify-center mr-4">
- <Icon :icon="getSocialIcon(account.title)" class="h-6 w-6 text-primary" />
- </div>
- <div>
- <h3 class="text-lg font-bold text-text">{{ account.title }}</h3>
- <p class="text-text-secondary text-sm">{{ account.username }}</p>
- </div>
- </div>
-
- <p class="text-text-secondary mb-4" v-html="account.description"></p>
-
- <a
- :href="account.url"
- target="_blank"
- rel="noopener noreferrer"
- class="inline-flex items-center text-primary-light hover:text-primary-dark transition-colors"
- >
- <span>{{ t('community.social_media.follow_us') }}</span>
- <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 ml-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
- </svg>
- </a>
- </div>
- </div>
- </div>
- </section>
-
- <!-- 社区论坛 Section -->
- <section class="py-16 px-6 md:px-12 lg:px-24 bg-background-light" :style="{ backgroundImage: `url(${getConfigImage('com_forum_bg')})` }" >
- <div class="container mx-auto">
- <h2 class="text-2xl md:text-3xl font-bold text-text mb-8 text-center wow animate__animated animate__fadeInUp animate__duration-fast">
- {{ t('community.forum.title') }}
- </h2>
-
- <div v-if="forumLoading" class="flex justify-center py-10">
- <div class="animate-spin rounded-full h-10 w-10 border-t-2 border-b-2 border-primary"></div>
- </div>
-
- <div v-else-if="forumList.length === 0" class="text-center py-10 text-text-secondary">
- {{ t('community.forum.no_topics') }}
- </div>
-
- <div v-else class="space-y-6">
- <div
- v-for="forum in forumList"
- :key="forum.id"
- class="bg-background rounded-xl shadow-card overflow-hidden wow animate__animated animate__fadeInUp"
- >
- <!-- 帖子内容 -->
- <div class="p-6">
- <div class="flex items-center mb-4">
- <div class="w-10 h-10 rounded-full bg-primary bg-opacity-10 flex items-center justify-center mr-3">
- <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-primary" fill="none" viewBox="0 0 24 24" stroke="currentColor">
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 8h10M7 12h4m1 8l-4-4H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-3l-4 4z" />
- </svg>
- </div>
- <div>
- <h3 class="text-lg font-bold text-text">{{ forum.title }}</h3>
- <div class="flex items-center text-text-secondary text-sm">
- <span>{{ forum.createBy }}</span>
- <span class="mx-2">•</span>
- <span>{{ formatDate(forum.createTime) }}</span>
- </div>
- </div>
- </div>
-
- <div class="mb-4">
- <p class="text-text-secondary" v-html="forum.content"></p>
- </div>
-
- <div class="flex justify-between items-center">
- <div class="flex items-center space-x-4">
- <button class="flex items-center text-text-secondary hover:text-primary transition-colors">
- <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
- <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-10V5a2 2 0 00-2-2h-.095c-.5 0-.905.405-.905.905 0 .714-.211 1.412-.608 2.006L7 11v9m7-10h-2M7 20H5a2 2 0 01-2-2v-6a2 2 0 012-2h2.5" />
- </svg>
- <span>{{ forum.likeCount || 0 }}</span>
- </button>
-
- <button class="flex items-center text-text-secondary hover:text-primary transition-colors" @click="prepareComment(forum.id)">
- <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 8h10M7 12h4m1 8l-4-4H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-3l-4 4z" />
- </svg>
- <span>{{ getComments(forum.id).length }}</span>
- </button>
- </div>
-
- <button class="flex items-center text-primary-light hover:text-primary-dark transition-colors" @click="prepareComment(forum.id)">
- <span>{{ t('community.forum.add_comment') }}</span>
- <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 ml-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 10h10a8 8 0 018 8v2M3 10l6 6m-6-6l6-6" />
- </svg>
- </button>
- </div>
- </div>
-
- <!-- 评论区 -->
- <div v-if="currentForumId === forum.id">
- <div class="border-t border-background-light">
- <div class="p-6">
- <h4 class="text-lg font-bold text-text mb-4 flex items-center justify-between">
- <span>{{ t('community.forum.view_comments') }}</span>
- <button class="text-text-secondary hover:text-primary transition-colors" @click="closeComment">
- <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
- </svg>
- </button>
- </h4>
-
- <div v-if="commentsLoading" class="flex justify-center py-4">
- <div class="animate-spin rounded-full h-6 w-6 border-t-2 border-b-2 border-primary"></div>
- </div>
-
- <div v-else-if="getComments(forum.id).length === 0" class="text-center py-4 text-text-secondary">
- {{ t('community.forum.no_comments') }}
- </div>
-
- <div v-else class="space-y-4 mb-6">
- <div
- v-for="comment in getComments(forum.id)"
- :key="comment.id"
- class="bg-background-dark rounded-lg p-4"
- >
- <div class="flex items-center mb-2">
- <div class="w-8 h-8 rounded-full bg-primary bg-opacity-10 flex items-center justify-center mr-2">
- <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 text-primary" fill="none" viewBox="0 0 24 24" stroke="currentColor">
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
- </svg>
- </div>
- <div>
- <h5 class="text-sm font-medium text-text">{{ comment.createBy || t('community.forum.username') }}</h5>
- <p class="text-xs text-text-secondary">{{ formatDate(comment.createTime) }}</p>
- </div>
- </div>
-
- <p class="text-text-secondary" v-html="comment.content"></p>
- </div>
- </div>
-
- <!-- 评论表单 -->
- <div class="bg-background-dark rounded-lg p-4">
- <textarea
- v-model="newMessage.content"
- :placeholder="t('community.forum.comment')"
- class="w-full bg-background border border-background-light rounded-lg p-3 text-text-secondary resize-none focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent"
- rows="3"
- ></textarea>
-
- <div class="flex justify-end mt-3">
- <button
- @click="submitComment(forum.id)"
- :disabled="!newMessage.content || isSubmitting"
- class="px-4 py-2 bg-primary text-white rounded-lg hover:bg-primary-dark transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
- >
- <span v-if="isSubmitting">
- <svg class="animate-spin -ml-1 mr-2 h-4 w-4 text-white inline-block" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
- <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
- <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
- </svg>
- {{ t('community.forum.submitting') }}
- </span>
- <span v-else>{{ t('community.forum.submit') }}</span>
- </button>
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
- </section>
-
- <!-- 3. 信息公示 Section -->
- <!-- <section class="py-16 px-6 md:px-12 lg:px-24" :style="{ backgroundImage: `url(${getConfigImage('com_mes_bg')})` }">
- <div class="container mx-auto">
- <h2 class="text-3xl font-bold text-text mb-4 text-center wow animate__animated animate__fadeInUp">
- {{ t('community.announcements.title') }}
- </h2>
-
- <div v-if="messageLoading" class="flex justify-center items-center py-12">
- <div class="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-primary"></div>
- </div>
-
- <div v-else-if="messageList.length === 0" class="text-center py-10">
- <Icon icon="carbon:no-content" class="mx-auto mb-4" width="48" height="48" />
- <p class="text-text-secondary">暂无信息公示数据</p>
- </div>
-
- <div v-else class="grid grid-cols-1 md:grid-cols-2 gap-6 max-w-4xl mx-auto mt-8">
- <div
- v-for="message in messageList"
- :key="message.id"
- class="bg-background-light rounded-xl p-6 shadow-card hover:shadow-lg transition-all duration-300"
- >
- <div class="flex items-start gap-4">
- <div v-if="message.image" class="w-16 h-16 rounded-lg overflow-hidden flex-shrink-0">
- <img :src="message.image" :alt="message.title" class="w-full h-full object-cover" />
- </div>
- <div v-else class="w-16 h-16 rounded-lg bg-primary bg-opacity-10 flex items-center justify-center flex-shrink-0">
- <Icon icon="carbon:notification" width="32" height="32" class="text-primary" />
- </div>
-
- <div class="flex-1">
- <h3 class="text-lg font-bold text-text mb-2">{{ message.title }}</h3>
- <p class="text-sm text-text-secondary mb-2" v-html="message.content"></p>
- <div class="text-xs text-text-secondary">{{ formatDate(message.createTime || '') }}</div>
- </div>
- </div>
- </div>
- </div>
- </div>
- </section> -->
-
- <section class="py-16 px-6 md:px-12 lg:px-24 bg-background-light" :style="{ backgroundImage: `url(${getConfigImage('com_show_bg')})` }">
- <div class="container mx-auto">
- <h2 class="text-3xl font-bold text-text mb-4 text-center wow animate__animated animate__fadeInUp">
- {{ t('community.highlights.title') }}
- </h2>
-
- <div v-if="communityLoading" class="flex justify-center items-center py-12">
- <div class="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-primary"></div>
- </div>
-
- <div v-else-if="communityList.length === 0" class="text-center py-10">
- <Icon icon="carbon:no-content" class="mx-auto mb-4" width="48" height="48" />
- <p class="text-text-secondary">暂无社区活动数据</p>
- </div>
-
- <div v-else class="grid grid-cols-1 md:grid-cols-3 gap-6 mt-8">
- <div
- v-for="community in communityList"
- :key="community.id"
- class="bg-background rounded-xl overflow-hidden shadow-card hover:shadow-lg transition-all duration-300"
- >
- <div v-if="community.image" class="h-48 overflow-hidden">
- <img :src="community.image" :alt="community.title" class="w-full h-full object-cover hover:scale-105 transition-transform duration-300">
- </div>
- <div v-else class="h-48 bg-primary bg-opacity-10 flex items-center justify-center">
- <Icon icon="carbon:events" width="64" height="64" class="text-primary opacity-50" />
- </div>
- <div class="p-6">
- <h4 class="text-lg font-bold text-text mb-2">{{ community.title }}</h4>
- <p class="text-text-secondary text-sm mb-2" v-html="community.content"></p>
- <p class="text-text-secondary text-xs">{{ formatDate(community.createTime || '') }}</p>
- </div>
- </div>
- </div>
- </div>
- </section>
- </div>
- </template>
-
- <style scoped>
- .shadow-card {
- box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
- transition: transform 0.3s ease, box-shadow 0.3s ease;
- }
-
- .shadow-card:hover {
- box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
- }
-
- .line-clamp-2 {
- display: -webkit-box;
- -webkit-line-clamp: 2;
- -webkit-box-orient: vertical;
- overflow: hidden;
- }
-
- /* Swiper分页器样式 */
- :deep(.swiper-pagination-bullet) {
- width: 12px;
- height: 12px;
- background: rgba(255, 255, 255, 0.5);
- opacity: 1;
- border: 2px solid rgba(255, 255, 255, 0.8);
- transition: all 0.3s ease;
- }
-
- :deep(.swiper-pagination-bullet-active) {
- background: var(--color-primary);
- border-color: var(--color-primary);
- transform: scale(1.3);
- box-shadow: 0 0 10px rgba(var(--color-primary-rgb), 0.5);
- }
-
- :deep(.swiper-pagination) {
- position: relative;
- bottom: 0;
- margin-top: 20px;
- }
- </style>
|