142 lines
4.5 KiB
Markdown
142 lines
4.5 KiB
Markdown
# 分页规范
|
||
|
||
## 概述
|
||
|
||
项目统一使用 `pageNo` / `pageSize` 作为分页参数,通过 `BaseService.get_page()` → `Repository.page()` → `BaseRepository.find_page()` 链路完成分页查询,返回标准分页结构。
|
||
|
||
:::tip 分页约定
|
||
- 前端传递 `pageNo`(页码,从 1 开始)和 `pageSize`(每页条数)
|
||
- 后端返回分页数据字典,包含 `items` / `total` / `page` / `pageSize` / `pages` 等字段
|
||
- 默认每页 10 条(`PAGE_SIZE = 10`)
|
||
:::
|
||
|
||
## 分页流程
|
||
|
||
```
|
||
前端请求 GET /api/v1/position/page?pageNo=1&pageSize=10&name=工程师
|
||
↓
|
||
Router 层:R.ok(data=position.get_position_page(request.args))
|
||
↓
|
||
Service 层:BaseService.get_page(params)
|
||
↓
|
||
Repository 层:PositionRepository.page(params)
|
||
↓
|
||
BaseRepository.find_page(page, page_size, filters, like_filters, order_by)
|
||
↓
|
||
返回 (items, total, pages)
|
||
↓
|
||
build_page_data 构建分页数据字典
|
||
```
|
||
|
||
## Repository 层实现
|
||
|
||
```python
|
||
# src/modules/system/position/repository.py
|
||
from src.common.base_repository import BaseRepository
|
||
from src.common.constants.common import PAGE_SIZE
|
||
from src.modules.system.position.models import Position
|
||
|
||
|
||
class PositionRepository(BaseRepository):
|
||
model = Position
|
||
|
||
@classmethod
|
||
def page(cls, params: dict):
|
||
"""
|
||
岗位分页查询
|
||
|
||
Args:
|
||
params: 查询参数字典
|
||
- pageNo: 当前页码
|
||
- pageSize: 每页记录数
|
||
- name: 岗位名称(模糊匹配)
|
||
- status: 状态(精确匹配)
|
||
|
||
Returns:
|
||
tuple: (items, total, pages)
|
||
"""
|
||
return cls.find_page(
|
||
page=int(params.get('pageNo', 1)),
|
||
page_size=int(params.get('pageSize', PAGE_SIZE)),
|
||
filters={'status': params.get('status')},
|
||
like_filters={'name': params.get('name')},
|
||
order_by='sort',
|
||
)
|
||
```
|
||
|
||
## Service 层调用
|
||
|
||
```python
|
||
# src/common/base_service.py
|
||
from src.common.utils.model_utils import build_page_data
|
||
|
||
class BaseService:
|
||
@classmethod
|
||
def get_page(cls, params: dict) -> dict:
|
||
"""分页查询"""
|
||
page = int(params.get("pageNo", 1))
|
||
page_size = int(params.get("pageSize", PAGE_SIZE))
|
||
items, total, pages = cls.repository.page(params)
|
||
return build_page_data(
|
||
items, total, page, page_size, pages,
|
||
enrich=cls._enrich_page_records,
|
||
)
|
||
```
|
||
|
||
## Router 层调用
|
||
|
||
```python
|
||
# src/modules/system/position/router.py
|
||
@position_blue.route('/page', methods=["GET"])
|
||
@check_permission("sys:position:page")
|
||
def page():
|
||
"""查询岗位分页数据"""
|
||
return R.ok(data=position.get_position_page(request.args))
|
||
```
|
||
|
||
## 分页参数说明
|
||
|
||
| 参数 | 类型 | 说明 | 默认值 |
|
||
|:---|:---|:---|:---|
|
||
| `pageNo` | int | 当前页码(从 1 开始) | 1 |
|
||
| `pageSize` | int | 每页记录数 | 10 |
|
||
|
||
## 筛选参数说明
|
||
|
||
| 筛选类型 | Repository 参数 | 说明 | SQL 效果 |
|
||
|:---|:---|:---|:---|
|
||
| 等值匹配 | `filters={'status': 1}` | 精确匹配 | `WHERE status = 1` |
|
||
| 模糊匹配 | `like_filters={'name': '工程师'}` | 模糊匹配 | `WHERE name LIKE '%工程师%'` |
|
||
| 范围匹配 | `range_filters={'create_time': (start, end)}` | 范围匹配 | `WHERE create_time BETWEEN start AND end` |
|
||
|
||
:::tip 空值跳过
|
||
所有筛选条件自动跳过 `None` 和空字符串 `''`,前端不传某参数时不会影响查询结果。
|
||
:::
|
||
|
||
## 前端调用示例
|
||
|
||
```typescript
|
||
// ui/src/api/system/position/index.ts
|
||
export const getPositionPageApi = (params: any) => {
|
||
return http.request('get', '/position/page', { params });
|
||
};
|
||
|
||
// 使用
|
||
const { data } = await getPositionPageApi({ pageNo: 1, pageSize: 10, name: '工程师' });
|
||
// data.items = [...]
|
||
// data.total = 100
|
||
// data.pages = 10
|
||
```
|
||
|
||
## 开发要点
|
||
|
||
1. **分页参数统一使用 `pageNo` / `pageSize`**:前端和后端保持一致
|
||
2. **Repository 的 `page(params)` 方法必须实现**:Service 的 `get_page` 依赖此方法
|
||
3. **使用 `find_page` 构建查询**:传入 `filters` / `like_filters` / `range_filters` / `order_by`
|
||
4. **空值自动跳过**:前端不传的参数不会影响查询条件
|
||
5. **软删除自动过滤**:`find_page` 自动添加 `is_delete = 0` 条件
|
||
|
||
## 总结
|
||
|
||
分页查询通过 `Service.get_page(params)` → `Repository.page(params)` → `BaseRepository.find_page(...)` 链路完成。筛选条件通过 `filters`(等值)、`like_filters`(模糊)、`range_filters`(范围)三类参数传入,空值自动跳过,软删除自动过滤。
|