【前端】简易化看板
项目简介
看板分为三个模块,分别是待办,正在做,已做完三个部分。每个事件采取"卡片"式设计,支持任务间拖拽,删除等操作。
代码
import React, { useState } from 'react';
import { Card, CardContent } from '@/components/ui/card';
import { Input } from '@/components/ui/input';
import { Button } from '@/components/ui/button';
const KanbanBoard = () => {
const [tasks, setTasks] = useState({
todo: [
{ id: '1', title: '设计新的登录页面' },
{ id: '2', title: '优化数据库查询' }
],
doing: [
{ id: '3', title: '实现用户认证功能' },
{ id: '4', title: '编写单元测试' }
],
done: [
{ id: '5', title: '更新API文档' },
{ id: '6', title: '修复登录bug' }
]
});
const [newTasks, setNewTasks] = useState({
todo: '',
doing: '',
done: ''
});
const onDragStart = (e, id, sourceColumn) => {
e.dataTransfer.setData('text/plain', JSON.stringify({ id, sourceColumn }));
};
const onDragOver = (e) => {
e.preventDefault();
};
const onDrop = (e, targetColumn) => {
e.preventDefault();
const { id, sourceColumn } = JSON.parse(e.dataTransfer.getData('text'));
if (sourceColumn === targetColumn) return;
const updatedTasks = { ...tasks };
const taskToMove = updatedTasks[sourceColumn].find(task => task.id === id);
updatedTasks[sourceColumn] = updatedTasks[sourceColumn].filter(task => task.id !== id);
updatedTasks[targetColumn].push(taskToMove);
setTasks(updatedTasks);
};
const addTask = (column) => {
if (newTasks[column].trim()) {
const updatedTasks = { ...tasks };
updatedTasks[column] = [...tasks[column], {
id: Date.now().toString(),
title: newTasks[column]
}];
setTasks(updatedTasks);
setNewTasks({...newTasks, [column]: ''});
}
};
const deleteTask = (columnId, taskId) => {
const updatedTasks = { ...tasks };
updatedTasks[columnId] = tasks[columnId].filter(task => task.id !== taskId);
setTasks(updatedTasks);
};
const renderTask = (task, columnId) => (
<Card
key={task.id}
draggable
onDragStart={(e) => onDragStart(e, task.id, columnId)}
className="mb-2 shadow-sm hover:shadow-md transition-shadow duration-200"
>
<CardContent className="p-3 flex justify-between items-center">
<p className="text-sm font-medium text-gray-700 truncate flex-grow mr-2">{task.title}</p>
<Button
onClick={() => deleteTask(columnId, task.id)}
className="text-gray-400 hover:text-red-500 transition-colors duration-200 flex-shrink-0"
variant="ghost"
size="sm"
>
×
</Button>
</CardContent>
</Card>
);
const renderColumn = (title, columnId) => (
<div
className="flex-1 min-w-[250px] bg-gray-50 rounded-lg p-4 border border-gray-200"
onDragOver={onDragOver}
onDrop={(e) => onDrop(e, columnId)}
>
<h2 className="text-lg font-semibold mb-4 text-gray-700">{title}</h2>
<div className="space-y-2">
{tasks[columnId].map(task => renderTask(task, columnId))}
</div>
<div className="mt-4">
<Input
type="text"
value={newTasks[columnId]}
onChange={(e) => setNewTasks({...newTasks, [columnId]: e.target.value})}
placeholder={`添加任务到 ${title}`}
className="mb-2"
/>
<Button onClick={() => addTask(columnId)} className="w-full bg-blue-500 hover:bg-blue-600 text-white">
添加任务
</Button>
</div>
</div>
);
return (
<div className="container mx-auto p-6 bg-white">
<header className="text-center mb-8">
<h1 className="text-3xl font-bold text-gray-800">SeeBoard</h1>
</header>
<div className="flex flex-wrap gap-6">
{renderColumn('待办', 'todo')}
{renderColumn('进行中', 'doing')}
{renderColumn('已完成', 'done')}
</div>
</div>
);
};
export default KanbanBoard;
效果图