From 24d8a9ece13d797f299a8a0ce2386fa70674f6d7 Mon Sep 17 00:00:00 2001 From: yelan Date: Thu, 27 Aug 2026 15:35:36 +0800 Subject: [PATCH] =?UTF-8?q?feat(clinical-web):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E7=AB=AF=E4=B8=8E=E5=B7=A5=E4=BD=9C=E5=8F=B0?= =?UTF-8?q?=20API=20=E6=A8=A1=E5=9D=97=E5=8F=8A=E7=B1=BB=E5=9E=8B=E5=AE=9A?= =?UTF-8?q?=E4=B9=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- clinical-web/.env.example | 1 + clinical-web/README.md | 16 +++ clinical-web/src/api/index.ts | 6 - .../api/management/document-permissions.ts | 53 ++++++++ clinical-web/src/api/management/documents.ts | 60 +++++++++ clinical-web/src/api/management/reports.ts | 38 ++++++ clinical-web/src/api/management/settings.ts | 59 +++++++++ clinical-web/src/api/management/types.ts | 117 ++++++++++++++++++ clinical-web/src/api/management/users.ts | 63 ++++++++++ clinical-web/src/api/workbench/home.ts | 42 +++++++ clinical-web/src/api/workbench/signing.ts | 83 +++++++++++++ clinical-web/src/api/workbench/types.ts | 38 ++++++ clinical-web/src/types/common.ts | 29 +++++ clinical-web/src/types/consent.ts | 11 -- clinical-web/src/utils/request.ts | 77 ++++++++++++ .../management/document-permissions/types.ts | 15 +++ .../src/views/management/documents/types.ts | 16 +++ .../src/views/management/reports/types.ts | 21 ++++ .../src/views/management/settings/types.ts | 15 +++ .../src/views/management/users/types.ts | 17 +++ .../src/views/workbench/home/types.ts | 19 +++ .../src/views/workbench/signing/types.ts | 18 +++ 22 files changed, 797 insertions(+), 17 deletions(-) delete mode 100644 clinical-web/src/api/index.ts create mode 100644 clinical-web/src/api/management/document-permissions.ts create mode 100644 clinical-web/src/api/management/documents.ts create mode 100644 clinical-web/src/api/management/reports.ts create mode 100644 clinical-web/src/api/management/settings.ts create mode 100644 clinical-web/src/api/management/types.ts create mode 100644 clinical-web/src/api/management/users.ts create mode 100644 clinical-web/src/api/workbench/home.ts create mode 100644 clinical-web/src/api/workbench/signing.ts create mode 100644 clinical-web/src/api/workbench/types.ts create mode 100644 clinical-web/src/types/common.ts delete mode 100644 clinical-web/src/types/consent.ts create mode 100644 clinical-web/src/utils/request.ts create mode 100644 clinical-web/src/views/management/document-permissions/types.ts create mode 100644 clinical-web/src/views/management/documents/types.ts create mode 100644 clinical-web/src/views/management/reports/types.ts create mode 100644 clinical-web/src/views/management/settings/types.ts create mode 100644 clinical-web/src/views/management/users/types.ts create mode 100644 clinical-web/src/views/workbench/home/types.ts create mode 100644 clinical-web/src/views/workbench/signing/types.ts diff --git a/clinical-web/.env.example b/clinical-web/.env.example index 6fed534..65734d5 100644 --- a/clinical-web/.env.example +++ b/clinical-web/.env.example @@ -1,2 +1,3 @@ VITE_APP_NAME=medical-sign-clinical VITE_API_BASE_URL=/api +VITE_USE_MOCK=true diff --git a/clinical-web/README.md b/clinical-web/README.md index e8168b8..54f9035 100644 --- a/clinical-web/README.md +++ b/clinical-web/README.md @@ -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` 后切换为真实接口。 diff --git a/clinical-web/src/api/index.ts b/clinical-web/src/api/index.ts deleted file mode 100644 index 629b64d..0000000 --- a/clinical-web/src/api/index.ts +++ /dev/null @@ -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, -}) diff --git a/clinical-web/src/api/management/document-permissions.ts b/clinical-web/src/api/management/document-permissions.ts new file mode 100644 index 0000000..072d229 --- /dev/null +++ b/clinical-web/src/api/management/document-permissions.ts @@ -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 { + if (!useMockData) { + return request.get('/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, + }) +} diff --git a/clinical-web/src/api/management/documents.ts b/clinical-web/src/api/management/documents.ts new file mode 100644 index 0000000..ed29dde --- /dev/null +++ b/clinical-web/src/api/management/documents.ts @@ -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 { + if (!useMockData) { + return request.get('/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 { + if (!useMockData) { + return request.get('/management/documents/' + id) + } + + return Promise.resolve(mockRecords.find((record) => record.id === id) ?? null) +} diff --git a/clinical-web/src/api/management/reports.ts b/clinical-web/src/api/management/reports.ts new file mode 100644 index 0000000..1fb7e51 --- /dev/null +++ b/clinical-web/src/api/management/reports.ts @@ -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 { + if (useMockData) { + return Promise.resolve(mockOverview) + } + + return request.get('/management/reports/overview', { + params: query, + }) +} diff --git a/clinical-web/src/api/management/settings.ts b/clinical-web/src/api/management/settings.ts new file mode 100644 index 0000000..a7547d7 --- /dev/null +++ b/clinical-web/src/api/management/settings.ts @@ -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 { + if (useMockData) { + return Promise.resolve(mockSettings) + } + + return request.get('/management/settings') +} + +export function updateSystemSettings(payload: UpdateSystemSettingsRequest): Promise { + if (useMockData) { + return Promise.resolve() + } + + return request.put('/management/settings', payload) +} diff --git a/clinical-web/src/api/management/types.ts b/clinical-web/src/api/management/types.ts new file mode 100644 index 0000000..e1a47c2 --- /dev/null +++ b/clinical-web/src/api/management/types.ts @@ -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 + +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 + +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 + +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 +} diff --git a/clinical-web/src/api/management/users.ts b/clinical-web/src/api/management/users.ts new file mode 100644 index 0000000..823b2e2 --- /dev/null +++ b/clinical-web/src/api/management/users.ts @@ -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 { + if (!useMockData) { + return request.get('/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, + }) +} diff --git a/clinical-web/src/api/workbench/home.ts b/clinical-web/src/api/workbench/home.ts new file mode 100644 index 0000000..1c1a848 --- /dev/null +++ b/clinical-web/src/api/workbench/home.ts @@ -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 { + if (useMockData) { + return Promise.resolve(mockOverview) + } + + return request.get('/workbench/overview') +} diff --git a/clinical-web/src/api/workbench/signing.ts b/clinical-web/src/api/workbench/signing.ts new file mode 100644 index 0000000..eb383dc --- /dev/null +++ b/clinical-web/src/api/workbench/signing.ts @@ -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 { + if (!useMockData) { + return request.get('/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 { + if (!useMockData) { + return request.get('/workbench/signing/tasks/' + id) + } + + return Promise.resolve(mockRecords.find((record) => record.id === id) ?? null) +} diff --git a/clinical-web/src/api/workbench/types.ts b/clinical-web/src/api/workbench/types.ts new file mode 100644 index 0000000..c5ef141 --- /dev/null +++ b/clinical-web/src/api/workbench/types.ts @@ -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 diff --git a/clinical-web/src/types/common.ts b/clinical-web/src/types/common.ts new file mode 100644 index 0000000..9388ef0 --- /dev/null +++ b/clinical-web/src/types/common.ts @@ -0,0 +1,29 @@ +export type ID = string + +export interface PageQuery { + page: number + pageSize: number +} + +export interface PageResult { + records: T[] + total: number + page: number + pageSize: number +} + +export interface ApiResponse { + code: number + message: string + data: T +} + +export interface SelectOption { + label: string + value: T +} + +export interface DateRange { + startDate: string + endDate: string +} diff --git a/clinical-web/src/types/consent.ts b/clinical-web/src/types/consent.ts deleted file mode 100644 index 152f1f4..0000000 --- a/clinical-web/src/types/consent.ts +++ /dev/null @@ -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 -} diff --git a/clinical-web/src/utils/request.ts b/clinical-web/src/utils/request.ts new file mode 100644 index 0000000..718bf61 --- /dev/null +++ b/clinical-web/src/utils/request.ts @@ -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(config: AxiosRequestConfig): Promise { + return this.instance.request>(config).then((response) => response.data) + } + + get(url: string, config: AxiosRequestConfig = {}): Promise { + return this.request({ + ...config, + url, + method: 'GET', + }) + } + + post(url: string, data?: unknown, config: AxiosRequestConfig = {}): Promise { + return this.request({ + ...config, + url, + data, + method: 'POST', + }) + } + + put(url: string, data?: unknown, config: AxiosRequestConfig = {}): Promise { + return this.request({ + ...config, + url, + data, + method: 'PUT', + }) + } + + delete(url: string, config: AxiosRequestConfig = {}): Promise { + return this.request({ + ...config, + url, + method: 'DELETE', + }) + } +} + +export const request = new Request() + +export default request diff --git a/clinical-web/src/views/management/document-permissions/types.ts b/clinical-web/src/views/management/document-permissions/types.ts new file mode 100644 index 0000000..756e54b --- /dev/null +++ b/clinical-web/src/views/management/document-permissions/types.ts @@ -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 +} diff --git a/clinical-web/src/views/management/documents/types.ts b/clinical-web/src/views/management/documents/types.ts new file mode 100644 index 0000000..425fc1f --- /dev/null +++ b/clinical-web/src/views/management/documents/types.ts @@ -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 +} diff --git a/clinical-web/src/views/management/reports/types.ts b/clinical-web/src/views/management/reports/types.ts new file mode 100644 index 0000000..5e1b0a5 --- /dev/null +++ b/clinical-web/src/views/management/reports/types.ts @@ -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 +} diff --git a/clinical-web/src/views/management/settings/types.ts b/clinical-web/src/views/management/settings/types.ts new file mode 100644 index 0000000..47963cf --- /dev/null +++ b/clinical-web/src/views/management/settings/types.ts @@ -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[] +} diff --git a/clinical-web/src/views/management/users/types.ts b/clinical-web/src/views/management/users/types.ts new file mode 100644 index 0000000..e0b0c73 --- /dev/null +++ b/clinical-web/src/views/management/users/types.ts @@ -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 +} diff --git a/clinical-web/src/views/workbench/home/types.ts b/clinical-web/src/views/workbench/home/types.ts new file mode 100644 index 0000000..b17b1a2 --- /dev/null +++ b/clinical-web/src/views/workbench/home/types.ts @@ -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 +} diff --git a/clinical-web/src/views/workbench/signing/types.ts b/clinical-web/src/views/workbench/signing/types.ts new file mode 100644 index 0000000..83d8da2 --- /dev/null +++ b/clinical-web/src/views/workbench/signing/types.ts @@ -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 +}