mirror of
https://github.com/fantastic-admin/basic.git
synced 2026-10-07 15:58:19 +08:00
增加基于文件系统的路由支持
This commit is contained in:
@@ -47,17 +47,17 @@ function handleCopy() {
|
||||
<el-alert title="应用配置可实时预览效果,但仅是临时生效,要想真正作用于项目,可以点击下方的“复制配置”按钮,将配置粘贴到 src/settings.custom.json 中即可,或者也可在 src/settings.js 中直接修改默认配置。同时建议在生产环境隐藏应用配置功能。" type="error" :closable="false" />
|
||||
<el-divider v-if="settingsStore.mode === 'pc'">导航栏模式</el-divider>
|
||||
<div v-if="settingsStore.mode === 'pc'" class="menu-mode">
|
||||
<el-tooltip content="侧边栏模式(含主导航)" placement="top" :show-after="500" :append-to-body="false">
|
||||
<el-tooltip content="侧边栏模式(含主导航)" placement="top" :show-after="500" append-to=".container">
|
||||
<div class="mode mode-side" :class="{'active': settings.menu.menuMode === 'side'}" @click="settings.menu.menuMode = 'side'">
|
||||
<svg-icon name="el-icon-check" />
|
||||
</div>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="顶部模式" placement="top" :show-after="500" :append-to-body="false">
|
||||
<el-tooltip content="顶部模式" placement="top" :show-after="500" append-to=".container">
|
||||
<div class="mode mode-head" :class="{'active': settings.menu.menuMode === 'head'}" @click="settings.menu.menuMode = 'head'">
|
||||
<svg-icon name="el-icon-check" />
|
||||
</div>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="侧边栏模式(不含主导航)" placement="top" :show-after="500" :append-to-body="false">
|
||||
<el-tooltip content="侧边栏模式(不含主导航)" placement="top" :show-after="500" append-to=".container">
|
||||
<div class="mode mode-single" :class="{'active': settings.menu.menuMode === 'single'}" @click="settings.menu.menuMode = 'single'">
|
||||
<svg-icon name="el-icon-check" />
|
||||
</div>
|
||||
@@ -71,7 +71,7 @@ function handleCopy() {
|
||||
<div class="setting-item">
|
||||
<div class="label">
|
||||
切换跳转
|
||||
<el-tooltip content="开启该功能后,切换侧边栏时,页面自动跳转至该侧边栏导航下第一个路由地址" placement="top" :append-to-body="false">
|
||||
<el-tooltip content="开启该功能后,切换侧边栏时,页面自动跳转至该侧边栏导航下第一个路由地址" placement="top" append-to=".container">
|
||||
<svg-icon name="el-icon-question-filled" />
|
||||
</el-tooltip>
|
||||
</div>
|
||||
@@ -80,7 +80,7 @@ function handleCopy() {
|
||||
<div class="setting-item">
|
||||
<div class="label">
|
||||
保持展开一个
|
||||
<el-tooltip content="开启该功能后,侧边栏只保持一个子菜单的展开" placement="top" :append-to-body="false">
|
||||
<el-tooltip content="开启该功能后,侧边栏只保持一个子菜单的展开" placement="top" append-to=".container">
|
||||
<svg-icon name="el-icon-question-filled" />
|
||||
</el-tooltip>
|
||||
</div>
|
||||
@@ -102,7 +102,7 @@ function handleCopy() {
|
||||
<div class="setting-item">
|
||||
<div class="label">
|
||||
导航栏搜索
|
||||
<el-tooltip content="对导航栏进行快捷搜索" placement="top" :append-to-body="false">
|
||||
<el-tooltip content="对导航栏进行快捷搜索" placement="top" append-to=".container">
|
||||
<svg-icon name="el-icon-question-filled" />
|
||||
</el-tooltip>
|
||||
</div>
|
||||
@@ -111,7 +111,7 @@ function handleCopy() {
|
||||
<div v-if="settingsStore.mode === 'pc'" class="setting-item">
|
||||
<div class="label">
|
||||
全屏
|
||||
<el-tooltip content="该功能使用场景极少,用户习惯于通过窗口“最大化”功能来扩大显示区域,以显示更多内容,并且使用 F11 键也可以进入全屏效果" placement="top" :append-to-body="false">
|
||||
<el-tooltip content="该功能使用场景极少,用户习惯于通过窗口“最大化”功能来扩大显示区域,以显示更多内容,并且使用 F11 键也可以进入全屏效果" placement="top" append-to=".container">
|
||||
<svg-icon name="el-icon-question-filled" />
|
||||
</el-tooltip>
|
||||
</div>
|
||||
@@ -120,7 +120,7 @@ function handleCopy() {
|
||||
<div class="setting-item">
|
||||
<div class="label">
|
||||
页面刷新
|
||||
<el-tooltip content="开启时会阻止原生 F5 键刷新功能,并采用框架提供的刷新模式进行页面刷新" placement="top" :append-to-body="false">
|
||||
<el-tooltip content="开启时会阻止原生 F5 键刷新功能,并采用框架提供的刷新模式进行页面刷新" placement="top" append-to=".container">
|
||||
<svg-icon name="el-icon-question-filled" />
|
||||
</el-tooltip>
|
||||
</div>
|
||||
@@ -147,7 +147,7 @@ function handleCopy() {
|
||||
<div class="setting-item">
|
||||
<div class="label">
|
||||
是否开启
|
||||
<el-tooltip content="该功能开启时,登录成功默认进入控制台页面,反之则默认进入导航栏里第一个导航页面" placement="top" :append-to-body="false">
|
||||
<el-tooltip content="该功能开启时,登录成功默认进入控制台页面,反之则默认进入导航栏里第一个导航页面" placement="top" append-to=".container">
|
||||
<svg-icon name="el-icon-question-filled" />
|
||||
</el-tooltip>
|
||||
</div>
|
||||
@@ -161,7 +161,7 @@ function handleCopy() {
|
||||
<div class="setting-item">
|
||||
<div class="label">
|
||||
组件尺寸
|
||||
<el-tooltip content="全局设置 Element Plus 组件的默认尺寸大小" placement="top" :append-to-body="false">
|
||||
<el-tooltip content="全局设置 Element Plus 组件的默认尺寸大小" placement="top" append-to=".container">
|
||||
<svg-icon name="el-icon-question-filled" />
|
||||
</el-tooltip>
|
||||
</div>
|
||||
@@ -175,14 +175,10 @@ function handleCopy() {
|
||||
<div class="label">是否启用权限</div>
|
||||
<el-switch v-model="settings.app.enablePermission" />
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="label">是否开启后端返回路由数据</div>
|
||||
<el-switch v-model="settings.app.enableBackendReturnRoute" />
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<div class="label">
|
||||
载入进度条
|
||||
<el-tooltip content="该功能开启时,跳转路由会看到页面顶部有进度条" placement="top" :append-to-body="false">
|
||||
<el-tooltip content="该功能开启时,跳转路由会看到页面顶部有进度条" placement="top" append-to=".container">
|
||||
<svg-icon name="el-icon-question-filled" />
|
||||
</el-tooltip>
|
||||
</div>
|
||||
@@ -191,7 +187,7 @@ function handleCopy() {
|
||||
<div class="setting-item">
|
||||
<div class="label">
|
||||
动态标题
|
||||
<el-tooltip content="该功能开启时,页面标题会显示当前路由标题,格式为“页面标题 - 网站名称”;关闭时则显示网站名称,网站名称在项目根目录下 .env.* 文件里配置" placement="top" :append-to-body="false">
|
||||
<el-tooltip content="该功能开启时,页面标题会显示当前路由标题,格式为“页面标题 - 网站名称”;关闭时则显示网站名称,网站名称在项目根目录下 .env.* 文件里配置" placement="top" append-to=".container">
|
||||
<svg-icon name="el-icon-question-filled" />
|
||||
</el-tooltip>
|
||||
</div>
|
||||
|
||||
@@ -7,6 +7,8 @@ import { useSettingsStore } from '@/store/modules/settings'
|
||||
const settingsStore = useSettingsStore()
|
||||
import { useRouteStore } from '@/store/modules/route'
|
||||
const routeStore = useRouteStore()
|
||||
import { useMenuStore } from '@/store/modules/menu'
|
||||
const menuStore = useMenuStore()
|
||||
|
||||
const isShow = ref(false)
|
||||
const searchInput = ref('')
|
||||
@@ -68,9 +70,15 @@ onMounted(() => {
|
||||
isShow.value = true
|
||||
}
|
||||
})
|
||||
routeStore.routes.map(item => {
|
||||
getSourceList(item.children)
|
||||
})
|
||||
if (settingsStore.app.routeBaseOn !== 'filesystem') {
|
||||
routeStore.routes.map(item => {
|
||||
getSourceList(item.children)
|
||||
})
|
||||
} else {
|
||||
menuStore.menus.map(item => {
|
||||
getSourceList(item.children)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
function hasChildren(item) {
|
||||
|
||||
@@ -30,8 +30,8 @@ function onSidebarScroll(e) {
|
||||
}"
|
||||
>
|
||||
<transition-group name="sub-sidebar">
|
||||
<template v-for="route in menuStore.sidebarMenus">
|
||||
<SidebarItem v-if="route.meta.sidebar !== false" :key="route.path" :item="route" :base-path="route.path" />
|
||||
<template v-for="(route, index) in menuStore.sidebarMenus">
|
||||
<SidebarItem v-if="route.meta.sidebar !== false" :key="route.path || index" :item="route" :base-path="route.path" />
|
||||
</template>
|
||||
</transition-group>
|
||||
</el-menu>
|
||||
|
||||
@@ -57,7 +57,7 @@ function pathCompile(path) {
|
||||
<div v-if="enableSidebarCollapse" class="sidebar-collapse" :class="{'is-collapse': settingsStore.menu.subMenuCollapse}" @click="settingsStore.toggleSidebarCollapse()">
|
||||
<svg-icon name="toolbar-collapse" />
|
||||
</div>
|
||||
<el-breadcrumb v-if="settingsStore.topbar.enableBreadcrumb && settingsStore.mode === 'pc'" separator-class="el-icon-arrow-right">
|
||||
<el-breadcrumb v-if="settingsStore.topbar.enableBreadcrumb && settingsStore.mode === 'pc' && settingsStore.app.routeBaseOn !== 'filesystem'" separator-class="el-icon-arrow-right">
|
||||
<transition-group name="breadcrumb">
|
||||
<el-breadcrumb-item v-for="(item, index) in breadcrumbList" :key="item.path" :to="index < breadcrumbList.length - 1 ? pathCompile(item.path) : ''">
|
||||
{{ item.title }}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import MultilevelMenuExample from './modules/multilevel.menu.example'
|
||||
|
||||
const menu = [
|
||||
{
|
||||
meta: {
|
||||
title: '演示',
|
||||
icon: 'sidebar-default'
|
||||
},
|
||||
children: [
|
||||
MultilevelMenuExample
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
export default menu
|
||||
@@ -0,0 +1,46 @@
|
||||
export default {
|
||||
meta: {
|
||||
title: '多级导航',
|
||||
icon: 'sidebar-menu'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '/multilevel_menu_example/page',
|
||||
meta: {
|
||||
title: '导航1'
|
||||
}
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
title: '导航2'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '/multilevel_menu_example/level2/page',
|
||||
meta: {
|
||||
title: '导航2-1'
|
||||
}
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
title: '导航2-2'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '/multilevel_menu_example/level2/level3/page1',
|
||||
meta: {
|
||||
title: '导航2-2-1'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/multilevel_menu_example/level2/level3/page2',
|
||||
meta: {
|
||||
title: '导航2-2-2'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
export default [
|
||||
{
|
||||
url: '/mock/menu/list',
|
||||
method: 'get',
|
||||
response: () => {
|
||||
return {
|
||||
error: '',
|
||||
status: 1,
|
||||
data: [
|
||||
{
|
||||
meta: {
|
||||
title: '演示',
|
||||
icon: 'sidebar-default'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
meta: {
|
||||
title: '多级导航',
|
||||
icon: 'sidebar-menu'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '/multilevel_menu_example/page',
|
||||
meta: {
|
||||
title: '导航1'
|
||||
}
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
title: '导航2'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '/multilevel_menu_example/level2/page',
|
||||
meta: {
|
||||
title: '导航2-1'
|
||||
}
|
||||
},
|
||||
{
|
||||
meta: {
|
||||
title: '导航2-2'
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '/multilevel_menu_example/level2/level3/page1',
|
||||
meta: {
|
||||
title: '导航2-2-1'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/multilevel_menu_example/level2/level3/page2',
|
||||
meta: {
|
||||
title: '导航2-2-2'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
+41
-9
@@ -10,7 +10,7 @@ import { useNProgress } from '@vueuse/integrations/useNProgress'
|
||||
const { isLoading } = useNProgress()
|
||||
|
||||
// 固定路由
|
||||
const constantRoutes = [
|
||||
let constantRoutes = [
|
||||
{
|
||||
path: '/login',
|
||||
name: 'login',
|
||||
@@ -81,7 +81,7 @@ import EcologyExample from './modules/ecology.example'
|
||||
import CooperationExample from './modules/cooperation.example'
|
||||
|
||||
// 动态路由(异步路由、导航栏路由)
|
||||
const asyncRoutes = [
|
||||
let asyncRoutes = [
|
||||
{
|
||||
meta: {
|
||||
title: '演示',
|
||||
@@ -129,13 +129,26 @@ const asyncRoutes = [
|
||||
]
|
||||
|
||||
const lastRoute = {
|
||||
path: '/:pathMatch(.*)*',
|
||||
component: () => import('@/views/404.vue'),
|
||||
path: '/:all(.*)*',
|
||||
name: 'notFound',
|
||||
component: () => import('@/views/[...all].vue'),
|
||||
meta: {
|
||||
title: '找不到页面'
|
||||
}
|
||||
}
|
||||
|
||||
import { setupLayouts } from 'virtual:generated-layouts'
|
||||
import generatedRoutes from 'virtual:generated-pages'
|
||||
|
||||
if (useSettingsOutsideStore().app.routeBaseOn === 'filesystem') {
|
||||
constantRoutes = generatedRoutes.filter(item => {
|
||||
return item.meta?.enabled !== false && item.meta?.constant === true
|
||||
})
|
||||
asyncRoutes = setupLayouts(generatedRoutes.filter(item => {
|
||||
return item.meta?.enabled !== false && item.meta?.constant !== true && item.meta?.layout !== false
|
||||
}))
|
||||
}
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes: constantRoutes
|
||||
@@ -184,10 +197,24 @@ router.beforeEach(async(to, from, next) => {
|
||||
next()
|
||||
}
|
||||
} else {
|
||||
if (!settingsOutsideStore.app.enableBackendReturnRoute) {
|
||||
await routeOutsideStore.generateRoutesAtFront(asyncRoutes)
|
||||
} else {
|
||||
await routeOutsideStore.generateRoutesAtBack()
|
||||
switch (settingsOutsideStore.app.routeBaseOn) {
|
||||
case 'frontend':
|
||||
await routeOutsideStore.generateRoutesAtFront(asyncRoutes)
|
||||
break
|
||||
case 'backend':
|
||||
await routeOutsideStore.generateRoutesAtBack()
|
||||
break
|
||||
case 'filesystem':
|
||||
await routeOutsideStore.generateRoutesAtFilesystem(asyncRoutes)
|
||||
switch (settingsOutsideStore.menu.baseOn) {
|
||||
case 'frontend':
|
||||
await menuOutsideStore.generateMenusAtFront()
|
||||
break
|
||||
case 'backend':
|
||||
await menuOutsideStore.generateMenusAtBack()
|
||||
break
|
||||
}
|
||||
break
|
||||
}
|
||||
let removeRoutes = []
|
||||
routeOutsideStore.flatRoutes.forEach(route => {
|
||||
@@ -195,7 +222,12 @@ router.beforeEach(async(to, from, next) => {
|
||||
removeRoutes.push(router.addRoute(route))
|
||||
}
|
||||
})
|
||||
removeRoutes.push(router.addRoute(lastRoute))
|
||||
if (settingsOutsideStore.app.routeBaseOn === 'filesystem') {
|
||||
const otherRoutes = generatedRoutes.filter(item => item.meta?.constant !== true && item.meta?.layout === false)
|
||||
otherRoutes.length && removeRoutes.push(router.addRoute(...otherRoutes))
|
||||
} else {
|
||||
removeRoutes.push(router.addRoute(lastRoute))
|
||||
}
|
||||
// 记录的 accessRoutes 路由数据,在登出时会使用到,不使用 router.removeRoute 是考虑配置的路由可能不一定有设置 name ,则通过调用 router.addRoute() 返回的回调进行删除
|
||||
routeOutsideStore.setCurrentRemoveRoutes(removeRoutes)
|
||||
next({ ...to, replace: true })
|
||||
|
||||
+14
-3
@@ -10,12 +10,17 @@ let globalSettings = {
|
||||
* 4、鉴权函数:this.$auth()、this.$authAll()
|
||||
*/
|
||||
enablePermission: false,
|
||||
// 是否开启后端返回路由数据
|
||||
enableBackendReturnRoute: false,
|
||||
// 是否开启载入进度条
|
||||
enableProgress: true,
|
||||
// 是否开启动态标题
|
||||
enableDynamicTitle: false
|
||||
enableDynamicTitle: false,
|
||||
/**
|
||||
* 路由数据来源
|
||||
* frontend 前端
|
||||
* backend 后端
|
||||
* filesystem 文件系统
|
||||
*/
|
||||
routeBaseOn: 'frontend'
|
||||
},
|
||||
// 控制台
|
||||
dashboard: {
|
||||
@@ -31,6 +36,12 @@ let globalSettings = {
|
||||
},
|
||||
// 导航栏
|
||||
menu: {
|
||||
/**
|
||||
* 数据来源,当 app.routeBaseOn 为 filesystem 时生效
|
||||
* frontend 前端
|
||||
* backend 后端
|
||||
*/
|
||||
baseOn: 'frontend',
|
||||
/**
|
||||
* 导航栏模式
|
||||
* side 侧边栏模式(含主导航)
|
||||
|
||||
+100
-16
@@ -1,9 +1,12 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { piniaStore } from '@/store'
|
||||
import { resolveRoutePath } from '@/util'
|
||||
import { deepClone, resolveRoutePath } from '@/util'
|
||||
import path from 'path-browserify'
|
||||
import api from '@/api'
|
||||
import menu from '@/menu'
|
||||
|
||||
import { useSettingsStore } from './settings'
|
||||
import { useUserStore } from './user'
|
||||
import { useRouteStore } from './route'
|
||||
|
||||
function getDeepestPath(routes, rootPath = '') {
|
||||
@@ -29,28 +32,67 @@ function getDeepestPath(routes, rootPath = '') {
|
||||
return retnPath
|
||||
}
|
||||
|
||||
function hasPermission(permissions, route) {
|
||||
let isAuth = false
|
||||
if (route.meta && route.meta.auth) {
|
||||
isAuth = permissions.some(auth => {
|
||||
if (typeof route.meta.auth == 'string') {
|
||||
return route.meta.auth === auth
|
||||
} else {
|
||||
return route.meta.auth.some(routeAuth => {
|
||||
return routeAuth === auth
|
||||
})
|
||||
}
|
||||
})
|
||||
} else {
|
||||
isAuth = true
|
||||
}
|
||||
return isAuth
|
||||
}
|
||||
|
||||
function filterAsyncMenus(menus, permissions) {
|
||||
const res = []
|
||||
menus.forEach(menu => {
|
||||
let tmpMenu = deepClone(menu)
|
||||
if (hasPermission(permissions, tmpMenu)) {
|
||||
if (tmpMenu.children) {
|
||||
tmpMenu.children = filterAsyncMenus(tmpMenu.children, permissions)
|
||||
tmpMenu.children.length && res.push(tmpMenu)
|
||||
} else {
|
||||
res.push(tmpMenu)
|
||||
}
|
||||
}
|
||||
})
|
||||
return res
|
||||
}
|
||||
|
||||
export const useMenuStore = defineStore(
|
||||
// 唯一ID
|
||||
'menu',
|
||||
{
|
||||
state: () => ({
|
||||
menus: [],
|
||||
actived: 0
|
||||
}),
|
||||
getters: {
|
||||
// 完整导航数据
|
||||
allMenus() {
|
||||
const settingsStore = useSettingsStore()
|
||||
const routeStore = useRouteStore()
|
||||
let routes
|
||||
if (settingsStore.menu.menuMode === 'single') {
|
||||
routes = [{ children: [] }]
|
||||
routeStore.routes.map(item => {
|
||||
routes[0].children.push(...item.children)
|
||||
})
|
||||
let menus
|
||||
if (settingsStore.app.routeBaseOn !== 'filesystem') {
|
||||
const routeStore = useRouteStore()
|
||||
if (settingsStore.menu.menuMode === 'single') {
|
||||
menus = [{ children: [] }]
|
||||
routeStore.routes.map(item => {
|
||||
menus[0].children.push(...item.children)
|
||||
})
|
||||
} else {
|
||||
menus = routeStore.routes
|
||||
}
|
||||
} else {
|
||||
routes = routeStore.routes
|
||||
menus = this.menus
|
||||
}
|
||||
return routes
|
||||
return menus
|
||||
},
|
||||
// 次导航数据
|
||||
sidebarMenus() {
|
||||
@@ -61,18 +103,60 @@ export const useMenuStore = defineStore(
|
||||
return this.allMenus.length > 0 ? getDeepestPath(this.sidebarMenus[0]) : '/'
|
||||
},
|
||||
defaultOpenedPaths() {
|
||||
const routeStore = useRouteStore()
|
||||
const settingsStore = useSettingsStore()
|
||||
let defaultOpenedPaths = []
|
||||
routeStore.routes.map(item => {
|
||||
item.meta.defaultOpened && defaultOpenedPaths.push(item.path)
|
||||
item.children && item.children.map(child => {
|
||||
child.meta.defaultOpened && defaultOpenedPaths.push(path.resolve(item.path, child.path))
|
||||
if (settingsStore.app.routeBaseOn !== 'filesystem') {
|
||||
const routeStore = useRouteStore()
|
||||
routeStore.routes.map(item => {
|
||||
item.meta.defaultOpened && defaultOpenedPaths.push(item.path)
|
||||
item.children && item.children.map(child => {
|
||||
child.meta.defaultOpened && defaultOpenedPaths.push(path.resolve(item.path, child.path))
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
return defaultOpenedPaths
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
// 生成导航(前端生成)
|
||||
generateMenusAtFront() {
|
||||
// eslint-disable-next-line no-async-promise-executor
|
||||
return new Promise(async resolve => {
|
||||
const settingsStore = useSettingsStore()
|
||||
const userStore = useUserStore()
|
||||
let accessedMenus
|
||||
// 如果权限功能开启,则需要对导航数据进行筛选过滤
|
||||
if (settingsStore.app.enablePermission) {
|
||||
const permissions = await userStore.getPermissions()
|
||||
accessedMenus = filterAsyncMenus(menu, permissions)
|
||||
} else {
|
||||
accessedMenus = deepClone(menu)
|
||||
}
|
||||
this.menus = accessedMenus.filter(item => item.children.length != 0)
|
||||
resolve()
|
||||
})
|
||||
},
|
||||
// 生成导航(后端生成)
|
||||
generateMenusAtBack() {
|
||||
return new Promise(resolve => {
|
||||
api.get('menu/list', {
|
||||
baseURL: '/mock/'
|
||||
}).then(async res => {
|
||||
const settingsStore = useSettingsStore()
|
||||
const userStore = useUserStore()
|
||||
let accessedMenus
|
||||
// 如果权限功能开启,则需要对导航数据进行筛选过滤
|
||||
if (settingsStore.app.enablePermission) {
|
||||
const permissions = await userStore.getPermissions()
|
||||
accessedMenus = filterAsyncMenus(res.data, permissions)
|
||||
} else {
|
||||
accessedMenus = deepClone(res.data)
|
||||
}
|
||||
this.menus = accessedMenus.filter(item => item.children.length != 0)
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
},
|
||||
// 切换主导航
|
||||
setActived(data) {
|
||||
if (typeof data === 'number') {
|
||||
|
||||
+38
-11
@@ -130,19 +130,26 @@ export const useRouteStore = defineStore(
|
||||
getters: {
|
||||
// 扁平化路由(将三级及以上路由数据拍平成二级)
|
||||
flatRoutes: state => {
|
||||
const settingsStore = useSettingsStore()
|
||||
let routes = []
|
||||
if (state.routes) {
|
||||
state.routes.map(item => {
|
||||
routes.push(...deepClone(item.children))
|
||||
})
|
||||
routes.map(item => {
|
||||
if (item.children) {
|
||||
item.children = flatAsyncRoutes(item.children, [{
|
||||
path: item.path,
|
||||
title: item.meta.title
|
||||
}], item.path)
|
||||
}
|
||||
})
|
||||
if (settingsStore.app.routeBaseOn !== 'filesystem') {
|
||||
state.routes.map(item => {
|
||||
routes.push(...deepClone(item.children))
|
||||
})
|
||||
routes.map(item => {
|
||||
if (item.children) {
|
||||
item.children = flatAsyncRoutes(item.children, [{
|
||||
path: item.path,
|
||||
title: item.meta.title
|
||||
}], item.path)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
state.routes.map(item => {
|
||||
routes.push(deepClone(item))
|
||||
})
|
||||
}
|
||||
}
|
||||
return routes
|
||||
}
|
||||
@@ -192,6 +199,26 @@ export const useRouteStore = defineStore(
|
||||
})
|
||||
})
|
||||
},
|
||||
// 根据权限动态生成路由(文件系统生成)
|
||||
generateRoutesAtFilesystem(asyncRoutes) {
|
||||
// eslint-disable-next-line no-async-promise-executor
|
||||
return new Promise(async resolve => {
|
||||
const settingsStore = useSettingsStore()
|
||||
const userStore = useUserStore()
|
||||
let accessedRoutes
|
||||
// 如果权限功能开启,则需要对路由数据进行筛选过滤
|
||||
if (settingsStore.app.enablePermission) {
|
||||
const permissions = await userStore.getPermissions()
|
||||
accessedRoutes = filterAsyncRoutes(asyncRoutes, permissions)
|
||||
} else {
|
||||
accessedRoutes = deepClone(asyncRoutes)
|
||||
}
|
||||
// 设置 routes 数据
|
||||
this.isGenerate = true
|
||||
this.routes = accessedRoutes.filter(item => item.children.length != 0)
|
||||
resolve()
|
||||
})
|
||||
},
|
||||
// 记录 accessRoutes 路由,用于登出时删除路由
|
||||
setCurrentRemoveRoutes(routes) {
|
||||
this.currentRemoveRoutes = routes
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
<route>
|
||||
{
|
||||
name: 'notFound',
|
||||
meta: {
|
||||
title: "找不到页面",
|
||||
layout: false
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<script setup>
|
||||
import { onBeforeRouteLeave } from 'vue-router'
|
||||
const router = useRouter()
|
||||
@@ -1,3 +1,11 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<page-main>
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<page-main>
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<template>
|
||||
<page-main>
|
||||
<router-link :to="{name: 'breadcrumbExampleDetail1'}">查看详情页</router-link>
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<template>
|
||||
<page-main>
|
||||
<router-link :to="{name: 'breadcrumbExampleDetail2'}">查看详情页</router-link>
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<script setup>
|
||||
import Alert from './components/alert.vue'
|
||||
import { Search, Edit, Check, Message, Star, Delete, Share, ArrowLeft } from '@element-plus/icons-vue'
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<script>
|
||||
import Alert from './components/alert.vue'
|
||||
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<script>
|
||||
import Alert from './components/alert.vue'
|
||||
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<script>
|
||||
import Alert from './components/alert.vue'
|
||||
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<script setup>
|
||||
import Alert from './components/alert.vue'
|
||||
import { Search } from '@element-plus/icons-vue'
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<script>
|
||||
import Alert from './components/alert.vue'
|
||||
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<script>
|
||||
import Alert from './components/alert.vue'
|
||||
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<script setup>
|
||||
import Alert from './components/alert.vue'
|
||||
import { Edit } from '@element-plus/icons-vue'
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<script>
|
||||
import Alert from './components/alert.vue'
|
||||
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<script>
|
||||
import Alert from './components/alert.vue'
|
||||
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<script>
|
||||
import Alert from './components/alert.vue'
|
||||
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<script>
|
||||
import Alert from './components/alert.vue'
|
||||
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<script>
|
||||
import Alert from './components/alert.vue'
|
||||
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<script>
|
||||
import Alert from './components/alert.vue'
|
||||
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ComponentExampleArea',
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ComponentExampleBatchactionbar',
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<script setup>
|
||||
import { mavonEditor } from 'mavon-editor'
|
||||
import 'mavon-editor/dist/css/index.css'
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {},
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<page-header title="页头" content="PageHeader" />
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<page-header title="内容块" content="PageMain" />
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ComponentExampleTableHeightAdaption',
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<page-header title="趋势符号">
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
<route>
|
||||
{
|
||||
name: 'dashboard',
|
||||
meta: {
|
||||
title: "控制台"
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<script setup>
|
||||
const locationOrigin = location.origin
|
||||
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<template>
|
||||
<page-main>
|
||||
<el-button @click="$router.go(-1)">返回</el-button>
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<script setup name="TabExampleNested1">
|
||||
import { useKeepAliveStore } from '@/store/modules/keepAlive'
|
||||
const keepAliveStore = useKeepAliveStore()
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<script setup name="TabExampleNested2">
|
||||
import { useKeepAliveStore } from '@/store/modules/keepAlive'
|
||||
const keepAliveStore = useKeepAliveStore()
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'TabExampleNestedTest',
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'TabExampleNestedTest2',
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<script setup name="KeepAliveExamplePage">
|
||||
import { onBeforeRouteLeave } from 'vue-router'
|
||||
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
title: "登录",
|
||||
constant: true,
|
||||
layout: false
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<script setup name="Login">
|
||||
const { proxy } = getCurrentInstance()
|
||||
const route = useRoute(), router = useRouter()
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<script setup>
|
||||
const { proxy } = getCurrentInstance()
|
||||
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
title: '导航2-2-1'
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<page-main>
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
title: '导航2-2-2'
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<page-main>
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
title: '导航2-1'
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<page-main>
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
title: '导航1'
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<page-main>
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<script setup>
|
||||
const { proxy } = getCurrentInstance()
|
||||
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
title: "修改密码"
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<script setup name="PersonalEditPassword">
|
||||
const route = useRoute(), router = useRouter()
|
||||
const { proxy } = getCurrentInstance()
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
title: "个人设置",
|
||||
cache: "personal-edit.password"
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<script setup name="PersonalSetting">
|
||||
const router = useRouter()
|
||||
const { proxy } = getCurrentInstance()
|
||||
@@ -26,7 +35,6 @@ function editPassword() {
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<!-- 页面:Setting -->
|
||||
<page-main>
|
||||
<el-tabs tab-position="left" style="height: 600px;">
|
||||
<el-tab-pane label="基本设置" class="basic">
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
constant: true,
|
||||
layout: false
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<script setup>
|
||||
const router = useRouter()
|
||||
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
<route>
|
||||
{
|
||||
meta: {
|
||||
enabled: false
|
||||
}
|
||||
}
|
||||
</route>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<page-header title="即将推出 Vue3 版本视频教程,尽请期待" />
|
||||
|
||||
Reference in New Issue
Block a user