-
Notifications
You must be signed in to change notification settings - Fork 286
Expand file tree
/
Copy pathuse-input-number.ts
More file actions
197 lines (166 loc) · 5.8 KB
/
use-input-number.ts
File metadata and controls
197 lines (166 loc) · 5.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
import { computed, reactive, toRefs, watch, ref, inject, InjectionKey } from 'vue';
import type { SetupContext, Ref, CSSProperties } from 'vue';
import { InputNumberProps, UseEvent, UseRender, IState, UseExpose } from './input-number-types';
import { useNamespace } from '../../shared/hooks/use-namespace';
import { isNumber, isUndefined } from '../../shared/utils';
import { FORM_TOKEN, type FormProps } from '../../form';
const ns = useNamespace('input-number');
export function useRender(props: InputNumberProps, ctx: SetupContext): UseRender {
const formContext: FormProps | undefined | any = inject(FORM_TOKEN, undefined); // 修复ts语法错误组件不被d-from组件引用时,formContext未被定义
const { style, class: customClass, ...otherAttrs } = ctx.attrs;
const customStyle = { style: style as CSSProperties };
const inputNumberSize = computed(() => props.size || formContext?.size || 'md');
const wrapClass = computed(() => [
{
[ns.b()]: true,
[ns.m('glow-style')]: props.showGlowStyle,
[ns.m(inputNumberSize.value)]: true,
},
customClass,
]);
const controlButtonsClass = computed(() => ({
[ns.e('control-buttons')]: true,
disabled: props.disabled,
}));
const inputWrapClass = computed(() => ({
[ns.e('input-wrap')]: true,
}));
const inputInnerClass = computed(() => ({
[ns.e('input-box')]: true,
disabled: props.disabled,
}));
return { wrapClass, customStyle, otherAttrs, controlButtonsClass, inputWrapClass, inputInnerClass };
}
export function useExpose(ctx: SetupContext): UseExpose {
const inputRef = ref();
const focus = () => {
inputRef.value.focus();
};
const blur = () => {
inputRef.value.blur();
};
const select = () => {
inputRef.value.select();
};
ctx.expose({ focus, blur, select });
return { inputRef };
}
function getPrecision(pre: string | number | undefined | null): number {
let precision = 0;
if (isUndefined(pre)) {
return precision;
}
const preString = (pre as string).toString();
const dotIndex = preString.indexOf('.');
if (dotIndex !== -1) {
precision = preString.length - dotIndex - 1;
}
return precision;
}
export function useEvent(props: InputNumberProps, ctx: SetupContext, inputRef: Ref): UseEvent {
const { min, max, step, disabled } = toRefs(props);
const state = reactive<IState>({
currentValue: props.modelValue || '',
userInputValue: undefined,
});
const numPrecision = computed(() => {
if (!isUndefined(props.precision)) {
return props.precision;
} else {
return Math.max(getPrecision(props.modelValue), getPrecision(step.value));
}
});
const inputVal = computed(() => {
if (!isUndefined(state.userInputValue)) {
return state.userInputValue;
}
let currentValue = state.currentValue;
if (!currentValue && currentValue !== 0) {
return null;
}
if (isNumber(currentValue)) {
// todo 小数精度 确认是否应该以正则处理
currentValue = currentValue.toFixed(numPrecision.value);
}
return currentValue;
});
const toPrecision = (num: number) => {
return Number.parseFloat(num.toFixed(numPrecision.value));
};
const computeByStep = (val: number | string, addOrNot: 1 | -1 = 1) => {
if (!isNumber(val)) {
return state.currentValue;
}
return toPrecision(val + step.value * addOrNot);
};
const correctValue = (value: number | string | undefined | null) => {
if ((!value && value !== 0) && props.allowEmpty) { // 当用户开始允许空值时 value不为0的false全返回null(即'',null,undefined,NaN都会反回null设计与dev_ui_ag版本一致)
return null;
}
// 校验正则
const valueStr = value + '';
if (props.reg && !valueStr.match(new RegExp(props.reg))) {
return undefined;
}
let newVal = Number(value);
// 精度限制存在才做转换
if (!isUndefined(props.precision)) {
newVal = toPrecision(newVal);
}
// 判断数据是否大于或小于限定数值
if (newVal > max.value || newVal < min.value) {
newVal = newVal > max.value ? max.value : min.value;
}
return newVal;
};
const setCurrentValue = (value: number | string | undefined | null) => {
const oldVal = state.currentValue;
const newVal = correctValue(value);
state.userInputValue = undefined;
// 0 和 '' 可以被更新
if (newVal !== 0 && newVal !== null && !newVal) {
ctx.emit('update:modelValue', oldVal);
return;
}
// 新旧数据一致不做更新
if (oldVal === newVal) {
return;
}
ctx.emit('update:modelValue', newVal);
ctx.emit('input', newVal);
ctx.emit('change', newVal, oldVal);
state.currentValue = newVal;
};
const minDisabled = computed(() => isNumber(state.currentValue) && (computeByStep(state.currentValue, -1) as number) < props.min);
const maxDisabled = computed(() => isNumber(state.currentValue) && (computeByStep(state.currentValue) as number) > props.max);
const onAdd = () => {
if (disabled.value || maxDisabled.value) {
return;
}
inputRef.value.focus();
const newVal = computeByStep(state.currentValue || 0);
setCurrentValue(newVal);
};
const onSubtract = () => {
if (disabled.value || minDisabled.value) {
return;
}
inputRef.value.focus();
const newVal = computeByStep(state.currentValue || 0, -1);
setCurrentValue(newVal);
};
watch(
() => props.modelValue,
(val) => {
state.currentValue = correctValue(val);
},
{ immediate: true }
);
const onInput = (event: Event) => {
state.userInputValue = (event.target as HTMLInputElement).value;
};
const onChange = (event: Event) => {
setCurrentValue((event.target as HTMLInputElement).value);
};
return { inputVal, minDisabled, maxDisabled, onAdd, onSubtract, onInput, onChange };
}