Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions ui/src/api/admin/workspace/knowledge/document.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,15 +75,15 @@ const putLarkDocumentSync = (knowledgeId: string, documentId: string) =>
const putMulLarkSyncDocument = (knowledgeId: string, documentIds: string[]) =>
put<{ id_list: string[] }, boolean>(`/workspace/${getWorkspaceId()}/knowledge/lark/${knowledgeId}/_batch`, { id_list: documentIds })

/** 保存单个文档的召回及来源设置。 */
/** 单个文档设置。 */
const putDocumentSetting = (knowledgeId: string, documentId: string, data: DocumentSettingPayload) =>
put(`${getPrefix(knowledgeId)}/${documentId}`, data)

/** 批量保存文档召回设置。 */
/** 批量文档设置。 */
const putBatchDocumentSetting = (knowledgeId: string, documentIds: string[], data: DocumentSettingPayload) =>
put(`${getPrefix(knowledgeId)}/batch_hit_handling`, { ...data, id_list: documentIds })

/** 将选中文档迁移至目标知识库。 */
/** 批量/单个迁移至目标知识库。 */
const putMigrateDocuments = (knowledgeId: string, targetKnowledgeId: string, documentIds: string[]) =>
put(`${getPrefix(knowledgeId)}/migrate/${targetKnowledgeId}`, documentIds)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ defineExpose({ open, close })

