- Set up Next.js project structure - Added UI components and styling - Configured package dependencies - Added feature documentation 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
200 lines
6.7 KiB
TypeScript
200 lines
6.7 KiB
TypeScript
"use client";
|
|
|
|
import { useState } from 'react';
|
|
import { Button } from '@/components/ui/button';
|
|
import { Input } from '@/components/ui/input';
|
|
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/components/ui/dialog';
|
|
import { Edit3, Trash2 } from 'lucide-react';
|
|
import { Account, StatsOverview } from '@/lib/types';
|
|
import { toast } from 'sonner';
|
|
import { PlatformSelector, OwnerSelector, StatusSelector } from '@/components/shared';
|
|
|
|
interface BatchOperationsProps {
|
|
selectedCount: number;
|
|
selectedAccounts: Account[];
|
|
stats: StatsOverview | null;
|
|
onBatchUpdate: (payload: Partial<Pick<Account, 'status' | 'ownerId' | 'notes' | 'platform'>>) => Promise<void>;
|
|
onBatchDelete: () => Promise<void>;
|
|
}
|
|
|
|
export function BatchOperations({ selectedCount, selectedAccounts, stats, onBatchUpdate, onBatchDelete }: BatchOperationsProps) {
|
|
const [updateDialog, setUpdateDialog] = useState(false);
|
|
const [updateData, setUpdateData] = useState({
|
|
status: '',
|
|
platform: '',
|
|
ownerId: '',
|
|
notes: ''
|
|
});
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
// 计算选中账户的统计信息
|
|
const getSelectedStats = () => {
|
|
const platforms = new Set(selectedAccounts.map(account => account.platform));
|
|
const owners = new Set(selectedAccounts.map(account => account.ownerId));
|
|
|
|
return {
|
|
platformCount: platforms.size,
|
|
ownerCount: owners.size,
|
|
platforms: Array.from(platforms).slice(0, 3), // 只显示前3个
|
|
owners: Array.from(owners).slice(0, 3) // 只显示前3个
|
|
};
|
|
};
|
|
|
|
const selectedStats = getSelectedStats();
|
|
|
|
|
|
const handleBatchUpdate = async () => {
|
|
const payload: Partial<Pick<Account, 'status' | 'ownerId' | 'notes' | 'platform'>> = {};
|
|
if (updateData.status) payload.status = updateData.status;
|
|
if (updateData.platform) payload.platform = updateData.platform;
|
|
if (updateData.ownerId) payload.ownerId = updateData.ownerId;
|
|
if (updateData.notes) payload.notes = updateData.notes;
|
|
|
|
if (Object.keys(payload).length === 0) {
|
|
toast.warning('请至少选择一个字段进行更新');
|
|
return;
|
|
}
|
|
|
|
setLoading(true);
|
|
try {
|
|
await onBatchUpdate(payload);
|
|
setUpdateDialog(false);
|
|
setUpdateData({ status: '', platform: '', ownerId: '', notes: '' });
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const handleBatchDelete = async () => {
|
|
setLoading(true);
|
|
try {
|
|
await onBatchDelete();
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
if (selectedCount === 0) {
|
|
return null;
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<div className="flex items-center space-x-2 p-3 bg-muted rounded-lg">
|
|
<div className="flex-1">
|
|
<div className="flex items-center space-x-4">
|
|
<span className="text-sm font-medium">已选择 {selectedCount} 个账户</span>
|
|
<div className="flex items-center space-x-3 text-xs text-muted-foreground">
|
|
<span>
|
|
{selectedStats.ownerCount} 个用户
|
|
{selectedStats.ownerCount > 0 && (
|
|
<span className="ml-1">
|
|
({selectedStats.owners.join(', ')}
|
|
{selectedStats.ownerCount > 3 && ` 等${selectedStats.ownerCount}个`})
|
|
</span>
|
|
)}
|
|
</span>
|
|
<span>•</span>
|
|
<span>
|
|
{selectedStats.platformCount} 个平台
|
|
{selectedStats.platformCount > 0 && (
|
|
<span className="ml-1">
|
|
({selectedStats.platforms.join(', ')}
|
|
{selectedStats.platformCount > 3 && ` 等${selectedStats.platformCount}个`})
|
|
</span>
|
|
)}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={() => setUpdateDialog(true)}
|
|
disabled={loading}
|
|
>
|
|
<Edit3 className="h-4 w-4 mr-1" />
|
|
批量更新
|
|
</Button>
|
|
<Button
|
|
variant="destructive"
|
|
size="sm"
|
|
onClick={handleBatchDelete}
|
|
disabled={loading}
|
|
>
|
|
<Trash2 className="h-4 w-4 mr-1" />
|
|
批量删除
|
|
</Button>
|
|
</div>
|
|
|
|
<Dialog open={updateDialog} onOpenChange={setUpdateDialog}>
|
|
<DialogContent>
|
|
<DialogHeader>
|
|
<DialogTitle>批量更新账户</DialogTitle>
|
|
<DialogDescription>
|
|
将对 {selectedCount} 个账户进行批量更新操作
|
|
</DialogDescription>
|
|
</DialogHeader>
|
|
<div className="grid grid-cols-1 gap-6">
|
|
<div className="space-y-3">
|
|
<label className="text-sm font-medium">平台</label>
|
|
<PlatformSelector
|
|
value={updateData.platform}
|
|
onValueChange={(value) => setUpdateData({...updateData, platform: value})}
|
|
stats={stats}
|
|
showNoneOption={true}
|
|
placeholder="选择新平台(可选)"
|
|
/>
|
|
</div>
|
|
|
|
<div className="space-y-3">
|
|
<label className="text-sm font-medium">状态</label>
|
|
<StatusSelector
|
|
value={updateData.status}
|
|
onValueChange={(value) => setUpdateData({...updateData, status: value})}
|
|
stats={stats}
|
|
showNoneOption={true}
|
|
placeholder="选择新状态(可选)"
|
|
/>
|
|
</div>
|
|
|
|
<div className="space-y-3">
|
|
<label className="text-sm font-medium">所有者ID</label>
|
|
<OwnerSelector
|
|
value={updateData.ownerId}
|
|
onValueChange={(value) => setUpdateData({...updateData, ownerId: value})}
|
|
stats={stats}
|
|
showNoneOption={true}
|
|
placeholder="选择所有者(可选)"
|
|
/>
|
|
</div>
|
|
|
|
<div className="space-y-3">
|
|
<label className="text-sm font-medium">备注</label>
|
|
<Input
|
|
placeholder="输入新的备注(可选)"
|
|
value={updateData.notes}
|
|
onChange={(e) => setUpdateData({...updateData, notes: e.target.value})}
|
|
/>
|
|
</div>
|
|
</div>
|
|
<DialogFooter>
|
|
<Button
|
|
variant="outline"
|
|
onClick={() => setUpdateDialog(false)}
|
|
disabled={loading}
|
|
>
|
|
取消
|
|
</Button>
|
|
<Button
|
|
onClick={handleBatchUpdate}
|
|
disabled={loading}
|
|
>
|
|
{loading ? '更新中...' : '确认更新'}
|
|
</Button>
|
|
</DialogFooter>
|
|
</DialogContent>
|
|
</Dialog>
|
|
</>
|
|
);
|
|
} |