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 @@
-

{{ $t('settings.panelConnection') }}

+
+

{{ $t('settings.panelConnection') }}

+ {{ $t('settings.experimentalBadge') }} +
@@ -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 @@ 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; + } +}