vue3项目中通过LuckySheet实现Excel在线编辑、本地导入与导出
一、集成LuckySheet
集成方式有CDN引入和本地引入两种,我采用的是本地引入。参考文章:https://blog.csdn.net/m0_59415345/article/details/136963918
二、实现本地自动导入Excel
因为我的业务要求用户在上一个界面点击编辑后自动跳转LuckySheet在线编辑界面,所以文件上传方式和参考文章实现的方式不太相同。
我的项目中通过上一个界面路由query传递文件的路径path参数(http或者https开头)到onlineEdit.vue,在LuckySheet初始化完成后通过JavaScript触发input按钮的文件选择并模拟文件输入变化,以编程方式设置文件并触发change事件。
1、先安装LuckyExcel依赖
npm install luckyexcel --save
2、vue组件代码如下
<template>
<div className="hello">
<div style="position: absolute; top: 0">
<input ref="fileInput" style="font-size: 16px; width: 400px" type="file" @change="uploadExcel" disabled />
</div>
<div id="luckysheet" style="margin: 0px; padding: 0px; position: absolute; width: 100%; left: 0px; top: 30px; bottom: 0px"></div>
</div>
</template>
<script>
import LuckyExcel from 'luckyexcel';
import { getFileAccessHttpUrl } from '@/utils/common/compUtils';
export default {
props: {
msg: String,
},
data() {
return {
selected: '',
isMaskShow: false,
};
},
async mounted() {
// 初始化Luckysheet的空表格
this.initLuckySheet();
// 从路由参数获取文件路径
let filePath = getFileAccessHttpUrl(this.$route.query.path);
if (filePath) {
try {
// 自动加载文件
await this.loadFileAutomatically(filePath);
} catch (error) {
alert('自动加载文件失败,请检查文件路径是否正确。');
}
}
},
methods: {
initLuckySheet() {
this.$nextTick(() => {
$(function () {
luckysheet.create({
// 保持原来的初始化配置
container: 'luckysheet',
title: 'Luckysheet De1mo',
lang: 'zh',
plugins: ['chart'],
});
});
});
},
async loadFileAutomatically(filePath) {
try {
if (!filePath.startsWith('http')) {
throw new Error('仅支持 HTTP/HTTPS 协议');
}
// 处理特殊字符编码
const encodedFilePath = encodeURI(filePath);
const response = await fetch(encodedFilePath);
if (!response.ok) {
throw new Error(`文件下载失败: ${response.status} ${response.statusText}`);
}
// 校验文件扩展名
const fileName = decodeURIComponent(
// 解码中文文件名
encodedFilePath.split('/').pop() || 'file.xlsx'
);
const fileExt = fileName.split('.').pop().toLowerCase();
if (fileExt !== 'xlsx') {
throw new Error('仅支持 .xlsx 文件');
}
// 强制修正 MIME 类型
const blob = await response.blob();
const fixedBlob = new Blob([blob], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
});
// 创建 File 对象
const file = new File([fixedBlob], fileName);
// 触发文件选择
const input = this.$refs.fileInput;
const dataTransfer = new DataTransfer();
dataTransfer.items.add(file);
input.files = dataTransfer.files;
input.dispatchEvent(new Event('change'));
} catch (error) {
console.error('自动加载失败:', error);
alert(`错误: ${error.message}`);
}
},
uploadExcel(evt) {
// 保持原有的上传处理逻辑
const files = evt.target.files;
if (!files || files.length === 0) {
alert('请选择文件');
return;
}
// 验证文件类型
const file = files[0];
if (!file.name.endsWith('.xlsx')) {
alert('仅支持.xlsx文件');
return;
}
// 转换并加载表格
LuckyExcel.transformExcelToLucky(file, (exportJson) => {
if (!exportJson.sheets) {
alert('文件解析失败');
return;
}
window.luckysheet.destroy();
window.luckysheet.create({
container: 'luckysheet',
showinfobar: false,
data: exportJson.sheets,
title: exportJson.info.name,
});
});
},
},
};
</script>
其中getFileAccessHttpUrl函数如下,主要作用是将文件路径转换为完整的静态资源HTTP访问URL
export const getFileAccessHttpUrl = (fileUrl, prefix = 'http') => {
let result = fileUrl;
try {
if (fileUrl && fileUrl.length > 0 && !fileUrl.startsWith(prefix)) {
//判断是否是数组格式
let isArray = fileUrl.indexOf('[') != -1;
if (!isArray) {
let prefix = `${baseApiUrl}/x/x/x/`; //这里替换成自己的后端文件下载api
// 判断是否已包含前缀
if (!fileUrl.startsWith(prefix)) {
result = `${prefix}${fileUrl}`;
}
}
}
} catch (err) {}
return result;
};
三、导出Excel
导出文件就比较简单,用的LuckySheet官方函数,跟开头提到的参考文章实现逻辑一致
1、先安装依赖
npm i exceljs --save
npm i file-saver --save
2、新建export.js函数(我的代码做了一点点修改,可以直接复制参考文章的使用)
整体onlineEdit.vue组件代码如下:
<template>
<div className="hello">
<div style="position: absolute; top: 0">
<input ref="fileInput" style="font-size: 16px; width: 400px" type="file" @change="uploadExcel" disabled />
<a-button @click="saveHandle(e)">保存</a-button>
</div>
<div id="luckysheet" style="margin: 0px; padding: 0px; position: absolute; width: 100%; left: 0px; top: 30px; bottom: 0px"></div>
</div>
</template>
<script>
import LuckyExcel from 'luckyexcel';
import { exportExcel } from './export';
import { getFileAccessHttpUrl } from '@/utils/common/compUtils';
export default {
props: {
msg: String,
},
data() {
return {
selected: '',
isMaskShow: false,
};
},
async mounted() {
// 初始化Luckysheet的空表格
this.initLuckySheet();
// 从路由参数获取文件路径
let filePath = getFileAccessHttpUrl(this.$route.query.path);
console.log('this is path', filePath);
if (filePath) {
try {
// 自动加载文件
await this.loadFileAutomatically(filePath);
} catch (error) {
alert('自动加载文件失败,请检查文件路径是否正确。');
}
}
},
methods: {
initLuckySheet() {
this.$nextTick(() => {
$(function () {
luckysheet.create({
// 保持原来的初始化配置
container: 'luckysheet',
title: 'Luckysheet De1mo',
lang: 'zh',
plugins: ['chart'],
});
});
});
},
async loadFileAutomatically(filePath) {
try {
if (!filePath.startsWith('http')) {
throw new Error('仅支持 HTTP/HTTPS 协议');
}
// 处理特殊字符编码
const encodedFilePath = encodeURI(filePath);
const response = await fetch(encodedFilePath);
if (!response.ok) {
throw new Error(`文件下载失败: ${response.status} ${response.statusText}`);
}
// 校验文件扩展名
const fileName = decodeURIComponent(
// 解码中文文件名
encodedFilePath.split('/').pop() || 'file.xlsx'
);
const fileExt = fileName.split('.').pop().toLowerCase();
if (fileExt !== 'xlsx') {
throw new Error('仅支持 .xlsx 文件');
}
// 强制修正 MIME 类型
const blob = await response.blob();
const fixedBlob = new Blob([blob], {
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
});
// 创建 File 对象
const file = new File([fixedBlob], fileName);
// 触发文件选择
const input = this.$refs.fileInput;
const dataTransfer = new DataTransfer();
dataTransfer.items.add(file);
input.files = dataTransfer.files;
input.dispatchEvent(new Event('change'));
} catch (error) {
console.error('自动加载失败:', error);
alert(`错误: ${error.message}`);
}
},
uploadExcel(evt) {
// 保持原有的上传处理逻辑
const files = evt.target.files;
if (!files || files.length === 0) {
alert('请选择文件');
return;
}
// 验证文件类型
const file = files[0];
if (!file.name.endsWith('.xlsx')) {
alert('仅支持.xlsx文件');
return;
}
// 转换并加载表格
LuckyExcel.transformExcelToLucky(file, (exportJson) => {
if (!exportJson.sheets) {
alert('文件解析失败');
return;
}
window.luckysheet.destroy();
window.luckysheet.create({
container: 'luckysheet',
showinfobar: false,
data: exportJson.sheets,
title: exportJson.info.name,
});
});
},
saveHandle() {
try {
// 获取所有工作表数据(注意这里要使用正确的API)
const sheets = window.luckysheet.getAllSheets();
// console.log('this is content:', sheets);
// 调用导出方法(假设value参数是文件名)
const defaultName = sheets[0]?.name || 'export';
exportExcel(sheets, defaultName);
this.$message.success('保存成功');
} catch (error) {
console.error('保存失败:', error);
this.$message.error('保存失败,请检查控制台');
}
},
},
};
</script>