Files
fullstack-starter/apps/demo-app/src/routes/index.tsx
imbytecat a6125718f5 refactor: 重构演示应用目录结构与配置文件路径
- 重命名构建配置文件至指定应用目录
- 重命名 drizzle.config.ts 文件至 apps/demo-app/ 目录下
- 添加演示应用的完整包配置,包含构建、开发、数据库管理及依赖项,支持 Tauri 桌面应用开发。
- 重命名 robots.txt 文件至 demo-app 应用目录下
- 重命名 src-tauri/.gitignore 文件为 apps/demo-app/src-tauri/.gitignore
- 重命名 AGENTS.md 文件至指定目录路径
- 重命名构建脚本文件以匹配新项目路径结构
- 重命名默认能力配置文件以匹配新项目路径
- 重命名 Cargo.lock 文件以匹配新的项目路径结构
- 重命名 Cargo.toml 文件以正确反映其在项目中的路径位置
- 重命名图标文件以正确匹配新项目路径
- 重命名128x128.png图标文件至demo-app应用目录下
- 重命名图标文件以正确匹配新项目路径
- 重命名图标文件以匹配新项目路径
- 重命名图标文件以正确匹配新项目路径
- 重命名图标文件以匹配新项目路径
- 重命名图标文件以正确反映其在项目中的路径位置
- 重命名图标文件以匹配新项目路径结构
- 重命名图标文件以正确匹配新项目路径
- 重命名图标文件以正确匹配新项目路径
- 重命名图标文件以正确匹配新项目路径
- 重命名图标文件以正确匹配新项目路径
- 重命名图标文件以匹配新项目路径结构
- 重命名图标文件以正确放置在演示应用的资源目录中
- 重命名图标文件以正确匹配新项目路径
- 重命名图标文件以正确匹配新项目路径
- 重命名命令模块文件路径以匹配项目结构
- 重命名文件以正确反映其在项目中的位置
- 重命名主程序文件路径以匹配项目结构
- 重命名 sidecar.rs 文件至 demo-app 项目路径下
- 重命名 tauri.conf.json 文件至指定目录路径
- 重命名错误组件文件以正确反映其在项目中的位置
- 重命名文件 NotFount.tsx 到指定目录路径
- 重命名数据库入口文件路径以适应项目结构调整
- 重命名数据 schema 文件路径以匹配项目结构
- 重命名文件以正确反映其在项目中的位置路径
- 重命名环境配置文件以正确反映其在项目中的位置。
- 重命名文件以正确反映其在项目中的路径位置
- 重命名文件以正确反映其在项目中的位置
- 重命名文件以正确反映其在项目中的路径位置
- 重命名文件以正确反映其在项目中的路径位置
- 重命名工具函数文件以正确反映其在项目中的位置
- 重命名客户端文件以正确反映其在项目中的位置
- 重命名文件以正确反映其在项目中的位置
- 重命名文件路径以正确组织项目结构中的契约文件
- 重命名文件路径以正确反映其在项目中的位置
- 重命名文件以正确反映其在项目中的位置
- 重命名数据库中间件文件以正确反映其在项目中的位置
- 重命名中间件文件路径以匹配项目结构
- 重命名路由文件以正确反映其在项目中的位置
- 重命名文件以正确反映其在项目中的位置。
- 重命名类型文件以正确反映其在项目中的路径位置
- 重命名路由配置文件以匹配项目目录结构
- 重命名根路由文件以正确反映其在项目中的位置
- 重命名RPC路由文件至demo-app应用目录下
- 重命名路由文件路径以匹配项目结构调整
- 重命名路由树生成文件至 demo-app 应用目录下
- 重命名样式文件以正确反映其在项目中的位置
- 添加 TypeScript 配置以扩展 React 项目模板并设置路径别名。
- 重命名 vite.config.ts 文件至 apps/demo-app/ 目录下
- 移除 biome.json 中对 routeTree.gen.ts 文件的排除规则
- 更新依赖版本以统一使用 catalog 依赖管理,提升项目依赖一致性并升级关键包至最新稳定版本。
- 配置安装时的公共提升模式,包含类型包和特定命名空间的包。
- 删除空的 drizzle 目录占位文件
- 将 node 版本更新为最新版本,同时将 bun 和 rust 版本设置为最新。
- 将项目名称更新为 monorepo 并重构脚本与依赖配置以支持工作区结构和统一的 turbo 管理。
- 添加基础 TypeScript 配置文件,启用严格模式并配置模块解析与编译选项以支持现代 JavaScript 特性。
- 添加 Bun 专用的 TypeScript 配置,继承基础配置并引入 Bun 类型定义。
- 添加 tsconfig 包的配置文件并定义基础、Bun 和 React 的配置导出,同时引入 Bun 类型定义作为开发依赖。
- 添加React项目专用的TypeScript配置,指定JSX处理方式并扩展基础配置。
- 删除旧的 TypeScript 配置文件以移除过时的编译选项和路径别名设置。
2026-01-21 14:31:12 +08:00

