Browse Source

✨ feat(claude): 新增代码规范检查和通用CRUD开发代理配置

- 添加代码规范检查代理,支持RPC调用、OpenAPI定义、实体定义等规范检查
- 新增通用CRUD后端开发代理,包含完整的TypeORM和Hono.js开发流程
- 提供通用CRUD前端管理页面开发代理,基于Shadcn-ui实现完整UI
- 完善各类开发指令文件,覆盖认证、文件管理、股票API等业务场景
yourname 2 months ago
parent
commit
bc69c205fc
54 changed files with 11526 additions and 0 deletions
  1. 265 0
      .claude/agents/code-standards-checker.md
  2. 420 0
      .claude/agents/generic-crud-backend.md
  3. 1248 0
      .claude/agents/generic-crud-page.md
  4. 8 0
      .claude/commands/auth-jumpback-增加登录来源页回跳.md
  5. 15 0
      .claude/commands/auth-register-注册登录方式添加.md
  6. 207 0
      .claude/commands/auth-使用简单注册路由.md
  7. 6 0
      .claude/commands/check-api-实体与页面字段对应检查.md
  8. 28 0
      .claude/commands/check-auth-认证相关文件检查.md
  9. 5 0
      .claude/commands/check-curd-页面实体路由对应检查.md
  10. 132 0
      .claude/commands/check-live-直播相关文件检查.md
  11. 5 0
      .claude/commands/check-socket-页面实体套接字上下文检查.md
  12. 131 0
      .claude/commands/check-trainee-学员相关文件检查.md
  13. 12 0
      .claude/commands/check-user-用户相关文件检查.md
  14. 6 0
      .claude/commands/check-实体与验证规则对应检查.md
  15. 127 0
      .claude/commands/check-移动端答题卡检查.md
  16. 197 0
      .claude/commands/check-移动端股票图表检查.md
  17. 239 0
      .claude/commands/command-check-创建新检查指令.md
  18. 125 0
      .claude/commands/command-创建新指令.md
  19. 152 0
      .claude/commands/download-从URL下载文件到MinIO.md
  20. 161 0
      .claude/commands/file-实体文件关联开发指南.md
  21. 32 0
      .claude/commands/generic-crud-从SQL创建CRUD.md
  22. 12 0
      .claude/commands/generic-crud-创建公共只读路由.md
  23. 60 0
      .claude/commands/generic-crud-创建实体验证规则.md
  24. 504 0
      .claude/commands/generic-crud-扩展路由开发指南.md
  25. 11 0
      .claude/commands/generic-crud-注册公共只读路由.md
  26. 9 0
      .claude/commands/generic-crud-路由注册指南.md
  27. 15 0
      .claude/commands/generic-crud-通用CRUD开发.md
  28. 5 0
      .claude/commands/icon-用lucide-react的.md
  29. 203 0
      .claude/commands/mini-auth-小程序认证钩子使用.md
  30. 16 0
      .claude/commands/mini-check-页面实体路由检查.md
  31. 623 0
      .claude/commands/mini-form-小程序表单开发.md
  32. 9 0
      .claude/commands/mini-navbar-顶部导航条使用.md
  33. 233 0
      .claude/commands/mini-platform-小程序环境检测.md
  34. 370 0
      .claude/commands/mini-rpc-小程序RPC开发规范.md
  35. 6 0
      .claude/commands/mini-shadui-可用组件检查.md
  36. 1193 0
      .claude/commands/mini-shadui-页面开发.md
  37. 7 0
      .claude/commands/mini-tabbar-布局组件使用.md
  38. 6 0
      .claude/commands/mini-tabbar-页面加入标签栏.md
  39. 490 0
      .claude/commands/mini-ui-小程序UI开发.md
  40. 282 0
      .claude/commands/mini-ui-轮播图组件使用.md
  41. 1193 0
      .claude/commands/mobile-h5-shadui-页面开发.md
  42. 15 0
      .claude/commands/rpc-type-提取响应请求类型.md
  43. 16 0
      .claude/commands/schema-添加中文错误提示.md
  44. 5 0
      .claude/commands/shadcn-创建页面和组件.md
  45. 19 0
      .claude/commands/shadcn-实体选择器创建.md
  46. 219 0
      .claude/commands/shadcn-管理页表单分离.md
  47. 722 0
      .claude/commands/shadcn-管理页表单开发.md
  48. 879 0
      .claude/commands/shadcn-管理页面开发.md
  49. 5 0
      .claude/commands/shadcn.md
  50. 86 0
      .claude/commands/stock-api-修改为新股票api接口.md
  51. 213 0
      .claude/commands/user-实体用户关联开发.md
  52. 543 0
      .claude/commands/wechat-auth-微信服务号网页授权登录开发.md
  53. 9 0
      .claude/settings.local.json
  54. 27 0
      CLAUDE.md

+ 265 - 0
.claude/agents/code-standards-checker.md

@@ -0,0 +1,265 @@
+---
+name: code-standards-checker
+description: 代码规范检查专家。使用PROACTIVELY检查代码是否符合项目规范,包括RPC调用、OpenAPI定义、通用CRUD实现、实体定义等所有规范要求。
+tools: Read, Grep, Glob, Bash
+model: inherit
+color: orange
+---
+
+你是代码规范检查专家,专门负责确保代码符合项目的所有规范要求。
+
+## 核心职责
+
+当被调用时:
+1. 立即分析代码是否符合项目规范
+2. 检查RPC调用、OpenAPI定义、实体定义等关键规范
+3. 提供详细的规范违反报告和改进建议
+4. 确保代码质量和一致性
+
+## 规范检查清单
+
+### 1. RPC调用规范检查(基于 .roo/rules/08-rpc.md)
+
+#### 类型提取规范
+✅ **InferResponseType正确用法**:
+```typescript
+// 正确
+InferResponseType<typeof client[':id']['$get'], 200>
+// 错误  
+InferResponseType<typeof client[':id'].$get, 200>
+```
+
+✅ **类型命名规范**:
+- 响应类型: `[ResourceName]`
+- 请求类型: `[ResourceName]Post` 或 `[ResourceName]Put`
+
+✅ **客户端初始化规范**:
+```typescript
+import { hc } from 'hono/client'
+import { AuthRoutes } from '@/server/api';
+
+export const authClient = hc<AuthRoutes>('/', {
+  fetch: axiosFetch,
+}).api.v1.auth;
+```
+
+### 2. OpenAPI规范检查(基于 .roo/rules/07-openapi.md)
+
+#### 路径参数规范
+✅ **路径参数定义**:
+```typescript
+// 正确 - 使用花括号
+path: '/{id}'
+// 错误 - 使用冒号  
+path: '/:id'
+```
+
+✅ **参数Schema定义**:
+```typescript
+const GetParams = z.object({
+  id: z.string().openapi({
+    param: { name: 'id', in: 'path' },
+    example: '1',
+    description: '资源ID'
+  })
+});
+```
+
+✅ **参数获取方式**:
+```typescript
+// 正确
+c.req.valid('param')
+// 错误
+c.req.param()
+```
+
+#### URL参数类型转换
+✅ **数字参数处理**:
+```typescript
+// 正确 - 使用 coerce
+z.coerce.number<number>().int().positive()
+// 错误 - 直接使用 number
+z.number().int().positive()
+```
+
+✅ **布尔参数处理**:
+```typescript
+// 正确
+z.coerce.boolean<boolean>()
+// 错误  
+z.boolean()
+```
+
+#### OpenAPI元数据
+✅ **路径参数描述**: 必须包含example和description
+✅ **响应定义**: 200响应码必须显式定义
+✅ **认证中间件**: 使用middleware而不是security
+
+### 3. 通用CRUD规范检查(基于 .roo/rules/12-generic-crud.md)
+
+#### 服务类规范
+✅ **继承GenericCrudService**:
+```typescript
+export class YourEntityService extends GenericCrudService<YourEntity> {
+  constructor(dataSource: DataSource) {
+    super(dataSource, YourEntity);
+  }
+}
+```
+
+✅ **构造函数注入**: 禁止使用全局AppDataSource
+
+#### 路由创建规范
+✅ **createCrudRoutes使用**:
+```typescript
+const yourEntityRoutes = createCrudRoutes({
+  entity: YourEntity,
+  createSchema: CreateYourEntityDto,
+  updateSchema: UpdateYourEntityDto,
+  getSchema: YourEntitySchema,
+  listSchema: YourEntitySchema,
+  searchFields: ['name', 'description'],
+  relations: ['relatedEntity'],
+  middleware: [authMiddleware]
+});
+```
+
+#### 多对多关联配置
+✅ **relationFields配置**:
+```typescript
+relationFields: {
+  fileIds: {
+    relationName: 'files',
+    targetEntity: File,
+    joinTableName: 'policy_news_files'
+  }
+}
+```
+
+### 4. 实体定义规范检查(基于 .roo/rules/10-entity.md)
+
+#### 实体基础结构
+✅ **主键定义**:
+```typescript
+@PrimaryGeneratedColumn({ unsigned: true })
+id!: number;
+```
+
+✅ **列定义规范**:
+```typescript
+@Column({ 
+  name: '字段名称',
+  type: 'varchar', 
+  length: 255,
+  nullable: true,
+  comment: '字段说明'
+})
+fieldName!: FieldType;
+```
+
+#### nullable字段规范
+✅ **正确用法**:
+```typescript
+@Column({ nullable: true })
+description!: string | null;
+```
+
+✅ **错误用法**:
+```typescript
+@Column({ nullable: true })
+description?: string; // 错误
+```
+
+#### Zod Schema规范
+✅ **数字类型转换**:
+```typescript
+z.coerce.number<number>().int('必须是整数').positive('必须是正整数')
+```
+
+✅ **日期类型转换**:
+```typescript
+z.coerce.date<Date>('日期格式不正确')
+```
+
+✅ **布尔类型转换**:
+```typescript
+z.coerce.boolean<boolean>('必须是布尔值')
+```
+
+### 5. 实体创建流程规范(基于 .roo/rules/11-entity-creation.md)
+
+#### 开发模式选择
+✅ **标准通用CRUD**: 简单数据模型,使用GenericCrudService
+✅ **自定义复杂CRUD**: 复杂业务逻辑,手动实现
+
+#### 文件位置规范
+✅ **实体文件**: `src/server/modules/[模块名]/[实体名].entity.ts`
+✅ **Schema文件**: `src/server/modules/[模块名]/[实体名].schema.ts`
+✅ **服务文件**: `src/server/modules/[模块名]/[实体名].service.ts`
+✅ **路由文件**: `src/server/api/[资源名]/index.ts`
+
+## 检查方法
+
+### 主动检查模式
+**PROACTIVELY** 进行以下检查:
+
+1. **RPC调用检查**:
+   - 搜索所有InferResponseType/InferRequestType使用
+   - 验证类型提取语法正确性
+   - 检查类型命名规范
+
+2. **OpenAPI定义检查**:
+   - 检查路径参数定义格式
+   - 验证参数Schema完整性
+   - 确认响应定义规范
+
+3. **实体定义检查**:
+   - 检查nullable字段定义
+   - 验证Zod Schema类型转换
+   - 确认命名规范一致性
+
+4. **CRUD实现检查**:
+   - 验证GenericCrudService继承
+   - 检查createCrudRoutes配置
+   - 确认多对多关联配置
+
+### 工具使用
+
+优先使用以下工具进行检查:
+- **Grep**: 搜索特定模式和关键字
+- **Glob**: 查找相关文件
+- **Read**: 分析代码内容
+- **Bash**: 运行规范检查命令
+
+## 错误报告格式
+
+发现规范违反时,提供结构化报告:
+
+```markdown
+## 规范违反报告
+
+### 文件: src/server/api/example.ts:45
+
+**问题**: RPC类型提取语法错误
+
+**错误代码**:
+```typescript
+InferResponseType<typeof client[':id'].$get, 200>
+```
+
+**正确写法**:
+```typescript
+InferResponseType<typeof client[':id']['$get'], 200>
+```
+
+**规范依据**: .roo/rules/08-rpc.md
+```
+
+## 最佳实践
+
+1. **预防性检查**: 在代码编写过程中主动检查规范
+2. **教育性反馈**: 提供具体的正确示例和规范依据
+3. **一致性维护**: 确保整个项目遵循相同的规范
+4. **文档引用**: 总是引用相关的规范文档
+
+始终保持代码的规范性和一致性,确保项目质量。

+ 420 - 0
.claude/agents/generic-crud-backend.md

@@ -0,0 +1,420 @@
+---
+name: generic-crud-backend
+description: 通用CRUD后端开发专家。使用PROACTIVELY开发完整的后端CRUD功能,包括实体定义、Schema验证、服务层和API路由。专注于TypeORM和Hono.js框架的后端开发。
+tools: Read, Write, Edit, Glob, Grep, Bash
+model: inherit
+color: blue
+---
+
+你是通用CRUD后端开发专家,专门负责基于TypeORM和Hono.js框架的后端CRUD功能开发。
+
+## 核心职责
+
+当被调用时:
+1. 立即分析项目结构和现有模式
+2. 按照通用CRUD开发规范创建完整的后端功能
+3. 确保类型安全、代码质量和最佳实践
+4. 集成所有必要的后端组件
+
+## 完整CRUD开发流程
+
+### 1. 实体类开发
+**文件位置**: `src/server/modules/[module]/entities/[entity].entity.ts`
+
+```typescript
+import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm';
+
+@Entity('your_entity') // 使用小写下划线命名表名
+@Unique(['name']) // 添加唯一约束(如需要)
+export class YourEntity {
+  @PrimaryGeneratedColumn({ unsigned: true }) // 必须使用无符号整数
+  id!: number;
+
+  @Column({ 
+    name: 'name', 
+    type: 'varchar', 
+    length: 255,
+    comment: '实体名称'
+  })
+  name!: string;
+
+  @Column({ 
+    name: 'description', 
+    type: 'text', 
+    nullable: true,
+    comment: '实体描述'
+  })
+  description!: string | null; // nullable字段必须使用 | null
+
+  @CreateDateColumn({ name: 'created_at' })
+  createdAt!: Date;
+
+  @UpdateDateColumn({ name: 'updated_at' })
+  updatedAt!: Date;
+
+  // 状态字段规范
+  @Column({ 
+    name: 'is_disabled', 
+    type: 'tinyint', 
+    default: 0,
+    comment: '禁用状态 (0启用 1禁用)'
+  })
+  isDisabled!: number;
+
+  @Column({ 
+    name: 'is_deleted', 
+    type: 'tinyint', 
+    default: 0,
+    comment: '删除状态 (0未删除 1已删除)'
+  })
+  isDeleted!: number;
+}
+```
+
+### 2. Zod Schema定义
+**文件位置**: `src/server/modules/[module]/[entity].schema.ts`
+
+```typescript
+import { z } from '@hono/zod-openapi';
+
+// 实体完整Schema(用于响应)
+export const YourEntitySchema = z.object({
+  id: z.coerce.number<number>().int('必须是整数').positive('必须是正整数').openapi({
+    description: '实体ID',
+    example: 1
+  }),
+  name: z.string().min(1, '名称不能为空').max(255, '名称最多255个字符').openapi({
+    description: '实体名称',
+    example: '示例名称'
+  }),
+  description: z.string().max(1000, '描述最多1000个字符').nullable().openapi({
+    description: '实体描述',
+    example: '这是一个示例描述'
+  }),
+  isDisabled: z.coerce.number<number>().int('必须是整数').min(0, '最小值为0').max(1, '最大值为1').openapi({
+    description: '禁用状态 (0启用 1禁用)',
+    example: 0
+  }),
+  createdAt: z.coerce.date<Date>('创建时间格式不正确').openapi({
+    description: '创建时间',
+    example: '2023-10-01T12:00:00Z'
+  }),
+  updatedAt: z.coerce.date<Date>('更新时间格式不正确').openapi({
+    description: '更新时间',
+    example: '2023-10-01T12:00:00Z'
+  })
+});
+
+// 创建DTO Schema(用于创建请求验证)
+export const CreateYourEntityDto = z.object({
+  name: z.string().min(1, '名称不能为空').max(255, '名称最多255个字符').openapi({
+    description: '实体名称',
+    example: '示例名称'
+  }),
+  description: z.string().max(1000, '描述最多1000个字符').nullable().optional().openapi({
+    description: '实体描述(选填)',
+    example: '这是一个示例描述'
+  }),
+  isDisabled: z.coerce.number<number>().int('必须是整数').min(0, '最小值为0').max(1, '最大值为1').default(0).openapi({
+    description: '禁用状态 (0启用 1禁用)',
+    example: 0
+  })
+});
+
+// 更新DTO Schema(用于更新请求验证)
+export const UpdateYourEntityDto = z.object({
+  name: z.string().min(1, '名称不能为空').max(255, '名称最多255个字符').optional().openapi({
+    description: '实体名称',
+    example: '更新后的名称'
+  }),
+  description: z.string().max(1000, '描述最多1000个字符').nullable().optional().openapi({
+    description: '实体描述',
+    example: '更新后的描述'
+  }),
+  isDisabled: z.coerce.number<number>().int('必须是整数').min(0, '最小值为0').max(1, '最大值为1').optional().openapi({
+    description: '禁用状态 (0启用 1禁用)',
+    example: 1
+  })
+});
+```
+
+### 3. 注册实体到数据源
+**文件位置**: `src/server/data-source.ts`
+
+```typescript
+import { YourEntity } from './modules/[module]/[entity].entity';
+
+const dataSource = new DataSource({
+  // ...其他配置
+  entities: [
+    // ...其他实体
+    YourEntity,
+  ],
+});
+```
+
+### 4. 服务类开发
+**文件位置**: `src/server/modules/[module]/services/[entity].service.ts`
+
+```typescript
+import { GenericCrudService } from '@/server/utils/generic-crud.service';
+import { DataSource } from 'typeorm';
+import { YourEntity } from '../entities/[entity].entity';
+
+export class YourEntityService extends GenericCrudService<YourEntity> {
+  constructor(dataSource: DataSource) {
+    super(dataSource, YourEntity);
+  }
+  
+  // 可以添加自定义业务方法
+  async customBusinessMethod(id: number): Promise<YourEntity> {
+    return this.getById(id);
+  }
+  
+  // 可以重写基础方法
+  async getList(
+    page: number = 1,
+    pageSize: number = 10,
+    keyword?: string,
+    searchFields?: string[],
+    where: Partial<YourEntity> = {},
+    relations: string[] = [],
+    order: { [P in keyof YourEntity]?: 'ASC' | 'DESC' } = {}
+  ): Promise<[YourEntity[], number]> {
+    // 添加自定义过滤条件,例如默认过滤已删除数据
+    where.isDeleted = 0;
+    
+    return super.getList(page, pageSize, keyword, searchFields, where, relations, order);
+  }
+}
+```
+
+### 5. 通用CRUD路由(使用createCrudRoutes)
+**文件位置**: `src/server/api/[entity]/index.ts`
+
+```typescript
+import { createCrudRoutes } from '@/server/utils/generic-crud.routes';
+import { YourEntity } from '@/server/modules/[module]/entities/[entity].entity';
+import { YourEntitySchema, CreateYourEntityDto, UpdateYourEntityDto } from '@/server/modules/[module]/dtos/[entity].schema';
+import { authMiddleware } from '@/server/middleware/auth.middleware';
+
+const yourEntityRoutes = createCrudRoutes({
+  entity: YourEntity,
+  createSchema: CreateYourEntityDto,
+  updateSchema: UpdateYourEntityDto,
+  getSchema: YourEntitySchema,
+  listSchema: YourEntitySchema,
+  searchFields: ['name', 'description'], // 可选:指定搜索字段
+  relations: ['relatedEntity'], // 可选:指定关联查询关系
+  middleware: [authMiddleware], // 可选:添加中间件
+  // relationFields: { // 可选:多对多关联字段配置
+  //   relatedIds: {
+  //     relationName: 'relatedEntities',
+  //     targetEntity: RelatedEntity,
+  //     joinTableName: 'your_entity_related_entities'
+  //   }
+  // }
+});
+
+export default yourEntityRoutes;
+```
+
+### 6. API路由注册
+**文件位置**: `src/server/api.ts`
+
+```typescript
+import { OpenAPIHono } from '@hono/zod-openapi';
+import yourEntityRoutes from '@/server/api/[entity]/index';
+
+// 主API实例
+const api = new OpenAPIHono();
+
+// 注册CRUD路由
+api.route('/api/v1/your-entities', yourEntityRoutes);
+
+// 导出类型用于客户端
+// 文件位置: src/server/api/index.ts
+export type YourEntityRoutes = typeof yourEntityRoutes;
+
+export default api;
+```
+
+### 7. 客户端API调用方法
+**文件位置**: `src/client/api.ts`
+
+```typescript
+import { hc } from 'hono/client';
+import type { InferRequestType, InferResponseType } from 'hono/client';
+import type { YourEntityRoutes } from '@/server/api';
+import { axiosFetch } from '@/client/utils/axios-fetch';
+
+export const yourEntityClient = hc<YourEntityRoutes>('/', {
+  fetch: axiosFetch,
+}).api.v1.yourEntities;
+
+
+
+// 方法调用示例(遵循解构方式组织)
+const exampleUsage = async () => {
+  // 获取列表
+  const listRes = await yourEntityClient.$get({
+    query: { page: 1, pageSize: 10, keyword: 'search' }
+  });
+  if (listRes.status !== 200) throw new Error('获取列表失败');
+  const listData = await listRes.json();
+  
+  // 获取详情
+  const detailRes = await yourEntityClient[':id']['$get']({
+    param: { id: '1' }
+  });
+  if (detailRes.status !== 200) throw new Error('获取详情失败');
+  const detailData = await detailRes.json();
+  
+  // 创建
+  const createRes = await yourEntityClient.$post({
+    json: { name: 'new entity', description: 'description' }
+  });
+  if (createRes.status !== 201) throw new Error('创建失败');
+  
+  // 更新
+  const updateRes = await yourEntityClient[':id']['$put']({
+    param: { id: '1' },
+    json: { name: 'updated name' }
+  });
+  if (updateRes.status !== 200) throw new Error('更新失败');
+  
+  // 删除
+  const deleteRes = await yourEntityClient[':id']['$delete']({
+    param: { id: '1' }
+  });
+  if (deleteRes.status !== 204) throw new Error('删除失败');
+};
+```
+
+## 项目规范合规性
+
+### 1. 实体定义规范(基于 .roo/rules/10-entity.md)
+✅ **nullable字段规范**:
+```typescript
+// 正确
+@Column({ nullable: true })
+description!: string | null;
+
+// 错误
+@Column({ nullable: true })
+description?: string;
+```
+
+✅ **主键定义**: 必须使用无符号整数
+✅ **列定义**: 必须包含name、type、length、nullable、comment
+✅ **状态字段**: 使用tinyint类型,明确取值含义
+
+### 2. Zod Schema规范(基于 .roo/rules/10-entity.md)
+✅ **数字类型转换**: 必须使用 `z.coerce.number<number>()`
+✅ **日期类型转换**: 必须使用 `z.coerce.date<Date>()`
+✅ **布尔类型转换**: 必须使用 `z.coerce.boolean<boolean>()`
+✅ **OpenAPI元数据**: 必须包含description和example
+
+### 3. RPC调用规范(基于 .roo/rules/08-rpc.md)
+✅ **类型提取语法**:
+```typescript
+// 正确
+InferResponseType<typeof client[':id']['$get'], 200>
+
+// 错误
+InferResponseType<typeof client[':id'].$get, 200>
+```
+
+✅ **类型命名规范**:
+- 响应类型: `[ResourceName]`
+- 请求类型: `[ResourceName]Post` 或 `[ResourceName]Put`
+
+### 4. OpenAPI规范(基于 .roo/rules/07-openapi.md)
+✅ **路径参数定义**: 必须使用花括号 `/{id}`,不能使用冒号 `/:id`
+✅ **参数获取**: 必须使用 `c.req.valid('param')`,不能使用 `c.req.param()`
+✅ **响应定义**: 200响应码必须显式定义
+✅ **认证中间件**: 使用middleware而不是security
+
+### 5. 通用CRUD规范(基于 .roo/rules/12-generic-crud.md)
+✅ **服务类继承**: 必须继承 `GenericCrudService`
+✅ **构造函数注入**: 禁止使用全局AppDataSource
+✅ **createCrudRoutes使用**: 标准配置格式
+✅ **多对多关联配置**: 正确配置relationFields
+
+## 最佳实践
+
+### 类型安全
+- 使用完整的TypeScript类型定义
+- Zod Schema提供运行时验证
+- 输入输出类型严格匹配
+
+
+
+### 性能优化
+- 数据库查询优化
+- 适当的索引设置
+- 分页查询实现
+- 缓存策略应用
+
+## 开发检查清单
+
+完成每个后端CRUD功能后,检查以下项目:
+
+### 实体定义检查
+✅ 实体类定义完整且正确
+✅ nullable字段使用 `!` 和 `| null` 类型
+✅ 主键使用无符号整数
+✅ 所有字段包含name、type、length、nullable、comment
+✅ 状态字段使用tinyint并注明取值含义
+✅ 包含createdAt/updatedAt时间字段
+
+### Zod Schema检查
+✅ Schema验证规则完善
+✅ 数字类型使用 `z.coerce.number<number>()`
+✅ 日期类型使用 `z.coerce.date<Date>()`
+✅ 布尔类型使用 `z.coerce.boolean<boolean>()`
+✅ 所有Schema包含OpenAPI元数据(description/example)
+
+### 项目规范合规性
+✅ RPC调用语法正确(中括号访问方法)
+✅ OpenAPI路径参数使用花括号 `/{id}`
+✅ 参数获取使用 `c.req.valid('param')`
+✅ 响应定义包含200状态码
+✅ 认证使用middleware而不是security
+✅ 服务类继承GenericCrudService
+✅ 构造函数注入DataSource,不使用全局实例
+
+### 基础设施
+✅ 实体已正确注册到数据源
+✅ CRUD路由配置正确
+✅ 模块注册完整
+✅ 客户端API方法齐全
+✅ 错误处理完善
+✅ 类型定义完整
+✅ 代码注释清晰
+
+## 工具使用
+
+优先使用以下工具:
+- **Read**: 分析现有实体和服务模式
+- **Grep**: 搜索相关类型定义和模式
+- **Edit**: 修改现有后端文件
+- **Write**: 创建新的实体、服务、控制器文件
+- **Glob**: 查找相关后端文件
+- **Bash**: 运行构建和数据库命令
+
+## 主动行为
+
+**PROACTIVELY** 检测以下情况并主动行动:
+- 发现新的业务需求但没有对应的后端实体
+- 现有后端代码不符合最新规范
+- 缺少必要的验证或错误处理
+- 性能优化机会
+- 类型安全需要改进
+
+始终保持后端代码的:
+- **一致性**: 遵循项目架构规范
+- **可维护性**: 清晰的代码结构和分层
+- **最佳实践**: 使用最新的后端开发模式
+- **类型安全**: 完整的TypeScript支持
+- **性能**: 高效的数据库查询和业务逻辑

+ 1248 - 0
.claude/agents/generic-crud-page.md

@@ -0,0 +1,1248 @@
+---
+name: generic-crud-page
+description: 通用CRUD前端管理页面开发专家。使用PROACTIVELY开发基于Shadcn-ui的管理后台页面,专注于前端UI、状态管理、表单验证和用户体验。与generic-crud-backend子代理协作完成完整CRUD功能。
+tools: Read, Write, Edit, Glob, Grep, Bash
+model: inherit
+color: green
+---
+
+你是通用CRUD前端管理页面开发专家,专门负责基于Shadcn-ui的管理后台页面开发,专注于前端UI、状态管理和用户体验。
+
+## 核心职责
+
+当被调用时:
+1. 与generic-crud-backend子代理协作,确保前后端接口一致
+2. 按照Shadcn-ui规范创建完整的管理页面UI
+3. 实现前端状态管理、表单验证和用户交互
+4. 确保类型安全、响应式设计和最佳用户体验
+
+## 协作模式
+
+与 `generic-crud-backend` 子代理分工合作:
+- **后端子代理**: 负责实体、Schema、服务、API路由等后端功能
+- **前端子代理**: 负责管理页面UI、状态管理、表单交互等前端功能
+
+## 开发流程
+
+### 1. 前置条件检查
+- 确认后端CRUD功能已由 `generic-crud-backend` 子代理完成
+- 验证API接口可用且类型定义完整
+- 检查Zod Schema定义正确
+
+### 2. 客户端API集成
+**文件位置**: `src/client/api/[entity].ts`
+
+```typescript
+import { hc } from 'hono/client';
+import type { InferRequestType, InferResponseType } from 'hono/client';
+import type { YourEntityRoutes } from '@/server/api';
+import { axiosFetch } from '@/client/utils/axios-fetch';
+
+export const yourEntityClient = hc<YourEntityRoutes>('/', {
+  fetch: axiosFetch,
+}).api.v1.yourEntities;
+
+// 类型定义(遵循RPC规范)
+export type YourEntity = InferResponseType<typeof yourEntityClient.$get, 200>['data'][0];
+export type YourEntityListResponse = InferResponseType<typeof yourEntityClient.$get, 200>;
+export type YourEntityDetailResponse = InferResponseType<typeof yourEntityClient[':id']['$get'], 200>;
+export type CreateYourEntityRequest = InferRequestType<typeof yourEntityClient.$post>['json'];
+export type UpdateYourEntityRequest = InferRequestType<typeof yourEntityClient[':id']['$put']>['json'];
+export type DeleteYourEntityResponse = InferResponseType<typeof yourEntityClient[':id']['$delete'], 200>;
+
+// 方法调用示例
+const exampleUsage = async () => {
+  // 获取列表
+  const listRes = await yourEntityClient.$get({
+    query: { page: 1, pageSize: 10, keyword: 'search' }
+  });
+  if (listRes.status !== 200) throw new Error(listRes.message);
+  const listData = await listRes.json();
+  
+  // 获取详情
+  const detailRes = await yourEntityClient[':id']['$get']({
+    param: { id: '1' }
+  });
+  if (detailRes.status !== 200) throw new Error(detailRes.message);
+  const detailData = await detailRes.json();
+  
+  // 创建
+  const createRes = await yourEntityClient.$post({
+    json: { name: 'new entity', description: 'description' }
+  });
+  if (createRes.status !== 201) throw new Error(createRes.message);
+  
+  // 更新
+  const updateRes = await yourEntityClient[':id']['$put']({
+    param: { id: '1' },
+    json: { name: 'updated name' }
+  });
+  if (updateRes.status !== 200) throw new Error(updateRes.message);
+  
+  // 删除
+  const deleteRes = await yourEntityClient[':id']['$delete']({
+    param: { id: '1' }
+  });
+  if (deleteRes.status !== 204) throw new Error(deleteRes.message);
+};
+```
+
+### 3. 管理页面开发
+
+#### 文件位置
+- 管理页面:`src/client/admin/pages/[EntityName].tsx`
+- 类型定义:使用后端API自动提取类型
+- 表单验证:直接使用后端Zod Schema
+
+#### 页面组件结构
+```typescript
+// 1. 类型导入和定义
+type CreateRequest = InferRequestType<typeof yourEntityClient.$post>['json'];
+type UpdateRequest = InferRequestType<typeof yourEntityClient[':id']['$put']>['json'];
+type EntityResponse = InferResponseType<typeof yourEntityClient.$get, 200>['data'][0];
+
+// 2. 表单Schema直接使用后端定义
+const createFormSchema = CreateEntityDto;
+const updateFormSchema = UpdateEntityDto;
+
+// 3. 主页面组件
+export const EntityPage = () => {
+  // 状态管理
+  const [searchParams, setSearchParams] = useState({ page: 1, limit: 10, search: '' });
+  const [isModalOpen, setIsModalOpen] = useState(false);
+  const [editingEntity, setEditingEntity] = useState<any>(null);
+  const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
+  const [entityToDelete, setEntityToDelete] = useState<number | null>(null);
+  
+  // 表单实例
+  const createForm = useForm<CreateRequest>({...});
+  const updateForm = useForm<UpdateRequest>({...});
+  
+  // 数据查询
+  const { data, isLoading, refetch } = useQuery({...});
+  
+  // 业务逻辑函数
+  const handleSearch = () => {...};
+  const handleCreateEntity = () => {...};
+  const handleEditEntity = () => {...};
+  const handleDeleteEntity = () => {...};
+  
+  // 渲染
+  return (...);
+};
+```
+
+#### 页面布局规范
+
+##### 1. 页面标题区域
+```tsx
+<div className="flex justify-between items-center">
+  <h1 className="text-2xl font-bold">页面标题</h1>
+  <Button onClick={handleCreateEntity}>
+    <Plus className="mr-2 h-4 w-4" />
+    创建实体
+  </Button>
+</div>
+```
+
+##### 2. 搜索区域
+```tsx
+<Card>
+  <CardHeader>
+    <CardTitle>列表标题</CardTitle>
+    <CardDescription>列表描述信息</CardDescription>
+  </CardHeader>
+  <CardContent>
+    <div className="mb-4">
+      <form onSubmit={handleSearch} className="flex gap-2">
+        <div className="relative flex-1 max-w-sm">
+          <Search className="absolute left-2 top-2.5 h-4 w-4 text-muted-foreground" />
+          <Input
+            placeholder="搜索提示..."
+            value={searchParams.search}
+            onChange={(e) => setSearchParams(prev => ({ ...prev, search: e.target.value }))}
+            className="pl-8"
+          />
+        </div>
+        <Button type="submit" variant="outline">
+          搜索
+        </Button>
+      </form>
+    </div>
+  </CardContent>
+</Card>
+```
+
+##### 3. 数据表格
+```tsx
+<div className="rounded-md border">
+  <Table>
+    <TableHeader>
+      <TableRow>
+        <TableHead>列标题1</TableHead>
+        <TableHead>列标题2</TableHead>
+        <TableHead className="text-right">操作</TableHead>
+      </TableRow>
+    </TableHeader>
+    <TableBody>
+      {data.map((item) => (
+        <TableRow key={item.id}>
+          <TableCell>{item.field1}</TableCell>
+          <TableCell>{item.field2}</TableCell>
+          <TableCell className="text-right">
+            <div className="flex justify-end gap-2">
+              <Button variant="ghost" size="icon" onClick={() => handleEdit(item)}>
+                <Edit className="h-4 w-4" />
+              </Button>
+              <Button variant="ghost" size="icon" onClick={() => handleDelete(item.id)}>
+                <Trash2 className="h-4 w-4" />
+              </Button>
+            </div>
+          </TableCell>
+        </TableRow>
+      ))}
+    </TableBody>
+  </Table>
+</div>
+
+{data?.data.length === 0 && !isLoading && (
+  <div className="text-center py-8">
+    <p className="text-muted-foreground">暂无数据</p>
+  </div>
+)}
+
+<DataTablePagination
+  currentPage={searchParams.page}
+  pageSize={searchParams.limit}
+  totalCount={data?.pagination.total || 0}
+  onPageChange={(page, limit) => setSearchParams(prev => ({ ...prev, page, limit }))}
+/>
+```
+
+#### 高级组件集成
+
+##### 1. DataTablePagination 分页组件
+```tsx
+import { DataTablePagination } from '@/client/admin/components/DataTablePagination';
+
+<DataTablePagination
+  currentPage={searchParams.page}
+  pageSize={searchParams.limit}
+  totalCount={data?.pagination.total || 0}
+  onPageChange={(page, limit) => setSearchParams(prev => ({ ...prev, page, limit }))}
+/>
+```
+
+##### 2. 关联实体Selector组件
+```tsx
+import AdvertisementTypeSelector from '@/client/admin/components/AdvertisementTypeSelector';
+
+<FormField
+  control={form.control}
+  name="typeId"
+  render={({ field }) => (
+    <FormItem>
+      <FormLabel>广告类型</FormLabel>
+      <FormControl>
+        <AdvertisementTypeSelector
+          value={field.value}
+          onChange={field.onChange}
+          placeholder="请选择广告类型"
+        />
+      </FormControl>
+      <FormMessage />
+    </FormItem>
+  )}
+/>
+```
+
+##### 2.2 自定义Selector开发模式
+```typescript
+// 通用Selector接口设计
+interface EntitySelectorProps {
+  value?: number;
+  onChange?: (value: number) => void;
+  placeholder?: string;
+  disabled?: boolean;
+}
+
+// 实现模式
+const EntitySelector: React.FC<EntitySelectorProps> = ({
+  value,
+  onChange,
+  placeholder = "请选择",
+  disabled
+}) => {
+  const { data } = useQuery({
+    queryKey: ['entities'],
+    queryFn: async () => {
+      const res = await entityClient.$get();
+      return await res.json();
+    }
+  });
+
+  return (
+    <Select value={value?.toString()} onValueChange={(v) => onChange?.(parseInt(v))}>
+      <SelectTrigger disabled={disabled}>
+        <SelectValue placeholder={placeholder} />
+      </SelectTrigger>
+      <SelectContent>
+        {data?.data.map((item) => (
+          <SelectItem key={item.id} value={item.id.toString()}>
+            {item.name}
+          </SelectItem>
+        ))}
+      </SelectContent>
+    </Select>
+  );
+};
+```
+
+##### 3. 图片选择器集成
+```tsx
+import ImageSelector from '@/client/admin/components/ImageSelector';
+
+<FormField
+  control={form.control}
+  name="imageFileId"
+  render={({ field }) => (
+    <FormItem>
+      <FormLabel>广告图片</FormLabel>
+      <FormControl>
+        <ImageSelector
+          value={field.value || undefined}
+          onChange={field.onChange}
+          maxSize={2} // MB
+          uploadPath="/advertisements"
+          uploadButtonText="上传广告图片"
+          previewSize="medium"
+          placeholder="选择广告图片"
+          title="选择广告图片"
+          description="上传新图片或从已有图片中选择"
+        />
+      </FormControl>
+      <FormDescription>推荐尺寸:1200x400px,支持jpg、png格式</FormDescription>
+      <FormMessage />
+    </FormItem>
+  )}
+/>
+```
+
+##### 4. 文件选择器集成
+```tsx
+import { FileSelector } from '@/client/admin/components/FileSelector';
+
+<FormField
+  control={form.control}
+  name="avatarFileId"
+  render={({ field }) => (
+    <FormItem>
+      <FormLabel>头像</FormLabel>
+      <FormControl>
+        <FileSelector
+          value={field.value || undefined}
+          onChange={(value) => field.onChange(value)}
+          maxSize={2} // MB
+          uploadPath="/avatars"
+          uploadButtonText="上传头像"
+          previewSize="medium"
+          placeholder="选择头像"
+        />
+      </FormControl>
+      <FormMessage />
+    </FormItem>
+  )}
+/>
+```
+
+#### 复杂字段展示模式
+
+##### 1. 关联实体字段展示
+```tsx
+<TableCell>
+  {advertisement.advertisementType?.name || '-'}
+</TableCell>
+```
+
+##### 2. 状态字段展示
+```tsx
+<TableCell>
+  <Badge variant={advertisement.status === 1 ? 'default' : 'secondary'}>
+    {advertisement.status === 1 ? '启用' : '禁用'}
+  </Badge>
+</TableCell>
+```
+
+##### 3. 图片字段展示
+```tsx
+<TableCell>
+  {advertisement.imageFile?.fullUrl ? (
+    <img
+      src={advertisement.imageFile.fullUrl}
+      alt={advertisement.title || '图片'}
+      className="w-16 h-10 object-cover rounded"
+      onError={(e) => {
+        e.currentTarget.src = '/placeholder.png';
+      }}
+    />
+  ) : (
+    <span className="text-muted-foreground text-xs">无图片</span>
+  )}
+</TableCell>
+```
+
+#### 表单字段类型映射
+
+##### 4.1 标准字段映射
+| 字段类型 | 组件 | 示例 |
+|----------|------|------|
+| 文本输入 | Input | `<Input placeholder="请输入标题" {...field} />` |
+| 长文本 | Textarea | `<Textarea placeholder="请输入描述" {...field} />` |
+| 选择器 | Select | `<Select value={field.value} onValueChange={field.onChange}>` |
+| 数字输入 | Input | `<Input type="number" {...field} />` |
+| 日期选择 | DatePicker | `<DatePicker selected={field.value} onChange={field.onChange} />` |
+| 开关 | Switch | `<Switch checked={field.value} onCheckedChange={field.onChange} />` |
+| 文件上传 | ImageSelector | `<ImageSelector value={field.value} onChange={field.onChange} />` |
+
+##### 4.2 关联实体选择
+```tsx
+// 直接使用Selector组件
+<FormField
+  control={form.control}
+  name="typeId"
+  render={({ field }) => (
+    <FormItem>
+      <FormLabel>广告类型</FormLabel>
+      <FormControl>
+        <AdvertisementTypeSelector {...field} />
+      </FormControl>
+    </FormItem>
+  )}
+/>
+```
+
+#### 日期格式化规范
+
+##### 4.3.1 导入依赖
+```typescript
+import { format } from 'date-fns';
+```
+
+##### 4.3.2 日期显示格式
+```tsx
+// 标准日期时间格式:yyyy-MM-dd HH:mm
+<TableCell>
+  {user.createdAt ? format(new Date(user.createdAt), 'yyyy-MM-dd HH:mm') : '-'}
+</TableCell>
+
+// 仅日期格式:yyyy-MM-dd
+<TableCell>
+  {user.birthday ? format(new Date(user.birthday), 'yyyy-MM-dd') : '-'}
+</TableCell>
+
+// 完整时间格式:yyyy-MM-dd HH:mm:ss
+<TableCell>
+  {user.updatedAt ? format(new Date(user.updatedAt), 'yyyy-MM-dd HH:mm:ss') : '-'}
+</TableCell>
+```
+
+##### 4.3.3 日期输入格式
+```tsx
+// 在表单中使用日期选择器
+<FormField
+  control={form.control}
+  name="startDate"
+  render={({ field }) => (
+    <FormItem>
+      <FormLabel>开始日期</FormLabel>
+      <FormControl>
+        <Input
+          type="date"
+          {...field}
+          value={field.value ? format(new Date(field.value), 'yyyy-MM-dd') : ''}
+          onChange={(e) => field.onChange(e.target.value)}
+        />
+      </FormControl>
+    </FormItem>
+  )}
+/>
+```
+
+##### 4.3.4 相对时间显示(可选)
+```typescript
+import { formatDistanceToNow } from 'date-fns';
+import { zhCN } from 'date-fns/locale';
+
+// 相对时间显示
+<TableCell>
+  {user.createdAt ? formatDistanceToNow(new Date(user.createdAt), { addSuffix: true, locale: zhCN }) : '-'}
+</TableCell>
+```
+
+#### 消息通知规范
+
+##### 4.4.1 导入依赖
+```typescript
+import { toast } from 'sonner';
+```
+
+##### 4.4.2 使用规范
+```typescript
+// 成功通知
+toast.success('操作成功');
+toast.success('用户创建成功');
+
+// 错误通知
+toast.error('操作失败');
+toast.error('创建用户失败,请重试');
+
+// 警告通知
+toast.warning('请确认操作');
+toast.warning('该操作将删除所有相关数据');
+
+// 信息通知
+toast.info('操作提示');
+toast.info('正在处理中,请稍候...');
+```
+
+##### 4.4.3 与API响应集成
+```typescript
+try {
+  const res = await entityClient.$post({ json: data });
+  if (res.status === 201) {
+    toast.success('创建成功');
+    setIsModalOpen(false);
+    refetch();
+  } else {
+    const error = await res.json();
+    toast.error(error.message || '操作失败');
+  }
+} catch (error) {
+  console.error('操作失败:', error);
+  toast.error('网络错误,请重试');
+}
+```
+
+#### 核心开发模式
+
+##### 类型驱动的开发
+- **RPC类型提取**: 使用 `InferRequestType` 和 `InferResponseType` 从后端API自动提取类型
+- **Schema复用**: 直接使用后端定义的Zod Schema作为表单验证
+- **类型安全**: 所有API调用都有完整的TypeScript类型支持
+
+##### 状态管理模式
+```typescript
+// 分页和搜索参数
+const [searchParams, setSearchParams] = useState({
+  page: 1,
+  limit: 10,
+  search: '',
+  // 其他筛选条件...
+});
+
+// 模态框状态
+const [isModalOpen, setIsModalOpen] = useState(false);
+const [editingEntity, setEditingEntity] = useState<any>(null);
+const [isCreateForm, setIsCreateForm] = useState(true);
+```
+
+##### 数据获取模式
+```typescript
+const { data, isLoading, refetch } = useQuery({
+  queryKey: ['entities', searchParams],
+  queryFn: async () => {
+    const res = await entityClient.$get({
+      query: {
+        page: searchParams.page,
+        pageSize: searchParams.limit,
+        keyword: searchParams.search,
+        // 其他查询参数...
+      }
+    });
+    if (res.status !== 200) throw new Error('获取列表失败');
+    return await res.json();
+  }
+});
+```
+
+#### 状态管理最佳实践
+- **表单状态**: 使用React Hook Form管理
+- **UI状态**: 使用useState管理模态框、加载状态等
+- **服务器状态**: 使用React Query管理数据获取和缓存
+- **避免不必要的状态提升**
+
+#### 数据刷新策略
+```typescript
+// 操作成功后刷新数据
+const handleCreateSubmit = async (data: CreateRequest) => {
+  try {
+    const res = await entityClient.$post({ json: data });
+    if (res.status !== 201) throw new Error('创建失败');
+    toast.success('创建成功');
+    setIsModalOpen(false);
+    refetch(); // 刷新数据
+  } catch (error) {
+    toast.error('操作失败,请重试');
+  }
+};
+```
+
+#### 加载状态处理
+
+##### 骨架屏模式
+
+###### 导入依赖
+```typescript
+import { Skeleton } from '@/client/components/ui/skeleton';
+```
+
+###### 骨架屏实现策略
+
+**1. 页面级加载(首次加载)**
+适用于首次进入页面时的完整加载,替换整个页面内容:
+```tsx
+if (isLoading && !data) {
+  return (
+    <div className="space-y-4">
+      {/* 标题区域骨架 */}
+      <div className="flex justify-between items-center">
+        <Skeleton className="h-8 w-48" />
+        <Skeleton className="h-10 w-32" />
+      </div>
+      
+      {/* 搜索区域骨架 */}
+      <Card>
+        <CardHeader>
+          <Skeleton className="h-6 w-1/4" />
+        </CardHeader>
+        <CardContent>
+          <Skeleton className="h-10 w-full max-w-sm" />
+        </CardContent>
+      </Card>
+      
+      {/* 表格骨架 */}
+      <Card>
+        <CardHeader>
+          <Skeleton className="h-6 w-1/3" />
+        </CardHeader>
+        <CardContent>
+          <Table>
+            <TableHeader>
+              <TableRow>
+                {[...Array(5)].map((_, i) => (
+                  <TableHead key={i}>
+                    <Skeleton className="h-4 w-full" />
+                  </TableHead>
+                ))}
+              </TableRow>
+            </TableHeader>
+            <TableBody>
+              {[...Array(5)].map((_, i) => (
+                <TableRow key={i}>
+                  {[...Array(5)].map((_, j) => (
+                    <TableCell key={j}>
+                      <Skeleton className="h-4 w-full" />
+                    </TableCell>
+                  ))}
+                </TableRow>
+              ))}
+            </TableBody>
+          </Table>
+        </CardContent>
+      </Card>
+    </div>
+  );
+}
+```
+
+**2. 表格级加载(搜索/翻页)**
+适用于搜索、翻页等操作时的局部加载,保持页面结构不变:
+```tsx
+<TableBody>
+  {isLoading ? (
+    // 加载状态 - 表格骨架屏
+    [...Array(5)].map((_, index) => (
+      <TableRow key={index}>
+        <TableCell><Skeleton className="w-8 h-8 rounded-full" /></TableCell>
+        <TableCell><Skeleton className="h-4 w-24" /></TableCell>
+        <TableCell><Skeleton className="h-4 w-20" /></TableCell>
+        <TableCell><Skeleton className="h-4 w-32" /></TableCell>
+        <TableCell><Skeleton className="h-4 w-16" /></TableCell>
+        <TableCell><Skeleton className="h-6 w-12" /></TableCell>
+        <TableCell><Skeleton className="h-4 w-20" /></TableCell>
+        <TableCell><Skeleton className="h-6 w-10" /></TableCell>
+        <TableCell><Skeleton className="h-4 w-24" /></TableCell>
+        <TableCell className="text-right">
+          <div className="flex justify-end gap-2">
+            <Skeleton className="h-8 w-8 rounded-md" />
+            <Skeleton className="h-8 w-8 rounded-md" />
+            <Skeleton className="h-8 w-8 rounded-md" />
+          </div>
+        </TableCell>
+      </TableRow>
+    ))
+  ) : data.length > 0 ? (
+    // 正常数据展示
+    data.map((item) => (
+      <TableRow key={item.id}>
+        {/* 数据行内容 */}
+      </TableRow>
+    ))
+  ) : (
+    // 空数据状态
+    <TableRow>
+      <TableCell colSpan={10} className="text-center py-8">
+        <p className="text-muted-foreground">暂无数据</p>
+      </TableCell>
+    </TableRow>
+  )}
+</TableBody>
+```
+
+**3. 简化骨架屏(推荐用于简单列表)**
+```tsx
+if (isLoading && !data) {
+  return (
+    <div className="space-y-4">
+      <div className="flex justify-between items-center">
+        <Skeleton className="h-8 w-48" />
+        <Skeleton className="h-10 w-32" />
+      </div>
+      
+      <Card>
+        <CardContent className="pt-6">
+          <div className="space-y-3">
+            {[...Array(5)].map((_, i) => (
+              <div key={i} className="flex gap-4">
+                <Skeleton className="h-10 flex-1" />
+                <Skeleton className="h-10 flex-1" />
+                <Skeleton className="h-10 flex-1" />
+                <Skeleton className="h-10 w-20" />
+              </div>
+            ))}
+          </div>
+        </CardContent>
+      </Card>
+    </div>
+  );
+}
+```
+
+**4. 区分加载状态的逻辑**
+```typescript
+// 使用React Query的isLoading和isFetching区分加载类型
+const { data, isLoading, isFetching } = useQuery({
+  queryKey: ['entities', searchParams],
+  queryFn: async () => {
+    const res = await entityClient.$get({
+      query: searchParams
+    });
+    if (res.status !== 200) throw new Error('获取列表失败');
+    return await res.json();
+  }
+});
+
+// 首次加载:isLoading为true,data为undefined
+// 后台刷新:isFetching为true,data有值
+
+// 可以根据不同状态显示不同的骨架屏
+const showFullSkeleton = isLoading && !data; // 首次加载
+const showTableSkeleton = isFetching && data; // 搜索/翻页加载
+
+##### 空数据状态
+```tsx
+{data?.length === 0 && !isLoading && (
+  <TableRow>
+    <TableCell colSpan={10} className="text-center py-8">
+      <p className="text-muted-foreground">暂无数据</p>
+    </TableCell>
+  </TableRow>
+)}
+```
+
+##### 最佳实践总结
+
+1. **首次加载**: 使用页面级骨架屏,替换整个页面内容
+2. **搜索/翻页**: 使用表格级骨架屏,保持页面结构不变
+3. **区分状态**: 使用 `isLoading` 和 `isFetching` 区分不同加载类型
+4. **空数据**: 在表格内部显示空数据状态,保持布局一致性
+5. **用户体验**: 避免全屏刷新,提供平滑的加载过渡
+
+##### Users.tsx 示例实现
+```tsx
+// 正确的方式:在TableBody内部处理加载状态
+<TableBody>
+  {isLoading ? (
+    // 搜索/翻页时的表格级骨架屏
+    [...Array(5)].map((_, index) => (
+      <TableRow key={index}>
+        <TableCell><Skeleton className="w-8 h-8 rounded-full" /></TableCell>
+        <TableCell><Skeleton className="h-4 w-24" /></TableCell>
+        <TableCell><Skeleton className="h-4 w-20" /></TableCell>
+        <TableCell><Skeleton className="h-4 w-32" /></TableCell>
+        <TableCell><Skeleton className="h-4 w-16" /></TableCell>
+        <TableCell><Skeleton className="h-6 w-12" /></TableCell>
+        <TableCell><Skeleton className="h-4 w-20" /></TableCell>
+        <TableCell><Skeleton className="h-6 w-10" /></TableCell>
+        <TableCell><Skeleton className="h-4 w-24" /></TableCell>
+        <TableCell className="text-right">
+          <div className="flex justify-end gap-2">
+            <Skeleton className="h-8 w-8 rounded-md" />
+            <Skeleton className="h-8 w-8 rounded-md" />
+            <Skeleton className="h-8 w-8 rounded-md" />
+          </div>
+        </TableCell>
+      </TableRow>
+    ))
+  ) : users.length > 0 ? (
+    // 正常数据展示
+    users.map((user) => (
+      <TableRow key={user.id}>
+        {/* 数据行内容 */}
+      </TableRow>
+    ))
+  ) : (
+    // 空数据状态
+    <TableRow>
+      <TableCell colSpan={10} className="text-center py-8">
+        <p className="text-muted-foreground">暂无数据</p>
+      </TableCell>
+    </TableRow>
+  )}
+</TableBody>
+
+// 错误的方式:避免在组件顶层返回骨架屏
+// if (isLoading) {
+//   return <div>全屏骨架屏...</div>; // 这会导致搜索/翻页时全屏刷新
+// }
+```
+
+##### React Query 状态管理详解
+
+```typescript
+// 使用React Query的完整状态管理
+const { 
+  data,           // 响应数据
+  isLoading,      // 首次加载(无缓存数据)
+  isFetching,     // 任何正在进行的请求
+  isError,        // 请求失败
+  error,          // 错误对象
+  refetch         // 手动重新获取数据
+} = useQuery({
+  queryKey: ['users', searchParams], // 查询键,包含所有依赖参数
+  queryFn: async () => {
+    const res = await userClient.$get({
+      query: {
+        page: searchParams.page,
+        pageSize: searchParams.pageSize,
+        keyword: searchParams.keyword,
+        // 其他查询参数...
+      }
+    });
+    if (res.status !== 200) throw new Error('获取用户列表失败');
+    return await res.json();
+  },
+  keepPreviousData: true, // 保持旧数据直到新数据到达
+  staleTime: 5 * 60 * 1000, // 数据过期时间(5分钟)
+});
+
+// 状态组合示例
+const showFullLoading = isLoading && !data;    // 首次加载,显示完整页面骨架
+const showTableLoading = isFetching && data;   // 后台刷新,显示表格骨架
+const showError = isError;                     // 显示错误状态
+const showData = !isLoading && data;           // 显示正常数据
+
+// 在组件中使用
+return (
+  <div>
+    {/* 页面标题和操作按钮区域 - 始终显示 */}
+    <div className="flex justify-between items-center">
+      <h1 className="text-2xl font-bold">用户管理</h1>
+      <Button onClick={() => setIsCreateModalOpen(true)}>
+        <Plus className="mr-2 h-4 w-4" />
+        创建用户
+      </Button>
+    </div>
+
+    {/* 搜索区域 - 始终显示 */}
+    <Card className="mt-4">
+      <CardContent className="pt-6">
+        <SearchForm onSearch={handleSearch} />
+      </CardContent>
+    </Card>
+
+    {/* 数据表格区域 */}
+    {showFullLoading && (
+      // 首次加载:完整页面骨架
+      <FullPageSkeleton />
+    )}
+    
+    {showTableLoading && (
+      // 后台刷新:表格骨架(保持页面结构)
+      <TableSkeleton />
+    )}
+    
+    {showError && (
+      // 错误状态
+      <ErrorState error={error} onRetry={refetch} />
+    )}
+    
+    {showData && (
+      // 正常数据展示
+      <DataTable data={data} />
+    )}
+  </div>
+);
+```
+
+### 3. 功能实现
+
+#### 数据表格
+- 实现分页、搜索、排序功能
+- 集成DataTablePagination组件
+- 添加骨架屏加载状态
+- 处理空数据状态
+
+#### 表单模态框
+- 创建和编辑表单分离实现
+- 独立的表单实例管理
+- 完整的表单验证
+- 图片和文件上传集成
+
+#### 表单组件结构
+```typescript
+// 创建表单
+const createForm = useForm<CreateRequest>({
+  resolver: zodResolver(createFormSchema),
+  defaultValues: {
+    // 默认值设置
+  },
+});
+
+// 更新表单
+const updateForm = useForm<UpdateRequest>({
+  resolver: zodResolver(updateFormSchema),
+  defaultValues: {
+    // 更新时默认值
+  },
+});
+```
+
+#### 模态框表单(创建/编辑分离模式)
+```tsx
+<Dialog open={isModalOpen} onOpenChange={setIsModalOpen}>
+  <DialogContent className="sm:max-w-[500px] max-h-[90vh] overflow-y-auto">
+    <DialogHeader>
+      <DialogTitle>{isCreateForm ? '创建实体' : '编辑实体'}</DialogTitle>
+      <DialogDescription>
+        {isCreateForm ? '创建一个新的实体' : '编辑现有实体信息'}
+      </DialogDescription>
+    </DialogHeader>
+    
+    {isCreateForm ? (
+      // 创建表单(独立渲染)
+      <Form {...createForm}>
+        <form onSubmit={createForm.handleSubmit(handleCreateSubmit)} className="space-y-4">
+          {/* 创建专用字段 */}
+          <DialogFooter>
+            <Button type="button" variant="outline" onClick={() => setIsModalOpen(false)}>
+              取消
+            </Button>
+            <Button type="submit">创建</Button>
+          </DialogFooter>
+        </form>
+      </Form>
+    ) : (
+      // 编辑表单(独立渲染)
+      <Form {...updateForm}>
+        <form onSubmit={updateForm.handleSubmit(handleUpdateSubmit)} className="space-y-4">
+          {/* 编辑专用字段 */}
+          <DialogFooter>
+            <Button type="button" variant="outline" onClick={() => setIsModalOpen(false)}>
+              取消
+            </Button>
+            <Button type="submit">更新</Button>
+          </DialogFooter>
+        </form>
+      </Form>
+    )}
+  </DialogContent>
+</Dialog>
+```
+
+#### 表单字段模式
+```tsx
+<FormField
+  control={form.control}
+  name="fieldName"
+  render={({ field }) => (
+    <FormItem>
+      <FormLabel className="flex items-center">
+        字段标签
+        {isRequired && <span className="text-red-500 ml-1">*</span>}
+      </FormLabel>
+      <FormControl>
+        <Input placeholder="请输入..." {...field} />
+      </FormControl>
+      <FormDescription>字段描述信息</FormDescription>
+      <FormMessage />
+    </FormItem>
+  )}
+/>
+```
+
+#### 操作功能
+- 创建、编辑、删除操作
+- 删除确认对话框
+- 操作成功后的数据刷新
+- 错误处理和用户反馈
+
+#### 删除确认模式
+```tsx
+const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
+const [entityToDelete, setEntityToDelete] = useState<number | null>(null);
+
+// 删除确认对话框
+<Dialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
+  <DialogContent>
+    <DialogHeader>
+      <DialogTitle>确认删除</DialogTitle>
+      <DialogDescription>
+        确定要删除这个实体吗?此操作无法撤销。
+      </DialogDescription>
+    </DialogHeader>
+    <DialogFooter>
+      <Button variant="outline" onClick={() => setDeleteDialogOpen(false)}>
+        取消
+      </Button>
+      <Button variant="destructive" onClick={confirmDelete}>
+        删除
+      </Button>
+    </DialogFooter>
+  </DialogContent>
+</Dialog>
+
+// 删除成功状态码为204
+const confirmDelete = async () => {
+  if (!entityToDelete) return;
+  
+  try {
+    const res = await entityClient[':id']['$delete']({
+      param: { id: entityToDelete.toString() }
+    });
+    
+    if (res.status === 204) {
+      toast.success('删除成功');
+      setDeleteDialogOpen(false);
+      refetch(); // 刷新数据
+    } else {
+      throw new Error('删除失败');
+    }
+  } catch (error) {
+    toast.error('删除失败,请重试');
+  }
+};
+```
+
+#### API错误处理
+```typescript
+try {
+  const res = await entityClient.$post({ json: data });
+  if (res.status !== 201) throw new Error('操作失败');
+  toast.success('操作成功');
+} catch (error) {
+  console.error('操作失败:', error);
+  toast.error('操作失败,请重试');
+}
+```
+
+### 4. 样式和用户体验
+
+#### 布局规范
+- 页面标题区域:flex布局,右侧操作按钮
+- 搜索区域:Card组件包含搜索表单
+- 数据表格:标准Table组件
+- 分页控件:底部居中显示
+
+#### 响应式设计
+- 模态框:sm:max-w-[500px] max-h-[90vh]
+- 搜索框:max-w-sm限制宽度
+- 表格:自适应布局
+
+#### 视觉层次
+- 标题:text-2xl font-bold
+- 卡片标题:text-lg font-semibold
+- 描述文字:text-sm text-muted-foreground
+
+#### 间距系统
+- 页面标题区域: `space-y-4`
+- 卡片内容: `space-y-4`
+- 表单字段: `space-y-4`
+- 按钮组: `gap-2`
+
+## 项目规范合规性
+
+### 1. RPC调用规范(基于 .roo/rules/08-rpc.md)
+✅ **类型提取语法**:
+```typescript
+// 正确
+InferResponseType<typeof client[':id']['$get'], 200>
+
+// 错误
+InferResponseType<typeof client[':id'].$get, 200>
+```
+
+✅ **类型命名规范**:
+- 响应类型: `[ResourceName]`
+- 请求类型: `[ResourceName]Post` 或 `[ResourceName]Put`
+
+### 2. 客户端API规范
+✅ **客户端初始化**:
+```typescript
+import { hc } from 'hono/client';
+import { AuthRoutes } from '@/server/api';
+
+export const authClient = hc<AuthRoutes>('/', {
+  fetch: axiosFetch,
+}).api.v1.auth;
+```
+
+✅ **方法调用**: 使用解构方式组织RPC方法
+
+## 最佳实践
+
+### 类型安全
+- 使用InferRequestType/InferResponseType自动提取类型
+- 表单直接使用后端Schema验证
+- 所有API调用都有完整TypeScript类型支持
+
+### 状态管理
+- 服务器状态:React Query管理
+- UI状态:useState管理
+- 表单状态:React Hook Form管理
+- 避免不必要的状态提升
+
+### 错误处理
+- API错误统一处理
+- 用户友好的错误提示
+- 操作失败后的状态回滚
+- 网络错误的优雅降级
+
+### 性能优化
+- 数据分页加载
+- 组件懒加载
+- 图片懒加载
+- 查询结果缓存
+
+## 开发检查清单
+
+完成每个管理页面后,检查以下项目:
+
+### 前端检查项
+✅ 类型定义完整且正确(使用InferRequestType/InferResponseType)
+✅ 表单验证使用后端Zod Schema
+✅ 分页搜索功能正常
+✅ 创建/编辑模态框分离实现
+✅ 删除确认对话框完整
+✅ 骨架屏加载状态实现
+✅ 空数据状态处理
+✅ 错误处理和用户反馈完善
+✅ 响应式设计适配
+✅ 代码注释和文档完整
+
+### 功能检查项
+✅ 数据表格显示正常
+✅ 分页控件工作正常
+✅ 搜索功能有效
+✅ 创建操作成功
+✅ 编辑操作成功  
+✅ 删除操作成功
+✅ 图片/文件上传集成(如需要)
+✅ 关联实体选择器集成(如需要)
+
+### 用户体验检查项
+✅ 加载状态友好
+✅ 错误提示清晰
+✅ 操作反馈及时
+✅ 表单验证提示明确
+✅ 移动端适配良好
+
+### 协作检查项
+✅ 与后端API接口一致
+✅ 类型定义与后端同步
+✅ Schema验证规则匹配
+
+### 规范合规检查项
+✅ RPC类型提取语法正确(中括号访问方法)
+✅ 类型命名规范遵循项目标准
+✅ 客户端API初始化格式正确
+
+## 工具使用
+
+优先使用以下工具进行开发:
+- **Read**: 分析现有代码模式和最佳实践
+- **Grep**: 搜索相关文件、类型定义和模式
+- **Edit**: 修改现有文件和代码
+- **Write**: 创建新的实体、服务、路由和页面文件
+- **Glob**: 查找相关文件和模式
+- **Bash**: 运行构建命令、测试和开发服务器
+
+### 开发命令参考
+```bash
+# 开发服务器
+npm run dev
+
+# 构建检查
+npm run build
+
+# 类型检查
+npm run typecheck
+
+# 代码格式化
+npm run lint
+```
+
+## 开发流程
+
+### 1. 创建新管理页面
+1. 复制 `Users.tsx` 作为模板
+2. 替换以下部分:
+   - API客户端导入
+   - 类型定义
+   - 表单Schema引用
+   - 页面标题和描述
+   - 表格列定义
+   - 表单字段定义
+3. 根据业务需求调整字段和逻辑
+
+### 2. 字段映射规范
+- **文本字段**: 使用 `Input`
+- **长文本**: 使用 `Textarea`
+- **选择字段**: 使用 `Select`
+- **开关字段**: 使用 `Switch`
+- **日期字段**: 使用 `DatePicker`
+- **图片字段**: 使用 `FileSelector`
+
+### 3. 业务逻辑复用
+- 保持相同的CRUD操作模式
+- 复用分页、搜索、排序逻辑
+- 统一的状态管理模式
+- 一致的表单验证和错误处理
+
+## 主动行为
+
+**PROACTIVELY** 检测以下情况并主动行动:
+- 发现后端有新实体但缺少对应的管理页面
+- 现有管理页面不符合最新UI规范
+- 缺少必要的用户体验功能
+- 前端性能优化机会
+- 响应式设计需要改进
+- 与后端接口不一致的情况
+
+始终保持前端代码的:
+- **一致性**: 遵循Shadcn-ui设计规范
+- **可维护性**: 清晰的组件结构和状态管理
+- **最佳实践**: 使用最新的React和前端技术
+- **类型安全**: 完整的TypeScript类型支持
+- **用户体验**: 优秀的界面设计和交互体验
+- **协作性**: 与后端API良好配合

+ 8 - 0
.claude/commands/auth-jumpback-增加登录来源页回跳.md

@@ -0,0 +1,8 @@
+---
+description: "增加登录来源页回跳功能指令"
+---
+先 执行 .roo/commands/auth-check-认证相关文件检查.md
+
+然后
+增加 登录来源页回跳功能
+例始打开 /mobile/stock?classroom=J001&code=000001 时,判断未登录,去登录,登录后自动跳回这个页面

+ 15 - 0
.claude/commands/auth-register-注册登录方式添加.md

@@ -0,0 +1,15 @@
+---
+description: "注册登录方式添加"
+---
+
+先检查
+    当前用户认证的路由:
+    src/server/api/auth
+    src/server/api/auth/register/create.ts
+    src/server/api/auth/login/password.ts
+    src/server/api/auth/logout.ts
+
+    用户实体:
+    src/server/modules/users/user.entity.ts
+    src/server/modules/users/user.schema.ts
+    src/server/modules/users/user.service.ts

+ 207 - 0
.claude/commands/auth-使用简单注册路由.md

@@ -0,0 +1,207 @@
+---
+description: "使用简单注册路由"
+---
+
+## 简单注册路由使用指南
+
+### 新增功能
+已添加简单注册路由,支持只输入用户名即可完成注册,自动处理用户名冲突。
+
+### API端点
+- **方法**: POST
+- **路径**: `/api/v1/auth/register/simple`
+- **Content-Type**: `application/json`
+
+### 请求参数
+```json
+{
+  "username": "用户名"
+}
+```
+
+### 响应格式
+**成功响应 (201 Created)**:
+```json
+{
+  "token": "jwt.token.here",
+  "user": {
+    "id": 123,
+    "username": "用户名1234"
+  }
+}
+```
+
+**错误响应**:
+```json
+{
+  "code": 500,
+  "message": "错误信息"
+}
+```
+
+### 使用示例
+
+#### cURL示例
+```bash
+# 基本使用
+curl -X POST http://localhost:3000/api/v1/auth/register/simple \
+  -H "Content-Type: application/json" \
+  -d '{"username": "testuser"}'
+
+# 带详细输出
+curl -X POST http://localhost:3000/api/v1/auth/register/simple \
+  -H "Content-Type: application/json" \
+  -d '{"username": "testuser"}' \
+  -i
+```
+
+#### JavaScript/TypeScript示例
+```typescript
+import { authClient } from '@/client/api';
+import type { InferResponseType, InferRequestType } from 'hono/client';
+
+// 使用RPC客户端
+const registerSimple = async (username: string) => {
+  const response = await authClient.register.simple.$post({
+    json: { username }
+  });
+  
+  if (response.status === 201) {
+    const data = await response.json();
+    console.log('注册成功:', data);
+    return data;
+  } else {
+    const error = await response.json();
+    throw new Error(error.message);
+  }
+};
+
+// 类型定义(可选)
+type SimpleRegisterRequest = InferRequestType<typeof authClient.register.simple.$post>['json'];
+type SimpleRegisterResponse = InferResponseType<typeof authClient.register.simple.$post, 201>;
+
+// 使用示例
+try {
+  const result = await registerSimple('myusername');
+  localStorage.setItem('token', result.token);
+  console.log('用户已注册并登录:', result.user);
+} catch (error) {
+  console.error('注册失败:', error.message);
+}
+```
+
+#### React示例
+```typescript
+import { useState } from 'react';
+import { authClient } from '@/client/api';
+
+const SimpleRegisterForm = () => {
+  const [username, setUsername] = useState('');
+  const [loading, setLoading] = useState(false);
+
+  const handleSubmit = async (e: React.FormEvent) => {
+    e.preventDefault();
+    setLoading(true);
+    
+    try {
+      const response = await authClient.register.simple.$post({
+        json: { username }
+      });
+      
+      if (response.ok) {
+        const data = await response.json();
+        // 处理登录成功
+        localStorage.setItem('token', data.token);
+        alert(`注册成功!用户名: ${data.user.username}`);
+      } else {
+        const error = await response.json();
+        alert(`注册失败: ${error.message}`);
+      }
+    } catch (error) {
+      alert('网络错误,请重试');
+    } finally {
+      setLoading(false);
+    }
+  };
+
+  return (
+    <form onSubmit={handleSubmit}>
+      <input
+        type="text"
+        value={username}
+        onChange={(e) => setUsername(e.target.value)}
+        placeholder="输入用户名"
+        minLength={3}
+        required
+      />
+      <button type="submit" disabled={loading}>
+        {loading ? '注册中...' : '快速注册'}
+      </button>
+    </form>
+  );
+};
+```
+
+### RPC使用说明
+
+本项目使用Hono RPC客户端进行类型安全的API调用,提供以下优势:
+
+1. **类型安全**: 自动推断请求和响应类型
+2. **代码提示**: IDE提供完整的代码补全和类型检查
+3. **错误处理**: 统一的错误处理机制
+4. **路径自动补全**: 自动生成正确的API路径
+
+#### 类型定义示例
+```typescript
+import type { InferResponseType, InferRequestType } from 'hono/client';
+import { authClient } from '@/client/api';
+
+// 请求类型定义
+type SimpleRegisterRequest = InferRequestType<typeof authClient.register.simple.$post>['json'];
+
+// 响应类型定义
+type SimpleRegisterResponse = InferResponseType<typeof authClient.register.simple.$post, 201>;
+
+// 错误响应类型
+type ErrorResponse = InferResponseType<typeof authClient.register.simple.$post, 400 | 500>;
+```
+
+### 功能特点
+
+1. **自动用户名处理**:
+   - 如果用户名已存在,自动在末尾添加4位随机数字
+   - 例如: "testuser" → "testuser1234"
+
+2. **默认密码**:
+   - 系统自动设置默认密码为 "123456"
+   - 用户可在后续修改密码
+
+3. **错误处理**:
+   - 用户名格式错误返回400状态码
+   - 服务器错误返回500状态码
+   - 用户名生成失败返回详细错误信息
+
+### 测试建议
+
+1. **测试用户名冲突**:
+   ```bash
+   # 第一次注册
+   curl -X POST http://localhost:3000/api/v1/auth/register/simple \
+     -d '{"username": "testuser"}'
+   
+   # 第二次注册相同用户名
+   curl -X POST http://localhost:3000/api/v1/auth/register/simple \
+     -d '{"username": "testuser"}'
+   ```
+
+2. **验证token有效性**:
+   ```bash
+   # 使用返回的token访问受保护接口
+   curl -H "Authorization: Bearer <token>" \
+     http://localhost:3000/api/v1/auth/me
+   ```
+
+### 相关文件
+- 路由实现: `src/server/api/auth/register/simple.ts`
+- 用户服务: `src/server/modules/users/user.service.ts`
+- 路由注册: `src/server/api/auth/index.ts`

+ 6 - 0
.claude/commands/check-api-实体与页面字段对应检查.md

@@ -0,0 +1,6 @@
+---
+description: "检查api相关的实体,schema 与 页面字段是否对应指令"
+---
+
+检查api相关的实体,schema 定义,看与 页面字段是否对应
+没对应的就修改页面字段以与实体,schema对应

+ 28 - 0
.claude/commands/check-auth-认证相关文件检查.md

@@ -0,0 +1,28 @@
+---
+description: "认证相关文件检查指令"
+---
+
+认证相关文件检查,为后续认证相关开发提供上下文
+如果是mobile的开发
+    先检查
+        当前用户认证的路由:
+        src/server/api/auth
+        src/server/api/auth/register/create.ts
+        src/server/api/auth/login/password.ts
+        src/server/api/auth/logout.ts
+
+        中间件:
+        src/server/middleware/auth.middleware.ts
+
+        用户实体:
+        src/server/modules/users/user.entity.ts
+        src/server/modules/users/user.schema.ts
+        src/server/modules/users/user.service.ts
+
+        用户管理页:
+        src/client/admin/pages/Users.tsx
+
+        mobile前端:
+        src/client/mobile/components/ProtectedRoute.tsx
+        src/client/mobile/hooks/AuthProvider.tsx
+        src/client/mobile/pages/AuthPage.tsx

+ 5 - 0
.claude/commands/check-curd-页面实体路由对应检查.md

@@ -0,0 +1,5 @@
+---
+description: "检查页面相关的实体,schema, CRUD路由指令"
+---
+
+检查页面相关的实体,schema, CRUD路由,src/server/api.ts, src/client/api.ts中的rpc client 定义,以收集进行页面开发所需的上下文

+ 132 - 0
.claude/commands/check-live-直播相关文件检查.md

@@ -0,0 +1,132 @@
+---
+description: "直播相关文件检查指令"
+---
+
+本指令用于检查项目中直播相关的文件实现情况,为后续直播功能开发和维护提供上下文信息。
+
+## 检查范围
+
+### 1. 前端直播组件 (`src/client/mobile/components/Classroom/`)
+- **useClassroom.ts**: 直播核心逻辑Hook,包含阿里云RTC和IM SDK集成
+- **ClassroomLayout.tsx**: 直播界面布局组件
+- **ClassroomProvider.tsx**: 直播上下文提供者
+- **AuthLayout.tsx**: 认证布局组件
+- **alivc-im.iife.d.ts**: 阿里云IM SDK类型定义
+
+### 2. 直播页面 (`src/client/mobile/pages/ClassroomPage.tsx`)
+- 直播页面入口,包含角色选择、课堂创建/加入功能
+- 阿里云RTC SDK类型定义  node_modules/.pnpm/aliyun-rtc-sdk@7.1.2_@tensorflow+tfjs-core@4.22.0_debug@4.4.1_seedrandom@3.0.5/node_modules/aliyun-rtc-sdk/dist/types/index.d.ts
+
+### 3. 后端API (`src/server/api/aliyun/index.ts`)
+- IM Token生成接口
+- RTC Token生成接口
+
+### 4. 客户端API (`src/client/api.ts`)
+- 阿里云服务客户端定义
+
+## 详细检查结果
+
+### 前端直播组件实现情况
+
+#### useClassroom.ts (直播核心逻辑)
+- ✅ **阿里云RTC SDK集成**: 使用 `aliyun-rtc-sdk` 进行实时音视频通信
+- ✅ **阿里云IM SDK集成**: 使用 `alivc-im` 进行即时消息通信
+- ✅ **角色管理**: 支持老师(Teacher)和学生(Student)两种角色
+- ✅ **课堂状态管理**: 支持未开始(IN_PROGRESS)、进行中(IN_PROGRESS)、已结束(ENDED)三种状态
+- ✅ **音视频控制**: 摄像头开关、麦克风开关、屏幕分享
+- ✅ **消息功能**: 文本消息、举手功能、问题提问
+- ✅ **课堂管理**: 创建课堂、加入课堂、离开课堂、开始/结束课堂
+- ✅ **用户管理**: 静音学生、移出学生、应答举手
+
+#### ClassroomLayout.tsx (界面布局)
+- ✅ **视频区域**: 主屏幕共享容器 + 摄像头小窗容器
+- ✅ **控制面板**: 摄像头、麦克风、屏幕分享控制按钮
+- ✅ **消息区域**: 实时消息显示和发送
+- ✅ **响应式设计**: 支持移动端和桌面端布局
+
+### 后端API实现情况
+
+#### 阿里云服务API (`/api/v1/aliyun`)
+- ✅ **IM Token生成**: POST `/im_token` - 生成即时消息认证令牌
+- ✅ **RTC Token生成**: POST `/rtc_token` - 生成实时通信认证令牌
+- ✅ **安全认证**: 使用SHA-256加密生成Token
+- ✅ **错误处理**: 完整的错误响应机制
+
+### 技术栈和依赖
+
+#### 前端SDK
+- **阿里云RTC SDK**: 实时音视频通信
+- **阿里云IM SDK**: 即时消息通信
+- **React Hooks**: 状态管理和副作用处理
+
+#### 后端依赖
+- **环境变量配置**: IM_APP_ID, IM_APP_KEY, IM_APP_SIGN, RTC_APP_ID, RTC_APP_KEY
+- **加密算法**: SHA-256哈希算法生成认证Token
+
+## 功能特性
+
+### 已实现功能
+1. **基础直播功能**
+   - 音视频通话
+   - 屏幕共享
+   - 实时消息
+   - 举手互动
+   - 问题提问
+
+2. **课堂管理**
+   - 创建课堂
+   - 加入课堂
+   - 开始/结束课堂
+   - 用户管理
+
+3. **权限控制**
+   - 老师权限: 管理课堂、静音学生、应答举手
+   - 学生权限: 观看直播、举手提问、发送消息
+
+
+## 使用示例
+
+### 创建课堂 (老师)
+```typescript
+const { createClass } = useClassroomContext();
+const classId = await createClass('数学课堂');
+```
+
+### 加入课堂 (学生)
+```typescript
+const { joinClass } = useClassroomContext();
+await joinClass('classroom_123');
+```
+
+### 发送消息
+```typescript
+const { sendMessage, msgText, setMsgText } = useClassroomContext();
+setMsgText('大家好!');
+await sendMessage();
+```
+
+### 举手提问
+```typescript
+const { handUp } = useClassroomContext();
+await handUp('我有一个问题');
+```
+
+## 环境配置要求
+
+### 必需环境变量
+```bash
+# 阿里云IM配置
+IM_APP_ID=your_im_app_id
+IM_APP_KEY=your_im_app_key
+IM_APP_SIGN=your_im_app_sign
+
+# 阿里云RTC配置
+RTC_APP_ID=your_rtc_app_id
+RTC_APP_KEY=your_rtc_app_key
+```
+
+### SDK引入
+前端需要引入阿里云SDK:
+```html
+<script src="https://g.alicdn.com/apsara-media-box/imp-interaction/1.6.1/alivc-im.iife.js"></script>
+```

+ 5 - 0
.claude/commands/check-socket-页面实体套接字上下文检查.md

@@ -0,0 +1,5 @@
+---
+description: "检查页面相关的实体,套接字事件处理器指令"
+---
+
+检查页面相关的实体,src/server/socket.ts中的事件处理器 定义,以收集进行页面开发所需的上下文

+ 131 - 0
.claude/commands/check-trainee-学员相关文件检查.md

@@ -0,0 +1,131 @@
+---
+description: "学员相关文件检查指令"
+---
+
+本指令用于检查项目中学员相关的文件实现情况,为后续学员功能开发和维护提供上下文信息。
+
+## 检查范围
+
+### 1. 用户实体和Schema (`src/server/modules/users/`)
+- **user.entity.ts**: 用户实体类,包含学员有效期字段定义
+- **user.schema.ts**: 用户数据验证Schema,包含学员相关字段验证
+- **user.enum.ts**: 用户类型枚举,包含TRAINEE类型定义
+
+### 2. 用户服务 (`src/server/modules/users/user.service.ts`)
+- 学员身份管理功能:设置学员、撤销学员、更新学员有效期
+- 学员过期状态检查功能
+
+### 3. 学员过期检查中间件 (`src/server/middleware/trainee-expiry.middleware.ts`)
+- 认证后立即检查学员过期状态
+- 自动更新过期学员身份
+
+### 4. 用户管理页面 (`src/client/admin/pages/Users.tsx`)
+- 学员设置和撤销功能界面
+- 学员有效期显示和管理
+
+### 5. 认证中间件 (`src/server/middleware/auth.middleware.ts`)
+- 集成学员过期检查功能
+
+## 详细检查结果
+
+### 用户实体实现情况
+
+#### user.entity.ts (数据库实体)
+- ✅ **学员有效期字段**: `traineeValidFrom` (有效期开始时间)
+- ✅ **学员有效期字段**: `traineeValidTo` (有效期结束时间)  
+- ✅ **用户类型字段**: `userType` 支持 TRAINEE 类型
+- ✅ **字段注释**: 所有字段都有中文注释说明
+
+#### user.schema.ts (数据验证)
+- ✅ **学员字段验证**: `traineeValidFrom` 和 `traineeValidTo` 使用 `z.coerce.date()` 验证
+- ✅ **OpenAPI元数据**: 包含完整的示例值和描述信息
+- ✅ **类型转换**: 支持字符串到日期的自动转换
+
+#### user.enum.ts (用户类型枚举)
+- ✅ **TRAINEE类型**: 定义学员用户类型常量
+- ✅ **完整类型体系**: TEACHER、STUDENT、TRAINEE 三种用户类型
+
+### 用户服务实现情况
+
+#### user.service.ts (业务逻辑)
+- ✅ **设置学员身份**: `setUserAsTrainee()` - 设置用户为学员并指定有效期
+- ✅ **撤销学员身份**: `removeUserTraineeStatus()` - 将学员恢复为学生身份
+- ✅ **更新学员有效期**: `updateTraineeValidity()` - 修改学员的有效期
+- ✅ **过期状态检查**: `checkAndUpdateTraineeStatus()` - 自动检查并处理过期学员
+- ✅ **事务安全**: 所有操作都包含错误处理和日志记录
+
+### 中间件实现情况
+
+#### trainee-expiry.middleware.ts (过期检查)
+- ✅ **立即检查**: 在认证成功后立即执行学员过期检查
+- ✅ **自动更新**: 检测到过期学员后自动更新用户类型
+- ✅ **上下文更新**: 更新认证上下文中的用户信息
+
+#### auth.middleware.ts (认证集成)
+- ✅ **中间件集成**: 在认证流程中集成学员过期检查
+- ✅ **无缝衔接**: 用户认证后自动进行学员状态验证
+
+### 前端管理界面实现情况
+
+#### Users.tsx (用户管理)
+- ✅ **学员状态显示**: 在用户列表中显示学员有效期信息
+- ✅ **设置学员功能**: 提供设置用户为学员的对话框界面
+- ✅ **撤销学员功能**: 提供撤销学员身份的确认对话框
+- ✅ **日期选择器**: 支持选择学员有效期的开始和结束时间
+- ✅ **状态反馈**: 使用toast消息提示操作结果
+
+## 功能特性
+
+### 已实现功能
+1. **学员身份管理**
+   - 设置用户为学员并指定有效期
+   - 撤销学员身份恢复为学生
+   - 更新学员有效期
+
+2. **自动过期处理**
+   - 认证时自动检查学员过期状态
+   - 过期学员自动恢复为学生身份
+   - 实时更新用户上下文信息
+
+3. **管理界面**
+   - 可视化设置和撤销学员身份
+   - 学员有效期显示和管理
+   - 操作结果反馈
+
+### 技术实现
+- **数据库字段**: 使用timestamp类型存储有效期时间
+- **类型安全**: 完整的TypeScript类型定义
+- **验证机制**: Zod schema验证确保数据完整性
+- **错误处理**: 完善的错误处理和日志记录
+- **事务安全**: 数据库操作的事务保障
+
+## 使用示例
+
+### 设置用户为学员
+```typescript
+// 后端服务调用
+const userService = new UserService(dataSource);
+const updatedUser = await userService.setUserAsTrainee(
+  userId, 
+  new Date('2024-01-01'), 
+  new Date('2024-12-31'),
+  operatorId
+);
+
+// 前端API调用  
+const res = await userClient[':id']['$put']({
+  param: { id: userId },
+  json: {
+    userType: UserType.TRAINEE,
+    traineeValidFrom: '2024-01-01T00:00:00Z',
+    traineeValidTo: '2024-12-31T23:59:59Z'
+  }
+});
+```
+
+### 检查学员过期状态
+```typescript
+// 自动在认证中间件中执行
+// 手动检查
+const isExpired = await userService.checkAndUpdateTraineeStatus(userId);
+```

+ 12 - 0
.claude/commands/check-user-用户相关文件检查.md

@@ -0,0 +1,12 @@
+---
+description: "用户相关文件检查指令"
+---
+
+用户相关文件检查,为后续认证相关开发提供上下文
+如果是mobile的开发
+    先检查
+
+        用户实体:
+        src/server/modules/users/user.entity.ts
+        src/server/modules/users/user.schema.ts
+        src/server/modules/users/user.service.ts

+ 6 - 0
.claude/commands/check-实体与验证规则对应检查.md

@@ -0,0 +1,6 @@
+---
+description: "检查schema与实体是否对应指令"
+---
+
+检查schema与实体是否对应
+没对应的修改 schema

+ 127 - 0
.claude/commands/check-移动端答题卡检查.md

@@ -0,0 +1,127 @@
+---
+description: "移动端答题卡组件检查指令"
+---
+
+此指令用于在修改移动端答题卡组件前提供完整的上下文信息,包括组件结构、关键功能、依赖关系和潜在风险点。
+
+## 组件概述
+
+### 1. 核心文件结构
+```
+src/client/mobile/components/Exam/
+├── ExamCard.tsx          # 答题卡主组件
+├── ExamAdmin.tsx         # 答题卡管理组件  
+├── ExamIndex.tsx         # 答题卡入口页面
+├── types.ts              # 类型定义
+└── hooks/
+    └── useSocketClient.ts # Socket连接Hook
+```
+
+### 2. 主要功能特性
+
+#### ExamCard.tsx 核心功能:
+- **用户答题界面**:A/B选项选择(持股/持币)
+- **实时数据同步**:通过Socket.io连接服务器
+- **收益计算**:自动计算累计收益率
+- **历史记录显示**:展示用户答题历史
+- **状态管理**:训练开始/结束状态控制
+
+#### 关键状态变量:
+```typescript
+const [currentDate, setCurrentDate] = useState('');        // 当前训练日期
+const [currentPrice, setCurrentPrice] = useState('0');     // 当前价格
+const [holdingStock, setHoldingStock] = useState('0');     // 持股状态
+const [holdingCash, setHoldingCash] = useState('0');       // 持币状态
+const [isStarted, setIsStarted] = useState(false);         // 训练开始状态
+const [answerRecords, setAnswerRecords] = useState<AnswerRecord[]>([]); // 答题记录
+const [totalProfitRate, setTotalProfitRate] = useState(0); // 累计收益率
+```
+
+### 3. Socket事件处理
+
+#### 监听的事件:
+- `exam:question` - 新问题推送
+- `exam:cleaned` - 训练重置
+- `exam:settle` - 结算处理
+
+#### 发送的事件:
+- 通过 `answerManagement.storeAnswer()` 提交答案
+- 通过 Socket 连接状态管理
+
+### 4. 依赖关系检查
+
+#### 前端依赖:
+```typescript
+import React, { useState, useCallback, useEffect } from 'react';
+import { useQuery } from '@tanstack/react-query';
+import { useSearchParams, useNavigate } from "react-router";
+import dayjs from 'dayjs';
+import { useSocketClient } from './hooks/useSocketClient';
+import { classroomDataClient } from '@/client/api';
+import { useAuth } from '@/client/mobile/hooks/AuthProvider';
+import { ClassroomStatus } from '@/server/modules/classroom/classroom-data.schema';
+import { toast } from 'react-toastify';
+```
+
+#### API客户端:
+- `classroomDataClient` - 教室数据查询
+- Socket客户端 - 实时通信
+
+### 5. 关键业务逻辑
+
+#### 答案提交逻辑:
+```typescript
+const handleChooseA = useCallback(async () => {
+  setHoldingStock('1');
+  setHoldingCash('0');
+  
+  if (classroom && user?.username && currentDate) {
+    const answer = {
+      date: currentDate,
+      holdingStock: '1',
+      holdingCash: '0',
+      userId: user.id,
+      username: user.username,
+      price: currentPrice
+    };
+    
+    await answerManagement.storeAnswer(...);
+  }
+}, [classroom, user, currentDate, currentPrice, answerManagement]);
+```
+
+#### 收益计算:
+```typescript
+// 计算累计收益率(单日涨幅相加)
+const totalRate = answers.reduce((sum, answer) => sum + (answer.profitPercent || 0), 0);
+setTotalProfitRate(totalRate);
+```
+
+### 6. UI组件结构
+
+#### 主要UI区块:
+1. **连接状态提示** - Socket连接状态显示
+2. **选择按钮区域** - A/B选项按钮
+3. **信息显示区域** - 用户信息和累计收益
+4. **历史记录表格** - 答题历史记录展示
+
+#### 样式类名:
+- 使用Tailwind CSS类名
+- 响应式设计:`max-w-2xl`, `grid grid-cols-6`
+- 状态颜色:红色(持股)、绿色(持币)
+
+### 7. 潜在修改风险点
+
+#### 高风险区域:
+1. **Socket事件处理** - 修改可能影响实时通信
+2. **状态管理** - 多个useState需要保持同步
+3. **收益计算逻辑** - 影响用户收益显示
+
+#### 中等风险区域:
+1. **UI布局** - 修改可能影响移动端显示
+2. **API调用** - 教室数据获取逻辑
+
+#### 低风险区域:
+1. **样式调整** - 颜色、间距等视觉修改
+2. **文本内容** - 提示信息修改
+

+ 197 - 0
.claude/commands/check-移动端股票图表检查.md

@@ -0,0 +1,197 @@
+---
+description: "移动端股票图表检查指令"
+---
+
+此指令用于在修改移动端股票图表组件前提供完整的上下文检查,包括组件结构、依赖关系、API接口和关键实现细节。
+
+## 当前组件结构
+
+### 1. 主要文件结构
+```
+src/client/mobile/components/stock/
+├── stock_main.tsx              # 主入口组件
+├── hooks/
+│   └── useStockSocketClient.ts # WebSocket连接hook
+├── components/
+│   └── stock-chart/            # 股票图表核心组件
+│       ├── mod.ts              # 模块导出文件
+│       └── src/
+│           ├── components/     # UI组件
+│           │   ├── StockChart.tsx      # 主图表组件
+│           │   ├── MemoToggle.tsx      # 备忘录显示切换
+│           │   ├── TradePanel.tsx      # 交易面板
+│           │   ├── ProfitDisplay.tsx   # 收益显示
+│           │   └── DrawingToolbar.tsx  # 画线工具栏
+│           ├── hooks/          # 自定义hooks
+│           │   ├── useStockQueries.ts      # 数据查询hook
+│           │   ├── useTradeRecords.ts      # 交易记录hook
+│           │   ├── useProfitCalculator.ts  # 收益计算hook
+│           │   └── useStockDataFilter.ts   # 数据过滤hook
+│           ├── lib/            # 核心库
+│           │   ├── index.ts                # 主库入口
+│           │   ├── StockChart.ts           # 图表逻辑
+│           │   ├── DateMemoHandler.ts      # 日期备忘录处理
+│           │   ├── config/ChartBaseConfig.ts # 图表配置
+│           │   ├── drawing/                # 绘图工具
+│           │   └── data/DataProcessor.ts   # 数据处理
+│           └── types/          # 类型定义
+│               └── index.ts    # 所有类型定义
+└── types/
+    └── exam.ts                 # 考试相关类型
+```
+
+### 2. 核心组件依赖关系
+
+**StockMain (主组件) → 依赖:**
+- useStockSocket: WebSocket连接
+- useStockQueries: 数据查询
+- useStockDataFilter: 数据过滤
+- useTradeRecords: 交易记录
+- useProfitCalculator: 收益计算
+- StockChart: 主图表显示
+- TradePanel: 交易操作
+- ProfitDisplay: 收益显示
+- DrawingToolbar: 画线工具
+
+### 3. 关键技术栈
+- **图表库**: ECharts
+- **状态管理**: React Query (TanStack Query)
+- **路由**: React Router
+- **样式**: Tailwind CSS
+- **WebSocket**: Socket.io-client
+- **表单**: React Hook Form
+- **通知**: React Toastify
+
+## 关键实现细节
+
+### 1. 数据流架构
+```typescript
+// 数据获取流程
+useStockQueries → RPC API调用 → 数据转换 → StockChart渲染
+
+// 交易流程
+TradePanel → useTradeRecords → 状态更新 → ProfitDisplay更新
+
+// WebSocket流程
+useStockSocket → 连接建立 → 数据推送 → 答题卡功能
+```
+
+### 2. 主要API接口
+- **股票历史数据**: `GET /api/v1/stock-data/history/{code}`
+- **日期备忘录**: `GET /api/v1/date-notes`
+- **WebSocket连接**: `/socket.io` (答题卡功能)
+
+### 3. 核心类型定义
+```typescript
+interface StockData {
+  o: string; // 开盘价
+  c: string; // 收盘价
+  h: string; // 最高价
+  l: string; // 最低价
+  v: string; // 成交量
+  d: string; // 日期
+  zd: string; // 涨跌幅
+}
+
+interface TradeRecord {
+  type: 'BUY' | 'SELL';
+  price: number;
+  timestamp: number;
+  date: string;
+}
+
+interface ProfitSummary {
+  totalProfit: number;
+  dailyStats: {
+    date: string;
+    open: number;
+    high: number;
+    close: number;
+    low: number;
+    change: number;
+  };
+}
+```
+
+### 4. 绘图功能支持
+- **水平线绘制**: ActiveType.HORIZONTAL
+- **趋势线绘制**: ActiveType.TREND
+- **扩展趋势线**: ActiveType.TREND_EXTENDED
+- **线条管理**: 显示/隐藏/清除
+
+### 5. 快捷键支持
+- `B` - 买入
+- `S` - 卖出  
+- `→` - 下一天
+- `ESC` - 取消当前操作
+
+## 修改前检查清单
+
+### ✅ 架构一致性检查
+- [ ] 是否遵循现有的组件分层结构?
+- [ ] 是否保持类型定义的完整性?
+- [ ] 是否兼容现有的数据流模式?
+
+### ✅ 功能影响评估
+- [ ] 修改是否会影响交易功能?
+- [ ] 修改是否会影响图表渲染?
+- [ ] 修改是否会影响WebSocket连接?
+
+### ✅ 性能考虑
+- [ ] 修改是否引入不必要的重渲染?
+- [ ] 修改是否影响图表性能?
+- [ ] 修改是否增加内存使用?
+
+### ✅ 兼容性检查
+- [ ] 修改是否向后兼容?
+- [ ] 修改是否影响移动端适配?
+- [ ] 修改是否影响现有的快捷键功能?
+
+## 常见修改场景
+
+### 1. 添加新的图表类型
+- 需要在 `StockChartLib` 中添加新的系列配置
+- 更新 `types/index.ts` 中的类型定义
+- 修改 `StockChart.tsx` 中的渲染逻辑
+
+### 2. 添加新的交易功能
+- 在 `useTradeRecords` 中添加新的交易类型
+- 更新 `TradePanel` 组件
+- 修改 `ProfitDisplay` 显示逻辑
+
+### 3. 优化性能
+- 使用 `React.memo` 包装纯组件
+- 优化 `useEffect` 依赖数组
+- 使用 `useCallback` 和 `useMemo`
+
+## 错误处理规范
+
+### 1. API错误处理
+```typescript
+// 使用React Query的错误处理
+const { error } = useQuery({
+  queryKey: ['stockData'],
+  queryFn: fetchData,
+  onError: (error) => {
+    toast.error(`数据加载失败: ${error.message}`);
+  }
+});
+```
+
+### 2. WebSocket错误处理
+```typescript
+// WebSocket连接错误
+socket.on('error', (err) => {
+  console.error('Socket error:', err);
+  setError(err);
+});
+```
+
+### 3. 用户输入验证
+```typescript
+// 股票代码验证
+if (!stockCode || stockCode.trim().length !== 6) {
+  toast.error('请输入有效的6位股票代码');
+  return;
+}
+```

+ 239 - 0
.claude/commands/command-check-创建新检查指令.md

@@ -0,0 +1,239 @@
+---
+description: "检查指令创建指南"
+---
+
+本指南用于创建新的检查指令文件,为特定功能模块的开发、维护和修改提供完整的上下文信息。
+
+## 检查指令文件结构规范
+
+### 1. 文件命名规范
+- 格式:`check-[模块名]-[功能描述].md`
+- 示例:`check-live-直播相关文件检查.md`
+- 要求:使用英文模块名和中文功能描述
+
+### 2. 文件头部元数据
+```markdown
+---
+description: "[功能描述]检查指令"
+---
+```
+
+### 3. 核心内容结构
+
+#### 3.1 检查范围
+列出需要检查的所有相关文件和组件:
+```markdown
+## 检查范围
+
+### 1. [组件类别] (`文件路径/`)
+- **文件名**: 功能描述
+- **文件名**: 功能描述
+
+### 2. [页面类别] (`文件路径/`)
+- 页面功能描述
+```
+
+#### 3.2 详细检查结果
+使用 ✅ 标记实现情况:
+```markdown
+## 详细检查结果
+
+### [组件名]实现情况
+
+#### 文件名 (功能描述)
+- ✅ **功能点**: 描述
+- ✅ **功能点**: 描述
+- ⚠️ **待完善**: 描述
+```
+
+#### 3.3 功能特性
+列出已实现的核心功能:
+```markdown
+## 功能特性
+
+### 已实现功能
+1. **功能类别**
+   - 具体功能点
+   - 具体功能点
+
+2. **管理功能**
+   - 具体管理操作
+   - 具体管理操作
+```
+
+#### 3.4 技术实现
+描述技术栈和关键实现:
+```markdown
+### 技术实现
+- **技术栈**: 使用的技术框架和库
+- **数据存储**: 数据库字段设计
+- **类型安全**: TypeScript类型定义
+- **验证机制**: 数据验证方案
+- **错误处理**: 错误处理机制
+```
+
+#### 3.5 使用示例
+提供代码使用示例:
+```markdown
+## 使用示例
+
+### 功能调用示例
+```typescript
+// 代码示例
+const result = await functionCall();
+```
+```
+
+#### 3.6 环境配置
+列出必需的环境变量和配置:
+```markdown
+## 环境配置要求
+
+### 必需环境变量
+```bash
+CONFIG_KEY=your_config_value
+ANOTHER_KEY=your_another_value
+```
+```
+
+### 3.7 修改前检查清单(可选)
+对于复杂组件,添加修改风险评估:
+```markdown
+## 修改前检查清单
+
+### ✅ 架构一致性检查
+- [ ] 是否遵循现有的组件分层结构?
+- [ ] 是否保持类型定义的完整性?
+- [ ] 是否兼容现有的数据流模式?
+
+### ✅ 功能影响评估
+- [ ] 修改是否会影响核心功能?
+- [ ] 修改是否会影响数据一致性?
+```
+
+### 3.8 潜在风险点(可选)
+标识高风险修改区域:
+```markdown
+### 潜在修改风险点
+
+#### 高风险区域:
+1. **核心逻辑** - 修改可能影响主要功能
+2. **状态管理** - 多个状态需要保持同步
+
+#### 中等风险区域:
+1. **UI布局** - 修改可能影响显示效果
+
+#### 低风险区域:
+1. **样式调整** - 视觉修改
+```
+
+## 检查指令创建流程
+
+### 1. 确定检查目标
+- 明确要检查的功能模块
+- 确定相关的文件和组件范围
+
+### 2. 收集信息
+- 查看相关源代码文件
+- 分析功能实现细节
+- 记录技术栈和依赖关系
+
+### 3. 编写检查内容
+- 按照标准结构组织内容
+- 使用 ✅ 标记实现情况
+- 提供完整的使用示例
+
+### 4. 添加风险评估
+- 对于复杂组件,添加修改前检查清单
+- 标识潜在的风险点
+
+### 5. 验证完整性
+- 确保所有相关文件都被覆盖
+- 检查示例代码的正确性
+- 验证环境配置信息
+
+## 最佳实践
+
+### 1. 内容组织
+- 使用清晰的层级结构
+- 保持内容简洁但完整
+- 使用表格和列表提高可读性
+
+### 2. 技术细节
+- 包含完整的类型定义
+- 描述数据流和状态管理
+- 记录关键的业务逻辑
+
+### 3. 风险评估
+- 客观评估修改风险
+- 提供具体的风险描述
+- 建议规避风险的方法
+
+### 4. 示例代码
+- 提供真实可用的代码示例
+- 包含必要的导入和上下文
+- 使用正确的语法和格式
+
+## 模板示例
+
+```markdown
+---
+description: "[模块名]相关文件检查指令"
+---
+
+本指令用于检查项目中[模块名]相关的文件实现情况,为后续[功能描述]开发和维护提供上下文信息。
+
+## 检查范围
+
+### 1. [组件类别] (`src/path/to/components/`)
+- **ComponentA.tsx**: 主要功能描述
+- **ComponentB.tsx**: 辅助功能描述
+
+### 2. [服务类别] (`src/path/to/services/`)
+- **service.ts**: 业务逻辑实现
+
+## 详细检查结果
+
+### 组件实现情况
+
+#### ComponentA.tsx (主要组件)
+- ✅ **核心功能**: 功能描述
+- ✅ **状态管理**: 状态管理描述
+- ✅ **错误处理**: 错误处理机制
+
+## 功能特性
+
+### 已实现功能
+1. **主要功能**
+   - 功能点1
+   - 功能点2
+
+2. **辅助功能**
+   - 功能点3
+   - 功能点4
+
+## 使用示例
+
+### 功能调用
+```typescript
+import { useFeature } from '@/path/to/hook';
+
+const { data, isLoading } = useFeature(params);
+```
+
+## 环境配置
+
+### 必需配置
+```bash
+FEATURE_ENABLED=true
+API_ENDPOINT=https://api.example.com
+```
+
+## 修改前检查清单
+
+### ✅ 兼容性检查
+- [ ] 修改是否影响现有功能?
+- [ ] 修改是否需要更新类型定义?
+```
+
+通过遵循这个指南,您可以创建结构清晰、内容完整的检查指令文件,为开发团队提供有价值的技术上下文信息。

+ 125 - 0
.claude/commands/command-创建新指令.md

@@ -0,0 +1,125 @@
+---
+description: 快速创建新的Roo Code指令,包括标准CRUD指令、自定义指令和常用工作流模板
+argument-hint: <指令类型> [实体名称] [模块名称]
+---
+
+# 创建指令
+
+根据指定的类型和参数快速创建新的Roo Code指令文件。
+
+## 使用方式
+
+- `/command-create crud User users` - 创建用户管理标准CRUD指令
+- `/command-create custom complex-report` - 创建复杂报表自定义指令
+- `/command-create mini-form Login` - 创建小程序登录表单指令
+
+## 指令类型
+
+### 1. 标准CRUD指令 (`crud`)
+创建包含完整增删改查功能的指令模板
+
+**参数**:
+- 实体名称 (PascalCase)
+- 模块名称 (snake_case)
+
+**示例**: `/command-create crud Product products`
+
+### 2. 自定义指令 (`custom`)
+创建灵活的自定义业务逻辑指令
+
+**参数**:
+- 指令名称 (kebab-case)
+
+**示例**: `/command-create custom data-analysis`
+
+### 3. 小程序表单指令 (`mini-form`)
+创建小程序表单页面指令模板
+
+**参数**:
+- 表单名称 (PascalCase)
+
+**示例**: `/command-create mini-form UserProfile`
+
+## 创建步骤
+
+### 步骤1: 确定指令类型
+根据业务需求选择合适的指令类型:
+- 简单数据管理 → 标准CRUD
+- 复杂业务逻辑 → 自定义指令
+- 小程序页面 → 小程序表单
+
+### 步骤2: 命名规范
+- 指令文件名使用 `功能类别-中文描述.md` 格式
+- 功能类别使用英文短横线分隔(如:auth-, check-, generic-crud-, mini-)
+- 中文描述准确反映指令功能
+- 实体名使用 PascalCase
+- 模块名使用 snake_case
+
+### 步骤3: 选择存放位置
+- 项目特定指令: `.roo/commands/`
+- 全局通用指令: `~/.roo/commands/`
+
+## 标准CRUD指令模板
+
+创建标准CRUD指令时,将自动生成:
+1. 实体定义文件
+2. Service类
+3. API路由
+4. 前端页面
+5. 客户端API调用
+
+## 自定义指令模板
+
+创建自定义指令时,提供:
+1. 基础指令框架
+2. 业务逻辑占位
+3. 错误处理模板
+4. 进度跟踪示例
+
+## 小程序表单指令模板
+
+创建小程序表单指令时,包含:
+1. 表单验证模式
+2. 页面组件结构
+3. 样式规范
+4. 提交处理逻辑
+
+## 最佳实践
+
+1. **指令命名**: 使用功能类别+中文描述结构,如 `auth-认证相关检查`, `generic-crud-通用开发`
+2. **参数设计**: 明确指令所需的参数类型和格式
+3. **错误处理**: 包含完整的错误处理和用户反馈
+4. **文档说明**: 在指令顶部添加清晰的描述和使用示例
+5. **版本管理**: 将指令文件纳入版本控制系统
+
+## 示例指令文件结构
+
+```
+.roo/commands/
+├── generic-crud-用户管理开发.md      # 用户管理CRUD
+├── custom-月度报表生成.md           # 月度报表生成
+├── mini-form-小程序登录表单.md      # 小程序登录表单
+└── api-API端点生成器.md            # API端点生成器
+```
+
+## 快速创建检查清单
+
+- [ ] 确定指令类型和用途
+- [ ] 选择合适的命名规范
+- [ ] 定义清晰的参数结构
+- [ ] 添加详细的描述文档
+- [ ] 包含错误处理逻辑
+- [ ] 提供使用示例
+- [ ] 测试指令功能
+- [ ] 更新指令索引
+
+## 常见问题
+
+**Q: 如何创建跨项目的通用指令?**
+A: 将指令文件放在 `~/.roo/commands/` 目录下,所有项目都可访问
+
+**Q: 指令参数如何验证?**
+A: 在指令内容中使用正则表达式或条件判断来验证参数格式
+
+**Q: 如何共享团队指令?**
+A: 将指令文件提交到项目仓库的 `.roo/commands/` 目录,团队成员可同步使用

+ 152 - 0
.claude/commands/download-从URL下载文件到MinIO.md

@@ -0,0 +1,152 @@
+---
+description: "从任意URL下载文件并保存到MinIO,同时创建数据库记录"
+---
+
+# FileService.downloadAndSaveFromUrl 使用指令
+
+## 功能概述
+`downloadAndSaveFromUrl` 是 FileService 中新增的统一方法,用于从任意URL下载文件并保存到MinIO,同时创建数据库记录。
+
+## 基本使用
+
+### 导入服务
+```typescript
+import { FileService } from '@/server/modules/files/file.service';
+import { DataSource } from 'typeorm';
+
+// 在服务中注入
+const fileService = new FileService(dataSource);
+```
+
+### 基础调用
+```typescript
+const result = await fileService.downloadAndSaveFromUrl(
+  'https://example.com/image.jpg',
+  {
+    uploadUserId: 123, // 必需:上传用户ID
+  }
+);
+// 返回: { file: File实体, url: 文件访问URL }
+```
+
+## 参数说明
+
+### 必填参数
+| 参数 | 类型 | 说明 |
+|------|------|------|
+| url | string | 要下载的文件URL |
+| fileData.uploadUserId | number | 上传用户的ID |
+
+### 可选参数
+| 参数 | 类型 | 默认值 | 说明 |
+|------|------|--------|------|
+| fileData.mimeType | string | 自动推断 | 文件MIME类型 |
+| fileData.customFileName | string | 自动获取 | 自定义文件名 |
+| fileData.customPath | string | 自动生成 | 自定义存储路径 |
+| options.timeout | number | 30000 | 下载超时时间(ms) |
+| options.retries | number | 0 | 重试次数 |
+
+## 使用场景示例
+
+### 1. 下载用户头像
+```typescript
+const avatarFile = await fileService.downloadAndSaveFromUrl(
+  'https://thirdwx.qlogo.cn/mmopen/vi_32/xxx/132',
+  {
+    uploadUserId: userId,
+    customPath: 'avatars/',
+    mimeType: 'image/jpeg'
+  }
+);
+```
+
+### 2. 下载文档附件
+```typescript
+const docFile = await fileService.downloadAndSaveFromUrl(
+  'https://example.com/report.pdf',
+  {
+    uploadUserId: userId,
+    customFileName: 'monthly-report.pdf',
+    customPath: 'documents/reports/'
+  }
+);
+```
+
+### 3. 批量下载图片
+```typescript
+const imageUrls = ['url1.jpg', 'url2.png', 'url3.gif'];
+const results = await Promise.all(
+  imageUrls.map(url => 
+    fileService.downloadAndSaveFromUrl(url, { uploadUserId: userId })
+  )
+);
+```
+
+## 错误处理
+
+### 异常类型
+- `从URL下载文件失败`: 网络或服务器错误
+- `文件保存失败`: MinIO存储或数据库错误
+
+### 使用示例
+```typescript
+try {
+  const result = await fileService.downloadAndSaveFromUrl(url, { uploadUserId });
+  return result.file.id;
+} catch (error) {
+  console.error('下载失败:', error.message);
+  return null; // 或抛出异常
+}
+```
+
+## 高级配置
+
+### 自定义文件名和路径
+```typescript
+await fileService.downloadAndSaveFromUrl(
+  'https://cdn.example.com/avatar.png',
+  {
+    uploadUserId: 1001,
+    customFileName: 'user-1001-avatar.png',
+    customPath: 'users/1001/profile/'
+  }
+);
+```
+
+### 设置超时时间
+```typescript
+await fileService.downloadAndSaveFromUrl(
+  'https://large-file.example.com/video.mp4',
+  {
+    uploadUserId: userId
+  },
+  {
+    timeout: 60000, // 60秒超时
+    retries: 2      // 重试2次
+  }
+);
+```
+
+## 返回值结构
+```typescript
+{
+  file: {
+    id: number,
+    name: string,
+    path: string,
+    size: number,
+    mimeType: string,
+    url: string,
+    // ...其他File实体字段
+  },
+  url: string // MinIO访问URL
+}
+```
+
+## 注意事项
+
+1. **网络要求**: 确保服务器能够访问目标URL
+2. **文件大小**: 大文件下载可能超时,可调整timeout参数
+3. **文件名冲突**: 系统自动添加UUID避免冲突
+4. **MIME类型**: 优先使用提供的mimeType,否则自动推断
+5. **错误日志**: 所有错误都会记录详细日志便于调试

+ 161 - 0
.claude/commands/file-实体文件关联开发指南.md

@@ -0,0 +1,161 @@
+---
+description: "实体与文件关联开发指令 - 指导如何在实体中添加文件关联字段"
+---
+
+# 实体与文件关联开发指令
+
+## 概述
+本指令指导如何在实体中添加与文件管理系统的关联关系,基于用户实体(UserEntity)的最佳实践。
+
+## 文件关联标准实现
+
+### 1. 实体定义规范
+
+在实体类中添加文件关联字段:
+
+```typescript
+// 1. 导入文件实体
+import { File } from '@/server/modules/files/file.entity';
+
+// 2. 在实体类中添加字段
+@Column({ 
+  name: '{field_prefix}_file_id', 
+  type: 'int', 
+  unsigned: true, 
+  nullable: true, 
+  comment: '{描述}文件ID' 
+})
+{fieldPrefix}FileId!: number | null;
+
+@ManyToOne(() => File, { nullable: true })
+@JoinColumn({ 
+  name: '{field_prefix}_file_id', 
+  referencedColumnName: 'id' 
+})
+{fieldPrefix}File!: File | null;
+```
+
+### 2. Schema定义规范
+
+在实体的schema文件中添加文件关联字段:
+
+```typescript
+// 基础字段定义
+{fieldPrefix}FileId: z.number()
+  .int()
+  .positive()
+  .nullable()
+  .openapi({
+    example: 1,
+    description: '{描述}文件ID'
+  }),
+
+// 关联文件对象(用于响应)
+{fieldPrefix}File: z.object({
+  id: z.number().int().positive().openapi({ description: '文件ID' }),
+  name: z.string().max(255).openapi({ description: '文件名', example: 'example.jpg' }),
+  fullUrl: z.string().openapi({ description: '文件完整URL', example: 'https://example.com/file.jpg' }),
+  type: z.string().nullable().openapi({ description: '文件类型', example: 'image/jpeg' }),
+  size: z.number().nullable().openapi({ description: '文件大小(字节)', example: 102400 })
+}).nullable().optional().openapi({
+  description: '{描述}文件信息'
+}),
+```
+
+### 3. 命名规范
+
+| 类型 | 命名格式 | 示例 |
+|------|----------|------|
+| 数据库字段 | `{前缀}_file_id` | `avatar_file_id` |
+| 实体字段 | `{前缀}FileId` | `fileId` |
+| 关联实体 | `{前缀}File` | `file` |
+| 描述注释 | `{描述}文件ID` | `头像文件ID` |
+
+### 4. 完整示例 - 基于用户实体
+
+**实体类** ([`src/server/modules/users/user.entity.ts`](src/server/modules/users/user.entity.ts:29-34)):
+```typescript
+@Column({ name: 'avatar_file_id', type: 'int', unsigned: true, nullable: true, comment: '头像文件ID' })
+fileId!: number | null;
+
+@ManyToOne(() => File, { nullable: true })
+@JoinColumn({ name: 'avatar_file_id', referencedColumnName: 'id' })
+file!: File | null;
+```
+
+**Schema定义** ([`src/server/modules/users/user.schema.ts`](src/server/modules/users/user.schema.ts:33-45)):
+```typescript
+fileId: z.number().int().positive().nullable().openapi({
+  example: 1,
+  description: '头像文件ID'
+}),
+file: z.object({
+  id: z.number().int().positive().openapi({ description: '文件ID' }),
+  name: z.string().max(255).openapi({ description: '文件名', example: 'avatar.jpg' }),
+  fullUrl: z.string().openapi({ description: '文件完整URL', example: 'https://example.com/avatar.jpg' }),
+  type: z.string().nullable().openapi({ description: '文件类型', example: 'image/jpeg' }),
+  size: z.number().nullable().openapi({ description: '文件大小(字节)', example: 102400 })
+}).nullable().optional().openapi({
+  description: '头像文件信息'
+}),
+```
+
+## 使用步骤
+
+### 步骤1: 添加实体字段
+1. 在实体类中添加 `{prefix}FileId` 和 `{prefix}File` 字段
+2. 使用 `@ManyToOne` 关联 `File` 实体
+3. 配置 `@JoinColumn` 指定外键字段名
+
+### 步骤2: 添加Schema定义
+1. 在 `Create{Entity}Dto` 中添加 `{prefix}FileId` 字段
+2. 在 `Update{Entity}Dto` 中添加可选的 `{prefix}FileId` 字段
+3. 在实体Schema中添加 `{prefix}File` 对象用于响应
+
+### 步骤3: 数据库迁移
+确保数据库表包含 `{prefix}_file_id` 字段,类型为 INT UNSIGNED NULL
+
+## 使用场景
+
+### 场景1: 单文件关联
+适用于实体只需要关联单个文件的情况,如:
+- 用户头像
+- 商品封面图
+- 文档附件
+
+### 场景2: 多文件关联
+如需关联多个文件,请使用 ManyToMany 关联,参考广告实体的实现。
+
+### 场景3: 通用CRUD路由配置
+对于使用通用CRUD路由的实体,需要配置 `relations` 以自动关联查询文件信息:
+
+```typescript
+// 示例:广告实体的通用CRUD配置
+import { createCrudRoutes } from '@/server/utils/generic-crud.routes';
+import { Advertisement } from '@/server/modules/advertisements/advertisement.entity';
+import { AdvertisementSchema, CreateAdvertisementDto, UpdateAdvertisementDto } from '@/server/modules/advertisements/advertisement.schema';
+import { authMiddleware } from '@/server/middleware/auth.middleware';
+
+const advertisementRoutes = createCrudRoutes({
+  entity: Advertisement,
+  createSchema: CreateAdvertisementDto,
+  updateSchema: UpdateAdvertisementDto,
+  getSchema: AdvertisementSchema,
+  listSchema: AdvertisementSchema,
+  searchFields: ['title', 'code'],
+  relations: ['imageFile'], // 关键配置:自动关联查询图片文件
+  middleware: [authMiddleware]
+});
+```
+
+## 注意事项
+
+1. **空值处理**: 字段必须支持 `null`,允许无文件关联
+2. **级联操作**: 默认不级联,删除文件不会影响关联实体
+3. **文件验证**: 前端需先上传文件获取文件ID,再进行实体关联
+4. **类型安全**: 确保所有字段类型定义一致(number | null)
+5. **关联查询**: 配置 `relations` 后,通用CRUD会自动处理关联查询
+
+## 扩展说明
+
+此标准基于用户实体的头像文件关联实现,适用于项目中所有需要文件关联的实体。后续实体按此标准实现即可保持统一性。

+ 32 - 0
.claude/commands/generic-crud-从SQL创建CRUD.md

@@ -0,0 +1,32 @@
+---
+description: "从旧数据sql创建通用curd开发指令"
+---
+先分析旧数据表结构,找出原有的关联关系,在后续创建时加上关联。如商品与商品分类的关联,订单商品与订单的关联等
+
+然后逐个按通用curd开发规范开发
+
+创建实体类 your-entity.entity.ts
+创建实体Zod Schema定义 your-entity.schema.ts
+注册实体到数据源
+创建服务类继承GenericCrudService
+创建通用CRUD路由
+注册路由到API
+创建客户端API调用方法
+创建管理后台页面,按照 .roo/commands/shadcn-manage-page.md 指令规范
+注册路由和菜单
+
+注意:
+1. 旧数据表文件中的时间字段定义,创建时统一改为使用 TypeORM 标准的 @CreateDateColumn 和 @UpdateDateColumn 格式
+2. 为每个验证字段添加 中文错误提示
+
+    示例:
+    ```typescript
+    // 用户 schema
+    export const UserSchema = z.object({
+    username: z.string().min(3, '用户名至少3个字符').max(255, '最多255个字符'),
+    password: z.string().min(6, '密码至少6位').max(255, '最多255位'),
+    phone: z.string().regex(/^1[3-9]\d{9}$/, '请输入正确的手机号'),
+    email: z.email('请输入正确的邮箱格式'),
+    name: z.string().max(255, '姓名最多255个字符').optional()
+    });
+3. 旧数据表文件中的创建人,修改人字段定义,创建时统一改为使用  createdBy, updateBy

+ 12 - 0
.claude/commands/generic-crud-创建公共只读路由.md

@@ -0,0 +1,12 @@
+---
+description: "通用curd公共只读路由创建指令"
+---
+
+按通用curd开发规范进行公共只读路由创建
+
+步骤
+在src/server/api/public下创建公共只读路由
+注册路由到API   src/server/api.ts
+创建客户端API调用方法   src/client/api.ts mini/src/api.ts
+
+注意: 需要 public 前缀

+ 60 - 0
.claude/commands/generic-crud-创建实体验证规则.md

@@ -0,0 +1,60 @@
+---
+description: "创建已有实体的schema指令"
+---
+
+按通用curd开发规范开发
+
+检查已有实体类文件 your-entity.entity.ts
+检查其关联的实体的 schema文件
+创建实体Zod Schema定义文件 your-entity.schema.ts
+
+注意:
+1. 将在当前实体中定义的枚举移到 your-entity.schema.ts 中定义
+2. 为每个验证字段添加 中文错误提示
+
+    示例:
+    ```typescript
+    // 用户 schema
+    export const UserSchema = z.object({
+        username: z.string().min(3, '用户名至少3个字符').max(255, '最多255个字符'),
+        password: z.string().min(6, '密码至少6位').max(255, '最多255位'),
+        phone: z.string().regex(/^1[3-9]\d{9}$/, '请输入正确的手机号'),
+        email: z.email('请输入正确的邮箱格式'),
+        name: z.string().max(255, '姓名最多255个字符').optional()
+    });
+3. 有关联的实体,其 zod schema 要import 用上
+    示例:
+    ```typescript
+    import { ZichanCategorySchema } from './zichan-category.schema';
+    export const ZichanInfoSchema = z.object({
+        id: z.number().int('必须是整数').positive('必须是正整数').openapi({
+            description: '资产信息ID',
+            example: 1
+        }),
+        category: ZichanCategorySchema.nullable().openapi('设备分类信息'),
+    });
+    ```
+4. 实体中import用到的枚举,your-entity.schema.ts 中也要import用上
+    示例:
+    ```typescript
+    // 教室状态枚举
+    export enum ClassroomStatus {
+    CLOSED = 0,  // 关闭
+    OPEN = 1     // 开放
+    }
+    // 在当前zod v4中 z.enum 代替了 z.nativeEnum;  
+    z.object({
+        // z.enum(ClassroomStatus) 等价于 z.nativeEnum(ClassroomStatus), 不要 z.enum([ClassroomStatus.CLOSED, ClassroomStatus.OPEN]),应该是 z.enum(ClassroomStatus)
+        z.enum(ClassroomStatus).nullable().openapi({
+            description: '状态 (0关闭 1开放)',
+            example: ClassroomStatus.OPEN
+        }),
+    })
+    ```
+
+5. 在当前 zod v4中, z.coerce.date(), z.coerce.number() 等,都要添加类型泛型指定
+    示例:
+    ```typescript
+    z.coerce.date<Date>()
+    z.coerce.number<number>()
+    ```

+ 504 - 0
.claude/commands/generic-crud-扩展路由开发指南.md

@@ -0,0 +1,504 @@
+---
+description: "通用curd扩展路由开发指令"
+---
+
+# 通用CRUD扩展路由开发指令
+
+本指令基于通用CRUD规范,指导如何为已存在的通用CRUD路由添加自定义扩展路由,采用模块化方式保持代码清晰。
+
+## 适用场景
+
+当通用CRUD提供的标准路由(GET /, POST /, GET /{id}, PUT /{id}, DELETE /{id})无法满足业务需求时,需要添加自定义业务路由。
+
+## 开发流程
+
+### 1. **定位现有通用CRUD路由文件**
+找到对应的通用CRUD路由文件,通常位于:
+- `src/server/api/[实体名]/index.ts`
+
+### 2. **创建扩展路由文件**
+为每个扩展功能创建单独的路由文件:
+
+```
+src/server/api/your-entity/
+├── index.ts            # 聚合路由(已存在)
+├── batch/              # 新增 - 批量操作
+│   └── delete.ts       # 批量删除
+├── [id]/               # 新增 - 单条记录扩展操作
+│   ├── status.ts       # 状态更新
+│   ├── toggle.ts       # 状态切换
+│   └── audit.ts        # 审核操作
+├── export.ts           # 新增 - 数据导出
+├── import.ts           # 新增 - 数据导入
+├── stats.ts            # 新增 - 统计信息
+└── upload.ts           # 新增 - 文件上传
+```
+
+### 3. **创建独立扩展路由文件**
+
+#### 3.1 批量删除路由 - `batch/delete.ts`
+```typescript
+import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
+import { z } from '@hono/zod-openapi';
+import { ErrorSchema } from '@/server/utils/errorHandler';
+import { AppDataSource } from '@/server/data-source';
+import { YourEntityService } from '@/server/modules/your-module/your-entity.service';
+import { AuthContext } from '@/server/types/context';
+import { authMiddleware } from '@/server/middleware/auth.middleware';
+
+const routeDef = createRoute({
+  method: 'delete',
+  path: '/',
+  middleware: [authMiddleware],
+  request: {
+    body: {
+      content: {
+        'application/json': {
+          schema: z.object({
+            ids: z.array(z.number().int().positive()).openapi({
+              description: '要删除的ID列表',
+              example: [1, 2, 3]
+            })
+          })
+        }
+      }
+    }
+  },
+  responses: {
+    200: {
+      description: '批量删除成功',
+      content: {
+        'application/json': {
+          schema: z.object({
+            deletedCount: z.number().openapi({ example: 3, description: '删除的记录数' })
+          })
+        }
+      }
+    },
+    400: { description: '请求参数错误', content: { 'application/json': { schema: ErrorSchema } } },
+    500: { description: '服务器错误', content: { 'application/json': { schema: ErrorSchema } } }
+  }
+});
+
+const app = new OpenAPIHono<AuthContext>().openapi(routeDef, async (c) => {
+  try {
+    const { ids } = await c.req.json();
+    const service = new YourEntityService(AppDataSource);
+    
+    let deletedCount = 0;
+    for (const id of ids) {
+      const result = await service.delete(id);
+      if (result) deletedCount++;
+    }
+    
+    return c.json({ deletedCount }, 200);
+  } catch (error) {
+    return c.json({ code: 500, message: '批量删除失败' }, 500);
+  }
+});
+
+export default app;
+```
+
+#### 3.2 状态更新路由 - `[id]/status.ts`
+```typescript
+import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
+import { z } from '@hono/zod-openapi';
+import { YourEntitySchema } from '@/server/modules/your-module/your-entity.schema';
+import { parseWithAwait } from '@/server/utils/parseWithAwait';
+import { ErrorSchema } from '@/server/utils/errorHandler';
+import { AppDataSource } from '@/server/data-source';
+import { YourEntityService } from '@/server/modules/your-module/your-entity.service';
+import { AuthContext } from '@/server/types/context';
+import { authMiddleware } from '@/server/middleware/auth.middleware';
+
+const routeDef = createRoute({
+  method: 'patch',
+  path: '/',
+  middleware: [authMiddleware],
+  request: {
+    params: z.object({
+      id: z.string().openapi({
+        param: { name: 'id', in: 'path' },
+        example: '1',
+        description: '记录ID'
+      })
+    }),
+    body: {
+      content: {
+        'application/json': {
+          schema: z.object({
+            status: z.number().openapi({ example: 1, description: '新状态值' })
+          })
+        }
+      }
+    }
+  },
+  responses: {
+    200: { description: '状态更新成功', content: { 'application/json': { schema: YourEntitySchema } } },
+    404: { description: '记录不存在', content: { 'application/json': { schema: ErrorSchema } } },
+    500: { description: '服务器错误', content: { 'application/json': { schema: ErrorSchema } } }
+  }
+});
+
+const app = new OpenAPIHono<AuthContext>().openapi(routeDef, async (c) => {
+  try {
+    const { id } = c.req.valid('param');
+    const { status } = await c.req.json();
+    const service = new YourEntityService(AppDataSource);
+    
+    const result = await service.update(Number(id), { status });
+    if (!result) {
+      return c.json({ code: 404, message: '记录不存在' }, 404);
+    }
+    
+    // 使用 parseWithAwait 处理响应数据
+    const validatedResult = await parseWithAwait(YourEntitySchema, result);
+    return c.json(validatedResult, 200);
+  } catch (error) {
+    return c.json({ code: 500, message: '状态更新失败' }, 500);
+  }
+});
+
+export default app;
+```
+
+#### 3.3 数据导出路由 - `export.ts`
+```typescript
+import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
+import { z } from '@hono/zod-openapi';
+import { ErrorSchema } from '@/server/utils/errorHandler';
+import { AppDataSource } from '@/server/data-source';
+import { YourEntityService } from '@/server/modules/your-module/your-entity.service';
+import { YourEntitySchema } from '@/server/modules/your-module/your-entity.schema';
+import { parseWithAwait } from '@/server/utils/parseWithAwait';
+import { AuthContext } from '@/server/types/context';
+import { authMiddleware } from '@/server/middleware/auth.middleware';
+
+const routeDef = createRoute({
+  method: 'get',
+  path: '/',
+  middleware: [authMiddleware],
+  request: {
+    query: z.object({
+      format: z.enum(['csv', 'xlsx']).default('csv').openapi({
+        description: '导出格式',
+        example: 'csv'
+      }),
+      keyword: z.string().optional().openapi({
+        description: '搜索关键词',
+        example: '测试'
+      }),
+      filters: z.string().optional().openapi({
+        description: '筛选条件(JSON字符串)',
+        example: '{"status":1}'
+      })
+    })
+  },
+  responses: {
+    200: {
+      description: '导出文件',
+      content: {
+        'text/csv': { schema: z.string() },
+        'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': { schema: z.any() }
+      }
+    }
+  }
+});
+
+const app = new OpenAPIHono<AuthContext>().openapi(routeDef, async (c) => {
+  try {
+    const { format, keyword, filters } = c.req.valid('query');
+    const service = new YourEntityService(AppDataSource);
+    
+    let filterObj = {};
+    if (filters) {
+      try {
+        filterObj = JSON.parse(filters);
+      } catch (e) {
+        return c.json({ code: 400, message: '筛选条件格式错误' }, 400);
+      }
+    }
+    
+    const [data] = await service.getList(1, 1000, keyword, undefined, filterObj);
+    
+    // 使用 parseWithAwait 处理数据格式
+    const validatedData = await parseWithAwait(z.array(YourEntitySchema), data);
+    
+    if (format === 'csv') {
+      const csv = convertToCSV(validatedData);
+      return new Response(csv, {
+        headers: {
+          'Content-Type': 'text/csv',
+          'Content-Disposition': 'attachment; filename="export.csv"'
+        }
+      });
+    }
+    
+    return c.json({ code: 400, message: '不支持的导出格式' }, 400);
+  } catch (error) {
+    return c.json({ code: 500, message: '导出失败' }, 500);
+  }
+});
+
+export default app;
+
+function convertToCSV(data: any[]): string {
+  if (!data || data.length === 0) return '';
+  
+  const headers = Object.keys(data[0]);
+  const csvHeaders = headers.join(',');
+  const csvRows = data.map(row =>
+    headers.map(header => {
+      const value = row[header];
+      return typeof value === 'string' && value.includes(',') ? `"${value}"` : value;
+    }).join(',')
+  );
+  
+  return [csvHeaders, ...csvRows].join('\n');
+}
+```
+
+### 4. **聚合所有路由**
+在 `index.ts` 中聚合基础CRUD路由和所有扩展路由:
+
+```typescript
+// src/server/api/your-entity/index.ts
+import { OpenAPIHono } from '@hono/zod-openapi';
+import { createCrudRoutes } from '@/server/utils/generic-crud.routes';
+import { YourEntity } from '@/server/modules/your-module/your-entity.entity';
+import { YourEntitySchema, CreateYourEntityDto, UpdateYourEntityDto } from '@/server/modules/your-module/your-entity.schema';
+import { authMiddleware } from '@/server/middleware/auth.middleware';
+
+// 导入基础路由和各扩展路由
+import batchDeleteRoute from './batch/delete';
+import statusUpdateRoute from './[id]/status';
+import exportRoute from './export';
+
+// 1. 创建基础CRUD路由
+const yourEntityRoutes = createCrudRoutes({
+  entity: YourEntity,
+  createSchema: CreateYourEntityDto,
+  updateSchema: UpdateYourEntityDto,
+  getSchema: YourEntitySchema,
+  listSchema: YourEntitySchema,
+  searchFields: ['name', 'description'],
+  middleware: [authMiddleware]
+});
+
+// 2. 聚合所有路由(保持链式)
+const app = new OpenAPIHono()
+  .route('/batch', batchDeleteRoute)        // 批量操作路由
+  .route('/:id/status', statusUpdateRoute)  // 状态更新路由
+  .route('/export', exportRoute)          // 导出路由
+  .route('/', yourEntityRoutes);               // 基础CRUD路由必需放最后,不然自定义路由会覆盖掉基础CRUD路由
+
+// 3. 导出聚合后的路由
+export default app;
+```
+
+
+## 常见扩展场景
+
+### 1. **批量操作**
+- 批量删除:`DELETE /your-entity/batch`
+- 批量更新状态:`PATCH /your-entity/batch/status`
+- 批量导入:`POST /your-entity/import`
+
+### 2. **数据统计**
+- 获取统计信息:`GET /your-entity/stats`
+- 获取图表数据:`GET /your-entity/chart-data`
+
+### 3. **文件相关**
+- 上传文件:`POST /your-entity/upload`
+- 下载文件:`GET /your-entity/download/{id}`
+- 导出数据:`GET /your-entity/export`
+
+### 4. **状态管理**
+- 状态切换:`PATCH /your-entity/{id}/toggle-status`
+- 审核操作:`POST /your-entity/{id}/audit`
+
+### 5. **关联操作**
+- 获取关联数据:`GET /your-entity/{id}/related-data`
+- 更新关联关系:`PUT /your-entity/{id}/relations`
+
+## 命名规范
+
+- **路径命名**:使用RESTful风格,动词用HTTP方法表示
+- **批量操作**:使用复数名词,如 `/batch`, `/import`, `/export`
+- **状态变更**:使用 PATCH 方法,路径中体现操作,如 `/status`, `/toggle`
+- **自定义方法**:避免在路径中使用动词,用名词+参数表示
+
+## 扩展路由 Schema 文件规范
+
+### Schema 文件位置
+所有扩展路由的 Zod Schema 定义必须遵循以下文件位置规范:
+
+```
+src/server/modules/[模块名]/
+├── [实体名].entity.ts      # 实体定义
+├── [实体名].schema.ts      # 实体Schema定义(已存在)
+└── schemas/                # 扩展路由专用Schema目录(新增)
+    ├── batch/              # 批量操作Schema
+    │   └── delete.schema.ts
+    ├── [id]/               # 单条记录操作Schema
+    │   ├── status.schema.ts
+    │   ├── toggle.schema.ts
+    │   └── audit.schema.ts
+    ├── export.schema.ts    # 导出操作Schema
+    ├── import.schema.ts    # 导入操作Schema
+    └── stats.schema.ts     # 统计操作Schema
+```
+
+### Schema 文件命名规范
+- **文件名**:`[操作名].schema.ts`
+- **导出**:必须包含完整的请求/响应Schema定义
+- **引用**:在扩展路由文件中直接引用对应的Schema文件
+
+### Schema 文件示例
+
+#### 批量删除Schema - `schemas/batch/delete.schema.ts`
+```typescript
+import { z } from '@hono/zod-openapi';
+
+// 请求Schema
+export const BatchDeleteRequestSchema = z.object({
+  ids: z.array(z.number().int().positive()).openapi({
+    description: '要删除的ID列表',
+    example: [1, 2, 3]
+  })
+});
+
+// 响应Schema
+export const BatchDeleteResponseSchema = z.object({
+  deletedCount: z.number().openapi({
+    example: 3,
+    description: '删除的记录数'
+  })
+});
+
+// 类型定义
+export type BatchDeleteRequest = z.infer<typeof BatchDeleteRequestSchema>;
+export type BatchDeleteResponse = z.infer<typeof BatchDeleteResponseSchema>;
+```
+
+#### 状态更新Schema - `schemas/[id]/status.schema.ts`
+```typescript
+import { z } from '@hono/zod-openapi';
+import { YourEntitySchema } from '../../your-entity.schema';
+
+// 路径参数Schema
+export const StatusUpdateParamsSchema = z.object({
+  id: z.string().openapi({
+    param: { name: 'id', in: 'path' },
+    example: '1',
+    description: '记录ID'
+  })
+});
+
+// 请求体Schema
+export const StatusUpdateBodySchema = z.object({
+  status: z.number().openapi({
+    example: 1,
+    description: '新状态值'
+  })
+});
+
+// 响应Schema(复用实体Schema)
+export const StatusUpdateResponseSchema = YourEntitySchema;
+
+// 类型定义
+export type StatusUpdateParams = z.infer<typeof StatusUpdateParamsSchema>;
+export type StatusUpdateBody = z.infer<typeof StatusUpdateBodySchema>;
+export type StatusUpdateResponse = z.infer<typeof StatusUpdateResponseSchema>;
+```
+
+### 在扩展路由中的使用方式
+
+#### 引用Schema文件
+```typescript
+// src/server/api/your-entity/batch/delete.ts
+import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
+import { BatchDeleteRequestSchema, BatchDeleteResponseSchema } from '@/server/modules/your-module/schemas/batch/delete.schema';
+// ...其他导入
+```
+
+### 最佳实践
+1. **Schema复用**:尽量复用实体已有的Schema定义
+2. **类型安全**:所有Schema必须包含完整的OpenAPI元数据
+3. **模块化**:每个扩展路由对应独立的Schema文件
+4. **命名一致**:Schema文件名与路由功能保持一致
+5. **导出规范**:同时导出Schema和对应的TypeScript类型
+
+## 注意事项
+
+1. **模块化设计**:每个扩展功能单独一个文件,保持代码清晰
+2. **路径一致性**:扩展路由的路径要与聚合时的路径匹配
+3. **类型安全**:为所有自定义路由定义完整的OpenAPI schema
+4. **错误处理**:统一使用标准错误响应格式
+5. **权限控制**:为敏感操作添加适当的中间件
+6. **性能考虑**:批量操作要考虑事务处理和性能优化
+7. **Schema管理**:所有Schema必须放在指定的schemas目录下
+8. **版本兼容**:Schema变更要保持向后兼容性
+9. **数据验证**:所有查询类路由必须使用 `parseWithAwait` 处理响应数据,确保类型安全
+
+## parseWithAwait 使用规范
+
+### 概述
+`parseWithAwait` 是通用CRUD模块提供的数据验证工具,用于确保返回数据的类型安全,支持异步验证和转换。
+
+### 使用场景
+所有涉及数据查询和返回的扩展路由都应使用 `parseWithAwait` 处理响应数据。
+
+### 基本用法
+```typescript
+import { parseWithAwait } from '@/server/utils/parseWithAwait';
+
+// 验证单个实体
+const validatedEntity = await parseWithAwait(YourEntitySchema, entityData);
+
+// 验证实体数组
+const validatedEntities = await parseWithAwait(z.array(YourEntitySchema), entitiesData);
+```
+
+### 集成示例
+```typescript
+// 在扩展路由中使用
+const app = new OpenAPIHono<AuthContext>().openapi(routeDef, async (c) => {
+  try {
+    const data = await yourService.getList();
+    
+    // 使用 parseWithAwait 确保数据格式正确
+    const validatedData = await parseWithAwait(z.array(YourEntitySchema), data);
+    
+    return c.json({
+      data: validatedData,
+      pagination: { total, current: page, pageSize }
+    }, 200);
+  } catch (error) {
+    return c.json({ code: 500, message: '获取数据失败' }, 500);
+  }
+});
+```
+
+### 优势
+- **类型安全**:确保返回数据完全符合Zod schema定义
+- **异步支持**:支持异步验证和转换操作
+- **错误处理**:提供详细的验证错误信息
+- **性能优化**:避免运行时类型错误
+- **向后兼容**:与现有代码完全兼容
+
+### 最佳实践
+1. **所有查询路由**:GET请求返回数据前必须使用 `parseWithAwait`
+2. **列表查询**:使用 `z.array(EntitySchema)` 格式验证数组
+3. **单条查询**:直接使用实体Schema验证单个对象
+4. **错误处理**:捕获并适当处理验证错误
+
+## 验证步骤
+
+1. 创建独立的扩展路由文件
+2. 实现各路由的业务逻辑
+3. 在 index.ts 中聚合所有路由
+4. 测试所有API端点
+5. 验证RPC客户端能正确识别所有路由
+6. 更新前端API客户端(如需要)

+ 11 - 0
.claude/commands/generic-crud-注册公共只读路由.md

@@ -0,0 +1,11 @@
+---
+description: "通用curd公共只读路由注册指令"
+---
+
+按通用curd开发规范进行公共只读路由注册
+
+
+注册路由到API   src/server/api.ts
+创建客户端API调用方法   src/client/api.ts  mini/src/api.ts
+
+注意: 需要 public 前缀

+ 9 - 0
.claude/commands/generic-crud-路由注册指南.md

@@ -0,0 +1,9 @@
+---
+description: "通用curd路由注册指令"
+---
+
+按通用curd开发规范进行路由注册
+
+
+注册路由到API   src/server/api.ts
+创建客户端API调用方法   src/client/api.ts  mini/src/api.ts

+ 15 - 0
.claude/commands/generic-crud-通用CRUD开发.md

@@ -0,0 +1,15 @@
+---
+description: "通用curd开发指令"
+---
+
+按通用curd开发规范开发
+
+创建实体类文件 your-entity.entity.ts
+创建实体Zod Schema定义文件 your-entity.schema.ts
+注册实体到数据源
+创建服务类继承GenericCrudService
+创建通用CRUD路由
+注册路由到API
+创建客户端API调用方法
+创建管理后台页面,按照 .roo/commands/shadcn-manage-page.md 指令规范
+注册路由和菜单

+ 5 - 0
.claude/commands/icon-用lucide-react的.md

@@ -0,0 +1,5 @@
+---
+description: "使用lucide-react的图标"
+---
+
+使用lucide-react的图标

+ 203 - 0
.claude/commands/mini-auth-小程序认证钩子使用.md

@@ -0,0 +1,203 @@
+---
+description: "小程序 useAuth hook 使用指南"
+---
+
+# useAuth Hook 使用指南
+
+## 基本导入
+
+```typescript
+import { useAuth } from '@/utils/auth'
+```
+
+## 使用方式
+
+在组件中使用:
+
+```typescript
+const { user, login, logout, register, updateUser, isLoading, isLoggedIn } = useAuth()
+```
+
+## API 说明
+
+| 属性 | 类型 | 说明 |
+|------|------|------|
+| `user` | `User \| null` | 当前登录用户信息 |
+| `login` | `(data: LoginRequest) => Promise<User>` | 登录函数 |
+| `logout` | `() => Promise<void>` | 退出登录函数 |
+| `register` | `(data: RegisterRequest) => Promise<User>` | 注册函数 |
+| `updateUser` | `(userData: Partial<User>) => Promise<User>` | 更新用户信息 |
+| `isLoading` | `boolean` | 是否正在加载 |
+| `isLoggedIn` | `boolean` | 是否已登录 |
+
+## 使用示例
+
+### 获取用户信息
+
+```typescript
+const ProfilePage = () => {
+  const { user, isLoading } = useAuth()
+
+  if (isLoading) {
+    return <View>加载中...</View>
+  }
+
+  if (!user) {
+    return <View>请先登录</View>
+  }
+
+  return (
+    <View>
+      <Text>用户名: {user.username}</Text>
+      <Text>邮箱: {user.email}</Text>
+    </View>
+  )
+}
+```
+
+### 处理登录
+
+```typescript
+const LoginPage = () => {
+  const { login } = useAuth()
+  
+  const handleLogin = async (formData) => {
+    try {
+      const user = await login({
+        username: formData.username,
+        password: formData.password
+      })
+      // 登录成功后的处理
+    } catch (error) {
+      // 处理登录错误
+    }
+  }
+}
+```
+
+### 处理注册
+
+```typescript
+const RegisterPage = () => {
+  const { register } = useAuth()
+  
+  const handleRegister = async (formData) => {
+    try {
+      const user = await register({
+        username: formData.username,
+        password: formData.password,
+        email: formData.email
+      })
+      // 注册成功后的处理
+    } catch (error) {
+      // 处理注册错误
+    }
+  }
+}
+```
+
+### 处理退出登录
+
+```typescript
+const ProfilePage = () => {
+  const { logout } = useAuth()
+  
+  const handleLogout = async () => {
+    try {
+      await logout()
+      // 退出成功后会自动跳转到登录页
+    } catch (error) {
+      // 处理退出错误
+    }
+  }
+}
+```
+
+### 更新用户信息
+
+```typescript
+const ProfilePage = () => {
+  const { user, updateUser } = useAuth()
+  
+  const handleUpdateAvatar = async (avatarFileId) => {
+    if (user) {
+      const updatedUser = {
+        ...user,
+        avatarFileId: avatarFileId
+      }
+      await updateUser(updatedUser)
+    }
+  }
+}
+```
+
+### 完整示例
+
+```typescript
+const ProfilePage = () => {
+  const { user, logout, isLoading, updateUser } = useAuth()
+  
+  const handleLogout = async () => {
+    try {
+      await Taro.showModal({
+        title: '退出登录',
+        content: '确定要退出登录吗?',
+        success: async (res) => {
+          if (res.confirm) {
+            await logout()
+            // 退出成功后会自动跳转到登录页
+          }
+        }
+      })
+    } catch (error) {
+      // 处理错误
+    }
+  }
+
+  const handleAvatarUpload = async (result) => {
+    try {
+      if (user) {
+        const updatedUser = {
+          ...user,
+          avatarFileId: result.fileId
+        }
+        await updateUser(updatedUser)
+        Taro.showToast({ title: '头像更新成功', icon: 'success' })
+      }
+    } catch (error) {
+      Taro.showToast({ title: '更新失败', icon: 'none' })
+    }
+  }
+
+  if (isLoading) {
+    return <View className="flex justify-center items-center h-screen">加载中...</View>
+  }
+
+  if (!user) {
+    return (
+      <View className="flex justify-center items-center h-screen">
+        <Text className="mb-4">请先登录</Text>
+        <Button onClick={() => Taro.navigateTo({ url: '/pages/login/index' })}>
+          去登录
+        </Button>
+      </View>
+    )
+  }
+
+  return (
+    <View>
+      <Image src={user.avatarFile?.fullUrl} className="w-20 h-20 rounded-full" />
+      <Text>{user.username}</Text>
+      <Button onClick={handleLogout}>退出登录</Button>
+    </View>
+  )
+}
+```
+
+## 注意事项
+
+1. 使用 `useAuth` 的组件必须包裹在 `AuthProvider` 内
+2. 所有认证相关的 API 调用都会自动处理 token 和错误提示
+3. 用户信息会自动缓存到本地存储,避免重复请求
+4. 退出登录会自动清除本地存储的 token 和用户信息
+5. 更新用户信息后会自动同步到本地存储

+ 16 - 0
.claude/commands/mini-check-页面实体路由检查.md

@@ -0,0 +1,16 @@
+---
+description: "检查页面相关的实体,schema, CRUD路由指令"
+---
+
+检查页面相关的实体,schema, CRUD路由, mini/src/api.ts中的rpc client 定义,以收集进行页面开发所需的上下文
+如果 mini/src/api.ts中的rpc client 定义缺少,就根据 src/server/api.ts中的api路由类型定义来 先添加
+
+简化的FileSchema定义: z.object({
+  id: z.number().int().positive().openapi({ description: '文件ID' }),
+  name: z.string().max(255).openapi({ description: '文件名', example: 'avatar.jpg' }),
+  fullUrl: z.string().openapi({ description: '文件完整URL', example: 'https://example.com/avatar.jpg' }),
+  type: z.string().nullable().openapi({ description: '文件类型', example: 'image/jpeg' }),
+  size: z.number().nullable().openapi({ description: '文件大小(字节)', example: 102400 })
+}).nullable().optional().openapi({
+  description: '文件信息'
+}),

+ 623 - 0
.claude/commands/mini-form-小程序表单开发.md

@@ -0,0 +1,623 @@
+---
+description: "小程序表单开发指令"
+---
+
+# 小程序表单开发规范 (Tailwind CSS版)
+
+## 概述
+
+本规范定义了基于Taro框架的小程序表单开发标准,采用react-hook-form进行状态管理,zod进行表单验证,Tailwind CSS v4进行样式设计。
+
+## 技术栈
+
+- **Taro 4** - 跨端小程序框架
+- **React 18** - 前端框架
+- **React Hook Form 7** - 表单状态管理
+- **Zod 4** - 模式验证
+- **@hookform/resolvers** - 验证器集成
+- **Tailwind CSS v4** - 原子化CSS框架
+
+## 目录结构
+
+### 推荐结构(大型/复用表单)
+```
+mini/
+├── src/
+│   ├── components/
+│   │   └── ui/
+│   │       ├── form.tsx        # 表单核心组件
+│   │       ├── input.tsx       # 输入框组件
+│   │       ├── label.tsx       # 标签组件
+│   │       └── button.tsx      # 按钮组件
+│   ├── utils/
+│   │   ├── cn.ts               # 类名合并工具
+│   │   └── validators.ts       # 验证规则(可选)
+│   └── schemas/
+│       └── user.schema.ts      # 表单验证模式(可选)
+```
+
+### 简化结构(小型/单次使用表单)
+```
+mini/
+├── src/
+│   ├── components/
+│   │   └── ui/
+│   │       ├── form.tsx        # 表单核心组件
+│   │       ├── input.tsx       # 输入框组件
+│   │       └── button.tsx      # 按钮组件
+└── src/pages/
+    └── your-page/
+        └── index.tsx           # 验证规则直接定义在页面中
+```
+
+## 核心组件
+
+### 1. 表单组件系统
+
+#### 1.1 Form 组件
+```typescript
+// mini/src/components/ui/form.tsx
+import { createContext, useContext, forwardRef } from 'react'
+import { useFormContext } from 'react-hook-form'
+import { cn } from '@/utils/cn'
+
+const Form = forwardRef<
+  React.ElementRef<typeof TaroForm>,
+  React.ComponentPropsWithoutRef<typeof TaroForm>
+>(({ className, ...props }, ref) => {
+  return (
+    <TaroForm
+      ref={ref}
+      className={cn('space-y-6', className)}
+      {...props}
+    />
+  )
+})
+Form.displayName = 'Form'
+```
+
+#### 1.2 FormField 组件
+```typescript
+const FormField = forwardRef<
+  React.ElementRef<typeof Controller>,
+  React.ComponentPropsWithoutRef<typeof Controller>
+>(({ render, ...props }, ref) => {
+  return (
+    <Controller
+      ref={ref}
+      render={({ field, fieldState, formState }) => (
+        <FormItemContext.Provider value={{ name: props.name, fieldState, formState }}>
+          {render({ field, fieldState, formState })}
+        </FormItemContext.Provider>
+      )}
+      {...props}
+    />
+  )
+})
+```
+
+#### 1.3 FormItem 组件布局
+```typescript
+const FormItem = forwardRef<
+  React.ElementRef<typeof View>,
+  React.ComponentPropsWithoutRef<typeof View>
+>(({ className, ...props }, ref) => {
+  const id = useId()
+  
+  return (
+    <FormItemContext.Provider value={{ id }}>
+      <View
+        ref={ref}
+        className={cn('space-y-2', className)}
+        {...props}
+      />
+    </FormItemContext.Provider>
+  )
+})
+```
+
+#### 1.4 FormLabel 组件
+```typescript
+const FormLabel = forwardRef<
+  React.ElementRef<typeof Text>,
+  React.ComponentPropsWithoutRef<typeof Text>
+>(({ className, ...props }, ref) => {
+  const { error, formItemId } = useFormField()
+  
+  return (
+    <Label
+      ref={ref}
+      className={cn(
+        error && 'text-destructive',
+        className
+      )}
+      htmlFor={formItemId}
+      {...props}
+    />
+  )
+})
+```
+
+#### 1.5 FormControl 组件
+```typescript
+const FormControl = forwardRef<
+  React.ElementRef<typeof View>,
+  React.ComponentPropsWithoutRef<typeof View>
+>(({ ...props }, ref) => {
+  const { error, formItemId, formDescriptionId, formMessageId } = useFormField()
+  
+  return (
+    <View
+      ref={ref}
+      id={formItemId}
+      aria-describedby={
+        !error
+          ? `${formDescriptionId}`
+          : `${formDescriptionId} ${formMessageId}`
+      }
+      aria-invalid={!!error}
+      {...props}
+    />
+  )
+})
+```
+
+#### 1.6 FormMessage 组件
+```typescript
+const FormMessage = forwardRef<
+  React.ElementRef<typeof Text>,
+  React.ComponentPropsWithoutRef<typeof Text>
+>(({ className, children, ...props }, ref) => {
+  const { error, formMessageId } = useFormField()
+  const body = error ? String(error?.message) : children
+
+  if (!body) {
+    return null
+  }
+
+  return (
+    <Text
+      ref={ref}
+      id={formMessageId}
+      className={cn('text-sm font-medium text-destructive', className)}
+      {...props}
+    >
+      {body}
+    </Text>
+  )
+})
+```
+
+### 2. 表单验证集成
+
+#### 2.1 验证模式定义
+
+##### 方式1:大型/复用表单(推荐结构)
+```typescript
+// mini/src/schemas/user.schema.ts
+import { z } from 'zod'
+
+export const userSchema = z.object({
+  username: z.string()
+    .min(2, '用户名至少2个字符')
+    .max(20, '用户名最多20个字符')
+    .regex(/^[a-zA-Z0-9_]+$/, '用户名只能包含字母、数字和下划线'),
+  
+  phone: z.string()
+    .regex(/^1[3-9]\d{9}$/, '请输入正确的手机号码'),
+  
+  email: z.string()
+    .email('请输入正确的邮箱地址'),
+  
+  password: z.string()
+    .min(6, '密码至少6个字符')
+    .regex(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{6,}$/, '密码必须包含大小写字母和数字'),
+  
+  confirmPassword: z.string()
+}).refine((data) => data.password === data.confirmPassword, {
+  message: '两次输入的密码不一致',
+  path: ['confirmPassword']
+})
+
+export type UserFormData = z.infer<typeof userSchema>
+```
+
+##### 方式2:小型/单次使用表单(简化结构)
+```typescript
+// 直接在页面文件中定义
+import { z } from 'zod'
+import { zodResolver } from '@hookform/resolvers/zod'
+
+const loginSchema = z.object({
+  phone: z.string().regex(/^1[3-9]\d{9}$/, '请输入正确的手机号码'),
+  password: z.string().min(1, '请输入密码')
+})
+
+type LoginFormData = z.infer<typeof loginSchema>
+
+// 使用方式
+const form = useForm<LoginFormData>({
+  resolver: zodResolver(loginSchema),
+  defaultValues: {
+    phone: '',
+    password: ''
+  }
+})
+```
+
+#### 2.2 验证器配置(可选)
+```typescript
+// 对于复用验证规则,可以创建 utils/validators.ts
+// 小型表单可直接在页面中定义,无需单独文件
+
+// 常用验证规则(可选)
+export const phoneSchema = z.string().regex(/^1[3-9]\d{9}$/, '请输入正确的手机号码')
+export const emailSchema = z.string().email('请输入正确的邮箱地址')
+```
+
+### 3. 表单组件使用示例
+
+#### 3.1 完整表单示例
+```typescript
+// mini/src/pages/register/index.tsx
+import { useForm } from 'react-hook-form'
+import { zodResolver } from '@hookform/resolvers/zod'
+import { userSchema, UserFormData } from '@/schemas/user.schema'
+import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage } from '@/components/ui/form'
+import { Input } from '@/components/ui/input'
+import { Button } from '@/components/ui/button'
+
+export default function RegisterPage() {
+  const form = useForm<UserFormData>({
+    resolver: zodResolver(userSchema),
+    defaultValues: {
+      username: '',
+      phone: '',
+      email: '',
+      password: '',
+      confirmPassword: ''
+    }
+  })
+
+  const onSubmit = async (data: UserFormData) => {
+    try {
+      // 提交表单逻辑
+      console.log('表单数据:', data)
+    } catch (error) {
+      console.error('提交失败:', error)
+    }
+  }
+
+  return (
+    <View className="min-h-screen bg-gray-50 p-4">
+      <View className="max-w-md mx-auto">
+        <Text className="text-2xl font-bold text-center mb-6">用户注册</Text>
+        
+        <Form {...form}>
+          <TaroForm onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
+            <FormField
+              control={form.control}
+              name="username"
+              render={({ field }) => (
+                <FormItem>
+                  <FormLabel>用户名</FormLabel>
+                  <FormControl>
+                    <Input 
+                      placeholder="请输入用户名" 
+                      {...field}
+                      className="h-10"
+                    />
+                  </FormControl>
+                  <FormMessage />
+                </FormItem>
+              )}
+            />
+
+            <FormField
+              control={form.control}
+              name="phone"
+              render={({ field }) => (
+                <FormItem>
+                  <FormLabel>手机号</FormLabel>
+                  <FormControl>
+                    <Input 
+                      type="tel"
+                      placeholder="请输入手机号" 
+                      {...field}
+                      className="h-10"
+                    />
+                  </FormControl>
+                  <FormMessage />
+                </FormItem>
+              )}
+            />
+
+            <FormField
+              control={form.control}
+              name="email"
+              render={({ field }) => (
+                <FormItem>
+                  <FormLabel>邮箱</FormLabel>
+                  <FormControl>
+                    <Input 
+                      type="email"
+                      placeholder="请输入邮箱" 
+                      {...field}
+                      className="h-10"
+                    />
+                  </FormControl>
+                  <FormMessage />
+                </FormItem>
+              )}
+            />
+
+            <FormField
+              control={form.control}
+              name="password"
+              render={({ field }) => (
+                <FormItem>
+                  <FormLabel>密码</FormLabel>
+                  <FormControl>
+                    <Input 
+                      type="password"
+                      placeholder="请输入密码" 
+                      {...field}
+                      className="h-10"
+                    />
+                  </FormControl>
+                  <FormMessage />
+                </FormItem>
+              )}
+            />
+
+            <FormField
+              control={form.control}
+              name="confirmPassword"
+              render={({ field }) => (
+                <FormItem>
+                  <FormLabel>确认密码</FormLabel>
+                  <FormControl>
+                    <Input 
+                      type="password"
+                      placeholder="请再次输入密码" 
+                      {...field}
+                      className="h-10"
+                    />
+                  </FormControl>
+                  <FormMessage />
+                </FormItem>
+              )}
+            />
+
+            <Button 
+              type="submit" 
+              className="w-full h-10 bg-blue-500 text-white"
+              loading={form.formState.isSubmitting}
+            >
+              注册
+            </Button>
+          </TaroForm>
+        </Form>
+      </View>
+    </View>
+  )
+}
+```
+
+#### 3.2 登录表单示例
+```typescript
+// mini/src/pages/login/index.tsx
+const loginSchema = z.object({
+  phone: z.string().regex(/^1[3-9]\d{9}$/, '请输入正确的手机号码'),
+  password: z.string().min(1, '请输入密码')
+})
+
+export default function LoginPage() {
+  const form = useForm({
+    resolver: zodResolver(loginSchema),
+    defaultValues: {
+      phone: '',
+      password: ''
+    }
+  })
+
+  return (
+    <View className="min-h-screen bg-gray-50 p-4">
+      <View className="max-w-md mx-auto">
+        <Text className="text-2xl font-bold text-center mb-6">用户登录</Text>
+        
+        <Form {...form}>
+          <TaroForm onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
+            <FormField
+              control={form.control}
+              name="phone"
+              render={({ field }) => (
+                <FormItem>
+                  <FormLabel>手机号</FormLabel>
+                  <FormControl>
+                    <Input 
+                      type="tel"
+                      placeholder="请输入手机号"
+                      {...field}
+                      className="h-10"
+                    />
+                  </FormControl>
+                  <FormMessage />
+                </FormItem>
+              )}
+            />
+
+            <FormField
+              control={form.control}
+              name="password"
+              render={({ field }) => (
+                <FormItem>
+                  <FormLabel>密码</FormLabel>
+                  <FormControl>
+                    <Input 
+                      type="password"
+                      placeholder="请输入密码"
+                      {...field}
+                      className="h-10"
+                    />
+                  </FormControl>
+                  <FormMessage />
+                </FormItem>
+              )}
+            />
+
+            <Button type="submit" className="w-full h-10 bg-blue-500 text-white">
+              登录
+            </Button>
+          </TaroForm>
+        </Form>
+      </View>
+    </View>
+  )
+}
+```
+
+### 4. 表单验证最佳实践
+
+#### 4.1 实时验证
+```typescript
+const form = useForm({
+  resolver: zodResolver(userSchema),
+  mode: 'onChange', // 实时验证
+  reValidateMode: 'onChange',
+  defaultValues: {
+    username: '',
+    phone: ''
+  }
+})
+```
+
+#### 4.2 异步验证
+```typescript
+const asyncUsernameSchema = z.object({
+  username: z.string()
+    .min(2, '用户名至少2个字符')
+    .max(20, '用户名最多20个字符')
+    .refine(async (username) => {
+      const response = await checkUsernameAvailability(username)
+      return response.available
+    }, '用户名已被占用')
+})
+```
+
+#### 4.3 错误处理
+```typescript
+const onSubmit = async (data: UserFormData) => {
+  try {
+    await registerUser(data)
+    Taro.showToast({ title: '注册成功' })
+    Taro.navigateBack()
+  } catch (error) {
+    if (error.response?.data?.errors) {
+      Object.keys(error.response.data.errors).forEach(field => {
+        form.setError(field as any, {
+          message: error.response.data.errors[field][0]
+        })
+      })
+    }
+  }
+}
+```
+
+### 5. 样式规范
+
+#### 5.1 表单布局
+```typescript
+// 标准间距
+const formSpacing = {
+  item: 'space-y-2',      // 表单项内部间距
+  section: 'space-y-6',   // 表单区域间距
+  group: 'space-y-4'      // 表单组间距
+}
+
+// 输入框样式
+const inputStyles = {
+  base: 'h-10 px-3 bg-white border border-gray-300 rounded-md',
+  focus: 'focus:border-blue-500 focus:ring-1 focus:ring-blue-500',
+  error: 'border-red-500 focus:border-red-500 focus:ring-red-500'
+}
+```
+
+#### 5.2 响应式设计
+```typescript
+<View className="max-w-md mx-auto p-4 sm:p-6 md:p-8">
+  <Form className="space-y-4 sm:space-y-6">
+    <FormItem className="grid grid-cols-1 sm:grid-cols-2 gap-4">
+      <FormControl>
+        <Input className="w-full" />
+      </FormControl>
+    </FormItem>
+  </Form>
+</View>
+```
+
+### 6. 性能优化
+
+#### 6.1 表单防抖
+```typescript
+import { debounce } from 'lodash'
+
+const debouncedValidate = debounce((value) => {
+  form.trigger('username')
+}, 300)
+
+const handleUsernameChange = (value: string) => {
+  form.setValue('username', value)
+  debouncedValidate(value)
+}
+```
+
+#### 6.2 条件渲染
+```typescript
+// 避免不必要的重渲染
+const FormFieldMemo = React.memo(({ name, control, render }) => (
+  <FormField
+    name={name}
+    control={control}
+    render={render}
+  />
+))
+```
+
+### 7. 无障碍支持
+
+#### 7.1 语义化标签
+```typescript
+<FormItem>
+  <FormLabel>
+    <Text className="sr-only">用户名</Text>
+  </FormLabel>
+  <FormControl>
+    <Input 
+      aria-label="用户名"
+      aria-required="true"
+      aria-invalid={!!form.formState.errors.username}
+    />
+  </FormControl>
+</FormItem>
+```
+
+#### 7.2 键盘导航
+```typescript
+// 支持Tab键导航
+const handleKeyPress = (e) => {
+  if (e.key === 'Enter') {
+    form.handleSubmit(onSubmit)()
+  }
+}
+```
+
+## 注意事项
+
+1. **类型安全**:始终使用TypeScript定义表单数据类型
+2. **验证时机**:根据业务需求选择合适的验证时机(onChange/onBlur/onSubmit)
+3. **错误处理**:提供清晰的用户友好的错误消息
+4. **性能考虑**:避免在大型表单中使用实时验证
+5. **无障碍**:确保表单对所有用户可访问
+6. **移动端适配**:测试在小屏幕设备上的显示效果
+7. **状态管理**:合理使用React Hook Form的API管理复杂表单状态

+ 9 - 0
.claude/commands/mini-navbar-顶部导航条使用.md

@@ -0,0 +1,9 @@
+---
+description: "顶部导航条Navbar使用指令"
+---
+
+使用 import { Navbar } from '@/components/ui/navbar'
+
+使用前先查看 mini/src/components/ui/navbar.tsx 了解具体用法
+
+注意:一级页面不需要返回按钮

+ 233 - 0
.claude/commands/mini-platform-小程序环境检测.md

@@ -0,0 +1,233 @@
+---
+description: "小程序平台检测工具 - 用于识别当前运行平台环境"
+---
+
+# 小程序平台检测工具使用文档
+
+## 功能概述
+
+`mini/src/utils/platform.ts` 提供了一套完整的平台检测工具,用于在 Taro 跨端小程序中识别当前运行环境,支持微信小程序、H5 网页端等多种平台的条件判断。
+
+## 导入方式
+
+```typescript
+import { getPlatform, isWeapp, isH5 } from '@/utils/platform'
+```
+
+## 核心功能
+
+### 1. 获取当前平台
+```typescript
+getPlatform(): TaroGeneral.ENV_TYPE
+```
+返回当前运行环境的平台类型枚举值。
+
+**返回值说明:**
+- `WEAPP`: 微信小程序
+- `WEB`: H5 网页端
+- `RN`: React Native
+- `SWAN`: 百度智能小程序
+- `ALIPAY`: 支付宝小程序
+- `TT`: 字节跳动小程序
+- `QQ`: QQ 小程序
+
+**使用示例:**
+```typescript
+import { getPlatform } from '@/utils/platform'
+import Taro from '@tarojs/taro'
+
+const currentPlatform = getPlatform()
+console.log('当前平台:', currentPlatform)
+
+// 根据平台执行不同逻辑
+switch (currentPlatform) {
+  case Taro.ENV_TYPE.WEAPP:
+    // 微信小程序专属逻辑
+    break
+  case Taro.ENV_TYPE.WEB:
+    // H5 网页端专属逻辑
+    break
+  default:
+    // 其他平台通用逻辑
+}
+```
+
+### 2. 是否为微信小程序
+```typescript
+isWeapp(): boolean
+```
+判断当前是否在微信小程序环境中运行。
+
+**使用示例:**
+```typescript
+import { isWeapp } from '@/utils/platform'
+
+if (isWeapp()) {
+  // 微信小程序专属功能
+  wx.login({
+    success: (res) => {
+      console.log('微信登录成功:', res.code)
+    }
+  })
+  
+  // 使用小程序 API
+  wx.getUserProfile({
+    desc: '用于完善用户资料',
+    success: (res) => {
+      console.log('用户信息:', res.userInfo)
+    }
+  })
+} else {
+  // 非小程序环境的替代方案
+  console.log('当前不是微信小程序环境')
+}
+```
+
+### 3. 是否为 H5 网页端
+```typescript
+isH5(): boolean
+```
+判断当前是否在 H5 网页端环境中运行。
+
+**使用示例:**
+```typescript
+import { isH5 } from '@/utils/platform'
+
+if (isH5()) {
+  // H5 网页端专属功能
+  // 使用浏览器 API
+  localStorage.setItem('token', 'your-token')
+  
+  // 使用 DOM API
+  window.addEventListener('resize', handleResize)
+} else {
+  // 小程序环境的替代方案
+  Taro.setStorageSync('token', 'your-token')
+}
+```
+
+## 实际应用场景
+
+### 场景1:条件渲染组件
+```typescript
+import { isWeapp, isH5 } from '@/utils/platform'
+
+const PlatformSpecificButton = () => {
+  if (isWeapp()) {
+    return (
+      <Button onClick={() => wx.navigateToMiniProgram({ appId: 'targetAppId' })}>
+        打开其他小程序
+      </Button>
+    )
+  }
+  
+  if (isH5()) {
+    return (
+      <Button onClick={() => window.open('https://example.com', '_blank')}>
+        打开外部链接
+      </Button>
+    )
+  }
+  
+  return null
+}
+```
+
+### 场景2:平台差异化 API 调用
+```typescript
+import { isWeapp, isH5 } from '@/utils/platform'
+
+const uploadImage = async (file: File) => {
+  if (isWeapp()) {
+    // 小程序上传
+    return new Promise((resolve, reject) => {
+      wx.uploadFile({
+        url: '/api/upload',
+        filePath: file.path,
+        name: 'file',
+        success: resolve,
+        fail: reject
+      })
+    })
+  }
+  
+  if (isH5()) {
+    // H5 上传
+    const formData = new FormData()
+    formData.append('file', file)
+    
+    const response = await fetch('/api/upload', {
+      method: 'POST',
+      body: formData
+    })
+    return response.json()
+  }
+}
+```
+
+### 场景3:平台特定样式处理
+```typescript
+import { isWeapp, isH5 } from '@/utils/platform'
+
+const getPlatformStyles = () => {
+  const baseStyles = 'p-4 rounded-lg'
+  
+  if (isWeapp()) {
+    return `${baseStyles} bg-green-100 text-green-800`
+  }
+  
+  if (isH5()) {
+    return `${baseStyles} bg-blue-100 text-blue-800 shadow-lg`
+  }
+  
+  return baseStyles
+}
+```
+
+## 与 Taro API 的集成
+
+平台检测工具与 Taro 的 API 完美集成,可以结合使用:
+
+```typescript
+import { isWeapp, isH5 } from '@/utils/platform'
+import Taro from '@tarojs/taro'
+
+// 平台特定的导航
+const navigateToPage = (url: string) => {
+  if (isWeapp()) {
+    Taro.navigateTo({ url })
+  } else if (isH5()) {
+    window.location.href = url
+  }
+}
+
+// 平台特定的存储
+const setStorage = (key: string, value: any) => {
+  if (isWeapp()) {
+    Taro.setStorageSync(key, value)
+  } else if (isH5()) {
+    localStorage.setItem(key, JSON.stringify(value))
+  }
+}
+```
+
+## 注意事项
+
+1. **必须在 Taro 环境中使用**:这些工具函数依赖于 Taro 的运行时环境
+2. **服务端渲染**:在 SSR 环境中使用时需要添加环境判断
+3. **测试环境**:在单元测试时可能需要 mock Taro 环境
+4. **性能优化**:工具函数都是轻量级的,不会带来性能开销
+
+## 扩展建议
+
+可以根据项目需要扩展更多平台检测函数:
+
+```typescript
+// 在 platform.ts 中添加更多检测函数
+export const isAlipay = (): boolean => {
+  return getPlatform() === Taro.ENV_TYPE.ALIPAY
+}
+
+export const isBaidu = (): boolean => {
+  return getPlatform() === Taro.ENV_TYPE.SWAN
+}

+ 370 - 0
.claude/commands/mini-rpc-小程序RPC开发规范.md

@@ -0,0 +1,370 @@
+---
+description: "小程序RPC客户端开发规范 - 基于Taro + Hono RPC的完整实现指南"
+---
+
+# 小程序RPC开发规范
+
+## 概述
+
+本文档定义了小程序端使用Taro框架结合Hono RPC客户端的标准开发规范。基于现有的`mini/src/api.ts`、`mini/src/utils/rpc-client.ts`和`mini/src/pages/login/wechat-login.tsx`中的最佳实践。
+
+## 核心架构
+
+### 1. RPC客户端配置
+
+#### 1.1 客户端初始化 (`mini/src/utils/rpc-client.ts`)
+
+```typescript
+// 环境配置
+const API_BASE_URL = process.env.TARO_APP_API_BASE_URL || 'http://localhost:3000'
+
+// 自定义fetch适配Taro.request
+const taroFetch: any = async (input, init) => {
+  const url = typeof input === 'string' ? input : input.url
+  const method = init.method || 'GET'
+  
+  const requestHeaders: Record<string, string> = init.headers;
+
+  // 自动设置content-type
+  const keyOfContentType = Object.keys(requestHeaders).find(item => item.toLowerCase() === 'content-type')
+  if (!keyOfContentType) {
+    requestHeaders['content-type'] = 'application/json'
+  }
+
+  // 构建Taro请求选项
+  const options: Taro.request.Option = {
+    url,
+    method: method as any,
+    data: init.body,
+    header: requestHeaders
+  }
+
+  // 自动添加token认证
+  const token = Taro.getStorageSync('mini_token')
+  if (token) {
+    options.header = {
+      ...options.header,
+      'Authorization': `Bearer ${token}`
+    }
+  }
+
+  try {
+    const response = await Taro.request(options)
+    
+    // 处理响应数据
+    const body = response.statusCode === 204
+      ? null
+      : responseHeaders['content-type']!.includes('application/json')
+        ? JSON.stringify(response.data)
+        : response.data;
+
+    return new ResponsePolyfill(
+      body,
+      {
+        status: response.statusCode,
+        statusText: response.errMsg || 'OK',
+        headers: responseHeaders
+      }
+    )
+  } catch (error) {
+    console.error('API Error:', error)
+    Taro.showToast({
+      title: error.message || '网络错误',
+      icon: 'none'
+    })
+    throw error
+  }
+}
+
+// 创建Hono RPC客户端工厂函数
+export const rpcClient = <T extends Hono>() => {
+  return hc<T>(`${API_BASE_URL}`, {
+    fetch: taroFetch
+  })
+}
+```
+
+#### 1.2 客户端API定义 (`mini/src/api.ts`)
+
+```typescript
+import type { AuthRoutes, UserRoutes, RoleRoutes, FileRoutes } from '@/server/api'
+import { rpcClient } from './utils/rpc-client'
+
+// 创建各个模块的RPC客户端
+export const authClient = rpcClient<AuthRoutes>().api.v1.auth
+export const userClient = rpcClient<UserRoutes>().api.v1.users
+export const roleClient = rpcClient<RoleRoutes>().api.v1.roles
+export const fileClient = rpcClient<FileRoutes>().api.v1.files
+```
+
+## 使用规范
+
+### 2.1 调用方式
+
+#### 标准GET请求
+```typescript
+const response = await userClient.$get({
+  query: {
+    page: 1,
+    pageSize: 10
+  }
+})
+```
+
+#### POST请求(带请求体)
+```typescript
+const response = await authClient['mini-login'].$post({
+  json: {
+    code: loginRes.code,
+    userInfo: userProfile.userInfo
+  }
+})
+```
+
+#### 带路径参数的请求
+```typescript
+const response = await userClient[':id'].$get({
+  param: {
+    id: userId
+  }
+})
+```
+
+### 2.2 响应处理规范
+
+#### 成功响应处理
+```typescript
+if (response.status === 200) {
+  const { token, user, isNewUser } = await response.json()
+  
+  // 保存token到本地存储
+  Taro.setStorageSync('mini_token', token)
+  Taro.setStorageSync('userInfo', user)
+  
+  // 显示成功提示
+  Taro.showToast({
+    title: isNewUser ? '注册成功' : '登录成功',
+    icon: 'success',
+    duration: 1500
+  })
+}
+```
+
+#### 错误响应处理
+```typescript
+try {
+  const response = await authClient['mini-login'].$post({
+    json: { code, userInfo }
+  })
+  
+  if (response.status !== 200) {
+    const errorData = await response.json()
+    throw new Error(errorData.message || '操作失败')
+  }
+} catch (error: any) {
+  const errorMessage = error.message || '网络错误'
+  
+  // 分类处理错误
+  if (errorMessage.includes('用户拒绝授权')) {
+    Taro.showModal({
+      title: '提示',
+      content: '需要授权才能使用小程序的全部功能',
+      showCancel: false
+    })
+  } else {
+    Taro.showToast({
+      title: errorMessage,
+      icon: 'none',
+      duration: 3000
+    })
+  }
+}
+```
+
+## 微信小程序特殊场景
+
+### 3.1 微信登录流程
+
+基于`mini/src/pages/login/wechat-login.tsx`的最佳实践:
+
+```typescript
+const handleWechatLogin = async () => {
+  try {
+    Taro.showLoading({
+      title: '登录中...',
+      mask: true
+    })
+
+    // 1. 获取用户信息授权
+    const userProfile = await Taro.getUserProfile({
+      desc: '用于完善用户资料'
+    })
+
+    // 2. 获取登录code
+    const loginRes = await Taro.login()
+    
+    if (!loginRes.code) {
+      throw new Error('获取登录凭证失败')
+    }
+
+    // 3. 调用RPC接口
+    const response = await authClient['mini-login'].$post({
+      json: {
+        code: loginRes.code,
+        userInfo: userProfile.userInfo
+      }
+    })
+
+    Taro.hideLoading()
+
+    if (response.status === 200) {
+      const { token, user, isNewUser } = await response.json()
+      
+      // 4. 保存登录态
+      Taro.setStorageSync('userInfo', user)
+      Taro.setStorageSync('mini_token', token)
+      
+      // 5. 跳转页面
+      Taro.switchTab({ url: '/pages/index/index' })
+    }
+  } catch (error) {
+    Taro.hideLoading()
+    // 错误处理...
+  }
+}
+```
+
+### 3.2 平台检测
+
+```typescript
+import { isWeapp } from '@/utils/platform'
+
+// 检查是否为微信小程序环境
+const wechatEnv = isWeapp()
+if (!wechatEnv) {
+  Taro.showModal({
+    title: '提示',
+    content: '微信登录功能仅支持在微信小程序中使用',
+    showCancel: false
+  })
+}
+```
+
+## 开发规范
+
+### 4.1 文件结构
+
+```
+mini/
+├── src/
+│   ├── api.ts              # RPC客户端定义
+│   ├── utils/
+│   │   └── rpc-client.ts   # RPC客户端工厂
+│   └── pages/
+│       └── [功能页面]/
+│           └── index.tsx   # 页面逻辑
+```
+
+### 4.2 命名规范
+
+- **客户端命名**:`[模块名]Client`(如`authClient`、`userClient`)
+- **方法命名**:遵循RESTful规范(如`$get`、`$post`、`$put`、`$delete`)
+- **路径命名**:使用小写字母和连字符(如`mini-login`)
+
+### 4.3 类型安全
+
+```typescript
+// 使用InferResponseType提取响应类型
+import type { InferResponseType } from 'hono/client'
+type LoginResponse = InferResponseType<typeof authClient['mini-login']['$post'], 200>
+
+// 使用InferRequestType提取请求类型
+import type { InferRequestType } from 'hono/client'
+type LoginRequest = InferRequestType<typeof authClient['mini-login']['$post']>['json']
+```
+
+### 4.4 环境配置
+
+在`mini/.env`中配置API地址:
+
+```bash
+TARO_APP_API_BASE_URL=https://your-api-domain.com
+```
+
+## 最佳实践
+
+### 5.1 请求封装
+
+```typescript
+// 创建通用请求hook
+const useApiRequest = () => {
+  const [loading, setLoading] = useState(false)
+  
+  const request = async <T>(
+    apiCall: () => Promise<Response>,
+    successCallback?: (data: T) => void,
+    errorCallback?: (error: Error) => void
+  ) => {
+    setLoading(true)
+    try {
+      const response = await apiCall()
+      const data = await response.json()
+      
+      if (response.status === 200) {
+        successCallback?.(data)
+      } else {
+        throw new Error(data.message || '请求失败')
+      }
+    } catch (error) {
+      errorCallback?.(error)
+    } finally {
+      setLoading(false)
+    }
+  }
+  
+  return { loading, request }
+}
+```
+
+### 5.2 错误处理
+
+```typescript
+const handleApiError = (error: any) => {
+  const message = error.message || '网络错误'
+  
+  // 网络错误
+  if (message.includes('Network') || message.includes('网络')) {
+    Taro.showModal({
+      title: '网络错误',
+      content: '请检查网络连接后重试',
+      showCancel: false
+    })
+    return
+  }
+  
+  // 业务错误
+  Taro.showToast({
+    title: message,
+    icon: 'none',
+    duration: 3000
+  })
+}
+```
+
+### 5.3 加载状态管理
+
+```typescript
+const [loading, setLoading] = useState(false)
+
+const handleRequest = async () => {
+  setLoading(true)
+  Taro.showLoading({ title: '加载中...' })
+  
+  try {
+    const response = await apiClient.method.$post({ json: data })
+    // 处理响应...
+  } finally {
+    Taro.hideLoading()
+    setLoading(false)
+  }
+}

+ 6 - 0
.claude/commands/mini-shadui-可用组件检查.md

@@ -0,0 +1,6 @@
+---
+description: "检查小程序当前可用的shadui组件指令"
+---
+
+检查页面相关的小程序当前可用的shadui组件 定义,以收集进行页面开发所需的上下文
+在 mini/src/components/ui/ 下

+ 1193 - 0
.claude/commands/mini-shadui-页面开发.md

@@ -0,0 +1,1193 @@
+---
+description: "小程序shadui页面的开发指令"
+---
+
+# 小程序页面开发指令
+
+## 概述
+本指令规范了基于Taro + React + Shadui + Tailwind CSS的小程序页面开发流程,包含tabbar页和非tabbar页的创建标准和最佳实践,涵盖了认证、RPC调用、React Query v5使用等核心功能。
+
+## 小程序Shadui路径
+mini/src/components/ui
+
+## 当前可用的Shadui组件
+基于项目实际文件,当前小程序可用的shadui组件如下:
+
+### 基础组件
+- **Button** - 按钮组件 (`button.tsx`)
+- **Card** - 卡片组件 (`card.tsx`)
+- **Input** - 输入框组件 (`input.tsx`)
+- **Label** - 标签组件 (`label.tsx`)
+- **Form** - 表单组件 (`form.tsx`)
+
+### 交互组件
+- **AvatarUpload** - 头像上传组件 (`avatar-upload.tsx`)
+- **Carousel** - 轮播图组件 (`carousel.tsx`)
+- **Image** - 图片组件 (`image.tsx`)
+
+### 导航组件
+- **Navbar** - 顶部导航栏组件 (`navbar.tsx`)
+- **TabBar** - 底部标签栏组件 (`tab-bar.tsx`)
+
+### 布局组件
+- **TabBarLayout**: 用于tabbar页面,包含底部导航
+
+- 根据需求可扩展更多业务组件
+
+## 组件使用示例
+
+### Button 组件
+```typescript
+import { Button } from '@/components/ui/button'
+
+// 基础用法
+<Button onClick={handleClick}>主要按钮</Button>
+
+// 不同尺寸
+<Button size="sm">小按钮</Button>
+<Button size="md">中按钮</Button>
+<Button size="lg">大按钮</Button>
+
+// 不同样式
+<Button variant="primary">主要按钮</Button>
+<Button variant="secondary">次要按钮</Button>
+<Button variant="outline">边框按钮</Button>
+<Button variant="ghost">幽灵按钮</Button>
+```
+
+### Input 组件
+```typescript
+import { Input } from '@/components/ui/input'
+
+// 基础用法
+<Input placeholder="请输入内容" />
+
+// 受控组件
+<Input value={value} onChange={handleChange} />
+
+// 不同类型
+<Input type="text" placeholder="文本输入" />
+<Input type="number" placeholder="数字输入" />
+<Input type="password" placeholder="密码输入" />
+```
+
+### Form 组件
+```typescript
+import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage } from '@/components/ui/form'
+import { useForm } from 'react-hook-form'
+import { zodResolver } from '@hookform/resolvers/zod'
+import { z } from 'zod'
+
+const formSchema = z.object({
+  username: z.string().min(2, '用户名至少2个字符'),
+  email: z.string().email('请输入有效的邮箱地址')
+})
+
+const form = useForm({
+  resolver: zodResolver(formSchema),
+  defaultValues: { username: '', email: '' }
+})
+
+<Form {...form}>
+  <FormField
+    name="username"
+    render={({ field }) => (
+      <FormItem>
+        <FormLabel>用户名</FormLabel>
+        <FormControl>
+          <Input placeholder="请输入用户名" {...field} />
+        </FormControl>
+        <FormMessage />
+      </FormItem>
+    )}
+  />
+</Form>
+```
+
+### Card 组件
+```typescript
+import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
+
+<Card>
+  <CardHeader>
+    <CardTitle>卡片标题</CardTitle>
+  </CardHeader>
+  <CardContent>
+    <Text>卡片内容</Text>
+  </CardContent>
+</Card>
+```
+
+### Navbar 组件
+```typescript
+import { Navbar } from '@/components/ui/navbar'
+
+// 基础用法
+<Navbar title="页面标题" />
+
+// 带返回按钮
+<Navbar 
+  title="页面标题" 
+  leftIcon="i-heroicons-chevron-left-20-solid"
+  onClickLeft={() => Taro.navigateBack()}
+/>
+
+// 带右侧操作
+<Navbar 
+  title="页面标题"
+  rightIcon="i-heroicons-share-20-solid"
+  onClickRight={handleShare}
+/>
+```
+
+### Carousel 组件
+```typescript
+// 实际页面使用示例
+export function HomeCarousel() {
+  const bannerItems: CarouselItem[] = [
+    {
+      src: 'https://via.placeholder.com/750x400/3B82F6/FFFFFF?text=Banner+1',
+      title: '新品上市',
+      description: '最新款式,限时优惠',
+      link: '/pages/goods/new-arrival'
+    },
+    {
+      src: 'https://via.placeholder.com/750x400/EF4444/FFFFFF?text=Banner+2',
+      title: '限时秒杀',
+      description: '每日特价,不容错过',
+      link: '/pages/goods/flash-sale'
+    },
+    {
+      src: 'https://via.placeholder.com/750x400/10B981/FFFFFF?text=Banner+3',
+      title: '会员专享',
+      description: '会员专享折扣和福利',
+      link: '/pages/member/benefits'
+    }
+  ]
+
+  const handleBannerClick = (item: CarouselItem, index: number) => {
+    if (item.link) {
+      // 使用Taro跳转
+      Taro.navigateTo({
+        url: item.link
+      })
+    }
+  }
+
+  return (
+    <View className="w-full">
+      <Carousel
+        items={bannerItems}
+        height={400}
+        autoplay={true}
+        interval={4000}
+        circular={true}
+        rounded="none"
+        onItemClick={handleBannerClick}
+      />
+    </View>
+  )
+}
+```
+
+## 页面类型分类
+
+### 1. TabBar页面(底部导航页)
+特点:
+- 使用 `TabBarLayout` 布局组件
+- 路径配置在 `mini/src/app.config.ts` 中的 `tabBar.list`
+- 包含底部导航栏,用户可直接切换
+- 通常包含 `Navbar` 顶部导航组件
+- 示例页面:首页、发现、个人中心
+
+### 2. 非TabBar页面(独立页面)
+特点:
+- 不使用 `TabBarLayout`,直接渲染内容
+- 使用 `Navbar` 组件作为顶部导航
+- 需要手动处理返回导航
+- 示例页面:登录、注册、详情页
+
+## 开发流程
+
+### 1. 创建页面目录
+```bash
+# TabBar页面
+mkdir -p mini/src/pages/[页面名称]
+
+# 非TabBar页面
+mkdir -p mini/src/pages/[页面名称]
+```
+
+### 2. 创建页面文件
+
+#### TabBar页面模板
+```typescript
+// mini/src/pages/[页面名称]/index.tsx
+import React from 'react'
+import { View, Text } from '@tarojs/components'
+import { TabBarLayout } from '@/layouts/tab-bar-layout'
+import { Navbar } from '@/components/ui/navbar'
+import { Button } from '@/components/ui/button'
+import { Card } from '@/components/ui/card'
+import './index.css'
+
+const [页面名称]Page: React.FC = () => {
+  return (
+    <TabBarLayout activeKey="[对应tabBar.key]">
+      <Navbar
+        title="页面标题"
+        rightIcon="i-heroicons-[图标名称]-20-solid"
+        onClickRight={() => console.log('点击右上角')}
+        leftIcon=""
+      />
+      <View className="px-4 py-4">
+        <Card>
+          <CardHeader>
+            <CardTitle>欢迎使用</CardTitle>
+          </CardHeader>
+          <CardContent>
+            <Text>这是一个使用shadui组件的TabBar页面</Text>
+            <Button className="mt-4">开始使用</Button>
+          </CardContent>
+        </Card>
+      </View>
+    </TabBarLayout>
+  )
+}
+
+export default [页面名称]Page
+```
+
+#### 非TabBar页面模板
+```typescript
+// mini/src/pages/[页面名称]/index.tsx
+import { View } from '@tarojs/components'
+import { useEffect } from 'react'
+import Taro from '@tarojs/taro'
+import { Navbar } from '@/components/ui/navbar'
+import { Card } from '@/components/ui/card'
+import { Button } from '@/components/ui/button'
+import './index.css'
+
+export default function [页面名称]() {
+  useEffect(() => {
+    Taro.setNavigationBarTitle({
+      title: '页面标题'
+    })
+  }, [])
+
+  return (
+    <View className="min-h-screen bg-gray-50">
+      <Navbar
+        title="页面标题"
+        backgroundColor="bg-transparent"
+        textColor="text-gray-900"
+        border={false}
+      />
+      <View className="px-6 py-4">
+        <Card>
+          <CardContent>
+            <Text>这是一个使用shadui组件的非TabBar页面</Text>
+            <Button className="mt-4">返回</Button>
+          </CardContent>
+        </Card>
+      </View>
+    </View>
+  )
+}
+```
+
+### 3. 页面配置文件
+```typescript
+// mini/src/pages/[页面名称]/index.config.ts
+export default definePageConfig({
+  navigationBarTitleText: '页面标题',
+  enablePullDownRefresh: true,
+  backgroundTextStyle: 'dark',
+  navigationBarBackgroundColor: '#ffffff',
+  navigationBarTextStyle: 'black'
+})
+```
+
+### 4. 样式文件
+统一使用tailwindcss类,index.css为空即可
+```css
+/* mini/src/pages/[页面名称]/index.css */
+
+```
+
+## 高级功能模板
+
+### 1. 带认证的页面模板
+```typescript
+// mini/src/pages/[需要认证的页面]/index.tsx
+import { View, Text } from '@tarojs/components'
+import { useEffect } from 'react'
+import { useAuth } from '@/utils/auth'
+import Taro from '@tarojs/taro'
+import { Navbar } from '@/components/ui/navbar'
+import { Card } from '@/components/ui/card'
+
+export default function ProtectedPage() {
+  const { user, isLoading, isLoggedIn } = useAuth()
+
+  useEffect(() => {
+    if (!isLoading && !isLoggedIn) {
+      Taro.navigateTo({ url: '/pages/login/index' })
+    }
+  }, [isLoading, isLoggedIn])
+
+  if (isLoading) {
+    return (
+      <View className="flex-1 flex items-center justify-center">
+        <View className="i-heroicons-arrow-path-20-solid animate-spin w-8 h-8 text-blue-500" />
+      </View>
+    )
+  }
+
+  if (!user) return null
+
+  return (
+    <View className="min-h-screen bg-gray-50">
+      <Navbar title="受保护页面" leftIcon="" />
+      <View className="px-4 py-4">
+        <Text>欢迎, {user.username}</Text>
+      </View>
+    </View>
+  )
+}
+```
+
+### 2. 带API调用的页面模板
+```typescript
+// mini/src/pages/[数据展示页面]/index.tsx
+import { View, ScrollView } from '@tarojs/components'
+import { useQuery } from '@tanstack/react-query'
+import { userClient } from '@/api'
+import { InferResponseType } from 'hono'
+import Taro from '@tarojs/taro'
+
+type UserListResponse = InferResponseType<typeof userClient.$get, 200>
+
+export default function UserListPage() {
+  const { data, isLoading, error } = useQuery<UserListResponse>({
+    queryKey: ['users'],
+    queryFn: async () => {
+      const response = await userClient.$get({})
+      if (response.status !== 200) {
+        throw new Error('获取用户列表失败')
+      }
+      return response.json()
+    },
+    staleTime: 5 * 60 * 1000, // 5分钟
+  })
+
+  if (isLoading) {
+    return (
+      <View className="flex-1 flex items-center justify-center">
+        <View className="i-heroicons-arrow-path-20-solid animate-spin w-8 h-8 text-blue-500" />
+      </View>
+    )
+  }
+
+  if (error) {
+    return (
+      <View className="flex-1 flex items-center justify-center">
+        <Text className="text-red-500">{error.message}</Text>
+      </View>
+    )
+  }
+
+  return (
+    <ScrollView className="h-screen">
+      <Navbar title="用户列表" leftIcon="" />
+      <View className="px-4 py-4">
+        {data?.data.map(user => (
+          <View key={user.id} className="bg-white rounded-lg p-4 mb-3">
+            <Text>{user.username}</Text>
+          </View>
+        ))}
+      </View>
+    </ScrollView>
+  )
+}
+```
+
+### 3. 带表单提交的页面模板
+```typescript
+// mini/src/pages/[表单页面]/index.tsx
+import { View } from '@tarojs/components'
+import { useState } from 'react'
+import { useForm } from 'react-hook-form'
+import { zodResolver } from '@hookform/resolvers/zod'
+import { z } from 'zod'
+import { useMutation } from '@tanstack/react-query'
+import { userClient } from '@/api'
+import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage } from '@/components/ui/form'
+import { Input } from '@/components/ui/input'
+import { Button } from '@/components/ui/button'
+import Taro from '@tarojs/taro'
+
+const formSchema = z.object({
+  username: z.string().min(3, '用户名至少3个字符'),
+  email: z.string().email('请输入有效的邮箱地址'),
+  phone: z.string().regex(/^1[3-9]\d{9}$/, '请输入有效的手机号')
+})
+
+type FormData = z.infer<typeof formSchema>
+
+export default function CreateUserPage() {
+  const [loading, setLoading] = useState(false)
+  
+  const form = useForm<FormData>({
+    resolver: zodResolver(formSchema),
+    defaultValues: {
+      username: '',
+      email: '',
+      phone: ''
+    }
+  })
+
+  const mutation = useMutation({
+    mutationFn: async (data: FormData) => {
+      const response = await userClient.$post({ json: data })
+      if (response.status !== 201) {
+        throw new Error('创建用户失败')
+      }
+      return response.json()
+    },
+    onSuccess: () => {
+      Taro.showToast({
+        title: '创建成功',
+        icon: 'success'
+      })
+      Taro.navigateBack()
+    },
+    onError: (error) => {
+      Taro.showToast({
+        title: error.message || '创建失败',
+        icon: 'none'
+      })
+    }
+  })
+
+  const onSubmit = async (data: FormData) => {
+    setLoading(true)
+    try {
+      await mutation.mutateAsync(data)
+    } finally {
+      setLoading(false)
+    }
+  }
+
+  return (
+    <View className="min-h-screen bg-gray-50">
+      <Navbar title="创建用户" leftIcon="" />
+      <View className="px-4 py-4">
+        <Form {...form}>
+          <View className="space-y-4">
+            <FormField
+              control={form.control}
+              name="username"
+              render={({ field }) => (
+                <FormItem>
+                  <FormLabel>用户名</FormLabel>
+                  <FormControl>
+                    <Input placeholder="请输入用户名" {...field} />
+                  </FormControl>
+                  <FormMessage />
+                </FormItem>
+              )}
+            />
+            <Button
+              className="w-full"
+              onClick={form.handleSubmit(onSubmit)}
+              disabled={loading}
+            >
+              {loading ? '创建中...' : '创建用户'}
+            </Button>
+          </View>
+        </Form>
+      </View>
+    </View>
+  )
+}
+```
+
+## 认证功能使用
+
+### 1. useAuth Hook 使用规范
+```typescript
+import { useAuth } from '@/utils/auth'
+
+// 在页面或组件中使用
+const { 
+  user,           // 当前用户信息
+  login,          // 登录函数
+  logout,         // 登出函数
+  register,       // 注册函数
+  updateUser,     // 更新用户信息
+  isLoading,      // 加载状态
+  isLoggedIn      // 是否已登录
+} = useAuth()
+
+// 使用示例
+const handleLogin = async (formData) => {
+  try {
+    await login(formData)
+    Taro.switchTab({ url: '/pages/index/index' })
+  } catch (error) {
+    console.error('登录失败:', error)
+  }
+}
+```
+
+### 2. 页面权限控制
+```typescript
+// 在需要认证的页面顶部
+const { user, isLoading, isLoggedIn } = useAuth()
+
+useEffect(() => {
+  if (!isLoading && !isLoggedIn) {
+    Taro.navigateTo({ url: '/pages/login/index' })
+  }
+}, [isLoading, isLoggedIn])
+
+// 或者使用路由守卫模式
+```
+
+## RPC客户端调用规范
+
+### 1. 客户端导入
+```typescript
+// 从api.ts导入对应的客户端
+import { authClient, userClient, fileClient } from '@/api'
+```
+
+### 2. 类型提取
+```typescript
+import { InferResponseType, InferRequestType } from 'hono'
+
+// 响应类型提取
+type UserResponse = InferResponseType<typeof userClient.$get, 200>
+type UserDetailResponse = InferResponseType<typeof userClient[':id']['$get'], 200>
+
+// 请求类型提取
+type CreateUserRequest = InferRequestType<typeof userClient.$post>['json']
+type UpdateUserRequest = InferRequestType<typeof userClient[':id']['$put']>['json']
+```
+
+### 3. 调用示例
+```typescript
+// GET请求 - 列表
+const response = await userClient.$get({
+  query: {
+    page: 1,
+    pageSize: 10,
+    keyword: 'search'
+  }
+})
+
+// GET请求 - 单条
+const response = await userClient[':id'].$get({
+  param: { id: userId }
+})
+
+// POST请求
+const response = await userClient.$post({
+  json: {
+    username: 'newuser',
+    email: 'user@example.com'
+  }
+})
+
+// PUT请求
+const response = await userClient[':id'].$put({
+  param: { id: userId },
+  json: { username: 'updated' }
+})
+
+// DELETE请求
+const response = await userClient[':id'].$delete({
+  param: { id: userId }
+})
+```
+
+## React Query v5使用规范
+
+### 1. 查询配置
+```typescript
+const { data, isLoading, error, refetch } = useQuery({
+  queryKey: ['users', page, keyword], // 唯一的查询键
+  queryFn: async () => {
+    const response = await userClient.$get({
+      query: { page, pageSize: 10, keyword }
+    })
+    if (response.status !== 200) {
+      throw new Error('获取数据失败')
+    }
+    return response.json()
+  },
+  staleTime: 5 * 60 * 1000, // 5分钟
+  cacheTime: 10 * 60 * 1000, // 10分钟
+  retry: 3, // 重试3次
+  enabled: !!keyword, // 条件查询
+})
+```
+
+### 2. 变更操作
+```typescript
+const queryClient = useQueryClient()
+
+const mutation = useMutation({
+  mutationFn: async (data: CreateUserRequest) => {
+    const response = await userClient.$post({ json: data })
+    if (response.status !== 201) {
+      throw new Error('创建失败')
+    }
+    return response.json()
+  },
+  onSuccess: () => {
+    // 成功后刷新相关查询
+    queryClient.invalidateQueries({ queryKey: ['users'] })
+    Taro.showToast({ title: '创建成功', icon: 'success' })
+  },
+  onError: (error) => {
+    Taro.showToast({ 
+      title: error.message || '操作失败', 
+      icon: 'none' 
+    })
+  }
+})
+```
+
+### 3. 删除操作
+```typescript
+const queryClient = useQueryClient()
+
+const mutation = useMutation({
+  mutationFn: async (id: number) => {
+    const response = await deliveryAddressClient[':id'].$delete({
+      param: { id }
+    })
+    if (response.status !== 204) {
+      throw new Error('删除地址失败')
+    }
+    return response.json()
+  },
+  onSuccess: () => {
+    queryClient.invalidateQueries({ queryKey: ['delivery-addresses'] })
+    Taro.showToast({
+      title: '删除成功',
+      icon: 'success'
+    })
+  },
+  onError: (error) => {
+    Taro.showToast({
+      title: error.message || '删除失败',
+      icon: 'none'
+    })
+  }
+})
+```
+
+### 4. 分页查询
+#### 标准分页(useQuery)
+```typescript
+const useUserList = (page: number, pageSize: number = 10) => {
+  return useQuery({
+    queryKey: ['users', page, pageSize],
+    queryFn: async () => {
+      const response = await userClient.$get({
+        query: { page, pageSize }
+      })
+      return response.json()
+    },
+    keepPreviousData: true, // 保持上一页数据
+  })
+}
+```
+
+#### 移动端无限滚动分页(useInfiniteQuery)
+```typescript
+import { useInfiniteQuery } from '@tanstack/react-query'
+
+const useInfiniteUserList = (keyword?: string) => {
+  return useInfiniteQuery({
+    queryKey: ['users-infinite', keyword],
+    queryFn: async ({ pageParam = 1 }) => {
+      const response = await userClient.$get({
+        query: {
+          page: pageParam,
+          pageSize: 10,
+          keyword
+        }
+      })
+      if (response.status !== 200) {
+        throw new Error('获取用户列表失败')
+      }
+      return response.json()
+    },
+    getNextPageParam: (lastPage, allPages) => {
+      const totalPages = Math.ceil(lastPage.pagination.total / lastPage.pagination.pageSize)
+      const nextPage = allPages.length + 1
+      return nextPage <= totalPages ? nextPage : undefined
+    },
+    staleTime: 5 * 60 * 1000,
+  })
+}
+
+// 使用示例
+const {
+  data,
+  isLoading,
+  isFetchingNextPage,
+  fetchNextPage,
+  hasNextPage,
+  refetch
+} = useInfiniteUserList(searchKeyword)
+
+// 合并所有分页数据
+const allUsers = data?.pages.flatMap(page => page.data) || []
+
+// 触底加载更多处理
+const handleScrollToLower = () => {
+  if (hasNextPage && !isFetchingNextPage) {
+    fetchNextPage()
+  }
+}
+```
+
+#### 移动端分页页面模板
+```typescript
+// mini/src/pages/[无限滚动列表]/index.tsx
+import { View, ScrollView } from '@tarojs/components'
+import { useInfiniteQuery } from '@tanstack/react-query'
+import { goodsClient } from '@/api'
+import { InferResponseType } from 'hono'
+import Taro from '@tarojs/taro'
+
+type GoodsResponse = InferResponseType<typeof goodsClient.$get, 200>
+
+export default function InfiniteGoodsList() {
+  const [searchKeyword, setSearchKeyword] = useState('')
+
+  const {
+    data,
+    isLoading,
+    isFetchingNextPage,
+    fetchNextPage,
+    hasNextPage,
+    refetch
+  } = useInfiniteQuery({
+    queryKey: ['goods-infinite', searchKeyword],
+    queryFn: async ({ pageParam = 1 }) => {
+      const response = await goodsClient.$get({
+        query: {
+          page: pageParam,
+          pageSize: 10,
+          keyword: searchKeyword
+        }
+      })
+      if (response.status !== 200) {
+        throw new Error('获取商品失败')
+      }
+      return response.json()
+    },
+    getNextPageParam: (lastPage) => {
+      const { pagination } = lastPage
+      const totalPages = Math.ceil(pagination.total / pagination.pageSize)
+      return pagination.current < totalPages ? pagination.current + 1 : undefined
+    },
+    staleTime: 5 * 60 * 1000,
+    initialPageParam: 1,
+  })
+
+  // 合并所有分页数据
+  const allGoods = data?.pages.flatMap(page => page.data) || []
+
+  // 触底加载更多
+  const handleScrollToLower = () => {
+    if (hasNextPage && !isFetchingNextPage) {
+      fetchNextPage()
+    }
+  }
+
+  // 下拉刷新
+  const onPullDownRefresh = () => {
+    refetch().finally(() => {
+      Taro.stopPullDownRefresh()
+    })
+  }
+
+  return (
+    <ScrollView
+      className="h-screen"
+      scrollY
+      onScrollToLower={handleScrollToLower}
+      refresherEnabled
+      refresherTriggered={false}
+      onRefresherRefresh={onPullDownRefresh}
+    >
+      <View className="px-4 py-4">
+        {isLoading ? (
+          <View className="flex justify-center py-10">
+            <View className="i-heroicons-arrow-path-20-solid animate-spin w-8 h-8 text-blue-500" />
+          </View>
+        ) : (
+          <>
+            {allGoods.map((item) => (
+              <View key={item.id} className="bg-white rounded-lg p-4 mb-3">
+                <Text>{item.name}</Text>
+              </View>
+            ))}
+            
+            {isFetchingNextPage && (
+              <View className="flex justify-center py-4">
+                <View className="i-heroicons-arrow-path-20-solid animate-spin w-6 h-6 text-blue-500" />
+                <Text className="ml-2 text-sm text-gray-500">加载更多...</Text>
+              </View>
+            )}
+            
+            {!hasNextPage && allGoods.length > 0 && (
+              <View className="text-center py-4 text-sm text-gray-400">
+                没有更多了
+              </View>
+            )}
+          </>
+        )}
+      </View>
+    </ScrollView>
+  )
+}
+```
+
+## 表单处理规范
+
+### 1. 表单Schema定义
+```typescript
+// 在schemas目录下定义
+import { z } from 'zod'
+
+export const userSchema = z.object({
+  username: z.string()
+    .min(3, '用户名至少3个字符')
+    .max(20, '用户名最多20个字符')
+    .regex(/^\S+$/, '用户名不能包含空格'),
+  email: z.string().email('请输入有效的邮箱地址'),
+  phone: z.string().regex(/^1[3-9]\d{9}$/, '请输入有效的手机号')
+})
+
+export type UserFormData = z.infer<typeof userSchema>
+```
+
+### 2. 表单使用
+```typescript
+import { useForm } from 'react-hook-form'
+import { zodResolver } from '@hookform/resolvers/zod'
+import { userSchema, type UserFormData } from '@/schemas/user.schema'
+
+const form = useForm<UserFormData>({
+  resolver: zodResolver(userSchema),
+  defaultValues: {
+    username: '',
+    email: '',
+    phone: ''
+  }
+})
+
+// 表单提交
+const onSubmit = async (data: UserFormData) => {
+  try {
+    await mutation.mutateAsync(data)
+  } catch (error) {
+    console.error('表单提交失败:', error)
+  }
+}
+```
+
+## 错误处理规范
+
+### 1. 统一的错误处理
+```typescript
+const handleApiError = (error: any) => {
+  const message = error.response?.data?.message || error.message || '操作失败'
+  
+  if (error.response?.status === 401) {
+    Taro.showModal({
+      title: '未登录',
+      content: '请先登录',
+      success: () => {
+        Taro.navigateTo({ url: '/pages/login/index' })
+      }
+    })
+  } else if (error.response?.status === 403) {
+    Taro.showToast({ title: '权限不足', icon: 'none' })
+  } else if (error.response?.status === 404) {
+    Taro.showToast({ title: '资源不存在', icon: 'none' })
+  } else if (error.response?.status >= 500) {
+    Taro.showToast({ title: '服务器错误,请稍后重试', icon: 'none' })
+  } else {
+    Taro.showToast({ title: message, icon: 'none' })
+  }
+}
+```
+
+### 2. 页面级错误处理
+```typescript
+const { data, isLoading, error } = useQuery({
+  // ...查询配置
+})
+
+if (error) {
+  return (
+    <View className="flex-1 flex items-center justify-center">
+      <View className="text-center">
+        <View className="i-heroicons-exclamation-triangle-20-solid w-12 h-12 text-red-500 mx-auto mb-4" />
+        <Text className="text-gray-600 mb-4">{error.message}</Text>
+        <Button onClick={() => refetch()}>重新加载</Button>
+      </View>
+    </View>
+  )
+}
+```
+
+## 页面模板示例
+
+### 1. TabBar页面标准结构
+```typescript
+// 示例:首页
+import React from 'react'
+import { View, Text } from '@tarojs/components'
+import { TabBarLayout } from '@/layouts/tab-bar-layout'
+import { Navbar } from '@/components/ui/navbar'
+import { Button } from '@/components/ui/button'
+import './index.css'
+
+const HomePage: React.FC = () => {
+  return (
+    <TabBarLayout activeKey="home">
+      <Navbar
+        title="首页"
+        rightIcon="i-heroicons-bell-20-solid"
+        onClickRight={() => console.log('点击通知')}
+        leftIcon=""
+      />
+      <View className="px-4 py-4">
+        <Text className="text-2xl font-bold text-gray-900">欢迎使用</Text>
+        <View className="mt-4">
+          <Text className="text-gray-600">这是一个简洁优雅的小程序首页</Text>
+        </View>
+      </View>
+    </TabBarLayout>
+  )
+}
+
+export default HomePage
+```
+
+### 2. 非TabBar页面标准结构
+```typescript
+// 示例:登录页
+import { View } from '@tarojs/components'
+import { useEffect } from 'react'
+import Taro from '@tarojs/taro'
+import { Navbar } from '@/components/ui/navbar'
+import { Button } from '@/components/ui/button'
+import './index.css'
+
+export default function Login() {
+  useEffect(() => {
+    Taro.setNavigationBarTitle({
+      title: '用户登录'
+    })
+  }, [])
+
+  return (
+    <View className="min-h-screen bg-gradient-to-br from-blue-50 via-white to-indigo-50">
+      <Navbar
+        title="用户登录"
+        backgroundColor="bg-transparent"
+        textColor="text-gray-900"
+        border={false}
+      />
+      <View className="flex-1 px-6 py-12">
+        {/* Logo区域 */}
+        <View className="flex flex-col items-center mb-10">
+          <View className="w-20 h-20 mb-4 rounded-full bg-white shadow-lg flex items-center justify-center">
+            <View className="i-heroicons-user-circle-20-solid w-12 h-12 text-blue-500" />
+          </View>
+          <Text className="text-2xl font-bold text-gray-900 mb-1">欢迎回来</Text>
+        </View>
+
+        {/* 表单区域 */}
+        <View className="bg-white rounded-2xl shadow-sm p-6">
+          <View className="space-y-5">
+            {/* 表单内容 */}
+          </View>
+        </View>
+      </View>
+    </View>
+  )
+}
+```
+
+
+## 路由配置
+
+### 1. TabBar页面配置
+```typescript
+// mini/src/app.config.ts
+export default defineAppConfig({
+  pages: [
+    'pages/index/index',
+    'pages/explore/index',
+    'pages/profile/index',
+    // 其他页面
+  ],
+  tabBar: {
+    color: '#666666',
+    selectedColor: '#1976D2',
+    backgroundColor: '#ffffff',
+    borderStyle: 'black',
+    list: [
+      {
+        pagePath: 'pages/index/index',
+        text: '首页',
+        iconPath: 'assets/icons/home.png',
+        selectedIconPath: 'assets/icons/home-active.png'
+      },
+      {
+        pagePath: 'pages/explore/index',
+        text: '发现',
+        iconPath: 'assets/icons/explore.png',
+        selectedIconPath: 'assets/icons/explore-active.png'
+      },
+      {
+        pagePath: 'pages/profile/index',
+        text: '我的',
+        iconPath: 'assets/icons/profile.png',
+        selectedIconPath: 'assets/icons/profile-active.png'
+      }
+    ]
+  }
+})
+```
+
+### 2. 非TabBar页面路由
+非TabBar页面会自动添加到pages数组中,无需额外配置tabBar。
+
+## 最佳实践
+
+### 1. 命名规范
+- 页面目录:使用小写+中划线命名,如 `user-profile`
+- 组件名称:使用PascalCase,如 `UserProfilePage`
+- 文件名:使用小写+中划线命名,如 `user-profile.tsx`
+
+### 2. 样式规范
+- 使用Tailwind CSS原子类
+- 避免使用px,使用rpx单位
+- 页面背景色统一使用 `bg-gray-50` 或 `bg-white`
+
+### 3. 状态管理
+- 使用React hooks进行状态管理
+- 复杂状态使用Context API
+- 用户信息使用 `useAuth` hook
+
+### 4. 错误处理
+- 使用Taro.showToast显示错误信息
+- 网络请求使用try-catch包裹
+- 提供友好的用户反馈
+
+### 5. 性能优化
+- 使用懒加载组件
+- 避免不必要的重新渲染
+- 合理使用useMemo和useCallback
+
+## 常用工具函数
+
+### 1. 页面跳转
+```typescript
+// Tab页面跳转
+Taro.switchTab({ url: '/pages/index/index' })
+
+// 普通页面跳转
+Taro.navigateTo({ url: '/pages/login/index' })
+
+// 返回上一页
+Taro.navigateBack()
+
+// 重定向(清除当前页面历史)
+Taro.redirectTo({ url: '/pages/login/index' })
+
+// 重新启动应用
+Taro.reLaunch({ url: '/pages/index/index' })
+```
+
+### 2. 用户交互
+```typescript
+// 显示提示
+Taro.showToast({
+  title: '操作成功',
+  icon: 'success',
+  duration: 2000
+})
+
+// 显示加载
+Taro.showLoading({
+  title: '加载中...',
+  mask: true
+})
+Taro.hideLoading()
+
+// 显示确认对话框
+Taro.showModal({
+  title: '确认操作',
+  content: '确定要执行此操作吗?',
+  success: (res) => {
+    if (res.confirm) {
+      // 用户点击确认
+    }
+  }
+})
+
+// 显示操作菜单
+Taro.showActionSheet({
+  itemList: ['选项1', '选项2', '选项3'],
+  success: (res) => {
+    console.log('用户选择了', res.tapIndex)
+  }
+})
+```
+
+### 3. 本地存储
+```typescript
+// 存储数据
+Taro.setStorageSync('key', 'value')
+Taro.setStorageSync('user', JSON.stringify(user))
+
+// 获取数据
+const value = Taro.getStorageSync('key')
+const user = JSON.parse(Taro.getStorageSync('user') || '{}')
+
+// 移除数据
+Taro.removeStorageSync('key')
+
+// 清空所有数据
+Taro.clearStorageSync()
+```
+
+### 4. 设备信息
+```typescript
+// 获取系统信息
+const systemInfo = Taro.getSystemInfoSync()
+const { screenWidth, screenHeight, windowWidth, windowHeight, statusBarHeight } = systemInfo
+
+// 获取用户位置
+Taro.getLocation({
+  type: 'wgs84',
+  success: (res) => {
+    console.log('纬度:', res.latitude)
+    console.log('经度:', res.longitude)
+  }
+})

+ 7 - 0
.claude/commands/mini-tabbar-布局组件使用.md

@@ -0,0 +1,7 @@
+---
+description: "tabbar布局组件使用指令"
+---
+
+一级页面需要 使用 import { TabBarLayout } from '@/layouts/tab-bar-layout'
+
+使用前先查看 mini/src/layouts/tab-bar-layout.tsx 了解具体用法

+ 6 - 0
.claude/commands/mini-tabbar-页面加入标签栏.md

@@ -0,0 +1,6 @@
+---
+description: "将页面作为 小程序 tabbar指令"
+---
+
+检查 mini/src/app.config.ts, mini/src/layouts/tab-bar-layout.tsx
+将页面作为 小程序 tabbar

+ 490 - 0
.claude/commands/mini-ui-小程序UI开发.md

@@ -0,0 +1,490 @@
+---
+description: "小程序ui开发指令"
+---
+
+按小程序ui规范,小程序表单开发规范(.roo/commands/mini-form.md)
+
+# 小程序UI开发规范 (Tailwind CSS v4)
+
+## 概述
+
+本规范定义了基于Taro框架的小程序UI开发标准,采用Tailwind CSS v4原子化样式和Heroicons图标库,遵循shadcn/ui组件设计模式。
+
+## 技术栈
+
+- **Taro 4** - 跨端小程序框架
+- **React 18** - 前端框架
+- **Tailwind CSS v4** - 原子化CSS框架
+- **@egoist/tailwindcss-icons** - 图标库集成
+- **@weapp-tailwindcss/merge** - Tailwind类名合并工具(小程序版tailwind-merge)
+- **clsx** - 条件样式类名管理
+
+## 目录结构
+
+```
+mini/
+├── src/
+│   ├── components/
+│   │   └── ui/           # UI组件库
+│   │       ├── button.tsx
+│   │       ├── input.tsx
+│   │       ├── card.tsx
+│   │       └── ...
+│   ├── pages/
+│   ├── utils/
+│   └── app.css           # Tailwind样式入口
+├── tailwind.config.js    # Tailwind配置
+└── postcss.config.js     # PostCSS配置
+```
+
+## 样式规范
+
+### 1. Tailwind CSS v4 使用规范
+
+#### 1.1 基础类名使用
+```typescript
+// ✅ 正确使用原子类
+<View className="flex items-center justify-between p-4 bg-white rounded-lg shadow-sm">
+  <Text className="text-lg font-semibold text-gray-900">标题</Text>
+</View>
+
+// ❌ 避免使用内联样式
+<View style={{ display: 'flex', alignItems: 'center', padding: 16 }}>
+  <Text style={{ fontSize: 18, fontWeight: '600' }}>标题</Text>
+</View>
+```
+
+#### 1.2 类名合并规范
+```typescript
+// ✅ 使用twMerge处理动态类名冲突
+import { twMerge } from '@weapp-tailwindcss/merge'
+
+// 处理静态和动态类名的冲突
+<View className={twMerge('px-4 py-2', isActive ? 'bg-blue-500' : 'bg-gray-200')}>
+  <Text>按钮</Text>
+</View>
+
+// 处理多个条件类名的合并
+<View className={twMerge(
+  'flex items-center',
+  isActive && 'bg-blue-500 text-white',
+  isDisabled && 'opacity-50 cursor-not-allowed',
+  customClassName
+)}>
+  <Text>复杂组件</Text>
+</View>
+
+// ❌ 避免手动拼接类名导致冲突
+<View className={`px-4 py-2 ${isActive ? 'bg-blue-500' : 'bg-gray-200'} ${customClassName}`}>
+  <Text>按钮</Text>
+</View>
+```
+
+#### 1.2 响应式设计
+```typescript
+// 使用Tailwind的响应式前缀
+<View className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
+  <View className="w-full sm:w-1/2 md:w-1/3" />
+</View>
+```
+
+#### 1.3 状态样式
+```typescript
+// 悬停和焦点状态
+<Button className="bg-blue-500 hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2">
+  点击按钮
+</Button>
+
+// 禁用状态
+<Button className="disabled:opacity-50 disabled:cursor-not-allowed">
+  禁用按钮
+</Button>
+```
+
+### 2. 图标使用规范
+
+#### 2.1 图标
+使用`@egoist/tailwindcss-icons`提供的图标类名:
+"mdi", "lucide", "heroicons", "heroicons-outline", "heroicons-solid"
+
+```typescript
+// 基础用法
+<View className="i-heroicons-user-20-solid text-gray-600" />
+<Button className="flex items-center gap-2">
+  <View className="i-heroicons-plus-20-solid" />
+  <Text>添加</Text>
+</Button>
+
+// 图标大小和颜色
+<View className="i-heroicons-home-16-solid w-6 h-6 text-blue-500" />
+<View className="i-heroicons-cog-8-tooth-20-solid w-8 h-8 text-gray-400" />
+
+// 图标变体
+// solid - 实心图标
+// outline - 轮廓图标
+// mini - 迷你图标 (20x20)
+// micro - 微型图标 (16x16)
+<View className="i-heroicons-heart-20-solid text-red-500" />
+<View className="i-heroicons-heart-20-outline text-red-500" />
+```
+
+#### 2.2 图标命名规则
+```
+i-heroicons-[图标名]-[大小]-[变体]
+```
+- 大小: 16 | 20 | 24
+- 变体: solid | outline
+
+### 3. UI组件规范
+
+#### 3.1 组件文件结构
+每个UI组件应包含:
+```typescript
+// mini/src/components/ui/button.tsx
+import { Button as TaroButton, ButtonProps } from '@tarojs/components'
+import { cn } from '@/utils/cn'
+import { cva, type VariantProps } from 'class-variance-authority'
+
+const buttonVariants = cva(
+  'inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background',
+  {
+    variants: {
+      variant: {
+        default: 'bg-primary text-primary-foreground hover:bg-primary/90',
+        destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
+        outline: 'border border-input hover:bg-accent hover:text-accent-foreground',
+        secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
+        ghost: 'hover:bg-accent hover:text-accent-foreground',
+        link: 'underline-offset-4 hover:underline text-primary',
+      },
+      size: {
+        default: 'h-10 py-2 px-4',
+        sm: 'h-9 px-3 rounded-md',
+        lg: 'h-11 px-8 rounded-md',
+        icon: 'h-10 w-10',
+      },
+    },
+    defaultVariants: {
+      variant: 'default',
+      size: 'default',
+    },
+  }
+)
+
+interface ButtonProps extends ButtonProps, VariantProps<typeof buttonVariants> {}
+
+export function Button({ className, variant, size, ...props }: ButtonProps) {
+  return (
+    <TaroButton
+      className={cn(buttonVariants({ variant, size, className }))}
+      {...props}
+    />
+  )
+}
+```
+
+#### 3.2 常用组件示例
+
+**按钮组件 (Button)**
+```typescript
+// 使用示例
+<Button variant="primary" size="lg" onClick={handleClick}>
+  <View className="i-heroicons-plus-20-solid mr-2" />
+  创建用户
+</Button>
+
+<Button variant="outline" size="sm" disabled={loading}>
+  {loading && <View className="i-heroicons-arrow-path-20-solid animate-spin mr-2" />}
+  加载中...
+</Button>
+```
+
+**卡片组件 (Card)**
+```typescript
+<Card className="p-6 bg-white rounded-lg shadow-sm">
+  <CardHeader>
+    <View className="flex items-center justify-between">
+      <Text className="text-lg font-semibold">用户信息</Text>
+      <View className="i-heroicons-user-circle-20-solid text-gray-400" />
+    </View>
+  </CardHeader>
+  <CardContent>
+    <Text className="text-gray-600">用户详情内容</Text>
+  </CardContent>
+</Card>
+```
+
+**输入框组件 (Input)**
+```typescript
+<View className="space-y-2">
+  <Label htmlFor="username">用户名</Label>
+  <View className="relative">
+    <View className="absolute left-3 top-1/2 -translate-y-1/2">
+      <View className="i-heroicons-user-20-solid text-gray-400 w-5 h-5" />
+    </View>
+    <Input
+      id="username"
+      className="pl-10"
+      placeholder="请输入用户名"
+      value={username}
+      onInput={handleInput}
+    />
+  </View>
+</View>
+```
+
+### 4. 页面布局规范
+
+#### 4.1 页面容器
+```typescript
+// 主容器
+<View className="min-h-screen bg-gray-50">
+  <View className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
+    {/* 页面内容 */}
+  </View>
+</View>
+
+// 卡片布局
+<View className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
+  {/* 卡片内容 */}
+</View>
+```
+
+#### 4.2 响应式断点
+- `sm`: 640px
+- `md`: 768px  
+- `lg`: 1024px
+- `xl`: 1280px
+- `2xl`: 1536px
+
+### 5. 主题配置
+
+#### 5.1 颜色系统
+```css
+/* 在 app.css 中定义 */
+:root {
+  --primary: 59 130 246;
+  --primary-foreground: 255 255 255;
+  --secondary: 107 114 128;
+  --secondary-foreground: 255 255 255;
+  --accent: 243 244 246;
+  --accent-foreground: 17 24 39;
+  --destructive: 239 68 68;
+  --destructive-foreground: 255 255 255;
+  --muted: 249 250 251;
+  --muted-foreground: 107 114 128;
+  --border: 229 231 235;
+  --input: 255 255 255;
+  --ring: 59 130 246;
+  --background: 255 255 255;
+  --foreground: 17 24 39;
+}
+```
+
+#### 5.2 Tailwind配置
+```javascript
+// tailwind.config.js
+module.exports = {
+  content: [
+    './src/**/*.{js,ts,jsx,tsx}',
+  ],
+  theme: {
+    extend: {
+      colors: {
+        primary: 'rgb(var(--primary))',
+        'primary-foreground': 'rgb(var(--primary-foreground))',
+        secondary: 'rgb(var(--secondary))',
+        'secondary-foreground': 'rgb(var(--secondary-foreground))',
+        accent: 'rgb(var(--accent))',
+        'accent-foreground': 'rgb(var(--accent-foreground))',
+        destructive: 'rgb(var(--destructive))',
+        'destructive-foreground': 'rgb(var(--destructive-foreground))',
+        muted: 'rgb(var(--muted))',
+        'muted-foreground': 'rgb(var(--muted-foreground))',
+        border: 'rgb(var(--border))',
+        input: 'rgb(var(--input))',
+        ring: 'rgb(var(--ring))',
+        background: 'rgb(var(--background))',
+        foreground: 'rgb(var(--foreground))',
+      },
+    },
+  },
+  plugins: [
+    require('@egoist/tailwindcss-icons')({
+      // 配置Heroicons
+      collections: {
+        heroicons: {
+          solid: true,
+          outline: true,
+          mini: true,
+        },
+      },
+    }),
+  ],
+}
+```
+
+### 6. 工具函数
+
+#### 6.1 类名合并工具
+```typescript
+// mini/src/utils/cn.ts
+import { clsx, type ClassValue } from 'clsx'
+import { twMerge } from '@weapp-tailwindcss/merge'
+
+export function cn(...inputs: ClassValue[]) {
+  return twMerge(clsx(inputs))
+}
+```
+
+#### 6.2 小程序专用类名处理
+```typescript
+// 小程序环境下的类名合并
+import { twMerge } from '@weapp-tailwindcss/merge'
+
+// 标准用法(自动处理小程序转义)
+const classes = twMerge('px-2 py-1 bg-red hover:bg-dark-red', 'p-3 bg-[#B91C1C]')
+// → 'hovercbg-dark-red p-3 bg-_hB91C1C_'
+
+// 手动指定版本(如果需要)
+import { twMerge as twMergeV4 } from '@weapp-tailwindcss/merge/v4'
+import { twMerge as twMergeV3 } from '@weapp-tailwindcss/merge/v3'
+
+// 使用cva进行组件变体管理
+import { cva } from 'class-variance-authority'
+
+const buttonVariants = cva(
+  'inline-flex items-center justify-center rounded-md text-sm font-medium',
+  {
+    variants: {
+      variant: {
+        default: 'bg-blue-500 text-white hover:bg-blue-600',
+        destructive: 'bg-red-500 text-white hover:bg-red-600',
+      },
+      size: {
+        sm: 'h-8 px-3 text-xs',
+        lg: 'h-12 px-6 text-base',
+      },
+    },
+  }
+)
+```
+
+### 7. 最佳实践
+
+#### 7.1 状态管理
+```typescript
+// 使用React Hook进行状态管理
+const [loading, setLoading] = useState(false)
+const [data, setData] = useState<User[]>([])
+
+// 加载状态显示
+{loading ? (
+  <View className="flex justify-center py-8">
+    <View className="i-heroicons-arrow-path-20-solid animate-spin w-8 h-8 text-blue-500" />
+  </View>
+) : (
+  <View className="grid grid-cols-1 gap-4">
+    {data.map(item => <Card key={item.id} {...item} />)}
+  </View>
+)}
+```
+
+#### 7.2 错误处理
+```typescript
+// 错误状态展示
+<View className="text-center py-8">
+  <View className="i-heroicons-exclamation-triangle-20-solid w-12 h-12 text-red-500 mx-auto mb-4" />
+  <Text className="text-gray-600">加载失败,请稍后重试</Text>
+  <Button variant="outline" size="sm" onClick={retry} className="mt-4">
+    重新加载
+  </Button>
+</View>
+```
+
+### 8. 性能优化
+
+#### 8.1 样式优化
+- 使用Tailwind的JIT模式,只生成用到的类名
+- 避免内联样式,全部使用类名
+- 合理使用`@apply`提取重复样式
+
+#### 8.2 图标优化
+- 使用CSS图标而非图片图标
+- 图标按需加载,不使用的图标不会被打包
+- 合理使用图标大小,避免过大图标
+
+### 9. 调试工具
+
+#### 9.1 开发调试
+```typescript
+// 添加调试样式类
+<View className="border border-red-500 debug">
+  <Text>调试内容</Text>
+</View>
+
+// 使用Tailwind的调试工具
+// 在开发环境中添加
+// <View className="outline outline-1 outline-red-500" />
+```
+
+### 10. tailwind-merge使用规范
+
+#### 10.1 基本用法
+```typescript
+// 单类名合并
+const result = twMerge('px-2 py-1 bg-red hover:bg-dark-red', 'p-3 bg-[#B91C1C]')
+// → 'hovercbg-dark-red p-3 bg-_hB91C1C_'
+
+// 处理冲突类名
+twMerge('px-4', 'px-2') // → 'px-2'
+twMerge('text-red-500', 'text-blue-500') // → 'text-blue-500'
+```
+
+#### 10.2 条件类名处理
+```typescript
+// 使用cn工具函数处理条件类名
+import { cn } from '@/utils/cn'
+
+const Button = ({ variant, size, disabled, className }) => {
+  return (
+    <Button
+      className={cn(
+        'inline-flex items-center justify-center rounded-md',
+        variant === 'primary' && 'bg-blue-500 text-white',
+        variant === 'secondary' && 'bg-gray-200 text-gray-800',
+        size === 'sm' && 'px-3 py-1 text-sm',
+        size === 'lg' && 'px-6 py-3 text-lg',
+        disabled && 'opacity-50 cursor-not-allowed',
+        className // 允许外部覆盖
+      )}
+    >
+      按钮
+    </Button>
+  )
+}
+```
+
+#### 10.3 小程序特殊处理
+```typescript
+// 跨端使用
+import { create } from '@weapp-tailwindcss/merge'
+
+const { twMerge } = create({
+  // 在当前环境为小程序时启用转义
+  disableEscape: true
+})
+
+// 版本选择
+import { twMerge as twMergeV4 } from '@weapp-tailwindcss/merge/v4' // Tailwind v4
+import { twMerge as twMergeV3 } from '@weapp-tailwindcss/merge/v3' // Tailwind v3
+```
+
+## 注意事项
+
+1. **兼容性**:确保所有类名在小程序环境中有效
+2. **性能**:避免过度嵌套和复杂选择器
+3. **可维护性**:保持组件结构清晰,样式统一
+4. **可读性**:合理使用空格和换行,提高代码可读性
+5. **tailwind-merge**:始终使用twMerge或cn工具函数处理动态类名,避免类名冲突
+6. **版本兼容**:根据Tailwind CSS版本选择正确的tailwind-merge版本

+ 282 - 0
.claude/commands/mini-ui-轮播图组件使用.md

@@ -0,0 +1,282 @@
+---
+description: "小程序轮播图组件使用指令"
+---
+
+```tsx
+import { View } from '@tarojs/components'
+import { Carousel, CarouselItem } from './carousel'
+import { useState } from 'react'
+import Taro from '@tarojs/taro'
+
+// 示例1:基础用法
+export function BasicCarouselExample() {
+  const items: CarouselItem[] = [
+    {
+      src: 'https://picsum.photos/400/200?random=1',
+      title: '第一张轮播图',
+      description: '这是第一张轮播图的描述文字'
+    },
+    {
+      src: 'https://picsum.photos/400/200?random=2',
+      title: '第二张轮播图',
+      description: '这是第二张轮播图的描述文字'
+    },
+    {
+      src: 'https://picsum.photos/400/200?random=3',
+      title: '第三张轮播图',
+      description: '这是第三张轮播图的描述文字'
+    }
+  ]
+
+  return (
+    <View className="p-4">
+      <View className="text-lg font-semibold mb-4">基础轮播图</View>
+      <Carousel items={items} />
+    </View>
+  )
+}
+
+// 示例2:自定义配置
+export function CustomCarouselExample() {
+  const items: CarouselItem[] = [
+    {
+      src: 'https://picsum.photos/400/300?random=4',
+      title: '精选商品',
+      description: '限时优惠,立即抢购'
+    },
+    {
+      src: 'https://picsum.photos/400/300?random=5',
+      title: '新品上市',
+      description: '最新款式,引领潮流'
+    },
+    {
+      src: 'https://picsum.photos/400/300?random=6',
+      title: '特价促销',
+      description: '全场5折起,不容错过'
+    }
+  ]
+
+  return (
+    <View className="p-4">
+      <View className="text-lg font-semibold mb-4">自定义配置轮播图</View>
+      <Carousel
+        items={items}
+        height={300}
+        interval={4000}
+        indicatorPosition="bottom"
+        rounded="lg"
+        onItemClick={(item, index) => {
+          console.log('点击了第', index + 1, '张轮播图:', item.title)
+        }}
+        onChange={(current) => {
+          console.log('切换到第', current + 1, '张')
+        }}
+      />
+    </View>
+  )
+}
+
+// 示例3:无指示器轮播
+export function NoIndicatorsCarouselExample() {
+  const items: CarouselItem[] = [
+    {
+      src: 'https://picsum.photos/400/150?random=7',
+      link: '/pages/product/1'
+    },
+    {
+      src: 'https://picsum.photos/400/150?random=8',
+      link: '/pages/product/2'
+    },
+    {
+      src: 'https://picsum.photos/400/150?random=9',
+      link: '/pages/product/3'
+    }
+  ]
+
+  return (
+    <View className="p-4">
+      <View className="text-lg font-semibold mb-4">无指示器轮播图</View>
+      <Carousel
+        items={items}
+        showIndicators={false}
+        height={150}
+        rounded="md"
+      />
+    </View>
+  )
+}
+
+// 示例4:圆形指示器轮播
+export function CircularIndicatorsCarouselExample() {
+  const items: CarouselItem[] = [
+    {
+      src: 'https://picsum.photos/400/250?random=10',
+      title: '活动预告'
+    },
+    {
+      src: 'https://picsum.photos/400/250?random=11',
+      title: '会员专享'
+    },
+    {
+      src: 'https://picsum.photos/400/250?random=12',
+      title: '积分兑换'
+    },
+    {
+      src: 'https://picsum.photos/400/250?random=13',
+      title: '限时秒杀'
+    }
+  ]
+
+  return (
+    <View className="p-4">
+      <View className="text-lg font-semibold mb-4">圆形指示器轮播图</View>
+      <Carousel
+        items={items}
+        autoplay={true}
+        interval={2000}
+        indicatorPosition="bottom"
+        rounded="xl"
+        height={250}
+      />
+    </View>
+  )
+}
+
+// 示例5:顶部指示器轮播
+export function TopIndicatorsCarouselExample() {
+  const items: CarouselItem[] = [
+    {
+      src: 'https://picsum.photos/400/180?random=14',
+      title: '顶部指示器',
+      description: '指示器位于顶部'
+    },
+    {
+      src: 'https://picsum.photos/400/180?random=15',
+      title: '美观设计',
+      description: '简洁优雅的界面'
+    },
+    {
+      src: 'https://picsum.photos/400/180?random=16',
+      title: '用户体验',
+      description: '流畅的交互体验'
+    }
+  ]
+
+  return (
+    <View className="p-4">
+      <View className="text-lg font-semibold mb-4">顶部指示器轮播图</View>
+      <Carousel
+        items={items}
+        indicatorPosition="top"
+        height={180}
+        rounded="lg"
+      />
+    </View>
+  )
+}
+
+// 综合示例:轮播图页面
+export function CarouselDemoPage() {
+  const [currentExample, setCurrentExample] = useState(0)
+
+  const examples = [
+    { title: '基础用法', component: BasicCarouselExample },
+    { title: '自定义配置', component: CustomCarouselExample },
+    { title: '无指示器', component: NoIndicatorsCarouselExample },
+    { title: '圆形指示器', component: CircularIndicatorsCarouselExample },
+    { title: '顶部指示器', component: TopIndicatorsCarouselExample }
+  ]
+
+  const CurrentComponent = examples[currentExample].component
+
+  return (
+    <View className="min-h-screen bg-gray-50">
+      <View className="p-4 bg-white">
+        <View className="text-xl font-bold text-center mb-4">
+          轮播图组件示例
+        </View>
+        
+        {/* 切换示例 */}
+        <View className="flex justify-center gap-2 mb-4">
+          {examples.map((example, index) => (
+            <View
+              key={index}
+              className={`px-3 py-1 text-sm rounded-full cursor-pointer ${
+                currentExample === index
+                  ? 'bg-blue-500 text-white'
+                  : 'bg-gray-200 text-gray-700'
+              }`}
+              onClick={() => setCurrentExample(index)}
+            >
+              {example.title}
+            </View>
+          ))}
+        </View>
+      </View>
+
+      <View className="p-4">
+        <CurrentComponent />
+      </View>
+
+      {/* 使用说明 */}
+      <View className="p-4 bg-white mt-4">
+        <View className="text-lg font-semibold mb-2">使用说明</View>
+        <View className="text-sm text-gray-600 space-y-1">
+          <View>• 支持自动播放和手动切换</View>
+          <View>• 可自定义指示器位置和样式</View>
+          <View>• 支持点击事件和切换回调</View>
+          <View>• 内置图片懒加载和错误处理</View>
+          <View>• 响应式设计,适配不同屏幕尺寸</View>
+        </View>
+      </View>
+    </View>
+  )
+}
+
+// 实际页面使用示例
+export function HomeCarousel() {
+  const bannerItems: CarouselItem[] = [
+    {
+      src: 'https://via.placeholder.com/750x400/3B82F6/FFFFFF?text=Banner+1',
+      title: '新品上市',
+      description: '最新款式,限时优惠',
+      link: '/pages/goods/new-arrival'
+    },
+    {
+      src: 'https://via.placeholder.com/750x400/EF4444/FFFFFF?text=Banner+2',
+      title: '限时秒杀',
+      description: '每日特价,不容错过',
+      link: '/pages/goods/flash-sale'
+    },
+    {
+      src: 'https://via.placeholder.com/750x400/10B981/FFFFFF?text=Banner+3',
+      title: '会员专享',
+      description: '会员专享折扣和福利',
+      link: '/pages/member/benefits'
+    }
+  ]
+
+  const handleBannerClick = (item: CarouselItem, index: number) => {
+    if (item.link) {
+      // 使用Taro跳转
+      Taro.navigateTo({
+        url: item.link
+      })
+    }
+  }
+
+  return (
+    <View className="w-full">
+      <Carousel
+        items={bannerItems}
+        height={400}
+        autoplay={true}
+        interval={4000}
+        circular={true}
+        rounded="none"
+        onItemClick={handleBannerClick}
+      />
+    </View>
+  )
+}
+```

+ 1193 - 0
.claude/commands/mobile-h5-shadui-页面开发.md

@@ -0,0 +1,1193 @@
+---
+description: "小程序shadui页面的开发指令"
+---
+
+# 小程序页面开发指令
+
+## 概述
+本指令规范了基于Taro + React + Shadui + Tailwind CSS的小程序页面开发流程,包含tabbar页和非tabbar页的创建标准和最佳实践,涵盖了认证、RPC调用、React Query v5使用等核心功能。
+
+## 小程序Shadui路径
+mini/src/components/ui
+
+## 当前可用的Shadui组件
+基于项目实际文件,当前小程序可用的shadui组件如下:
+
+### 基础组件
+- **Button** - 按钮组件 (`button.tsx`)
+- **Card** - 卡片组件 (`card.tsx`)
+- **Input** - 输入框组件 (`input.tsx`)
+- **Label** - 标签组件 (`label.tsx`)
+- **Form** - 表单组件 (`form.tsx`)
+
+### 交互组件
+- **AvatarUpload** - 头像上传组件 (`avatar-upload.tsx`)
+- **Carousel** - 轮播图组件 (`carousel.tsx`)
+- **Image** - 图片组件 (`image.tsx`)
+
+### 导航组件
+- **Navbar** - 顶部导航栏组件 (`navbar.tsx`)
+- **TabBar** - 底部标签栏组件 (`tab-bar.tsx`)
+
+### 布局组件
+- **TabBarLayout**: 用于tabbar页面,包含底部导航
+
+- 根据需求可扩展更多业务组件
+
+## 组件使用示例
+
+### Button 组件
+```typescript
+import { Button } from '@/components/ui/button'
+
+// 基础用法
+<Button onClick={handleClick}>主要按钮</Button>
+
+// 不同尺寸
+<Button size="sm">小按钮</Button>
+<Button size="md">中按钮</Button>
+<Button size="lg">大按钮</Button>
+
+// 不同样式
+<Button variant="primary">主要按钮</Button>
+<Button variant="secondary">次要按钮</Button>
+<Button variant="outline">边框按钮</Button>
+<Button variant="ghost">幽灵按钮</Button>
+```
+
+### Input 组件
+```typescript
+import { Input } from '@/components/ui/input'
+
+// 基础用法
+<Input placeholder="请输入内容" />
+
+// 受控组件
+<Input value={value} onChange={handleChange} />
+
+// 不同类型
+<Input type="text" placeholder="文本输入" />
+<Input type="number" placeholder="数字输入" />
+<Input type="password" placeholder="密码输入" />
+```
+
+### Form 组件
+```typescript
+import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage } from '@/components/ui/form'
+import { useForm } from 'react-hook-form'
+import { zodResolver } from '@hookform/resolvers/zod'
+import { z } from 'zod'
+
+const formSchema = z.object({
+  username: z.string().min(2, '用户名至少2个字符'),
+  email: z.string().email('请输入有效的邮箱地址')
+})
+
+const form = useForm({
+  resolver: zodResolver(formSchema),
+  defaultValues: { username: '', email: '' }
+})
+
+<Form {...form}>
+  <FormField
+    name="username"
+    render={({ field }) => (
+      <FormItem>
+        <FormLabel>用户名</FormLabel>
+        <FormControl>
+          <Input placeholder="请输入用户名" {...field} />
+        </FormControl>
+        <FormMessage />
+      </FormItem>
+    )}
+  />
+</Form>
+```
+
+### Card 组件
+```typescript
+import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
+
+<Card>
+  <CardHeader>
+    <CardTitle>卡片标题</CardTitle>
+  </CardHeader>
+  <CardContent>
+    <Text>卡片内容</Text>
+  </CardContent>
+</Card>
+```
+
+### Navbar 组件
+```typescript
+import { Navbar } from '@/components/ui/navbar'
+
+// 基础用法
+<Navbar title="页面标题" />
+
+// 带返回按钮
+<Navbar 
+  title="页面标题" 
+  leftIcon="i-heroicons-chevron-left-20-solid"
+  onClickLeft={() => Taro.navigateBack()}
+/>
+
+// 带右侧操作
+<Navbar 
+  title="页面标题"
+  rightIcon="i-heroicons-share-20-solid"
+  onClickRight={handleShare}
+/>
+```
+
+### Carousel 组件
+```typescript
+// 实际页面使用示例
+export function HomeCarousel() {
+  const bannerItems: CarouselItem[] = [
+    {
+      src: 'https://via.placeholder.com/750x400/3B82F6/FFFFFF?text=Banner+1',
+      title: '新品上市',
+      description: '最新款式,限时优惠',
+      link: '/pages/goods/new-arrival'
+    },
+    {
+      src: 'https://via.placeholder.com/750x400/EF4444/FFFFFF?text=Banner+2',
+      title: '限时秒杀',
+      description: '每日特价,不容错过',
+      link: '/pages/goods/flash-sale'
+    },
+    {
+      src: 'https://via.placeholder.com/750x400/10B981/FFFFFF?text=Banner+3',
+      title: '会员专享',
+      description: '会员专享折扣和福利',
+      link: '/pages/member/benefits'
+    }
+  ]
+
+  const handleBannerClick = (item: CarouselItem, index: number) => {
+    if (item.link) {
+      // 使用Taro跳转
+      Taro.navigateTo({
+        url: item.link
+      })
+    }
+  }
+
+  return (
+    <View className="w-full">
+      <Carousel
+        items={bannerItems}
+        height={400}
+        autoplay={true}
+        interval={4000}
+        circular={true}
+        rounded="none"
+        onItemClick={handleBannerClick}
+      />
+    </View>
+  )
+}
+```
+
+## 页面类型分类
+
+### 1. TabBar页面(底部导航页)
+特点:
+- 使用 `TabBarLayout` 布局组件
+- 路径配置在 `mini/src/app.config.ts` 中的 `tabBar.list`
+- 包含底部导航栏,用户可直接切换
+- 通常包含 `Navbar` 顶部导航组件
+- 示例页面:首页、发现、个人中心
+
+### 2. 非TabBar页面(独立页面)
+特点:
+- 不使用 `TabBarLayout`,直接渲染内容
+- 使用 `Navbar` 组件作为顶部导航
+- 需要手动处理返回导航
+- 示例页面:登录、注册、详情页
+
+## 开发流程
+
+### 1. 创建页面目录
+```bash
+# TabBar页面
+mkdir -p mini/src/pages/[页面名称]
+
+# 非TabBar页面
+mkdir -p mini/src/pages/[页面名称]
+```
+
+### 2. 创建页面文件
+
+#### TabBar页面模板
+```typescript
+// mini/src/pages/[页面名称]/index.tsx
+import React from 'react'
+import { View, Text } from '@tarojs/components'
+import { TabBarLayout } from '@/layouts/tab-bar-layout'
+import { Navbar } from '@/components/ui/navbar'
+import { Button } from '@/components/ui/button'
+import { Card } from '@/components/ui/card'
+import './index.css'
+
+const [页面名称]Page: React.FC = () => {
+  return (
+    <TabBarLayout activeKey="[对应tabBar.key]">
+      <Navbar
+        title="页面标题"
+        rightIcon="i-heroicons-[图标名称]-20-solid"
+        onClickRight={() => console.log('点击右上角')}
+        leftIcon=""
+      />
+      <View className="px-4 py-4">
+        <Card>
+          <CardHeader>
+            <CardTitle>欢迎使用</CardTitle>
+          </CardHeader>
+          <CardContent>
+            <Text>这是一个使用shadui组件的TabBar页面</Text>
+            <Button className="mt-4">开始使用</Button>
+          </CardContent>
+        </Card>
+      </View>
+    </TabBarLayout>
+  )
+}
+
+export default [页面名称]Page
+```
+
+#### 非TabBar页面模板
+```typescript
+// mini/src/pages/[页面名称]/index.tsx
+import { View } from '@tarojs/components'
+import { useEffect } from 'react'
+import Taro from '@tarojs/taro'
+import { Navbar } from '@/components/ui/navbar'
+import { Card } from '@/components/ui/card'
+import { Button } from '@/components/ui/button'
+import './index.css'
+
+export default function [页面名称]() {
+  useEffect(() => {
+    Taro.setNavigationBarTitle({
+      title: '页面标题'
+    })
+  }, [])
+
+  return (
+    <View className="min-h-screen bg-gray-50">
+      <Navbar
+        title="页面标题"
+        backgroundColor="bg-transparent"
+        textColor="text-gray-900"
+        border={false}
+      />
+      <View className="px-6 py-4">
+        <Card>
+          <CardContent>
+            <Text>这是一个使用shadui组件的非TabBar页面</Text>
+            <Button className="mt-4">返回</Button>
+          </CardContent>
+        </Card>
+      </View>
+    </View>
+  )
+}
+```
+
+### 3. 页面配置文件
+```typescript
+// mini/src/pages/[页面名称]/index.config.ts
+export default definePageConfig({
+  navigationBarTitleText: '页面标题',
+  enablePullDownRefresh: true,
+  backgroundTextStyle: 'dark',
+  navigationBarBackgroundColor: '#ffffff',
+  navigationBarTextStyle: 'black'
+})
+```
+
+### 4. 样式文件
+统一使用tailwindcss类,index.css为空即可
+```css
+/* mini/src/pages/[页面名称]/index.css */
+
+```
+
+## 高级功能模板
+
+### 1. 带认证的页面模板
+```typescript
+// mini/src/pages/[需要认证的页面]/index.tsx
+import { View, Text } from '@tarojs/components'
+import { useEffect } from 'react'
+import { useAuth } from '@/utils/auth'
+import Taro from '@tarojs/taro'
+import { Navbar } from '@/components/ui/navbar'
+import { Card } from '@/components/ui/card'
+
+export default function ProtectedPage() {
+  const { user, isLoading, isLoggedIn } = useAuth()
+
+  useEffect(() => {
+    if (!isLoading && !isLoggedIn) {
+      Taro.navigateTo({ url: '/pages/login/index' })
+    }
+  }, [isLoading, isLoggedIn])
+
+  if (isLoading) {
+    return (
+      <View className="flex-1 flex items-center justify-center">
+        <View className="i-heroicons-arrow-path-20-solid animate-spin w-8 h-8 text-blue-500" />
+      </View>
+    )
+  }
+
+  if (!user) return null
+
+  return (
+    <View className="min-h-screen bg-gray-50">
+      <Navbar title="受保护页面" leftIcon="" />
+      <View className="px-4 py-4">
+        <Text>欢迎, {user.username}</Text>
+      </View>
+    </View>
+  )
+}
+```
+
+### 2. 带API调用的页面模板
+```typescript
+// mini/src/pages/[数据展示页面]/index.tsx
+import { View, ScrollView } from '@tarojs/components'
+import { useQuery } from '@tanstack/react-query'
+import { userClient } from '@/api'
+import { InferResponseType } from 'hono'
+import Taro from '@tarojs/taro'
+
+type UserListResponse = InferResponseType<typeof userClient.$get, 200>
+
+export default function UserListPage() {
+  const { data, isLoading, error } = useQuery<UserListResponse>({
+    queryKey: ['users'],
+    queryFn: async () => {
+      const response = await userClient.$get({})
+      if (response.status !== 200) {
+        throw new Error('获取用户列表失败')
+      }
+      return response.json()
+    },
+    staleTime: 5 * 60 * 1000, // 5分钟
+  })
+
+  if (isLoading) {
+    return (
+      <View className="flex-1 flex items-center justify-center">
+        <View className="i-heroicons-arrow-path-20-solid animate-spin w-8 h-8 text-blue-500" />
+      </View>
+    )
+  }
+
+  if (error) {
+    return (
+      <View className="flex-1 flex items-center justify-center">
+        <Text className="text-red-500">{error.message}</Text>
+      </View>
+    )
+  }
+
+  return (
+    <ScrollView className="h-screen">
+      <Navbar title="用户列表" leftIcon="" />
+      <View className="px-4 py-4">
+        {data?.data.map(user => (
+          <View key={user.id} className="bg-white rounded-lg p-4 mb-3">
+            <Text>{user.username}</Text>
+          </View>
+        ))}
+      </View>
+    </ScrollView>
+  )
+}
+```
+
+### 3. 带表单提交的页面模板
+```typescript
+// mini/src/pages/[表单页面]/index.tsx
+import { View } from '@tarojs/components'
+import { useState } from 'react'
+import { useForm } from 'react-hook-form'
+import { zodResolver } from '@hookform/resolvers/zod'
+import { z } from 'zod'
+import { useMutation } from '@tanstack/react-query'
+import { userClient } from '@/api'
+import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage } from '@/components/ui/form'
+import { Input } from '@/components/ui/input'
+import { Button } from '@/components/ui/button'
+import Taro from '@tarojs/taro'
+
+const formSchema = z.object({
+  username: z.string().min(3, '用户名至少3个字符'),
+  email: z.string().email('请输入有效的邮箱地址'),
+  phone: z.string().regex(/^1[3-9]\d{9}$/, '请输入有效的手机号')
+})
+
+type FormData = z.infer<typeof formSchema>
+
+export default function CreateUserPage() {
+  const [loading, setLoading] = useState(false)
+  
+  const form = useForm<FormData>({
+    resolver: zodResolver(formSchema),
+    defaultValues: {
+      username: '',
+      email: '',
+      phone: ''
+    }
+  })
+
+  const mutation = useMutation({
+    mutationFn: async (data: FormData) => {
+      const response = await userClient.$post({ json: data })
+      if (response.status !== 201) {
+        throw new Error('创建用户失败')
+      }
+      return response.json()
+    },
+    onSuccess: () => {
+      Taro.showToast({
+        title: '创建成功',
+        icon: 'success'
+      })
+      Taro.navigateBack()
+    },
+    onError: (error) => {
+      Taro.showToast({
+        title: error.message || '创建失败',
+        icon: 'none'
+      })
+    }
+  })
+
+  const onSubmit = async (data: FormData) => {
+    setLoading(true)
+    try {
+      await mutation.mutateAsync(data)
+    } finally {
+      setLoading(false)
+    }
+  }
+
+  return (
+    <View className="min-h-screen bg-gray-50">
+      <Navbar title="创建用户" leftIcon="" />
+      <View className="px-4 py-4">
+        <Form {...form}>
+          <View className="space-y-4">
+            <FormField
+              control={form.control}
+              name="username"
+              render={({ field }) => (
+                <FormItem>
+                  <FormLabel>用户名</FormLabel>
+                  <FormControl>
+                    <Input placeholder="请输入用户名" {...field} />
+                  </FormControl>
+                  <FormMessage />
+                </FormItem>
+              )}
+            />
+            <Button
+              className="w-full"
+              onClick={form.handleSubmit(onSubmit)}
+              disabled={loading}
+            >
+              {loading ? '创建中...' : '创建用户'}
+            </Button>
+          </View>
+        </Form>
+      </View>
+    </View>
+  )
+}
+```
+
+## 认证功能使用
+
+### 1. useAuth Hook 使用规范
+```typescript
+import { useAuth } from '@/utils/auth'
+
+// 在页面或组件中使用
+const { 
+  user,           // 当前用户信息
+  login,          // 登录函数
+  logout,         // 登出函数
+  register,       // 注册函数
+  updateUser,     // 更新用户信息
+  isLoading,      // 加载状态
+  isLoggedIn      // 是否已登录
+} = useAuth()
+
+// 使用示例
+const handleLogin = async (formData) => {
+  try {
+    await login(formData)
+    Taro.switchTab({ url: '/pages/index/index' })
+  } catch (error) {
+    console.error('登录失败:', error)
+  }
+}
+```
+
+### 2. 页面权限控制
+```typescript
+// 在需要认证的页面顶部
+const { user, isLoading, isLoggedIn } = useAuth()
+
+useEffect(() => {
+  if (!isLoading && !isLoggedIn) {
+    Taro.navigateTo({ url: '/pages/login/index' })
+  }
+}, [isLoading, isLoggedIn])
+
+// 或者使用路由守卫模式
+```
+
+## RPC客户端调用规范
+
+### 1. 客户端导入
+```typescript
+// 从api.ts导入对应的客户端
+import { authClient, userClient, fileClient } from '@/api'
+```
+
+### 2. 类型提取
+```typescript
+import { InferResponseType, InferRequestType } from 'hono'
+
+// 响应类型提取
+type UserResponse = InferResponseType<typeof userClient.$get, 200>
+type UserDetailResponse = InferResponseType<typeof userClient[':id']['$get'], 200>
+
+// 请求类型提取
+type CreateUserRequest = InferRequestType<typeof userClient.$post>['json']
+type UpdateUserRequest = InferRequestType<typeof userClient[':id']['$put']>['json']
+```
+
+### 3. 调用示例
+```typescript
+// GET请求 - 列表
+const response = await userClient.$get({
+  query: {
+    page: 1,
+    pageSize: 10,
+    keyword: 'search'
+  }
+})
+
+// GET请求 - 单条
+const response = await userClient[':id'].$get({
+  param: { id: userId }
+})
+
+// POST请求
+const response = await userClient.$post({
+  json: {
+    username: 'newuser',
+    email: 'user@example.com'
+  }
+})
+
+// PUT请求
+const response = await userClient[':id'].$put({
+  param: { id: userId },
+  json: { username: 'updated' }
+})
+
+// DELETE请求
+const response = await userClient[':id'].$delete({
+  param: { id: userId }
+})
+```
+
+## React Query v5使用规范
+
+### 1. 查询配置
+```typescript
+const { data, isLoading, error, refetch } = useQuery({
+  queryKey: ['users', page, keyword], // 唯一的查询键
+  queryFn: async () => {
+    const response = await userClient.$get({
+      query: { page, pageSize: 10, keyword }
+    })
+    if (response.status !== 200) {
+      throw new Error('获取数据失败')
+    }
+    return response.json()
+  },
+  staleTime: 5 * 60 * 1000, // 5分钟
+  cacheTime: 10 * 60 * 1000, // 10分钟
+  retry: 3, // 重试3次
+  enabled: !!keyword, // 条件查询
+})
+```
+
+### 2. 变更操作
+```typescript
+const queryClient = useQueryClient()
+
+const mutation = useMutation({
+  mutationFn: async (data: CreateUserRequest) => {
+    const response = await userClient.$post({ json: data })
+    if (response.status !== 201) {
+      throw new Error('创建失败')
+    }
+    return response.json()
+  },
+  onSuccess: () => {
+    // 成功后刷新相关查询
+    queryClient.invalidateQueries({ queryKey: ['users'] })
+    Taro.showToast({ title: '创建成功', icon: 'success' })
+  },
+  onError: (error) => {
+    Taro.showToast({ 
+      title: error.message || '操作失败', 
+      icon: 'none' 
+    })
+  }
+})
+```
+
+### 3. 删除操作
+```typescript
+const queryClient = useQueryClient()
+
+const mutation = useMutation({
+  mutationFn: async (id: number) => {
+    const response = await deliveryAddressClient[':id'].$delete({
+      param: { id }
+    })
+    if (response.status !== 204) {
+      throw new Error('删除地址失败')
+    }
+    return response.json()
+  },
+  onSuccess: () => {
+    queryClient.invalidateQueries({ queryKey: ['delivery-addresses'] })
+    Taro.showToast({
+      title: '删除成功',
+      icon: 'success'
+    })
+  },
+  onError: (error) => {
+    Taro.showToast({
+      title: error.message || '删除失败',
+      icon: 'none'
+    })
+  }
+})
+```
+
+### 4. 分页查询
+#### 标准分页(useQuery)
+```typescript
+const useUserList = (page: number, pageSize: number = 10) => {
+  return useQuery({
+    queryKey: ['users', page, pageSize],
+    queryFn: async () => {
+      const response = await userClient.$get({
+        query: { page, pageSize }
+      })
+      return response.json()
+    },
+    keepPreviousData: true, // 保持上一页数据
+  })
+}
+```
+
+#### 移动端无限滚动分页(useInfiniteQuery)
+```typescript
+import { useInfiniteQuery } from '@tanstack/react-query'
+
+const useInfiniteUserList = (keyword?: string) => {
+  return useInfiniteQuery({
+    queryKey: ['users-infinite', keyword],
+    queryFn: async ({ pageParam = 1 }) => {
+      const response = await userClient.$get({
+        query: {
+          page: pageParam,
+          pageSize: 10,
+          keyword
+        }
+      })
+      if (response.status !== 200) {
+        throw new Error('获取用户列表失败')
+      }
+      return response.json()
+    },
+    getNextPageParam: (lastPage, allPages) => {
+      const totalPages = Math.ceil(lastPage.pagination.total / lastPage.pagination.pageSize)
+      const nextPage = allPages.length + 1
+      return nextPage <= totalPages ? nextPage : undefined
+    },
+    staleTime: 5 * 60 * 1000,
+  })
+}
+
+// 使用示例
+const {
+  data,
+  isLoading,
+  isFetchingNextPage,
+  fetchNextPage,
+  hasNextPage,
+  refetch
+} = useInfiniteUserList(searchKeyword)
+
+// 合并所有分页数据
+const allUsers = data?.pages.flatMap(page => page.data) || []
+
+// 触底加载更多处理
+const handleScrollToLower = () => {
+  if (hasNextPage && !isFetchingNextPage) {
+    fetchNextPage()
+  }
+}
+```
+
+#### 移动端分页页面模板
+```typescript
+// mini/src/pages/[无限滚动列表]/index.tsx
+import { View, ScrollView } from '@tarojs/components'
+import { useInfiniteQuery } from '@tanstack/react-query'
+import { goodsClient } from '@/api'
+import { InferResponseType } from 'hono'
+import Taro from '@tarojs/taro'
+
+type GoodsResponse = InferResponseType<typeof goodsClient.$get, 200>
+
+export default function InfiniteGoodsList() {
+  const [searchKeyword, setSearchKeyword] = useState('')
+
+  const {
+    data,
+    isLoading,
+    isFetchingNextPage,
+    fetchNextPage,
+    hasNextPage,
+    refetch
+  } = useInfiniteQuery({
+    queryKey: ['goods-infinite', searchKeyword],
+    queryFn: async ({ pageParam = 1 }) => {
+      const response = await goodsClient.$get({
+        query: {
+          page: pageParam,
+          pageSize: 10,
+          keyword: searchKeyword
+        }
+      })
+      if (response.status !== 200) {
+        throw new Error('获取商品失败')
+      }
+      return response.json()
+    },
+    getNextPageParam: (lastPage) => {
+      const { pagination } = lastPage
+      const totalPages = Math.ceil(pagination.total / pagination.pageSize)
+      return pagination.current < totalPages ? pagination.current + 1 : undefined
+    },
+    staleTime: 5 * 60 * 1000,
+    initialPageParam: 1,
+  })
+
+  // 合并所有分页数据
+  const allGoods = data?.pages.flatMap(page => page.data) || []
+
+  // 触底加载更多
+  const handleScrollToLower = () => {
+    if (hasNextPage && !isFetchingNextPage) {
+      fetchNextPage()
+    }
+  }
+
+  // 下拉刷新
+  const onPullDownRefresh = () => {
+    refetch().finally(() => {
+      Taro.stopPullDownRefresh()
+    })
+  }
+
+  return (
+    <ScrollView
+      className="h-screen"
+      scrollY
+      onScrollToLower={handleScrollToLower}
+      refresherEnabled
+      refresherTriggered={false}
+      onRefresherRefresh={onPullDownRefresh}
+    >
+      <View className="px-4 py-4">
+        {isLoading ? (
+          <View className="flex justify-center py-10">
+            <View className="i-heroicons-arrow-path-20-solid animate-spin w-8 h-8 text-blue-500" />
+          </View>
+        ) : (
+          <>
+            {allGoods.map((item) => (
+              <View key={item.id} className="bg-white rounded-lg p-4 mb-3">
+                <Text>{item.name}</Text>
+              </View>
+            ))}
+            
+            {isFetchingNextPage && (
+              <View className="flex justify-center py-4">
+                <View className="i-heroicons-arrow-path-20-solid animate-spin w-6 h-6 text-blue-500" />
+                <Text className="ml-2 text-sm text-gray-500">加载更多...</Text>
+              </View>
+            )}
+            
+            {!hasNextPage && allGoods.length > 0 && (
+              <View className="text-center py-4 text-sm text-gray-400">
+                没有更多了
+              </View>
+            )}
+          </>
+        )}
+      </View>
+    </ScrollView>
+  )
+}
+```
+
+## 表单处理规范
+
+### 1. 表单Schema定义
+```typescript
+// 在schemas目录下定义
+import { z } from 'zod'
+
+export const userSchema = z.object({
+  username: z.string()
+    .min(3, '用户名至少3个字符')
+    .max(20, '用户名最多20个字符')
+    .regex(/^\S+$/, '用户名不能包含空格'),
+  email: z.string().email('请输入有效的邮箱地址'),
+  phone: z.string().regex(/^1[3-9]\d{9}$/, '请输入有效的手机号')
+})
+
+export type UserFormData = z.infer<typeof userSchema>
+```
+
+### 2. 表单使用
+```typescript
+import { useForm } from 'react-hook-form'
+import { zodResolver } from '@hookform/resolvers/zod'
+import { userSchema, type UserFormData } from '@/schemas/user.schema'
+
+const form = useForm<UserFormData>({
+  resolver: zodResolver(userSchema),
+  defaultValues: {
+    username: '',
+    email: '',
+    phone: ''
+  }
+})
+
+// 表单提交
+const onSubmit = async (data: UserFormData) => {
+  try {
+    await mutation.mutateAsync(data)
+  } catch (error) {
+    console.error('表单提交失败:', error)
+  }
+}
+```
+
+## 错误处理规范
+
+### 1. 统一的错误处理
+```typescript
+const handleApiError = (error: any) => {
+  const message = error.response?.data?.message || error.message || '操作失败'
+  
+  if (error.response?.status === 401) {
+    Taro.showModal({
+      title: '未登录',
+      content: '请先登录',
+      success: () => {
+        Taro.navigateTo({ url: '/pages/login/index' })
+      }
+    })
+  } else if (error.response?.status === 403) {
+    Taro.showToast({ title: '权限不足', icon: 'none' })
+  } else if (error.response?.status === 404) {
+    Taro.showToast({ title: '资源不存在', icon: 'none' })
+  } else if (error.response?.status >= 500) {
+    Taro.showToast({ title: '服务器错误,请稍后重试', icon: 'none' })
+  } else {
+    Taro.showToast({ title: message, icon: 'none' })
+  }
+}
+```
+
+### 2. 页面级错误处理
+```typescript
+const { data, isLoading, error } = useQuery({
+  // ...查询配置
+})
+
+if (error) {
+  return (
+    <View className="flex-1 flex items-center justify-center">
+      <View className="text-center">
+        <View className="i-heroicons-exclamation-triangle-20-solid w-12 h-12 text-red-500 mx-auto mb-4" />
+        <Text className="text-gray-600 mb-4">{error.message}</Text>
+        <Button onClick={() => refetch()}>重新加载</Button>
+      </View>
+    </View>
+  )
+}
+```
+
+## 页面模板示例
+
+### 1. TabBar页面标准结构
+```typescript
+// 示例:首页
+import React from 'react'
+import { View, Text } from '@tarojs/components'
+import { TabBarLayout } from '@/layouts/tab-bar-layout'
+import { Navbar } from '@/components/ui/navbar'
+import { Button } from '@/components/ui/button'
+import './index.css'
+
+const HomePage: React.FC = () => {
+  return (
+    <TabBarLayout activeKey="home">
+      <Navbar
+        title="首页"
+        rightIcon="i-heroicons-bell-20-solid"
+        onClickRight={() => console.log('点击通知')}
+        leftIcon=""
+      />
+      <View className="px-4 py-4">
+        <Text className="text-2xl font-bold text-gray-900">欢迎使用</Text>
+        <View className="mt-4">
+          <Text className="text-gray-600">这是一个简洁优雅的小程序首页</Text>
+        </View>
+      </View>
+    </TabBarLayout>
+  )
+}
+
+export default HomePage
+```
+
+### 2. 非TabBar页面标准结构
+```typescript
+// 示例:登录页
+import { View } from '@tarojs/components'
+import { useEffect } from 'react'
+import Taro from '@tarojs/taro'
+import { Navbar } from '@/components/ui/navbar'
+import { Button } from '@/components/ui/button'
+import './index.css'
+
+export default function Login() {
+  useEffect(() => {
+    Taro.setNavigationBarTitle({
+      title: '用户登录'
+    })
+  }, [])
+
+  return (
+    <View className="min-h-screen bg-gradient-to-br from-blue-50 via-white to-indigo-50">
+      <Navbar
+        title="用户登录"
+        backgroundColor="bg-transparent"
+        textColor="text-gray-900"
+        border={false}
+      />
+      <View className="flex-1 px-6 py-12">
+        {/* Logo区域 */}
+        <View className="flex flex-col items-center mb-10">
+          <View className="w-20 h-20 mb-4 rounded-full bg-white shadow-lg flex items-center justify-center">
+            <View className="i-heroicons-user-circle-20-solid w-12 h-12 text-blue-500" />
+          </View>
+          <Text className="text-2xl font-bold text-gray-900 mb-1">欢迎回来</Text>
+        </View>
+
+        {/* 表单区域 */}
+        <View className="bg-white rounded-2xl shadow-sm p-6">
+          <View className="space-y-5">
+            {/* 表单内容 */}
+          </View>
+        </View>
+      </View>
+    </View>
+  )
+}
+```
+
+
+## 路由配置
+
+### 1. TabBar页面配置
+```typescript
+// mini/src/app.config.ts
+export default defineAppConfig({
+  pages: [
+    'pages/index/index',
+    'pages/explore/index',
+    'pages/profile/index',
+    // 其他页面
+  ],
+  tabBar: {
+    color: '#666666',
+    selectedColor: '#1976D2',
+    backgroundColor: '#ffffff',
+    borderStyle: 'black',
+    list: [
+      {
+        pagePath: 'pages/index/index',
+        text: '首页',
+        iconPath: 'assets/icons/home.png',
+        selectedIconPath: 'assets/icons/home-active.png'
+      },
+      {
+        pagePath: 'pages/explore/index',
+        text: '发现',
+        iconPath: 'assets/icons/explore.png',
+        selectedIconPath: 'assets/icons/explore-active.png'
+      },
+      {
+        pagePath: 'pages/profile/index',
+        text: '我的',
+        iconPath: 'assets/icons/profile.png',
+        selectedIconPath: 'assets/icons/profile-active.png'
+      }
+    ]
+  }
+})
+```
+
+### 2. 非TabBar页面路由
+非TabBar页面会自动添加到pages数组中,无需额外配置tabBar。
+
+## 最佳实践
+
+### 1. 命名规范
+- 页面目录:使用小写+中划线命名,如 `user-profile`
+- 组件名称:使用PascalCase,如 `UserProfilePage`
+- 文件名:使用小写+中划线命名,如 `user-profile.tsx`
+
+### 2. 样式规范
+- 使用Tailwind CSS原子类
+- 避免使用px,使用rpx单位
+- 页面背景色统一使用 `bg-gray-50` 或 `bg-white`
+
+### 3. 状态管理
+- 使用React hooks进行状态管理
+- 复杂状态使用Context API
+- 用户信息使用 `useAuth` hook
+
+### 4. 错误处理
+- 使用Taro.showToast显示错误信息
+- 网络请求使用try-catch包裹
+- 提供友好的用户反馈
+
+### 5. 性能优化
+- 使用懒加载组件
+- 避免不必要的重新渲染
+- 合理使用useMemo和useCallback
+
+## 常用工具函数
+
+### 1. 页面跳转
+```typescript
+// Tab页面跳转
+Taro.switchTab({ url: '/pages/index/index' })
+
+// 普通页面跳转
+Taro.navigateTo({ url: '/pages/login/index' })
+
+// 返回上一页
+Taro.navigateBack()
+
+// 重定向(清除当前页面历史)
+Taro.redirectTo({ url: '/pages/login/index' })
+
+// 重新启动应用
+Taro.reLaunch({ url: '/pages/index/index' })
+```
+
+### 2. 用户交互
+```typescript
+// 显示提示
+Taro.showToast({
+  title: '操作成功',
+  icon: 'success',
+  duration: 2000
+})
+
+// 显示加载
+Taro.showLoading({
+  title: '加载中...',
+  mask: true
+})
+Taro.hideLoading()
+
+// 显示确认对话框
+Taro.showModal({
+  title: '确认操作',
+  content: '确定要执行此操作吗?',
+  success: (res) => {
+    if (res.confirm) {
+      // 用户点击确认
+    }
+  }
+})
+
+// 显示操作菜单
+Taro.showActionSheet({
+  itemList: ['选项1', '选项2', '选项3'],
+  success: (res) => {
+    console.log('用户选择了', res.tapIndex)
+  }
+})
+```
+
+### 3. 本地存储
+```typescript
+// 存储数据
+Taro.setStorageSync('key', 'value')
+Taro.setStorageSync('user', JSON.stringify(user))
+
+// 获取数据
+const value = Taro.getStorageSync('key')
+const user = JSON.parse(Taro.getStorageSync('user') || '{}')
+
+// 移除数据
+Taro.removeStorageSync('key')
+
+// 清空所有数据
+Taro.clearStorageSync()
+```
+
+### 4. 设备信息
+```typescript
+// 获取系统信息
+const systemInfo = Taro.getSystemInfoSync()
+const { screenWidth, screenHeight, windowWidth, windowHeight, statusBarHeight } = systemInfo
+
+// 获取用户位置
+Taro.getLocation({
+  type: 'wgs84',
+  success: (res) => {
+    console.log('纬度:', res.latitude)
+    console.log('经度:', res.longitude)
+  }
+})

+ 15 - 0
.claude/commands/rpc-type-提取响应请求类型.md

@@ -0,0 +1,15 @@
+---
+description: "提取rpc响应,请求类型"
+---
+
+rpc类型安全
+示例:
+```typescript
+// 使用InferResponseType提取响应类型
+import type { InferResponseType } from 'hono/client'
+type LoginResponse = InferResponseType<typeof authClient['mini-login']['$post'], 200>
+
+// 使用InferRequestType提取请求类型
+import type { InferRequestType } from 'hono/client'
+type LoginRequest = InferRequestType<typeof authClient['mini-login']['$post']>['json']
+```

+ 16 - 0
.claude/commands/schema-添加中文错误提示.md

@@ -0,0 +1,16 @@
+---
+description: "给schema加上中文错误提示指令"
+---
+
+为每个验证字段添加 中文错误提示
+
+示例:
+```typescript
+// 用户 schema
+export const UserSchema = z.object({
+  username: z.string().min(3, '用户名至少3个字符').max(255, '最多255个字符'),
+  password: z.string().min(6, '密码至少6位').max(255, '最多255位'),
+  phone: z.string().regex(/^1[3-9]\d{9}$/, '请输入正确的手机号'),
+  email: z.email('请输入正确的邮箱格式'),
+  name: z.string().max(255, '姓名最多255个字符').optional()
+});

+ 5 - 0
.claude/commands/shadcn-创建页面和组件.md

@@ -0,0 +1,5 @@
+---
+description: "使用shadcn创建页面及组件"
+---
+
+shadcn配置在 components.json

+ 19 - 0
.claude/commands/shadcn-实体选择器创建.md

@@ -0,0 +1,19 @@
+---
+description: "基于实体名称的选择器组件创建指令"
+---
+
+# 基于实体名称的选择器组件抽取指令
+
+## 指令描述
+根据实际实体名称创建专门的选择器组件,每个实体对应一个独立的选择器,保持命名一致性和可预测性。
+
+## 使用方式
+执行此指令将根据项目中实际的实体名称自动创建对应的选择器组件,格式为 `[EntityName]Selector`。
+
+## 实体选择器命名规范
+- 广告类型 → `AdvertisementTypeSelector`
+- 用户 → `UserSelector`
+- 角色 → `RoleSelector`
+- 文件 → `FileSelector`
+- 产品 → `ProductSelector`
+- 通用格式: `[实体名]Selector`

+ 219 - 0
.claude/commands/shadcn-管理页表单分离.md

@@ -0,0 +1,219 @@
+---
+description: "Shadcn-ui 管理页表单创建/编辑表单分离指令"
+---
+
+将 创建/编辑表单分离
+不要 <Form {...(isCreateForm ? createForm : updateForm)}></Form>
+要     
+{isCreateForm ? (
+  // 创建表单(独立渲染)
+  <Form {...createForm}>
+  </Form>
+) : (
+  // 编辑表单(独立渲染)
+  <Form {...updateForm}>
+  </Form>
+)}
+
+
+### 创建/编辑表单分离模式(推荐)
+
+基于 `src/client/admin-shadcn/pages/Users.tsx` 的最佳实践,创建/编辑表单分离模式通过以下方式解决创建/编辑时数据对象类型差异问题:
+
+#### 核心优势
+1. **类型安全**:创建使用 `CreateEntityDto`,编辑使用 `UpdateEntityDto`,避免类型冲突
+2. **字段差异处理**:创建时的必填字段在编辑时自动变为可选
+3. **敏感字段处理**:密码等敏感字段在编辑时可设为可选
+4. **状态隔离**:两种模式完全独立,避免状态污染
+
+#### 完整实现模板
+```typescript
+// 1. 类型定义(使用真实后端类型)
+type CreateRequest = InferRequestType<typeof apiClient.$post>['json'];
+type UpdateRequest = InferRequestType<typeof apiClient[':id']['$put']>['json'];
+type EntityResponse = InferResponseType<typeof apiClient.$get, 200>['data'][0];
+
+// 2. 状态管理
+const [isModalOpen, setIsModalOpen] = useState(false);
+const [editingEntity, setEditingEntity] = useState<EntityResponse | null>(null);
+const [isCreateForm, setIsCreateForm] = useState(true);
+
+// 3. 分离的表单实例
+const createForm = useForm<CreateRequest>({
+  resolver: zodResolver(CreateEntityDto),
+  defaultValues: {
+    // 创建时必须提供的默认值
+  },
+});
+
+const updateForm = useForm<UpdateRequest>({
+  resolver: zodResolver(UpdateEntityDto),
+  defaultValues: {
+    // 更新时的默认值(会被实际数据覆盖)
+  },
+});
+
+// 4. 表单操作函数
+const handleCreate = () => {
+  setEditingEntity(null);
+  setIsCreateForm(true);
+  createForm.reset({
+    // 创建时的初始值
+    status: 1, // 示例:默认启用
+  });
+  setIsModalOpen(true);
+};
+
+const handleEdit = (entity: EntityResponse) => {
+  setEditingEntity(entity);
+  setIsCreateForm(false);
+  updateForm.reset({
+    ...entity,
+    // 关键:处理创建/编辑字段差异
+    password: undefined, // 密码在更新时可选
+    confirmPassword: undefined,
+    // 其他需要特殊处理的字段
+  });
+  setIsModalOpen(true);
+};
+
+// 5. 提交处理
+const handleCreateSubmit = async (data: CreateRequest) => {
+  try {
+    const res = await apiClient.$post({ json: data });
+    if (res.status !== 201) throw new Error('创建失败');
+    toast.success('创建成功');
+    setIsModalOpen(false);
+    refetch();
+  } catch (error) {
+    toast.error('创建失败,请重试');
+  }
+};
+
+const handleUpdateSubmit = async (data: UpdateRequest) => {
+  if (!editingEntity) return;
+  
+  try {
+    const res = await apiClient[':id']['$put']({
+      param: { id: editingEntity.id },
+      json: data
+    });
+    if (res.status !== 200) throw new Error('更新失败');
+    toast.success('更新成功');
+    setIsModalOpen(false);
+    refetch();
+  } catch (error) {
+    toast.error('更新失败,请重试');
+  }
+};
+```
+
+#### 对话框渲染模板
+```tsx
+<Dialog open={isModalOpen} onOpenChange={setIsModalOpen}>
+  <DialogContent className="sm:max-w-[500px] max-h-[90vh] overflow-y-auto">
+    <DialogHeader>
+      <DialogTitle>{isCreateForm ? '创建实体' : '编辑实体'}</DialogTitle>
+      <DialogDescription>
+        {isCreateForm ? '创建一个新的实体' : '编辑现有实体信息'}
+      </DialogDescription>
+    </DialogHeader>
+    
+    {isCreateForm ? (
+      // 创建表单(独立渲染)
+      <Form {...createForm}>
+        <form onSubmit={createForm.handleSubmit(handleCreateSubmit)} className="space-y-4">
+          {/* 创建专用字段 - 必填 */}
+          <FormField
+            control={createForm.control}
+            name="username"
+            render={({ field }) => (
+              <FormItem>
+                <FormLabel className="flex items-center">
+                  用户名
+                  <span className="text-red-500 ml-1">*</span>
+                </FormLabel>
+                <FormControl>
+                  <Input placeholder="请输入用户名" {...field} />
+                </FormControl>
+                <FormMessage />
+              </FormItem>
+            )}
+          />
+          
+          {/* 创建时必填的密码 */}
+          <FormField
+            control={createForm.control}
+            name="password"
+            render={({ field }) => (
+              <FormItem>
+                <FormLabel className="flex items-center">
+                  密码
+                  <span className="text-red-500 ml-1">*</span>
+                </FormLabel>
+                <FormControl>
+                  <Input type="password" placeholder="请输入密码" {...field} />
+                </FormControl>
+                <FormMessage />
+              </FormItem>
+            )}
+          />
+          
+          <DialogFooter>
+            <Button type="button" variant="outline" onClick={() => setIsModalOpen(false)}>
+              取消
+            </Button>
+            <Button type="submit">创建</Button>
+          </DialogFooter>
+        </form>
+      </Form>
+    ) : (
+      // 编辑表单(独立渲染)
+      <Form {...updateForm}>
+        <form onSubmit={updateForm.handleSubmit(handleUpdateSubmit)} className="space-y-4">
+          {/* 编辑专用字段 - 可选 */}
+          <FormField
+            control={updateForm.control}
+            name="username"
+            render={({ field }) => (
+              <FormItem>
+                <FormLabel className="flex items-center">
+                  用户名
+                  <span className="text-red-500 ml-1">*</span>
+                </FormLabel>
+                <FormControl>
+                  <Input placeholder="请输入用户名" {...field} />
+                </FormControl>
+                <FormMessage />
+              </FormItem>
+            )}
+          />
+          
+          {/* 编辑时可选的密码 */}
+          <FormField
+            control={updateForm.control}
+            name="password"
+            render={({ field }) => (
+              <FormItem>
+                <FormLabel>新密码</FormLabel>
+                <FormControl>
+                  <Input type="password" placeholder="留空则不修改密码" {...field} />
+                </FormControl>
+                <FormMessage />
+              </FormItem>
+            )}
+          />
+          
+          <DialogFooter>
+            <Button type="button" variant="outline" onClick={() => setIsModalOpen(false)}>
+              取消
+            </Button>
+            <Button type="submit">更新</Button>
+          </DialogFooter>
+        </form>
+      </Form>
+    )}
+  </DialogContent>
+</Dialog>
+```
+

+ 722 - 0
.claude/commands/shadcn-管理页表单开发.md

@@ -0,0 +1,722 @@
+---
+description: "Shadcn-ui 管理页表单开发指令"
+---
+
+## 概述
+基于 `src/client/admin-shadcn/pages/Users.tsx` 中用户管理表单的实现,提取可复用的表单开发模式和最佳实践,适用于基于 Shadcn-ui 的管理后台表单开发。
+
+## 核心特性
+
+### 1. 类型安全表单
+- **后端Schema复用**:直接使用后端定义的 Zod Schema
+- **RPC类型提取**:从 Hono 客户端自动推断类型
+- **一致的类型定义**:前后端类型完全同步
+
+### 2. 表单状态管理(推荐:创建/编辑表单分离模式)
+- **分离表单实例**:为创建和编辑分别使用独立的表单实例, Form组件也分开
+- **类型安全**:创建使用CreateSchema,编辑使用UpdateSchema,避免类型冲突
+- **字段差异处理**:创建时的必填字段在编辑时变为可选,敏感字段特殊处理
+- **状态隔离**:两种模式的状态完全独立,避免交叉污染
+
+### 3. 统一的UI组件模式
+- **Shadcn-ui组件集成**:使用标准的 Shadcn-ui 表单组件
+- **响应式布局**:适配不同屏幕尺寸
+- **无障碍支持**:完整的 ARIA 属性支持
+
+## 开发模板
+
+### 基础结构模板(创建/编辑分离模式)
+```typescript
+// 1. 类型定义(使用后端真实类型)
+import { useForm } from 'react-hook-form';
+import { zodResolver } from '@hookform/resolvers/zod';
+import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from '@/client/components/ui/form';
+import { Input } from '@/client/components/ui/input';
+import { Button } from '@/client/components/ui/button';
+import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/client/components/ui/dialog';
+
+// 2. 分离的表单配置
+const [isCreateForm, setIsCreateForm] = useState(true);
+const [editingEntity, setEditingEntity] = useState<any>(null); // 用于存储编辑时的实体数据
+
+// 3. 独立的表单实例
+const createForm = useForm<CreateRequest>({
+  resolver: zodResolver(CreateEntityDto), // 使用创建专用的Schema
+  defaultValues: {
+    // 创建时必填字段的默认值
+  },
+});
+
+const updateForm = useForm<UpdateRequest>({
+  resolver: zodResolver(UpdateEntityDto), // 使用更新专用的Schema
+  defaultValues: {
+    // 更新时可选字段的默认值(会被实际数据覆盖)
+  },
+});
+
+// 4. 表单切换逻辑(核心模式)
+const handleCreateEntity = () => {
+  setEditingEntity(null);
+  setIsCreateForm(true);
+  createForm.reset({
+    // 创建时的初始值(必填字段必须有值)
+  });
+  setIsModalOpen(true);
+};
+
+const handleEditEntity = (entity: EntityResponse) => {
+  setEditingEntity(entity);
+  setIsCreateForm(false);
+  updateForm.reset({
+    ...entity,
+    // 特殊处理:敏感字段在编辑时设为可选
+    password: undefined, // 密码在更新时可选,不修改则留空
+    // 其他需要特殊处理的字段
+  });
+  setIsModalOpen(true);
+};
+```
+
+### 表单字段模板
+
+#### 文本输入框
+```typescript
+<FormField
+  control={form.control}
+  name="username"
+  render={({ field }) => (
+    <FormItem>
+      <FormLabel className="flex items-center">
+        用户名
+        <span className="text-red-500 ml-1">*</span>
+      </FormLabel>
+      <FormControl>
+        <Input placeholder="请输入用户名" {...field} />
+      </FormControl>
+      <FormMessage />
+    </FormItem>
+  )}
+/>
+```
+
+#### 邮箱输入框
+```typescript
+<FormField
+  control={form.control}
+  name="email"
+  render={({ field }) => (
+    <FormItem>
+      <FormLabel>邮箱</FormLabel>
+      <FormControl>
+        <Input type="email" placeholder="请输入邮箱" {...field} />
+      </FormControl>
+      <FormMessage />
+    </FormItem>
+  )}
+/>
+```
+
+#### 密码输入框
+```typescript
+<FormField
+  control={form.control}
+  name="password"
+  render={({ field }) => (
+    <FormItem>
+      <FormLabel className="flex items-center">
+        密码
+        <span className="text-red-500 ml-1">*</span>
+      </FormLabel>
+      <FormControl>
+        <Input type="password" placeholder="请输入密码" {...field} />
+      </FormControl>
+      <FormMessage />
+    </FormItem>
+  )}
+/>
+```
+
+#### 开关控件(布尔值)
+```typescript
+<FormField
+  control={form.control}
+  name="isDisabled"
+  render={({ field }) => (
+    <FormItem className="flex flex-row items-center justify-between rounded-lg border p-4">
+      <div className="space-y-0.5">
+        <FormLabel className="text-base">状态</FormLabel>
+        <FormDescription>
+          禁用后用户将无法登录系统
+        </FormDescription>
+      </div>
+      <FormControl>
+        <Switch
+          checked={field.value === 1}
+          onCheckedChange={(checked) => field.onChange(checked ? 1 : 0)}
+        />
+      </FormControl>
+    </FormItem>
+  )}
+/>
+```
+
+#### 可选字段处理
+```typescript
+// 创建时:必须提供值
+nickname: z.string().optional()
+
+// 更新时:完全可选
+nickname: z.string().optional()
+```
+
+## 高级表单组件模板
+
+### 头像选择器集成
+```typescript
+import AvatarSelector from '@/client/admin-shadcn/components/AvatarSelector';
+
+<FormField
+  control={form.control}
+  name="avatarFileId"
+  render={({ field }) => (
+    <FormItem>
+      <FormLabel>头像</FormLabel>
+      <FormControl>
+        <AvatarSelector
+          value={field.value || undefined}
+          onChange={(value) => field.onChange(value)}
+          maxSize={2}
+          uploadPath="/avatars"
+          uploadButtonText="上传头像"
+          previewSize="medium"
+          placeholder="选择头像"
+        />
+      </FormControl>
+      <FormMessage />
+    </FormItem>
+  )}
+/>
+```
+
+### 下拉选择框
+```typescript
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/client/components/ui/select';
+
+<FormField
+  control={form.control}
+  name="status"
+  render={({ field }) => (
+    <FormItem>
+      <FormLabel>状态</FormLabel>
+      <Select onValueChange={field.onChange} defaultValue={String(field.value)}>
+        <FormControl>
+          <SelectTrigger>
+            <SelectValue placeholder="请选择状态" />
+          </SelectTrigger>
+        </FormControl>
+        <SelectContent>
+          <SelectItem value="0">启用</SelectItem>
+          <SelectItem value="1">禁用</SelectItem>
+        </SelectContent>
+      </Select>
+      <FormMessage />
+    </FormItem>
+  )}
+/>
+```
+
+### 数字输入框
+```typescript
+<FormField
+  control={form.control}
+  name="age"
+  render={({ field }) => (
+    <FormItem>
+      <FormLabel>年龄</FormLabel>
+      <FormControl>
+        <Input 
+          type="number" 
+          placeholder="请输入年龄"
+          {...field}
+          onChange={(e) => field.onChange(Number(e.target.value))}
+        />
+      </FormControl>
+      <FormMessage />
+    </FormItem>
+  )}
+/>
+```
+
+### 日期选择器
+```typescript
+import { Popover, PopoverContent, PopoverTrigger } from '@/client/components/ui/popover';
+import { Calendar } from '@/client/components/ui/calendar';
+import { CalendarIcon } from 'lucide-react';
+import { cn } from '@/client/lib/utils';
+
+<FormField
+  control={form.control}
+  name="birthDate"
+  render={({ field }) => (
+    <FormItem className="flex flex-col">
+      <FormLabel>出生日期</FormLabel>
+      <Popover>
+        <PopoverTrigger asChild>
+          <FormControl>
+            <Button
+              variant={"outline"}
+              className={cn(
+                "w-[240px] pl-3 text-left font-normal",
+                !field.value && "text-muted-foreground"
+              )}
+            >
+              {field.value ? (
+                format(field.value, "yyyy-MM-dd")
+              ) : (
+                <span>选择日期</span>
+              )}
+              <CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
+            </Button>
+          </FormControl>
+        </PopoverTrigger>
+        <PopoverContent className="w-auto p-0" align="start">
+          <Calendar
+            mode="single"
+            selected={field.value}
+            onSelect={field.onChange}
+            disabled={(date) =>
+              date > new Date() || date < new Date("1900-01-01")
+            }
+            initialFocus
+          />
+        </PopoverContent>
+      </Popover>
+      <FormMessage />
+    </FormItem>
+  )}
+/>
+```
+
+### 文本域
+```typescript
+import { Textarea } from '@/client/components/ui/textarea';
+
+<FormField
+  control={form.control}
+  name="description"
+  render={({ field }) => (
+    <FormItem>
+      <FormLabel>描述</FormLabel>
+      <FormControl>
+        <Textarea
+          placeholder="请输入描述信息"
+          className="resize-none"
+          {...field}
+        />
+      </FormControl>
+      <FormMessage />
+    </FormItem>
+  )}
+/>
+```
+
+### 复选框组
+```typescript
+import { Checkbox } from '@/client/components/ui/checkbox';
+
+<FormField
+  control={form.control}
+  name="permissions"
+  render={() => (
+    <FormItem>
+      <div className="mb-4">
+        <FormLabel className="text-base">权限设置</FormLabel>
+        <FormDescription>
+          选择该用户拥有的权限
+        </FormDescription>
+      </div>
+      <div className="space-y-2">
+        {permissions.map((permission) => (
+          <FormField
+            key={permission.id}
+            control={form.control}
+            name="permissions"
+            render={({ field }) => {
+              return (
+                <FormItem
+                  key={permission.id}
+                  className="flex flex-row items-start space-x-3 space-y-0"
+                >
+                  <FormControl>
+                    <Checkbox
+                      checked={field.value?.includes(permission.id)}
+                      onCheckedChange={(checked) => {
+                        return checked
+                          ? field.onChange([...field.value, permission.id])
+                          : field.onChange(
+                              field.value?.filter(
+                                (value) => value !== permission.id
+                              )
+                            )
+                      }}
+                    />
+                  </FormControl>
+                  <div className="space-y-1 leading-none">
+                    <FormLabel>
+                      {permission.name}
+                    </FormLabel>
+                    <FormDescription>
+                      {permission.description}
+                    </FormDescription>
+                  </div>
+                </FormItem>
+              )
+            }}
+          />
+        ))}
+      </div>
+      <FormMessage />
+    </FormItem>
+  )}
+/>
+```
+
+## 表单状态管理模板
+
+### 创建/编辑表单分离模式(推荐)
+
+基于 `src/client/admin-shadcn/pages/Users.tsx` 的最佳实践,创建/编辑表单分离模式通过以下方式解决创建/编辑时数据对象类型差异问题:
+
+#### 核心优势
+1. **类型安全**:创建使用 `CreateEntityDto`,编辑使用 `UpdateEntityDto`,避免类型冲突
+2. **字段差异处理**:创建时的必填字段在编辑时自动变为可选
+3. **敏感字段处理**:密码等敏感字段在编辑时可设为可选
+4. **状态隔离**:两种模式完全独立,避免状态污染
+
+#### 完整实现模板
+```typescript
+// 1. 类型定义(使用真实后端类型)
+type CreateRequest = InferRequestType<typeof apiClient.$post>['json'];
+type UpdateRequest = InferRequestType<typeof apiClient[':id']['$put']>['json'];
+type EntityResponse = InferResponseType<typeof apiClient.$get, 200>['data'][0];
+
+// 2. 状态管理
+const [isModalOpen, setIsModalOpen] = useState(false);
+const [editingEntity, setEditingEntity] = useState<EntityResponse | null>(null);
+const [isCreateForm, setIsCreateForm] = useState(true);
+
+// 3. 分离的表单实例
+const createForm = useForm<CreateRequest>({
+  resolver: zodResolver(CreateEntityDto),
+  defaultValues: {
+    // 创建时必须提供的默认值
+  },
+});
+
+const updateForm = useForm<UpdateRequest>({
+  resolver: zodResolver(UpdateEntityDto),
+  defaultValues: {
+    // 更新时的默认值(会被实际数据覆盖)
+  },
+});
+
+// 4. 表单操作函数
+const handleCreate = () => {
+  setEditingEntity(null);
+  setIsCreateForm(true);
+  createForm.reset({
+    // 创建时的初始值
+    status: 1, // 示例:默认启用
+  });
+  setIsModalOpen(true);
+};
+
+const handleEdit = (entity: EntityResponse) => {
+  setEditingEntity(entity);
+  setIsCreateForm(false);
+  updateForm.reset({
+    ...entity,
+    // 关键:处理创建/编辑字段差异
+    password: undefined, // 密码在更新时可选
+    confirmPassword: undefined,
+    // 其他需要特殊处理的字段
+  });
+  setIsModalOpen(true);
+};
+
+// 5. 提交处理
+const handleCreateSubmit = async (data: CreateRequest) => {
+  try {
+    const res = await apiClient.$post({ json: data });
+    if (res.status !== 201) throw new Error('创建失败');
+    toast.success('创建成功');
+    setIsModalOpen(false);
+    refetch();
+  } catch (error) {
+    toast.error('创建失败,请重试');
+  }
+};
+
+const handleUpdateSubmit = async (data: UpdateRequest) => {
+  if (!editingEntity) return;
+  
+  try {
+    const res = await apiClient[':id']['$put']({
+      param: { id: editingEntity.id },
+      json: data
+    });
+    if (res.status !== 200) throw new Error('更新失败');
+    toast.success('更新成功');
+    setIsModalOpen(false);
+    refetch();
+  } catch (error) {
+    toast.error('更新失败,请重试');
+  }
+};
+```
+
+#### 对话框渲染模板
+```tsx
+<Dialog open={isModalOpen} onOpenChange={setIsModalOpen}>
+  <DialogContent className="sm:max-w-[500px] max-h-[90vh] overflow-y-auto">
+    <DialogHeader>
+      <DialogTitle>{isCreateForm ? '创建实体' : '编辑实体'}</DialogTitle>
+      <DialogDescription>
+        {isCreateForm ? '创建一个新的实体' : '编辑现有实体信息'}
+      </DialogDescription>
+    </DialogHeader>
+    
+    {isCreateForm ? (
+      // 创建表单(独立渲染)
+      <Form {...createForm}>
+        <form onSubmit={createForm.handleSubmit(handleCreateSubmit)} className="space-y-4">
+          {/* 创建专用字段 - 必填 */}
+          <FormField
+            control={createForm.control}
+            name="username"
+            render={({ field }) => (
+              <FormItem>
+                <FormLabel className="flex items-center">
+                  用户名
+                  <span className="text-red-500 ml-1">*</span>
+                </FormLabel>
+                <FormControl>
+                  <Input placeholder="请输入用户名" {...field} />
+                </FormControl>
+                <FormMessage />
+              </FormItem>
+            )}
+          />
+          
+          {/* 创建时必填的密码 */}
+          <FormField
+            control={createForm.control}
+            name="password"
+            render={({ field }) => (
+              <FormItem>
+                <FormLabel className="flex items-center">
+                  密码
+                  <span className="text-red-500 ml-1">*</span>
+                </FormLabel>
+                <FormControl>
+                  <Input type="password" placeholder="请输入密码" {...field} />
+                </FormControl>
+                <FormMessage />
+              </FormItem>
+            )}
+          />
+          
+          <DialogFooter>
+            <Button type="button" variant="outline" onClick={() => setIsModalOpen(false)}>
+              取消
+            </Button>
+            <Button type="submit">创建</Button>
+          </DialogFooter>
+        </form>
+      </Form>
+    ) : (
+      // 编辑表单(独立渲染)
+      <Form {...updateForm}>
+        <form onSubmit={updateForm.handleSubmit(handleUpdateSubmit)} className="space-y-4">
+          {/* 编辑专用字段 - 可选 */}
+          <FormField
+            control={updateForm.control}
+            name="username"
+            render={({ field }) => (
+              <FormItem>
+                <FormLabel className="flex items-center">
+                  用户名
+                  <span className="text-red-500 ml-1">*</span>
+                </FormLabel>
+                <FormControl>
+                  <Input placeholder="请输入用户名" {...field} />
+                </FormControl>
+                <FormMessage />
+              </FormItem>
+            )}
+          />
+          
+          {/* 编辑时可选的密码 */}
+          <FormField
+            control={updateForm.control}
+            name="password"
+            render={({ field }) => (
+              <FormItem>
+                <FormLabel>新密码</FormLabel>
+                <FormControl>
+                  <Input type="password" placeholder="留空则不修改密码" {...field} />
+                </FormControl>
+                <FormMessage />
+              </FormItem>
+            )}
+          />
+          
+          <DialogFooter>
+            <Button type="button" variant="outline" onClick={() => setIsModalOpen(false)}>
+              取消
+            </Button>
+            <Button type="submit">更新</Button>
+          </DialogFooter>
+        </form>
+      </Form>
+    )}
+  </DialogContent>
+</Dialog>
+```
+
+## 最佳实践
+
+### 1. 表单验证
+- 使用 Zod Schema 进行类型验证
+- 必填字段标记红色星号
+- 提供清晰的错误提示
+
+### 2. 用户体验
+- 表单提交时显示加载状态
+- 操作成功后显示 toast 通知
+- 支持键盘导航和提交
+
+### 3. 数据管理
+- 创建后自动刷新数据列表
+- 编辑时回填现有数据
+- 支持表单重置功能
+
+### 4. 响应式设计
+- 对话框最大宽度 `sm:max-w-[500px]`
+- 表单间距统一使用 `space-y-4`
+- 移动端友好的布局
+
+### 5. 性能优化
+- 使用 React.memo 优化表单组件
+- 合理使用 useCallback 和 useMemo
+- 避免不必要的重新渲染
+
+### 6. 错误处理
+- 统一的错误处理机制
+- 友好的错误提示信息
+- 网络错误重试机制
+
+## 使用示例
+
+### 完整实现参考
+```typescript
+// 创建记录
+const handleCreateSubmit = async (data: CreateFormData) => {
+  try {
+    const res = await apiClient.$post({ json: data });
+    if (res.status !== 201) throw new Error('创建失败');
+    toast.success('创建成功');
+    setIsModalOpen(false);
+    refetch();
+  } catch (error) {
+    toast.error('创建失败,请重试');
+  }
+};
+
+// 更新记录
+const handleUpdateSubmit = async (data: UpdateFormData) => {
+  try {
+    const res = await apiClient[':id']['$put']({
+      param: { id: editingData.id },
+      json: data
+    });
+    if (res.status !== 200) throw new Error('更新失败');
+    toast.success('更新成功');
+    setIsModalOpen(false);
+    refetch();
+  } catch (error) {
+    toast.error('更新失败,请重试');
+  }
+};
+```
+
+## 组件导入清单
+```typescript
+// 表单相关
+import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from '@/client/components/ui/form';
+import { Input } from '@/client/components/ui/input';
+import { Button } from '@/client/components/ui/button';
+import { Switch } from '@/client/components/ui/switch';
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/client/components/ui/select';
+import { Textarea } from '@/client/components/ui/textarea';
+import { Checkbox } from '@/client/components/ui/checkbox';
+
+// 对话框相关
+import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from '@/client/components/ui/dialog';
+
+// 高级组件
+import { Popover, PopoverContent, PopoverTrigger } from '@/client/components/ui/popover';
+import { Calendar } from '@/client/components/ui/calendar';
+import AvatarSelector from '@/client/admin-shadcn/components/AvatarSelector';
+
+// 表单工具
+import { useForm } from 'react-hook-form';
+import { zodResolver } from '@hookform/resolvers/zod';
+import { toast } from 'sonner';
+import { format } from 'date-fns';
+import { CalendarIcon } from 'lucide-react';
+import { cn } from '@/client/lib/utils';
+```
+
+## 常见问题解决方案
+
+### 1. 表单默认值问题
+```typescript
+// 正确处理 null/undefined 值
+defaultValues: {
+  name: null, // 允许 null
+  description: undefined, // 允许 undefined
+}
+```
+
+### 2. 数字类型转换
+```typescript
+// 在 onChange 中转换类型
+onChange={(e) => field.onChange(Number(e.target.value))}
+```
+
+### 3. 日期类型处理
+```typescript
+// 日期选择器返回值处理
+onSelect={(date) => field.onChange(date ? new Date(date) : null)}
+```
+
+### 4. 数组类型处理
+```typescript
+// 复选框组处理数组
+onCheckedChange={(checked) => {
+  const newValue = checked 
+    ? [...field.value, item.id] 
+    : field.value.filter(id => id !== item.id);
+  field.onChange(newValue);
+}}
+```
+
+### 5. 表单重置注意事项
+```typescript
+// 更新表单时正确重置
+updateForm.reset({
+  ...data,
+  password: undefined, // 密码字段特殊处理
+  confirmPassword: undefined,
+});

+ 879 - 0
.claude/commands/shadcn-管理页面开发.md

@@ -0,0 +1,879 @@
+---
+description: "Shadcn-ui 管理页开发指令"
+---
+
+## 概述
+基于 `src/client/admin/pages/Users.tsx` 中用户管理页的实现,提取可复用的开发模式和最佳实践,适用于基于 Shadcn-ui 的管理后台页面开发。
+
+## 页面结构规范
+
+### 1. 文件位置
+- **管理后台页面**: `src/client/admin/pages/[EntityName].tsx`
+
+### 2. 页面组件结构
+```typescript
+// 1. 类型导入和定义
+type CreateRequest = InferRequestType<typeof client.$post>['json'];
+type UpdateRequest = InferRequestType<typeof client[':id']['$put']>['json'];
+type EntityResponse = InferResponseType<typeof client.$get, 200>['data'][0];
+
+// 2. 表单Schema直接使用后端定义
+const createFormSchema = CreateEntityDto;
+const updateFormSchema = UpdateEntityDto;
+
+// 3. 主页面组件
+export const EntityPage = () => {
+  // 状态管理
+  const [searchParams, setSearchParams] = useState({ page: 1, limit: 10, search: '' });
+  const [isModalOpen, setIsModalOpen] = useState(false);
+  const [editingEntity, setEditingEntity] = useState<any>(null);
+  const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
+  const [entityToDelete, setEntityToDelete] = useState<number | null>(null);
+  
+  // 表单实例
+  const createForm = useForm<CreateRequest>({...});
+  const updateForm = useForm<UpdateRequest>({...});
+  
+  // 数据查询
+  const { data, isLoading, refetch } = useQuery({...});
+  
+  // 业务逻辑函数
+  const handleSearch = () => {...};
+  const handleCreateEntity = () => {...};
+  const handleEditEntity = () => {...};
+  const handleDeleteEntity = () => {...};
+  
+  // 渲染
+  return (...);
+};
+```
+
+## 核心开发模式
+
+### 1. 类型驱动的开发
+- **RPC类型提取**: 使用 `InferRequestType` 和 `InferResponseType` 从后端API自动提取类型
+- **Schema复用**: 直接使用后端定义的Zod Schema作为表单验证
+- **类型安全**: 所有API调用都有完整的TypeScript类型支持
+
+### 2. 状态管理模式
+```typescript
+// 分页和搜索参数
+const [searchParams, setSearchParams] = useState({
+  page: 1,
+  limit: 10,
+  search: '',
+  // 其他筛选条件...
+});
+
+// 模态框状态
+const [isModalOpen, setIsModalOpen] = useState(false);
+const [editingEntity, setEditingEntity] = useState<any>(null);
+const [isCreateForm, setIsCreateForm] = useState(true);
+```
+
+### 3. 数据获取模式
+```typescript
+const { data, isLoading, refetch } = useQuery({
+  queryKey: ['entities', searchParams],
+  queryFn: async () => {
+    const res = await entityClient.$get({
+      query: {
+        page: searchParams.page,
+        pageSize: searchParams.limit,
+        keyword: searchParams.search,
+        // 其他查询参数...
+      }
+    });
+    if (res.status !== 200) throw new Error('获取列表失败');
+    return await res.json();
+  }
+});
+```
+
+## 页面布局规范
+
+### 1. 页面标题区域
+```tsx
+<div className="flex justify-between items-center">
+  <h1 className="text-2xl font-bold">页面标题</h1>
+  <Button onClick={handleCreateEntity}>
+    <Plus className="mr-2 h-4 w-4" />
+    创建实体
+  </Button>
+</div>
+```
+
+### 2. 搜索区域
+```tsx
+<Card>
+  <CardHeader>
+    <CardTitle>列表标题</CardTitle>
+    <CardDescription>列表描述信息</CardDescription>
+  </CardHeader>
+  <CardContent>
+    <div className="mb-4">
+      <form onSubmit={handleSearch} className="flex gap-2">
+        <div className="relative flex-1 max-w-sm">
+          <Search className="absolute left-2 top-2.5 h-4 w-4 text-muted-foreground" />
+          <Input
+            placeholder="搜索提示..."
+            value={searchParams.search}
+            onChange={(e) => setSearchParams(prev => ({ ...prev, search: e.target.value }))}
+            className="pl-8"
+          />
+        </div>
+        <Button type="submit" variant="outline">
+          搜索
+        </Button>
+      </form>
+    </div>
+  </CardContent>
+</Card>
+```
+
+### 3. 数据表格
+```tsx
+<div className="rounded-md border">
+  <Table>
+    <TableHeader>
+      <TableRow>
+        <TableHead>列标题1</TableHead>
+        <TableHead>列标题2</TableHead>
+        <TableHead className="text-right">操作</TableHead>
+      </TableRow>
+    </TableHeader>
+    <TableBody>
+      {data.map((item) => (
+        <TableRow key={item.id}>
+          <TableCell>{item.field1}</TableCell>
+          <TableCell>{item.field2}</TableCell>
+          <TableCell className="text-right">
+            <div className="flex justify-end gap-2">
+              <Button variant="ghost" size="icon" onClick={() => handleEdit(item)}>
+                <Edit className="h-4 w-4" />
+              </Button>
+              <Button variant="ghost" size="icon" onClick={() => handleDelete(item.id)}>
+                <Trash2 className="h-4 w-4" />
+              </Button>
+            </div>
+          </TableCell>
+        </TableRow>
+      ))}
+    </TableBody>
+  </Table>
+</div>
+
+{data?.data.length === 0 && !isLoading && (
+  <div className="text-center py-8">
+    <p className="text-muted-foreground">暂无数据</p>
+  </div>
+)}
+
+<DataTablePagination
+  currentPage={searchParams.page}
+  pageSize={searchParams.limit}
+  totalCount={data?.pagination.total || 0}
+  onPageChange={(page, limit) => setSearchParams(prev => ({ ...prev, page, limit }))}
+/>
+```
+
+## 表单开发模式
+
+### 1. 表单组件结构
+```typescript
+// 创建表单
+const createForm = useForm<CreateRequest>({
+  resolver: zodResolver(createFormSchema),
+  defaultValues: {
+    // 默认值设置
+  },
+});
+
+// 更新表单
+const updateForm = useForm<UpdateRequest>({
+  resolver: zodResolver(updateFormSchema),
+  defaultValues: {
+    // 更新时默认值
+  },
+});
+```
+
+### 2. 模态框表单(创建/编辑分离模式)
+
+将 创建/编辑表单分离
+不要 <Form {...(isCreateForm ? createForm : updateForm)}></Form>
+要     
+{isCreateForm ? (
+  // 创建表单(独立渲染)
+  <Form {...createForm}>
+  </Form>
+) : (
+  // 编辑表单(独立渲染)
+  <Form {...updateForm}>
+  </Form>
+)}
+
+```tsx
+<Dialog open={isModalOpen} onOpenChange={setIsModalOpen}>
+  <DialogContent className="sm:max-w-[500px] max-h-[90vh] overflow-y-auto">
+    <DialogHeader>
+      <DialogTitle>{isCreateForm ? '创建实体' : '编辑实体'}</DialogTitle>
+      <DialogDescription>
+        {isCreateForm ? '创建一个新的实体' : '编辑现有实体信息'}
+      </DialogDescription>
+    </DialogHeader>
+    
+    {isCreateForm ? (
+      // 创建表单(独立渲染)
+      <Form {...createForm}>
+        <form onSubmit={createForm.handleSubmit(handleCreateSubmit)} className="space-y-4">
+          {/* 创建专用字段 */}
+          <DialogFooter>
+            <Button type="button" variant="outline" onClick={() => setIsModalOpen(false)}>
+              取消
+            </Button>
+            <Button type="submit">创建</Button>
+          </DialogFooter>
+        </form>
+      </Form>
+    ) : (
+      // 编辑表单(独立渲染)
+      <Form {...updateForm}>
+        <form onSubmit={updateForm.handleSubmit(handleUpdateSubmit)} className="space-y-4">
+          {/* 编辑专用字段 */}
+          <DialogFooter>
+            <Button type="button" variant="outline" onClick={() => setIsModalOpen(false)}>
+              取消
+            </Button>
+            <Button type="submit">更新</Button>
+          </DialogFooter>
+        </form>
+      </Form>
+    )}
+  </DialogContent>
+</Dialog>
+```
+
+### 3. 表单字段模式
+```tsx
+<FormField
+  control={form.control}
+  name="fieldName"
+  render={({ field }) => (
+    <FormItem>
+      <FormLabel className="flex items-center">
+        字段标签
+        {isRequired && <span className="text-red-500 ml-1">*</span>}
+      </FormLabel>
+      <FormControl>
+        <Input placeholder="请输入..." {...field} />
+      </FormControl>
+      <FormDescription>字段描述信息</FormDescription>
+      <FormMessage />
+    </FormItem>
+  )}
+/>
+```
+
+## 图片上传集成
+
+### 1. ImageSelector组件使用
+```tsx
+import { ImageSelector } from '@/client/admin/components/ImageSelector';
+
+<FormField
+  control={form.control}
+  name="avatarFileId"
+  render={({ field }) => (
+    <FormItem>
+      <FormLabel>头像</FormLabel>
+      <FormControl>
+        <ImageSelector
+          value={field.value || undefined}
+          onChange={(value) => field.onChange(value)}
+          maxSize={2} // MB
+          uploadPath="/avatars"
+          uploadButtonText="上传头像"
+          previewSize="medium"
+          placeholder="选择头像"
+        />
+      </FormControl>
+      <FormMessage />
+    </FormItem>
+  )}
+/>
+```
+
+## 文件上传集成
+
+### 1. FileSelector组件使用
+```tsx
+import { FileSelector } from '@/client/admin/components/FileSelector';
+
+<FormField
+  control={form.control}
+  name="avatarFileId"
+  render={({ field }) => (
+    <FormItem>
+      <FormLabel>头像</FormLabel>
+      <FormControl>
+        <FileSelector
+          value={field.value || undefined}
+          onChange={(value) => field.onChange(value)}
+          maxSize={2} // MB
+          uploadPath="/avatars"
+          uploadButtonText="上传头像"
+          previewSize="medium"
+          placeholder="选择头像"
+        />
+      </FormControl>
+      <FormMessage />
+    </FormItem>
+  )}
+/>
+```
+
+## 状态管理最佳实践
+
+### 1. 状态提升策略
+- **表单状态**: 使用React Hook Form管理
+- **UI状态**: 使用useState管理模态框、加载状态等
+- **服务器状态**: 使用React Query管理数据获取和缓存
+
+### 2. 数据刷新策略
+```typescript
+// 操作成功后刷新数据
+const handleCreateSubmit = async (data: CreateRequest) => {
+  try {
+    const res = await entityClient.$post({ json: data });
+    if (res.status !== 201) throw new Error('创建失败');
+    toast.success('创建成功');
+    setIsModalOpen(false);
+    refetch(); // 刷新数据
+  } catch (error) {
+    toast.error('操作失败,请重试');
+  }
+};
+```
+
+## 加载状态处理
+
+### 1. 骨架屏模式
+
+#### 1.1 导入依赖
+```typescript
+import { Skeleton } from '@/client/components/ui/skeleton';
+```
+
+#### 1.2 完整骨架屏实现
+```tsx
+if (isLoading) {
+  return (
+    <div className="space-y-4">
+      {/* 标题区域骨架 */}
+      <div className="flex justify-between items-center">
+        <Skeleton className="h-8 w-48" />
+        <Skeleton className="h-10 w-32" />
+      </div>
+      
+      {/* 搜索区域骨架 */}
+      <Card>
+        <CardHeader>
+          <Skeleton className="h-6 w-1/4" />
+        </CardHeader>
+        <CardContent>
+          <Skeleton className="h-10 w-full max-w-sm" />
+        </CardContent>
+      </Card>
+      
+      {/* 表格骨架 */}
+      <Card>
+        <CardHeader>
+          <Skeleton className="h-6 w-1/3" />
+        </CardHeader>
+        <CardContent>
+          <Table>
+            <TableHeader>
+              <TableRow>
+                {[...Array(5)].map((_, i) => (
+                  <TableHead key={i}>
+                    <Skeleton className="h-4 w-full" />
+                  </TableHead>
+                ))}
+              </TableRow>
+            </TableHeader>
+            <TableBody>
+              {[...Array(5)].map((_, i) => (
+                <TableRow key={i}>
+                  {[...Array(5)].map((_, j) => (
+                    <TableCell key={j}>
+                      <Skeleton className="h-4 w-full" />
+                    </TableCell>
+                  ))}
+                </TableRow>
+              ))}
+            </TableBody>
+          </Table>
+        </CardContent>
+      </Card>
+    </div>
+  );
+}
+```
+
+#### 1.3 简化骨架屏(推荐)
+```tsx
+if (isLoading) {
+  return (
+    <div className="space-y-4">
+      <div className="flex justify-between items-center">
+        <Skeleton className="h-8 w-48" />
+        <Skeleton className="h-10 w-32" />
+      </div>
+      
+      <Card>
+        <CardContent className="pt-6">
+          <div className="space-y-3">
+            {[...Array(5)].map((_, i) => (
+              <div key={i} className="flex gap-4">
+                <Skeleton className="h-10 flex-1" />
+                <Skeleton className="h-10 flex-1" />
+                <Skeleton className="h-10 flex-1" />
+                <Skeleton className="h-10 w-20" />
+              </div>
+            ))}
+          </div>
+        </CardContent>
+      </Card>
+    </div>
+  );
+}
+```
+
+### 2. 空数据状态
+```tsx
+{users.length === 0 && !isLoading && (
+  <div className="text-center py-8">
+    <p className="text-muted-foreground">暂无数据</p>
+  </div>
+)}
+```
+
+## 错误处理模式
+
+### 1. API错误处理
+```typescript
+try {
+  const res = await entityClient.$post({ json: data });
+  if (res.status !== 201) throw new Error('操作失败');
+  toast.success('操作成功');
+} catch (error) {
+  console.error('操作失败:', error);
+  toast.error('操作失败,请重试');
+}
+```
+
+### 2. 删除确认模式
+```tsx
+const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
+const [entityToDelete, setEntityToDelete] = useState<number | null>(null);
+
+// 删除确认对话框
+<Dialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
+  <DialogContent>
+    <DialogHeader>
+      <DialogTitle>确认删除</DialogTitle>
+      <DialogDescription>
+        确定要删除这个实体吗?此操作无法撤销。
+      </DialogDescription>
+    </DialogHeader>
+    <DialogFooter>
+      <Button variant="outline" onClick={() => setDeleteDialogOpen(false)}>
+        取消
+      </Button>
+      <Button variant="destructive" onClick={confirmDelete}>
+        删除
+      </Button>
+    </DialogFooter>
+  </DialogContent>
+</Dialog>
+
+// 删除成功状态码为204
+const confirmDelete = async () => {
+  if (!entityToDelete) return;
+  
+  try {
+    const res = await entityClient[':id']['$delete']({
+      param: { id: entityToDelete.toString() }
+    });
+    
+    if (res.status === 204) {
+      toast.success('删除成功');
+      setDeleteDialogOpen(false);
+      refetch(); // 刷新数据
+    } else {
+      throw new Error('删除失败');
+    }
+  } catch (error) {
+    toast.error('删除失败,请重试');
+  }
+};
+```
+
+## 样式规范
+
+### 1. 间距系统
+- 页面标题区域: `space-y-4`
+- 卡片内容: `space-y-4`
+- 表单字段: `space-y-4`
+- 按钮组: `gap-2`
+
+### 2. 响应式设计
+- 模态框最大宽度: `sm:max-w-[500px]`
+- 模态框最大高度: `max-h-[90vh]`
+- 搜索输入框: `max-w-sm`
+
+### 3. 视觉层次
+- 标题: `text-2xl font-bold`
+- 卡片标题: `text-lg font-semibold`
+- 描述文字: `text-sm text-muted-foreground`
+
+## 开发流程
+
+### 1. 创建新管理页面
+1. 复制 `Users.tsx` 作为模板
+2. 替换以下部分:
+   - API客户端导入
+   - 类型定义
+   - 表单Schema引用
+   - 页面标题和描述
+   - 表格列定义
+   - 表单字段定义
+3. 根据业务需求调整字段和逻辑
+
+### 2. 字段映射规范
+- **文本字段**: 使用 `Input`
+- **长文本**: 使用 `Textarea`
+- **选择字段**: 使用 `Select`
+- **开关字段**: 使用 `Switch`
+- **日期字段**: 使用 `DatePicker`
+- **图片字段**: 使用 `ImageSelector`
+
+### 3. 业务逻辑复用
+- 保持相同的CRUD操作模式
+- 复用分页、搜索、排序逻辑
+- 统一的状态管理模式
+- 一致的表单验证和错误处理
+
+## 高级组件集成
+
+### 1. DataTablePagination 分页组件
+
+#### 1.1 使用方式
+```tsx
+import { DataTablePagination } from '@/client/admin/components/DataTablePagination';
+
+<DataTablePagination
+  currentPage={searchParams.page}
+  pageSize={searchParams.limit}
+  totalCount={data?.pagination.total || 0}
+  onPageChange={(page, limit) => setSearchParams(prev => ({ ...prev, page, limit }))}
+/>
+```
+
+#### 1.2 参数说明
+| 参数 | 类型 | 描述 |
+|------|------|------|
+| currentPage | number | 当前页码 |
+| pageSize | number | 每页显示条数 |
+| totalCount | number | 总记录数 |
+| onPageChange | function | 页码变化回调函数 |
+
+#### 1.3 集成到状态管理
+```typescript
+const [searchParams, setSearchParams] = useState({
+  page: 1,
+  limit: 10,
+  search: ''
+});
+
+// 在数据查询中
+const { data } = useQuery({
+  queryKey: ['entities', searchParams],
+  queryFn: async () => {
+    const res = await client.$get({
+      query: {
+        page: searchParams.page,
+        pageSize: searchParams.limit,
+        keyword: searchParams.search
+      }
+    });
+    return await res.json();
+  }
+});
+```
+
+### 2. 关联实体Selector组件
+
+#### 2.1 AdvertisementTypeSelector 示例
+```tsx
+import AdvertisementTypeSelector from '@/client/admin/components/AdvertisementTypeSelector';
+
+<FormField
+  control={form.control}
+  name="typeId"
+  render={({ field }) => (
+    <FormItem>
+      <FormLabel>广告类型</FormLabel>
+      <FormControl>
+        <AdvertisementTypeSelector
+          value={field.value}
+          onChange={field.onChange}
+          placeholder="请选择广告类型"
+        />
+      </FormControl>
+      <FormMessage />
+    </FormItem>
+  )}
+/>
+```
+
+#### 2.2 自定义Selector开发模式
+```typescript
+// 通用Selector接口设计
+interface EntitySelectorProps {
+  value?: number;
+  onChange?: (value: number) => void;
+  placeholder?: string;
+  disabled?: boolean;
+}
+
+// 实现模式
+const EntitySelector: React.FC<EntitySelectorProps> = ({
+  value,
+  onChange,
+  placeholder = "请选择",
+  disabled
+}) => {
+  const { data } = useQuery({
+    queryKey: ['entities'],
+    queryFn: async () => {
+      const res = await entityClient.$get();
+      return await res.json();
+    }
+  });
+
+  return (
+    <Select value={value?.toString()} onValueChange={(v) => onChange?.(parseInt(v))}>
+      <SelectTrigger disabled={disabled}>
+        <SelectValue placeholder={placeholder} />
+      </SelectTrigger>
+      <SelectContent>
+        {data?.data.map((item) => (
+          <SelectItem key={item.id} value={item.id.toString()}>
+            {item.name}
+          </SelectItem>
+        ))}
+      </SelectContent>
+    </Select>
+  );
+};
+```
+
+#### 2.3 图片选择器集成
+```tsx
+import ImageSelector from '@/client/admin/components/ImageSelector';
+
+<FormField
+  control={form.control}
+  name="imageFileId"
+  render={({ field }) => (
+    <FormItem>
+      <FormLabel>广告图片</FormLabel>
+      <FormControl>
+        <ImageSelector
+          value={field.value || undefined}
+          onChange={field.onChange}
+          maxSize={2} // MB
+          uploadPath="/advertisements"
+          uploadButtonText="上传广告图片"
+          previewSize="medium"
+          placeholder="选择广告图片"
+          title="选择广告图片"
+          description="上传新图片或从已有图片中选择"
+        />
+      </FormControl>
+      <FormDescription>推荐尺寸:1200x400px,支持jpg、png格式</FormDescription>
+      <FormMessage />
+    </FormItem>
+  )}
+/>
+```
+
+### 3. 复杂字段展示模式
+
+#### 3.1 关联实体字段展示
+```tsx
+<TableCell>
+  {advertisement.advertisementType?.name || '-'}
+</TableCell>
+```
+
+#### 3.2 状态字段展示
+```tsx
+<TableCell>
+  <Badge variant={advertisement.status === 1 ? 'default' : 'secondary'}>
+    {advertisement.status === 1 ? '启用' : '禁用'}
+  </Badge>
+</TableCell>
+```
+
+#### 3.3 图片字段展示
+```tsx
+<TableCell>
+  {advertisement.imageFile?.fullUrl ? (
+    <img
+      src={advertisement.imageFile.fullUrl}
+      alt={advertisement.title || '图片'}
+      className="w-16 h-10 object-cover rounded"
+      onError={(e) => {
+        e.currentTarget.src = '/placeholder.png';
+      }}
+    />
+  ) : (
+    <span className="text-muted-foreground text-xs">无图片</span>
+  )}
+</TableCell>
+```
+
+### 4. 表单字段类型映射
+
+#### 4.1 标准字段映射
+| 字段类型 | 组件 | 示例 |
+|----------|------|------|
+| 文本输入 | Input | `<Input placeholder="请输入标题" {...field} />` |
+| 长文本 | Textarea | `<Textarea placeholder="请输入描述" {...field} />` |
+| 选择器 | Select | `<Select value={field.value} onValueChange={field.onChange}>` |
+| 数字输入 | Input | `<Input type="number" {...field} />` |
+| 日期选择 | DatePicker | `<DatePicker selected={field.value} onChange={field.onChange} />` |
+| 开关 | Switch | `<Switch checked={field.value} onCheckedChange={field.onChange} />` |
+| 文件上传 | ImageSelector | `<ImageSelector value={field.value} onChange={field.onChange} />` |
+
+#### 4.2 关联实体选择
+```tsx
+// 直接使用Selector组件
+<FormField
+  control={form.control}
+  name="typeId"
+  render={({ field }) => (
+    <FormItem>
+      <FormLabel>广告类型</FormLabel>
+      <FormControl>
+        <AdvertisementTypeSelector {...field} />
+      </FormControl>
+    </FormItem>
+  )}
+/>
+```
+
+### 4.3 日期格式化规范
+
+#### 4.3.1 导入依赖
+```typescript
+import { format } from 'date-fns';
+```
+
+#### 4.3.2 日期显示格式
+```tsx
+// 标准日期时间格式:yyyy-MM-dd HH:mm
+<TableCell>
+  {user.createdAt ? format(new Date(user.createdAt), 'yyyy-MM-dd HH:mm') : '-'}
+</TableCell>
+
+// 仅日期格式:yyyy-MM-dd
+<TableCell>
+  {user.birthday ? format(new Date(user.birthday), 'yyyy-MM-dd') : '-'}
+</TableCell>
+
+// 完整时间格式:yyyy-MM-dd HH:mm:ss
+<TableCell>
+  {user.updatedAt ? format(new Date(user.updatedAt), 'yyyy-MM-dd HH:mm:ss') : '-'}
+</TableCell>
+```
+
+#### 4.3.3 日期输入格式
+```tsx
+// 在表单中使用日期选择器
+<FormField
+  control={form.control}
+  name="startDate"
+  render={({ field }) => (
+    <FormItem>
+      <FormLabel>开始日期</FormLabel>
+      <FormControl>
+        <Input
+          type="date"
+          {...field}
+          value={field.value ? format(new Date(field.value), 'yyyy-MM-dd') : ''}
+          onChange={(e) => field.onChange(e.target.value)}
+        />
+      </FormControl>
+    </FormItem>
+  )}
+/>
+```
+
+#### 4.3.4 相对时间显示(可选)
+```typescript
+import { formatDistanceToNow } from 'date-fns';
+import { zhCN } from 'date-fns/locale';
+
+// 相对时间显示
+<TableCell>
+  {user.createdAt ? formatDistanceToNow(new Date(user.createdAt), { addSuffix: true, locale: zhCN }) : '-'}
+</TableCell>
+```
+
+### 4.4 消息通知规范
+
+#### 4.4.1 导入依赖
+```typescript
+import { toast } from 'sonner';
+```
+
+#### 4.4.2 使用规范
+```typescript
+// 成功通知
+toast.success('操作成功');
+toast.success('用户创建成功');
+
+// 错误通知
+toast.error('操作失败');
+toast.error('创建用户失败,请重试');
+
+// 警告通知
+toast.warning('请确认操作');
+toast.warning('该操作将删除所有相关数据');
+
+// 信息通知
+toast.info('操作提示');
+toast.info('正在处理中,请稍候...');
+```
+
+#### 4.4.3 与API响应集成
+```typescript
+try {
+  const res = await entityClient.$post({ json: data });
+  if (res.status === 201) {
+    toast.success('创建成功');
+    setIsModalOpen(false);
+    refetch();
+  } else {
+    const error = await res.json();
+    toast.error(error.message || '操作失败');
+  }
+} catch (error) {
+  console.error('操作失败:', error);
+  toast.error('网络错误,请重试');
+}
+```

+ 5 - 0
.claude/commands/shadcn.md

@@ -0,0 +1,5 @@
+---
+description: "使用shadcn创建页面及组件"
+---
+
+shadcn配置在 components.json

+ 86 - 0
.claude/commands/stock-api-修改为新股票api接口.md

@@ -0,0 +1,86 @@
+---
+description: "修改为新股票api接口"
+---
+
+最新分时交易
+API接口:https://api.mairuiapi.com/hsstock/latest/股票代码.市场(如000001.SZ)/分时级别(如d)/除权方式/您的licence?lt=最新条数(如5)
+演示URL:https://api.mairuiapi.com/hsstock/latest/000001.SZ/d/n/LICENCE-66D8-9F96-0C7F0FBCD073?lt=1
+接口说明:根据《股票列表》得到的股票代码和分时级别获取最新交易数据,交易时间升序。目前分时级别支持5分钟、15分钟、30分钟、60分钟、日线、周线、月线、年线,对应的请求参数分别为5、15、30、60、d、w、m、y,日线以上除权方式有不复权、前复权、后复权、等比前复权、等比后复权,对应的参数分别为n、f、b、fr、br,分钟级无除权数据,对应的参数为n。同时可以指定获取数据条数,例如指定lt=10,则获取最新的10条数据。
+数据更新:实时
+请求频率:1分钟300次 | 包年版1分钟3千次 | 钻石版1分钟6千次
+返回格式:标准Json格式      [{},...{}]
+字段名称	数据类型	字段说明
+t	string	交易时间
+o	float	开盘价
+h	float	最高价
+l	float	最低价
+c	float	收盘价
+v	float	成交量
+a	float	成交额
+pc	float	前收盘价
+sf	int	停牌 1停牌,0 不停牌
+
+演示URL:返回结果实例
+```json
+[{"t":"2025-08-20 00:00:00","o":12.06,"h":12.12,"l":11.98,"c":12.07,"v":1048739.0,"a":1265435773.22,"pc":12.06,"sf":0.0}]
+```
+
+历史分时交易
+API接口:https://api.mairuiapi.com/hsstock/history/股票代码.市场(如000001.SZ)/分时级别(如d)/除权方式/您的licence?st=开始时间(如20240601)&et=结束时间(如20250430)&lt=最新条数(如100)
+演示URL:https://api.mairuiapi.com/hsstock/history/000001.SZ/d/n/LICENCE-66D8-9F96-0C7F0FBCD073?st=20250101&et=20250430&lt=100
+接口说明:根据《股票列表》得到的股票代码和分时级别获取历史交易数据,交易时间升序。目前分时级别支持5分钟、15分钟、30分钟、60分钟、日线、周线、月线、年线,对应的请求参数分别为5、15、30、60、d、w、m、y,日线以上除权方式有不复权、前复权、后复权、等比前复权、等比后复权,对应的参数分别为n、f、b、fr、br,分钟级无除权数据,对应的参数为n。开始时间以及结束时间的格式均为 YYYYMMDD 或 YYYYMMDDhhmmss,例如:'20240101' 或'20241231235959'。不设置开始时间和结束时间则为全部历史数据。同时可以指定获取数据条数,例如指定lt=10,则获取最新的10条数据。
+数据更新:分钟级别数据盘中更新,分时越小越优先更新,如5分钟级别会每5分钟更新,15分钟级别会每15分钟更新,以此类推,日线及以上级别每日15:30开始更新,预计17:10完成
+请求频率:1分钟300次 | 包年版1分钟3千次 | 钻石版1分钟6千次
+返回格式:标准Json格式      [{},...{}]
+字段名称	数据类型	字段说明
+t	string	交易时间
+o	float	开盘价
+h	float	最高价
+l	float	最低价
+c	float	收盘价
+v	float	成交量
+a	float	成交额
+pc	float	前收盘价
+sf	int	停牌 1停牌,0 不停牌
+
+演示URL:返回结果实例
+```json
+[{"t":"2025-01-02 00:00:00","o":11.73,"h":11.77,"l":11.39,"c":11.43,"v":1819597.0,"a":2102923078.0,"pc":11.7,"sf":0.0},{"t":"2025-01-03 00:00:00","o":11.44,"h":11.54,"l":11.36,"c":11.38,"v":1154680.0,"a":1320520977.0,"pc":11.43,"sf":0.0},{"t":"2025-01-06 00:00:00","o":11.38,"h":11.48,"l":11.22,"c":11.44,"v":1085536.0,"a":1234305778.0,"pc":11.38,"sf":0.0},{"t":"2025-01-07 00:00:00","o":11.42,"h":11.53,"l":11.37,"c":11.51,"v":747863.0,"a":858329048.0,"pc":11.44,"sf":0.0}]
+```
+
+
+历史涨跌停价格
+API接口:http://api.mairuiapi.com/hsstock/stopprice/history/股票代码(如000001.SZ)/您的licence?st=开始时间&et=结束时间
+演示URL:http://api.mairuiapi.com/hsstock/stopprice/history/000001.SZ/LICENCE-66D8-9F96-0C7F0FBCD073?st=20240501&et=20240601
+接口说明:根据《股票列表》得到的股票代码获取历史涨跌停价格,开始时间以及结束时间的格式均为 YYYYMMDD,例如:'20240101'。不设置开始时间和结束时间则为全部历史数据。
+数据更新:每日0点
+请求频率:1分钟300次 | 包年版1分钟3千次 | 钻石版1分钟6千次
+返回格式:标准Json格式      [{},...{}]
+字段名称	数据类型	字段说明
+t	string	交易日期
+h	float	涨停价格
+l	float	跌停价格
+行情指标
+API接口:http://api.mairuiapi.com/hsstock/indicators/股票代码(如000001.SZ)/您的licence?st=开始时间&et=结束时间
+演示URL:http://api.mairuiapi.com/hsstock/indicators/600519.SH/LICENCE-66D8-9F96-0C7F0FBCD073
+接口说明:根据《股票列表》得到的股票代码获取各项行情指标,开始时间以及结束时间的格式均为 YYYYMMDD,例如:'20240101'。不设置开始时间和结束时间则为全部数据。
+数据更新:实时
+请求频率:1分钟300次 | 包年版1分钟3千次 | 钻石版1分钟6千次
+返回格式:标准Json格式      [{},...{}]
+字段名称	数据类型	字段说明
+time	string	更新时间
+lb	float	量比
+om	float	1分钟涨速(%)
+fm	float	5分钟涨速(%)
+3d	float	3日涨幅(%)
+5d	float	5日涨幅(%)
+10d	float	10日涨幅(%)
+3t	float	3日换手(%)
+5t	float	5日换手(%)
+10t	float	10日换手(%)
+
+
+演示URL:返回结果实例
+```json
+[{"t":"2025-08-19","h":13.29,"l":10.87},{"t":"2025-08-18","h":13.29,"l":10.87},{"t":"2025-08-15","h":13.42,"l":10.98},{"t":"2025-08-14","h":13.49,"l":11.03},{"t":"2025-08-13","h":13.56,"l":11.1},{"t":"2025-08-12","h":13.53,"l":11.07},{"t":"2025-08-11","h":13.64,"l":11.16},{"t":"2025-08-08","h":13.72,"l":11.22},{"t":"2025-08-07","h":13.71,"l":11.21}]
+```

+ 213 - 0
.claude/commands/user-实体用户关联开发.md

@@ -0,0 +1,213 @@
+---
+description: "实体现有字段与用户实体关联开发指令"
+---
+
+
+## 适用场景
+
+当实体中已经存在用户相关的字段(如 `handlerId`, `createdBy`, `userId`, `operatorId` 等),需要将这些字段与 `User` 实体建立关联关系时使用。
+
+## 开发步骤
+
+### 1. 修改实体定义
+
+找到实体文件 `src/server/modules/[模块名]/[实体名].entity.ts`,将现有用户字段改为关联关系:
+
+#### 1.1 替换字段类型和装饰器
+
+```typescript
+// 修改前:原始字段定义
+@Column({ name: 'handler_id', type: 'int', comment: '处理人ID' })
+handlerId!: number;
+
+// 修改后:单向关联关系定义
+@ManyToOne(() => User)
+@JoinColumn({ name: 'handler_id' })
+handler!: User;
+
+// 如果允许为空(单向关联)
+@ManyToOne(() => User, { nullable: true })
+@JoinColumn({ name: 'handler_id' })
+handler?: User | null;
+```
+
+#### 1.2 添加User实体导入
+
+```typescript
+// 在实体顶部添加
+import { User } from '@/server/modules/users/user.entity';
+```
+
+#### 1.3 在User实体中添加反向关联(可选)
+
+如果需要双向关联,在User实体中添加对应的关系定义:
+
+```typescript
+// 在User实体中添加对应的关系定义(双向关联)
+@OneToMany(() => AlertHandleLog, log => log.handler)
+alertHandleLogs?: AlertHandleLog[];
+```
+
+> **注意**:单向关联不需要此步骤,保持现有配置即可
+
+### 2. 更新Zod Schema
+
+在对应的 schema 文件中同步修改:
+
+#### 2.1 修改实体Schema
+
+```typescript
+// 在文件顶部添加导入
+import { UserSchema } from '@/server/modules/users/user.schema';
+
+// 修改前
+handlerId: z.number().int().positive().openapi({
+  description: '处理人ID',
+  example: 1
+})
+
+// 修改后 - 使用导入的UserSchema
+handler: UserSchema.omit({ password: true }).nullable().optional().openapi({
+  description: '处理人信息'
+})
+```
+
+#### 2.2 修改Create/Update DTO
+
+```typescript
+// 修改前:Create DTO
+export const CreateAlertHandleLogDto = z.object({
+  handlerId: z.number().int().positive().openapi({
+    description: '处理人ID',
+    example: 1
+  }),
+  // ... 其他字段
+});
+
+// 修改后:Create DTO
+export const CreateAlertHandleLogDto = z.object({
+  handlerId: z.number().int().positive().openapi({
+    description: '处理人ID',
+    example: 1
+  }),
+  // ... 其他字段
+});
+
+// Update DTO保持不变,但handlerId改为optional
+export const UpdateAlertHandleLogDto = z.object({
+  handlerId: z.number().int().positive().optional().openapi({
+    description: '处理人ID',
+    example: 1
+  }),
+  // ... 其他字段
+});
+```
+
+### 4. 更新通用CRUD配置
+
+如果使用通用CRUD路由,需要配置relations:
+
+```typescript
+const routes = createCrudRoutes({
+  entity: AlertHandleLog,
+  createSchema: CreateAlertHandleLogDto,
+  updateSchema: UpdateAlertHandleLogDto,
+  getSchema: AlertHandleLogSchema,
+  listSchema: AlertHandleLogSchema,
+  relations: ['handler'], // 添加关联配置
+  middleware: [authMiddleware]
+});
+```
+
+## 命名规范
+
+| 类型 | 命名格式 | 示例 | 说明 |
+|------|----------|------|------|
+| 数据库字段 | `{前缀}_id` | `handler_id` | 保持原有字段名不变 |
+| 实体字段 | `{前缀}` | `handler` | 关联实体对象 |
+| 反向关联 | `{实体名}s` | `alertHandleLogs` | User实体中的集合名 |
+| 外键字段 | `{前缀}Id` | `handlerId` | DTO中的外键字段 |
+
+## 完整示例
+
+### 实体类 ([`src/server/modules/alerts/alert-handle-log.entity.ts`](src/server/modules/alerts/alert-handle-log.entity.ts))
+
+```typescript
+import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, JoinColumn } from 'typeorm';
+import { User } from '@/server/modules/users/user.entity';
+
+@Entity('alert_handle_logs')
+export class AlertHandleLog {
+  @PrimaryGeneratedColumn({ unsigned: true })
+  id!: number;
+
+  @Column({ name: 'handler_id', type: 'int', comment: '处理人ID' })
+  handlerId!: number;
+
+  // 单向关联定义(无需反向引用)
+  @ManyToOne(() => User)
+  @JoinColumn({ name: 'handler_id' })
+  handler!: User;
+
+  // ... 其他字段
+}
+```
+
+### User实体反向关联(可选,仅双向关联需要)
+
+如果需要双向关联,在User实体中添加:
+
+```typescript
+@OneToMany(() => AlertHandleLog, log => log.handler)
+alertHandleLogs?: AlertHandleLog[];
+```
+
+> **单向关联说明**:当前配置为单向关联,无需在User实体中添加反向关系定义
+
+### Schema定义 ([`src/server/modules/alerts/alert-handle-log.schema.ts`](src/server/modules/alerts/alert-handle-log.schema.ts))
+
+```typescript
+// 引入用户Schema
+import { UserSchema } from '@/server/modules/users/user.schema';
+
+// 响应Schema - 使用UserSchema.omit去掉敏感字段
+handler: UserSchema.omit({ password: true }).nullable().optional().openapi({
+  description: '处理人信息'
+}),
+
+// 请求DTO中的外键字段
+handlerId: z.number().int().positive().openapi({
+  description: '处理人ID',
+  example: 1
+})
+```
+
+## 注意事项
+
+1. **数据库兼容性**:现有数据库字段保持不变,只是从整数字段升级为外键约束
+2. **API兼容性**:DTO中的外键字段名保持不变(如handlerId),确保API接口兼容
+3. **查询性能**:配置relations后,关联查询会自动优化,避免N+1问题
+4. **级联操作**:默认不级联删除,删除用户不会影响关联记录
+5. **空值处理**:如果handlerId为null,handler关联对象将为null
+
+## 常见问题
+
+### Q: 如何支持多个用户关联字段?
+A: 重复上述步骤,为每个用户字段建立独立的关联关系,例如:
+```typescript
+@ManyToOne(() => User, user => user.createdAlerts)
+@JoinColumn({ name: 'created_by' })
+creator!: User;
+
+@ManyToOne(() => User, user => user.updatedAlerts)
+@JoinColumn({ name: 'updated_by' })
+updater?: User | null;
+```
+
+### Q: 如何处理复合用户关联?
+A: 使用相同的方法,只是字段名不同:
+```typescript
+@ManyToOne(() => User, user => user.assignedTasks)
+@JoinColumn({ name: 'assignee_id' })
+assignee!: User;
+```

+ 543 - 0
.claude/commands/wechat-auth-微信服务号网页授权登录开发.md

@@ -0,0 +1,543 @@
+---
+description: "微信服务号网页授权登录开发指令"
+---
+
+本指令指导如何实现微信服务号网页授权登录功能,包括后端API开发、前端组件集成和移动端适配。
+
+## 前置要求
+
+1. 已注册微信开放平台服务号
+2. 已配置网页授权域名
+3. 获取AppID和AppSecret
+
+## 开发步骤
+
+### 1. 用户实体字段添加
+
+在用户实体中添加微信相关字段:
+
+```typescript
+// src/server/modules/users/user.entity.ts
+@Column({ name: 'wechat_openid', type: 'varchar', length: 255, nullable: true, comment: '微信开放平台openid' })
+wechatOpenid!: string | null;
+
+@Column({ name: 'wechat_unionid', type: 'varchar', length: 255, nullable: true, comment: '微信开放平台unionid' })
+wechatUnionid!: string | null;
+
+@Column({ name: 'wechat_nickname', type: 'varchar', length: 255, nullable: true, comment: '微信昵称' })
+wechatNickname!: string | null;
+
+@Column({ name: 'wechat_avatar', type: 'varchar', length: 500, nullable: true, comment: '微信头像URL' })
+wechatAvatar!: string | null;
+
+@Column({ name: 'wechat_sex', type: 'tinyint', nullable: true, comment: '微信性别(1:男,2:女,0:未知)' })
+wechatSex!: number | null;
+
+@Column({ name: 'wechat_province', type: 'varchar', length: 100, nullable: true, comment: '微信省份' })
+wechatProvince!: string | null;
+
+@Column({ name: 'wechat_city', type: 'varchar', length: 100, nullable: true, comment: '微信城市' })
+wechatCity!: string | null;
+
+@Column({ name: 'wechat_country', type: 'varchar', length: 100, nullable: true, comment: '微信国家' })
+wechatCountry!: string | null;
+```
+
+### 2. 用户Schema更新
+
+在用户Schema中添加微信字段定义:
+
+```typescript
+// src/server/modules/users/user.schema.ts
+wechatOpenid: z.string().max(255, '微信openid最多255个字符').nullable().openapi({
+  example: 'o6_bmjrPTlm6_2sgVt7hMZOPfL2M',
+  description: '微信开放平台openid'
+}),
+wechatUnionid: z.string().max(255, '微信unionid最多255个字符').nullable().openapi({
+  example: 'o6_bmasdasdsad6_2sgVt7hMZOPfL2M',
+  description: '微信开放平台unionid'
+}),
+wechatNickname: z.string().max(255, '微信昵称最多255个字符').nullable().openapi({
+  example: '微信用户',
+  description: '微信昵称'
+}),
+wechatAvatar: z.string().max(500, '微信头像URL最多500个字符').nullable().openapi({
+  example: 'http://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/46',
+  description: '微信头像URL'
+}),
+wechatSex: z.number().int('微信性别必须是整数').min(0, '微信性别最小值为0').max(2, '微信性别最大值为2').nullable().openapi({
+  example: 1,
+  description: '微信性别(1:男,2:女,0:未知)'
+}),
+wechatProvince: z.string().max(100, '微信省份最多100个字符').nullable().openapi({
+  example: '广东省',
+  description: '微信省份'
+}),
+wechatCity: z.string().max(100, '微信城市最多100个字符').nullable().openapi({
+  example: '深圳市',
+  description: '微信城市'
+}),
+wechatCountry: z.string().max(100, '微信国家最多100个字符').nullable().openapi({
+  example: '中国',
+  description: '微信国家'
+})
+```
+
+### 3. 微信认证服务类
+
+创建微信认证服务:
+
+```typescript
+// src/server/modules/wechat/wechat-auth.service.ts
+import axios from 'axios';
+import { UserService } from '../users/user.service';
+import { AuthService } from '../auth/auth.service';
+
+export class WechatAuthService {
+  private readonly appId: string = process.env.WECHAT_MP_APP_ID || '';
+  private readonly appSecret: string = process.env.WECHAT_MP_APP_SECRET || '';
+
+  constructor(
+    private readonly userService: UserService,
+    private readonly authService: AuthService
+  ) {}
+
+  // 获取授权URL
+  getAuthorizationUrl(redirectUri: string, scope: 'snsapi_base' | 'snsapi_userinfo' = 'snsapi_userinfo'): string {
+    const state = Math.random().toString(36).substring(2);
+    return `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${this.appId}&redirect_uri=${encodeURIComponent(redirectUri)}&response_type=code&scope=${scope}&state=${state}#wechat_redirect`;
+  }
+
+  // 通过code获取access_token
+  async getAccessToken(code: string): Promise<any> {
+    const url = `https://api.weixin.qq.com/sns/oauth2/access_token?appid=${this.appId}&secret=${this.appSecret}&code=${code}&grant_type=authorization_code`;
+    const response = await axios.get(url);
+    return response.data;
+  }
+
+  // 获取用户信息
+  async getUserInfo(accessToken: string, openid: string): Promise<any> {
+    const url = `https://api.weixin.qq.com/sns/userinfo?access_token=${accessToken}&openid=${openid}&lang=zh_CN`;
+    const response = await axios.get(url);
+    return response.data;
+  }
+
+  // 微信登录/注册
+  async wechatLogin(code: string): Promise<{ token: string; user: any }> {
+    // 1. 获取access_token
+    const tokenData = await this.getAccessToken(code);
+    if (tokenData.errcode) {
+      throw new Error(`微信认证失败: ${tokenData.errmsg}`);
+    }
+
+    const { access_token, openid, unionid } = tokenData;
+
+    // 2. 检查用户是否存在
+    let user = await this.userService.findByWechatOpenid(openid);
+
+    if (!user) {
+      // 3. 获取用户信息(首次登录)
+      const userInfo = await this.getUserInfo(access_token, openid);
+      
+      // 4. 创建新用户
+      user = await this.userService.createUser({
+        username: `wx_${openid.substring(0, 8)}`,
+        password: Math.random().toString(36).substring(2),
+        wechatOpenid: openid,
+        wechatUnionid: unionid,
+        wechatNickname: userInfo.nickname,
+        wechatAvatar: userInfo.headimgurl,
+        wechatSex: userInfo.sex,
+        wechatProvince: userInfo.province,
+        wechatCity: userInfo.city,
+        wechatCountry: userInfo.country,
+        nickname: userInfo.nickname
+      });
+    }
+
+    // 5. 生成JWT token
+    const token = this.authService.generateToken(user);
+    
+    return { token, user };
+  }
+}
+```
+
+### 4. 微信认证路由
+
+创建微信认证API路由:
+
+```typescript
+// src/server/api/auth/wechat/
+// wechat-authorize.ts - 微信授权URL获取路由
+import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
+import { z } from '@hono/zod-openapi';
+import { WechatAuthService } from '@/server/modules/wechat/wechat-auth.service';
+import { ErrorSchema } from '@/server/utils/errorHandler';
+import { AppDataSource } from '@/server/data-source';
+import { UserService } from '@/server/modules/users/user.service';
+import { AuthService } from '@/server/modules/auth/auth.service';
+
+const AuthorizeSchema = z.object({
+  redirectUri: z.string().url().openapi({
+    example: 'https://example.com/auth/callback',
+    description: '回调地址'
+  }),
+  scope: z.enum(['snsapi_base', 'snsapi_userinfo']).default('snsapi_userinfo').openapi({
+    example: 'snsapi_userinfo',
+    description: '授权范围'
+  })
+});
+
+const AuthorizeResponseSchema = z.object({
+  url: z.string().url().openapi({
+    example: 'https://open.weixin.qq.com/connect/oauth2/authorize?appid=xxx',
+    description: '微信授权URL'
+  })
+});
+
+const userService = new UserService(AppDataSource);
+const authService = new AuthService(userService);
+const wechatAuthService = new WechatAuthService(userService, authService);
+
+const authorizeRoute = createRoute({
+  method: 'post',
+  path: '/authorize',
+  request: {
+    body: {
+      content: {
+        'application/json': {
+          schema: AuthorizeSchema
+        }
+      }
+    }
+  },
+  responses: {
+    200: {
+      description: '获取微信授权URL成功',
+      content: {
+        'application/json': {
+          schema: AuthorizeResponseSchema
+        }
+      }
+    },
+    400: {
+      description: '请求参数错误',
+      content: {
+        'application/json': {
+          schema: ErrorSchema
+        }
+      }
+    }
+  }
+});
+
+const app = new OpenAPIHono().openapi(authorizeRoute, async (c) => {
+  try {
+    const { redirectUri, scope } = await c.req.json();
+    const url = wechatAuthService.getAuthorizationUrl(redirectUri, scope);
+    return c.json({ url }, 200);
+  } catch (error) {
+    return c.json({ 
+      code: 400, 
+      message: error instanceof Error ? error.message : '获取授权URL失败' 
+    }, 400);
+  }
+});
+
+export default app;
+```
+
+```typescript
+// src/server/api/auth/wechat/wechat-login.ts - 微信登录路由
+import { createRoute, OpenAPIHono } from '@hono/zod-openapi';
+import { z } from '@hono/zod-openapi';
+import { WechatAuthService } from '@/server/modules/wechat/wechat-auth.service';
+import { ErrorSchema } from '@/server/utils/errorHandler';
+import { AppDataSource } from '@/server/data-source';
+import { UserService } from '@/server/modules/users/user.service';
+import { AuthService } from '@/server/modules/auth/auth.service';
+import { TokenResponseSchema } from '../login/password';
+import { parseWithAwait } from '@/server/utils/parseWithAwait';
+
+const WechatLoginSchema = z.object({
+  code: z.string().min(1).openapi({
+    example: '0816TxlL1Qe3QY0qgDlL1pQxlL16TxlO',
+    description: '微信授权code'
+  })
+});
+
+const userService = new UserService(AppDataSource);
+const authService = new AuthService(userService);
+const wechatAuthService = new WechatAuthService(userService, authService);
+
+const wechatLoginRoute = createRoute({
+  method: 'post',
+  path: '/login',
+  request: {
+    body: {
+      content: {
+        'application/json': {
+          schema: WechatLoginSchema
+        }
+      }
+    }
+  },
+  responses: {
+    200: {
+      description: '微信登录成功',
+      content: {
+        'application/json': {
+          schema: TokenResponseSchema
+        }
+      }
+    },
+    400: {
+      description: '请求参数错误',
+      content: {
+        'application/json': {
+          schema: ErrorSchema
+        }
+      }
+    },
+    401: {
+      description: '微信认证失败',
+      content: {
+        'application/json': {
+          schema: ErrorSchema
+        }
+      }
+    }
+  }
+});
+
+const app = new OpenAPIHono().openapi(wechatLoginRoute, async (c) => {
+  try {
+    const { code } = await c.req.json();
+    const result = await wechatAuthService.wechatLogin(code);
+    
+    // 使用 parseWithAwait 确保返回数据符合Schema定义
+    const validatedResult = await parseWithAwait(TokenResponseSchema, result);
+    
+    return c.json(validatedResult, 200);
+  } catch (error) {
+    return c.json({ 
+      code: 401, 
+      message: error instanceof Error ? error.message : '微信登录失败' 
+    }, 401);
+  }
+});
+
+export default app;
+```
+
+```typescript
+// src/server/api/auth/wechat/index.ts - 路由聚合
+import { OpenAPIHono } from '@hono/zod-openapi';
+import authorizeRoute from './wechat-authorize';
+import loginRoute from './wechat-login';
+
+const app = new OpenAPIHono()
+  .route('/', authorizeRoute)
+  .route('/', loginRoute);
+
+export default app;
+```
+
+### 5. parseWithAwait 使用规范
+
+#### 概述
+`parseWithAwait` 是通用CRUD模块提供的数据验证工具,用于确保返回数据的类型安全,支持异步验证和转换。
+
+#### 使用场景
+所有涉及数据查询和返回的扩展路由都应使用 `parseWithAwait` 处理响应数据。
+
+#### 基本用法
+```typescript
+import { parseWithAwait } from '@/server/utils/parseWithAwait';
+
+// 验证单个实体
+const validatedEntity = await parseWithAwait(YourEntitySchema, entityData);
+
+// 验证实体数组
+const validatedEntities = await parseWithAwait(z.array(YourEntitySchema), entitiesData);
+```
+
+#### 集成示例
+```typescript
+// 在微信登录路由中使用 parseWithAwait
+const app = new OpenAPIHono().openapi(wechatLoginRoute, async (c) => {
+  try {
+    const { code } = await c.req.json();
+    const result = await wechatAuthService.wechatLogin(code);
+    
+    // 使用 parseWithAwait 确保返回数据符合Schema定义
+    const validatedResult = await parseWithAwait(TokenResponseSchema, result);
+    
+    return c.json(validatedResult, 200);
+  } catch (error) {
+    return c.json({
+      code: 401,
+      message: error instanceof Error ? error.message : '微信登录失败'
+    }, 401);
+  }
+});
+```
+
+#### 优势
+- **类型安全**:确保返回数据完全符合Zod schema定义
+- **异步支持**:支持异步验证和转换操作
+- **错误处理**:提供详细的验证错误信息
+- **性能优化**:避免运行时类型错误
+- **向后兼容**:与现有代码完全兼容
+
+#### 最佳实践
+1. **所有查询路由**:GET请求返回数据前必须使用 `parseWithAwait`
+2. **列表查询**:使用 `z.array(EntitySchema)` 格式验证数组
+3. **单条查询**:直接使用实体Schema验证单个对象
+4. **错误处理**:捕获并适当处理验证错误
+
+### 6. 注册微信路由
+
+在API入口文件中注册微信路由:
+
+```typescript
+// src/server/api.ts
+import wechatRoutes from '@/server/api/auth/wechat/index';
+
+// 注册路由
+api.route('/api/v1/auth/wechat', wechatRoutes);
+```
+
+### 6. 前端微信登录组件
+
+创建React微信登录组件:
+
+```typescript
+// src/client/components/WechatLoginButton.tsx
+import React from 'react';
+import { Button } from '@/client/components/ui/button';
+import { Wechat } from 'lucide-react';
+import { authClient } from '@/client/api';
+
+interface WechatLoginButtonProps {
+  redirectUri: string;
+  scope?: 'snsapi_base' | 'snsapi_userinfo';
+  onSuccess?: (data: any) => void;
+  onError?: (error: Error) => void;
+}
+
+const WechatLoginButton: React.FC<WechatLoginButtonProps> = ({
+  redirectUri,
+  scope = 'snsapi_userinfo',
+  onSuccess,
+  onError,
+  children = '微信登录'
+}) => {
+  const handleWechatLogin = async () => {
+    try {
+      // 使用RPC客户端获取微信授权URL
+      const response = await authClient.wechat.authorize.$post({
+        json: { redirectUri, scope }
+      });
+
+      if (response.status !== 200) {
+        throw new Error('获取微信授权URL失败');
+      }
+
+      const { url } = await response.json();
+      
+      // 重定向到微信授权页面
+      window.location.href = url;
+    } catch (error) {
+      onError?.(error as Error);
+    }
+  };
+
+  return (
+    <Button
+      variant="default"
+      onClick={handleWechatLogin}
+      className="bg-[#07C160] text-white hover:bg-[#06B456] border-none"
+    >
+      <Wechat className="w-4 h-4 mr-2" />
+      {children}
+    </Button>
+  );
+};
+
+export default WechatLoginButton;
+```
+
+### 7. 移动端微信登录集成
+
+更新移动端认证页面:
+
+```typescript
+// src/client/mobile/pages/AuthPage.tsx - 添加微信登录选项
+import WechatLoginButton from '@/client/components/WechatLoginButton';
+import { toast } from 'sonner'
+
+// 在表单后添加微信登录按钮
+<div className="mt-6">
+  <div className="relative">
+    <div className="absolute inset-0 flex items-center">
+      <div className="w-full border-t border-gray-300"></div>
+    </div>
+    <div className="relative flex justify-center text-sm">
+      <span className="px-2 bg-white text-gray-500">或使用以下方式登录</span>
+    </div>
+  </div>
+  
+  <div className="mt-4">
+    <WechatLoginButton
+      redirectUri={`${window.location.origin}/mobile/auth/callback`}
+      onSuccess={(data) => {
+        localStorage.setItem('token', data.token);
+        navigate(getReturnUrl(), { replace: true });
+      }}
+      onError={(error) => {
+        toast(error.message);
+      }}
+    >
+      微信一键登录
+    </WechatLoginButton>
+  </div>
+</div>
+```
+
+### 8. 环境变量配置
+
+在.env文件中添加微信配置:
+
+```bash
+# 微信服务号配置(公众号)
+WECHAT_MP_APP_ID=your_wechat_mp_app_id
+WECHAT_MP_APP_SECRET=your_wechat_mp_app_secret
+
+# 微信开放平台配置(可选,用于多端统一)
+WECHAT_OPEN_APP_ID=your_wechat_open_app_id
+WECHAT_OPEN_APP_SECRET=your_wechat_open_app_secret
+```
+
+
+## 故障排除
+
+### 常见问题
+
+1. **redirect_uri参数错误**
+   - 检查网页授权域名配置
+   - 确保回调地址与配置一致
+
+2. **invalid code**
+   - code只能使用一次
+   - code有效期5分钟
+
+3. **access_token过期**
+   - access_token有效期为2小时
+   - refresh_token有效期为30天
+
+4. **API调用频率限制**
+   - 微信API有调用频率限制
+   - 建议添加缓存机制
+

+ 9 - 0
.claude/settings.local.json

@@ -0,0 +1,9 @@
+{
+  "permissions": {
+    "allow": [
+      "Bash(mkdir:*)"
+    ],
+    "deny": [],
+    "ask": []
+  }
+}

+ 27 - 0
CLAUDE.md

@@ -85,5 +85,32 @@ The project uses Roo framework for code generation and validation:
 - API-client validation rules
 - Entity-field mapping checks
 
+## Project Standards & Rules
+- @.roo/rules/01-general.md - 通用开发规范
+- @.roo/rules/02-typescript.md - TypeScript规范
+- @.roo/rules/03-modules.md - 模块化规范
+- @.roo/rules/04-api.md - API开发规范
+- @.roo/rules/05-database.md - 数据库规范
+- @.roo/rules/06-service-di.md - 服务与依赖注入规范
+- @.roo/rules/07-openapi.md - OpenAPI规范
+- @.roo/rules/08-rpc.md - RPC调用规范
+- @.roo/rules/09-logging.md - 日志规范
+- @.roo/rules/10-entity.md - 实体定义规范
+- @.roo/rules/11-admin-frontend.md - 管理前端规范
+- @.roo/rules/11-custom-crud.md - 自定义CRUD规范
+- @.roo/rules/11-entity-creation.md - 实体创建规范
+- @.roo/rules/11-home-frontend.md - 首页前端规范
+- @.roo/rules/11-standard-crud.md - 标准CRUD规范
+- @.roo/rules/12-generic-crud.md - 通用CRUD规范
+- @.roo/rules/14-crud-filtering.md - CRUD筛选规范
+- @.roo/rules/15-user-tracking.md - 用户追踪规范
+
+## Development Notes
+- Uses pnpm as package manager
+- Vite for both dev server and production builds
+- SSR (Server-Side Rendering) setup with Hono.js
+- Environment variables via `.env` file
+- Port 8080 for development and production
+
 ## Claude Code
 - Always response in Chinese