feat: 优化设置面板和模型提供者组件的布局与样式

This commit is contained in:
Wenjie Zhang 2026-01-15 05:33:24 +08:00
parent 62685b7ffe
commit 673a7fdd75
3 changed files with 126 additions and 190 deletions

View File

@ -1,42 +1,56 @@
<template>
<div class="basic-settings-section">
<h3 class="section-title">检索配置</h3>
<div class="section">
<div class="card card-select">
<span class="label">{{ items?.default_model?.des || '默认对话模型' }}</span>
<ModelSelectorComponent
@select-model="handleChatModelSelect"
:model_spec="configStore.config?.default_model"
placeholder="请选择默认模型"
/>
<div class="settings-panel">
<div class="setting-row two-cols">
<div class="col-item">
<div class="setting-label">{{ items?.default_model?.des || '默认对话模型' }}</div>
<div class="setting-content">
<ModelSelectorComponent
@select-model="handleChatModelSelect"
:model_spec="configStore.config?.default_model"
placeholder="请选择默认模型"
/>
</div>
</div>
<div class="col-item">
<div class="setting-label">{{ items?.fast_model.des }}</div>
<div class="setting-content">
<ModelSelectorComponent
@select-model="handleFastModelSelect"
:model_spec="configStore.config?.fast_model"
placeholder="请选择模型"
/>
</div>
</div>
</div>
<div class="card card-select">
<span class="label">{{ items?.fast_model.des }}</span>
<ModelSelectorComponent
@select-model="handleFastModelSelect"
:model_spec="configStore.config?.fast_model"
placeholder="请选择模型"
/>
</div>
<div class="card card-select">
<span class="label">{{ items?.embed_model.des }}</span>
<EmbeddingModelSelector
:value="configStore.config?.embed_model"
@change="handleChange('embed_model', $event)"
style="width: 320px"
/>
</div>
<div class="card card-select">
<span class="label">{{ items?.reranker.des }}</span>
<a-select style="width: 320px"
:value="configStore.config?.reranker"
@change="handleChange('reranker', $event)"
>
<a-select-option
v-for="(name, idx) in rerankerChoices" :key="idx"
:value="name">{{ name }}
</a-select-option>
</a-select>
<div class="setting-row two-cols">
<div class="col-item">
<div class="setting-label">{{ items?.embed_model.des }}</div>
<div class="setting-content">
<EmbeddingModelSelector
:value="configStore.config?.embed_model"
@change="handleChange('embed_model', $event)"
style="width: 100%"
/>
</div>
</div>
<div class="col-item">
<div class="setting-label">{{ items?.reranker.des }}</div>
<div class="setting-content">
<a-select
class="full-width"
:value="configStore.config?.reranker"
@change="handleChange('reranker', $event)"
placeholder="请选择重排序模型"
>
<a-select-option
v-for="(name, idx) in rerankerChoices" :key="idx"
:value="name">{{ name }}
</a-select-option>
</a-select>
</div>
</div>
</div>
</div>
@ -195,7 +209,7 @@ const openLink = (url) => {
color: var(--gray-900);
font-size: 16px;
font-weight: 600;
margin: 24px 0 12px 0;
margin: 24px 0 0 0;
padding-bottom: 8px;
&:first-child {
@ -211,7 +225,6 @@ const openLink = (url) => {
}
.section {
margin-top: 10px;
background-color: var(--gray-0);
padding: 10px 16px;
border-radius: 8px;
@ -221,6 +234,49 @@ const openLink = (url) => {
border: 1px solid var(--gray-150);
}
.settings-panel {
background-color: var(--gray-50);
border: 1px solid var(--gray-200);
border-radius: 8px;
padding: 16px;
display: flex;
flex-direction: column;
gap: 16px;
}
.setting-row {
display: flex;
flex-direction: column;
gap: 8px;
&.two-cols {
flex-direction: row;
gap: 20px;
}
.col-item {
flex: 1;
display: flex;
flex-direction: column;
gap: 6px;
min-width: 0;
}
}
.setting-label {
font-size: 13px;
font-weight: 500;
color: var(--gray-700);
}
.setting-content {
width: 100%;
.full-width {
width: 100%;
}
}
.card {
display: flex;
align-items: center;
@ -288,79 +344,4 @@ const openLink = (url) => {
}
}
}
//
@media (max-width: 768px) {
.basic-settings-section {
.section {
padding: 12px;
}
.card {
flex-direction: column;
align-items: flex-start;
gap: 12px;
padding: 16px 0;
.label {
margin-right: 0;
margin-bottom: 8px;
}
&.card-select {
gap: 0px;
padding: 0;
.label {
margin-top: 0;
}
}
}
.services-grid {
grid-template-columns: 1fr;
gap: 12px;
}
.service-link-card {
flex-direction: column;
align-items: flex-start;
gap: 12px;
min-height: auto;
padding: 12px;
.service-info {
margin-right: 0;
margin-bottom: 8px;
}
}
}
}
//
@media (max-width: 480px) {
.basic-settings-section {
.section-title {
font-size: 15px;
}
.card {
.label {
font-size: 14px;
min-width: 120px;
}
}
.service-link-card {
.service-info {
h4 {
font-size: 14px;
}
p {
font-size: 12px;
}
}
}
}
}
</style>

View File

@ -103,19 +103,15 @@
</div>
<div class="model-title-container">
<h3>{{ modelNames[item].name }}</h3>
<div class="provider-meta">
<span class="model-count">{{ modelNames[item].models?.length || 0 }} 个模型</span>
<span class="provider-id">{{ item }}</span>
</div>
</div>
<div class="provider-actions">
<div class="provider-meta">
<a-button
type="text"
class="expand-button"
@click.stop="openProviderConfig(item)"
title="配置模型"
>
<SettingOutlined />
<SettingOutlined /> 已选 {{ modelNames[item].models?.length || 0 }} 个模型
</a-button>
</div>
</div>
@ -798,7 +794,7 @@ const testCustomProvider = async (providerId, modelName) => {
display: flex;
justify-content: space-between;
align-items: center;
padding: 16px 20px;
padding: 8px 12px;
background: var(--gray-25);
border-bottom: 1px solid var(--gray-200);
@ -830,17 +826,16 @@ const testCustomProvider = async (providerId, modelName) => {
}
.card-content {
padding: 16px 20px;
padding: 8px 12px;
.provider-details {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 12px;
gap: 12px 8px;
.detail-item {
display: flex;
flex-direction: column;
gap: 4px;
.label {
font-size: 12px;
@ -929,13 +924,6 @@ const testCustomProvider = async (providerId, modelName) => {
.model-icon {
filter: grayscale(0%);
}
.provider-meta {
.model-count {
color: var(--gray-600);
font-weight: 500;
}
}
}
// provider
@ -957,9 +945,9 @@ const testCustomProvider = async (providerId, modelName) => {
.card-header {
display: flex;
align-items: center;
gap: 12px;
gap: 10px;
cursor: pointer;
padding: 8px 16px;
padding: 6px 10px;
background: var(--gray-0);
.model-title-container {
@ -973,28 +961,6 @@ const testCustomProvider = async (providerId, modelName) => {
font-weight: 600;
color: var(--gray-900);
}
.provider-meta {
display: flex;
align-items: center;
gap: 8px;
font-size: 12px;
.model-count {
color: var(--gray-600);
font-weight: 500;
}
.provider-id {
background: var(--gray-50);
color: var(--gray-900);
padding: 2px 6px;
border-radius: 4px;
font-size: 11px;
font-weight: 500;
font-family: 'Courier New', monospace;
}
}
}
.model-url {
@ -1009,21 +975,21 @@ const testCustomProvider = async (providerId, modelName) => {
}
.model-icon {
width: 36px;
height: 36px;
width: 32px;
height: 32px;
border-radius: 6px;
overflow: hidden;
filter: grayscale(100%);
flex-shrink: 0;
background-color: white;
border: 1px solid var(--gray-200);
// border: 1px solid var(--gray-200);
img {
width: 100%;
height: 100%;
object-fit: contain;
padding: 4px;
border-radius: 6px;
border-radius: 8px;
}
&.available {
@ -1032,16 +998,16 @@ const testCustomProvider = async (providerId, modelName) => {
}
.expand-button, .config-button {
height: 32px;
width: 32px;
height: 28px;
display: flex;
align-items: center;
justify-content: center;
padding: 0;
padding: 4px 6px;
cursor: pointer;
color: var(--gray-500);
color: var(--gray-800);
border-radius: 6px;
transition: all 0.2s ease;
font-size: 12px;
&:hover {
background-color: var(--gray-50);
@ -1093,21 +1059,22 @@ const testCustomProvider = async (providerId, modelName) => {
.card-body {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 12px;
padding: 16px;
gap: 8px;
padding: 10px;
padding-top: 4px;
//
.card-models {
width: 100%;
border-radius: 6px;
border: 1px solid var(--gray-150);
padding: 12px 16px;
padding: 8px 12px;
display: flex;
justify-content: space-between;
align-items: center;
box-sizing: border-box;
background: var(--gray-0);
min-height: 48px;
// min-height: 48px;
.model_name {
font-size: 14px;
@ -1156,7 +1123,7 @@ const testCustomProvider = async (providerId, modelName) => {
.modal-models-section {
.model-search {
margin-bottom: 10px;
padding: 0 6px;
padding: 0;
.ant-input-affix-wrapper {
border-radius: 6px;
@ -1177,6 +1144,7 @@ const testCustomProvider = async (providerId, modelName) => {
.modal-checkbox-item {
display: inline-block;
margin-bottom: 4px;
margin-right: 4px;
padding: 4px 6px;
border-radius: 6px;
background-color: var(--gray-0);
@ -1202,24 +1170,6 @@ const testCustomProvider = async (providerId, modelName) => {
}
}
//
@media (max-width: 768px) {
.model-provider-card {
margin-bottom: 10px;
.card-body {
grid-template-columns: 1fr;
gap: 8px;
padding: 10px;
}
.card-header {
padding: 10px;
gap: 8px;
}
}
}
// Simple Notice Styles
.simple-notice {
padding: 8px 12px;

View File

@ -182,8 +182,9 @@ watch(() => props.visible, (newVal) => {
display: flex;
height: 100%;
width: 100%;
gap: 6px;
@media (max-width: 768px) {
@media (max-width: 900px) {
flex-direction: column;
}
}
@ -199,7 +200,7 @@ watch(() => props.visible, (newVal) => {
gap: 8px;
flex-shrink: 0;
@media (max-width: 768px) {
@media (max-width: 900px) {
display: none;
}
@ -236,15 +237,19 @@ watch(() => props.visible, (newVal) => {
.settings-content-wrapper {
flex: 1;
height: 100%;
/* background-color: #fff; SettingView seems to use default background */
max-width: calc(100% - 128px);
@media (max-width: 900px) {
max-width: 100%;
}
.settings-content {
padding: 0 20px; /* Matches SettingView .setting padding */
padding: 0; /* Matches SettingView .setting padding */
// margin-bottom: 40px; /* Matches SettingView .setting margin-bottom */
overflow-y: scroll;
height: 70vh;
@media (max-width: 768px) {
@media (max-width: 900px) {
height: 70vh;
padding: 0px;
}
@ -268,7 +273,7 @@ watch(() => props.visible, (newVal) => {
padding: 0;
flex-shrink: 0;
@media (max-width: 768px) {
@media (max-width: 900px) {
display: flex;
}