mirror of
https://github.com/dotnetcore/BootstrapBlazor.git
synced 2025-12-20 10:26:41 +08:00
feat(Dock): add GetLayoutConfig instance method (#2221)
* feat: 增加 Reset 方法 * feat: 增加 Reset 方法 * refactor: 增加 Readonly 关键字 * LayoutConfig and SaveLayoutCallback * Update BootstrapBlazor.Shared.csproj * 分开配置 * 更新复位功能 * GetLayout 获取面板的显示布局 * refactor: 增加销毁逻辑 * fix: 修复 layout 查找逻辑 * doc: 更新资源文件 * refactor: 更新脚本 * doc: 更新注释文档 * refactor: 移除 SaveLayoutCallback 回调方法 * refactor: 更改方法为 getLayoutConfig * revert: 移除 Update 方法参数 * revert: 撤销布局逻辑 * feat: 移除 LayoutConfig * refactor: 更改标题样式 * refactor: 精简代码 * feat: 增加 LayoutConfig 参数 * refactor: 支持服务器端配置 * refactor: 更新示例代码 * doc: 更新示例 --------- Co-authored-by: Argo-AscioTech <argo@live.ca>
This commit is contained in:
@@ -743,6 +743,11 @@ internal static class MenusLocalizerExtensions
|
||||
{
|
||||
Text = Localizer["DockViewLock"],
|
||||
Url = "dock-view/lock"
|
||||
},
|
||||
new()
|
||||
{
|
||||
Text = Localizer["DockViewLayout"],
|
||||
Url = "dock-view/layout"
|
||||
}
|
||||
};
|
||||
AddBadge(item, count: 1);
|
||||
|
||||
@@ -4503,6 +4503,7 @@
|
||||
"DockViewComplex": "Complex",
|
||||
"DockViewVisible": "Visible",
|
||||
"DockViewLock": "Lock",
|
||||
"DockViewLayout": "Custom",
|
||||
"OtherComponents": "Others",
|
||||
"MouseFollowerIntro": "MouseFollower",
|
||||
"Live2DDisplayIntro": "Live2D Widget",
|
||||
|
||||
@@ -4503,6 +4503,7 @@
|
||||
"DockViewComplex": "组合布局",
|
||||
"DockViewVisible": "可见性切换",
|
||||
"DockViewLock": "布局锁定",
|
||||
"DockViewLayout": "布局自定义",
|
||||
"OtherComponents": "其他组件",
|
||||
"MouseFollowerIntro": "鼠标跟随",
|
||||
"Live2DDisplayIntro": "Live2D 插件",
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
@page "/dock-view/layout"
|
||||
@inherits BaseDockView
|
||||
|
||||
<h4>自定义布局</h4>
|
||||
<p>通过设置 <code>DockView</code> 的属性 <code>LayoutConfig</code> 初始化控制面板的显示布局, 方法 <code>GetLayoutConfig</code> 获取面板的显示布局</p>
|
||||
|
||||
<GroupBox Title="布局切换">
|
||||
<Button OnClick="OnToggleLayout1" Text="布局1"></Button>
|
||||
<Button OnClick="OnToggleLayout2" Text="布局2"></Button>
|
||||
<Button OnClick="OnToggleLayout3" Text="布局3"></Button>
|
||||
<Button OnClick="GetLayout" Text="获取布局"></Button>
|
||||
<Button OnClick="Reset" Text="复位"></Button>
|
||||
<code class="config">@LayoutConfigSave</code>
|
||||
</GroupBox>
|
||||
|
||||
<div class="dock-toggle-demo">
|
||||
<DockView @ref="DockView" Name="DockViewLayout" EnableLocalStorage="true" LayoutConfig="@LayoutConfig">
|
||||
<DockContent Type="DockContentType.Column">
|
||||
<DockComponent Title="标签一">
|
||||
<Table TItem="DynamicObject" DynamicContext="DataTableDynamicContext"
|
||||
IsStriped="true" IsBordered="true" IsExcel="true" ShowRefresh="false"
|
||||
ShowDefaultButtons="false">
|
||||
<DetailRowTemplate>
|
||||
<div class="p-2 w-100">
|
||||
<Table TItem="DynamicObject" DynamicContext="GetDetailDataTableDynamicContext(context)" IsStriped="true" IsBordered="true" IsExcel="true">
|
||||
</Table>
|
||||
</div>
|
||||
</DetailRowTemplate>
|
||||
</Table>
|
||||
</DockComponent>
|
||||
<DockComponent Title="标签二">
|
||||
<Table TItem="Foo" @bind-Items="Items"
|
||||
IsStriped="true" IsBordered="true" IsMultipleSelect="true"
|
||||
ShowToolbar="true" ShowExtendButtons="true" ShowSkeleton="true"
|
||||
OnAddAsync="@OnAddAsync">
|
||||
<TableColumns>
|
||||
<TableColumn @bind-Field="@context.DateTime" Width="180" />
|
||||
<TableColumn @bind-Field="@context.Name" />
|
||||
<TableColumn @bind-Field="@context.Address" Width="180" TextEllipsis="true" ShowTips="true" />
|
||||
<TableColumn @bind-Field="@context.Education" />
|
||||
<TableColumn @bind-Field="@context.Count" />
|
||||
<TableColumn @bind-Field="@context.Complete" />
|
||||
</TableColumns>
|
||||
</Table>
|
||||
</DockComponent>
|
||||
<DockComponent Title="标签三">
|
||||
<FetchData></FetchData>
|
||||
</DockComponent>
|
||||
</DockContent>
|
||||
</DockView>
|
||||
</div>
|
||||
|
||||
@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}
|
||||
"""";
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
@page "/dock-view/lock"
|
||||
@inherits BaseDockView
|
||||
|
||||
<h4 class="mt-3">锁定面板</h4>
|
||||
<h4>锁定面板</h4>
|
||||
<p>通过设置 <code>DockView</code> 的属性 <code>IsLock</code>,控制所有面板是否能拖动</p>
|
||||
<p>通过设置 <code>DockComponent</code> 的属性 <code>IsLock</code>,控制某个面板是否能拖动</p>
|
||||
|
||||
<Button OnClick="OnToggleLock" Text="@LockText"></Button>
|
||||
|
||||
<div class="dock-lock-demo">
|
||||
<DockView Name="DockViewLock" EnableLocalStorage="false" OnLockChangedCallbackAsync="OnLockChangedCallbackAsync" IsLock="@IsLock">
|
||||
<DockView Name="DockViewLock" EnableLocalStorage="true" OnLockChangedCallbackAsync="OnLockChangedCallbackAsync" IsLock="@IsLock">
|
||||
<DockContent Type="DockContentType.Row">
|
||||
<DockComponent Title="标签一" IsLock="true">
|
||||
<DockComponent Title="标签一">
|
||||
<Table TItem="DynamicObject" DynamicContext="DataTableDynamicContext"
|
||||
IsStriped="true" IsBordered="true" IsExcel="true" ShowRefresh="true"
|
||||
ShowDefaultButtons="false" IsFixedHeader="false">
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
@page "/dock-view/visible"
|
||||
@inherits BaseDockView
|
||||
|
||||
<h4 class="mt-3">可隐藏的面板</h4>
|
||||
<h4>可隐藏的面板</h4>
|
||||
<p>通过设置 <code>DockComponent</code> 的属性 <code>Visible</code> 控制面板的显示和隐藏</p>
|
||||
|
||||
<Button OnClick="OnToggleVisible" Text="切换标签一"></Button>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
||||
|
||||
<PropertyGroup>
|
||||
<Version>7.0.11</Version>
|
||||
<Version>7.0.12</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
||||
@@ -88,6 +88,12 @@ public partial class DockView
|
||||
[Parameter]
|
||||
public string? LocalStoragePrefix { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获得/设置 布局配置
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public string? LayoutConfig { get; set; }
|
||||
|
||||
private DockViewConfig Config { get; } = new();
|
||||
|
||||
private DockContent Content { get; } = new();
|
||||
@@ -150,6 +156,7 @@ public partial class DockView
|
||||
EnableLocalStorage = EnableLocalStorage,
|
||||
IsLock = IsLock,
|
||||
Contents = Config.Contents,
|
||||
LayoutConfig = LayoutConfig,
|
||||
LocalStorageKeyPrefix = $"{LocalStoragePrefix}-{Name}",
|
||||
VisibleChangedCallback = nameof(VisibleChangedCallbackAsync),
|
||||
InitializedCallback = nameof(InitializedCallbackAsync),
|
||||
@@ -203,11 +210,25 @@ public partial class DockView
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取布局配置
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Task<string?> GetLayoutConfig() => InvokeAsync<string>("getLayoutConfig", Id);
|
||||
|
||||
/// <summary>
|
||||
/// 重置为默认布局
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 标签页关闭回调方法 由 JavaScript 调用
|
||||
|
||||
@@ -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 => {
|
||||
|
||||
@@ -67,4 +67,9 @@ class DockViewConfig
|
||||
[JsonPropertyName("content")]
|
||||
[JsonConverter(typeof(DockContentRootConverter))]
|
||||
public List<DockContent> Contents { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// 获得/设置 布局配置 默认 null 未设置
|
||||
/// </summary>
|
||||
public string? LayoutConfig { get; set; }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user