手摸手,帶你用vue擼後臺 系列三(實戰篇)

花褲衩發表於2017-06-13

完整專案地址:vue-element-admin

系列文章:

前言

在前面兩篇文章中已經把基礎工作環境構建完成,也已經把後臺核心的登入和許可權問題完成了,現在手摸手,一起進入實操。

Element

去年十月份開始用 vue 做管理後臺的時候毫不猶豫的就選擇了 element-ui,那時候 vue2.0 剛釋出也沒多久,市面上也沒有很多其它的 vue2.0 的 ui 框架可供選擇。雖然 element-ui 也有很多的不足,前期的bug也不少,但我還是選擇了它,簡單說一下我選擇element-ui的原因吧:

  • 有大廠背書 : 雖然核心開發只有兩三個人,但至少不用擔心哪天就不維護,帶著小姨子跑路了
  • 持續迭代 : element-ui發版至今release了四十多個版本,之前平均都是一週一個小版本更新(是不是不小心暴露了它bug多的問題/(ㄒoㄒ)/~~)(ps: 至2017.12.4 已經迭代了74個版本,還保持著較高更新頻率)。
  • 生態圈優異,社群活躍 :其 contributors已經有250多人(前期我有饒有興致的貢獻過幾個pr,參與過七八十個issue),社群裡也有很多基於element-ui 的擴充元件,也有很多相關的 qq 討論群或者 gitter
  • 社群的認可:目前Element已經是vue相關最多star的開源專案了,體現出了社群對其的認可。

說了這麼多優點,作為一個資深element-ui使用者還是有些要抱怨的~和react老大哥 Ant Design 相比還是有一定的差距的,不管是元件的豐富性,引數的可配性還是文件的完整性,亦或是UI的互動和美觀度。不過 ant 也是經過了近9k次commit的不斷打磨,才有了今天。我也相信 element-ui也會越來越好的。

這裡還有一些其它的框架(只討論pc端的框架)大家可以自行選擇:

  • ivew 一國人個人寫的框架,美觀度和互動性都不錯,有種介於Element和Ant之間的感覺,之前和element團隊小小的撕了一下,有興趣的自己去圍觀吧,框架還是很不做的,一個人能做出這樣,也是很不容易的。作者公開信件
  • vue-admin 也是一個不錯的選擇,程式碼寫的和不錯,官方也出了一個admin的架子,也很值得借鑑
  • vue-material 一個material design vue框架庫
  • vuetify 又是一個material design vue框架庫
  • Keen-UI 又又是一個material design vue框架庫
  • CoreUI-Free-Bootstrap-Admin-Template 和以前的Bootstrap一樣,搭好了一個完整的架子,大家可以進行二次擴充,它有vue,react,angular多個版本
  • Framework7-Vue 個人感覺這是本人體驗到現在移動端體驗最好的框架。不過Framework7-Vue感覺還不是很完善,還需要觀望一段時間。而且它有自己的路由規則,所以不能使用 vue-router,這點還是很不方便的。

簡單列舉了一些主流的框架,不得不感慨現在vue的生態圈真是太繁榮了,上述框架樓主並沒有深入使用過,不好發表太多建議,大家自行甄別適合自己業務的框架吧。


這裡開始我們會開始介紹一些結合Element的開發經驗。

基於Element的動態換膚

有些產品就是這麼殘忍,能完成需求就不錯了,還要讓我們做動態換膚。Element官網上也提供了自定義主題的方案 同時也提供了一個線上自定義主題的demo

手摸手,帶你用vue擼後臺 系列三(實戰篇)

是不是很酷,作者也說明了實現的方案 地址,大概思路:

  1. 先把預設主題檔案中涉及到顏色的 CSS 值替換成關鍵詞
  2. 根據使用者選擇的主題色生成一系列對應的顏色值
  3. 把關鍵詞再換回剛剛生成的相應的顏色值
  4. 直接在頁面上加 style 標籤,把生成的樣式填進去

