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

소스 정보

저장소
zerx-lab/get-topic
최근 소스 활동
2026년 9월 7일 05:17
감지된 SKILL.md 언어
중국어
스타
0
포크
0

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
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}`。
GitHub에서 보기