feat(全局):增加 number-range-input 组件
parent
08511191f7
commit
3135b28211
|
|
@ -0,0 +1,39 @@
|
||||||
|
import type { VbenFormSchema } from '#/adapter/form';
|
||||||
|
|
||||||
|
import { markRaw } from 'vue';
|
||||||
|
|
||||||
|
import NumberRangeInput from './number-range-input.vue';
|
||||||
|
|
||||||
|
export { default as NumberRangeInput } from './number-range-input.vue';
|
||||||
|
|
||||||
|
export type NumberRangeValue = [number | undefined, number | undefined];
|
||||||
|
|
||||||
|
function splitNumberRange(minFieldName: string, maxFieldName: string) {
|
||||||
|
return (
|
||||||
|
value: NumberRangeValue | undefined,
|
||||||
|
setValue: (fieldName: string, value: number | undefined) => void,
|
||||||
|
) => {
|
||||||
|
setValue(minFieldName, value?.[0]);
|
||||||
|
setValue(maxFieldName, value?.[1]);
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildNumberRangeSchema(
|
||||||
|
label: string,
|
||||||
|
fieldName: string,
|
||||||
|
minFieldName: string,
|
||||||
|
maxFieldName: string,
|
||||||
|
precision: number,
|
||||||
|
): VbenFormSchema {
|
||||||
|
return {
|
||||||
|
component: markRaw(NumberRangeInput),
|
||||||
|
componentProps: {
|
||||||
|
min: 0,
|
||||||
|
precision,
|
||||||
|
},
|
||||||
|
fieldName,
|
||||||
|
label,
|
||||||
|
valueFormat: splitNumberRange(minFieldName, maxFieldName),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { InputNumber } from 'ant-design-vue';
|
||||||
|
|
||||||
|
type NumberRangeValue = [number | undefined, number | undefined];
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
maxPlaceholder?: string;
|
||||||
|
min?: number;
|
||||||
|
minPlaceholder?: string;
|
||||||
|
precision?: number;
|
||||||
|
value?: NumberRangeValue;
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
maxPlaceholder: '最大值',
|
||||||
|
min: undefined,
|
||||||
|
minPlaceholder: '最小值',
|
||||||
|
precision: 2,
|
||||||
|
value: undefined,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
'update:value': [value: NumberRangeValue | undefined];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
function normalizeValue(value: unknown) {
|
||||||
|
if (typeof value === 'number') {
|
||||||
|
return Number.isFinite(value) ? value : undefined;
|
||||||
|
}
|
||||||
|
if (typeof value === 'string' && value.trim() !== '') {
|
||||||
|
const numberValue = Number(value);
|
||||||
|
return Number.isFinite(numberValue) ? numberValue : undefined;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateValue(index: 0 | 1, value: unknown) {
|
||||||
|
const next: NumberRangeValue = [
|
||||||
|
props.value?.[0] ?? undefined,
|
||||||
|
props.value?.[1] ?? undefined,
|
||||||
|
];
|
||||||
|
next[index] = normalizeValue(value);
|
||||||
|
emit(
|
||||||
|
'update:value',
|
||||||
|
next[0] === undefined && next[1] === undefined ? undefined : next,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex w-full items-center gap-2">
|
||||||
|
<InputNumber
|
||||||
|
:controls="false"
|
||||||
|
:min="min"
|
||||||
|
:placeholder="minPlaceholder"
|
||||||
|
:precision="precision"
|
||||||
|
:value="value?.[0]"
|
||||||
|
class="min-w-0 flex-1"
|
||||||
|
@update:value="updateValue(0, $event)"
|
||||||
|
/>
|
||||||
|
<span class="shrink-0 text-muted-foreground">至</span>
|
||||||
|
<InputNumber
|
||||||
|
:controls="false"
|
||||||
|
:min="min"
|
||||||
|
:placeholder="maxPlaceholder"
|
||||||
|
:precision="precision"
|
||||||
|
:value="value?.[1]"
|
||||||
|
class="min-w-0 flex-1"
|
||||||
|
@update:value="updateValue(1, $event)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
import type { VbenFormSchema } from '#/adapter/form';
|
||||||
|
|
||||||
|
import { markRaw } from 'vue';
|
||||||
|
|
||||||
|
import NumberRangeInput from './number-range-input.vue';
|
||||||
|
|
||||||
|
export { default as NumberRangeInput } from './number-range-input.vue';
|
||||||
|
|
||||||
|
export type NumberRangeValue = [number | undefined, number | undefined];
|
||||||
|
|
||||||
|
function splitNumberRange(minFieldName: string, maxFieldName: string) {
|
||||||
|
return (
|
||||||
|
value: NumberRangeValue | undefined,
|
||||||
|
setValue: (fieldName: string, value: number | undefined) => void,
|
||||||
|
) => {
|
||||||
|
setValue(minFieldName, value?.[0]);
|
||||||
|
setValue(maxFieldName, value?.[1]);
|
||||||
|
return undefined;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildNumberRangeSchema(
|
||||||
|
label: string,
|
||||||
|
fieldName: string,
|
||||||
|
minFieldName: string,
|
||||||
|
maxFieldName: string,
|
||||||
|
precision: number,
|
||||||
|
): VbenFormSchema {
|
||||||
|
return {
|
||||||
|
component: markRaw(NumberRangeInput),
|
||||||
|
componentProps: {
|
||||||
|
min: 0,
|
||||||
|
precision,
|
||||||
|
},
|
||||||
|
fieldName,
|
||||||
|
label,
|
||||||
|
valueFormat: splitNumberRange(minFieldName, maxFieldName),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ElInputNumber } from 'element-plus';
|
||||||
|
|
||||||
|
type NumberRangeValue = [number | undefined, number | undefined];
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
maxPlaceholder?: string;
|
||||||
|
min?: number;
|
||||||
|
minPlaceholder?: string;
|
||||||
|
modelValue?: NumberRangeValue;
|
||||||
|
precision?: number;
|
||||||
|
}>(),
|
||||||
|
{
|
||||||
|
maxPlaceholder: '最大值',
|
||||||
|
min: undefined,
|
||||||
|
minPlaceholder: '最小值',
|
||||||
|
modelValue: undefined,
|
||||||
|
precision: 2,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
'update:modelValue': [value: NumberRangeValue | undefined];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
function normalizeValue(value: unknown) {
|
||||||
|
if (typeof value === 'number') {
|
||||||
|
return Number.isFinite(value) ? value : undefined;
|
||||||
|
}
|
||||||
|
if (typeof value === 'string' && value.trim() !== '') {
|
||||||
|
const numberValue = Number(value);
|
||||||
|
return Number.isFinite(numberValue) ? numberValue : undefined;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateValue(index: 0 | 1, value: unknown) {
|
||||||
|
const next: NumberRangeValue = [
|
||||||
|
props.modelValue?.[0] ?? undefined,
|
||||||
|
props.modelValue?.[1] ?? undefined,
|
||||||
|
];
|
||||||
|
next[index] = normalizeValue(value);
|
||||||
|
emit(
|
||||||
|
'update:modelValue',
|
||||||
|
next[0] === undefined && next[1] === undefined ? undefined : next,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex w-full items-center gap-2">
|
||||||
|
<ElInputNumber
|
||||||
|
:controls="false"
|
||||||
|
:min="min"
|
||||||
|
:model-value="modelValue?.[0]"
|
||||||
|
:placeholder="minPlaceholder"
|
||||||
|
:precision="precision"
|
||||||
|
class="min-w-0 flex-1"
|
||||||
|
@update:model-value="updateValue(0, $event)"
|
||||||
|
/>
|
||||||
|
<span class="shrink-0 text-muted-foreground">至</span>
|
||||||
|
<ElInputNumber
|
||||||
|
:controls="false"
|
||||||
|
:min="min"
|
||||||
|
:model-value="modelValue?.[1]"
|
||||||
|
:placeholder="maxPlaceholder"
|
||||||
|
:precision="precision"
|
||||||
|
class="min-w-0 flex-1"
|
||||||
|
@update:model-value="updateValue(1, $event)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
Loading…
Reference in New Issue