216 lines
8.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useMutation, useSuspenseQuery } from '@tanstack/react-query'
import { createFileRoute } from '@tanstack/react-router'
import { isTauri } from '@tauri-apps/api/core'
import { getCurrentWindow } from '@tauri-apps/api/window'
import type { ChangeEventHandler, FormEventHandler } from 'react'
import { useEffect, useState } from 'react'
import { orpc } from '@/orpc'
export const Route = createFileRoute('/')({
component: Todos,
loader: async ({ context }) => {
await context.queryClient.ensureQueryData(orpc.todo.list.queryOptions())
},
})
function Todos() {
const [newTodoTitle, setNewTodoTitle] = useState('')
const listQuery = useSuspenseQuery(orpc.todo.list.queryOptions())
const createMutation = useMutation(orpc.todo.create.mutationOptions())
const updateMutation = useMutation(orpc.todo.update.mutationOptions())
const deleteMutation = useMutation(orpc.todo.remove.mutationOptions())
useEffect(() => {
if (!isTauri()) return
getCurrentWindow().setTitle('待办事项')
}, [])
const handleCreateTodo: FormEventHandler<HTMLFormElement> = (e) => {
e.preventDefault()
if (newTodoTitle.trim()) {
createMutation.mutate({ title: newTodoTitle.trim() })
setNewTodoTitle('')
}
}
const handleInputChange: ChangeEventHandler<HTMLInputElement> = (e) => {
setNewTodoTitle(e.target.value)
}
const handleToggleTodo = (id: string, currentCompleted: boolean) => {
updateMutation.mutate({
id,
data: { completed: !currentCompleted },
})
}
const handleDeleteTodo = (id: string) => {
deleteMutation.mutate({ id })
}
const todos = listQuery.data
const completedCount = todos.filter((todo) => todo.completed).length
const totalCount = todos.length
const progress = totalCount > 0 ? (completedCount / totalCount) * 100 : 0
return (
<div className="min-h-screen bg-slate-50 py-12 px-4 sm:px-6 font-sans">
<div className="max-w-2xl mx-auto space-y-8">
{/* Header */}
<div className="flex items-end justify-between">
<div>
<h1 className="text-3xl font-bold text-slate-900 tracking-tight">
</h1>
<p className="text-slate-500 mt-1"></p>
</div>
<div className="text-right">
<div className="text-2xl font-semibold text-slate-900">
{completedCount}
<span className="text-slate-400 text-lg">/{totalCount}</span>
</div>
<div className="text-xs font-medium text-slate-400 uppercase tracking-wider">
</div>
</div>
</div>
{/* Add Todo Form */}
<form onSubmit={handleCreateTodo} className="relative group z-10">
<div className="relative transform transition-all duration-200 focus-within:-translate-y-1">
<input
type="text"
value={newTodoTitle}
onChange={handleInputChange}
placeholder="添加新任务..."
className="w-full pl-6 pr-32 py-5 bg-white rounded-2xl shadow-[0_8px_30px_rgb(0,0,0,0.04)] border-0 ring-1 ring-slate-100 focus:ring-2 focus:ring-indigo-500/50 outline-none transition-all placeholder:text-slate-400 text-lg text-slate-700"
disabled={createMutation.isPending}
/>
<button
type="submit"
disabled={createMutation.isPending || !newTodoTitle.trim()}
className="absolute right-3 top-3 bottom-3 px-6 bg-indigo-600 hover:bg-indigo-700 text-white rounded-xl font-medium transition-all shadow-md shadow-indigo-200 disabled:opacity-50 disabled:shadow-none hover:shadow-lg hover:shadow-indigo-300 active:scale-95"
>
{createMutation.isPending ? '添加中' : '添加'}
</button>
</div>
</form>
{/* Progress Bar (Only visible when there are tasks) */}
{totalCount > 0 && (
<div className="h-1.5 w-full bg-slate-200 rounded-full overflow-hidden">
<div
className="h-full bg-indigo-500 transition-all duration-500 ease-out rounded-full"
style={{ width: `${progress}%` }}
/>
</div>
)}
{/* Todo List */}
<div className="space-y-3">
{todos.length === 0 ? (
<div className="py-20 text-center">
<div className="inline-flex items-center justify-center w-16 h-16 rounded-full bg-slate-100 mb-4">
<svg
className="w-8 h-8 text-slate-400"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
aria-hidden="true"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={1.5}
d="M12 6v6m0 0v6m0-6h6m-6 0H6"
/>
</svg>
</div>
<p className="text-slate-500 text-lg font-medium"></p>
<p className="text-slate-400 text-sm mt-1">
</p>
</div>
) : (
todos.map((todo) => (
<div
key={todo.id}
className={`group relative flex items-center p-4 bg-white rounded-xl border border-slate-100 shadow-sm transition-all duration-200 hover:shadow-md hover:border-slate-200 ${
todo.completed ? 'bg-slate-50/50' : ''
}`}
>
<button
type="button"
onClick={() => handleToggleTodo(todo.id, todo.completed)}
className={`flex-shrink-0 w-6 h-6 rounded-full border-2 transition-all duration-200 flex items-center justify-center mr-4 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 ${
todo.completed
? 'bg-indigo-500 border-indigo-500'
: 'border-slate-300 hover:border-indigo-500 bg-white'
}`}
>
{todo.completed && (
<svg
className="w-3.5 h-3.5 text-white"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth={3}
aria-hidden="true"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M5 13l4 4L19 7"
/>
</svg>
)}
</button>
<div className="flex-1 min-w-0">
<p
className={`text-lg transition-all duration-200 truncate ${
todo.completed
? 'text-slate-400 line-through decoration-slate-300 decoration-2'
: 'text-slate-700'
}`}
>
{todo.title}
</p>
</div>
<div className="flex items-center opacity-0 group-hover:opacity-100 transition-opacity duration-200 absolute right-4 pl-4 bg-gradient-to-l from-white via-white to-transparent sm:static sm:bg-none">
<span className="text-xs text-slate-400 mr-3 hidden sm:inline-block">
{new Date(todo.createdAt).toLocaleDateString('zh-CN')}
</span>
<button
type="button"
onClick={() => handleDeleteTodo(todo.id)}
className="p-2 text-slate-400 hover:text-red-500 hover:bg-red-50 rounded-lg transition-colors focus:outline-none"
title="删除"
>
<svg
className="w-5 h-5"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
strokeWidth={1.5}
aria-hidden="true"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"
/>
</svg>
</button>
</div>
</div>
))
)}
</div>
</div>
</div>
)
}