前端使用xlsx導出導入文件

就vue來講:

第一步首先要下載xlsx

yarn add xlsx 
yarn add xlsx-style

導入會用到的插件文件解析

yarn add worker-loader
yarn add uuid

下載好了過後,我們需要寫個解析的導入文件:命名爲xlsx.worker.js,然後需要引入到main.js中

xlsx.worker.js代碼附上:

import { read, utils } from 'xlsx';

self.onmessage = e => {
  const { file, id, keyMaps } = e.data;
  const reader = new FileReader();
  reader.onload = e => {
    const workbook = read(e.target.result, {type: 'binary'});
    const Sheets = workbook.Sheets;
    const list = Object.keys(Sheets).map((key, index) => {
      const list = utils.sheet_to_json(Sheets[key]);
      // 某張表的label和key的map
      const keyMap = keyMaps[index];
      return list.map(item => {
        const row = {};
        for (let key in item) {
          const column = keyMap[key];
          const value = item[key];
          if (column) {
            if (typeof column === 'string') {
              row[column] = value + '';
            } else {
              const { type, key: prop } = column;
              if (type === 'number') {
                row[prop] = isNaN(value * 1) ? value : value * 1;
              } else {
                row[prop] = value;
              }
            }
          } else {
            // row[prop] = value;
          }
        }
        return row;
      });
    })
    self.postMessage({ id, list });
  }
  reader.readAsArrayBuffer(file);
}

把該文件引入到main.js中

import XLSXWorker from '@/workers/xlsx.worker';
Vue.prototype.$workers = {
  xlsx: new XLSXWorker()
};

好了,這樣我們就完成引入部分了,就可以開始做導入功能了。

在.vue文件中寫導入和導出功能附上代碼:

<template>
  <a-modal
      title="商品導入"
      :width="1200"
      v-on="$listeners"
      :confirm-loading="loadingBtn"
      :visible="importVisible"
      @cancel="handleCancel"
      @ok="submitAdd">
    <a-form-model :label-col="{ span: 6 }" 
            :wrapper-col="{ span: 16 }"
            style="padding: 0 0 0 20px;">
      <BaseTitle title="設置商品" />
      <a-row>
        <a-col :span="9">
          <a-form-item :label-col="{ span: 4 }" :wrapper-col="{ span: 18 }">
            <a-button type="link" class="right-a" @click="downloadTemplete">下載商品導入模板</a-button>
            <a-upload-dragger name="file" 
                    :file-list="fileList"
                    :multiple="false"
                    :beforeUpload="onBeforeUpload"
                    accept=".xls,.xlsx,.xlsm"
                    action="">
              <div>
                <a-icon type="cloud-upload" style="font-size: 24px; color: @primary-color;" />
                <p>點擊或拖拽商品文件導入</p>
              </div>
            </a-upload-dragger>
          </a-form-item>
        </a-col>
      </a-row>
      <BaseTitle title="導入結果" />
      <a-row>
        <a-table
          :columns="columns"
          :data-source="tableData"
          size="small" />
      </a-row>
    </a-form-model>
  </a-modal>
</template>

