<template> <view :class="{'tui-textarea__border':textareaBorder}" :style="{marginTop:marginTop+'rpx'}" @tap="fieldClick"> <view class="tui-textarea__wrap" :class="{'tui-line__left':lineLeft,'tui-border__top':!borderTop || textareaBorder,'tui-border__bottom':!borderBottom || textareaBorder,'tui-textarea__flex-start':flexStart}" :style="{padding:padding,backgroundColor:backgroundColor}"> <!-- #ifdef APP-NVUE --> <view class="tui-textarea__required" v-if="required && !flexStart"> <text :style="{color:requiredColor,paddingTop:'2rpx'}">*</text> </view> <text class="tui-textarea__required" :style="{color:requiredColor,top:requiredTop}" v-if="required && flexStart">*</text> <!-- #endif --> <!-- #ifndef APP-NVUE --> <view class="tui-textarea__required" :class="{'tui-required__flex-start':flexStart}" :style="{color:requiredColor,top:flexStart?requiredTop:'50%'}" v-if="required">*</view> <!-- #endif --> <view class="tui-textarea__label" :style="{fontSize:labelSize+'rpx',color:labelColor,minWidth:labelWidth+'rpx'}" v-if="label"> <text :style="{fontSize:labelSize+'rpx',color:labelColor}">{{label}}</text> </view> <slot name="left"></slot> <view class="tui-textarea__flex-1"> <textarea class="tui-textarea__self" :class="{'tui-text__right':textRight}" :style="{height:height,minHeight:minHeight,fontSize:size+'rpx',color:color}" placeholder-class="tui-placeholder" :name="name" :value="inputVal" :placeholder="placeholder" :placeholderStyle="placeholderStyl" :disabled="disabled" :cursor-spacing="cursorSpacing" :maxlength="maxlength" :focus="focused" :auto-height="autoHeight" :fixed="fixed" :show-confirm-bar="showConfirmBar" :cursor="cursor" :selection-start="selectionStart" :selection-end="selectionEnd" :adjust-position="adjustPosition" :hold-keyboard="holdKeyboard" :show-count="false" :disable-default-padding="disableDefaultPadding" @focus="onFocus" @blur="onBlur" @input="onInput" @confirm="onConfirm" @linechange="onLinechange" @keyboardheightchange="onKeyboardheightchange"></textarea> <view class="tui-textarea__counter" :style="{fontSize:counterSize+'rpx',color:counterColor}" v-if="isCounter"> <text :style="{fontSize:counterSize+'rpx',color:counterColor}">{{maxlength!=-1?`${count}/${maxlength}`:count}}</text> </view> </view> <slot name="right"></slot> </view> </view> </template> <script> export default { name: "tui-textarea", emits: ['input', 'update:modelValue', 'focus', 'blur', 'confirm', 'click', 'linechange', 'keyboardheightchange'], //这里加group是为了避免在表单中使用时给组件加value属性 // #ifndef VUE3 // #ifdef MP-WEIXIN //加group是为了避免在表单中使用时给组件加value属性 behaviors: ['wx://form-field-group'], // #endif // #ifdef MP-BAIDU || MP-QQ //如果在这些平台不需要也能识别,则删除 behaviors: ['uni://form-field'], // #endif // #endif // #ifdef MP-WEIXIN options: { addGlobalClass: true, virtualHost: true }, // #endif props: { //是否为必填项 required: { type: Boolean, default: false }, requiredColor: { type: String, default: '#EB0909' }, requiredTop: { type: String, default: '32rpx' }, //左侧标题 label: { type: String, default: '' }, //标题字体大小 labelSize: { type: Number, default: 32 }, labelColor: { type: String, default: '#333' }, //label 最小宽度 rpx labelWidth: { type: Number, default: 140 }, //获取焦点 focus: { type: Boolean, default: false }, autoHeight: { type: Boolean, default: false }, fixed: { type: Boolean, default: false }, placeholder: { type: String, default: '' }, placeholderStyle: { type: String, default: '' }, //输入框名称 name: { type: String, default: '' }, //输入框值 value: { type: [Number, String], default: '' }, // #ifdef VUE3 //输入框值 modelValue: { type: [Number, String], default: '' }, // #endif disabled: { type: Boolean, default: false }, maxlength: { type: [Number, String], default: 140 }, cursorSpacing: { type: Number, default: 0, }, showConfirmBar: { type: Boolean, default: true }, cursor: { type: Number, default: -1 }, selectionStart: { type: Number, default: -1 }, selectionEnd: { type: Number, default: -1 }, adjustPosition: { type: Boolean, default: true }, disableDefaultPadding: { type: Boolean, default: true }, holdKeyboard: { type: Boolean, default: false }, height: { type: String, default: '200rpx' }, minHeight: { type: String, default: '200rpx' }, //标题与输入框是否顶端对齐 flexStart: { type: Boolean, default: false }, //输入框字体大小 rpx size: { type: Number, default: 32 }, //输入框字体颜色 color: { type: String, default: '#333' }, // 是否显示 textarea 边框 textareaBorder: { type: Boolean, default: false }, // 是否显示上边框 borderTop: { type: Boolean, default: true }, // 是否显示下边框 borderBottom: { type: Boolean, default: true }, //下边框线条是否有左偏移距离 lineLeft: { type: Boolean, default: false }, // 是否自动去除两端的空格 trim: { type: Boolean, default: true }, textRight: { type: Boolean, default: false }, //输入框padding值 padding: { type: String, default: '26rpx 30rpx' }, //输入框背景颜色 backgroundColor: { type: String, default: '#FFFFFF' }, //输入框margin-top值 rpx marginTop: { type: Number, default: 0 }, //是否显示底部输入长度计数 isCounter: { type: Boolean, default: false }, //计数文本颜色 counterColor: { type: String, default: '#999' }, //计数文本大小 rpx counterSize: { type: Number, default: 28 } }, data() { return { placeholderStyl: '', count: 0, focused: false, inputVal: '' }; }, watch: { focus(val) { this.$nextTick(() => { this.focused = val }) }, placeholderStyle() { this.fieldPlaceholderStyle() }, // #ifdef VUE3 modelValue(newVal) { this.inputVal = newVal || '' this.count = this.getCount(this.inputVal.length) }, // #endif value(newVal) { this.inputVal = newVal || '' this.count = this.getCount(this.inputVal.length) } }, created() { // #ifndef VUE3 this.inputVal = this.value || '' // #endif // #ifdef VUE3 if (this.value && !this.modelValue) { this.inputVal = this.value || '' } else { this.inputVal = this.modelValue || '' } // #endif this.count = this.getCount(this.inputVal.length) this.fieldPlaceholderStyle() }, mounted() { this.$nextTick(() => { this.focused = this.focus }) }, methods: { getCount(count) { const max = Number(this.maxlength) if (count > max && max !== -1) { return max } return count; }, fieldPlaceholderStyle() { if (this.placeholderStyle) { this.placeholderStyl = this.placeholderStyle } else { const size = uni.upx2px(this.size) this.placeholderStyl = `fontSize:${size}px` } }, onInput(event) { let value = event.detail.value; if (this.trim) value = this.trimStr(value); const lenth = value.length; const max = Number(this.maxlength) if (lenth > max && max !== -1) { lenth = max; value = value.substring(0, max - 1) } this.count = lenth; this.$emit('input', value); // #ifdef VUE3 this.$emit('update:modelValue', value) // #endif }, onFocus(event) { this.$emit('focus', event); }, onBlur(event) { this.$emit('blur', event); }, onConfirm(e) { this.$emit('confirm', e.detail.value); }, fieldClick() { this.$emit('click', { name: this.name }); }, onLinechange(e) { this.$emit('linechange', e.detail) }, onKeyboardheightchange(e) { this.$emit('keyboardheightchange', e.detail) }, trimStr(str) { return str.replace(/^\s+|\s+$/g, ''); } } } </script> <style> .tui-textarea__wrap { /* #ifndef APP-NVUE */ width: 100%; box-sizing: border-box; display: flex; /* #endif */ flex-direction: row; flex: 1; align-items: center; position: relative; /* #ifdef APP-NVUE */ border-top-width: 0.5px; border-top-style: solid; border-top-color: rgba(0, 0, 0, 0.1); border-bottom-width: 0.5px; border-bottom-style: solid; border-bottom-color: rgba(0, 0, 0, 0.1); padding: 26rpx 30rpx; /* #endif */ } .tui-textarea__flex-start { align-items: flex-start !important; } /* #ifndef APP-NVUE */ .tui-textarea__wrap::before { content: ' '; position: absolute; top: 0; right: 0; left: 0; border-top: 1px solid var(--thorui-line-color, rgba(0, 0, 0, 0.1)); -webkit-transform: scaleY(0.5); transform: scaleY(0.5); transform-origin: 0 0; z-index: 2; pointer-events: none; } .tui-textarea__wrap::after { content: ' '; position: absolute; border-bottom: 1px solid var(--thorui-line-color, rgba(0, 0, 0, 0.1)); -webkit-transform: scaleY(0.5); transform: scaleY(0.5); transform-origin: 0 100%; bottom: 0; right: 0; left: 0; z-index: 2; pointer-events: none; } .tui-line__left::after { left: 30rpx !important; } .tui-border__top::before { border-top: 0; } .tui-border__bottom::after { border-bottom: 0; } /* #endif */ /* #ifdef APP-NVUE */ .tui-border__top { border-top-width: 0; } .tui-border__bottom { border-bottom-width: 0; } /* #endif */ .tui-textarea__required { position: absolute; left: 12rpx; /* #ifndef APP-NVUE */ height: 30rpx; top: 50%; transform: translateY(-50%); line-height: 1.15; /* #endif */ /* #ifdef APP-NVUE */ flex: 1; height: 100wx; align-items: center; justify-content: center; /* #endif */ } /* #ifndef APP-NVUE */ .tui-required__flex-start { transform: translateY(0); } /* #endif */ .tui-textarea__label { padding-right: 12rpx; /* #ifndef APP-NVUE */ flex-shrink: 0; /* #endif */ } .tui-textarea__self { flex: 1; /* #ifndef APP-NVUE */ width: 100%; overflow: visible; box-sizing: border-box; /* #endif */ /* #ifdef APP-NVUE */ padding-top: 6px; padding-bottom: 10px; /* #endif */ /* #ifdef MP-ALIPAY || MP-TOUTIAO*/ padding-top: 0 !important; padding-bottom: 0; /* #endif */ /* #ifdef MP-TOUTIAO */ background-color: rgba(255, 255, 255, 0) !important; /* #endif */ } .tui-placeholder { /* #ifndef APP-NVUE */ color: var(--thorui-text-color-placeholder, #ccc); overflow: visible; /* #endif */ /* #ifdef APP-NVUE */ color: #ccc; /* #endif */ /* #ifdef MP-TOUTIAO */ padding-top: 0 !important; /* #endif */ } /* #ifdef MP */ ::v-deep .tui-placeholder { color: var(--thorui-text-color-placeholder, #ccc); overflow: visible; } /* #endif */ .tui-textarea__border { border-radius: 4rpx; position: relative; /* #ifdef APP-NVUE */ border-style: solid; border-width: 0.5px; border-color: #d1d1d1; /* #endif */ /* #ifndef APP-NVUE */ border-width: 0; /* #endif */ } /* #ifndef APP-NVUE */ .tui-textarea__border::after { content: ' '; position: absolute; height: 200%; width: 200%; border: 1px solid var(--thorui-border-color, #d1d1d1); transform-origin: 0 0; transform: scale(0.5); left: 0; top: 0; border-radius: 8rpx; pointer-events: none; } /* #endif */ .tui-textarea__flex-1 { flex: 1; } .tui-textarea__counter { padding-top: 8rpx; text-align: right; /* #ifdef APP-NVUE */ flex-direction: row; flex: 1; justify-content: flex-end; /* #endif */ } .tui-text__right { text-align: right; } </style>