feat: 实现账户批量导出功能和界面优化
- 新增批量导出功能,支持text模式导出账户数据 - 添加导出弹窗,支持文本全选和文件下载 - 移动刷新按钮到全局位置,统一刷新账户和统计数据 - 在统计卡片中将已导出状态计入可用账户 - 创建自定义确认对话框替换系统confirm弹窗 - 统一按钮尺寸,修复刷新和上传按钮大小不一致 - 添加已导出状态的中文映射和样式 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -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<Pick<Account, 'status' | 'ownerId' | 'notes' | 'platform'>>, targetIds?: number[]) => Promise<void>;
|
||||
onBatchDelete: () => Promise<void>;
|
||||
onRefresh?: () => Promise<void>;
|
||||
}
|
||||
|
||||
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<HTMLTextAreaElement>) => {
|
||||
const target = event.target as HTMLTextAreaElement;
|
||||
target.select();
|
||||
};
|
||||
|
||||
const handleBatchUpdate = async () => {
|
||||
const payload: Partial<Pick<Account, 'status' | 'ownerId' | 'notes' | 'platform'>> = {};
|
||||
if (updateData.status) payload.status = updateData.status;
|
||||
@@ -116,6 +169,15 @@ export function BatchOperations({ selectedCount, selectedAccounts, stats, onBatc
|
||||
<Edit3 className="h-4 w-4 mr-1" />
|
||||
批量更新
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={handleBatchExport}
|
||||
disabled={loading}
|
||||
>
|
||||
<Download className="h-4 w-4 mr-1" />
|
||||
导出
|
||||
</Button>
|
||||
<Button
|
||||
variant="destructive"
|
||||
size="sm"
|
||||
@@ -195,6 +257,43 @@ export function BatchOperations({ selectedCount, selectedAccounts, stats, onBatc
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
<Dialog open={exportDialog} onOpenChange={setExportDialog}>
|
||||
<DialogContent className="max-w-2xl">
|
||||
<DialogHeader>
|
||||
<DialogTitle>账户导出</DialogTitle>
|
||||
<DialogDescription>
|
||||
成功导出 {exportData.count} 个账户
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium">导出内容(点击文本框全选):</label>
|
||||
<Textarea
|
||||
value={exportData.text}
|
||||
readOnly
|
||||
onClick={selectAllText}
|
||||
className="min-h-[300px] font-mono text-sm"
|
||||
placeholder="导出的账户数据将显示在这里..."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setExportDialog(false)}
|
||||
>
|
||||
关闭
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleDownloadTxt}
|
||||
>
|
||||
<Download className="h-4 w-4 mr-1" />
|
||||
下载 .txt 文件
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user