| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455 |
- <template>
- <view :class="classes" @click="onClick">
- <template v-if="$slots.input">
- <view
- v-if="label"
- class="nut-input-label"
- :class="labelClass"
- :style="{
- width: `${labelWidth}px`,
- textAlign: labelAlign
- }"
- >
- <view class="label-string">
- {{ label }}
- {{ colon ? ':' : '' }}
- </view>
- </view>
- <view class="nut-input-value">
- <view class="nut-input-inner" @click="onClickInput">
- <slot name="input"></slot>
- </view>
- </view>
- </template>
- <template v-else>
- <view v-if="leftIcon && leftIcon.length > 0" class="nut-input-left-icon" @click="onClickLeftIcon">
- <nut-icon :name="leftIcon" :size="leftIconSize"></nut-icon>
- </view>
- <view
- v-if="label"
- class="nut-input-label"
- :class="labelClass"
- :style="{
- width: `${labelWidth}px`,
- textAlign: labelAlign
- }"
- >
- <view class="label-string">
- {{ label }}
- {{ colon ? ':' : '' }}
- </view>
- </view>
- <view class="nut-input-value">
- <view class="nut-input-inner">
- <view class="nut-input-box">
- <textarea
- v-if="type == 'textarea'"
- class="input-text"
- ref="inputRef"
- :style="stylesTextarea"
- :maxlength="maxLength"
- :placeholder="placeholder || translate('placeholder')"
- :disabled="disabled"
- :readonly="readonly"
- :value="modelValue"
- :formatTrigger="formatTrigger"
- :autofocus="autofocus"
- @input="onInput"
- @focus="onFocus"
- @blur="onBlur"
- @click="onClickInput"
- />
- <input
- v-else
- class="input-text"
- ref="inputRef"
- :style="styles"
- :type="inputType(type)"
- :maxlength="maxLength"
- :placeholder="placeholder || translate('placeholder')"
- :disabled="disabled"
- :readonly="readonly"
- :value="modelValue"
- :formatTrigger="formatTrigger"
- :autofocus="autofocus"
- @input="onInput"
- @focus="onFocus"
- @blur="onBlur"
- @click="onClickInput"
- />
- </view>
- <view class="nut-input-clear-box">
- <nut-icon
- class="nut-input-clear"
- v-if="clearable && !readonly"
- v-show="active && modelValue.length > 0"
- :name="clearIcon"
- :size="clearSize"
- @click="clear"
- >
- </nut-icon>
- </view>
- <view v-if="rightIcon && rightIcon.length > 0" class="nut-input-right-icon" @click="onClickRightIcon">
- <nut-icon :name="rightIcon" :size="rightIconSize"></nut-icon>
- </view>
- <slot v-if="$slots.button" name="button" class="nut-input-button"></slot>
- </view>
- <view v-if="showWordLimit && maxLength" class="nut-input-word-limit">
- <span class="nut-input-word-num">{{ modelValue ? modelValue.length : 0 }}</span
- >/{{ maxLength }}
- </view>
- <view
- v-if="errorMessage"
- class="nut-input-error-message"
- :style="{
- textAlign: errorMessageAlign
- }"
- >
- {{ errorMessage }}
- </view>
- </view>
- </template>
- </view>
- </template>
- <script lang="ts">
- import { PropType, ref, reactive, computed, onMounted, watch, nextTick, inject } from 'vue';
- import { createComponent } from '@/packages/utils/create';
- import { formatNumber } from './util';
- const { componentName, create, translate } = createComponent('input');
- export default create({
- props: {
- ref: {
- type: String,
- default: ''
- },
- type: {
- type: String as PropType<import('./type').InputType>,
- default: 'text'
- },
- modelValue: {
- type: [String, Number],
- default: ''
- },
- placeholder: {
- type: String,
- default: ''
- },
- label: {
- type: String,
- default: ''
- },
- labelClass: {
- type: String,
- default: ''
- },
- labelWidth: {
- type: [String, Number],
- default: '80'
- },
- labelAlign: {
- type: String as PropType<import('./type').InputAlignType>,
- default: 'left'
- },
- colon: {
- type: Boolean,
- default: false
- },
- inputAlign: {
- type: String,
- default: 'left'
- },
- center: {
- type: Boolean,
- default: false
- },
- required: {
- type: Boolean,
- default: false
- },
- disabled: {
- type: Boolean,
- default: false
- },
- readonly: {
- type: Boolean,
- default: false
- },
- error: {
- type: Boolean,
- default: false
- },
- maxLength: {
- type: [String, Number],
- default: '9999'
- },
- leftIcon: {
- type: String,
- default: ''
- },
- leftIconSize: {
- type: [String, Number],
- default: ''
- },
- rightIcon: {
- type: String,
- default: ''
- },
- rightIconSize: {
- type: [String, Number],
- default: ''
- },
- clearable: {
- type: Boolean,
- default: false
- },
- clearIcon: {
- type: String,
- default: 'mask-close'
- },
- clearSize: {
- type: [String, Number],
- default: '14'
- },
- border: {
- type: Boolean,
- default: true
- },
- formatTrigger: {
- type: String as PropType<import('./type').InputFormatTrigger>,
- default: 'onChange'
- },
- formatter: {
- type: Function as PropType<(value: string) => string>,
- default: null
- },
- rules: {
- type: Array as PropType<import('./type').InputRule>,
- default: []
- },
- errorMessage: {
- type: String,
- default: ''
- },
- errorMessageAlign: {
- type: String as PropType<import('./type').InputAlignType>,
- default: ''
- },
- rows: {
- type: [String, Number],
- default: null
- },
- showWordLimit: {
- type: Boolean,
- default: false
- },
- autofocus: {
- type: Boolean,
- default: false
- }
- },
- emits: [
- 'update:modelValue',
- 'change',
- 'blur',
- 'focus',
- 'clear',
- 'keypress',
- 'click-input',
- 'click-left-icon',
- 'click-right-icon'
- ],
- setup(props, { emit, slots }) {
- const active = ref(false);
- const inputRef = ref<HTMLInputElement>();
- const customValue = ref<() => unknown>();
- const getModelValue = () => String(props.modelValue ?? '');
- // const form = inject('form');
- const state = reactive({
- focused: false,
- validateFailed: false, // 校验失败
- validateMessage: '' // 校验信息
- });
- const classes = computed(() => {
- const prefixCls = componentName;
- return {
- [prefixCls]: true,
- center: props.center,
- [`${prefixCls}-disabled`]: props.disabled,
- [`${prefixCls}-required`]: props.required,
- [`${prefixCls}-error`]: props.error,
- [`${prefixCls}-border`]: props.border
- };
- });
- const styles: any = computed(() => {
- return {
- textAlign: props.inputAlign
- };
- });
- const stylesTextarea: any = computed(() => {
- return {
- textAlign: props.inputAlign,
- height: Number(props.rows) * 24 + 'px'
- };
- });
- const inputType = (type: string) => {
- if (type === 'number') {
- return 'text';
- } else if (type === 'digit') {
- return 'tel';
- } else {
- return type;
- }
- };
- // const formValue = computed(() => {
- // if (customValue.value && slots.input) {
- // return customValue.value();
- // }
- // return props.modelValue;
- // });
- const onInput = (event: Event) => {
- const input = event.target as HTMLInputElement;
- let value = input.value;
- if (props.maxLength && value.length > Number(props.maxLength)) {
- value = value.slice(0, Number(props.maxLength));
- }
- updateValue(value);
- };
- const updateValue = (value: string, trigger: import('./type').InputFormatTrigger = 'onChange') => {
- if (props.type === 'digit') {
- value = formatNumber(value, false, false);
- }
- if (props.type === 'number') {
- // console.log('value', value)
- value = formatNumber(value, true, true);
- }
- if (props.formatter && trigger === props.formatTrigger) {
- value = props.formatter(value);
- }
- if (inputRef?.value?.value !== value) {
- inputRef.value.value = value;
- }
- if (value !== props.modelValue) {
- emit('update:modelValue', value);
- emit('change', value);
- }
- };
- const onFocus = (event: Event) => {
- if (props.disabled || props.readonly) {
- return;
- }
- const input = event.target as HTMLInputElement;
- let value = input.value;
- active.value = true;
- emit('focus', value, event);
- // if (getProp('readonly')) {
- // blur();
- // }
- };
- const onBlur = (event: Event) => {
- if (props.disabled || props.readonly) {
- return;
- }
- setTimeout(() => {
- active.value = false;
- }, 200);
- const input = event.target as HTMLInputElement;
- let value = input.value;
- if (props.maxLength && value.length > Number(props.maxLength)) {
- value = value.slice(0, Number(props.maxLength));
- }
- updateValue(getModelValue(), 'onBlur');
- emit('blur', value, event);
- };
- const clear = (event: Event) => {
- if (props.disabled) return;
- emit('update:modelValue', '', event);
- emit('change', '', event);
- emit('clear', '', event);
- };
- const resetValidation = () => {
- if (state.validateFailed) {
- state.validateFailed = false;
- state.validateMessage = '';
- }
- };
- const onClickInput = (event: MouseEvent) => {
- if (props.disabled) {
- return;
- }
- emit('click-input', event);
- };
- const onClickLeftIcon = (event: MouseEvent) => {
- if (props.disabled) {
- return;
- }
- emit('click-left-icon', event);
- };
- const onClickRightIcon = (event: MouseEvent) => {
- if (props.disabled) {
- return;
- }
- emit('click-right-icon', event);
- };
- const onClick = (e: PointerEvent) => {
- if (props.disabled) {
- e.stopImmediatePropagation();
- return;
- }
- };
- watch(
- () => props.modelValue,
- () => {
- updateValue(getModelValue());
- resetValidation();
- }
- );
- onMounted(() => {
- updateValue(getModelValue(), props.formatTrigger);
- });
- return {
- inputRef,
- active,
- classes,
- styles,
- stylesTextarea,
- inputType,
- onInput,
- onFocus,
- onBlur,
- clear,
- onClickInput,
- onClickLeftIcon,
- onClickRightIcon,
- onClick,
- translate
- };
- }
- });
- </script>
|