我看完覺得真的還是有點複雜的。有沒有簡單的方案呢? 讓我們思考一下,讓我們自己寫動態換膚該怎麼寫呢?最常見的方法就是寫兩套主題,一套叫day theme ,一套叫night themenight theme主題 都在一個.night-theme的名稱空間下,我們動態的在bodyadd .night-themeremove .night-theme。這就是最簡單的動態換膚。所以我們也能不能順著這個思路,基於 element-ui 實現動態換膚呢?

首先我們下載官方通過的 Theme generator ,一個專門用來生成Element主題的工具。按照文件,我們生成了需要的主題。

手摸手,帶你用vue擼後臺 系列三(實戰篇)
之後就是我們要做的事情了,將這個主題的每個元素外面包裹一個class 來做名稱空間。 我們這裡用到了gulp-css-wrap這個神器,輕輕鬆鬆就完成了我們想要的結果

var path = require('path')
var gulp = require('gulp')
var cleanCSS = require('gulp-clean-css');
var cssWrap = require('gulp-css-wrap');

var customThemeName='.custom-theme'

gulp.task('css-wrap', function() {
  return gulp.src( path.resolve('./theme/index.css'))
    .pipe(cssWrap({selector:customThemeName}))
    .pipe(cleanCSS())
    .pipe(gulp.dest('dist'));
});

gulp.task('move-font', function() {
  return gulp.src(['./theme/fonts/**']).pipe(gulp.dest('dist/fonts'));
});

gulp.task('default',['css-wrap','move-font']);
複製程式碼

這樣就得到了一個以.custom-theme為名稱空間的自定義主題了,之後我們在專案中引入主題

//main.js
import 'assets/custom-theme/index.css'
複製程式碼

我們在換膚的地方toggleClass(document.body, 'custom-theme')一直toggle body 的 class就可以了。我們就簡單實現了動態換膚效果。

手摸手,帶你用vue擼後臺 系列三(實戰篇)
不過這種模式實現換膚也是有一個弊端的,它等於把這兩個主題都打包在了專案裡,如果你的專案主題需要七八種,這種模式就不適合了。我們就需要動態的載入css,下面就是最簡單的動態新增css的例子,當然你可以封裝一下,增加成功或者失敗回撥,判斷是否載入過改資源等等就不展開了。

var head = document.getElementsByTagName('HEAD').item(0);
var style = document.createElement('link');
style.href = 'style.css';
style.rel = 'stylesheet';
style.type = 'text/css';
head.appendChild(style);
複製程式碼

更新(2017.12)

element-ui 官方更新了2.0版本,同時也提供了一個新的換膚思路。 文件


側邊欄

這裡又有談一下導航欄的問題,本專案裡的側邊欄是根據 router.js 配置的路由並且根據許可權動態生成的,這樣就省去了寫一遍路由還要手動再寫一次側邊欄這種麻煩事,但也遇到了一個問題,路由可能會有多層巢狀,很多人反饋自己的側邊欄會有三級,甚至還有五級的。所以重構了一下側邊欄,使用了遞迴元件,這樣不管你多少級,都能愉快的顯示了。程式碼

手摸手,帶你用vue擼後臺 系列三(實戰篇)
側邊欄高亮問題: 很多人在群裡問為什麼自己的側邊欄不能跟著自己的路由高亮,其實很簡單,element-ui官方已經給了default-active所以我們只要

:default-active="$route.path"
複製程式碼

default-active一直指向當前路由就可以了,就是這麼簡單。

點選側邊欄 重新整理當前路由

在用 spa(單頁面開發) 這種開發模式之前,大部分都是多頁面後臺,使用者每次點選側邊欄都會重新請求這個頁面,使用者漸漸養成了點選側邊欄當前路由來重新整理頁面的習慣。但現在 spa 就不一樣了,使用者點選當前高亮的路由並不會重新整理view,因為vue-router會攔截你的路由,它判斷你的url並沒有任何變化,所以它不會觸發任何鉤子或者是view的變化。issue地址,社群也對該問題展開了激烈討論。

