vue3二次封裝element-ui中的table組件的過程詳解
為什么要做這件事
借助封裝table組件的過程來鞏固一下vue3相關(guān)知識點。
組件有哪些配置項
- options:表格的配置項
- data: 表格數(shù)據(jù)源
- elementLoadingText:加載文案
- elementLoadingSpinner:加載圖標(biāo)
- elementLoadingBackground:背景遮罩的顏色
- elementLoadingSvg:加載圖標(biāo)(svg)
- elementLoadingSvgViewBox:加載圖標(biāo)是svg的話,配置項
- editIcon:編輯圖標(biāo)
- isEditRow:是否支持編輯
- editRowIndex:編輯行的標(biāo)識符
- pagination:是否支持分頁
- paginationAlign:分頁對齊方式
- currentPage:當(dāng)前頁數(shù)
- pageSize:每頁顯示條目個數(shù)
- pageSizes:每頁顯示個數(shù)選擇器的選項設(shè)置
- total:總條目數(shù)
實現(xiàn)過程
首先,將一個普通的element-plus中的table組件引入進(jìn)來,表格數(shù)據(jù)源就是我們通過父組件傳遞進(jìn)來的data,所以我們使用defineProps來定義,并且它的數(shù)據(jù)類型是一個數(shù)組;同時我們遵循單向數(shù)據(jù)流的原則,使用lodash中的深拷貝方法將data拷貝一份出來賦值給變量tableData,將tableData傳遞給element-plus中的table組件,用來渲染數(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>父組件在使用這個自定義組件的時候應(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>這樣頁面上就能夠顯示出來我們的數(shù)據(jù)了:
第一步完成了,我們接著再分析,還有什么是可以封裝的呢?仔細(xì)看上面的代碼,是不是有了想法?是的,label、width、prop這些也是可以放在一個配置項里面的,那我們繼續(xù)來進(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({
// 表格配置項
options: {
type: Array,
required: true
}
})
// 過濾操作項之后的配置
let tableOption = computed(() => props.options.filter((item) => !item.action))
</script>一般來說,表格都會配置一下loading狀態(tài),所以我們繼續(xù)封裝,將loading相關(guān)的配置項也添加進(jìn)來:
完整代碼
<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是一個插槽出口,表示了父元素提供的插槽內(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({
// 表格配置項
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)識
editRowIndex: {
type: String,
default: ''
},
// 是否顯示分頁
pagination: {
type: Boolean,
default: false
},
// 分頁的對齊方式
paginationAlign: {
type: String,
default: 'left'
},
// 當(dāng)前第幾頁
currentPage: {
type: Number,
default: 1
},
// 顯示分頁數(shù)據(jù)多少條的選項
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)
// 過濾操作項之后的配置
let tableOption = computed(() => props.options.filter((item) => !item.action))
let actionOption = computed(() => props.options.find((item) => item.action))
// 監(jiān)聽的標(biāo)識
let watchData = ref<boolean>(false)
// 如果data的數(shù)據(jù)變了 要重新給tableData賦值
// 只需要監(jiān)聽一次就可以了
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)前被點擊的單元格的標(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組件的過程詳解的詳細(xì)內(nèi)容,更多關(guān)于vue3二次封裝table的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
從Echarts報錯中學(xué)習(xí)Vue3?ref和shallowRef區(qū)別及其組件二次封裝demo
這篇文章主要介紹了從Echarts報錯中學(xué)習(xí)Vue3?ref和shallowRef區(qū)別及其組件二次封裝demo,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11
Electron?store及shareObject進(jìn)程間數(shù)據(jù)交互存儲功能封裝
這篇文章主要為大家介紹了Electron?store及shareObject進(jìn)程間數(shù)據(jù)交互存儲功能封裝示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09

