Vue 2.x + Element后台模板开发教程(三)后台首页模板设计

源码下载地址:http://www.80cxy.com/Blog/ResourceView?arId=202012091714193847b2Euxl

最近在学习vue.js开发,想做个简单的管理系统,研究了很长时间,看了很多慕课网的视频教程,教程地址:https://www.imooc.com/new/course/list?c=vuejs,看完教程开始试着做项目,做到后台首页被几个问题困扰了很久。

1、后台模板,模板功能怎么实现,之前都是嵌套iframe,现在怎么处理呢?研究了两天,发现这里的控制使用子路由实现,vue在路由跳转时,跳转到子路由会保留父路由的页面内容。

2、从网上下载了很多代码,都没跑起来,左侧菜单都是动态的,都得配置接口程序。

3、vue后台模板路由也需要根据接口返回权限动态实现,如果访问地址路由里面没有跳转到404页面。

本文实现的后台模板只是静态页面,动态路由、动态菜单会随着学习逐步完善。

先看看效果图:

实现思路:先创建一个主模板文件,home.vue,然后顶部是一个组件header.vue,底部是一个组件footer.vue,左侧菜单栏是一个组件Sidebar.vue,还有个tags.vue组件用于管理打开页面,中间内容部分是一个<router-view></router-view>用于展示具体跳转的页面,新增完目录如下:

home页面代码:

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
<template>
    <div class="wrapper">
        <v-head></v-head>
        <v-sidebar></v-sidebar>
        <div class="content-box" :class="{'content-collapse':collapse}">
            <div class="content_wrapper">
                <v-tags></v-tags>
                <div class="content" style="flex:1;">
                    <div class="content_inner">
                        <transition name="move" mode="out-in">
                            <keep-alive :include="tagsList">
                                <router-view></router-view>
                            </keep-alive>
                        </transition>
                    </div>
                </div>
                <v-footer></v-footer>
            </div>
        </div>
    </div>
</template>
 
<style lang="scss" scoped>
    .content_wrapper {
        display: flex;
        flex-direction: column;
        height: 100%;
 
        .content_inner {
            //    background: #fff;
            height: 100%;
        }
    }
</style>
 
<script>
    import vHead from './Header.vue';
    import vSidebar from './Sidebar.vue';
    import vTags from './Tags.vue';
    import vFooter from './Footer.vue';
    import bus from './bus';
    export default {
        data() {
            return {
                tagsList: [],
                collapse: false
            }
        },
        components: {
            vHead,
            vSidebar,
            vTags,
            vFooter
        },
        created() {
            bus.$on('collapse', msg => {
                this.collapse = msg;
            })
 
            // 只有在标签页列表里的页面才使用keep-alive,即关闭标签之后就不保存到内存中了。
            bus.$on('tags', msg => {
                let arr = [];
                for (let i = 0, len = msg.length; i < len; i++) {
                    msg[i].name && arr.push(msg[i].name);
                }
                this.tagsList = arr;
            })
        }
    }
</script>
1
2
3
4
5
6
7
8
9
10
11
12
    import vHead from './Header.vue';
    import vSidebar from './Sidebar.vue';
    import vTags from './Tags.vue';
    import vFooter from './Footer.vue';
    import bus from './bus';
 
components: {
            vHead,
            vSidebar,
            vTags,
            vFooter
        },

