示例功能
以待办与个人资料作为可替换的功能范式。
有两个示例功能端到端接线出厂,让你可以复制范式而无需从头发明:一个 待办 (Todos)CRUD 列表,以及一个 个人资料 + 头像 编辑器。二者都标记为 Example ——是意在被你自己的产品数据替换的可用演示。两者都需要 Supabase 配置中的 Supabase 表。
待办(Example)
待办是完整数据路径的范例——service → TanStack Query → 组件。service
(app/services/todos.ts)是一组基于类型化 Supabase 客户端的纯函数,不导入任何
组件:
// app/services/todos.ts
export const listTodos = async (): Promise<Todo[]> => {
const { data, error } = await supabase
.from('todos')
.select(TODO_COLUMNS)
.order('sort_order', { ascending: true })
.order('created_at', { ascending: false })
if (error) throw new Error(error.message)
return (data ?? []) as Todo[]
}
// createTodo、toggleTodo、deleteTodo …TodosDemo(app/routes/showcase/demos/TodosDemo.tsx)通过 TanStack Query 消费
这些 service——用 useQuery 取列表,用 useMutation 做创建/切换/删除——并在 UI
层负责缓存失效。RLS 保证用户只能看到自己的行(参见
数据层),因此 service 在读取时无需按用户 id 过滤。
service 就是替换点。要用你自己的 API 而非 Supabase 支撑待办,只需重写
app/services/todos.ts,而查询钩子与组件保持不动。
个人资料 + 头像(Example)
账户页(/account,AccountPage.tsx)编辑已登录用户的个人资料。有两个
service 支撑它:
app/services/profile.ts—— 基于profiles表的getProfile/updateProfile(full_name、bio、phone、location、avatar_url)。app/services/avatar.ts—— 针对avatars存储桶的头像上传/读取循环。
头像流程本身就是一个有用的范式:
- 校验 选中的图片(客户端)——
guardAvatarInput拒绝空文件、超过 5 MB 的 文件,或 PNG/JPEG/WebP 之外的 MIME 类型。 - 上传 到
avatars/<userId>/avatar.<ext>(upsert: true),随后把该路径 存到个人资料行上。 - 读回 经由
getAvatarUrl,它生成一个签名 URL(7 天 TTL),并追加一个 来自updated_at的破缓存键,使重新上传能立即显示。
该桶是私有的;读取走签名 URL 而非公开链接。请按 Supabase 配置所示配置桶及其存储策略。
刻意不包含的内容
这是一个桌面外壳,而非 SaaS UI 套件。它刻意不含:
- 带图表的仪表盘,
- 管理后台或用户管理表格,
- CRM/内容管理脚手架。
这些示例存在的意义是展示一个功能的形态——一个类型化 service、查询钩子、一个
页面、RLS——而不是充当组件库。要找构建块(按钮、对话框、表单、表格以及其余
shadcn/ui 组件),请打开 /showcase/components 处的组件画廊。参见
能力展示。
复制这个范式
要端到端添加你自己的 CRUD 功能,照着待办做:
