- 添加 CLAUDE.md 项目文档 - 添加自动部署脚本 - 修改登录页面"其他登录方式"文本 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
7.6 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Project Overview
This is a multi-tenant service management platform ("百业到家") built with Ant Design Pro, React 19, TypeScript, and UmiJS Max framework. The application uses dynamic routing loaded from a backend API and implements a comprehensive permission system.
Development Commands
# Start development server (with mocks)
pnpm dev
# or
pnpm start
# Start development server (without mocks, connects to backend)
pnpm start:dev
# Build for production
pnpm build
# Lint (Biome + TypeScript check)
pnpm lint
# Run tests
pnpm test
# Run tests with coverage
pnpm test:coverage
# Type check only
pnpm tsc
Important: This project uses pnpm as the package manager, not npm or yarn.
Architecture
Dynamic Routing System
Routes are not statically defined in config/routes.ts (most routes are commented out). Instead, routes are dynamically loaded from the backend API:
- On app initialization,
getInfo()fetches user permissions and menu structure patchClientRoutes()insrc/app.tsxtransforms backend menu data into UmiJS routesloopMenuItem()insrc/utils/menuUtils.tsxrecursively converts menu items to route objects- Routes are lazy-loaded using
React.lazy(() => import('@/pages/${component}'))
When adding new pages: Create the page component in src/pages/, then configure the route in the backend system (not in code).
Multi-Tenant Architecture
Every API request includes a tenant-id header. Tenant information is:
- Stored in web-storage-cache with key
CACHE_KEY.TenantId - Set during login via
setTenantId() - Automatically added to all requests by the request interceptor in
src/app.tsx
Authentication Flow
- User logs in with tenant name, username, and password (password is RSA encrypted via JSEncrypt)
- Backend returns
accessTokenandrefreshToken - Tokens stored in web-storage-cache (see
src/utils/auth.ts) - All requests include
Authorization: Bearer ${accessToken}header - On 401 response, automatically attempts token refresh using
refreshToken - If refresh fails, redirects to login page
Token refresh logic is in src/requestErrorConfig.ts with request queuing to prevent duplicate refresh attempts.
Request Interceptors
All API requests are automatically modified (see src/app.tsx):
- API prefix:
/admin-apiis prepended to all URLs (unless already present) - Headers:
Authorization,tenant-id, andAcceptheaders are added - Parameter serialization: Arrays are serialized specially:
createTimearrays becomecreateTime[0]andcreateTime[1]- Other arrays become
key[]format
Caching Strategy
Uses web-storage-cache library (see src/hooks/web/useCache.ts):
CACHE_KEY.USER: Current user infoCACHE_KEY.ROLE_ROUTERS: Dynamic menu/route dataCACHE_KEY.ACCESS_TOKEN/CACHE_KEY.REFRESH_TOKEN: Auth tokensCACHE_KEY.DICT_CACHE: Dictionary/enum data loaded on app initCACHE_KEY.LoginForm: Remember me functionality (password encrypted)
State Management
- UmiJS Model Plugin: For global state (see
src/app.tsx-initialState) - Custom Hooks: In
src/hooks/stores/(e.g.,useDictStorefor dictionary management) - Web Storage Cache: For persistent data across sessions
Project Structure
src/
├── pages/ # Feature pages organized by domain
│ ├── ai/ # AI model and sample management
│ ├── prod/ # Product and category management
│ ├── system/ # System admin (users, roles, menus, tenants, logs, messages)
│ ├── trade/ # Order and sales management
│ └── user/ # Login page
├── services/ # API service layer (mirrors page structure)
├── components/ # Reusable components
│ ├── Draggable/ # Drag-and-drop components using @dnd-kit
│ ├── Upload/ # File/image/video upload components
│ ├── GroupTag/ # Tag management components
│ └── Tinymce/ # Rich text editor wrapper
├── hooks/ # Custom React hooks
│ ├── antd/ # Ant Design related hooks
│ ├── stores/ # State management hooks
│ └── web/ # Web utilities (cache, etc.)
├── utils/ # Utility functions
│ ├── auth.ts # Token and tenant management
│ ├── menuUtils.tsx # Dynamic route conversion
│ ├── dict.ts # Dictionary/enum utilities
│ └── tree.ts # Tree data structure helpers
├── constants/ # Shared constants and enums
├── locales/ # i18n translations (zh-CN default)
└── app.tsx # App initialization and runtime config
Environment Configuration
Multiple environment files:
.env.local- Local overrides (gitignored).env.dev- Development environment.env.prod- Production environment
Proxy configuration in config/proxy.ts:
dev: Points to local backend (currentlyhttp://192.168.1.89:48080)test: Points to test environmentpre: Points to pre-production environment
Switch environments using UMI_ENV:
cross-env UMI_ENV=dev pnpm start # Development
cross-env UMI_ENV=test pnpm start # Test
cross-env UMI_ENV=pre pnpm start # Pre-production
Code Conventions
Linting
- Uses Biome (not ESLint) for linting and formatting
- Configured in
biome.json - Pre-commit hook runs Biome via lint-staged
Commit Messages
- Uses commitlint with conventional commits
- Enforced via Husky pre-commit hook
- Format:
type(scope): subject(e.g.,feat: add user management)
Component Patterns
- ProTable: Use
EnhancedProTablewrapper for common table functionality - Forms: Use
DrawerFormorModalFormfrom@ant-design/pro-components - File Uploads: Use custom
UploadImagesorUploadVideocomponents insrc/components/Upload/ - Rich Text: Use
Tinymcecomponent wrapper (TinyMCE is inpublic/tinymce/)
Service Layer
- All API calls go through
src/services/ - Use UmiJS
requestfrom@umijs/max(configured insrc/app.tsx) - Response format:
{ code: number, data: any, msg: string } - Success when
code === 0
Common Patterns
Adding a New Page
- Create page component in
src/pages/{domain}/{feature}/index.tsx - Create corresponding service in
src/services/{domain}/{feature}/index.ts - Configure the menu/route in the backend system (not in code)
- The route will automatically appear after backend configuration
Working with Dictionaries/Enums
Dictionaries are loaded on app init and cached:
import { useDictStore } from '@/hooks/stores/dict';
const dictStore = useDictStore();
const dictData = dictStore.getDictByType('dict_type_key');
File Uploads
Media files are uploaded via src/services/infra/file/index.ts or src/services/infra/media/index.ts. Use the Upload components which handle the API integration.
Testing
- Jest configured with
jest.config.ts - Test files should be colocated with components or in
__tests__directories - Run single test:
pnpm test -- path/to/test.spec.ts
Important Notes
- Node version: Requires Node.js >= 20.0.0 (see
package.jsonengines) - React 19: Uses React 19 with
@ant-design/v5-patch-for-react-19compatibility patch - Mako bundler: Uses UmiJS Mako for faster builds (configured in
config/config.ts) - Mock data: Located in
mock/directory, enabled by default in dev mode - TinyMCE: Static files in
public/tinymce/, Chinese language pack included