日日加班至夜半,環視周圍無人走; 明晚八點準時走,誰不打卡誰是狗。前端
使用過element-ui
的表格的同窗應該都有這樣的體會,作一個簡單的表格還比較容易,但若是這個表格包含了頂部的按鈕,還有分頁,甚至再包含了行編輯,那開發工做量就成倍的增長,特別是在開發管理系統的時候,表格一個接一個的去開發, 即浪費時間,還對我的沒有什麼提高。今天小編帶來了本身封裝的一個表格,讓你用JSON
就能夠簡單的生成表格。vue
本文主要集中於封裝思想與核心代碼說明,完整代碼請訪問 github.com/snowzijun/v…,若是以爲有用,麻煩給小編一個
star
,你的每個star
都是對小編的支持,當前功能比較簡陋,本倉庫將持續更新。同時您也能夠微信搜索【前端有的玩】公衆號,與小編進行溝通。git
通常管理系統對錶格會有如下需求github
以下圖爲一個示例表格npm
若是咱們直接使用element-ui
提供的組件的話,那麼開發一個這樣的表格就須要使用到如下內容element-ui
不只僅開發表格比較麻煩,並且還要考慮團隊協做,若是每一個人實現表格的方式存在差異,那麼可能後期的維護成本也會變得很高。那怎麼辦呢?api
在使用element-ui
的項目中,能夠經過如下命令進行安裝數組
npm install vue-elementui-table -S
複製代碼
在main.js
中添加如下代碼微信
import ZjTable from 'vue-elementui-table' Vue.use(ZjTable) 複製代碼
而後便可像下文中的使用方式進行使用markdown
爲了知足團隊快速開發的須要,小編對上面提出來的需求進行了封裝,而後使用的時候,開發人員只須要配置一些JSON
即可以完成以上功能的開發。
一個基礎的表格包含了數據和列信息,那麼如何用封裝的表格去配置呢?
<template>
<zj-table
:columns="columns"
:data="data"
:pagination="false"
/>
</template>
<script>
export default {
data() {
return {
// 表格的列信息, 數組每一項表明一個字段,可使用element 列屬性的全部屬性,如下僅爲示例
columns: Object.freeze([
{
// 表頭顯示的文字
label: '姓名',
// 對應數據裏面的字段
prop: 'name'
},
{
label: '性別',
prop: 'sex',
// 格式化表格,與element-ui 的表格屬性相同
formatter(row, column, cellValue) {
return cellValue === 1 ? '男' : '女'
}
},
{
label: '年齡',
prop: 'age'
}
]),
data: [
{
name: '子君',
sex: 1,
age: 18
}
]
}
}
}
</script>
複製代碼
經過上面的配置,就能夠完成一個基礎表格的開發,完整代碼見 github.com/snowzijun/v…,效果以下圖所示
表格默認會顯示覆選框,也能夠經過配置selectable
屬性來關閉掉
簡單的表格用封裝以後的或未封裝的開發工做量區別並不大,咱們繼續爲表格添加上分頁
<template>
<!--
current-page.sync 表示頁碼, 添加上 .sync 在頁碼發生變化時自動同步頁碼
page-size.sync 每頁條數
total 總條數
height="auto" 配置height:auto, 表格高度會根據內容自動調整,若是不指定,表格將保持充滿父容器,同時表頭會固定,不跟隨滾動條滾動
@page-change 不管pageSize currentPage 哪個變化,都會觸發這個事件
-->
<zj-table
v-loading="loading"
:columns="columns"
:data="data"
:current-page.sync="currentPage"
:page-size.sync="pageSize"
:total="total"
height="auto"
@page-change="$_handlePageChange"
/>
</template>
<script>
export default {
data() {
return {
columns: Object.freeze([
// 列字段與上例同樣,此處省略
]),
data: [],
// 當前頁碼
currentPage: 1,
// 每頁條數
pageSize: 10,
// 總條數
total: 0,
// 是否顯示loading
loading: false
}
},
created() {
this.loadData()
},
methods: {
// 加載表格數據
loadData() {
this.loading = true
setTimeout(() => {
// 假設總條數是40條
this.total = 40
const { currentPage, pageSize } = this
// 模擬數據請求獲取數據
this.data = new Array(pageSize).fill({}).map((item, index) => {
return {
name: `子君${currentPage + (index + 1) * 10}`,
sex: Math.random() > 0.5 ? 1 : 0,
age: Math.floor(Math.random() * 100)
}
})
this.loading = false
}, 1000)
},
$_handlePageChange() {
// 由於上面設置屬性指定了.sync,因此這兩個屬性會自動變化
console.log(this.pageSize, this.currentPage)
// 分頁發生變化,從新請求數據
this.loadData()
}
}
}
</script>
複製代碼
完整代碼請參考 github.com/snowzijun/v…
經過封裝,表格將自帶分頁功能,經過上面代碼,實現效果以下所示,是否是變得簡單了一些。接下來咱們繼續給表格添加按鈕
表格上面可能會有新增,刪除等等按鈕,怎麼辦呢,接下來咱們繼續經過配置去添加按鈕
<template>
<zj-table
:buttons="buttons"
/>
</template>
<script>
export default {
data() {
return {
buttons: Object.freeze([
{
// id 必須有並且是在當前按鈕數組裏面是惟一的
id: 'add',
text: '新增',
type: 'primary',
icon: 'el-icon-circle-plus',
click: this.$_handleAdd
},
{
id: 'delete',
text: '刪除',
// rows 是表格選中的行,若是沒有選中行,則禁用刪除按鈕, disabled能夠是一個boolean值或者函數
disabled: rows => !rows.length,
click: this.$_handleRemove
},
{
id: 'auth',
text: '這個按鈕根據權限顯示',
// 能夠經過返回 true/false來控制按鈕是否顯示
before: (/** rows */) => {
return true
}
},
// 能夠配置下拉按鈕哦
{
id: 'dropdown',
text: '下拉按鈕',
children: [
{
id: 'moveUp',
text: '上移',
icon: 'el-icon-arrow-up',
click: () => {
console.log('上移')
}
},
{
id: 'moveDown',
text: '下移',
icon: 'el-icon-arrow-down',
disabled: rows => !rows.length,
click: () => {
console.log('下移')
}
}
]
}
])
}
},
created() {},
methods: {
// 新增
$_handleAdd() {
this.$alert('點擊了新增按鈕')
},
// 頂部按鈕會自動將表格所選的行傳出來
$_handleRemove(rows) {
const ids = rows.map(({ id }) => id)
this.$alert(`要刪除的行id爲${ids.join(',')}`)
},
// 關注做者公衆號
$_handleFollowAuthor() {}
}
}
</script>
複製代碼
表格頂部能夠添加普通的按鈕,也能夠添加下拉按鈕,同時還能夠經過before
來配置按鈕是否顯示,disabled
來配置按鈕是否禁用,上面完整代碼見 github.com/snowzijun/v…
經過上面的代碼就能夠配置出下面的表格,是否是很簡單呢?
表格頂部能夠有按鈕,行尾也是能夠添加按鈕的,一塊兒來看看
通常咱們會將一些單行操做的按鈕放在行尾,好比編輯,下載等按鈕,那如何給行尾配置按鈕呢?
<template> <zj-table :columns="columns" /> </template> <script> export default { data() { return { columns: Object.freeze([ { // 能夠指定列的寬度,與element-ui原生用法一致 width: 220, label: '姓名', prop: 'name' }, // 行編輯按鈕,在表格末尾出現,自動鎖定右側 { width: 180, label: '操做', // 經過 actions 指定行尾按鈕 actions: [ { id: 'follow', text: '關注做者', click: this.$_handleFollowAuthor }, { id: 'edit', text: '編輯', // 能夠經過before控制按鈕是否顯示,好比下面年齡四十歲的纔會顯示編輯按鈕 before(row) { return row.age < 40 }, click: this.$_handleEdit }, { id: 'delete', text: '刪除', icon: 'el-icon-delete', disabled(row) { return row.sex === 0 }, // 爲了拿到this,這裏須要用箭頭函數 click: () => { this.$alert('女生被禁止刪除了') } } ] } ]) } }, methods: { // 關注做者公衆號 $_handleFollowAuthor() { console.log('微信搜索【前端有的玩】,這是對小編最大的支持') }, /** * row 這一行的數據 */ $_handleEdit(row, column) { this.$alert(`點擊了姓名爲【${row.name}】的行上的按鈕`) } } } </script> 複製代碼
行操做按鈕會被凍結到表格最右側,不會跟隨滾動條滾動而滾動,上面完整代碼見, github.com/snowzijun/v…
經過上面的代碼就能夠完成如下效果
最後再來一塊兒看看行編輯
好比上例,我但願點擊行尾的編輯按鈕的時候,能夠直接在行上面編輯用戶的姓名與性別,如何配置呢?
<template> <zj-table ref="table" :columns="columns" :data="data" /> </template> <script> export default { data() { return { columns: Object.freeze([ { label: '姓名', prop: 'name', editable: true, field: { componentType: 'input', rules: [ { required: true, message: '請輸入姓名' } ] } }, { label: '性別', prop: 'sex', // 格式化表格,與element-ui 的表格屬性相同 formatter(row, column, cellValue) { return cellValue === '1' ? '男' : '女' }, editable: true, field: { componentType: 'select', options: [ { label: '男', value: '1' }, { label: '女', value: '0' } ] } }, { label: '年齡', prop: 'age', editable: true, field: { componentType: 'number' } }, { label: '操做', actions: [ { id: 'edit', text: '編輯', // 若是當前行啓用了編輯,則不顯示編輯按鈕 before: row => { return !this.editIds.includes(row.id) }, click: this.$_handleEdit }, { id: 'save', text: '保存', // 若是當前行啓用了編輯,則顯示保存按鈕 before: row => { return this.editIds.includes(row.id) }, click: this.$_handleSave } ] } ]), data: [ { // 行編輯必須指定rowKey字段,默認是id,若是修改成其餘字段,須要給表格指定row-key="字段名" id: '0', name: '子君', sex: '1', age: 18 }, { // 行編輯必須指定rowKey字段,默認是id,若是修改成其餘字段,須要給表格指定row-key="字段名" id: '1', name: '子君1', sex: '0', age: 18 } ], editIds: [] } }, methods: { $_handleEdit(row) { // 經過調用 startEditRow 能夠開啓行編輯 this.$refs.table.startEditRow(row.id) // 記錄開啓了行編輯的id this.editIds.push(row.id) }, $_handleSave(row) { // 點擊保存的時候,經過endEditRow 結束行編輯 this.$refs.table.endEditRow(row.id, (valid, result, oldRow) => { // 若是有表單驗證,則valid會返回是否驗證成功 if (valid) { console.log('修改以後的數據', result) console.log('原始數據', oldRow) const index = this.editIds.findIndex(item => item === row.id) this.editIds.splice(index, 1) } else { // 若是校驗失敗,則返回校驗的第一個輸入框的異常信息 console.log(result) this.$message.error(result.message) } }) } } } </script> 複製代碼
不須要使用插槽就能夠完成行編輯,是否是很開心。上述完整代碼見 github.com/snowzijun/v…
效果以下圖所示:
除了上面的功能以外,表格還能夠配置其餘許多功能,好比
link
屬性經過上面的代碼示例,咱們已經知道了封裝以後的表格能夠完成哪些事情,接下來一塊兒來看看錶格是如何實現的。完整代碼見 github.com/snowzijun/v…
整個表格是經過JSX
來封裝的,由於JSX
使用起來更加靈活。對於咱們封裝的表格,咱們從豎向能夠分爲三部分,分別是頂部按鈕區,中間表格區,底部分頁區,如何去實現三個區域的佈局呢,核心代碼以下
render(h) { // 按鈕區域 const toolbar = this.$_renderToolbar(h) // 表格區域 const table = this.$_renderTable(h) // 分頁區域 const page = this.$_renderPage(h) return ( <div class="zj-table" style={{ height: this.tableContainerHeight }}> {toolbar} {table} {page} </div> ) } 複製代碼
經過三個render
函數分別渲染對應區域,而後將三個區域組合在一塊兒。
經過前文的講解,咱們能夠將表格的列分爲如下幾種
$_renderColumns(h, columns) { // 總體是否排序 let sortable = this.sortable ? 'custom' : false return columns .filter(column => { const { hidden } = column if (hidden !== undefined) { if (typeof hidden === 'function') { return hidden({ columns, column }) } return hidden } return true }) .map(column => { const { useSlot = false, // 若是存在操做按鈕,則actions爲非空數組 actions = [], // 是否可編輯列, 對於可編輯列須要動態啓用編輯 editable = false, // 是否有嵌套列 nests, // 是否可點擊 link = false } = column let newSortable = sortable if (column.sortable !== undefined) { newSortable = column.sortable ? 'custom' : false } column = { ...column, sortable: newSortable } if (nests && nests.length) { // 使用嵌套列 return this.$_renderNestColumn(h, column) } else if (editable) { // 使用編輯列 return this.$_renderEditColumn(h, column) } else if (useSlot) { // 使用插槽列 return this.$_renderSlotColumn(h, column) } else if (actions && actions.length > 0) { // 使用操做列 column.sortable = false return this.$_renderActionColumn(h, column) } else if (link) { // 使用連接列 return this.$_renderLinkColumn(h, column) } else { // 使用默認列 return this.$_renderDefaultColumn(h, column) } }) }, 複製代碼
當前表格行編輯支持input
,select
,datepicker
,TimeSelect
,InputNumber
等組件,具體渲染代碼以下所示
// 編輯單元格 $_renderEditCell(h, field) { const components = { input: Input, select: ZjSelect, date: DatePicker, time: TimeSelect, number: InputNumber } const componentType = field.componentType const component = components[componentType] if (component) { return this.$_renderField(h, field, component) } else if (componentType === 'custom') { // 若是自定義,能夠經過component指定組件 return this.$_renderField(h, field, field.component) } return this.$_renderField(h, field, Input) }, $_renderField(h, field, Component) { // 編輯行的id字段 const { rowId, events = {}, nativeEvents = {} } = field const getEvents = events => { const newEvents = {} Object.keys(events).forEach(key => { const event = events[key] newEvents[key] = (...rest) => { const args = [ ...rest, { rowId, row: this.editRowsData[rowId], value: this.editRowsData[rowId][field.prop] } ] return event(...args) } }) return newEvents } // 事件改寫 const newEvents = getEvents(events) const newNativeEvents = getEvents(nativeEvents) return ( <Component size="small" on={newEvents} nativeOn={newNativeEvents} v-model={this.editRowsData[rowId][field.prop]} {...{ attrs: field, props: field }} /> ) } 複製代碼
這個表格包含了許多功能,文章長度優先,若是以爲有用,能夠經過訪問 github.com/snowzijun/v… 查看完整代碼,本倉庫代碼及文檔小編將持續完善,歡迎star。緣始積累,關注公衆號,小編拉你進前端交流羣
閱讀小編近期的熱門
Vue
相關文章,我是子君,每週一,不見不散
實戰技巧,Vue原來還能夠這樣寫 獲贊 2700+
絕對乾貨~!學會這些Vue小技巧,能夠早點下班和女神約會了 獲贊 1200+
前方高能,這是最新的一波Vue實戰技巧,不用則已,一用驚人 獲贊 1000+
我在項目中是這樣配置Vue的 獲贊 1000+
學會使用Vue JSX,一車老乾媽都是你的 獲贊700+
讓Vue項目更絲滑的幾個小技巧 獲贊 300+
看到賺到!重讀vue2.0風格指南,我整理了這些關鍵規則 獲贊 150+
不要吹滅你的靈感和你的想象力; 不要成爲你的模型的奴隸。 ——文森特・梵高
本文使用 mdnice 排版