diff --git a/ui/src/api/API_README.md b/ui/src/api/API_README.md index 1a0fc51bd27..65771a72cab 100644 --- a/ui/src/api/API_README.md +++ b/ui/src/api/API_README.md @@ -498,9 +498,18 @@ System 接口由资源管理服务提供;本地开源后端没有对应扩展 `workspace/knowledge/document.ts` 仅维护文档分页查询,路径为 `/workspace//knowledge//document//`。 -分页使用 `ParamsPage` / `ResponsePage`,支持 `name`、`create_user`、`status`、`task_type` 筛选; +分页使用 `ParamsPage` / `ResponsePage`,支持 `name`、`create_user`、`status`、`task_type`、`is_active`、`hit_handling_method` 和 `tags` 筛选; 文件任务使用 `DOCUMENT_TASK_STATE` 的字符状态及 `DOCUMENT_TASK_TYPE` 的任务位置, 不使用通用 `STATE_TYPES`;选择全部时省略状态与任务类型,非任务专属状态省略任务类型。 +文件状态响应还包含 `REVOKE: '4'`、`REVOKED: '5'`、`IGNORED: 'n'`。 +`DocumentItem.status_meta` 使用可空的 `DocumentStatusMeta`:`aggs` 为 `{ status, count }[]`, +`state_time` 按任务编号、状态字符两级索引时间字符串;两项均可缺省。 +`DocumentTaskState` 和 `DocumentTaskType` 从文档枚举派生,统一从 `@/api/types` 导入。 +启用状态传布尔值,`false` 不省略;单选全部时省略该字段。`tags` 传数组,沿用 Axios 的 +`tags[]` 序列化,内容为标签 ID 或特殊值 `NO_TAG`,不传标签名称或 JSON 字符串。 +`workspace/knowledge/knowledge.ts` 和 `workspace/shared/knowledge/knowledge.ts` 的 +`getKnowledgeTags(knowledgeId)` 分别请求对应资源前缀的 `//tags`,返回 +`KnowledgeTagGroup[]`;页面将分组转换为级联选项,MkTableFilter 内部展开父组并去重,页面直接用组件返回的叶子标签 ID 数组查询。 文档页传 `resource_type: 'document'`,创建者选项复用 Workspace `common.ts` 的 `getAllUsers`。 类型从 `@/api/types` 导入,命中处理枚举从 `@/api/enums` 导入。API 不接收 loading,不重复包装响应。 diff --git a/ui/src/api/admin/workspace/knowledge/knowledge.ts b/ui/src/api/admin/workspace/knowledge/knowledge.ts index 216a997cd75..d75de553233 100644 --- a/ui/src/api/admin/workspace/knowledge/knowledge.ts +++ b/ui/src/api/admin/workspace/knowledge/knowledge.ts @@ -1,6 +1,14 @@ import { del, get, getExportFile, post, put } from '../../core/request' import type { ParamsPage, ResponsePage } from '../../core/types' -import type { Dict, KnowledgeDetail, KnowledgeItem, KnowledgeCreatePayload, WebKnowledgeCreatePayload, LarkKnowledgeCreatePayload } from '@/api/types' +import type { + Dict, + KnowledgeTagGroup, + KnowledgeDetail, + KnowledgeItem, + KnowledgeCreatePayload, + WebKnowledgeCreatePayload, + LarkKnowledgeCreatePayload, +} from '@/api/types' import { getWorkspaceId } from '@/utils/resource-context' const getPrefix = () => { @@ -99,7 +107,11 @@ const getKnowledgeMcpConfig = (knowledgeId: string): Promise => /** 提交知识库分词索引任务。 */ const putKnowledgeKeywordIndex = (knowledgeId: string) => put(`${getPrefix()}/${knowledgeId}/tokenize`) +/** 获取知识库按标签名称分组的标签选项。 */ +const getKnowledgeTags = (knowledgeId: string) => get(`${getPrefix()}/${knowledgeId}/tags`) + export default { + getKnowledgeTags, getKnowledgeMcpConfig, putKnowledgeKeywordIndex, exportKnowledgeExcel, diff --git a/ui/src/api/admin/workspace/shared/knowledge/knowledge.ts b/ui/src/api/admin/workspace/shared/knowledge/knowledge.ts index c74ec55a775..acee1cd1679 100644 --- a/ui/src/api/admin/workspace/shared/knowledge/knowledge.ts +++ b/ui/src/api/admin/workspace/shared/knowledge/knowledge.ts @@ -1,6 +1,6 @@ import { get } from '../../../core/request' import type { ParamsPage, ResponsePage } from '../../../core/types' -import type { Dict, KnowledgeDetail, KnowledgeItem } from '@/api/types' +import type { Dict, KnowledgeTagGroup, KnowledgeDetail, KnowledgeItem } from '@/api/types' import { getWorkspaceId } from '@/utils/resource-context' const getPrefix = () => `/system/shared/workspace/${getWorkspaceId()}/knowledge` @@ -15,4 +15,7 @@ const getAllKnowledge = (query?: Dict) => get(getPrefi /** 获取工作空间共享的知识库详情。 */ const getKnowledgeDetail = (knowledgeId: string) => get(`${getPrefix()}/${knowledgeId}`) -export default { getKnowledgePage, getAllKnowledge, getKnowledgeDetail } +/** 获取知识库按标签名称分组的标签选项。 */ +const getKnowledgeTags = (knowledgeId: string) => get(`${getPrefix()}/${knowledgeId}/tags`) + +export default { getKnowledgeTags, getKnowledgePage, getAllKnowledge, getKnowledgeDetail } diff --git a/ui/src/api/enums/document.ts b/ui/src/api/enums/document.ts index 27971188a6f..0b1f5d8b4ec 100644 --- a/ui/src/api/enums/document.ts +++ b/ui/src/api/enums/document.ts @@ -2,7 +2,7 @@ export const DOCUMENT_HIT_HANDLING = { OPTIMIZATION: 'optimization', DIRECTLY_RETURN: 'directly_return' } as const /** 文档任务状态,使用文件状态协议中的字符值。 */ -export const DOCUMENT_TASK_STATE = { PENDING: '0', STARTED: '1', SUCCESS: '2', FAILURE: '3' } as const +export const DOCUMENT_TASK_STATE = { PENDING: '0', STARTED: '1', SUCCESS: '2', FAILURE: '3', REVOKE: '4', REVOKED: '5', IGNORED: 'n' } as const /** 文档状态字符串中各任务的位置。 */ export const DOCUMENT_TASK_TYPE = { EMBEDDING: 1, GENERATE_PROBLEM: 2, SYNC: 3, TOKENIZE: 4 } as const diff --git a/ui/src/api/enums/state.ts b/ui/src/api/enums/state.ts index 7542d89eaa3..454bdb704c3 100644 --- a/ui/src/api/enums/state.ts +++ b/ui/src/api/enums/state.ts @@ -2,6 +2,10 @@ export const STATE_TYPES = { PENDING: 'PENDING', STARTED: 'STARTED', + EMBEDDING: 'EMBEDDING', + GENERATE: 'GENERATE', + SYNC: 'SYNC', + TOKENIZE: 'TOKENIZE', SUCCESS: 'SUCCESS', FAILURE: 'FAILURE', REVOKE: 'REVOKE', diff --git a/ui/src/api/types/document.ts b/ui/src/api/types/document.ts index ba5b10ef1c3..5211d5d6a50 100644 --- a/ui/src/api/types/document.ts +++ b/ui/src/api/types/document.ts @@ -1,6 +1,15 @@ -import type { DOCUMENT_HIT_HANDLING } from '@/api/enums' +import type { DOCUMENT_HIT_HANDLING, DOCUMENT_TASK_STATE, DOCUMENT_TASK_TYPE } from '@/api/enums' import type { KnowledgeType } from './knowledge' export type DocumentHitHandling = (typeof DOCUMENT_HIT_HANDLING)[keyof typeof DOCUMENT_HIT_HANDLING] +export type DocumentTaskState = (typeof DOCUMENT_TASK_STATE)[keyof typeof DOCUMENT_TASK_STATE] +export type DocumentTaskType = (typeof DOCUMENT_TASK_TYPE)[keyof typeof DOCUMENT_TASK_TYPE] + +/** 文件各任务的分段计数及状态发生时间。 */ +export interface DocumentStatusMeta { + aggs?: { status: string; count: number }[] + state_time?: Partial>>> +} + /** 文档分页列表数据。 */ export interface DocumentItem { id: string @@ -9,13 +18,14 @@ export interface DocumentItem { type: KnowledgeType is_active: boolean status: string - status_meta?: Record + status_meta?: DocumentStatusMeta | null paragraph_count: number char_length: number hit_num: number hit_handling_method: DocumentHitHandling directly_return_similarity: number tags?: { id: string; key: string; value: string }[] + tag_count?: number | null meta?: Record nick_name?: string | null create_time: string diff --git a/ui/src/assets/iconfont.js b/ui/src/assets/iconfont.js index 0ce468f0183..b23687ab52f 100644 --- a/ui/src/assets/iconfont.js +++ b/ui/src/assets/iconfont.js @@ -1 +1 @@ -window._iconfont_svg_string_5200172='',(h=>{var a=(l=(l=document.getElementsByTagName("script"))[l.length-1]).getAttribute("data-injectcss"),l=l.getAttribute("data-disable-injectsvg");if(!l){var o,v,t,i,c,d=function(a,l){l.parentNode.insertBefore(a,l)};if(a&&!h.__iconfont__svg__cssinject__){h.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(a){console&&console.log(a)}}o=function(){var a,l=document.createElement("div");l.innerHTML=h._iconfont_svg_string_5200172,(l=l.getElementsByTagName("svg")[0])&&(l.setAttribute("aria-hidden","true"),l.style.position="absolute",l.style.width=0,l.style.height=0,l.style.overflow="hidden",l=l,(a=document.body).firstChild?d(l,a.firstChild):a.appendChild(l))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(o,0):(v=function(){document.removeEventListener("DOMContentLoaded",v,!1),o()},document.addEventListener("DOMContentLoaded",v,!1)):document.attachEvent&&(t=o,i=h.document,c=!1,m(),i.onreadystatechange=function(){"complete"==i.readyState&&(i.onreadystatechange=null,e())})}function e(){c||(c=!0,t())}function m(){try{i.documentElement.doScroll("left")}catch(a){return void setTimeout(m,50)}e()}})(window); \ No newline at end of file +window._iconfont_svg_string_5200172='',(h=>{var a=(l=(l=document.getElementsByTagName("script"))[l.length-1]).getAttribute("data-injectcss"),l=l.getAttribute("data-disable-injectsvg");if(!l){var o,v,t,i,c,d=function(a,l){l.parentNode.insertBefore(a,l)};if(a&&!h.__iconfont__svg__cssinject__){h.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(a){console&&console.log(a)}}o=function(){var a,l=document.createElement("div");l.innerHTML=h._iconfont_svg_string_5200172,(l=l.getElementsByTagName("svg")[0])&&(l.setAttribute("aria-hidden","true"),l.style.position="absolute",l.style.width=0,l.style.height=0,l.style.overflow="hidden",l=l,(a=document.body).firstChild?d(l,a.firstChild):a.appendChild(l))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(o,0):(v=function(){document.removeEventListener("DOMContentLoaded",v,!1),o()},document.addEventListener("DOMContentLoaded",v,!1)):document.attachEvent&&(t=o,i=h.document,c=!1,m(),i.onreadystatechange=function(){"complete"==i.readyState&&(i.onreadystatechange=null,e())})}function e(){c||(c=!0,t())}function m(){try{i.documentElement.doScroll("left")}catch(a){return void setTimeout(m,50)}e()}})(window); \ No newline at end of file diff --git a/ui/src/components.d.ts b/ui/src/components.d.ts index 0649b7d0464..faf4d8e35a5 100644 --- a/ui/src/components.d.ts +++ b/ui/src/components.d.ts @@ -40,7 +40,7 @@ declare module 'vue' { MkSourceCardActionDropdown: typeof import('./components/global/mk-source-card/MkSourceCardActionDropdown.vue')['default'] MkStatusLabel: typeof import('./components/global/mk-status-label/index.vue')['default'] MkTable: typeof import('./components/global/mk-table/index.vue')['default'] - MkTableFilter: typeof import('./components/global/mk-table/MkTableFilter.vue')['default'] + MkTableFilter: typeof import('./components/global/mk-table/mk-table-filter/index.vue')['default'] MkTableMoreDropdown: typeof import('./components/global/mk-table/MkTableMoreDropdown.vue')['default'] MkTagGroup: typeof import('./components/global/mk-tag-group/index.vue')['default'] MkTooltip: typeof import('./components/global/mk-tooltip/index.vue')['default'] @@ -48,6 +48,9 @@ declare module 'vue' { PortalIcon: typeof import('./components/global/mk-icon/PortalIcon.vue')['default'] RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] + TableFilterCascader: typeof import('./components/global/mk-table/mk-table-filter/TableFilterCascader.vue')['default'] + TableFilterMultiple: typeof import('./components/global/mk-table/mk-table-filter/TableFilterMultiple.vue')['default'] + TableFilterSingle: typeof import('./components/global/mk-table/mk-table-filter/TableFilterSingle.vue')['default'] ToolIcon: typeof import('./components/global/mk-icon/ToolIcon.vue')['default'] TriggerIcon: typeof import('./components/global/mk-icon/TriggerIcon.vue')['default'] } @@ -83,7 +86,7 @@ declare global { const MkSourceCardActionDropdown: typeof import('./components/global/mk-source-card/MkSourceCardActionDropdown.vue')['default'] const MkStatusLabel: typeof import('./components/global/mk-status-label/index.vue')['default'] const MkTable: typeof import('./components/global/mk-table/index.vue')['default'] - const MkTableFilter: typeof import('./components/global/mk-table/MkTableFilter.vue')['default'] + const MkTableFilter: typeof import('./components/global/mk-table/mk-table-filter/index.vue')['default'] const MkTableMoreDropdown: typeof import('./components/global/mk-table/MkTableMoreDropdown.vue')['default'] const MkTagGroup: typeof import('./components/global/mk-tag-group/index.vue')['default'] const MkTooltip: typeof import('./components/global/mk-tooltip/index.vue')['default'] @@ -91,6 +94,9 @@ declare global { const PortalIcon: typeof import('./components/global/mk-icon/PortalIcon.vue')['default'] const RouterLink: typeof import('vue-router')['RouterLink'] const RouterView: typeof import('vue-router')['RouterView'] + const TableFilterCascader: typeof import('./components/global/mk-table/mk-table-filter/TableFilterCascader.vue')['default'] + const TableFilterMultiple: typeof import('./components/global/mk-table/mk-table-filter/TableFilterMultiple.vue')['default'] + const TableFilterSingle: typeof import('./components/global/mk-table/mk-table-filter/TableFilterSingle.vue')['default'] const ToolIcon: typeof import('./components/global/mk-icon/ToolIcon.vue')['default'] const TriggerIcon: typeof import('./components/global/mk-icon/TriggerIcon.vue')['default'] } \ No newline at end of file diff --git a/ui/src/components/COMPONENT_README.md b/ui/src/components/COMPONENT_README.md index 2b3e42bfcd7..1b6566d1dca 100644 --- a/ui/src/components/COMPONENT_README.md +++ b/ui/src/components/COMPONENT_README.md @@ -239,6 +239,8 @@ Element Plus 的 `v-infinite-scroll`。组件通过 `v-model` 管理已经加载 传入 `status` 时仅显示该状态配置的图标;未配置状态或没有图标时只显示文案,不回退到其他状态图标。 未传 `status` 时,`active` 为 true 显示成功图标,为 false 显示 `icon_ban_filled`。 组件直接使用公共枚举 `STATE_TYPES` 匹配图标;新增状态需补齐 `STATE_LABELS` 文案,图标可按需配置。 +`STARTED`、`PENDING`、`REVOKE` 及 `EMBEDDING`、`GENERATE`、`SYNC`、`TOKENIZE` +统一显示内置加载图标,使用方直接传入 `status`,无需额外组合 `LoadingIcon`。 `status` 优先于 `active`,状态文案统一使用 `STATE_LABELS`,不提供 `text` 覆盖。 ```vue @@ -279,9 +281,57 @@ Dialog、Drawer、Popover、嵌套区域等其他大、小表格均禁止开启 虚拟表格、跨页和跨列表拖拽不属于该接口。排序工具不会深拷贝普通表格的行对象;工作流使用方应在 数据写回边界自行 `cloneDeep`,保持 LogicFlow 的可观察树约束。 -表头需要多选筛选时使用 `MkTableFilter`。`label` 设置表头文案,`options` 接收 -`OptionItem[]`,必填 `v-model` 绑定已选字符串数组;打开时复制为草稿,确认或重置后 -写回并触发 `change`。过长的选项文案会显示省略号,悬停时可查看完整文案。 +`MkTableFilter` 放在 `global/mk-table/mk-table-filter/`,公共入口为 `index.vue`。 +入口统一选择模式、渲染触发按钮、计算筛选高亮和回写模型;三个内部组件分别维护对应交互: +`TableFilterSingle.vue` 复用 `MkDropdown`、`MkDropdownMenu` 和 `MkDropdownItem`, +`TableFilterMultiple.vue` 管理多选草稿及确认,`TableFilterCascader.vue` 管理级联面板及即时提交。 +内部组件通过默认插槽接收同一个触发按钮,只向入口发送 `select` 和 `open`;页面只使用 +`MkTableFilter`,不直接引用模式组件。多选与级联继续使用 Popover,不强行统一其提交和关闭行为。 + +表头筛选使用 `MkTableFilter`,`label` 设置表头文案,必填 `v-model` 保存已生效的条件。 +`mode` 支持以下模式,默认 `multiple`;调用时显式填写 `mode`,便于区分筛选方式: + +| 模式 | options / v-model | 生效方式 | +| ---------- | --------------------------------------------------------- | ----------------------------------------------------------- | +| `multiple` | `TableFilterOption[]` / 字符串、数字或布尔值数组 | 打开时复制草稿,确定后提交;重置提交 `[]`;点击外部丢弃草稿 | +| `single` | `TableFilterOption[]` / 字符串、数字或布尔值,加 `null` | 点击立即提交并关闭;“全部”提交 `null` | +| `cascader` | Element Plus `CascaderOption[]` / 由 `cascaderProps` 决定 | 勾选立即提交;多选保持打开,单选提交后关闭 | + +`TableFilterOption` 位于 `global/mk-table/mk-table-filter/types.ts`,包含 `label`、`value` 和可选 +`disabled`;扁平选项不使用空字符串作为业务值。`false`、`0` 都是有效筛选条件,正确高亮筛选图标。 +多选“全部”只操作可用选项,保留已选禁用项;无草稿选择时仍通过“重置”清空。 +过长的扁平选项文案省略,悬停查看完整文案。 + +级联默认配置为 `{ multiple: true, checkStrictly: true, emitPath: false, showPrefix: false }`,与 v2 文档标签筛选一致: +父子节点独立选择,隐藏节点前的选择框。静态多选时,组件保留面板的原始勾选, +对外将选中的父组递归展开为可用叶子节点并去重;页面直接使用回写值,不再自行转换父组。 +单选、父子联动和懒加载沿用原生返回值;`emitPath: true` 时返回对应叶子的完整路径。 +外部模型变更同步面板;自身提交后的模型回写保留原始父组勾选,关闭再打开也保持。 +通过 `cascaderProps` 覆盖字段映射、单多选、路径输出及懒加载等原生配置。 +级联多选重置为 `[]`,单选重置为 `null`;单选模型需声明可空类型。 +`width` 可覆盖弹层宽度,扁平模式默认 192px,级联默认自适应。 + +`change(value)` 在写回模型后触发,类型跟随模型;`open` 在每次打开时触发,供页面加载选项。 +组件不请求业务接口、不转换查询参数。文档文件状态用唯一选项值映射为 `status` 和 `task_type`, +映射和分页重置由页面负责,切换条件时一起更新这两个参数,避免残留任务类型。 + +```vue + + + + + + +``` 表格操作列需要 More 菜单时使用 `MkTableMoreDropdown`。组件统一提供点击型、右下定位的 More 按钮以及 `MkDropdownMenu`,默认插槽中直接放置 `MkDropdownItem`;插槽为空,或其中的条件菜单项 diff --git a/ui/src/components/business/related-resources-drawer/index.vue b/ui/src/components/business/related-resources-drawer/index.vue index 8762a449eaf..80025286c12 100644 --- a/ui/src/components/business/related-resources-drawer/index.vue +++ b/ui/src/components/business/related-resources-drawer/index.vue @@ -188,7 +188,13 @@ defineExpose({ open, close }) diff --git a/ui/src/components/global/mk-status-label/index.vue b/ui/src/components/global/mk-status-label/index.vue index 571f48c4067..e4f2444c8f9 100644 --- a/ui/src/components/global/mk-status-label/index.vue +++ b/ui/src/components/global/mk-status-label/index.vue @@ -21,6 +21,10 @@ const statusOptions: Partial [STATE_TYPES.SUCCESS]: { name: 'icon_succeed_colorful' }, [STATE_TYPES.FAILURE]: { name: 'icon_close_colorful' }, [STATE_TYPES.STARTED]: { loading: true }, + [STATE_TYPES.EMBEDDING]: { loading: true }, + [STATE_TYPES.GENERATE]: { loading: true }, + [STATE_TYPES.SYNC]: { loading: true }, + [STATE_TYPES.TOKENIZE]: { loading: true }, [STATE_TYPES.REVOKE]: { loading: true }, [STATE_TYPES.PENDING]: { loading: true }, [STATE_TYPES.REVOKED]: { name: 'icon_close_colorful' }, diff --git a/ui/src/components/global/mk-table/MkTableFilter.vue b/ui/src/components/global/mk-table/MkTableFilter.vue deleted file mode 100644 index cfe5fd4be15..00000000000 --- a/ui/src/components/global/mk-table/MkTableFilter.vue +++ /dev/null @@ -1,76 +0,0 @@ - - - - - diff --git a/ui/src/components/global/mk-table/mk-table-filter/TableFilterCascader.vue b/ui/src/components/global/mk-table/mk-table-filter/TableFilterCascader.vue new file mode 100644 index 00000000000..7819c4a5994 --- /dev/null +++ b/ui/src/components/global/mk-table/mk-table-filter/TableFilterCascader.vue @@ -0,0 +1,79 @@ + + + diff --git a/ui/src/components/global/mk-table/mk-table-filter/TableFilterMultiple.vue b/ui/src/components/global/mk-table/mk-table-filter/TableFilterMultiple.vue new file mode 100644 index 00000000000..de9779d088e --- /dev/null +++ b/ui/src/components/global/mk-table/mk-table-filter/TableFilterMultiple.vue @@ -0,0 +1,86 @@ + + + + + diff --git a/ui/src/components/global/mk-table/mk-table-filter/TableFilterSingle.vue b/ui/src/components/global/mk-table/mk-table-filter/TableFilterSingle.vue new file mode 100644 index 00000000000..32e0ff0378f --- /dev/null +++ b/ui/src/components/global/mk-table/mk-table-filter/TableFilterSingle.vue @@ -0,0 +1,39 @@ + + + diff --git a/ui/src/components/global/mk-table/mk-table-filter/index.vue b/ui/src/components/global/mk-table/mk-table-filter/index.vue new file mode 100644 index 00000000000..ae1d4bd1b19 --- /dev/null +++ b/ui/src/components/global/mk-table/mk-table-filter/index.vue @@ -0,0 +1,75 @@ + + + diff --git a/ui/src/components/global/mk-table/mk-table-filter/types.ts b/ui/src/components/global/mk-table/mk-table-filter/types.ts new file mode 100644 index 00000000000..27757302792 --- /dev/null +++ b/ui/src/components/global/mk-table/mk-table-filter/types.ts @@ -0,0 +1,9 @@ +import type { CascaderValue, CheckboxValueType } from 'element-plus' + +export interface TableFilterOption { + label: string + value: CheckboxValueType + disabled?: boolean +} + +export type TableFilterValue = CascaderValue | CheckboxValueType | CheckboxValueType[] | null | undefined diff --git a/ui/src/components/global/mk-tag-group/index.vue b/ui/src/components/global/mk-tag-group/index.vue index 63044443e33..81ea20b1f43 100644 --- a/ui/src/components/global/mk-tag-group/index.vue +++ b/ui/src/components/global/mk-tag-group/index.vue @@ -14,7 +14,7 @@ withDefaults( /** 首个标签的类型 */ type?: TagProps['type'] }>(), - { popoverDisabled: false, tags: () => [], type: 'info' }, + { popoverDisabled: false, tags: () => [], type: 'info', size: 'small' }, ) diff --git a/ui/src/components/mk-dynamics-form/constructor/items/UploadInputConstructor.vue b/ui/src/components/mk-dynamics-form/constructor/items/UploadInputConstructor.vue index 28a5560c1aa..4ad1d0199d5 100644 --- a/ui/src/components/mk-dynamics-form/constructor/items/UploadInputConstructor.vue +++ b/ui/src/components/mk-dynamics-form/constructor/items/UploadInputConstructor.vue @@ -43,23 +43,17 @@ onMounted(() => { - + diff --git a/ui/src/constants/CONSTANT_README.md b/ui/src/constants/CONSTANT_README.md index e4a6f3a54f9..d51d84f5b7c 100644 --- a/ui/src/constants/CONSTANT_README.md +++ b/ui/src/constants/CONSTANT_README.md @@ -37,8 +37,11 @@ const knowledgeTypeLabel = KNOWLEDGE_TYPE_LABELS[knowledgeType] `trigger.ts` 的 `TRIGGER_SCHEDULE_OPTIONS` 统一提供触发器与长期记忆的周期级联选项, 包含周期文案、执行时间和间隔数值;使用方只读,配置回填与校验仍由各自组件维护。 -`state.ts` 的 `STATE_LABELS` 统一维护公共枚举 `STATE_TYPES` 的展示文案。触发器、文件等业务 +`state.ts` 的 `STATE_LABELS` 统一维护公共枚举 `STATE_TYPES` 的展示文案。触发器等使用通用状态协议的业务 按需复用 `api/enums/state.ts` 中的状态值;新增状态时同步补齐文案和 `MkStatusLabel` 的图标映射。 +`STATE_LABELS` 还提供 `EMBEDDING`(索引中)、`GENERATE`(生成中)、`SYNC`(同步中)、 +`TOKENIZE`(分词索引中)的任务阶段文案,文档筛选直接复用对应文案。接口值继续使用 +`DOCUMENT_TASK_STATE` 和 `DOCUMENT_TASK_TYPE` 映射,不将展示状态键直接提交为文档任务状态。 `document.ts` 的 `DOCUMENT_HIT_HANDLING_LABELS` 维护文档命中处理方式的展示文案, 映射键引用 API 枚举 `DOCUMENT_HIT_HANDLING`。 diff --git a/ui/src/constants/state.ts b/ui/src/constants/state.ts index 57c5c835af0..e2b63fe5b1e 100644 --- a/ui/src/constants/state.ts +++ b/ui/src/constants/state.ts @@ -5,6 +5,10 @@ import type { State } from '@/api/types' export const STATE_LABELS: Record = { [STATE_TYPES.PENDING]: '排队中', [STATE_TYPES.STARTED]: '执行中', + [STATE_TYPES.EMBEDDING]: '索引中', + [STATE_TYPES.GENERATE]: '生成中', + [STATE_TYPES.SYNC]: '同步中', + [STATE_TYPES.TOKENIZE]: '分词索引中', [STATE_TYPES.SUCCESS]: '成功', [STATE_TYPES.FAILURE]: '失败', [STATE_TYPES.REVOKE]: '取消中', diff --git a/ui/src/styles/app.scss b/ui/src/styles/app.scss index 05224a5fd91..f26d67f52d4 100644 --- a/ui/src/styles/app.scss +++ b/ui/src/styles/app.scss @@ -72,6 +72,26 @@ h6 { line-height: var(--mk-line-height-base); } +/* button */ +// 资源菜单的导入按钮 +.mk-import-button { + width: 100%; + .el-upload { + width: 100%; + } +} +// 添加标签虚线按钮 +.mk-add-tag-button { + min-width: auto !important; + padding: 0 6px; + height: 24px; + line-height: 24px; + font-size: 14px; + border: 1px dashed var(--el-border-color); + border-radius: var(--el-border-radius-small); + color: var(--mk-N500); +} + /* dot */ // 状态圆点 .mk-dot-danger { @@ -308,12 +328,3 @@ h6 { background: var(--el-color-primary); } } - -/* upload-button */ -// 资源菜单的导入按钮 -.mk-import-button { - width: 100%; - .el-upload { - width: 100%; - } -} diff --git a/ui/src/styles/element-plus.scss b/ui/src/styles/element-plus.scss index 2ef9e063bff..067dd429f75 100644 --- a/ui/src/styles/element-plus.scss +++ b/ui/src/styles/element-plus.scss @@ -1122,11 +1122,7 @@ text-overflow: ellipsis; white-space: nowrap; } - &:not(&--small) { - --el-tag-font-size: 14px; - height: 20px; - color: var(--mk-N900) !important; - } + &--small { padding: 4px; } diff --git a/ui/src/views/VIEW_README.md b/ui/src/views/VIEW_README.md index 3cade36a72a..a79f7784856 100644 --- a/ui/src/views/VIEW_README.md +++ b/ui/src/views/VIEW_README.md @@ -872,6 +872,31 @@ Layout 保留标题栏固定位置和外层间距,页面负责内部布局。 资源入口只负责详情数据和侧栏 `resource-header`,无需转发标题栏属性。 文档页将标题与搜索放入标题栏,表格在正文中独立显示 loading。 +文档页的筛选选项、状态和事件集中在 `// 过滤项` 下:文件状态、启用状态、召回处理显式使用 +`MkTableFilter mode="single"`,标签使用 `mode="cascader"`。文件状态按 v2 提供成功、失败、 +索引中、分词索引中、排队中、生成中,同时映射 `status` 与可选 `task_type`;切换到非任务状态 +或全部时清除任务类型。启用状态保留布尔 `false`,召回处理复用 `DOCUMENT_HIT_HANDLING_LABELS`。 +标签首次打开时按普通/共享范围查询知识库标签,成功后通过非空选项复用缓存(始终包含“无标签”), +不额外维护 loaded 标记;普通布尔请求锁防止重复请求,失败释放后下次打开可重试。 +标签级联沿用 MkTableFilter 默认的父子独立选择和隐藏复选框外观;组件内部保留原始勾选, +对外将父组转换为叶子值并去重。页面直接将回写的叶子标签 ID 数组用于查询, +“无标签”使用 `NO_TAG`;页面不维护父组展开或去重逻辑。 +任一筛选确认或重置后回到第一页查询,保留其他筛选和名称/创建者搜索条件。 +标签列使用页面专属 `components/DocumentTags.vue`,接收 `document`,展示标签数量, +悬浮时显示标签名与值,长文本省略并保留 title。数量优先读取 `tag_count`,缺省时使用标签数组长度, +无标签时仅显示添加入口。添加按钮阻止点击冒泡,通过 `add(documentId)` 通知调用方; +当前文档页尚未接入添加弹窗与保存接口,不新增权限判断。 + +文件状态的解析、聚合与展示统一放在文档页的 `components/DocumentStatus.vue`。组件接收 `status`、`statusMeta`,按从右起的任务位置解析向量化、生成问题、 +同步、分词索引;聚合优先级为取消中、执行中、排队中、失败、取消完成、成功, +同优先级取字符串最左侧的任务,忽略 `n` 及未知状态;无有效任务时显示 `-`。 +汇总与悬浮明细均复用 `MkStatusLabel`,任务执行中文案使用 `STATE_TYPES` 对应阶段, +文案和加载图标均由 `MkStatusLabel` 统一呈现。保持 v2 的取消完成显示成功规则,不修改通用状态语义。 +悬浮内容按需渲染,展示各任务名称、状态、成功分段数/总数和时间;分段计数读取 `status_meta.aggs`, +成功数只累计状态 `2`,失败与取消完成的进度使用危险色。 +时间读取 `state_time[taskType]` 的排队时间,取消完成读取取消完成时间;缺失元数据使用 0/0 并隐藏时间。 + + ## Workspace 共享知识库详情 知识库列表根据当前目录进入普通或 `workspace-shared-knowledge-detail` 共享详情路由。 diff --git a/ui/src/views/knowledge-detail/document/components/DocumentStatus.vue b/ui/src/views/knowledge-detail/document/components/DocumentStatus.vue new file mode 100644 index 00000000000..672ffe3e176 --- /dev/null +++ b/ui/src/views/knowledge-detail/document/components/DocumentStatus.vue @@ -0,0 +1,111 @@ + + + diff --git a/ui/src/views/knowledge-detail/document/components/DocumentTags.vue b/ui/src/views/knowledge-detail/document/components/DocumentTags.vue new file mode 100644 index 00000000000..329dd39133c --- /dev/null +++ b/ui/src/views/knowledge-detail/document/components/DocumentTags.vue @@ -0,0 +1,40 @@ + + + diff --git a/ui/src/views/knowledge-detail/document/index.vue b/ui/src/views/knowledge-detail/document/index.vue index 222df42101c..9c93030e96d 100644 --- a/ui/src/views/knowledge-detail/document/index.vue +++ b/ui/src/views/knowledge-detail/document/index.vue @@ -1,17 +1,23 @@ @@ -93,13 +169,33 @@ onMounted(() => { @size-change="loadDocuments" resizable > + - - - + + + - + + @@ -113,8 +209,32 @@ onMounted(() => { + + + + - + + diff --git a/ui/src/views/system/operate-logs/index.vue b/ui/src/views/system/operate-logs/index.vue index 2cc3e873191..6e4aecec14c 100644 --- a/ui/src/views/system/operate-logs/index.vue +++ b/ui/src/views/system/operate-logs/index.vue @@ -153,7 +153,13 @@ onMounted(() => { > @@ -164,7 +170,13 @@ onMounted(() => { diff --git a/ui/src/views/system/resource-management/model/index.vue b/ui/src/views/system/resource-management/model/index.vue index 8f51e62fa56..66d6c412180 100644 --- a/ui/src/views/system/resource-management/model/index.vue +++ b/ui/src/views/system/resource-management/model/index.vue @@ -136,9 +136,9 @@ onMounted(() => { - + diff --git a/ui/src/views/system/resource-management/tool/index.vue b/ui/src/views/system/resource-management/tool/index.vue index bb22b90094d..9f97dc2b4c8 100644 --- a/ui/src/views/system/resource-management/tool/index.vue +++ b/ui/src/views/system/resource-management/tool/index.vue @@ -161,9 +161,9 @@ onMounted(() => { - + diff --git a/ui/src/views/system/settings/theme/components/PlatformPreview.vue b/ui/src/views/system/settings/theme/components/PlatformPreview.vue index 92e57e0e3c2..18bee2be8b4 100644 --- a/ui/src/views/system/settings/theme/components/PlatformPreview.vue +++ b/ui/src/views/system/settings/theme/components/PlatformPreview.vue @@ -45,7 +45,7 @@ const platformMenuItems = computed(() => [
飞小致

feixaozhi

- 系统管理员 + 系统管理员