完善后台管理与测试支撑

1、为后台用户列表增加关键词和状态筛选,后端同步支持过滤与总数统计。

2、创建用户时要求填写初始密码,并补充前端校验和自动填充隔离。

3、优化项目管理列表角色展示、项目详情抽屉脏数据保护和审计详情抽屉关闭体验。

4、清理旧费用附件关联、统一测试模型注册,并更新迁移端点和注册筛选测试。
This commit is contained in:
Cheng Zhou
2026-06-04 11:12:40 +08:00
parent 90ca061db8
commit 41968384b5
17 changed files with 337 additions and 145 deletions
+33 -7
View File
@@ -63,8 +63,8 @@
<!-- 项目表格 -->
<div class="unified-section project-table-section">
<el-table :data="projects" v-loading="loading" class="project-table" table-layout="fixed">
<el-table-column :label="TEXT.common.fields.projectName" min-width="220">
<el-table :data="projects" v-loading="loading" class="project-table" style="width: 100%" table-layout="fixed">
<el-table-column :label="TEXT.common.fields.projectName">
<template #default="scope">
<div class="project-cell">
<div class="project-icon" :class="'icon--' + (scope.row.status || 'draft').toLowerCase()">
@@ -86,7 +86,12 @@
<span class="text-muted">{{ scope.row.protocol_no || '-' }}</span>
</template>
</el-table-column>
<el-table-column :label="TEXT.common.fields.status" width="110" align="center">
<el-table-column v-if="!isAdmin" label="我的角色" align="center">
<template #default="scope">
<span class="role-badge">{{ roleLabel(scope.row.role_in_study) || '-' }}</span>
</template>
</el-table-column>
<el-table-column :label="TEXT.common.fields.status" align="center">
<template #default="scope">
<span class="status-badge" :class="'badge--' + (scope.row.status || 'draft').toLowerCase()">
<span class="badge-dot"></span>
@@ -94,7 +99,7 @@
</span>
</template>
</el-table-column>
<el-table-column label="锁定状态" width="100" align="center">
<el-table-column label="锁定状态" align="center">
<template #default="scope">
<span v-if="scope.row.is_locked" class="lock-indicator locked">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="13" height="13">
@@ -112,7 +117,7 @@
</span>
</template>
</el-table-column>
<el-table-column :label="TEXT.common.labels.actions" width="200" align="center" fixed="right">
<el-table-column :label="TEXT.common.labels.actions" align="center">
<template #default="scope">
<div class="action-row">
<el-tooltip v-if="canProject(scope.row, 'project_members', 'read')" :content="TEXT.modules.adminProjects.members" placement="top">
@@ -159,6 +164,7 @@ import { useStudyStore } from "../../store/study";
import { useAuthStore } from "../../store/auth";
import { isSystemAdmin } from "../../utils/roles";
import { isApiPermissionAllowed } from "../../utils/apiPermissionValue";
import { useRoleTemplateMeta } from "../../composables/useRoleTemplateMeta";
import { TEXT } from "../../locales";
const projects = ref<Study[]>([]);
@@ -170,11 +176,11 @@ const studyStore = useStudyStore();
const auth = useAuthStore();
const isAdmin = computed(() => isSystemAdmin(auth.user));
const permissionsByProject = ref<Record<string, ApiEndpointPermissionsResponse>>({});
const { roleLabel, loadRoleTemplates } = useRoleTemplateMeta();
const activeProjectCount = computed(() => projects.value.filter(p => p.status === 'ACTIVE').length);
const draftProjectCount = computed(() => projects.value.filter(p => p.status === 'DRAFT').length);
const lockedProjectCount = computed(() => projects.value.filter(p => p.is_locked).length);
const loadProjects = async () => {
loading.value = true;
try {
@@ -208,7 +214,7 @@ const openCreate = () => {
};
const MODULE_TO_OPERATION: Record<string, Record<string, string>> = {
project_members: { read: "project_members:list", write: "project_members:update" },
project_members: { read: "project_members:read", write: "project_members:update" },
sites: { read: "sites:read", write: "sites:update" },
audit_export: { read: "audit_logs:read", write: "audit_logs:export" },
setup_config: { read: "setup_config:read", write: "setup_config:update" },
@@ -346,6 +352,7 @@ const statusLabel = (status: string) =>
TEXT.enums.projectStatus[status as keyof typeof TEXT.enums.projectStatus] || status;
onMounted(() => {
loadRoleTemplates().catch(() => {});
loadProjects();
});
</script>
@@ -471,6 +478,25 @@ onMounted(() => {
font-size: 13px;
}
.role-badge {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 64px;
max-width: 104px;
min-height: 28px;
padding: 4px 10px;
border-radius: 6px;
background: var(--ctms-neutral-100);
color: var(--ctms-text-primary);
font-size: 12px;
font-weight: 600;
line-height: 1.2;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* 状态徽章 */
.status-badge {
display: inline-flex;