<template>
<div class="app-container">
<el-row :gutter="20">
<!-- 项目树菜单 -->
<el-col :span="6" :xs="24">
<div class="head-container">
<el-input
v-model="projectName"
placeholder="请输入项目名称"
clearable
size="mini"
prefix-icon="el-icon-search"
style="margin-bottom: 20px"
@input="filterProjectTree"
/>
</div>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="info"
plain
icon="el-icon-sort"
size="mini"
@click="toggleExpandAll"
>展开/折叠</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-plus"
size="mini"
@click="handleAddProject"
v-hasPermi="['cms:project:add']"
>新增项目</el-button>
</el-col>
</el-row>
<el-tree
v-if="refreshTable"
style="margin-top: 0.8rem;"
:data="filteredProjectTreeData"
:props="defaultProps"
:expand-on-click-node="false"
:filter-node-method="filterNode"
:highlight-current="true"
:default-expand-all="isExpandAll"
ref="projectTree"
empty-text="加载中,请稍候"
node-key="id"
@node-click="handleProjectClick"
>
<span class="custom-tree-node" slot-scope="{ node, data }">
<div v-if="node.label && node.label.length > 25">
<el-tooltip :show-after="300" :content="node.label" placement="top-start">
<span>{{ ellipsis(node.label, 25) }}</span>
</el-tooltip>
</div>
<div v-else>
<span>{{ node.label }}</span>
</div>
<span>
<el-button
type="text"
size="mini"
icon="el-icon-edit"
v-hasPermi="['cms:project:update']"
@click.stop="() => handleEditProject(data)"
></el-button>
<el-button
type="text"
size="mini"
icon="el-icon-plus"
v-hasPermi="['cms:project:add']"
@click.stop="() => handleAddSubProject(data)"
></el-button>
<el-button
type="text"
size="mini"
icon="el-icon-delete"
v-hasPermi="['cms:project:delete']"
@click.stop="() => handleDeleteProject(data)"
></el-button>
</span>
</span>
</el-tree>
</el-col>
<!-- 文档区域 -->
<el-col :span="18" :xs="24">
<!-- 文档列表视图 -->
<div v-if="activeView === 'list'">
<el-form :model="docQueryParams" ref="docQueryForm" :inline="true" label-width="68px">
<el-form-item label="文档标题" prop="title">
<el-input
v-model="docQueryParams.title"
placeholder="请输入文档标题"
clearable
size="mini"
@keyup.enter.native="getDocumentList"
/>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select v-model="docQueryParams.status" placeholder="状态" size="mini" clearable>
<el-option label="草稿" value="0" />
<el-option label="审核中" value="1" />
<el-option label="已发布" value="2" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="getDocumentList">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetDocQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 已移除新增文档按钮 -->
<el-table
v-loading="docLoading"
:data="documentList"
highlight-current-row
@row-click="handleRowClick"
>
<el-table-column label="序号" type="index" width="50" align="center" />
<el-table-column label="文档标题" prop="title" min-width="200" />
<el-table-column label="状态" prop="status" width="100">
<template slot-scope="scope">
<el-tag v-if="scope.row.status === '0'" type="info">草稿</el-tag>
<el-tag v-if="scope.row.status === '1'" type="warning">审核中</el-tag>
<el-tag v-if="scope.row.status === '2'" type="success">已发布</el-tag>
</template>
</el-table-column>
<el-table-column label="创建人" prop="creator" width="100" />
<el-table-column label="创建时间" prop="createTime" width="140" />
<el-table-column label="操作" width="120" align="center">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click.stop="handleEditDocument(scope.row)"></el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click.stop="handleDeleteDocument(scope.row)" v-hasPermi="['cms:project:deleteArticleFromProject']"></el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="docTotal>0"
:total="docTotal"
:page.sync="docQueryParams.pageNum"
:limit.sync="docQueryParams.pageSize"
@pagination="getDocumentList"
/>
</div>
<!-- 文档详情视图 -->
<div v-else-if="activeView === 'detail'">
<div class="doc-header">
<el-button type="text" icon="el-icon-back" @click="backToList">返回文档列表</el-button>
<h2>{{ currentDocument.title }}</h2>
</div>
<!-- 富文本编辑器 -->
<el-form :model="currentDocument" ref="docForm" label-width="80px">
<el-form-item label="文档内容">
<Tinymce :height='600' v-model='currentDocument.content'></Tinymce>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="saveDocument">保存</el-button>
<el-button @click="backToList">取消</el-button>
</el-form-item>
</el-form>
</div>
</el-col>
</el-row>
<!-- 项目编辑对话框 -->
<el-dialog :title="projectDialogTitle" :visible.sync="projectDialogVisible" width="50%">
<el-form :model="projectForm" ref="projectForm" label-width="100px">
<el-form-item label="项目名称" prop="name" required>
<el-input v-model="projectForm.name" placeholder="请输入项目名称" />
</el-form-item>
<el-form-item label="上级项目" prop="parentId">
<treeselect
v-model="projectForm.parentId"
:options="projectTreeData"
:normalizer="normalizer"
placeholder="选择上级项目"
/>
</el-form-item>
<el-form-item label="项目描述" prop="description">
<el-input type="textarea" v-model="projectForm.description" :rows="3" />
</el-form-item>
</el-form>
<div slot="footer">
<el-button @click="projectDialogVisible = false">取消</el-button>
<el-button type="primary" @click="saveProject">保存</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import {
listData,
getData,
addData,
updateData
} from "@/api/cms/data";
import {
getProjectTree,
saveProject,
updateProject,
deleteProject,
removeMenusFromProject // 导入新的API方法
} from "@/api/cms/articleProject";
import Tinymce from '@/components/Tinymce';
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
export default {
name: "ProjectManagement",
components: { Treeselect, Tinymce },
data() {
return {
// 项目树相关数据
projectName: '',
projectTreeData: [],
filteredProjectTreeData: [],
refreshTable: true,
isExpandAll: true,
defaultProps: {
children: "children",
label: "label"
},
// 项目对话框相关
projectDialogVisible: false,
projectDialogTitle: '',
projectForm: {
id: null,
name: '',
parentId: null,
description: ''
},
// 文档列表相关
activeView: 'list', // 'list' 或 'detail'
docQueryParams: {
menuIds: null,
title: '',
status: '',
pageNum: 1,
pageSize: 10
},
documentList: [],
docTotal: 0,
docLoading: false,
// 文档详情相关
currentDocument: {
id: null,
menuId: null,
title: '',
content: '',
status: '0'
},
// 当前选中的项目ID(用于删除操作)
currentProjectId: null
};
},
created() {
this.getProjectTree();
},
methods: {
// ================= 项目树方法 =================
ellipsis(text, maxLength) {
return text.length > maxLength ? text.substring(0, maxLength) + '...' : text;
},
toggleExpandAll() {
this.refreshTable = false;
this.isExpandAll = !this.isExpandAll;
this.$nextTick(() => {
this.refreshTable = true;
});
},
filterNode(value, data) {
if (!value) return true;
return data.label.toLowerCase().includes(value.toLowerCase());
},
filterProjectTree() {
this.$refs.projectTree.filter(this.projectName);
},
handleProjectClick(data) {
// 保存当前选中的项目ID
this.currentProjectId = data.id;
// 将menuIds字符串转换为数组
const menuIdsArray = data.menuIds ? data.menuIds.split(',').map(id => id.trim()) : [];
// 将数组转换回逗号分隔的字符串用于查询
this.docQueryParams.menuIds = menuIdsArray.join(',');
this.getDocumentList();
},
// ================= 项目管理方法 =================
handleAddProject() {
this.projectForm = {
id: null,
name: '',
parentId: null,
description: ''
};
this.projectDialogTitle = '新增项目';
this.projectDialogVisible = true;
},
handleAddSubProject(data) {
this.projectForm = {
id: null,
name: '',
parentId: data.id,
description: ''
};
this.projectDialogTitle = '新增子项目';
this.projectDialogVisible = true;
},
handleEditProject(data) {
this.projectForm = {
id: data.id,
name: data.name,
parentId: data.parentId,
description: data.description || ''
};
this.projectDialogTitle = '编辑项目';
this.projectDialogVisible = true;
},
handleDeleteProject(data) {
this.$confirm(`确定删除项目 "${data.name}" 及其所有子项目吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
// 使用deleteProject API删除项目
deleteProject(data.id).then(response => {
if (response.code === 200) {
this.$message.success('删除成功');
this.getProjectTree();
} else {
this.$message.error(response.msg || '删除失败');
}
}).catch(error => {
this.$message.error('删除失败: ' + error.message);
});
});
},
saveProject() {
this.$refs.projectForm.validate(valid => {
if (valid) {
const saveMethod = this.projectForm.id ? updateProject : saveProject;
saveMethod(this.projectForm).then(response => {
if (response.code === 200) {
this.$message.success('保存成功');
this.projectDialogVisible = false;
this.getProjectTree();
} else {
this.$message.error(response.msg || '保存失败');
}
}).catch(error => {
this.$message.error('保存失败: ' + error.message);
});
}
});
},
normalizer(node) {
return {
id: node.id,
label: node.name,
children: node.children && node.children.length > 0 ? node.children : undefined
};
},
// 获取项目树数据
getProjectTree() {
getProjectTree().then(response => {
if (response.code === 200 && response.data) {
// 处理根节点
const rootNode = response.data;
// 转换数据结构
this.projectTreeData = this.transformTreeData([rootNode]);
this.filteredProjectTreeData = [...this.projectTreeData];
// 默认展开根节点
this.$nextTick(() => {
if (this.projectTreeData.length > 0) {
this.$refs.projectTree.setCurrentKey(rootNode.id);
this.handleProjectClick(rootNode);
}
});
} else {
this.$message.error('获取项目树失败: ' + (response.msg || '未知错误'));
}
}).catch(error => {
console.error("获取项目树失败:", error);
this.$message.error('获取项目树失败: ' + error.message);
});
},
// 转换数据结构为el-tree需要的格式
transformTreeData(nodes) {
if (!nodes || !Array.isArray(nodes)) return [];
return nodes.map(node => ({
id: node.id,
label: node.name,
name: node.name,
parentId: node.parentId,
menuIds: node.menuIds,
description: node.description,
createBy: node.createBy,
createTime: node.createTime,
updateBy: node.updateBy,
updateTime: node.updateTime,
children: this.transformTreeData(node.children || []),
rawData: node
}));
},
// ================= 文档管理方法 =================
getDocumentList() {
if (!this.docQueryParams.menuIds) {
return;
}
this.docLoading = true;
listData(this.docQueryParams).then(response => {
if (response.code === 200) {
this.documentList = response.rows;
this.docTotal = response.total;
} else {
this.$message.error(response.msg || '获取文档列表失败');
}
this.docLoading = false;
}).catch(error => {
this.$message.error('获取文档列表失败: ' + error.message);
this.docLoading = false;
});
},
resetDocQuery() {
this.docQueryParams.title = '';
this.docQueryParams.status = '';
this.getDocumentList();
},
handleEditDocument(row) {
getData(row.id).then(response => {
if (response.code === 200) {
this.currentDocument = {
id: response.data.id,
menuId: response.data.menuId,
title: response.data.title,
content: response.data.content,
status: response.data.status
};
this.activeView = 'detail';
} else {
this.$message.error(response.msg || '获取文档详情失败');
}
}).catch(error => {
this.$message.error('获取文档详情失败: ' + error.message);
});
},
handleDeleteDocument(row) {
this.$confirm(`确定从项目中移除文档 "${row.title}" 吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
// 使用removeMenusFromProject API从项目中移除文档
// 参数: { projectId: 当前项目ID, menusIds: [文档ID] }
const params = {
projectId: this.currentProjectId,
menusIds: [row.id] // 使用文档ID数组
};
removeMenusFromProject(params).then(response => {
if (response.code === 200) {
this.$message.success('文档已从项目中移除');
this.getDocumentList();
} else {
this.$message.error(response.msg || '移除文档失败');
}
}).catch(error => {
this.$message.error('移除文档失败: ' + error.message);
});
});
},
handleRowClick(row) {
this.handleEditDocument(row);
},
backToList() {
this.activeView = 'list';
},
saveDocument() {
const saveMethod = this.currentDocument.id ? updateData : addData;
saveMethod(this.currentDocument).then(response => {
if (response.code === 200) {
this.$message.success('保存成功');
this.getDocumentList();
this.backToList();
} else {
this.$message.error(response.msg || '保存失败');
}
}).catch(error => {
this.$message.error('保存失败: ' + error.message);
});
}
}
};
</script>
<style scoped>
.app-container {
padding: 20px;
}
.head-container {
padding: 10px;
background-color: #f5f7fa;
border-radius: 4px;
}
.custom-tree-node {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
padding-right: 8px;
}
.doc-header {
display: flex;
align-items: center;
margin-bottom: 20px;
}
.doc-header h2 {
margin-left: 15px;
margin-bottom: 0;
}
.el-tree {
border: 1px solid #ebeef5;
border-radius: 4px;
padding: 10px;
max-height: 70vh;
overflow-y: auto;
}
.custom-tree-node .el-button {
padding: 4px;
margin-left: 5px;
}
</style>
该页面搜索栏没生效,表格要有多选功能,改变原有的功能下优化该页面的ui和交互,图标也要加上,添加一个批量导出按钮
最新发布