uniapp 封装textarea文本域

525 阅读2分钟

封装原因

  • 项目中有时候会要求文本域获取焦点后改变背景色、添加边框

组件属性

属性说明示例
height文本域的高度(autoHeight为false)、最小高度(autoHeight为true)字符串:'200rpx'
textareaBoxStyle文本域父盒子的样式(比如设置边框、背景色、内边距等等)对象
textareaBoxPadding文本域父盒子的上下padding之和(autoHeight为true、textareaBoxStyle中设置了padding则需要传入)字符串:'40rpx'
textareaStyle文本域的样式(比如设置字号、字体、文本颜色等等)对象
placeholder未输入内容时的占位内容对象
placeholderStyle占位内容的样式(比如设置字号、字体、文本颜色等等)对象
disabled文本域是否禁用布尔类型:false
autoHeight文本域是否自动增高布尔类型:true
maxlength最大输入长度,设置为 -1 的时候不限制最大长度number:150
focusStyle文本域获得焦点时的样式对象

注意事项

  • 只封装了文本域常用的功能,如果还有其他需求可以进行二次封装
  • 当autoHeight为true、textareaBoxStyle设置了padding则需要传入textareaBoxPadding
  • 样式对象中的line-height行高问题:开启自动增高后行高无法生效
  • 当占位内容过长(超过文本域一行的宽度后)iOS端设备无法显示除第一行外的剩余内容(Android端不会出现这个问题)
  • placeholderStyle设置行高没有效果(占位内容设置行高无效,估计是textarea组件底层的运算不支持)

封装代码

<template>
	<view class="textarea-box" :style="[textareaBoxStyle, isFocus ? focusStyle : '']">
		<textarea
			:style="[textareaStyle]"
			:placeholder="props.placeholder"
			:placeholder-style="props.placeholderStyle"
			:maxlength="props.maxlength"
			:auto-height="props.autoHeight"
			:disabled="props.disabled"
			@focus="isFocus = true"
			@blur="isFocus = false"
			@input="inputChange"
		></textarea>
	</view>
</template>

<script setup>
	import { ref, onMounted, computed, watch, getCurrentInstance } from 'vue';
	import { onLoad } from '@dcloudio/uni-app';
	const { proxy } = getCurrentInstance();
	const example = proxy;
	const props = defineProps({
		height: {
			type: String,
			default: '200rpx',
		},
		textareaBoxStyle: {
			type: Object,
			default: {},
		},
		textareaBoxPadding: {
			type: String,
			default: '40rpx',
		},
		textareaStyle: {
			type: Object,
			default: {},
		},
		placeholder: {
			type: String,
			default: '请输入',
		},
		placeholderStyle: {
			type: String,
			default: '',
		},
		disabled: {
			type: Boolean,
			default: false,
		},
		autoHeight: {
			type: Boolean,
			default: true,
		},
		maxlength: {
			type: Number,
			default: 150,
		},
		focusStyle: {
			type: Object,
			default: {},
		},
	});
	const emit = defineEmits(['update:modelValue']);

	onMounted(() => {
		init();
	});
	function init() {
		textareaBoxStyle.value = {
			padding: '20rpx',
			borderRadius: '10rpx',
			backgroundColor: '#f8f8f8',
			...props.textareaBoxStyle,
		};
		textareaStyle.value = {
			...props.textareaStyle,
		};
		for (let key in textareaBoxStyle.value) {
			if (key == 'height' || key == 'minHeight' || key == 'min-height') {
				delete textareaBoxStyle.value[key];
			}
		}
		for (let key in textareaStyle.value) {
			if (key == 'height' || key == 'minHeight' || key == 'min-height') {
				delete textareaStyle.value[key];
			}
		}
		if (props.autoHeight) {
			for (let key in textareaBoxStyle.value) {
				if (key == 'lineHeight' || key == 'line-height') {
					delete textareaBoxStyle.value[key];
				}
			}
			for (let key in textareaStyle.value) {
				if (key == 'lineHeight' || key == 'line-height') {
					delete textareaStyle.value[key];
				}
			}
			textareaBoxStyle.value.minHeight = props.height;
			// 处理textareaStyle的min-height
			let result = extractNumberAndUnit(props.textareaBoxPadding);
			let heightResult = extractNumberAndUnit(props.height);
			let minHeight = '';
			if (heightResult.unit == 'rpx') {
				if (result.unit == 'rpx') {
					minHeight = heightResult.number - result.number + 'rpx';
				} else {
					minHeight = heightResult.number - result.number * 2 + 'rpx';
				}
			} else {
				if (result.unit == 'rpx') {
					minHeight = heightResult.number - result.number / 2 + 'px';
				} else {
					minHeight = heightResult.number - result.number + 'px';
				}
			}
			textareaStyle.value.minHeight = minHeight;
		} else {
			textareaBoxStyle.value.height = props.height;
			textareaStyle.value.height = '100%';
		}
		focusStyle.value = {
			backgroundColor: '#fff',
			border: '1rpx solid #97CBE8',
			...props.focusStyle,
		};
	}
	function extractNumberAndUnit(inputString) {
		const match = inputString.match(/(\d+)([a-zA-Z]+)/);
		if (match) {
			const number = match[1] * 1;
			const unit = match[2];
			return { number, unit };
		} else {
			return { number: null, unit: null };
		}
	}
	let textareaBoxStyle = ref({});
	let textareaStyle = ref({});
	let focusStyle = ref({});
	let isFocus = ref(false);
	function inputChange(e) {
		emit('update:modelValue', e.detail.value);
	}
</script>

<style lang="scss" scoped>
	textarea {
		width: 100%;
		z-index: 99;
	}
</style>

页面使用

<myTextarea v-model="textarea"></myTextarea>
let textarea = ref('');