mirror of
https://gitee.com/LongbowEnterprise/BootstrapBlazor.git
synced 2024-12-04 04:59:38 +08:00
!3724 feat(#I65BIW): update DateTimePicker/Range component
* chore: bump version 7.2.0 * test: 精简单元测试 * test: 增加 Range 组件单元测试 * test: 增加 DateTimePicker 单元测试 * refactor: 重构 Range 组件切换日视图逻辑 * refactor: 重构 Range 样式逻辑 * feat: 重构选择框值逻辑 * doc: 精简代码 * doc: 移除 TValue 设置 * doc: 更新空值示例 * refactor: 更新双向绑定示例 * feat: 增加 SelectedValue 初始化值 * doc: 更新 DateTimeOffset 示例 * doc: 更新测试用例 * refactor: 优先处理资源文件逻辑
This commit is contained in:
parent
b0dba7dd3e
commit
a2a2fc8bfd
@ -1,6 +1,6 @@
|
||||
<div class="row g-3">
|
||||
<div class="col-sm-6">
|
||||
<DateTimePicker TValue="DateTime?" Value="@BindValue" ValueChanged="@DateTimeValueChanged" Placement="Placement.Right" />
|
||||
<DateTimePicker @bind-Value="@BindValue" />
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<input class="form-control" @bind="@BindValueString" />
|
||||
@ -10,15 +10,9 @@
|
||||
@code {
|
||||
private DateTime? BindValue { get; set; } = DateTime.Today;
|
||||
|
||||
private Task DateTimeValueChanged(DateTime? d)
|
||||
{
|
||||
BindValue = d;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private string BindValueString
|
||||
{
|
||||
get => BindValue.HasValue ? BindValue.Value.ToString("yyyy-MM-dd") : "";
|
||||
set => BindValue = DateTime.TryParse(value, out var d) ? d : DateTime.Today;
|
||||
set => BindValue = DateTime.TryParse(value, out var d) ? d : null;
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
<DateTimePicker TValue="DateTime" Value="@DateTime.Today" />
|
@ -0,0 +1,5 @@
|
||||
<DateTimePicker TValue="DateTimeOffset?" Value="Value" />
|
||||
|
||||
@code {
|
||||
private DateTimeOffset? Value { get; set; } = DateTimeOffset.Now;
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
<div class="row g-3">
|
||||
<div class="col-12 col-sm-8">
|
||||
<DateTimePicker TValue="DateTime?" @bind-Value="@BindNullValue" />
|
||||
<DateTimePicker @bind-Value="@BindNullValue" />
|
||||
</div>
|
||||
<div class="col-12 col-sm-4">
|
||||
<Display TValue="string" Value="@GetNullValueString" />
|
||||
|
@ -1,6 +1,6 @@
|
||||
@inject IStringLocalizer<DateTimePickerShowLabel> Localizer
|
||||
|
||||
<DateTimePicker TValue="DateTime?" ShowLabel="true" DisplayText="@Localizer["DisplayText"]" @bind-Value="@BindNullValue" />
|
||||
<DateTimePicker ShowLabel="true" DisplayText="@Localizer["DisplayText"]" @bind-Value="@BindNullValue" />
|
||||
|
||||
@code {
|
||||
private DateTime? BindNullValue { get; set; }
|
||||
|
@ -6,28 +6,15 @@
|
||||
<DateTimePicker @bind-Value="@Value" />
|
||||
</div>
|
||||
<div class="col-12 col-sm-auto align-self-end">
|
||||
<Button ButtonType="ButtonType.Submit" Text="@SubmitText" Icon="fa-solid fa-floppy-disk" />
|
||||
<Button ButtonType="ButtonType.Submit" Text="@Localizer["SubmitText"]" Icon="fa-solid fa-floppy-disk" />
|
||||
</div>
|
||||
</div>
|
||||
</ValidateForm>
|
||||
|
||||
@code {
|
||||
/// <summary>
|
||||
/// SubmitText
|
||||
/// </summary>
|
||||
private string? SubmitText { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// ModelValidateValue
|
||||
/// </summary>
|
||||
[Required]
|
||||
public DateTime? Value { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// OnInitialized 方法
|
||||
/// </summary>
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
SubmitText ??= Localizer[nameof(SubmitText)];
|
||||
}
|
||||
}
|
||||
|
@ -2559,10 +2559,10 @@
|
||||
"TimeIntro": "Click the confirm button to select the box value consistent with the text box value",
|
||||
"ValidateFormTitle": "Client validation",
|
||||
"ValidateFormIntro": "Check data validity and prompt automatically based on custom validation rules",
|
||||
"DateTitle": "Click on the pop-up date box",
|
||||
"DateIntro": "Select the control based on the date of the day in 「day」 as the base unit",
|
||||
"PlacementTitle": "Data is bound in both directions",
|
||||
"PlacementIntro": "The values in the text box change as the date component time changes",
|
||||
"DateTimeOffsetTitle": "Click on the pop-up date box",
|
||||
"DateTimeOffsetIntro": "Select the control based on the date of the day in 「day」 as the base unit",
|
||||
"BindValueTitle": "Data is bound in both directions",
|
||||
"BindValueIntro": "The values in the text box change as the date component time changes",
|
||||
"ViewModeTitle": "Selector with time",
|
||||
"ViewModeIntro": "Select the date and time in the same selector, click the confirm button and close the pop-up window",
|
||||
"ViewModeTip": "Set the value of the <code>viewMode</code> property to The <code>DateTime</code> of DatePickerViewMode.DateTime",
|
||||
|
@ -2566,10 +2566,10 @@
|
||||
"TimeIntro": "点击确认按钮时间选择框值与文本框值一致",
|
||||
"ValidateFormTitle": "客户端验证",
|
||||
"ValidateFormIntro": "根据自定义验证规则进行数据有效性检查并自动提示",
|
||||
"DateTitle": "点击弹出日期框",
|
||||
"DateIntro": "以「日」为基本单位,基础的日期选择控件",
|
||||
"PlacementTitle": "数据双向绑定",
|
||||
"PlacementIntro": "日期组件时间改变时文本框内的数值也跟着改变",
|
||||
"DateTimeOffsetTitle": "点击弹出日期框",
|
||||
"DateTimeOffsetIntro": "以「日」为基本单位,基础的日期选择控件,此示例绑定 <code>DateTimeOffset</code> 数据类型",
|
||||
"BindValueTitle": "数据双向绑定",
|
||||
"BindValueIntro": "日期组件时间改变时文本框内的数值也跟着改变",
|
||||
"ViewModeTitle": "带时间的选择器",
|
||||
"ViewModeIntro": "在同一个选择器里选择日期和时间,点击确认按钮后关闭弹窗",
|
||||
"ViewModeTip": "设置 <code>ViewMode</code> 属性值为 <code>DatePickerViewMode.DateTime</code>",
|
||||
|
@ -12,9 +12,9 @@
|
||||
|
||||
<DemoBlock Title="@Localizer["ValidateFormTitle"]" Introduction="@Localizer["ValidateFormIntro"]" Name="ValidateForm" Demo="typeof(Demos.DateTimePicker.DateTimePickerValidateDemo)" />
|
||||
|
||||
<DemoBlock Title="@Localizer["DateTitle"]" Introduction="@Localizer["DateIntro"]" Name="Date" Demo="typeof(Demos.DateTimePicker.DateTimePickerDate)" />
|
||||
<DemoBlock Title="@Localizer["DateTimeOffsetTitle"]" Introduction="@Localizer["DateTimeOffsetIntro"]" Name="DateTimeOffset" Demo="typeof(Demos.DateTimePicker.DateTimePickerDateTimeOffsetDemo)" />
|
||||
|
||||
<DemoBlock Title="@Localizer["PlacementTitle"]" Introduction="@Localizer["PlacementIntro"]" Name="Placement" Demo="typeof(Demos.DateTimePicker.DateTimePickerPlacement)" />
|
||||
<DemoBlock Title="@Localizer["BindValueTitle"]" Introduction="@Localizer["BindValueIntro"]" Name="BindValue" Demo="typeof(Demos.DateTimePicker.DateTimePickerBindValueDemo)" />
|
||||
|
||||
<DemoBlock Title="@Localizer["ViewModeTitle"]" Introduction="@Localizer["ViewModeIntro"]" Name="ViewMode" Demo="typeof(Demos.DateTimePicker.DateTimePickerViewMode)">
|
||||
<p>@((MarkupString)Localizer["ViewModeTip"].Value)</p>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
||||
|
||||
<PropertyGroup>
|
||||
<Version>7.1.9-beta04</Version>
|
||||
<Version>7.2.0</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'net5.0'">
|
||||
|
@ -57,9 +57,7 @@ public partial class DatePickerBody
|
||||
.AddClass("current", day == Value && Ranger == null && day.Month == CurrentDate.Month && !overflow)
|
||||
.AddClass("start", Ranger != null && day == Ranger.SelectedValue.Start.Date)
|
||||
.AddClass("end", Ranger != null && day == Ranger.SelectedValue.End.Date)
|
||||
.AddClass("range", Ranger != null && CurrentDate.Month >= Ranger.SelectedValue.Start.Month
|
||||
&& Ranger.SelectedValue.Start != DateTime.MinValue && Ranger.SelectedValue.End != DateTime.MinValue
|
||||
&& day >= Ranger.SelectedValue.Start && day <= Ranger.SelectedValue.End)
|
||||
.AddClass("range", Ranger != null && day >= Ranger.SelectedValue.Start && day <= Ranger.SelectedValue.End)
|
||||
.AddClass("today", day == DateTime.Today)
|
||||
.AddClass("disabled", IsDisabled(day) || overflow)
|
||||
.Build();
|
||||
|
@ -137,6 +137,13 @@ public partial class DateTimePicker<TValue>
|
||||
{
|
||||
base.OnParametersSet();
|
||||
|
||||
DateTimePlaceHolderText ??= Localizer[nameof(DateTimePlaceHolderText)];
|
||||
DatePlaceHolderText ??= Localizer[nameof(DatePlaceHolderText)];
|
||||
GenericTypeErroMessage ??= Localizer[nameof(GenericTypeErroMessage)];
|
||||
DateTimeFormat ??= Localizer[nameof(DateTimeFormat)];
|
||||
DateFormat ??= Localizer[nameof(DateFormat)];
|
||||
Icon ??= "fa-regular fa-calendar-days";
|
||||
|
||||
var type = typeof(TValue);
|
||||
|
||||
// 判断泛型类型
|
||||
@ -145,13 +152,6 @@ public partial class DateTimePicker<TValue>
|
||||
throw new InvalidOperationException(GenericTypeErroMessage);
|
||||
}
|
||||
|
||||
DateTimePlaceHolderText ??= Localizer[nameof(DateTimePlaceHolderText)];
|
||||
DatePlaceHolderText ??= Localizer[nameof(DatePlaceHolderText)];
|
||||
GenericTypeErroMessage ??= Localizer[nameof(GenericTypeErroMessage)];
|
||||
DateTimeFormat ??= Localizer[nameof(DateTimeFormat)];
|
||||
DateFormat ??= Localizer[nameof(DateFormat)];
|
||||
Icon ??= "fa-regular fa-calendar-days";
|
||||
|
||||
// 泛型设置为可为空
|
||||
AllowNull = Nullable.GetUnderlyingType(type) != null;
|
||||
|
||||
@ -167,13 +167,25 @@ public partial class DateTimePicker<TValue>
|
||||
}
|
||||
|
||||
// Value 为 MinValue 时 设置 Value 默认值
|
||||
if (AutoToday)
|
||||
if (AutoToday && (Value == null || Value.ToString() == DateTime.MinValue.ToString()))
|
||||
{
|
||||
if (Value == null || Value.ToString() == DateTime.MinValue.ToString())
|
||||
SelectedValue = DateTime.Today;
|
||||
if (!AllowNull)
|
||||
{
|
||||
SelectedValue = DateTime.Today;
|
||||
CurrentValueAsString = SelectedValue.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
}
|
||||
}
|
||||
else if (Value is DateTime dt)
|
||||
{
|
||||
SelectedValue = dt;
|
||||
}
|
||||
else
|
||||
{
|
||||
var offset = (DateTimeOffset?)(object)Value;
|
||||
SelectedValue = offset.HasValue
|
||||
? offset.Value.DateTime
|
||||
: DateTime.MinValue;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -324,7 +324,7 @@ public partial class DateTimeRange
|
||||
/// <param name="d"></param>
|
||||
internal void UpdateStart(DateTime d)
|
||||
{
|
||||
StartValue = StartValue.AddYears(d.Year - StartValue.Year).AddMonths(d.Month - StartValue.Month);
|
||||
StartValue = d;
|
||||
EndValue = StartValue.AddMonths(1);
|
||||
StateHasChanged();
|
||||
}
|
||||
@ -335,7 +335,7 @@ public partial class DateTimeRange
|
||||
/// <param name="d"></param>
|
||||
internal void UpdateEnd(DateTime d)
|
||||
{
|
||||
EndValue = EndValue.AddYears(d.Year - EndValue.Year).AddMonths(d.Month - EndValue.Month);
|
||||
EndValue = d;
|
||||
StartValue = EndValue.AddMonths(-1);
|
||||
StateHasChanged();
|
||||
}
|
||||
@ -364,11 +364,12 @@ public partial class DateTimeRange
|
||||
SelectedValue.End = DateTime.MinValue;
|
||||
}
|
||||
|
||||
if (d.Year < StartValue.Year || d.Month < StartValue.Month)
|
||||
var startDate = StartValue.AddDays(1 - StartValue.Day);
|
||||
if (d < startDate)
|
||||
{
|
||||
UpdateStart(d);
|
||||
}
|
||||
else if (d.Month > EndValue.Month)
|
||||
else if (d > startDate.AddMonths(2).AddDays(-1))
|
||||
{
|
||||
UpdateEnd(d);
|
||||
}
|
||||
|
@ -17,7 +17,61 @@ public class DateTimePickerTest : BootstrapBlazorTestBase
|
||||
pb.Add(a => a.Value, DateTime.MinValue);
|
||||
});
|
||||
// 设置 Value 为 MinValue 内部更改为 DateTime.Now
|
||||
Assert.NotEqual(DateTime.MinValue, cut.Instance.Value);
|
||||
Assert.Equal(DateTime.MinValue, cut.Instance.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AutoToday_Ok()
|
||||
{
|
||||
var cut = Context.RenderComponent<DateTimePicker<DateTime>>(pb =>
|
||||
{
|
||||
pb.Add(a => a.Value, DateTime.MinValue);
|
||||
});
|
||||
Assert.Equal(DateTime.Today, cut.Instance.Value);
|
||||
|
||||
cut.SetParametersAndRender(pb =>
|
||||
{
|
||||
pb.Add(a => a.AutoToday, false);
|
||||
pb.Add(a => a.Value, DateTime.MinValue);
|
||||
});
|
||||
Assert.Equal(DateTime.MinValue, cut.Instance.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AllowNull_Ok()
|
||||
{
|
||||
var cut = Context.RenderComponent<DateTimePicker<DateTime?>>(pb =>
|
||||
{
|
||||
pb.Add(a => a.Value, DateTime.MinValue);
|
||||
});
|
||||
Assert.Equal(DateTime.MinValue, cut.Instance.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DataTimeOffsetNull_Ok()
|
||||
{
|
||||
var cut = Context.RenderComponent<DateTimePicker<DateTimeOffset?>>(pb =>
|
||||
{
|
||||
pb.Add(a => a.Value, DateTimeOffset.MinValue);
|
||||
});
|
||||
Assert.Equal(DateTimeOffset.MinValue, cut.Instance.Value);
|
||||
|
||||
cut.SetParametersAndRender(pb =>
|
||||
{
|
||||
pb.Add(a => a.Value, null);
|
||||
pb.Add(a => a.AutoToday, false);
|
||||
});
|
||||
Assert.Null(cut.Instance.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DataTimeOffset_Ok()
|
||||
{
|
||||
var cut = Context.RenderComponent<DateTimePicker<DateTimeOffset>>(pb =>
|
||||
{
|
||||
pb.Add(a => a.Value, DateTimeOffset.MinValue);
|
||||
});
|
||||
Assert.Equal(DateTimeOffset.MinValue, cut.Instance.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -105,6 +159,13 @@ public class DateTimePickerTest : BootstrapBlazorTestBase
|
||||
|
||||
cut.InvokeAsync(() => buttons[0].Click());
|
||||
Assert.Null(cut.Instance.Value);
|
||||
|
||||
cut.SetParametersAndRender(pb =>
|
||||
{
|
||||
pb.Add(a => a.AutoToday, false);
|
||||
});
|
||||
cut.InvokeAsync(() => buttons[0].Click());
|
||||
Assert.Null(cut.Instance.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -753,7 +814,6 @@ public class DateTimePickerTest : BootstrapBlazorTestBase
|
||||
Assert.True(confirm);
|
||||
}
|
||||
|
||||
[JSModuleNotInherited]
|
||||
class MockDateTimePicker : DatePickerBody
|
||||
{
|
||||
public static bool GetSafeYearDateTime_Ok()
|
||||
|
@ -2,6 +2,7 @@
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
// Website: https://www.blazor.zone or https://argozhang.github.io/
|
||||
|
||||
using AngleSharp.Dom;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace UnitTest.Components;
|
||||
@ -30,6 +31,26 @@ public class DateTimeRangeTest : BootstrapBlazorTestBase
|
||||
// 内部 StartValue 自动减一个月
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void RangeValue_Ok()
|
||||
{
|
||||
var cut = Context.RenderComponent<DateTimeRange>();
|
||||
var cells = cut.FindAll(".date-table tbody span");
|
||||
var end = cells.First(i => i.TextContent == "7");
|
||||
var first = cells.First(i => i.TextContent == "1");
|
||||
cut.InvokeAsync(() => end.Click());
|
||||
cut.InvokeAsync(() => first.Click());
|
||||
|
||||
// confirm
|
||||
var confirm = cut.FindAll(".is-confirm").Last();
|
||||
cut.InvokeAsync(() => confirm.Click());
|
||||
var value = cut.Instance.Value;
|
||||
var startDate = DateTime.Today.AddDays(1 - DateTime.Today.Day);
|
||||
var endDate = startDate.AddDays(7).AddSeconds(-1);
|
||||
Assert.Equal(startDate, value.Start);
|
||||
Assert.Equal(endDate, value.End);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TodayButtonText_Ok()
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user