diff --git a/src/views/creative-generation-workshop/manuscript-writer/check/components/content-card/highlight-textarea.vue b/src/views/creative-generation-workshop/manuscript-writer/check/components/content-card/highlight-textarea.vue index 9dce8c2..c02dbf5 100644 --- a/src/views/creative-generation-workshop/manuscript-writer/check/components/content-card/highlight-textarea.vue +++ b/src/views/creative-generation-workshop/manuscript-writer/check/components/content-card/highlight-textarea.vue @@ -1,24 +1,11 @@ @@ -47,9 +34,11 @@ const emit = defineEmits<{ (e: 'update:modelValue', value: string): void; }>(); +// 内部状态管理 const inputValue = ref(props.modelValue || ''); -const scrollTop = ref(0); const focus = ref(false); +const textareaWrapRef = ref(); +let nativeTextarea: HTMLTextAreaElement | null = null; const highlightedHtml = computed(() => generateHighlightedHtml()); // 监听外部modelValue变化 @@ -67,12 +56,6 @@ const handleInput = (value: string) => { emit('update:modelValue', value); }; -// 同步滚动位置 -const syncScroll = (e: Event) => { - console.log('syncScroll'); - scrollTop.value = (e.target as HTMLTextAreaElement).scrollTop; -}; - const escapeHtml = (str: string): string => { if (!isString(str)) return ''; return str @@ -114,11 +97,24 @@ const generateHighlightedHtml = (): string => { }; onMounted(() => { - document.querySelector('.textarea-input .arco-textarea')?.addEventListener('scroll', handleTextareaScroll); + nativeTextarea = (textareaWrapRef.value?.$el || textareaWrapRef.value)?.querySelector?.('textarea.arco-textarea') || + document.querySelector('.textarea-input .arco-textarea'); + + if (nativeTextarea) { + nativeTextarea.addEventListener('scroll', handleTextareaScroll); + nativeTextarea.addEventListener('compositionstart', handleCompositionUpdate); + nativeTextarea.addEventListener('compositionupdate', handleCompositionUpdate); + nativeTextarea.addEventListener('compositionend', handleCompositionUpdate); + } }); onUnmounted(() => { - document.querySelector('.textarea-input .arco-textarea')?.removeEventListener('scroll', handleTextareaScroll); + if (nativeTextarea) { + nativeTextarea.removeEventListener('scroll', handleTextareaScroll); + nativeTextarea.removeEventListener('compositionstart', handleCompositionUpdate); + nativeTextarea.removeEventListener('compositionupdate', handleCompositionUpdate); + nativeTextarea.removeEventListener('compositionend', handleCompositionUpdate); + } }); const handleTextareaScroll = (e: Event) => { @@ -129,6 +125,18 @@ const handleTextareaScroll = (e: Event) => { highlightElement.scrollTop = _scrollTop; } }; + +const handleCompositionUpdate = () => { + if (!nativeTextarea) return; + // 使用 rAF 等待浏览器把最新字符写入 textarea.value 再读取 + requestAnimationFrame(() => { + if (!nativeTextarea) return; + const latest = nativeTextarea.value.slice(0, 1000); + if (latest !== inputValue.value) { + inputValue.value = latest; + } + }); +}; diff --git a/src/views/creative-generation-workshop/manuscript/check/components/content-card/highlight-textarea.vue b/src/views/creative-generation-workshop/manuscript/check/components/content-card/highlight-textarea.vue index aceb257..3644d31 100644 --- a/src/views/creative-generation-workshop/manuscript/check/components/content-card/highlight-textarea.vue +++ b/src/views/creative-generation-workshop/manuscript/check/components/content-card/highlight-textarea.vue @@ -1,24 +1,11 @@ @@ -51,6 +38,8 @@ const emit = defineEmits<{ const inputValue = ref(props.modelValue || ''); const scrollTop = ref(0); const focus = ref(false); +const textareaWrapRef = ref(); +let nativeTextarea: HTMLTextAreaElement | null = null; const highlightedHtml = computed(() => generateHighlightedHtml()); // 监听外部modelValue变化 @@ -68,12 +57,6 @@ const handleInput = (value: string) => { emit('update:modelValue', value); }; -// 同步滚动位置 -const syncScroll = (e: Event) => { - console.log('syncScroll'); - scrollTop.value = (e.target as HTMLTextAreaElement).scrollTop; -}; - const escapeHtml = (str: string): string => { if (!isString(str)) return ''; return str @@ -115,11 +98,24 @@ const generateHighlightedHtml = (): string => { }; onMounted(() => { - document.querySelector('.textarea-input .arco-textarea')?.addEventListener('scroll', handleTextareaScroll); + nativeTextarea = (textareaWrapRef.value?.$el || textareaWrapRef.value)?.querySelector?.('textarea.arco-textarea') || + document.querySelector('.textarea-input .arco-textarea'); + + if (nativeTextarea) { + nativeTextarea.addEventListener('scroll', handleTextareaScroll); + nativeTextarea.addEventListener('compositionstart', handleCompositionUpdate); + nativeTextarea.addEventListener('compositionupdate', handleCompositionUpdate); + nativeTextarea.addEventListener('compositionend', handleCompositionUpdate); + } }); onUnmounted(() => { - document.querySelector('.textarea-input .arco-textarea')?.removeEventListener('scroll', handleTextareaScroll); + if (nativeTextarea) { + nativeTextarea.removeEventListener('scroll', handleTextareaScroll); + nativeTextarea.removeEventListener('compositionstart', handleCompositionUpdate); + nativeTextarea.removeEventListener('compositionupdate', handleCompositionUpdate); + nativeTextarea.removeEventListener('compositionend', handleCompositionUpdate); + } }); const handleTextareaScroll = (e: Event) => { @@ -130,6 +126,18 @@ const handleTextareaScroll = (e: Event) => { highlightElement.scrollTop = _scrollTop; } }; + +const handleCompositionUpdate = () => { + if (!nativeTextarea) return; + // 使用 rAF 等待浏览器把最新字符写入 textarea.value 再读取 + requestAnimationFrame(() => { + if (!nativeTextarea) return; + const latest = nativeTextarea.value.slice(0, 1000); + if (latest !== inputValue.value) { + inputValue.value = latest; + } + }); +};