!58 增加功能:Blazor 个人中心增加保存应用与样式功能

Merge pull request !58 from Argo/dev-blazor-wip
This commit is contained in:
Argo 2020-01-16 21:46:53 +08:00 committed by Gitee
commit 9ca7df8578
6 changed files with 211 additions and 89 deletions

View File

@ -30,6 +30,12 @@ namespace Bootstrap.Admin.Components
[Parameter]
public string AuthKey { get; set; } = "";
/// <summary>
/// 获得/设置 是否显示
/// </summary>
[Parameter]
public bool? Condition { get; set; }
/// <summary>
/// 获得/设置 子控件
/// </summary>
@ -61,6 +67,7 @@ namespace Bootstrap.Admin.Components
render = ComponentAuthorization?.Authorizate(user, url.ToMvcMenuUrl(), AuthKey) ?? false;
}
}
else if (Condition.HasValue) render = Condition.Value;
else render = RootLayout?.Model.IsDemo ?? false;
if (Inverse) render = !render;
if (render) builder.AddContent(0, ChildContent);

View File

@ -0,0 +1,29 @@
using Microsoft.AspNetCore.Components;
using System.Collections.Generic;
namespace Bootstrap.Admin.Components
{
/// <summary>
/// Dropdown 组件
/// </summary>
public class DropdownBase : ComponentBase
{
/// <summary>
/// 获得/设置 绑定数据集合
/// </summary>
[Parameter]
public IEnumerable<SelectedItem> Items { get; set; } = new SelectedItem[0];
/// <summary>
/// 获得/设置 选中项实例
/// </summary>
[Parameter]
public SelectedItem Value { get; set; } = new SelectedItem();
/// <summary>
/// 获得/设置 选中项改变回调方法
/// </summary>
[Parameter]
public EventCallback<SelectedItem> ValueChanged { get; set; }
}
}

View File