以上代码引用各个子组件。

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
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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
<template>
    <div class="header">
        <!-- 折叠按钮 -->
        <div class="collapse-btn" @click="collapseChage">
            <i class="el-icon-menu"></i>
        </div>
        <div class="logo">孔子人才网后台管理系统</div>
        <div class="header-right">
            <div class="header-user-con">
                <!-- <marquee onMouseOver="this.start()" style="font-size:18px;padding-bottom:4px;width:100px;" scrollamount="1">{{getlev}}</marquee> -->
                <div style="font-size:18px;padding-bottom:4px;width:60px;">admin</div>
                <!-- 全屏显示 -->
                <div class="btn-fullscreen" @click="handleFullScreen">
                    <el-tooltip effect="dark" :content="fullscreen?`取消全屏`:`全屏`" placement="bottom">
                        <i class="el-icon-rank"></i>
                    </el-tooltip>
                </div>
                <!-- 消息中心 -->
                <div class="btn-bell">
                    <el-tooltip effect="dark" :content="message?`有${message}条未读消息`:`消息中心`" placement="bottom">
                        <router-link to="/tabs">
                            <i class="el-icon-bell"></i>
                        </router-link>
                    </el-tooltip>
                    <span class="btn-bell-badge" v-if="message"></span>
                </div>
                <!-- 用户头像 -->
                <div class="user-avator"><img src="../../assets/Images/img.jpg"></div>
                <!-- 用户名下拉菜单 -->
                <el-dropdown class="user-name" trigger="click" @command="handleCommand">
                    <span class="el-dropdown-link">
                        用户名<i class="el-icon-caret-bottom"></i>
                    </span>
                     
                    <el-dropdown-menu slot="dropdown">
                         
                        <a  href="https://github.com/merciqiao" target="_blank">
                            <el-dropdown-item>关于作者</el-dropdown-item>
                        </a>
                        <a href="https://github.com/merciqiao/merciqiao-vue" target="_blank">
                            <el-dropdown-item>项目仓库</el-dropdown-item>
                        </a>
                        <a href="/zanzhu" target="_blank">
                            <el-dropdown-item style="color:orange;">赞助作者</el-dropdown-item>
                        </a>
                        <el-dropdown-item divided  command="changeZh">切换中文</el-dropdown-item>
                        <el-dropdown-item command="changeEn">切换英文</el-dropdown-item>
                        <el-dropdown-item divided  command="loginout">退出登录</el-dropdown-item>
                    </el-dropdown-menu>
                </el-dropdown>
            </div>
        </div>
    </div>
</template>
<script>
    import bus from './bus';
    export default {
        data() {
            return {
                collapse: false,
                fullscreen: false,
                name: 'merciqiao',
                lev:'青铜级',
                message: 2
            }
        },
        computed:{
            username(){
                let username = this.$common.getSessionStorage('username');
                return username ? username : this.name;
            },
            getlev(){
         
                let levList = this.$common.getSessionStorage('lev',true);
                let lev='';
                if(levList){
                    for(var i=0;i<levList.length;i++){
                        lev+=levList[i].roleName;
                    }
                }
                
                return lev ? lev : this.lev;
            }
        },
        methods:{
            // 用户名下拉菜单选择事件
            handleCommand(command) {
                if(command == 'loginout'){
                    this.$common.removeSessionStorage('token');
                    this.$router.push('/login');
                }
                else if(command == 'changeZh'){
                    this.$i18n.locale = 'zh_CN';
                }
                else if(command == 'changeEn'){
                    this.$i18n.locale = 'en_US';
                }
            },
            // 侧边栏折叠
            collapseChage(){
                this.collapse = !this.collapse;
                bus.$emit('collapse', this.collapse);
            },
            // 全屏事件
            handleFullScreen(){
                let element = document.documentElement;
                if (this.fullscreen) {
                    if (document.exitFullscreen) {
                        document.exitFullscreen();
                    } else if (document.webkitCancelFullScreen) {
                        document.webkitCancelFullScreen();
                    } else if (document.mozCancelFullScreen) {
                        document.mozCancelFullScreen();
                    } else if (document.msExitFullscreen) {
                        document.msExitFullscreen();
                    }
                } else {
                    if (element.requestFullscreen) {
                        element.requestFullscreen();
                    } else if (element.webkitRequestFullScreen) {
                        element.webkitRequestFullScreen();
                    } else if (element.mozRequestFullScreen) {
                        element.mozRequestFullScreen();
                    } else if (element.msRequestFullscreen) {
                        // IE11
                        element.msRequestFullscreen();
                    }
                }
                this.fullscreen = !this.fullscreen;
            }
        },
        mounted(){
            if(document.body.clientWidth < 1366){
                this.collapseChage();
            }
        }
    }