手摸手,帶你用vue擼後臺 系列三(實戰篇)
尤大本來也說要增加一個方法來強刷view,但後來他又改變了心意/(ㄒoㄒ)/~~。但需要就擺在這裡,我們該怎麼辦呢?他說了不改變current URL 就不會觸發任何東西,那我可不可以強行觸發東西你?上有政策, 下有對策我們變著花來hack。方法也很簡單,通過不斷改變urlquery來觸發view的變化。我們監聽側邊欄每個link 的 click事件,每次點選都給router push 一個不一樣的query 來確保會重新重新整理view。

clickLink(path) {
  this.$router.push({
    path,
    query: {
      t: +new Date() //保證每次點選路由的query項都是不一樣的,確保會重新重新整理view
    }
  })
}

複製程式碼

但這也有一個弊端就是 url 後面有一個很難看的 query 字尾如 xxx.com/article/list?t=1496832345025,但我司使用者們表示能接受。。。只能暫時這樣hack了,不知道大家有沒有更好的方法,學習學習。


Table

經過好幾個版本的迭代,element-ui 的table元件已經能滿足大部分業務需求了。不過rowSpan colSpan表格行/列合併現在並不是支援(element-ui2.0版本之後開始支援)。官方對此功能的更新情況可以關注這個issue

這裡我著重講一下table表格幾個常用的業務形態。

Table 拖拽排序

手摸手,帶你用vue擼後臺 系列三(實戰篇)
這裡主要是基於Sortable

import Sortable from 'sortablejs'
let el = document.querySelectorAll('.el-table__body-wrapper > table > tbody')[0]
let sortable = Sortable.create(el)
複製程式碼

在table mounted之後申明Sortable.create(el) table的每行tr就可以隨意拖拽了,麻煩的目前我們的排序都是基於dom的,我們的資料層list並沒有隨之改變。所以我們就要手動的來管理我們的列表。

this.sortable = Sortable.create(el, {
  onEnd: evt => { //監聽end事件 手動維護列表
    const tempIndex = this.newList.splice(evt.oldIndex, 1)[0];
    this.newList.splice(evt.newIndex, 0, tempIndex);
  }
});
複製程式碼

這樣我們就簡單的完成了 table 拖拽排序。這裡如果不是基於 dom 的排序推薦使用Vue.Draggable完整程式碼


Table 內聯編輯

table內聯編輯也是一個常見的需求。

手摸手,帶你用vue擼後臺 系列三(實戰篇)
其實也很簡單,當我們拿到 list 資料之後先洗一下資料,每一條資料裡面插入一個edit[ true or false ]判斷符,來表示當前行是否處於編輯狀態。之後就是通過v-show動態切換不同的相應view就可以了。完整程式碼

<el-table-column min-width="300px" label="標題">
  <template scope="scope">
    <el-input v-show="scope.row.edit" size="small" v-model="scope.row.title"></el-input>
    <span v-show="!scope.row.edit">{{ scope.row.title }}</span>
  </template>
</el-table-column>
<el-table-column align="center" label="編輯" width="120">
  <template scope="scope">
    <el-button v-show='!scope.row.edit' type="primary" @click='scope.row.edit=true' size="small" icon="edit">編輯</el-button>
    <el-button v-show='scope.row.edit' type="success" @click='scope.row.edit=false' size="small" icon="check">完成</el-button>
  </template>
</el-table-column>

複製程式碼

Table 常見坑

通過dialog來編輯,新建,刪除table的元素這種業務場景相對於前面說的兩種更加的常見。而且也有不少的小坑。 首先我們要明確一個點 vue 是一個MVVM框架,我們傳統寫程式碼是指令式程式設計,拿到table這個dom之後就是命令式對dom增刪改。而我們現在用宣告式程式設計,只用關注data的變化就好了,所以我們這裡的增刪改都是基於list這個陣列來的。這裡我們還要明確一點vue 列表渲染注意事項

