新增: 设置页 UX 重构(master-detail 左导航 + 搜索 + 即时反馈 + 导入导出)

- SettingsNav 左导航 7 类 + 顶部搜索(索引匹配 label/desc 高亮定位)
- GeneralPanel 拆分 Appearance/Performance/Advanced Section
- SettingRow 统一行 + 即时反馈(已保存)+ 下轮生效 badge
- 统一组件库 settings.css(scoped 重复 class 抽全局)
- 导入导出(默认排除 keyring 密钥)
- activeCategory 持久化 + 窄屏折叠 tab
This commit is contained in:
2026-06-21 20:50:44 +08:00
parent 4b5da38e28
commit 330bb7f505
17 changed files with 1539 additions and 606 deletions

View File

@@ -127,7 +127,7 @@
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue'
import { reactive, ref, onMounted } from 'vue'
import { useI18n } from 'vue-i18n'
import { aiApi } from '@/api'
import { useAiStore } from '@/stores/ai'
@@ -337,39 +337,26 @@ function maskKey(key: string): string {
return key.slice(0, 4) + '••••••••' + key.slice(-4)
}
/** 暴露给 shell:onMounted 拉取 / Knowledge 读 embeddingProviders / 卸载清 timer */
/** 暴露给 shell:loadProviders 供外部主动刷新 / Knowledge 读 aiProviders / 卸载清 timer */
defineExpose({
aiProviders,
loadProviders,
clearPoolTimer() { if (_poolTimer) clearTimeout(_poolTimer) },
})
// 阶段3 UX 重构:本面板改为 ai-model 类懒挂载,不再由 Settings.vue onMounted 调用,
// 改为自身挂载时拉取 provider 列表 + 同步 aiStore。
onMounted(loadProviders)
</script>
<style scoped>
/* ===== 面板容器(原 Settings.vue 抽出,各子组件自持 scoped 副本) ===== */
.panel {
background: var(--df-bg-card);
border: 0.5px solid var(--df-border);
border-radius: var(--df-radius-lg);
padding: var(--df-pad-panel);
}
.panel-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: var(--df-gap-head);
}
.panel-header h2 { font-size: 15px; font-weight: 500; }
.empty-hint { font-size: 13px; color: var(--df-text-dim); padding: 12px 0; }
/* 阶段1/3 UX 重构:panel/panel-header/empty-hint/btn 系列/setting-select/
toggle/toggle-slider 已迁移到 settings.css 全局。
本 scoped 仅保留 ProviderPanel 特有样式:provider-card/default-badge/
pool-badge/detail 系列/form 系列/setting-input/btn-link/toggle--sm 等。
注:注释内勿用斜杠分隔类名列表(会误闭合 CSS 注释)。
/* ===== 按钮 ===== */
.btn { padding: 8px 16px; border: none; border-radius: var(--df-radius-sm); font-size: 13px; cursor: pointer; transition: all 0.15s; }
.btn-primary { background: var(--df-accent); color: #fff; }
.btn-primary:hover { background: var(--df-accent-hover); }
.btn-primary:disabled { opacity: 0.5; cursor: not-allowed; }
.btn-ghost { background: transparent; color: var(--df-text-secondary); border: 0.5px solid var(--df-border); }
.btn-ghost:hover { background: var(--df-bg-card); color: var(--df-text); }
.btn-sm { padding: 4px 12px; font-size: 12px; }
/* ===== 链接按钮(本面板特有,未进全局) ===== */
.btn-link { background: none; border: none; color: var(--df-accent); font-size: 12px; cursor: pointer; padding: 2px 6px; }
.btn-link:hover { text-decoration: underline; }
.btn-link-danger { color: var(--df-danger); }
@@ -434,7 +421,7 @@ defineExpose({
.detail-value--control { display: flex; align-items: center; gap: 8px; font-family: var(--df-font-mono); }
.pool-weight-hint { font-size: 11px; color: var(--df-text-dim); }
/* 表单 */
/* 表单(本面板特有输入控件样式;.setting-select 走全局) */
.form-grid { display: flex; flex-direction: column; gap: 14px; }
.form-field { display: flex; flex-direction: column; gap: 4px; }
.form-label { font-size: 12px; font-weight: 500; color: var(--df-text-secondary); }
@@ -443,16 +430,8 @@ defineExpose({
.setting-input { padding: 6px 12px; background: var(--df-bg); border: 0.5px solid var(--df-border); border-radius: var(--df-radius-sm); color: var(--df-text); font-size: 13px; outline: none; min-width: 260px; }
.setting-input:focus { border-color: var(--df-accent); }
.setting-input--sm { min-width: 0; width: 70px; padding: 4px 8px; font-size: 12px; }
.setting-select { padding: 6px 12px; background: var(--df-bg); border: 0.5px solid var(--df-border); border-radius: var(--df-radius-sm); color: var(--df-text); font-size: 13px; outline: none; cursor: pointer; min-width: 160px; }
.setting-select:focus { border-color: var(--df-accent); }
/* Toggle */
.toggle { position: relative; display: inline-block; width: 40px; height: 22px; cursor: pointer; }
.toggle input { opacity: 0; width: 0; height: 0; }
.toggle-slider { position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: var(--df-border); border-radius: var(--df-radius-lg); transition: background 0.2s; }
.toggle-slider::before { content: ''; position: absolute; width: 16px; height: 16px; left: 3px; bottom: 3px; background: var(--df-text-dim); border-radius: 50%; transition: transform 0.2s, background 0.2s; }
.toggle input:checked + .toggle-slider { background: var(--df-accent); }
.toggle input:checked + .toggle-slider::before { transform: translateX(18px); background: #fff; }
/* Toggle 小号变体(本面板负载均衡池用;.toggle 基础走全局) */
.toggle--sm { width: 32px; height: 18px; }
.toggle--sm .toggle-slider::before { width: 12px; height: 12px; left: 3px; bottom: 3px; }
.toggle--sm input:checked + .toggle-slider::before { transform: translateX(14px); }