diff --git a/src/BootstrapBlazor.Shared/App.razor b/src/BootstrapBlazor.Shared/App.razor index 159bc74611a39d7236906c67a51013943ebb1774..b69aac41fa402b73d025ec5493f571e0d27b810f 100644 --- a/src/BootstrapBlazor.Shared/App.razor +++ b/src/BootstrapBlazor.Shared/App.razor @@ -8,3 +8,5 @@ + + diff --git a/src/BootstrapBlazor.Shared/App.razor.cs b/src/BootstrapBlazor.Shared/App.razor.cs index 4ffca260fb6d501d070dff6bdcde92ec5f89bc4a..b6fe4a10d606e47cf1e13d271500bbe3c7802e88 100644 --- a/src/BootstrapBlazor.Shared/App.razor.cs +++ b/src/BootstrapBlazor.Shared/App.razor.cs @@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Components; using Microsoft.JSInterop; +using System.Threading.Tasks; namespace BootstrapBlazor.Shared { @@ -22,13 +23,13 @@ namespace BootstrapBlazor.Shared /// /// /// - protected override void OnAfterRender(bool firstRender) + protected override async Task OnAfterRenderAsync(bool firstRender) { - base.OnAfterRender(firstRender); + await base.OnAfterRenderAsync(firstRender); if (firstRender && JSRuntime != null) { - JSRuntime.InvokeVoidAsync("$.loading"); + await JSRuntime.InvokeVoidAsync("$.loading"); } } } diff --git a/src/BootstrapBlazor.Shared/Locales/en-US.json b/src/BootstrapBlazor.Shared/Locales/en-US.json index 50bcac2bb873781ebe503a57946839423024aaba..c12530ea7d22b54e4df30586d55e92425a84faef 100644 --- a/src/BootstrapBlazor.Shared/Locales/en-US.json +++ b/src/BootstrapBlazor.Shared/Locales/en-US.json @@ -1,4 +1,7 @@ { + "BootstrapBlazor.Shared.App": { + "Title": "Bootstrap of Blazor" + }, "BootstrapBlazor.Shared.Pages.Components.Block": { "Title": "", "SubTitle": "Demo" diff --git a/src/BootstrapBlazor.Shared/Locales/zh-CN.json b/src/BootstrapBlazor.Shared/Locales/zh-CN.json index 00deeb6399c6552cb0ecc2cac4b2ea235d818913..30f9ba985086229ad103aca72e1dc21e5987d5d8 100644 --- a/src/BootstrapBlazor.Shared/Locales/zh-CN.json +++ b/src/BootstrapBlazor.Shared/Locales/zh-CN.json @@ -1,4 +1,7 @@ { + "BootstrapBlazor.Shared.App": { + "Title": "Bootstrap Blazor - 企业级 UI 组件库" + }, "BootstrapBlazor.Shared.Pages.Components.Block": { "Title": "未设置", "SubTitle": "示例" diff --git a/src/BootstrapBlazor.Shared/Pages/Index.razor b/src/BootstrapBlazor.Shared/Pages/Index.razor index 0d8c623017e004398202eba9bffb015809c32654..b7375f490a902dd010338e5bc338252811e99ac4 100644 --- a/src/BootstrapBlazor.Shared/Pages/Index.razor +++ b/src/BootstrapBlazor.Shared/Pages/Index.razor @@ -1,8 +1,10 @@ -@using Microsoft.Extensions.Localization -@layout HomeLayout +@layout HomeLayout @page "/" @page "/index" @page "/home" +@inject IStringLocalizer Localizer + +
diff --git a/src/BootstrapBlazor.Shared/Pages/Introduction.razor b/src/BootstrapBlazor.Shared/Pages/Introduction.razor index 66e0936232b739c52327fa95a096a2552d98f45d..4399234f8537f8bf8c9d1791d17cfdff34020e6a 100644 --- a/src/BootstrapBlazor.Shared/Pages/Introduction.razor +++ b/src/BootstrapBlazor.Shared/Pages/Introduction.razor @@ -1,6 +1,7 @@ @page "/docs" @page "/introduction" @inject IOptions WebsiteOption +

简介

BootstrapBlazor 是一套基于 Bootstrap 和 Blazor 的企业级组件库,可以认为是 Bootstrap 项目的 Blazor 版实现。

diff --git a/src/BootstrapBlazor.Shared/Pages/Samples/Titles.razor b/src/BootstrapBlazor.Shared/Pages/Samples/Titles.razor new file mode 100644 index 0000000000000000000000000000000000000000..666c4eb039a806034f4c00930bdf4271928a6585 --- /dev/null +++ b/src/BootstrapBlazor.Shared/Pages/Samples/Titles.razor @@ -0,0 +1,50 @@ +@page "/titles" + +

Title 网站标题

+ +

用于设置网页页面标题

+ + + +

+ Title 组件设置 Text 后为普通组件使用,未设置 Text 属性时供服务调用 +

+
+

设置 Text 属性

+
@@page "/titles"
+<Title Text="我是标题" />
+<p>测试网页<p/>
+
+
+ + +

+ 特别注意: +

使用此种方法时请在 App 或者 MainLayout 或者 Page 中添加 未设置 Text 属性Title 组件
+

+
[Inject]
+[NotNull]
+private TitleService? TitleService { get; set; }
+
+protected override async Task OnAfterRenderAsync(bool firstRender)
+{
+    await base.OnAfterRenderAsync(firstRender);
+
+    await TitleService.SetTitle("我是标题");
+}
+
+
+ + +

+ 特别注意: +

使用此种方法时请在 App 或者 MainLayout 或者 Page 中添加 未设置 Text 属性Title 组件
+

+
protected override async Task OnAfterRenderAsync(bool firstRender)
+{
+    await base.OnAfterRenderAsync(firstRender);
+
+    await TitleService.SetWebSiteTitle("我是标题");
+}
+
+
diff --git a/src/BootstrapBlazor.Shared/Pages/Samples/Titles.razor.cs b/src/BootstrapBlazor.Shared/Pages/Samples/Titles.razor.cs new file mode 100644 index 0000000000000000000000000000000000000000..a841cba7183e5dfa00fcc05f2f96efd358fff218 --- /dev/null +++ b/src/BootstrapBlazor.Shared/Pages/Samples/Titles.razor.cs @@ -0,0 +1,19 @@ +// Copyright (c) Argo Zhang (argo@163.com). All rights reserved. +// 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 System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BootstrapBlazor.Shared.Pages +{ + /// + /// Title 网站标题示例代码 + /// + public partial class Title + { + } +} diff --git a/src/BootstrapBlazor.Shared/Shared/NavMenu.razor.cs b/src/BootstrapBlazor.Shared/Shared/NavMenu.razor.cs index d485a7f76b1893ec1da1d4cc962a87d8a7526332..3fe1bc1ec31b7bb5039a8f39beafaf54c5143460 100644 --- a/src/BootstrapBlazor.Shared/Shared/NavMenu.razor.cs +++ b/src/BootstrapBlazor.Shared/Shared/NavMenu.razor.cs @@ -4,7 +4,10 @@ using BootstrapBlazor.Components; using BootstrapBlazor.Shared.Pages.Components; +using Microsoft.AspNetCore.Components; +using Microsoft.Extensions.Localization; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading.Tasks; @@ -17,6 +20,10 @@ namespace BootstrapBlazor.Shared.Shared { private bool collapseNavMenu = true; + [Inject] + [NotNull] + private IStringLocalizer? Localizer { get; set; } + private string? NavMenuCssClass => CssBuilder.Default("sidebar-content") .AddClass("collapse", collapseNavMenu) .Build(); @@ -40,6 +47,11 @@ namespace BootstrapBlazor.Shared.Shared ToggleNavMenu(); StateHasChanged(); } + + if (!string.IsNullOrEmpty(item.Text)) + { + await TitleService.SetWebSiteTitle($"{item.Text} - {Localizer["Title"]}"); + } } private void ToggleNavMenu() @@ -372,6 +384,7 @@ namespace BootstrapBlazor.Shared.Shared }); item.AddItem(new DemoMenuItem() { + IsNew = true, Text = "网站标题 Title", Url = "titles" }); diff --git a/src/BootstrapBlazor/Components/Title/Title.cs b/src/BootstrapBlazor/Components/Title/Title.cs new file mode 100644 index 0000000000000000000000000000000000000000..8f21b9c238f61306c5cc647c882bb6c430bae4f0 --- /dev/null +++ b/src/BootstrapBlazor/Components/Title/Title.cs @@ -0,0 +1,75 @@ +// Copyright (c) Argo Zhang (argo@163.com). All rights reserved. +// 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 Microsoft.AspNetCore.Components; +using Microsoft.JSInterop; +using System.Diagnostics.CodeAnalysis; +using System.Threading.Tasks; + +namespace BootstrapBlazor.Components +{ + /// + /// Title 组件 + /// + public class Title : BootstrapComponentBase + { + [Inject] + [NotNull] + private TitleService? TitleService { get; set; } + + /// + /// 获得/设置 当前页标题文字 + /// + [Parameter] + public string? Text { get; set; } + + /// + /// OnInitialized 方法 + /// + protected override void OnInitialized() + { + base.OnInitialized(); + + if (string.IsNullOrEmpty(Text)) + { + TitleService.Register(this, SetTitle); + } + } + + /// + /// OnAfterRenderAsync 方法 + /// + /// + /// + protected override async Task OnAfterRenderAsync(bool firstRender) + { + await base.OnAfterRenderAsync(firstRender); + + if (!string.IsNullOrEmpty(Text)) + { + await SetTitle(Text); + } + } + + /// + /// 设置网站 Title 方法 + /// + /// + /// + private ValueTask SetTitle(string title) => JSRuntime.InvokeVoidAsync(identifier: "$.bb_setTitle", title); + + /// + /// Dispose 方法 + /// + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + + if (disposing) + { + TitleService.UnRegister(this); + } + } + } +} diff --git a/src/BootstrapBlazor/Components/Title/Title.cs.js b/src/BootstrapBlazor/Components/Title/Title.cs.js new file mode 100644 index 0000000000000000000000000000000000000000..5e25367b3c6498607b164d7b7bd4c349fba42cea --- /dev/null +++ b/src/BootstrapBlazor/Components/Title/Title.cs.js @@ -0,0 +1,7 @@ +(function ($) { + $.extend({ + bb_setTitle: function (title) { + document.title = title; + } + }); +})(jQuery); diff --git a/src/BootstrapBlazor/Components/Title/Title.cs.min.js b/src/BootstrapBlazor/Components/Title/Title.cs.min.js new file mode 100644 index 0000000000000000000000000000000000000000..92099bfaeec6800514a3bf022bc05b1713228e3e --- /dev/null +++ b/src/BootstrapBlazor/Components/Title/Title.cs.min.js @@ -0,0 +1 @@ +(function(n){n.extend({bb_setTitle:function(n){document.title=n}})})(jQuery); \ No newline at end of file diff --git a/src/BootstrapBlazor/Components/Title/TitleService.cs b/src/BootstrapBlazor/Components/Title/TitleService.cs new file mode 100644 index 0000000000000000000000000000000000000000..90a0404189396dfcac38a091fbde7842591b3da5 --- /dev/null +++ b/src/BootstrapBlazor/Components/Title/TitleService.cs @@ -0,0 +1,65 @@ +// Copyright (c) Argo Zhang (argo@163.com). All rights reserved. +// 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 Microsoft.AspNetCore.Components; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace BootstrapBlazor.Components +{ + /// + /// Title 服务 + /// + public class TitleService + { + /// + /// 获得 回调委托缓存集合 + /// + private List<(IComponent Key, Func Callback)> Cache { get; set; } = new(); + + /// + /// 设置当前页面 Title 方法 + /// + /// + /// + public async ValueTask SetTitle(string title) + { + var cb = Cache.FirstOrDefault().Callback; + if (cb != null) + { + await cb.Invoke(title); + } + } + + /// + /// 注册服务 + /// + /// + /// + internal void Register(IComponent key, Func callback) => Cache.Add((key, callback)); + + /// + /// 注销事件 + /// + internal void UnRegister(IComponent key) + { + var item = Cache.FirstOrDefault(i => i.Key == key); + if (item.Key != null) Cache.Remove(item); + } + + /// + /// 设置网站标题方法 + /// + /// + /// + public static ValueTask SetWebSiteTitle(string title) + { + var titleSvr = ServiceProviderHelper.ServiceProvider.GetRequiredService(); + return titleSvr.SetTitle(title); + } + } +} diff --git a/src/BootstrapBlazor/Extensions/BootstrapBlazorServiceCollectionExtensions.cs b/src/BootstrapBlazor/Extensions/BootstrapBlazorServiceCollectionExtensions.cs index bef35c448f256691f632810992a0bb58c59c21fe..3f80daa783dced03db513783951f3c89d8b85f66 100644 --- a/src/BootstrapBlazor/Extensions/BootstrapBlazorServiceCollectionExtensions.cs +++ b/src/BootstrapBlazor/Extensions/BootstrapBlazorServiceCollectionExtensions.cs @@ -35,6 +35,7 @@ namespace Microsoft.Extensions.DependencyInjection services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped(); + services.TryAddScoped(); services.TryAddSingleton, ConfigureOptions>(); services.Configure(options => { diff --git a/src/BootstrapBlazor/bundleconfig.json b/src/BootstrapBlazor/bundleconfig.json index e106a0d8fe52cace446a268831137e5eedeec9c4..f24808ce7b9f6ae70152328d53cc1d09d2dc732e 100644 --- a/src/BootstrapBlazor/bundleconfig.json +++ b/src/BootstrapBlazor/bundleconfig.json @@ -500,6 +500,12 @@ "Components/Table/Filter/TableFilter.razor.cs.js" ] }, + { + "outputFileName": "Components/Title/Title.cs.min.js", + "inputFiles": [ + "Components/Title/Title.cs.js" + ] + }, { "outputFileName": "Components/Toast/Toast.razor.cs.min.js", "inputFiles": [ @@ -548,7 +554,7 @@ "Components/ColorPicker/ColorPicker.razor.cs.min.js", "Components/Console/Console.razor.cs.min.js", "Components/DateTimePicker/DateTimePicker.razor.cs.min.js", - "Components/DateTimePicker/TimePickerBody.razor.cs.js", + "Components/DateTimePicker/TimePickerBody.razor.cs.min.js", "Components/DateTimeRange/DateTimeRange.razor.cs.min.js", "Components/Drawer/Drawer.razor.cs.min.js", "Components/Editor/Editor.razor.cs.min.js", @@ -568,6 +574,7 @@ "Components/Tab/Tab.razor.cs.min.js", "Components/Table/Table.razor.cs.min.js", "Components/Table/Filter/TableFilter.razor.cs.min.js", + "Components/Title/Title.cs.min.js", "Components/Toast/Toast.razor.cs.min.js", "Components/Tooltip/Tooltip.cs.min.js", "Components/Tree/Tree.razor.cs.min.js", diff --git a/src/BootstrapBlazor/wwwroot/js/bootstrap.blazor.bundle.min.js b/src/BootstrapBlazor/wwwroot/js/bootstrap.blazor.bundle.min.js index b278aa258c195b1b7e8dddf5a45c3d8eba8daea5..d4546b3b8d7f5cbd95e71787c709d032e0b5fa5c 100644 --- a/src/BootstrapBlazor/wwwroot/js/bootstrap.blazor.bundle.min.js +++ b/src/BootstrapBlazor/wwwroot/js/bootstrap.blazor.bundle.min.js @@ -52,28 +52,7 @@ x();"bottom"===a.start?(c.css({top:b.outerHeight()-c.outerHeight()}),n(0,!0)):"t (function(n){n.extend({bb_color_picker:function(t,i,r){n(t).colorpicker().on("change",function(n){i.invokeMethodAsync(r,n.value)})}})})(jQuery); (function(n){n.extend({bb_console:function(t){var u=n(t),i=u.find('[data-scroll="auto"]'),r;i.length>0&&(r=i.find(".console-window"),i.scrollTop(r.height()))}})})(jQuery); (function(n){n.extend({bb_datetimePicker:function(t,i){var r=n(t),f=r.attr("data-placement")||"auto",u=r.find(".datetime-picker-input");if(i)u.popover(i);else{u.popover({toggle:"datetime-picker",placement:f,template:'