diff --git a/ui/src/api/API_README.md b/ui/src/api/API_README.md index b7b82d69752..6245f95e447 100644 --- a/ui/src/api/API_README.md +++ b/ui/src/api/API_README.md @@ -168,6 +168,17 @@ FormData;保存返回完整配置,页面以该返回值作为唯一数据来 `admin/auth/current-user.ts` 的 postCurrentUserPassword 向 `/user/current/reset_password` 提交 RSA 加密的 `{ encryptedData }`;调用方校验与加密,成功清除凭据后进入登录。 +### 系统用户列表排序 + +系统用户分页排序以 `order_by` 传入 `create_time`;对话用户分页传入 `create_time` 或 `used_tokens`, +后者从列字段 `token_quota.used_tokens` 映射。升序使用字段原名,降序添加 `-`,取消排序不传值。 +排序由服务端处理,不做前端当前页排序。 + +### 操作日志排序 + +操作日志列表与导出共用查询条件,操作时间排序通过 `order_by` 传入 `create_time` 或 `-create_time`, +取消排序不传值。 + ### 平台许可证 `admin/system/license.ts` 通过 GET、PUT `/license/profile` 查询和更新授权信息, @@ -592,7 +603,8 @@ Workspace 共享 Paragraph API 仅 getParagraphPage、getParagraphDetail,不 四范围 `knowledge/problem.ts` 使用各自知识库前缀追加 `//problem`, 由知识库及文档详情上下文的 `resourceApi.problem` 选择,不固定 Workspace。 -`getProblemsPage(knowledgeId, page, query)` 返回 `ResponsePage`,query 仅传 content。 +`getProblemsPage(knowledgeId, page, query)` 返回 `ResponsePage`,query 支持 content 和 order_by。 +排序字段为 paragraph_count、hit_num、last_hit_time、create_time、update_time,降序在字段名前加 `-`。 `ProblemItem` 包含 id、content、paragraph_count、hit_num、last_hit_time、create_time、update_time; 分段已有问题仍使用轻量 `ParagraphProblem`,不要求包含列表统计字段。 diff --git a/ui/src/api/admin/system/resource-management/knowledge/problem.ts b/ui/src/api/admin/system/resource-management/knowledge/problem.ts index f558fc797dc..8ba835c26a0 100644 --- a/ui/src/api/admin/system/resource-management/knowledge/problem.ts +++ b/ui/src/api/admin/system/resource-management/knowledge/problem.ts @@ -1,6 +1,6 @@ import { del, get, post, put } from '@/api/admin/core/request' import type { ParamsPage, ResponsePage } from '@/api/admin/core/types' -import type { ProblemItem, ProblemBatchAssociationPayload, ProblemParagraph } from '@/api/types' +import type { Dict, ProblemItem, ProblemBatchAssociationPayload, ProblemParagraph } from '@/api/types' const getPrefix = (knowledgeId: string) => `/system/resource/knowledge/${knowledgeId}/problem` @@ -8,7 +8,7 @@ const getPrefix = (knowledgeId: string) => `/system/resource/knowledge/${knowled const postProblems = (knowledgeId: string, contents: string[]) => post(getPrefix(knowledgeId), contents) /** 分页搜索知识库问题,供问题列表及分段关联候选使用。 */ -const getProblemsPage = (knowledgeId: string, page: ParamsPage, query?: { content?: string }) => +const getProblemsPage = (knowledgeId: string, page: ParamsPage, query?: Dict) => get>(`${getPrefix(knowledgeId)}/${page.currentPage}/${page.pageSize}`, query) /** 编辑单个知识库问题的内容。 */ diff --git a/ui/src/api/admin/system/shared-resources/knowledge/problem.ts b/ui/src/api/admin/system/shared-resources/knowledge/problem.ts index f2ebfc09c5d..163b622790e 100644 --- a/ui/src/api/admin/system/shared-resources/knowledge/problem.ts +++ b/ui/src/api/admin/system/shared-resources/knowledge/problem.ts @@ -1,6 +1,6 @@ import { del, get, post, put } from '@/api/admin/core/request' import type { ParamsPage, ResponsePage } from '@/api/admin/core/types' -import type { ProblemItem, ProblemBatchAssociationPayload, ProblemParagraph } from '@/api/types' +import type { Dict, ProblemItem, ProblemBatchAssociationPayload, ProblemParagraph } from '@/api/types' const getPrefix = (knowledgeId: string) => `/system/shared/knowledge/${knowledgeId}/problem` @@ -8,7 +8,7 @@ const getPrefix = (knowledgeId: string) => `/system/shared/knowledge/${knowledge const postProblems = (knowledgeId: string, contents: string[]) => post(getPrefix(knowledgeId), contents) /** 分页搜索知识库问题,供问题列表及分段关联候选使用。 */ -const getProblemsPage = (knowledgeId: string, page: ParamsPage, query?: { content?: string }) => +const getProblemsPage = (knowledgeId: string, page: ParamsPage, query?: Dict) => get>(`${getPrefix(knowledgeId)}/${page.currentPage}/${page.pageSize}`, query) /** 编辑单个知识库问题的内容。 */ diff --git a/ui/src/api/admin/workspace/knowledge/problem.ts b/ui/src/api/admin/workspace/knowledge/problem.ts index d85890bf069..0e2fa55f2bd 100644 --- a/ui/src/api/admin/workspace/knowledge/problem.ts +++ b/ui/src/api/admin/workspace/knowledge/problem.ts @@ -1,6 +1,6 @@ import { del, get, post, put } from '../../core/request' import type { ParamsPage, ResponsePage } from '../../core/types' -import type { ProblemItem, ProblemBatchAssociationPayload, ProblemParagraph } from '@/api/types' +import type { Dict, ProblemItem, ProblemBatchAssociationPayload, ProblemParagraph } from '@/api/types' import { getWorkspaceId } from '@/utils/resource-context' const getPrefix = (knowledgeId: string) => `/workspace/${getWorkspaceId()}/knowledge/${knowledgeId}/problem` @@ -9,7 +9,7 @@ const getPrefix = (knowledgeId: string) => `/workspace/${getWorkspaceId()}/knowl const postProblems = (knowledgeId: string, contents: string[]) => post(getPrefix(knowledgeId), contents) /** 分页搜索知识库问题,供问题列表及分段关联候选使用。 */ -const getProblemsPage = (knowledgeId: string, page: ParamsPage, query?: { content?: string }) => +const getProblemsPage = (knowledgeId: string, page: ParamsPage, query?: Dict) => get>(`${getPrefix(knowledgeId)}/${page.currentPage}/${page.pageSize}`, query) /** 编辑单个知识库问题的内容。 */ diff --git a/ui/src/api/admin/workspace/shared/knowledge/problem.ts b/ui/src/api/admin/workspace/shared/knowledge/problem.ts index dfc96ee8690..3eb3f7c6382 100644 --- a/ui/src/api/admin/workspace/shared/knowledge/problem.ts +++ b/ui/src/api/admin/workspace/shared/knowledge/problem.ts @@ -1,12 +1,12 @@ import { get } from '../../../core/request' import type { ParamsPage, ResponsePage } from '../../../core/types' -import type { ProblemItem } from '@/api/types' +import type { Dict, ProblemItem } from '@/api/types' import { getWorkspaceId } from '@/utils/resource-context' const getPrefix = (knowledgeId: string) => `/system/shared/workspace/${getWorkspaceId()}/knowledge/${knowledgeId}/problem` /** 分页查询工作空间共享知识库的问题,只提供只读访问。 */ -const getProblemsPage = (knowledgeId: string, page: ParamsPage, query?: { content?: string }) => +const getProblemsPage = (knowledgeId: string, page: ParamsPage, query?: Dict) => get>(`${getPrefix(knowledgeId)}/${page.currentPage}/${page.pageSize}`, query) export default { getProblemsPage } diff --git a/ui/src/views/VIEW_README.md b/ui/src/views/VIEW_README.md index 074d7edf195..958c1c479df 100644 --- a/ui/src/views/VIEW_README.md +++ b/ui/src/views/VIEW_README.md @@ -95,6 +95,8 @@ - Python 生成入口由 `CodeGenerate` 选择 Tool/Model API,复用 `GenerateContent`;全屏替换只修改草稿,确认后回写。 - System 模型/工具使用表格,保留既有权限、引用数量删除提示和末页回退;不新增不存在的创建/导入能力。 工作空间列与选项仅企业版显示,筛选 `workspace_ids` 使用 JSON 数组字符串。 +- System 资源管理智能体列表支持发布时间、创建时间排序;知识库、工具、模型列表支持更新时间、创建时间排序。 + 通过 `toOrderBy` 将时间列转换为 `order_by` 查询参数,切换排序回第一页;搜索、筛选及分页保留排序,取消排序不传值。 - Workspace 共享知识库只读;System 共享资源页面按自身 API 维护,不组合 Workspace 文件夹移动等操作。 - System 共享知识库、模型、工具菜单复用功能本地 `authorized-workspace-action/index.vue`,打开同目录的 `AuthorizedWorkspaceDialog` 配置工作空间白名单/黑名单;保留弹窗实例,关闭动画结束后清空本次数据, @@ -208,7 +210,7 @@ System 资源管理或 System 共享 Knowledge API 的 `postKnowledgeHitTest`; - 名称使用 `MkEditName`,最长 128,禁止 `: \ / ? * [ ]`;成功按 ID 更新名称与时间。 - 文件状态、启用、命中处理用 `MkTableFilter single`,标签用 `cascader`;改变条件回第一页并清空选择,保留其他条件。 状态查询同步更新 `status` 与 `task_type`;布尔 false 保留,“全部”省略条件;标签传叶子 ID 或 `NO_TAG`。 -- 字符数使用服务端表头排序,通过 `toOrderBy` 更新独立的 `orderBy`,请求时作为 `order_by` 传入;切换排序回第一页并清空选择, +- 字符数、分段数、召回次数、最后一次召回时间、创建者、创建时间和更新时间使用服务端表头排序,通过 `toOrderBy` 更新独立的 `orderBy`,请求时作为 `order_by` 传入;切换排序回第一页并清空选择, 取消排序恢复后端默认顺序。搜索、筛选、分页和轮询保留当前排序,导入成功清空搜索及筛选时也保留排序。 - 任务解析及筛选统一在 `document/status.ts`,展示由 `DocumentStatus.vue` 负责;不复用执行记录状态。 汇总优先级为取消中、执行中、排队中、失败、取消完成、成功;忽略未知和 n,保留取消完成显示成功的既有规则。 @@ -253,7 +255,8 @@ System 资源管理或 System 共享 Knowledge API 的 `postKnowledgeHitTest`; ### 问题管理 -`problem/` 使用 `resourceApi.problem`,默认每页 20 条,不提供表头排序,搜索将 content 传给后端。 +`problem/` 使用 `resourceApi.problem`,默认每页 20 条,搜索将 content 传给后端。 +关联分段数、召回次数、最近一次召回时间、创建时间和更新时间使用 `order_by` 表头排序;切换排序清空选择并查询第一页,搜索和分页保留排序。 `ButtonCreateProblem` 内聚创建按钮和多行输入弹窗;快捷创建使用 `MkQuickCreate`,每个问题最多 256 字符。 单项与批量删除直接提交,删除后末页为空时回退到最后有效页;名称编辑复用 `MkEditName`。 点击数据行打开 `ProblemDetailDrawer`,勾选、名称编辑和操作按钮不触发行详情。 @@ -324,7 +327,6 @@ View 负责路由、资源与模式、图加载、保存/发布/调试编排 - 工具、知识库执行记录抽屉按需挂载,关闭后卸载;知识库记录每 6 秒刷新,关闭停止。 执行时间列声明 `sortable`,独立 `orderBy` 通过 `toOrderBy` 添加 `ett.` 前缀,以 `order` 查询参数传入。 切换排序回第一页;搜索、筛选、分页与轮询保留排序,重新打开时恢复默认排序。 - 当前本地后端两类接口仍固定按执行时间降序,需支持 `order` 后才能实际响应升序请求。 ## 其他页面约束 @@ -372,12 +374,14 @@ View 负责路由、资源与模式、图加载、保存/发布/调试编排 概览内暂时隐藏卡片详情入口,不接入排行抽屉、搜索、分页或导出。 - 触发器执行记录的执行时间列使用服务端排序,独立 `orderBy` 经 `toOrderBy` 添加 `ett.` 前缀, 通过查询参数 `order` 传入。切换排序回第一页;搜索、分页和详情跨页保留排序,重新打开抽屉恢复默认排序。 +- 触发器列表的下次执行时间、创建时间使用 `order_by` 表头排序,切换时清空选择并回到第一页;搜索、筛选和分页保留排序。 - 触发器表单共用创建/编辑,保留任务 ID 和参数;资源模式固定单任务,完整输入定义不足时补查详情。 保存校验全部折叠任务;参数初始化不覆盖已配置值,移除事件引用时恢复自定义默认值。 - 智能体/知识库对话用户页使用双栏布局和当前页授权;智能体从 context 取 Workspace/System 资源管理 ChatUser API,知识库取四范围 API。 自动授权立即保存,手动保存只提交当前页;请求共用 loading 与 inert,失败保留勾选。 授权人数取 `authorized_count`,不以分页勾选推算。 - 对话日志筛选、导出使用同组查询;无勾选导出筛选结果,日期清空禁用导出。 + 对话次数、对话用户、最近对话时间使用 `order_by` 表头排序,用户列映射为 `username`;切换时清空选择并回到第一页,筛选、分页和导出保留排序。 清除策略通过当前范围 ChatLog API 保存、使用弹窗自身 loading;添加知识库固定所选对话 ID,选项使用当前范围 Knowledge/Document API。 `ChatLogDetailDrawer` 使用现有 `LogConversation` 展示只读历史对话与执行详情,正文查询仍使用 Workspace API;上一条/下一条与跨页由页面管理。 - 外部检索页从 context 读取四范围 ExternalRetrieval API;Workspace 共享只读,禁用开关并阻止提交。 @@ -388,6 +392,11 @@ View 负责路由、资源与模式、图加载、保存/发布/调试编排 默认登录方式仅展示草稿中已选的 `login_value`;跨域地址按行清理,取消不回写。 - 用户导入、改密、批量角色/配额流程留在各自业务目录;工作空间成员按用户联动角色行选择, 删除按 `user_relation_id` 去重,不按用户 ID 丢弃角色记录。 +- 系统用户列表的创建时间、对话用户列表的 Tokens 使用量和创建时间使用服务端表头排序。 + 切换排序清空选择并回到第一页,搜索、筛选和分页保留排序;取消排序恢复后端默认顺序。 + 排序参数见 API 文档。 +- 系统操作日志的操作时间使用服务端表头排序,切换后回到第一页,保留搜索及筛选条件。 + 分页和导出沿用当前排序条件,取消排序不传排序值。 - 登录页使用 `formEl.validate` 回调和清晰的 LDAP/本地分支,不额外抽象或改写提交控制流程。 - Chat/Portal 页面以 auth token 为面板 key,token 变化重建并加载用户档案;路由认证见路由文档。 会话装配在 conversation-panel,列表 Store 不感知 Router;门户复用 chat 装配,保留各智能体最近会话分组。 diff --git a/ui/src/views/application-detail/chat-log/index.vue b/ui/src/views/application-detail/chat-log/index.vue index f785dcf2666..67ec499a72d 100644 --- a/ui/src/views/application-detail/chat-log/index.vue +++ b/ui/src/views/application-detail/chat-log/index.vue @@ -7,6 +7,7 @@ import MkDateRange from '@/components/mk-date-range/index.vue' import type { MkDateRangeValue } from '@/components/mk-date-range/types' import type { ResourceDetailPageProps } from '@/layout/ResourceDetailLayout.vue' import { beforeDay, datetimeFormat } from '@/utils/time' +import { toOrderBy } from '@/utils/common' import { useApplicationDetailContext } from '../context' import ButtonCleanStrategy from './clean-strategy/ButtonCleanStrategy.vue' import ButtonAddKnowledge from './add-knowledge/ButtonAddKnowledge.vue' @@ -22,6 +23,7 @@ const { application, applicationId, resourceApi } = useApplicationDetailContext( // 日志搜索与日期筛选 const loading = ref(false) const chatLogs = ref([]) +const orderBy = ref() const paginationConfig = ref({ currentPage: 1, pageSize: 20, total: 0 }) const sourceOptions: OptionItem[] = Object.entries(CHAT_LOG_SOURCE_LABELS).map(([value, label]) => ({ value, label })) const searchFields: OptionItem[] = [ @@ -59,11 +61,20 @@ function handleFeedbackChange() { const chatLogQuery = computed>(() => ({ ...chatLogSearchQuery.value, ...chatLogDateQuery.value, + order_by: orderBy.value, ...(selectedFeedback.value === 'star' ? { min_star: 1 } : {}), ...(selectedFeedback.value === 'trample' ? { min_trample: 1 } : {}), comparer: 'and', })) +/* 表头排序:清空选择并重新查询第一页。 */ +function handleSortChange(sort: Parameters[0]) { + orderBy.value = toOrderBy(sort, (prop) => (prop === 'asker.username' ? 'username' : prop)) + chatLogTableRef.value?.clearSelection() + paginationConfig.value.currentPage = 1 + return loadChatLogs() +} + function loadChatLogs(currentPage = paginationConfig.value.currentPage): Promise { // 自定义日期清空后等待重新选择,避免提交后端不接受的空日期。 if (!chatLogDateQuery.value.start_time) { @@ -190,13 +201,14 @@ onMounted(() => loadChatLogs()) @current-change="loadChatLogs()" @size-change="loadChatLogs()" @selection-change="handleSelectionChange" + @sort-change="handleSortChange" @row-click="handleRowClick" row-class-name="cursor-pointer" resizable > - + - + @@ -225,7 +237,7 @@ onMounted(() => loadChatLogs()) - + - + - + - - + + - + diff --git a/ui/src/views/knowledge-detail/problem/index.vue b/ui/src/views/knowledge-detail/problem/index.vue index b53cf2d4094..a2bcea6cd84 100644 --- a/ui/src/views/knowledge-detail/problem/index.vue +++ b/ui/src/views/knowledge-detail/problem/index.vue @@ -6,6 +6,7 @@ import type MkQuickCreate from '@/components/global/mk-table/MkQuickCreate.vue' import MkEditName from '@/components/mk-edit-name/index.vue' import type { ResourceDetailPageProps } from '@/layout/ResourceDetailLayout.vue' import { datetimeFormat } from '@/utils/time' +import { toOrderBy } from '@/utils/common' import { numberFormat } from '@/utils/number' import { MsgSuccess } from '@/utils/message' import { isWorkspaceSharedResource } from '@/utils/resource-context' @@ -23,6 +24,7 @@ const loading = ref(false) const operationLoading = ref(false) const problems = ref([]) const searchText = ref('') +const orderBy = ref() const paginationConfig = ref({ currentPage: 1, pageSize: 20, total: 0 }) const selectedProblems = ref([]) const problemTableRef = ref<{ clearSelection: () => void }>() @@ -47,7 +49,11 @@ function clearSelection() { function loadProblems(currentPage = paginationConfig.value.currentPage): Promise { loading.value = true return resourceApi.problem - .getProblemsPage(knowledgeId.value, { currentPage, pageSize: paginationConfig.value.pageSize }, { content: searchText.value.trim() || undefined }) + .getProblemsPage( + knowledgeId.value, + { currentPage, pageSize: paginationConfig.value.pageSize }, + { content: searchText.value.trim() || undefined, order_by: orderBy.value }, + ) .then(({ records, total }) => { const lastPage = Math.max(1, Math.ceil(total / paginationConfig.value.pageSize)) if (currentPage > lastPage) return loadProblems(lastPage) @@ -72,6 +78,12 @@ function handleSearchChange() { return loadProblems(1) } +/* 表头排序:清空选择并重新查询第一页。 */ +function handleSortChange(sort: Parameters[0]) { + orderBy.value = toOrderBy(sort) + return handleSearchChange() +} + function handleSelectionChange(selection: unknown[]) { selectedProblems.value = selection as ProblemItem[] } @@ -198,6 +210,7 @@ onMounted(() => loadProblems()) @current-change="loadProblems()" @size-change="loadProblems()" @selection-change="handleSelectionChange" + @sort-change="handleSortChange" @row-click="handleOpenProblem" > - - + + - + - + - + diff --git a/ui/src/views/system/chat-management/users/index.vue b/ui/src/views/system/chat-management/users/index.vue index 5ea806cf2a2..cdc48aa5eea 100644 --- a/ui/src/views/system/chat-management/users/index.vue +++ b/ui/src/views/system/chat-management/users/index.vue @@ -5,6 +5,7 @@ import type { ChatUser, Dict, LoginMethod, OptionItem } from '@/api/types' import { LOGIN_METHOD, QUOTA_TYPE } from '@/api/enums' import { LOGIN_METHOD_LABELS } from '@/constants' import { datetimeFormat } from '@/utils/time' +import { toOrderBy } from '@/utils/common' import { MsgConfirm, MsgSuccess } from '@/utils/message' import { formatTokenNumber } from '@/utils/number' import UserFromDrawer from './UserFromDrawer.vue' @@ -26,6 +27,7 @@ const userTableRef = useTemplateRef('userTableRef') const chatUsersLoading = ref(false) const paginationConfig = ref({ currentPage: 1, pageSize: 20, total: 0 }) const chatUsersData = ref([]) +const orderBy = ref() const searchFields: OptionItem[] = [ { label: '姓名', value: 'nick_name' }, { label: '用户名', value: 'username' }, @@ -46,6 +48,7 @@ const sourceOptions: OptionItem[] = Object.entries(LOGIN_METHOD_LABELS). })) const chatUserQuery = computed>(() => ({ ...chatUserSearchQuery.value, + order_by: orderBy.value, ...(selectedStatus.value !== null ? { is_active: selectedStatus.value } : {}), ...(selectedSource.value !== null ? { source: selectedSource.value } : {}), })) @@ -65,7 +68,14 @@ function handleSearchChange(query?: Dict) { function handleFilterChange() { paginationConfig.value.currentPage = 1 - loadChatUsers() + return loadChatUsers() +} + +/* 表头排序:清空选择并重新查询第一页。 */ +function handleSortChange(sort: Parameters[0]) { + orderBy.value = toOrderBy(sort, (prop) => (prop === 'token_quota.used_tokens' ? 'used_tokens' : prop)) + userTableRef.value?.clearSelection() + return handleFilterChange() } function loadChatUsers(resetQuery = false) { @@ -165,6 +175,7 @@ onMounted(() => loadChatUsers()) @current-change="loadChatUsers()" @size-change="loadChatUsers()" @selection-change="handleBatchSelectionChange" + @sort-change="handleSortChange" > @@ -204,7 +215,7 @@ onMounted(() => loadChatUsers()) - + @@ -216,7 +227,7 @@ onMounted(() => loadChatUsers()) - + diff --git a/ui/src/views/system/identity/users/index.vue b/ui/src/views/system/identity/users/index.vue index c412d47cf1c..0edd2945ad4 100644 --- a/ui/src/views/system/identity/users/index.vue +++ b/ui/src/views/system/identity/users/index.vue @@ -6,6 +6,7 @@ import { LOGIN_METHOD, ROLE_TYPE } from '@/api/enums' import type { Dict, LoginMethod, OptionItem, SystemUser } from '@/api/types' import { MsgConfirm, MsgSuccess } from '@/utils/message' import { datetimeFormat } from '@/utils/time' +import { toOrderBy } from '@/utils/common' import { LOGIN_METHOD_LABELS } from '@/constants/auth.ts' import WorkspaceRelationTags from '@/components/business/workspace-relation-tags/index.vue' import UserFromDrawer from './UserFromDrawer.vue' @@ -28,6 +29,7 @@ const userTableRef = useTemplateRef('userTableRef') const systemUsersLoading = ref(false) const paginationConfig = ref({ currentPage: 1, pageSize: 20, total: 0 }) const systemUsersData = ref([]) +const orderBy = ref() const searchFields: OptionItem[] = [ { label: '姓名', value: 'nick_name' }, @@ -44,6 +46,7 @@ const selectedSource = ref(null) const systemUserQuery = computed>(() => ({ ...systemUserSearchQuery.value, + order_by: orderBy.value, ...(selectedStatus.value !== null ? { is_active: selectedStatus.value } : {}), ...(selectedSource.value !== null ? { source: selectedSource.value } : {}), })) @@ -56,7 +59,14 @@ function handleSearchChange(query?: Dict) { function handleHeaderFilterChange() { paginationConfig.value.currentPage = 1 - loadSystemUsers() + return loadSystemUsers() +} + +/* 表头排序:清空选择并重新查询第一页。 */ +function handleSortChange(sort: Parameters[0]) { + orderBy.value = toOrderBy(sort) + userTableRef.value?.clearSelection() + return handleHeaderFilterChange() } function loadSystemUsers(resetQuery = false) { @@ -158,6 +168,7 @@ onMounted(() => loadSystemUsers()) @current-change="loadSystemUsers()" @size-change="loadSystemUsers()" @selection-change="handleBatchSelectionChange" + @sort-change="handleSortChange" > @@ -236,7 +247,7 @@ onMounted(() => loadSystemUsers()) - + diff --git a/ui/src/views/system/operate-logs/index.vue b/ui/src/views/system/operate-logs/index.vue index d1716d7616f..74bf6d68dbb 100644 --- a/ui/src/views/system/operate-logs/index.vue +++ b/ui/src/views/system/operate-logs/index.vue @@ -8,6 +8,7 @@ import type { MkDateRangeValue } from '@/components/mk-date-range/types' import ButtonCleanStrategy from './clean-strategy/ButtonCleanStrategy.vue' import OperateLogDetailDialog from './dialog/OperateLogDetailDialog.vue' import { beforeDay, datetimeFormat } from '@/utils/time' +import { toOrderBy } from '@/utils/common' import { useStore } from '@/stores' import perm from '@/permission/index.ts' @@ -15,6 +16,7 @@ const { auth } = useStore() /* 日志筛选与列表 */ const loading = ref(false) const operateLogs = ref([]) +const orderBy = ref() const paginationConfig = ref({ currentPage: 1, pageSize: 20, total: 0 }) const searchFields: OptionItem[] = [ { label: '操作详情', value: 'operate' }, @@ -89,11 +91,19 @@ function handleStatusChange() { const operateLogQuery = computed>(() => ({ ...operateLogDateQuery.value, ...operateLogSearchQuery.value, + order_by: orderBy.value, ...(selectedOperateMenus.value.length ? { menu: JSON.stringify(selectedOperateMenus.value) } : {}), ...(selectedWorkspaceIds.value.length ? { workspace_ids: JSON.stringify(selectedWorkspaceIds.value) } : {}), ...(selectedStatus.value != null ? { status: selectedStatus.value } : {}), })) +/* 表头排序:重新查询第一页,保留当前筛选。 */ +function handleSortChange(sort: Parameters[0]) { + orderBy.value = toOrderBy(sort) + paginationConfig.value.currentPage = 1 + return loadOperateLogs() +} + function loadOperateLogs(resetQuery = false) { if (resetQuery) { operateLogSearchQuery.value = undefined @@ -154,6 +164,7 @@ onMounted(() => { :data="operateLogs" @current-change="loadOperateLogs()" @size-change="loadOperateLogs()" + @sort-change="handleSortChange" :max-table-height="200" resizable > @@ -206,7 +217,7 @@ onMounted(() => { - + diff --git a/ui/src/views/system/resource-management/application/index.vue b/ui/src/views/system/resource-management/application/index.vue index 12d6ff07ea8..765b1de720b 100644 --- a/ui/src/views/system/resource-management/application/index.vue +++ b/ui/src/views/system/resource-management/application/index.vue @@ -9,7 +9,7 @@ import type { ApplicationDetail, Dict, OptionItem } from '@/api/types' import { APPLICATION_TYPE } from '@/api/enums' import { useStore } from '@/stores' import { datetimeFormat } from '@/utils/time' -import { isWorkFlow } from '@/utils/common' +import { isWorkFlow, toOrderBy } from '@/utils/common' import { AuthorizeApplicationAction, ExportApplicationAction, @@ -31,6 +31,7 @@ const operationLoading = ref(false) const applicationData = ref([]) const pagination = ref({ currentPage: 1, pageSize: 20, total: 0 }) const applicationQuery = ref>() +const orderBy = ref() const creatorOptions = ref[]>([]) const workspaceOptions = ref[]>([]) const selectedWorkspaceIds = ref([]) @@ -60,6 +61,7 @@ function loadApplicationPage() { loading.value = true return SystemResourceApplicationApi.getApplicationPage(pagination.value, { ...applicationQuery.value, + order_by: orderBy.value, ...(selectedWorkspaceIds.value.length ? { workspace_ids: JSON.stringify(selectedWorkspaceIds.value) } : {}), ...(selectedStatus.value ? { status: selectedStatus.value } : {}), ...(selectedType.value ? { type: selectedType.value } : {}), @@ -83,6 +85,12 @@ function handleFilterChange() { return loadApplicationPage() } +/* 表头排序:保留筛选并重新查询第一页。 */ +function handleSortChange(sort: Parameters[0]) { + orderBy.value = toOrderBy(sort) + return handleFilterChange() +} + onMounted(() => { loadApplicationPage() if (auth.isEE) { @@ -107,6 +115,7 @@ onMounted(() => { :max-table-height="210" @current-change="loadApplicationPage" @size-change="loadApplicationPage" + @sort-change="handleSortChange" resizable > @@ -150,10 +159,10 @@ onMounted(() => { - + - + diff --git a/ui/src/views/system/resource-management/knowledge/index.vue b/ui/src/views/system/resource-management/knowledge/index.vue index 771cafd966e..d5a98d61263 100644 --- a/ui/src/views/system/resource-management/knowledge/index.vue +++ b/ui/src/views/system/resource-management/knowledge/index.vue @@ -8,6 +8,7 @@ import SystemWorkspaceApi from '@/api/admin/system/workspace' import type { KnowledgeItem, Dict, OptionItem } from '@/api/types' import { useStore } from '@/stores' import { datetimeFormat } from '@/utils/time' +import { toOrderBy } from '@/utils/common' import { KNOWLEDGE_TYPE } from '@/api/enums' import SystemRelatedResourcesApi from '@/api/admin/system/resource-management/related-resources' @@ -47,6 +48,7 @@ const knowledgeOperationLoading = ref(false) const knowledgeData = ref([]) const pagination = ref({ currentPage: 1, pageSize: 20, total: 0 }) const KnowledgeQuery = ref>() +const orderBy = ref() const creatorOptions = ref[]>([]) const workspaceOptions = ref[]>([]) const selectedWorkspaceIds = ref([]) @@ -72,6 +74,7 @@ function loadKnowledgePage() { loading.value = true return SystemResourceKnowledgeApi.getKnowledgePage(pagination.value, { ...KnowledgeQuery.value, + order_by: orderBy.value, ...(selectedWorkspaceIds.value.length ? { workspace_ids: JSON.stringify(selectedWorkspaceIds.value) } : {}), ...(selectedType.value !== null && selectedType.value !== undefined ? { type: selectedType.value } : {}), }) @@ -107,6 +110,12 @@ function handleFilterChange() { return loadKnowledgePage() } +/* 表头排序:保留筛选并重新查询第一页。 */ +function handleSortChange(sort: Parameters[0]) { + orderBy.value = toOrderBy(sort) + return handleFilterChange() +} + onMounted(() => { loadKnowledgePage() if (auth.isEE) { @@ -131,6 +140,7 @@ onMounted(() => { :max-table-height="210" @current-change="loadKnowledgePage" @size-change="loadKnowledgePage" + @sort-change="handleSortChange" resizable > @@ -155,10 +165,10 @@ onMounted(() => { - + - + diff --git a/ui/src/views/system/resource-management/model/index.vue b/ui/src/views/system/resource-management/model/index.vue index 98172636d74..3e94df51770 100644 --- a/ui/src/views/system/resource-management/model/index.vue +++ b/ui/src/views/system/resource-management/model/index.vue @@ -9,6 +9,7 @@ import type { Dict, ModelItem, ModelProviderItem, OptionItem } from '@/api/types import { MODEL_TYPE_LABELS } from '@/constants' import { useStore } from '@/stores' import { datetimeFormat } from '@/utils/time' +import { toOrderBy } from '@/utils/common' import { MsgConfirm, MsgSuccess } from '@/utils/message' import { EditModelAction, AuthorizeModelAction, ParamSettingAction, RelatedResourcesModelAction } from '@/views/model/model-card/action-dropdown' import perm from '@/permission' @@ -20,6 +21,7 @@ const loading = ref(false) const modelsData = ref([]) const pagination = ref({ currentPage: 1, pageSize: 20, total: 0 }) const modelQuery = ref>() +const orderBy = ref() const creatorOptions = ref[]>([]) const workspaceOptions = ref[]>([]) const selectedModelType = ref('') @@ -41,6 +43,7 @@ function loadModels() { loading.value = true return SystemModelApi.getModelPage(pagination.value, { ...modelQuery.value, + order_by: orderBy.value, ...(selectedModelType.value ? { model_type: selectedModelType.value } : {}), ...(selectedProvider.value ? { provider: selectedProvider.value } : {}), ...(selectedWorkspaceIds.value.length ? { workspace_ids: JSON.stringify(selectedWorkspaceIds.value) } : {}), @@ -64,6 +67,12 @@ function handleFilterChange() { return loadModels() } +/* 表头排序:保留筛选并重新查询第一页。 */ +function handleSortChange(sort: Parameters[0]) { + orderBy.value = toOrderBy(sort) + return handleFilterChange() +} + /* 供应商展示 */ const providers = ref([]) const providerById = computed(() => new Map(providers.value.map((provider) => [provider.provider, provider]))) @@ -120,6 +129,7 @@ onMounted(() => { :max-table-height="210" @current-change="loadModels" @size-change="loadModels" + @sort-change="handleSortChange" resizable > @@ -154,10 +164,10 @@ onMounted(() => { - + - + diff --git a/ui/src/views/system/resource-management/tool/index.vue b/ui/src/views/system/resource-management/tool/index.vue index 0c16b307982..e0f082bea1a 100644 --- a/ui/src/views/system/resource-management/tool/index.vue +++ b/ui/src/views/system/resource-management/tool/index.vue @@ -12,6 +12,7 @@ import { TOOL_TYPE } from '@/api/enums' import { TOOL_TYPE_OPTIONS } from '@/constants' import { useStore } from '@/stores' import { datetimeFormat } from '@/utils/time' +import { toOrderBy } from '@/utils/common' import { MsgConfirm, MsgSuccess } from '@/utils/message' import ToolStatusSwitch from '@/views/tool/tool-card/ToolStatusSwitch.vue' import { @@ -35,6 +36,7 @@ const operationLoading = ref(false) const tools = ref([]) const pagination = ref({ currentPage: 1, pageSize: 20, total: 0 }) const toolQuery = ref>() +const orderBy = ref() const creatorOptions = ref[]>([]) const workspaceOptions = ref[]>([]) const selectedToolType = ref('') @@ -66,6 +68,7 @@ function loadTools() { loading.value = true return SystemToolApi.getToolPage(pagination.value, { ...toolQuery.value, + order_by: orderBy.value, ...(selectedToolType.value ? { tool_type: selectedToolType.value } : {}), ...(selectedSource.value ? { source: selectedSource.value } : {}), ...(selectedStatus.value ? { status: selectedStatus.value } : {}), @@ -90,6 +93,12 @@ function handleFilterChange() { return loadTools() } +/* 表头排序:保留筛选并重新查询第一页。 */ +function handleSortChange(sort: Parameters[0]) { + orderBy.value = toOrderBy(sort) + return handleFilterChange() +} + /* 工具编辑、启停与工作流入口 */ function handleToolUpdate(tool: ToolItem) { const index = tools.value.findIndex(({ id }) => id === tool.id) @@ -149,6 +158,7 @@ onMounted(() => { :max-table-height="210" @current-change="loadTools" @size-change="loadTools" + @sort-change="handleSortChange" resizable > @@ -183,10 +193,10 @@ onMounted(() => { - + - + diff --git a/ui/src/views/trigger/index.vue b/ui/src/views/trigger/index.vue index 4466caef7b9..e1ca16dc6fe 100644 --- a/ui/src/views/trigger/index.vue +++ b/ui/src/views/trigger/index.vue @@ -6,6 +6,7 @@ import CommonApi from '@/api/admin/workspace/common' import { TRIGGER_TYPE } from '@/api/enums' import type { Dict, OptionItem, Trigger, TriggerType } from '@/api/types' import { datetimeFormat } from '@/utils/time' +import { toOrderBy } from '@/utils/common' import { MsgConfirm, MsgSuccess } from '@/utils/message' import TriggerFormDrawer from './trigger-form/TriggerFormDrawer.vue' import TriggerTaskPopover from './components/TriggerTaskPopover.vue' @@ -16,6 +17,7 @@ const loading = ref(false) const triggerData = ref([]) const paginationConfig = ref({ currentPage: 1, pageSize: 20, total: 0 }) const triggerSearchQuery = ref>({}) +const orderBy = ref() const triggerTypeLabels: Record = { [TRIGGER_TYPE.SCHEDULED]: '定时触发', [TRIGGER_TYPE.EVENT]: '事件触发', @@ -35,6 +37,7 @@ const selectedTriggerType = ref(null) const selectedStatus = ref(null) const triggerQuery = computed>(() => ({ ...triggerSearchQuery.value, + order_by: orderBy.value, ...(selectedTriggerType.value ? { type: selectedTriggerType.value } : {}), ...(selectedStatus.value !== null ? { is_active: selectedStatus.value } : {}), })) @@ -70,6 +73,12 @@ function handleFilterChange() { loadTriggers() } +/* 表头排序:清空选择并重新查询第一页。 */ +function handleSortChange(sort: Parameters[0]) { + orderBy.value = toOrderBy(sort) + handleFilterChange() +} + /* 新建、编辑 */ const recordDrawerRef = ref>() const triggerDrawerRef = ref>() @@ -176,6 +185,7 @@ onMounted(() => loadTriggers()) :max-table-height="200" @current-change="loadTriggers" @size-change="loadTriggers" + @sort-change="handleSortChange" resizable > @@ -212,14 +222,14 @@ onMounted(() => loadTriggers()) - + - +