@@ -24,6 +24,26 @@ function fixEmojiLength(value: string, maxLength: number) {
24
24
return [ ...( value || '' ) ] . slice ( 0 , maxLength ) . join ( '' ) ;
25
25
}
26
26
27
+ function setTriggerValue (
28
+ isCursorInEnd : boolean ,
29
+ preValue : string ,
30
+ triggerValue : string ,
31
+ maxLength : number ,
32
+ ) {
33
+ let newTriggerValue = triggerValue ;
34
+ if ( isCursorInEnd ) {
35
+ // 光标在尾部,直接截断
36
+ newTriggerValue = fixEmojiLength ( triggerValue , maxLength ! ) ;
37
+ } else if (
38
+ [ ...( preValue || '' ) ] . length < triggerValue . length &&
39
+ [ ...( triggerValue || '' ) ] . length > maxLength !
40
+ ) {
41
+ // 光标在中间,如果最后的值超过最大值,则采用原先的值
42
+ newTriggerValue = preValue ;
43
+ }
44
+ return newTriggerValue ;
45
+ }
46
+
27
47
export default defineComponent ( {
28
48
name : 'ATextarea' ,
29
49
inheritAttrs : false ,
@@ -40,6 +60,40 @@ export default defineComponent({
40
60
// Max length value
41
61
const hasMaxLength = computed ( ( ) => Number ( props . maxlength ) > 0 ) ;
42
62
const compositing = ref ( false ) ;
63
+
64
+ const oldCompositionValueRef = ref < string > ( ) ;
65
+ const oldSelectionStartRef = ref < number > ( 0 ) ;
66
+ const onInternalCompositionStart = ( e : CompositionEvent ) => {
67
+ compositing . value = true ;
68
+ // 拼音输入前保存一份旧值
69
+ oldCompositionValueRef . value = mergedValue . value as string ;
70
+ // 保存旧的光标位置
71
+ oldSelectionStartRef . value = ( e . currentTarget as any ) . selectionStart ;
72
+ emit ( 'compositionstart' , e ) ;
73
+ } ;
74
+
75
+ const onInternalCompositionEnd = ( e : CompositionEvent ) => {
76
+ compositing . value = false ;
77
+ let triggerValue = ( e . currentTarget as any ) . value ;
78
+ if ( hasMaxLength . value ) {
79
+ const isCursorInEnd =
80
+ oldSelectionStartRef . value >= props . maxlength + 1 ||
81
+ oldSelectionStartRef . value === oldCompositionValueRef . value ?. length ;
82
+ triggerValue = setTriggerValue (
83
+ isCursorInEnd ,
84
+ oldCompositionValueRef . value as string ,
85
+ triggerValue ,
86
+ props . maxlength ,
87
+ ) ;
88
+ }
89
+ // Patch composition onChange when value changed
90
+ if ( triggerValue !== mergedValue . value ) {
91
+ setValue ( triggerValue ) ;
92
+ resolveOnChange ( e . currentTarget as any , e , triggerChange , triggerValue ) ;
93
+ }
94
+
95
+ emit ( 'compositionend' , e ) ;
96
+ } ;
43
97
const instance = getCurrentInstance ( ) ;
44
98
watch (
45
99
( ) => props . value ,
@@ -103,12 +157,24 @@ export default defineComponent({
103
157
} ;
104
158
105
159
const handleChange = ( e : Event ) => {
106
- const { value, composing } = e . target as any ;
107
- compositing . value = ( e as any ) . isComposing || composing ;
108
- if ( ( compositing . value && props . lazy ) || stateValue . value === value ) return ;
109
- let triggerValue = ( e . currentTarget as any ) . value ;
160
+ const { composing } = e . target as any ;
161
+ let triggerValue = ( e . target as any ) . value ;
162
+ compositing . value = ! ! ( ( e as any ) . isComposing || composing ) ;
163
+ if ( ( compositing . value && props . lazy ) || stateValue . value === triggerValue ) return ;
164
+
110
165
if ( hasMaxLength . value ) {
111
- triggerValue = fixEmojiLength ( triggerValue , props . maxlength ! ) ;
166
+ // 1. 复制粘贴超过maxlength的情况 2.未超过maxlength的情况
167
+ const target = e . target as any ;
168
+ const isCursorInEnd =
169
+ target . selectionStart >= props . maxlength ! + 1 ||
170
+ target . selectionStart === triggerValue . length ||
171
+ ! target . selectionStart ;
172
+ triggerValue = setTriggerValue (
173
+ isCursorInEnd ,
174
+ mergedValue . value as string ,
175
+ triggerValue ,
176
+ props . maxlength ! ,
177
+ ) ;
112
178
}
113
179
resolveOnChange ( e . currentTarget as any , e , triggerChange , triggerValue ) ;
114
180
setValue ( triggerValue ) ;
@@ -132,6 +198,8 @@ export default defineComponent({
132
198
onChange : handleChange ,
133
199
onBlur,
134
200
onKeydown : handleKeyDown ,
201
+ onCompositionstart : onInternalCompositionStart ,
202
+ onCompositionend : onInternalCompositionEnd ,
135
203
} ;
136
204
if ( props . valueModifiers ?. lazy ) {
137
205
delete resizeProps . onInput ;
@@ -172,7 +240,7 @@ export default defineComponent({
172
240
mergedValue . value = val ;
173
241
} ) ;
174
242
return ( ) => {
175
- const { maxlength, bordered = true } = props ;
243
+ const { maxlength, bordered = true , hidden } = props ;
176
244
const { style, class : customClass } = attrs ;
177
245
178
246
const inputProps : any = {
@@ -204,6 +272,7 @@ export default defineComponent({
204
272
}
205
273
textareaNode = (
206
274
< div
275
+ hidden = { hidden }
207
276
class = { classNames (
208
277
`${ prefixCls . value } -textarea` ,
209
278
{
0 commit comments