添加文章列表接口
This commit is contained in:
parent
1cc7c965cc
commit
22f8d99a2a
177
app/components/DevelopNews.vue
Normal file
177
app/components/DevelopNews.vue
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
<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: []
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const results = await Promise.all(promises)
|
||||
newsData.value = results
|
||||
|
||||
} 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 py-12">
|
||||
<div
|
||||
class="text-black text-4xl leading-tight mb-10 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>
|
||||
|
||||
|
||||
|
|
@ -35,6 +35,43 @@ export interface ApiResponse<T> {
|
|||
data: T
|
||||
}
|
||||
|
||||
/**
|
||||
* 文章数据类型定义
|
||||
*/
|
||||
export interface Article {
|
||||
id: string
|
||||
title: string
|
||||
brief: string | null
|
||||
content: string | null
|
||||
createTime: string
|
||||
createBy?: string | null
|
||||
updateTime?: string | null
|
||||
updateBy?: string | null
|
||||
remark?: string | null
|
||||
code?: string | null
|
||||
status?: string | null
|
||||
scope?: string | null
|
||||
deleted?: string | null
|
||||
areaPermissions?: string | null
|
||||
}
|
||||
|
||||
/**
|
||||
* 分页响应数据结构
|
||||
*/
|
||||
export interface PageResponse<T> {
|
||||
total: number
|
||||
rows: T[]
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文章列表的查询参数
|
||||
*/
|
||||
export interface GetArticleListParams {
|
||||
pageNum?: number
|
||||
pageSize?: number
|
||||
code?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取轮播图列表
|
||||
* @returns Promise<BannerItem[]> 返回轮播图数据数组
|
||||
|
|
@ -72,3 +109,52 @@ export async function getBannerList(): Promise<BannerItem[]> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文章列表
|
||||
* @param params 查询参数(pageNum, pageSize, code等)
|
||||
* @returns Promise<PageResponse<Article>> 返回分页的文章数据
|
||||
*/
|
||||
export async function getArticleList(
|
||||
params: GetArticleListParams = {}
|
||||
): Promise<PageResponse<Article>> {
|
||||
try {
|
||||
const { pageNum = 1, pageSize = 10, code } = params
|
||||
|
||||
// 构建查询参数
|
||||
const queryParams: Record<string, string> = {
|
||||
pageNum: pageNum.toString(),
|
||||
pageSize: pageSize.toString(),
|
||||
}
|
||||
|
||||
if (code) {
|
||||
queryParams.code = code
|
||||
}
|
||||
|
||||
const response = await $fetch<PageResponse<Article>>(
|
||||
`${API_BASE_URL}/app/owArticle/list`,
|
||||
{
|
||||
method: 'GET',
|
||||
params: queryParams,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
// 检查响应数据
|
||||
if (response && Array.isArray(response.rows)) {
|
||||
return {
|
||||
total: response.total || 0,
|
||||
rows: response.rows || [],
|
||||
}
|
||||
}
|
||||
|
||||
console.warn('获取文章列表失败,返回空数据', response)
|
||||
return { total: 0, rows: [] }
|
||||
} catch (error) {
|
||||
console.error('获取文章列表时发生错误:', error)
|
||||
// 发生错误时返回空数据,避免页面崩溃
|
||||
return { total: 0, rows: [] }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
<div>
|
||||
<AppHeader/>
|
||||
<slot/>
|
||||
<DevelopNews/>
|
||||
<AppFooter/>
|
||||
<ContactFloatingButton/>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user