vue3+luckysheet實現在線編輯Excel的項目實踐
更新時間:2025年07月01日 09:55:43 作者:Passerby_K
本文介紹了使用Luckysheet實現在線Excel表格功能,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
效果圖奉上:
引入的依賴:
"dependencies": { "@types/jquery": "^3.5.32", "@types/xlsx": "^0.0.36", "jquery": "^3.7.1", "xlsx": "^0.18.5", }
在index.html中引入:
<!-- Luckysheet CSS --> <link rel="stylesheet" rel="external nofollow" /> <link rel="stylesheet" rel="external nofollow" /> <link rel="stylesheet" rel="external nofollow" /> <link rel="stylesheet" rel="external nofollow" /> <!-- jQuery 和 Luckysheet 的JS CDN --> <script src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js"></script> <!-- jQuery mousewheel 插件 --> <script src="https://cdn.jsdelivr.net/npm/jquery-mousewheel@3.1.13/jquery.mousewheel.min.js"></script> <!-- XLSX 庫 --> <script src="https://cdn.jsdelivr.net/npm/xlsx@0.18.5/dist/xlsx.full.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/luckysheet/dist/plugins/js/plugin.js"></script> <script src="https://cdn.jsdelivr.net/npm/luckysheet@2.1.13/dist/luckysheet.umd.js"></script>
完整代碼塊
<template> <div class="luckysheet-container"> <!-- 工具欄 --> <div class="toolbar"> <el-button type="primary" @click="importExcel"> <el-icon> <Upload /> </el-icon> 導入Excel </el-button> <el-button type="success" @click="exportExcel"> <el-icon> <Download /> </el-icon> 導出Excel </el-button> <el-button type="warning" @click="clearData"> <el-icon> <Delete /> </el-icon> 清空數據 </el-button> <el-button type="info" @click="addSheet"> <el-icon> <Plus /> </el-icon> 添加工作表 </el-button> <el-button type="success" @click="getData"> <el-icon> <Document /> </el-icon> 保存 </el-button> <!-- <el-button type="info" @click="printSheet"> <el-icon> <Document /> </el-icon> 打印 </el-button> --> </div> <!-- 隱藏的文件輸入框 --> <input ref="fileInput" type="file" accept=".xlsx,.xls" style="display: none" @change="handleFileChange" /> <!-- Luckysheet容器 --> <div id="luckysheet" class="luckysheet-wrapper" ref="luckysheetRef"></div> </div> </template> <script setup lang="ts"> declare global { interface Window { luckysheet: any; XLSX: any; $: any; jQuery: any; } } import { ref, onMounted, onUnmounted } from 'vue' import { ElMessage, ElMessageBox } from 'element-plus' import { Upload, Download, Delete, Plus, Document } from '@element-plus/icons-vue' // 響應式數據 const fileInput = ref<HTMLInputElement>() let luckysheetInstance: any = null const luckysheetRef = ref<HTMLDivElement>() // 默認數據 const defaultData = [ { name: 'Sheet1', color: '', index: 0, status: 1, order: 0, hide: 0, row: 36, column: 18, defaultRowHeight: 19, defaultColWidth: 73, celldata: [ { r: 0, c: 0, v: { v: '歡迎使用Luckysheet在線編輯器', ct: { fa: 'General', t: 'g' }, m: '歡迎使用Luckysheet在線編輯器', bg: '#f4f5f8', bl: 1, it: 0, ff: 0, fs: 14, fc: '#000000', cl: 0, un: 0, vt: 0 } }, { r: 1, c: 0, v: { v: '這是一個示例表格', ct: { fa: 'General', t: 'g' }, m: '這是一個示例表格', bg: '#ffffff', bl: 0, it: 0, ff: 0, fs: 12, fc: '#000000', cl: 0, un: 0, vt: 0 } } ] as any[], config: {}, scrollLeft: 0, scrollTop: 0, luckysheet_select_save: [], calcChain: [], isPivotTable: false, pivotTable: {}, filter_select: {}, filter: null, luckysheet_alternateformat_save: [], luckysheet_alternateformat_save_modelCustom: [], luckysheet_conditionformat_save: {}, frozen: {}, chart: [], zoomRatio: 1, image: [], showGridLines: 1, dataVerification: {} } ] // 初始化Luckysheet const initLuckysheet = (): Promise<boolean> => { return new Promise((resolve) => { console.log('開始初始化Luckysheet...') console.log('window.luckysheet:', window.luckysheet) // 檢查容器是否存在 const container = document.getElementById('luckysheet') console.log('容器元素:', container) if (!container) { console.error('找不到luckysheet容器') resolve(false) return } // 清空容器 container.innerHTML = '' const options = { container: 'luckysheet', title: '在線Excel編輯器', lang: 'zh', data: defaultData, showinfobar: true, showsheetbar: true, showstatisticBar: true, enableAddRow: true, enableAddCol: true, userInfo: false, myFolderUrl: '', showtoolbar: true, showtoolbarConfig: { // 隱藏Luckysheet自帶打印按鈕 print: false }, hook: { cellEditBefore: (r: number, c: number, value: any) => { console.log('編輯前:', r, c, value) return value }, cellEditAfter: (r: number, c: number, oldValue: any, newValue: any) => { console.log('編輯后:', r, c, oldValue, newValue) }, cellUpdated: (r: number, c: number, oldValue: any, newValue: any) => { console.log('單元格更新:', r, c, oldValue, newValue) } } } try { console.log('創(chuàng)建Luckysheet實例...') console.log('使用的配置:', options) // 直接調用全局方法 window.luckysheet.create(options) // 檢查是否創(chuàng)建成功 setTimeout(() => { const sheets = window.luckysheet.getAllSheets() console.log('初始化后獲取到的sheets:', sheets) if (sheets && sheets.length > 0) { console.log('Luckysheet初始化成功') luckysheetInstance = window.luckysheet // 使用全局對象作為實例 resolve(true) } else { console.error('Luckysheet初始化失敗,沒有獲取到sheets') resolve(false) } }, 1000) } catch (error) { console.error('創(chuàng)建Luckysheet實例失敗:', error) resolve(false) } }) } // 等待Luckysheet加載 const waitForLuckysheet = (maxAttempts = 10): Promise<boolean> => { return new Promise((resolve) => { let attempts = 0 const checkLuckysheet = () => { attempts++ console.log(`檢查Luckysheet加載狀態(tài) (${attempts}/${maxAttempts})`) if (window.luckysheet && typeof window.luckysheet.create === 'function') { console.log('Luckysheet已加載完成') resolve(true) } else if (attempts >= maxAttempts) { console.error('Luckysheet加載超時') resolve(false) } else { setTimeout(checkLuckysheet, 500) } } checkLuckysheet() }) } // 導入Excel文件 const importExcel = () => { console.log('importExcel被調用') console.log('window.XLSX:', window.XLSX) // 檢查XLSX庫是否可用 if (!window.XLSX || !window.XLSX.utils) { ElMessage.error('XLSX庫未加載,請刷新頁面重試') return } fileInput.value?.click() } // 處理文件選擇 const handleFileChange = async (event: Event) => { const target = event.target as HTMLInputElement const file = target.files?.[0] if (!file) return console.log('選擇的文件:', file.name, file.size) try { ElMessage.info('正在解析Excel文件...') const data = await parseExcelFile(file) console.log('解析到的數據:', data) if (data && data.length > 0) { // 直接進行重新初始化,不再檢查可用方法 console.log('開始加載數據到Luckysheet...') console.log('解析到的數據:', data) try { // 直接使用重新初始化的方式,避免transToData的錯誤 console.log('跳過transToData方法,直接重新初始化...') ElMessage.info('正在加載Excel數據...') const initSuccess = await initLuckysheetWithData(data) if (initSuccess) { // 初始化成功后,檢查導入結果 setTimeout(() => { try { const sheets = window.luckysheet.getAllSheets() console.log('導入完成,檢查結果:') console.log('- 工作表數量:', sheets.length) sheets.forEach((sheet: any, index: number) => { console.log(`- 工作表 ${index}: ${sheet.name}`) console.log(` - 行數: ${sheet.row}`) console.log(` - 列數: ${sheet.column}`) console.log(` - 單元格數據: ${sheet.celldata ? sheet.celldata.length : 0} 個`) if (sheet.config && sheet.config.merge) { console.log(` - 合并單元格: ${sheet.config.merge.length} 個`) } }) // 顯示成功消息 const totalCells = sheets.reduce((total: number, sheet: any) => { return total + (sheet.celldata ? sheet.celldata.length : 0) }, 0) // 生成詳細的導入報告 const importReport = { totalSheets: sheets.length, totalCells, sheets: sheets.map((sheet: any) => ({ name: sheet.name, cells: sheet.celldata ? sheet.celldata.length : 0, rows: sheet.row, columns: sheet.column })) } console.log('導入報告:', importReport) // 顯示詳細成功消息 const sheetDetails = importReport.sheets.map(s => `${s.name}(${s.cells}個單元格)` ).join('、') ElMessage.success(`Excel文件導入成功!共導入 ${importReport.totalSheets} 個工作表,${importReport.totalCells} 個單元格數據`) console.log(`工作表詳情: ${sheetDetails}`) // 顯示合并單元格檢測信息 if (data.some((sheet: any) => sheet.config && sheet.config.merge && sheet.config.merge.length > 0)) { const mergeInfo = data .filter((sheet: any) => sheet.config && sheet.config.merge && sheet.config.merge.length > 0) .map((sheet: any) => `${sheet.name}(${sheet.config.merge.length}個)`) .join('、') console.log(`檢測到合并單元格: ${mergeInfo}`) ElMessage.info(`注意:檢測到合并單元格但暫時未應用,以避免顯示錯誤`) } } catch (error) { console.error('檢查導入結果時出錯:', error) ElMessage.success('Excel文件導入成功!') } }, 500) } else { ElMessage.error('表格初始化失敗,請刷新頁面重試') } } catch (loadError) { console.error('加載數據失敗:', loadError) ElMessage.error('加載數據失敗: ' + (loadError instanceof Error ? loadError.message : '未知錯誤')) } } else { ElMessage.warning('Excel文件為空或格式不正確') } } catch (error) { console.error('導入Excel失敗:', error) const errorMessage = error instanceof Error ? error.message : '未知錯誤' ElMessage.error('導入Excel失敗: ' + errorMessage) } target.value = '' } // 使用新數據初始化Luckysheet const initLuckysheetWithData = (data: any[]): Promise<boolean> => { return new Promise((resolve) => { console.log('開始使用新數據初始化Luckysheet...') console.log('新數據:', data) // 檢查容器是否存在 const container = document.getElementById('luckysheet') console.log('容器元素:', container) if (!container) { console.error('找不到luckysheet容器') resolve(false) return } // 清空容器 container.innerHTML = '' // 驗證和清理數據 const cleanData = data.map((sheet: any, index: number) => { console.log(`清理工作表 ${index}:`, sheet.name) // 確保必要的字段存在 const cleanSheet = { name: sheet.name || `Sheet${index + 1}`, color: sheet.color || '', index: sheet.index || index, status: sheet.status || 1, order: sheet.order || index, hide: sheet.hide || 0, row: Math.max(sheet.row || 36, 36), column: Math.max(sheet.column || 18, 18), defaultRowHeight: sheet.defaultRowHeight || 19, defaultColWidth: sheet.defaultColWidth || 73, celldata: Array.isArray(sheet.celldata) ? sheet.celldata : [], config: sheet.config || {}, scrollLeft: sheet.scrollLeft || 0, scrollTop: sheet.scrollTop || 0, luckysheet_select_save: sheet.luckysheet_select_save || [], calcChain: sheet.calcChain || [], isPivotTable: sheet.isPivotTable || false, pivotTable: sheet.pivotTable || {}, filter_select: sheet.filter_select || {}, filter: sheet.filter || null, luckysheet_alternateformat_save: sheet.luckysheet_alternateformat_save || [], luckysheet_alternateformat_save_modelCustom: sheet.luckysheet_alternateformat_save_modelCustom || [], luckysheet_conditionformat_save: sheet.luckysheet_conditionformat_save || {}, frozen: sheet.frozen || {}, chart: sheet.chart || [], zoomRatio: sheet.zoomRatio || 1, image: sheet.image || [], showGridLines: sheet.showGridLines || 1, dataVerification: sheet.dataVerification || {} } // 暫時禁用合并單元格處理以避免內部錯誤 if (cleanSheet.config.merge) { console.log(`工作表 ${index} 移除合并單元格配置以避免內部錯誤`) delete cleanSheet.config.merge } console.log(`清理后的工作表 ${index}:`, cleanSheet) return cleanSheet }) const options = { container: 'luckysheet', title: '在線Excel編輯器', lang: 'zh', data: cleanData, showinfobar: true, showsheetbar: true, showstatisticBar: true, enableAddRow: true, enableAddCol: true, userInfo: false, myFolderUrl: '', showtoolbar: true, showtoolbarConfig: { // 隱藏Luckysheet自帶打印按鈕 print: false }, hook: { cellEditBefore: (r: number, c: number, value: any) => { console.log('編輯前:', r, c, value) return value }, cellEditAfter: (r: number, c: number, oldValue: any, newValue: any) => { console.log('編輯后:', r, c, oldValue, newValue) }, cellUpdated: (r: number, c: number, oldValue: any, newValue: any) => { console.log('單元格更新:', r, c, oldValue, newValue) } } } try { console.log('使用新數據創(chuàng)建Luckysheet實例...') console.log('使用的配置:', options) // 直接調用全局方法 window.luckysheet.create(options) // 檢查是否創(chuàng)建成功 setTimeout(() => { try { const sheets = window.luckysheet.getAllSheets() console.log('重新初始化后獲取到的sheets:', sheets) if (sheets && sheets.length > 0) { console.log('Luckysheet重新初始化成功') luckysheetInstance = window.luckysheet // 使用全局對象作為實例 resolve(true) } else { console.error('Luckysheet重新初始化失敗,沒有獲取到sheets') resolve(false) } } catch (error) { console.error('檢查初始化結果時出錯:', error) // 即使有錯誤,如果容器中有內容,也認為初始化成功 if (container.children.length > 0) { console.log('容器中有內容,認為初始化成功') luckysheetInstance = window.luckysheet resolve(true) } else { resolve(false) } } }, 1000) } catch (error) { console.error('使用新數據創(chuàng)建Luckysheet實例失敗:', error) resolve(false) } }) } // 解析Excel文件 const parseExcelFile = (file: File): Promise<any[]> => { return new Promise((resolve, reject) => { console.log('開始解析Excel文件...') console.log('文件信息:', { name: file.name, size: file.size, type: file.type, lastModified: file.lastModified }) // 檢查XLSX庫是否可用 if (!window.XLSX || !window.XLSX.utils) { console.error('XLSX庫未加載') reject(new Error('XLSX庫未加載')) return } // 檢查文件類型 const validTypes = [ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', // .xlsx 'application/vnd.ms-excel', // .xls 'application/octet-stream' // 某些系統(tǒng)可能顯示為這個類型 ] if (!validTypes.includes(file.type) && !file.name.match(/\.(xlsx|xls)$/i)) { console.error('不支持的文件類型:', file.type) reject(new Error('不支持的文件類型,請選擇.xlsx或.xls文件')) return } const reader = new FileReader() reader.onload = (e) => { try { console.log('文件讀取完成,開始解析...') const result = e.target?.result console.log('讀取結果類型:', typeof result) if (!result) { reject(new Error('文件讀取結果為空')) return } const data = new Uint8Array(result as ArrayBuffer) console.log('轉換為Uint8Array,長度:', data.length) // 嘗試不同的解析方式 let workbook try { workbook = window.XLSX.read(data, { type: 'array', cellStyles: true }) } catch (readError) { console.error('使用array類型解析失敗,嘗試binary類型:', readError) try { workbook = window.XLSX.read(data, { type: 'binary', cellStyles: true }) } catch (binaryError) { console.error('使用binary類型解析也失敗:', binaryError) reject(new Error('無法解析Excel文件,請檢查文件格式')) return } } console.log('工作簿解析成功:', workbook) console.log('工作表名稱:', workbook.SheetNames) if (!workbook.SheetNames || workbook.SheetNames.length === 0) { reject(new Error('Excel文件中沒有找到工作表')) return } const sheets = workbook.SheetNames.map((sheetName: string, index: number) => { console.log(`處理工作表: ${sheetName}`) const worksheet = workbook.Sheets[sheetName] if (!worksheet) { console.warn(`工作表 ${sheetName} 為空`) return { name: sheetName, color: '', index, status: 1, order: index, hide: 0, row: 36, column: 18, defaultRowHeight: 19, defaultColWidth: 73, celldata: [], config: {}, scrollLeft: 0, scrollTop: 0, luckysheet_select_save: [], calcChain: [], isPivotTable: false, pivotTable: {}, filter_select: {}, filter: null, luckysheet_alternateformat_save: [], luckysheet_alternateformat_save_modelCustom: [], luckysheet_conditionformat_save: {}, frozen: {}, chart: [], zoomRatio: 1, image: [], showGridLines: 1, dataVerification: {} } } // 獲取單元格范圍 const range = window.XLSX.utils.decode_range(worksheet['!ref'] || 'A1') console.log(`工作表 ${sheetName} 范圍:`, range) const celldata: any[] = [] const merges: any[] = [] // 處理合并單元格 - 暫時禁用以避免mergeCalculation錯誤 if (worksheet['!merges']) { console.log(`工作表 ${sheetName} 檢測到合并單元格:`, worksheet['!merges'].length, '個') console.log('注意:合并單元格已識別但暫時禁用以避免內部錯誤') // 暫時注釋掉合并單元格處理,但保留識別信息 /* worksheet['!merges'].forEach((merge: any, mergeIndex: number) => { try { // 確保合并單元格數據格式正確 const mergeData = { r: merge.s.r, c: merge.s.c, rs: merge.e.r - merge.s.r + 1, cs: merge.e.c - merge.s.c + 1 } // 驗證合并單元格數據 if (mergeData.r >= 0 && mergeData.c >= 0 && mergeData.rs > 0 && mergeData.cs > 0 && mergeData.r + mergeData.rs <= range.e.r + 1 && mergeData.c + mergeData.cs <= range.e.c + 1) { merges.push(mergeData) console.log(`合并單元格 ${mergeIndex}:`, mergeData) } else { console.warn(`跳過無效的合并單元格 ${mergeIndex}:`, mergeData) } } catch (mergeError) { console.warn(`處理合并單元格 ${mergeIndex} 時出錯:`, mergeError) } }) */ } // 遍歷所有單元格 for (let r = range.s.r; r <= range.e.r; r++) { for (let c = range.s.c; c <= range.e.c; c++) { const cellAddress = window.XLSX.utils.encode_cell({ r, c }) const cell = worksheet[cellAddress] if (cell) { const cellData: any = { r, c, v: { v: cell.v, ct: { fa: 'General', t: 'g' }, m: String(cell.v), // 只有有背景色時才加bg字段 ...(cell.s && cell.s.fill && cell.s.fill.fgColor && cell.s.fill.fgColor.rgb ? { bg: '#' + cell.s.fill.fgColor.rgb.substring(2) } : {}), bl: 0, it: 0, ff: 0, fs: 10, fc: '#000000', cl: 0, un: 0, vt: 0 } } // 處理單元格格式 if (cell.s) { const style = cell.s // 字體格式 if (style.font) { if (style.font.bold) cellData.v.bl = 1 if (style.font.italic) cellData.v.it = 1 if (style.font.size) cellData.v.fs = style.font.size if (style.font.color) { const color = style.font.color if (color.rgb) { cellData.v.fc = '#' + color.rgb.substring(2) } } } // 背景色 if (style.fill) { if (style.fill.fgColor) { const bgColor = style.fill.fgColor if (bgColor.rgb) { cellData.v.bg = '#' + bgColor.rgb.substring(2) } } } // 對齊方式 if (style.alignment) { const alignment = style.alignment if (alignment.horizontal) { switch (alignment.horizontal) { case 'left': cellData.v.ff = 0 break case 'center': cellData.v.ff = 1 break case 'right': cellData.v.ff = 2 break } } if (alignment.vertical) { switch (alignment.vertical) { case 'top': cellData.v.vt = 0 break case 'middle': cellData.v.vt = 1 break case 'bottom': cellData.v.vt = 2 break } } } // 邊框 if (style.border) { const border = style.border if (border.top || border.bottom || border.left || border.right) { cellData.v.cl = 1 } } } // 處理數字格式 if (cell.t === 'n' && cell.z) { cellData.v.ct = { fa: cell.z, t: 'n' } } else if (cell.t === 'd') { cellData.v.ct = { fa: 'yyyy-mm-dd', t: 'd' } } else if (cell.t === 'b') { cellData.v.ct = { fa: 'General', t: 'b' } } celldata.push(cellData) } } } console.log(`工作表 ${sheetName} 轉換后的celldata:`, celldata) console.log(`工作表 ${sheetName} 合并單元格:`, merges) // 驗證數據完整性 if (celldata.length === 0) { console.warn(`工作表 ${sheetName} 沒有數據,添加默認單元格`) celldata.push({ r: 0, c: 0, v: { v: '', ct: { fa: 'General', t: 'g' }, m: '', bg: '#ffffff', bl: 0, it: 0, ff: 0, fs: 10, fc: '#000000', cl: 0, un: 0, vt: 0 } }) } // 創(chuàng)建工作表配置 - 不處理邊框 const sheetConfig: any = {} // 不再賦值borderInfo,保持默認網格線 return { name: sheetName, color: '', index, status: 1, order: index, hide: 0, row: Math.max(range.e.r + 1, 36), column: Math.max(range.e.c + 1, 18), defaultRowHeight: 19, defaultColWidth: 73, celldata, config: sheetConfig, scrollLeft: 0, scrollTop: 0, luckysheet_select_save: [], calcChain: [], isPivotTable: false, pivotTable: {}, filter_select: {}, filter: null, luckysheet_alternateformat_save: [], luckysheet_alternateformat_save_modelCustom: [], luckysheet_conditionformat_save: {}, frozen: {}, chart: [], zoomRatio: 1, image: [], showGridLines: 1, dataVerification: {} } }) console.log('所有工作表轉換完成:', sheets) resolve(sheets) } catch (error) { console.error('解析Excel文件時出錯:', error) reject(error) } } reader.onerror = (error) => { console.error('文件讀取失敗:', error) reject(new Error('文件讀取失敗')) } reader.onprogress = (event) => { if (event.lengthComputable) { const progress = (event.loaded / event.total) * 100 console.log(`文件讀取進度: ${progress.toFixed(2)}%`) } } console.log('開始讀取文件...') reader.readAsArrayBuffer(file) }) } // 導出Excel const exportExcel = async () => { console.log('exportExcel被調用') console.log('luckysheetInstance:', luckysheetInstance) console.log('window.luckysheet:', window.luckysheet) console.log('window.XLSX:', window.XLSX) try { // 檢查XLSX庫是否可用 if (!window.XLSX || !window.XLSX.utils) { ElMessage.error('XLSX庫未加載,請刷新頁面重試') return } // 檢查是否有可用的Luckysheet實例 const availableInstance = luckysheetInstance || window.luckysheet if (!availableInstance || typeof availableInstance.getAllSheets !== 'function') { console.log('Luckysheet實例不可用,嘗試重新初始化...') // 等待Luckysheet加載 const isLoaded = await waitForLuckysheet() if (!isLoaded) { ElMessage.error('Luckysheet加載失敗,請刷新頁面重試') return } // 嘗試初始化 const initSuccess = await initLuckysheet() if (initSuccess) { // 等待初始化完成 setTimeout(() => { exportExcel() }, 1500) } else { ElMessage.error('表格初始化失敗,請刷新頁面重試') } return } ElMessage.info('正在導出Excel文件...') console.log('開始導出,使用實例:', availableInstance) const data = availableInstance.getAllSheets() console.log('獲取到的數據:', data) if (!data || data.length === 0) { ElMessage.warning('沒有數據可導出') return } const workbook = window.XLSX.utils.book_new() data.forEach((sheet: any, index: number) => { console.log(`處理工作表 ${index}:`, sheet.name) const sheetData: any[][] = [] const celldata = Array.isArray(sheet.celldata) ? sheet.celldata : [] if (celldata.length === 0) { console.log(`工作表 ${sheet.name} 為空,創(chuàng)建空工作表`) const worksheet = window.XLSX.utils.aoa_to_sheet([['']]) window.XLSX.utils.book_append_sheet(workbook, worksheet, sheet.name) return } // 計算最大行列 const maxRow = Math.max(...celldata.map((cell: any) => cell.r)) + 1 const maxCol = Math.max(...celldata.map((cell: any) => cell.c)) + 1 console.log(`工作表 ${sheet.name} 大小: ${maxRow}行 x ${maxCol}列`) // 初始化二維數組 for (let r = 0; r < maxRow; r++) { sheetData[r] = [] for (let c = 0; c < maxCol; c++) { sheetData[r][c] = '' } } // 填充數據 celldata.forEach((cell: any) => { if (cell.v && cell.v.v !== undefined) { sheetData[cell.r][cell.c] = cell.v.v } }) console.log(`工作表 ${sheet.name} 數據:`, sheetData) const worksheet = window.XLSX.utils.aoa_to_sheet(sheetData) window.XLSX.utils.book_append_sheet(workbook, worksheet, sheet.name) }) const fileName = `luckysheet_export_${new Date().getTime()}.xlsx` console.log('導出文件名:', fileName) window.XLSX.writeFile(workbook, fileName) ElMessage.success('Excel文件導出成功!') } catch (error) { console.error('導出Excel失敗:', error) const errorMessage = error instanceof Error ? error.message : '未知錯誤' ElMessage.error('導出Excel失敗: ' + errorMessage) } } // 清空數據 const clearData = async () => { try { await ElMessageBox.confirm('確定要清空所有數據嗎?此操作不可恢復。', '確認清空', { confirmButtonText: '確定', cancelButtonText: '取消', type: 'warning' }) console.log('開始清空數據...') // 重新初始化 Luckysheet 使用默認數據 const success = await initLuckysheetWithData(defaultData) if (success) { ElMessage.success('數據已清空') } else { ElMessage.error('清空數據失敗,請刷新頁面重試') } } catch (error) { if (error !== 'cancel') { console.error('清空數據失敗:', error) ElMessage.error('清空數據失敗') } } } // 添加工作表 const addSheet = async () => { try { console.log('開始添加工作表...') // 獲取當前所有工作表 let currentSheets: any[] = [] if (window.luckysheet && typeof window.luckysheet.getAllSheets === 'function') { currentSheets = window.luckysheet.getAllSheets() || [] } console.log('當前工作表數量:', currentSheets.length) const sheetCount = currentSheets.length const newSheet = { name: `Sheet${sheetCount + 1}`, color: '', index: sheetCount, status: 1, order: sheetCount, hide: 0, row: 36, column: 18, defaultRowHeight: 19, defaultColWidth: 73, celldata: [] as any[], config: {}, scrollLeft: 0, scrollTop: 0, luckysheet_select_save: [], calcChain: [], isPivotTable: false, pivotTable: {}, filter_select: {}, filter: null, luckysheet_alternateformat_save: [], luckysheet_alternateformat_save_modelCustom: [], luckysheet_conditionformat_save: {}, frozen: {}, chart: [], zoomRatio: 1, image: [], showGridLines: 1, dataVerification: {} } console.log('新工作表配置:', newSheet) // 合并現有工作表和新工作表 const newData = [...currentSheets, newSheet] console.log('合并后的數據:', newData) // 重新初始化 Luckysheet 使用新數據 const success = await initLuckysheetWithData(newData) if (success) { ElMessage.success('工作表添加成功') } else { ElMessage.error('添加工作表失敗,請刷新頁面重試') } } catch (error) { console.error('添加工作表失敗:', error) ElMessage.error('添加工作表失敗: ' + (error instanceof Error ? error.message : '未知錯誤')) } } // 獲取數據 const getData = async () => { console.log("1111111"); console.log('getData被調用') console.log('luckysheetInstance:', luckysheetInstance) console.log('window.luckysheet:', window.luckysheet) // 檢查是否有可用的Luckysheet實例 const availableInstance = luckysheetInstance || window.luckysheet if (!availableInstance || typeof availableInstance.getAllSheets !== 'function') { console.log('Luckysheet實例不可用,嘗試重新初始化...') // 等待Luckysheet加載 const isLoaded = await waitForLuckysheet() if (!isLoaded) { ElMessage.error('Luckysheet加載失敗,請刷新頁面重試') return } // 嘗試初始化 const initSuccess = await initLuckysheet() if (initSuccess) { // 等待初始化完成 setTimeout(() => { getData() }, 1500) } else { ElMessage.error('表格初始化失敗,請刷新頁面重試') } return } try { // 使用可用的實例 const instance = availableInstance // 獲取所有工作表數據 const sheets = instance.getAllSheets() console.log('所有工作表數據:', sheets) // 獲取當前工作表數據 const currentSheet = instance.getSheetData() console.log('當前工作表數據:', currentSheet) // 獲取選中的單元格數據 const selectedRange = instance.getRangeByTxt() console.log('選中的單元格范圍:', selectedRange) ElMessage.success('數據已獲取,請查看控制臺') } catch (error) { console.error('獲取數據失敗:', error) ElMessage.error('獲取數據失敗') } } // 打印工作表 const printSheet = () => { const container = document.getElementById('luckysheet'); if (!container) { window.print(); return; } // 獲取內容實際高度和寬度 const grid = container.querySelector('.luckysheet-grid-container'); const contentHeight = grid ? grid.scrollHeight : container.scrollHeight; const contentWidth = grid ? grid.scrollWidth : container.scrollWidth; // 記錄原始尺寸 const originalHeight = container.style.height; const originalWidth = container.style.width; // 設置為內容實際尺寸 container.style.height = contentHeight + 'px'; container.style.width = contentWidth + 'px'; // 等待渲染后打印 setTimeout(() => { window.print(); // 恢復原始尺寸 container.style.height = originalHeight; container.style.width = originalWidth; }, 500); }; // 組件掛載時初始化 onMounted(async () => { console.log('組件掛載,開始初始化...') console.log('window.luckysheet:', window.luckysheet) // 等待Luckysheet加載完成 const isLoaded = await waitForLuckysheet() if (isLoaded) { console.log('Luckysheet已加載,開始初始化') await initLuckysheet() } else { console.error('Luckysheet加載失敗') ElMessage.error('Luckysheet加載失敗,請刷新頁面重試') } }) // 組件卸載時清理 onUnmounted(() => { if (luckysheetInstance) { luckysheetInstance.destroy() } }) </script> <style scoped lang="scss"> .luckysheet-container { width: 100%; height: 100%; display: flex; flex-direction: column; } .toolbar { padding: 16px; background: #f5f5f5; border-bottom: 1px solid #e0e0e0; display: flex; justify-content: flex-end; gap: 12px; flex-wrap: wrap; } .luckysheet-wrapper { flex: 1; min-height: 600px; position: relative; } /* 確保Luckysheet容器有足夠的高度 */ #luckysheet { width: 100%; height: 100%; min-height: 600px; } /* 響應式設計 */ @media (max-width: 768px) { .toolbar { padding: 12px; gap: 8px; } .toolbar .el-button { padding: 8px 12px; font-size: 12px; } } </style> <style> .luckysheet_info_detail div.luckysheet_info_detail_back{ display: none !important; } .luckysheet_info_detail_update,.luckysheet_info_detail_save{ display: none !important; } #luckysheet .luckysheet-share-logo, .luckysheet .luckysheet-share-logo, .luckysheet-share-logo{ background-image:url('@/assets/imgs/logo.png') !important; background-size: contain !important; background-repeat: no-repeat !important; background-position: center !important; width: 120px !important; height: 40px !important; } @media print { body * { visibility: hidden; } #luckysheet, #luckysheet * { visibility: visible; } #luckysheet { position: static !important; left: 0 !important; top: 0 !important; width: 100% !important; min-height: 100vh !important; height: auto !important; background: #fff !important; overflow: visible !important; box-sizing: border-box !important; padding: 0 !important; margin: 0 !important; page-break-inside: avoid !important; } .toolbar { display: none !important; } } </style>
到此這篇關于vue3+luckysheet實現在線編輯Excel的項目實踐的文章就介紹到這了,更多相關vue3 luckysheet 在線編輯Excel內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
您可能感興趣的文章:
相關文章
Vue3如何利用xlsx、xlsx-js-style導出Excel表格使用(適合新手)
在Vue項目中導出Excel表格是常見的功能,特別是在后臺管理系統(tǒng)中,為了方便用戶將大量數據保存為本地文件,這篇文章主要給大家介紹了關于Vue3如何利用xlsx、xlsx-js-style導出Excel表格使用的相關資料,需要的朋友可以參考下2024-06-06vue elementUi+sortable.js實現嵌套表格拖拽問題
這篇文章主要介紹了vue elementUi+sortable.js實現嵌套表格拖拽問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-06-06