elementPlus的table二次封装 |
您所在的位置:网站首页 › 信息时代弊端作文 › elementPlus的table二次封装 |
一、简介
公司业务可能需要进行一些组件的封装,基于第三方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)子组件 最终代码示例: 实现思路: 1)table的属性和column的属性要分开;原因:我们来分析一下上面的代码,首先我们需要用组件封装的思想思考。el-table标签上的属性都可以通过父子传值的方式传进组件。但是el-table-column到底有几个我们不确定,那我们很自然的就会想到组件内部需要用到v-for循环,循环的数据也是通过外部传进来。 所以子组件内部发展为: import {defineProps} from 'vue' defineProps({ tableData:{ type:Array }, columnData:{ type:Array } })父页面传入的数据发展为: 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 {row.contractAmout}万; }, }, { 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 ( { handleEdit(row,column,cellValue,index); }} > 编辑 { handleDelete(row,column,cellValue,index); }} > 删除 ); }, }, ]); //表格数据的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列和序号列由外部决定是否需要;也就是外部传入个布尔值去控制是否渲染这两列即可,所以子组件再前面的基础上再加上: import {defineProps} from 'vue' defineProps({ tableData:{ type:Array }, columnData:{ type:Array }, selection: Boolean, index: Boolean, })如果需要渲染这两列,父页面使用的时候直接传入相应的布尔值即可,如下示例: 3)插槽支持更多框架内容自定义;由于formatter的方法内部是react写法,不支持template标签。如果自定义的内容包含el-tag标签或其他可扩展内容时,可以通过插槽实现; 子组件内部就变成了: 父页面使用的时候就可以通过插槽的名称来实现内容的自定义: tag标签插槽: {{ row.state }} {{ row.state }} {{ row.state }} {{ row.state }} {{ row.state }}link扩展链接插槽: 催填 汇总 重新汇总 重新确认 调整 4)配置搜索表单在el-table标签上方加上如上代码,使用插槽支持自定义; 在父页面使用的时候: 选项一 选项二 重置 确定 import {ref, reactive} from 'vue' const isShow = ref(false); const form = reactive({ value1: "张三", value2: "10", value3: "", value4: "", value5: "", value6: "", value7: "", });展开效果图: 隐藏效果图: 5)配置分页器效果图: 直接运用el-pagination,html代码示例: 已选择{{ checkNumber }}项 | 取消参考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)父组件 最终代码示例: 催填 汇总 重新汇总 重新确认 调整 | 二级配置 更多菜单一 更多菜单二 更多菜单三 更多菜单四 上会结束 更多菜单一 更多菜单二 更多菜单三 更多菜单四 二级配置 更多菜单一 更多菜单二 更多菜单三 更多菜单四 上会结束 更多菜单一 更多菜单二 更多菜单三 更多菜单四 二级配置 更多菜单一 更多菜单二 更多菜单三 更多菜单四 {{ row.state }} {{ row.state }} {{ row.state }} {{ row.state }} {{ row.state }} 选项一 选项二 重置 确定 四、真实业务对接如果业务要求直接传入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); });使用父页面: 阶段总结:基本上表格的数据已经可以渲染出来了,至于请求request拦截器,需要和后端配合去完善。这里主要是前端先负责渲染出数据和调整效果样式等。 2,实现分页器和搜索框请求参数 (1)步骤一:tableData.json数据中添加分页器参数 "totalElements":10, //总条数 "page":1, //当前页码 "rows":5, //每行显示条数 (2)步骤二:组件内处理请求参数 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)步骤三:处理页码变化时,传入相应的值给参数进行请求数据父组件传入请求分页器参数 const request_data = reactive({ page: 1, //当前页码 rows: 10, //每页显示条数 searchVal: {}, //搜索框输入的值 }); 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 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。亲测有效。 export default{ name:"组件名" } //正常业务代码 2,关于组件内其他属性这里主要讲解了一些重要的常用的属性,至于其他控制样式的属性可以参考elementPlus的组件属性表。 3,关于组件组件开发框架搭建这个之前也是慢慢研究的,但是还没有整理,整理之后再单独更新吧。 4,关于对接真实接口上面主要展示的是mock数据的对接模拟,至于真实数据需要对接自己的后端给的字段或者要求等;情况不一。但是大概的思路都可以参考的,大家可以根据自己的真实接口去灵活应用的。 5,关于组件需求不同业务或许有不同的组件封装需求,以上主要是提供一些需求的一种解决方案,大家可以灵活参考。 |
今日新闻 |
点击排行 |
|
推荐新闻 |
图片新闻 |
|
专题文章 |
CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭 |