mirror of
https://github.com/Tencent/WeKnora.git
synced 2025-11-25 03:15:00 +08:00
chore(ui): Update Setting page
This commit is contained in:
@@ -19,6 +19,7 @@ export interface InitializationConfig {
|
||||
modelName: string;
|
||||
baseUrl: string;
|
||||
apiKey?: string;
|
||||
enabled: boolean;
|
||||
};
|
||||
multimodal: {
|
||||
enabled: boolean;
|
||||
|
||||
4
frontend/src/assets/img/user-green.svg
Normal file
4
frontend/src/assets/img/user-green.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="10" cy="6" r="3" stroke="#07C05F" stroke-width="1.5" fill="none"/>
|
||||
<path d="M4 16c0-3.314 2.686-6 6-6s6 2.686 6 6" stroke="#07C05F" stroke-width="1.5" fill="none"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 284 B |
4
frontend/src/assets/img/user.svg
Normal file
4
frontend/src/assets/img/user.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<circle cx="10" cy="6" r="3" stroke="currentColor" stroke-width="1.5" fill="none"/>
|
||||
<path d="M4 16c0-3.314 2.686-6 6-6s6 2.686 6 6" stroke="currentColor" stroke-width="1.5" fill="none"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 294 B |
@@ -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();
|
||||
|
||||
@@ -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 }
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
@@ -2,29 +2,20 @@
|
||||
<div class="initialization-container">
|
||||
<!-- 页面标题区域 -->
|
||||
<div class="initialization-header">
|
||||
<h1>WeKnora 系统初始化配置</h1>
|
||||
<p>首次使用需要配置模型和服务信息,完成后即可开始使用系统</p>
|
||||
<div class="header-content">
|
||||
<h1>WeKnora 系统初始化配置</h1>
|
||||
<p>首次使用需要配置模型和服务信息,完成后即可开始使用系统</p>
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<t-button size="small" variant="outline" theme="danger" @click="handleLogout">
|
||||
退出登录
|
||||
</t-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 页面主体两栏布局:左侧导航 + 右侧内容 -->
|
||||
<div class="init-layout">
|
||||
<!-- 左侧导航 -->
|
||||
<aside class="sidebar">
|
||||
<div class="sidebar-card">
|
||||
<div class="nav-title">配置导航</div>
|
||||
<ul class="nav-list">
|
||||
<li v-for="s in sections" :key="s.id" :class="['nav-item', { active: activeSectionId === s.id }]" @click="goToSection(s.id)">
|
||||
<span class="dot" />{{ s.label }}
|
||||
</li>
|
||||
</ul>
|
||||
<t-divider />
|
||||
<t-button size="small" variant="outline" theme="danger" block @click="handleLogout">
|
||||
退出登录
|
||||
</t-button>
|
||||
</div>
|
||||
</aside>
|
||||
<div class="init-main">
|
||||
<!-- 顶部公共区域:Ollama 服务状态与已安装模型 -->
|
||||
<!-- 主要内容区域 -->
|
||||
<div class="init-content">
|
||||
<!-- 顶部公共区域:Ollama 服务状态与已安装模型 -->
|
||||
<div class="ollama-summary-card" id="section-ollama">
|
||||
<div class="summary-header">
|
||||
<span class="title"><t-icon name="server" />Ollama 服务状态</span>
|
||||
@@ -758,8 +749,7 @@
|
||||
|
||||
</div>
|
||||
</t-form>
|
||||
</div> <!-- .init-main -->
|
||||
</div> <!-- .init-layout -->
|
||||
</div> <!-- .init-content -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -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<string>('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 () => {
|
||||
|
||||
<style lang="less" scoped>
|
||||
.initialization-container {
|
||||
padding: 20px 16px;
|
||||
background: linear-gradient(180deg, #f7faf9 0%, #f9fbfa 60%, #ffffff 100%);
|
||||
scroll-behavior: smooth;
|
||||
min-height: 100vh;
|
||||
background: #f8fafb;
|
||||
padding: 0;
|
||||
|
||||
.initialization-header {
|
||||
text-align: center;
|
||||
margin: 10px auto 18px;
|
||||
background: #fff;
|
||||
border-bottom: 1px solid #e8f0fe;
|
||||
padding: 24px 32px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
h1 {
|
||||
margin: 0 0 6px;
|
||||
font-size: 22px;
|
||||
font-weight: 700;
|
||||
color: #0f172a;
|
||||
.header-content {
|
||||
h1 {
|
||||
margin: 0 0 8px;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
color: #1f2937;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
color: #6b7280;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
color: #64748b;
|
||||
font-size: 14px;
|
||||
.header-actions {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
.init-layout {
|
||||
display: grid;
|
||||
grid-template-columns: 220px 1fr;
|
||||
gap: 24px;
|
||||
|
||||
.init-content {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 24px 32px;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
position: sticky;
|
||||
top: 20px;
|
||||
height: fit-content;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.sidebar-card {
|
||||
background: #fff;
|
||||
border: 1px solid #e8f5e8;
|
||||
border-radius: 14px;
|
||||
box-shadow: 0 8px 24px rgba(7, 192, 95, 0.08);
|
||||
padding: 16px 12px;
|
||||
}
|
||||
|
||||
.nav-title {
|
||||
font-weight: 600;
|
||||
color: #2c5234;
|
||||
padding: 8px 10px 12px;
|
||||
border-bottom: 2px solid #f0fdf4;
|
||||
margin-bottom: 12px;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.nav-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: #6b7280;
|
||||
padding: 10px 12px;
|
||||
border-radius: 10px;
|
||||
transition: all 0.2s ease;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.nav-item:hover {
|
||||
background: #f0fdf4;
|
||||
color: #166534;
|
||||
transform: translateX(2px);
|
||||
}
|
||||
|
||||
.nav-item.active {
|
||||
background: linear-gradient(135deg, #07c05f, #00a651);
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
box-shadow: 0 4px 12px rgba(7, 192, 95, 0.2);
|
||||
transform: translateX(2px);
|
||||
}
|
||||
|
||||
.nav-item .dot {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: currentColor;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.nav-item.active .dot {
|
||||
background: white;
|
||||
box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.init-main {
|
||||
min-width: 0;
|
||||
max-width: 960px;
|
||||
}
|
||||
/* 统一分区卡片视觉 */
|
||||
.config-section {
|
||||
background: #fff;
|
||||
border: 1px solid #eef4f0;
|
||||
border: 1px solid #e8f0fe;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 6px 18px rgba(7, 192, 95, 0.04);
|
||||
padding: 16px;
|
||||
margin: 14px 0;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
padding: 24px;
|
||||
margin-bottom: 24px;
|
||||
|
||||
h3 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin: 0 0 12px;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
color: #0f172a;
|
||||
margin: 0 0 20px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #1f2937;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 1px solid #f3f4f6;
|
||||
}
|
||||
|
||||
.section-icon {
|
||||
color: #07c05f;
|
||||
font-size: 18px;
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
.ollama-summary-card {
|
||||
max-width: 100%;
|
||||
margin: 0 0 16px 0;
|
||||
background: #ffffff;
|
||||
border: 1px solid #e9edf5;
|
||||
background: #fff;
|
||||
border: 1px solid #e8f0fe;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 8px 24px rgba(15, 23, 42, 0.05);
|
||||
padding: 14px 16px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
padding: 20px;
|
||||
margin-bottom: 24px;
|
||||
|
||||
.summary-header {
|
||||
display: flex;
|
||||
@@ -2522,57 +2425,6 @@ const detectEmbeddingDimension = async () => {
|
||||
}
|
||||
}
|
||||
}
|
||||
min-height: 100vh;
|
||||
/* 更柔和的浅色背景,贴近截图的干净感 */
|
||||
background: linear-gradient(180deg, #f7fbff 0%, #f8fff9 100%);
|
||||
padding: 48px 20px;
|
||||
|
||||
.initialization-header {
|
||||
text-align: center;
|
||||
margin-bottom: 40px;
|
||||
color: #2c5234;
|
||||
|
||||
.logo-container {
|
||||
margin-bottom: 20px;
|
||||
|
||||
.logo {
|
||||
height: 60px;
|
||||
width: auto;
|
||||
max-width: 200px;
|
||||
object-fit: contain;
|
||||
filter: drop-shadow(0 4px 8px rgba(44, 82, 52, 0.1));
|
||||
transition: transform 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 32px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 10px;
|
||||
text-shadow: 0 2px 4px rgba(44, 82, 52, 0.1);
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 16px;
|
||||
opacity: 0.8;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.t-form) {
|
||||
/* 表单容器卡片化:更宽一点、更浅的阴影与细边框 */
|
||||
max-width: 100%;
|
||||
margin: 0;
|
||||
background: #fff;
|
||||
padding: 32px 36px;
|
||||
border-radius: 14px;
|
||||
border: 1px solid #e9edf5;
|
||||
box-shadow: 0 12px 32px rgba(15, 23, 42, 0.06);
|
||||
}
|
||||
|
||||
.config-section {
|
||||
margin-bottom: 32px;
|
||||
|
||||
2712
frontend/src/views/initialization/InitializationContent.vue
Normal file
2712
frontend/src/views/initialization/InitializationContent.vue
Normal file
File diff suppressed because it is too large
Load Diff
125
frontend/src/views/settings/SystemSettings.vue
Normal file
125
frontend/src/views/settings/SystemSettings.vue
Normal file
@@ -0,0 +1,125 @@
|
||||
<template>
|
||||
<div class="system-settings-container">
|
||||
<!-- 页面标题区域 -->
|
||||
<div class="settings-header">
|
||||
<h2>系统设置</h2>
|
||||
<p class="settings-subtitle">管理和更新系统模型与服务配置</p>
|
||||
</div>
|
||||
|
||||
<!-- 配置内容 -->
|
||||
<div class="settings-content">
|
||||
<!-- 直接引用初始化配置组件的内容,不使用外层卡片包装 -->
|
||||
<InitializationContent />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
// 异步加载初始化配置组件
|
||||
const InitializationContent = defineAsyncComponent(() => import('../initialization/InitializationContent.vue'))
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.system-settings-container {
|
||||
padding: 20px;
|
||||
background-color: #fff;
|
||||
margin: 0 20px 0 20px;
|
||||
height: calc(100vh);
|
||||
overflow-y: auto;
|
||||
box-sizing: border-box;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.settings-header {
|
||||
margin-bottom: 20px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
padding-bottom: 16px;
|
||||
|
||||
h2 {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
color: #000000;
|
||||
margin: 0 0 8px 0;
|
||||
}
|
||||
|
||||
.settings-subtitle {
|
||||
font-size: 14px;
|
||||
color: #666666;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.settings-content {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.system-settings-container {
|
||||
padding: 16px;
|
||||
margin: 10px;
|
||||
height: calc(100vh - 20px);
|
||||
}
|
||||
|
||||
.settings-header h2 {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 覆盖TDesign组件样式,与账户信息页面保持一致 */
|
||||
:deep(.t-card) {
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
border: 1px solid #e5e7eb;
|
||||
}
|
||||
|
||||
/* 调整InitializationContent内部样式,使每个配置区域显示为独立卡片 */
|
||||
:deep(.config-section) {
|
||||
background: #fff;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #000000;
|
||||
margin: 0 0 16px 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0;
|
||||
background: none;
|
||||
border-left: none;
|
||||
border-radius: 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
padding-bottom: 12px;
|
||||
|
||||
.section-icon {
|
||||
margin-right: 8px;
|
||||
color: #07c05f;
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.ollama-summary-card) {
|
||||
background: #fff;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
:deep(.submit-section) {
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"),
|
||||
|
||||
Reference in New Issue
Block a user