Files
projectSystem/ruoyi-ui/src/views/project/doc/index.vue

492 lines
18 KiB
Vue
Raw Normal View History

2025-09-07 15:59:40 +08:00
<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">
2025-09-11 14:37:55 +08:00
<el-radio :label="0" :disabled="isNormalUser">项目文档</el-radio>
2025-09-07 15:59:40 +08:00
<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>
2025-09-11 14:37:55 +08:00
<strong>所属项目</strong>{{ (projectOptions.find(p=>String(p.projectId)===String(upload.meta.projectId))||{}).projectName || '-' }}<br>
2025-09-07 15:59:40 +08:00
<template v-if="upload.meta.kindType === 1">
2025-09-11 14:37:55 +08:00
<strong>所属模块</strong>{{ (moduleOptions.find(m=>String(m.moduleId)===String(upload.meta.moduleId))||{}).moduleName || '-' }}
2025-09-07 15:59:40 +08:00
</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'
2025-09-08 10:43:38 +08:00
import { listDoc, delDoc } from '@/api/project/doc'
2025-09-07 15:59:40 +08:00
import { listProject } from '@/api/project/project'
import { listModulesByProject, getMyModules } from '@/api/project/module'
export default {
name: "Doc",
computed: {
2025-09-07 17:29:59 +08:00
// 是否项目管理员/平台管理员
isProjectAdmin() {
const roles = this.$store.state.user.roles || []
return roles.some(r => {
const key = typeof r === 'string' ? r : r.roleKey
return key === 'project_admin' || key === 'platform_admin'
})
},
// 是否普通用户(且不是管理员)
2025-09-07 15:59:40 +08:00
isNormalUser() {
const roles = this.$store.state.user.roles || []
2025-09-07 17:29:59 +08:00
const hasNormal = roles.some(r => (typeof r === 'string' ? r : r.roleKey) === 'normal_user')
return hasNormal && !this.isProjectAdmin
2025-09-12 10:35:17 +08:00
},
// 当前用户ID用于前端过滤项目管理员仅看见自己创建的项目
currentUserId() {
const u = this.$store.state.user || {}
return u.userId || u.user_id || u.id || null
2025-09-07 15:59:40 +08:00
}
},
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 {
2025-09-07 17:29:59 +08:00
if (this.isProjectAdmin) {
2025-09-12 10:35:17 +08:00
// 项目管理员:仅显示自己创建的项目
const projRes = await listProject({ pageNum: 1, pageSize: 9999 })
const owned = (projRes && Array.isArray(projRes.rows))
? projRes.rows.filter(p => String(p.ownerId) === String(this.currentUserId))
: []
this.projectOptions = owned.map(p => ({ projectId: p.projectId, projectName: p.projectName || (`项目#${p.projectId}`) }))
2025-09-11 14:12:22 +08:00
} else {
2025-09-12 10:35:17 +08:00
// 普通用户:根据“我的模块”推导可见项目
const myModRes = await getMyModules({ pageNum: 1, pageSize: 9999 })
const map = new Map()
if (myModRes && Array.isArray(myModRes.rows)) {
myModRes.rows.forEach(m => {
if (m.projectId && !map.has(m.projectId)) {
map.set(m.projectId, { projectId: m.projectId, projectName: m.projectName || (`项目#${m.projectId}`) })
}
})
}
// 注:普通用户可保留路由上下文补入,管理员不补入非本人项目
if (this.currentContext.projectId && !map.has(this.currentContext.projectId)) {
map.set(this.currentContext.projectId, { projectId: this.currentContext.projectId, projectName: this.currentContext.projectName || (`项目#${this.currentContext.projectId}`) })
}
this.projectOptions = Array.from(map.values())
2025-09-09 10:24:33 +08:00
}
2025-09-12 10:35:17 +08:00
// 如果之前没选过,按上下文预选(仅当选项中存在)
2025-09-11 14:12:22 +08:00
if (!this.upload.meta.projectId && this.currentContext.projectId) {
2025-09-12 10:35:17 +08:00
const exists = this.projectOptions.some(p => String(p.projectId) === String(this.currentContext.projectId))
this.upload.meta.projectId = exists ? this.currentContext.projectId : null
2025-09-09 10:24:33 +08:00
}
if (this.upload.meta.projectId) {
await this.handleProjectChange(this.upload.meta.projectId)
2025-09-11 14:12:22 +08:00
} else {
this.moduleOptions = []
this.upload.meta.moduleId = null
2025-09-07 15:59:40 +08:00
}
} 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) {
2025-09-09 10:24:33 +08:00
// 统一为数字,避免 el-select 由于类型不一致显示原始ID
this.currentContext.projectId = parseInt(query.projectId);
2025-09-07 15:59:40 +08:00
}
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 {
2025-09-07 17:29:59 +08:00
if (!this.isProjectAdmin && this.isNormalUser) {
2025-09-07 15:59:40 +08:00
// 从“我的模块”中过滤出所选项目的可见模块
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;
},
/** 上传按钮操作 */
2025-09-09 10:24:33 +08:00
async handleUpload() {
2025-09-07 15:59:40 +08:00
if (this.currentContext.moduleName) {
this.uploadTitle = `上传文档 - ${this.currentContext.moduleName}`;
} else {
this.uploadTitle = "上传文档";
}
2025-09-11 14:12:22 +08:00
// 每次打开均刷新一次项目集合,避免角色/指派变更导致的偶发不显示
await this.initProjects()
2025-09-12 10:35:17 +08:00
// 普通用户默认选择“模块文档”,并禁用“项目文档”的单选
if (this.isNormalUser && !this.isProjectAdmin) {
this.upload.meta.kindType = 1
}
2025-09-09 10:24:33 +08:00
// 预选:优先使用路由上下文;否则选第一项
if (this.currentContext.projectId) {
this.upload.meta.projectId = this.currentContext.projectId
} else {
// 默认不选任何项目避免显示成固定ID
this.upload.meta.projectId = null
}
2025-09-11 14:12:22 +08:00
// initProjects 内已处理联动
2025-09-07 15:59:40 +08:00
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('下载失败')
}
},
2025-09-08 10:43:38 +08:00
/** 删除按钮操作(支持单条与批量) */
2025-09-07 15:59:40 +08:00
handleDelete(row) {
2025-09-08 10:43:38 +08:00
const docIds = row && row.docId ? row.docId : (this.ids && this.ids.length ? this.ids.join(',') : '');
if (!docIds) {
this.$modal.msgError('请先选择要删除的文档');
return;
}
this.$modal
.confirm('是否确认删除文档编号为"' + docIds + '"的数据项?')
.then(() => delDoc(docIds))
.then(() => {
this.$modal.msgSuccess('删除成功');
this.getList();
this.ids = [];
this.multiple = true;
})
.catch(() => {});
2025-09-07 15:59:40 +08:00
},
// 文件上传中处理
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>