</script>
<style scoped>
    .header {
        position: relative;
        box-sizing: border-box;
        width: 100%;
        height: 70px;
        font-size: 22px;
        color: #fff;
        background-color: #07c4a8;
    }
    .collapse-btn{
        float: left;
        padding: 0 21px;
        cursor: pointer;
        line-height: 70px;
    }
    .header .logo{
        float: left;
        width:250px;
        line-height: 70px;
    }
    .header-right{
        float: right;
        padding-right: 50px;
    }
    .header-user-con{
        display: flex;
        height: 70px;
        align-items: center;
    }
    .btn-fullscreen{
        transform: rotate(45deg);
        margin-right: 5px;
        font-size: 24px;
    }
    .btn-bell, .btn-fullscreen{
        position: relative;
        width: 30px;
        height: 30px;
        text-align: center;
        border-radius: 15px;
        cursor: pointer;
    }
    .btn-bell-badge{
        position: absolute;
        right: 0;
        top: -2px;
        width: 8px;
        height: 8px;
        border-radius: 4px;
        background: #f56c6c;
        color: #fff;
    }
    .btn-bell .el-icon-bell{
        color: #fff;
    }
    .user-name{
        margin-left: 10px;
    }
    .user-avator{
        margin-left: 20px;
    }
    .user-avator img{
        display: block;
        width:40px;
        height:40px;
        border-radius: 50%;
    }
    .el-dropdown-link{
        color: #fff;
        cursor: pointer;
    }
    .el-dropdown-menu__item{
        text-align: center;
    }
</style>

footer.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
<template>
  <footer>
      <div class="footer_content">
          <!-- 京ICP备18050367号-1 qq群:73110051(无广告)
          {{$t("footer.title")}}-->
      </div>
       
  </footer>
</template>
<style lang="scss">
    footer{
        border-top:1px solid #ddd;
        flex: 0 0 auto;
        .footer_content{
            width: 340px;
            margin: 0 auto;
            text-align: center;
            line-height: 60px;
            height: 60px;
            color:#606266;
            .qq{
                font-size: 9px;
            }
        }
    }
</style>

Siderbar.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
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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
<template>
    <div class="sidebar">
        <el-menu class="sidebar-el-menu" :default-active="onRoutes" :collapse="collapse" background-color="#324157"
            text-color="#bfcbd9" active-text-color="#20a0ff" unique-opened router>
            <el-submenu index="1">
                <template slot="title">
                    <i class="el-icon-location"></i>
                    <span>导航一</span>
                </template>
                <el-menu-item-group>
                    <template slot="title">分组一</template>
                    <el-menu-item index="1-1">选项1</el-menu-item>
                    <el-menu-item index="1-2">选项2</el-menu-item>
                </el-menu-item-group>
                <el-menu-item-group title="分组2">
                    <el-menu-item index="1-3">选项3</el-menu-item>
                </el-menu-item-group>
                <el-submenu index="1-4">
                    <template slot="title">选项4</template>
                    <el-menu-item index="1-4-1">选项1</el-menu-item>
                </el-submenu>
            </el-submenu>
            <el-menu-item index="2">
                <i class="el-icon-menu"></i>
                <span slot="title">导航二</span>
            </el-menu-item>
            <el-menu-item index="3" disabled>
                <i class="el-icon-document"></i>
                <span slot="title">导航三</span>
            </el-menu-item>
            <el-menu-item index="4">
                <i class="el-icon-setting"></i>
                <span slot="title">导航四</span>
            </el-menu-item>
        </el-menu>
        <!--<el-menu class="sidebar-el-menu" :default-active="onRoutes" :collapse="collapse" background-color="#324157"
            text-color="#bfcbd9" active-text-color="#20a0ff" unique-opened router>
            <template v-for="(item,i) in menuList">
                <template v-if="item.subs&&item.subs.length">
                    <el-submenu :index="item.index" :key="i">
                        <template slot="title">
                            <i :class="item.icon"></i><span slot="title">{{ item.title }}</span>
                        </template>
                        <el-menu-item v-for="(subItem,i) in item.subs" :key="i" :index="subItem.index">
                            {{ subItem.title }}
                        </el-menu-item>
                    </el-submenu>
                </template>
            </template>
        </el-menu>-->
    </div>
</template>
 
<script>
    import bus from './bus';
    import {
        mapGetters
    } from 'vuex'
    export default {
        data() {
            return {
                collapse: false,
                items: [{
                        icon: 'el-icon-setting',
                        index: 'index',
                        title: '系统首页',
                    },
                    {
                        icon: 'el-icon-tickets',
                        index: 'table',
                        title: '基础模块',
                        subs: [{
                                index: 'searchinput',
                                title: '查询输入页'
                            },
                            {
                                index: 'tabpage',
                                title: '标签选项卡'
                            },
                            {
                                index: 'tablepage',
                                title: '综合表格页'
                            },
                            {
                                index: 'formpage',
                                title: '表单页'
                            },
                            {
                                index: 'treepage',
                                title: '树组件页'
                            }
                        ]
                    },
                    {
                        icon: 'el-icon-setting',
                        index: '3',
                        title: '系统管理',
                        subs: [{
                                index: 'organizationTree',
                                title: '机构管理'
                            },
                            {
                                index: 'sysUser',
                                title: '用户管理'
                            },
                            {
                                index: 'sysRole',
                                title: '角色管理'
                            },
                            {
                                index: 'sysAcl',
                                title: '角色资源授权'
                            },
                            {
                                index: 'sysResource',
                                title: '资源管理'
                            },
                            {
                                index: 'sysMenu',
                                title: '菜单管理'
                            }
 
 
                        ]
                    },
                    // {
                    //     icon: 'el-icon-message',
                    //     index: 'tabs',
                    //     title: 'tab选项卡'
                    // },
                    // {
                    //     icon: 'el-icon-date',
                    //     index: '3',
                    //     title: '表单相关',
                    //     subs: [
                    //         {
                    //             index: 'form',
                    //             title: '基本表单'
                    //         },
                    //         {
                    //             index: 'editor',
                    //             title: '富文本编辑器'
                    //         },
                    //         {
                    //             index: 'markdown',
                    //             title: 'markdown编辑器'
                    //         },
                    //         {
                    //             index: 'upload',
                    //             title: '文件上传'
                    //         }
                    //     ]
                    // },
                    // {
                    //     icon: 'el-icon-star-on',
                    //     index: 'charts',
                    //     title: 'schart图表'
                    // },
                    // {
                    //     icon: 'el-icon-rank',
                    //     index: 'drag',
                    //     title: '拖拽列表'
                    // },
                    // {
                    //     icon: 'el-icon-warning',
                    //     index: 'permission',
                    //     title: '权限测试'
                    // },
                    {
                        icon: 'el-icon-error',
                        index: '404',
                        title: '404页面'
                    }
                ]
            }
        },
        computed: mapGetters({
            menuList: 'getMenuList',
            onRoutes() {
                return this.$route.path.replace('/', '');
            }
        }),
        created() {
            // 通过 Event Bus 进行组件间通信,来折叠侧边栏
            bus.$on('collapse', msg => {
                this.collapse = msg;
            })
        }
    }
</script>
 
<style scoped>
    .sidebar {
        display: block;
        position: absolute;
        left: 0;
        top: 70px;
        bottom: 0;
        overflow-y: scroll;
    }
 
    .sidebar::-webkit-scrollbar {
        width: 0;
    }
 
    .sidebar-el-menu:not(.el-menu--collapse) {
        width: 250px;
    }
 
    .sidebar>ul {
        height: 100%;
    }
</style>

Tags.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
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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
<template>
    <div class="tags" v-if="showTags">
        <ul>
            <li class="tags-li" v-for="(item,index) in tagsList" :class="{'active': isActive(item.path)}" :key="index">
                <router-link :to="item.path" class="tags-li-title">
                    {{item.title}}
                </router-link>
                 
                <span v-show="item.title!='系统首页'" class="tags-li-icon" @click="closeTags(index)"><i class="el-icon-close"></i></span>
            </li>
        </ul>
        <div class="tags-close-box">
            <el-dropdown @command="handleTags">
                <el-button size="small" type="primary">
                    标签选项<i class="el-icon-arrow-down el-icon--right"></i>
                </el-button>
                <el-dropdown-menu size="medium" slot="dropdown">
                    <el-dropdown-item command="other">关闭其他</el-dropdown-item>
                    <el-dropdown-item command="all">关闭所有</el-dropdown-item>
                </el-dropdown-menu>
            </el-dropdown>
        </div>
         
    </div>
