diff --git a/src/BootstrapBlazor/BootstrapBlazor.csproj b/src/BootstrapBlazor/BootstrapBlazor.csproj index e2bbb2fc3..ebcd3ad7d 100644 --- a/src/BootstrapBlazor/BootstrapBlazor.csproj +++ b/src/BootstrapBlazor/BootstrapBlazor.csproj @@ -1,7 +1,7 @@ - 6.4.1-beta02 + 6.4.1-beta03 diff --git a/src/BootstrapBlazor/Components/Dialog/DialogService.cs b/src/BootstrapBlazor/Components/Dialog/DialogService.cs index abe92f6c7..38275c386 100644 --- a/src/BootstrapBlazor/Components/Dialog/DialogService.cs +++ b/src/BootstrapBlazor/Components/Dialog/DialogService.cs @@ -19,195 +19,4 @@ public class DialogService : BootstrapServiceBase /// 指定弹窗组件 默认为 null 使用 组件内置弹窗组件 /// public Task Show(DialogOption option, Dialog? dialog = null) => Invoke(option, dialog); - - /// - /// 弹出搜索对话框 - /// - /// SearchDialogOption 配置类实例 - /// 指定弹窗组件 默认为 null 使用 组件内置弹窗组件 - public async Task ShowSearchDialog(SearchDialogOption option, Dialog? dialog = null) - { - var parameters = new Dictionary - { - [nameof(SearchDialog.ShowLabel)] = option.ShowLabel, - [nameof(SearchDialog.Items)] = option.Items ?? Utility.GenerateColumns(item => item.Searchable), - [nameof(SearchDialog.OnResetSearchClick)] = new Func(async () => - { - option.OnCloseAsync = null; - option.Dialog.RemoveDialog(); - if (option.OnResetSearchClick != null) - { - await option.OnResetSearchClick(); - } - }), - [nameof(SearchDialog.OnSearchClick)] = new Func(async () => - { - option.OnCloseAsync = null; - option.Dialog.RemoveDialog(); - if (option.OnSearchClick != null) - { - await option.OnSearchClick(); - } - }), - [nameof(SearchDialog.RowType)] = option.RowType, - [nameof(SearchDialog.LabelAlign)] = option.LabelAlign, - [nameof(ItemsPerRow)] = option.ItemsPerRow, - [nameof(SearchDialog.ResetButtonText)] = option.ResetButtonText, - [nameof(SearchDialog.QueryButtonText)] = option.QueryButtonText, - [nameof(SearchDialog.Model)] = option.Model, - [nameof(SearchDialog.BodyTemplate)] = option.DialogBodyTemplate - }; - option.Component = BootstrapDynamicComponent.CreateComponent>(parameters); - await Invoke(option, dialog); - } - - /// - /// 弹出编辑对话框 - /// - /// EditDialogOption 配置类实例 - /// - public async Task ShowEditDialog(EditDialogOption option, Dialog? dialog = null) - { - var parameters = new Dictionary - { - [nameof(EditDialog.ShowLoading)] = option.ShowLoading, - [nameof(EditDialog.ShowLabel)] = option.ShowLabel, - [nameof(EditDialog.Items)] = option.Items ?? Utility.GenerateColumns(item => item.Editable), - [nameof(EditDialog.OnCloseAsync)] = new Func(async () => - { - option.Dialog.RemoveDialog(); - await option.Dialog.CloseOrPopDialog(); - }), - [nameof(EditDialog.OnSaveAsync)] = new Func(async context => - { - if (option.OnEditAsync != null) - { - var ret = await option.OnEditAsync(context); - if (ret) - { - option.Dialog.RemoveDialog(); - await option.Dialog.CloseOrPopDialog(); - } - } - }), - [nameof(EditDialog.RowType)] = option.RowType, - [nameof(EditDialog.LabelAlign)] = option.LabelAlign, - [nameof(EditDialog.IsTracking)] = option.IsTracking, - [nameof(EditDialog.ItemChangedType)] = option.ItemChangedType, - [nameof(ItemsPerRow)] = option.ItemsPerRow, - [nameof(EditDialog.CloseButtonText)] = option.CloseButtonText, - [nameof(EditDialog.SaveButtonText)] = option.SaveButtonText, - [nameof(EditDialog.Model)] = option.Model, - [nameof(EditDialog.BodyTemplate)] = option.DialogBodyTemplate - }; - - option.Component = BootstrapDynamicComponent.CreateComponent>(parameters); - await Invoke(option, dialog); - } - - /// - /// 弹出带结果的对话框 - /// - /// 对话框参数 - /// 指定弹窗组件 默认为 null 使用 组件内置弹窗组件 - /// - public async Task ShowModal(ResultDialogOption option, Dialog? dialog = null) - where TDialog : IComponent, IResultDialog - { - IResultDialog? resultDialog = null; - var result = DialogResult.Close; - - option.BodyTemplate = builder => - { - var index = 0; - builder.OpenComponent(index++, typeof(TDialog)); - if (option.ComponentParamters != null) - { - foreach (var p in option.ComponentParamters) - { - builder.AddAttribute(index++, p.Key, p.Value); - } - } - builder.AddComponentReferenceCapture(index++, com => resultDialog = (IResultDialog)com); - builder.CloseComponent(); - }; - - option.FooterTemplate = BootstrapDynamicComponent.CreateComponent(new Dictionary - { - [nameof(ResultDialogFooter.ButtonCloseText)] = option.ButtonCloseText, - [nameof(ResultDialogFooter.ButtonNoText)] = option.ButtonNoText, - [nameof(ResultDialogFooter.ButtonYesText)] = option.ButtonYesText, - [nameof(ResultDialogFooter.ShowCloseButton)] = option.ShowCloseButton, - [nameof(ResultDialogFooter.ButtonCloseColor)] = option.ButtonCloseColor, - [nameof(ResultDialogFooter.ButtonCloseIcon)] = option.ButtonCloseIcon, - [nameof(ResultDialogFooter.OnClickClose)] = new Func(async () => - { - result = DialogResult.Close; - if (option.OnCloseAsync != null) { await option.OnCloseAsync(); } - }), - - [nameof(ResultDialogFooter.ShowYesButton)] = option.ShowYesButton, - [nameof(ResultDialogFooter.ButtonYesColor)] = option.ButtonYesColor, - [nameof(ResultDialogFooter.ButtonYesIcon)] = option.ButtonYesIcon, - [nameof(ResultDialogFooter.OnClickYes)] = new Func(async () => - { - result = DialogResult.Yes; - if (option.OnCloseAsync != null) { await option.OnCloseAsync(); } - }), - - [nameof(ResultDialogFooter.ShowNoButton)] = option.ShowNoButton, - [nameof(ResultDialogFooter.ButtonNoColor)] = option.ButtonNoColor, - [nameof(ResultDialogFooter.ButtonNoIcon)] = option.ButtonNoIcon, - [nameof(ResultDialogFooter.OnClickNo)] = new Func(async () => - { - result = DialogResult.No; - if (option.OnCloseAsync != null) { await option.OnCloseAsync(); } - }) - }).Render(); - - var closeCallback = option.OnCloseAsync; - option.OnCloseAsync = async () => - { - if (resultDialog != null && await resultDialog.OnClosing(result)) - { - await resultDialog.OnClose(result); - if (closeCallback != null) - { - await closeCallback(); - } - - // Modal 与 ModalDialog 的 OnClose 事件陷入死循环 - // option.OnClose -> Modal.Close -> ModalDialog.Close -> ModalDialog.OnClose -> option.OnClose - option.OnCloseAsync = null; - await option.Dialog.Close(); - option.ReturnTask.SetResult(result); - } - }; - - await Invoke(option, dialog); - return await option.ReturnTask.Task; - } - - /// - /// 弹出保存对话窗方法 - /// - /// - /// - /// - /// - /// - /// - /// - public async Task ShowSaveDialog(string title, Func> saveCallback, Dictionary? parameters = null, Action? configureOption = null, Dialog? dialog = null) where TComponent : ComponentBase - { - var option = new DialogOption() - { - Title = title, - Component = BootstrapDynamicComponent.CreateComponent(parameters), - ShowSaveButton = true, - OnSaveAsync = saveCallback - }; - configureOption?.Invoke(option); - await Invoke(option, dialog); - } } diff --git a/src/BootstrapBlazor/Extensions/DialogServiceExtensions.cs b/src/BootstrapBlazor/Extensions/DialogServiceExtensions.cs new file mode 100644 index 000000000..7c0bb8d07 --- /dev/null +++ b/src/BootstrapBlazor/Extensions/DialogServiceExtensions.cs @@ -0,0 +1,209 @@ +// Copyright (c) Argo Zhang (argo@163.com). All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Website: https://www.blazor.zone or https://argozhang.github.io/ + +using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Forms; + +namespace BootstrapBlazor.Components; + +/// +/// DialogService 扩展方法 +/// +public static class DialogServiceExtensions +{ + /// + /// 弹出搜索对话框 + /// + /// DialogService 服务实例 + /// SearchDialogOption 配置类实例 + /// 指定弹窗组件 默认为 null 使用 组件内置弹窗组件 + public static async Task ShowSearchDialog(this DialogService service, SearchDialogOption option, Dialog? dialog = null) + { + var parameters = new Dictionary + { + [nameof(SearchDialog.ShowLabel)] = option.ShowLabel, + [nameof(SearchDialog.Items)] = option.Items ?? Utility.GenerateColumns(item => item.Searchable), + [nameof(SearchDialog.OnResetSearchClick)] = new Func(async () => + { + option.OnCloseAsync = null; + option.Dialog.RemoveDialog(); + if (option.OnResetSearchClick != null) + { + await option.OnResetSearchClick(); + } + }), + [nameof(SearchDialog.OnSearchClick)] = new Func(async () => + { + option.OnCloseAsync = null; + option.Dialog.RemoveDialog(); + if (option.OnSearchClick != null) + { + await option.OnSearchClick(); + } + }), + [nameof(SearchDialog.RowType)] = option.RowType, + [nameof(SearchDialog.LabelAlign)] = option.LabelAlign, + [nameof(ItemsPerRow)] = option.ItemsPerRow, + [nameof(SearchDialog.ResetButtonText)] = option.ResetButtonText, + [nameof(SearchDialog.QueryButtonText)] = option.QueryButtonText, + [nameof(SearchDialog.Model)] = option.Model, + [nameof(SearchDialog.BodyTemplate)] = option.DialogBodyTemplate + }; + option.Component = BootstrapDynamicComponent.CreateComponent>(parameters); + await service.Show(option, dialog); + } + + /// + /// 弹出编辑对话框 + /// + /// DialogService 服务实例 + /// EditDialogOption 配置类实例 + /// + public static async Task ShowEditDialog(this DialogService service, EditDialogOption option, Dialog? dialog = null) + { + var parameters = new Dictionary + { + [nameof(EditDialog.ShowLoading)] = option.ShowLoading, + [nameof(EditDialog.ShowLabel)] = option.ShowLabel, + [nameof(EditDialog.Items)] = option.Items ?? Utility.GenerateColumns(item => item.Editable), + [nameof(EditDialog.OnCloseAsync)] = new Func(async () => + { + option.Dialog.RemoveDialog(); + await option.Dialog.CloseOrPopDialog(); + }), + [nameof(EditDialog.OnSaveAsync)] = new Func(async context => + { + if (option.OnEditAsync != null) + { + var ret = await option.OnEditAsync(context); + if (ret) + { + option.Dialog.RemoveDialog(); + await option.Dialog.CloseOrPopDialog(); + } + } + }), + [nameof(EditDialog.RowType)] = option.RowType, + [nameof(EditDialog.LabelAlign)] = option.LabelAlign, + [nameof(EditDialog.IsTracking)] = option.IsTracking, + [nameof(EditDialog.ItemChangedType)] = option.ItemChangedType, + [nameof(ItemsPerRow)] = option.ItemsPerRow, + [nameof(EditDialog.CloseButtonText)] = option.CloseButtonText, + [nameof(EditDialog.SaveButtonText)] = option.SaveButtonText, + [nameof(EditDialog.Model)] = option.Model, + [nameof(EditDialog.BodyTemplate)] = option.DialogBodyTemplate + }; + + option.Component = BootstrapDynamicComponent.CreateComponent>(parameters); + await service.Show(option, dialog); + } + + /// + /// 弹出带结果的对话框 + /// + /// DialogService 服务实例 + /// 对话框参数 + /// 指定弹窗组件 默认为 null 使用 组件内置弹窗组件 + /// + public static async Task ShowModal(this DialogService service, ResultDialogOption option, Dialog? dialog = null) + where TDialog : IComponent, IResultDialog + { + IResultDialog? resultDialog = null; + var result = DialogResult.Close; + + option.BodyTemplate = builder => + { + var index = 0; + builder.OpenComponent(index++, typeof(TDialog)); + if (option.ComponentParamters != null) + { + foreach (var p in option.ComponentParamters) + { + builder.AddAttribute(index++, p.Key, p.Value); + } + } + builder.AddComponentReferenceCapture(index++, com => resultDialog = (IResultDialog)com); + builder.CloseComponent(); + }; + + option.FooterTemplate = BootstrapDynamicComponent.CreateComponent(new Dictionary + { + [nameof(ResultDialogFooter.ButtonCloseText)] = option.ButtonCloseText, + [nameof(ResultDialogFooter.ButtonNoText)] = option.ButtonNoText, + [nameof(ResultDialogFooter.ButtonYesText)] = option.ButtonYesText, + [nameof(ResultDialogFooter.ShowCloseButton)] = option.ShowCloseButton, + [nameof(ResultDialogFooter.ButtonCloseColor)] = option.ButtonCloseColor, + [nameof(ResultDialogFooter.ButtonCloseIcon)] = option.ButtonCloseIcon, + [nameof(ResultDialogFooter.OnClickClose)] = new Func(async () => + { + result = DialogResult.Close; + if (option.OnCloseAsync != null) { await option.OnCloseAsync(); } + }), + + [nameof(ResultDialogFooter.ShowYesButton)] = option.ShowYesButton, + [nameof(ResultDialogFooter.ButtonYesColor)] = option.ButtonYesColor, + [nameof(ResultDialogFooter.ButtonYesIcon)] = option.ButtonYesIcon, + [nameof(ResultDialogFooter.OnClickYes)] = new Func(async () => + { + result = DialogResult.Yes; + if (option.OnCloseAsync != null) { await option.OnCloseAsync(); } + }), + + [nameof(ResultDialogFooter.ShowNoButton)] = option.ShowNoButton, + [nameof(ResultDialogFooter.ButtonNoColor)] = option.ButtonNoColor, + [nameof(ResultDialogFooter.ButtonNoIcon)] = option.ButtonNoIcon, + [nameof(ResultDialogFooter.OnClickNo)] = new Func(async () => + { + result = DialogResult.No; + if (option.OnCloseAsync != null) { await option.OnCloseAsync(); } + }) + }).Render(); + + var closeCallback = option.OnCloseAsync; + option.OnCloseAsync = async () => + { + if (resultDialog != null && await resultDialog.OnClosing(result)) + { + await resultDialog.OnClose(result); + if (closeCallback != null) + { + await closeCallback(); + } + + // Modal 与 ModalDialog 的 OnClose 事件陷入死循环 + // option.OnClose -> Modal.Close -> ModalDialog.Close -> ModalDialog.OnClose -> option.OnClose + option.OnCloseAsync = null; + await option.Dialog.Close(); + option.ReturnTask.SetResult(result); + } + }; + + await service.Show(option, dialog); + return await option.ReturnTask.Task; + } + + /// + /// 弹出保存对话窗方法 + /// + /// + /// DialogService 服务实例 + /// + /// + /// + /// + /// + /// + public static async Task ShowSaveDialog(this DialogService service, string title, Func> saveCallback, Dictionary? parameters = null, Action? configureOption = null, Dialog? dialog = null) where TComponent : ComponentBase + { + var option = new DialogOption() + { + Title = title, + Component = BootstrapDynamicComponent.CreateComponent(parameters), + ShowSaveButton = true, + OnSaveAsync = saveCallback + }; + configureOption?.Invoke(option); + await service.Show(option, dialog); + } +}