前端界面在线excel编辑器 。node编写post接口获取文件流,使用传参替换表格内容展示、前后端一把梭。

首先luckysheet插件是支持在线替换excel内容编辑得但是浏览器无法调用本地文件,如果只是展示,让后端返回文件得二进制文件流就可以了,直接使用luckysheet展示。

这里我们使用xlsx-populate得node简单应用来调用本地文件,自己写一个接口,让自己对后端有更一步得了解。

效果图:

首先我们创建一个node应用

1、只是想展示文件,直接让后端返回文件流可以跳过直接往下拉,看如何在线展示excel编辑器

2、node下载得二进制文件流,不需要展示得可以直接导出下载

前置条件:已经安装node16以上版本

初始化项目:

1.创建项目目录 :一个文件夹名为 my-xlsx-populate
2.文件内右键打开cmd  使用命令创建   生成package.json:(-y使用默认配置)
npm init -y

3.package.json文件我们需要得依赖复制一下,大家直接npm i 就可以了

{"name": "my-xlsx-populate", // 项目唯一标识符"version": "1.0.0", // 初始版本号"description": "基于Excel文件操作的Node.js服务框架","main": "server.js", // 主入口文件"scripts": {"test": "echo \"Error: no test specified\" && exit 1", "start": "node server.js" // 启动服务脚本[2](@ref)},"keywords": [], // 关键词列表"license": "ISC", // 开源协议"dependencies": {"cors": "^2.8.5", // 跨域资源共享中间件,用于处理跨域请求"express": "^4.21.2", // Node.js核心Web框架,提供HTTP服务基础能力"multer": "^1.4.5-lts.1", // 文件上传处理中间件,支持multipart/form-data"xlsx-populate": "^1.21.0" // Excel文件操作库,支持读写xlsx文件}
}

4.创建server.js文件,功能如下 。

  1. /excelprocess创建post接口
  2. 获取body传参循环操作xlsx-populate替换excel内容
  3. 接口返回文件流
