vue-manage-system 后台管理系统开发总结
前言
vue-manage-system,一個基于 Vue.js 和 element-ui 的后臺管理系統模板,從2016年年底第一個commit,到現在差不多兩年了,GitHub上也有了 5k star,也是這些讓我有了持續更新的動力,其中也踩了很多坑,在這總結一下。
github地址:vue-manage-system
線上地址:blog.gdfengshuo.com/example/work/
自定義圖標
element-ui 自帶的字體圖標比較少,而且許多比較常見的都沒有,因此需要自己引入自己想要的字體圖標。最受歡迎的圖標庫 Font Awesome,足足有 675 個圖標,但也因此導致字體文件比較大,而項目中又不需要用到這么多圖標。那么這時候,阿里圖標庫就是一個非常不錯的選擇。
首先在阿里圖標上創建一個項目,設置圖標前綴,比如 el-icon-lx,設置Font Family,比如 lx-iconfont,添加需要用到的圖標到項目中,我這邊選擇 Font class 生成在線鏈接,因為所有頁面都需要用到圖標,就直接在 index.html 中引入該css鏈接就行了
<!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>然后需要設置前綴為 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 在我們自定義樣式后面才執行,就會覆蓋了我們的樣式,那自定義的圖標就顯示不了。而在 build 項目的時候,會把 APP.vue 中的的樣式打包進 app.css 中,然后再把 main.js 中引用到的樣式追加到后面。那么我們可以把自定義樣式放到一個css文件中,然后在 main.js 引入 element-ui css 的后面引入,那就可以覆蓋掉默認字體了,然后便可以在項目中通過 <i class="el-icon-lx-people"></i> 使用圖標了。
那機智的人就發現了,我自定義圖標的前綴不要含 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 關于導航菜單的文檔也是非常詳細了,但是還是有人提 issue 或者加 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 就是子菜單了。而模板則通過判斷菜單中是否包含 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>這樣就完成了一個動態的導航菜單。
通過 Header 組件中的一個按鈕來觸發 Sidebar 組件展開或收起,涉及到了組件之間傳遞數據,這里通過 Vue.js 單獨的事件中心(Event Bus)管理組件間的通信。
const bus = new Vue();在 Header 組件中點擊按鈕時觸發 collapse 事件:
bus.$emit('collapse', true);在 Sidebar 組件中監聽 collapse 事件:
bus.$on('collapse', msg => {this.collapse = msg; })圖表自適應
vue-manage-system 中用到的圖表插件是 vue-schart,是把一個基于 canvas 的圖表插件 schart.js 進行了封裝。要做到圖表能夠自適應寬度,隨著 window 或者父元素的大小改變而重新渲染,如果圖表插件里沒實現該功能,就需要自己手動實現。
vue-schart 中提供了 renderChart() 的方法可以重新渲染圖表,Vue.js 中父組件調用子組件的方法,可以通過 $refs 進行調用。
<schart ref="bar" canvasId="bar" :data="data" type="bar" :options="options"></schart>然后監聽 window 的 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); });多標簽頁
多標簽頁,也是提 issue 最多的一個功能。
當在 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 一起討論。
更多文章:lin-xin/blog
轉載于:https://www.cnblogs.com/linxin/p/9638461.html
總結
以上是生活随笔為你收集整理的vue-manage-system 后台管理系统开发总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: DHCPv6
- 下一篇: Tornado入门三