feat 对接路由生成

This commit is contained in:
xxm
2022-10-13 17:35:05 +08:00
parent 8112891db2
commit bb79544e81
19 changed files with 139 additions and 117 deletions

View File

@@ -1,20 +1,17 @@
# 是否打开mock
VITE_USE_MOCK = true
VITE_USE_MOCK=true
# 发布路径
VITE_PUBLIC_PATH = /
VITE_PUBLIC_PATH=/
# 跨域代理,您可以配置多个 ,请注意,没有换行符
VITE_PROXY = [["/api","http://localhost:9999"],["/upload","http://localhost:3300/upload"]]
#VITE_PROXY = [["/api","http://localhost:9999"],["/upload","http://localhost:3300/upload"]]
# VITE_PROXY=[["/api","http://localhost:9999"]]
VITE_PROXY=[["/api","http://localhost:9999"],["/upload","http://localhost:3300/upload"]]
# 控制台不输出console
VITE_DROP_CONSOLE = false
VITE_DROP_CONSOLE=false
# 接口地址
VITE_GLOB_API_URL=/api
#VITE_GLOB_API_URL=/basic-api
# 文件上传地址
VITE_GLOB_UPLOAD_URL=/upload

View File

@@ -1,35 +1,33 @@
# Whether to open mock
VITE_USE_MOCK = true
# 是否打开mock
VITE_USE_MOCK=true
# public path
VITE_PUBLIC_PATH = /
# 发布路径
VITE_PUBLIC_PATH=/
# Delete console
VITE_DROP_CONSOLE = true
# 控制台不输出console
VITE_DROP_CONSOLE=true
# Whether to enable gzip or brotli compression
# Optional: gzip | brotli | none
# If you need multiple forms, you can use `,` to separate
VITE_BUILD_COMPRESS = 'none'
# 是否启用gzip或brotli压缩可选: gzip | brotli |
# 无如果您需要多个表单,可以使用 '' 分隔
VITE_BUILD_COMPRESS='none'
# Whether to delete origin files when using compress, default false
VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE = false
# 使用compress时是否删除origin文件默认为false
VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE=false
# Basic interface address SPA
VITE_GLOB_API_URL=/basic-api
# 接口地址
VITE_GLOB_API_URL=/api
# File upload address optional
# It can be forwarded by nginx or write the actual address directly
# 文件上传地址
VITE_GLOB_UPLOAD_URL=/upload
# Interface prefix
# 接口前缀
VITE_GLOB_API_URL_PREFIX=
# Whether to enable image compression
VITE_USE_IMAGEMIN= true
VITE_USE_IMAGEMIN=true
# use pwa
VITE_USE_PWA = false
VITE_USE_PWA=false
# Is it compatible with older browsers
VITE_LEGACY = false

View File

