index.vue 9.0 KB
Newer Older
fxy060608's avatar
fxy060608 已提交
1
<template>
2
  <uni-input v-on="$listeners">
3 4 5
    <div
      ref="wrapper"
      class="uni-input-wrapper"
fxy060608's avatar
fxy060608 已提交
6
    >
7
      <div
8
        v-show="!(composing || valueSync.length || cachedValue === '-')"
9 10 11 12
        ref="placeholder"
        :style="placeholderStyle"
        :class="placeholderClass"
        class="uni-input-placeholder"
13 14
        v-text="placeholder"
      />
15
      <input
16
        v-if="!disabled || !fixColor"
17
        ref="input"
18
        v-model="valueSync"
19
        v-keyboard
20
        v-field
21 22 23
        :disabled="disabled"
        :type="inputType"
        :maxlength="maxlength"
24
        :step="step"
25
        :enterkeyhint="confirmType"
26
        :pattern="type === 'number' ? '[0-9]*' : null"
27
        class="uni-input-input"
28
        :autocomplete="autocomplete"
29
        @change.stop
30 31 32
        @focus="_onFocus"
        @blur="_onBlur"
        @input.stop="_onInput"
33 34
        @compositionstart.stop="_onComposition"
        @compositionend.stop="_onComposition"
35
        @keyup.enter.stop="_onKeyup"
36
      >
37
      <!-- fix: 禁止 readonly 状态获取焦点 -->
38 39 40 41 42 43 44 45 46 47
      <input
        v-if="disabled && fixColor"
        ref="input"
        :value="valueSync"
        tabindex="-1"
        :readonly="disabled"
        :type="inputType"
        :maxlength="maxlength"
        :step="step"
        class="uni-input-input"
48
        @focus="($event) => $event.target.blur()"
49
      >
fxy060608's avatar
fxy060608 已提交
50 51 52 53 54
    </div>
  </uni-input>
