Explorar o código

增加globalTabs组件

tongshangming %!s(int64=3) %!d(string=hai) anos
pai
achega
5075e47a7e

+ 5 - 0
src/components.d.ts

@@ -12,6 +12,8 @@ declare module '@vue/runtime-core' {
     DialogForm: typeof import('./components/form/DialogForm.vue')['default']
     ElAside: typeof import('element-plus/es')['ElAside']
     ElAvatar: typeof import('element-plus/es')['ElAvatar']
+    ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb']
+    ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem']
     ElButton: typeof import('element-plus/es')['ElButton']
     ElCard: typeof import('element-plus/es')['ElCard']
     ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
@@ -41,6 +43,8 @@ declare module '@vue/runtime-core' {
     ElSwitch: typeof import('element-plus/es')['ElSwitch']
     ElTable: typeof import('element-plus/es')['ElTable']
     ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
+    ElTabPane: typeof import('element-plus/es')['ElTabPane']
+    ElTabs: typeof import('element-plus/es')['ElTabs']
     Exception: typeof import('./components/Exception.vue')['default']
     FormComp: typeof import('./components/form/FormComp.vue')['default']
     FormItem: typeof import('./components/form/FormItem.vue')['default']
@@ -48,6 +52,7 @@ declare module '@vue/runtime-core' {
     GlobalHeader: typeof import('./components/GlobalHeader.vue')['default']
     GlobalMenu: typeof import('./components/GlobalMenu.vue')['default']
     GlobalSetting: typeof import('./components/GlobalSetting.vue')['default']
+    GlobalTabs: typeof import('./components/GlobalTabs.vue')['default']
     IEpArrowDown: typeof import('~icons/ep/arrow-down')['default']
     IEpLock: typeof import('~icons/ep/lock')['default']
     IEpRefresh: typeof import('~icons/ep/refresh')['default']

+ 14 - 4
src/components/GlobalHeader.vue

@@ -3,6 +3,7 @@ import { useMenuStore } from '@/stores/menu'
 import { useUserStore } from '@/stores/user'
 import config from '@/config/defaultSetting'
 import avatar from '@/assets/images/avatar.png'
+import type { RouteRecordRaw } from 'vue-router'
 
 const iconSize = ref('22')
 
@@ -14,6 +15,10 @@ const handleToggle = () => {
   menuStore.setCollapse(value.value)
 }
 
+// 面包屑
+const route = useRoute()
+const matched = computed(() => route.matched)
+
 // 全屏切换
 const { toggle: toggleScreen, isFullscreen } = useFullscreen()
 
@@ -32,7 +37,7 @@ const logout = () => {
 }
 
 // 主题设置
-const visible = ref(false)
+const settingVisible = ref(false)
 </script>
 
 <template>
@@ -47,13 +52,18 @@ const visible = ref(false)
           <icon-menu-unfold-one :size="iconSize" v-if="!value" />
           <icon-menu-fold-one :size="iconSize" v-else />
         </el-button>
+        <el-breadcrumb class="breadcrumb">
+          <el-breadcrumb-item v-for="item in matched.slice(1, matched.length)" :key="item.path">
+            {{ item.meta.title }}
+          </el-breadcrumb-item>
+        </el-breadcrumb>
       </el-space>
       <div class="flex items-center">
         <el-button link @click="toggleScreen">
           <icon-off-screen :size="iconSize" v-if="isFullscreen" />
           <icon-full-screen :size="iconSize" v-else />
         </el-button>
-        <el-button link class="mr-4" @click="visible = true">
+        <el-button link class="mr-4" @click="settingVisible = true">
           <icon-setting-two :size="iconSize" />
         </el-button>
         <el-avatar class="mr-10px" :src="user.photo">
@@ -63,7 +73,7 @@ const visible = ref(false)
           <span class="el-dropdown-link">
             {{ user.name }}
             <el-icon class="el-icon--right">
-              <i-ep-arrow-down />
+              <arrow-down />
             </el-icon>
           </span>
           <template #dropdown>
@@ -82,7 +92,7 @@ const visible = ref(false)
       </div>
     </div>
 
-    <GlobalSetting v-model="visible"></GlobalSetting>
+    <GlobalSetting v-model="settingVisible"></GlobalSetting>
   </header>
 </template>
 

+ 113 - 0
src/components/GlobalTabs.vue

@@ -0,0 +1,113 @@
+<script lang="ts" setup>
+import type { TabPanelName } from 'element-plus'
+import type { RouteLocationNormalizedLoaded } from 'vue-router'
+import config from '@/config/defaultSetting'
+import { useRouterStore } from '@/stores/router'
+
+const router = useRouter()
+const route = useRoute()
+const routerStore = useRouterStore()
+
+const tabs = ref<any[]>([])
+const activeValue = ref(route.fullPath)
+const contextMenuVisible = ref(false)
+
+const getHomeRoute = (route: []) => {
+  return route.find((item: any) => {
+    if (item.children) {
+      getHomeRoute(item.children)
+    } else {
+      return item.name === config.homeRoute
+    }
+  })
+}
+
+watch(
+  () => route,
+  to => {
+    setTab(to)
+  },
+  {
+    deep: true
+  }
+)
+watch(
+  tabs,
+  () => {
+    sessionStorage.setItem('globalTabs', JSON.stringify(tabs.value))
+  },
+  {
+    deep: true
+  }
+)
+const setTab = (tab: RouteLocationNormalizedLoaded) => {
+  activeValue.value = tab.fullPath
+
+  if (tab.name === 'login' || tabs.value.find(item => item.fullPath === tab.fullPath)) {
+    return
+  }
+
+  tabs.value.push(routeToTab(tab))
+}
+const routeToTab = (route: any) => {
+  return {
+    name: route.name,
+    fullPath: route.fullPath,
+    params: route.params,
+    query: route.query,
+    title: route.meta.title
+  }
+}
+tabs.value.push(routeToTab(getHomeRoute(routerStore.menuRouter)))
+console.log(routeToTab(getHomeRoute(routerStore.menuRouter)))
+const initTabs = () => {
+  tabs.value = JSON.parse(sessionStorage.getItem('globalTabs') || '[]')
+  activeValue.value = window.sessionStorage.getItem('activeValue') || route.fullPath
+}
+initTabs()
+
+const changeTab = (name: TabPanelName) => {
+  router.push(tabs.value.find(item => item.fullPath === name))
+  sessionStorage.setItem('activeValue', name as string)
+}
+const removeTab = () => {}
+const openContextMenu = (event: any) => {}
+</script>
+
+<template>
+  <div class="bg-white" style="border-top: 1px solid var(--el-border-color-light)">
+    <el-tabs
+      v-model="activeValue"
+      :closable="!(tabs.length === 1)"
+      type="card"
+      @contextmenu.prevent="openContextMenu($event)"
+      @tab-change="changeTab"
+      @tab-remove="removeTab"
+    >
+      <el-tab-pane v-for="item in tabs" :key="item.name" :label="item.title" :name="item.fullPath" :tab="item">
+      </el-tab-pane>
+    </el-tabs>
+  </div>
+
+  <!--自定义右键菜单html代码-->
+  <!-- <ul
+      v-show="contextMenuVisible"
+      :style="{ left: left + 'px', top: top + 'px' }"
+      class="contextmenu"
+    >
+      <li @click="closeAll">关闭所有</li>
+      <li @click="closeLeft">关闭左侧</li>
+      <li @click="closeRight">关闭右侧</li>
+      <li @click="closeOther">关闭其他</li>
+    </ul> -->
+</template>
+
+<style lang="scss" scoped>
+:deep(.el-tabs__header) {
+  margin: 0;
+}
+:deep(.el-tabs__nav) {
+  border-top: none !important;
+  border-radius: 0 !important;
+}
+</style>

+ 1 - 1
src/components/ProTable.vue

@@ -166,7 +166,7 @@ defineExpose({
 </script>
 
 <template>
-  <div class="flex flex-col" style="height: calc(100vh - 60px - var(--main-padding) * 2)">
+  <div class="flex flex-col" style="height: calc(100vh - 100px - var(--main-padding) * 2)">
     <el-card class="mb-4" shadow="never">
       <el-form :inline="true">
         <el-form-item :label="item.label" v-for="item in searchList">

+ 1 - 0
src/config/defaultSetting.ts

@@ -1,5 +1,6 @@
 export default {
   title: '方是科技管理系统',
   logo: '/logo.png',
+  homeRoute: 'home',
   themeColor: '#1890ff'
 }

+ 5 - 2
src/layouts/BasicLayout.vue

@@ -7,8 +7,11 @@
     </el-header>
     <el-container>
       <global-menu />
-      <el-main>
-        <router-view></router-view>
+      <el-main style="padding: 0">
+        <global-tabs></global-tabs>
+        <div style="padding: var(--main-padding)">
+          <router-view></router-view>
+        </div>
       </el-main>
       <!-- <el-footer>
         <global-footer />

+ 5 - 0
src/main.ts

@@ -5,6 +5,7 @@ import 'uno.css'
 
 import ElementPlus from 'element-plus'
 import 'element-plus/dist/index.css'
+import * as ElementPlusIconsVue from '@element-plus/icons-vue'
 
 import editor from '@/components/ElEditor.vue'
 
@@ -22,6 +23,10 @@ app.use(router)
 app.use(ElementPlus)
 app.component('ElEditor', editor)
 
+for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
+  app.component(key, component)
+}
+
 install(app)
 
 app.mount('#app')

+ 3 - 3
src/router/asyncRouter.ts

@@ -33,16 +33,16 @@ const asyncRouter: RouteRecordRaw[] = [
         component: () => import('@/views/system/User.vue'),
         meta: {
           title: '用户管理',
-          icon: 'icon-people'
+          icon: 'user'
         }
       },
       {
-        path: 'https://baidu.com',
+        path: 'menu',
         name: 'systemMenu',
         component: () => import('@/views/system/Menu.vue'),
         meta: {
           title: '菜单管理',
-          icon: 'icon-application-menu'
+          icon: 'menu'
         }
       }
     ]

+ 2 - 1
src/stores/user.ts

@@ -19,10 +19,11 @@ export const useUserStore = defineStore({
       this.token = res.accessToken
       localStorage.setItem('token', this.token)
 
-      router.replace({ path: (router.currentRoute.value.query.redirect as string) || '/home' })
+      router.replace({ path: (router.currentRoute.value.query.redirect as string) || '/' })
     },
     logout() {
       localStorage.removeItem('token')
+      sessionStorage.removeItem('globalTabs')
       this.$reset()
       console.log(router.currentRoute)
       router.push({ path: '/login', query: { redirect: router.currentRoute.value.fullPath } })

+ 2 - 2
src/views/user/Login.vue

@@ -866,14 +866,14 @@ const handleSubmit = () => {
         <el-form-item prop="username">
           <el-input v-model="formData.username" placeholder="请输入用户名">
             <template #prefix>
-              <i-ep-user />
+              <user />
             </template>
           </el-input>
         </el-form-item>
         <el-form-item prop="password">
           <el-input type="password" v-model="formData.password" placeholder="请输入密码">
             <template #prefix>
-              <i-ep-lock />
+              <lock />
             </template>
           </el-input>
         </el-form-item>

+ 2 - 15
vite.config.ts

@@ -20,27 +20,14 @@ export default defineConfig({
     DefineOptions(),
     AutoImport({
       imports: ['vue', 'vue-router', 'pinia', '@vueuse/core'],
-      resolvers: [
-        ElementPlusResolver(),
-        IconsResolver({
-          prefix: 'Icon'
-        })
-      ],
+      resolvers: [ElementPlusResolver()],
       dts: 'src/auto-import.d.ts'
     }),
     Components({
-      resolvers: [
-        ElementPlusResolver(),
-        IconsResolver({
-          enabledCollections: ['ep']
-        })
-      ],
+      resolvers: [ElementPlusResolver()],
       dirs: ['src/components'],
       extensions: ['vue'],
       dts: 'src/components.d.ts'
-    }),
-    Icons({
-      autoInstall: true
     })
   ],
   resolve: {