mirror of
https://gitee.com/wanwujie/sub2api
synced 2026-04-03 06:52:13 +08:00
feat: 支持批量重置状态和批量刷新令牌
- 提取 refreshSingleAccount 私有方法复用单账号刷新逻辑 - 新增 BatchClearError handler (POST /admin/accounts/batch-clear-error) - 新增 BatchRefresh handler (POST /admin/accounts/batch-refresh) - 前端 AccountBulkActionsBar 添加批量重置状态/刷新令牌按钮 - AccountsView 添加 handler 支持 partial success 反馈 - i18n 中英文补充批量操作相关翻译
This commit is contained in:
@@ -581,6 +581,43 @@ export async function validateSoraSessionToken(
|
||||
return data
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch operation result type
|
||||
*/
|
||||
export interface BatchOperationResult {
|
||||
total: number
|
||||
success: number
|
||||
failed: number
|
||||
errors?: Array<{ account_id: number; error: string }>
|
||||
warnings?: Array<{ account_id: number; warning: string }>
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch clear account errors
|
||||
* @param accountIds - Array of account IDs
|
||||
* @returns Batch operation result
|
||||
*/
|
||||
export async function batchClearError(accountIds: number[]): Promise<BatchOperationResult> {
|
||||
const { data } = await apiClient.post<BatchOperationResult>('/admin/accounts/batch-clear-error', {
|
||||
account_ids: accountIds
|
||||
})
|
||||
return data
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch refresh account credentials
|
||||
* @param accountIds - Array of account IDs
|
||||
* @returns Batch operation result
|
||||
*/
|
||||
export async function batchRefresh(accountIds: number[]): Promise<BatchOperationResult> {
|
||||
const { data } = await apiClient.post<BatchOperationResult>('/admin/accounts/batch-refresh', {
|
||||
account_ids: accountIds,
|
||||
}, {
|
||||
timeout: 120000 // 120s timeout for large batch refreshes
|
||||
})
|
||||
return data
|
||||
}
|
||||
|
||||
export const accountsAPI = {
|
||||
list,
|
||||
listWithEtag,
|
||||
@@ -615,7 +652,9 @@ export const accountsAPI = {
|
||||
syncFromCrs,
|
||||
exportData,
|
||||
importData,
|
||||
getAntigravityDefaultModelMapping
|
||||
getAntigravityDefaultModelMapping,
|
||||
batchClearError,
|
||||
batchRefresh
|
||||
}
|
||||
|
||||
export default accountsAPI
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<button @click="$emit('delete')" class="btn btn-danger btn-sm">{{ t('admin.accounts.bulkActions.delete') }}</button>
|
||||
<button @click="$emit('reset-status')" class="btn btn-secondary btn-sm">{{ t('admin.accounts.bulkActions.resetStatus') }}</button>
|
||||
<button @click="$emit('refresh-token')" class="btn btn-secondary btn-sm">{{ t('admin.accounts.bulkActions.refreshToken') }}</button>
|
||||
<button @click="$emit('toggle-schedulable', true)" class="btn btn-success btn-sm">{{ t('admin.accounts.bulkActions.enableScheduling') }}</button>
|
||||
<button @click="$emit('toggle-schedulable', false)" class="btn btn-warning btn-sm">{{ t('admin.accounts.bulkActions.disableScheduling') }}</button>
|
||||
<button @click="$emit('edit')" class="btn btn-primary btn-sm">{{ t('admin.accounts.bulkActions.edit') }}</button>
|
||||
@@ -29,5 +31,5 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useI18n } from 'vue-i18n'
|
||||
defineProps(['selectedIds']); defineEmits(['delete', 'edit', 'clear', 'select-page', 'toggle-schedulable']); const { t } = useI18n()
|
||||
</script>
|
||||
defineProps(['selectedIds']); defineEmits(['delete', 'edit', 'clear', 'select-page', 'toggle-schedulable', 'reset-status', 'refresh-token']); const { t } = useI18n()
|
||||
</script>
|
||||
|
||||
@@ -1836,7 +1836,12 @@ export default {
|
||||
edit: 'Bulk Edit',
|
||||
delete: 'Bulk Delete',
|
||||
enableScheduling: 'Enable Scheduling',
|
||||
disableScheduling: 'Disable Scheduling'
|
||||
disableScheduling: 'Disable Scheduling',
|
||||
resetStatus: 'Reset Status',
|
||||
refreshToken: 'Refresh Token',
|
||||
resetStatusSuccess: 'Successfully reset {count} account(s) status',
|
||||
refreshTokenSuccess: 'Successfully refreshed {count} account(s) token',
|
||||
partialSuccess: 'Partially completed: {success} succeeded, {failed} failed'
|
||||
},
|
||||
bulkEdit: {
|
||||
title: 'Bulk Edit Accounts',
|
||||
|
||||
@@ -1983,7 +1983,12 @@ export default {
|
||||
edit: '批量编辑账号',
|
||||
delete: '批量删除',
|
||||
enableScheduling: '批量启用调度',
|
||||
disableScheduling: '批量停止调度'
|
||||
disableScheduling: '批量停止调度',
|
||||
resetStatus: '批量重置状态',
|
||||
refreshToken: '批量刷新令牌',
|
||||
resetStatusSuccess: '已成功重置 {count} 个账号状态',
|
||||
refreshTokenSuccess: '已成功刷新 {count} 个账号令牌',
|
||||
partialSuccess: '操作部分完成:{success} 成功,{failed} 失败'
|
||||
},
|
||||
bulkEdit: {
|
||||
title: '批量编辑账号',
|
||||
|
||||
@@ -131,7 +131,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<template #table>
|
||||
<AccountBulkActionsBar :selected-ids="selIds" @delete="handleBulkDelete" @edit="showBulkEdit = true" @clear="clearSelection" @select-page="selectPage" @toggle-schedulable="handleBulkToggleSchedulable" />
|
||||
<AccountBulkActionsBar :selected-ids="selIds" @delete="handleBulkDelete" @reset-status="handleBulkResetStatus" @refresh-token="handleBulkRefreshToken" @edit="showBulkEdit = true" @clear="clearSelection" @select-page="selectPage" @toggle-schedulable="handleBulkToggleSchedulable" />
|
||||
<div ref="accountTableRef" class="flex min-h-0 flex-1 flex-col overflow-hidden">
|
||||
<DataTable
|
||||
:columns="cols"
|
||||
@@ -889,6 +889,38 @@ const toggleSelectAllVisible = (event: Event) => {
|
||||
toggleVisible(target.checked)
|
||||
}
|
||||
const handleBulkDelete = async () => { if(!confirm(t('common.confirm'))) return; try { await Promise.all(selIds.value.map(id => adminAPI.accounts.delete(id))); clearSelection(); reload() } catch (error) { console.error('Failed to bulk delete accounts:', error) } }
|
||||
const handleBulkResetStatus = async () => {
|
||||
if (!confirm(t('common.confirm'))) return
|
||||
try {
|
||||
const result = await adminAPI.accounts.batchClearError(selIds.value)
|
||||
if (result.failed > 0) {
|
||||
appStore.showError(t('admin.accounts.bulkActions.partialSuccess', { success: result.success, failed: result.failed }))
|
||||
} else {
|
||||
appStore.showSuccess(t('admin.accounts.bulkActions.resetStatusSuccess', { count: result.success }))
|
||||
clearSelection()
|
||||
}
|
||||
reload()
|
||||
} catch (error) {
|
||||
console.error('Failed to bulk reset status:', error)
|
||||
appStore.showError(String(error))
|
||||
}
|
||||
}
|
||||
const handleBulkRefreshToken = async () => {
|
||||
if (!confirm(t('common.confirm'))) return
|
||||
try {
|
||||
const result = await adminAPI.accounts.batchRefresh(selIds.value)
|
||||
if (result.failed > 0) {
|
||||
appStore.showError(t('admin.accounts.bulkActions.partialSuccess', { success: result.success, failed: result.failed }))
|
||||
} else {
|
||||
appStore.showSuccess(t('admin.accounts.bulkActions.refreshTokenSuccess', { count: result.success }))
|
||||
clearSelection()
|
||||
}
|
||||
reload()
|
||||
} catch (error) {
|
||||
console.error('Failed to bulk refresh token:', error)
|
||||
appStore.showError(String(error))
|
||||
}
|
||||
}
|
||||
const updateSchedulableInList = (accountIds: number[], schedulable: boolean) => {
|
||||
if (accountIds.length === 0) return
|
||||
const idSet = new Set(accountIds)
|
||||
|
||||
Reference in New Issue
Block a user