Files
openbridge-token-usage-viewer/src/routes/todo.tsx
imbytecat 4c81148df8 feat: 添加待办事项列表功能并更新环境变量源
- 将运行时环境变量源从 import.meta.env 更新为 process.env。
- 添加待办事项列表功能,包含进度条、任务状态显示、完成统计和响应式界面设计。
2026-01-17 02:59:23 +08:00

193 lines
7.1 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 { useSuspenseQuery } from '@tanstack/react-query'
import { createFileRoute } from '@tanstack/react-router'
import { createServerFn } from '@tanstack/react-start'
import { db } from '@/db'
const getTodos = createServerFn({ method: 'GET' }).handler(async () => {
const todos = await db.query.todoTable.findMany()
return todos
})
export const Route = createFileRoute('/todo')({
component: Todo,
})
function Todo() {
const { data: todos } = useSuspenseQuery({
queryKey: ['todos'],
queryFn: () => getTodos(),
})
const completedCount = todos.filter((todo) => todo.completed).length
const totalCount = todos.length
return (
<div className="min-h-screen bg-gradient-to-br from-indigo-50 via-white to-purple-50 py-12 px-4 sm:px-6 lg:px-8">
<div className="max-w-3xl mx-auto">
{/* Header */}
<div className="text-center mb-8">
<h1 className="text-4xl font-bold text-gray-900 mb-2">
</h1>
<p className="text-gray-600">
{completedCount} / {totalCount}
</p>
</div>
{/* Progress Bar */}
<div className="mb-8 bg-white rounded-lg shadow-sm p-4">
<div className="flex items-center justify-between mb-2">
<span className="text-sm font-medium text-gray-700"></span>
<span className="text-sm font-medium text-indigo-600">
{totalCount > 0
? Math.round((completedCount / totalCount) * 100)
: 0}
%
</span>
</div>
<div className="w-full bg-gray-200 rounded-full h-2.5">
<div
className="bg-gradient-to-r from-indigo-500 to-purple-600 h-2.5 rounded-full transition-all duration-300"
style={{
width: `${totalCount > 0 ? (completedCount / totalCount) * 100 : 0}%`,
}}
/>
</div>
</div>
{/* Todo List */}
<div className="space-y-3">
{todos.length === 0 ? (
<div className="bg-white rounded-lg shadow-sm p-12 text-center">
<div className="text-gray-400 mb-4">
<svg
className="mx-auto h-12 w-12"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
aria-hidden="true"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
/>
</svg>
</div>
<p className="text-gray-500 text-lg"></p>
<p className="text-gray-400 text-sm mt-2">
</p>
</div>
) : (
todos.map((todo) => (
<div
key={todo.id}
className={`bg-white rounded-lg shadow-sm hover:shadow-md transition-all duration-200 p-5 border-l-4 ${
todo.completed
? 'border-green-500 bg-gray-50'
: 'border-indigo-500'
}`}
>
<div className="flex items-start gap-4">
{/* Checkbox */}
<div className="flex-shrink-0 pt-1">
<div
className={`w-6 h-6 rounded-full border-2 flex items-center justify-center transition-all ${
todo.completed
? 'bg-green-500 border-green-500'
: 'border-gray-300 hover:border-indigo-500'
}`}
>
{todo.completed && (
<svg
className="w-4 h-4 text-white"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
aria-hidden="true"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={3}
d="M5 13l4 4L19 7"
/>
</svg>
)}
</div>
</div>
{/* Todo Content */}
<div className="flex-1 min-w-0">
<h3
className={`text-lg font-medium ${
todo.completed
? 'text-gray-500 line-through'
: 'text-gray-900'
}`}
>
{todo.title}
</h3>
<div className="mt-2 flex items-center gap-2 text-xs text-gray-500">
<span className="inline-flex items-center gap-1">
<svg
className="w-4 h-4"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
aria-hidden="true"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"
/>
</svg>
{new Date(todo.createdAt).toLocaleDateString('zh-CN')}
</span>
</div>
</div>
{/* Status Badge */}
<div className="flex-shrink-0">
<span
className={`inline-flex items-center px-3 py-1 rounded-full text-xs font-medium ${
todo.completed
? 'bg-green-100 text-green-800'
: 'bg-indigo-100 text-indigo-800'
}`}
>
{todo.completed ? '已完成' : '进行中'}
</span>
</div>
</div>
</div>
))
)}
</div>
{/* Footer Stats */}
{todos.length > 0 && (
<div className="mt-8 grid grid-cols-2 gap-4">
<div className="bg-white rounded-lg shadow-sm p-4 text-center">
<p className="text-2xl font-bold text-indigo-600">
{totalCount - completedCount}
</p>
<p className="text-sm text-gray-600 mt-1"></p>
</div>
<div className="bg-white rounded-lg shadow-sm p-4 text-center">
<p className="text-2xl font-bold text-green-600">
{completedCount}
</p>
<p className="text-sm text-gray-600 mt-1"></p>
</div>
</div>
)}
</div>
</div>
)
}