preface

Vue-manage-system, a backend management system template based on Vue.js and Element-UI, was the first commit at the end of 2016, and it’s been almost two years now. There’s also 8.6 K Star on GitHub, which keeps me motivated to keep updating. There are also a lot of pits, which I’ll summarize here.

GitHub address: vue-manage-system

Online address: https://lin-xin.gitee.io/example/work/

Custom Icon

Element-UI comes with very few font ICONS, and many of the more common ones don’t, so you have to import the font ICONS you want. The most popular icon library, Font Awesome, has a whopping 675 ICONS, but as a result the Font file is relatively large and the project doesn’t need as many ICONS. In this case, Ali Icon Library is a very good choice.

First of all, create a project on the Ali icon, set the icon prefix, such as el-icon-lx, set the icon Family, such as lx-iconfont, and add the necessary icon to the project. I select the Font class to generate online links, because all pages need to use the icon. Simply introduce the CSS link 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> <div id="app"></div> </body> </html>

Then you need to set the icon class name prefixed with el-icon-lx to use the LX-IconFont font.

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

But where can I put this style? You can’t just put it away. In main.js, we introduce the Element-UI style, which has a CSS section like this:

[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 CSS is executed after our custom style, it will overwrite our style and the custom icon will not be displayed. When we build the project, we wrap the styles from app.vue into app.css, and then append the styles referenced in main.js. So we can put our custom styles in a CSS file, and then we can put them in after the element-ui CSS that was introduced in main.js, so that we can override the default font. You can then use ICONS in your project with < I class=”el-icon-lx-people”>
.

The smart people will have figured out that I don’t have to use the el-icon prefix in my custom icon- there will be no such problem. Yes, so in order to keep the same style as the original font, you need to copy its entire CSS

/ * assume the prefix for el - lx * / [class * = "el - lx -"], [class ^ = el - lx -] {the 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

The ELEMent-UI documentation for navigation menus is pretty detailed, but I still get issues or QQ questions: what about the three-level menu, etc. And the specific menu item may be the server side according to the permission to return a specific data item, so can not be written dead in the template.

First of all, decide the format of the menu data as follows. Even if the format returned by the server side is not like this, the front end needs to process it in the following format:

Export default {data() {return {items: [{icon: 'el-icon-lx-home', index: 'dashboard', title: 'system'},{icon: 'el-icon-lx-calendar', index: '1', title: 'table-related ', subs: [{index: '1-1', title:' table-related ', subs: [{index: '1-1', subs: [{index: 'editor, the title:' the rich text editor '}}]]}, {icon: 'el - icon - lx - warn, index:' 2 ', the title: 'error handling, subs: [{index:' 404 ', the title: '404 page '}]}]}}}

Icon is the menu icon, you can use our above custom icon; Index is the routing address; Title is the name of the menu; Subs is the submenu. The template displays the second and third level menus by determining whether there are subs in the menu.

<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 Sid Bar expansion or collapse via a button in the Header component involves passing data between components, where communication between components is managed through a separate Event center (Event Bus) in Vue.js.

const bus = new Vue();

Collapse events are triggered when a button is clicked in the Header component:

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

Listen for collapse events in the Sidebar component:

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

Graph adaptation

The charting plug-in used in vue-manage-system is vue-schart, which wraps a canvas-based charting plug-in called schart.js. To make the chart adaptable to the width, re-render it as the window or parent element changes in size. If this is not available in the chart plugin, you will need to do it manually.

Vue-Schart provides a renderChart() method to render a chart, and Vue.js calls a parent component’s child methods, which can be called with $refs.

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

It then listens for the window’s resize event and calls 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(); }}})

However, remember to remove listening when the component is destroyed! Listen for window size change. What about parent element size change? Because the parent element width is set to a percentage, the parent element width changes when the sidebar collapses. But div doesn’t have a resize event, so we can’t listen for its width to change, but we know when the folding is triggered. Is it possible to re-render the chart by listening for a change in the fold and then calling the rendering function? Then listen for the sidebar changes via the Event Bus and re-render after 300ms, because 300ms of animation is involved when folding

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

Multiple tabs

Multi – TAB pages, is also a feature that mentions the most issues.

When you input some content in the A TAB, you open the B TAB and return to A. You need to keep the state you left before, so you need to use Keep-Alive for caching, and the closed tabs will not be cached anymore, so you can avoid opening the closed tabs in the same state. The purpose of the keep-alive attribute include is that only matching components will be cached. Include does not match the route name, but the component name, so each component needs to add a name attribute.

In the Tags component, listen for route changes and add the open route to the TAB:

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); }}}

In the setTags method, store a tag object in an array of tags, including the title (the tag’s title), path (the tag’s routing address), and name (the component’s name for include matching). Routing addresses need to use the fullPath field. If you use the path field, then if the address is followed by an argument, it will not be saved.

In the Home component, listen for tag changes and cache the required components.

<keep-alive :include="tagsList">
    <router-view></router-view>
</keep-alive>
Export default {data(){return {tagsList: []}}, created(){// Keep - Alive is used only in pages that are in the list of tabs. bus.$on('tags', msg => { let arr = []; for(let i = 0, len = msg.length; i < len; I ++){// Push (MSG [I].name & &arr. Push (MSG [I].name); } this.tagsList = arr; }}})

conclusion

This project is relatively simple because it does not contain any business code, but it has gained some experience from development and can be developed more skillfully on other projects. Although the function is not much, it is barely enough. If there is any good suggestion, we can open an issue to discuss together.

More articles:lin-xin/blog