feat(clinical-web): 添加管理端与工作台 API 模块及类型定义
This commit is contained in:
@@ -1,2 +1,3 @@
|
||||
VITE_APP_NAME=medical-sign-clinical
|
||||
VITE_API_BASE_URL=/api
|
||||
VITE_USE_MOCK=true
|
||||
|
||||
@@ -107,3 +107,19 @@ ClinicalLayout
|
||||
布局拆分为 `Menu`、`Header` 和 `Container` 三个公共组件,页面内容由各自的 View 负责。
|
||||
|
||||
页面目录按业务域组织:`auth` 保留登录页;工作台页面位于 `views/workbench`;管理页面位于 `views/management`。每个具体页面目录都有 `index.vue` 入口和 `components` 目录,用于继续拆分当前页面组件。
|
||||
|
||||
## API 与类型约定
|
||||
|
||||
API 模块与页面按业务域对应。工作台页面使用 `api/workbench` 下的页面文件,管理页面使用 `api/management` 下的直接文件:
|
||||
|
||||
- `api/workbench/home.ts`
|
||||
- `api/workbench/signing.ts`
|
||||
- `api/management/documents.ts`
|
||||
- `api/management/reports.ts`
|
||||
- `api/management/users.ts`
|
||||
- `api/management/document-permissions.ts`
|
||||
- `api/management/settings.ts`
|
||||
|
||||
API 请求和响应类型按业务域放在 `api/workbench/types.ts`、`api/management/types.ts`;页面展示和交互类型放在对应 View 目录的 `types.ts`;全局复用类型放在 `types/common.ts`。
|
||||
|
||||
所有接口统一使用 `utils/request.ts` 导出的单例请求实例。当前 API 模块默认返回 mock 数据,设置 `VITE_USE_MOCK=false` 后切换为真实接口。
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
import axios from 'axios'
|
||||
|
||||
export const apiClient = axios.create({
|
||||
baseURL: import.meta.env.VITE_API_BASE_URL || '/api',
|
||||
timeout: 10_000,
|
||||
})
|
||||
53
clinical-web/src/api/management/document-permissions.ts
Normal file
53
clinical-web/src/api/management/document-permissions.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { request } from '@/utils/request'
|
||||
|
||||
import type { PermissionListResponse, PermissionQuery, PermissionRecord } from './types'
|
||||
|
||||
const useMockData = import.meta.env.VITE_USE_MOCK !== 'false'
|
||||
|
||||
const mockRecords: PermissionRecord[] = [
|
||||
{
|
||||
id: 'permission-001',
|
||||
subjectType: 'role',
|
||||
subjectName: '医生',
|
||||
documentName: '住院患者知情同意书',
|
||||
permission: 'sign',
|
||||
updatedAt: '2026-08-26 16:40',
|
||||
},
|
||||
{
|
||||
id: 'permission-002',
|
||||
subjectType: 'role',
|
||||
subjectName: '护士',
|
||||
documentName: '急诊留观知情同意书',
|
||||
permission: 'view',
|
||||
updatedAt: '2026-08-25 11:08',
|
||||
},
|
||||
]
|
||||
|
||||
export function getDocumentPermissions(query: PermissionQuery): Promise<PermissionListResponse> {
|
||||
if (!useMockData) {
|
||||
return request.get<PermissionListResponse>('/management/document-permissions', {
|
||||
params: query,
|
||||
})
|
||||
}
|
||||
|
||||
const keyword = query.keyword?.trim().toLowerCase()
|
||||
const filteredRecords = mockRecords.filter((record) => {
|
||||
const matchesKeyword =
|
||||
!keyword ||
|
||||
[record.subjectName, record.documentName].join(' ').toLowerCase().includes(keyword)
|
||||
const matchesSubjectType =
|
||||
!query.subjectType || query.subjectType === 'all' || record.subjectType === query.subjectType
|
||||
|
||||
return matchesKeyword && matchesSubjectType
|
||||
})
|
||||
const page = Math.max(query.page, 1)
|
||||
const pageSize = Math.max(query.pageSize, 1)
|
||||
const start = (page - 1) * pageSize
|
||||
|
||||
return Promise.resolve({
|
||||
records: filteredRecords.slice(start, start + pageSize),
|
||||
total: filteredRecords.length,
|
||||
page,
|
||||
pageSize,
|
||||
})
|
||||
}
|
||||
60
clinical-web/src/api/management/documents.ts
Normal file
60
clinical-web/src/api/management/documents.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { request } from '@/utils/request'
|
||||
|
||||
import type { DocumentListResponse, DocumentQuery, DocumentRecord } from './types'
|
||||
|
||||
const useMockData = import.meta.env.VITE_USE_MOCK !== 'false'
|
||||
|
||||
const mockRecords: DocumentRecord[] = [
|
||||
{
|
||||
id: 'doc-001',
|
||||
name: '住院患者知情同意书',
|
||||
category: '住院',
|
||||
version: 'V2.1',
|
||||
status: 'published',
|
||||
updatedBy: '张文静',
|
||||
updatedAt: '2026-08-26 17:20',
|
||||
},
|
||||
{
|
||||
id: 'doc-002',
|
||||
name: '急诊留观知情同意书',
|
||||
category: '急诊',
|
||||
version: 'V1.3',
|
||||
status: 'published',
|
||||
updatedBy: '张文静',
|
||||
updatedAt: '2026-08-25 10:15',
|
||||
},
|
||||
]
|
||||
|
||||
export function getDocuments(query: DocumentQuery): Promise<DocumentListResponse> {
|
||||
if (!useMockData) {
|
||||
return request.get<DocumentListResponse>('/management/documents', {
|
||||
params: query,
|
||||
})
|
||||
}
|
||||
|
||||
const keyword = query.keyword?.trim().toLowerCase()
|
||||
const filteredRecords = mockRecords.filter((record) => {
|
||||
const matchesKeyword = !keyword || record.name.toLowerCase().includes(keyword)
|
||||
const matchesStatus = !query.status || query.status === 'all' || record.status === query.status
|
||||
|
||||
return matchesKeyword && matchesStatus
|
||||
})
|
||||
const page = Math.max(query.page, 1)
|
||||
const pageSize = Math.max(query.pageSize, 1)
|
||||
const start = (page - 1) * pageSize
|
||||
|
||||
return Promise.resolve({
|
||||
records: filteredRecords.slice(start, start + pageSize),
|
||||
total: filteredRecords.length,
|
||||
page,
|
||||
pageSize,
|
||||
})
|
||||
}
|
||||
|
||||
export function getDocumentDetail(id: string): Promise<DocumentRecord | null> {
|
||||
if (!useMockData) {
|
||||
return request.get<DocumentRecord>('/management/documents/' + id)
|
||||
}
|
||||
|
||||
return Promise.resolve(mockRecords.find((record) => record.id === id) ?? null)
|
||||
}
|
||||
38
clinical-web/src/api/management/reports.ts
Normal file
38
clinical-web/src/api/management/reports.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { request } from '@/utils/request'
|
||||
|
||||
import type { ReportOverviewResponse, ReportQuery } from './types'
|
||||
|
||||
const useMockData = import.meta.env.VITE_USE_MOCK !== 'false'
|
||||
|
||||
const mockOverview: ReportOverviewResponse = {
|
||||
metrics: [
|
||||
{ key: 'total', label: '签署总量', value: 1268, unit: '份', trend: 12.8 },
|
||||
{ key: 'signed', label: '已完成', value: 1142, unit: '份', trend: 8.6 },
|
||||
{ key: 'rate', label: '完成率', value: 90.1, unit: '%', trend: 3.2 },
|
||||
{ key: 'average', label: '平均耗时', value: 6.4, unit: '分钟', trend: -4.5 },
|
||||
],
|
||||
trend: [
|
||||
{ date: '08-21', signed: 146, rejected: 5, pending: 12 },
|
||||
{ date: '08-22', signed: 158, rejected: 4, pending: 10 },
|
||||
{ date: '08-23', signed: 132, rejected: 7, pending: 15 },
|
||||
{ date: '08-24', signed: 176, rejected: 3, pending: 9 },
|
||||
{ date: '08-25', signed: 188, rejected: 6, pending: 13 },
|
||||
{ date: '08-26', signed: 172, rejected: 5, pending: 11 },
|
||||
{ date: '08-27', signed: 170, rejected: 4, pending: 8 },
|
||||
],
|
||||
departments: [
|
||||
{ department: '消化内科', total: 328, signed: 305, completionRate: 93.0 },
|
||||
{ department: '儿科急诊', total: 286, signed: 249, completionRate: 87.1 },
|
||||
{ department: '普外科', total: 264, signed: 243, completionRate: 92.0 },
|
||||
],
|
||||
}
|
||||
|
||||
export function getReportOverview(query: ReportQuery): Promise<ReportOverviewResponse> {
|
||||
if (useMockData) {
|
||||
return Promise.resolve(mockOverview)
|
||||
}
|
||||
|
||||
return request.get<ReportOverviewResponse>('/management/reports/overview', {
|
||||
params: query,
|
||||
})
|
||||
}
|
||||
59
clinical-web/src/api/management/settings.ts
Normal file
59
clinical-web/src/api/management/settings.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { request } from '@/utils/request'
|
||||
|
||||
import type { SystemSettingsResponse, UpdateSystemSettingsRequest } from './types'
|
||||
|
||||
const useMockData = import.meta.env.VITE_USE_MOCK !== 'false'
|
||||
|
||||
const mockSettings: SystemSettingsResponse = {
|
||||
sections: [
|
||||
{
|
||||
key: 'signature',
|
||||
label: '签署设置',
|
||||
items: [
|
||||
{
|
||||
key: 'linkExpireMinutes',
|
||||
label: '手机签署链接有效期',
|
||||
description: '超过有效期后,患者需要重新获取签署链接。',
|
||||
value: 30,
|
||||
valueType: 'number',
|
||||
},
|
||||
{
|
||||
key: 'allowTablet',
|
||||
label: '允许平板端签署',
|
||||
description: '开启后可以在医院平板设备上发起现场签署。',
|
||||
value: true,
|
||||
valueType: 'boolean',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
key: 'notification',
|
||||
label: '通知设置',
|
||||
items: [
|
||||
{
|
||||
key: 'notifyOnFailure',
|
||||
label: '回传失败提醒',
|
||||
description: '电子病历回传失败时通知相关管理人员。',
|
||||
value: true,
|
||||
valueType: 'boolean',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export function getSystemSettings(): Promise<SystemSettingsResponse> {
|
||||
if (useMockData) {
|
||||
return Promise.resolve(mockSettings)
|
||||
}
|
||||
|
||||
return request.get<SystemSettingsResponse>('/management/settings')
|
||||
}
|
||||
|
||||
export function updateSystemSettings(payload: UpdateSystemSettingsRequest): Promise<void> {
|
||||
if (useMockData) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
return request.put<void>('/management/settings', payload)
|
||||
}
|
||||
117
clinical-web/src/api/management/types.ts
Normal file
117
clinical-web/src/api/management/types.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
import type { PageQuery, PageResult } from '@/types/common'
|
||||
|
||||
export type DocumentStatus = 'draft' | 'published' | 'archived'
|
||||
|
||||
export interface DocumentQuery extends PageQuery {
|
||||
keyword?: string
|
||||
status?: DocumentStatus | 'all'
|
||||
}
|
||||
|
||||
export interface DocumentRecord {
|
||||
id: string
|
||||
name: string
|
||||
category: string
|
||||
version: string
|
||||
status: DocumentStatus
|
||||
updatedBy: string
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export type DocumentListResponse = PageResult<DocumentRecord>
|
||||
|
||||
export type ReportPeriod = 'today' | 'week' | 'month' | 'year'
|
||||
|
||||
export interface ReportQuery {
|
||||
period: ReportPeriod
|
||||
department?: string
|
||||
}
|
||||
|
||||
export interface ReportMetric {
|
||||
key: string
|
||||
label: string
|
||||
value: number
|
||||
unit: string
|
||||
trend: number
|
||||
}
|
||||
|
||||
export interface ReportTrendPoint {
|
||||
date: string
|
||||
signed: number
|
||||
rejected: number
|
||||
pending: number
|
||||
}
|
||||
|
||||
export interface ReportDepartment {
|
||||
department: string
|
||||
total: number
|
||||
signed: number
|
||||
completionRate: number
|
||||
}
|
||||
|
||||
export interface ReportOverviewResponse {
|
||||
metrics: ReportMetric[]
|
||||
trend: ReportTrendPoint[]
|
||||
departments: ReportDepartment[]
|
||||
}
|
||||
|
||||
export type UserStatus = 'enabled' | 'disabled'
|
||||
|
||||
export interface UserQuery extends PageQuery {
|
||||
keyword?: string
|
||||
department?: string
|
||||
status?: UserStatus | 'all'
|
||||
}
|
||||
|
||||
export interface UserRecord {
|
||||
id: string
|
||||
name: string
|
||||
account: string
|
||||
department: string
|
||||
role: string
|
||||
status: UserStatus
|
||||
lastLoginAt: string
|
||||
}
|
||||
|
||||
export type UserListResponse = PageResult<UserRecord>
|
||||
|
||||
export type PermissionSubjectType = 'user' | 'role'
|
||||
|
||||
export interface PermissionQuery extends PageQuery {
|
||||
keyword?: string
|
||||
subjectType?: PermissionSubjectType | 'all'
|
||||
}
|
||||
|
||||
export interface PermissionRecord {
|
||||
id: string
|
||||
subjectType: PermissionSubjectType
|
||||
subjectName: string
|
||||
documentName: string
|
||||
permission: 'view' | 'sign' | 'manage'
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export type PermissionListResponse = PageResult<PermissionRecord>
|
||||
|
||||
export type SettingValue = string | number | boolean
|
||||
|
||||
export interface SystemSettingRecord {
|
||||
key: string
|
||||
label: string
|
||||
description: string
|
||||
value: SettingValue
|
||||
valueType: 'string' | 'number' | 'boolean'
|
||||
}
|
||||
|
||||
export interface SystemSettingSection {
|
||||
key: string
|
||||
label: string
|
||||
items: SystemSettingRecord[]
|
||||
}
|
||||
|
||||
export interface SystemSettingsResponse {
|
||||
sections: SystemSettingSection[]
|
||||
}
|
||||
|
||||
export interface UpdateSystemSettingsRequest {
|
||||
values: Record<string, SettingValue>
|
||||
}
|
||||
63
clinical-web/src/api/management/users.ts
Normal file
63
clinical-web/src/api/management/users.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { request } from '@/utils/request'
|
||||
|
||||
import type { UserListResponse, UserQuery, UserRecord } from './types'
|
||||
|
||||
const useMockData = import.meta.env.VITE_USE_MOCK !== 'false'
|
||||
|
||||
const mockRecords: UserRecord[] = [
|
||||
{
|
||||
id: 'user-001',
|
||||
name: '张文静',
|
||||
account: 'zhangwenjing',
|
||||
department: '医务管理',
|
||||
role: '系统管理员',
|
||||
status: 'enabled',
|
||||
lastLoginAt: '2026-08-27 08:12',
|
||||
},
|
||||
{
|
||||
id: 'user-002',
|
||||
name: '王医生',
|
||||
account: 'wangdoctor',
|
||||
department: '消化内科',
|
||||
role: '医生',
|
||||
status: 'enabled',
|
||||
lastLoginAt: '2026-08-27 08:35',
|
||||
},
|
||||
{
|
||||
id: 'user-003',
|
||||
name: '李护士',
|
||||
account: 'linurse',
|
||||
department: '儿科急诊',
|
||||
role: '护士',
|
||||
status: 'disabled',
|
||||
lastLoginAt: '2026-08-20 14:26',
|
||||
},
|
||||
]
|
||||
|
||||
export function getUsers(query: UserQuery): Promise<UserListResponse> {
|
||||
if (!useMockData) {
|
||||
return request.get<UserListResponse>('/management/users', {
|
||||
params: query,
|
||||
})
|
||||
}
|
||||
|
||||
const keyword = query.keyword?.trim().toLowerCase()
|
||||
const filteredRecords = mockRecords.filter((record) => {
|
||||
const matchesKeyword =
|
||||
!keyword || [record.name, record.account].join(' ').toLowerCase().includes(keyword)
|
||||
const matchesDepartment = !query.department || record.department === query.department
|
||||
const matchesStatus = !query.status || query.status === 'all' || record.status === query.status
|
||||
|
||||
return matchesKeyword && matchesDepartment && matchesStatus
|
||||
})
|
||||
const page = Math.max(query.page, 1)
|
||||
const pageSize = Math.max(query.pageSize, 1)
|
||||
const start = (page - 1) * pageSize
|
||||
|
||||
return Promise.resolve({
|
||||
records: filteredRecords.slice(start, start + pageSize),
|
||||
total: filteredRecords.length,
|
||||
page,
|
||||
pageSize,
|
||||
})
|
||||
}
|
||||
42
clinical-web/src/api/workbench/home.ts
Normal file
42
clinical-web/src/api/workbench/home.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { request } from '@/utils/request'
|
||||
|
||||
import type { WorkbenchOverviewResponse } 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',
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export function getWorkbenchOverview(): Promise<WorkbenchOverviewResponse> {
|
||||
if (useMockData) {
|
||||
return Promise.resolve(mockOverview)
|
||||
}
|
||||
|
||||
return request.get<WorkbenchOverviewResponse>('/workbench/overview')
|
||||
}
|
||||
83
clinical-web/src/api/workbench/signing.ts
Normal file
83
clinical-web/src/api/workbench/signing.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import { request } from '@/utils/request'
|
||||
|
||||
import type { SigningTaskListResponse, SigningTaskQuery, SigningTaskRecord } from './types'
|
||||
|
||||
const useMockData = import.meta.env.VITE_USE_MOCK !== 'false'
|
||||
|
||||
const mockRecords: SigningTaskRecord[] = [
|
||||
{
|
||||
id: 'task-001',
|
||||
patientName: '李某某',
|
||||
visitNo: 'ZY20260827001',
|
||||
documentName: '住院患者知情同意书',
|
||||
department: '消化内科',
|
||||
status: 'pending',
|
||||
signerName: '李某某',
|
||||
source: '电子病历',
|
||||
expiresAt: '2026-08-28 09:30',
|
||||
updatedAt: '2026-08-27 09:30',
|
||||
},
|
||||
{
|
||||
id: 'task-002',
|
||||
patientName: '王某某',
|
||||
visitNo: 'JZ20260827018',
|
||||
documentName: '急诊留观知情同意书',
|
||||
department: '儿科急诊',
|
||||
status: 'signed',
|
||||
signerName: '王某某家属',
|
||||
source: '工作台发起',
|
||||
expiresAt: '2026-08-27 18:00',
|
||||
updatedAt: '2026-08-27 09:12',
|
||||
},
|
||||
{
|
||||
id: 'task-003',
|
||||
patientName: '赵某某',
|
||||
visitNo: 'ZY20260826027',
|
||||
documentName: '内镜检查知情同意书',
|
||||
department: '消化内科',
|
||||
status: 'rejected',
|
||||
signerName: '赵某某家属',
|
||||
source: '电子病历',
|
||||
expiresAt: '2026-08-27 16:00',
|
||||
updatedAt: '2026-08-27 08:45',
|
||||
},
|
||||
]
|
||||
|
||||
export function getSigningTasks(query: SigningTaskQuery): Promise<SigningTaskListResponse> {
|
||||
if (!useMockData) {
|
||||
return request.get<SigningTaskListResponse>('/workbench/signing/tasks', {
|
||||
params: query,
|
||||
})
|
||||
}
|
||||
|
||||
const keyword = query.keyword?.trim().toLowerCase()
|
||||
const filteredRecords = mockRecords.filter((record) => {
|
||||
const matchesKeyword =
|
||||
!keyword ||
|
||||
[record.patientName, record.visitNo, record.documentName]
|
||||
.join(' ')
|
||||
.toLowerCase()
|
||||
.includes(keyword)
|
||||
const matchesStatus = !query.status || query.status === 'all' || record.status === query.status
|
||||
|
||||
return matchesKeyword && matchesStatus
|
||||
})
|
||||
const page = Math.max(query.page, 1)
|
||||
const pageSize = Math.max(query.pageSize, 1)
|
||||
const start = (page - 1) * pageSize
|
||||
|
||||
return Promise.resolve({
|
||||
records: filteredRecords.slice(start, start + pageSize),
|
||||
total: filteredRecords.length,
|
||||
page,
|
||||
pageSize,
|
||||
})
|
||||
}
|
||||
|
||||
export function getSigningTaskDetail(id: string): Promise<SigningTaskRecord | null> {
|
||||
if (!useMockData) {
|
||||
return request.get<SigningTaskRecord>('/workbench/signing/tasks/' + id)
|
||||
}
|
||||
|
||||
return Promise.resolve(mockRecords.find((record) => record.id === id) ?? null)
|
||||
}
|
||||
38
clinical-web/src/api/workbench/types.ts
Normal file
38
clinical-web/src/api/workbench/types.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import type { PageQuery, PageResult } from '@/types/common'
|
||||
|
||||
export type SigningTaskStatus = 'pending' | 'signing' | 'signed' | 'rejected' | 'expired'
|
||||
|
||||
export interface WorkbenchSummary {
|
||||
pendingTasks: number
|
||||
todaySigned: number
|
||||
abnormalTasks: number
|
||||
documentCount: number
|
||||
}
|
||||
|
||||
export interface WorkbenchTask {
|
||||
id: string
|
||||
patientName: string
|
||||
visitNo: string
|
||||
documentName: string
|
||||
department: string
|
||||
status: SigningTaskStatus
|
||||
updatedAt: string
|
||||
}
|
||||
|
||||
export interface WorkbenchOverviewResponse {
|
||||
summary: WorkbenchSummary
|
||||
recentTasks: WorkbenchTask[]
|
||||
}
|
||||
|
||||
export interface SigningTaskQuery extends PageQuery {
|
||||
keyword?: string
|
||||
status?: SigningTaskStatus | 'all'
|
||||
}
|
||||
|
||||
export interface SigningTaskRecord extends WorkbenchTask {
|
||||
signerName: string
|
||||
source: string
|
||||
expiresAt: string
|
||||
}
|
||||
|
||||
export type SigningTaskListResponse = PageResult<SigningTaskRecord>
|
||||
29
clinical-web/src/types/common.ts
Normal file
29
clinical-web/src/types/common.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
export type ID = string
|
||||
|
||||
export interface PageQuery {
|
||||
page: number
|
||||
pageSize: number
|
||||
}
|
||||
|
||||
export interface PageResult<T> {
|
||||
records: T[]
|
||||
total: number
|
||||
page: number
|
||||
pageSize: number
|
||||
}
|
||||
|
||||
export interface ApiResponse<T> {
|
||||
code: number
|
||||
message: string
|
||||
data: T
|
||||
}
|
||||
|
||||
export interface SelectOption<T extends string = string> {
|
||||
label: string
|
||||
value: T
|
||||
}
|
||||
|
||||
export interface DateRange {
|
||||
startDate: string
|
||||
endDate: string
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
export type ConsentTaskStatus = 'pending' | 'signed' | 'rejected' | 'expired'
|
||||
|
||||
export interface ConsentTask {
|
||||
id: string
|
||||
patientName: string
|
||||
visitNo: string
|
||||
documentName: string
|
||||
source: string
|
||||
status: ConsentTaskStatus
|
||||
updatedAt: string
|
||||
}
|
||||
77
clinical-web/src/utils/request.ts
Normal file
77
clinical-web/src/utils/request.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import axios, {
|
||||
type AxiosInstance,
|
||||
type AxiosRequestConfig,
|
||||
type AxiosResponse,
|
||||
type InternalAxiosRequestConfig,
|
||||
} from 'axios'
|
||||
|
||||
const DEFAULT_TIMEOUT = 15_000
|
||||
|
||||
class Request {
|
||||
private readonly instance: AxiosInstance
|
||||
|
||||
constructor(baseURL = import.meta.env.VITE_API_BASE_URL || '/api', timeout = DEFAULT_TIMEOUT) {
|
||||
this.instance = axios.create({
|
||||
baseURL,
|
||||
timeout,
|
||||
withCredentials: true,
|
||||
})
|
||||
|
||||
this.instance.interceptors.request.use((config: InternalAxiosRequestConfig) => {
|
||||
const token = localStorage.getItem('token')
|
||||
|
||||
if (token) {
|
||||
config.headers.set('Authorization', 'Bearer ' + token)
|
||||
}
|
||||
|
||||
return config
|
||||
})
|
||||
|
||||
this.instance.interceptors.response.use(
|
||||
(response: AxiosResponse) => response,
|
||||
(error: unknown) => Promise.reject(error),
|
||||
)
|
||||
}
|
||||
|
||||
request<T = unknown>(config: AxiosRequestConfig): Promise<T> {
|
||||
return this.instance.request<T, AxiosResponse<T>>(config).then((response) => response.data)
|
||||
}
|
||||
|
||||
get<T = unknown>(url: string, config: AxiosRequestConfig = {}): Promise<T> {
|
||||
return this.request<T>({
|
||||
...config,
|
||||
url,
|
||||
method: 'GET',
|
||||
})
|
||||
}
|
||||
|
||||
post<T = unknown>(url: string, data?: unknown, config: AxiosRequestConfig = {}): Promise<T> {
|
||||
return this.request<T>({
|
||||
...config,
|
||||
url,
|
||||
data,
|
||||
method: 'POST',
|
||||
})
|
||||
}
|
||||
|
||||
put<T = unknown>(url: string, data?: unknown, config: AxiosRequestConfig = {}): Promise<T> {
|
||||
return this.request<T>({
|
||||
...config,
|
||||
url,
|
||||
data,
|
||||
method: 'PUT',
|
||||
})
|
||||
}
|
||||
|
||||
delete<T = unknown>(url: string, config: AxiosRequestConfig = {}): Promise<T> {
|
||||
return this.request<T>({
|
||||
...config,
|
||||
url,
|
||||
method: 'DELETE',
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export const request = new Request()
|
||||
|
||||
export default request
|
||||
@@ -0,0 +1,15 @@
|
||||
export type PermissionSubjectFilter = 'all' | 'user' | 'role'
|
||||
|
||||
export interface PermissionFilterForm {
|
||||
keyword: string
|
||||
subjectType: PermissionSubjectFilter
|
||||
}
|
||||
|
||||
export interface PermissionTableRow {
|
||||
id: string
|
||||
subjectType: PermissionSubjectFilter
|
||||
subjectName: string
|
||||
documentName: string
|
||||
permission: 'view' | 'sign' | 'manage'
|
||||
updatedAt: string
|
||||
}
|
||||
16
clinical-web/src/views/management/documents/types.ts
Normal file
16
clinical-web/src/views/management/documents/types.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
export type DocumentFilterStatus = 'all' | 'draft' | 'published' | 'archived'
|
||||
|
||||
export interface DocumentFilterForm {
|
||||
keyword: string
|
||||
status: DocumentFilterStatus
|
||||
}
|
||||
|
||||
export interface DocumentTableRow {
|
||||
id: string
|
||||
name: string
|
||||
category: string
|
||||
version: string
|
||||
status: DocumentFilterStatus
|
||||
updatedBy: string
|
||||
updatedAt: string
|
||||
}
|
||||
21
clinical-web/src/views/management/reports/types.ts
Normal file
21
clinical-web/src/views/management/reports/types.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
export type ReportPeriodOption = 'today' | 'week' | 'month' | 'year'
|
||||
|
||||
export interface ReportFilterForm {
|
||||
period: ReportPeriodOption
|
||||
department: string
|
||||
}
|
||||
|
||||
export interface ReportMetricCard {
|
||||
key: string
|
||||
label: string
|
||||
value: number
|
||||
unit: string
|
||||
trend: number
|
||||
}
|
||||
|
||||
export interface ReportChartPoint {
|
||||
date: string
|
||||
signed: number
|
||||
rejected: number
|
||||
pending: number
|
||||
}
|
||||
15
clinical-web/src/views/management/settings/types.ts
Normal file
15
clinical-web/src/views/management/settings/types.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
export type SettingValue = string | number | boolean
|
||||
|
||||
export interface SettingFormItem {
|
||||
key: string
|
||||
label: string
|
||||
description: string
|
||||
value: SettingValue
|
||||
valueType: 'string' | 'number' | 'boolean'
|
||||
}
|
||||
|
||||
export interface SettingSection {
|
||||
key: string
|
||||
label: string
|
||||
items: SettingFormItem[]
|
||||
}
|
||||
17
clinical-web/src/views/management/users/types.ts
Normal file
17
clinical-web/src/views/management/users/types.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
export type UserFilterStatus = 'all' | 'enabled' | 'disabled'
|
||||
|
||||
export interface UserFilterForm {
|
||||
keyword: string
|
||||
department: string
|
||||
status: UserFilterStatus
|
||||
}
|
||||
|
||||
export interface UserTableRow {
|
||||
id: string
|
||||
name: string
|
||||
account: string
|
||||
department: string
|
||||
role: string
|
||||
status: UserFilterStatus
|
||||
lastLoginAt: string
|
||||
}
|
||||
19
clinical-web/src/views/workbench/home/types.ts
Normal file
19
clinical-web/src/views/workbench/home/types.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
export type HomeMetricTone = 'brand' | 'success' | 'warning' | 'danger'
|
||||
|
||||
export interface HomeMetricCard {
|
||||
key: string
|
||||
label: string
|
||||
value: number
|
||||
unit: string
|
||||
tone: HomeMetricTone
|
||||
description?: string
|
||||
}
|
||||
|
||||
export interface HomeTaskItem {
|
||||
id: string
|
||||
title: string
|
||||
patientName: string
|
||||
documentName: string
|
||||
status: string
|
||||
updatedAt: string
|
||||
}
|
||||
18
clinical-web/src/views/workbench/signing/types.ts
Normal file
18
clinical-web/src/views/workbench/signing/types.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
export type SigningFilterStatus = 'all' | 'pending' | 'signing' | 'signed' | 'rejected' | 'expired'
|
||||
|
||||
export interface SigningFilterForm {
|
||||
keyword: string
|
||||
status: SigningFilterStatus
|
||||
dateRange: [string, string] | null
|
||||
}
|
||||
|
||||
export interface SigningTableRow {
|
||||
id: string
|
||||
patientName: string
|
||||
visitNo: string
|
||||
documentName: string
|
||||
department: string
|
||||
signerName: string
|
||||
status: SigningFilterStatus
|
||||
updatedAt: string
|
||||
}
|
||||
Reference in New Issue
Block a user