# 借用列表加载速度优化方案

## 一、问题分析

列表加载慢可能来自以下方面：

| 来源 | 可能原因 |
|------|----------|
| **后端 API** | 数据库查询慢、N+1 查询、缺少索引、返回字段过多 |
| **前端请求** | 首次加载时并发请求多、请求参数冗余 |
| **前端渲染** | 列过多、复杂 formatter/cellRender、compute 计算量大 |

---

## 二、后端优化（需后端配合）

### 2.1 数据库层面

- **索引**：为常用筛选字段建索引（如 `status`、`create_datetime`、`borrow_type`）
- **分页**：确保使用 `limit/offset` 或游标分页，避免全表扫描

### 2.2 接口层面（Django 示例）

```python
# 使用 select_related / prefetch_related 减少 N+1 查询
queryset = BorrowApplication.objects.select_related(
    'borrower_info', 'creator'
).prefetch_related(
    'items'
).only('id', 'application_no', 'status', ...)  # 只返回列表需要的字段
```

### 2.3 返回字段精简

- 列表接口只返回表格展示所需字段
- 详情接口再返回完整数据

---

## 三、前端优化（可立即实施）

### 3.1 移除调试日志

`crud.tsx` 中 `pageRequest` 的 `console.log(query)` 会在每次请求时执行，生产环境应移除。

### 3.2 减少首屏列数

- 将非核心列默认 `show: false`，通过列设置由用户按需显示
- 当前已有部分列隐藏，可继续评估是否可再精简

### 3.3 优化 formatter

- 将 `dayjs` 等格式化逻辑尽量复用，避免每行重复创建
- 简单格式化可考虑后端直接返回格式化字符串

### 3.4 延迟加载弹窗组件

- 使用 `defineAsyncComponent` 或路由懒加载，减少首屏 bundle
- 弹窗在首次打开时再加载对应组件

### 3.5 请求参数优化

- 确保 `transformQuery` 只传递有值的搜索条件，减少无效参数

### 3.6 避免重复刷新

- `handleBorrowSubmit` 中已去除重复的 `doRefresh` 调用，仅保留 `crudExpose.doRefresh()`

### 3.7 首屏加载与 doSearch

- 若通过 URL 的 `status` 做初始筛选，`onMounted` 中会调用 `doSearch`。若 fast-crud 默认也会做一次初始请求，可能造成首屏请求两次，可考虑在 crud 配置中关闭默认首次加载，仅依赖 `doSearch` 触发

---

## 四、排查步骤

1. **定位瓶颈**：用浏览器 DevTools → Network 查看 `/api/system/borrow/application/` 请求耗时
   - 若接口耗时 > 1s，重点优化后端
   - 若接口很快但页面渲染慢，重点优化前端

2. **后端慢**：检查数据库慢查询、接口是否有 N+1、是否缺少索引

3. **前端慢**：检查列数量、formatter 复杂度、是否有大量 compute

---

## 五、列表缓存（已实现）

### 5.1 实现方式

- **工具**：`src/utils/requestCache.ts` 提供通用 `createRequestCache`
- **接入**：`crud.tsx` 中 `pageRequest` 使用缓存，相同查询参数在 TTL 内直接返回缓存
- **失效**：增删改、领取、归还等操作后调用 `invalidateBorrowListCache()` 再 `doRefresh()`，确保展示最新数据

### 5.2 配置

- **TTL**：默认 30 秒，可在 `crud.tsx` 中调整 `createRequestCache({ ttl: 30 * 1000 })`
- **keyPrefix**：`borrow:list:`，用于按业务隔离

### 5.3 适用场景

- 分页来回切换（如 1→2→1）时，相同页可立即展示
- 短时间内重复搜索相同条件时，减少后端请求

### 5.4 其他列表复用

其他 crud 列表如需缓存，可参考借用列表：

```ts
import { createRequestCache } from '/@/utils/requestCache';
const cache = createRequestCache({ ttl: 30 * 1000, keyPrefix: 'xxx:list:' });
// pageRequest 中：先 cache.get(query)，命中则返回；否则请求后 cache.set(query, res)
// 变更后：export invalidateXxxListCache，在刷新前调用
```

---

## 六、快速见效项（优先级）

1. 移除 `console.log`（立即）
2. 后端：为 `status`、`create_datetime` 等建索引
3. 后端：列表接口使用 `select_related`/`prefetch_related` 并精简返回字段
4. 前端：确认 `pageSize` 合理（当前 10 条/页已较小）
