Files
smart-crop-ui/docs/Pages目录结构与开发规范.md

779 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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目录的开发提供了完整的指导确保代码的可维护性、可扩展性和团队协作效率。