diff --git a/src/layouts/components/Header.vue b/src/layouts/components/Header.vue index dd2afd9..97ea956 100644 --- a/src/layouts/components/Header.vue +++ b/src/layouts/components/Header.vue @@ -74,7 +74,7 @@ import { MenuRoute } from '@/types/interface'; import Notice from './Notice.vue'; import Search from './Search.vue'; -import MenuContent from './MenuContent'; +import MenuContent from './MenuContent.vue'; const props = defineProps({ theme: { diff --git a/src/layouts/components/LayoutContent.vue b/src/layouts/components/LayoutContent.vue new file mode 100644 index 0000000..fe730df --- /dev/null +++ b/src/layouts/components/LayoutContent.vue @@ -0,0 +1,113 @@ + + + diff --git a/src/layouts/components/LayoutHeader.vue b/src/layouts/components/LayoutHeader.vue new file mode 100644 index 0000000..6f17d5b --- /dev/null +++ b/src/layouts/components/LayoutHeader.vue @@ -0,0 +1,34 @@ + + + diff --git a/src/layouts/components/LayoutSideNav.vue b/src/layouts/components/LayoutSideNav.vue new file mode 100644 index 0000000..1a6fb67 --- /dev/null +++ b/src/layouts/components/LayoutSideNav.vue @@ -0,0 +1,37 @@ + + + diff --git a/src/layouts/components/MenuContent.tsx b/src/layouts/components/MenuContent.tsx deleted file mode 100755 index 4b35286..0000000 --- a/src/layouts/components/MenuContent.tsx +++ /dev/null @@ -1,98 +0,0 @@ -import { defineComponent, PropType, computed, h } from 'vue'; -import { prefix } from '@/config/global'; -import { MenuRoute } from '@/types/interface'; -import { getActive } from '@/router'; - -const getMenuList = (list: MenuRoute[], basePath?: string): MenuRoute[] => { - if (!list) { - return []; - } - // 如果meta中有orderNo则按照从小到大排序 - list.sort((a, b) => { - return (a.meta?.orderNo || 0) - (b.meta?.orderNo || 0); - }); - return list - .map((item) => { - const path = basePath ? `${basePath}/${item.path}` : item.path; - return { - path, - title: item.meta?.title, - icon: item.meta?.icon || '', - children: getMenuList(item.children, path), - meta: item.meta, - redirect: item.redirect, - }; - }) - .filter((item) => item.meta && item.meta.hidden !== true); -}; - -const renderIcon = (item) => { - if (typeof item.icon === 'string') { - return () => item.icon && ; - } - if (item.icon && typeof item.icon.render === 'function') { - return () => - h(item.icon.render(), { - class: 't-icon', - }); - } - return () => ''; -}; - -const getPath = (active, item) => { - if (active.startsWith(item.path)) { - return active; - } - return item.meta?.single ? item.redirect : item.path; -}; - -const useRenderNav = (active: string, list: Array) => { - return list.map((item) => { - if (!item.children || !item.children.length || item.meta?.single) { - const href = item.path.match(/(http|https):\/\/([\w.]+\/?)\S*/); - if (href) { - return ( - - {item.title} - - ); - } - return ( - - {item.title} - - ); - } - return ( - - {item.children && useRenderNav(active, item.children)} - - ); - }); -}; - -export default defineComponent({ - props: { - navData: { - type: Array as PropType, - default: () => [], - }, - }, - setup(props) { - const active = computed(() => getActive()); - const list = computed(() => { - const { navData } = props; - return getMenuList(navData); - }); - - return { - prefix, - active, - list, - useRenderNav, - }; - }, - render() { - return
{this.useRenderNav(this.active, this.list)}
; - }, -}); diff --git a/src/layouts/components/MenuContent.vue b/src/layouts/components/MenuContent.vue new file mode 100644 index 0000000..2ba7a09 --- /dev/null +++ b/src/layouts/components/MenuContent.vue @@ -0,0 +1,102 @@ + + + + + diff --git a/src/layouts/components/SideNav.tsx b/src/layouts/components/SideNav.tsx deleted file mode 100644 index b4ee42c..0000000 --- a/src/layouts/components/SideNav.tsx +++ /dev/null @@ -1,169 +0,0 @@ -import { defineComponent, PropType, computed, onMounted } from 'vue'; -import { useRouter } from 'vue-router'; -import union from 'lodash/union'; -import { prefix } from '@/config/global'; -import pgk from '../../../package.json'; -import MenuContent from './MenuContent'; -import AssetLogo from '@/assets/assets-t-logo.svg?component'; -import AssetLogoFull from '@/assets/assets-logo-full.svg?component'; -import { useSettingStore } from '@/store'; -import { getActive, getRoutesExpanded } from '@/router'; - -const MIN_POINT = 992 - 1; - -const useComputed = (props) => { - const collapsed = computed(() => useSettingStore().isSidebarCompact); - - const active = computed(() => getActive()); - - const defaultExpanded = computed(() => { - const path = getActive(); - const parentPath = path.substring(0, path.lastIndexOf('/')); - const expanded = getRoutesExpanded(); - return union(expanded, parentPath === '' ? [] : [parentPath]); - }); - - const sideNavCls = computed(() => { - const { isCompact } = props; - return [ - `${prefix}-sidebar-layout`, - { - [`${prefix}-sidebar-compact`]: isCompact, - }, - ]; - }); - - const menuCls = computed(() => { - const { showLogo, isFixed, layout } = props; - return [ - `${prefix}-side-nav`, - { - [`${prefix}-side-nav-no-logo`]: !showLogo, - [`${prefix}-side-nav-no-fixed`]: !isFixed, - [`${prefix}-side-nav-mix-fixed`]: layout === 'mix' && isFixed, - }, - ]; - }); - - const layoutCls = computed(() => { - const { layout } = props; - return [`${prefix}-side-nav-${layout}`, `${prefix}-sidebar-layout`]; - }); - - return { - active, - defaultExpanded, - collapsed, - sideNavCls, - menuCls, - layoutCls, - }; -}; - -export default defineComponent({ - name: 'SideNav', - components: { - AssetLogoFull, - AssetLogo, - MenuContent, - }, - props: { - menu: { - type: Array as PropType, - default: () => [], - }, - showLogo: { - type: Boolean as PropType, - default: true, - }, - isFixed: { - type: Boolean as PropType, - default: true, - }, - layout: { - type: String as PropType, - default: '', - }, - headerHeight: { - type: String as PropType, - default: '64px', - }, - theme: { - type: String as PropType, - default: 'light', - }, - isCompact: { - type: Boolean as PropType, - default: false, - }, - }, - setup(props) { - const router = useRouter(); - const settingStore = useSettingStore(); - - const changeCollapsed = () => { - settingStore.updateConfig({ - isSidebarCompact: !settingStore.isSidebarCompact, - }); - }; - - const autoCollapsed = () => { - const isCompact = window.innerWidth <= MIN_POINT; - settingStore.updateConfig({ - isSidebarCompact: isCompact, - }); - }; - - onMounted(() => { - autoCollapsed(); - window.onresize = () => { - autoCollapsed(); - }; - }); - - const goHome = () => { - router.push('/dashboard/base'); - }; - - return { - prefix, - ...useComputed(props), - autoCollapsed, - changeCollapsed, - goHome, - }; - }, - render() { - return ( -
- - this.showLogo && ( - - {this.collapsed ? ( - - ) : ( - - )} - - ), - operations: () => ( - - {!this.collapsed && 'TDesign Starter'} {pgk.version} - - ), - }} - > - - -
-
- ); - }, -}); diff --git a/src/layouts/components/SideNav.vue b/src/layouts/components/SideNav.vue new file mode 100644 index 0000000..2ffe464 --- /dev/null +++ b/src/layouts/components/SideNav.vue @@ -0,0 +1,126 @@ + + + + + diff --git a/src/layouts/index.tsx b/src/layouts/index.tsx deleted file mode 100644 index 540d8e8..0000000 --- a/src/layouts/index.tsx +++ /dev/null @@ -1,273 +0,0 @@ -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 '@/types/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, - query, - meta: { title }, - name, - } = route; - tabsRouterStore.appendTabRouterList({ path, query, 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(); - document.querySelector(`.${prefix}-layout`).scrollTo({ top: 0, behavior: 'smooth' }); - }, - ); - - 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({ path: nextRouter.path, query: nextRouter.query }); - } - }; - const handleChangeCurrentTab = (path: string) => { - const { tabRouters } = tabsRouterStore; - const route = tabRouters.find((i) => i.path === path); - router.push({ path, query: route.query }); - }; - const handleRefresh = (route: TRouterInfo, routeIdx: number) => { - tabsRouterStore.toggleTabRouterAlive(routeIdx); - nextTick(() => { - tabsRouterStore.toggleTabRouterAlive(routeIdx); - router.replace({ path: route.path, query: route.query }); - }); - }; - 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 && ( - - ) - ); - }; - - const renderHeader = () => { - return ( - settingStore.showHeader && ( - - ) - ); - }; - - const renderFooter = () => { - return ( - - - - ); - }; - - const renderContent = () => { - const { showBreadcrumb, showFooter, isUseTabsRouter } = settingStore; - const tabRouters = tabsRouterStore.tabRouters.filter((route) => route.isAlive || route.isHome); - return ( - // 如果存在多个滚动列表之间切换时,页面不刷新导致的样式问题 请设置key 但会导致多标签tab页的缓存失效 - - {isUseTabsRouter && ( - - {tabRouters.map((router: TRouterInfo, idx: number) => ( - ( - - router.path === route.path ? ( - - handleRefresh(router, idx)}> - - 刷新 - - {idx > 1 && ( - handleCloseAhead(router.path, idx)}> - - 关闭左侧 - - )} - {idx < tabRouters.length - 1 && ( - handleCloseBehind(router.path, idx)}> - - 关闭右侧 - - )} - handleCloseOther(router.path, idx)}> - - 关闭其它 - - - ) : null, - }} - > - {!router.isHome ? router.title : } - - ), - }} - removable={!router.isHome} - /> - ))} - - )} - - {showBreadcrumb && } - - - {showFooter && renderFooter()} - - ); - }; - - return { - setting, - mainLayoutCls, - renderSidebar, - renderHeader, - renderContent, - }; - }, - render() { - const { layout } = this.setting; - const header = this.renderHeader(); - const sidebar = this.renderSidebar(); - const content = this.renderContent(); - return ( -
- {layout === 'side' ? ( - - {sidebar} - {[header, content]} - - ) : ( - - {header} - {[sidebar, content]} - - )} - -
- ); - }, -}); diff --git a/src/layouts/index.vue b/src/layouts/index.vue new file mode 100644 index 0000000..53d3f0b --- /dev/null +++ b/src/layouts/index.vue @@ -0,0 +1,93 @@ + + + + + diff --git a/src/router/modules/base.ts b/src/router/modules/base.ts index e4ef588..000d455 100644 --- a/src/router/modules/base.ts +++ b/src/router/modules/base.ts @@ -1,4 +1,4 @@ -import Layout from '@/layouts'; +import Layout from '@/layouts/index.vue'; import DashboardIcon from '@/assets/assets-slide-dashboard.svg'; export default [ diff --git a/src/router/modules/components.ts b/src/router/modules/components.ts index 631189a..ba98689 100644 --- a/src/router/modules/components.ts +++ b/src/router/modules/components.ts @@ -1,4 +1,4 @@ -import Layout from '@/layouts'; +import Layout from '@/layouts/index.vue'; import ListIcon from '@/assets/assets-slide-list.svg'; import FormIcon from '@/assets/assets-slide-form.svg'; import DetailIcon from '@/assets/assets-slide-detail.svg'; diff --git a/src/router/modules/others.ts b/src/router/modules/others.ts index 6d3747a..974bfb7 100644 --- a/src/router/modules/others.ts +++ b/src/router/modules/others.ts @@ -1,4 +1,4 @@ -import Layout from '@/layouts'; +import Layout from '@/layouts/index.vue'; import LogoutIcon from '@/assets/assets-slide-logout.svg'; export default [