1
0
mirror of synced 2025-11-06 04:20:54 +08:00

表格组件新增导出按钮

This commit is contained in:
laa
2025-05-15 13:57:27 +08:00
committed by lma
parent 5665dce2cd
commit f4f9492ce1
3 changed files with 406 additions and 8 deletions

View File

@@ -45,7 +45,8 @@
"vue-ruler-tool": "^1.2.4",
"vue-superslide": "^0.1.1",
"vuedraggable": "^2.24.1",
"vuex": "^3.6.2"
"vuex": "^3.6.2",
"xlsx": "^0.18.5"
},
"devDependencies": {
"autoprefixer": "8.5.0",

View File

@@ -26,6 +26,14 @@ export const widgetTable = {
{
name: '表格设置',
list: [
{
type: 'el-switch',
label: '显示导出按钮',
name: 'showExportButton',
required: false,
placeholder: '',
value: false
},
{
type: 'el-input-number',
label: '显示行数',

View File

@@ -1,6 +1,6 @@
<template>
<div :style="styleObj">
<superslide v-if="hackReset" :options="options" class="txtScroll-top">
<superslide v-if="hackReset" :options="options" class="txtScroll-top" ref="superslide">
<!--表头-->
<div class="title">
<div v-for="(item, index) in header" :key="index"
@@ -27,6 +27,48 @@
</ul>
</div>
</superslide>
<!-- 添加导出按钮仅在预览模式显示 -->
<div v-if="ispreview && optionsSetUp.showExportButton" class="export-button-container" :style="exportButtonContainerStyle">
<div class="export-button" :style="exportButtonStyle" @click="openExportDialog">
<span class="button-text">导出</span>
</div>
</div>
<!-- 导出对话框 -->
<el-dialog
title="导出表格数据"
:visible.sync="exportDialogVisible"
:width="dialogWidth"
:fullscreen="isMobile"
:close-on-click-modal="false"
center
:modal-append-to-body="true"
append-to-body
:z-index="9999"
class="export-dialog">
<el-form :model="exportForm" label-width="100px" size="small">
<el-form-item label="文件名称">
<el-input v-model="exportForm.fileName" placeholder="请输入文件名称" clearable></el-input>
</el-form-item>
<el-form-item label="导出格式">
<el-radio-group v-model="exportForm.format">
<el-radio label="excel">Excel格式(.xlsx)</el-radio>
<el-radio label="csv">CSV格式(.csv)</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item>
<div class="export-info">
<p><i class="el-icon-info"></i> 表格名称数据表</p>
<p><i class="el-icon-tickets"></i> 数据总量{{list.length}} {{header.length}}</p>
</div>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button size="small" @click="exportDialogVisible = false"> </el-button>
<el-button size="small" type="primary" @click="confirmExport" :loading="exporting">确定导出</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
@@ -60,6 +102,16 @@ export default {
optionsPosition: {},
optionsData: {},
flagInter: null,
// 新增导出对话框相关数据
exportDialogVisible: false,
exporting: false,
exportForm: {
fileName: '',
format: 'excel'
},
// 添加设备检测变量
isMobile: false,
screenWidth: window.innerWidth
};
},
computed: {
@@ -107,6 +159,57 @@ export default {
"border-color": bodyStyle.borderColor,
"color": bodyStyle.bodyColor,
};
},
// 新增样式计算属性
exportButtonStyle() {
// 根据表头字体大小动态计算按钮大小
const headerFontSize = parseInt(this.optionsSetUp.fontSizeHeader || 16);
// 计算图标尺寸,基于表头字体大小
const iconSize = Math.max(14, Math.min(24, headerFontSize * 1.1));
// 计算按钮高度,略大于图标尺寸
const buttonHeight = Math.round(iconSize * 1.8);
return {
height: `${buttonHeight}px`,
padding: '0 12px',
fontSize: `${iconSize}px`,
backgroundColor: 'rgba(39, 174, 96, 0.9)',
borderRadius: '4px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
boxShadow: '0 2px 12px 0 rgba(0, 0, 0, 0.1)',
color: '#ffffff'
};
},
exportButtonContainerStyle() {
// 计算按钮容器的位置,使其恰好超出表头几个像素
const headerHeight = this.optionsSetUp.rowHeight || 50;
// 按钮高度
const buttonHeight = parseInt(this.exportButtonStyle.height);
// 设置按钮位置
return {
position: 'absolute',
top: `-${buttonHeight + 5}px`, // 按钮高度 + 5px的间距
right: '10px',
zIndex: '100'
};
},
// 添加对话框宽度计算属性
dialogWidth() {
if (this.isMobile) {
return '100%';
} else if (this.screenWidth < 768) {
return '90%';
} else if (this.screenWidth < 992) {
return '80%';
} else if (this.screenWidth < 1200) {
return '60%';
} else {
return '420px';
}
}
},
watch: {
@@ -126,6 +229,13 @@ export default {
this.optionsData = this.value.data;
this.initData();
targetWidgetLinkageLogic(this); // 联动-目标组件逻辑
// 添加窗口大小变化监听
this.checkDeviceType();
window.addEventListener('resize', this.handleResize);
},
beforeDestroy() {
// 移除窗口大小变化监听
window.removeEventListener('resize', this.handleResize);
},
methods: {
initData() {
@@ -230,7 +340,163 @@ export default {
styleJson["width"] = this.optionsSetUp.dynamicAddTable[index].width;
}
return styleJson;
}
},
// 添加的方法 - 设备类型检测
checkDeviceType() {
this.screenWidth = window.innerWidth;
this.isMobile = this.screenWidth <= 576;
},
// 处理窗口大小变化
handleResize() {
this.checkDeviceType();
},
// 打开导出对话框前检查设备类型
openExportDialog() {
if (!this.list || this.list.length === 0) {
this.$message.warning('没有数据可导出');
return;
}
// 重新检查设备类型
this.checkDeviceType();
// 设置默认文件名(表格名称+日期时间)
const now = new Date();
const dateStr = `${now.getFullYear()}${(now.getMonth()+1).toString().padStart(2,'0')}${now.getDate().toString().padStart(2,'0')}`;
const timeStr = `${now.getHours().toString().padStart(2,'0')}${now.getMinutes().toString().padStart(2,'0')}`;
// 准备对话框数据
this.exportForm.fileName = `表格数据_${dateStr}_${timeStr}`;
this.exportForm.format = 'excel'; // 默认选择Excel格式
// 显示导出对话框
this.exportDialogVisible = true;
},
// 确认导出方法
confirmExport() {
// 检查文件名是否为空
if (!this.exportForm.fileName || this.exportForm.fileName.trim() === '') {
this.$message.warning('请输入文件名');
return;
}
// 设置导出中状态
this.exporting = true;
// 根据选择的格式执行不同的导出方法
try {
if (this.exportForm.format === 'excel') {
this.doExportExcel();
} else {
this.doExportCSV();
// 只有CSV导出直接在这里显示成功消息和关闭对话框
this.$message.success('导出成功');
this.exportDialogVisible = false;
this.exporting = false;
}
} catch (error) {
console.error('导出失败:', error);
this.$message.error('导出失败,请重试');
this.exporting = false;
}
},
// 执行Excel导出
doExportExcel() {
// 引入xlsx库使用动态导入以减少初始加载时间
import('xlsx').then(XLSX => {
// 准备数据:表头和内容
const tableHeaders = this.header.map(header => header.name);
const tableKeys = this.header.map(header => header.key);
// 创建表格数据数组,从列表数据映射
const tableData = this.list.map(row => {
const rowData = {};
tableKeys.forEach((key, index) => {
rowData[tableHeaders[index]] = row[key];
});
return rowData;
});
// 创建工作簿和工作表
const wb = XLSX.utils.book_new();
const ws = XLSX.utils.json_to_sheet(tableData, { header: tableHeaders });
// 计算列宽 - 动态计算合适的列宽
ws['!cols'] = tableHeaders.map((header, index) => {
// 尝试计算合适的列宽,考虑标题和内容
const headerLength = header ? header.toString().length : 0;
// 计算该列数据的最大长度
let maxLength = headerLength;
tableData.forEach(row => {
const cellValue = row[header];
const cellLength = cellValue ? cellValue.toString().length : 0;
maxLength = Math.max(maxLength, cellLength);
});
// 为确保列宽适合内容,添加一些额外空间
return { wch: Math.min(50, maxLength + 2) }; // 限制最大宽度为50个字符
});
// 添加工作表到工作簿
XLSX.utils.book_append_sheet(wb, ws, '数据表');
// 导出文件,使用用户指定的文件名
XLSX.writeFile(wb, `${this.exportForm.fileName}.xlsx`);
// 导出成功
this.$message.success('导出成功');
this.exportDialogVisible = false;
this.exporting = false;
}).catch(err => {
console.error('导出Excel失败:', err);
this.$message.error('导出Excel失败请重试');
this.exporting = false;
});
},
// 执行CSV导出
doExportCSV() {
// 准备表头和数据
const tableHeaders = this.header.map(header => header.name);
const tableKeys = this.header.map(header => header.key);
// 创建CSV内容
let csvContent = '\uFEFF'; // 添加BOM标记以支持中文
// 添加表头
csvContent += tableHeaders.join(',') + '\r\n';
// 添加数据行
this.list.forEach(row => {
const rowValues = tableKeys.map(key => {
const value = row[key] !== undefined && row[key] !== null ? row[key] : '';
// 如果值包含逗号、引号或换行符,需要用引号包裹并转义内部引号
if (typeof value === 'string' && (value.includes(',') || value.includes('"') || value.includes('\n'))) {
return `"${value.replace(/"/g, '""')}"`;
}
return value;
});
csvContent += rowValues.join(',') + '\r\n';
});
// 创建Blob对象
const blob = new Blob([csvContent], {
type: 'text/csv;charset=utf-8'
});
// 创建下载链接,使用用户指定的文件名
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = `${this.exportForm.fileName}.csv`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
},
}
};
</script>
@@ -272,11 +538,134 @@ export default {
flex-wrap: wrap;
}
/*.txtScroll-top .infoList li:nth-child(n) {
background: rgb(0, 59, 81);
/* 添加导出按钮样式 */
.export-button-container {
position: absolute;
right: 0px;
z-index: 100;
.export-button {
transition: all 0.3s ease;
cursor: pointer;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
&:hover {
transform: scale(1.05);
background-color: rgba(39, 174, 96, 1) !important;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
}
i {
margin-right: 5px;
}
.button-text {
font-size: 14px;
font-weight: 500;
}
}
}
.txtScroll-top .infoList li:nth-child(2n) {
background: rgb(10, 39, 50);
}*/
/* 添加导出信息样式 */
.export-info {
background-color: #f5f7fa;
border-radius: 4px;
padding: 10px 15px;
margin-top: 5px;
p {
margin: 5px 0;
font-size: 13px;
color: #606266;
display: flex;
align-items: center;
i {
margin-right: 5px;
color: #409EFF;
}
}
}
/* 确保对话框在大屏中显示正常 */
::v-deep .el-dialog {
background-color: #fff;
border-radius: 6px;
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.3);
position: relative;
z-index: 9999 !important;
.el-dialog__header {
padding: 15px 20px;
border-bottom: 1px solid #e4e7ed;
}
.el-dialog__body {
padding: 20px;
}
.el-dialog__footer {
padding: 10px 20px 15px;
border-top: 1px solid #e4e7ed;
}
.el-form-item {
margin-bottom: 15px;
}
}
/* 为移动设备添加特定样式 */
::v-deep .export-dialog {
@media screen and (max-width: 576px) {
display: flex;
flex-direction: column;
margin: 0 !important;
.el-dialog {
width: 100% !important;
margin: 0 !important;
max-width: 100%;
border-radius: 0;
display: flex;
flex-direction: column;
height: 100%;
.el-dialog__body {
flex: 1;
overflow-y: auto;
padding: 15px;
}
.el-form-item__label {
width: 80px !important;
}
.el-radio-group {
display: flex;
flex-direction: column;
.el-radio {
margin-left: 0;
margin-bottom: 10px;
}
}
}
}
@media screen and (max-width: 768px) {
.el-form-item__label {
width: 80px !important;
}
.el-form {
.el-radio-group {
flex-wrap: wrap;
}
}
}
}
::v-deep .v-modal {
z-index: 9998 !important;
}
</style>