Vue3項目中通過LuckySheet實現(xiàn)Excel在線編輯功能
一、場景
在實現(xiàn)Excel文件導入時,領導要求實現(xiàn)在前端導入文件后,不調(diào)用后端的接口,而是直接顯示excel文件的內(nèi)容,等待用戶修改完以后,再調(diào)用后端接口進行文件的提交。在這種應用場景下,使用了LuckySheet進行改功能的實現(xiàn)。
二、Vue3中的基本使用
1、引入luckySheet
引入方式有兩種,分別是CDN引入與本地引入,下面都會做介紹:
(1)CDN引入
在你的index.html中復制下面這段代碼,實現(xiàn)CDN引入。
<link rel='stylesheet' /> <link rel='stylesheet' /> <link rel='stylesheet' /> <link rel='stylesheet' /> <script src="https://cdn.jsdelivr.net/npm/luckysheet/dist/plugins/js/plugin.js"></script> <script src="https://cdn.jsdelivr.net/npm/luckysheet/dist/luckysheet.umd.js"></script>
注意,https://cdn.jsdelivr.net/npm/luckysheet/dist/luckysheet.umd.js這個路徑意思是會拉取到最新的luckysheet代碼,但是如果Luckysheet剛剛發(fā)布,jsdelivr網(wǎng)站可能還沒來得及從npm上同步過去,故而使用這個路徑還是會拉到上一個版本,我們推薦您直接指定最新版本。
想要指定Luckysheet版本,請在所有的CDN依賴文件后面加上版本號,如:https://cdn.jsdelivr.net/npm/luckysheet@2.1.12/dist/luckysheet.umd.js。
如何知道最新版本是哪一版?查看最新 release記錄 (opens new window)或者 package.json (opens new window)的
version字段。
如果不方便訪問 jsdelivr.net,還可以采用本地方式引入。
(2)本地引入
本地引入是將CDN引入的文件都放在本地文件中
第一步:克隆或者下載下面的代碼
git clone https://github.com/dream-num/Luckysheet.git
第二步:安裝依賴
npm install npm install gulp -g
第三步:運行查看
npm run dev
運行效果

第四步:打包
npm run build
第五步:本地引入
打包執(zhí)行成功后,在文件夾目錄下會出現(xiàn)dis文件夾,如下圖所示:

