diff --git a/src/BootstrapBlazor.Shared/Extensions/MenusLocalizerExtensions.cs b/src/BootstrapBlazor.Shared/Extensions/MenusLocalizerExtensions.cs index 080bd903a0d..cda750cfd11 100644 --- a/src/BootstrapBlazor.Shared/Extensions/MenusLocalizerExtensions.cs +++ b/src/BootstrapBlazor.Shared/Extensions/MenusLocalizerExtensions.cs @@ -743,6 +743,11 @@ void AddDockView(DemoMenuItem item) { Text = Localizer["DockViewLock"], Url = "dock-view/lock" + }, + new() + { + Text = Localizer["DockViewLayout"], + Url = "dock-view/layout" } }; AddBadge(item, count: 1); diff --git a/src/BootstrapBlazor.Shared/Locales/en.json b/src/BootstrapBlazor.Shared/Locales/en.json index 338f0431740..49c70103371 100644 --- a/src/BootstrapBlazor.Shared/Locales/en.json +++ b/src/BootstrapBlazor.Shared/Locales/en.json @@ -4503,6 +4503,7 @@ "DockViewComplex": "Complex", "DockViewVisible": "Visible", "DockViewLock": "Lock", + "DockViewLayout": "Custom", "OtherComponents": "Others", "MouseFollowerIntro": "MouseFollower", "Live2DDisplayIntro": "Live2D Widget", diff --git a/src/BootstrapBlazor.Shared/Locales/zh.json b/src/BootstrapBlazor.Shared/Locales/zh.json index 97cebe49ad3..f29ceb95e66 100644 --- a/src/BootstrapBlazor.Shared/Locales/zh.json +++ b/src/BootstrapBlazor.Shared/Locales/zh.json @@ -4503,6 +4503,7 @@ "DockViewComplex": "组合布局", "DockViewVisible": "可见性切换", "DockViewLock": "布局锁定", + "DockViewLayout": "布局自定义", "OtherComponents": "其他组件", "MouseFollowerIntro": "鼠标跟随", "Live2DDisplayIntro": "Live2D 插件", diff --git a/src/BootstrapBlazor.Shared/Samples/DockViews/DockViewLayout.razor b/src/BootstrapBlazor.Shared/Samples/DockViews/DockViewLayout.razor new file mode 100644 index 00000000000..f013af87085 --- /dev/null +++ b/src/BootstrapBlazor.Shared/Samples/DockViews/DockViewLayout.razor @@ -0,0 +1,94 @@ +@page "/dock-view/layout" +@inherits BaseDockView + +

自定义布局

+

通过设置 DockView 的属性 LayoutConfig 初始化控制面板的显示布局, 方法 GetLayoutConfig 获取面板的显示布局

