Files
momentry_studio/docs/Quick_Implementation_Checklist.md
Momentry Studio 896b12be9a 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
2026-10-11 21:37:50 +08:00

7.3 KiB
Raw Blame History

Studio 分页功能快速实施清单

日期: 2026-10-11
目标: 在 SearchView 中添加 More 按钮和分页加载
预计时间: 1-2 小时


📝 需要修改的文件

只需修改 1 个文件: src/views/SearchView.vue


🚀 5 步快速实施

步骤 1: 添加状态(1 分钟)

在 <script setup> 中添加:

// === 在现有状态后添加 ===
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 函数后添加:

/**
 * 加载更多结果
 */
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 函数末尾添加:

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> 结果列表后添加:

<!-- 在结果列表后添加 -->
<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> 中添加:

/* 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"

🎯 完整代码片段

复制即用的完整修改

// === 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
}
<!-- === 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>
/* === 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 一个文件。