const express = require('express');
const XlsxPopulate = require('xlsx-populate');
const path = require('path');
const fs = require('fs');
const cors = require('cors'); // 引入 cors 中间件const app = express();// 使用 cors 中间件,允许所有来源的请求
app.use(cors());// 解析 JSON 请求体
app.use(express.json());// 检查 excelTemplates 目录是否存在,不存在则创建
const templateDir = path.join(__dirname, 'excelTemplates');
if (!fs.existsSync(templateDir)) {fs.mkdirSync(templateDir);
}// 处理静态文件,使得前端页面可以被访问
app.use(express.static(__dirname));// 处理文件处理请求
app.post('/excelprocess', async (req, res) => {try {// 获取 excelTemplates 目录下的所有文件const files = fs.readdirSync(templateDir);if (files.length === 0) {console.error('excelTemplates 目录中没有文件');return res.status(400).send('excelTemplates 目录中没有文件');}// 选择第一个文件作为模板const selectedFile = path.join(templateDir, files[0]);console.log('选定模板文件:', selectedFile);// 检查文件扩展名const fileExtension = path.extname(selectedFile).toLowerCase();if (fileExtension !== '.xlsx') {console.error('文件扩展名不正确:', fileExtension);return res.status(400).send('仅支持 .xlsx 文件');}// 检查文件是否可读fs.accessSync(selectedFile, fs.constants.R_OK);// 读取 Excel 文件const workbook = await XlsxPopulate.fromFileAsync(selectedFile);console.log('成功读取文件:', selectedFile);const sheet = workbook.sheet(0);// 获取传入的修改数据const modifications = req.body;// 根据传入的数据修改 Excel 文件内容for (const [cellAddress, value] of Object.entries(modifications)) {sheet.cell(cellAddress).value(value);}// 生成修改后的文件路径const defaultFileName = 'output.xlsx';const outputPath = path.join(__dirname, defaultFileName);// 将修改后的文件保存到磁盘await workbook.toFileAsync(outputPath);console.log('文件已保存到:', outputPath);// 对文件名进行严格编码const originalFileName = req.query.filename || '修改后.xlsx'; // 使用查询参数中的文件名或默认文件名const encodedFileName = encodeURIComponent(originalFileName).replace(/'/g, '%27');// 设置响应头并发送文件流res.setHeader('Content-Disposition', `attachment; filename="${encodedFileName}"`);res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');// 使用流的方式发送文件const fileStream = fs.createReadStream(outputPath);fileStream.pipe(res);// 监听文件流的错误fileStream.on('error', (err) => {console.error('文件流发送时出错:', err);res.status(500).send('文件流发送时出错');});} catch (error) {console.error('处理文件时出错:', error.message);res.status(500).send(`处理文件时出错: ${error.message}`);}
});// 启动服务器
const port = 3000;
app.listen(port, () => {console.log(`服务器运行在端口 http://localhost:${port}/`);
});

5.创建excelTemplates文件夹用于存放我们要操作得本地文件,在里放入一个excel文件

全部目录如下:

excel模板sheet设置如下:

6.运行node 服务

7.打开接口调试工具

接口类型: post

接口地址 : http://localhost:3000/excelprocess

接口传参:

[{"sheetName": "Sheet1", "data": {"A1": "新值1"}},{"sheetName": "Sheet2", "data": {"A1": "新值2"}},{"sheetName": "Sheet3", "data": {"A1": "新值3"}}
]

点击下载:

打开文件内容如下:

替换成功,我们获取到了一个替换后得二进制文件流

xlsx-populate更多具体配置请看我得另一篇文章:

前端插件使用xlsx-populate,花样配置excel内容,根据坐添加标替换excel内容,修改颜色,合并单元格...。_xlsxpopulate-CSDN博客

接下来我们将二进制文件展示到前端接口

接下来我们使用luckysheet插件展示操作完得excel文件

使用luckysheet插件

1.克隆官方gite代码到本地
git clone https://gitee.com/mengshukeji/Luckysheet.git
2.流水线操作-下载依赖打包得到dist文件
npm install
npm install gulp -gnpm run build

3.来到VUE项目根目录下创建   public/Luckysheet
  将dist里面得文件复制到public/Luckysheet目录下
4.来到VUE项目根目录下luckysheet.html 

将public/Luckysheet里面得文件引入到luckysheet.html 

luckysheet.html 内容如下

<!DOCTYPE html>
<html lang="en">
<head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1.0"><link rel="icon" href="<%= BASE_URL %>favicon.ico"><title>luckysheet-wrapper</title><link rel="stylesheet" href="/Luckysheet/plugins/css/pluginsCss.css"><link rel="stylesheet" href="/Luckysheet/plugins/plugins.css"><link rel="stylesheet" href="/Luckysheet/css/luckysheet.css"><link rel="stylesheet" href="/Luckysheet/assets/iconfont/iconfont.css"><script src="/Luckysheet/plugins/js/plugin.js"></script><script src="/Luckysheet/luckysheet.umd.js"></script>
</head>
<body>
<noscript><strong>We're sorry but luckysheet-wrapper doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<div id="sheetContainer" style="margin:0px;padding:0px;position:absolute;width:100%;height:95%;left: 0px;top: 0px;"></div>
<!-- built files will be auto injected -->
</body>
</html>

目录结构如下:

5.界面引用Luckysheet

这里我们使用  iframe 官方得方法是把Luckysheet实列挂在到windows实列上,初始化为windows.luckysheet  但是我想一个界面同时存在多个编辑器时候,就会出现只能有一个excel展示,我们使用iframe把luckysheet 实列用沙箱隔离,保证独立性

<iframe :id="iframeId" height="100%" width="100%" src="/luckysheet.html" class="no-scroll"></iframe>

我得另一篇具体文章详情:解决Luckysheet在线预览编辑Excel、PDF.....无法在同一个界面创建多个luckysheet实列问题-CSDN博客

什么是iframe,火爆的微前端解决方案方案,教你快速看懂使用-CSDN博客

有兴趣大家可以去看看哈~

6.这里为了编辑器逻辑隔离,也为了可以创建多个实列,我们把编辑器封装为一个子组件使用:

功能逻辑:

第一步:这里我们需要引入luckyexcel ,因为luckysheet只支持展示json格式得文件,我们从node接口http://localhost:3000/excelprocess 获取得是二进制文件流,所以要转换。
npm install luckyexcel --save
第二步:将我们准备得 luckysheet.html 文件引入到 iframe ,设置动态id
  <iframe :id="iframeId" height="100%" width="100%" src="/luckysheet.html" class="no-scroll"></iframe>const iframeId = `iframe${Math.random().toString().substring(2)}`;

第三步:onMounted 里面 初始化 luckysheet实列
const $sheet = ref(null); // 存储luckysheet实例
onMounted(() => {const frame = document.querySelector(`#${iframeId}`);frame.onload = () => {$sheet.value = frame.contentWindow.luckysheet;const container = frame.contentDocument.createElement('div');container.id = 'sheetContainer';frame.contentDocument.body.appendChild(container);$sheet.value.create({container: container.id});initLuckysheet(); //调用后端接口};});
第四步:调用node接口
const initLuckysheet = async () => {try {const response = await axios.post('http://localhost:3000/excelprocess',[{"sheetName": "Sheet1", "data": {"A1": "新值1"}},{"sheetName": "Sheet2", "data": {"A1": "新值2"}},{"sheetName": "Sheet3", "data": {"A1": "新值3"}}] , {responseType: 'blob'}).then(response => {const file = new File([response.data], 'XXX.xlsx', {type: response.data.type});console.log(file);var files = [];files.push(file);uploadExcel(files); //加载表单数据});} catch (error) {console.error('加载并解析 Excel 失败:', error);}
};

第五步:接口返回数据使用 luckyexcel 转换为json格式 初始化  luckysheet ,加载表单数据
const uploadExcel = (files) => {LuckyExcel.transformExcelToLucky(files[0], function (exportJson, luckysheetfile) {if (exportJson.sheets == null || exportJson.sheets.length == 0) return alert('读取excel文件内容失败, 目前不支持XLS文件!');$sheet.value.destroy();$sheet.value.create({data: exportJson.sheets,title: exportJson.info.name,userInfo: exportJson.info.name.creator,container: 'sheetContainer', // 设定DOM容器的idshowtoolbar: false, // 是否显示工具栏showinfobar: false, // 是否显示顶部信息栏showstatisticBar: true, // 是否显示底部计数栏sheetBottomConfig: false, // sheet页下方的添加行按钮和回到顶部按钮配置allowEdit: false, // 是否允许前台编辑enableAddRow: false, // 是否允许增加行enableAddCol: false, // 是否允许增加列sheetFormulaBar: true, // 是否显示公式栏enableAddBackTop: false, // 返回头部按钮showsheetbar: true, // 是否显示底部sheet页按钮// 自定义配置底部sheet页按钮showsheetbarConfig: {add: false,menu: false,},});});
};
子组件eftExcel.vue 完整代码 如下
<template><div style="height: 100%; overflow: hidden;"><div class="controls"></div><iframe :id="iframeId" height="100%" width="100%" src="/luckysheet.html" class="no-scroll"></iframe></div>
</template><script setup>
import {ref, onMounted, defineProps} from 'vue';
import axios from 'axios';
import * as LuckyExcel from 'luckyexcel';
import { ElMessage } from 'element-plus'
const iframeId = `iframe${Math.random().toString().substring(2)}`;
const $sheet = ref(null); // 存储luckysheet实例onMounted(() => {const frame = document.querySelector(`#${iframeId}`);frame.onload = () => {$sheet.value = frame.contentWindow.luckysheet;const container = frame.contentDocument.createElement('div');container.id = 'sheetContainer';frame.contentDocument.body.appendChild(container);$sheet.value.create({container: container.id});initLuckysheet();};});const initLuckysheet = async () => {try {const response = await axios.post('http://localhost:3000/excelprocess',[{"sheetName": "Sheet1", "data": {"A1": "新值1"}},{"sheetName": "Sheet2", "data": {"A1": "新值2"}},{"sheetName": "Sheet3", "data": {"A1": "新值3"}}] , {responseType: 'blob'}).then(response => {const file = new File([response.data], 'XXX.xlsx', {type: response.data.type});console.log(file);var files = [];files.push(file);uploadExcel(files);});} catch (error) {console.error('加载并解析 Excel 失败:', error);}
};
const uploadExcel = (files) => {LuckyExcel.transformExcelToLucky(files[0], function (exportJson, luckysheetfile) {if (exportJson.sheets == null || exportJson.sheets.length == 0) return alert('读取excel文件内容失败, 目前不支持XLS文件!');$sheet.value.destroy();$sheet.value.create({data: exportJson.sheets,title: exportJson.info.name,userInfo: exportJson.info.name.creator,container: 'sheetContainer', // 设定DOM容器的idshowtoolbar: false, // 是否显示工具栏showinfobar: false, // 是否显示顶部信息栏showstatisticBar: true, // 是否显示底部计数栏sheetBottomConfig: false, // sheet页下方的添加行按钮和回到顶部按钮配置allowEdit: false, // 是否允许前台编辑enableAddRow: false, // 是否允许增加行enableAddCol: false, // 是否允许增加列sheetFormulaBar: true, // 是否显示公式栏enableAddBackTop: false, // 返回头部按钮showsheetbar: true, // 是否显示底部sheet页按钮// 自定义配置底部sheet页按钮showsheetbarConfig: {add: false,menu: false,},});});
};</script><style scoped>
.controls {margin-bottom: 20px;
}.no-scroll {overflow: hidden; /* 隐藏水平和垂直滚动条 */
}
</style>
第六步:父组件引入调用
<template>
<div class="index_body"><div class="container"><el-main style="height: calc(100% - 80px); position: relative; padding: 0px"><LeftExcel ></LeftExcel></el-main></div>
</div>
</template><script setup>
import LeftExcel from './leftExcel.vue'
import { ref, onMounted, onUnmounted } from 'vue'</script><style lang="scss" scoped>
.index_body{
margin-top: 50px;.container {display: flex;justify-content: space-between; /* 根据需要调整 */align-items: stretch; /* 根据需要调整 */height: 100vh; /* 根据需要调整 */
}.left-component {flex: 1; /* 根据需要调整 */margin-right: 10px; /* 根据需要调整 */
}.right-component {flex: 1; /* 根据需要调整 */margin-left: 10px; /* 根据需要调整 */
}
}
</style>

效果展示:

第六步:我们操作一下功能

接口传参中我们可以自己修改为需要得内容

    const response = await axios.post('http://localhost:3000/excelprocess',[{"sheetName": "Sheet1", "data": {"A1": "新值1"}},{"sheetName": "Sheet2", "data": {"A1": "新值2"}},{"sheetName": "Sheet3", "data": {"A1": "新值3"}}] , {responseType: 'blob'})

我们把本地得表格添加边框,只替换其中得值,再次展示,实际开发中我们可以提前设置好模板得样式比如换行,合并单元格,这样我们只要替换其中得坐标值就可以了。

大家看到这里麻烦给个赞吧!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/76838.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JavaScript学习20-Event事件对象

1.属性 即点击谁就打印出来谁 2.方法 未添加stopPropagatio方法&#xff1a; 添加stopPropagatio方法后&#xff1a;

FreeRTOS 启动过程中 SVC 和 PendSV 的工作流程​

在 FreeRTOS 的启动过程中,SVC(Supervisor Call) 和 PendSV(Pendable Service Call) 是两个关键的系统异常,分别用于 首次任务启动 和 任务上下文切换。它们的协作确保了从内核初始化到多任务调度的平滑过渡。以下是详细的工作流程分析(以 ARM Cortex-M 为例): 1. SVC…

[自制调试工具]构建高效调试利器:Debugger 类详解

一、引言 在软件开发的漫漫征程中&#xff0c;调试就像是一位忠诚的伙伴&#xff0c;时刻陪伴着开发者解决代码里的各类问题。为了能更清晰地了解程序运行时变量的状态&#xff0c;我们常常需要输出各种变量的值。而 Debugger 类就像是一个贴心的调试助手&#xff0c;它能帮我…

foobar2000 VU Meter Visualisation 插件汉化版 VU表

原英文插件点此 界面展示 下载 https://wwtn.lanzout.com/iheI22ssoybi 安装方式 解压安装文件&#xff0c;文件名为&#xff1a;foo_vis_vumeter-0.10.2_CHINIESE.fb2k-component

消息中间件对比与选型指南:Kafka、ActiveMQ、RabbitMQ与RocketMQ

目录 引言 消息中间件的定义与作用 消息中间件在分布式系统中的重要性 对比分析的四种主流消息中间件概述 消息中间件核心特性对比 消息传递模型 Kafka&#xff1a;专注于发布-订阅模型 ActiveMQ&#xff1a;支持点对点和发布-订阅两种模型 RabbitMQ&#xff1a;支持点…

liunx输入法

1安装fcitx5 sudo apt update sudo apt install fcitx fcitx-pinyin 2配置为默认输入法 设置-》系统-》区域和语言 点击系统弹出语言和支持选择键盘输入法系统 3设置设置 fcitx-configtool 如果没显示需要重启电脑 4配置fcitx 把搜狗输入法放到第一位&#xff08;点击下面…

WindowsPE文件格式入门05.PE加载器LoadPE

https://bpsend.net/thread-316-1-1.html LoadPE - pe 加载器 壳的前身 如果想访问一个程序运行起来的内存,一种方法就是跨进程读写内存,但是跨进程读写内存需要来回调用api,不如直接访问地址来得方便,那么如果我们需要直接访问地址,该怎么做呢?.需要把dll注进程,注进去的代码…

QGIS中第三方POI坐标偏移的快速校正-百度POI

1.百度POI&#xff1a; name,lng,lat,address 龙记黄焖鸡米饭(共享区店),121.908315,30.886636,南汇新城镇沪城环路699弄117号(A1区110室) 好福记黄焖鸡(御桥路店),121.571409,31.162292,沪南路2419弄26号1层B间 御品黄焖鸡米饭(安亭店),121.160322,31.305977,安亭镇新源路792号…

SQL的调优方案

一、前言 SQL调优是提升数据库性能的关键手段。需结合索引优化、SQL语句优化、执行计划分析及数据库架构设计等多方面综合处理。 二、索引优化 创建合适索引 高频查询字段&#xff1a;对WHERE、JOIN、ORDER BY涉及的字段创建索引&#xff0c;尤其是区分度高的字段&#xff08…

【项目管理】第一部分 信息技术 1/2

相关文档&#xff0c;希望互相学习&#xff0c;共同进步 风123456789&#xff5e;-CSDN博客 概要 知识点&#xff1a; 现代化基础设施、数字经济、工业互联网、车联网、智能制造、智慧城市、数字政府、5G、常用数据库类型、数据仓库、信息安全、网络安全态势感知、物联网、大数…

【玩泰山派】1、mac上使用串口连接泰山派

文章目录 前言picocom工具连接泰山派安装picocom工具安装ch340的驱动串口工具接线使用picocom连接泰山派 参考 前言 windows上面有xshell这个好用的工具可以使用串口连接板子&#xff0c;在mac上好像没找到太好的工具&#xff0c;只能使用命令行工具去搞了。 之前查找说mac上…

【C++奇遇记】C++中的进阶知识(继承(一))

&#x1f3ac; 博客主页&#xff1a;博主链接 &#x1f3a5; 本文由 M malloc 原创&#xff0c;首发于 CSDN&#x1f649; &#x1f384; 学习专栏推荐&#xff1a;LeetCode刷题集 数据库专栏 初阶数据结构 &#x1f3c5; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如…

【Scratch编程系列】Scratch编程软件界面

Scratch是一款由麻省理工学院(MIT&#xff09; 设计开发的少儿编程工具。其特点是&#xff1a;使用者可以不认识英文单词&#xff0c;也可以不使用键盘&#xff0c;就可以进行编程。构成程序的命令和参数通过积木形状的模块来实现。用鼠标拖动指令模块到脚本区就可以了。 这个软…

开篇 - 配置Unlua+VsCode的智能提示、调试以及学习方法

智能提示 为要绑定Lua的蓝图创建模板文件&#xff0c;这会在Content/Script下生成lua文件 然后点击生成智能代码提示&#xff0c;这会在Plugins/Unlua/Intermediate/生成Intenllisense文件夹 打开VSCode,点击文件->将工作区另存为。生成一个空工作区&#xff0c;放置在工程…

QEMU-KVM加SPICE,云电脑诞生了

没错&#xff01;‌QEMU-KVM SPICE‌ 的组合&#xff0c;本质上就是一套‌轻量级云电脑&#xff08;云桌面&#xff09;‌的解决方案。通过虚拟化技术将计算资源池化&#xff0c;再通过SPICE协议提供流畅的远程桌面体验&#xff0c;用户用任意设备&#xff08;笔记本/平板/瘦客…

hashtable遍历的方法有哪些

在 Java 中&#xff0c;遍历 Hashtable&#xff08;或其现代替代品 HashMap&#xff09;有多种方式&#xff0c;以下是 6 种常用方法的详细说明和代码示例&#xff1a; 1. 使用 keySet() 增强 for 循环 Hashtable<String, Integer> table new Hashtable<>(); // …

如何完整迁移 Git 仓库 ?

Git 已经成为软件开发中版本控制和协作的事实上的标准。有时&#xff0c;开发人员可能需要将整个 Git 存储库 (包括其历史记录、分支和标记) 移动到新的位置或托管服务。在这个全面的指南中&#xff0c;我们将讨论在不丢失任何关键数据或历史记录的情况下无缝地重新定位完整 Gi…

BPSK调制器的作用和基本原理

BPSK&#xff08;Binary Phase Shift Keying&#xff0c;二进制相移键控&#xff09;调制器是数字通信系统中的关键组件&#xff0c;其核心作用是将二进制数字信号转换为模拟载波信号&#xff0c;通过相位变化传递信息。其具体作用的详细说明如下&#xff1a; 一、BPSK调制器的…

LeetCode-98. 验证二叉搜索树

一、题目 给定一个二叉树&#xff0c;判断其是否是一个有效的二叉搜索树。假设一个二叉搜索树具有如下特征&#xff1a; 若它的左子树不空&#xff0c;则左子树上所有结点的值均小于它的根结点的值&#xff1b; 若它的右子树不空&#xff0c;则右子树上所有结点的值均大于它的…

Python菜鸟教程(小程序)

目录 一.简易计算器 二.学生成绩分级 三.密码设置 四.作业选择 点赞收藏,评论支持 一.简易计算器 print(-------使用的运算符-------\n) print(1.加号) print(2.减号) print(3.乘号) print(4.除号) Aint(input(请输入第一个数: )) Bint(input(请输入第二个数: )) Fi…