feat: 🎸 权限管理开发中
This commit is contained in:
parent
8edb2a3493
commit
6d7ea6694d
|
@ -1,8 +0,0 @@
|
|||
module.exports = {
|
||||
'*.{js,jsx,ts,tsx}': ['eslint --fix', 'prettier --write'],
|
||||
'{!(package)*.json,*.code-snippets,.!(browserslist)*rc}': ['prettier --write--parser json'],
|
||||
'package.json': ['prettier --write'],
|
||||
'*.vue': ['prettier --write', 'stylelint --fix'],
|
||||
'*.{scss,less,styl,css,html}': ['stylelint --fix', 'prettier --write'],
|
||||
'*.md': ['prettier --write']
|
||||
}
|
|
@ -2,9 +2,13 @@ import Mock from 'mockjs'
|
|||
import { param2Obj } from '@/utils'
|
||||
|
||||
import example from './example'
|
||||
import user from './user'
|
||||
import role from './role'
|
||||
|
||||
const mocks: any[] = [
|
||||
...example
|
||||
...example,
|
||||
...user,
|
||||
...role
|
||||
]
|
||||
|
||||
// for front mock
|
||||
|
|
|
@ -0,0 +1,420 @@
|
|||
export const checkedNodes = [{
|
||||
'path': '/components-demo',
|
||||
'title': '功能组件',
|
||||
'name': 'ComponentsDemo',
|
||||
'children': [{
|
||||
'path': '/components-demo/echarts',
|
||||
'title': '图表',
|
||||
'name': 'EchartsDemo'
|
||||
}, {
|
||||
'path': '/components-demo/preview',
|
||||
'title': '图片预览',
|
||||
'name': 'PreviewDemo'
|
||||
}, {
|
||||
'path': '/components-demo/button',
|
||||
'title': '按钮',
|
||||
'name': 'ButtonDemo'
|
||||
}, {
|
||||
'path': '/components-demo/message',
|
||||
'title': '消息提示',
|
||||
'name': 'MessageDemo'
|
||||
}, {
|
||||
'path': '/components-demo/count-to',
|
||||
'title': '数字动画',
|
||||
'name': 'CountToDemo'
|
||||
}, {
|
||||
'path': '/components-demo/search',
|
||||
'title': '查询',
|
||||
'name': 'SearchDemo'
|
||||
}, {
|
||||
'path': '/components-demo/editor',
|
||||
'title': '富文本编辑器',
|
||||
'name': 'EditorDemo'
|
||||
}, {
|
||||
'path': '/components-demo/markdown',
|
||||
'title': 'markdown编辑器',
|
||||
'name': 'MarkdownDemo'
|
||||
}, {
|
||||
'path': '/components-demo/dialog',
|
||||
'title': '弹窗',
|
||||
'name': 'DialogDemo'
|
||||
}, {
|
||||
'path': '/components-demo/more',
|
||||
'title': '显示更多',
|
||||
'name': 'MoreDemo'
|
||||
}, {
|
||||
'path': '/components-demo/detail',
|
||||
'title': '详情组件',
|
||||
'name': 'DetailDemo'
|
||||
}]
|
||||
}, {
|
||||
'path': '/components-demo/echarts',
|
||||
'title': '图表',
|
||||
'name': 'EchartsDemo'
|
||||
}, {
|
||||
'path': '/components-demo/preview',
|
||||
'title': '图片预览',
|
||||
'name': 'PreviewDemo'
|
||||
}, {
|
||||
'path': '/components-demo/button',
|
||||
'title': '按钮',
|
||||
'name': 'ButtonDemo'
|
||||
}, {
|
||||
'path': '/components-demo/message',
|
||||
'title': '消息提示',
|
||||
'name': 'MessageDemo'
|
||||
}, {
|
||||
'path': '/components-demo/count-to',
|
||||
'title': '数字动画',
|
||||
'name': 'CountToDemo'
|
||||
}, {
|
||||
'path': '/components-demo/search',
|
||||
'title': '查询',
|
||||
'name': 'SearchDemo'
|
||||
}, {
|
||||
'path': '/components-demo/editor',
|
||||
'title': '富文本编辑器',
|
||||
'name': 'EditorDemo'
|
||||
}, {
|
||||
'path': '/components-demo/markdown',
|
||||
'title': 'markdown编辑器',
|
||||
'name': 'MarkdownDemo'
|
||||
}, {
|
||||
'path': '/components-demo/dialog',
|
||||
'title': '弹窗',
|
||||
'name': 'DialogDemo'
|
||||
}, {
|
||||
'path': '/components-demo/more',
|
||||
'title': '显示更多',
|
||||
'name': 'MoreDemo'
|
||||
}, {
|
||||
'path': '/components-demo/detail',
|
||||
'title': '详情组件',
|
||||
'name': 'DetailDemo'
|
||||
}, {
|
||||
'path': '/table-demo',
|
||||
'title': '表格',
|
||||
'name': 'TableDemo',
|
||||
'children': [{
|
||||
'path': '/table-demo/basic-table',
|
||||
'title': '基础表格',
|
||||
'name': 'BasicTable'
|
||||
}, {
|
||||
'path': '/table-demo/page-table',
|
||||
'title': '分页表格',
|
||||
'name': 'PageTable'
|
||||
}, {
|
||||
'path': '/table-demo/stripe-table',
|
||||
'title': '带斑马纹表格',
|
||||
'name': 'StripeTable'
|
||||
}, {
|
||||
'path': '/table-demo/border-table',
|
||||
'title': '带边框表格',
|
||||
'name': 'BorderTable'
|
||||
}, {
|
||||
'path': '/table-demo/state-table',
|
||||
'title': '带状态表格',
|
||||
'name': 'StateTable'
|
||||
}, {
|
||||
'path': '/table-demo/fixed-header',
|
||||
'title': '固定表头',
|
||||
'name': 'FixedHeader'
|
||||
}, {
|
||||
'path': '/table-demo/fixed-column',
|
||||
'title': '固定列',
|
||||
'name': 'FixedColumn'
|
||||
}, {
|
||||
'path': '/table-demo/fixed-column-header',
|
||||
'title': '固定列和表头',
|
||||
'name': 'FixedColumnHeader'
|
||||
}, {
|
||||
'path': '/table-demo/fluid-height',
|
||||
'title': '流体高度',
|
||||
'name': 'FluidHeight'
|
||||
}, {
|
||||
'path': '/table-demo/multi-header',
|
||||
'title': '多级表头',
|
||||
'name': 'MultiHeader'
|
||||
}, {
|
||||
'path': '/table-demo/single-choice',
|
||||
'title': '单选',
|
||||
'name': 'SingleChoice'
|
||||
}, {
|
||||
'path': '/table-demo/multiple-choice',
|
||||
'title': '多选',
|
||||
'name': 'MultipleChoice'
|
||||
}, {
|
||||
'path': '/table-demo/sort-table',
|
||||
'title': '排序',
|
||||
'name': 'SortTable'
|
||||
}, {
|
||||
'path': '/table-demo/screen-table',
|
||||
'title': '筛选',
|
||||
'name': 'ScreenTable'
|
||||
}, {
|
||||
'path': '/table-demo/expand-row',
|
||||
'title': '展开行',
|
||||
'name': 'ExpandRow'
|
||||
}, {
|
||||
'path': '/table-demo/tree-and-load',
|
||||
'title': '树形数据与懒加载',
|
||||
'name': 'TreeAndLoad'
|
||||
}, {
|
||||
'path': '/table-demo/custom-header',
|
||||
'title': '自定义表头',
|
||||
'name': 'CustomHeader'
|
||||
}, {
|
||||
'path': '/table-demo/total-table',
|
||||
'title': '表尾合计行',
|
||||
'name': 'TotalTable'
|
||||
}, {
|
||||
'path': '/table-demo/merge-table',
|
||||
'title': '合并行或列',
|
||||
'name': 'MergeTable'
|
||||
}, {
|
||||
'path': '/table-demo/custom-index',
|
||||
'title': '自定义索引',
|
||||
'name': 'CustomIndex'
|
||||
}]
|
||||
}, {
|
||||
'path': '/table-demo/basic-table',
|
||||
'title': '基础表格',
|
||||
'name': 'BasicTable'
|
||||
}, {
|
||||
'path': '/table-demo/page-table',
|
||||
'title': '分页表格',
|
||||
'name': 'PageTable'
|
||||
}, {
|
||||
'path': '/table-demo/stripe-table',
|
||||
'title': '带斑马纹表格',
|
||||
'name': 'StripeTable'
|
||||
}, {
|
||||
'path': '/table-demo/border-table',
|
||||
'title': '带边框表格',
|
||||
'name': 'BorderTable'
|
||||
}, {
|
||||
'path': '/table-demo/state-table',
|
||||
'title': '带状态表格',
|
||||
'name': 'StateTable'
|
||||
}, {
|
||||
'path': '/table-demo/fixed-header',
|
||||
'title': '固定表头',
|
||||
'name': 'FixedHeader'
|
||||
}, {
|
||||
'path': '/table-demo/fixed-column',
|
||||
'title': '固定列',
|
||||
'name': 'FixedColumn'
|
||||
}, {
|
||||
'path': '/table-demo/fixed-column-header',
|
||||
'title': '固定列和表头',
|
||||
'name': 'FixedColumnHeader'
|
||||
}, {
|
||||
'path': '/table-demo/fluid-height',
|
||||
'title': '流体高度',
|
||||
'name': 'FluidHeight'
|
||||
}, {
|
||||
'path': '/table-demo/multi-header',
|
||||
'title': '多级表头',
|
||||
'name': 'MultiHeader'
|
||||
}, {
|
||||
'path': '/table-demo/single-choice',
|
||||
'title': '单选',
|
||||
'name': 'SingleChoice'
|
||||
}, {
|
||||
'path': '/table-demo/multiple-choice',
|
||||
'title': '多选',
|
||||
'name': 'MultipleChoice'
|
||||
}, {
|
||||
'path': '/table-demo/sort-table',
|
||||
'title': '排序',
|
||||
'name': 'SortTable'
|
||||
}, {
|
||||
'path': '/table-demo/screen-table',
|
||||
'title': '筛选',
|
||||
'name': 'ScreenTable'
|
||||
}, {
|
||||
'path': '/table-demo/expand-row',
|
||||
'title': '展开行',
|
||||
'name': 'ExpandRow'
|
||||
}, {
|
||||
'path': '/table-demo/tree-and-load',
|
||||
'title': '树形数据与懒加载',
|
||||
'name': 'TreeAndLoad'
|
||||
}, {
|
||||
'path': '/table-demo/custom-header',
|
||||
'title': '自定义表头',
|
||||
'name': 'CustomHeader'
|
||||
}, {
|
||||
'path': '/table-demo/total-table',
|
||||
'title': '表尾合计行',
|
||||
'name': 'TotalTable'
|
||||
}, {
|
||||
'path': '/table-demo/merge-table',
|
||||
'title': '合并行或列',
|
||||
'name': 'MergeTable'
|
||||
}, {
|
||||
'path': '/table-demo/custom-index',
|
||||
'title': '自定义索引',
|
||||
'name': 'CustomIndex'
|
||||
}, {
|
||||
'path': '/directives-demo',
|
||||
'title': '自定义指令',
|
||||
'name': 'DirectivesDemo',
|
||||
'children': [{
|
||||
'path': '/directives-demo/clipboard',
|
||||
'title': 'Clipboard',
|
||||
'name': 'ClipboardDemo'
|
||||
}]
|
||||
}, {
|
||||
'path': '/directives-demo/clipboard',
|
||||
'title': 'Clipboard',
|
||||
'name': 'ClipboardDemo'
|
||||
}, {
|
||||
'path': '/hooks-demo',
|
||||
'title': 'Hooks',
|
||||
'name': 'HooksDemo',
|
||||
'children': [{
|
||||
'path': '/hooks-demo/watermark',
|
||||
'title': 'UseWaterMark',
|
||||
'name': 'UseWatermarkDemo'
|
||||
}, {
|
||||
'path': '/hooks-demo/useScrollTo',
|
||||
'title': 'UseScrollTo',
|
||||
'name': 'UseScrollToDemo'
|
||||
}]
|
||||
}, {
|
||||
'path': '/hooks-demo/watermark',
|
||||
'title': 'UseWaterMark',
|
||||
'name': 'UseWatermarkDemo'
|
||||
}, {
|
||||
'path': '/hooks-demo/useScrollTo',
|
||||
'title': 'UseScrollTo',
|
||||
'name': 'UseScrollToDemo'
|
||||
}, {
|
||||
'path': '/icon/index',
|
||||
'title': '图标',
|
||||
'name': 'Icons'
|
||||
}, {
|
||||
'path': '/level',
|
||||
'title': '多级菜单缓存',
|
||||
'name': 'Level',
|
||||
'children': [{
|
||||
'path': '/level/menu1',
|
||||
'title': 'Menu1',
|
||||
'name': 'Menu1Demo',
|
||||
'children': [{
|
||||
'path': '/level/menu1/menu1-1',
|
||||
'title': 'Menu1-1',
|
||||
'name': 'Menu11Demo',
|
||||
'children': [{
|
||||
'path': '/level/menu1/menu1-1/menu1-1-1',
|
||||
'title': 'Menu1-1-1',
|
||||
'name': 'Menu111Demo'
|
||||
}]
|
||||
}, {
|
||||
'path': '/level/menu1/menu1-2',
|
||||
'title': 'Menu1-2',
|
||||
'name': 'Menu12Demo'
|
||||
}]
|
||||
}, {
|
||||
'path': '/level/menu2',
|
||||
'title': 'Menu2',
|
||||
'name': 'Menu2Demo'
|
||||
}]
|
||||
}, {
|
||||
'path': '/level/menu1',
|
||||
'title': 'Menu1',
|
||||
'name': 'Menu1Demo',
|
||||
'children': [{
|
||||
'path': '/level/menu1/menu1-1',
|
||||
'title': 'Menu1-1',
|
||||
'name': 'Menu11Demo',
|
||||
'children': [{
|
||||
'path': '/level/menu1/menu1-1/menu1-1-1',
|
||||
'title': 'Menu1-1-1',
|
||||
'name': 'Menu111Demo'
|
||||
}]
|
||||
}, {
|
||||
'path': '/level/menu1/menu1-2',
|
||||
'title': 'Menu1-2',
|
||||
'name': 'Menu12Demo'
|
||||
}]
|
||||
}, {
|
||||
'path': '/level/menu1/menu1-1',
|
||||
'title': 'Menu1-1',
|
||||
'name': 'Menu11Demo',
|
||||
'children': [{
|
||||
'path': '/level/menu1/menu1-1/menu1-1-1',
|
||||
'title': 'Menu1-1-1',
|
||||
'name': 'Menu111Demo'
|
||||
}]
|
||||
}, {
|
||||
'path': '/level/menu1/menu1-1/menu1-1-1',
|
||||
'title': 'Menu1-1-1',
|
||||
'name': 'Menu111Demo'
|
||||
}, {
|
||||
'path': '/level/menu1/menu1-2',
|
||||
'title': 'Menu1-2',
|
||||
'name': 'Menu12Demo'
|
||||
}, {
|
||||
'path': '/level/menu2',
|
||||
'title': 'Menu2',
|
||||
'name': 'Menu2Demo'
|
||||
}, {
|
||||
'path': '/example-demo',
|
||||
'title': '综合实例',
|
||||
'name': 'ExampleDemo',
|
||||
'children': [{
|
||||
'path': '/example-demo/example-dialog',
|
||||
'title': '列表综合实例-弹窗',
|
||||
'name': 'ExampleDialog'
|
||||
}, {
|
||||
'path': '/example-demo/example-page',
|
||||
'title': '列表综合实例-页面',
|
||||
'name': 'ExamplePage'
|
||||
}]
|
||||
}, {
|
||||
'path': '/example-demo/example-dialog',
|
||||
'title': '列表综合实例-弹窗',
|
||||
'name': 'ExampleDialog'
|
||||
}, {
|
||||
'path': '/example-demo/example-page',
|
||||
'title': '列表综合实例-页面',
|
||||
'name': 'ExamplePage'
|
||||
}, {
|
||||
'path': '/role-demo',
|
||||
'title': '权限管理',
|
||||
'name': 'RoleDemo',
|
||||
'children': [{
|
||||
'path': '/role-demo/user',
|
||||
'title': '用户管理',
|
||||
'name': 'User'
|
||||
}, {
|
||||
'path': '/role-demo/role',
|
||||
'title': '角色管理',
|
||||
'name': 'Role'
|
||||
}]
|
||||
}, {
|
||||
'path': '/role-demo/user',
|
||||
'title': '用户管理',
|
||||
'name': 'User'
|
||||
}, {
|
||||
'path': '/role-demo/role',
|
||||
'title': '角色管理',
|
||||
'name': 'Role'
|
||||
}]
|
||||
|
||||
export const checkedkeys = ['/components-demo', '/components-demo/echarts', '/components-demo/preview',
|
||||
'/components-demo/button', '/components-demo/message', '/components-demo/count-to', '/components-demo/search',
|
||||
'/components-demo/editor', '/components-demo/markdown', '/components-demo/dialog', '/components-demo/more',
|
||||
'/components-demo/detail', '/table-demo', '/table-demo/basic-table', '/table-demo/page-table',
|
||||
'/table-demo/stripe-table', '/table-demo/border-table', '/table-demo/state-table', '/table-demo/fixed-header',
|
||||
'/table-demo/fixed-column', '/table-demo/fixed-column-header', '/table-demo/fluid-height',
|
||||
'/table-demo/multi-header', '/table-demo/single-choice', '/table-demo/multiple-choice', '/table-demo/sort-table',
|
||||
'/table-demo/screen-table', '/table-demo/expand-row', '/table-demo/tree-and-load', '/table-demo/custom-header',
|
||||
'/table-demo/total-table', '/table-demo/merge-table', '/table-demo/custom-index', '/directives-demo',
|
||||
'/directives-demo/clipboard', '/hooks-demo', '/hooks-demo/watermark', '/hooks-demo/useScrollTo', '/icon/index',
|
||||
'/level', '/level/menu1', '/level/menu1/menu1-1', '/level/menu1/menu1-1/menu1-1-1', '/level/menu1/menu1-2',
|
||||
'/level/menu2', '/example-demo', '/example-demo/example-dialog', '/example-demo/example-page', '/role-demo',
|
||||
'/role-demo/user', '/role-demo/role'
|
||||
]
|
|
@ -0,0 +1,97 @@
|
|||
import wsCache from '@/cache'
|
||||
import { Role } from './types'
|
||||
import { checkedNodes, checkedkeys } from './admin-role'
|
||||
import { checkedRoleNodes } from './test-role'
|
||||
|
||||
let List: Role[] = wsCache.get('roleList') || [
|
||||
{
|
||||
roleName: 'admin',
|
||||
id: '1',
|
||||
checkedNodes: checkedNodes,
|
||||
checkedkeys: checkedkeys
|
||||
},
|
||||
{
|
||||
roleName: 'test',
|
||||
id: '2',
|
||||
checkedNodes: checkedRoleNodes,
|
||||
checkedkeys: []
|
||||
}
|
||||
]
|
||||
|
||||
export default [
|
||||
// 列表接口
|
||||
{
|
||||
url: 'http://mockjs.test.cn/role/list',
|
||||
type: 'get',
|
||||
response: (config: any) => {
|
||||
const {
|
||||
roleName,
|
||||
pageIndex,
|
||||
pageSize
|
||||
} = config.query
|
||||
|
||||
const mockList = List.filter(item => {
|
||||
if (roleName && item.roleName.indexOf(roleName) < 0) return false
|
||||
return true
|
||||
})
|
||||
const pageList = mockList.filter((item, index) => index < pageSize * pageIndex && index >= pageSize * (pageIndex - 1))
|
||||
|
||||
return {
|
||||
code: '0000',
|
||||
data: {
|
||||
total: mockList.length,
|
||||
list: pageList
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 详情接口
|
||||
{
|
||||
url: 'http://mockjs.test.cn/role/detail',
|
||||
type: 'get',
|
||||
response: (config: any) => {
|
||||
const {
|
||||
id
|
||||
} = config.query
|
||||
for (const role of List) {
|
||||
if (role.id === id) {
|
||||
return {
|
||||
code: '0000',
|
||||
data: role
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 保存接口
|
||||
{
|
||||
url: 'http://mockjs.test.cn/role/save',
|
||||
type: 'post',
|
||||
response: (config: any) => {
|
||||
const data = config.body
|
||||
if (!data.id) {
|
||||
List = [data].concat(List)
|
||||
return {
|
||||
code: '0000',
|
||||
data: 'success'
|
||||
}
|
||||
} else {
|
||||
List.map(item => {
|
||||
if (item.id === data.id) {
|
||||
for (const key in item) {
|
||||
item[key] = data[key]
|
||||
}
|
||||
}
|
||||
})
|
||||
// 存在缓存中,避免刷新没有掉
|
||||
wsCache.set('roleList', List)
|
||||
return {
|
||||
code: '0000',
|
||||
data: 'success'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
|
@ -0,0 +1,489 @@
|
|||
export const checkedRoleNodes = [{
|
||||
path: '/components-demo',
|
||||
component: '#',
|
||||
redirect: '/components-demo/echarts',
|
||||
name: 'ComponentsDemo',
|
||||
meta: {
|
||||
title: '功能组件',
|
||||
icon: 'component',
|
||||
alwaysShow: true
|
||||
},
|
||||
children: [{
|
||||
path: 'echarts',
|
||||
component: 'pages/index/views/components-demo/echarts/index.vue',
|
||||
name: 'EchartsDemo',
|
||||
meta: {
|
||||
title: '图表'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'preview',
|
||||
component: 'pages/index/views/components-demo/preview/index.vue',
|
||||
name: 'PreviewDemo',
|
||||
meta: {
|
||||
title: '图片预览'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'button',
|
||||
component: 'pages/index/views/components-demo/button/index.vue',
|
||||
name: 'ButtonDemo',
|
||||
meta: {
|
||||
title: '按钮'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'message',
|
||||
component: 'pages/index/views/components-demo/message/index.vue',
|
||||
name: 'MessageDemo',
|
||||
meta: {
|
||||
title: '消息提示'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'count-to',
|
||||
component: 'pages/index/views/components-demo/count-to/index.vue',
|
||||
name: 'CountToDemo',
|
||||
meta: {
|
||||
title: '数字动画'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'search',
|
||||
component: 'pages/index/views/components-demo/search/index.vue',
|
||||
name: 'SearchDemo',
|
||||
meta: {
|
||||
title: '查询'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'editor',
|
||||
component: 'pages/index/views/components-demo/editor/index.vue',
|
||||
name: 'EditorDemo',
|
||||
meta: {
|
||||
title: '富文本编辑器'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'markdown',
|
||||
component: 'pages/index/views/components-demo/markdown/index.vue',
|
||||
name: 'MarkdownDemo',
|
||||
meta: {
|
||||
title: 'markdown编辑器'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'dialog',
|
||||
component: 'pages/index/views/components-demo/dialog/index.vue',
|
||||
name: 'DialogDemo',
|
||||
meta: {
|
||||
title: '弹窗'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'more',
|
||||
component: 'pages/index/views/components-demo/more/index.vue',
|
||||
name: 'MoreDemo',
|
||||
meta: {
|
||||
title: '显示更多'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'detail',
|
||||
component: 'pages/index/views/components-demo/detail/index.vue',
|
||||
name: 'DetailDemo',
|
||||
meta: {
|
||||
title: '详情组件'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/table-demo',
|
||||
component: '#',
|
||||
redirect: '/table-demo/basic-usage',
|
||||
name: 'TableDemo',
|
||||
meta: {
|
||||
title: '表格',
|
||||
icon: 'table',
|
||||
alwaysShow: true
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'basic-table',
|
||||
component: 'pages/index/views/table-demo/basic-table/index.vue',
|
||||
name: 'BasicTable',
|
||||
meta: {
|
||||
title: '基础表格'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'page-table',
|
||||
component: 'pages/index/views/table-demo/page-table/index.vue',
|
||||
name: 'PageTable',
|
||||
meta: {
|
||||
title: '分页表格'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'stripe-table',
|
||||
component: 'pages/index/views/table-demo/stripe-table/index.vue',
|
||||
name: 'StripeTable',
|
||||
meta: {
|
||||
title: '带斑马纹表格'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'border-table',
|
||||
component: 'pages/index/views/table-demo/border-table/index.vue',
|
||||
name: 'BorderTable',
|
||||
meta: {
|
||||
title: '带边框表格'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'state-table',
|
||||
component: 'pages/index/views/table-demo/state-table/index.vue',
|
||||
name: 'StateTable',
|
||||
meta: {
|
||||
title: '带状态表格'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'fixed-header',
|
||||
component: 'pages/index/views/table-demo/fixed-header/index.vue',
|
||||
name: 'FixedHeader',
|
||||
meta: {
|
||||
title: '固定表头'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'fixed-column',
|
||||
component: 'pages/index/views/table-demo/fixed-column/index.vue',
|
||||
name: 'FixedColumn',
|
||||
meta: {
|
||||
title: '固定列'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'fixed-column-header',
|
||||
component: 'pages/index/views/table-demo/fixed-column-header/index.vue',
|
||||
name: 'FixedColumnHeader',
|
||||
meta: {
|
||||
title: '固定列和表头'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'fluid-height',
|
||||
component: 'pages/index/views/table-demo/fluid-height/index.vue',
|
||||
name: 'FluidHeight',
|
||||
meta: {
|
||||
title: '流体高度'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'multi-header',
|
||||
component: 'pages/index/views/table-demo/multi-header/index.vue',
|
||||
name: 'MultiHeader',
|
||||
meta: {
|
||||
title: '多级表头'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'single-choice',
|
||||
component: 'pages/index/views/table-demo/single-choice/index.vue',
|
||||
name: 'SingleChoice',
|
||||
meta: {
|
||||
title: '单选'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'multiple-choice',
|
||||
component: 'pages/index/views/table-demo/multiple-choice/index.vue',
|
||||
name: 'MultipleChoice',
|
||||
meta: {
|
||||
title: '多选'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'sort-table',
|
||||
component: 'pages/index/views/table-demo/sort-table/index.vue',
|
||||
name: 'SortTable',
|
||||
meta: {
|
||||
title: '排序'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'screen-table',
|
||||
component: 'pages/index/views/table-demo/screen-table/index.vue',
|
||||
name: 'ScreenTable',
|
||||
meta: {
|
||||
title: '筛选'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'expand-row',
|
||||
component: 'pages/index/views/table-demo/expand-row/index.vue',
|
||||
name: 'ExpandRow',
|
||||
meta: {
|
||||
title: '展开行'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'tree-and-load',
|
||||
component: 'pages/index/views/table-demo/tree-and-load/index.vue',
|
||||
name: 'TreeAndLoad',
|
||||
meta: {
|
||||
title: '树形数据与懒加载'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'custom-header',
|
||||
component: 'pages/index/views/table-demo/custom-header/index.vue',
|
||||
name: 'CustomHeader',
|
||||
meta: {
|
||||
title: '自定义表头'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'total-table',
|
||||
component: 'pages/index/views/table-demo/total-table/index.vue',
|
||||
name: 'TotalTable',
|
||||
meta: {
|
||||
title: '表尾合计行'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'merge-table',
|
||||
component: 'pages/index/views/table-demo/merge-table/index.vue',
|
||||
name: 'MergeTable',
|
||||
meta: {
|
||||
title: '合并行或列'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'custom-index',
|
||||
component: 'pages/index/views/table-demo/custom-index/index.vue',
|
||||
name: 'CustomIndex',
|
||||
meta: {
|
||||
title: '自定义索引'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/directives-demo',
|
||||
component: '#',
|
||||
redirect: '/directives-demo/clipboard',
|
||||
name: 'DirectivesDemo',
|
||||
meta: {
|
||||
title: '自定义指令',
|
||||
icon: 'clipboard',
|
||||
alwaysShow: true
|
||||
},
|
||||
children: [{
|
||||
path: 'clipboard',
|
||||
component: 'pages/index/views/directives-demo/clipboard/index.vue',
|
||||
name: 'ClipboardDemo',
|
||||
meta: {
|
||||
title: 'Clipboard'
|
||||
}
|
||||
}]
|
||||
},
|
||||
{
|
||||
path: '/hooks-demo',
|
||||
component: '#',
|
||||
redirect: '/hooks-demo/watermark',
|
||||
name: 'HooksDemo',
|
||||
meta: {
|
||||
title: 'Hooks',
|
||||
icon: 'international',
|
||||
alwaysShow: true
|
||||
},
|
||||
children: [{
|
||||
path: 'watermark',
|
||||
component: 'pages/index/views/hooks-demo/useWatermark/index.vue',
|
||||
name: 'UseWatermarkDemo',
|
||||
meta: {
|
||||
title: 'UseWaterMark'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'useScrollTo',
|
||||
component: 'pages/index/views/hooks-demo/useScrollTo/index.vue',
|
||||
name: 'UseScrollToDemo',
|
||||
meta: {
|
||||
title: 'UseScrollTo'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/icon',
|
||||
component: '#',
|
||||
name: 'IconsDemo',
|
||||
meta: {
|
||||
title: '图标',
|
||||
icon: 'icon'
|
||||
},
|
||||
children: [{
|
||||
path: 'index',
|
||||
component: 'pages/index/views/icons/index.vue',
|
||||
name: 'Icons',
|
||||
meta: {
|
||||
title: '图标',
|
||||
icon: 'icon'
|
||||
}
|
||||
}]
|
||||
},
|
||||
{
|
||||
path: '/level',
|
||||
component: '#',
|
||||
redirect: '/level/menu1/menu1-1/menu1-1-1',
|
||||
name: 'Level',
|
||||
meta: {
|
||||
title: '多级菜单缓存',
|
||||
icon: 'nested'
|
||||
},
|
||||
children: [{
|
||||
path: 'menu1',
|
||||
name: 'Menu1Demo',
|
||||
component: '##Menu1Demo',
|
||||
redirect: '/level/menu1/menu1-1/menu1-1-1',
|
||||
meta: {
|
||||
title: 'Menu1'
|
||||
},
|
||||
children: [{
|
||||
path: 'menu1-1',
|
||||
name: 'Menu11Demo',
|
||||
component: '##Menu11Demo',
|
||||
redirect: '/level/menu1/menu1-1/menu1-1-1',
|
||||
meta: {
|
||||
title: 'Menu1-1',
|
||||
alwaysShow: true
|
||||
},
|
||||
children: [{
|
||||
path: 'menu1-1-1',
|
||||
name: 'Menu111Demo',
|
||||
component: 'pages/index/views/level/Menu111.vue',
|
||||
meta: {
|
||||
title: 'Menu1-1-1'
|
||||
}
|
||||
}]
|
||||
},
|
||||
{
|
||||
path: 'menu1-2',
|
||||
name: 'Menu12Demo',
|
||||
component: 'pages/index/views/level/Menu12.vue',
|
||||
meta: {
|
||||
title: 'Menu1-2'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: 'menu2',
|
||||
name: 'Menu2Demo',
|
||||
component: 'pages/index/views/level/Menu2.vue',
|
||||
meta: {
|
||||
title: 'Menu2'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/example-demo',
|
||||
component: '#',
|
||||
name: 'ExampleDemo',
|
||||
redirect: '/example-demo/example-dialog',
|
||||
meta: {
|
||||
alwaysShow: true,
|
||||
icon: 'example',
|
||||
title: '综合实例'
|
||||
},
|
||||
children: [{
|
||||
path: 'example-dialog',
|
||||
component: 'pages/index/views/example-demo/example-dialog/index.vue',
|
||||
name: 'ExampleDialog',
|
||||
meta: {
|
||||
title: '列表综合实例-弹窗'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'example-page',
|
||||
component: 'pages/index/views/example-demo/example-page/index.vue',
|
||||
name: 'ExamplePage',
|
||||
meta: {
|
||||
title: '列表综合实例-页面'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'example-add',
|
||||
component: 'pages/index/views/example-demo/example-page/example-add.vue',
|
||||
name: 'ExampleAdd',
|
||||
meta: {
|
||||
title: '列表综合实例-新增',
|
||||
noTagsView: true,
|
||||
noCache: true,
|
||||
hidden: true,
|
||||
showMainRoute: true,
|
||||
activeMenu: '/example-demo/example-page'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'example-edit',
|
||||
component: 'pages/index/views/example-demo/example-page/example-edit.vue',
|
||||
name: 'ExampleEdit',
|
||||
meta: {
|
||||
title: '列表综合实例-编辑',
|
||||
noTagsView: true,
|
||||
noCache: true,
|
||||
hidden: true,
|
||||
showMainRoute: true,
|
||||
activeMenu: '/example-demo/example-page'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'example-detail',
|
||||
component: 'pages/index/views/example-demo/example-page/example-detail.vue',
|
||||
name: 'ExampleDetail',
|
||||
meta: {
|
||||
title: '列表综合实例-详情',
|
||||
noTagsView: true,
|
||||
noCache: true,
|
||||
hidden: true,
|
||||
showMainRoute: true,
|
||||
activeMenu: '/example-demo/example-page'
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/role-demo',
|
||||
component: '#',
|
||||
redirect: '/role-demo/user',
|
||||
name: 'RoleDemo',
|
||||
meta: {
|
||||
title: '权限管理',
|
||||
icon: 'user',
|
||||
alwaysShow: true
|
||||
},
|
||||
children: [{
|
||||
path: 'user',
|
||||
component: 'pages/index/views/role-demo/user/index.vue',
|
||||
name: 'User',
|
||||
meta: {
|
||||
title: '用户管理'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'role',
|
||||
component: 'pages/index/views/role-demo/role/index.vue',
|
||||
name: 'Role',
|
||||
meta: {
|
||||
title: '角色管理'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
|
@ -0,0 +1,6 @@
|
|||
export interface Role {
|
||||
roleName: String
|
||||
id: String
|
||||
checkedNodes: any[]
|
||||
checkedkeys: any[]
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
import { User } from './types'
|
||||
|
||||
const List: User[] = [
|
||||
{
|
||||
userName: 'admin',
|
||||
password: 'admin',
|
||||
role: 'admin',
|
||||
roleId: '1'
|
||||
},
|
||||
{
|
||||
userName: 'test',
|
||||
password: 'test',
|
||||
role: 'test',
|
||||
roleId: '2'
|
||||
}
|
||||
]
|
||||
|
||||
export default [
|
||||
// 列表接口
|
||||
{
|
||||
url: 'http://mockjs.test.cn/user/list',
|
||||
type: 'get',
|
||||
response: (config: any) => {
|
||||
const {
|
||||
userName,
|
||||
pageIndex,
|
||||
pageSize
|
||||
} = config.query
|
||||
|
||||
const mockList = List.filter(item => {
|
||||
if (userName && item.userName.indexOf(userName) < 0) return false
|
||||
return true
|
||||
})
|
||||
const pageList = mockList.filter((item, index) => index < pageSize * pageIndex && index >= pageSize * (pageIndex - 1))
|
||||
|
||||
return {
|
||||
code: '0000',
|
||||
data: {
|
||||
total: mockList.length,
|
||||
list: pageList
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 登录接口
|
||||
{
|
||||
url: 'http://mockjs.test.cn/user/login',
|
||||
type: 'post',
|
||||
response: (config: any) => {
|
||||
const data = config.body
|
||||
let hasUser = false
|
||||
for (const user of List) {
|
||||
if (user.userName === data.userName && user.password === data.passWord) {
|
||||
hasUser = true
|
||||
return {
|
||||
code: '0000',
|
||||
data: user
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!hasUser) {
|
||||
return {
|
||||
code: '500',
|
||||
message: '账号或密码错误'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
|
@ -0,0 +1,6 @@
|
|||
export interface User {
|
||||
userName: String
|
||||
password: String
|
||||
role: String
|
||||
roleId: String
|
||||
}
|
|
@ -20,7 +20,7 @@
|
|||
"clipboard": "^2.0.6",
|
||||
"core-js": "^3.6.5",
|
||||
"echarts": "^4.9.0",
|
||||
"element-plus": "1.0.1-beta.14",
|
||||
"element-plus": "1.0.1-beta.26",
|
||||
"highlight.js": "^10.4.0",
|
||||
"lodash-es": "^4.17.15",
|
||||
"mitt": "^2.1.0",
|
||||
|
@ -93,10 +93,5 @@
|
|||
"hooks": {
|
||||
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,vue}": [
|
||||
"vue-cli-service lint"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
# replace default config
|
||||
|
||||
# multipass: true
|
||||
# full: true
|
||||
|
||||
plugins:
|
||||
|
||||
# - name
|
||||
#
|
||||
# or:
|
||||
# - name: false
|
||||
# - name: true
|
||||
#
|
||||
# or:
|
||||
# - name:
|
||||
# param1: 1
|
||||
# param2: 2
|
||||
|
||||
- removeAttrs:
|
||||
attrs:
|
||||
- 'fill'
|
||||
- 'fill-rule'
|
|
@ -33,12 +33,14 @@
|
|||
:class="{'detail__content--flex': !vertical}"
|
||||
>
|
||||
<div class="content__item--label" :style="labelStyleObj">
|
||||
<slot v-if="item.slots && item.slots.title" :name="item.slots.title" :row="item" />
|
||||
<template v-else>{{ item.label }}</template>
|
||||
<slot :name="item.field" :row="item">
|
||||
{{ item.label }}
|
||||
</slot>
|
||||
</div>
|
||||
<div class="content__item--message" :style="messageStyleObj">
|
||||
<slot v-if="item.slots && item.slots.default" :name="item.slots.default" :row="data" />
|
||||
<template v-else>{{ data[item.field] }}</template>
|
||||
<slot :name="`${item.field}Content`" :row="data">
|
||||
{{ data[item.field] }}
|
||||
</slot>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
|
|
|
@ -1,16 +1,33 @@
|
|||
<template>
|
||||
<el-dialog
|
||||
ref="dialogRef"
|
||||
v-bind="getBindValue"
|
||||
:fullscreen="fullscreen"
|
||||
destroy-on-close
|
||||
lock-scroll
|
||||
:close-on-click-modal="false"
|
||||
top="10vh"
|
||||
>
|
||||
<template v-if="slots.title" #title>
|
||||
<slot name="title" />
|
||||
<template #title>
|
||||
<slot name="title">
|
||||
{{ title }}
|
||||
</slot>
|
||||
<svg-icon
|
||||
v-if="showFullscreen"
|
||||
:icon-class="fullscreen ? 'exit-fullscreen' : 'fullscreen'"
|
||||
class-name="dialog__icon"
|
||||
@click="toggleFull"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- 弹窗内容 -->
|
||||
<el-scrollbar class="com-dialog__content">
|
||||
<el-scrollbar
|
||||
:class="fullscreen && slots.footer
|
||||
? 'com-dialog__content--footer'
|
||||
: (fullscreen && !slots.footer
|
||||
? 'com-dialog__content--fullscreen'
|
||||
: 'com-dialog__content')"
|
||||
>
|
||||
<div class="content__wrap">
|
||||
<slot />
|
||||
</div>
|
||||
|
@ -23,24 +40,149 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, computed } from 'vue'
|
||||
import { defineComponent, ref, computed, PropType, nextTick, unref } from 'vue'
|
||||
import SvgIcon from '@/components/SvgIcon/index.vue'
|
||||
export default defineComponent({
|
||||
name: 'Dialog',
|
||||
components: {
|
||||
SvgIcon
|
||||
},
|
||||
props: {
|
||||
title: {
|
||||
type: String as PropType<string>,
|
||||
default: ''
|
||||
},
|
||||
// 是否显示全屏按钮
|
||||
showFullscreen: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: false
|
||||
},
|
||||
// 是否可以拖拽
|
||||
draggable: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
setup(props, { slots, attrs }) {
|
||||
const dialogRef = ref<HTMLElement | null>(null)
|
||||
|
||||
const fullscreen = ref<boolean>(false)
|
||||
|
||||
const getBindValue = computed((): any => {
|
||||
const bindValue = { ...attrs, ...props }
|
||||
return bindValue
|
||||
const delArr: string[] = ['showFullscreen', 'draggable']
|
||||
const obj = { ...attrs, ...props }
|
||||
for (const key in obj) {
|
||||
if (delArr.indexOf(key) !== -1) {
|
||||
delete obj[key]
|
||||
}
|
||||
}
|
||||
return obj
|
||||
})
|
||||
|
||||
function toggleFull(): void {
|
||||
fullscreen.value = !fullscreen.value
|
||||
// 全屏的时候需要重新定义left top
|
||||
if (fullscreen.value && props.draggable) {
|
||||
const dragDom = unref(dialogRef as any).$refs.dialogRef
|
||||
dragDom.style.cssText += `;left:0px;top:0px;`
|
||||
}
|
||||
}
|
||||
|
||||
function initDraggable() {
|
||||
nextTick(() => {
|
||||
const dragDom = unref(dialogRef as any).$refs.dialogRef
|
||||
const dialogHeaderEl = dragDom.querySelector('.el-dialog__header') as HTMLElement
|
||||
dragDom.style.cssText += ';top:0px;'
|
||||
dialogHeaderEl.style.cssText += ';cursor:move;user-select:none;'
|
||||
dialogHeaderEl.onmousedown = (e) => {
|
||||
const disX = e.clientX - dialogHeaderEl.offsetLeft
|
||||
const disY = e.clientY - dialogHeaderEl.offsetTop
|
||||
|
||||
const dragDomWidth = dragDom.offsetWidth
|
||||
const dragDomHeight = dragDom.offsetHeight
|
||||
|
||||
const screenWidth = document.body.clientWidth
|
||||
const screenHeight = document.body.clientHeight
|
||||
|
||||
const minDragDomLeft = dragDom.offsetLeft
|
||||
const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth
|
||||
|
||||
const minDragDomTop = dragDom.offsetTop
|
||||
const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomHeight
|
||||
|
||||
const styleLeftStr = getComputedStyle(dragDom).left
|
||||
const styleTopStr = getComputedStyle(dragDom).top
|
||||
if (!styleLeftStr || !styleTopStr) return
|
||||
let styleLeft: number
|
||||
let styleTop: number
|
||||
|
||||
// Format may be "##%" or "##px"
|
||||
if (styleLeftStr.includes('%')) {
|
||||
styleLeft = +document.body.clientWidth * (+styleLeftStr.replace(/%/g, '') / 100)
|
||||
styleTop = +document.body.clientHeight * (+styleTopStr.replace(/%/g, '') / 100)
|
||||
} else {
|
||||
styleLeft = +styleLeftStr.replace(/px/g, '')
|
||||
styleTop = +styleTopStr.replace(/px/g, '')
|
||||
}
|
||||
|
||||
document.onmousemove = (e) => {
|
||||
let left = e.clientX - disX
|
||||
let top = e.clientY - disY
|
||||
|
||||
// Handle edge cases
|
||||
if (-(left) > minDragDomLeft) {
|
||||
left = -minDragDomLeft
|
||||
} else if (left > maxDragDomLeft) {
|
||||
left = maxDragDomLeft
|
||||
}
|
||||
if (-(top) > minDragDomTop) {
|
||||
top = -minDragDomTop
|
||||
} else if (top > maxDragDomTop) {
|
||||
top = maxDragDomTop
|
||||
}
|
||||
|
||||
// Move current element
|
||||
dragDom.style.cssText += `;left:${left + styleLeft}px;top:${top + styleTop}px;`
|
||||
}
|
||||
|
||||
document.onmouseup = () => {
|
||||
document.onmousemove = null
|
||||
document.onmouseup = null
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (props.draggable) {
|
||||
initDraggable()
|
||||
}
|
||||
|
||||
return {
|
||||
dialogRef,
|
||||
fullscreen,
|
||||
getBindValue,
|
||||
slots
|
||||
slots,
|
||||
toggleFull,
|
||||
initDraggable
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.dialog__icon {
|
||||
position: absolute;
|
||||
top: 22px;
|
||||
right: 45px;
|
||||
color: #909399;
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
cursor: pointer;
|
||||
transition: color 0.2s;
|
||||
&:hover {
|
||||
color: #409EFF;
|
||||
}
|
||||
}
|
||||
.com-dialog__content {
|
||||
.content__wrap {
|
||||
padding-right: 10px;
|
||||
|
@ -50,4 +192,14 @@ export default defineComponent({
|
|||
overflow-x: hidden; // 隐藏横向滚动栏
|
||||
}
|
||||
}
|
||||
.com-dialog__content--fullscreen {
|
||||
@{deep}(.el-scrollbar__wrap) {
|
||||
height: calc(~"100vh - 46px - 60px"); // 最大高度
|
||||
}
|
||||
}
|
||||
.com-dialog__content--footer {
|
||||
@{deep}(.el-scrollbar__wrap) {
|
||||
max-height: calc(~"100vh - 46px - 60px - 66px"); // 最大高度
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -259,7 +259,6 @@ export default defineComponent({
|
|||
try {
|
||||
form.validate((valid: boolean) => {
|
||||
if (valid) {
|
||||
console.log(valid)
|
||||
emit('search-submit', unref(formInline))
|
||||
} else {
|
||||
console.log('error submit!!')
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<i v-if="icon.includes('el-icon')" :class="[icon, 'sub-el-icon', 'anticon']" />
|
||||
<svg-icon v-else :icon-class="icon" class="anticon" />
|
||||
<i v-if="icon && icon.includes('el-icon')" :class="[icon, 'sub-el-icon', 'anticon']" />
|
||||
<svg-icon v-else-if="icon" :icon-class="icon" class="anticon" />
|
||||
<slot name="title">
|
||||
<span class="anticon-item">{{ title }}</span>
|
||||
</slot>
|
||||
|
|
|
@ -34,11 +34,6 @@
|
|||
:index="scope.$index"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- 不需要插槽 -->
|
||||
<!-- <span v-if="!item.slots || !item.slots.default">
|
||||
{{ scope.row[item.field] }}
|
||||
</span> -->
|
||||
</el-table-column>
|
||||
</template>
|
||||
</template>
|
||||
|
|
|
@ -53,10 +53,6 @@
|
|||
:index="scope.$index"
|
||||
/>
|
||||
</template>
|
||||
<!-- 不需要插槽 -->
|
||||
<!-- <span v-if="!item.slots || !item.slots.default">
|
||||
{{ scope.row[item.field] }}
|
||||
</span> -->
|
||||
</el-table-column>
|
||||
</template>
|
||||
</template>
|
||||
|
|
|
@ -25,12 +25,14 @@ import { resetRouter } from '_p/index/router'
|
|||
import wsCache from '@/cache'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { tagsViewStore } from '_p/index/store/modules/tagsView'
|
||||
import { appStore } from '_p/index/store/modules/app'
|
||||
export default defineComponent({
|
||||
name: 'UserInfo',
|
||||
setup() {
|
||||
const { replace, push } = useRouter()
|
||||
async function loginOut(): Promise<void> {
|
||||
wsCache.clear()
|
||||
// wsCache.clear()
|
||||
wsCache.delete(appStore.userInfo)
|
||||
await resetRouter() // 重置静态路由表
|
||||
await tagsViewStore.delAllViews() // 删除所有的tags标签页
|
||||
replace('/login')
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import { EmptyObj } from '@/types/glob'
|
||||
// import { EmptyObj } from '@/types/glob'
|
||||
|
||||
const modulesFiles: any = require.context('./modules', true, /\.ts$/)
|
||||
// const modulesFiles: any = require.context('./modules', true, /\.ts$/)
|
||||
|
||||
const modules: EmptyObj = modulesFiles.keys().reduce((modules: EmptyObj, modulePath: string): Object => {
|
||||
const moduleName: string = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
|
||||
const value: EmptyObj = modulesFiles(modulePath)
|
||||
modules[moduleName] = value.default
|
||||
return modules
|
||||
}, {})
|
||||
// const modules: EmptyObj = modulesFiles.keys().reduce((modules: EmptyObj, modulePath: string): Object => {
|
||||
// const moduleName: string = modulePath.replace(/^\.\/(.*)\.\w+$/, '$1')
|
||||
// const value: EmptyObj = modulesFiles(modulePath)
|
||||
// modules[moduleName] = value.default
|
||||
// return modules
|
||||
// }, {})
|
||||
|
||||
export default {
|
||||
...modules
|
||||
}
|
||||
// export default {
|
||||
// ...modules
|
||||
// }
|
||||
|
|
|
@ -26,6 +26,7 @@ const Layout = () => import('../layout/index.vue')
|
|||
activeMenu: '/dashboard' 显示高亮的路由路径
|
||||
followAuth: '/dashboard' 跟随哪个路由进行权限过滤
|
||||
showMainRoute: true 设置为true即使hidden为true,也依然可以进行路由跳转(默认 false)
|
||||
followRoute: '/dashboard' 为路由设置跟随其他路由的权限
|
||||
}
|
||||
**/
|
||||
|
||||
|
@ -573,6 +574,35 @@ export const asyncRouterMap: AppRouteRecordRaw[] = [
|
|||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/role-demo',
|
||||
component: Layout,
|
||||
redirect: '/role-demo/user',
|
||||
name: 'RoleDemo',
|
||||
meta: {
|
||||
title: '权限管理',
|
||||
icon: 'user',
|
||||
alwaysShow: true
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'user',
|
||||
component: () => import('_p/index/views/role-demo/user/index.vue'),
|
||||
name: 'User',
|
||||
meta: {
|
||||
title: '用户管理'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'role',
|
||||
component: () => import('_p/index/views/role-demo/role/index.vue'),
|
||||
name: 'Role',
|
||||
meta: {
|
||||
title: '角色管理'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ export interface RouteMeta {
|
|||
noTagsView?: boolean
|
||||
followAuth?: string
|
||||
showMainRoute?: boolean
|
||||
followRoute?: string
|
||||
}
|
||||
|
||||
export interface AppRouteRecordRaw extends Omit<RouteRecordRaw, 'meta'> {
|
||||
|
|
|
@ -4,6 +4,15 @@ import { deepClone } from '@/utils'
|
|||
import store from '../index'
|
||||
import { VuexModule, getModule, Module, Mutation, Action } from 'vuex-module-decorators'
|
||||
import { AppRouteRecordRaw } from '_p/index/router/types'
|
||||
import wsCache from '@/cache'
|
||||
import { isExternal } from '@/utils/validate'
|
||||
import path from 'path'
|
||||
import { getParentLayout } from '_p/index/router/utils'
|
||||
|
||||
import { appStore } from '_p/index/store/modules/app'
|
||||
|
||||
/* Layout */
|
||||
const Layout = () => import('_p/index/layout/index.vue')
|
||||
|
||||
export interface PermissionState {
|
||||
routers: AppRouteRecordRaw[]
|
||||
|
@ -19,8 +28,7 @@ class Permission extends VuexModule implements PermissionState {
|
|||
|
||||
@Mutation
|
||||
private SET_ROUTERS(routers: AppRouteRecordRaw[]): void {
|
||||
// const flatRoutes: AppRouteRecordRaw[] = getFlatRoutes(deepClone(asyncRouterMap, ['component']))
|
||||
// const flatRoutes: AppRouteRecordRaw[] = deepClone(asyncRouterMap, ['component'])
|
||||
// 动态路由,404一定要放到最后面
|
||||
this.addRouters = routers.concat([{
|
||||
path: '/:path(.*)*',
|
||||
redirect: '/404',
|
||||
|
@ -30,6 +38,7 @@ class Permission extends VuexModule implements PermissionState {
|
|||
breadcrumb: false
|
||||
}
|
||||
}])
|
||||
// 渲染菜单的所有路由
|
||||
this.routers = deepClone(constantRouterMap, ['component']).concat(routers)
|
||||
}
|
||||
@Mutation
|
||||
|
@ -40,7 +49,16 @@ class Permission extends VuexModule implements PermissionState {
|
|||
@Action
|
||||
public GenerateRoutes(): Promise<unknown> {
|
||||
return new Promise(resolve => {
|
||||
const routerMap: AppRouteRecordRaw[] = generateRoutes(deepClone(asyncRouterMap, ['component']))
|
||||
// 路由权限控制
|
||||
let routerMap: AppRouteRecordRaw[] = []
|
||||
if (wsCache.get(appStore.userInfo).roleName === 'admin') {
|
||||
// 模拟前端控制权限
|
||||
routerMap = generateRoutes(deepClone(asyncRouterMap, ['component']))
|
||||
} else {
|
||||
// 模拟后端控制权限
|
||||
routerMap = getFilterRoutes(wsCache.get(appStore.userInfo).checkedNodes)
|
||||
}
|
||||
// const routerMap: AppRouteRecordRaw[] = generateRoutes(deepClone(asyncRouterMap, ['component']))
|
||||
this.SET_ROUTERS(routerMap)
|
||||
resolve()
|
||||
})
|
||||
|
@ -52,7 +70,7 @@ class Permission extends VuexModule implements PermissionState {
|
|||
}
|
||||
|
||||
// 路由过滤,主要用于权限控制
|
||||
function generateRoutes(routes: AppRouteRecordRaw[]): AppRouteRecordRaw[] {
|
||||
function generateRoutes(routes: AppRouteRecordRaw[], basePath = '/'): AppRouteRecordRaw[] {
|
||||
const res: AppRouteRecordRaw[] = []
|
||||
|
||||
for (const route of routes) {
|
||||
|
@ -61,12 +79,37 @@ function generateRoutes(routes: AppRouteRecordRaw[]): AppRouteRecordRaw[] {
|
|||
continue
|
||||
}
|
||||
|
||||
let onlyOneChild = null
|
||||
|
||||
if (route.children && route.children.length === 1 && !route.meta.alwaysShow) {
|
||||
onlyOneChild = isExternal(route.children[0].path)
|
||||
? route.children[0].path
|
||||
: path.resolve(path.resolve(basePath, route.path), route.children[0].path)
|
||||
}
|
||||
|
||||
let data: any = null
|
||||
data = Object.assign({}, route)
|
||||
|
||||
// 如不需要路由权限,可注释以下逻辑
|
||||
// 权限过滤,通过获取登录信息里面的角色权限,动态的渲染菜单。
|
||||
const list = wsCache.get(appStore.userInfo).checkedNodes
|
||||
// 开发者可以根据实际情况进行扩展
|
||||
for (const item of list) {
|
||||
// 通过路径去匹配
|
||||
if (isExternal(item.path) && (onlyOneChild === item.path || route.path === item.path)) {
|
||||
data = Object.assign({}, route)
|
||||
} else {
|
||||
const routePath = path.resolve(basePath, onlyOneChild || route.path)
|
||||
if (routePath === item.path || (route.meta && route.meta.followRoute === item.path)) {
|
||||
data = Object.assign({}, route)
|
||||
}
|
||||
}
|
||||
}
|
||||
// 如不需要路由权限,解注释下面一行
|
||||
// data = Object.assign({}, route)
|
||||
|
||||
// recursive child routes
|
||||
if (route.children && data) {
|
||||
data.children = generateRoutes(route.children)
|
||||
data.children = generateRoutes(route.children, path.resolve(basePath, data.path))
|
||||
}
|
||||
if (data) {
|
||||
res.push(data as AppRouteRecordRaw)
|
||||
|
@ -75,4 +118,34 @@ function generateRoutes(routes: AppRouteRecordRaw[]): AppRouteRecordRaw[] {
|
|||
return res
|
||||
}
|
||||
|
||||
// 模拟后端过滤路由
|
||||
function getFilterRoutes(routes: any[]): any[] {
|
||||
const res = []
|
||||
|
||||
for (const route of routes) {
|
||||
const data: any = {
|
||||
path: route.path,
|
||||
name: route.name,
|
||||
redirect: route.redirect
|
||||
}
|
||||
data.meta = Object.assign({}, route.meta || {}, { title: route.title })
|
||||
if (route.component) {
|
||||
// 动态加载路由文件,可根据实际情况进行自定义逻辑
|
||||
data.component = route.component === '#'
|
||||
? Layout
|
||||
: (route.component.includes('##')
|
||||
? getParentLayout(route.component.split('##')[1])
|
||||
: () => new Promise((resolve) => {
|
||||
resolve(import(`${route.component}`))
|
||||
}))
|
||||
}
|
||||
// recursive child routes
|
||||
if (route.children) {
|
||||
data.children = getFilterRoutes(route.children)
|
||||
}
|
||||
res.push(data)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
export const permissionStore = getModule<Permission>(Permission)
|
||||
|
|
|
@ -62,7 +62,7 @@
|
|||
<template #title="scope">
|
||||
<span class="is-required-item">{{ scope.row.label }}</span>
|
||||
</template>
|
||||
<template #titleDefault>
|
||||
<template #titleContent>
|
||||
<el-form-item prop="title">
|
||||
<el-input v-model="form.title" placeholder="请输入标题" />
|
||||
</el-form-item>
|
||||
|
@ -71,7 +71,7 @@
|
|||
<template #author="scope">
|
||||
<span class="is-required-item">{{ scope.row.label }}</span>
|
||||
</template>
|
||||
<template #authorDefault>
|
||||
<template #authorContent>
|
||||
<el-form-item prop="author">
|
||||
<el-input v-model="form.author" placeholder="请输入作者" />
|
||||
</el-form-item>
|
||||
|
@ -80,7 +80,7 @@
|
|||
<template #time="scope">
|
||||
<span class="is-required-item">{{ scope.row.label }}</span>
|
||||
</template>
|
||||
<template #timeDefault>
|
||||
<template #timeContent>
|
||||
<el-form-item prop="display_time">
|
||||
<el-date-picker
|
||||
v-model="form.display_time"
|
||||
|
@ -94,7 +94,7 @@
|
|||
<template #importance="scope">
|
||||
<span class="is-required-item">{{ scope.row.label }}</span>
|
||||
</template>
|
||||
<template #importanceDefault>
|
||||
<template #importanceContent>
|
||||
<el-form-item prop="importance">
|
||||
<el-select v-model="form.importance" placeholder="请选择重要性" style="width: 100%;">
|
||||
<el-option label="重要" value="3" />
|
||||
|
@ -107,7 +107,7 @@
|
|||
<template #pageviews="scope">
|
||||
<span class="is-required-item">{{ scope.row.label }}</span>
|
||||
</template>
|
||||
<template #pageviewsDefault>
|
||||
<template #pageviewsContent>
|
||||
<el-form-item prop="pageviews">
|
||||
<el-input-number
|
||||
v-model="form.pageviews"
|
||||
|
@ -172,43 +172,23 @@ const fromSchema: any[] = [
|
|||
{
|
||||
field: 'title',
|
||||
label: '标题',
|
||||
span: 24,
|
||||
slots: {
|
||||
title: 'title',
|
||||
default: 'titleDefault'
|
||||
}
|
||||
span: 24
|
||||
},
|
||||
{
|
||||
field: 'author',
|
||||
label: '作者',
|
||||
slots: {
|
||||
title: 'author',
|
||||
default: 'authorDefault'
|
||||
}
|
||||
label: '作者'
|
||||
},
|
||||
{
|
||||
field: 'display_time',
|
||||
label: '创建时间',
|
||||
slots: {
|
||||
title: 'time',
|
||||
default: 'timeDefault'
|
||||
}
|
||||
label: '创建时间'
|
||||
},
|
||||
{
|
||||
field: 'importance',
|
||||
label: '重要性',
|
||||
slots: {
|
||||
title: 'importance',
|
||||
default: 'importanceDefault'
|
||||
}
|
||||
label: '重要性'
|
||||
},
|
||||
{
|
||||
field: 'pageviews',
|
||||
label: '阅读数',
|
||||
slots: {
|
||||
title: 'pageviews',
|
||||
default: 'pageviewsDefault'
|
||||
}
|
||||
label: '阅读数'
|
||||
}
|
||||
]
|
||||
|
||||
|
|
|
@ -82,7 +82,7 @@ export default defineComponent({
|
|||
id: id
|
||||
}
|
||||
})
|
||||
if (res.code === '0000') {
|
||||
if (res.code) {
|
||||
for (const key in form) {
|
||||
if (key === 'importance') {
|
||||
form[key] = res.data[key].toString()
|
||||
|
|
|
@ -118,7 +118,7 @@ export default defineComponent({
|
|||
id: id
|
||||
}
|
||||
})
|
||||
if (res.code === '0000') {
|
||||
if (res) {
|
||||
for (const key in form) {
|
||||
if (key === 'importance') {
|
||||
form[key] = res.data[key].toString()
|
||||
|
@ -148,7 +148,7 @@ export default defineComponent({
|
|||
const res = await setExampApi({
|
||||
data: formData
|
||||
})
|
||||
if (res.code === '0000') {
|
||||
if (res) {
|
||||
Message.success(form.id ? '编辑成功' : '新增成功')
|
||||
emit('success', form.id ? 'edit' : 'add')
|
||||
}
|
||||
|
|
|
@ -154,7 +154,7 @@ export default defineComponent({
|
|||
const res = await getExampleListApi({
|
||||
params: Object.assign(defalutParams, data || {})
|
||||
})
|
||||
if (res.code === '0000') {
|
||||
if (res.code) {
|
||||
total.value = res.data.total
|
||||
tableData.value = res.data.list
|
||||
}
|
||||
|
@ -205,7 +205,7 @@ export default defineComponent({
|
|||
const res = await delsExampApi({
|
||||
data: { ids }
|
||||
})
|
||||
if (res.code === '0000') {
|
||||
if (res.code) {
|
||||
Message.success('删除成功!')
|
||||
getExampleList()
|
||||
}
|
||||
|
|
|
@ -84,7 +84,7 @@ export default defineComponent({
|
|||
id: id
|
||||
}
|
||||
})
|
||||
if (res.code === '0000') {
|
||||
if (res.code) {
|
||||
for (const key in form) {
|
||||
if (key === 'importance') {
|
||||
form[key] = res.data[key].toString()
|
||||
|
|
|
@ -121,7 +121,7 @@ export default defineComponent({
|
|||
id: id
|
||||
}
|
||||
})
|
||||
if (res.code === '0000') {
|
||||
if (res) {
|
||||
for (const key in form) {
|
||||
if (key === 'importance') {
|
||||
form[key] = res.data[key].toString()
|
||||
|
@ -151,7 +151,7 @@ export default defineComponent({
|
|||
const res = await setExampApi({
|
||||
data: formData
|
||||
})
|
||||
if (res.code === '0000') {
|
||||
if (res) {
|
||||
Message.success(form.id ? '编辑成功' : '新增成功')
|
||||
emit('success', form.id ? 'edit' : 'add')
|
||||
}
|
||||
|
|
|
@ -131,7 +131,7 @@ export default defineComponent({
|
|||
const res = await getExampleListApi({
|
||||
params: Object.assign(defalutParams, data || {})
|
||||
})
|
||||
if (res.code === '0000') {
|
||||
if (res) {
|
||||
total.value = res.data.total
|
||||
tableData.value = res.data.list
|
||||
}
|
||||
|
@ -182,7 +182,7 @@ export default defineComponent({
|
|||
const res = await delsExampApi({
|
||||
data: { ids }
|
||||
})
|
||||
if (res.code === '0000') {
|
||||
if (res) {
|
||||
Message.success('删除成功!')
|
||||
getExampleList()
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
import { fetch } from '_p/index/axios-config/axios'
|
||||
|
||||
interface PropsData {
|
||||
params?: any
|
||||
data?: any
|
||||
}
|
||||
|
||||
export const loginApi = ({ data }: PropsData) => {
|
||||
return fetch({ url: '/user/login', method: 'post', data })
|
||||
}
|
||||
|
||||
export const getRoleDetApi = ({ params }: PropsData) => {
|
||||
return fetch({ url: '/role/detail', method: 'get', params })
|
||||
}
|
|
@ -14,7 +14,7 @@
|
|||
<el-form-item prop="userName">
|
||||
<el-input
|
||||
v-model="form.userName"
|
||||
placeholder="请输入账号"
|
||||
placeholder="请输入账号 admin or test"
|
||||
class="form--input"
|
||||
>
|
||||
<template #prefix>
|
||||
|
@ -30,7 +30,7 @@
|
|||
show-password
|
||||
:minlength="3"
|
||||
:maxlength="18"
|
||||
placeholder="请输入密码"
|
||||
placeholder="请输入密码 admin or test"
|
||||
class="form--input"
|
||||
>
|
||||
<template #prefix>
|
||||
|
@ -63,6 +63,9 @@ import type { RouteRecordRaw } from 'vue-router'
|
|||
import { permissionStore } from '_p/index/store/modules/permission'
|
||||
import { appStore } from '_p/index/store/modules/app'
|
||||
import wsCache from '@/cache'
|
||||
import { ElNotification } from 'element-plus'
|
||||
|
||||
import { loginApi, getRoleDetApi } from './api'
|
||||
|
||||
interface FormModule {
|
||||
userName: string,
|
||||
|
@ -88,8 +91,8 @@ export default defineComponent({
|
|||
immediate: true
|
||||
})
|
||||
const form = reactive<FormModule>({
|
||||
userName: 'admin',
|
||||
passWord: 'admin'
|
||||
userName: '',
|
||||
passWord: ''
|
||||
})
|
||||
const rules = reactive<RulesModule>({
|
||||
userName: [{ required: true, message: '请输入账号' }],
|
||||
|
@ -100,16 +103,28 @@ export default defineComponent({
|
|||
if (!formWrap) return
|
||||
loading.value = true
|
||||
try {
|
||||
formWrap.validate((valid: boolean) => {
|
||||
formWrap.validate(async(valid: boolean) => {
|
||||
if (valid) {
|
||||
permissionStore.GenerateRoutes().then(() => {
|
||||
permissionStore.addRouters.forEach(async(route: RouteRecordRaw) => {
|
||||
await addRoute(route.name!, route) // 动态添加可访问路由表
|
||||
// 模拟登录接口之后返回角色信息
|
||||
const res = await loginApi({ data: form })
|
||||
if (res) {
|
||||
// 获取权限信息
|
||||
const role = await getRoleDetApi({
|
||||
params: {
|
||||
id: res.data.roleId
|
||||
}
|
||||
})
|
||||
wsCache.set(appStore.userInfo, form)
|
||||
permissionStore.SetIsAddRouters(true)
|
||||
push({ path: redirect.value || '/' })
|
||||
})
|
||||
if (role) {
|
||||
wsCache.set(appStore.userInfo, Object.assign(form, role.data))
|
||||
permissionStore.GenerateRoutes().then(() => {
|
||||
permissionStore.addRouters.forEach(async(route: RouteRecordRaw) => {
|
||||
await addRoute(route.name!, route) // 动态添加可访问路由表
|
||||
})
|
||||
permissionStore.SetIsAddRouters(true)
|
||||
push({ path: redirect.value || '/' })
|
||||
})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log('error submit!!')
|
||||
return false
|
||||
|
@ -121,6 +136,13 @@ export default defineComponent({
|
|||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
ElNotification({
|
||||
title: '提示',
|
||||
message: '账号 admin 为 前端 控制路由权限,账号 test 为 后端 控制路由权限。密码与账号相同',
|
||||
duration: 60000
|
||||
})
|
||||
|
||||
return {
|
||||
loginForm,
|
||||
loading, redirect, form, rules,
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
import { fetch } from '_p/index/axios-config/axios'
|
||||
|
||||
interface PropsData {
|
||||
params?: any
|
||||
data?: any
|
||||
}
|
||||
|
||||
export const getRoleListApi = ({ params }: PropsData) => {
|
||||
return fetch({ url: '/role/list', method: 'get', params })
|
||||
}
|
||||
|
||||
export const setRoleApi = ({ data }: PropsData) => {
|
||||
return fetch({ url: '/role/save', method: 'post', data })
|
||||
}
|
||||
|
||||
export const getRoleDetApi = ({ params }: PropsData) => {
|
||||
return fetch({ url: '/role/detail', method: 'get', params })
|
||||
}
|
|
@ -0,0 +1,230 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-width="100px"
|
||||
>
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-form-item prop="roleName" label="角色名">
|
||||
<el-input v-model="form.roleName" disabled placeholder="请输入角色名" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="角色权限">
|
||||
<el-tree
|
||||
ref="tree"
|
||||
:check-strictly="false"
|
||||
:data="routesData"
|
||||
:props="defaultProps"
|
||||
show-checkbox
|
||||
accordion
|
||||
node-key="path"
|
||||
highlight-current
|
||||
class="permission-tree"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<div class="dialong__button--wrap">
|
||||
<el-button @click="close">取消</el-button>
|
||||
<el-button :loading="subLoading" type="primary" @click="setListData">保存</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import path from 'path'
|
||||
import { defineComponent, PropType, ref, reactive, nextTick, unref } from 'vue'
|
||||
import { setRoleApi, getRoleDetApi } from '../api'
|
||||
import { asyncRouterMap } from '_p/index/router'
|
||||
import { AppRouteRecordRaw } from '_p/index/router/types'
|
||||
import { isExternal } from '@/utils/validate'
|
||||
import { Message } from '_c/Message'
|
||||
|
||||
const requiredRule = {
|
||||
required: true,
|
||||
message: '该项为必填项'
|
||||
}
|
||||
|
||||
interface Form {
|
||||
id: String
|
||||
roleName: String
|
||||
checkedNodes: any[]
|
||||
checkedkeys: any[]
|
||||
}
|
||||
|
||||
interface Rules {
|
||||
roleName: any[]
|
||||
}
|
||||
|
||||
interface DefaultProps {
|
||||
children: String
|
||||
label: String
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'InfoWrite',
|
||||
props: {
|
||||
info: {
|
||||
type: Object as PropType<object>,
|
||||
default: () => null
|
||||
}
|
||||
},
|
||||
emits: ['success', 'close'],
|
||||
setup(props, { emit }) {
|
||||
const tree = ref<HTMLElement | null>(null)
|
||||
const formRef = ref<HTMLElement | null>(null)
|
||||
|
||||
const subLoading = ref<boolean>(false)
|
||||
|
||||
let form = reactive<Form>({
|
||||
id: '', // id
|
||||
roleName: '', // 角色名
|
||||
checkedNodes: [], // 被选中的节点
|
||||
checkedkeys: [] // 被选中的keys
|
||||
})
|
||||
|
||||
const rules = reactive<Rules>({
|
||||
roleName: [requiredRule]
|
||||
})
|
||||
|
||||
const defaultProps = reactive<DefaultProps>({
|
||||
children: 'children',
|
||||
label: 'title'
|
||||
})
|
||||
|
||||
const routesData = ref<any[]>(generateRoutes([...asyncRouterMap]))
|
||||
|
||||
async function getDet(): Promise<void> {
|
||||
if (props.info) {
|
||||
const id = (props.info as any).id
|
||||
try {
|
||||
const res = await getRoleDetApi({
|
||||
params: {
|
||||
id: id
|
||||
}
|
||||
})
|
||||
if (res) {
|
||||
const formData = {}
|
||||
for (const key in form) {
|
||||
formData[key] = res.data[key]
|
||||
}
|
||||
form = Object.assign(form, formData)
|
||||
nextTick(() => {
|
||||
const treeRef = unref(tree as any)
|
||||
treeRef.setCheckedKeys(form.checkedkeys)
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 新增或者编辑
|
||||
function setListData() {
|
||||
try {
|
||||
subLoading.value = true
|
||||
const formRefWrap = unref(formRef as any)
|
||||
formRefWrap.validate(async(valid: boolean) => {
|
||||
if (valid) {
|
||||
const treeRef = unref(tree as any)
|
||||
// 获取所有被选中节点,由于是前端渲染,所以只要保存一维数组就行
|
||||
form.checkedNodes = treeRef.getCheckedNodes(false, true)
|
||||
console.log(JSON.stringify(form.checkedNodes))
|
||||
// 获取所有被选中的keys,便于渲染是否选中
|
||||
form.checkedkeys = treeRef.getCheckedKeys()
|
||||
console.log(JSON.stringify(form.checkedkeys))
|
||||
const res = await setRoleApi({
|
||||
data: form
|
||||
})
|
||||
if (res) {
|
||||
Message.success(form.id ? '编辑成功,请重新退出登录后查看效果' : '新增成功,请重新退出登录后查看效果')
|
||||
emit('success', form.id ? 'edit' : 'add')
|
||||
}
|
||||
} else {
|
||||
console.log('error submit!!')
|
||||
return false
|
||||
}
|
||||
})
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
} finally {
|
||||
subLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function close() {
|
||||
emit('close')
|
||||
}
|
||||
|
||||
function generateRoutes(routes: AppRouteRecordRaw[], basePath = '/') {
|
||||
const res: any[] = []
|
||||
|
||||
for (let route of routes) {
|
||||
// skip some route
|
||||
if (route.meta.hidden) { continue }
|
||||
|
||||
const oneShowingChild = onlyOneShowingChild(route.children, route, path.resolve(basePath, route.path))
|
||||
|
||||
if (route.children && oneShowingChild && !route.meta.alwaysShow) {
|
||||
route = oneShowingChild
|
||||
}
|
||||
|
||||
const data = {
|
||||
path: isExternal(route.path) ? route.path : path.resolve(basePath, route.path),
|
||||
title: route.meta && route.meta.title,
|
||||
name: route.name
|
||||
}
|
||||
// recursive child routes
|
||||
if (route.children) {
|
||||
(data as any).children = generateRoutes(route.children, data.path)
|
||||
}
|
||||
res.push(data)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
function onlyOneShowingChild(children: AppRouteRecordRaw[] = [], parent: AppRouteRecordRaw, basePath: string) {
|
||||
let onlyOneChild = null
|
||||
const showingChildren = children.filter(item => !item.meta.hidden)
|
||||
// When there is only one child route, the child route is displayed by default
|
||||
if (showingChildren.length === 1) {
|
||||
onlyOneChild = showingChildren[0]
|
||||
onlyOneChild.path = isExternal(onlyOneChild.path) ? onlyOneChild.path : path.resolve(basePath, onlyOneChild.path)
|
||||
return onlyOneChild
|
||||
}
|
||||
|
||||
// Show parent if there are no child route to display
|
||||
if (showingChildren.length === 0) {
|
||||
onlyOneChild = { ...parent, path: '', noShowingChildren: true }
|
||||
return onlyOneChild
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
getDet()
|
||||
|
||||
return {
|
||||
formRef,
|
||||
routesData,
|
||||
tree,
|
||||
subLoading,
|
||||
form,
|
||||
rules,
|
||||
defaultProps,
|
||||
getDet,
|
||||
close,
|
||||
setListData
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
|
@ -0,0 +1,259 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-width="130px"
|
||||
>
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-form-item prop="roleName" label="角色名">
|
||||
<el-input v-model="form.roleName" disabled placeholder="请输入角色名" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="角色权限">
|
||||
<el-tree
|
||||
ref="tree"
|
||||
:check-strictly="false"
|
||||
:expand-on-click-node="false"
|
||||
:data="routesData"
|
||||
:props="defaultProps"
|
||||
accordion
|
||||
node-key="path"
|
||||
highlight-current
|
||||
class="permission-tree"
|
||||
@node-click="handleNodeClick"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col v-if="seletTreeData" :span="12">
|
||||
<el-form-item label="title">
|
||||
<el-input v-model="seletTreeData.title" />
|
||||
</el-form-item>
|
||||
<el-form-item label="component">
|
||||
<el-input v-model="seletTreeData.component" />
|
||||
</el-form-item>
|
||||
<el-form-item label="redirect">
|
||||
<el-input v-model="seletTreeData.redirect" />
|
||||
</el-form-item>
|
||||
<el-form-item label="activeMenu">
|
||||
<el-input v-model="seletTreeData.meta.activeMenu" />
|
||||
</el-form-item>
|
||||
<el-form-item label="name">
|
||||
<el-input v-model="seletTreeData.name" />
|
||||
</el-form-item>
|
||||
<el-form-item label="icon">
|
||||
<el-input v-model="seletTreeData.meta.icon" />
|
||||
</el-form-item>
|
||||
<el-form-item label="hidden">
|
||||
<el-switch v-model="seletTreeData.meta.hidden" />
|
||||
</el-form-item>
|
||||
<el-form-item label="alwaysShow">
|
||||
<el-switch v-model="seletTreeData.meta.alwaysShow" />
|
||||
</el-form-item>
|
||||
<el-form-item label="noCache">
|
||||
<el-switch v-model="seletTreeData.meta.noCache" />
|
||||
</el-form-item>
|
||||
<el-form-item label="breadcrumb">
|
||||
<el-switch v-model="seletTreeData.meta.breadcrumb" />
|
||||
</el-form-item>
|
||||
<el-form-item label="affix">
|
||||
<el-switch v-model="seletTreeData.meta.affix" />
|
||||
</el-form-item>
|
||||
<el-form-item label="noTagsView">
|
||||
<el-switch v-model="seletTreeData.meta.noTagsView" />
|
||||
</el-form-item>
|
||||
<el-form-item label="showMainRoute">
|
||||
<el-switch v-model="seletTreeData.meta.showMainRoute" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<div class="dialong__button--wrap">
|
||||
<el-button @click="close">取消</el-button>
|
||||
<el-button :loading="subLoading" type="primary" @click="setListData">保存</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { setRoleApi, getRoleDetApi } from '../api'
|
||||
import { defineComponent, PropType, ref, reactive, nextTick, unref } from 'vue'
|
||||
import { AppRouteRecordRaw } from '_p/index/router/types'
|
||||
import { isExternal } from '@/utils/validate'
|
||||
import { Message } from '_c/Message'
|
||||
|
||||
const requiredRule = {
|
||||
required: true,
|
||||
message: '该项为必填项'
|
||||
}
|
||||
|
||||
interface Form {
|
||||
id: String
|
||||
roleName: String
|
||||
checkedNodes: any[]
|
||||
checkedkeys: any[]
|
||||
}
|
||||
|
||||
interface Rules {
|
||||
roleName: any[]
|
||||
}
|
||||
|
||||
interface DefaultProps {
|
||||
children: String
|
||||
label: String
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'InfoWrite2',
|
||||
props: {
|
||||
info: {
|
||||
type: Object as PropType<object>,
|
||||
default: () => null
|
||||
}
|
||||
},
|
||||
emits: ['success', 'close'],
|
||||
setup(props, { emit }) {
|
||||
const tree = ref<HTMLElement | null>(null)
|
||||
const formRef = ref<HTMLElement | null>(null)
|
||||
|
||||
const subLoading = ref<boolean>(false)
|
||||
|
||||
let form = reactive<Form>({
|
||||
id: '', // id
|
||||
roleName: '', // 角色名
|
||||
checkedNodes: [], // 被选中的节点
|
||||
checkedkeys: [] // 被选中的keys
|
||||
})
|
||||
|
||||
const rules = reactive<Rules>({
|
||||
roleName: [requiredRule]
|
||||
})
|
||||
|
||||
const defaultProps = reactive<DefaultProps>({
|
||||
children: 'children',
|
||||
label: 'title'
|
||||
})
|
||||
|
||||
const routesData = ref<any[]>([])
|
||||
|
||||
const seletTreeData = ref<any>(null) // 选中的菜单
|
||||
|
||||
async function getDet(): Promise<void> {
|
||||
if (props.info) {
|
||||
const id = (props.info as any).id
|
||||
try {
|
||||
const res = await getRoleDetApi({
|
||||
params: {
|
||||
id: id
|
||||
}
|
||||
})
|
||||
if (res) {
|
||||
const formData = {}
|
||||
for (const key in form) {
|
||||
formData[key] = res.data[key]
|
||||
}
|
||||
form = Object.assign(form, formData)
|
||||
routesData.value = generateRoutes(form.checkedNodes)
|
||||
nextTick(() => {
|
||||
const treeRef = unref(tree as any)
|
||||
treeRef.setCheckedKeys(form.checkedkeys)
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 新增或者编辑
|
||||
function setListData() {
|
||||
try {
|
||||
subLoading.value = true
|
||||
const formRefWrap = unref(formRef as any)
|
||||
formRefWrap.validate(async(valid: boolean) => {
|
||||
if (valid) {
|
||||
const res = await setRoleApi({
|
||||
data: Object.assign(form, { checkedNodes: routesData })
|
||||
})
|
||||
if (res) {
|
||||
Message.success(form.id ? '编辑成功,请重新退出登录后查看效果' : '新增成功,请重新退出登录后查看效果')
|
||||
emit('success', form.id ? 'edit' : 'add')
|
||||
}
|
||||
} else {
|
||||
console.log('error submit!!')
|
||||
return false
|
||||
}
|
||||
})
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
} finally {
|
||||
subLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 树形点击
|
||||
function handleNodeClick(data: any) {
|
||||
seletTreeData.value = data
|
||||
}
|
||||
|
||||
function close() {
|
||||
emit('close')
|
||||
}
|
||||
|
||||
function generateRoutes(routes: any[]) {
|
||||
const res: any[] = []
|
||||
|
||||
for (const route of routes) {
|
||||
const data: any = {
|
||||
path: route.path,
|
||||
title: route.title || (route.meta && route.meta.title),
|
||||
name: route.name,
|
||||
redirect: route.redirect || '',
|
||||
component: route.component || '',
|
||||
meta: {
|
||||
hidden: route.meta && route.meta.hidden,
|
||||
alwaysShow: route.meta && route.meta.alwaysShow,
|
||||
icon: route.meta && route.meta.icon,
|
||||
noCache: route.meta && route.meta.noCache,
|
||||
breadcrumb: route.meta && route.meta.breadcrumb,
|
||||
affix: route.meta && route.meta.affix,
|
||||
noTagsView: route.meta && route.meta.noTagsView,
|
||||
activeMenu: route.meta && route.meta.activeMenu,
|
||||
showMainRoute: route.meta && route.meta.showMainRoute
|
||||
}
|
||||
}
|
||||
// recursive child routes
|
||||
if (route.children) {
|
||||
data.children = generateRoutes(route.children)
|
||||
}
|
||||
res.push(data)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
getDet()
|
||||
|
||||
return {
|
||||
formRef,
|
||||
routesData,
|
||||
tree,
|
||||
subLoading,
|
||||
form,
|
||||
rules,
|
||||
defaultProps,
|
||||
getDet,
|
||||
handleNodeClick,
|
||||
close,
|
||||
setListData,
|
||||
seletTreeData
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
|
@ -0,0 +1,213 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="由于是模拟数据,所以只提供了两种不同权限的角色,开发者可根据实际情况自行改造结合。"
|
||||
type="info"
|
||||
style="margin-bottom: 20px;"
|
||||
/>
|
||||
|
||||
<div class="search__example--wrap">
|
||||
<com-search
|
||||
:data="searchData"
|
||||
@search-submit="searchSubmit"
|
||||
@reset-submit="resetSubmit"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<com-table
|
||||
v-loading="loading"
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
:pagination="{
|
||||
currentPage: defalutParams.pageIndex,
|
||||
total: total,
|
||||
onSizeChange: handleSizeChange,
|
||||
onCurrentChange: handleCurrentChange
|
||||
}"
|
||||
>
|
||||
<template #remark="scope">
|
||||
<span>模拟</span>
|
||||
<el-tag
|
||||
:type="scope.row.roleName === 'admin' ? 'success' : 'warning'"
|
||||
style="margin: 0 15px;"
|
||||
>{{ scope.row.roleName === 'admin' ? '前端' : '后端' }}</el-tag>
|
||||
<span>角色</span>
|
||||
</template>
|
||||
|
||||
<template #action="scope">
|
||||
<el-button type="primary" size="mini" @click="open(scope.row)">编辑</el-button>
|
||||
</template>
|
||||
</com-table>
|
||||
|
||||
<com-dialog v-model="dialogVisible" :title="title">
|
||||
<info-write
|
||||
v-if="comName === 'InfoWrite' && dialogVisible"
|
||||
:info="info"
|
||||
@close="toggleVisible"
|
||||
@success="success"
|
||||
/>
|
||||
<info-write2
|
||||
v-if="comName === 'InfoWrite2' && dialogVisible"
|
||||
:info="info"
|
||||
@close="toggleVisible"
|
||||
@success="success"
|
||||
/>
|
||||
</com-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from 'vue'
|
||||
|
||||
import { useExample } from '@/hooks/useExample'
|
||||
|
||||
import { getRoleListApi } from './api'
|
||||
|
||||
import InfoWrite from './components/InfoWrite.vue'
|
||||
import InfoWrite2 from './components/InfoWrite2.vue'
|
||||
|
||||
const searchData = [
|
||||
{
|
||||
label: '角色名',
|
||||
value: '',
|
||||
itemType: 'input',
|
||||
field: 'roleName',
|
||||
placeholder: '请输入角色名',
|
||||
clearable: true
|
||||
}
|
||||
]
|
||||
|
||||
const columns = [
|
||||
{
|
||||
field: 'roleName',
|
||||
label: '角色名'
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
slots: {
|
||||
default: 'remark'
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'action',
|
||||
label: '操作',
|
||||
width: '80px',
|
||||
slots: {
|
||||
default: 'action'
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Role',
|
||||
components: {
|
||||
InfoWrite,
|
||||
InfoWrite2
|
||||
},
|
||||
setup() {
|
||||
const info = ref<any>(null)
|
||||
|
||||
const {
|
||||
defalutParams,
|
||||
tableData,
|
||||
loading,
|
||||
total,
|
||||
dialogVisible,
|
||||
title,
|
||||
currentChange,
|
||||
sizeChange,
|
||||
comName,
|
||||
toggleVisible
|
||||
} = useExample()
|
||||
|
||||
// 请求数据
|
||||
async function getRoleList(data?: any): Promise<void> {
|
||||
try {
|
||||
const res = await getRoleListApi({
|
||||
params: Object.assign(defalutParams, data || {})
|
||||
})
|
||||
if (res) {
|
||||
total.value = res.data.total
|
||||
tableData.value = res.data.list
|
||||
}
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 查询
|
||||
function searchSubmit(data: any) {
|
||||
// 该方法重置了一些默认参数
|
||||
currentChange(1)
|
||||
getRoleList(data)
|
||||
}
|
||||
|
||||
// 重置
|
||||
function resetSubmit(data: any) {
|
||||
// 该方法重置了一些默认参数
|
||||
currentChange(1)
|
||||
getRoleList(data)
|
||||
}
|
||||
|
||||
// 展示多少条
|
||||
function handleSizeChange(val: number) {
|
||||
// 该方法重置了一些默认参数
|
||||
sizeChange(val)
|
||||
getRoleList()
|
||||
}
|
||||
|
||||
// 展示第几页
|
||||
function handleCurrentChange(val: number) {
|
||||
// 该方法重置了一些默认参数
|
||||
currentChange(val)
|
||||
getRoleList()
|
||||
}
|
||||
|
||||
// 打开弹窗
|
||||
function open(row: any) {
|
||||
comName.value = row.roleName === 'admin' ? 'InfoWrite' : 'InfoWrite2'
|
||||
title.value = !row ? '新增' : '编辑'
|
||||
info.value = row || null
|
||||
toggleVisible(true)
|
||||
}
|
||||
|
||||
// 成功之后的回调
|
||||
function success(type: string) {
|
||||
if (type === 'add') {
|
||||
currentChange(1)
|
||||
}
|
||||
toggleVisible()
|
||||
getRoleList()
|
||||
}
|
||||
|
||||
getRoleList()
|
||||
|
||||
return {
|
||||
searchData,
|
||||
columns,
|
||||
info,
|
||||
defalutParams,
|
||||
tableData,
|
||||
loading,
|
||||
total,
|
||||
dialogVisible,
|
||||
title,
|
||||
currentChange,
|
||||
sizeChange,
|
||||
comName,
|
||||
toggleVisible,
|
||||
searchSubmit,
|
||||
resetSubmit,
|
||||
handleSizeChange,
|
||||
handleCurrentChange,
|
||||
open,
|
||||
success
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
|
@ -0,0 +1,10 @@
|
|||
import { fetch } from '_p/index/axios-config/axios'
|
||||
|
||||
interface PropsData {
|
||||
params?: any
|
||||
data?: any
|
||||
}
|
||||
|
||||
export const getUserListApi = ({ params }: PropsData): any => {
|
||||
return fetch({ url: '/user/list', method: 'get', params })
|
||||
}
|
|
@ -0,0 +1,159 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="由于是模拟数据,所以只提供了两种不同权限的帐号,开发者可根据实际情况自行改造结合。"
|
||||
type="info"
|
||||
style="margin-bottom: 20px;"
|
||||
/>
|
||||
|
||||
<div class="search__example--wrap">
|
||||
<com-search
|
||||
:data="searchData"
|
||||
@search-submit="searchSubmit"
|
||||
@reset-submit="resetSubmit"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<com-table
|
||||
v-loading="loading"
|
||||
:columns="columns"
|
||||
:data="tableData"
|
||||
:pagination="{
|
||||
currentPage: defalutParams.pageIndex,
|
||||
total: total,
|
||||
onSizeChange: handleSizeChange,
|
||||
onCurrentChange: handleCurrentChange
|
||||
}"
|
||||
>
|
||||
<template #remark="scope">
|
||||
<span>模拟</span>
|
||||
<el-tag
|
||||
:type="scope.row.userName === 'admin' ? 'success' : 'warning'"
|
||||
style="margin: 0 15px;"
|
||||
>{{ scope.row.userName === 'admin' ? '前端' : '后端' }}</el-tag>
|
||||
<span>控制路由权限</span>
|
||||
</template>
|
||||
</com-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
|
||||
import { useExample } from '@/hooks/useExample'
|
||||
|
||||
import { getUserListApi } from './api'
|
||||
|
||||
const searchData = [
|
||||
{
|
||||
label: '帐号',
|
||||
value: '',
|
||||
itemType: 'input',
|
||||
field: 'userName',
|
||||
placeholder: '请输入帐号',
|
||||
clearable: true
|
||||
}
|
||||
]
|
||||
|
||||
const columns = [
|
||||
{
|
||||
field: 'userName',
|
||||
label: '帐号'
|
||||
},
|
||||
{
|
||||
field: 'password',
|
||||
label: '密码'
|
||||
},
|
||||
{
|
||||
field: 'role',
|
||||
label: '角色'
|
||||
},
|
||||
{
|
||||
label: '备注',
|
||||
slots: {
|
||||
default: 'remark'
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
export default defineComponent({
|
||||
name: 'User',
|
||||
setup() {
|
||||
const {
|
||||
defalutParams,
|
||||
tableData,
|
||||
loading,
|
||||
total,
|
||||
title,
|
||||
currentChange,
|
||||
sizeChange
|
||||
} = useExample()
|
||||
|
||||
// 请求数据
|
||||
async function getUserList(data?: any): Promise<void> {
|
||||
try {
|
||||
const res = await getUserListApi({
|
||||
params: Object.assign(defalutParams, data || {})
|
||||
})
|
||||
if (res) {
|
||||
total.value = res.data.total
|
||||
tableData.value = res.data.list
|
||||
}
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 查询
|
||||
function searchSubmit(data: any) {
|
||||
// 该方法重置了一些默认参数
|
||||
currentChange(1)
|
||||
getUserList(data)
|
||||
}
|
||||
|
||||
// 重置
|
||||
function resetSubmit(data: any) {
|
||||
// 该方法重置了一些默认参数
|
||||
currentChange(1)
|
||||
getUserList(data)
|
||||
}
|
||||
|
||||
// 展示多少条
|
||||
function handleSizeChange(val: number) {
|
||||
// 该方法重置了一些默认参数
|
||||
sizeChange(val)
|
||||
getUserList()
|
||||
}
|
||||
|
||||
// 展示第几页
|
||||
function handleCurrentChange(val: number) {
|
||||
// 该方法重置了一些默认参数
|
||||
currentChange(val)
|
||||
getUserList()
|
||||
}
|
||||
|
||||
getUserList()
|
||||
|
||||
return {
|
||||
searchData,
|
||||
columns,
|
||||
defalutParams,
|
||||
tableData,
|
||||
loading,
|
||||
total,
|
||||
title,
|
||||
currentChange,
|
||||
sizeChange,
|
||||
searchSubmit,
|
||||
resetSubmit,
|
||||
handleSizeChange,
|
||||
handleCurrentChange
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
|
@ -0,0 +1,5 @@
|
|||
// 新版element-plus样式问题,先手动修复,后面更新版本在看看会不会出问题
|
||||
.el-overlay {
|
||||
overflow: hidden !important;
|
||||
text-align: left;
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
@import '~element-plus/lib/theme-chalk/index.css';
|
||||
@import './element-plus.less';
|
||||
@import './transition.less';
|
||||
@import './sider.less';
|
||||
@import './glob.less';
|
||||
|
|
|
@ -10,7 +10,7 @@ export function isEmail(path: any): boolean {
|
|||
|
||||
// 验证手机
|
||||
export function isPhone(tel: any): boolean {
|
||||
return /^[1][3,4,5,7,8][0-9]{9}$/.test(tel)
|
||||
return /^[1][3,4,5,6,7,8,9][0-9]{9}$/.test(tel)
|
||||
}
|
||||
|
||||
// 验证身份证号
|
||||
|
|
|
@ -3954,10 +3954,10 @@ electron-to-chromium@^1.3.621:
|
|||
resolved "https://registry.npm.taobao.org/electron-to-chromium/download/electron-to-chromium-1.3.622.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Felectron-to-chromium%2Fdownload%2Felectron-to-chromium-1.3.622.tgz#9726bd2e67a5462154750ce9701ca6af07d07877"
|
||||
integrity sha1-lya9LmelRiFUdQzpcBymrwfQeHc=
|
||||
|
||||
element-plus@1.0.1-beta.14:
|
||||
version "1.0.1-beta.14"
|
||||
resolved "https://registry.yarnpkg.com/element-plus/-/element-plus-1.0.1-beta.14.tgz#67e6742ef0a380156d306d519d474220f8c3e03e"
|
||||
integrity sha512-iqc8lAmj4yYTVQFlxwm5IWj3vxufgmF8FVwKgEKJfy1qQQVqA34R81IgywQpYh3jO/d+ofmHXhsm+z3ojXVp0A==
|
||||
element-plus@1.0.1-beta.26:
|
||||
version "1.0.1-beta.26"
|
||||
resolved "https://registry.yarnpkg.com/element-plus/-/element-plus-1.0.1-beta.26.tgz#f24181aab2569b62ca01e63541209b70d524b8ab"
|
||||
integrity sha512-nFzkn31AlZ+bXjnAAeXRoewesC57fI6yaUbxZaE+f1maj9ll5dbtiwk6petJhYvjQ3si4fN3A9P/qZA3ZPeyMg==
|
||||
dependencies:
|
||||
"@popperjs/core" "^2.4.4"
|
||||
async-validator "^3.4.0"
|
||||
|
|
Loading…
Reference in New Issue