From 8d7b3ef73eb373bd133c52a375c978e60119c49a Mon Sep 17 00:00:00 2001 From: Tony-ST0754 <6914529@qq.com> Date: Fri, 9 Oct 2026 18:12:19 +0800 Subject: [PATCH 01/17] =?UTF-8?q?feat(table):=E6=96=B0=E5=A2=9EKeepAdding?= =?UTF-8?q?=E5=8F=82=E6=95=B0=EF=BC=8C=E7=94=A8=E6=9D=A5=E5=A4=84=E7=90=86?= =?UTF-8?q?=E8=BF=9E=E7=BB=AD=E6=96=B0=E5=A2=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1、给`Table`组件新增参数`KeepAdding`,用来处理新增数据时是否能连续新增,即编辑弹窗不关闭连续新增 2、在连续新增时,如果需要全部保留或部分保留上一轮的信息,可通过组件中`OnAddAsync`回调来处理 3、由于要在不关闭编辑弹窗的情况下继续新增数据,因此,在弹窗中`Popup`、`Drawer`模式中,既要考虑下一轮新增时编辑弹窗中的绑定验证的模型是否成功创建或创建异常,还要考虑弹窗关闭时的清理问题,整体对`Popup`、`Drawer`模式的弹窗改动比较大 3.1、给`ITableEditDialogOption`及相应的`...DialogOption`新增`KeepOpenAfterSave`参数,用来告知编辑弹窗保存成功后是否继续保存弹窗为打开状态,新增`CreateNextModelAsync`回调供弹窗内部配合`KeepOpenAfterSave`参数使用,作用为连续新增时创建新的绑定模型 3.2、新增`DialogCloseContext`类,用来负责弹窗关闭相关的职责处理,`Popup`、`Drawer`模式,使用统一关闭状态;关闭检查按所属窗口执行,逐个等待回调并尊重否决结要,避免重复关闭及旧窗口清理误伤新窗口 3.3、关闭状态、检查和收尾按当前展示实例管理,保存后的回调或刷新失败时,显示错误并禁止重复保存当前记录,同时不允许初始化下一条记录。 3.4、编辑弹窗的关闭逻辑,由原来的直接关闭,变更为判断是否允许关闭 3.5、连续新增时,保存成功后创建下一条模型,重建表单状态;正文、编辑项和Footer使用同一当前模型 3.6、保存、创建下一条期间禁止重复提交和关闭;忽略旧表单、旧展示及组件销毁后的异步提交 3.7、保存未成功时允许重试;已实际保存、但后续处理失败时阻止重复保存。下一条初始化失败会提示错误,允许正常关闭并清理。 --- .../Components/Dialog/Dialog.razor | 2 +- .../Components/Dialog/Dialog.razor.cs | 79 +- .../Components/Dialog/EditDialog.razor | 8 +- .../Components/Dialog/EditDialog.razor.cs | 223 ++- .../Components/Dialog/EditDialogOption.cs | 10 + .../Components/Drawer/Drawer.razor | 4 +- .../Components/Drawer/Drawer.razor.cs | 69 +- .../Components/Drawer/DrawerContainer.cs | 38 +- .../Components/Drawer/DrawerOption.cs | 2 + .../Components/Drawer/DrawerService.cs | 12 +- .../Components/Modal/DialogCloseContext.cs | 105 ++ .../Components/Modal/Modal.razor.cs | 124 +- .../Components/Modal/Modal.razor.js | 242 ++-- .../Components/Modal/ModalDialog.razor | 174 +-- .../Components/Modal/ModalDialog.razor.cs | 11 +- .../Table/ITableEditDialogOption.cs | 12 + .../Components/Table/Table.razor.Toolbar.cs | 195 ++- .../Components/Table/TableEditDrawerOption.cs | 10 + .../Extensions/DrawerExtensions.cs | 36 +- .../TableEditDialogOptionExtensions.cs | 2 + src/BootstrapBlazor/Locales/en.json | 4 +- src/BootstrapBlazor/Locales/zh.json | 4 +- .../Components/EditDialogLifecycleTest.cs | 1194 +++++++++++++++++ test/UnitTest/Components/EditDialogTest.cs | 239 ++++ test/UnitTest/Components/TableDialogTest.cs | 511 +++++++ test/UnitTest/Components/TableDrawerTest.cs | 97 ++ test/UnitTest/Services/DrawerServiceTest.cs | 188 +++ 27 files changed, 3274 insertions(+), 321 deletions(-) create mode 100644 src/BootstrapBlazor/Components/Modal/DialogCloseContext.cs create mode 100644 test/UnitTest/Components/EditDialogLifecycleTest.cs diff --git a/src/BootstrapBlazor/Components/Dialog/Dialog.razor b/src/BootstrapBlazor/Components/Dialog/Dialog.razor index 53b5bb91b97..837c3555f87 100644 --- a/src/BootstrapBlazor/Components/Dialog/Dialog.razor +++ b/src/BootstrapBlazor/Components/Dialog/Dialog.razor @@ -2,7 +2,7 @@ @inherits BootstrapComponentBase @for (var index = 0; index < DialogParameters.Keys.Count; index++) { diff --git a/src/BootstrapBlazor/Components/Dialog/Dialog.razor.cs b/src/BootstrapBlazor/Components/Dialog/Dialog.razor.cs index 7d555585284..c0c31b6ef0d 100644 --- a/src/BootstrapBlazor/Components/Dialog/Dialog.razor.cs +++ b/src/BootstrapBlazor/Components/Dialog/Dialog.razor.cs @@ -18,10 +18,9 @@ public partial class Dialog : IDisposable [NotNull] private Modal? _modal = null; private Func? _onShownAsync = null; - private Func? _onCloseAsync = null; - private Func>? _onClosingAsync = null; - - private readonly Dictionary, (bool IsKeyboard, bool IsBackdrop, Func? OnCloseCallback)> DialogParameters = []; + private readonly Dictionary, + (bool IsKeyboard, bool IsBackdrop, bool? IsFade, Func? OnShownCallback, + Func? OnCloseCallback, Func>? OnClosingCallback)> DialogParameters = []; private Dictionary? _currentParameter; private bool _isKeyboard = false; private bool _isBackdrop = false; @@ -59,37 +58,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender) private async Task Show(DialogOption option) { - _onShownAsync = async () => - { - if (option.OnShownAsync != null) - { - await option.OnShownAsync(); - } - }; - - _onCloseAsync = async () => - { - // Remove current DialogParameter - if (_currentParameter != null) - { - DialogParameters.Remove(_currentParameter, out var v); - if (v.OnCloseCallback != null) - { - await v.OnCloseCallback(); - } - - // Support for multiple dialogs - var p = DialogParameters.LastOrDefault(); - _currentParameter = p.Key; - _isKeyboard = p.Value.IsKeyboard; - _isBackdrop = p.Value.IsBackdrop; - - StateHasChanged(); - } - }; - - _onClosingAsync = option.OnClosingAsync; - + _onShownAsync = option.OnShownAsync; _isKeyboard = option.IsKeyboard; _isBackdrop = option.IsBackdrop; _isFade = option.IsFade; @@ -159,11 +128,41 @@ private async Task Show(DialogOption option) _currentParameter = parameters; // Add ModalDialog to the container - DialogParameters.Add(parameters, (_isKeyboard, _isBackdrop, option.OnCloseAsync)); + DialogParameters.Add(parameters, (_isKeyboard, _isBackdrop, _isFade, option.OnShownAsync, option.OnCloseAsync, option.OnClosingAsync)); await InvokeAsync(StateHasChanged); } - private static RenderFragment RenderDialog(int index, Dictionary parameter) => builder => + private async Task CloseAsync(Dictionary parameter) + { + // Remove current DialogParameter + if (!DialogParameters.Remove(parameter, out var settings)) + { + return; + } + // Support for multiple dialogs + if (ReferenceEquals(_currentParameter, parameter)) + { + var current = DialogParameters.LastOrDefault(); + _currentParameter = current.Key; + _isKeyboard = current.Value.IsKeyboard; + _isBackdrop = current.Value.IsBackdrop; + _isFade = current.Value.IsFade; + _onShownAsync = current.Value.OnShownCallback; + } + try + { + if (settings.OnCloseCallback != null) + { + await settings.OnCloseCallback(); + } + } + finally + { + StateHasChanged(); + } + } + + private RenderFragment RenderDialog(int index, Dictionary parameter) => builder => { if (index > 0) { @@ -173,6 +172,12 @@ private static RenderFragment RenderDialog(int index, Dictionary builder.OpenComponent(100 + index); builder.AddMultipleAttributes(101 + index, parameter); builder.SetKey(parameter); + builder.AddComponentReferenceCapture(102 + index, component => + { + var dialog = (ModalDialog)component; + dialog.OnClosingCallback = DialogParameters[parameter].OnClosingCallback; + dialog.OnCloseCallback = () => CloseAsync(parameter); + }); builder.CloseComponent(); }; diff --git a/src/BootstrapBlazor/Components/Dialog/EditDialog.razor b/src/BootstrapBlazor/Components/Dialog/EditDialog.razor index 4591eee23fa..0cb96f46210 100644 --- a/src/BootstrapBlazor/Components/Dialog/EditDialog.razor +++ b/src/BootstrapBlazor/Components/Dialog/EditDialog.razor @@ -3,14 +3,18 @@ @inherits DialogBase @attribute [BootstrapModuleAutoLoader("Dialog/EditDialog.razor.js", AutoInvokeInit = false, AutoInvokeDispose = false)] - + @if (_saveError != null) + { + + } @if (BodyTemplate != null) {
- @BodyTemplate(Model) + @BodyTemplate(_currentModel)