feat: add system settings with model connectivity and encrypted storage
This commit is contained in:
@@ -14,8 +14,7 @@
|
||||
:key="section.id"
|
||||
class="settings-nav-item"
|
||||
:class="{
|
||||
active: activeSection === section.id,
|
||||
complete: sectionStatus[section.id]
|
||||
active: activeSection === section.id
|
||||
}"
|
||||
type="button"
|
||||
@click="activateSection(section.id)"
|
||||
@@ -24,17 +23,8 @@
|
||||
<strong>{{ section.label }}</strong>
|
||||
<small>{{ section.desc }}</small>
|
||||
</span>
|
||||
|
||||
<span class="nav-item-state">
|
||||
<i :class="sectionStatus[section.id] ? 'mdi mdi-check' : 'mdi mdi-chevron-right'"></i>
|
||||
</span>
|
||||
</button>
|
||||
</nav>
|
||||
|
||||
<div class="settings-nav-foot">
|
||||
<span>当前环境</span>
|
||||
<strong>{{ pageState.companyForm.environment }}</strong>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<div class="settings-body">
|
||||
@@ -46,11 +36,6 @@
|
||||
</div>
|
||||
|
||||
<div class="settings-toolbar-actions">
|
||||
<span class="section-status" :class="{ complete: sectionStatus[activeSection] }">
|
||||
<i :class="sectionStatus[activeSection] ? 'mdi mdi-check-decagram' : 'mdi mdi-progress-clock'"></i>
|
||||
<span>{{ sectionStatus[activeSection] ? '当前项已就绪' : '当前项待补全' }}</span>
|
||||
</span>
|
||||
|
||||
<button class="save-button" type="button" @click="saveActiveSection">
|
||||
<i class="mdi mdi-content-save-outline"></i>
|
||||
<span>{{ activeSectionConfig.actionLabel }}</span>
|
||||
@@ -64,7 +49,7 @@
|
||||
<div class="card-head">
|
||||
<div>
|
||||
<h4>系统基本信息</h4>
|
||||
<p>统一维护企业名称、显示名称和版权信息,保存后左侧品牌名称会立即同步预览。</p>
|
||||
<p>统一维护企业名称、系统显示名称和版权信息,保存后会同步更新当前系统品牌名称。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -96,15 +81,6 @@
|
||||
<input v-model="pageState.companyForm.recordNumber" type="text" placeholder="请输入备案号" />
|
||||
</label>
|
||||
|
||||
<label class="field">
|
||||
<span>运行环境</span>
|
||||
<select v-model="pageState.companyForm.environment">
|
||||
<option value="生产环境">生产环境</option>
|
||||
<option value="预发布环境">预发布环境</option>
|
||||
<option value="测试环境">测试环境</option>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label class="field field-full">
|
||||
<span><em>*</em> 版权信息</span>
|
||||
<input
|
||||
@@ -115,29 +91,6 @@
|
||||
</label>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="settings-card">
|
||||
<div class="card-head">
|
||||
<div>
|
||||
<h4>品牌预览</h4>
|
||||
<p>用于确认侧边栏品牌、页脚版权和系统入口名称的实际展示效果。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="preview-card">
|
||||
<div class="preview-icon">
|
||||
<i class="mdi mdi-domain"></i>
|
||||
</div>
|
||||
|
||||
<div class="preview-copy">
|
||||
<strong>{{ pageState.companyForm.displayName || '系统显示名称' }}</strong>
|
||||
<p>{{ pageState.companyForm.companyName || '企业法定名称' }}</p>
|
||||
<small>{{ pageState.companyForm.copyright || '版权信息将显示在这里' }}</small>
|
||||
</div>
|
||||
|
||||
<span class="preview-badge">{{ pageState.companyForm.environment }}</span>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<template v-else-if="activeSection === 'admin'">
|
||||
@@ -231,97 +184,203 @@
|
||||
</template>
|
||||
|
||||
<template v-else-if="activeSection === 'llm'">
|
||||
<section class="settings-card">
|
||||
<div class="card-head">
|
||||
<div>
|
||||
<h4>模型接入</h4>
|
||||
<p>配置大语言模型的供应商、模型名称和接入地址,用于 AI 助手与识别流程。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-grid">
|
||||
<label class="field">
|
||||
<span><em>*</em> 供应商</span>
|
||||
<select v-model="pageState.llmForm.provider">
|
||||
<option value="OpenAI Compatible">OpenAI Compatible</option>
|
||||
<option value="Azure OpenAI">Azure OpenAI</option>
|
||||
<option value="Ollama">Ollama</option>
|
||||
<option value="自定义网关">自定义网关</option>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label class="field">
|
||||
<span><em>*</em> 模型名称</span>
|
||||
<input v-model="pageState.llmForm.model" type="text" placeholder="请输入主模型名称" />
|
||||
</label>
|
||||
|
||||
<label class="field field-full">
|
||||
<span><em>*</em> 接口地址</span>
|
||||
<input v-model="pageState.llmForm.endpoint" type="text" placeholder="请输入兼容接口地址" />
|
||||
</label>
|
||||
|
||||
<label class="field">
|
||||
<span>Embedding 模型</span>
|
||||
<input v-model="pageState.llmForm.embeddingModel" type="text" placeholder="请输入向量模型名称" />
|
||||
</label>
|
||||
|
||||
<label class="field">
|
||||
<span>API Key</span>
|
||||
<input v-model="pageState.llmForm.apiKey" type="password" autocomplete="off" placeholder="保存后不会保留在草稿中" />
|
||||
</label>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="settings-card">
|
||||
<div class="card-head">
|
||||
<div>
|
||||
<h4>推理与知识策略</h4>
|
||||
<p>控制响应质量、输出长度以及知识库、引用回溯等增强能力。</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-grid compact-grid">
|
||||
<label class="field">
|
||||
<span>推理模式</span>
|
||||
<select v-model="pageState.llmForm.reasoningMode">
|
||||
<option value="balanced">平衡</option>
|
||||
<option value="quality">优先质量</option>
|
||||
<option value="latency">优先速度</option>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label class="field">
|
||||
<span>最大 Token</span>
|
||||
<input v-model.number="pageState.llmForm.maxTokens" type="number" min="512" step="256" />
|
||||
</label>
|
||||
|
||||
<label class="field field-full">
|
||||
<span>Temperature</span>
|
||||
<div class="range-shell">
|
||||
<input v-model.number="pageState.llmForm.temperature" type="range" min="0" max="1" step="0.1" />
|
||||
<strong>{{ pageState.llmForm.temperature.toFixed(1) }}</strong>
|
||||
<div class="model-grid">
|
||||
<section class="settings-card">
|
||||
<div class="card-head">
|
||||
<div>
|
||||
<h4>主模型配置</h4>
|
||||
<p>用于 AI 助手和主业务链路的默认模型接入。</p>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="card-head-actions">
|
||||
<button class="test-button" type="button" :disabled="isModelTesting('main')" @click="testModelConnection('main')">
|
||||
<i :class="isModelTesting('main') ? 'mdi mdi-loading mdi-spin' : 'mdi mdi-connection'"></i>
|
||||
<span>{{ isModelTesting('main') ? '测试中...' : '测试模型' }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="switch-group">
|
||||
<button class="switch-row" type="button" @click="toggleBoolean('llmForm', 'knowledgeEnabled')">
|
||||
<span class="switch-copy">
|
||||
<strong>启用知识库检索</strong>
|
||||
<small>允许模型在回答时结合制度知识库和业务文档。</small>
|
||||
</span>
|
||||
<span class="switch" :class="{ active: pageState.llmForm.knowledgeEnabled }"><i></i></span>
|
||||
</button>
|
||||
<div class="form-grid">
|
||||
<label class="field">
|
||||
<span><em>*</em> 供应商</span>
|
||||
<select v-model="pageState.llmForm.mainProvider" @change="applyProviderPreset('main')">
|
||||
<option v-for="option in providerOptions" :key="option" :value="option">{{ option }}</option>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<button class="switch-row" type="button" @click="toggleBoolean('llmForm', 'citationEnabled')">
|
||||
<span class="switch-copy">
|
||||
<strong>输出引用来源</strong>
|
||||
<small>在 AI 助手回答中附带依据与来源提示。</small>
|
||||
</span>
|
||||
<span class="switch" :class="{ active: pageState.llmForm.citationEnabled }"><i></i></span>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
<label class="field">
|
||||
<span><em>*</em> 模型名称</span>
|
||||
<input v-model="pageState.llmForm.mainModel" type="text" placeholder="请输入主模型名称" />
|
||||
</label>
|
||||
|
||||
<label class="field field-full">
|
||||
<span><em>*</em> 接口地址</span>
|
||||
<input v-model="pageState.llmForm.mainEndpoint" type="text" placeholder="请输入模型接口地址" />
|
||||
</label>
|
||||
|
||||
<label class="field field-full">
|
||||
<span>API Key</span>
|
||||
<input
|
||||
v-model="pageState.llmForm.mainApiKey"
|
||||
type="password"
|
||||
autocomplete="off"
|
||||
:placeholder="pageState.llmForm.mainApiKeyConfigured ? '已配置,如需修改请重新输入' : '保存后不会保留在草稿中'"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div v-if="getModelTestState('main').message" class="test-feedback" :class="`is-${getModelTestState('main').status}`">
|
||||
<i :class="getModelTestState('main').status === 'success' ? 'mdi mdi-check-circle' : getModelTestState('main').status === 'testing' ? 'mdi mdi-loading mdi-spin' : 'mdi mdi-alert-circle'"></i>
|
||||
<span>{{ getModelTestState('main').message }}</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="settings-card">
|
||||
<div class="card-head">
|
||||
<div>
|
||||
<h4>备份模型配置</h4>
|
||||
<p>主模型不可用时用于兜底切换的备用模型接入。</p>
|
||||
</div>
|
||||
<div class="card-head-actions">
|
||||
<button class="test-button" type="button" :disabled="isModelTesting('backup')" @click="testModelConnection('backup')">
|
||||
<i :class="isModelTesting('backup') ? 'mdi mdi-loading mdi-spin' : 'mdi mdi-connection'"></i>
|
||||
<span>{{ isModelTesting('backup') ? '测试中...' : '测试模型' }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-grid">
|
||||
<label class="field">
|
||||
<span><em>*</em> 供应商</span>
|
||||
<select v-model="pageState.llmForm.backupProvider" @change="applyProviderPreset('backup')">
|
||||
<option v-for="option in providerOptions" :key="option" :value="option">{{ option }}</option>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label class="field">
|
||||
<span><em>*</em> 模型名称</span>
|
||||
<input v-model="pageState.llmForm.backupModel" type="text" placeholder="请输入备份模型名称" />
|
||||
</label>
|
||||
|
||||
<label class="field field-full">
|
||||
<span><em>*</em> 接口地址</span>
|
||||
<input v-model="pageState.llmForm.backupEndpoint" type="text" placeholder="请输入模型接口地址" />
|
||||
</label>
|
||||
|
||||
<label class="field field-full">
|
||||
<span>API Key</span>
|
||||
<input
|
||||
v-model="pageState.llmForm.backupApiKey"
|
||||
type="password"
|
||||
autocomplete="off"
|
||||
:placeholder="pageState.llmForm.backupApiKeyConfigured ? '已配置,如需修改请重新输入' : '保存后不会保留在草稿中'"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div v-if="getModelTestState('backup').message" class="test-feedback" :class="`is-${getModelTestState('backup').status}`">
|
||||
<i :class="getModelTestState('backup').status === 'success' ? 'mdi mdi-check-circle' : getModelTestState('backup').status === 'testing' ? 'mdi mdi-loading mdi-spin' : 'mdi mdi-alert-circle'"></i>
|
||||
<span>{{ getModelTestState('backup').message }}</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="settings-card">
|
||||
<div class="card-head">
|
||||
<div>
|
||||
<h4>VLM 模型设置</h4>
|
||||
<p>用于票据、图像等多模态识别场景的视觉语言模型配置。</p>
|
||||
</div>
|
||||
<div class="card-head-actions">
|
||||
<button class="test-button" type="button" :disabled="isModelTesting('vlm')" @click="testModelConnection('vlm')">
|
||||
<i :class="isModelTesting('vlm') ? 'mdi mdi-loading mdi-spin' : 'mdi mdi-connection'"></i>
|
||||
<span>{{ isModelTesting('vlm') ? '测试中...' : '测试模型' }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-grid">
|
||||
<label class="field">
|
||||
<span><em>*</em> 供应商</span>
|
||||
<select v-model="pageState.llmForm.vlmProvider" @change="applyProviderPreset('vlm')">
|
||||
<option v-for="option in providerOptions" :key="option" :value="option">{{ option }}</option>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label class="field">
|
||||
<span><em>*</em> 模型名称</span>
|
||||
<input v-model="pageState.llmForm.vlmModel" type="text" placeholder="请输入 VLM 模型名称" />
|
||||
</label>
|
||||
|
||||
<label class="field field-full">
|
||||
<span><em>*</em> 接口地址</span>
|
||||
<input v-model="pageState.llmForm.vlmEndpoint" type="text" placeholder="请输入模型接口地址" />
|
||||
</label>
|
||||
|
||||
<label class="field field-full">
|
||||
<span>API Key</span>
|
||||
<input
|
||||
v-model="pageState.llmForm.vlmApiKey"
|
||||
type="password"
|
||||
autocomplete="off"
|
||||
:placeholder="pageState.llmForm.vlmApiKeyConfigured ? '已配置,如需修改请重新输入' : '保存后不会保留在草稿中'"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div v-if="getModelTestState('vlm').message" class="test-feedback" :class="`is-${getModelTestState('vlm').status}`">
|
||||
<i :class="getModelTestState('vlm').status === 'success' ? 'mdi mdi-check-circle' : getModelTestState('vlm').status === 'testing' ? 'mdi mdi-loading mdi-spin' : 'mdi mdi-alert-circle'"></i>
|
||||
<span>{{ getModelTestState('vlm').message }}</span>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="settings-card">
|
||||
<div class="card-head">
|
||||
<div>
|
||||
<h4>Embedding 模型配置</h4>
|
||||
<p>用于向量检索、知识库召回和语义匹配的嵌入模型设置。</p>
|
||||
</div>
|
||||
<div class="card-head-actions">
|
||||
<button class="test-button" type="button" :disabled="isModelTesting('embedding')" @click="testModelConnection('embedding')">
|
||||
<i :class="isModelTesting('embedding') ? 'mdi mdi-loading mdi-spin' : 'mdi mdi-connection'"></i>
|
||||
<span>{{ isModelTesting('embedding') ? '测试中...' : '测试模型' }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-grid">
|
||||
<label class="field">
|
||||
<span><em>*</em> 供应商</span>
|
||||
<select v-model="pageState.llmForm.embeddingProvider" @change="applyProviderPreset('embedding')">
|
||||
<option v-for="option in providerOptions" :key="option" :value="option">{{ option }}</option>
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label class="field">
|
||||
<span><em>*</em> 模型名称</span>
|
||||
<input v-model="pageState.llmForm.embeddingModel" type="text" placeholder="请输入 Embedding 模型名称" />
|
||||
</label>
|
||||
|
||||
<label class="field field-full">
|
||||
<span><em>*</em> 接口地址</span>
|
||||
<input v-model="pageState.llmForm.embeddingEndpoint" type="text" placeholder="请输入模型接口地址" />
|
||||
</label>
|
||||
|
||||
<label class="field field-full">
|
||||
<span>API Key</span>
|
||||
<input
|
||||
v-model="pageState.llmForm.embeddingApiKey"
|
||||
type="password"
|
||||
autocomplete="off"
|
||||
:placeholder="pageState.llmForm.embeddingApiKeyConfigured ? '已配置,如需修改请重新输入' : '保存后不会保留在草稿中'"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div
|
||||
v-if="getModelTestState('embedding').message"
|
||||
class="test-feedback"
|
||||
:class="`is-${getModelTestState('embedding').status}`"
|
||||
>
|
||||
<i :class="getModelTestState('embedding').status === 'success' ? 'mdi mdi-check-circle' : getModelTestState('embedding').status === 'testing' ? 'mdi mdi-loading mdi-spin' : 'mdi mdi-alert-circle'"></i>
|
||||
<span>{{ getModelTestState('embedding').message }}</span>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-else-if="activeSection === 'logs'">
|
||||
@@ -455,7 +514,12 @@
|
||||
|
||||
<label class="field field-full">
|
||||
<span>SMTP 密码</span>
|
||||
<input v-model="pageState.mailForm.password" type="password" autocomplete="off" placeholder="保存后不会保留在草稿中" />
|
||||
<input
|
||||
v-model="pageState.mailForm.password"
|
||||
type="password"
|
||||
autocomplete="off"
|
||||
:placeholder="pageState.mailForm.passwordConfigured ? '已配置,如需修改请重新输入' : '保存后不会保留在草稿中'"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user