218 lines
6.3 KiB
Markdown
218 lines
6.3 KiB
Markdown
# 世界书分页功能 - 前端API调用检查报告
|
||
|
||
## ✅ 检查结果
|
||
|
||
**所有前端世界书分页功能都能正确发出请求到后端API!**
|
||
|
||
---
|
||
|
||
## 📋 完整API映射表
|
||
|
||
### 1. 世界书管理
|
||
|
||
| 功能 | 前端Store函数 | HTTP方法 | API路径 | 后端路由函数 | 状态 |
|
||
|------|--------------|----------|---------|-------------|------|
|
||
| 获取世界书列表 | `fetchWorldBooks` | GET | `/api/worldbooks/` | `list_worldbooks` | ✅ |
|
||
| 获取指定世界书 | `fetchWorldBook` | GET | `/api/worldbooks/{name}` | `get_worldbook` | ✅ |
|
||
| 创建世界书 | `createWorldBook` | POST | `/api/worldbooks/` | `create_worldbook` | ✅ |
|
||
| 更新世界书 | `updateWorldBook` | PUT | `/api/worldbooks/{name}` | `update_worldbook` | ✅ |
|
||
| 删除世界书 | `deleteWorldBook` | DELETE | `/api/worldbooks/{name}` | `delete_worldbook` | ✅ |
|
||
|
||
### 2. 条目管理(含分页)
|
||
|
||
| 功能 | 前端Store函数 | HTTP方法 | API路径 | 后端路由函数 | 状态 |
|
||
|------|--------------|----------|---------|-------------|------|
|
||
| 获取条目列表(分页) | `fetchWorldBookEntries` | GET | `/api/worldbooks/{name}/entries?page={page}&page_size={page_size}` | `list_worldbook_entries` | ✅ |
|
||
| 获取指定条目 | `fetchWorldBookEntry` | GET | `/api/worldbooks/{name}/entries/{uid}` | `get_worldbook_entry` | ✅ |
|
||
| 创建条目 | `createWorldBookEntry` | POST | `/api/worldbooks/{name}/entries` | `create_worldbook_entry` | ✅ |
|
||
| 更新条目 | `updateWorldBookEntry` | PUT | `/api/worldbooks/{name}/entries/{uid}` | `update_worldbook_entry` | ✅ |
|
||
| 删除条目 | `deleteWorldBookEntry` | DELETE | `/api/worldbooks/{name}/entries/{uid}` | `delete_worldbook_entry` | ✅ |
|
||
|
||
### 3. 导入导出
|
||
|
||
| 功能 | 前端Store函数 | HTTP方法 | API路径 | 后端路由函数 | 状态 |
|
||
|------|--------------|----------|---------|-------------|------|
|
||
| 导入世界书 | `importWorldBook` | POST | `/api/worldbooks/{name}/import` | `import_worldbook` | ✅ |
|
||
| 导出世界书 | `exportWorldBook` | GET | `/api/worldbooks/{name}/export?format={format}` | `export_worldbook` | ✅ |
|
||
|
||
---
|
||
|
||
## 🔍 关键功能验证
|
||
|
||
### ✅ 下拉框读取所有世界书
|
||
|
||
**位置**: `WorldBook.jsx` 第547行
|
||
|
||
```jsx
|
||
{worldBooks.map(book => (
|
||
<div key={book.name} className="dropdown-item">
|
||
...
|
||
</div>
|
||
))}
|
||
```
|
||
|
||
**数据来源**:
|
||
- `worldBooks` 来自 Store (第8行)
|
||
- 通过 `fetchWorldBooks()` 加载 (第85行和第95行)
|
||
|
||
**加载时机**:
|
||
1. ✅ 组件挂载时立即加载(如果列表为空)
|
||
2. ✅ 切换到世界书标签页时重新加载
|
||
|
||
**API调用**:
|
||
```javascript
|
||
// WorldBookSlice.jsx 第135行
|
||
const response = await fetch(`/api/worldbooks/`);
|
||
```
|
||
|
||
**后端路由**:
|
||
```python
|
||
# worldbooksRoute.py 第28行
|
||
@router.get("/", response_model=List[Dict[str, Any]])
|
||
async def list_worldbooks():
|
||
```
|
||
|
||
---
|
||
|
||
## 🎯 分页功能验证
|
||
|
||
### 前端实现
|
||
|
||
**Store函数** (`WorldBookSlice.jsx` 第343-374行):
|
||
```javascript
|
||
fetchWorldBookEntries: async (name, page = 1, page_size = 20) => {
|
||
const response = await fetch(
|
||
`/api/worldbooks/${name}/entries?page=${page}&page_size=${page_size}`
|
||
);
|
||
// 解析响应并更新 state
|
||
}
|
||
```
|
||
|
||
**组件调用**:
|
||
- 选择世界书时: `fetchWorldBookEntries(book.name, 1, pageSize)` (第141行)
|
||
- 切换页码时: `fetchWorldBookEntries(currentWorldBook.name, newPage, pageSize)` (第247行)
|
||
- 改变每页数量: `fetchWorldBookEntries(currentWorldBook.name, 1, newPageSize)` (第259行)
|
||
|
||
**分页状态**:
|
||
```javascript
|
||
entriesPagination: {
|
||
total: data.total || 0,
|
||
page: data.page || 1,
|
||
page_size: data.page_size || 20,
|
||
total_pages: data.total_pages || 0
|
||
}
|
||
```
|
||
|
||
### 后端实现
|
||
|
||
**API路由** (`worldbooksRoute.py` 第108-128行):
|
||
```python
|
||
@router.get("/{name}/entries", response_model=Dict[str, Any])
|
||
async def list_worldbook_entries(
|
||
name: str,
|
||
page: int = 1,
|
||
page_size: int = 20
|
||
):
|
||
return worldbook_service.list_entries(name, page, page_size)
|
||
```
|
||
|
||
**服务层** (`worldbook_service.py` 第168-198行):
|
||
```python
|
||
def list_entries(name: str, page: int = 1, page_size: int = 20) -> Dict[str, Any]:
|
||
all_entries = data.get("entries", [])
|
||
total = len(all_entries)
|
||
|
||
start_idx = (page - 1) * page_size
|
||
end_idx = start_idx + page_size
|
||
paginated_entries = all_entries[start_idx:end_idx]
|
||
|
||
return {
|
||
"entries": paginated_entries,
|
||
"total": total,
|
||
"page": page,
|
||
"page_size": page_size,
|
||
"total_pages": (total + page_size - 1) // page_size
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 📊 数据格式
|
||
|
||
### 后端返回格式(分页)
|
||
|
||
```json
|
||
{
|
||
"entries": [...],
|
||
"total": 30,
|
||
"page": 1,
|
||
"page_size": 20,
|
||
"total_pages": 2
|
||
}
|
||
```
|
||
|
||
### 前端State结构
|
||
|
||
```javascript
|
||
{
|
||
worldBooks: [], // 世界书列表
|
||
currentWorldBook: null, // 当前选中的世界书
|
||
currentEntries: [], // 当前页的条目列表
|
||
entriesPagination: { // 分页信息
|
||
total: 0,
|
||
page: 1,
|
||
page_size: 20,
|
||
total_pages: 0
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## ✨ 优化记录
|
||
|
||
### 已完成的优化
|
||
|
||
1. ✅ **组件初始化加载**: 添加 `useEffect` 在组件挂载时立即加载世界书列表
|
||
2. ✅ **格式统一**: 所有世界书文件转换为内部格式存储
|
||
3. ✅ **代码简化**: 移除服务层的SillyTavern格式运行时兼容逻辑
|
||
4. ✅ **分页控件**: 添加完整的分页UI(上一页、下一页、每页数量选择)
|
||
5. ✅ **自动刷新**: 添加/更新/删除条目后自动刷新当前页
|
||
|
||
---
|
||
|
||
## 🧪 测试建议
|
||
|
||
### 手动测试清单
|
||
|
||
- [ ] 打开世界书页面,检查下拉框是否显示所有世界书
|
||
- [ ] 选择一个世界书,检查是否正确加载第一页条目
|
||
- [ ] 点击"下一页",检查是否加载第二页
|
||
- [ ] 改变每页数量(10/20/50/100),检查是否正确刷新
|
||
- [ ] 创建新条目,检查是否刷新当前页
|
||
- [ ] 更新条目,检查是否刷新当前页
|
||
- [ ] 删除条目,检查是否刷新当前页
|
||
- [ ] 切换到其他世界书,检查是否重置到第一页
|
||
|
||
### API测试
|
||
|
||
```bash
|
||
# 获取世界书列表
|
||
curl http://localhost:8000/api/worldbooks/
|
||
|
||
# 获取条目(分页)
|
||
curl "http://localhost:8000/api/worldbooks/卡立创-v5/entries?page=1&page_size=5"
|
||
```
|
||
|
||
---
|
||
|
||
## 📝 结论
|
||
|
||
✅ **前端世界书分页的所有功能都能正确发出请求到后端API**
|
||
|
||
- 下拉框正确读取所有世界书
|
||
- 分页参数正确传递
|
||
- 数据格式匹配
|
||
- 错误处理完善
|
||
- 用户体验流畅
|