Vue實戰技巧Element Table二次封裝

前言

因爲重構後臺管理項目中有好多表格頁面, 舉個栗子javascript

k-table-demo.png

這表格看着還挺好看,寫起來叫人直呼XX,多動腦子少掉髮,少走彎路多省鞋。css

寫了一個後感受太麻煩了,因而我奮筆疾書,利用Vue+Element Table從新封裝出了一套表格組件前端

3分鐘就能夠實現一個表格頁面的騷操做,你值得擁有vue

思考

看了表格我們簡單的把它們劃分了下功能區java

  • 最早入場的選手是最上方一排整整齊齊有點嚴肅的搜索功能區git

  • 其次入場的是略帶風騷的表格功能區github

  • 而後入場的是全能綜合性型選手錶格內容區域web

  • 最後入場的是有着長腿帶着長隊的分頁組件區域api

誒,讓咱們思考下最複雜的地方在哪裏?數組

下面我用個圖來標註下我們接下來須要拿下的高地

總的來講表格內容這一塊最不可控,他可能有多選、序號、圖片、狀態、時間、操做列......

k-table.png

實踐

我們把搜索層寫成一個組件filterPane.vue

把表格分紅一個組件tablePane.vue

表格組件tablePane.vue包括功能區、表格內容區、分頁

filterPane.vue

明確目標

搜索層通常包括日期選擇器、輸入框、select下拉選擇器等+搜索功能、重置功能

傳入數據結構整理

// 搜索欄組件
 filterData:{
   timeSelect:true,    //是否顯示日期控件
   elinput:[          
     {
       name:'姓名',    //提示語
       key:'userName',  //字段名
       width:100        //寬度
     }
   ],
   elselect:[
     {
       name:'部門',
       key:'department',
       width:100
       option:[{
         key:1,
         value:'技術部'
       }]
     }
   ]
 }
複製代碼

timeSelect

  • 類型 Boolean

是否顯示時間選擇器

elinput

  • 類型 Array

輸入框選項,子對象內

name爲輸入框的placeholder

key爲字段名

elselect

  • 類型 Array

select下拉框選項,子對象內

name爲輸入框的placeholder

key爲字段名

option爲select下拉選項

開始封裝

<template>
  <div> <div class="filter-container"> <el-date-picker v-if="filterData.timeSelect" v-model="dateRange" style="width: 300px" type="daterange" start-placeholder="開始日期" end-placeholder="結束日期" :default-time="['', '']" :picker-options="pickerOptions" class="filter-item" /> <template v-if="filterData.elinput"> <el-input v-for="(item,index) in filterData.elinput" :key="index" v-model="listQuery[item.key]" :placeholder="item.name" :style="{'width':item.width?item.width+'px':'200px'}" class="filter-item" /> </template> <template v-if="filterData.elselect"> <el-select v-for="(item,index) in filterData.elselect" :key="index" v-model="listQuery[item.key]" :placeholder="item.name" clearable :style="{'width':item.width?item.width+'px':'90px'}" class="filter-item" > <el-option v-for="i in item.option" :key="i.key" :label="i.value" :value="i.key" /> </el-select> </template> <div class="btn"> <el-button class="filter-item" type="primary" @click="handleSearch"> 搜索 </el-button> <el-button class="filter-item" type="warning" @click="handleRest"> 重置 </el-button> </div> </div> </div>
