「这是我参与11月更文挑战的第5天,活动详情查看:2021最后一次更文挑战」
一、效果
此组件使用起来非常简单,只需配置如下:
/**
* 表格列项数据 (必传)
* label 表格列项名称
* prop: 表格数据的字段名
* width 列项宽度
* align 内容对齐方式 (默认居中)
* headerAlign 表头对齐方式 (默认居中)
* columnType 是否开启插槽
* soltName 插槽名(自定义)
*/
tableColumnOptions: [
{ label: "账号", prop: "id" },
{ label: "姓名", prop: "name" },
{ label: "tag", prop: "type", columnType: true, soltName: "tag" },
{ label: "特殊样式", columnType: true, soltName: "link" },
{ label: "操作", width: "300", columnType: true, soltName: "operation"},
]
即可显示:
二、技术点
1. 组件传值
通过 Prop 向子组件传递数据
Prop 是你可以在组件上注册的一些自定义 attribute。当一个值传递给一个 prop attribute 的时候,它就变成了那个组件实例的一个 property,使用起来也很简单:
- 在父组件的子组件调用标签上:绑定自定义属性名及属性值
// partent.vue
<template>
<div>
//在父组件的子组件调用标签上:绑定自定义属性名及属性值
<childTemp :title="partentTitle"></childTemp>
</div>
</template>
<script>
import childTemp from "./child.vue"
export default {
compontent:{childTemp},
data(){
return{
partentTitle:"xxxxxxxx"
}
}
}
</script>
- 在子组件中:使用props接收传过来的值,接收到的数据使用方法与data中的数据一致
// child.vue
<template>
<div>
<h2>{{title}}</h2>
</div>
</template>
<script>
export default {
props:['title'] //在子组件中使用props接收在父组件中绑定的title
}
</script>
监听子组件事件
- 在子组件的点击事件中绑定一个函数,再通过this.$emit("
自定义一个父组件的事件名",参数) 传递数据
//child.vue
<template>
<div>
<button @click="childClick">按钮</button>
</div>
</template>
<script>
export default {
props:['title'],
data(){
return{
childData:"来自子组件的数据"
}
},
methods:{
childClick(){
this.$emit("fun",this.childData)
}
}
}
</script>
- 在父组件中:在调用标签上使用v-bind(简写为@)绑定刚才在子组件中定义的事件名fun,在赋予其一个自定义函数, 刚才在子组件中通过this.$emit传递的参数会自动注入至此自定义函数的参数中
<template>
<div>
<childTemp @fun="getData"></childTemp>
</div>
</template>
<script>
import childTemp from "../co/PropsChild.vue"
export default {
components:{childTemp},
data(){
return{
partentTitle:"xxxxxxxx"
}
},
methods:{
getData(e){//
console.log(e); //打印出 "来自子组件的数据"
}
}
}
</script>
2.具名插槽
具名插槽可以出现在不同的地方,不限制出现的次数。只要匹配了 name 那么这些内容就会被插入到这个 name 的插槽中去。
跟 v-on 和 v-bind 一样,v-slot 也有缩写,即把参数之前的所有内容 (v-slot:) 替换为字符 #。例如 v-slot:header 可以被重写为 #header:
- 父组件中
<template #header>xxxxxx</template>标签会自动取找子组件中那个叫header的slot标签,将template标签内包裹的所有东西替换掉那个slot标签
<template>
<div>
<childTemp>
<template #header>我这里写的东西,他会自动去找子组件中叫header的slot标签,替换掉他</template>
<!-- <template #main>我这里写的东西,他会自动去找子组件中叫main的slot标签,替换掉他</template> -->
<template #footer>我这里写的东西,他会自动去找子组件中叫footer的slot标签,替换掉他</template>
</childTemp>
</div>
</template>
<script>
import childTemp from "./childTemp.vue";
export default {
components: { childTemp }
};
</script>
</script>
- 子组件中
<template>
<div class="container">
我没在slot标签里,所以还是可以看见我
<header style="background:blue">
<slot name="header">写了又怎样,只要人家调用我,我就被替换掉了</slot>
</header>
<main style="background:red">
<slot name="main">不调用我的话我就还在</slot>
</main>
<footer style="background:pink">
<slot name="footer"></slot>
</footer>
</div>
</template>
<script>
</script>
提示,
v-slot指令自 Vue 2.6.0 起被引入,提供更好的支持slot和slot-scopeattribute 的 API 替代方案。在接下来所有的 2.x 版本中slot和slot-scopeattribute 仍会被支持,但已经被官方废弃且不会出现在 Vue 3 中。
三、回归正题,封装组件
封装的table组件代码如下
BaseTable.vue
<template>
<div id="Wrap">
<!-- 表格 -->
<el-table height="100px" @selection-change="selectionChange" class="table" :data="tableData" :border="hasBorder" :row-class-name="tableRowClassName">
<el-table-column type="selection" width="50" v-if="selectionShow"> </el-table-column>
<!-- 序号 -->
<el-table-column v-if="hasIndex" type="index" label="序号" header-align="center" align="center" width="80" > </el-table-column>
<!-- 其他 -->
<template v-for="item in tableColumnOptions">
<!-- 插槽列 -->
<el-table-column
v-if="item.columnType"
:key="item.label"
:prop="item.prop"
:label="item.label"
:width="item.width"
:header-align="item.headerAlign || 'center'"
:align="item.align || 'center'"
>
<template slot-scope="{ row }">
<slot :name="item.soltName" :data="row"></slot>
</template>
</el-table-column>
<!-- 非插槽列 -->
<el-table-column
v-else
:key="item.label"
:prop="item.prop"
:label="item.label"
:width="item.width"
:header-align="item.headerAlign || 'center'"
:align="item.align || 'center'"
>
</el-table-column>
</template>
</el-table>
<!-- 分页器 -->
<div class="pagination" v-if="paginationShow">
<el-pagination
:hide-on-single-page="isShowPagination"
:page-sizes="[10, 20, 50, 100]"
:current-page="currentPage"
:page-size="pageSize"
:total="tableDataTotal"
layout="total, sizes, prev, pager, next, jumper"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
>
</el-pagination>
</div>
</div>
</template>
<script type="text/ecmascript-6">
export default {
name: "BaseTable",
props: {
// 表格数据
tableData: {
type: Array,
default() {
return [];
},
},
// 表格列项
tableColumnOptions: {
type: Array,
require: true,
default() {
return [];
},
},
// 表格数据总量
tableDataTotal: {
type: Number,
default() {
return 0;
},
},
// 是否具有索引
hasIndex: {
type: Boolean,
default() {
return true;
},
},
// 是否显示边框
hasBorder: {
type: Boolean,
default() {
return false;
},
},
// 是否显示选择框
selectionShow: {
type: Boolean,
default() {
return true;
},
},
// 是否显示选分页器
paginationShow: {
type: Boolean,
default() {
return true;
},
},
},
data() {
return {
// 表格当前页数
currentPage: 1,
// 表格每页数量
pageSize: 10,
};
},
computed: {
// 计算是否显示分页器
isShowPagination() {
const isShow = this.tableDataTotal === 0;
return isShow;
},
},
methods: {
// 表格变色
tableRowClassName({ row, rowIndex }) {
if (rowIndex % 2 == 1) {
return "color-row";
}
},
// 修改当前页
handleCurrentChange(val) {
const params = {
currentPage: val,
pageSize: this.pageSize,
};
this.$emit("tableUpdate", params);
},
// 修改每页数量
handleSizeChange(val) {
const params = {
currentPage: this.currentPage,
pageSize: val,
};
this.$emit("tableUpdate", params);
},
// 选择框改变
selectionChange(e){
this.$emit('selectionChange',e)
}
},
};
</script>
<style scoped>
#Wrap {
width: 100%;
height: 100%;
box-sizing: border-box;
background-color: #fff;
display: flex;
flex-direction: column;
}
>>>.el-table{
/* overflow-y:scroll!important; */
}
#Wrap .pagination{
margin-top: auto;
margin-left: auto;
}
>>>.cell{
text-align: center;
}
>>> .cell #link{
font-family: MicrosoftYaHei;
color: #445af7;
cursor: pointer;
}
>>> .cell #delete {
color: #ff5b5b;
margin-left: 35px;
cursor: pointer;
}
>>>.color-row{
background-color: rgb(243, 243, 243);
}
>>>.el-table--border::after, .el-table--group::after, .el-table::before {
content: '';
position: absolute;
background-color: transparent!important;
z-index: 1;
}
</style>
五、使用
在要使用的页面中
<template>
<div id="baseButton">
<BaseTable
:has-index="true"
:has-border="false"
:table-data="tableData"
:table-data-total="tableData.length"
:table-column-options="tableColumnOptions"
@tableUpdate="tableUpdate"
@selectionChange="selectionChange"
>
<!-- 类型插槽 -->
<template #tag="{ data }">
<div class="tag">
<el-tag type="success" v-if="data.type == 1">tag1</el-tag>
<el-tag type="danger" v-if="data.type == 0">tag2</el-tag>
</div>
</template>
<!-- 操作插槽 -->
<template #operation="{ data }">
<span @click="tableRowEdit(data)">编辑</span>
<span @click="tableRowDelete(data)" id="delete">删除</span>
</template>
<!-- 链接插槽 -->
<template #link="{ data }">
<span id="link" @click="check(data)">查看</span>
</template>
</BaseTable>
</div>
</template>
<script>
import BaseTable from "../BaseTable.vue";
export default {
components: {
BaseTable,
},
methods: {
selectionChange(e) {
console.log("选择框改变时:",e);
},
tableUpdate(e) {
console.log("表格发生变化时",e);
},
tableRowEdit(e){
console.log("拿到此行的数据",e);
}
},
data() {
return {
//表格组件--模拟数据
tableData: [
{ id: 201791074073, name: "张三", type: 1 },
{ id: 201791074071, name: "李四", type: 0 },
{ id: 201791074074, name: "张二", type: 0 },
{ id: 201791074074, name: "张二", type: 0 },
{ id: 201791074074, name: "张二", type: 1 },
],
/**
* 表格列项数据 (必传)
* label 表格列项名称
* prop: ''
* width 列项宽度
* align 内容对齐方式 (默认居中)
* headerAlign 表头对齐方式 (默认居中)
* columnType 是否开启插槽
* soltName 插槽名
*/
tableColumnOptions: [
{ label: "账号", prop: "id" },
{ label: "姓名", prop: "name" },
{ label: "tag", prop: "type", columnType: true, soltName: "tag" },
{ label: "特殊样式", columnType: true, soltName: "link" },
{ label: "操作", width: "300", columnType: true, soltName: "operation"},
],
};
},
};
</script>
原理很简单,使用起来也很方便,注释写的很清楚了,普通数据就直接配置一个lable和prop,特殊样式或功能的配置columnType将插槽开启及soltName插槽名。在baseTable标签里里使用具名插槽去编写你的特殊样式。
这样,我们在不同页面调用此组件时,既保证了不同页面的表格风格一致,也保证不同页面的差异化,使用此组件最大的好处就是,表格的样式统一设置在封装的表格组件内,一处更改,其他地方随之更改,可以快速全局改变风格。