feat(front): 项目/会议管理 admin 端补齐 + 删除按钮 + 级联 UI + 角色劳务组件

admin 会议管理:
- 新建 admin/AdminMeetings.vue (查看/删除/批量删除, 面包屑 首页/项目管理/会议管理)
- AdminLayout.vue 加 /admin/meetings 菜单
- router/index.js 加 admin-meetings + admin-meetings-detail 路由
- MeetingDetail.vue admin 删除按钮改 admin+manager 双角色可触发 (onAdminDelete 跳回按角色)

manager 会议管理:
- Meetings.vue 加批量删除 + 删除按钮 + 删除确认文案 (6 张表级联)
- MeetingDetail.vue 删除按钮 v-if 改 canDeleteMeeting = isAdmin || isManager

manager 项目管理:
- Projects.vue 加批量删除 + 删除项 + doDelete 改走 cascade (5 张项目表 + 会议链)
- ProjectsNew.vue style-alignment: page-card 居左 (max-width 1200px, 无 margin auto), form-actions 包在 el-form 内
- Projects.vue + Meetings.vue + AdminMeetings.vue 删页内冗余删除提示

store + 组件:
- store/user.js 加 isAdmin getter
- components/ProjectRoleSelect.vue 新建 (项目级角色劳务下拉, 接 roleLabor)
This commit is contained in:
郭庆泰
2026-08-22 20:16:46 +08:00
parent 0eb5da6ea8
commit 2eb2d1a43b
10 changed files with 582 additions and 31 deletions
+48 -2
View File
@@ -78,6 +78,9 @@
<el-button class="action-btn secondary" :disabled="!selection.length" @click="openBatch('activate')">
批量开通<span v-if="selection.length" class="badge">{{ selection.length }}</span>
</el-button>
<el-button v-if="canDelete" class="action-btn secondary" type="danger" :disabled="!selection.length" @click="onBatchDelete">
批量删除<span v-if="selection.length" class="badge">{{ selection.length }}</span>
</el-button>
</div>
<el-table :data="rows" v-loading="loading" stripe border @selection-change="onSel">
<el-table-column type="selection" width="44" />
@@ -131,6 +134,7 @@
<el-dropdown-item command="exportExperts" divided>导出报名专家</el-dropdown-item>
<el-dropdown-item v-if="row.isPublished === '1' || row.publishUrl" command="activate" divided>开通</el-dropdown-item>
<el-dropdown-item v-if="row.isPublished === '1' || row.publishUrl" command="delAnn" divided>删除公告</el-dropdown-item>
<el-dropdown-item v-if="canDelete" command="delete" divided>删除项目</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
@@ -269,7 +273,13 @@ function onAction(cmd, row) {
else if (cmd === 'exportExperts') exportExperts(row)
else if (cmd === 'activate') openActivate(row)
else if (cmd === 'delAnn') openDeleteAnnouncement(row)
else if (cmd === 'delete') doDelete(row)
}
// 仅 admin / manager 可见删除按钮 (前端 v-if, 后端 controller 再兜底 role 校验)
const canDelete = computed(() => {
const r = userStore.role
return r === 'admin' || r === 'manager'
})
function doCreateMeeting(row) { router.push(`/manager/meetings/new?projectId=${row.projectId}`) }
function doAssign(row) {
// 跳转独立子页面 /manager/projects/assign?projectId=X
@@ -277,18 +287,54 @@ function doAssign(row) {
}
function doEdit(row) { router.push(`/manager/projects/edit/${row.projectId}`) }
// 删除项目(按需补充,原型无但需要)
// 删除项目 (软删除 + 级联置 5 张项目表 + 项目下所有会议 is_deleted=1)
// 后端 BizProjectController.remove 已强制 role in (admin, manager), 前端只做可见性 v-if
async function doDelete(row) {
try {
await ElMessageBox.confirm(`确认删除项目"${row.projectName}"\n项目编号: ${row.projectNo}\n\n删除后无法恢复!`, '删除确认', { type: 'error', confirmButtonText: '确认删除', cancelButtonText: '取消' })
await ElMessageBox.confirm(
`确认删除项目"${row.projectName}"\n项目编号: ${row.projectNo}\n\n` +
`此操作将级联软删除:\n` +
`· 项目主表 + 方案 + 执行方分配 + 支持方分配 + 评分 (5 张表)\n` +
`· 项目下所有会议 + 会议附件 + 参会人 + 监督员 + 执行人 + 审计日志 (6 张表)\n\n` +
`数据保留在库, 不再展示给前台用户, 审计追溯可查。`,
'删除确认',
{ type: 'error', confirmButtonText: '确认软删除', cancelButtonText: '取消' }
)
await bizDelete('project', row.projectId)
ElMessage.success('已删除')
// 从 selection 里移除已删行, 避免批量按钮残留
selection.value = selection.value.filter(r => r.projectId !== row.projectId)
load()
} catch (e) {
if (e !== 'cancel' && e !== 'close') ElMessage.error(e?.msg || '删除失败')
}
}
// 批量软删除 (admin/manager)
async function onBatchDelete() {
if (!selection.value.length) return ElMessage.warning('请先勾选项目')
try {
await ElMessageBox.confirm(
`确认批量软删除选中的 ${selection.value.length} 个项目?\n\n` +
`此操作将级联软删除每个项目下的:\n` +
`· 5 张项目表 (主表/方案/分配/支持方/评分)\n` +
`· 6 张会议表 (会议本身 + 附件 + 参会人 + 监督员 + 执行人 + 审计日志)\n\n` +
`数据保留, 不再展示给前台用户。`,
'批量删除确认',
{ type: 'error', confirmButtonText: `确认删除 ${selection.value.length}`, cancelButtonText: '取消' }
)
} catch { return }
const ids = selection.value.map(r => r.projectId)
try {
await bizDelete('project', ids)
ElMessage.success(`已批量软删除 ${ids.length} 个项目`)
selection.value = []
load()
} catch (e) {
ElMessage.error(e?.msg || '批量删除失败')
}
}
function reset() { q.value = { sponsorAdminUserIds:[], execAdminUserIds:[], projectNo:'', projectName:'', projectForm:'', startTime:'', endTime:'', isFinished:'', isSettled:'', managerScore:'' }; page.pageNum = 1; load() }
// 导出