300 lines
8.2 KiB
TypeScript
300 lines
8.2 KiB
TypeScript
import axios from 'axios'
|
|
|
|
import { unwrapApiResponse } from '@/utils/api-response'
|
|
import { request } from '@/utils/request'
|
|
import type { ApiResponse, PageResult } from '@/types/common'
|
|
|
|
import type {
|
|
BackendCollection,
|
|
BackendPage,
|
|
BackendTemplateStatus,
|
|
CreateTemplateRequest,
|
|
CreateTemplateVersionRequest,
|
|
DocumentListResponse,
|
|
DocumentQuery,
|
|
DocumentRecord,
|
|
TemplateResponseDto,
|
|
TemplateVersionResponseDto,
|
|
} from './types'
|
|
|
|
const useMockData = import.meta.env.VITE_USE_MOCK !== 'false'
|
|
|
|
export const isDocumentMockEnabled = useMockData
|
|
|
|
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',
|
|
},
|
|
]
|
|
|
|
function formatDateTime(value: string | null | undefined) {
|
|
if (!value) {
|
|
return ''
|
|
}
|
|
|
|
const date = new Date(value)
|
|
|
|
if (Number.isNaN(date.getTime())) {
|
|
return value
|
|
.replace('T', ' ')
|
|
.replace(/\.\d+Z$/, '')
|
|
.replace(/Z$/, '')
|
|
}
|
|
|
|
const pad = (part: number) => String(part).padStart(2, '0')
|
|
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(
|
|
date.getHours(),
|
|
)}:${pad(date.getMinutes())}`
|
|
}
|
|
|
|
function normalizeDocumentStatus(status: BackendTemplateStatus | null | undefined) {
|
|
switch (status) {
|
|
case 'PUBLISHED':
|
|
case 'APPROVED':
|
|
return 'published' as const
|
|
case 'ARCHIVED':
|
|
case 'DISABLED':
|
|
return 'archived' as const
|
|
case 'PENDING_REVIEW':
|
|
case 'REJECTED':
|
|
return 'review' as const
|
|
case 'DRAFT':
|
|
default:
|
|
return 'draft' as const
|
|
}
|
|
}
|
|
|
|
function mapDocument(dto: TemplateResponseDto): DocumentRecord {
|
|
const versionId = dto.currentVersionId ?? undefined
|
|
const version = dto.versionSequence ? `v${dto.versionSequence}` : '—'
|
|
|
|
return {
|
|
id: dto.id,
|
|
name: dto.name,
|
|
code: dto.templateCode,
|
|
category: dto.category ?? '未分类',
|
|
version: String(version),
|
|
status: normalizeDocumentStatus(dto.status),
|
|
backendStatus: dto.status ?? undefined,
|
|
updatedBy: dto.updatedBy ?? dto.createdBy ?? '—',
|
|
updatedAt: formatDateTime(dto.updatedAt ?? dto.createdAt),
|
|
versionId,
|
|
departmentId: dto.departmentId,
|
|
departmentName: dto.departmentId ? undefined : '全院通用',
|
|
campusId: dto.campusId,
|
|
description: dto.description ?? '',
|
|
}
|
|
}
|
|
|
|
function normalizePage<T>(
|
|
data: BackendPage<T> | T[],
|
|
fallbackPage: number,
|
|
fallbackPageSize: number,
|
|
): PageResult<T> {
|
|
const records = Array.isArray(data) ? data : (data.records ?? data.items ?? data.content ?? [])
|
|
const page = Array.isArray(data) ? fallbackPage : (data.page ?? fallbackPage)
|
|
const pageSize = Array.isArray(data) ? fallbackPageSize : (data.size ?? fallbackPageSize)
|
|
|
|
return {
|
|
records,
|
|
total: Array.isArray(data) ? records.length : (data.total ?? records.length),
|
|
page,
|
|
pageSize,
|
|
}
|
|
}
|
|
|
|
function toBackendQuery(query: DocumentQuery) {
|
|
const params: Record<string, string | number> = {
|
|
page: Math.max(query.page, 1),
|
|
size: Math.min(Math.max(query.pageSize, 1), 200),
|
|
}
|
|
|
|
if (query.keyword?.trim()) {
|
|
params.keyword = query.keyword.trim()
|
|
}
|
|
|
|
if (query.status && query.status !== 'all') {
|
|
const statusMap: Record<Exclude<DocumentQuery['status'], undefined | 'all'>, string> = {
|
|
draft: 'DRAFT',
|
|
published: 'PUBLISHED',
|
|
archived: 'ARCHIVED',
|
|
review: 'PENDING_REVIEW',
|
|
}
|
|
params.status = statusMap[query.status]
|
|
}
|
|
|
|
if (query.campusId) {
|
|
params.campusId = query.campusId
|
|
}
|
|
|
|
if (query.departmentId) {
|
|
params.departmentId = query.departmentId
|
|
}
|
|
|
|
return params
|
|
}
|
|
|
|
function getRemoteDocuments(path: string, query: DocumentQuery): Promise<DocumentListResponse> {
|
|
return request
|
|
.get<ApiResponse<BackendCollection<TemplateResponseDto>>>(path, {
|
|
params: toBackendQuery(query),
|
|
})
|
|
.then((response) => {
|
|
const page = normalizePage(unwrapApiResponse(response), query.page, query.pageSize)
|
|
|
|
return {
|
|
...page,
|
|
records: page.records.map(mapDocument),
|
|
}
|
|
})
|
|
}
|
|
|
|
export function getDocuments(query: DocumentQuery): Promise<DocumentListResponse> {
|
|
if (!useMockData) {
|
|
return getRemoteDocuments('/v1/templates', 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).map((record) => ({ ...record })),
|
|
total: filteredRecords.length,
|
|
page,
|
|
pageSize,
|
|
})
|
|
}
|
|
|
|
export function getTemplatePermissionConfig(query: DocumentQuery): Promise<DocumentListResponse> {
|
|
if (useMockData) {
|
|
return getDocuments(query)
|
|
}
|
|
|
|
return getRemoteDocuments('/v1/templates/permission-config', query)
|
|
}
|
|
|
|
export async function getDocumentDetail(id: string): Promise<DocumentRecord | null> {
|
|
if (useMockData) {
|
|
const record = mockRecords.find((item) => item.id === id)
|
|
return record ? { ...record } : null
|
|
}
|
|
|
|
try {
|
|
const response = await request.get<ApiResponse<TemplateResponseDto>>(
|
|
`/v1/templates/${encodeURIComponent(id)}`,
|
|
)
|
|
return mapDocument(unwrapApiResponse(response))
|
|
} catch (error) {
|
|
if (axios.isAxiosError(error) && error.response?.status === 404) {
|
|
return null
|
|
}
|
|
|
|
throw error
|
|
}
|
|
}
|
|
|
|
export async function createTemplate(payload: CreateTemplateRequest): Promise<DocumentRecord> {
|
|
if (useMockData) {
|
|
throw new Error('Mock 模式不执行模板写操作')
|
|
}
|
|
|
|
const response = await request.post<ApiResponse<TemplateResponseDto>>('/v1/templates', payload)
|
|
return mapDocument(unwrapApiResponse(response))
|
|
}
|
|
|
|
export async function createTemplateVersion(
|
|
templateId: string,
|
|
payload: CreateTemplateVersionRequest,
|
|
): Promise<TemplateVersionResponseDto> {
|
|
if (useMockData) {
|
|
throw new Error('Mock 模式不执行模板写操作')
|
|
}
|
|
|
|
const response = await request.post<ApiResponse<TemplateVersionResponseDto>>(
|
|
`/v1/templates/${encodeURIComponent(templateId)}/versions`,
|
|
payload,
|
|
)
|
|
return unwrapApiResponse(response)
|
|
}
|
|
|
|
export async function getTemplateVersions(
|
|
templateId: string,
|
|
): Promise<TemplateVersionResponseDto[]> {
|
|
if (useMockData) {
|
|
return []
|
|
}
|
|
|
|
const response = await request.get<ApiResponse<BackendCollection<TemplateVersionResponseDto>>>(
|
|
`/v1/templates/${encodeURIComponent(templateId)}/versions`,
|
|
)
|
|
return normalizePage(unwrapApiResponse(response), 1, 200).records
|
|
}
|
|
|
|
export async function getTemplateVersionDetail(
|
|
templateId: string,
|
|
versionId: string,
|
|
): Promise<TemplateVersionResponseDto | null> {
|
|
if (useMockData) {
|
|
return null
|
|
}
|
|
|
|
try {
|
|
const response = await request.get<ApiResponse<TemplateVersionResponseDto>>(
|
|
`/v1/templates/${encodeURIComponent(templateId)}/versions/${encodeURIComponent(versionId)}`,
|
|
)
|
|
return unwrapApiResponse(response)
|
|
} catch (error) {
|
|
if (axios.isAxiosError(error) && error.response?.status === 404) {
|
|
return null
|
|
}
|
|
|
|
throw error
|
|
}
|
|
}
|
|
|
|
export type TemplateVersionAction =
|
|
'submit-review' | 'reject' | 'approve' | 'publish' | 'disable' | 'archive'
|
|
|
|
export function updateTemplateVersionStatus(
|
|
templateId: string,
|
|
versionId: string,
|
|
action: TemplateVersionAction,
|
|
reason?: string,
|
|
): Promise<TemplateVersionResponseDto> {
|
|
if (useMockData) {
|
|
return Promise.reject(new Error('Mock 模式不执行模板版本工作流'))
|
|
}
|
|
|
|
const requestData = action === 'reject' && reason?.trim() ? { comment: reason.trim() } : undefined
|
|
|
|
return request
|
|
.post<ApiResponse<TemplateVersionResponseDto>>(
|
|
`/v1/templates/${encodeURIComponent(templateId)}/versions/${encodeURIComponent(versionId)}/${action}`,
|
|
requestData,
|
|
)
|
|
.then(unwrapApiResponse)
|
|
}
|