ct/app/components/DevelopNews.vue
2025-11-18 09:25:36 +08:00

283 lines
6.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script lang="ts" setup>
import {ref, onMounted} from 'vue'
import {useArticleApi} from '~/composables/useArticleApi'
// 类型定义
interface Article {
id: string
title: string
brief: string | null
content: string | null
createTime: string
code: string
status: string | null
}
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 => {
const date = new Date(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 articles = await useArticleApi().getArticles({
code: type.code,
pageNum: 1,
pageSize: 10
})
return {
name: type.name,
code: type.code,
articleList: articles || []
}
} catch (err) {
console.error(`Error fetching ${type.name} articles:`, err)
return {
name: type.name,
code: type.code,
articleList: []
}
}
})
const results = await Promise.all(promises)
console.log(results, 'results')
// newsData.value = results.filter(category => category.articleList.length > 0)
newsData.value = results
console.log(newsData.value, 'newsData')
} catch (err) {
error.value = err instanceof Error ? err.message : '获取新闻数据失败'
console.error('Error fetching news data:', err)
} finally {
loading.value = false
}
}
onMounted(() => {
fetchNewsData()
})
</script>
<template>
<!--box3 新闻-->
<div class="container">
<!-- 标题区域 -->
<div class="text-center">
<div class="blank50"/>
<div
class="cBlack f_42 line42 marginB10 wow fadeInDown animated"
style="visibility: visible; animation-name: fadeInDown;">
开发资讯
</div>
<div class="cGray f_18 wow fadeInUp animated" style="visibility: visible; animation-name: fadeInUp;">
NEWS
</div>
<div class="blank20"/>
<div class="blank20"/>
</div>
<!-- 加载状态 -->
<div v-if="loading" class="text-center">
<div class="blank50"/>
<div class="cGray f_18 loading-text">正在加载新闻...</div>
<div class="blank50"/>
</div>
<!-- 错误状态 -->
<div v-else-if="error" class="text-center">
<div class="blank50"/>
<div class="cRed f_18">加载失败: {{ error }}</div>
<div class="blank20"/>
<button
class="btn btn-primary"
style="background-color: #007bff; color: white; border: none; padding: 10px 20px; border-radius: 4px; cursor: pointer;"
@click="fetchNewsData">
重新加载
</button>
<div class="blank50"/>
</div>
<!-- 新闻分类列表 -->
<div v-else class="row">
<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="col-md-4 firstPageBox3Li wow fadeInDown animated active">
<!-- 分类标题 -->
<div :title="category.name" class="title f_22 cBlue text-center pointer">
{{ category.name }}
</div>
<hr>
<div class="blank20"/>
<!-- 文章列表 -->
<ul>
<li v-for="article in getDisplayArticles(category.articleList)" :key="article.id">
<a rel="nofollow" target="_blank">
<span class="pull-right"/>
</a>
<a
:href="getArticleLink(article.id)"
:title="article.title"
class="cGray f_14"
target="_blank">
<span class="pull-right">{{ formatTime(article.createTime) }}</span>
<span class="oneIn oneIn_1">{{ article.title }}</span>
<div class="blank20"/>
</a>
</li>
</ul>
<div class="blank50"/>
</div>
</div>
<!-- 空状态 -->
<div v-if="!loading && !error && newsData.length === 0" class="text-center">
<div class="blank50"/>
<div class="cGray f_18">暂无新闻数据</div>
<div class="blank50"/>
</div>
</div>
</template>
<style scoped>
/* 错误状态样式 */
.cRed {
color: #dc3545;
}
/* 按钮样式 */
.btn {
display: inline-block;
font-weight: 400;
text-align: center;
white-space: nowrap;
vertical-align: middle;
user-select: none;
border: 1px solid transparent;
padding: 0.375rem 0.75rem;
font-size: 1rem;
line-height: 1.5;
border-radius: 0.25rem;
transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
cursor: pointer;
}
.btn:hover {
opacity: 0.9;
}
.btn-primary {
color: #fff;
background-color: #007bff;
border-color: #007bff;
}
/* 加载状态动画 */
@keyframes pulse {
0% {
opacity: 1;
}
50% {
opacity: 0.5;
}
100% {
opacity: 1;
}
}
.loading-text {
animation: pulse 1.5s ease-in-out infinite;
}
/* 文章列表样式优化 */
.firstPageBox3Li ul {
list-style: none;
padding: 0;
margin: 0;
}
.firstPageBox3Li li {
margin-bottom: 10px;
}
.firstPageBox3Li a {
text-decoration: none;
display: block;
padding: 5px 0;
}
.firstPageBox3Li a:hover {
text-decoration: underline;
}
/* 响应式调整 */
@media (max-width: 1024px) {
.grid {
gap: 1.5rem;
}
.firstPageBox3Li {
margin-bottom: 2rem;
}
}
@media (max-width: 768px) {
.firstPageBox3Li {
margin-bottom: 1.5rem;
}
.title {
font-size: 18px !important;
}
}
</style>