feat(home): implement workbench dashboard
This commit is contained in:
@@ -1,42 +1,263 @@
|
||||
import { request } from '@/utils/request'
|
||||
|
||||
import type { WorkbenchOverviewResponse } from './types'
|
||||
import type {
|
||||
HomeRankingPeriod,
|
||||
WorkbenchCampus,
|
||||
WorkbenchDocumentRanking,
|
||||
WorkbenchOverviewQuery,
|
||||
WorkbenchOverviewResponse,
|
||||
WorkbenchTodoTask,
|
||||
WorkbenchTrendPoint,
|
||||
} from './types'
|
||||
|
||||
const useMockData = import.meta.env.VITE_USE_MOCK !== 'false'
|
||||
|
||||
const mockOverview: WorkbenchOverviewResponse = {
|
||||
summary: {
|
||||
pendingTasks: 18,
|
||||
todaySigned: 36,
|
||||
abnormalTasks: 2,
|
||||
documentCount: 128,
|
||||
},
|
||||
recentTasks: [
|
||||
{
|
||||
id: 'task-001',
|
||||
patientName: '李某某',
|
||||
visitNo: 'ZY20260827001',
|
||||
documentName: '住院患者知情同意书',
|
||||
department: '消化内科',
|
||||
status: 'pending',
|
||||
updatedAt: '2026-08-27 09:30',
|
||||
},
|
||||
{
|
||||
id: 'task-002',
|
||||
patientName: '王某某',
|
||||
visitNo: 'JZ20260827018',
|
||||
documentName: '急诊留观知情同意书',
|
||||
department: '儿科急诊',
|
||||
status: 'signed',
|
||||
updatedAt: '2026-08-27 09:12',
|
||||
},
|
||||
],
|
||||
interface MockCampusOverview {
|
||||
summary: WorkbenchOverviewResponse['summary']
|
||||
trend: WorkbenchTrendPoint[]
|
||||
todos: WorkbenchTodoTask[]
|
||||
ranking: Array<Omit<WorkbenchDocumentRanking, 'signedCount'> & { baseCount: number }>
|
||||
}
|
||||
|
||||
export function getWorkbenchOverview(): Promise<WorkbenchOverviewResponse> {
|
||||
const mockCampusOverviews: Record<WorkbenchCampus, MockCampusOverview> = {
|
||||
本部院区: {
|
||||
summary: {
|
||||
todaySigned: 36,
|
||||
pendingPatientSigning: 18,
|
||||
todayOverdue: 2,
|
||||
availableTemplates: 128,
|
||||
coveredDepartments: 9,
|
||||
},
|
||||
trend: [
|
||||
{ label: '8/21', value: 24 },
|
||||
{ label: '8/22', value: 31 },
|
||||
{ label: '8/23', value: 28 },
|
||||
{ label: '8/24', value: 35 },
|
||||
{ label: '8/25', value: 32 },
|
||||
{ label: '8/26', value: 39 },
|
||||
{ label: '8/27', value: 36 },
|
||||
],
|
||||
todos: [
|
||||
{
|
||||
id: 'T0818003',
|
||||
patientId: 'P202610005',
|
||||
patientName: '陈志强',
|
||||
documentName: '住院须知及告知书',
|
||||
department: '消化内科',
|
||||
status: 'pending',
|
||||
method: 'pad',
|
||||
updatedAt: '2026-08-27 11:20',
|
||||
},
|
||||
{
|
||||
id: 'T0818002',
|
||||
patientId: 'P202610002',
|
||||
patientName: '李淑芬',
|
||||
documentName: 'MRI 磁共振检查知情同意书',
|
||||
department: '放射科',
|
||||
status: 'pending',
|
||||
method: 'sms',
|
||||
updatedAt: '2026-08-27 10:05',
|
||||
},
|
||||
{
|
||||
id: 'T0815001',
|
||||
patientId: 'P202610001',
|
||||
patientName: '王建国',
|
||||
documentName: '心包穿刺知情同意书',
|
||||
department: '心内科',
|
||||
status: 'overdue',
|
||||
method: 'sms',
|
||||
updatedAt: '2026-08-27 09:40',
|
||||
},
|
||||
],
|
||||
ranking: [
|
||||
{ id: 'doc-001', documentName: '住院患者知情同意书', department: '全院通用', baseCount: 58 },
|
||||
{ id: 'doc-002', documentName: '入院须知及告知书', department: '全院通用', baseCount: 51 },
|
||||
{
|
||||
id: 'doc-003',
|
||||
documentName: 'MRI 磁共振检查知情同意书',
|
||||
department: '放射科',
|
||||
baseCount: 43,
|
||||
},
|
||||
{ id: 'doc-004', documentName: '手术知情同意书', department: '普外科', baseCount: 39 },
|
||||
{ id: 'doc-005', documentName: '麻醉知情同意书', department: '麻醉科', baseCount: 35 },
|
||||
{ id: 'doc-006', documentName: '患者授权委托书', department: '医务处', baseCount: 31 },
|
||||
{ id: 'doc-007', documentName: '输血治疗知情同意书', department: '血液科', baseCount: 27 },
|
||||
{ id: 'doc-008', documentName: '内镜检查知情同意书', department: '消化内科', baseCount: 24 },
|
||||
{ id: 'doc-009', documentName: '介入治疗知情同意书', department: '心内科', baseCount: 21 },
|
||||
{ id: 'doc-010', documentName: '特殊检查风险告知书', department: '影像科', baseCount: 18 },
|
||||
],
|
||||
},
|
||||
东院区: {
|
||||
summary: {
|
||||
todaySigned: 22,
|
||||
pendingPatientSigning: 11,
|
||||
todayOverdue: 1,
|
||||
availableTemplates: 76,
|
||||
coveredDepartments: 6,
|
||||
},
|
||||
trend: [
|
||||
{ label: '8/21', value: 15 },
|
||||
{ label: '8/22', value: 18 },
|
||||
{ label: '8/23', value: 17 },
|
||||
{ label: '8/24', value: 23 },
|
||||
{ label: '8/25', value: 20 },
|
||||
{ label: '8/26', value: 25 },
|
||||
{ label: '8/27', value: 22 },
|
||||
],
|
||||
todos: [
|
||||
{
|
||||
id: 'T0818001',
|
||||
patientId: 'P202610004',
|
||||
patientName: '赵美英',
|
||||
documentName: '体检报告发放及知情同意书',
|
||||
department: '健康管理中心',
|
||||
status: 'pending',
|
||||
method: 'pad',
|
||||
updatedAt: '2026-08-27 09:12',
|
||||
},
|
||||
{
|
||||
id: 'T0818011',
|
||||
patientId: 'P202610011',
|
||||
patientName: '周雅琴',
|
||||
documentName: '检查前注意事项告知书',
|
||||
department: '体检中心',
|
||||
status: 'overdue',
|
||||
method: 'sms',
|
||||
updatedAt: '2026-08-27 08:30',
|
||||
},
|
||||
],
|
||||
ranking: [
|
||||
{
|
||||
id: 'doc-011',
|
||||
documentName: '体检报告发放及知情同意书',
|
||||
department: '健康管理中心',
|
||||
baseCount: 46,
|
||||
},
|
||||
{ id: 'doc-012', documentName: '检前告知书', department: '健康管理中心', baseCount: 39 },
|
||||
{
|
||||
id: 'doc-013',
|
||||
documentName: '超声检查知情同意书',
|
||||
department: '超声医学科',
|
||||
baseCount: 32,
|
||||
},
|
||||
{
|
||||
id: 'doc-014',
|
||||
documentName: '胃肠镜检查知情同意书',
|
||||
department: '消化内科',
|
||||
baseCount: 28,
|
||||
},
|
||||
{ id: 'doc-015', documentName: '采血及检验告知书', department: '检验科', baseCount: 25 },
|
||||
{ id: 'doc-016', documentName: '影像检查告知书', department: '影像科', baseCount: 21 },
|
||||
{ id: 'doc-017', documentName: '健康体检知情同意书', department: '体检中心', baseCount: 19 },
|
||||
{ id: 'doc-018', documentName: '隐私保护告知书', department: '健康管理中心', baseCount: 16 },
|
||||
{ id: 'doc-019', documentName: '麻醉检查知情同意书', department: '麻醉科', baseCount: 14 },
|
||||
{ id: 'doc-020', documentName: '报告领取授权书', department: '健康管理中心', baseCount: 12 },
|
||||
],
|
||||
},
|
||||
西院区: {
|
||||
summary: {
|
||||
todaySigned: 17,
|
||||
pendingPatientSigning: 9,
|
||||
todayOverdue: 2,
|
||||
availableTemplates: 54,
|
||||
coveredDepartments: 5,
|
||||
},
|
||||
trend: [
|
||||
{ label: '8/21', value: 11 },
|
||||
{ label: '8/22', value: 14 },
|
||||
{ label: '8/23', value: 13 },
|
||||
{ label: '8/24', value: 16 },
|
||||
{ label: '8/25', value: 15 },
|
||||
{ label: '8/26', value: 19 },
|
||||
{ label: '8/27', value: 17 },
|
||||
],
|
||||
todos: [
|
||||
{
|
||||
id: 'T0818021',
|
||||
patientId: 'P202610021',
|
||||
patientName: '孙浩然',
|
||||
documentName: '心脏介入手术知情同意书',
|
||||
department: '心内科',
|
||||
status: 'pending',
|
||||
method: 'pad',
|
||||
updatedAt: '2026-08-27 10:15',
|
||||
},
|
||||
{
|
||||
id: 'T0818022',
|
||||
patientId: 'P202610022',
|
||||
patientName: '刘晓琴',
|
||||
documentName: '骨科手术知情同意书',
|
||||
department: '骨科',
|
||||
status: 'pending',
|
||||
method: 'sms',
|
||||
updatedAt: '2026-08-27 09:52',
|
||||
},
|
||||
{
|
||||
id: 'T0818023',
|
||||
patientId: 'P202610023',
|
||||
patientName: '何文博',
|
||||
documentName: '术前麻醉访视告知书',
|
||||
department: '麻醉科',
|
||||
status: 'overdue',
|
||||
method: 'pad',
|
||||
updatedAt: '2026-08-27 08:05',
|
||||
},
|
||||
],
|
||||
ranking: [
|
||||
{
|
||||
id: 'doc-021',
|
||||
documentName: '心脏介入手术知情同意书',
|
||||
department: '心内科',
|
||||
baseCount: 37,
|
||||
},
|
||||
{ id: 'doc-022', documentName: '骨科手术知情同意书', department: '骨科', baseCount: 33 },
|
||||
{ id: 'doc-023', documentName: '术前麻醉访视告知书', department: '麻醉科', baseCount: 28 },
|
||||
{ id: 'doc-024', documentName: '住院患者知情同意书', department: '全院通用', baseCount: 25 },
|
||||
{ id: 'doc-025', documentName: '输血治疗知情同意书', department: '血液科', baseCount: 22 },
|
||||
{ id: 'doc-026', documentName: '介入治疗知情同意书', department: '心内科', baseCount: 20 },
|
||||
{ id: 'doc-027', documentName: '术后康复告知书', department: '骨科', baseCount: 18 },
|
||||
{ id: 'doc-028', documentName: '特殊检查风险告知书', department: '影像科', baseCount: 16 },
|
||||
{ id: 'doc-029', documentName: '患者授权委托书', department: '医务处', baseCount: 14 },
|
||||
{ id: 'doc-030', documentName: '手术部位标识确认书', department: '手术部', baseCount: 12 },
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
function getPeriodFactor(period: HomeRankingPeriod) {
|
||||
return period === 7 ? 1 : period === 14 ? 2 : 3.5
|
||||
}
|
||||
|
||||
function createRanking(
|
||||
rows: MockCampusOverview['ranking'],
|
||||
period: HomeRankingPeriod,
|
||||
): WorkbenchDocumentRanking[] {
|
||||
const factor = getPeriodFactor(period)
|
||||
|
||||
return rows
|
||||
.map((row, index) => ({
|
||||
id: row.id,
|
||||
documentName: row.documentName,
|
||||
department: row.department,
|
||||
signedCount: Math.round(row.baseCount * factor + (index % 3)),
|
||||
}))
|
||||
.sort((left, right) => right.signedCount - left.signedCount)
|
||||
}
|
||||
|
||||
export function getWorkbenchOverview(
|
||||
query: WorkbenchOverviewQuery,
|
||||
): Promise<WorkbenchOverviewResponse> {
|
||||
if (useMockData) {
|
||||
return Promise.resolve(mockOverview)
|
||||
const overview = mockCampusOverviews[query.campus] ?? mockCampusOverviews['本部院区']
|
||||
|
||||
return Promise.resolve({
|
||||
summary: { ...overview.summary },
|
||||
trend: overview.trend.map((point) => ({ ...point })),
|
||||
todos: overview.todos.map((task) => ({ ...task })),
|
||||
documentRanking: createRanking(overview.ranking, query.rankingPeriod),
|
||||
})
|
||||
}
|
||||
|
||||
return request.get<WorkbenchOverviewResponse>('/workbench/overview')
|
||||
return request.get<WorkbenchOverviewResponse>('/workbench/overview', {
|
||||
params: query,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,12 +1,22 @@
|
||||
import type { PageQuery, PageResult } from '@/types/common'
|
||||
|
||||
export type WorkbenchCampus = '本部院区' | '东院区' | '西院区'
|
||||
|
||||
export type HomeRankingPeriod = 7 | 14 | 30
|
||||
|
||||
export interface WorkbenchOverviewQuery {
|
||||
campus: WorkbenchCampus
|
||||
rankingPeriod: HomeRankingPeriod
|
||||
}
|
||||
|
||||
export type SigningTaskStatus = 'pending' | 'signing' | 'signed' | 'rejected' | 'expired'
|
||||
|
||||
export interface WorkbenchSummary {
|
||||
pendingTasks: number
|
||||
todaySigned: number
|
||||
abnormalTasks: number
|
||||
documentCount: number
|
||||
pendingPatientSigning: number
|
||||
todayOverdue: number
|
||||
availableTemplates: number
|
||||
coveredDepartments: number
|
||||
}
|
||||
|
||||
export interface WorkbenchTask {
|
||||
@@ -19,9 +29,38 @@ export interface WorkbenchTask {
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export type WorkbenchTodoStatus = 'pending' | 'overdue'
|
||||
|
||||
export type SigningMethod = 'pad' | 'sms'
|
||||
|
||||
export interface WorkbenchTodoTask {
|
||||
id: string
|
||||
patientId: string
|
||||
patientName: string
|
||||
documentName: string
|
||||
department: string
|
||||
status: WorkbenchTodoStatus
|
||||
method: SigningMethod
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface WorkbenchTrendPoint {
|
||||
label: string
|
||||
value: number
|
||||
}
|
||||
|
||||
export interface WorkbenchDocumentRanking {
|
||||
id: string
|
||||
documentName: string
|
||||
department: string
|
||||
signedCount: number
|
||||
}
|
||||
|
||||
export interface WorkbenchOverviewResponse {
|
||||
summary: WorkbenchSummary
|
||||
recentTasks: WorkbenchTask[]
|
||||
trend: WorkbenchTrendPoint[]
|
||||
todos: WorkbenchTodoTask[]
|
||||
documentRanking: WorkbenchDocumentRanking[]
|
||||
}
|
||||
|
||||
export interface SigningTaskQuery extends PageQuery {
|
||||
|
||||
Reference in New Issue
Block a user