</template>
<script> // 搜索欄組件 // filterData:{ // timeSelect:true, // elinput:[ // { // name:'姓名', // key:'userName' // } // ], // elselect:[ // { // name:'部門', // key:'department' // option:[{ // key:1, // value:'技術部' // }] // } // ] // } export default { props: { // eslint-disable-next-line vue/require-default-prop filterData: { type: Object } }, data() { return { pickerOptions: { disabledDate(time) { return time.getTime() > Date.now() } }, dateRange: ['', ''], listQuery: {} } }, watch: { 'filterData'(val) { console.log(val) if (val.elinput.length > 0) { val.elinput.map(item => { this.listQuery[item.key] = '' }) } if (val.elselect.length > 0) { val.elinput.map(item => { this.listQuery[item.key] = '' }) } }, //緩存進頁面想清空可用 'filterData.rest': { handler: function(val) { if (val) { this.handleRest() } }, deep: true } }, methods: { handleSearch() { console.log('搜索成功', this.listQuery) const data = this.$global.deepClone(this.listQuery) if (this.dateRange && this.dateRange[0] !== '') { const startTime = this.$moment(this.dateRange[0]).format('YYYY-MM-DD') + ' 00:00:00' const endTime = this.$moment(this.dateRange[1]).format('YYYY-MM-DD') + ' 23:59:59' data.beginDate = startTime data.endDate = endTime } Object.keys(data).forEach(function(key) { if (data[key] === '') { delete data[key] } }) this.$emit('filterMsg', data) }, handleRest() { const data = this.$global.deepClone(this.listQuery) Object.keys(data).forEach(function(key) { data[key] = '' }) this.listQuery = data this.dateRange = ['', ''] console.log('重置成功', this.listQuery) } } } </script>

<style scoped lang='scss'> .filter-item{ margin-left: 10px; display: inline-block; } .filter-container .filter-item:nth-of-type(1){ margin-left: 0px; } .btn{ display: inline-block; margin-left: 10px; } </style>

複製代碼

tablePane.vue

明確目標

實現表格功能行、實現表格基本功能、實現分頁功能

傳入數據結構整理

dataSource: {
          tool:[
            {
              name: '新增用戶', //按鈕名稱
              key: 1,  // 惟一標識符
              permission: 2010106, // 權限點
              type: '',  // 使用element自帶按鈕類型
              bgColor: '#67c23a', // 自定義背景色
              handleClick: this.handleAdd //自定義事件
            },
          ]
         data: [], // 表格數據
         cols: [], // 表格的列數據
         isSelection: false, // 表格有多選時設置
         selectable: function(val) {//禁用部分行多選
          if (val.isVideoStatus === 1) {
            return false
          } else {
            return true
          }
        },
         handleSelectionChange:(val)=>{} //點擊行選中多選返回選中數組
         isOperation: true, // 表格有操做列時設置
         isIndex: true, // 列表序號
         loading: true, // loading
         pageData: {
          total: 0, // 總條數
          pageSize: 10, // 每頁數量
          pageNum: 1 // 頁碼
         }
         operation: {
           // 表格有操做列時設置
           label: '操做', // 列名
           width: '350', // 根據實際狀況給寬度
           data: [
             {
               label: '凍結', // 操做名稱
               permission:'' //權限點
               type: 'info', //按鈕類型
               handleRow: function(){} // 自定義事件
             },
           ]
         }
       },
複製代碼

tool

  • 類型 Array
  • 默認值 [ ]

配置表格工具列

dataSource: {
         tool:[
           {
             name: '新增用戶', //按鈕名稱
             key: 1,  // 惟一標識符
             permission: 2010106, // 權限點
             type: '',  // 使用element自帶按鈕類型
             bgColor: '#67c23a', // 自定義背景色
             handleClick: this.handleAdd //自定義事件
           },
         ]
  }
複製代碼

cols

  • 類型 Array
  • 默認值 [ ]

配置表頭

dataSource: {
         cols:[
            {
               label: '標題',                       //列名
               prop: 'belongUserId',                //字段名稱
               width: 100                           //列寬度
            },
            {
               label: '副標題(季)',
               prop: 'subtitle',
               isCodeTableFormatter: function(val) {//過濾器
                 if (val.subtitle === 0) {
                   return '無'
                 } else {
                   return val.subtitle
                 }
               },
               width: 100
            },
             {
               label: '建立時間',
               prop: 'createTime',
               isCodeTableFormatter: function(val) {//時間過濾器
                 return timeFormat(val.createTime)
               },
               width: 150
             }
         ]
  }