<template>
<MkDialog v-model="visible" title="生成问题" @closed="emit('closed')">
<el-form ref="formRef" :model="form" label-position="top" v-loading="loading || modelLoading" @submit.prevent>
<el-form ref="formRef" :model="form" label-position="top" require-asterisk-position="right" v-loading="loading || modelLoading" @submit.prevent>
<el-alert :closable="false" :title="promptTip" type="primary" class="mb-4! items-start! whitespace-pre-line" show-icon>
<template #icon><MkIcon name="icon_info_filled" class="mt-2" /></template>
</el-alert>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ const selectedKnowledgeIds = computed({
</div>
</el-option>
<template #label="{ label, value }">
<span class="inline-flex max-w-full items-center gap-2">
<KnowledgeIcon :type="knowledgeById.get(value)?.type" :size="14" class="shrink-0" style="--el-avatar-border-radius: 4px" />
<span class="flex-align-center gap-2">
<KnowledgeIcon :type="knowledgeById.get(value)?.type" :size="16" class="shrink-0 small" />
<span class="truncate" :title="label">{{ label }}</span>
</span>
</template>
Expand Down
14 changes: 13 additions & 1 deletion ui/src/views/VIEW_README.md
Original file line number Diff line number Diff line change
Expand Up @@ -717,6 +717,10 @@ Web 来源地址与选择器仅在单项设置中编辑,批量设置不展示
`MigrateDocumentAction` 选择目标知识库,`DocumentTagsAction` 添加已有标签和移除文档标签关联;
生成问题复用公共配置弹窗,其余 Action 按下文维护各自专属弹窗。组件接收完整 Document API、知识库 ID、文档 ID 或文档数组及禁用状态,
点击时快照本次操作对象,内部管理弹窗和请求,成功后通过 `refresh` 通知页面刷新,失败保留输入。
迁移操作集中在 `document/action-dropdown/migrate/`,`MigrateDocumentAction.vue` 负责入口与弹窗按需挂载,
`DocumentMigrateDialog.vue` 负责目标知识库查询、选择及迁移请求;关闭动画结束后卸载,失败保留输入。
Action 继续通过上层 `action-dropdown/index.ts` 导出,单项与批量入口共用;入口文案由必填 `label` 传入,
图标由可选 `icon` 传入,不传时不显示图标。
单项向量化仅传 `document`,内部判断排队或执行状态,固定展示行内图标按钮,分别调用单项向量化和取消接口。
批量向量化仅传 `document-ids`,使用独立的 `BatchEmbeddingAction` 普通按钮;
批量取消向量化继续使用 `BatchCancelTaskAction` 菜单项,单项和批量请求均通过 `v-model:loading` 共用页面操作状态。
Expand All @@ -730,7 +734,15 @@ Action 仍通过上层 `action-dropdown/index.ts` 导出,弹窗仅在目录内
提交同一文档批量接口并通过 `refresh` 刷新状态;通过 `v-model:loading` 复用页面操作状态。
公共弹窗按需挂载、关闭后卸载,不负责选择提交接口。当前尚无段落页,后续段落入口复用公共
弹窗时关闭 `showParagraphScope`,由段落 Action 提交段落接口,不扩展公共弹窗的接口分支。
设置组件通过 `batch` 区分单项与批量,标签组件通过 `manageTags` 区分标签管理与批量添加。
设置操作集中在 `document/action-dropdown/setting/`,通过上层 `index.ts` 导出两个入口。
`SettingDocumentAction` 接收单个 `document`,展示菜单项;`BatchSettingDocumentAction` 接收
`documentIds`,展示批量设置按钮。入口文案由 `label` 传入,单项图标由可选 `icon` 传入。
两个入口通过 `v-model:loading` 共用页面操作状态,打开时固定操作目标,分别调用单项和批量设置接口。
共用的 `DocumentSettingDialog.vue` 负责表单回填、校验和提交数据,按需挂载、关闭动画结束后卸载;
弹窗通过 `batch` 参数区分批量模式,批量入口显式传入;文档地址和选择器仅在 `!batch` 且为 Web 知识库时显示。
单项保留文档原有 `meta`,批量使用默认配置并将 `allow_download`
放在顶层。请求与成功关闭由 Action 负责,成功通知页面刷新,失败保留表单。
标签组件通过 `manageTags` 区分标签管理与批量添加。
分词索引集中在 `document/action-dropdown/tokenize/`:`TokenizeDocumentAction` 接收单个 `document`,
按运行状态调用单项分词或取消接口;`BatchTokenizeAction` 接收 `documentIds`,仅调用批量分词接口。
两个 Action 均处理全部分段状态,通过 `v-model:loading` 共用页面操作状态,成功后通知页面刷新。
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ function handleClosed() {
@click="handleOpenDialog"
/>
<MkDialog v-model="visible" :title="manage ? '标签设置' : '添加标签'" :show-close="!loading && !optionLoading" @closed="handleClosed">
<el-form label-position="top" @submit.prevent>
<el-form label-position="top" require-asterisk-position="right" @submit.prevent>
<el-form-item label="标签">
<el-select
v-model="selectedTagIds"
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ export { default as BatchGenerateQuestionsAction } from './generate-questions/Ba
export { default as DownloadDocumentAction } from './DownloadDocumentAction.vue'
export { default as ExportDocumentAction } from './export/ExportDocumentAction.vue'
export { default as BatchExportDocumentAction } from './export/BatchExportDocumentAction.vue'
export { default as MigrateDocumentAction } from './MigrateDocumentAction.vue'
export { default as MigrateDocumentAction } from './migrate/MigrateDocumentAction.vue'
export { default as ReplaceDocumentAction } from './ReplaceDocumentAction.vue'
export { default as SettingDocumentAction } from './SettingDocumentAction.vue'
export { default as SettingDocumentAction } from './setting/SettingDocumentAction.vue'
export { default as BatchSettingDocumentAction } from './setting/BatchSettingDocumentAction.vue'
export { default as SyncDocumentAction } from './sync/SyncDocumentAction.vue'
export { default as BatchSyncDocumentAction } from './sync/BatchSyncDocumentAction.vue'
export { default as TokenizeDocumentAction } from './tokenize/TokenizeDocumentAction.vue'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<script setup lang="ts">
import { computed, reactive, ref, useTemplateRef } from 'vue'
import type { FormInstance, FormRules } from 'element-plus'
import type { KnowledgeItem } from '@/api/types'
import KnowledgeApi from '@/api/admin/workspace/knowledge/knowledge'
import type DocumentApi from '@/api/admin/workspace/knowledge/document'
import { MsgSuccess } from '@/utils/message'

defineOptions({ name: 'DocumentMigrateDialog' })
const props = defineProps<{ api: typeof DocumentApi; knowledgeId: string }>()
const emit = defineEmits<{ refresh: []; closed: [] }>()
const visible = ref(false)
const loading = defineModel<boolean>('loading', { default: false })
const optionLoading = ref(false)
const knowledgeOptions = ref<KnowledgeItem[]>([])
const knowledgeById = computed(() => new Map(knowledgeOptions.value.map((knowledge) => [knowledge.id, knowledge])))
const migrateFormRef = useTemplateRef<FormInstance>('migrateFormRef')
const migrateForm = reactive({ targetKnowledgeId: '' })
const migrateRules: FormRules<typeof migrateForm> = {
targetKnowledgeId: [{ required: true, message: '请选择知识库', trigger: 'change' }],
}
const targetDocumentIds = ref<string[]>([])

// 打开迁移弹窗,固定本次文档并查询目标知识库。
function open(documentIds: string[]) {
if (loading.value || optionLoading.value || !documentIds.length) return
targetDocumentIds.value = [...documentIds]
visible.value = true
optionLoading.value = true
KnowledgeApi.getAllKnowledge()
.then((knowledge) => {
knowledgeOptions.value = knowledge.filter(({ id }) => id !== props.knowledgeId)
})
.catch(() => {
// 请求层统一提示错误,保留当前输入。
})
.finally(() => {
optionLoading.value = false
})
}

// 迁移成功后关闭并通知页面刷新,失败保留选择。
async function handleSubmit() {
if (loading.value || optionLoading.value || !(await migrateFormRef.value?.validate().catch(() => false))) return
loading.value = true
return props.api
.putMigrateDocuments(props.knowledgeId, migrateForm.targetKnowledgeId, targetDocumentIds.value)
.then(() => {
MsgSuccess('迁移成功')
visible.value = false
emit('refresh')
})
.catch(() => {
// 请求层统一提示错误,保留当前输入。
})
.finally(() => {
loading.value = false
})
}

defineExpose({ open })
</script>

<template>
<MkDialog v-model="visible" title="文档迁移到" @closed="emit('closed')">
<el-form ref="migrateFormRef" :model="migrateForm" :rules="migrateRules" label-position="top" require-asterisk-position="right" @submit.prevent>
<el-form-item label="选择知识库" prop="targetKnowledgeId">
<el-select v-model="migrateForm.targetKnowledgeId" fit-input-width filterable placeholder="请选择知识库" :loading="optionLoading">
<template v-for="target in knowledgeOptions" :key="target.id">
<el-option :label="target.name" :value="target.id">
<div class="flex-align-center min-w-0 gap-2">
<KnowledgeIcon :type="target.type" :size="20" class="shrink-0" />
<span class="truncate" :title="target.name">{{ target.name }}</span>
</div>
</el-option>
</template>
<template #label="{ label, value }">
<span class="flex-align-center gap-2">
<KnowledgeIcon :type="knowledgeById.get(value)?.type" :size="20" class="shrink-0" />
<span class="truncate" :title="label">{{ label }}</span>
</span>
</template>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<!-- 取消文档迁移 -->
<el-button plain :disabled="loading || optionLoading" @click="visible = false">取消</el-button>
<!-- 确认迁移文档 -->
<el-button type="primary" :loading="loading" :disabled="optionLoading" @click="handleSubmit">确认</el-button>
</template>
</MkDialog>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<script setup lang="ts">
import { nextTick, ref, useTemplateRef } from 'vue'
import type DocumentApi from '@/api/admin/workspace/knowledge/document'
import DocumentMigrateDialog from './DocumentMigrateDialog.vue'

defineOptions({ name: 'MigrateDocumentAction' })
const props = defineProps<{ api: typeof DocumentApi; knowledgeId: string; documentIds: string[]; label: string; icon?: string; disabled?: boolean }>()
const emit = defineEmits<{ refresh: [] }>()
const loading = ref(false)

// 迁移弹窗按需挂载,关闭动画结束后卸载。
const dialogMounted = ref(false)
const migrateDialogRef = useTemplateRef<InstanceType<typeof DocumentMigrateDialog>>('migrateDialogRef')

function handleOpenDialog() {
const documentIds = [...props.documentIds]
dialogMounted.value = true
return nextTick(() => migrateDialogRef.value?.open(documentIds))
}

function handleDialogClosed() {
dialogMounted.value = false
}
</script>

<template>
<!-- 文档迁移入口 -->
<MkAction :label="label" :icon="icon" :disabled="disabled || loading" @click="handleOpenDialog" />
<DocumentMigrateDialog
v-if="dialogMounted"
ref="migrateDialogRef"
v-model:loading="loading"
:api="api"
:knowledge-id="knowledgeId"
@refresh="emit('refresh')"
@closed="handleDialogClosed"
/>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<script setup lang="ts">
import { nextTick, ref, useTemplateRef } from 'vue'
import type { DocumentSettingPayload } from '@/api/types'
import type DocumentApi from '@/api/admin/workspace/knowledge/document'
import { MsgSuccess } from '@/utils/message'
import DocumentSettingDialog from './DocumentSettingDialog.vue'

defineOptions({ name: 'BatchSettingDocumentAction' })
const props = defineProps<{ api: typeof DocumentApi; knowledgeId: string; documentIds: string[]; label: string; disabled?: boolean }>()
const loading = defineModel<boolean>('loading', { default: false })
const emit = defineEmits<{ refresh: [] }>()

// 打开设置时固定操作对象,弹窗关闭后卸载。
const dialogMounted = ref(false)
const settingDialogRef = useTemplateRef<InstanceType<typeof DocumentSettingDialog>>('settingDialogRef')
const targetKnowledgeId = ref('')
const targetDocumentIds = ref<string[]>([])

function handleOpenDialog() {
if (props.disabled || loading.value || dialogMounted.value || !props.documentIds.length) return
targetKnowledgeId.value = props.knowledgeId
targetDocumentIds.value = [...props.documentIds]
dialogMounted.value = true
return nextTick(() => settingDialogRef.value?.open())
}

// 设置成功后关闭弹窗并刷新列表,失败保留表单。
function handleSubmit(data: DocumentSettingPayload) {
if (loading.value) return
loading.value = true
return props.api
.putBatchDocumentSetting(targetKnowledgeId.value, targetDocumentIds.value, data)
.then(() => {
MsgSuccess('设置成功')
settingDialogRef.value?.close()
emit('refresh')
})
.catch(() => {
// 请求层统一提示错误,保留当前输入。
})
.finally(() => {
loading.value = false
})
}

function handleDialogClosed() {
dialogMounted.value = false
}
</script>

<template>
<!-- 批量文档设置 -->
<el-button plain :disabled="disabled || loading" @click="handleOpenDialog">{{ label }}</el-button>
<DocumentSettingDialog v-if="dialogMounted" ref="settingDialogRef" batch :loading="loading" @submit="handleSubmit" @closed="handleDialogClosed" />
</template>
Loading
Loading