Skip to content

模块开发教程 ​

本框架采用了模块化的前端开发模式,旨在提高代码的可维护性和可扩展性。

开发流程 ​

本实践提倡新功能开发遵循以下步骤:

  1. 前端开发(表现和行为层)
  2. 后端开发(API)
  3. 前端联调(数据层)

基于AI辅助的开发模式和传统开发模式有所不同。传统的开发模式强调并行开发,一般先设计程序,再设计服务接口标准,然后开发人员基于该接口标准分别实现前后端程序,再联调测试。而基于AI辅助的开发实践一般提倡“原型既前端”,将功能设计和前端开发过程合并,在根据前端设计开发后端服务。

模块结构 ​

如果您正在开发一个新的业务模块(例如 products),请将其放置在 src/modules/ 目录下。该目录下的模块支持自动路由、菜单注册和系统设置等。

请遵循以下目录结构:

text
src/modules/products/
├── components/          # 模块私有组件
├── Products.tsx         # 主页面
├── ProductDetail.tsx    # 详情页
├── routes.tsx           # 路由配置文件 (自动注册)
├── menu.ts              # 菜单配置文件 (自动注册)
├── settings.tsx         # 模块设置描述文件 (可选,自动注册到系统设置)
├── package.json         # 模块包定义 (自动注册)
└── README.md            # 模块说明文件

定义模块包 ​

在模块目录下创建 package.json,系统会自动将其注册到模块管理中。

json
// src/modules/products/package.json
{
  "name": "@pocketstack/products",
  "version": "1.0.0",
  "title": "产品管理",
  "description": "产品管理模块,提供产品列表、详情等功能",
  "private": true,
  "type": "module",
  "dependencies": {},
  "peerDependencies": {
    "react": "^19.0.0",
    "react-dom": "^19.0.0"
  }
}
字段说明必填
name包名,格式为 @pocketstack/{module}是
version版本号是
title模块显示名称是
description模块描述否
private是否私有,固定为 true是
type模块类型,固定为 module是
dependencies模块依赖,不需要可为空对象 {}否
peerDependencies同级依赖,固定包含 react 和 react-dom是

注意:dependencies 不要和根目录 package.json 中的依赖冲突、重复。

创建页面组件 ​

在模块目录下创建页面文件,文件命名遵循大驼峰。

tsx
// src/modules/products/Products.tsx
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';

export function Products() {
  return (
    <div className="space-y-6">
      <h1 className="text-3xl font-bold">产品管理</h1>
      <Card>
        <CardHeader>
          <CardTitle>产品列表</CardTitle>
        </CardHeader>
        <CardContent>
          {/* 功能实现代码 */}
        </CardContent>
      </Card>
    </div>
  );
}

配置模块路由 ​

在模块目录下创建 routes.tsx,导出名为 routes 的变量。系统会自动将其注册到主路由中。

tsx
// src/modules/products/routes.tsx
import { Route } from 'react-router-dom';
import { Products } from './Products';

export const routes = (
  <>
    <Route path="products/list" element={<Products />} />
    {/* 其他路由... */}
  </>
);

注意:无需在 src/App.tsx 中手动导入。

配置模块菜单 ​

在模块目录下创建 menu.ts,导出名为 menu 的变量。系统会自动扫描 src/modules/ 目录下的 menu.ts 并注册到侧边栏。

typescript
// src/modules/products/menu.ts
import { PackageIcon } from '@heroicons/react/24/outline';

export const menu = {
  title: '产品管理',
  icon: PackageIcon,
  order: 10,
  children: [
    { title: '产品列表', path: '/products/list', order: 1 },
  ],
};

注意:无需在 src/components/menu.ts 中手动导入。

菜单数据结构 ​

菜单项统一由 MenuItem 接口定义(类型位于 src/components/menu.ts)。顶级菜单项可通过 children 嵌套子菜单:

typescript
interface MenuItem {
  title: string;           // 菜单名称
  path?: string;          // 跳转路径(如果有子菜单则可选)
  icon: any;              // 图标
  order?: number;         // 排序权重,越小越靠前,默认为 999
  adminOnly?: boolean;    // 是否仅超级管理员可见
  userOnly?: boolean;     // 是否仅普通用户可见
  show?: boolean;         // 是否在侧边栏显示(默认为 true)
  activePath?: string;    // 手动指定激活路径匹配模式(正则或前缀)
  external?: boolean;     // 是否为外部链接
  children?: {            // 子菜单列表
    title: string;
    path: string;
    order?: number;       // 子菜单排序权重
    adminOnly?: boolean;
    userOnly?: boolean;
    show?: boolean;       // 子菜单是否显示
    external?: boolean;
  }[];
}

菜单属性说明 ​

属性名类型说明默认值
titlestring菜单显示的名称必填
pathstring链接路径(顶级菜单有 children 时可选)-
iconComponentHeroicons 图标组件必填
ordernumber排序权重,越小越靠前999
adminOnlyboolean是否仅超级管理员可见false
userOnlyboolean是否仅普通用户可见false
showboolean是否在侧边栏渲染true
activePathstring手动指定激活路径匹配模式(正则或前缀)-
externalboolean是否在新标签页打开外部链接false

