Files
xh-medical-sign-web/clinical-web/src/api/management/users.ts

212 lines
5.7 KiB
TypeScript

import axios from 'axios'
import { unwrapApiResponse, unwrapNullableApiResponse } from '@/utils/api-response'
import { request } from '@/utils/request'
import type { ApiResponse, PageResult } from '@/types/common'
import type {
BackendCollection,
BackendPage,
CreateUserRequest,
UpdateUserRequest,
UserListResponse,
UserQuery,
UserRecord,
UserResponseDto,
} from './types'
const useMockData = import.meta.env.VITE_USE_MOCK !== 'false'
export const isUserMockEnabled = useMockData
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',
},
]
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 normalizeStatus(value: string | null | undefined): UserRecord['status'] {
return value === 'DISABLED' || value === 'INACTIVE' ? 'disabled' : 'enabled'
}
function mapUser(dto: UserResponseDto): UserRecord {
return {
id: dto.id,
name: dto.displayName,
account: dto.username,
department: dto.departmentId ?? '未指定科室',
role: '未分配角色',
status: normalizeStatus(dto.status),
lastLoginAt: formatDateTime(dto.lastLoginAt),
employeeNo: dto.employeeNo,
phone: dto.phone,
email: dto.email,
campusId: dto.campusId,
departmentId: dto.departmentId,
dataScope: dto.dataScope ?? '—',
}
}
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 ?? [])
return {
records,
total: Array.isArray(data) ? records.length : (data.total ?? records.length),
page: Array.isArray(data) ? fallbackPage : (data.page ?? fallbackPage),
pageSize: Array.isArray(data) ? fallbackPageSize : (data.size ?? fallbackPageSize),
}
}
function toBackendQuery(query: UserQuery) {
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()
}
return params
}
export function getUsers(query: UserQuery): Promise<UserListResponse> {
if (!useMockData) {
return request
.get<ApiResponse<BackendCollection<UserResponseDto>>>('/v1/users', {
params: toBackendQuery(query),
})
.then((response) => {
const page = normalizePage(unwrapApiResponse(response), query.page, query.pageSize)
return {
...page,
records: page.records.map(mapUser),
}
})
}
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,
})
}
export async function getUserDetail(id: string): Promise<UserRecord | null> {
if (useMockData) {
const record = mockRecords.find((item) => item.id === id)
return record ? { ...record } : null
}
try {
const response = await request.get<ApiResponse<UserResponseDto>>(
`/v1/users/${encodeURIComponent(id)}`,
)
return mapUser(unwrapApiResponse(response))
} catch (error) {
if (axios.isAxiosError(error) && error.response?.status === 404) {
return null
}
throw error
}
}
export function createUser(payload: CreateUserRequest): Promise<UserRecord> {
if (useMockData) {
return Promise.reject(new Error('Mock 模式不执行用户写操作'))
}
return request
.post<ApiResponse<UserResponseDto>>('/v1/users', payload)
.then(unwrapApiResponse)
.then(mapUser)
}
export function updateUser(id: string, payload: UpdateUserRequest): Promise<UserRecord> {
if (useMockData) {
return Promise.reject(new Error('Mock 模式不执行用户写操作'))
}
return request
.put<ApiResponse<UserResponseDto>>(`/v1/users/${encodeURIComponent(id)}`, payload)
.then(unwrapApiResponse)
.then(mapUser)
}
export function deleteUser(id: string): Promise<void> {
if (useMockData) {
return Promise.reject(new Error('Mock 模式不执行用户写操作'))
}
return request
.delete<ApiResponse<null>>(`/v1/users/${encodeURIComponent(id)}`)
.then((response) => {
unwrapNullableApiResponse(response)
})
}