diff --git a/apps/web-antd/src/components/number-range-input/index.ts b/apps/web-antd/src/components/number-range-input/index.ts
new file mode 100644
index 000000000..f1647e75d
--- /dev/null
+++ b/apps/web-antd/src/components/number-range-input/index.ts
@@ -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),
+ };
+}
diff --git a/apps/web-antd/src/components/number-range-input/number-range-input.vue b/apps/web-antd/src/components/number-range-input/number-range-input.vue
new file mode 100644
index 000000000..6ee67c046
--- /dev/null
+++ b/apps/web-antd/src/components/number-range-input/number-range-input.vue
@@ -0,0 +1,73 @@
+
+
+
+
+
+ 至
+
+
+
diff --git a/apps/web-ele/src/components/number-range-input/index.ts b/apps/web-ele/src/components/number-range-input/index.ts
new file mode 100644
index 000000000..f1647e75d
--- /dev/null
+++ b/apps/web-ele/src/components/number-range-input/index.ts
@@ -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),
+ };
+}
diff --git a/apps/web-ele/src/components/number-range-input/number-range-input.vue b/apps/web-ele/src/components/number-range-input/number-range-input.vue
new file mode 100644
index 000000000..b5ee5391c
--- /dev/null
+++ b/apps/web-ele/src/components/number-range-input/number-range-input.vue
@@ -0,0 +1,73 @@
+
+
+
+
+
+ 至
+
+
+