Compare commits

..

2 Commits

Author SHA1 Message Date
04128fac14 fix(frontend): 修复 read_file 搜索结果显示"行??"的问题
后端 search_text 返回 line_start/line_end,前端错误地读取了不存在的 line_number 字段。
改为正确读取 line_start 和 line_end,且当上下文窗口合并时显示完整行范围。
2026-06-10 19:49:06 +08:00
76e4c5144e feat(personalization): 去掉保存按钮,所有选项改为实时自动保存
- personalization store 新增 scheduleAutoSave/autoSave 防抖方法(500ms)
- updateField 及所有 mutation 方法修改后自动触发保存
- PersonalizationDrawer 删除保存按钮和 settings-save-bar
- handleStackedHideBordersChange 移除显式 save 调用
2026-06-10 19:44:33 +08:00
4 changed files with 57 additions and 7 deletions

View File

@ -527,7 +527,8 @@ function renderReadFile(result: any, args: any): string {
matches.forEach((match: any, idx: number) => { matches.forEach((match: any, idx: number) => {
if (idx > 0) html += '<div class="search-match-separator">⋯</div>'; if (idx > 0) html += '<div class="search-match-separator">⋯</div>';
html += '<div class="search-match-item">'; html += '<div class="search-match-item">';
html += `<div class="search-match-line">行 ${match.line_number || '?'}</div>`; const lineLabel = match.line_start === match.line_end ? `${match.line_start}` : `${match.line_start}-${match.line_end}`;
html += `<div class="search-match-line">行 ${lineLabel}</div>`;
html += `<pre>${escapeHtml(match.snippet || match.content || '')}</pre>`; html += `<pre>${escapeHtml(match.snippet || match.content || '')}</pre>`;
html += '</div>'; html += '</div>';
}); });

View File

@ -479,7 +479,8 @@ function renderReadFile(result: any, args: any): string {
matches.forEach((match: any, idx: number) => { matches.forEach((match: any, idx: number) => {
if (idx > 0) html += '<div class="search-match-separator">⋯</div>'; if (idx > 0) html += '<div class="search-match-separator">⋯</div>';
html += '<div class="search-match-item">'; html += '<div class="search-match-item">';
html += `<div class="search-match-line">行 ${match.line_number || '?'}</div>`; const lineLabel = match.line_start === match.line_end ? `${match.line_start}` : `${match.line_start}-${match.line_end}`;
html += `<div class="search-match-line">行 ${lineLabel}</div>`;
html += `<pre>${escapeHtml(match.snippet || match.content || '')}</pre>`; html += `<pre>${escapeHtml(match.snippet || match.content || '')}</pre>`;
html += '</div>'; html += '</div>';
}); });

View File

@ -27,7 +27,6 @@
<div class="personalization-body settings-redesign-body" v-if="!loading"> <div class="personalization-body settings-redesign-body" v-if="!loading">
<form <form
class="personal-form settings-redesign-form" class="personal-form settings-redesign-form"
@submit.prevent="personalization.save()"
> >
<div class="settings-redesign-layout"> <div class="settings-redesign-layout">
<nav class="settings-redesign-tabs" aria-label="个人空间分组切换"> <nav class="settings-redesign-tabs" aria-label="个人空间分组切换">
@ -1651,9 +1650,6 @@
><span class="status error" v-if="error">{{ error }}</span></transition ><span class="status error" v-if="error">{{ error }}</span></transition
> >
</div> </div>
<button type="submit" class="settings-primary-button" :disabled="saving">
{{ saving ? '保存中...' : '保存设置' }}
</button>
</div> </div>
</section> </section>
</div> </div>
@ -2501,7 +2497,6 @@ const handleStackedHideBordersChange = (event: Event) => {
key: 'stacked_hide_borders', key: 'stacked_hide_borders',
value: target.checked value: target.checked
}); });
personalization.save();
}; };
const compactMessageDisplayOptions = [ const compactMessageDisplayOptions = [

View File

@ -207,6 +207,9 @@ const loadExperimentState = (): ExperimentState => {
} }
}; };
// 防抖自动保存计时器(非响应式,避免触发不必要的重渲染)
let autoSaveTimer: ReturnType<typeof setTimeout> | null = null;
export const usePersonalizationStore = defineStore('personalization', { export const usePersonalizationStore = defineStore('personalization', {
state: (): PersonalizationState => ({ state: (): PersonalizationState => ({
visible: false, visible: false,
@ -591,6 +594,40 @@ export const usePersonalizationStore = defineStore('personalization', {
[payload.key]: payload.value [payload.key]: payload.value
}; };
this.clearFeedback(); this.clearFeedback();
this.scheduleAutoSave();
},
/** 防抖自动保存500ms 内多次调用只执行最后一次 */
scheduleAutoSave() {
if (autoSaveTimer) {
clearTimeout(autoSaveTimer);
}
autoSaveTimer = setTimeout(() => {
autoSaveTimer = null;
this.autoSave();
}, 500);
},
/** 静默自动保存,成功不显示提示,失败显示错误 */
async autoSave() {
if (this.saving) {
return;
}
this.saving = true;
try {
const resp = await fetch('/api/personalization', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(this.form)
});
const result = await resp.json();
if (!resp.ok || !result.success) {
throw new Error(result.error || '自动保存失败');
}
// 静默成功,不刷新表单(避免打断用户编辑)
} catch (error: any) {
this.error = error?.message || '自动保存失败';
} finally {
this.saving = false;
}
}, },
setThinkingInterval(value: number | null) { setThinkingInterval(value: number | null) {
let target: number | null = value; let target: number | null = value;
@ -612,6 +649,7 @@ export const usePersonalizationStore = defineStore('personalization', {
thinking_interval: target thinking_interval: target
}; };
this.clearFeedback(); this.clearFeedback();
this.scheduleAutoSave();
}, },
setRecentConversationsPromptLimit(value: number | null) { setRecentConversationsPromptLimit(value: number | null) {
const target = const target =
@ -623,6 +661,7 @@ export const usePersonalizationStore = defineStore('personalization', {
recent_conversations_prompt_limit: target recent_conversations_prompt_limit: target
}; };
this.clearFeedback(); this.clearFeedback();
this.scheduleAutoSave();
}, },
toggleDefaultToolCategory(categoryId: string) { toggleDefaultToolCategory(categoryId: string) {
if (!categoryId) { if (!categoryId) {
@ -639,6 +678,7 @@ export const usePersonalizationStore = defineStore('personalization', {
disabled_tool_categories: Array.from(current) disabled_tool_categories: Array.from(current)
}; };
this.clearFeedback(); this.clearFeedback();
this.scheduleAutoSave();
}, },
toggleSkill(skillId: string) { toggleSkill(skillId: string) {
if (!skillId) { if (!skillId) {
@ -655,6 +695,7 @@ export const usePersonalizationStore = defineStore('personalization', {
enabled_skills: Array.from(current) enabled_skills: Array.from(current)
}; };
this.clearFeedback(); this.clearFeedback();
this.scheduleAutoSave();
}, },
setDefaultRunMode(mode: RunMode | null) { setDefaultRunMode(mode: RunMode | null) {
let target: RunMode | null = null; let target: RunMode | null = null;
@ -666,6 +707,7 @@ export const usePersonalizationStore = defineStore('personalization', {
default_run_mode: target default_run_mode: target
}; };
this.clearFeedback(); this.clearFeedback();
this.scheduleAutoSave();
}, },
setDefaultPermissionMode(mode: PermissionMode) { setDefaultPermissionMode(mode: PermissionMode) {
const allowed: PermissionMode[] = ['readonly', 'approval', 'auto_approval', 'unrestricted']; const allowed: PermissionMode[] = ['readonly', 'approval', 'auto_approval', 'unrestricted'];
@ -675,6 +717,7 @@ export const usePersonalizationStore = defineStore('personalization', {
default_permission_mode: target default_permission_mode: target
}; };
this.clearFeedback(); this.clearFeedback();
this.scheduleAutoSave();
}, },
setVersioningRestoreMode(_mode: 'overwrite') { setVersioningRestoreMode(_mode: 'overwrite') {
const target: 'overwrite' = 'overwrite'; const target: 'overwrite' = 'overwrite';
@ -683,6 +726,7 @@ export const usePersonalizationStore = defineStore('personalization', {
versioning_restore_mode: target versioning_restore_mode: target
}; };
this.clearFeedback(); this.clearFeedback();
this.scheduleAutoSave();
}, },
setDefaultModel(model: string | null) { setDefaultModel(model: string | null) {
const modelStore = useModelStore(); const modelStore = useModelStore();
@ -693,6 +737,7 @@ export const usePersonalizationStore = defineStore('personalization', {
default_model: target default_model: target
}; };
this.clearFeedback(); this.clearFeedback();
this.scheduleAutoSave();
}, },
setImageCompression(mode: string) { setImageCompression(mode: string) {
const allowed = ['original', '1080p', '720p', '540p']; const allowed = ['original', '1080p', '720p', '540p'];
@ -702,6 +747,7 @@ export const usePersonalizationStore = defineStore('personalization', {
image_compression: mode image_compression: mode
}; };
this.clearFeedback(); this.clearFeedback();
this.scheduleAutoSave();
}, },
applyTonePreset(preset: string) { applyTonePreset(preset: string) {
if (!preset) { if (!preset) {
@ -712,6 +758,7 @@ export const usePersonalizationStore = defineStore('personalization', {
tone: preset tone: preset
}; };
this.clearFeedback(); this.clearFeedback();
this.scheduleAutoSave();
}, },
updateNewConsideration(value: string) { updateNewConsideration(value: string) {
this.newConsideration = value; this.newConsideration = value;
@ -730,6 +777,7 @@ export const usePersonalizationStore = defineStore('personalization', {
}; };
this.newConsideration = ''; this.newConsideration = '';
this.clearFeedback(); this.clearFeedback();
this.scheduleAutoSave();
}, },
removeConsideration(index: number) { removeConsideration(index: number) {
const items = [...this.form.considerations]; const items = [...this.form.considerations];
@ -739,6 +787,7 @@ export const usePersonalizationStore = defineStore('personalization', {
considerations: items considerations: items
}; };
this.clearFeedback(); this.clearFeedback();
this.scheduleAutoSave();
}, },
considerationDragStart(index: number, event: DragEvent) { considerationDragStart(index: number, event: DragEvent) {
this.draggedConsiderationIndex = index; this.draggedConsiderationIndex = index;
@ -769,6 +818,7 @@ export const usePersonalizationStore = defineStore('personalization', {
} }
this.considerationDragEnd(); this.considerationDragEnd();
this.considerationDragOver(index, event); this.considerationDragOver(index, event);
this.scheduleAutoSave();
}, },
considerationDragEnd() { considerationDragEnd() {
this.draggedConsiderationIndex = null; this.draggedConsiderationIndex = null;
@ -818,6 +868,7 @@ export const usePersonalizationStore = defineStore('personalization', {
blockDisplayMode: mode blockDisplayMode: mode
}; };
this.persistExperiments(); this.persistExperiments();
this.scheduleAutoSave();
}, },
setCompactMessageDisplay(mode: CompactMessageDisplay) { setCompactMessageDisplay(mode: CompactMessageDisplay) {
const target: CompactMessageDisplay = mode === 'brief' ? 'brief' : 'full'; const target: CompactMessageDisplay = mode === 'brief' ? 'brief' : 'full';
@ -856,6 +907,7 @@ export const usePersonalizationStore = defineStore('personalization', {
communication_style: style communication_style: style
}; };
this.clearFeedback(); this.clearFeedback();
this.scheduleAutoSave();
}, },
setConversationContinuity(value: ConversationContinuity) { setConversationContinuity(value: ConversationContinuity) {
this.form = { this.form = {
@ -863,6 +915,7 @@ export const usePersonalizationStore = defineStore('personalization', {
conversation_continuity: value conversation_continuity: value
}; };
this.clearFeedback(); this.clearFeedback();
this.scheduleAutoSave();
} }
} }
}); });