This commit is contained in:
2025-09-07 17:29:59 +08:00
parent 0cb1c34755
commit f080c0c4a7
7 changed files with 293 additions and 234 deletions

View File

@@ -73,8 +73,8 @@ export default {
title: '项目管理系统',
codeUrl: "",
loginForm: {
username: "admin",
password: "admin123",
username: "jsl",
password: "123456",
rememberMe: false,
code: "",
uuid: ""

View File

@@ -161,9 +161,19 @@ import { listModulesByProject, getMyModules } from '@/api/project/module'
export default {
name: "Doc",
computed: {
// 是否项目管理员/平台管理员
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'
})
},
// 是否普通用户(且不是管理员)
isNormalUser() {
const roles = this.$store.state.user.roles || []
return roles.some(r => (typeof r === 'string' ? r : r.roleKey) === 'normal_user')
const hasNormal = roles.some(r => (typeof r === 'string' ? r : r.roleKey) === 'normal_user')
return hasNormal && !this.isProjectAdmin
}
},
data() {
@@ -238,7 +248,11 @@ export default {
methods: {
async initProjects() {
try {
if (this.isNormalUser) {
if (this.isProjectAdmin) {
// 管理员/项目管理员:读取全部可管理项目
const { rows } = await listProject({ pageNum: 1, pageSize: 9999 })
this.projectOptions = rows || []
} else if (this.isNormalUser) {
// 普通用户:从“我的模块”汇总所属项目
const { rows } = await getMyModules({ pageNum: 1, pageSize: 9999 })
const map = new Map()
@@ -248,10 +262,6 @@ export default {
}
})
this.projectOptions = Array.from(map.values())
} else {
// 管理员/项目管理员:读取全部可管理项目
const { rows } = await listProject({ pageNum: 1, pageSize: 9999 })
this.projectOptions = rows || []
}
} catch (e) {
this.projectOptions = []
@@ -297,7 +307,7 @@ export default {
this.upload.meta.moduleId = null;
if (!projectId) { this.moduleOptions = []; return; }
try {
if (this.isNormalUser) {
if (!this.isProjectAdmin && this.isNormalUser) {
// 从“我的模块”中过滤出所选项目的可见模块
const { rows } = await getMyModules({ pageNum: 1, pageSize: 9999 })
this.moduleOptions = (rows || []).filter(m => String(m.projectId) === String(projectId))

View File

@@ -36,59 +36,13 @@
<div style="margin-top: 10px; color: #666;">
当前用户{{ userName }}ID: {{ userId }} | 用户角色{{ userRoles }}
</div>
<!-- 调试信息开发环境显示 -->
<div v-if="false" style="margin-top: 10px; color: #999; font-size: 12px;">
调试信息: Store状态={{ JSON.stringify($store.state.user) }}
</div>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="el-icon-plus"
size="mini"
@click="handleAdd"
v-hasPermi="['project:module:add']"
>新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="el-icon-edit"
size="mini"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['project:module:edit']"
>修改</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:module:remove']"
>删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-download"
size="mini"
@click="handleExport"
v-hasPermi="['project:module:export']"
>导出</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="moduleList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="模块ID" align="center" prop="moduleId" />
@@ -101,7 +55,7 @@
</el-table-column>
<el-table-column label="接取人" align="center" prop="assignee">
<template slot-scope="scope">
{{ userMap[String(scope.row.assignee)] || scope.row.assignee || '未指派' }}
{{ scope.row.assignee || '未指派' }}
</template>
</el-table-column>
<el-table-column label="接取时间" align="center" prop="assignTime" width="180">
@@ -169,7 +123,7 @@
</template>
</el-table-column>
</el-table>
<div v-if="!loading && moduleList.length === 0" class="empty-state">
<i class="el-icon-document-remove" style="font-size: 100px; color: #dcdfe6;"></i>
<p>暂无被指派的模块</p>
@@ -188,24 +142,15 @@
<script>
import { getMyModules, claimModule, giveupModule, completeModule } from "@/api/project/module"
import { listUser } from "@/api/system/user"
export default {
name: "MyModules",
dicts: ['module_status'],
computed: {
// 判断是否为项目管理员
isProjectAdmin() {
return this.$store.state.user.roles &&
this.$store.state.user.roles.some(role =>
(typeof role === 'string' && (role === 'platform_admin' || role === 'project_admin')) ||
(typeof role === 'object' && (role.roleKey === 'platform_admin' || role.roleKey === 'project_admin'))
);
},
// 判断是否为普通用户
isNormalUser() {
return this.$store.state.user.roles &&
this.$store.state.user.roles.some(role =>
this.$store.state.user.roles.some(role =>
(typeof role === 'string' && role === 'normal_user') ||
(typeof role === 'object' && role.roleKey === 'normal_user')
);
@@ -231,7 +176,6 @@ export default {
userName: this.$store.state.user.name,
userId: this.$store.state.user.id,
userRoles: '无角色信息',
userMap: {},
// 查询参数
queryParams: {
pageNum: 1,
@@ -244,10 +188,6 @@ export default {
},
created() {
this.buildUserRolesText();
// 仅管理员加载全量用户映射,普通用户避免触发无权限接口
if (this.isProjectAdmin) {
this.loadUserMap();
}
this.getList();
},
methods: {
@@ -272,16 +212,6 @@ export default {
});
this.userRoles = names.join('') || '无角色信息';
},
/** 载入用户映射userId -> 中文名 */
loadUserMap() {
listUser({ pageNum: 1, pageSize: 9999 }).then(res => {
const rows = (res && res.rows) ? res.rows : [];
this.userMap = rows.reduce((acc, u) => {
acc[String(u.userId)] = u.nickName || u.userName || String(u.userId);
return acc;
}, {});
});
},
/** 查询我的模块列表 */
getList() {
this.loading = true;
@@ -355,8 +285,8 @@ export default {
// 跳转到文档上传页面传递模块ID和类型
this.$router.push({
path: '/project/doc',
query: {
pmodel_id: row.moduleId,
query: {
pmodel_id: row.moduleId,
kind_type: 1, // 1表示模块文档
moduleName: row.moduleName,
projectId: row.projectId,

View File

@@ -34,12 +34,11 @@
</el-date-picker>
</el-form-item>
<el-form-item label="项目标签" prop="projectLabel">
<el-input
v-model="queryParams.projectLabel"
placeholder="请输入项目标签"
clearable
@keyup.enter.native="handleQuery"
/>
<el-select v-model="queryParams.projectLabel" placeholder="请选择项目标签" clearable>
<el-option label="项目任务" value="项目任务"/>
<el-option label="项目研究" value="项目研究"/>
<el-option label="项目学习" value="项目学习"/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
@@ -129,7 +128,7 @@
size="mini"
type="text"
icon="el-icon-edit"
@click="handleUpdate(scope.row)"
@click.stop="handleUpdate(scope.row)"
v-hasPermi="['project:project:edit']"
>修改
</el-button>
@@ -137,7 +136,7 @@
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
@click.stop="handleDelete(scope.row)"
v-hasPermi="['project:project:remove']"
>删除
</el-button>
@@ -167,6 +166,18 @@
>新增
</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-delete"
size="mini"
:disabled="moduleMultiple"
@click="handleModelsDelete"
v-hasPermi="['project:module:remove']"
>批量删除
</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table :data="moduleList"
@@ -186,7 +197,7 @@
<el-table-column label="操作">
<template #default="{ row }">
<el-button size="mini" type="text" icon="el-icon-edit" @click="editModule(row)" v-hasPermi="['project:module:edit']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="deleteModule(row)" v-hasPermi="['project:module:remove']">删除</el-button>
<el-button v-if="row.status === '0'" size="mini" type="text" icon="el-icon-delete" @click="deleteModule(row)" v-hasPermi="['project:module:remove']">删除</el-button>
<el-button v-if="row.status === '0'" size="mini" type="text" icon="el-icon-position" @click="openAssign(row)" v-hasPermi="['project:module:assign']">指派</el-button>
</template>
</el-table-column>
@@ -226,7 +237,11 @@
</el-date-picker>
</el-form-item>
<el-form-item label="项目标签" prop="projectLabel">
<el-input v-model="form.projectLabel" placeholder="请输入项目标签"/>
<el-select v-model="form.projectLabel" placeholder="请选择项目标签">
<el-option label="项目任务" value="项目任务"/>
<el-option label="项目研究" value="项目研究"/>
<el-option label="项目学习" value="项目学习"/>
</el-select>
</el-form-item>
<el-form-item label="起始日期" prop="startTime">
<el-date-picker clearable
@@ -245,9 +260,7 @@
<el-option label="进行中" value="0"/>
<el-option label="已完成" value="1"/>
</el-select>
</el-form-item>
<br>
<el-button type="primary" icon="el-icon-search" size="mini" @click="openProjectUpload">上传文件</el-button>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
@@ -279,17 +292,20 @@
<!-- 完成模块 + 上传文件弹窗 -->
<el-dialog title="完成模块并上传附件" :visible.sync="finishDialog" width="500px">
<el-upload
ref="uploader"
class="upload-demo"
action=""
:limit="5"
:file-list="fileList"
:on-change="handleFileChange"
:action="upload.url"
:headers="upload.headers"
:file-list="upload.fileList"
:data="upload.data"
:before-upload="beforeUpload"
multiple
:on-progress="handleFileUploadProgress"
:on-success="handleFileSuccess"
:auto-upload="false"
accept=".pdf,.doc,.docx,.txt,.md"
>
<el-button size="small" type="primary">点击上传</el-button>
<div slot="tip" class="el-upload__tip">支持多文件上传文件大小不超过 20MB</div>
<el-button size="small" type="primary">选择文件</el-button>
<div slot="tip" class="el-upload__tip">支持 PDFDOCDOCXTXTMD文件20MB</div>
</el-upload>
<div slot="footer">
<el-button @click="finishDialog=false"> </el-button>
@@ -308,7 +324,8 @@ import Cookies from "js-cookie";
import {getUserRole} from "@/api/system/role";
import {getSysUser} from "@/api/system/user";
import moment from "moment";
import {addDoc, downloadDoc} from "@/api/doc/doc";
import { downloadDoc } from "@/api/doc/doc";
import { getToken } from "@/utils/auth";
export default {
name: "Project",
@@ -354,10 +371,13 @@ export default {
status:0,
finishTime: null,
},
fileList: [], // 文件列表
//上传文件信息
uploadFiles: [],
// 统一上传配置
upload: {
url: process.env.VUE_APP_BASE_API + "/project/doc/upload",
headers: { Authorization: "Bearer " + getToken() },
fileList: [],
data: {}
},
//模块校验
modelsForm: {
@@ -396,6 +416,8 @@ export default {
//模块抽屉
drawer: false,
moduleList: [],
// 多选保存行数据,用于批量删除时判断状态
selectedModules: [],
uploadTarget: '', // 'project' | 'module'
@@ -423,99 +445,77 @@ export default {
},
watch: {
'form.startTime'(nv) {
if (!this.form.projectId) {
const today = moment().startOf('day')
if (nv) {
const st = moment(nv, 'YYYY-MM-DD')
const today = moment().startOf('day')
if (nv) {
const st = moment(nv, 'YYYY-MM-DD')
if (this.form.status !== '1') {
this.form.status = st.isAfter(today) ? '2' : '0'
} else {
}
} else {
if (this.form.status !== '1') {
this.form.status = '0'
}
}
}
},
methods: {
//批量删除模块
//批量删除模块仅删除待接取status === '0'
handleModelsDelete() {
if (this.moduleIds.length === 0) return
this.$modal.confirm(`确定删除选中的 ${this.moduleIds.length} 条模块?`)
const deletableIds = this.selectedModules
.filter(item => String(item.status) === '0')
.map(item => item.moduleId)
const notDeletableCount = this.selectedModules.length - deletableIds.length
if (deletableIds.length === 0) {
this.$modal.msgError('请选择状态为“待接取”的模块进行删除')
return
}
const tip = notDeletableCount > 0
? `确定删除选中的 ${deletableIds.length} 条未接取模块?(其中 ${notDeletableCount} 条进行中或已完成,无法删除)`
: `确定删除选中的 ${deletableIds.length} 条模块?`
this.$modal.confirm(tip)
.then(() => {
// 后端若支持批量return delModules(this.moduleIds.join(','))
// 后端只支持单条:循环 Promise
const promises = this.moduleIds.map(id => delModels(id))
const promises = deletableIds.map(id => delModels(id))
return Promise.all(promises)
})
.then(() => {
this.$message.success('删除成功')
// 前端数组剔除已删记录,实现无刷新
this.moduleList = this.moduleList.filter(
m => !this.moduleIds.includes(m.moduleId)
)
this.moduleIds = [] // 清空选中
this.moduleMultiple = true // 重置按钮状态
})
.catch(() => {
this.moduleList = this.moduleList.filter(m => !deletableIds.includes(m.moduleId))
this.moduleIds = []
this.selectedModules = []
this.moduleMultiple = true
})
.catch(() => {})
},
// 1. 勾选回调
handleModuleSelectionChange(selection) {
this.selectedModules = selection
this.moduleIds = selection.map(item => item.moduleId)
this.kindType = 1;
console.log(this.moduleIds[0])
this.moduleMultiple = selection.length === 0 // 没勾选就禁用按钮
},
//上传单个文件
uploadSingleFile(fileObj) {
addDoc(fileObj).then(res => {
// console.log(res);
if (res.code === 200) {
console.log(`文件 ${fileObj.fileName} 上传成功`);
}
})
// 上传回调
handleFileUploadProgress() {},
handleFileSuccess() {
this.$message.success(this.uploadTarget === 'project' ? '项目文档上传成功' : '模块文档上传成功')
this.finishDialog = false
if (this.uploadTarget === 'module') {
this.getModuleList()
}
},
// 文件选择后触发:异步读取所有文件,并构建数组
handleFileChange(file, fileList) {
this.fileList = fileList
this.uploadFiles = []
const targetId = this.uploadTarget === 'project'
? this.form.projectId
: this.modelsForm.moduleId
const kindType = (this.uploadTarget === 'project' ? '0' : '1');
const promises = fileList.map(item => {
return new Promise((resolve, reject) => {
const reader = new FileReader()
const rawFile = item.raw
reader.readAsDataURL(rawFile)
reader.onload = e => {
const base64Str = e.target.result.split(',')[1]
resolve({
pmoduleId: targetId,
fileName: rawFile.name,
fileContent: base64Str,
uploadBy: Cookies.get('username'),
kindType: kindType,
uploadTime: moment().format('YYYY-MM-DD HH:mm:ss')
})
}
reader.onerror = reject
})
})
Promise.all(promises)
.then(res => {
this.uploadFiles = res
// console.log("文件:",this.uploadFiles)
})
.catch(err => {
this.$message.error('文件读取失败:' + err)
})
// 触发上传
triggerUpload() {
if (this.$refs.uploader) this.$refs.uploader.submit()
},
// 上传前校验
@@ -529,27 +529,7 @@ export default {
/* 点击弹窗“确定”时调用 */
submitUploadFiles() {
if (this.uploadFiles.length === 0) {
this.getModuleList();
this.finishDialog = false
return
}
const promises = this.uploadFiles.map(file => this.uploadSingleFile(file))
Promise.all(promises)
.then(() => {
this.$message.success(
this.uploadTarget === 'project' ? '项目文档上传成功' : '模块文档上传成功'
)
this.finishDialog = false
if (this.uploadTarget === 'module') {
this.getModuleList()
}
})
.catch(err => {
this.$message.error('部分文件上传失败')
console.error(err)
})
this.triggerUpload()
},
//上传文件并完成模块
@@ -570,8 +550,8 @@ export default {
//项目上传文件
openProjectUpload() {
this.uploadTarget = 'project'
this.fileList = []
this.uploadFiles = []
this.upload.fileList = []
this.upload.data = { kindType: 0, projectId: this.form.projectId }
this.finishDialog = true
},
//完成模块
@@ -581,8 +561,8 @@ export default {
this.finishDialog = true;
this.modelsForm.moduleId = row.moduleId
this.modelsForm.status = 2;
this.fileList = []
this.uploadFiles = []
this.upload.fileList = []
this.upload.data = { kindType: 1, projectId: this.projectId, moduleId: row.moduleId }
this.modelsForm.finishTime = moment().format('YYYY-MM-DD HH:mm:ss');
},
@@ -850,6 +830,10 @@ export default {
},
// 删除模块
deleteModule(row) {
if (String(row.status) !== '0') {
this.$modal.msgError('进行中或已完成的模块无法删除')
return
}
const id = row.moduleId
this.$modal.confirm('是否确认删除模块编号为"' + id + '"的数据项?').then(() => {
return delModels(id)