From 728b3e45f4e88fae9255899a74dd6389d97a7b9f Mon Sep 17 00:00:00 2001 From: keep <1603421097@qq.com> Date: Thu, 16 Apr 2026 18:43:13 +0800 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E4=BC=98=E5=85=88=E7=BA=A7=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在前后端添加优先级字段(高/中/低) - 更新任务表单和列表显示优先级 - 添加优先级筛选和排序功能 --- .../task_app/migrations/0004_task_priority.py | 22 +++++++ todo_app_backend/task_app/models.py | 6 ++ todo_app_frontend/src/App.tsx | 60 +++++++++++++++---- todo_app_frontend/src/components/TaskForm.tsx | 21 ++++++- todo_app_frontend/src/components/TaskItem.tsx | 37 +++++++++++- todo_app_frontend/src/types/Task.ts | 3 + 6 files changed, 133 insertions(+), 16 deletions(-) create mode 100644 todo_app_backend/task_app/migrations/0004_task_priority.py diff --git a/todo_app_backend/task_app/migrations/0004_task_priority.py b/todo_app_backend/task_app/migrations/0004_task_priority.py new file mode 100644 index 0000000..0619479 --- /dev/null +++ b/todo_app_backend/task_app/migrations/0004_task_priority.py @@ -0,0 +1,22 @@ +# Generated by Django 5.2.8 on 2026-04-16 10:37 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("task_app", "0003_alter_task_status"), + ] + + operations = [ + migrations.AddField( + model_name="task", + name="priority", + field=models.CharField( + choices=[("High", "High"), ("Medium", "Medium"), ("Low", "Low")], + default="Medium", + max_length=20, + ), + ), + ] diff --git a/todo_app_backend/task_app/models.py b/todo_app_backend/task_app/models.py index dff2f03..a7cc483 100644 --- a/todo_app_backend/task_app/models.py +++ b/todo_app_backend/task_app/models.py @@ -6,8 +6,14 @@ class Task(models.Model): ('In-Progress','In-progress'), ('Completed','Completed') ) + PRIORITY = ( + ('High','High'), + ('Medium','Medium'), + ('Low','Low') + ) task_name = models.CharField(max_length=100) description = models.TextField() status = models.CharField(max_length=20, choices=STATUS, default='In-Progress') + priority = models.CharField(max_length=20, choices=PRIORITY, default='Medium') created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) \ No newline at end of file diff --git a/todo_app_frontend/src/App.tsx b/todo_app_frontend/src/App.tsx index c38f864..5b32d89 100644 --- a/todo_app_frontend/src/App.tsx +++ b/todo_app_frontend/src/App.tsx @@ -7,16 +7,19 @@ function App() { const [error, setError] = useState(null); const [searchTerm, setSearchTerm] = useState(''); const [filterStatus, setFilterStatus] = useState('All'); + const [filterPriority, setFilterPriority] = useState('All'); + const [sortBy, setSortBy] = useState('None'); const [showAddForm, setShowAddForm] = useState(false); const [editingTask, setEditingTask] = useState(null); const [newTask, setNewTask] = useState({ task_name: '', - description: '' + description: '', + priority: 'Medium' }); // API Base URL - Update this to your Django backend URL - // const API_BASE_URL = 'http://localhost:8000/api'; // Change this to your backend URL - const API_BASE_URL = 'https://todo-app-fullstack-nrfk.onrender.com/api'; + const API_BASE_URL = 'http://localhost:8000/api'; // Change this to your backend URL + // const API_BASE_URL = 'https://todo-app-fullstack-nrfk.onrender.com/api'; // Fetch tasks from API const fetchTasks = async () => { @@ -122,10 +125,11 @@ function App() { } }; - const getPriorityColor = (status) => { - switch(status) { - case 'In-Progress': return 'bg-orange-100 text-orange-800 border-orange-200'; - case 'Completed': return 'bg-green-100 text-green-800 border-green-200'; + const getPriorityColor = (priority) => { + switch(priority) { + case 'High': return 'bg-red-100 text-red-800 border-red-200'; + case 'Medium': return 'bg-yellow-100 text-yellow-800 border-yellow-200'; + case 'Low': return 'bg-green-100 text-green-800 border-green-200'; default: return 'bg-gray-100 text-gray-800 border-gray-200'; } }; @@ -142,7 +146,16 @@ function App() { const matchesSearch = task.task_name.toLowerCase().includes(searchTerm.toLowerCase()) || task.description.toLowerCase().includes(searchTerm.toLowerCase()); const matchesStatus = filterStatus === 'All' || task.status === filterStatus; - return matchesSearch && matchesStatus; + const matchesPriority = filterPriority === 'All' || task.priority === filterPriority; + return matchesSearch && matchesStatus && matchesPriority; + }); + + const sortedTasks = [...filteredTasks].sort((a, b) => { + if (sortBy === 'Priority') { + const priorityOrder = { 'High': 0, 'Medium': 1, 'Low': 2 }; + return priorityOrder[a.priority] - priorityOrder[b.priority]; + } + return 0; }); const taskStats = { @@ -276,6 +289,24 @@ function App() { + + @@ -302,7 +333,7 @@ function App() { ) : (
- {filteredTasks.map(task => ( + {sortedTasks.map(task => (
@@ -347,9 +378,14 @@ function App() {

- - {task.status} - +
+ + {task.status} + + + {task.priority} + +
{task.updated_at && (
diff --git a/todo_app_frontend/src/components/TaskForm.tsx b/todo_app_frontend/src/components/TaskForm.tsx index d16a894..e16c74c 100644 --- a/todo_app_frontend/src/components/TaskForm.tsx +++ b/todo_app_frontend/src/components/TaskForm.tsx @@ -10,6 +10,7 @@ const TaskForm: React.FC = ({ onSubmit }) => { const [taskName, setTaskName] = useState(''); const [description, setDescription] = useState(''); const [status, setStatus] = useState<'In-Progress' | 'Completed'>('In-Progress'); + const [priority, setPriority] = useState<'High' | 'Medium' | 'Low'>('Medium'); const [isSubmitting, setIsSubmitting] = useState(false); const handleSubmit = async (e: React.FormEvent) => { @@ -21,12 +22,14 @@ const TaskForm: React.FC = ({ onSubmit }) => { task_name: taskName.trim(), description: description.trim(), status, + priority, }); if (success) { setTaskName(''); setDescription(''); setStatus('In-Progress'); + setPriority('Medium'); } setIsSubmitting(false); }; @@ -39,7 +42,7 @@ const TaskForm: React.FC = ({ onSubmit }) => {
-
+
+ +
+ + +
diff --git a/todo_app_frontend/src/components/TaskItem.tsx b/todo_app_frontend/src/components/TaskItem.tsx index 2e7cecf..3342c82 100644 --- a/todo_app_frontend/src/components/TaskItem.tsx +++ b/todo_app_frontend/src/components/TaskItem.tsx @@ -13,6 +13,7 @@ const TaskItem: React.FC = ({ task, onUpdate, onDelete }) => { const [editName, setEditName] = useState(task.task_name); const [editDescription, setEditDescription] = useState(task.description); const [editStatus, setEditStatus] = useState(task.status); + const [editPriority, setEditPriority] = useState(task.priority); const [isUpdating, setIsUpdating] = useState(false); const [isDeleting, setIsDeleting] = useState(false); @@ -21,6 +22,7 @@ const TaskItem: React.FC = ({ task, onUpdate, onDelete }) => { setEditName(task.task_name); setEditDescription(task.description); setEditStatus(task.status); + setEditPriority(task.priority); }; const handleCancel = () => { @@ -28,6 +30,7 @@ const TaskItem: React.FC = ({ task, onUpdate, onDelete }) => { setEditName(task.task_name); setEditDescription(task.description); setEditStatus(task.status); + setEditPriority(task.priority); }; const handleSave = async () => { @@ -38,6 +41,7 @@ const TaskItem: React.FC = ({ task, onUpdate, onDelete }) => { task_name: editName.trim(), description: editDescription.trim(), status: editStatus, + priority: editPriority, }); if (success) { @@ -54,6 +58,18 @@ const TaskItem: React.FC = ({ task, onUpdate, onDelete }) => { }; const statusColor = task.status === 'Completed' ? 'bg-green-100 text-green-800' : 'bg-blue-100 text-blue-800'; + const getPriorityColor = (priority: string) => { + switch (priority) { + case 'High': + return 'bg-red-100 text-red-800'; + case 'Medium': + return 'bg-yellow-100 text-yellow-800'; + case 'Low': + return 'bg-green-100 text-green-800'; + default: + return 'bg-gray-100 text-gray-800'; + } + }; const formatDate = (dateString: string) => { return new Date(dateString).toLocaleDateString('en-US', { @@ -94,14 +110,29 @@ const TaskItem: React.FC = ({ task, onUpdate, onDelete }) => { +
) : (

{task.task_name}

{task.description}

- - {task.status} - +
+ + {task.status} + + + {task.priority} + +
Created: {formatDate(task.created_at)}
Updated: {formatDate(task.updated_at)}
diff --git a/todo_app_frontend/src/types/Task.ts b/todo_app_frontend/src/types/Task.ts index a816dcc..9730b0e 100644 --- a/todo_app_frontend/src/types/Task.ts +++ b/todo_app_frontend/src/types/Task.ts @@ -3,6 +3,7 @@ export interface Task { task_name: string; description: string; status: 'In-Progress' | 'Completed'; + priority: 'High' | 'Medium' | 'Low'; created_at: string; updated_at: string; } @@ -11,10 +12,12 @@ export interface CreateTaskRequest { task_name: string; description: string; status: 'In-Progress' | 'Completed'; + priority: 'High' | 'Medium' | 'Low'; } export interface UpdateTaskRequest { task_name: string; description: string; status: 'In-Progress' | 'Completed'; + priority: 'High' | 'Medium' | 'Low'; } \ No newline at end of file From 2b3ab54169ca0594d214415a75fd17e611eb5644 Mon Sep 17 00:00:00 2001 From: keep <1603421097@qq.com> Date: Thu, 16 Apr 2026 18:48:27 +0800 Subject: [PATCH 2/3] =?UTF-8?q?fix:=20=E4=B8=BA=E4=BB=BB=E5=8A=A1=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E9=BB=98=E8=AE=A4=E4=BC=98=E5=85=88=E7=BA=A7=E5=B9=B6?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E6=95=B0=E6=8D=AE=E5=BA=93=E5=9B=9E=E9=80=80?= =?UTF-8?q?=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在前后端任务创建和更新时添加默认优先级'Medium' - 设置SQLite作为数据库配置的回退选项 --- todo_app_backend/todo_app/settings.py | 2 +- todo_app_frontend/src/App.tsx | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/todo_app_backend/todo_app/settings.py b/todo_app_backend/todo_app/settings.py index cdff89f..a0d7c9d 100644 --- a/todo_app_backend/todo_app/settings.py +++ b/todo_app_backend/todo_app/settings.py @@ -81,7 +81,7 @@ import dj_database_url DATABASES = { - "default": dj_database_url.config(default=os.environ.get("DATABASE_URL")) + "default": dj_database_url.config(default=os.environ.get("DATABASE_URL", "sqlite:///db.sqlite3")) } diff --git a/todo_app_frontend/src/App.tsx b/todo_app_frontend/src/App.tsx index 5b32d89..ec49c60 100644 --- a/todo_app_frontend/src/App.tsx +++ b/todo_app_frontend/src/App.tsx @@ -50,12 +50,15 @@ function App() { const response = await fetch(`${API_BASE_URL}/tasks/`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(newTask) + body: JSON.stringify({ + ...newTask, + priority: newTask.priority || 'Medium' + }) }); if (!response.ok) throw new Error('Failed to create task'); await fetchTasks(); // Refresh tasks list - setNewTask({ task_name: '', description: '' }); + setNewTask({ task_name: '', description: '', priority: 'Medium' }); setShowAddForm(false); } catch (err) { setError(err.message); @@ -75,7 +78,8 @@ function App() { body: JSON.stringify({ task_name: editingTask.task_name, description: editingTask.description, - status: editingTask.status + status: editingTask.status, + priority: editingTask.priority || 'Medium' }) }); if (!response.ok) throw new Error('Failed to update task'); From ac29f3c33c54010e73b3d3c21d265cf7337bbfd9 Mon Sep 17 00:00:00 2001 From: keep <1603421097@qq.com> Date: Thu, 16 Apr 2026 18:56:56 +0800 Subject: [PATCH 3/3] =?UTF-8?q?feat(=E4=BB=BB=E5=8A=A1=E8=A1=A8=E5=8D=95):?= =?UTF-8?q?=20=E6=B7=BB=E5=8A=A0=E4=BB=BB=E5=8A=A1=E4=BC=98=E5=85=88?= =?UTF-8?q?=E7=BA=A7=E9=80=89=E6=8B=A9=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在任务表单中新增优先级选择下拉框,支持高、中、低三个优先级选项 --- todo_app_frontend/src/App.tsx | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/todo_app_frontend/src/App.tsx b/todo_app_frontend/src/App.tsx index ec49c60..e617cce 100644 --- a/todo_app_frontend/src/App.tsx +++ b/todo_app_frontend/src/App.tsx @@ -245,6 +245,23 @@ function App() { className="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent text-sm sm:text-base" />
+
+ + +