vue プロジェクトが Excel に正常にインポートされたら、インポート結果 (バックエンドによって返されたファイル ストリーム) をダウンロードします。

要件:
vue プロジェクトでは、インポートが成功した後、テーブル table がいくつかのデータをバッチでインポートします。

ポップアップコンポーネント
ここに画像の説明を挿入

テンプレートをダウンロード
ここに画像の説明を挿入

アップロードされたファイル
ここに画像の説明を挿入

アップロードが成功すると、バックエンドから返されたファイル ストリームが自動的にローカル ファイルにダウンロードされます。
ここに画像の説明を挿入

list.vue

// shop_group_web:src/module/views/prodManaged/list.vue
<template>
	<el-button type="primary" @click="add" size="mini">批量上下架</el-button>

	<up-loader ref="addUploader" @downLoad="addExcelExport" @upLoad="addExcelImport"></up-loader>
</template>

<script>
import UpLoader from "@/module/components/uploader/uploader.vue"
import eportsheet from '@/module/components/exportsheet/exportsheet'

export default {
    
    
  components: {
    
     UpLoader },
  data() {
    
    
	return {
    
    
		importAddConfig: {
    
    
	        skuCode: '商品编码(SGU)',
	        priceStatusStr: '上下架状态',
	    },
	}
  }
}
methods: {
    
    
    add() {
    
    
      this.$refs.addUploader.open()
    },
    addExcelExport() {
    
    
      eportsheet.exportFileByConfig([{
    
    }], `批量上下架模板.xlsx`, this.importAddConfig)
    },
    async addExcelImport(file) {
    
    
      let parseConfig = {
    
    }
      for (let key in this.importAddConfig) {
    
    
        parseConfig[this.importAddConfig[key]] = key
      }
      await eportsheet.parseToJSON(file, parseConfig).then(result => {
    
    

        if(!result){
    
    
          this.$notify.error({
    
     title: '提示', message:"上传文件数据解析为空" })
          return false
        }
        // 循环excel中的每一项
        for(let i=0; i<result.length; i++){
    
    
          let row = result[i]
          if(!row.skuCode){
    
    
            this.$notify.error({
    
     title: '提示', message:"商品编码(SGU)不能为空" })
            return false
          }
          if(!row.priceStatusStr){
    
    
            this.$notify.error({
    
     title: '提示', message:"上下架状态不能为空" })
            return false
          }
        }
        // 将excel的数据转成数组后传给后端,后端返回操作结果的文件流
        this.$http.post(`${
      
      api.updateStatusByList}ForExcel`, result, {
    
    }, 'blob').then(res => {
    
    
          if (res) {
    
    
            this.$refs.addUploader.close()
            this.$notify.success({
    
    title: '操作提示',message: "上传成功"})
            this.search()
            this.exportLoading = false
              var blob = new Blob([res], {
    
    type: 'application/ms-excel'});
              var elink = document.createElement('a');
              elink.download = '批量上下架结果' + '.xlsx';
              elink.href = URL.createObjectURL(blob);
              document.body.appendChild(elink);
              elink.click(); //点击下载
              document.body.removeChild(elink); //下载完成移除元素
              window.URL.revokeObjectURL(elink); //释放掉blob对象
              this.loading = false
              return
          } else {
    
    
            this.$notify.error({
    
    
              title: '提示',
              message: res.msg
            })
          }
        })
      })
    },
    
}
</script>

アップローダー.ビュー

