Appearance
Textarea 文本域
用于多行文本输入,适合备注、描述、评论等较长内容。
何时使用
- 需要输入多行内容,例如备注、描述、审批意见、评论。
- 需要限制或提示字数,并在输入时展示统计。
- 需要根据内容自动调整高度。
- 需要在表单中展示禁用、只读、错误等状态。
基础用法
vue
<template>
<x-textarea v-model="value" placeholder="请输入描述" />
</template>
<script setup lang="ts">
import { ref } from 'vue';
const value = ref('用于搭建业务系统的 Vue 3 组件库');
</script>可清除
vue
<template>
<x-textarea v-model="value" allow-clear placeholder="请输入内容" />
</template>
<script setup lang="ts">
import { ref } from 'vue';
const value = ref('这段内容可以一键清空');
</script>字数统计
20/80
vue
<template>
<x-textarea
v-model="value"
:max-length="80"
show-word-limit
allow-clear
placeholder="请输入不超过 80 个字符"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const value = ref('Textarea 支持字数统计和清除操作');
</script>仅提示错误
19/20
vue
<template>
<x-textarea
v-model="value"
:max-length="{ length: 20, errorOnly: true }"
show-word-limit
allow-clear
/>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const value = ref('这段内容允许超出限制,但会进入错误状态');
</script>自适应高度
自适应高度会随内容增长,并在达到 maxRows 后出现滚动条。继续输入更多内容可以观察高度变化。
49/160
vue
<template>
<x-textarea
v-model="value"
:auto-size="{ minRows: 2, maxRows: 5 }"
:max-length="160"
show-word-limit
allow-clear
/>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const value = ref('自适应高度会随内容增长,并在达到 maxRows 后出现滚动条。');
</script>动态切换自适应高度
vue
<template>
<x-checkbox v-model="enabled">启用自动高度(2 至 4 行)</x-checkbox>
<x-textarea v-model="value" :auto-size="enabled ? { minRows: 2, maxRows: 4 } : false" allow-clear />
</template>
<script setup lang="ts">
import { ref } from 'vue';
const enabled = ref(false);
const value = ref('第一行\n第二行\n第三行\n第四行');
</script>开启后立即按内容计算高度;关闭后恢复手动纵向拉伸。修改 minRows、maxRows 或 size 时会重新计算。
尺寸
各尺寸档会同时调整最小高度、字号与内边距;size 变化后自适应高度会重新计算。
vue
<template>
<x-textarea v-model="value" size="mini" />
<x-textarea v-model="value" size="small" />
<x-textarea v-model="value" size="medium" />
<x-textarea v-model="value" size="large" />
</template>
<script setup lang="ts">
import { ref } from 'vue';
const value = ref('四种尺寸的文本域');
</script>禁用与状态
12/20
vue
<template>
<x-textarea v-model="disabledValue" disabled />
<x-textarea v-model="readonlyValue" :textarea-attrs="{ readonly: true }" />
<x-textarea v-model="errorValue" error show-word-limit :max-length="20" />
</template>
<script setup lang="ts">
import { ref } from 'vue';
const disabledValue = ref('禁用状态不可编辑');
const readonlyValue = ref('只读状态通过 textareaAttrs 透传给原生 textarea');
const errorValue = ref('错误状态用于校验失败提示');
</script>后缀内容
备注
14/80
vue
<template>
<x-textarea v-model="value" :max-length="80" show-word-limit>
<template #suffix>
<span>备注</span>
</template>
</x-textarea>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const value = ref('审批备注需要更明确的填写状态');
</script>原生属性
vue
<template>
<x-textarea
v-model="value"
:textarea-attrs="{ rows: 4, name: 'description', required: true, autocomplete: 'off' }"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const value = ref('内部 textarea 可以接收 rows、name、required 等原生属性');
</script>自定义长度计算
22/20
vue
<template>
<x-textarea
v-model="value"
:max-length="20"
show-word-limit
:word-length="getMixedLength"
:word-slice="sliceMixedLength"
allow-clear
/>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const value = ref('中文算 2,English 算 1');
const getMixedLength = (value: string) =>
Array.from(value).reduce((total, char) => total + (char.charCodeAt(0) > 255 ? 2 : 1), 0);
const sliceMixedLength = (value: string, maxLength: number) => {
let total = 0;
let result = '';
for (const char of Array.from(value)) {
const next = total + (char.charCodeAt(0) > 255 ? 2 : 1);
if (next > maxLength) break;
total = next;
result += char;
}
return result;
};
</script>事件
17/80
vue
<template>
<x-textarea
v-model="value"
allow-clear
:max-length="80"
show-word-limit
@input="(value) => pushLog(`input: ${value.length}`)"
@change="(value) => pushLog(`change: ${value.length}`)"
@clear="() => pushLog('clear')"
@focus="() => pushLog('focus')"
@blur="() => pushLog('blur')"
/>
<div>
<div v-for="(log, index) in logs" :key="`${index}-${log}`">{{ log }}</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const value = ref('修改内容后失焦会触发 change');
const logs = ref<string[]>([]);
const pushLog = (message: string) => {
logs.value = [message, ...logs.value].slice(0, 5);
};
</script>在 Form 中校验
组件放进 x-form-item 后会自动继承 Form 的尺寸与禁用状态,并按 FormItem 的校验规则展示错误。默认 validateTrigger 为 change,即值变化时触发校验。
vue
<template>
<x-form ref="formRef" :model="formState" :rules="formRules">
<x-form-item field="description" label="描述">
<x-textarea v-model="formState.description" placeholder="请输入描述" allow-clear />
</x-form-item>
<x-form-item>
<x-space>
<x-button type="primary" @click="handleValidate">校验</x-button>
<x-button @click="handleReset">重置</x-button>
</x-space>
</x-form-item>
</x-form>
<p role="status">{{ validateResult }}</p>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue';
import type { FormInstance, FieldRule } from 'x-next';
const formState = reactive({ description: "" });
const formRules: Record<string, FieldRule[]> = {
description: [{ required: true, message: '请输入描述' }],
};
const formRef = ref<FormInstance>();
const validateResult = ref('');
const handleValidate = async () => {
const errors = await formRef.value!.validate();
validateResult.value = errors
? `校验失败:${Object.values(errors).map((error) => error.message).join(';')}`
: '校验通过';
};
const handleReset = () => {
formRef.value!.resetFields();
validateResult.value = '已重置';
};
</script>按需导入
ts
import { Textarea } from 'x-next';样式按需引入(base.css 为共享基础层,多个组件只需引入一次):
ts
import 'x-next/style/base.css';
import 'x-next/style/form-textarea.css';Props
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
modelValue | 绑定值 | string | - |
defaultValue | 默认值,非受控模式 | string | '' |
placeholder | 占位提示 | string | - |
disabled | 是否禁用 | boolean | false |
error | 是否为错误状态 | boolean | false |
maxLength | 最大长度;传入 errorOnly 时仅展示错误状态,不截断输入 | number | { length: number; errorOnly?: boolean } | 0 |
showWordLimit | 是否显示字数统计 | boolean | false |
allowClear | 是否允许清空 | boolean | false |
autoSize | 是否自适应高度,可配置最小和最大行数 | boolean | { minRows?: number; maxRows?: number } | false |
wordLength | 自定义字符长度计算方法 | (value: string) => number | - |
wordSlice | 自定义超长截取方法,通常与 wordLength 一起使用 | (value: string, maxLength: number) => string | - |
size | 组件尺寸 | 'mini' | 'small' | 'medium' | 'large' | 跟随全局配置 |
textareaAttrs | 透传给内部 textarea 的原生属性 | Record<string, any> | - |
Events
| 事件名 | 说明 | 回调参数 |
|---|---|---|
update:modelValue | 值更新时触发 | value: string |
input | 用户输入时触发 | value: string, event: Event |
change | 文本域失焦且值变化时触发 | value: string, event: Event |
clear | 点击清除按钮时触发 | event: MouseEvent |
focus | 获取焦点时触发 | event: FocusEvent |
blur | 失去焦点时触发 | event: FocusEvent |
Slots
| 插槽名 | 说明 |
|---|---|
suffix | 后缀内容 |
Methods
| 方法 | 说明 |
|---|---|
focus | 使文本域获取焦点 |
blur | 使文本域失去焦点 |