複製代碼

pageData

  • 類型 Object
  • 默認值 { }

配置分頁

dataSource: {
        pageData: {
         total: 0, // 總條數
         pageSize: 10, // 每頁數量
         pageNum: 1, // 頁碼
         pageSize:[5,10,15,20]// 每頁數量
        }
 }
複製代碼

operation

  • 類型 Object
  • 默認值 { }

配置操做列

dataSource: {
       operation: {
         // 表格有操做列時設置
         label: '操做', // 列名
         width: '350', // 根據實際狀況給寬度
         data: [
           {
             label: '修改', // 操做名稱
             permission:'1001' //權限點
             type: 'info', //按鈕類型icon爲圖表類型
             handleRow: function(){} // 自定義事件
           },
           {
             label: '修改', // 操做名稱
             permission:'1001' //權限點
             type: 'icon', //按鈕類型icon爲圖表類型
             icon:'el-icon-plus'
             handleRow: function(){} // 自定義事件
           }
         ]
       }
}
複製代碼

tablePane.vue配置項Cols詳解

  • 普通列
cols:[
   {
       label: '標題',
       prop: 'title',
       width: 200
    }
]
複製代碼
  • 普通列字體顏色改變
cols:[
  {
    label: '狀態',
    prop: 'status',
    isTemplate: function(val) {
      if (val === 1) {
        return '禁言中'
      } else {
        return '已解禁'
      }
    },
    isTemplateClass: function(val) {
      if (val === 1) {
        return 'color-red'
      } else {
        return 'color-green'
      }
    }
  }
]
複製代碼
  • 帶filter過濾器列
cols:[
   {
      label: '推送時間',
      prop: 'pushTime',
      isCodeTableFormatter: function(val) {
        return timeFormat(val.pushTime)
      }
    },
    {
      label: '狀態',
      prop: 'status',
      isCodeTableFormatter: function(val) {
        if(val.status===1){
          return '成功'
        }else{
          return '失敗'
        }
      }
    }
]
複製代碼
  • 帶圖標列
cols:[
  {
     label: '目標類型',
     prop: 'targetType',
     isIcon: true,
     filter: function(val) {
       if (val === 4) {
         return '特定用戶'
       } else if (val === 3) {
         return '新註冊用戶'
       } else if (val === 2) {
         return '標籤用戶'
       } else if (val === 1) {
         return '所有用戶'
       }
     },
     icon: function(val) {
       if (val === 4) {
         return 'el-icon-mobile'
       } else {
         return false
       }
     },
     handlerClick: this.handlerClick
   }
]
複製代碼

開始封裝