由於 JavaScript 的限制, Vue 不能檢測以下變動的陣列: * 當你利用索引直接設定一個項時,例如: vm.items[indexOfItem] = newValue

所以我們想改變table中第一條資料的值,通過this.list[0]=newValue這樣是不會生效的。

解決方案:
// Array.prototype.splice`
example1.items.splice(indexOfItem, 1, newValue)
複製程式碼

所以我們可以通過

//新增資料
this.list.unshift(this.temp);

//刪除資料 
const index = this.list.indexOf(row); //找到要刪除資料在list中的位置
this.list.splice(index, 1); //通過splice 刪除資料

//修改資料
const index = this.list.indexOf(row); //找到修改的資料在list中的位置
this.list.splice(index, 1,this.updatedData); //通過splice 替換資料 觸發檢視更新
複製程式碼

這樣我們就完成了對table的增刪改操作,列表view也自動響應發生了變化。這裡在修改資料的時候還有一個小坑需要主要。 當我們拿到需要修改行的資料時候不能直接將它直接賦值給dialog,不然會發生下面的問題。

手摸手,帶你用vue擼後臺 系列三(實戰篇)
如上圖所示,我們在dialog裡面改變狀態的時候,遮罩下面的table裡面該行的狀態也在那裡跟著一隻變化著。原因想必大家都猜到了。賦值的資料是一個objec引用型別共享一個記憶體區域的。所以我們就不能直接連等複製,需要重新指向一個新的引用,方案如下:

//賦值物件是一個obj
this.objData=Object.assign({}, row) //這樣就不會共用同一個物件

//陣列我們也有一個巧妙的防範
newArray = oldArray.slice(); //slice會clone返回一個新陣列
複製程式碼

Tabs

tab在後臺專案中也比較常用的。假設我們有四個tab選項,每個tab都會向後端請求資料,但我們希望一開始只會請求當前的tab資料,而且tab來回切換的時候不會重複請求,只會例項化一次。首先我們想到的就是用v-if 這樣的確能做到一開始不會掛載後面的tab,但有一個問題,每次點選這個tab元件都會重新掛載一次,這是我們不想看到的,這時候我們就可以用到<keep-alive>了。

keep-alive 包裹動態元件時,會快取不活動的元件例項,而不是銷燬它們。 它是一個抽象元件:它自身不會渲染一個 DOM 元素,也不會出現在父元件鏈中。

所以我們就可以這樣寫tabs了

<el-tabs v-model="activeTab">
  <el-tab-pane label="簡介及公告" name="announcement">
    <announcement />
  </el-tab-pane>
  <el-tab-pane label="資訊" name="information">
    <keep-alive>
      <information v-if="activeTab=='information'" />
    </keep-alive>
  </el-tab-pane>
  <el-tab-pane label="直播流配置" name="stream">
    <keep-alive>
      <stream v-if="activeTab=='stream'" />
    </keep-alive>
  </el-tab-pane>
</el-tabs>
複製程式碼

Select 選擇器

Select 選擇器直接使用沒有什麼太多問題,但很多時候我們需要通過Select來回顯一些資料,當我們<el-select v-model="objValue"> select 繫結一個obj value回顯就會很蛋疼了,它要求必須保持同一個引用issue。這就意味著,我們回顯資料的時候想先要找到該資料在arr中的位置,再回塞:demo。這還不是在遠端搜尋的情況下,如果是遠端搜尋的情況還要當疼。 這裡推薦一下vue-multiselect 它能完美的解決前面Element select的問題。目前也是vue component 中比較好用的一個,ui也非常的好看,建議大家可以嘗試性用一下,真的非常的不錯。


Upload 上傳

Upload本身沒什麼好說的,文件寫的蠻清楚了。這裡主要說一下怎麼將Upload元件和七牛直傳結合在一起。

這裡我們選擇api直傳的方式,就是我們首先要通過後端(go,node,php都可以)文件生成七牛上傳必要的token(上傳憑證)和key(資源的最終名稱)。 所以現在只要想辦法講token和key塞進post請求裡面就可以了,好在官方也提供了這個方法。