@@ -1,14 +1,14 @@
import { defHttp } from '/@/utils/http/axios'
import { getMenuListResultModel } from './model/menuModel'
import { getMenuListResultModel, MenuAndResource } from './model/menuModel'
import { Result } from '/#/axios'
enum Api {
GetMenuList = '/getMenuList',
}
/**
* @description: Get user menu based on id
* 获取菜单和权限码
*/
export const getMenuList = () => {
return defHttp.get<getMenuListResultModel>({ url: Api.GetMenuList })
export const getPermissions = (clientCode: string) => {
return defHttp.get<Result<MenuAndResource>>({ url: '/role/menu/getPermissions', params: { clientCode } })
}

View File

@@ -14,3 +14,18 @@ export interface RouteItem {
* @description: Get menu return value
*/
export type getMenuListResultModel = RouteItem[]
/**
* 用户菜单及资源权限返回类
*/
export interface MenuAndResource {
// 权限码
resourcePerms: Array<string>
// 菜单
menus: Array<PermMenu>
}
/**
* 权限菜单
*/
export interface PermMenu {}

View File

@@ -41,6 +41,7 @@ export function usePermission() {
* @param id
*/
async function resume() {
console.log('重置和重新获得权限资源信息')
const tabStore = useMultipleTabStore()
tabStore.clearCacheTabs()
resetRouter()

View File

@@ -1,13 +1,11 @@
<template>
<Footer :class="prefixCls" v-if="getShowLayoutFooter" ref="footerRef">
<div :class="`${prefixCls}__links`">
<a @click="openWindow(SITE_URL)">{{ t('layout.footer.onlinePreview') }}</a>
<GithubFilled @click="openWindow(GITHUB_URL)" :class="`${prefixCls}__github`" />
<a @click="openWindow(DOC_URL)">{{ t('layout.footer.onlineDocument') }}</a>
</div>
<div>Copyright &copy;2021 Bootx</div>
<!-- <div :class="`${prefixCls}__links`">-->
<!-- <a @click="openWindow(SITE_URL)">{{ t('layout.footer.onlinePreview') }}</a>-->
<!-- <GithubFilled @click="openWindow(GITHUB_URL)" :class="`${prefixCls}__github`" />-->
<!-- <a @click="openWindow(DOC_URL)">{{ t('layout.footer.onlineDocument') }}</a>-->
<!-- </div>-->
<div>Copyright &copy;2020 Bootx出品</div>
</Footer>
</template>

View File

@@ -46,7 +46,7 @@ function createPageGuard(router: Router) {
})
}
// Used to handle page loading status
// 用于处理页面加载状态
function createPageLoadingGuard(router: Router) {
const userStore = useUserStoreWithOut()
const appStore = useAppStoreWithOut()

View File

@@ -32,16 +32,15 @@ export function createParamMenuGuard(router: Router) {
next()
})
}
const getPermissionMode = () => {
const appStore = useAppStoreWithOut()
return appStore.getProjectConfig.permissionMode
}
// 后端模式
const isBackMode = () => {
return getPermissionMode() === PermissionModeEnum.BACK
}
// 路由映射
const isRouteMappingMode = () => {
return getPermissionMode() === PermissionModeEnum.ROUTE_MAPPING
}

View File

@@ -15,6 +15,10 @@ const ROOT_PATH = RootRoute.path
const whitePathList: PageEnum[] = [LOGIN_PATH]
/**
* 路由守卫
* @param router
*/
export function createPermissionGuard(router: Router) {
const userStore = useUserStoreWithOut()
const permissionStore = usePermissionStoreWithOut()
@@ -22,6 +26,7 @@ export function createPermissionGuard(router: Router) {
if (
from.path === ROOT_PATH &&
to.path === PageEnum.BASE_HOME &&
// TODO 没有用户首页配置这个字段
userStore.getUserInfo.homePath &&
userStore.getUserInfo.homePath !== PageEnum.BASE_HOME
) {
@@ -94,7 +99,8 @@ export function createPermissionGuard(router: Router) {
next()
return
}
console.log(`路由守卫`)
// 重载菜单
const routes = await permissionStore.buildRoutesAction()
routes.forEach((route) => {

View File

@@ -41,7 +41,9 @@ export function transformMenuModule(menuModule: MenuModule): Menu {
return menuList[0]
}
// 将路由转换成菜单
/**
* 将路由转换成菜单
*/
export function transformRouteToMenu(routeModList: AppRouteModule[], routerMapping = false) {
// 借助 lodash 深拷贝
const cloneRouteModList = cloneDeep(routeModList)

View File

@@ -64,8 +64,9 @@ function dynamicImport(dynamicViewsModules: Record<string, () => Promise<Recorda
}
}
// Turn background objects into routing objects
// 将背景对象变成路由对象
/**
* 将后端对象变成路由对象
*/
export function transformObjToRoute<T = AppRouteModule>(routeList: AppRouteModule[]): T[] {
routeList.forEach((route) => {
const component = route.component as string

View File

@@ -25,7 +25,7 @@ export const router = createRouter({
scrollBehavior: () => ({ left: 0, top: 0 }),
})
// reset router
// 重置路由
export function resetRouter() {
router.getRoutes().forEach((route) => {
const { name } = route

View File

@@ -1,4 +1,4 @@
// Used to configure the general configuration of some components without modifying the components
// 用于配置某些组件的通用配置,而无需修改组件
import type { SorterResult } from '../components/Table'

View File

@@ -12,95 +12,94 @@ import {
import { SIDE_BAR_BG_COLOR_LIST, HEADER_PRESET_BG_COLOR_LIST } from './designSetting'
import { primaryColor } from '../../build/config/themeConfig'
// ! You need to clear the browser cache after the change
// ! 更改后需要清除浏览器缓存
const setting: ProjectConfig = {
// Whether to show the configuration button
// 是否显示配置按钮
showSettingButton: true,
// Whether to show the theme switch button
// 是否显示主题切换按钮
showDarkModeToggle: true,
// `Settings` button position
// `Settings` 按钮位置
settingButtonPosition: SettingButtonPositionEnum.AUTO,
// Permission mode
permissionMode: PermissionModeEnum.ROUTE_MAPPING,
// 权限模式
permissionMode: PermissionModeEnum.BACK,
// Permission-related cache is stored in sessionStorage or localStorage
// 权限相关缓存存储在sessionStoragelocalStorage
permissionCacheType: CacheTypeEnum.LOCAL,
// Session timeout processing
// 会话超时处理
sessionTimeoutProcessing: SessionTimeoutProcessingEnum.ROUTE_JUMP,
// color
// 颜色
themeColor: primaryColor,
// Website gray mode, open for possible mourning dates
// 灰度迷失
grayMode: false,
// Color Weakness Mode
// 色日模式
colorWeak: false,
// Whether to cancel the menu, the top, the multi-tab page display, for possible embedded in other systems
// 是否取消菜单,顶部,多选项卡页面显示,对于可能嵌入其他系统
fullContent: false,
// content mode
// 内容模式
contentMode: ContentEnum.FULL,
// Whether to display the logo
// 是否显示Logo
showLogo: true,
// Whether to show footer
// 是否显示页脚
showFooter: false,
// Header configuration
// 页头设置
headerSetting: {
// header bg color
// 背景颜色
bgColor: HEADER_PRESET_BG_COLOR_LIST[0],
// Fixed at the top
// 固定在顶部
fixed: true,
// Whether to show top
// 是否显示
show: true,
// theme
// 主题
theme: ThemeEnum.LIGHT,
// Whether to enable the lock screen function
// 是否启用锁屏功能
useLockPage: true,
// Whether to show the full screen button
// 是否显示全屏按钮
showFullScreen: true,
// Whether to show the document button
// 是否显示文档按钮
showDoc: true,
// Whether to show the notification button
// 是否显示通知按钮
showNotice: true,
// Whether to display the menu search
// 是否显示搜索按钮
showSearch: true,
},
// Menu configuration
// 菜单配置
menuSetting: {
// sidebar menu bg color
// 背景颜色
bgColor: SIDE_BAR_BG_COLOR_LIST[0],
// Whether to fix the left menu
// 是否固定
fixed: true,
// Menu collapse
collapsed: false,
// When sider hide because of the responsive layout
// 当sider因为响应式布局而隐藏时
siderHidden: false,
// Whether to display the menu name when folding the menu
// 折叠菜单时是否显示菜单名称
collapsedShowTitle: false,
// Whether it can be dragged
// Only limited to the opening of the left menu, the mouse has a drag bar on the right side of the menu
// 是否可以拖动仅限于左侧菜单的打开,鼠标在菜单的右侧有一个拖动条
canDrag: false,
// Whether to show no dom
// Whether to show no dom 是否不显示dom
show: true,
// Whether to show dom
// Whether to show dom 是否隐藏
hidden: false,
// Menu width
// Menu width 宽度
menuWidth: 210,
// Menu mode
// Menu mode 模式
mode: MenuModeEnum.INLINE,
// Menu type
// Menu 类型
type: MenuTypeEnum.SIDEBAR,
// Menu theme
// Menu 主题
theme: ThemeEnum.DARK,
// Split menu
split: false,

View File

@@ -78,6 +78,7 @@ export const useAppStore = defineStore({
},
async resetAllState() {
console.log('重置所有状态')
resetRouter()
Persistent.clearAll()
},

View File

@@ -18,7 +18,7 @@ import { ERROR_LOG_ROUTE, PAGE_NOT_FOUND_ROUTE } from '/@/router/routes/basic'
import { filter } from '/@/utils/helper/treeHelper'
import { getMenuList } from '/@/api/sys/menu'
import { getMenuList, getPermissions } from "/@/api/sys/menu";
import { getPermCode } from '/@/api/sys/user'
import { useMessage } from '/@/hooks/web/useMessage'
@@ -103,14 +103,22 @@ export const usePermissionStore = defineStore({
this.backMenuList = []
this.lastBuildMenuTime = 0
},
async changePermissionCode() {
const codeList = await getPermCode()
this.setPermCodeList(codeList)
/**
* 权限码和菜单更新
*/
async changeMenuAndPermCode() {
const {
data: { menus, resourcePerms },
} = await getPermissions('admin')
console.log(menus)
// this.setBackMenuList(menus)
this.setPermCodeList(resourcePerms)
},
// 构建路由
/**
* 构建路由
*/
async buildRoutesAction(): Promise<AppRouteRecordRaw[]> {
const { t } = useI18n()
const userStore = useUserStore()
const appStore = useAppStoreWithOut()
@@ -141,7 +149,8 @@ export const usePermissionStore = defineStore({
* */
const patchHomeAffix = (routes: AppRouteRecordRaw[]) => {
if (!routes || routes.length === 0) return
let homePath: string = userStore.getUserInfo.homePath || PageEnum.BASE_HOME
// let homePath: string = userStore.getUserInfo.homePath || PageEnum.BASE_HOME
let homePath: string = PageEnum.BASE_HOME
function patcher(routes: AppRouteRecordRaw[], parentPath = '') {
if (parentPath) parentPath = parentPath + '/'
@@ -180,7 +189,7 @@ export const usePermissionStore = defineStore({
routes = flatMultiLevelRoutes(routes)
break
// 路由映射, 默认进入该case
// 路由映射, 默认进入该case(不用了)
case PermissionModeEnum.ROUTE_MAPPING:
// 对非一级路由进行过滤
routes = filter(asyncRoutes, routeFilter)
@@ -196,7 +205,6 @@ export const usePermissionStore = defineStore({
menuList.sort((a, b) => {
return (a.meta?.orderNo || 0) - (b.meta?.orderNo || 0)
})
// 设置菜单列表
this.setFrontMenuList(menuList)
@@ -205,38 +213,34 @@ export const usePermissionStore = defineStore({
routes = flatMultiLevelRoutes(routes)
break
// If you are sure that you do not need to do background dynamic permissions, please comment the entire judgment below
// 如果确定不需要做后台动态权限,请在下方评论整个判断
// 后端控制 现在在用的方案
case PermissionModeEnum.BACK:
const { createMessage } = useMessage()
createMessage.loading({
content: t('sys.app.menuLoading'),
content: '菜单加载中...',
duration: 1,
})
// !Simulate to obtain permission codes from the background,
// 模拟从后台获取权限码,
// this function may only need to be executed once, and the actual project can be put at the right time by itself
// 这个功能可能只需要执行一次,实际项目可以自己放在合适的时间
let routeList: AppRouteRecordRaw[] = []
// 获取菜单和权限码
try {
await this.changePermissionCode()
routeList = (await getMenuList()) as AppRouteRecordRaw[]
await this.changeMenuAndPermCode()
// 获取菜单列表
routeList = this.backMenuList as any
} catch (error) {
console.error(error)
}
// Dynamically introduce components
// 动态引入组件
console.log(routeList)
routeList = transformObjToRoute(routeList)
// Background routing to menu structure
// 后台路由到菜单结构
const backMenuList = transformRouteToMenu(routeList)
this.setBackMenuList(backMenuList)
// remove meta.ignoreRoute item
// 删除 meta.ignoreRoute 项
routeList = filter(routeList, routeRemoveIgnoreFilter)
routeList = routeList.filter(routeRemoveIgnoreFilter)

View File

@@ -107,12 +107,13 @@ export const useUserStore = defineStore({
this.setSessionTimeout(false)
} else {
const permissionStore = usePermissionStore()
console.log(permissionStore)
if (!permissionStore.isDynamicAddedRoute) {
// 构建路由
const routes = await permissionStore.buildRoutesAction()
routes.forEach((route) => {
router.addRoute(route as unknown as RouteRecordRaw)
})
// 404路由
router.addRoute(PAGE_NOT_FOUND_ROUTE as unknown as RouteRecordRaw)
permissionStore.setDynamicAddedRoute(true)
}

View File

@@ -10,13 +10,13 @@
<a-spin :spinning="confirmLoading">
<a-form class="small-from-item" ref="formRef" :label-col="labelCol" :wrapper-col="wrapperCol">
<a-form-item label="主键" :hidden="true">
<Input v-model:value="form.id" :disabled="showable" />
<a-input v-model:value="form.id" :disabled="showable" />
</a-form-item>
<a-form-item label="编码" v-bind="validateInfos.code" name="code">
<Input v-model:value="form.code" :disabled="showable" @blur="validate('code')" placeholder="请输入编码" />
<a-input v-model:value="form.code" :disabled="showable" @blur="validate('code')" placeholder="请输入编码" />
</a-form-item>
<a-form-item label="名称" v-bind="validateInfos.name" name="name">
<Input v-model:value="form.name" :disabled="showable" @blur="validate('name')" placeholder="请输入名称" />
<a-input v-model:value="form.name" :disabled="showable" @blur="validate('name')" placeholder="请输入名称" />
</a-form-item>
<a-form-item label="启用状态" v-bind="validateInfos.enable" name="enable">
<a-switch checked-children="" un-checked-children="" v-model:checked="form.enable" :disabled="showable || form.system" />

View File

@@ -10,13 +10,13 @@
<a-spin :spinning="confirmLoading">
<a-form class="small-from-item" ref="formRef" :model="form" :rules="rules" :label-col="labelCol" :wrapper-col="wrapperCol">
<a-form-item label="主键" name="id" :hidden="true">
<Input v-model:value="form.id" :disabled="showable" />
<a-input v-model:value="form.id" :disabled="showable" />
</a-form-item>
<a-form-item label="编码" name="code">
<Input v-model:value="form.code" :disabled="showable" placeholder="请输入登录方式编码" />
<a-input v-model:value="form.code" :disabled="showable" placeholder="请输入登录方式编码" />
</a-form-item>
<a-form-item label="名称" name="name">
<Input v-model:value="form.name" :disabled="showable" placeholder="请输入登录方式名称" />
<a-input v-model:value="form.name" :disabled="showable" placeholder="请输入登录方式名称" />
</a-form-item>
<a-form-item label="类型" name="type">
<a-radio-group v-model:value="form.type" button-style="solid">
@@ -35,7 +35,7 @@
<a-switch checked-children="" un-checked-children="" v-model:checked="form.captcha" :disabled="showable" />
</a-form-item>
<a-form-item label="超时时间(分钟)" name="timeout">
<Input-number
<a-input-number
v-model:value="form.timeout"
:disabled="showable"
:precision="0"
@@ -45,7 +45,7 @@
/>
</a-form-item>
<a-form-item label="密码可错误次数" name="pwdErrNum" v-show="form.type === PASSWORD">
<Input-number
<a-input-number
v-model:value="form.pwdErrNum"
:disabled="showable"
:min="-1"