<template>
  <!--批量导入弹窗-->
  <el-dialog :visible.sync="visible" size="large" title="批量导入" :loading="loadingInfo">
    <el-row class="mv10">
      <el-col :span="24">
        <label :for="`excelFile${tmp}`" class="excelFileLabel">
          <span class="file-label-text">请选择文件</span>
          <input accept=".xlm,.xls,.xlt,.xlw,.xlsx" type="file" :id="`excelFile${tmp}`" hidden @change="handleFileSelect" :value="fileValue" />
          <el-input class="search-input" v-model="file.name" disabled></el-input>
        </label>
        <el-button style="margin-left: 10px" size="small" type="text" @click="exportTemp">下载模版</el-button>
      </el-col>
    </el-row>
    <el-row>
      <el-col :span="24">
        <el-button style="margin-top: 20px" :loading="loadingInfo" size="small" type="primary" @click="submitUpload">导入
        </el-button>
        <el-button @click="exportErrorFile" v-if="upStatus.code">下载错误列表</el-button>
      </el-col>
    </el-row>
    <el-row class="mt20" v-if="upStatus.code">
      <el-col :span="24" style="color:red">
        <p>{
    
    {
    
    file.name}} 导入失败,请修改后重新上传</p>
        <p>失败原因:{
    
    {
    
    upStatus.msg}}</p>
      </el-col>
    </el-row>
  </el-dialog>
</template>
<script>

export default {
    
    
  name: 'upLoader',
  props: {
    
    
    loadingInfo: {
    
     type: Boolean, default: false }
  },
  data () {
    
    
    return {
    
    
      tmp: Date.now(),
      visible: false,
      fileValue: '',
      loading: false,
      file: {
    
     name: '' },
      upStatus: {
    
    
        code: '',
        msg: '',
        data: []
      },
      tempConfig: {
    
    }
    }
  },
  methods: {
    
    
    handleFileSelect (e) {
    
    
      this.file = e.target.files[0] || {
    
     name: '' }
      // 如果文件改变需要初始化上传状态
      this.upStatus = {
    
     code: '', msg: '', data: [] }
    },
    exportTemp () {
    
    
      try {
    
    
        this.$emit('downLoad')
      } catch (e) {
    
    
        this.$notify.error({
    
    
          title: '提示',
          message: '模板下载遇到错误'
        })
      }
    },
    submitUpload () {
    
    
      if (!this.file.name || this.file.name.indexOf(".xl") === -1) {
    
    
        this.$notify.warning({
    
    
          title: '提示',
          message: '请选择excel文件'
        })
        return
      }
      this.$emit('upLoad', this.file)
    },
    open () {
    
    
      this.visible = true
      this.fileValue = ''
      this.file = {
    
     name: '' }
    },
    close () {
    
    
      this.visible = false
    }
  }
}
</script>
<style scoped>
.file-label-text {
    
    
  cursor: pointer;
  color: #409eff;
}
</style>

エクスポートシート.js

import XLSX from 'xlsx'
import * as util from '@/utils/utils'

// js导出execl
function s2ab (s) {
    
    
  if (typeof ArrayBuffer !== 'undefined') {
    
    
    let buf = new ArrayBuffer(s.length)
    let view = new Uint8Array(buf)
    for (let i = 0; i !== s.length; ++i) {
    
    
      view[i] = s.charCodeAt(i) & 0xFF
    }
    return buf
  } else {
    
    
    let buf = new Array(s.length)
    for (let i = 0; i !== s.length; ++i) {
    
    
      buf[i] = s.charCodeAt(i) & 0xFF
    }
    return buf
  }
}

// 将指定的自然数转换为26进制表示。映射关系:[0-25] -> [A-Z]。
function getCharCol (n) {
    
    
  let s = ''
  let m = 0
  while (n > 0) {
    
    
    m = n % 26 + 1
    s = String.fromCharCode(m + 64) + s
    n = (n - m) / 26
  }
  return s
}

function exportFileByConfig (json, fileName, exportConfig, extraData = []) {
    
    
  if (exportConfig) {
    
    
    json = json.map((item, index) => {
    
    
      let newItem = {
    
    }
      for (let k of Object.keys(exportConfig)) {
    
    
        let value = item[k]
        if (value === true) {
    
    
          value = '是'
        }
        if (value === false) {
    
    
          value = '否'
        }
        if (value === null || value === undefined) {
    
    
          value = ''
        }
        newItem[exportConfig[k]] = value
      }
      return newItem
    })
  }
  createFile(json, fileName, extraData)
}

