From 45180f7688a4ebe58ba047070f344dfb2b324905 Mon Sep 17 00:00:00 2001 From: Tony <6914529@qq.com> Date: Fri, 5 Jun 2026 21:34:26 +0800 Subject: [PATCH 01/30] =?UTF-8?q?feat(BootstrapInputNumber):=20=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E6=88=96=E9=85=8D=E7=BD=AE=E4=B8=AD=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=E4=BA=86Step=E7=9A=84HTML=E6=A0=87=E5=87=86=E5=B1=9E=E6=80=A7?= =?UTF-8?q?=E5=AF=BC=E8=87=B4=E8=A1=A8=E5=8D=95=E6=8F=90=E4=BA=A4=E6=97=B6?= =?UTF-8?q?=E5=BC=95=E5=AF=BCHTML=E7=9A=84=E6=A0=87=E5=87=86=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix:当组件中启用HTML标准属性Step后,在表单提交时会引导HTML的标准校验,从而导致表单的提交按钮会被禁用 refactor:修改为在组件或配置中当Step值不为any时,组件将自动将输入的值处理为Step的精度 chore:补充单元测试 --- .../InputNumber/BootstrapInputNumber.razor.cs | 9 ++++++ test/UnitTest/Components/InputNumberTest.cs | 31 +++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs b/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs index 768a46b96f3..2bc5ecf2f7e 100644 --- a/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs +++ b/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs @@ -257,6 +257,15 @@ protected override async Task OnBlur() if (!PreviousParsingAttemptFailed) { CurrentValue = SetMax(SetMin(Value)); + string stepValue = GetStepString(); + if (stepValue != "any" && (CurrentValue is float || CurrentValue is double || CurrentValue is decimal)) + { + // 先强转用,固定不变的文化信息,已解决全球化数字信息问题 + stepValue = Convert.ToDecimal(stepValue).ToString(CultureInfo.InvariantCulture); + int precision = stepValue.Contains('.') ? stepValue.Split('.')[1].Length : 0; + dynamic val = CurrentValue!; + CurrentValue = val == 0 ? CurrentValue : (TValue)Math.Round(val, precision); + } } else { diff --git a/test/UnitTest/Components/InputNumberTest.cs b/test/UnitTest/Components/InputNumberTest.cs index feecc2470ae..cb413308318 100644 --- a/test/UnitTest/Components/InputNumberTest.cs +++ b/test/UnitTest/Components/InputNumberTest.cs @@ -321,6 +321,37 @@ public async Task ShowButton_EmptyStep_Ok() Assert.Equal(2, cut.Instance.Value); } + [Fact] + public async Task Step_Ok() + { + var cut = Context.Render>(pb => + { + pb.Add(a => a.Value, 1); + pb.Add(a => a.Step, "0.001"); + }); + var input = cut.Find("input"); + await cut.InvokeAsync(() => input.Blur()); + Assert.Equal(1, cut.Instance.Value); + + cut.Render(pb => + { + pb.Add(a => a.Value, 1); + pb.Add(a => a.Step, null); + }); + input = cut.Find("input"); + await cut.InvokeAsync(() => input.Blur()); + Assert.Equal(1, cut.Instance.Value); + + cut.Render(pb => + { + pb.Add(a => a.Value, 98.12356m); + pb.Add(a => a.Step, "0.001"); + }); + input = cut.Find("input"); + await cut.InvokeAsync(() => input.Blur()); + Assert.Equal(98.124m, cut.Instance.Value); + } + [Fact] public async Task MinMax_Ok() { From 02268aca1ce9324138033ca08470a92168df0342 Mon Sep 17 00:00:00 2001 From: Tony <6914529@qq.com> Date: Sat, 6 Jun 2026 07:35:16 +0800 Subject: [PATCH 02/30] =?UTF-8?q?feat(BootstrapInputNumber):=20=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E6=88=96=E9=85=8D=E7=BD=AE=E4=B8=AD=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=E4=BA=86Step=E7=9A=84HTML=E6=A0=87=E5=87=86=E5=B1=9E=E6=80=A7?= =?UTF-8?q?=E5=AF=BC=E8=87=B4=E8=A1=A8=E5=8D=95=E6=8F=90=E4=BA=A4=E6=97=B6?= =?UTF-8?q?=E5=BC=95=E5=AF=BCHTML=E7=9A=84=E6=A0=87=E5=87=86=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit refactor:单元测试修改补充 --- test/UnitTest/Components/InputNumberTest.cs | 27 +++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/test/UnitTest/Components/InputNumberTest.cs b/test/UnitTest/Components/InputNumberTest.cs index cb413308318..94cce2bcd96 100644 --- a/test/UnitTest/Components/InputNumberTest.cs +++ b/test/UnitTest/Components/InputNumberTest.cs @@ -342,6 +342,15 @@ public async Task Step_Ok() await cut.InvokeAsync(() => input.Blur()); Assert.Equal(1, cut.Instance.Value); + cut.Render(pb => + { + pb.Add(a => a.Value, 0); + pb.Add(a => a.Step, "0.001"); + }); + input = cut.Find("input"); + await cut.InvokeAsync(() => input.Blur()); + Assert.Equal(0, cut.Instance.Value); + cut.Render(pb => { pb.Add(a => a.Value, 98.12356m); @@ -350,6 +359,24 @@ public async Task Step_Ok() input = cut.Find("input"); await cut.InvokeAsync(() => input.Blur()); Assert.Equal(98.124m, cut.Instance.Value); + + var cutd = Context.Render>(pb => + { + pb.Add(a => a.Value, 1); + pb.Add(a => a.Step, "0.001"); + }); + input = cutd.Find("input"); + await cutd.InvokeAsync(() => input.Blur()); + Assert.Equal(1, cutd.Instance.Value); + + var cutf = Context.Render>(pb => + { + pb.Add(a => a.Value, 1); + pb.Add(a => a.Step, "0.001"); + }); + input = cutf.Find("input"); + await cutf.InvokeAsync(() => input.Blur()); + Assert.Equal(1, cutf.Instance.Value); } [Fact] From 6dcba077ad46e892fb2686265970f982fdb72fa4 Mon Sep 17 00:00:00 2001 From: Tony <6914529@qq.com> Date: Sat, 6 Jun 2026 07:42:00 +0800 Subject: [PATCH 03/30] =?UTF-8?q?feat(BootstrapInputNumber):=20=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E6=88=96=E9=85=8D=E7=BD=AE=E4=B8=AD=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=E4=BA=86Step=E7=9A=84HTML=E6=A0=87=E5=87=86=E5=B1=9E=E6=80=A7?= =?UTF-8?q?=E5=AF=BC=E8=87=B4=E8=A1=A8=E5=8D=95=E6=8F=90=E4=BA=A4=E6=97=B6?= =?UTF-8?q?=E5=BC=95=E5=AF=BCHTML=E7=9A=84=E6=A0=87=E5=87=86=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit refactor:补单元测试,当Step值为any时的情况 --- test/UnitTest/Components/InputNumberTest.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/UnitTest/Components/InputNumberTest.cs b/test/UnitTest/Components/InputNumberTest.cs index 94cce2bcd96..670f3214f69 100644 --- a/test/UnitTest/Components/InputNumberTest.cs +++ b/test/UnitTest/Components/InputNumberTest.cs @@ -342,6 +342,15 @@ public async Task Step_Ok() await cut.InvokeAsync(() => input.Blur()); Assert.Equal(1, cut.Instance.Value); + cut.Render(pb => + { + pb.Add(a => a.Value, 1); + pb.Add(a => a.Step, "any"); + }); + input = cut.Find("input"); + await cut.InvokeAsync(() => input.Blur()); + Assert.Equal(1, cut.Instance.Value); + cut.Render(pb => { pb.Add(a => a.Value, 0); From e8eadec3f856b3af9de9bc765852c8da264eb870 Mon Sep 17 00:00:00 2001 From: Tony <6914529@qq.com> Date: Sat, 6 Jun 2026 07:52:05 +0800 Subject: [PATCH 04/30] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20InputNumberTest.cs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit refactor:继续补充单元测试,Step,以便达到100%覆盖率 --- test/UnitTest/Components/InputNumberTest.cs | 36 +++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/test/UnitTest/Components/InputNumberTest.cs b/test/UnitTest/Components/InputNumberTest.cs index 670f3214f69..6bddba6d00f 100644 --- a/test/UnitTest/Components/InputNumberTest.cs +++ b/test/UnitTest/Components/InputNumberTest.cs @@ -378,6 +378,24 @@ public async Task Step_Ok() await cutd.InvokeAsync(() => input.Blur()); Assert.Equal(1, cutd.Instance.Value); + cutd.Render(pb => + { + pb.Add(a => a.Value, 1); + pb.Add(a => a.Step, "any"); + }); + input = cutd.Find("input"); + await cutd.InvokeAsync(() => input.Blur()); + Assert.Equal(1, cutd.Instance.Value); + + cutd.Render(pb => + { + pb.Add(a => a.Value, 1); + pb.Add(a => a.Step, null); + }); + input = cutd.Find("input"); + await cutd.InvokeAsync(() => input.Blur()); + Assert.Equal(1, cutd.Instance.Value); + var cutf = Context.Render>(pb => { pb.Add(a => a.Value, 1); @@ -386,6 +404,24 @@ public async Task Step_Ok() input = cutf.Find("input"); await cutf.InvokeAsync(() => input.Blur()); Assert.Equal(1, cutf.Instance.Value); + + cutf.Render(pb => + { + pb.Add(a => a.Value, 1); + pb.Add(a => a.Step, "any"); + }); + input = cutf.Find("input"); + await cutf.InvokeAsync(() => input.Blur()); + Assert.Equal(1, cutf.Instance.Value); + + cutf.Render(pb => + { + pb.Add(a => a.Value, 1); + pb.Add(a => a.Step, null); + }); + input = cutf.Find("input"); + await cutf.InvokeAsync(() => input.Blur()); + Assert.Equal(1, cutf.Instance.Value); } [Fact] From 942b6e3fc2ecd6d4ecf66fa878548829c301673b Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sat, 6 Jun 2026 11:02:00 +0800 Subject: [PATCH 05/30] =?UTF-8?q?refactor:=20=E7=A7=BB=E9=99=A4=20ParseDec?= =?UTF-8?q?imal=20=E6=96=B9=E6=B3=95=E4=BD=BF=E7=94=A8=E5=8E=9F=E7=94=9F?= =?UTF-8?q?=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../InputNumber/BootstrapInputNumber.razor.cs | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs b/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs index 2bc5ecf2f7e..1d4647c6595 100644 --- a/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs +++ b/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs @@ -191,8 +191,6 @@ protected override void OnAfterRender(bool firstRender) private string GetStepString() => (string.IsNullOrEmpty(StepString) || StepString.Equals("any", StringComparison.OrdinalIgnoreCase)) ? "1" : StepString; - private static decimal ParseDecimal(string value) => decimal.Parse(value, CultureInfo.InvariantCulture); - private static TValue ParseValue(string value) { return value.TryConvertTo(out var ret) @@ -208,17 +206,17 @@ private static TValue ParseValue(string value) var factor = increment ? 1 : -1; ret = value switch { - sbyte @sbyte => (TValue)(object)(sbyte)Math.Clamp(@sbyte + factor * ParseDecimal(step), sbyte.MinValue, sbyte.MaxValue), - byte @byte => (TValue)(object)(byte)Math.Clamp(@byte + factor * ParseDecimal(step), byte.MinValue, byte.MaxValue), - short @short => (TValue)(object)(short)Math.Clamp(@short + factor * ParseDecimal(step), short.MinValue, short.MaxValue), - ushort @ushort => (TValue)(object)(ushort)Math.Clamp(@ushort + factor * ParseDecimal(step), ushort.MinValue, ushort.MaxValue), - int @int => (TValue)(object)(int)Math.Clamp(@int + factor * ParseDecimal(step), int.MinValue, int.MaxValue), - uint @uint => (TValue)(object)(uint)Math.Clamp(@uint + factor * ParseDecimal(step), uint.MinValue, uint.MaxValue), - long @long => (TValue)(object)(long)Math.Clamp(@long + factor * ParseDecimal(step), long.MinValue, long.MaxValue), - ulong @ulong => (TValue)(object)(ulong)Math.Clamp(@ulong + factor * ParseDecimal(step), ulong.MinValue, ulong.MaxValue), - float @float => (TValue)(object)(@float + factor * float.Parse(step, CultureInfo.InvariantCulture)), - double @double => (TValue)(object)(@double + factor * double.Parse(step, CultureInfo.InvariantCulture)), - decimal @decimal => (TValue)(object)(@decimal + factor * ParseDecimal(step)), + sbyte @sbyte => (TValue)(object)Math.Clamp(@sbyte + (sbyte)factor * Convert.ToSByte(step), sbyte.MinValue, sbyte.MaxValue), + byte @byte => (TValue)(object)Math.Clamp(@byte + (byte)factor * Convert.ToByte(step), byte.MinValue, byte.MaxValue), + short @short => (TValue)(object)Math.Clamp(@short + (short)factor * Convert.ToInt16(step), short.MinValue, short.MaxValue), + ushort @ushort => (TValue)(object)Math.Clamp(@ushort + (ushort)factor * Convert.ToUInt16(step), ushort.MinValue, ushort.MaxValue), + int @int => (TValue)(object)Math.Clamp(@int + factor * Convert.ToInt32(step), int.MinValue, int.MaxValue), + uint @uint => (TValue)(object)Math.Clamp(@uint + (uint)factor * Convert.ToUInt32(step), uint.MinValue, uint.MaxValue), + long @long => (TValue)(object)Math.Clamp(@long + factor * Convert.ToInt64(step), long.MinValue, long.MaxValue), + ulong @ulong => (TValue)(object)Math.Clamp(@ulong + (ulong)factor * Convert.ToUInt64(step), ulong.MinValue, ulong.MaxValue), + float @float => (TValue)(object)Math.Clamp(@float + factor * Convert.ToSingle(step), float.MinValue, float.MaxValue), + double @double => (TValue)(object)Math.Clamp(@double + factor * Convert.ToDouble(step), double.MinValue, double.MaxValue), + decimal @decimal => (TValue)(object)(@decimal + factor * Convert.ToDecimal(step)), _ => value }; } @@ -249,6 +247,13 @@ private async Task OnClickInc() } } + // 检查数据是否可能包含小数点 + private static bool IsDecimalType() + { + var type = NullableUnderlyingType ?? typeof(TValue); + return type == typeof(float) || type == typeof(double) || type == typeof(decimal); + } + /// /// /// @@ -258,7 +263,7 @@ protected override async Task OnBlur() { CurrentValue = SetMax(SetMin(Value)); string stepValue = GetStepString(); - if (stepValue != "any" && (CurrentValue is float || CurrentValue is double || CurrentValue is decimal)) + if (IsDecimalType() && stepValue != "any") { // 先强转用,固定不变的文化信息,已解决全球化数字信息问题 stepValue = Convert.ToDecimal(stepValue).ToString(CultureInfo.InvariantCulture); From e04c10c2e352f394f870b132956241c492cc996d Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sat, 6 Jun 2026 11:05:24 +0800 Subject: [PATCH 06/30] =?UTF-8?q?refactor:=20=E7=A7=BB=E9=99=A4=20static?= =?UTF-8?q?=20=E5=85=B3=E9=94=AE=E5=AD=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/InputNumber/BootstrapInputNumber.razor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs b/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs index 1d4647c6595..ff007a45455 100644 --- a/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs +++ b/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs @@ -248,7 +248,7 @@ private async Task OnClickInc() } // 检查数据是否可能包含小数点 - private static bool IsDecimalType() + private bool IsDecimalType() { var type = NullableUnderlyingType ?? typeof(TValue); return type == typeof(float) || type == typeof(double) || type == typeof(decimal); From 9e044a1116e34eb0725112394a018295cd7a402d Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sat, 6 Jun 2026 11:30:45 +0800 Subject: [PATCH 07/30] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../InputNumber/BootstrapInputNumber.razor.cs | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs b/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs index ff007a45455..3472c73d898 100644 --- a/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs +++ b/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs @@ -206,17 +206,17 @@ private static TValue ParseValue(string value) var factor = increment ? 1 : -1; ret = value switch { - sbyte @sbyte => (TValue)(object)Math.Clamp(@sbyte + (sbyte)factor * Convert.ToSByte(step), sbyte.MinValue, sbyte.MaxValue), - byte @byte => (TValue)(object)Math.Clamp(@byte + (byte)factor * Convert.ToByte(step), byte.MinValue, byte.MaxValue), - short @short => (TValue)(object)Math.Clamp(@short + (short)factor * Convert.ToInt16(step), short.MinValue, short.MaxValue), - ushort @ushort => (TValue)(object)Math.Clamp(@ushort + (ushort)factor * Convert.ToUInt16(step), ushort.MinValue, ushort.MaxValue), - int @int => (TValue)(object)Math.Clamp(@int + factor * Convert.ToInt32(step), int.MinValue, int.MaxValue), - uint @uint => (TValue)(object)Math.Clamp(@uint + (uint)factor * Convert.ToUInt32(step), uint.MinValue, uint.MaxValue), - long @long => (TValue)(object)Math.Clamp(@long + factor * Convert.ToInt64(step), long.MinValue, long.MaxValue), - ulong @ulong => (TValue)(object)Math.Clamp(@ulong + (ulong)factor * Convert.ToUInt64(step), ulong.MinValue, ulong.MaxValue), - float @float => (TValue)(object)Math.Clamp(@float + factor * Convert.ToSingle(step), float.MinValue, float.MaxValue), - double @double => (TValue)(object)Math.Clamp(@double + factor * Convert.ToDouble(step), double.MinValue, double.MaxValue), - decimal @decimal => (TValue)(object)(@decimal + factor * Convert.ToDecimal(step)), + sbyte @sbyte => (TValue)(object)(sbyte)Math.Clamp(@sbyte + factor * Convert.ToDecimal(step), sbyte.MinValue, sbyte.MaxValue), + byte @byte => (TValue)(object)(byte)Math.Clamp(@byte + factor * Convert.ToDecimal(step), byte.MinValue, byte.MaxValue), + short @short => (TValue)(object)(short)Math.Clamp(@short + factor * Convert.ToDecimal(step), short.MinValue, short.MaxValue), + ushort @ushort => (TValue)(object)(ushort)Math.Clamp(@ushort + factor * Convert.ToDecimal(step), ushort.MinValue, ushort.MaxValue), + int @int => (TValue)(object)(int)Math.Clamp(@int + factor * Convert.ToDecimal(step), int.MinValue, int.MaxValue), + uint @uint => (TValue)(object)(uint)Math.Clamp(@uint + factor * Convert.ToDecimal(step), uint.MinValue, uint.MaxValue), + long @long => (TValue)(object)(long)Math.Clamp(@long + factor * Convert.ToDecimal(step), long.MinValue, long.MaxValue), + ulong @ulong => (TValue)(object)(ulong)Math.Clamp(@ulong + factor * Convert.ToDecimal(step), ulong.MinValue, ulong.MaxValue), + float @float => (TValue)(object)(float)Math.Clamp(@float + factor * Convert.ToSingle(step), float.MinValue, float.MaxValue), + double @double => (TValue)(object)(double)Math.Clamp(@double + factor * Convert.ToDouble(step), double.MinValue, double.MaxValue), + decimal @decimal => (TValue)(object)(decimal)Math.Clamp(@decimal + factor * Convert.ToDecimal(step), decimal.MinValue, decimal.MaxValue), _ => value }; } From 124efec73da8fadc20bcd8e8a1a5040185954783 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sat, 6 Jun 2026 11:30:56 +0800 Subject: [PATCH 08/30] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84=20Index?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/UnitTest/Components/InputNumberTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/UnitTest/Components/InputNumberTest.cs b/test/UnitTest/Components/InputNumberTest.cs index 6bddba6d00f..f3e99db25ad 100644 --- a/test/UnitTest/Components/InputNumberTest.cs +++ b/test/UnitTest/Components/InputNumberTest.cs @@ -214,7 +214,7 @@ public async Task Type_Ok(Type t) { builder.OpenComponent(0, typeof(BootstrapInputNumber<>).MakeGenericType(t)); builder.AddAttribute(1, "ShowButton", true); - builder.AddAttribute(1, "Max", "10"); + builder.AddAttribute(2, "Max", "10"); builder.CloseComponent(); }); var buttons = cut.FindAll("button"); From 8d7a1eafcd3ddddf4a529e7243ef9908f2b36301 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sat, 6 Jun 2026 11:34:07 +0800 Subject: [PATCH 09/30] =?UTF-8?q?test:=20=E6=B6=88=E9=99=A4=E8=AD=A6?= =?UTF-8?q?=E5=91=8A=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/UnitTest/Components/InputNumberTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/UnitTest/Components/InputNumberTest.cs b/test/UnitTest/Components/InputNumberTest.cs index f3e99db25ad..87d501e8b02 100644 --- a/test/UnitTest/Components/InputNumberTest.cs +++ b/test/UnitTest/Components/InputNumberTest.cs @@ -581,7 +581,7 @@ private void AssertInputNumberValueChanged(TValue value, string inputVal var cut = Context.Render>(pb => { pb.Add(a => a.Value, value); - pb.Add(a => a.ValueChanged, EventCallback.Factory.Create(this, v => currentValue = v)); + pb.Add(a => a.ValueChanged, EventCallback.Factory.Create(this, v => currentValue = v)); }); var input = cut.Find(".form-control"); From 8838638494ecf83cc0be450a03bffbda761fec9f Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sat, 6 Jun 2026 11:36:24 +0800 Subject: [PATCH 10/30] =?UTF-8?q?test:=20=E6=B6=88=E9=99=A4=E8=AD=A6?= =?UTF-8?q?=E5=91=8A=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/UnitTest/Components/InputNumberTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/UnitTest/Components/InputNumberTest.cs b/test/UnitTest/Components/InputNumberTest.cs index 87d501e8b02..e926f08aafb 100644 --- a/test/UnitTest/Components/InputNumberTest.cs +++ b/test/UnitTest/Components/InputNumberTest.cs @@ -470,11 +470,11 @@ public void PrivateFallback_Ok() value = calculateMethod.Invoke(null, [null, "1", true]); Assert.Null(value); - var cut = new BootstrapInputNumber() { Min = "test" }; + var cut = Context.Render>(pb => { pb.Add(a => a.Min, "test"); }); var setMinMethod = typeof(BootstrapInputNumber).GetMethod("SetMin", BindingFlags.NonPublic | BindingFlags.Instance); Assert.NotNull(setMinMethod); - var ex = Assert.Throws(() => setMinMethod.Invoke(cut, [1])); + var ex = Assert.Throws(() => setMinMethod.Invoke(cut.Instance, [1])); Assert.IsType(ex.InnerException); } From 2b85742f98c51e6c13cfa951b9099c6fe57f683a Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sat, 6 Jun 2026 13:24:35 +0800 Subject: [PATCH 11/30] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E6=8F=90=E9=AB=98=E5=8F=AF=E8=AF=BB=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../InputNumber/BootstrapInputNumber.razor.cs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs b/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs index 3472c73d898..93f900cbca6 100644 --- a/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs +++ b/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs @@ -265,11 +265,24 @@ protected override async Task OnBlur() string stepValue = GetStepString(); if (IsDecimalType() && stepValue != "any") { - // 先强转用,固定不变的文化信息,已解决全球化数字信息问题 stepValue = Convert.ToDecimal(stepValue).ToString(CultureInfo.InvariantCulture); int precision = stepValue.Contains('.') ? stepValue.Split('.')[1].Length : 0; - dynamic val = CurrentValue!; - CurrentValue = val == 0 ? CurrentValue : (TValue)Math.Round(val, precision); + + object val = CurrentValue!; + if (CurrentValue is float v1) + { + val = (float)Math.Round(v1, precision); + } + else if (CurrentValue is double v2) + { + val = Math.Round(v2, precision); + } + else + { + val = Math.Round((decimal)val, precision); + } + + CurrentValue = (TValue)val; } } else From 3e3a6112d8b89314d6ca2508cb0247ec3fe95c4a Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sat, 6 Jun 2026 13:24:42 +0800 Subject: [PATCH 12/30] =?UTF-8?q?test:=20=E6=9B=B4=E6=96=B0=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/UnitTest/Components/InputNumberTest.cs | 122 +++++--------------- 1 file changed, 27 insertions(+), 95 deletions(-) diff --git a/test/UnitTest/Components/InputNumberTest.cs b/test/UnitTest/Components/InputNumberTest.cs index e926f08aafb..b276175d437 100644 --- a/test/UnitTest/Components/InputNumberTest.cs +++ b/test/UnitTest/Components/InputNumberTest.cs @@ -321,107 +321,39 @@ public async Task ShowButton_EmptyStep_Ok() Assert.Equal(2, cut.Instance.Value); } - [Fact] - public async Task Step_Ok() + [Theory] + [InlineData("float", "0.012", "0.01", "0.01")] + [InlineData("float", "0.012", "1", "0")] + [InlineData("float", "2.012", "1", "2")] + [InlineData("double", "0.012", "0.01", "0.01")] + [InlineData("double", "0.012", "1", "0")] + [InlineData("double", "2.012", "1", "2")] + [InlineData("decimal", "0.012", "0.01", "0.01")] + [InlineData("decimal", "0.012", "1", "0")] + [InlineData("decimal", "2.012", "1", "2")] + [InlineData("int", "0", "1", "0")] + [InlineData("int", "1", "1", "1")] + [InlineData("int", "2", "1", "2")] + public async Task Step_Ok(string typeName, string valueText, string step, string expected) { - var cut = Context.Render>(pb => + var (type, val) = typeName switch { - pb.Add(a => a.Value, 1); - pb.Add(a => a.Step, "0.001"); - }); - var input = cut.Find("input"); - await cut.InvokeAsync(() => input.Blur()); - Assert.Equal(1, cut.Instance.Value); + "float" => (typeof(float), (object)float.Parse(valueText, CultureInfo.InvariantCulture)), + "double" => (typeof(double), (object)double.Parse(valueText, CultureInfo.InvariantCulture)), + "decimal" => (typeof(decimal), (object)decimal.Parse(valueText, CultureInfo.InvariantCulture)), + _ => (typeof(int), (object)int.Parse(valueText, CultureInfo.InvariantCulture)) + }; - cut.Render(pb => - { - pb.Add(a => a.Value, 1); - pb.Add(a => a.Step, null); - }); - input = cut.Find("input"); - await cut.InvokeAsync(() => input.Blur()); - Assert.Equal(1, cut.Instance.Value); - - cut.Render(pb => - { - pb.Add(a => a.Value, 1); - pb.Add(a => a.Step, "any"); - }); - input = cut.Find("input"); - await cut.InvokeAsync(() => input.Blur()); - Assert.Equal(1, cut.Instance.Value); - - cut.Render(pb => - { - pb.Add(a => a.Value, 0); - pb.Add(a => a.Step, "0.001"); - }); - input = cut.Find("input"); - await cut.InvokeAsync(() => input.Blur()); - Assert.Equal(0, cut.Instance.Value); - - cut.Render(pb => + var cut = Context.Render(builder => { - pb.Add(a => a.Value, 98.12356m); - pb.Add(a => a.Step, "0.001"); + builder.OpenComponent(0, typeof(BootstrapInputNumber<>).MakeGenericType(type)); + builder.AddAttribute(1, "Value", val); + builder.AddAttribute(2, "Step", step); + builder.CloseComponent(); }); - input = cut.Find("input"); + var input = cut.Find("input"); await cut.InvokeAsync(() => input.Blur()); - Assert.Equal(98.124m, cut.Instance.Value); - - var cutd = Context.Render>(pb => - { - pb.Add(a => a.Value, 1); - pb.Add(a => a.Step, "0.001"); - }); - input = cutd.Find("input"); - await cutd.InvokeAsync(() => input.Blur()); - Assert.Equal(1, cutd.Instance.Value); - - cutd.Render(pb => - { - pb.Add(a => a.Value, 1); - pb.Add(a => a.Step, "any"); - }); - input = cutd.Find("input"); - await cutd.InvokeAsync(() => input.Blur()); - Assert.Equal(1, cutd.Instance.Value); - - cutd.Render(pb => - { - pb.Add(a => a.Value, 1); - pb.Add(a => a.Step, null); - }); - input = cutd.Find("input"); - await cutd.InvokeAsync(() => input.Blur()); - Assert.Equal(1, cutd.Instance.Value); - - var cutf = Context.Render>(pb => - { - pb.Add(a => a.Value, 1); - pb.Add(a => a.Step, "0.001"); - }); - input = cutf.Find("input"); - await cutf.InvokeAsync(() => input.Blur()); - Assert.Equal(1, cutf.Instance.Value); - - cutf.Render(pb => - { - pb.Add(a => a.Value, 1); - pb.Add(a => a.Step, "any"); - }); - input = cutf.Find("input"); - await cutf.InvokeAsync(() => input.Blur()); - Assert.Equal(1, cutf.Instance.Value); - - cutf.Render(pb => - { - pb.Add(a => a.Value, 1); - pb.Add(a => a.Step, null); - }); - input = cutf.Find("input"); - await cutf.InvokeAsync(() => input.Blur()); - Assert.Equal(1, cutf.Instance.Value); + Assert.Equal(expected, input.GetAttribute("value")); } [Fact] From 6be2fb24c57e5ea1fe66c2b504e46611ccb0ad7d Mon Sep 17 00:00:00 2001 From: Tony996 <6914529@qq.com> Date: Sat, 6 Jun 2026 15:53:52 +0800 Subject: [PATCH 13/30] =?UTF-8?q?docs=EF=BC=9A=E6=9B=B4=E6=96=B0InputNumbe?= =?UTF-8?q?r=E7=BB=84=E4=BB=B6=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 更新组件文档,主要注意HTML标准中input标签中step、min、max同时存在时可能带来意想不到的结果,该问题由HTML标准引起,无法修复 --- src/BootstrapBlazor.Server/Locales/en-US.json | 2 +- src/BootstrapBlazor.Server/Locales/zh-CN.json | 2 +- .../InputNumber/BootstrapInputNumber.razor.cs | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/BootstrapBlazor.Server/Locales/en-US.json b/src/BootstrapBlazor.Server/Locales/en-US.json index a2d81fffe1f..27870795cf8 100644 --- a/src/BootstrapBlazor.Server/Locales/en-US.json +++ b/src/BootstrapBlazor.Server/Locales/en-US.json @@ -2769,7 +2769,7 @@ "InputNumbersColorTitle": "Color", "InputNumbersDateTypeIntro": "This component uses generic support the underlying data type int short long float double decimal", "InputNumbersDateTypeTitle": "Data type", - "InputNumbersDescription": "Only standard numeric values are allowed, and custom ranges and other advanced features are supported", + "InputNumbersDescription": "Only standard numeric values are allowed, and custom ranges and other advanced features are supported, It is recommended to adopt a custom attribute validation approach. When the Step, Min, and Max attributes are set simultaneously, unexpected results may occur due to HTML standards", "InputNumbersDisabledIntro": "When you set the IsDisabled property value to true, the component suppresses input", "InputNumbersDisabledTitle": "Disable", "InputNumbersNormalIntro": "The Numer numeric type displays a text box, and the mobile side automatically pops up the numeric keypad", diff --git a/src/BootstrapBlazor.Server/Locales/zh-CN.json b/src/BootstrapBlazor.Server/Locales/zh-CN.json index 8495be1a2e2..598d0458df0 100644 --- a/src/BootstrapBlazor.Server/Locales/zh-CN.json +++ b/src/BootstrapBlazor.Server/Locales/zh-CN.json @@ -2769,7 +2769,7 @@ "InputNumbersColorTitle": "颜色", "InputNumbersDateTypeIntro": "本组件采用泛型支持 int short long float double decimal 基础数据类型", "InputNumbersDateTypeTitle": "数据类型", - "InputNumbersDescription": "仅允许输入标准的数字值,支持自定义范围及其他高级功能", + "InputNumbersDescription": "仅允许输入标准的数字值,支持自定义范围及其他高级功能,建议采用自定义属性验证方式,当Step、Min、Max 属性同时设置时,由于HTML标准会导致可能意想不到的结果", "InputNumbersDisabledIntro": "设置 IsDisabled 属性值为 true 时,组件禁止输入", "InputNumbersDisabledTitle": "禁用", "InputNumbersNormalIntro": "Number 数值类型显示文本框,移动端自动弹出数字键盘", diff --git a/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs b/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs index 93f900cbca6..6e7ee2ca664 100644 --- a/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs +++ b/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs @@ -52,22 +52,22 @@ public partial class BootstrapInputNumber public Func? OnDecrement { get; set; } /// - /// 获得/设置 最小值 - /// Gets or sets Minimum Value + /// 获得/设置 最小值,建议采用自定义属性验证方式,当Step、Min、Max 属性同时设置时,由于HTML标准会导致可能意想不到的结果 + /// Gets or sets Minimum Value, It is recommended to adopt a custom attribute validation approach. When the Step, Min, and Max attributes are set simultaneously, unexpected results may occur due to HTML standards /// [Parameter] public string? Min { get; set; } /// - /// 获得/设置 最大值 - /// Gets or sets Maximum Value + /// 获得/设置 最大值,建议采用自定义属性验证方式,当Step、Min、Max 属性同时设置时,由于HTML标准会导致可能意想不到的结果 + /// Gets or sets Maximum Value, It is recommended to adopt a custom attribute validation approach. When the Step, Min, and Max attributes are set simultaneously, unexpected results may occur due to HTML standards /// [Parameter] public string? Max { get; set; } /// - /// 获得/设置 步长 默认为 null - /// Gets or sets Step. Default null + /// 获得/设置 步长 默认为 null,建议采用自定义属性验证方式,当Step、Min、Max 属性同时设置时,由于HTML标准会导致可能意想不到的结果 + /// Gets or sets Step. Default null, It is recommended to adopt a custom attribute validation approach. When the Step, Min, and Max attributes are set simultaneously, unexpected results may occur due to HTML standards /// [Parameter] public string? Step { get; set; } From bd5f7edbd5f5550b8e03c82cb8fa354fb05a7233 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sat, 6 Jun 2026 16:57:31 +0800 Subject: [PATCH 14/30] =?UTF-8?q?revert:=20=E6=92=A4=E9=94=80=E5=9B=9B?= =?UTF-8?q?=E8=88=8D=E4=BA=94=E5=85=A5=E5=86=85=E7=BD=AE=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../InputNumber/BootstrapInputNumber.razor.cs | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs b/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs index 6e7ee2ca664..407df97c3f0 100644 --- a/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs +++ b/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs @@ -262,28 +262,6 @@ protected override async Task OnBlur() if (!PreviousParsingAttemptFailed) { CurrentValue = SetMax(SetMin(Value)); - string stepValue = GetStepString(); - if (IsDecimalType() && stepValue != "any") - { - stepValue = Convert.ToDecimal(stepValue).ToString(CultureInfo.InvariantCulture); - int precision = stepValue.Contains('.') ? stepValue.Split('.')[1].Length : 0; - - object val = CurrentValue!; - if (CurrentValue is float v1) - { - val = (float)Math.Round(v1, precision); - } - else if (CurrentValue is double v2) - { - val = Math.Round(v2, precision); - } - else - { - val = Math.Round((decimal)val, precision); - } - - CurrentValue = (TValue)val; - } } else { From 8af3b060fee6b0105cbf43541a6177ddb5ba9f2c Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sat, 6 Jun 2026 16:59:35 +0800 Subject: [PATCH 15/30] =?UTF-8?q?test:=20=E6=92=A4=E9=94=80=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/UnitTest/Components/InputNumberTest.cs | 35 --------------------- 1 file changed, 35 deletions(-) diff --git a/test/UnitTest/Components/InputNumberTest.cs b/test/UnitTest/Components/InputNumberTest.cs index b276175d437..a1d4e365f6e 100644 --- a/test/UnitTest/Components/InputNumberTest.cs +++ b/test/UnitTest/Components/InputNumberTest.cs @@ -321,41 +321,6 @@ public async Task ShowButton_EmptyStep_Ok() Assert.Equal(2, cut.Instance.Value); } - [Theory] - [InlineData("float", "0.012", "0.01", "0.01")] - [InlineData("float", "0.012", "1", "0")] - [InlineData("float", "2.012", "1", "2")] - [InlineData("double", "0.012", "0.01", "0.01")] - [InlineData("double", "0.012", "1", "0")] - [InlineData("double", "2.012", "1", "2")] - [InlineData("decimal", "0.012", "0.01", "0.01")] - [InlineData("decimal", "0.012", "1", "0")] - [InlineData("decimal", "2.012", "1", "2")] - [InlineData("int", "0", "1", "0")] - [InlineData("int", "1", "1", "1")] - [InlineData("int", "2", "1", "2")] - public async Task Step_Ok(string typeName, string valueText, string step, string expected) - { - var (type, val) = typeName switch - { - "float" => (typeof(float), (object)float.Parse(valueText, CultureInfo.InvariantCulture)), - "double" => (typeof(double), (object)double.Parse(valueText, CultureInfo.InvariantCulture)), - "decimal" => (typeof(decimal), (object)decimal.Parse(valueText, CultureInfo.InvariantCulture)), - _ => (typeof(int), (object)int.Parse(valueText, CultureInfo.InvariantCulture)) - }; - - var cut = Context.Render(builder => - { - builder.OpenComponent(0, typeof(BootstrapInputNumber<>).MakeGenericType(type)); - builder.AddAttribute(1, "Value", val); - builder.AddAttribute(2, "Step", step); - builder.CloseComponent(); - }); - var input = cut.Find("input"); - await cut.InvokeAsync(() => input.Blur()); - Assert.Equal(expected, input.GetAttribute("value")); - } - [Fact] public async Task MinMax_Ok() { From 4ccdda4c74536d0562c45e3d461e9ef99d5eee6d Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sat, 6 Jun 2026 17:01:15 +0800 Subject: [PATCH 16/30] =?UTF-8?q?revert:=20=E6=92=A4=E9=94=80=20IsDecimalT?= =?UTF-8?q?ype=20=E5=88=A4=E6=96=AD=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/InputNumber/BootstrapInputNumber.razor.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs b/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs index 407df97c3f0..22268e70779 100644 --- a/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs +++ b/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs @@ -247,13 +247,6 @@ private async Task OnClickInc() } } - // 检查数据是否可能包含小数点 - private bool IsDecimalType() - { - var type = NullableUnderlyingType ?? typeof(TValue); - return type == typeof(float) || type == typeof(double) || type == typeof(decimal); - } - /// /// /// From 7b5c3b09c50c36748423b7ac79671981c4d8f877 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sat, 6 Jun 2026 17:01:30 +0800 Subject: [PATCH 17/30] =?UTF-8?q?Revert=20"docs=EF=BC=9A=E6=9B=B4=E6=96=B0?= =?UTF-8?q?InputNumber=E7=BB=84=E4=BB=B6=E6=96=87=E6=A1=A3"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 6be2fb24c57e5ea1fe66c2b504e46611ccb0ad7d. --- src/BootstrapBlazor.Server/Locales/en-US.json | 2 +- src/BootstrapBlazor.Server/Locales/zh-CN.json | 2 +- .../InputNumber/BootstrapInputNumber.razor.cs | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/BootstrapBlazor.Server/Locales/en-US.json b/src/BootstrapBlazor.Server/Locales/en-US.json index 27870795cf8..a2d81fffe1f 100644 --- a/src/BootstrapBlazor.Server/Locales/en-US.json +++ b/src/BootstrapBlazor.Server/Locales/en-US.json @@ -2769,7 +2769,7 @@ "InputNumbersColorTitle": "Color", "InputNumbersDateTypeIntro": "This component uses generic support the underlying data type int short long float double decimal", "InputNumbersDateTypeTitle": "Data type", - "InputNumbersDescription": "Only standard numeric values are allowed, and custom ranges and other advanced features are supported, It is recommended to adopt a custom attribute validation approach. When the Step, Min, and Max attributes are set simultaneously, unexpected results may occur due to HTML standards", + "InputNumbersDescription": "Only standard numeric values are allowed, and custom ranges and other advanced features are supported", "InputNumbersDisabledIntro": "When you set the IsDisabled property value to true, the component suppresses input", "InputNumbersDisabledTitle": "Disable", "InputNumbersNormalIntro": "The Numer numeric type displays a text box, and the mobile side automatically pops up the numeric keypad", diff --git a/src/BootstrapBlazor.Server/Locales/zh-CN.json b/src/BootstrapBlazor.Server/Locales/zh-CN.json index 598d0458df0..8495be1a2e2 100644 --- a/src/BootstrapBlazor.Server/Locales/zh-CN.json +++ b/src/BootstrapBlazor.Server/Locales/zh-CN.json @@ -2769,7 +2769,7 @@ "InputNumbersColorTitle": "颜色", "InputNumbersDateTypeIntro": "本组件采用泛型支持 int short long float double decimal 基础数据类型", "InputNumbersDateTypeTitle": "数据类型", - "InputNumbersDescription": "仅允许输入标准的数字值,支持自定义范围及其他高级功能,建议采用自定义属性验证方式,当Step、Min、Max 属性同时设置时,由于HTML标准会导致可能意想不到的结果", + "InputNumbersDescription": "仅允许输入标准的数字值,支持自定义范围及其他高级功能", "InputNumbersDisabledIntro": "设置 IsDisabled 属性值为 true 时,组件禁止输入", "InputNumbersDisabledTitle": "禁用", "InputNumbersNormalIntro": "Number 数值类型显示文本框,移动端自动弹出数字键盘", diff --git a/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs b/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs index 22268e70779..7172400fa5f 100644 --- a/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs +++ b/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs @@ -52,22 +52,22 @@ public partial class BootstrapInputNumber public Func? OnDecrement { get; set; } /// - /// 获得/设置 最小值,建议采用自定义属性验证方式,当Step、Min、Max 属性同时设置时,由于HTML标准会导致可能意想不到的结果 - /// Gets or sets Minimum Value, It is recommended to adopt a custom attribute validation approach. When the Step, Min, and Max attributes are set simultaneously, unexpected results may occur due to HTML standards + /// 获得/设置 最小值 + /// Gets or sets Minimum Value /// [Parameter] public string? Min { get; set; } /// - /// 获得/设置 最大值,建议采用自定义属性验证方式,当Step、Min、Max 属性同时设置时,由于HTML标准会导致可能意想不到的结果 - /// Gets or sets Maximum Value, It is recommended to adopt a custom attribute validation approach. When the Step, Min, and Max attributes are set simultaneously, unexpected results may occur due to HTML standards + /// 获得/设置 最大值 + /// Gets or sets Maximum Value /// [Parameter] public string? Max { get; set; } /// - /// 获得/设置 步长 默认为 null,建议采用自定义属性验证方式,当Step、Min、Max 属性同时设置时,由于HTML标准会导致可能意想不到的结果 - /// Gets or sets Step. Default null, It is recommended to adopt a custom attribute validation approach. When the Step, Min, and Max attributes are set simultaneously, unexpected results may occur due to HTML standards + /// 获得/设置 步长 默认为 null + /// Gets or sets Step. Default null /// [Parameter] public string? Step { get; set; } From 79fd37ac93a9cf128487adaa933bcd167f750301 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sat, 6 Jun 2026 17:15:44 +0800 Subject: [PATCH 18/30] =?UTF-8?q?test:=20=E8=A1=A5=E5=85=A8=20StepSettings?= =?UTF-8?q?=20=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/BootstrapBlazor/Options/StepSettings.cs | 76 +++++++++++++++++---- 1 file changed, 64 insertions(+), 12 deletions(-) diff --git a/src/BootstrapBlazor/Options/StepSettings.cs b/src/BootstrapBlazor/Options/StepSettings.cs index 6e1d079da6a..cba82a113b4 100644 --- a/src/BootstrapBlazor/Options/StepSettings.cs +++ b/src/BootstrapBlazor/Options/StepSettings.cs @@ -3,6 +3,8 @@ // See the LICENSE file in the project root for more information. // Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone +using System.Globalization; + namespace BootstrapBlazor.Components; /// @@ -11,6 +13,24 @@ namespace BootstrapBlazor.Components; /// public class StepSettings { + /// + /// 获得/设置 sbyte 数据类型步长 默认 null 未设置 + /// Gets or sets sbyte type step default null + /// + public sbyte? SByte { get; set; } + + /// + /// 获得/设置 byte 数据类型步长 默认 null 未设置 + /// Gets or sets byte type step default null + /// + public byte? Byte { get; set; } + + /// + /// 获得/设置 uint 数据类型步长 默认 null 未设置 + /// Gets or sets uint type step default null + /// + public uint? UInt { get; set; } + /// /// 获得/设置 int 数据类型步长 默认 null 未设置 /// Gets or sets int type step default null @@ -23,12 +43,24 @@ public class StepSettings /// public int? Long { get; set; } + /// + /// 获得/设置 ulong 数据类型步长 默认 null 未设置 + /// Gets or sets ulong type step default null + /// + public ulong? ULong { get; set; } + /// /// 获得/设置 short 数据类型步长 默认 null 未设置 /// Gets or sets short type step default null /// public int? Short { get; set; } + /// + /// 获得/设置 ushort 数据类型步长 默认 null 未设置 + /// Gets or sets ushort type step default null + /// + public ushort? UShort { get; set; } + /// /// 获得/设置 float 数据类型步长 默认 null 未设置 /// Gets or sets float type step default null @@ -55,29 +87,49 @@ public class StepSettings public string? GetStep(Type type) { string? ret = null; - if (type == typeof(int)) + if (type == typeof(sbyte)) + { + ret = SByte?.ToString(CultureInfo.InvariantCulture); + } + else if (type == typeof(byte)) + { + ret = Byte?.ToString(CultureInfo.InvariantCulture); + } + else if (type == typeof(uint)) + { + ret = UInt?.ToString(CultureInfo.InvariantCulture); + } + else if (type == typeof(int)) + { + ret = Int?.ToString(CultureInfo.InvariantCulture); + } + else if (type == typeof(long)) + { + ret = Long?.ToString(CultureInfo.InvariantCulture); + } + else if (type == typeof(short)) { - ret = Int?.ToString(); + ret = Short?.ToString(CultureInfo.InvariantCulture); } - if (type == typeof(long)) + else if (type == typeof(ushort)) { - ret = Long?.ToString(); + ret = UShort?.ToString(CultureInfo.InvariantCulture); } - if (type == typeof(short)) + else if (type == typeof(ulong)) { - ret = Short?.ToString(); + ret = ULong?.ToString(CultureInfo.InvariantCulture); } - if (type == typeof(float)) + else if (type == typeof(float)) { - ret = Float?.ToString(); + ret = Float?.ToString(CultureInfo.InvariantCulture); } - if (type == typeof(double)) + else if (type == typeof(double)) { - ret = Double?.ToString(); + ret = Double?.ToString(CultureInfo.InvariantCulture); } - if (type == typeof(decimal)) + else if (type == typeof(decimal)) { - ret = Decimal?.ToString(); + ret = Decimal?.ToString(CultureInfo.InvariantCulture); } return ret; } From 9b88e74a6b90b775c82ca076af3dc44b3aec25dc Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sat, 6 Jun 2026 17:15:52 +0800 Subject: [PATCH 19/30] =?UTF-8?q?test:=20=E6=9B=B4=E6=96=B0=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Options/BootstrapBlazorOptionsTest.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/UnitTest/Options/BootstrapBlazorOptionsTest.cs b/test/UnitTest/Options/BootstrapBlazorOptionsTest.cs index 48a184b5f02..452b755daba 100644 --- a/test/UnitTest/Options/BootstrapBlazorOptionsTest.cs +++ b/test/UnitTest/Options/BootstrapBlazorOptionsTest.cs @@ -60,23 +60,38 @@ public void Options_StepSettings() options.StepSettings = new(); + Assert.Null(options.GetStep()); + Assert.Null(options.GetStep()); Assert.Null(options.GetStep()); + Assert.Null(options.GetStep()); Assert.Null(options.GetStep()); + Assert.Null(options.GetStep()); Assert.Null(options.GetStep()); + Assert.Null(options.GetStep()); Assert.Null(options.GetStep()); Assert.Null(options.GetStep()); Assert.Null(options.GetStep()); + options.StepSettings.SByte = 1; + options.StepSettings.Byte = 2; options.StepSettings.Short = 1; + options.StepSettings.UShort = 2; options.StepSettings.Int = 2; + options.StepSettings.UInt = 3; options.StepSettings.Long = 3; + options.StepSettings.ULong = 4; options.StepSettings.Float = 0.1f; options.StepSettings.Double = 0.01d; options.StepSettings.Decimal = 0.001M; + Assert.Equal("1", options.GetStep()); + Assert.Equal("2", options.GetStep()); Assert.Equal("1", options.GetStep()); + Assert.Equal("2", options.GetStep()); Assert.Equal("2", options.GetStep()); + Assert.Equal("3", options.GetStep()); Assert.Equal("3", options.GetStep(typeof(long?))); + Assert.Equal("4", options.GetStep()); Assert.Equal("0.1", options.GetStep()); Assert.Equal("0.01", options.GetStep()); From 236fb4b662afb3a9ad300ad530b51ce5a6bfc9c5 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sat, 6 Jun 2026 17:18:42 +0800 Subject: [PATCH 20/30] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=20Step=20?= =?UTF-8?q?=E9=BB=98=E8=AE=A4=E5=80=BC=E8=A7=84=E5=88=99=E5=85=81=E8=AE=B8?= =?UTF-8?q?=E5=B0=8F=E6=95=B0=E7=82=B9=E7=9A=84=E4=BD=BF=E7=94=A8=20any=20?= =?UTF-8?q?=E5=85=B6=E4=BB=96=E4=BD=BF=E7=94=A8=20null?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../InputNumber/BootstrapInputNumber.razor.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs b/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs index 7172400fa5f..405bdab8ce9 100644 --- a/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs +++ b/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs @@ -133,7 +133,7 @@ protected override void OnParametersSet() MinusIcon ??= IconTheme.GetIconByKey(ComponentIcons.InputNumberMinusIcon); PlusIcon ??= IconTheme.GetIconByKey(ComponentIcons.InputNumberPlusIcon); - StepString = Step ?? StepOption.CurrentValue.GetStep() ?? "any"; + StepString = Step ?? StepOption.CurrentValue.GetStep() ?? GetStepStringByType(); if (Value is null) { @@ -198,6 +198,15 @@ private static TValue ParseValue(string value) : throw new InvalidOperationException($"Unsupported type {typeof(TValue)}"); } + private string GetStepStringByType() => IsDecimalType() ? "any" : "null"; + + private bool IsDecimalType() + { + // 检查是否允许带小数点数据类型 + var type = NullableUnderlyingType ?? typeof(TValue); + return type == typeof(float) || type == typeof(double) || type == typeof(decimal); + } + private static TValue? Calculate(TValue? value, string step, bool increment) { TValue? ret = default; From 6cd62a562253d961a5c0426a3876a3ca630d59e8 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sat, 6 Jun 2026 17:57:31 +0800 Subject: [PATCH 21/30] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84=20GetStepS?= =?UTF-8?q?tring=20=E6=96=B9=E6=B3=95=E6=94=AF=E6=8C=81=20=E7=BB=86?= =?UTF-8?q?=E5=8C=96=20any=20=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../InputNumber/BootstrapInputNumber.razor.cs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs b/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs index 405bdab8ce9..604db4ccae6 100644 --- a/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs +++ b/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs @@ -189,7 +189,19 @@ protected override void OnAfterRender(bool firstRender) _ => throw new InvalidOperationException($"Unsupported type {value!.GetType()}") }; - private string GetStepString() => (string.IsNullOrEmpty(StepString) || StepString.Equals("any", StringComparison.OrdinalIgnoreCase)) ? "1" : StepString; + private string GetStepString() + { + if (StepString.Equals("null", StringComparison.OrdinalIgnoreCase)) + { + return "1"; + } + else if (StepString.Equals("any", StringComparison.OrdinalIgnoreCase)) + { + return IsDecimalType() ? "1" : StepString; + } + + return StepString; + } private static TValue ParseValue(string value) { From 75f309dc8d1b7bf94c0bab613abeb1f21a8215d8 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sat, 6 Jun 2026 17:57:39 +0800 Subject: [PATCH 22/30] =?UTF-8?q?test:=20=E6=9B=B4=E6=96=B0=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/UnitTest/Components/InputNumberTest.cs | 28 ++++++++++++--------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/test/UnitTest/Components/InputNumberTest.cs b/test/UnitTest/Components/InputNumberTest.cs index a1d4e365f6e..cfbab979670 100644 --- a/test/UnitTest/Components/InputNumberTest.cs +++ b/test/UnitTest/Components/InputNumberTest.cs @@ -197,24 +197,28 @@ public async Task ShowButton_Ok() } [Theory] - [InlineData(typeof(sbyte))] - [InlineData(typeof(byte))] - [InlineData(typeof(short))] - [InlineData(typeof(ushort))] - [InlineData(typeof(int))] - [InlineData(typeof(uint))] - [InlineData(typeof(long))] - [InlineData(typeof(ulong))] - [InlineData(typeof(float))] - [InlineData(typeof(double))] - [InlineData(typeof(decimal))] - public async Task Type_Ok(Type t) + [InlineData(typeof(sbyte), null)] + [InlineData(typeof(byte), null)] + [InlineData(typeof(short), null)] + [InlineData(typeof(ushort), null)] + [InlineData(typeof(int), null)] + [InlineData(typeof(uint), null)] + [InlineData(typeof(long), null)] + [InlineData(typeof(ulong), null)] + [InlineData(typeof(float), null)] + [InlineData(typeof(double), null)] + [InlineData(typeof(decimal), null)] + [InlineData(typeof(float), "any")] + [InlineData(typeof(double), "any")] + [InlineData(typeof(decimal), "any")] + public async Task Type_Ok(Type t, string? step) { var cut = Context.Render(builder => { builder.OpenComponent(0, typeof(BootstrapInputNumber<>).MakeGenericType(t)); builder.AddAttribute(1, "ShowButton", true); builder.AddAttribute(2, "Max", "10"); + builder.AddAttribute(3, "Step", step); builder.CloseComponent(); }); var buttons = cut.FindAll("button"); From d53703949a036e1da78199fdb7633099ec88902f Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sat, 6 Jun 2026 18:03:57 +0800 Subject: [PATCH 23/30] =?UTF-8?q?refactor:=20=E6=9B=B4=E6=96=B0=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/InputNumber/BootstrapInputNumber.razor.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs b/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs index 604db4ccae6..7247bc67347 100644 --- a/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs +++ b/src/BootstrapBlazor/Components/InputNumber/BootstrapInputNumber.razor.cs @@ -191,14 +191,12 @@ protected override void OnAfterRender(bool firstRender) private string GetStepString() { - if (StepString.Equals("null", StringComparison.OrdinalIgnoreCase)) + if (string.IsNullOrEmpty(StepString) + || StepString.Equals("null", StringComparison.OrdinalIgnoreCase) + || StepString.Equals("any", StringComparison.OrdinalIgnoreCase)) { return "1"; } - else if (StepString.Equals("any", StringComparison.OrdinalIgnoreCase)) - { - return IsDecimalType() ? "1" : StepString; - } return StepString; } From 79f9016a04c8d5b8983f1be3976102631fbbf08f Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sat, 6 Jun 2026 18:04:04 +0800 Subject: [PATCH 24/30] =?UTF-8?q?test:=20=E6=9B=B4=E6=96=B0=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/UnitTest/Components/InputNumberTest.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/UnitTest/Components/InputNumberTest.cs b/test/UnitTest/Components/InputNumberTest.cs index cfbab979670..d7ff634d722 100644 --- a/test/UnitTest/Components/InputNumberTest.cs +++ b/test/UnitTest/Components/InputNumberTest.cs @@ -202,15 +202,14 @@ public async Task ShowButton_Ok() [InlineData(typeof(short), null)] [InlineData(typeof(ushort), null)] [InlineData(typeof(int), null)] + [InlineData(typeof(int), "any")] [InlineData(typeof(uint), null)] [InlineData(typeof(long), null)] [InlineData(typeof(ulong), null)] [InlineData(typeof(float), null)] + [InlineData(typeof(float), "any")] [InlineData(typeof(double), null)] [InlineData(typeof(decimal), null)] - [InlineData(typeof(float), "any")] - [InlineData(typeof(double), "any")] - [InlineData(typeof(decimal), "any")] public async Task Type_Ok(Type t, string? step) { var cut = Context.Render(builder => From 53aa819f90843d262c21f5bb496bf36ab4f125a3 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sat, 6 Jun 2026 18:06:35 +0800 Subject: [PATCH 25/30] =?UTF-8?q?doc:=20=E8=B0=83=E6=95=B4=E9=A1=BA?= =?UTF-8?q?=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/UnitTest/Components/InputNumberTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/UnitTest/Components/InputNumberTest.cs b/test/UnitTest/Components/InputNumberTest.cs index d7ff634d722..99b16776498 100644 --- a/test/UnitTest/Components/InputNumberTest.cs +++ b/test/UnitTest/Components/InputNumberTest.cs @@ -202,14 +202,14 @@ public async Task ShowButton_Ok() [InlineData(typeof(short), null)] [InlineData(typeof(ushort), null)] [InlineData(typeof(int), null)] - [InlineData(typeof(int), "any")] [InlineData(typeof(uint), null)] [InlineData(typeof(long), null)] [InlineData(typeof(ulong), null)] [InlineData(typeof(float), null)] - [InlineData(typeof(float), "any")] [InlineData(typeof(double), null)] [InlineData(typeof(decimal), null)] + [InlineData(typeof(int), "any")] + [InlineData(typeof(float), "any")] public async Task Type_Ok(Type t, string? step) { var cut = Context.Render(builder => From 63c5aceee59dd630eded4a891f1ae06510fb7621 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sat, 6 Jun 2026 18:16:41 +0800 Subject: [PATCH 26/30] =?UTF-8?q?refactor:=20=E8=B0=83=E6=95=B4=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E7=B1=BB=E5=9E=8B=E6=8E=A5=E6=94=B6=20any=20=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/BootstrapBlazor/Options/StepSettings.cs | 46 ++++++++++----------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/src/BootstrapBlazor/Options/StepSettings.cs b/src/BootstrapBlazor/Options/StepSettings.cs index cba82a113b4..9487e21f9a5 100644 --- a/src/BootstrapBlazor/Options/StepSettings.cs +++ b/src/BootstrapBlazor/Options/StepSettings.cs @@ -3,8 +3,6 @@ // See the LICENSE file in the project root for more information. // Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone -using System.Globalization; - namespace BootstrapBlazor.Components; /// @@ -17,67 +15,67 @@ public class StepSettings /// 获得/设置 sbyte 数据类型步长 默认 null 未设置 /// Gets or sets sbyte type step default null /// - public sbyte? SByte { get; set; } + public string? SByte { get; set; } /// /// 获得/设置 byte 数据类型步长 默认 null 未设置 /// Gets or sets byte type step default null /// - public byte? Byte { get; set; } + public string? Byte { get; set; } /// /// 获得/设置 uint 数据类型步长 默认 null 未设置 /// Gets or sets uint type step default null /// - public uint? UInt { get; set; } + public string? UInt { get; set; } /// /// 获得/设置 int 数据类型步长 默认 null 未设置 /// Gets or sets int type step default null /// - public int? Int { get; set; } + public string? Int { get; set; } /// /// 获得/设置 long 数据类型步长 默认 null 未设置 /// Gets or sets long type step default null /// - public int? Long { get; set; } + public string? Long { get; set; } /// /// 获得/设置 ulong 数据类型步长 默认 null 未设置 /// Gets or sets ulong type step default null /// - public ulong? ULong { get; set; } + public string? ULong { get; set; } /// /// 获得/设置 short 数据类型步长 默认 null 未设置 /// Gets or sets short type step default null /// - public int? Short { get; set; } + public string? Short { get; set; } /// /// 获得/设置 ushort 数据类型步长 默认 null 未设置 /// Gets or sets ushort type step default null /// - public ushort? UShort { get; set; } + public string? UShort { get; set; } /// /// 获得/设置 float 数据类型步长 默认 null 未设置 /// Gets or sets float type step default null /// - public float? Float { get; set; } + public string? Float { get; set; } /// /// 获得/设置 double 数据类型步长 默认 null 未设置 /// Gets or sets double type step default null /// - public double? Double { get; set; } + public string? Double { get; set; } /// /// 获得/设置 decimal 数据类型步长 默认 null 未设置 /// Gets or sets decimal type step default null /// - public decimal? Decimal { get; set; } + public string? Decimal { get; set; } /// /// 获得步长字符串 @@ -89,47 +87,47 @@ public class StepSettings string? ret = null; if (type == typeof(sbyte)) { - ret = SByte?.ToString(CultureInfo.InvariantCulture); + ret = SByte; } else if (type == typeof(byte)) { - ret = Byte?.ToString(CultureInfo.InvariantCulture); + ret = Byte; } else if (type == typeof(uint)) { - ret = UInt?.ToString(CultureInfo.InvariantCulture); + ret = UInt; } else if (type == typeof(int)) { - ret = Int?.ToString(CultureInfo.InvariantCulture); + ret = Int; } else if (type == typeof(long)) { - ret = Long?.ToString(CultureInfo.InvariantCulture); + ret = Long; } else if (type == typeof(short)) { - ret = Short?.ToString(CultureInfo.InvariantCulture); + ret = Short; } else if (type == typeof(ushort)) { - ret = UShort?.ToString(CultureInfo.InvariantCulture); + ret = UShort; } else if (type == typeof(ulong)) { - ret = ULong?.ToString(CultureInfo.InvariantCulture); + ret = ULong; } else if (type == typeof(float)) { - ret = Float?.ToString(CultureInfo.InvariantCulture); + ret = Float; } else if (type == typeof(double)) { - ret = Double?.ToString(CultureInfo.InvariantCulture); + ret = Double; } else if (type == typeof(decimal)) { - ret = Decimal?.ToString(CultureInfo.InvariantCulture); + ret = Decimal; } return ret; } From c836639c19fcc82525bb98fe3dc580b315c6c897 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sat, 6 Jun 2026 18:16:48 +0800 Subject: [PATCH 27/30] =?UTF-8?q?test:=20=E6=9B=B4=E6=96=B0=E5=8D=95?= =?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Options/BootstrapBlazorOptionsTest.cs | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/test/UnitTest/Options/BootstrapBlazorOptionsTest.cs b/test/UnitTest/Options/BootstrapBlazorOptionsTest.cs index 452b755daba..251de41f2d0 100644 --- a/test/UnitTest/Options/BootstrapBlazorOptionsTest.cs +++ b/test/UnitTest/Options/BootstrapBlazorOptionsTest.cs @@ -72,17 +72,17 @@ public void Options_StepSettings() Assert.Null(options.GetStep()); Assert.Null(options.GetStep()); - options.StepSettings.SByte = 1; - options.StepSettings.Byte = 2; - options.StepSettings.Short = 1; - options.StepSettings.UShort = 2; - options.StepSettings.Int = 2; - options.StepSettings.UInt = 3; - options.StepSettings.Long = 3; - options.StepSettings.ULong = 4; - options.StepSettings.Float = 0.1f; - options.StepSettings.Double = 0.01d; - options.StepSettings.Decimal = 0.001M; + options.StepSettings.SByte = "1"; + options.StepSettings.Byte = "2"; + options.StepSettings.Short = "1"; + options.StepSettings.UShort = "2"; + options.StepSettings.Int = "2"; + options.StepSettings.UInt = "3"; + options.StepSettings.Long = "3"; + options.StepSettings.ULong = "4"; + options.StepSettings.Float = "0.1"; + options.StepSettings.Double = "0.01"; + options.StepSettings.Decimal = "0.001"; Assert.Equal("1", options.GetStep()); Assert.Equal("2", options.GetStep()); @@ -96,6 +96,14 @@ public void Options_StepSettings() Assert.Equal("0.1", options.GetStep()); Assert.Equal("0.01", options.GetStep()); Assert.Equal("0.001", options.GetStep()); + + options.StepSettings.Float = "any"; + options.StepSettings.Double = "any"; + options.StepSettings.Decimal = "any"; + + Assert.Equal("any", options.GetStep()); + Assert.Equal("any", options.GetStep()); + Assert.Equal("any", options.GetStep()); } [Fact] From bd7dbdd51ae4dc22fdd528a2bfb877727b787e5c Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sat, 6 Jun 2026 18:23:40 +0800 Subject: [PATCH 28/30] chore: bump version 10.7.1 --- src/BootstrapBlazor/BootstrapBlazor.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BootstrapBlazor/BootstrapBlazor.csproj b/src/BootstrapBlazor/BootstrapBlazor.csproj index 3271a09ae5c..e0a3198dd60 100644 --- a/src/BootstrapBlazor/BootstrapBlazor.csproj +++ b/src/BootstrapBlazor/BootstrapBlazor.csproj @@ -1,7 +1,7 @@  - 10.7.0 + 10.7.1 From ecafac613ed40298aae6c24e86681b3c7fd318ed Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sat, 6 Jun 2026 20:26:39 +0800 Subject: [PATCH 29/30] =?UTF-8?q?doc:=20=E5=A2=9E=E5=8A=A0=20Step=20?= =?UTF-8?q?=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/Samples/InputNumbers.razor | 51 ++++++++++++++----- 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/src/BootstrapBlazor.Server/Components/Samples/InputNumbers.razor b/src/BootstrapBlazor.Server/Components/Samples/InputNumbers.razor index c6f992e8e74..65bd7da3c05 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/InputNumbers.razor +++ b/src/BootstrapBlazor.Server/Components/Samples/InputNumbers.razor @@ -72,19 +72,44 @@ -
-
-

@Localizer["InputNumbersStep1"]

- -
-
-

@Localizer["InputNumbersStep10"]

- -
-
-

@Localizer["InputNumbersStep0.1"]

- -
+
+

html 相关 step 文档请查阅 传送门

+

10.7.1 版本前项目模板文件通过 appsettings.json 配置文件设置了数据默认 step 配置如下:

+
{
+  "BootstrapBlazorOptions": {
+    "StepSettings": {
+      "Float": "0.01",
+      "Double": "0.01",
+      "Decimal": "0.01"
+    }
+  }
+

10.7.1 版本后默认配置做了调整 float double decimal 默认值由 0.01 改为 any,其他数据类型由 1 更改为 null 未设置

+
{
+  "BootstrapBlazorOptions": {
+    "StepSettings": {
+      "Float": "any",
+      "Double": "any",
+      "Decimal": "any"
+    }
+  }
+

也可以通过注入服务 BootstrapBlazorOptions 对象的 StepSettings 来配置默认的 step 值。

+
[Inject]
+[NotNull]
+private IOptions<BootstrapBlazorOptions>? BootstrapBlazorOptions { get; set; }
+

特别说明

+

设置 Step Min Max 值时,当组件值不符合 step 策略时会触发,html 的验证规则,需要自行处理

+
+
+

@Localizer["InputNumbersStep1"]

+ +
+
+

@Localizer["InputNumbersStep10"]

+ +
+
+

@Localizer["InputNumbersStep0.1"]

+
From 8b68938c3dc9e9c339b527bdae16d3538099a76f Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Sat, 6 Jun 2026 20:41:13 +0800 Subject: [PATCH 30/30] =?UTF-8?q?doc:=20=E6=94=AF=E6=8C=81=E5=A4=9A?= =?UTF-8?q?=E8=AF=AD=E8=A8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/Samples/InputNumbers.razor | 16 ++++++++-------- src/BootstrapBlazor.Server/Locales/en-US.json | 8 +++++++- src/BootstrapBlazor.Server/Locales/zh-CN.json | 8 +++++++- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/BootstrapBlazor.Server/Components/Samples/InputNumbers.razor b/src/BootstrapBlazor.Server/Components/Samples/InputNumbers.razor index 65bd7da3c05..d7e0a5be2e1 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/InputNumbers.razor +++ b/src/BootstrapBlazor.Server/Components/Samples/InputNumbers.razor @@ -73,8 +73,8 @@ Introduction="@Localizer["InputNumbersStepIntro"]" Name="Step">
-

html 相关 step 文档请查阅 传送门

-

10.7.1 版本前项目模板文件通过 appsettings.json 配置文件设置了数据默认 step 配置如下:

+

@((MarkupString)Localizer["InputNumbersStepDocLink"].Value)

+

@((MarkupString)Localizer["InputNumbersStepLegacyConfigDescription"].Value)

{
   "BootstrapBlazorOptions": {
     "StepSettings": {
@@ -82,8 +82,8 @@
       "Double": "0.01",
       "Decimal": "0.01"
     }
-  }
-

10.7.1 版本后默认配置做了调整 float double decimal 默认值由 0.01 改为 any,其他数据类型由 1 更改为 null 未设置

+} +

@((MarkupString)Localizer["InputNumbersStepCurrentConfigDescription"].Value)

{
   "BootstrapBlazorOptions": {
     "StepSettings": {
@@ -91,13 +91,13 @@
       "Double": "any",
       "Decimal": "any"
     }
-  }
-

也可以通过注入服务 BootstrapBlazorOptions 对象的 StepSettings 来配置默认的 step 值。

+} +

@((MarkupString)Localizer["InputNumbersStepOptionsDescription"].Value)

[Inject]
 [NotNull]
 private IOptions<BootstrapBlazorOptions>? BootstrapBlazorOptions { get; set; }
-

特别说明

-

设置 Step Min Max 值时,当组件值不符合 step 策略时会触发,html 的验证规则,需要自行处理

+

@Localizer["InputNumbersStepNotesTitle"]

+

@((MarkupString)Localizer["InputNumbersStepNotesDescription"].Value)

@Localizer["InputNumbersStep1"]

diff --git a/src/BootstrapBlazor.Server/Locales/en-US.json b/src/BootstrapBlazor.Server/Locales/en-US.json index a2d81fffe1f..ec58b31f563 100644 --- a/src/BootstrapBlazor.Server/Locales/en-US.json +++ b/src/BootstrapBlazor.Server/Locales/en-US.json @@ -2767,7 +2767,7 @@ "InputNumbersColorDescription2": "No button", "InputNumbersColorIntro": "Set the Color parameter to customize the button color", "InputNumbersColorTitle": "Color", - "InputNumbersDateTypeIntro": "This component uses generic support the underlying data type int short long float double decimal", + "InputNumbersDateTypeIntro": "This component uses generics to support the underlying data types sbyte byte short ushort int uint long ulong float double decimal", "InputNumbersDateTypeTitle": "Data type", "InputNumbersDescription": "Only standard numeric values are allowed, and custom ranges and other advanced features are supported", "InputNumbersDisabledIntro": "When you set the IsDisabled property value to true, the component suppresses input", @@ -2784,7 +2784,13 @@ "InputNumbersStep0.1": "The step defaults to 0.1", "InputNumbersStep1": "The step defaults to 1", "InputNumbersStep10": "The step defaults to 10", + "InputNumbersStepCurrentConfigDescription": "Starting with version 10.7.1, the default configuration was adjusted. The default values for float, double, and decimal changed from 0.01 to any, and all other numeric types changed from 1 to null (unset).", + "InputNumbersStepDocLink": "For the related html step documentation, see the docs", "InputNumbersStepIntro": "Set the Step parameter to control the increase or decrease in steps", + "InputNumbersStepLegacyConfigDescription": "Before version 10.7.1, the project template configured the default step values in appsettings.json like this:", + "InputNumbersStepNotesDescription": "When Step, Min, and Max are set, the browser will apply the native html validation rules if the component value does not match the configured step strategy, and you need to handle that behavior yourself.", + "InputNumbersStepNotesTitle": "Notes", + "InputNumbersStepOptionsDescription": "You can also configure the default step value by injecting BootstrapBlazorOptions and setting its StepSettings.", "InputNumbersStepTitle": "Custom steps", "InputNumbersTitle": "InputNumber", "InputNumbersUseInputEventIntro": "Component uses OnInput event for value updates", diff --git a/src/BootstrapBlazor.Server/Locales/zh-CN.json b/src/BootstrapBlazor.Server/Locales/zh-CN.json index 8495be1a2e2..c47993258b0 100644 --- a/src/BootstrapBlazor.Server/Locales/zh-CN.json +++ b/src/BootstrapBlazor.Server/Locales/zh-CN.json @@ -2767,7 +2767,7 @@ "InputNumbersColorDescription2": "无按钮", "InputNumbersColorIntro": "设置 Color 参数来自定义按钮颜色", "InputNumbersColorTitle": "颜色", - "InputNumbersDateTypeIntro": "本组件采用泛型支持 int short long float double decimal 基础数据类型", + "InputNumbersDateTypeIntro": "本组件采用泛型支持 sbyte byte short ushort int uint long ulong float double decimal 基础数据类型", "InputNumbersDateTypeTitle": "数据类型", "InputNumbersDescription": "仅允许输入标准的数字值,支持自定义范围及其他高级功能", "InputNumbersDisabledIntro": "设置 IsDisabled 属性值为 true 时,组件禁止输入", @@ -2784,7 +2784,13 @@ "InputNumbersStep0.1": "步长设置为 0.1", "InputNumbersStep1": "步长默认为 1", "InputNumbersStep10": "步长设置为 10", + "InputNumbersStepCurrentConfigDescription": "10.7.1 版本后默认配置做了调整,float double decimal 默认值由 0.01 改为 any,其他数据类型由 1 更改为 null 未设置。", + "InputNumbersStepDocLink": "html 相关 step 文档请查阅 传送门", "InputNumbersStepIntro": "设置 Step 参数来控制增加或减少的步长", + "InputNumbersStepLegacyConfigDescription": "10.7.1 版本前项目模板文件通过 appsettings.json 配置文件设置了数据默认 step 配置如下:", + "InputNumbersStepNotesDescription": "设置 Step Min Max 值时,当组件值不符合 step 策略时会触发 html 验证规则,需要自行处理。", + "InputNumbersStepNotesTitle": "特别说明", + "InputNumbersStepOptionsDescription": "也可以通过注入服务 BootstrapBlazorOptions 对象的 StepSettings 来配置默认的 step 值。", "InputNumbersStepTitle": "自定义步长", "InputNumbersTitle": "InputNumber 组件", "InputNumbersUseInputEventIntro": "组件使用 OnInput 事件进行数值更新",