<template>
 <div> <div v-if="dataSource.tool" class="tool"> <el-button v-for="(item) in dataSource.tool" :key="item.key" v-permission="item.permission" class="filter-item" :style="{'background':item.bgColor,borderColor:item.bgColor}" :type="item.type || 'primary'" @click="item.handleClick(item.name,$event)" > {{ item.name }} </el-button> </div> <el-table ref="table" v-loading="dataSource.loading" style="width: 100%;" :class="{ 'no-data': !dataSource.data || !dataSource.data.length }" :data="dataSource.data" @row-click="getRowData" @selection-change="dataSource.handleSelectionChange" > <!-- 是否有多選 --> <el-table-column v-if="dataSource.isSelection" :selectable="dataSource.selectable" type="selection" :width="dataSource.selectionWidth || 50" align="center" /> <!-- 是否須要序號 --> <el-table-column v-if="dataSource.isIndex" type="index" label="序號" width="55" align="center" /> <template v-for="item in dataSource.cols"> <!-- 表格的列展現 特殊狀況處理 好比要輸入框 顯示圖片 --> <el-table-column v-if="item.isTemplate" :key="item.prop" v-bind="item" > <template slot-scope="scope"> <!-- 好比要輸入框 顯示圖片等等 本身定義 --> <slot :name="item.prop" :scope="scope" /> </template> </el-table-column> <!-- 須要特殊顏色顯示字體--> <el-table-column v-if="item.isSpecial" :key="item.prop" v-bind="item" align="center" > <template slot-scope="scope"> <span :class="item.isSpecialClass(scope.row[scope.column.property])">{{ item.isSpecial(scope.row[scope.column.property]) }}</span> </template> </el-table-column> <!-- 須要帶圖標的某列,帶回調事件--> <el-table-column v-if="item.isIcon" :key="item.prop" v-bind="item" align="center" > <template slot-scope="scope"> <span> <span>{{ item.filter(scope.row[scope.column.property]) }}</span> <i v-if="item.icon" :class="[item.icon(scope.row[scope.column.property]),'icon-normal']" @click="item.handlerClick(scope.row)" /> </span> <!-- 好比要輸入框 顯示圖片等等 本身定義 --> <slot :name="item.prop" :scope="scope" /> </template> </el-table-column> <!-- 圖片帶tooltip --> <el-table-column v-if="item.isImagePopover" :key="item.prop" v-bind="item" align="center" > <template slot-scope="scope"> <el-popover placement="right" title="" trigger="hover" > <img class="image-popover" :src="scope.row[scope.column.property]+'?x-oss-process=image/quality,q_60'" alt=""> <img slot="reference" class="reference-img" :src="scope.row[scope.column.property]+'?x-oss-process=image/quality,q_10'" alt=""> </el-popover> </template> </el-table-column> <!-- 大部分適用 --> <el-table-column v-if="!item.isImagePopover && !item.isTemplate && !item.isSpecial&&!item.isIcon" :key="item.prop" v-bind="item.isCodeTableFormatter ? Object.assign({ formatter: item.isCodeTableFormatter }, item) : item" align="center" show-overflow-tooltip /> </template> <!-- 是否有操做列 --> <!-- 沒有數據時候不固定列 --> <el-table-column v-if="dataSource.isOperation" :show-overflow-tooltip="dataSource.operation.overflowTooltip" v-bind="dataSource.data && dataSource.data.length ? { fixed: 'right' } : null" style="margin-right:20px" class-name="handle-td" label-class-name="tc" :width="dataSource.operation.width" :label="dataSource.operation.label" align="center" > <!-- UI統一一排放3個,4個以上出現更多 --> <template slot-scope="scope"> <!-- 三個一排的狀況,去掉隱藏的按鈕後的長度 --> <template v-if="dataSource.operation.data.length > 0"> <div class="btn"> <div v-for="(item) in dataSource.operation.data" :key="item.label"> <template v-if="item.type!=='icon'"> <el-button v-permission="item.permission" v-bind="item" :type="item.type?item.type:''" size="mini" @click.native.prevent="item.handleRow(scope.$index, scope.row, item.label)" > {{ item.label }} </el-button> </template> <template v-else> <i :class="[icon,item.icon]" v-bind="item" @click="item.handleRow(scope.$index, scope.row, item.label)" /> </template> </div> </div> </template> </template> </el-table-column> </el-table> <div class="page"> <el-pagination v-if="dataSource.pageData.total>0" :current-page="dataSource.pageData.pageNum" :page-sizes="dataSource.pageData.pageSizes?dataSource.pageData.pageSizes:[5,10,15,20]" :page-size="dataSource.pageData.pageSize" layout="total, sizes, prev, pager, next, jumper" :total="dataSource.pageData.total" @size-change="handleSizeChange" @current-change="handleCurrentChange" /> </div> </div> </template> <script> // dataSource: { // tool:[ // { // name: '新增用戶', //按鈕名稱 // key: 1, // 惟一標識符 // permission: 2010106, // 權限點 // type: '', // 使用element自帶按鈕類型 // bgColor: '#67c23a', // 自定義背景色 // handleClick: this.handleAdd //自定義事件 // }, // ] // data: [], // 表格數據 // cols: [], // 表格的列數據 // handleSelectionChange:(val)=>{} //點擊行選中多選返回選中數組 // isSelection: false, // 表格有多選時設置 // isOperation: true, // 表格有操做列時設置 // isIndex: true, // 列表序號 // loading: true, // loading // pageData: { // total: 0, // 總條數 // pageSize: 10, // 每頁數量 // pageNum: 1, // 頁碼 // pageSize:[5,10,15,20]// 每頁數量 // } // operation: { // // 表格有操做列時設置 // label: '操做', // 列名 // width: '350', // 根據實際狀況給寬度 // data: [ // { // label: '凍結', // 操做名稱 // permission:'' //權限點 // type: 'info', //按鈕類型 // handleRow: function(){} // 自定義事件 // }, // ] // } // }, export default { // 接收父組件傳遞過來的值 props: { // 表格數據和表格部分屬性的對象 // eslint-disable-next-line vue/require-default-prop dataSource: { type: Object } }, data() { return { } }, watch: { 'dataSource.cols': { // 監聽表格列變化 deep: true, handler() { // 解決表格列變更的抖動問題 this.$nextTick(this.$refs.table.doLayout) } } }, methods: { handleAdd(name) { console.log(name) this.$emit('toolMsg', name) }, handleRow(index, row, lable) { console.log(index, row, lable) }, handleSizeChange(val) { this.$emit('changeSize', val) console.log(`每頁 ${val} 條`) }, handleCurrentChange(val) { this.$emit('changeNum', val) console.log(`當前頁: ${val}`) }, // 點擊行便可選中 getRowData(row) { this.$refs.table.toggleRowSelection(row) } } } </script> <style lang="scss" scoped> .page{ margin-top: 20px; } .btn{ display: flex; justify-content: center; } .btn div{ margin-left: 5px; } .reference-img{ width: 40px; height: 40px; background-size:100% 100%; border-radius: 4px; } .image-popover{ width: 200px; height: 200px; background-size:100% 100%; } .icon { width: 25px; font-size: 20px; font-weight: bold; } </style> 複製代碼

