当前位置: 首页 > news >正文

vue在线编辑表格导入导出

npm i file-saver
npm i exceljs 
npm i luckyexcel

index.html (方式一在html中引入)

<link rel='stylesheet' href='https://cdn.jsdelivr.net/npm/luckysheet@latest/dist/plugins/css/pluginsCss.css' /><link rel='stylesheet' href='https://cdn.jsdelivr.net/npm/luckysheet@latest/dist/plugins/plugins.css' /><link rel='stylesheet' href='https://cdn.jsdelivr.net/npm/luckysheet@latest/dist/css/luckysheet.css' /><link rel='stylesheet' href='https://cdn.jsdelivr.net/npm/luckysheet@latest/dist/assets/iconfont/iconfont.css' /><script src="https://cdn.jsdelivr.net/npm/luckysheet@latest/dist/plugins/js/plugin.js"></script><script src="https://cdn.jsdelivr.net/npm/luckysheet@latest/dist/luckysheet.umd.js"></script>

exportExcel.js

// const Excel = require('exceljs/dist/es5/exceljs.browser')
import Excel from 'exceljs'import FileSaver from 'file-saver'export let exportExcel = (luckysheet, value) => {// 参数为luckysheet.getluckysheetfile()获取的对象// 1.创建工作簿,可以为工作簿添加属性const workbook = new Excel.Workbook()// 2.创建表格,第二个参数可以配置创建什么样的工作表if (Object.prototype.toString.call(luckysheet) === '[object Object]') {luckysheet = [luckysheet]}luckysheet.forEach(function(table) {if (table.data.length === 0) return true// ws.getCell('B2').fill = fills.const worksheet = workbook.addWorksheet(table.name)const merge = (table.config && table.config.merge) || {}const borderInfo = (table.config && table.config.borderInfo) || {}// 3.设置单元格合并,设置单元格边框,设置单元格样式,设置值setStyleAndValue(table.data, worksheet)setMerge(merge, worksheet)setBorder(borderInfo, worksheet)return true})// return// 4.写入 bufferconst buffer = workbook.xlsx.writeBuffer().then(data => {// console.log('data', data)const blob = new Blob([data], {type: 'application/vnd.ms-excel;charset=utf-8',})FileSaver.saveAs(blob, `${value}.xlsx`)})return buffer
}var setMerge = function(luckyMerge = {}, worksheet) {const mergearr = Object.values(luckyMerge)mergearr.forEach(function(elem) {// elem格式:{r: 0, c: 0, rs: 1, cs: 2}// 按开始行,开始列,结束行,结束列合并(相当于 K10:M12)worksheet.mergeCells(elem.r + 1, elem.c + 1, elem.r + elem.rs, elem.c + elem.cs)})
}var setBorder = function(luckyBorderInfo, worksheet) {if (!Array.isArray(luckyBorderInfo)) return// console.log('luckyBorderInfo', luckyBorderInfo)luckyBorderInfo.forEach(function(elem) {// 现在只兼容到borderType 为range的情况// console.log('ele', elem)if (elem.rangeType === 'range') {let border = borderConvert(elem.borderType, elem.style, elem.color)let rang = elem.range[0]// console.log('range', rang)let row = rang.rowlet column = rang.columnfor (let i = row[0] + 1; i < row[1] + 2; i++) {for (let y = column[0] + 1; y < column[1] + 2; y++) {worksheet.getCell(i, y).border = border}}}if (elem.rangeType === 'cell') {// col_index: 2// row_index: 1// b: {//   color: '#d0d4e3'//   style: 1// }const { col_index, row_index } = elem.valueconst borderData = Object.assign({}, elem.value)delete borderData.col_indexdelete borderData.row_indexlet border = addborderToCell(borderData, row_index, col_index)// console.log('bordre', border, borderData)worksheet.getCell(row_index + 1, col_index + 1).border = border}// console.log(rang.column_focus + 1, rang.row_focus + 1)// worksheet.getCell(rang.row_focus + 1, rang.column_focus + 1).border = border})
}
var setStyleAndValue = function(cellArr, worksheet) {if (!Array.isArray(cellArr)) returncellArr.forEach(function(row, rowid) {row.every(function(cell, columnid) {if (!cell) return truelet fill = fillConvert(cell.bg)let font = fontConvert(cell.ff, cell.fc, cell.bl, cell.it, cell.fs, cell.cl, cell.ul)let alignment = alignmentConvert(cell.vt, cell.ht, cell.tb, cell.tr)let value = ''if (cell.f) {value = { formula: cell.f, result: cell.v }} else if (!cell.v && cell.ct && cell.ct.s) {// xls转为xlsx之后,内部存在不同的格式,都会进到富文本里,即值不存在与cell.v,而是存在于cell.ct.s之后// value = cell.ct.s[0].vcell.ct.s.forEach(arr => {value += arr.v})} else {value = cell.v}//  style 填入到_value中可以实现填充色let letter = createCellPos(columnid)let target = worksheet.getCell(letter + (rowid + 1))// console.log('1233', letter + (rowid + 1))for (const key in fill) {target.fill = fillbreak}target.font = fonttarget.alignment = alignmenttarget.value = valuereturn true})})
}var fillConvert = function(bg) {if (!bg) {return {}}// const bgc = bg.replace('#', '')let fill = {type: 'pattern',pattern: 'solid',fgColor: { argb: bg.replace('#', '') },}return fill
}var fontConvert = function(ff = 0, fc = '#000000', bl = 0, it = 0, fs = 10, cl = 0, ul = 0) {// luckysheet:ff(样式), fc(颜色), bl(粗体), it(斜体), fs(大小), cl(删除线), ul(下划线)const luckyToExcel = {0: '微软雅黑',1: '宋体(Song)',2: '黑体(ST Heiti)',3: '楷体(ST Kaiti)',4: '仿宋(ST FangSong)',5: '新宋体(ST Song)',6: '华文新魏',7: '华文行楷',8: '华文隶书',9: 'Arial',10: 'Times New Roman ',11: 'Tahoma ',12: 'Verdana',num2bl: function(num) {return num === 0 ? false : true},}// 出现Bug,导入的时候ff为luckyToExcel的vallet font = {name: typeof ff === 'number' ? luckyToExcel[ff] : ff,family: 1,size: fs,color: { argb: fc.replace('#', '') },bold: luckyToExcel.num2bl(bl),italic: luckyToExcel.num2bl(it),underline: luckyToExcel.num2bl(ul),strike: luckyToExcel.num2bl(cl),}return font
}var alignmentConvert = function(vt = 'default', ht = 'default', tb = 'default', tr = 'default') {// luckysheet:vt(垂直), ht(水平), tb(换行), tr(旋转)const luckyToExcel = {vertical: {0: 'middle',1: 'top',2: 'bottom',default: 'top',},horizontal: {0: 'center',1: 'left',2: 'right',default: 'left',},wrapText: {0: false,1: false,2: true,default: false,},textRotation: {0: 0,1: 45,2: -45,3: 'vertical',4: 90,5: -90,default: 0,},}let alignment = {vertical: luckyToExcel.vertical[vt],horizontal: luckyToExcel.horizontal[ht],wrapText: luckyToExcel.wrapText[tb],textRotation: luckyToExcel.textRotation[tr],}return alignment
}var borderConvert = function(borderType, style = 1, color = '#000') {// 对应luckysheet的config中borderinfo的的参数if (!borderType) {return {}}const luckyToExcel = {type: {'border-all': 'all','border-top': 'top','border-right': 'right','border-bottom': 'bottom','border-left': 'left',},style: {0: 'none',1: 'thin',2: 'hair',3: 'dotted',4: 'dashDot', // 'Dashed',5: 'dashDot',6: 'dashDotDot',7: 'double',8: 'medium',9: 'mediumDashed',10: 'mediumDashDot',11: 'mediumDashDotDot',12: 'slantDashDot',13: 'thick',},}let template = {style: luckyToExcel.style[style],color: { argb: color.replace('#', '') },}let border = {}if (luckyToExcel.type[borderType] === 'all') {border['top'] = templateborder['right'] = templateborder['bottom'] = templateborder['left'] = template} else {border[luckyToExcel.type[borderType]] = template}// console.log('border', border)return border
}function addborderToCell(borders, row_index, col_index) {let border = {}const luckyExcel = {type: {l: 'left',r: 'right',b: 'bottom',t: 'top',},style: {0: 'none',1: 'thin',2: 'hair',3: 'dotted',4: 'dashDot', // 'Dashed',5: 'dashDot',6: 'dashDotDot',7: 'double',8: 'medium',9: 'mediumDashed',10: 'mediumDashDot',11: 'mediumDashDotDot',12: 'slantDashDot',13: 'thick',},}// console.log('borders', borders)for (const bor in borders) {// console.log(bor)if (borders[bor].color.indexOf('rgb') === -1) {border[luckyExcel.type[bor]] = {style: luckyExcel.style[borders[bor].style],color: { argb: borders[bor].color.replace('#', '') },}} else {border[luckyExcel.type[bor]] = {style: luckyExcel.style[borders[bor].style],color: { argb: borders[bor].color },}}}return border
}function createCellPos(n) {let ordA = 'A'.charCodeAt(0)let ordZ = 'Z'.charCodeAt(0)let len = ordZ - ordA + 1let s = ''while (n >= 0) {s = String.fromCharCode((n % len) + ordA) + sn = Math.floor(n / len) - 1}return s
}

LuckyExcel.vue

<template><div><div class="control"><p>在线链接: <input type="text" v-model="fileURL" placeholder="输入在线地址" /><button @click="getOriginFile">预览链接文件</button></p><p>本地文件(.xlsx格式) :<input type="file" @change="previewLocal" /></p><p><button @click="exportFile">导出当前表格</button></p><p>没有做loading处理,请打开控制台查看打印信息</p></div><!-- 用于渲染表格的容器 --><div id="luckysheet"></div></div>
</template>
<script>
import axios from 'axios'
import { exportExcel } from '@/utils/exportExcel.js'
import LuckyExcel from "luckyexcel"
export default {name: 'LuckyExcel',data() {return {fileURL: ''}},created() {this.loadPlugins()},methods: {loadPlugins() {const baseURL = '//cdn.jsdelivr.net/npm/luckysheet@latest/dist'this.loading = truethis.tip = '正在加载依赖插件,请耐心等待...'Promise.all([this.asynLoad(`${baseURL}/plugins/css/pluginsCss.css`, true),this.asynLoad(`${baseURL}/plugins/plugins.css`, true),this.asynLoad(`${baseURL}/css/luckysheet.css`, true),this.asynLoad(`${baseURL}/assets/iconfont/iconfont.css`, true),this.asynLoad(`${baseURL}/plugins/js/plugin.js`),this.asynLoad(`${baseURL}/luckysheet.umd.js`),]).then(() => {luckysheet = window.luckysheetthis.getOriginFile() // 获取远端文件}).catch(res => {this.loading = falsethis.errStatus = 1this.errorTitle = '插件加载失败,请刷新后重试!'})},asynLoad(src, isCss = false) {return new Promise(res => {let elif (isCss) {el = document.createElement('link')el.rel = 'stylesheet'el.href = src} else {el = document.createElement('script')el.src = src}document.documentElement.appendChild(el)el.onload = el.onreadystatechange = function() {if (!this.readyState || this.readyState == 'loaded' || this.readyState == 'complete') {res(true)}this.onload = this.onreadystatechange = null}})},// 获取远程文件getOriginFile() {this.tip = '正在下载文件...'this.loading = trueaxios({url: this.fileURL,responseType: 'blob',}).then(({ data }) => {const blob = new Blob([data])const file = new File([blob], this.fileName)this.init(file) // 开始渲染}).catch(e => {this.errorTitle = '文件解析失败,请下载后使用 Excel 打开或点击重试!'this.errStatus = 2}).finally(() => {this.loading = false})},// 渲染方法,接受文件对象,如果是本地文件直接传入文件即可init(file) {luckysheet.destroy() // 先销毁当前容器LuckyExcel.transformExcelToLucky(file, exportJson => {if (exportJson.sheets === null || !exportJson.sheets.length) {this.$message.error('无法读取excel文件的内容,当前不支持xls文件!')return}luckysheet.create({container: 'luckysheet',showinfobar: false,lang: 'zh',data: exportJson.sheets,title: exportJson.info.name,userInfo: exportJson.info.name.creator,})})},previewLocal(e) {const file = e.target.files[0]if (!file) return console.log('没有选中文件')this.init(file)},exportFile() {exportExcel(luckysheet.getAllSheets(), 'fileName');}},
}
</script>
<style lang="scss" scoped>
#luckysheet {width: 80vw;height: 70vh;flex: 1;box-shadow: inset 0 0 3px #ccc;
}
.control {display: flex;align-items: center;justify-content: space-between;box-sizing: border-box;
}
.control p + p {margin-left: 10px;padding-left: 10px;border-left: 1px solid #ccc;
}
</style>

使用组件

<template>`
<LuckyExcel></LuckyExcel>
</template>
components: {LuckyExcel},

相关文章:

vue在线编辑表格导入导出

npm i file-saver npm i exceljs npm i luckyexcelindex.html &#xff08;方式一在html中引入&#xff09; <link relstylesheet hrefhttps://cdn.jsdelivr.net/npm/luckysheetlatest/dist/plugins/css/pluginsCss.css /><link relstylesheet hrefhttps://cdn.jsde…...

监控Kafka的关键指标

Kafka 架构 上面绿色部分 PRODUCER&#xff08;生产者&#xff09;和下面紫色部分 CONSUMER&#xff08;消费者&#xff09;是业务程序&#xff0c;通常由研发人员埋点解决监控问题&#xff0c;如果是 Java 客户端也会暴露 JMX 指标。组件运维监控层面着重关注蓝色部分的 BROKE…...

React18 hook学习笔记

useState useState用于在函数组件中声明和管理状态 它接受初始状态&#xff0c;并返回一个状态变量和一个更新状态的函数 通过调用更新状态的函数&#xff0c;可以改变状态的值并触发组件的重新渲染 import { useState } from "react"function App() {const [obj, …...

Java038——正则表达式

一、认识正则表达式 正则表达式通常被用于判断语句中&#xff0c;用来检查某一字符串是否满足某一格式。正则表达式是含有一些具有特殊意义字符的字符串&#xff0c;这些特殊字符称为正则表达式的元字符。例如&#xff0c;“\d”表示数字 0~9 中的任何一个&#xff0c;“d”就…...

JavaScript元素选择器

目录 一、getElementsByTagName1.说明2.用法示例 二、getElementsByName1.说明2.用法示例 三、getElementById1.说明2.用法示例 四、getElementsByClassName1.说明2.用法示例 五、querySelector1.说明2.用法示例 六、querySelectorAll1.说明2.用法示例 七、综合示例 一、getEle…...

Docker安装 elasticsearch-head

目录 前言安装elasticsearch-head步骤1&#xff1a;准备1. 安装docker2. 搜索可以使用的镜像。3. 也可从docker hub上搜索镜像。4. 选择合适的redis镜像。 步骤2&#xff1a;拉取elasticsearch-head镜像拉取镜像查看已拉取的镜像 步骤3&#xff1a;创建容器创建容器方式1&#…...

交换排序——选择排序和冒泡排序的区别是什么?

今天重温一下算法&#xff0c;其实刚开始我觉得冒泡排序和选择排序是一样的&#xff0c;因为他们排序过程中都是通过相邻的数据比较找到最小/最大的数据&#xff0c;通过不断思考和学习才明白&#xff0c;两者还是有区别的。 冒泡排序 概念 冒泡排序(Bubble Sort)&#xff0…...

吉他谱:Melodies of Life - Final Fantasy Solo Guitar Collections

原始出处&#xff1a; Final Fantasy Solo Guitar Collections - 南泽大介改编的最终幻想9主题曲吉他谱 更多吉他谱&#xff1a; https://github.com/NaisuXu/Guitar_Sheet_Music_Collection...

微信小程序下拉刷新

小程序中的下拉刷新 - 掘金...

TX2 NX 修改设备树--GPIO

确认模组内使用的是哪个设备树文件 模组上电输入如下指令,查看返回值:cat /proc/device-tree/nvidia,dtsfilename找到相应的设备树文件设备树存放路径 /public_sources/Linux_for_Tegra/source/public/kernel_src/hardware/nvidia/platform/t18x/lanai/kernel-dts 确认设备树…...

.NET对象的内存布局

在.NET中&#xff0c;理解对象的内存布局是非常重要的&#xff0c;这将帮助我们更好地理解.NET的运行机制和优化代码&#xff0c;本文将介绍.NET中的对象内存布局。 .NET中的数据类型主要分为两类&#xff0c;值类型和引用类型。值类型包括了基本类型(如int、bool、double、cha…...

Hybrid App 可以从哪些技术路径实现性能优化

说到 Hybrid App&#xff08;混合应用&#xff09;大家都不陌生&#xff0c;因为这种开发模式大行其道发展的这些年取代了很多原生和 Web 应用&#xff0c;为什么大家对这种「Native HTML5」的开发模式额外偏爱呢&#xff1f; 因为一方面在一定程度上兼顾了原生应用的优质体验…...

C++入门篇7---string类

所谓的string类&#xff0c;其实就是我们所说的字符串&#xff0c;本质和c语言中的字符串数组一样&#xff0c;但是为了更符合C面向对象的特性&#xff0c;特地将它写成了一个单独的类&#xff0c;方便我们的使用 对其定义有兴趣的可以去看string类的文档介绍&#xff0c;这里…...

2308d的静态构造函数循环依赖示例

原文 //Steve: __gshared string[string] dict; shared static this() {dict ["a" : "b"]; }这里有两个论点:这不能是CRT构造器,因为它依赖于D运行时,并且认为它应该进入自己的模块是一个QoL问题,当你想要私有到类而不是私有到模块时,可为类提供它,因为语…...

Linux 目录和文件常见操作

就常见的命令&#xff1a; pwd pwd 显示当前的目录 目录迁移 我以如下的目录大致结构做一个简单的例子 cd 迁移到指定的路径&#xff0c;可以指定相对路径和绝对路径&#xff0c;默认相对 .指向当前路径&#xff0c;…/ 指向上一级的目录。 ls 列出文件及其目录 命令选…...

不基于比较的排序:基数排序

本篇只是讨论桶排序的具体实现&#xff0c;想了解更多算法内容可以在我的博客里搜&#xff0c;建议大家看看这篇排序算法总结&#xff1a;排序算法总结_鱼跃鹰飞的博客-CSDN博客 桶排序的原理&#xff1a; 代码&#xff1a;sort1是一个比较二逼的实现方式浪费空间&#xff0c;s…...

shell和反弹shell

文章目录 是什么&#xff1f;bash是什么&#xff1f;反弹shell 是什么&#xff1f; Shell 是一个用 C 语言编写的程序&#xff0c;它是用户使用 Linux 的桥梁。Shell 既是一种命令语言&#xff0c;又是一种程序设计语言。 Shell 是指一种应用程序&#xff0c;这个应用程序提供了…...

构建Docker容器监控系统(Cadvisor +Prometheus+Grafana)

Cadvisor PrometheusGrafana 1.1、Cadvisor产品简介 Cadvisor是Google开源的一款用于展示和分析容器运行状态的可视化工具。通过在主机上运行Cadvisor用户可以轻松的获取到当前主机上容器的运行统计信息&#xff0c;并以图表的形式向用户展示。 1.2、安装docker-ce [rootloc…...

java实现文件的下载

系统日志的获取不可能每次都登录服务器&#xff0c;所以在页面上能够下载系统运行的日志是必须的 如何来实现日志的下载&#xff0c;这样的一个功能 前端我们用到的是window.open(...)这样可以发送一个get请求到后台 后台接收到get请求之后&#xff0c;如何实现对文件的下载 R…...

分享Python技术下AutojsPro7云控代码

引言 有图有真相&#xff0c;那短视频就更是真相了。下面是三大语言的短视频。 Java源码版云控示例&#xff1a; Java源码版云控示例在线视频 Net源码版云控示例&#xff1a; Net源码版云控示例在线视频亚丁号-知识付费平台 支付后可见 扫码付费可见 Python源码版云控示例…...

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…...

变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析

一、变量声明设计&#xff1a;let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性&#xff0c;这种设计体现了语言的核心哲学。以下是深度解析&#xff1a; 1.1 设计理念剖析 安全优先原则&#xff1a;默认不可变强制开发者明确声明意图 let x 5; …...

idea大量爆红问题解决

问题描述 在学习和工作中&#xff0c;idea是程序员不可缺少的一个工具&#xff0c;但是突然在有些时候就会出现大量爆红的问题&#xff0c;发现无法跳转&#xff0c;无论是关机重启或者是替换root都无法解决 就是如上所展示的问题&#xff0c;但是程序依然可以启动。 问题解决…...

装饰模式(Decorator Pattern)重构java邮件发奖系统实战

前言 现在我们有个如下的需求&#xff0c;设计一个邮件发奖的小系统&#xff0c; 需求 1.数据验证 → 2. 敏感信息加密 → 3. 日志记录 → 4. 实际发送邮件 装饰器模式&#xff08;Decorator Pattern&#xff09;允许向一个现有的对象添加新的功能&#xff0c;同时又不改变其…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序

一、开发环境准备 ​​工具安装​​&#xff1a; 下载安装DevEco Studio 4.0&#xff08;支持HarmonyOS 5&#xff09;配置HarmonyOS SDK 5.0确保Node.js版本≥14 ​​项目初始化​​&#xff1a; ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...

现代密码学 | 椭圆曲线密码学—附py代码

Elliptic Curve Cryptography 椭圆曲线密码学&#xff08;ECC&#xff09;是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础&#xff0c;例如椭圆曲线数字签…...

【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)

🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...

Android15默认授权浮窗权限

我们经常有那种需求&#xff0c;客户需要定制的apk集成在ROM中&#xff0c;并且默认授予其【显示在其他应用的上层】权限&#xff0c;也就是我们常说的浮窗权限&#xff0c;那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...

Web后端基础(基础知识)

BS架构&#xff1a;Browser/Server&#xff0c;浏览器/服务器架构模式。客户端只需要浏览器&#xff0c;应用程序的逻辑和数据都存储在服务端。 优点&#xff1a;维护方便缺点&#xff1a;体验一般 CS架构&#xff1a;Client/Server&#xff0c;客户端/服务器架构模式。需要单独…...

掌握 HTTP 请求:理解 cURL GET 语法

cURL 是一个强大的命令行工具&#xff0c;用于发送 HTTP 请求和与 Web 服务器交互。在 Web 开发和测试中&#xff0c;cURL 经常用于发送 GET 请求来获取服务器资源。本文将详细介绍 cURL GET 请求的语法和使用方法。 一、cURL 基本概念 cURL 是 "Client URL" 的缩写…...