6565 <div
6666 class =" message-content"
6767 :style =" computedStyles.contentStyle"
68- @dblclick.stop =" startEditing "
68+ @dblclick.stop =" $emit('startEdit', message) "
6969 >
7070 <template v-if =" isEditing " >
7171 <textarea
7272 ref =" editInput"
73- v-model =" editContent "
73+ v-model =" editContentLocal "
7474 class =" content-editor"
75- @blur =" saveEdit"
76- @keydown.enter.ctrl =" saveEdit"
77- @keydown.esc =" cancelEdit"
75+ @blur =" $emit(' saveEdit', message.messageId) "
76+ @keydown.enter.ctrl =" $emit(' saveEdit', message.messageId) "
77+ @keydown.esc =" $emit(' cancelEdit') "
7878 ></textarea >
7979 </template >
8080 <div v-else >
8585</template >
8686
8787<script setup lang="ts">
88- import { computed , ref , nextTick } from ' vue' ;
88+ import { computed , ref , nextTick , watch } from ' vue' ;
8989import type { Message } from ' @/types/log' ;
9090import { useStyleStore } from ' @/stores/styleStore' ;
9191import { formatDate } from ' @/utils/date' ;
9292import { computeStyleForMessage } from ' @/editor/styleEngine' ;
9393import { Trash2 , Plus , Scissors , ChevronsDown } from ' @lucide/vue' ;
9494
95+ // 加入虚拟滚动之后,把编辑状态放在messageItem里会导致状态丢失
96+ // 统一改成父组件传入
9597const props = defineProps <{
9698 message: Message ;
9799 chunkId: string ;
98100 index: number ;
99101 isSelected: boolean ;
100102 isActive: boolean ;
103+ isEditing: boolean ;
104+ editingContent: string ;
101105}>();
102106
103- // 向上事件传递
104107const emit = defineEmits <{
105108 (e : ' select' , event : MouseEvent , messageId : string , index : number ): void ;
106109 (
@@ -117,7 +120,10 @@ const emit = defineEmits<{
117120 (e : ' actionMerge' ): void ;
118121 (e : ' actionSplit' ): void ;
119122 (e : ' actionDelete' ): void ;
123+ (e : ' startEdit' , message : Message ): void ;
120124 (e : ' updateContent' , content : string ): void ;
125+ (e : ' saveEdit' , messageId : string ): void ;
126+ (e : ' cancelEdit' ): void ;
121127}>();
122128
123129const styleStore = useStyleStore ();
@@ -161,35 +167,29 @@ const isHidden = computed(() => {
161167 );
162168});
163169
164- const isEditing = ref (false );
165- const editContent = ref (' ' );
170+ const editContentLocal = computed ({
171+ get : () => props .editingContent ,
172+ set : (val ) => emit (' updateContent' , val ),
173+ });
166174const editInput = ref <HTMLTextAreaElement | null >(null );
167-
168- function startEditing() {
169- editContent .value = props .message .content ;
170- isEditing .value = true ;
171- nextTick (() => {
172- if (editInput .value ) {
173- editInput .value .focus ();
174- editInput .value .setSelectionRange (
175- editContent .value .length ,
176- editContent .value .length ,
177- );
175+ watch (
176+ () => props .isEditing ,
177+ async (newVal ) => {
178+ if (newVal ) {
179+ await nextTick ();
180+ setTimeout (() => {
181+ if (editInput .value ) {
182+ editInput .value .focus ();
183+ editInput .value .setSelectionRange (
184+ props .editingContent .length ,
185+ props .editingContent .length ,
186+ );
187+ }
188+ }, 50 );
189+ // 这里增加延迟,确保虚拟滚动完全稳定,避免textarea闪现
178190 }
179- });
180- }
181-
182- function saveEdit() {
183- if (! isEditing .value ) return ;
184- if (editContent .value !== props .message .content ) {
185- emit (' updateContent' , editContent .value );
186- }
187- isEditing .value = false ;
188- }
189-
190- function cancelEdit() {
191- isEditing .value = false ;
192- }
191+ },
192+ );
193193 </script >
194194
195195<style scoped>
0 commit comments