未知(继上次中断)

This commit is contained in:
Cheng Zhou
2026-02-04 10:52:34 +08:00
parent 8e258d21a7
commit 737f84bf54
99 changed files with 5497 additions and 2143 deletions
+73 -58
View File
@@ -1,46 +1,34 @@
<template>
<div class="ctms-page">
<div class="ctms-page-header">
<div>
<h1 class="ctms-page-title">{{ TEXT.modules.fileVersionManagement.title }}</h1>
</div>
<div class="ctms-page-actions">
<el-button type="primary" @click="openCreate">
<el-icon class="el-icon--left"><Plus /></el-icon>
{{ TEXT.modules.fileVersionManagement.newDocument }}
</el-button>
</div>
</div>
<el-card class="ctms-filter-card">
<div class="ctms-filter-row">
<div class="ctms-filter-item">
<span class="ctms-filter-label">{{ TEXT.modules.fileVersionManagement.filters.scope }}</span>
<el-select v-model="filters.scope_type" :placeholder="TEXT.modules.fileVersionManagement.filters.all" clearable>
<el-option v-for="item in scopeOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</div>
<div class="ctms-filter-item">
<span class="ctms-filter-label">{{ TEXT.modules.fileVersionManagement.filters.site }}</span>
<el-select v-model="filters.site_id" :placeholder="TEXT.modules.fileVersionManagement.filters.all" clearable filterable>
<el-option v-for="item in siteOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</div>
<div class="ctms-filter-item">
<span class="ctms-filter-label">{{ TEXT.modules.fileVersionManagement.filters.docType }}</span>
<el-select v-model="filters.doc_type" :placeholder="TEXT.modules.fileVersionManagement.filters.all" clearable>
<el-option v-for="item in docTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</div>
<div class="ctms-filter-spacer"></div>
<div class="ctms-filter-actions">
<el-button type="primary" @click="load">{{ TEXT.common.actions.search }}</el-button>
<el-button @click="resetFilters">{{ TEXT.common.actions.reset }}</el-button>
</div>
<el-card shadow="never" class="main-content-card">
<div class="filter-container">
<el-form :inline="true" :model="filters" class="filter-form">
<el-form-item label="" class="filter-item-form">
<el-select v-model="filters.scope_type" :placeholder="TEXT.modules.fileVersionManagement.filters.scope" clearable class="filter-select-comp">
<el-option v-for="item in scopeOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="" class="filter-item-form">
<el-select v-model="filters.site_id" :placeholder="TEXT.modules.fileVersionManagement.filters.site" clearable filterable class="filter-select-comp">
<el-option v-for="item in siteOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="" class="filter-item-form">
<el-select v-model="filters.doc_type" :placeholder="TEXT.modules.fileVersionManagement.filters.docType" clearable class="filter-select-comp">
<el-option v-for="item in docTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item class="filter-item-form">
<el-button @click="resetFilters">{{ TEXT.common.actions.reset }}</el-button>
</el-form-item>
<div class="filter-spacer"></div>
<el-button type="primary" @click="openCreate" class="header-action-btn">
<el-icon class="el-icon--left"><Plus /></el-icon>
{{ TEXT.modules.fileVersionManagement.newDocument }}
</el-button>
</el-form>
</div>
</el-card>
<el-card class="ctms-table-card">
<el-table
:data="sortedItems"
v-loading="loading"
@@ -86,7 +74,7 @@
</el-table-column>
<el-table-column :label="TEXT.modules.fileVersionManagement.columns.actions" width="100" align="center">
<template #default="{ row }">
<el-button link type="danger" :disabled="isInactiveSite(row.site_id)" @click.stop="confirmDelete(row)">
<el-button v-if="isAdmin" link type="danger" :disabled="isInactiveSite(row.site_id)" @click.stop="confirmDelete(row)">
{{ TEXT.common.actions.delete }}
</el-button>
</template>
@@ -137,6 +125,7 @@ import { Plus } from "@element-plus/icons-vue";
import { fetchDocuments, createDocument, deleteDocument } from "../../api/documents";
import { fetchSites } from "../../api/sites";
import { useStudyStore } from "../../store/study";
import { useAuthStore } from "../../store/auth";
import type { DocumentSummary } from "../../types/documents";
import type { Site } from "../../types/api";
import { displayEnum, displayText } from "../../utils/display";
@@ -145,6 +134,7 @@ import { TEXT } from "../../locales";
const route = useRoute();
const router = useRouter();
const study = useStudyStore();
const auth = useAuthStore();
const loading = ref(false);
const items = ref<DocumentSummary[]>([]);
const sites = ref<Site[]>([]);
@@ -178,11 +168,23 @@ const siteActiveMap = computed(() => {
});
return map;
});
const isAdmin = computed(() => {
const role = auth.user?.role;
return role === "ADMIN";
});
const isInactiveSite = (siteId?: string | null) => !!siteId && siteActiveMap.value[siteId] === false;
const documentRowClass = ({ row }: { row: DocumentSummary }) =>
`${row?.id ? "clickable-row" : ""}${isInactiveSite(row?.site_id) ? " row-inactive" : ""}`.trim();
const filteredItems = computed(() =>
items.value.filter((item) => {
if (filters.scope_type && item?.scope_type !== filters.scope_type) return false;
if (filters.site_id && item?.site_id !== filters.site_id) return false;
if (filters.doc_type && item?.doc_type !== filters.doc_type) return false;
return true;
})
);
const sortedItems = computed(() =>
[...items.value].sort((a, b) => Number(isInactiveSite(a?.site_id)) - Number(isInactiveSite(b?.site_id)))
[...filteredItems.value].sort((a, b) => Number(isInactiveSite(a?.site_id)) - Number(isInactiveSite(b?.site_id)))
);
const createForm = reactive({
@@ -240,12 +242,7 @@ const load = async () => {
if (!trialId.value) return;
loading.value = true;
try {
const { data } = await fetchDocuments({
trial_id: trialId.value,
scope_type: filters.scope_type || undefined,
site_id: filters.site_id || undefined,
doc_type: filters.doc_type || undefined,
});
const { data } = await fetchDocuments({ trial_id: trialId.value });
items.value = data.items || [];
} catch (e: any) {
ElMessage.error(e?.response?.data?.message || TEXT.common.messages.loadFailed);
@@ -258,7 +255,6 @@ const resetFilters = () => {
filters.scope_type = "";
filters.site_id = "";
filters.doc_type = "";
load();
};
const openCreate = () => {
@@ -343,24 +339,43 @@ watch(
</script>
<style scoped>
.text-secondary {
color: var(--ctms-text-secondary);
.ctms-page {
display: flex;
flex-direction: column;
}
.font-mono {
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
.main-content-card :deep(.el-card__body) {
padding: 20px;
}
.font-medium {
font-weight: 500;
.filter-container {
margin-bottom: 20px;
}
.text-primary {
color: var(--ctms-primary);
.filter-form {
display: flex;
width: 100%;
gap: 12px;
align-items: center;
}
.font-semibold {
font-weight: 600;
.filter-item-form {
margin-bottom: 0 !important;
margin-right: 0 !important;
}
.filter-select-comp {
width: 140px;
}
.filter-spacer {
flex: 1;
}
.header-action-btn {
height: 36px;
padding: 0 20px;
border-radius: 8px;
}
</style>