176 lines
5.3 KiB
Vue
176 lines
5.3 KiB
Vue
<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: []
|
||
}
|
||
}
|
||
})
|
||
|
||
newsData.value = await Promise.all(promises)
|
||
|
||
|
||
} 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">
|
||
<!-- 标题区域 -->
|
||
<div class="text-center ">
|
||
<div
|
||
class="text-black text-4xl leading-tight mb-3 wow fadeInDown animated"
|
||
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>
|
||
|
||
|