咱們項目中使用的表格通常都比較相似,若是不進行封裝的話,那麼每一個頁面均可能有一些相似的代碼。不只浪費時間,並且因爲開發人員不一樣的開發習慣。後期維護人員須要花費一點時間去看每一個人的代碼。因此我直接將表格作一個二次封裝,只要一我的去維護這份代碼便可。下面是我封裝的內容vue
一、支持直接傳入後臺請求地址渲染列表,且參數修改以後自動刷新
二、支持自定義每一列的顯示
三、支持根據內容自動撐開列寬
四、支持動態篩選表頭
五、支持分頁
六、防抖
七、列權限
...
更多請自行嘗試
複製代碼
<template>
<!--刷新按鈕 和 篩選列的多選框 layout中要有 control 才顯示,默認顯示-->
<div class="table">
<div class="table-control" v-if="layoutKey.control">
<div class="table-control-title" @click="reload">
<i class="el-icon-refresh"></i></div>
<el-dropdown class="table-control-title">
<span class="el-dropdown-link"><i class="el-icon-s-operation table-control-icon"></i></span>
<el-dropdown-menu slot="dropdown" class="table-control-checkbox">
<el-checkbox-group v-model="headItems" @change="changeChecked">
<el-checkbox class="table-control-checkbox-item"
v-for="(item,index) in allColumn"
:label="item"
:key="index">{{item}}
</el-checkbox>
</el-checkbox-group>
</el-dropdown-menu>
</el-dropdown>
</div>
<!--列表主體-->
<el-table class="table" style="width: 100%"
ref="tableList"
:data="tableData"
:defaultSort.async="defaultSort"
v-bind="$attrs"
v-on="$listeners"
@selectionChange="selectionChange"
@sort-change="sortChange">
<ex-table-column v-if="layoutKey.expand" type="expand" fixed>
<slot name="expand" :data="props" slot-scope="props"/>
</ex-table-column>
<ex-table-column v-if="layoutKey.checkBox" fixed type="selection" :width="62"
:selectable="selectable"/>
<ex-table-column v-if="layoutKey.index" type="index" label="序號" width="60"/>
<ex-table-column v-for="(column,index) in activeColumn" :key="index"
:prop="column.field"
:column-key="column.field" :label="column.title" :fixed="column.fixed"
:sortable="column.sort" :selectable="column.selectable"
:show-overflow-tooltip="column.tooltip"
:autoFit='true' :width="column.width"
:fitByClass="autoWidth(column.width)"
:minWidth="column.minWidth || defaultWidth">
<slot :name="column.field" :data="scope.row" :field="column.field" :content="column.field"
:index="index" slot-scope="scope">
<div>{{$utils.nvl(scope.row[column.field],'--')}}</div>
</slot>
</ex-table-column>
</el-table>
<!--分頁控件,layout中要有 control 才顯示,默認顯示-->
<el-pagination background small class="table-pagination"
:current-page.sync="page.pageNo"
:page-sizes="page.list"
:page-size="page.pageSize"
:layout="page.layout" :total="page.total"
@current-change="handleCurrentChange"
@size-change="handleSizeChange" v-if="layoutKey.page"/>
</div>
</template>
<script>
import debounce from 'lodash/debounce';
import ExTableColumn from './ExTableColumn';
export default {
components: { ExTableColumn },
// 提供出來給其餘組件調用,具體用法參考 vue 官方用法
provide() {
return {
tableList: this,
};
},
props: {
// 默認的表格大小
defaultWidth: {
type: Number,
required: false,
default: 100,
},
// 顯示的控件,目前就page,control 兩個可選,可根據需求自行擴展
layout: {
default: 'page,control',
},
// 多選時 返回的key,默認id
checkKey: {
type: [Number, String],
default: () => 'id',
},
// 請求參數,必填
req: {
type: Object,
required: true,
default: () => ({
url: undefined,
type: 'post',
params: {
query: {},
},
pageNo: 1,
pageSize: 1,
}),
},
// 默認排序,參考 elementUI table 用法
defaultSort: {
type: [String, Object],
required: false,
},
// 列表顯示的列
// {
title : 必填 String, 顯示的列名
field : 必填 String , 列中的key
width : 選填,String 列寬,單位像素,
fixed : 選填,String 是否固定的列,可選 right, left
sort : 選填,Boolean 是否可排序
expend: 選填,Boolean 是否可展開,配置slot:expand 顯示展開內容
limit : 選填,Boolean 權限控制,false 則不顯示
}
columns: {
type: Array,
required: true,
default: () => [{ title: '操做', field: 'ctrl', width: '60', fixed: 'right' }],
},
// 這一行的 CheckBox 是否能夠勾選,用法參考elementUI table用法
selectable: {
type: Function,
default: () => true,
},
// 其餘table參數,都會傳給table
},
data() {
return {
layoutKey: {},
page: {
list: [5, 10, 20, 50, 100],
total: null,
pageSize: 10,
pageNo: 1,
layout: 'total,sizes,prev,pager,next,jumper',
},
tableData: [],
sort: '',
checkRows: [],
checkKeys: [],
headItems: [],
allColumn: [],
activeColumn: [],
};
},
methods: {
sortChange({ prop, order }) {
this.sort = `${prop} ${order === 'descending' ? 'desc' : 'asc'}`;
this.refresh();
},
selectionChange(selection) {
this.checkRows = selection;
this.checkKeys = [];
selection.map((row) => {
this.checkKeys.push(row[this.checkKey]);
return true;
});
this.$emit('selectionChange', selection);
},
/** **************************method*********************************** */
// 分頁
async handleCurrentChange(currentPage) {
this.page.pageNo = currentPage;
this.$emit('handleCurrentChange', currentPage);
await this.initTable({ vm: this });
},
handleSizeChange(size) {
this.page.pageSize = size;
this.page.pageNo = 1;
this.$emit('handleSizeChange', size);
this.initTable({ vm: this });
},
/** *****************************動態渲染頭部************************************* */
// 取消選中菜單
changeChecked() {
this.changeColumn({ vm: this });
},
changeColumn: debounce(async ({ vm }) => {
const that = vm;
const keys = new Set(vm.headItems);
const activeColumn = vm.columns.filter((item) => {
if (keys.has(item.title)) {
return true;
}
return false;
});
that.activeColumn = activeColumn;
that.activeColumn.splice(1, 0);
}, 200),
/** *****************************刷新************************************* */
// 刷新表格數據(使用現有參數)
refresh(type) {
if (type === 'delete' && this.page.pageNo > 1 && this.tableData.length === 1) {
this.page.pageNo = this.page.pageNo - 1;
}
this.initTable({ vm: this });
},
// 從新加載數據(重置到第一頁)
reload() {
if (this.page.pageNo !== 1) {
this.page.pageNo = 1;
}
this.initTable({ vm: this });
},
initTable: debounce(async ({ vm }) => {
const that = vm;
that.tableData = [];
const params = that._.assign({
pageNo: that.page.pageNo,
pageSize: that.page.pageSize,
sortStr: that.sort
}, that.req.params); // 右值覆蓋左值,返回左值
// 發起請求,根據實際項目中,接口來作
const { data } = await window.axios[that.req.type || 'post'](that.req.url, params);
if (data && that.$utils.Type.isArray(data.result)) {
that.tableData = data.result;
that.page.total = data.total * 1;
}
that.$nextTick(() => {
that.$emit('loadDone', that.tableData, params);
});
}, 300),
getCheckRows() {
return this.checkRows;
},
getCheckKeys() {
return this.checkKeys;
},
handleHead(columns) {
const allColumn = [];
columns.map((item) => {
if (!item.limit) {
allColumn.push(item.title);
}
return true;
});
this.headItems = allColumn;
this.allColumn = allColumn;
this.changeChecked();
},
handleLayout() {
const layout = this.layout;
if (!layout) return null;
layout.split(',')
.map(
(item) => {
const key = item.trim();
this.layoutKey[key] = true;
return true;
},
);
return this.layoutKey;
},
autoWidth(width) {
if (this.$utils.isEmpty(width)) {
return 'cell';
}
return width;
},
init() {
this.handleLayout();
this.handleHead(this.columns);
if (this.defaultSort) {
const { prop, order } = this.defaultSort;
this.sort = `${prop} ${order === 'descending' ? 'desc' : 'asc'}`;
}
this.initTable({ vm: this });
}
,
},
async created() {
this.init();
},
watch: {
queryParams: {
handler() {
this.reload({ vm: this });
},
},
columns: {
handler() {
this.handleHead(this.columns);
},
},
},
computed: {
queryParams() {
if (this.$utils.isNotEmpty(this.req.params)) {
return this.req.params;
}
return {};
},
},
};
</script>
<style lang="less" scoped>
@import './style/index';
</style>
複製代碼
<template>
<div>
<table-list
:req="tableReq"
ref="tableList"
:stripe="true" // table 原來的參數也是支持使用的,方法也支持使用。
:columns="[ {title:'用戶名',field:'name',sort:'true',fixed:true}, {title:'英文名',field:'aliasName',sort:'true',fixed:true}, {title:'年齡',field:'age', sort:true}, {title:'職業',field:'job', sort:true}, {title:'郵箱',field:'email'}, {title:'出生日期',field:'birthday'}, {title:'家庭住址',field:'address'}, {title:'戶籍',field:'domicile'}, ]">
<!--格式化時間列,全部的列均可以這麼使用,slot 名爲列field-->
<template slot="birthday" slot-scope="{data}">
<span>{{format(data.birthday)}}</span>
</template>
</table-list>
</div>
</template>
<script>
import TableList from './table/components/TableList';
export default {
name: 'HelloWorld',
components: { TableList },
data() {
return {
tableReq: {
url: '/user/list', //必填
type: 'post', //可選, 默認 post
params: {} // 可選
}
};
},
methods: {
format(time) {
if (this.$utils.isEmpty(time)) return '/';
return this.$utils.format(new Date(time), 'yyyy-MM-dd HH:mm');
},
},
mounted() {
}
};
</script>
複製代碼