diff --git a/clinical-web/src/api/management/documents.ts b/clinical-web/src/api/management/documents.ts index 3455613..d45ab60 100644 --- a/clinical-web/src/api/management/documents.ts +++ b/clinical-web/src/api/management/documents.ts @@ -114,6 +114,9 @@ function mapDocument(dto: TemplateResponseDto): DocumentRecord { // 现在取不到就是 undefined,卡片不渲染对应标签,不伪造取值 examType: dto.examType ?? undefined, projectCodes: dto.projectCodes ?? undefined, + // 需科室确认:后端没给就是 false(走原路直接归档), + // 不能默认成 true —— 那会给所有文书凭空多插一道人工节点 + needConfirm: dto.needConfirm ?? false, } } diff --git a/clinical-web/src/api/management/types.ts b/clinical-web/src/api/management/types.ts index b3dced2..3b0a376 100644 --- a/clinical-web/src/api/management/types.ts +++ b/clinical-web/src/api/management/types.ts @@ -49,6 +49,13 @@ export interface DocumentRecord { * 同样只在后端提供时才有值,缺省即不显示。 */ projectCodes?: string[] + /** + * 患者签署后是否需要「科室复核签章」节点(原型 V10.2 的 needConfirm)。 + * + * 检验 / 基因检测 / 自费类文书要科室再确认一次,避免漏签补签; + * 发起签署时它随文书带到任务上,决定签完落「待科室确认」还是「已签署」。 + */ + needConfirm?: boolean contentHtml?: string contentSha256?: string /** 当前已发布版本;发起签署必须使用它,不能误用最新草稿版本。 */ @@ -83,6 +90,8 @@ export interface TemplateResponseDto { */ examType?: string | null projectCodes?: string[] | null + /** 患者签署后是否需要科室复核签章;取不到即按 false(直接归档)。 */ + needConfirm?: boolean | null status?: BackendTemplateStatus | null currentVersionId?: string | null latestVersionId?: string | null @@ -331,7 +340,15 @@ export interface OrganizationResponseDto { } export type ReportBackendTaskStatus = - 'CREATED' | 'WAITING_SIGN' | 'SIGNED' | 'EXPIRED' | 'VOIDED' | 'GENERATING' | 'FAILED' + | 'CREATED' + | 'WAITING_SIGN' + | 'SIGNED' + | 'EXPIRED' + | 'VOIDED' + | 'GENERATING' + | 'FAILED' + /** 患者已签、等科室复核签章(与 workbench 的 WAITING_CONFIRM 同口径) */ + | 'WAITING_CONFIRM' /** 与 workbench 的 BackendSigningMethod 同口径:四种方式平铺,无通道归并 */ export type ReportBackendSignMethod = 'PAD' | 'SMS' | 'IPAD' | 'WX' diff --git a/clinical-web/src/api/workbench/records.ts b/clinical-web/src/api/workbench/records.ts index 82b66d7..b29dd55 100644 --- a/clinical-web/src/api/workbench/records.ts +++ b/clinical-web/src/api/workbench/records.ts @@ -122,6 +122,8 @@ interface MockRecordSeed { minutes?: number source?: string voidReason?: string + /** 患者签署后是否需要科室复核签章(原型 needConfirm) */ + needConfirm?: boolean } const MOCK_RECORD_SEEDS: MockRecordSeed[] = [ @@ -131,6 +133,8 @@ const MOCK_RECORD_SEEDS: MockRecordSeed[] = [ { patient: '周雅琴', sex: '女', age: 29, visitType: '门诊', doc: 'mock-doc-endoscopy', status: 'pending', method: 'sms', days: 0, at: '11:20' }, { patient: '赵某某', sex: '男', age: 52, visitType: '住院', doc: 'mock-doc-admission', status: 'pending', method: 'ipad', days: 0, at: '09:05', source: '工作台发起' }, { patient: '孙浩然', sex: '男', age: 54, visitType: '住院', doc: 'mock-doc-endoscopy', status: 'pending', method: 'wx', days: 0, at: '08:50' }, + // 「待科室确认」样本:患者已签完,等科室复核签章(原型 T0818006 那种) + { patient: '王秀兰', sex: '女', age: 63, visitType: '住院', doc: 'mock-doc-endoscopy', status: 'confirming', method: 'sms', days: 0, at: '10:40', minutes: 5, needConfirm: true }, // 昨天 { patient: '孙浩然', sex: '男', age: 54, visitType: '住院', doc: 'mock-doc-narcotic', status: 'signed', method: 'pad', days: 1, at: '08:40', minutes: 6, source: '工作台发起' }, { patient: '王建国', sex: '男', age: 71, visitType: '住院', doc: 'mock-doc-blood', status: 'signed', method: 'sms', days: 1, at: '14:15', minutes: 9 }, @@ -139,6 +143,7 @@ const MOCK_RECORD_SEEDS: MockRecordSeed[] = [ // 近 3 天 { patient: '吴桂芳', sex: '女', age: 73, visitType: '住院', doc: 'mock-doc-surgery', status: 'signed', method: 'pad', days: 2, at: '09:35', minutes: 5 }, { patient: '刘伟', sex: '男', age: 45, visitType: '门诊', doc: 'mock-doc-cardiac', status: 'signed', method: 'sms', days: 2, at: '15:48', minutes: 3, source: '工作台发起' }, + { patient: '张海涛', sex: '男', age: 47, visitType: '门诊', doc: 'mock-doc-blood', status: 'confirming', method: 'pad', days: 2, at: '16:20', minutes: 6, needConfirm: true }, { patient: '郑海燕', sex: '女', age: 38, visitType: '门诊', doc: 'mock-doc-endoscopy', status: 'expired', method: 'sms', days: 3, at: '10:22' }, { patient: '黄志明', sex: '男', age: 66, visitType: '住院', doc: 'mock-doc-mri', status: 'signed', method: 'pad', days: 3, at: '13:07', minutes: 8 }, // 近 7 天 @@ -187,7 +192,9 @@ function buildMockRecord(seed: MockRecordSeed, index: number): SigningRecord { const createdAt = formatDateTime(createdAtDate) const signedDate = new Date(createdAtDate) signedDate.setMinutes(signedDate.getMinutes() + (seed.minutes ?? 0)) - const signedAt = seed.status === 'signed' ? formatDateTime(signedDate) : undefined + // 待科室确认的记录同样已经签过了,签名时间与签名图都要有 + const hasSigned = seed.status === 'signed' || seed.status === 'confirming' + const signedAt = hasSigned ? formatDateTime(signedDate) : undefined // 已超时的记录按 24 小时失效,其余按 48 小时;失效时间只做展示,不参与筛选 const expireHours = seed.status === 'expired' ? 24 : 48 @@ -218,6 +225,7 @@ function buildMockRecord(seed: MockRecordSeed, index: number): SigningRecord { signerName: seed.patient, method: seed.method, status: seed.status, + needConfirm: seed.needConfirm ?? false, source: seed.source ?? '电子病历', createdAt, updatedAt: signedAt ?? createdAt, @@ -225,7 +233,7 @@ function buildMockRecord(seed: MockRecordSeed, index: number): SigningRecord { expiresAt: formatDateTime(expiresAtDate), voidReason: seed.voidReason, // 已签署的记录带上签名图,否则「列表说已签署、打开没有签名」自相矛盾 - signatureDataUrl: seed.status === 'signed' ? DEMO_SIGNATURE_DATA_URL : undefined, + signatureDataUrl: hasSigned ? DEMO_SIGNATURE_DATA_URL : undefined, documentContentHtml: getMockDocumentHtml(document.id), } } @@ -509,6 +517,27 @@ function buildMockEvents(record: SigningRecord): SigningTaskEvent[] { }) } + // 需科室确认的记录:签完先入科室确认队列,科室签章后才归档(与工作台留痕同一口径) + if (record.needConfirm && record.signedAt) { + events.push({ + id: `${record.id}-confirm-queued`, + time: record.signedAt, + text: `已推送至${record.department}确认队列,等待科室复核签章`, + eventType: 'CONFIRM_QUEUED', + channel: backendChannel, + }) + + if (record.status === 'signed') { + events.push({ + id: `${record.id}-confirmed`, + time: record.updatedAt, + text: '科室复核签章完成,文档归档至本次诊疗记录', + eventType: 'CONFIRMED', + channel: backendChannel, + }) + } + } + if (record.status === 'failed') { events.push({ id: `${record.id}-failed`, diff --git a/clinical-web/src/api/workbench/report-analytics.ts b/clinical-web/src/api/workbench/report-analytics.ts index 327bd0c..30cadd8 100644 --- a/clinical-web/src/api/workbench/report-analytics.ts +++ b/clinical-web/src/api/workbench/report-analytics.ts @@ -156,9 +156,10 @@ const DEMO_PATIENTS: DemoPatient[] = [ 演示任务生成 ========================================================================= */ -/** 状态分布:已签署占多数,其余各留一点,六种状态在总览统计块里都看得到 */ +/** 状态分布:已签署占多数,其余各留一点,七种状态在总览统计块里都看得到 */ const STATUS_WEIGHTS: { status: SigningTaskStatus; weight: number }[] = [ - { status: 'signed', weight: 68 }, + { status: 'signed', weight: 62 }, + { status: 'confirming', weight: 6 }, { status: 'pending', weight: 9 }, { status: 'signing', weight: 5 }, { status: 'expired', weight: 8 }, @@ -259,7 +260,9 @@ function buildTasks(): ReportTaskRow[] { createdAt.setDate(createdAt.getDate() - daysAgo) createdAt.setHours(intBetween(random, 8, 19), intBetween(random, 0, 59), 0, 0) - const isSigned = status === 'signed' + // 待科室确认的任务患者也已经签完了,签署时间与用时都要有 —— + // 否则「签署时效分析」会把它们当成"还没签",24 小时内完成率被算低 + const isSigned = status === 'signed' || status === 'confirming' const signMinutes = isSigned ? buildSignMinutes(random) : undefined const row: ReportTaskRow = { diff --git a/clinical-web/src/api/workbench/signing.ts b/clinical-web/src/api/workbench/signing.ts index f421d71..5cddf65 100644 --- a/clinical-web/src/api/workbench/signing.ts +++ b/clinical-web/src/api/workbench/signing.ts @@ -336,6 +336,35 @@ const defaultMockRecords: SigningTaskRecord[] = [ expiresAt: '2026-08-31 18:00', updatedAt: '2026-08-27 09:50', }, + { + // 「待科室确认」节点:患者已经签完,只差科室复核签章。 + // 四种状态徽标之外还必须有一条这个状态的样本,否则节点卡、复核签章动作、 + // 状态筛选在演示环境里一次都走不到。 + id: 'task-009', + templateVersionId: 'tpl-004-v1', + campus: '西院区', + patientId: 'P202610005', + patientName: '陈志强', + sex: '男', + age: 61, + visitNo: 'ZY20260826011', + visitType: '住院', + visitDate: '2026-08-26', + documentId: 'tpl-004', + documentName: '内镜检查知情同意书', + department: '普外科', + status: 'confirming', + signerName: '陈志强', + signerType: '本人', + // 需科室确认:检验 / 基因检测 / 自费类文书的典型场景,由模板标记带出 + needConfirm: true, + method: 'sms', + smsMobileMasked: '135****9012', + source: '电子病历', + expiresAt: '2026-08-31 12:00', + signedAt: '2026-08-27 10:05', + updatedAt: '2026-08-27 10:05', + }, ] /** @@ -364,7 +393,9 @@ function hydrateTaskDocuments(records: SigningTaskRecord[]): SigningTaskRecord[] return records.map((record, index) => { let next = record - if (next.status === 'signed' && !next.signatureDataUrl) { + // 待科室确认的任务患者已经签过了,同样要有签名图 —— + // 否则详情里文书签名位是空的,看着像"患者没签却进了科室确认队列" + if ((next.status === 'signed' || next.status === 'confirming') && !next.signatureDataUrl) { next = { ...next, signatureDataUrl: DEMO_SIGNATURE_DATA_URL } } @@ -573,6 +604,8 @@ function normalizeTaskStatus(value: BackendSigningTaskStatus): SigningTaskStatus switch (value) { case 'SIGNED': return 'signed' + case 'WAITING_CONFIRM': + return 'confirming' case 'EXPIRED': return 'expired' case 'VOIDED': @@ -631,6 +664,8 @@ function mapAvailableTemplate(dto: AvailableTemplateVersionResponseDto): Signing description: `已发布版本 ${dto.versionNo}`, // 可用模板接口不返回方式 ACL;四种方式都放行,实际允许范围由后端在创建时校验。 supportedMethods: [...ALL_SIGNING_METHODS], + // 需科室确认随模板带出;后端没给就是 false(签完直接归档) + needConfirm: dto.needConfirm ?? false, // 接口不返回正文,只能带出哈希;正文由 hydrateTemplateContents 按需补回 contentSha256: dto.contentSha256, contentHtml: cachedContent || undefined, @@ -726,6 +761,8 @@ function mapSigningTask( // 签署人类型 / 关系:后端补齐后自动带出,取不到即 undefined,列表不显示代签标签 signerType: dto.signerType ?? undefined, signerRelation: dto.signerRelation ?? undefined, + // 需科室确认:后端没给就是 false,不凭空给任务多插一道人工节点 + needConfirm: dto.needConfirm ?? false, method: normalizeSigningMethod(dto.signMethod), smsMobileMasked: dto.smsMobileMasked ?? undefined, status: normalizeTaskStatus(dto.status), @@ -780,6 +817,7 @@ function createTemplateFromInput(input: CreateSigningTaskInput): SigningTemplate category: '—', description: '', supportedMethods: [...ALL_SIGNING_METHODS], + needConfirm: input.needConfirm ?? false, } } @@ -797,6 +835,7 @@ function toBackendTaskQuery(query: SigningTaskQuery, options: SigningApiOptions const statusMap: Partial> = { pending: 'WAITING_SIGN', signing: 'GENERATING', + confirming: 'WAITING_CONFIRM', signed: 'SIGNED', expired: 'EXPIRED', void: 'VOIDED', @@ -1187,6 +1226,8 @@ export async function createSigningTask(input: CreateSigningTaskInput): Promise< signerName: patient?.name ?? '演示患者', // 原型新建任务固定落「本人」(发起弹窗没有代签人选择,代签是签署现场才知道的事) signerType: '本人', + // 需科室确认由文书模板标记带出(原型 ntDocNeedConfirm),不是弹窗里选的 + needConfirm: input.needConfirm ?? false, method: input.method, status: 'pending', source: '工作台发起', @@ -1238,7 +1279,9 @@ export function completeSigningTask( return Promise.resolve(null) } - task.status = 'signed' + // 患者签完之后落哪个状态由文书的「需科室确认」标记决定: + // 需要科室复核的先停在 confirming,等科室签章才算归档(原型 padTask.status 同款写法) + task.status = task.needConfirm ? 'confirming' : 'signed' task.signatureDataUrl = signatureDataUrl task.signedAt = formatMockDate() task.updatedAt = task.signedAt @@ -1250,6 +1293,34 @@ export function completeSigningTask( return Promise.resolve(cloneTask(task)) } +/** + * 科室复核签章(原型 confirmDept)。 + * + * 「待科室确认」是个**节点**不是终态:科室确认完才转「已签署」、文档才归档。 + * 真实模式暂未接入(后端要重做),按项目既有口径如实提示"待接入", + * 不前端自己把状态改成已签署 —— 那是要留痕的正式动作。 + */ +export async function confirmDepartmentSigning(id: string): Promise { + if (!useMockData) { + throw new Error('当前后端未提供科室复核签章接口') + } + + const task = mockRecords.find((record) => record.id === id) + + if (!task) { + return Promise.resolve(null) + } + + if (task.status !== 'confirming') { + throw new Error('只有「待科室确认」的任务需要科室复核签章') + } + + task.status = 'signed' + task.updatedAt = formatMockDate() + persistMockRecords() + return Promise.resolve(cloneTask(task)) +} + export async function reopenSigningTask( id: string, options: SigningTaskActionOptions = {}, @@ -1371,6 +1442,25 @@ export async function getSigningTaskEvents(id: string): Promise = { pending: '待签署', signing: '签署中', + confirming: '待科室确认', signed: '已签署', expired: '已超时', void: '已作废', diff --git a/clinical-web/src/api/workbench/types.ts b/clinical-web/src/api/workbench/types.ts index 39b5bf2..3acdc27 100644 --- a/clinical-web/src/api/workbench/types.ts +++ b/clinical-web/src/api/workbench/types.ts @@ -18,7 +18,22 @@ export interface WorkbenchOverviewRequestDto { rankingLimit?: number } -export type SigningTaskStatus = 'pending' | 'signing' | 'signed' | 'expired' | 'void' | 'failed' +/** + * 签署任务状态(原型 V10.2 的六种 + 「待科室确认」)。 + * + * `confirming` 是**节点状态**,不是失败态:检验 / 基因检测 / 自费等场景要求 + * 患者签完之后由科室再复核签章(原型 `needConfirm`),此时文书已经签好、只差科室那一道章。 + * 它与 `signed` 的区别是「能不能算最终归档」——`signed` 才能下载归档原件, + * `confirming` 只能下载患者已签版本。 + */ +export type SigningTaskStatus = + | 'pending' + | 'signing' + | 'confirming' + | 'signed' + | 'expired' + | 'void' + | 'failed' export type PatientSex = '男' | '女' | '未知' @@ -67,6 +82,11 @@ export interface SigningTemplate { category: string description: string supportedMethods: SigningMethod[] + /** + * 患者签署后是否需要科室复核签章(原型 needConfirm)。 + * 发起签署时随文书带到任务上,决定签完落「待科室确认」还是「已签署」。 + */ + needConfirm?: boolean /** 原型:文书定稿 HTML 随模板携带,发起签署时进入任务快照。 */ contentHtml?: string /** @@ -258,6 +278,13 @@ export interface SigningTaskRecord extends WorkbenchTask { */ signerType?: string | null signerRelation?: string | null + /** + * 是否需要在患者签署后增加「科室复核签章」节点(原型 needConfirm,由文书模板标记带出)。 + * + * 为 true 时患者签完落 `confirming` 而不是 `signed`,要等科室确认完才算归档。 + * 取不到即按 false 处理(走原路直接归档),不凭空多插一道人工节点。 + */ + needConfirm?: boolean method: SigningMethod /** * 短信发送到的号码,后端只返回脱敏值(如 `138****5678`)。 @@ -337,6 +364,8 @@ export interface CreateSigningTaskInput { department: string /** 页面快照字段:仅 Mock 模式进入任务记录;真实接口不提交(后端按 templateVersionId 取版本快照)。 */ documentContentHtml?: string + /** 由文书模板带出的「需科室确认」标记;真实接口不提交(后端按 templateVersionId 取)。 */ + needConfirm?: boolean } /** MEDISIGN 创建签署任务请求 DTO。不要向后端提交页面快照字段。 */ @@ -362,7 +391,15 @@ export interface SigningTaskActionOptions extends SigningApiOptions { } export type BackendSigningTaskStatus = - 'CREATED' | 'WAITING_SIGN' | 'SIGNED' | 'EXPIRED' | 'VOIDED' | 'GENERATING' | 'FAILED' + | 'CREATED' + | 'WAITING_SIGN' + | 'SIGNED' + | 'EXPIRED' + | 'VOIDED' + | 'GENERATING' + | 'FAILED' + /** 患者已签、等科室复核签章(对应前端 confirming)。后端重做时按这个取值对齐。 */ + | 'WAITING_CONFIRM' /** * 后端签署方式,与前端 SigningMethod 一一对应(大小写之差)。 @@ -421,6 +458,8 @@ export interface AvailableTemplateVersionResponseDto { versionNo: string contentSha256: string effectiveAt: string + /** 患者签署后是否需要科室复核签章;后端补齐后前端无需改动即可带出。 */ + needConfirm?: boolean | null } export interface SignTaskPatientSnapshotDto { @@ -474,6 +513,8 @@ export interface SignTaskResponseDto { */ signerType?: string | null signerRelation?: string | null + /** 患者签署后是否需要科室复核签章(由文书模板标记带出)。 */ + needConfirm?: boolean | null signedAt: string | null expiredAt: string | null voidedAt: string | null diff --git a/clinical-web/src/composables/useSigningTaskForm.ts b/clinical-web/src/composables/useSigningTaskForm.ts index c3d92d4..1697f7f 100644 --- a/clinical-web/src/composables/useSigningTaskForm.ts +++ b/clinical-web/src/composables/useSigningTaskForm.ts @@ -565,6 +565,8 @@ export function useSigningTaskForm(options: UseSigningTaskFormOptions) { visitNo: selectedVisit.value.visitNo, department: selectedVisit.value.department, documentContentHtml: selectedTemplate.value.contentHtml, + // 需科室确认由文书模板标记带出(原型 V6.9 起弹窗里不再让人选) + needConfirm: selectedTemplate.value.needConfirm ?? false, }) if (!isSigningMockEnabled && method.value === 'sms') { diff --git a/clinical-web/src/utils/mock-document-library.ts b/clinical-web/src/utils/mock-document-library.ts index 3df9021..4199b5d 100644 --- a/clinical-web/src/utils/mock-document-library.ts +++ b/clinical-web/src/utils/mock-document-library.ts @@ -58,6 +58,8 @@ export interface LocalDocument { */ examType?: string | null projectCodes?: string[] + /** 患者签署后是否需要科室复核签章(原型 needConfirm),发起签署时随文书带到任务上 */ + needConfirm?: boolean /** * 演示数据的更新时间。 * 演示文库的种子不带时间,页面在这一层会显示成"—"而不是伪造一个时间戳; @@ -323,6 +325,7 @@ export function readLocalSigningTemplates(): SigningTemplate[] { category: category.name, description: document.description ?? '', supportedMethods: [...ALL_SIGNING_METHODS], + needConfirm: document.needConfirm ?? false, contentHtml: document.contentHtml, contentSha256: document.contentSha256, })), diff --git a/clinical-web/src/views/management/documents/index.vue b/clinical-web/src/views/management/documents/index.vue index faa591e..994ada3 100644 --- a/clinical-web/src/views/management/documents/index.vue +++ b/clinical-web/src/views/management/documents/index.vue @@ -998,6 +998,7 @@ async function toSigningTemplate(template: DocumentTemplate): Promise = { signed: '已签署', + confirming: '待科室确认', pending: '待签署', signing: '签署中', expired: '已超时', diff --git a/clinical-web/src/views/management/reports/index.vue b/clinical-web/src/views/management/reports/index.vue index 5649fe1..9f37073 100644 --- a/clinical-web/src/views/management/reports/index.vue +++ b/clinical-web/src/views/management/reports/index.vue @@ -814,6 +814,7 @@ function mapReportTaskStatus(status: ReportTaskResponseDto['status']): ReportTas CREATED: 'pending', WAITING_SIGN: 'pending', SIGNED: 'signed', + WAITING_CONFIRM: 'confirming', EXPIRED: 'expired', VOIDED: 'void', GENERATING: 'signing', @@ -1018,6 +1019,7 @@ function handleView(row: ReportTask) { function getReportStatusLabel(status: ReportTaskStatus) { const labels: Record = { signed: '已签署', + confirming: '待科室确认', pending: '待签署', signing: '签署中', expired: '已超时', diff --git a/clinical-web/src/views/management/reports/mock.ts b/clinical-web/src/views/management/reports/mock.ts index 1a73ee8..90c3cd3 100644 --- a/clinical-web/src/views/management/reports/mock.ts +++ b/clinical-web/src/views/management/reports/mock.ts @@ -60,6 +60,23 @@ export const reportTasks: ReportTask[] = [ expiredAt: daysAgo(-1, 9, 12), durationMinutes: 7, }, + { + id: 'T0818009', + patientName: '王秀兰', + patientId: 'P202610007', + documentName: '内镜检查知情同意书', + department: '消化内科', + campus: '本部院区', + category: 'consent', + method: 'sms', + // 「待科室确认」样本:患者已签完,等科室复核签章 —— + // 没有这条样本,报表里的这一栏永远是 0,看不出它到底有没有接上 + status: 'confirming', + createdAt: daysAgo(0, 10, 40), + signedAt: daysAgo(0, 10, 45), + expiredAt: daysAgo(-1, 10, 40), + durationMinutes: 5, + }, { id: 'T0818002', patientName: '李淑芬', diff --git a/clinical-web/src/views/management/reports/types.ts b/clinical-web/src/views/management/reports/types.ts index 0e7c6f6..b8e7260 100644 --- a/clinical-web/src/views/management/reports/types.ts +++ b/clinical-web/src/views/management/reports/types.ts @@ -30,7 +30,14 @@ export interface ReportMetricCard { tone: ReportMetricTone } -export type ReportTaskStatus = 'signed' | 'pending' | 'signing' | 'expired' | 'void' | 'failed' +export type ReportTaskStatus = + | 'signed' + | 'confirming' + | 'pending' + | 'signing' + | 'expired' + | 'void' + | 'failed' export type ReportSigningMethod = SigningMethod diff --git a/clinical-web/src/views/reports/aggregate.ts b/clinical-web/src/views/reports/aggregate.ts index 7190d73..d63d6ab 100644 --- a/clinical-web/src/views/reports/aggregate.ts +++ b/clinical-web/src/views/reports/aggregate.ts @@ -49,12 +49,16 @@ export const METHOD_LABELS: Record = Object.fromEntries( ) as Record /** - * 状态文案。六种状态与后端枚举一一对应,不映射成原型里的「待科室确认」—— - * 那个状态在后端枚举里不存在,硬映射会让报表与签署记录页对同一个任务给出不同说法。 + * 状态文案。七种状态与后端枚举一一对应。 + * + * `confirming`(待科室确认)是**节点状态**:患者已经签完、只差科室复核签章。 + * 它在报表里单列一栏而不是并进「已签署」—— 并进去会让「已签署」虚高, + * 而科室复核本来就是这类文书的关键卡点,混掉就看不见卡在哪了。 */ export const STATUS_LABELS: Record = { pending: '待签署', signing: '签署中', + confirming: '待科室确认', signed: '已签署', expired: '已超时', void: '已作废', @@ -64,6 +68,7 @@ export const STATUS_LABELS: Record = { /** 汇总矩阵与统计块共用的列顺序:先「好」后「坏」,读起来符合预期 */ export const STATUS_COLUMNS: SigningTaskStatus[] = [ 'signed', + 'confirming', 'pending', 'signing', 'expired', @@ -106,9 +111,18 @@ export function axisLabelOf(dateKey: string): string { return `${Number(month)}/${Number(day)}` } -/** 任务状态 → 六类计数里的字段名:状态枚举与计数字段一一对应,直接索引即可 */ +/** 任务状态 → 计数里的字段名:状态枚举与计数字段一一对应,直接索引即可 */ export function emptyCounts(): ReportStatusCounts { - return { total: 0, signed: 0, pending: 0, signing: 0, expired: 0, void: 0, failed: 0 } + return { + total: 0, + signed: 0, + confirming: 0, + pending: 0, + signing: 0, + expired: 0, + void: 0, + failed: 0, + } } export function addToCounts(counts: ReportStatusCounts, status: ReportTaskRow['status']): void { diff --git a/clinical-web/src/views/reports/components/ReportSummaryMatrix.vue b/clinical-web/src/views/reports/components/ReportSummaryMatrix.vue index fd10b37..814a9ba 100644 --- a/clinical-web/src/views/reports/components/ReportSummaryMatrix.vue +++ b/clinical-web/src/views/reports/components/ReportSummaryMatrix.vue @@ -40,13 +40,23 @@ const totals = computed(() => (sum, row) => ({ total: sum.total + row.total, signed: sum.signed + row.signed, + confirming: sum.confirming + row.confirming, pending: sum.pending + row.pending, signing: sum.signing + row.signing, expired: sum.expired + row.expired, void: sum.void + row.void, failed: sum.failed + row.failed, }), - { total: 0, signed: 0, pending: 0, signing: 0, expired: 0, void: 0, failed: 0 }, + { + total: 0, + signed: 0, + confirming: 0, + pending: 0, + signing: 0, + expired: 0, + void: 0, + failed: 0, + }, ), ) diff --git a/clinical-web/src/views/reports/overview/index.vue b/clinical-web/src/views/reports/overview/index.vue index a3caf71..b3b7d20 100644 --- a/clinical-web/src/views/reports/overview/index.vue +++ b/clinical-web/src/views/reports/overview/index.vue @@ -39,6 +39,9 @@ const tiles = computed(() => [ { label: '累计发起任务', value: counts.value.total, color: '#0e6e8c' }, { label: '已签署', value: counts.value.signed, color: '#0f9d6c' }, { label: '待签署', value: counts.value.pending, color: '#3a7bd5' }, + // 待科室确认:患者已签、等科室复核签章。取色与任务徽标(原型 .tg-cfm)一致, + // 不沿用原型的 #7a4dbb —— 那个色在本仓库已经给了「签署中」,同色两块会看串 + { label: '待科室确认', value: counts.value.confirming, color: '#c07f00' }, { label: '签署中', value: counts.value.signing, color: '#7a4dbb' }, { label: '已超时', value: counts.value.expired, color: '#d9821f' }, { label: '已作废', value: counts.value.void, color: '#d0534f' }, diff --git a/clinical-web/src/views/reports/types.ts b/clinical-web/src/views/reports/types.ts index da2e319..d989188 100644 --- a/clinical-web/src/views/reports/types.ts +++ b/clinical-web/src/views/reports/types.ts @@ -41,6 +41,8 @@ export interface ReportFilterOption { export interface ReportStatusCounts { total: number signed: number + /** 待科室确认:患者已签、等科室复核签章,不能并进 signed */ + confirming: number pending: number signing: number expired: number diff --git a/clinical-web/src/views/workbench/records/components/SigningRecordDetailDialog.vue b/clinical-web/src/views/workbench/records/components/SigningRecordDetailDialog.vue index 9287208..6da4f83 100644 --- a/clinical-web/src/views/workbench/records/components/SigningRecordDetailDialog.vue +++ b/clinical-web/src/views/workbench/records/components/SigningRecordDetailDialog.vue @@ -28,6 +28,7 @@ const emit = defineEmits<{ const statusLabels: Record = { pending: '待签署', signing: '签署中', + confirming: '待科室确认', signed: '已签署', expired: '已超时', void: '已作废', diff --git a/clinical-web/src/views/workbench/records/components/SigningRecordTable.vue b/clinical-web/src/views/workbench/records/components/SigningRecordTable.vue index c8477b0..f877866 100644 --- a/clinical-web/src/views/workbench/records/components/SigningRecordTable.vue +++ b/clinical-web/src/views/workbench/records/components/SigningRecordTable.vue @@ -30,6 +30,7 @@ const pageCount = computed(() => Math.max(Math.ceil(props.total / props.pageSize const statusLabels: Record = { pending: '待签署', signing: '签署中', + confirming: '待科室确认', signed: '已签署', expired: '已超时', void: '已作废', diff --git a/clinical-web/src/views/workbench/records/index.vue b/clinical-web/src/views/workbench/records/index.vue index 95411eb..886fa64 100644 --- a/clinical-web/src/views/workbench/records/index.vue +++ b/clinical-web/src/views/workbench/records/index.vue @@ -44,6 +44,7 @@ const DEFAULT_PAGE_SIZE = 20 const statusLabels: Record = { pending: '待签署', signing: '签署中', + confirming: '待科室确认', signed: '已签署', expired: '已超时', void: '已作废', @@ -128,9 +129,9 @@ const campusOptions = computed[] = [ { label: '全部状态', value: 'all' }, - ...(['pending', 'signing', 'signed', 'expired', 'void', 'failed'] as SigningTaskStatus[]).map( - (status) => ({ label: statusLabels[status], value: status }), - ), + ...( + ['pending', 'signing', 'confirming', 'signed', 'expired', 'void', 'failed'] as SigningTaskStatus[] + ).map((status) => ({ label: statusLabels[status], value: status })), ] /** 原型 #qMethod 只有 3 项(手写板 / 短信线上 / Pad),没有公众号 */ diff --git a/clinical-web/src/views/workbench/signing/components/SigningTaskDetail.vue b/clinical-web/src/views/workbench/signing/components/SigningTaskDetail.vue index 8e4f3f6..85cd6e4 100644 --- a/clinical-web/src/views/workbench/signing/components/SigningTaskDetail.vue +++ b/clinical-web/src/views/workbench/signing/components/SigningTaskDetail.vue @@ -1,5 +1,5 @@