blob: b562798acea97a6231a9f7148bd3ee17f64c516e [file] [log] [blame]
guangchao.xu070005a2020-12-07 09:56:40 +08001<template>
2 <view class="u-field" :class="{'u-border-top': borderTop, 'u-border-bottom': borderBottom }">
3 <view class="u-field-inner" :class="[type == 'textarea' ? 'u-textarea-inner' : '', 'u-label-postion-' + labelPosition]">
4 <view class="u-label" :class="[required ? 'u-required' : '']" :style="{
5 justifyContent: justifyContent,
6 flex: labelPosition == 'left' ? `0 0 ${labelWidth}rpx` : '1'
7 }">
8 <view class="u-icon-wrap" v-if="icon">
9 <u-icon size="32" :custom-style="iconStyle" :name="icon" :color="iconColor" class="u-icon"></u-icon>
10 </view>
11 <slot name="icon"></slot>
12 <text class="u-label-text" :class="[this.$slots.icon || icon ? 'u-label-left-gap' : '']">{{ label }}</text>
13 </view>
14 <view class="fild-body">
15 <view class="u-flex-1 u-flex" :style="[inputWrapStyle]">
16 <textarea v-if="type == 'textarea'" class="u-flex-1 u-textarea-class" :style="[fieldStyle]" :value="value"
17 :placeholder="placeholder" :placeholderStyle="placeholderStyle" :disabled="disabled" :maxlength="inputMaxlength"
18 :focus="focus" :autoHeight="autoHeight" :fixed="fixed" @input="onInput" @blur="onBlur" @focus="onFocus" @confirm="onConfirm"
19 @tap="fieldClick" />
20 <input
21 v-else
22 :style="[fieldStyle]"
23 :type="type"
24 class="u-flex-1 u-field__input-wrap"
25 :value="value"
26 :password="password || this.type === 'password'"
27 :placeholder="placeholder"
28 :placeholderStyle="placeholderStyle"
29 :disabled="disabled"
30 :maxlength="inputMaxlength"
31 :focus="focus"
32 :confirmType="confirmType"
33 @focus="onFocus"
34 @blur="onBlur"
35 @input="onInput"
36 @confirm="onConfirm"
37 @tap="fieldClick"
38 />
39 </view>
40 <u-icon :size="clearSize" v-if="clearable && value != '' && focused" name="close-circle-fill" color="#c0c4cc" class="u-clear-icon" @click="onClear"/>
41 <view class="u-button-wrap"><slot name="right" /></view>
42 <u-icon v-if="rightIcon" @click="rightIconClick" :name="rightIcon" color="#c0c4cc" :style="[rightIconStyle]" size="26" class="u-arror-right" />
43 </view>
44 </view>
45 <view v-if="errorMessage !== false && errorMessage != ''" class="u-error-message" :style="{
46 paddingLeft: labelWidth + 'rpx'
47 }">{{ errorMessage }}</view>
48 </view>
49</template>
50
51<script>
52/**
53 * field 输入框
54 * @description 借助此组件,可以实现表单的输入, 有"text"和"textarea"类型的,此外,借助uView的picker和actionSheet组件可以快速实现上拉菜单,时间,地区选择等, 为表单解决方案的利器。
55 * @tutorial https://www.uviewui.com/components/field.html
56 * @property {String} type 输入框的类型(默认text)
57 * @property {String} icon label左边的图标,限uView的图标名称
58 * @property {Object} icon-style 左边图标的样式,对象形式
59 * @property {Boolean} right-icon 输入框右边的图标名称,限uView的图标名称(默认false)
60 * @property {Boolean} required 是否必填,左边您显示红色"*"号(默认false)
61 * @property {String} label 输入框左边的文字提示
62 * @property {Boolean} password 是否密码输入方式(用点替换文字),type为text时有效(默认false)
63 * @property {Boolean} clearable 是否显示右侧清空内容的图标控件(输入框有内容,且获得焦点时才显示),点击可清空输入框内容(默认true)
64 * @property {Number String} label-width label的宽度,单位rpx(默认130)
65 * @property {String} label-align label的文字对齐方式(默认left)
66 * @property {Object} field-style 自定义输入框的样式,对象形式
67 * @property {Number | String} clear-size 清除图标的大小,单位rpx(默认30)
68 * @property {String} input-align 输入框内容对齐方式(默认left)
69 * @property {Boolean} border-bottom 是否显示field的下边框(默认true)
70 * @property {Boolean} border-top 是否显示field的上边框(默认false)
71 * @property {String} icon-color 左边通过icon配置的图标的颜色(默认#606266)
72 * @property {Boolean} auto-height 是否自动增高输入区域,type为textarea时有效(默认true)
73 * @property {String Boolean} error-message 显示的错误提示内容,如果为空字符串或者false,则不显示错误信息
74 * @property {String} placeholder 输入框的提示文字
75 * @property {String} placeholder-style placeholder的样式(内联样式,字符串),如"color: #ddd"
76 * @property {Boolean} focus 是否自动获得焦点(默认false)
77 * @property {Boolean} fixed 如果type为textarea,且在一个"position:fixed"的区域,需要指明为true(默认false)
78 * @property {Boolean} disabled 是否不可输入(默认false)
79 * @property {Number String} maxlength 最大输入长度,设置为 -1 的时候不限制最大长度(默认140)
80 * @property {String} confirm-type 设置键盘右下角按钮的文字,仅在type="text"时生效(默认done)
81 * @event {Function} input 输入框内容发生变化时触发
82 * @event {Function} focus 输入框获得焦点时触发
83 * @event {Function} blur 输入框失去焦点时触发
84 * @event {Function} confirm 点击完成按钮时触发
85 * @event {Function} right-icon-click 通过right-icon生成的图标被点击时触发
86 * @event {Function} click 输入框被点击或者通过right-icon生成的图标被点击时触发,这样设计是考虑到传递右边的图标,一般都为需要弹出"picker"等操作时的场景,点击倒三角图标,理应发出此事件,见上方说明
87 * @example <u-field v-model="mobile" label="手机号" required :error-message="errorMessage"></u-field>
88 */
89export default {
90 name:"u-field",
91 props: {
92 icon: String,
93 rightIcon: String,
94 // arrowDirection: {
95 // type: String,
96 // default: 'right'
97 // },
98 required: Boolean,
99 label: String,
100 password: Boolean,
101 clearable: {
102 type: Boolean,
103 default: true
104 },
105 // 左边标题的宽度单位rpx
106 labelWidth: {
107 type: [Number, String],
108 default: 130
109 },
110 // 对齐方式,left|center|right
111 labelAlign: {
112 type: String,
113 default: 'left'
114 },
115 inputAlign: {
116 type: String,
117 default: 'left'
118 },
119 iconColor: {
120 type: String,
121 default: '#606266'
122 },
123 autoHeight: {
124 type: Boolean,
125 default: true
126 },
127 errorMessage: {
128 type: [String, Boolean],
129 default: ''
130 },
131 placeholder: String,
132 placeholderStyle: String,
133 focus: Boolean,
134 fixed: Boolean,
135 value: [Number, String],
136 type: {
137 type: String,
138 default: 'text'
139 },
140 disabled: {
141 type: Boolean,
142 default: false
143 },
144 maxlength: {
145 type: [Number, String],
146 default: 140
147 },
148 confirmType: {
149 type: String,
150 default: 'done'
151 },
152 // lable的位置,可选为 left-左边,top-上边
153 labelPosition: {
154 type: String,
155 default: 'left'
156 },
157 // 输入框的自定义样式
158 fieldStyle: {
159 type: Object,
160 default() {
161 return {}
162 }
163 },
164 // 清除按钮的大小
165 clearSize: {
166 type: [Number, String],
167 default: 30
168 },
169 // lable左边的图标样式,对象形式
170 iconStyle: {
171 type: Object,
172 default() {
173 return {}
174 }
175 },
176 // 是否显示上边框
177 borderTop: {
178 type: Boolean,
179 default: false
180 },
181 // 是否显示下边框
182 borderBottom: {
183 type: Boolean,
184 default: true
185 },
186 // 是否自动去除两端的空格
187 trim: {
188 type: Boolean,
189 default: true
190 }
191 },
192 data() {
193 return {
194 focused: false,
195 itemIndex: 0,
196 };
197 },
198 computed: {
199 inputWrapStyle() {
200 let style = {};
201 style.textAlign = this.inputAlign;
202 // 判断lable的位置,如果是left的话,让input左边两边有间隙
203 if(this.labelPosition == 'left') {
204 style.margin = `0 8rpx`;
205 } else {
206 // 如果lable是top的,input的左边就没必要有间隙了
207 style.marginRight = `8rpx`;
208 }
209 return style;
210 },
211 rightIconStyle() {
212 let style = {};
213 if (this.arrowDirection == 'top') style.transform = 'roate(-90deg)';
214 if (this.arrowDirection == 'bottom') style.transform = 'roate(90deg)';
215 else style.transform = 'roate(0deg)';
216 return style;
217 },
218 labelStyle() {
219 let style = {};
220 if(this.labelAlign == 'left') style.justifyContent = 'flext-start';
221 if(this.labelAlign == 'center') style.justifyContent = 'center';
222 if(this.labelAlign == 'right') style.justifyContent = 'flext-end';
223 return style;
224 },
225 // uni不支持在computed中写style.justifyContent = 'center'的形式,故用此方法
226 justifyContent() {
227 if(this.labelAlign == 'left') return 'flex-start';
228 if(this.labelAlign == 'center') return 'center';
229 if(this.labelAlign == 'right') return 'flex-end';
230 },
231 // 因为uniapp的input组件的maxlength组件必须要数值,这里转为数值,给用户可以传入字符串数值
232 inputMaxlength() {
233 return Number(this.maxlength)
234 },
235 // label的位置
236 fieldInnerStyle() {
237 let style = {};
238 if(this.labelPosition == 'left') {
239 style.flexDirection = 'row';
240 } else {
241 style.flexDirection = 'column';
242 }
243
244 return style;
245 }
246 },
247 methods: {
248 onInput(event) {
249 let value = event.detail.value;
250 // 判断是否去除空格
251 if(this.trim) value = this.$u.trim(value);
252 this.$emit('input', value);
253 },
254 onFocus(event) {
255 this.focused = true;
256 this.$emit('focus', event);
257 },
258 onBlur(event) {
259 // 最开始使用的是监听图标@touchstart事件,自从hx2.8.4后,此方法在微信小程序出错
260 // 这里改为监听点击事件,手点击清除图标时,同时也发生了@blur事件,导致图标消失而无法点击,这里做一个延时
261 setTimeout(() => {
262 this.focused = false;
263 }, 100)
264 this.$emit('blur', event);
265 },
266 onConfirm(e) {
267 this.$emit('confirm', e.detail.value);
268 },
269 onClear(event) {
270 this.$emit('input', '');
271 },
272 rightIconClick() {
273 this.$emit('right-icon-click');
274 this.$emit('click');
275 },
276 fieldClick() {
277 this.$emit('click');
278 }
279 }
280};
281</script>
282
283<style lang="scss" scoped>
284@import "../../libs/css/style.components.scss";
285
286.u-field {
287 font-size: 28rpx;
288 padding: 20rpx 28rpx;
289 text-align: left;
290 position: relative;
291 color: $u-main-color;
292}
293
294.u-field-inner {
295 @include vue-flex;
296 align-items: center;
297}
298
299.u-textarea-inner {
300 align-items: flex-start;
301}
302
303.u-textarea-class {
304 min-height: 96rpx;
305 width: auto;
306 font-size: 28rpx;
307}
308
309.fild-body {
310 @include vue-flex;
311 flex: 1;
312 align-items: center;
313}
314
315.u-arror-right {
316 margin-left: 8rpx;
317}
318
319.u-label-text {
320 /* #ifndef APP-NVUE */
321 display: inline-flex;
322 /* #endif */
323}
324
325.u-label-left-gap {
326 margin-left: 6rpx;
327}
328
329.u-label-postion-top {
330 flex-direction: column;
331 align-items: flex-start;
332}
333
334.u-label {
335 width: 130rpx;
336 flex: 1 1 130rpx;
337 text-align: left;
338 position: relative;
339 @include vue-flex;
340 align-items: center;
341}
342
343.u-required::before {
344 content: '*';
345 position: absolute;
346 left: -16rpx;
347 font-size: 14px;
348 color: $u-type-error;
349 height: 9px;
350 line-height: 1;
351}
352
353.u-field__input-wrap {
354 position: relative;
355 overflow: hidden;
356 font-size: 28rpx;
357 height: 48rpx;
358 flex: 1;
359 width: auto;
360}
361
362.u-clear-icon {
363 @include vue-flex;
364 align-items: center;
365}
366
367.u-error-message {
368 color: $u-type-error;
369 font-size: 26rpx;
370 text-align: left;
371}
372
373.placeholder-style {
374 color: rgb(150, 151, 153);
375}
376
377.u-input-class {
378 font-size: 28rpx;
379}
380
381.u-button-wrap {
382 margin-left: 8rpx;
383}
384</style>