diff --git a/src/BootstrapBlazor.Server/BootstrapBlazor.Server.csproj b/src/BootstrapBlazor.Server/BootstrapBlazor.Server.csproj index 7a4286b8747..5d4ca951ed0 100644 --- a/src/BootstrapBlazor.Server/BootstrapBlazor.Server.csproj +++ b/src/BootstrapBlazor.Server/BootstrapBlazor.Server.csproj @@ -32,9 +32,10 @@ - + + @@ -46,7 +47,7 @@ - + @@ -70,8 +71,9 @@ - + + @@ -80,7 +82,7 @@ - + diff --git a/src/BootstrapBlazor.Server/Components/App.razor b/src/BootstrapBlazor.Server/Components/App.razor index eeb266ab05c..8eb51d6eb06 100644 --- a/src/BootstrapBlazor.Server/Components/App.razor +++ b/src/BootstrapBlazor.Server/Components/App.razor @@ -14,7 +14,7 @@ - Codestin Search App + Codestin Search App diff --git a/src/BootstrapBlazor.Server/Components/Components/CustomerFilter.razor b/src/BootstrapBlazor.Server/Components/Components/CustomerFilter.razor index 41682c8d957..a3110a6adc1 100644 --- a/src/BootstrapBlazor.Server/Components/Components/CustomerFilter.razor +++ b/src/BootstrapBlazor.Server/Components/Components/CustomerFilter.razor @@ -2,9 +2,12 @@ @if (IsHeaderRow) { - + } else { - + } + + diff --git a/src/BootstrapBlazor.Server/Components/Components/CustomerFilter.razor.cs b/src/BootstrapBlazor.Server/Components/Components/CustomerFilter.razor.cs index fe073eeccd3..f13164565b9 100644 --- a/src/BootstrapBlazor.Server/Components/Components/CustomerFilter.razor.cs +++ b/src/BootstrapBlazor.Server/Components/Components/CustomerFilter.razor.cs @@ -3,29 +3,45 @@ // See the LICENSE file in the project root for more information. // Maintainer: Argo Zhang(argo@live.ca) Website: https://www.blazor.zone +using BootstrapBlazor.Server.Components.Samples.Table; + namespace BootstrapBlazor.Server.Components.Components; /// -/// +/// CustomerFilter 组件示例代码 /// public partial class CustomerFilter { - private int? Value; + [Inject] + [NotNull] + private IStringLocalizer? TableFilterLocalizer { get; set; } + + private int? _value; - private readonly IEnumerable _items = new SelectedItem[] + private List _items = []; + + /// + /// + /// + protected override void OnInitialized() { - new() { Value = "", Text = "请选择 ..." }, - new() { Value = "10", Text = "大于 10" }, - new() { Value = "50", Text = "大于 50" }, - new() { Value = "80", Text = "大于 80" } - }; + base.OnInitialized(); + + _items = + [ + new SelectedItem { Value = "", Text = TableFilterLocalizer["CustomerFilterItem1"] }, + new SelectedItem { Value = "10", Text = TableFilterLocalizer["CustomerFilterItem2"] }, + new SelectedItem { Value = "50", Text = TableFilterLocalizer["CustomerFilterItem3"] }, + new SelectedItem { Value = "80", Text = TableFilterLocalizer["CustomerFilterItem4"] } + ]; + } /// /// 重置过滤条件方法 /// public override void Reset() { - Value = null; + _value = null; StateHasChanged(); } @@ -36,12 +52,12 @@ public override void Reset() public override FilterKeyValueAction GetFilterConditions() { var filter = new FilterKeyValueAction(); - if (Value != null) + if (_value != null) { filter.Filters.Add(new FilterKeyValueAction() { FieldKey = FieldKey, - FieldValue = Value.Value, + FieldValue = _value.Value, FilterAction = FilterAction.GreaterThan }); } diff --git a/src/BootstrapBlazor.Server/Components/Components/Header.razor b/src/BootstrapBlazor.Server/Components/Components/Header.razor index ff5d44e649f..b5db1b2cb8d 100644 --- a/src/BootstrapBlazor.Server/Components/Components/Header.razor +++ b/src/BootstrapBlazor.Server/Components/Components/Header.razor @@ -37,7 +37,7 @@ - @DownloadText + @DownloadText diff --git a/src/BootstrapBlazor.Server/Components/Components/Header.razor.cs b/src/BootstrapBlazor.Server/Components/Components/Header.razor.cs index 297655ae40d..0ec65b95f85 100644 --- a/src/BootstrapBlazor.Server/Components/Components/Header.razor.cs +++ b/src/BootstrapBlazor.Server/Components/Components/Header.razor.cs @@ -36,8 +36,6 @@ public partial class Header [NotNull] private string? TutorialsText { get; set; } - private const string DownloadUrl = "https://github.com/dotnetcore/BootstrapBlazor/releases?wt.mc_id=DT-MVP-5004174"; - private string _versionString = ""; /// diff --git a/src/BootstrapBlazor.Server/Components/Components/TooltipContent.razor b/src/BootstrapBlazor.Server/Components/Components/TooltipContent.razor new file mode 100644 index 00000000000..90ee795c511 --- /dev/null +++ b/src/BootstrapBlazor.Server/Components/Components/TooltipContent.razor @@ -0,0 +1 @@ + diff --git a/src/BootstrapBlazor.Server/Components/Components/TooltipContent.razor.cs b/src/BootstrapBlazor.Server/Components/Components/TooltipContent.razor.cs new file mode 100644 index 00000000000..3b453dc86ca --- /dev/null +++ b/src/BootstrapBlazor.Server/Components/Components/TooltipContent.razor.cs @@ -0,0 +1,25 @@ +// 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.Server.Components.Components; + +/// +/// TooltipContent 组件用于显示 Tooltip 的内容 +/// +public partial class TooltipContent +{ + [CascadingParameter] + private Tooltip? Tooltip { get; set; } + + private async Task ToggleShow() + { + if (Tooltip == null) + { + return; + } + + await Tooltip.Toggle(); + } +} diff --git a/src/BootstrapBlazor.Server/Components/Layout/BaseLayout.razor.cs b/src/BootstrapBlazor.Server/Components/Layout/BaseLayout.razor.cs index 1ec51dc3808..d089760ff06 100644 --- a/src/BootstrapBlazor.Server/Components/Layout/BaseLayout.razor.cs +++ b/src/BootstrapBlazor.Server/Components/Layout/BaseLayout.razor.cs @@ -3,7 +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 BootstrapBlazor.Server.Data; using Microsoft.Extensions.Options; using Microsoft.JSInterop; diff --git a/src/BootstrapBlazor.Server/Components/Layout/HomeLayout.razor b/src/BootstrapBlazor.Server/Components/Layout/HomeLayout.razor index 7922e1a4b05..ceab4b681d0 100644 --- a/src/BootstrapBlazor.Server/Components/Layout/HomeLayout.razor +++ b/src/BootstrapBlazor.Server/Components/Layout/HomeLayout.razor @@ -11,7 +11,7 @@
本项目属于 .NET 基金会,并根据其 行为准则 运作
- + dotnet-foundation
public partial class Html2Images { - /// - /// 获得 IconTheme 实例 - /// - [Inject] - [NotNull] - private IIconTheme? IconTheme { get; set; } - [Inject] [NotNull] private IStringLocalizer? LocalizerFoo { get; set; } @@ -29,10 +22,6 @@ public partial class Html2Images [NotNull] private IStringLocalizer? Localizer { get; set; } - [Inject] - [NotNull] - private NavigationManager? NavigationManager { get; set; } - [NotNull] private List? Items { get; set; } diff --git a/src/BootstrapBlazor.Server/Components/Samples/PdfViewers.razor.cs b/src/BootstrapBlazor.Server/Components/Samples/PdfViewers.razor.cs index 1c0c7ce673a..175db17dd0d 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/PdfViewers.razor.cs +++ b/src/BootstrapBlazor.Server/Components/Samples/PdfViewers.razor.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.ComponentModel; - namespace BootstrapBlazor.Server.Components.Samples; /// diff --git a/src/BootstrapBlazor.Server/Components/Samples/Popovers.razor b/src/BootstrapBlazor.Server/Components/Samples/Popovers.razor index 3196a4cc376..90a8c2f40dc 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/Popovers.razor +++ b/src/BootstrapBlazor.Server/Components/Samples/Popovers.razor @@ -66,4 +66,19 @@ + +
+
    +
  • @((MarkupString)Localizer["PopoversManualDescLI1"].Value)
  • +
  • @((MarkupString)Localizer["PopoversManualDescLI2"].Value)
  • +
+
+ + + + +
+ diff --git a/src/BootstrapBlazor.Server/Components/Samples/Popovers.razor.cs b/src/BootstrapBlazor.Server/Components/Samples/Popovers.razor.cs index 40c694123cd..c9a32d7ca07 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/Popovers.razor.cs +++ b/src/BootstrapBlazor.Server/Components/Samples/Popovers.razor.cs @@ -18,6 +18,16 @@ public sealed partial class Popovers private string? _templateTitle; + private Popover? _popover; + + private async Task ToggleShow() + { + if (_popover != null) + { + await _popover.Toggle(); + } + } + /// /// /// diff --git a/src/BootstrapBlazor.Server/Components/Samples/SelectTables.razor.cs b/src/BootstrapBlazor.Server/Components/Samples/SelectTables.razor.cs index cf0dd455f5c..fcdca1de2c0 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/SelectTables.razor.cs +++ b/src/BootstrapBlazor.Server/Components/Samples/SelectTables.razor.cs @@ -99,14 +99,6 @@ class SelectTableMode /// private AttributeItem[] GetAttributes() => [ - new() - { - Name = "Items", - Description = Localizer["AttributeItems"], - Type = "IEnumerable", - ValueList = " — ", - DefaultValue = " — " - }, new() { Name = "TableColumns", diff --git a/src/BootstrapBlazor.Server/Components/Samples/SvgEditors.razor b/src/BootstrapBlazor.Server/Components/Samples/SvgEditors.razor index 8d8c0ad844a..9948dad8f30 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/SvgEditors.razor +++ b/src/BootstrapBlazor.Server/Components/Samples/SvgEditors.razor @@ -3,6 +3,8 @@

@Localizer["SvgEditorTitle"]

+ + diff --git a/src/BootstrapBlazor.Server/Components/Samples/Table/TablesFilter.razor b/src/BootstrapBlazor.Server/Components/Samples/Table/TablesFilter.razor index 215cd90e2c3..997fe95c8f4 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/Table/TablesFilter.razor +++ b/src/BootstrapBlazor.Server/Components/Samples/Table/TablesFilter.razor @@ -88,7 +88,13 @@ - + + + + + + + diff --git a/src/BootstrapBlazor.Server/Components/Samples/TaskBoard.razor b/src/BootstrapBlazor.Server/Components/Samples/TaskBoard.razor new file mode 100644 index 00000000000..3dda0358228 --- /dev/null +++ b/src/BootstrapBlazor.Server/Components/Samples/TaskBoard.razor @@ -0,0 +1,16 @@ +@page "/task-board" +@using BootstrapBlazor.Components.Tasks + +@inject IStringLocalizer Localizer + +

@Localizer["TaskBoardTitle"]

+ +

@Localizer["TaskBoardIntro"]

+ + + + + + diff --git a/src/BootstrapBlazor.Server/Components/Samples/TaskBoard.razor.cs b/src/BootstrapBlazor.Server/Components/Samples/TaskBoard.razor.cs new file mode 100644 index 00000000000..ca232a02e09 --- /dev/null +++ b/src/BootstrapBlazor.Server/Components/Samples/TaskBoard.razor.cs @@ -0,0 +1,14 @@ +// 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.Server.Components.Samples; + +/// +/// TaskBoard 组件示例 +/// +public partial class TaskBoard +{ + +} diff --git a/src/BootstrapBlazor.Server/Components/Samples/Tooltips.razor b/src/BootstrapBlazor.Server/Components/Samples/Tooltips.razor index 73434013139..0f4bfad44a5 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/Tooltips.razor +++ b/src/BootstrapBlazor.Server/Components/Samples/Tooltips.razor @@ -88,4 +88,19 @@ + +
+
    +
  • @((MarkupString)Localizer["TooltipsManualDescLI1"].Value)
  • +
  • @((MarkupString)Localizer["TooltipsManualDescLI2"].Value)
  • +
+
+ + + + +
+ diff --git a/src/BootstrapBlazor.Server/Components/Samples/Tooltips.razor.cs b/src/BootstrapBlazor.Server/Components/Samples/Tooltips.razor.cs index f6392484fa3..ca0274194e4 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/Tooltips.razor.cs +++ b/src/BootstrapBlazor.Server/Components/Samples/Tooltips.razor.cs @@ -20,6 +20,16 @@ public partial class Tooltips private static string HtmlString => "This is Blazor tooltip"; + private Tooltip? _tooltip; + + private async Task ToggleShow() + { + if (_tooltip != null) + { + await _tooltip.Toggle(); + } + } + /// /// 获得属性方法 /// diff --git a/src/BootstrapBlazor.Server/Components/Samples/Tutorials/LoginAndRegister/Template5.razor b/src/BootstrapBlazor.Server/Components/Samples/Tutorials/LoginAndRegister/Template5.razor index a51b377624f..f7633160bbb 100644 --- a/src/BootstrapBlazor.Server/Components/Samples/Tutorials/LoginAndRegister/Template5.razor +++ b/src/BootstrapBlazor.Server/Components/Samples/Tutorials/LoginAndRegister/Template5.razor @@ -1,6 +1,7 @@ @page "/tutorials/template5" @layout TutorialsLayout @inherits WebSiteModuleComponentBase +@inject IOptions WebsiteOption @attribute [JSModuleAutoLoader("Samples/Tutorials/LoginAndRegister/Template5.razor.js", AutoInvokeDispose = false)] @@ -57,7 +58,7 @@ - +

此登录高仿微软登录 UI

diff --git a/src/BootstrapBlazor.Server/Extensions/MenusLocalizerExtensions.cs b/src/BootstrapBlazor.Server/Extensions/MenusLocalizerExtensions.cs index 6257ea71252..76758c82c7c 100644 --- a/src/BootstrapBlazor.Server/Extensions/MenusLocalizerExtensions.cs +++ b/src/BootstrapBlazor.Server/Extensions/MenusLocalizerExtensions.cs @@ -816,6 +816,12 @@ void AddData(DemoMenuItem item) Url = "tag" }, new() + { + IsNew = true, + Text = Localizer["TaskDashBoard"], + Url = "task-board" + }, + new() { Text = Localizer["Timeline"], Url = "timeline" @@ -1553,6 +1559,12 @@ void AddServices(DemoMenuItem item) Url = "dispatch" }, new() + { + IsNew = true, + Text = Localizer["Dom2ImageService"], + Url = "dom2image" + }, + new() { Text = Localizer["Download"], Url = "download" diff --git a/src/BootstrapBlazor.Server/Extensions/ServiceCollectionExtensions.cs b/src/BootstrapBlazor.Server/Extensions/ServiceCollectionExtensions.cs index e0f9fc56317..e62f7e8803e 100644 --- a/src/BootstrapBlazor.Server/Extensions/ServiceCollectionExtensions.cs +++ b/src/BootstrapBlazor.Server/Extensions/ServiceCollectionExtensions.cs @@ -3,7 +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 BootstrapBlazor.OpcDa; using Longbow.Tasks.Services; using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.Options; diff --git a/src/BootstrapBlazor.Server/Extensions/ServiceCollectionSharedExtensions.cs b/src/BootstrapBlazor.Server/Extensions/ServiceCollectionSharedExtensions.cs index 13c4ae2790a..c965c15bd2c 100644 --- a/src/BootstrapBlazor.Server/Extensions/ServiceCollectionSharedExtensions.cs +++ b/src/BootstrapBlazor.Server/Extensions/ServiceCollectionSharedExtensions.cs @@ -67,6 +67,9 @@ public static IServiceCollection AddBootstrapBlazorServices(this IServiceCollect // 增加 Html2Image 导出服务 services.AddBootstrapBlazorHtml2ImageService(); + // 增加 Dom2Image 导出服务 + services.AddBootstrapBlazorDom2ImageService(); + // 增加 WinBox 弹窗服务 services.AddBootstrapBlazorWinBoxService(); diff --git a/src/BootstrapBlazor.Server/Locales/en-US.json b/src/BootstrapBlazor.Server/Locales/en-US.json index bad1a1235da..3f262a8381b 100644 --- a/src/BootstrapBlazor.Server/Locales/en-US.json +++ b/src/BootstrapBlazor.Server/Locales/en-US.json @@ -108,7 +108,11 @@ "TooltipsCustomClassIntro": "Set the custom style by setting the CustomClass parameter", "BootstrapTooltipIntro": "Try to make a new component that is more comfortable to use, you can use it early, the name of the component may be changed later", "BootstrapTooltipTips1": "Wrap other components or HTML elements through BootstrapTooltip so that the wrapped object has the function of Tooltip", - "BootstrapTooltipTips2": "In this example, an icon is wrapped by BootstrapTooltip. When the mouse moves over the icon, the default Tooltip is displayed, which is easier and faster to use." + "BootstrapTooltipTips2": "In this example, an icon is wrapped by BootstrapTooltip. When the mouse moves over the icon, the default Tooltip is displayed, which is easier and faster to use.", + "TooltipsManualTitle": "Manual", + "TooltipsManualIntro": "Use code to control the tooltip state by setting Trigger=\"manual\"", + "TooltipsManualDescLI1": "The child component uses the cascade parameters to get the Tooltip instance and then calls its methods Show Hide Toggle", + "TooltipsManualDescLI2": "Get the Tooltip instance through @ref and call its instances method" }, "BootstrapBlazor.Server.Components.Samples.Toasts": { "ToastsTitle": "Toast Lightweight Popup", @@ -348,7 +352,11 @@ "PopoversCssClassTitle": "custom style", "PopoversCssClassIntro": "Customize the pop-up window by setting the Popover parameter CssClass", "PopoversCssClassDescription": "set up Popover parameter CssClass=\"custom-popover\" make custom styles", - "PopoversCssClassButtonText": "Click to activate/deactivate" + "PopoversCssClassButtonText": "Click to activate/deactivate", + "PopoversManualTitle": "Manual", + "PopoversManualIntro": "Use code to control the popover state by setting Trigger=\"manual\"", + "PopoversManualDescLI1": "The child component uses the cascade parameters to get the Popover instance and then calls its methods Show Hide Toggle", + "PopoversManualDescLI2": "Get the Popover instance through @ref and call its instances method" }, "BootstrapBlazor.Server.Components.Samples.Progress": { "ProgressTitle": "Progress progress bar", @@ -2536,7 +2544,7 @@ "EditorPlaceholderIntro": "The prompt message when a null value is set by setting the Placeholder attribute", "EditorPlaceholderDescription": "The default prompt is Edit after clicking", "EditorIsEditorTitle": "Display as a rich text edit box by default", - "EditorIsEditorIntro": "Set the component to be directly displayed as a rich text edit box by setting the IsEditor property", + "EditorIsEditorIntro": "Set the component to be directly displayed as a rich text edit box by setting the IsEditor property. When uploading an image, you can get the image information through the OnFileUpload callback method", "EditorHeightTitle": "Custom height", "EditorHeightIntro": "Set the height of the component by setting the Height property", "EditorOnValueChangedTitle": "Two-way binding", @@ -2577,7 +2585,8 @@ "DoMethodAsyncButton2": "Update to H2", "DoMethodAsyncButton3": "Insert Image", "DoMethodAsyncButton4": "Get Code", - "DoMethodAsyncPasteHTML": "Here is the content inserted by the external button" + "DoMethodAsyncPasteHTML": "Here is the content inserted by the external button", + "OnFileUploadAttribute": "File upload callback method" }, "BootstrapBlazor.Server.Components.Samples.EditorForms": { "Title": "EditorForm", @@ -4850,7 +4859,9 @@ "NetworkMonitor": "Network Monitor", "Toolbar": "Toolbar", "OpcDaService": "OpcDaServer", - "Navbar": "Navbar" + "Navbar": "Navbar", + "TaskDashBoard": "TaskDashBoard", + "Dom2ImageService": "IDom2HtmlService" }, "BootstrapBlazor.Server.Components.Samples.Table.TablesHeader": { "TablesHeaderTitle": "Header grouping function", @@ -5703,7 +5714,11 @@ "SetFilterInCodeIntro": "Example shows how to set filters through code", "SetFilterInCodeButtonText1": "Name contains 01", "SetFilterInCodeButtonText2": "Reset All Filter", - "TablesFilterTemplateDescription": "

The FilterTemplate type is RenderFragment its value is a custom component, and the component must inherit the filterBase In this case, the last column in this case, the Quantity column, uses the custom component by filtering the template CustomerFilter [portal] CustomerFilter component source code

Notes:

  • Custom filter components are wrapped with FilterProvider, and FilterProvider must be under the FilterTemplate node
  • Filters can be fine-tuned through the parameters of the FilterProvider component; for example, by setting ShowMoreButton to control whether the + - symbol is displayed
  • " + "TablesFilterTemplateDescription": "

    The FilterTemplate type is RenderFragment its value is a custom component, and the component must inherit the filterBase In this case, the last column in this case, the Quantity column, uses the custom component by filtering the template CustomerFilter [portal] CustomerFilter component source code

    Notes:

    • Custom filter components are wrapped with FilterProvider, and FilterProvider must be under the FilterTemplate node
    • Filters can be fine-tuned through the parameters of the FilterProvider component; for example, by setting ShowMoreButton to control whether the + - symbol is displayed
    • ", + "CustomerFilterItem1": "All", + "CustomerFilterItem2": "Greater than 10", + "CustomerFilterItem3": "Greater than 50", + "CustomerFilterItem4": "Greater than 80" }, "BootstrapBlazor.Server.Components.Samples.Table.TablesFixedHeader": { "TablesFixedHeaderTitle": "Fixed header function", @@ -7174,5 +7189,21 @@ "NavbarDescription": "A powerful, responsive navigation header, the navbar. Includes support for branding, navigation, and more", "NormalTitle": "Basic usage", "NormalIntro": "By setting NavbarBrand NavbarToggleButton NavbarCollapse NavbarGroup NavbarItem to layout its internal elements" + }, + "BootstrapBlazor.Server.Components.Samples.TaskBoard": { + "TaskBoardTitle": "Task DashBoard", + "TaskBoardIntro": "Background task dashboard", + "TaskBoardNormalTitle": "Basic usage", + "TaskBoardNormalIntro": "This component provides detailed information about background task schedule trigger." + }, + "BootstrapBlazor.Server.Components.Samples.Dom2Images": { + "Dom2ImageTitle": "Dom2Image", + "Dom2ImageIntro": "Export HTML snippets as images", + "Dom2ImageNormalTitle": "Basic usage", + "Dom2ImageNormalIntro": "Convert the node to an image by specifying a Selector", + "Dom2ImageDesc": "Since the underlying framework uses SnapDOM , if you encounter any problems during actual use, please refer to the project Issue", + "Dom2ImageButtonText": "Convert", + "Dom2ImageDownloadText": "Download", + "Dom2ImageFullText": "Capture" } } diff --git a/src/BootstrapBlazor.Server/Locales/zh-CN.json b/src/BootstrapBlazor.Server/Locales/zh-CN.json index 6520118edc3..53c6acca4c0 100644 --- a/src/BootstrapBlazor.Server/Locales/zh-CN.json +++ b/src/BootstrapBlazor.Server/Locales/zh-CN.json @@ -108,7 +108,11 @@ "TooltipsCustomClassIntro": "通过设置 CustomClass 参数进行自定义样式设置", "BootstrapTooltipIntro": "尝试新做一个用起来比较舒服的组件,可尝鲜使用,后期组件名字可能会更改", "BootstrapTooltipTips1": "通过 BootstrapTooltip 对其他组件或者 HTML 元素进行包裹,使其被包裹对象具有 Tooltip 功能", - "BootstrapTooltipTips2": "本例中通过 BootstrapTooltip 包裹一个图标,鼠标移动到图标上时,显示预设 Tooltip 使用更简单快捷" + "BootstrapTooltipTips2": "本例中通过 BootstrapTooltip 包裹一个图标,鼠标移动到图标上时,显示预设 Tooltip 使用更简单快捷", + "TooltipsManualTitle": "手动控制状态", + "TooltipsManualIntro": "通过设置 Trigger=\"manual\" 使用代码控制提示栏状态", + "TooltipsManualDescLI1": "子组件使用级联参数得到 Tooltip 实例,然后调用其相应方法 Show Hide Toggle", + "TooltipsManualDescLI2": "通过 @ref 获得 Tooltip 实例调用其对应方法" }, "BootstrapBlazor.Server.Components.Samples.Toasts": { "ToastsTitle": "Toast 轻量弹窗", @@ -348,7 +352,11 @@ "PopoversCssClassTitle": "自定义样式", "PopoversCssClassIntro": "通过设置 Popover 参数 CssClass 对弹窗进行自定义样式", "PopoversCssClassDescription": "设置 Popover 参数 CssClass=\"custom-popover\" 进行自定义样式", - "PopoversCssClassButtonText": "Click 激活/关闭" + "PopoversCssClassButtonText": "Click 激活/关闭", + "PopoversManualTitle": "手动控制状态", + "PopoversManualIntro": "通过设置 Trigger=\"manual\" 使用代码控制提示栏状态", + "PopoversManualDescLI1": "子组件使用级联参数得到 Popover 实例,然后调用其相应方法 Show Hide Toggle", + "PopoversManualDescLI2": "通过 @ref 获得 Popover 实例调用其对应方法" }, "BootstrapBlazor.Server.Components.Samples.Progress": { "ProgressTitle": "Progress 进度条", @@ -2537,7 +2545,7 @@ "EditorPlaceholderIntro": "通过设置 Placeholder 属性来设置空值时的提示消息", "EditorPlaceholderDescription": "默认提示是 点击后进行编辑", "EditorIsEditorTitle": "默认显示为富文本编辑框", - "EditorIsEditorIntro": "通过设置 IsEditor 属性来设置组件直接显示为富文本编辑框", + "EditorIsEditorIntro": "通过设置 IsEditor 属性来设置组件直接显示为富文本编辑框,上传图片时可以通过 OnFileUpload 回调方法获得图片信息", "EditorHeightTitle": "自定义高度", "EditorHeightIntro": "通过设置 Height 属性来设置组件高度", "EditorOnValueChangedTitle": "双向绑定", @@ -2578,7 +2586,8 @@ "DoMethodAsyncButton2": "将段落修改为 H2", "DoMethodAsyncButton3": "添加一张图片", "DoMethodAsyncButton4": "获得组件内容", - "DoMethodAsyncPasteHTML": "这里是外部按钮插入的内容" + "DoMethodAsyncPasteHTML": "这里是外部按钮插入的内容", + "OnFileUploadAttribute": "文件上传回调方法" }, "BootstrapBlazor.Server.Components.Samples.EditorForms": { "Title": "EditorForm 表单组件", @@ -4851,7 +4860,9 @@ "NetworkMonitor": "网络状态 NetworkMonitor", "Toolbar": "工具栏 Toolbar", "OpcDaService": "OpcDaServer 服务", - "Navbar": "导航栏 Navbar" + "Navbar": "导航栏 Navbar", + "TaskDashBoard": "任务管理器 TaskDashBoard", + "Dom2ImageService": "节点转图片服务 IDom2HtmlService" }, "BootstrapBlazor.Server.Components.Samples.Table.TablesHeader": { "TablesHeaderTitle": "表头分组功能", @@ -5704,7 +5715,11 @@ "SetFilterInCodeIntro": "示例展示如何通过代码设置过滤条件", "SetFilterInCodeButtonText1": "名称包含01", "SetFilterInCodeButtonText2": "重置条件", - "TablesFilterTemplateDescription": "

      FilterTemplate 类型为 RenderFragment 其值为自定义组件,组件必须继承 FilterBase 本例中最后一列 数量列 通过筛选模板使用自定义组件 CustomerFilter [传送门] CustomerFilter 组件源码

      注意事项:

      • 自定义过滤组件使用 FilterProvider 包裹,FilterProvider必须在 FilterTemplate 节点下
      • 通过 FilterProvider 组件的参数可微调过滤器;例如通过设置 ShowMoreButton 控制是否显示 + - 符号
      • " + "TablesFilterTemplateDescription": "

        FilterTemplate 类型为 RenderFragment 其值为自定义组件,组件必须继承 FilterBase 本例中最后一列 数量列 通过筛选模板使用自定义组件 CustomerFilter [传送门] CustomerFilter 组件源码

        注意事项:

        • 自定义过滤组件使用 FilterProvider 包裹,FilterProvider必须在 FilterTemplate 节点下
        • 通过 FilterProvider 组件的参数可微调过滤器;例如通过设置 ShowMoreButton 控制是否显示 + - 符号
        • ", + "CustomerFilterItem1": "全部", + "CustomerFilterItem2": "大于 10", + "CustomerFilterItem3": "大于 50", + "CustomerFilterItem4": "大于 80" }, "BootstrapBlazor.Server.Components.Samples.Table.TablesFixedHeader": { "TablesFixedHeaderTitle": "固定表头功能", @@ -7175,5 +7190,21 @@ "NavbarDescription": "是网站开发中用于定义顶部导航区域或主页快速启动区域的结构化组件", "NormalTitle": "基本用法", "NormalIntro": "通过设置 NavbarBrand NavbarToggleButton NavbarCollapse NavbarGroup NavbarItem 对其内部元素布局" + }, + "BootstrapBlazor.Server.Components.Samples.TaskBoard": { + "TaskBoardTitle": "Task DashBoard 任务管理器", + "TaskBoardIntro": "后台任务看板", + "TaskBoardNormalTitle": "基本用法", + "TaskBoardNormalIntro": "本组件提供后台任务调度触发器详细信息" + }, + "BootstrapBlazor.Server.Components.Samples.Dom2Images": { + "Dom2ImageTitle": "Dom2Image 元素转图片", + "Dom2ImageIntro": "将 Html 片段导出为图片", + "Dom2ImageNormalTitle": "基本用法", + "Dom2ImageNormalIntro": "通过指定 Selector 将此节点转成图片", + "Dom2ImageDesc": "由于底层使用的是 SnapDOM 实际使用过程中遇到问题请参考项目 Issue", + "Dom2ImageButtonText": "转换", + "Dom2ImageDownloadText": "下载", + "Dom2ImageFullText": "长截图" } } diff --git a/src/BootstrapBlazor.Server/docs.json b/src/BootstrapBlazor.Server/docs.json index 92636c3bb40..b69bf4d3956 100644 --- a/src/BootstrapBlazor.Server/docs.json +++ b/src/BootstrapBlazor.Server/docs.json @@ -90,6 +90,7 @@ "html-renderer": "HtmlRenderers", "html2image": "Html2Images", "html2pdf": "Html2Pdfs", + "dom2image": "Dom2Images", "label": "Labels", "layout": "Layouts", "light": "Lights", @@ -251,7 +252,8 @@ "network-monitor": "NetworkMonitors", "toolbar": "Toolbars", "opc-da": "OpcDa", - "navbar": "Navbars" + "navbar": "Navbars", + "task-board": "TaskBoard" }, "video": { "table": "BV1ap4y1x7Qn?p=1", diff --git a/src/BootstrapBlazor/BootstrapBlazor.csproj b/src/BootstrapBlazor/BootstrapBlazor.csproj index 31cd068303b..1eb7e4c700e 100644 --- a/src/BootstrapBlazor/BootstrapBlazor.csproj +++ b/src/BootstrapBlazor/BootstrapBlazor.csproj @@ -1,7 +1,7 @@  - 9.9.2 + 9.9.3 diff --git a/src/BootstrapBlazor/Components/Label/BootstrapLabel.razor.cs b/src/BootstrapBlazor/Components/Label/BootstrapLabel.razor.cs index 8221930a009..e0118c6d65e 100644 --- a/src/BootstrapBlazor/Components/Label/BootstrapLabel.razor.cs +++ b/src/BootstrapBlazor/Components/Label/BootstrapLabel.razor.cs @@ -40,6 +40,7 @@ public partial class BootstrapLabel private string? StyleString => CssBuilder.Default() .AddStyle($"--bb-row-label-width", $"{LabelWidth}px", LabelWidth.HasValue) + .AddStyleFromAttributes(AdditionalAttributes) .Build(); /// diff --git a/src/BootstrapBlazor/Components/Layout/Layout.razor b/src/BootstrapBlazor/Components/Layout/Layout.razor index bd76cfaa9b6..65fd1443dee 100644 --- a/src/BootstrapBlazor/Components/Layout/Layout.razor +++ b/src/BootstrapBlazor/Components/Layout/Layout.razor @@ -5,7 +5,7 @@ @if (_init) { - @if (IsAuthenticated) + @if (_authenticated) {
          @if (Side == null) @@ -126,8 +126,8 @@ } else { - + @HandlerMain() } @@ -135,7 +135,7 @@ RenderFragment RenderTab => @ + EnableErrorLogger="@EnableLogger" ErrorLoggerToastTitle="@ErrorLoggerToastTitle"> ; RenderFragment RenderFooter => diff --git a/src/BootstrapBlazor/Components/Layout/Layout.razor.cs b/src/BootstrapBlazor/Components/Layout/Layout.razor.cs index 477057934e6..32c498f9f52 100644 --- a/src/BootstrapBlazor/Components/Layout/Layout.razor.cs +++ b/src/BootstrapBlazor/Components/Layout/Layout.razor.cs @@ -361,6 +361,12 @@ public partial class Layout : IHandlerException, ITabHeader [Parameter] public bool ShowTabInHeader { get; set; } + /// + /// 获得/设置 是否跳过认证逻辑 默认 false + /// + [Parameter] + public bool SkipAuthenticate { get; set; } + [Inject] [NotNull] private NavigationManager? Navigation { get; set; } @@ -374,7 +380,7 @@ public partial class Layout : IHandlerException, ITabHeader /// /// 获得/设置 是否已授权 /// - private bool IsAuthenticated { get; set; } + private bool _authenticated; /// /// 获得 组件样式 @@ -501,15 +507,15 @@ public partial class Layout : IHandlerException, ITabHeader private IOptionsMonitor? Options { get; set; } private bool _init; - private LayoutHeader? _layoutHeader = null; + private LayoutHeader? _layoutHeader; private ITabHeader? TabHeader => ShowTabInHeader ? this : null; - private bool _enableErrorLogger => EnableErrorLogger ?? Options.CurrentValue.EnableErrorLogger; + private bool EnableLogger => EnableErrorLogger ?? Options.CurrentValue.EnableErrorLogger; - private bool _showToast => ShowErrorLoggerToast ?? Options.CurrentValue.ShowErrorLoggerToast; + private bool ShowToast => ShowErrorLoggerToast ?? Options.CurrentValue.ShowErrorLoggerToast; - private bool _enableILogger => EnableErrorLoggerILogger ?? Options.CurrentValue.EnableErrorLoggerILogger; + private bool EnableILogger => EnableErrorLoggerILogger ?? Options.CurrentValue.EnableErrorLoggerILogger; /// /// @@ -535,30 +541,28 @@ protected override async Task OnInitializedAsync() { await base.OnInitializedAsync(); - // 需要认证并且未认证 - if (AuthenticationStateTask != null) + if (SkipAuthenticate || AuthenticationStateTask == null) { - // wasm 模式下 开启权限必须提供 AdditionalAssemblies 参数 - AdditionalAssemblies ??= [Assembly.GetEntryAssembly()!]; + _authenticated = true; + _init = true; + return; + } - var url = Navigation.ToBaseRelativePathWithoutQueryAndFragment(); - var context = RouteTableFactory.Create(AdditionalAssemblies, url); - if (context.Handler != null) - { - IsAuthenticated = await context.Handler.IsAuthorizedAsync(ServiceProvider, AuthenticationStateTask, Resource); + // wasm 模式下 开启权限必须提供 AdditionalAssemblies 参数 + AdditionalAssemblies ??= [Assembly.GetEntryAssembly()!]; - // 检查当前 Url - if (OnAuthorizing != null) - { - IsAuthenticated = IsAuthenticated && await OnAuthorizing(Navigation.Uri); - } - } - } - else + var url = Navigation.ToBaseRelativePathWithoutQueryAndFragment(); + var context = RouteTableFactory.Create(AdditionalAssemblies, url); + if (context.Handler != null) { - IsAuthenticated = true; - } + _authenticated = await context.Handler.IsAuthorizedAsync(ServiceProvider, AuthenticationStateTask, Resource); + // 检查当前 Url + if (OnAuthorizing != null) + { + _authenticated = _authenticated && await OnAuthorizing(Navigation.Uri); + } + } _init = true; } diff --git a/src/BootstrapBlazor/Components/Popover/Popover.razor b/src/BootstrapBlazor/Components/Popover/Popover.razor index b2751275abc..318ac5215ef 100644 --- a/src/BootstrapBlazor/Components/Popover/Popover.razor +++ b/src/BootstrapBlazor/Components/Popover/Popover.razor @@ -4,7 +4,8 @@ diff --git a/src/BootstrapBlazor/Components/Popover/Popover.razor.cs b/src/BootstrapBlazor/Components/Popover/Popover.razor.cs index c30c51a9328..32cdc4d947c 100644 --- a/src/BootstrapBlazor/Components/Popover/Popover.razor.cs +++ b/src/BootstrapBlazor/Components/Popover/Popover.razor.cs @@ -30,10 +30,11 @@ public partial class Popover private string? _lastContent; - /// - /// - /// - protected override string? CustomClassString => CssBuilder.Default(CustomClass) + private string? ClassString => CssBuilder.Default("bb-popover") + .AddClassFromAttributes(AdditionalAttributes) + .Build(); + + private string? CustomClassString => CssBuilder.Default(CustomClass) .AddClass("shadow", ShowShadow) .Build(); diff --git a/src/BootstrapBlazor/Components/Popover/Popover.razor.js b/src/BootstrapBlazor/Components/Popover/Popover.razor.js index 6dd1bfd2339..5994ab60cbd 100644 --- a/src/BootstrapBlazor/Components/Popover/Popover.razor.js +++ b/src/BootstrapBlazor/Components/Popover/Popover.razor.js @@ -9,6 +9,42 @@ export function init(id, options) { } } +export function show(id, delay) { + const el = document.getElementById(id) + if (el) { + const pop = bootstrap.Popover.getInstance(el); + if (pop) { + setTimeout(() => { + pop.show(); + }, delay || 0); + } + } +} + +export function hide(id, delay) { + const el = document.getElementById(id) + if (el) { + const pop = bootstrap.Popover.getInstance(el); + if (pop) { + setTimeout(() => { + pop.hide(); + }, delay || 0); + } + } +} + +export function toggle(id, delay) { + const el = document.getElementById(id) + if (el) { + const pop = bootstrap.Popover.getInstance(el); + if (pop) { + setTimeout(() => { + pop.toggle(); + }, delay || 0); + } + } +} + export function dispose(id) { const el = document.getElementById(id) if (el) { diff --git a/src/BootstrapBlazor/Components/Select/SelectTree.razor b/src/BootstrapBlazor/Components/Select/SelectTree.razor index b5582d0aab4..84678968bb0 100644 --- a/src/BootstrapBlazor/Components/Select/SelectTree.razor +++ b/src/BootstrapBlazor/Components/Select/SelectTree.razor @@ -15,7 +15,7 @@ } else { - + } diff --git a/src/BootstrapBlazor/Components/Select/SelectTree.razor.cs b/src/BootstrapBlazor/Components/Select/SelectTree.razor.cs index 014544f6ae6..d31a1833252 100644 --- a/src/BootstrapBlazor/Components/Select/SelectTree.razor.cs +++ b/src/BootstrapBlazor/Components/Select/SelectTree.razor.cs @@ -150,6 +150,10 @@ public partial class SelectTree : IModelEqualityComparer [NotNull] private IStringLocalizer>? Localizer { get; set; } + [Inject] + [NotNull] + private IIconTheme? IconTheme { get; set; } + /// /// 获得 input 组件 Id 方法 /// @@ -161,19 +165,9 @@ public partial class SelectTree : IModelEqualityComparer /// private string? InputId => $"{Id}_input"; - /// - /// 获得/设置 上次选项 - /// - private TreeViewItem? SelectedItem { get; set; } - - private List>? ItemCache { get; set; } - - [NotNull] - private List>? ExpandedItemsCache { get; set; } - - [Inject] - [NotNull] - private IIconTheme? IconTheme { get; set; } + private TreeViewItem? _selectedItem; + private List>? _itemCache; + private List>? _expandedItemsCache; private string? SelectTreeCustomClassString => CssBuilder.Default(CustomClassString) .AddClass("select-tree", IsPopover) @@ -190,19 +184,6 @@ protected override void OnInitialized() AddRequiredValidator(); } - /// - /// - /// - protected override async Task OnInitializedAsync() - { - await base.OnInitializedAsync(); - - if (Value != null) - { - await TriggerItemChanged(s => Equals(s.Value, Value)); - } - } - /// /// /// @@ -263,18 +244,22 @@ private async Task TriggerItemChanged(Func, bool> predicate if (currentItem != null) { currentItem.IsActive = true; - await ItemChanged(currentItem); + + if (_selectedItem == null || !Equals(_selectedItem.Value, Value)) + { + await ItemChanged(currentItem); + } } } private List> GetExpandedItems() { - if (ItemCache != Items) + if (_itemCache != Items) { - ItemCache = Items; - ExpandedItemsCache = TreeViewExtensions.GetAllItems(ItemCache).ToList(); + _itemCache = Items; + _expandedItemsCache = [.. TreeViewExtensions.GetAllItems(_itemCache)]; } - return ExpandedItemsCache; + return _expandedItemsCache!; } /// @@ -295,7 +280,7 @@ private async Task OnItemClick(TreeViewItem item) /// private async Task ItemChanged(TreeViewItem item) { - SelectedItem = item; + _selectedItem = item; CurrentValue = item.Value; // 触发 SelectedItemChanged 事件 diff --git a/src/BootstrapBlazor/Components/Tooltip/ITooltip.cs b/src/BootstrapBlazor/Components/Tooltip/ITooltip.cs index 8fcbcbdf2dd..8ffda90c231 100644 --- a/src/BootstrapBlazor/Components/Tooltip/ITooltip.cs +++ b/src/BootstrapBlazor/Components/Tooltip/ITooltip.cs @@ -26,8 +26,9 @@ public interface ITooltip bool IsHtml { get; set; } /// - /// 获得/设置 触发方式 可组合 click focus hover 默认为 focus hover + /// 获得/设置 触发方式 可组合 click focus hover manual 默认为 focus hover /// + /// 设置 manual 时,请使用 组件实例方法 对弹窗状态进行控制 string? Trigger { get; set; } /// @@ -39,6 +40,7 @@ public interface ITooltip /// /// 获得/设置 显示隐藏延时 默认 null /// + /// Delay showing and hiding the tooltip (ms)—doesn’t apply to manual trigger type. If a number is supplied, delay is applied to both hide/show. Object structure is: delay: { "show": 500, "hide": 100 }. string? Delay { get; set; } /// diff --git a/src/BootstrapBlazor/Components/Tooltip/Tooltip.razor b/src/BootstrapBlazor/Components/Tooltip/Tooltip.razor index f3ccbc1af45..2ff453a0a18 100644 --- a/src/BootstrapBlazor/Components/Tooltip/Tooltip.razor +++ b/src/BootstrapBlazor/Components/Tooltip/Tooltip.razor @@ -4,7 +4,7 @@ diff --git a/src/BootstrapBlazor/Components/Tooltip/Tooltip.razor.cs b/src/BootstrapBlazor/Components/Tooltip/Tooltip.razor.cs index 5999ea3625e..bcba585290e 100644 --- a/src/BootstrapBlazor/Components/Tooltip/Tooltip.razor.cs +++ b/src/BootstrapBlazor/Components/Tooltip/Tooltip.razor.cs @@ -25,10 +25,7 @@ public partial class Tooltip : ITooltip /// protected string? HtmlString => IsHtml ? "true" : null; - /// - /// component class - /// - protected string? ClassString => CssBuilder.Default() + private string? ClassString => CssBuilder.Default("bb-tooltip") .AddClassFromAttributes(AdditionalAttributes) .Build(); @@ -50,7 +47,7 @@ public partial class Tooltip : ITooltip public string? Selector { get; set; } /// - /// 获得/设置 显示内容 + /// /// [Parameter] public string? Title { get; set; } @@ -62,7 +59,7 @@ public partial class Tooltip : ITooltip public Func>? GetTitleCallback { get; set; } /// - /// 获得/设置 显示文字是否为 Html 默认为 false + /// /// [Parameter] public bool IsHtml { get; set; } @@ -74,7 +71,7 @@ public partial class Tooltip : ITooltip public bool Sanitize { get; set; } = true; /// - /// 获得/设置 位置 默认为 Placement.Top + /// /// [Parameter] public Placement Placement { get; set; } = Placement.Top; @@ -92,14 +89,13 @@ public partial class Tooltip : ITooltip public string? Offset { get; set; } /// - /// 获得/设置 自定义样式 默认 null + /// /// - /// 由 data-bs-custom-class 实现 [Parameter] public string? CustomClass { get; set; } /// - /// 获得/设置 触发方式 可组合 click focus hover 默认为 focus hover + /// /// [Parameter] public string? Trigger { get; set; } @@ -110,11 +106,6 @@ public partial class Tooltip : ITooltip [Parameter] public RenderFragment? ChildContent { get; set; } - /// - /// 获得 CustomClass 字符串 - /// - protected virtual string? CustomClassString => CustomClass; - /// /// /// @@ -156,4 +147,25 @@ public void SetParameters(string title, Placement placement = Placement.Auto, st if (!string.IsNullOrEmpty(offset)) Offset = offset; StateHasChanged(); } + + /// + /// 显示 Tooltip 弹窗方法 + /// + /// 延时指定毫秒后显示弹窗 默认 null 不延时 + /// + public Task Show(int? delay = null) => InvokeVoidAsync("show", Id, delay); + + /// + /// 关闭 Tooltip 弹窗方法 + /// + /// 延时指定毫秒后关闭弹窗 默认 null 不延时 + /// + public Task Hide(int? delay = null) => InvokeVoidAsync("hide", Id, delay); + + /// + /// 切换 Tooltip 弹窗当前状态方法 + /// + /// 延时指定毫秒后切换弹窗方法 默认 null 不延时 + /// + public Task Toggle(int? delay = null) => InvokeVoidAsync("toggle", Id, delay); } diff --git a/src/BootstrapBlazor/Components/Tooltip/Tooltip.razor.js b/src/BootstrapBlazor/Components/Tooltip/Tooltip.razor.js index b7591813b75..47f34c789c6 100644 --- a/src/BootstrapBlazor/Components/Tooltip/Tooltip.razor.js +++ b/src/BootstrapBlazor/Components/Tooltip/Tooltip.razor.js @@ -20,6 +20,33 @@ export function init(id) { } } +export function show(id, delay) { + const tip = Data.get(id) + const { tooltip } = tip; + + setTimeout(() => { + tooltip.show(); + }, delay || 0); +} + +export function hide(id, delay) { + const tip = Data.get(id) + const { tooltip } = tip; + + setTimeout(() => { + tooltip.hide(); + }, delay || 0); +} + +export function toggle(id, delay) { + const tip = Data.get(id) + const { tooltip } = tip; + + setTimeout(() => { + tooltip.toggle(); + }, delay || 0); +} + export function dispose(id) { const tip = Data.get(id) Data.remove(id) diff --git a/src/BootstrapBlazor/Extensions/ObjectExtensions.cs b/src/BootstrapBlazor/Extensions/ObjectExtensions.cs index d631022682c..a644209337e 100644 --- a/src/BootstrapBlazor/Extensions/ObjectExtensions.cs +++ b/src/BootstrapBlazor/Extensions/ObjectExtensions.cs @@ -246,6 +246,11 @@ internal static void Clone(this TModel source, TModel item) /// An instance of the specified type with initialized properties. public static TItem? CreateInstance(bool isAutoInitializeModelProperty = false) { + if(typeof(TItem).IsInterface) + { + return default; + } + var instance = Activator.CreateInstance(); if (isAutoInitializeModelProperty) { @@ -277,7 +282,9 @@ private static void EnsureInitialized(this object? instance, bool isAutoInitiali } // Reflection performance needs to be optimized here - foreach (var propertyInfo in instance.GetType().GetProperties().Where(p => p.PropertyType.IsClass && p.PropertyType != typeof(string))) + foreach (var propertyInfo in instance.GetType().GetProperties().Where(p => p.PropertyType.IsClass + && p.PropertyType != typeof(string) + && p.CanWrite)) { var type = propertyInfo.PropertyType; var value = propertyInfo.GetValue(instance, null); diff --git a/src/BootstrapBlazor/wwwroot/modules/upload.js b/src/BootstrapBlazor/wwwroot/modules/upload.js index a8e445fb1d0..59070ddab2b 100644 --- a/src/BootstrapBlazor/wwwroot/modules/upload.js +++ b/src/BootstrapBlazor/wwwroot/modules/upload.js @@ -1,6 +1,5 @@ import Data from "./data.js" import EventHandler from "./event-handler.js" -import { readFileAsync } from "./utility.js" export function init(id) { const el = document.getElementById(id) @@ -91,17 +90,14 @@ export function preview(previewerId, index) { } } -export async function getPreviewUrl(id, fileName) { +export function getPreviewUrl(id, fileName) { let url = ''; const upload = Data.get(id); const { files } = upload; if (files) { const file = [...files].find(v => v.name === fileName); if (file) { - const data = await readFileAsync(file); - if (data) { - url = URL.createObjectURL(data); - } + url = URL.createObjectURL(file); } } return url; diff --git a/src/BootstrapBlazor/wwwroot/modules/utility.js b/src/BootstrapBlazor/wwwroot/modules/utility.js index e01a94c81cc..025e6eda04a 100644 --- a/src/BootstrapBlazor/wwwroot/modules/utility.js +++ b/src/BootstrapBlazor/wwwroot/modules/utility.js @@ -670,7 +670,7 @@ export function isMobile() { const hashCode = str => { let hash = 0; for (let i = 0; i < str.length; i++) { - const char = str.charCodeAt(1); + const char = str.charCodeAt(i); hash = (hash << 5) - hash + char; hash |= 0; } diff --git a/test/UnitTest/Components/LayoutTest.cs b/test/UnitTest/Components/LayoutTest.cs index e778aa92225..72682e31423 100644 --- a/test/UnitTest/Components/LayoutTest.cs +++ b/test/UnitTest/Components/LayoutTest.cs @@ -673,6 +673,23 @@ public void CollapseBarTemplate_Ok() Assert.Contains("CollapseBarTemplate-Content", cut.Markup); } + + [Fact] + public void SkipAuthenticate_Ok() + { + // 未授权,通过控制 SkipAuthenticate 属性跳过授权 + var cut = Context.RenderComponent>>(pb => + { + pb.Add(a => a.Value, Task.FromResult(new AuthenticationState(new ClaimsPrincipal()))); + pb.AddChildContent(pb => + { + pb.Add(a => a.SkipAuthenticate, true); + pb.Add(a => a.Main, builder => builder.AddContent(0, "Main")); + }); + }); + cut.MarkupMatches("
          Main
          "); + } + private static RenderFragment CreateHeader(string? content = "Header") => builder => builder.AddContent(0, content); private static RenderFragment CreateFooter(string? content = "Footer") => builder => builder.AddContent(0, content); diff --git a/test/UnitTest/Components/TooltipTest.cs b/test/UnitTest/Components/TooltipTest.cs index d62219e83a7..3b346362122 100644 --- a/test/UnitTest/Components/TooltipTest.cs +++ b/test/UnitTest/Components/TooltipTest.cs @@ -176,4 +176,17 @@ public void FallbackPlacements_Ok() }); cut.Contains("data-bs-fallbackPlacements=\"top,left\""); } + + [Fact] + public async Task Toggle_Ok() + { + var cut = Context.RenderComponent(pb => + { + pb.Add(a => a.Title, "test_tooltip"); + pb.Add(a => a.Trigger, "manual"); + }); + await cut.InvokeAsync(() => cut.Instance.Show()); + await cut.InvokeAsync(() => cut.Instance.Hide()); + await cut.InvokeAsync(() => cut.Instance.Toggle()); + } } diff --git a/test/UnitTest/Extensions/LambadaExtensionsTest.cs b/test/UnitTest/Extensions/LambadaExtensionsTest.cs index ab24e5c429e..8eef3dd73d9 100644 --- a/test/UnitTest/Extensions/LambadaExtensionsTest.cs +++ b/test/UnitTest/Extensions/LambadaExtensionsTest.cs @@ -244,6 +244,10 @@ public void GetExpression_Contains() var filter = new FilterKeyValueAction() { FieldKey = "Name", FieldValue = "test", FilterAction = FilterAction.Contains }; var invoker = filter.GetFilterLambda().Compile(); Assert.True(invoker.Invoke(new Foo() { Name = "1test1" })); + Assert.False(invoker.Invoke(new Foo() { Name = "1Test1" })); + Assert.False(invoker.Invoke(new Foo() { Name = "1Test123" })); + Assert.False(invoker.Invoke(new Foo() { Name = "Test" })); + Assert.False(invoker.Invoke(new Foo() { Name = "Test2" })); } [Fact] diff --git a/test/UnitTest/Extensions/ObjectExtensionsTest.cs b/test/UnitTest/Extensions/ObjectExtensionsTest.cs index 90beda9ef93..73ffd82e807 100644 --- a/test/UnitTest/Extensions/ObjectExtensionsTest.cs +++ b/test/UnitTest/Extensions/ObjectExtensionsTest.cs @@ -315,6 +315,19 @@ public void CreateInstance_Ok() var instance = ObjectExtensions.CreateInstance(false); Assert.NotNull(instance); Assert.Null(instance.Test); + + // 接口类型不报错 + Assert.Null(ObjectExtensions.CreateInstance(true)); + + var bar = ObjectExtensions.CreateInstance(true); + Assert.NotNull(bar); + Assert.NotNull(bar.Foo); + Assert.Null(bar.Bar); + } + + private interface MockInterface + { + string? Name { get; set; } } private class MockComplexObject @@ -324,6 +337,15 @@ private class MockComplexObject public (string Name, int Count)[]? Test { get; set; } } + private class MockObject + { + public string? Name { get; set; } + + public Foo? Foo { get; set; } + + public Foo? Bar { get; } + } + private class MockStatic { private static int _test;