</template>
 
<script>
    import bus from './bus';
    export default {
        data() {
            return {
                tagsList: []
            }
        },
        methods: {
            isActive(path) {
                return path === this.$route.fullPath;
            },
            // 关闭单个标签
            closeTags(index) {
                const delItem = this.tagsList.splice(index, 1)[0];
                const item = this.tagsList[index] ? this.tagsList[index] : this.tagsList[index - 1];
                if (item) {
                    delItem.path === this.$route.fullPath && this.$router.push(item.path);
                }else{
                    this.$router.push('/');
                }
            },
            // 关闭全部标签
            closeAll(){
                this.tagsList = [{
                    name: "index",
                    path: "/index",
                    title: "系统首页"
                }];
                this.$router.push('/');
            },
            // 关闭其他标签
            closeOther(){
                const curItem = this.tagsList.filter(item => {
                    return item.path === this.$route.fullPath;
                })
                this.tagsList = curItem;
            },
            // 设置标签
            setTags(route){
                const isExist = this.tagsList.some(item => {
                    return item.path === route.fullPath;
                })
                !isExist && this.tagsList.push({
                    title: route.meta.title,
                    path: route.fullPath,
                    // name: route.matched[1].components.default.name
                    name: route.name
                })
                bus.$emit('tags', this.tagsList);
            },
            handleTags(command){
                command === 'other' ? this.closeOther() : this.closeAll();
            }
        },
        computed: {
            showTags() {
                return this.tagsList.length > 0;
            }
        },
        watch:{
            $route(newValue, oldValue){
                this.setTags(newValue);
            }
        },
        created(){
            this.setTags(this.$route);
        }
    }
 
</script>
 
 
<style lang="scss">
    $tag_height:34px;//tab高度变量
    .tags {
        position: relative;
        height: $tag_height;
        overflow: hidden;
        background: #fff;
        padding-right: 120px;
        flex: 0 0 auto;
    }
 
    .tags ul {
        box-sizing: border-box;
        width: 100%;
        height: 100%;
    }
 
    .tags-li {
        float: left;
        margin: 3px 5px 2px 3px;
        border-radius: 3px;
        font-size: 12px;
        overflow: hidden;
        cursor: pointer;
        height: $tag_height - 8px;
        line-height: $tag_height - 8px;
        border: 1px solid #e9eaec;
        background: #fff;
        padding: 0 5px 0 12px;
        vertical-align: middle;
        color: #666;
        -webkit-transition: all .3s ease-in;
        -moz-transition: all .3s ease-in;
        transition: all .3s ease-in;
    }
 
    .tags-li:not(.active):hover {
        background: #f8f8f8;
    }
 
    .tags-li.active {
        color: #fff;
    }
 
    .tags-li-title {
        float: left;
        max-width: 80px;
        overflow: hidden;
        white-space: nowrap;
        text-overflow: ellipsis;
        margin-right: 5px;
        color: #666;
    }
 
    .tags-li.active .tags-li-title {
        color: #fff;
    }
 
    .tags-close-box {
        position: absolute;
        right: 0;
        top: 0;
        box-sizing: border-box;
        padding-top: 1px;
        text-align: center;
        width: 110px;
        height: $tag_height;
        background: #fff;
        box-shadow: -3px 0 15px 3px rgba(0, 0, 0, .1);
        z-index: 10;
    }
 
</style>

最后修改路由配置文件,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const routes = [{
        path: '/',
        name: 'Login',
        component: Login
    },
    {
        name: 'Home',
        path: '/Home',
        component: Home,
        meta: {
            title: '系统首页'
        }
    }
]

项目创建是用的vue cli3脚手架创建的。

以下是博主微信欢迎沟通交流。

posted @ 2020-12-11 09:04  kwstu2018  阅读(897)  评论(0编辑  收藏  举报