Después de que el proyecto vue se haya importado con éxito a Excel, descargue el resultado de la importación (el flujo de archivos devuelto por el backend)

Requisitos:
en el proyecto vue, la tabla table importa algunos datos en lotes; después de que la importación sea exitosa

componente emergente
inserte la descripción de la imagen aquí

descargar plantilla
inserte la descripción de la imagen aquí

archivo subido
inserte la descripción de la imagen aquí

Una vez que la carga se realiza correctamente, el flujo de archivos devuelto por el backend se descarga automáticamente en el servidor local.
inserte la descripción de la imagen aquí

lista.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>

uploader.view

<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>

exportsheet.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(':')
  }
}

Artículo de referencia:
Excel de exportación front-end, establecer fuente, ancho de columna, alto de fila, método de alineación, combinar celdas y otros efectos

Vue usa js-xlsx, xlsx, xlsxStyle para exportar Excel, que puede fusionar tablas y modificar estilos de cuadrícula, como color de fondo, tamaño de fuente, ancho de columna, etc.

Front-end de xlsx module_pure front-end utiliza el estilo de celda de js-xlsx (4) -Alibaba Cloud Developer Community

estilo xlsx

Supongo que te gusta

Origin blog.csdn.net/guairena/article/details/126001754
Recomendado
Clasificación