2025-10-31 16:27:10 +08:00
|
|
|
|
<script lang="ts" setup>
|
|
|
|
|
|
import { ref, onMounted } from 'vue'
|
|
|
|
|
|
import { getArticleList, type Article } from '~/config/api'
|
|
|
|
|
|
|
|
|
|
|
|
// 类型定义
|
|
|
|
|
|
interface NewsCategory {
|
|
|
|
|
|
name: string
|
|
|
|
|
|
code: string
|
|
|
|
|
|
articleList: Article[]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 响应式数据
|
|
|
|
|
|
const newsData = ref<NewsCategory[]>([])
|
|
|
|
|
|
const loading = ref(false)
|
|
|
|
|
|
const error = ref('')
|
|
|
|
|
|
|
|
|
|
|
|
// 文章类型配置
|
|
|
|
|
|
const articleTypes = [
|
|
|
|
|
|
{ name: '解决方案', code: 'solution' },
|
|
|
|
|
|
{ name: '开发知识', code: 'developKnowledge' },
|
|
|
|
|
|
{ name: '行业动态', code: 'industryTrend' }
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
// 格式化时间
|
|
|
|
|
|
const formatTime = (timeString: string): string => {
|
|
|
|
|
|
if (!timeString) return ''
|
|
|
|
|
|
const date = new Date(timeString)
|
|
|
|
|
|
if (isNaN(date.getTime())) return timeString
|
|
|
|
|
|
|
|
|
|
|
|
const Y = date.getFullYear()
|
|
|
|
|
|
const M = String(date.getMonth() + 1).padStart(2, '0')
|
|
|
|
|
|
const D = String(date.getDate()).padStart(2, '0')
|
|
|
|
|
|
const h = String(date.getHours()).padStart(2, '0')
|
|
|
|
|
|
const m = String(date.getMinutes()).padStart(2, '0')
|
|
|
|
|
|
return `${Y}-${M}-${D} ${h}:${m}`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取文章链接
|
|
|
|
|
|
const getArticleLink = (articleId: string): string => {
|
|
|
|
|
|
return `/article/${articleId}`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取显示的文章列表(每个分类显示前2篇文章)
|
|
|
|
|
|
const getDisplayArticles = (articles: Article[]): Article[] => {
|
|
|
|
|
|
return articles.slice(0, 2)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取新闻数据
|
|
|
|
|
|
const fetchNewsData = async (): Promise<void> => {
|
|
|
|
|
|
loading.value = true
|
|
|
|
|
|
error.value = ''
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 并发获取所有类型的文章数据
|
|
|
|
|
|
const promises = articleTypes.map(async (type) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const response = await getArticleList({
|
|
|
|
|
|
code: type.code,
|
|
|
|
|
|
pageNum: 1,
|
|
|
|
|
|
pageSize: 10
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
name: type.name,
|
|
|
|
|
|
code: type.code,
|
|
|
|
|
|
articleList: response.rows || []
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (err) {
|
|
|
|
|
|
console.error(`获取 ${type.name} 文章失败:`, err)
|
|
|
|
|
|
return {
|
|
|
|
|
|
name: type.name,
|
|
|
|
|
|
code: type.code,
|
|
|
|
|
|
articleList: []
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
|
2025-10-31 17:09:26 +08:00
|
|
|
|
newsData.value = await Promise.all(promises)
|
|
|
|
|
|
|
2025-10-31 16:27:10 +08:00
|
|
|
|
|
|
|
|
|
|
} catch (err) {
|
|
|
|
|
|
error.value = err instanceof Error ? err.message : '获取新闻数据失败'
|
|
|
|
|
|
console.error('获取新闻数据时发生错误:', err)
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
loading.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
|
fetchNewsData()
|
|
|
|
|
|
})
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
|
<!--box3 新闻-->
|
|
|
|
|
|
<div class="container mx-auto px-4">
|
|
|
|
|
|
<!-- 标题区域 -->
|
2025-10-31 17:09:26 +08:00
|
|
|
|
<div class="text-center ">
|
2025-10-31 16:27:10 +08:00
|
|
|
|
<div
|
2025-10-31 17:09:26 +08:00
|
|
|
|
class="text-black text-4xl leading-tight mb-3 wow fadeInDown animated"
|
2025-10-31 16:27:10 +08:00
|
|
|
|
style="visibility: visible; animation-name: fadeInDown;">
|
|
|
|
|
|
开发资讯
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="text-gray-600 text-lg wow fadeInUp animated" style="visibility: visible; animation-name: fadeInUp;">
|
|
|
|
|
|
NEWS
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="py-5"/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 加载状态 -->
|
|
|
|
|
|
<div v-if="loading" class="text-center py-12">
|
|
|
|
|
|
<div class="text-gray-600 text-lg animate-pulse">正在加载新闻...</div>
|
|
|
|
|
|
<div class="py-12"/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 错误状态 -->
|
|
|
|
|
|
<div v-else-if="error" class="text-center py-12">
|
|
|
|
|
|
<div class="text-red-600 text-lg mb-5">加载失败: {{ error }}</div>
|
|
|
|
|
|
<button
|
|
|
|
|
|
class="inline-block px-5 py-2.5 bg-blue-600 hover:bg-blue-700 text-white text-base font-normal rounded transition-all duration-150 ease-in-out cursor-pointer border border-transparent"
|
|
|
|
|
|
@click="fetchNewsData">
|
|
|
|
|
|
重新加载
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<div class="py-12"/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 新闻分类列表 -->
|
|
|
|
|
|
<div v-else class="grid grid-cols-1 md:grid-cols-3 gap-6 lg:gap-8">
|
|
|
|
|
|
<div
|
|
|
|
|
|
v-for="(category, index) in newsData"
|
|
|
|
|
|
:key="category.name"
|
|
|
|
|
|
:data-wow-delay="`${300 + index * 200}ms`"
|
|
|
|
|
|
:style="`visibility: visible; animation-delay: ${300 + index * 200}ms; animation-name: fadeInDown;`"
|
|
|
|
|
|
class="wow fadeInDown animated active mb-8 lg:mb-12">
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 分类标题 -->
|
|
|
|
|
|
<div :title="category.name" class="text-xl lg:text-2xl text-blue-600 text-center cursor-pointer mb-5">
|
|
|
|
|
|
{{ category.name }}
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<hr class="border-gray-300 mb-5"/>
|
|
|
|
|
|
<div class="py-5"/>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 文章列表 -->
|
|
|
|
|
|
<template v-if="category.articleList.length > 0">
|
|
|
|
|
|
<ul class="list-none p-0 m-0">
|
|
|
|
|
|
<li v-for="article in getDisplayArticles(category.articleList)" :key="article.id" class="mb-2.5">
|
|
|
|
|
|
<a
|
|
|
|
|
|
:href="getArticleLink(article.id)"
|
|
|
|
|
|
:title="article.title"
|
|
|
|
|
|
class="text-gray-600 text-sm block py-1.5 no-underline hover:underline transition-all"
|
|
|
|
|
|
target="_blank">
|
|
|
|
|
|
<div class="flex items-center justify-between gap-3">
|
|
|
|
|
|
<span class="truncate flex-1 pr-2">{{ article.title }}</span>
|
|
|
|
|
|
<span class="text-gray-500 text-xs whitespace-nowrap flex-shrink-0">{{ formatTime(article.createTime) }}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="py-5"/>
|
|
|
|
|
|
</a>
|
|
|
|
|
|
</li>
|
|
|
|
|
|
</ul>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<div v-else class="text-center text-gray-600 text-sm py-5">
|
|
|
|
|
|
暂无文章
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 空状态 -->
|
|
|
|
|
|
<div v-if="!loading && !error && newsData.length === 0" class="text-center py-12">
|
|
|
|
|
|
<div class="text-gray-600 text-lg">暂无新闻数据</div>
|
|
|
|
|
|
<div class="py-12"/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
|