Skip to main content

zerx-frontend

gettopic 前端开发规约(React 19 + TanStack + connect-query + Zod4 + Tailwind v4)。当新增/修改路由页、CRUD 列表/表单、调用 connect-query hooks、做缓存失效、i18n、主题、按钮样式时使用。Keywords: React, TanStack Router, react-query, useQuery, useMutation, connect-query, createConnectQueryKey, invalidateQueries, react-form, Zod, Tailwind v4, bigint, uint64, table v8, i18n, 主题, theme, 语义 token, Can, permissions, gcTime, placeholderData, Button, outline, ghost, 按钮, 透明, 按钮背景, 前端, frontend, bun, pnpm

Informations de source

Dépôt
zerx-lab/get-topic
Dernière activité de la source
7 septembre 2026 à 05:17
Langue détectée de SKILL.md
chinois
Étoiles
0
Forks
0

Options d'installation

Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.

Vérifiez les fichiers source

Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.

Affichage de SKILL.md

SKILL.md
Instructions source · Aperçu en lecture seule
name
zerx-frontend
description
gettopic 前端开发规约(React 19 + TanStack + connect-query + Zod4 + Tailwind v4)。当新增/修改路由页、CRUD 列表/表单、调用 connect-query hooks、做缓存失效、i18n、主题、按钮样式时使用。Keywords: React, TanStack Router, react-query, useQuery, useMutation, connect-query, createConnectQueryKey, invalidateQueries, react-form, Zod, Tailwind v4, bigint, uint64, table v8, i18n, 主题, theme, 语义 token, Can, permissions, gcTime, placeholderData, Button, outline, ghost, 按钮, 透明, 按钮背景, 前端, frontend, bun, pnpm
# gettopic 前端开发规约 > Claude 已熟悉 React 19 / TanStack / Zod;以下是 gettopic 特有规则。最全范式页:`web/src/routes/_authed/users.tsx`(次选 `params.tsx`)。 ## 数据访问与失效 - hook 导入:`import { createConnectQueryKey, useMutation, useQuery } from "@connectrpc/connect-query"`。 - method 导入路径形状:`@/gen/zerx/v1/<entity>-<Service>_connectquery`,如 `@/gen/zerx/v1/user-UserService_connectquery`(导出 `listUsers/createUser/updateUser/deleteUser`)。注意 entity 前缀按文件名,如 `site-SiteSettingsService_connectquery`。 - 查询:`useQuery(listUsers, { page: { page, pageSize }, keyword })`;无入参 `useQuery(listRoles)`。 - 变更:`const m = useMutation(createUser); await m.mutateAsync(value)`;loading 用 `m.isPending`。 - 失效(**确切对象参数**): ```ts queryClient.invalidateQueries({ queryKey: createConnectQueryKey({ schema: listUsers, cardinality: "finite" }), }); ``` ## 坑表 | 错误写法 | 正解 | |---|---| | `cacheTime` / `isLoading` | react-query v5:`gcTime` / `isPending` / `placeholderData` | | `tailwindcss-animate` | Tailwind v4 动画用 `tw-animate-css` | | 改 `tailwind.config.js` / `postcss.config.js` | 二者**不存在**;主题变量在 `web/src/styles.css` | | `z.string().email()` | Zod4 顶层格式函数 `z.email()` | | 期望 `onSubmit` 拿到 transform 后值 | Standard Schema **不 transform**,拿到的是输入值;object 级 `.refine/.check` 仅基础字段通过后运行 | | 拦截器数组首项先执行 | connect `Interceptor` 数组**末尾先执行**(洋葱) | | `id` 当 number | `uint64`→`bigint`;显示 `String(id)`,列/key 用 `String(info.getValue())` | | 用 shadcn `form` / react-hook-form | 表单用 `@tanstack/react-form` | | 依赖 `exactOptionalPropertyTypes` | 已**关闭**(与 shadcn/Radix 不兼容);其余严格 flag(`strict`/`noUncheckedIndexedAccess`/`noUnused*`)保留 | | `pnpm` / `npm` 装前端依赖 | 包管理器是 bun(`web/packageManager: bun@1.4.0`)。进 `web/` 再 `bun install` / `bun run <script>`(`bun build` 是 bun 自带打包器,不是 vite) | ## 表格(react-table v8) `const columnHelper = createColumnHelper<User>()` → `columnHelper.accessor("col", { header, cell })` / `columnHelper.display({ id, header, cell })` → `useReactTable({ data, columns, getCoreRowModel: getCoreRowModel() })` → `flexRender`。 ## 表单(react-form) `useForm({ defaultValues, validators: { onChange: zodSchema }, onSubmit: async ({ value }) => … })`;`<form.Field name="...">`;错误用 `firstErrorMessage(field.state.meta.errors)`(`web/src/lib/form.ts`);可复用字段组件用 `AnyFieldApi` 类型。 ## i18n(`web/src/lib/i18n/index.tsx` + `locales/{en,zh}.ts`) - 结构锁:`locales/en.ts` 导出 `en`,`locales/zh.ts` 为 `zh: typeof en`。新增文案 **en/zh 同步加同名 key**(缺 key 编译失败;勿用可选属性,否则结构锁失效)。 - 用法:`const { t, locale, setLocale, toggleLocale } = useI18n(); t(key, params?)`;缺失回退 en 再回退 key。 ## 主题(`web/src/lib/theme.tsx`) - `const { theme, setTheme, toggleTheme } = useTheme()`。 - 样式一律用**语义 token**(`bg-background` / `text-foreground` 等,定义于 `web/src/styles.css` 的 `:root`/`.dark`,`@theme inline` 映射),**勿写死颜色**。 - Toaster 主题由 `__root.tsx` 透传 `useTheme()`。 ## 按钮(`web/src/components/ui/button.tsx`) 页级操作默认不透明白底;`ghost`/`link` 才是显式透明。 | 场景 | variant | |---|---| | 主操作(新建) | `default`(primary) | | 次操作(导出/清理/搜索/分页/取消) | `outline`(`bg-card` 白底,禁 `bg-background`/`bg-transparent`) | | 顶栏 icon、表格行内编辑删除 | `ghost`(唯一默认透明变体;须明确要透明才用) | | 危险确认 | `destructive` | 禁止在 `Button` 上覆写 `bg-background` / `bg-transparent`(与页面灰底融为一体)。 ## 权限显隐(纯 UX,非安全边界) - `<Can code="user:create">…</Can>`(组件 `web/src/components/can.tsx`,props `{ code, children }`)。 - 判定源 `web/src/lib/permissions.tsx`:`usePermissions().can(code)` = `roles.includes("admin") || codes.has(code)`(`roles: string[]` 多角色;接口 `PermissionContextValue{ roles, can, isLoading }`,`isLoading` = `me` 或 `getUserButtons` 任一 `isPending`,页面据此渲染骨架而非误判无权限;`me` 返 `user.roles`);数据来自 `me` + `getUserButtons` query。 - 真正鉴权在同名 procedure 的 Casbin 策略上,见 `skill://zerx-authz`。 ## lib 速查 - `transport.ts`:`transport`、`authedFetch(input, init?)`(401 single-flight 刷新→重试一次→失败清 token 跳登录)。 - `query-client.ts`:`queryClient`(staleTime 30s, gcTime 5min, retry 1)。 - `auth.ts`:`getAccessToken/setTokens/clearTokens/isAuthenticated/auth`。 - `menu-icons.ts`:`iconByName: Record<string, LucideIcon>`、`menuIcon(name)`(fallback `CircleIcon`)。 - 生成物命名:`web/src/gen/zerx/v1/<entity>_pb.ts`、`<entity>-<Service>_connectquery.ts`、`web/src/gen/buf/validate/validate_pb.ts`。 - 路由:`web/vite.config.ts` 用 `tanstackRouter({ target:"react", autoCodeSplitting:true })` 生成并提交 `src/routeTree.gen.ts`;404 / 错误边界在 `router.tsx`(`defaultNotFoundComponent` / `defaultErrorComponent` → `components/{not-found,error-view}.tsx`,`error-view` 按 `ConnectError.code` 区分 403 / 服务器错误)。 - CSP:后端对 SPA 下发 `script-src 'self'`,**禁止 inline `<script>`**;主题预加载脚本在 `web/public/theme-init.js`(`index.html` 以 `src` 引用)。 - 构建:Vite 8 底层是 rolldown,**不用 `manualChunks`**(兼容层会递归吞依赖);分包用 `rollupOptions.output.codeSplitting.groups`(first-match-wins):`vendor`(react/react-dom/scheduler/@tanstack)→ `connect`(@connectrpc/@bufbuild)→ `charts`(recharts + d3-*,`includeDependenciesRecursively:false`);`sourcemap:false`、`target:"es2022"`。 - 测试:vitest + jsdom + Testing Library(`web/vitest.config.ts`,`bun run test`,文件 `src/**/*.test.{ts,tsx}`,显式 `import { … } from "vitest"`);现有用例 `lib/transport.test.ts`(401 单飞刷新)、`lib/permissions.test.tsx`。 ## 源码锚点 `web/src/routes/_authed/users.tsx`、`web/src/components/ui/button.tsx`、`web/src/components/can.tsx`、`web/src/lib/{permissions.tsx,i18n.tsx,transport.ts,theme.tsx,query-client.ts,form.ts,auth.ts,menu-icons.ts}`。
Voir sur GitHub