vue前端解析excel并在页面展示数据

本文介绍了如何使用Vue框架结合XLSX库,实现文件上传、解析Excel内容为JSON格式,并在前端展示数据的过程,包括文件选择、数据预处理和表格显示。

使用vue实现关于前端导入excel 解析成json并在页面展示数据

<template>
  <div class="app-container">
    <div class="index" v-loading.fullscreen.lock="fullscreenLoading" element-loading-text="拼命加载中...">
      <input
        type="file"
        @change="importFile(this)"
        id="imFile"
        style="display: none"
        accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel"/>
      <a id="downlink"></a>
      <el-button class="button" @click="uploadFile()">导入</el-button>
      <el-button class="button" @click="downloadFile(excelData)">导出</el-button>
      <!--错误信息提示-->
      <el-dialog title="提示" v-model="errorDialog" size="tiny">
        <span>{{errorMsg}}</span>
        <span slot="footer" class="dialog-footer">
   <el-button type="primary" @click="errorDialog=false">确认</el-button>
   </span>
      </el-dialog>
      <!--展示导入信息-->
      <el-table :data="excelData" tooltip-effect="dark">
        <el-table-column label="题型" prop="attachmentType" show-overflow-tooltip></el-table-column>
        <el-table-column label="题干" prop="question" show-overflow-tooltip></el-table-column>
        <el-table-column label="选项A" prop="optionA" show-overflow-tooltip></el-table-column>
        <el-table-column label="选项B" prop="optionB" show-overflow-tooltip></el-table-column>
        <el-table-column label="选项C" prop="optionC" show-overflow-tooltip></el-table-column>
        <el-table-column label="选项D" prop="optionD" show-overflow-tooltip></el-table-column>
        <el-table-column label="解析" prop="analysis" show-overflow-tooltip></el-table-column>
        <el-table-column label="答案" prop="answer" show-overflow-tooltip></el-table-column>
      </el-table>
    </div>
  </div>
</template>

<script type="javascript">
  import { formatDate } from '@/utils/date.js'
  import Pagination from '@/components/Pagination'
  import vueJsonEditor from 'vue-json-editor'
  var XLSX = require('xlsx')
  export default {
    name: 'questionList',
    filters: {
      // statusFilter(status) {
      //   const statusMap = {
      //     published: 'success',
      //     draft: 'gray',
      //     deleted: 'danger'
      //   }
      //   return statusMap[status]
      // },
      formatDate(time) {
        if (time != null && time != '') {
          var date = new Date(Date.parse(time.replace(/-/g, '/')))
          return formatDate(date, 'yyyy-MM-dd')
        } else {
          return ''
        }
      }
    },
    total: 0,
    components: {
      Pagination,
      vueJsonEditor,

    },
    data() {
      return {
        fullscreenLoading: false, // 加载中
        imFile: '', // 导入文件el
        outFile: '', // 导出文件el
        errorDialog: false, // 错误信息弹窗
        errorMsg: '', // 错误信息内容
        excelData: [],

        list: [],
        listLoading: true,
        total: 0,
        listQuery: {
          page: 1,
          limit: 10
        },
        //弹窗
        isInputHidden:false,
        config: {},
        info:{},
        dialogFormVisible: false,
        dialogStatus: '',
        textMap: {
          editConfig: '',
          addConfig: ''
        },
        rules: {}
      }
    },
    created() {

    },
    mounted: function() {
      this.listLoading = true
      this.imFile = document.getElementById('imFile')
      this.outFile = document.getElementById('downlink')
    },
    methods: {
      uploadFile: function () { // 点击导入按钮
        this.imFile.click()
      },
      downloadFile: function (rs) { // 点击导出按钮
        let data = [{}]
        for (let k in rs[0]) {
          data[0][k] = k
        }
        data = data.concat(rs)
        this.downloadExl(data, '菜单')
      },
      importFile: function () { // 导入excel
        this.fullscreenLoading = true
        let obj = this.imFile
        if (!obj.files) {
          this.fullscreenLoading = false
          return
        }
        var f = obj.files[0]
        var reader = new FileReader()
        let _this = this
        reader.onload = function (e) {
          var data = e.target.result
          if (_this.rABS) {
            _this.wb = XLSX.read(btoa(this.fixdata(data)), { // 手动转化
              type: 'base64'
            })
          } else {
            _this.wb = XLSX.read(data, {
              type: 'binary'
            })
          }
          let json = XLSX.utils.sheet_to_json(_this.wb.Sheets[_this.wb.SheetNames[0]])
          console.log(typeof json)
          _this.dealFile(_this.analyzeData(json)) // analyzeData: 解析导入数据
        }
        if (this.rABS) {
          reader.readAsArrayBuffer(f)
        } else {
          reader.readAsBinaryString(f)
        }
      },
      downloadExl: function (json, downName, type) { // 导出到excel
        let keyMap = [] // 获取键
        for (let k in json[0]) {
          keyMap.push(k)
        }
        console.info('keyMap', keyMap, json)
        let tmpdata = [] // 用来保存转换好的json
        json.map((v, i) => keyMap.map((k, j) => Object.assign({}, {
          v: v[k],
          position: (j > 25 ? this.getCharCol(j) : String.fromCharCode(65 + j)) + (i + 1)
        }))).reduce((prev, next) => prev.concat(next)).forEach(function (v) {
          tmpdata[v.position] = {
            v: v.v
          }
        })
        let outputPos = Object.keys(tmpdata) // 设置区域,比如表格从A1到D10
        let tmpWB = {
          SheetNames: ['mySheet'], // 保存的表标题
          Sheets: {
            'mySheet': Object.assign({},
              tmpdata, // 内容
              {
                '!ref': outputPos[0] + ':' + outputPos[outputPos.length - 1] // 设置填充区域
              })
          }
        }
        let tmpDown = new Blob([this.s2ab(XLSX.write(tmpWB,
          {bookType: (type === undefined ? 'xlsx' : type), bookSST: false, type: 'binary'} // 这里的数据是用来定义导出的格式类型
        ))], {
          type: ''
        }) // 创建二进制对象写入转换好的字节流
        var href = URL.createObjectURL(tmpDown) // 创建对象超链接
        this.outFile.download = downName + '.xlsx' // 下载名称
        this.outFile.href = href // 绑定a标签
        this.outFile.click() // 模拟点击实现下载
        setTimeout(function () { // 延时释放
          URL.revokeObjectURL(tmpDown) // 用URL.revokeObjectURL()来释放这个object URL
        }, 100)
      },
      analyzeData: function (data) { // 此处可以解析导入数据
        return data
      },
      dealFile: function (data) { // 处理导入的数据
        console.log(data[0], '处理导入的数据')
        this.imFile.value = ''
        this.fullscreenLoading = false
        if (data.length <= 0) {
          this.errorDialog = true
          this.errorMsg = '请导入正确信息'
        } else {
          this.excelData = data.map(item => {
            const arrItem = {
              attachmentType:"",
              question:"",
              optionA:"",
              optionB:"",
              optionC:"",
              optionD:"",
              analysis:"",
              answer:""
            }
            //将excel文件的标题名称转换,将数据格式转换成数组显示格式
            for (let key in item) {
              if (key === '题型') {
                arrItem.attachmentType = item[key]
              } else if (key === '题干') {
                arrItem.question = item[key]
              } else if (key === '选项A') {
                arrItem.optionA = item[key]
              } else if (key === '选项B') {
                arrItem.optionB = item[key]
              } else if (key === '选项C') {
                arrItem.optionC = item[key]
              }else if (key === '选项D') {
                arrItem.optionD = item[key]
              }else if (key === '解析') {
                arrItem.analysis = item[key]
              }else if (key === '答案') {
                arrItem.answer= item[key]
              }
            }
            return arrItem
          })
        }
      },
      s2ab: function (s) { // 字符串转字符流
        var buf = new ArrayBuffer(s.length)
        var view = new Uint8Array(buf)
        for (var i = 0; i !== s.length; ++i) {
          view[i] = s.charCodeAt(i) & 0xFF
        }
        return buf
      },
      getCharCol: function (n) { // 将指定的自然数转换为26进制表示。映射关系:[0-25] -> [A-Z]。
        let s = ''
        let m = 0
        while (n > 0) {
          m = n % 26 + 1
          s = String.fromCharCode(m + 64) + s
          n = (n - m) / 26
        }
        return s
      },
      fixdata: function (data) { // 文件流转BinaryString
        var o = ''
        var l = 0
        var w = 10240
        for (; l < data.byteLength / w; ++l) {
          o += String.fromCharCode.apply(null, new Uint8Array(data.slice(l * w, l * w + w)))
        }
        o += String.fromCharCode.apply(null, new Uint8Array(data.slice(l * w)))
        return o
      }
    }
  }
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style>
  .el-table th>.cell {
    text-align: center
  }
  .button {
    margin-bottom: 20px
  }
</style>

