elementUI?el-table二次封裝的詳細實例
前言
很多中后臺業(yè)務(wù)的系統(tǒng)中,表格是最高頻的組件之一,其中一般包括搜索條件、表格展示、表格操作列、分頁等。那么我們二次封裝的這個表格組件就需要包含以下幾個功能點:
1、數(shù)據(jù)自動獲取和刷新
2、自定義列配置
3、分頁功能
4、根據(jù)搜索條件進行搜索功能
5、加載中狀態(tài)和空數(shù)據(jù)狀態(tài)
一、先上頁面最終效果
二、創(chuàng)建目錄yxt-table如下圖
index.vue為父級頁面
yxt-table.vue為表格組件
二、數(shù)據(jù)自動獲取和刷新
因為表格的數(shù)據(jù)一般都比較簡單,就是根據(jù)搜索條件,調(diào)用接口請求一個到列表,然后將列表數(shù)據(jù)一一展示到表格上,再對特定的列進行一些過濾轉(zhuǎn)化等個性化操作。但是萬變不離其宗,這個步驟基本每個表格都要進行一遍,所以考慮到通用性(其實是為了偷懶),將請求接口獲取數(shù)據(jù)這一步放在組件里面實現(xiàn)。
created () { this.getData() }, methods: { getData () { const fun = this.apiUrl fun().then(res => { this.tableData = res[this.otherConfig.list] || [] this.tableTotal = res.pageInfo?.total || 0 }) } }
三、自定義列配置
組件接收一個數(shù)組作為自定義列
tableColumn: [ { prop: 'name', label: '名稱' }, { prop: 'code', label: '編碼' }, { prop: 'status', label: '狀態(tài)' } ]
index.vue
<!-- index.vue --> <template> <div> <yxt-table :apiUrl="yxtTableList" :tableColumn="tableColumn"></yxt-table> </div> </template> <!-- index.vue --> <script> import yxtTable from './yxt-table.vue' import { yxtTableList } from 'https/yxtDemo.js' export default { name: 'yxtDemoTable', components: { yxtTable }, data () { return { yxtTableList, tableColumn: [ { prop: 'name', label: '名稱' }, { prop: 'code', label: '編碼' }, { prop: 'status', label: '狀態(tài)' } ] } } } </script>
yxt-table.vue
<!-- yxt-table.vue --> <template> <div> <el-table :data="tableData"> <el-table-column v-for="item in tableColumn" :key="item.prop" :prop="item.prop" :label="item.label"></el-table-column> </el-table> </div> </template> <!-- yxt-table.vue --> <script> export default { name: 'yxtTable', props: { apiUrl: { // 列表接口(必填) type: Function, required: true }, tableColumn: { // 自定義列配置 type: Array, default: () => [] }, otherConfig: { // type: Object, default: () => { return { list: 'list' } } } }, data () { return { tableData: [] } }, created () { this.getData() }, methods: { getData () { const fun = this.apiUrl fun().then(res => { this.tableData = res[this.otherConfig.list] || [] this.tableTotal = res.pageInfo?.total || 0 }) } } } </script>
至此,一個表格可以實現(xiàn)了
1、otherConfig說明
由于我們的接口請求放在組件里面了,但是我們對接的接口可能由于業(yè)務(wù)的不同項目組的不同開發(fā)人員的不同,而導(dǎo)致接口返回的列表的字段名不同,這里通過傳參的形式做一下兼容
2、上面這樣只能實現(xiàn)簡單的展示功能,還有些數(shù)據(jù)比如 狀態(tài)1 需要轉(zhuǎn)化為打卡成功,狀態(tài)0 需要轉(zhuǎn)化為打卡失敗進行顯示,這類需求可以通過filter進行轉(zhuǎn)化
<!-- yxt-table.vue --> <el-table-column v-for="item in tableColumn" :key="item.prop" :prop="item.prop" :label="item.label"> <template v-slot:default="scope"> <div v-if="item.dictCode"> {{ scope.row[item.prop] | filterStatus(dict[item.dictCode]) }} </div> <div v-else> {{ scope.row[item.prop] }} </div> </template> </el-table-column> props: { dict: { // 全部字典 type: Object, default: () => {} } }, filters: { filterStatus (value, array, code = 'code', name = 'name') { if (!value && value !== 0) { // 要把0摘出來,一般0都是正常的數(shù)據(jù),所以不能只用 !value return '' } const find = array.find(e => (e[code] === value.toString()) || (e[code] === +value)) // 字符型數(shù)值型都得匹配 if (find) { return find[name] } else { // 沒有匹配的就原樣返回 return value } } },
<!-- index.vue --> <yxt-table :apiUrl="yxtTableList" :tableColumn="tableColumn" :dict="dict"></yxt-table> data () { return { tableColumn: [ { prop: 'name', label: '名稱' }, { prop: 'code', label: '編碼' }, { prop: 'status', label: '狀態(tài)', dictCode: 'status' } ], dict: { status: [ { code: 0, name: '打卡失敗' }, { code: 1, name: '打卡成功' } ] } } }
這里dict設(shè)置為對象的原因是為了裝進更多字典
3、思考一下,如果要在表格中展示這樣的自定義圖標怎么辦?
使用插槽slot,在tableColumn里面設(shè)置某行屬性的slot為true,改造el-table-column如下:
<!-- yxt-table.vue --> <el-table-column v-for="(item, index) in tableColumn" :key="index" :prop="item.prop" :label="item.label"> <template v-if="item.slot" v-slot:default="scope"> <slot :name="item.prop" :row="scope.row" :index="scope.$index"></slot> </template> <template v-else v-slot:default="scope"> <div v-if="item.dictCode"> {{ scope.row[item.prop] | filterStatus(dict[item.dictCode]) }} </div> <div v-else> {{ scope.row[item.prop] }} </div> </template> </el-table-column>
<!-- index.vue --> <yxt-table :apiUrl="yxtTableList" :tableColumn="tableColumn" :otherConfig="otherConfig" :dict="dict"> <template v-slot:icon="{row, index}"> <i :class="row.status ? 'el-icon-circle-check' : 'el-icon-circle-close'"></i> </template> </yxt-table> data () { return { tableColumn: [ { prop: 'name', label: '名稱' }, { prop: 'code', label: '編碼' }, { prop: 'status', label: '狀態(tài)', dictCode: 'status' }, { prop: 'icon', label: '圖標', slot: true } ] } }
4、在實際項目中,除了字典轉(zhuǎn)化,還有一些比較定制化的展示需求,這個可以通過傳入一個函數(shù)format進行計算,然后在這個方法里面將最后的計算結(jié)果return
<!-- yxt-table.vue --> <el-table-column v-for="(item, index) in tableColumn" :key="index" :prop="item.prop" :label="item.label"> <template v-if="item.slot" v-slot:default="scope"> <slot :name="item.prop" :row="scope.row" :index="scope.$index"></slot> </template> <template v-else v-slot:default="scope"> <div v-if="item.dictCode"> {{ scope.row[item.prop] | filterStatus(dict[item.dictCode]) }} </div> <div v-else-if="item.format"> {{ item.format(scope.row) }} </div> <div v-else> {{ scope.row[item.prop] }} </div> </template> </el-table-column>
<!-- index.vue --> data () { return { tableColumn: [ { prop: 'name', label: '名稱' }, { prop: 'code', label: '編碼' }, { prop: 'status', label: '狀態(tài)', dictCode: 'status' }, { prop: 'icon', label: '圖標', slot: true }, { prop: 'phone', label: '電話號碼', format: (row) => { return `${row.name}-${row.code}(${row.phone})` } } ] } }
5、表格一般還有批量操作,所以需要多選和單選以及針對特定場景設(shè)置禁選
yxt-table.vue
<!-- yxt-table.vue --> <template> <div class="yxt-table"> <!-- 批量操作按鈕,因為每個需求不同,批量操作的功能也不同,所以這里只放一個插槽,不設(shè)置默認內(nèi)容,所有按鈕均在父級設(shè)置 --> <div class="multiple-operation"> <slot name="multiple-operation" :selectionData="selectionData"></slot> </div> <!-- 頁面主表格 --> <el-table :data="tableData" :row-key="rowKey" @selection-change="selectionChange"> <!-- 可選框(多選) --> <el-table-column v-if="selection === 'multiple'" type="selection" align="center" width="55" :reserve-selection="rowKey ? true : false" :selectable="selectable"/> <!-- 可選框(單選) --> <el-table-column v-else-if="selection === 'single'" align="center" width="30"> <template v-slot:default="scope"> <el-radio v-model="selectionRadio" :label="scope.$index" :disabled="selectable ? !selectable(scope.row) : false" @change="selectionChangeSingle(scope.row)"> {{ '' }} </el-radio> </template> </el-table-column> <el-table-column v-for="(item, index) in tableColumn" :key="index" :prop="item.prop" :label="item.label"> <template v-if="item.slot" v-slot:default="scope"> <slot :name="item.prop" :row="scope.row" :index="scope.$index"></slot> </template> <template v-else v-slot:default="scope"> <div v-if="item.dictCode"> {{ scope.row[item.prop] | filterStatus(dict[item.dictCode]) }} </div> <div v-else-if="item.format"> {{ item.format(scope.row) }} </div> <div v-else> {{ scope.row[item.prop] }} </div> </template> </el-table-column> </el-table> </div> </template> <!-- yxt-table.vue --> <script> export default { name: 'yxtTable', props: { apiUrl: { // 列表接口(必填) type: Function, required: true }, tableColumn: { // 自定義列配置 type: Array, default: () => [] }, otherConfig: { // 其他配置 type: Object, default: () => { return { list: 'list' // 接口返回數(shù)據(jù)的列表字段的字段名(因為在組件里面調(diào)接口,可能不同業(yè)務(wù)不同項目組不同一個開發(fā)者返回給前端的參數(shù)名不一致,這里進行兼容) } } }, dict: { // 全部字典 type: [Array, Object], default: () => [] }, selection: { // 是否顯示可選框(多選-multiple 、單選-single ) type: String }, selectable: { // 當(dāng)前行是否可選擇 type: Function }, rowKey: { // 表格唯一key(適用于分頁多選表格,保留之前的選擇,不傳則為單頁選擇) type: [Number, String, Function], default: '' } }, filters: { filterStatus (value, array, code = 'code', name = 'name') { if (!value && value !== 0) { // 要把0摘出來,一般0都是正常的數(shù)據(jù),所以不能只用 !value return '' } const find = array.find(e => (e[code] === value.toString()) || (e[code] === +value)) // 字符型數(shù)值型都得匹配 if (find) { return find[name] } else { // 沒有匹配的就原樣返回 return value } } }, data () { return { tableData: [], tableTotal: 0, selectionRadio: '', selectionData: [] } }, created () { this.getData() }, methods: { getData () { const fun = this.apiUrl fun().then(res => { this.tableData = res[this.otherConfig.list] || [] this.tableTotal = res.pageInfo?.total || 0 }) }, // 多選,選擇行數(shù)據(jù)change selectionChange (selection) { this.selectionData = selection }, // 單選,選擇行數(shù)據(jù)change selectionChangeSingle (selection) { this.selectionData = [selection] } } } </script> <style scoped lang="scss"> .yxt-table { margin: 30px; .multiple-operation { margin-bottom: 10px; } } </style>
index.vue
<!-- index.vue --> <template> <div> <yxt-table :apiUrl="yxtTableList" :tableColumn="tableColumn" :otherConfig="otherConfig" :dict="dict" selection="multiple" :selectable="isSelectable"> <!-- 圖標插槽 --> <template v-slot:icon="{row, index}"> <i :class="row.status ? 'el-icon-circle-check' : 'el-icon-circle-close'"></i> </template> <!-- 批量操作按鈕插槽 --> <template v-slot:multiple-operation="{selectionData}"> <el-button type="primary" size="small" @click="handleClick1(selectionData)">批量操作1</el-button> <el-button type="success" size="small" @click="handleClick2(selectionData)">批量操作2</el-button> </template> </yxt-table> <yxt-table :apiUrl="yxtTableList" :tableColumn="tableColumn" :otherConfig="otherConfig" :dict="dict" selection="single" :selectable="isSelectable"> <!-- 圖標插槽 --> <template v-slot:icon="{row, index}"> <i :class="row.status ? 'el-icon-circle-check' : 'el-icon-circle-close'"></i> </template> <!-- 批量操作按鈕插槽 --> <template v-slot:multiple-operation="{selectionData}"> <el-button type="primary" size="small" @click="handleClick1(selectionData)">單選操作</el-button> </template> </yxt-table> </div> </template> <!-- index.vue --> <script> import yxtTable from './yxt-table.vue' import { yxtTableList } from 'https/yxtDemo.js' export default { name: 'yxtDemoTable', components: { yxtTable }, data () { return { yxtTableList, tableColumn: [ { prop: 'name', label: '名稱' }, { prop: 'code', label: '編碼' }, { prop: 'status', label: '狀態(tài)', dictCode: 'status' }, { prop: 'icon', label: '圖標', slot: true }, { prop: 'phone', label: '電話號碼', format: (row) => { return `${row.name}-${row.code}(${row.phone})` } } ], tableConfig: { stripe: 'stripe', border: 'border', height: '200', maxHeight: '200', showHeader: true }, otherConfig: { list: 'tasks' }, dict: { status: [ { code: 0, name: '打卡失敗' }, { code: 1, name: '打卡成功' } ] } } }, methods: { handleClick1 (selectionData) { console.log('1', selectionData) }, handleClick2 (selectionData) { console.log('2', selectionData) }, isSelectable (row) { return row.selectable !== 0 } } } </script> <style scoped lang="scss"> .el-icon-circle-check { font-size: 28px; color: #67C23A; } .el-icon-circle-close { font-size: 28px; color: #F00; } </style>
6、操作列
根據(jù)業(yè)務(wù)需求,可以在操作列設(shè)置幾個默認按鈕,通過setupConfig設(shè)置開關(guān),如果有除了默認按鈕之外的操作需求,再通過插槽slot進行插入
<!-- yxt-table.vue --> <!-- 操作列 --> <el-table-column v-if="setupConfig.width !== 0" :fixed="setupConfig.fixed" :width="setupConfig.width" label="操作"> <template v-slot:default="scope"> <slot name="setup" :row="scope.row" :index="scope.$index"></slot> <!-- 查看 --> <el-button v-if="setupConfig.view" type="text" @click="setupEvents('view', scope.row)">查看</el-button> <!-- 編輯 --> <el-button v-if="setupConfig.edit" type="text" @click="setupEvents('edit', scope.row)">編輯</el-button> <!-- 刪除 --> <el-button v-if="setupConfig.del" type="text" @click="setupEvents('del', scope.row)">刪除</el-button> <!-- 操作日志 --> <el-button v-if="setupConfig.log" type="text" @click="setupEvents('log', scope.row)">操作日志</el-button> </template> </el-table-column> props: { setupConfig: { type: Object, default: () => { return { width: 'auto' } } } }, methods: { setupEvents (setupType, row) { // 操作列方法 查看/編輯/刪除/操作日志 this.$emit(setupType, row) } }
index.vue做相應(yīng)的處理,這里不貼代碼了
7、分頁
pagination控制是否需要分頁組件,如果不需要分頁則設(shè)置為false。根據(jù)業(yè)務(wù)需求,可傳入pageSizes控制條數(shù)下拉框的條數(shù)選項
<!-- yxt-table.vue --> <!-- 分頁 --> <el-pagination v-if="pagination" class="pagination tablePage" :pager-count="5" :page-sizes="pageSizes || [10, 20, 50, 100]" :total="tableTotal || 0" :page-size="pageInfo.pageSize || 10" :current-page="pageInfo.startPage || 1" layout="total, sizes, prev, pager, next, jumper" @size-change="sizeChange" @current-change="pageChange"></el-pagination> props: { pagination: { // 是否需要分頁,默認需要 type: Boolean, default: true }, pageSizes: { type: Array } }, methods: { getData () { const fun = this.apiUrl const pageInfo = { // 分頁信息 pageSize: this.pageInfo.pageSize, startPage: this.pageInfo.startPage } let param = { // 其他的搜素條件 } if (this.pagination) { // 如果需要分頁,則傳分頁信息 param = { ...param, ...pageInfo } } fun(param).then(res => { this.tableData = res[this.otherConfig.list] || [] this.tableTotal = res.pageInfo?.total || 0 }) }, // 條數(shù)變化 sizeChange (size) { this.pageInfo.startPage = 1 this.pageInfo.pageSize = size this.getData() }, // 頁碼變化 pageChange (page) { this.pageInfo.startPage = page this.getData() } }
8、el-table還有一個展開行功能expand,根據(jù)業(yè)務(wù)需求,也可以加進組件里
<!-- yxt-table.vue --> <!-- 展開行 --> <el-table-column v-if="expand" type="expand"> <template v-slot:default="scope"> <slot name="expand" :row="scope.row" :index="scope.$index"></slot> </template> </el-table-column> props: { expand: { // 是否展開行 type: Boolean, default: false } }
<!-- index.vue --> <yxt-table :apiUrl="yxtTableList" :tableColumn="tableColumn" :expand="true"> <template v-slot:expand="{row, index}"> <div> <p>序號:{{index}}</p> <p>內(nèi)容:{{row}}</p> </div> </template> </yxt-table>
四、根據(jù)搜索條件進行搜索更新表格數(shù)據(jù)
新增一個yxt-search.vue
<!-- yxt-search.vue --> <template> <div class="yxt-search"> <div v-for="(item,index) in searchConfig" :key="index" class="yxt-search-item"> <el-input v-if="item.type==='input'" v-model="searchModel[item.key]" size="medium" :clearable="item.clearable || true" :placeholder="item.placeholder || '請輸入'" :maxlength="item.maxlength"></el-input> <el-select v-if="item.type==='select'" v-model="searchModel[item.key]" size="medium" style="width: 100%" :clearable="item.clearable || true" :filterable="item.filterable || true" :disabled="item.disabled || false" :multiple="item.multiple || false" :allow-create="item.allowCreate" :placeholder="item.placeholder || '請選擇'"> <el-option v-for="(selectItem, selectIndex) in item.selectList" :key="selectIndex" :label="selectItem[item.listLabel]" :value="selectItem[item.listValue]"></el-option> </el-select> </div> <div v-if="searchConfig.length" class="yxt-search-button"> <el-button size="medium" type="primary" @click="search">搜索</el-button> <el-button size="medium" type="primary" plain @click="reset">重置</el-button> <!-- 其他的按鈕需求通過插槽傳入 --> <slot name="searchBtn" :searchData="searchModel"></slot> </div> </div> </template> <!-- yxt-search.vue --> <script> export default { name: 'yxtSearch', props: { searchConfig: { // 搜索條件配置項 type: Array, required: true, default () { return [] } }, searchModel: { // 搜索條件綁定值 type: Object, required: true, default () { return {} } }, searchReset: { // 搜索條件默認值重置值 type: Object } }, data () { return { } }, methods: { search () { this.$emit('search', this.searchModel) }, reset () { if (this.searchReset) { // 如果傳入有默認值,則重置后為默認值 Object.keys(this.searchModel).forEach((item) => { this.searchModel[item] = this.searchReset[item] }) } else { Object.keys(this.searchModel).forEach((item) => { this.searchModel[item] = '' }) } } } } </script> <style scoped lang="scss"> .yxt-search { display: flex; flex-direction: row; flex-wrap: wrap; justify-content: flex-start; .yxt-search-item { flex: 1; margin: 0 10px 10px 0; width: calc((100% - 30px) / 4); // 這里的30px = (分布個數(shù)4-1)*間隙1px, 可以根據(jù)實際的分布個數(shù)和間隙區(qū)調(diào)整 min-width: calc((100% - 30px) / 4); max-width: calc((100% - 30px) / 4); &:nth-child(4n) { // 去除每行最后一個(第4n個)的margin-right margin-right: 0; } } .yxt-search-button { margin: 0 0 10px 0; width: 100%; text-align: right; } } </style>
<!-- yxt-table.vue --> <yxt-search :searchConfig="searchConfig" :searchModel="searchModel" :searchReset="searchReset" @search="getData(1)"> <template v-slot:searchBtn="{searchData}"> <!-- 其他的按鈕需求通過插槽傳入 --> <slot name="searchBtn" :searchData="searchData"></slot> </template> </yxt-search> props: { searchConfig: { // 搜索條件配置項 type: Array, default () { return [] } }, searchReset: { // 搜索條件默認值重置值 type: Object } }, data () { return { searchModel: this.searchReset ? JSON.parse(JSON.stringify(this.searchReset)) : {} } }, methods: { getData (startPage) { if (startPage) { // 如果傳入值,則從改值的頁碼數(shù)開始 this.pageInfo.startPage = startPage } let param = { // 其他的搜素條件 ...this.searchModel } ... } }
<!-- index.vue --> <yxt-table :apiUrl="yxtTableList" :tableColumn="tableColumn" :searchConfig="searchConfig" :searchReset="searchReset"> <template v-slot:searchBtn="{searchData}"> <el-button size="medium" type="success" @click="handleClickExport(searchData)">導(dǎo)出</el-button> </template> </yxt-table> data () { return { searchConfig: [ { type: 'input', key: 'name' }, { type: 'input', key: 'code' }, { type: 'select', key: 'status', selectList: [ { code: 0, name: '打卡失敗' }, { code: 1, name: '打卡成功' } ], listLabel: 'name', listValue: 'code' } ], searchReset: { name: '張三', code: '', status: 1 } } }, methods: { handleClickExport (data) { console.log(data) } }
五、加載中狀態(tài)和空數(shù)據(jù)狀態(tài)
加載中:el-table 添加 v-loading="loading",getData里面,發(fā)送請求之前設(shè)置為true,獲得數(shù)據(jù)后設(shè)置為false
空數(shù)據(jù):通過插槽empty設(shè)置
六、完整代碼:
index.vue
<!-- index.vue --> <template> <div> <yxt-table :apiUrl="yxtTableList" :tableColumn="tableColumn" :otherConfig="otherConfig" :dict="dict" selection="multiple" :selectable="isSelectable" :setupConfig="setupConfig" :searchConfig="searchConfig" :searchReset="searchReset" @view="view" @log="log"> <!-- 圖標插槽 --> <template v-slot:icon="{row, index}"> <i :class="row.status ? 'el-icon-circle-check' : 'el-icon-circle-close'"></i> </template> <!-- 批量操作按鈕插槽 --> <template v-slot:multiple-operation="{selectionData}"> <el-button type="primary" size="small" @click="handleClick1(selectionData)">批量操作1</el-button> <el-button type="success" size="small" @click="handleClick2(selectionData)">批量操作2</el-button> </template> <template v-slot:searchBtn="{searchData}"> <el-button size="medium" type="success" @click="handleClickExport(searchData)">導(dǎo)出</el-button> </template> </yxt-table> <yxt-table :apiUrl="yxtTableList" :tableColumn="tableColumn" :otherConfig="otherConfig" :dict="dict" selection="single" :selectable="isSelectable" :setupConfig="setupConfig2" :pagination="false" :expand="true" :emptyText="'沒有數(shù)據(jù)的展示文字'"> <!-- 圖標插槽 --> <template v-slot:icon="{row, index}"> <i :class="row.status ? 'el-icon-circle-check' : 'el-icon-circle-close'"></i> </template> <!-- 批量操作按鈕插槽 --> <template v-slot:multiple-operation="{selectionData}"> <el-button type="primary" size="small" @click="handleClick1(selectionData)">單選操作</el-button> </template> <template v-slot:expand="{row, index}"> <div> <p>序號:{{index}}</p> <p>內(nèi)容:{{row}}</p> </div> </template> </yxt-table> </div> </template> <!-- index.vue --> <script> import yxtTable from './yxt-table.vue' import { yxtTableList } from 'https/yxtDemo.js' export default { name: 'yxtDemoTable', components: { yxtTable }, data () { return { yxtTableList, tableColumn: [ { prop: 'name', label: '名稱' }, { prop: 'code', label: '編碼' }, { prop: 'status', label: '狀態(tài)', dictCode: 'status' }, { prop: 'icon', label: '圖標', slot: true }, { prop: 'phone', label: '電話號碼', format: (row) => { return `${row.name}-${row.code}(${row.phone})` } } ], tableConfig: { stripe: 'stripe', border: 'border', height: '200', maxHeight: '200', showHeader: true }, otherConfig: { list: 'tasks' }, setupConfig: { width: 100, view: true, log: true }, setupConfig2: { edit: true, del: true, log: true }, dict: { status: [ { code: 0, name: '打卡失敗' }, { code: 1, name: '打卡成功' } ] }, searchConfig: [ { type: 'input', key: 'name' }, { type: 'input', key: 'code' }, { type: 'select', key: 'status', selectList: [ { code: 0, name: '打卡失敗' }, { code: 1, name: '打卡成功' } ], listLabel: 'name', listValue: 'code' } ], searchReset: { name: '張三', code: '', status: 1 } } }, methods: { handleClick1 (selectionData) { console.log('1', selectionData) }, handleClick2 (selectionData) { console.log('2', selectionData) }, handleClickExport (data) { console.log(data) }, isSelectable (row) { return row.selectable !== 0 }, view (row) { console.log('view', row) }, log (row) { console.log('log', row) } } } </script> <style scoped lang="scss"> .el-icon-circle-check { font-size: 28px; color: #67C23A; } .el-icon-circle-close { font-size: 28px; color: #F00; } </style>
yxt-table.vue
<!-- yxt-table.vue --> <template> <div class="yxt-table"> <yxt-search :searchConfig="searchConfig" :searchModel="searchModel" :searchReset="searchReset" @search="getData(1)"> <template v-slot:searchBtn="{searchData}"> <!-- 其他的按鈕需求通過插槽傳入 --> <slot name="searchBtn" :searchData="searchData"></slot> </template> </yxt-search> <!-- 批量操作按鈕,因為每個需求不同,批量操作的功能也不同,所以這里只放一個插槽,不設(shè)置默認內(nèi)容,所有按鈕均在父級設(shè)置 --> <div class="multiple-operation"> <slot name="multiple-operation" :selectionData="selectionData"></slot> </div> <!-- 頁面主表格 --> <el-table :data="tableData" :row-key="rowKey" v-loading="loading" @selection-change="selectionChange"> <!-- 可選框(多選) --> <el-table-column v-if="selection === 'multiple'" type="selection" align="center" width="55" :reserve-selection="rowKey ? true : false" :selectable="selectable"/> <!-- 可選框(單選) --> <el-table-column v-else-if="selection === 'single'" align="center" width="30"> <template v-slot:default="scope"> <el-radio v-model="selectionRadio" :label="scope.$index" :disabled="selectable ? !selectable(scope.row) : false" @change="selectionChangeSingle(scope.row)"> {{ '' }} </el-radio> </template> </el-table-column> <!-- 展開行 --> <el-table-column v-if="expand" type="expand"> <template v-slot:default="scope"> <slot name="expand" :row="scope.row" :index="scope.$index"></slot> </template> </el-table-column> <el-table-column v-for="(item, index) in tableColumn" :key="index" :prop="item.prop" :label="item.label"> <template v-if="item.slot" v-slot:default="scope"> <slot :name="item.prop" :row="scope.row" :index="scope.$index"></slot> </template> <template v-else v-slot:default="scope"> <div v-if="item.dictCode"> {{ scope.row[item.prop] | filterStatus(dict[item.dictCode]) }} </div> <div v-else-if="item.format"> {{ item.format(scope.row) }} </div> <div v-else> {{ scope.row[item.prop] }} </div> </template> </el-table-column> <!-- 操作列 --> <el-table-column v-if="setupConfig.width !== 0" :fixed="setupConfig.fixed" :width="setupConfig.width" label="操作"> <template v-slot:default="scope"> <slot name="setup" :row="scope.row" :index="scope.$index"></slot> <!-- 查看 --> <el-button v-if="setupConfig.view" type="text" @click="setupEvents('view', scope.row)">查看</el-button> <!-- 編輯 --> <el-button v-if="setupConfig.edit" type="text" @click="setupEvents('edit', scope.row)">編輯</el-button> <!-- 刪除 --> <el-button v-if="setupConfig.del" type="text" @click="setupEvents('del', scope.row)">刪除</el-button> <!-- 操作日志 --> <el-button v-if="setupConfig.log" type="text" @click="setupEvents('log', scope.row)">操作日志</el-button> </template> </el-table-column> <!-- 空狀態(tài) --> <template slot="empty"> <p>{{ emptyText }}</p> </template> </el-table> <!-- 分頁 --> <el-pagination v-if="pagination" class="pagination tablePage" :pager-count="5" :page-sizes="pageSizes || [10, 20, 50, 100]" :total="tableTotal || 0" :page-size="pageInfo.pageSize || 10" :current-page="pageInfo.startPage || 1" layout="total, sizes, prev, pager, next, jumper" @size-change="sizeChange" @current-change="pageChange"></el-pagination> </div> </template> <!-- yxt-table.vue --> <script> import yxtSearch from './yxt-search' export default { name: 'yxtTable', components: { yxtSearch }, props: { apiUrl: { // 列表接口(必填) type: Function, required: true }, tableColumn: { // 自定義列配置 type: Array, default: () => [] }, otherConfig: { // 其他配置 type: Object, default: () => { return { list: 'list' // 接口返回數(shù)據(jù)的列表字段的字段名(因為在組件里面調(diào)接口,可能不同業(yè)務(wù)不同項目組不同一個開發(fā)者返回給前端的參數(shù)名不一致,這里進行兼容) } } }, dict: { // 全部字典 type: [Array, Object], default: () => [] }, selection: { // 是否顯示可選框(多選-multiple 、單選-single ) type: String }, selectable: { // 當(dāng)前行是否可選擇 type: Function }, rowKey: { // 表格唯一key(適用于分頁多選表格,保留之前的選擇,不傳則為單頁選擇) type: [Number, String, Function], default: '' }, setupConfig: { type: Object, default: () => { return { width: 'auto' } } }, pagination: { // 是否需要分頁,默認需要 type: Boolean, default: true }, pageSizes: { // 分頁的下拉框選項 type: Array }, expand: { // 是否展開行 type: Boolean, default: false }, searchConfig: { // 搜索條件配置項 type: Array, default () { return [] } }, searchReset: { // 搜索條件默認值重置值 type: Object }, emptyText: { type: String } }, filters: { filterStatus (value, array, code = 'code', name = 'name') { if (!value && value !== 0) { // 要把0摘出來,一般0都是正常的數(shù)據(jù),所以不能只用 !value return '' } const find = array.find(e => (e[code] === value.toString()) || (e[code] === +value)) // 字符型數(shù)值型都得匹配 if (find) { return find[name] } else { // 沒有匹配的就原樣返回 return value } } }, data () { return { loading: true, tableData: [], tableTotal: 0, pageInfo: { pageSize: 10, startPage: 1 }, selectionRadio: '', selectionData: [], searchModel: this.searchReset ? JSON.parse(JSON.stringify(this.searchReset)) : {} } }, created () { this.getData() }, methods: { getData (startPage) { if (startPage) { // 如果傳入值,則從改值的頁碼數(shù)開始 this.pageInfo.startPage = startPage } this.loading = true const fun = this.apiUrl const pageInfo = { // 分頁信息 pageSize: this.pageInfo.pageSize, startPage: this.pageInfo.startPage } let param = { // 其他的搜素條件 ...this.searchModel } if (this.pagination) { // 如果需要分頁,則傳分頁信息 param = { ...param, ...pageInfo } } fun(param).then(res => { setTimeout(() => { this.tableData = res[this.otherConfig.list] || [] this.tableTotal = res.pageInfo?.total || 0 this.loading = false }, 2000) }) }, // 多選,選擇行數(shù)據(jù)change selectionChange (selection) { this.selectionData = selection }, // 單選,選擇行數(shù)據(jù)change selectionChangeSingle (selection) { this.selectionData = [selection] }, // 操作列方法 查看/編輯/刪除/操作日志 setupEvents (setupType, row) { this.$emit(setupType, row) }, // 條數(shù)變化 sizeChange (size) { this.pageInfo.startPage = 1 this.pageInfo.pageSize = size this.getData() }, // 頁碼變化 pageChange (page) { this.pageInfo.startPage = page this.getData() } } } </script> <style scoped lang="scss"> .yxt-table { margin: 30px; .multiple-operation { margin-bottom: 10px; } } </style>
yxt-search.vue
<!-- yxt-search.vue --> <template> <div class="yxt-search"> <div v-for="(item,index) in searchConfig" :key="index" class="yxt-search-item"> <el-input v-if="item.type==='input'" v-model="searchModel[item.key]" size="medium" :clearable="item.clearable || true" :placeholder="item.placeholder || '請輸入'" :maxlength="item.maxlength"></el-input> <el-select v-if="item.type==='select'" v-model="searchModel[item.key]" size="medium" style="width: 100%" :clearable="item.clearable || true" :filterable="item.filterable || true" :disabled="item.disabled || false" :multiple="item.multiple || false" :allow-create="item.allowCreate" :placeholder="item.placeholder || '請選擇'"> <el-option v-for="(selectItem, selectIndex) in item.selectList" :key="selectIndex" :label="selectItem[item.listLabel]" :value="selectItem[item.listValue]"></el-option> </el-select> </div> <div v-if="searchConfig.length" class="yxt-search-button"> <el-button size="medium" type="primary" @click="search">搜索</el-button> <el-button size="medium" type="primary" plain @click="reset">重置</el-button> <!-- 其他的按鈕需求通過插槽傳入 --> <slot name="searchBtn" :searchData="searchModel"></slot> </div> </div> </template> <!-- yxt-search.vue --> <script> export default { name: 'yxtSearch', props: { searchConfig: { // 搜索條件配置項 type: Array, required: true, default () { return [] } }, searchModel: { // 搜索條件綁定值 type: Object, required: true, default () { return {} } }, searchReset: { // 搜索條件默認值重置值 type: Object } }, data () { return { } }, methods: { search () { this.$emit('search', this.searchModel) }, reset () { if (this.searchReset) { // 如果傳入有默認值,則重置后為默認值 Object.keys(this.searchModel).forEach((item) => { this.searchModel[item] = this.searchReset[item] }) } else { Object.keys(this.searchModel).forEach((item) => { this.searchModel[item] = '' }) } } } } </script> <style scoped lang="scss"> .yxt-search { display: flex; flex-direction: row; flex-wrap: wrap; justify-content: flex-start; .yxt-search-item { flex: 1; margin: 0 10px 10px 0; width: calc((100% - 30px) / 4); // 這里的30px = (分布個數(shù)4-1)*間隙1px, 可以根據(jù)實際的分布個數(shù)和間隙區(qū)調(diào)整 min-width: calc((100% - 30px) / 4); max-width: calc((100% - 30px) / 4); &:nth-child(4n) { // 去除每行最后一個(第4n個)的margin-right margin-right: 0; } } .yxt-search-button { margin: 0 0 10px 0; width: 100%; text-align: right; } } </style>
yxtTable.json
{ "retCode": "0", "retMsg": "success", "pageInfo": { "total": 300 }, "tasks": [ { "name": "張三", "code": "zhangSan", "status": 1, "icon": true, "phone": "17801010101", "selectable": 1 }, { "name": "李四", "code": "liSi", "status": 0, "icon": false, "phone": "17802020202", "selectable": 2 }, { "name": "王五", "code": "wangWu", "status": 2, "icon": true, "phone": "17803030303", "selectable": 0 }, { "name": "馬六", "code": "maLiu", "status": 1, "icon": false, "phone": "17804040404", "selectable": 2 } ] }
最后效果
總結(jié)
到此這篇關(guān)于elementUI el-table二次封裝的文章就介紹到這了,更多相關(guān)el-table二次封裝內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
前端XSS攻擊場景詳解與Vue.js處理XSS的方法(vue-xss)
這篇文章主要給大家介紹了關(guān)于前端XSS攻擊場景與Vue.js使用vue-xss處理XSS的方法,介紹了實際工作中渲染數(shù)據(jù)時遇到XSS攻擊時的防范措施,以及解決方案,需要的朋友可以參考下2024-02-02解決Vue3?echarts?v-show無法重新渲染的問題
這篇文章主要介紹了Vue3?echarts?v-show無法重新渲染的問題,本文通過示例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-09-09Vue使用vue-area-linkage實現(xiàn)地址三級聯(lián)動效果的示例
很多時候我們需要使用地址三級聯(lián)動,即省市區(qū)三級聯(lián)動,這篇文章主要介紹了Vue使用vue-area-linkage實現(xiàn)地址三級聯(lián)動效果的示例,感興趣的小伙伴們可以參考一下2018-06-06