Files
projectSystem/ruoyi-ui/src/views/project/doc/index.vue
2025-09-07 15:59:40 +08:00

432 lines
15 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="app-container">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="文档名称" prop="docName">
<el-input
v-model="queryParams.docName"
placeholder="请输入文档名称"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="上传者" prop="createBy">
<el-input
v-model="queryParams.createBy"
placeholder="请输入上传者"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-upload"
size="mini"
@click="handleUpload"
v-hasPermi="['project:doc:upload']"
>上传文档</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-delete"
size="mini"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['project:doc:remove']"
>删除</el-button>
</el-col>
</el-row>
<el-table v-loading="loading" :data="docList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="文档名称" align="center" prop="docName" />
<el-table-column label="所属项目" align="center" prop="projectName" />
<el-table-column label="文档类型" align="center">
<template slot-scope="scope">
<span>{{ (scope.row.docType) || (scope.row.docPath ? scope.row.docPath.split('.').pop() : '-') }}</span>
</template>
</el-table-column>
<el-table-column label="文件大小" align="center">
<template slot-scope="scope">
<span>{{ scope.row.docSize || '-' }}</span>
</template>
</el-table-column>
<el-table-column label="上传者" align="center" prop="createBy" />
<el-table-column label="上传时间" align="center" prop="createTime" width="180">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.createTime, '{y}-{m}-{d}') }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-download"
@click="handleDownload(scope.row)"
v-hasPermi="['project:doc:download']"
>下载</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
v-hasPermi="['project:doc:remove']"
>删除</el-button>
</template>
</el-table-column>
</el-table>
<pagination
v-show="total>0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<!-- 上传文档对话框 -->
<el-dialog :title="uploadTitle" :visible.sync="uploadOpen" width="600px" append-to-body>
<el-form label-width="90px" size="small" style="margin-bottom: 10px;">
<el-form-item label="文档类型">
<el-radio-group v-model="upload.meta.kindType" @change="handleKindTypeChange">
<el-radio :label="0">项目文档</el-radio>
<el-radio :label="1">模块文档</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="所属项目">
<el-select v-model="upload.meta.projectId" placeholder="请选择项目" filterable @change="handleProjectChange">
<el-option v-for="p in projectOptions" :key="p.projectId" :label="p.projectName" :value="p.projectId"/>
</el-select>
</el-form-item>
<el-form-item v-if="upload.meta.kindType === 1" label="所属模块">
<el-select v-model="upload.meta.moduleId" placeholder="请选择模块" filterable :disabled="!moduleOptions.length">
<el-option v-for="m in moduleOptions" :key="m.moduleId" :label="m.moduleName" :value="m.moduleId"/>
</el-select>
</el-form-item>
</el-form>
<el-upload
ref="upload"
:limit="1"
accept=".pdf,.doc,.docx,.txt"
:action="upload.url"
:headers="upload.headers"
:file-list="upload.fileList"
:data="upload.data"
:on-progress="handleFileUploadProgress"
:on-success="handleFileSuccess"
:auto-upload="false"
drag
>
<i class="el-icon-upload"></i>
<div class="el-upload__text">
将文件拖到此处<em>点击上传</em>
</div>
<div class="el-upload__tip" slot="tip">
<div>
<strong>文档类型</strong>{{ upload.meta.kindType === 1 ? '模块文档' : '项目文档' }}<br>
<strong>所属项目</strong>{{ (projectOptions.find(p=>p.projectId===upload.meta.projectId)||{}).projectName || '-' }}<br>
<template v-if="upload.meta.kindType === 1">
<strong>所属模块</strong>{{ (moduleOptions.find(m=>m.moduleId===upload.meta.moduleId)||{}).moduleName || '-' }}
</template>
</div>
<br>
支持格式PDFDOCDOCXTXT
</div>
</el-upload>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitFileForm"> </el-button>
<el-button @click="uploadOpen = false"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { getToken } from '@/utils/auth'
import { listDoc } from '@/api/project/doc'
import { listProject } from '@/api/project/project'
import { listModulesByProject, getMyModules } from '@/api/project/module'
export default {
name: "Doc",
computed: {
isNormalUser() {
const roles = this.$store.state.user.roles || []
return roles.some(r => (typeof r === 'string' ? r : r.roleKey) === 'normal_user')
}
},
data() {
return {
// 遮罩层
loading: true,
// 选中数组
ids: [],
// 非多个禁用
multiple: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 文档表格数据
docList: [],
// 弹出层标题
uploadTitle: "",
// 是否显示弹出层
uploadOpen: false,
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
docName: null,
createBy: null,
// 查询条件:根据上下文映射到 projectId 或 moduleId
projectId: null,
moduleId: null,
kind_type: null, // 文档类型0=项目文档1=模块文档
},
// 当前上下文信息
currentContext: {
pmodel_id: null,
kind_type: null,
moduleName: null,
projectId: null,
projectName: null,
isViewMode: false
},
// 上传参数
upload: {
// 是否禁用上传
isUploading: false,
// 设置上传的请求头部
headers: { Authorization: "Bearer " + getToken() },
// 上传的地址
url: process.env.VUE_APP_BASE_API + "/project/doc/upload",
// 上传的文件列表
fileList: [],
// 上传时的额外参数
data: {},
// 选择元数据不直接污染data最终同步到data里提交
meta: {
kindType: 0,
projectId: null,
moduleId: null,
}
},
// 下拉选项
projectOptions: [],
moduleOptions: []
};
},
created() {
// 处理路由参数
this.handleRouteParams();
this.getList();
// 初始化项目下拉(仅加载当前用户相关项目:后端会做数据权限控制)
this.initProjects();
},
methods: {
async initProjects() {
try {
if (this.isNormalUser) {
// 普通用户:从“我的模块”汇总所属项目
const { rows } = await getMyModules({ pageNum: 1, pageSize: 9999 })
const map = new Map()
;(rows || []).forEach(m => {
if (m.projectId && m.projectName && !map.has(m.projectId)) {
map.set(m.projectId, { projectId: m.projectId, projectName: m.projectName })
}
})
this.projectOptions = Array.from(map.values())
} else {
// 管理员/项目管理员:读取全部可管理项目
const { rows } = await listProject({ pageNum: 1, pageSize: 9999 })
this.projectOptions = rows || []
}
} catch (e) {
this.projectOptions = []
}
},
/** 处理路由参数 */
handleRouteParams() {
const query = this.$route.query;
console.log('路由参数:', query); // 调试信息
if (query.pmodel_id) {
this.currentContext.pmodel_id = query.pmodel_id;
this.queryParams.pmodel_id = query.pmodel_id;
this.upload.data.pmodel_id = query.pmodel_id;
}
if (query.kind_type !== undefined && query.kind_type !== null) {
// 确保kind_type是数字类型
this.currentContext.kind_type = parseInt(query.kind_type);
this.queryParams.kind_type = parseInt(query.kind_type);
this.upload.data.kind_type = parseInt(query.kind_type);
}
if (query.moduleName) {
this.currentContext.moduleName = query.moduleName;
}
if (query.projectId) {
this.currentContext.projectId = query.projectId;
}
if (query.projectName) {
this.currentContext.projectName = query.projectName;
}
if (query.view === 'true') {
this.currentContext.isViewMode = true;
}
console.log('处理后的上下文:', this.currentContext); // 调试信息
},
handleKindTypeChange() {
if (this.upload.meta.kindType === 0) {
this.upload.meta.moduleId = null;
}
},
async handleProjectChange(projectId) {
this.upload.meta.moduleId = null;
if (!projectId) { this.moduleOptions = []; return; }
try {
if (this.isNormalUser) {
// 从“我的模块”中过滤出所选项目的可见模块
const { rows } = await getMyModules({ pageNum: 1, pageSize: 9999 })
this.moduleOptions = (rows || []).filter(m => String(m.projectId) === String(projectId))
} else {
const { rows } = await listModulesByProject(projectId)
this.moduleOptions = rows || []
}
} catch (e) {
this.moduleOptions = []
}
},
/** 查询文档列表 */
async getList() {
this.loading = true;
// 构建查询参数根据上下文映射pmodel_id -> projectId/moduleId
const query = { ...this.queryParams };
if (!query.projectId && !query.moduleId && this.currentContext.pmodel_id) {
if (this.currentContext.kind_type === 0) {
query.projectId = this.currentContext.pmodel_id;
} else if (this.currentContext.kind_type === 1) {
query.moduleId = this.currentContext.pmodel_id;
}
}
// 参数命名转换kind_type -> kindType后端为驼峰
if (query.kind_type !== undefined) {
query.kindType = query.kind_type;
delete query.kind_type;
}
try {
const { rows, total } = await listDoc(query);
this.docList = rows || [];
this.total = total || 0;
} catch (e) {
console.error('获取文档列表失败', e);
this.docList = [];
this.total = 0;
} finally {
this.loading = false;
}
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
// 多选框选中数据
handleSelectionChange(selection) {
this.ids = selection.map(item => item.docId);
this.multiple = !selection.length;
},
/** 上传按钮操作 */
handleUpload() {
if (this.currentContext.moduleName) {
this.uploadTitle = `上传文档 - ${this.currentContext.moduleName}`;
} else {
this.uploadTitle = "上传文档";
}
this.uploadOpen = true;
},
/** 下载按钮操作 */
async handleDownload(row) {
const url = process.env.VUE_APP_BASE_API + '/project/doc/download/' + row.docId
try {
const res = await fetch(url, { headers: { Authorization: 'Bearer ' + getToken() } })
const contentType = res.headers.get('content-type') || ''
if (contentType.includes('application/json')) {
const j = await res.json()
this.$message.error(j.msg || '下载失败')
return
}
const blob = await res.blob()
const a = document.createElement('a')
a.href = window.URL.createObjectURL(blob)
a.download = row.docName || 'file'
document.body.appendChild(a)
a.click()
a.remove()
} catch (e) {
this.$message.error('下载失败')
}
},
/** 删除按钮操作 */
handleDelete(row) {
const docIds = row.docId || this.ids;
this.$modal.confirm('是否确认删除文档编号为"' + docIds + '"的数据项?').then(() => {
// 这里应该是删除API调用
this.getList();
this.$modal.msgSuccess("删除成功");
});
},
// 文件上传中处理
handleFileUploadProgress(event, file, fileList) {
this.upload.isUploading = true;
},
// 文件上传成功处理
handleFileSuccess(response, file, fileList) {
this.upload.open = false;
this.upload.isUploading = false;
this.$refs.upload.clearFiles();
this.$alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + "</div>", "导入结果", { dangerouslyUseHTMLString: true });
this.getList();
},
// 提交上传文件
submitFileForm() {
// 将选择的元数据同步到上传表单字段
const { kindType, projectId, moduleId } = this.upload.meta;
this.upload.data.kindType = kindType;
this.upload.data.projectId = projectId;
if (kindType === 1) {
this.upload.data.moduleId = moduleId;
} else {
delete this.upload.data.moduleId;
}
// 简单校验
if (!projectId) {
this.$modal.msgError('请选择所属项目');
return;
}
if (kindType === 1 && !moduleId) {
this.$modal.msgError('请选择所属模块');
return;
}
this.$refs.upload.submit();
}
}
};
</script>