|
|
@@ -1,20 +1,23 @@
|
|
|
<template>
|
|
|
<view :class="classes">
|
|
|
- <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 v-if="leftIcon && leftIcon.length > 0" class="nut-input-left-icon" @click="onClickLeftIcon">
|
|
|
+ <nut-icon :name="leftIcon" v-bind="$attrs" :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>
|
|
|
+ <template v-if="$slots.input">
|
|
|
<view class="nut-input-value">
|
|
|
<view class="nut-input-inner" @click="onClickInput">
|
|
|
<slot name="input"></slot>
|
|
|
@@ -22,66 +25,27 @@
|
|
|
</view>
|
|
|
</template>
|
|
|
<template v-else>
|
|
|
- <view v-if="leftIcon && leftIcon.length > 0" class="nut-input-left-icon" @click="onClickLeftIcon">
|
|
|
- <nut-icon :name="leftIcon" v-bind="$attrs" :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"
|
|
|
- placeholder-class="nut-placeholder"
|
|
|
- :disabled="disabled"
|
|
|
- :readonly="readonly"
|
|
|
- :value="modelValue"
|
|
|
- :formatTrigger="formatTrigger"
|
|
|
- :adjust-position="adjustPosition"
|
|
|
- :always-system="alwaysSystem"
|
|
|
- @input="onInput"
|
|
|
- @focus="onFocus"
|
|
|
- @blur="onBlur"
|
|
|
- @click="onClickInput"
|
|
|
- />
|
|
|
- <input
|
|
|
- v-else
|
|
|
+ <component
|
|
|
+ :is="renderInput(type)"
|
|
|
class="input-text"
|
|
|
ref="inputRef"
|
|
|
:style="styles"
|
|
|
- :type="inputType(type)"
|
|
|
:maxlength="maxLength"
|
|
|
:placeholder="placeholder"
|
|
|
- placeholder-class="nut-placeholder"
|
|
|
:disabled="disabled"
|
|
|
:readonly="readonly"
|
|
|
:value="modelValue"
|
|
|
:formatTrigger="formatTrigger"
|
|
|
- :confirm-type="confirmType"
|
|
|
- :adjust-position="adjustPosition"
|
|
|
- :always-system="alwaysSystem"
|
|
|
+ :autofocus="autofocus"
|
|
|
+ :enterkeyhint="confirmType"
|
|
|
@input="onInput"
|
|
|
@focus="onFocus"
|
|
|
@blur="onBlur"
|
|
|
@click="onClickInput"
|
|
|
- />
|
|
|
+ ></component>
|
|
|
<view v-if="readonly" class="nut-input-disabled-mask" @click="onClickInput"></view>
|
|
|
</view>
|
|
|
<view class="nut-input-clear-box">
|
|
|
@@ -120,12 +84,22 @@
|
|
|
</view>
|
|
|
</template>
|
|
|
<script lang="ts">
|
|
|
-import { PropType, ref, reactive, computed, onMounted, watch, nextTick, inject } from 'vue';
|
|
|
+import { PropType, ref, reactive, computed, onMounted, watch, ComputedRef, InputHTMLAttributes, h } from 'vue';
|
|
|
import { createComponent } from '@/packages/utils/create';
|
|
|
import { formatNumber } from './util';
|
|
|
|
|
|
const { componentName, create } = createComponent('input');
|
|
|
|
|
|
+export type InputType = InputHTMLAttributes['type'];
|
|
|
+export type InputAlignType = 'left' | 'center' | 'right'; // text-align
|
|
|
+export type InputFormatTrigger = 'onChange' | 'onBlur'; // onChange: 在输入时执行格式化 ; onBlur: 在失焦时执行格式化
|
|
|
+export type InputRule = {
|
|
|
+ pattern?: RegExp;
|
|
|
+ message?: string;
|
|
|
+ required?: boolean;
|
|
|
+};
|
|
|
+export type ConfirmTextType = 'send' | 'search' | 'next' | 'go' | 'done';
|
|
|
+
|
|
|
export default create({
|
|
|
props: {
|
|
|
ref: {
|
|
|
@@ -133,11 +107,11 @@ export default create({
|
|
|
default: ''
|
|
|
},
|
|
|
type: {
|
|
|
- type: String as PropType<import('./type').InputType>,
|
|
|
+ type: String as PropType<InputType>,
|
|
|
default: 'text'
|
|
|
},
|
|
|
modelValue: {
|
|
|
- type: [String, Number],
|
|
|
+ type: String,
|
|
|
default: ''
|
|
|
},
|
|
|
placeholder: {
|
|
|
@@ -157,7 +131,7 @@ export default create({
|
|
|
default: '80'
|
|
|
},
|
|
|
labelAlign: {
|
|
|
- type: String as PropType<import('./type').InputAlignType>,
|
|
|
+ type: String as PropType<InputAlignType>,
|
|
|
default: 'left'
|
|
|
},
|
|
|
colon: {
|
|
|
@@ -225,7 +199,7 @@ export default create({
|
|
|
default: true
|
|
|
},
|
|
|
formatTrigger: {
|
|
|
- type: String as PropType<import('./type').InputFormatTrigger>,
|
|
|
+ type: String as PropType<InputFormatTrigger>,
|
|
|
default: 'onChange'
|
|
|
},
|
|
|
formatter: {
|
|
|
@@ -233,7 +207,7 @@ export default create({
|
|
|
default: null
|
|
|
},
|
|
|
rules: {
|
|
|
- type: Array as PropType<import('./type').InputRule>,
|
|
|
+ type: Array as PropType<InputRule>,
|
|
|
default: []
|
|
|
},
|
|
|
errorMessage: {
|
|
|
@@ -241,7 +215,7 @@ export default create({
|
|
|
default: ''
|
|
|
},
|
|
|
errorMessageAlign: {
|
|
|
- type: String as PropType<import('./type').InputAlignType>,
|
|
|
+ type: String as PropType<InputAlignType>,
|
|
|
default: ''
|
|
|
},
|
|
|
rows: {
|
|
|
@@ -257,7 +231,7 @@ export default create({
|
|
|
default: false
|
|
|
},
|
|
|
confirmType: {
|
|
|
- type: String as PropType<import('./type').ConfirmTextType>,
|
|
|
+ type: String as PropType<ConfirmTextType>,
|
|
|
default: 'done'
|
|
|
},
|
|
|
adjustPosition: {
|
|
|
@@ -285,9 +259,14 @@ export default create({
|
|
|
setup(props, { emit, slots }) {
|
|
|
const active = ref(false);
|
|
|
|
|
|
- const inputRef: any = ref(null);
|
|
|
+ const inputRef = ref();
|
|
|
const getModelValue = () => String(props.modelValue ?? '');
|
|
|
- // const form = inject('form');
|
|
|
+
|
|
|
+ const renderInput = (type: InputType) => {
|
|
|
+ return h(type == 'textarea' ? 'textarea' : 'input', {
|
|
|
+ style: type == 'textarea' ? stylesTextarea : styles
|
|
|
+ });
|
|
|
+ };
|
|
|
|
|
|
const state = reactive({
|
|
|
focused: false,
|
|
|
@@ -307,19 +286,19 @@ export default create({
|
|
|
};
|
|
|
});
|
|
|
|
|
|
- const styles: any = computed(() => {
|
|
|
+ const styles: ComputedRef = computed(() => {
|
|
|
return {
|
|
|
textAlign: props.inputAlign
|
|
|
};
|
|
|
});
|
|
|
- const stylesTextarea: any = computed(() => {
|
|
|
+ const stylesTextarea: ComputedRef = computed(() => {
|
|
|
return {
|
|
|
textAlign: props.inputAlign,
|
|
|
height: Number(props.rows) * 24 + 'px'
|
|
|
};
|
|
|
});
|
|
|
|
|
|
- const inputType = (type: string) => {
|
|
|
+ const inputType = (type: InputType) => {
|
|
|
if (type === 'number') {
|
|
|
return 'text';
|
|
|
} else if (type === 'digit') {
|
|
|
@@ -338,12 +317,11 @@ export default create({
|
|
|
updateValue(value);
|
|
|
};
|
|
|
|
|
|
- const updateValue = (value: string, trigger: import('./type').InputFormatTrigger = 'onChange') => {
|
|
|
+ const updateValue = (value: string, trigger: InputFormatTrigger = 'onChange') => {
|
|
|
if (props.type === 'digit') {
|
|
|
value = formatNumber(value, false, false);
|
|
|
}
|
|
|
if (props.type === 'number') {
|
|
|
- // console.log('value', value)
|
|
|
value = formatNumber(value, true, true);
|
|
|
}
|
|
|
|
|
|
@@ -351,7 +329,7 @@ export default create({
|
|
|
value = props.formatter(value);
|
|
|
}
|
|
|
|
|
|
- if (inputRef && inputRef.value && inputRef.value !== value) {
|
|
|
+ if (inputRef?.value !== value) {
|
|
|
inputRef.value = value;
|
|
|
}
|
|
|
|
|
|
@@ -368,7 +346,8 @@ export default create({
|
|
|
const input = event.target as HTMLInputElement;
|
|
|
let value = input.value;
|
|
|
active.value = true;
|
|
|
- emit('focus', value, event);
|
|
|
+ emit('focus', event);
|
|
|
+ emit('update:modelValue', value);
|
|
|
};
|
|
|
|
|
|
const onBlur = (event: Event) => {
|
|
|
@@ -385,13 +364,13 @@ export default create({
|
|
|
value = value.slice(0, Number(props.maxLength));
|
|
|
}
|
|
|
updateValue(getModelValue(), 'onBlur');
|
|
|
- emit('blur', value, event);
|
|
|
+ emit('blur', event);
|
|
|
+ emit('update:modelValue', value);
|
|
|
};
|
|
|
|
|
|
const clear = (event: Event) => {
|
|
|
+ event.stopPropagation();
|
|
|
if (props.disabled) return;
|
|
|
- emit('update:modelValue', '', event);
|
|
|
- emit('change', '', event);
|
|
|
emit('clear', '', event);
|
|
|
};
|
|
|
|
|
|
@@ -410,6 +389,7 @@ export default create({
|
|
|
};
|
|
|
|
|
|
const onClickLeftIcon = (event: MouseEvent) => {
|
|
|
+ event.stopPropagation();
|
|
|
if (props.disabled) {
|
|
|
return;
|
|
|
}
|
|
|
@@ -417,6 +397,7 @@ export default create({
|
|
|
};
|
|
|
|
|
|
const onClickRightIcon = (event: MouseEvent) => {
|
|
|
+ event.stopPropagation();
|
|
|
if (props.disabled) {
|
|
|
return;
|
|
|
}
|
|
|
@@ -443,6 +424,7 @@ export default create({
|
|
|
});
|
|
|
|
|
|
return {
|
|
|
+ renderInput,
|
|
|
inputRef,
|
|
|
active,
|
|
|
classes,
|