手摸手,帶你用vue擼後臺 系列三(實戰篇)
。但怎麼才能先非同步的拿到token再將它塞入請求裡呢?

手摸手,帶你用vue擼後臺 系列三(實戰篇)
這時候我們又發現了before-upload 這個鉤子還支援promise簡直合我們的心意。 但我們寫著寫著怎樣才能動態的改變之前的dataObj呢?通過看原始碼發現我們可以_self._data這樣子拿到我們想要的資料。線上程式碼

<template>
  <el-upload
      action="https://upload.qbox.me"
      :data="dataObj"
      drag
      :multiple="true"
      :before-upload="beforeUpload">
    <i class="el-icon-upload"></i>
    <div class="el-upload__text">將檔案拖到此處,或<em>點選上傳</em></div>
  </el-upload>
</template>
<script>
    import { getToken } from 'api/qiniu'; // 獲取七牛token 後端通過Access Key,Secret Key,bucket等生成token
    // 七牛官方sdk https://developer.qiniu.com/sdk#official-sdk
    export default{
      data() {
        return {
          dataObj: { token: '', key: '' },
          image_uri: [],
          fileList: []
        }
      },
      methods: {
        beforeUpload() {
          const _self = this;
          return new Promise((resolve, reject) => {
            getToken().then(response => {
              const key = response.data.qiniu_key;
              const token = response.data.qiniu_token;
              _self._data.dataObj.token = token;
              _self._data.dataObj.key = key;
              resolve(true);
            }).catch(err => {
              console.log(err)
              reject(false)
            });
          });
        }
      }
    }
</script>

複製程式碼

jsx

在使用Element的時候,官方提供了很多可以自己寫render function的地方,但由於Element內部都是用jsx 寫render function的,所以demo也都是jsx,但很多人自己專案中其實是沒有安裝的,導致報錯。但說真的用createElement裸寫render 函式還是有些蛋疼。我們要用jsx,首先要安裝 babel-plugin-transform-vue-jsx 安裝方法如下:

npm install\
  babel-plugin-syntax-jsx\
  babel-plugin-transform-vue-jsx\
  babel-helper-vue-jsx-merge-props\
  babel-preset-es2015\
  --save-dev
  
複製程式碼

.babelrc:檔案

{
  "presets": ["es2015"],
  "plugins": ["transform-vue-jsx"]
}
複製程式碼

這樣我們就可以愉快的使用 jsx 寫render function了。


element 常見問題

**click事件不觸發問題:**一直有人在群裡問<el-input @click="handlenClick">Click Me</el-input>怎麼不觸發click事件,雖然element文件還有完善的空間但這種問題大家還真要自己好好認真看一下官方的FAQ了。

官方說明了所有的原生事件必須新增 .native 修飾符。

修改element樣式問題: 用ui元件總免不了需要對它做一些個性化定製的需求,所以我們就要覆蓋element的一些樣式。 首先我們要了解一下vue scoped是什麼,很多人非常喜歡用scoped,媽媽再也不用擔心樣式衝突問題了,其實scoped也沒有很神祕的,它就是基於PostCss的,加了一個作用局的概念。

//編譯前
.example {
  color: red;
}
//編譯後
.example[_v-f3f3eg9] {
  color: red;
}
複製程式碼

它和我們傳統的名稱空間的方法避免css衝突沒有什麼本質性的區別。 現在我們來說說怎麼覆蓋element-ui樣式。由於element-ui的樣式我們是在全域性引入的,所以你想在某個view裡面覆蓋它的樣式就不能加scoped,但你又想只覆蓋這個頁面的element樣式,你就可在它的父級加一個class,以用名稱空間來解決問題。

.aritle-page{ //你的名稱空間
    .el-tag { //element-ui 元素
      margin-right: 0px;
    }
}
複製程式碼

建議向樓主一樣專門建一個scss檔案裡專門自定義element-ui的各種樣式。線上程式碼

