@fesjs/fes-design
Version:
fes-design for PC
248 lines (239 loc) • 8.63 kB
JavaScript
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 };