diff --git a/AGENTS.md b/AGENTS.md index 582850b..0e09022 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,7 +4,7 @@ WonderQ-Admin-UI 是独立的 WonderQ 后台管理前端,用于维护小程序/H5 前台的首页结构、目的地、线路商品、活动入口和客户需求线索。 -当前项目是一个轻量单页后台应用,技术栈为 Vite + React + TypeScript + 普通 CSS。前端通过 `/api/admin/...` 调用后端管理接口,本地开发时由 Vite 代理到后端服务。 +当前项目是一个轻量单页后台应用,技术栈为 Vite + React + TypeScript + Tailwind CSS 4 + shadcn/ui 风格组件 + 普通 CSS。前端通过 `/api/admin/...` 调用后端管理接口,本地开发时由 Vite 代理到后端服务。 ## 目录结构 @@ -12,6 +12,7 @@ WonderQ-Admin-UI 是独立的 WonderQ 后台管理前端,用于维护小程序 WonderQ-Admin-UI/ ├─ AGENTS.md # Agent 项目规则 ├─ README.md # 项目启动说明 +├─ components.json # shadcn/ui 组件配置 ├─ docs/ │ ├─ README.md # 文档索引 │ └─ admin-backend-plan.md # 后台管理规划文档 @@ -22,8 +23,12 @@ WonderQ-Admin-UI/ ├─ src/ │ ├─ App.tsx # 后台主界面、页面状态、表单和业务 UI │ ├─ api.ts # Admin API 类型、Token 管理和请求封装 +│ ├─ components/ +│ │ └─ ui/ # 本地 shadcn/ui 风格基础组件 +│ ├─ lib/ +│ │ └─ utils.ts # className 合并工具 │ ├─ main.tsx # React 挂载入口 -│ ├─ styles.css # 全局样式与响应式布局 +│ ├─ styles.css # Tailwind 入口、设计变量、全局样式与响应式布局 │ └─ vite-env.d.ts # Vite 类型声明 ├─ index.html # Vite HTML 入口 ├─ package.json # 脚本与依赖 @@ -34,9 +39,11 @@ WonderQ-Admin-UI/ ## 模块分工 -- `src/App.tsx`:集中实现登录页、侧边导航、结构维护、首页/目的地维护、商品维护、线索跟进、Toast 提示等后台 UI。 +- `src/App.tsx`:集中实现登录页、侧边导航、结构维护、首页/目的地维护、商品维护、线索跟进、Toast 提示等后台 UI,并优先复用 `src/components/ui/` 基础组件。 - `src/api.ts`:定义 `Product`、`Destination`、`Lead`、`SiteConfig` 等接口类型,封装登录、商品、目的地、站点配置、线索、发布和重置接口。 -- `src/styles.css`:全局布局、后台工作台、表格/表单、商品编辑器、移动端适配等样式。 +- `src/components/ui/`:本地 shadcn/ui 风格组件目录,当前包含 Button、Card、Input、Textarea、Badge、Alert、Switch、NativeSelect、Separator 等基础组件。 +- `src/lib/utils.ts`:封装 `clsx` + `tailwind-merge` 的 `cn` 工具。 +- `src/styles.css`:Tailwind CSS 4 入口、设计变量、全局布局、后台工作台、表格/表单、商品编辑器、移动端适配等样式。 - `public/assets/`:前台/后台预览用静态图片资源,代码中保留 `/assets/...` 路径引用。 - `docs/`:后台规划与后续协作边界,涉及后端 API 和数据模型时先参考这里,再看后端仓库文档。 @@ -48,6 +55,8 @@ WonderQ-Admin-UI/ yarn install ``` +本仓库使用 Yarn 1 锁文件。Windows 本地构建依赖 Vite/Rolldown、Tailwind Oxide 和 lightningcss 的 native binding,`package.json` 中固定了对应 Windows 包;未完整验证前不要随意移除这些依赖。 + 本地开发: ```bash @@ -64,6 +73,7 @@ http://localhost:5601 - 后端 Admin API 默认运行在 `http://localhost:4000`。 - `vite.config.ts` 将 `/api` 代理到本地后端。 +- `vite.config.ts` 同时接入 `@tailwindcss/vite` 和 `@/*` 路径别名。 - 如需改 API 地址,优先使用环境变量 `VITE_API_BASE_URL`,不要硬编码生产地址。 类型检查与生产构建: @@ -103,7 +113,8 @@ yarn build - React 采用函数组件和 Hooks,不引入 class component。 - 遵循现有单文件轻量结构;未获授权前不要主动拆分大型组件或引入状态管理库。 - UI 图标优先复用 `lucide-react`。 -- 样式优先沿用 `src/styles.css` 的类名、色彩、间距和 6-8px 圆角习惯。 +- 通用按钮、输入框、卡片、提示、徽标、开关等基础控件优先复用 `src/components/ui/`,保持 shadcn/ui 风格一致。 +- 样式优先沿用 `src/styles.css` 的设计变量、类名、色彩、间距和 6-10px 圆角习惯;避免绕过组件体系写一套重复按钮/表单样式。 - API 访问统一走 `src/api.ts` 的 `request` 封装,不在组件里重复拼接鉴权逻辑。 - 中文文案文件按 UTF-8 处理;修改前确认编辑器编码,避免造成乱码。 @@ -113,6 +124,7 @@ yarn build - 函数、变量使用 camelCase,例如 `getProducts`、`selectedId`。 - 联合类型字面量使用小写英文,例如 `"draft"`、`"published"`、`"leads"`。 - CSS class 沿用 kebab-case,例如 `.admin-shell`、`.product-table`。 +- 组件文件命名沿用 shadcn/ui 习惯,基础组件放在 `src/components/ui/`,工具函数放在 `src/lib/`。 - API 类型与后端字段保持同名,避免在前端私自改字段语义。 ## 开发准则 diff --git a/README.md b/README.md index bd82927..e608685 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # WonderQ-Admin-UI -独立的 WonderQ 后台管理前端,基于 Vite、React、TypeScript 和普通 CSS。 +独立的 WonderQ 后台管理前端,基于 Vite、React、TypeScript、Tailwind CSS 4 和 shadcn/ui 风格组件。 ## 本地启动 @@ -10,6 +10,8 @@ yarn install ``` +本项目使用 Yarn 1 锁文件。Windows 环境下,Vite 8、Tailwind CSS 4、lightningcss 依赖 native binding,`package.json` 中已固定对应的 Windows 构建包,避免 Yarn 1 漏装 optional dependency 导致构建失败。 + 2. 确认后端 API 运行在 `http://localhost:4000`。 3. 启动后台 UI: @@ -24,8 +26,13 @@ yarn dev ## 目录说明 -- `src/`:后台管理 UI 和 API 客户端。 +- `src/App.tsx`:后台管理主界面和业务交互。 +- `src/api.ts`:Admin API 类型、Token 管理和请求封装。 +- `src/components/ui/`:本地 shadcn/ui 风格基础组件,包括 Button、Card、Input、Textarea、Badge、Alert、Switch 等。 +- `src/lib/utils.ts`:组件 className 合并工具。 +- `src/styles.css`:Tailwind 入口、设计变量、后台布局和响应式样式。 - `public/assets/`:后台预览所需静态资源,保留 `/assets/...` 引用方式。 -- `vite.config.ts`:开发环境将 `/api` 代理到 `http://localhost:4000`。 +- `components.json`:shadcn/ui 组件配置。 +- `vite.config.ts`:开发环境将 `/api` 代理到 `http://localhost:4000`,并接入 Tailwind Vite 插件与 `@/*` 路径别名。 本仓库已经独立,不再依赖 `WonderQ-MiniAPP` 的相对路径。 diff --git a/components.json b/components.json new file mode 100644 index 0000000..3ad1981 --- /dev/null +++ b/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "", + "css": "src/styles.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} diff --git a/package.json b/package.json index dfff1a8..33d4abf 100644 --- a/package.json +++ b/package.json @@ -9,16 +9,27 @@ "preview": "vite preview --host 0.0.0.0 --port 5602" }, "dependencies": { + "@radix-ui/react-select": "^2.3.2", + "@radix-ui/react-slot": "^1.3.0", + "@radix-ui/react-switch": "^1.3.2", + "@rolldown/binding-win32-x64-msvc": "1.1.3", + "@tailwindcss/oxide-win32-x64-msvc": "4.3.2", + "@tailwindcss/vite": "^4.3.2", "@vitejs/plugin-react": "^6.0.3", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "lightningcss-win32-x64-msvc": "1.32.0", "lucide-react": "^1.21.0", "react": "^19.2.7", "react-dom": "^19.2.7", + "tailwind-merge": "^3.6.0", + "tailwindcss": "^4.3.2", "vite": "^8.1.0" }, "devDependencies": { + "@types/node": "^26.0.1", "@types/react": "^19.2.17", "@types/react-dom": "^19.2.3", "typescript": "^6.0.3" } } - diff --git a/src/App.tsx b/src/App.tsx index 2982312..acac029 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -45,6 +45,14 @@ import { updateProduct, updateSiteConfigItem, } from "./api"; +import { Alert, AlertDescription } from "@/components/ui/alert"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Input } from "@/components/ui/input"; +import { NativeSelect } from "@/components/ui/select"; +import { Switch } from "@/components/ui/switch"; +import { Textarea } from "@/components/ui/textarea"; type Tab = "structure" | "home" | "destinations" | "products" | "leads"; type PageId = "home" | "destination" | "detail" | "campaign" | "demand"; @@ -317,9 +325,9 @@ function ToastStack({ toasts, onClose }: { toasts: Toast[]; onClose: (id: string {toast.title} {toast.message} - + ))} @@ -349,23 +357,31 @@ function LoginScreen({ onDone }: { onDone: () => void }) { return (
-
- 万趣运营后台 -

