ct/app/components/DevelopNews.vue

283 lines
6.6 KiB
Vue
Raw Normal View History

2025-10-09 13:49:17 +08:00
<script lang="ts" setup>
2025-11-18 09:25:36 +08:00
import {ref, onMounted} from 'vue'
import {useArticleApi} from '~/composables/useArticleApi'
2025-10-09 13:49:17 +08:00
2025-10-09 16:23:04 +08:00
// 类型定义
interface Article {
2025-10-11 09:42:31 +08:00
id: string
2025-10-09 16:23:04 +08:00
title: string
2025-10-11 09:42:31 +08:00
brief: string | null
content: string | null
createTime: string
code: string
status: string | null
2025-10-09 16:23:04 +08:00
}
2025-10-09 13:49:17 +08:00
2025-10-09 16:23:04 +08:00
interface NewsCategory {
name: string
code: string
2025-10-11 09:42:31 +08:00
articleList: Article[]
2025-10-09 16:23:04 +08:00
}
2025-10-09 13:49:17 +08:00
2025-10-09 16:23:04 +08:00
// 响应式数据
const newsData = ref<NewsCategory[]>([])
const loading = ref(false)
const error = ref('')
2025-10-09 13:49:17 +08:00
2025-10-11 09:42:31 +08:00
// 文章类型配置
const articleTypes = [
2025-11-18 09:25:36 +08:00
{name: '解决方案', code: 'solution'},
{name: '开发知识', code: 'developKnowledge'},
{name: '行业动态', code: 'industryTrend'}
2025-10-11 09:42:31 +08:00
]
// 格式化时间
const formatTime = (timeString: string): string => {
const date = new Date(timeString)
2025-10-09 16:23:04 +08:00
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}`
}
2025-10-09 13:49:17 +08:00
2025-10-11 09:42:31 +08:00
// 获取文章链接
const getArticleLink = (articleId: string): string => {
return `/article/${articleId}`
}
// 获取显示的文章列表每个分类显示前2篇文章
const getDisplayArticles = (articles: Article[]): Article[] => {
return articles.slice(0, 2)
}
2025-10-09 16:23:04 +08:00
// 获取新闻数据
const fetchNewsData = async (): Promise<void> => {
loading.value = true
error.value = ''
2025-10-10 10:46:12 +08:00
2025-10-09 16:23:04 +08:00
try {
2025-10-11 09:42:31 +08:00
// 并发获取所有类型的文章数据
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: []
}
}
2025-10-09 16:23:04 +08:00
})
2025-10-10 10:46:12 +08:00
2025-10-11 09:42:31 +08:00
const results = await Promise.all(promises)
2025-11-18 09:25:36 +08:00
console.log(results, 'results')
// newsData.value = results.filter(category => category.articleList.length > 0)
newsData.value = results
console.log(newsData.value, 'newsData')
2025-10-09 16:23:04 +08:00
} catch (err) {
error.value = err instanceof Error ? err.message : '获取新闻数据失败'
console.error('Error fetching news data:', err)
} finally {
loading.value = false
}
2025-10-09 13:49:17 +08:00
}
2025-10-09 16:23:04 +08:00
onMounted(() => {
fetchNewsData()
})
2025-10-09 13:49:17 +08:00
</script>
2025-09-29 18:02:28 +08:00
<template>
2025-10-08 16:50:25 +08:00
<!--box3 新闻-->
<div class="container">
2025-10-09 16:23:04 +08:00
<!-- 标题区域 -->
2025-10-08 16:50:25 +08:00
<div class="text-center">
<div class="blank50"/>
<div
class="cBlack f_42 line42 marginB10 wow fadeInDown animated"
2025-10-09 16:23:04 +08:00
style="visibility: visible; animation-name: fadeInDown;">
开发资讯
</div>
<div class="cGray f_18 wow fadeInUp animated" style="visibility: visible; animation-name: fadeInUp;">
NEWS
2025-09-29 18:02:28 +08:00
</div>
2025-10-08 16:50:25 +08:00
<div class="blank20"/>
<div class="blank20"/>
</div>
2025-09-29 18:02:28 +08:00
2025-10-09 16:23:04 +08:00
<!-- 加载状态 -->
<div v-if="loading" class="text-center">
<div class="blank50"/>
<div class="cGray f_18 loading-text">正在加载新闻...</div>
2025-10-08 16:50:25 +08:00
<div class="blank50"/>
</div>
2025-09-29 18:02:28 +08:00
2025-10-09 16:23:04 +08:00
<!-- 错误状态 -->
<div v-else-if="error" class="text-center">
<div class="blank50"/>
<div class="cRed f_18">加载失败: {{ error }}</div>
2025-10-08 16:50:25 +08:00
<div class="blank20"/>
2025-10-10 10:46:12 +08:00
<button
class="btn btn-primary"
style="background-color: #007bff; color: white; border: none; padding: 10px 20px; border-radius: 4px; cursor: pointer;"
@click="fetchNewsData">
2025-10-09 16:23:04 +08:00
重新加载
</button>
2025-10-08 16:50:25 +08:00
<div class="blank50"/>
</div>
2025-09-29 18:02:28 +08:00
2025-10-09 16:23:04 +08:00
<!-- 新闻分类列表 -->
<div v-else class="row">
<div
v-for="(category, index) in newsData"
:key="category.name"
:data-wow-delay="`${300 + index * 200}ms`"
2025-10-10 10:46:12 +08:00
:style="`visibility: visible; animation-delay: ${300 + index * 200}ms; animation-name: fadeInDown;`"
class="col-md-4 firstPageBox3Li wow fadeInDown animated active">
2025-10-09 16:23:04 +08:00
<!-- 分类标题 -->
2025-10-10 10:46:12 +08:00
<div :title="category.name" class="title f_22 cBlue text-center pointer">
2025-10-09 16:23:04 +08:00
{{ category.name }}
</div>
<hr>
<div class="blank20"/>
2025-10-10 10:46:12 +08:00
2025-10-09 16:23:04 +08:00
<!-- 文章列表 -->
<ul>
<li v-for="article in getDisplayArticles(category.articleList)" :key="article.id">
<a rel="nofollow" target="_blank">
2025-10-10 10:46:12 +08:00
<span class="pull-right"/>
2025-10-09 16:23:04 +08:00
</a>
2025-10-10 10:46:12 +08:00
<a
:href="getArticleLink(article.id)"
:title="article.title"
class="cGray f_14"
target="_blank">
2025-10-11 09:42:31 +08:00
<span class="pull-right">{{ formatTime(article.createTime) }}</span>
2025-10-09 16:23:04 +08:00
<span class="oneIn oneIn_1">{{ article.title }}</span>
2025-10-10 10:46:12 +08:00
<div class="blank20"/>
2025-10-09 16:23:04 +08:00
</a>
</li>
</ul>
2025-10-10 10:46:12 +08:00
2025-10-09 16:23:04 +08:00
<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>
2025-10-08 16:50:25 +08:00
<div class="blank50"/>
2025-09-29 18:02:28 +08:00
</div>
</div>
2025-10-08 16:50:25 +08:00
</template>
2025-10-07 17:10:00 +08:00
2025-09-29 18:02:28 +08:00
<style scoped>
2025-10-10 17:01:34 +08:00
2025-09-29 18:02:28 +08:00
2025-10-09 16:23:04 +08:00
/* 错误状态样式 */
.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;
}
2025-09-29 18:02:28 +08:00
/* 响应式调整 */
@media (max-width: 1024px) {
.grid {
gap: 1.5rem;
}
2025-10-10 10:46:12 +08:00
2025-10-09 16:23:04 +08:00
.firstPageBox3Li {
margin-bottom: 2rem;
}
}
@media (max-width: 768px) {
.firstPageBox3Li {
margin-bottom: 1.5rem;
}
2025-10-10 10:46:12 +08:00
2025-10-09 16:23:04 +08:00
.title {
font-size: 18px !important;
}
2025-09-29 18:02:28 +08:00
}
</style>