【feat】项目手册管理功能优化

xuhongjie
项洋 2 months ago
parent 8f890ee7cf
commit ae6e44fd70

@ -40,6 +40,7 @@
"@riophae/vue-treeselect": "0.4.0", "@riophae/vue-treeselect": "0.4.0",
"@vue-office/docx": "^1.6.3", "@vue-office/docx": "^1.6.3",
"@vue/composition-api": "^1.7.2", "@vue/composition-api": "^1.7.2",
"autofit.js": "^3.2.2",
"axios": "0.28.1", "axios": "0.28.1",
"clipboard": "2.0.8", "clipboard": "2.0.8",
"core-js": "3.37.1", "core-js": "3.37.1",

@ -906,7 +906,9 @@ export function deleteHandbook(idList) {
return request({ return request({
url: `/gysl/handbook/delete`, url: `/gysl/handbook/delete`,
method: 'delete', method: 'delete',
params: { idList: idList.join(',') } params:{
idList: idList.join(',')
}
}); });
} }

@ -3,59 +3,57 @@
<!-- 项目手册管理 --> <!-- 项目手册管理 -->
<!-- 表单查询项 --> <!-- 表单查询项 -->
<div v-if="!previewMode" class="headerbox"> <div v-if="!previewMode" class="headerbox">
<div class="search-form"> <el-form size="small" :inline="true" label-width="200">
<div class="form-item"> <el-row>
<span class="label">手册名称</span> <el-col :span="5">
<el-input <el-form-item label="手册名称" style="width: 100%;">
v-model="queryParams.name" <el-input v-model="queryParams.name" placeholder="请输入手册名称" clearable />
placeholder="请输入模板标题" </el-form-item>
clearable </el-col>
class="search-input" <el-col :span="5">
/> <el-form-item label="创建时间">
</div>
<div class="form-item">
<span class="label">创建时间</span>
<el-date-picker <el-date-picker
v-model="queryParams.startTime" v-model="queryParams.startTime"
type="date" type="date"
placeholder="选择日期" placeholder="选择日期"
class="search-date" format="yyyy-MM-dd"
></el-date-picker> value-format="yyyy-MM-dd HH:mm:ss"
</div> style="width: 100%;">
<div class="form-item btn-group"> </el-date-picker>
<el-button type="primary" icon="el-icon-search" @click="handleQuery" </el-form-item>
>查询</el-button </el-col>
> <el-col :span="8">
<el-button icon="el-icon-refresh" @click="resetQuery"></el-button> <el-form-item>
</div> <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery"></el-button>
</div> <el-button icon="el-icon-refresh" size="mini" @click="resetQuery"></el-button>
</el-form-item>
</el-col>
</el-row>
</el-form>
</div> </div>
<!-- 表格内容区 --> <!-- 表格内容区 -->
<div v-if="!previewMode" class="tablebox"> <div v-if="!previewMode" class="tablebox">
<div class="tablebtntwo"> <div class="tablebtntwo">
<el-button <!-- <el-button
type="primary" type="primary"
icon="el-icon-plus" icon="el-icon-plus"
size="mini" size="mini"
:loading="exportLoading" :loading="exportLoading"
@click="handleExport" @click="handleExport"
>项目手册导出</el-button >项目手册导出</el-button
> > -->
<el-button <el-button
type="primary" type="primary"
icon="el-icon-plus" icon="el-icon-plus"
size="mini" size="small"
@click="handleAdd" @click="handleAdd"
>新增</el-button >新增</el-button
> >
</div> </div>
<el-table <el-table v-loading="loading" :data="postList">
v-loading="loading" <!-- @selection-change="handleSelectionChange"
:data="postList" <el-table-column type="selection" width="55" align="center" /> -->
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="序号" align="center"> <el-table-column label="序号" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<span>{{ scope.$index + 1 }}</span> <span>{{ scope.$index + 1 }}</span>
@ -88,6 +86,13 @@
@click="handleDelete(scope.row)" @click="handleDelete(scope.row)"
>删除</el-button >删除</el-button
> >
<el-button
size="mini"
type="text"
style="color: #409eff"
@click="handleExport(scope.row.id)"
>项目手册导出</el-button
>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -203,7 +208,7 @@
</div> </div>
<!-- 步骤2选择项目 --> <!-- 步骤2选择项目 -->
<div v-if="activeStep === 2" class="step-content project-step"> <div v-show="activeStep === 2" class="step-content project-step">
<div class="cover-form"> <div class="cover-form">
<div class="project-top"> <div class="project-top">
<div class="search-container"> <div class="search-container">
@ -222,22 +227,17 @@
<el-button @click="resetProjectSearch" class="search-btn" <el-button @click="resetProjectSearch" class="search-btn"
>重置</el-button >重置</el-button
> >
<el-button
type="primary"
@click="confirmSelection"
class="search-btn"
>确认勾选</el-button
>
</div> </div>
<div class="project-table"> <div class="project-table">
<el-table <el-table
ref="projectTable" ref="projectTable"
:data="projectList" :data="projectList"
@selection-change="handleProjectSelectionChange"
@row-click="handleRowClick" @row-click="handleRowClick"
height="400" height="400"
:row-key="(row) => row.id" :row-key="(row) => row.id"
v-loading="loading"
@selection-change="handleSelectAll"
> >
<el-table-column <el-table-column
type="selection" type="selection"
@ -366,7 +366,7 @@
@click="submitHandbook" @click="submitHandbook"
v-if="activeStep === 3" v-if="activeStep === 3"
size="small" size="small"
>{{ editingHandbook ? '更新手册' : '生成手册' }}</el-button >{{ editingHandbook ? "更新手册" : "生成手册" }}</el-button
> >
</div> </div>
</el-dialog> </el-dialog>
@ -376,7 +376,7 @@
<script> <script>
import VueOfficeDocx from "@vue-office/docx"; import VueOfficeDocx from "@vue-office/docx";
import pdf from "vue-pdf"; import pdf from "vue-pdf";
import axios from "axios";
import { import {
getHandbookPage, getHandbookPage,
addHandbook, addHandbook,
@ -418,7 +418,6 @@ export default {
subtitle: "", subtitle: "",
imageUrl: "", imageUrl: "",
}, },
// //
projectSearch: "", projectSearch: "",
projectList: [], projectList: [],
@ -428,34 +427,29 @@ export default {
pageSize: 10, pageSize: 10,
}, },
projectTotal: 0, projectTotal: 0,
// //
endForm: { endForm: {
imageUrl: "", imageUrl: "",
subtitle: "", subtitle: "",
}, },
// //
selectedRows: [], selectedRows: [],
exportLoading: false, exportLoading: false,
selectedProjectIds: [], // id selectedProjectIds: [], // id
// PDF // PDF
pdfData: null, pdfData: null,
currentPage: 1, currentPage: 1,
pageCount: 0, pageCount: 0,
loadingPdf: false, loadingPdf: false,
pdfScale: 0.9, // PDF pdfScale: 0.9, // PDF
//
editingHandbook: null, // editingHandbook: null, //
isRestoringSelection: false, //
}; };
}, },
created() { mounted() {
this.getList(); this.getList();
}, },
methods: { methods: {
//
getList() { getList() {
this.loading = true; this.loading = true;
getHandbookPage(this.queryParams) getHandbookPage(this.queryParams)
@ -510,39 +504,26 @@ export default {
// APIPDF // APIPDF
async loadPdfFromApi(id) { async loadPdfFromApi(id) {
try { try {
console.log("正在获取PDF文件ID:", id);
// 使APIPDF
const res = await exportHandbook([id]); const res = await exportHandbook([id]);
if (!res) { if (!res) {
throw new Error("获取PDF文件失败返回为空"); throw new Error("获取PDF文件失败返回为空");
} }
// Blob
const blob = new Blob([res], { type: "application/pdf" }); const blob = new Blob([res], { type: "application/pdf" });
// URL
this.pdfData = URL.createObjectURL(blob); this.pdfData = URL.createObjectURL(blob);
console.log("PDF文件加载成功");
this.loadingPdf = false;
} catch (error) { } catch (error) {
console.error("加载PDF文件失败:", error);
this.$message.error("PDF加载失败: " + (error.message || "未知错误")); this.$message.error("PDF加载失败: " + (error.message || "未知错误"));
this.loadingPdf = false; this.loadingPdf = false;
} }
}, },
// PDF
pageLoaded() { pageLoaded() {
console.log("PDF页面加载完成");
this.loadingPdf = false; this.loadingPdf = false;
}, },
// PDF // PDF
pdfLoaded() { pdfLoaded() {
console.log("PDF文档加载完成"); this.loadingPdf = false;
}, },
// PDF // PDF
pdfError(error) { pdfError(error) {
console.error("PDF渲染错误:", error);
this.$message.error("PDF渲染失败: " + (error.message || "未知错误")); this.$message.error("PDF渲染失败: " + (error.message || "未知错误"));
this.loadingPdf = false; this.loadingPdf = false;
}, },
@ -559,20 +540,20 @@ export default {
} }
}, },
// //
async handleExport() { async handleExport(id) {
if (!this.selectedRows || this.selectedRows.length === 0) { // if (!this.selectedRows || this.selectedRows.length === 0) {
this.$message.warning("请先选择要导出的手册"); // this.$message.warning("");
return; // return;
} // }
try { try {
this.exportLoading = true; this.exportLoading = true;
const idList = this.selectedRows.map((item) => item.id); // const idList = this.selectedRows.map((item) => item.id);
// //
const res = await exportHandbook(idList); // const res = await exportHandbook(idList);
const res = await exportHandbook([id]);
this.$download.saveAs(res, "项目手册.pdf"); this.$download.saveAs(res, "项目手册.pdf");
this.$message.success("导出成功"); this.$message.success("导出成功");
} catch (error) { } catch (error) {
console.error("导出错误:", error);
this.$message.error("导出过程中发生错误"); this.$message.error("导出过程中发生错误");
} finally { } finally {
this.exportLoading = false; this.exportLoading = false;
@ -585,6 +566,7 @@ export default {
this.editingHandbook = null; this.editingHandbook = null;
this.selectedProjectIds = []; this.selectedProjectIds = [];
this.selectedProjects = []; this.selectedProjects = [];
this.projectParams.pageNum = 1 ;
this.getProjectList(); this.getProjectList();
}, },
handleAddDialogClose(done) { handleAddDialogClose(done) {
@ -617,7 +599,6 @@ export default {
// //
nextStep() { nextStep() {
if (this.activeStep === 1) { if (this.activeStep === 1) {
//
if (!this.coverForm.title) { if (!this.coverForm.title) {
this.$message.warning("请输入手册名称"); this.$message.warning("请输入手册名称");
return; return;
@ -625,11 +606,9 @@ export default {
} }
this.activeStep++; this.activeStep++;
}, },
//
prevStep() { prevStep() {
this.activeStep--; this.activeStep--;
}, },
//
getProjectList() { getProjectList() {
this.loading = true; this.loading = true;
const params = { const params = {
@ -642,21 +621,21 @@ export default {
.then((response) => { .then((response) => {
this.projectList = response.data.records; this.projectList = response.data.records;
this.projectTotal = response.data.total; this.projectTotal = response.data.total;
this.loading = false; if (this.selectedProjectIds.length > 0) {
//
this.$nextTick(() => { this.$nextTick(() => {
this.restoreSelection(); this.restoreSelection();
}); });
}
this.loading = false;
}) })
.catch((error) => { .catch((error) => {
console.error("获取项目列表失败:", error);
this.loading = false; this.loading = false;
this.$message.error("获取项目列表失败"); this.$message.error("获取项目列表失败");
}); });
}, },
// //
searchProjects() { searchProjects() {
// ID
this.projectParams.pageNum = 1; this.projectParams.pageNum = 1;
this.getProjectList(); this.getProjectList();
}, },
@ -666,51 +645,55 @@ export default {
this.projectParams.pageNum = 1; this.projectParams.pageNum = 1;
this.getProjectList(); this.getProjectList();
}, },
//
confirmSelection() {
if (this.selectedProjects.length === 0) {
this.$message.warning("请至少选择一个项目");
return;
}
this.$message.success(`已选择 ${this.selectedProjects.length} 个项目`);
},
handleProjectSelectionChange(selection) {
const currentPageIds = this.projectList.map((item) => item.id);
const otherPagesSelectedProjects = this.selectedProjects.filter(
(project) => !currentPageIds.includes(project.id)
);
this.selectedProjects = [...selection, ...otherPagesSelectedProjects];
const uncheckedIds = currentPageIds.filter( handleRowClick(row) {
(id) => !selection.some((selected) => selected.id === id) //
); this.$refs.projectTable.toggleRowSelection(row);
this.selectedProjectIds = this.selectedProjectIds.filter(
(id) => !uncheckedIds.includes(id)
);
selection.forEach((item) => {
if (!this.selectedProjectIds.includes(item.id)) {
this.selectedProjectIds.push(item.id);
}
});
}, },
removeSelectedProject(project) { removeSelectedProject(project) {
//
this.selectedProjects = this.selectedProjects.filter( this.selectedProjects = this.selectedProjects.filter(
(item) => item.id !== project.id (item) => item.id !== project.id
); );
this.selectedProjectIds = this.selectedProjectIds.filter( this.selectedProjectIds = this.selectedProjectIds.filter(
(id) => id !== project.id (id) => id !== project.id
); );
//
if (this.$refs.projectTable) {
const projectInCurrentPage = this.projectList.find(
(item) => item.id == project.id
);
if (projectInCurrentPage) {
this.$refs.projectTable.toggleRowSelection( this.$refs.projectTable.toggleRowSelection(
this.projectList.find((item) => item.id === project.id), projectInCurrentPage,
false false
); );
}
}
}, },
handleProjectPagination() { handleProjectPagination() {
this.getProjectList(); this.getProjectList();
}, },
handleRowClick(row) { //
this.$refs.projectTable.toggleRowSelection(row); restoreSelection() {
if (!this.$refs.projectTable) return;
this.$refs.projectTable.clearSelection();
if (!this.selectedProjectIds.length) return;
//
this.isRestoringSelection = true;
this.projectList.forEach((row) => {
if (this.selectedProjectIds.includes(row.id)) {
this.$refs.projectTable.toggleRowSelection(row, true);
}
});
//
this.$nextTick(() => {
this.isRestoringSelection = false;
});
}, },
// //
@ -727,14 +710,8 @@ export default {
tail: this.endForm.subtitle, tail: this.endForm.subtitle,
xmId: this.selectedProjects.map((project) => project.id).join(","), xmId: this.selectedProjects.map((project) => project.id).join(","),
}; };
console.log("提交的数据:", data);
if (this.editingHandbook) { if (this.editingHandbook) {
// ID
data.id = this.editingHandbook.id; data.id = this.editingHandbook.id;
// API
updateHandbook(data) updateHandbook(data)
.then((res) => { .then((res) => {
this.$message.success("手册更新成功"); this.$message.success("手册更新成功");
@ -759,33 +736,55 @@ export default {
}); });
} }
}, },
handleSelectAll(selection) {
//
if (this.isRestoringSelection) return;
// ID
const currentPageIds = this.projectList.map((item) => item.id);
//
const projectsNotInCurrentPage = this.selectedProjects.filter(
(project) => !currentPageIds.includes(project.id)
);
//
this.selectedProjects = [...projectsNotInCurrentPage, ...selection];
// ID
this.selectedProjectIds = this.selectedProjects.map(
(project) => project.id
);
},
// //
handleEdit(row) { handleEdit(row) {
// //
this.loading = true; this.loading = true;
getHandbookDetail(row.id).then(res => { getHandbookDetail(row.id)
.then((res) => {
const detail = res.data; const detail = res.data;
//
this.coverForm = { this.coverForm = {
title: detail.name, title: detail.name,
subtitle: detail.subtitle, subtitle: detail.subtitle,
imageUrl: detail.coverImg imageUrl: detail.coverImg,
}; };
//
this.endForm = { this.endForm = {
imageUrl: detail.tailingImg, imageUrl: detail.tailingImg,
subtitle: detail.tail subtitle: detail.tail,
}; };
// ID //
if (detail.xmId) { this.selectedProjectIds = [];
const projectIds = detail.xmId.split(','); this.selectedProjects = [];
this.selectedProjectIds = projectIds;
console.log("已选项目ID列表", this.selectedProjectIds);
// if (detail.xmId) {
const projectIds = detail.xmId.split(",");
this.selectedProjectIds = projectIds.map((id) => Number(id));
//
if (detail.projectName && Array.isArray(detail.projectName)) {
this.selectedProjects = detail.projectName;
}
this.projectParams.pageNum = 1 ;
this.getProjectList(); this.getProjectList();
} }
@ -793,7 +792,8 @@ export default {
this.addDialogVisible = true; this.addDialogVisible = true;
this.activeStep = 1; this.activeStep = 1;
this.editingHandbook = detail; this.editingHandbook = detail;
}).catch(() => { })
.catch(() => {
this.$message.error("获取手册详情失败"); this.$message.error("获取手册详情失败");
this.loading = false; this.loading = false;
}); });
@ -805,39 +805,17 @@ export default {
cancelButtonText: "取消", cancelButtonText: "取消",
type: "warning", type: "warning",
}) })
.then(() => { .then(async () => {
this.loading = true;
const idList = [row.id];
await deleteHandbook(idList).then(() => {
this.$message.success("删除成功"); this.$message.success("删除成功");
this.getList(); this.getList();
});
this.loading = false;
}) })
.catch(() => {}); .catch(() => {});
}, },
//
restoreSelection() {
if (this.$refs.projectTable) {
this.$refs.projectTable.clearSelection();
const selectedRows = this.projectList.filter((row) =>
this.selectedProjectIds.includes(row.id)
);
selectedRows.forEach((row) => {
this.$refs.projectTable.toggleRowSelection(row, true);
});
const currentPageIds = this.projectList.map((item) => item.id);
const otherPagesSelectedProjects = this.selectedProjects.filter(
(project) =>
!currentPageIds.includes(project.id) &&
this.selectedProjectIds.includes(project.id)
);
const currentPageSelectedProjects = this.projectList.filter((item) =>
this.selectedProjectIds.includes(item.id)
);
//
this.selectedProjects = [
...currentPageSelectedProjects,
...otherPagesSelectedProjects,
];
}
},
}, },
}; };
</script> </script>
@ -851,7 +829,11 @@ export default {
padding: 20px; padding: 20px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
border: 1px solid #eee; border: 1px solid #eee;
margin: 0.5rem margin: 0.5rem;
.el-input {
flex: 1;
max-width: 100%;
}
} }
.headerbox { .headerbox {
@ -882,9 +864,17 @@ export default {
.search-input, .search-input,
.search-date { .search-date {
width: 240px; width: 30%;
transition: all 0.3s; transition: all 0.3s;
margin-right: 30px;
::v-deep .el-input {
flex: 1!important;
max-width: 100%!important;
}
::v-deep .el-input__inner {
width: 100%!important;
}
&:hover { &:hover {
box-shadow: 0 0 0 1px #409eff; box-shadow: 0 0 0 1px #409eff;
} }
@ -899,7 +889,7 @@ export default {
.tablebtntwo { .tablebtntwo {
width: 100%; width: 100%;
display: flex; display: flex;
justify-content: space-between; justify-content: right;
margin: 10px 0 20px; margin: 10px 0 20px;
} }
@ -935,7 +925,7 @@ export default {
/* PDF预览区域样式 */ /* PDF预览区域样式 */
.previewbox { .previewbox {
height: calc(100vh - 50px); height: calc(100% - 50px);
z-index: 1500; z-index: 1500;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -1136,11 +1126,6 @@ export default {
font-size: 14px; font-size: 14px;
} }
.el-input {
flex: 1;
max-width: 80%;
}
/* 上传区域样式 */ /* 上传区域样式 */
.upload-area { .upload-area {
flex: 1; flex: 1;

Loading…
Cancel
Save