diff --git a/src/components/textarea/index.ts b/src/components/textarea/index.ts index 7ef1f66f..00b03382 100644 --- a/src/components/textarea/index.ts +++ b/src/components/textarea/index.ts @@ -4,6 +4,7 @@ import { CommonEvent } from '@tarojs/components/types/common' import Taro from '@tarojs/taro' import { AtTextareaProps } from 'types/textarea' import { pxTransform } from '../../utils/common' +import { useModelValue } from '../../composables/model' type ExtendEvent = { target: { @@ -50,19 +51,15 @@ const AtTextarea = defineComponent({ height: { type: [String, Number], default: 100 }, cursorSpacing: { type: Number, default: 100 }, // event handlers - onChange: { - type: Function as PropType, - default: () => (value: string, event?: CommonEvent) => { }, - required: true - }, + onChange: Function as PropType, onFocus: Function as PropType, onBlur: Function as PropType, onConfirm: Function as PropType, onLinechange: Function as PropType, }, - setup(props: AtTextareaProps, { attrs, slots }) { - const isAlipay = Taro.getEnv() === Taro.ENV_TYPE.ALIPAY + setup(props: AtTextareaProps, { attrs, emit }) { + const inputValue = useModelValue(props, emit, 'value') const _maxLength = computed(() => parseInt(props.maxLength!.toString())) @@ -79,34 +76,36 @@ const AtTextarea = defineComponent({ const rootClasses = computed(() => ({ 'at-textarea': true, [`at-textarea--${ENV}`]: true, - 'at-textarea--error': _maxLength.value < props.value.length + 'at-textarea--error': _maxLength.value < inputValue.value.length })) - const placeholderClasses = computed(() => `placeholder ${props.placeholderClass}`) - - const alipayShowCount = computed(() => isAlipay - ? { showCount: props.count } - : {} - ) + const placeholderClasses = computed(() => ({ + 'placeholder': true, + [`${props.placeholderClass}`]: Boolean(props.placeholderClass) + })) function handleInput(event: CommonEvent & ExtendEvent): void { - props.onChange(event.target.value, event) + if (attrs['onUpdate:value']) { + inputValue.value = event.target.value + } else { + props.onChange?.(event.target.value, event) + } } function handleFocus(event: CommonEvent): void { - props.onFocus && props.onFocus(event) + props.onFocus?.(event) } function handleBlur(event: CommonEvent): void { - props.onBlur && props.onBlur(event) + props.onBlur?.(event) } function handleConfirm(event: CommonEvent): void { - props.onConfirm && props.onConfirm(event) + props.onConfirm?.(event) } function handleLinechange(event: CommonEvent): void { - props.onLinechange && props.onLinechange(event) + props.onLinechange?.(event) } return () => ( @@ -114,33 +113,35 @@ const AtTextarea = defineComponent({ class: rootClasses.value }), { default: () => [ - h(Textarea, mergeProps(alipayShowCount.value, { - class: 'at-textarea__textarea', - style: textareaStyle.value, - placeholderstyle: props.placeholderStyle, - placeholderClass: placeholderClasses.value, - cursorSpacing: props.cursorSpacing, - value: props.value, - maxlength: actualMaxLength.value, - placeholder: props.placeholder, - disabled: props.disabled, - autoFocus: props.autoFocus, - focus: props.focus, - fixed: props.fixed, - showConfirmBar: props.showConfirmBar, - selectionStart: props.selectionStart, - selectionEnd: props.selectionEnd, - onInput: handleInput, - onFocus: handleFocus, - onBlur: handleBlur, - onConfirm: handleConfirm, - onLineChange: handleLinechange, - })), - - props.count && !isAlipay && ( + h(Textarea, mergeProps( + process.env.TARO_ENV === 'alipay' ? { showCount: props.count } : {}, + { + class: 'at-textarea__textarea', + style: textareaStyle.value, + placeholderstyle: props.placeholderStyle, + placeholderClass: placeholderClasses.value, + cursorSpacing: props.cursorSpacing, + value: inputValue.value, + maxlength: actualMaxLength.value, + placeholder: props.placeholder, + disabled: props.disabled, + autoFocus: props.autoFocus, + focus: props.focus, + fixed: props.fixed, + showConfirmBar: props.showConfirmBar, + selectionStart: props.selectionStart, + selectionEnd: props.selectionEnd, + onInput: handleInput, + onFocus: handleFocus, + onBlur: handleBlur, + onConfirm: handleConfirm, + onLineChange: handleLinechange, + })), + + props.count && process.env.TARO_ENV !== 'alipay' && ( h(View, { class: 'at-textarea__counter' - }, { default: () => `${props.value.length} / ${_maxLength.value}` }) + }, { default: () => `${inputValue.value.length} / ${_maxLength.value}` }) ) ] }) diff --git a/src/pages/form/textarea/index.ts b/src/pages/form/textarea/index.ts deleted file mode 100644 index 66bc8bac..00000000 --- a/src/pages/form/textarea/index.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { h, defineComponent, reactive } from 'vue' -import { AtTextarea } from '../../../index' -import { Page, Panel, ExampleItem } from '../../components/demo-page' -import './index.scss' - -interface IndexState { - [key: string]: string -} - -export default defineComponent({ - name: "TextareaDemo", - - setup() { - const state = reactive({ - value1: '', - value2: '', - value3: '', - value4: '' - }) - - function handleChange(stateName: string, value: string): void { - state[stateName] = value - } - - return () => ( - h(Page, { headerTitle: 'Textarea 多行文本框' }, { - default: () => [ - - h(Panel, { title: '基础', }, { - default: () => [ - h(ExampleItem, null, { - default: () => [ - h(AtTextarea, { - value: state.value1, - onChange: handleChange.bind(this, 'value1'), - maxLength: 200, - placeholder: '你的问题是...' - }) - ] - }), - ] - }), - - h(Panel, { title: '不显示字数', }, { - default: () => [ - h(ExampleItem, null, { - default: () => [ - h(AtTextarea, { - count: false, - value: state.value2, - onChange: handleChange.bind(this, 'value2'), - maxLength: 200, - placeholder: '你的问题是...' - }) - ] - }), - ] - }), - - h(Panel, { title: '文字超出仍可输入', }, { - default: () => [ - h(ExampleItem, null, { - default: () => [ - h(AtTextarea, { - textOverflowForbidden: false, - value: state.value3, - onChange: handleChange.bind(this, 'value3'), - maxLength: 200, - placeholder: '你的问题是...' - }) - ] - }), - ] - }), - - h(Panel, { title: '自定义高度', }, { - default: () => [ - h(ExampleItem, null, { - default: () => [ - h(AtTextarea, { - height: '300', - value: state.value4, - onChange: handleChange.bind(this, 'value4'), - maxLength: 200, - placeholder: '你的问题是...' - }) - ] - }), - ] - }), - ] - }) - ) - } -}) diff --git a/src/pages/form/textarea/index.vue b/src/pages/form/textarea/index.vue new file mode 100644 index 00000000..b4e722f5 --- /dev/null +++ b/src/pages/form/textarea/index.vue @@ -0,0 +1,88 @@ + + + \ No newline at end of file diff --git a/types/textarea.d.ts b/types/textarea.d.ts index 01692c25..21ba8f7d 100644 --- a/types/textarea.d.ts +++ b/types/textarea.d.ts @@ -7,7 +7,7 @@ import AtComponent from './base' export interface AtTextareaProps extends AtComponent { /** - * 输入框当前值,用户需要通过 onChange 事件的 event.target.value 来更新 value 值,必填 + * 输入框当前值,支持 v-model,用户可通过 onChange 事件的 event.target.value 或 v-model:value 来更新 value 值,必填 */ value: string /** @@ -86,10 +86,10 @@ export interface AtTextareaProps extends AtComponent { cursorSpacing?: number /** * 输入框值改变时触发的事件, - * 开发者需要通过 onChange 事件来更新 value 值变化, - * onChange 函数必填 + * 开发者可通过 onChange 事件或 v-model:value 来更新 value 值变化, + * 不使用 v-model 时,onChange 函数必填 */ - onChange: (value: string, event?: CommonEvent) => void + onChange?: (value: string, event?: CommonEvent) => void /** * 输入框获得焦点时触发,height 为键盘高度,在基础库 1.9.90 起支持 */