diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 381f234e..b2abf452 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,6 +6,16 @@ permissions: contents: read jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: actions/setup-node@v5 + with: + node-version: 20 + - run: npm ci + - run: npx antd lint ./src + build: runs-on: ${{ matrix.os }} strategy: diff --git a/CLAUDE.md b/CLAUDE.md index c8d0b9f7..ee40aa3f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -90,7 +90,7 @@ Umi Max (`@umijs/max`) is the meta-framework. It wraps the build pipeline and pr - **@tanstack/react-query** for complex server state (e.g., table-list uses `useMutation` + `useQuery`) - Most pages use ProTable's built-in `request` prop for data loading -### Styling: Three Systems Coexist +### Styling Systems 1. **Tailwind CSS v4** — entry: `src/tailwind.css`, PostCSS plugin in `postcss.config.js`. Best for layout utilities 2. **antd-style v4** (`createStyles`) — CSS-in-JS with design token access (`{ token }`). Preferred when consuming theme tokens @@ -117,6 +117,12 @@ Each page directory contains its own index.tsx, optional service.ts, _mock.ts, d `cloudflare-worker/` is a separate deployable (Hono framework, own `package.json`/`tsconfig.json`). Not an npm workspace — manage independently. Provides the production demo API. +```bash +cd cloudflare-worker +npm run dev # Local dev (wrangler dev) +npm run deploy # Deploy to Cloudflare (wrangler deploy) +``` + ## Ant Design CLI `@ant-design/cli` (antd) is installed as a dev dependency. It provides offline antd component metadata and project analysis. Run via `npx antd`. @@ -139,7 +145,17 @@ The CLI also supports MCP server mode: `npx antd mcp` (for IDE integrations). - **TypeScript strict mode** enabled. Path aliases: `@/*` → `./src/*`, `@@/*` → `./src/.umi/*`. - **Node >= 20** required. - **Markdown as raw strings**: `config/md-raw-loader.cjs` lets `.md` files be imported as strings (used in Welcome/cheatsheet pages with `@ant-design/x-markdown`). -- **Auto-generated code in `src/services/`** is excluded from Biome linting. Never edit manually — regenerate with `npm run openapi`. +- **Auto-generated code in `src/services/`** is excluded from Biome linting - **Adding a new page**: 1) Create component in `src/pages/` 2) Add route in `config/routes.ts` 3) Add menu translation in `src/locales/` (route `name` maps to `menu.xxx` i18n key) - **Adding global state**: Create a file in `src/models/` exporting a custom Hook, use `useModel('filename')` in components -- **Access control**: Route-level via `access` field in routes; component-level via `` or `useAccess()` hook from `@umijs/max` \ No newline at end of file +- **Access control**: Route-level via `access` field in routes; component-level via `` or `useAccess()` hook from `@umijs/max` + +## Gotchas & Troubleshooting + +- **`.umi` directory**: Auto-generated by Umi. If dev server behaves unexpectedly, delete `src/.umi` and restart +- **Port 8000 in use**: Kill the process (`lsof -ti:8000 | xargs kill`) or use `PORT=3000 npm start` to change it +- **Mock not updating**: Umi usually auto-discovers `mock/` and `src/pages/**/_mock.ts` changes. If a new file isn't recognized, try restarting the dev server +- **Biome + antd lint**: Both must pass before committing. `npm run lint` runs Biome + tsc; `npx antd lint ./src` runs separately. Do not install ESLint or Prettier — this project uses Biome only +- **`src/services/` is auto-generated**: Never edit manually. Run `npm run openapi` to regenerate +- **`npm run simple` is irreversible**: Always commit or branch before running it +- **Lock file**: Uses `package-lock.json`. If deps break, delete `node_modules` and reinstall diff --git a/docs/cheatsheet.en-US.md b/docs/cheatsheet.en-US.md index d017a0da..9ea85058 100644 --- a/docs/cheatsheet.en-US.md +++ b/docs/cheatsheet.en-US.md @@ -100,6 +100,7 @@ npm install # Update dependencies **Route config** is in `config/routes.ts`: ```ts +// File: config/routes.ts export default [ { path: '/welcome', @@ -143,12 +144,19 @@ const location = useLocation(); // current route info **ProLayout config** is in `config/defaultSettings.ts`: ```ts +// File: config/defaultSettings.ts export default { - navTheme: 'light', // nav theme: light / dark - colorPrimary: '#1890ff', // primary color - layout: 'mix', // layout mode: side / top / mix - contentWidth: 'Fluid', // content width: Fluid / Fixed - fixSiderbar: true, // fixed sidebar + navTheme: 'light', // nav theme: light / dark + colorPrimary: '#1890ff', // primary color + layout: 'mix', // layout mode: side / top / mix + contentWidth: 'Fluid', // content width: Fluid / Fixed + fixedHeader: false, // fixed header + fixSiderbar: true, // fixed sidebar + colorWeak: false, // color weak mode + title: 'Ant Design Pro', // site title + logo: 'https://...', // logo URL + iconfontUrl: '', // iconfont URL + token: {}, // ProLayout token for fine-grained style customization }; ``` @@ -181,7 +189,7 @@ const Page = () => ( **useModel — lightweight global state:** Create a file in `src/models/` to auto-register: ```ts -// src/models/counter.ts +// File: src/models/counter.ts import { useState } from 'react'; export default function useCounter() { @@ -229,7 +237,7 @@ const mutation = useMutation({ **Initial state — getInitialState:** Define in `src/app.tsx`, accessible globally: ```tsx -// src/app.tsx +// File: src/app.tsx export async function getInitialState() { const currentUser = await fetchUserInfo(); return { currentUser }; @@ -249,6 +257,7 @@ const { initialState } = useModel('@@initialState'); **Request config** is in `src/app.tsx`: ```ts +// File: src/app.tsx export const request: RequestConfig = { baseURL: 'https://api.example.com', timeout: 10000, @@ -288,6 +297,7 @@ Auto-generates API calling code under `src/services/` based on `config/oneapi.js **Define permissions** in `src/access.ts`: ```ts +// File: src/access.ts export default function access(initialState: { currentUser?: API.CurrentUser }) { const { currentUser } = initialState; return { @@ -441,7 +451,7 @@ Test files go next to the component, named `*.test.ts(x)`. **Mock data:** Create files in `mock/`: ```ts -// mock/user.ts +// File: mock/user.ts export default { 'GET /api/currentUser': { name: 'Serati Ma', access: 'admin' }, 'POST /api/login': (req, res) => { res.end('ok'); }, @@ -453,6 +463,7 @@ Umi auto-registers mocks, active in dev mode. **Proxy config** is in `config/proxy.ts`: ```ts +// File: config/proxy.ts export default { dev: { '/api/': { @@ -488,3 +499,71 @@ Create a file in `src/models/` exporting a custom Hook, then use `useModel('file 1. Configure `openAPI` in `config/config.ts` 2. Run `npm run openapi` 3. Code is auto-generated under `src/services/` → See [umi FAQ](https://umijs.org/en-US/docs/introduce/faq) + +## Common Tasks + +### Add a New Page + +```bash +# 1. Create the page component +# File: src/pages/my-page/index.tsx + +# 2. Register in route config +# File: config/routes.ts +# { path: '/my-page', name: 'myPage', icon: 'file', component: './my-page' } + +# 3. Add i18n translations (for menu display) +# File: src/locales/zh-CN/menu.ts → menu.myPage: '我的页面' +# File: src/locales/en-US/menu.ts → menu.myPage: 'My Page' +``` + +### Add Global State + +```bash +# 1. Create a model file (filename becomes the model key) +# File: src/models/myModel.ts +# Export a custom Hook: export default function useMyModel() { ... } + +# 2. Use in components +# import { useModel } from '@umijs/max'; +# const { data } = useModel('myModel'); // 'myModel' matches filename +``` + +### Add a Mock API + +```bash +# Global mock: mock/api.ts (applies to all environments) +# Page-level mock: src/pages/my-page/_mock.ts (auto-discovered by Umi) + +# Mock format: +# export default { 'GET /api/my-data': { data: [] } } +``` + +### Generate API Service Code + +```bash +# 1. Edit OpenAPI config: config/oneapi.json +# 2. Run generation (overwrites src/services/ant-design-pro/) +npm run openapi +# 3. Never edit generated code manually — modify oneapi.json and regenerate +``` + +### Switch to Simple Mode + +```bash +git add -A && git commit -m "chore: save before simple" # Must commit first +npm run simple # Irreversible +npm install # Update dependencies +``` + +## Constraints & Gotchas + +- **`src/services/ant-design-pro/`** is auto-generated code. Do NOT edit manually. Modify `config/oneapi.json` and run `npm run openapi` to regenerate. +- **`npm run simple` is irreversible**: It deletes demo pages and unused dependencies. Always commit before running. +- **`.umi` temp directory**: `src/.umi` is auto-generated by Umi. Delete it and restart the dev server if you encounter unexpected behavior. +- **Biome over ESLint**: This project uses Biome for linting and formatting. Do not install ESLint or Prettier plugins. +- **Commit convention**: Must follow [Conventional Commits](https://www.conventionalcommits.org/) (e.g., `feat:`, `fix:`, `chore:`). +- **`npx antd lint ./src`**: Must pass with zero errors and warnings before committing. +- **Mock priority**: `mock/` directory for global mocks, `src/pages/**/_mock.ts` for page-level mocks. Both are auto-registered by Umi. +- **Styling priority**: Tailwind (layout) > antd-style (theme tokens) > CSS Modules (component styles) > Less (legacy global styles only). +- **Path aliases**: `@/*` → `./src/*`, `@@/*` → `./src/.umi/*` diff --git a/docs/cheatsheet.zh-CN.md b/docs/cheatsheet.zh-CN.md index f640e875..8ff9b7c5 100644 --- a/docs/cheatsheet.zh-CN.md +++ b/docs/cheatsheet.zh-CN.md @@ -100,6 +100,7 @@ npm install # 更新依赖 **路由配置** 位于 `config/routes.ts`: ```ts +// File: config/routes.ts export default [ { path: '/welcome', @@ -143,12 +144,19 @@ const location = useLocation(); // 当前路由信息 **ProLayout 配置** 位于 `config/defaultSettings.ts`: ```ts +// File: config/defaultSettings.ts export default { - navTheme: 'light', // 导航主题:light / dark - colorPrimary: '#1890ff', // 主题色 - layout: 'mix', // 布局模式:side / top / mix - contentWidth: 'Fluid', // 内容宽度:Fluid / Fixed - fixSiderbar: true, // 固定侧边栏 + navTheme: 'light', // 导航主题:light / dark + colorPrimary: '#1890ff', // 主题色 + layout: 'mix', // 布局模式:side / top / mix + contentWidth: 'Fluid', // 内容宽度:Fluid / Fixed + fixedHeader: false, // 固定顶部导航 + fixSiderbar: true, // 固定侧边栏 + colorWeak: false, // 色弱模式 + title: 'Ant Design Pro', // 站点标题 + logo: 'https://...', // Logo URL + iconfontUrl: '', // 图标字体 URL + token: {}, // ProLayout token,用于细粒度样式定制 }; ``` @@ -181,7 +189,7 @@ const Page = () => ( **useModel — 轻量全局状态:** 在 `src/models/` 下创建文件即自动注册: ```ts -// src/models/counter.ts +// File: src/models/counter.ts import { useState } from 'react'; export default function useCounter() { @@ -229,7 +237,7 @@ const mutation = useMutation({ **初始状态 — getInitialState:** 在 `src/app.tsx` 中定义,全局可访问: ```tsx -// src/app.tsx +// File: src/app.tsx export async function getInitialState() { const currentUser = await fetchUserInfo(); return { currentUser }; @@ -249,6 +257,7 @@ const { initialState } = useModel('@@initialState'); **请求配置** 位于 `src/app.tsx`: ```ts +// File: src/app.tsx export const request: RequestConfig = { baseURL: 'https://api.example.com', timeout: 10000, @@ -288,6 +297,7 @@ npm run openapi **定义权限** 在 `src/access.ts`: ```ts +// File: src/access.ts export default function access(initialState: { currentUser?: API.CurrentUser }) { const { currentUser } = initialState; return { @@ -441,7 +451,7 @@ npm run test:update # 更新快照 **Mock 数据:** 在 `mock/` 目录下创建文件: ```ts -// mock/user.ts +// File: mock/user.ts export default { 'GET /api/currentUser': { name: 'Serati Ma', access: 'admin' }, 'POST /api/login': (req, res) => { res.end('ok'); }, @@ -453,6 +463,7 @@ Umi 自动注册 mock,开发模式下生效。 **代理配置** 位于 `config/proxy.ts`: ```ts +// File: config/proxy.ts export default { dev: { '/api/': { @@ -488,3 +499,71 @@ export default { 1. 在 `config/config.ts` 配置 `openAPI` 2. 运行 `npm run openapi` 3. 自动生成 `src/services/` 下的代码 → 更多内容见 [umi FAQ](https://umijs.org/docs/introduce/faq) + +## 常见任务 + +### 添加新页面 + +```bash +# 1. 创建页面组件 +# 文件路径:src/pages/my-page/index.tsx + +# 2. 在路由配置中注册 +# 文件路径:config/routes.ts +# { path: '/my-page', name: 'myPage', icon: 'file', component: './my-page' } + +# 3. 添加国际化翻译(如需菜单显示) +# 文件路径:src/locales/zh-CN/menu.ts → menu.myPage: '我的页面' +# 文件路径:src/locales/en-US/menu.ts → menu.myPage: 'My Page' +``` + +### 添加全局状态 + +```bash +# 1. 创建 model 文件(文件名即 model key) +# 文件路径:src/models/myModel.ts +# 导出自定义 Hook:export default function useMyModel() { ... } + +# 2. 在组件中使用 +# import { useModel } from '@umijs/max'; +# const { data } = useModel('myModel'); // 'myModel' 对应文件名 +``` + +### 添加 Mock 接口 + +```bash +# 全局 Mock:mock/api.ts(匹配所有环境) +# 页面级 Mock:src/pages/my-page/_mock.ts(Umi 自动发现) + +# Mock 格式: +# export default { 'GET /api/my-data': { data: [] } } +``` + +### 生成 API 服务代码 + +```bash +# 1. 编辑 OpenAPI 配置:config/oneapi.json +# 2. 运行生成命令(覆盖 src/services/ant-design-pro/) +npm run openapi +# 3. 不要手动编辑生成代码,改 oneapi.json 重新生成 +``` + +### 切换到精简模式 + +```bash +git add -A && git commit -m "chore: save before simple" # 必须先提交 +npm run simple # 不可逆操作 +npm install # 更新依赖 +``` + +## 注意事项 + +- **`src/services/ant-design-pro/`** 为自动生成代码,禁止手动编辑。修改 `config/oneapi.json` 后执行 `npm run openapi` 重新生成 +- **`npm run simple` 不可逆**:会删除示例页面和多余依赖,执行前务必提交代码 +- **`.umi` 临时目录**:`src/.umi` 由 Umi 自动生成,遇到异常可删除后重启开发服务器 +- **Biome 代替 ESLint**:项目使用 Biome 进行 lint 和格式化,不要安装 ESLint 或 Prettier 插件 +- **Commit 规范**:必须遵循 [Conventional Commits](https://www.conventionalcommits.org/),如 `feat:`, `fix:`, `chore:` 等 +- **`npx antd lint ./src`**:提交前必须零错误零警告 +- **Mock 优先级**:`mock/` 目录为全局 Mock,`src/pages/**/_mock.ts` 为页面级 Mock,两者都会被 Umi 自动注册 +- **样式优先级**:Tailwind(布局)> antd-style(主题 token)> CSS Modules(组件样式)> Less(仅遗留全局样式) +- **路径别名**:`@/*` → `./src/*`,`@@/*` → `./src/.umi/*`