feat(admin): 支持按筛选结果全选账号

This commit is contained in:
Litong
2026-07-30 18:07:27 +08:00
parent a35ff9613e
commit 2a871ec852
9 changed files with 533 additions and 18 deletions
@@ -1,26 +1,45 @@
<template>
<div class="mb-4 flex items-center justify-between rounded-lg bg-primary-50 p-3 dark:bg-primary-900/20">
<div class="flex flex-wrap items-center gap-2">
<span v-if="selectedIds.length > 0" class="text-sm font-medium text-primary-900 dark:text-primary-100">
<span v-if="allResultsSelected" class="text-sm font-medium text-primary-900 dark:text-primary-100">
{{ t('admin.accounts.bulkActions.selectedAll', { count: selectedIds.length }) }}
</span>
<span v-else-if="selectedIds.length > 0" class="text-sm font-medium text-primary-900 dark:text-primary-100">
{{ t('admin.accounts.bulkActions.selected', { count: selectedIds.length }) }}
</span>
<span v-else class="text-sm font-medium text-primary-900 dark:text-primary-100">
{{ t('admin.accounts.bulkEdit.title') }}
</span>
<template v-if="selectedIds.length > 0">
<button
@click="$emit('select-page')"
class="text-xs font-medium text-primary-700 hover:text-primary-800 dark:text-primary-300 dark:hover:text-primary-200"
>
{{ t('admin.accounts.bulkActions.selectCurrentPage') }}
</button>
<span class="text-gray-300 dark:text-primary-800">•</span>
<button
@click="$emit('clear')"
class="text-xs font-medium text-primary-700 hover:text-primary-800 dark:text-primary-300 dark:hover:text-primary-200"
>
{{ t('admin.accounts.bulkActions.clear') }}
</button>
<button
@click="$emit('select-page')"
class="text-xs font-medium text-primary-700 hover:text-primary-800 dark:text-primary-300 dark:hover:text-primary-200"
>
{{ t('admin.accounts.bulkActions.selectCurrentPage') }}
</button>
</template>
<template v-if="!allResultsSelected && totalResults > selectedIds.length">
<span v-if="selectedIds.length > 0" class="text-gray-300 dark:text-primary-800">•</span>
<button
:disabled="selectingAll"
@click="$emit('select-all-results')"
class="text-xs font-medium text-primary-700 hover:text-primary-800 disabled:cursor-not-allowed disabled:opacity-60 dark:text-primary-300 dark:hover:text-primary-200"
>
{{
selectingAll
? t('admin.accounts.bulkActions.selectingAll')
: t('admin.accounts.bulkActions.selectAllResults', { count: totalResults })
}}
</button>
</template>
<template v-if="selectedIds.length > 0">
<span class="text-gray-300 dark:text-primary-800">•</span>
<button
@click="$emit('clear')"
class="text-xs font-medium text-primary-700 hover:text-primary-800 dark:text-primary-300 dark:hover:text-primary-200"
>
{{ t('admin.accounts.bulkActions.clear') }}
</button>
</template>
</div>
<div class="flex gap-2">
@@ -43,13 +62,20 @@
<script setup lang="ts">
import { useI18n } from 'vue-i18n'
defineProps<{ selectedIds: number[] }>()
defineProps<{
selectedIds: number[]
totalResults: number
selectingAll: boolean
allResultsSelected: boolean
}>()
defineEmits([
'delete',
'edit-selected',
'edit-filtered',
'clear',
'select-page',
'select-all-results',
'toggle-schedulable',
'reset-status',
'refresh-token',
@@ -0,0 +1,50 @@
import { describe, expect, it, vi } from 'vitest'
import { mount } from '@vue/test-utils'
import AccountBulkActionsBar from '../AccountBulkActionsBar.vue'
vi.mock('vue-i18n', () => ({
useI18n: () => ({
t: (key: string) => key
})
}))
describe('AccountBulkActionsBar', () => {
it('allows selecting all results before any row is selected', async () => {
const wrapper = mount(AccountBulkActionsBar, {
props: {
selectedIds: [],
totalResults: 45,
selectingAll: false,
allResultsSelected: false
}
})
const button = wrapper.findAll('button').find(item =>
item.text().includes('admin.accounts.bulkActions.selectAllResults')
)
expect(button).toBeDefined()
await button!.trigger('click')
expect(wrapper.emitted('select-all-results')).toHaveLength(1)
})
it('preserves the upstream billing probe action from v0.1.166', async () => {
const wrapper = mount(AccountBulkActionsBar, {
props: {
selectedIds: [1],
totalResults: 45,
selectingAll: false,
allResultsSelected: false
}
})
const button = wrapper.findAll('button').find(item =>
item.text().includes('admin.accounts.bulkActions.probeUpstreamBilling')
)
expect(button).toBeDefined()
await button!.trigger('click')
expect(wrapper.emitted('probe-upstream-billing')).toHaveLength(1)
})
})