277 lines
7.9 KiB
Vue
277 lines
7.9 KiB
Vue
<template>
|
||
<AdminLayout
|
||
title="资源管理"
|
||
subtitle="管理文章、资源、数据等内容"
|
||
>
|
||
<div class="article-management">
|
||
<div class="action-bar">
|
||
<el-button type="primary" @click="showCreateDialog">+ 新增文章</el-button>
|
||
<el-input
|
||
v-model="searchKeyword"
|
||
placeholder="搜索文章..."
|
||
style="width: 300px"
|
||
onkeydown=""
|
||
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" />
|
||
<el-table-column prop="publishTime" label="发布日期" width="120" />
|
||
<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>
|
||
<el-table-column label="操作" width="250" fixed="right">
|
||
<template #default="{ row }">
|
||
<el-button size="small" @click="viewArticle(row)">查看</el-button>
|
||
<el-button
|
||
size="small"
|
||
:type="getActionButtonType(row.status)"
|
||
@click="changeArticleStatus(row)"
|
||
>
|
||
{{ getActionButtonText(row.status) }}
|
||
</el-button>
|
||
<el-button size="small" @click="editArticle(row)">编辑</el-button>
|
||
<el-button size="small" type="danger" @click="deleteArticle()">删除</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
|
||
<el-pagination
|
||
v-model:current-page="pageParam.pageNumber"
|
||
v-model:page-size="pageParam.pageSize"
|
||
:total="total"
|
||
layout="total, sizes, prev, pager, next, jumper"
|
||
@size-change="handleSizeChange"
|
||
@current-change="handleCurrentChange"
|
||
/>
|
||
|
||
<!-- 文章查看弹窗 -->
|
||
<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"
|
||
/>
|
||
</div>
|
||
</AdminLayout>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { AdminLayout } from '@/views/admin';
|
||
|
||
defineOptions({
|
||
name: 'ArticleManagementView'
|
||
});
|
||
import { ref, onMounted } from 'vue';
|
||
import { ElButton, ElInput, ElTable, ElTableColumn, ElTag, ElPagination, ElMessage } from 'element-plus';
|
||
import { useRouter } from 'vue-router';
|
||
import { resourceApi, resourceTagApi } from '@/apis/resource'
|
||
import type { PageParam, ResourceSearchParams, Resource, Tag } from '@/types';
|
||
import { ArticleShowView } from '@/views/public/article';
|
||
import { ArticleStatus } from '@/types/enums';
|
||
|
||
const router = useRouter();
|
||
const searchKeyword = ref('');
|
||
const pageParam = ref<PageParam>({
|
||
pageNumber: 1,
|
||
pageSize: 10
|
||
});
|
||
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);
|
||
const categoryList = ref<Tag[]>([]); // 改为使用Tag类型(tagType=1表示文章分类)
|
||
|
||
onMounted(() => {
|
||
loadArticles();
|
||
loadCategories();
|
||
});
|
||
|
||
async function loadCategories() {
|
||
try {
|
||
// 使用新的标签API获取文章分类标签(tagType=1)
|
||
const res = await resourceTagApi.getTagsByType(1); // 1 = 文章分类标签
|
||
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 || [];
|
||
total.value = res.pageDomain?.pageParam.totalElements || 0;
|
||
}
|
||
}
|
||
|
||
function showCreateDialog() {
|
||
// 尝试跳转
|
||
router.push('/article/add')
|
||
.then(() => {
|
||
console.log('路由跳转成功!');
|
||
console.log('跳转后路由:', router.currentRoute.value.fullPath);
|
||
})
|
||
.catch(err => {
|
||
console.error('路由跳转失败:', err);
|
||
});
|
||
}
|
||
|
||
function handleDataCollection() {
|
||
// TODO: 数据采集功能
|
||
ElMessage.info('数据采集功能开发中');
|
||
}
|
||
|
||
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);
|
||
}
|
||
}
|
||
|
||
function editArticle(row: any) {
|
||
router.push('/article/add?id=' + row.resourceID);
|
||
}
|
||
|
||
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('操作失败');
|
||
}
|
||
}
|
||
|
||
function handleEditFromView() {
|
||
if (currentArticle.value?.resourceID) {
|
||
showViewDialog.value = false;
|
||
router.push('/article/add?id=' + currentArticle.value.resourceID);
|
||
}
|
||
}
|
||
|
||
function deleteArticle() {
|
||
// TODO: 删除文章
|
||
ElMessage.info('删除功能开发中');
|
||
}
|
||
|
||
function getStatusType(status: number) {
|
||
const typeMap: Record<number, any> = {
|
||
[ArticleStatus.DRAFT]: 'info',
|
||
[ArticleStatus.PUBLISHED]: 'success',
|
||
[ArticleStatus.OFFLINE]: 'warning',
|
||
[ArticleStatus.FAILED]: 'danger'
|
||
};
|
||
return typeMap[status] || 'info';
|
||
}
|
||
|
||
function getStatusText(status: number) {
|
||
const textMap: Record<number, string> = {
|
||
[ArticleStatus.DRAFT]: '草稿',
|
||
[ArticleStatus.PUBLISHED]: '已发布',
|
||
[ArticleStatus.OFFLINE]: '已下架',
|
||
[ArticleStatus.FAILED]: '审核失败'
|
||
};
|
||
return textMap[status] || '未知';
|
||
}
|
||
|
||
function getActionButtonType(status: number) {
|
||
// 草稿或下架状态显示主要按钮(发布), 已发布状态显示警告按钮(下架)
|
||
if (status === ArticleStatus.DRAFT || status === ArticleStatus.OFFLINE || status === ArticleStatus.FAILED) {
|
||
return 'primary';
|
||
} else if (status === ArticleStatus.PUBLISHED) {
|
||
return 'warning';
|
||
}
|
||
return '';
|
||
}
|
||
|
||
function getActionButtonText(status: number) {
|
||
// 草稿或下架状态显示"发布", 已发布状态显示"下架"
|
||
if (status === ArticleStatus.DRAFT || status === ArticleStatus.OFFLINE || status === ArticleStatus.FAILED) {
|
||
return '发布';
|
||
} else if (status === ArticleStatus.PUBLISHED) {
|
||
return '下架';
|
||
}
|
||
return '操作';
|
||
}
|
||
|
||
function handleSizeChange(val: number) {
|
||
pageParam.value.pageSize = val;
|
||
loadArticles();
|
||
}
|
||
|
||
function handleCurrentChange(val: number) {
|
||
pageParam.value.pageNumber = val;
|
||
loadArticles();
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.article-management {
|
||
background: #FFFFFF;
|
||
padding: 24px;
|
||
border-radius: 14px;
|
||
}
|
||
|
||
.action-bar {
|
||
display: flex;
|
||
gap: 16px;
|
||
margin-bottom: 20px;
|
||
align-items: center;
|
||
}
|
||
|
||
.el-table {
|
||
margin-bottom: 20px;
|
||
}
|
||
</style>
|
||
|