Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
phsl
/
admin
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
b7d7b11d
authored
Feb 28, 2025
by
YunaiV
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
【功能新增】AI:知识库文档上传:10% 搭建整体页面结构
parent
94091598
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
929 additions
and
8 deletions
+929
-8
src/router/modules/remaining.ts
+12
-0
src/views/ai/knowledge/document/create/ProcessStep.vue
+235
-0
src/views/ai/knowledge/document/create/SplitStep.vue
+234
-0
src/views/ai/knowledge/document/create/UploadStep.vue
+225
-0
src/views/ai/knowledge/document/create/index.vue
+214
-0
src/views/ai/knowledge/document/index.vue
+8
-7
src/views/bpm/model/form/index.vue
+1
-1
No files found.
src/router/modules/remaining.ts
View file @
b7d7b11d
...
@@ -630,6 +630,18 @@ const remainingRouter: AppRouteRecordRaw[] = [
...
@@ -630,6 +630,18 @@ const remainingRouter: AppRouteRecordRaw[] = [
icon
:
'ep:document'
,
icon
:
'ep:document'
,
noCache
:
false
noCache
:
false
}
}
},
{
path
:
'console/knowledge/document/create'
,
component
:
()
=>
import
(
'@/views/ai/knowledge/document/create/index.vue'
),
name
:
'AiKnowledgeDocumentCreate'
,
meta
:
{
title
:
'创建文档'
,
icon
:
'ep:plus'
,
noCache
:
true
,
hidden
:
true
,
activeMenu
:
'/ai/console/knowledge/document'
}
}
}
]
]
},
},
...
...
src/views/ai/knowledge/document/create/ProcessStep.vue
0 → 100644
View file @
b7d7b11d
<
template
>
<div
class=
"process-complete"
>
<div
class=
"mb-20px"
>
<el-alert
title=
"处理说明"
type=
"info"
description=
"系统将对文档进行处理,包括文本提取、向量化等操作,处理完成后文档将被添加到知识库中。"
show-icon
:closable=
"false"
/>
</div>
<div
class=
"mb-20px"
>
<el-card
class=
"box-card"
>
<template
#
header
>
<div
class=
"card-header"
>
<span
class=
"text-16px font-bold"
>
文档信息
</span>
</div>
</
template
>
<div
class=
"document-info"
>
<div
class=
"info-item"
>
<span
class=
"label"
>
文档名称:
</span>
<span
class=
"value"
>
{{ modelData.name }}
</span>
</div>
<div
class=
"info-item"
>
<span
class=
"label"
>
知识库:
</span>
<span
class=
"value"
>
{{ getKnowledgeBaseName(modelData.knowledgeBaseId) }}
</span>
</div>
<div
class=
"info-item"
>
<span
class=
"label"
>
文档类型:
</span>
<span
class=
"value"
>
{{ getDocumentTypeName(modelData.documentType) }}
</span>
</div>
<div
class=
"info-item"
>
<span
class=
"label"
>
段落数量:
</span>
<span
class=
"value"
>
{{ modelData.segments.length }}
</span>
</div>
</div>
</el-card>
</div>
<div
class=
"mb-20px"
>
<el-card
class=
"box-card"
>
<
template
#
header
>
<div
class=
"card-header"
>
<span
class=
"text-16px font-bold"
>
处理选项
</span>
</div>
</
template
>
<div
class=
"process-options"
>
<el-form
:model=
"processOptions"
label-width=
"120px"
>
<el-form-item
label=
"处理模式"
>
<el-radio-group
v-model=
"processOptions.mode"
>
<el-radio
:label=
"1"
>
标准处理
</el-radio>
<el-radio
:label=
"2"
>
高级处理
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
label=
"向量模型"
v-if=
"processOptions.mode === 2"
>
<el-select
v-model=
"processOptions.vectorModel"
placeholder=
"请选择向量模型"
>
<el-option
label=
"文本嵌入模型-基础版"
value=
"text-embedding-basic"
/>
<el-option
label=
"文本嵌入模型-高级版"
value=
"text-embedding-advanced"
/>
<el-option
label=
"多模态嵌入模型"
value=
"multimodal-embedding"
/>
</el-select>
</el-form-item>
<el-form-item
label=
"处理优先级"
v-if=
"processOptions.mode === 2"
>
<el-select
v-model=
"processOptions.priority"
placeholder=
"请选择处理优先级"
>
<el-option
label=
"低"
value=
"low"
/>
<el-option
label=
"中"
value=
"medium"
/>
<el-option
label=
"高"
value=
"high"
/>
</el-select>
</el-form-item>
</el-form>
</div>
</el-card>
</div>
<div
class=
"mb-20px"
>
<el-card
class=
"box-card"
>
<
template
#
header
>
<div
class=
"card-header"
>
<span
class=
"text-16px font-bold"
>
处理状态
</span>
</div>
</
template
>
<div
class=
"process-status"
>
<div
v-if=
"!isProcessing && !isProcessed"
>
<el-empty
description=
"尚未开始处理"
/>
<div
class=
"flex justify-center mt-20px"
>
<el-button
type=
"primary"
@
click=
"handleStartProcess"
>
开始处理
</el-button>
</div>
</div>
<div
v-else-if=
"isProcessing"
>
<div
class=
"flex flex-col items-center"
>
<el-progress
type=
"circle"
:percentage=
"processPercentage"
/>
<div
class=
"mt-10px"
>
{{ processStatus }}
</div>
</div>
</div>
<div
v-else
>
<div
class=
"flex items-center justify-center"
>
<el-result
icon=
"success"
title=
"处理完成"
sub-title=
"文档已成功处理并添加到知识库中"
>
<
template
#
extra
>
<el-button
type=
"primary"
@
click=
"handleViewDocument"
>
查看文档
</el-button>
</
template
>
</el-result>
</div>
</div>
</div>
</el-card>
</div>
</div>
</template>
<
script
lang=
"ts"
setup
>
import
{
PropType
}
from
'vue'
const
props
=
defineProps
({
modelValue
:
{
type
:
Object
as
PropType
<
any
>
,
required
:
true
}
})
const
emit
=
defineEmits
([
'update:modelValue'
])
// 表单数据
const
modelData
=
computed
({
get
:
()
=>
props
.
modelValue
,
set
:
(
val
)
=>
emit
(
'update:modelValue'
,
val
)
})
// 处理选项
const
processOptions
=
ref
({
mode
:
1
,
// 1: 标准处理, 2: 高级处理
vectorModel
:
'text-embedding-basic'
,
priority
:
'medium'
})
// 处理状态
const
isProcessing
=
ref
(
false
)
const
isProcessed
=
ref
(
false
)
const
processPercentage
=
ref
(
0
)
const
processStatus
=
ref
(
'正在准备处理...'
)
// 知识库列表(模拟数据)
const
knowledgeBaseList
=
[
{
id
:
1
,
name
:
'产品知识库'
},
{
id
:
2
,
name
:
'技术文档库'
},
{
id
:
3
,
name
:
'客户服务知识库'
}
]
// 获取知识库名称
const
getKnowledgeBaseName
=
(
id
)
=>
{
const
base
=
knowledgeBaseList
.
find
((
item
)
=>
item
.
id
===
id
)
return
base
?
base
.
name
:
'未知知识库'
}
// 获取文档类型名称
const
getDocumentTypeName
=
(
type
)
=>
{
const
typeMap
=
{
pdf
:
'PDF文档'
,
word
:
'Word文档'
,
text
:
'文本文件'
,
url
:
'网页链接'
}
return
typeMap
[
type
]
||
'未知类型'
}
// 开始处理
const
handleStartProcess
=
()
=>
{
isProcessing
.
value
=
true
processPercentage
.
value
=
0
processStatus
.
value
=
'正在准备处理...'
// 模拟处理过程
const
timer
=
setInterval
(()
=>
{
processPercentage
.
value
+=
10
if
(
processPercentage
.
value
<
30
)
{
processStatus
.
value
=
'正在提取文本内容...'
}
else
if
(
processPercentage
.
value
<
60
)
{
processStatus
.
value
=
'正在进行向量化处理...'
}
else
if
(
processPercentage
.
value
<
90
)
{
processStatus
.
value
=
'正在写入知识库...'
}
else
{
processStatus
.
value
=
'处理完成,正在整理结果...'
}
if
(
processPercentage
.
value
>=
100
)
{
clearInterval
(
timer
)
isProcessing
.
value
=
false
isProcessed
.
value
=
true
modelData
.
value
.
status
=
2
// 已完成
}
},
500
)
}
// 查看文档
const
handleViewDocument
=
()
=>
{
// 跳转到文档详情页
console
.
log
(
'查看文档:'
,
modelData
.
value
.
id
)
}
// 表单校验
const
validate
=
()
=>
{
return
new
Promise
((
resolve
,
reject
)
=>
{
if
(
modelData
.
value
.
status
===
2
)
{
resolve
(
true
)
}
else
{
reject
(
new
Error
(
'请先完成文档处理'
))
}
})
}
// 对外暴露方法
defineExpose
({
validate
})
</
script
>
<
style
lang=
"scss"
scoped
>
.process-complete
{
.document-info
{
.info-item
{
margin-bottom
:
10px
;
display
:
flex
;
.label
{
width
:
100px
;
color
:
#606266
;
}
.value
{
font-weight
:
bold
;
}
}
}
}
</
style
>
src/views/ai/knowledge/document/create/SplitStep.vue
0 → 100644
View file @
b7d7b11d
<
template
>
<div
class=
"document-segment"
>
<div
class=
"mb-20px"
>
<el-alert
title=
"文档分段说明"
type=
"info"
description=
"系统会自动将文档内容分割成多个段落,您可以根据需要调整分段方式和内容。"
show-icon
:closable=
"false"
/>
</div>
<div
class=
"mb-20px flex justify-between items-center"
>
<div
class=
"text-16px font-bold"
>
分段设置
</div>
<div>
<el-button
type=
"primary"
@
click=
"handleAutoSegment"
>
自动分段
</el-button>
<el-button
@
click=
"handleAddSegment"
>
添加段落
</el-button>
</div>
</div>
<div
class=
"segment-settings mb-20px"
>
<el-form
:model=
"segmentSettings"
label-width=
"120px"
>
<el-form-item
label=
"分段方式"
>
<el-radio-group
v-model=
"segmentSettings.type"
>
<el-radio
:label=
"1"
>
按段落分割
</el-radio>
<el-radio
:label=
"2"
>
按字数分割
</el-radio>
<el-radio
:label=
"3"
>
按标题分割
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
label=
"最大字数"
v-if=
"segmentSettings.type === 2"
>
<el-input-number
v-model=
"segmentSettings.maxChars"
:min=
"100"
:max=
"5000"
/>
</el-form-item>
</el-form>
</div>
<div
class=
"segment-list"
>
<div
class=
"text-16px font-bold mb-10px"
>
段落列表 (
{{
modelData
.
segments
.
length
}}
)
</div>
<el-empty
v-if=
"modelData.segments.length === 0"
description=
"暂无段落数据"
/>
<div
v-else
>
<el-collapse
v-model=
"activeSegments"
>
<el-collapse-item
v-for=
"(segment, index) in modelData.segments"
:key=
"index"
:title=
"`段落 $
{index + 1}`"
:name="index"
>
<div
class=
"segment-content"
>
<el-input
v-model=
"segment.content"
type=
"textarea"
:rows=
"5"
placeholder=
"段落内容"
/>
<div
class=
"mt-10px flex justify-end"
>
<el-button
type=
"danger"
size=
"small"
@
click=
"handleDeleteSegment(index)"
>
删除段落
</el-button>
</div>
</div>
</el-collapse-item>
</el-collapse>
</div>
</div>
<!-- 添加底部按钮 -->
<div
class=
"mt-20px flex justify-between"
>
<el-button
@
click=
"handlePrevStep"
>
上一步
</el-button>
<el-button
type=
"primary"
@
click=
"handleNextStep"
>
保存并处理
</el-button>
</div>
</div>
</
template
>
<
script
lang=
"ts"
setup
>
import
{
PropType
}
from
'vue'
const
props
=
defineProps
({
modelValue
:
{
type
:
Object
as
PropType
<
any
>
,
required
:
true
}
})
const
emit
=
defineEmits
([
'update:modelValue'
])
// 获取父组件实例
const
parent
=
inject
(
'parent'
,
null
)
// 表单数据
const
modelData
=
computed
({
get
:
()
=>
props
.
modelValue
,
set
:
(
val
)
=>
emit
(
'update:modelValue'
,
val
)
})
// 分段设置
const
segmentSettings
=
ref
({
type
:
1
,
// 1: 按段落, 2: 按字数, 3: 按标题
maxChars
:
1000
})
// 当前展开的段落
const
activeSegments
=
ref
([
0
])
// 自动分段
const
handleAutoSegment
=
()
=>
{
// 根据文档类型和分段设置进行自动分段
// 这里只是模拟实现,实际需要根据文档内容进行分析
// 清空现有段落
modelData
.
value
.
segments
=
[]
// 模拟生成段落
if
(
modelData
.
value
.
documentType
===
'text'
&&
modelData
.
value
.
content
)
{
// 文本类型,直接按段落或字数分割
const
content
=
modelData
.
value
.
content
if
(
segmentSettings
.
value
.
type
===
1
)
{
// 按段落分割
const
paragraphs
=
content
.
split
(
/
\n\s
*
\n
/
)
paragraphs
.
forEach
((
paragraph
)
=>
{
if
(
paragraph
.
trim
())
{
modelData
.
value
.
segments
.
push
({
content
:
paragraph
.
trim
(),
order
:
modelData
.
value
.
segments
.
length
+
1
})
}
})
}
else
if
(
segmentSettings
.
value
.
type
===
2
)
{
// 按字数分割
const
maxChars
=
segmentSettings
.
value
.
maxChars
let
remaining
=
content
while
(
remaining
.
length
>
0
)
{
const
segment
=
remaining
.
substring
(
0
,
maxChars
)
remaining
=
remaining
.
substring
(
maxChars
)
modelData
.
value
.
segments
.
push
({
content
:
segment
,
order
:
modelData
.
value
.
segments
.
length
+
1
})
}
}
}
else
{
// 其他类型文档,模拟生成5个段落
for
(
let
i
=
0
;
i
<
5
;
i
++
)
{
modelData
.
value
.
segments
.
push
({
content
:
`这是第
${
i
+
1
}
个自动生成的段落,实际内容将根据文档解析结果填充。`
,
order
:
i
+
1
})
}
}
// 默认展开第一个段落
activeSegments
.
value
=
[
0
]
}
// 添加段落
const
handleAddSegment
=
()
=>
{
modelData
.
value
.
segments
.
push
({
content
:
''
,
order
:
modelData
.
value
.
segments
.
length
+
1
})
// 展开新添加的段落
activeSegments
.
value
=
[
modelData
.
value
.
segments
.
length
-
1
]
}
// 删除段落
const
handleDeleteSegment
=
(
index
)
=>
{
modelData
.
value
.
segments
.
splice
(
index
,
1
)
// 更新段落顺序
modelData
.
value
.
segments
.
forEach
((
segment
,
idx
)
=>
{
segment
.
order
=
idx
+
1
})
}
// 上一步按钮处理
const
handlePrevStep
=
()
=>
{
// 获取父组件的goToPrevStep方法
const
parentEl
=
parent
||
getCurrentInstance
()?.
parent
if
(
parentEl
&&
typeof
parentEl
.
exposed
?.
goToPrevStep
===
'function'
)
{
parentEl
.
exposed
.
goToPrevStep
()
}
}
// 下一步按钮处理
const
handleNextStep
=
()
=>
{
// 获取父组件的goToNextStep方法
const
parentEl
=
parent
||
getCurrentInstance
()?.
parent
if
(
parentEl
&&
typeof
parentEl
.
exposed
?.
goToNextStep
===
'function'
)
{
parentEl
.
exposed
.
goToNextStep
()
}
}
// 表单校验
const
validate
=
()
=>
{
return
new
Promise
((
resolve
,
reject
)
=>
{
if
(
modelData
.
value
.
segments
.
length
===
0
)
{
reject
(
new
Error
(
'请至少添加一个段落'
))
}
else
{
// 检查是否有空段落
const
emptySegment
=
modelData
.
value
.
segments
.
find
((
segment
)
=>
!
segment
.
content
.
trim
())
if
(
emptySegment
)
{
reject
(
new
Error
(
'存在空段落,请填写内容或删除'
))
}
else
{
resolve
(
true
)
}
}
})
}
// 对外暴露方法
defineExpose
({
validate
})
// 初始化
onMounted
(()
=>
{
// 如果已有段落数据,默认展开第一个
if
(
modelData
.
value
.
segments
&&
modelData
.
value
.
segments
.
length
>
0
)
{
activeSegments
.
value
=
[
0
]
}
})
</
script
>
<
style
lang=
"scss"
scoped
>
.document-segment
{
.segment-content
{
padding
:
10px
;
}
}
</
style
>
src/views/ai/knowledge/document/create/UploadStep.vue
0 → 100644
View file @
b7d7b11d
<
template
>
<el-form
ref=
"formRef"
:model=
"modelData"
:rules=
"rules"
label-width=
"120px"
class=
"mt-20px"
>
<el-form-item
label=
"文档名称"
prop=
"name"
class=
"mb-20px"
>
<el-input
v-model=
"modelData.name"
clearable
placeholder=
"请输入文档名称"
/>
</el-form-item>
<el-form-item
label=
"知识库"
prop=
"knowledgeBaseId"
class=
"mb-20px"
>
<el-select
class=
"!w-full"
v-model=
"modelData.knowledgeBaseId"
clearable
placeholder=
"请选择知识库"
>
<el-option
v-for=
"base in knowledgeBaseList"
:key=
"base.id"
:label=
"base.name"
:value=
"base.id"
/>
</el-select>
</el-form-item>
<el-form-item
label=
"文档类型"
prop=
"documentType"
class=
"mb-20px"
>
<el-select
class=
"!w-full"
v-model=
"modelData.documentType"
clearable
placeholder=
"请选择文档类型"
>
<el-option
label=
"PDF文档"
value=
"pdf"
/>
<el-option
label=
"Word文档"
value=
"word"
/>
<el-option
label=
"文本文件"
value=
"text"
/>
<el-option
label=
"网页链接"
value=
"url"
/>
</el-select>
</el-form-item>
<el-form-item
label=
"文档内容"
prop=
"content"
class=
"mb-20px"
v-if=
"modelData.documentType === 'text'"
>
<el-input
v-model=
"modelData.content"
type=
"textarea"
:rows=
"6"
placeholder=
"请输入文档内容"
/>
</el-form-item>
<el-form-item
label=
"网页链接"
prop=
"url"
class=
"mb-20px"
v-if=
"modelData.documentType === 'url'"
>
<el-input
v-model=
"modelData.url"
clearable
placeholder=
"请输入网页链接"
/>
</el-form-item>
<el-form-item
label=
"上传文件"
prop=
"file"
class=
"mb-20px"
v-if=
"['pdf', 'word'].includes(modelData.documentType)"
>
<el-upload
class=
"upload-demo"
drag
action=
"#"
:auto-upload=
"false"
:on-change=
"handleFileChange"
:limit=
"1"
>
<el-icon
class=
"el-icon--upload"
><upload-filled
/></el-icon>
<div
class=
"el-upload__text"
>
拖拽文件到此处,或
<em>
点击上传
</em>
</div>
<template
#
tip
>
<div
class=
"el-upload__tip"
>
{{
modelData
.
documentType
===
'pdf'
?
'PDF文件'
:
'Word文件(.docx, .doc)'
}}
</div>
</
template
>
</el-upload>
</el-form-item>
<!-- 添加下一步按钮 -->
<el-form-item>
<div
class=
"flex justify-end"
>
<el-button
type=
"primary"
@
click=
"handleNextStep"
>
下一步
</el-button>
</div>
</el-form-item>
</el-form>
</template>
<
script
lang=
"ts"
setup
>
import
{
PropType
}
from
'vue'
import
{
UploadFilled
}
from
'@element-plus/icons-vue'
const
props
=
defineProps
({
modelValue
:
{
type
:
Object
as
PropType
<
any
>
,
required
:
true
}
})
const
emit
=
defineEmits
([
'update:modelValue'
])
// 表单引用
const
formRef
=
ref
()
// 获取父组件实例
const
parent
=
inject
(
'parent'
,
null
)
// 表单数据
const
modelData
=
computed
({
get
:
()
=>
props
.
modelValue
,
set
:
(
val
)
=>
emit
(
'update:modelValue'
,
val
)
})
// 知识库列表
interface
KnowledgeBase
{
id
:
number
name
:
string
}
const
knowledgeBaseList
=
ref
<
KnowledgeBase
[]
>
([])
// 表单校验规则
const
rules
=
{
name
:
[{
required
:
true
,
message
:
'请输入文档名称'
,
trigger
:
'blur'
}],
knowledgeBaseId
:
[{
required
:
true
,
message
:
'请选择知识库'
,
trigger
:
'change'
}],
documentType
:
[{
required
:
true
,
message
:
'请选择文档类型'
,
trigger
:
'change'
}],
content
:
[
{
required
:
true
,
message
:
'请输入文档内容'
,
trigger
:
'blur'
,
validator
:
(
rule
,
value
,
callback
)
=>
{
if
(
modelData
.
value
.
documentType
===
'text'
&&
!
value
)
{
callback
(
new
Error
(
'请输入文档内容'
))
}
else
{
callback
()
}
}
}
],
url
:
[
{
required
:
true
,
message
:
'请输入网页链接'
,
trigger
:
'blur'
,
validator
:
(
rule
,
value
,
callback
)
=>
{
if
(
modelData
.
value
.
documentType
===
'url'
&&
!
value
)
{
callback
(
new
Error
(
'请输入网页链接'
))
}
else
{
callback
()
}
}
}
],
file
:
[
{
required
:
true
,
message
:
'请上传文件'
,
trigger
:
'change'
,
validator
:
(
rule
,
value
,
callback
)
=>
{
if
([
'pdf'
,
'word'
].
includes
(
modelData
.
value
.
documentType
)
&&
!
modelData
.
value
.
file
)
{
callback
(
new
Error
(
'请上传文件'
))
}
else
{
callback
()
}
}
}
]
}
// 文件上传处理
const
handleFileChange
=
(
file
)
=>
{
modelData
.
value
.
file
=
file
.
raw
}
// 下一步按钮处理
const
handleNextStep
=
()
=>
{
// 获取父组件的goToNextStep方法
const
parentEl
=
parent
||
getCurrentInstance
()?.
parent
if
(
parentEl
&&
typeof
parentEl
.
exposed
?.
goToNextStep
===
'function'
)
{
parentEl
.
exposed
.
goToNextStep
()
}
}
// 初始化数据
const
initData
=
async
()
=>
{
// 获取知识库列表
// knowledgeBaseList.value = await KnowledgeBaseApi.getKnowledgeBaseList()
// 模拟数据
knowledgeBaseList
.
value
=
[
{
id
:
1
,
name
:
'产品知识库'
},
{
id
:
2
,
name
:
'技术文档库'
},
{
id
:
3
,
name
:
'客户服务知识库'
}
]
}
// 表单校验
const
validate
=
()
=>
{
return
new
Promise
((
resolve
,
reject
)
=>
{
formRef
.
value
?.
validate
((
valid
)
=>
{
if
(
valid
)
{
resolve
(
true
)
}
else
{
reject
(
new
Error
(
'请完善表单信息'
))
}
})
})
}
// 对外暴露方法
defineExpose
({
validate
})
// 初始化
onMounted
(()
=>
{
initData
()
})
</
script
>
<
style
lang=
"scss"
scoped
>
.upload-demo
{
width
:
100%
;
}
</
style
>
src/views/ai/knowledge/document/create/index.vue
0 → 100644
View file @
b7d7b11d
<
template
>
<ContentWrap>
<div
class=
"mx-auto"
>
<!-- 头部导航栏 -->
<div
class=
"absolute top-0 left-0 right-0 h-50px bg-white border-bottom z-10 flex items-center px-20px"
>
<!-- 左侧标题 -->
<div
class=
"w-200px flex items-center overflow-hidden"
>
<Icon
icon=
"ep:arrow-left"
class=
"cursor-pointer flex-shrink-0"
@
click=
"handleBack"
/>
<span
class=
"ml-10px text-16px truncate"
:title=
"formData.name || '创建知识库文档'"
>
{{
formData
.
name
||
'创建知识库文档'
}}
</span>
</div>
<!-- 步骤条 -->
<div
class=
"flex-1 flex items-center justify-center h-full"
>
<div
class=
"w-400px flex items-center justify-between h-full"
>
<div
v-for=
"(step, index) in steps"
:key=
"index"
class=
"flex items-center mx-15px relative h-full"
:class=
"[
currentStep === index
? 'text-[#3473ff] border-[#3473ff] border-b-2 border-b-solid'
: 'text-gray-500'
]"
>
<div
class=
"w-28px h-28px rounded-full flex items-center justify-center mr-8px border-2 border-solid text-15px"
:class=
"[
currentStep === index
? 'bg-[#3473ff] text-white border-[#3473ff]'
: 'border-gray-300 bg-white text-gray-500'
]"
>
{{
index
+
1
}}
</div>
<span
class=
"text-16px font-bold whitespace-nowrap"
>
{{
step
.
title
}}
</span>
</div>
</div>
</div>
<!-- 右侧按钮 - 已移除 -->
<div
class=
"w-200px flex items-center justify-end gap-2"
>
</div>
</div>
<!-- 主体内容 -->
<div
class=
"mt-50px"
>
<!-- 第一步:上传文档 -->
<div
v-if=
"currentStep === 0"
class=
"mx-auto w-560px"
>
<UploadStep
v-model=
"formData"
ref=
"uploadDocumentRef"
/>
</div>
<!-- 第二步:文档分段 -->
<div
v-if=
"currentStep === 1"
class=
"mx-auto w-560px"
>
<SplitStep
v-model=
"formData"
ref=
"documentSegmentRef"
/>
</div>
<!-- 第三步:处理并完成 -->
<div
v-if=
"currentStep === 2"
class=
"mx-auto w-560px"
>
<ProcessStep
v-model=
"formData"
ref=
"processCompleteRef"
/>
</div>
</div>
</div>
</ContentWrap>
</
template
>
<
script
lang=
"ts"
setup
>
import
{
useRoute
,
useRouter
}
from
'vue-router'
import
{
useMessage
}
from
'@/hooks/web/useMessage'
import
{
useTagsViewStore
}
from
'@/store/modules/tagsView'
import
UploadStep
from
'./UploadStep.vue'
import
SplitStep
from
'./SplitStep.vue'
import
ProcessStep
from
'./ProcessStep.vue'
const
router
=
useRouter
()
const
{
delView
}
=
useTagsViewStore
()
// 视图操作
const
route
=
useRoute
()
const
message
=
useMessage
()
// 组件引用
const
uploadDocumentRef
=
ref
()
const
documentSegmentRef
=
ref
()
const
processCompleteRef
=
ref
()
const
currentStep
=
ref
(
0
)
// 步骤控制
const
steps
=
[{
title
:
'上传文档'
},
{
title
:
'文档分段'
},
{
title
:
'处理并完成'
}]
// 表单数据
const
formData
=
ref
({
id
:
undefined
,
name
:
''
,
knowledgeBaseId
:
undefined
,
documentType
:
undefined
,
content
:
''
,
file
:
null
,
segments
:
[],
status
:
0
// 0: 草稿, 1: 处理中, 2: 已完成
})
/** 初始化数据 */
const
initData
=
async
()
=>
{
const
documentId
=
route
.
params
.
id
as
string
if
(
documentId
)
{
// 修改场景
// 这里需要调用API获取文档数据
// formData.value = await DocumentApi.getDocument(documentId)
}
}
/** 切换到下一步 */
const
goToNextStep
=
()
=>
{
if
(
currentStep
.
value
<
steps
.
length
-
1
)
{
currentStep
.
value
++
}
}
/** 切换到上一步 */
const
goToPrevStep
=
()
=>
{
if
(
currentStep
.
value
>
0
)
{
currentStep
.
value
--
}
}
/** 保存操作 */
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
=
()
=>
{
// 先删除当前页签
delView
(
unref
(
router
.
currentRoute
))
// 跳转到列表页
router
.
push
({
name
:
'AiKnowledgeDocument'
})
}
/** 初始化 */
onMounted
(
async
()
=>
{
await
initData
()
})
// 提供parent给子组件使用
provide
(
'parent'
,
getCurrentInstance
())
// 添加组件卸载前的清理代码
onBeforeUnmount
(()
=>
{
// 清理所有的引用
uploadDocumentRef
.
value
=
null
documentSegmentRef
.
value
=
null
processCompleteRef
.
value
=
null
})
// 暴露方法给子组件使用
defineExpose
({
goToNextStep
,
goToPrevStep
,
handleSave
})
</
script
>
<
style
lang=
"scss"
scoped
>
.border-bottom
{
border-bottom
:
1px
solid
#dcdfe6
;
}
.text-primary
{
color
:
#3473ff
;
}
.bg-primary
{
background-color
:
#3473ff
;
}
.border-primary
{
border-color
:
#3473ff
;
}
</
style
>
src/views/ai/knowledge/document/index.vue
View file @
b7d7b11d
...
@@ -35,12 +35,7 @@
...
@@ -35,12 +35,7 @@
<el-form-item>
<el-form-item>
<el-button
@
click=
"handleQuery"
><Icon
icon=
"ep:search"
class=
"mr-5px"
/>
搜索
</el-button>
<el-button
@
click=
"handleQuery"
><Icon
icon=
"ep:search"
class=
"mr-5px"
/>
搜索
</el-button>
<el-button
@
click=
"resetQuery"
><Icon
icon=
"ep:refresh"
class=
"mr-5px"
/>
重置
</el-button>
<el-button
@
click=
"resetQuery"
><Icon
icon=
"ep:refresh"
class=
"mr-5px"
/>
重置
</el-button>
<el-button
<el-button
type=
"primary"
plain
@
click=
"handleCreate"
v-hasPermi=
"['ai:knowledge:create']"
>
type=
"primary"
plain
@
click=
"openForm('create')"
v-hasPermi=
"['ai:knowledge:create']"
>
<Icon
icon=
"ep:plus"
class=
"mr-5px"
/>
新增
<Icon
icon=
"ep:plus"
class=
"mr-5px"
/>
新增
</el-button>
</el-button>
</el-form-item>
</el-form-item>
...
@@ -106,7 +101,7 @@
...
@@ -106,7 +101,7 @@
import
{
getIntDictOptions
,
DICT_TYPE
}
from
'@/utils/dict'
import
{
getIntDictOptions
,
DICT_TYPE
}
from
'@/utils/dict'
import
{
dateFormatter
}
from
'@/utils/formatTime'
import
{
dateFormatter
}
from
'@/utils/formatTime'
import
{
KnowledgeDocumentApi
,
KnowledgeDocumentVO
}
from
'@/api/ai/knowledge/document'
import
{
KnowledgeDocumentApi
,
KnowledgeDocumentVO
}
from
'@/api/ai/knowledge/document'
import
{
useRoute
}
from
'vue-router'
import
{
useRoute
,
useRouter
}
from
'vue-router'
// import KnowledgeDocumentForm from './KnowledgeDocumentForm.vue'
// import KnowledgeDocumentForm from './KnowledgeDocumentForm.vue'
/** AI 知识库文档 列表 */
/** AI 知识库文档 列表 */
...
@@ -115,6 +110,7 @@ defineOptions({ name: 'KnowledgeDocument' })
...
@@ -115,6 +110,7 @@ defineOptions({ name: 'KnowledgeDocument' })
const
message
=
useMessage
()
// 消息弹窗
const
message
=
useMessage
()
// 消息弹窗
const
{
t
}
=
useI18n
()
// 国际化
const
{
t
}
=
useI18n
()
// 国际化
const
route
=
useRoute
()
// 路由
const
route
=
useRoute
()
// 路由
const
router
=
useRouter
()
// 路由
const
loading
=
ref
(
true
)
// 列表的加载中
const
loading
=
ref
(
true
)
// 列表的加载中
const
list
=
ref
<
KnowledgeDocumentVO
[]
>
([])
// 列表的数据
const
list
=
ref
<
KnowledgeDocumentVO
[]
>
([])
// 列表的数据
...
@@ -158,6 +154,11 @@ const openForm = (type: string, id?: number) => {
...
@@ -158,6 +154,11 @@ const openForm = (type: string, id?: number) => {
formRef
.
value
.
open
(
type
,
id
)
formRef
.
value
.
open
(
type
,
id
)
}
}
/** 跳转到创建文档页面 */
const
handleCreate
=
()
=>
{
router
.
push
({
name
:
'AiKnowledgeDocumentCreate'
})
}
/** 删除按钮操作 */
/** 删除按钮操作 */
const
handleDelete
=
async
(
id
:
number
)
=>
{
const
handleDelete
=
async
(
id
:
number
)
=>
{
try
{
try
{
...
...
src/views/bpm/model/form/index.vue
View file @
b7d7b11d
...
@@ -380,7 +380,7 @@ const handleStepClick = async (index: number) => {
...
@@ -380,7 +380,7 @@ const handleStepClick = async (index: number) => {
if
(
index
===
2
)
{
if
(
index
===
2
)
{
await
nextTick
()
await
nextTick
()
// 等待更长时间确保组件完全初始化
// 等待更长时间确保组件完全初始化
await
new
Promise
(
resolve
=>
setTimeout
(
resolve
,
200
))
await
new
Promise
(
(
resolve
)
=>
setTimeout
(
resolve
,
200
))
if
(
processDesignRef
.
value
?.
refresh
)
{
if
(
processDesignRef
.
value
?.
refresh
)
{
await
processDesignRef
.
value
.
refresh
()
await
processDesignRef
.
value
.
refresh
()
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment