项目演示地址:https://hujiyi.github.io/acme-world-web/


文件与路由规划

相对于其他两大部分来说,后台管理界面的组成更复杂,包含的组件、视图也更多。 为了更方便进行管理, 将相应的文件分别放到不同的文件夹下。就当前项目来说,主要实现一个论坛的后台管理功能,所以在 dashboard 文件夹中分别创建三个文件夹用于存放不同的文件:

  • layout 文件夹: 用于存放后台管理界面(仪表盘)的组成组件;

  • forum 文件夹:用于存放和论坛管理有关的视图;

  • pages 文件夹: 用于存放仪表盘其他部分的视图;

如果有更多的内容,也可以建相应的文件夹进行分类存放

创建文件夹和文件

根据以上规划,在 dashboard 文件夹 分别 创建名为:layoutforumpages 的文件夹。

src/views/dashboard/forum/ 文件夹下添加以下两个文件:

  1. TopicManager.vue: 论坛话题管理视图;

  2. CommentManager.vue: 话题评论管理视图。

src/views/dashboard/layout/ 文件夹下添加以下三个文件:

  1. Aside.vue: 侧边栏组件;

  2. Header.vue: 顶部栏组件;

  3. MenuTree.vue: 无限嵌套菜单组件.

src/views/dashboard/pages 文件夹中添加以下一个文件:

  1. MainIndex.vue: 仪表盘首页视图。

文件夹和文件添加完毕后,src/views 文件夹内的结构如下图所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|-- account
| |-- pages
| | |-- Login.vue
| | |-- PasswordReset.vue
| | `-- SignUp.vue
| `-- Index.vue
|-- dashboard
| |-- forum
| | |-- CommentManager.vue
| | `-- TopicManager.vue
| |-- layout
| | |-- Aside.vue
| | |-- Header.vue
| | `-- MenuTree.vue
| |-- pages
| | `-- MainIndex.vue
| `-- Index.vue
`-- home
`-- Index.vue

添加路由

打开路由配置文件 src/router/index.js, 在原 path: '/dashboard' 的路由项添加相应的子路由,修改后内容如以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
import Vue from 'vue';
import VueRouter from 'vue-router';


Vue.use(VueRouter);

const routes = [
{
path: '/',
name: 'Home',
component: () => import('../views/home/Index.vue'),
},
{
path: '/account',
redirect: '/login', // 路由重定向, 跳转至其默认子路由
// name: 'Account', // 包含子路由时,父路由不需要 name, 此处注释掉
// 父路由组件内必须有一个 <router-view /> 用于响应、显示匹配的子路由组件
component: () => import('../views/account/Index.vue'),
children: [
{
// 子路由的 path 以 / 开头,表示绝对路径,父path 在子路由中不起作用
path: '/login', // 实际路径:/login
name: 'Login', // 命名路由
component: () => import('../views/account/pages/Login.vue'),
},
{
path: '/signup', // 实际路径:/signup
name: 'SignUp',
component: () => import('../views/account/pages/SignUp.vue'),
},
{
path: '/password_reset', // 实际路径:/password_reset
name: 'PasswordReset',
component: () => import('../views/account/pages/PasswordReset.vue'),
},
]
},
{
// 后台管理部分
path: '/dashboard',
// name: 'Dashboard', // 包含子路由时,不需要 name 项
// 父路由组件内必须有一个 <router-view /> 用于响应、显示匹配的子路由组件
component: () => import('../views/dashboard/Index.vue'),
children: [
{
// 子路由的 path 不是以 / 开头时,实际路径为 父path 与 子path 的组合
path: '', // 实际路径为: /dashboard
alias: 'index', // 别名,相当于第二个 path, 实际路径为: /dashboard/index
name: 'Dashboard', // 父路由没有 name 项,此处使用父路由原来的 name
component: () => import('@/views/dashboard/pages/MainIndex.vue'),
},
{
path: 'topic_manager', // 实际路径为: /dashboard/topic_manager
name: 'TopicManager',
component: () => import('@/views/dashboard/forum/TopicManager.vue'),
},
{
path: 'comment_manager', // 实际路径为: /dashboard/comment_manager
name: 'CommentManager',
component: () => import('@/views/dashboard/forum/CommentManager.vue'),
},
],
}
];

const router = new VueRouter({
routes
});

export default router;

后台管理界面的整体布局

打开文件 src/views/dashboard/Index.vue , 编辑其内容如以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<template>
<el-container>
<el-aside width="240px">Aside</el-aside>
<el-container>
<el-header>Header</el-header>
<el-main>Main</el-main>
</el-container>
</el-container>
</template>

<script>
export default {
name: 'Dashboard',
}
</script>

<style>
.el-container {
height: 100vh;
}
.el-header {
background-color: #b3c0d1;
color: #333;
}
.el-main {
background-color: #e9eef3;
color: #333;
}
</style>

在运行结果的首页点击 链接跳转至 后台管理, 显示结果如下图所示:

顶栏的实现

打开文件 src/views/dashboard/layout/Header.vue, 编辑其内容如以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<template>
<div class="lay-header horiz-container">
<router-link to="/">
<el-button type="text">首页</el-button>
</router-link>
<div class="spacer"></div>
<el-link :underline="false" type="success" class="mx-2">
<i class="fa fa-bell fa-lg"></i> 消息
</el-link>
<el-link :underline="false" type="danger" class="mx-2">
<i class="fa fa-envelope fa-lg"></i> 邮件
</el-link>
<el-link :underline="false" type="primary" class="mx-2">当前用户:张三</el-link>
</div>
</template>

<script>
export default {
name: 'LayoutHeader',
}
</script>

<style>
.lay-header {
height: 60px;
align-items: center;
}
</style>

打开文件 src/views/dashboard/Index.vue , 添加对顶部栏组件LayoutHeader引用,修改后内容如以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<template>
<el-container>
<el-aside width="240px">Aside</el-aside>
<el-container>
<el-header>
<!-- 使用组件,组件名可以改为 短横线命名的格式 -->
<layout-header></layout-header>
</el-header>
<el-main>Main</el-main>
</el-container>
</el-container>
</template>

<script>
export default {
name: 'Dashboard',
// 引用的组件
components:{
// 导入并注册顶部栏组件 LayoutHeader
LayoutHeader:()=>import('./layout/Header.vue'),
}
}
</script>

<style>
.el-container {
height: 100vh;
}
.el-header {
background-color: #b3c0d1;
color: #333;
}
.el-main {
background-color: #e9eef3;
color: #333;
}
</style>

项目运行效果如下图所示:

侧边栏的实现

后台管理的侧边栏一般显示仪表盘的控制菜单,菜单项可以通过 json 数据来生成。

准备生成菜单项的 json 数据

assets 文件夹下新建 json 文件夹,再创建文件: menuItems.json

打开新建的文件 src/assets/json/menuItems.json, 复制以下内容粘贴到该文件中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
{
"menuItems": [
{
"id": 0,
"title": "仪表盘",
"icon": "fa fa-tachometer fa-lg",
"chip": "new",
"link": "/dashboard"
},
{
"id": 1,
"title": "评论管理",
"icon": "fa fa-commenting-o fa-lg",
"chip": "6",
"link": "/dashboard"
},
{
"id": 2,
"title": "图片管理",
"icon": "fa fa-image fa-lg",
"chip": "new",
"link": "/dashboard"
},
{
"id": 4,
"title": "论坛管理",
"icon": "fa fa-newspaper-o fa-lg",
"chip": "new",
"link": "/dashboard",
"children": [
{
"id": 42,
"title": "话题管理",
"icon": "fa fa-pencil-square-o fa-lg",
"chip": "new",
"link": "/dashboard/topic_manager"
},
{
"id": 43,
"title": "评论管理",
"icon": "fa fa-commenting-o fa-lg",
"chip": "new",
"link": "/dashboard/comment_manager"
}
]
},
{
"id": 7,
"title": "权限管理",
"icon": "fa fa-user-secret fa-lg",
"chip": "new",
"link": "/dashboard",
"children": [
{
"id": 8,
"title": "角色管理",
"icon": "fa fa-id-card-o fa-lg",
"chip": "new",
"link": "/dashboard"
},
{
"id": 9,
"title": "用户管理",
"icon": "fa fa-users fa-lg",
"chip": "new",
"link": "/dashboard"
}
]
},
{
"id": 10,
"title": "仪表盘",
"icon": "el-icon-odometer",
"chip": "new",
"link": "/dashboard"
},
{
"id": 11,
"title": "仪表盘",
"icon": "el-icon-odometer",
"chip": "new",
"link": "/dashboard"
},
{
"id": 12,
"title": "仪表盘",
"icon": "el-icon-odometer",
"chip": "new",
"link": "/dashboard"
}
]
}

说明:还没有实际跳转目标的项,"link" 的值都是 "/dashboard", 这么写是为了防止跳转到不存在的视图导致错误。如有添加其他后台管理功能,请自行修改就可以了。

创建无限菜单组件

侧边栏的菜单可以包含有子菜单项,如果想实现子菜单项的嵌套, 就需要通过一个组件来实现。 打开文件 src/views/dashboard/layout/MenuTree.vue, 编辑其内容如以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<template>
<div>
<template v-for="item in menuData">
<el-menu-item v-if="!item.children" :key="item.id" :index="item.link">
<i :class="item.icon"></i>
{{ item.title }}
<el-badge :value="item.chip" :max="99" class="badge-item"></el-badge>
</el-menu-item>
<el-submenu v-else-if="item.children.length > 0" :key="'s' + item.id">
<template slot="title">
<i :class="item.icon"></i>
<span slot="title">{{ item.title }}</span>
</template>
<!-- 递归生成下一级子菜单 -->
<menu-tree menuData="item.children"></menu-tree>
</el-submenu>
</template>
</div>
</template>

<script>
export default {
name: "MenuTree",
props: ["menuData"],
}
</script>

编辑侧边栏组件

打开文件 src\views\dashboard\layout\Aside.vue, 编辑其内容如以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<template>
<div>
<div class="dashboard-logo">
<p>Admin</p>
</div>
<el-menu :router="true">
<template v-for="(item, index) in menu.menuItems">
<el-menu-item v-if="!item.children" :key="index" :index="item.link">
<i :class="item.icon"></i>
{{ item.title }}
<el-badge :value="item.chip" :max="99" class="badge-item"></el-badge>
</el-menu-item>

<el-submenu
v-else-if="item.children.length > 0"
:key="'k' + item.id"
:index="item.link"
>
<template slot="title">
<i :class="item.icon"></i>
<span slot="title">{{ item.title }}</span>
</template>
<!-- 子菜单组件 -->
<menu-tree :menuData="item.children"></menu-tree>
</el-submenu>
</template>
</el-menu>
</div>
</template>

<script>
// 导入生成左侧菜单列表的 json 文件
import menu from '@/assets/json/menuItems.json';
export default {
name: 'LayoutAside',
components: {
MenuTree: () => import('./MenuTree.vue'),
},
data: () => ({
menu, // 导入的json 数据 作为变量
}),
}
</script>

<style>
.dashboard-logo {
text-align: center;
font-size: 28px;
font-weight: 600;
}
</style>

使用侧边栏组件, 打开文件 src/views/dashboard/Index.vue , 编辑其内容如以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<template>
<el-container>
<el-aside width="240px">
<!-- 使用侧边栏组件 -->
<layout-aside></layout-aside>
</el-aside>
<el-container>
<el-header>
<layout-header></layout-header>
</el-header>
<el-main>Main</el-main>
</el-container>
</el-container>
</template>

<script>
export default {
name: 'Dashboard',
// 引用的组件
components: {
LayoutHeader: () => import('./layout/Header.vue'),
// 导入侧边栏组件 LayoutAside
LayoutAside: () => import('./layout/Aside.vue'),
}
}
</script>

<style>
.el-container {
height: 100vh;
}
.el-header {
background-color: #b3c0d1;
color: #333;
}
.el-main {
background-color: #e9eef3;
color: #333;
}
</style>

项目运行效果如下图所示:

菜单匹配的子路由视图

打开文件 src/views/dashboard/pages/MainIndex.vue, 编辑其内容如以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
<template>
<div>仪表盘首页正在建设中</div>
</template>

<script>
export default {
name: 'MainIndex',
}
</script>

<style>
</style>

打开文件 src/views/dashboard/forum/TopicManager.vue, 编辑其内容如以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
<template>
<div>话题管理</div>
</template>

<script>
export default {
name: 'TopicManager',
}
</script>

<style>
</style>

打开文件 src/views/dashboard/forum/CommentManager.vue, 编辑其内容如以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
<template>
<div>评论管理</div>
</template>

<script>
export default {
name: 'CommentManager',
}
</script>

<style>
</style>

在父组件中显示匹配的子路由视图

打开文件 src/views/dashboard/Index.vue, 在 <el-main></el-main> 标签中添加 用于显示匹配的子路由的视图, 修改后的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<template>
<el-container>
<el-aside width="240px">
<layout-aside></layout-aside>
</el-aside>
<el-container>
<el-header>
<layout-header></layout-header>
</el-header>
<el-main>
<!-- 用于响应匹配的子路由视图 -->
<router-view />
</el-main>
</el-container>
</el-container>
</template>

<script>
export default {
name: 'Dashboard',
components: {
LayoutHeader: () => import('./layout/Header.vue'),
LayoutAside: () => import('./layout/Aside.vue'),
}
}
</script>

<style>
.el-container {
height: 100vh;
}
.el-header {
background-color: #b3c0d1;
color: #333;
}
.el-main {
background-color: #e9eef3;
color: #333;
}
</style>

修改完毕,项目运行效果如下图:

后继内容:
使用 Element UI 和 Leancloud 的 Vue.js 项目开发 VI

===END===