From 330bb7f5050dbe963313e9c79a7927799869b7c1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=BB=9D=E5=B0=98?= <237809796@qq.com>
Date: Sun, 21 Jun 2026 20:50:44 +0800
Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E:=20=E8=AE=BE=E7=BD=AE?=
=?UTF-8?q?=E9=A1=B5=20UX=20=E9=87=8D=E6=9E=84(master-detail=20=E5=B7=A6?=
=?UTF-8?q?=E5=AF=BC=E8=88=AA=20+=20=E6=90=9C=E7=B4=A2=20+=20=E5=8D=B3?=
=?UTF-8?q?=E6=97=B6=E5=8F=8D=E9=A6=88=20+=20=E5=AF=BC=E5=85=A5=E5=AF=BC?=
=?UTF-8?q?=E5=87=BA)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- SettingsNav 左导航 7 类 + 顶部搜索(索引匹配 label/desc 高亮定位)
- GeneralPanel 拆分 Appearance/Performance/Advanced Section
- SettingRow 统一行 + 即时反馈(已保存)+ 下轮生效 badge
- 统一组件库 settings.css(scoped 重复 class 抽全局)
- 导入导出(默认排除 keyring 密钥)
- activeCategory 持久化 + 窄屏折叠 tab
---
src/components/settings/AdvancedSection.vue | 54 +++
src/components/settings/AllowedDirsPanel.vue | 37 +-
src/components/settings/AppearanceSection.vue | 122 +++++++
src/components/settings/ConnectionPanel.vue | 59 ++--
src/components/settings/GeneralPanel.vue | 329 +-----------------
src/components/settings/KnowledgePanel.vue | 202 ++++-------
.../settings/PerformanceSection.vue | 231 ++++++++++++
src/components/settings/ProviderPanel.vue | 49 +--
src/components/settings/SettingRow.vue | 84 +++++
.../settings/SettingsImportExport.vue | 233 +++++++++++++
src/components/settings/SettingsNav.vue | 276 +++++++++++++++
src/components/settings/searchIndex.ts | 88 +++++
src/components/settings/settings.css | 69 ++++
src/i18n/en/settings.ts | 44 ++-
src/i18n/zh-CN/settings.ts | 44 ++-
src/main.ts | 1 +
src/views/Settings.vue | 223 +++++++++---
17 files changed, 1539 insertions(+), 606 deletions(-)
create mode 100644 src/components/settings/AdvancedSection.vue
create mode 100644 src/components/settings/AppearanceSection.vue
create mode 100644 src/components/settings/PerformanceSection.vue
create mode 100644 src/components/settings/SettingRow.vue
create mode 100644 src/components/settings/SettingsImportExport.vue
create mode 100644 src/components/settings/SettingsNav.vue
create mode 100644 src/components/settings/searchIndex.ts
create mode 100644 src/components/settings/settings.css
diff --git a/src/components/settings/AdvancedSection.vue b/src/components/settings/AdvancedSection.vue
new file mode 100644
index 0000000..70842ba
--- /dev/null
+++ b/src/components/settings/AdvancedSection.vue
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
diff --git a/src/components/settings/AllowedDirsPanel.vue b/src/components/settings/AllowedDirsPanel.vue
index 3d93198..e82bc51 100644
--- a/src/components/settings/AllowedDirsPanel.vue
+++ b/src/components/settings/AllowedDirsPanel.vue
@@ -142,20 +142,10 @@ onMounted(load)
diff --git a/src/components/settings/AppearanceSection.vue b/src/components/settings/AppearanceSection.vue
new file mode 100644
index 0000000..235a796
--- /dev/null
+++ b/src/components/settings/AppearanceSection.vue
@@ -0,0 +1,122 @@
+
+
+
+
+
+
+
+
diff --git a/src/components/settings/ConnectionPanel.vue b/src/components/settings/ConnectionPanel.vue
index 168740d..742831d 100644
--- a/src/components/settings/ConnectionPanel.vue
+++ b/src/components/settings/ConnectionPanel.vue
@@ -2,7 +2,10 @@
@@ -67,7 +70,7 @@
diff --git a/src/components/settings/GeneralPanel.vue b/src/components/settings/GeneralPanel.vue
index dbe1421..98addfc 100644
--- a/src/components/settings/GeneralPanel.vue
+++ b/src/components/settings/GeneralPanel.vue
@@ -1,325 +1,20 @@
-
-
-
-
-
-
- {{ $t('settings.labelTheme') }}
- {{ $t('settings.descTheme') }}
-
-
-
-
-
-
-
- {{ $t('settings.labelLanguage') }}
- {{ $t('settings.descLanguage') }}
-
-
-
-
-
-
-
- {{ $t('settings.labelAiLanguage') }}
- {{ $t('settings.descAiLanguage') }}
-
-
-
-
-
-
-
- {{ $t('settings.labelAutoExecute') }}
- {{ $t('settings.descAutoExecute') }}
-
-
-
-
-
-
-
- {{ $t('settings.labelLogLevel') }}
- {{ $t('settings.descLogLevel') }}
-
-
-
-
-
-
-
- {{ $t('settings.labelShowTokenUsage') }}
- {{ $t('settings.descShowTokenUsage') }}
-
-
-
-
-
-
-
- {{ $t('settings.labelGlobalConcurrency') }}
- {{ $t('settings.descGlobalConcurrency') }}
-
-
-
-
-
-
-
- {{ $t('settings.labelPerConvConcurrency') }}
- {{ $t('settings.descPerConvConcurrency') }}
-
-
-
-
-
-
-
- {{ $t('settings.labelAgentMaxIterations') }}
- {{ $t('settings.descAgentMaxIterations') }}
-
-
-
-
-
-
-
- {{ $t('settings.labelAgentMaxRetries') }}
- {{ $t('settings.descAgentMaxRetries') }}
-
-
-
-
-
-
-
+
+
+
+
diff --git a/src/components/settings/KnowledgePanel.vue b/src/components/settings/KnowledgePanel.vue
index a8b6792..59b96ee 100644
--- a/src/components/settings/KnowledgePanel.vue
+++ b/src/components/settings/KnowledgePanel.vue
@@ -4,117 +4,67 @@
{{ $t('settings.panelKnowledge') }}
-
-
- {{ $t('settings.labelAutoExtract') }}
- {{ $t('settings.descAutoExtract') }}
-
-
-
-
-
-
-
- {{ $t('settings.labelTriggerMode') }}
- {{ $t('settings.descTriggerMode') }}
-
-
-
-
-
-
-
- {{ $t('settings.labelMinMessages') }}
- {{ $t('settings.descMinMessages') }}
-
-
-
-
-
-
-
- {{ $t('settings.labelIdleTimeout') }}
- {{ $t('settings.descIdleTimeout') }}
-
-
-
-
-
-
-
- {{ $t('settings.labelAutoInject') }}
- {{ $t('settings.descAutoInject') }}
-
-
-
-
-
-
-
- {{ $t('settings.labelVectorEnabled') }}
- {{ $t('settings.descVectorEnabled') }}
-
-
-
-
-
-
-
- {{ $t('settings.labelEmbeddingProvider') }}
- {{ $t('settings.descEmbeddingProvider') }}
-
-
-
-
-
-
-
- {{ $t('settings.labelEmbeddingModel') }}
- {{ $t('settings.descEmbeddingModel') }}
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/settings/PerformanceSection.vue b/src/components/settings/PerformanceSection.vue
new file mode 100644
index 0000000..efd1111
--- /dev/null
+++ b/src/components/settings/PerformanceSection.vue
@@ -0,0 +1,231 @@
+
+
+
+
+
+
+
+
diff --git a/src/components/settings/ProviderPanel.vue b/src/components/settings/ProviderPanel.vue
index 6772765..61470fb 100644
--- a/src/components/settings/ProviderPanel.vue
+++ b/src/components/settings/ProviderPanel.vue
@@ -127,7 +127,7 @@
diff --git a/src/components/settings/SettingsImportExport.vue b/src/components/settings/SettingsImportExport.vue
new file mode 100644
index 0000000..06325a7
--- /dev/null
+++ b/src/components/settings/SettingsImportExport.vue
@@ -0,0 +1,233 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/settings/SettingsNav.vue b/src/components/settings/SettingsNav.vue
new file mode 100644
index 0000000..d09dcaf
--- /dev/null
+++ b/src/components/settings/SettingsNav.vue
@@ -0,0 +1,276 @@
+
+
+
+
+
+
+
+
diff --git a/src/components/settings/searchIndex.ts b/src/components/settings/searchIndex.ts
new file mode 100644
index 0000000..6ff9c8b
--- /dev/null
+++ b/src/components/settings/searchIndex.ts
@@ -0,0 +1,88 @@
+// ============================================================
+// 设置项搜索索引(阶段4 UX 重构)
+// ------------------------------------------------------------
+// 静态表:覆盖全部设置项,每项绑定 i18n label/desc key(运行时按当前 locale
+// 翻译成文本做匹配)。新增设置项时同步此索引,避免索引与设置项漂移(对齐 plan
+// 阶段4 风险项)。分类 value 与 SettingsNav SettingsCategory 一致。
+//
+// 匹配维度:label + desc(当前 locale 翻译值),小写 includes 模糊匹配。
+// 搜索命中 → 左导航高亮匹配类 + 选中首匹配类 + 右侧滚动到对应 setting-row
+// (靠 setting-row 上的 data-setting-key 属性定位,见 SettingsNav + Settings.vue)。
+// ============================================================
+
+/** 分类值类型 — 与 Settings.vue 渲染分支 + SettingsNav categories 一致。
+ * 定义在此处(纯 .ts,无循环导入),SettingsNav.vue re-export 以兼容现有引用。 */
+export type SettingsCategory =
+ | 'appearance'
+ | 'ai-model'
+ | 'knowledge'
+ | 'performance'
+ | 'security'
+ | 'advanced'
+
+/** 搜索索引项:category 所属分类 + labelKey/descKey(i18n key) + key 唯一标识 */
+export interface SettingIndexEntry {
+ /** 所属分类(与左导航 SettingsCategory 一致) */
+ category: SettingsCategory
+ /** 设置项 label 的 i18n key(如 'settings.labelTheme') */
+ labelKey: string
+ /** 设置项 desc 的 i18n key(可空,部分项仅有 label) */
+ descKey?: string
+ /** 唯一标识,对应右侧 setting-row 上 data-setting-key(用于 scrollIntoView 定位) */
+ key: string
+}
+
+/**
+ * 设置项静态索引 —— 覆盖全部 6 类设置项。
+ * 分类映射(对齐 plan):
+ * appearance 外观(theme/language/aiLanguage/showTokenUsage)
+ * ai-model AI 模型(provider 列表 / 负载均衡池 / 模型拉取)
+ * knowledge 知识库(auto_extract/trigger_mode/min_messages/auto_inject/
+ * vector_enabled/embedding_provider/embedding_model)
+ * performance 性能(global-concurrency/per-conv-concurrency/
+ * max-iterations/max-retries)
+ * security 安全(allowed_dirs)
+ * advanced 高级(autoExecute/logLevel + 连接)
+ */
+export const SETTINGS_INDEX: SettingIndexEntry[] = [
+ // ===== 外观 =====
+ { category: 'appearance', key: 'theme', labelKey: 'settings.labelTheme', descKey: 'settings.descTheme' },
+ { category: 'appearance', key: 'language', labelKey: 'settings.labelLanguage', descKey: 'settings.descLanguage' },
+ { category: 'appearance', key: 'aiLanguage', labelKey: 'settings.labelAiLanguage', descKey: 'settings.descAiLanguage' },
+ { category: 'appearance', key: 'showTokenUsage', labelKey: 'settings.labelShowTokenUsage', descKey: 'settings.descShowTokenUsage' },
+ // 加固(索引漂移):streamingMd 在 AppearanceSection.vue 渲染(AR-1 流式 Markdown 渲染开关),
+ // 此前漏入索引 → 搜索"流式/markdown/渲染"无法命中该项。补入 appearance 类与渲染处对齐。
+ { category: 'appearance', key: 'streamingMd', labelKey: 'settings.labelStreamingMd', descKey: 'settings.descStreamingMd' },
+
+ // ===== AI 模型(provider 卡片级设置在 ProviderPanel 动态渲染,此处索引面板标题 +
+ // 负载均衡池/模型拉取等关键交互项,i18n 文案可被关键词命中) =====
+ { category: 'ai-model', key: 'provider', labelKey: 'settings.panelAiProvider' },
+ { category: 'ai-model', key: 'addProvider', labelKey: 'settings.addProvider' },
+ { category: 'ai-model', key: 'poolEnabled', labelKey: 'settings.detailPoolEnabled', descKey: 'settings.descPoolEnabled' },
+ { category: 'ai-model', key: 'poolWeight', labelKey: 'settings.detailPoolWeight', descKey: 'settings.descPoolWeight' },
+ { category: 'ai-model', key: 'testConnection', labelKey: 'settings.testConnection' },
+ { category: 'ai-model', key: 'modelList', labelKey: 'settings.modelListTitle' },
+
+ // ===== 知识库 =====
+ { category: 'knowledge', key: 'autoExtract', labelKey: 'settings.labelAutoExtract', descKey: 'settings.descAutoExtract' },
+ { category: 'knowledge', key: 'triggerMode', labelKey: 'settings.labelTriggerMode', descKey: 'settings.descTriggerMode' },
+ { category: 'knowledge', key: 'minMessages', labelKey: 'settings.labelMinMessages', descKey: 'settings.descMinMessages' },
+ { category: 'knowledge', key: 'autoInject', labelKey: 'settings.labelAutoInject', descKey: 'settings.descAutoInject' },
+ { category: 'knowledge', key: 'vectorEnabled', labelKey: 'settings.labelVectorEnabled', descKey: 'settings.descVectorEnabled' },
+ { category: 'knowledge', key: 'embeddingProvider', labelKey: 'settings.labelEmbeddingProvider', descKey: 'settings.descEmbeddingProvider' },
+ { category: 'knowledge', key: 'embeddingModel', labelKey: 'settings.labelEmbeddingModel', descKey: 'settings.descEmbeddingModel' },
+
+ // ===== 性能 =====
+ { category: 'performance', key: 'globalConcurrency', labelKey: 'settings.labelGlobalConcurrency', descKey: 'settings.descGlobalConcurrency' },
+ { category: 'performance', key: 'perConvConcurrency', labelKey: 'settings.labelPerConvConcurrency', descKey: 'settings.descPerConvConcurrency' },
+ { category: 'performance', key: 'agentMaxIterations', labelKey: 'settings.labelAgentMaxIterations', descKey: 'settings.descAgentMaxIterations' },
+ { category: 'performance', key: 'agentMaxRetries', labelKey: 'settings.labelAgentMaxRetries', descKey: 'settings.descAgentMaxRetries' },
+
+ // ===== 安全 =====
+ { category: 'security', key: 'allowedDirs', labelKey: 'settings.panelAllowedDirs', descKey: 'settings.descAllowedDirs' },
+
+ // ===== 高级 =====
+ { category: 'advanced', key: 'autoExecute', labelKey: 'settings.labelAutoExecute', descKey: 'settings.descAutoExecute' },
+ { category: 'advanced', key: 'logLevel', labelKey: 'settings.labelLogLevel', descKey: 'settings.descLogLevel' },
+ { category: 'advanced', key: 'connection', labelKey: 'settings.panelConnection', descKey: 'settings.emptyConnection' },
+]
diff --git a/src/components/settings/settings.css b/src/components/settings/settings.css
new file mode 100644
index 0000000..62008d5
--- /dev/null
+++ b/src/components/settings/settings.css
@@ -0,0 +1,69 @@
+/* DevFlow 设置统一组件库(UX 重构阶段1)
+ 各 Panel scoped 副本抽取到全局,消除 panel/setting 系列/toggle/btn 5/5+3/5 重复。
+ 来源基准:GeneralPanel/KnowledgePanel scoped(已确认 CSS 一致)。
+ 各 Panel scoped 仅保留特有样式:provider-card/dir-row/connection-card 等。 */
+
+/* ===== 面板容器(5/5 Panel 重复) ===== */
+.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; }
+
+/* ===== 设置行容器(表单型 Panel 共用) ===== */
+.general-settings { display: flex; flex-direction: column; }
+.setting-row {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 14px 0;
+ border-bottom: 0.5px solid var(--df-border);
+}
+.setting-row:last-child { border-bottom: none; }
+.setting-info { display: flex; flex-direction: column; gap: 2px; }
+.setting-label { font-size: 14px; font-weight: 500; color: var(--df-text); }
+.setting-desc { font-size: 12px; color: var(--df-text-dim); }
+.setting-control { display: flex; align-items: center; }
+
+/* ===== 控件(5/5 Panel 重复) ===== */
+.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(3/5 Panel 重复) */
+.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; }
+
+/* ===== 按钮(原 Settings.vue,列表型 Panel 共用) ===== */
+.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-ghost:disabled { opacity: 0.5; cursor: not-allowed; }
+.btn-sm { padding: 4px 12px; font-size: 12px; }
+
+/* ===== 空提示(列表型 Panel 共用) ===== */
+.empty-hint { color: var(--df-text-dim); font-size: 13px; padding: 16px 0; text-align: center; }
diff --git a/src/i18n/en/settings.ts b/src/i18n/en/settings.ts
index 315ff9c..bb8584d 100644
--- a/src/i18n/en/settings.ts
+++ b/src/i18n/en/settings.ts
@@ -3,6 +3,25 @@ export default {
// ===== Page title =====
title: '⚙️ Settings',
+ // ===== Left nav categories (phase 3 master-detail) =====
+ navAppearance: 'Appearance',
+ navAiModel: 'AI Models',
+ navKnowledge: 'Knowledge',
+ navPerformance: 'Performance',
+ navSecurity: 'Security',
+ navAdvanced: 'Advanced',
+
+ // ===== Settings search (phase 4) =====
+ searchPlaceholder: 'Search settings…',
+ searchNoResult: 'No results',
+
+ // ===== Instant feedback (phase 5 SettingRow) =====
+ savedHint: '✓ Saved',
+ nextRoundBadge: 'Next message',
+
+ // ===== Connection mgmt (phase 7: experimental badge, not wired to AI yet) =====
+ experimentalBadge: 'Experimental · not wired to AI tools',
+
// ===== AI Provider panel =====
panelAiProvider: '🤖 AI Model Config',
addProvider: '+ Add Provider',
@@ -73,6 +92,10 @@ export default {
// ===== General settings panel =====
panelGeneral: '🎨 General',
+ // 阶段2 UX 重构:GeneralPanel 拆 3 Section 后各 Section 标题
+ sectionAppearance: '🎨 Appearance',
+ sectionPerformance: '⚡ Performance',
+ sectionAdvanced: '⚙️ Advanced',
labelTheme: 'Theme',
descTheme: 'Dark / light',
themeDark: '🌙 Dark',
@@ -97,6 +120,9 @@ export default {
labelShowTokenUsage: 'Show token usage',
descShowTokenUsage: 'Show token consumption of each reply in conversations',
+ labelStreamingMd: 'Streaming Markdown rendering',
+ descStreamingMd: 'Render code blocks/lists/headings during streaming (unclosed code blocks degrade to plain text to avoid flicker); when off, only plain text is shown during streaming and full formatting applies after generation completes',
+
labelGlobalConcurrency: 'Global max concurrency',
descGlobalConcurrency: 'Shared cap on simultaneous LLM requests across all conversations',
@@ -104,7 +130,8 @@ export default {
descPerConvConcurrency: 'Concurrent LLM calls within one conversation (main loop / title / distill shared)',
labelAgentMaxIterations: 'Agent max iterations',
- descAgentMaxIterations: 'Max rounds of the agentic loop (stream → tool → feedback) (1-50, default 10); changes take effect on the next message',
+ descAgentMaxIterations: 'Max rounds of the agentic loop (stream → tool → feedback) (0=unlimited, 1-50, default 10); changes take effect on the next message',
+ labelUnlimited: 'Unlimited',
labelAgentMaxRetries: 'Stream failure retries',
descAgentMaxRetries: 'Auto-retry count on stream failure (0-10, default 3); only retries pre-stream failures (no token output yet); mid-stream failures are abandoned',
@@ -131,12 +158,9 @@ export default {
labelTriggerMode: 'Distill trigger',
descTriggerMode: 'When auto-distillation is triggered',
triggerOnComplete: 'On conversation complete',
- triggerOnIdle: 'On conversation idle',
triggerManualOnly: 'Manual only',
labelMinMessages: 'Min message count',
descMinMessages: 'Distillation triggers only after this many messages (filters out small talk noise)',
- labelIdleTimeout: 'Idle timeout (s)',
- descIdleTimeout: 'Seconds to wait in idle-trigger mode',
labelAutoInject: 'Auto-inject related knowledge',
descAutoInject: 'Retrieve related knowledge and inject it into the conversation context during chat',
labelVectorEnabled: 'Semantic retrieval (vector)',
@@ -159,6 +183,18 @@ export default {
toastSetDefaultFail: 'Failed to set default: {msg}',
toastConnIncomplete: 'Please fill in Name and Host',
+ // ===== Import / Export (phase 6) =====
+ export: 'Export',
+ import: 'Import',
+ exportHint: 'Export all preferences (appSettings + knowledge config) as a JSON file',
+ importHint: 'Import from a JSON file and overwrite current preferences',
+ importConfirm: 'Importing will overwrite all current preferences (appearance / performance / knowledge, etc.). Continue?',
+ importSuccess: 'Imported successfully (some items like concurrency / iterations take effect after restart or next message)',
+ importFail: 'Import failed: {msg}',
+ exportFail: 'Export failed: {msg}',
+ importInvalidJson: 'The file is not a valid DevFlow settings backup (wrong format)',
+ importScopeNote: 'Scope: appSettings + knowledge config (no provider keys)',
+
// ===== Confirm dialogs =====
confirmDeleteProvider: 'Delete provider "{name}"? This cannot be undone.',
confirmDeleteConn: 'Delete connection "{name}"?',
diff --git a/src/i18n/zh-CN/settings.ts b/src/i18n/zh-CN/settings.ts
index 4610ddc..7b7ff62 100644
--- a/src/i18n/zh-CN/settings.ts
+++ b/src/i18n/zh-CN/settings.ts
@@ -3,6 +3,25 @@ export default {
// ===== 页面标题 =====
title: '⚙️ 设置',
+ // ===== 左导航分类(阶段3 master-detail) =====
+ navAppearance: '外观',
+ navAiModel: 'AI 模型',
+ navKnowledge: '知识库',
+ navPerformance: '性能',
+ navSecurity: '安全',
+ navAdvanced: '高级',
+
+ // ===== 设置搜索(阶段4) =====
+ searchPlaceholder: '搜索设置项…',
+ searchNoResult: '无结果',
+
+ // ===== 即时生效反馈(阶段5 SettingRow) =====
+ savedHint: '✓ 已保存',
+ nextRoundBadge: '下次对话生效',
+
+ // ===== 连接管理(阶段7:实验功能 badge 明示暂未接入 AI) =====
+ experimentalBadge: '实验功能·暂未接入 AI 工具',
+
// ===== AI 模型配置面板 =====
panelAiProvider: '🤖 AI 模型配置',
addProvider: '+ 添加 Provider',
@@ -73,6 +92,10 @@ export default {
// ===== 通用设置面板 =====
panelGeneral: '🎨 通用设置',
+ // 阶段2 UX 重构:GeneralPanel 拆 3 Section 后各 Section 标题
+ sectionAppearance: '🎨 外观',
+ sectionPerformance: '⚡ 性能',
+ sectionAdvanced: '⚙️ 高级',
labelTheme: '主题',
descTheme: '深色 / 浅色模式',
themeDark: '🌙 深色模式',
@@ -97,6 +120,9 @@ export default {
labelShowTokenUsage: '显示 Token 用量',
descShowTokenUsage: '在对话中展示每次回复的 token 消耗',
+ labelStreamingMd: '流式 Markdown 渲染',
+ descStreamingMd: '流式生成过程中渲染代码块/列表/标题格式(未闭合代码块降级纯文本防闪烁);关闭后流式期间仅显纯文本,生成完成后才渲染完整格式',
+
labelGlobalConcurrency: '全局最大并发',
descGlobalConcurrency: '所有对话共享的 LLM 同时请求数上限',
@@ -104,7 +130,8 @@ export default {
descPerConvConcurrency: '单个对话内同时进行的 LLM 调用数(主循环 / 标题 / 提炼共享)',
labelAgentMaxIterations: 'Agent 最大轮次',
- descAgentMaxIterations: 'Agentic 循环(流式→工具→回传)的最大轮次(1-50,默认 10);热改下次发消息生效',
+ descAgentMaxIterations: 'Agentic 循环(流式→工具→回传)的最大轮次(0=不限,1-50,默认 10);热改下次发消息生效',
+ labelUnlimited: '不限',
labelAgentMaxRetries: '流式失败重试次数',
descAgentMaxRetries: '流式对话失败时自动重试次数(0-10,默认 3);只重试未输出任何 token 的流前失败,已输出文本的失败直接放弃',
@@ -131,12 +158,9 @@ export default {
labelTriggerMode: '提炼触发方式',
descTriggerMode: '自动提炼的触发时机',
triggerOnComplete: '对话完成后',
- triggerOnIdle: '对话闲置后',
triggerManualOnly: '仅手动',
labelMinMessages: '最少消息数',
descMinMessages: '对话达到此消息数才触发提炼(防闲聊噪音)',
- labelIdleTimeout: '闲置超时(秒)',
- descIdleTimeout: '闲置触发模式下等待的秒数',
labelAutoInject: '自动注入相关知识',
descAutoInject: '聊天时自动检索相关知识注入对话上下文',
labelVectorEnabled: '语义检索(向量)',
@@ -159,6 +183,18 @@ export default {
toastSetDefaultFail: '设置默认失败:{msg}',
toastConnIncomplete: '请填写名称和 Host',
+ // ===== 导入 / 导出(阶段6) =====
+ export: '导出',
+ import: '导入',
+ exportHint: '导出全部偏好(appSettings + 知识库配置)为 JSON 文件',
+ importHint: '从 JSON 文件导入并覆盖当前偏好',
+ importConfirm: '导入将覆盖当前的全部偏好设置(外观 / 性能 / 知识库等),确定继续?',
+ importSuccess: '导入成功(部分项如并发 / 轮次需重启或下次对话生效)',
+ importFail: '导入失败:{msg}',
+ exportFail: '导出失败:{msg}',
+ importInvalidJson: '文件不是有效的 DevFlow 设置备份(格式不符)',
+ importScopeNote: '范围:appSettings + 知识库配置(不含 Provider 密钥)',
+
// ===== 确认弹层 =====
confirmDeleteProvider: '确定删除提供商「{name}」?该操作不可恢复。',
confirmDeleteConn: '确定删除连接「{name}」?',
diff --git a/src/main.ts b/src/main.ts
index 941afff..063d5a3 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -5,6 +5,7 @@ import i18n from "./i18n";
import "./styles/global.css";
import "./styles/ai-md.css";
import "./styles/components.css";
+import "./components/settings/settings.css"; // UX 重构阶段1:设置统一组件库全局
const app = createApp(App);
diff --git a/src/views/Settings.vue b/src/views/Settings.vue
index d36542f..7fca2e4 100644
--- a/src/views/Settings.vue
+++ b/src/views/Settings.vue
@@ -19,47 +19,84 @@
{{ $t('settings.title') }}
-
-
-
+
+
+
+
+
+
+
+
-
-
+
+
+
-
-
+
+
-
-
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
@@ -157,30 +280,30 @@ onUnmounted(() => {
}
.page-header h1 { font-size: 24px; font-weight: 500; color: var(--df-text); }
-/* ===== 按钮 ===== */
-.btn {
- padding: 8px 16px;
- border: none;
- border-radius: var(--df-radius-sm);
- font-size: 13px;
- cursor: pointer;
- transition: all 0.15s;
+/* ===== master-detail 布局(阶段3 UX 重构) =====
+ 左 SettingsNav(~200px 固定)+ 右 content(flex 占满 + 纵向滚动)。
+ nav 宽度/折叠由 SettingsNav scoped 控制;此处仅布局骨架。 */
+.settings-body {
+ display: flex;
+ gap: 20px;
+ align-items: flex-start;
}
-.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; }
-
-/* ===== 设置网格 ===== */
-.settings-grid {
+.settings-content {
+ flex: 1;
+ min-width: 0; /* 允许收缩,防子面板溢出撑破 flex */
display: flex;
flex-direction: column;
gap: var(--df-gap-grid);
}
-/* 注:.panel / .panel-header / .empty-hint 等子组件内元素的样式
- 已随功能域拆到各子组件 scoped(父 scoped 无法穿透命中子组件根 section),
- 各子组件自带面板容器 + 按钮基础样式副本,确保零视觉变化。 */
+/* .btn / .btn-primary / .btn-ghost / .btn-sm 已迁移到 settings.css 全局,
+ 本壳的 toast/confirm 内联按钮沿用全局基础样式;此处仅保留全局未定义的 .btn-danger。 */
+
+/* ===== 窄屏响应式:nav 折叠为顶部横向 tab(由 SettingsNav is-horizontal 渲染) ===== */
+@media (max-width: 768px) {
+ .settings-body {
+ flex-direction: column;
+ gap: 12px;
+ }
+}