From 1682b628323d7f5f0c3533ad5cf4b0f8ce392c9a Mon Sep 17 00:00:00 2001 From: kjqwer <2990346238@qq.com> Date: Wed, 12 Nov 2025 09:17:32 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=92=A4=E5=9B=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/PromptEditor.vue | 66 ++++++++++++++++++++++----------- 1 file changed, 45 insertions(+), 21 deletions(-) diff --git a/src/components/PromptEditor.vue b/src/components/PromptEditor.vue index 374caa0..acbc83e 100644 --- a/src/components/PromptEditor.vue +++ b/src/components/PromptEditor.vue @@ -136,6 +136,35 @@ function getTextSegmentBounds(txt: string, pos: number) { return { start, end }; } +// 统一的文本替换方法:优先使用原生插入以保留撤回栈,失败时回退 +function applyTextReplacement( + el: HTMLTextAreaElement | HTMLInputElement, + start: number, + end: number, + text: string, +) { + try { + if (typeof el.setSelectionRange === 'function') { + el.setSelectionRange(start, end); + } + const ok = (document as any).execCommand && (document as any).execCommand('insertText', false, text); + if (ok) return; + } catch {} + try { + el.setRangeText(text, start, end, 'end'); + try { + const ie = new (window as any).InputEvent('input', { bubbles: true, data: text, inputType: 'insertReplacementText' }); + el.dispatchEvent(ie); + } catch { + el.dispatchEvent(new Event('input', { bubbles: true })); + } + } catch { + const value = (el as any).value as string; + (el as any).value = value.slice(0, start) + text + value.slice(end); + el.dispatchEvent(new Event('input', { bubbles: true })); + } +} + async function onKeyDown(e: KeyboardEvent) { if (e.key === 'Tab') { // 在光标位置进行补全,不影响撤回 @@ -145,21 +174,19 @@ async function onKeyDown(e: KeyboardEvent) { const before = store.promptText.slice(0, pos); const match = before.match(/[^,,]*$/); const prefix = (match ? match[0] : '').trim(); - const { start, end } = getTextSegmentBounds(store.promptText, pos); - const list = store.getSuggestions(prefix, 8); - if (list.length > 0) { - e.preventDefault(); - const s = list[0]; - if (!s) return; - // 通过 setRangeText 模拟用户输入,保留撤回/前进栈 - el.setRangeText(s, start, end, 'end'); - // 确保触发 v-model 同步 - el.dispatchEvent(new Event('input', { bubbles: true })); - await nextTick(); - updateSuggestions(); - } + const { start, end } = getTextSegmentBounds(store.promptText, pos); + const list = store.getSuggestions(prefix, 8); + if (list.length > 0) { + e.preventDefault(); + const s = list[0]; + if (!s) return; + // 使用原生插入或回退方案,确保撤回可用 + applyTextReplacement(el, start, end, s); + await nextTick(); + updateSuggestions(); } } +} async function copyLeft() { try { @@ -403,9 +430,8 @@ async function applySuggestion(s: string) { el.focus(); const pos = el.selectionStart ?? store.promptText.length; const { start, end } = getTextSegmentBounds(store.promptText, pos); - // 使用 setRangeText 替换整个片段以支持撤回 - el.setRangeText(s, start, end, 'end'); - el.dispatchEvent(new Event('input', { bubbles: true })); + // 使用原生插入或回退方式替换片段,确保撤回可用 + applyTextReplacement(el, start, end, s); await nextTick(); updateSuggestions(); } @@ -423,8 +449,7 @@ function onEditKeyDown(e: KeyboardEvent) { if (list.length > 0) { e.preventDefault(); const s = list[0]; - if (s) el.setRangeText(s, 0, val.length, 'end'); - el.dispatchEvent(new Event('input', { bubbles: true })); + if (s) applyTextReplacement(el, 0, val.length, s); updateEditSuggestions(); } } @@ -435,9 +460,8 @@ function applyEditSuggestion(s: string) { // 保持焦点在编辑输入上 el.focus(); const val = editingValue.value || ''; - // 直接替换整个输入为建议 - el.setRangeText(s, 0, val.length, 'end'); - el.dispatchEvent(new Event('input', { bubbles: true })); + // 直接替换整个输入为建议,保证撤回可用 + applyTextReplacement(el, 0, val.length, s); updateEditSuggestions(); }