實戰

配置某頁面,咱先看配置圖片是否是省事多了,並且條理清楚

1.png

2.png

<template>
  <div class="app-container"> <filter-pane :filter-data="filterData" @filterMsg="filterMsg" /> <table-pane :data-source="dataSource" @changeSize="changeSize" @changeNum="changeNum" /> <add :dialog-add="dialogAdd" @childMsg="childMsg" /> </div>
</template>
<script> import filterPane from '@/components/Table/filterPane' import tablePane from '@/components/Table/tablePane' import add from './components/add' import { getVersionList, delVersion } from '@/api/user' import { timeFormat } from '@/filters/index' export default { name: 'Suggestion', components: { filterPane, tablePane, add }, data() { return { // 搜索欄配置 filterData: { timeSelect: false, elselect: [ { name: '狀態', width: 120, key: 'platform', option: [ { key: '所有', value: '所有' }, { key: 1, value: 'IOS' }, { key: 2, value: '安卓' } ] } ] }, // 表格配置 dataSource: { tool: [{ name: '新增版本', key: 1, permission: 2010701, handleClick: this.handleAdd }], data: [], // 表格數據 cols: [ { label: '發佈時間', prop: 'appIssueTime', isCodeTableFormatter: function(val) { return timeFormat(val.appIssueTime) } }, { label: 'APP名稱', prop: 'appName' }, { label: 'APP版本', prop: 'appVersion' }, { label: '平臺', prop: 'appPlatform', isCodeTableFormatter: function(val) { if (val.appPlatform === 1) { return 'IOS' } else { return 'Android' } } }, { label: '是否自動更新', prop: 'appAutoUpdate', isCodeTableFormatter: function(val) { if (val.appAutoUpdate === 1) { return '是' } else { return '否' } } }, { label: '更新描述', prop: 'appDesc', width: 300 }, { label: '下載地址', prop: 'downloadAddr' }, { label: '發佈人', prop: 'userName' } ], // 表格的列數據 handleSelectionChange: this.handleSelectionChange, isSelection: false, // 表格有多選時設置 isOperation: true, // 表格有操做列時設置 isIndex: true, // 列表序號 loading: true, // loading pageData: { total: 0, // 總條數 pageSize: 10, // 每頁數量 pageNum: 1 // 頁碼 }, operation: { // 表格有操做列時設置 label: '操做', // 列名 width: '100', // 根據實際狀況給寬度 data: [ { label: '刪除', // 操做名稱 type: 'danger', permission: '2010702', // 後期這個操做的權限,用來控制權限 handleRow: this.handleRow } ] } }, dialogAdd: false, msg: {}, selected: [] } }, created() { this.getList() }, methods: { // 獲取列表數據 getList() { const data = { pageSize: this.dataSource.pageData.pageSize, pageNum: this.dataSource.pageData.pageNum } if (this.msg) { if (this.msg.platform === 'IOS') { data.platform = 1 } else if (this.msg.platform === '安卓') { data.platform = 2 } } this.dataSource.loading = true getVersionList(data).then(res => { this.dataSource.loading = false if (res.succeed) { if (res.data.total > 0) { this.dataSource.pageData.total = res.data.total this.dataSource.data = res.data.data } else { this.dataSource.data = [] this.dataSource.pageData.total = 0 } } }) }, // 搜索層事件 filterMsg(msg) { this.msg = msg if (Object.keys(msg).length > 0) { this.getList(msg) } else { this.getList() } }, // 子組件通訊 childMsg(msg) { if (msg.dialogAdd === false) { this.dialogAdd = false } else if (msg.refreshList) { this.getList() } }, // 改變每頁數量 changeSize(size) { this.dataSource.pageData.pageSize = size this.getList() }, // 改變頁碼 changeNum(pageNum) { this.dataSource.pageData.pageNum = pageNum this.getList() }, // 多選事件 handleSelectionChange(val) { this.selected = val }, // 表格上方工具欄回調 handleAdd(index, row) { this.dialogAdd = true }, // 表格操做列回調 handleRow(index, row, lable) { if (lable === '刪除') { this.$confirm('確認刪除該版本?', '舒適提示', { confirmButtonText: '肯定', cancelButtonText: '取消', type: 'warning' }).then(() => { delVersion({ versionId: row.id }).then(res => { if (res.succeed) { this.$message.success('刪除成功') this.getList() } }) }).catch(() => { }) } } } } </script>

<style scoped lang='scss'> </style>
複製代碼

結尾

filterPane.vuetablePane.vue已完成,有些特殊頁面只須要複製下到當前特殊頁面的components裏改動下就

能夠了,目前還在不斷完善中,你們有什麼問題能夠提出來,也好進一步優化。

完整源文件在gitHub,能夠下載直接使用,後續會持續更新

我給起了個名k-tablek開頭表明快速的意思

k-table 若是對你有幫助,請點亮你的小星星⭐⭐⭐哦~(瘋狂暗示)

超級簡單的API,提供手把手教你使用的實例!

寫在最後

我是涼城a,一個前端,熱愛技術也熱愛生活。

與你相逢,我很開心。

若是你想了解更多,請點這裏,期待你的小⭐⭐

  • 文中若有錯誤,歡迎在評論區指正,若是這篇文章幫到了你,歡迎點贊和關注😊

  • 本文首發於掘金,未經許可禁止轉載💌

相關文章
相關標籤/搜索