权限控制 ​

  1. 角色过滤(adminOnly/userOnly):在侧边栏渲染前,会根据用户的角色状态进行过滤:
    • adminOnly: true:仅当用户为超级管理员时可见。
    • userOnly: true:仅当用户为普通用户时可见。
  2. 路由保护:除了 UI 层的隐藏,配套的路由也应在模块的 routes.tsx 中使用 AdminOnlyRoute 或 UserOnlyRoute 进行包裹。

显示控制 ​

  1. 显示过滤(show):如果菜单项或子菜单项设置了 show: false,则该项在侧边栏中会被直接过滤掉,不进行渲染。这适用于某些功能尚未上线或仅用于路由跳转而不需在菜单显示的场景。
  2. 路径匹配激活(activePath):如果设置了 activePath,则会根据当前路径与 activePath 进行匹配。如果匹配成功,菜单项会被激活。

如何修改菜单 ​

  1. 全局菜单:编辑 src/components/menu.ts 中的 adminMenu 常量。
  2. 模块菜单:在 src/modules/{module}/menu.ts 中定义并导出 menu 变量,系统会自动扫描并组合这些菜单。
  3. 属性设置:根据需要设置 adminOnly、userOnly 或 show 等属性来控制可见性与显示。

配置模块设置(可选) ​

如果模块需要暴露可配置项,可在模块目录下创建 settings.tsx,默认导出一个 ModuleSettings 对象。系统设置页会自动将其注册为一个设置 Tab,并根据声明的字段类型自动渲染表单控件。

tsx
// src/modules/products/settings.tsx
import { CubeIcon } from '@heroicons/react/24/outline';
import type { ModuleSettings } from '@/lib/module-settings';

const settings: ModuleSettings = {
  title: '产品设置',            // Tab 标题
  description: '产品模块的全局配置', // Tab 内说明
  icon: CubeIcon,              // Tab 图标
  order: 20,                   // 可选,排序权重(越小越靠前,默认 100)
  fields: [
    {
      type: 'switch',
      key: 'enable_review',
      label: '启用审核',
      description: '创建产品时是否需要管理员审核',
      defaultValue: false,
    },
    {
      type: 'text',
      key: 'default_currency',
      label: '默认币种',
      defaultValue: 'CNY',
    },
    {
      type: 'select',
      key: 'page_size',
      label: '分页大小',
      options: [
        { label: '20 条', value: '20' },
        { label: '50 条', value: '50' },
      ],
      defaultValue: '20',
    },
  ],
};

export default settings;

字段 key 自动加模块名前缀 ​

为避免不同模块间的配置 key 冲突,所有字段 key 在存储时会自动加上 {模块名}_ 前缀。例如上面的 enable_review 最终存储为 products_enable_review,页面上会显示带前缀的完整 key。

在页面中读取模块设置 ​

tsx
import { useSettings } from '@/lib/use-settings';

export function Products() {
  const { get } = useSettings();
  const enableReview = get('products_enable_review', false) === true;
  const pageSize = get('products_page_size', '20');
  // ...
}

支持的字段类型 ​

type控件说明
text单行输入框字符串配置
textarea多行输入框长文本配置
number数字输入框支持 min/max/step
switch布尔开关开关型配置
select下拉选择通过 options 提供候选项

完整示例参考 src/modules/examples/settings.tsx(该模块的「欢迎横幅」已联动到示例仪表盘)。如需查看系统级全局配置的实现,参见 src/modules/settings/systemSettings.ts。

编写模块文档 ​

每个模块目录下建议创建 README.md 文件,用于说明模块的功能、页面、数据模型等。系统支持在模块管理中查看各模块的 README 文档。

markdown
# 产品管理

## 模块概述

产品管理模块,提供产品列表、详情等功能。

## 功能特性

- **产品列表**:支持分页、搜索、筛选
- **产品详情**:查看产品完整信息
- **分类管理**:产品分类的增删改查

## 页面

| 页面 | 路径 | 说明 |
|------|------|------|
| 产品列表 | `/products/list` | 产品列表页 |
| 产品详情 | `/products/detail` | 产品详情页 |

## 数据模型

- `products` — 产品数据(迁移文件位于 `migrations/products.json`)

可参考 src/modules/examples/README.md 了解完整的模块文档写法。

后端开发 ​

无论通过 PocketBase 提供的 UI 界面,还是通过 MCP (强烈推荐),后端开发都基于以下流程:

  1. 启动 PocketBase 控制台 (默认 http://127.0.0.1:8090/_/)。
  2. 创建新的 Collection (例如 posts, products 等)。
  3. 配置字段类型并设置 API Rules (ACL),确保安全访问。
  4. 创建 Mock 数据,以便于联调。

开发规则总结 ​

  • 目录位置:必须放置在 src/modules/ 目录下。
  • 包定义:必须包含 package.json 文件。
  • 自动注册导出名:路由必须导出为 routes,菜单必须导出为 menu;模块设置需在 settings.tsx 中默认导出 ModuleSettings。
  • 页面命名:遵循大驼峰(如 Products.tsx)。
  • 组件位置:模块私有组件存放在模块目录下的 components/。
  • 路径规范:页面的访问路径通常为 /{module}/{page}。