|
|
@@ -725,13 +725,14 @@ async def save_knowledge(knowledge: KnowledgeIn):
|
|
|
|
|
|
@app.get("/api/knowledge")
|
|
|
def list_knowledge(
|
|
|
- limit: int = Query(default=100, ge=1, le=1000),
|
|
|
+ page: int = Query(default=1, ge=1),
|
|
|
+ page_size: int = Query(default=20, ge=1, le=100),
|
|
|
types: Optional[str] = None,
|
|
|
scopes: Optional[str] = None,
|
|
|
owner: Optional[str] = None,
|
|
|
tags: Optional[str] = None
|
|
|
):
|
|
|
- """列出知识(支持后端筛选)"""
|
|
|
+ """列出知识(支持后端筛选和分页)"""
|
|
|
try:
|
|
|
# 构建过滤表达式
|
|
|
filters = []
|
|
|
@@ -757,13 +758,33 @@ def list_knowledge(
|
|
|
# 如果没有过滤条件,查询所有
|
|
|
filter_expr = ' and '.join(filters) if filters else 'id != ""'
|
|
|
|
|
|
- # 查询 Milvus
|
|
|
- results = milvus_store.query(filter_expr, limit=limit)
|
|
|
+ # 查询 Milvus(先获取所有符合条件的数据)
|
|
|
+ # Milvus 的 limit 是总数限制,我们需要获取足够多的数据来支持分页
|
|
|
+ max_limit = 10000 # 设置一个合理的上限
|
|
|
+ results = milvus_store.query(filter_expr, limit=max_limit)
|
|
|
|
|
|
# 转换为可序列化的格式
|
|
|
serialized_results = [serialize_milvus_result(r) for r in results]
|
|
|
|
|
|
- return {"results": serialized_results, "count": len(serialized_results)}
|
|
|
+ # 按 created_at 降序排序(最新的在前)
|
|
|
+ serialized_results.sort(key=lambda x: x.get('created_at', 0), reverse=True)
|
|
|
+
|
|
|
+ # 计算分页
|
|
|
+ total = len(serialized_results)
|
|
|
+ total_pages = (total + page_size - 1) // page_size # 向上取整
|
|
|
+ start_idx = (page - 1) * page_size
|
|
|
+ end_idx = start_idx + page_size
|
|
|
+ page_results = serialized_results[start_idx:end_idx]
|
|
|
+
|
|
|
+ return {
|
|
|
+ "results": page_results,
|
|
|
+ "pagination": {
|
|
|
+ "page": page,
|
|
|
+ "page_size": page_size,
|
|
|
+ "total": total,
|
|
|
+ "total_pages": total_pages
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
except Exception as e:
|
|
|
print(f"[List Knowledge] 错误: {e}")
|
|
|
@@ -1431,6 +1452,17 @@ def frontend():
|
|
|
|
|
|
<!-- 知识列表 -->
|
|
|
<div id="knowledgeList" class="space-y-4"></div>
|
|
|
+
|
|
|
+ <!-- 分页控件 -->
|
|
|
+ <div id="pagination" class="flex justify-center items-center gap-4 mt-6 hidden">
|
|
|
+ <button onclick="goToPage(currentPage - 1)" id="prevBtn" class="bg-gray-600 hover:bg-gray-700 text-white px-4 py-2 rounded disabled:opacity-50 disabled:cursor-not-allowed">
|
|
|
+ 上一页
|
|
|
+ </button>
|
|
|
+ <span id="pageInfo" class="text-gray-700"></span>
|
|
|
+ <button onclick="goToPage(currentPage + 1)" id="nextBtn" class="bg-gray-600 hover:bg-gray-700 text-white px-4 py-2 rounded disabled:opacity-50 disabled:cursor-not-allowed">
|
|
|
+ 下一页
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
|
|
|
<!-- 新增/编辑 Modal -->
|
|
|
@@ -1481,6 +1513,10 @@ def frontend():
|
|
|
<script>
|
|
|
let allKnowledge = [];
|
|
|
let availableTags = [];
|
|
|
+ let currentPage = 1;
|
|
|
+ let pageSize = 20;
|
|
|
+ let totalPages = 1;
|
|
|
+ let totalCount = 0;
|
|
|
|
|
|
async function loadTags() {
|
|
|
const res = await fetch('/api/knowledge/meta/tags');
|
|
|
@@ -1500,9 +1536,10 @@ def frontend():
|
|
|
).join('');
|
|
|
}
|
|
|
|
|
|
- async function loadKnowledge() {
|
|
|
+ async function loadKnowledge(page = 1) {
|
|
|
const params = new URLSearchParams();
|
|
|
- params.append('limit', '1000');
|
|
|
+ params.append('page', page);
|
|
|
+ params.append('page_size', pageSize);
|
|
|
|
|
|
const selectedTypes = Array.from(document.querySelectorAll('.type-filter:checked')).map(el => el.value);
|
|
|
if (selectedTypes.length > 0) {
|
|
|
@@ -1533,7 +1570,11 @@ def frontend():
|
|
|
}
|
|
|
const data = await res.json();
|
|
|
allKnowledge = data.results || [];
|
|
|
+ currentPage = data.pagination.page;
|
|
|
+ totalPages = data.pagination.total_pages;
|
|
|
+ totalCount = data.pagination.total;
|
|
|
renderKnowledge(allKnowledge);
|
|
|
+ updatePagination();
|
|
|
} catch (error) {
|
|
|
console.error('加载错误:', error);
|
|
|
document.getElementById('knowledgeList').innerHTML = '<p class="text-red-500 text-center py-8">加载错误: ' + error.message + '</p>';
|
|
|
@@ -1541,7 +1582,29 @@ def frontend():
|
|
|
}
|
|
|
|
|
|
function applyFilters() {
|
|
|
- loadKnowledge();
|
|
|
+ currentPage = 1; // 重置到第一页
|
|
|
+ loadKnowledge(currentPage);
|
|
|
+ }
|
|
|
+
|
|
|
+ function goToPage(page) {
|
|
|
+ if (page < 1 || page > totalPages) return;
|
|
|
+ loadKnowledge(page);
|
|
|
+ }
|
|
|
+
|
|
|
+ function updatePagination() {
|
|
|
+ const paginationDiv = document.getElementById('pagination');
|
|
|
+ const pageInfo = document.getElementById('pageInfo');
|
|
|
+ const prevBtn = document.getElementById('prevBtn');
|
|
|
+ const nextBtn = document.getElementById('nextBtn');
|
|
|
+
|
|
|
+ if (totalPages <= 1) {
|
|
|
+ paginationDiv.classList.add('hidden');
|
|
|
+ } else {
|
|
|
+ paginationDiv.classList.remove('hidden');
|
|
|
+ pageInfo.textContent = `第 ${currentPage} / ${totalPages} 页 (共 ${totalCount} 条)`;
|
|
|
+ prevBtn.disabled = currentPage === 1;
|
|
|
+ nextBtn.disabled = currentPage === totalPages;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
function renderKnowledge(list) {
|