diff --git a/docs/archives/106-template-management/README.md b/docs/archives/106-template-management/README.md index 91e4a103..018793f6 100644 --- a/docs/archives/106-template-management/README.md +++ b/docs/archives/106-template-management/README.md @@ -14,20 +14,23 @@ ## 文档清单 - [x] `troubleshooting.md` - 模板管理故障排除清单 +- [x] `event-propagation-fix.md` - 事件传播机制修复(内置模板语言切换bug) - [ ] `design.md` - 模板管理功能设计 - [ ] `experience.md` - 开发经验总结(待从experience.md中提取) ## 相关代码变更 -- 影响包:@prompt-optimizer/core, @prompt-optimizer/ui +- 影响包:@prompt-optimizer/core, @prompt-optimizer/ui, @prompt-optimizer/web, @prompt-optimizer/extension - 主要变更: - 模板管理功能实现 - 异步操作优化 - 错误处理改进 + - **事件传播机制完善**:修复内置模板语言切换后迭代页面不更新的问题 ## 已知问题和解决方案 - 模板删除错误"Template not found":异步方法调用缺少await关键字 - 模态框渲染问题:缺少v-if指令控制显示 - 模板管理器调用逻辑:优化模式选择与模板管理的关联 +- **内置模板语言切换后迭代页面不更新**:事件传播机制缺失,需要建立完整的事件传播链 ## 后续影响 - 提升模板管理用户体验 diff --git a/docs/archives/106-template-management/event-propagation-fix.md b/docs/archives/106-template-management/event-propagation-fix.md new file mode 100644 index 00000000..c614d324 --- /dev/null +++ b/docs/archives/106-template-management/event-propagation-fix.md @@ -0,0 +1,209 @@ +# 事件传播机制修复 - 内置模板语言切换bug + +## 🎯 问题描述 + +### 核心问题 +内置模板语言切换后,主界面的优化提示词下拉框正确更新,但迭代页面的模板选择显示旧语言的模板名称。 + +### 问题表现 +1. **主界面正常**:优化提示词下拉框从"通用优化"正确切换到"General Optimization" +2. **迭代页面异常**: + - 当前选中项显示"通用迭代"(中文) + - 下拉列表显示"General Iteration"(英文) + - 用户需要手动重新选择才能使用英文模板 +3. **实际请求正常**:发送请求时生效的是新语言(因为通过templateId重新获取) + +### 用户体验影响 +- 造成用户困惑:UI显示不一致 +- 需要额外操作:用户必须手动重新选择模板 +- 功能不完整:语言切换功能没有完全生效 + +## 🔍 根本原因分析 + +### 组件层级差异 +**主界面的优化提示词下拉框(正常):** +``` +App.vue +└── TemplateSelectUI (ref="templateSelectRef") +``` + +**迭代页面的模板下拉框(异常):** +``` +App.vue +└── PromptPanelUI (ref="promptPanelRef") + └── TemplateSelect (ref="iterateTemplateSelectRef") +``` + +### 事件传播路径差异 +**主界面的刷新机制:** +1. TemplateManager关闭时自动调用 `templateSelectRef?.refresh?.()` +2. 直接引用,事件传播路径短 +3. 有完整的刷新机制 + +**迭代页面的问题:** +1. 语言切换事件无法传播到深层的TemplateSelect组件 +2. 组件层级更深,需要额外的事件传播机制 +3. 之前没有建立完整的事件传播链 + +### 技术细节 +1. **事件源**:`BuiltinTemplateLanguageSwitch` 发出 `languageChanged` 事件 +2. **处理层**:`TemplateManager` 处理事件并更新自身状态 +3. **传播断点**:事件没有继续传播到App.vue层级 +4. **影响范围**:只有TemplateManager内部的组件得到更新 + +## 🔧 解决方案 + +### 1. 建立事件传播链 + +**TemplateManager.vue** - 发出语言变化事件: +```javascript +const handleLanguageChanged = async (newLanguage: string) => { + // 重新加载模板列表以反映新的语言 + await loadTemplates() + + // 如果当前选中的模板是内置模板,需要重新选择以获取新语言版本 + const currentSelected = selectedTemplate.value + if (currentSelected && currentSelected.isBuiltin) { + try { + const updatedTemplate = await getTemplateManager.value.getTemplate(currentSelected.id) + if (updatedTemplate) { + emit('select', updatedTemplate, getCurrentTemplateType()); + } + } catch (error) { + // 错误处理逻辑... + } + } + + // 🔑 关键修复:发出语言变化事件,通知父组件 + emit('languageChanged', newLanguage) +} +``` + +**事件定义:** +```javascript +const emit = defineEmits(['close', 'select', 'update:show', 'languageChanged']) +``` + +### 2. App.vue处理事件并传播 + +**监听语言变化事件:** +```vue + +``` + +**处理语言变化:** +```javascript +// 处理模板语言变化 +const handleTemplateLanguageChanged = (newLanguage: string) => { + console.log('[App] 模板语言已切换:', newLanguage) + + // 刷新主界面的模板选择组件 + if (templateSelectRef.value?.refresh) { + templateSelectRef.value.refresh() + } + + // 🔑 关键修复:刷新迭代页面的模板选择组件 + if (promptPanelRef.value?.refreshIterateTemplateSelect) { + promptPanelRef.value.refreshIterateTemplateSelect() + } +} +``` + +**添加组件引用:** +```javascript +const templateSelectRef = ref<{ refresh?: () => void } | null>(null) +const promptPanelRef = ref<{ refreshIterateTemplateSelect?: () => void } | null>(null) +``` + +### 3. PromptPanel暴露刷新方法 + +**添加迭代模板选择组件引用:** +```vue + +``` + +**暴露刷新方法:** +```javascript +const iterateTemplateSelectRef = ref<{ refresh?: () => void } | null>(null); + +// 暴露刷新迭代模板选择的方法 +const refreshIterateTemplateSelect = () => { + if (iterateTemplateSelectRef.value?.refresh) { + iterateTemplateSelectRef.value.refresh() + } +} + +defineExpose({ + refreshIterateTemplateSelect +}) +``` + +## ✅ 修复验证 + +### 测试步骤 +1. 打开应用,确认主界面显示中文模板 +2. 点击"功能提示词"打开模板管理界面 +3. 点击"中文"按钮切换到"English" +4. 确认主界面优化提示词下拉框更新为英文 +5. 输入测试内容并执行优化 +6. 点击"继续优化"打开迭代页面 +7. **关键验证**:确认迭代页面的模板选择正确显示英文模板 + +### 验证结果 +- [x] 语言切换事件正确传播到所有TemplateSelect组件 +- [x] 迭代页面的下拉列表正确更新为新语言 +- [x] 用户可以在迭代页面直接使用正确语言的模板 +- [x] 主界面和迭代页面行为一致 +- [x] 无需用户手动重新选择模板 + +## 💡 经验总结 + +### 架构设计原则 +1. **事件传播完整性**:确保状态变化事件能传播到所有相关组件 +2. **组件层级意识**:深层组件需要额外的事件传播机制 +3. **统一响应机制**:相同功能的组件应该有相同的响应机制 +4. **接口一致性**:所有相关组件都应该暴露统一的刷新接口 + +### 最佳实践 +1. **建立完整的事件链**:从事件源到所有消费者的完整路径 +2. **使用ref和defineExpose**:为深层组件提供外部访问接口 +3. **统一刷新机制**:所有TemplateSelect组件都有refresh方法 +4. **日志记录**:添加适当的日志帮助调试事件传播 + +### 避免的陷阱 +1. **假设事件会自动传播**:Vue的事件系统不会自动向下传播 +2. **忽略组件层级差异**:不同层级的组件需要不同的处理方式 +3. **不完整的修复**:只修复部分组件而忽略其他相关组件 +4. **缺乏验证**:没有完整测试所有相关功能 + +### 适用场景 +这个修复模式适用于: +- 全局状态变化需要通知多个层级的组件 +- 组件层级复杂的应用架构 +- 需要统一响应机制的功能模块 +- 事件传播路径不一致的问题 + +## 🔗 相关文档 +- `112-desktop-ipc-fixes/language-switch-fix.md` - 语言切换按钮修复 +- `106-template-management/troubleshooting.md` - 模板管理故障排除清单 + +## 📅 修复记录 +- **发现时间**:2025-01-07 +- **修复时间**:2025-01-07 +- **影响范围**:Web和Extension环境 +- **修复类型**:事件传播机制完善 +- **重要程度**:高(影响用户体验的核心功能) diff --git a/docs/archives/106-template-management/troubleshooting.md b/docs/archives/106-template-management/troubleshooting.md index 2760559b..5809b52a 100644 --- a/docs/archives/106-template-management/troubleshooting.md +++ b/docs/archives/106-template-management/troubleshooting.md @@ -180,7 +180,87 @@ - [ ] 测试UI组件与服务层的交互 - [ ] 测试Electron环境下的IPC通信 -### 9. 监控和调试 +### 9. 内置模板语言切换后迭代页面模板选择不更新 + +**症状:** +- 在模板管理界面切换内置模板语言后,主界面的优化提示词下拉框正确更新 +- 但执行优化后点击"继续优化",迭代页面的模板选择显示旧语言的模板名称 +- 下拉列表已更新为新语言,但当前选中项还是旧语言 +- 实际发送请求时生效的是新语言(因为通过templateId重新获取) + +**根本原因:** +- **事件传播路径不同**:主界面和迭代页面的TemplateSelect组件在不同的层级 +- **组件层级差异**: + - 主界面:`App.vue → TemplateSelectUI`(直接引用) + - 迭代页面:`App.vue → PromptPanelUI → TemplateSelect`(间接引用) +- **刷新机制缺失**:语言切换事件无法传播到深层的TemplateSelect组件 + +**详细分析:** +1. **主界面正常的原因**: + - 在TemplateManager关闭时会自动调用 `templateSelectRef?.refresh?.()` + - 组件层级简单,事件传播路径短 + - 有直接的引用和刷新机制 + +2. **迭代页面异常的原因**: + - 迭代页面的TemplateSelect没有被包含在语言切换的刷新逻辑中 + - 组件层级更深,需要额外的事件传播机制 + - 之前没有建立完整的事件传播链 + +**解决方案:** +1. **建立事件传播链**: + ```javascript + // TemplateManager.vue - 发出语言变化事件 + const handleLanguageChanged = async (newLanguage: string) => { + // ... 现有逻辑 ... + + // 发出语言变化事件,通知父组件 + emit('languageChanged', newLanguage) + } + ``` + +2. **App.vue处理事件并传播**: + ```javascript + // 处理模板语言变化 + const handleTemplateLanguageChanged = (newLanguage: string) => { + // 刷新主界面的模板选择组件 + if (templateSelectRef.value?.refresh) { + templateSelectRef.value.refresh() + } + + // 刷新迭代页面的模板选择组件 + if (promptPanelRef.value?.refreshIterateTemplateSelect) { + promptPanelRef.value.refreshIterateTemplateSelect() + } + } + ``` + +3. **PromptPanel暴露刷新方法**: + ```javascript + // PromptPanel.vue - 暴露刷新迭代模板的方法 + const refreshIterateTemplateSelect = () => { + if (iterateTemplateSelectRef.value?.refresh) { + iterateTemplateSelectRef.value.refresh() + } + } + + defineExpose({ + refreshIterateTemplateSelect + }) + ``` + +**修复验证:** +- [x] 语言切换事件正确传播到所有TemplateSelect组件 +- [x] 迭代页面的下拉列表正确更新为新语言 +- [x] 用户可以在迭代页面选择正确语言的模板 +- [x] 主界面和迭代页面行为一致 + +**经验总结:** +1. **组件层级影响事件传播**:深层组件需要额外的事件传播机制 +2. **统一刷新机制**:所有相关组件都应该有统一的刷新接口 +3. **完整的事件链**:确保事件能够传播到所有需要响应的组件 +4. **架构一致性**:相同功能的组件应该有相同的响应机制 + +### 10. 监控和调试 **日志记录:** - [ ] 记录模板操作的开始和结束 diff --git a/docs/archives/112-desktop-ipc-fixes/language-switch-fix.md b/docs/archives/112-desktop-ipc-fixes/language-switch-fix.md index 3c7d6683..99877259 100644 --- a/docs/archives/112-desktop-ipc-fixes/language-switch-fix.md +++ b/docs/archives/112-desktop-ipc-fixes/language-switch-fix.md @@ -84,10 +84,15 @@ ipcMain.handle('template-getCurrentBuiltinTemplateLanguage', async (event) => { 1. **接口一致性**: 跨环境的接口必须保持一致的异步性 2. **错误处理**: 让错误自然传播,便于问题定位 3. **类型安全**: 使用TypeScript确保接口实现的完整性 +4. **事件传播**: 确保语言切换事件能传播到所有相关组件 ### 最佳实践 1. **统一异步**: 所有跨环境接口都应该是异步的 2. **接口驱动**: 先定义接口,再实现具体类 3. **完整测试**: 在两种环境下都要验证功能 +4. **事件链完整性**: 建立完整的事件传播机制,确保深层组件也能响应状态变化 + +### 相关问题 +- **迭代页面模板选择不更新**: 语言切换后,由于组件层级差异和事件传播机制缺失,迭代页面的模板选择无法正确更新。解决方案是建立完整的事件传播链,确保所有TemplateSelect组件都能响应语言切换事件。详见 `106-template-management/troubleshooting.md` 第9节。 这个修复建立了完整的异步接口设计模式,为后续的IPC开发提供了标准。 diff --git a/docs/developer/general-experience.md b/docs/developer/general-experience.md index 4876caf0..021be812 100644 --- a/docs/developer/general-experience.md +++ b/docs/developer/general-experience.md @@ -84,6 +84,87 @@ defineOptions({ ``` +#### 深层组件事件传播机制 +**问题**:当全局状态变化需要通知多层级嵌套的组件时,事件传播可能中断,导致深层组件无法及时更新。 + +**典型场景**: +- 语言切换后,主界面组件更新正常,但Modal内部的组件显示旧状态 +- 组件层级差异:`App.vue → ComponentA`(直接引用)vs `App.vue → ComponentB → ComponentC`(间接引用) + +**核心原因**: +1. **v-if条件渲染**:组件被销毁后ref失效,无法调用组件方法 +2. **事件传播断点**:事件只传播到直接子组件,不会自动向下传播到深层组件 +3. **组件生命周期差异**:不同层级的组件可能处于不同的生命周期阶段 + +**解决方案**: +1. **使用v-show替代v-if**:确保组件实例始终存在,ref保持有效 + ```vue + + + + + + + + + + ``` + +2. **建立完整事件传播链**:从事件源到所有消费组件 + ```javascript + // 父组件:建立事件传播 + const handleGlobalStateChange = (newState) => { + // 刷新直接子组件 + if (directChildRef.value?.refresh) { + directChildRef.value.refresh() + } + + // 刷新深层组件(通过中间组件的暴露方法) + if (intermediateRef.value?.refreshDeepChild) { + intermediateRef.value.refreshDeepChild() + } + } + + // 中间组件:暴露深层组件的刷新方法 + const deepChildRef = ref() + + const refreshDeepChild = () => { + if (deepChildRef.value?.refresh) { + deepChildRef.value.refresh() + } + } + + defineExpose({ + refreshDeepChild + }) + ``` + +3. **统一刷新接口**:所有相关组件都暴露相同的刷新方法 + ```javascript + // 每个需要响应全局状态变化的组件都实现refresh方法 + const refresh = () => { + // 重新加载数据或更新状态 + } + + defineExpose({ + refresh + }) + ``` + +**最佳实践**: +- **架构设计**:在设计阶段考虑事件传播的完整路径 +- **接口一致性**:定义标准的组件刷新接口(如`refresh()`方法) +- **文档记录**:为复杂的事件传播链建立清晰的架构图 +- **测试验证**:确保在所有使用场景下事件都能正确传播 + +**适用场景**: +- 全局主题切换 +- 语言切换 +- 用户权限变更 +- 模板/配置更新 + +> **详细案例**:参见 [106-template-management/event-propagation-fix.md](../archives/106-template-management/event-propagation-fix.md) + ## ⚡ 快速问题排查 ### 布局问题 @@ -96,6 +177,22 @@ defineOptions({ 2. 确认高度约束是否从顶层正确传递 3. 验证滚动容器是否有正确的 `overflow-y: auto` +### 组件状态同步问题 +1. **深层组件未更新**: + - 检查是否使用了 `v-if` 导致组件被销毁 + - 确认事件传播链是否完整(父→中间→目标组件) + - 验证目标组件是否暴露了刷新方法 + +2. **Modal内组件状态异常**: + - 检查Modal是否使用 `v-show` 而非 `v-if` + - 确认组件ref在Modal关闭时是否仍然有效 + - 验证全局状态变化事件是否传播到Modal内部 + +3. **组件ref调用失败**: + - 确认组件是否已完成挂载(`nextTick`) + - 检查条件渲染是否导致组件不存在 + - 验证ref绑定的组件是否暴露了对应方法 + ### API调用问题 1. 检查环境变量是否正确设置(`VITE_` 前缀) 2. 确认参数是否过度设置默认值 diff --git a/packages/extension/src/App.vue b/packages/extension/src/App.vue index 3a0c60d8..dfd98431 100644 --- a/packages/extension/src/App.vue +++ b/packages/extension/src/App.vue @@ -99,6 +99,7 @@