<script>
  import { columns } from '@/tables/goods/goodsImport';
  import BaseTitle from '@/components/BaseTitle/index.vue'
  import {goodsService} from '@/service/index'
  import * as xlsx from 'xlsx';
  import { v4 as uuid } from 'uuid';

  const keyMaps = [
    {
      '商品名稱(必填)': 'cnName',
      '倉庫id(必填)': 'warehouseId',
      '品牌id(必填)': 'brandId',
      '國家編碼(必填)': 'originCountryCode',
      '商品規格(必填)': 'goodsModel',
      '毛重(克)(必填)': { key: 'netWeight', type: 'number' },
      '淨重(克)(必填)': { key: 'grossWeight', type: 'number' },
      '幣種(必填)': 'currency',
      '條形碼(必填)': 'barCode',
      '到期時間(必填)': 'expirationDate',
      'hsCode': 'hsCode',
      '銷售價格': { key: 'priceSales', type: 'number' },
      '成本價格': { key: 'priceCost', type: 'number' },
      '法定計量單位': 'unit',
      '商品配料': 'goodsComponent',
      '包裝類型': 'packageType'
    },
    { '倉庫名稱': 'warehouseTitle', '倉庫編碼': 'id' },
    { '品牌名稱': 'brandName', '倉庫編碼': 'brandId' },
    { '國家': 'cnname', '國家編碼': 'isoNcode' },
    { '幣種名稱': 'shortCnname', '幣種編碼': 'customsCode' },
    { '計量單位': 'shortCnname', '計量單位編碼': 'customsCode' }
  ];

  export default {
    components: { BaseTitle },
    model: {
      prop: 'importVisible',
      event: 'change'
    },
    props: {
      importVisible: {type: Boolean, default: false},
      importData: {type:Object, default: ()=> ({})}
    },
    data() {
      return {
        fileList: [],
        tableData: [],
        columns,
        rowSelection: {},
        worderId: uuid(),//商品ID
        loadingBtn: false
      }
    },
    mounted() {
      //導入數據解析結果監聽
      this.$workers.xlsx.onmessage = res => {
        const { id, list } = res.data;
        if (id !== this.worderId) {
          return;
        }
        this.tableData = list[0];
      }
    },
    methods: {
      //下載商品導入模板
      downloadTemplete() {
        //商品列表-sheet1
        let newList = [{
          '商品名稱(必填)': '',
          '倉庫id(必填)': '',
          '品牌id(必填)': '',
          '國家編碼(必填)': '',
          '商品規格(必填)': '',
          '毛重(克)(必填)': 0,
          '淨重(克)(必填)': 0,
          '幣種(必填)': '',
          '條形碼(必填)': '',
          '到期時間(必填)': '',
          'hsCode': '',
          '銷售價格': 0,
          '成本價格': 0,
          '法定計量單位': '',
          '商品配料': '',
          '包裝類型': ''
        }]
        //倉庫對應編碼
        const warehouseList = []
        const warehouseName = {
          warehouseTitle: '倉庫名稱',
          id: '倉庫編碼'
        }
        // 轉換
        if(this.importData.warehouseList.length) {
          this.importData.warehouseList.forEach(item=>{
            let obj = {};
            for(let key in item){
              if(warehouseName[key]){
                obj[warehouseName[key]] = item[key];
              }
            }
            warehouseList.push(obj);
          })
        } else {
          warehouseList.push({'倉庫名稱': '', '倉庫編碼': ''});
        }
        
        //品牌對應編碼
        const bransList = []
        const brandName = {
          brandName: '品牌名稱',
          brandId: '品牌編碼'
        }
        // 轉換
        if(this.importData.brandList.length) {
          this.importData.brandList.forEach(item=>{
            let obj = {};
            for(let key in item){
              if(brandName[key]){
                obj[brandName[key]] = item[key];
              }
            }
            bransList.push(obj);
          })
        } else {
          bransList.push({'品牌名稱': '', '品牌編碼': ''});
        }

        //國家對應編碼
        const countryList = []
        const countryName = {
          cnname: '國家',
          isoNcode: '國家編碼'
        }
        // 轉換
        if(this.importData.addressList.length) {
          this.importData.addressList.forEach(item=>{
            let obj = {};
            for(let key in item){
              if(countryName[key]){
                obj[countryName[key]] = item[key];
              }
            }
            countryList.push(obj);
          })
        } else {
          countryList.push({'國家': '', '國家編碼': ''});
        }

        //幣種對應編碼
        const currencyList = []
        const currencyName = {
          shortCnname: '幣種名稱',
          customsCode: '幣種編碼'
        }
        // 轉換
        if(this.importData.currencyList.length) {
          this.importData.currencyList.forEach(item=>{
            let obj = {};
            for(let key in item){
              if(currencyName[key]){
                obj[currencyName[key]] = item[key];
              }
            }
            currencyList.push(obj);
          })
        } else {
          currencyList.push({'幣種名稱': '', '幣種編碼': ''});
        }

        //法定計量單位對應編碼
        const unitList = []
        const unitName = {
          shortCnname: '計量單位',
          customsCode: '計量單位編碼'
        }
        // 轉換
        if(this.importData.unitList.length) {
          this.importData.unitList.forEach(item=>{
            let obj = {};
            for(let key in item){
              if(unitName[key]){
                obj[unitName[key]] = item[key];
              }
            }
            unitList.push(obj);
          })
        } else {
          unitList.push({'計量單位': '', '計量單位編碼': ''});
        }

        let sheet1 = xlsx.utils.json_to_sheet(newList);//商品列表
        let wscols = [    // 每列不同寬度px
          { wch: 15 },{ wch: 15 },{ wch: 15 },
          { wch: 15 },{ wch: 15 },{ wch: 15 },
          { wch: 15 },{ wch: 15 },{ wch: 15 },
          { wch: 15 },{ wch: 15 },{ wch: 15 },
          { wch: 15 },{ wch: 15 },{ wch: 15 },
          { wch: 15 },{ wch: 15 }
        ];
        // workbook.SheetNames[0]獲取到到是文件裏的到第一個表格
        sheet1["!cols"] = wscols;
        let wsrows = [{ hpx: 20 }];  // 每行固定高度px
        for (let i = 0; i <= this.total; i++) {   // total  列表條數
          wsrows.push({ hpx: 20 });
        }
        sheet1["!rows"] = wsrows;
        let sheet2 = xlsx.utils.json_to_sheet(warehouseList);//倉庫列表
        let sheet3 = xlsx.utils.json_to_sheet(bransList);//品牌列表
        let sheet4 = xlsx.utils.json_to_sheet(countryList);//國家列表
        let sheet5 = xlsx.utils.json_to_sheet(currencyList);//幣種列表
        let sheet6 = xlsx.utils.json_to_sheet(unitList);//法定計量單位列表
        let book = xlsx.utils.book_new();
        xlsx.utils.book_append_sheet(book, sheet1, "商品列表");
        xlsx.utils.book_append_sheet(book, sheet2, "倉庫對應編碼");
        xlsx.utils.book_append_sheet(book, sheet3, "品牌對應編碼");
        xlsx.utils.book_append_sheet(book, sheet4, "國家對應編碼");
        xlsx.utils.book_append_sheet(book, sheet5, "幣種對應編碼");
        xlsx.utils.book_append_sheet(book, sheet6, "法定計量單位對應編碼");
        xlsx.writeFile(book, `iwms_goods_import_template.xlsx`);
      },
      //上傳處理
      onBeforeUpload(file, fileList) {
        this.fileList = [fileList.pop()];
        this.$workers.xlsx.postMessage({
          id: this.worderId,
          keyMaps,
          file,
        });
        return false;
      },
      //保存
      submitAdd() {
        if(!this.fileList.length){
          this.$message.error('請上傳文件')
        }
        this.loadingBtn = true
        goodsService.goodsAddto({goodsData: this.tableData}).then(res => {
          this.loadingBtn = false
          if(res.code !== 200) return
          this.$message.success('導入成功')
          this.handleCancel()
          this.$emit('confirm', confirm);
        })
      },
      handleCancel() {
        this.$emit('cancel');
        this.fileList = [];
        this.tableData = []
      }
    }
  }
</script>
<style scoped lang="less">
.right-a { margin-right: 10px;}
/deep/.ant-btn-link{padding:0;}
</style>
View Code

下載的模板圖片:

 

 

最後圖片:

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章