diff --git a/docs/develop-guides/changelog.md b/docs/develop-guides/changelog.md
index 4ff8d481..f1646afd 100644
--- a/docs/develop-guides/changelog.md
+++ b/docs/develop-guides/changelog.md
@@ -33,6 +33,7 @@
- 优化图谱抽取器配置:未配置时在图谱中心展示配置入口,抽取方案收敛为 LLM,前端仅保留“更多拓展中”占位;LLM 抽取器使用固定 Prompt + 自定义 Schema,并支持模型参数与并发队列数;已配置后允许修改参数并提示重置重抽风险。修复上传并入库新文件时旧内存 metadata 覆盖数据库图谱配置的问题。
- 新增 Milvus 图谱检索链路:Query 可召回图谱实体和三元组,结合 Chunk 命中实体构造 seed entity,读取 Neo4j 2-hop 子图后用 igraph 执行 PPR,最终以 Chunk 为产物并通过 RRF 与原 Chunk 召回融合;检索配置改为 dataclass 元数据生成,支持 `depend_on` 控制重排序和图检索参数展示。
- 收紧用户管理部门隔离:普通管理员创建用户时固定归属本部门,用户列表、访问选项、详情、更新和删除接口均限制在本部门范围内。
+- 修复用户管理列表超过 100 人时被默认分页截断的问题:前端按 `skip/limit` 分批加载用户,并在用户卡片列表中补充分页渲染。
- 调整 Agent 资源默认选择与运行时上下文:未显式配置工具、知识库、MCP、Skills、子智能体时默认启用当前用户可访问/可用的全部资源,显式选择后按允许列表过滤;Agent 创建前统一完成最终资源权限过滤、知识库 `kb_id` 可见范围派生和 Skill prompt/readable 依赖闭包派生,聊天运行时与文件系统预览复用同一结果。
- 重构 Skills 权限与安装流程:Skill 增加 `source_type/share_config/enabled`,内置 Skill 作为启动同步入库的全局资源,不再保留前端安装/更新状态,支持启停但不允许删除;上传和远程添加统一为解析草稿后确认生效范围,安装 slug 优先读取 `SKILL.md` 的 `slug` 字段并保留 `name` 展示名,压缩包名称不参与 slug 校验;管理端支持编辑生效范围与启停;Agent 运行时按当前用户可访问 Skills 派生 prompt/readable 依赖闭包并限制挂载/激活,Skills prompt 改为模型请求级注入以避免污染 runtime context;主智能体恢复 `install_skill` 工具,允许当前用户安装私有 Skill 并激活当前会话,子智能体配置和运行态均禁用该工具。
- 精简历史兼容层:移除 sandbox provisioner `local` 后端别名、ask_user_question 单问题旧协议、JWT 历史默认密钥特殊判断、内置 Skill `SKILLS.md` 文件名回退、运行事件数字 seq 兼容和前端若干旧字段回退。
diff --git a/web/src/components/UserManagementComponent.vue b/web/src/components/UserManagementComponent.vue
index bb333c1d..882c22d7 100644
--- a/web/src/components/UserManagementComponent.vue
+++ b/web/src/components/UserManagementComponent.vue
@@ -69,7 +69,7 @@
/>
-
@@ -283,6 +293,8 @@ const userManagement = reactive({
searchKeyword: '',
departmentFilter: '',
roleFilter: '',
+ currentPage: 1,
+ pageSize: 50,
error: null,
modalVisible: false,
modalTitle: '添加用户',
@@ -356,6 +368,12 @@ const filteredUsers = computed(() => {
})
})
+const paginatedUsers = computed(() => {
+ const pageSize = Number(userManagement.pageSize)
+ const start = (userManagement.currentPage - 1) * pageSize
+ return filteredUsers.value.slice(start, start + pageSize)
+})
+
// 获取部门列表
const fetchDepartments = async () => {
if (!userStore.isSuperAdmin) return // 普通管理员不需要获取所有部门列表
@@ -427,6 +445,23 @@ watch(
}
)
+watch(
+ () => [userManagement.searchKeyword, userManagement.departmentFilter, userManagement.roleFilter],
+ () => {
+ userManagement.currentPage = 1
+ }
+)
+
+watch(
+ () => filteredUsers.value.length,
+ (total) => {
+ const maxPage = Math.max(1, Math.ceil(total / Number(userManagement.pageSize)))
+ if (userManagement.currentPage > maxPage) {
+ userManagement.currentPage = maxPage
+ }
+ }
+)
+
// 格式化时间显示
const formatTime = (timeStr) => formatDateTime(timeStr)
@@ -946,6 +981,12 @@ onMounted(async () => {
}
}
}
+
+ .pagination-section {
+ display: flex;
+ justify-content: flex-end;
+ margin-top: 16px;
+ }
}
}
diff --git a/web/src/stores/user.js b/web/src/stores/user.js
index 6ba4ff3a..90208696 100644
--- a/web/src/stores/user.js
+++ b/web/src/stores/user.js
@@ -146,19 +146,37 @@ export const useUserStore = defineStore('user', () => {
}
// 用户管理功能
- async function getUsers() {
+ async function getUsers({ pageSize = 100 } = {}) {
try {
- const response = await fetch('/api/auth/users', {
- headers: {
- ...getAuthHeaders()
- }
- })
+ const users = []
+ let skip = 0
- if (!response.ok) {
- throw new Error('获取用户列表失败')
+ while (true) {
+ const params = new URLSearchParams({
+ skip: String(skip),
+ limit: String(pageSize)
+ })
+ const response = await fetch(`/api/auth/users?${params.toString()}`, {
+ headers: {
+ ...getAuthHeaders()
+ }
+ })
+
+ if (!response.ok) {
+ throw new Error('获取用户列表失败')
+ }
+
+ const batch = await response.json()
+ users.push(...batch)
+
+ if (batch.length < pageSize) {
+ break
+ }
+
+ skip += pageSize
}
- return await response.json()
+ return users
} catch (error) {
console.error('获取用户列表错误:', error)
throw error