其它關於element相關的東西真的沒有什麼好說的了,人家文件和原始碼就放在那裡,有問題就去看文件,再去issue裡找找,再去看看原始碼,大部分問題都能解決了。給一個訣竅其實大部分詭異的問題都可以通過加一個key或者 Vue.nextTick來解決。。


富文字

管理後臺富文字也是一個非常重要的功能,樓主在這裡也踩了不少的坑。樓主在專案裡最終選擇了 tinymce

這裡在簡述一下推薦使用tinymce的原因:tinymce 是一家老牌做富文字的公司(這裡也推薦 ckeditor,也是一家一直做富文字的公司,新版本很不錯),它的產品經受了市場的認可,不管是文件還是配置的自由度都很好。在使用富文字的時候有一點也很關鍵就是複製格式化,之前在用一款韓國人做的富文字summernote被它的格式化坑的死去活來,但 tinymce 的去格式化相當的好,它還有一個增值專案就是powerpaste,那是無比的強大,支援從word裡面複製各種東西,都不會有問題。富文字還有一點也很關鍵,就是擴充性。樓主用tinymce寫了好幾個外掛,學習成本和容易度都不錯,很方便擴充。最後一點就是文件很完善,基本你想得到的配置項,它都有。tinymce也支援按需載入,你可以通過它官方的build頁定製自己需要的plugins。 我再來分析一下市面上其它的一些富文字:

  • summernote 先來說一個我絕對不推薦的富文字。這是一個韓國人開源的富文字(當然不推薦的理由不是因為這個),它對很多富文字業界公認的預設行為理解是反起到而行的,而且只為用了一個dialog的功能,引入了boostrap,一堆人抗議就是不改。格式化也是差勁。。反正不要用!不要用!不要用!
  • ckeditor ckeditor也是一家老牌做富文字的公司,樓主舊版後臺用的就是這個,今年也出了5.0版本,ui也變美觀了不少,相當的不錯,而且它號稱是外掛最豐富的富文字了。推薦大家也可以試用一下。
  • quill 也是一個非常火的富文字,長相很不錯。基於它寫外掛也很簡單,api設計也很簡單。樓主不選擇它的原因是它對圖片的各種操作不友善,而且很難改。如果對圖片沒什麼操作的使用者,推薦使用。
  • medium-editor 大名鼎鼎的medium的富文字(非官方出品),但完成度還是不很不錯,擴充性也不錯。不過我覺得大部分使用者還是會不習慣medium這種寫作方式的。
  • Squire 一個比較輕量的富文字,壓縮完才11.5kb,相對於其它的富文字來說是非常的小了,推薦功能不復雜的建議使用。
  • wangEditor 一個國人寫的富文字,用過感覺還是不錯的。不過畢竟是個人的,不像專門公司做富文字的,配置型和豐富性不足。前端幾大禁忌就有富文字 為什麼都說富文字編輯器是天坑?,不過個人能做成這樣子很不容易了。
  • 百度UEditor 沒有深入使用過,只在一個angular1X的專案簡單用過,不過說著的ui真的不好看,不符合當今審美了,官方也已經很久沒跟新過了。

樓主列舉了很多富文字但並沒有列舉任何 vue 相關的富文字,主要是因為富文字真的比想象中複雜,在前面的文章裡也說過了,其實用 vue 封裝元件很方便的,沒必要去用人家封裝的東西什麼vue-quill vue-editor這種都只是簡單包了一層,沒什麼難度的。還不如自己來封裝,靈活性可控性更強一點。還有一點基於 vue 真沒什麼好的富文字,不像 react 有 facebook 出的 draft-js,ory 出的 editor,這種大廠出的產品。

當然你也可以選擇一些付費的富文字編輯器,作者自己公司裡面有一個專案就使用了 froala-editor 這款編輯器。不管是美觀和易用性都是不錯的,公司買的是專業版,一年也就 $349 ,價格也是很合理的,但其實省去的程式設計師開發陳本可能遠不止這個價錢。

Tinymce

這裡來簡單講一下在自己專案中使用 Tinymce 的方法。

