Слияние кода завершено, страница обновится автоматически
<!--
// NumberRange组件,用于输入数字范围(支持前缀和后缀插槽,支持数字精度,支持限制取值范围),在数值、金额等的范围输入场景中使用
// 使用示例:
<template>
<el-col :xs="24" :sm="12" :md="12" :lg="8" :xl="4" class="mb10">
<el-form-item label="订单金额">
<number-range v-model="queryParams.amountRange">
<template #prepend>
<span>范围</span>
</template>
</number-range>
</el-form-item>
</el-col>
</template>
<script lang="ts" setup>
import NumberRange from '/@/components/numberRange/index.vue';
</script>
-->
<template>
<div class="number-range-container">
<div :id="usePrepend ? 'prepend' : ''" :class="{ 'slot-default': slotStyle === 'default', 'slot-pend ': usePrepend }">
<slot name="prepend">
<!-- 前缀插槽 -->
</slot>
</div>
<div
class="number-range"
:class="{
'is-disabled': disabled,
'is-focus': isFocus,
'number-range-left-border-radius-0': usePrepend,
'number-range-right-border-radius-0': useAppend,
}"
>
<el-input-number
:disabled="disabled"
placeholder="最小值"
@blur="handleBlur"
@focus="handleFocus"
@change="handleChangeMinValue"
@update:modelValue="updateMinValue"
v-model="minValue_"
v-bind="$attrs"
:controls="false"
/>
<div class="to">
<span>{{ to }}</span>
</div>
<el-input-number
:disabled="disabled"
placeholder="最大值"
@blur="handleBlur"
@focus="handleFocus"
@change="handleChangeMaxValue"
@update:modelValue="updateMaxValue"
v-model="maxValue_"
v-bind="$attrs"
:controls="false"
/>
<!-- 清除图标 -->
<el-icon v-if="clearable && (minValue_ || maxValue_)" class="el-icon el-input__icon el-input__clear" @click="clearValues">
<CircleClose />
</el-icon>
</div>
<div :id="useAppend ? 'append' : ''" :class="{ 'slot-default': slotStyle === 'default', 'slot-pend ': useAppend }">
<slot name="append">
<!-- 后缀插槽 -->
</slot>
</div>
</div>
</template>
<script lang="ts" setup name="numberRange">
import { computed, ref, useSlots } from 'vue';
import { CircleClose } from '@element-plus/icons-vue';
const props = defineProps({
modelValue: {
type: Array<Number>,
default: () => [null, null], // 调用时使用v-model="[min,max]" 绑定
},
clearable: {
type: Boolean,
default: false,
},
minValue: {
type: Number,
default: null, // 调用时使用v-model:min-value="" 绑定多个v-model
},
maxValue: {
type: Number,
default: null, // 调用时使用v-model:max-value="" 绑定多个v-model
},
// 是否禁用
disabled: {
type: Boolean,
default: false,
},
to: {
type: String,
default: '-',
},
// 精度参数 -保留小数位数
precision: {
type: Number,
default: 0,
validator(val: number) {
return val >= 0 && val === parseInt(String(val), 10);
},
},
// 限制取值范围
valueRange: {
type: Array,
default: () => [],
validator(val: []) {
if (val && val.length > 0) {
// @ts-ignore
if (val.length !== 2) {
throw new Error('请传入长度为2的Number数组');
}
// @ts-ignore
if (typeof val[0] !== 'number' || typeof val[1] !== 'number') {
throw new Error('取值范围只接受Number类型,请确认');
}
// @ts-ignore
if (val[1] < val[0]) {
throw new Error('valueRange格式须为[最小值,最大值],请确认');
}
}
return true;
},
},
// 插槽样式
slotStyle: {
type: String, // default --异色背景 | plain--无背景色
default: 'plain',
},
});
const emit = defineEmits(['update:modelValue', 'update:minValue', 'update:maxValue', 'change']);
const minValue_ = computed({
get() {
return props.minValue || props.modelValue[0] || null;
},
set(value) {
if (value === null) {
emit('update:minValue', null);
emit('update:modelValue', [null, maxValue_.value]);
return;
}
emit('update:minValue', value);
emit('update:modelValue', [value, maxValue_.value]);
},
});
const maxValue_ = computed({
get() {
return props.maxValue || props.modelValue[1] || null;
},
set(value) {
if (value === null) {
emit('update:maxValue', null);
emit('update:modelValue', [minValue_.value, null]);
return;
}
emit('update:maxValue', value);
emit('update:modelValue', [minValue_.value, value]);
},
});
// 清除值的方法
const clearValues = () => {
minValue_.value = null;
maxValue_.value = null;
emit('update:modelValue', [null, null]);
};
const handleChangeMinValue = (value: number | null) => {
// 非数字空返回null
if (value === null || isNaN(value)) {
emit('update:minValue', null);
return;
}
// 初始化数字精度
const newMinValue = parsePrecision(value, props.precision);
// min > max 交换min max
if (typeof newMinValue === 'number' && parseFloat(String(newMinValue)) > parseFloat(String(maxValue_.value))) {
// 取值范围判定
const { min, max } = decideValueRange(Number(maxValue_.value), newMinValue);
// 更新绑定值
updateValue(min, max);
}
};
const handleChangeMaxValue = (value: number | null) => {
// 非数字空返回null
if (value === null || isNaN(value)) {
emit('update:maxValue', null);
return;
}
// 初始化数字精度
const newMaxValue = parsePrecision(value, props.precision);
// max < min 交换min max
if (typeof newMaxValue === 'number' && parseFloat(String(newMaxValue)) < parseFloat(String(minValue_.value))) {
// 取值范围判定
const { min, max } = decideValueRange(newMaxValue, Number(minValue_.value));
// 更新绑定值
updateValue(min, max);
}
};
const updateMinValue = (value: number | null) => {
minValue_.value = value;
};
const updateMaxValue = (value: number | null) => {
maxValue_.value = value;
};
// 更新数据
const updateValue = (min: number | null, max: number | null) => {
emit('update:minValue', min);
emit('update:maxValue', max);
emit('update:modelValue', [min, max]);
emit('change', { min, max });
};
// 取值范围判定
const decideValueRange = (min: number | null, max: number | null) => {
if (min === null || max === null) {
return { min, max };
}
if (props.valueRange && props.valueRange.length > 0) {
// @ts-ignore
min = min < props.valueRange[0] ? props.valueRange[0] : min > props.valueRange[1] ? props.valueRange[1] : min;
// @ts-ignore
max = max > props.valueRange[1] ? props.valueRange[1] : max;
}
return { min, max };
};
// input焦点事件
const isFocus = ref();
const handleFocus = () => {
isFocus.value = true;
};
const handleBlur = () => {
isFocus.value = false;
};
// 处理数字精度
const parsePrecision = (number: number | null, precision = 0) => {
if (number === null) {
return null;
}
return parseFloat(String(Math.round(number * Math.pow(10, precision)) / Math.pow(10, precision)));
};
// 判断插槽是否被使用
// 组件外部使用时插入了
// <template #插槽名 >
// </template>
// 无论template标签内是否插入了内容,均视为已使用该插槽
const slots = useSlots();
const usePrepend = computed(() => {
// 前缀插槽
return slots && slots.prepend ? true : false;
});
const useAppend = computed(() => {
// 后缀插槽
return slots && slots.append ? true : false;
});
</script>
<style lang="scss" scoped>
.number-range-container {
position: relative;
display: flex;
height: 100%;
.slot-pend {
white-space: nowrap;
color: var(--el-color-info);
border-radius: var(--el-input-border-radius, var(--el-border-radius-base));
}
#prepend {
padding: 0 20px;
box-shadow:
1px 0 0 0 var(--el-input-border-color, var(--el-border-color)) inset,
0 1px 0 0 var(--el-input-border-color, var(--el-border-color)) inset,
0 -1px 0 0 var(--el-input-border-color, var(--el-border-color)) inset;
border-right: 0;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
#append {
padding: 0 20px;
box-shadow:
0 1px 0 0 var(--el-input-border-color, var(--el-border-color)) inset,
0 -1px 0 0 var(--el-input-border-color, var(--el-border-color)) inset,
-1px 0 0 0 var(--el-input-border-color, var(--el-border-color)) inset;
border-left: 0;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
.slot-default {
background-color: var(--el-fill-color-light);
}
.number-range-left-border-radius-0 {
border-top-left-radius: 0 !important;
border-bottom-left-radius: 0 !important;
}
.number-range-right-border-radius-0 {
border-top-right-radius: 0 !important;
border-bottom-right-radius: 0 !important;
}
.number-range {
background-color: var(--el-bg-color) !important;
box-shadow: 0 0 0 1px var(--el-input-border-color, var(--el-border-color)) inset;
border-radius: var(--el-input-border-radius, var(--el-border-radius-base));
padding: 0 2px;
display: flex;
flex-direction: row;
width: 100%;
justify-content: center;
align-items: center;
color: var(--el-input-text-color, var(--el-text-color-regular));
transition: var(--el-transition-box-shadow);
transform: translate3d(0, 0, 0);
overflow: hidden;
.to {
margin-top: 1px;
}
}
.is-focus {
transition: all 0.3s;
box-shadow: 0 0 0 1px var(--el-color-primary) inset !important;
}
.is-disabled {
background-color: var(--el-input-bg-color);
color: var(--el-input-text-color, var(--el-text-color-regular));
cursor: not-allowed;
.to {
height: calc(100% - 3px);
background-color: var(--el-fill-color-light) !important;
}
}
}
.el-input__clear {
cursor: pointer;
color: var(--el-input-icon-color, var(--el-text-color-placeholder));
margin-left: 10px;
display: none;
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
}
.number-range:hover .el-input__clear {
display: flex;
align-items: center;
color: var(--el-input-clear-hover-color);
}
:deep(.el-input) {
border: none;
}
:deep(.el-input__wrapper) {
margin: 0;
padding: 0 15px;
background-color: transparent;
border: none !important;
box-shadow: none !important;
&.is-focus {
border: none !important;
box-shadow: none !important;
}
}
:deep(.el-input),
:deep(.el-select),
:deep(.el-input-number) {
width: 100%;
}
</style>
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )