feat: Add dynamic route

This commit is contained in:
kailong321200875 2022-02-19 20:34:44 +08:00
parent d5b6e2a777
commit 9d926b2760
15 changed files with 851 additions and 66 deletions

475
mock/role/index.ts Normal file
View File

@ -0,0 +1,475 @@
import { config } from '@/config/axios/config'
import { MockMethod } from 'vite-plugin-mock'
const { result_code } = config
const timeout = 1000
const adminList = [
{
path: '/dashboard',
component: '#',
redirect: '/dashboard/analysis',
name: 'Dashboard',
meta: {
title: 'router.dashboard',
icon: 'ant-design:dashboard-filled',
alwaysShow: true
},
children: [
{
path: 'analysis',
component: 'views/Dashboard/Analysis',
name: 'Analysis',
meta: {
title: 'router.analysis',
noCache: true
}
},
{
path: 'workplace',
component: 'views/Dashboard/Workplace',
name: 'Workplace',
meta: {
title: 'router.workplace',
noCache: true
}
}
]
},
{
path: '/guide',
component: '#',
name: 'Guide',
meta: {},
children: [
{
path: 'index',
component: 'views/Guide/Guide',
name: 'GuideDemo',
meta: {
title: 'router.guide',
icon: 'cib:telegram-plane'
}
}
]
},
{
path: '/components',
component: '#',
redirect: '/components/icon',
name: 'ComponentsDemo',
meta: {
title: 'router.component',
icon: 'bx:bxs-component',
alwaysShow: true
},
children: [
{
path: 'form',
component: '##',
name: 'Form',
meta: {
title: 'router.form',
alwaysShow: true
},
children: [
{
path: 'default-form',
component: 'views/Components/Form/DefaultForm',
name: 'DefaultForm',
meta: {
title: 'router.defaultForm'
}
},
{
path: 'use-form',
component: 'views/Components/Form/UseFormDemo',
name: 'UseForm',
meta: {
title: 'UseForm'
}
},
{
path: 'ref-form',
component: 'views/Components/Form/RefForm',
name: 'RefForm',
meta: {
title: 'RefForm'
}
}
]
},
{
path: 'table',
component: '##',
name: 'TableDemo',
meta: {
title: 'router.table',
alwaysShow: true
},
children: [
{
path: 'default-table',
component: 'views/Components/Table/DefaultTable',
name: 'DefaultTable',
meta: {
title: 'router.defaultTable'
}
},
{
path: 'use-table',
component: 'views/Components/Table/UseTableDemo',
name: 'UseTable',
meta: {
title: 'UseTable'
}
},
{
path: 'ref-table',
component: 'views/Components/Table/RefTable',
name: 'RefTable',
meta: {
title: 'RefTable'
}
}
]
},
{
path: 'editor-demo',
component: '##',
name: 'EditorDemo',
meta: {
title: 'router.editor',
alwaysShow: true
},
children: [
{
path: 'editor',
component: 'views/Components/Editor/Editor',
name: 'Editor',
meta: {
title: 'router.richText'
}
}
]
},
{
path: 'search',
component: 'views/Components/Search',
name: 'Search',
meta: {
title: 'router.search'
}
},
{
path: 'descriptions',
component: 'views/Components/Descriptions',
name: 'Descriptions',
meta: {
title: 'router.descriptions'
}
},
{
path: 'image-viewer',
component: 'views/Components/ImageViewer',
name: 'ImageViewer',
meta: {
title: 'router.imageViewer'
}
},
{
path: 'dialog',
component: 'views/Components/Dialog',
name: 'Dialog',
meta: {
title: 'router.dialog'
}
},
{
path: 'icon',
component: 'views/Components/Icon',
name: 'Icon',
meta: {
title: 'router.icon'
}
},
{
path: 'echart',
component: 'views/Components/Echart',
name: 'Echart',
meta: {
title: 'router.echart'
}
},
{
path: 'count-to',
component: 'views/Components/CountTo',
name: 'CountTo',
meta: {
title: 'router.countTo'
}
},
{
path: 'watermark',
component: 'views/Components/Watermark',
name: 'Watermark',
meta: {
title: 'router.watermark'
}
},
{
path: 'qrcode',
component: 'views/Components/Qrcode',
name: 'Qrcode',
meta: {
title: 'router.qrcode'
}
},
{
path: 'highlight',
component: 'views/Components/Highlight',
name: 'Highlight',
meta: {
title: 'router.highlight'
}
},
{
path: 'infotip',
component: 'views/Components/Infotip',
name: 'Infotip',
meta: {
title: 'router.infotip'
}
}
]
},
{
path: '/level',
component: '#',
redirect: '/level/menu1/menu1-1/menu1-1-1',
name: 'Level',
meta: {
title: 'router.level',
icon: 'carbon:skill-level-advanced'
},
children: [
{
path: 'menu1',
name: 'Menu1',
component: '##',
redirect: '/level/menu1/menu1-1/menu1-1-1',
meta: {
title: 'router.menu1'
},
children: [
{
path: 'menu1-1',
name: 'Menu11',
component: '##',
redirect: '/level/menu1/menu1-1/menu1-1-1',
meta: {
title: 'router.menu11',
alwaysShow: true
},
children: [
{
path: 'menu1-1-1',
name: 'Menu111',
component: 'views/Level/Menu111',
meta: {
title: 'router.menu111'
}
}
]
},
{
path: 'menu1-2',
name: 'Menu12',
component: 'views/Level/Menu12',
meta: {
title: 'router.menu12'
}
}
]
},
{
path: 'menu2',
name: 'Menu2Demo',
component: 'views/Level/Menu2',
meta: {
title: 'router.menu2'
}
}
]
},
{
path: '/example',
component: '#',
redirect: '/example/example-dialog',
name: 'Example',
meta: {
title: 'router.example',
icon: 'ep:management',
alwaysShow: true
},
children: [
{
path: 'example-dialog',
component: 'views/Example/Dialog/ExampleDialog',
name: 'ExampleDialog',
meta: {
title: 'router.exampleDialog'
}
},
{
path: 'example-page',
component: 'views/Example/Page/ExamplePage',
name: 'ExamplePage',
meta: {
title: 'router.examplePage'
}
},
{
path: 'example-add',
component: 'views/Example/Page/ExampleAdd',
name: 'ExampleAdd',
meta: {
title: 'router.exampleAdd',
noTagsView: true,
noCache: true,
hidden: true,
showMainRoute: true,
activeMenu: '/example/example-page'
}
},
{
path: 'example-edit',
component: 'views/Example/Page/ExampleEdit',
name: 'ExampleEdit',
meta: {
title: 'router.exampleEdit',
noTagsView: true,
noCache: true,
hidden: true,
showMainRoute: true,
activeMenu: '/example/example-page'
}
},
{
path: 'example-detail',
component: 'views/Example/Page/ExampleDetail',
name: 'ExampleDetail',
meta: {
title: 'router.exampleDetail',
noTagsView: true,
noCache: true,
hidden: true,
showMainRoute: true,
activeMenu: '/example/example-page'
}
}
]
},
{
path: '/error',
component: '#',
redirect: '/error/404',
name: 'Error',
meta: {
title: 'router.errorPage',
icon: 'ci:error',
alwaysShow: true
},
children: [
{
path: '404-demo',
component: 'views/Error/404',
name: '404Demo',
meta: {
title: '404'
}
},
{
path: '403-demo',
component: 'views/Error/403',
name: '403Demo',
meta: {
title: '403'
}
},
{
path: '500-demo',
component: 'views/Error/500',
name: '500Demo',
meta: {
title: '500'
}
}
]
}
]
const testList: string[] = [
'/dashboard',
'/dashboard/analysis',
'/dashboard/workplace',
'/guide',
'/guide/index',
'/components',
'/components/form',
'/components/form/default-form',
'/components/form/use-form',
'/components/form/ref-form',
'/components/table',
'/components/table/default-table',
'/components/table/use-table',
'/components/table/ref-table',
'/components/editor-demo',
'/components/editor-demo/editor',
'/components/search',
'/components/descriptions',
'/components/image-viewer',
'/components/dialog',
'/components/icon',
'/components/echart',
'/components/count-to',
'/components/watermark',
'/components/qrcode',
'/components/highlight',
'/components/infotip',
'/level',
'/level/menu1',
'/level/menu1/menu1-1',
'/level/menu1/menu1-1/menu1-1-1',
'/level/menu1/menu1-2',
'/level/menu2',
'/example',
'/example/example-dialog',
'/example/example-page',
'/example/example-add',
'/example/example-edit',
'/example/example-detail',
'/error',
'/error/404-demo',
'/error/403-demo',
'/error/500-demo'
]
export default [
// 列表接口
{
url: '/role/list',
method: 'get',
timeout,
response: ({ query }) => {
const { roleName } = query
return {
code: result_code,
data: {
list: roleName === 'admin' ? adminList : testList
}
}
}
}
] as MockMethod[]

