前言
vue-manage-system,基于Vue.js和element-ui的后端管理系统模板。 自 2016 年底第一次提交以来,已经三年了,在 GitHub 上拥有 8,300 个 star。 这就是让我不断更新的动力,也导致了很多陷阱,我在这里总结一下。
github地址:vue-manage-system
在线地址:lin-xin.gitee.io/example/work/
图像
图像
图像
自定义图标
element-ui自带的字体图标相对较少,很多比较常见的都没有,所以需要引入你想要的字体图标。 最流行的图标库Font Awesome,足有675个图标,但这也导致字体文件比较大,项目中没有必要使用这么多图标。 那么这个时候,阿里巴巴图标库就是一个非常好的选择。
首先在阿里图标上创建一个项目,设置图标前缀,如el-icon-lx,设置Font Family,如lx-iconfont导航样式的自定义,将需要使用的图标添加到项目中。 我选择Font类来生成在线链接,因为所有页面都需要使用图标,只需在index.html中直接引入css链接即可
vue-manage-system
然后需要设置带有前缀 el-icon-lx 的图标类名才能使用 lx-iconfont 字体。
[class*="el-icon-lx"], [class^=el-icon-lx] {
font-family: lx-iconfont!important;
}
但这种风格应该放在哪里呢? 这可不是随便就能放进去的。 main.js中引入了element-ui样式,样式中有一段css:
[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;
}
显然,如果这个css在我们自定义样式之后执行,就会覆盖我们的样式,自定义图标就不会显示了。 构建项目时,会将APP.vue中的样式打包到app.css中,然后将main.js中引用的样式追加到后面。 那么我们可以把自定义的样式放到一个css文件中,然后在main.js中引入element-ui css后引入,然后就可以覆盖掉默认的字体了,然后就可以在项目中使用图标了。
聪明人发现,如果我的自定义图标的前缀不应该包含el-icon-,就不会有这样的问题。 是的,为了保持与原字体相同的样式,需要复制它的整个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;
}
导航菜单
element-ui关于导航菜单的文档也很详细,但是还是有人提出问题或者加QQ问我:如何创建三级菜单等。而且,具体的菜单项可能是返回的具体数据项由服务器根据权限进行分配,因此不能将它们硬编码在模板中。
首先,确定菜单数据的格式如下。 即使服务器返回的格式不是这样的导航样式的自定义,前端也需要将其处理成如下格式:
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是菜单图标,可以使用上面我们自定义的图标; index为路由地址; title 是菜单名称; subs 是子菜单。 模板通过判断菜单是否包含子菜单来显示二级菜单和三级菜单。
{{ item.title }}
{{ subItem.title }}
{{ threeItem.title }}
{{ subItem.title }}
{{ item.title }}
这样就完成了动态导航菜单。
通过 Header 组件中的按钮触发 Sidebar 组件的展开或折叠涉及到在组件之间传递数据。 这里,组件之间的通信是通过 Vue.js 的一个单独的事件中心(Event Bus)来管理的。
const bus = new Vue();
单击 Header 组件中的按钮时会触发折叠事件:
bus.$emit('collapse', true);
监听 Sidebar 组件中的折叠事件:
bus.$on('collapse', msg => {
this.collapse = msg;
})
图表自适应
vue-manage-system中使用的图表插件是vue-schart,它封装了一个基于canvas的图表插件schart.js。 要使图表适应其宽度并随着窗口或父元素的大小变化而重新渲染,如果图表插件中没有实现此功能,则需要手动实现。
vue-schart 提供了 renderChart() 方法来重新渲染图表。 在Vue.js中,父组件调用子组件的方法可以通过$refs来调用。
然后监听窗口的resize事件并调用renderChart()方法重新渲染图表。
import Schart from 'vue-schart';
export default {
components: {
Schart
},
mounted(){
window.addEventListener('resize', ()=>{
this.$refs.bar.renderChart();
})
}
}
但记得在组件被销毁时移除监听器! 监听窗口的大小改变完成了,但是父元素的大小改变呢? 因为父元素的宽度设置为百分比,所以当侧边栏折叠时父元素的宽度会发生变化。 然而,div没有resize事件,无法监听其宽度变化,但我们会知道何时触发折叠。 那么是不是可以在折叠发生变化时,通过调用渲染函数来重新渲染图表呢?那么最好通过Event Bus监听侧边栏的变化,300ms后重新渲染,因为当折叠时有一个300ms的动画过程折叠式的。
bus.$on('collapse', msg => {
setTimeout(() => {
this.$refs.bar.renderChart();
}, 300);
});
多个选项卡
多标签页也是提出最多问题的功能。
在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方法中,tag数组中保存了一个tag对象,包括title(标签显示的标题)、path(标签的路由地址)、name(组件名称,用于include匹配)。 路由地址需要使用fullPath字段。 如果使用路径字段,如果地址后面有参数,则不会保存。
在Home组件中,它监听标签的变化并缓存所需的组件。
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一起讨论。
更多文章:lin-xin/blog
暂无评论内容