diff --git a/frontend/src/api/initialization/index.ts b/frontend/src/api/initialization/index.ts index 66591c797..c42f1483b 100644 --- a/frontend/src/api/initialization/index.ts +++ b/frontend/src/api/initialization/index.ts @@ -19,6 +19,7 @@ export interface InitializationConfig { modelName: string; baseUrl: string; apiKey?: string; + enabled: boolean; }; multimodal: { enabled: boolean; diff --git a/frontend/src/assets/img/user-green.svg b/frontend/src/assets/img/user-green.svg new file mode 100644 index 000000000..6685b1b68 --- /dev/null +++ b/frontend/src/assets/img/user-green.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/assets/img/user.svg b/frontend/src/assets/img/user.svg new file mode 100644 index 000000000..1e26d1ed4 --- /dev/null +++ b/frontend/src/assets/img/user.svg @@ -0,0 +1,4 @@ + + + + diff --git a/frontend/src/components/menu.vue b/frontend/src/components/menu.vue index ede1d29ba..afb55ca59 100644 --- a/frontend/src/components/menu.vue +++ b/frontend/src/components/menu.vue @@ -201,24 +201,19 @@ let knowledgeIcon = ref('zhishiku-green.svg'); let prefixIcon = ref('prefixIcon.svg'); let settingIcon = ref('setting.svg'); let logoutIcon = ref('logout.svg'); -let tenantIcon = ref('setting.svg'); // 暂时使用setting图标 +let tenantIcon = ref('user.svg'); // 使用专门的用户图标 let pathPrefix = ref(route.name) const getIcon = (path) => { fileAddIcon.value = path == 'knowledgeBase' ? 'file-add-green.svg' : 'file-add.svg'; knowledgeIcon.value = path == 'knowledgeBase' ? 'zhishiku-green.svg' : 'zhishiku.svg'; prefixIcon.value = path == 'creatChat' ? 'prefixIcon-green.svg' : path == 'knowledgeBase' ? 'prefixIcon-grey.svg' : 'prefixIcon.svg'; settingIcon.value = path == 'settings' ? 'setting-green.svg' : 'setting.svg'; - tenantIcon.value = path == 'tenant' ? 'setting-green.svg' : 'setting.svg'; // 暂时使用setting图标 + tenantIcon.value = path == 'tenant' ? 'user-green.svg' : 'user.svg'; // 使用专门的用户图标 logoutIcon.value = 'logout.svg'; } getIcon(route.name) const gotopage = (path) => { pathPrefix.value = path; - // 如果是系统设置,跳转到初始化配置页面 - if (path === 'settings') { - router.push('/initialization'); - return; - } // 处理退出登录 if (path === 'logout') { authStore.logout(); diff --git a/frontend/src/router/index.ts b/frontend/src/router/index.ts index f0a4b4a6c..f1fee0765 100644 --- a/frontend/src/router/index.ts +++ b/frontend/src/router/index.ts @@ -62,9 +62,9 @@ const router = createRouter({ { path: "settings", name: "settings", - component: () => import("../views/settings/Settings.vue"), - meta: { requiresInit: true } - }, + component: () => import("../views/settings/SystemSettings.vue"), + meta: { requiresInit: true } + }, ], }, ], diff --git a/frontend/src/views/initialization/InitializationConfig.vue b/frontend/src/views/initialization/InitializationConfig.vue index 8a82a71df..fa26650bc 100644 --- a/frontend/src/views/initialization/InitializationConfig.vue +++ b/frontend/src/views/initialization/InitializationConfig.vue @@ -2,29 +2,20 @@
-

WeKnora 系统初始化配置

-

首次使用需要配置模型和服务信息,完成后即可开始使用系统

+
+

WeKnora 系统初始化配置

+

首次使用需要配置模型和服务信息,完成后即可开始使用系统

+
+
+ + 退出登录 + +
- -
- - -
- + +
+
Ollama 服务状态 @@ -758,8 +749,7 @@
-
-
+
@@ -941,26 +931,6 @@ const imageUpload = ref(null); // Embedding 维度检测状态 const embeddingDimDetecting = ref(false); -// 左侧导航区段 -type Section = { id: string; label: string }; -const sections: Section[] = [ - { id: 'ollama', label: 'Ollama 服务' }, - { id: 'llm', label: 'LLM 模型' }, - { id: 'embedding', label: 'Embedding 模型' }, - { id: 'rerank', label: 'Rerank 配置' }, - { id: 'multimodal', label: '多模态配置' }, - { id: 'docsplit', label: '文档分割' }, - { id: 'submit', label: '完成配置' }, -]; - -const activeSectionId = ref('ollama'); -const goToSection = (id: string) => { - const el = document.getElementById(`section-${id}`); - if (el) { - el.scrollIntoView({ behavior: 'smooth', block: 'start' }); - activeSectionId.value = id; - } -}; // 退出登录 const handleLogout = () => { @@ -968,19 +938,6 @@ const handleLogout = () => { router.replace('/login'); }; -// 监听滚动,高亮当前区块 -const onScroll = () => { - const order = ['ollama','llm','embedding','rerank','multimodal','docsplit','submit']; - for (const id of order) { - const el = document.getElementById(`section-${id}`); - if (!el) continue; - const rect = el.getBoundingClientRect(); - if (rect.top <= 120 && rect.bottom >= 120) { - activeSectionId.value = id; - break; - } - } -}; // 配置回填 const loadCurrentConfig = async () => { @@ -1004,8 +961,8 @@ const loadCurrentConfig = async () => { if (config.rerank) { Object.assign(formData.rerank, config.rerank); } - formData.storageType = config.multimodal.storageType; if (config.multimodal) { + formData.storageType = config.multimodal.storageType || 'minio'; formData.multimodal.enabled = config.multimodal.enabled || false; if (config.multimodal.vlm) { Object.assign(formData.multimodal.vlm, config.multimodal.vlm); @@ -1014,11 +971,14 @@ const loadCurrentConfig = async () => { formData.multimodal.vlm.interfaceType = 'ollama'; } } - if (config.multimodal.storageType === 'cos') { + if (config.multimodal.storageType === 'cos' && config.multimodal.cos) { Object.assign(formData.multimodal.cos, config.multimodal.cos); - } else if (config.multimodal.storageType === 'minio') { + } else if (config.multimodal.storageType === 'minio' && config.multimodal.minio) { Object.assign(formData.multimodal.minio, config.multimodal.minio); } + } else { + // 如果没有多模态配置,设置默认存储类型 + formData.storageType = 'minio'; } if (config.documentSplitting) { Object.assign(formData.documentSplitting, config.documentSplitting); @@ -1351,8 +1311,6 @@ onUnmounted(() => { clearTimeout(submitDebounceTimer.value); submitDebounceTimer.value = null; } - - window.removeEventListener('scroll', onScroll); }); // 事件处理 @@ -1740,6 +1698,23 @@ const handleSubmit = async () => { // 调用初始化API const payload: any = JSON.parse(JSON.stringify(formData)); + + // 确保存储类型在正确的位置,并只保留选中的存储配置 + if (payload.multimodal && payload.storageType) { + payload.multimodal.storageType = payload.storageType; + + // 根据选择的存储类型,只保留对应的配置,删除其他存储配置 + if (payload.storageType === 'cos') { + // 保留COS配置,删除MinIO配置 + delete payload.multimodal.minio; + } else if (payload.storageType === 'minio') { + // 保留MinIO配置,删除COS配置 + delete payload.multimodal.cos; + } + + delete payload.storageType; // 删除顶级的storageType字段 + } + await initializeSystem(payload); const successMessage = isUpdateMode.value ? '配置更新成功!' : '系统初始化成功!'; @@ -1777,9 +1752,6 @@ onMounted(async () => { // 检查已配置模型状态 await checkAllConfiguredModels(); - - // 绑定滚动监听,用于左侧导航高亮 - window.addEventListener('scroll', onScroll, { passive: true }); }); const onRerankChange = () => { @@ -2347,144 +2319,75 @@ const detectEmbeddingDimension = async () => { diff --git a/frontend/src/views/settings/SystemSettings.vue b/frontend/src/views/settings/SystemSettings.vue new file mode 100644 index 000000000..730b3d04f --- /dev/null +++ b/frontend/src/views/settings/SystemSettings.vue @@ -0,0 +1,125 @@ + + + + + diff --git a/frontend/src/views/tenant/TenantInfo.vue b/frontend/src/views/tenant/TenantInfo.vue index 6379b154d..1890cb26c 100644 --- a/frontend/src/views/tenant/TenantInfo.vue +++ b/frontend/src/views/tenant/TenantInfo.vue @@ -311,11 +311,11 @@ onMounted(() => { .tenant-info-container { padding: 20px; background-color: #fff; - border-radius: 8px; - margin: 0 20px 20px 20px; - height: calc(100vh - 40px); + margin: 0 20px 0 20px; + height: calc(100vh); overflow-y: auto; box-sizing: border-box; + flex: 1; } .tenant-header { @@ -363,7 +363,7 @@ onMounted(() => { .api-key-content, .storage-content, .doc-content { -// padding: 16px 0; + margin-top: 0; } .api-key-input { diff --git a/internal/handler/initialization.go b/internal/handler/initialization.go index 07078a401..b74fd5580 100644 --- a/internal/handler/initialization.go +++ b/internal/handler/initialization.go @@ -83,9 +83,7 @@ func NewInitializationHandler( // InitializationRequest 初始化请求结构 type InitializationRequest struct { - // 前端传入的存储类型:cos 或 minio - StorageType string `json:"storageType"` - LLM struct { + LLM struct { Source string `json:"source" binding:"required"` ModelName string `json:"modelName" binding:"required"` BaseURL string `json:"baseUrl"` @@ -115,7 +113,8 @@ type InitializationRequest struct { APIKey string `json:"apiKey"` InterfaceType string `json:"interfaceType"` // "ollama" or "openai" } `json:"vlm,omitempty"` - COS *struct { + StorageType string `json:"storageType"` + COS *struct { SecretID string `json:"secretId"` SecretKey string `json:"secretKey"` Region string `json:"region"` @@ -210,7 +209,7 @@ func (h *InitializationHandler) Initialize(c *gin.Context) { // 验证多模态配置 if req.Multimodal.Enabled { - storageType := strings.ToLower(req.StorageType) + storageType := strings.ToLower(req.Multimodal.StorageType) if req.Multimodal.VLM == nil { logger.Error(ctx, "Multimodal enabled but missing VLM configuration") c.Error(errors.NewBadRequestError("启用多模态时需要配置VLM信息")) @@ -502,11 +501,11 @@ func (h *InitializationHandler) Initialize(c *gin.Context) { InterfaceType: req.Multimodal.VLM.InterfaceType, }, } - switch req.StorageType { + switch req.Multimodal.StorageType { case "cos": if req.Multimodal.COS != nil { kb.StorageConfig = types.StorageConfig{ - Provider: req.StorageType, + Provider: req.Multimodal.StorageType, BucketName: req.Multimodal.COS.BucketName, AppID: req.Multimodal.COS.AppID, PathPrefix: req.Multimodal.COS.PathPrefix, @@ -518,7 +517,7 @@ func (h *InitializationHandler) Initialize(c *gin.Context) { case "minio": if req.Multimodal.Minio != nil { kb.StorageConfig = types.StorageConfig{ - Provider: req.StorageType, + Provider: req.Multimodal.StorageType, BucketName: req.Multimodal.Minio.BucketName, PathPrefix: req.Multimodal.Minio.PathPrefix, SecretID: os.Getenv("MINIO_ACCESS_KEY_ID"), @@ -560,11 +559,11 @@ func (h *InitializationHandler) Initialize(c *gin.Context) { APIKey: req.Multimodal.VLM.APIKey, InterfaceType: req.Multimodal.VLM.InterfaceType, } - switch req.StorageType { + switch req.Multimodal.StorageType { case "cos": if req.Multimodal.COS != nil { kb.StorageConfig = types.StorageConfig{ - Provider: req.StorageType, + Provider: req.Multimodal.StorageType, SecretID: req.Multimodal.COS.SecretID, SecretKey: req.Multimodal.COS.SecretKey, Region: req.Multimodal.COS.Region, @@ -576,7 +575,7 @@ func (h *InitializationHandler) Initialize(c *gin.Context) { case "minio": if req.Multimodal.Minio != nil { kb.StorageConfig = types.StorageConfig{ - Provider: req.StorageType, + Provider: req.Multimodal.StorageType, BucketName: req.Multimodal.Minio.BucketName, PathPrefix: req.Multimodal.Minio.PathPrefix, SecretID: os.Getenv("MINIO_ACCESS_KEY_ID"),