立项配置页初步优化
This commit is contained in:
@@ -1,7 +1,31 @@
|
||||
<template>
|
||||
<div class="page">
|
||||
<div class="page-header unified-action-bar">
|
||||
<div>
|
||||
<h1 class="page-title">{{ detail.subject_no || TEXT.modules.subjectManagement.screeningNo }}</h1>
|
||||
<p class="page-subtitle">{{ TEXT.modules.subjectManagement.screeningNo }}</p>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<el-button
|
||||
v-if="subjectEditing"
|
||||
type="primary"
|
||||
:loading="subjectSaving"
|
||||
:disabled="isReadOnly"
|
||||
@click="saveSubjectEdit"
|
||||
>
|
||||
{{ TEXT.common.actions.save }}
|
||||
</el-button>
|
||||
<el-button v-if="subjectEditing" :disabled="isReadOnly" @click="cancelSubjectEdit">
|
||||
{{ TEXT.common.actions.cancel }}
|
||||
</el-button>
|
||||
<el-button v-else type="primary" :disabled="isReadOnly" @click="startSubjectEdit">
|
||||
{{ TEXT.common.actions.edit }}
|
||||
</el-button>
|
||||
<el-button @click="goBack">{{ TEXT.common.actions.back }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-card v-loading="loading">
|
||||
<el-card class="unified-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>
|
||||
@@ -52,7 +76,7 @@
|
||||
</el-descriptions>
|
||||
</el-card>
|
||||
|
||||
<el-card>
|
||||
<el-card class="unified-shell">
|
||||
<el-tabs v-model="activeTab">
|
||||
<el-tab-pane :label="TEXT.modules.subjectDetail.tabs.history" name="history">
|
||||
<div class="tab-actions">
|
||||
@@ -182,17 +206,17 @@
|
||||
<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>
|
||||
<el-table-column :label="TEXT.common.fields.aeType" width="120">
|
||||
<template #default="scope">
|
||||
<el-tag size="small" effect="light" :class="getAeTypeClass(scope.row)">
|
||||
{{ getAeTypeLabel(scope.row) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="term" :label="TEXT.common.fields.title" min-width="140" />
|
||||
<el-table-column prop="seriousness" :label="TEXT.common.fields.seriousness" width="120">
|
||||
<template #default="scope">
|
||||
<el-tag
|
||||
:type="getAeSeriousnessType(normalizeSeriousness(scope.row.seriousness))"
|
||||
size="small"
|
||||
effect="light"
|
||||
class="seriousness-tag"
|
||||
>
|
||||
{{ displayEnum(TEXT.enums.aeSeriousness, normalizeSeriousness(scope.row.seriousness)) }}
|
||||
</el-tag>
|
||||
{{ displayEnum(TEXT.enums.aeSeriousness, normalizeSeriousness(scope.row.seriousness)) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="causality" :label="TEXT.common.fields.causality" width="120">
|
||||
@@ -227,6 +251,10 @@
|
||||
</el-table>
|
||||
<StateEmpty v-if="!loadingAes && aeItems.length === 0" :description="TEXT.modules.subjectDetail.emptyAe" />
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane :label="TEXT.modules.subjectDetail.tabs.pd" name="pd">
|
||||
<StateEmpty :description="TEXT.modules.subjectDetail.emptyPd" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-card>
|
||||
|
||||
@@ -289,6 +317,12 @@
|
||||
<el-option v-for="option in aeSeriousnessOptions" :key="option.value" :label="option.label" :value="option.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="TEXT.common.fields.isSae">
|
||||
<el-switch v-model="aeForm.is_sae" @change="onSaeChanged" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="TEXT.common.fields.isSusar">
|
||||
<el-switch v-model="aeForm.is_susar" @change="onSusarChanged" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="TEXT.common.fields.causality">
|
||||
<el-select v-model="aeForm.causality" clearable>
|
||||
<el-option v-for="option in aeCausalityOptions" :key="option.value" :label="option.label" :value="option.value" />
|
||||
@@ -312,7 +346,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, ref } from "vue";
|
||||
import { computed, onMounted, reactive, ref, watch } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { useStudyStore } from "../../store/study";
|
||||
@@ -330,6 +364,10 @@ const router = useRouter();
|
||||
const study = useStudyStore();
|
||||
const subjectId = route.params.subjectId as string;
|
||||
const studyId = study.currentStudy?.id || "";
|
||||
const resolveTabFromQuery = () => {
|
||||
const tab = typeof route.query.tab === "string" ? route.query.tab : "";
|
||||
return ["history", "visits", "ae", "pd"].includes(tab) ? tab : "history";
|
||||
};
|
||||
|
||||
const loading = ref(false);
|
||||
const detail = reactive<any>({
|
||||
@@ -356,7 +394,7 @@ const subjectForm = reactive({
|
||||
const siteMap = ref<Record<string, string>>({});
|
||||
const siteActiveMap = ref<Record<string, boolean>>({});
|
||||
const isReadOnly = computed(() => !!detail.site_id && siteActiveMap.value[detail.site_id] === false);
|
||||
const activeTab = ref("history");
|
||||
const activeTab = ref(resolveTabFromQuery());
|
||||
|
||||
const historyItems = ref<any[]>([]);
|
||||
const loadingHistory = ref(false);
|
||||
@@ -410,6 +448,8 @@ const aeForm = reactive({
|
||||
term: "",
|
||||
onset_date: "",
|
||||
seriousness: "I",
|
||||
is_sae: false,
|
||||
is_susar: false,
|
||||
causality: "",
|
||||
outcome: "",
|
||||
description: "",
|
||||
@@ -714,19 +754,25 @@ const getVisitStatusTagType = (status: string) => {
|
||||
}
|
||||
};
|
||||
|
||||
const getAeSeriousnessType = (seriousness: string) => {
|
||||
switch (seriousness) {
|
||||
case "I":
|
||||
return "info";
|
||||
case "II":
|
||||
return "success";
|
||||
case "III":
|
||||
return "warning";
|
||||
case "IV":
|
||||
case "V":
|
||||
return "danger";
|
||||
default:
|
||||
return "info";
|
||||
const getAeTypeKey = (row: any) => {
|
||||
if (row?.is_susar) return "susar";
|
||||
if (row?.is_sae) return "sae";
|
||||
return "ae";
|
||||
};
|
||||
|
||||
const getAeTypeLabel = (row: any) => TEXT.modules.subjectDetail.aeType[getAeTypeKey(row)];
|
||||
|
||||
const getAeTypeClass = (row: any) => `ae-type-tag ae-type-${getAeTypeKey(row)}`;
|
||||
|
||||
const onSaeChanged = (value: boolean) => {
|
||||
if (!value) {
|
||||
aeForm.is_susar = false;
|
||||
}
|
||||
};
|
||||
|
||||
const onSusarChanged = (value: boolean) => {
|
||||
if (value) {
|
||||
aeForm.is_sae = true;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -803,6 +849,8 @@ const openAeDialog = (row?: any) => {
|
||||
term: row?.term || "",
|
||||
onset_date: row?.onset_date || "",
|
||||
seriousness: normalizeSeriousness(row?.seriousness),
|
||||
is_sae: !!row?.is_sae || !!row?.is_susar,
|
||||
is_susar: !!row?.is_susar,
|
||||
causality: row?.causality || "",
|
||||
outcome: row?.outcome || "",
|
||||
description: row?.description || "",
|
||||
@@ -823,6 +871,8 @@ const saveAe = async () => {
|
||||
term: aeForm.term,
|
||||
onset_date: aeForm.onset_date || null,
|
||||
seriousness: aeForm.seriousness,
|
||||
is_sae: !!aeForm.is_sae || !!aeForm.is_susar,
|
||||
is_susar: !!aeForm.is_susar,
|
||||
causality: aeForm.causality || null,
|
||||
outcome: aeForm.outcome || null,
|
||||
description: aeForm.description || null,
|
||||
@@ -857,6 +907,13 @@ const removeAe = async (row: any) => {
|
||||
|
||||
const goBack = () => router.push("/subjects");
|
||||
|
||||
watch(
|
||||
() => route.query.tab,
|
||||
() => {
|
||||
activeTab.value = resolveTabFromQuery();
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(async () => {
|
||||
await loadSites();
|
||||
await loadSubject();
|
||||
@@ -901,4 +958,23 @@ onMounted(async () => {
|
||||
justify-content: flex-end;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.ae-type-tag {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.ae-type-ae {
|
||||
color: #ffffff;
|
||||
background-color: #3b82f6;
|
||||
}
|
||||
|
||||
.ae-type-sae {
|
||||
color: #ffffff;
|
||||
background-color: #ef4444;
|
||||
}
|
||||
|
||||
.ae-type-susar {
|
||||
color: #ffffff;
|
||||
background-color: #8b5cf6;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="page">
|
||||
<div class="page-header">
|
||||
<div class="page-header unified-action-bar">
|
||||
<div>
|
||||
<h1 class="page-title">{{ isEdit ? TEXT.modules.subjectForm.titleEdit : TEXT.modules.subjectForm.titleNew }}</h1>
|
||||
<p class="page-subtitle">{{ TEXT.modules.subjectForm.subtitle }}</p>
|
||||
@@ -8,7 +8,7 @@
|
||||
<el-button @click="goBack">{{ TEXT.common.actions.back }}</el-button>
|
||||
</div>
|
||||
|
||||
<el-card>
|
||||
<el-card class="unified-shell">
|
||||
<el-form :model="form" label-width="120px">
|
||||
<el-form-item :label="TEXT.modules.subjectManagement.screeningNo" required>
|
||||
<el-input
|
||||
|
||||
Reference in New Issue
Block a user