由於目前使用 npm 安裝 Tinymce 方法比較負責複雜而且還有一些問題(日後可能會採用該模式)。:space_invader:

目前採用全域性引用的方式。程式碼地址:static/tinymce static目錄下的檔案不會被打包, 在 index.html 中引入。

使用 由於富文字不適合雙向資料流,所以只會 watch 傳入富文字的內容一次變化,只會就不會再監聽了,如果之後還有改變富文字內容的需求。 可以通過 this.refs.xxx.setContent() 來設定

原始碼也很簡單,有任何別的需求都可以在 @/components/Tinymce/index.vue 中自行修改。


Markdown

markdown 我們這裡選用了 simplemde-markdown-editor ,簡單的用vue封裝了一下地址,如果需求方能接受 markdown 就一定要用 markdown,坑真心會比富文字少很多。這裡我們用markdown做了編輯器,還需要一個能解析的的東西。可以你傳給後端讓後端幫你轉化,也可以前端自己來,這裡推薦一個轉化庫showdown。使用方法:

import('showdown').then(showdown => { //用了 Dynamic import
  const converter = new showdown.Converter();//初始化
  this.html = converter.makeHtml(this.content)//轉化
})
複製程式碼

用法也很簡單兩行程式碼就完成了markdown to html,當然它還有很多個性畫的配置,大家有需求自行研究吧。

手摸手,帶你用vue擼後臺 系列三(實戰篇)


匯出excel

這裡先明確一點,如果你的業務需求對匯出檔案的格式沒有什麼要求,不建議匯出成xlsx格式的,直接匯出成csv的就好了,真的會簡單很多。建立一個a標籤,寫上data:text/csv;charset=utf-8頭,再把資料塞進去,encodeURI(csvContent)一下就好了,詳情就不展開了,大家可以借鑑這個stackoverflow回答。 我們重點說一下轉xlsx,我們這裡用到了js-xlsx,一個功能很強大excel處理庫,只是下載各種格式excel,還支援讀取excel,但上手難度也非常大,相當的複雜,其中涉及不少二進位制相關的東西。不過好在官方給了我們一個demo例子,我們寫不來還抄不來麼,於是我們就借鑑官方的例子來改造了一下,具體原理就不詳細說了,真的很複雜。。。 重點是我們怎麼使用!首先我們封裝一個Export2Excel.js, 它又依賴三個庫

require('script-loader!file-saver'); //儲存檔案用
require('script-loader!vendor/Blob'); //轉二進位制用
require('script-loader!xlsx/dist/xlsx.core.min'); //xlsx核心

由於這幾個檔案不支援import引入,所以我們需要`script-loader`來將他們掛載到全域性環境下。
複製程式碼

它暴露了兩個介面export_table_to_excelexport_json_to_excel,我們常用export_json_to_excel因為更加的可控一點,我們可以自由的洗資料。

handleDownload() {
  require.ensure([], () => { // 用 webpack Code Splitting xlsl還是很大的
    const { export_json_to_excel } = require('vendor/Export2Excel');
    const tHeader = ['序號', '文章標題', '作者', '閱讀數', '釋出時間']; // excel 表格頭
    const filterVal = ['id', 'title', 'author', 'pageviews', 'display_time'];
    const list = this.list;
    const data = this.formatJson(filterVal, list); // 自行洗資料 按序排序的一個array陣列
    export_json_to_excel(tHeader, data, '列表excel');
  })
},
formatJson(filterVal, jsonData) {
  return jsonData.map(v => filterVal.map(j => v[j]))
}

複製程式碼

完整顯示線上程式碼


ECharts

管理後臺圖表也是常見得需求。這裡圖表就只推薦ECharts,功能齊全,社群demo也豐富gallery。我還是那個觀點,大部分外掛建議大家還是自己用vue來包裝就好了,真的很簡單。ECharts支援webpack引入,圖省事可以將ECharts整個引入var echarts = require('echarts');不過ECharts還是不小的,我們大部分情況只是用到很少一部分功能,我平時習慣於按需引入的。

