143 lines
4.1 KiB
TypeScript
143 lines
4.1 KiB
TypeScript
import { create } from 'zustand'
|
|
|
|
import { userApi } from '../http/userApi'
|
|
import type { UserQuery, UserRequest } from '../interfaces/user/api'
|
|
import type { Role, Status, User, UserSummary } from '../interfaces/user/model'
|
|
import { errorMessage } from '../utils/user'
|
|
|
|
interface UserStore {
|
|
users: User[]
|
|
total: number
|
|
summary: UserSummary
|
|
search: string
|
|
roleFilter: Role | ''
|
|
statusFilter: Status | ''
|
|
selectedIds: Set<number>
|
|
isLoading: boolean
|
|
isSaving: boolean
|
|
isBulkUpdating: boolean
|
|
error: string
|
|
setSearch: (search: string) => void
|
|
setRoleFilter: (role: Role | '') => void
|
|
setStatusFilter: (status: Status | '') => void
|
|
clearFilters: () => void
|
|
loadUsers: () => Promise<void>
|
|
loadSummary: () => Promise<void>
|
|
createUser: (payload: UserRequest) => Promise<boolean>
|
|
updateUser: (id: number, payload: UserRequest) => Promise<boolean>
|
|
deleteUser: (id: number) => Promise<boolean>
|
|
updateSelectedStatus: (status: Status) => Promise<boolean>
|
|
toggleSelected: (id: number) => void
|
|
toggleAll: () => void
|
|
clearError: () => void
|
|
}
|
|
|
|
const emptySummary: UserSummary = { total: 0, active: 0, inactive: 0, admins: 0 }
|
|
|
|
function queryFromState(state: UserStore): UserQuery {
|
|
return {
|
|
keyword: state.search,
|
|
role: state.roleFilter,
|
|
status: state.statusFilter,
|
|
page: 1,
|
|
pageSize: 50,
|
|
}
|
|
}
|
|
|
|
export const useUserStore = create<UserStore>((set, get) => ({
|
|
users: [],
|
|
total: 0,
|
|
summary: emptySummary,
|
|
search: '',
|
|
roleFilter: '',
|
|
statusFilter: '',
|
|
selectedIds: new Set<number>(),
|
|
isLoading: true,
|
|
isSaving: false,
|
|
isBulkUpdating: false,
|
|
error: '',
|
|
setSearch: (search) => set({ search }),
|
|
setRoleFilter: (roleFilter) => set({ roleFilter }),
|
|
setStatusFilter: (statusFilter) => set({ statusFilter }),
|
|
clearFilters: () => set({ search: '', roleFilter: '', statusFilter: '' }),
|
|
loadUsers: async () => {
|
|
set({ isLoading: true, error: '' })
|
|
try {
|
|
const data = await userApi.list(queryFromState(get()))
|
|
set({ users: data.items, total: data.total, selectedIds: new Set<number>() })
|
|
} catch (error) {
|
|
set({ error: errorMessage(error, '无法加载用户列表') })
|
|
} finally {
|
|
set({ isLoading: false })
|
|
}
|
|
},
|
|
loadSummary: async () => {
|
|
try {
|
|
const summary = await userApi.summary()
|
|
set({ summary })
|
|
} catch (error) {
|
|
set({ error: errorMessage(error, '无法加载用户概览') })
|
|
}
|
|
},
|
|
createUser: async (payload) => {
|
|
set({ isSaving: true, error: '' })
|
|
try {
|
|
await userApi.create(payload)
|
|
return true
|
|
} catch (error) {
|
|
set({ error: errorMessage(error, '保存失败,请检查输入') })
|
|
return false
|
|
} finally {
|
|
set({ isSaving: false })
|
|
}
|
|
},
|
|
updateUser: async (id, payload) => {
|
|
set({ isSaving: true, error: '' })
|
|
try {
|
|
await userApi.update(id, payload)
|
|
return true
|
|
} catch (error) {
|
|
set({ error: errorMessage(error, '保存失败,请检查输入') })
|
|
return false
|
|
} finally {
|
|
set({ isSaving: false })
|
|
}
|
|
},
|
|
deleteUser: async (id) => {
|
|
set({ error: '' })
|
|
try {
|
|
await userApi.remove(id)
|
|
return true
|
|
} catch (error) {
|
|
set({ error: errorMessage(error, '删除失败,请稍后重试') })
|
|
return false
|
|
}
|
|
},
|
|
updateSelectedStatus: async (status) => {
|
|
const ids = [...get().selectedIds]
|
|
if (ids.length === 0) return false
|
|
set({ isBulkUpdating: true, error: '' })
|
|
try {
|
|
await userApi.updateStatuses({ ids, status })
|
|
return true
|
|
} catch (error) {
|
|
set({ error: errorMessage(error, '批量更新失败,请稍后重试') })
|
|
return false
|
|
} finally {
|
|
set({ isBulkUpdating: false })
|
|
}
|
|
},
|
|
toggleSelected: (id) => set((state) => {
|
|
const selectedIds = new Set(state.selectedIds)
|
|
if (selectedIds.has(id)) selectedIds.delete(id)
|
|
else selectedIds.add(id)
|
|
return { selectedIds }
|
|
}),
|
|
toggleAll: () => set((state) => ({
|
|
selectedIds: state.users.length > 0 && state.selectedIds.size === state.users.length
|
|
? new Set<number>()
|
|
: new Set(state.users.map((user) => user.id)),
|
|
})),
|
|
clearError: () => set({ error: '' }),
|
|
}))
|