diff --git a/components/tabs/Reuse/ReusePages.razor b/components/tabs/Reuse/ReusePages.razor
index 73485572..aa958933 100644
--- a/components/tabs/Reuse/ReusePages.razor
+++ b/components/tabs/Reuse/ReusePages.razor
@@ -12,7 +12,7 @@
{
@foreach (var item in ReuseTabsService.Pages)
{
-
+
@item.Body
}
diff --git a/components/tabs/Reuse/ReuseTabs.razor b/components/tabs/Reuse/ReuseTabs.razor
index 655a3d8b..dfbead45 100644
--- a/components/tabs/Reuse/ReuseTabs.razor
+++ b/components/tabs/Reuse/ReuseTabs.razor
@@ -2,12 +2,12 @@
@inherits AntDomComponentBase
@inject NavigationManager _navigationManager
-
+
@if (ReuseTabsService.Pages?.Any() == true)
{
@foreach (var item in ReuseTabsService.Pages)
{
-
+
@item.Title
diff --git a/components/tabs/Reuse/ReuseTabs.razor.cs b/components/tabs/Reuse/ReuseTabs.razor.cs
index 88329c97..26d1b6ed 100644
--- a/components/tabs/Reuse/ReuseTabs.razor.cs
+++ b/components/tabs/Reuse/ReuseTabs.razor.cs
@@ -109,7 +109,7 @@ namespace AntDesign
if (action != "remove")
return false;
- return ReuseTabsService.ClosePage(key);
+ return ReuseTabsService.ClosePageByKey(key);
}
private void OnLocationChanged(object o, LocationChangedEventArgs _)
diff --git a/components/tabs/Reuse/ReuseTabsPageAttribute.cs b/components/tabs/Reuse/ReuseTabsPageAttribute.cs
index c1fe9840..2084cb4d 100644
--- a/components/tabs/Reuse/ReuseTabsPageAttribute.cs
+++ b/components/tabs/Reuse/ReuseTabsPageAttribute.cs
@@ -6,20 +6,58 @@ using System;
namespace AntDesign
{
+ ///
+ /// Attribute for ReuseTabsPage, used to set the page title and other properties.
+ ///
public class ReuseTabsPageAttribute : Attribute
{
+ ///
+ /// Specifies the title of the tab.
+ ///
+ /// If you want to set a , you need implement in the page.
+ ///
+ ///
public string Title { get; set; }
+ ///
+ /// Weather the page wont be reused.
+ ///
public bool Ignore { get; set; }
+ ///
+ /// Weather the tab can be closed.
+ ///
public bool Closable { get; set; } = true;
+ ///
+ /// Weather the tab can be pinned and opened at the first time.
+ ///
public bool Pin { get; set; } = false;
+ ///
+ /// The url of the pinned page. Because when the tab is clicked, it need to navigate to the page through the url.
+ ///
public string PinUrl { get; set; }
+ ///
+ /// Weather the page is keeping alive.
+ ///
+ /// If true, the page will be kept in memory, otherwise, the page will be destroyed when it is not active.
+ ///
+ ///
public bool KeepAlive { get; set; } = true;
+ ///
+ /// The order of the page, the smaller the order, the earlier the page will be displayed.
+ ///
public int Order { get; set; } = 999;
+
+ ///
+ /// Weather the page is a singleton.
+ ///
+ /// If true, the page will be reused although the parameters is different, otherwise, another tab will be created.
+ ///
+ ///
+ public bool Singleton { get; set; }
}
}
diff --git a/components/tabs/Reuse/ReuseTabsPageItem.cs b/components/tabs/Reuse/ReuseTabsPageItem.cs
index 1a7ce3f3..4a54c0ec 100644
--- a/components/tabs/Reuse/ReuseTabsPageItem.cs
+++ b/components/tabs/Reuse/ReuseTabsPageItem.cs
@@ -26,5 +26,14 @@ namespace AntDesign
public bool KeepAlive { get; set; } = true;
public int Order { get; set; } = 9999;
+
+ public string TypeName { get; set; }
+
+ public string Key { get; set; }
+
+ ///
+ /// Weather the page is a singleton. If true, the page will be reused although the url is different, otherwise, another tab will be created.
+ ///
+ public bool Singleton { get; set; }
}
}
diff --git a/components/tabs/Reuse/ReuseTabsService.cs b/components/tabs/Reuse/ReuseTabsService.cs
index 8178b3d8..22aad2eb 100644
--- a/components/tabs/Reuse/ReuseTabsService.cs
+++ b/components/tabs/Reuse/ReuseTabsService.cs
@@ -17,8 +17,7 @@ namespace AntDesign
{
private readonly NavigationManager _navmgr;
private readonly MenuService _menusService;
- private readonly Dictionary _pageMap = [];
- private IReadOnlyCollection _pages;
+ private ICollection _pages = new List();
internal event Action OnStateHasChanged;
@@ -38,10 +37,27 @@ namespace AntDesign
}
}
+ private string _activeKey;
+
+ internal string ActiveKey
+ {
+ get => _activeKey;
+ set
+ {
+ _activeKey = value;
+ var pageItem = _pages.FirstOrDefault(r => r.Key == value);
+ if (pageItem != null && (pageItem.Url != CurrentUrl || pageItem.Body == null))
+ {
+ CurrentUrl = pageItem.Url;
+ }
+
+ }
+ }
+
///
/// The page information list of the currently opened page, which can be used for caching and recovery
///
- public IReadOnlyCollection Pages => _pages;
+ public IReadOnlyCollection Pages => [.. _pages];
public ReuseTabsService(NavigationManager navmgr, MenuService menusService)
{
@@ -60,11 +76,9 @@ namespace AntDesign
/// The title show on the tab
public void CreateTab(string pageUrl, RenderFragment titleTemplate = null)
{
- if (_pageMap.ContainsKey(pageUrl))
- {
+ if (_pages.Any(x => x.Url == pageUrl))
return;
- }
- AddPage(pageUrl, new ReuseTabsPageItem() { Url = pageUrl, Title = titleTemplate ?? pageUrl.ToRenderFragment(), CreatedAt = DateTime.MinValue });
+ AddPage(new ReuseTabsPageItem() { Url = pageUrl, Title = titleTemplate ?? pageUrl.ToRenderFragment(), CreatedAt = DateTime.MinValue });
OnStateHasChanged?.Invoke();
}
@@ -75,12 +89,7 @@ namespace AntDesign
/// The title show on the tab
public void CreateTab(string pageUrl, string title)
{
- if (_pageMap.ContainsKey(pageUrl))
- {
- return;
- }
- AddPage(pageUrl, new ReuseTabsPageItem() { Url = pageUrl, Title = title.ToRenderFragment(), CreatedAt = DateTime.MinValue });
- OnStateHasChanged?.Invoke();
+ CreateTab(pageUrl, title.ToRenderFragment());
}
//public void Pin(string key)
@@ -95,30 +104,48 @@ namespace AntDesign
//}
///
- /// Close the page corresponding to the specified key
+ /// Close the page corresponding to the specified url
///
- /// The specified page's key
- public bool ClosePage(string key)
+ /// The specified page's url
+ public bool ClosePage(string url)
{
- var reuseTabsPageItem = _pages?.FirstOrDefault(w => w.Url == key);
+ var reuseTabsPageItem = _pages?.FirstOrDefault(w => w.Url == url);
if (reuseTabsPageItem?.Closable != true)
{
return false;
}
- RemovePageBase(key);
+ RemovePageBase(reuseTabsPageItem.Url);
StateHasChanged();
return true;
}
///
- /// Close all pages except the page with the specified key
+ /// Close the page corresponding to the specified key
///
/// The specified page's key
- public void CloseOther(string key)
+ internal bool ClosePageByKey(string key)
{
- foreach (var item in _pages?.Where(x => x.Closable && x.Url != key && !x.Pin))
+ var reuseTabsPageItem = _pages?.FirstOrDefault(w => w.Key == key);
+ if (reuseTabsPageItem?.Closable != true)
+ {
+ return false;
+ }
+
+ RemovePageBase(reuseTabsPageItem.Url);
+ StateHasChanged();
+
+ return true;
+ }
+
+ ///
+ /// Close all pages except the page with the specified url
+ ///
+ /// The specified page's url
+ public void CloseOther(string url)
+ {
+ foreach (var item in _pages?.Where(x => x.Closable && x.Url != url && !x.Pin))
{
RemovePageBase(item.Url);
}
@@ -154,16 +181,20 @@ namespace AntDesign
}
///
- /// Reload the page corresponding to the specified key
+ /// Reload the page corresponding to the specified url
///
- ///
- public void ReloadPage(string key)
+ ///
+ public void ReloadPage(string url)
{
- key ??= CurrentUrl;
- _pageMap[key].Body = null;
- if (CurrentUrl == key)
+ url ??= CurrentUrl;
+ var reuseTabsPageItem = _pages?.FirstOrDefault(w => w.Url == url);
+ if (reuseTabsPageItem != null)
{
- CurrentUrl = key; // auto reload current page, and other page would be load by tab navigation.
+ // Reset content
+ reuseTabsPageItem.Body = null;
+ // auto reload current page, and other page would be load by tab navigation
+ if (ActiveKey == reuseTabsPageItem.Key)
+ ActiveKey = reuseTabsPageItem.Key;
}
StateHasChanged();
}
@@ -198,22 +229,28 @@ namespace AntDesign
if (!reuse)
{
- _pageMap.Clear();
+ _pages.Clear();
}
+ var reuseTabsPageItem = _pages?.FirstOrDefault(w => w.Url == CurrentUrl || (w.Singleton && w.TypeName == routeData.PageType?.FullName));
- var reuseTabsPageItem = _pageMap.ContainsKey(CurrentUrl) ? _pageMap[CurrentUrl] : null;
if (reuseTabsPageItem == null)
{
reuseTabsPageItem = new ReuseTabsPageItem
{
Url = CurrentUrl,
CreatedAt = DateTime.Now,
+ TypeName = routeData.PageType.FullName
};
- AddPage(CurrentUrl, reuseTabsPageItem);
+ AddPage(reuseTabsPageItem);
+ }
+ else
+ {
+ reuseTabsPageItem.Url = CurrentUrl;
}
- reuseTabsPageItem.Body ??= CreateBody(routeData, reuseTabsPageItem);
+ reuseTabsPageItem.Body = CreateBody(routeData, reuseTabsPageItem);
+ ActiveKey = reuseTabsPageItem.Key;
OnStateHasChanged?.Invoke();
}
@@ -262,6 +299,7 @@ namespace AntDesign
pageItem.Pin = attr.Pin;
pageItem.KeepAlive = attr.KeepAlive;
pageItem.Order = attr.Order;
+ pageItem.Singleton = attr.Singleton;
}
pageItem.Title ??= b =>
@@ -300,8 +338,8 @@ namespace AntDesign
this.AddReuseTabsPageItem(pageType);
}
}
-
- CurrentUrl ??= _pages.FirstOrDefault()?.Url;
+ if (CurrentUrl == null)
+ ActiveKey = _pages.FirstOrDefault()?.Key;
}
private void AddReuseTabsPageItem(Type pageType)
@@ -310,17 +348,21 @@ namespace AntDesign
var reuseTabsAttribute = pageType.GetCustomAttribute();
var url = reuseTabsAttribute?.PinUrl ?? routeAttribute.Template;
- var reuseTabsPageItem = new ReuseTabsPageItem();
+ var reuseTabsPageItem = new ReuseTabsPageItem
+ {
+ Url = url,
+ CreatedAt = DateTime.MinValue,
+ TypeName = pageType.FullName
+ };
GetPageInfo(reuseTabsPageItem, pageType, url, Activator.CreateInstance(pageType));
- reuseTabsPageItem.CreatedAt = DateTime.MinValue;
- reuseTabsPageItem.Url = url;
- AddPage(url, reuseTabsPageItem);
+ AddPage(reuseTabsPageItem);
}
- private void AddPage(string key, ReuseTabsPageItem pageItem)
+ private void AddPage(ReuseTabsPageItem pageItem)
{
- _pageMap.TryAdd(key, pageItem);
- _pages = _pageMap.Values.Where(x => !x.Ignore)
+ pageItem.Key = pageItem.GetHashCode().ToString();
+ _pages.Add(pageItem);
+ _pages = _pages.Where(x => !x.Ignore)
.OrderBy(x => x.CreatedAt)
.ThenByDescending(x => x.Pin ? 1 : 0)
.ThenBy(x => x.Order)
@@ -329,13 +371,12 @@ namespace AntDesign
private void RemovePageBase(string key)
{
- _pageMap[key].Body = null;
- _pageMap.Remove(key);
- _pages = _pageMap.Values.Where(x => !x.Ignore)
- .OrderBy(x => x.CreatedAt)
- .ThenByDescending(x => x.Pin ? 1 : 0)
- .ThenBy(x => x.Order)
- .ToList();
+ var pageItem = _pages.Where(x => x.Url == key).FirstOrDefault();
+ if (pageItem != null)
+ {
+ pageItem.Body = null;
+ _pages.Remove(pageItem);
+ }
}
public void Dispose()
diff --git a/site/AntDesign.Docs/Demos/Experimental/ReuseTabs/demo/Singleton.razor b/site/AntDesign.Docs/Demos/Experimental/ReuseTabs/demo/Singleton.razor
new file mode 100644
index 00000000..f02379c5
--- /dev/null
+++ b/site/AntDesign.Docs/Demos/Experimental/ReuseTabs/demo/Singleton.razor
@@ -0,0 +1,54 @@
+@layout AntDesign.Docs.Demos.Experimental.ReuseTabs.demo.Layout_
+@inject NavigationManager navigationManager
+@implements IDisposable
+@implements IReuseTabsPage
+
+@page "/reuse/Singleton"
+@page "/reuse/Singleton/{Id:int}"
+
+@attribute [ReuseTabsPage(Singleton = true)]
+
+Singleton Page
+
+Singleton Page
+
+@text
+
+@dynamicText
+
+Navigate to next id: @NextId
+
+
+@code{
+
+ [Parameter] public int Id { get; set; }
+
+ [SupplyParameterFromQuery] public string Query { get; set; }
+
+ private int NextId => Id + 1;
+
+ string text = "";
+ string dynamicText = "";
+
+ protected override void OnInitialized()
+ {
+ text = "OnInitialized only be called once, Id = " + Id;
+ navigationManager.LocationChanged += OnLationChanged;
+ }
+
+ private void OnLationChanged(object sender, EventArgs e)
+ {
+ dynamicText = $"Current Id = {Id}, Query = {Query}";
+ InvokeAsync(StateHasChanged);
+ }
+
+ public RenderFragment GetPageTitle()
+ {
+ return @ Singleton Page with Id @Id ;
+ }
+
+ public void Dispose()
+ {
+ navigationManager.LocationChanged -= OnLationChanged;
+ }
+}
\ No newline at end of file
diff --git a/site/AntDesign.Docs/Demos/Experimental/ReuseTabs/demo/singleton.md b/site/AntDesign.Docs/Demos/Experimental/ReuseTabs/demo/singleton.md
new file mode 100644
index 00000000..f140b4b5
--- /dev/null
+++ b/site/AntDesign.Docs/Demos/Experimental/ReuseTabs/demo/singleton.md
@@ -0,0 +1,21 @@
+---
+order: 4
+iframe: 360
+link: /reuse/singleton
+title:
+ zh-CN: 单例页面
+ en-US: Singleton Page
+---
+
+## zh-CN
+
+如果页面是单例的,它会被不同参数复用,否则,当参数不同时会打开另一个页面。默认不是单例。
+
+单例页面不会重新实例化,也不再执行初始化方法,因此需要监听页面导航事件来更新界面。
+
+
+## en-US
+
+If the page is Singleton, it will be reused although the parameters is different, otherwise, another tab will be created.
+
+The singleton page is not re-instantiated, nor is the `OnInitialized{Async}` method performed, so it needs to listen for location navigation events to update the UI.
\ No newline at end of file
diff --git a/site/AntDesign.Docs/Demos/Experimental/ReuseTabs/doc/index.en-US.md b/site/AntDesign.Docs/Demos/Experimental/ReuseTabs/doc/index.en-US.md
index b197fc79..f4942b64 100644
--- a/site/AntDesign.Docs/Demos/Experimental/ReuseTabs/doc/index.en-US.md
+++ b/site/AntDesign.Docs/Demos/Experimental/ReuseTabs/doc/index.en-US.md
@@ -70,6 +70,9 @@ Used to implement in-application page tabs and page caching.
| PinUrl | Specify the Url of the loaded page, and then open the page with a route parameter, such as `/order/1` | string | - |
| KeepAlive| Whether to cache the page state | bool | true |
| Order | The sequence number | int | 999 |
+| TypeName | The page's classsname | string | - |
+| Key | The page's key | string | - |
+| NewPageForParams | Whether to create a new page for route with different params | bool | false |
### IReuseTabsPage interface
@@ -86,9 +89,9 @@ Used to control ReuseTabs in pages
| --- | --- |
| Pages | The information list of the currently opened pages can be used for caching and recovery |
| CreateTab(string pageUrl, RenderFragment? title = null) | Create a tab, but do not navigate to the page, and initialize the page when you navigate to the page. |
-| ClosePage(string key) | Close the page with the specified key. |
-| CloseOther(string key) | Close all pages except those that specify key, `Cloasable=false`, or `Pin=true`. |
+| ClosePage(string url) | Close the page with the specified url. |
+| CloseOther(string url) | Close all pages except those that specify url, `Cloasable=false`, or `Pin=true`. |
| CloseAll() | Close all pages except those that `Cloasable=false` or `Pin=true`.|
| CloseCurrent() | Close current page. |
| Update() | Update the state of current tab. When the variable referenced in `GetPageTitle()` changes, `Update()` needs to be called to update the tab display. |
-| ReloadPage(key) | Reload the page for the specified label, allowing the page components to be reinitialized without refreshing the browser. If no key is passed, reload the current page . |
+| ReloadPage(url) | Reload the page for the specified label, allowing the page components to be reinitialized without refreshing the browser. If no url is passed, reload the current page . |
diff --git a/site/AntDesign.Docs/Demos/Experimental/ReuseTabs/doc/index.zh-CN.md b/site/AntDesign.Docs/Demos/Experimental/ReuseTabs/doc/index.zh-CN.md
index 780c3ead..aa777cba 100644
--- a/site/AntDesign.Docs/Demos/Experimental/ReuseTabs/doc/index.zh-CN.md
+++ b/site/AntDesign.Docs/Demos/Experimental/ReuseTabs/doc/index.zh-CN.md
@@ -71,6 +71,9 @@ cover: https://gw.alipayobjects.com/zos/antfincdn/lkI2hNEDr2V/Tabs.svg
| PinUrl | 固定加载页面的 Url,在打开路由有参数的页面时需要,比如 `/order/1` | string | - |
| KeepAlive| 是否缓存页面状态 | bool | true |
| Order | 标签顺序 | int | 999 |
+| TypeName | 当前页面对应的类别 | string | - |
+| Key | 当前标签页的关键字 | string | - |
+| NewPageForParams | 是否针对具有不同参数的路由创建新的页面 | bool | false |
### IReuseTabsPage 接口
@@ -87,9 +90,9 @@ cover: https://gw.alipayobjects.com/zos/antfincdn/lkI2hNEDr2V/Tabs.svg
| --- | --- |
| Pages | 当前打开过的页面信息列表,可自行用于缓存和恢复 |
| CreateTab(string pageUrl, RenderFragment? title = null) | 创建一个标签,但不导航到该页面,等导航到该页面时才初始化这个页面。|
-| ClosePage(string key) | 关闭指定key的页面,key 就是 url。 |
-| CloseOther(string key) | 关闭除了指定key的页面,或者设置了 `Cloasable=false` 或 `Pin=true` 的页面。 |
+| ClosePage(string url) | 关闭指定url的页面。 |
+| CloseOther(string url) | 关闭除了指定url的页面,或者设置了 `Cloasable=false` 或 `Pin=true` 的页面。 |
| CloseAll() | 关闭除了设置了 `Cloasable=false` 或者 `Pin=true` 的页面。 |
| CloseCurrent() | 关闭当前页面。 |
| Update() | 更新 Tab 状态。当 `GetPageTitle()` 中引用的变量发生变化时,需要调用 `Update()` 来更新 tab 的显示。 |
-| ReloadPage(key) | 重新加载指定标签的页面,让页面组件重新初始化,且无需刷新浏览器。不传key时重新加载当前页面。 |
\ No newline at end of file
+| ReloadPage(url) | 重新加载指定标签的页面,让页面组件重新初始化,且无需刷新浏览器。不传url时重新加载当前页面。 |
\ No newline at end of file