- 更新UseAccountsReturn接口中handleBatchUpdate的类型签名,添加可选的targetIds参数 - 更新BatchOperationsProps接口中onBatchUpdate的类型定义以匹配实际函数签名 - 修复response.data可能为null的TypeScript严格检查错误 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			200 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			200 lines
		
	
	
		
			6.8 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'>>, targetIds?: number[]) => 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>
 | |
|     </>
 | |
|   );
 | |
| } |