<think>我们正在处理一个Vue应用中的需求:上传Excel文件,解析内容,数据展示页面上。 根据引用内容,我们可以得知: 1. 引用[1]和[2]提到了通过封装组件实现批量导入,包括文件上传的处理和验证。 2. 引用[3]提到了使用js-xlsx(现在可能是xlsx库)来解析Excel文件,但注意这里说的是导出,解析导入也是类似的。 步骤: 1. 文件上传:使用input type="file"或上传组件(如element-ui的上传组件)让用户选择Excel文件。 2. 文件验证:验证文件类型(如.xls, .xlsx)。 3. 读取文件:使用FileReader读取文件内容。 4. 解析Excel:使用xlsx库解析文件内容,得到JSON数据。 5. 将解析后的数据展示页面上,比如使用表格展示。 注意:引用[3]中提到了懒加载xlsx库,因为它的体积较大。 实现方案: 1. 安装依赖:xlsx ```bash npm install xlsx ``` 2. 创建一个文件上传的组件(或直接在页面中实现),这里以element-ui的上传组件为例,也可以使用原生input。 3. 在文件上传成功后,读取文件解析。 4. 将解析后的数据绑定到页面的表格中。 下面是一个简单的实现示例: 步骤1:模板部分(使用element-ui的上传组件) 步骤2:处理文件上传,读取解析Excel 步骤3:将解析后的数据展示在表格中 具体代码:</think>### Vue 实现 Excel 文件上传、解析数据展示 以下是完整的实现方案,包含文件上传、格式验证、Excel 解析数据展示步骤: #### 1. 安装必要依赖 ```bash npm install xlsx file-saver element-ui ``` #### 2. 文件上传组件 (FileUpload.vue) ```vue <template> <div> <el-upload action="" :auto-upload="false" :show-file-list="false" :on-change="handleFileChange" > <el-button type="primary">选择Excel文件</el-button> </el-upload> <el-table :data="tableData" v-if="tableData.length"> <el-table-column v-for="(header, index) in tableHeaders" :key="index" :prop="header" :label="header" /> </el-table> </div> </template> <script> import * as XLSX from &#39;xlsx&#39;; export default { data() { return { tableData: [], tableHeaders: [] }; }, methods: { handleFileChange(file) { // 验证文件类型 const validTypes = [&#39;xlsx&#39;, &#39;xls&#39;, &#39;csv&#39;]; const extension = file.name.split(&#39;.&#39;).pop().toLowerCase(); if (!validTypes.includes(extension)) { this.$message.error(&#39;仅支持.xlsx, .xls, .csv格式文件&#39;); return; } this.parseExcel(file.raw); }, async parseExcel(file) { try { const reader = new FileReader(); reader.onload = (e) => { const data = new Uint8Array(e.target.result); const workbook = XLSX.read(data, { type: &#39;array&#39; }); // 获取第一个工作表 const firstSheet = workbook.Sheets[workbook.SheetNames[0]]; // 转换为JSON const jsonData = XLSX.utils.sheet_to_json(firstSheet, { header: 1 }); // 提取表头和数据 if (jsonData.length > 0) { this.tableHeaders = jsonData[0]; this.tableData = jsonData.slice(1).map(row => { return this.tableHeaders.reduce((obj, header, index) => { obj[header] = row[index] || &#39;&#39;; return obj; }, {}); }); } }; reader.readAsArrayBuffer(file); } catch (error) { console.error(&#39;解析失败:&#39;, error); this.$message.error(&#39;Excel解析失败&#39;); } } } }; </script> ``` #### 3. 后端处理(Node.js示例) ```javascript // server.js const express = require(&#39;express&#39;); const multer = require(&#39;multer&#39;); const XLSX = require(&#39;xlsx&#39;); const app = express(); const upload = multer({ dest: &#39;uploads/&#39; }); app.post(&#39;/upload&#39;, upload.single(&#39;file&#39;), (req, res) => { try { const workbook = XLSX.readFile(req.file.path); const sheet = workbook.Sheets[workbook.SheetNames[0]]; const data = XLSX.utils.sheet_to_json(sheet); res.json({ success: true, data: data }); } catch (error) { res.status(500).json({ success: false, message: &#39;文件处理失败&#39; }); } }); app.listen(3000, () => console.log(&#39;Server running on port 3000&#39;)); ``` #### 4. 关键实现说明 1. **文件验证**: - 验证文件扩展名(.xlsx/.xls/.csv) - 前端使用`FileReader` API读取文件内容 2. **Excel解析**: - 使用`xlsx`库的`sheet_to_json`方法转换数据 - 表头自动检测(工作表第一行) - 空值处理:`row[index] || &#39;&#39;`确保空单元格转为空字符串 3. **数据展示**: - 动态生成表头(`v-for`遍历`tableHeaders`) - 响应式数据绑定(数据变化自动更新表格) 4. **性能优化**: - 使用`Uint8Array`处理二进制数据提升读取效率 - 前端解析避免不必要的网络请求 #### 5. 效果演示 1. 用户击按钮选择Excel文件 2. 系统自动验证文件格式 3. 前端解析文件内容 4. 数据以表格形式展示: - 首行为表头(自动识别) - 后续行显示数据记录 - 支持大数据量分页(可扩展) #### 6. 常见问题解决 1. **中文乱码问题**: ```javascript // 在读取时指定编码 XLSX.read(data, { type: &#39;array&#39;, codepage: 65001 }); ``` 2. **大文件处理**: ```javascript // 使用Web Worker后台解析 const worker = new Worker(&#39;excelParser.js&#39;); worker.postMessage(file); ``` 3. **复杂格式处理**: ```javascript // 获取单元格原始值 const cellValue = XLSX.utils.format_cell(sheet[&#39;A1&#39;]); ``` [^1]: 引用来源:Vue + elementUi 通过excel文件实现批量导入 [^2]: 引用来源:文件上传处理逻辑 [^3]: 引用来源:js-xlsx库的使用方法
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值