模块开发教程
本框架采用了模块化的前端开发模式,旨在提高代码的可维护性和可扩展性。
开发流程
本实践提倡新功能开发遵循以下步骤:
- 前端开发(表现和行为层)
- 后端开发(API)
- 前端联调(数据层)
基于AI辅助的开发模式和传统开发模式有所不同。传统的开发模式强调并行开发,一般先设计程序,再设计服务接口标准,然后开发人员基于该接口标准分别实现前后端程序,再联调测试。而基于AI辅助的开发实践一般提倡“原型既前端”,将功能设计和前端开发过程合并,在根据前端设计开发后端服务。
模块结构
如果您正在开发一个新的业务模块(例如 products),请将其放置在 src/modules/ 目录下。该目录下的模块支持自动路由、菜单注册和系统设置等。
请遵循以下目录结构:
src/modules/products/
├── components/ # 模块私有组件
├── Products.tsx # 主页面
├── ProductDetail.tsx # 详情页
├── routes.tsx # 路由配置文件 (自动注册)
├── menu.ts # 菜单配置文件 (自动注册)
├── settings.tsx # 模块设置描述文件 (可选,自动注册到系统设置)
├── package.json # 模块包定义 (自动注册)
└── README.md # 模块说明文件定义模块包
在模块目录下创建 package.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中的依赖冲突、重复。
创建页面组件
在模块目录下创建页面文件,文件命名遵循大驼峰。
// 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 的变量。系统会自动将其注册到主路由中。
// 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 并注册到侧边栏。
// 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 嵌套子菜单:
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;
}[];
}菜单属性说明
| 属性名 | 类型 | 说明 | 默认值 |
|---|---|---|---|
| title | string | 菜单显示的名称 | 必填 |
| path | string | 链接路径(顶级菜单有 children 时可选) | - |
| icon | Component | Heroicons 图标组件 | 必填 |
| order | number | 排序权重,越小越靠前 | 999 |
| adminOnly | boolean | 是否仅超级管理员可见 | false |
| userOnly | boolean | 是否仅普通用户可见 | false |
| show | boolean | 是否在侧边栏渲染 | true |
| activePath | string | 手动指定激活路径匹配模式(正则或前缀) | - |
| external | boolean | 是否在新标签页打开外部链接 | false |
权限控制
- 角色过滤(
adminOnly/userOnly):在侧边栏渲染前,会根据用户的角色状态进行过滤:adminOnly: true:仅当用户为超级管理员时可见。userOnly: true:仅当用户为普通用户时可见。
- 路由保护:除了 UI 层的隐藏,配套的路由也应在模块的
routes.tsx中使用AdminOnlyRoute或UserOnlyRoute进行包裹。
显示控制
- 显示过滤(
show):如果菜单项或子菜单项设置了show: false,则该项在侧边栏中会被直接过滤掉,不进行渲染。这适用于某些功能尚未上线或仅用于路由跳转而不需在菜单显示的场景。 - 路径匹配激活(
activePath):如果设置了activePath,则会根据当前路径与activePath进行匹配。如果匹配成功,菜单项会被激活。
如何修改菜单
- 全局菜单:编辑
src/components/menu.ts中的adminMenu常量。 - 模块菜单:在
src/modules/{module}/menu.ts中定义并导出menu变量,系统会自动扫描并组合这些菜单。 - 属性设置:根据需要设置
adminOnly、userOnly或show等属性来控制可见性与显示。
配置模块设置(可选)
如果模块需要暴露可配置项,可在模块目录下创建 settings.tsx,默认导出一个 ModuleSettings 对象。系统设置页会自动将其注册为一个设置 Tab,并根据声明的字段类型自动渲染表单控件。
// 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。
在页面中读取模块设置
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 文档。
# 产品管理
## 模块概述
产品管理模块,提供产品列表、详情等功能。
## 功能特性
- **产品列表**:支持分页、搜索、筛选
- **产品详情**:查看产品完整信息
- **分类管理**:产品分类的增删改查
## 页面
| 页面 | 路径 | 说明 |
|------|------|------|
| 产品列表 | `/products/list` | 产品列表页 |
| 产品详情 | `/products/detail` | 产品详情页 |
## 数据模型
- `products` — 产品数据(迁移文件位于 `migrations/products.json`)可参考
src/modules/examples/README.md了解完整的模块文档写法。
后端开发
无论通过 PocketBase 提供的 UI 界面,还是通过 MCP (强烈推荐),后端开发都基于以下流程:
- 启动 PocketBase 控制台 (默认
http://127.0.0.1:8090/_/)。 - 创建新的 Collection (例如
posts,products等)。 - 配置字段类型并设置 API Rules (ACL),确保安全访问。
- 创建 Mock 数据,以便于联调。
开发规则总结
- 目录位置:必须放置在
src/modules/目录下。 - 包定义:必须包含
package.json文件。 - 自动注册导出名:路由必须导出为
routes,菜单必须导出为menu;模块设置需在settings.tsx中默认导出ModuleSettings。 - 页面命名:遵循大驼峰(如
Products.tsx)。 - 组件位置:模块私有组件存放在模块目录下的
components/。 - 路径规范:页面的访问路径通常为
/{module}/{page}。