// 引入 ECharts 主模組
var echarts = require('echarts/lib/echarts');
// 引入柱狀圖
require('echarts/lib/chart/bar');
// 引入提示框和標題元件
require('echarts/lib/component/tooltip');
require('echarts/lib/component/title');
複製程式碼

webpack中使用ECharts文件 ECharts按需引入模組文件 接下來我們就要在vue中宣告初始化ECharts了。因為ECharts初始化必須繫結dom,所以我們只能在vue的mounted生命週期裡初始化。

mounted() {
  this.initCharts();
},
methods: {
  this.initCharts() {
    this.chart = echarts.init(this.$el);
    this.setOptions();
  },
  setOptions() {
    this.chart.setOption({
      title: {
        text: 'ECharts 入門示例'
      },
      tooltip: {},
      xAxis: {
        data: ["襯衫", "羊毛衫", "雪紡衫", "褲子", "高跟鞋", "襪子"]
      },
      yAxis: {},
      series: [{
        name: '銷量',
        type: 'bar',
        data: [5, 20, 36, 10, 10, 20]
      }]
    })
  }
}

複製程式碼

就這樣簡單,ECharts就配置完成了,這時候你想說我的data是遠端獲取的,或者說我動態改變ECharts的配置該怎麼辦呢?我們可以通過watch來觸發setOptions方法

//第一種 watch options變化 利用vue的深度 watcher,options一有變化就重新setOption
watch: {
  options: {
    handler(options) {
      this.chart.setOption(this.options)
    },
    deep: true
  },
}
//第二種 只watch 資料的變化 只有資料變化時觸發ECharts
watch: {
  seriesData(val) {
    this.setOptions({series:val})
  }
}

複製程式碼

其實都差不多,還是要結合自己業務來封裝。後面就和平時使用ECharts沒有什麼區別了。題外話ECharts的可配置項真心多,大家使用的時候可能要花一點時間瞭解它的api的。知乎有個問題:百度還有什麼比較良心的產品?答案:ECharts,可見ECharts的強大與好用。


相同component 不同引數

建立與編輯 其實後臺建立與編輯功能是最常見的了,它區別去前臺專案多了改的需求,但大部分建立頁面與編輯頁面欄位和ui幾乎是一樣的,所以我們準備公用一個component來對應不同的頁面。有兩種常見的方法,來區別建立與編輯。

  1. 通過路由path的方式 這種方式最簡單暴力,我自己的專案中使用這種方式,通過約定路徑中出現'edit'就判斷為編輯模式。比較省力和方便,不過這是要在大家寫路徑的時候都按照規範來寫的前提下。
  2. 通過meta來區分 比較推薦這種方式來區分。
    手摸手,帶你用vue擼後臺 系列三(實戰篇)
computed: {
  isEdit() {
    return this.$route.meta.isEdit // 根據meta判斷
    // return this.$route.path.indexOf('edit') !== -1 // 根據路由判斷
  }
},
created() {
  if (this.isEdit) { 
    this.fetchData();
  }
},
複製程式碼

就這樣簡單的實現了多路由複用了一個component,其實不只是建立和編輯可以這樣用,如兩個列表的一模一樣,只是一個是內部文章另一個是調取外部文章都能複用元件,通過meta的方式來判斷調取不同的介面。


佔坑

常規佔坑,這裡是手摸手,帶你用vue擼後臺系列。
完整專案地址:vue-element-admin
系類文章一:手摸手,帶你用vue擼後臺 系列一(基礎篇)
系類文章二:手摸手,帶你用vue擼後臺 系列二(登入許可權篇)
系類文章三:手摸手,帶你用vue 擼後臺 系列三 (實戰篇)
系類文章四:手摸手,帶你用vue擼後臺 系列四(vueAdmin 一個極簡的後臺基礎模板)
系類文章:手摸手,帶你優雅的使用 icon
系類文章:手摸手,帶你封裝一個vue component
樓主個人免費圈子

相關文章