万趣小程序管理端

-

按前台页面结构维护内容、商品和线索。

- - - {error ?

{error}

: null} - -
+ + + 万趣运营后台 + 万趣小程序管理端 + 按前台页面结构维护内容、商品和线索。 + + +
+ + + {error ? ( + + {error} + + ) : null} + +
+
+
); } @@ -495,18 +511,18 @@ function StructureWorkbench({

{fixedPage ? activePage.subtitle : "按用户看到的前台页面组织后台,逐层展开到可维护内容。"}

- - + - + +
{message ?

{message}

: null} @@ -515,13 +531,13 @@ function StructureWorkbench({ ) : ( @@ -542,13 +558,13 @@ function StructureWorkbench({

页面模块

{activePage.modules.map((module, index) => ( - + ))}
@@ -557,13 +573,13 @@ function StructureWorkbench({ {moduleEditable(moduleId) ? (
{editableItems.map((item) => ( - + ))}
) : ( @@ -595,7 +611,7 @@ function ReferencePanel({ moduleId, products, onJump }: { moduleId: ModuleId; pr return (

这个模块由客户需求列表维护,重点是状态流转、服务管家跟进和来源追踪。

- +
); } @@ -611,7 +627,7 @@ function ReferencePanel({ moduleId, products, onJump }: { moduleId: ModuleId; pr ))} - + ); } @@ -637,38 +653,38 @@ function SiteItemEditor({

编辑当前内容

保存后会影响对应前台模块 - +
{moduleId === "heroSlides" ? ( ) : null} {moduleId !== "destinations" ? (
) : null} @@ -678,7 +694,7 @@ function SiteItemEditor({
@@ -806,10 +822,10 @@ function ProductManager({

商品维护

先管理商品列表,再点击单个商品进入详情维护。

- + {message ?

{message}

: null}
@@ -833,27 +849,29 @@ function ProductManager({ load().catch((err) => setMessage(err.message)); }} > - setKeyword(event.target.value)} placeholder="搜索商品标题、短标题或标签" /> - +
{products.map((product) => ( - + ))} {!products.length ?

暂无商品,点击右上角新建商品。

: null}
@@ -871,14 +889,14 @@ function ProductManager({

把列表卡片、销售信息、详情图文拆成可单独维护的模块。

- - + +
{message ?

{message}

: null} @@ -910,19 +928,19 @@ function ProductManager({