+ + + + + + + + @LayoutConfigSave + + +
+ + + + + +
+
+
+
+ + + + + + + + + + + + + +
+
+ + + + + + + +@code { + [NotNull] + private DockView? DockView { get; set; } + + private async Task GetLayout() + { + LayoutConfigSave = await DockView.GetLayoutConfig(); + } + + private Task Reset() => DockView.Reset(); + + private void OnToggleLayout1() + { + LayoutConfig = LayoutConfig1; + } + + private void OnToggleLayout2() + { + LayoutConfig = LayoutConfig2; + } + + private void OnToggleLayout3() + { + LayoutConfig = LayoutConfig3; + } + + string? LayoutConfig; + + string? LayoutConfigSave; + + string LayoutConfig1 = """" +{"root":{"type":"row","content":[{"type":"column","content":[{"type":"stack","content":[{"type":"component","content":[],"width":50,"minWidth":0,"height":50,"minHeight":0,"id":"bb_45517422","maximised":false,"isClosable":true,"reorderEnabled":true,"title":"标签三","componentType":"component","componentState":{"id":"bb_45517422","showClose":true,"class":null,"key":"标签三","lock":false}}],"width":50,"minWidth":0,"height":33.460076045627375,"minHeight":0,"id":"","isClosable":true,"maximised":false,"activeItemIndex":0},{"type":"stack","content":[{"type":"component","content":[],"width":50,"minWidth":0,"height":50,"minHeight":0,"id":"bb_42425232","maximised":false,"isClosable":true,"reorderEnabled":true,"title":"标签二","componentType":"component","componentState":{"id":"bb_42425232","showClose":true,"class":null,"key":"标签二","lock":false}}],"width":50,"minWidth":0,"height":66.53992395437263,"minHeight":0,"id":"","isClosable":true,"maximised":false,"activeItemIndex":0}],"width":50,"minWidth":50,"height":50,"minHeight":50,"id":"","isClosable":true},{"type":"stack","content":[{"type":"component","content":[],"width":50,"minWidth":0,"height":50,"minHeight":0,"id":"bb_21184535","maximised":false,"isClosable":true,"reorderEnabled":true,"title":"标签一","componentType":"component","componentState":{"id":"bb_21184535","showClose":true,"class":null,"key":"标签一","lock":false}}],"width":50,"minWidth":0,"height":50,"minHeight":0,"id":"bb_54183781","isClosable":true,"maximised":false,"activeItemIndex":0}],"width":50,"minWidth":50,"height":50,"minHeight":50,"id":"","isClosable":true},"openPopouts":[],"settings":{"constrainDragToContainer":true,"reorderEnabled":true,"popoutWholeStack":false,"blockedPopoutsThrowError":true,"closePopoutsOnUnload":true,"responsiveMode":"none","tabOverlapAllowance":0,"reorderOnTabMenuClick":true,"tabControlOffset":10,"popInOnClose":false},"dimensions":{"borderWidth":5,"borderGrabWidth":5,"minItemHeight":10,"minItemWidth":10,"headerHeight":25,"dragProxyWidth":300,"dragProxyHeight":200},"header":{"show":"top","popout":"lock/unlock","dock":"dock","close":"close","maximise":"maximise","minimise":"minimise","tabDropdown":"additional tabs"},"resolved":true} +""""; + + string LayoutConfig2 = """" +{"root":{"type":"row","content":[{"type":"stack","content":[{"type":"component","content":[],"width":50,"minWidth":0,"height":50,"minHeight":0,"id":"bb_24636646","maximised":false,"isClosable":true,"reorderEnabled":true,"title":"标签三","componentType":"component","componentState":{"id":"bb_24636646","showClose":true,"class":null,"key":"标签三","lock":false}}],"width":33.333333333333336,"minWidth":0,"height":50,"minHeight":0,"id":"","isClosable":true,"maximised":false,"activeItemIndex":0},{"type":"stack","content":[{"type":"component","content":[],"width":50,"minWidth":0,"height":50,"minHeight":0,"id":"bb_60600063","maximised":false,"isClosable":true,"reorderEnabled":true,"title":"标签一","componentType":"component","componentState":{"id":"bb_60600063","showClose":true,"class":null,"key":"标签一","lock":false}}],"width":33.333333333333336,"minWidth":0,"height":50,"minHeight":0,"id":"bb_54183781","isClosable":true,"maximised":false,"activeItemIndex":0},{"type":"stack","content":[{"type":"component","content":[],"width":50,"minWidth":0,"height":50,"minHeight":0,"id":"bb_6744750","maximised":false,"isClosable":true,"reorderEnabled":true,"title":"标签二","componentType":"component","componentState":{"id":"bb_6744750","showClose":true,"class":null,"key":"标签二","lock":false}}],"width":33.33333333333333,"minWidth":0,"height":50,"minHeight":0,"id":"bb_6744750","isClosable":true,"maximised":false,"activeItemIndex":0}],"width":50,"minWidth":50,"height":50,"minHeight":50,"id":"","isClosable":true},"openPopouts":[],"settings":{"constrainDragToContainer":true,"reorderEnabled":true,"popoutWholeStack":false,"blockedPopoutsThrowError":true,"closePopoutsOnUnload":true,"responsiveMode":"none","tabOverlapAllowance":0,"reorderOnTabMenuClick":true,"tabControlOffset":10,"popInOnClose":false},"dimensions":{"borderWidth":5,"borderGrabWidth":5,"minItemHeight":10,"minItemWidth":10,"headerHeight":25,"dragProxyWidth":300,"dragProxyHeight":200},"header":{"show":"top","popout":"lock/unlock","dock":"dock","close":"close","maximise":"maximise","minimise":"minimise","tabDropdown":"additional tabs"},"resolved":true} +""""; + + string LayoutConfig3 = """" +{"root":{"type":"stack","content":[{"type":"component","content":[],"width":50,"minWidth":0,"height":50,"minHeight":0,"id":"bb_24636646","maximised":false,"isClosable":true,"reorderEnabled":true,"title":"标签三","componentType":"component","componentState":{"id":"bb_24636646","showClose":true,"class":null,"key":"标签三","lock":false}},{"type":"component","content":[],"width":50,"minWidth":0,"height":50,"minHeight":0,"id":"bb_60600063","maximised":false,"isClosable":true,"reorderEnabled":true,"title":"标签一","componentType":"component","componentState":{"id":"bb_60600063","showClose":true,"class":null,"key":"标签一","lock":false}},{"type":"component","content":[],"width":50,"minWidth":0,"height":50,"minHeight":0,"id":"bb_6744750","maximised":false,"isClosable":true,"reorderEnabled":true,"title":"标签二","componentType":"component","componentState":{"id":"bb_6744750","showClose":true,"class":null,"key":"标签二","lock":false}}],"width":50,"minWidth":0,"height":50,"minHeight":0,"id":"bb_60600063","isClosable":true,"maximised":false,"activeItemIndex":1},"openPopouts":[],"settings":{"constrainDragToContainer":true,"reorderEnabled":true,"popoutWholeStack":false,"blockedPopoutsThrowError":true,"closePopoutsOnUnload":true,"responsiveMode":"none","tabOverlapAllowance":0,"reorderOnTabMenuClick":true,"tabControlOffset":10,"popInOnClose":false},"dimensions":{"borderWidth":5,"borderGrabWidth":5,"minItemHeight":10,"minItemWidth":10,"headerHeight":25,"dragProxyWidth":300,"dragProxyHeight":200},"header":{"show":"top","popout":"lock/unlock","dock":"dock","close":"close","maximise":"maximise","minimise":"minimise","tabDropdown":"additional tabs"},"resolved":true} +""""; +} diff --git a/src/BootstrapBlazor.Shared/Samples/DockViews/DockViewLayout.razor.css b/src/BootstrapBlazor.Shared/Samples/DockViews/DockViewLayout.razor.css new file mode 100644 index 00000000000..acc045f20f3 --- /dev/null +++ b/src/BootstrapBlazor.Shared/Samples/DockViews/DockViewLayout.razor.css @@ -0,0 +1,14 @@ +.config { + display: block; + margin-top: 1rem; + border: 1px solid var(--bs-secondary); + border-radius: var(--bs-border-radius); + padding: 0.5rem; + overflow: auto; + height: 88px; +} + +.dock-toggle-demo { + height: calc(100vh - 424px); + margin-top: 1rem; +} diff --git a/src/BootstrapBlazor.Shared/Samples/DockViews/DockViewLock.razor b/src/BootstrapBlazor.Shared/Samples/DockViews/DockViewLock.razor index 0eb0902e559..fa06e88977e 100644 --- a/src/BootstrapBlazor.Shared/Samples/DockViews/DockViewLock.razor +++ b/src/BootstrapBlazor.Shared/Samples/DockViews/DockViewLock.razor @@ -1,16 +1,16 @@ @page "/dock-view/lock" @inherits BaseDockView -

锁定面板

+

锁定面板

通过设置 DockView 的属性 IsLock,控制所有面板是否能拖动

通过设置 DockComponent 的属性 IsLock,控制某个面板是否能拖动

- + - + diff --git a/src/BootstrapBlazor.Shared/Samples/DockViews/DockViewVisible.razor b/src/BootstrapBlazor.Shared/Samples/DockViews/DockViewVisible.razor index 9263461a4cd..e94565949b0 100644 --- a/src/BootstrapBlazor.Shared/Samples/DockViews/DockViewVisible.razor +++ b/src/BootstrapBlazor.Shared/Samples/DockViews/DockViewVisible.razor @@ -1,7 +1,7 @@ @page "/dock-view/visible" @inherits BaseDockView -

可隐藏的面板

+

可隐藏的面板

通过设置 DockComponent 的属性 Visible 控制面板的显示和隐藏

diff --git a/src/Extensions/Components/BootstrapBlazor.Dock/BootstrapBlazor.Dock.csproj b/src/Extensions/Components/BootstrapBlazor.Dock/BootstrapBlazor.Dock.csproj index e1e89cf26ac..24688a2cc70 100644 --- a/src/Extensions/Components/BootstrapBlazor.Dock/BootstrapBlazor.Dock.csproj +++ b/src/Extensions/Components/BootstrapBlazor.Dock/BootstrapBlazor.Dock.csproj @@ -1,7 +1,7 @@ - 7.0.11 + 7.0.12 diff --git a/src/Extensions/Components/BootstrapBlazor.Dock/Components/DockView/DockView.razor.cs b/src/Extensions/Components/BootstrapBlazor.Dock/Components/DockView/DockView.razor.cs index 8de7a4bd6b9..5c4f074c31f 100644 --- a/src/Extensions/Components/BootstrapBlazor.Dock/Components/DockView/DockView.razor.cs +++ b/src/Extensions/Components/BootstrapBlazor.Dock/Components/DockView/DockView.razor.cs @@ -88,6 +88,12 @@ public partial class DockView [Parameter] public string? LocalStoragePrefix { get; set; } + /// + /// 获得/设置 布局配置 + /// + [Parameter] + public string? LayoutConfig { get; set; } + private DockViewConfig Config { get; } = new(); private DockContent Content { get; } = new(); @@ -150,6 +156,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender) EnableLocalStorage = EnableLocalStorage, IsLock = IsLock, Contents = Config.Contents, + LayoutConfig = LayoutConfig, LocalStorageKeyPrefix = $"{LocalStoragePrefix}-{Name}", VisibleChangedCallback = nameof(VisibleChangedCallbackAsync), InitializedCallback = nameof(InitializedCallbackAsync), @@ -203,11 +210,25 @@ public async Task Lock(bool @lock) } } + /// + /// 获取布局配置 + /// + /// + public Task GetLayoutConfig() => InvokeAsync("getLayoutConfig", Id); + /// /// 重置为默认布局 /// /// - public Task Reset() => InvokeVoidAsync("reset", Id, GetOption(), Interop); + public Task Reset(string? layoutConfig = null) + { + var config = GetOption(); + if (layoutConfig != null) + { + config.LayoutConfig = layoutConfig; + } + return InvokeVoidAsync("reset", Id, config); + } /// /// 标签页关闭回调方法 由 JavaScript 调用 diff --git a/src/Extensions/Components/BootstrapBlazor.Dock/Components/DockView/DockView.razor.js b/src/Extensions/Components/BootstrapBlazor.Dock/Components/DockView/DockView.razor.js index 9c938c052bd..1bbc3099590 100644 --- a/src/Extensions/Components/BootstrapBlazor.Dock/Components/DockView/DockView.razor.js +++ b/src/Extensions/Components/BootstrapBlazor.Dock/Components/DockView/DockView.razor.js @@ -11,7 +11,7 @@ export async function init(id, option, invoke) { await addLink("./_content/BootstrapBlazor.Dock/css/goldenlayout-bb.css") const eventsData = new Map() - const dock = { el, eventsData, lock: option.lock } + const dock = { el, eventsData, invoke, lock: option.lock, layoutConfig: option.layoutConfig } Data.set(id, dock) option.invokeVisibleChangedCallback = (title, visible) => { @@ -74,7 +74,10 @@ export function update(id, option) { const dock = Data.get(id) if (dock) { - if (dock.lock !== option.lock) { + if (dock.layoutConfig !== option.layoutConfig) { + reset(id, option) + } + else if (dock.lock !== option.lock) { // 处理 Lock 逻辑 dock.lock = option.lock lockDock(dock) @@ -92,7 +95,17 @@ export function lock(id, lock) { lockDock(dock) } -export function reset(id, option, invoke) { +export function getLayoutConfig(id) { + let config = ""; + const dock = Data.get(id) + if (dock) { + const layout = dock.layout + config = JSON.stringify(layout.saveLayout()) + } + return config; +} + +export function reset(id, option) { const dock = Data.get(id) if (dock) { removeConfig(option); @@ -108,7 +121,7 @@ export function reset(id, option, invoke) { }) dispose(id) - init(id, option, invoke) + init(id, option, dock.invoke) } } @@ -122,6 +135,13 @@ export function dispose(id) { dock.eventsData.clear() dock.layout.destroy() + + if (goldenLayout.bb_docks !== void 0) { + const index = goldenLayout.bb_docks.indexOf(dock); + if (index > 0) { + goldenLayout.bb_docks.splice(index, 1); + } + } } const lockDock = dock => { @@ -291,21 +311,24 @@ const closeItem = (el, component) => { } const getConfig = option => { - let config = null option = { enableLocalStorage: false, + layoutConfig: null, name: 'default', ...option } - if (option.enableLocalStorage) { - const localConfig = localStorage.getItem(getLocalStorageKey(option)); - if (localConfig) { - // 当tab全部关闭时,没有root节点 - const configItem = JSON.parse(localConfig) - if (configItem.root) { - config = configItem - resetComponentId(config, option) - } + + let config = null + let layoutConfig = option.layoutConfig; + if (layoutConfig === null && option.enableLocalStorage) { + layoutConfig = localStorage.getItem(getLocalStorageKey(option)); + } + if (layoutConfig) { + // 当tab全部关闭时,没有root节点 + const configItem = JSON.parse(layoutConfig) + if (configItem.root) { + config = configItem + resetComponentId(config, option) } } @@ -474,7 +497,7 @@ const hackGoldenLayout = dock => { this._closeButton.onClick = function (ev) { // find own dock - const dock = goldenLayout.bb_docks.find(i => i.layout === this.layoutManager); + const dock = goldenLayout.bb_docks.find(i => i.layout === this._header.layoutManager); const eventsData = dock.eventsData const tabs = this._header.tabs.map(tab => { diff --git a/src/Extensions/Components/BootstrapBlazor.Dock/Components/DockView/DockViewConfig.cs b/src/Extensions/Components/BootstrapBlazor.Dock/Components/DockView/DockViewConfig.cs index 9e4e99db65c..2619df29090 100644 --- a/src/Extensions/Components/BootstrapBlazor.Dock/Components/DockView/DockViewConfig.cs +++ b/src/Extensions/Components/BootstrapBlazor.Dock/Components/DockView/DockViewConfig.cs @@ -67,4 +67,9 @@ class DockViewConfig [JsonPropertyName("content")] [JsonConverter(typeof(DockContentRootConverter))] public List Contents { get; set; } = new(); + + /// + /// 获得/设置 布局配置 默认 null 未设置 + /// + public string? LayoutConfig { get; set; } }