Browse Source

图片再处理初步调整

zhangjie 3 years ago
parent
commit
3cce55515c

+ 1 - 0
package.json

@@ -24,6 +24,7 @@
     "gm": "^1.23.1",
     "imagemagick": "^0.1.3",
     "log4js": "^6.3.0",
+    "node-xlsx": "^0.21.0",
     "sqlite3": "^4.2.0",
     "view-design": "^4.2.0",
     "vue": "^2.6.11",

+ 2 - 0
src/modules/cropper-task/CropperTaskDetailDialog.vue

@@ -176,6 +176,7 @@ export default {
       document.getElementById("task-list").scrollTop = scrollTop;
     },
     getTaskOutputFilePath(taskPath) {
+      // TODO:
       const paperPath = taskPath.replace(this.cropperTask.inputDir, "");
       const types = ["sheet", "images", "thumbs"];
       return types.map(item =>
@@ -183,6 +184,7 @@ export default {
       );
     },
     async toDo(task) {
+      // TODO:操作流程与正常采集过程保持一致,需要点时间整理,数据表需要再调整。
       const [sheetPath, imagesPath, thumbsPath] = this.getTaskOutputFilePath(
         task.filepath
       );

+ 1 - 6
src/modules/cropper-task/CropperTaskManage.vue

@@ -61,15 +61,10 @@ export default {
           minWidth: 120
         },
         {
-          title: "输入目录",
+          title: "导入文件",
           key: "inputDir",
           minWidth: 200
         },
-        {
-          title: "导出目录",
-          key: "outputDir",
-          minWidth: 200
-        },
         {
           title: "任务总量",
           key: "taskCount",

+ 92 - 63
src/modules/cropper-task/ModifyCropperTask.vue

@@ -21,28 +21,17 @@
           clearable
         ></Input>
       </FormItem>
-      <FormItem prop="inputDir" label="输入目录:">
+      <FormItem prop="inputFile" label="导入文件:">
         <Input
-          v-model="modalForm.inputDir"
+          v-model="modalForm.inputFile"
           readonly
-          placeholder="请选择输入目录"
+          placeholder="请选择导入文件"
           style="width: 500px; margin-right: 15px"
         ></Input>
         <Button class="export-paper-btn" type="primary" @click="seleteInputpath"
           >选择</Button
         >
       </FormItem>
-      <FormItem prop="outputDir" label="导出目录:">
-        <Input
-          v-model="modalForm.outputDir"
-          readonly
-          placeholder="请选择导出目录"
-          style="width: 500px; margin-right: 15px"
-        ></Input>
-        <Button class="export-paper-btn" type="primary" @click="seleteOutpath"
-          >选择</Button
-        >
-      </FormItem>
     </Form>
     <div slot="footer">
       <Button type="text" @click="cancel">取消</Button>
@@ -52,7 +41,6 @@
 </template>
 
 <script>
-import { getValidFiles } from "./taskUtils";
 import {
   addCropperTask,
   updateCropperTask,
@@ -60,13 +48,13 @@ import {
   deleteCropperTaskDetail
 } from "../../plugins/db";
 const remote = require("electron").remote;
-const path = require("path");
+const fs = require("fs");
+const nodeXlsx = require("node-xlsx");
 
 const initModalForm = {
   id: "",
   name: "",
-  inputDir: "",
-  outputDir: ""
+  inputFile: ""
 };
 
 export default {
@@ -100,17 +88,10 @@ export default {
             trigger: "change"
           }
         ],
-        inputDir: [
+        inputFile: [
           {
             required: true,
-            message: "请选择输入目录",
-            trigger: "change"
-          }
-        ],
-        outputDir: [
-          {
-            required: true,
-            message: "请选择导出目录",
+            message: "请选择导入文件",
             trigger: "change"
           }
         ]
@@ -140,49 +121,85 @@ export default {
       remote.dialog.showOpenDialog(
         {
           title: "选择输入目录",
+          filters: [{ name: "excel", extensions: ["xls", "xlsx"] }],
           properties: ["openDirectory"]
         },
         folderPaths => {
           if (folderPaths && folderPaths[0]) {
-            this.modalForm.inputDir = folderPaths[0];
+            this.modalForm.inputFile = folderPaths[0];
           }
         }
       );
     },
-    seleteOutpath() {
-      remote.dialog.showOpenDialog(
-        {
-          title: "选择导出目录",
-          properties: ["openDirectory"]
-        },
-        folderPaths => {
-          if (folderPaths && folderPaths[0]) {
-            this.modalForm.outputDir = folderPaths[0];
+    checkData(data) {
+      // let error
+      const columnNames = ["工作id", "科目", "科目名称", "考号", "姓名"];
+      const columnCodes = [
+        "examId",
+        "subjectId",
+        "subjectName",
+        "examNumber",
+        "studentName"
+      ];
+      const needCheckColumn = [
+        "examId",
+        "subjectId",
+        "examNumber",
+        "studentName"
+      ];
+      let errMsg = [];
+      data.forEach((item, index) => {
+        let itemErrorMsg = [];
+        item.forEach((elem, eindex) => {
+          if (needCheckColumn.includes(columnCodes[eindex]) && !elem) {
+            itemErrorMsg.push(columnNames[eindex]);
           }
+        });
+        if (itemErrorMsg.length) {
+          errMsg.push(`第${index + 2}行,${itemErrorMsg.join("、")}不能为空。`);
         }
-      );
+      });
+      if (errMsg.length) {
+        this.$Message.error(errMsg.join(""));
+        return;
+      }
+      return true;
     },
     async submit() {
-      const valid = await this.$refs.modalFormComp.validate();
-      if (!valid) return;
-
       if (this.isSubmit) return;
       this.isSubmit = true;
 
-      const taskList = await getValidFiles(this.modalForm.inputDir).catch(
-        () => {
-          this.isSubmit = false;
-        }
-      );
-      console.log(taskList);
-      if (!taskList || !taskList.length) {
-        this.$Message.error("可操作文件获取失败,请重新选择输入目录!");
+      const valid = await this.$refs.modalFormComp.validate();
+      if (!valid) {
+        this.isSubmit = false;
         return;
       }
 
-      const datas = { ...this.modalForm, taskCount: taskList.length };
+      if (!fs.existsSync(this.modalForm.inputFile)) {
+        this.$Message.error("导入文件不存在!");
+        this.isSubmit = false;
+        return;
+      }
+
+      const data = nodeXlsx.parse(this.modalForm.inputFile);
+      const validColumns = data[0].data
+        .slice(1)
+        .filter(item => item.some(elem => elem));
+
+      if (!validColumns.length) {
+        this.$Message.error("没有可操作数据,请重新选择导入文件!");
+        this.isSubmit = false;
+        return;
+      }
+
+      if (!this.checkData(validColumns)) {
+        this.isSubmit = false;
+        return;
+      }
+
+      const datas = { ...this.modalForm, taskCount: validColumns.length };
       let cropperTaskId = this.modalForm.id;
-      if (this.isEdit) {
+      if (cropperTaskId) {
         await updateCropperTask(datas);
         await deleteCropperTaskDetail(this.instance.id);
       } else {
@@ -190,22 +207,34 @@ export default {
       }
 
       // 记录任务详情数据
-      const len = taskList.length;
+      const len = validColumns.length;
       const pageSize = 100;
       const pageCount = Math.ceil(len / pageSize);
       for (let i = 0; i < pageCount; i++) {
         const startNo = i * pageSize;
-        const datas = taskList.slice(startNo, startNo + pageSize).map(item => {
-          return {
-            cropperTaskId,
-            filename: path.basename(item),
-            filepath: item,
-            sheetPath: "",
-            imagesPath: "",
-            thumbsPath: "",
-            cropperSet: "{}"
-          };
-        });
+        const datas = validColumns
+          .slice(startNo, startNo + pageSize)
+          .map(item => {
+            const [
+              examId,
+              subjectId,
+              subjectName,
+              examNumber,
+              studentName
+            ] = item;
+            return {
+              cropperTaskId,
+              examId,
+              subjectId,
+              subjectName,
+              examNumber,
+              studentName,
+              sheetPath: "",
+              imagesPath: "",
+              thumbsPath: "",
+              cropperSet: "{}"
+            };
+          });
         await addCropperTaskDetail(datas).catch(error => {
           console.log(error);
         });

+ 25 - 25
src/modules/cropper-task/taskUtils.js

@@ -67,26 +67,26 @@ export const getInputFirstPaper = folderPath => {
   });
 };
 
-function saveThumbsImage(imgPath, outputThumbsPath) {
-  const outputThumbsDir = path.dirname(outputThumbsPath);
-  if (!fs.existsSync(outputThumbsDir)) makeDirSync(outputThumbsDir);
-
-  // slice图:完整处理流程
-  let imgObj = gm(imgPath);
-
-  // 生成缩略图
-  imgObj.resize(500).quality(80);
-
-  return new Promise((resolve, reject) => {
-    imgObj.write(outputThumbsPath, function(err) {
-      if (err) {
-        reject(err);
-        return;
-      }
-      resolve(outputThumbsPath);
-    });
-  });
-}
+// function saveThumbsImage(imgPath, outputThumbsPath) {
+//   const outputThumbsDir = path.dirname(outputThumbsPath);
+//   if (!fs.existsSync(outputThumbsDir)) makeDirSync(outputThumbsDir);
+
+//   // slice图:完整处理流程
+//   let imgObj = gm(imgPath);
+
+//   // 生成缩略图
+//   imgObj.resize(500).quality(80);
+
+//   return new Promise((resolve, reject) => {
+//     imgObj.write(outputThumbsPath, function(err) {
+//       if (err) {
+//         reject(err);
+//         return;
+//       }
+//       resolve(outputThumbsPath);
+//     });
+//   });
+// }
 
 function saveSliceImage(paperInfo, collectConfig) {
   const imgPath = paperInfo.filepath;
@@ -147,10 +147,10 @@ function saveSliceImage(paperInfo, collectConfig) {
 
 export async function saveCropperImage(paperInfo, collectConfig) {
   const outputSlicelPath = await saveSliceImage(paperInfo, collectConfig);
-  const outputThumbsPath = await saveThumbsImage(
-    outputSlicelPath,
-    paperInfo.thumbsPath
-  );
+  // const outputThumbsPath = await saveThumbsImage(
+  //   outputSlicelPath,
+  //   paperInfo.thumbsPath
+  // );
 
-  return { outputSlicelPath, outputThumbsPath };
+  return { outputSlicelPath };
 }

+ 15 - 11
src/plugins/db.js

@@ -586,11 +586,10 @@ export function deleteCropperTaskDetail(cropperTaskId) {
 }
 
 export function addCropperTask(params) {
-  const sql = `INSERT INTO cropper_task (name,inputDir,outputDir,taskCount,finishedCount,createTime,updateTime) VALUES (?,?,?,?,?,?,?)`;
+  const sql = `INSERT INTO cropper_task (name,inputFile,taskCount,finishedCount,createTime,updateTime) VALUES (?,?,?,?,?,?)`;
   const datas = [
     params.name,
-    params.inputDir,
-    params.outputDir,
+    params.inputFile,
     params.taskCount,
     0,
     formatDate(), // createTime
@@ -606,12 +605,11 @@ export function addCropperTask(params) {
 }
 
 export function updateCropperTask(params) {
-  const sql = `UPDATE cropper_task SET name=$name,inputDir=$inputDir,outputDir=$outputDir,taskCount=$taskCount,finishedCount=$finishedCount,updateTime=$updateTime WHERE id=$id`;
+  const sql = `UPDATE cropper_task SET name=$name,inputFile=$inputFile,taskCount=$taskCount,finishedCount=$finishedCount,updateTime=$updateTime WHERE id=$id`;
   const datas = {
     $id: params.id,
     $name: params.name,
-    $inputDir: params.inputDir,
-    $outputDir: params.outputDir,
+    $inputFile: params.inputFile,
     $taskCount: params.taskCount,
     $finishedCount: 0,
     $updateTime: formatDate()
@@ -661,8 +659,11 @@ export function getCropperTaskFinishCount(cropperTaskId) {
  * 
  *CREATE TABLE "cropper_task_detail" (
   "id" INTEGER NOT NULL,
-  "filename" TEXT NOT NULL ,
-  "filepath" TEXT NOT NULL,
+  "examId" INTEGER NOT NULL,
+  "subjectId" text NOT NULL,
+  "subjectName" text NOT NULL,
+  "examNumber" text NOT NULL,
+  "studentName" TEXT NOT NULL,
   "sheetPath" TEXT NOT NULL,
   "imagesPath" TEXT NOT NULL,
   "thumbsPath" TEXT NOT NULL,
@@ -695,8 +696,11 @@ export function addCropperTaskDetail(paramList) {
   const listVals = paramList.map(params => {
     const datas = [
       params.cropperTaskId,
-      params.filename,
-      params.filepath,
+      params.examId,
+      params.subjectId,
+      params.subjectName,
+      params.examNumber,
+      params.studentName,
       params.sheetPath,
       params.imagesPath,
       params.thumbsPath,
@@ -712,7 +716,7 @@ export function addCropperTaskDetail(paramList) {
   });
   const vals = listVals.join(",");
 
-  const sql = `INSERT INTO cropper_task_detail (cropperTaskId,filename,filepath, sheetPath, imagesPath,thumbsPath,cropperSet,isFinished,createTime,updateTime) VALUES ${vals}`;
+  const sql = `INSERT INTO cropper_task_detail (cropperTaskId,examId, subjectId,subjectName,examNumber,studentName,sheetPath, imagesPath,thumbsPath,cropperSet,isFinished,createTime,updateTime) VALUES ${vals}`;
 
   return new Promise((resolve, reject) => {
     db.run(sql, function(err) {

+ 1 - 0
src/plugins/imageUpload.js

@@ -28,6 +28,7 @@ function toUploadImg(options, type) {
   const file = new File([buffer], options.examNumber + ".jpg");
   formData.append("file", file);
   formData.append("level", options.level);
+  formData.append("scanUserId", options.clientUserId);
 
   return type === "formal"
     ? uploadFormalImage(options, formData, { headers: { md5 } })

+ 83 - 0
yarn.lock

@@ -1395,6 +1395,19 @@ address@^1.1.2:
   resolved "https://registry.npm.taobao.org/address/download/address-1.1.2.tgz#bf1116c9c758c51b7a933d296b72c221ed9428b6"
   integrity sha1-vxEWycdYxRt6kz0pa3LCIe2UKLY=
 
+adler-32@~1.2.0:
+  version "1.2.0"
+  resolved "https://registry.npmmirror.com/adler-32/-/adler-32-1.2.0.tgz#6a3e6bf0a63900ba15652808cb15c6813d1a5f25"
+  integrity sha512-/vUqU/UY4MVeFsg+SsK6c+/05RZXIHZMGJA+PX5JyWI0ZRcBpupnRuPLU/NXXoFwMYCPCoxIfElM2eS+DUXCqQ==
+  dependencies:
+    exit-on-epipe "~1.0.1"
+    printj "~1.1.0"
+
+adler-32@~1.3.0:
+  version "1.3.1"
+  resolved "https://registry.npmmirror.com/adler-32/-/adler-32-1.3.1.tgz#1dbf0b36dda0012189a32b3679061932df1821e2"
+  integrity sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==
+
 aggregate-error@^3.0.0:
   version "3.0.1"
   resolved "https://registry.npm.taobao.org/aggregate-error/download/aggregate-error-3.0.1.tgz?cache=0&sync_timestamp=1570167911603&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Faggregate-error%2Fdownload%2Faggregate-error-3.0.1.tgz#db2fe7246e536f40d9b5442a39e117d7dd6a24e0"
@@ -2368,6 +2381,14 @@ caseless@~0.12.0:
   resolved "https://registry.npm.taobao.org/caseless/download/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
   integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=
 
+cfb@^1.1.4:
+  version "1.2.2"
+  resolved "https://registry.npmmirror.com/cfb/-/cfb-1.2.2.tgz#94e687628c700e5155436dac05f74e08df23bc44"
+  integrity sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==
+  dependencies:
+    adler-32 "~1.3.0"
+    crc-32 "~1.2.0"
+
 chalk@^1.0.0, chalk@^1.1.3:
   version "1.1.3"
   resolved "https://registry.npm.taobao.org/chalk/download/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
@@ -2614,6 +2635,11 @@ code-point-at@^1.0.0:
   resolved "https://registry.npm.taobao.org/code-point-at/download/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
   integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
 
+codepage@~1.15.0:
+  version "1.15.0"
+  resolved "https://registry.npmmirror.com/codepage/-/codepage-1.15.0.tgz#2e00519024b39424ec66eeb3ec07227e692618ab"
+  integrity sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==
+
 collection-visit@^1.0.0:
   version "1.0.0"
   resolved "https://registry.npm.taobao.org/collection-visit/download/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0"
@@ -2875,6 +2901,11 @@ cosmiconfig@^5.0.0, cosmiconfig@^5.2.1:
     js-yaml "^3.13.1"
     parse-json "^4.0.0"
 
+crc-32@~1.2.0:
+  version "1.2.2"
+  resolved "https://registry.npmmirror.com/crc-32/-/crc-32-1.2.2.tgz#3cad35a934b8bf71f25ca524b6da51fb7eace2ff"
+  integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==
+
 crc32-stream@^2.0.0:
   version "2.0.0"
   resolved "https://registry.npm.taobao.org/crc32-stream/download/crc32-stream-2.0.0.tgz#e3cdd3b4df3168dd74e3de3fbbcb7b297fe908f4"
@@ -4080,6 +4111,11 @@ execa@^3.3.0:
     signal-exit "^3.0.2"
     strip-final-newline "^2.0.0"
 
+exit-on-epipe@~1.0.1:
+  version "1.0.1"
+  resolved "https://registry.npmmirror.com/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz#0bdd92e87d5285d267daa8171d0eb06159689692"
+  integrity sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw==
+
 expand-brackets@^2.1.4:
   version "2.1.4"
   resolved "https://registry.npm.taobao.org/expand-brackets/download/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622"
@@ -4472,6 +4508,11 @@ forwarded@~0.1.2:
   resolved "https://registry.npm.taobao.org/forwarded/download/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84"
   integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=
 
+frac@~1.1.2:
+  version "1.1.2"
+  resolved "https://registry.npmmirror.com/frac/-/frac-1.1.2.tgz#3d74f7f6478c88a1b5020306d747dc6313c74d0b"
+  integrity sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==
+
 fragment-cache@^0.2.1:
   version "0.2.1"
   resolved "https://registry.npm.taobao.org/fragment-cache/download/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19"
@@ -6806,6 +6847,13 @@ node-releases@^1.1.53:
   resolved "https://registry.npm.taobao.org/node-releases/download/node-releases-1.1.55.tgz?cache=0&sync_timestamp=1588708673007&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fnode-releases%2Fdownload%2Fnode-releases-1.1.55.tgz#8af23b7c561d8e2e6e36a46637bab84633b07cee"
   integrity sha1-ivI7fFYdji5uNqRmN7q4RjOwfO4=
 
+node-xlsx@^0.21.0:
+  version "0.21.0"
+  resolved "https://registry.npmmirror.com/node-xlsx/-/node-xlsx-0.21.0.tgz#085d69869d7fa7ed1e6d2a13eee6aa6dce7867a1"
+  integrity sha512-MB+KcNCuRzwjgr17scpKiVTPd4Vbj3V+7QwKpqACGyJzhvC67xCQUbw2vYEIKtNfMfcLxgB2q2kEuRS8rmak9g==
+  dependencies:
+    xlsx "^0.17.4"
+
 nopt@^4.0.1:
   version "4.0.3"
   resolved "https://registry.npm.taobao.org/nopt/download/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48"
@@ -7914,6 +7962,11 @@ pretty-error@^2.0.2:
     renderkid "^2.0.1"
     utila "~0.4"
 
+printj@~1.1.0:
+  version "1.1.2"
+  resolved "https://registry.npmmirror.com/printj/-/printj-1.1.2.tgz#d90deb2975a8b9f600fb3a1c94e3f4c53c78a222"
+  integrity sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ==
+
 private@^0.1.8:
   version "0.1.8"
   resolved "https://registry.npm.taobao.org/private/download/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
@@ -9001,6 +9054,13 @@ sqlite3@^4.2.0:
     nan "^2.12.1"
     node-pre-gyp "^0.11.0"
 
+ssf@~0.11.2:
+  version "0.11.2"
+  resolved "https://registry.npmmirror.com/ssf/-/ssf-0.11.2.tgz#0b99698b237548d088fc43cdf2b70c1a7512c06c"
+  integrity sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==
+  dependencies:
+    frac "~1.1.2"
+
 sshpk@^1.7.0:
   version "1.16.1"
   resolved "https://registry.npm.taobao.org/sshpk/download/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877"
@@ -10379,11 +10439,21 @@ widest-line@^2.0.0:
   dependencies:
     string-width "^2.1.1"
 
+wmf@~1.0.1:
+  version "1.0.2"
+  resolved "https://registry.npmmirror.com/wmf/-/wmf-1.0.2.tgz#7d19d621071a08c2bdc6b7e688a9c435298cc2da"
+  integrity sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==
+
 word-wrap@~1.2.3:
   version "1.2.3"
   resolved "https://registry.npm.taobao.org/word-wrap/download/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
   integrity sha1-YQY29rH3A4kb00dxzLF/uTtHB5w=
 
+word@~0.3.0:
+  version "0.3.0"
+  resolved "https://registry.npmmirror.com/word/-/word-0.3.0.tgz#8542157e4f8e849f4a363a288992d47612db9961"
+  integrity sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==
+
 wordwrap@~0.0.2:
   version "0.0.3"
   resolved "https://registry.npm.taobao.org/wordwrap/download/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107"
@@ -10455,6 +10525,19 @@ xdg-basedir@^3.0.0:
   resolved "https://registry.npm.taobao.org/xdg-basedir/download/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4"
   integrity sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=
 
+xlsx@^0.17.4:
+  version "0.17.5"
+  resolved "https://registry.npmmirror.com/xlsx/-/xlsx-0.17.5.tgz#78b788fcfc0773d126cdcd7ea069cb7527c1ce81"
+  integrity sha512-lXNU0TuYsvElzvtI6O7WIVb9Zar1XYw7Xb3VAx2wn8N/n0whBYrCnHMxtFyIiUU1Wjf09WzmLALDfBO5PqTb1g==
+  dependencies:
+    adler-32 "~1.2.0"
+    cfb "^1.1.4"
+    codepage "~1.15.0"
+    crc-32 "~1.2.0"
+    ssf "~0.11.2"
+    wmf "~1.0.1"
+    word "~0.3.0"
+
 xtend@^4.0.0, xtend@~4.0.0, xtend@~4.0.1:
   version "4.0.2"
   resolved "https://registry.npm.taobao.org/xtend/download/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"