feat: 增强 Knowledge 创建流程步骤验证
- 添加步骤有效性验证逻辑 - 支持跳转到已完成步骤 - 优化创建流程用户体验 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,64 @@
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { ref, computed } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { useModelSettings } from './settings/useModelSettings'
|
||||
import './knowledge/knowledge.css'
|
||||
|
||||
// 获取已配置的模型列表
|
||||
const { models, fetchModels } = useModelSettings()
|
||||
|
||||
// 步骤验证
|
||||
const step1Valid = computed(() => !!newKbForm.value.name.trim())
|
||||
const step2Valid = computed(() => !!embeddingConfig.value.modelId)
|
||||
const step3Valid = computed(() => true) // Parsing - 暂时默认通过
|
||||
const step4Valid = computed(() => true) // Storage - 暂时默认通过
|
||||
|
||||
// 获取当前步骤是否有效
|
||||
const isCurrentStepValid = computed(() => {
|
||||
switch (createStep.value) {
|
||||
case 1: return step1Valid.value
|
||||
case 2: return step2Valid.value
|
||||
case 3: return step3Valid.value
|
||||
case 4: return step4Valid.value
|
||||
default: return false
|
||||
}
|
||||
})
|
||||
|
||||
// 步骤是否可以点击(只能点击当前步骤或已完成的步骤)
|
||||
const canClickStep = (step: number) => {
|
||||
if (step === 1) return true
|
||||
if (step === createStep.value) return true
|
||||
// 可以点击已完成的前面的步骤
|
||||
if (step < createStep.value) {
|
||||
switch (step) {
|
||||
case 1: return step1Valid.value
|
||||
case 2: return step2Valid.value
|
||||
case 3: return step3Valid.value
|
||||
case 4: return step4Valid.value
|
||||
default: return false
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 下一步
|
||||
const nextStep = () => {
|
||||
if (!isCurrentStepValid.value) {
|
||||
ElMessage.warning('Please complete the current step first')
|
||||
return
|
||||
}
|
||||
if (createStep.value < 4) {
|
||||
createStep.value++
|
||||
}
|
||||
}
|
||||
|
||||
// 上一步
|
||||
const prevStep = () => {
|
||||
if (createStep.value > 1) {
|
||||
createStep.value--
|
||||
}
|
||||
}
|
||||
|
||||
// 模拟知识库数据
|
||||
const knowledgeBases = ref([
|
||||
{
|
||||
@@ -55,8 +111,7 @@ const newKbForm = ref({
|
||||
description: '',
|
||||
})
|
||||
const embeddingConfig = ref({
|
||||
provider: '',
|
||||
model: '',
|
||||
modelId: '',
|
||||
})
|
||||
|
||||
const parsingConfig = ref({
|
||||
@@ -71,7 +126,9 @@ const parsingConfig = ref({
|
||||
const openCreateDialog = () => {
|
||||
createStep.value = 1
|
||||
newKbForm.value = { name: '', description: '' }
|
||||
embeddingConfig.value = { provider: '', model: '' }
|
||||
embeddingConfig.value = { modelId: '' }
|
||||
// 获取已配置的模型列表
|
||||
fetchModels()
|
||||
parsingConfig.value = {
|
||||
enablePdf: true,
|
||||
engine: 'default',
|
||||
@@ -85,7 +142,7 @@ const openCreateDialog = () => {
|
||||
|
||||
const cancelCreate = () => {
|
||||
newKbForm.value = { name: '', description: '' }
|
||||
embeddingConfig.value = { provider: '', model: '' }
|
||||
embeddingConfig.value = { modelId: '' }
|
||||
parsingConfig.value = {
|
||||
enablePdf: true,
|
||||
engine: 'default',
|
||||
@@ -97,18 +154,6 @@ const cancelCreate = () => {
|
||||
showCreateDialog.value = false
|
||||
}
|
||||
|
||||
const nextStep = () => {
|
||||
if (!newKbForm.value.name) {
|
||||
ElMessage.warning('Please enter knowledge base name')
|
||||
return
|
||||
}
|
||||
createStep.value = 2
|
||||
}
|
||||
|
||||
const prevStep = () => {
|
||||
createStep.value = 1
|
||||
}
|
||||
|
||||
const createKnowledgeBase = () => {
|
||||
knowledgeBases.value.push({
|
||||
id: Date.now().toString(),
|
||||
@@ -120,7 +165,7 @@ const createKnowledgeBase = () => {
|
||||
status: 'ready'
|
||||
})
|
||||
newKbForm.value = { name: '', description: '' }
|
||||
embeddingConfig.value = { provider: '', model: '' }
|
||||
embeddingConfig.value = { modelId: '' }
|
||||
showCreateDialog.value = false
|
||||
ElMessage.success('Knowledge base created successfully')
|
||||
}
|
||||
@@ -300,11 +345,12 @@ const viewDetail = (kb: any) => {
|
||||
<span class="menu-desc">Name and description</span>
|
||||
</div>
|
||||
<i v-if="createStep === 1" class="fa-solid fa-chevron-right menu-arrow"></i>
|
||||
<i v-else-if="createStep > 1 && step1Valid" class="fa-solid fa-check-circle menu-check"></i>
|
||||
</div>
|
||||
<div
|
||||
class="menu-item"
|
||||
:class="{ active: createStep === 2 }"
|
||||
@click="createStep = 2"
|
||||
:class="{ active: createStep === 2, disabled: !canClickStep(2) }"
|
||||
@click="canClickStep(2) && (createStep = 2)"
|
||||
>
|
||||
<i class="fa-solid fa-robot menu-icon"></i>
|
||||
<div class="menu-content">
|
||||
@@ -312,11 +358,12 @@ const viewDetail = (kb: any) => {
|
||||
<span class="menu-desc">Embedding & rerank models</span>
|
||||
</div>
|
||||
<i v-if="createStep === 2" class="fa-solid fa-chevron-right menu-arrow"></i>
|
||||
<i v-else-if="createStep > 2 && step2Valid" class="fa-solid fa-check-circle menu-check"></i>
|
||||
</div>
|
||||
<div
|
||||
class="menu-item"
|
||||
:class="{ active: createStep === 3 }"
|
||||
@click="createStep = 3"
|
||||
:class="{ active: createStep === 3, disabled: !canClickStep(3) }"
|
||||
@click="canClickStep(3) && (createStep = 3)"
|
||||
>
|
||||
<i class="fa-solid fa-file-lines menu-icon"></i>
|
||||
<div class="menu-content">
|
||||
@@ -324,11 +371,12 @@ const viewDetail = (kb: any) => {
|
||||
<span class="menu-desc">Document parsing settings</span>
|
||||
</div>
|
||||
<i v-if="createStep === 3" class="fa-solid fa-chevron-right menu-arrow"></i>
|
||||
<i v-else-if="createStep > 3 && step3Valid" class="fa-solid fa-check-circle menu-check"></i>
|
||||
</div>
|
||||
<div
|
||||
class="menu-item"
|
||||
:class="{ active: createStep === 4 }"
|
||||
@click="createStep = 4"
|
||||
:class="{ active: createStep === 4, disabled: !canClickStep(4) }"
|
||||
@click="canClickStep(4) && (createStep = 4)"
|
||||
>
|
||||
<i class="fa-solid fa-hard-drive menu-icon"></i>
|
||||
<div class="menu-content">
|
||||
@@ -336,42 +384,7 @@ const viewDetail = (kb: any) => {
|
||||
<span class="menu-desc">Vector & file storage</span>
|
||||
</div>
|
||||
<i v-if="createStep === 4" class="fa-solid fa-chevron-right menu-arrow"></i>
|
||||
</div>
|
||||
<div
|
||||
class="menu-item"
|
||||
:class="{ active: createStep === 5 }"
|
||||
@click="createStep = 5"
|
||||
>
|
||||
<i class="fa-solid fa-scissors menu-icon"></i>
|
||||
<div class="menu-content">
|
||||
<span class="menu-title">Chunking</span>
|
||||
<span class="menu-desc">Text chunking strategy</span>
|
||||
</div>
|
||||
<i v-if="createStep === 5" class="fa-solid fa-chevron-right menu-arrow"></i>
|
||||
</div>
|
||||
<div
|
||||
class="menu-item"
|
||||
:class="{ active: createStep === 6 }"
|
||||
@click="createStep = 6"
|
||||
>
|
||||
<i class="fa-solid fa-project-diagram menu-icon"></i>
|
||||
<div class="menu-content">
|
||||
<span class="menu-title">Knowledge Graph</span>
|
||||
<span class="menu-desc">Graph extraction settings</span>
|
||||
</div>
|
||||
<i v-if="createStep === 6" class="fa-solid fa-chevron-right menu-arrow"></i>
|
||||
</div>
|
||||
<div
|
||||
class="menu-item"
|
||||
:class="{ active: createStep === 7 }"
|
||||
@click="createStep = 7"
|
||||
>
|
||||
<i class="fa-solid fa-sliders menu-icon"></i>
|
||||
<div class="menu-content">
|
||||
<span class="menu-title">Advanced</span>
|
||||
<span class="menu-desc">Additional settings</span>
|
||||
</div>
|
||||
<i v-if="createStep === 7" class="fa-solid fa-chevron-right menu-arrow"></i>
|
||||
<i v-else-if="createStep > 4 && step4Valid" class="fa-solid fa-check-circle menu-check"></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -400,31 +413,21 @@ const viewDetail = (kb: any) => {
|
||||
<span class="section-title">Model Config</span>
|
||||
</div>
|
||||
<el-form label-position="top" class="kb-form">
|
||||
<el-form-item label="Embedding Provider">
|
||||
<el-select v-model="embeddingConfig.provider" placeholder="Select embedding provider" class="w-full">
|
||||
<el-option label="OpenAI" value="openai">
|
||||
<div class="provider-option">
|
||||
<i class="fa-solid fa-robot"></i>
|
||||
<span>OpenAI</span>
|
||||
</div>
|
||||
</el-option>
|
||||
<el-option label="Ollama" value="ollama">
|
||||
<div class="provider-option">
|
||||
<i class="fa-solid fa-microchip"></i>
|
||||
<span>Ollama</span>
|
||||
</div>
|
||||
</el-option>
|
||||
<el-option label="Azure OpenAI" value="azure">
|
||||
<div class="provider-option">
|
||||
<i class="fa-brands fa-microsoft"></i>
|
||||
<span>Azure OpenAI</span>
|
||||
<el-form-item label="Embedding Model">
|
||||
<el-select v-model="embeddingConfig.modelId" placeholder="Select a configured model" class="w-full" popper-class="dark-select-dropdown">
|
||||
<el-option
|
||||
v-for="model in models"
|
||||
:key="model.id"
|
||||
:label="model.name"
|
||||
:value="model.id"
|
||||
>
|
||||
<div class="model-option">
|
||||
<span class="model-name">{{ model.name }}</span>
|
||||
<span class="model-info">{{ model.provider }} - {{ model.model }}</span>
|
||||
</div>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="Embedding Model">
|
||||
<el-input v-model="embeddingConfig.model" placeholder="e.g., text-embedding-ada-002" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
@@ -510,76 +513,25 @@ const viewDetail = (kb: any) => {
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<!-- Step 5: Chunking -->
|
||||
<div v-if="createStep === 5" class="form-content">
|
||||
<div class="section-header">
|
||||
<i class="fa-solid fa-scissors section-icon"></i>
|
||||
<span class="section-title">Chunking</span>
|
||||
</div>
|
||||
<el-form label-position="top" class="kb-form">
|
||||
<el-form-item label="Chunk Size">
|
||||
<el-input type="number" placeholder="e.g., 500" />
|
||||
</el-form-item>
|
||||
<el-form-item label="Chunk Overlap">
|
||||
<el-input type="number" placeholder="e.g., 50" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<!-- Step 6: Knowledge Graph -->
|
||||
<div v-if="createStep === 6" class="form-content">
|
||||
<div class="section-header">
|
||||
<i class="fa-solid fa-project-diagram section-icon"></i>
|
||||
<span class="section-title">Knowledge Graph</span>
|
||||
</div>
|
||||
<el-form label-position="top" class="kb-form">
|
||||
<el-form-item label="Enable Knowledge Graph">
|
||||
<el-switch />
|
||||
</el-form-item>
|
||||
<el-form-item label="Graph Extraction">
|
||||
<el-select placeholder="Select extraction level" class="w-full">
|
||||
<el-option label="Entities Only" value="entities" />
|
||||
<el-option label="Entities & Relations" value="relations" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<!-- Step 7: Advanced -->
|
||||
<div v-if="createStep === 7" class="form-content">
|
||||
<div class="section-header">
|
||||
<i class="fa-solid fa-sliders section-icon"></i>
|
||||
<span class="section-title">Advanced</span>
|
||||
</div>
|
||||
<el-form label-position="top" class="kb-form">
|
||||
<el-form-item label="Top K">
|
||||
<el-input type="number" placeholder="e.g., 5" />
|
||||
</el-form-item>
|
||||
<el-form-item label="Score Threshold">
|
||||
<el-input type="number" placeholder="e.g., 0.7" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<div class="footer-left">
|
||||
<span class="step-hint">Step {{ createStep }} of 7</span>
|
||||
<span class="step-hint">Step {{ createStep }} of 4</span>
|
||||
</div>
|
||||
<div class="footer-right">
|
||||
<el-button @click="cancelCreate" class="cancel-btn">Cancel</el-button>
|
||||
<el-button v-if="createStep > 1" @click="createStep--" class="prev-btn">
|
||||
<el-button v-if="createStep > 1" @click="prevStep" class="prev-btn">
|
||||
<i class="fa-solid fa-arrow-left"></i>
|
||||
Previous
|
||||
</el-button>
|
||||
<el-button v-if="createStep < 7" @click="createStep++" class="next-btn">
|
||||
<el-button v-if="createStep < 4" :disabled="!isCurrentStepValid" @click="nextStep" class="next-btn">
|
||||
Next
|
||||
<i class="fa-solid fa-arrow-right"></i>
|
||||
</el-button>
|
||||
<el-button v-if="createStep === 7" @click="createKnowledgeBase" class="confirm-btn">
|
||||
<el-button v-if="createStep === 4" :disabled="!isCurrentStepValid" @click="createKnowledgeBase" class="confirm-btn">
|
||||
<i class="fa-solid fa-plus"></i>
|
||||
Create
|
||||
</el-button>
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
}
|
||||
|
||||
.search-input:focus {
|
||||
border-color: #f97316;
|
||||
border-color: #ffffff;
|
||||
}
|
||||
|
||||
.search-input::placeholder {
|
||||
@@ -116,7 +116,7 @@
|
||||
}
|
||||
|
||||
.kb-dialog :deep(.el-dialog__headerbtn:hover .el-dialog__close) {
|
||||
color: #f97316;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.kb-dialog :deep(.el-dialog__body) {
|
||||
@@ -135,8 +135,8 @@
|
||||
}
|
||||
|
||||
.kb-dialog :deep(.el-button--primary) {
|
||||
background-color: #f97316;
|
||||
border-color: #f97316;
|
||||
background-color: #ffffff;
|
||||
border-color: #ffffff;
|
||||
}
|
||||
|
||||
.kb-dialog :deep(.el-button--primary:hover) {
|
||||
@@ -226,6 +226,20 @@
|
||||
border-color: rgba(54, 191, 250, 0.25);
|
||||
}
|
||||
|
||||
.menu-item.disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.menu-item.disabled .menu-content {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.menu-check {
|
||||
color: #22c55e;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.menu-icon {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
@@ -241,9 +255,9 @@
|
||||
}
|
||||
|
||||
.menu-item.active .menu-icon {
|
||||
background: linear-gradient(135deg, #36bffa 0%, #0ea5e9 100%);
|
||||
background: linear-gradient(135deg, #f97316 0%, #ea580c 100%);
|
||||
color: white;
|
||||
box-shadow: 0 4px 12px rgba(54, 191, 250, 0.3);
|
||||
box-shadow: 0 4px 12px rgba(249, 115, 22, 0.3);
|
||||
}
|
||||
|
||||
.menu-content {
|
||||
@@ -262,7 +276,7 @@
|
||||
}
|
||||
|
||||
.menu-item.active .menu-title {
|
||||
color: #36bffa;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.menu-desc {
|
||||
@@ -272,7 +286,7 @@
|
||||
|
||||
.menu-arrow {
|
||||
font-size: 12px;
|
||||
color: #36bffa;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/* 右侧内容区 */
|
||||
@@ -311,11 +325,11 @@
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 14px;
|
||||
background: linear-gradient(135deg, rgba(54, 191, 250, 0.2) 0%, rgba(14, 165, 233, 0.12) 100%);
|
||||
background: linear-gradient(135deg, rgba(249, 115, 22, 0.2) 0%, rgba(234, 88, 12, 0.12) 100%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #36bffa;
|
||||
color: #ffffff;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
@@ -342,11 +356,11 @@
|
||||
}
|
||||
|
||||
.kb-form :deep(.el-input__wrapper:hover) {
|
||||
border-color: #36bffa;
|
||||
border-color: #ffffff;
|
||||
}
|
||||
|
||||
.kb-form :deep(.el-input__wrapper.is-focus) {
|
||||
border-color: #36bffa;
|
||||
border-color: #ffffff;
|
||||
box-shadow: 0 0 0 3px rgba(54, 191, 250, 0.15);
|
||||
}
|
||||
|
||||
@@ -370,11 +384,11 @@
|
||||
}
|
||||
|
||||
.kb-form :deep(.el-textarea__inner:hover) {
|
||||
border-color: #36bffa;
|
||||
border-color: #ffffff;
|
||||
}
|
||||
|
||||
.kb-form :deep(.el-textarea__inner:focus) {
|
||||
border-color: #36bffa;
|
||||
border-color: #ffffff;
|
||||
box-shadow: 0 0 0 3px rgba(54, 191, 250, 0.15);
|
||||
}
|
||||
|
||||
@@ -391,11 +405,11 @@
|
||||
}
|
||||
|
||||
.kb-form :deep(.el-select .el-input__wrapper:hover) {
|
||||
border-color: #36bffa;
|
||||
border-color: #ffffff;
|
||||
}
|
||||
|
||||
.kb-form :deep(.el-select .el-input__wrapper.is-focus) {
|
||||
border-color: #36bffa;
|
||||
border-color: #ffffff;
|
||||
box-shadow: 0 0 0 3px rgba(54, 191, 250, 0.15);
|
||||
}
|
||||
|
||||
@@ -408,7 +422,24 @@
|
||||
}
|
||||
|
||||
.provider-option i {
|
||||
color: #36bffa;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/* 模型选项 */
|
||||
.model-option {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
color: #e8eaed;
|
||||
}
|
||||
|
||||
.model-option .model-name {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.model-option .model-info {
|
||||
font-size: 12px;
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
/* Parsing 配置样式 */
|
||||
@@ -454,8 +485,8 @@
|
||||
|
||||
/* Switch 样式 */
|
||||
:deep(.el-switch.is-checked .el-switch__core) {
|
||||
background-color: #36bffa;
|
||||
border-color: #36bffa;
|
||||
background-color: #ffffff;
|
||||
border-color: #ffffff;
|
||||
}
|
||||
|
||||
.parsing-divider {
|
||||
@@ -566,7 +597,7 @@
|
||||
}
|
||||
|
||||
.next-btn {
|
||||
background: linear-gradient(135deg, #36bffa 0%, #0ea5e9 100%);
|
||||
background: linear-gradient(135deg, #ffffff 0%, #0ea5e9 100%);
|
||||
border: none;
|
||||
color: white;
|
||||
padding: 10px 20px;
|
||||
@@ -580,7 +611,7 @@
|
||||
}
|
||||
|
||||
.next-btn:hover {
|
||||
background: linear-gradient(135deg, #4dc3ff 0%, #36bffa 100%);
|
||||
background: linear-gradient(135deg, #4dc3ff 0%, #ffffff 100%);
|
||||
box-shadow: 0 6px 16px rgba(54, 191, 250, 0.35);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user