管理后台前后端逻辑、UI美化

This commit is contained in:
Cheng Zhou
2026-01-16 13:50:08 +08:00
parent 05c1f9579a
commit 7fdcfdaadd
82 changed files with 3210 additions and 549 deletions
+135 -16
View File
@@ -11,7 +11,9 @@
<el-button @click="cancelSubjectEdit">{{ TEXT.common.actions.cancel }}</el-button>
</template>
<template v-else>
<el-button type="primary" @click="startSubjectEdit">{{ TEXT.common.actions.edit }}</el-button>
<el-button type="primary" :disabled="isReadOnly" @click="startSubjectEdit">
{{ TEXT.common.actions.edit }}
</el-button>
<el-button @click="goBack">{{ TEXT.common.actions.back }}</el-button>
</template>
</div>
@@ -72,7 +74,9 @@
<el-tabs v-model="activeTab">
<el-tab-pane :label="TEXT.modules.subjectDetail.tabs.history" name="history">
<div class="tab-actions">
<el-button type="primary" @click="openHistoryDialog()">{{ TEXT.common.actions.newHistory }}</el-button>
<el-button type="primary" :disabled="isReadOnly" @click="openHistoryDialog()">
{{ TEXT.common.actions.newHistory }}
</el-button>
</div>
<el-table :data="historyItems" v-loading="loadingHistory" style="width: 100%">
<el-table-column prop="record_date" :label="TEXT.common.fields.recordDate" width="140">
@@ -81,8 +85,24 @@
<el-table-column prop="content" :label="TEXT.common.fields.content" min-width="240" />
<el-table-column :label="TEXT.common.labels.actions" width="200">
<template #default="scope">
<el-button link type="primary" size="small" @click="openHistoryDialog(scope.row)">{{ TEXT.common.actions.edit }}</el-button>
<el-button link type="danger" size="small" @click="removeHistory(scope.row)">{{ TEXT.common.actions.delete }}</el-button>
<el-button
link
type="primary"
size="small"
:disabled="isReadOnly"
@click="openHistoryDialog(scope.row)"
>
{{ TEXT.common.actions.edit }}
</el-button>
<el-button
link
type="danger"
size="small"
:disabled="isReadOnly"
@click="removeHistory(scope.row)"
>
{{ TEXT.common.actions.delete }}
</el-button>
</template>
</el-table-column>
</el-table>
@@ -91,7 +111,9 @@
<el-tab-pane :label="TEXT.modules.subjectDetail.tabs.visits" name="visits">
<div class="tab-actions">
<el-button type="primary" @click="openVisitDialog()">{{ TEXT.common.actions.newVisit }}</el-button>
<el-button type="primary" :disabled="isReadOnly" @click="openVisitDialog()">
{{ TEXT.common.actions.newVisit }}
</el-button>
</div>
<el-table :data="visitItems" v-loading="loadingVisits" style="width: 100%">
<el-table-column prop="visit_code" :label="TEXT.common.fields.visitCode" width="120" />
@@ -135,12 +157,32 @@
<el-table-column :label="TEXT.common.labels.actions" min-width="160">
<template #default="scope">
<template v-if="visitEditingRowId === scope.row.id">
<el-button link type="primary" size="small" @click="saveVisitRow(scope.row)">{{ TEXT.common.actions.save }}</el-button>
<el-button link size="small" @click="cancelVisitRow">{{ TEXT.common.actions.cancel }}</el-button>
<el-button link type="primary" size="small" :disabled="isReadOnly" @click="saveVisitRow(scope.row)">
{{ TEXT.common.actions.save }}
</el-button>
<el-button link size="small" :disabled="isReadOnly" @click="cancelVisitRow">
{{ TEXT.common.actions.cancel }}
</el-button>
</template>
<template v-else>
<el-button link type="primary" size="small" @click="startVisitRowEdit(scope.row)">{{ TEXT.common.actions.edit }}</el-button>
<el-button link type="danger" size="small" @click="removeVisit(scope.row)">{{ TEXT.common.actions.delete }}</el-button>
<el-button
link
type="primary"
size="small"
:disabled="isReadOnly"
@click="startVisitRowEdit(scope.row)"
>
{{ TEXT.common.actions.edit }}
</el-button>
<el-button
link
type="danger"
size="small"
:disabled="isReadOnly"
@click="removeVisit(scope.row)"
>
{{ TEXT.common.actions.delete }}
</el-button>
</template>
</template>
</el-table-column>
@@ -150,7 +192,9 @@
<el-tab-pane :label="TEXT.modules.subjectDetail.tabs.ae" name="ae">
<div class="tab-actions">
<el-button type="primary" @click="openAeDialog()">{{ TEXT.common.actions.newAe }}</el-button>
<el-button type="primary" :disabled="isReadOnly" @click="openAeDialog()">
{{ TEXT.common.actions.newAe }}
</el-button>
</div>
<el-table :data="aeItems" v-loading="loadingAes" style="width: 100%">
<el-table-column prop="onset_date" :label="TEXT.common.fields.occurDate" width="140">
@@ -165,8 +209,24 @@
<el-table-column prop="description" :label="TEXT.common.fields.remark" min-width="200" />
<el-table-column :label="TEXT.common.labels.actions" width="200">
<template #default="scope">
<el-button link type="primary" size="small" @click="openAeDialog(scope.row)">{{ TEXT.common.actions.edit }}</el-button>
<el-button link type="danger" size="small" @click="removeAe(scope.row)">{{ TEXT.common.actions.delete }}</el-button>
<el-button
link
type="primary"
size="small"
:disabled="isReadOnly"
@click="openAeDialog(scope.row)"
>
{{ TEXT.common.actions.edit }}
</el-button>
<el-button
link
type="danger"
size="small"
:disabled="isReadOnly"
@click="removeAe(scope.row)"
>
{{ TEXT.common.actions.delete }}
</el-button>
</template>
</el-table-column>
</el-table>
@@ -186,7 +246,7 @@
</el-form>
<template #footer>
<el-button @click="historyDialogVisible = false">{{ TEXT.common.actions.cancel }}</el-button>
<el-button type="primary" @click="saveHistory">{{ TEXT.common.actions.save }}</el-button>
<el-button type="primary" :disabled="isReadOnly" @click="saveHistory">{{ TEXT.common.actions.save }}</el-button>
</template>
</el-dialog>
@@ -217,7 +277,7 @@
</el-form>
<template #footer>
<el-button @click="visitDialogVisible = false">{{ TEXT.common.actions.cancel }}</el-button>
<el-button type="primary" @click="saveVisit">{{ TEXT.common.actions.save }}</el-button>
<el-button type="primary" :disabled="isReadOnly" @click="saveVisit">{{ TEXT.common.actions.save }}</el-button>
</template>
</el-dialog>
@@ -254,14 +314,14 @@
</el-form>
<template #footer>
<el-button @click="aeDialogVisible = false">{{ TEXT.common.actions.cancel }}</el-button>
<el-button type="primary" @click="saveAe">{{ TEXT.common.actions.save }}</el-button>
<el-button type="primary" :disabled="isReadOnly" @click="saveAe">{{ TEXT.common.actions.save }}</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { onMounted, reactive, ref } from "vue";
import { computed, onMounted, reactive, ref } from "vue";
import { useRoute, useRouter } from "vue-router";
import { ElMessage, ElMessageBox } from "element-plus";
import { useStudyStore } from "../../store/study";
@@ -303,6 +363,8 @@ 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 historyItems = ref<any[]>([]);
@@ -355,8 +417,13 @@ const loadSites = async () => {
acc[site.id] = site.name;
return acc;
}, {});
siteActiveMap.value = list.reduce((acc: Record<string, boolean>, site: any) => {
acc[site.id] = !!site.is_active;
return acc;
}, {});
} catch {
siteMap.value = {};
siteActiveMap.value = {};
}
};
@@ -374,6 +441,10 @@ const loadSubject = async () => {
};
const startSubjectEdit = () => {
if (isReadOnly.value) {
ElMessage.warning("中心已停用");
return;
}
subjectForm.status = detail.status || "SCREENING";
subjectForm.consent_date = detail.consent_date || "";
subjectForm.enrollment_date = detail.enrollment_date || "";
@@ -387,6 +458,10 @@ const cancelSubjectEdit = () => {
};
const saveSubjectEdit = async () => {
if (isReadOnly.value) {
ElMessage.warning("中心已停用");
return;
}
if (!studyId || !subjectId) return;
subjectSaving.value = true;
try {
@@ -422,6 +497,10 @@ const loadHistories = async () => {
};
const openHistoryDialog = (row?: any) => {
if (isReadOnly.value) {
ElMessage.warning("中心已停用");
return;
}
historyEditingId.value = row?.id || null;
historyForm.record_date = row?.record_date || "";
historyForm.content = row?.content || "";
@@ -429,6 +508,10 @@ const openHistoryDialog = (row?: any) => {
};
const saveHistory = async () => {
if (isReadOnly.value) {
ElMessage.warning("中心已停用");
return;
}
if (!studyId || !subjectId) return;
try {
const payload = {
@@ -449,6 +532,10 @@ const saveHistory = async () => {
};
const removeHistory = async (row: any) => {
if (isReadOnly.value) {
ElMessage.warning("中心已停用");
return;
}
if (!studyId || !subjectId) return;
const ok = await ElMessageBox.confirm(TEXT.common.confirm.delete, TEXT.common.labels.tips).catch(() => null);
if (!ok) return;
@@ -474,6 +561,10 @@ const loadVisits = async () => {
};
const openVisitDialog = (row?: any) => {
if (isReadOnly.value) {
ElMessage.warning("中心已停用");
return;
}
visitEditingId.value = row?.id || null;
if (row) {
visitForm.actual_date = row.actual_date || "";
@@ -490,6 +581,10 @@ const openVisitDialog = (row?: any) => {
};
const startVisitRowEdit = (row: any) => {
if (isReadOnly.value) {
ElMessage.warning("中心已停用");
return;
}
visitEditingRowId.value = row?.id || null;
visitRowDraft.actual_date = row?.actual_date || "";
visitRowDraft.notes = row?.notes || "";
@@ -500,6 +595,10 @@ const cancelVisitRow = () => {
};
const saveVisitRow = async (row: any) => {
if (isReadOnly.value) {
ElMessage.warning("中心已停用");
return;
}
if (!studyId || !subjectId || !row?.id) return;
try {
const payload = {
@@ -559,6 +658,10 @@ const getVisitStatusTagType = (status: string) => {
};
const saveVisit = async () => {
if (isReadOnly.value) {
ElMessage.warning("中心已停用");
return;
}
if (!studyId || !subjectId) return;
try {
if (visitEditingId.value) {
@@ -585,6 +688,10 @@ const saveVisit = async () => {
};
const removeVisit = async (row: any) => {
if (isReadOnly.value) {
ElMessage.warning("中心已停用");
return;
}
if (!studyId || !subjectId) return;
const ok = await ElMessageBox.confirm(TEXT.common.confirm.delete, TEXT.common.labels.tips).catch(() => null);
if (!ok) return;
@@ -610,6 +717,10 @@ const loadAes = async () => {
};
const openAeDialog = (row?: any) => {
if (isReadOnly.value) {
ElMessage.warning("中心已停用");
return;
}
aeEditingId.value = row?.id || null;
Object.assign(aeForm, {
term: row?.term || "",
@@ -624,6 +735,10 @@ const openAeDialog = (row?: any) => {
};
const saveAe = async () => {
if (isReadOnly.value) {
ElMessage.warning("中心已停用");
return;
}
if (!studyId || !subjectId) return;
try {
const payload = {
@@ -650,6 +765,10 @@ const saveAe = async () => {
};
const removeAe = async (row: any) => {
if (isReadOnly.value) {
ElMessage.warning("中心已停用");
return;
}
if (!studyId) return;
const ok = await ElMessageBox.confirm(TEXT.common.confirm.delete, TEXT.common.labels.tips).catch(() => null);
if (!ok) return;