欢迎点击领取 -《前端开发面试题进阶秘籍》:前端登顶之巅-最全面的前端知识点梳理总结
vue2.X element-ui table的二次封装
1. 简介
因小编之前主写react,已经习惯数据表格的json配置;而element-ui的table主要是以template的方式进行页面的渲染,容易造成业务代码可观性并不是很友好,维护也不是很方便;element-ui的成熟度、以及使用率很大,市场上面的大多封装也很多,可扩展性也很方便。这里小编也做了一套简易版的二次封装,希望能帮到各位。
2. 展现
组件名我称为TableList,一个可以快速生成查询列表的封装组件,因项目组的原因未能使用jsx+render的方式来进行组件式的开发,有兴趣的可以自己试试;为了使扩展性更强,下面将Search查询组件和Table组件进行了分离封装;写过react的大家都知道props的传参方式{…props}很方便,那么vue的怎么支持这样的一个方式,下面我就使用到了v-bind属性绑定方式
3. 实现方式及使用
- TableList组件 github地址
- Npm使用地址
- 可以在component中创建TableList文件复制组件代码,在业务组件中引入即可;
/**
* TableList表格组件
*/
/**
* Table表格
*/
<template>
<div class="table_list_fix">
<!-- 扩展性内容 -->
<slot name="content_context"></slot>
<!-- table中间button eg:导出 -->
<div v-if="extendButton && extendButton.length > 0" class="btn-operates">
<a :key="index" :href="item.href || null" @click="item.method()" v-for="(item, index) in extendButton">
<el-button size="small" type="primary">{{ item.title }}</el-button>
</a>
</div>
<!-- table表格 -->
<el-table
size="small"
v-bind="options"
:border="border"
:data="dataSource"
v-loading="loading"
v-on="tableEvents"
ref="multipleTable"
style="width: 100%;"
:header-cell-style="{ background: '#F5F7FA' }"
@selection-change="handleSelectionChange"
>
<!-- 复选框 -->
<el-table-column
type="selection"
style="width: 55px;"
v-if="options && options.selection && (!options.isShow || options.isShow())"
/>
<el-table-column
width="55"
align="center"
type="index"
v-if="options && options.index"
:label="options && options.labelIndex"
/>
<!-- 表格数据 -->
<template v-for="(column, index) in columns">
<el-table-column
:key="index"
v-bind="column.props"
:prop="column.prop"
:label="column.label"
:align="column.align"
:width="column.width"
show-overflow-tooltip
v-if="!column.isShow || (column.isShow && column.isShow())"
>
<template slot-scope="scope">
<template v-if="!column.render">
<template v-if="column.formatter">
<span
v-html="column.formatter(scope.row, column, scope.$index)"
@click="column.click && column.click(scope.row, scope.$index)"
></span>
</template>
<!-- 跳转操作 -->
<template v-else-if="column.newjump">
<router-link
class="newjump"
v-bind="{ target: '_blank', ...column.target }"
:to="column.newjump(scope.row, column, scope.$index)"
>{{ scope.row[column.prop] || column.content }}</router-link
>
</template>
<!-- slot插槽 -->
<template v-else-if="column.slot">
<slot :slotName="column.slot" :row="scope.row" :index="index" />
</template>
<!-- 操作按钮 -->
<template v-else-if="column.children">
<template v-for="(btn, key) in column.children">
<span :key="key" v-if="!btn.isShow || (btn.isShow && btn.isShow(scope.row, scope.$index))">
<el-button
:icon="btn.icon"
:plain="btn.plain"
style="padding: 6px"
:loading="scope.row?.loading"
:size="btn.size || 'small'"
:type="btn.type || 'text'"
:type="btn.type ? btn.type : 'primary'"
:disabled="btn.disabled && btn.disabled(scope.row, scope.$index)"
@click="btn.method(scope.row, scope.$index)"
>{{ btn.label }}</el-button
>
</span>
</template>
</template>
<template v-else>
<span
:style="column.click ? 'color: #409EFF; cursor: pointer;' : null"
@click="column.click && column.click(scope.row, scope.$index)"
>
{{ scope.row[column.prop] || column.content }}
{{ `${scope.row[column.prop] && column.unit ? column.unit : ''}` }}
</span>
</template>
</template>
<template v-else>
<render :column="column" :row="scope.row" :render="column.render" :index="index"></render>
</template>
</template>
</el-table-column>
</template>
</el-table>
<br />
<!-- 分页部分 -->
<el-pagination
background
v-if="pagination"
:total="dataTotal"
@size-change="handleSizeChange"
:page-sizes="[5, 10, 20, 30, 50, 100]"
:current-page="pagination.currentPage"
:page-size="pagination.pageSize"
@current-change="handleChangePage"
:class="options && options.pageExtendLayout ? '' : 'pagination'"
:layout="options && options.pageExtendLayout || 'total, prev, pager, next'"
/>
</div>
</template>
<script>
const methods = {
// 复选框选中项
handleSelectionChange(val) {
this.multipleSelection = val;
this.$emit('handleSelectionChange', Array.from(val));
},
// 改变分页触发事件
handleChangePage(val) {
this.$emit('current-change', val);
},
handleSizeChange(page) {
this.$emit('size-change', page);
}
};
export default {
name: 'TableList',
props: {
dataSource: {
type: Array,
default: () => []
},
columns: {
type: Array,
default: () => []
},
border: {
type: Boolean,
default: () => false
},
loading: {
type: Boolean,
default: () => false
},
slotcontent: {
type: Boolean,
default: () => false
},
dataTotal: {
type: Number,
default: 0
},
operates: Array,
pagination: Object,
extendButton: Array,
options: Object,
tableEvents: Object
},
data() {
return {
multipleSelection: []
};
},
methods,
mounted() {
this.$nextTick(() => {
this.$emit('toggleRowSelection', this.$refs.multipleTable);
});
},
components: {
render: {
functional: true,
props: {
row: Object,
render: Function,
index: Number,
column: {
type: Object,
default: null
}
},
render: (h, opt) => {
const params = {
row: opt.props.row,
index: opt.props.index
};
if (opt.props.column) params.column = opt.props.column;
return opt.props.render(h, params);
}
}
}
};
</script>
<style lang="scss" scoped>
.table_list_fix {
overflow: hidden;
.btn-operates {
margin-bottom: 6px;
a {
color: #fff;
text-decoration: none;
display: inline-block;
}
}
}
.table-header {
padding-top: 10px;
.table-header_button {
text-align: right;
float: right;
margin-bottom: 12px;
line-height: 40px;
}
}
.newjump {
text-decoration: none;
color: dodgerblue;
}
.pagination {
text-align: end;
padding-bottom: 15px;
}
</style>
/**
* 查询条件组件
*/
<template>
<div class="formSearch">
<div class="table-header" v-if="tableSearch && tableSearch.length > 0">
<el-form
size="small"
:rules="rules"
:model="formSearch"
:inline="true"
ref="formSearch"
label-position="right"
v-bind="{'label-width': '110px', ...(options && options.formProps)}"
>
<el-form-item
class="table-header-item"
:label="item.label + ':'"
:prop="item.value"
:key="index"
v-bind="item.labelProps"
v-for="(item, index) in tableSearch"
>
<el-select
clearable
v-bind="item.props"
v-if="item.type === 'select'"
v-model="formSearch[item.value]"
:placeholder="`请选择${item.placeholder || item.label}`"
>
<el-option
v-for="option in item.children"
:key="option.value"
:value="option.value"
:label="option.label"
/>
</el-select>
<el-date-picker
clearable
style="width: 100%;"
placeholder="选择日期"
v-bind="item.props || {type: 'date'}"
v-else-if="item.type === 'picker'"
v-model="formSearch[item.value]"
/>
<el-input
v-else
clearable
v-bind="item.props"
:type="item.inputType || 'text'"
v-model="formSearch[item.value]"
:placeholder="`请输入${item.placeholder || item.label}`"
:maxlength="item.maxlength"
@keyup.enter.native="handleSearch"
:oninput="handleChangeInput(item)"
/>
</el-form-item>
<el-form-item>
<el-button size="small" type="primary" icon="el-icon-search" @click="handleSearch">查询</el-button>
<el-button
plain
size="small"
icon="el-icon-refresh-right"
@click="handleReset('formSearch')"
>重置</el-button>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script>
import { convertParams } from "@/util/utilTool";
let methods = {
// 搜索查询按钮
handleSearch() {
if (this.rules) {
return this.$refs["formSearch"].validate(valid => {
if (!valid) return false;
this.$emit(
"handleSearch",
convertParams(Object.assign({}, this.formSearch))
);
});
}
this.$emit(
"handleSearch",
convertParams(Object.assign({}, this.formSearch))
);
},
// 搜索重置按钮
handleReset(formName) {
this.$refs[formName].resetFields();
this.formSearch = this.reset ? { ...this.value } : {};
this.$emit("handleReset");
if (this.reset) return false;
this.handleSearch();
},
// input为number校验
handleChangeInput(item) {
return item.inputType === "number"
? this.handleOnInput(
this.formSearch[item.value],
item.value,
item.rulesLength,
item.maxlength
)
: null;
},
// input渲染长度校验
handleOnInput(val, label, rulesLength, maxlength) {
if (val && Number(val) <= 0) {
this.formSearch[label] = 0;
}
if (rulesLength && val && val.length > maxlength) {
this.formSearch[label] = this.formSearch[label].slice(0, maxlength);
}
}
};
export default {
name: "FormSearch",
props: {
tableSearch: {
type: Array
},
rules: {
type: Object
},
value: {
type: Object
},
reset: {
type: Boolean
},
rulesLength: {
type: Boolean
},
options: Object,
},
data() {
return {
formSearch: {
...this.value
}
};
},
methods,
watch: {
value(val) {
if (val) return (this.formSearch = { ...this.value });
}
}
};
</script>
<style lang="less" scoped>
.table-header {
padding-top: 10px;
.table-header-item .el-form-item {
width: 100%;
display: flex;
margin-bottom: 12px;
.el-form-item__content,
.el-select {
width: 100%;
}
}
}
</style>
4. TableList使用文档
https://github.com/njt1340953658/TableList
简单的一个思路就是将繁琐的事情,通过json的数据结构模式,来帮助我们渲染ui层,将一部分的逻辑代码放在我们的组件内部,从而减少业务层代码的复杂性操作。
5. 思考
- 如何在工作中达到快速开发,不在做重复性工作?
- 怎样去思考自己的业务线去发展,沉淀出属于自己的东西
- 如何去封装组件,怎么样才能够到达可观性、复用性,降低团队的开发成本
- 身在职位长期的一个积累与思考、认知度要逐步去提高
- 一定要学会去看api使用文档,在框架的时代也要逐步去习读源码。
6. 结语
小编也是前端小白,代码也有不合理之处,只是在闲余时间内简单写写,提升提升能力。以上代码可根据自己的项目去做调整。