From 8a4d82b1076e366916919ad3fbe8840068d35489 Mon Sep 17 00:00:00 2001 From: "xinxin.wu" Date: Mon, 12 Oct 2026 14:24:10 +0800 Subject: [PATCH] refactor: database management tags --- ui/src/api/API_README.md | 8 +- ui/src/api/admin/workspace/knowledge/tags.ts | 5 +- ui/src/views/VIEW_README.md | 22 ++- ...rawer.vue => AssociatedDocumentDrawer.vue} | 105 +++++------ .../tags/components/CreateTagDialog.vue | 113 ++++++++++++ .../library/tags/components/EditTagDialog.vue | 83 +++++++++ .../library/tags/components/TagFormDialog.vue | 172 ------------------ .../knowledge-detail/library/tags/index.vue | 61 ++++--- 8 files changed, 300 insertions(+), 269 deletions(-) rename ui/src/views/knowledge-detail/library/tags/{TagDocumentDrawer.vue => AssociatedDocumentDrawer.vue} (67%) create mode 100644 ui/src/views/knowledge-detail/library/tags/components/CreateTagDialog.vue create mode 100644 ui/src/views/knowledge-detail/library/tags/components/EditTagDialog.vue delete mode 100644 ui/src/views/knowledge-detail/library/tags/components/TagFormDialog.vue diff --git a/ui/src/api/API_README.md b/ui/src/api/API_README.md index 20cf1688dd6..80b9da3756c 100644 --- a/ui/src/api/API_README.md +++ b/ui/src/api/API_README.md @@ -630,7 +630,9 @@ System 接口由资源管理服务提供;本地开源后端没有对应扩展 标签查询暂保留在 `workspace/knowledge/knowledge.ts`,`getKnowledgeTags(knowledgeId)` 返回非分页 `KnowledgeTagGroup[]`; 标签值中的可选 `doc_count` 表示关联文档数量。`postKnowledgeTags` 直接提交 `KnowledgeTagPayload[]`(`key`、`value`),用于创建标签和新增标签值; -`putKnowledgeTag` 向 `/` 提交 `KnowledgeTagUpdatePayload`(`id`、`key`、`value`),仅用于已有的单项编辑。 +`putKnowledgeTag` 向 `/` 提交完整 `KnowledgeTagUpdatePayload`(`id`、`key`、`value`), +路径 `tagId` 与载荷 `id` 保持一致。重命名提交新 `key` 与该行原 `value`,服务端更新当前标签所属同名组; +编辑单个值提交原 `key` 与新 `value`,保留同组重复值校验,不提交其他行的值。 `deleteKnowledgeTag` 按路径末尾 `key` 删除整个标签、`one` 删除单个标签值; `putBatchDeleteKnowledgeTags` 使用 PUT `batch_delete`,请求体直接为标签 ID 数组,不包装为 `id_list`。 `postImportKnowledgeTags(knowledgeId, file)` POST 到 `import`,使用 FormData 的单个 `file` 字段上传 XLS 或 XLSX, @@ -638,8 +640,8 @@ System 接口由资源管理服务提供;本地开源后端没有对应扩展 服务端跳过文件内及知识库已有的重复组合,校验所有工作表后统一写入。空行跳过,标签与值分别限制为 64、128 字。 `exportKnowledgeTagTemplate(knowledgeId)` GET 到 `template/export`,复用 `getExportFile` 下载“标签模板.xlsx”, 服务端返回与提供的模板一致的表头和示例数据。两个接口均校验目标知识库的工作空间及标签创建权限。 -标签类型统一维护在 `types/knowledge.ts`。多行编辑等待 V3 新协议,不组合旧接口模拟整组保存, -也不声明推测的新接口。 +标签类型统一维护在 `types/knowledge.ts`。创建支持多行,编辑仅支持重命名或单个值修改, +不声明多行编辑接口,也不组合旧接口模拟多行保存。 文档与标签的关联由 `workspace/knowledge/document.ts` 维护,不使用标签删除接口代替取消关联。 `getDocumentTags` 查询 `document//tags`,返回分组标签; diff --git a/ui/src/api/admin/workspace/knowledge/tags.ts b/ui/src/api/admin/workspace/knowledge/tags.ts index 65d49a71c17..3a0e8689dce 100644 --- a/ui/src/api/admin/workspace/knowledge/tags.ts +++ b/ui/src/api/admin/workspace/knowledge/tags.ts @@ -4,13 +4,10 @@ import { getWorkspaceId } from '@/utils/resource-context' const getPrefix = (knowledgeId: string) => `/workspace/${getWorkspaceId()}/knowledge/${knowledgeId}/tags` -// TODO:接入标签整组/多行编辑接口。 -// 请求路径、参数和响应类型以 V3 接口协议为准。 - /** 创建知识库标签或为已有标签新增值。 */ const postKnowledgeTags = (knowledgeId: string, tags: KnowledgeTagPayload[]) => post(getPrefix(knowledgeId), tags) -/** 编辑单个知识库标签,不支持多行编辑。 */ +/** 重命名知识库标签或编辑单个标签值。 */ const putKnowledgeTag = (knowledgeId: string, tagId: string, tag: KnowledgeTagUpdatePayload) => put(`${getPrefix(knowledgeId)}/${tagId}`, tag) diff --git a/ui/src/views/VIEW_README.md b/ui/src/views/VIEW_README.md index 4765f608441..5063888bb2d 100644 --- a/ui/src/views/VIEW_README.md +++ b/ui/src/views/VIEW_README.md @@ -478,7 +478,7 @@ Dialog。新增或重命名文件时,应同步更新所有导入和页面功 | `home/index.vue` | Workspace 首页 | | `knowledge/index.vue` | 工作空间知识库目录与知识库卡片页面 | | `knowledge-detail/index.vue` | 知识库详情页面 | -| `knowledge-detail/library/tags/index.vue` | 标签分组查询、创建、单值编辑、删除与关联文档管理 | +| `knowledge-detail/library/tags/index.vue` | 标签分组查询、创建、重命名、单值编辑、删除与关联文档管理 | | `knowledge-detail/setting/base/index.vue` | 知识库基本信息、来源配置与上传限制设置 | | `knowledge-detail/setting/document-strategy/index.vue` | Web 知识库文档处理策略回填、校验与保存 | | `knowledge-detail/setting/scheduled-sync/index.vue` | Web、飞书与工作流知识库定时同步设置与日志 | @@ -874,15 +874,19 @@ library 的接口调整已撤回,文档和标签沿用原有调用方式,Sys 标签名旁提供新增值、编辑标签、删除标签;行末提供编辑值和删除值, 数量链接打开同目录 `TagDocumentDrawer.vue`。 -`library/tags/components/TagFormDialog.vue` 为标签管理和后续文档入口共用的创建、编辑弹窗, +`library/tags/components/CreateTagDialog.vue` 为顶部创建和标签旁新增值共用的创建弹窗, 接收知识库 ID 与完整 Tags API,参考添加成员流程复用 `MkDialog`、`el-form` 和 `MkFormList`。 -创建允许多行标签名与值;新增值锁定标签名;编辑值回填单项并锁定标签名。 -标签名最多 64 字、标签值最多 128 字,校验必填、纯空白和表单内重复组合。 -创建与单个已有标签值编辑使用已确认接口;整组编辑或编辑中增删行走独立请求入口, -保留编辑前快照、已有 ID 与清理首尾空格后的当前草稿,交由请求入口按 V3 协议映射参数。 -所有分支共用校验、loading、成功关闭和刷新;多行接口未接入时仅提示并保留草稿, -不即时删除、不拼接多次旧请求、不模拟成功。 -常驻弹窗在 `closed` 清理草稿和校验。 +创建允许多行标签名与值;新增值通过 `open(key)` 回填并锁定标签名,新增行沿用该名称。 +顶部创建标题为“创建标签”,传入标签名时标题为“添加标签值”。 +两个入口统一通过 `postKnowledgeTags` 提交标签数组,校验表单内重复组合。 +同目录 `EditTagDialog.vue` 共用标签重命名和单值编辑,通过 `open(tag, field)` 回填独立草稿。 +重命名只显示标签名;编辑值只显示一行并锁定标签名。两个入口均提交完整 `id`、`key`、`value`, +请求路径 ID 与载荷 ID 一致,仅修改当前编辑字段,未编辑字段原样保留。 +两种编辑均使用 `putKnowledgeTag`,不提供增删行或多行编辑,不组合多个请求模拟保存。 +两个弹窗均保留 `MkDialog` 默认 600px 宽度,标签名最多 64 字、标签值最多 128 字, +校验必填与纯空白,提交时清理可编辑字段首尾空格。各自维护 loading、防重复提交与关闭保护; +成功后提示、关闭并发出 `refresh`,失败保留草稿;常驻实例在 `closed` 清理草稿和校验。 +后续文档入口直接复用上述弹窗,不复制创建和编辑逻辑。 `library/tags/components/ButtonImportTags.vue` 在同一组件内维护导入按钮与弹窗, 复用 `MkDialog`、`el-form` 和 `MkDragUpload`,不再单独拆分导入 Dialog。 diff --git a/ui/src/views/knowledge-detail/library/tags/TagDocumentDrawer.vue b/ui/src/views/knowledge-detail/library/tags/AssociatedDocumentDrawer.vue similarity index 67% rename from ui/src/views/knowledge-detail/library/tags/TagDocumentDrawer.vue rename to ui/src/views/knowledge-detail/library/tags/AssociatedDocumentDrawer.vue index e91262e3d60..a77629e133f 100644 --- a/ui/src/views/knowledge-detail/library/tags/TagDocumentDrawer.vue +++ b/ui/src/views/knowledge-detail/library/tags/AssociatedDocumentDrawer.vue @@ -27,7 +27,6 @@ const documentData = ref([]) const paginationConfig = ref({ currentPage: 1, pageSize: 10, total: 0 }) const documentTableRef = ref<{ clearSelection: () => void }>() const selectedDocuments = ref([]) -const selectedDocumentIds = computed(() => new Set(selectedDocuments.value.map(({ id }) => id))) /* 两个页签复用同一文档查询,未关联页通过 tag_exclude 排除当前标签。 */ function loadDocuments(): Promise { @@ -78,10 +77,6 @@ function handleSelectionChange(selection: unknown[]) { selectedDocuments.value = selection as DocumentItem[] } -function getRowClass({ row }: { row: DocumentItem }) { - return selectedDocumentIds.value.has(row.id) ? '[&>td]:bg-primary/10!' : '' -} - function handleDocumentFilterChange() { if (loading.value) return paginationConfig.value.currentPage = 1 @@ -157,58 +152,54 @@ defineExpose({ open }) - -
- - - - - - - - - - - - - - - - -
+ + + + + + + + + + + + + + + + diff --git a/ui/src/views/knowledge-detail/library/tags/components/CreateTagDialog.vue b/ui/src/views/knowledge-detail/library/tags/components/CreateTagDialog.vue new file mode 100644 index 00000000000..29d523ad10b --- /dev/null +++ b/ui/src/views/knowledge-detail/library/tags/components/CreateTagDialog.vue @@ -0,0 +1,113 @@ + + + diff --git a/ui/src/views/knowledge-detail/library/tags/components/EditTagDialog.vue b/ui/src/views/knowledge-detail/library/tags/components/EditTagDialog.vue new file mode 100644 index 00000000000..42f167214a1 --- /dev/null +++ b/ui/src/views/knowledge-detail/library/tags/components/EditTagDialog.vue @@ -0,0 +1,83 @@ + + + diff --git a/ui/src/views/knowledge-detail/library/tags/components/TagFormDialog.vue b/ui/src/views/knowledge-detail/library/tags/components/TagFormDialog.vue deleted file mode 100644 index 2bc0bec3086..00000000000 --- a/ui/src/views/knowledge-detail/library/tags/components/TagFormDialog.vue +++ /dev/null @@ -1,172 +0,0 @@ - - - diff --git a/ui/src/views/knowledge-detail/library/tags/index.vue b/ui/src/views/knowledge-detail/library/tags/index.vue index 17c6503fb76..729381e880e 100644 --- a/ui/src/views/knowledge-detail/library/tags/index.vue +++ b/ui/src/views/knowledge-detail/library/tags/index.vue @@ -8,9 +8,10 @@ import KnowledgeApi from '@/api/admin/workspace/knowledge/knowledge' import DocumentApi from '@/api/admin/workspace/knowledge/document' import type { KnowledgeTagGroup, KnowledgeTagUpdatePayload } from '@/api/types' import { MsgConfirm, MsgSuccess } from '@/utils/message' -import TagFormDialog from './components/TagFormDialog.vue' +import CreateTagDialog from './components/CreateTagDialog.vue' +import EditTagDialog from './components/EditTagDialog.vue' import ButtonImportTags from './components/ButtonImportTags.vue' -import TagDocumentDrawer from './TagDocumentDrawer.vue' +import TagDocumentDrawer from './AssociatedDocumentDrawer.vue' defineOptions({ name: 'TagManagementView' }) defineProps() @@ -31,7 +32,6 @@ const tagPage = ref({ currentPage: 1, pageSize: 10 }) const tagTableRef = ref<{ clearSelection: () => void }>() const selectedTags = ref([]) const hoveredTagKey = ref(null) -const selectedTagIds = computed(() => new Set(selectedTags.value.map(({ id }) => id))) /* 保留完整分组进行搜索和分页,避免同一标签跨页拆开。 */ const filteredTagGroups = computed(() => { @@ -88,11 +88,6 @@ function spanMethod({ column, row }: { column: TableColumnCtx; row: if (column.type === 'selection' || column.property === 'key') return row.rowspan ? [row.rowspan, 1] : [0, 0] } -function getCellClass({ row }: { row: TagTableRow }) { - if (selectedTagIds.value.has(row.id)) return 'bg-primary/10!' - return hoveredTagKey.value === row.key ? 'bg-N100!' : '' -} - function handleCellEnter(row: TagTableRow) { hoveredTagKey.value = row.key } @@ -102,24 +97,24 @@ function handleCellLeave() { } /* 共用创建、编辑弹窗和关联文档抽屉。 */ -const tagFormRef = ref>() +const createTagRef = ref>() +const editTagRef = ref>() const tagDocumentRef = ref>() function handleOpenCreateTag() { - tagFormRef.value?.open() + createTagRef.value?.open() } function handleOpenAddTagValue(row: TagTableRow) { - tagFormRef.value?.open({ mode: 'add-value', key: row.key }) + createTagRef.value?.open(row.key) } function handleOpenEditTag(row: TagTableRow) { - const group = tagGroups.value.find(({ key }) => key === row.key) - if (group) tagFormRef.value?.open({ mode: 'edit-group', group }) + editTagRef.value?.open(row, 'key') } function handleOpenEditTagValue(row: TagTableRow) { - tagFormRef.value?.open({ mode: 'edit-value', tag: { id: row.id, key: row.key, value: row.value } }) + editTagRef.value?.open(row, 'value') } function handleOpenTagDocuments(row: TagTableRow) { @@ -200,10 +195,7 @@ onMounted(() => { v-loading="loading || operationLoading" :data="tagRows" :span-method="spanMethod" - :cell-class-name="getCellClass" :max-table-height="210" - row-key="id" - row-class-name="group" @selection-change="handleSelectionChange" @current-change="clearSelection" @size-change="clearSelection" @@ -216,12 +208,24 @@ onMounted(() => {
{{ row.key }}
- - + + + + + + - + + + + + - + + + + +
@@ -237,9 +241,17 @@ onMounted(() => { @@ -248,6 +260,7 @@ onMounted(() => { 删除 - + +