diff --git a/.changeset/thin-items-promise.md b/.changeset/thin-items-promise.md
new file mode 100644
index 00000000..f71a5631
--- /dev/null
+++ b/.changeset/thin-items-promise.md
@@ -0,0 +1,5 @@
+---
+'@alita/plugins': patch
+---
+
+feat: keepalive support custom tabs
diff --git a/examples/tabs/.umirc.ts b/examples/tabs/.umirc.ts
index 22621efc..a257fac5 100644
--- a/examples/tabs/.umirc.ts
+++ b/examples/tabs/.umirc.ts
@@ -8,5 +8,7 @@ export default {
mfsu: {},
antd: {},
hash: false,
- tabsLayout: {},
+ tabsLayout: {
+ hasCustomTabs: true,
+ },
};
diff --git a/examples/tabs/src/app.tsx b/examples/tabs/src/app.tsx
index bb125c00..91a4fc9e 100644
--- a/examples/tabs/src/app.tsx
+++ b/examples/tabs/src/app.tsx
@@ -1,3 +1,8 @@
+import { message, Tabs } from 'antd';
+import React from 'react';
+
+const { TabPane } = Tabs;
+
export const request = {
prefix: '/api',
method: 'get',
@@ -27,3 +32,59 @@ export const tabsLayout = {
// '/foo':'其他'
// }};
// }
+
+export const getCustomTabs = () => {
+ return ({
+ isKeep,
+ keepElements,
+ navigate,
+ dropByCacheKey,
+ local,
+ activeKey,
+ }: any) => {
+ return (
+
+ {
+ navigate(key);
+ }}
+ activeKey={activeKey}
+ type="editable-card"
+ onEdit={(targetKey: string) => {
+ let newActiveKey = activeKey;
+ let lastIndex = -1;
+ const newPanel = Object.keys(keepElements.current);
+ for (let i = 0; i < newPanel.length; i++) {
+ if (newPanel[i] === targetKey) {
+ lastIndex = i - 1;
+ }
+ }
+ const newPanes = newPanel.filter((pane) => pane !== targetKey);
+ if (newPanes.length && newActiveKey === targetKey) {
+ if (lastIndex >= 0) {
+ newActiveKey = newPanes[lastIndex];
+ } else {
+ newActiveKey = newPanes[0];
+ }
+ }
+ if (lastIndex === -1 && targetKey === location.pathname) {
+ message.info('至少要保留一个窗口');
+ } else {
+ dropByCacheKey(targetKey);
+ if (newActiveKey !== location.pathname) {
+ navigate(newActiveKey);
+ }
+ }
+ }}
+ >
+ {Object.entries(keepElements.current).map(
+ ([pathname, element]: any) => (
+
+ ),
+ )}
+
+
+ );
+ };
+};
diff --git a/examples/tabs/src/layouts/index.tsx b/examples/tabs/src/layouts/index.tsx
index 26deef8f..c8e54ab7 100644
--- a/examples/tabs/src/layouts/index.tsx
+++ b/examples/tabs/src/layouts/index.tsx
@@ -6,7 +6,7 @@ import {
import { useKeepOutlets, useLocation, useNavigate } from 'alita';
import type { MenuProps } from 'antd';
import { Layout, Menu } from 'antd';
-import React, { useState, useEffect } from 'react';
+import React, { useEffect, useState } from 'react';
const { Header, Content, Footer, Sider } = Layout;
@@ -38,7 +38,7 @@ const App: React.FC = () => {
const location = useLocation();
const [collapsed, setCollapsed] = useState(false);
- const [activeItem ,setActiveItem] = useState(location.pathname);
+ const [activeItem, setActiveItem] = useState(location.pathname);
// 监听路由变化,激活路由对应的菜单
useEffect(() => {
@@ -72,6 +72,7 @@ const App: React.FC = () => {
}}
theme="dark"
defaultSelectedKeys={[activeItem]}
+ selectedKeys={[activeItem]}
mode="inline"
items={items}
/>
diff --git a/packages/plugins/src/keepalive.ts b/packages/plugins/src/keepalive.ts
index d308774e..054b717a 100644
--- a/packages/plugins/src/keepalive.ts
+++ b/packages/plugins/src/keepalive.ts
@@ -44,6 +44,7 @@ export default (api: AlitaApi) => {
noPluginDir: true,
content: Mustache.render(contextTpl, {
hasTabsLayout: !!tabsLayout,
+ hasCustomTabs: !!tabsLayout?.hasCustomTabs,
}),
});
const runtimeTpl = readFileSync(
diff --git a/packages/plugins/src/tabs-layout.ts b/packages/plugins/src/tabs-layout.ts
index a60f1e8a..c27f2960 100644
--- a/packages/plugins/src/tabs-layout.ts
+++ b/packages/plugins/src/tabs-layout.ts
@@ -11,12 +11,17 @@ export default (api: AlitaApi) => {
key: 'tabsLayout',
config: {
schema(Joi) {
- return Joi.alternatives(Joi.boolean(), Joi.object());
+ return Joi.alternatives(
+ Joi.boolean(),
+ Joi.object({
+ hasCustomTabs: Joi.boolean(),
+ }),
+ );
},
onChange: api.ConfigChangeType.regenerateTmpFiles,
},
enableBy: api.EnableBy.config,
});
// 注册runtime配置
- api.addRuntimePluginKey(() => ['tabsLayout']);
+ api.addRuntimePluginKey(() => ['tabsLayout', 'getCustomTabs']);
};
diff --git a/packages/plugins/templates/keepalive/context.tpl b/packages/plugins/templates/keepalive/context.tpl
index 9e32032f..cbef6b41 100644
--- a/packages/plugins/templates/keepalive/context.tpl
+++ b/packages/plugins/templates/keepalive/context.tpl
@@ -1,14 +1,25 @@
import React, { useState } from 'react';
import { useOutlet, useLocation, matchPath, useNavigate } from 'react-router-dom'
-{{#hasTabsLayout}}
+{{^hasCustomTabs}}
+{{#hasTabsLayout}}
import { Tabs, message } from 'antd';
+{{/hasTabsLayout}}
+{{/hasCustomTabs}}
+{{#hasTabsLayout}}
import { getPluginManager } from '../core/plugin';
{{/hasTabsLayout}}
+{{#hasCustomTabs}}
+import { getCustomTabs } from '@/app';
+{{/hasCustomTabs}}
+
+
export const KeepAliveContext = React.createContext({});
+{{^hasCustomTabs}}
{{#hasTabsLayout}}
const { TabPane } = Tabs;
{{/hasTabsLayout}}
+{{/hasCustomTabs}}
const isKeepPath = (aliveList: any[], path: string) => {
let isKeep = false;
@@ -34,14 +45,25 @@ export function useKeepOutlets() {
const runtime = getPluginManager().applyPlugins({ key: 'tabsLayout',type: 'modify', initialValue: {} });
const { local } = runtime;
{{/hasTabsLayout}}
+
const { cacheKeyMap, keepElements, keepalive, dropByCacheKey } = React.useContext(KeepAliveContext);
const isKeep = isKeepPath(keepalive, location.pathname);
if (isKeep) {
keepElements.current[location.pathname] = element;
}
+{{#hasCustomTabs}}
+ const CustomTabs = getCustomTabs();
+ const tabsProps = {
+ isKeep, keepElements, navigate, dropByCacheKey, local, activeKey: location.pathname
+ }
+{{/hasCustomTabs}}
return <>
+{{#hasCustomTabs}}
+
+{{/hasCustomTabs}}
+{{^hasCustomTabs}}
{{#hasTabsLayout}}
-
+
{
navigate(key);
}} activeKey={location.pathname} type="editable-card" onEdit={(targetKey: string,) => {
@@ -61,11 +83,13 @@ export function useKeepOutlets() {
newActiveKey = newPanes[0];
}
}
- if (newActiveKey !== location.pathname) {
- dropByCacheKey(targetKey);
- navigate(newActiveKey);
- } else if (lastIndex === -1 && targetKey === location.pathname) {
+ if (lastIndex === -1 && targetKey === location.pathname) {
message.info('至少要保留一个窗口');
+ } else {
+ dropByCacheKey(targetKey);
+ if (newActiveKey !== location.pathname) {
+ navigate(newActiveKey);
+ }
}
}}>
{Object.entries(keepElements.current).map(([pathname, element]: any) => (
@@ -74,6 +98,7 @@ export function useKeepOutlets() {
{{/hasTabsLayout}}
+{{/hasCustomTabs}}
{
Object.entries(keepElements.current).map(([pathname, children]: any) => (