From 1993e73d27d0fa894fbe3a01219f905df9b4203d Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Thu, 6 Aug 2026 14:50:33 +0800 Subject: [PATCH 01/17] feat(BootstrapBlazorOptions): add DateTimeSettings settings --- .../Options/BootstrapBlazorOptions.cs | 6 +++++ .../Options/DateTimeSettings.cs | 22 +++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 src/BootstrapBlazor/Options/DateTimeSettings.cs diff --git a/src/BootstrapBlazor/Options/BootstrapBlazorOptions.cs b/src/BootstrapBlazor/Options/BootstrapBlazorOptions.cs index af600ddf312..fe32dfac25e 100644 --- a/src/BootstrapBlazor/Options/BootstrapBlazorOptions.cs +++ b/src/BootstrapBlazor/Options/BootstrapBlazorOptions.cs @@ -190,6 +190,12 @@ public class BootstrapBlazorOptions : IOptions /// public int ChangeDetectionTaskInterval { get; set; } = 5000; + /// + /// 获得/设置 配置实例 + /// Gets or sets the configuration instance + /// + public DateTimeSettings DateTimeSettings { get; set; } = new(); + BootstrapBlazorOptions IOptions.Value => this; /// diff --git a/src/BootstrapBlazor/Options/DateTimeSettings.cs b/src/BootstrapBlazor/Options/DateTimeSettings.cs new file mode 100644 index 00000000000..6aa363b4b71 --- /dev/null +++ b/src/BootstrapBlazor/Options/DateTimeSettings.cs @@ -0,0 +1,22 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the Apache 2.0 License +// See the LICENSE file in the project root for more information. +// Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone + +namespace BootstrapBlazor.Components; + +/// +/// DateTime 相关全局配置类 +/// +public class DateTimeSettings +{ + /// + /// 自定义解析日期时间的方法 + /// + public Func? ParseDateTimeResolve { get; set; } + + /// + /// 自定义解析日期时间偏移的方法 + /// + public Func? ParseDateTimeOffsetResolve { get; set; } +} From 1079f2b6c9fa689efff444eae0b3d8a721230ea6 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Thu, 6 Aug 2026 15:13:48 +0800 Subject: [PATCH 02/17] feat: add ParseDateTimeResolve parameter --- .../DateTimePicker/DateTimePicker.razor.cs | 50 ++++++++++++++++++- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/src/BootstrapBlazor/Components/DateTimePicker/DateTimePicker.razor.cs b/src/BootstrapBlazor/Components/DateTimePicker/DateTimePicker.razor.cs index 1f7a774fd46..7ed2fe1ff23 100644 --- a/src/BootstrapBlazor/Components/DateTimePicker/DateTimePicker.razor.cs +++ b/src/BootstrapBlazor/Components/DateTimePicker/DateTimePicker.razor.cs @@ -336,6 +336,18 @@ public string? Format [Parameter] public Func? OnBlurAsync { get; set; } + /// + /// 自定义解析日期时间的方法 + /// Custom method to parse date and time + /// + public Func? ParseDateTimeResolve { get; set; } + + /// + /// 自定义解析日期时间偏移的方法 + /// Custom method to parse date and time offset + /// + public Func? ParseDateTimeOffsetResolve { get; set; } + [Inject] [NotNull] private IStringLocalizer>? Localizer { get; set; } @@ -344,6 +356,10 @@ public string? Format [NotNull] private IIconTheme? IconTheme { get; set; } + [Inject] + [NotNull] + private IOptionsMonitor? Options { get; set; } + [NotNull] private string? GenericTypeErrorMessage { get; set; } @@ -570,14 +586,44 @@ protected override bool TryParseValueFromString(string value, [MaybeNullWhen(fal { result = default; validationErrorMessage = null; - var ret = DateTimeHelper.TryToDateTime(value, out var val); + var ret = Value is DateTime ? TryParseDateTime(value, out var val) : TryParseDateTimeOffset(value, out val); if (ret) { - result = (TValue)(object)val; + result = (TValue)val; + } + return ret; + } + + private bool TryParseDateTime(string value, out object val) + { + var op = Options.CurrentValue; + var resolve = ParseDateTimeResolve ?? op.DateTimeSettings.ParseDateTimeResolve; + if (resolve != null) + { + val = resolve(value); + return true; } + + var ret = DateTimeHelper.TryToDateTime(value, out var d); + val = ret ? d : DateTime.MinValue; return ret; } + private bool TryParseDateTimeOffset(string value, out object val) + { + var op = Options.CurrentValue; + var resolve = ParseDateTimeOffsetResolve ?? op.DateTimeSettings.ParseDateTimeOffsetResolve; + if (resolve != null) + { + val = resolve(value); + return true; + } + + var v = DateTimeHelper.ToDateTimeOffset(value); + val = v ?? DateTimeOffset.MinValue; + return v != null; + } + private string? ReadonlyString => IsEditable ? null : "readonly"; /// From dd921df107ed9d5030277d0a314436abb1e19071 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Thu, 6 Aug 2026 15:16:20 +0800 Subject: [PATCH 03/17] =?UTF-8?q?doc:=20=E5=A2=9E=E5=8A=A0=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/BootstrapBlazor/Options/BootstrapBlazorOptions.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/BootstrapBlazor/Options/BootstrapBlazorOptions.cs b/src/BootstrapBlazor/Options/BootstrapBlazorOptions.cs index fe32dfac25e..192d96c0b24 100644 --- a/src/BootstrapBlazor/Options/BootstrapBlazorOptions.cs +++ b/src/BootstrapBlazor/Options/BootstrapBlazorOptions.cs @@ -193,6 +193,7 @@ public class BootstrapBlazorOptions : IOptions /// /// 获得/设置 配置实例 /// Gets or sets the configuration instance + /// v10.9.1 /// public DateTimeSettings DateTimeSettings { get; set; } = new(); From 38ff62c046ccfe07bf49127e2393d6755423e0c8 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Thu, 6 Aug 2026 15:16:31 +0800 Subject: [PATCH 04/17] =?UTF-8?q?doc:=20=E5=A2=9E=E5=8A=A0=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/DateTimePicker/DateTimePicker.razor.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/BootstrapBlazor/Components/DateTimePicker/DateTimePicker.razor.cs b/src/BootstrapBlazor/Components/DateTimePicker/DateTimePicker.razor.cs index 7ed2fe1ff23..bf4d356c95d 100644 --- a/src/BootstrapBlazor/Components/DateTimePicker/DateTimePicker.razor.cs +++ b/src/BootstrapBlazor/Components/DateTimePicker/DateTimePicker.razor.cs @@ -339,12 +339,14 @@ public string? Format /// /// 自定义解析日期时间的方法 /// Custom method to parse date and time + /// v10.9.1 /// public Func? ParseDateTimeResolve { get; set; } /// /// 自定义解析日期时间偏移的方法 /// Custom method to parse date and time offset + /// v10.9.1 /// public Func? ParseDateTimeOffsetResolve { get; set; } From 4c19f6467a9bc18afe2987604e38b49b3c527b04 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Thu, 6 Aug 2026 15:27:35 +0800 Subject: [PATCH 05/17] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=20parameter=20?= =?UTF-8?q?=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/DateTimePicker/DateTimePicker.razor.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/BootstrapBlazor/Components/DateTimePicker/DateTimePicker.razor.cs b/src/BootstrapBlazor/Components/DateTimePicker/DateTimePicker.razor.cs index bf4d356c95d..dced0305a29 100644 --- a/src/BootstrapBlazor/Components/DateTimePicker/DateTimePicker.razor.cs +++ b/src/BootstrapBlazor/Components/DateTimePicker/DateTimePicker.razor.cs @@ -341,6 +341,7 @@ public string? Format /// Custom method to parse date and time /// v10.9.1 /// + [Parameter] public Func? ParseDateTimeResolve { get; set; } /// @@ -348,6 +349,7 @@ public string? Format /// Custom method to parse date and time offset /// v10.9.1 /// + [Parameter] public Func? ParseDateTimeOffsetResolve { get; set; } [Inject] From 72193dcd326557f9f6ed617bad0743acf8785e94 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Thu, 6 Aug 2026 15:27:48 +0800 Subject: [PATCH 06/17] =?UTF-8?q?refactor:=20=E6=9B=B4=E6=AD=A3=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/DateTimePicker/DateTimePicker.razor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/BootstrapBlazor/Components/DateTimePicker/DateTimePicker.razor.cs b/src/BootstrapBlazor/Components/DateTimePicker/DateTimePicker.razor.cs index dced0305a29..f7361db4ab6 100644 --- a/src/BootstrapBlazor/Components/DateTimePicker/DateTimePicker.razor.cs +++ b/src/BootstrapBlazor/Components/DateTimePicker/DateTimePicker.razor.cs @@ -590,7 +590,7 @@ protected override bool TryParseValueFromString(string value, [MaybeNullWhen(fal { result = default; validationErrorMessage = null; - var ret = Value is DateTime ? TryParseDateTime(value, out var val) : TryParseDateTimeOffset(value, out val); + var ret = ValueType == typeof(DateTime) ? TryParseDateTime(value, out var val) : TryParseDateTimeOffset(value, out val); if (ret) { result = (TValue)val; From a12991f4ca42866f1d557be2cc27c27dcdbfce8e Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Thu, 6 Aug 2026 15:27:55 +0800 Subject: [PATCH 07/17] =?UTF-8?q?test:=20=E5=A2=9E=E5=8A=A0=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 --- .../UnitTest/Components/DateTimePickerTest.cs | 147 ++++++++++++++++++ 1 file changed, 147 insertions(+) diff --git a/test/UnitTest/Components/DateTimePickerTest.cs b/test/UnitTest/Components/DateTimePickerTest.cs index eb848ba4541..40cd1bb5b2a 100644 --- a/test/UnitTest/Components/DateTimePickerTest.cs +++ b/test/UnitTest/Components/DateTimePickerTest.cs @@ -1425,6 +1425,153 @@ public async Task OnBlurAsync_Ok() Assert.True(blur); } + [Fact] + public async Task TryParseValueFromString_NullableDateTime_Ok() + { + // Value 为 null 时应按泛型类型走 DateTime 解析分支 + var cut = Context.Render>(pb => + { + pb.Add(a => a.IsEditable, true); + pb.Add(a => a.ViewMode, DatePickerViewMode.Date); + pb.Add(a => a.DateFormat, "MM/dd/yyyy"); + pb.Add(a => a.Value, null); + }); + var input = cut.Find(".datetime-picker-input"); + await cut.InvokeAsync(() => input.Change("02/15/2024")); + Assert.Equal(new DateTime(2024, 2, 15), cut.Instance.Value); + + // 非法数值组件值保持不变 + await cut.InvokeAsync(() => input.Change("test")); + Assert.Equal(new DateTime(2024, 2, 15), cut.Instance.Value); + } + + [Fact] + public async Task TryParseValueFromString_DateTimeOffset_Ok() + { + var cut = Context.Render>(pb => + { + pb.Add(a => a.IsEditable, true); + pb.Add(a => a.ViewMode, DatePickerViewMode.Date); + pb.Add(a => a.DateFormat, "MM/dd/yyyy"); + pb.Add(a => a.Value, DateTimeOffset.MinValue); + }); + var input = cut.Find(".datetime-picker-input"); + + // 无时区信息按本地时区补齐偏移 + await cut.InvokeAsync(() => input.Change("02/15/2024")); + Assert.Equal(new DateTimeOffset(new DateTime(2024, 2, 15, 0, 0, 0, DateTimeKind.Local)), cut.Instance.Value); + + // 非法数值组件值保持不变 + await cut.InvokeAsync(() => input.Change("test")); + Assert.Equal(new DateTimeOffset(new DateTime(2024, 2, 15, 0, 0, 0, DateTimeKind.Local)), cut.Instance.Value); + } + + [Fact] + public async Task TryParseValueFromString_NullableDateTimeOffset_Ok() + { + // Value 为 null 时应按泛型类型走 DateTimeOffset 解析分支 + var cut = Context.Render>(pb => + { + pb.Add(a => a.IsEditable, true); + pb.Add(a => a.ViewMode, DatePickerViewMode.Date); + pb.Add(a => a.DateFormat, "MM/dd/yyyy"); + pb.Add(a => a.Value, null); + }); + var input = cut.Find(".datetime-picker-input"); + await cut.InvokeAsync(() => input.Change("02/15/2024")); + Assert.Equal(new DateTimeOffset(new DateTime(2024, 2, 15, 0, 0, 0, DateTimeKind.Local)), cut.Instance.Value); + } + + [Fact] + public async Task ParseDateTimeResolve_Parameter_Ok() + { + // 自定义解析方法优先级高于内置解析 + var cut = Context.Render>(pb => + { + pb.Add(a => a.IsEditable, true); + pb.Add(a => a.ViewMode, DatePickerViewMode.Date); + pb.Add(a => a.DateFormat, "MM/dd/yyyy"); + pb.Add(a => a.ParseDateTimeResolve, v => new DateTime(2020, 1, 1)); + }); + var input = cut.Find(".datetime-picker-input"); + + // 传入非法字符串同样返回自定义解析结果 + await cut.InvokeAsync(() => input.Change("test")); + Assert.Equal(new DateTime(2020, 1, 1), cut.Instance.Value); + } + + [Fact] + public async Task ParseDateTimeResolve_Options_Ok() + { + // 全局配置自定义解析方法 + var options = Context.Services.GetRequiredService>(); + options.CurrentValue.DateTimeSettings.ParseDateTimeResolve = v => new DateTime(2021, 2, 2); + + var cut = Context.Render>(pb => + { + pb.Add(a => a.IsEditable, true); + pb.Add(a => a.ViewMode, DatePickerViewMode.Date); + pb.Add(a => a.DateFormat, "MM/dd/yyyy"); + }); + var input = cut.Find(".datetime-picker-input"); + await cut.InvokeAsync(() => input.Change("test")); + Assert.Equal(new DateTime(2021, 2, 2), cut.Instance.Value); + + // 组件参数优先级高于全局配置 + cut.Render(pb => + { + pb.Add(a => a.ParseDateTimeResolve, v => new DateTime(2022, 3, 3)); + }); + await cut.InvokeAsync(() => input.Change("test")); + Assert.Equal(new DateTime(2022, 3, 3), cut.Instance.Value); + } + + [Fact] + public async Task ParseDateTimeOffsetResolve_Parameter_Ok() + { + var expected = new DateTimeOffset(new DateTime(2020, 1, 1), TimeSpan.FromHours(8)); + var cut = Context.Render>(pb => + { + pb.Add(a => a.IsEditable, true); + pb.Add(a => a.ViewMode, DatePickerViewMode.Date); + pb.Add(a => a.DateFormat, "MM/dd/yyyy"); + pb.Add(a => a.ParseDateTimeOffsetResolve, v => expected); + }); + var input = cut.Find(".datetime-picker-input"); + + // 传入非法字符串同样返回自定义解析结果 + await cut.InvokeAsync(() => input.Change("test")); + Assert.Equal(expected, cut.Instance.Value); + } + + [Fact] + public async Task ParseDateTimeOffsetResolve_Options_Ok() + { + // 全局配置自定义解析方法 + var expected = new DateTimeOffset(new DateTime(2021, 2, 2), TimeSpan.FromHours(8)); + var options = Context.Services.GetRequiredService>(); + options.CurrentValue.DateTimeSettings.ParseDateTimeOffsetResolve = v => expected; + + var cut = Context.Render>(pb => + { + pb.Add(a => a.IsEditable, true); + pb.Add(a => a.ViewMode, DatePickerViewMode.Date); + pb.Add(a => a.DateFormat, "MM/dd/yyyy"); + }); + var input = cut.Find(".datetime-picker-input"); + await cut.InvokeAsync(() => input.Change("test")); + Assert.Equal(expected, cut.Instance.Value); + + // 组件参数优先级高于全局配置 + var expectedParameter = new DateTimeOffset(new DateTime(2022, 3, 3), TimeSpan.FromHours(8)); + cut.Render(pb => + { + pb.Add(a => a.ParseDateTimeOffsetResolve, v => expectedParameter); + }); + await cut.InvokeAsync(() => input.Change("test")); + Assert.Equal(expectedParameter, cut.Instance.Value); + } + class MockDateTimePicker : DatePickerBody { public static bool GetSafeYearDateTime_Ok() From d28764104d6a36356372d49a0686319174eff663 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Thu, 6 Aug 2026 15:36:37 +0800 Subject: [PATCH 08/17] =?UTF-8?q?test:=20=E6=9B=B4=E6=96=B0=E5=91=BD?= =?UTF-8?q?=E5=90=8D=E7=A9=BA=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/UnitTest/Components/DateTimePickerTest.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/test/UnitTest/Components/DateTimePickerTest.cs b/test/UnitTest/Components/DateTimePickerTest.cs index 40cd1bb5b2a..4fd607b61b5 100644 --- a/test/UnitTest/Components/DateTimePickerTest.cs +++ b/test/UnitTest/Components/DateTimePickerTest.cs @@ -4,6 +4,7 @@ // Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone using AngleSharp.Dom; +using Microsoft.Extensions.Options; namespace UnitTest.Components; From 6403d1fc2325bc3f2f963938259b5acc21bfe5b0 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Thu, 6 Aug 2026 15:36:47 +0800 Subject: [PATCH 09/17] chore: bump version 10.9.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 2b61a006de0..bedc64e8a22 100644 --- a/src/BootstrapBlazor/BootstrapBlazor.csproj +++ b/src/BootstrapBlazor/BootstrapBlazor.csproj @@ -1,7 +1,7 @@  - 10.9.1-beta03 + 10.9.1 From 674b3e0ffde758a6b4ea183b7846f6be3ea99920 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Thu, 6 Aug 2026 17:34:08 +0800 Subject: [PATCH 10/17] =?UTF-8?q?refactor:=20=E7=B2=BE=E7=AE=80=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/BootstrapBlazor/Utils/DateTimeHelper.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/BootstrapBlazor/Utils/DateTimeHelper.cs b/src/BootstrapBlazor/Utils/DateTimeHelper.cs index 1c5bd7e6976..c2748771797 100644 --- a/src/BootstrapBlazor/Utils/DateTimeHelper.cs +++ b/src/BootstrapBlazor/Utils/DateTimeHelper.cs @@ -20,9 +20,7 @@ static class DateTimeHelper "yyyyMMddHHmmssfff", "yyyyMMdd HHmmss", "yyyyMMdd HH:mm:ss", - "yyyyMMdd HH:mm", - "yyyy-M-d", - "yyyy/M/d" + "yyyyMMdd HH:mm" ]; /// From 3c7cc8c72ac00b135d089f623b8c8bf8ec7bc494 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Thu, 6 Aug 2026 17:34:35 +0800 Subject: [PATCH 11/17] =?UTF-8?q?doc:=20=E6=9B=B4=E6=96=B0=E7=A4=BA?= =?UTF-8?q?=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/Samples/DateTimePickers.razor | 5 ++++- .../Components/Samples/DateTimePickers.razor.cs | 4 ++++ src/BootstrapBlazor.Server/Locales/en-US.json | 3 ++- src/BootstrapBlazor.Server/Locales/zh-CN.json | 3 ++- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/BootstrapBlazor.Server/Components/Samples/DateTimePickers.razor b/src/BootstrapBlazor.Server/Components/Samples/DateTimePickers.razor index b0cc8be8d60..ac8cd099b1e 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/DateTimePickers.razor +++ b/src/BootstrapBlazor.Server/Components/Samples/DateTimePickers.razor @@ -103,9 +103,12 @@ +
+ @((MarkupString)Localizer["IsEditableTip"].Value) +
- +
diff --git a/src/BootstrapBlazor.Server/Components/Samples/DateTimePickers.razor.cs b/src/BootstrapBlazor.Server/Components/Samples/DateTimePickers.razor.cs index c28cd05715c..d7b42dc91d2 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/DateTimePickers.razor.cs +++ b/src/BootstrapBlazor.Server/Components/Samples/DateTimePickers.razor.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.Server.Components.Samples; /// @@ -73,6 +75,8 @@ private static string FormatterSpanString(TimeSpan ts) private DateTime? BindValue { get; set; } = DateTime.Today; + private static DateTime ParseDateTime(string value) => DateTime.ParseExact(value, "yyyyMMdd", System.Globalization.CultureInfo.InvariantCulture); + private string BindValueString { get => BindValue.HasValue ? BindValue.Value.ToString("yyyy-MM-dd") : ""; diff --git a/src/BootstrapBlazor.Server/Locales/en-US.json b/src/BootstrapBlazor.Server/Locales/en-US.json index a6b7a666ad4..33923b308a9 100644 --- a/src/BootstrapBlazor.Server/Locales/en-US.json +++ b/src/BootstrapBlazor.Server/Locales/en-US.json @@ -1679,7 +1679,8 @@ "FeatureShowLunarIntro": "ShowLunar Whether to display the lunar calendar", "FeatureShowSolarTerm": "Solar Term", "FeatureShowSolarTermIntro": "ShowSolarTerm Whether to display the 24 solar terms", - "IsEditableIntro": "Enable manual input function by setting IsEditable=\"true\"", + "IsEditableIntro": "Enable manual date input by setting IsEditable=\"true\", and customize how the input text is converted to a date with ParseDateTimeResolve", + "IsEditableTip": "This example sets ParseDateTimeResolve to convert text in yyyyMMdd format to a date. Try entering 20250808. When binding to DateTimeOffset or DateTimeOffset?, use ParseDateTimeOffsetResolve to customize the conversion logic", "IsEditableTitle": "Editable", "MinValueIntro": "Set the MinValue property value to the MaxValue limit the range of optional values, in this case setting the range to 45days", "MinValueTitle": "Set the range of values", diff --git a/src/BootstrapBlazor.Server/Locales/zh-CN.json b/src/BootstrapBlazor.Server/Locales/zh-CN.json index 3182df86e80..1f1c00d2c7a 100644 --- a/src/BootstrapBlazor.Server/Locales/zh-CN.json +++ b/src/BootstrapBlazor.Server/Locales/zh-CN.json @@ -1679,7 +1679,8 @@ "FeatureShowLunarIntro": "ShowLunar 是否显示农历", "FeatureShowSolarTerm": "24 节气", "FeatureShowSolarTermIntro": "ShowSolarTerm 是否显示 24 节气", - "IsEditableIntro": "通过设置 IsEditable=\"true\" 开启手工录入日期功能", + "IsEditableIntro": "通过设置 IsEditable=\"true\" 开启手工录入日期功能,通过 ParseDateTimeResolve 自定义录入文本的日期格式转换逻辑", + "IsEditableTip": "本例设置 ParseDateTimeResolveyyyyMMdd 格式的文本转换为日期,请尝试输入 20250808。绑定值为 DateTimeOffsetDateTimeOffset? 时,请使用 ParseDateTimeOffsetResolve 参数自定义转换逻辑", "IsEditableTitle": "手工录入", "MinValueIntro": "设置 MinValue 属性值与 MaxValue 限制可选值范围,本例中设置范围为 45 天", "MinValueTitle": "设置值范围", From f2149eca476c7f54e0f9d1d27cc7102476fc1469 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Thu, 6 Aug 2026 17:56:06 +0800 Subject: [PATCH 12/17] =?UTF-8?q?refactor:=20=E6=9B=B4=E6=94=B9=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E5=90=8D=E7=A7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DateTimePicker/DateTimePicker.razor.cs | 8 +-- .../Options/DateTimeSettings.cs | 4 +- src/BootstrapBlazor/Utils/DateTimeHelper.cs | 52 +++++++++++++------ .../UnitTest/Components/DateTimePickerTest.cs | 12 ++--- 4 files changed, 49 insertions(+), 27 deletions(-) diff --git a/src/BootstrapBlazor/Components/DateTimePicker/DateTimePicker.razor.cs b/src/BootstrapBlazor/Components/DateTimePicker/DateTimePicker.razor.cs index f7361db4ab6..099483c4ea6 100644 --- a/src/BootstrapBlazor/Components/DateTimePicker/DateTimePicker.razor.cs +++ b/src/BootstrapBlazor/Components/DateTimePicker/DateTimePicker.razor.cs @@ -342,7 +342,7 @@ public string? Format /// v10.9.1 /// [Parameter] - public Func? ParseDateTimeResolve { get; set; } + public Func? ParseDateTimeCallback { get; set; } /// /// 自定义解析日期时间偏移的方法 @@ -350,7 +350,7 @@ public string? Format /// v10.9.1 /// [Parameter] - public Func? ParseDateTimeOffsetResolve { get; set; } + public Func? ParseDateTimeOffsetCallback { get; set; } [Inject] [NotNull] @@ -601,7 +601,7 @@ protected override bool TryParseValueFromString(string value, [MaybeNullWhen(fal private bool TryParseDateTime(string value, out object val) { var op = Options.CurrentValue; - var resolve = ParseDateTimeResolve ?? op.DateTimeSettings.ParseDateTimeResolve; + var resolve = ParseDateTimeCallback ?? op.DateTimeSettings.ParseDateTimeCallback; if (resolve != null) { val = resolve(value); @@ -616,7 +616,7 @@ private bool TryParseDateTime(string value, out object val) private bool TryParseDateTimeOffset(string value, out object val) { var op = Options.CurrentValue; - var resolve = ParseDateTimeOffsetResolve ?? op.DateTimeSettings.ParseDateTimeOffsetResolve; + var resolve = ParseDateTimeOffsetCallback ?? op.DateTimeSettings.ParseDateTimeOffsetCallback; if (resolve != null) { val = resolve(value); diff --git a/src/BootstrapBlazor/Options/DateTimeSettings.cs b/src/BootstrapBlazor/Options/DateTimeSettings.cs index 6aa363b4b71..85bfe2f7219 100644 --- a/src/BootstrapBlazor/Options/DateTimeSettings.cs +++ b/src/BootstrapBlazor/Options/DateTimeSettings.cs @@ -13,10 +13,10 @@ public class DateTimeSettings /// /// 自定义解析日期时间的方法 /// - public Func? ParseDateTimeResolve { get; set; } + public Func? ParseDateTimeCallback { get; set; } /// /// 自定义解析日期时间偏移的方法 /// - public Func? ParseDateTimeOffsetResolve { get; set; } + public Func? ParseDateTimeOffsetCallback { get; set; } } diff --git a/src/BootstrapBlazor/Utils/DateTimeHelper.cs b/src/BootstrapBlazor/Utils/DateTimeHelper.cs index c2748771797..21884b6e2aa 100644 --- a/src/BootstrapBlazor/Utils/DateTimeHelper.cs +++ b/src/BootstrapBlazor/Utils/DateTimeHelper.cs @@ -7,7 +7,11 @@ namespace BootstrapBlazor.Components; -static class DateTimeHelper +/// +/// 日期时间相关帮助类 +/// DateTime related helper class +/// +public static class DateTimeHelper { /// /// 无分隔符等标准解析无法识别的紧凑格式,需要显式列出 @@ -24,24 +28,43 @@ static class DateTimeHelper ]; /// - /// 将字符串解析为 ,无法解析时返回 + /// 将字符串解析为 ,无法解析时返回 + /// Parse the string to , return if parsing fails /// + /// + /// 要解析的字符串 + /// The string to parse + /// public static DateTime? ToDateTime(string value) => TryToDateTime(value, out var result) ? result : null; /// - /// 将字符串解析为 ,无法解析时返回 + /// 将字符串解析为 ,无法解析时返回 + /// Parse the string to , return if parsing fails /// - /// 要解析的字符串 - /// 解析失败时返回的默认值 + /// + /// 要解析的字符串 + /// The string to parse + /// + /// + /// 解析失败时返回的默认值 + /// The default value to return if parsing fails + /// public static DateTime ToDateTime(string value, DateTime defaultValue) => TryToDateTime(value, out var result) ? result : defaultValue; /// - /// 尝试将字符串解析为 + /// 尝试将字符串解析为 + /// Try to parse the string to /// - /// 要解析的字符串 - /// 解析成功时的结果,失败时为 + /// + /// 要解析的字符串 + /// The string to parse + /// + /// + /// 解析成功时的结果,失败时为 + /// The result when parsing succeeds, otherwise + /// public static bool TryToDateTime(string value, out DateTime result) { result = DateTime.MinValue; @@ -63,14 +86,13 @@ public static bool TryToDateTime(string value, out DateTime result) } /// - /// 将字符串解析为 ,无法解析时返回 + /// 将字符串解析为 ,无法解析时返回 + /// Parse the string to , return if parsing fails /// - /// - /// 适用于 DateTimePicker 绑定 的场景; - /// 不含时区信息时按本地时区处理。 - /// - /// 要解析的字符串 - /// 解析成功返回对应的 ,否则返回 + /// + /// 要解析的字符串 + /// The string to parse + /// public static DateTimeOffset? ToDateTimeOffset(string value) { if (TryToDateTime(value, out var dateTime)) diff --git a/test/UnitTest/Components/DateTimePickerTest.cs b/test/UnitTest/Components/DateTimePickerTest.cs index 4fd607b61b5..e8dbab390f6 100644 --- a/test/UnitTest/Components/DateTimePickerTest.cs +++ b/test/UnitTest/Components/DateTimePickerTest.cs @@ -1492,7 +1492,7 @@ public async Task ParseDateTimeResolve_Parameter_Ok() pb.Add(a => a.IsEditable, true); pb.Add(a => a.ViewMode, DatePickerViewMode.Date); pb.Add(a => a.DateFormat, "MM/dd/yyyy"); - pb.Add(a => a.ParseDateTimeResolve, v => new DateTime(2020, 1, 1)); + pb.Add(a => a.ParseDateTimeCallback, v => new DateTime(2020, 1, 1)); }); var input = cut.Find(".datetime-picker-input"); @@ -1506,7 +1506,7 @@ public async Task ParseDateTimeResolve_Options_Ok() { // 全局配置自定义解析方法 var options = Context.Services.GetRequiredService>(); - options.CurrentValue.DateTimeSettings.ParseDateTimeResolve = v => new DateTime(2021, 2, 2); + options.CurrentValue.DateTimeSettings.ParseDateTimeCallback = v => new DateTime(2021, 2, 2); var cut = Context.Render>(pb => { @@ -1521,7 +1521,7 @@ public async Task ParseDateTimeResolve_Options_Ok() // 组件参数优先级高于全局配置 cut.Render(pb => { - pb.Add(a => a.ParseDateTimeResolve, v => new DateTime(2022, 3, 3)); + pb.Add(a => a.ParseDateTimeCallback, v => new DateTime(2022, 3, 3)); }); await cut.InvokeAsync(() => input.Change("test")); Assert.Equal(new DateTime(2022, 3, 3), cut.Instance.Value); @@ -1536,7 +1536,7 @@ public async Task ParseDateTimeOffsetResolve_Parameter_Ok() pb.Add(a => a.IsEditable, true); pb.Add(a => a.ViewMode, DatePickerViewMode.Date); pb.Add(a => a.DateFormat, "MM/dd/yyyy"); - pb.Add(a => a.ParseDateTimeOffsetResolve, v => expected); + pb.Add(a => a.ParseDateTimeOffsetCallback, v => expected); }); var input = cut.Find(".datetime-picker-input"); @@ -1551,7 +1551,7 @@ public async Task ParseDateTimeOffsetResolve_Options_Ok() // 全局配置自定义解析方法 var expected = new DateTimeOffset(new DateTime(2021, 2, 2), TimeSpan.FromHours(8)); var options = Context.Services.GetRequiredService>(); - options.CurrentValue.DateTimeSettings.ParseDateTimeOffsetResolve = v => expected; + options.CurrentValue.DateTimeSettings.ParseDateTimeOffsetCallback = v => expected; var cut = Context.Render>(pb => { @@ -1567,7 +1567,7 @@ public async Task ParseDateTimeOffsetResolve_Options_Ok() var expectedParameter = new DateTimeOffset(new DateTime(2022, 3, 3), TimeSpan.FromHours(8)); cut.Render(pb => { - pb.Add(a => a.ParseDateTimeOffsetResolve, v => expectedParameter); + pb.Add(a => a.ParseDateTimeOffsetCallback, v => expectedParameter); }); await cut.InvokeAsync(() => input.Change("test")); Assert.Equal(expectedParameter, cut.Instance.Value); From 99b6e6d2ccc266920cdd726ee0676dd1c203f803 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Thu, 6 Aug 2026 17:56:18 +0800 Subject: [PATCH 13/17] =?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/Utils/DateTimeHelperTest.cs | 52 +++++++---------------- 1 file changed, 16 insertions(+), 36 deletions(-) diff --git a/test/UnitTest/Utils/DateTimeHelperTest.cs b/test/UnitTest/Utils/DateTimeHelperTest.cs index a1db225c7c4..2a00e7ff94e 100644 --- a/test/UnitTest/Utils/DateTimeHelperTest.cs +++ b/test/UnitTest/Utils/DateTimeHelperTest.cs @@ -3,37 +3,29 @@ // See the LICENSE file in the project root for more information. // Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone -using System.Runtime.CompilerServices; - namespace UnitTest.Utils; /// -/// DateTimeHelper 为内部类,通过 配合 -/// 直接访问其静态方法进行测试 +/// 测试类 /// public class DateTimeHelperTest { - /// - /// 内部类型的程序集限定名 - /// - private const string HelperTypeName = "BootstrapBlazor.Components.DateTimeHelper, BootstrapBlazor"; - [Fact] public void ToDateTime_Ok() { // 紧凑格式解析成功 - Assert.Equal(new DateTime(2026, 5, 1), ToDateTime(null, "20260501")); + Assert.Equal(new DateTime(2026, 5, 1), DateTimeHelper.ToDateTime("20260501")); // 带分隔符格式解析成功 - Assert.Equal(new DateTime(2026, 5, 1, 13, 4, 5), ToDateTime(null, "2026-05-01 13:04:05")); + Assert.Equal(new DateTime(2026, 5, 1, 13, 4, 5), DateTimeHelper.ToDateTime("2026-05-01 13:04:05")); } [Fact] public void ToDateTime_Null() { // 解析失败返回 null - Assert.Null(ToDateTime(null, "test")); - Assert.Null(ToDateTime(null, null!)); + Assert.Null(DateTimeHelper.ToDateTime("test")); + Assert.Null(DateTimeHelper.ToDateTime(null!)); } [Fact] @@ -42,10 +34,10 @@ public void ToDateTime_DefaultValue_Ok() var defaultValue = new DateTime(2020, 1, 1); // 解析成功返回解析值 - Assert.Equal(new DateTime(2026, 5, 1), ToDateTimeWithDefaultValue(null, "20260501", defaultValue)); + Assert.Equal(new DateTime(2026, 5, 1), DateTimeHelper.ToDateTime("20260501", defaultValue)); // 解析失败返回默认值 - Assert.Equal(defaultValue, ToDateTimeWithDefaultValue(null, "test", defaultValue)); + Assert.Equal(defaultValue, DateTimeHelper.ToDateTime("test", defaultValue)); } [Theory] @@ -56,7 +48,7 @@ public void ToDateTime_DefaultValue_Ok() public void TryToDateTime_NullOrWhiteSpace(string? value) { // 空字符串直接返回 false 且结果为 DateTime.MinValue - Assert.False(TryToDateTime(null, value!, out var result)); + Assert.False(DateTimeHelper.TryToDateTime(value!, out var result)); Assert.Equal(DateTime.MinValue, result); } @@ -64,10 +56,10 @@ public void TryToDateTime_NullOrWhiteSpace(string? value) public void TryToDateTime_Invalid() { // 两种解析方式均失败 - Assert.False(TryToDateTime(null, "test", out var result)); + Assert.False(DateTimeHelper.TryToDateTime("test", out var result)); Assert.Equal(DateTime.MinValue, result); - Assert.False(TryToDateTime(null, "20261301", out _)); + Assert.False(DateTimeHelper.TryToDateTime("20261301", out _)); } [Theory] @@ -83,7 +75,7 @@ public void TryToDateTime_Invalid() public void TryToDateTime_CompactFormats(string value, int year, int month, int day, int hour, int minute, int second, int millisecond) { // 紧凑格式由 TryParseExact 分支解析 - Assert.True(TryToDateTime(null, value, out var result)); + Assert.True(DateTimeHelper.TryToDateTime(value, out var result)); Assert.Equal(new DateTime(year, month, day, hour, minute, second, millisecond), result); } @@ -93,7 +85,7 @@ public void TryToDateTime_CompactFormats(string value, int year, int month, int public void TryToDateTime_Fallback(string value) { // 标准格式由 TryParse 回退分支解析 - Assert.True(TryToDateTime(null, value, out var result)); + Assert.True(DateTimeHelper.TryToDateTime(value, out var result)); Assert.Equal(new DateTime(2026, 5, 1), result); } @@ -101,7 +93,7 @@ public void TryToDateTime_Fallback(string value) public void TryToDateTime_Trim() { // 前后空白被裁剪后可正常解析 - Assert.True(TryToDateTime(null, " 20260501 ", out var result)); + Assert.True(DateTimeHelper.TryToDateTime(" 20260501 ", out var result)); Assert.Equal(new DateTime(2026, 5, 1), result); } @@ -112,7 +104,7 @@ public void ToDateTimeOffset_Unspecified() var value = new DateTime(2026, 5, 1, 13, 4, 5); var expected = TimeZoneInfo.Local.GetUtcOffset(value); - var actual = ToDateTimeOffset(null, "20260501 130405"); + var actual = DateTimeHelper.ToDateTimeOffset("20260501 130405"); Assert.NotNull(actual); Assert.Equal(expected, actual.Value.Offset); Assert.Equal(value, actual.Value.DateTime); @@ -122,7 +114,7 @@ public void ToDateTimeOffset_Unspecified() public void ToDateTimeOffset_Local() { // 带时区信息的字符串解析出的 Kind 不是 Unspecified,直接构造 - var actual = ToDateTimeOffset(null, "2026-05-01T13:04:05Z"); + var actual = DateTimeHelper.ToDateTimeOffset("2026-05-01T13:04:05Z"); Assert.NotNull(actual); Assert.Equal(new DateTime(2026, 5, 1, 13, 4, 5, DateTimeKind.Utc), actual.Value.UtcDateTime); } @@ -131,18 +123,6 @@ public void ToDateTimeOffset_Local() public void ToDateTimeOffset_Null() { // 解析失败返回 null - Assert.Null(ToDateTimeOffset(null, "test")); + Assert.Null(DateTimeHelper.ToDateTimeOffset("test")); } - - [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "ToDateTime")] - static extern DateTime? ToDateTime([UnsafeAccessorType(HelperTypeName)] object? @this, string value); - - [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "ToDateTime")] - static extern DateTime ToDateTimeWithDefaultValue([UnsafeAccessorType(HelperTypeName)] object? @this, string value, DateTime defaultValue); - - [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "TryToDateTime")] - static extern bool TryToDateTime([UnsafeAccessorType(HelperTypeName)] object? @this, string value, out DateTime result); - - [UnsafeAccessor(UnsafeAccessorKind.StaticMethod, Name = "ToDateTimeOffset")] - static extern DateTimeOffset? ToDateTimeOffset([UnsafeAccessorType(HelperTypeName)] object? @this, string value); } From 374862446a1a32d50ecea149e68391f675600db4 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Thu, 6 Aug 2026 18:10:35 +0800 Subject: [PATCH 14/17] =?UTF-8?q?refactor:=20=E6=9B=B4=E6=96=B0=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E5=90=8D=E7=A7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/Samples/DateTimePickers.razor | 2 +- src/BootstrapBlazor.Server/Locales/en-US.json | 4 ++-- src/BootstrapBlazor.Server/Locales/zh-CN.json | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/BootstrapBlazor.Server/Components/Samples/DateTimePickers.razor b/src/BootstrapBlazor.Server/Components/Samples/DateTimePickers.razor index ac8cd099b1e..e4651eee4d7 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/DateTimePickers.razor +++ b/src/BootstrapBlazor.Server/Components/Samples/DateTimePickers.razor @@ -108,7 +108,7 @@
- +
diff --git a/src/BootstrapBlazor.Server/Locales/en-US.json b/src/BootstrapBlazor.Server/Locales/en-US.json index 33923b308a9..2308bb02310 100644 --- a/src/BootstrapBlazor.Server/Locales/en-US.json +++ b/src/BootstrapBlazor.Server/Locales/en-US.json @@ -1679,8 +1679,8 @@ "FeatureShowLunarIntro": "ShowLunar Whether to display the lunar calendar", "FeatureShowSolarTerm": "Solar Term", "FeatureShowSolarTermIntro": "ShowSolarTerm Whether to display the 24 solar terms", - "IsEditableIntro": "Enable manual date input by setting IsEditable=\"true\", and customize how the input text is converted to a date with ParseDateTimeResolve", - "IsEditableTip": "This example sets ParseDateTimeResolve to convert text in yyyyMMdd format to a date. Try entering 20250808. When binding to DateTimeOffset or DateTimeOffset?, use ParseDateTimeOffsetResolve to customize the conversion logic", + "IsEditableIntro": "Enable manual date input by setting IsEditable=\"true\", and customize how the input text is converted to a date with ParseDateTimeCallback", + "IsEditableTip": "This example sets ParseDateTimeCallback to convert text in yyyyMMdd format to a date. Try entering 20250808. When binding to DateTimeOffset or DateTimeOffset?, use ParseDateTimeOffsetCallback to customize the conversion logic", "IsEditableTitle": "Editable", "MinValueIntro": "Set the MinValue property value to the MaxValue limit the range of optional values, in this case setting the range to 45days", "MinValueTitle": "Set the range of values", diff --git a/src/BootstrapBlazor.Server/Locales/zh-CN.json b/src/BootstrapBlazor.Server/Locales/zh-CN.json index 1f1c00d2c7a..6ad6b993b69 100644 --- a/src/BootstrapBlazor.Server/Locales/zh-CN.json +++ b/src/BootstrapBlazor.Server/Locales/zh-CN.json @@ -1679,8 +1679,8 @@ "FeatureShowLunarIntro": "ShowLunar 是否显示农历", "FeatureShowSolarTerm": "24 节气", "FeatureShowSolarTermIntro": "ShowSolarTerm 是否显示 24 节气", - "IsEditableIntro": "通过设置 IsEditable=\"true\" 开启手工录入日期功能,通过 ParseDateTimeResolve 自定义录入文本的日期格式转换逻辑", - "IsEditableTip": "本例设置 ParseDateTimeResolveyyyyMMdd 格式的文本转换为日期,请尝试输入 20250808。绑定值为 DateTimeOffsetDateTimeOffset? 时,请使用 ParseDateTimeOffsetResolve 参数自定义转换逻辑", + "IsEditableIntro": "通过设置 IsEditable=\"true\" 开启手工录入日期功能,通过 ParseDateTimeCallback 自定义录入文本的日期格式转换逻辑", + "IsEditableTip": "本例设置 ParseDateTimeCallbackyyyyMMdd 格式的文本转换为日期,请尝试输入 20250808。绑定值为 DateTimeOffsetDateTimeOffset? 时,请使用 ParseDateTimeOffsetCallback 参数自定义转换逻辑", "IsEditableTitle": "手工录入", "MinValueIntro": "设置 MinValue 属性值与 MaxValue 限制可选值范围,本例中设置范围为 45 天", "MinValueTitle": "设置值范围", From 28c58d3f9fd7aa706da6ce8e2b6433b3cb411f2d Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Thu, 6 Aug 2026 20:01:46 +0800 Subject: [PATCH 15/17] =?UTF-8?q?doc:=20=E6=9B=B4=E6=96=B0=20ParseDateTime?= =?UTF-8?q?Callback=20=E5=9B=9E=E8=B0=83=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/Samples/DateTimePickers.razor | 1 + src/BootstrapBlazor.Server/Locales/en-US.json | 1 + src/BootstrapBlazor.Server/Locales/zh-CN.json | 1 + 3 files changed, 3 insertions(+) diff --git a/src/BootstrapBlazor.Server/Components/Samples/DateTimePickers.razor b/src/BootstrapBlazor.Server/Components/Samples/DateTimePickers.razor index e4651eee4d7..fe9c06c237d 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/DateTimePickers.razor +++ b/src/BootstrapBlazor.Server/Components/Samples/DateTimePickers.razor @@ -105,6 +105,7 @@
@((MarkupString)Localizer["IsEditableTip"].Value) +

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

