修复伦理与启动管理的显示问题
This commit is contained in:
+10
-2
@@ -321,6 +321,7 @@ CREATE TABLE public.milestones (
|
|||||||
planned_date date,
|
planned_date date,
|
||||||
actual_date date,
|
actual_date date,
|
||||||
status character varying(20) NOT NULL,
|
status character varying(20) NOT NULL,
|
||||||
|
site_id uuid,
|
||||||
owner_id uuid,
|
owner_id uuid,
|
||||||
notes text,
|
notes text,
|
||||||
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
created_at timestamp with time zone DEFAULT now() NOT NULL,
|
||||||
@@ -603,8 +604,8 @@ bbbbbbb1-bbbb-bbbb-bbbb-bbbbbbbbbbb1 44444444-4444-4444-4444-444444444444 555555
|
|||||||
-- Data for Name: milestones; Type: TABLE DATA; Schema: public; Owner: ctms_user
|
-- Data for Name: milestones; Type: TABLE DATA; Schema: public; Owner: ctms_user
|
||||||
--
|
--
|
||||||
|
|
||||||
COPY public.milestones (id, study_id, type, name, planned_date, actual_date, status, owner_id, notes, created_at, updated_at) FROM stdin;
|
COPY public.milestones (id, study_id, type, name, planned_date, actual_date, status, site_id, owner_id, notes, created_at, updated_at) FROM stdin;
|
||||||
66666666-6666-6666-6666-666666666666 44444444-4444-4444-4444-444444444444 SIV 启动会 2025-01-10 2025-01-12 DONE 22222222-2222-2222-2222-222222222222 现场启动完成 2025-12-18 00:55:17.568504+00 2025-12-18 00:55:17.568504+00
|
66666666-6666-6666-6666-666666666666 44444444-4444-4444-4444-444444444444 SIV 启动会 2025-01-10 2025-01-12 DONE 55555555-5555-5555-5555-555555555555 22222222-2222-2222-2222-222222222222 现场启动完成 2025-12-18 00:55:17.568504+00 2025-12-18 00:55:17.568504+00
|
||||||
\.
|
\.
|
||||||
|
|
||||||
|
|
||||||
@@ -1503,6 +1504,13 @@ ALTER TABLE ONLY public.issues
|
|||||||
ALTER TABLE ONLY public.milestones
|
ALTER TABLE ONLY public.milestones
|
||||||
ADD CONSTRAINT milestones_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES public.users(id);
|
ADD CONSTRAINT milestones_owner_id_fkey FOREIGN KEY (owner_id) REFERENCES public.users(id);
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Name: milestones milestones_site_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: ctms_user
|
||||||
|
--
|
||||||
|
|
||||||
|
ALTER TABLE ONLY public.milestones
|
||||||
|
ADD CONSTRAINT milestones_site_id_fkey FOREIGN KEY (site_id) REFERENCES public.sites(id);
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Name: milestones milestones_study_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: ctms_user
|
-- Name: milestones milestones_study_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: ctms_user
|
||||||
|
|||||||
Vendored
+2
-2
@@ -4,8 +4,8 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>CTMS</title>
|
<title>CTMS</title>
|
||||||
<script type="module" crossorigin src="/assets/index-BJ37HHdy.js"></script>
|
<script type="module" crossorigin src="/assets/index-B28dIBQG.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-DNzNq5qy.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-Dvp__k11.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app"></div>
|
<div id="app"></div>
|
||||||
|
|||||||
@@ -101,6 +101,23 @@ import { exportAuditCsv } from "../../audit/export/auditExportService";
|
|||||||
import { logAudit } from "../../audit";
|
import { logAudit } from "../../audit";
|
||||||
import { displayDateTime } from "../../utils/display";
|
import { displayDateTime } from "../../utils/display";
|
||||||
|
|
||||||
|
const buildLogKey = (log: any) => {
|
||||||
|
if (log.id) return `id:${log.id}`;
|
||||||
|
return `${log.action || log.eventType || "event"}-${log.entity_id || log.entityId || ""}-${log.created_at || log.timestamp || ""}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const dedupeLogs = (list: any[]) => {
|
||||||
|
const seen = new Set<string>();
|
||||||
|
const result: any[] = [];
|
||||||
|
list.forEach((log) => {
|
||||||
|
const key = buildLogKey(log);
|
||||||
|
if (seen.has(key)) return;
|
||||||
|
seen.add(key);
|
||||||
|
result.push(log);
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
const study = useStudyStore();
|
const study = useStudyStore();
|
||||||
const auth = useAuthStore();
|
const auth = useAuthStore();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@@ -163,7 +180,7 @@ const loadLogs = async () => {
|
|||||||
const { data } = await fetchAuditLogs(study.currentStudy.id, params);
|
const { data } = await fetchAuditLogs(study.currentStudy.id, params);
|
||||||
const items = Array.isArray(data) ? data : (data as any).items || [];
|
const items = Array.isArray(data) ? data : (data as any).items || [];
|
||||||
const local = loadLocalLogs();
|
const local = loadLocalLogs();
|
||||||
const merged = [...items, ...local];
|
const merged = dedupeLogs([...items, ...local]);
|
||||||
rawLogs.value = merged;
|
rawLogs.value = merged;
|
||||||
total.value = (data as any).total || merged.length;
|
total.value = (data as any).total || merged.length;
|
||||||
enrichLogs();
|
enrichLogs();
|
||||||
@@ -233,7 +250,7 @@ const fetchAllForExport = async () => {
|
|||||||
const { data } = await fetchAuditLogs(study.currentStudy.id, params);
|
const { data } = await fetchAuditLogs(study.currentStudy.id, params);
|
||||||
const items = Array.isArray(data) ? data : (data as any).items || [];
|
const items = Array.isArray(data) ? data : (data as any).items || [];
|
||||||
const local = loadLocalLogs();
|
const local = loadLocalLogs();
|
||||||
const merged = [...items, ...local];
|
const merged = dedupeLogs([...items, ...local]);
|
||||||
const userMap = users.value.reduce<Record<string, string>>((acc, cur) => {
|
const userMap = users.value.reduce<Record<string, string>>((acc, cur) => {
|
||||||
acc[cur.id] = cur.username;
|
acc[cur.id] = cur.username;
|
||||||
return acc;
|
return acc;
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
<el-descriptions-item label="项目名称">{{ project.name }}</el-descriptions-item>
|
<el-descriptions-item label="项目名称">{{ project.name }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="申办方">{{ project.sponsor || "—" }}</el-descriptions-item>
|
<el-descriptions-item label="申办方">{{ project.sponsor || "—" }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="状态">{{ statusLabel(project.status) }}</el-descriptions-item>
|
<el-descriptions-item label="状态">{{ statusLabel(project.status) }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="创建时间">{{ project.created_at || "—" }}</el-descriptions-item>
|
<el-descriptions-item label="创建时间">{{ displayDateTime(project.created_at) }}</el-descriptions-item>
|
||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
<el-skeleton v-else :rows="4" animated />
|
<el-skeleton v-else :rows="4" animated />
|
||||||
</el-card>
|
</el-card>
|
||||||
@@ -48,6 +48,7 @@ import { fetchStudyDetail } from "../../api/studies";
|
|||||||
import type { Study } from "../../types/api";
|
import type { Study } from "../../types/api";
|
||||||
import AttachmentList from "../../components/attachments/AttachmentList.vue";
|
import AttachmentList from "../../components/attachments/AttachmentList.vue";
|
||||||
import { useStudyStore } from "../../store/study";
|
import { useStudyStore } from "../../store/study";
|
||||||
|
import { displayDateTime } from "../../utils/display";
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|||||||
Reference in New Issue
Block a user