赞
踩
由于项目中有很多菜单都是列表数据的展示,为避免太多重复代码,故将 Element Plus 的 Table 表格进行封装,实现通过配置展示列表数据
到 Element Plus官网复制一份最简单的 Table 代码,并删除多余代码
<template> <el-table :data="tableData"> <el-table-column prop="date" label="Date" /> <el-table-column prop="name" label="Name" /> <el-table-column prop="state" label="State" /> <el-table-column prop="city" label="City" /> <el-table-column prop="address" label="Address" /> <el-table-column prop="zip" label="Zip" /> <el-table-column fixed="right" label="Operations"> <template #default> <el-button link type="primary" size="small" @click="handleClick">Detail</el-button> </template> </el-table-column> </el-table> </template> <script lang="ts" setup> const tableData = [ { date: '2016-05-03', name: 'Tom', state: 'California', city: 'Los Angeles', address: 'No. 189, Grove St, Los Angeles', zip: 'CA 90036', tag: 'Home' }, { date: '2016-05-02', name: 'Tom', state: 'California', city: 'Los Angeles', address: 'No. 189, Grove St, Los Angeles', zip: 'CA 90036', tag: 'Office' }, { date: '2016-05-04', name: 'Tom', state: 'California', city: 'Los Angeles', address: 'No. 189, Grove St, Los Angeles', zip: 'CA 90036', tag: 'Home' }, { date: '2016-05-01', name: 'Tom', state: 'California', city: 'Los Angeles', address: 'No. 189, Grove St, Los Angeles', zip: 'CA 90036', tag: 'Office' } ] const handleClick = () => { console.log('click') } </script>
tableData 数据改为从 props.api 接口获取
数据列(除多选框与操作列外)通过 props.columns 自动生成
<el-table-column
v-for="item in props.columns"
:key="item.prop"
:prop="item.prop"
:label="item.label"
:sortable="item.sortable ? 'custom' : false"
:width="item.width"
>
</el-table-column>
interface TableConfigInterface {
api: string // 表格数据获取接口
columns: {
// 显示列
prop: string // 键名
label?: string // 表头显示名称
formatter?: (row: unknown) => string // 自定义单元格格式化方法,参数为当前行数据
tooltip?: string // 表头 tooltip
sortable?: boolean // 是否可以排序
width?: number | string // 宽度
style?: string // 单元格样式
labelStyle?: string // 表头样式
}[]
}
- el-table-column 使用
<template #header>
自定义表头<slot :name="item.prop + 'Header'">
支持插槽- labelStyle 支持样式自定义
- 支持 tooltip
<el-table-column v-for="item in props.columns" :key="item.prop" :prop="item.prop" :label="item.label" :sortable="item.sortable ? 'custom' : false" :width="item.width" > <template #header> <slot :name="item.prop + 'Header'"> <div class="inline-flex" :style="item.labelStyle"> <span>{{ item.label }}</span> <el-tooltip popper-class="table-tooltip" effect="dark" placement="top-start" :content="item.tooltip" v-if="item.tooltip" > <el-icon><i-ep-Warning /></el-icon> </el-tooltip> </div> </slot> </template> </el-table-column>
- el-table-column 使用
<template #default="scope">
自定义单元格<slot :name="item.prop" :row="scope.row">
支持插槽- style 属性支持自定义样式
- formatter 方法支持自定义显示内容
<el-table-column v-for="item in props.columns" :key="item.prop" :prop="item.prop" :label="item.label" :sortable="item.sortable ? 'custom' : false" :width="item.width" > <template #header> <slot :name="item.prop + 'Header'"> <div class="inline-flex" :style="item.labelStyle"> <span>{{ item.label }}</span> <el-tooltip popper-class="table-tooltip" effect="dark" placement="top-start" :content="item.tooltip" v-if="item.tooltip" > <el-icon><i-ep-Warning /></el-icon> </el-tooltip> </div> </slot> </template> <template #default="scope"> <slot :name="item.prop" :row="scope.row"> <div :style="item.style"> <span v-if="item.formatter">{{ item.formatter(scope.row) }}</span> <span v-else>{{ scope.row[item.prop] }}</span> </div> </slot> </template> </el-table-column>
- el-table-column 使用
<template #default="scope">
自定义操作列<slot :name="item.prop" :row="scope.row">
支持插槽- visible 方法支持自定义按钮显示逻辑
<el-table-column fixed="right" label="操作" :width="props.operation?.width" v-if="props.operation?.columns" > <template #default="scope"> <slot name="operations" :row="scope.row"> <span v-for="item in props.operation?.columns" :key="item.text || item.icon"> <el-button v-if="setVisible(scope.row, item.visible)" :type="item.type" :link="item.link" :plain="item.plain" @click="item.click(scope.row)" size="small" class="margin-right: 4px" > <el-icon v-if="item.icon" :class="item.icon"></el-icon> {{ item.text }} </el-button> </span> </slot> </template> </el-table-column>
// 操作框逻辑
const setVisible = (row: unknown, visible?: (row: unknown) => boolean) => {
if (!visible || visible(row)) {
return true
}
return false
}
<el-table>
增加 selection 列
<el-table-column fixed :selectable="setSelectable" type="selection" v-if="showSelectBox" />
TableConfigInterface 增加 rowKey、selectable
interface TableConfigInterface { // ...... rowKey?: string // 行数据的 Key selectable?: boolean | ((row: unknown) => boolean) // 当前行多选框是否可以勾选,参数为当前行数据,默认为 false } const props = withDefaults(defineProps<TableConfigInterface>(), { rowKey: 'id', }) // 多选框逻辑 const disabledList = reactive<string[]>([]) // 禁止勾选的数据 const showSelectBox = computed(() => props.selectable && disabledList.length < tableData.length) const setSelectable = (row: unknown) => { const selectable = typeof props.selectable === 'boolean' ? props.selectable : props.selectable?.(row) if (!selectable && !disabledList.includes(row?.[props.rowKey])) { disabledList.push(row?.[props.rowKey]) } return selectable }
<el-table>
增加 @selection-change 及 ref 配置<slot name="footer">
支持插槽- visible 方法支持自定义按钮显示逻辑
<el-table v-loading="loading" :data="tableData" @selection-change="handleSelectionChange" table-layout="auto" ref="tableRef" > <!-- ...... --> </el-table> <div v-if="showSelectBox" class="p-14"> <el-checkbox v-model="isSelected" @click="tableRef?.toggleAllSelection()" :indeterminate="indeterminate" label="全选" style="vertical-align: middle; margin-right: 10px" /> <slot name="footer" :rows="selectionRows"> <span v-for="item in props.footer?.operations" :key="item.text || item.icon"> <el-button v-if="item.visible ? item.visible() : true" :type="item.type || 'primary'" :link="item.link" :plain="item.plain" :disabled="!selectionRows.length" @click="item.click(selectionRows)" style="margin-left: 10px" > <el-icon v-if="item.icon" :class="item.icon"></el-icon> {{ item.text }} </el-button> </span> </slot> </div>
const tableRef = ref()
const isSelected = ref(false) // 是否有选中数据
const selectionRows = ref<unknown[]>([]) // 当前选中的数据
const handleSelectionChange = (rows: unknown[]) => {
selectionRows.value = rows
isSelected.value = rows.length > 0
}
const indeterminate = computed(
() =>
selectionRows.value.length > 0 &&
selectionRows.value.length < tableData.length - disabledList.length
)
最底部增加
<el-pagination></el-pagination>
<el-pagination
background
:total="tableData.length"
:layout="props.layout"
v-model:current-page="pagination.currentPage"
v-model:page-size="pagination.pageSize"
@current-change="getTableData"
@size-change="getTableData"
class="p-y-20"
>
</el-pagination>
interface TableConfigInterface {
// ......
layout?: string // 组件布局
}
const pagination = ref({
currentPage: 1,
pageSize: 10
})
<template> <TableComponent v-bind="tableConfig"> <template #nameHeader> <div>姓名</div> </template> <template #name> <div>Yana</div> </template> </TableComponent> </template> <script setup lang="ts"> const tableConfig: TableConfigInterface = { api: 'getTableData', columns: [ { prop: 'date', label: 'Date', tooltip: 'This is Date' }, { prop: 'name', label: 'Name' }, { prop: 'state', label: 'State' }, { prop: 'city', label: 'City' }, { prop: 'address', label: 'Address' }, { prop: 'zip', label: 'Zip', style: 'color: red', labelStyle: 'color: red', sortable: true } ], operation: { columns: [ { text: '编辑', click: () => {}, visible: (row) => row.date === '2016-05-07' } ] }, rowKey: 'date', selectable: (row) => row.date === '2016-05-07', footer: { operations: [ { text: '删除', click: () => {} } ] } } </script>
<template> <div v-loading="loading" class="table-wrapper"> <el-table :data="tableData" :max-height="props.maxHeight" :default-sort="props.defaultSort" @selection-change="handleSelectionChange" @sort-change="handleSortChange" @row-click="goDetail" :row-style="{ cursor: 'pointer' }" table-layout="auto" ref="tableRef" > <el-table-column fixed :selectable="setSelectable" type="selection" v-if="showSelectBox" /> <el-table-column v-for="item in props.columns" :key="item.prop" :prop="item.prop" :label="item.label" :sortable="item.sortable ? 'custom' : false" :width="item.width" > <template #header> <slot name="header"> <div class="inline-flex" :style="item.labelStyle"> <span>{{ item.label }}</span> <el-tooltip popper-class="table-tooltip" effect="dark" placement="top-start" :content="item.tooltip" v-if="item.tooltip" > <el-icon><i-ep-Warning /></el-icon> </el-tooltip> </div> </slot> </template> <template #default="scope"> <slot :name="item.prop" :row="scope.row"> <div :style="item.style"> <span v-if="item.formatter">{{ item.formatter(scope.row) }}</span> <span v-else>{{ scope.row[item.prop] }}</span> </div> </slot> </template> </el-table-column> <el-table-column fixed="right" label="操作" :width="props.operation?.width" v-if="props.operation?.columns" > <template #default="scope"> <slot name="operations" :row="scope.row"> <span v-for="item in props.operation?.columns" :key="item.text || item.icon"> <el-button v-if="setVisible(scope.row, item.visible)" :type="item.type" :link="item.link" :plain="item.plain" @click="item.click(scope.row)" size="small" style="margin-right: 4px" > <el-icon v-if="item.icon" :class="item.icon"></el-icon> {{ item.text }} </el-button> </span> </slot> </template> </el-table-column> </el-table> <div v-if="showSelectBox" class="p-14"> <el-checkbox v-model="isSelected" @click="tableRef?.toggleAllSelection()" :indeterminate="indeterminate" label="全选" style="vertical-align: middle; margin-right: 10px" /> <slot name="footer" :rows="selectionRows"> <span v-for="item in props.footer?.operations" :key="item.text || item.icon"> <el-button v-if="item.visible ? item.visible() : true" :type="item.type || 'primary'" :link="item.link" :plain="item.plain" :disabled="!selectionRows.length" @click="item.click(selectionRows)" style="margin-left: 10px" > <el-icon v-if="item.icon" :class="item.icon"></el-icon> {{ item.text }} </el-button> </span> </slot> </div> </div> <el-pagination background :total="tableData.length" :layout="props.layout" v-model:current-page="pagination.currentPage" v-model:page-size="pagination.pageSize" @current-change="getTableData" @size-change="getTableData" class="p-y-20" /> </template> <script lang="ts" setup> interface OperationInterface { click: (row: unknown) => void // 按钮点击方法,参数为当前行数据 text?: string // 按钮显示文字 icon?: string // 按钮 icon visible?: (row?: unknown) => boolean // 设置按钮是否可见,参数为当前行数据,默认为 true type?: string // 按钮类型['primary'| 'success'| 'warning'| 'danger'| 'info'] link?: boolean // 是否为链接按钮 plain?: boolean // 是否为朴素按钮 } interface TableConfigInterface { api: string // 表格数据获取接口 rowKey?: string // 行数据的 Key columns: { // 显示列 prop: string // 键名 label?: string // 表头显示名称 formatter?: (row: unknown) => string // 自定义单元格格式化方法,参数为当前行数据 tooltip?: string // 表头 tooltip sortable?: boolean // 是否可以排序 width?: number | string // 宽度 style?: string // 单元格样式 labelStyle?: string // 表头样式 }[] selectable?: boolean | ((row: unknown) => boolean) // 当前行多选框是否可以勾选,参数为当前行数据,默认为 false operation?: { // 操作列 columns: OperationInterface[] width?: number | string // 宽度 } footer?: { // 操作列 operations: OperationInterface[] } defaultSort?: { // 默认排序 prop: string // 默认排序的列 order?: string // ['ascending'| 'descending'], 没有指定 order, 则默认顺序是 ascending } maxHeight?: number | string // 表格最大高度 layout?: string } const props = withDefaults(defineProps<TableConfigInterface>(), { rowKey: 'id', layout: 'prev, pager, next, total' }) const pagination = ref({ currentPage: 1, pageSize: 10 }) const tableRef = ref() let tableData = reactive<unknown[]>([]) // 多选框逻辑 const isSelected = ref(false) // 是否有选中数据 const selectionRows = ref<unknown[]>([]) // 当前选中的数据 const handleSelectionChange = (rows: unknown[]) => { selectionRows.value = rows isSelected.value = rows.length > 0 } const disabledList = reactive<string[]>([]) // 禁止勾选的数据 const setSelectable = (row: unknown) => { const selectable = typeof props.selectable === 'boolean' ? props.selectable : props.selectable?.(row) if (!selectable && !disabledList.includes(row?.[props.rowKey])) { disabledList.push(row?.[props.rowKey]) } return selectable } const indeterminate = computed( () => selectionRows.value.length > 0 && selectionRows.value.length < tableData.length - disabledList.length ) const showSelectBox = computed(() => props.selectable && disabledList.length < tableData.length) // 操作框逻辑 const showOperation = ref(false) const setVisible = (row: unknown, visible?: (row: unknown) => boolean) => { if (!visible || visible(row)) { showOperation.value = true return true } return false } // 排序 const handleSortChange = (data: { prop: string; order: string | null }) => { const { prop, order } = data console.log(prop, order) // getTableData } // 跳转详情页 const goDetail = (row: unknown) => { console.log(row) } // 发送接口 const loading = ref(true) const getTableData = () => { loading.value = true showOperation.value = false tableData = [ { date: '2016-05-02', name: 'Tom', state: 'California', city: 'Los Angeles', address: 'No. 189, Grove St, Los Angeles', zip: 'CA 90036' }, { date: '2016-05-03', name: 'Tom', state: 'California', city: 'Los Angeles', address: 'No. 189, Grove St, Los Angeles', zip: 'CA 90036' }, { date: '2016-05-04', name: 'Tom', state: 'California', city: 'Los Angeles', address: 'No. 189, Grove St, Los Angeles', zip: 'CA 90036' }, { date: '2016-05-05', name: 'Tom', state: 'California', city: 'Los Angeles', address: 'No. 189, Grove St, Los Angeles', zip: 'CA 90036' }, { date: '2016-05-06', name: 'Tom', state: 'California', city: 'Los Angeles', address: 'No. 189, Grove St, Los Angeles', zip: 'CA 90036' }, { date: '2016-05-07', name: 'Tom', state: 'California', city: 'Los Angeles', address: 'No. 189, Grove St, Los Angeles', zip: 'CA 90036' }, { date: '2016-05-08', name: 'Tom', state: 'California', city: 'Los Angeles', address: 'No. 189, Grove St, Los Angeles', zip: 'CA 90036' }, { date: '2016-05-09', name: 'Tom', state: 'California', city: 'Los Angeles', address: 'No. 189, Grove St, Los Angeles', zip: 'CA 90036' }, { date: '2016-05-10', name: 'Tom', state: 'California', city: 'Los Angeles', address: 'No. 189, Grove St, Los Angeles', zip: 'CA 90036' }, { date: '2016-05-11', name: 'Tom', state: 'California', city: 'Los Angeles', address: 'No. 189, Grove St, Los Angeles', zip: 'CA 90036' } ] loading.value = false } getTableData() </script> <style lang="scss" scoped> .table-wrapper { border-top: 1px solid #eaeaea; border-left: 1px solid #eaeaea; border-right: 1px solid #eaeaea; } .inline-flex { display: inline-flex; align-items: center; } .p-14 { border-bottom: 1px solid #eaeaea; padding: 14px; } .p-y-20 { padding-top: 20px; padding-bottom: 20px; justify-content: center; } </style> <style lang="scss"> .table-tooltip { max-width: 220px; } </style>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。