vue-element-admin:一個后臺前端解決方案

官方文檔

功能

- 登錄 / 注銷

- 權限驗證
  - 頁面權限
  - 指令權限
  - 權限配置
  - 二步登錄

- 多環(huán)境發(fā)布
  - dev sit stage prod

- 全局功能
  - 國際化多語言
  - 多種動態(tài)換膚
  - 動態(tài)側邊欄(支持多級路由嵌套)
  - 動態(tài)面包屑
  - 快捷導航(標簽頁)
  - Svg Sprite 圖標
  - 本地/后端 mock 數(shù)據(jù)
  - Screenfull全屏
  - 自適應收縮側邊欄

- 編輯器
  - 富文本
  - Markdown
  - JSON 等多格式

- Excel
  - 導出excel
  - 導入excel
  - 前端可視化excel
  - 導出zip

- 表格
  - 動態(tài)表格
  - 拖拽表格
  - 內(nèi)聯(lián)編輯

- 錯誤頁面
  - 401
  - 404

- 組件
  - 頭像上傳
  - 返回頂部
  - 拖拽Dialog
  - 拖拽Select
  - 拖拽看板
  - 列表拖拽
  - SplitPane
  - Dropzone
  - Sticky
  - CountTo

- 綜合實例
- 錯誤日志
- Dashboard
- 引導頁
- ECharts 圖表
- Clipboard(剪貼復制)
- Markdown2html

前序準備

項目技術?;?ES2015+、vuevuex、vue-router 、vue-cli 、axioselement-ui,所有的請求數(shù)據(jù)都使用Mock.js進行模擬。

項目代碼

vue-element-admin

目錄結構

├── build                      # 構建相關
├── mock                       # 項目mock 模擬數(shù)據(jù)
├── plop-templates             # 基本模板
├── public                     # 靜態(tài)資源
│   │── favicon.ico            # favicon圖標
│   └── index.html             # html模板
├── src                        # 源代碼
│   ├── api                    # 所有請求
│   ├── assets                 # 主題 字體等靜態(tài)資源
│   ├── components             # 全局公用組件
│   ├── directive              # 全局指令
│   ├── filters                # 全局 filter
│   ├── icons                  # 項目所有 svg icons
│   ├── lang                   # 國際化 language
│   ├── layout                 # 全局 layout
│   ├── router                 # 路由
│   ├── store                  # 全局 store管理
│   ├── styles                 # 全局樣式
│   ├── utils                  # 全局公用方法
│   ├── vendor                 # 公用vendor
│   ├── views                  # views 所有頁面
│   ├── App.vue                # 入口頁面
│   ├── main.js                # 入口文件 加載組件 初始化等
│   └── permission.js          # 權限管理
├── tests                      # 測試
├── .env.xxx                   # 環(huán)境變量配置
├── .eslintrc.js               # eslint 配置項
├── .babelrc                   # babel-loader 配置
├── .travis.yml                # 自動化CI配置
├── vue.config.js              # vue-cli 配置
├── postcss.config.js          # postcss 配置
└── package.json               # package.json

開發(fā):

工具安裝:npm包管理工具,git 代碼管理

   # 克隆項目
   `git clone https://github.com/PanJiaChen/vue-element-admin.git`
   # 進入項目目錄
   `cd vue-element-admin`
   # 安裝依賴
   `npm install`
   # 建議不要直接使用 cnpm 安裝依賴,會有各種詭異的 bug。
   # 可以通過如下淘寶鏡像解決 npm 下載速度慢的問題
   `npm install --registry=https://registry.npm.taobao.org`
   # 項目運行
   `npm run dev`

瀏覽器訪問 http://localhost:9527

若出現(xiàn)node-sass安裝錯誤,項目根目錄新建文件.npmrc,并添加如下代碼后重新安裝。

sass_binary_site=https://npm.taobao.org/mirrors/node-sass/
registry=https://registry.npm.taobao.org

路由和側邊欄

本項目側邊欄和路由是綁定在一起的,所以你只有在 @/router/index.js 下面配置對應的路由,側邊欄就能動態(tài)的生成了。

配置項

//當設置 true 的時候該路由不會在側邊欄出現(xiàn) 如401,login等頁面,或者如一些編輯頁面/edit/1
hidden: true // (默認 false)

//當設置 noRedirect 的時候該路由在面包屑導航中不可被點擊
redirect: 'noRedirect'