// 建议使用exportFileByConfig方法,可以控制顺序
function exportFile (json, fileName, exportConfig, extraData = []) {
    
    
  if (exportConfig) {
    
    
    json = json.map((item, index) => {
    
    
      let newItem = {
    
    }
      // 设置导出的表头名称
      for (let k of Object.keys(item)) {
    
    
        if (!(k in exportConfig)) {
    
    
          // newItem[k] = item[k] === null ? '' : (item[k] === true ? '是' : '否')  //将本句注释取消可将未配置字段也导出
          continue
        }
        let value = item[k]
        if (value === true) {
    
    
          value = '是'
        }
        if (value === false) {
    
    
          value = '否'
        }
        if (value === null || value === undefined) {
    
    
          value = ''
        }
        newItem[exportConfig[k]] = value
      }
      return newItem
    })
  }
  createFile(json, fileName, extraData)
}

function createFile (json, fileName, extraList) {
    
    
  let _tmpdata = json[0]
  json.unshift({
    
    })
  var keyMap = [] // 获取keys
  for (var k in _tmpdata) {
    
    
    keyMap.push(k)
    json[0][k] = k
  }
  let tmpdata = [] // 用来保存转换好的json
  let blankNum = extraList.length ? 2 : 1
  json.map((v, i) => keyMap.map((k, j) => Object.assign({
    
    }, {
    
    
    v: v[k],
    // position: (j > 25 ? getCharCol(j) : String.fromCharCode(65 + j)) + (i + 1 + extraList.length) // 开头加个空行
    position: (j > 25 ? getCharCol(j) : String.fromCharCode(65 + j)) + (i + blankNum)
  }))).reduce((prev, next) => prev.concat(next)).forEach((v, i) => {
    
    
    tmpdata[v.position] = {
    
    
      v: v.v
    }
  })
  let temObj = {
    
    }
  for (let i = 0; i < extraList.length; i++) {
    
    
    let item = extraList[i]
    for (let j = 0; j < item.length; j++) {
    
    
      let key = String.fromCharCode(65 + i) + (j + 1)
      temObj[key] = {
    
    v: item[j].label + item[j].value}
    }
  }
  tmpdata = Object.assign(temObj, tmpdata)
  var outputPos = Object.keys(tmpdata) // 设置区域,比如表格从A1到D10
  var tmpWB = {
    
    
    SheetNames: ['mySheet'], // 保存的表标题
    Sheets: {
    
    
      'mySheet': Object.assign({
    
    },
        tmpdata, // 内容
        {
    
    
          '!ref': 'A1' + ':' + outputPos[outputPos.length - 1] // 设置填充区域
        })
    }
  }
  let wbout = XLSX.write(tmpWB, {
    
    bookType: 'xlsx', type: 'binary'})
  /* force a download */
  let tmpDown = new Blob([s2ab(wbout)], {
    
    type: 'application/octet-stream'})
  var href = URL.createObjectURL(tmpDown) // 创建对象超链接
  var a = document.createElement('a')
  a.href = href // 绑定a标签
  a.download = fileName || `导出数据${
      
      util.formatDate(new Date())}.xlsx`
  document.body.appendChild(a)
  a.click() // 模拟点击实现下载
  document.body.removeChild(a)
  setTimeout(function () {
    
     // 延时释放
    URL.revokeObjectURL(tmpDown) // 用URL.revokeObjectURL()来释放这个object URL
  }, 100)
}

function toJSON (excelFile, config) {
    
    
  return new Promise((resolve, reject) => {
    
    
    if (window.FileReader) {
    
    
      let result = []
      let fr = new FileReader()
      fr.readAsBinaryString(excelFile)
      fr.onload = ev => {
    
    
        try {
    
    
          let data = ev.target.result
          // 以二进制形式读取excel文件
          let workbook = XLSX.read(data, {
    
    
            type: 'binary'
          })
          // 遍历每个表
          for (let sheet of workbook.SheetNames) {
    
    
            if (workbook.Sheets[sheet]) {
    
    
              // 修改field配置
              result = config ? result.concat(XLSX.utils.sheet_to_json(workbook.Sheets[sheet], config)) : result.concat(XLSX.utils.sheet_to_json(workbook.Sheets[sheet]))
            }
          }
          resolve(result)
        } catch (e) {
    
    
          return reject(new Error('文件类型不正确'))
        }
      }
      return
    }
    reject(new Error('该浏览器不支持该功能,请更换或升级浏览器'))
  })
}

/*
 *  @excelFile File
 *  @config Object 解析字段
 *  @setter Object XLSX.utils.sheet_to_json的配置
*/
function parseToJSON (excelFile, config, setter = {
     
     }) {
    
    
  return new Promise((resolve, reject) => {
    
    
    if (window.FileReader) {
    
     // 判断浏览器是否支持FileReader对象
      let result = []
      let fr = new FileReader() // 新建文件读取
      fr.readAsBinaryString(excelFile) // 将文件读取二进制码
      fr.onload = ev => {
    
     // 数据读取成功完成时触发
        try {
    
    
          let data = ev.target.result
          // 以二进制流方式读取得到整份excel表格对象
          let workbook = XLSX.read(data, {
    
    
            type: 'binary'
          })
          // 只遍历第一个表
          let name = workbook.SheetNames[0]
          let sheet = workbook.Sheets[name]
          if (sheet) {
    
    
            result = XLSX.utils.sheet_to_json(sheet, {
    
    })
            if (config) {
    
    
              for (let item of result) {
    
    
                for (let excelTitleKey in config) {
    
    
                  item[config[excelTitleKey]] = ''
                }
              }
              result.forEach(item => {
    
    
                for (let key in item) {
    
    
                  if (config[key]) {
    
    
                    item[config[key]] = item[key].toString().trim()
                    delete item[key]
                  }
                }
              })
              // 去空行
              if (result && result.length) {
    
    
                let keyList = Object.keys(result[0])
                for (let i = result.length - 1; i > 0; i--) {
    
    
                  let value = ''
                  for (let key of keyList) {
    
    
                    value += result[i][key]
                  }
                  if (value.trim() === '') {
    
    
                    result.splice(i, 1)
                  }
                }
              }
            }
          }
          resolve(result)
        } catch (e) {
    
    
          reject(new Error('文件类型不正确'))
        }
      }
      return
    }
    reject(new Error('该浏览器不支持该功能,请更换或升级浏览器'))
  })
}

export default {
    
    
  exportFile,
  exportFileByConfig,
  toJSON,
  parseToJSON
}


utils/utils.js

/**
 * formatDate 格式化日期 yyyy-MM-dd ios默认是utc时间比gmt少8个小时  2017-11-30新增 支持解析到秒
 * @param str
 * @param seconds
 * @returns {string}
 */
export function formatDate(str, seconds, isNow = false) {
    
    
  if (!str || str.toString().trim() === '') {
    
    
    return ''
  }

  function parse(n) {
    
    
    return n < 10 ? ('0' + n) : n
  }

  let dt = new Date(str)
  let y = dt.getFullYear()
  let m = dt.getMonth() + 1
  let d = dt.getDate()
  if (!seconds) {
    
    
    return [y, parse(m), parse(d)].join('-')
  } else if (seconds && isNow) {
    
    
    let nowDate = new Date()
    let h = nowDate.getHours()
    let M = nowDate.getMinutes()
    let s = nowDate.getSeconds()
    return [y, parse(m), parse(d)].join('-') + ' ' + [parse(h), parse(M), parse(s)].join(':')
  } else {
    
    
    let h = dt.getHours()
    let M = dt.getMinutes()
    let s = dt.getSeconds()
    return [y, parse(m), parse(d)].join('-') + ' ' + [parse(h), parse(M), parse(s)].join(':')
  }
}

参考記事:
フロントエンドExcelエクスポート、フォント、列幅、行高さ、配置方法、セルの結合などの効果の設定

Vue は js-xlsx、xlsx、xlsxStyle を使用して Excel をエクスポートします。これにより、テーブルを結合したり、背景色、フォント サイズ、列幅などのグリッド スタイルを変更したりできます。

xlsx のフロントエンド module_pure フロントエンドは js-xlsx のセル スタイルを使用します (4) - Alibaba Cloud 開発者コミュニティ

xlsx スタイル

おすすめ

転載: blog.csdn.net/guairena/article/details/126001754