Home  >  Article  >  Web Front-end  >  vue-manage-system background management system development process (code)

vue-manage-system background management system development process (code)

不言
不言Original
2018-09-13 16:16:152850browse

The content of this article is about the development process (code) of the vue-manage-system background management system. It has certain reference value. Friends in need can refer to it. I hope it will be helpful to you.

Preface

vue-manage-system, a backend management system template based on Vue.js and element-ui, has been almost two years since its first commit at the end of 2016. There are also 5k stars on GitHub, which gave me the motivation to continue updating. I also encountered many pitfalls, which I will summarize here.

github address: vue-manage-system

Custom icon

element-ui comes with relatively few font icons, and many of the more common ones are not available, so you need Introduce the font icons you want yourself. The most popular icon library, Font Awesome, has 675 icons, but this also results in a relatively large font file, and there is no need to use so many icons in the project. Then at this time, Alibaba Icon Library is a very good choice.

First create a project on Ali icon, set the icon prefix, such as el-icon-lx, set Font Family, such as lx-iconfont, add the icons you need to use to the project, I choose Font here class generates online links, because all pages need to use icons, just introduce the css link directly in index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>vue-manage-system</title>
    <!-- 这里引入阿里图标样式 -->
    <link rel="stylesheet" href="//at.alicdn.com/t/font_830376_qzecyukz0s.css">
</head>
<body>
<p id="app"></p>
</body>
</html>

Then you need to set the icon class name with the prefix el-icon-lx and use lx -iconfont font.

[class*="el-icon-lx"], [class^=el-icon-lx] {
    font-family: lx-iconfont!important;
}

But where should this style be placed? This is not something you can just put in casually. In main.js, the element-ui style is introduced, and there is a piece of css in the style:

[class*=" el-icon-"], [class^=el-icon-]{
    font-family: element-icons!important;
    speak: none;
    font-style: normal;
    font-weight: 400;
    font-variant: normal;
    text-transform: none;
    line-height: 1;
    vertical-align: baseline;
    display: inline-block;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

Obviously, if this piece of css is executed after our custom style, it will be overwritten. With our style, the customized icons cannot be displayed. When building the project, the styles in APP.vue will be packaged into app.css, and then the styles referenced in main.js will be appended to the back. Then we can put the custom style into a css file, and then introduce it after the element-ui css is introduced in main.js, then the default font can be overwritten, and then it can be used in the project through<i class="el-icon-lx-people"></i> Icons are used.

The clever person discovered that if the prefix of my custom icon does not contain el-icon-, there will be no such problem. Yes, in order to maintain the same style as the original font, you need to copy its entire css

/* 假设前缀为 el-lx */
[class*="el-lx-"], [class^=el-lx-]{
    font-family: lx-iconfont!important;
    speak: none;
    font-style: normal;
    font-weight: 400;
    font-variant: normal;
    text-transform: none;
    line-height: 1;
    vertical-align: baseline;
    display: inline-block;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

Navigation menu

element-ui The documentation on the navigation menu is also very detailed, but Some people still raise issues or add QQ to ask me: how to make the third-level menu, etc. Moreover, specific menu items may be specific data items returned by the server based on permissions, so they cannot be hard-coded in the template.

First determine the format of the menu data as follows. Even if the format returned by the server is not like this, the front-end needs to process it into the following format:

export default {
    data() {
        return {
            items: [{
                icon: 'el-icon-lx-home',
                index: 'dashboard',
                title: '系统首页'
            },{
                icon: 'el-icon-lx-calendar',
                index: '1',
                title: '表单相关',
                subs: [{
                    index: '1-1',
                    title: '三级菜单',
                    subs: [{
                        index: 'editor',
                        title: '富文本编辑器'
                    }]
                }]
            },{
                icon: 'el-icon-lx-warn',
                index: '2',
                title: '错误处理',
                subs: [{
                    index: '404',
                    title: '404页面'
                }]
            }]
        }
    }
}

icon is the menu icon, which can be used above. The customized icon; index is the routing address; title is the menu name; subs is the submenu. The template displays the second-level menu and the third-level menu by determining whether the menu contains subs.

<el-menu :default-active="onRoutes" :collapse="collapse" router>
    <template v-for="item in items">
        <template v-if="item.subs">
            <el-submenu :index="item.index" :key="item.index">
                <template slot="title">
                    <i :class="item.icon"></i><span slot="title">{{ item.title }}</span>
                </template>
                <template v-for="subItem in item.subs">
                    <el-submenu v-if="subItem.subs" :index="subItem.index" :key="subItem.index">
                        <template slot="title">{{ subItem.title }}</template>
                        <!-- 三级菜单 -->
                        <el-menu-item v-for="(threeItem,i) in subItem.subs" :key="i" :index="threeItem.index">
                            {{ threeItem.title }}
                        </el-menu-item>
                    </el-submenu>
                    <el-menu-item v-else :index="subItem.index" :key="subItem.index">
                        {{ subItem.title }}
                    </el-menu-item>
                </template>
            </el-submenu>
        </template>
        <!-- 没有二级菜单 -->
        <template v-else>
            <el-menu-item :index="item.index" :key="item.index">
                <i :class="item.icon"></i><span slot="title">{{ item.title }}</span>
            </el-menu-item>
        </template>
    </template>
</el-menu>

This completes a dynamic navigation menu.

Triggering the expansion or collapse of the Sidebar component through a button in the Header component involves transferring data between components. Here, the communication between components is managed through a separate event center (Event Bus) of Vue.js.

const bus = new Vue();

Trigger the collapse event when the button is clicked in the Header component:

bus.$emit('collapse', true);

Listen to the collapse event in the Sidebar component:

bus.$on('collapse', msg => {
    this.collapse = msg;
})

Chart adaptive

vue The chart plug-in used in -manage-system is vue-schart, which encapsulates a canvas-based chart plug-in schart.js. To make the chart adapt to its width and re-render as the size of the window or parent element changes, if this function is not implemented in the chart plug-in, you need to implement it manually.

vue-schart provides the renderChart() method to re-render the chart. In Vue.js, the method of a parent component calling a child component can be called through $refs.

<schart ref="bar" canvasId="bar" :data="data" type="bar" :options="options"></schart>

Then listen to the resize event of the window and call the renderChart() method to re-render the chart.

import Schart from 'vue-schart';
export default {
    components: {
        Schart
    },
    mounted(){
        window.addEventListener('resize', ()=>{
            this.$refs.bar.renderChart();
        })
    }
}

But remember to remove the listener when the component is destroyed! The size change of the listening window is completed, but what about the size change of the parent element? Because the width of the parent element is set to a percentage, the width of the parent element changes when the sidebar collapses. However, p does not have a resize event and cannot monitor its width change, but we will know when folding is triggered. So is it possible to re-render the chart by calling the rendering function when the fold changes? Then it is better to monitor the changes of the sidebar through Event Bus and re-render it after 300ms, because there is a 300ms animation process when folding

bus.$on('collapse', msg => {
    setTimeout(() => {
        this.$refs.bar.renderChart();
    }, 300);
});

Multi-tab page

Multi-tab page, it is also an issue The most functional one.

当在 A 标签页输入一些内容之后,打开 B 标签再返回到 A,要保留离开前的状态,因此需要使用 keep-alive 进行缓存,而且关闭之后的标签页就不再缓存,避免关闭后再打开还是之前的状态。keep-alive 的属性 include 的作用就是只有匹配的组件会被缓存。include 匹配的不是路由名,而是组件名,那么每个组件都需要添加 name 属性。

在 Tags 组件中,监听路由变化,将打开的路由添加到标签页中:

export default {
    data() {
        return {
            tagsList: []
        }
    },
    methods: {
        setTags(route){
            const isExist = this.tagsList.some(item => {
                return item.path === route.fullPath;
            })
            if(!isExist){
                this.tagsList.push({
                    title: route.meta.title,
                    path: route.fullPath,
                    name: route.matched[1].components.default.name
                })
            }
        }
    },
    watch:{
        $route(newValue, oldValue){
            this.setTags(newValue);
        }
    }
}

在 setTags 方法中,将一个标签对象存到标签数组中,包括title(标签显示的title),path(标签的路由地址),name(组件名,用于include匹配的)。路由地址需要用 fullPath 字段,如果使用 path 字段,那如果地址后面带有参数,就都没保存起来了。

在 Home 组件中,监听到标签的变化,缓存需要的组件。

<keep-alive :include="tagsList">
    <router-view></router-view>
</keep-alive>
export default {
    data(){
        return {
            tagsList: []
        }
    },
    created(){
        // 只有在标签页列表里的页面才使用keep-alive,即关闭标签之后就不保存到内存中了。
        bus.$on('tags', msg => {
            let arr = [];
            for(let i = 0, len = msg.length; i < len; i ++){
                // 提取组件名存到tagsList中,通过include匹配
                msg[i].name && arr.push(msg[i].name);
            }
            this.tagsList = arr;
        })
    }
}

总结

由于该项目中不包含任何业务代码,所以还是相对比较简单的,不过从开发中还是积累了一些经验,在其它项目中可以更加熟练地开发。功能虽然不算多,但是也勉强够用,如果有什么好的建议,可以开 issue 一起讨论。

相关推荐:

ASP.NET MVC5+EF6+EasyUI 后台管理系统微信公众平台开发

基于thinkphp的后台管理系统模板快速搭建,thinkphp后台模板

The above is the detailed content of vue-manage-system background management system development process (code). For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn