212 lines
5.7 KiB
TypeScript
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)
|
|
})
|
|
}
|