Vue純前端使用exceljs導(dǎo)出excel文件的完整圖文教程
一.導(dǎo)出數(shù)據(jù)為xlsx表格文件 表格帶下拉選擇
1.npm引入
npm install exceljs@4.3.0
2.cnpm引入(npm引入失敗的話)
cnpm install exceljs@4.3.0
3.頁面引入方式 import ExcelJS from "exceljs";
二、實(shí)例1
<template> <button @click="exportExcel">導(dǎo)出Excel</button> </template> <script> import ExcelJS from "exceljs"; export default { methods: { async exportExcel() { const workbook = new ExcelJS.Workbook(); // 創(chuàng)建一個(gè)工作表 elecel里面可以有多個(gè)工作表 const worksheet = workbook.addWorksheet("Sheet1"); // 設(shè)置創(chuàng)建的工作表的表頭 worksheet.getCell("A1").value = "姓名"; worksheet.getCell("B1").value = "年齡"; worksheet.getCell("C1").value = "性別"; worksheet.getCell("D1").value = "來源"; // 添加數(shù)據(jù)行 一般用于循環(huán)我們后端傳來的數(shù)據(jù)設(shè)置導(dǎo)出的每一行 worksheet.addRow(["Alex", 25, "男", "微信"]); worksheet.addRow(["Alix", 30, "女", "微信"]); worksheet.addRow(["Andy", 35, "男", "微信"]); // 設(shè)置設(shè)置我們的下拉選項(xiàng)的數(shù)據(jù)列表 const dropdownOptions = ["男", "女"]; const dropdownOptions1 = ["微信", "支付寶", "云閃付"]; // 創(chuàng)建我們的第二個(gè)工作表用于存放我們下拉菜單的選項(xiàng) const worksheet2 = workbook.addWorksheet("sheet2"); // 這里設(shè)置的是第二個(gè)工作表一列一列的展示 看圖2 worksheet2.getColumn("A").values = dropdownOptions worksheet2.getColumn("B").values = dropdownOptions1 // 這里設(shè)置的是第二個(gè)工作表一行一行的展示 看圖3 // worksheet2.addRow(dropdownOptions); // worksheet2.addRow(dropdownOptions1); // 注意:-------------解釋 // 如果是一行一行的 我們的sheet 的c列關(guān)聯(lián)sheet2的第一行數(shù)據(jù)的話就要這樣寫 // formulae: [`=sheet2!$A$1:$Z$1`], // 第二行就要這樣寫formulae: [`=sheet2!$A$2:$Z$2`]但是這樣的和 他只會寫到第二行的A-Z // 如果一列一列的話 我們的sheet 的c列關(guān)聯(lián)sheet2的第一列數(shù)據(jù)的話就要這樣寫 // formulae: [`=sheet2!$A:$A`], // 如果關(guān)聯(lián)到第二列就要這樣寫 formulae: [`=sheet2!$B:$B`], // 性別下拉 這是用來把工作表1中的某一列關(guān)聯(lián)到工作表二中的某一行作為下拉框的值或者是某一列 const col = worksheet.getColumn("C"); // 遍歷此列中的所有當(dāng)前單元格,包括空單元格 col.eachCell({ includeEmpty: true }, function (cell, rowNumber) { // 設(shè)置下拉列表 cell.dataValidation = { type: "list", allowBlank: true, formulae: [`=sheet2!$A:$A`], //行取值 // formulae: [`=sheet2!$A$1:$Z$1`], //列取值 }; }); //來源下拉 const col3 = worksheet.getColumn("D"); // 遍歷此列中的所有當(dāng)前單元格,包括空單元格 col3.eachCell({ includeEmpty: true }, function (cell, rowNumber) { // 設(shè)置下拉列表 cell.dataValidation = { type: "list", allowBlank: true, formulae: [`=sheet2!$B:$B`],//行取值 // formulae: [`=sheet2!$A$2:$Z$2`],//列取值 }; }); // 導(dǎo)出Excel文件 const buffer = await workbook.xlsx.writeBuffer(); const blob = new Blob([buffer], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", }); const url = URL.createObjectURL(blob); const link = document.createElement("a"); link.href = url; link.download = "data.xlsx"; link.click(); URL.revokeObjectURL(url); }, }, }; </script>
三、實(shí)例2-動態(tài)數(shù)據(jù)
// 引入格式化工具類 import {timestampToDay, timestampToTime} from '@/utils/util.js' export default { props: { exportExcelArray: { type: Array, default: () => { return [] } }, tableData: { type: Array, default: () => { return [] } }, // 導(dǎo)出自定義字體顏色所屬字段 showRedList: { type: Array, default: () => { return ['未通過', '否'] } }, showOrangeList: { type: Array, default: () => { return ['未測試'] } }, showGreenList: { type: Array, default: () => { return ['通過', '是'] } }, // 導(dǎo)出自定義字體顏色 customColor: { type: String, default: 'D63640' }, customGreen: { type: String, default: '1AD96F' }, customOrange: { type: String, default: 'f2b400' }, // 是否顯示多個(gè)工作表-帶下拉框表格 showMultipleMenu: { type: Boolean, default: false }, // 是否顯示多個(gè)工作表-獨(dú)立 showMultiple: { type: Boolean, default: false }, }, methods: { //表格formatter數(shù)據(jù)格式化 formatter (value,item,row,prop) { // console.log('value',value) // console.log('item',item) // console.log('row',row) // console.log('prop',prop) //針對table中item多層對象層級的情況 if(prop.indexOf('.') > 0){ let temp = prop.split('.') // console.log('temp',temp) //item中嵌套兩層(列表數(shù)組) if(temp.length == 2){ let temp = prop.split('.') let arry = row[temp[0]] let result = '' // 多層對象層級的情況是否為數(shù)組 if(Array.isArray(arry)){ for(let i in arry){ // console.log('arry[i][temp[1]]',arry[i][temp[1]]) if(!this.isNull(arry[i][temp[1]])){ if(item.formatterType == 'common-type'){ //通用類型轉(zhuǎn)換 result = arry[i][temp[1]] } else if(item.formatterType == 'time-type'){ //時(shí)間標(biāo)準(zhǔn)格式化 if(item.timestampToDay){ result = timestampToDay(arry[i][temp[1]]) }else { result = timestampToTime(arry[i][temp[1]]) } } else if(item.formatterType == 'amount-type'){ //金額轉(zhuǎn)換 result = (arry[i][temp[1]] / 100).toFixed(2) } else { result = arry[i][temp[1]] } return result } } }else{ // 多層對象層級的情況是否為對象 if(!this.isNull(row[temp[0]][temp[1]])){ if(item.formatterType == 'common-type'){ //通用類型轉(zhuǎn)換 result = row[temp[0]][temp[1]] } else if(item.formatterType == 'time-type'){ //時(shí)間標(biāo)準(zhǔn)格式化 if(item.timestampToDay){ result = timestampToDay(row[temp[0]][temp[1]]) }else { result = timestampToTime(row[temp[0]][temp[1]]) } } else if(item.formatterType == 'amount-type'){ //金額轉(zhuǎn)換 result = (row[temp[0]][temp[1]] / 100).toFixed(2) } else { result = row[temp[0]][temp[1]] } return result } } } } else{ //item中無嵌套對象 let temp = prop.split('.') if(item.formatterType == 'common-type'){ //通用類型轉(zhuǎn)換 let arry = item.formatterInfo for(let i in arry){ if(arry[i].value == value){ return arry[i].label } } } else if(item.formatterType == 'time-type'){ //時(shí)間標(biāo)準(zhǔn)格式化 // console.log('row[temp[0]])1',row[temp[0]]) if(!this.isNull(row[temp[0]])){ // return value.substring(0,value.length - 2) if(item.timestampToDay){ return timestampToDay(value) }else { return timestampToTime(value) } } } else if(item.formatterType == 'amount-type'){ //金額轉(zhuǎn)換 return (value / 100).toFixed(2) } else { return value } } }, // 是否顯示多個(gè)工作表 async exportExcelMultiple() { const vm = this let workbook = new ExcelJS.Workbook(); // 是否顯示多個(gè)工作表-獨(dú)立 if(vm.showMultiple){ let workbookList = [ { "sheetName": '質(zhì)檢列表', worksheetData: this.tableData, // 接口導(dǎo)出數(shù)據(jù) worksheetHeaderList: this.exportExcelArray // excel頂部標(biāo)題自定義 } ] workbook = await this.exportExcelMultipleEach(workbook,workbookList); }else { // 創(chuàng)建一個(gè)工作表 elecel里面可以有多個(gè)工作表 const worksheet = workbook.addWorksheet("Sheet1"); // 設(shè)置創(chuàng)建的工作表的表頭 // 表頭對象-標(biāo)題超過26個(gè)字母則繼續(xù)添加 let topCellObj = { 1: 'A', 2: 'B', 3: 'C', 4: 'D', 5: 'E', 6: 'F', 7: 'G', 8: 'H', 9: 'I', 10: 'J', 11: 'K', 12: 'L', 13: 'M', 14: 'N', 15: 'O', 16: 'P', 17: 'Q', 18: 'R', 19: 'S', 20: 'T', 21: 'U', 22: 'V', 23: 'W', 24: 'S', 25: 'Y', 26: 'Z', } this.exportExcelArray.forEach((item,index)=>{ for(let path in topCellObj){ if(path == index + 1){ // 標(biāo)頭標(biāo)題賦值 worksheet.getCell(topCellObj[path]+"1").value = item.label; break } } }) // 添加數(shù)據(jù)行 一般用于循環(huán)我們后端傳來的數(shù)據(jù)設(shè)置導(dǎo)出的每一行 this.tableData.forEach((row)=> { let arr = [] for (const [key, value] of Object.entries(row)) { // console.log(`Key: ${key}, Value: ${value}`); for (let i=0; i < this.exportExcelArray.length; i++) { let item = this.exportExcelArray[i] if (item.prop == key) { // 格式化字段 let formatterValue = this.formatter(row[item.prop],item,row,item.prop) arr = [...arr, ...[{index:i,value: formatterValue ? formatterValue : value}]] break } } } // index排序并且返回value字段 const arrNew = arr.sort((a, b) => a.index - b.index).map(item => item.value); worksheet.addRow(arrNew); }) // return // 添加列標(biāo)題并定義列鍵和寬度 // 注意:這些列結(jié)構(gòu)僅是構(gòu)建工作簿的方便之處,除了列寬之外,它們不會完全保留。 worksheet.columns = this.exportExcelArray.map((item)=>{ return { header: item.label, key: item.prop, width: item.width, } }) // 設(shè)置第一行表頭背景色和字體顏色 worksheet.getRow(1).eachCell({ includeEmpty: true }, (cell, colNumber) => { cell.fill = { type: 'pattern', pattern: 'solid', // fgColor: { argb: (colNumber === 1 || colNumber === 2) ? 'FF79bbff' : 'FF95d475' } fgColor: { argb: 'FF79bbff' } }; cell.font = { size: 10, color: { argb: 'FFFFFFFF' } // 白色 }; cell.alignment = { horizontal: "center",// 水平居中 vertical: 'middle', // 垂直居中 } }); // 添加邊框+水平+垂直居中 for (let num = 0; num < worksheet.lastRow._number; num++) { // 循環(huán)出每一行 for (let index = 0; index < worksheet.columns.length; index++) { let rowCell = worksheet.getRow(num + 1).getCell(index + 1) // console.log('rowCell',rowCell) // 表格的值包含于顏色列表內(nèi)則給單元格賦值顏色樣式 if(rowCell&&rowCell._value&&rowCell._value.model&&rowCell._value.model.value&&num>0){ const colValue = rowCell._value.model.value // 自定義字體顏色 let col = '000000' // 紅色 if(vm.showRedList.includes(colValue)){ col = vm.customColor } //綠色 if(vm.showGreenList.includes(colValue)){ col = vm.customGreen } // 橙色 if(vm.showOrangeList.includes(colValue)){ col = vm.customOrange } // 字體 rowCell.font = { size: 10, // 自定義字體顏色 color: {argb: col}, } } // 循環(huán)出每一個(gè)單元格 rowCell.border = { // 為單元格添加邊框 top: { style: "thin" }, left: { style: "thin" }, bottom: { style: "thin" }, right: { style: "thin" }, }; rowCell.alignment = { // 為單元格添加水平+垂直居中 horizontal: "center",// 水平居中 vertical: 'middle', // 垂直居中 }; } } // 設(shè)置設(shè)置我們的下拉選項(xiàng)的數(shù)據(jù)列表 const dropdownOptions = ["吉祥星", "納斯達(dá)", "普耐爾", "阿龍"]; const dropdownOptions1 = ["自研", "英卡", "微步", "茂現(xiàn)", "文泰"]; // 創(chuàng)建我們的第二個(gè)工作表用于存放我們下拉菜單的選項(xiàng) const worksheet2 = workbook.addWorksheet("sheet2"); // 這里設(shè)置的是第二個(gè)工作表一列一列的展示 看圖2 worksheet2.getColumn("A").values = dropdownOptions worksheet2.getColumn("B").values = dropdownOptions1 // 這里設(shè)置的是第二個(gè)工作表一行一行的展示 看圖3 // worksheet2.addRow(dropdownOptions); // worksheet2.addRow(dropdownOptions1); // 注意:-------------解釋 // 如果是一行一行的 我們的sheet 的c列關(guān)聯(lián)sheet2的第一行數(shù)據(jù)的話就要這樣寫 // formulae: [`=sheet2!$A$1:$Z$1`], // 第二行就要這樣寫formulae: [`=sheet2!$A$2:$Z$2`]但是這樣的和 他只會寫到第二行的A-Z // 如果一列一列的話 我們的sheet 的c列關(guān)聯(lián)sheet2的第一列數(shù)據(jù)的話就要這樣寫 // formulae: [`=sheet2!$A:$A`], // 如果關(guān)聯(lián)到第二列就要這樣寫 formulae: [`=sheet2!$B:$B`], // 性別下拉 這是用來把工作表1中的某一列關(guān)聯(lián)到工作表二中的某一行作為下拉框的值或者是某一列 const col = worksheet.getColumn("F"); // 遍歷此列中的所有當(dāng)前單元格,包括空單元格 col.eachCell({ includeEmpty: true }, function (cell, rowNumber) { // 設(shè)置下拉列表 cell.dataValidation = { type: "list", allowBlank: true, formulae: [`=sheet2!$A:$A`], //行取值 // formulae: [`=sheet2!$A$1:$Z$1`], //列取值 }; }); //來源下拉 const col3 = worksheet.getColumn("G"); // 遍歷此列中的所有當(dāng)前單元格,包括空單元格 col3.eachCell({ includeEmpty: true }, function (cell, rowNumber) { // 設(shè)置下拉列表 cell.dataValidation = { type: "list", allowBlank: true, formulae: [`=sheet2!$B:$B`],//行取值 // formulae: [`=sheet2!$A$2:$Z$2`],//列取值 }; }); } // 導(dǎo)出Excel文件 const buffer = await workbook.xlsx.writeBuffer(); const blob = new Blob([buffer], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", }); const url = URL.createObjectURL(blob); const link = document.createElement("a"); link.href = url; link.download = "質(zhì)檢列表記錄.xlsx"; link.click(); URL.revokeObjectURL(url); }, // workbook:新建表單 // workbook-sheetName: 表格名稱, // workbook-worksheetData: 表格數(shù)據(jù), // workbook-worksheetHeaderList: 表格頂部標(biāo)題 // workbookList: 導(dǎo)出列表+表頭 async exportExcelMultipleEach(workbook,workbookList) { const vm = this workbookList.forEach((mItem,index)=>{ // 創(chuàng)建一個(gè)工作表 elecel里面可以有多個(gè)工作表 const worksheet = workbook.addWorksheet(mItem&&mItem.sheetName ? mItem.sheetName : "Sheet"+(index+1)); // 設(shè)置創(chuàng)建的工作表的表頭 // 表頭對象-標(biāo)題超過26個(gè)字母則繼續(xù)添加 let topCellObj = { 1: 'A', 2: 'B', 3: 'C', 4: 'D', 5: 'E', 6: 'F', 7: 'G', 8: 'H', 9: 'I', 10: 'J', 11: 'K', 12: 'L', 13: 'M', 14: 'N', 15: 'O', 16: 'P', 17: 'Q', 18: 'R', 19: 'S', 20: 'T', 21: 'U', 22: 'V', 23: 'W', 24: 'S', 25: 'Y', 26: 'Z', } mItem.worksheetHeaderList.forEach((item,index)=>{ for(let path in topCellObj){ if(path == index + 1){ worksheet.getCell(topCellObj[path]+"1").value = item.label; break } } }) // 添加數(shù)據(jù)行 一般用于循環(huán)我們后端傳來的數(shù)據(jù)設(shè)置導(dǎo)出的每一行 mItem.worksheetData.forEach((row)=> { let arr = [] for (const [key, value] of Object.entries(row)) { // console.log(`Key: ${key}, Value: ${value}`); for (let i=0; i < mItem.worksheetHeaderList.length; i++) { let item = mItem.worksheetHeaderList[i] if (item.prop == key) { // 格式化字段 let formatterValue = this.formatter(row[item.prop],item,row,item.prop) arr = [...arr, ...[{index:i,value: formatterValue ? formatterValue : value}]] break } } } // index排序并且返回value字段 const arrNew = arr.sort((a, b) => a.index - b.index).map(item => item.value); worksheet.addRow(arrNew); }) // return // 添加列標(biāo)題并定義列鍵和寬度 // 注意:這些列結(jié)構(gòu)僅是構(gòu)建工作簿的方便之處,除了列寬之外,它們不會完全保留。 worksheet.columns = mItem.worksheetHeaderList.map((item)=>{ return { header: item.label, key: item.prop, width: item.width, } }) // 設(shè)置第一行表頭背景色和字體顏色 worksheet.getRow(1).eachCell({ includeEmpty: true }, (cell, colNumber) => { cell.fill = { type: 'pattern', pattern: 'solid', // fgColor: { argb: (colNumber === 1 || colNumber === 2) ? 'FF79bbff' : 'FF95d475' } fgColor: { argb: 'FF79bbff' } }; cell.font = { size: 10, color: { argb: 'FFFFFFFF' } // 白色 }; cell.alignment = { horizontal: "center",// 水平居中 vertical: 'middle', // 垂直居中 } }); // 添加邊框+水平+垂直居中 for (let num = 0; num < worksheet.lastRow._number; num++) { // 循環(huán)出每一行 for (let index = 0; index < worksheet.columns.length; index++) { let rowCell = worksheet.getRow(num + 1).getCell(index + 1) // console.log('rowCell',rowCell) // 表格的值包含于顏色列表內(nèi)則給單元格賦值顏色樣式 if(rowCell&&rowCell._value&&rowCell._value.model&&rowCell._value.model.value&&num>0){ const colValue = rowCell._value.model.value // 自定義字體顏色 let col = '000000' // 紅色 if(vm.showRedList.includes(colValue)){ col = vm.customColor } //綠色 if(vm.showGreenList.includes(colValue)){ col = vm.customGreen } // 橙色 if(vm.showOrangeList.includes(colValue)){ col = vm.customOrange } // 字體 rowCell.font = { size: 10, // 自定義字體顏色 color: {argb: col}, } } // 循環(huán)出每一個(gè)單元格 rowCell.border = { // 為單元格添加邊框 top: { style: "thin" }, left: { style: "thin" }, bottom: { style: "thin" }, right: { style: "thin" }, }; rowCell.alignment = { // 為單元格添加水平+垂直居中 horizontal: "center",// 水平居中 vertical: 'middle', // 垂直居中 }; } } }) return workbook }, } }
3.1 exportExcelArray格式
exportExcelArray: [ { prop: 'serialNumber', // 字段key label: '序號', // 字段名稱 formatterFlag: false, // 值是否需要格式化 align: 'center', // 布局-居中 width: 10, // 列寬度 }, { prop: 'createTime', label: '創(chuàng)建時(shí)間', formatterFlag: true, formatterType: 'time-type', // 時(shí)間校驗(yàn)-時(shí)間戳轉(zhuǎn)為日期 }, ]
3.2 @/utils/util.js
/** * 時(shí)間戳 * @param {*} timestamp 時(shí)間戳 */ //尾部補(bǔ)零(常用13為時(shí)間戳字符串,后端返回格式不一定是13位) const padEndFun = (val,digit,num) => { if(val == '' || val == undefined || val == null){ return '' } if(typeof val == 'number'){ val = val.toString() } // console.log('val',val) let str = val.padEnd(digit, num.toString()) return Number(str) }, const timestampToTime = (timestamp) => { if(timestamp == '' || timestamp == undefined || timestamp == null){ return '' } if(padEndFun){ //時(shí)間戳為10位需*1000,時(shí)間戳為13位的話不需乘1000 timestamp = padEndFun(timestamp,13,0) } let date = new Date(timestamp) //時(shí)間戳為10位需*1000,時(shí)間戳為13位的話不需乘1000 let Y = date.getFullYear() + '-' let M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-' let D = (date.getDate() < 10 ? '0' + date.getDate() : date.getDate()) + ' ' let h = (date.getHours() < 10 ? '0' + date.getHours() : date.getHours()) + ':' let m = (date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()) + ':' let s = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds() return Y + M + D + h + m + s }; const timestampToDay = (timestamp) => { if(timestamp == '' || timestamp == undefined || timestamp == null){ return '' } if(padEndFun){ //時(shí)間戳為10位需*1000,時(shí)間戳為13位的話不需乘1000 timestamp = padEndFun(timestamp,13,0) } let date = new Date(timestamp) //時(shí)間戳為10位需*1000,時(shí)間戳為13位的話不需乘1000 let Y = date.getFullYear() + '-' let M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-' let D = (date.getDate() < 10 ? '0' + date.getDate() : date.getDate()) return Y + M + D }; /** * 存儲localStorage */ const setStore = (name, content) => { if (!name) return; if (typeof content !== 'string') { content = JSON.stringify(content); } window.localStorage.setItem(name, content); } /** * 獲取localStorage */ const getStore = name => { if (!name) return; return window.localStorage.getItem(name); } /** * 刪除localStorage */ const removeStore = name => { if (!name) return; window.localStorage.removeItem(name); } /** * 設(shè)置cookie **/ function setCookie(name, value, day) { let date = new Date(); date.setDate(date.getDate() + day); document.cookie = name + '=' + value + ';expires=' + date; }; /** * 獲取cookie **/ function getCookie(name) { let reg = RegExp(name + '=([^;]+)'); let arr = document.cookie.match(reg); if (arr) { return arr[1]; } else { return ''; } }; /** * 刪除cookie **/ function delCookie(name) { setCookie(name, null, -1); }; /** * 導(dǎo)出 **/ export { timestampToTime, timestampToDay, setStore, getStore, removeStore, setCookie, getCookie, delCookie }
以上就是Vue純前端使用exceljs導(dǎo)出excel文件的完整圖文教程的詳細(xì)內(nèi)容,更多關(guān)于Vue exceljs導(dǎo)出excel的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
vue作用域插槽詳解、slot、v-slot、slot-scope
這篇文章主要介紹了vue作用域插槽詳解、slot、v-slot、slot-scope,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03Vue.js實(shí)現(xiàn)模擬微信朋友圈開發(fā)demo
本篇文章主要介紹了Vue.js實(shí)現(xiàn)模擬微信朋友圈開發(fā)demo,實(shí)現(xiàn)展示朋友圈,評論,點(diǎn)贊等功能,有興趣的可以了解一下。2017-04-04解決Vue 瀏覽器后退無法觸發(fā)beforeRouteLeave的問題
這篇文章主要介紹了解決Vue 瀏覽器后退無法觸發(fā)beforeRouteLeave的問題,需要的朋友可以參考下2017-12-12vue學(xué)習(xí)筆記之v-if和v-show的區(qū)別
本篇文章主要介紹了vue學(xué)習(xí)筆記之v-if和v-show的區(qū)別,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-09-09基于Vue.js的文件選擇與多選對話框組件Dccfile的使用教程
在現(xiàn)代前端開發(fā)中,Vue.js 提供了強(qiáng)大的組件化開發(fā)能力,使得我們可以輕松構(gòu)建復(fù)雜且可復(fù)用的用戶界面,本文將介紹一個(gè)基于 Vue.js 的文件選擇與多選對話框組件——Dccfile,并詳細(xì)解析其功能和實(shí)現(xiàn)細(xì)節(jié)2025-04-04