提交1 bmad搭建与项目启动 - ok

This commit is contained in:
2025-10-17 17:24:56 +08:00
commit ec58562661
686 changed files with 149750 additions and 0 deletions

View File

@@ -0,0 +1,779 @@
# Pages目录结构与开发规范
## 1. 目录结构规范
### 1.1 页面目录标准结构
每个页面目录采用以下标准结构:
```
pages/ModuleName/SubModuleName/
├── 📄 index.jsx # 页面主组件
├── 📂 components/ # 页面子组件目录
│ ├── 📂 Component1/ # 子组件1拆分的大组件
│ │ ├── 📄 index.jsx # 子组件主文件
│ │ ├── 📄 Component1.jsx # 子组件实现
│ │ ├── 📄 index.css # 子组件样式
│ │ └── 📄 types.ts # 子组件类型定义
│ ├── 📂 Component2/ # 子组件2
│ │ └── ...
│ └── 📂 common/ # 页面通用组件
│ ├── 📄 Header.jsx
│ ├── 📄 Footer.jsx
│ └── ...
├── 📄 index.css # 页面主样式文件
├── 📄 index.types.ts # 页面类型定义
├── 📄 hooks/ # 页面专用Hooks
│ ├── 📄 usePageData.tsx
│ └── 📄 usePageActions.tsx
├── 📄 utils/ # 页面专用工具函数
│ ├── 📄 pageHelpers.tsx
│ └── 📄 formatters.tsx
└── 📄 constants.tsx # 页面常量定义
```
### 1.2 组件拆分原则
**拆分条件:**
1. 组件代码超过100行
2. 组件承担多个职责
3. 组件需要复用
4. 组件逻辑复杂包含多个useState或useEffect
**拆分命名:**
- 主组件保持原名称MachineryEntry
- 子组件基于功能命名MachineryList、MachineryForm、MachineryFilter
- 通用组件基于用途命名TableHeader、FormActions
## 2. 开发规范
### 2.1 组件开发规范
#### 2.1.1 主组件规范
```jsx
// pages/AgriculturalMachinery/Archive/MachineryEntry/index.jsx
import { useState, useEffect, useCallback } from 'react';
import { MachineryList } from './components/MachineryList';
import { MachineryForm } from './components/MachineryForm';
import { MachineryFilter } from './components/MachineryFilter';
import { useMachineryData } from './hooks/usePageData';
import { useMachineryActions } from './hooks/usePageActions';
import './index.css';
export function MachineryEntry() {
const {
machinery,
loading,
error,
filters,
pagination,
handleFilterChange,
handlePageChange,
refreshData
} = useMachineryData();
const {
handleCreate,
handleEdit,
handleDelete,
handleBatchDelete,
handleExport
} = useMachineryActions(refreshData);
return (
<div className="machinery-entry">
<div className="page-header">
<h1>农机档案管理</h1>
<div className="page-actions">
<button onClick={handleCreate}>新增农机</button>
<button onClick={handleExport}>导出数据</button>
</div>
</div>
<MachineryFilter
filters={filters}
onFilterChange={handleFilterChange}
/>
<MachineryList
machinery={machinery}
loading={loading}
error={error}
pagination={pagination}
onPageChange={handlePageChange}
onEdit={handleEdit}
onDelete={handleDelete}
onBatchDelete={handleBatchDelete}
/>
<MachineryForm />
</div>
);
}
```
#### 2.1.2 子组件规范
```jsx
// pages/AgriculturalMachinery/Archive/MachineryEntry/components/MachineryList/index.jsx
import { memo } from 'react';
import { MachineryTable } from './MachineryTable';
import { TableActions } from './TableActions';
import { useMachineryList } from '../hooks/usePageData';
import { MachineryRecord } from '../types';
import './index.css';
interface MachineryListProps {
machinery: MachineryRecord[];
loading: boolean;
error: string | null;
pagination: {
current: number;
pageSize: number;
total: number;
};
onPageChange: (page: number) => void;
onEdit: (record: MachineryRecord) => void;
onDelete: (id: string) => void;
onBatchDelete: (ids: string[]) => void;
}
export const MachineryList = memo<MachineryListProps>(({
machinery,
loading,
error,
pagination,
onPageChange,
onEdit,
onDelete,
onBatchDelete
}) => {
const {
selectedRows,
handleRowSelection,
handleSelectAll,
handleBatchActions
} = useMachineryList(machinery);
if (error) {
return (
<div className="error-state">
<p>加载失败{error}</p>
<button onClick={() => window.location.reload()}>
重新加载
</button>
</div>
);
}
return (
<div className="machinery-list">
<TableActions
selectedRows={selectedRows}
onBatchDelete={handleBatchDelete}
onRefresh={() => onPageChange(pagination.current)}
/>
<MachineryTable
data={machinery}
loading={loading}
selectedRows={selectedRows}
onRowSelection={handleRowSelection}
onSelectAll={handleSelectAll}
onEdit={onEdit}
onDelete={onDelete}
pagination={pagination}
onPageChange={onPageChange}
/>
</div>
);
});
MachineryList.displayName = 'MachineryList';
```
### 2.2 类型定义规范
#### 2.2.1 页面类型定义
```typescript
// pages/AgriculturalMachinery/Archive/MachineryEntry/index.types.ts
export interface MachineryRecord {
id: string;
name: string;
model: string;
category: MachineryCategory;
status: MachineryStatus;
manufacturer: string;
purchaseDate: string;
price: number;
// ... 其他字段
}
export type MachineryCategory =
| '耕地机械'
| '播种机械'
| '收获机械'
| '植保机械';
export type MachineryStatus =
| '运行中'
| '空闲中'
| '待维护'
| '已报废';
export interface MachineryFilters {
category?: MachineryCategory;
status?: MachineryStatus;
manufacturer?: string;
dateRange?: [string, string];
}
export interface PaginationState {
current: number;
pageSize: number;
total: number;
}
```
#### 2.2.2 组件类型定义
```typescript
// pages/AgriculturalMachinery/Archive/MachineryEntry/components/MachineryList/types.ts
import { MachineryRecord, MachineryFilters, PaginationState } from '../../index.types';
export interface MachineryListProps {
machinery: MachineryRecord[];
loading: boolean;
error: string | null;
filters: MachineryFilters;
pagination: PaginationState;
onPageChange: (page: number) => void;
onEdit: (record: MachineryRecord) => void;
onDelete: (id: string) => void;
onBatchDelete: (ids: string[]) => void;
}
export interface SelectedRow {
id: string;
name: string;
// ... 其他需要显示在选中行的字段
}
```
### 2.3 Hooks规范
#### 2.3.1 页面数据Hook
```javascript
// pages/AgriculturalMachinery/Archive/MachineryEntry/hooks/usePageData.js
import { useState, useEffect, useCallback } from 'react';
import { getMachineryList, deleteMachinery } from '@/apis/subModules/agriculturalMachinery';
import { showMessage } from '@/utils/message';
export function useMachineryData() {
const [machinery, setMachinery] = useState([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [filters, setFilters] = useState({});
const [pagination, setPagination] = useState({
current: 1,
pageSize: 10,
total: 0
});
// 获取数据
const fetchMachinery = useCallback(async () => {
setLoading(true);
setError(null);
try {
const params = {
...filters,
page: pagination.current,
pageSize: pagination.pageSize
};
const response = await getMachineryList(params);
setMachinery(response.data.list);
setPagination(prev => ({
...prev,
total: response.data.total
}));
} catch (err) {
setError(err.message);
showMessage('error', '获取农机数据失败');
} finally {
setLoading(false);
}
}, [filters, pagination.current, pagination.pageSize]);
// 初始加载
useEffect(() => {
fetchMachinery();
}, [fetchMachinery]);
// 处理筛选变化
const handleFilterChange = useCallback((newFilters) => {
setFilters(newFilters);
setPagination(prev => ({ ...prev, current: 1 }));
}, []);
// 处理分页变化
const handlePageChange = useCallback((page) => {
setPagination(prev => ({ ...prev, current: page }));
}, []);
return {
machinery,
loading,
error,
filters,
pagination,
handleFilterChange,
handlePageChange,
refreshData: fetchMachinery
};
}
```
#### 2.3.2 页面操作Hook
```javascript
// pages/AgriculturalMachinery/Archive/MachineryEntry/hooks/usePageActions.js
import { useCallback } from 'react';
import { deleteMachinery, updateMachinery } from '@/apis/subModules/agriculturalMachinery';
import { showMessage } from '@/utils/message';
export function useMachineryActions(refreshData) {
// 删除农机
const handleDelete = useCallback(async (id) => {
try {
await deleteMachinery(id);
showMessage('success', '删除成功');
refreshData();
} catch (error) {
showMessage('error', '删除失败');
}
}, [refreshData]);
// 编辑农机
const handleEdit = useCallback(async (data) => {
try {
await updateMachinery(data.id, data);
showMessage('success', '更新成功');
refreshData();
} catch (error) {
showMessage('error', '更新失败');
}
}, [refreshData]);
// 批量删除
const handleBatchDelete = useCallback(async (ids) => {
try {
await Promise.all(ids.map(id => deleteMachinery(id)));
showMessage('success', '批量删除成功');
refreshData();
} catch (error) {
showMessage('error', '批量删除失败');
}
}, [refreshData]);
// 导出数据
const handleExport = useCallback(() => {
// 实现导出逻辑
console.log('导出数据');
}, []);
return {
handleDelete,
handleEdit,
handleBatchDelete,
handleExport
};
}
```
### 2.4 样式规范
#### 2.4.1 页面主样式
```css
/* pages/AgriculturalMachinery/Archive/MachineryEntry/index.css */
.machinery-entry {
padding: 24px;
background: #f5f5f5;
min-height: 100vh;
}
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
padding: 16px;
background: white;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.page-header h1 {
font-size: 24px;
font-weight: 600;
color: #333;
margin: 0;
}
.page-actions {
display: flex;
gap: 12px;
}
.page-actions button {
padding: 8px 16px;
border-radius: 4px;
border: 1px solid #d9d9d9;
background: white;
cursor: pointer;
transition: all 0.2s;
}
.page-actions button:hover {
background: #f0f0f0;
border-color: #40a9ff;
}
.page-actions button.primary {
background: #40a9ff;
color: white;
border-color: #40a9ff;
}
.page-actions button.primary:hover {
background: #1890ff;
border-color: #1890ff;
}
/* 响应式设计 */
@media (max-width: 768px) {
.page-header {
flex-direction: column;
align-items: flex-start;
gap: 16px;
}
.page-actions {
width: 100%;
justify-content: flex-end;
}
}
```
#### 2.4.2 组件样式
```css
/* pages/AgriculturalMachinery/Archive/MachineryEntry/components/MachineryList/index.css */
.machinery-list {
background: white;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.error-state {
padding: 48px;
text-align: center;
color: #666;
}
.error-state p {
margin-bottom: 16px;
font-size: 16px;
}
.error-state button {
padding: 8px 16px;
background: #ff4d4f;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background 0.2s;
}
.error-state button:hover {
background: #ff7875;
}
.table-actions {
padding: 16px;
border-bottom: 1px solid #f0f0f0;
display: flex;
justify-content: space-between;
align-items: center;
}
.selected-info {
color: #666;
font-size: 14px;
}
.batch-actions {
display: flex;
gap: 8px;
}
.batch-actions button {
padding: 4px 8px;
font-size: 12px;
border-radius: 4px;
border: 1px solid #d9d9d9;
background: white;
cursor: pointer;
transition: all 0.2s;
}
.batch-actions button:hover {
background: #f0f0f0;
}
.batch-actions button.danger {
color: #ff4d4f;
border-color: #ff4d4f;
}
.batch-actions button.danger:hover {
background: #ff4d4f;
color: white;
}
```
### 2.5 工具函数规范
#### 2.5.1 页面工具函数
```javascript
// pages/AgriculturalMachinery/Archive/MachineryEntry/utils/pageHelpers.js
import dayjs from 'dayjs';
import { formatCurrency } from '@/utils/formatters';
/**
* 格式化农机状态
*/
export function formatMachineryStatus(status) {
const statusMap = {
'运行中': { text: '运行中', color: '#52c41a' },
'空闲中': { text: '空闲中', color: '#d9d9d9' },
'待维护': { text: '待维护', color: '#faad14' },
'已报废': { text: '已报废', color: '#ff4d4f' }
};
return statusMap[status] || { text: '未知', color: '#d9d9d9' };
}
/**
* 格式化日期
*/
export function formatDate(date) {
return dayjs(date).format('YYYY-MM-DD HH:mm');
}
/**
* 格式化价格
*/
export function formatPrice(price) {
return formatCurrency(price);
}
/**
* 生成农机二维码内容
*/
export function generateQRCode(machinery) {
return JSON.stringify({
id: machinery.id,
name: machinery.name,
model: machinery.model,
category: machinery.category,
manufacturer: machinery.manufacturer
});
}
```
#### 2.5.2 格式化工具
```javascript
// pages/AgriculturalMachinery/Archive/MachineryEntry/utils/formatters.js
/**
* 格式化货币
*/
export function formatCurrency(amount) {
return new Intl.NumberFormat('zh-CN', {
style: 'currency',
currency: 'CNY'
}).format(amount);
}
/**
* 格式化数字
*/
export function formatNumber(num, precision = 2) {
return new Intl.NumberFormat('zh-CN', {
minimumFractionDigits: precision,
maximumFractionDigits: precision
}).format(num);
}
/**
* 格式化百分比
*/
export function formatPercent(value) {
return `${(value * 100).toFixed(1)}%`;
}
```
### 2.6 常量定义规范
```javascript
// pages/AgriculturalMachinery/Archive/MachineryEntry/constants.js
export const MACHINERY_CATEGORIES = [
{ value: '耕地机械', label: '耕地机械' },
{ value: '播种机械', label: '播种机械' },
{ value: '收获机械', label: '收获机械' },
{ value: '植保机械', label: '植保机械' }
];
export const MACHINERY_STATUS = [
{ value: '运行中', label: '运行中', color: '#52c41a' },
{ value: '空闲中', label: '空闲中', color: '#d9d9d9' },
{ value: '待维护', label: '待维护', color: '#faad14' },
{ value: '已报废', label: '已报废', color: '#ff4d4f' }
];
export const MANUFACTURERS = [
'约翰迪尔',
'久保田',
'福田雷沃',
'中国一拖',
'时风集团'
];
export const PAGE_SIZE_OPTIONS = [10, 20, 50, 100];
export const DEFAULT_PAGE_SIZE = 10;
```
## 3. 组件拆分示例
### 3.1 MachineryEntry 组件拆分
```
MachineryEntry/
├── index.jsx # 主组件
├── index.css # 主样式
├── index.types.ts # 主类型定义
├── hooks/ # 页面Hooks
│ ├── usePageData.js # 数据管理Hook
│ └── usePageActions.js # 操作Hook
├── utils/ # 工具函数
│ ├── pageHelpers.js # 页面工具函数
│ └── formatters.js # 格式化函数
├── constants.js # 常量定义
└── components/ # 子组件
├── MachineryFilter/ # 筛选组件
│ ├── index.jsx
│ ├── index.css
│ └── types.ts
├── MachineryList/ # 列表组件
│ ├── index.jsx
│ ├── index.css
│ ├── types.ts
│ └── components/
│ ├── MachineryTable/ # 表格组件
│ │ ├── index.jsx
│ │ ├── index.css
│ │ └── types.ts
│ └── TableActions/ # 表格操作组件
│ ├── index.jsx
│ ├── index.css
│ └── types.ts
├── MachineryForm/ # 表单组件
│ ├── index.jsx
│ ├── index.css
│ ├── types.ts
│ └── components/
│ ├── BasicInfo/ # 基本信息表单
│ ├── TechnicalInfo/ # 技术参数表单
│ ├── PurchaseInfo/ # 购买信息表单
│ └── InsuranceInfo/ # 保险信息表单
└── common/ # 通用组件
├── PageHeader/ # 页面头部
├── LoadingSpinner/ # 加载动画
└── EmptyState/ # 空状态
```
## 4. 文件命名规范
### 4.1 组件文件命名
- **主组件**: 使用功能名称,如 `MachineryEntry.jsx`
- **子组件**: 使用功能描述,如 `MachineryTable.jsx`
- **通用组件**: 使用通用描述,如 `TableHeader.jsx`
- **Hooks**: 以 `use` 开头,如 `usePageData.js`
- **工具函数**: 使用动词或名词,如 `pageHelpers.js`, `formatters.js`
- **类型文件**: 以 `.types.ts` 结尾,如 `index.types.ts`
- **样式文件**: 与组件同名,如 `index.css`
### 4.2 目录命名
- **页面目录**: 使用业务模块名,如 `AgriculturalMachinery`
- **组件目录**: 使用功能描述,如 `MachineryList`
- **通用目录**: 使用通用描述,如 `common`
## 5. 开发流程
### 5.1 新页面开发流程
1. **创建页面目录结构**
2. **定义类型接口** (index.types.ts)
3. **开发主组件** (index.jsx)
4. **拆分子组件** (components/)
5. **创建Hooks** (hooks/)
6. **编写工具函数** (utils/)
7. **定义常量** (constants.js)
8. **编写样式** (index.css)
9. **单元测试**
### 5.2 组件拆分流程
1. **识别拆分点**: 组件代码复杂度、复用性、职责单一性
2. **创建组件目录**: 创建 `components/ComponentName/` 目录
3. **提取组件逻辑**: 将相关代码移动到新组件
4. **定义Props接口**: 创建类型定义文件
5. **处理状态管理**: 使用Hooks或状态提升
6. **更新主组件**: 修改主组件使用新组件
7. **样式分离**: 创建独立的样式文件
8. **测试验证**: 确保功能正常
## 6. 质量保证
### 6.1 代码检查
- 使用ESLint检查代码规范
- 使用Prettier格式化代码
- 使用TypeScript进行类型检查
- 组件必须有PropTypes或TypeScript接口
### 6.2 性能优化
- 使用React.memo包装组件避免不必要的重渲染
- 使用useCallback和useMemo优化性能
- 实现虚拟滚动处理大数据量
- 使用懒加载和代码分割
### 6.3 可维护性
- 保持组件单一职责
- 提取可复用的通用组件
- 编写清晰的注释和文档
- 保持一致的代码风格
这个规范为pages目录的开发提供了完整的指导确保代码的可维护性、可扩展性和团队协作效率。