diff --git a/src/BootstrapBlazor.Server/Locales/en-US.json b/src/BootstrapBlazor.Server/Locales/en-US.json index 2308bb02310..c6e1fdfcffb 100644 --- a/src/BootstrapBlazor.Server/Locales/en-US.json +++ b/src/BootstrapBlazor.Server/Locales/en-US.json @@ -1679,6 +1679,7 @@ "FeatureShowLunarIntro": "ShowLunar Whether to display the lunar calendar", "FeatureShowSolarTerm": "Solar Term", "FeatureShowSolarTermIntro": "ShowSolarTerm Whether to display the 24 solar terms", + "IsEditableCallbackTip": "To automatically convert entered text to DateTime/DateTimeOffset, use the component-level ParseDateTimeCallback and ParseDateTimeOffsetCallback parameters to customize the conversion logic. Corresponding options are also available through the DateTimeSettings property of the global BootstrapBlazorOptions configuration. The component callback takes precedence, followed by the global callback. If neither is configured, the default DateTimeHelper.ToDateTime conversion logic is used.", "IsEditableIntro": "Enable manual date input by setting IsEditable=\"true\", and customize how the input text is converted to a date with ParseDateTimeCallback", "IsEditableTip": "This example sets ParseDateTimeCallback to convert text in yyyyMMdd format to a date. Try entering 20250808. When binding to DateTimeOffset or DateTimeOffset?, use ParseDateTimeOffsetCallback to customize the conversion logic", "IsEditableTitle": "Editable", diff --git a/src/BootstrapBlazor.Server/Locales/zh-CN.json b/src/BootstrapBlazor.Server/Locales/zh-CN.json index 6ad6b993b69..b85498c0dd6 100644 --- a/src/BootstrapBlazor.Server/Locales/zh-CN.json +++ b/src/BootstrapBlazor.Server/Locales/zh-CN.json @@ -1679,6 +1679,7 @@ "FeatureShowLunarIntro": "ShowLunar 是否显示农历", "FeatureShowSolarTerm": "24 节气", "FeatureShowSolarTermIntro": "ShowSolarTerm 是否显示 24 节气", + "IsEditableCallbackTip": "为了方便将录入的字符串自动转换为 DateTime/DateTimeOffset 数据类型,除了组件提供的 ParseDateTimeCallbackParseDateTimeOffsetCallback 参数可用于自定义转换逻辑外,全局配置 BootstrapBlazorOptionsDateTimeSettings 属性也提供了对应的配置项。组件会优先使用自身的回调函数,其次使用全局配置的回调函数;如果均未设置,则使用默认的 DateTimeHelper.ToDateTime 转换逻辑。", "IsEditableIntro": "通过设置 IsEditable=\"true\" 开启手工录入日期功能,通过 ParseDateTimeCallback 自定义录入文本的日期格式转换逻辑", "IsEditableTip": "本例设置 ParseDateTimeCallbackyyyyMMdd 格式的文本转换为日期,请尝试输入 20250808。绑定值为 DateTimeOffsetDateTimeOffset? 时,请使用 ParseDateTimeOffsetCallback 参数自定义转换逻辑", "IsEditableTitle": "手工录入", From a4f7ae171a54af2a00fa23952bdc43778b9268b4 Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Thu, 6 Aug 2026 20:02:01 +0800 Subject: [PATCH 16/17] =?UTF-8?q?doc:=20=E5=A2=9E=E5=8A=A0=E5=85=A8?= =?UTF-8?q?=E5=B1=80=E9=85=8D=E7=BD=AE=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Components/Pages/GlobalOption.razor | 17 ++++++++++++++++- src/BootstrapBlazor.Server/Locales/en-US.json | 3 +++ src/BootstrapBlazor.Server/Locales/zh-CN.json | 3 +++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/BootstrapBlazor.Server/Components/Pages/GlobalOption.razor b/src/BootstrapBlazor.Server/Components/Pages/GlobalOption.razor index b8eedf08a9d..8d18fcab022 100644 --- a/src/BootstrapBlazor.Server/Components/Pages/GlobalOption.razor +++ b/src/BootstrapBlazor.Server/Components/Pages/GlobalOption.razor @@ -1,4 +1,4 @@ -@page "/global-option" +@page "/global-option" @inject IStringLocalizer Localizer

