u-textarea.vue 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. <template>
  2. <view class="u-textarea" :class="textareaClass" :style="[textareaStyle]">
  3. <textarea class="u-textarea__field" :value="innerValue" :style="{ height: $u.addUnit(height) }"
  4. :placeholder="placeholder" :placeholder-style="$u.addStyle(placeholderStyle, 'string')"
  5. :placeholder-class="placeholderClass" :disabled="disabled" :focus="focus" :autoHeight="autoHeight"
  6. :fixed="fixed" :cursorSpacing="cursorSpacing" :cursor="cursor" :showConfirmBar="showConfirmBar"
  7. :selectionStart="selectionStart" :selectionEnd="selectionEnd" :adjustPosition="adjustPosition"
  8. :disableDefaultPadding="disableDefaultPadding" :holdKeyboard="holdKeyboard" :maxlength="maxlength"
  9. :confirmType="confirmType" :ignoreCompositionEvent="ignoreCompositionEvent" @focus="onFocus" @blur="onBlur"
  10. @linechange="onLinechange" @input="onInput" @confirm="onConfirm"
  11. @keyboardheightchange="onKeyboardheightchange"></textarea>
  12. <text class="u-textarea__count" :style="{
  13. 'background-color': disabled ? 'transparent' : '#fff',
  14. }" v-if="count"><text style="color: black;">{{ innerValue.length }}</text>/{{ maxlength }}</text>
  15. </view>
  16. </template>
  17. <script>
  18. import props from "./props.js";
  19. /**
  20. * Textarea 文本域
  21. * @description 文本域此组件满足了可能出现的表单信息补充,编辑等实际逻辑的功能,内置了字数校验等
  22. * @tutorial https://www.uviewui.com/components/textarea.html
  23. *
  24. * @property {String | Number} value 输入框的内容
  25. * @property {String | Number} placeholder 输入框为空时占位符
  26. * @property {String} placeholderClass 指定placeholder的样式类,注意页面或组件的style中写了scoped时,需要在类名前写/deep/ ( 默认 'input-placeholder' )
  27. * @property {String | Object} placeholderStyle 指定placeholder的样式,字符串/对象形式,如"color: red;"
  28. * @property {String | Number} height 输入框高度(默认 70 )
  29. * @property {String} confirmType 设置键盘右下角按钮的文字,仅微信小程序,App-vue和H5有效(默认 'done' )
  30. * @property {Boolean} disabled 是否禁用(默认 false )
  31. * @property {Boolean} count 是否显示统计字数(默认 false )
  32. * @property {Boolean} focus 是否自动获取焦点,nvue不支持,H5取决于浏览器的实现(默认 false )
  33. * @property {Boolean | Function} autoHeight 是否自动增加高度(默认 false )
  34. * @property {Boolean} fixed 如果textarea是在一个position:fixed的区域,需要显示指定属性fixed为true(默认 false )
  35. * @property {Number} cursorSpacing 指定光标与键盘的距离(默认 0 )
  36. * @property {String | Number} cursor 指定focus时的光标位置
  37. * @property {Function} formatter 内容式化函数
  38. * @property {Boolean} showConfirmBar 是否显示键盘上方带有”完成“按钮那一栏,(默认 true )
  39. * @property {Number} selectionStart 光标起始位置,自动聚焦时有效,需与selection-end搭配使用,(默认 -1 )
  40. * @property {Number | Number} selectionEnd 光标结束位置,自动聚焦时有效,需与selection-start搭配使用(默认 -1 )
  41. * @property {Boolean} adjustPosition 键盘弹起时,是否自动上推页面(默认 true )
  42. * @property {Boolean | Number} disableDefaultPadding 是否去掉 iOS 下的默认内边距,只微信小程序有效(默认 false )
  43. * @property {Boolean} holdKeyboard focus时,点击页面的时候不收起键盘,只微信小程序有效(默认 false )
  44. * @property {String | Number} maxlength 最大输入长度,设置为 -1 的时候不限制最大长度(默认 140 )
  45. * @property {String} border 边框类型,surround-四周边框,none-无边框,bottom-底部边框(默认 'surround' )
  46. * @property {Boolean} ignoreCompositionEvent 是否忽略组件内对文本合成系统事件的处理
  47. *
  48. * @event {Function(e)} focus 输入框聚焦时触发,event.detail = { value, height },height 为键盘高度
  49. * @event {Function(e)} blur 输入框失去焦点时触发,event.detail = {value, cursor}
  50. * @event {Function(e)} linechange 输入框行数变化时调用,event.detail = {height: 0, heightRpx: 0, lineCount: 0}
  51. * @event {Function(e)} input 当键盘输入时,触发 input 事件
  52. * @event {Function(e)} confirm 点击完成时, 触发 confirm 事件
  53. * @event {Function(e)} keyboardheightchange 键盘高度发生变化的时候触发此事件
  54. * @example <u--textarea v-model="value1" placeholder="请输入内容" ></u--textarea>
  55. */
  56. export default {
  57. name: "u-textarea",
  58. mixins: [uni.$u.mpMixin, uni.$u.mixin, props],
  59. data() {
  60. return {
  61. // 输入框的值
  62. innerValue: "",
  63. // 是否处于获得焦点状态
  64. focused: false,
  65. // value是否第一次变化,在watch中,由于加入immediate属性,会在第一次触发,此时不应该认为value发生了变化
  66. firstChange: true,
  67. // value绑定值的变化是由内部还是外部引起的
  68. changeFromInner: false,
  69. // 过滤处理方法
  70. innerFormatter: value => value
  71. }
  72. },
  73. watch: {
  74. value: {
  75. immediate: true,
  76. handler(newVal, oldVal) {
  77. this.innerValue = newVal;
  78. /* #ifdef H5 */
  79. // 在H5中,外部value变化后,修改input中的值,不会触发@input事件,此时手动调用值变化方法
  80. if (
  81. this.firstChange === false &&
  82. this.changeFromInner === false
  83. ) {
  84. this.valueChange();
  85. }
  86. /* #endif */
  87. this.firstChange = false;
  88. // 重置changeFromInner的值为false,标识下一次引起默认为外部引起的
  89. this.changeFromInner = false;
  90. },
  91. },
  92. },
  93. computed: {
  94. // 组件的类名
  95. textareaClass() {
  96. let classes = [],
  97. {
  98. border,
  99. disabled,
  100. shape
  101. } = this;
  102. border === "surround" &&
  103. (classes = classes.concat(["u-border", "u-textarea--radius"]));
  104. border === "bottom" &&
  105. (classes = classes.concat([
  106. "u-border-bottom",
  107. "u-textarea--no-radius",
  108. ]));
  109. disabled && classes.push("u-textarea--disabled");
  110. return classes.join(" ");
  111. },
  112. // 组件的样式
  113. textareaStyle() {
  114. const style = {};
  115. // #ifdef APP-NVUE
  116. // 由于textarea在安卓nvue上的差异性,需要额外再调整其内边距
  117. if (uni.$u.os() === "android") {
  118. style.paddingTop = "6px";
  119. style.paddingLeft = "9px";
  120. style.paddingBottom = "3px";
  121. style.paddingRight = "6px";
  122. }
  123. // #endif
  124. return uni.$u.deepMerge(style, uni.$u.addStyle(this.customStyle));
  125. },
  126. },
  127. methods: {
  128. // 在微信小程序中,不支持将函数当做props参数,故只能通过ref形式调用
  129. setFormatter(e) {
  130. this.innerFormatter = e
  131. },
  132. onFocus(e) {
  133. this.$emit("focus", e);
  134. },
  135. onBlur(e) {
  136. this.$emit("blur", e);
  137. // 尝试调用u-form的验证方法
  138. uni.$u.formValidate(this, "blur");
  139. },
  140. onLinechange(e) {
  141. this.$emit("linechange", e);
  142. },
  143. onInput(e) {
  144. let {
  145. value = ""
  146. } = e.detail || {};
  147. // 格式化过滤方法
  148. const formatter = this.formatter || this.innerFormatter
  149. const formatValue = formatter(value)
  150. // 为了避免props的单向数据流特性,需要先将innerValue值设置为当前值,再在$nextTick中重新赋予设置后的值才有效
  151. this.innerValue = value
  152. this.$nextTick(() => {
  153. this.innerValue = formatValue;
  154. this.valueChange();
  155. })
  156. },
  157. // 内容发生变化,进行处理
  158. valueChange() {
  159. const value = this.innerValue;
  160. this.$nextTick(() => {
  161. this.$emit("input", value);
  162. // 标识value值的变化是由内部引起的
  163. this.changeFromInner = true;
  164. this.$emit("change", value);
  165. // 尝试调用u-form的验证方法
  166. uni.$u.formValidate(this, "change");
  167. });
  168. },
  169. onConfirm(e) {
  170. this.$emit("confirm", e);
  171. },
  172. onKeyboardheightchange(e) {
  173. this.$emit("keyboardheightchange", e);
  174. },
  175. },
  176. };
  177. </script>
  178. <style lang="scss" scoped>
  179. @import "../../libs/css/components.scss";
  180. .u-textarea {
  181. border-radius: 4px;
  182. background-color: #fff;
  183. position: relative;
  184. @include flex;
  185. flex: 1;
  186. padding: 9px;
  187. &--radius {
  188. border-radius: 4px;
  189. }
  190. &--no-radius {
  191. border-radius: 0;
  192. }
  193. &--disabled {
  194. background-color: #f5f7fa;
  195. }
  196. &__field {
  197. flex: 1;
  198. font-size: 15px;
  199. color: $u-content-color;
  200. width: 100%;
  201. }
  202. &__count {
  203. position: absolute;
  204. right: 5px;
  205. bottom: 2px;
  206. font-size: 12px;
  207. color: $u-tips-color;
  208. background-color: #ffffff;
  209. padding: 1px 4px;
  210. }
  211. }
  212. </style>