!49 增加功能:Blazor 框架下字典表维护功能完成
* feat: Query 组件支持泛型 * refactor: Dicts 组件移除不用的引用 * refactor: EditPage 组件增加 Id 保护 * doc: 增加程序注释 * feat: 增加验证控件扩展 * refactor: IValidateComponent 更改方法名 * refactor: 重构 checkbox 选中机制 * fix: 查询按钮过滤条件不生效问题 * fix: 修复查询区域按钮点击不能切换到首页数据问题 * feat: 完善保存功能 * refactor: Save 方法参数更改为实体类 * refactor: 优化每页显示数据算法 * fix: 修复新建数据项时清除已选数据功能 * feat: 完善删除功能 * feat: 查询后清楚已选条目 * feat: 修复编辑按钮功能 * feat: 增加行号设置 * refactor: 重构分页组件移除 Table 组件对其引用 * refactor: 重构 Dicts 页面组件化 * feat: 增加 Validate 功能 * refactor: 查询方法增加默认参数值 * feat: 增加删除确认功能 * style: 微调表格内按钮高度 * style: 微调 gear 样式 * fix: 修复表格数据删除后仍然可编辑 * feat: 增加 Table 表格内按钮组 * refactor: 增加删除数据是否成功提示 * feat: 增加 Toast 组件 * feat: 增加 Alert 控件 * feat: 增加保存功能 * feat: 表格首列设置为 checkbox 功能 * feat: 增加选择列
This commit is contained in:
parent
c824c008fc
commit
93af3948be
|
@ -22,6 +22,11 @@ namespace Bootstrap.Admin.Components
|
||||||
[CascadingParameter(Name = "Default")]
|
[CascadingParameter(Name = "Default")]
|
||||||
public DefaultLayout RootLayout { get; protected set; } = new DefaultLayout();
|
public DefaultLayout RootLayout { get; protected set; } = new DefaultLayout();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Toast 组件实例
|
||||||
|
/// </summary>
|
||||||
|
protected Toast Toast { get; set; } = new Toast();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -47,5 +52,13 @@ namespace Bootstrap.Admin.Components
|
||||||
var menu = menus.FirstOrDefault(menu => path.Contains(menu.Url.ToBlazorMenuUrl()));
|
var menu = menus.FirstOrDefault(menu => path.Contains(menu.Url.ToBlazorMenuUrl()));
|
||||||
if (menu != null) TabSet?.AddTab(menu);
|
if (menu != null) TabSet?.AddTab(menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="title"></param>
|
||||||
|
/// <param name="text"></param>
|
||||||
|
/// <param name="cate"></param>
|
||||||
|
public void ShowMessage(string title, string text, ToastCategory cate = ToastCategory.Success) => Toast.ShowMessage(title, text, cate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
namespace Bootstrap.Admin.Components
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 弹窗组件基类
|
||||||
|
/// </summary>
|
||||||
|
public class AlertBase : ModalBase
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment? AlertBody { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获得/设置 弹窗 Footer 代码块
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment? AlertFooter { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获得/设置 是否自动关闭 默认为 true
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public bool AutoClose { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获得/设置 自动关闭时长 默认 1500 毫秒
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public int Interval { get; set; } = 1500;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 控件渲染完毕后回调方法
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="firstRender"></param>
|
||||||
|
protected override void OnAfterRender(bool firstRender)
|
||||||
|
{
|
||||||
|
base.OnAfterRender(firstRender);
|
||||||
|
|
||||||
|
if (_show)
|
||||||
|
{
|
||||||
|
_show = false;
|
||||||
|
Toggle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _show;
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="title"></param>
|
||||||
|
public void Show(string title)
|
||||||
|
{
|
||||||
|
Title = title;
|
||||||
|
_show = true;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using Microsoft.AspNetCore.Components.Forms;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Bootstrap.Admin.Components
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// BootstrapAdminDataAnnotationsValidator 验证组件
|
||||||
|
/// </summary>
|
||||||
|
public class BootstrapAdminDataAnnotationsValidator : ComponentBase
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 获得/设置 当前编辑数据上下文
|
||||||
|
/// </summary>
|
||||||
|
[CascadingParameter]
|
||||||
|
EditContext? CurrentEditContext { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获得/设置 当前编辑窗体上下文
|
||||||
|
/// </summary>
|
||||||
|
[CascadingParameter]
|
||||||
|
public LgbEditFormBase? EditForm { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 初始化方法
|
||||||
|
/// </summary>
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
if (CurrentEditContext == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"{nameof(DataAnnotationsValidator)} requires a cascading " +
|
||||||
|
$"parameter of type {nameof(EditContext)}. For example, you can use {nameof(DataAnnotationsValidator)} " +
|
||||||
|
$"inside an EditForm.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EditForm == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"{nameof(DataAnnotationsValidator)} requires a cascading " +
|
||||||
|
$"parameter of type {nameof(LgbEditFormBase)}. For example, you can use {nameof(BootstrapAdminDataAnnotationsValidator)} " +
|
||||||
|
$"inside an EditForm.");
|
||||||
|
}
|
||||||
|
|
||||||
|
CurrentEditContext.AddBootstrapAdminDataAnnotationsValidation(EditForm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
using Microsoft.AspNetCore.Components.Forms;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Bootstrap.Admin.Components
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public static class BootstrapAdminEditContextDataAnnotationsExtensions
|
||||||
|
{
|
||||||
|
private static ConcurrentDictionary<(Type ModelType, string FieldName), PropertyInfo> _propertyInfoCache = new ConcurrentDictionary<(Type, string), PropertyInfo>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="editContext">The <see cref="EditContext"/>.</param>
|
||||||
|
/// <param name="editForm"></param>
|
||||||
|
public static EditContext AddBootstrapAdminDataAnnotationsValidation(this EditContext editContext, LgbEditFormBase editForm)
|
||||||
|
{
|
||||||
|
if (editContext == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(editContext));
|
||||||
|
}
|
||||||
|
|
||||||
|
var messages = new ValidationMessageStore(editContext);
|
||||||
|
|
||||||
|
editContext.OnValidationRequested +=
|
||||||
|
(sender, eventArgs) => ValidateModel(sender as EditContext, messages, editForm);
|
||||||
|
|
||||||
|
editContext.OnFieldChanged +=
|
||||||
|
(sender, eventArgs) => ValidateField(editContext, messages, eventArgs.FieldIdentifier, editForm);
|
||||||
|
|
||||||
|
return editContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ValidateModel(EditContext? editContext, ValidationMessageStore messages, LgbEditFormBase editForm)
|
||||||
|
{
|
||||||
|
if (editContext != null)
|
||||||
|
{
|
||||||
|
var validationContext = new ValidationContext(editContext.Model);
|
||||||
|
var validationResults = new List<ValidationResult>();
|
||||||
|
Validator.TryValidateObject(editContext.Model, validationContext, validationResults, true);
|
||||||
|
editForm.ValidateObject(editContext.Model, validationContext, validationResults);
|
||||||
|
|
||||||
|
messages.Clear();
|
||||||
|
|
||||||
|
foreach (var validationResult in validationResults)
|
||||||
|
{
|
||||||
|
if (!validationResult.MemberNames.Any())
|
||||||
|
{
|
||||||
|
messages.Add(new FieldIdentifier(editContext.Model, fieldName: string.Empty), validationResult.ErrorMessage);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var memberName in validationResult.MemberNames)
|
||||||
|
{
|
||||||
|
messages.Add(editContext.Field(memberName), validationResult.ErrorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
editContext.NotifyValidationStateChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ValidateField(EditContext editContext, ValidationMessageStore messages, in FieldIdentifier fieldIdentifier, LgbEditFormBase editForm)
|
||||||
|
{
|
||||||
|
if (TryGetValidatableProperty(fieldIdentifier, out var propertyInfo))
|
||||||
|
{
|
||||||
|
var propertyValue = propertyInfo.GetValue(fieldIdentifier.Model);
|
||||||
|
var validationContext = new ValidationContext(fieldIdentifier.Model)
|
||||||
|
{
|
||||||
|
MemberName = propertyInfo.Name
|
||||||
|
};
|
||||||
|
var results = new List<ValidationResult>();
|
||||||
|
|
||||||
|
Validator.TryValidateProperty(propertyValue, validationContext, results);
|
||||||
|
editForm.ValidateProperty(propertyValue, validationContext, results);
|
||||||
|
|
||||||
|
messages.Clear(fieldIdentifier);
|
||||||
|
messages.Add(fieldIdentifier, results.Select(result => result.ErrorMessage));
|
||||||
|
|
||||||
|
editContext.NotifyValidationStateChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
internal static bool TryGetValidatableProperty(in FieldIdentifier fieldIdentifier, out PropertyInfo propertyInfo)
|
||||||
|
{
|
||||||
|
var cacheKey = (ModelType: fieldIdentifier.Model.GetType(), fieldIdentifier.FieldName);
|
||||||
|
if (!_propertyInfoCache.TryGetValue(cacheKey, out propertyInfo))
|
||||||
|
{
|
||||||
|
propertyInfo = cacheKey.ModelType.GetProperty(cacheKey.FieldName);
|
||||||
|
|
||||||
|
_propertyInfoCache[cacheKey] = propertyInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
return propertyInfo != null;
|
||||||
|
}
|
||||||
|
#nullable restore
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,5 +26,13 @@ namespace Bootstrap.Admin.Components
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Inject]
|
[Inject]
|
||||||
protected IJSRuntime? JSRuntime { get; set; }
|
protected IJSRuntime? JSRuntime { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="title"></param>
|
||||||
|
/// <param name="text"></param>
|
||||||
|
/// <param name="cate"></param>
|
||||||
|
protected void ShowMessage(string title, string text, ToastCategory cate = ToastCategory.Success) => Layout.ShowMessage(title, text, cate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
namespace Bootstrap.Admin.Components
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// CheckBox 组件状态枚举值
|
||||||
|
/// </summary>
|
||||||
|
public enum CheckBoxState
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 未选中
|
||||||
|
/// </summary>
|
||||||
|
UnChecked,
|
||||||
|
/// <summary>
|
||||||
|
/// 选中
|
||||||
|
/// </summary>
|
||||||
|
Checked,
|
||||||
|
/// <summary>
|
||||||
|
/// 混合模式
|
||||||
|
/// </summary>
|
||||||
|
Mixed
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public static class CheckBoxStateExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="state"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string ToCss(this CheckBoxState state)
|
||||||
|
{
|
||||||
|
var ret = "false";
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case CheckBoxState.Checked:
|
||||||
|
ret = "true";
|
||||||
|
break;
|
||||||
|
case CheckBoxState.Mixed:
|
||||||
|
ret = "mixed";
|
||||||
|
break;
|
||||||
|
case CheckBoxState.UnChecked:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,79 @@
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Bootstrap.Admin.Components
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Checkbox 组件基类
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TItem"></typeparam>
|
||||||
|
public class CheckboxBase<TItem> : ComponentBase
|
||||||
|
{
|
||||||
|
#nullable disable
|
||||||
|
/// <summary>
|
||||||
|
/// 获得/设置 数据绑定项
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public TItem Item { get; set; }
|
||||||
|
#nullable restore
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获得/设置 显示文本
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public string Text { get; set; } = "";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获得/设置 是否被选中
|
||||||
|
/// </summary>
|
||||||
|
protected bool Checked { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 勾选回调方法
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public Action<TItem, bool> ToggleCallback { get; set; } = new Action<TItem, bool>((v, c) => { });
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public Func<TItem, CheckBoxState> SetCheckCallback { get; set; } = new Func<TItem, CheckBoxState>(item => CheckBoxState.UnChecked);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
protected override void OnParametersSet()
|
||||||
|
{
|
||||||
|
State = SetCheckCallback(Item);
|
||||||
|
Checked = State == CheckBoxState.Checked;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public CheckBoxState State { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
protected string RenderStateCss()
|
||||||
|
{
|
||||||
|
var ret = "form-checkbox";
|
||||||
|
switch (State)
|
||||||
|
{
|
||||||
|
case CheckBoxState.Mixed:
|
||||||
|
ret = "form-checkbox is-indeterminate";
|
||||||
|
break;
|
||||||
|
case CheckBoxState.Checked:
|
||||||
|
ret = "form-checkbox is-checked";
|
||||||
|
break;
|
||||||
|
case CheckBoxState.UnChecked:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
namespace Bootstrap.Admin.Components
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public enum Color
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
Primary,
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
Secondary,
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
Success,
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
Danger,
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
Warning,
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
Info,
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
Light,
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
Dark,
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
White,
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
Transparent
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public static class ColorExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="color"></param>
|
||||||
|
/// <param name="prefix"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string ToCss(this Color color, string prefix = "")
|
||||||
|
{
|
||||||
|
string ret = color.ToString().ToLowerInvariant();
|
||||||
|
return string.IsNullOrEmpty(prefix) ? ret : $"{prefix}-{ret}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
using Bootstrap.Security;
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Bootstrap.Admin.Components
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 字典表维护组件
|
||||||
|
/// </summary>
|
||||||
|
public class DictsBase : ComponentBase
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
protected BootstrapDict QueryModel { get; set; } = new BootstrapDict() { Define = -1 };
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
protected List<SelectedItem> DefineItems { get; set; } = new List<SelectedItem>(new SelectedItem[] { new SelectedItem() { Text = "系统使用", Value = "0" }, new SelectedItem() { Text = "自定义", Value = "1" } });
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
protected List<SelectedItem> QueryDefine { get; set; } = new List<SelectedItem>(new SelectedItem[] { new SelectedItem() { Text = "全部", Value = "-1", Active = true }, new SelectedItem() { Text = "系统使用", Value = "0" }, new SelectedItem() { Text = "自定义", Value = "1" } });
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pageIndex"></param>
|
||||||
|
/// <param name="pageItems"></param>
|
||||||
|
protected QueryData<BootstrapDict> Query(int pageIndex, int pageItems)
|
||||||
|
{
|
||||||
|
var data = DataAccess.DictHelper.RetrieveDicts();
|
||||||
|
if (QueryModel.Define != -1) data = data.Where(d => d.Define == QueryModel.Define);
|
||||||
|
if (QueryModel.Name != "") data = data.Where(d => d.Name == QueryModel.Name);
|
||||||
|
if (QueryModel.Category != "") data = data.Where(d => d.Category == QueryModel.Category);
|
||||||
|
var totalCount = data.Count();
|
||||||
|
var items = data.Skip((pageIndex - 1) * pageItems).Take(pageItems);
|
||||||
|
return new QueryData<BootstrapDict>() { Items = items, TotalCount = totalCount, PageIndex = pageIndex, PageItems = pageItems };
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
protected BootstrapDict Add()
|
||||||
|
{
|
||||||
|
return new BootstrapDict();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
protected bool Save(BootstrapDict dict) => DataAccess.DictHelper.Save(dict);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
protected bool Delete(IEnumerable<BootstrapDict> items) => DataAccess.DictHelper.Delete(items.Select(item => item.Id ?? ""));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,153 @@
|
||||||
|
using Bootstrap.Admin.Shared;
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Bootstrap.Admin.Components
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 可编辑页面组件包含查询与数据表格
|
||||||
|
/// </summary>
|
||||||
|
public class EditPageBase<TItem> : BootstrapComponentBase
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public string Id { get; set; } = "";
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public TItem QueryModel { get; set; }
|
||||||
|
#nullable restore
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查询模板
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment<TItem>? QueryBody { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查询按钮回调方法
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public Func<int, int, QueryData<TItem>>? OnQuery { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment? TableHeader { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment<TItem>? RowTemplate { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 按钮模板
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment<TItem>? ButtonTemplate { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment? TableFooter { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment<TItem>? EditTemplate { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
protected Table<TItem>? Table { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 新建按钮回调方法
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public Func<TItem>? OnAdd { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 编辑按钮回调方法
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public Action<TItem>? OnEdit { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 保存按钮回调方法
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public Func<TItem, bool>? OnSave { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 删除按钮回调方法
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public Func<IEnumerable<TItem>, bool>? OnDelete { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 组件初始化方法
|
||||||
|
/// </summary>
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(Id)) throw new InvalidOperationException($"The property {nameof(Id)} can't set to Null");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 数据表格内删除按钮方法
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item"></param>
|
||||||
|
protected void Delete(TItem item)
|
||||||
|
{
|
||||||
|
if (Table != null)
|
||||||
|
{
|
||||||
|
Table.SelectedItems.Clear();
|
||||||
|
Table.SelectedItems.Add(item);
|
||||||
|
Table.Delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
protected void Edit(TItem item)
|
||||||
|
{
|
||||||
|
if (Table != null)
|
||||||
|
{
|
||||||
|
Table.SelectedItems.Clear();
|
||||||
|
Table.SelectedItems.Add(item);
|
||||||
|
Table.Edit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
protected void Query()
|
||||||
|
{
|
||||||
|
// 查询控件按钮触发此事件
|
||||||
|
if (OnQuery != null && Table != null)
|
||||||
|
{
|
||||||
|
Table.Query(OnQuery.Invoke(1, Table.PageItems));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pageIndex"></param>
|
||||||
|
/// <param name="pageItems"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
protected QueryData<TItem> QueryData(int pageIndex, int pageItems) => OnQuery?.Invoke(pageIndex, pageItems) ?? new QueryData<TItem>();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Bootstrap.Admin.Components
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// IValidComponent 接口
|
||||||
|
/// </summary>
|
||||||
|
public interface IValidateComponent
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 数据验证方法
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="propertyValue"></param>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
/// <param name="results"></param>
|
||||||
|
void ValidateProperty(object? propertyValue, ValidationContext context, List<ValidationResult> results);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 显示或者隐藏提示信息方法
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="results"></param>
|
||||||
|
/// <param name="validProperty"></param>
|
||||||
|
void ToggleMessage(IEnumerable<ValidationResult> results, bool validProperty);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,119 @@
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using Microsoft.AspNetCore.Components.Forms;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Bootstrap.Admin.Components
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// LgbEditForm 组件
|
||||||
|
/// </summary>
|
||||||
|
public class LgbEditFormBase : ComponentBase
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a collection of additional attributes that will be applied to the created <c>form</c> element.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter(CaptureUnmatchedValues = true)]
|
||||||
|
public IReadOnlyDictionary<string, object>? AdditionalAttributes { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies the top-level model object for the form. An edit context will
|
||||||
|
/// be constructed for this model. If using this parameter, do not also supply
|
||||||
|
/// a value for <see cref="EditContext"/>.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public object? Model { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies the content to be rendered inside this
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment? ChildContent { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A callback that will be invoked when the form is submitted.
|
||||||
|
/// If using this parameter, you are responsible for triggering any validation
|
||||||
|
/// manually, e.g., by calling <see cref="EditContext.Validate"/>.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter] public EventCallback<EditContext> OnSubmit { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A callback that will be invoked when the form is submitted and the
|
||||||
|
/// <see cref="EditContext"/> is determined to be valid.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter] public EventCallback<EditContext> OnValidSubmit { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A callback that will be invoked when the form is submitted and the
|
||||||
|
/// <see cref="EditContext"/> is determined to be invalid.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter] public EventCallback<EditContext> OnInvalidSubmit { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证组件缓存 静态全局提高性能
|
||||||
|
/// </summary>
|
||||||
|
private static ConcurrentDictionary<(Type ModelType, string FieldName), IValidateComponent> _validatorCache = new ConcurrentDictionary<(Type, string), IValidateComponent>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加数据验证组件到 EditForm 中
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="key"></param>
|
||||||
|
/// <param name="comp"></param>
|
||||||
|
public void AddValidator((Type ModelType, string FieldName) key, IValidateComponent comp) => _validatorCache.AddOrUpdate(key, k => comp, (k, c) => c = comp);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// EditModel 数据模型验证方法
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="model"></param>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
/// <param name="results"></param>
|
||||||
|
public void ValidateObject(object model, ValidationContext context, List<ValidationResult> results)
|
||||||
|
{
|
||||||
|
// 遍历所有可验证组件进行数据验证
|
||||||
|
foreach (var key in _validatorCache)
|
||||||
|
{
|
||||||
|
if (key.Key.ModelType == context.ObjectType)
|
||||||
|
{
|
||||||
|
if (BootstrapAdminEditContextDataAnnotationsExtensions.TryGetValidatableProperty(new FieldIdentifier(model, key.Key.FieldName), out var propertyInfo))
|
||||||
|
{
|
||||||
|
// 设置其关联属性字段
|
||||||
|
var propertyValue = propertyInfo.GetValue(model);
|
||||||
|
context.MemberName = propertyInfo.Name;
|
||||||
|
|
||||||
|
var validator = _validatorCache[key.Key];
|
||||||
|
|
||||||
|
// 数据验证
|
||||||
|
validator.ValidateProperty(propertyValue, context, results);
|
||||||
|
validator.ToggleMessage(results, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 字段验证方法
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="propertyValue"></param>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
/// <param name="results"></param>
|
||||||
|
public void ValidateProperty(object? propertyValue, ValidationContext context, List<ValidationResult> results)
|
||||||
|
{
|
||||||
|
if (_validatorCache.TryGetValue((context.ObjectType, context.MemberName), out var validator))
|
||||||
|
{
|
||||||
|
validator.ValidateProperty(propertyValue, context, results);
|
||||||
|
validator.ToggleMessage(results, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="firstRender"></param>
|
||||||
|
protected override void OnAfterRender(bool firstRender)
|
||||||
|
{
|
||||||
|
base.OnAfterRender(firstRender);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,135 @@
|
||||||
|
using Bootstrap.Admin.Extensions;
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using Microsoft.AspNetCore.Components.Forms;
|
||||||
|
using Microsoft.AspNetCore.Components.Rendering;
|
||||||
|
using Microsoft.JSInterop;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Bootstrap.Admin.Components
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// LgbInputText 组件
|
||||||
|
/// </summary>
|
||||||
|
public class LgbInputText : InputText, IValidateComponent
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
[Inject]
|
||||||
|
protected IJSRuntime? JSRuntime { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
[CascadingParameter]
|
||||||
|
public LgbEditFormBase? EditForm { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public string Id { get; set; } = "";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment? ChildContent { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
protected string ErrorMessage { get; set; } = "";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
protected string ValidCss { get; set; } = "";
|
||||||
|
|
||||||
|
private string _tooltipMethod = "";
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="propertyValue"></param>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
/// <param name="results"></param>
|
||||||
|
public void ValidateProperty(object? propertyValue, ValidationContext context, List<ValidationResult> results)
|
||||||
|
{
|
||||||
|
Rules.ToList().ForEach(validator => validator.Validate(propertyValue, context, results));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 显示/隐藏验证结果方法
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="results"></param>
|
||||||
|
/// <param name="validProperty">是否对本属性进行数据验证</param>
|
||||||
|
public void ToggleMessage(IEnumerable<ValidationResult> results, bool validProperty)
|
||||||
|
{
|
||||||
|
if (results.Where(r => r.MemberNames.Any(m => m == FieldIdentifier.FieldName)).Any())
|
||||||
|
{
|
||||||
|
ErrorMessage = results.First().ErrorMessage;
|
||||||
|
ValidCss = "is-invalid";
|
||||||
|
|
||||||
|
// 控件自身数据验证时显示 tooltip
|
||||||
|
// EditForm 数据验证时调用 tooltip('enable') 保证 tooltip 组件生成
|
||||||
|
// 调用 tooltip('hide') 后导致鼠标悬停时 tooltip 无法正常显示
|
||||||
|
_tooltipMethod = validProperty ? "show" : "enable";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ErrorMessage = "";
|
||||||
|
ValidCss = "is-valid";
|
||||||
|
_tooltipMethod = "dispose";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
EditForm?.AddValidator((FieldIdentifier.Model.GetType(), FieldIdentifier.FieldName), this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="firstRender"></param>
|
||||||
|
protected override void OnAfterRender(bool firstRender)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(_tooltipMethod) && !string.IsNullOrEmpty(Id))
|
||||||
|
{
|
||||||
|
JSRuntime.Tooltip(Id, _tooltipMethod);
|
||||||
|
_tooltipMethod = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public ICollection<ValidatorComponentBase> Rules { get; } = new HashSet<ValidatorComponentBase>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="builder"></param>
|
||||||
|
protected override void BuildRenderTree(RenderTreeBuilder builder)
|
||||||
|
{
|
||||||
|
builder.OpenElement(0, "input");
|
||||||
|
builder.AddMultipleAttributes(1, AdditionalAttributes);
|
||||||
|
builder.AddAttribute(2, "class", $"{CssClass} {ValidCss}");
|
||||||
|
builder.AddAttribute(3, "value", BindConverter.FormatValue(CurrentValue));
|
||||||
|
builder.AddAttribute(4, "oninput", EventCallback.Factory.CreateBinder<string>(this, __value => CurrentValueAsString = __value, CurrentValueAsString));
|
||||||
|
if (!string.IsNullOrEmpty(ErrorMessage)) builder.AddAttribute(5, "data-original-title", ErrorMessage);
|
||||||
|
if (!string.IsNullOrEmpty(Id)) builder.AddAttribute(6, "id", Id);
|
||||||
|
builder.OpenComponent<CascadingValue<LgbInputText>>(7);
|
||||||
|
builder.AddAttribute(8, "IsFixed", true);
|
||||||
|
builder.AddAttribute(9, "Value", this);
|
||||||
|
builder.AddAttribute(10, "ChildContent", ChildContent);
|
||||||
|
builder.CloseComponent();
|
||||||
|
builder.CloseElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,6 +15,18 @@ namespace Bootstrap.Admin.Components
|
||||||
[Inject]
|
[Inject]
|
||||||
protected IJSRuntime? JSRuntime { get; set; }
|
protected IJSRuntime? JSRuntime { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment? ModalBody { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获得/设置 弹窗 Footer 代码块
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment? ModalFooter { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -28,22 +40,28 @@ namespace Bootstrap.Admin.Components
|
||||||
public string Title { get; set; } = "未设置";
|
public string Title { get; set; } = "未设置";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
/// 获得/设置 是否允许点击后台关闭弹窗 默认为 false
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public RenderFragment? ModalBody { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public bool Backdrop { get; set; }
|
public bool Backdrop { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
/// 获得/设置 弹窗大小
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public RenderFragment? ModalFooter { get; set; }
|
public ModalSize Size { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获得/设置 是否垂直居中 默认为 true
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public bool IsCentered { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获得/设置 是否显示 Footer 默认为 true
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public bool ShowFooter { get; set; } = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
|
@ -56,5 +74,60 @@ namespace Bootstrap.Admin.Components
|
||||||
JSRuntime.InitModal();
|
JSRuntime.InitModal();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 输出窗口大小样式
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
protected string RenderModalSize()
|
||||||
|
{
|
||||||
|
var ret = "";
|
||||||
|
switch (Size)
|
||||||
|
{
|
||||||
|
case ModalSize.Default:
|
||||||
|
break;
|
||||||
|
case ModalSize.Small:
|
||||||
|
ret = "modal-sm";
|
||||||
|
break;
|
||||||
|
case ModalSize.Large:
|
||||||
|
ret = "modal-lg";
|
||||||
|
break;
|
||||||
|
case ModalSize.ExtraLarge:
|
||||||
|
ret = "modal-xl";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public void Toggle()
|
||||||
|
{
|
||||||
|
JSRuntime.ToggleModal($"#{Id}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 弹窗大小
|
||||||
|
/// </summary>
|
||||||
|
public enum ModalSize
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
Default,
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
Small,
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
Large,
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
ExtraLarge,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
using Microsoft.JSInterop;
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Bootstrap.Admin.Components
|
namespace Bootstrap.Admin.Components
|
||||||
{
|
{
|
||||||
|
@ -10,25 +10,13 @@ namespace Bootstrap.Admin.Components
|
||||||
public class PaginationBase : ComponentBase
|
public class PaginationBase : ComponentBase
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获得/设置 页码总数
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public int PageCount { get; set; } = 0;
|
public int TotalCount { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获得/设置 每页显示数据数量
|
///
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public int PageItems { get; set; } = 20;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获得/设置 数据总数
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public int ItemsCount { get; set; } = 0;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获得/设置 当前页码
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public int PageIndex { get; set; } = 1;
|
public int PageIndex { get; set; } = 1;
|
||||||
|
@ -36,25 +24,38 @@ namespace Bootstrap.Admin.Components
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Inject]
|
[Parameter]
|
||||||
protected IJSRuntime? JSRuntime { get; set; }
|
public int PageItems { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获得/设置 页码总数
|
||||||
|
/// </summary>
|
||||||
|
public int PageCount
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return (int)Math.Ceiling(TotalCount * 1.0 / PageItems); ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Action<int> ClickPageCallback { get; set; } = new Action<int>(i => { });
|
[Parameter]
|
||||||
|
public Action<int, int> OnPageClick { get; set; } = new Action<int, int>((pageIndex, pageItems) => { });
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Action PageItemsChangeCallback { get; set; } = new Action(() => { });
|
[Parameter]
|
||||||
|
public Action<int> OnPageItemsChange { get; set; } = new Action<int>((pageItems) => { });
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected void MovePrev()
|
protected void MovePrev()
|
||||||
{
|
{
|
||||||
if (PageIndex > 1) ClickPageCallback(PageIndex - 1);
|
if (PageIndex > 1) OnPageClick(PageIndex - 1, PageItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -62,7 +63,7 @@ namespace Bootstrap.Admin.Components
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected void MoveNext()
|
protected void MoveNext()
|
||||||
{
|
{
|
||||||
if (PageIndex < PageCount) ClickPageCallback(PageIndex + 1);
|
if (PageIndex < PageCount) OnPageClick(PageIndex + 1, PageItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -72,7 +73,36 @@ namespace Bootstrap.Admin.Components
|
||||||
protected void ClickItem(int pageItems)
|
protected void ClickItem(int pageItems)
|
||||||
{
|
{
|
||||||
PageItems = pageItems;
|
PageItems = pageItems;
|
||||||
PageItemsChangeCallback();
|
OnPageItemsChange(PageItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
protected IEnumerable<int> GetPages()
|
||||||
|
{
|
||||||
|
var pages = new List<int>() { 20, 40, 80, 100, 200 };
|
||||||
|
var ret = new List<int>();
|
||||||
|
for (int i = 0; i < pages.Count; i++)
|
||||||
|
{
|
||||||
|
ret.Add(pages[i]);
|
||||||
|
if (pages[i] >= TotalCount) break;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="totalCount"></param>
|
||||||
|
/// <param name="pageIndex"></param>
|
||||||
|
/// <param name="pageItems"></param>
|
||||||
|
public void Update(int totalCount, int pageIndex, int pageItems)
|
||||||
|
{
|
||||||
|
TotalCount = totalCount;
|
||||||
|
PageIndex = pageIndex;
|
||||||
|
PageItems = pageItems;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
namespace Bootstrap.Admin.Components
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 组件位置枚举类型
|
||||||
|
/// </summary>
|
||||||
|
public enum Placement
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
Default,
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
Top,
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
TopLeft,
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
TopRight,
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
Bottom,
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
BottomLeft,
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
BottomRight,
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
Center
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public static class PlacementExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="placement"></param>
|
||||||
|
/// <param name="prefix"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static string ToCss(this Placement placement, string prefix = "")
|
||||||
|
{
|
||||||
|
string ret = "";
|
||||||
|
switch (placement)
|
||||||
|
{
|
||||||
|
case Placement.Center:
|
||||||
|
ret = "center";
|
||||||
|
break;
|
||||||
|
case Placement.Top:
|
||||||
|
ret = "top";
|
||||||
|
break;
|
||||||
|
case Placement.TopLeft:
|
||||||
|
ret = "top-left";
|
||||||
|
break;
|
||||||
|
case Placement.TopRight:
|
||||||
|
ret = "top-right";
|
||||||
|
break;
|
||||||
|
case Placement.Bottom:
|
||||||
|
ret = "bottom";
|
||||||
|
break;
|
||||||
|
case Placement.BottomLeft:
|
||||||
|
ret = "bottom-left";
|
||||||
|
break;
|
||||||
|
case Placement.BottomRight:
|
||||||
|
case Placement.Default:
|
||||||
|
ret = "bottom-right";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return string.IsNullOrEmpty(prefix) ? ret : $"{prefix}-{ret}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Bootstrap.Admin.Components
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 查询组件
|
||||||
|
/// </summary>
|
||||||
|
public class QueryBase<TItem> : ComponentBase
|
||||||
|
{
|
||||||
|
private readonly string _defaultTitle = "查询条件";
|
||||||
|
private readonly string _defaultText = "查询";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查询组件标题 默认为 查询条件
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public string Title { get; set; } = "";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查询按钮显示文字 默认为 查询
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public string Text { get; set; } = "";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查询组件模板
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment<TItem>? ChildContent { get; set; }
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public TItem QueryModel { get; set; }
|
||||||
|
#nullable restore
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public EventCallback<TItem> QueryModelChanged { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查询按钮回调方法
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public Action? OnQuery { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 参数设置方法
|
||||||
|
/// </summary>
|
||||||
|
protected override void OnParametersSet()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(Title)) Title = _defaultTitle;
|
||||||
|
if (string.IsNullOrEmpty(Text)) Text = _defaultText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Bootstrap.Admin.Components
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
public class QueryData<T>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<T> Items { get; set; } = new T[0];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public int TotalCount { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public int PageIndex { get; set; } = 1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public int PageItems { get; set; } = 20;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
namespace Bootstrap.Admin.Components
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public class RequiredValidator : ValidatorComponentBase
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 获得/设置 是否允许空字符串 默认 false 不允许
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public bool AllowEmptyString { get; set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="propertyValue"></param>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
/// <param name="results"></param>
|
||||||
|
public override void Validate(object? propertyValue, ValidationContext context, List<ValidationResult> results)
|
||||||
|
{
|
||||||
|
var val = propertyValue?.ToString() ?? "";
|
||||||
|
if (!AllowEmptyString && val == string.Empty)
|
||||||
|
{
|
||||||
|
results.Add(new ValidationResult(ErrorMessage, new string[] { context.MemberName }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,27 +1,23 @@
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Bootstrap.Admin.Components
|
namespace Bootstrap.Admin.Components
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Select 组件基类
|
/// Select 组件基类
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class SelectBase : ComponentBase
|
public class SelectBase<TItem> : ComponentBase
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
/// 获得/设置 控件 ID
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string Id { get; set; } = "";
|
public string Id { get; set; } = "";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
/// 获得/设置 背景显示文字
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public RenderFragment? ChildContent { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string PlaceHolder { get; set; } = "请选择 ...";
|
public string PlaceHolder { get; set; } = "请选择 ...";
|
||||||
|
@ -29,49 +25,64 @@ namespace Bootstrap.Admin.Components
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 当前选择项实例
|
/// 当前选择项实例
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public SelectItemBase? SelectedItem { get; set; }
|
public SelectedItem SelectedItem { get; set; } = new SelectedItem();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获得/设置 绑定数据集
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public List<SelectedItem> Items { get; set; } = new List<SelectedItem>();
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
private TItem _value;
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public TItem SelectedValue
|
||||||
|
{
|
||||||
|
get { return _value; }
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_value = value;
|
||||||
|
Items.ForEach(t =>
|
||||||
|
{
|
||||||
|
t.Active = t.Value == _value.ToString();
|
||||||
|
if (t.Active) SelectedItem = t;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#nullable restore
|
||||||
|
|
||||||
///<summary>
|
///<summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public int SelectedValue { get; set; }
|
public EventCallback<TItem> SelectedValueChanged { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter]
|
protected override void OnInitialized()
|
||||||
public EventCallback<int> SelectedValueChanged { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public Action<SelectItemBase>? ClickItemCallback { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public Action<SelectItemBase>? ActiveChanged { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="firstRender"></param>
|
|
||||||
protected override void OnAfterRender(bool firstRender)
|
|
||||||
{
|
{
|
||||||
if (firstRender)
|
if (!SelectedItem.Active)
|
||||||
{
|
{
|
||||||
ClickItemCallback = UpdateItem;
|
SelectedItem = Items.FirstOrDefault(item => item.Active) ?? Items.First();
|
||||||
ActiveChanged = UpdateItem;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="item"></param>
|
[Parameter]
|
||||||
protected void UpdateItem(SelectItemBase item)
|
public Action<SelectedItem> SelectedItemChanged { get; set; } = new Action<SelectedItem>(v => { });
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public void ItemClickCallback(SelectedItem item)
|
||||||
{
|
{
|
||||||
|
SelectedItemChanged(item);
|
||||||
SelectedItem = item;
|
SelectedItem = item;
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
using Bootstrap.Admin.Shared;
|
using Microsoft.AspNetCore.Components;
|
||||||
using Microsoft.AspNetCore.Components;
|
using System;
|
||||||
|
|
||||||
namespace Bootstrap.Admin.Components
|
namespace Bootstrap.Admin.Components
|
||||||
{
|
{
|
||||||
|
@ -12,44 +12,12 @@ namespace Bootstrap.Admin.Components
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public bool Active { get; set; }
|
public SelectedItem Item { get; set; } = new SelectedItem();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public string Text { get; set; } = "";
|
public Action<SelectedItem> ItemClickCallback { get; set; } = new Action<SelectedItem>(SelectedItem => { });
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[Parameter]
|
|
||||||
public string Value { get; set; } = "";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[CascadingParameter]
|
|
||||||
public Select? Select { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="firstRender"></param>
|
|
||||||
protected override void OnAfterRender(bool firstRender)
|
|
||||||
{
|
|
||||||
if (firstRender)
|
|
||||||
{
|
|
||||||
if (Active) Select?.ActiveChanged?.Invoke(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
protected void ClickItem()
|
|
||||||
{
|
|
||||||
Select?.ClickItemCallback?.Invoke(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
namespace Bootstrap.Admin.Components
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 选项类
|
||||||
|
/// </summary>
|
||||||
|
public class SelectedItem
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 获得/设置 显示名称
|
||||||
|
/// </summary>
|
||||||
|
public string Text { get; set; } = "";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获得/设置 选项值
|
||||||
|
/// </summary>
|
||||||
|
public string Value { get; set; } = "";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获得/设置 是否选中
|
||||||
|
/// </summary>
|
||||||
|
public bool Active { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
|
||||||
|
namespace Bootstrap.Admin.Components
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public class StringLengthValidator : ValidatorComponentBase
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public int Length { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="propertyValue"></param>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
/// <param name="results"></param>
|
||||||
|
public override void Validate(object? propertyValue, ValidationContext context, List<ValidationResult> results)
|
||||||
|
{
|
||||||
|
var val = propertyValue?.ToString() ?? "";
|
||||||
|
if (val.Length > Length) results.Add(new ValidationResult(ErrorMessage, new string[] { context.MemberName }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using Microsoft.AspNetCore.Components.Forms;
|
||||||
|
|
||||||
|
namespace Bootstrap.Admin.Components
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 数据绑定提交弹窗组件
|
||||||
|
/// </summary>
|
||||||
|
public class SubmitModalBase<TItem> : ModalBase
|
||||||
|
{
|
||||||
|
#nullable disable
|
||||||
|
/// <summary>
|
||||||
|
/// 获得/设置 弹窗绑定数据实体
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public TItem Model { get; set; }
|
||||||
|
#nullable restore
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public EventCallback<TItem> ModelChanged { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A callback that will be invoked when the form is submitted.
|
||||||
|
/// If using this parameter, you are responsible for triggering any validation
|
||||||
|
/// manually, e.g., by calling <see cref="EditContext.Validate"/>.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter] public EventCallback<EditContext> OnSubmit { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A callback that will be invoked when the form is submitted and the
|
||||||
|
/// <see cref="EditContext"/> is determined to be valid.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter] public EventCallback<EditContext> OnValidSubmit { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A callback that will be invoked when the form is submitted and the
|
||||||
|
/// <see cref="EditContext"/> is determined to be invalid.
|
||||||
|
/// </summary>
|
||||||
|
[Parameter] public EventCallback<EditContext> OnInvalidSubmit { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,21 +1,27 @@
|
||||||
using Bootstrap.Admin.Extensions;
|
using Bootstrap.Admin.Shared;
|
||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
using Microsoft.JSInterop;
|
using Microsoft.AspNetCore.Components.Forms;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Bootstrap.Admin.Components
|
namespace Bootstrap.Admin.Components
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 表格组件类
|
/// 表格组件类
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class TableBase<TItem> : ComponentBase
|
public class TableBase<TItem> : BootstrapComponentBase
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Inject]
|
public const int DefaultPageItems = 20;
|
||||||
protected IJSRuntime? JSRuntime { get; set; }
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public string Id { get; set; } = "";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
|
@ -29,6 +35,18 @@ namespace Bootstrap.Admin.Components
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public RenderFragment<TItem>? RowTemplate { get; set; }
|
public RenderFragment<TItem>? RowTemplate { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 按钮模板
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment<TItem>? ButtonTemplate { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public RenderFragment<TItem>? EditTemplate { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -38,30 +56,271 @@ namespace Bootstrap.Admin.Components
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
protected IEnumerable<TItem> Items { get; set; } = new TItem[0];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public List<TItem> SelectedItems { get; } = new List<TItem>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获得/设置 是否显示行号
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public bool ShowLineNo { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获得/设置 是否显示选择列 默认为 true
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public bool ShowCheckbox { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获得/设置 是否显示按钮列 默认为 true
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public bool ShowButtons { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获得/设置 是否显示工具栏 默认为 true
|
||||||
|
/// </summary>
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public bool ShowToolBar { get; set; } = true;
|
public bool ShowToolBar { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获得/设置 按钮列 Header 文本 默认为 操作
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public string ButtonTemplateHeaderText { get; set; } = "操作";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 点击翻页回调方法
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public Func<int, int, QueryData<TItem>>? OnQuery { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 新建按钮回调方法
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public Func<TItem>? OnAdd { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 编辑按钮回调方法
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public Action<TItem>? OnEdit { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 保存按钮回调方法
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public Func<TItem, bool>? OnSave { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 删除按钮回调方法
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public Func<IEnumerable<TItem>, bool>? OnDelete { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public IEnumerable<TItem> Items { get; set; } = new TItem[0];
|
public int TotalCount { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Action AddCallback { get; set; } = new Action(() => { });
|
[Parameter]
|
||||||
/// <summary>
|
public int PageIndex { get; set; } = 1;
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
public Action EditCallback { get; set; } = new Action(() => { });
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public int PageItems { get; set; } = DefaultPageItems;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 确认删除弹窗
|
||||||
|
/// </summary>
|
||||||
|
protected Modal? ConfirmModal { get; set; }
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
protected TItem EditModel { get; set; }
|
||||||
|
#nullable restore
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 编辑数据弹窗
|
||||||
|
/// </summary>
|
||||||
|
protected SubmitModal<TItem>? EditModal { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
if (OnAdd != null) EditModel = OnAdd.Invoke();
|
||||||
|
if (OnQuery != null)
|
||||||
|
{
|
||||||
|
var queryData = OnQuery.Invoke(1, DefaultPageItems);
|
||||||
|
Items = queryData.Items;
|
||||||
|
TotalCount = queryData.TotalCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pageIndex"></param>
|
||||||
|
/// <param name="pageItems"></param>
|
||||||
|
protected void PageClick(int pageIndex, int pageItems)
|
||||||
|
{
|
||||||
|
if (pageIndex != PageIndex)
|
||||||
|
{
|
||||||
|
PageIndex = pageIndex;
|
||||||
|
PageItems = pageItems;
|
||||||
|
Query();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
protected void PageItemsChange(int pageItems)
|
||||||
|
{
|
||||||
|
if (OnQuery != null)
|
||||||
|
{
|
||||||
|
PageIndex = 1;
|
||||||
|
PageItems = pageItems;
|
||||||
|
Query();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item"></param>
|
||||||
|
/// <param name="check"></param>
|
||||||
|
protected void ToggleCheck(TItem item, bool check)
|
||||||
|
{
|
||||||
|
if (item == null)
|
||||||
|
{
|
||||||
|
SelectedItems.Clear();
|
||||||
|
if (check) SelectedItems.AddRange(Items);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (check) SelectedItems.Add(item);
|
||||||
|
else SelectedItems.Remove(item);
|
||||||
|
}
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
protected CheckBoxState CheckState(TItem item)
|
||||||
|
{
|
||||||
|
var ret = CheckBoxState.UnChecked;
|
||||||
|
if (SelectedItems.Count > 0)
|
||||||
|
{
|
||||||
|
ret = SelectedItems.Count == Items.Count() ? CheckBoxState.Checked : CheckBoxState.Mixed;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public void Add()
|
||||||
|
{
|
||||||
|
if (OnAdd != null) EditModel = OnAdd.Invoke();
|
||||||
|
SelectedItems.Clear();
|
||||||
|
EditModal?.Toggle();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public void Edit()
|
||||||
|
{
|
||||||
|
if (SelectedItems.Count == 1)
|
||||||
|
{
|
||||||
|
EditModel = SelectedItems[0];
|
||||||
|
EditModal?.Toggle();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ShowMessage("编辑数据", "请选择一个要编辑的数据", ToastCategory.Information);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 更新查询数据
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="queryData"></param>
|
||||||
|
public void Query(QueryData<TItem> queryData)
|
||||||
|
{
|
||||||
|
SelectedItems.Clear();
|
||||||
|
PageIndex = queryData.PageIndex;
|
||||||
|
Items = queryData.Items;
|
||||||
|
TotalCount = queryData.TotalCount;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Query()
|
||||||
|
{
|
||||||
|
if (OnQuery != null) Query(OnQuery.Invoke(PageIndex, PageItems));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
protected void Save(EditContext context)
|
||||||
|
{
|
||||||
|
var valid = OnSave?.Invoke((TItem)context.Model) ?? false;
|
||||||
|
if (valid)
|
||||||
|
{
|
||||||
|
EditModal?.Toggle();
|
||||||
|
Query();
|
||||||
|
}
|
||||||
|
ShowMessage("保存数据", "保存数据" + (valid ? "成功" : "失败"), valid ? ToastCategory.Success : ToastCategory.Error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 删除按钮方法
|
||||||
|
/// </summary>
|
||||||
public void Delete()
|
public void Delete()
|
||||||
{
|
{
|
||||||
|
if (SelectedItems.Count > 0)
|
||||||
|
{
|
||||||
|
ConfirmModal?.Toggle();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ShowMessage("删除数据", "请选择要删除的数据", ToastCategory.Information);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 确认删除方法
|
||||||
|
/// </summary>
|
||||||
|
public void Confirm()
|
||||||
|
{
|
||||||
|
var result = OnDelete?.Invoke(SelectedItems) ?? false;
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
ConfirmModal?.Toggle();
|
||||||
|
Query();
|
||||||
|
}
|
||||||
|
ShowMessage("删除数据", "删除数据" + (result ? "成功" : "失败"), result ? ToastCategory.Success : ToastCategory.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,123 @@
|
||||||
|
using Bootstrap.Admin.Extensions;
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using Microsoft.JSInterop;
|
||||||
|
|
||||||
|
namespace Bootstrap.Admin.Components
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Toast 组件基类
|
||||||
|
/// </summary>
|
||||||
|
public class ToastBase : ComponentBase
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// IJSRuntime 实例
|
||||||
|
/// </summary>
|
||||||
|
[Inject]
|
||||||
|
protected IJSRuntime? JSRuntime { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否自动隐藏 默认为 true
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public bool AutoHide { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 自动隐藏延时 默认为 1500 毫秒
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public int Interval { get; set; } = 2000;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 组件显示位置
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public Placement Placement { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Toast 类型
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public ToastCategory Category { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 组件标题文本
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public string Title { get; set; } = "BootstrapAdmin Blazor";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 消息正文
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public string Text { get; set; } = "Toast 消息正文内容-未设置";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 控件呈现后回调方法
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="firstRender"></param>
|
||||||
|
protected override void OnAfterRender(bool firstRender)
|
||||||
|
{
|
||||||
|
if (firstRender)
|
||||||
|
{
|
||||||
|
JSRuntime.InitToast();
|
||||||
|
}
|
||||||
|
if (_show)
|
||||||
|
{
|
||||||
|
_show = false;
|
||||||
|
Show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
protected void Show()
|
||||||
|
{
|
||||||
|
JSRuntime.ShowToast();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _show;
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public void ShowMessage(string title, string text, ToastCategory category = ToastCategory.Success)
|
||||||
|
{
|
||||||
|
Title = title;
|
||||||
|
Text = text;
|
||||||
|
Category = category;
|
||||||
|
_show = true;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
protected string RenderCategory()
|
||||||
|
{
|
||||||
|
var ret = "";
|
||||||
|
switch (Category)
|
||||||
|
{
|
||||||
|
case ToastCategory.Error:
|
||||||
|
ret = "fa fa-times-circle text-danger";
|
||||||
|
break;
|
||||||
|
case ToastCategory.Information:
|
||||||
|
ret = "fa fa-exclamation-triangle text-info";
|
||||||
|
break;
|
||||||
|
case ToastCategory.Success:
|
||||||
|
ret = "fa fa-check-circle text-success";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
protected string RenderAnimation()
|
||||||
|
{
|
||||||
|
return AutoHide ? $"transition: width {(Interval * 1.0 - 200) / 1000}s linear;" : "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
namespace Bootstrap.Admin.Components
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Toast 组件类型
|
||||||
|
/// </summary>
|
||||||
|
public enum ToastCategory
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 成功
|
||||||
|
/// </summary>
|
||||||
|
Success,
|
||||||
|
/// <summary>
|
||||||
|
/// 提示信息
|
||||||
|
/// </summary>
|
||||||
|
Information,
|
||||||
|
/// <summary>
|
||||||
|
/// 错误信息
|
||||||
|
/// </summary>
|
||||||
|
Error
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
using Microsoft.AspNetCore.Components;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Bootstrap.Admin.Components
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public abstract class ValidatorComponentBase : ComponentBase
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
[Parameter]
|
||||||
|
public string ErrorMessage { get; set; } = "";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
[CascadingParameter]
|
||||||
|
public LgbInputText? Input { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 初始化方法
|
||||||
|
/// </summary>
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
if (Input == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"{nameof(ValidatorComponentBase)} requires a cascading " +
|
||||||
|
$"parameter of type {nameof(LgbInputText)}. For example, you can use {nameof(ValidatorComponentBase)} " +
|
||||||
|
$"inside an LgbInputText.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Input.Rules.Add(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="propertyValue"></param>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
/// <param name="results"></param>
|
||||||
|
public abstract void Validate(object? propertyValue, ValidationContext context, List<ValidationResult> results);
|
||||||
|
}
|
||||||
|
}
|
|
@ -45,7 +45,7 @@ namespace Bootstrap.Admin.Extensions
|
||||||
/// 启用动画
|
/// 启用动画
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="jSRuntime"></param>
|
/// <param name="jSRuntime"></param>
|
||||||
public static void EnableAnimation(this IJSRuntime? jSRuntime) => jSRuntime.InvokeVoidAsync("$.enableAnimation");
|
public static void EnableAnimation(this IJSRuntime? jSRuntime) => jSRuntime.InvokeVoidAsync("$.initDocument");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 修复 Modal 组件
|
/// 修复 Modal 组件
|
||||||
|
@ -53,11 +53,31 @@ namespace Bootstrap.Admin.Extensions
|
||||||
/// <param name="jSRuntime"></param>
|
/// <param name="jSRuntime"></param>
|
||||||
public static void InitModal(this IJSRuntime? jSRuntime) => jSRuntime.InvokeVoidAsync("$.initModal");
|
public static void InitModal(this IJSRuntime? jSRuntime) => jSRuntime.InvokeVoidAsync("$.initModal");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 修复 Modal 组件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="jSRuntime"></param>
|
||||||
|
public static void InitToast(this IJSRuntime? jSRuntime) => jSRuntime.InvokeVoidAsync("$.initToast");
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 弹出 Modal 组件
|
/// 弹出 Modal 组件
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="jSRuntime"></param>
|
/// <param name="jSRuntime"></param>
|
||||||
/// <param name="modalId"></param>
|
/// <param name="modalId"></param>
|
||||||
public static void ToggleModal(this IJSRuntime? jSRuntime, string modalId) => jSRuntime.InvokeVoidAsync("$.toggleModal", modalId);
|
public static void ToggleModal(this IJSRuntime? jSRuntime, string modalId) => jSRuntime.InvokeVoidAsync("$.toggleModal", modalId);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 弹出 Toast 组件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="jSRuntime"></param>
|
||||||
|
public static void ShowToast(this IJSRuntime? jSRuntime) => jSRuntime.InvokeVoidAsync("$.showToast");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 弹出 Tooltip 组件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="jSRuntime"></param>
|
||||||
|
/// <param name="id"></param>
|
||||||
|
/// <param name="method"></param>
|
||||||
|
public static void Tooltip(this IJSRuntime? jSRuntime, string id, string method) => jSRuntime.InvokeVoidAsync("$.tooltip", $"#{id}", method);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,40 +1,20 @@
|
||||||
@inject IJSRuntime JSRuntime
|
@inherits DictsBase
|
||||||
|
|
||||||
<div class="card">
|
<EditPage Id="dict" TItem="Bootstrap.Security.BootstrapDict" QueryModel="QueryModel" OnQuery="Query" OnAdd="Add" OnDelete="Delete" OnSave="Save">
|
||||||
<div class="card-header">查询条件</div>
|
<QueryBody>
|
||||||
<div class="card-body">
|
|
||||||
<form class="form-inline">
|
|
||||||
<div class="row">
|
|
||||||
<div class="form-group col-sm-6 col-md-auto">
|
|
||||||
<label class="control-label" for="txt_dict_name">字典名称</label>
|
|
||||||
<input type="text" class="form-control" id="txt_dict_name" />
|
|
||||||
</div>
|
|
||||||
<div class="form-group col-sm-6 col-md-auto">
|
<div class="form-group col-sm-6 col-md-auto">
|
||||||
<label class="control-label" for="txt_dict_cate">字典标签</label>
|
<label class="control-label" for="txt_dict_cate">字典标签</label>
|
||||||
<input type="text" class="form-control" id="txt_dict_cate" data-provide="typeahead" />
|
<input type="text" class="form-control" @bind="context.Category" data-provide="typeahead" />
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group col-sm-6 col-md-auto">
|
<div class="form-group col-sm-6 col-md-auto">
|
||||||
<label class="control-label" for="txt_dict_define">字典类别</label>
|
<label class="control-label" for="txt_dict_name">字典名称</label>
|
||||||
<Select @ref="DictCate">
|
<input type="text" class="form-control" @bind="context.Name" />
|
||||||
<SelectItem Text="全部" Value="" Active="true"></SelectItem>
|
|
||||||
<SelectItem Text="系统使用" Value="0"></SelectItem>
|
|
||||||
<SelectItem Text="自定义" Value="1"></SelectItem>
|
|
||||||
</Select>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group col-sm-6 col-md-auto flex-sm-fill justify-content-sm-end align-self-sm-end">
|
<div class="form-group col-sm-6 col-md-auto">
|
||||||
<button type="button" id="btn_query" class="btn btn-primary btn-fill" @onclick="e => Query(1)"><i class="fa fa-search" aria-hidden="true"></i><span>查询</span></button>
|
<label class="control-label" for="txt_dict_define">字典类型</label>
|
||||||
|
<Select Items="QueryDefine" TItem="int" SelectedItemChanged="item=>context.Define = int.Parse(item.Value)"></Select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</QueryBody>
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-header">
|
|
||||||
查询结果
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="bootstrap-table">
|
|
||||||
<Table @ref="Table" Items="Items" TItem="Bootstrap.Security.BootstrapDict">
|
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
<th>字典标签</th>
|
<th>字典标签</th>
|
||||||
<th>字典名称</th>
|
<th>字典名称</th>
|
||||||
|
@ -47,131 +27,30 @@
|
||||||
<td>@context.Code</td>
|
<td>@context.Code</td>
|
||||||
<td>@context.Define</td>
|
<td>@context.Define</td>
|
||||||
</RowTemplate>
|
</RowTemplate>
|
||||||
</Table>
|
<EditTemplate>
|
||||||
<Pagination @ref="Pagination" PageCount="PageCount" PageIndex="PageIndex" ItemsCount="ItemsCount"></Pagination>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div id="tableButtons" class="d-none">
|
|
||||||
<div class='btn-group'>
|
|
||||||
<button class='edit btn btn-sm btn-success' asp-auth="edit"><i class='fa fa-edit'></i><span>编辑</span></button>
|
|
||||||
<button class='del btn btn-sm btn-danger' asp-auth="del"><i class='fa fa-remove'></i><span>删除</span></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Modal Id="DialogDict" Title="字典编辑窗口">
|
|
||||||
<ModalBody>
|
|
||||||
<form class="form-inline">
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="form-group col-sm-6">
|
<div class="form-group col-sm-6">
|
||||||
<label class="control-label" for="dictCate">字典标签</label>
|
<label class="control-label" for="dictCate">字典标签</label>
|
||||||
<input type="text" class="form-control" id="dictCate" @bind="Model.Category" placeholder="不可为空,50字以内" maxlength="50" data-valid="true" />
|
<LgbInputText Id="dictCate" type="text" class="form-control" data-toggle="tooltip" @bind-Value="context.Category" placeholder="不可为空,50字以内">
|
||||||
|
<RequiredValidator AllowEmptyString="false" ErrorMessage="这是必填字段" />
|
||||||
|
<StringLengthValidator Length="5" ErrorMessage="不可为空,50字以内" />
|
||||||
|
</LgbInputText>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group col-sm-6">
|
<div class="form-group col-sm-6">
|
||||||
<label class="control-label" for="dictDefine">字典类型</label>
|
<label class="control-label" for="dictDefine">字典类型</label>
|
||||||
<Select Id="dictDefine" @bind-SelectedValue="Model.Define">
|
<Select Id="dictDefine" Items="DefineItems" TItem="int" @bind-SelectedValue="context.Define" SelectedItemChanged="@(item=>context.Define = int.Parse(item.Value))"></Select>
|
||||||
<SelectItem Text="系统使用" Value="0" Active="true"></SelectItem>
|
|
||||||
<SelectItem Text="自定义" Value="1"></SelectItem>
|
|
||||||
</Select>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group col-sm-6">
|
<div class="form-group col-sm-6">
|
||||||
<label class="control-label" for="dictName">字典名称</label>
|
<label class="control-label" for="dictName">字典名称</label>
|
||||||
<input type="text" class="form-control" id="dictName" @bind="Model.Name" placeholder="不可为空,50字以内" maxlength="50" data-valid="true" />
|
<LgbInputText Id="dictName" type="text" class="form-control" @bind-Value="context.Name" placeholder="不可为空,50字以内" maxlength="50">
|
||||||
|
<RequiredValidator AllowEmptyString="false" ErrorMessage="这是必填字段" />
|
||||||
|
<StringLengthValidator Length="5" ErrorMessage="不可为空,50字以内" />
|
||||||
|
</LgbInputText>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group col-sm-6">
|
<div class="form-group col-sm-6">
|
||||||
<label class="control-label" for="dictCode">字典代码</label>
|
<label class="control-label" for="dictCode">字典代码</label>
|
||||||
<input type="text" class="form-control" id="dictCode" @bind="Model.Code" placeholder="不可为空,2000字以内" maxlength="2000" data-valid="true" />
|
<LgbInputText Id="dictCode" type="text" class="form-control" @bind-Value="context.Code" placeholder="不可为空,2000字以内" maxlength="2000" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</EditTemplate>
|
||||||
</ModalBody>
|
</EditPage>
|
||||||
<ModalFooter>
|
|
||||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">
|
|
||||||
<i class="fa fa-times"></i>
|
|
||||||
<span>关闭</span>
|
|
||||||
</button>
|
|
||||||
<button type="button" class="btn btn-primary" @onclick="Save">
|
|
||||||
<i class="fa fa-save"></i>
|
|
||||||
<span>保存</span>
|
|
||||||
</button>
|
|
||||||
</ModalFooter>
|
|
||||||
</Modal>
|
|
||||||
|
|
||||||
@code {
|
|
||||||
[Parameter]
|
|
||||||
public IEnumerable<BootstrapDict> Items { get; set; } = new BootstrapDict[0];
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public int ItemsCount { get; set; } = 0;
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public int PageCount { get; set; } = 0;
|
|
||||||
|
|
||||||
[Parameter]
|
|
||||||
public int PageIndex { get; set; } = 1;
|
|
||||||
|
|
||||||
protected Select? DictCate { get; set; }
|
|
||||||
|
|
||||||
protected Pagination? Pagination { get; set; }
|
|
||||||
|
|
||||||
protected Table<BootstrapDict>? Table { get; set; }
|
|
||||||
|
|
||||||
protected BootstrapDict Model { get; set; } = new BootstrapDict();
|
|
||||||
|
|
||||||
protected override void OnInitialized()
|
|
||||||
{
|
|
||||||
Query(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnAfterRender(bool firstRender)
|
|
||||||
{
|
|
||||||
if (firstRender)
|
|
||||||
{
|
|
||||||
if (Pagination != null)
|
|
||||||
{
|
|
||||||
Pagination.ClickPageCallback = pageIndex =>
|
|
||||||
{
|
|
||||||
if (pageIndex != PageIndex)
|
|
||||||
{
|
|
||||||
Query(pageIndex);
|
|
||||||
StateHasChanged();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Pagination.PageItemsChangeCallback = () =>
|
|
||||||
{
|
|
||||||
Query(1);
|
|
||||||
StateHasChanged();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if (Table != null)
|
|
||||||
{
|
|
||||||
Table.AddCallback = () =>
|
|
||||||
{
|
|
||||||
JSRuntime.ToggleModal("#DialogDict");
|
|
||||||
};
|
|
||||||
Table.EditCallback = () =>
|
|
||||||
{
|
|
||||||
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void Query(int pageIndex)
|
|
||||||
{
|
|
||||||
var data = DataAccess.DictHelper.RetrieveDicts();
|
|
||||||
if (!string.IsNullOrEmpty(DictCate?.SelectedItem?.Value)) data = data.Where(d => d.Define.ToString() == DictCate?.SelectedItem?.Value);
|
|
||||||
ItemsCount = data.Count();
|
|
||||||
|
|
||||||
var pageItems = Pagination?.PageItems ?? 20;
|
|
||||||
Items = data.Skip((pageIndex - 1) * pageItems).Take(pageItems);
|
|
||||||
PageCount = (int)Math.Ceiling(data.Count() * 1.0d / pageItems);
|
|
||||||
PageIndex = pageIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void Save()
|
|
||||||
{
|
|
||||||
JSRuntime.ToggleModal("#DialogDict");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -17,23 +17,16 @@
|
||||||
<environment include="Development">
|
<environment include="Development">
|
||||||
<link href="~/lib/twitter-bootstrap/css/bootstrap.css" rel="stylesheet" />
|
<link href="~/lib/twitter-bootstrap/css/bootstrap.css" rel="stylesheet" />
|
||||||
<link href="~/lib/font-awesome/css/font-awesome.css" rel="stylesheet" />
|
<link href="~/lib/font-awesome/css/font-awesome.css" rel="stylesheet" />
|
||||||
<link href="~/lib/sweetalert/sweetalert2.css" rel="stylesheet" />
|
|
||||||
<link href="~/lib/toastr.js/toastr.css" rel="stylesheet" />
|
|
||||||
<link href="~/lib/nprogress/nprogress.css" rel="stylesheet" />
|
|
||||||
<link href="~/lib/sweetalert/sweetalert2.css" rel="stylesheet" />
|
|
||||||
<link href="~/lib/overlayscrollbars/OverlayScrollbars.css" rel="stylesheet" />
|
<link href="~/lib/overlayscrollbars/OverlayScrollbars.css" rel="stylesheet" />
|
||||||
</environment>
|
</environment>
|
||||||
<environment exclude="Development">
|
<environment exclude="Development">
|
||||||
<link href="~/lib/twitter-bootstrap/css/bootstrap.min.css" rel="stylesheet" />
|
<link href="~/lib/twitter-bootstrap/css/bootstrap.min.css" rel="stylesheet" />
|
||||||
<link href="~/lib/font-awesome/css/font-awesome.min.css" rel="stylesheet" />
|
<link href="~/lib/font-awesome/css/font-awesome.min.css" rel="stylesheet" />
|
||||||
<link href="~/lib/sweetalert/sweetalert2.min.css" rel="stylesheet" />
|
|
||||||
<link href="~/lib/toastr.js/toastr.min.css" rel="stylesheet" />
|
|
||||||
<link href="~/lib/nprogress/nprogress.min.css" rel="stylesheet" />
|
|
||||||
<link href="~/lib/sweetalert/sweetalert2.min.css" rel="stylesheet" />
|
|
||||||
<link href="~/lib/overlayscrollbars/OverlayScrollbars.min.css" rel="stylesheet" />
|
<link href="~/lib/overlayscrollbars/OverlayScrollbars.min.css" rel="stylesheet" />
|
||||||
</environment>
|
</environment>
|
||||||
<link href="~/lib/captcha/slidercaptcha.css" rel="stylesheet" />
|
<link href="~/lib/captcha/slidercaptcha.css" rel="stylesheet" />
|
||||||
<link href="~/lib/longbow-select/longbow-select.css" rel="stylesheet" />
|
<link href="~/lib/longbow-select/longbow-select.css" rel="stylesheet" />
|
||||||
|
<link href="~/lib/longbow-checkbox/longbow-checkbox.css" rel="stylesheet" />
|
||||||
<link href="~/css/theme.css" rel="stylesheet" asp-append-version="true" />
|
<link href="~/css/theme.css" rel="stylesheet" asp-append-version="true" />
|
||||||
<link href="~/css/theme-responsive.css" rel="stylesheet" asp-append-version="true" />
|
<link href="~/css/theme-responsive.css" rel="stylesheet" asp-append-version="true" />
|
||||||
<link href="~/css/site.css" rel="stylesheet" asp-append-version="true" />
|
<link href="~/css/site.css" rel="stylesheet" asp-append-version="true" />
|
||||||
|
|
|
@ -5,4 +5,5 @@
|
||||||
<Section ShowCardTitle="@RootLayout.Model.ShowCardTitle" LockScreenPeriod=@RootLayout.Model.LockScreenPeriod>
|
<Section ShowCardTitle="@RootLayout.Model.ShowCardTitle" LockScreenPeriod=@RootLayout.Model.LockScreenPeriod>
|
||||||
<TabSet @ref="TabSet"></TabSet>
|
<TabSet @ref="TabSet"></TabSet>
|
||||||
</Section>
|
</Section>
|
||||||
|
<Toast @ref="Toast"></Toast>
|
||||||
</CascadingValue>
|
</CascadingValue>
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
@inherits AlertBase
|
||||||
|
|
||||||
|
<Modal Backdrop="@Backdrop" IsCentered="@IsCentered" Size="@Size" Id="@Id" Title="@Title" ShowFooter="@ShowFooter">
|
||||||
|
<ModalBody>
|
||||||
|
@AlertBody
|
||||||
|
</ModalBody>
|
||||||
|
<ModalFooter>
|
||||||
|
@AlertFooter
|
||||||
|
</ModalFooter>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
@typeparam TItem
|
||||||
|
@inherits CheckboxBase<TItem>
|
||||||
|
|
||||||
|
<label role="checkbox" aria-checked="@State.ToCss()" class="@RenderStateCss()" @onclick="e => ToggleCallback(Item, !Checked)">
|
||||||
|
<span class="checkbox-input">
|
||||||
|
<span class="checkbox-inner"></span>
|
||||||
|
</span>
|
||||||
|
<span class="checkbox-label">@Text</span>
|
||||||
|
</label>
|
|
@ -0,0 +1,31 @@
|
||||||
|
@typeparam TItem
|
||||||
|
@inherits EditPageBase<TItem>
|
||||||
|
|
||||||
|
<Query OnQuery="Query" TItem="TItem" @bind-QueryModel="QueryModel">
|
||||||
|
@QueryBody?.Invoke(context)
|
||||||
|
</Query>
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
查询结果
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<Table @ref="Table" Id="@Id" TItem="TItem" OnQuery="QueryData" OnAdd="OnAdd" OnDelete="OnDelete" OnSave="OnSave">
|
||||||
|
<TableHeader>
|
||||||
|
@TableHeader
|
||||||
|
</TableHeader>
|
||||||
|
<RowTemplate>
|
||||||
|
@RowTemplate?.Invoke(context)
|
||||||
|
</RowTemplate>
|
||||||
|
<ButtonTemplate>
|
||||||
|
<div class='btn-group'>
|
||||||
|
<button class='btn btn-sm btn-success' asp-auth="edit" @onclick="e => Edit(context)"><i class='fa fa-edit'></i><span>编辑</span></button>
|
||||||
|
<button class='btn btn-sm btn-danger' asp-auth="del" @onclick="e => Delete(context)"><i class='fa fa-remove'></i><span>删除</span></button>
|
||||||
|
@ButtonTemplate
|
||||||
|
</div>
|
||||||
|
</ButtonTemplate>
|
||||||
|
<EditTemplate>
|
||||||
|
@EditTemplate?.Invoke(context)
|
||||||
|
</EditTemplate>
|
||||||
|
</Table>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,13 @@
|
||||||
|
@inherits LgbEditFormBase
|
||||||
|
|
||||||
|
<CascadingValue Value="this">
|
||||||
|
<EditForm Model="@Model" AdditionalAttributes="@AdditionalAttributes" OnSubmit="OnSubmit" OnValidSubmit="OnValidSubmit" OnInvalidSubmit="OnInvalidSubmit">
|
||||||
|
<BootstrapAdminDataAnnotationsValidator />
|
||||||
|
@ChildContent
|
||||||
|
<button type="submit" class="d-none">submit</button>
|
||||||
|
</EditForm>
|
||||||
|
</CascadingValue>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
|
||||||
|
}
|
|
@ -1,8 +1,7 @@
|
||||||
@inherits ModalBase
|
@inherits ModalBase
|
||||||
|
|
||||||
<!-- Modal -->
|
|
||||||
<div class="modal fade" id="@Id" tabindex="-1" role="dialog" aria-labelledby="modal_@Id" aria-hidden="true" data-backdrop="@(Backdrop ? "none" : "static")">
|
<div class="modal fade" id="@Id" tabindex="-1" role="dialog" aria-labelledby="modal_@Id" aria-hidden="true" data-backdrop="@(Backdrop ? "none" : "static")">
|
||||||
<div class="modal-dialog modal-dialog-centered modal-lg" role="document">
|
<div class="@(IsCentered ? "modal-dialog modal-dialog-centered" : "modal-dialog") @RenderModalSize()" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<h5 class="modal-title" id="modal_@Id">@Title</h5>
|
<h5 class="modal-title" id="modal_@Id">@Title</h5>
|
||||||
|
@ -13,9 +12,12 @@
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
@ModalBody
|
@ModalBody
|
||||||
</div>
|
</div>
|
||||||
|
@if (ShowFooter)
|
||||||
|
{
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
@ModalFooter
|
@ModalFooter
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -3,18 +3,19 @@
|
||||||
<CascadingValue Value="this">
|
<CascadingValue Value="this">
|
||||||
<nav class="d-flex align-items-center" aria-label="分页组件">
|
<nav class="d-flex align-items-center" aria-label="分页组件">
|
||||||
<div class="pagination-bar">
|
<div class="pagination-bar">
|
||||||
显示第 <span>@((PageIndex - 1) * PageItems + 1)</span> 到第 <span>@(Math.Min(PageIndex * PageItems, ItemsCount))</span> 条记录,总共 <span>@ItemsCount</span> 条记录 每页显示
|
显示第 <span>@((PageIndex - 1) * PageItems + 1)</span> 到第 <span>@(Math.Min(PageIndex * PageItems, TotalCount))</span> 条记录,总共 <span>@TotalCount</span> 条记录 每页显示
|
||||||
<div class="btn-group dropup">
|
<div class="btn-group dropup">
|
||||||
<button type="button" class="btn btn-secondary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">@PageItems</button>
|
<button type="button" class="btn btn-secondary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">@PageItems</button>
|
||||||
<div class="dropdown-menu">
|
<div class="dropdown-menu">
|
||||||
<div class="@(PageItems == 20 ? "dropdown-item active" : "dropdown-item")" @onclick="@(e => ClickItem(20))">20</div>
|
@foreach (var page in GetPages())
|
||||||
<div class="@(PageItems == 40 ? "dropdown-item active" : "dropdown-item")" @onclick="@(e => ClickItem(40))">40</div>
|
{
|
||||||
<div class="@(PageItems == 80 ? "dropdown-item active" : "dropdown-item")" @onclick="@(e => ClickItem(80))">80</div>
|
<div class="@(PageItems == page ? "dropdown-item active" : "dropdown-item")" @onclick="@(e => ClickItem(page))">@(page.ToString())</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
条记录
|
条记录
|
||||||
</div>
|
</div>
|
||||||
<ul class="@(PageCount == 1 ? "pagination d-none" : "pagination")">
|
<ul class="@(PageCount > 1 ? "pagination" : "pagination d-none")">
|
||||||
<li class="page-item" @onclick="MovePrev"><div class="page-link" aria-label="上一页"><i class="fa fa-angle-double-left"></i></div></li>
|
<li class="page-item" @onclick="MovePrev"><div class="page-link" aria-label="上一页"><i class="fa fa-angle-double-left"></i></div></li>
|
||||||
@for (int i = 1; i <= PageCount; i++)
|
@for (int i = 1; i <= PageCount; i++)
|
||||||
{
|
{
|
||||||
|
|
|
@ -17,13 +17,13 @@
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Parameter]
|
[Parameter]
|
||||||
public int PageIndex { get; set; } = 0;
|
public int PageIndex { get; set; } = 1;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected void ClickPage()
|
protected void ClickPage()
|
||||||
{
|
{
|
||||||
Pagination?.ClickPageCallback?.Invoke(PageIndex);
|
Pagination?.OnPageClick?.Invoke(PageIndex, Pagination.PageItems);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
@typeparam TItem
|
||||||
|
@inherits QueryBase<TItem>
|
||||||
|
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">@Title</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="form-inline">
|
||||||
|
<div class="row">
|
||||||
|
@ChildContent?.Invoke(QueryModel)
|
||||||
|
<div class="form-group col-sm-6 col-md-auto flex-sm-fill justify-content-sm-end align-self-sm-end">
|
||||||
|
<button type="button" id="btn_query" class="btn btn-primary btn-fill" @onclick="e => OnQuery?.Invoke()"><i class="fa fa-search" aria-hidden="true"></i><span>@Text</span></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -1,13 +1,15 @@
|
||||||
@inherits SelectBase
|
@typeparam TItem
|
||||||
|
@inherits SelectBase<TItem>
|
||||||
|
|
||||||
<div data-toggle="lgbSelect" class="form-select dropdown">
|
<div data-toggle="lgbSelect" class="form-select dropdown">
|
||||||
<input type="text" readonly="readonly" class="form-control form-select-input" id="@Id" data-toggle="dropdown" placeholder="@PlaceHolder" value="@SelectedItem?.Text">
|
<input type="text" readonly="readonly" class="form-control form-select-input" id="@Id" data-toggle="dropdown" placeholder="@PlaceHolder" value="@SelectedItem.Text">
|
||||||
<span class="form-select-append"><i class="fa fa-angle-up"></i></span>
|
<span class="form-select-append"><i class="fa fa-angle-up"></i></span>
|
||||||
<div class="dropdown-menu-arrow"></div>
|
<div class="dropdown-menu-arrow"></div>
|
||||||
<div class="dropdown-menu">
|
<div class="dropdown-menu">
|
||||||
<CascadingValue Value="this">
|
@foreach (var item in Items)
|
||||||
@ChildContent
|
{
|
||||||
</CascadingValue>
|
<SelectItem Item="@item" ItemClickCallback="ItemClickCallback"></SelectItem>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
@inherits SelectItemBase
|
@inherits SelectItemBase
|
||||||
|
|
||||||
<div class="@(Active ? "dropdown-item active" : "dropdown-item")" data-val="@Value" @onclick="ClickItem">@Text</div>
|
<div class="@(Item.Active ? "dropdown-item active" : "dropdown-item")" data-val="@Item.Value" @onclick="@(e=>ItemClickCallback(Item))">@Item.Text</div>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
@typeparam TItem
|
||||||
|
@inherits SubmitModalBase<TItem>
|
||||||
|
|
||||||
|
<Modal Id="@Id" Title="Title" Size="Size">
|
||||||
|
<ModalBody>
|
||||||
|
<LgbEditForm class="form-inline" Model="Model" OnValidSubmit="OnValidSubmit" OnInvalidSubmit="OnInvalidSubmit" OnSubmit="OnSubmit">
|
||||||
|
@ModalBody
|
||||||
|
</LgbEditForm>
|
||||||
|
</ModalBody>
|
||||||
|
<ModalFooter>
|
||||||
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
<span>关闭</span>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-primary" onclick="$.submitForm(this)">
|
||||||
|
<i class="fa fa-save"></i>
|
||||||
|
<span>保存</span>
|
||||||
|
</button>
|
||||||
|
</ModalFooter>
|
||||||
|
</Modal>
|
|
@ -1,36 +1,89 @@
|
||||||
@typeparam TItem
|
@typeparam TItem
|
||||||
@inherits TableBase<TItem>
|
@inherits TableBase<TItem>
|
||||||
|
|
||||||
|
<div class="bootstrap-table">
|
||||||
<div class="@(ShowToolBar ? "bs-bars" : "bs-bars d-none")">
|
<div class="@(ShowToolBar ? "bs-bars" : "bs-bars d-none")">
|
||||||
<div class="toolbar btn-group">
|
<div class="toolbar btn-group">
|
||||||
<button id="btn_add" type="button" @onclick="(e => AddCallback())" class="btn btn-success" asp-auth="add"><i class="fa fa-plus" aria-hidden="true"></i><span>新增</span></button>
|
<button type="button" @onclick="Add" class="btn btn-success" asp-auth="add"><i class="fa fa-plus" aria-hidden="true"></i><span>新增</span></button>
|
||||||
<button id="btn_delete" type="button" @onclick="Delete" class="btn btn-danger" asp-auth="del"><i class="fa fa-remove" aria-hidden="true"></i><span>删除</span></button>
|
<button type="button" @onclick="Delete" class="btn btn-danger" asp-auth="del"><i class="fa fa-remove" aria-hidden="true"></i><span>删除</span></button>
|
||||||
<button id="btn_edit" type="button" @onclick="(e => EditCallback())" class="btn btn-primary" asp-auth="edit"><i class="fa fa-pencil" aria-hidden="true"></i><span>编辑</span></button>
|
<button type="button" @onclick="Edit" class="btn btn-primary" asp-auth="edit"><i class="fa fa-pencil" aria-hidden="true"></i><span>编辑</span></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="gear btn-group">
|
<div class="gear btn-group">
|
||||||
<button class="btn btn-secondary dropdown-toggle" data-toggle="dropdown" type="button"><i class="fa fa-gear"></i></button>
|
<button class="btn btn-secondary dropdown-toggle" data-toggle="dropdown" type="button"><i class="fa fa-gear"></i></button>
|
||||||
<div class="dropdown-menu">
|
<div class="dropdown-menu">
|
||||||
<div id="tb_add" title="新增" @onclick="(e => AddCallback())" asp-auth="add"><i class="fa fa-plus"></i></div>
|
<div class="dropdown-item" title="新增" @onclick="Add" asp-auth="add"><i class="fa fa-plus"></i></div>
|
||||||
<div id="tb_delete" title="删除" @onclick="Delete" asp-auth="del"><i class="fa fa-remove"></i></div>
|
<div class="dropdown-item" title="删除" @onclick="Delete" asp-auth="del"><i class="fa fa-remove"></i></div>
|
||||||
<div id="tb_edit" title="编辑" @onclick="(e => EditCallback())" asp-auth="edit"><i class="fa fa-pencil"></i></div>
|
<div class="dropdown-item" title="编辑" @onclick="Edit" asp-auth="edit"><i class="fa fa-pencil"></i></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<table class="table table-striped table-bordered table-hover">
|
<table class="table table-striped table-bordered table-hover table-selected">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>@TableHeader</tr>
|
<tr>
|
||||||
|
@if (ShowLineNo)
|
||||||
|
{
|
||||||
|
<th class="table-col-lineno">行号</th>
|
||||||
|
}
|
||||||
|
@if (ShowCheckbox)
|
||||||
|
{
|
||||||
|
<th class="table-col-checkbox"><Checkbox TItem="TItem" SetCheckCallback="CheckState" ToggleCallback="ToggleCheck"></Checkbox></th>
|
||||||
|
}
|
||||||
|
@TableHeader
|
||||||
|
@if (ShowButtons)
|
||||||
|
{
|
||||||
|
<th>@ButtonTemplateHeaderText</th>
|
||||||
|
}
|
||||||
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@foreach (var item in Items)
|
@for (int index = 0; index < Items.Count(); index++)
|
||||||
{
|
{
|
||||||
<tr>@RowTemplate?.Invoke(item)</tr>
|
<tr>
|
||||||
|
@if (ShowLineNo)
|
||||||
|
{
|
||||||
|
<td class="table-col-lineno">@(index + 1 + (PageIndex - 1) * PageItems)</td>
|
||||||
|
}
|
||||||
|
@if (ShowCheckbox)
|
||||||
|
{
|
||||||
|
<td class="table-col-checkbox"><Checkbox TItem="TItem" Item="Items.ElementAt(index)" SetCheckCallback="item => SelectedItems.Contains(item) ? CheckBoxState.Checked : CheckBoxState.UnChecked" ToggleCallback="ToggleCheck"></Checkbox></td>
|
||||||
|
}
|
||||||
|
@RowTemplate?.Invoke(Items.ElementAt(index))
|
||||||
|
@if (ShowButtons)
|
||||||
|
{
|
||||||
|
<td>@ButtonTemplate?.Invoke(Items.ElementAt(index))</td>
|
||||||
|
}
|
||||||
|
</tr>
|
||||||
}
|
}
|
||||||
</tbody>
|
</tbody>
|
||||||
<tfoot>
|
<tfoot>
|
||||||
<tr>@TableFooter</tr>
|
<tr>@TableFooter</tr>
|
||||||
</tfoot>
|
</tfoot>
|
||||||
</table>
|
</table>
|
||||||
|
<Pagination PageItems="PageItems" TotalCount="TotalCount" PageIndex="PageIndex" OnPageClick="PageClick" OnPageItemsChange="PageItemsChange"></Pagination>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Modal @ref="ConfirmModal" Id="@($"{Id}_confirm")" Title="数据删除">
|
||||||
|
<ModalBody>
|
||||||
|
<div class="modal-confirm-body">您确定要删除选中的所有数据吗?</div>
|
||||||
|
</ModalBody>
|
||||||
|
<ModalFooter>
|
||||||
|
<button type="button" class="btn btn-danger" @onclick="Confirm">
|
||||||
|
<i class="fa fa-trash-o"></i>
|
||||||
|
<span>我要删除</span>
|
||||||
|
</button>
|
||||||
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">
|
||||||
|
<i class="fa fa-times"></i>
|
||||||
|
<span>取消</span>
|
||||||
|
</button>
|
||||||
|
</ModalFooter>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
<SubmitModal @ref="EditModal" Id="@($"{Id}_submit")" TItem="TItem" Title="字典编辑窗口" Size="ModalSize.ExtraLarge" @bind-Model="EditModel" OnValidSubmit="Save">
|
||||||
|
<ModalBody>
|
||||||
|
@EditTemplate?.Invoke(EditModel)
|
||||||
|
</ModalBody>
|
||||||
|
</SubmitModal>
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
@inherits ToastBase
|
||||||
|
|
||||||
|
<div class="toast fade @Placement.ToCss("toast")" role="alert" aria-live="assertive" aria-atomic="true" data-autohide="@(AutoHide ? "true":"false")" data-delay="@Interval">
|
||||||
|
<div class="toast-header">
|
||||||
|
<div class="toast-bar"><i class="@RenderCategory()"></i></div>
|
||||||
|
<strong class="mr-auto">@Title</strong>
|
||||||
|
<button type="button" class="ml-2 mb-1 close" data-dismiss="toast" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="toast-body">
|
||||||
|
@Text
|
||||||
|
</div>
|
||||||
|
<div class="toast-progress" style="@RenderAnimation()"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
|
||||||
|
}
|
|
@ -1,7 +1,6 @@
|
||||||
using Bootstrap.DataAccess;
|
using Bootstrap.DataAccess;
|
||||||
using Longbow.Tasks;
|
using Longbow.Tasks;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using System;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using Task = System.Threading.Tasks.Task;
|
using Task = System.Threading.Tasks.Task;
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
#components-reconnect-modal {
|
||||||
|
z-index: 9999 !important;
|
||||||
|
}
|
||||||
|
|
||||||
.nav-link-bar {
|
.nav-link-bar {
|
||||||
border: 1px solid #dee2e6;
|
border: 1px solid #dee2e6;
|
||||||
border-top-left-radius: 0.25rem;
|
border-top-left-radius: 0.25rem;
|
||||||
|
@ -67,6 +71,35 @@ nav .dropdown .nav-link-close.dropdown-toggle:after {
|
||||||
padding: 0.5rem;
|
padding: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.table .btn.btn-sm {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
padding: 1px 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table .btn-group {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-col-lineno {
|
||||||
|
width: 46px;
|
||||||
|
text-align: right;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-col-checkbox {
|
||||||
|
width: 32px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-col-checkbox .checkbox-label {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-selected .form-checkbox {
|
||||||
|
line-height: 1;
|
||||||
|
vertical-align: text-bottom;
|
||||||
|
}
|
||||||
|
|
||||||
.card-body .bootstrap-table {
|
.card-body .bootstrap-table {
|
||||||
margin-top: 0rem;
|
margin-top: 0rem;
|
||||||
}
|
}
|
||||||
|
@ -75,6 +108,19 @@ nav .dropdown .nav-link-close.dropdown-toggle:after {
|
||||||
margin-bottom: 0.625rem;
|
margin-bottom: 0.625rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bs-bars .dropdown-menu.show {
|
||||||
|
display: flex;
|
||||||
|
min-width: unset;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bs-bars .dropdown-item {
|
||||||
|
padding: 6px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bs-bars .dropdown-item:not(:first-child) {
|
||||||
|
border-left: solid 1px #aeb2b7;
|
||||||
|
}
|
||||||
|
|
||||||
.bootstrap-table {
|
.bootstrap-table {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
@ -83,3 +129,94 @@ nav .dropdown .nav-link-close.dropdown-toggle:after {
|
||||||
flex: 1 1 auto;
|
flex: 1 1 auto;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.toast {
|
||||||
|
position: absolute;
|
||||||
|
z-index: -1;
|
||||||
|
min-width: 260px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast.show {
|
||||||
|
z-index: 1080;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-top {
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 1rem;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-top-left {
|
||||||
|
top: 1rem;
|
||||||
|
left: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-top-right {
|
||||||
|
top: 1rem;
|
||||||
|
right: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-bottom {
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 1rem;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-bottom-left {
|
||||||
|
bottom: 1rem;
|
||||||
|
left: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-bottom-right {
|
||||||
|
bottom: 1rem;
|
||||||
|
right: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-center {
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
margin: 0 auto;
|
||||||
|
margin-top: calc(50vh - 42px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-bar {
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-header {
|
||||||
|
border-bottom: 1px solid #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-body {
|
||||||
|
background-color: #e9ecef;
|
||||||
|
color: rgba(0, 0, 0, 0.9);
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-progress {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
height: 4px;
|
||||||
|
background-color: #000000;
|
||||||
|
opacity: 0.4;
|
||||||
|
-ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=40);
|
||||||
|
filter: alpha(opacity=40);
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.showing .toast-progress, .show .toast-progress {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-confirm-body {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
font-size: 1.125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Validation */
|
||||||
|
input[type="text"].valid, input[type="password"].valid {
|
||||||
|
padding-right: 30px;
|
||||||
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
body {
|
body {
|
||||||
color: #797979;
|
color: #797979;
|
||||||
background: #f1f2f7;
|
background: #f1f2f7;
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
-webkit-overflow-scrolling: touch;
|
-webkit-overflow-scrolling: touch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -106,8 +106,9 @@
|
||||||
var $curTab = $navBar.find('.active').first();
|
var $curTab = $navBar.find('.active').first();
|
||||||
return $curTab.next().attr('url');
|
return $curTab.next().attr('url');
|
||||||
},
|
},
|
||||||
enableAnimation: function () {
|
initDocument: function () {
|
||||||
$('body').removeClass('trans-mute');
|
$('body').removeClass('trans-mute');
|
||||||
|
$('[data-toggle="tooltip"]').tooltip();
|
||||||
},
|
},
|
||||||
initSidebar: function () {
|
initSidebar: function () {
|
||||||
$('.sidebar').addNiceScroll().autoScrollSidebar();
|
$('.sidebar').addNiceScroll().autoScrollSidebar();
|
||||||
|
@ -119,15 +120,30 @@
|
||||||
initModal: function () {
|
initModal: function () {
|
||||||
$('.modal').appendTo($('body'));
|
$('.modal').appendTo($('body'));
|
||||||
},
|
},
|
||||||
|
initToast: function () {
|
||||||
|
$('.toast').appendTo($('body'));
|
||||||
|
},
|
||||||
toggleModal: function (modalId) {
|
toggleModal: function (modalId) {
|
||||||
$(modalId).modal('toggle');
|
$(modalId).modal('toggle');
|
||||||
|
},
|
||||||
|
showToast: function () {
|
||||||
|
$('.toast').toast('show');
|
||||||
|
},
|
||||||
|
tooltip: function (id, method) {
|
||||||
|
$(id).tooltip(method);
|
||||||
|
},
|
||||||
|
submitForm: function (btn) {
|
||||||
|
$(btn).parent().prev().find('form :submit').click();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$(function () {
|
$(function () {
|
||||||
$(document)
|
$(document)
|
||||||
.on('click', '.nav-tabs .nav-link', function (e) {
|
.on('hidden.bs.toast', '.toast', function () {
|
||||||
|
$(this).removeClass('hide');
|
||||||
|
})
|
||||||
|
.on('inserted.bs.tooltip', '.is-invalid', function () {
|
||||||
|
$('#' + $(this).attr('aria-describedby')).addClass('is-invalid');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
})(jQuery);
|
})(jQuery);
|
||||||
|
|
|
@ -51,7 +51,7 @@
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-checkbox.is-checked .checkbox-input .checkbox-inner {
|
.form-checkbox.is-checked .checkbox-input .checkbox-inner, .form-checkbox.is-indeterminate .checkbox-input .checkbox-inner {
|
||||||
background-color: #409eff;
|
background-color: #409eff;
|
||||||
border-color: #409eff;
|
border-color: #409eff;
|
||||||
}
|
}
|
||||||
|
@ -77,6 +77,18 @@
|
||||||
border-color: #c0c4cc;
|
border-color: #c0c4cc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form-checkbox.is-indeterminate .checkbox-input .checkbox-inner:before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
display: block;
|
||||||
|
background-color: #fff;
|
||||||
|
height: 2px;
|
||||||
|
transform: scale(.5);
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
@media (min-width: 576px) {
|
@media (min-width: 576px) {
|
||||||
.form-inline .form-checkbox {
|
.form-inline .form-checkbox {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|
Loading…
Reference in New Issue