UNPKG

@fesjs/fes-design

Version:
248 lines (239 loc) 8.63 kB
import { defineComponent, computed, ref, onMounted, nextTick, resolveComponent, openBlock, createElementBlock, normalizeClass, withModifiers, createVNode, createSlots, withCtx, renderSlot, createElementVNode, createCommentVNode } from 'vue'; import { isNumber } from 'lodash-es'; import { UpOutlined, DownOutlined } from '../icon'; import { useTheme } from '../_theme/useTheme'; import getPrefixCls from '../_util/getPrefixCls'; import { useNormalModel } from '../_util/use/useModel'; import useFormAdaptor from '../_util/use/useFormAdaptor'; import InputInner from '../input/inputInner.js'; const prefixCls = getPrefixCls('input-number'); var ActionEnum = /*#__PURE__*/function (ActionEnum) { ActionEnum[ActionEnum["PLUS"] = 0] = "PLUS"; ActionEnum[ActionEnum["REDUCE"] = 1] = "REDUCE"; return ActionEnum; }(ActionEnum || {}); const inputNumberProps = { modelValue: Number, min: { type: Number, default: Number.NEGATIVE_INFINITY }, max: { type: Number, default: Number.POSITIVE_INFINITY }, step: { type: Number, default: 1 }, showStepAction: { type: Boolean, default: true }, precision: Number, disabled: Boolean, placeholder: String, autofocus: { type: Boolean, default: false } }; var script = defineComponent({ name: 'FInputNumber', components: { InputInner, UpOutlined, DownOutlined }, props: inputNumberProps, emits: ['update:modelValue', 'change', 'input', 'blur', 'focus'], setup(props, _ref) { let { emit } = _ref; useTheme(); const { validate, isError, isFormDisabled } = useFormAdaptor({ valueType: 'number', forbidChildValidate: true }); const [currentValue, updateCurrentValue] = useNormalModel(props, emit); const innerDisabled = computed(() => props.disabled || isFormDisabled.value); const classes = computed(() => [`${prefixCls}`, innerDisabled.value && 'is-disabled'].filter(Boolean)); const inputRef = ref(); const tempValue = ref(); const displayValue = computed(() => { if (tempValue.value != null) { return tempValue.value; } return currentValue.value; }); // 获取输入值的小数位数 const getPrecision = val => { if (val == null) { return 0; } const valueString = val.toString(); const dotPosition = valueString.indexOf('.'); let valuePrecision = 0; if (dotPosition !== -1) { valuePrecision = valueString.length - dotPosition - 1; } return valuePrecision; }; // 数字的实际精度 (组件绑定的精度属性要处理) const numPrecision = computed(() => { const stepPrecision = getPrecision(props.step); if (props.precision != null) { const positiveIntegerPrecision = Math.abs(Math.round(props.precision)); if (stepPrecision > positiveIntegerPrecision) { console.warn('[InputNumber]precision should not be less than the decimal places of step'); } return positiveIntegerPrecision; } return Math.max(getPrecision(currentValue.value), stepPrecision); // 计算时可能currentvalue 无值 }); // 保留指定的小数位数 const toPrecision = (num, pre) => { if (pre == null) { pre = numPrecision.value; } return Math.round(num * 10 ** pre) / 10 ** pre; }; const setCurrentValue = newVal => { const oldVal = currentValue.value; if (isNumber(newVal) && props.precision != null) { newVal = toPrecision(newVal, props.precision); } if (newVal != null && newVal >= props.max) { newVal = props.max; } if (oldVal === newVal) { return; } tempValue.value = null; updateCurrentValue(newVal); emit('input', newVal); emit('change', newVal, oldVal); validate('input'); validate('change'); }; const handleBlur = e => { if (tempValue.value) { tempValue.value = null; } // 避免输入的值小于最小值而导致无法继续输入的情况, 失焦的时候再处理 if (currentValue.value != null && currentValue.value <= props.min) { currentValue.value = props.min; } emit('blur', e); validate('blur'); }; const handleInput = value => { tempValue.value = value; // 在下一个 tick 处理 tempValue,避免无法重制 displayValue nextTick(() => { if (!value.endsWith('.') && (!Number.isNaN(Number(value)) || value === '')) { setCurrentValue(value === '' ? null : Number(value)); } }); }; const onFocused = e => { emit('focus', e); }; const _calculationNum = (val, type) => { if (!isNumber(val) && val != null) { return tempValue.value; } const precisionFactor = 10 ** numPrecision.value; let tmp; if (type === ActionEnum.PLUS) { tmp = precisionFactor * val + precisionFactor * props.step; } else { tmp = precisionFactor * val - precisionFactor * props.step; } return toPrecision(tmp / precisionFactor); }; // 是否已减小到最小值 const minDisabled = computed(() => _calculationNum(currentValue.value, ActionEnum.REDUCE) < props.min); // 是否已加到最大值 const maxDisabled = computed(() => _calculationNum(currentValue.value, ActionEnum.PLUS) > props.max); const calculationNum = type => { if (props.disabled || maxDisabled.value && type === ActionEnum.PLUS || minDisabled.value && type === ActionEnum.REDUCE || isFormDisabled.value) { return; } tempValue.value = null; setCurrentValue(_calculationNum(currentValue.value || 0, type)); }; const focus = () => { inputRef.value.focus(); }; onMounted(() => { if (props.autofocus) { focus(); } }); return { prefixCls, isError, ActionEnum, innerDisabled, classes, handleInput, onFocused, handleBlur, calculationNum, displayValue, minDisabled, maxDisabled, inputRef }; } }); function render(_ctx, _cache, $props, $setup, $data, $options) { const _component_UpOutlined = resolveComponent("UpOutlined"); const _component_DownOutlined = resolveComponent("DownOutlined"); const _component_InputInner = resolveComponent("InputInner"); return openBlock(), createElementBlock("div", { class: normalizeClass(_ctx.classes), onDragstart: _cache[4] || (_cache[4] = withModifiers(() => {}, ["prevent"])) }, [createVNode(_component_InputInner, { ref: "inputRef", modelValue: _ctx.displayValue, disabled: _ctx.innerDisabled, placeholder: _ctx.placeholder, class: normalizeClass([`${_ctx.prefixCls}-inner`]), innerIsError: _ctx.isError, onInput: _ctx.handleInput, onFocus: _ctx.onFocused, onBlur: _ctx.handleBlur }, createSlots({ suffix: withCtx(() => [renderSlot(_ctx.$slots, "suffix"), _ctx.showStepAction ? (openBlock(), createElementBlock("div", { key: 0, class: normalizeClass([`${_ctx.prefixCls}-actions`, _ctx.$slots.suffix && `${_ctx.prefixCls}-actions-suffix`]) }, [createElementVNode("span", { class: normalizeClass([`${_ctx.prefixCls}-actions-increase`, { 'is-disabled': _ctx.maxDisabled || _ctx.innerDisabled }]), onMousedown: _cache[0] || (_cache[0] = withModifiers(() => {}, ["prevent"])), onClick: _cache[1] || (_cache[1] = $event => _ctx.calculationNum(_ctx.ActionEnum.PLUS)) }, [createVNode(_component_UpOutlined)], 34 /* CLASS, NEED_HYDRATION */), createElementVNode("span", { class: normalizeClass([`${_ctx.prefixCls}-actions-decrease`, { 'is-disabled': _ctx.minDisabled || _ctx.innerDisabled }]), onMousedown: _cache[2] || (_cache[2] = withModifiers(() => {}, ["prevent"])), onClick: _cache[3] || (_cache[3] = $event => _ctx.calculationNum(_ctx.ActionEnum.REDUCE)) }, [createVNode(_component_DownOutlined)], 34 /* CLASS, NEED_HYDRATION */)], 2 /* CLASS */)) : createCommentVNode("v-if", true)]), _: 2 /* DYNAMIC */ }, [_ctx.$slots.prefix ? { name: "prefix", fn: withCtx(() => [renderSlot(_ctx.$slots, "prefix")]), key: "0" } : undefined]), 1032 /* PROPS, DYNAMIC_SLOTS */, ["modelValue", "disabled", "placeholder", "class", "innerIsError", "onInput", "onFocus", "onBlur"])], 34 /* CLASS, NEED_HYDRATION */); } script.render = render; script.__file = "components/input-number/input-number.vue"; export { script as default, inputNumberProps };