@ -29,90 +29,86 @@
</ConditionComponent>
</div>
</AuthorizateComponent>
<div class="card" asp-auth="savePassword" asp-condition="!@Model?.External">
<div class="card-header">修改密码</div>
<div class="card-body">
<ConditionComponent>
<div class="alert alert-danger" role="alert">
<span>演示系统禁止更改管理员密码</span>
<ConditionComponent Condition="@(Model?.External ?? false)" Inverse="true">
<ConditionComponent AuthKey="savePassword">
<div class="card">
<div class="card-header">修改密码</div>
<div class="card-body">
<ConditionComponent>
<div class="alert alert-danger" role="alert">
<span>演示系统禁止更改管理员密码</span>
</div>
</ConditionComponent>
<LgbEditForm class="form-inline" Id="Profile" Model="Password" OnValidSubmit="SavePassword">
<div class="row">
<LgbInputText InputType="password" ColumnClass="col-sm-6 col-md-auto" @bind-Value="@Password.Password" autocomplete="off" placeholder="原密码" maxlength="16">
<RequiredValidator />
<StringLengthValidator Length="16" />
</LgbInputText>
</div>
<div class="row">
<LgbInputText InputType="password" ColumnClass="col-sm-6 col-md-auto" @bind-Value="@Password.NewPassword" autocomplete="off" placeholder="新密码" maxlength="16">
<RequiredValidator />
<StringLengthValidator Length="16" />
</LgbInputText>
<LgbInputText InputType="password" ColumnClass="col-sm-6 col-md-auto" @bind-Value="@Password.ConfirmPassword" autocomplete="off" placeholder="与新密码一致" maxlength="16">
<RequiredValidator />
<StringLengthValidator Length="16" />
<EqualToValidator @bind-Value="@Password.NewPassword" />
</LgbInputText>
</div>
</LgbEditForm>
</div>
</ConditionComponent>
<LgbEditForm class="form-inline" Id="Profile" Model="Password" OnValidSubmit="SavePassword">
<div class="row">
<LgbInputText InputType="password" ColumnClass="col-sm-6 col-md-auto" @bind-Value="@Password.Password" autocomplete="off" placeholder="原密码" maxlength="16">
<RequiredValidator />
<StringLengthValidator Length="16" />
</LgbInputText>
</div>
<div class="row">
<LgbInputText InputType="password" ColumnClass="col-sm-6 col-md-auto" @bind-Value="@Password.NewPassword" autocomplete="off" placeholder="新密码" maxlength="16">
<RequiredValidator />
<StringLengthValidator Length="16" />
</LgbInputText>
<LgbInputText InputType="password" ColumnClass="col-sm-6 col-md-auto" @bind-Value="@Password.ConfirmPassword" autocomplete="off" placeholder="与新密码一致" maxlength="16">
<RequiredValidator />
<StringLengthValidator Length="16" />
<EqualToValidator @bind-Value="@Password.NewPassword" />
</LgbInputText>
</div>
</LgbEditForm>
</div>
<ConditionComponent Inverse="true">
<div class="card-footer">
<button class="btn btn-secondary" type="button" onclick="$.submitForm(this)"><i class="fa fa-save"></i><span>保存</span></button>
<ConditionComponent Inverse="true">
<div class="card-footer">
<button class="btn btn-secondary" type="button" onclick="$.submitForm(this)"><i class="fa fa-save"></i><span>保存</span></button>
</div>
</ConditionComponent>
</div>
</ConditionComponent>
</div>
<div class="card" asp-auth="saveApp">
<div class="card-header">默认应用</div>
<div class="card-body">
<div class="form-group">
<div class="btn-group" role="group">
<button id="app" class="btn btn-success dropdown-select dropdown-toggle" data-toggle="dropdown" value="@Model?.AppId">@Model?.AppName</button>
<div class="dropdown-menu">
@foreach (var app in Model?.Applications ?? new KeyValuePair<string, string>[0])
{
<a href="#" data-val="@app.Key">@app.Value</a>
}
</div>
</div>
</div>
</div>
<div class="card-footer">
<button id="btnSaveApp" data-method="app" class="btn btn-secondary" type="button"><i class="fa fa-save"></i><span>保存</span></button>
</div>
</div>
<div class="card" asp-auth="saveTheme">
<div class="card-header">网站样式</div>
<div class="card-body">
<div class="alert alert-info" role="alert">
<span>注意:本设置将覆盖<b><a class="badge-pill" href="./Settings">网站设置</a></b>中设置的网站样式</span>
</div>
<div class="form-group">
<div class="btn-group" role="group">
<button id="css" class="btn btn-success dropdown-select dropdown-toggle" data-toggle="dropdown" data-default-val="" value="@Model?.Css">默认样式</button>
<div class="dropdown-menu">
<a href="#" data-val="">默认样式</a>
@foreach (var css in Model?.Themes ?? new Bootstrap.Security.BootstrapDict[0])
{
<a href="#" data-val="@css.Code">@css.Name</a>
}
</div>
</div>
</div>
</div>
<div class="card-footer">
<button id="btnSaveCss" data-method="profileCss" class="btn btn-secondary" type="button"><i class="fa fa-save"></i><span>保存</span></button>
</div>
</div>
<div class="card" asp-auth="saveIcon">
<div class="card-header">修改头像</div>
<div class="card-body">
<form enctype="multipart/form-data">
</ConditionComponent>
<ConditionComponent AuthKey="saveApp">
<div class="card">
<div class="card-header">默认应用</div>
<div class="card-body">
<div class="form-group">
<input id="fileIcon" type="file" data-init="@Model?.Size" data-file="@Model?.FileName">
<div class="btn-group" role="group">
<Dropdown @bind-Value="@SelectedApp" Items="@Apps"></Dropdown>
</div>
</div>
</form>
<img class="card-img d-none" src="@Model?.Icon.ToBlazorLink()" />
</div>
<div class="card-footer">
<button class="btn btn-secondary" type="button" @onclick="SaveApp"><i class="fa fa-save"></i><span>保存</span></button>
</div>
</div>
</div>
</ConditionComponent>
<ConditionComponent AuthKey="saveTheme">
<div class="card">
<div class="card-header">网站样式</div>
<div class="card-body">
<div class="alert alert-info" role="alert">
<span>注意:本设置将覆盖<b><a class="badge-pill" href="@("~/Admin/Settings".ToBlazorMenuUrl())">网站设置</a></b>中设置的网站样式</span>
</div>
<div class="form-group">
<Dropdown @bind-Value="@SelectedTheme" Items="@Themes"></Dropdown>
</div>
</div>
<div class="card-footer">
<button class="btn btn-secondary" type="button" @onclick="SaveTheme"><i class="fa fa-save"></i><span>保存</span></button>
</div>
</div>
</ConditionComponent>
<ConditionComponent AuthKey="saveIcon">
<div class="card">
<div class="card-header">修改头像</div>
<div class="card-body">
<form enctype="multipart/form-data">
<div class="form-group">
<input id="fileIcon" type="file" data-init="@Model?.Size" data-file="@Model?.FileName">
</div>
</form>
<img class="card-img d-none" src="@Model?.Icon.ToBlazorLink()" />
</div>
</div>
</ConditionComponent>
<Toast @ref="Toast" Id="toast_profile"></Toast>

View File

