ct/app/components/DevelopNews.vue
2025-10-10 10:46:12 +08:00

271 lines
6.2 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'
// 类型定义
interface Article {
id: number
title: string
time: number
}
interface NewsCategory {
name: string
articleList: Article[]
}
interface NewsApiResponse {
code: string
data: NewsCategory[]
}
// 响应式数据
const newsData = ref<NewsCategory[]>([])
const loading = ref(false)
const error = ref('')
// 计算属性 - 格式化时间
const formatTime = (timestamp: number): string => {
const date = new Date(timestamp * 1000)
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 fetchNewsData = async (): Promise<void> => {
loading.value = true
error.value = ''
try {
const response = await fetch('https://article.yuxiit.com/Home/Article/articleList.html', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'p1_Num=10'
})
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`)
}
const result: NewsApiResponse = await response.json()
if (result.code === '10000') {
newsData.value = result.data
} else {
throw new Error('API returned error code: ' + result.code)
}
} catch (err) {
error.value = err instanceof Error ? err.message : '获取新闻数据失败'
console.error('Error fetching news data:', err)
} finally {
loading.value = false
}
}
// 获取文章链接
const getArticleLink = (articleId: number): string => {
return `http://www.yuxiit.com/news/news_${articleId}.html`
}
// 获取显示的文章列表每个分类显示前2篇文章与原来jQuery版本保持一致
const getDisplayArticles = (articles: Article[]): Article[] => {
return articles.slice(0, 2)
}
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.time) }}</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>
/* 文本截断样式 */
.line-clamp-2 {
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
/* 错误状态样式 */
.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>