View File

@ -26,6 +26,30 @@ const List: {
] ]
export default [ export default [
// 列表接口
{
url: '/user/list',
method: 'get',
response: ({ query }) => {
const { username, pageIndex, pageSize } = query
const mockList = List.filter((item) => {
if (username && item.username.indexOf(username) < 0) return false
return true
})
const pageList = mockList.filter(
(_, index) => index < pageSize * pageIndex && index >= pageSize * (pageIndex - 1)
)
return {
code: result_code,
data: {
total: mockList.length,
list: pageList
}
}
}
},
// 登录接口 // 登录接口
{ {
url: '/user/login', url: '/user/login',

View File

@ -1,5 +1,5 @@
import { useAxios } from '@/hooks/web/useAxios' import { useAxios } from '@/hooks/web/useAxios'
import type { UserLoginType } from './types' import type { UserLoginType, UserType } from './types'
const { request } = useAxios() const { request } = useAxios()
@ -13,3 +13,22 @@ export const loginApi = (data: UserLoginType) => {
export const loginOutApi = () => { export const loginOutApi = () => {
return request({ url: '/user/loginOut', method: 'get' }) return request({ url: '/user/loginOut', method: 'get' })
} }
export const getUserListApi = ({ params }: AxiosConfig) => {
return request<{
total: number
list: UserType[]
}>({ url: '/user/list', method: 'get', params })
}
export const getAdminRoleApi = ({ params }: AxiosConfig) => {
return request<{
list: AppCustomRouteRecordRaw[]
}>({ url: '/role/list', method: 'get', params })
}
export const getTestRoleApi = ({ params }: AxiosConfig) => {
return request<{
list: string[]
}>({ url: '/role/list', method: 'get', params })
}

View File

@ -2,3 +2,10 @@ export type UserLoginType = {
username: string username: string
password: string password: string
} }
export type UserType = {
username: string
password: string
role: string
roleId: string
}

View File

@ -126,7 +126,10 @@ export default {
exampleAdd: 'Example page - add', exampleAdd: 'Example page - add',
exampleEdit: 'Example page - edit', exampleEdit: 'Example page - edit',
exampleDetail: 'Example page - detail', exampleDetail: 'Example page - detail',
errorPage: 'Error page' errorPage: 'Error page',
authorization: 'Authorization',
user: 'User management',
role: 'Role management'
}, },
analysis: { analysis: {
newUser: 'New user', newUser: 'New user',
@ -393,5 +396,18 @@ export default {
content: 'Content', content: 'Content',
save: 'Save', save: 'Save',
detail: 'Detail' detail: 'Detail'
},
userDemo: {
title: 'User management',
message:
'Because it is simulated data, only two accounts with different permissions are provided, which can be modified and combined by developers according to the actual situation.',
index: 'Index',
action: 'Action',
username: 'Username',
password: 'Password',
role: 'Role',
remark: 'Remark',
remarkMessage1: 'Back end control routing permission',
remarkMessage2: 'Front end control routing permission'
} }
} }

View File

@ -126,7 +126,10 @@ export default {
exampleAdd: '综合示例 - 新增', exampleAdd: '综合示例 - 新增',
exampleEdit: '综合示例 - 编辑', exampleEdit: '综合示例 - 编辑',
exampleDetail: '综合示例 - 详情', exampleDetail: '综合示例 - 详情',
errorPage: '错误页面' errorPage: '错误页面',
authorization: '权限管理',
user: '用户管理',
role: '角色管理'
}, },
analysis: { analysis: {
newUser: '新增用户', newUser: '新增用户',
@ -390,5 +393,17 @@ export default {
content: '内容', content: '内容',
save: '保存', save: '保存',
detail: '详情' detail: '详情'
},
userDemo: {
title: '用户管理',
message: '由于是模拟数据,所以只提供了两种不同权限的帐号,开发者可根据实际情况自行改造结合。',
index: '序号',
action: '操作',
username: '用户名',
password: '密码',
role: '角色',
remark: '备注',
remarkMessage1: '后端控制路由权限',
remarkMessage2: '前端控制路由权限'
} }
} }

