diff --git a/ui/src/api/API_README.md b/ui/src/api/API_README.md
index 1fc36ad9177..72f45454fdb 100644
--- a/ui/src/api/API_README.md
+++ b/ui/src/api/API_README.md
@@ -506,6 +506,9 @@ System 接口由资源管理服务提供;本地开源后端没有对应扩展
批量任务沿用 v2 的 `id_list` / `state_list`;生成问题使用 `document_id_list`,
添加标签使用 `document_ids` / `tag_ids`,迁移和批量导出直接提交文档 ID 数组。
向量化、生成问题默认排除成功分段,也可选择全部分段。任务类型使用 `DOCUMENT_TASK_TYPE`。
+单项向量化使用 `putDocumentRefresh(knowledgeId, documentId, stateList)` 请求 `//refresh`;
+单项取消使用 `putCancelTask(knowledgeId, documentId, taskType)` 请求 `//cancel_task`,提交 `{ type }`。
+批量向量化与取消分别沿用 `batch_refresh` 和 `batch_cancel_task`,不以单元素批量请求代替单项接口。
文档设置的共享载荷为 `DocumentSettingPayload`,生成问题使用 `DocumentGeneratePayload`;
单项设置通过 `meta` 保留来源数据,批量设置将 `allow_download` 放在顶层。
飞书同步沿用 v2 扩展接口 `/knowledge/lark//_batch`,需部署环境支持。
diff --git a/ui/src/api/admin/workspace/knowledge/document.ts b/ui/src/api/admin/workspace/knowledge/document.ts
index 8b73776328e..fa5581d7c59 100644
--- a/ui/src/api/admin/workspace/knowledge/document.ts
+++ b/ui/src/api/admin/workspace/knowledge/document.ts
@@ -26,8 +26,6 @@ const putQuickCreateDocuments = (knowledgeId: string, documents: DocumentQuickCr
const putDocumentActive = (knowledgeId: string, documentId: string, isActive: boolean) =>
put(`${getPrefix(knowledgeId)}/${documentId}`, { is_active: isActive })
-
-
/** 删除单个文档。 */
const deleteDocument = (knowledgeId: string, documentId: string) => del(`${getPrefix(knowledgeId)}/${documentId}`)
@@ -35,19 +33,36 @@ const deleteDocument = (knowledgeId: string, documentId: string) => del(`${getPr
const putBatchDeleteDocuments = (knowledgeId: string, documentIds: string[]) =>
put(`${getPrefix(knowledgeId)}/batch_delete`, { id_list: documentIds })
+/** 单个文档向量化。 */
+const putDocumentRefresh = (knowledgeId: string, documentId: string, stateList: DocumentTaskState[]) =>
+ put(`${getPrefix(knowledgeId)}/${documentId}/refresh`, { state_list: stateList })
-/** 批量提交文档向量化。 */
+/** 批量文档向量化。 */
const putBatchRefreshDocuments = (knowledgeId: string, documentIds: string[], stateList: DocumentTaskState[]) =>
put(`${getPrefix(knowledgeId)}/batch_refresh`, { id_list: documentIds, state_list: stateList })
-/** 批量提交文档分词索引。 */
+/** 单个文档分词索引。 */
+const putDocumentTokenize: (knowledge_id: string, document_id: string, state_list: Array, loading?: Ref) => Promise> = (
+ knowledge_id,
+ document_id,
+ state_list,
+ loading,
+) => {
+ return put(`${getPrefix(knowledge_id)}/${document_id}/tokenize`, { state_list }, undefined, loading)
+}
+/** 批量文档分词索引。 */
const putBatchTokenizeDocuments = (knowledgeId: string, documentIds: string[], stateList: DocumentTaskState[]) =>
put(`${getPrefix(knowledgeId)}/batch_tokenize`, { id_list: documentIds, state_list: stateList })
+/** 单个取消文档任务。 */
+const putCancelTask = (knowledgeId: string, documentId: string, taskType: DocumentTaskType) =>
+ put(`${getPrefix(knowledgeId)}/${documentId}/cancel_task`, { type: taskType })
+
/** 批量取消指定文档任务。 */
const putBatchCancelDocumentTask = (knowledgeId: string, documentIds: string[], taskType: DocumentTaskType) =>
put(`${getPrefix(knowledgeId)}/batch_cancel_task`, { id_list: documentIds, type: taskType })
+
/** 保存单个文档的召回及来源设置。 */
const putDocumentSetting = (knowledgeId: string, documentId: string, data: DocumentSettingPayload) =>
put(`${getPrefix(knowledgeId)}/${documentId}`, data)
@@ -103,8 +118,10 @@ export default {
putDocumentActive,
deleteDocument,
putBatchDeleteDocuments,
+ putDocumentRefresh,
putBatchRefreshDocuments,
putBatchTokenizeDocuments,
+ putCancelTask,
putBatchCancelDocumentTask,
putDocumentSetting,
putBatchDocumentSetting,
diff --git a/ui/src/api/enums/state.ts b/ui/src/api/enums/state.ts
index 454bdb704c3..d8953bc5c36 100644
--- a/ui/src/api/enums/state.ts
+++ b/ui/src/api/enums/state.ts
@@ -1,11 +1,7 @@
-/** 跨业务复用的任务状态,按需扩展。 */
+/** 执行记录接口状态;文档任务阶段在所属 View 中维护。 */
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/assets/iconfont.js b/ui/src/assets/iconfont.js
index af81f216b8b..f9240124287 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,e(),i.onreadystatechange=function(){"complete"==i.readyState&&(i.onreadystatechange=null,m())})}function m(){c||(c=!0,t())}function e(){try{i.documentElement.doScroll("left")}catch(a){return void setTimeout(e,50)}m()}})(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/COMPONENT_README.md b/ui/src/components/COMPONENT_README.md
index 0c3ee5232e4..617badd2459 100644
--- a/ui/src/components/COMPONENT_README.md
+++ b/ui/src/components/COMPONENT_README.md
@@ -193,6 +193,11 @@ Drawer 没有 `MkDialog` 的外壳挂载控制;需要按需挂载时,由业
菜单项通过 `icon` Prop 传 Element Plus 图标,或使用 `icon` 插槽;`selectable` 预留勾选位,
`selected` 控制选中。触发器遵循单根节点规则。
+`hide-when-empty` 默认关闭。开启后检查 `dropdown` 插槽,展开标准 `MkDropdownMenu` 的默认插槽
+和 Fragment,忽略注释、空白文本;没有有效 VNode 时不渲染下拉入口,不改变 `persistent`。
+业务 Action 的组件 VNode 视为有效内容,不检查其内部的 `v-if`。此能力用于文档批量 More,
+表格和卡片 More 保持原有的默认插槽判断。
+
### MkViewLayout
满高页面布局,`loading` 覆盖整个根节点。`title` 默认读取路由标题,传空字符串隐藏;
@@ -276,19 +281,21 @@ Element Plus 的 `v-infinite-scroll`。组件通过 `v-model` 管理已经加载
### MkStatusLabel
-`active` 控制布尔状态,默认“已启用 / 已禁用”;用 `activeText`、`inactiveText` 修改文案。
-多状态通过 `status` 直接传入 `STATE_LABELS` 的状态键,文案统一读取 `constants/state.ts`。
-传入 `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` 覆盖。
+提供 `success`、`failure`、`loading`、`disabled` 四种图标类型,通过 `type + label` 展示业务状态。
+组件不依赖 API 状态枚举或业务文案;业务方维护状态与 `{ type, label }` 的映射。
+`StatusLabelType`、`StatusLabelOptions` 从 `global/mk-status-label/types.ts` 导入。
+传入 `type` 时使用该类型和外部 `label`,优先于布尔模式,不回退到启用/禁用文案。
+
+未传 `type` 时保留 `active` 便捷模式,默认“已启用 / 已禁用”;
+`activeText`、`inactiveText` 可修改文案,`inactiveIcon` 可覆盖未激活图标(默认 `icon_ban_filled`)。
+成功图标固定为 `icon_succeed_colorful`,失败图标为 `icon_close_colorful`,加载类型使用 `LoadingIcon`,
+禁用图标使用灰色样式。`inactiveIcon` 仅影响布尔模式。
+不再接收 `status`;未知业务状态由调用方显示原始文案,不伪装成成功或禁用状态。
```vue
-
-
+
+
```
### MkTable
diff --git a/ui/src/components/business/resource-authorization-drawer/PermissionConfigDialog.vue b/ui/src/components/business/resource-authorization-drawer/PermissionConfigDialog.vue
index 7167014975a..8ab16931a95 100644
--- a/ui/src/components/business/resource-authorization-drawer/PermissionConfigDialog.vue
+++ b/ui/src/components/business/resource-authorization-drawer/PermissionConfigDialog.vue
@@ -44,7 +44,7 @@ defineExpose({ open, close })
-
+
{{ option.label }}
{{ option.description }}
@@ -55,7 +55,7 @@ defineExpose({ open, close })
生效资源
-
+
仅当前资源
包含所有子文件夹
diff --git a/ui/src/components/global/mk-dropdown/index.vue b/ui/src/components/global/mk-dropdown/index.vue
index c24951d3c48..b932ca20c85 100644
--- a/ui/src/components/global/mk-dropdown/index.vue
+++ b/ui/src/components/global/mk-dropdown/index.vue
@@ -1,24 +1,43 @@
-
+
diff --git a/ui/src/components/global/mk-status-label/index.vue b/ui/src/components/global/mk-status-label/index.vue
index e4f2444c8f9..55ee1d49b94 100644
--- a/ui/src/components/global/mk-status-label/index.vue
+++ b/ui/src/components/global/mk-status-label/index.vue
@@ -1,48 +1,34 @@
-
-
-
- {{ label }}
+
+
+ {{ displayLabel }}
diff --git a/ui/src/components/global/mk-status-label/types.ts b/ui/src/components/global/mk-status-label/types.ts
new file mode 100644
index 00000000000..e5d33536425
--- /dev/null
+++ b/ui/src/components/global/mk-status-label/types.ts
@@ -0,0 +1,6 @@
+export type StatusLabelType = 'success' | 'failure' | 'loading' | 'disabled'
+
+export interface StatusLabelOptions {
+ type: StatusLabelType
+ label: string
+}
diff --git a/ui/src/constants/CONSTANT_README.md b/ui/src/constants/CONSTANT_README.md
index 8fbd0ac817b..9c5259e4dfa 100644
--- a/ui/src/constants/CONSTANT_README.md
+++ b/ui/src/constants/CONSTANT_README.md
@@ -37,11 +37,11 @@ const knowledgeTypeLabel = KNOWLEDGE_TYPE_LABELS[knowledgeType]
`trigger.ts` 的 `TRIGGER_SCHEDULE_OPTIONS` 统一提供触发器与长期记忆的周期级联选项,
包含周期文案、执行时间和间隔数值;使用方只读,配置回填与校验仍由各自组件维护。
-`state.ts` 的 `STATE_LABELS` 统一维护公共枚举 `STATE_TYPES` 的展示文案。触发器等使用通用状态协议的业务
-按需复用 `api/enums/state.ts` 中的状态值;新增状态时同步补齐文案和 `MkStatusLabel` 的图标映射。
-`STATE_LABELS` 还提供 `EMBEDDING`(索引中)、`GENERATE`(生成中)、`SYNC`(同步中)、
-`TOKENIZE`(分词索引中)的任务阶段文案,文档筛选直接复用对应文案。接口值继续使用
-`DOCUMENT_TASK_STATE` 和 `DOCUMENT_TASK_TYPE` 映射,不将展示状态键直接提交为文档任务状态。
+`state.ts` 的 `EXECUTION_STATUS_OPTIONS` 维护触发器、工具和知识库工作流执行记录共用的
+中文文案与图标类型,仅包含 `PENDING`、`STARTED`、`SUCCESS`、`FAILURE`、`REVOKE`、
+`REVOKED`、`TRIGGER_ERROR` 七种接口状态,列表筛选及执行详情复用该配置。新增状态只需在业务配置中补齐展示,
+不修改 `MkStatusLabel`。文档任务使用 `views/knowledge-detail/document/status.ts` 的独立配置,
+筛选查询仍映射 `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 e2b63fe5b1e..3491efc2b1e 100644
--- a/ui/src/constants/state.ts
+++ b/ui/src/constants/state.ts
@@ -1,17 +1,14 @@
-/** 任务状态的通用展示文案。 */
+/** 触发器、工具和知识库工作流执行记录共用的状态展示配置。 */
import { STATE_TYPES } from '@/api/enums'
import type { State } from '@/api/types'
+import type { StatusLabelOptions } from '@/components/global/mk-status-label/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]: '取消中',
- [STATE_TYPES.REVOKED]: '已取消',
- [STATE_TYPES.TRIGGER_ERROR]: '触发失败',
+export const EXECUTION_STATUS_OPTIONS: Record = {
+ [STATE_TYPES.PENDING]: { type: 'loading', label: '排队中' },
+ [STATE_TYPES.STARTED]: { type: 'loading', label: '执行中' },
+ [STATE_TYPES.SUCCESS]: { type: 'success', label: '成功' },
+ [STATE_TYPES.FAILURE]: { type: 'failure', label: '失败' },
+ [STATE_TYPES.REVOKE]: { type: 'loading', label: '取消中' },
+ [STATE_TYPES.REVOKED]: { type: 'failure', label: '已取消' },
+ [STATE_TYPES.TRIGGER_ERROR]: { type: 'failure', label: '触发失败' },
}
diff --git a/ui/src/styles/STYLE_README.md b/ui/src/styles/STYLE_README.md
index 11b97e3bdb6..6d479f43077 100644
--- a/ui/src/styles/STYLE_README.md
+++ b/ui/src/styles/STYLE_README.md
@@ -257,6 +257,9 @@ CSS 自定义属性同样按变量名排序。嵌套选择器、伪类和媒体
## 常用交互工具类
+纵向单选组在 `el-radio-group` 上添加 `mk-radio-group-vertical`,统一整行宽度、8px 间距及
+多行选项的顶部对齐;普通单行和带描述的选项均可使用。
+
卡片内需要文字居左、单选圆点居右并垂直居中时,在 `el-radio` 上添加 `mk-card-radio`。
该类由 `app.scss` 统一设置整行宽度、自适应高度、文字换行和左右间距;卡片边框与选中状态
仍由外层卡片控制。
diff --git a/ui/src/styles/app.scss b/ui/src/styles/app.scss
index 06e25d16921..a403ce66a31 100644
--- a/ui/src/styles/app.scss
+++ b/ui/src/styles/app.scss
@@ -235,7 +235,7 @@ h6 {
}
// radio-group 垂直排列
-.vertical-radio-group {
+.mk-radio-group-vertical {
align-items: stretch;
display: flex;
flex-direction: column;
diff --git a/ui/src/views/VIEW_README.md b/ui/src/views/VIEW_README.md
index e8870abc58e..1da0a1b14c3 100644
--- a/ui/src/views/VIEW_README.md
+++ b/ui/src/views/VIEW_README.md
@@ -636,7 +636,7 @@ Workspace 与 System 授权均使用该工作空间 ID,不读取路由工作
`trigger/execution-record/TriggerTaskRecordDrawer.vue` 通过列表操作列打开,负责执行记录分页、
名称/状态/类型筛选、执行时间排序及跨页浏览。`ExecutionDetailDrawer.vue` 根据当前记录查询详情,
-展示智能体问答、工具输入输出、触发错误及工作流节点详情;状态展示直接使用 `MkStatusLabel` 和共享 `STATE_LABELS`。
+展示智能体问答、工具输入输出、触发错误及工作流节点详情;状态展示通过共享 `EXECUTION_STATUS_OPTIONS` 向 `MkStatusLabel` 传入 `type + label`,筛选复用同一配置的文案。
## 查看关联资源 Action
@@ -675,32 +675,42 @@ Workspace API 内部通过 `getWorkspaceId()` 读取当前路由工作空间。
文档页通过 `onMounted` 加载文档列表,创建者选项在展开下拉或输入关键词时通过 `remoteMethod`
按需加载,不在进入页面时预加载;其他列表页的同类创建者筛选遵循相同方式。不额外监听路由重置状态。切换工作空间
整页加载并返回知识库列表;当前知识库与共享资源入口均从其他页面进入,挂载时初始化筛选和分页。
-无轮询和排序入口。非共享文档显示选择列,按文档 ID 跨页保留选择,搜索或筛选变化时清空;
+普通与共享文档列表沿用 v2 的 6 秒轮询间隔,在请求结束后安排下一次静默刷新,保留当前筛选、
+分页和勾选,不显示列表 loading;操作请求期间推迟轮询,失败后继续重试,卸载时停止且不再重新调度。
+无排序入口。非共享文档显示选择列,按文档 ID 跨页保留选择,搜索或筛选变化时清空;
选中后通过 `footer-batch-actions` 展示批量操作。右侧固定操作列提供启停、向量化、分词索引和更多菜单,
运行或排队中的任务切换为取消入口。Web、飞书提供同步,普通与工作流文档提供原文下载和替换。
单项与批量操作共用页面请求状态,删除和同步先确认,成功后清空选择并刷新,导出和下载保留选择。
文档操作统一放在 `document/action-dropdown/`,通过该目录 `index.ts` 导出,页面显式组合各 Action。
-`EmbeddingDocumentAction` 负责向量化分段范围与取消向量化,`GenerateDocumentQuestionsAction` 负责生成问题配置,`SettingDocumentAction` 负责召回及来源设置,
+文档任务是否排队或执行中统一通过 `document/utils/task-status.ts` 的 `isDocumentTaskRunning(document, taskType)` 判断,
+页面及 Action 不重复解析状态字符串;文档或任务状态缺失时返回 `false`。
+`EmbeddingDocumentAction` 负责单项向量化与取消向量化,`BatchEmbeddingDocumentAction` 负责批量向量化按钮,`GenerateDocumentQuestionsAction` 负责生成问题配置,`SettingDocumentAction` 负责召回及来源设置,
`MigrateDocumentAction` 选择目标知识库,`DocumentTagsAction` 添加已有标签和移除文档标签关联;
这些 Action 继续将操作入口与专属弹窗放在同一组件内。组件接收完整 Document API、知识库 ID、文档 ID 或文档数组及禁用状态,
点击时快照本次操作对象,内部管理弹窗和请求,成功后通过 `refresh` 通知页面刷新,失败保留输入。
-向量化和生成问题使用独立 Action,通过 `batch` 切换批量按钮;行内入口使用 `display` 区分菜单与图标按钮。
-单项向量化传入 `document`,由 `EmbeddingDocumentAction` 内部判断排队或执行状态并切换取消入口,
-取消请求通过 `v-model:loading` 共用页面操作状态;页面不再拆分向量化和取消向量化两个入口。
+单项向量化仅传 `document`,内部判断排队或执行状态,固定展示行内图标按钮,分别调用单项向量化和取消接口。
+批量向量化仅传 `document-ids`,使用独立的 `BatchEmbeddingDocumentAction` 普通按钮;
+批量取消向量化继续使用 `CancelDocumentTaskAction` 菜单项,单项和批量请求均通过 `v-model:loading` 共用页面操作状态。
+向量化相关文件集中在 `document/action-dropdown/embedding/`,包含两个 Action 和共用的 `DocumentEmbeddingDialog.vue`;
+Action 仍通过上层 `action-dropdown/index.ts` 导出,弹窗仅在目录内部使用,管理分段范围与确认事件,
+请求和成功关闭由各自 Action 负责;打开时快照文档目标,失败保留范围选择。
+生成问题仍通过 `batch` 切换批量按钮,通过 `display` 区分行内菜单与图标按钮。
设置组件通过 `batch` 区分单项与批量,标签组件通过 `manageTags` 区分标签管理与批量添加。
`CancelDocumentTaskAction`、`TokenizeDocumentAction`、`SyncDocumentAction`、
`ExportDocumentAction`、`DownloadDocumentAction`、`ReplaceDocumentAction` 和 `DeleteDocumentAction`
分别负责任务取消、分词、同步、导出、下载、替换与删除,单项和批量入口复用对应组件。
直接请求的 Action 通过 `v-model:loading` 共用页面操作状态,各 Action 自行管理请求、
防重复提交、成功提示与失败处理,不额外抽取统一请求封装;需要刷新时发出 `refresh`,导出与下载不发出。
+`DeleteDocumentAction` 单个删除传 `document`,确认框展示文档名称并调用单个删除接口;
+批量删除传 `batch` 和 `document-ids`,确认框展示数量并调用批量删除接口。
删除后分页总数和页码回退由页面重新查询服务端处理,不在 Action 中维护分页数据。
启停开关直接使用页面内的 `el-switch`,由页面 `handleChangeActive` 管理请求,
接口成功后刷新列表回显,不在请求前切换状态,不单独封装 Action。
页面保留列表、多选、任务状态展示判断与刷新方法,不维护各 Action 的请求流程。
`DocumentStatus`、`DocumentTags` 等纯展示组件继续留在 `document/components/`。
页面不再维护这些弹窗 Ref 和打开方法。行内更多菜单使用 `MkTableMoreDropdown`;批量操作栏使用
-`MkDropdown` 搭配非 text 的“更多操作 + 下箭头”按钮和 `MkDropdownMenu`,点击展开。
-两种菜单均保留 `persistent`,避免菜单收起时卸载 Action 内的弹窗。
+`MkDropdown` 搭配非 text 的 More 图标按钮和 `MkDropdownMenu`,点击展开。
+批量 More 开启 `hide-when-empty`,按标准菜单插槽判断空内容,不强制开启 `persistent`;不检查业务 Action 内部的条件渲染。
不新增前端权限判断;共享文档不展示选择列及操作入口。
文档分页接口维护在 `workspace/knowledge/document.ts`,页面管理 loading。文件状态表头使用
`MkDropdown` 单选筛选全部、成功、失败、索引中、分词索引中、排队中和生成中;查询组合
@@ -926,8 +936,9 @@ Layout 保留标题栏固定位置和外层间距,页面负责内部布局。
文件状态的解析、聚合与展示统一放在文档页的 `components/DocumentStatus.vue`。组件接收 `status`、`statusMeta`,按从右起的任务位置解析向量化、生成问题、
同步、分词索引;聚合优先级为取消中、执行中、排队中、失败、取消完成、成功,
同优先级取字符串最左侧的任务,忽略 `n` 及未知状态;无有效任务时显示 `-`。
-汇总与悬浮明细均复用 `MkStatusLabel`,任务执行中文案使用 `STATE_TYPES` 对应阶段,
-文案和加载图标均由 `MkStatusLabel` 统一呈现。保持 v2 的取消完成显示成功规则,不修改通用状态语义。
+汇总与悬浮明细均复用 `MkStatusLabel`,通过文档本地 `status.ts` 的 `DOCUMENT_STATUS_OPTIONS`
+传入图标类型和文案;`DOCUMENT_STATUS_FILTER_OPTIONS` 复用对应文案并维护筛选请求映射。
+文档配置独立于执行记录配置。保持 v2 的取消完成显示成功规则,不修改通用状态语义。
悬浮内容按需渲染,展示各任务名称、状态、成功分段数/总数和时间;分段计数读取 `status_meta.aggs`,
成功数只累计状态 `2`,失败与取消完成的进度使用危险色。
时间读取 `state_time[taskType]` 的排队时间,取消完成读取取消完成时间;缺失元数据使用 0/0 并隐藏时间。
diff --git a/ui/src/views/application/application-card/ApplicationCard.vue b/ui/src/views/application/application-card/ApplicationCard.vue
index 154a89ef74c..d6a76f2baaa 100644
--- a/ui/src/views/application/application-card/ApplicationCard.vue
+++ b/ui/src/views/application/application-card/ApplicationCard.vue
@@ -41,7 +41,7 @@ function handleOpen() {
-
+
diff --git a/ui/src/views/knowledge-detail/document/action-dropdown/CancelDocumentTaskAction.vue b/ui/src/views/knowledge-detail/document/action-dropdown/CancelDocumentTaskAction.vue
index 09a2a6f1dff..fffbaa4a68f 100644
--- a/ui/src/views/knowledge-detail/document/action-dropdown/CancelDocumentTaskAction.vue
+++ b/ui/src/views/knowledge-detail/document/action-dropdown/CancelDocumentTaskAction.vue
@@ -36,12 +36,5 @@ function handleCancelTask() {
-
+
diff --git a/ui/src/views/knowledge-detail/document/action-dropdown/DeleteDocumentAction.vue b/ui/src/views/knowledge-detail/document/action-dropdown/DeleteDocumentAction.vue
index 4b0aded152f..6339e61b4c9 100644
--- a/ui/src/views/knowledge-detail/document/action-dropdown/DeleteDocumentAction.vue
+++ b/ui/src/views/knowledge-detail/document/action-dropdown/DeleteDocumentAction.vue
@@ -1,27 +1,34 @@
-
-
-
- 向量化
-
-
-
-
-
-
- 未成功的分段
- 全部分段
-
-
-
-
-
- 取消
-
- 确认
-
-
-
diff --git a/ui/src/views/knowledge-detail/document/action-dropdown/TokenizeDocumentAction.vue b/ui/src/views/knowledge-detail/document/action-dropdown/TokenizeDocumentAction.vue
index 46c421f6dbd..8afaf0391f8 100644
--- a/ui/src/views/knowledge-detail/document/action-dropdown/TokenizeDocumentAction.vue
+++ b/ui/src/views/knowledge-detail/document/action-dropdown/TokenizeDocumentAction.vue
@@ -4,6 +4,7 @@ import type DocumentApi from '@/api/admin/workspace/knowledge/document'
import { computed } from 'vue'
import { DOCUMENT_TASK_STATE, DOCUMENT_TASK_TYPE } from '@/api/enums'
import type { DocumentItem } from '@/api/types'
+import { isDocumentTaskRunning } from '../utils'
defineOptions({ name: 'TokenizeDocumentAction' })
const props = defineProps<{ api: typeof DocumentApi; knowledgeId: string; documentIds: string[]; document?: DocumentItem; batch?: boolean }>()
@@ -11,10 +12,7 @@ const loading = defineModel('loading', { default: false })
const emit = defineEmits<{ refresh: [] }>()
// 行操作根据任务状态切换为取消;批量操作始终提交分词索引。
-const running = computed(() => {
- const state = props.document?.status?.at(-DOCUMENT_TASK_TYPE.TOKENIZE)
- return state === DOCUMENT_TASK_STATE.PENDING || state === DOCUMENT_TASK_STATE.STARTED
-})
+const running = computed(() => isDocumentTaskRunning(props.document, DOCUMENT_TASK_TYPE.TOKENIZE))
function handleTokenize() {
const ids = [...props.documentIds]
if (!ids.length || loading.value) return
diff --git a/ui/src/views/knowledge-detail/document/action-dropdown/embedding/BatchEmbeddingDocumentAction.vue b/ui/src/views/knowledge-detail/document/action-dropdown/embedding/BatchEmbeddingDocumentAction.vue
new file mode 100644
index 00000000000..a90b19e08a3
--- /dev/null
+++ b/ui/src/views/knowledge-detail/document/action-dropdown/embedding/BatchEmbeddingDocumentAction.vue
@@ -0,0 +1,45 @@
+
+
+
+
+ 向量化
+
+
diff --git a/ui/src/views/knowledge-detail/document/action-dropdown/embedding/DocumentEmbeddingDialog.vue b/ui/src/views/knowledge-detail/document/action-dropdown/embedding/DocumentEmbeddingDialog.vue
new file mode 100644
index 00000000000..201995c81f1
--- /dev/null
+++ b/ui/src/views/knowledge-detail/document/action-dropdown/embedding/DocumentEmbeddingDialog.vue
@@ -0,0 +1,37 @@
+
+
+
+
+
+ 仅执行未成功部分
+ 全部分段
+
+
+
+ 取消
+
+ 提交
+
+
+
diff --git a/ui/src/views/knowledge-detail/document/action-dropdown/embedding/EmbeddingDocumentAction.vue b/ui/src/views/knowledge-detail/document/action-dropdown/embedding/EmbeddingDocumentAction.vue
new file mode 100644
index 00000000000..b280f384699
--- /dev/null
+++ b/ui/src/views/knowledge-detail/document/action-dropdown/embedding/EmbeddingDocumentAction.vue
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
diff --git a/ui/src/views/knowledge-detail/document/action-dropdown/index.ts b/ui/src/views/knowledge-detail/document/action-dropdown/index.ts
index 762332ed242..82efcaf06f8 100644
--- a/ui/src/views/knowledge-detail/document/action-dropdown/index.ts
+++ b/ui/src/views/knowledge-detail/document/action-dropdown/index.ts
@@ -1,7 +1,8 @@
+export { default as BatchEmbeddingDocumentAction } from './embedding/BatchEmbeddingDocumentAction.vue'
export { default as CancelDocumentTaskAction } from './CancelDocumentTaskAction.vue'
export { default as DeleteDocumentAction } from './DeleteDocumentAction.vue'
export { default as DocumentTagsAction } from './DocumentTagsAction.vue'
-export { default as EmbeddingDocumentAction } from './EmbeddingDocumentAction.vue'
+export { default as EmbeddingDocumentAction } from './embedding/EmbeddingDocumentAction.vue'
export { default as GenerateDocumentQuestionsAction } from './GenerateDocumentQuestionsAction.vue'
export { default as DownloadDocumentAction } from './DownloadDocumentAction.vue'
export { default as ExportDocumentAction } from './ExportDocumentAction.vue'
diff --git a/ui/src/views/knowledge-detail/document/components/DocumentStatus.vue b/ui/src/views/knowledge-detail/document/components/DocumentStatus.vue
index bdfb6e7fba4..cd44cb525d4 100644
--- a/ui/src/views/knowledge-detail/document/components/DocumentStatus.vue
+++ b/ui/src/views/knowledge-detail/document/components/DocumentStatus.vue
@@ -2,16 +2,17 @@
import { TOOLTIP_SHOW_DELAY } from '@/components/global/mk-tooltip/constants'
import { computed, ref } from 'vue'
import { DOCUMENT_TASK_STATE, DOCUMENT_TASK_TYPE, STATE_TYPES } from '@/api/enums'
-import type { DocumentStatusMeta, DocumentTaskState, DocumentTaskType, State } from '@/api/types'
+import type { DocumentStatusMeta, DocumentTaskState, DocumentTaskType } from '@/api/types'
+import { DOCUMENT_STATUS_OPTIONS } from '../status'
defineOptions({ name: 'DocumentStatus' })
const props = defineProps<{ status: string; statusMeta?: DocumentStatusMeta | null }>()
const DOCUMENT_TASKS = [
- { type: DOCUMENT_TASK_TYPE.EMBEDDING, label: '向量化', startedStatus: STATE_TYPES.EMBEDDING },
- { type: DOCUMENT_TASK_TYPE.GENERATE_PROBLEM, label: '生成问题', startedStatus: STATE_TYPES.GENERATE },
- { type: DOCUMENT_TASK_TYPE.SYNC, label: '同步', startedStatus: STATE_TYPES.SYNC },
- { type: DOCUMENT_TASK_TYPE.TOKENIZE, label: '分词索引', startedStatus: STATE_TYPES.TOKENIZE },
+ { type: DOCUMENT_TASK_TYPE.EMBEDDING, label: '向量化', startedStatus: 'EMBEDDING' },
+ { type: DOCUMENT_TASK_TYPE.GENERATE_PROBLEM, label: '生成问题', startedStatus: 'GENERATE' },
+ { type: DOCUMENT_TASK_TYPE.SYNC, label: '同步', startedStatus: 'SYNC' },
+ { type: DOCUMENT_TASK_TYPE.TOKENIZE, label: '分词索引', startedStatus: 'TOKENIZE' },
] as const
const DOCUMENT_STATUS_PRIORITY = [
@@ -23,7 +24,7 @@ const DOCUMENT_STATUS_PRIORITY = [
DOCUMENT_TASK_STATE.SUCCESS,
] as const
-const DOCUMENT_STATUS_DISPLAY: Partial> = {
+const DOCUMENT_STATUS_DISPLAY: Partial> = {
[DOCUMENT_TASK_STATE.PENDING]: STATE_TYPES.PENDING,
[DOCUMENT_TASK_STATE.REVOKE]: STATE_TYPES.REVOKE,
// 文件状态沿用 v2:取消完成展示成功,明细完成数仍只统计真正成功的分段。
@@ -36,7 +37,7 @@ interface DocumentTaskStatus {
type: DocumentTaskType
label: string
state: DocumentTaskState
- displayStatus: State
+ displayStatus: keyof typeof DOCUMENT_STATUS_OPTIONS
completed: number
total: number
time: string
@@ -102,7 +103,7 @@ const aggregateStatus = computed(() => getAggregateDocumentStatus(taskStatuses.v
>
-
+
-
@@ -110,7 +111,7 @@ const aggregateStatus = computed(() => getAggregateDocumentStatus(taskStatuses.v
{{ task.label }}:
-
+
完成 {{ task.completed }}/{{ task.total }}
{{ task.time }}
diff --git a/ui/src/views/knowledge-detail/document/index.vue b/ui/src/views/knowledge-detail/document/index.vue
index 900fe0f9587..c4ad8dab301 100644
--- a/ui/src/views/knowledge-detail/document/index.vue
+++ b/ui/src/views/knowledge-detail/document/index.vue
@@ -1,5 +1,5 @@
@@ -259,8 +258,8 @@ onMounted(() => {
:data="documentData"
v-model:pagination-config="paginationConfig"
:max-table-height="210"
- @current-change="loadDocuments"
- @size-change="loadDocuments"
+ @current-change="loadDocuments()"
+ @size-change="loadDocuments()"
@selection-change="handleSelectionChange"
resizable
>
@@ -279,7 +278,7 @@ onMounted(() => {
v-model="selectedDocumentStatus"
mode="single"
label="文件状态"
- :options="documentStatusOptions"
+ :options="DOCUMENT_STATUS_FILTER_OPTIONS"
@change="handleDocumentFilterChange"
/>
@@ -367,10 +366,8 @@ onMounted(() => {
@@ -410,7 +407,7 @@ onMounted(() => {
:document-ids="[row.id]"
:task-type="DOCUMENT_TASK_TYPE.GENERATE_PROBLEM"
label="取消生成问题"
- v-if="isTaskRunning(row, DOCUMENT_TASK_TYPE.GENERATE_PROBLEM)"
+ v-if="isDocumentTaskRunning(row, DOCUMENT_TASK_TYPE.GENERATE_PROBLEM)"
@refresh="refreshAfterOperation"
/>
@@ -474,7 +471,7 @@ onMounted(() => {
:api="DocumentApi"
:knowledge-id="knowledgeId"
v-model:loading="operationLoading"
- :document-ids="[row.id]"
+ :document="row"
@refresh="refreshAfterOperation"
/>
@@ -484,12 +481,11 @@ onMounted(() => {
-
@@ -510,24 +506,20 @@ onMounted(() => {
:disabled="operationLoading"
@refresh="refreshAfterOperation"
/>
+
+ 设置
+
+
+ 添加标签
+
-
+
-
- 更多操作
-
+
+
-
-
{
:disabled="operationLoading"
@refresh="refreshAfterOperation"
/>
-
-
+
{
v-model:loading="operationLoading"
:document-ids="selectedDocumentIds"
:task-type="DOCUMENT_TASK_TYPE.GENERATE_PROBLEM"
- label="取消生成问题"
+ label="取消生成"
@refresh="refreshAfterOperation"
/>
diff --git a/ui/src/views/knowledge-detail/document/status.ts b/ui/src/views/knowledge-detail/document/status.ts
new file mode 100644
index 00000000000..03f04896ac6
--- /dev/null
+++ b/ui/src/views/knowledge-detail/document/status.ts
@@ -0,0 +1,46 @@
+import { DOCUMENT_TASK_STATE, DOCUMENT_TASK_TYPE, STATE_TYPES } from '@/api/enums'
+import type { StatusLabelOptions } from '@/components/global/mk-status-label/types'
+
+// 文档展示阶段独立于执行记录接口状态,筛选值不直接提交给后端。
+export const DOCUMENT_STATUS_OPTIONS = {
+ [STATE_TYPES.PENDING]: { type: 'loading', label: '排队中' },
+ [STATE_TYPES.STARTED]: { type: 'loading', label: '执行中' },
+ EMBEDDING: { type: 'loading', label: '索引中' },
+ GENERATE: { type: 'loading', label: '生成中' },
+ SYNC: { type: 'loading', label: '同步中' },
+ TOKENIZE: { type: 'loading', label: '分词索引中' },
+ [STATE_TYPES.SUCCESS]: { type: 'success', label: '成功' },
+ [STATE_TYPES.FAILURE]: { type: 'failure', label: '失败' },
+ [STATE_TYPES.REVOKE]: { type: 'loading', label: '取消中' },
+ [STATE_TYPES.REVOKED]: { type: 'failure', label: '已取消' },
+} satisfies Record
+
+interface DocumentStatusFilter {
+ label: string
+ value: string
+ status: (typeof DOCUMENT_TASK_STATE)[keyof typeof DOCUMENT_TASK_STATE]
+ task_type?: (typeof DOCUMENT_TASK_TYPE)[keyof typeof DOCUMENT_TASK_TYPE]
+}
+export const DOCUMENT_STATUS_FILTER_OPTIONS: DocumentStatusFilter[] = [
+ { label: DOCUMENT_STATUS_OPTIONS[STATE_TYPES.SUCCESS].label, value: STATE_TYPES.SUCCESS, status: DOCUMENT_TASK_STATE.SUCCESS },
+ { label: DOCUMENT_STATUS_OPTIONS[STATE_TYPES.FAILURE].label, value: STATE_TYPES.FAILURE, status: DOCUMENT_TASK_STATE.FAILURE },
+ {
+ label: DOCUMENT_STATUS_OPTIONS.EMBEDDING.label,
+ value: 'EMBEDDING',
+ status: DOCUMENT_TASK_STATE.STARTED,
+ task_type: DOCUMENT_TASK_TYPE.EMBEDDING,
+ },
+ {
+ label: DOCUMENT_STATUS_OPTIONS.TOKENIZE.label,
+ value: 'TOKENIZE',
+ status: DOCUMENT_TASK_STATE.STARTED,
+ task_type: DOCUMENT_TASK_TYPE.TOKENIZE,
+ },
+ { label: DOCUMENT_STATUS_OPTIONS[STATE_TYPES.PENDING].label, value: STATE_TYPES.PENDING, status: DOCUMENT_TASK_STATE.PENDING },
+ {
+ label: DOCUMENT_STATUS_OPTIONS.GENERATE.label,
+ value: 'GENERATE',
+ status: DOCUMENT_TASK_STATE.STARTED,
+ task_type: DOCUMENT_TASK_TYPE.GENERATE_PROBLEM,
+ },
+]
diff --git a/ui/src/views/knowledge-detail/document/utils.ts b/ui/src/views/knowledge-detail/document/utils.ts
new file mode 100644
index 00000000000..56321e4966a
--- /dev/null
+++ b/ui/src/views/knowledge-detail/document/utils.ts
@@ -0,0 +1,9 @@
+/** 提供文档任务运行状态判断,供页面和文档 Action 复用。 */
+import { DOCUMENT_TASK_STATE } from '@/api/enums'
+import type { DocumentItem, DocumentTaskType } from '@/api/types'
+
+/** 按状态字符串从右起的任务位置判断是否排队或执行中;缺少文档或状态时返回 false。 */
+export function isDocumentTaskRunning(document: DocumentItem | undefined, taskType: DocumentTaskType): boolean {
+ const state = document?.status?.at(-taskType)
+ return state === DOCUMENT_TASK_STATE.PENDING || state === DOCUMENT_TASK_STATE.STARTED
+}
diff --git a/ui/src/views/system/identity/resource-authorization/dialog/BatchSetPermissionDialog.vue b/ui/src/views/system/identity/resource-authorization/dialog/BatchSetPermissionDialog.vue
index a4035add2e6..2fc9786a5e5 100644
--- a/ui/src/views/system/identity/resource-authorization/dialog/BatchSetPermissionDialog.vue
+++ b/ui/src/views/system/identity/resource-authorization/dialog/BatchSetPermissionDialog.vue
@@ -37,7 +37,7 @@ defineExpose({ open, close })
-
+
{{ permissionOption.label }}
diff --git a/ui/src/views/system/resource-management/tool/index.vue b/ui/src/views/system/resource-management/tool/index.vue
index cb89f4fdc8a..c05ae13ccbe 100644
--- a/ui/src/views/system/resource-management/tool/index.vue
+++ b/ui/src/views/system/resource-management/tool/index.vue
@@ -229,7 +229,7 @@ onMounted(() => {
-
+
删除
diff --git a/ui/src/views/trigger/execution-record/ExecutionDetailDrawer.vue b/ui/src/views/trigger/execution-record/ExecutionDetailDrawer.vue
index d89959e7d0b..c4c3dd8ec13 100644
--- a/ui/src/views/trigger/execution-record/ExecutionDetailDrawer.vue
+++ b/ui/src/views/trigger/execution-record/ExecutionDetailDrawer.vue
@@ -1,4 +1,5 @@