@Localizer["Title"]

@@ -65,6 +65,21 @@
  • TableExportOptions.ArrayDelimiter 数组类型合并操作时使用的分隔符
  • +

    DateTimeSettings 日期时间全局配置

    + +

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

    + +
      +
    • @((MarkupString)Localizer["DateTimeSettingsParseDateTimeCallback"].Value)
    • +
    • @((MarkupString)Localizer["DateTimeSettingsParseDateTimeOffsetCallback"].Value)
    • +
    + +
    builder.Services.AddBootstrapBlazor(options =>
    +{
    +    options.DateTimeSettings.ParseDateTimeCallback = value => DateTime.ParseExact(value, "yyyyMMdd", CultureInfo.InvariantCulture);
    +    options.DateTimeSettings.ParseDateTimeOffsetCallback = value => DateTimeOffset.ParseExact(value, "yyyyMMdd zzz", CultureInfo.InvariantCulture);
    +});
    +

    StepSettings 步长全局统一配置各种数据类型的步长值

    ConnectionHubOptions 步长全局统一配置各种数据类型的步长值

    diff --git a/src/BootstrapBlazor.Server/Locales/en-US.json b/src/BootstrapBlazor.Server/Locales/en-US.json index c6e1fdfcffb..e898ec568c0 100644 --- a/src/BootstrapBlazor.Server/Locales/en-US.json +++ b/src/BootstrapBlazor.Server/Locales/en-US.json @@ -525,6 +525,9 @@ "Title": "Globalization" }, "BootstrapBlazor.Server.Components.Pages.GlobalOption": { + "DateTimeSettingsIntro": "Use this configuration to customize the text conversion logic of the DateTimePicker component when IsEditable=\"true\". The global configuration is used when no component-level callback is set; a component-level callback takes precedence over the global configuration.", + "DateTimeSettingsParseDateTimeCallback": "ParseDateTimeCallback Custom callback for parsing DateTime", + "DateTimeSettingsParseDateTimeOffsetCallback": "ParseDateTimeOffsetCallback Custom callback for parsing DateTimeOffset", "SubTitle": "Added component ErrorLogger Through this component, global logs and exceptions can be output uniformly; currently, the Blazor framework does not provide a MVC like Global exception The overall solution", "Title": "Global exception" }, diff --git a/src/BootstrapBlazor.Server/Locales/zh-CN.json b/src/BootstrapBlazor.Server/Locales/zh-CN.json index b85498c0dd6..29c6fa52b8e 100644 --- a/src/BootstrapBlazor.Server/Locales/zh-CN.json +++ b/src/BootstrapBlazor.Server/Locales/zh-CN.json @@ -525,6 +525,9 @@ "Title": "全球化" }, "BootstrapBlazor.Server.Components.Pages.GlobalOption": { + "DateTimeSettingsIntro": "通过此配置统一设置 DateTimePicker 组件在 IsEditable=\"true\" 时的文本转换逻辑。组件未单独设置回调时,使用此处的全局配置;组件回调的优先级高于全局配置。", + "DateTimeSettingsParseDateTimeCallback": "ParseDateTimeCallback 自定义解析 DateTime 的回调方法", + "DateTimeSettingsParseDateTimeOffsetCallback": "ParseDateTimeOffsetCallback 自定义解析 DateTimeOffset 的回调方法", "SubTitle": "组件库提供一种对当前应用程序中所有组件进行配置的方法,通过 BootstrapBlazorOptions 全局配置类实现此功能", "Title": "全局配置" }, From 6504f0b2c9e11d6491e0c6a0380eb4918d151e9e Mon Sep 17 00:00:00 2001 From: Argo Zhang Date: Thu, 6 Aug 2026 20:27:13 +0800 Subject: [PATCH 17/17] =?UTF-8?q?doc:=20=E8=B5=84=E6=BA=90=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E6=8E=92=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/BootstrapBlazor.Server/Locales/en-US.json | 112 +++++++++--------- src/BootstrapBlazor.Server/Locales/zh-CN.json | 112 +++++++++--------- 2 files changed, 112 insertions(+), 112 deletions(-) diff --git a/src/BootstrapBlazor.Server/Locales/en-US.json b/src/BootstrapBlazor.Server/Locales/en-US.json index e898ec568c0..f287afdb633 100644 --- a/src/BootstrapBlazor.Server/Locales/en-US.json +++ b/src/BootstrapBlazor.Server/Locales/en-US.json @@ -169,7 +169,12 @@ "BarcodeReader": "BarcodeReader", "Block": "Block", "Bluetooth": "IBluetooth", + "BootstrapBlazorIcon": "Icon", "BootstrapIcon": "Bootstrap Icons", + "BootstrapInput": "BootstrapInput", + "BootstrapInputGroup": "BootstrapInputGroup", + "BootstrapInputNumber": "BootstrapInputNumber", + "BootstrapLabel": "Labels", "Breadcrumb": "Breadcrumb", "Breakpoints": "Breakpoints", "BrowserFinger": "BrowserFingerService", @@ -278,15 +283,11 @@ "Html2Image": "IHtml2Image", "Html2Pdf": "IHtml2Pdf", "HtmlRenderer": "HtmlRenderer", - "BootstrapBlazorIcon": "Icon", "IconPark": "ByteDance IconPark", "Icons": "Icons", "IFrame": "IFrame", "ImageCropper": "ImageCropper", "ImageViewer": "ImageViewer", - "BootstrapInput": "BootstrapInput", - "BootstrapInputGroup": "BootstrapInputGroup", - "BootstrapInputNumber": "BootstrapInputNumber", "InputUpload": "InputUpload", "Install": "Install", "IntersectionObserver": "IntersectionObserver", @@ -294,7 +295,6 @@ "IpAddress": "IpAddress", "JitViewer": "Jit Viewer", "JSExtension": "JSRuntime Extensions", - "BootstrapLabel": "Labels", "Layout": "Layout", "LayoutComponents": "Layouts", "LayoutPage": "Admin Template", @@ -3350,6 +3350,54 @@ "Scale2ButtonText": "ZoomIn", "SetDataButtonText": "SetData" }, + "BootstrapBlazor.Server.Components.Samples.Modals": { + "ModalsBigPopup": "big popup", + "ModalsCenterVerticallyIntro": "Set the vertical centering of the popup component via IsCentered", + "ModalsCenterVerticallyTitle": "Center vertically", + "ModalsDescription": "Inform the user and host relevant actions while preserving the current page state", + "ModalsDialogResizeIntro": "Resize the modal box by setting the ShowResize property", + "ModalsDialogResizeTitle": "Resize", + "ModalsDialogSizeIntro": "Set the size of the popup component through Size", + "ModalsDialogSizeTitle": "Bullet size", + "ModalsFullScreenPopup": "Full screen popup", + "ModalsFullScreenPopup1200": "Full screen popup(<1200px)", + "ModalsFullScreenPopup1400": "Full screen popup(<1400px)", + "ModalsFullScreenPopup992": "Full screen popup(<992px)", + "ModalsFullScreenSizeIntro": "Just set the property FullScreenSize", + "ModalsFullScreenSizeTitle": "Full screen popup", + "ModalsIsBackdropIntro": "Click the area outside the pop-up window to close the pop-up window effect by default", + "ModalsIsBackdropTitle": "IsBackdrop background off mode", + "ModalsIsBackdropToClose": "Click on the background to close the popup", + "ModalsIsDraggableIntro": "Click on the title bar of the pop-up window to drag and drop the pop-up window", + "ModalsIsDraggableTitle": "Draggable popup", + "ModalsLargeFullScreenPopup": "Large full screen popup", + "ModalsLargeFullScreenPopupWindow": "Large full-screen pop-up window", + "ModalsLongContentIntro": "Use IsScrolling to set the scroll wheel sliding function of the pop-up frame component for the excess content", + "ModalsLongContentTitle": "Extra long content", + "ModalsMaximizeIntro": "Show the maximize button by setting the ShowMaximinzeButton popup", + "ModalsMaximizePopup": "Maximize popup", + "ModalsMaximizeTitle": "Maximize button", + "ModalsNormalDefaultPopup": "Default popup", + "ModalsNormalDefaultPopupText": "I am the text in the pop-up window", + "ModalsNormalIntro": "A dialog box pops up, suitable for scenarios that require more customization", + "ModalsNormalIsKeyboard": "by setting Modal component IsKeyboard:true parameter, whether to open the pop-up window is supported ESC", + "ModalsNormalPopups": "Pop-ups", + "ModalsNormalPopupText": "popup text", + "ModalsNormalPopupTitle": "Popup title", + "ModalsNormalTitle": "Basic usage", + "ModalsOverSizedPopup": "OverSized pop-up window", + "ModalsResize": "Resize", + "ModalsScrollBarPopup": "Built-in scroll bar popup", + "ModalsShownCallbackAsyncIntro": "By setting the ShowCallbackAsync callback delegate, this method will be recalled after the pop-up window is displayed", + "ModalsShownCallbackAsyncTitle": "Popup shows callback method", + "ModalsSmallPopup": "small popup", + "ModalsSuperLargeFullScreenPopupWindow": "Super large full-screen pop-up window", + "ModalsSuperLargePopup": "super large pop-up window", + "ModalsTitle": "Modal box", + "ModalsTitlePopupWindowText": "I am the text in the pop-up window", + "ModalsVerticallyCenteredPopup": "Vertically centered popup", + "ModalsVeryLongContent": "Pop-up window with very long content" + }, "BootstrapBlazor.Server.Components.Samples.Modbus.ModbusFactories": { "BenchmarkProjectIntro": "The project includes a Benchmark test project", "BenchmarkResultIntro": "The Benchmark results are shown below:", @@ -3371,9 +3419,9 @@ "FactoryMethodRtu": "Use GetOrCreateRtuMaster to get an IModbusRtuClient instance", "FactoryMethodRtuOverTcp": "Use GetOrCreateRtuOverTcpMaster to get an IModbusTcpClient instance", "FactoryMethodRtuOverUdp": "Use GetOrCreateRtuOverUdpMaster to get an IModbusTcpClient instance", + "FactoryMethodsTitle": "IModbusFactory instance methods", "FactoryMethodTcp": "Use GetOrCreateTcpMaster to get an IModbusTcpClient instance", "FactoryMethodUdp": "Use GetOrCreateUdpMaster to get an IModbusTcpClient instance", - "FactoryMethodsTitle": "IModbusFactory instance methods", "FloatCustomExtensionIntro": "By accessing the raw Buffer through IModbusResponse, you can easily build custom extensions for your own business data types. For example, two consecutive registers can be interpreted as a 32-bit floating-point value that follows the IEEE 754 standard.", "FloatEndiannessTips": "Note: when converting Buffer into custom types such as a 32-bit floating-point value, pay attention to endianness. Byte order changes how the data is interpreted, and the wrong byte order can produce incorrect results. Choose the proper conversion based on the actual device or protocol specification.", "FloatIndexIntro": "In the methods above, the index parameter represents the float value index. Each float occupies 2 registers. If multiple float values are read at once, adjust index to access them in sequence.", @@ -3404,54 +3452,6 @@ "TransportTcp": "Modbus TCP/IP: Runs over Ethernet using the TCP/IP protocol, with the default port 502. It adds an MBAP header on top of the Modbus RTU protocol and omits the CRC checksum because TCP itself already provides a reliable connection service.", "WasmTips": "Special note: this service does not support wasm mode" }, - "BootstrapBlazor.Server.Components.Samples.Modals": { - "ModalsBigPopup": "big popup", - "ModalsCenterVerticallyIntro": "Set the vertical centering of the popup component via IsCentered", - "ModalsCenterVerticallyTitle": "Center vertically", - "ModalsDescription": "Inform the user and host relevant actions while preserving the current page state", - "ModalsDialogResizeIntro": "Resize the modal box by setting the ShowResize property", - "ModalsDialogResizeTitle": "Resize", - "ModalsDialogSizeIntro": "Set the size of the popup component through Size", - "ModalsDialogSizeTitle": "Bullet size", - "ModalsFullScreenPopup": "Full screen popup", - "ModalsFullScreenPopup1200": "Full screen popup(<1200px)", - "ModalsFullScreenPopup1400": "Full screen popup(<1400px)", - "ModalsFullScreenPopup992": "Full screen popup(<992px)", - "ModalsFullScreenSizeIntro": "Just set the property FullScreenSize", - "ModalsFullScreenSizeTitle": "Full screen popup", - "ModalsIsBackdropIntro": "Click the area outside the pop-up window to close the pop-up window effect by default", - "ModalsIsBackdropTitle": "IsBackdrop background off mode", - "ModalsIsBackdropToClose": "Click on the background to close the popup", - "ModalsIsDraggableIntro": "Click on the title bar of the pop-up window to drag and drop the pop-up window", - "ModalsIsDraggableTitle": "Draggable popup", - "ModalsLargeFullScreenPopup": "Large full screen popup", - "ModalsLargeFullScreenPopupWindow": "Large full-screen pop-up window", - "ModalsLongContentIntro": "Use IsScrolling to set the scroll wheel sliding function of the pop-up frame component for the excess content", - "ModalsLongContentTitle": "Extra long content", - "ModalsMaximizeIntro": "Show the maximize button by setting the ShowMaximinzeButton popup", - "ModalsMaximizePopup": "Maximize popup", - "ModalsMaximizeTitle": "Maximize button", - "ModalsNormalDefaultPopup": "Default popup", - "ModalsNormalDefaultPopupText": "I am the text in the pop-up window", - "ModalsNormalIntro": "A dialog box pops up, suitable for scenarios that require more customization", - "ModalsNormalIsKeyboard": "by setting Modal component IsKeyboard:true parameter, whether to open the pop-up window is supported ESC", - "ModalsNormalPopups": "Pop-ups", - "ModalsNormalPopupText": "popup text", - "ModalsNormalPopupTitle": "Popup title", - "ModalsNormalTitle": "Basic usage", - "ModalsOverSizedPopup": "OverSized pop-up window", - "ModalsResize": "Resize", - "ModalsScrollBarPopup": "Built-in scroll bar popup", - "ModalsShownCallbackAsyncIntro": "By setting the ShowCallbackAsync callback delegate, this method will be recalled after the pop-up window is displayed", - "ModalsShownCallbackAsyncTitle": "Popup shows callback method", - "ModalsSmallPopup": "small popup", - "ModalsSuperLargeFullScreenPopupWindow": "Super large full-screen pop-up window", - "ModalsSuperLargePopup": "super large pop-up window", - "ModalsTitle": "Modal box", - "ModalsTitlePopupWindowText": "I am the text in the pop-up window", - "ModalsVerticallyCenteredPopup": "Vertically centered popup", - "ModalsVeryLongContent": "Pop-up window with very long content" - }, "BootstrapBlazor.Server.Components.Samples.MouseFollowers": { "MouseFollowerIconIntro": "If you use SVG spritesheet in your project and want to display them in the cursor, then you can use this method. In this case, you need to specify the path to the SVG sprite in the options and set class names.", "MouseFollowerIconTitle": "Icon mode", @@ -5782,10 +5782,10 @@ "TransferBindTitle": "Two-way binding", "TransferCustomerIntro": "You can customize the list title copy, button copy, rendering function of data items, check status copy at the bottom of the list, content area at the bottom of the list, and so on.", "TransferCustomerTitle": "Customizable", - "TransferDisableIntro": "When you set the IsDisabled property value to true, the component suppresses input", - "TransferDisableTitle": "Disable", "TransferDisabledCallbackIntro": "By setting the OnDisabledCallback callback method, you can control whether specific items are disabled. The first argument target identifies the current panel — left for the left panel and right for the right panel — so you can apply different disable rules to each side. The second argument is the SelectedItem, which is null for the header select-all option. In this example the left panel disables 2 4 6 and the right panel disables 5. Disabled items cannot be checked and are excluded from the select-all/deselect-all operations", "TransferDisabledCallbackTitle": "Disable specific items", + "TransferDisableIntro": "When you set the IsDisabled property value to true, the component suppresses input", + "TransferDisableTitle": "Disable", "TransferItemClassIntro": "By setting the OnSetItemClass callback method styles options based on the values of SelectedItem", "TransferItemClassTitle": "Set the Item style", "TransferMinMaxIntro": "By setting Min Max Parameter to limit the number of options", diff --git a/src/BootstrapBlazor.Server/Locales/zh-CN.json b/src/BootstrapBlazor.Server/Locales/zh-CN.json index 29c6fa52b8e..67dbb377588 100644 --- a/src/BootstrapBlazor.Server/Locales/zh-CN.json +++ b/src/BootstrapBlazor.Server/Locales/zh-CN.json @@ -169,7 +169,12 @@ "BarcodeReader": "条码扫描 BarcodeReader", "Block": "条件块 Block", "Bluetooth": "蓝牙服务 IBluetoothService", + "BootstrapBlazorIcon": "图标 Icon", "BootstrapIcon": "Bootstrap Icons", + "BootstrapInput": "输入框 BootstrapInput", + "BootstrapInputGroup": "输入组 BootstrapInputGroup", + "BootstrapInputNumber": "数字框 BootstrapInputNumber", + "BootstrapLabel": "表单标签", "Breadcrumb": "面包屑 Breadcrumb", "Breakpoints": "断点阈值", "BrowserFinger": "浏览器指纹 BrowserFingerService", @@ -278,15 +283,11 @@ "Html2Image": "Html 转 Image IHtml2Image", "Html2Pdf": "Html 转 Pdf IHtml2Pdf", "HtmlRenderer": "Html 转换器 HtmlRenderer", - "BootstrapBlazorIcon": "图标 Icon", "IconPark": "字节跳动图标 IconPark", "Icons": "内置图标", "IFrame": "内嵌框架 IFrame", "ImageCropper": "图像裁剪 ImageCropper", "ImageViewer": "图片 ImageViewer", - "BootstrapInput": "输入框 BootstrapInput", - "BootstrapInputGroup": "输入组 BootstrapInputGroup", - "BootstrapInputNumber": "数字框 BootstrapInputNumber", "InputUpload": "上传组件 InputUpload", "Install": "安装", "IntersectionObserver": "交叉观察者 IntersectionObserver", @@ -294,7 +295,6 @@ "IpAddress": "IP 地址 IpAddress", "JitViewer": "文件预览器 JitViewer", "JSExtension": "JSRuntime 扩展", - "BootstrapLabel": "表单标签", "Layout": "布局组件 Layout", "LayoutComponents": "布局组件", "LayoutPage": "后台模拟器", @@ -3350,6 +3350,54 @@ "Scale2ButtonText": "放大", "SetDataButtonText": "SetData" }, + "BootstrapBlazor.Server.Components.Samples.Modals": { + "ModalsBigPopup": "大弹窗", + "ModalsCenterVerticallyIntro": "通过 IsCentered 设置弹框组件的垂直居中", + "ModalsCenterVerticallyTitle": "垂直居中", + "ModalsDescription": "在保留当前页面状态的情况下,告知用户并承载相关操作", + "ModalsDialogResizeIntro": "通过设置 ShowResize=\"true\" 可以通过鼠标拉动弹窗右下角进行窗口大小调整", + "ModalsDialogResizeTitle": "调整大小弹窗", + "ModalsDialogSizeIntro": "通过 Size 设置弹框组件的大小", + "ModalsDialogSizeTitle": "弹框大小", + "ModalsFullScreenPopup": "全屏弹窗", + "ModalsFullScreenPopup1200": "全屏弹窗(<1200px)", + "ModalsFullScreenPopup1400": "全屏弹窗(<1400px)", + "ModalsFullScreenPopup992": "全屏弹窗(<992px)", + "ModalsFullScreenSizeIntro": "设置属性 FullScreenSize 即可", + "ModalsFullScreenSizeTitle": "全屏弹窗", + "ModalsIsBackdropIntro": "点击弹窗以外区域默认关闭弹窗效果", + "ModalsIsBackdropTitle": "IsBackdrop 背景关闭模式", + "ModalsIsBackdropToClose": "点击背景可关闭弹窗", + "ModalsIsDraggableIntro": "点击弹窗标题栏对弹窗进行拖拽", + "ModalsIsDraggableTitle": "可拖拽弹窗", + "ModalsLargeFullScreenPopup": "大全屏弹窗", + "ModalsLargeFullScreenPopupWindow": "超大全屏弹窗", + "ModalsLongContentIntro": "通过 IsScrolling 针对超出内容设置弹框组件滚轮滑动功能", + "ModalsLongContentTitle": "超长内容", + "ModalsMaximizeIntro": "通过设置 ShowMaximinzeButton 弹窗显示最大化按钮", + "ModalsMaximizePopup": "可最大化弹窗", + "ModalsMaximizeTitle": "最大化按钮", + "ModalsNormalDefaultPopup": "默认弹窗", + "ModalsNormalDefaultPopupText": "我是弹窗内正文", + "ModalsNormalIntro": "弹出一个对话框,适合需要定制性更大的场景", + "ModalsNormalIsKeyboard": "通过设置 Modal 组件的 IsKeyboard:true 参数,开启弹窗是否支持 ESC", + "ModalsNormalPopups": "弹窗", + "ModalsNormalPopupText": "弹窗正文", + "ModalsNormalPopupTitle": "弹窗标题", + "ModalsNormalTitle": "基本用法", + "ModalsOverSizedPopup": "超大弹窗", + "ModalsResize": "弹框大小", + "ModalsScrollBarPopup": "内置滚动条弹窗", + "ModalsShownCallbackAsyncIntro": "通过设置 ShownCallbackAsync 回调委托,弹窗显示后回调此方法", + "ModalsShownCallbackAsyncTitle": "弹窗已显示回调方法", + "ModalsSmallPopup": "小弹窗", + "ModalsSuperLargeFullScreenPopupWindow": "超超大全屏弹窗", + "ModalsSuperLargePopup": "超超大弹窗", + "ModalsTitle": "Modal 模态框", + "ModalsTitlePopupWindowText": "我是弹窗内正文", + "ModalsVerticallyCenteredPopup": "垂直居中的弹窗", + "ModalsVeryLongContent": "内容超长的弹窗" + }, "BootstrapBlazor.Server.Components.Samples.Modbus.ModbusFactories": { "BenchmarkProjectIntro": "项目包含 Benchmark 基准测试工程", "BenchmarkResultIntro": "Benchmark 结果如下:", @@ -3371,9 +3419,9 @@ "FactoryMethodRtu": "通过 GetOrCreateRtuMaster 方法得到 IModbusRtuClient 实例", "FactoryMethodRtuOverTcp": "通过 GetOrCreateRtuOverTcpMaster 方法得到 IModbusTcpClient 实例", "FactoryMethodRtuOverUdp": "通过 GetOrCreateRtuOverUdpMaster 方法得到 IModbusTcpClient 实例", + "FactoryMethodsTitle": "IModbusFactory 实例方法", "FactoryMethodTcp": "通过 GetOrCreateTcpMaster 方法得到 IModbusTcpClient 实例", "FactoryMethodUdp": "通过 GetOrCreateUdpMaster 方法得到 IModbusTcpClient 实例", - "FactoryMethodsTitle": "IModbusFactory 实例方法", "FloatCustomExtensionIntro": "通过接口 IModbusResponse 获得到其原始数据 Buffer 可以通过自定义扩展非常方便的扩展出符合自己业务的数据类型。如通过连续 2 个寄存器存储的数据,得到遵循 IEEE 754 标准的 32 位 浮点数", "FloatEndiannessTips": "注意:在将 Buffer 转换为自定义类型(如 32 位浮点数)时,需要注意字节序(Endianness)。字节序会影响数据的解释方式,错误的字节序可能导致解析结果不正确。请根据实际设备或协议规范选择合适的字节序进行转换。", "FloatIndexIntro": "以上方法中的参数 index 表示 float 值索引,每个 float 占用 2 个寄存器。若一次读取多个浮点数,可通过调整 index 依次访问。", @@ -3404,54 +3452,6 @@ "TransportTcp": "Modbus TCP/IP: 运行于以太网上,使用 TCP/IP 协议,默认端口 502。它在 Modbus RTU 协议基础上添加了 MBAP 报文头,并由于 TCP 本身是可靠连接的服务,因此去掉了 CRC 校验码。", "WasmTips": "特别注意:本服务不支持 wasm 模式" }, - "BootstrapBlazor.Server.Components.Samples.Modals": { - "ModalsBigPopup": "大弹窗", - "ModalsCenterVerticallyIntro": "通过 IsCentered 设置弹框组件的垂直居中", - "ModalsCenterVerticallyTitle": "垂直居中", - "ModalsDescription": "在保留当前页面状态的情况下,告知用户并承载相关操作", - "ModalsDialogResizeIntro": "通过设置 ShowResize=\"true\" 可以通过鼠标拉动弹窗右下角进行窗口大小调整", - "ModalsDialogResizeTitle": "调整大小弹窗", - "ModalsDialogSizeIntro": "通过 Size 设置弹框组件的大小", - "ModalsDialogSizeTitle": "弹框大小", - "ModalsFullScreenPopup": "全屏弹窗", - "ModalsFullScreenPopup1200": "全屏弹窗(<1200px)", - "ModalsFullScreenPopup1400": "全屏弹窗(<1400px)", - "ModalsFullScreenPopup992": "全屏弹窗(<992px)", - "ModalsFullScreenSizeIntro": "设置属性 FullScreenSize 即可", - "ModalsFullScreenSizeTitle": "全屏弹窗", - "ModalsIsBackdropIntro": "点击弹窗以外区域默认关闭弹窗效果", - "ModalsIsBackdropTitle": "IsBackdrop 背景关闭模式", - "ModalsIsBackdropToClose": "点击背景可关闭弹窗", - "ModalsIsDraggableIntro": "点击弹窗标题栏对弹窗进行拖拽", - "ModalsIsDraggableTitle": "可拖拽弹窗", - "ModalsLargeFullScreenPopup": "大全屏弹窗", - "ModalsLargeFullScreenPopupWindow": "超大全屏弹窗", - "ModalsLongContentIntro": "通过 IsScrolling 针对超出内容设置弹框组件滚轮滑动功能", - "ModalsLongContentTitle": "超长内容", - "ModalsMaximizeIntro": "通过设置 ShowMaximinzeButton 弹窗显示最大化按钮", - "ModalsMaximizePopup": "可最大化弹窗", - "ModalsMaximizeTitle": "最大化按钮", - "ModalsNormalDefaultPopup": "默认弹窗", - "ModalsNormalDefaultPopupText": "我是弹窗内正文", - "ModalsNormalIntro": "弹出一个对话框,适合需要定制性更大的场景", - "ModalsNormalIsKeyboard": "通过设置 Modal 组件的 IsKeyboard:true 参数,开启弹窗是否支持 ESC", - "ModalsNormalPopups": "弹窗", - "ModalsNormalPopupText": "弹窗正文", - "ModalsNormalPopupTitle": "弹窗标题", - "ModalsNormalTitle": "基本用法", - "ModalsOverSizedPopup": "超大弹窗", - "ModalsResize": "弹框大小", - "ModalsScrollBarPopup": "内置滚动条弹窗", - "ModalsShownCallbackAsyncIntro": "通过设置 ShownCallbackAsync 回调委托,弹窗显示后回调此方法", - "ModalsShownCallbackAsyncTitle": "弹窗已显示回调方法", - "ModalsSmallPopup": "小弹窗", - "ModalsSuperLargeFullScreenPopupWindow": "超超大全屏弹窗", - "ModalsSuperLargePopup": "超超大弹窗", - "ModalsTitle": "Modal 模态框", - "ModalsTitlePopupWindowText": "我是弹窗内正文", - "ModalsVerticallyCenteredPopup": "垂直居中的弹窗", - "ModalsVeryLongContent": "内容超长的弹窗" - }, "BootstrapBlazor.Server.Components.Samples.MouseFollowers": { "MouseFollowerIconIntro": "在光标中显示SVG图", "MouseFollowerIconTitle": "图标模式", @@ -5782,10 +5782,10 @@ "TransferBindTitle": "双向绑定", "TransferCustomerIntro": "可以对列表标题文案、按钮文案、数据项的渲染函数、列表底部的勾选状态文案、列表底部的内容区等进行自定义。", "TransferCustomerTitle": "可自定义", - "TransferDisableIntro": "设置 IsDisabled 属性值为 true 时,组件禁止输入", - "TransferDisableTitle": "禁用", "TransferDisabledCallbackIntro": "通过设置 OnDisabledCallback 回调方法控制指定选项是否禁用。回调首个参数 target 用于标识当前面板,左侧面板为 left、右侧面板为 right,可据此对左右两侧分别设置禁用规则;第二个参数为 SelectedItem,为 null 时代表头部全选项。本例左侧面板禁用 2 4 6,右侧面板禁用 5。被禁用的选项不可勾选,且不参与全选/取消全选操作", "TransferDisabledCallbackTitle": "禁用指定选项", + "TransferDisableIntro": "设置 IsDisabled 属性值为 true 时,组件禁止输入", + "TransferDisableTitle": "禁用", "TransferItemClassIntro": "通过设置 OnSetItemClass 回调方法根据 SelectedItem 值设置选项样式", "TransferItemClassTitle": "设置 Item 样式", "TransferMinMaxIntro": "通过设置 Min Max 参数来限制选项个数",