From 928a78a33552ba86cb6db9cd9ff3808a961cb3e2 Mon Sep 17 00:00:00 2001
From: imbytecat
Date: Sat, 17 Jan 2026 03:09:08 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E4=BB=BB=E5=8A=A1?=
=?UTF-8?q?=E7=AE=A1=E7=90=86=E5=8A=9F=E8=83=BD=E5=B9=B6=E4=BC=98=E5=8C=96?=
=?UTF-8?q?=E4=BA=A4=E4=BA=92=E4=BD=93=E9=AA=8C?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 添加任务创建、完成状态切换和删除功能,优化界面交互并集成表单验证与加载状态反馈。
- 添加DOM和DOM.Iterable库支持以增强类型定义和浏览器API兼容性。
---
src/routes/todo.tsx | 159 ++++++++++++++++++++++++++++++++++++++++++--
tsconfig.json | 2 +-
2 files changed, 153 insertions(+), 8 deletions(-)
diff --git a/src/routes/todo.tsx b/src/routes/todo.tsx
index cc4531f..69cc967 100644
--- a/src/routes/todo.tsx
+++ b/src/routes/todo.tsx
@@ -1,23 +1,118 @@
-import { useSuspenseQuery } from '@tanstack/react-query'
+import { useMutation, useSuspenseQuery } from '@tanstack/react-query'
import { createFileRoute } from '@tanstack/react-router'
import { createServerFn } from '@tanstack/react-start'
+import { eq } from 'drizzle-orm'
+import type { ChangeEvent, FormEvent } from 'react'
+import { useState } from 'react'
+import { z } from 'zod'
import { db } from '@/db'
+import { todoTable } from '@/db/schema'
+// Zod Schemas
+const createTodoSchema = z.object({
+ title: z.string().min(1, '标题不能为空'),
+})
+
+const updateTodoSchema = z.object({
+ id: z.string().uuid(),
+ completed: z.boolean(),
+})
+
+const deleteTodoSchema = z.object({
+ id: z.string().uuid(),
+})
+
+// Server Functions - CRUD 操作
const getTodos = createServerFn({ method: 'GET' }).handler(async () => {
- const todos = await db.query.todoTable.findMany()
+ const todos = await db.query.todoTable.findMany({
+ orderBy: (todos, { desc }) => [desc(todos.createdAt)],
+ })
return todos
})
+const createTodo = createServerFn({ method: 'POST' })
+ .inputValidator(createTodoSchema)
+ .handler(async ({ data }) => {
+ const [newTodo] = await db
+ .insert(todoTable)
+ .values({ title: data.title })
+ .returning()
+ return newTodo
+ })
+
+const updateTodo = createServerFn({ method: 'POST' })
+ .inputValidator(updateTodoSchema)
+ .handler(async ({ data }) => {
+ const [updatedTodo] = await db
+ .update(todoTable)
+ .set({ completed: data.completed })
+ .where(eq(todoTable.id, data.id))
+ .returning()
+ return updatedTodo
+ })
+
+const deleteTodo = createServerFn({ method: 'POST' })
+ .inputValidator(deleteTodoSchema)
+ .handler(async ({ data }) => {
+ await db.delete(todoTable).where(eq(todoTable.id, data.id))
+ return { success: true }
+ })
+
export const Route = createFileRoute('/todo')({
component: Todo,
})
function Todo() {
- const { data: todos } = useSuspenseQuery({
+ const [newTodoTitle, setNewTodoTitle] = useState('')
+ const { data: todos, refetch } = useSuspenseQuery({
queryKey: ['todos'],
queryFn: () => getTodos(),
})
+ // Mutations
+ const createMutation = useMutation({
+ mutationFn: (title: string) => createTodo({ data: { title } }),
+ onSuccess: () => {
+ setNewTodoTitle('')
+ refetch()
+ },
+ })
+
+ const updateMutation = useMutation({
+ mutationFn: ({ id, completed }: { id: string; completed: boolean }) =>
+ updateTodo({ data: { id, completed } }),
+ onSuccess: () => {
+ refetch()
+ },
+ })
+
+ const deleteMutation = useMutation({
+ mutationFn: (id: string) => deleteTodo({ data: { id } }),
+ onSuccess: () => {
+ refetch()
+ },
+ })
+
+ // Handlers
+ const handleCreateTodo = (e: FormEvent) => {
+ e.preventDefault()
+ if (newTodoTitle.trim()) {
+ createMutation.mutate(newTodoTitle.trim())
+ }
+ }
+
+ const handleInputChange = (e: ChangeEvent) => {
+ setNewTodoTitle(e.target.value)
+ }
+
+ const handleToggleTodo = (id: string, currentCompleted: boolean) => {
+ updateMutation.mutate({ id, completed: !currentCompleted })
+ }
+
+ const handleDeleteTodo = (id: string) => {
+ deleteMutation.mutate(id)
+ }
+
const completedCount = todos.filter((todo) => todo.completed).length
const totalCount = todos.length
@@ -34,6 +129,29 @@ function Todo() {
+ {/* Add Todo Form */}
+
+
{/* Progress Bar */}
@@ -92,7 +210,12 @@ function Todo() {
>
{/* Checkbox */}
-
+
+
{/* Todo Content */}
@@ -151,8 +274,8 @@ function Todo() {
- {/* Status Badge */}
-
+ {/* Status Badge and Delete Button */}
+
{todo.completed ? '已完成' : '进行中'}
+
diff --git a/tsconfig.json b/tsconfig.json
index dd311f4..2f120d7 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,7 +1,7 @@
{
"compilerOptions": {
// Environment setup & latest features
- "lib": ["ESNext"],
+ "lib": ["ESNext", "DOM", "DOM.Iterable"],
"target": "ESNext",
"module": "Preserve",
"moduleDetection": "force",