feat: 功能迁移过来了,接下来优化样式

This commit is contained in:
许永平
2025-07-06 13:18:08 +08:00
parent ae6821d917
commit 2cdbd4dd73
14 changed files with 2288 additions and 127 deletions

View File

@@ -1,5 +1,354 @@
import React from 'react';
import React, { useState, useEffect } from 'react';
import { useNavigate, Link } from 'react-router-dom';
import { Plus, Filter, Search, RefreshCw, MoreVertical, Clock, Edit, Trash2, Eye, Copy } from 'lucide-react';
import { Card } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Badge } from '@/components/ui/badge';
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@/components/ui/dropdown-menu';
import { Switch } from '@/components/ui/switch';
import PageHeader from '@/components/PageHeader';
import { useToast } from '@/components/ui/toast';
interface SyncTask {
id: string;
name: string;
status: number; // 1-运行中0-暂停
deviceCount: number;
contentLib: string;
syncCount: number;
lastSyncTime: string;
createTime: string;
creator: string;
config: {
devices: string[];
contentLibraryNames: string[];
};
creatorName: string;
}
export default function MomentsSync() {
return <div></div>;
const navigate = useNavigate();
const { toast } = useToast();
const [tasks, setTasks] = useState<SyncTask[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [searchQuery, setSearchQuery] = useState('');
const [currentPage, setCurrentPage] = useState(1);
const [pageSize, setPageSize] = useState(10);
const [total, setTotal] = useState(0);
// 模拟数据
const mockTasks: SyncTask[] = [
{
id: '1',
name: '朋友圈同步任务1',
status: 1,
deviceCount: 3,
contentLib: '营销素材库',
syncCount: 156,
lastSyncTime: '2024-03-18 16:30:00',
createTime: '2024-03-15 10:00:00',
creator: 'admin',
creatorName: '管理员',
config: {
devices: ['device1', 'device2', 'device3'],
contentLibraryNames: ['营销素材库', '产品介绍库']
}
},
{
id: '2',
name: '朋友圈同步任务2',
status: 0,
deviceCount: 2,
contentLib: '产品介绍库',
syncCount: 89,
lastSyncTime: '2024-03-17 14:20:00',
createTime: '2024-03-14 15:30:00',
creator: 'user1',
creatorName: '用户1',
config: {
devices: ['device4', 'device5'],
contentLibraryNames: ['产品介绍库']
}
}
];
// 获取任务列表
const fetchTasks = async () => {
setIsLoading(true);
try {
// 模拟API调用
await new Promise(resolve => setTimeout(resolve, 1000));
// 模拟搜索过滤
let filteredTasks = mockTasks;
if (searchQuery) {
filteredTasks = mockTasks.filter(task =>
task.name.toLowerCase().includes(searchQuery.toLowerCase())
);
}
setTasks(filteredTasks);
setTotal(filteredTasks.length);
} catch (error: any) {
console.error('获取朋友圈同步任务列表失败:', error);
toast({
title: '获取失败',
description: error?.message || '请检查网络连接',
});
} finally {
setIsLoading(false);
}
};
// 组件加载时获取任务列表
useEffect(() => {
fetchTasks();
}, [currentPage, pageSize]);
// 处理页码变化
const handlePageChange = (page: number) => {
setCurrentPage(page);
};
// 处理每页条数变化
const handlePageSizeChange = (size: number) => {
setPageSize(size);
setCurrentPage(1); // 重置到第一页
};
// 搜索任务
const handleSearch = () => {
fetchTasks();
};
// 切换任务状态
const toggleTaskStatus = async (taskId: string, currentStatus: number) => {
try {
// 模拟API调用
await new Promise(resolve => setTimeout(resolve, 500));
const newStatus = currentStatus === 1 ? 0 : 1;
setTasks(
tasks.map((task) =>
task.id === taskId ? { ...task, status: newStatus } : task
)
);
toast({
title: '状态更新成功',
description: `任务已${newStatus === 1 ? '启用' : '暂停'}`,
});
} catch (error: any) {
console.error('更新任务状态失败:', error);
toast({
title: '更新失败',
description: error?.message || '更新任务状态失败',
});
}
};
// 执行删除
const handleDelete = async (taskId: string) => {
try {
// 模拟API调用
await new Promise(resolve => setTimeout(resolve, 500));
setTasks(tasks.filter((task) => task.id !== taskId));
toast({
title: '删除成功',
description: '已成功删除同步任务',
});
} catch (error: any) {
console.error('删除任务失败:', error);
toast({
title: '删除失败',
description: error?.message || '删除任务失败',
});
}
};
// 编辑任务
const handleEdit = (taskId: string) => {
navigate(`/workspace/moments-sync/${taskId}/edit`);
};
// 查看任务详情
const handleView = (taskId: string) => {
navigate(`/workspace/moments-sync/${taskId}`);
};
// 复制任务
const handleCopy = async (taskId: string) => {
try {
// 模拟API调用
await new Promise(resolve => setTimeout(resolve, 500));
const taskToCopy = tasks.find(task => task.id === taskId);
if (taskToCopy) {
const newTask = {
...taskToCopy,
id: Date.now().toString(),
name: `${taskToCopy.name} (副本)`,
status: 0,
createTime: new Date().toLocaleString(),
syncCount: 0,
lastSyncTime: '-'
};
setTasks([newTask, ...tasks]);
toast({
title: '复制成功',
description: '已成功复制同步任务',
});
}
} catch (error: any) {
console.error('复制任务失败:', error);
toast({
title: '复制失败',
description: error?.message || '复制任务失败',
});
}
};
// 过滤任务
const filteredTasks = tasks.filter(
(task) => task.name.toLowerCase().includes(searchQuery.toLowerCase())
);
return (
<div className="flex-1 bg-gray-50 min-h-screen pb-20">
<PageHeader
title="朋友圈同步"
defaultBackPath="/workspace"
rightContent={
<Link to="/workspace/moments-sync/new">
<Button>
<Plus className="h-4 w-4 mr-2" />
</Button>
</Link>
}
/>
<div className="p-4">
<Card className="p-4 mb-4">
<div className="flex items-center space-x-2">
<div className="relative flex-1">
<Search className="absolute left-3 top-2.5 h-4 w-4 text-gray-400" />
<Input
placeholder="搜索任务名称"
className="pl-9"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && handleSearch()}
/>
</div>
<Button variant="outline" size="icon" onClick={handleSearch}>
<Filter className="h-4 w-4" />
</Button>
<Button variant="outline" size="icon" onClick={fetchTasks} disabled={isLoading}>
<RefreshCw className={`h-4 w-4 ${isLoading ? 'animate-spin' : ''}`} />
</Button>
</div>
</Card>
<div className="space-y-4">
{isLoading ? (
<div className="text-center py-12 text-gray-400">...</div>
) : filteredTasks.length > 0 ? (
filteredTasks.map((task) => (
<Card key={task.id} className="p-4">
<div className="flex items-center justify-between mb-4">
<div className="flex items-center space-x-2">
<h3 className="font-medium">{task.name}</h3>
<Badge variant={task.status === 1 ? 'default' : 'secondary'}>
{task.status === 1 ? '运行中' : '已暂停'}
</Badge>
</div>
<div className="flex items-center space-x-2">
<Switch checked={task.status === 1} onCheckedChange={() => toggleTaskStatus(task.id, task.status)} />
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="sm" className="h-8 w-8 p-0">
<MoreVertical className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => handleView(task.id)}>
<Eye className="h-4 w-4 mr-2" />
</DropdownMenuItem>
<DropdownMenuItem onClick={() => handleEdit(task.id)}>
<Edit className="h-4 w-4 mr-2" />
</DropdownMenuItem>
<DropdownMenuItem onClick={() => handleCopy(task.id)}>
<Copy className="h-4 w-4 mr-2" />
</DropdownMenuItem>
<DropdownMenuItem onClick={() => handleDelete(task.id)}>
<Trash2 className="h-4 w-4 mr-2" />
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</div>
<div className="grid grid-cols-2 gap-4 mb-4">
<div className="text-sm text-gray-500">
<div className="mb-1">{task.deviceCount} </div>
<div className="mb-1">{task.contentLib}</div>
<div>{task.creatorName}</div>
</div>
<div className="text-sm text-gray-500">
<div className="mb-1">{task.syncCount} </div>
<div className="mb-1">{task.lastSyncTime}</div>
<div>{task.createTime}</div>
</div>
</div>
<div className="border-t pt-4">
<div className="flex items-center text-sm text-gray-500">
<Clock className="h-4 w-4 mr-2" />
<span>{task.config.devices.join(', ')}</span>
</div>
</div>
</Card>
))
) : (
<div className="text-center py-12 text-gray-400">
{searchQuery ? '没有找到匹配的任务' : '暂无同步任务'}
</div>
)}
</div>
{/* 分页 */}
{total > pageSize && (
<div className="flex justify-center mt-6 space-x-2">
<Button
variant="outline"
size="sm"
onClick={() => handlePageChange(Math.max(1, currentPage - 1))}
disabled={currentPage === 1 || isLoading}
>
</Button>
<div className="flex items-center space-x-1">
<span className="text-sm text-gray-500"> {currentPage} </span>
<span className="text-sm text-gray-500"> {Math.ceil(total / pageSize)} </span>
</div>
<Button
variant="outline"
size="sm"
onClick={() => handlePageChange(Math.min(Math.ceil(total / pageSize), currentPage + 1))}
disabled={currentPage >= Math.ceil(total / pageSize) || isLoading}
>
</Button>
</div>
)}
</div>
</div>
);
}