diff --git a/ui/src/api/API_README.md b/ui/src/api/API_README.md index b9350227aff..be626f439bc 100644 --- a/ui/src/api/API_README.md +++ b/ui/src/api/API_README.md @@ -432,6 +432,17 @@ System 不读取 workspaceId;飞书在知识库前缀追加 lark/ System/飞书扩展及文件 ID 导入、策略协议需对应部署支持,不能因前端接口存在认定已联调。 文档分页使用 ParamsPage/ResponsePage,名称/创建者/任务/启用/命中/标签筛选支持组合。 +图片与文档共用 Document API 的 `getDocumentPage` 列表接口及响应结构,图片列表额外传入查询参数 +`resource_type: 'image'`,不单独封装图片列表接口。 +Workspace 与 System 资源管理的 `knowledge/image.ts` 提供图片专属接口,通过上下文 `resourceApi.image` 读取。 +`postUploadImagePreview` 通过 POST `image/preview` 按策略生成图片预览, +FormData 重复提交 `file` 并附带 JSON 字符串 `doc_strategy`,返回 ImageDocumentPreview[]; +图片预览结果沿用 DocumentSplitResult 的分段结构,额外包含 preview_id;页面直接展示 content,不进行结构转换。 +`putBatchCreateImageDocuments` 通过 PUT `image/batch_create` 提交 `{ preview_ids }`,返回 DocumentItem[]。 +未生成预览时,`preview_ids` 直接使用上一步上传成功的图片文件 ID;已预览时使用生成结果的预览 ID。 +图片支持 JPG、JPEG、PNG、WEBP、BMP;共享范围尚未装配图片上传和导入接口,不回退到 Workspace。 +System 资源管理接口前缀为 `/system/resource/knowledge//document/image`; +生成预览 POST `preview`,批量添加 PUT `batch_create`。 is_active 为 false 时保留,“全部”省略;tags 传叶子 ID 数组或 NO_TAG,按 tags[] 序列化。 状态使用 DOCUMENT_TASK_STATE 与 DOCUMENT_TASK_TYPE,不用通用 STATE_TYPES;非任务状态省略 task_type。 status_meta 可空,aggs 为 `{ status, count }[]`,state_time 按任务和状态索引;null 聚合状态按忽略处理。 diff --git a/ui/src/api/admin/system/resource-management/knowledge/image.ts b/ui/src/api/admin/system/resource-management/knowledge/image.ts new file mode 100644 index 00000000000..f16546d418a --- /dev/null +++ b/ui/src/api/admin/system/resource-management/knowledge/image.ts @@ -0,0 +1,21 @@ +import { post, put } from '@/api/admin/core/request' +import type { DocumentItem, DocumentStrategy, ImageDocumentPreview } from '@/api/types' + +const getPrefix = (knowledgeId: string) => `/system/resource/knowledge/${knowledgeId}/document/image` + +/** 按图片处理策略上传图片并生成预览。 */ +const postUploadImagePreview = (knowledgeId: string, files: File[], strategy: DocumentStrategy) => { + const data = new FormData() + files.forEach((file) => data.append('file', file)) + data.append('doc_strategy', JSON.stringify(strategy)) + return post(`${getPrefix(knowledgeId)}/preview`, data) +} + +/** 将已上传的图片预览导入知识库。 */ +const putBatchCreateImageDocuments = (knowledgeId: string, previewIds: string[]) => + put<{ preview_ids: string[] }, DocumentItem[]>(`${getPrefix(knowledgeId)}/batch_create`, { preview_ids: previewIds }) + +export default { + postUploadImagePreview, + putBatchCreateImageDocuments, +} diff --git a/ui/src/api/admin/system/resource-management/knowledge/termbase.ts b/ui/src/api/admin/system/resource-management/knowledge/termbase.ts index 02952f12acb..0a95d8ed3f9 100644 --- a/ui/src/api/admin/system/resource-management/knowledge/termbase.ts +++ b/ui/src/api/admin/system/resource-management/knowledge/termbase.ts @@ -11,10 +11,6 @@ const postTermbases = (knowledgeId: string, contents: string[]) => post get>(`${getPrefix(knowledgeId)}/${page.currentPage}/${page.pageSize}`, query) -/** 编辑单个知识库自定义分词条目。 */ -const putTermbase = (knowledgeId: string, termbaseId: string, payload: TermbaseUpdatePayload) => - put(`${getPrefix(knowledgeId)}/${termbaseId}`, payload) - /** 删除单个知识库自定义分词条目。 */ const deleteTermbase = (knowledgeId: string, termbaseId: string) => del(`${getPrefix(knowledgeId)}/${termbaseId}`) @@ -29,7 +25,6 @@ const postExportTermbases = (knowledgeId: string, termbaseIds: string[]) => export default { postTermbases, getTermbasePage, - putTermbase, deleteTermbase, putBatchDeleteTermbases, postExportTermbases, diff --git a/ui/src/api/admin/system/shared-resources/knowledge/termbase.ts b/ui/src/api/admin/system/shared-resources/knowledge/termbase.ts index f0ed9c38ab7..f5a8e28802a 100644 --- a/ui/src/api/admin/system/shared-resources/knowledge/termbase.ts +++ b/ui/src/api/admin/system/shared-resources/knowledge/termbase.ts @@ -11,10 +11,6 @@ const postTermbases = (knowledgeId: string, contents: string[]) => post get>(`${getPrefix(knowledgeId)}/${page.currentPage}/${page.pageSize}`, query) -/** 编辑单个知识库自定义分词条目。 */ -const putTermbase = (knowledgeId: string, termbaseId: string, payload: TermbaseUpdatePayload) => - put(`${getPrefix(knowledgeId)}/${termbaseId}`, payload) - /** 删除单个知识库自定义分词条目。 */ const deleteTermbase = (knowledgeId: string, termbaseId: string) => del(`${getPrefix(knowledgeId)}/${termbaseId}`) @@ -29,7 +25,6 @@ const postExportTermbases = (knowledgeId: string, termbaseIds: string[]) => export default { postTermbases, getTermbasePage, - putTermbase, deleteTermbase, putBatchDeleteTermbases, postExportTermbases, diff --git a/ui/src/api/admin/workspace/knowledge/image.ts b/ui/src/api/admin/workspace/knowledge/image.ts new file mode 100644 index 00000000000..4d3fc183f81 --- /dev/null +++ b/ui/src/api/admin/workspace/knowledge/image.ts @@ -0,0 +1,22 @@ +import { post, put } from '@/api/admin/core/request' +import type { DocumentItem, DocumentStrategy, ImageDocumentPreview } from '@/api/types' +import { getWorkspaceId } from '@/utils/resource-context' + +const getPrefix = (knowledgeId: string) => `/workspace/${getWorkspaceId()}/knowledge/${knowledgeId}/document/image` + +/** 按图片处理策略上传图片并生成预览。 */ +const postUploadImagePreview = (knowledgeId: string, files: File[], strategy: DocumentStrategy) => { + const data = new FormData() + files.forEach((file) => data.append('file', file)) + data.append('doc_strategy', JSON.stringify(strategy)) + return post(`${getPrefix(knowledgeId)}/preview`, data) +} + +/** 将已上传的图片预览导入知识库。 */ +const putBatchCreateImageDocuments = (knowledgeId: string, previewIds: string[]) => + put<{ preview_ids: string[] }, DocumentItem[]>(`${getPrefix(knowledgeId)}/batch_create`, { preview_ids: previewIds }) + +export default { + postUploadImagePreview, + putBatchCreateImageDocuments, +} diff --git a/ui/src/api/admin/workspace/knowledge/termbase.ts b/ui/src/api/admin/workspace/knowledge/termbase.ts index 480706b2266..d859f34959a 100644 --- a/ui/src/api/admin/workspace/knowledge/termbase.ts +++ b/ui/src/api/admin/workspace/knowledge/termbase.ts @@ -12,10 +12,6 @@ const postTermbases = (knowledgeId: string, contents: string[]) => post get>(`${getPrefix(knowledgeId)}/${page.currentPage}/${page.pageSize}`, query) -/** 编辑单个知识库自定义分词条目。 */ -const putTermbase = (knowledgeId: string, termbaseId: string, payload: TermbaseUpdatePayload) => - put(`${getPrefix(knowledgeId)}/${termbaseId}`, payload) - /** 删除单个知识库自定义分词条目。 */ const deleteTermbase = (knowledgeId: string, termbaseId: string) => del(`${getPrefix(knowledgeId)}/${termbaseId}`) @@ -30,7 +26,6 @@ const postExportTermbases = (knowledgeId: string, termbaseIds: string[]) => export default { postTermbases, getTermbasePage, - putTermbase, deleteTermbase, putBatchDeleteTermbases, postExportTermbases, diff --git a/ui/src/api/enums/document.ts b/ui/src/api/enums/document.ts index 0b1f5d8b4ec..e507ed683b6 100644 --- a/ui/src/api/enums/document.ts +++ b/ui/src/api/enums/document.ts @@ -6,3 +6,6 @@ export const DOCUMENT_TASK_STATE = { PENDING: '0', STARTED: '1', SUCCESS: '2', F /** 文档状态字符串中各任务的位置。 */ export const DOCUMENT_TASK_TYPE = { EMBEDDING: 1, GENERATE_PROBLEM: 2, SYNC: 3, TOKENIZE: 4 } as const + +/** 独立图片视觉增强的处理状态。 */ +export const IMAGE_PROCESS_STATUS = { PENDING: 'pending', SUCCESS: 'success', FAILURE: 'failure', SKIPPED: 'skipped' } as const diff --git a/ui/src/api/types/document.ts b/ui/src/api/types/document.ts index c14681eddd2..29aab5420b5 100644 --- a/ui/src/api/types/document.ts +++ b/ui/src/api/types/document.ts @@ -1,4 +1,4 @@ -import type { DOCUMENT_HIT_HANDLING, DOCUMENT_TASK_STATE, DOCUMENT_TASK_TYPE } from '@/api/enums' +import type { DOCUMENT_HIT_HANDLING, DOCUMENT_TASK_STATE, DOCUMENT_TASK_TYPE, IMAGE_PROCESS_STATUS } from '@/api/enums' import type { DocumentStrategy, KnowledgeGeneratePayload, 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] @@ -102,3 +102,15 @@ export interface LarkDocumentList { export interface LarkDocumentImportPayload extends Pick { doc_strategy: DocumentStrategy } + +/** 独立图片上传后的处理预览。 */ +export interface ImageDocumentPreview extends DocumentSplitResult { + preview_id: string + name: string + url: string + caption: string + ocr_text: string + description: string + process_status: (typeof IMAGE_PROCESS_STATUS)[keyof typeof IMAGE_PROCESS_STATUS] + process_error: string +} diff --git a/ui/src/components/COMPONENT_README.md b/ui/src/components/COMPONENT_README.md index 457c0cc06a0..a14e49a8120 100644 --- a/ui/src/components/COMPONENT_README.md +++ b/ui/src/components/COMPONENT_README.md @@ -73,7 +73,7 @@ Element Plus 已在 Admin、Chat 入口注册。使用前先检查原生 API, - `close()` 只发起关闭,表单清理放在 `closed`,避免关闭动画中内容跳变;依赖本次参数的默认值、 编辑快照、动态字段及每次打开必须重新查询的数据仍在 `open()` 初始化。 父级负责校验或请求时,子组件提交数据后由父级成功调用 `close()`,失败保持打开。 -- 仅登录表单支持回车提交;其他业务表单使用 `@submit.prevent`,保存、添加由按钮触发。 +- 仅登录表单及 `MkQuickCreate` 行内快捷创建支持回车提交;其他业务表单使用 `@submit.prevent`,保存、添加由按钮触发。 自定义 `submit` 事件不等于原生表单提交。 ## 全局 UI 组件 @@ -281,6 +281,7 @@ Element Plus `append`,仅在传入该插槽时将直属表体滚动容器设 由页面管理请求、`loading`、成功提示及列表刷新;成功后通过组件 ref 调用 `close()` 清空并收起,失败保留输入。 `text` 默认为“快速创建”,`placeholder` 默认为“请输入名称”,`maxlength` 默认为 256。 组件不使用 Form 或表单校验;空名称、纯空白及提交期间禁用创建按钮,提交前 trim。 +输入框按 Enter 触发同一创建流程,输入法选词时不提交。 Esc 或取消清理草稿;`loading` 默认为 false,为 true 时禁止创建和取消。组件不等待 Promise,不自动收起。 `paginationConfig` 包含 `currentPage`、`pageSize`、`total` 和可选 `pageSizes`;不传则隐藏分页器, @@ -541,8 +542,15 @@ Logo 数据统一从 Theme Store 获取。 兼容 UploadUserFile,支持 file_id、errMsg、canRetry;类型从组件 `types.ts` 导入。 - `multiple` 默认 false;单文件展示卡片和更换入口,多文件保留拖拽区、文件夹选择与列表。 +- 多文件 `mode` 默认 `file`,使用文件图标和上传进度;`mode="image"` 按选择顺序展示 104×104 的 + `el-image` 方块,不使用 Card,不展示汇总、文件名或状态文案。等待时中央显示空进度条,上传时显示实际进度, + 成功或失败后隐藏进度条。右上角提供删除,未上传成功时 + 在原图上叠加灰色遮罩,成功后移除;网络失败和大小超限使用红框,仅网络失败在中央提供重试。 + 失败且有错误信息时通过 MkTooltip 展示文件名及失败原因,其他情况禁用提示。内部仍维护上传状态与 uploadingCount,本地缩略图 URL 在文件移除 + 和组件卸载时释放。单文件不使用 mode。 - `accept`、`limit` 控制格式/数量,多文件 `sizeLimit` 默认 100MB;格式、空文件、重复选择移除, - 大小超限保留不可重试 fail 卡片,网络失败可重试。超量发出 exceed,DS_Store 格式不支持时静默移除。 + 大小超限保留不可重试 fail 文件,图片模式保留原图和红框;网络失败可重试。 + 超量发出 exceed,DS_Store 格式不支持时静默移除。 - `dragText`、`selectText`、`tipText`、`replaceText` 覆盖文案;download 作用域插槽提供下载操作。 - 多文件通过 `inject('upload')` 获取 `DragUploadHandler`,返回 `{ request: Promise, abort }`。 change 后等待调用方回写和校验,再上传 ready 文件;响应成功才标记 success 并提取 file_id,100% 仅表示传输完成。 @@ -773,6 +781,9 @@ Skills 场景传入 `[TOOL_TYPE.SKILL]`,并通过 `title` 指定标题。两 ### SelectModel +`loading` 默认为 `false`,透传给 `el-select` 展示列表加载状态;模型选项加载使用 `loading`,不因此禁用选择器。 +`disabled` 继续表示只读或业务禁用。 + `teleported` 默认为 `false`,画布节点内保留此默认值,使下拉浮层跟随画布。 画布外的表单、Dialog 和 Drawer 显式传入 `teleported`(即 `true`),将下拉浮层挂载到 body, 避免被滚动容器裁剪。动态表单 Model 字段可通过透传属性配置该值。 diff --git a/ui/src/components/business/select-model/index.vue b/ui/src/components/business/select-model/index.vue index e741b04ae9f..c29a1269710 100644 --- a/ui/src/components/business/select-model/index.vue +++ b/ui/src/components/business/select-model/index.vue @@ -1,5 +1,5 @@ @@ -54,6 +60,7 @@ defineExpose({ close: resetDraft }) class="mr-3 w-80!" :placeholder="props.placeholder" :maxlength="props.maxlength" + @keydown="handleKeydown" show-word-limit clearable /> diff --git a/ui/src/components/mk-drag-upload/MultipleFileUpload.vue b/ui/src/components/mk-drag-upload/MultipleFileUpload.vue index 31238bdd077..a77d197845e 100644 --- a/ui/src/components/mk-drag-upload/MultipleFileUpload.vue +++ b/ui/src/components/mk-drag-upload/MultipleFileUpload.vue @@ -1,7 +1,7 @@ diff --git a/ui/src/views/knowledge-detail/library/image/upload-image/ButtonUploadImage.vue b/ui/src/views/knowledge-detail/library/image/upload-image/ButtonUploadImage.vue new file mode 100644 index 00000000000..899ddf77aed --- /dev/null +++ b/ui/src/views/knowledge-detail/library/image/upload-image/ButtonUploadImage.vue @@ -0,0 +1,40 @@ + + + diff --git a/ui/src/views/knowledge-detail/library/image/upload-image/ImageProcessingForm.vue b/ui/src/views/knowledge-detail/library/image/upload-image/ImageProcessingForm.vue new file mode 100644 index 00000000000..511983fc5ec --- /dev/null +++ b/ui/src/views/knowledge-detail/library/image/upload-image/ImageProcessingForm.vue @@ -0,0 +1,171 @@ + + + diff --git a/ui/src/views/knowledge-detail/library/image/upload-image/UploadImageDrawer.vue b/ui/src/views/knowledge-detail/library/image/upload-image/UploadImageDrawer.vue new file mode 100644 index 00000000000..b275d8c956d --- /dev/null +++ b/ui/src/views/knowledge-detail/library/image/upload-image/UploadImageDrawer.vue @@ -0,0 +1,172 @@ + + + diff --git a/ui/src/views/knowledge-detail/library/tags/index.vue b/ui/src/views/knowledge-detail/library/tags/index.vue index 904c0f4ace4..be20334a9e4 100644 --- a/ui/src/views/knowledge-detail/library/tags/index.vue +++ b/ui/src/views/knowledge-detail/library/tags/index.vue @@ -185,7 +185,6 @@ onMounted(() => { :data="tagRows" selection-group-key="key" :span-method="spanMethod" - :max-table-height="210" @selection-change="handleSelectionChange" @current-change="clearSelection" @size-change="clearSelection" diff --git a/ui/src/views/knowledge-detail/setting/scheduled-sync/components/SyncLogTable.vue b/ui/src/views/knowledge-detail/setting/scheduled-sync/components/SyncLogTable.vue index e5778cb2f57..43cd516ed35 100644 --- a/ui/src/views/knowledge-detail/setting/scheduled-sync/components/SyncLogTable.vue +++ b/ui/src/views/knowledge-detail/setting/scheduled-sync/components/SyncLogTable.vue @@ -72,7 +72,7 @@ watch( v-model:pagination-config="paginationConfig" v-loading="loading" :data="syncLogs" - :max-table-height="260" + :max-table-height="200" @current-change="loadSyncLogs" @size-change="loadSyncLogs" > diff --git a/ui/src/views/knowledge-detail/termbase/index.vue b/ui/src/views/knowledge-detail/termbase/index.vue index 927a3fc3df2..073a8741c65 100644 --- a/ui/src/views/knowledge-detail/termbase/index.vue +++ b/ui/src/views/knowledge-detail/termbase/index.vue @@ -170,7 +170,6 @@ onMounted(() => { v-model:pagination-config="paginationConfig" v-loading="loading || operationLoading" :data="termbases" - :max-table-height="210" @current-change="loadTermbases" @size-change="loadTermbases" @selection-change="handleSelectionChange" diff --git a/ui/src/views/knowledge/create-knowledge/components/DocumentStrategyForm.vue b/ui/src/views/knowledge/create-knowledge/components/DocumentStrategyForm.vue index 440a86b2155..965e836ce8f 100644 --- a/ui/src/views/knowledge/create-knowledge/components/DocumentStrategyForm.vue +++ b/ui/src/views/knowledge/create-knowledge/components/DocumentStrategyForm.vue @@ -224,7 +224,7 @@ defineExpose({ validate, getStrategy, setStrategy }) v-model="visualModelId" :options="modelOptions" :provider-options="providerOptions" - :disabled="modelOptionsLoading" + :loading="modelOptionsLoading" placeholder="请选择视觉模型" teleported can-add