View File

@ -30,7 +30,15 @@ router.beforeEach(async (to, from, next) => {
to.path === '/' ? next({ path: permissionStore.addRouters[0]?.path as string }) : next() to.path === '/' ? next({ path: permissionStore.addRouters[0]?.path as string }) : next()
return return
} }
await permissionStore.generateRoutes()
// 开发者可根据实际情况进行修改
const roleRouters = wsCache.get('roleRouters') || []
const userInfo = wsCache.get(appStore.getUserInfo)
userInfo.role === 'admin'
? await permissionStore.generateRoutes('admin', roleRouters as AppCustomRouteRecordRaw[])
: await permissionStore.generateRoutes('test', roleRouters as string[])
permissionStore.getAddRouters.forEach((route) => { permissionStore.getAddRouters.forEach((route) => {
router.addRoute(route as unknown as RouteRecordRaw) // 动态添加可访问路由表 router.addRoute(route as unknown as RouteRecordRaw) // 动态添加可访问路由表
}) })

View File

@ -424,31 +424,60 @@ export const asyncRouterMap: AppRouteRecordRaw[] = [
}, },
children: [ children: [
{ {
path: '404', path: '404-demo',
component: () => import('@/views/Error/404.vue'), component: () => import('@/views/Error/404.vue'),
name: '404', name: '404Demo',
meta: { meta: {
title: '404' title: '404'
} }
}, },
{ {
path: '403', path: '403-demo',
component: () => import('@/views/Error/403.vue'), component: () => import('@/views/Error/403.vue'),
name: '403', name: '403Demo',
meta: { meta: {
title: '403' title: '403'
} }
}, },
{ {
path: '500', path: '500-demo',
component: () => import('@/views/Error/500.vue'), component: () => import('@/views/Error/500.vue'),
name: '500', name: '500Demo',
meta: { meta: {
title: '500' title: '500'
} }
} }
] ]
} }
// {
// path: '/authorization',
// component: Layout,
// redirect: '/authorization/user',
// name: 'Authorization',
// meta: {
// title: t('router.authorization'),
// icon: 'eos-icons:role-binding',
// alwaysShow: true
// },
// children: [
// {
// path: 'user',
// component: () => import('@/views/Authorization/User.vue'),
// name: 'User',
// meta: {
// title: t('router.user')
// }
// },
// {
// path: 'role',
// component: () => import('@/views/Authorization/Role.vue'),
// name: 'Role',
// meta: {
// title: t('router.role')
// }
// }
// ]
// }
] ]
const router = createRouter({ const router = createRouter({
@ -459,7 +488,7 @@ const router = createRouter({
}) })
export const resetRouter = (): void => { export const resetRouter = (): void => {
const resetWhiteNameList = ['RedirectRoot', 'Redirect', 'Login', 'Root', 'Dashboard', 'Page404'] const resetWhiteNameList = ['Redirect', 'Login', 'NoFind']
router.getRoutes().forEach((route) => { router.getRoutes().forEach((route) => {
const { name } = route const { name } = route
if (name && !resetWhiteNameList.includes(name as string)) { if (name && !resetWhiteNameList.includes(name as string)) {

View File

@ -1,16 +1,9 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { asyncRouterMap, constantRouterMap } from '@/router' import { asyncRouterMap, constantRouterMap } from '@/router'
// import { useCache } from '@/hooks/web/useCache' import { generateRoutesFn1, generateRoutesFn2, flatMultiLevelRoutes } from '@/utils/routerHelper'
import { flatMultiLevelRoutes } from '@/utils/routerHelper'
// import { generateRoutesFn1, generateRoutesFn2, flatMultiLevelRoutes } from '@/utils/routerHelper'
import { store } from '../index' import { store } from '../index'
// import { useAppStoreWithOut } from '@/store/modules/app'
import { cloneDeep } from 'lodash-es' import { cloneDeep } from 'lodash-es'
// const { wsCache } = useCache()
// const appStore = useAppStoreWithOut()
export interface PermissionState { export interface PermissionState {
routers: AppRouteRecordRaw[] routers: AppRouteRecordRaw[]
addRouters: AppRouteRecordRaw[] addRouters: AppRouteRecordRaw[]
@ -44,21 +37,23 @@ export const usePermissionStore = defineStore({
} }
}, },
actions: { actions: {
generateRoutes(): Promise<unknown> { generateRoutes(
type: 'admin' | 'test' | 'none',
routers?: AppCustomRouteRecordRaw[] | string[]
): Promise<unknown> {
return new Promise<void>((resolve) => { return new Promise<void>((resolve) => {
// 路由权限控制,如果不需要权限控制,请注释 let routerMap: AppRouteRecordRaw[] = []
// let routerMap: AppRouteRecordRaw[] = [] if (type === 'admin') {
// if (wsCache.get(appStore.getUserInfo).username === 'admin') { // 模拟后端过滤菜单
// // 模拟前端控制权限 routerMap = generateRoutesFn2(routers as AppCustomRouteRecordRaw[])
// routerMap = generateRoutesFn1(cloneDeep(asyncRouterMap)) } else if (type === 'test') {
// } else { // 模拟前端过滤菜单
// // 模拟后端控制权限 routerMap = generateRoutesFn1(cloneDeep(asyncRouterMap), routers as string[])
// routerMap = generateRoutesFn2(wsCache.get(appStore.getUserInfo).checkedNodes) } else {
// } // 直接读取静态路由表
routerMap = cloneDeep(asyncRouterMap)
// 不需要权限控制 }
const routerMap: AppRouteRecordRaw[] = cloneDeep(asyncRouterMap) console.log(routerMap)
// 动态路由404一定要放到最后面 // 动态路由404一定要放到最后面
this.addRouters = routerMap.concat([ this.addRouters = routerMap.concat([
{ {

View File

@ -16,9 +16,6 @@ export const useTagsViewStore = defineStore({
visitedViews: [], visitedViews: [],
cachedViews: new Set() cachedViews: new Set()
}), }),
persist: {
enabled: true
},
getters: { getters: {
getVisitedViews(): RouteLocationNormalizedLoaded[] { getVisitedViews(): RouteLocationNormalizedLoaded[] {
return this.visitedViews return this.visitedViews

View File

@ -1,15 +1,9 @@
import { createRouter, createWebHashHistory } from 'vue-router' import { createRouter, createWebHashHistory } from 'vue-router'
import type { Router, RouteLocationNormalized, RouteRecordNormalized, RouteMeta } from 'vue-router' import type { Router, RouteLocationNormalized, RouteRecordNormalized, RouteMeta } from 'vue-router'
import { isUrl } from '@/utils/is' import { isUrl } from '@/utils/is'
import { useCache } from '@/hooks/web/useCache'
import { useAppStoreWithOut } from '@/store/modules/app'
import { omit, cloneDeep } from 'lodash-es' import { omit, cloneDeep } from 'lodash-es'
const appStore = useAppStoreWithOut() const modules = import.meta.glob('../views/**/*.{vue,tsx}')
const { wsCache } = useCache()
const modules = import.meta.glob('../../views/**/*.{vue,tsx}')
/* Layout */ /* Layout */
export const Layout = () => import('@/layout/Layout.vue') export const Layout = () => import('@/layout/Layout.vue')
@ -41,6 +35,7 @@ export const getRawRoute = (route: RouteLocationNormalized): RouteLocationNormal
// 前端控制路由生成 // 前端控制路由生成
export const generateRoutesFn1 = ( export const generateRoutesFn1 = (
routes: AppRouteRecordRaw[], routes: AppRouteRecordRaw[],
keys: string[],
basePath = '/' basePath = '/'
): AppRouteRecordRaw[] => { ): AppRouteRecordRaw[] => {
const res: AppRouteRecordRaw[] = [] const res: AppRouteRecordRaw[] = []
@ -55,7 +50,6 @@ export const generateRoutesFn1 = (
let data: Nullable<AppRouteRecordRaw> = null let data: Nullable<AppRouteRecordRaw> = null
let onlyOneChild: Nullable<string> = null let onlyOneChild: Nullable<string> = null
if (route.children && route.children.length === 1 && !meta.alwaysShow) { if (route.children && route.children.length === 1 && !meta.alwaysShow) {
onlyOneChild = ( onlyOneChild = (
isUrl(route.children[0].path) isUrl(route.children[0].path)
@ -64,16 +58,14 @@ export const generateRoutesFn1 = (
) as string ) as string
} }
// 权限过滤,通过获取登录信息里面的角色权限,动态的渲染菜单。
const list = wsCache.get(appStore.getUserInfo).checkedNodes
// 开发者可以根据实际情况进行扩展 // 开发者可以根据实际情况进行扩展
for (const item of list) { for (const item of keys) {
// 通过路径去匹配 // 通过路径去匹配
if (isUrl(item.path) && (onlyOneChild === item.path || route.path === item.path)) { if (isUrl(item) && (onlyOneChild === item || route.path === item)) {
data = Object.assign({}, route) data = Object.assign({}, route)
} else { } else {
const routePath = pathResolve(basePath, onlyOneChild || route.path) const routePath = pathResolve(basePath, onlyOneChild || route.path)
if (routePath === item.path || meta.followRoute === item.path) { if (routePath === item || meta.followRoute === item) {
data = Object.assign({}, route) data = Object.assign({}, route)
} }
} }
@ -81,7 +73,7 @@ export const generateRoutesFn1 = (
// recursive child routes // recursive child routes
if (route.children && data) { if (route.children && data) {
data.children = generateRoutesFn1(route.children, pathResolve(basePath, data.path)) data.children = generateRoutesFn1(route.children, keys, pathResolve(basePath, data.path))
} }
if (data) { if (data) {
res.push(data as AppRouteRecordRaw) res.push(data as AppRouteRecordRaw)
@ -91,7 +83,7 @@ export const generateRoutesFn1 = (
} }
// 后端控制路由生成 // 后端控制路由生成
export const generateRoutesFn2 = (routes: AppRouteRecordRaw[]): AppRouteRecordRaw[] => { export const generateRoutesFn2 = (routes: AppCustomRouteRecordRaw[]): AppRouteRecordRaw[] => {
const res: AppRouteRecordRaw[] = [] const res: AppRouteRecordRaw[] = []
for (const route of routes) { for (const route of routes) {
@ -102,15 +94,14 @@ export const generateRoutesFn2 = (routes: AppRouteRecordRaw[]): AppRouteRecordRa
meta: route.meta meta: route.meta
} }
if (route.component) { if (route.component) {
const comModule = const comModule = modules[`../${route.component}.vue`] || modules[`../${route.component}.tsx`]
modules[`../../${route.component}.vue`] || modules[`../../${route.component}.tsx`] const component = route.component as string
if (comModule) { if (!comModule && !component.includes('#')) {
console.error(`未找到${route.component}.vue文件或${route.component}.tsx文件请创建`)
} else {
// 动态加载路由文件,可根据实际情况进行自定义逻辑 // 动态加载路由文件,可根据实际情况进行自定义逻辑
const component = route.component as string
data.component = data.component =
component === '#' ? Layout : component.includes('##') ? getParentLayout() : comModule component === '#' ? Layout : component.includes('##') ? getParentLayout() : comModule
} else {
console.error(`未找到${route.component}.vue文件或${route.component}.tsx文件请创建`)
} }
} }
// recursive child routes // recursive child routes
@ -124,7 +115,7 @@ export const generateRoutesFn2 = (routes: AppRouteRecordRaw[]): AppRouteRecordRa
export const pathResolve = (parentPath: string, path: string) => { export const pathResolve = (parentPath: string, path: string) => {
const childPath = path.startsWith('/') || !path ? path : `/${path}` const childPath = path.startsWith('/') || !path ? path : `/${path}`
return `${parentPath}${childPath}` return `${parentPath}${childPath}`.replace(/\/\//g, '/')
} }
// 路由降级 // 路由降级

View File

@ -0,0 +1,88 @@
<script setup lang="ts">
import { ContentWrap } from '@/components/ContentWrap'
import { useI18n } from '@/hooks/web/useI18n'
import { Table } from '@/components/Table'
import { getUserListApi } from '@/api/login'
import { UserType } from '@/api/login/types'
import { ref, h } from 'vue'
import { ElButton } from 'element-plus'
interface Params {
pageIndex?: number
pageSize?: number
}
const { t } = useI18n()
const columns: TableColumn[] = [
{
field: 'index',
label: t('userDemo.index'),
type: 'index'
},
{
field: 'username',
label: t('userDemo.username')
},
{
field: 'password',
label: t('userDemo.password')
},
{
field: 'role',
label: t('userDemo.role')
},
{
field: 'remark',
label: t('userDemo.remark'),
formatter: (row: UserType) => {
return h(
'span',
row.username === 'admin' ? t('userDemo.remarkMessage1') : t('userDemo.remarkMessage2')
)
}
},
{
field: 'action',
label: t('userDemo.action')
}
]
const loading = ref(true)
let tableDataList = ref<UserType[]>([])
const getTableList = async (params?: Params) => {
const res = await getUserListApi({
params: params || {
pageIndex: 1,
pageSize: 10
}
})
.catch(() => {})
.finally(() => {
loading.value = false
})
if (res) {
tableDataList.value = res.data.list
}
}
getTableList()
const acitonFn = (data: TableSlotDefault) => {
console.log(data)
}
</script>
<template>
<ContentWrap :title="t('userDemo.title')" :message="t('userDemo.message')">
<Table :columns="columns" :data="tableDataList" :loading="loading" :selection="false">
<template #action="data">
<ElButton type="primary" @click="acitonFn(data as TableSlotDefault)">
{{ t('tableDemo.action') }}
</ElButton>
</template>
</Table>
</ContentWrap>
</template>

View File

@ -0,0 +1,88 @@
<script setup lang="ts">
import { ContentWrap } from '@/components/ContentWrap'
import { useI18n } from '@/hooks/web/useI18n'
import { Table } from '@/components/Table'
import { getUserListApi } from '@/api/login'
import { UserType } from '@/api/login/types'
import { ref, h } from 'vue'
import { ElButton } from 'element-plus'
interface Params {
pageIndex?: number
pageSize?: number
}
const { t } = useI18n()
const columns: TableColumn[] = [
{
field: 'index',
label: t('userDemo.index'),
type: 'index'
},
{
field: 'username',
label: t('userDemo.username')
},
{
field: 'password',
label: t('userDemo.password')
},
{
field: 'role',
label: t('userDemo.role')
},
{
field: 'remark',
label: t('userDemo.remark'),
formatter: (row: UserType) => {
return h(
'span',
row.username === 'admin' ? t('userDemo.remarkMessage1') : t('userDemo.remarkMessage2')
)
}
},
{
field: 'action',
label: t('userDemo.action')
}
]
const loading = ref(true)
let tableDataList = ref<UserType[]>([])
const getTableList = async (params?: Params) => {
const res = await getUserListApi({
params: params || {
pageIndex: 1,
pageSize: 10
}
})
.catch(() => {})
.finally(() => {
loading.value = false
})
if (res) {
tableDataList.value = res.data.list
}
}
getTableList()
const acitonFn = (data: TableSlotDefault) => {
console.log(data)
}
</script>
<template>
<ContentWrap :title="t('userDemo.title')" :message="t('userDemo.message')">
<Table :columns="columns" :data="tableDataList" :loading="loading" :selection="false">
<template #action="data">
<ElButton type="primary" @click="acitonFn(data as TableSlotDefault)">
{{ t('tableDemo.action') }}
</ElButton>
</template>
</Table>
</ContentWrap>
</template>

View File

@ -5,7 +5,7 @@ import { useI18n } from '@/hooks/web/useI18n'
import { ElButton, ElCheckbox, ElLink } from 'element-plus' import { ElButton, ElCheckbox, ElLink } from 'element-plus'
import { required } from '@/utils/formRules' import { required } from '@/utils/formRules'
import { useForm } from '@/hooks/web/useForm' import { useForm } from '@/hooks/web/useForm'
import { loginApi } from '@/api/login' import { loginApi, getTestRoleApi, getAdminRoleApi } from '@/api/login'
import type { UserLoginType } from '@/api/login/types' import type { UserLoginType } from '@/api/login/types'
import { useCache } from '@/hooks/web/useCache' import { useCache } from '@/hooks/web/useCache'
import { useAppStore } from '@/store/modules/app' import { useAppStore } from '@/store/modules/app'
@ -126,17 +126,41 @@ const signIn = async () => {
if (res) { if (res) {
const { wsCache } = useCache() const { wsCache } = useCache()
wsCache.set(appStore.getUserInfo, res.data) wsCache.set(appStore.getUserInfo, res.data)
await permissionStore.generateRoutes().catch(() => {})
permissionStore.getAddRouters.forEach((route) => { getRole()
addRoute(route as RouteRecordRaw) // 访
})
permissionStore.setIsAddRouters(true)
// push({ path: redirect.value || permissionStore.addRouters[0].path })
push({ path: permissionStore.addRouters[0].path })
} }
} }
} }
//
const getRole = async () => {
const { getFormData } = methods
const formData = await getFormData<UserLoginType>()
const params = {
roleName: formData.username
}
// admin -
// test -
const res =
formData.username === 'admin'
? await getAdminRoleApi({ params })
: await getTestRoleApi({ params })
if (res) {
const { wsCache } = useCache()
const routers = res.data.list || []
wsCache.set('roleRouters', routers)
formData.username === 'admin'
? await permissionStore.generateRoutes('admin', routers).catch(() => {})
: await permissionStore.generateRoutes('test', routers).catch(() => {})
permissionStore.getAddRouters.forEach((route) => {
addRoute(route as RouteRecordRaw) // 访
})
permissionStore.setIsAddRouters(true)
push({ path: redirect.value || permissionStore.addRouters[0].path })
}
}
</script> </script>
<template> <template>

9
types/router.d.ts vendored
View File

@ -62,4 +62,13 @@ declare global {
props?: Recordable props?: Recordable
fullPath?: string fullPath?: string
} }
declare interface AppCustomRouteRecordRaw extends Omit<RouteRecordRaw, 'meta'> {
name: string
meta: RouteMeta
component: string
path: string
redirect: string
children?: AppCustomRouteRecordRaw[]
}
} }