Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 6f490b3

Browse files
Su-u-unlinshenkx
andauthoredJun 27, 2025
feat(template): 修复在切换模板语言后,模板选择器不响应式更新 (#120)
* feat(template): 修复在切换模板语言后,模板选择器不响应式更新 - 在关闭模板管理后,对模板选择器的模板列表进行更新 - 在模板选择器中,对展示的模板进行更新 * fix(template): 修复数组内容深度比较和代码冗余问题 - 修复 BugBot 发现的模板内容数组引用比较问题 * 实现 deepCompareTemplateContent 函数支持 string/Array 类型深度比较 * 避免数组引用相同但内容不同时的错误判断 * 防止数组内容相同但引用不同时的不必要更新 - 消除父子组件间的代码冗余 * 移除 App.vue 中重复的模板更新逻辑 * 明确职责分工:子组件负责刷新逻辑,父组件负责触发时机 * 通过 v-model 实现状态同步,避免双重更新 - 完善组件接口和文档 * 正确暴露 TemplateSelect 组件的 refresh 方法 * 添加详细的职责分工注释和使用说明 * 更新 experience.md 记录此次重要修复 解决的问题: - Template Update Logic Fails - Deep Array Comparison Bug - Array Comparison Bug Causes Unnecessary Updates - Template Selector Update Method Mismatch 测试状态: 所有核心功能测试通过 * fix(template): 修复模板选择器类型过滤器验证问题 - 修复 BugBot 发现的模板类型过滤器忽略问题 * 在 refreshTemplates 函数中添加类型验证逻辑 * 确保更新后的模板匹配当前组件的类型过滤器 * 避免选择不匹配类型的模板导致的显示错误 解决问题: Template Selection Ignores Type Filter 测试状态: UI 组件测试全部通过 (23/23) --------- Co-authored-by: linshen <32978552+linshenkx@users.noreply.github.com>
1 parent fa3a3fb commit 6f490b3

File tree

4 files changed

+159
-14
lines changed

4 files changed

+159
-14
lines changed
 

‎docs/experience.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,60 @@ for (const [key, value] of Object.entries(importData)) {
182182
- **用户模板**:可修改,导入时生成新ID
183183
- **导入规则**:跳过与内置模板ID重复的模板
184184

185+
### 4. 数组内容深度比较修复(2025-01-27)
186+
**问题**:BugBot 发现模板内容比较使用引用比较而非深度比较
187+
```typescript
188+
// ❌ 错误:数组引用比较
189+
if (updatedTemplate.content !== currentTemplate.content) {
190+
// 数组内容相同但引用不同时会触发不必要更新
191+
}
192+
193+
// ✅ 正确:深度比较函数
194+
const deepCompareTemplateContent = (content1, content2) => {
195+
if (typeof content1 !== typeof content2) return false;
196+
197+
if (typeof content1 === 'string') return content1 === content2;
198+
199+
if (Array.isArray(content1) && Array.isArray(content2)) {
200+
if (content1.length !== content2.length) return false;
201+
return content1.every((item1, index) => {
202+
const item2 = content2[index];
203+
return item1.role === item2.role && item1.content === item2.content;
204+
});
205+
}
206+
207+
return JSON.stringify(content1) === JSON.stringify(content2);
208+
};
209+
210+
// 使用深度比较
211+
if (!deepCompareTemplateContent(updatedTemplate.content, currentTemplate.content)) {
212+
// 只有内容真正改变时才更新
213+
}
214+
```
215+
216+
**关键**:Template 接口的 content 可以是 `string | Array<{role: string; content: string}>`,必须支持两种类型的正确比较。
217+
218+
### 5. 模板类型过滤器验证修复(2025-01-27)
219+
**问题**:BugBot 发现 refreshTemplates 函数可能选择不匹配类型过滤器的模板
220+
```typescript
221+
// ❌ 问题:更新模板后直接返回,未验证类型匹配
222+
if (updatedTemplate && contentChanged) {
223+
emit('update:modelValue', updatedTemplate)
224+
return // 跳过了类型验证
225+
}
226+
227+
// ✅ 修复:添加类型验证
228+
if (updatedTemplate && contentChanged) {
229+
// 验证更新后的模板是否还匹配当前类型过滤器
230+
if (updatedTemplate.metadata.templateType === props.type) {
231+
emit('update:modelValue', updatedTemplate)
232+
return
233+
}
234+
// 类型不匹配时继续执行后续逻辑
235+
}
236+
```
237+
**修复效果**:确保模板选择器只选择匹配当前类型的模板,避免类型不一致的问题。
238+
185239
---
186240

187241
## 📝 文档更新规范

‎packages/extension/src/App.vue

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
</template>
7878
<template #template-select>
7979
<TemplateSelectUI
80+
ref="templateSelectRef"
8081
v-model="currentSelectedTemplate"
8182
:type="selectedOptimizationMode === 'system' ? 'optimize' : 'userOptimize'"
8283
:optimization-mode="selectedOptimizationMode"
@@ -346,8 +347,17 @@ const openTemplateManager = (type: string) => {
346347
showTemplates.value = true
347348
}
348349
350+
// 模板选择器引用
351+
const templateSelectRef = ref()
352+
349353
const handleTemplateManagerClose = () => {
350354
showTemplates.value = false
355+
356+
// 刷新模板选择器以反映语言变更后的模板
357+
// 子组件会通过 v-model 自动更新父组件的状态
358+
if (templateSelectRef.value?.refresh) {
359+
templateSelectRef.value.refresh()
360+
}
351361
}
352362
353363
// 数据管理器

‎packages/ui/src/components/TemplateSelect.vue

Lines changed: 85 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ const { t } = useI18n()
7676
interface Template {
7777
id: string;
7878
name: string;
79+
content: string | Array<{role: string; content: string}>;
7980
isBuiltin?: boolean;
8081
metadata: {
8182
description?: string;
@@ -189,7 +190,7 @@ watch(
189190
// 添加对模板列表变化的监听
190191
watch(
191192
templates, // 监听模板列表
192-
(newTemplates, oldTemplates) => {
193+
(newTemplates) => {
193194
const currentTemplate = props.modelValue
194195
// 只有在模板列表真正发生变化,且当前模板不在新列表中时才自动切换
195196
if (currentTemplate && !newTemplates.find(t => t.id === currentTemplate.id)) {
@@ -205,32 +206,102 @@ watch(
205206
{ deep: true }
206207
)
207208
208-
// 改进刷新方法
209+
/**
210+
* 深度比较模板内容
211+
* 支持 string 和 Array<{role: string; content: string}> 两种类型
212+
* 修复 BugBot 发现的数组引用比较问题
213+
*/
214+
const deepCompareTemplateContent = (content1: any, content2: any): boolean => {
215+
// 类型相同性检查
216+
if (typeof content1 !== typeof content2) {
217+
return false
218+
}
219+
220+
// 字符串类型直接比较
221+
if (typeof content1 === 'string') {
222+
return content1 === content2
223+
}
224+
225+
// 数组类型深度比较
226+
if (Array.isArray(content1) && Array.isArray(content2)) {
227+
if (content1.length !== content2.length) {
228+
return false
229+
}
230+
231+
return content1.every((item1, index) => {
232+
const item2 = content2[index]
233+
return item1.role === item2.role && item1.content === item2.content
234+
})
235+
}
236+
237+
// 其他情况使用 JSON 序列化比较(兜底方案)
238+
return JSON.stringify(content1) === JSON.stringify(content2)
239+
}
240+
241+
/**
242+
* 刷新模板列表和当前选中的模板
243+
* 职责:
244+
* 1. 刷新模板列表显示
245+
* 2. 检查当前选中模板是否需要更新(如语言切换)
246+
* 3. 处理模板不存在的情况(自动选择默认模板)
247+
*/
209248
const refreshTemplates = () => {
210-
const oldTrigger = refreshTrigger.value
211249
refreshTrigger.value++
212-
250+
213251
// 检查模板管理器是否已初始化
214252
if (!templateManager.isInitialized()) {
215253
return
216254
}
217-
218-
// 只在初始化时检查和自动选择模板,避免重复触发
255+
219256
const currentTemplates = templateManager.listTemplatesByType(props.type)
220257
const currentTemplate = props.modelValue
221-
222-
// 仅在当前没有选中模板或选中的模板不在列表中时才自动选择
258+
259+
// 处理当前选中模板的更新(主要用于语言切换场景)
260+
if (currentTemplate) {
261+
try {
262+
const updatedTemplate = templateManager.getTemplate(currentTemplate.id)
263+
// 使用深度比较检查模板内容是否发生变化(修复 BugBot 发现的数组比较问题)
264+
if (updatedTemplate && (
265+
updatedTemplate.name !== currentTemplate.name ||
266+
!deepCompareTemplateContent(updatedTemplate.content, currentTemplate.content)
267+
)) {
268+
// 验证更新后的模板是否还匹配当前类型过滤器(修复类型过滤器忽略问题)
269+
if (updatedTemplate.metadata.templateType === props.type) {
270+
// 通过 v-model 更新父组件状态
271+
emit('update:modelValue', updatedTemplate)
272+
// 静默更新,不显示用户提示
273+
emit('select', updatedTemplate, false)
274+
return
275+
}
276+
// 如果类型不匹配,继续执行后续逻辑选择合适的模板
277+
}
278+
} catch (error) {
279+
console.warn('[TemplateSelect] Failed to get updated template:', error)
280+
}
281+
}
282+
283+
// 处理模板不存在的情况:当前模板已被删除或不在当前类型列表中
223284
if (!currentTemplate || !currentTemplates.find(t => t.id === currentTemplate.id)) {
224-
const firstTemplate = currentTemplates[0] || null
225-
if (firstTemplate && firstTemplate.id !== currentTemplate?.id) {
226-
emit('update:modelValue', firstTemplate)
227-
// 静默选择,不显示toast(通过传递false参数)
228-
emit('select', firstTemplate, false)
285+
const defaultTemplate = currentTemplates[0] || null
286+
if (defaultTemplate && defaultTemplate.id !== currentTemplate?.id) {
287+
emit('update:modelValue', defaultTemplate)
288+
// 静默选择,不显示用户提示
289+
emit('select', defaultTemplate, false)
229290
}
230291
}
231292
}
232293
233-
// 暴露刷新方法给父组件
294+
/**
295+
* 暴露给父组件的接口
296+
*
297+
* refresh(): 当外部状态变化(如语言切换、模板管理操作)时,
298+
* 父组件可以调用此方法通知子组件刷新数据。
299+
* 子组件负责检查数据变化并通过 v-model 更新父组件状态。
300+
*
301+
* 职责分工:
302+
* - 父组件:检测需要刷新的时机,调用 refresh()
303+
* - 子组件:执行具体的刷新逻辑,管理自身状态,通过事件通知父组件
304+
*/
234305
defineExpose({
235306
refresh: refreshTemplates
236307
})

‎packages/web/src/App.vue

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@
7777
</template>
7878
<template #template-select>
7979
<TemplateSelectUI
80+
ref="templateSelectRef"
8081
v-model="currentSelectedTemplate"
8182
:type="selectedOptimizationMode === 'system' ? 'optimize' : 'userOptimize'"
8283
:optimization-mode="selectedOptimizationMode"
@@ -346,8 +347,17 @@ const openTemplateManager = (type: string) => {
346347
showTemplates.value = true
347348
}
348349
350+
// 模板选择器引用
351+
const templateSelectRef = ref()
352+
349353
const handleTemplateManagerClose = () => {
350354
showTemplates.value = false
355+
356+
// 刷新模板选择器以反映语言变更后的模板
357+
// 子组件会通过 v-model 自动更新父组件的状态
358+
if (templateSelectRef.value?.refresh) {
359+
templateSelectRef.value.refresh()
360+
}
351361
}
352362
353363
// 数据管理器

0 commit comments

Comments
 (0)
Failed to load comments.