96 changed files with 6460 additions and 7214 deletions
@ -1,25 +1,55 @@ |
|||
# Lsw.Abp.AntDesignUI |
|||
|
|||
**An Abp Blazor Theme based [Ant-Design-Blazor](https://github.com/ant-design-blazor/ant-design-blazor) !** |
|||
ABP Blazor UI theme and module set based on [Ant Design Blazor](https://github.com/ant-design-blazor/ant-design-blazor). |
|||
|
|||
[](https://www.nuget.org/packages/Lsw.Abp.AntDesignUI/) |
|||
[](https://www.nuget.org/packages/Lsw.Abp.AntDesignUI/) |
|||
|
|||
## Samples |
|||
## Features |
|||
|
|||
Lsw.Abp.AntDesignUI provides an Ant Design Pro-style application shell for ABP Blazor applications. |
|||
|
|||
- Refactored application layout with side and top navigation. |
|||
- Responsive sidebar behavior for desktop and mobile screens. |
|||
- Light, menu-dark, and real-dark visual styles. |
|||
- Runtime controls for content width, fixed header, fixed sidebar, split menus, and page regions. |
|||
- Floating theme settings panel for authenticated users. |
|||
- Admin-managed theme setting availability through ABP Setting Management. |
|||
- AntDesign UI implementations for common ABP management modules. |
|||
|
|||
## Theme Settings |
|||
|
|||
Authenticated users can open the settings panel from the right side of the application and change the layout without restarting the app. |
|||
|
|||
The panel includes: |
|||
|
|||
* [Blazor WebApp(Auto mode) sample](/samples/WebApp/) |
|||
* [Blazor WebApp(Server mode) sample](/samples/WebAppBlazorServer/) |
|||
* [Blazor WebApp(WebAssembly mode) sample](/samples/WebAppBlazorWebAssembly/) |
|||
- Page style selection. |
|||
- Navigation mode selection. |
|||
- Content width and fixed layout switches. |
|||
- Header, footer, menu, and menu header visibility. |
|||
- Weak color mode. |
|||
|
|||
Administrators can choose which groups are visible from `Administration -> Settings -> Theme settings management`. |
|||
|
|||
## Screenshots |
|||
|
|||
 |
|||
|
|||
 |
|||
|
|||
## Samples |
|||
|
|||
 |
|||
 |
|||
- [WebApp (Auto mode)](./samples/WebApp/) |
|||
- [WebApp Blazor Server](./samples/WebAppBlazorServer/) |
|||
- [WebApp Blazor WebAssembly](./samples/WebAppBlazorWebAssembly/) |
|||
|
|||
## Quick Start |
|||
Sample login: |
|||
|
|||
* [Change the theme of the ABP project to AntBlazorTheme for Blazor WebApp.](./README.WebApp.md) |
|||
* [Change the theme of the ABP project to AntBlazorTheme for Blazor Server.](./README.BlazorServer.md) |
|||
* [Change the theme of the ABP project to AntBlazorTheme for Blazor WebAssembly.](./README.BlazorWebAssembly.md) |
|||
- Username: `admin` |
|||
- Password: `1q2w3E*` |
|||
|
|||
## Road map |
|||
## Usage Guides |
|||
|
|||
Updating... |
|||
- [Use AntDesign theme in ABP Blazor WebApp](./README.WebApp.md) |
|||
- [Use AntDesign theme in ABP Blazor Server](./README.BlazorServer.md) |
|||
- [Use AntDesign theme in ABP Blazor WebAssembly](./README.BlazorWebAssembly.md) |
|||
|
|||
|
After Width: | Height: | Size: 22 KiB |
|
After Width: | Height: | Size: 53 KiB |
@ -1,40 +1,96 @@ |
|||
using System; |
|||
using System.Security.AccessControl; |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using AntDesign; |
|||
using AntDesign.Core.Helpers.MemberPath; |
|||
using Microsoft.AspNetCore.Components; |
|||
using Microsoft.Extensions.Options; |
|||
using Lsw.Abp.AntDesignThemeManagement; |
|||
using Lsw.Abp.AntDesignThemeManagement.Dtos; |
|||
using Lsw.Abp.AntDesignThemeManagement.Settings; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.Settings; |
|||
using Volo.Abp.Users; |
|||
|
|||
namespace Lsw.Abp.AspnetCore.Components.Web.AntDesignTheme.Settings; |
|||
|
|||
public class AntDesignSettingsProvider : IAntDesignSettingsProvider, IScopedDependency |
|||
{ |
|||
//TODO use SettingProvider instead of AbpAntDesignThemeOptions
|
|||
// [Inject]
|
|||
// protected ISettingProvider SettingProvider { get; set; }
|
|||
|
|||
[Inject] |
|||
public IOptions<AbpAntDesignThemeOptions> Options { get; set; } |
|||
|
|||
public delegate Task AntDesignSettingChangedHandler(); |
|||
|
|||
public event AntDesignSettingChangedHandler SettingChanged; |
|||
|
|||
public Task<MenuPlacement> GetMenuPlacementAsync() |
|||
protected IAntDesignThemePreferenceAppService ThemePreferenceAppService { get; } |
|||
|
|||
protected ICurrentUser CurrentUser { get; } |
|||
|
|||
public AntDesignSettingsProvider( |
|||
IAntDesignThemePreferenceAppService themePreferenceAppService, |
|||
ICurrentUser currentUser) |
|||
{ |
|||
ThemePreferenceAppService = themePreferenceAppService; |
|||
CurrentUser = currentUser; |
|||
} |
|||
|
|||
public event Func<Task>? SettingChanged; |
|||
|
|||
public async Task<AntDesignThemePreferenceDto> GetPreferenceAsync() |
|||
{ |
|||
if (!CurrentUser.IsAuthenticated) |
|||
{ |
|||
return BuildDefaultPreference(); |
|||
} |
|||
|
|||
try |
|||
{ |
|||
return await ThemePreferenceAppService.GetAsync(); |
|||
} |
|||
catch |
|||
{ |
|||
return BuildDefaultPreference(); |
|||
} |
|||
} |
|||
|
|||
public async Task<MenuPlacement> GetMenuPlacementAsync() |
|||
{ |
|||
var setting = await GetPreferenceAsync(); |
|||
|
|||
return setting.NavigationMode == NavigationModes.Top |
|||
? MenuPlacement.Top |
|||
: MenuPlacement.Left; |
|||
} |
|||
|
|||
public async Task<MenuTheme> GetMenuThemeAsync() |
|||
{ |
|||
var setting = await GetPreferenceAsync(); |
|||
|
|||
return setting.ThemeStyle == ThemeStyles.Light |
|||
? MenuTheme.Light |
|||
: MenuTheme.Dark; |
|||
} |
|||
|
|||
public async Task ApplyPreferenceAsync(UpdateAntDesignThemePreferenceDto input) |
|||
{ |
|||
return Task.FromResult(Options.Value.Menu.Placement); |
|||
await ThemePreferenceAppService.UpdateAsync(input); |
|||
await TriggerSettingChangedAsync(); |
|||
} |
|||
|
|||
public Task<MenuTheme> GetMenuThemeAsync() |
|||
public Task TriggerSettingChangedAsync() |
|||
{ |
|||
return Task.FromResult(Options.Value.Menu.Theme); |
|||
return SettingChanged?.Invoke() ?? Task.CompletedTask; |
|||
} |
|||
|
|||
public Task TriggerSettingChanged() |
|||
protected virtual AntDesignThemePreferenceDto BuildDefaultPreference() |
|||
{ |
|||
return SettingChanged?.Invoke(); |
|||
return new AntDesignThemePreferenceDto |
|||
{ |
|||
ThemeSettingsEnabled = AntDesignThemeSettingDefaults.EnableThemeSettings, |
|||
PageStyleSettingEnabled = AntDesignThemeSettingDefaults.EnablePageStyleSetting, |
|||
NavigationModeSettingEnabled = AntDesignThemeSettingDefaults.EnableNavigationModeSetting, |
|||
RegionalSettingsEnabled = AntDesignThemeSettingDefaults.EnableRegionalSettings, |
|||
OtherSettingsEnabled = AntDesignThemeSettingDefaults.EnableOtherSettings, |
|||
ThemeStyle = AntDesignThemeSettingDefaults.ThemeStyle, |
|||
NavigationMode = AntDesignThemeSettingDefaults.NavigationMode, |
|||
ContentWidth = AntDesignThemeSettingDefaults.ContentWidth, |
|||
FixedHeader = AntDesignThemeSettingDefaults.FixedHeader, |
|||
FixSiderbar = AntDesignThemeSettingDefaults.FixSiderbar, |
|||
SplitMenus = AntDesignThemeSettingDefaults.SplitMenus, |
|||
ShowHeader = AntDesignThemeSettingDefaults.ShowHeader, |
|||
ShowFooter = AntDesignThemeSettingDefaults.ShowFooter, |
|||
ShowMenu = AntDesignThemeSettingDefaults.ShowMenu, |
|||
ShowMenuHeader = AntDesignThemeSettingDefaults.ShowMenuHeader, |
|||
ColorWeak = AntDesignThemeSettingDefaults.ColorWeak |
|||
}; |
|||
} |
|||
} |
|||
|
|||
@ -1,16 +1,21 @@ |
|||
using System; |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using AntDesign; |
|||
using Lsw.Abp.AntDesignThemeManagement.Dtos; |
|||
|
|||
namespace Lsw.Abp.AspnetCore.Components.Web.AntDesignTheme.Settings; |
|||
|
|||
public interface IAntDesignSettingsProvider |
|||
{ |
|||
Task<AntDesignThemePreferenceDto> GetPreferenceAsync(); |
|||
|
|||
Task<MenuPlacement> GetMenuPlacementAsync(); |
|||
|
|||
Task<MenuTheme> GetMenuThemeAsync(); |
|||
|
|||
Task TriggerSettingChanged(); |
|||
|
|||
public event AntDesignSettingsProvider.AntDesignSettingChangedHandler SettingChanged; |
|||
Task ApplyPreferenceAsync(UpdateAntDesignThemePreferenceDto input); |
|||
|
|||
Task TriggerSettingChangedAsync(); |
|||
|
|||
event Func<Task>? SettingChanged; |
|||
} |
|||
|
|||
@ -1,10 +1,16 @@ |
|||
@using Lsw.Abp.AspnetCore.Components.Web.AntDesignTheme.Settings |
|||
<Menu Mode="@(Placement == MenuPlacement.Left? MenuMode.Inline: MenuMode.Horizontal)" Theme="@Theme" InlineCollapsed="@Collapsed"> |
|||
@using Lsw.Abp.AspnetCore.Components.Web.AntDesignTheme.Settings |
|||
@inherits Volo.Abp.AspNetCore.Components.AbpComponentBase |
|||
<Menu @key="MenuRenderKey" |
|||
Mode="@MenuMode" |
|||
Theme="@Theme" |
|||
InlineCollapsed="@InlineCollapsedValue" |
|||
AutoCloseDropdown="true" |
|||
TriggerSubMenuAction="@TriggerSubMenuAction"> |
|||
@if (Menu != null) |
|||
{ |
|||
foreach (var menu in Menu.Items) |
|||
{ |
|||
<MainMenuItem Menu="@menu"></MainMenuItem> |
|||
<MainMenuItem Menu="@menu" OnItemClick="OnMenuItemClickedAsync"></MainMenuItem> |
|||
} |
|||
} |
|||
</Menu> |
|||
|
|||
@ -1,4 +1,5 @@ |
|||
@foreach (var render in ToolbarItemRenders) |
|||
@inherits Volo.Abp.AspNetCore.Components.AbpComponentBase |
|||
@foreach (var render in ToolbarItemRenders) |
|||
{ |
|||
@render |
|||
} |
|||
|
|||
@ -0,0 +1,147 @@ |
|||
@using Lsw.Abp.AntDesignThemeManagement.Settings |
|||
@inherits Volo.Abp.AspNetCore.Components.AbpComponentBase |
|||
@if (IsVisible) |
|||
{ |
|||
<div id="@HostId" |
|||
class="@FloatingButtonClass" |
|||
role="button" |
|||
tabindex="0" |
|||
aria-label="@ThemeL["ThemeSettings"]" |
|||
@onclick="TogglePanel" |
|||
@onkeydown="OnFabKeyDownAsync"> |
|||
<span class="lsw-theme-setting-fab-handle lsw-theme-setting-fab-icon" aria-hidden="true"> |
|||
<Icon Type="setting" Theme="IconThemeType.Outline" /> |
|||
</span> |
|||
</div> |
|||
|
|||
<div class="lsw-theme-setting-mask @(PanelVisible ? "is-open" : string.Empty)" @onclick="ClosePanel"></div> |
|||
|
|||
<aside class="lsw-theme-setting-panel @(PanelVisible ? "is-open" : string.Empty)" tabindex="0"> |
|||
<div class="lsw-theme-setting-panel-header"> |
|||
<h3>@ThemeL["ThemeSettings"]</h3> |
|||
<button type="button" class="lsw-theme-setting-close" @onclick="ClosePanel" aria-label="Close"> |
|||
<Icon Type="close" Theme="IconThemeType.Outline" /> |
|||
</button> |
|||
</div> |
|||
|
|||
<div class="lsw-theme-setting-panel-body"> |
|||
@if (Preference.PageStyleSettingEnabled) |
|||
{ |
|||
<section class="lsw-theme-setting-section"> |
|||
<h4>@ThemeL["PageStyleSetting"]</h4> |
|||
<div class="lsw-theme-style-grid"> |
|||
@foreach (var style in ThemeStyleOptions) |
|||
{ |
|||
<button |
|||
type="button" |
|||
class="lsw-theme-style-option @(Preference.ThemeStyle == style.Value ? "active" : string.Empty)" |
|||
@onclick="() => SetThemeStyleAsync(style.Value)"> |
|||
<span class="lsw-theme-style-preview @style.CssClass"></span> |
|||
<span class="lsw-theme-style-label">@ThemeL[style.LocalizationKey]</span> |
|||
</button> |
|||
} |
|||
</div> |
|||
</section> |
|||
} |
|||
|
|||
@if (Preference.NavigationModeSettingEnabled) |
|||
{ |
|||
<section class="lsw-theme-setting-section"> |
|||
<h4>@ThemeL["NavigationMode"]</h4> |
|||
<div class="lsw-theme-style-grid lsw-theme-style-grid-nav"> |
|||
@foreach (var nav in NavigationOptions) |
|||
{ |
|||
<button |
|||
type="button" |
|||
class="lsw-theme-style-option @(Preference.NavigationMode == nav.Value ? "active" : string.Empty)" |
|||
@onclick="() => SetNavigationModeAsync(nav.Value)"> |
|||
<span class="lsw-theme-style-preview @nav.CssClass"></span> |
|||
<span class="lsw-theme-style-label">@ThemeL[nav.LocalizationKey]</span> |
|||
</button> |
|||
} |
|||
</div> |
|||
</section> |
|||
|
|||
<section class="lsw-theme-setting-section"> |
|||
<div class="lsw-theme-setting-item"> |
|||
<span>@ThemeL["ContentWidth"]</span> |
|||
<select class="lsw-theme-setting-select" value="@Preference.ContentWidth" @onchange="OnContentWidthChangedAsync"> |
|||
<option value="@ContentWidths.Fluid">@ThemeL["Fluid"]</option> |
|||
<option value="@ContentWidths.Fixed">@ThemeL["Fixed"]</option> |
|||
</select> |
|||
</div> |
|||
<div class="lsw-theme-setting-item"> |
|||
<span>@ThemeL["FixedHeader"]</span> |
|||
<label class="lsw-theme-setting-switch"> |
|||
<input type="checkbox" checked="@Preference.FixedHeader" @onchange="OnFixedHeaderChangedAsync" /> |
|||
<span></span> |
|||
</label> |
|||
</div> |
|||
<div class="lsw-theme-setting-item"> |
|||
<span>@ThemeL["WorksOnSideMenuLayout"]</span> |
|||
<label class="lsw-theme-setting-switch"> |
|||
<input type="checkbox" checked="@Preference.FixSiderbar" @onchange="OnFixedSiderbarChangedAsync" /> |
|||
<span></span> |
|||
</label> |
|||
</div> |
|||
<div class="lsw-theme-setting-item"> |
|||
<span>@ThemeL["SplitMenus"]</span> |
|||
<label class="lsw-theme-setting-switch"> |
|||
<input type="checkbox" checked="@Preference.SplitMenus" @onchange="OnSplitMenusChangedAsync" /> |
|||
<span></span> |
|||
</label> |
|||
</div> |
|||
</section> |
|||
} |
|||
|
|||
@if (Preference.RegionalSettingsEnabled) |
|||
{ |
|||
<section class="lsw-theme-setting-section"> |
|||
<h4>@ThemeL["RegionalSettings"]</h4> |
|||
<div class="lsw-theme-setting-item"> |
|||
<span>@ThemeL["Header"]</span> |
|||
<label class="lsw-theme-setting-switch"> |
|||
<input type="checkbox" checked="@Preference.ShowHeader" @onchange="OnShowHeaderChangedAsync" /> |
|||
<span></span> |
|||
</label> |
|||
</div> |
|||
<div class="lsw-theme-setting-item"> |
|||
<span>@ThemeL["Footer"]</span> |
|||
<label class="lsw-theme-setting-switch"> |
|||
<input type="checkbox" checked="@Preference.ShowFooter" @onchange="OnShowFooterChangedAsync" /> |
|||
<span></span> |
|||
</label> |
|||
</div> |
|||
<div class="lsw-theme-setting-item"> |
|||
<span>@ThemeL["Menu"]</span> |
|||
<label class="lsw-theme-setting-switch"> |
|||
<input type="checkbox" checked="@Preference.ShowMenu" @onchange="OnShowMenuChangedAsync" /> |
|||
<span></span> |
|||
</label> |
|||
</div> |
|||
<div class="lsw-theme-setting-item"> |
|||
<span>@ThemeL["MenuHeader"]</span> |
|||
<label class="lsw-theme-setting-switch"> |
|||
<input type="checkbox" checked="@Preference.ShowMenuHeader" @onchange="OnShowMenuHeaderChangedAsync" /> |
|||
<span></span> |
|||
</label> |
|||
</div> |
|||
</section> |
|||
} |
|||
|
|||
@if (Preference.OtherSettingsEnabled) |
|||
{ |
|||
<section class="lsw-theme-setting-section"> |
|||
<h4>@ThemeL["OtherSettings"]</h4> |
|||
<div class="lsw-theme-setting-item"> |
|||
<span>@ThemeL["WeakMode"]</span> |
|||
<label class="lsw-theme-setting-switch"> |
|||
<input type="checkbox" checked="@Preference.ColorWeak" @onchange="OnColorWeakChangedAsync" /> |
|||
<span></span> |
|||
</label> |
|||
</div> |
|||
</section> |
|||
} |
|||
</div> |
|||
</aside> |
|||
} |
|||
@ -0,0 +1,281 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading.Tasks; |
|||
using Lsw.Abp.AntDesignThemeManagement.Dtos; |
|||
using Lsw.Abp.AntDesignThemeManagement.Localization; |
|||
using Lsw.Abp.AntDesignThemeManagement.Settings; |
|||
using Lsw.Abp.AspnetCore.Components.Web.AntDesignTheme.Settings; |
|||
using Microsoft.AspNetCore.Components; |
|||
using Microsoft.AspNetCore.Components.Web; |
|||
using Microsoft.Extensions.Localization; |
|||
using Microsoft.JSInterop; |
|||
using Volo.Abp.Users; |
|||
|
|||
namespace Lsw.Abp.AspnetCore.Components.Web.AntDesignTheme.Themes.AntDesignTheme; |
|||
|
|||
public partial class ThemeSettingPanel : IAsyncDisposable |
|||
{ |
|||
[Inject] |
|||
protected IStringLocalizer<AntDesignThemeManagementResource> ThemeL { get; set; } = default!; |
|||
|
|||
[Inject] |
|||
protected IAntDesignSettingsProvider AntDesignSettingsProvider { get; set; } = default!; |
|||
|
|||
[Inject] |
|||
protected IJSRuntime JsRuntime { get; set; } = default!; |
|||
|
|||
protected bool PanelVisible { get; set; } |
|||
|
|||
protected bool IsVisible { get; set; } |
|||
|
|||
protected AntDesignThemePreferenceDto Preference { get; set; } = new(); |
|||
|
|||
protected string HostId { get; } = $"lsw-theme-settings-fab-{Guid.NewGuid():N}"; |
|||
|
|||
protected string FloatingButtonClass => |
|||
$"lsw-theme-setting-fab {(PanelVisible ? "lsw-theme-setting-fab-active" : string.Empty)}".Trim(); |
|||
|
|||
private bool _isSubscribedToSettingChanged; |
|||
private bool _jsInitialized; |
|||
|
|||
protected IReadOnlyList<OptionItem> ThemeStyleOptions { get; } = new[] |
|||
{ |
|||
new OptionItem(ThemeStyles.Light, "Light", "lsw-theme-preview-light"), |
|||
new OptionItem(ThemeStyles.Dark, "Dark", "lsw-theme-preview-dark"), |
|||
new OptionItem(ThemeStyles.RealDark, "RealDark", "lsw-theme-preview-real-dark") |
|||
}; |
|||
|
|||
protected IReadOnlyList<OptionItem> NavigationOptions { get; } = new[] |
|||
{ |
|||
new OptionItem(NavigationModes.Side, "Side", "lsw-theme-preview-nav-side"), |
|||
new OptionItem(NavigationModes.Top, "Top", "lsw-theme-preview-nav-top") |
|||
}; |
|||
|
|||
protected override async Task OnInitializedAsync() |
|||
{ |
|||
try |
|||
{ |
|||
if (!CurrentUser.IsAuthenticated) |
|||
{ |
|||
IsVisible = false; |
|||
return; |
|||
} |
|||
|
|||
AntDesignSettingsProvider.SettingChanged += OnSettingChangedAsync; |
|||
_isSubscribedToSettingChanged = true; |
|||
|
|||
Preference = await AntDesignSettingsProvider.GetPreferenceAsync(); |
|||
IsVisible = Preference.ThemeSettingsEnabled; |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
await HandleErrorAsync(ex); |
|||
IsVisible = false; |
|||
} |
|||
} |
|||
|
|||
protected override async Task OnAfterRenderAsync(bool firstRender) |
|||
{ |
|||
if (!IsVisible || _jsInitialized) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
try |
|||
{ |
|||
await JsRuntime.InvokeVoidAsync("lswAntDesignThemeSettings.initialize", HostId); |
|||
_jsInitialized = true; |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
await HandleErrorAsync(ex); |
|||
} |
|||
} |
|||
|
|||
protected virtual Task TogglePanel() |
|||
{ |
|||
PanelVisible = !PanelVisible; |
|||
return Task.CompletedTask; |
|||
} |
|||
|
|||
protected virtual Task OnFabKeyDownAsync(KeyboardEventArgs args) |
|||
{ |
|||
if (args.Key is "Enter" or " ") |
|||
{ |
|||
return TogglePanel(); |
|||
} |
|||
|
|||
return Task.CompletedTask; |
|||
} |
|||
|
|||
protected virtual Task ClosePanel() |
|||
{ |
|||
PanelVisible = false; |
|||
return Task.CompletedTask; |
|||
} |
|||
|
|||
protected virtual async Task SetThemeStyleAsync(string style) |
|||
{ |
|||
Preference.ThemeStyle = style; |
|||
await SaveAsync(); |
|||
} |
|||
|
|||
protected virtual async Task SetNavigationModeAsync(string mode) |
|||
{ |
|||
var previousMode = Preference.NavigationMode; |
|||
Preference.NavigationMode = mode; |
|||
await SaveAsync(); |
|||
|
|||
if (Preference.NavigationMode != mode) |
|||
{ |
|||
Preference.NavigationMode = previousMode; |
|||
} |
|||
} |
|||
|
|||
protected virtual async Task OnContentWidthChangedAsync(ChangeEventArgs args) |
|||
{ |
|||
Preference.ContentWidth = args.Value?.ToString() ?? ContentWidths.Fluid; |
|||
await SaveAsync(); |
|||
} |
|||
|
|||
protected virtual async Task OnFixedHeaderChangedAsync(ChangeEventArgs args) |
|||
{ |
|||
Preference.FixedHeader = ReadBoolean(args); |
|||
await SaveAsync(); |
|||
} |
|||
|
|||
protected virtual async Task OnFixedSiderbarChangedAsync(ChangeEventArgs args) |
|||
{ |
|||
Preference.FixSiderbar = ReadBoolean(args); |
|||
await SaveAsync(); |
|||
} |
|||
|
|||
protected virtual async Task OnSplitMenusChangedAsync(ChangeEventArgs args) |
|||
{ |
|||
Preference.SplitMenus = ReadBoolean(args); |
|||
await SaveAsync(); |
|||
} |
|||
|
|||
protected virtual async Task OnShowHeaderChangedAsync(ChangeEventArgs args) |
|||
{ |
|||
Preference.ShowHeader = ReadBoolean(args); |
|||
await SaveAsync(); |
|||
} |
|||
|
|||
protected virtual async Task OnShowFooterChangedAsync(ChangeEventArgs args) |
|||
{ |
|||
Preference.ShowFooter = ReadBoolean(args); |
|||
await SaveAsync(); |
|||
} |
|||
|
|||
protected virtual async Task OnShowMenuChangedAsync(ChangeEventArgs args) |
|||
{ |
|||
Preference.ShowMenu = ReadBoolean(args); |
|||
await SaveAsync(); |
|||
} |
|||
|
|||
protected virtual async Task OnShowMenuHeaderChangedAsync(ChangeEventArgs args) |
|||
{ |
|||
Preference.ShowMenuHeader = ReadBoolean(args); |
|||
await SaveAsync(); |
|||
} |
|||
|
|||
protected virtual async Task OnColorWeakChangedAsync(ChangeEventArgs args) |
|||
{ |
|||
Preference.ColorWeak = ReadBoolean(args); |
|||
await SaveAsync(); |
|||
} |
|||
|
|||
protected virtual bool ReadBoolean(ChangeEventArgs args) |
|||
{ |
|||
return args.Value switch |
|||
{ |
|||
bool boolValue => boolValue, |
|||
string stringValue when bool.TryParse(stringValue, out var result) => result, |
|||
_ => false |
|||
}; |
|||
} |
|||
|
|||
protected virtual async Task SaveAsync() |
|||
{ |
|||
try |
|||
{ |
|||
await AntDesignSettingsProvider.ApplyPreferenceAsync(new UpdateAntDesignThemePreferenceDto |
|||
{ |
|||
ThemeStyle = Preference.ThemeStyle, |
|||
NavigationMode = Preference.NavigationMode, |
|||
ContentWidth = Preference.ContentWidth, |
|||
FixedHeader = Preference.FixedHeader, |
|||
FixSiderbar = Preference.FixSiderbar, |
|||
SplitMenus = Preference.SplitMenus, |
|||
ShowHeader = Preference.ShowHeader, |
|||
ShowFooter = Preference.ShowFooter, |
|||
ShowMenu = Preference.ShowMenu, |
|||
ShowMenuHeader = Preference.ShowMenuHeader, |
|||
ColorWeak = Preference.ColorWeak |
|||
}); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
await HandleErrorAsync(ex); |
|||
try |
|||
{ |
|||
Preference = await AntDesignSettingsProvider.GetPreferenceAsync(); |
|||
} |
|||
catch |
|||
{ |
|||
// Intentionally ignored to keep the panel responsive even if reloading fails.
|
|||
} |
|||
|
|||
await InvokeAsync(StateHasChanged); |
|||
} |
|||
} |
|||
|
|||
protected virtual async Task OnSettingChangedAsync() |
|||
{ |
|||
try |
|||
{ |
|||
if (!CurrentUser.IsAuthenticated) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
Preference = await AntDesignSettingsProvider.GetPreferenceAsync(); |
|||
IsVisible = Preference.ThemeSettingsEnabled; |
|||
if (!IsVisible) |
|||
{ |
|||
PanelVisible = false; |
|||
} |
|||
else |
|||
{ |
|||
_jsInitialized = false; |
|||
} |
|||
|
|||
await InvokeAsync(StateHasChanged); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
await HandleErrorAsync(ex); |
|||
} |
|||
} |
|||
|
|||
public async ValueTask DisposeAsync() |
|||
{ |
|||
if (_isSubscribedToSettingChanged) |
|||
{ |
|||
AntDesignSettingsProvider.SettingChanged -= OnSettingChangedAsync; |
|||
_isSubscribedToSettingChanged = false; |
|||
} |
|||
|
|||
try |
|||
{ |
|||
await JsRuntime.InvokeVoidAsync("lswAntDesignThemeSettings.dispose", HostId); |
|||
} |
|||
catch |
|||
{ |
|||
// Ignore disposal errors when the JS runtime is no longer available.
|
|||
} |
|||
} |
|||
|
|||
protected record OptionItem(string Value, string LocalizationKey, string CssClass); |
|||
} |
|||
File diff suppressed because it is too large
@ -0,0 +1,200 @@ |
|||
(function () { |
|||
if (!window.lswAntDesignThemeSettings) { |
|||
window.lswAntDesignThemeSettings = {}; |
|||
} |
|||
|
|||
const registry = {}; |
|||
const themeClasses = [ |
|||
"lsw-pro-theme", |
|||
"lsw-pro-theme-light", |
|||
"lsw-pro-theme-dark", |
|||
"lsw-pro-theme-real-dark", |
|||
"colorWeak" |
|||
]; |
|||
|
|||
function clamp(value, min, max) { |
|||
return Math.min(Math.max(value, min), max); |
|||
} |
|||
|
|||
function clearFadeTimer(state) { |
|||
if (state.fadeTimer) { |
|||
window.clearTimeout(state.fadeTimer); |
|||
state.fadeTimer = null; |
|||
} |
|||
} |
|||
|
|||
function scheduleFade(state) { |
|||
clearFadeTimer(state); |
|||
state.fadeTimer = window.setTimeout(() => { |
|||
if (!state.host.classList.contains("lsw-theme-setting-fab-active")) { |
|||
state.host.classList.add("lsw-theme-setting-fab-faded"); |
|||
} |
|||
}, 1200); |
|||
} |
|||
|
|||
function snapToRight(state, keepTop) { |
|||
const host = state.host; |
|||
const rect = host.getBoundingClientRect(); |
|||
const width = rect.width || 48; |
|||
const top = keepTop ? rect.top : window.innerHeight * 0.6; |
|||
const clampedTop = clamp(top, 24, window.innerHeight - rect.height - 24); |
|||
|
|||
host.style.left = ""; |
|||
host.style.right = `${Math.round(-width / 2)}px`; |
|||
host.style.top = `${Math.round(clampedTop)}px`; |
|||
} |
|||
|
|||
function initialize(id) { |
|||
const host = document.getElementById(id); |
|||
if (!host || registry[id]) { |
|||
return; |
|||
} |
|||
|
|||
const handle = host; |
|||
|
|||
const state = { |
|||
host: host, |
|||
pointerId: null, |
|||
startX: 0, |
|||
startY: 0, |
|||
originX: 0, |
|||
originY: 0, |
|||
moved: false, |
|||
fadeTimer: null |
|||
}; |
|||
|
|||
const onPointerDown = function (evt) { |
|||
state.pointerId = evt.pointerId; |
|||
state.startX = evt.clientX; |
|||
state.startY = evt.clientY; |
|||
const rect = host.getBoundingClientRect(); |
|||
state.originX = rect.left; |
|||
state.originY = rect.top; |
|||
state.moved = false; |
|||
host.classList.remove("lsw-theme-setting-fab-faded"); |
|||
clearFadeTimer(state); |
|||
handle.setPointerCapture(evt.pointerId); |
|||
}; |
|||
|
|||
const onPointerMove = function (evt) { |
|||
if (state.pointerId !== evt.pointerId) { |
|||
return; |
|||
} |
|||
|
|||
const dx = evt.clientX - state.startX; |
|||
const dy = evt.clientY - state.startY; |
|||
if (Math.abs(dx) > 3 || Math.abs(dy) > 3) { |
|||
state.moved = true; |
|||
} |
|||
|
|||
if (!state.moved) { |
|||
return; |
|||
} |
|||
|
|||
const rect = host.getBoundingClientRect(); |
|||
const nextLeft = clamp(state.originX + dx, 0, window.innerWidth - rect.width); |
|||
const nextTop = clamp(state.originY + dy, 24, window.innerHeight - rect.height - 24); |
|||
|
|||
host.style.right = ""; |
|||
host.style.left = `${Math.round(nextLeft)}px`; |
|||
host.style.top = `${Math.round(nextTop)}px`; |
|||
}; |
|||
|
|||
const onPointerUp = function (evt) { |
|||
if (state.pointerId !== evt.pointerId) { |
|||
return; |
|||
} |
|||
|
|||
state.pointerId = null; |
|||
if (state.moved) { |
|||
snapToRight(state, true); |
|||
} |
|||
scheduleFade(state); |
|||
}; |
|||
|
|||
const onMouseEnter = function () { |
|||
host.classList.remove("lsw-theme-setting-fab-faded"); |
|||
clearFadeTimer(state); |
|||
}; |
|||
|
|||
const onMouseLeave = function () { |
|||
scheduleFade(state); |
|||
}; |
|||
|
|||
const onWindowBlur = function () { |
|||
if (!host.classList.contains("lsw-theme-setting-fab-active")) { |
|||
host.classList.add("lsw-theme-setting-fab-faded"); |
|||
} |
|||
}; |
|||
|
|||
const onWindowFocus = function () { |
|||
host.classList.remove("lsw-theme-setting-fab-faded"); |
|||
}; |
|||
|
|||
handle.addEventListener("pointerdown", onPointerDown); |
|||
handle.addEventListener("pointermove", onPointerMove); |
|||
handle.addEventListener("pointerup", onPointerUp); |
|||
handle.addEventListener("pointercancel", onPointerUp); |
|||
host.addEventListener("mouseenter", onMouseEnter); |
|||
host.addEventListener("mouseleave", onMouseLeave); |
|||
window.addEventListener("blur", onWindowBlur); |
|||
window.addEventListener("focus", onWindowFocus); |
|||
|
|||
registry[id] = { |
|||
state: state, |
|||
onPointerDown: onPointerDown, |
|||
onPointerMove: onPointerMove, |
|||
onPointerUp: onPointerUp, |
|||
onMouseEnter: onMouseEnter, |
|||
onMouseLeave: onMouseLeave, |
|||
onWindowBlur: onWindowBlur, |
|||
onWindowFocus: onWindowFocus |
|||
}; |
|||
|
|||
snapToRight(state, false); |
|||
scheduleFade(state); |
|||
} |
|||
|
|||
function dispose(id) { |
|||
const entry = registry[id]; |
|||
if (!entry) { |
|||
return; |
|||
} |
|||
|
|||
const host = entry.state.host; |
|||
const handle = host; |
|||
|
|||
if (handle) { |
|||
handle.removeEventListener("pointerdown", entry.onPointerDown); |
|||
handle.removeEventListener("pointermove", entry.onPointerMove); |
|||
handle.removeEventListener("pointerup", entry.onPointerUp); |
|||
handle.removeEventListener("pointercancel", entry.onPointerUp); |
|||
} |
|||
|
|||
host.removeEventListener("mouseenter", entry.onMouseEnter); |
|||
host.removeEventListener("mouseleave", entry.onMouseLeave); |
|||
window.removeEventListener("blur", entry.onWindowBlur); |
|||
window.removeEventListener("focus", entry.onWindowFocus); |
|||
clearFadeTimer(entry.state); |
|||
delete registry[id]; |
|||
} |
|||
|
|||
function applyThemeClass(themeClass) { |
|||
const body = document.body; |
|||
if (!body) { |
|||
return; |
|||
} |
|||
|
|||
themeClasses.forEach((name) => body.classList.remove(name)); |
|||
if (themeClass && typeof themeClass === "string") { |
|||
themeClass.split(" ") |
|||
.map((item) => item.trim()) |
|||
.filter((item) => item.length > 0) |
|||
.forEach((item) => body.classList.add(item)); |
|||
} |
|||
} |
|||
|
|||
window.lswAntDesignThemeSettings.initialize = initialize; |
|||
window.lswAntDesignThemeSettings.dispose = dispose; |
|||
window.lswAntDesignThemeSettings.applyThemeClass = applyThemeClass; |
|||
})(); |
|||
@ -0,0 +1,31 @@ |
|||
using Lsw.Abp.AntDesignThemeManagement.Localization; |
|||
using Lsw.Abp.AntDesignThemeManagement.Settings; |
|||
using Volo.Abp.Authorization; |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.SettingManagement; |
|||
using Volo.Abp.VirtualFileSystem; |
|||
|
|||
namespace Lsw.Abp.AntDesignThemeManagement; |
|||
|
|||
[DependsOn( |
|||
typeof(AbpAuthorizationModule), |
|||
typeof(AbpSettingManagementApplicationContractsModule) |
|||
)] |
|||
public class AbpAntDesignThemeManagementApplicationContractsModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<AbpVirtualFileSystemOptions>(options => |
|||
{ |
|||
options.FileSets.AddEmbedded<AbpAntDesignThemeManagementApplicationContractsModule>(); |
|||
}); |
|||
|
|||
Configure<AbpLocalizationOptions>(options => |
|||
{ |
|||
options.Resources |
|||
.Add<AntDesignThemeManagementResource>("en") |
|||
.AddVirtualJson("/Localization/Resources/AntDesignThemeManagement"); |
|||
}); |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
namespace Lsw.Abp.AntDesignThemeManagement.Dtos; |
|||
|
|||
public class AntDesignThemePreferenceDto |
|||
{ |
|||
public bool ThemeSettingsEnabled { get; set; } |
|||
public bool PageStyleSettingEnabled { get; set; } |
|||
public bool NavigationModeSettingEnabled { get; set; } |
|||
public bool RegionalSettingsEnabled { get; set; } |
|||
public bool OtherSettingsEnabled { get; set; } |
|||
|
|||
public string ThemeStyle { get; set; } = string.Empty; |
|||
public string NavigationMode { get; set; } = string.Empty; |
|||
public string ContentWidth { get; set; } = string.Empty; |
|||
public bool FixedHeader { get; set; } |
|||
public bool FixSiderbar { get; set; } |
|||
public bool SplitMenus { get; set; } |
|||
public bool ShowHeader { get; set; } |
|||
public bool ShowFooter { get; set; } |
|||
public bool ShowMenu { get; set; } |
|||
public bool ShowMenuHeader { get; set; } |
|||
public bool ColorWeak { get; set; } |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
namespace Lsw.Abp.AntDesignThemeManagement.Dtos; |
|||
|
|||
public class UpdateAntDesignThemePreferenceDto |
|||
{ |
|||
public string ThemeStyle { get; set; } = string.Empty; |
|||
public string NavigationMode { get; set; } = string.Empty; |
|||
public string ContentWidth { get; set; } = string.Empty; |
|||
public bool FixedHeader { get; set; } |
|||
public bool FixSiderbar { get; set; } |
|||
public bool SplitMenus { get; set; } |
|||
public bool ShowHeader { get; set; } |
|||
public bool ShowFooter { get; set; } |
|||
public bool ShowMenu { get; set; } |
|||
public bool ShowMenuHeader { get; set; } |
|||
public bool ColorWeak { get; set; } |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
namespace Lsw.Abp.AntDesignThemeManagement.Dtos; |
|||
|
|||
public class UpdateAntDesignThemeSettingsAvailabilityDto |
|||
{ |
|||
public bool ThemeSettingsEnabled { get; set; } |
|||
public bool PageStyleSettingEnabled { get; set; } |
|||
public bool NavigationModeSettingEnabled { get; set; } |
|||
public bool RegionalSettingsEnabled { get; set; } |
|||
public bool OtherSettingsEnabled { get; set; } |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -0,0 +1,16 @@ |
|||
using System.Threading.Tasks; |
|||
using Lsw.Abp.AntDesignThemeManagement.Dtos; |
|||
using Volo.Abp.Application.Services; |
|||
|
|||
namespace Lsw.Abp.AntDesignThemeManagement; |
|||
|
|||
public interface IAntDesignThemePreferenceAppService : IApplicationService |
|||
{ |
|||
Task<AntDesignThemePreferenceDto> GetAsync(); |
|||
|
|||
Task UpdateAsync(UpdateAntDesignThemePreferenceDto input); |
|||
|
|||
Task UpdateThemeSettingsAvailabilityAsync(UpdateAntDesignThemeSettingsAvailabilityDto input); |
|||
|
|||
Task SetThemeSettingsEnabledAsync(bool isEnabled); |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
using Volo.Abp.Localization; |
|||
|
|||
namespace Lsw.Abp.AntDesignThemeManagement.Localization; |
|||
|
|||
[LocalizationResourceName("AntDesignThemeManagement")] |
|||
public class AntDesignThemeManagementResource |
|||
{ |
|||
} |
|||
@ -0,0 +1,48 @@ |
|||
{ |
|||
"culture": "en", |
|||
"texts": { |
|||
"Permission:AntDesignThemeManagement": "Ant Design theme management", |
|||
"Permission:AntDesignThemeManagement.Settings": "Manage theme settings switch", |
|||
"ThemeSettings": "Theme settings", |
|||
"PageStyleSetting": "Page style setting", |
|||
"NavigationMode": "Navigation mode", |
|||
"ContentWidth": "Content width", |
|||
"FixedHeader": "Fixed header", |
|||
"WorksOnSideMenuLayout": "Works on side menu layout", |
|||
"SplitMenus": "Split menus", |
|||
"RegionalSettings": "Regional settings", |
|||
"Header": "Header", |
|||
"Footer": "Footer", |
|||
"Menu": "Menu", |
|||
"MenuHeader": "Menu header", |
|||
"OtherSettings": "Other settings", |
|||
"WeakMode": "Weak mode", |
|||
"Light": "Light", |
|||
"Dark": "Menu dark", |
|||
"RealDark": "Real dark", |
|||
"Side": "Side", |
|||
"Top": "Top", |
|||
"Mix": "Mix", |
|||
"Fluid": "Fluid", |
|||
"Fixed": "Fixed", |
|||
"Submit": "Submit", |
|||
"Menu:ThemeSettingsManagement": "Theme settings management", |
|||
"SavedSuccessfully": "Saved successfully.", |
|||
"Settings:EnableThemeSettings": "Enable theme settings", |
|||
"Settings:PageStyleSetting": "Page style setting", |
|||
"Settings:NavigationModeSetting": "Navigation mode", |
|||
"Settings:RegionalSettings": "Regional settings", |
|||
"Settings:OtherSettings": "Other settings", |
|||
"Settings:ThemeStyle": "Theme style", |
|||
"Settings:NavigationMode": "Navigation mode", |
|||
"Settings:ContentWidth": "Content width", |
|||
"Settings:FixedHeader": "Fixed header", |
|||
"Settings:FixSiderbar": "Fix siderbar", |
|||
"Settings:SplitMenus": "Split menus", |
|||
"Settings:ShowHeader": "Show header", |
|||
"Settings:ShowFooter": "Show footer", |
|||
"Settings:ShowMenu": "Show menu", |
|||
"Settings:ShowMenuHeader": "Show menu header", |
|||
"Settings:ColorWeak": "Weak mode" |
|||
} |
|||
} |
|||
@ -0,0 +1,48 @@ |
|||
{ |
|||
"culture": "zh-Hans", |
|||
"texts": { |
|||
"Permission:AntDesignThemeManagement": "AntDesign 主题管理", |
|||
"Permission:AntDesignThemeManagement.Settings": "管理主题设置开关", |
|||
"ThemeSettings": "主题设置", |
|||
"PageStyleSetting": "页面风格设置", |
|||
"NavigationMode": "导航模式", |
|||
"ContentWidth": "内容宽度", |
|||
"FixedHeader": "固定 Header", |
|||
"WorksOnSideMenuLayout": "侧边菜单布局生效", |
|||
"SplitMenus": "分割菜单", |
|||
"RegionalSettings": "区域设置", |
|||
"Header": "页头", |
|||
"Footer": "页脚", |
|||
"Menu": "菜单", |
|||
"MenuHeader": "菜单头", |
|||
"OtherSettings": "其他设置", |
|||
"WeakMode": "色弱模式", |
|||
"Light": "明亮", |
|||
"Dark": "菜单深色", |
|||
"RealDark": "深邃暗色", |
|||
"Side": "侧边", |
|||
"Top": "顶部", |
|||
"Mix": "混合", |
|||
"Fluid": "自适应", |
|||
"Fixed": "定宽", |
|||
"Submit": "提交", |
|||
"Menu:ThemeSettingsManagement": "主题设置管理", |
|||
"SavedSuccessfully": "保存成功。", |
|||
"Settings:EnableThemeSettings": "启用主题设置", |
|||
"Settings:PageStyleSetting": "页面风格设置", |
|||
"Settings:NavigationModeSetting": "导航模式", |
|||
"Settings:RegionalSettings": "区域设置", |
|||
"Settings:OtherSettings": "其他设置", |
|||
"Settings:ThemeStyle": "主题风格", |
|||
"Settings:NavigationMode": "导航模式", |
|||
"Settings:ContentWidth": "内容宽度", |
|||
"Settings:FixedHeader": "固定 Header", |
|||
"Settings:FixSiderbar": "固定侧边栏", |
|||
"Settings:SplitMenus": "分割菜单", |
|||
"Settings:ShowHeader": "显示页头", |
|||
"Settings:ShowFooter": "显示页脚", |
|||
"Settings:ShowMenu": "显示菜单", |
|||
"Settings:ShowMenuHeader": "显示菜单头", |
|||
"Settings:ColorWeak": "色弱模式" |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\common.props" /> |
|||
<Import Project="..\..\..\configureawait.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net10.0</TargetFramework> |
|||
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Microsoft.Extensions.FileProviders.Embedded" /> |
|||
<PackageReference Include="Volo.Abp.Authorization" /> |
|||
<PackageReference Include="Volo.Abp.SettingManagement.Application.Contracts" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<EmbeddedResource Include="Localization\Resources\AntDesignThemeManagement\*.json" /> |
|||
<Content Remove="Localization\Resources\AntDesignThemeManagement\*.json" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,26 @@ |
|||
using Lsw.Abp.AntDesignThemeManagement.Localization; |
|||
using Volo.Abp.Authorization.Permissions; |
|||
using Volo.Abp.Localization; |
|||
|
|||
namespace Lsw.Abp.AntDesignThemeManagement.Permissions; |
|||
|
|||
public class AntDesignThemeManagementPermissionDefinitionProvider : PermissionDefinitionProvider |
|||
{ |
|||
public override void Define(IPermissionDefinitionContext context) |
|||
{ |
|||
var group = context.AddGroup( |
|||
AntDesignThemeManagementPermissions.GroupName, |
|||
L("Permission:AntDesignThemeManagement") |
|||
); |
|||
|
|||
group.AddPermission( |
|||
AntDesignThemeManagementPermissions.Settings, |
|||
L("Permission:AntDesignThemeManagement.Settings") |
|||
); |
|||
} |
|||
|
|||
private static LocalizableString L(string name) |
|||
{ |
|||
return LocalizableString.Create<AntDesignThemeManagementResource>(name); |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
namespace Lsw.Abp.AntDesignThemeManagement.Permissions; |
|||
|
|||
public static class AntDesignThemeManagementPermissions |
|||
{ |
|||
public const string GroupName = "Lsw.AntDesignThemeManagement"; |
|||
|
|||
public const string Settings = GroupName + ".Settings"; |
|||
} |
|||
@ -0,0 +1,115 @@ |
|||
using Lsw.Abp.AntDesignThemeManagement.Localization; |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.Settings; |
|||
|
|||
namespace Lsw.Abp.AntDesignThemeManagement.Settings; |
|||
|
|||
public class AntDesignThemeManagementSettingDefinitionProvider : SettingDefinitionProvider |
|||
{ |
|||
public override void Define(ISettingDefinitionContext context) |
|||
{ |
|||
context.Add( |
|||
new SettingDefinition( |
|||
AntDesignThemeManagementSettingNames.EnableThemeSettings, |
|||
AntDesignThemeSettingDefaults.EnableThemeSettings.ToString().ToLowerInvariant(), |
|||
L("Settings:EnableThemeSettings"), |
|||
isVisibleToClients: true |
|||
), |
|||
new SettingDefinition( |
|||
AntDesignThemeManagementSettingNames.EnablePageStyleSetting, |
|||
AntDesignThemeSettingDefaults.EnablePageStyleSetting.ToString().ToLowerInvariant(), |
|||
L("Settings:PageStyleSetting"), |
|||
isVisibleToClients: true |
|||
), |
|||
new SettingDefinition( |
|||
AntDesignThemeManagementSettingNames.EnableNavigationModeSetting, |
|||
AntDesignThemeSettingDefaults.EnableNavigationModeSetting.ToString().ToLowerInvariant(), |
|||
L("Settings:NavigationModeSetting"), |
|||
isVisibleToClients: true |
|||
), |
|||
new SettingDefinition( |
|||
AntDesignThemeManagementSettingNames.EnableRegionalSettings, |
|||
AntDesignThemeSettingDefaults.EnableRegionalSettings.ToString().ToLowerInvariant(), |
|||
L("Settings:RegionalSettings"), |
|||
isVisibleToClients: true |
|||
), |
|||
new SettingDefinition( |
|||
AntDesignThemeManagementSettingNames.EnableOtherSettings, |
|||
AntDesignThemeSettingDefaults.EnableOtherSettings.ToString().ToLowerInvariant(), |
|||
L("Settings:OtherSettings"), |
|||
isVisibleToClients: true |
|||
), |
|||
new SettingDefinition( |
|||
AntDesignThemeManagementSettingNames.ThemeStyle, |
|||
AntDesignThemeSettingDefaults.ThemeStyle, |
|||
L("Settings:ThemeStyle"), |
|||
isVisibleToClients: true |
|||
), |
|||
new SettingDefinition( |
|||
AntDesignThemeManagementSettingNames.NavigationMode, |
|||
AntDesignThemeSettingDefaults.NavigationMode, |
|||
L("Settings:NavigationMode"), |
|||
isVisibleToClients: true |
|||
), |
|||
new SettingDefinition( |
|||
AntDesignThemeManagementSettingNames.ContentWidth, |
|||
AntDesignThemeSettingDefaults.ContentWidth, |
|||
L("Settings:ContentWidth"), |
|||
isVisibleToClients: true |
|||
), |
|||
new SettingDefinition( |
|||
AntDesignThemeManagementSettingNames.FixedHeader, |
|||
AntDesignThemeSettingDefaults.FixedHeader.ToString().ToLowerInvariant(), |
|||
L("Settings:FixedHeader"), |
|||
isVisibleToClients: true |
|||
), |
|||
new SettingDefinition( |
|||
AntDesignThemeManagementSettingNames.FixSiderbar, |
|||
AntDesignThemeSettingDefaults.FixSiderbar.ToString().ToLowerInvariant(), |
|||
L("Settings:FixSiderbar"), |
|||
isVisibleToClients: true |
|||
), |
|||
new SettingDefinition( |
|||
AntDesignThemeManagementSettingNames.SplitMenus, |
|||
AntDesignThemeSettingDefaults.SplitMenus.ToString().ToLowerInvariant(), |
|||
L("Settings:SplitMenus"), |
|||
isVisibleToClients: true |
|||
), |
|||
new SettingDefinition( |
|||
AntDesignThemeManagementSettingNames.ShowHeader, |
|||
AntDesignThemeSettingDefaults.ShowHeader.ToString().ToLowerInvariant(), |
|||
L("Settings:ShowHeader"), |
|||
isVisibleToClients: true |
|||
), |
|||
new SettingDefinition( |
|||
AntDesignThemeManagementSettingNames.ShowFooter, |
|||
AntDesignThemeSettingDefaults.ShowFooter.ToString().ToLowerInvariant(), |
|||
L("Settings:ShowFooter"), |
|||
isVisibleToClients: true |
|||
), |
|||
new SettingDefinition( |
|||
AntDesignThemeManagementSettingNames.ShowMenu, |
|||
AntDesignThemeSettingDefaults.ShowMenu.ToString().ToLowerInvariant(), |
|||
L("Settings:ShowMenu"), |
|||
isVisibleToClients: true |
|||
), |
|||
new SettingDefinition( |
|||
AntDesignThemeManagementSettingNames.ShowMenuHeader, |
|||
AntDesignThemeSettingDefaults.ShowMenuHeader.ToString().ToLowerInvariant(), |
|||
L("Settings:ShowMenuHeader"), |
|||
isVisibleToClients: true |
|||
), |
|||
new SettingDefinition( |
|||
AntDesignThemeManagementSettingNames.ColorWeak, |
|||
AntDesignThemeSettingDefaults.ColorWeak.ToString().ToLowerInvariant(), |
|||
L("Settings:ColorWeak"), |
|||
isVisibleToClients: true |
|||
) |
|||
); |
|||
} |
|||
|
|||
private static LocalizableString L(string name) |
|||
{ |
|||
return LocalizableString.Create<AntDesignThemeManagementResource>(name); |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
namespace Lsw.Abp.AntDesignThemeManagement.Settings; |
|||
|
|||
public static class AntDesignThemeManagementSettingNames |
|||
{ |
|||
public const string GroupName = "Lsw.AntDesignTheme"; |
|||
|
|||
public const string EnableThemeSettings = GroupName + ".EnableThemeSettings"; |
|||
public const string EnablePageStyleSetting = GroupName + ".Enable.PageStyleSetting"; |
|||
public const string EnableNavigationModeSetting = GroupName + ".Enable.NavigationModeSetting"; |
|||
public const string EnableRegionalSettings = GroupName + ".Enable.RegionalSettings"; |
|||
public const string EnableOtherSettings = GroupName + ".Enable.OtherSettings"; |
|||
|
|||
public const string ThemeStyle = GroupName + ".ThemeStyle"; |
|||
public const string NavigationMode = GroupName + ".NavigationMode"; |
|||
public const string ContentWidth = GroupName + ".ContentWidth"; |
|||
public const string FixedHeader = GroupName + ".FixedHeader"; |
|||
public const string FixSiderbar = GroupName + ".FixSiderbar"; |
|||
public const string SplitMenus = GroupName + ".SplitMenus"; |
|||
public const string ShowHeader = GroupName + ".Region.Header"; |
|||
public const string ShowFooter = GroupName + ".Region.Footer"; |
|||
public const string ShowMenu = GroupName + ".Region.Menu"; |
|||
public const string ShowMenuHeader = GroupName + ".Region.MenuHeader"; |
|||
public const string ColorWeak = GroupName + ".ColorWeak"; |
|||
} |
|||
@ -0,0 +1,42 @@ |
|||
namespace Lsw.Abp.AntDesignThemeManagement.Settings; |
|||
|
|||
public static class AntDesignThemeSettingDefaults |
|||
{ |
|||
public const bool EnableThemeSettings = true; |
|||
public const bool EnablePageStyleSetting = true; |
|||
public const bool EnableNavigationModeSetting = true; |
|||
public const bool EnableRegionalSettings = true; |
|||
public const bool EnableOtherSettings = true; |
|||
|
|||
public const string ThemeStyle = ThemeStyles.Light; |
|||
public const string NavigationMode = NavigationModes.Side; |
|||
public const string ContentWidth = ContentWidths.Fluid; |
|||
public const bool FixedHeader = true; |
|||
public const bool FixSiderbar = true; |
|||
public const bool SplitMenus = false; |
|||
public const bool ShowHeader = true; |
|||
public const bool ShowFooter = true; |
|||
public const bool ShowMenu = true; |
|||
public const bool ShowMenuHeader = true; |
|||
public const bool ColorWeak = false; |
|||
} |
|||
|
|||
public static class ThemeStyles |
|||
{ |
|||
public const string Light = "Light"; |
|||
public const string Dark = "Dark"; |
|||
public const string RealDark = "RealDark"; |
|||
} |
|||
|
|||
public static class NavigationModes |
|||
{ |
|||
public const string Side = "Side"; |
|||
public const string Top = "Top"; |
|||
public const string Mix = "Mix"; |
|||
} |
|||
|
|||
public static class ContentWidths |
|||
{ |
|||
public const string Fluid = "Fluid"; |
|||
public const string Fixed = "Fixed"; |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.SettingManagement; |
|||
|
|||
namespace Lsw.Abp.AntDesignThemeManagement; |
|||
|
|||
[DependsOn( |
|||
typeof(AbpAntDesignThemeManagementApplicationContractsModule), |
|||
typeof(AbpSettingManagementApplicationModule) |
|||
)] |
|||
public class AbpAntDesignThemeManagementApplicationModule : AbpModule |
|||
{ |
|||
} |
|||
@ -0,0 +1,284 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Lsw.Abp.AntDesignThemeManagement.Dtos; |
|||
using Lsw.Abp.AntDesignThemeManagement.Permissions; |
|||
using Lsw.Abp.AntDesignThemeManagement.Settings; |
|||
using Volo.Abp.Application.Services; |
|||
using Volo.Abp.Authorization; |
|||
using Volo.Abp.Authorization.Permissions; |
|||
using Volo.Abp.SettingManagement; |
|||
using Volo.Abp.Settings; |
|||
|
|||
namespace Lsw.Abp.AntDesignThemeManagement; |
|||
|
|||
public class AntDesignThemePreferenceAppService : ApplicationService, IAntDesignThemePreferenceAppService |
|||
{ |
|||
protected ISettingProvider AbpSettingProvider { get; } |
|||
protected ISettingManager AbpSettingManager { get; } |
|||
protected IPermissionChecker PermissionChecker { get; } |
|||
|
|||
public AntDesignThemePreferenceAppService( |
|||
ISettingProvider settingProvider, |
|||
ISettingManager settingManager, |
|||
IPermissionChecker permissionChecker) |
|||
{ |
|||
AbpSettingProvider = settingProvider; |
|||
AbpSettingManager = settingManager; |
|||
PermissionChecker = permissionChecker; |
|||
} |
|||
|
|||
public virtual async Task<AntDesignThemePreferenceDto> GetAsync() |
|||
{ |
|||
var preference = new AntDesignThemePreferenceDto |
|||
{ |
|||
ThemeSettingsEnabled = await GetBoolAsync( |
|||
AntDesignThemeManagementSettingNames.EnableThemeSettings, |
|||
AntDesignThemeSettingDefaults.EnableThemeSettings), |
|||
PageStyleSettingEnabled = await GetBoolAsync( |
|||
AntDesignThemeManagementSettingNames.EnablePageStyleSetting, |
|||
AntDesignThemeSettingDefaults.EnablePageStyleSetting), |
|||
NavigationModeSettingEnabled = await GetBoolAsync( |
|||
AntDesignThemeManagementSettingNames.EnableNavigationModeSetting, |
|||
AntDesignThemeSettingDefaults.EnableNavigationModeSetting), |
|||
RegionalSettingsEnabled = await GetBoolAsync( |
|||
AntDesignThemeManagementSettingNames.EnableRegionalSettings, |
|||
AntDesignThemeSettingDefaults.EnableRegionalSettings), |
|||
OtherSettingsEnabled = await GetBoolAsync( |
|||
AntDesignThemeManagementSettingNames.EnableOtherSettings, |
|||
AntDesignThemeSettingDefaults.EnableOtherSettings), |
|||
ThemeStyle = await GetThemeStyleAsync(), |
|||
NavigationMode = await GetNavigationModeAsync(), |
|||
ContentWidth = await GetContentWidthAsync(), |
|||
FixedHeader = await GetBoolAsync( |
|||
AntDesignThemeManagementSettingNames.FixedHeader, |
|||
AntDesignThemeSettingDefaults.FixedHeader), |
|||
FixSiderbar = await GetBoolAsync( |
|||
AntDesignThemeManagementSettingNames.FixSiderbar, |
|||
AntDesignThemeSettingDefaults.FixSiderbar), |
|||
SplitMenus = await GetBoolAsync( |
|||
AntDesignThemeManagementSettingNames.SplitMenus, |
|||
AntDesignThemeSettingDefaults.SplitMenus), |
|||
ShowHeader = await GetBoolAsync( |
|||
AntDesignThemeManagementSettingNames.ShowHeader, |
|||
AntDesignThemeSettingDefaults.ShowHeader), |
|||
ShowFooter = await GetBoolAsync( |
|||
AntDesignThemeManagementSettingNames.ShowFooter, |
|||
AntDesignThemeSettingDefaults.ShowFooter), |
|||
ShowMenu = await GetBoolAsync( |
|||
AntDesignThemeManagementSettingNames.ShowMenu, |
|||
AntDesignThemeSettingDefaults.ShowMenu), |
|||
ShowMenuHeader = await GetBoolAsync( |
|||
AntDesignThemeManagementSettingNames.ShowMenuHeader, |
|||
AntDesignThemeSettingDefaults.ShowMenuHeader), |
|||
ColorWeak = await GetBoolAsync( |
|||
AntDesignThemeManagementSettingNames.ColorWeak, |
|||
AntDesignThemeSettingDefaults.ColorWeak) |
|||
}; |
|||
|
|||
NormalizeThemeSettingsAvailability(preference); |
|||
return preference; |
|||
} |
|||
|
|||
public virtual async Task UpdateAsync(UpdateAntDesignThemePreferenceDto input) |
|||
{ |
|||
if (!CurrentUser.IsAuthenticated) |
|||
{ |
|||
throw new AbpAuthorizationException("Current user must be authenticated."); |
|||
} |
|||
|
|||
await AbpSettingManager.SetForCurrentUserAsync( |
|||
AntDesignThemeManagementSettingNames.ThemeStyle, |
|||
NormalizeThemeStyle(input.ThemeStyle)); |
|||
await AbpSettingManager.SetForCurrentUserAsync( |
|||
AntDesignThemeManagementSettingNames.NavigationMode, |
|||
NormalizeNavigationMode(input.NavigationMode)); |
|||
await AbpSettingManager.SetForCurrentUserAsync( |
|||
AntDesignThemeManagementSettingNames.ContentWidth, |
|||
NormalizeContentWidth(input.ContentWidth)); |
|||
await AbpSettingManager.SetForCurrentUserAsync( |
|||
AntDesignThemeManagementSettingNames.FixedHeader, |
|||
input.FixedHeader.ToString().ToLowerInvariant()); |
|||
await AbpSettingManager.SetForCurrentUserAsync( |
|||
AntDesignThemeManagementSettingNames.FixSiderbar, |
|||
input.FixSiderbar.ToString().ToLowerInvariant()); |
|||
await AbpSettingManager.SetForCurrentUserAsync( |
|||
AntDesignThemeManagementSettingNames.SplitMenus, |
|||
input.SplitMenus.ToString().ToLowerInvariant()); |
|||
await AbpSettingManager.SetForCurrentUserAsync( |
|||
AntDesignThemeManagementSettingNames.ShowHeader, |
|||
input.ShowHeader.ToString().ToLowerInvariant()); |
|||
await AbpSettingManager.SetForCurrentUserAsync( |
|||
AntDesignThemeManagementSettingNames.ShowFooter, |
|||
input.ShowFooter.ToString().ToLowerInvariant()); |
|||
await AbpSettingManager.SetForCurrentUserAsync( |
|||
AntDesignThemeManagementSettingNames.ShowMenu, |
|||
input.ShowMenu.ToString().ToLowerInvariant()); |
|||
await AbpSettingManager.SetForCurrentUserAsync( |
|||
AntDesignThemeManagementSettingNames.ShowMenuHeader, |
|||
input.ShowMenuHeader.ToString().ToLowerInvariant()); |
|||
await AbpSettingManager.SetForCurrentUserAsync( |
|||
AntDesignThemeManagementSettingNames.ColorWeak, |
|||
input.ColorWeak.ToString().ToLowerInvariant()); |
|||
} |
|||
|
|||
public virtual async Task UpdateThemeSettingsAvailabilityAsync(UpdateAntDesignThemeSettingsAvailabilityDto input) |
|||
{ |
|||
if (!await PermissionChecker.IsGrantedAsync(AntDesignThemeManagementPermissions.Settings)) |
|||
{ |
|||
throw new AbpAuthorizationException("Missing permission to update global theme settings."); |
|||
} |
|||
|
|||
var normalized = NormalizeThemeSettingsAvailability(input); |
|||
await SetThemeSettingsAvailabilityAsync(normalized); |
|||
} |
|||
|
|||
public virtual async Task SetThemeSettingsEnabledAsync(bool isEnabled) |
|||
{ |
|||
if (!await PermissionChecker.IsGrantedAsync(AntDesignThemeManagementPermissions.Settings)) |
|||
{ |
|||
throw new AbpAuthorizationException("Missing permission to update global theme settings."); |
|||
} |
|||
|
|||
var normalized = isEnabled |
|||
? new ThemeSettingsAvailability(true, true, true, true, true) |
|||
: new ThemeSettingsAvailability(false, false, false, false, false); |
|||
|
|||
await SetThemeSettingsAvailabilityAsync(normalized); |
|||
} |
|||
|
|||
protected virtual async Task<string> GetThemeStyleAsync() |
|||
{ |
|||
var value = await GetStringAsync( |
|||
AntDesignThemeManagementSettingNames.ThemeStyle, |
|||
AntDesignThemeSettingDefaults.ThemeStyle); |
|||
|
|||
return NormalizeThemeStyle(value); |
|||
} |
|||
|
|||
protected virtual async Task<string> GetNavigationModeAsync() |
|||
{ |
|||
var value = await GetStringAsync( |
|||
AntDesignThemeManagementSettingNames.NavigationMode, |
|||
AntDesignThemeSettingDefaults.NavigationMode); |
|||
|
|||
return NormalizeNavigationMode(value); |
|||
} |
|||
|
|||
protected virtual async Task<string> GetContentWidthAsync() |
|||
{ |
|||
var value = await GetStringAsync( |
|||
AntDesignThemeManagementSettingNames.ContentWidth, |
|||
AntDesignThemeSettingDefaults.ContentWidth); |
|||
|
|||
return NormalizeContentWidth(value); |
|||
} |
|||
|
|||
protected virtual async Task<string> GetStringAsync(string name, string defaultValue) |
|||
{ |
|||
var value = await AbpSettingProvider.GetOrNullAsync(name); |
|||
return string.IsNullOrWhiteSpace(value) ? defaultValue : value; |
|||
} |
|||
|
|||
protected virtual async Task<bool> GetBoolAsync(string name, bool defaultValue) |
|||
{ |
|||
var value = await AbpSettingProvider.GetOrNullAsync(name); |
|||
return bool.TryParse(value, out var parsed) ? parsed : defaultValue; |
|||
} |
|||
|
|||
protected virtual void NormalizeThemeSettingsAvailability(AntDesignThemePreferenceDto preference) |
|||
{ |
|||
var hasAnyEnabledSection = preference.PageStyleSettingEnabled |
|||
|| preference.NavigationModeSettingEnabled |
|||
|| preference.RegionalSettingsEnabled |
|||
|| preference.OtherSettingsEnabled; |
|||
|
|||
preference.ThemeSettingsEnabled = hasAnyEnabledSection; |
|||
} |
|||
|
|||
protected virtual ThemeSettingsAvailability NormalizeThemeSettingsAvailability( |
|||
UpdateAntDesignThemeSettingsAvailabilityDto input) |
|||
{ |
|||
if (!input.ThemeSettingsEnabled) |
|||
{ |
|||
return new ThemeSettingsAvailability(false, false, false, false, false); |
|||
} |
|||
|
|||
var hasAnyEnabledSection = input.PageStyleSettingEnabled |
|||
|| input.NavigationModeSettingEnabled |
|||
|| input.RegionalSettingsEnabled |
|||
|| input.OtherSettingsEnabled; |
|||
|
|||
if (!hasAnyEnabledSection) |
|||
{ |
|||
// Enabling the root switch should turn on all sub-items by default.
|
|||
return new ThemeSettingsAvailability(true, true, true, true, true); |
|||
} |
|||
|
|||
return new ThemeSettingsAvailability( |
|||
true, |
|||
input.PageStyleSettingEnabled, |
|||
input.NavigationModeSettingEnabled, |
|||
input.RegionalSettingsEnabled, |
|||
input.OtherSettingsEnabled |
|||
); |
|||
} |
|||
|
|||
protected virtual async Task SetThemeSettingsAvailabilityAsync(ThemeSettingsAvailability availability) |
|||
{ |
|||
await AbpSettingManager.SetGlobalAsync( |
|||
AntDesignThemeManagementSettingNames.EnableThemeSettings, |
|||
availability.ThemeSettingsEnabled.ToString().ToLowerInvariant()); |
|||
await AbpSettingManager.SetGlobalAsync( |
|||
AntDesignThemeManagementSettingNames.EnablePageStyleSetting, |
|||
availability.PageStyleSettingEnabled.ToString().ToLowerInvariant()); |
|||
await AbpSettingManager.SetGlobalAsync( |
|||
AntDesignThemeManagementSettingNames.EnableNavigationModeSetting, |
|||
availability.NavigationModeSettingEnabled.ToString().ToLowerInvariant()); |
|||
await AbpSettingManager.SetGlobalAsync( |
|||
AntDesignThemeManagementSettingNames.EnableRegionalSettings, |
|||
availability.RegionalSettingsEnabled.ToString().ToLowerInvariant()); |
|||
await AbpSettingManager.SetGlobalAsync( |
|||
AntDesignThemeManagementSettingNames.EnableOtherSettings, |
|||
availability.OtherSettingsEnabled.ToString().ToLowerInvariant()); |
|||
} |
|||
|
|||
protected virtual string NormalizeThemeStyle(string themeStyle) |
|||
{ |
|||
return themeStyle switch |
|||
{ |
|||
ThemeStyles.Light => ThemeStyles.Light, |
|||
ThemeStyles.Dark => ThemeStyles.Dark, |
|||
ThemeStyles.RealDark => ThemeStyles.RealDark, |
|||
_ => AntDesignThemeSettingDefaults.ThemeStyle |
|||
}; |
|||
} |
|||
|
|||
protected virtual string NormalizeNavigationMode(string navigationMode) |
|||
{ |
|||
return navigationMode switch |
|||
{ |
|||
NavigationModes.Side => NavigationModes.Side, |
|||
NavigationModes.Top => NavigationModes.Top, |
|||
NavigationModes.Mix => NavigationModes.Side, |
|||
_ => AntDesignThemeSettingDefaults.NavigationMode |
|||
}; |
|||
} |
|||
|
|||
protected virtual string NormalizeContentWidth(string contentWidth) |
|||
{ |
|||
return contentWidth switch |
|||
{ |
|||
ContentWidths.Fluid => ContentWidths.Fluid, |
|||
ContentWidths.Fixed => ContentWidths.Fixed, |
|||
_ => AntDesignThemeSettingDefaults.ContentWidth |
|||
}; |
|||
} |
|||
|
|||
protected record ThemeSettingsAvailability( |
|||
bool ThemeSettingsEnabled, |
|||
bool PageStyleSettingEnabled, |
|||
bool NavigationModeSettingEnabled, |
|||
bool RegionalSettingsEnabled, |
|||
bool OtherSettingsEnabled |
|||
); |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -0,0 +1,18 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\common.props" /> |
|||
<Import Project="..\..\..\configureawait.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net10.0</TargetFramework> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.SettingManagement.Application" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\Lsw.Abp.AntDesignThemeManagement.Application.Contracts\Lsw.Abp.AntDesignThemeManagement.Application.Contracts.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,14 @@ |
|||
using Lsw.Abp.AntDesignThemeManagement.Blazor; |
|||
using Lsw.Abp.AspnetCore.Components.Server.AntDesignTheme; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace Lsw.Abp.AntDesignThemeManagement.Blazor.Server; |
|||
|
|||
[DependsOn( |
|||
typeof(AbpAntDesignThemeManagementBlazorModule), |
|||
typeof(AbpAntDesignThemeManagementApplicationModule), |
|||
typeof(AbpAspNetCoreComponentsServerAntDesignThemeModule) |
|||
)] |
|||
public class AbpAntDesignThemeManagementBlazorServerModule : AbpModule |
|||
{ |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -0,0 +1,16 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk.Razor"> |
|||
|
|||
<Import Project="..\..\..\common.props" /> |
|||
<Import Project="..\..\..\configureawait.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net10.0</TargetFramework> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\Lsw.Abp.AntDesignThemeManagement.Application\Lsw.Abp.AntDesignThemeManagement.Application.csproj" /> |
|||
<ProjectReference Include="..\Lsw.Abp.AntDesignThemeManagement.Blazor\Lsw.Abp.AntDesignThemeManagement.Blazor.csproj" /> |
|||
<ProjectReference Include="..\..\AntDesignTheme\Lsw.Abp.AspnetCore.Components.Server.AntDesignTheme\Lsw.Abp.AspnetCore.Components.Server.AntDesignTheme.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,22 @@ |
|||
using Lsw.Abp.AntDesignThemeManagement.Blazor; |
|||
using Lsw.Abp.AspnetCore.Components.WebAssembly.AntDesignTheme; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.Http.Client; |
|||
using Volo.Abp.Http.Client.IdentityModel.WebAssembly; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace Lsw.Abp.AntDesignThemeManagement.Blazor.WebAssembly; |
|||
|
|||
[DependsOn( |
|||
typeof(AbpAntDesignThemeManagementBlazorModule), |
|||
typeof(AbpAspNetCoreComponentsWebAssemblyAntDesignThemeModule), |
|||
typeof(AbpHttpClientIdentityModelWebAssemblyModule) |
|||
)] |
|||
public class AbpAntDesignThemeManagementBlazorWebAssemblyModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
context.Services.AddHttpClientProxies( |
|||
typeof(AbpAntDesignThemeManagementApplicationContractsModule).Assembly); |
|||
} |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -0,0 +1,19 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk.Razor"> |
|||
|
|||
<Import Project="..\..\..\common.props" /> |
|||
<Import Project="..\..\..\configureawait.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net10.0</TargetFramework> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.Http.Client.IdentityModel.WebAssembly" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\Lsw.Abp.AntDesignThemeManagement.Blazor\Lsw.Abp.AntDesignThemeManagement.Blazor.csproj" /> |
|||
<ProjectReference Include="..\..\AntDesignTheme\Lsw.Abp.AspnetCore.Components.WebAssembly.AntDesignTheme\Lsw.Abp.AspnetCore.Components.WebAssembly.AntDesignTheme.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,23 @@ |
|||
using Lsw.Abp.AntDesignThemeManagement.Blazor.Settings; |
|||
using Lsw.Abp.AspnetCore.Components.Web.AntDesignTheme; |
|||
using Lsw.Abp.SettingManagement.Blazor.AntDesignUI; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.SettingManagement.Blazor; |
|||
|
|||
namespace Lsw.Abp.AntDesignThemeManagement.Blazor; |
|||
|
|||
[DependsOn( |
|||
typeof(AbpAntDesignThemeManagementApplicationContractsModule), |
|||
typeof(AbpAspNetCoreComponentsWebAntDesignThemeModule), |
|||
typeof(AbpSettingManagementBlazorAntDesignModule) |
|||
)] |
|||
public class AbpAntDesignThemeManagementBlazorModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<SettingManagementComponentOptions>(options => |
|||
{ |
|||
options.Contributors.Add(new AntDesignThemeManagementSettingContributor()); |
|||
}); |
|||
} |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -0,0 +1,16 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk.Razor"> |
|||
|
|||
<Import Project="..\..\..\common.props" /> |
|||
<Import Project="..\..\..\configureawait.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net10.0</TargetFramework> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\Lsw.Abp.AntDesignThemeManagement.Application.Contracts\Lsw.Abp.AntDesignThemeManagement.Application.Contracts.csproj" /> |
|||
<ProjectReference Include="..\..\AntDesignTheme\Lsw.Abp.AspnetCore.Components.Web.AntDesignTheme\Lsw.Abp.AspnetCore.Components.Web.AntDesignTheme.csproj" /> |
|||
<ProjectReference Include="..\..\SettingManagement\Lsw.Abp.SettingManagement.Blazor.AntDesignUI\Lsw.Abp.SettingManagement.Blazor.AntDesignUI.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,36 @@ |
|||
@inherits Volo.Abp.AspNetCore.Components.AbpComponentBase |
|||
@using AntDesign |
|||
@using Lsw.Abp.AntDesignThemeManagement.Localization |
|||
|
|||
<div class="lsw-theme-settings-management-root"> |
|||
<Checkbox @bind-Value="ThemeSettingsEnabled" OnChange="OnThemeSettingsEnabledChanged"> |
|||
@L["Settings:EnableThemeSettings"] |
|||
</Checkbox> |
|||
</div> |
|||
<div class="lsw-theme-settings-management-children"> |
|||
<div class="lsw-theme-settings-management-item"> |
|||
<Checkbox @bind-Value="PageStyleSettingEnabled" OnChange="OnPageStyleSettingEnabledChanged"> |
|||
@L["Settings:PageStyleSetting"] |
|||
</Checkbox> |
|||
</div> |
|||
<div class="lsw-theme-settings-management-item"> |
|||
<Checkbox @bind-Value="NavigationModeSettingEnabled" OnChange="OnNavigationModeSettingEnabledChanged"> |
|||
@L["Settings:NavigationModeSetting"] |
|||
</Checkbox> |
|||
</div> |
|||
<div class="lsw-theme-settings-management-item"> |
|||
<Checkbox @bind-Value="RegionalSettingsEnabled" OnChange="OnRegionalSettingsEnabledChanged"> |
|||
@L["Settings:RegionalSettings"] |
|||
</Checkbox> |
|||
</div> |
|||
<div class="lsw-theme-settings-management-item"> |
|||
<Checkbox @bind-Value="OtherSettingsEnabled" OnChange="OnOtherSettingsEnabledChanged"> |
|||
@L["Settings:OtherSettings"] |
|||
</Checkbox> |
|||
</div> |
|||
</div> |
|||
<div class="lsw-theme-settings-management-actions"> |
|||
<Button Type="@ButtonType.Primary" OnClick="@SaveAsync"> |
|||
@L["Submit"] |
|||
</Button> |
|||
</div> |
|||
@ -0,0 +1,120 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Lsw.Abp.AntDesignThemeManagement.Dtos; |
|||
using Lsw.Abp.AntDesignThemeManagement.Localization; |
|||
using Lsw.Abp.AspnetCore.Components.Web.AntDesignTheme.Settings; |
|||
using Microsoft.AspNetCore.Components; |
|||
|
|||
namespace Lsw.Abp.AntDesignThemeManagement.Blazor.Pages.SettingManagement.ThemeSettingsManagementGroup; |
|||
|
|||
public partial class ThemeSettingsManagementGroupViewComponent |
|||
{ |
|||
[Inject] |
|||
protected IAntDesignThemePreferenceAppService AntDesignThemePreferenceAppService { get; set; } = default!; |
|||
|
|||
[Inject] |
|||
protected IAntDesignSettingsProvider AntDesignSettingsProvider { get; set; } = default!; |
|||
|
|||
protected bool ThemeSettingsEnabled { get; set; } |
|||
protected bool PageStyleSettingEnabled { get; set; } |
|||
protected bool NavigationModeSettingEnabled { get; set; } |
|||
protected bool RegionalSettingsEnabled { get; set; } |
|||
protected bool OtherSettingsEnabled { get; set; } |
|||
|
|||
public ThemeSettingsManagementGroupViewComponent() |
|||
{ |
|||
LocalizationResource = typeof(AntDesignThemeManagementResource); |
|||
} |
|||
|
|||
protected override async Task OnInitializedAsync() |
|||
{ |
|||
try |
|||
{ |
|||
var preference = await AntDesignThemePreferenceAppService.GetAsync(); |
|||
ThemeSettingsEnabled = preference.ThemeSettingsEnabled; |
|||
PageStyleSettingEnabled = preference.PageStyleSettingEnabled; |
|||
NavigationModeSettingEnabled = preference.NavigationModeSettingEnabled; |
|||
RegionalSettingsEnabled = preference.RegionalSettingsEnabled; |
|||
OtherSettingsEnabled = preference.OtherSettingsEnabled; |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
await HandleErrorAsync(ex); |
|||
} |
|||
} |
|||
|
|||
protected virtual void OnThemeSettingsEnabledChanged(bool value) |
|||
{ |
|||
ThemeSettingsEnabled = value; |
|||
if (!value) |
|||
{ |
|||
SetAllSubItems(false); |
|||
return; |
|||
} |
|||
|
|||
SetAllSubItems(true); |
|||
} |
|||
|
|||
protected virtual void OnPageStyleSettingEnabledChanged(bool value) |
|||
{ |
|||
PageStyleSettingEnabled = value; |
|||
SyncRootSwitchByChildren(); |
|||
} |
|||
|
|||
protected virtual void OnNavigationModeSettingEnabledChanged(bool value) |
|||
{ |
|||
NavigationModeSettingEnabled = value; |
|||
SyncRootSwitchByChildren(); |
|||
} |
|||
|
|||
protected virtual void OnRegionalSettingsEnabledChanged(bool value) |
|||
{ |
|||
RegionalSettingsEnabled = value; |
|||
SyncRootSwitchByChildren(); |
|||
} |
|||
|
|||
protected virtual void OnOtherSettingsEnabledChanged(bool value) |
|||
{ |
|||
OtherSettingsEnabled = value; |
|||
SyncRootSwitchByChildren(); |
|||
} |
|||
|
|||
protected virtual async Task SaveAsync() |
|||
{ |
|||
try |
|||
{ |
|||
await AntDesignThemePreferenceAppService.UpdateThemeSettingsAvailabilityAsync( |
|||
new UpdateAntDesignThemeSettingsAvailabilityDto |
|||
{ |
|||
ThemeSettingsEnabled = ThemeSettingsEnabled, |
|||
PageStyleSettingEnabled = PageStyleSettingEnabled, |
|||
NavigationModeSettingEnabled = NavigationModeSettingEnabled, |
|||
RegionalSettingsEnabled = RegionalSettingsEnabled, |
|||
OtherSettingsEnabled = OtherSettingsEnabled |
|||
} |
|||
); |
|||
await AntDesignSettingsProvider.TriggerSettingChangedAsync(); |
|||
await Message.Success(L["SavedSuccessfully"]); |
|||
} |
|||
catch (Exception ex) |
|||
{ |
|||
await HandleErrorAsync(ex); |
|||
} |
|||
} |
|||
|
|||
protected virtual void SetAllSubItems(bool enabled) |
|||
{ |
|||
PageStyleSettingEnabled = enabled; |
|||
NavigationModeSettingEnabled = enabled; |
|||
RegionalSettingsEnabled = enabled; |
|||
OtherSettingsEnabled = enabled; |
|||
} |
|||
|
|||
protected virtual void SyncRootSwitchByChildren() |
|||
{ |
|||
ThemeSettingsEnabled = PageStyleSettingEnabled |
|||
|| NavigationModeSettingEnabled |
|||
|| RegionalSettingsEnabled |
|||
|| OtherSettingsEnabled; |
|||
} |
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
using System.Threading.Tasks; |
|||
using Lsw.Abp.AntDesignThemeManagement.Blazor.Pages.SettingManagement.ThemeSettingsManagementGroup; |
|||
using Lsw.Abp.AntDesignThemeManagement.Localization; |
|||
using Lsw.Abp.AntDesignThemeManagement.Permissions; |
|||
using Microsoft.AspNetCore.Authorization; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Microsoft.Extensions.Localization; |
|||
using Volo.Abp.SettingManagement.Blazor; |
|||
|
|||
namespace Lsw.Abp.AntDesignThemeManagement.Blazor.Settings; |
|||
|
|||
public class AntDesignThemeManagementSettingContributor : ISettingComponentContributor |
|||
{ |
|||
public virtual async Task ConfigureAsync(SettingComponentCreationContext context) |
|||
{ |
|||
if (!await CheckPermissionsAsync(context)) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
var l = context.ServiceProvider.GetRequiredService<IStringLocalizer<AntDesignThemeManagementResource>>(); |
|||
context.Groups.Add( |
|||
new SettingComponentGroup( |
|||
"Lsw.AntDesignThemeManagement", |
|||
l["Menu:ThemeSettingsManagement"], |
|||
typeof(ThemeSettingsManagementGroupViewComponent) |
|||
) |
|||
); |
|||
} |
|||
|
|||
public virtual async Task<bool> CheckPermissionsAsync(SettingComponentCreationContext context) |
|||
{ |
|||
var authorizationService = context.ServiceProvider.GetRequiredService<IAuthorizationService>(); |
|||
return await authorizationService.IsGrantedAsync(AntDesignThemeManagementPermissions.Settings); |
|||
} |
|||
} |
|||
@ -1,56 +1,58 @@ |
|||
# BookStore |
|||
# BookStore WebApp Sample |
|||
|
|||
## About this solution |
|||
This sample demonstrates the AntDesign theme in ABP Blazor WebApp auto mode. |
|||
|
|||
This is a layered startup solution based on [Domain Driven Design (DDD)](https://abp.io/docs/latest/framework/architecture/domain-driven-design) practises. All the fundamental ABP modules are already installed. Check the [Application Startup Template](https://abp.io/docs/latest/solution-templates/layered-web-application) documentation for more info. |
|||
## Prerequisites |
|||
|
|||
### Pre-requirements |
|||
- .NET SDK 10.0+ |
|||
- Node.js 18 or newer |
|||
- MongoDB |
|||
- ABP CLI (`dotnet tool install -g Volo.Abp.Cli` if needed) |
|||
|
|||
* [.net10.0+ SDK](https://dotnet.microsoft.com/download/dotnet) |
|||
* [Node v18 or 20](https://nodejs.org/en) |
|||
## Run |
|||
|
|||
### Configurations |
|||
Install front-end libraries: |
|||
|
|||
The solution comes with a default configuration that works out of the box. However, you may consider to change the following configuration before running your solution: |
|||
|
|||
|
|||
### Before running the application |
|||
|
|||
* Run `abp install-libs` command on your solution folder to install client-side package dependencies. This step is automatically done when you create a new solution, if you didn't especially disabled it. However, you should run it yourself if you have first cloned this solution from your source control, or added a new client-side package dependency to your solution. |
|||
* Run `BookStore.DbMigrator` to create the initial database. This step is also automatically done when you create a new solution, if you didn't especially disabled it. This should be done in the first run. It is also needed if a new database migration is added to the solution later. |
|||
|
|||
#### Generating a Signing Certificate |
|||
|
|||
In the production environment, you need to use a production signing certificate. ABP Framework sets up signing and encryption certificates in your application and expects an `openiddict.pfx` file in your application. |
|||
```bash |
|||
abp install-libs |
|||
``` |
|||
|
|||
To generate a signing certificate, you can use the following command: |
|||
Apply database migrations and seed data: |
|||
|
|||
```bash |
|||
dotnet dev-certs https -v -ep openiddict.pfx -p b0859b18-d774-4576-aad2-c49c1a9dc2ef |
|||
dotnet run --project .\src\BookStore.DbMigrator\ |
|||
``` |
|||
|
|||
> `b0859b18-d774-4576-aad2-c49c1a9dc2ef` is the password of the certificate, you can change it to any password you want. |
|||
|
|||
It is recommended to use **two** RSA certificates, distinct from the certificate(s) used for HTTPS: one for encryption, one for signing. |
|||
Start the WebApp host: |
|||
|
|||
For more information, please refer to: [OpenIddict Certificate Configuration](https://documentation.openiddict.com/configuration/encryption-and-signing-credentials.html#registering-a-certificate-recommended-for-production-ready-scenarios) |
|||
```bash |
|||
dotnet run --project .\src\BookStore.Blazor\ |
|||
``` |
|||
|
|||
> Also, see the [Configuring OpenIddict](https://abp.io/docs/latest/Deployment/Configuring-OpenIddict#production-environment) documentation for more information. |
|||
Open `https://localhost:44320`. |
|||
|
|||
### Solution structure |
|||
## Default Login |
|||
|
|||
This is a layered monolith application that consists of the following applications: |
|||
- Username: `admin` |
|||
- Password: `1q2w3E*` |
|||
|
|||
* `BookStore.DbMigrator`: A console application which applies the migrations and also seeds the initial data. It is useful on development as well as on production environment. |
|||
## What To Verify |
|||
|
|||
- The application uses the refactored AntDesign Pro-style layout. |
|||
- The sidebar is responsive and can collapse on smaller screens. |
|||
- Authenticated users can open the floating theme settings panel. |
|||
- Theme settings apply immediately without restarting the application. |
|||
|
|||
## Deploying the application |
|||
## Theme Settings Management |
|||
|
|||
Deploying an ABP application follows the same process as deploying any .NET or ASP.NET Core application. However, there are important considerations to keep in mind. For detailed guidance, refer to ABP's [deployment documentation](https://abp.io/docs/latest/Deployment/Index). |
|||
This sample is configured with `AntDesignThemeManagement`. |
|||
|
|||
### Additional resources |
|||
Go to `Administration -> Settings -> Theme settings management` to control which sections appear in the user-facing panel: |
|||
|
|||
You can see the following resources to learn more about your solution and the ABP Framework: |
|||
- `Enable theme settings` |
|||
- `Page style setting` |
|||
- `Navigation mode` |
|||
- `Regional settings` |
|||
- `Other settings` |
|||
|
|||
* [Web Application Development Tutorial](https://abp.io/docs/latest/tutorials/book-store/part-1) |
|||
* [Application Startup Template](https://abp.io/docs/latest/startup-templates/application/index) |
|||
If all child options are disabled, `Enable theme settings` is automatically disabled. |
|||
|
|||
File diff suppressed because it is too large
@ -1,58 +1,58 @@ |
|||
# BookStore |
|||
# BookStore Blazor Server Sample |
|||
|
|||
## About this solution |
|||
This sample demonstrates the AntDesign theme in an ABP Blazor Server application. |
|||
|
|||
This is a layered startup solution based on [Domain Driven Design (DDD)](https://abp.io/docs/latest/framework/architecture/domain-driven-design) practises. All the fundamental ABP modules are already installed. Check the [Application Startup Template](https://abp.io/docs/latest/solution-templates/layered-web-application) documentation for more info. |
|||
## Prerequisites |
|||
|
|||
### Pre-requirements |
|||
- .NET SDK 10.0+ |
|||
- Node.js 18 or newer |
|||
- MongoDB |
|||
- ABP CLI (`dotnet tool install -g Volo.Abp.Cli` if needed) |
|||
|
|||
* [.net10.0+ SDK](https://dotnet.microsoft.com/download/dotnet) |
|||
* [Node v18 or 20](https://nodejs.org/en) |
|||
## Run |
|||
|
|||
### Configurations |
|||
Install front-end libraries: |
|||
|
|||
The solution comes with a default configuration that works out of the box. However, you may consider to change the following configuration before running your solution: |
|||
|
|||
* Check the `ConnectionStrings` in `appsettings.json` files under the `BookStore.Blazor` and `BookStore.DbMigrator` projects and change it if you need. |
|||
|
|||
### Before running the application |
|||
|
|||
* Run `abp install-libs` command on your solution folder to install client-side package dependencies. This step is automatically done when you create a new solution, if you didn't especially disabled it. However, you should run it yourself if you have first cloned this solution from your source control, or added a new client-side package dependency to your solution. |
|||
* Run `BookStore.DbMigrator` to create the initial database. This step is also automatically done when you create a new solution, if you didn't especially disabled it. This should be done in the first run. It is also needed if a new database migration is added to the solution later. |
|||
|
|||
#### Generating a Signing Certificate |
|||
|
|||
In the production environment, you need to use a production signing certificate. ABP Framework sets up signing and encryption certificates in your application and expects an `openiddict.pfx` file in your application. |
|||
```bash |
|||
abp install-libs |
|||
``` |
|||
|
|||
To generate a signing certificate, you can use the following command: |
|||
Apply database migrations and seed data: |
|||
|
|||
```bash |
|||
dotnet dev-certs https -v -ep openiddict.pfx -p d6d09f80-360d-4bc1-b9c5-f9cbfc2cb8f3 |
|||
dotnet run --project .\src\BookStore.DbMigrator\ |
|||
``` |
|||
|
|||
> `d6d09f80-360d-4bc1-b9c5-f9cbfc2cb8f3` is the password of the certificate, you can change it to any password you want. |
|||
|
|||
It is recommended to use **two** RSA certificates, distinct from the certificate(s) used for HTTPS: one for encryption, one for signing. |
|||
Start the Blazor Server app: |
|||
|
|||
For more information, please refer to: [OpenIddict Certificate Configuration](https://documentation.openiddict.com/configuration/encryption-and-signing-credentials.html#registering-a-certificate-recommended-for-production-ready-scenarios) |
|||
```bash |
|||
dotnet run --project .\src\BookStore.Blazor\ |
|||
``` |
|||
|
|||
> Also, see the [Configuring OpenIddict](https://abp.io/docs/latest/Deployment/Configuring-OpenIddict#production-environment) documentation for more information. |
|||
Open `https://localhost:44322`. |
|||
|
|||
### Solution structure |
|||
## Default Login |
|||
|
|||
This is a layered monolith application that consists of the following applications: |
|||
- Username: `admin` |
|||
- Password: `1q2w3E*` |
|||
|
|||
* `BookStore.DbMigrator`: A console application which applies the migrations and also seeds the initial data. It is useful on development as well as on production environment. |
|||
* `BookStore.Blazor`: ASP.NET Core Blazor Server application that is the essential web application of the solution. |
|||
## What To Verify |
|||
|
|||
- The application uses the refactored AntDesign Pro-style layout. |
|||
- The sidebar is responsive and can collapse on smaller screens. |
|||
- Authenticated users can open the floating theme settings panel. |
|||
- Theme settings apply immediately without restarting the application. |
|||
|
|||
## Deploying the application |
|||
## Theme Settings Management |
|||
|
|||
Deploying an ABP application follows the same process as deploying any .NET or ASP.NET Core application. However, there are important considerations to keep in mind. For detailed guidance, refer to ABP's [deployment documentation](https://abp.io/docs/latest/Deployment/Index). |
|||
This sample is configured with `AntDesignThemeManagement`. |
|||
|
|||
### Additional resources |
|||
Go to `Administration -> Settings -> Theme settings management` to control which sections appear in the user-facing panel: |
|||
|
|||
You can see the following resources to learn more about your solution and the ABP Framework: |
|||
- `Enable theme settings` |
|||
- `Page style setting` |
|||
- `Navigation mode` |
|||
- `Regional settings` |
|||
- `Other settings` |
|||
|
|||
* [Web Application Development Tutorial](https://abp.io/docs/latest/tutorials/book-store/part-1) |
|||
* [Application Startup Template](https://abp.io/docs/latest/startup-templates/application/index) |
|||
If all child options are disabled, `Enable theme settings` is automatically disabled. |
|||
|
|||
@ -1,3 +1,4 @@ |
|||
{ |
|||
"role": "host.blazor-server" |
|||
"role": "host.blazor-server", |
|||
"projectId": "97bca713-f461-4f87-92d0-fb96867f0a10" |
|||
} |
|||
File diff suppressed because it is too large
@ -1,59 +1,64 @@ |
|||
# BookStore |
|||
# BookStore Blazor WebAssembly Sample |
|||
|
|||
## About this solution |
|||
This sample demonstrates the AntDesign theme in an ABP Blazor WebAssembly application. |
|||
|
|||
This is a layered startup solution based on [Domain Driven Design (DDD)](https://abp.io/docs/latest/framework/architecture/domain-driven-design) practises. All the fundamental ABP modules are already installed. Check the [Application Startup Template](https://abp.io/docs/latest/solution-templates/layered-web-application) documentation for more info. |
|||
## Prerequisites |
|||
|
|||
### Pre-requirements |
|||
- .NET SDK 10.0+ |
|||
- Node.js 18 or newer |
|||
- MongoDB |
|||
- ABP CLI (`dotnet tool install -g Volo.Abp.Cli` if needed) |
|||
|
|||
* [.net10.0+ SDK](https://dotnet.microsoft.com/download/dotnet) |
|||
* [Node v18 or 20](https://nodejs.org/en) |
|||
## Run |
|||
|
|||
### Configurations |
|||
Install front-end libraries: |
|||
|
|||
The solution comes with a default configuration that works out of the box. However, you may consider to change the following configuration before running your solution: |
|||
|
|||
* Check the `ConnectionStrings` in `appsettings.json` files under the `BookStore.HttpApi.Host` and `BookStore.DbMigrator` projects and change it if you need. |
|||
|
|||
### Before running the application |
|||
|
|||
* Run `abp install-libs` command on your solution folder to install client-side package dependencies. This step is automatically done when you create a new solution, if you didn't especially disabled it. However, you should run it yourself if you have first cloned this solution from your source control, or added a new client-side package dependency to your solution. |
|||
* Run `BookStore.DbMigrator` to create the initial database. This step is also automatically done when you create a new solution, if you didn't especially disabled it. This should be done in the first run. It is also needed if a new database migration is added to the solution later. |
|||
```bash |
|||
abp install-libs |
|||
``` |
|||
|
|||
#### Generating a Signing Certificate |
|||
Apply database migrations and seed data: |
|||
|
|||
In the production environment, you need to use a production signing certificate. ABP Framework sets up signing and encryption certificates in your application and expects an `openiddict.pfx` file in your application. |
|||
```bash |
|||
dotnet run --project .\src\BookStore.DbMigrator\ |
|||
``` |
|||
|
|||
To generate a signing certificate, you can use the following command: |
|||
Start the HTTP API host: |
|||
|
|||
```bash |
|||
dotnet dev-certs https -v -ep openiddict.pfx -p 2dae3a4f-9e73-4cb7-b7b9-7ec95c44e1a3 |
|||
dotnet run --project .\src\BookStore.HttpApi.Host\ |
|||
``` |
|||
|
|||
> `2dae3a4f-9e73-4cb7-b7b9-7ec95c44e1a3` is the password of the certificate, you can change it to any password you want. |
|||
|
|||
It is recommended to use **two** RSA certificates, distinct from the certificate(s) used for HTTPS: one for encryption, one for signing. |
|||
Start the Blazor WebAssembly host in another terminal: |
|||
|
|||
For more information, please refer to: [OpenIddict Certificate Configuration](https://documentation.openiddict.com/configuration/encryption-and-signing-credentials.html#registering-a-certificate-recommended-for-production-ready-scenarios) |
|||
```bash |
|||
dotnet run --project .\src\BookStore.Blazor\ |
|||
``` |
|||
|
|||
> Also, see the [Configuring OpenIddict](https://abp.io/docs/latest/Deployment/Configuring-OpenIddict#production-environment) documentation for more information. |
|||
Open `https://localhost:44376`. |
|||
|
|||
### Solution structure |
|||
## Default Login |
|||
|
|||
This is a layered monolith application that consists of the following applications: |
|||
- Username: `admin` |
|||
- Password: `1q2w3E*` |
|||
|
|||
* `BookStore.DbMigrator`: A console application which applies the migrations and also seeds the initial data. It is useful on development as well as on production environment. |
|||
* `BookStore.HttpApi.Host`: ASP.NET Core API application that is used to expose the APIs to the clients. |
|||
* `BookStore.Blazor`: ASP.NET Core Blazor Server application that is the essential web application of the solution. |
|||
## What To Verify |
|||
|
|||
- The application uses the refactored AntDesign Pro-style layout. |
|||
- The sidebar is responsive and can collapse on smaller screens. |
|||
- Authenticated users can open the floating theme settings panel. |
|||
- Theme settings apply immediately through the host application service. |
|||
|
|||
## Deploying the application |
|||
## Theme Settings Management |
|||
|
|||
Deploying an ABP application follows the same process as deploying any .NET or ASP.NET Core application. However, there are important considerations to keep in mind. For detailed guidance, refer to ABP's [deployment documentation](https://abp.io/docs/latest/Deployment/Index). |
|||
This sample is configured with `AntDesignThemeManagement`. |
|||
|
|||
### Additional resources |
|||
Go to `Administration -> Settings -> Theme settings management` to control which sections appear in the user-facing panel: |
|||
|
|||
You can see the following resources to learn more about your solution and the ABP Framework: |
|||
- `Enable theme settings` |
|||
- `Page style setting` |
|||
- `Navigation mode` |
|||
- `Regional settings` |
|||
- `Other settings` |
|||
|
|||
* [Web Application Development Tutorial](https://abp.io/docs/latest/tutorials/book-store/part-1) |
|||
* [Application Startup Template](https://abp.io/docs/latest/startup-templates/application/index) |
|||
If all child options are disabled, `Enable theme settings` is automatically disabled. |
|||
|
|||
@ -1,3 +1,4 @@ |
|||
{ |
|||
"role": "host.http-api" |
|||
} |
|||
"role": "host.http-api", |
|||
"projectId": "7c286d0e-2950-4f31-a8bf-5c462e6459bb" |
|||
} |
|||
Loading…
Reference in new issue