vue3二次封裝element-ui中的table組件的過(guò)程詳解
為什么要做這件事
借助封裝table組件的過(guò)程來(lái)鞏固一下vue3相關(guān)知識(shí)點(diǎn)。
組件有哪些配置項(xiàng)
- options:表格的配置項(xiàng)
- data: 表格數(shù)據(jù)源
- elementLoadingText:加載文案
- elementLoadingSpinner:加載圖標(biāo)
- elementLoadingBackground:背景遮罩的顏色
- elementLoadingSvg:加載圖標(biāo)(svg)
- elementLoadingSvgViewBox:加載圖標(biāo)是svg的話,配置項(xiàng)
- editIcon:編輯圖標(biāo)
- isEditRow:是否支持編輯
- editRowIndex:編輯行的標(biāo)識(shí)符
- pagination:是否支持分頁(yè)
- paginationAlign:分頁(yè)對(duì)齊方式
- currentPage:當(dāng)前頁(yè)數(shù)
- pageSize:每頁(yè)顯示條目個(gè)數(shù)
- pageSizes:每頁(yè)顯示個(gè)數(shù)選擇器的選項(xiàng)設(shè)置
- total:總條目數(shù)
實(shí)現(xiàn)過(guò)程
首先,將一個(gè)普通的element-plus中的table組件引入進(jìn)來(lái),表格數(shù)據(jù)源就是我們通過(guò)父組件傳遞進(jìn)來(lái)的data,所以我們使用defineProps來(lái)定義,并且它的數(shù)據(jù)類型是一個(gè)數(shù)組;同時(shí)我們遵循單向數(shù)據(jù)流的原則,使用lodash中的深拷貝方法將data拷貝一份出來(lái)賦值給變量tableData,將tableData傳遞給element-plus中的table組件,用來(lái)渲染數(shù)據(jù)。
// 子組件 m-table-copy <template> <el-table :data="tableData"> <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> <script setup> import { ref } from 'vue' import cloneDeep from 'lodash/cloneDeep' let props = defineProps({ data: { type: Array, required: true } }) // 拷貝一份兒數(shù)據(jù) let tableData = ref(cloneDeep(props.data)) </script>
父組件在使用這個(gè)自定義組件的時(shí)候應(yīng)該這么使用:
<!-- 父組件 --> <template> <m-table-copy :data="tableData"></m-table-copy> </template> <script setup> import { ref } from 'vue' let tableData = ref([]) tableData.value = [ { name: '張三', address: '杭州市', date: '1998-07-16' }, { name: '李四', address: '石家莊市', date: '2013-09-02' } ] </script>
這樣頁(yè)面上就能夠顯示出來(lái)我們的數(shù)據(jù)了:
第一步完成了,我們接著再分析,還有什么是可以封裝的呢?仔細(xì)看上面的代碼,是不是有了想法?是的,label、width、prop這些也是可以放在一個(gè)配置項(xiàng)里面的,那我們繼續(xù)來(lái)進(jìn)行封裝:
// 父組件準(zhǔn)備好的數(shù)據(jù)結(jié)構(gòu) let options = [ { prop: 'date', label: '日期', align: 'center', slot: 'date', editable: true, width: '230' }, { prop: 'name', label: '姓名', align: 'center', slot: 'name' }, { prop: 'address', label: '地址', align: 'center', editable: true }, { label: '操作', align: 'center', action: true } ]
<template> <el-table :data="tableData"> <template v-for="(item, index) in tableOption" :key="index"> <el-table-column :label="item.label" :prop="item.prop" :align="item.align" :width="item.width" /> </template> </el-table> </template> <script setup> import { ref, computed } from 'vue' let props = defineProps({ // 表格配置項(xiàng) options: { type: Array, required: true } }) // 過(guò)濾操作項(xiàng)之后的配置 let tableOption = computed(() => props.options.filter((item) => !item.action)) </script>
一般來(lái)說(shuō),表格都會(huì)配置一下loading狀態(tài),所以我們繼續(xù)封裝,將loading相關(guān)的配置項(xiàng)也添加進(jìn)來(lái):
完整代碼
<template> <el-table :data="tableData" v-loading="isLoading" :element-loading-text="elementLoadingText" :element-loading-spinner="elementLoadingSpinner" :element-loading-svg="elementLoadingSvg" :element-loading-svg-view-box="elementLoadingSvgViewBox" :element-loading-background="elementLoadingBackground" @row-click="rowClick" v-bind="$attrs"> <template v-for="(item, index) in tableOption" :key="index"> <el-table-column :label="item.label" :prop="item.prop" :align="item.align" :width="item.width"> <template #default="scope"> <!-- 編輯模式 --> <template v-if="scope.row.rowEdit"> <el-input v-model="scope.row[item.prop]"></el-input> </template> <template v-else> <template v-if="scope.$index + scope.column.id === currentEdit"> <div style="display: flex"> <el-input v-model="scope.row[item.prop]"></el-input> <div> <slot v-if="$slots.cellEdit" name="cellEdit" :scope="scope"></slot> <div class="action-icon" v-else> <el-icon-check class="check" @click.stop="check(scope)"></el-icon-check> <el-icon-close class="close" @click.stop="close(scope)"></el-icon-close> </div> </div> </div> </template> <template v-else> <!-- slot是一個(gè)插槽出口,表示了父元素提供的插槽內(nèi)容將在哪里被渲染 --> <slot v-if="item.slot" :name="item.slot" :scope="scope"></slot> <span v-else>{{ scope.row[item.prop] }}</span> <component v-if="item.editable" :is="`el-icon-${toLine(editIcon)}`" class="edit" @click.stop="clickEditIcon(scope)"></component> </template> </template> </template> </el-table-column> </template> <el-table-column :align="actionOption.align" :label="actionOption.label" :width="actionOption.width"> <template #default="scope"> <!-- 編輯模式下顯示確認(rèn)和取消 --> <slot name="editRow" :scope="scope" v-if="scope.row.rowEdit"></slot> <!-- 正常狀態(tài)下顯示 編輯和刪除 --> <slot name="action" :scope="scope" v-else></slot> </template> </el-table-column> </el-table> <div v-if="pagination && !isLoading" class="pagination" :style="{ justifyContent }"> <el-pagination v-model:currentPage="currentPage" :page-size="pageSize" :page-sizes="pageSizes" @size-change="handleSizeChange" @current-change="handleCurrentChange" layout="total, sizes, prev, pager, next, jumper" :total="total" /> </div> </template>
<script setup> import cloneDeep from 'lodash/cloneDeep' import { computed, onMounted, ref, watch } from 'vue' import { toLine } from '../../../utils' let props = defineProps({ // 表格配置項(xiàng) options: { type: Array, required: true }, // 表格數(shù)據(jù) data: { type: Array, required: true }, // 編輯圖標(biāo) editIcon: { type: String, default: 'Edit' }, // 顯示在加載圖標(biāo)下方的加載文案 elementLoadingText: { type: String }, // 自定義加載圖標(biāo) elementLoadingSpinner: { type: String }, // 自定義加載圖標(biāo)(svg) elementLoadingSvg: { type: String }, // 自定義加載圖標(biāo)(svg)的配置 elementLoadingSvgViewBox: { type: String }, // 背景遮罩的顏色 elementLoadingBackground: { type: String }, // 是否可用編輯行 isEditRow: { type: Boolean, default: false }, // 編輯行按鈕的標(biāo)識(shí) editRowIndex: { type: String, default: '' }, // 是否顯示分頁(yè) pagination: { type: Boolean, default: false }, // 分頁(yè)的對(duì)齊方式 paginationAlign: { type: String, default: 'left' }, // 當(dāng)前第幾頁(yè) currentPage: { type: Number, default: 1 }, // 顯示分頁(yè)數(shù)據(jù)多少條的選項(xiàng) pageSizes: { type: Array, default: () => [10, 20, 30, 40] }, // 數(shù)據(jù)總條數(shù) total: { type: Number, default: 0 } }) // 深拷貝一份表格的數(shù)據(jù) let tableData = ref(cloneDeep(props.data)) let cloneEditRowIndex = ref(props.editRowIndex) // 過(guò)濾操作項(xiàng)之后的配置 let tableOption = computed(() => props.options.filter((item) => !item.action)) let actionOption = computed(() => props.options.find((item) => item.action)) // 監(jiān)聽(tīng)的標(biāo)識(shí) let watchData = ref<boolean>(false) // 如果data的數(shù)據(jù)變了 要重新給tableData賦值 // 只需要監(jiān)聽(tīng)一次就可以了 let stopWatchData = watch( () => props.data, (val) => { watchData.value = true tableData.value = val tableData.value.map((item) => { item.rowEdit = false }) if (watchData.value) stopWatchData() }, { deep: true } ) watch( () => props.editRowIndex, (val) => { if (val) cloneEditRowIndex.value = val } ) // 當(dāng)前被點(diǎn)擊的單元格的標(biāo)志 let currentEdit = ref('') let currentPage = computed(() => props.currentPage) let justifyContent = computed(() => { if (props.paginationAlign === 'left') return 'flex-start' else if (props.paginationAlign === 'right') return 'flex-end' else return 'center' }) let isLoading = computed(() => !props.data || !props.data.length) let emits = defineEmits([ 'confirm', 'cancel', 'update:editRowIndex', 'size-change', 'current-change' ]) onMounted(() => { tableData.value.map((item) => { item.rowEdit = false }) }) let clickEditIcon = (scope) => { currentEdit.value = scope.$index + scope.column.id } let handleSizeChange = (val) => { emits('size-change', val) } let handleCurrentChange = (val) => { emits('current-change', val) } let check = (scope) => { emits('confirm', scope) currentEdit.value = '' } let close = (scope) => { emits('cancel', scope) currentEdit.value = '' } let rowClick = (row, column) => { if (column.label === actionOption.value.label) { if (props.isEditRow && cloneEditRowIndex.value === props.editRowIndex) { row.rowEdit = !row.rowEdit tableData.value.map((item) => { if (item !== row) item.rowEdit = false }) if (!row.rowEdit) emits('update:editRowIndex', '') } } } </script>
以上就是vue3二次封裝element-ui中的table組件的過(guò)程詳解的詳細(xì)內(nèi)容,更多關(guān)于vue3二次封裝table的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue實(shí)現(xiàn)nav導(dǎo)航欄的方法
這篇文章主要為大家詳細(xì)介紹了vue項(xiàng)目nav導(dǎo)航欄的實(shí)現(xiàn)方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12Vue實(shí)現(xiàn)導(dǎo)航吸頂效果的教程詳解
在瀏覽器上下滾動(dòng)的時(shí)候,如何距離頂部的距離大于78px,吸頂顯示,小于78px則隱藏,所以本文小編給大家介紹了Vue設(shè)置導(dǎo)航吸頂?shù)脑敿?xì)教程,文中有相關(guān)的代碼示例供大家參考,具有一定的參考價(jià)值,需要的朋友可以參考下2024-01-01vue 自動(dòng)化路由實(shí)現(xiàn)代碼
這篇文章主要介紹了vue 自動(dòng)化路由實(shí)現(xiàn)代碼,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-09-09從Echarts報(bào)錯(cuò)中學(xué)習(xí)Vue3?ref和shallowRef區(qū)別及其組件二次封裝demo
這篇文章主要介紹了從Echarts報(bào)錯(cuò)中學(xué)習(xí)Vue3?ref和shallowRef區(qū)別及其組件二次封裝demo,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11Electron?store及shareObject進(jìn)程間數(shù)據(jù)交互存儲(chǔ)功能封裝
這篇文章主要為大家介紹了Electron?store及shareObject進(jìn)程間數(shù)據(jù)交互存儲(chǔ)功能封裝示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09