From 6add8bf02745a8c4eec5133cba5fb2325463db96 Mon Sep 17 00:00:00 2001 From: Wang Zhuoxuan Date: Wed, 22 Apr 2026 15:06:41 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20ESLint=20=E9=94=99?= =?UTF-8?q?=E8=AF=AF=EF=BC=8C=E6=B7=BB=E5=8A=A0=20lint-first=20=E5=B7=A5?= =?UTF-8?q?=E4=BD=9C=E6=B5=81=E8=A7=84=E8=8C=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 提取 QuestionActionsCell 组件修复 rules-of-hooks 违规 - 抑制 shadcn/ui 文件的 react-refresh/only-export-components - 移除未使用的 _note 参数并抑制保留参数的 unused-vars 警告 - CLAUDE.md 添加 lint-first 工作流:本地 lint 优先,CI 作为兜底 --- CLAUDE.md | 2 + src/components/question/columns.tsx | 108 ++++++++++++++-------------- src/components/ui/badge.tsx | 1 + src/components/ui/button.tsx | 1 + src/components/ui/tabs.tsx | 1 + src/routes/questions/index.tsx | 4 +- 6 files changed, 62 insertions(+), 55 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 1930444..6d4093d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -14,6 +14,7 @@ Development follows the phased roadmap in [dev-spec.md](./dev-spec.md) §九. ```bash bun install bun run dev # Vite dev server, default port 5173 +bun run lint # ESLint check — run after every code change bun run build # Production build → dist/ ``` @@ -63,6 +64,7 @@ src/ - **Routing**: React Router v7 library mode (`createBrowserRouter` + `RouterProvider`). Routes defined in `App.tsx`. Root layout (`routes/__root.tsx`) handles auth guard — redirects to `/login` when not authenticated. - **Development order**: Follow [dev-spec.md](./dev-spec.md) §九 (Phase roadmap). +- **Lint first, CI as fallback**: Every code change must pass `bun run lint` locally before commit. CI lint is the safety net, not the first check. Run `bun run lint` after writing/modifying code — fix all errors before committing. - **Auth flow**: Login page supports Token and username/password login. Token: POST `/admin/auth/login` → receive JWT. Password: POST `/admin/auth/login` with credentials → receive JWT. Both modes fall back to offline mode when backend is unavailable (stores a `offline_` prefixed token locally). JWT stored in auth-store → attached as `Authorization: Bearer ` on all subsequent requests. - **API client**: All admin API calls go through `lib/api-client.ts` (ky v2). Uses `baseUrl` + `prefix: "/admin"`. Auto-attaches auth header. 401 responses trigger logout + redirect to `/login`. - **Data tables**: TanStack Table v8 headless + shadcn/ui styled components in `components/data-table/` diff --git a/src/components/question/columns.tsx b/src/components/question/columns.tsx index e4248b6..322fc07 100644 --- a/src/components/question/columns.tsx +++ b/src/components/question/columns.tsx @@ -1,3 +1,4 @@ +/* eslint-disable react-refresh/only-export-components */ import type { ColumnDef } from "@tanstack/react-table" import { useNavigate } from "react-router" import { MoreHorizontal } from "lucide-react" @@ -51,6 +52,59 @@ function getPrimaryTransition(current: QuestionStatus): QuestionStatus | null { } } +function QuestionActionsCell({ question, ctx }: { question: Question; ctx: ColumnContext }) { + const navigate = useNavigate() + const transitions = getQuestionStatusesForTransition(question.status) + + return ( + + + + + + navigate(`/questions/${question.id}/edit`)}> + 编辑 + + + {question.source === "ugc" && ctx.onReview && ( + ctx.onReview!(question)}> + 审核 + + )} + + {transitions.length > 0 && ( + <> + + + 状态流转 + + {transitions.map((status) => ( + ctx.onStatusChange(question, status)} + > + {QUESTION_STATUSES[status]} + + ))} + + + + )} + + + ctx.onDelete(question)} + > + 删除 + + + + ) +} + export function getColumns(ctx: ColumnContext): ColumnDef[] { return [ { @@ -159,59 +213,7 @@ export function getColumns(ctx: ColumnContext): ColumnDef[] { { id: "actions", header: "", - cell: ({ row }) => { - const question = row.original - const navigate = useNavigate() - const transitions = getQuestionStatusesForTransition(question.status) - - return ( - - - - - - navigate(`/questions/${question.id}/edit`)}> - 编辑 - - - {question.source === "ugc" && ctx.onReview && ( - ctx.onReview!(question)}> - 审核 - - )} - - {transitions.length > 0 && ( - <> - - - 状态流转 - - {transitions.map((status) => ( - ctx.onStatusChange(question, status)} - > - {QUESTION_STATUSES[status]} - - ))} - - - - )} - - - ctx.onDelete(question)} - > - 删除 - - - - ) - }, + cell: ({ row }) => , }, ] } diff --git a/src/components/ui/badge.tsx b/src/components/ui/badge.tsx index 6eb2a05..6fa4fda 100644 --- a/src/components/ui/badge.tsx +++ b/src/components/ui/badge.tsx @@ -45,4 +45,5 @@ function Badge({ ) } +// eslint-disable-next-line react-refresh/only-export-components export { Badge, badgeVariants } diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx index 4d38506..7b4a17d 100644 --- a/src/components/ui/button.tsx +++ b/src/components/ui/button.tsx @@ -61,4 +61,5 @@ function Button({ ) } +// eslint-disable-next-line react-refresh/only-export-components export { Button, buttonVariants } diff --git a/src/components/ui/tabs.tsx b/src/components/ui/tabs.tsx index 7f2c4f2..eebf5eb 100644 --- a/src/components/ui/tabs.tsx +++ b/src/components/ui/tabs.tsx @@ -86,4 +86,5 @@ function TabsContent({ ) } +// eslint-disable-next-line react-refresh/only-export-components export { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants } diff --git a/src/routes/questions/index.tsx b/src/routes/questions/index.tsx index 3e9ff8f..7e0727f 100644 --- a/src/routes/questions/index.tsx +++ b/src/routes/questions/index.tsx @@ -218,7 +218,7 @@ export default function QuestionsPage() { setUgcReviewOpen(true) } - async function handleApproveUgc(_note?: string) { + async function handleApproveUgc() { if (!ugcReviewQuestion) return try { await updateQuestionStatus(ugcReviewQuestion.id, "published") @@ -231,7 +231,7 @@ export default function QuestionsPage() { } } - async function handleRejectUgc(_note: string) { + async function handleRejectUgc(_note: string) { // eslint-disable-line @typescript-eslint/no-unused-vars if (!ugcReviewQuestion) return try { // TODO: 这里可以添加 API 调用来保存审核备注