mirror of
https://github.com/Tencent/tdesign-vue-next-starter.git
synced 2024-12-24 02:19:34 +08:00
3a2d29df6c
* fix: tab routes cache fails * feat: add ignore cache routes demo
268 lines
8.9 KiB
TypeScript
268 lines
8.9 KiB
TypeScript
import { defineComponent, computed, nextTick, onMounted, watch, onBeforeUnmount } from 'vue';
|
|
import { storeToRefs } from 'pinia';
|
|
import { useRoute, useRouter } from 'vue-router';
|
|
import { usePermissionStore, useSettingStore, useTabsRouterStore } from '@/store';
|
|
|
|
import LayoutHeader from './components/Header.vue';
|
|
import LayoutBreadcrumb from './components/Breadcrumb.vue';
|
|
import LayoutFooter from './components/Footer.vue';
|
|
import LayoutSideNav from './components/SideNav';
|
|
import LayoutContent from './components/Content.vue';
|
|
import Setting from './setting.vue';
|
|
|
|
import { prefix } from '@/config/global';
|
|
import { TRouterInfo } from '@/interface';
|
|
|
|
import '@/style/layout.less';
|
|
|
|
const name = `${prefix}-base-layout`;
|
|
|
|
export default defineComponent({
|
|
name,
|
|
setup() {
|
|
const route = useRoute();
|
|
const router = useRouter();
|
|
const permissionStore = usePermissionStore();
|
|
const settingStore = useSettingStore();
|
|
const tabsRouterStore = useTabsRouterStore();
|
|
const { routers: menuRouters } = storeToRefs(permissionStore);
|
|
const setting = storeToRefs(settingStore);
|
|
|
|
const mainLayoutCls = computed(() => [
|
|
{
|
|
't-layout--with-sider': settingStore.showSidebar,
|
|
},
|
|
]);
|
|
|
|
const headerMenu = computed(() => {
|
|
if (settingStore.layout === 'mix') {
|
|
if (settingStore.splitMenu) {
|
|
return menuRouters.value.map((menu) => ({
|
|
...menu,
|
|
children: [],
|
|
}));
|
|
}
|
|
return [];
|
|
}
|
|
return menuRouters.value;
|
|
});
|
|
|
|
const sideMenu = computed(() => {
|
|
const { layout, splitMenu } = settingStore;
|
|
let newMenuRouters = menuRouters.value;
|
|
if (layout === 'mix' && splitMenu) {
|
|
newMenuRouters.forEach((menu) => {
|
|
if (route.path.indexOf(menu.path) === 0) {
|
|
newMenuRouters = menu.children.map((subMenu) => ({ ...subMenu, path: `${menu.path}/${subMenu.path}` }));
|
|
}
|
|
});
|
|
}
|
|
return newMenuRouters;
|
|
});
|
|
|
|
const appendNewRoute = () => {
|
|
const {
|
|
path,
|
|
meta: { title },
|
|
name,
|
|
} = route;
|
|
tabsRouterStore.appendTabRouterList({ path, title: title as string, name, isAlive: true });
|
|
};
|
|
|
|
const getTabRouterListCache = () => {
|
|
tabsRouterStore.initTabRouterList(JSON.parse(localStorage.getItem('tabRouterList')));
|
|
};
|
|
const setTabRouterListCache = () => {
|
|
const { tabRouters } = tabsRouterStore;
|
|
localStorage.setItem('tabRouterList', JSON.stringify(tabRouters));
|
|
};
|
|
|
|
onMounted(() => {
|
|
appendNewRoute();
|
|
});
|
|
|
|
// 如果不需要持久化标签页可以注释掉以下的 onMounted 和 onBeforeUnmount 的内容
|
|
onMounted(() => {
|
|
if (localStorage.getItem('tabRouterList')) getTabRouterListCache();
|
|
window.addEventListener('beforeunload', setTabRouterListCache);
|
|
});
|
|
|
|
onBeforeUnmount(() => {
|
|
window.removeEventListener('beforeunload', setTabRouterListCache);
|
|
});
|
|
|
|
watch(
|
|
() => route.path,
|
|
() => {
|
|
appendNewRoute();
|
|
},
|
|
);
|
|
|
|
const handleRemove = ({ value: path, index }) => {
|
|
const { tabRouters } = tabsRouterStore;
|
|
const nextRouter = tabRouters[index + 1] || tabRouters[index - 1];
|
|
|
|
tabsRouterStore.subtractCurrentTabRouter({ path, routeIdx: index });
|
|
if (path === route.path) {
|
|
router.push(nextRouter.path);
|
|
}
|
|
};
|
|
const handleChangeCurrentTab = (path: string) => {
|
|
router.push(path);
|
|
};
|
|
const handleRefresh = (currentPath: string, routeIdx: number) => {
|
|
tabsRouterStore.toggleTabRouterAlive(routeIdx);
|
|
nextTick(() => {
|
|
tabsRouterStore.toggleTabRouterAlive(routeIdx);
|
|
router.replace({ path: currentPath });
|
|
});
|
|
};
|
|
const handleCloseAhead = (path: string, routeIdx: number) => {
|
|
tabsRouterStore.subtractTabRouterAhead({ path, routeIdx });
|
|
};
|
|
const handleCloseBehind = (path: string, routeIdx: number) => {
|
|
tabsRouterStore.subtractTabRouterBehind({ path, routeIdx });
|
|
};
|
|
const handleCloseOther = (path: string, routeIdx: number) => {
|
|
tabsRouterStore.subtractTabRouterOther({ path, routeIdx });
|
|
};
|
|
|
|
const renderSidebar = () => {
|
|
return (
|
|
settingStore.showSidebar && (
|
|
<LayoutSideNav
|
|
showLogo={settingStore.showSidebarLogo}
|
|
layout={settingStore.layout}
|
|
isFixed={settingStore.isSidebarFixed}
|
|
menu={sideMenu.value}
|
|
theme={settingStore.displayMode}
|
|
isCompact={settingStore.isSidebarCompact}
|
|
/>
|
|
)
|
|
);
|
|
};
|
|
|
|
const renderHeader = () => {
|
|
return (
|
|
settingStore.showHeader && (
|
|
<LayoutHeader
|
|
showLogo={settingStore.showHeaderLogo}
|
|
theme={settingStore.displayMode}
|
|
layout={settingStore.layout}
|
|
isFixed={settingStore.isHeaderFixed}
|
|
menu={headerMenu.value}
|
|
isCompact={settingStore.isSidebarCompact}
|
|
/>
|
|
)
|
|
);
|
|
};
|
|
|
|
const renderFooter = () => {
|
|
return (
|
|
<t-footer class={`${prefix}-footer-layout`}>
|
|
<LayoutFooter />
|
|
</t-footer>
|
|
);
|
|
};
|
|
|
|
const renderContent = () => {
|
|
const { showBreadcrumb, showFooter, isUseTabsRouter } = settingStore;
|
|
const { tabRouters } = tabsRouterStore;
|
|
return (
|
|
// <t-layout class={[`${prefix}-layout`]} key={route.name}> 如果存在多个滚动列表之间切换时,页面不刷新导致的样式问题 请设置key 但会导致多标签tab页的缓存失效
|
|
<t-layout class={[`${prefix}-layout`]}>
|
|
{isUseTabsRouter && (
|
|
<t-tabs
|
|
theme="card"
|
|
class={`${prefix}-layout-tabs-nav`}
|
|
value={route.path}
|
|
onChange={handleChangeCurrentTab}
|
|
style={{ width: '100%', position: 'sticky', top: 0 }}
|
|
onRemove={handleRemove}
|
|
>
|
|
{tabRouters.map((router: TRouterInfo, idx: number) => (
|
|
<t-tab-panel
|
|
value={router.path}
|
|
key={`${router.path}_${idx}`}
|
|
label={
|
|
<t-dropdown
|
|
trigger="context-menu"
|
|
minColumnWidth={128}
|
|
popupProps={{ overlayClassName: 'router-tabs-dropdown' }}
|
|
v-slots={{
|
|
dropdown: () =>
|
|
router.path === route.path ? (
|
|
<t-dropdown-menu>
|
|
<t-dropdown-item onClick={() => handleRefresh(router.path, idx)}>
|
|
<t-icon name="refresh" />
|
|
刷新
|
|
</t-dropdown-item>
|
|
{idx > 1 && (
|
|
<t-dropdown-item onClick={() => handleCloseAhead(router.path, idx)}>
|
|
<t-icon name="arrow-left" />
|
|
关闭左侧
|
|
</t-dropdown-item>
|
|
)}
|
|
{idx < tabRouters.length - 1 && (
|
|
<t-dropdown-item onClick={() => handleCloseBehind(router.path, idx)}>
|
|
<t-icon name="arrow-right" />
|
|
关闭右侧
|
|
</t-dropdown-item>
|
|
)}
|
|
<t-dropdown-item onClick={() => handleCloseOther(router.path, idx)}>
|
|
<t-icon name="close-circle" />
|
|
关闭其它
|
|
</t-dropdown-item>
|
|
</t-dropdown-menu>
|
|
) : null,
|
|
}}
|
|
>
|
|
{!router.isHome ? router.title : <t-icon name="home" />}
|
|
</t-dropdown>
|
|
}
|
|
removable={!router.isHome}
|
|
/>
|
|
))}
|
|
</t-tabs>
|
|
)}
|
|
<t-content class={`${prefix}-content-layout`}>
|
|
{showBreadcrumb && <LayoutBreadcrumb />}
|
|
<LayoutContent />
|
|
</t-content>
|
|
{showFooter && renderFooter()}
|
|
</t-layout>
|
|
);
|
|
};
|
|
|
|
return {
|
|
setting,
|
|
mainLayoutCls,
|
|
renderSidebar,
|
|
renderHeader,
|
|
renderContent,
|
|
};
|
|
},
|
|
render() {
|
|
const { layout } = this.setting;
|
|
const header = this.renderHeader();
|
|
const sidebar = this.renderSidebar();
|
|
const content = this.renderContent();
|
|
return (
|
|
<div>
|
|
{layout === 'side' ? (
|
|
<t-layout class={this.mainLayoutCls} key="side">
|
|
<t-aside>{sidebar}</t-aside>
|
|
<t-layout>{[header, content]}</t-layout>
|
|
</t-layout>
|
|
) : (
|
|
<t-layout key="no-side">
|
|
{header}
|
|
<t-layout class={this.mainLayoutCls}>{[sidebar, content]}</t-layout>
|
|
</t-layout>
|
|
)}
|
|
<Setting />
|
|
</div>
|
|
);
|
|
},
|
|
});
|