把dist文件夾中的代碼全部復制粘貼到你項目的public文件夾中,index.html文件除外。
在你項目的index.html文件中引入如下代碼,如果你復制的位置是其他地方,需要用絕對路徑引入這些文件。
<link rel='stylesheet' href='./public/plugins/css/pluginsCss.css' /> <link rel='stylesheet' href='./public/plugins/plugins.css' /> <link rel='stylesheet' href='./public/css/luckysheet.css' /> <link rel='stylesheet' href='./public/assets/iconfont/iconfont.css' /> <script src="./public/plugins/js/plugin.js"></script> <script src="./public/luckysheet.umd.js"></script>
2、在頁面中進行展示
(1)指定一個容器
<div id="luckysheet" class="luckysheet-wrap"></div>
<style scoped>
.luckysheet-wrap {
margin: 0px;
padding: 0px;
position: absolute;
width: 100%;
height: 100%;
left: 0px;
top: 0px;
}
</style>
(2)創(chuàng)建一個表格
onMounted(() => {
//如果這里luckysheet.create報錯
//請使用 window.luckysheet.create
luckysheet.create({
container: 'luckysheet'//這里需要和容器的id名稱一致
})
})
三、Excel文件的導入與導出
1、導入Excel文件
安裝luckyexcel
npm install luckyexcel --save
準備一個導入按鈕,也可以使用elementUI的導入組件
這邊準備了一個導入按鈕
<input id="uploadBtn" type="file" @change="loadExcel" />
const loadExcel = (evt) => {
const files = evt.target.files
if (files == null || files.length == 0) {
alert('請上傳文件')
return
}
let name = files[0].name
let suffixArr = name.split('.'),
suffix = suffixArr[suffixArr.length - 1]
if (suffix != 'xlsx') {
alert('只能導入xlsx文件格式的Excel')
return
}
LuckyExcel.transformExcelToLucky(files[0], function (exportJson, luckysheetfile) {
if (exportJson.sheets == null || exportJson.sheets.length == 0) {
alert('導入失敗!')
return
}
window.luckysheet.destroy()
window.luckysheet.create({
container: 'luckysheet', //容器的Id
showinfobar: false,
data: exportJson.sheets,
title: exportJson.info.name,
userInfo: exportJson.info.name.creator
})
})
}
2、導出Excel
導出需要用到excejs和file-saver
//安裝exceljs npm i exceljs --save //安裝文件保存的庫 npm i file-saver --save
這里附上LuckySheet官網(wǎng)提供的導出函數(shù),直接在項目中新建export.js使用
// import { createCellPos } from './translateNumToLetter'
import Excel from 'exceljs'
import FileSaver from 'file-saver'
const exportExcel = function(luckysheet, value) {
// 參數(shù)為luckysheet.getluckysheetfile()獲取的對象
// 1.創(chuàng)建工作簿,可以為工作簿添加屬性
const workbook = new Excel.Workbook()
// 2.創(chuàng)建表格,第二個參數(shù)可以配置創(chuàng)建什么樣的工作表
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.寫入 buffer
const buffer = workbook.xlsx.writeBuffer().then(data => {
// console.log('data', data)
const blob = new Blob([data], {
type: 'application/vnd.ms-excel;charset=utf-8'
})
console.log("導出成功!")
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) {
// 現(xiàn)在只兼容到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.row
let column = rang.column
for (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.value
const borderData = Object.assign({}, elem.value)
delete borderData.col_index
delete borderData.row_index
let 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)) return
cellArr.forEach(function(row, rowid) {
row.every(function(cell, columnid) {
if (!cell) return true
let 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之后,內(nèi)部存在不同的格式,都會進到富文本里,即值不存在與cell.v,而是存在于cell.ct.s之后
// value = cell.ct.s[0].v
cell.ct.s.forEach(arr => {
value += arr.v
})
} else {
value = cell.v
}
// style 填入到_value中可以實現(xiàn)填充色
let letter = createCellPos(columnid)
let target = worksheet.getCell(letter + (rowid + 1))
// console.log('1233', letter + (rowid + 1))
for (const key in fill) {
target.fill = fill
break
}
target.font = font
target.alignment = alignment
target.value = value
return 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
}
}
// 出現(xiàn)Bug,導入的時候ff為luckyToExcel的val
let 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的的參數(shù)
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'] = template
border['right'] = template
border['bottom'] = template
border['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 + 1
let s = ''
while (n >= 0) {
s = String.fromCharCode((n % len) + ordA) + s
n = Math.floor(n / len) - 1
}
return s
}
export {
exportExcel
}
在頁面中引入export.js文件
import { exportExcel } from './export'
在頁面中使用下面這句代碼進行導出
exportExcel(luckysheet.getAllSheets(), '導出的文件名字')
四、注意事項
在導出文件時,如果需要將導出的數(shù)據(jù)通過接口傳輸給后端,需要進行文件格式的轉換,以file為例。
//獲取buffer的
workbook.xlsx.writeBuffer()
.then((arrayBuffer) => {
const blob = new Blob(arrayBuffer, {
//導出為xlsx文件格式
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
});
//轉為file格式
let file = new File([blob], '導出.xlsx', {type: blob.type});
/**
這里書寫接口上傳代碼
*/
}).catch(err=>{
dialogVisible.value = false
})
LuckySheet現(xiàn)在升級為Univer Docs,如果需要使用Univer Docs進行展示excel,可以參考另一篇文章進行了解。
以上就是Vue3項目中通過LuckySheet實現(xiàn)Excel在線編輯的詳細內(nèi)容,更多關于Vue3 LuckySheet在線編輯Excel的資料請關注腳本之家其它相關文章!
相關文章
vue項目中頁面底部出現(xiàn)白邊及空白區(qū)域錯誤的問題
這篇文章主要介紹了vue項目中頁面底部出現(xiàn)白邊及空白區(qū)域錯誤的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-08-08
基于element-ui封裝可搜索的懶加載tree組件的實現(xiàn)
這篇文章主要介紹了基于element-ui封裝可搜索的懶加載tree組件的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-05-05
Vue3 defineProps設置默認值報錯問題及解決過程
這篇文章主要介紹了Vue3 defineProps設置默認值報錯問題及解決過程,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2025-08-08
vue內(nèi)置組件keep-alive事件動態(tài)緩存實例
這篇文章主要介紹了vue內(nèi)置組件keep-alive事件動態(tài)緩存實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-10-10
vue跳轉同一個組件,參數(shù)不同,頁面接收值只接收一次的解決方法
今天小編就為大家分享一篇vue跳轉同一個組件,參數(shù)不同,頁面接收值只接收一次的解決方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-11-11
Vue組件內(nèi)部實現(xiàn)一個雙向數(shù)據(jù)綁定的實例代碼
這篇文章主要介紹了Vue組件內(nèi)部實現(xiàn)一個雙向數(shù)據(jù)綁定的實例代碼,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下2019-04-04