//當你一個路由下面的 children 聲明的路由大于1個時,自動會變成嵌套的模式--如組件頁面
//只有一個時,會將那個子路由當做根路由顯示在側邊欄--如引導頁面
//若你想不管路由下面的 children 聲明的個數(shù)都顯示你的根路由
//你可以設置 alwaysShow: true,這樣它就會忽略之前定義的規(guī)則,一直顯示根路由
alwaysShow: true

name: 'router-name' //設定路由的名字,一定要填寫不然使用<keep-alive>時會出現(xiàn)各種問題
meta: {
  roles: ['admin', 'editor'] //設置該路由進入的權限,支持多個權限疊加
  title: 'title' //設置該路由在側邊欄和面包屑中展示的名字
  icon: 'svg-name' //設置該路由的圖標
  noCache: true //如果設置為true,則不會被 <keep-alive> 緩存(默認 false)
  breadcrumb: false // 如果設置為false,則不會在breadcrumb面包屑中顯示
}
示例:
{
  path: '/permission',
  component: Layout,
  redirect: '/permission/index', //重定向地址,在面包屑中點擊會重定向去的地址
  hidden: true, // 不在側邊欄線上
  alwaysShow: true, //一直顯示根路由
  meta: { roles: ['admin','editor'] }, //你可以在根路由設置權限,這樣它下面所以的子路由都繼承了這個權限
  children: [{
    path: 'index',
    component: ()=>import('permission/index'),
    name: 'permission',
    meta: {
      title: 'permission',
      icon: 'lock', //圖標
      role: ['admin','editor'], //或者你可以給每一個子路由設置自己的權限
      noCache: true // 不會被 <keep-alive> 緩存
    }
  }]
}

路由

這里的路由分為兩種,constantRoutesasyncRoutes。
constantRoutes: 代表那些不需要動態(tài)判斷權限的路由,如登錄頁、404、等通用頁面。
asyncRoutes: 代表那些需求動態(tài)判斷權限并通過 addRoutes 動態(tài)添加的頁面。

側邊欄

代碼地址:@/views/layout/components/Sidebar

  • 側邊欄有兩種形式:submenu 和 直接 el-menu-item。 一個是嵌套子菜單,另一個則是直接一個鏈接。
    • 當你一個路由下面的 children 聲明的路由大于>1 個時,自動會變成嵌套的模式。
    • 如果子路由正好等于一個就會默認將子路由作為根路由顯示在側邊欄中。若不想這樣,可以通過設置在根路由中設置 alwaysShow: true 來取消這一特性。
  • 所有的 css 都可以在 @/styles/sidebar.scss 中找到,可以根據(jù)自己的需求進行修改。
  • unique-opened
    你可以在Sidebar/index.vue中設置unique-opened來控制側邊欄,是否只保持一個子菜單的展開。

多級目錄(嵌套路由)

如果路由是多級目錄,如本項目 @/views/nested 那樣, 有三級路由嵌套的情況下,不要忘記還要手動在二級目錄的根文件下添加一個 <router-view>
如:@/views/nested/menu1/index.vue,原則上有多少級路由嵌套就需要多少個<router-view>

點擊側邊欄 刷新當前路由

  • 方法1:監(jiān)聽側邊欄每個 link 的 click 事件
clickLink(path) {
  this.$router.push({
    path,
    query: {
      t: +new Date() //保證每次點擊路由的query項都是不一樣的,確保會重新刷新view
    }
  })
}

ps:不要忘了在 router-view 加上一個特定唯一的 key,如 <router-view :key="$route.path"></router-view>, 但這也有一個弊端就是 url 后面有一個很難看的 query 后綴如 xxx.com/article/list?t=1496832345025

  • 方法2:判斷當前點擊的菜單路由和當前的路由是否一致,一致的時候,會先跳轉到一個專門 Redirect 的頁面,它會將路由重定向到我想去的頁面,這樣就起到了刷新的效果了。

點擊的時候重定向頁面至 /redirect

const { fullPath } = this.$route
this.$router.replace({
  path: '/redirect' + fullPath
})

redirect 頁面在重定向回原始頁面

// redirect.vue
// https://github.com/PanJiaChen/vue-element-admin/blob/master/src/views/redirect/index.vue
export default {
  beforeCreate() {
    const { params, query } = this.$route
    const { path } = params
    this.$router.replace({ path: '/' + path, query })
  },
  render: function(h) {
    return h() // avoid warning message
  }
}

內(nèi)容模塊:

頁面整體結構

