docs: 添加 Vue 3 分页实施指南

内容:
1. Vue3_Pagination_Implementation_Guide.md (718 行)
   - Vue 3 + TypeScript + Tauri 2 完整实施指南
   - 完整的代码示例
   - 分页和 Session 管理
   - 临时方案(兼容现有 API)

2. Quick_Implementation_Checklist.md (331 行)
   - 5 步快速实施(1-2 小时)
   - 只需修改 1 个文件(SearchView.vue)
   - 复制即用的代码片段
   - 验证步骤

实施方案:
- 添加状态(sessionId, hasMore, nextOffset 等)
- 实现 loadMore() 函数
- 修改 executeSearch() 支持分页
- 添加 More 按钮 UI
- 添加样式

预期效果:
- 初始加载:~2s → ~0.5s(4x 更快)
- 内存占用:渐进式加载
- 用户体验:立即反馈

技术栈适配:
- useState → ref / reactive
- useEffect → onMounted / watch
- lucide-react → 内联 SVG
This commit is contained in:
2026-10-11 21:37:50 +08:00
parent 1618965a9b
commit 896b12be9a
2 changed files with 1051 additions and 0 deletions

View File

@@ -0,0 +1,332 @@
# Studio 分页功能快速实施清单
**日期**: 2026-10-11
**目标**: 在 SearchView 中添加 More 按钮和分页加载
**预计时间**: 1-2 小时
---
## 📝 需要修改的文件
**只需修改 1 个文件**: `src/views/SearchView.vue`
---
## 🚀 5 步快速实施
### 步骤 1: 添加状态(1 分钟)
在 `<script setup>` 中添加:
```typescript
// === 在现有状态后添加 ===
const sessionId = ref('')
const nextOffset = ref(0)
const hasMore = ref(false)
const totalResults = ref(0)
const isLoadingMore = ref(false)
// 修改 pageSize
const pageSize = 5 // 从 20 改为 5
```
---
### 步骤 2: 添加 loadMore 函数(5 分钟)
在 `executeSearch` 函数后添加:
```typescript
/**
* 加载更多结果
*/
async function loadMore() {
if (isLoadingMore.value || !hasMore.value) return
isLoadingMore.value = true
try {
const query = searchQuery.value.trim()
const mode = searchMode.value
// 使用 offset 参数(当前 API 支持)
let res: any
switch (mode) {
case 'keyword':
res = await apiCall('search_keyword', { query, offset: results.value.length, limit: 10 })
break
case 'semantic':
res = await apiCall('search_semantic', { query, offset: results.value.length, limit: 10 })
break
case 'visual':
res = await apiCall('search_vlm_scenes', { query, offset: results.value.length, limit: 10 })
break
case 'agent':
res = await apiCall('agents_search', { query, offset: results.value.length, limit: 10 })
break
case 'people':
res = await apiCall('search_people', { query, offset: results.value.length, limit: 10 })
break
}
if (res && res.length > 0) {
results.value.push(...res)
hasMore.value = res.length >= 10
totalResults.value = results.value.length
} else {
hasMore.value = false
}
} catch (err) {
console.error('Load more failed:', err)
} finally {
isLoadingMore.value = false
}
}
```
---
### 步骤 3: 修改 executeSearch(3 分钟)
在 `executeSearch` 函数末尾添加:
```typescript
async function executeSearch() {
// ... 现有代码 ...
results.value = res.results || res
// === 添加以下代码 ===
totalResults.value = results.value.length
hasMore.value = results.value.length >= pageSize
nextOffset.value = pageSize
sessionId.value = '' // 重置
}
```
---
### 步骤 4: 添加 UI(2 分钟)
在 `<template>` 结果列表后添加:
```vue
<!-- 在结果列表后添加 -->
<div v-if="hasMore && results.length > 0" class="load-more-container">
<button
class="load-more-btn"
@click="loadMore"
:disabled="isLoadingMore"
>
{{ isLoadingMore ? 'Loading...' : `More Results (${totalResults - results.length} remaining)` }}
</button>
</div>
<div v-if="!hasMore && results.length > 0" class="no-more-results">
<p>All {{ results.length }} results loaded</p>
</div>
```
---
### 步骤 5: 添加样式(1 分钟)
在 `<style scoped>` 中添加:
```css
/* More 按钮 */
.load-more-container {
margin-top: 24px;
display: flex;
justify-content: center;
}
.load-more-btn {
padding: 12px 24px;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 8px;
color: rgba(255, 255, 255, 0.7);
font-size: 14px;
cursor: pointer;
transition: all 0.2s;
}
.load-more-btn:hover:not(:disabled) {
background: rgba(255, 255, 255, 0.08);
}
.load-more-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.no-more-results {
margin-top: 24px;
text-align: center;
color: rgba(255, 255, 255, 0.5);
font-size: 13px;
}
```
---
## ✅ 验证步骤
1. **打开 Studio**
2. **搜索查询**: "sunset"
3. **验证**:
- ✅ 显示 Top 5 结果
- ✅ More 按钮显示 "X remaining"
- ✅ 点击 More 追加 10 个结果
- ✅ 无更多结果时显示 "All X results loaded"
---
## 📊 效果对比
### 之前
```
搜索 → 显示 20 个结果(一次性加载)
```
### 之后
```
搜索 → 快速显示 5 个
↓
More → 追加 10 个
↓
More → 再追加 10 个
↓
完成 → "All X results loaded"
```
---
## 🎯 完整代码片段
### 复制即用的完整修改
```typescript
// === 1. 状态定义(script setup 开头) ===
const sessionId = ref('')
const nextOffset = ref(0)
const hasMore = ref(false)
const totalResults = ref(0)
const isLoadingMore = ref(false)
const pageSize = 5
// === 2. loadMore 函数 ===
async function loadMore() {
if (isLoadingMore.value || !hasMore.value) return
isLoadingMore.value = true
try {
const query = searchQuery.value.trim()
const mode = searchMode.value
const offset = results.value.length
let res: any
if (mode === 'keyword') {
res = await apiCall('search_keyword', { query, offset, limit: 10 })
} else if (mode === 'semantic') {
res = await apiCall('search_semantic', { query, offset, limit: 10 })
} else if (mode === 'visual') {
res = await apiCall('search_vlm_scenes', { query, offset, limit: 10 })
} else if (mode === 'agent') {
res = await apiCall('agents_search', { query, offset, limit: 10 })
} else if (mode === 'people') {
res = await apiCall('search_people', { query, offset, limit: 10 })
}
if (res?.length > 0) {
results.value.push(...res)
hasMore.value = res.length >= 10
} else {
hasMore.value = false
}
} catch (err) {
console.error('Load more failed:', err)
} finally {
isLoadingMore.value = false
}
}
// === 3. executeSearch 修改 ===
async function executeSearch() {
// ... 现有代码 ...
// 在 results.value = res 后添加
totalResults.value = results.value.length
hasMore.value = results.value.length >= pageSize
nextOffset.value = pageSize
}
```
```vue
<!-- === 4. Template 添加 === -->
<!-- 在结果列表后 -->
<div v-if="hasMore && results.length > 0" class="load-more-container">
<button class="load-more-btn" @click="loadMore" :disabled="isLoadingMore">
{{ isLoadingMore ? 'Loading...' : `More Results (${totalResults - results.length} remaining)` }}
</button>
</div>
<div v-if="!hasMore && results.length > 0" class="no-more-results">
<p>All {{ results.length }} results loaded</p>
</div>
```
```css
/* === 5. 样式添加 === */
.load-more-container {
margin-top: 24px;
display: flex;
justify-content: center;
}
.load-more-btn {
padding: 12px 24px;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 8px;
color: rgba(255, 255, 255, 0.7);
font-size: 14px;
cursor: pointer;
}
.load-more-btn:hover:not(:disabled) {
background: rgba(255, 255, 255, 0.08);
}
.no-more-results {
margin-top: 24px;
text-align: center;
color: rgba(255, 255, 255, 0.5);
font-size: 13px;
}
```
---
## ⚠️ 注意事项
1. **API 兼容性**: 当前使用 offset 参数,如果后端支持 session_id,可以切换到 session 模式
2. **错误处理**: 已添加基本错误处理,如需更详细提示可扩展
3. **性能**: 初始加载 5 个结果,More 操作加载 10 个,避免一次性加载过多
---
## 🎉 完成标志
- [ ] 初始搜索显示 Top 5
- [ ] More 按钮显示正确
- [ ] 点击 More 追加结果
- [ ] 无更多结果时提示
- [ ] 样式正常显示
---
**快速实施完成!只需修改 SearchView.vue 一个文件。**