优化登录页UI、新增占满布局类

This commit is contained in:
Cheng Zhou
2026-03-10 11:37:19 +08:00
parent 4c98147f3e
commit 8bec268a5b
47 changed files with 640 additions and 351 deletions
+1
View File
@@ -18,6 +18,7 @@ const session = useSessionStore();
<style scoped>
.app-shell {
min-height: 100vh;
min-height: 100dvh;
}
.app-locked {
+10 -4
View File
@@ -1,5 +1,5 @@
<template>
<el-card class="panel">
<div class="panel unified-shell">
<div class="header">
<span>{{ TEXT.common.labels.category }}</span>
<div v-if="canMaintain">
@@ -23,7 +23,7 @@
</el-menu-item>
</el-menu>
<FaqCategoryForm v-model="showForm" :category="editing" :is-admin="isAdmin" @success="emit('refresh')" />
</el-card>
</div>
</template>
<script setup lang="ts">
@@ -94,18 +94,24 @@ const onDelete = async (cat: FaqCategory) => {
<style scoped>
.panel {
padding: 0;
border: 0;
border-radius: 0;
box-shadow: none;
overflow: hidden;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px;
padding: 0 0 12px;
border-bottom: 1px solid #ebeef5;
}
.menu {
max-height: calc(100vh - 240px);
overflow: auto;
border-right: 0;
}
.actions {
margin-left: auto;
+5 -1
View File
@@ -1,5 +1,5 @@
<template>
<el-table :data="items" v-loading="loading" style="width: 100%" @row-click="onRowClick">
<el-table :data="items" v-loading="loading" style="width: 100%" class="faq-table" @row-click="onRowClick">
<el-table-column prop="question" :label="TEXT.common.fields.question" min-width="200">
<template #default="scope">
<el-link type="primary" :underline="false" class="question-link">
@@ -131,6 +131,10 @@ const remove = async (row: FaqItem) => {
</script>
<style scoped>
.faq-table :deep(.el-table__inner-wrapper::before) {
display: none;
}
.danger {
color: #f56c6c;
}
+6 -2
View File
@@ -195,8 +195,8 @@
<div class="content-wrapper">
<router-view v-slot="{ Component }">
<transition name="fade-transform" mode="out-in">
<div class="ctms-route-shell">
<component :is="Component" />
<div :key="route.fullPath" class="ctms-route-shell">
<component :is="Component" :key="route.fullPath" />
</div>
</transition>
</router-view>
@@ -435,6 +435,9 @@ const onCommand = (cmd: string) => {
<style scoped>
.layout-container {
height: 100vh;
height: 100dvh;
min-height: 100vh;
min-height: 100dvh;
background-color: transparent;
}
@@ -703,6 +706,7 @@ const onCommand = (cmd: string) => {
.content-wrapper {
padding: 6px 8px;
min-height: calc(100vh - 52px);
min-height: calc(100dvh - 52px);
background: #f7f8fb;
}
+20 -24
View File
@@ -1,19 +1,21 @@
<template>
<div class="module-page">
<div class="module-header">
<div>
<h1 class="module-title">{{ title }}</h1>
<p v-if="subtitle" class="module-subtitle">{{ subtitle }}</p>
</div>
<slot name="actions" />
</div>
<div class="module-content">
<div class="module-list">
<div class="module-list-header">
<span class="list-title">{{ listTitle }}</span>
<span class="list-note">{{ TEXT.common.empty.listPlaceholder }}</span>
<div class="module-page ctms-page-shell page--flush">
<div class="unified-shell">
<div class="module-header unified-action-bar bar--flush">
<div>
<h1 class="module-title">{{ title }}</h1>
<p v-if="subtitle" class="module-subtitle">{{ subtitle }}</p>
</div>
<slot name="actions" />
</div>
<div class="module-content unified-section section--flush">
<div class="module-list">
<div class="module-list-header">
<span class="list-title">{{ listTitle }}</span>
<span class="list-note">{{ TEXT.common.empty.listPlaceholder }}</span>
</div>
<StateEmpty :title="emptyTitle" :description="emptyDescription" />
</div>
<StateEmpty :title="emptyTitle" :description="emptyDescription" />
</div>
</div>
</div>
@@ -44,13 +46,7 @@ withDefaults(
.module-page {
display: flex;
flex-direction: column;
gap: 16px;
}
.module-header {
display: flex;
justify-content: space-between;
align-items: baseline;
gap: 0;
}
.module-title {
@@ -68,9 +64,9 @@ withDefaults(
.module-list {
background-color: #fff;
border: 1px solid var(--ctms-border-color);
border-radius: var(--ctms-radius);
padding: 20px;
border: 0;
border-radius: 0;
padding: 0;
}
.module-list-header {
@@ -1,5 +1,5 @@
<template>
<el-card>
<div class="attachment-list-panel unified-shell">
<div class="header">
<span>{{ headerTitle }}</span>
<AttachmentUploader
@@ -10,7 +10,7 @@
@uploaded="load"
/>
</div>
<el-table :data="attachments" v-loading="loading" style="width: 100%">
<el-table :data="attachments" v-loading="loading" style="width: 100%" class="attachment-table">
<el-table-column prop="filename" :label="TEXT.common.labels.filename" min-width="200" />
<el-table-column :label="TEXT.common.labels.size" width="120">
<template #default="scope">{{ formatFileSize(scope.row.file_size ?? scope.row.size) }}</template>
@@ -43,7 +43,7 @@
</template>
</el-table-column>
</el-table>
</el-card>
</div>
<el-dialog append-to=".layout-main .content-wrapper" v-model="previewVisible" :title="previewTitle" width="720px" :close-on-click-modal="false">
<div v-if="previewError" class="preview-error">
@@ -236,11 +236,23 @@ watch(previewVisible, (visible) => {
</script>
<style scoped>
.attachment-list-panel {
background: #ffffff;
border: 0;
border-radius: 0;
box-shadow: none;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
padding: 10px 16px;
border-bottom: 1px solid #eaf0f8;
}
.attachment-table :deep(.el-table__inner-wrapper::before) {
display: none;
}
.preview-frame {
@@ -1,6 +1,6 @@
<template>
<div class="fee-attachments">
<el-card v-for="group in groups" :key="group.key" class="group-card">
<div v-for="group in groups" :key="group.key" class="group-card unified-shell">
<div class="group-header">
<div>
<div class="group-title">{{ group.label }}</div>
@@ -28,7 +28,7 @@
<div v-if="(grouped[group.key] || []).length === 0" class="group-empty">
<StateEmpty :description="group.emptyText" />
</div>
<el-table v-else :data="grouped[group.key]" style="width: 100%">
<el-table v-else :data="grouped[group.key]" style="width: 100%" class="group-table">
<el-table-column prop="filename" :label="TEXT.common.labels.filename" min-width="160" />
<el-table-column :label="TEXT.common.labels.size" width="110">
<template #default="scope">{{ formatFileSize(scope.row.size || scope.row.file_size) }}</template>
@@ -59,7 +59,7 @@
</el-table-column>
</el-table>
</template>
</el-card>
</div>
</div>
<el-dialog append-to=".layout-main .content-wrapper" v-model="previewVisible" :title="previewTitle" width="720px" :close-on-click-modal="false">
@@ -315,14 +315,21 @@ watch(previewVisible, (visible) => {
gap: 12px;
}
.group-card {
background: #ffffff;
border: 0;
border-radius: 0;
box-shadow: none;
overflow: hidden;
}
.group-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 8px;
gap: 8px;
padding: 10px 16px;
border-bottom: 1px solid #eaf0f8;
}
.group-title {
@@ -338,7 +345,11 @@ watch(previewVisible, (visible) => {
}
.group-empty {
padding: 0;
padding: 12px 16px 0;
}
.group-table :deep(.el-table__inner-wrapper::before) {
display: none;
}
/* Compact Empty State Override */
+18 -4
View File
@@ -90,6 +90,12 @@
}
/* 全局重置 */
html,
body,
#app {
min-height: 100%;
}
body {
margin: 0;
font-family: "Manrope", "Segoe UI", sans-serif;
@@ -345,15 +351,15 @@ body {
.ctms-filter-row {
display: flex;
align-items: flex-end;
gap: 20px;
gap: 12px;
flex-wrap: wrap;
}
.ctms-filter-item {
display: flex;
flex-direction: column;
gap: 6px;
min-width: 160px;
gap: 4px;
min-width: 144px;
}
.ctms-filter-label {
@@ -368,6 +374,14 @@ body {
.ctms-table-card {
margin-bottom: 0;
border: 0;
border-radius: 0;
box-shadow: none !important;
background: transparent;
}
.ctms-table-card > .el-card__body {
padding: 0;
}
.ctms-table .el-table__row {
@@ -375,7 +389,7 @@ body {
}
.ctms-pagination {
margin-top: 16px;
margin-top: 10px;
display: flex;
justify-content: flex-end;
}
+50 -6
View File
@@ -3,8 +3,8 @@
--unified-shell-border: #d8e2ef;
--unified-shell-divider: #eaf0f8;
--unified-shell-radius: 16px;
--unified-shell-padding-x: 20px;
--unified-shell-padding-y: 14px;
--unified-shell-padding-x: 16px;
--unified-shell-padding-y: 10px;
--unified-title-color: #0f2345;
--unified-muted-color: #6f84a8;
}
@@ -13,6 +13,11 @@
padding: 16px 20px 20px;
}
.ctms-page-shell.page--flush,
.page.page--flush {
padding: 0 !important;
}
.ctms-page-header-row {
display: flex;
justify-content: space-between;
@@ -33,6 +38,23 @@
box-shadow: none;
}
.page--flush > .unified-shell,
.page--flush > .main-content-flat.unified-shell,
.page--flush .page-body > .unified-shell,
.page--flush .page-body > .main-content-flat.unified-shell {
margin: 0;
width: 100%;
border-radius: 0;
}
.main-content-flat {
background: transparent;
border: 0;
border-radius: 0;
box-shadow: none;
padding: 0;
}
.el-card.unified-shell {
margin-bottom: 0;
border: 0 !important;
@@ -54,6 +76,11 @@
margin-bottom: 0 !important;
}
.unified-action-bar.bar--flush {
padding-left: 0 !important;
padding-right: 0 !important;
}
.ctms-page > .ctms-page-header.unified-action-bar,
.page > .page-header.unified-action-bar {
border: 0;
@@ -67,6 +94,23 @@
border-bottom: 1px solid var(--unified-shell-divider);
}
.unified-section.section--flush-x {
padding-left: 0 !important;
padding-right: 0 !important;
}
.unified-section.section--flush-top {
padding-top: 0 !important;
}
.unified-section.section--flush-bottom {
padding-bottom: 0 !important;
}
.unified-section.section--flush {
padding: 0 !important;
}
.unified-section:last-child {
border-bottom: 0;
}
@@ -112,16 +156,16 @@
}
.unified-shell .el-table th.el-table__cell {
background-color: #f8fbff;
color: #35527d;
background-color: #f7f9fc;
color: #34506f;
font-weight: 600;
height: 42px;
height: 40px;
font-size: 13px;
}
.unified-shell .el-table td.el-table__cell {
border-bottom-color: #edf2f8;
padding: 8px 0;
padding: 7px 0;
}
.unified-shell .el-button,
+7 -7
View File
@@ -1,5 +1,5 @@
<template>
<div class="page">
<div class="page ctms-page-shell page--flush">
<el-row :gutter="12">
<el-col :span="5">
<FaqCategoryPanel
@@ -154,25 +154,25 @@ onMounted(async () => {
<style scoped>
.page {
padding: 16px;
padding: 0;
}
.faq-main {
background: #fff;
border: 1px solid var(--ctms-border-color);
border-radius: 10px;
padding: 16px;
border: 0;
border-radius: 0;
padding: 0;
overflow: hidden;
}
.filters {
display: flex;
gap: 8px;
align-items: center;
margin-bottom: 16px;
}
.spacer {
flex: 1;
}
.pagination-wrap {
margin-top: 14px;
padding: 10px 16px 0;
display: flex;
justify-content: flex-end;
}
+23 -6
View File
@@ -1,6 +1,6 @@
<template>
<div class="page">
<el-card shadow="never" class="main-content-card unified-shell">
<div class="main-content-flat unified-shell">
<div class="filter-container unified-action-bar">
<div class="filter-form">
<el-tag type="info" effect="plain" class="filter-label-tag">{{ TEXT.common.fields.status }}</el-tag>
@@ -14,7 +14,7 @@
</div>
</div>
<div class="unified-section approval-table-section">
<el-table :data="users" v-loading="loading" style="width: 100%">
<el-table :data="users" v-loading="loading" class="approval-table" style="width: 100%">
<el-table-column prop="email" :label="TEXT.common.fields.email" min-width="200" />
<el-table-column prop="full_name" :label="TEXT.common.fields.name" min-width="140" />
<el-table-column prop="role" :label="TEXT.common.labels.role" width="120">
@@ -54,7 +54,7 @@
</el-table-column>
</el-table>
</div>
</el-card>
</div>
</div>
</template>
@@ -124,12 +124,26 @@ onMounted(() => {
.page {
display: flex;
flex-direction: column;
gap: 0;
}
.main-content-flat {
width: 100%;
background: transparent;
border: 0;
border-radius: 0;
box-shadow: none;
padding: 0;
}
.main-content-flat :deep(.el-card__body) {
padding: 0;
}
.filter-form {
display: flex;
width: 100%;
gap: 12px;
gap: 10px;
align-items: center;
}
@@ -146,7 +160,10 @@ onMounted(() => {
}
.approval-table-section {
padding-top: 12px;
padding-bottom: 12px;
padding: 0;
}
.approval-table :deep(.el-table__inner-wrapper::before) {
display: none;
}
</style>
+30 -11
View File
@@ -1,6 +1,6 @@
<template>
<div class="page">
<el-card shadow="never" class="main-content-card unified-shell">
<div class="main-content-flat unified-shell">
<div class="audit-toolbar">
<el-form :inline="true" :model="filters" class="filter-form">
<el-form-item label="" class="filter-item-form">
@@ -60,7 +60,7 @@
</el-form>
</div>
<div class="unified-section table-section">
<el-table :data="logs" v-loading="loading" style="width: 100%" stripe>
<el-table :data="logs" v-loading="loading" class="audit-table" style="width: 100%" stripe>
<el-table-column prop="timestamp" :label="TEXT.modules.adminAuditLogs.columns.time" width="180" align="center">
<template #default="scope">
<span class="text-gray-500">{{ displayDateTime(scope.row.timestamp) }}</span>
@@ -115,7 +115,7 @@
/>
</div>
</div>
</el-card>
</div>
<el-drawer
v-model="detailVisible"
@@ -425,21 +425,35 @@ onMounted(async () => {
.page {
display: flex;
flex-direction: column;
gap: 0;
}
.main-content-flat {
width: 100%;
background: transparent;
border: 0;
border-radius: 0;
box-shadow: none;
padding: 0;
}
.main-content-flat :deep(.el-card__body) {
padding: 0;
}
.table-section {
padding-top: 12px;
padding-bottom: 12px;
padding: 0;
}
.audit-toolbar {
padding: 12px 16px;
padding: 10px 16px;
border-bottom: 1px solid var(--unified-shell-divider);
}
.filter-form {
display: flex;
width: 100%;
gap: 10px;
gap: 8px;
align-items: center;
flex-wrap: nowrap;
}
@@ -451,15 +465,15 @@ onMounted(async () => {
}
.filter-select-comp {
width: 140px;
width: 132px;
}
.filter-select-result {
width: 100px;
width: 96px;
}
.date-range-picker-comp {
width: 260px !important;
width: 248px !important;
}
.filter-spacer {
@@ -509,9 +523,14 @@ onMounted(async () => {
}
.pagination-wrap {
margin-top: 14px;
margin-top: 10px;
display: flex;
justify-content: flex-end;
padding: 0 0 6px;
}
.audit-table :deep(.el-table__inner-wrapper::before) {
display: none;
}
.drawer-header {
+31 -14
View File
@@ -1,5 +1,5 @@
<template>
<div class="ctms-page setup-page ctms-page-shell">
<div class="ctms-page setup-page ctms-page-shell page--flush">
<div class="setup-shell unified-shell">
<div class="setup-header unified-action-bar">
<div class="setup-flow">
@@ -537,7 +537,7 @@
<StateLoading v-else :rows="4" />
</section>
<section v-show="activeStep === 1" class="setup-section">
<section v-show="activeStep === 1" class="setup-section setup-section--table">
<el-table :data="currentSetupDraft.projectMilestones" class="ctms-table setup-milestone-table">
<el-table-column label="里程碑" min-width="180">
<template #default="scope">
@@ -668,7 +668,7 @@
</el-table>
</section>
<section v-show="activeStep === 2" class="setup-section enrollment-plan-section">
<section v-show="activeStep === 2" class="setup-section setup-section--table enrollment-plan-section">
<div class="enrollment-plan-toolbar">
<div class="enrollment-inline-group">
<span class="enrollment-inline-label required">计划入组日期</span>
@@ -886,7 +886,7 @@
</el-table>
</section>
<section v-show="activeStep === 3" class="setup-section">
<section v-show="activeStep === 3" class="setup-section setup-section--table">
<el-table :data="currentSetupDraft.siteMilestones" class="ctms-table">
<el-table-column label="里程碑" min-width="220">
<template #default="scope">
@@ -979,7 +979,7 @@
</el-table>
</section>
<section v-show="activeStep === 4" class="setup-section enrollment-plan-section">
<section v-show="activeStep === 4" class="setup-section setup-section--table enrollment-plan-section">
<div class="enrollment-plan-toolbar">
<div class="site-plan-selector">
<span class="enrollment-inline-label">中心</span>
@@ -1226,7 +1226,7 @@
</div>
</section>
<section v-show="activeStep === 5" class="setup-section">
<section v-show="activeStep === 5" class="setup-section setup-section--table">
<el-table :data="currentSetupDraft.monitoringStrategies" class="ctms-table">
<el-table-column label="监查类型" min-width="180">
<template #default="scope">
@@ -1303,7 +1303,7 @@
</el-table>
</section>
<section v-show="activeStep === 6" class="setup-section">
<section v-show="activeStep === 6" class="setup-section setup-section--table">
<el-table :data="centerConfirmDisplayRows" class="ctms-table">
<el-table-column label="中心" min-width="180">
<template #default="scope">
@@ -6045,11 +6045,10 @@ onBeforeUnmount(() => {
}
.setup-shell {
border: 1px solid #d8e2ef;
border-radius: 16px;
border: 0;
background: #ffffff;
overflow: hidden;
box-shadow: 0 6px 20px rgba(17, 42, 88, 0.05);
box-shadow: none;
}
.setup-header {
@@ -6977,6 +6976,12 @@ onBeforeUnmount(() => {
background: #ffffff;
}
.setup-section--table {
padding-left: 0;
padding-right: 0;
padding-top: 0;
}
.enrollment-plan-section {
display: flex;
flex-direction: column;
@@ -7207,13 +7212,16 @@ onBeforeUnmount(() => {
.setup-content {
position: relative;
padding-left: 0 !important;
padding-right: 0 !important;
padding-bottom: 0 !important;
}
.setup-content-step-title {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 12px;
padding: 9px 12px;
font-size: 18px;
font-weight: 700;
color: #162f57;
@@ -7473,6 +7481,12 @@ onBeforeUnmount(() => {
margin-bottom: 10px;
}
.setup-section--table .setup-section-header {
margin-bottom: 0;
padding-left: 12px;
padding-right: 12px;
}
.setup-content.setup-content-drawer .setup-section-header {
padding-bottom: 0;
margin-bottom: 0;
@@ -7698,9 +7712,10 @@ onBeforeUnmount(() => {
}
.setup-section :deep(.el-table th.el-table__cell) {
background: #f8fafc !important;
color: #64748b !important;
font-weight: 500 !important;
background: #f7f9fc !important;
color: #34506f !important;
font-weight: 600 !important;
height: 40px;
border-bottom: 1px solid #e2e8f0 !important;
border-right: none !important;
border-left: none !important;
@@ -7711,6 +7726,8 @@ onBeforeUnmount(() => {
border-right: none !important;
border-left: none !important;
background: transparent !important;
padding-top: 7px;
padding-bottom: 7px;
}
/* 覆盖悬停状态颜色,保持极简高级 */
+22 -5
View File
@@ -1,6 +1,6 @@
<template>
<div class="page">
<el-card shadow="never" class="main-content-card unified-shell">
<div class="main-content-flat unified-shell">
<div class="unified-action-bar actions-only-bar">
<div class="filter-spacer"></div>
<el-button type="primary" @click="openAdd">
@@ -8,7 +8,7 @@
</el-button>
</div>
<div class="unified-section member-table-section">
<el-table :data="memberRows" v-loading="loading" stripe>
<el-table :data="memberRows" v-loading="loading" stripe class="member-table">
<el-table-column prop="full_name" :label="TEXT.modules.adminProjectMembers.username" min-width="160" />
<el-table-column prop="role_in_study" :label="TEXT.modules.adminProjectMembers.projectRole" min-width="140">
<template #default="scope">
@@ -62,7 +62,7 @@
</el-table-column>
</el-table>
</div>
</el-card>
</div>
<el-dialog append-to=".layout-main .content-wrapper" :title="TEXT.modules.adminProjectMembers.newTitle" width="520px" v-model="addVisible" :close-on-click-modal="false">
<el-form :model="newMember" label-width="120px" ref="addFormRef" :rules="addRules">
@@ -301,6 +301,20 @@ onMounted(async () => {
.page {
display: flex;
flex-direction: column;
gap: 0;
}
.main-content-flat {
width: 100%;
background: transparent;
border: 0;
border-radius: 0;
box-shadow: none;
padding: 0;
}
.main-content-flat :deep(.el-card__body) {
padding: 0;
}
.actions-only-bar {
@@ -310,8 +324,11 @@ onMounted(async () => {
}
.member-table-section {
padding-top: 12px;
padding-bottom: 12px;
padding: 0;
}
.member-table :deep(.el-table__inner-wrapper::before) {
display: none;
}
.hint {
+24 -7
View File
@@ -1,14 +1,14 @@
<template>
<div class="page">
<el-card shadow="never" class="main-content-card unified-shell">
<div class="main-content-flat unified-shell">
<div class="unified-action-bar actions-only-bar">
<div class="filter-spacer"></div>
<el-button type="primary" @click="openCreate">
{{ TEXT.common.actions.add }}{{ TEXT.modules.adminProjects.projectLabel }}
</el-button>
</div>
<div class="unified-section">
<el-table :data="projects" v-loading="loading" stripe>
<div class="unified-section project-table-section">
<el-table :data="projects" v-loading="loading" stripe class="project-table">
<el-table-column prop="name" :label="TEXT.common.fields.projectName" min-width="300">
<template #default="scope">
<el-link type="primary" @click="goDetail(scope.row)" class="font-medium">{{ scope.row.name }}</el-link>
@@ -57,7 +57,7 @@
</el-table-column>
</el-table>
</div>
</el-card>
</div>
<ProjectForm v-model:visible="formVisible" :project="editingProject" @saved="loadProjects" />
</div>
</template>
@@ -219,6 +219,20 @@ onMounted(() => {
.page {
display: flex;
flex-direction: column;
gap: 0;
}
.main-content-flat {
width: 100%;
background: transparent;
border: 0;
border-radius: 0;
box-shadow: none;
padding: 0;
}
.main-content-flat :deep(.el-card__body) {
padding: 0;
}
.actions-only-bar {
@@ -227,9 +241,12 @@ onMounted(() => {
align-items: center;
}
.unified-section {
padding-top: 12px;
padding-bottom: 12px;
.project-table-section {
padding: 0;
}
.project-table :deep(.el-table__inner-wrapper::before) {
display: none;
}
.action-btn {
+7 -8
View File
@@ -1,14 +1,14 @@
<template>
<div class="page">
<el-card shadow="never" class="main-content-card unified-shell">
<div class="page page--flush">
<div class="main-content-flat unified-shell">
<div class="unified-action-bar actions-only-bar">
<div class="filter-spacer"></div>
<el-button type="primary" @click="openCreate">
{{ TEXT.common.actions.add }}{{ TEXT.modules.adminSites.siteLabel }}
</el-button>
</div>
<div class="unified-section site-table-section">
<el-table :data="displaySites" v-loading="loading" stripe :row-class-name="siteRowClass" style="width: 100%">
<div class="unified-section site-table-section section--flush-x section--flush-top section--flush-bottom">
<el-table :data="displaySites" v-loading="loading" stripe :row-class-name="siteRowClass" style="width: 100%" class="site-table">
<el-table-column prop="name" :label="TEXT.common.fields.siteName" min-width="220">
<template #default="scope">
<div class="site-name-cell">
@@ -61,7 +61,7 @@
</el-table-column>
</el-table>
</div>
</el-card>
</div>
<SiteForm
v-if="projectId"
v-model:visible="formVisible"
@@ -278,9 +278,8 @@ onMounted(async () => {
align-items: center;
}
.site-table-section {
padding-top: 12px;
padding-bottom: 12px;
.site-table :deep(.el-table__inner-wrapper::before) {
display: none;
}
.site-name-cell {
+38 -6
View File
@@ -1,6 +1,6 @@
<template>
<div class="page">
<el-card shadow="never" class="main-content-card unified-shell">
<div class="main-content-flat unified-shell">
<div class="filter-container unified-action-bar">
<div class="filter-form">
<el-form-item label="" class="filter-item-form">
@@ -28,7 +28,7 @@
</div>
<div class="unified-section user-table-section">
<el-table :data="users" stripe v-loading="loading" style="width: 100%">
<el-table :data="users" stripe v-loading="loading" class="user-table" style="width: 100%">
<el-table-column prop="email" :label="TEXT.common.fields.email" min-width="200" />
<el-table-column prop="full_name" :label="TEXT.common.fields.name" min-width="140" />
<el-table-column prop="department" :label="TEXT.common.fields.department" min-width="140" />
@@ -84,7 +84,7 @@
/>
</div>
</div>
</el-card>
</div>
<UserForm v-model:visible="formVisible" :user="editingUser" :admin-count="activeAdminCount" @saved="loadUsers" />
<UserResetPassword v-model:visible="resetVisible" :user="resetUser" @reset="loadUsers" />
</div>
@@ -291,12 +291,26 @@ onMounted(() => {
.page {
display: flex;
flex-direction: column;
gap: 0;
}
.main-content-flat {
width: 100%;
background: transparent;
border: 0;
border-radius: 0;
box-shadow: none;
padding: 0;
}
.main-content-flat :deep(.el-card__body) {
padding: 0;
}
.filter-form {
display: flex;
width: 100%;
gap: 12px;
gap: 10px;
align-items: center;
}
@@ -306,7 +320,7 @@ onMounted(() => {
}
.filter-input-comp {
width: 200px;
width: 190px;
}
.filter-spacer {
@@ -314,13 +328,31 @@ onMounted(() => {
}
.user-table-section {
padding: 0;
}
.user-table {
margin-top: 0;
}
.user-table :deep(.el-table__inner-wrapper::before) {
display: none;
}
.user-table :deep(th.el-table__cell) {
padding-top: 10px;
padding-bottom: 10px;
}
.user-table :deep(td.el-table__cell) {
padding-top: 12px;
padding-bottom: 12px;
}
.pagination-wrap {
margin-top: 14px;
margin-top: 10px;
display: flex;
justify-content: flex-end;
padding: 0 0 6px;
}
</style>
@@ -1,7 +1,7 @@
<template>
<div class="page">
<div class="page ctms-page-shell page--flush">
<div class="unified-shell">
<section class="unified-section" v-loading="loading">
<section class="unified-section section--flush-x" v-loading="loading">
<div class="unified-section-header">
<div>
<div class="ctms-section-title">
@@ -37,7 +37,7 @@
</el-descriptions>
</section>
<section class="unified-section">
<section class="unified-section section--flush-x section--flush-bottom">
<el-tabs v-model="activeTab" @tab-change="handleTabChange" class="ctms-tabs">
<el-tab-pane :label="TEXT.modules.fileVersionManagement.tabs.versions" name="versions">
<div class="ctms-section-header">
@@ -49,7 +49,7 @@
</el-button>
</div>
</div>
<el-card class="ctms-table-card unified-shell">
<div class="ctms-table-panel">
<el-table :data="detail.version_timeline" v-loading="versionLoading" class="ctms-table">
<el-table-column prop="version_no" :label="TEXT.modules.fileVersionManagement.fields.versionNo" width="120">
<template #default="{ row }">
@@ -94,7 +94,7 @@
</template>
</el-table-column>
</el-table>
</el-card>
</div>
</el-tab-pane>
<el-tab-pane :label="TEXT.modules.fileVersionManagement.tabs.distributions" name="distributions">
@@ -107,7 +107,7 @@
</el-button>
</div>
</div>
<el-card class="ctms-table-card unified-shell">
<div class="ctms-table-panel">
<el-table :data="distributions" v-loading="distributionLoading" class="ctms-table">
<el-table-column :label="TEXT.modules.fileVersionManagement.fields.targetType" width="120">
<template #default="{ row }">
@@ -125,7 +125,7 @@
</template>
</el-table-column>
</el-table>
</el-card>
</div>
</el-tab-pane>
</el-tabs>
@@ -680,6 +680,18 @@ watch(
gap: 0;
}
.ctms-table-panel {
padding: 0;
}
.ctms-section-header {
margin-bottom: 0;
}
.ctms-table-panel :deep(.el-table__inner-wrapper::before) {
display: none;
}
.doc-meta-row {
display: flex;
align-items: center;
+10 -14
View File
@@ -1,7 +1,7 @@
<template>
<div class="page">
<el-card shadow="never" class="main-content-card unified-shell">
<div class="filter-container unified-action-bar">
<div class="page ctms-page-shell page--flush">
<div class="main-content-flat unified-shell">
<div class="filter-container unified-action-bar bar--flush">
<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">
@@ -28,7 +28,7 @@
</el-button>
</el-form>
</div>
<section class="unified-section">
<section class="unified-section document-table-section section--flush-x section--flush-top section--flush-bottom">
<el-table
:data="sortedItems"
v-loading="loading"
@@ -84,7 +84,7 @@
</template>
</el-table>
</section>
</el-card>
</div>
<el-dialog append-to=".layout-main .content-wrapper" v-model="createVisible" :title="TEXT.modules.fileVersionManagement.dialog.createTitle" width="520px" destroy-on-close class="ctms-dialog">
<el-form ref="createFormRef" :model="createForm" :rules="createRules" label-width="100px" class="ctms-form">
@@ -346,18 +346,10 @@ watch(
gap: 0;
}
.main-content-card :deep(.el-card__body) {
padding: 0;
}
.filter-container {
margin-bottom: 0;
}
.filter-form {
display: flex;
width: 100%;
gap: 12px;
gap: 8px;
align-items: center;
}
@@ -370,6 +362,10 @@ watch(
width: 132px;
}
.ctms-table :deep(.el-table__inner-wrapper::before) {
display: none;
}
.filter-spacer {
flex: 1;
}
+5 -1
View File
@@ -36,7 +36,7 @@
<el-descriptions-item :label="TEXT.common.fields.remark" :span="2">{{ detail.remark || TEXT.common.fallback }}</el-descriptions-item>
</el-descriptions>
</section>
<section class="unified-section">
<section class="unified-section attachment-section">
<AttachmentList
v-if="shipmentId"
:study-id="studyId"
@@ -109,6 +109,10 @@ onMounted(load);
gap: 0;
}
.attachment-section {
padding: 0;
}
.section-title-row {
display: flex;
justify-content: space-between;
@@ -251,6 +251,10 @@ onMounted(async () => {
overflow: hidden;
}
.section-card :deep(.el-card__body) {
padding: 0;
}
.card-header {
padding: 4px 0;
}
+5 -1
View File
@@ -88,7 +88,7 @@
</el-row>
</el-card>
<el-card class="form-card section-margin unified-shell" shadow="never">
<el-card class="form-card section-margin attachment-card unified-shell" shadow="never">
<template #header>
<div class="card-header actions-header">
<span class="header-title">{{ TEXT.modules.feeContracts.paymentTitle }}</span>
@@ -517,6 +517,10 @@ onMounted(async () => {
margin-top: 24px;
}
.attachment-card :deep(.el-card__body) {
padding: 0;
}
.card-header {
padding: 4px 0;
}
+10 -16
View File
@@ -1,5 +1,5 @@
<template>
<div class="page ctms-page-shell">
<div class="page ctms-page-shell page--flush">
<div class="overview">
<el-row :gutter="20">
<el-col :xs="24" :sm="12" :md="6">
@@ -53,7 +53,7 @@
<StateLoading v-else-if="loading" :rows="6" />
<el-card shadow="never" class="main-content-card unified-shell" v-else>
<div class="main-content-flat unified-shell" v-else>
<div class="filter-container unified-action-bar">
<el-form :inline="true" :model="filters" class="filter-form">
<el-form-item label="" class="filter-item">
@@ -86,13 +86,13 @@
</el-button>
</el-form>
</div>
<section class="unified-section">
<section class="unified-section contract-table-section section--flush-x section--flush-top section--flush-bottom">
<el-table
:data="sortedContracts"
style="width: 100%"
@row-click="onRowClick"
:row-class-name="contractRowClass"
:header-cell-style="{ background: '#f8f9fb' }"
class="contract-table"
>
<el-table-column prop="center_name" :label="TEXT.common.fields.site" min-width="160">
<template #default="scope">
@@ -167,7 +167,7 @@
</el-table>
<StateEmpty v-if="sortedContracts.length === 0" :description="TEXT.modules.feeContracts.empty" />
</section>
</el-card>
</div>
</div>
</template>
@@ -350,18 +350,14 @@ onMounted(async () => {
gap: 0;
}
.main-content-card :deep(.el-card__body) {
padding: 0;
}
.filter-container {
margin-bottom: 0;
.overview {
margin: 0;
}
.filter-form {
display: flex;
width: 100%;
gap: 12px;
gap: 8px;
align-items: center;
}
@@ -383,10 +379,8 @@ onMounted(async () => {
flex: 1;
}
.table-card {
border-radius: 12px;
border: 1px solid var(--ctms-border-color);
overflow: hidden;
.contract-table :deep(.el-table__inner-wrapper::before) {
display: none;
}
.site-cell {
@@ -233,6 +233,10 @@ onMounted(async () => {
overflow: hidden;
}
.section-card :deep(.el-card__body) {
padding: 0;
}
.card-header {
display: flex;
justify-content: space-between;
@@ -56,7 +56,7 @@
</el-row>
</el-card>
<el-card class="form-card section-margin unified-shell" shadow="never">
<el-card class="form-card section-margin attachment-card unified-shell" shadow="never">
<template #header>
<div class="card-header">
<span class="header-title">{{ TEXT.common.fields.status }}</span>
@@ -394,6 +394,10 @@ onMounted(async () => {
margin-top: 24px;
}
.attachment-card :deep(.el-card__body) {
padding: 0;
}
.card-header {
padding: 4px 0;
}
+10 -16
View File
@@ -1,5 +1,5 @@
<template>
<div class="page ctms-page-shell">
<div class="page ctms-page-shell page--flush">
<div class="overview">
<el-row :gutter="20">
<el-col :xs="24" :sm="12" :md="6">
@@ -52,7 +52,7 @@
<StateLoading v-else-if="loading" :rows="6" />
<el-card shadow="never" class="main-content-card unified-shell" v-else>
<div class="main-content-flat unified-shell" v-else>
<div class="filter-container unified-action-bar">
<el-form :inline="true" :model="filters" class="filter-form">
<el-form-item label="" class="filter-item">
@@ -97,13 +97,13 @@
</el-button>
</el-form>
</div>
<section class="unified-section">
<section class="unified-section special-expense-table-section section--flush-x section--flush-top section--flush-bottom">
<el-table
:data="sortedExpenses"
style="width: 100%"
@row-click="onRowClick"
:header-cell-style="{ background: '#f8f9fb' }"
:row-class-name="expenseRowClass"
class="special-expense-table"
>
<el-table-column :label="TEXT.common.fields.occurDate" width="140">
<template #default="scope">
@@ -166,7 +166,7 @@
</el-table>
<StateEmpty v-if="sortedExpenses.length === 0" :description="TEXT.modules.feeSpecials.empty" />
</section>
</el-card>
</div>
</div>
</template>
@@ -355,18 +355,14 @@ onMounted(async () => {
gap: 0;
}
.main-content-card :deep(.el-card__body) {
padding: 0;
}
.filter-container {
margin-bottom: 0;
.overview {
margin: 0;
}
.filter-form {
display: flex;
width: 100%;
gap: 12px;
gap: 8px;
align-items: center;
}
@@ -391,10 +387,8 @@ onMounted(async () => {
flex: 1;
}
.table-card {
border-radius: 12px;
border: 1px solid var(--ctms-border-color);
overflow: hidden;
.special-expense-table :deep(.el-table__inner-wrapper::before) {
display: none;
}
.date-text {
@@ -19,7 +19,7 @@
<el-descriptions-item :label="TEXT.common.fields.remark" :span="2">{{ detail.remark || TEXT.common.fallback }}</el-descriptions-item>
</el-descriptions>
</section>
<section class="unified-section">
<section class="unified-section attachment-section">
<AttachmentList
v-if="contractId"
:study-id="studyId"
@@ -113,4 +113,8 @@ onMounted(async () => {
flex-direction: column;
gap: 0;
}
.attachment-section {
padding: 0;
}
</style>
+5 -1
View File
@@ -18,7 +18,7 @@
<el-descriptions-item :label="TEXT.common.fields.remark" :span="2">{{ detail.remark || TEXT.common.fallback }}</el-descriptions-item>
</el-descriptions>
</section>
<section class="unified-section">
<section class="unified-section attachment-section">
<AttachmentList
v-if="specialId"
:study-id="studyId"
@@ -112,4 +112,8 @@ onMounted(async () => {
flex-direction: column;
gap: 0;
}
.attachment-section {
padding: 0;
}
</style>
+12 -12
View File
@@ -1,7 +1,7 @@
<template>
<div class="page">
<div class="page page--flush">
<el-card shadow="never" class="main-content-card unified-shell">
<div class="main-content-flat unified-shell">
<div class="filter-container unified-action-bar">
<el-form :inline="true" :model="filters" class="filter-form">
<el-form-item label="" class="filter-item">
@@ -48,11 +48,12 @@
</el-button>
</el-form>
</div>
<div class="unified-section table-section">
<div class="unified-section table-section section--flush-x section--flush-top section--flush-bottom">
<el-table
:data="sortedItems"
v-loading="loading"
style="width: 100%"
class="shipment-table"
:row-class-name="shipmentRowClass"
@row-click="onRowClick"
>
@@ -104,7 +105,7 @@
</el-table>
<StateEmpty v-if="!loading && sortedItems.length === 0" :description="TEXT.modules.drugShipments.empty" />
</div>
</el-card>
</div>
</div>
</template>
@@ -252,7 +253,7 @@ onMounted(async () => {
.filter-form {
display: flex;
width: 100%;
gap: 12px;
gap: 8px;
align-items: center;
}
@@ -271,10 +272,10 @@ onMounted(async () => {
}
.filter-actions :deep(.el-button) {
height: 36px;
line-height: 36px;
padding: 0 18px;
border-radius: 8px;
height: 32px;
line-height: 32px;
padding: 0 12px;
border-radius: 10px;
}
.filter-select {
@@ -285,9 +286,8 @@ onMounted(async () => {
flex: 1;
}
.table-section {
padding-top: 12px;
padding-bottom: 12px;
.shipment-table :deep(.el-table__inner-wrapper::before) {
display: none;
}
.type-tag {
@@ -1,10 +1,10 @@
<template>
<div class="redirecting ctms-page-shell">
<section class="unified-action-bar">
<div class="redirecting ctms-page-shell page--flush">
<section class="unified-action-bar bar--flush">
<div class="redirecting-title">{{ TEXT.menu.fileVersionManagement }}</div>
</section>
<section class="unified-shell">
<section class="unified-section">
<section class="unified-section section--flush">
<el-empty :description="`${TEXT.common.loading}...`" />
</section>
</section>
+17 -5
View File
@@ -1,6 +1,6 @@
<template>
<div class="page">
<el-card shadow="never" class="main-content-card unified-shell">
<div class="main-content-flat unified-shell">
<div class="filter-container unified-action-bar">
<el-form :inline="true" :model="filters" class="filter-form">
<el-form-item label="" class="filter-item-form">
@@ -70,7 +70,7 @@
</el-table>
<StateEmpty v-if="!loading && sortedItems.length === 0" :description="TEXT.modules.financeContracts.empty" />
</div>
</el-card>
</div>
</div>
</template>
@@ -185,10 +185,18 @@ onMounted(async () => {
flex-direction: column;
}
.main-content-flat {
background: transparent;
border: 0;
border-radius: 0;
box-shadow: none;
padding: 0;
}
.filter-form {
display: flex;
width: 100%;
gap: 12px;
gap: 8px;
align-items: center;
}
@@ -206,8 +214,12 @@ onMounted(async () => {
}
.table-section {
padding-top: 12px;
padding-bottom: 12px;
padding-top: 0;
padding-bottom: 0;
}
.ctms-table :deep(.el-table__inner-wrapper::before) {
display: none;
}
+17 -5
View File
@@ -1,6 +1,6 @@
<template>
<div class="page">
<el-card shadow="never" class="main-content-card unified-shell">
<div class="main-content-flat unified-shell">
<div class="filter-container unified-action-bar">
<el-form :inline="true" :model="filters" class="filter-form">
<el-form-item label="" class="filter-item-form">
@@ -80,7 +80,7 @@
</el-table>
<StateEmpty v-if="!loading && sortedItems.length === 0" :description="TEXT.modules.financeSpecials.empty" />
</div>
</el-card>
</div>
</div>
</template>
@@ -194,10 +194,18 @@ onMounted(async () => {
flex-direction: column;
}
.main-content-flat {
background: transparent;
border: 0;
border-radius: 0;
box-shadow: none;
padding: 0;
}
.filter-form {
display: flex;
width: 100%;
gap: 12px;
gap: 8px;
align-items: center;
}
@@ -219,8 +227,12 @@ onMounted(async () => {
}
.table-section {
padding-top: 12px;
padding-bottom: 12px;
padding-top: 0;
padding-bottom: 0;
}
:deep(.ctms-table .el-table__inner-wrapper::before) {
display: none;
}
</style>
+8 -9
View File
@@ -1,8 +1,8 @@
<template>
<div class="page">
<div class="page ctms-page-shell page--flush">
<el-card shadow="never" class="main-content-card unified-shell">
<div class="filter-container unified-action-bar">
<div class="main-content-flat unified-shell">
<div class="filter-container unified-action-bar bar--flush">
<el-form :inline="true" :model="filters" class="filter-form">
<el-form-item label="" class="filter-item-form">
<el-input
@@ -29,7 +29,7 @@
</el-button>
</el-form>
</div>
<div class="unified-section table-section">
<div class="unified-section table-section section--flush-x section--flush-top section--flush-bottom">
<el-table
:data="sortedItems"
v-loading="loading"
@@ -60,7 +60,7 @@
</el-table>
<StateEmpty v-if="!loading && sortedItems.length === 0" :description="TEXT.modules.knowledgeNotes.empty" />
</div>
</el-card>
</div>
</div>
</template>
@@ -178,7 +178,7 @@ onMounted(async () => {
.filter-form {
display: flex;
width: 100%;
gap: 12px;
gap: 8px;
align-items: center;
}
@@ -195,9 +195,8 @@ onMounted(async () => {
flex: 1;
}
.table-section {
padding-top: 12px;
padding-bottom: 12px;
:deep(.ctms-table .el-table__inner-wrapper::before) {
display: none;
}
</style>
+9 -10
View File
@@ -1,7 +1,7 @@
<template>
<div class="page">
<div class="page page--flush">
<div v-if="study.currentStudy" class="unified-shell">
<section class="unified-section equipment-section">
<section class="unified-section section--flush">
<div class="filter-row">
<el-form :inline="true" :model="filters">
<el-form-item label="设备名称">
@@ -15,7 +15,7 @@
<el-button type="primary" @click="openCreate">新建</el-button>
</div>
<el-table v-loading="loading" :data="rows" class="ctms-table" style="width: 100%" table-layout="fixed">
<el-table v-loading="loading" :data="rows" class="ctms-table equipment-table" style="width: 100%" table-layout="fixed">
<el-table-column prop="name" label="设备名称" />
<el-table-column prop="specModel" label="规格型号" />
<el-table-column prop="unit" label="单位" />
@@ -391,11 +391,6 @@ watch(
flex-direction: column;
}
.equipment-section {
padding-top: 14px;
padding-bottom: 12px;
}
.filter-row {
display: flex;
align-items: center;
@@ -593,9 +588,13 @@ watch(
padding-bottom: 4px;
}
.equipment-table :deep(.el-table__inner-wrapper::before) {
display: none;
}
/* ========== 表格居中 ========== */
:deep(.ctms-table th.el-table__cell .cell),
:deep(.ctms-table td.el-table__cell .cell) {
.equipment-table :deep(th.el-table__cell .cell),
.equipment-table :deep(td.el-table__cell .cell) {
text-align: center;
}
</style>
+10 -11
View File
@@ -1,7 +1,7 @@
<template>
<div class="page ctms-page-shell">
<div class="page ctms-page-shell page--flush">
<div v-if="study.currentStudy" class="unified-shell ctms-table-card">
<section class="unified-action-bar">
<section class="unified-action-bar bar--flush">
<div class="card-header ctms-page-header-row">
<div>
<div class="card-title">{{ TEXT.modules.projectMilestones.listTitle }}</div>
@@ -22,7 +22,7 @@
</div>
</div>
</section>
<section class="unified-section ctms-page-content-grid">
<section class="unified-section ctms-page-content-grid section--flush-x section--flush-top section--flush-bottom">
<el-table :data="rows" v-loading="loading" class="ctms-table" style="width: 100%">
<el-table-column prop="name" :label="TEXT.modules.projectMilestones.columns.name" min-width="140" />
<el-table-column prop="planDate" :label="TEXT.modules.projectMilestones.columns.planDate" min-width="170">
@@ -450,12 +450,16 @@ watch(
flex-direction: column;
}
.ctms-page-content-grid {
gap: 0;
}
.card-header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
margin-bottom: 12px;
margin-bottom: 0;
flex-wrap: wrap;
}
@@ -657,12 +661,7 @@ watch(
background: #94a3b8;
}
:deep(.ctms-table th.el-table__cell .cell),
:deep(.ctms-table td.el-table__cell .cell) {
font-size: 11px;
padding-top: 4px;
padding-bottom: 4px;
padding-left: 6px;
padding-right: 6px;
.ctms-table :deep(.el-table__inner-wrapper::before) {
display: none;
}
</style>
+1 -51
View File
@@ -1,21 +1,7 @@
<template>
<div class="page ctms-page-shell">
<div class="page ctms-page-shell page--flush">
<div v-if="study.currentStudy" class="page-body">
<div class="unified-shell ctms-table-card">
<section class="unified-section">
<div class="kpi-grid">
<KpiCard
v-for="item in kpiItems"
:key="item.key"
class="kpi"
:title="item.title"
:value="item.value"
:subtext="item.subtext"
:type="item.type"
/>
</div>
</section>
<section class="unified-section">
<div class="overview-meta-inline">
<el-tag v-if="usingDemo" effect="plain" type="info" class="demo-tag">示例数据</el-tag>
@@ -83,7 +69,6 @@ import { fetchProjectOverview } from "../../api/overview";
import { fetchSites } from "../../api/sites";
import StateEmpty from "../../components/StateEmpty.vue";
import StateLoading from "../../components/StateLoading.vue";
import KpiCard from "../../components/KpiCard.vue";
import CenterProgressRow from "./project-overview/CenterProgressRow.vue";
import EnrollmentBarChart, { type EnrollmentBarItem } from "./project-overview/EnrollmentBarChart.vue";
import { adaptProjectOverview, type CenterOverview, type ProjectOverviewViewModel } from "./project-overview/overview.adapter";
@@ -99,35 +84,6 @@ const updatedAtLabel = ref(displayDateTime(new Date()));
const preferApi = String(import.meta.env.VITE_USE_OVERVIEW_API || "").toLowerCase() === "true";
const centers = computed(() => overview.value?.centers || []);
const kpiItems = computed(() => {
const summary = overview.value?.summary;
const totalTarget = summary?.total_target ?? 0;
const totalActual = summary?.total_actual ?? 0;
const activeCenters = centers.value.filter((item) => item.is_active !== false).length;
return [
{
key: "target",
title: "总入组目标",
value: totalTarget,
subtext: "项目整体计划",
type: "primary" as const,
},
{
key: "actual",
title: "当前入组",
value: totalActual,
subtext: `达成率 ${totalTarget > 0 ? Math.round((totalActual / totalTarget) * 100) : 0}%`,
type: "success" as const,
},
{
key: "centers",
title: "活跃中心",
value: activeCenters,
subtext: `中心总数 ${centers.value.length}`,
type: "info" as const,
},
];
});
const buildFallbackCentersFromSites = (siteList: any[]): CenterOverview[] =>
siteList.map((site: any) => ({
@@ -252,12 +208,6 @@ watch(
gap: 0;
}
.kpi-grid {
display: grid;
gap: 12px;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
}
.demo-tag {
border-color: var(--ctms-border-color);
color: var(--ctms-text-secondary);
@@ -1,8 +1,8 @@
<template>
<div class="page ctms-page-shell">
<div class="page ctms-page-shell page--flush">
<template v-if="study.currentStudy">
<el-card shadow="never" class="main-content-card unified-shell ctms-table-card">
<div class="filter-container unified-action-bar">
<div class="main-content-flat unified-shell">
<div class="filter-container unified-action-bar bar--flush">
<div class="filter-left">
<el-form :inline="true" :model="filters" class="filter-form">
<el-form-item label="问题分类" class="filter-item">
@@ -47,7 +47,7 @@
</div>
</div>
<div class="unified-section table-section">
<div class="unified-section table-section section--flush">
<el-table
class="issue-table"
:data="tableRows"
@@ -113,7 +113,7 @@
/>
</div>
</div>
</el-card>
</div>
<el-drawer
v-model="formDialogVisible"
@@ -723,7 +723,6 @@ watch(
justify-content: space-between;
gap: 16px;
flex-wrap: wrap;
padding: 16px 16px 0;
}
.filter-left {
@@ -734,7 +733,7 @@ watch(
.filter-form {
display: flex;
width: 100%;
gap: 12px;
gap: 8px;
align-items: center;
flex-wrap: wrap;
}
@@ -764,10 +763,6 @@ watch(
gap: 8px;
}
.table-section {
padding: 12px 0;
}
.issue-no {
font-weight: 600;
color: #24324a;
@@ -823,11 +818,15 @@ watch(
}
.pagination-wrap {
margin: 14px 16px 0;
margin: 10px 0 0;
display: flex;
justify-content: flex-end;
}
.issue-table :deep(.el-table__inner-wrapper::before) {
display: none;
}
.dialog-select {
width: 100%;
}
+12 -13
View File
@@ -1,7 +1,7 @@
<template>
<div class="page ctms-page-shell">
<el-card shadow="never" class="main-content-card unified-shell ctms-table-card">
<div class="filter-container unified-action-bar">
<div class="page ctms-page-shell page--flush">
<div class="main-content-flat unified-shell">
<div class="filter-container unified-action-bar bar--flush">
<el-form :inline="true" class="filter-form">
<el-form-item class="filter-item-form">
<el-input v-model="filters.keyword" :placeholder="TEXT.common.fields.keyword" clearable class="filter-input-comp">
@@ -33,8 +33,8 @@
</el-form-item>
</el-form>
</div>
<div class="unified-section table-section">
<el-table :data="filteredItems" v-loading="loading" style="width: 100%" table-layout="fixed">
<div class="unified-section table-section section--flush-x section--flush-top section--flush-bottom">
<el-table :data="filteredItems" v-loading="loading" style="width: 100%" table-layout="fixed" class="risk-table">
<el-table-column prop="pd_no" :label="TEXT.common.fields.pdNo" min-width="160" show-overflow-tooltip />
<el-table-column prop="subject_no" :label="TEXT.modules.subjectManagement.screeningNo" min-width="160" show-overflow-tooltip />
<el-table-column :label="TEXT.common.fields.site" min-width="160" show-overflow-tooltip>
@@ -54,7 +54,7 @@
</el-table>
<StateEmpty v-if="!loading && filteredItems.length === 0" :description="TEXT.modules.riskIssuePd.emptyDescription" />
</div>
</el-card>
</div>
</div>
</template>
@@ -162,21 +162,16 @@ onMounted(load);
flex-direction: column;
}
.table-section {
padding-top: 12px;
padding-bottom: 12px;
}
.filter-form {
display: flex;
width: 100%;
flex-wrap: wrap;
gap: 0;
gap: 8px;
}
.filter-item-form {
margin-bottom: 0;
margin-right: 12px;
margin-right: 0;
}
.filter-input-comp {
@@ -191,4 +186,8 @@ onMounted(load);
margin-bottom: 0;
margin-right: 0;
}
.risk-table :deep(.el-table__inner-wrapper::before) {
display: none;
}
</style>
+12 -13
View File
@@ -1,7 +1,7 @@
<template>
<div class="page ctms-page-shell">
<el-card shadow="never" class="main-content-card unified-shell ctms-table-card">
<div class="filter-container unified-action-bar">
<div class="page ctms-page-shell page--flush">
<div class="main-content-flat unified-shell">
<div class="filter-container unified-action-bar bar--flush">
<el-form :inline="true" class="filter-form">
<el-form-item class="filter-item-form">
<el-input v-model="filters.keyword" :placeholder="TEXT.common.fields.keyword" clearable class="filter-input-comp">
@@ -51,8 +51,8 @@
</el-form-item>
</el-form>
</div>
<div class="unified-section table-section">
<el-table :data="filteredItems" v-loading="loading" style="width: 100%">
<div class="unified-section table-section section--flush-x section--flush-top section--flush-bottom">
<el-table :data="filteredItems" v-loading="loading" style="width: 100%" class="risk-table">
<el-table-column prop="onset_date" :label="TEXT.common.fields.occurDate" width="140">
<template #default="scope">{{ displayDate(scope.row.onset_date) }}</template>
</el-table-column>
@@ -82,7 +82,7 @@
</el-table>
<StateEmpty v-if="!loading && filteredItems.length === 0" :description="TEXT.modules.riskIssueSae.emptyDescription" />
</div>
</el-card>
</div>
</div>
</template>
@@ -219,21 +219,16 @@ onMounted(load);
flex-direction: column;
}
.table-section {
padding-top: 12px;
padding-bottom: 12px;
}
.filter-form {
display: flex;
width: 100%;
flex-wrap: wrap;
gap: 0;
gap: 8px;
}
.filter-item-form {
margin-bottom: 0;
margin-right: 12px;
margin-right: 0;
}
.filter-input-comp {
@@ -248,4 +243,8 @@ onMounted(load);
margin-bottom: 0;
margin-right: 0;
}
.risk-table :deep(.el-table__inner-wrapper::before) {
display: none;
}
</style>
@@ -1,9 +1,9 @@
<template>
<div class="page">
<el-card shadow="never" class="main-content-card unified-shell">
<div class="page ctms-page-shell page--flush">
<div class="main-content-flat unified-shell">
<el-tabs v-model="activeTab" class="ctms-tabs">
<el-tab-pane :label="TEXT.modules.startupFeasibilityEthics.feasibilityTab" name="feasibility">
<div class="ctms-tab-content">
<div class="ctms-tab-content unified-section section--flush">
<div class="ctms-section-header">
<div class="ctms-section-title">{{ TEXT.modules.startupFeasibilityEthics.feasibilityTab }}</div>
<div class="ctms-section-actions">
@@ -63,7 +63,7 @@
</div>
</el-tab-pane>
<el-tab-pane :label="TEXT.modules.startupFeasibilityEthics.ethicsTab" name="ethics">
<div class="ctms-tab-content">
<div class="ctms-tab-content unified-section section--flush">
<div class="ctms-section-header">
<div class="ctms-section-title">{{ TEXT.modules.startupFeasibilityEthics.ethicsTab }}</div>
<div class="ctms-section-actions">
@@ -126,7 +126,7 @@
</div>
</el-tab-pane>
</el-tabs>
</el-card>
</div>
</div>
</template>
@@ -312,7 +312,15 @@ onMounted(() => {
}
.ctms-tab-content {
padding: 12px;
gap: 0;
}
.ctms-tab-content .ctms-section-header {
margin-bottom: 0;
}
.ctms-table :deep(.el-table__inner-wrapper::before) {
display: none;
}
</style>
+7 -3
View File
@@ -1,12 +1,12 @@
<template>
<div class="page">
<div class="page ctms-page-shell page--flush">
<div class="unified-shell">
<section class="unified-section">
<section class="unified-section kickoff-table-section section--flush">
<el-table
:data="kickoffRows"
v-loading="loading"
style="width: 100%"
class="ctms-table"
class="ctms-table kickoff-table"
:row-class-name="kickoffRowClass"
@row-click="onKickoffRowClick"
>
@@ -166,6 +166,10 @@ onMounted(() => {
gap: 0;
}
.kickoff-table :deep(.el-table__inner-wrapper::before) {
display: none;
}
</style>
<style>
+14 -10
View File
@@ -1,7 +1,7 @@
<template>
<div class="page ctms-page-shell">
<el-card shadow="never" class="main-content-card unified-shell ctms-table-card">
<div class="filter-container unified-action-bar">
<div class="page ctms-page-shell page--flush">
<div class="main-content-flat unified-shell">
<div class="filter-container unified-action-bar bar--flush">
<el-form :inline="true" :model="filters" class="filter-form">
<el-form-item label="" class="filter-item-form">
<el-input
@@ -54,7 +54,7 @@
</el-button>
</el-form>
</div>
<div class="unified-section table-section">
<div class="unified-section table-section section--flush-x section--flush-top section--flush-bottom">
<el-table
:data="pagedItems"
v-loading="loading"
@@ -118,7 +118,7 @@
</div>
<StateEmpty v-if="!loading && sortedItems.length === 0" :description="TEXT.modules.subjectManagement.empty" />
</div>
</el-card>
</div>
</div>
</template>
@@ -281,7 +281,7 @@ onMounted(async () => {
.filter-form {
display: flex;
width: 100%;
gap: 12px;
gap: 8px;
align-items: center;
}
@@ -300,9 +300,13 @@ onMounted(async () => {
}
.header-action-btn {
height: 36px;
padding: 0 20px;
border-radius: 8px;
height: 32px;
padding: 0 12px;
border-radius: 10px;
}
:deep(.ctms-table .el-table__inner-wrapper::before) {
display: none;
}
.subject-info-cell {
@@ -358,7 +362,7 @@ onMounted(async () => {
.pagination-wrap {
display: flex;
justify-content: flex-end;
margin-top: 14px;
margin-top: 10px;
}
</style>
+13 -1
View File
@@ -26,7 +26,7 @@
</el-descriptions-item>
</el-descriptions>
</section>
<section class="unified-section">
<section class="unified-section attachment-section">
<AttachmentList
v-if="recordId"
:study-id="studyId"
@@ -156,6 +156,18 @@ onMounted(() => {
});
</script>
<style scoped>
.page {
display: flex;
flex-direction: column;
gap: 0;
}
.attachment-section {
padding: 0;
}
</style>
<style scoped>
.page {
display: flex;
@@ -25,7 +25,7 @@
</el-descriptions-item>
</el-descriptions>
</section>
<section class="unified-section">
<section class="unified-section attachment-section">
<AttachmentList
v-if="recordId"
:study-id="studyId"
@@ -154,6 +154,18 @@ onMounted(() => {
});
</script>
<style scoped>
.page {
display: flex;
flex-direction: column;
gap: 0;
}
.attachment-section {
padding: 0;
}
</style>
<style scoped>
.page {
display: flex;
+12 -2
View File
@@ -41,7 +41,7 @@
</el-descriptions>
</section>
<section class="unified-section">
<section class="unified-section training-section">
<div class="training-header">
<div class="training-title">{{ TEXT.modules.startupMeetingAuth.trainingTab }}</div>
</div>
@@ -126,7 +126,7 @@
/>
</section>
<section v-if="meetingId" class="unified-section">
<section v-if="meetingId" class="unified-section attachment-section">
<div class="attachment-grid">
<AttachmentList
v-for="group in kickoffAttachmentGroups"
@@ -448,6 +448,16 @@ onMounted(async () => {
gap: 0;
}
.training-section,
.attachment-section {
padding-left: 0;
padding-right: 0;
}
.training-section {
padding-top: 0;
}
.training-header {
display: flex;
align-items: center;
+24 -10
View File
@@ -1,6 +1,6 @@
<template>
<div class="page">
<div class="page-header unified-action-bar">
<div class="page ctms-page-shell page--flush">
<div class="page-header unified-action-bar bar--flush">
<div>
<h1 class="page-title">{{ detail.subject_no || TEXT.modules.subjectManagement.screeningNo }}</h1>
<p class="page-subtitle">{{ TEXT.modules.subjectManagement.screeningNo }}</p>
@@ -25,7 +25,7 @@
</div>
</div>
<el-card class="unified-shell" v-loading="loading">
<el-card class="unified-shell subject-shell" v-loading="loading">
<el-descriptions :column="2" border>
<el-descriptions-item :label="TEXT.modules.subjectManagement.screeningNo">{{ detail.subject_no || TEXT.common.fallback }}</el-descriptions-item>
<el-descriptions-item :label="TEXT.common.fields.site">{{ siteMap[detail.site_id] || TEXT.common.fallback }}</el-descriptions-item>
@@ -76,7 +76,7 @@
</el-descriptions>
</el-card>
<el-card class="unified-shell">
<el-card class="unified-shell subject-shell">
<el-tabs v-model="activeTab">
<el-tab-pane :label="TEXT.modules.subjectDetail.tabs.history" name="history">
<div class="tab-actions">
@@ -84,7 +84,7 @@
{{ TEXT.common.actions.newHistory }}
</el-button>
</div>
<el-table :data="historyItems" v-loading="loadingHistory" style="width: 100%">
<el-table :data="historyItems" v-loading="loadingHistory" style="width: 100%" class="subject-detail-table">
<el-table-column prop="record_date" :label="TEXT.common.fields.recordDate" width="140">
<template #default="scope">{{ displayDate(scope.row.record_date) }}</template>
</el-table-column>
@@ -121,7 +121,7 @@
{{ TEXT.common.actions.newVisit }}
</el-button>
</div>
<el-table :data="visitItems" v-loading="loadingVisits" style="width: 100%">
<el-table :data="visitItems" v-loading="loadingVisits" style="width: 100%" class="subject-detail-table">
<el-table-column prop="visit_code" :label="TEXT.common.fields.visitCode" width="120" />
<el-table-column prop="planned_date" :label="TEXT.common.fields.plannedDate" min-width="180">
<template #default="scope">{{ displayDate(scope.row.planned_date) }}</template>
@@ -202,7 +202,7 @@
{{ TEXT.common.actions.newAe }}
</el-button>
</div>
<el-table :data="aeItems" v-loading="loadingAes" style="width: 100%">
<el-table :data="aeItems" v-loading="loadingAes" style="width: 100%" class="subject-detail-table">
<el-table-column prop="onset_date" :label="TEXT.common.fields.occurDate" width="140">
<template #default="scope">{{ displayDate(scope.row.onset_date) }}</template>
</el-table-column>
@@ -258,7 +258,7 @@
{{ TEXT.modules.subjectDetail.newPd }}
</el-button>
</div>
<el-table :data="pdItems" v-loading="loadingPds" style="width: 100%">
<el-table :data="pdItems" v-loading="loadingPds" style="width: 100%" class="subject-detail-table">
<el-table-column prop="pd_no" :label="TEXT.common.fields.pdNo" width="130" />
<el-table-column prop="pd_type" :label="TEXT.common.fields.pdType" min-width="140" />
<el-table-column prop="pd_level" :label="TEXT.common.fields.pdLevel" min-width="120">
@@ -1097,7 +1097,7 @@ onMounted(async () => {
.page {
display: flex;
flex-direction: column;
gap: 16px;
gap: 0;
}
.page-header {
@@ -1106,6 +1106,16 @@ onMounted(async () => {
align-items: flex-end;
}
.subject-shell {
border: 0;
border-radius: 0;
box-shadow: none;
}
.subject-shell :deep(.el-card__body) {
padding: 0;
}
.page-title {
margin: 0;
font-size: 22px;
@@ -1126,7 +1136,11 @@ onMounted(async () => {
.tab-actions {
display: flex;
justify-content: flex-end;
margin-bottom: 12px;
margin-bottom: 10px;
}
.subject-detail-table :deep(.el-table__inner-wrapper::before) {
display: none;
}
.ae-type-tag {
@@ -10,7 +10,7 @@
style="width: 100%"
v-loading="loading"
empty-text="暂无数据"
class="custom-table"
class="custom-table center-summary-table"
>
<el-table-column prop="name" label="管理中心" min-width="180">
<template #default="{ row }">
@@ -65,6 +65,29 @@ const getStageType = (status?: string) => {
margin-bottom: 24px;
}
.summary-table-container {
overflow: hidden;
}
.center-summary-table :deep(.el-table__inner-wrapper::before) {
display: none;
}
.summary-table-container :deep(.el-table th.el-table__cell) {
background: #f7f9fc;
}
.summary-table-container :deep(.el-table td.el-table__cell),
.summary-table-container :deep(.el-table th.el-table__cell) {
padding-left: 0;
padding-right: 0;
}
.summary-table-container :deep(.el-table td.el-table__cell) {
padding-top: 7px;
padding-bottom: 7px;
}
.box-header {
margin-bottom: 16px;
}