@ 是 webpack 的 alias 配置,對應本項目vue.config.js > configureWebpack > alias配置。

布局: @/layout

對應上圖頁面整體結構,可根據(jù)需求自行修改樣式

內(nèi)容區(qū):@/layout/components/AppMain
  • <transition>標簽是定義了頁面之間切換動畫,可在transition.scss文件中添加修改動畫;
  • <keep-alive>標簽主要是為了緩存 <router-view>路由,配合頁面的 tabs-view 標簽導航使用,如不需要可刪除。
  • <router-view>子頁面渲染處,根據(jù)路由children下配置

新增頁面步驟:

  1. 新增路由
{
  path: '/excel',
  component: Layout,
  redirect: '/excel/export-excel',
  name: 'excel',
  meta: {
    title: 'excel',
    icon: 'excel'
  },
  children: [
    {
      path: 'export-excel',
      component: ()=>import('excel/exportExcel'),
      name: 'exportExcel',
      meta: { title: 'exportExcel' }
    }
  ]
}
  1. 新增View

在 @/views 文件下 創(chuàng)建對應的文件夾,一般一個路由對應一個文件,該模塊下的功能組件或者方法就建議在本文件夾下創(chuàng)建一個 utilscomponents 文件夾,各個功能模塊維護自己的 utilscomponents 組件。

  1. 新增api

@/api

  1. 新增組件

@/components:公共組件

  1. 新增樣式

@/style:全局樣式,加 scoped 可防止樣式全局污染

  • 自定義 element-ui 樣式
.article-page {
  /* 你的命名空間 */
  .el-tag {
    /* element-ui 元素*/
    margin-right: 0px;
  }
}
  • 父組件改變子組件樣式 深度選擇器
<style scoped>
.a >>> .b { /* ... */ }
</style>

總結:不管組件還是樣式,公共放外部,私有放內(nèi)部,方便維護

Postcss

新版的 vue-cli webpack 模板 init 之后根目錄下默認有一個postcss.config.js。vue-loader 的 postcss 會默認讀取這個文件的里的配置項,所以在這里直接改配置文件就可以了。配置和 postcss是一樣的。

// postcss.config.js
module.exports = {
  plugins: {
    autoprefixer: {}
  }
}

// package.json
"browserslist": [
    "> 1%",
    "last 2 versions",
    "not ie <= 8"
  ]

如上面代碼所述的,autoprefixer 會去讀取 package.json 下 browserslist 的配置參數(shù)

  • > 1% 兼容全球使用率大于 1%的瀏覽器
  • last 2 versions 兼容每個瀏覽器的最近兩個版本
  • not ie <= 8 不兼容 ie8 及以下

具體可見 browserslist

Mixin

本項目沒有設置自動注入 sass 的 mixin 到全局,所以需要在使用的地方手動引入 mixin

<style rel="stylesheet/scss" lang="scss">
  @import "src/styles/mixin.scss";
</style>

如需要自動將 mixin 注入到全局 ,可以使用sass-resources-loader

和服務端進行交互

前端請求流程

在 vue-element-admin 中,一個完整的前端 UI 交互到服務端處理流程是這樣的:

  1. UI 組件交互操作;
  2. 調(diào)用統(tǒng)一管理的 api service 請求函數(shù);
  3. 使用封裝的 request.js 發(fā)送請求;
  4. 獲取服務端返回;
  5. 更新 data;

為了方便管理維護,統(tǒng)一的請求處理都放在 @/src/api 文件夾中,并且一般按照 model 緯度進行拆分文件

api/
  login.js
  article.js
  remoteSearch.js
  ...
request.js

@/src/utils/request.js 是基于 axios 的封裝,便于統(tǒng)一處理 POST,GET 等請求參數(shù),請求頭,以及錯誤提示信息等。具體可以參看 request.js。 它封裝了全局 request攔截器、response攔截器、統(tǒng)一的錯誤處理統(tǒng)一做了超時處理、baseURL設置等

一個請求文章列表頁的例子:

// api/article.js
import request from '../utils/request';
export function fetchList(query) {
  return request({
    url: '/article/list',
    method: 'get',
    params: query
  })
}


// views/example/list
import { fetchList } from '@/api/article'
export default {
  data() {
    list: null,
    listLoading: true
  },
  methods: {
    fetchData() {
      this.listLoading = true
      fetchList().then(response => {
        this.list = response.data.items
        this.listLoading = false
      })
    }
  }
}
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容