mirror of
https://gitee.com/myxzgzs/boyue-ui-admin-vue3
synced 2025-08-08 16:32:43 +08:00
【功能新增】AI:知识库文档上传:60%,SplitStep 完成度更高
This commit is contained in:
parent
da91896419
commit
de41b6cdba
@ -24,11 +24,16 @@ export const KnowledgeDocumentApi = {
|
|||||||
return await request.get({ url: `/ai/knowledge/document/get?id=` + id })
|
return await request.get({ url: `/ai/knowledge/document/get?id=` + id })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 新增知识库文档
|
// 新增知识库文档(单个)
|
||||||
createKnowledgeDocument: async (data: KnowledgeDocumentVO) => {
|
createKnowledgeDocument: async (data: KnowledgeDocumentVO) => {
|
||||||
return await request.post({ url: `/ai/knowledge/document/create`, data })
|
return await request.post({ url: `/ai/knowledge/document/create`, data })
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 新增知识库文档(批量)
|
||||||
|
createKnowledgeDocumentList: async (data: any) => {
|
||||||
|
return await request.post({ url: `/ai/knowledge/document/create-list`, data })
|
||||||
|
},
|
||||||
|
|
||||||
// // 修改AI 知识库文档
|
// // 修改AI 知识库文档
|
||||||
// updateKnowledgeDocument: async (data: KnowledgeDocumentVO) => {
|
// updateKnowledgeDocument: async (data: KnowledgeDocumentVO) => {
|
||||||
// return await request.put({ url: `/ai/knowledge/document/update`, data })
|
// return await request.put({ url: `/ai/knowledge/document/update`, data })
|
||||||
|
@ -633,7 +633,7 @@ const remainingRouter: AppRouteRecordRaw[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'console/knowledge/document/create',
|
path: 'console/knowledge/document/create',
|
||||||
component: () => import('@/views/ai/knowledge/document/create/index.vue'),
|
component: () => import('@/views/ai/knowledge/document/form/index.vue'),
|
||||||
name: 'AiKnowledgeDocumentCreate',
|
name: 'AiKnowledgeDocumentCreate',
|
||||||
meta: {
|
meta: {
|
||||||
title: '创建文档',
|
title: '创建文档',
|
||||||
@ -642,6 +642,18 @@ const remainingRouter: AppRouteRecordRaw[] = [
|
|||||||
hidden: true,
|
hidden: true,
|
||||||
activeMenu: '/ai/console/knowledge/document'
|
activeMenu: '/ai/console/knowledge/document'
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'console/knowledge/document/update',
|
||||||
|
component: () => import('@/views/ai/knowledge/document/form/index.vue'),
|
||||||
|
name: 'AiKnowledgeDocumentUpdate',
|
||||||
|
meta: {
|
||||||
|
title: '修改文档',
|
||||||
|
icon: 'ep:edit',
|
||||||
|
noCache: true,
|
||||||
|
hidden: true,
|
||||||
|
activeMenu: '/ai/console/knowledge/document'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -78,17 +78,24 @@
|
|||||||
|
|
||||||
<!-- 添加底部按钮 -->
|
<!-- 添加底部按钮 -->
|
||||||
<div class="mt-20px flex justify-between">
|
<div class="mt-20px flex justify-between">
|
||||||
<el-button @click="handlePrevStep">上一步</el-button>
|
<div>
|
||||||
<el-button type="primary" @click="handleNextStep">保存并处理</el-button>
|
<el-button v-if="!modelData.id" @click="handlePrevStep">上一步</el-button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<el-button type="primary" :loading="submitLoading" @click="handleSave">
|
||||||
|
保存并处理
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { PropType, ref, computed, inject, onMounted, getCurrentInstance } from 'vue'
|
import { computed, getCurrentInstance, inject, onMounted, PropType, ref } from 'vue'
|
||||||
import { Icon } from '@/components/Icon'
|
import { Icon } from '@/components/Icon'
|
||||||
import { KnowledgeSegmentApi } from '@/api/ai/knowledge/segment'
|
import { KnowledgeSegmentApi } from '@/api/ai/knowledge/segment'
|
||||||
import { useMessage } from '@/hooks/web/useMessage'
|
import { useMessage } from '@/hooks/web/useMessage'
|
||||||
|
import { KnowledgeDocumentApi } from '@/api/ai/knowledge/document'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: {
|
modelValue: {
|
||||||
@ -108,6 +115,7 @@ const modelData = computed({
|
|||||||
|
|
||||||
const splitLoading = ref(false) // 分段加载状态
|
const splitLoading = ref(false) // 分段加载状态
|
||||||
const currentFile = ref<any>(null) // 当前选中的文件
|
const currentFile = ref<any>(null) // 当前选中的文件
|
||||||
|
const submitLoading = ref(false) // 提交按钮加载状态
|
||||||
|
|
||||||
/** 选择文件 */
|
/** 选择文件 */
|
||||||
const selectFile = async (index: number) => {
|
const selectFile = async (index: number) => {
|
||||||
@ -124,8 +132,11 @@ const splitContent = async (file: any) => {
|
|||||||
|
|
||||||
splitLoading.value = true
|
splitLoading.value = true
|
||||||
try {
|
try {
|
||||||
const data = await KnowledgeSegmentApi.splitContent(file.url, modelData.value.segmentMaxTokens) // 调用后端分段接口,获取文档的分段内容、字符数和 Token 数
|
// 调用后端分段接口,获取文档的分段内容、字符数和 Token 数
|
||||||
file.segments = data
|
file.segments = await KnowledgeSegmentApi.splitContent(
|
||||||
|
file.url,
|
||||||
|
modelData.value.segmentMaxTokens
|
||||||
|
)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取分段内容失败:', file, error)
|
console.error('获取分段内容失败:', file, error)
|
||||||
message.error('获取分段内容失败')
|
message.error('获取分段内容失败')
|
||||||
@ -158,22 +169,46 @@ const handlePrevStep = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 下一步按钮处理 */
|
/** 保存操作 */
|
||||||
const handleNextStep = () => {
|
const handleSave = async () => {
|
||||||
const parentEl = parent || getCurrentInstance()?.parent
|
// 保存前验证
|
||||||
if (parentEl && typeof parentEl.exposed?.goToNextStep === 'function') {
|
if (!currentFile?.value?.segments || currentFile.value.segments.length === 0) {
|
||||||
parentEl.exposed.goToNextStep()
|
message.warning('请先预览分段内容')
|
||||||
}
|
return
|
||||||
}
|
|
||||||
|
|
||||||
/** 组件激活时自动调用分段接口 TODO 芋艿:需要看下 */
|
|
||||||
const activated = async () => {
|
|
||||||
if (!currentFile.value && modelData.value.list && modelData.value.list.length > 0) {
|
|
||||||
currentFile.value = modelData.value.list[0] // 如果没有选中文件,默认选中第一个
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentFile.value) {
|
// 设置按钮加载状态
|
||||||
await splitContent(currentFile.value) // 如果有选中的文件,获取分段内容
|
submitLoading.value = true
|
||||||
|
try {
|
||||||
|
if (modelData.value.id) {
|
||||||
|
// 修改场景
|
||||||
|
modelData.value.ids = await KnowledgeDocumentApi.createKnowledgeDocumentList({
|
||||||
|
id: modelData.value.id,
|
||||||
|
segmentMaxTokens: modelData.value.segmentMaxTokens
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// 新增场景
|
||||||
|
modelData.value.ids = await KnowledgeDocumentApi.createKnowledgeDocumentList({
|
||||||
|
knowledgeId: modelData.value.knowledgeId,
|
||||||
|
segmentMaxTokens: modelData.value.segmentMaxTokens,
|
||||||
|
list: modelData.value.list.map((item: any) => ({
|
||||||
|
name: item.name,
|
||||||
|
url: item.url
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 进入下一步
|
||||||
|
const parentEl = parent || getCurrentInstance()?.parent
|
||||||
|
if (parentEl && typeof parentEl.exposed?.goToNextStep === 'function') {
|
||||||
|
parentEl.exposed.goToNextStep()
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('保存失败:', modelData.value, error)
|
||||||
|
message.error(error.message)
|
||||||
|
} finally {
|
||||||
|
// 关闭按钮加载状态
|
||||||
|
submitLoading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -8,8 +8,8 @@
|
|||||||
<!-- 左侧标题 -->
|
<!-- 左侧标题 -->
|
||||||
<div class="w-200px flex items-center overflow-hidden">
|
<div class="w-200px flex items-center overflow-hidden">
|
||||||
<Icon icon="ep:arrow-left" class="cursor-pointer flex-shrink-0" @click="handleBack" />
|
<Icon icon="ep:arrow-left" class="cursor-pointer flex-shrink-0" @click="handleBack" />
|
||||||
<span class="ml-10px text-16px truncate" :title="formData.name || '创建知识库文档'">
|
<span class="ml-10px text-16px truncate">
|
||||||
{{ formData.name || '创建知识库文档' }}
|
{{ formData.id ? '编辑知识库文档' : '创建知识库文档' }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -68,29 +68,25 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { useMessage } from '@/hooks/web/useMessage'
|
|
||||||
import { useTagsViewStore } from '@/store/modules/tagsView'
|
import { useTagsViewStore } from '@/store/modules/tagsView'
|
||||||
import UploadStep from './UploadStep.vue'
|
import UploadStep from './UploadStep.vue'
|
||||||
import SplitStep from './SplitStep.vue'
|
import SplitStep from './SplitStep.vue'
|
||||||
import ProcessStep from './ProcessStep.vue'
|
import ProcessStep from './ProcessStep.vue'
|
||||||
|
import { KnowledgeDocumentApi } from '@/api/ai/knowledge/document'
|
||||||
|
|
||||||
const router = useRouter()
|
|
||||||
const { delView } = useTagsViewStore() // 视图操作
|
const { delView } = useTagsViewStore() // 视图操作
|
||||||
const route = useRoute()
|
const route = useRoute() // 路由
|
||||||
const message = useMessage()
|
const router = useRouter() // 路由
|
||||||
|
|
||||||
// 组件引用
|
// 组件引用
|
||||||
const uploadDocumentRef = ref()
|
const uploadDocumentRef = ref()
|
||||||
const documentSegmentRef = ref()
|
const documentSegmentRef = ref()
|
||||||
const processCompleteRef = ref()
|
const processCompleteRef = ref()
|
||||||
|
|
||||||
const currentStep = ref(0) // 步骤控制
|
const currentStep = ref(0) // 步骤控制
|
||||||
const steps = [{ title: '上传文档' }, { title: '文档分段' }, { title: '处理并完成' }]
|
const steps = [{ title: '上传文档' }, { title: '文档分段' }, { title: '处理并完成' }]
|
||||||
|
|
||||||
// 表单数据
|
|
||||||
const formData = ref({
|
const formData = ref({
|
||||||
knowlegeId: undefined, // 知识库编号
|
knowledgeId: undefined, // 知识库编号
|
||||||
id: undefined, // 文档编号(documentId)
|
id: undefined, // 编辑的文档编号(documentId)
|
||||||
segmentMaxTokens: 500, // 分段最大 token 数
|
segmentMaxTokens: 500, // 分段最大 token 数
|
||||||
list: [] as Array<{
|
list: [] as Array<{
|
||||||
name: string
|
name: string
|
||||||
@ -101,17 +97,35 @@ const formData = ref({
|
|||||||
tokens?: number
|
tokens?: number
|
||||||
}>
|
}>
|
||||||
}>, // 用于存储上传的文件列表
|
}>, // 用于存储上传的文件列表
|
||||||
|
documentIds: [], // 最终提交的创建/修改的文档编号,用于 ProcessStep 组件的轮询
|
||||||
status: 0 // 0: 草稿, 1: 处理中, 2: 已完成
|
status: 0 // 0: 草稿, 1: 处理中, 2: 已完成
|
||||||
})
|
}) // 表单数据
|
||||||
|
|
||||||
|
provide('parent', getCurrentInstance()) // 提供 parent 给子组件使用
|
||||||
|
|
||||||
/** 初始化数据 */
|
/** 初始化数据 */
|
||||||
const initData = async () => {
|
const initData = async () => {
|
||||||
// TODO @芋艿:knowlegeId 解析
|
// 【新增场景】从路由参数中获取知识库 ID
|
||||||
const documentId = route.params.id as string
|
if (route.query.knowledgeId) {
|
||||||
|
formData.value.knowledgeId = route.query.knowledgeId as any
|
||||||
|
}
|
||||||
|
|
||||||
|
// 【修改场景】从路由参数中获取文档 ID
|
||||||
|
const documentId = route.query.id
|
||||||
if (documentId) {
|
if (documentId) {
|
||||||
// 修改场景
|
// 获取文档信息
|
||||||
// 这里需要调用API获取文档数据
|
formData.value.id = documentId as any
|
||||||
// formData.value = await DocumentApi.getDocument(documentId)
|
const document = await KnowledgeDocumentApi.getKnowledgeDocument(documentId as any)
|
||||||
|
formData.value.segmentMaxTokens = document.segmentMaxTokens
|
||||||
|
formData.value.list = [
|
||||||
|
{
|
||||||
|
name: document.name,
|
||||||
|
url: document.url,
|
||||||
|
segments: []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
// 进入下一步
|
||||||
|
goToNextStep()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO @芋艿:为了开发方便,强制设置
|
// TODO @芋艿:为了开发方便,强制设置
|
||||||
@ -141,52 +155,12 @@ const goToPrevStep = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 保存操作 */
|
|
||||||
const handleSave = async () => {
|
|
||||||
try {
|
|
||||||
// 更新表单数据
|
|
||||||
const documentData = {
|
|
||||||
...formData.value
|
|
||||||
}
|
|
||||||
|
|
||||||
if (formData.value.id) {
|
|
||||||
// 修改场景
|
|
||||||
// await DocumentApi.updateDocument(documentData)
|
|
||||||
message.success('修改成功')
|
|
||||||
} else {
|
|
||||||
// 新增场景
|
|
||||||
// formData.value.id = await DocumentApi.createDocument(documentData)
|
|
||||||
message.success('新增成功')
|
|
||||||
try {
|
|
||||||
await message.confirm('创建文档成功,是否继续编辑?')
|
|
||||||
// 用户点击继续编辑,跳转到编辑页面
|
|
||||||
await nextTick()
|
|
||||||
// 先删除当前页签
|
|
||||||
delView(unref(router.currentRoute))
|
|
||||||
// 跳转到编辑页面
|
|
||||||
await router.push({
|
|
||||||
name: 'AiKnowledgeDocumentUpdate',
|
|
||||||
params: { id: formData.value.id }
|
|
||||||
})
|
|
||||||
} catch {
|
|
||||||
// 先删除当前页签
|
|
||||||
delView(unref(router.currentRoute))
|
|
||||||
// 用户点击返回列表
|
|
||||||
await router.push({ name: 'AiKnowledgeDocument' })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error: any) {
|
|
||||||
console.error('保存失败:', error)
|
|
||||||
message.warning(error.message || '请完善所有步骤的必填信息')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 返回列表页 */
|
/** 返回列表页 */
|
||||||
const handleBack = () => {
|
const handleBack = () => {
|
||||||
// 先删除当前页签
|
// 先删除当前页签
|
||||||
delView(unref(router.currentRoute))
|
delView(unref(router.currentRoute))
|
||||||
// 跳转到列表页
|
// 跳转到列表页
|
||||||
router.push({ name: 'AiKnowledgeDocument' })
|
router.push({ name: 'AiKnowledgeDocument', query: { knowledgeId: formData.value.knowledgeId } })
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 初始化 */
|
/** 初始化 */
|
||||||
@ -194,10 +168,7 @@ onMounted(async () => {
|
|||||||
await initData()
|
await initData()
|
||||||
})
|
})
|
||||||
|
|
||||||
// 提供parent给子组件使用
|
/** 添加组件卸载前的清理代码 */
|
||||||
provide('parent', getCurrentInstance())
|
|
||||||
|
|
||||||
// 添加组件卸载前的清理代码
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
// 清理所有的引用
|
// 清理所有的引用
|
||||||
uploadDocumentRef.value = null
|
uploadDocumentRef.value = null
|
||||||
@ -205,11 +176,10 @@ onBeforeUnmount(() => {
|
|||||||
processCompleteRef.value = null
|
processCompleteRef.value = null
|
||||||
})
|
})
|
||||||
|
|
||||||
// 暴露方法给子组件使用
|
/** 暴露方法给子组件使用 */
|
||||||
defineExpose({
|
defineExpose({
|
||||||
goToNextStep,
|
goToNextStep,
|
||||||
goToPrevStep,
|
goToPrevStep
|
||||||
handleSave
|
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -68,7 +68,7 @@
|
|||||||
<el-button
|
<el-button
|
||||||
link
|
link
|
||||||
type="primary"
|
type="primary"
|
||||||
@click="openForm('update', scope.row.id)"
|
@click="handleUpdate(scope.row.id)"
|
||||||
v-hasPermi="['ai:knowledge:update']"
|
v-hasPermi="['ai:knowledge:update']"
|
||||||
>
|
>
|
||||||
编辑
|
编辑
|
||||||
@ -148,15 +148,20 @@ const resetQuery = () => {
|
|||||||
handleQuery()
|
handleQuery()
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 添加/修改操作 */
|
|
||||||
const formRef = ref()
|
|
||||||
const openForm = (type: string, id?: number) => {
|
|
||||||
formRef.value.open(type, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 跳转到创建文档页面 */
|
/** 跳转到创建文档页面 */
|
||||||
const handleCreate = () => {
|
const handleCreate = () => {
|
||||||
router.push({ name: 'AiKnowledgeDocumentCreate' })
|
router.push({
|
||||||
|
name: 'AiKnowledgeDocumentCreate',
|
||||||
|
query: { knowledgeId: queryParams.knowledgeId }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 跳转到更新文档页面 */
|
||||||
|
const handleUpdate = (id: number) => {
|
||||||
|
router.push({
|
||||||
|
name: 'AiKnowledgeDocumentUpdate',
|
||||||
|
query: { id, knowledgeId: queryParams.knowledgeId }
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 删除按钮操作 */
|
/** 删除按钮操作 */
|
||||||
@ -174,10 +179,16 @@ const handleDelete = async (id: number) => {
|
|||||||
|
|
||||||
/** 初始化 **/
|
/** 初始化 **/
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// 从路由参数中获取知识库 ID
|
// 如果知识库 ID 不存在,显示错误提示并关闭页面
|
||||||
if (route.query.knowledgeId) {
|
if (!route.query.knowledgeId) {
|
||||||
queryParams.knowledgeId = route.query.knowledgeId as any
|
message.error('知识库 ID 不存在,无法查看文档列表')
|
||||||
|
// 关闭当前路由,返回到知识库列表页面
|
||||||
|
router.push({ name: 'AiKnowledge' })
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 从路由参数中获取知识库 ID
|
||||||
|
queryParams.knowledgeId = route.query.knowledgeId as any
|
||||||
getList()
|
getList()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user