fix: 修复用户管理超过 100 人列表截断

This commit is contained in:
Wenjie Zhang 2026-06-09 22:31:12 +08:00
parent 5c496e6347
commit 2474fb0a6c
3 changed files with 70 additions and 10 deletions

View File

@ -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 兼容和前端若干旧字段回退。

View File

@ -69,7 +69,7 @@
/>
</div>
<div v-else class="user-cards-grid">
<div v-for="user in filteredUsers" :key="user.id" class="user-card">
<div v-for="user in paginatedUsers" :key="user.id" class="user-card">
<div class="card-header">
<div class="user-info-main">
<div class="user-avatar">
@ -152,6 +152,16 @@
</div>
</div>
</div>
<div v-if="filteredUsers.length > userManagement.pageSize" class="pagination-section">
<a-pagination
v-model:current="userManagement.currentPage"
v-model:page-size="userManagement.pageSize"
:total="filteredUsers.length"
:page-size-options="['20', '50', '100']"
show-size-changer
size="small"
/>
</div>
</div>
</a-spin>
</div>
@ -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;
}
}
}

View File

@ -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