</template>
<script>
import {
55
  field
fxy060608's avatar
fxy060608 已提交
56
} from 'uni-mixins'
57 58
import { kebabCase } from 'uni-shared'
const INPUT_TYPES = ['text', 'number', 'idcard', 'digit', 'password', 'tel']
59
const NUMBER_TYPES = ['number', 'digit']
60
const AUTOCOMPLETES = ['off', 'one-time-code']
fxy060608's avatar
fxy060608 已提交
61 62
export default {
  name: 'Input',
63
  mixins: [field],
fxy060608's avatar
fxy060608 已提交
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
  props: {
    name: {
      type: String,
      default: ''
    },
    type: {
      type: String,
      default: 'text'
    },
    password: {
      type: [Boolean, String],
      default: false
    },
    placeholder: {
      type: String,
      default: ''
    },
    placeholderStyle: {
      type: String,
      default: ''
    },
    placeholderClass: {
      type: String,
87
      default: 'input-placeholder'
fxy060608's avatar
fxy060608 已提交
88 89 90 91 92 93 94 95 96 97 98 99
    },
    disabled: {
      type: [Boolean, String],
      default: false
    },
    maxlength: {
      type: [Number, String],
      default: 140
    },
    confirmType: {
      type: String,
      default: 'done'
100 101 102 103
    },
    textContentType: {
      type: String,
      default: ''
fxy060608's avatar
fxy060608 已提交
104 105 106 107
    }
  },
  data () {
    return {
108
      wrapperHeight: 0,
D
fix:  
DCloud_LXH 已提交
109
      cachedValue: ''
fxy060608's avatar
fxy060608 已提交
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
    }
  },
  computed: {
    inputType: function () {
      let type = ''
      switch (this.type) {
        case 'text':
          this.confirmType === 'search' && (type = 'search')
          break
        case 'idcard':
          // TODO 可能要根据不同平台进行区分处理
          type = 'text'
          break
        case 'digit':
          type = 'number'
          break
        default:
127
          type = ~INPUT_TYPES.indexOf(this.type) ? this.type : 'text'
fxy060608's avatar
fxy060608 已提交
128 129 130 131 132 133
          break
      }
      return this.password ? 'password' : type
    },
    step () {
      // 处理部分设备中无法输入小数点的问题
134
      return ~NUMBER_TYPES.indexOf(this.type) ? '0.000000000000000001' : ''
135 136 137 138 139 140 141 142 143 144
    },
    autocomplete () {
      const camelizeIndex = AUTOCOMPLETES.indexOf(this.textContentType)
      const kebabCaseIndex = AUTOCOMPLETES.indexOf(kebabCase(this.textContentType))
      const index = camelizeIndex !== -1
        ? camelizeIndex
        : kebabCaseIndex !== -1
          ? kebabCaseIndex
          : 0
      return AUTOCOMPLETES[index]
fxy060608's avatar
fxy060608 已提交
145 146 147 148
    }
  },
  watch: {
    maxlength (value) {
149 150
      const realValue = this.valueSync.slice(0, parseInt(value, 10))
      realValue !== this.valueSync && (this.valueSync = realValue)
151 152 153 154 155
    },
    valueSync (value) {
      if (this.type === 'number' && !(this.cachedValue === '-' && value === '')) {
        this.cachedValue = value
      }
fxy060608's avatar
fxy060608 已提交
156 157 158 159 160 161 162 163 164 165
    }
  },
  created () {
    this.$dispatch('Form', 'uni-form-group-update', {
      type: 'add',
      vm: this
    })
  },
  mounted () {
    if (this.confirmType === 'search') {
166
      const formElem = document.createElement('form')
fxy060608's avatar
fxy060608 已提交
167 168 169 170
      formElem.action = ''
      formElem.onsubmit = function () {
        return false
      }
171
      formElem.className = 'uni-input-form'
fxy060608's avatar
fxy060608 已提交
172 173 174 175
      formElem.appendChild(this.$refs.input)
      this.$refs.wrapper.appendChild(formElem)
    }

176 177 178 179 180 181 182 183
    let $vm = this
    while ($vm) {
      const scopeId = $vm.$options._scopeId
      if (scopeId) {
        this.$refs.placeholder.setAttribute(scopeId, '')
      }
      $vm = $vm.$parent
    }
fxy060608's avatar
fxy060608 已提交
184 185 186 187 188 189 190 191 192
  },
  beforeDestroy () {
    this.$dispatch('Form', 'uni-form-group-update', {
      type: 'remove',
      vm: this
    })
  },
  methods: {
    _onKeyup ($event) {
193
      const input = $event.target
194
      this.$trigger('confirm', $event, {
195
        value: input.value
196
      })
197 198 199
      if (!this.confirmHold) {
        input.blur()
      }
fxy060608's avatar
fxy060608 已提交
200
    },
201
    _onInput ($event, force) {
202 203
      let outOfMaxlength = false

fxy060608's avatar
fxy060608 已提交
204 205 206
      if (this.composing) {
        return
      }
207

208
      if (this.inputType === 'number') {
209
        // type="number" 不支持 maxlength 属性,因此需要主动限制长度。
210 211
        const maxlength = parseInt(this.maxlength, 10)
        if (maxlength > 0 && $event.target.value.length > maxlength) {
212 213 214 215 216 217 218 219 220
          // 输入前字符长度超出范围,则不触发input,且将值还原
          // 否则截取一定长度且触发input
          if (this.cachedValue.length === maxlength) {
            this.valueSync = this.cachedValue
            outOfMaxlength = true
          } else {
            $event.target.value = $event.target.value.slice(0, maxlength)
            this.valueSync = $event.target.value
          }
221 222
        }

223 224 225 226 227 228 229 230 231
        // 数字类型输入错误时无法获取具体的值,自定义校验和纠正。
        this.__clearCachedValue && $event.target.removeEventListener('blur', this.__clearCachedValue)
        if ($event.target.validity && !$event.target.validity.valid) {
          if ((!this.cachedValue && $event.data === '-') || (this.cachedValue[0] === '-' && $event.inputType === 'deleteContentBackward')) {
            this.cachedValue = '-'
            const clearCachedValue = this.__clearCachedValue = () => {
              this.cachedValue = ''
            }
            $event.target.addEventListener('blur', clearCachedValue)
232 233
            return
          }
234 235 236 237 238
          this.cachedValue = this.valueSync = $event.target.value = this.cachedValue === '-' ? '' : this.cachedValue
          // 输入非法字符不触发 input 事件
          return
        } else {
          this.cachedValue = this.valueSync
239
        }
240
      }
fxy060608's avatar
fxy060608 已提交
241

242
      if (outOfMaxlength) return
243

244
      this.$triggerInput($event, {
245
        value: this.valueSync
246
      }, force)
fxy060608's avatar
fxy060608 已提交
247 248 249 250
    },
    _onComposition ($event) {
      if ($event.type === 'compositionstart') {
        this.composing = true
251
      } else if (this.composing) {
fxy060608's avatar
fxy060608 已提交
252
        this.composing = false
253 254
        // 部分输入法 compositionend 事件可能晚于 input
        this._onInput($event)
fxy060608's avatar
fxy060608 已提交
255 256 257
      }
    },
    _resetFormData () {
258
      this.valueSync = ''
fxy060608's avatar
fxy060608 已提交
259 260 261
    },
    _getFormData () {
      return this.name ? {
262
        value: this.valueSync,
fxy060608's avatar
fxy060608 已提交
263 264 265 266 267 268 269
        key: this.name
      } : {}
    }
  }
}
</script>
<style>
270 271 272 273 274 275
uni-input {
  display: block;
  font-size: 16px;
  line-height: 1.4em;
  height: 1.4em;
  min-height: 1.4em;
276
  overflow: hidden;
277
}
fxy060608's avatar
fxy060608 已提交
278

