You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

189 lines
4.7 KiB

<template>
<div class="el_TablePage">
<span>表格导出名称</span>
<el-input
v-model="exportFileName"
placeholder="请输入导出的表格名称"
style="margin-bottom: 10px"
/>
<el-transfer
v-model="checkedColumns"
:data="transferData"
:titles="['可选字段', '已选字段']"
:filterable="true"
:props="transferProps"
/>
<div class="el_TablePage_btn">
<el-button type="primary" @click="exportToExcel">
<el-icon><Download /></el-icon>
</el-button>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue';
import { ElMessage } from 'element-plus';
import * as XLSX from 'xlsx';
import { Download } from '@element-plus/icons-vue';
// 传入的参数
const props = defineProps({
data: {
type: Array,
required: true,
},
menuData: {
type: Array,
required: true,
},
});
// 将 menuData 复制到一个局部变量中,以便在 setup 中操作
const menuData = ref([...props.menuData]);
// 移除指定列的函数
const removeColumn = (columnLabel) => {
const index = menuData.value.findIndex(item => item.label === columnLabel);
if (index !== -1) {
menuData.value.splice(index, 1);
}
};
// 移除 '序号' 列
removeColumn('序号');
removeColumn('操作');
removeColumn('复选框');
const exportFileName = ref('');
// 已选择的列
const checkedColumns = ref([]);
const initializeCheckedColumns = () => {
checkedColumns.value = transferData.value.map(item => item.key);
};
// 构建 el-transfer 所需的数据格式,并添加类型检查
const transferData = computed(() => {
if (Array.isArray(menuData.value)) {
return menuData.value.map(item => ({
...item,
key: item.prop,
}));
} else {
console.error('menuData 不是一个数组:', menuData.value);
return [];
}
});
initializeCheckedColumns();
// 设置 el-transfer 的 props
const transferProps = {
key: 'key',
label: 'label',
};
// 导出到 Excel 的函数
const exportToExcel = () => {
if (checkedColumns.value.length === 0) {
ElMessage.warning('请选择要导出的字段');
return;
}
if (!exportFileName.value) {
ElMessage({
message: '请输入导出名称',
type: 'warning',
});
return;
}
const selectedColumns = transferData.value.filter(item =>
checkedColumns.value.includes(item.key)
);
const headers = selectedColumns.map(col => col.label);
const fields = selectedColumns.map(col => col.prop);
// 准备导出数据
let exportData = props.data.map(item => {
return fields.map(field => {
const value = item[field];
return value !== undefined && value !== null ? value : '';
});
});
// 填充空白单元格
exportData = fillEmptyCells(exportData);
// 添加表头
exportData.unshift(headers);
// 创建工作表
const worksheet = XLSX.utils.aoa_to_sheet(exportData);
// 设置列宽和样式
applyStyles(worksheet, exportData);
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, exportFileName.value);
// 生成文件并下载
XLSX.writeFile(workbook, `${exportFileName.value}.xlsx`);
};
// 填充空白单元格的函数
const fillEmptyCells = (data) => {
return data;
};
// 设置样式的函数
const applyStyles = (worksheet, data) => {
const rowCount = data.length;
const colCount = data[0].length;
// 计算每列的最大宽度
const colWidths = new Array(colCount).fill(0);
data.forEach(row => {
row.forEach((cell, index) => {
const cellWidth = cell ? cell.toString().length : 0;
if (cellWidth > colWidths[index]) {
colWidths[index] = cellWidth;
}
});
});
// 设置列宽
const wscols = colWidths.map(width => ({ wch: Math.min(width + 2, 50) })); // 加2为了留些边距,最大宽度限制为50
worksheet['!cols'] = wscols;
// 设置所有单元格的默认样式
const range = XLSX.utils.decode_range(worksheet['!ref']);
for (let col = range.s.c; col <= range.e.c; col++) {
for (let row = range.s.r; row <= range.e.r; row++) {
const cellRef = XLSX.utils.encode_cell({ c: col, r: row });
if (!worksheet[cellRef]) worksheet[cellRef] = { v: '' };
if (!worksheet[cellRef].s) worksheet[cellRef].s = {};
worksheet[cellRef].s.alignment = {
vertical: 'center',
horizontal: 'center',
wrapText: true,
};
// 设置表头样式
if (row === 0) {
worksheet[cellRef].s.font = { bold: true };
}
}
}
};
</script>
<style lang="scss" scoped>
.el_TablePage {
margin: auto;
.el-transfer {
display: flex;
align-items: center;
justify-content: space-between;
}
}
.el_TablePage_btn {
text-align: right;
margin-top: 10px;
}
</style>