一、简介
公司业务可能需要进行一些组件的封装,基于第三方elementPlus框架,进行符合UI设计原型的组件封装,这篇主要讲解Table表格的封装,目的主要是梳理封装的思路,下面的代码并不是提供完整的源码,因此不包含样式代码。
二、环境准备
webpack+vue3+elementPlus
官方地址:https://element-plus.gitee.io/zh-CN/component/table.html
三、实现步骤
1,成果示例
2,需求分析
(1)表格组件内包含表格数据展示+分页器+搜索input;
(2)表格每列的内容自定义修改,并且支持自定义标签;
(3)搜索和分页器都需要和表格数据联动;
(4)checkbox勾选状态和分页器联动;
(5)搜索栏支持自定义;
3,具体代码
(1)子组件
最终代码示例:
<el-table
ref="table"
:data="tableData"
:height="height"
:style="{ fontSize: fontSize, fontFamily: fontFamily }"
:border="border"
:stripe="stripe"
:row-key="
(row) => {
return row.id;
}
"
:header-cell-style="{ 'text-align': headerCellStyle }"
:cell-style="cellStyle"
@selection-change="handleSelectionChange"
>
<!-- checkbox列 -->
<el-table-column
v-if="selection"
type="selection"
width="56"
align="center"
:fixed="fixed"
>
</el-table-column>
<!-- 序号列 -->
<el-table-column
v-if="index"
type="index"
width="56"
label="序号"
:fixed="fixed"
></el-table-column>
<!-- columnData属性列 -->
<template v-for="item in columnData" :key="item.id">
<el-table-column
v-if="item.type === 'link'"
:label="item.label"
:prop="item.prop"
:width="item.width"
:align="item.align"
:fixed="item.fixed"
:sortable="item.sort"
:formatter="item.formatter"
>
<template #default="scope">
<slot :name="item.prop" v-bind="scope"></slot>
</template>
</el-table-column>
<el-table-column
v-else-if="item.type === 'tag'"
:label="item.label"
:prop="item.prop"
:width="item.width"
:align="item.align"
:fixed="item.fixed"
:sortable="item.sort"
:filters="item.filters"
:filter-method="filterTag"
:formatter="item.formatter"
>
<template #default="scope">
<slot :name="item.prop" v-bind="scope"></slot>
</template>
</el-table-column>
<el-table-column
v-else
:label="item.label"
:prop="item.prop"
:width="item.width"
:align="item.align"
:fixed="item.fixed"
:sortable="item.sort"
:formatter="item.formatter"
>
</el-table-column>
</template>
</el-table>
实现思路:
1)table的属性和column的属性要分开;
<!--摘自elementPlus表格(基础用法)-->
<template>
<el-table :data="tableData" style="width: 100%">
<el-table-column prop="date" label="Date" width="180" />
<el-table-column prop="name" label="Name" width="180" />
<el-table-column prop="address" label="Address" />
</el-table>
</template>
原因:我们来分析一下上面的代码,首先我们需要用组件封装的思想思考。el-table标签上的属性都可以通过父子传值的方式传进组件。但是el-table-column到底有几个我们不确定,那我们很自然的就会想到组件内部需要用到v-for循环,循环的数据也是通过外部传进来。
所以子组件内部发展为:
<el-table
ref="table"
:data="tableData"
>
<!-- columnData属性列 -->
<template v-for="item in columnData" :key="item.id">
<el-table-column
:label="item.label" //每列标题内容,参考elemengPlus
:prop="item.prop" //每列字段名称,参考elemengPlus
:width="item.width" //每列宽度,参考elemengPlus
:align="item.align" //每列对齐方式,参考elemengPlus
:fixed="item.fixed" //首列和标题固定,参考elemengPlus
:sortable="item.sort" //排序,参考elemengPlus
:formatter="item.formatter" //支持内容自定义方法,参考elemengPlus
>
</el-table-column>
</template>
</el-table>
<script setup>
import {defineProps} from 'vue'
defineProps({
tableData:{
type:Array
},
columnData:{
type:Array
}
})
</script>
父页面传入的数据发展为:
const columnData = ref([
{
id: 0,
prop: "contractName",
// width: "140",
align: "left",
label: "常规名称长字段",
},
{
id: 1,
prop: "contractDate",
// width: "200",
align: "left",
label: "合同日期",
},
{
id: 2,
prop: "contractAmout",
// width: "130",
align: "right",
label: "合同金额",
sort: true,
formatter: (row) => {
return <div>{row.contractAmout}万</div>;
},
},
{
id: 3,
prop: "contractNum",
// width: "100",
align: "left",
label: "合同编号",
},
{
id: 4,
prop: "operation",
width: "150",
align: "left",
label: "操作",
type: "slot",
formatter: (row,column,cellValue,index) => {
//编辑按钮事件
const handleEdit = (val) => {
console.log('row',row);
console.log('column',column);
console.log('cellValue',cellValue);
console.log('index',index);
// console.log('val',val);
alert(val.contractName);
};
//删除按钮事件
const handleDelete = (val) => {
console.log('row',row);
console.log('column',column);
console.log('cellValue',cellValue);
console.log('index',index);
console.log('row',row);
// console.log('val',val);
alert(val.contractName);
};
return (
<div>
<el-button
type="primary"
size="small"
onClick={() => {
handleEdit(row,column,cellValue,index);
}}
>
编辑
</el-button>
<el-button
type="danger"
size="small"
onClick={() => {
handleDelete(row,column,cellValue,index);
}}
>
删除
</el-button>
</div>
);
},
},
]);
//表格数据的key键名要和columnData中的prop保持一致
const tableData = ref([
{
"id": "1",
"contractName": "医院设备1",
"contractDate": "2023-01-30 14:44:19",
"contractAmout": "50000",
"contractNum": "1",
},
{
"id": "2",
"contractName": "医院设备2",
"contractDate": "2023-01-29 14:44:19",
"contractAmout": "30000",
"contractNum": "2",
},
{
"id": "3",
"contractName": "医院设备3",
"contractDate": "2023-01-28 15:44:19",
"contractAmout": "75000",
"contractNum": "3",
},
{
"id": "4",
"contractName": "医院设备4",
"contractDate": "2023-01-27 15:44:19",
"contractAmout": "80000",
"contractNum": "4",
},
{
"id": "5",
"contractName": "医院设备5",
"contractDate": "2023-01-26 15:44:19",
"contractAmout": "80000",
"contractNum": "5",
},
{
"id": "6",
"contractName": "医院设备6",
"contractDate": "2023-01-25 15:44:19",
"contractAmout": "80000",
"contractNum": "6",
},
{
"id": "7",
"contractName": "医院设备7",
"contractDate": "2023-01-24 15:44:19",
"contractAmout": "80000",
"contractNum": "7",
},
{
"id": "8",
"contractName": "医院设备8",
"contractDate": "2023-01-23 15:44:19",
"contractAmout": "80000",
"contractNum": "8",
},
{
"id": "9",
"contractName": "医院设备9",
"contractDate": "2023-01-22 15:44:19",
"contractAmout": "80000",
"contractNum": "9",
},
{
"id": "10",
"contractName": "医院设备10",
"contractDate": "2023-01-21 15:44:19",
"contractAmout": "80000",
"contractNum": "10",
}
]);
注意:表格数据的key键名必须和columnData中的prop保持一致;
2)表格的checkbox列和序号列由外部决定是否需要;
也就是外部传入个布尔值去控制是否渲染这两列即可,所以子组件再前面的基础上再加上:
<!-- checkbox列 -->
<el-table-column
v-if="selection"
type="selection"
width="56"
align="center"
:fixed="fixed"
>
</el-table-column>
<!-- 序号列 -->
<el-table-column
v-if="index"
type="index"
width="56"
label="序号"
:fixed="fixed"
></el-table-column>
<script setup>
import {defineProps} from 'vue'
defineProps({
tableData:{
type:Array
},
columnData:{
type:Array
},
selection: Boolean,
index: Boolean,
})
</script>
如果需要渲染这两列,父页面使用的时候直接传入相应的布尔值即可,如下示例:
<flex-table
ref="flexTable"
:columnData="columnData"
:tableData="tableData"
selection
index
>
3)插槽支持更多框架内容自定义;
由于formatter的方法内部是react写法,不支持template标签。如果自定义的内容包含el-tag标签或其他可扩展内容时,可以通过插槽实现;
子组件内部就变成了:
<!-- columnData属性列 -->
<template v-for="item in columnData" :key="item.id">
<!-- 扩展列 -->
<el-table-column
v-if="item.type === 'link'"
:label="item.label"
:prop="item.prop"
:width="item.width"
:align="item.align"
:fixed="item.fixed"
:sortable="item.sort"
:formatter="item.formatter"
>
<template #default="scope">
<slot :name="item.prop" v-bind="scope"></slot>
</template>
</el-table-column>
<!-- 标签列 -->
<el-table-column
v-else-if="item.type === 'tag'"
:label="item.label"
:prop="item.prop"
:width="item.width"
:align="item.align"
:fixed="item.fixed"
:sortable="item.sort"
:filters="item.filters"
:filter-method="filterTag"
:formatter="item.formatter"
>
<template #default="scope">
<slot :name="item.prop" v-bind="scope"></slot>
</template>
</el-table-column>
<!-- 其他正常列 -->
<el-table-column
v-else
:label="item.label"
:prop="item.prop"
:width="item.width"
:align="item.align"
:fixed="item.fixed"
:sortable="item.sort"
:formatter="item.formatter"
>
</el-table-column>
</template>
父页面使用的时候就可以通过插槽的名称来实现内容的自定义:
tag标签插槽:
<template #state="{ row }">
<el-tag v-if="row.state === '进行中'" type="primary">{{
row.state
}}</el-tag>
<el-tag v-if="row.state === '已完成'" type="success">{{
row.state
}}</el-tag>
<el-tag v-if="row.state === '已终止'" type="danger">{{
row.state
}}</el-tag>
<el-tag v-if="row.state === '已中止'" type="warning">{{
row.state
}}</el-tag>
<el-tag v-if="row.state === '已取消'" type="info">{{
row.state
}}</el-tag>
</template>
link扩展链接插槽:
<template #link="{ row }">
<ul class="linkMenu">
<li>
<el-link type="primary" v-if="row.link === 1">催填</el-link>
<el-link type="primary" v-if="row.link === 2">汇总</el-link>
<el-link type="primary" v-if="row.link === 3">重新汇总</el-link>
<el-link type="primary" v-if="row.link === 4">重新确认</el-link>
<el-link type="primary" v-if="row.link === 5">调整</el-link>
</li>
</ul>
</template>
4)配置搜索表单
<!--labelWidth和labelPosition是el-form的属性,参考elementPlus-->
<el-form :label-width="labelWidth" :label-position="labelPosition">
<slot name="search"></slot>
</el-form>
在el-table标签上方加上如上代码,使用插槽支持自定义;
在父页面使用的时候:
<!-- 搜索插槽 -->
<template v-slot:search>
<div class="ipt-btn">
<!-- 输入框 -->
<el-row gutter="0">
<!-- 第一行 -->
<el-col :span="8">
<el-form-item label="标题标题">
<el-input
placeholder="请输入"
v-model="form.value1"
@keyup.enter="handleFilter(form)"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="标题标题">
<el-input
placeholder="请输入"
v-model="form.value2"
@keyup.enter="handleFilter(form)"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="标题标题">
<el-input
placeholder="请输入"
v-model="form.value3"
@keyup.enter="handleFilter(form)"
/>
</el-form-item>
</el-col>
<!-- 第二行 -->
<el-col :span="8">
<el-form-item label="标题标题" v-show="isShow">
<el-select v-model="form.value5" @change="handleFilter(form)">
<el-option label="选项一" value="选项一">选项一</el-option>
<el-option label="选项二" value="选项二">选项二</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="标题标题" v-show="isShow">
<el-date-picker
value-format="YYYY-MM-DD"
placeholder="请选择"
v-model="form.value6"
@change="handleFilter(form)"
></el-date-picker>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="标题标题" v-show="isShow">
<el-date-picker
type="datetime"
placeholder="请选择"
v-model="form.value7"
value-format="YYYY-MM-DD HH:mm:ss"
@change="handleFilter(form)"
></el-date-picker>
</el-form-item>
</el-col>
</el-row>
<!-- 按钮 -->
<div class="btn">
<el-form-item class="btns">
<el-button
class="arrow"
:icon="isShow ? ArrowUp : ArrowDown"
@click="isShow = !isShow"
></el-button>
<el-button
class="reset"
@click="resetForm"
>重置</el-button
>
<el-button
class="confirm"
type="primary"
@click="onSubmit"
>确定</el-button
>
</el-form-item>
</div>
</div>
</template>
<script setup>
import {ref, reactive} from 'vue'
const isShow = ref(false);
const form = reactive({
value1: "张三",
value2: "10",
value3: "",
value4: "",
value5: "",
value6: "",
value7: "",
});
</script>
展开效果图:
隐藏效果图:
5)配置分页器
效果图:
直接运用el-pagination,html代码示例:
<div class="bottom_box">
<!-- 左侧统计checkbox选中状态 -->
<ul class="flex-ul">
<li>
已选择<span style="color: #3480fb" v-if="checkNumber > 0">{{
checkNumber
}}</span
>项
</li>
<li>|</li>
<li class="cancelCheckBox" @click="toggleSelection">
<span style="color: #3480fb">取消</span>
</li>
</ul>
<!-- 右侧分页器组件 -->
<el-pagination
:small="small" //是否启用小型分页器,字号12px
:background="background" //页码选中是否有背景色
layout="total, prev, pager, next, sizes, jumper" //分页器各组件
:total="total" //总条数
:pager-count="4" //设置最大按钮数
:page-size="pageSize" //每页显示条数
:page-sizes="pageSizes" //每页显示个数选择器的选项设置
@size-change="sizeChange" //page-size 改变时触发
@current-change="handleCurrentChange" //页码发生变化触发事件
/>
</div>
参考elementPlus的方法,结合vue3的写法,js代码示例:
//checkbox点击事件
const instance = getCurrentInstance();
const table = ref(null);
const multipleSelection = ref([]);
const checkNumber = ref(null);
//取消勾选事件
const toggleSelection = () => {
var ultipleTabInstance = toRefs(instance.refs.table);
ultipleTabInstance.clearSelection.value();
};
//勾选checkbox事件
const handleSelectionChange = (val) => {
console.log("val", val);
multipleSelection.value = val;
checkNumber.value = multipleSelection.value.length;
};
(2)父组件
最终代码示例:
<flex-table
ref="flexTable"
size="large"
disabled
background
prevIcon="ArrowLeft"
nextIcon="ArrowRight"
:url="apiUrl.tableList2"
:requestData="request_data"
:columnData="columnData"
:pageSizes="pageSizes"
fontSize="14px"
fontFamily="PingFangSC-Regular"
labelWidth="110"
selection
border
fixed
>
<!-- 链接插槽 -->
<template #link="{ row }">
<ul class="linkMenu">
<li>
<el-link type="primary" v-if="row.link === 1">催填</el-link>
<el-link type="primary" v-if="row.link === 2">汇总</el-link>
<el-link type="primary" v-if="row.link === 3">重新汇总</el-link>
<el-link type="primary" v-if="row.link === 4">重新确认</el-link>
<el-link type="primary" v-if="row.link === 5">调整</el-link>
</li>
<li>|</li>
<li v-if="row.link === 1">
<el-dropdown>
<span class="el-dropdown-link">
二级配置
<el-icon class="el-icon--right">
<arrow-down />
</el-icon>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>更多菜单一</el-dropdown-item>
<el-dropdown-item>更多菜单二</el-dropdown-item>
<el-dropdown-item>更多菜单三</el-dropdown-item>
<el-dropdown-item>更多菜单四</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</li>
<li v-if="row.link === 2">
<el-dropdown>
<span class="el-dropdown-link">
上会结束
<el-icon class="el-icon--right">
<arrow-down />
</el-icon>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>更多菜单一</el-dropdown-item>
<el-dropdown-item>更多菜单二</el-dropdown-item>
<el-dropdown-item>更多菜单三</el-dropdown-item>
<el-dropdown-item>更多菜单四</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</li>
<li v-if="row.link === 3">
<el-dropdown>
<span class="el-dropdown-link">
二级配置
<el-icon class="el-icon--right">
<arrow-down />
</el-icon>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>更多菜单一</el-dropdown-item>
<el-dropdown-item>更多菜单二</el-dropdown-item>
<el-dropdown-item>更多菜单三</el-dropdown-item>
<el-dropdown-item>更多菜单四</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</li>
<li v-if="row.link === 4">
<el-dropdown>
<span class="el-dropdown-link">
上会结束
<el-icon class="el-icon--right">
<arrow-down />
</el-icon>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>更多菜单一</el-dropdown-item>
<el-dropdown-item>更多菜单二</el-dropdown-item>
<el-dropdown-item>更多菜单三</el-dropdown-item>
<el-dropdown-item>更多菜单四</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</li>
<li v-if="row.link === 5">
<el-dropdown>
<span class="el-dropdown-link">
二级配置
<el-icon class="el-icon--right">
<arrow-down />
</el-icon>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>更多菜单一</el-dropdown-item>
<el-dropdown-item>更多菜单二</el-dropdown-item>
<el-dropdown-item>更多菜单三</el-dropdown-item>
<el-dropdown-item>更多菜单四</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</li>
</ul>
</template>
<!-- 标签插槽 -->
<template #state="{ row }">
<el-tag v-if="row.state === '进行中'">{{ row.state }}</el-tag>
<el-tag v-if="row.state === '已完成'" type="success">{{
row.state
}}</el-tag>
<el-tag v-if="row.state === '已终止'" type="danger">{{
row.state
}}</el-tag>
<el-tag v-if="row.state === '已中止'" type="warning">{{
row.state
}}</el-tag>
<el-tag v-if="row.state === '已取消'" type="info">{{
row.state
}}</el-tag>
</template>
<!-- 搜索插槽 -->
<template v-slot:search>
<div class="ipt-btn">
<!-- 输入框 -->
<el-row gutter="0">
<!-- 第一行 -->
<el-col :span="8">
<el-form-item label="标题标题">
<el-input
placeholder="请输入"
v-model="form.value1"
@keyup.enter="handleFilter(form)"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="标题标题">
<el-input
placeholder="请输入"
v-model="form.value2"
@keyup.enter="handleFilter(form)"
/>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="标题标题">
<el-input
placeholder="请输入"
v-model="form.value3"
@keyup.enter="handleFilter(form)"
/>
</el-form-item>
</el-col>
<!-- <el-col :span="6">
<el-form-item label="标题标题">
<el-input
v-model="form.value4"
placeholder="请输入"
@keyup.enter="handleFilter(form)"
></el-input>
</el-form-item>
</el-col> -->
<!-- 第二行 -->
<el-col :span="8">
<el-form-item label="标题标题" v-show="isShow">
<el-select v-model="form.value5" @change="handleFilter(form)">
<el-option label="选项一" value="选项一">选项一</el-option>
<el-option label="选项二" value="选项二">选项二</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="标题标题" v-show="isShow">
<el-date-picker
value-format="YYYY-MM-DD"
placeholder="请选择"
v-model="form.value6"
@change="handleFilter(form)"
></el-date-picker>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="标题标题" v-show="isShow">
<el-date-picker
type="datetime"
placeholder="请选择"
v-model="form.value7"
value-format="YYYY-MM-DD HH:mm:ss"
@change="handleFilter(form)"
></el-date-picker>
</el-form-item>
</el-col>
</el-row>
<!-- 按钮 -->
<div class="btn">
<el-form-item class="btns">
<el-button
style="
width: 30px;
height: 34px;
background: #eef1f5;
borderradius: 4px;
"
class="arrow"
:icon="isShow ? ArrowUp : ArrowDown"
@click="isShow = !isShow"
></el-button>
<el-button
class="reset"
@click="resetForm"
style="
width: 50px;
height: 34px;
border: 1px solid rgba(0, 0, 0, 0.2);
border-radius: 4px;
"
>重置</el-button
>
<el-button
class="confirm"
type="primary"
@click="onSubmit"
style="
width: 50px;
height: 34px;
background: #3480FB;
border-radius: 4px;
"
>确定</el-button
>
</el-form-item>
</div>
</div>
</template>
</flex-table>
</div>
四、真实业务对接
如果业务要求直接传入url就可以实现数据渲染的话,就需要在组件内部去进行数据请求,下面主要进行axios请求mock数据模拟,先测试可行性,再根据真实数据对接即可。
1,实现表格数据成功渲染
(1),步骤一:下载依赖
yarn add mockjs / npm i mockjs
yarn add axios/ npm i axios
(2),步骤二:mock数据模拟
在scr文件夹下新建mock文件夹,新建index.js文件,和mockData文件夹;
在mockData文件里新建table.json文件,写上tableData的数据
{
"totalElements":10, //总条数
"page":1, //当前页码
"rows":5, //每行显示条数
"content":[
{
"id": "1",
"contractName": "医院设备1",
"contractDate": "2023-01-30 14:44:19",
"contractAmout": "50000",
"contractNum": "1",
},
{
"id": "2",
"contractName": "医院设备2",
"contractDate": "2023-01-29 14:44:19",
"contractAmout": "30000",
"contractNum": "2",
},
{
"id": "3",
"contractName": "医院设备3",
"contractDate": "2023-01-28 15:44:19",
"contractAmout": "75000",
"contractNum": "3",
},
{
"id": "4",
"contractName": "医院设备4",
"contractDate": "2023-01-27 15:44:19",
"contractAmout": "80000",
"contractNum": "4",
},
{
"id": "5",
"contractName": "医院设备5",
"contractDate": "2023-01-26 15:44:19",
"contractAmout": "80000",
"contractNum": "5",
},
{
"id": "6",
"contractName": "医院设备6",
"contractDate": "2023-01-25 15:44:19",
"contractAmout": "80000",
"contractNum": "6",
},
{
"id": "7",
"contractName": "医院设备7",
"contractDate": "2023-01-24 15:44:19",
"contractAmout": "80000",
"contractNum": "7",
},
{
"id": "8",
"contractName": "医院设备8",
"contractDate": "2023-01-23 15:44:19",
"contractAmout": "80000",
"contractNum": "8",
},
{
"id": "9",
"contractName": "医院设备9",
"contractDate": "2023-01-22 15:44:19",
"contractAmout": "80000",
"contractNum": "9",
},
{
"id": "10",
"contractName": "医院设备10",
"contractDate": "2023-01-21 15:44:19",
"contractAmout": "80000",
"contractNum": "10",
}
]
}
]
}
另外在index.js文件中配置mock数据请求地址:
import Mock from 'mockjs';
import tableData from './mockData/tableData.json';
Mock.mock("/getData",(config)=>{
let params = Json.parse(config.body) //这里是请求的参数
return {
success:true,
data:tableData
}
})
最后一定要记得在main.js文件中引入mock文件夹下的index.js文件
import './mock/index'
(3),步骤三:封装axios请求,新建api文件夹
import axios from 'axios';
import {ElMessage} from 'element-plus';
//请求表格数据
const getTableData = (url, data)=>{
console.log('post请求参数',data);
return axios({
method:'post',
url:url,
data:data
}).then(
res => {
return res.data
},
error => {
ElMessage({
type:'error',
message:error.message,
showClose: true
})
}
)
}
export default getTableData
(4),步骤四:组件中引入方法请求数据
组件代码:
import getTableData from "../api/table";
props: {
//外部传入url,但也需要给个默认值
url: {
type: String,
require: true,
default: "/getData",
},
}
//get请求数据方法
const getTableList = (url, options) => {
//判断url不存在不继续进行请求
if (!url) {
console.log("请求地址不存在!");
return false;
}
getTableData(url, options).then((res) => {
console.log("res", res);
const { data } = res;
//处理请求数据内容渲染表格
tableData.value = data.content;
});
};
onMounted(() => {
getTableList(props.url, props.requestData);
});
使用父页面:
<flex-table
url="/getData"
:columnData="columnData"
></flex-table>
阶段总结:基本上表格的数据已经可以渲染出来了,至于请求request拦截器,需要和后端配合去完善。这里主要是前端先负责渲染出数据和调整效果样式等。
2,实现分页器和搜索框请求参数
(1)步骤一:tableData.json数据中添加分页器参数
"totalElements":10, //总条数
"page":1, //当前页码
"rows":5, //每行显示条数
(2)步骤二:组件内处理请求参数
<el-pagination
:small="small" //是否启用小型分页器,字号12px
:background="background" //页码选中是否有背景色
layout="total, prev, pager, next, sizes, jumper" //分页器各组件
:total="total" //总条数
:pager-count="4" //设置最大按钮数
:page-size="pageSize" //每页显示条数
:page-sizes="pageSizes" //每页显示个数选择器的选项设置
@size-change="sizeChange" //page-size 改变时触发
@current-change="handleCurrentChange" //页码发生变化触发事件
/>
const total = ref(10); //总条数,可以给个默认值
const pageSize = ref(10); //每页条数,可以给个默认值
const currentPage = ref(1); //当前页码,可以给个默认值
//get请求数据方法
const getTableList = (url, options) => {
//判断url不存在不继续进行请求
if (!url) {
console.log("请求地址不存在!");
return false;
}
getTableData(url, options).then((res) => {
console.log("res", res);
const { data } = res;
//处理请求数据内容渲染表格
tableData.value = data.content;
//处理请求数据总条数 ---后端给的
total.value = data.totalElements;
//处理请求每页显示条数 ---后端给的
pageSize.value = data.rows;
//处理请求当前显示页码 ---后端给的
currentPage.value = data.page;
});
};
(3)步骤三:处理页码变化时,传入相应的值给参数进行请求数据
父组件传入请求分页器参数
<flex-table
url="/getData"
:columnData="columnData"
:requestData="request_data"
></flex-table>
<script>
const request_data = reactive({
page: 1, //当前页码
rows: 10, //每页显示条数
searchVal: {}, //搜索框输入的值
});
</script>
import {
ref,
computed,
onMounted,
getCurrentInstance,
toRefs,
defineExpose,
defineEmits,
} from "vue";
props: {
//分页器请求参数
requestData: {
type: Object,
default: () => ({
page: 1, //当前页数
rows: 10, //显示条数
searchVal: {}, //搜索框的值
}),
},
}
//处理props值可以重新赋值
const emits = defineEmits();
const pageParams = computed({
get: () => {
return props.requestData;
},
set: (val) => {
emits("update:requestData", val);
},
});
//当前页码变化事件
const handleCurrentChange = (val) => {
//把当前页码赋值给请求参数
pageParams.value.page = val;
//请求数据
getTableList(props.url, pageParams.value);
};
//每页显示条数变化事件
const sizeChange = (val) => {
//把变化的每页显示条数赋值给请求参数
pageParams.value.rows = val;
//请求数据
getTableList(props.url, pageParams.value)
};
//上一页点击事件
const handlePreClick = (val) => {
console.log(`handlePreClick,点击了上一页,当前第${val}页`);
};
//下一页点击事件
const handleNextClick = (val) => {
console.log(`handleNextClick,点击了下一页,当前第${val}页`);
};
注意:这里为什么需要用外部传入的requestData?
因为外部使用的时候需要用到searchVal,也就是搜索栏输入框的值;也有可能从刚开始请求表格初始数据的时候,需要传入特定页码和特定关键词的数据,所以暴露在外面可以提高组件的灵活性。
(4)步骤四:验证是否成功传入参数
import Mock from 'mockjs';
import tableData from './mockData/tableData.json';
Mock.mock("/getData",(config)=>{
let params = Json.parse(config.body)
console.log(params) //这里打印验证是否更新参数
return {
success:true,
data:tableData
}
})
(5)步骤五:mock手动模拟切换数据的效果
目前分页器和搜索框的值就暴露到了请求数据的参数中。但是在mock中想要实现有切换数据的效果,我们需要根据返回的参数自己模拟mock返回的数据。
import tableData1 from './mockData/tableData1.json'
import tableData2 from './mockData/tableData2.json'
Mock.mock("/getData",(config)=>{
let params = JSON.parse(config.body)
//如果当前页码 = 2,返回第二页数据
if(params.page == 2){
return {
success: true,
data: tableData1,
}
}else if(params.page == 1){
//如果当前页码 = 1,返回第一页数据
return {
success: true,
data: tableData2,
};
}
})
这样简单的数据切换的效果就实现了,方便去调试传给后端的参数;
3,实现外部调用内部reload方法
因为搜索表单内容是插槽,外部自己写搜索表单的样式和布局,所以包括表单提交事件也是在组件外去操作的,那这个时候就需求去调用内部的请求去重新请求数据。vue3的话可以通过ref来实现。
(1)步骤一:父组件需要给子组件绑定ref
<flex-table ref="flexTable"></flex-table>
const flexTable = ref(null)
//搜索表单v-model的值
const form = reactive({
value1: "张三",
value2: "10",
value3: "",
value4: "",
value5: "",
value6: "",
value7: "",
});
//重置按钮
const resetForm = () => {
Object.keys(form).forEach((key) => (form[key] = ""));
flexTable.value.reload(form);
};
//提交按钮
const onSubmit = () => {
flexTable.value.reload(form);
};
//输入框enter事件
const handleFilter = (val) => {
flexTable.value.reload(val);
};
(2)步骤二:子组件处理reload方法
//reload请求方法
const reload = (val) => {
pageParams.value.searchVal = val;
getTableList(props.url, pageParams.value);
};
(3)步骤三:子组件暴露出去reload方法
defineExpose({
reload,
});
五,注意说明
1,关于代码写法
因为之前开发组件的时候需要用到name,所以用的写法是vue3的,没有用到setup语法糖。后面在完善的时候发现可以实现setup语法,也可以保留name。亲测有效。
<sctipt>
export default{
name:"组件名"
}
</script>
<sctipt setup>
//正常业务代码
</script>
2,关于组件内其他属性
这里主要讲解了一些重要的常用的属性,至于其他控制样式的属性可以参考elementPlus的组件属性表。
3,关于组件组件开发框架搭建
这个之前也是慢慢研究的,但是还没有整理,整理之后再单独更新吧。
4,关于对接真实接口
上面主要展示的是mock数据的对接模拟,至于真实数据需要对接自己的后端给的字段或者要求等;情况不一。但是大概的思路都可以参考的,大家可以根据自己的真实接口去灵活应用的。文章来源:https://www.toymoban.com/news/detail-600158.html
5,关于组件需求
不同业务或许有不同的组件封装需求,以上主要是提供一些需求的一种解决方案,大家可以灵活参考。文章来源地址https://www.toymoban.com/news/detail-600158.html
到了这里,关于elementPlus的table二次封装的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!