效果如下:
elemetui自定义表格实现可拖拽列、显示隐藏列、数据汇总
1.可拖拽列
使用前需要先安装一个包
npm i -S vuedraggable
使用的时候建议需要设置两个对象:startCol和showCol,这两个对象是保存每一列的label和prop的,虽然这两个是一样的,但是必须要设置两个,否则就会导致你拖拽替换只会替换每一列对应的值不会替换label。对象形式如下:
//控制显示列显示隐藏
let startCol = ref([
{
label: '零件名称',
prop: 'partName',
status:true
},
{
label: '批次号',
prop: 'batchNo',
status:true
},
{
label: '发票号',
prop: 'invoiceNo',
status:true
},
{
label: '订单号',
prop: 'orderNo',
status:true
},
{
label: '箱号',
prop: 'boxNo',
status:true
},
{
label: '小箱号',
prop: 'caseNo',
status:true
}
])
<el-table :data="roleList" show-summary row-key="id"
:summary-method="getSummaries">
<template v-for="(col,index) in showCol" :key="index" >
<el-table-column
:label="col.label" v-if="showCol[index].status" :width="flexColumnWidth(showCol[1].prop, roleList)" :prop="showCol[index].prop" align="center" />
</template>
</el-table>
然后下面是列拖拽的方法
//列拖拽
function columnDrop() {
// const wrapperTr = document.querySelector('.el-table__header-wrapper tr')
const wrapperTr = document.querySelector('.el-popper .dragCol')
let sortableInstance = Sortable.create(wrapperTr, {
animation: 180,
delay: 0,
onEnd: evt => {
console.log(evt);
let oldItem =showCol.value[evt.oldIndex-1]
showCol.value.splice(evt.oldIndex-1,1)
showCol.value.splice(evt.newIndex-1,0,oldItem)
}
})
}
//需要在onMouted函数中调用一次
onMounted(() => {
columnDrop()
})
2.自定义列宽
给每一列添加这个代码
:width="flexColumnWidth(showCol[1].prop, roleList)"
如果只是想在某一列添加可以这样,宽度自定义
:width="index==0? flexColumnWidth(showCol.prop, roleList):'300'"
方法如下:
注意如果那一列的数据存在某一个是空值那么那一项就会不进入自适应宽度,把下面的注释掉就可以了;
// 自适应表格列宽
const flexColumnWidth = (str, tableData, flag = 'max') => {
// str为该列的字段名(传字符串);tableData为该表格的数据源(传变量);
// flag为可选值,可不传该参数,传参时可选'max'或'equal',默认为'max'
// flag为'max'则设置列宽适配该列中最长的内容,flag为'equal'则设置列宽适配该列中第一行内容的长度。
str = str + ''
let columnContent = ''
if (!tableData || !tableData.length || tableData.length === 0 || tableData === undefined) {
return
}
if (!str || !str.length || str.length === 0 || str === undefined) {
return
}
if (flag === 'equal') {
// 获取该列中第一个不为空的数据(内容)
for (let i = 0; i < tableData.length; i++) {
if (tableData[i][str].length > 0) {
// console.log('该列数据[0]:', tableData[0][str])
columnContent = tableData[i][str]
break
}
}
} else {
// 获取该列中最长的数据(内容)
let index = 0
for (let i = 0; i < tableData.length; i++) {
// 数据为空跳出循环
// if (tableData[i][str] === null) {
// return
// }
const now_temp = tableData[i][str] + ''
const max_temp = tableData[index][str] + ''
if (now_temp.length > max_temp.length) {
index = i
}
}
columnContent = tableData[index][str]
}
// console.log('该列数据[i]:', columnContent)
// 以下分配的单位长度可根据实际需求进行调整
let flexWidth = 0
columnContent = columnContent + ''
for (const char of columnContent) {
if ((char >= 'A' && char <= 'Z') || (char >= 'a' && char <= 'z')) {
// 如果是英文字符,为字符分配8个单位宽度
flexWidth += 12
} else if (char >= '\u4e00' && char <= '\u9fa5') {
// 如果是中文字符,为字符分配15个单位宽度
flexWidth += 15
} else {
// 其他种类字符,为字符分配15个单位宽度
flexWidth += 12
}
}
if (flexWidth < 80) {
// 设置最小宽度
flexWidth = 80
}
// if (flexWidth > 250) {
// // 设置最大宽度
// flexWidth = 250
// }
return flexWidth + 'px'
}
3.显示隐藏列
通过v-if命令动态修改你每一列是否显示
设置一个对象,包含你每一列的项
const startCol= ref([
{
label: '零件名称',
prop: 'partName',
status:true
},
{
label: '批次号',
prop: 'batchNo',
status:true
},
{
label: '发票号',
prop: 'invoiceNo',
status:true
},
{
label: '订单号',
prop: 'orderNo',
status:true
},
{
label: '箱号',
prop: 'boxNo',
status:true
},
{
label: '小箱号',
prop: 'caseNo',
status:true
}
])
控制列的面板:el-popover是一个弹框,然后里面自定义多选框内容
<el-popover placement="bottom-start" :width="600" trigger="hover">
<transition name="fade">
<div>
<div>选择需要显示的列</div>
<div class="dragCol">
<el-checkbox v-model="checkAll" @change="handleCheckAllChange">全选</el-checkbox>
<el-checkbox v-for="(item, index) in startCol" :key="index" @change="handleCheckedBoxChange(item)" v-model="item.status">{{item.label}}</el-checkbox>
</div>
</div>
</transition>
<template #reference>
<el-button icon="Setting">显示列</el-button>
</template>
</el-popover>
全选和复选的方法
function handleCheckAllChange() {
startCol.value.forEach(item => {
item.status = checkAll.value;
});
showCol.value.forEach(i => {
i.status=checkAll.value;
});
}
function handleCheckedBoxChange(item) {
const allTrue = startCol.value.every(item => item.status === true);
// 修改显示列表的状态
showCol.value.forEach(i => {
if(i.label==item.label){
i.status=item.status
}
});
if (allTrue) {
checkAll.value = true
} else {
checkAll.value = false
}
}
4.自定义汇总
表格需要添加show-summary :summary-method="getSummaries"
<el-table :data="roleList" show-summary :summary-method="getSummaries">
方法如下:
// 自定义汇总方法
const getSummaries = (param) => {
const { columns, data } = param
const sums = []
columns.forEach((column, index) => {
if (index === 0) {
sums[index] = '总数'
return
}
console.log(column.property);
// 将所有元素的值转换成数字类型
const values = data.map((item) => Number(item[column.property]))
console.log(values.some((val) => Number.isNaN(val)));
// every 只要有一个是数字,就会返回true
// 判断数组中的数据是否是数字类型 some 只要存在一个是NaN那么就返回false
if (!values.some((value) => Number.isNaN(value))) {
sums[index] = ` ${values.reduce((prev, curr) => {
const value = Number(curr)
if (!Number.isNaN(value)) {
return prev + curr
} else {
return prev
}
}, 0)}`
} else {
sums[index] = '/'
}
})
return sums
}
如果想要把合计行放到顶部去,可以使用css样式修改。
/* order默认值为0,只需将表体order置为1即可移到最后,这样合计行就上移到表体上方 */
::v-deep .el-table__body-wrapper {
order: 1;
}
::v-deep .el-table__fixed-body-wrapper {
top: 96px !important;
}
::v-deep .el-table__fixed-footer-wrapper {
z-index: 0;
}
5.所有功能汇总表格
template部分
<template>
<div>
<el-popover placement="bottom-start" :width="600" trigger="hover">
<transition name="fade">
<div>
<div>选择需要显示的列</div>
<div class="dragCol">
<el-checkbox v-model="checkAll" @change="handleCheckAllChange">全选</el-checkbox>
<el-checkbox v-for="(item, index) in startCol" :key="index" @change="handleCheckedBoxChange(item)" v-model="item.status">{{item.label}}</el-checkbox>
</div>
</div>
</transition>
<template #reference>
<el-button icon="Setting">显示列</el-button>
</template>
</el-popover>
<el-table :data="roleList" show-summary row-key="id"
:summary-method="getSummaries">
<template v-for="(col,index) in showCol" :key="index" >
<el-table-column
:label="col.label" v-if="showCol[index].status" :width="flexColumnWidth(showCol[1].prop, roleList)" :prop="showCol[index].prop" align="center" />
</template>
</el-table>
</div>
</template>
script部分
<script setup>
import { ref, onMounted } from 'vue' // 引入ref和onMounted
import Sortable from 'sortablejs'
const roleList = ref([
{partName: 1,
batchNo:2,
invoiceNo: 3,
orderNo:4,
boxNo: 5,
caseNo: 6,}
]);
//控制显示列显示隐藏
let startCol = ref([
{
label: '零件名称',
prop: 'partName',
status:true
},
{
label: '批次号',
prop: 'batchNo',
status:true
},
{
label: '发票号',
prop: 'invoiceNo',
status:true
},
{
label: '订单号',
prop: 'orderNo',
status:true
},
{
label: '箱号',
prop: 'boxNo',
status:true
},
{
label: '小箱号',
prop: 'caseNo',
status:true
}
])
// 控制列显示
let showCol = ref(JSON.parse(JSON.stringify(startCol.value)));
let checkAll = ref(true)
function handleCheckAllChange() {
startCol.value.forEach(item => {
item.status = checkAll.value;
});
showCol.value.forEach(i => {
i.status=checkAll.value;
});
}
function handleCheckedBoxChange(item) {
const allTrue = startCol.value.every(item => item.status === true);
// 修改显示列表的状态
showCol.value.forEach(i => {
if(i.label==item.label){
i.status=item.status
}
});
if (allTrue) {
checkAll.value = true
} else {
checkAll.value = false
}
}
function columnDrop() {
// const wrapperTr = document.querySelector('.el-table__header-wrapper tr')
const wrapperTr = document.querySelector('.el-popper .dragCol')
let sortableInstance = Sortable.create(wrapperTr, {
animation: 180,
delay: 0,
onEnd: evt => {
console.log(evt);
let oldItem =showCol.value[evt.oldIndex-1]
showCol.value.splice(evt.oldIndex-1,1)
showCol.value.splice(evt.newIndex-1,0,oldItem)
}
})
}
// 自适应表格列宽
const flexColumnWidth = (str, tableData, flag = 'max') => {
// str为该列的字段名(传字符串);tableData为该表格的数据源(传变量);
// flag为可选值,可不传该参数,传参时可选'max'或'equal',默认为'max'
// flag为'max'则设置列宽适配该列中最长的内容,flag为'equal'则设置列宽适配该列中第一行内容的长度。
str = str + ''
let columnContent = ''
if (!tableData || !tableData.length || tableData.length === 0 || tableData === undefined) {
return
}
if (!str || !str.length || str.length === 0 || str === undefined) {
return
}
if (flag === 'equal') {
// 获取该列中第一个不为空的数据(内容)
for (let i = 0; i < tableData.length; i++) {
if (tableData[i][str].length > 0) {
// console.log('该列数据[0]:', tableData[0][str])
columnContent = tableData[i][str]
break
}
}
} else {
// 获取该列中最长的数据(内容)
let index = 0
for (let i = 0; i < tableData.length; i++) {
// 数据为空跳出循环
// if (tableData[i][str] === null) {
// return
// }
const now_temp = tableData[i][str] + ''
const max_temp = tableData[index][str] + ''
if (now_temp.length > max_temp.length) {
index = i
}
}
columnContent = tableData[index][str]
}
// console.log('该列数据[i]:', columnContent)
// 以下分配的单位长度可根据实际需求进行调整
let flexWidth = 0
columnContent = columnContent + ''
for (const char of columnContent) {
if ((char >= 'A' && char <= 'Z') || (char >= 'a' && char <= 'z')) {
// 如果是英文字符,为字符分配8个单位宽度
flexWidth += 12
} else if (char >= '\u4e00' && char <= '\u9fa5') {
// 如果是中文字符,为字符分配15个单位宽度
flexWidth += 15
} else {
// 其他种类字符,为字符分配15个单位宽度
flexWidth += 12
}
}
if (flexWidth < 80) {
// 设置最小宽度
flexWidth = 80
}
// if (flexWidth > 250) {
// // 设置最大宽度
// flexWidth = 250
// }
return flexWidth + 'px'
}
// 自定义汇总方法
const getSummaries = (param) => {
const { columns, data } = param
const sums = []
columns.forEach((column, index) => {
if (index === 0) {
sums[index] = '总数'
return
}
// console.log(column.property);
// 将所有元素的值转换成数字类型
const values = data.map((item) => Number(item[column.property]))
// console.log(values.some((val) => Number.isNaN(val)));
// every 只要有一个是数字,就会返回true
// 判断数组中的数据是否是数字类型 some 只要存在一个是NaN那么就返回false
if (!values.some((value) => Number.isNaN(value))) {
sums[index] = ` ${values.reduce((prev, curr) => {
const value = Number(curr)
if (!Number.isNaN(value)) {
return prev + curr
} else {
return prev
}
}, 0)}`
} else {
sums[index] = '/'
}
})
return sums
}
onMounted(() => {
columnDrop()
})
</script>
6.组件封装
封装的代码在这篇文章