diff --git a/components/accounts-manager.tsx b/components/accounts-manager.tsx index 4cafab4..11cafb1 100644 --- a/components/accounts-manager.tsx +++ b/components/accounts-manager.tsx @@ -2,8 +2,11 @@ import { useState } from 'react'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; +import { Button } from '@/components/ui/button'; +import { RefreshCw } from 'lucide-react'; import { Account } from '@/lib/types'; import { useAccounts } from '@/lib/hooks/use-accounts'; +import { useConfirmDialog } from '@/components/ui/confirm-dialog'; // 组件导入 import { StatsCards } from './accounts/stats-cards'; @@ -14,6 +17,8 @@ import { AccountTable } from './accounts/account-table'; import { AccountDetails } from './accounts/account-details'; export function AccountsManager() { + const { showConfirm, ConfirmDialogComponent } = useConfirmDialog(); + const { accounts, stats, @@ -34,6 +39,11 @@ export function AccountsManager() { setSelectedIds } = useAccounts(); + // 全局刷新函数 + const handleGlobalRefresh = async () => { + await Promise.all([fetchAccounts(), fetchStats()]); + }; + // 账户详情状态 const [detailsDialog, setDetailsDialog] = useState(false); const [detailsMode, setDetailsMode] = useState<'view' | 'edit'>('view'); @@ -55,16 +65,23 @@ export function AccountsManager() { // 处理删除单个账户 const handleDeleteAccount = async (account: Account) => { - if (!confirm('确认删除此账户?')) return; - - try { - // 设置选中的账户ID - setSelectedIds([account.id]); - // 执行删除 - await handleBatchDelete(); - } catch (error) { - console.error('Failed to delete account:', error); - } + showConfirm({ + title: '确认删除', + description: `确认删除账户 "${account.customId}"?此操作不可撤销。`, + confirmText: '确认删除', + cancelText: '取消', + variant: 'destructive', + onConfirm: async () => { + try { + // 设置选中的账户ID + setSelectedIds([account.id]); + // 执行删除 + await handleBatchDelete(); + } catch (error) { + console.error('Failed to delete account:', error); + } + } + }); }; // 处理保存编辑 @@ -114,7 +131,18 @@ export function AccountsManager() {

账户管理系统

轻量化账户管理平台

- +
+ + +
{/* 统计卡片 */} @@ -142,7 +170,8 @@ export function AccountsManager() { selectedAccounts={accounts.filter(account => selectedIds.includes(account.id))} stats={stats} onBatchUpdate={handleBatchUpdate} - onBatchDelete={handleBatchDelete} + onBatchDelete={() => handleBatchDelete(showConfirm)} + onRefresh={handleGlobalRefresh} /> {/* 账户表格 - 移除高度限制 */} @@ -156,7 +185,6 @@ export function AccountsManager() { onSelectOne={handleSelectOne} onPageChange={handlePageChange} onPageSizeChange={handlePageSizeChange} - onRefresh={fetchAccounts} onView={handleViewAccount} onEdit={handleEditAccount} onDelete={handleDeleteAccount} @@ -176,6 +204,9 @@ export function AccountsManager() { onSave={handleSaveAccount} onDelete={handleDeleteAccount} /> + + {/* 确认删除对话框 */} + ); } \ No newline at end of file diff --git a/components/accounts/account-table.tsx b/components/accounts/account-table.tsx index 70f072e..45a86f3 100644 --- a/components/accounts/account-table.tsx +++ b/components/accounts/account-table.tsx @@ -5,7 +5,7 @@ import { Button } from '@/components/ui/button'; import { Checkbox } from '@/components/ui/checkbox'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; -import { ChevronLeft, ChevronRight, Eye, Edit, Trash2, RefreshCw } from 'lucide-react'; +import { ChevronLeft, ChevronRight, Eye, Edit, Trash2 } from 'lucide-react'; import { Account } from '@/lib/types'; interface AccountTableProps { @@ -22,7 +22,6 @@ interface AccountTableProps { onSelectOne: (id: number, checked: boolean) => void; onPageChange: (page: number) => void; onPageSizeChange: (pageSize: number) => void; - onRefresh: () => void; onView?: (account: Account) => void; onEdit?: (account: Account) => void; onDelete?: (account: Account) => void; @@ -37,7 +36,6 @@ export function AccountTable({ onSelectOne, onPageChange, onPageSizeChange, - onRefresh, onView, onEdit, onDelete @@ -50,6 +48,8 @@ export function AccountTable({ return 已锁定; case 'banned': return 已封禁; + case 'exported': + return 已导出; default: return {status}; } @@ -152,20 +152,7 @@ export function AccountTable({ 状态 备注 创建时间 - -
- 操作 - -
- + 操作 diff --git a/components/accounts/account-upload.tsx b/components/accounts/account-upload.tsx index 7caed37..6745cb4 100644 --- a/components/accounts/account-upload.tsx +++ b/components/accounts/account-upload.tsx @@ -165,7 +165,7 @@ export function AccountUpload({ onUpload, stats }: AccountUploadProps) { return ( - diff --git a/components/accounts/batch-operations.tsx b/components/accounts/batch-operations.tsx index 3adbc9d..3190df6 100644 --- a/components/accounts/batch-operations.tsx +++ b/components/accounts/batch-operations.tsx @@ -3,11 +3,13 @@ import { useState } from 'react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; +import { Textarea } from '@/components/ui/textarea'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog'; -import { Edit3, Trash2 } from 'lucide-react'; +import { Edit3, Trash2, Download } from 'lucide-react'; import { Account, StatsOverview } from '@/lib/types'; import { toast } from 'sonner'; import { PlatformSelector, OwnerSelector, StatusSelector } from '@/components/shared'; +import { apiClient } from '@/lib/api'; interface BatchOperationsProps { selectedCount: number; @@ -15,10 +17,13 @@ interface BatchOperationsProps { stats: StatsOverview | null; onBatchUpdate: (payload: Partial>, targetIds?: number[]) => Promise; onBatchDelete: () => Promise; + onRefresh?: () => Promise; } -export function BatchOperations({ selectedCount, selectedAccounts, stats, onBatchUpdate, onBatchDelete }: BatchOperationsProps) { +export function BatchOperations({ selectedCount, selectedAccounts, stats, onBatchUpdate, onBatchDelete, onRefresh }: BatchOperationsProps) { const [updateDialog, setUpdateDialog] = useState(false); + const [exportDialog, setExportDialog] = useState(false); + const [exportData, setExportData] = useState({ count: 0, text: '' }); const [updateData, setUpdateData] = useState({ status: '', platform: '', @@ -43,6 +48,54 @@ export function BatchOperations({ selectedCount, selectedAccounts, stats, onBatc const selectedStats = getSelectedStats(); + const handleBatchExport = async () => { + setLoading(true); + try { + const response = await apiClient.batchExportAccounts({ + ids: selectedAccounts.map(account => account.id), + mode: 'text' + }); + + if (response.code === 0 && response.data) { + setExportData({ + count: response.data.exportedCount, + text: response.data.data + }); + setExportDialog(true); + toast.success(`成功导出 ${response.data.exportedCount} 个账户`); + + // 导出后刷新数据 + if (onRefresh) { + await onRefresh(); + } + } else { + toast.error(response.message || '导出失败'); + } + } catch (error) { + console.error('Export error:', error); + toast.error('导出失败,请稍后重试'); + } finally { + setLoading(false); + } + }; + + const handleDownloadTxt = () => { + const blob = new Blob([exportData.text], { type: 'text/plain;charset=utf-8' }); + const url = URL.createObjectURL(blob); + const link = document.createElement('a'); + link.href = url; + link.download = `accounts_export_${new Date().toISOString().slice(0, 19).replace(/:/g, '-')}.txt`; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + URL.revokeObjectURL(url); + }; + + const selectAllText = (event: React.MouseEvent) => { + const target = event.target as HTMLTextAreaElement; + target.select(); + }; + const handleBatchUpdate = async () => { const payload: Partial> = {}; if (updateData.status) payload.status = updateData.status; @@ -116,6 +169,15 @@ export function BatchOperations({ selectedCount, selectedAccounts, stats, onBatc 批量更新 + + + + + + 账户导出 + + 成功导出 {exportData.count} 个账户 + + +
+
+ +