Fix admin users pagination follow-ups

This commit is contained in:
RWDai
2026-05-20 17:32:47 +08:00
parent 44fb1af287
commit 77051e6245
3 changed files with 52 additions and 8 deletions

View File

@@ -274,7 +274,6 @@ import type {
const props = defineProps<{ const props = defineProps<{
open: boolean open: boolean
users: User[]
}>() }>()
const emit = defineEmits<{ const emit = defineEmits<{
@@ -295,8 +294,11 @@ const {
const loading = ref(false) const loading = ref(false)
const saving = ref(false) const saving = ref(false)
const groups = ref<UserGroup[]>([]) const groups = ref<UserGroup[]>([])
const dialogUsers = ref<User[]>([])
const editingGroupId = ref<string | null>(null) const editingGroupId = ref<string | null>(null)
const memberUserIds = ref<string[]>([]) const memberUserIds = ref<string[]>([])
const USER_OPTIONS_CACHE_TTL_MS = 30 * 1000
let dialogUsersLoadedAt = 0
const form = ref({ const form = ref({
name: '', name: '',
@@ -311,7 +313,7 @@ const form = ref({
}) })
const selectedGroup = computed(() => groups.value.find((group) => group.id === editingGroupId.value) ?? null) const selectedGroup = computed(() => groups.value.find((group) => group.id === editingGroupId.value) ?? null)
const userOptions = computed(() => props.users.map((user) => ({ const userOptions = computed(() => dialogUsers.value.map((user) => ({
label: `${user.username}${user.email ? ` (${user.email})` : ''}`, label: `${user.username}${user.email ? ` (${user.email})` : ''}`,
value: user.id, value: user.id,
}))) })))
@@ -334,8 +336,11 @@ function handleDialogUpdate(value: boolean): void {
async function loadDialogData(): Promise<void> { async function loadDialogData(): Promise<void> {
loading.value = true loading.value = true
try { try {
const response = await usersStore.listUserGroups() const [groupsResponse] = await Promise.all([
groups.value = response.items usersStore.listUserGroups(),
ensureDialogUsers(),
])
groups.value = groupsResponse.items
if (editingGroupId.value && !groups.value.some((group) => group.id === editingGroupId.value)) { if (editingGroupId.value && !groups.value.some((group) => group.id === editingGroupId.value)) {
editingGroupId.value = null editingGroupId.value = null
} }
@@ -354,6 +359,16 @@ async function loadDialogData(): Promise<void> {
} }
} }
async function ensureDialogUsers(): Promise<void> {
const now = Date.now()
if (dialogUsersLoadedAt > 0 && now - dialogUsersLoadedAt < USER_OPTIONS_CACHE_TTL_MS) {
return
}
dialogUsers.value = await usersStore.listAllUsers({ cacheTtlMs: USER_OPTIONS_CACHE_TTL_MS })
dialogUsersLoadedAt = Date.now()
}
async function selectGroup(groupId: string): Promise<void> { async function selectGroup(groupId: string): Promise<void> {
const group = groups.value.find((item) => item.id === groupId) const group = groups.value.find((item) => item.id === groupId)
if (!group) return if (!group) return

View File

@@ -3,6 +3,7 @@ import { ref } from 'vue'
import { import {
usersApi, usersApi,
type User, type User,
type GetAllUsersOptions,
type CreateUserRequest, type CreateUserRequest,
type UpdateUserRequest, type UpdateUserRequest,
type ApiKey, type ApiKey,
@@ -31,6 +32,7 @@ export const useUsersStore = defineStore('users', () => {
const hasMore = ref(false) const hasMore = ref(false)
const loading = ref(false) const loading = ref(false)
const error = ref<string | null>(null) const error = ref<string | null>(null)
let fetchUsersRequestId = 0
async function fetchUsers(options: { async function fetchUsers(options: {
cacheTtlMs?: number cacheTtlMs?: number
@@ -41,20 +43,44 @@ export const useUsersStore = defineStore('users', () => {
skip?: number skip?: number
limit?: number limit?: number
} = {}) { } = {}) {
const requestId = ++fetchUsersRequestId
loading.value = true loading.value = true
error.value = null error.value = null
try { try {
const response = await usersApi.getAllUsersPage(options) const response = await usersApi.getAllUsersPage(options)
if (requestId !== fetchUsersRequestId) return
users.value = response.items users.value = response.items
total.value = response.total total.value = response.total
skip.value = response.skip skip.value = response.skip
limit.value = response.limit limit.value = response.limit
hasMore.value = response.has_more hasMore.value = response.has_more
} catch (err: unknown) { } catch (err: unknown) {
if (requestId !== fetchUsersRequestId) return
error.value = parseApiError(err, '获取用户列表失败') error.value = parseApiError(err, '获取用户列表失败')
} finally { } finally {
loading.value = false if (requestId === fetchUsersRequestId) {
loading.value = false
}
}
}
async function listAllUsers(options: Omit<GetAllUsersOptions, 'skip' | 'limit'> = {}): Promise<User[]> {
const pageSize = 1000
const allUsers: User[] = []
let skip = 0
for (;;) {
const response = await usersApi.getAllUsersPage({
...options,
skip,
limit: pageSize,
})
allUsers.push(...response.items)
if (!response.has_more || response.items.length === 0) {
return allUsers
}
skip += response.items.length
} }
} }
@@ -315,6 +341,7 @@ export const useUsersStore = defineStore('users', () => {
loading, loading,
error, error,
fetchUsers, fetchUsers,
listAllUsers,
createUser, createUser,
updateUser, updateUser,
deleteUser, deleteUser,

View File

@@ -843,7 +843,6 @@
<UserGroupsDialog <UserGroupsDialog
:open="showUserGroupsDialog" :open="showUserGroupsDialog"
:users="usersStore.users"
@close="showUserGroupsDialog = false" @close="showUserGroupsDialog = false"
@changed="handleUserGroupsChanged" @changed="handleUserGroupsChanged"
/> />
@@ -1947,7 +1946,6 @@ async function handleUserFormSubmit(data: UserFormData & { password?: string; un
updateData.password = data.password updateData.password = data.password
} }
await usersStore.updateUser(data.id, updateData) await usersStore.updateUser(data.id, updateData)
await loadUserWallets()
success('用户信息已更新') success('用户信息已更新')
} else { } else {
// 创建用户 // 创建用户
@@ -1965,10 +1963,10 @@ async function handleUserFormSubmit(data: UserFormData & { password?: string; un
if (data.is_active === false && newUser) { if (data.is_active === false && newUser) {
await usersStore.updateUser(newUser.id, { is_active: false }) await usersStore.updateUser(newUser.id, { is_active: false })
} }
await loadUserWallets()
success('用户创建成功') success('用户创建成功')
} }
closeUserFormDialog() closeUserFormDialog()
await refreshUsers()
} catch (err: unknown) { } catch (err: unknown) {
const title = data.id ? '更新用户失败' : '创建用户失败' const title = data.id ? '更新用户失败' : '创建用户失败'
error(parseApiError(err, '未知错误'), title) error(parseApiError(err, '未知错误'), title)
@@ -2272,6 +2270,10 @@ async function deleteUser(user: User) {
try { try {
await usersStore.deleteUser(user.id) await usersStore.deleteUser(user.id)
if (usersStore.users.length === 0 && currentPage.value > 1) {
currentPage.value -= 1
}
await refreshUsers()
success('用户已删除') success('用户已删除')
} catch (err: unknown) { } catch (err: unknown) {
error(parseApiError(err, '未知错误'), '删除用户失败') error(parseApiError(err, '未知错误'), '删除用户失败')