|
|
@@ -0,0 +1,241 @@
|
|
|
+import type {
|
|
|
+ AuditRecordDTO,
|
|
|
+ AuditRecordVO,
|
|
|
+} from '#/request/schema/audit-record';
|
|
|
+
|
|
|
+import { z } from '#/adapter/form';
|
|
|
+import { decodeList } from '#/request/schema';
|
|
|
+import { decodeAuditRecord } from '#/request/schema/audit-record';
|
|
|
+
|
|
|
+// ---------------------------------------------------------------------------
|
|
|
+// 枚举
|
|
|
+// ---------------------------------------------------------------------------
|
|
|
+
|
|
|
+export type ResearchReportCategory =
|
|
|
+ | 'basic'
|
|
|
+ | 'clinical'
|
|
|
+ | 'epidemiological'
|
|
|
+ | 'observational'
|
|
|
+ | 'translational';
|
|
|
+
|
|
|
+export type ResearchReportStatus = 'completed' | 'in_progress' | 'paused';
|
|
|
+
|
|
|
+export const RESEARCH_REPORT_CATEGORY_OPTIONS = [
|
|
|
+ { label: '临床研究', value: 'clinical' },
|
|
|
+ { label: '基础研究', value: 'basic' },
|
|
|
+ { label: '流行病学研究', value: 'epidemiological' },
|
|
|
+ { label: '观察性研究', value: 'observational' },
|
|
|
+ { label: '转化医学研究', value: 'translational' },
|
|
|
+] as const satisfies ReadonlyArray<{
|
|
|
+ label: string;
|
|
|
+ value: ResearchReportCategory;
|
|
|
+}>;
|
|
|
+
|
|
|
+export const RESEARCH_REPORT_STATUS_OPTIONS = [
|
|
|
+ { label: '进行中', value: 'in_progress' },
|
|
|
+ { label: '已完成', value: 'completed' },
|
|
|
+ { label: '已暂停', value: 'paused' },
|
|
|
+] as const satisfies ReadonlyArray<{
|
|
|
+ label: string;
|
|
|
+ value: ResearchReportStatus;
|
|
|
+}>;
|
|
|
+
|
|
|
+export function getResearchReportCategoryLabel(
|
|
|
+ category?: ResearchReportCategory,
|
|
|
+) {
|
|
|
+ return (
|
|
|
+ RESEARCH_REPORT_CATEGORY_OPTIONS.find((item) => item.value === category)
|
|
|
+ ?.label ?? ''
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
+export function getResearchReportStatusLabel(status?: ResearchReportStatus) {
|
|
|
+ return (
|
|
|
+ RESEARCH_REPORT_STATUS_OPTIONS.find((item) => item.value === status)
|
|
|
+ ?.label ?? ''
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
+export function getResearchReportStatusColor(status?: ResearchReportStatus) {
|
|
|
+ switch (status) {
|
|
|
+ case 'completed': {
|
|
|
+ return 'success';
|
|
|
+ }
|
|
|
+ case 'in_progress': {
|
|
|
+ return 'processing';
|
|
|
+ }
|
|
|
+ case 'paused': {
|
|
|
+ return 'default';
|
|
|
+ }
|
|
|
+ default: {
|
|
|
+ return 'default';
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+export function formatResearchPeriod(
|
|
|
+ startDate?: string,
|
|
|
+ endDate?: string,
|
|
|
+): string {
|
|
|
+ if (!startDate && !endDate) return '';
|
|
|
+ if (startDate && endDate) return `${startDate} 至 ${endDate}`;
|
|
|
+ return startDate ?? endDate ?? '';
|
|
|
+}
|
|
|
+
|
|
|
+// ---------------------------------------------------------------------------
|
|
|
+// DTO
|
|
|
+// ---------------------------------------------------------------------------
|
|
|
+
|
|
|
+export interface ResearchReportDTO extends AuditRecordDTO {
|
|
|
+ id?: number | string;
|
|
|
+ personalStudioId?: number | string;
|
|
|
+ status?: ResearchReportStatus;
|
|
|
+ category?: ResearchReportCategory;
|
|
|
+ title?: string;
|
|
|
+ leader?: string;
|
|
|
+ startDate?: string;
|
|
|
+ endDate?: string;
|
|
|
+ fundingSource?: string;
|
|
|
+ projectNumber?: string;
|
|
|
+ keywords?: string;
|
|
|
+ abstract?: string;
|
|
|
+ objectives?: string;
|
|
|
+ methods?: string;
|
|
|
+ expectedResults?: string;
|
|
|
+ fileUrl?: string;
|
|
|
+ viewCount?: number;
|
|
|
+}
|
|
|
+
|
|
|
+export interface ResearchReportQueryDTO {
|
|
|
+ mixture?: string;
|
|
|
+ personalStudioId?: number | string;
|
|
|
+ category?: ResearchReportCategory;
|
|
|
+ status?: ResearchReportStatus;
|
|
|
+ pageNum?: number;
|
|
|
+ pageSize?: number;
|
|
|
+}
|
|
|
+
|
|
|
+// ---------------------------------------------------------------------------
|
|
|
+// VO
|
|
|
+// ---------------------------------------------------------------------------
|
|
|
+
|
|
|
+export interface ResearchReportVO extends AuditRecordVO {
|
|
|
+ id?: string;
|
|
|
+ workroomId: string;
|
|
|
+ status: ResearchReportStatus;
|
|
|
+ category: ResearchReportCategory;
|
|
|
+ title: string;
|
|
|
+ leader: string;
|
|
|
+ startDate: string;
|
|
|
+ endDate?: string;
|
|
|
+ fundingSource?: string;
|
|
|
+ projectNumber?: string;
|
|
|
+ keywords?: string;
|
|
|
+ abstract: string;
|
|
|
+ objectives?: string;
|
|
|
+ methods?: string;
|
|
|
+ expectedResults?: string;
|
|
|
+ pdfUrl?: string;
|
|
|
+ viewCount?: number;
|
|
|
+}
|
|
|
+
|
|
|
+export type ResearchReportSubmitVO = ResearchReportVO;
|
|
|
+
|
|
|
+export interface ResearchReportQueryVO {
|
|
|
+ keyword?: string;
|
|
|
+ workroomId?: string;
|
|
|
+ category?: ResearchReportCategory;
|
|
|
+ status?: ResearchReportStatus;
|
|
|
+}
|
|
|
+
|
|
|
+// ---------------------------------------------------------------------------
|
|
|
+// Schema
|
|
|
+// ---------------------------------------------------------------------------
|
|
|
+
|
|
|
+export const ResearchReportVOSchema = z.object({
|
|
|
+ id: z.string().optional(),
|
|
|
+ workroomId: z.string().min(1, '工作室不能为空'),
|
|
|
+ status: z.enum(['completed', 'in_progress', 'paused'], {
|
|
|
+ message: '请选择状态',
|
|
|
+ }),
|
|
|
+ category: z.enum(
|
|
|
+ ['clinical', 'basic', 'epidemiological', 'observational', 'translational'],
|
|
|
+ { message: '请选择研究类型' },
|
|
|
+ ),
|
|
|
+ title: z.string().min(1, '请输入报告标题'),
|
|
|
+ leader: z.string().min(1, '请输入负责人'),
|
|
|
+ startDate: z.string().min(1, '请选择开始日期'),
|
|
|
+ endDate: z.string().optional(),
|
|
|
+ fundingSource: z.string().optional(),
|
|
|
+ projectNumber: z.string().optional(),
|
|
|
+ keywords: z.string().optional(),
|
|
|
+ abstract: z.string().min(1, '请输入研究摘要'),
|
|
|
+ objectives: z.string().optional(),
|
|
|
+ methods: z.string().optional(),
|
|
|
+ expectedResults: z.string().optional(),
|
|
|
+});
|
|
|
+
|
|
|
+// ---------------------------------------------------------------------------
|
|
|
+// 编解码
|
|
|
+// ---------------------------------------------------------------------------
|
|
|
+
|
|
|
+export function decodeResearchReport(dto: ResearchReportDTO): ResearchReportVO {
|
|
|
+ return {
|
|
|
+ ...decodeAuditRecord(dto),
|
|
|
+ id: dto.id?.toString(),
|
|
|
+ workroomId: dto.personalStudioId?.toString() ?? '',
|
|
|
+ status: dto.status ?? 'in_progress',
|
|
|
+ category: dto.category ?? 'clinical',
|
|
|
+ title: dto.title ?? '',
|
|
|
+ leader: dto.leader ?? '',
|
|
|
+ startDate: dto.startDate ?? '',
|
|
|
+ endDate: dto.endDate,
|
|
|
+ fundingSource: dto.fundingSource,
|
|
|
+ projectNumber: dto.projectNumber,
|
|
|
+ keywords: dto.keywords,
|
|
|
+ abstract: dto.abstract ?? '',
|
|
|
+ objectives: dto.objectives,
|
|
|
+ methods: dto.methods,
|
|
|
+ expectedResults: dto.expectedResults,
|
|
|
+ pdfUrl: dto.fileUrl,
|
|
|
+ viewCount: dto.viewCount ?? 0,
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+export function encodeResearchReportQuery(
|
|
|
+ query: Partial<ResearchReportQueryVO>,
|
|
|
+): ResearchReportQueryDTO {
|
|
|
+ return {
|
|
|
+ mixture: query.keyword,
|
|
|
+ personalStudioId: query.workroomId,
|
|
|
+ category: query.category,
|
|
|
+ status: query.status,
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+export function encodeResearchReport(
|
|
|
+ vo: ResearchReportSubmitVO,
|
|
|
+): ResearchReportDTO {
|
|
|
+ return {
|
|
|
+ id: vo.id,
|
|
|
+ personalStudioId: vo.workroomId,
|
|
|
+ status: vo.status,
|
|
|
+ category: vo.category,
|
|
|
+ title: vo.title,
|
|
|
+ leader: vo.leader,
|
|
|
+ startDate: vo.startDate,
|
|
|
+ endDate: vo.endDate,
|
|
|
+ fundingSource: vo.fundingSource,
|
|
|
+ projectNumber: vo.projectNumber,
|
|
|
+ keywords: vo.keywords,
|
|
|
+ abstract: vo.abstract,
|
|
|
+ objectives: vo.objectives,
|
|
|
+ methods: vo.methods,
|
|
|
+ expectedResults: vo.expectedResults,
|
|
|
+ fileUrl: vo.pdfUrl,
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+export function decodeResearchReportList(dto: ResearchReportDTO[]) {
|
|
|
+ return decodeList(dto, decodeResearchReport);
|
|
|
+}
|