279 280 281
uni-input[hidden] {
  display: none;
}
fxy060608's avatar
fxy060608 已提交
282

283 284 285 286 287 288 289 290 291 292
.uni-input-wrapper,
.uni-input-placeholder,
.uni-input-form,
.uni-input-input {
  outline: none;
  border: none;
  padding: 0;
  margin: 0;
  text-decoration: inherit;
}
fxy060608's avatar
fxy060608 已提交
293

294 295
.uni-input-wrapper,
.uni-input-form {
296
  display: flex;
297 298 299
  position: relative;
  width: 100%;
  height: 100%;
300 301
  flex-direction: column;
  justify-content: center;
302
}
fxy060608's avatar
fxy060608 已提交
303

304
.uni-input-placeholder,
Q
qiang 已提交
305
.uni-input-input {
306 307
  width: 100%;
}
fxy060608's avatar
fxy060608 已提交
308

309
.uni-input-placeholder {
310
  position: absolute;
311
  top: auto !important;
312
  left: 0;
313 314 315 316 317
  color: gray;
  overflow: hidden;
  text-overflow: clip;
  white-space: pre;
  word-break: keep-all;
318
  pointer-events: none;
319
  line-height: inherit;
320
}
fxy060608's avatar
fxy060608 已提交
321

322
.uni-input-input {
323
  position: relative;
324 325
  display: block;
  height: 100%;
326 327
  background: none;
  color: inherit;
328
  opacity: 1;
329 330 331 332 333 334 335 336
  font: inherit;
  line-height: inherit;
  letter-spacing: inherit;
  text-align: inherit;
  text-indent: inherit;
  text-transform: inherit;
  text-shadow: inherit;
}
fxy060608's avatar
fxy060608 已提交
337

Q
qiang 已提交
338 339
.uni-input-input[type="search"]::-webkit-search-cancel-button,
.uni-input-input[type="search"]::-webkit-search-decoration {
340 341
  display: none;
}
fxy060608's avatar
fxy060608 已提交
342

343 344 345 346 347
.uni-input-input::-webkit-outer-spin-button,
.uni-input-input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
fxy060608's avatar
fxy060608 已提交
348

349 350 351
.uni-input-input[type="number"] {
  -moz-appearance: textfield;
}
352 353 354 355 356

.uni-input-input:disabled {
  /* 用于重置iOS14以下禁用状态文字颜色 */
  -webkit-text-fill-color: currentcolor;
}
357
</style>