cnpm i xlsx --D
html
import XLSX from 'xlsx'
npm
/** * 一、 String.fromCharCode(65 + i) + 1 : A1,B1,C1.... * 二、 String.fromCharCode(65 + j) + (i + 2) A2,B2,C2... * A3,B3,C3... * 測試: * const headers = [{ key: 'date', title: '日期' }, { key: 'name', title: '名稱' }] * const data = [{ date: '2019-05-31', name: 'megen.huang' }, { date: '2019-06-20', name: '小明' }] * console.log(exportJsonToExcel(headers, data)) * 使用xlsx插件將json數據導出到Excel中----針對表格數據 * @param {Array} headers 表頭:[{key: 'date', title: '日期'}, {key: 'name', title: '名稱'}] * @param {Array} data 表體數據:[{date: '2019-05-31', name: 'megen.huang'}, {date: '2019-06-20', name: '小明'}] * @param {String} fileName 導出的文件名稱 :'export.xlsx' */ export function exportJsonToExcel (headers = [], data = [], fileName = 'export.xlsx') { // 先處理數據 data = handleCSV(data) const _headers = headers .map((item, i) => Object.assign({}, { key: item.key, title: item.title, position: String.fromCharCode(65 + i) + 1 })) .reduce((prev, next) => Object.assign({}, prev, { [next.position]: { key: next.key, v: next.title }}), {}) const _data = data // 二維數組 .map((item, i) => headers.map((key, j) => Object.assign({}, { content: item[key.key], position: String.fromCharCode(65 + j) + (i + 2) }))) // 二維轉一維 .reduce((prev, next) => prev.concat(next)) // 轉成worksheet須要的數據結構 .reduce((prev, next) => Object.assign({}, prev, { [next.position]: { v: next.content }}), {}) // 合併 headers 和 data const output = Object.assign({}, _headers, _data) console.log('output', output) // 獲取全部單元格的位置 const outputPos = Object.keys(output) // 計算出範圍 ,["A1",..., "H2"] const ref = `${outputPos[0]}:${outputPos[outputPos.length - 1]}` console.log('ref', ref) // 構建 workbook 對象 const wb = { SheetNames: ['mySheet'], Sheets: { mySheet: Object.assign( {}, output, { '!ref': ref, '!cols': headers.map(item => ({ wpx: 120 }))// width in screen pixels } ) } } // 導出 Excel XLSX.writeFile(wb, fileName) } // 防止CSV注入處理 export function handleCSV (arr) { const reg = new RegExp('(^=|^-)') if (Array.isArray(arr) && arr.length > 0) { for (const item of arr) { Object.keys(item).forEach(key => { if (item[key] && reg.test(item[key])) { item[key] = '\'' + item[key] } }) } } return arr } /** * 日期格式轉換 * `第一個參數爲傳入的以毫秒爲單位的時間戳,第二個參數爲格式,具體說明見代碼; * 不傳參則返回當前日期,則爲「'yyyy年MM月dd日'」格式顯示.` * @param {object} _date 日期 * @param {string} _format 轉換後的日期格式 */ export function FormatDate (_date, _format) { if (_format && !_date) { return '' } var date = _date || new Date() var format = _format || 'yyyy/MM/dd' date = new Date(_date) var map = { M: date.getMonth() + 1, // 月份 d: date.getDate(), // 日 h: date.getHours(), // 小時 m: date.getMinutes(), // 分 s: date.getSeconds(), // 秒 q: Math.floor((date.getMonth() + 3) / 3), // 季度 S: date.getMilliseconds() // 毫秒 } format = format.replace(/([yMdhmsqS])+/g, function (all, t) { var v = map[t] if (v !== undefined) { if (all.length > 1) { v = '0' + v v = v.substr(v.length - 2) } return v } else if (t === 'y') { return (date.getFullYear() + '').substr(4 - all.length) } return all }) return format }
<template> <div> <!-- 這裏使用了ElementUI的el-upload組件 --> <el-upload ref="upload" class="upload-demo" :action="''" :multiple="false" :show-file-list="false" :limit="1" :before-upload="beforeAvatarUpload" :file-list="fileList" >從Excel導入 </el-upload> </div> </template>
import { exportJsonToExcel, deepClone, FormatDate } from '@/utils' data(){ return { outJson: [], // 最後要發送給後臺的json數據 // 導入表頭必填字段 excelHeaderRequired: [ '序號(必填)', '子任務名稱(必填)', '子任務內容(必填)', '執行人(必填)', '預計開始時間(必填)', '預計完成時間(必填)', '緊急程度(必填)' ], exportHeader: Object.freeze([{ key: 'index', title: '序號' }, { key: 'workItem', title: '事項名稱' }, { key: 'taskName', title: '子任務名稱' }, { key: 'remark', title: '子任務內容' }, { key: 'executor', title: '執行人' }, { key: 'planStarttime', title: '預計開始時間' }, { key: 'planEndtime', title: '預計完成時間' }, { key: 'actualStarttime', title: '實際開始時間' }, { key: 'actualEndtime', title: '實際完成時間' }, { key: 'tagList', title: '標籤' }, { key: 'orderNum', title: '緊急程度' }, { key: 'taskProgress', title: '工做進展' } ]), order: Object.freeze({ '緊急': 0, '通常': 1, '重要緊急': 2 }), orderObj: Object.freeze({ 0: '緊急', 1: '通常', 2: '重要緊急' }), } } methods:{ <!-- 上傳以前進行文件校驗 --> beforeAvatarUpload (file) { if (!file) { // 沒有文件 return false } else if (!/\.(xls|xlsx)$/.test(file.name.toLowerCase())) { // 格式根據本身需求定義 this.$message.error('上傳格式不正確,請上傳xls或者xlsx格式') return false } this.handleExcel(file)// 處理數據 this.$refs.upload.clearFiles() }, handleExcel (file) { // 表格導入 const files = file // excel文件 const fileReader = new FileReader() fileReader.onload = ev => { try { const data = ev.target.result const workbook = XLSX.read(data, { type: 'binary', cellDates: true }) const wsname = workbook.SheetNames[0]// 取第一張表 const tempArr = XLSX.utils.sheet_to_json(workbook.Sheets[wsname]) // 生成json表格內容,若是某個字段列沒有填,則解析出來就會沒有該字段 console.log('解析出來的數據', tempArr) this.outJson = [] let isValidPass = true for (const item of tempArr) { console.log('幾回', item) const obj = {} const bool = this.validFieldRequired(item) if (!bool) { // 若是不是true,說明有些必填字段沒有填 isValidPass = false break } const isValidSuccess = this.handleoutJson(item, obj) if (!isValidSuccess) { isValidPass = false break } else { this.outJson.push(this.successObj)// this.outJson放的就是須要返回給後臺的 } } if (isValidPass) { // 若是this.outJson有數聽說明已經所有校驗成功能夠傳給後臺了 batchSaveTask(this.outJson).then(res => { this.$message.success('導入成功') this.init() }).catch(e => {}) } } catch (e) { console.log(e) return false } } fileReader.readAsBinaryString(files) }, // 校驗必填字段的值是否存在 validFieldRequired (item) { let boolean = true for (const el of this.excelHeaderRequired) { if (!Object.keys(item).includes(el)) { this.$message.error(`${el}`) boolean = false break } } return boolean }, // 將解析的json轉爲後臺須要的字段 handleoutJson (item, obj) { // if (item['事項名稱(必填)']) { // } this.successObj = {} obj['matterId'] = this.$route.query.id obj['taskName'] = item['子任務名稱(必填)'].trim() obj['remark'] = item['子任務內容(必填)'].trim() // 人員的須要傳三個參數 obj['executorAccount'] = item['執行人(必填)'].trim() obj['executorId'] = '' obj['executor'] = '' if (!DataType(item['預計開始時間(必填)'], 'date')) { this.$message.error('預計開始時間格式不符合') return false } if (!DataType(item['預計完成時間(必填)'], 'date')) { this.$message.error('預計結束時間格式不符合') return false } if (item['預計開始時間(必填)'] > item['預計完成時間(必填)']) { this.$message.error('預計開始時間不能晚於預計結束時間') return false } else { // 須要對時間進行處理 obj['planStarttime'] = FormatDate(item['預計開始時間(必填)'], 'yyyy-MM-dd hh:mm:ss') // 須要對時間進行處理 obj['planEndtime'] = FormatDate(item['預計完成時間(必填)'], 'yyyy-MM-dd hh:mm:ss') } console.log(item['預計開始時間(必填)'], item['預計完成時間(必填)']) if (item['緊急程度(必填)'] && Object.keys(this.order).includes(item['緊急程度(必填)'].trim())) { // 須要把漢字轉成key傳過去 obj['orderNum'] = this.order[item['緊急程度(必填)'].trim()] } else { this.$message.error('任務等級不存在') return false } this.successObj = { ...obj } return true }, }
// 導出 exportMatter (type, tag) { if (type === '1' && this.completeList.length > 0) { // 這裏深copy防止污染原數據 const arr = deepClone(this.completeList) // 須要處理數據 const sourceData = arr.map((item, index) => { item['index'] = index + 1 item['tagList'] = this.tagsText(item.tagList) item['orderNum'] = this.orderObj[item.orderNum] item['planStarttime'] = item.planStarttime ? FormatDate(item.planStarttime, 'yyyy/MM/dd hh:mm:ss') : '' item['planEndtime'] = item.planEndtime ? FormatDate(item.planEndtime, 'yyyy/MM/dd hh:mm:ss') : '' item['actualStarttime'] = item.actualStarttime ? FormatDate(item.actualStarttime, 'yyyy/MM/dd hh:mm:ss') : '' item['actualEndtime'] = item.planStarttime ? FormatDate(item.actualEndtime, 'yyyy/MM/dd hh:mm:ss') : '' Object.keys(item).forEach(key => { item[key] = item[key] === null ? '' : item[key] }) return item }) exportJsonToExcel(this.exportHeader, sourceData, 'exportSubTask.xlsx') } else { this.$message.error('沒有可導出的已完成任務') } },