linyang hai 3 semanas
pai
achega
ba7bedb018

+ 2 - 2
src/api/knowledge-base.ts

@@ -40,8 +40,8 @@ export interface CustomSchemaField {
 
 export interface CreateKnowledgeBaseData {
   name: string
-  collection_name_parent: string
-  collection_name_children?: string
+  collection_name_parent?: string
+  collection_name_children: string
   description?: string
   dimension?: number
   metadata_fields?: MetadataField[]

+ 58 - 14
src/views/documents/KnowledgeBase.vue

@@ -157,7 +157,14 @@
                 <el-col :span="12">
                     <template v-if="dialogType === 'create'">
             <el-form-item label="父集合名称" prop="collection_name_parent">
-                <el-input v-model="formData.collection_name_parent" placeholder="父集合名(英文)" />
+                <el-checkbox v-model="formData.enable_parent_collection" style="margin-bottom: 8px">
+                    创建父集合
+                </el-checkbox>
+                <el-input
+                    v-model="formData.collection_name_parent"
+                    placeholder="父集合名(英文)"
+                    :disabled="!formData.enable_parent_collection"
+                />
             </el-form-item>
             <el-form-item label="子集合名称" prop="collection_name_children">
                 <el-input v-model="formData.collection_name_children" placeholder="子集合名(英文)" />
@@ -191,10 +198,16 @@
                 </el-col>
                 <el-col :span="12">
                     <el-form-item label="状态" prop="status">
-                        <el-radio-group v-model="formData.status">
-                            <el-radio-button label="normal">正常</el-radio-button>
-                            <el-radio-button label="disabled">禁用</el-radio-button>
-                        </el-radio-group>
+                        <template v-if="dialogType === 'create'">
+                            <!-- 新建时状态固定为禁用,仅同步成功后才启用 -->
+                            <el-tag type="warning" effect="plain">默认禁用(同步到 Milvus 后自动启用)</el-tag>
+                        </template>
+                        <template v-else>
+                            <el-radio-group v-model="formData.status">
+                                <el-radio-button label="normal">正常</el-radio-button>
+                                <el-radio-button label="disabled">禁用</el-radio-button>
+                            </el-radio-group>
+                        </template>
                     </el-form-item>
                 </el-col>
             </el-row>
@@ -374,7 +387,8 @@ const formData = reactive({
   collection_name_children: '',
   description: '',
   dimension: 768,
-  status: 'normal',
+  status: 'disabled',
+  enable_parent_collection: true,
   metadata_fields: [{ field_zh_name: '', field_en_name: '', field_type: 'text', remark: '' }]
 })
 
@@ -407,15 +421,44 @@ const getDisabledOptions = (currentIndex: number) => {
     return selectedValues
 }
 
+const validateParentCollection = (rule: any, value: any, callback: any) => {
+    if (!formData.enable_parent_collection) {
+        callback()
+        return
+    }
+    if (!value) {
+        callback(new Error('请输入父集合名称'))
+        return
+    }
+    const pattern = /^[a-zA-Z_][a-zA-Z0-9_]*$/
+    if (!pattern.test(value)) {
+        callback(new Error('必须以字母或下划线开头,只能包含字母、数字和下划线'))
+        return
+    }
+    callback()
+}
+
+const validateChildCollection = (rule: any, value: any, callback: any) => {
+    if (!value) {
+        callback(new Error('请输入子集合名称'))
+        return
+    }
+    if (value === formData.collection_name_parent && formData.enable_parent_collection) {
+        callback(new Error('子集合名称不能与父集合名称相同'))
+        return
+    }
+    callback()
+}
+
 const rules: FormRules = {
   name: [{ required: true, message: '请输入知识库名称', trigger: 'blur' }],
-  collection_name1: [
-    { required: true, message: '请输入父集合名称', trigger: 'blur' },
-    { pattern: /^[a-zA-Z_][a-zA-Z0-9_]*$/, message: '必须以字母或下划线开头,只能包含字母、数字和下划线', trigger: 'blur' }
+  collection_name_parent: [
+    { validator: validateParentCollection, trigger: 'blur' }
   ],
-  collection_name2: [
-    { required: false, message: '请输入子集合名称', trigger: 'blur' },
-    { pattern: /^[a-zA-Z_][a-zA-Z0-9_]*$/, message: '必须以字母或下划线开头,只能包含字母、数字和下划线', trigger: 'blur' }
+  collection_name_children: [
+    { required: true, message: '请输入子集合名称', trigger: 'blur' },
+    { pattern: /^[a-zA-Z_][a-zA-Z0-9_]*$/, message: '必须以字母或下划线开头,只能包含字母、数字和下划线', trigger: 'blur' },
+    { validator: validateChildCollection, trigger: 'blur' }
   ],
   dimension: [{ required: true, message: '请输入维度', trigger: 'blur' }]
 }
@@ -487,6 +530,7 @@ const handleAdd = () => {
   formData.description = ''
   formData.dimension = 768
   formData.status = 'normal'
+  formData.enable_parent_collection = true
   formData.metadata_fields = [{ field_zh_name: '', field_en_name: '', field_type: 'text', remark: '' }]
   dialogVisible.value = true
 }
@@ -539,7 +583,7 @@ const handleView = async (row: KnowledgeBase) => {
     viewSchema.value = [
         { name: 'pk', type: 'INT64', desc: '主键' },
         { name: 'text', type: 'VARCHAR', desc: '内容 (Max: 65535)' },
-        { name: 'vector', type: 'FLOAT_VECTOR', desc: '向量列 (Dim: 768)' },
+        { name: 'dense', type: 'FLOAT_VECTOR', desc: '向量列 (Dim: 768)' },
         { name: 'sparse', type: 'BM25', desc: '关键字检索' },
         { name: 'document_id', type: 'VARCHAR', desc: '文档ID' },
         { name: 'parent_id', type: 'VARCHAR', desc: '父段ID' },
@@ -627,7 +671,7 @@ const handleSubmit = async () => {
         if (dialogType.value === 'create') {
           await createKnowledgeBase({
             name: formData.name,
-            collection_name_parent: formData.collection_name_parent,
+              collection_name_parent: formData.enable_parent_collection ? formData.collection_name_parent : undefined,
             collection_name_children: formData.collection_name_children,
             description: formData.description,
             dimension: formData.dimension,

+ 34 - 28
src/views/documents/SearchEngine.vue

@@ -67,6 +67,34 @@
             </div>
         </div>
 
+        <!-- 文档范围过滤 (简单模式可见) -->
+        <div class="search-form-row secondary-row" v-if="searchForm.kb_id">
+            <div class="form-item doc-filter">
+                <div class="label">文档范围过滤</div>
+                <el-select 
+                    v-model="searchForm.doc_names" 
+                    placeholder="默认检索全部文档,可选择特定文档进行范围限定" 
+                    multiple
+                    filterable
+                    remote
+                    clearable
+                    collapse-tags
+                    collapse-tags-tooltip
+                    :remote-method="loadDocOptions"
+                    :loading="docLoading"
+                    style="width: 100%"
+                    @focus="() => loadDocOptions('')"
+                >
+                    <el-option
+                        v-for="item in docOptions"
+                        :key="item.value"
+                        :label="item.label"
+                        :value="item.value"
+                    />
+                </el-select>
+            </div>
+        </div>
+
         <!-- 高级过滤区域 (仅在高级模式显示) -->
         <div v-if="searchForm.mode === 'advanced'" class="advanced-filter-area">
             <!-- 顶部操作栏:标题 + 添加按钮 -->
@@ -115,31 +143,6 @@
                     </el-row>
                 </div>
             </div>
-
-            <!-- 文档名称过滤 -->
-            <div class="filter-header" style="margin-top: 20px;">
-                <div class="filter-section-title">文档名称过滤</div>
-            </div>
-            <div class="doc-filter-row">
-                <el-select 
-                    v-model="searchForm.doc_names" 
-                    placeholder="请选择文档进行过滤(可多选)" 
-                    multiple
-                    filterable
-                    remote
-                    :remote-method="loadDocOptions"
-                    :loading="docLoading"
-                    style="width: 100%"
-                    @focus="() => loadDocOptions('')"
-                >
-                    <el-option
-                        v-for="item in docOptions"
-                        :key="item.value"
-                        :label="item.label"
-                        :value="item.value"
-                    />
-                </el-select>
-            </div>
         </div>
       </div>
     </el-card>
@@ -451,7 +454,8 @@ const handleSearch = async () => {
         : []
     
     // 将文档过滤也作为一种特殊的 filter 加入
-    if (searchForm.mode === 'advanced' && searchForm.doc_names.length > 0) {
+    // 注意:即便是简单模式,如果用户选了文档,也应该生效
+    if (searchForm.doc_names.length > 0) {
         // 对于多选文档,我们需要构建 OR 逻辑,但目前的 filters 结构是 AND
         // 我们可以约定 field 为 "doc_name_in",value 为逗号分隔的字符串
         validFilters.push({
@@ -587,8 +591,10 @@ onMounted(() => {
 
 .advanced-filter-area {
     margin-top: 20px;
-    padding-top: 20px;
-    border-top: 1px dashed #dcdfe6;
+    padding: 20px;
+    border-top: 1px solid #ebeef5;
+    background-color: #fafafa;
+    border-radius: 4px;
 }
 
 .filter-header {