zs 1 рік тому
батько
коміт
56208688f8

+ 11 - 0
package-lock.json

@@ -14,6 +14,7 @@
         "element-plus": "^2.5.6",
         "lodash-es": "^4.17.21",
         "path-browserify": "^1.0.1",
+        "path-to-regexp": "^6.2.1",
         "pinia": "^2.1.7",
         "vue": "^3.4.15",
         "vue-i18n": "^9.9.1",
@@ -2801,6 +2802,11 @@
       "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
       "dev": true
     },
+    "node_modules/path-to-regexp": {
+      "version": "6.2.1",
+      "resolved": "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-6.2.1.tgz",
+      "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw=="
+    },
     "node_modules/pathe": {
       "version": "1.1.2",
       "resolved": "https://registry.npmmirror.com/pathe/-/pathe-1.1.2.tgz",
@@ -5721,6 +5727,11 @@
       "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
       "dev": true
     },
+    "path-to-regexp": {
+      "version": "6.2.1",
+      "resolved": "https://registry.npmmirror.com/path-to-regexp/-/path-to-regexp-6.2.1.tgz",
+      "integrity": "sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw=="
+    },
     "pathe": {
       "version": "1.1.2",
       "resolved": "https://registry.npmmirror.com/pathe/-/pathe-1.1.2.tgz",

+ 1 - 0
package.json

@@ -17,6 +17,7 @@
     "element-plus": "^2.5.6",
     "lodash-es": "^4.17.21",
     "path-browserify": "^1.0.1",
+    "path-to-regexp": "^6.2.1",
     "pinia": "^2.1.7",
     "vue": "^3.4.15",
     "vue-i18n": "^9.9.1",

+ 92 - 0
src/components/Breadcrumb/index.vue

@@ -0,0 +1,92 @@
+<template>
+  <el-breadcrumb class="h-[50px] flex items-center">
+    <transition-group name="breadcrumb-transition">
+      <el-breadcrumb-item v-for="(item, index) in breadcrumbs" :key="item.path">
+        <span v-if="item.redirect === 'noredirect' || index === breadcrumbs.length - 1" class="text-[var(--el-disabled-text-color)]">{{
+          translateRouteTitle(item.meta.title)
+        }}</span>
+        <a v-else @click.prevent="handleLink(item)">
+          {{ translateRouteTitle(item.meta.title) }}
+        </a>
+      </el-breadcrumb-item>
+    </transition-group>
+  </el-breadcrumb>
+</template>
+
+<script setup>
+import { onBeforeMount, ref, watch } from 'vue'
+import { useRoute } from 'vue-router'
+import { compile } from 'path-to-regexp'
+import router from '@/router'
+import { translateRouteTitle } from '@/utils/i18n'
+
+const currentRoute = useRoute()
+const pathCompile = (path) => {
+  const { params } = currentRoute
+  const toPath = compile(path)
+  return toPath(params)
+}
+
+const breadcrumbs = ref([])
+
+function getBreadcrumb() {
+  let matched = currentRoute.matched.filter((item) => item.meta && item.meta.title)
+  const first = matched[0]
+  if (!isDashboard(first)) {
+    matched = [{ path: '/', meta: { title: 'dashboard' } }].concat(matched)
+  }
+  breadcrumbs.value = matched.filter((item) => {
+    return item.meta && item.meta.title && item.meta.breadcrumb !== false
+  })
+}
+
+function isDashboard(route) {
+  const name = route && route.name
+  if (!name) {
+    return false
+  }
+  return name.toString().trim().toLocaleLowerCase() === 'Dashboard'.toLocaleLowerCase()
+}
+
+function handleLink(item) {
+  const { redirect, path, children } = item
+  if (children && children.length > 0) return
+  if (redirect) {
+    router.push(redirect).catch((err) => {
+      console.warn(err)
+    })
+    return
+  }
+  router.push(pathCompile(path)).catch((err) => {
+    console.warn(err)
+  })
+}
+
+watch(
+  () => currentRoute.path,
+  (path) => {
+    if (path.startsWith('/redirect/')) {
+      return
+    }
+    getBreadcrumb()
+  }
+)
+
+onBeforeMount(() => {
+  getBreadcrumb()
+})
+</script>
+<style lang="scss" scoped>
+.app-breadcrumb.el-breadcrumb {
+  display: inline-block;
+  margin-left: 8px;
+  font-size: 14px;
+  line-height: 50px;
+}
+
+// 覆盖 element-plus 的样式
+.el-breadcrumb__inner,
+.el-breadcrumb__inner a {
+  font-weight: 400 !important;
+}
+</style>

+ 7 - 15
src/layout/parts/Header.vue

@@ -3,29 +3,20 @@
     <el-row>
       <el-col :span="24" class="main">
         <div class="left">
-          <el-breadcrumb separator="/">
-            <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
-            <el-breadcrumb-item :to="{ path: '/' }">用户管理</el-breadcrumb-item>
-            <el-breadcrumb-item>普通用户</el-breadcrumb-item>
-          </el-breadcrumb>
+          <component :is="breadcrumb"></component>
         </div>
         <div class="right">
           <el-dropdown>
-            <el-icon style="margin-right: 8px; margin-top: 1px">
-              <setting />
-            </el-icon>
+            <el-icon style="margin-right: 8px; margin-top: 1px"><setting /></el-icon>
             <template #dropdown>
               <el-dropdown-menu>
-                <el-dropdown-item>View</el-dropdown-item>
-                <el-dropdown-item>Add</el-dropdown-item>
-                <el-dropdown-item>Delete</el-dropdown-item>
+                <el-dropdown-item>我的信息</el-dropdown-item>
+                <el-dropdown-item>其他</el-dropdown-item>
+                <el-dropdown-item @click="logout">退出登录</el-dropdown-item>
               </el-dropdown-menu>
             </template>
           </el-dropdown>
           <span>
-            <el-icon>
-              <UserFilled />
-            </el-icon>
             {{ user && user._id ? user.nick_name : '游客' }}
           </span>
         </div>
@@ -35,10 +26,11 @@
 </template>
 
 <script setup>
+// 组件
+import breadcrumb from '@/components/Breadcrumb/index.vue'
 import { UserStore } from '@/store/user'
 const userStore = UserStore()
 const user = computed(() => userStore.user)
-let collapse = ref(false)
 const router = useRouter()
 // 退出登录
 const logout = () => {

+ 12 - 1
src/layout/parts/Sidebar.vue

@@ -62,11 +62,13 @@
 <script setup>
 import { siteInfo, menuInfo } from '@/layout/site'
 import { UserStore } from '@/store/user'
+import { useRoute } from 'vue-router'
 
 const route = useRoute()
+
+const onRoutes = ref(route.path)
 const userStore = UserStore()
 const user = computed(() => userStore.user)
-let onRoutes = route.path
 const styleInfo = ref(menuInfo.info)
 let items = ref(menuInfo.menuList)
 
@@ -88,6 +90,15 @@ watch(
     deep: true
   }
 )
+watch(
+  route,
+  (newVal) => {
+    if (newVal && newVal.path) onRoutes.value = newVal.path
+  },
+  {
+    immediate: true //初始化立即执行
+  }
+)
 </script>
 <style scoped lang="scss">
 .sidebar::-webkit-scrollbar {

+ 1 - 5
src/layout/parts/breadcrumb.vue

@@ -206,10 +206,7 @@ function toLastView(visitedViews, view) {
   if (latestView && latestView.fullPath) {
     router.push(latestView.fullPath)
   } else {
-    // now the default is to redirect to the home page if there is no tags-view,
-    // you can adjust it according to your needs.
     if (view?.name === 'Dashboard') {
-      // to reload home page
       router.replace({ path: '/redirect' + view.fullPath })
     } else {
       router.push('/')
@@ -258,7 +255,6 @@ function closeAllTags(view) {
  */
 function openContentMenu(tag, e) {
   const menuMinWidth = 105
-
   const offsetLeft = proxy?.$el.getBoundingClientRect().left // container margin left
   const offsetWidth = proxy?.$el.offsetWidth // container width
   const maxLeft = offsetWidth - menuMinWidth // left boundary
@@ -348,7 +344,6 @@ onMounted(() => {
 </script>
 <style lang="scss" scoped>
 .tags-container {
-  width: 100%;
   height: 35px;
   background-color: var(--el-bg-color);
   border: 1px solid var(--el-border-color-light);
@@ -396,6 +391,7 @@ onMounted(() => {
         width: 8px;
         height: 8px;
         margin-right: 5px;
+        content: "";
         background: #fff;
         border-radius: 50%;
       }

+ 36 - 35
src/router/index.js

@@ -3,38 +3,6 @@ import { registerBeforeRouter } from './guard'
 export const homeIndex = () => import('@/views/home/index.vue')
 export const Layout = () => import('@/layout/index.vue')
 
-const system = [
-  {
-    path: '/system/menus',
-    meta: { title: '菜单管理' },
-    component: () => import('@/views/system/menus/index.vue')
-  },
-  {
-    path: '/system/role',
-    meta: { title: '角色管理' },
-    component: () => import('@/views/system/role/index.vue')
-  },
-  {
-    path: '/system/dict',
-    meta: { title: '字典管理' },
-    component: () => import('@/views/system/dict/index.vue')
-  },
-  {
-    path: '/system/dictData',
-    meta: { title: '字典数据管理' },
-    component: () => import('@/views/system/dictData/index.vue')
-  },
-  {
-    path: '/system/config',
-    meta: { title: '平台设置' },
-    component: () => import('@/views/system/config/index.vue')
-  },
-  {
-    path: '/system/module',
-    meta: { title: '模块设置' },
-    component: () => import('@/views/system/module/index.vue')
-  }
-]
 const router = createRouter({
   history: createWebHistory(import.meta.env.BASE_URL),
   routes: [
@@ -57,9 +25,7 @@ const router = createRouter({
     },
     {
       path: '/',
-      meta: { title: '首页' },
       component: Layout,
-      redirect: '/dashboard',
       children: [
         {
           path: '/',
@@ -72,7 +38,42 @@ const router = createRouter({
           },
           component: () => import('@/views/home/index.vue')
         },
-        ...system
+        {
+          path: '/system',
+          meta: { title: '系统设置' },
+          children: [
+            {
+              path: '/system/menus',
+              meta: { title: '菜单管理' },
+              component: () => import('@/views/system/menus/index.vue')
+            },
+            {
+              path: '/system/role',
+              meta: { title: '角色管理' },
+              component: () => import('@/views/system/role/index.vue')
+            },
+            {
+              path: '/system/dict',
+              meta: { title: '字典管理' },
+              component: () => import('@/views/system/dict/index.vue')
+            },
+            {
+              path: '/system/dictData',
+              meta: { title: '字典数据管理' },
+              component: () => import('@/views/system/dictData/index.vue')
+            },
+            {
+              path: '/system/config',
+              meta: { title: '平台设置' },
+              component: () => import('@/views/system/config/index.vue')
+            },
+            {
+              path: '/system/module',
+              meta: { title: '模块设置' },
+              component: () => import('@/views/system/module/index.vue')
+            }
+          ]
+        }
       ]
     }
   ]