Files
schoolNews/schoolNewsWeb/src/views/admin/manage/resource/ArticleManagementView.vue

263 lines
7.5 KiB
Vue
Raw Normal View History

2025-10-16 18:03:46 +08:00
<template>
<div class="article-management">
<div class="action-bar">
<el-button type="primary" @click="showCreateDialog">+ 新增文章</el-button>
<el-button @click="handleDataCollection">数据采集</el-button>
<el-input
v-model="searchKeyword"
placeholder="搜索文章..."
style="width: 300px"
clearable
/>
</div>
<el-table :data="articles" style="width: 100%">
<el-table-column prop="title" label="文章标题" min-width="200" />
<el-table-column prop="category" label="分类" width="120" />
<el-table-column prop="author" label="作者" width="120" />
2025-10-20 15:08:41 +08:00
<el-table-column prop="publishTime" label="发布日期" width="120" />
2025-10-16 18:03:46 +08:00
<el-table-column prop="views" label="阅读量" width="100" />
<el-table-column prop="status" label="状态" width="100">
<template #default="{ row }">
<el-tag :type="getStatusType(row.status)">
{{ getStatusText(row.status) }}
</el-tag>
</template>
</el-table-column>
2025-10-24 18:28:35 +08:00
<el-table-column label="操作" width="250" fixed="right">
2025-10-16 18:03:46 +08:00
<template #default="{ row }">
2025-10-20 15:08:41 +08:00
<el-button size="small" @click="viewArticle(row)">查看</el-button>
2025-10-24 18:28:35 +08:00
<el-button
size="small"
:type="getActionButtonType(row.status)"
@click="changeArticleStatus(row)"
>
{{ getActionButtonText(row.status) }}
</el-button>
2025-10-16 18:03:46 +08:00
<el-button size="small" @click="editArticle(row)">编辑</el-button>
2025-10-20 15:08:41 +08:00
<el-button size="small" type="danger" @click="deleteArticle()">删除</el-button>
2025-10-16 18:03:46 +08:00
</template>
</el-table-column>
</el-table>
<el-pagination
2025-10-27 13:42:34 +08:00
v-model:current-page="pageParam.pageNumber"
v-model:page-size="pageParam.pageSize"
2025-10-16 18:03:46 +08:00
:total="total"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
2025-10-20 15:08:41 +08:00
<!-- 文章查看弹窗 -->
<ArticleShowView
v-model="showViewDialog"
:as-dialog="true"
title="文章详情"
width="900px"
:article-data="currentArticle"
:category-list="categoryList"
:show-edit-button="true"
@edit="handleEditFromView"
@close="showViewDialog = false"
/>
2025-10-16 18:03:46 +08:00
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { ElButton, ElInput, ElTable, ElTableColumn, ElTag, ElPagination, ElMessage } from 'element-plus';
2025-10-18 18:19:19 +08:00
import { useRouter } from 'vue-router';
2025-10-27 16:21:00 +08:00
import { resourceApi, resourceTagApi } from '@/apis/resource'
import type { PageParam, ResourceSearchParams, Resource, Tag } from '@/types';
2025-10-20 15:08:41 +08:00
import { ArticleShowView } from '@/views/article';
2025-10-24 18:28:35 +08:00
import { ArticleStatus } from '@/types/enums';
2025-10-16 18:03:46 +08:00
2025-10-18 18:19:19 +08:00
const router = useRouter();
2025-10-16 18:03:46 +08:00
const searchKeyword = ref('');
2025-10-20 15:08:41 +08:00
const pageParam = ref<PageParam>({
2025-10-27 13:42:34 +08:00
pageNumber: 1,
pageSize: 10
2025-10-20 15:08:41 +08:00
});
const filter = ref<ResourceSearchParams>({
keyword: searchKeyword.value
});
const total = ref<number>(0);
const articles = ref<Resource[]>([]);
const showViewDialog = ref(false);
const currentArticle = ref<any>(null);
2025-10-27 16:21:00 +08:00
const categoryList = ref<Tag[]>([]); // 改为使用Tag类型tagType=1表示文章分类
2025-10-16 18:03:46 +08:00
onMounted(() => {
loadArticles();
2025-10-20 15:08:41 +08:00
loadCategories();
2025-10-16 18:03:46 +08:00
});
2025-10-20 15:08:41 +08:00
async function loadCategories() {
try {
2025-10-27 16:21:00 +08:00
// 使用新的标签API获取文章分类标签tagType=1
const res = await resourceTagApi.getTagsByType(1); // 1 = 文章分类标签
2025-10-20 15:08:41 +08:00
if (res.success && res.dataList) {
categoryList.value = res.dataList;
}
} catch (error) {
console.error('加载分类列表失败:', error);
}
}
async function loadArticles() {
const res = await resourceApi.getResourcePage(pageParam.value, filter.value);
if (res.success) {
articles.value = res.pageDomain?.dataList || [];
2025-10-24 18:28:35 +08:00
total.value = res.pageDomain?.pageParam.totalElements || 0;
2025-10-20 15:08:41 +08:00
}
2025-10-16 18:03:46 +08:00
}
2025-10-18 18:19:19 +08:00
function showCreateDialog() {
// 尝试跳转
router.push('/article/add')
.then(() => {
console.log('路由跳转成功!');
console.log('跳转后路由:', router.currentRoute.value.fullPath);
})
.catch(err => {
console.error('路由跳转失败:', err);
});
2025-10-16 18:03:46 +08:00
}
function handleDataCollection() {
// TODO: 数据采集功能
ElMessage.info('数据采集功能开发中');
}
2025-10-20 15:08:41 +08:00
async function viewArticle(row: any) {
try {
const res = await resourceApi.getResourceById(row.resourceID);
if (res.success && res.data) {
// 将 ResourceVO 转换为 ArticleShowView 期望的格式
const resourceVO = res.data;
currentArticle.value = {
...resourceVO.resource,
tags: resourceVO.tags || []
};
showViewDialog.value = true;
} else {
ElMessage.error('获取文章详情失败');
}
} catch (error) {
ElMessage.error('获取文章详情失败');
console.error(error);
}
}
2025-10-16 18:03:46 +08:00
function editArticle(row: any) {
2025-10-20 15:08:41 +08:00
router.push('/article/add?id=' + row.resourceID);
}
2025-10-24 18:28:35 +08:00
async function changeArticleStatus(row: Resource) {
try {
// status: 0-草稿, 1-已发布, 2-已下架
if (row.status === ArticleStatus.DRAFT || row.status === ArticleStatus.OFFLINE) {
// 草稿或下架状态 -> 发布
const res = await resourceApi.publishResource(row.resourceID!);
if (res.success) {
ElMessage.success('发布成功');
loadArticles();
} else {
ElMessage.error('发布失败');
}
} else if (row.status === ArticleStatus.PUBLISHED) {
// 已发布状态 -> 下架
const res = await resourceApi.unpublishResource(row.resourceID!);
if (res.success) {
ElMessage.success('下架成功');
loadArticles();
} else {
ElMessage.error('下架失败');
}
}
} catch (error) {
console.error('操作失败:', error);
ElMessage.error('操作失败');
}
}
2025-10-20 15:08:41 +08:00
function handleEditFromView() {
if (currentArticle.value?.resourceID) {
showViewDialog.value = false;
router.push('/article/add?id=' + currentArticle.value.resourceID);
}
2025-10-16 18:03:46 +08:00
}
2025-10-20 15:08:41 +08:00
function deleteArticle() {
2025-10-16 18:03:46 +08:00
// TODO: 删除文章
2025-10-20 15:08:41 +08:00
ElMessage.info('删除功能开发中');
2025-10-16 18:03:46 +08:00
}
2025-10-24 18:28:35 +08:00
function getStatusType(status: number) {
const typeMap: Record<number, any> = {
[ArticleStatus.DRAFT]: 'info',
[ArticleStatus.PUBLISHED]: 'success',
[ArticleStatus.OFFLINE]: 'warning'
2025-10-16 18:03:46 +08:00
};
return typeMap[status] || 'info';
}
2025-10-24 18:28:35 +08:00
function getStatusText(status: number) {
const textMap: Record<number, string> = {
[ArticleStatus.DRAFT]: '草稿',
[ArticleStatus.PUBLISHED]: '已发布',
[ArticleStatus.OFFLINE]: '已下架'
2025-10-16 18:03:46 +08:00
};
2025-10-24 18:28:35 +08:00
return textMap[status] || '未知';
}
function getActionButtonType(status: number) {
// 草稿或下架状态显示主要按钮(发布), 已发布状态显示警告按钮(下架)
if (status === ArticleStatus.DRAFT || status === ArticleStatus.OFFLINE) {
return 'primary';
} else if (status === ArticleStatus.PUBLISHED) {
return 'warning';
}
return '';
}
function getActionButtonText(status: number) {
// 草稿或下架状态显示"发布", 已发布状态显示"下架"
if (status === ArticleStatus.DRAFT || status === ArticleStatus.OFFLINE) {
return '发布';
} else if (status === ArticleStatus.PUBLISHED) {
return '下架';
}
return '操作';
2025-10-16 18:03:46 +08:00
}
function handleSizeChange(val: number) {
2025-10-27 13:42:34 +08:00
pageParam.value.pageSize = val;
2025-10-16 18:03:46 +08:00
loadArticles();
}
function handleCurrentChange(val: number) {
2025-10-27 13:42:34 +08:00
pageParam.value.pageNumber = val;
2025-10-16 18:03:46 +08:00
loadArticles();
}
</script>
<style lang="scss" scoped>
.article-management {
padding: 20px;
}
.action-bar {
display: flex;
gap: 16px;
margin-bottom: 20px;
align-items: center;
}
.el-table {
margin-bottom: 20px;
}
</style>