@ -1,9 +1,13 @@
using System.ComponentModel;
using Bootstrap.Admin.Components;
using Bootstrap.Admin.Models;
using Bootstrap.Admin.Shared;
using Bootstrap.DataAccess;
using Bootstrap.Security;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
namespace Bootstrap.Pages.Admin.Components
{
@ -44,13 +48,51 @@ namespace Bootstrap.Pages.Admin.Components
[DisplayName("显示名称")]
public string DisplayName { get; set; } = "";
/// <summary>
/// Toast 组件实例
/// </summary>
protected Toast? Toast { get; set; }
/// <summary>
/// 获得/设置 选中的样式项
/// </summary>
protected SelectedItem SelectedTheme { get; set; } = new SelectedItem();
/// <summary>
/// 获得/设置 选中的应用程序
/// </summary>
protected SelectedItem SelectedApp { get; set; } = new SelectedItem();
/// <summary>
/// 获得/设置 应用程序集合
/// </summary>
protected IEnumerable<SelectedItem> Apps { get; set; } = new SelectedItem[0];
/// <summary>
/// 获得/设置 网站样式集合
/// </summary>
protected IEnumerable<SelectedItem> Themes { get; set; } = new SelectedItem[0];
/// <summary>
/// 显示提示信息
/// </summary>
/// <param name="text"></param>
/// <param name="ret"></param>
protected void ShowMessage(string text, bool ret = true) => Toast?.ShowMessage("个人中心", text, ret ? ToastCategory.Success : ToastCategory.Error);
/// <summary>
/// 组件初始化方法
/// </summary>
protected override void OnInitialized()
{
Model = new ProfilesModel(RootLayout?.UserName);
var user = DataAccess.UserHelper.RetrieveUserByUserName(Model?.UserName);
Themes = new SelectedItem[] { new SelectedItem() { Text = "默认样式" } }.Union(Model.Themes.Select(t => new SelectedItem() { Text = t.Name, Value = t.Code }));
Apps = Model.Applications.Select(t => new SelectedItem() { Text = t.Value, Value = t.Key });
SelectedTheme = Themes.First();
SelectedApp = Apps.First();
var user = UserHelper.RetrieveUserByUserName(Model?.UserName);
if (user != null) User = user;
// 直接绑定 User.DisplayName 导致未保存时 UI 的显示名称也会变化
@ -62,10 +104,18 @@ namespace Bootstrap.Pages.Admin.Components
/// </summary>
protected void SaveDisplayName(EditContext context)
{
if (!string.IsNullOrEmpty(User.UserName) && Bootstrap.DataAccess.UserHelper.SaveDisplayName(User.UserName, DisplayName))
if (!string.IsNullOrEmpty(User.UserName))
{
User.DisplayName = DisplayName;
RootLayout?.OnDisplayNameChanged(DisplayName);
var ret = UserHelper.SaveDisplayName(User.UserName, DisplayName);
if (ret)
{
User.DisplayName = DisplayName;
RootLayout?.OnDisplayNameChanged(DisplayName);
}
// 弹窗提示是否保存成功
var result = ret ? "成功" : "失败";
ShowMessage($"保存显示名称{result}", ret);
}
}
@ -74,7 +124,35 @@ namespace Bootstrap.Pages.Admin.Components
/// </summary>
protected void SavePassword(EditContext context)
{
Bootstrap.DataAccess.UserHelper.ChangePassword(User.UserName, Password.Password, Password.NewPassword);
var ret = UserHelper.ChangePassword(User.UserName, Password.Password, Password.NewPassword);
// 弹窗提示是否保存成功
var result = ret ? "成功" : "失败";
ShowMessage($"更新密码{result}", ret);
}
/// <summary>
/// 保存默认应用方法
/// </summary>
protected void SaveApp()
{
var ret = UserHelper.SaveApp(User.UserName, SelectedApp.Value);
// 弹窗提示是否保存成功
var result = ret ? "成功" : "失败";
ShowMessage($"保存默认应用{result}", ret);
}
/// <summary>
/// 保存网站样式方法
/// </summary>
protected void SaveTheme()
{
var ret = UserHelper.SaveUserCssByName(User.UserName, SelectedTheme.Value);
// 弹窗提示是否保存成功
var result = ret ? "成功" : "失败";
ShowMessage($"保存网站样式{result}", ret);
}
/// <summary>

View File

@ -0,0 +1,11 @@
@inherits DropdownBase
<div class="btn-group" role="group">
<button class="btn btn-success dropdown-select dropdown-toggle" data-toggle="dropdown">@Value.Text</button>
<div class="dropdown-menu">
@foreach (var item in Items)
{
<div class="dropdown-item" @onclick="e => Value = item">@item.Text</div>
}
</div>
</div>

View File

@ -435,7 +435,7 @@ footer {
outline: none;
}
.dropdown-select + .dropdown-menu a:hover {
.dropdown-select + .dropdown-menu a:hover, .dropdown-select + .dropdown-menu .dropdown-item:hover {
background: #007AC0;
color: #fff;
}
@ -444,10 +444,11 @@ footer {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.176);
}
.dropdown-menu a {
.dropdown-menu a, .dropdown-menu .dropdown-item {
transition: all .25s linear;
padding: 6px 20px;
display: block;
color: #007bff;
}
.dropdown-divider {