ソースを参照

添加psr比对等功能

Renxy 1 年間 前
コミット
c11b2ba95b

+ 2 - 0
package.json

@@ -17,6 +17,8 @@
     "@umijs/max": "^4.0.64",
     "antd": "^5.0.0",
     "dayjs": "^1.11.7",
+    "exceljs": "^4.3.0",
+    "file-saver": "^2.0.5",
     "moment": "^2.29.4",
     "qs": "^6.11.1",
     "react-file-viewer": "^1.2.1",

+ 115 - 0
src/pages/PSRManage/components/compareCom.js

@@ -0,0 +1,115 @@
+import { Alert } from 'antd';
+import styles from './index.less';
+import { useEffect, useRef, useState } from 'react';
+import { queryPsrMonthDetail } from '@/services/psr';
+import { toggleCompare } from '../../../utils/exportExcl';
+
+const CompareCom = ({ values = [] }) => {
+  const excelRef1 = useRef();
+  const excelRef2 = useRef();
+  const luckysheetRef1 = useRef();
+  const luckysheetRef2 = useRef();
+  const [datas, setDatas] = useState([]);
+
+  const [updateCount, setUpdateCount] = useState({
+    diff: 0,
+    add: 0,
+  });
+
+  useEffect(() => {
+    if (values.length == 2) {
+      initData();
+    }
+  }, [values]);
+
+  const initData = async () => {
+    const data1 = await queryData(values[0]);
+    const data2 = await queryData(values[1]);
+    setDatas([data1, data2]);
+  };
+
+  const queryData = async (id) => {
+    const res = await queryPsrMonthDetail({ id });
+    if (res.data?.json_data) {
+      const data = JSON.parse(res.data?.json_data);
+      return data;
+    }
+    return [];
+  };
+
+  const renderSheet = (currentData, luckysheetRef, is_edit = false) => {
+    const data = currentData;
+    //设置单元格不可编辑
+    data?.forEach((item) => {
+      delete item.data;
+      item.config.authority = is_edit
+        ? null
+        : {
+            sheet: true,
+            hintText: '当前excel不可编辑!',
+          };
+    });
+    console.log(data);
+    let option = {
+      lang: 'zh',
+      showinfobar: false,
+      showstatisticBar: false,
+      data: JSON.parse(JSON.stringify(data)),
+      hook: {
+        cellMousedown: (cell, position, sheet) => {
+          console.log(cell, sheet);
+        },
+        cellUpdated: () => {
+          luckysheetRef.current.refreshFormula();
+        },
+      },
+    };
+    //设置不可编辑
+    // if (!is_edit) unableEdit(option);
+
+    luckysheetRef.current.destroy();
+    luckysheetRef.current.create(option);
+  };
+
+  const handlerLoad = async (index) => {
+    if (!index) {
+      const contentWindow = excelRef1.current.contentWindow;
+      luckysheetRef1.current = contentWindow.luckysheet;
+      const data = toggleCompare(datas[0], datas[1], setUpdateCount);
+      renderSheet(data, luckysheetRef1);
+    } else {
+      const contentWindow = excelRef2.current.contentWindow;
+      luckysheetRef2.current = contentWindow.luckysheet;
+      const data = toggleCompare(datas[1], datas[0], setUpdateCount);
+      renderSheet(data, luckysheetRef2);
+    }
+  };
+
+  const renderSheetDom = (item, index) => {
+    return (
+      <div key={`temp_${index}`} className={styles.sheetItem}>
+        <h3>{item.version_name || item?.name}</h3>
+        <iframe
+          style={{
+            width: '100%',
+            height: '80vh',
+          }}
+          ref={!index ? excelRef1 : excelRef2}
+          onLoad={() => handlerLoad(index)}
+          src="/luckysheet.html"
+        />
+        {/* <div className={styles.sheet} ref={!index ? excelRef1 : excelRef2} /> */}
+      </div>
+    );
+  };
+  return (
+    <>
+      <Alert
+        message={`比对结果:${updateCount.diff}项差异。${updateCount.add}项新增`}
+        type="info"
+      />
+      <div className={styles.sheetBox}>{values?.map(renderSheetDom)}</div>
+    </>
+  );
+};
+export default CompareCom;

+ 42 - 0
src/pages/PSRManage/components/compareModal.js

@@ -0,0 +1,42 @@
+import { Checkbox, Modal, message, Space, Divider } from 'antd';
+import { useEffect, useMemo, useRef, useState } from 'react';
+import dayjs from 'dayjs';
+
+const CompareModal = ({ list, open, onOk, onCancel }) => {
+  const [values, setValues] = useState([]);
+  useEffect(() => {
+    if (!open) setValues([]);
+  }, [open]);
+  return (
+    <Modal
+      title="比对"
+      open={open}
+      onCancel={onCancel}
+      onOk={() => onOk(values)}
+      destroyOnClose
+    >
+      <Divider />
+      <Checkbox.Group
+        value={values}
+        onChange={(values) => {
+          if (values.length > 2) {
+            message.error('只能选择两个版本比对');
+            return;
+          }
+          setValues(values);
+        }}
+      >
+        <Space direction="vertical">
+          {list?.map((item) => {
+            return (
+              <Checkbox value={item.id}>
+                {dayjs(item.day).format('YYYY-MM')}
+              </Checkbox>
+            );
+          })}
+        </Space>
+      </Checkbox.Group>
+    </Modal>
+  );
+};
+export default CompareModal;

+ 17 - 0
src/pages/PSRManage/components/index.less

@@ -0,0 +1,17 @@
+.sheetBox {
+  display: flex;
+  .sheetItem {
+    padding: 10px;
+    width: 100%;
+    h3 {
+      font-size: 24px;
+      margin-bottom: 10px;
+      font-weight: bold;
+    }
+  }
+  iframe {
+    width: 100%;
+    height: 70vh;
+    border: none;
+  }
+}

+ 146 - 67
src/pages/PSRManage/detail.js

@@ -1,7 +1,7 @@
 import PageContent from '@/components/PageContent';
 import { Button, Tabs, Space, Drawer, Timeline, message } from 'antd';
 import styles from './index.less';
-import { useEffect, useRef, useState } from 'react';
+import { useEffect, useMemo, useRef, useState } from 'react';
 import axios from 'axios';
 import {
   useLocation,
@@ -13,12 +13,16 @@ import {
 import {
   queryPsrExcel,
   queryPsrMonthDetail,
+  queryPsrMonthLast,
   queryPsrMonthList,
   queryPsrWorkLoad,
   querySavePsrMonth,
 } from '../../services/psr';
 import SaveModal from './components/saveOtherModal';
+import CompareModal from './components/compareModal';
+import CompareCom from './components/compareCom';
 import dayjs from 'dayjs';
+import { exportExcel, getUUID } from '../../utils/exportExcl';
 const PSRDetail = () => {
   const navigate = useNavigate();
   const params = useParams();
@@ -31,8 +35,11 @@ const PSRDetail = () => {
 
   const [excelData, setExcelData] = useState();
   const [historyOpen, setHistoryOpen] = useState();
-  const [open, setOpen] = useState();
-  const [dataType, setDataType] = useState();
+  const [open, setOpen] = useState(false);
+  const [key, setKey] = useState();
+  const [compareOpen, setCompareOpen] = useState(false);
+  const [compareValues, setCompareValues] = useState([]);
+  const [isOriginVer, setIsOriginVer] = useState(false); //是否原始版本 是的话只能另存不能编辑
 
   const luckysheetRef = useRef();
   const iframeRef = useRef();
@@ -91,7 +98,7 @@ const PSRDetail = () => {
     (data) =>
       queryPsrMonthList({
         project_id: projectId,
-        data_type: dataType,
+        data_type: data_type,
         ...data,
       }),
     { manual: true },
@@ -124,57 +131,63 @@ const PSRDetail = () => {
         setHistoryOpen(false);
         const jsonData = JSON.parse(data.json_data);
         renderSheet(jsonData, data.is_edit);
-        if (data.is_edit) runWorkLoad({ project_id: projectId });
       }
     },
   });
 
-  //请求人日有效工时
-  const { data: workData, run: runWorkLoad } = useRequest(
-    (data) => queryPsrWorkLoad(data),
-    {
-      manual: true,
-    },
-  );
+  const data_type = useMemo(() => {
+    if (key == '3') return 1;
+    if (key == '4') return 2;
+    return null;
+  }, [key]);
+
+  useEffect(() => {
+    setCompareValues([]);
+    if (key) {
+      onChange(key);
+    } else {
+      runExcel({ gridKey: node_id });
+    }
+  }, [key]);
 
   const onChange = (key) => {
-    if (key == '3' || key == '4') {
-      const data_type = key == '3' ? 1 : 2;
-      setDataType(data_type);
-      runList({ data_type });
-      axios.get('/excelData.json').then((res) => {
-        if (res.status == 200) {
-          renderSheet(res.data);
-        }
-      });
-    } else if (key == '1') {
+    if (key == '1') {
       runExcel({ gridKey: node_id });
-    } else {
+    } else if (key == '2') {
       runExcel({ gridKey: flow_id });
+    } else {
+      initPsrActrual(key);
     }
   };
 
-  useEffect(() => {
-    runExcel({ gridKey: node_id });
-    // axios.get('/excelData.json').then((res) => {
-    //   if (res.status == 200) {
-    //     renderSheet(res.data);
-    //   }
-    // });
-    // setTimeout(() => {
-    //   luckysheetRef.current.setCellValue(1, 1, 33333);
-    // }, 5000);
-  }, []);
+  const initPsrActrual = async (key) => {
+    const data_type = key == '3' ? 1 : 2;
+    // runList({ data_type });
+    const res = await queryPsrMonthLast({ project_id: projectId, data_type });
+    if (res.data?.length > 0 && res.data[0].json_data) {
+      const day = dayjs(res.data[0].day).format('YYYY-MM');
+      const isOrigin = day.includes('1970-01') ? 1 : 0;
+      setIsOriginVer(isOrigin);
+      setExcelData({ ...res.data[0], dayFormat: day });
+      const data = JSON.parse(res.data[0].json_data);
+      console.log(data);
+      if (data.cell_data) data.celldata = JSON.parse(data.cell_data);
+      if (data.config) data.config = JSON.parse(data.config);
+      renderSheet(
+        Array.isArray(data) ? data : [data],
+        isOrigin ? 0 : res.data[0].is_edit,
+      );
+    }
+  };
 
   const handlerSaveOther = (date) => {
     const luckyData = luckysheetRef.current?.toJson();
     if (luckyData?.data) {
-      console.log('========================', luckyData);
       const params = {
         day: date,
-        project_id: projectId,
+        project_id: Number(projectId),
         json_data: JSON.stringify(luckyData.data),
-        data_type: dataType,
+        data_type: data_type,
       };
       runSave(params);
     }
@@ -190,6 +203,16 @@ const PSRDetail = () => {
     };
     runSave(params);
   };
+
+  const exportExcl = (title) => {
+    const luckyData = luckysheetRef.current?.toJson();
+    exportExcel(luckyData.data, title);
+  };
+
+  const handlerLoad = () => {
+    const contentWindow = iframeRef.current.contentWindow;
+    luckysheetRef.current = contentWindow.luckysheet;
+  };
   const renderSheet = (currentData, is_edit = false) => {
     const data = currentData;
     //设置单元格不可编辑
@@ -201,7 +224,6 @@ const PSRDetail = () => {
             hintText: '当前excel不可编辑!',
           };
     });
-    console.log(data);
     let option = {
       lang: 'zh',
       showinfobar: false,
@@ -209,22 +231,69 @@ const PSRDetail = () => {
       data: JSON.parse(JSON.stringify(data)),
       hook: {
         cellMousedown: (cell, position, sheet) => {
-          console.log(cell, position, sheet);
+          console.log(cell, sheet);
         },
         cellUpdated: () => {
           luckysheetRef.current.refreshFormula();
         },
-        workbookCreateAfter: () => {},
+        workbookCreateAfter: async () => {
+          //当前为为终版psr标签并且可编辑状态时填充人日数据
+          if (key == '3' && is_edit) {
+            const res = await queryPsrWorkLoad({ project_id: projectId });
+            if (res.data) {
+              luckysheetRef.current.setCellValue(8, 4, res.data.Total);
+              luckysheetRef.current.setCellValue(8, 8, res.data.Month);
+            }
+          }
+        },
       },
     };
+    option.data.forEach((item) => {
+      delete item.data;
+      delete item.cell_data;
+      if (item.celldata) {
+        item.celldata.forEach((cell) => {
+          // 生成uuid
+          if (!cell.v.cid) cell.v.cid = getUUID();
+        });
+      }
+      // 默认禁止编辑
+      // item.config.authority = { sheet: true, hintText };
+    });
+
+    console.log(option.data);
     //设置不可编辑
     if (!is_edit) unableEdit(option);
 
     luckysheetRef.current.destroy();
     luckysheetRef.current.create(option);
   };
-  const renderTitle = (data_type) => {
-    const title = data_type == 1 ? 'PSR' : '现金流';
+  const renderTitle = (data_type, title) => {
+    if (compareValues?.length == 2) {
+      return (
+        <div className={styles.exportDiv}>
+          <Button
+            type="primary"
+            onClick={() => {
+              setCompareValues([]);
+              setTimeout(() => {
+                initPsrActrual(key);
+              }, 500);
+            }}
+          >
+            退出比对
+          </Button>
+        </div>
+      );
+    }
+    if (data_type == 0)
+      return (
+        <div className={styles.exportDiv}>
+          <Button type="primary" onClick={() => exportExcl(title)}>
+            导出
+          </Button>
+        </div>
+      );
     return (
       <div className={styles.excelTitle}>
         <div>
@@ -248,43 +317,44 @@ const PSRDetail = () => {
           >
             另存为
           </Button>
-          {/* <Button type="primary">编辑</Button> */}
           <Button
             type="primary"
             onClick={handlerSave}
-            disabled={!excelData?.is_edit}
+            disabled={!excelData?.is_edit || isOriginVer}
           >
             保存
           </Button>
+          <Button type="primary" onClick={() => setCompareOpen(true)}>
+            比对
+          </Button>
+          <Button type="primary" onClick={() => exportExcl(title)}>
+            导出
+          </Button>
         </Space>
       </div>
     );
   };
-  const handlerLoad = () => {
-    const contentWindow = iframeRef.current.contentWindow;
-    luckysheetRef.current = contentWindow.luckysheet;
-  };
 
   const items = [
     {
       key: '1',
       label: '投标版PSR',
-      // children: `Content of Tab Pane 1`,
+      children: renderTitle(0, '投标版PSR'),
     },
     {
       key: '2',
       label: '签字版PSR',
-      // children: `Content of Tab Pane 2`,
+      children: renderTitle(0, '签字版PSR'),
     },
     {
       key: '3',
       label: '终版PSR',
-      children: renderTitle(1),
+      children: renderTitle(1, '终版PSR'),
     },
     {
       key: '4',
       label: '现金流',
-      children: renderTitle(2),
+      children: renderTitle(2, '现金流'),
     },
   ];
   return (
@@ -295,21 +365,21 @@ const PSRDetail = () => {
         </Button>
         <span className={styles.title}>{project_name}</span>
       </div>
-      <Tabs
-        defaultActiveKey="1"
-        type="card"
-        items={items}
-        onChange={onChange}
-      />
-      <iframe
-        style={{
-          width: '100%',
-          height: '80vh',
-        }}
-        ref={iframeRef}
-        onLoad={handlerLoad}
-        src="/luckysheet.html"
-      />
+      <Tabs defaultActiveKey="1" type="card" items={items} onChange={setKey} />
+      {compareValues?.length == 2 ? (
+        <CompareCom values={compareValues} />
+      ) : (
+        <iframe
+          style={{
+            width: '100%',
+            height: '80vh',
+          }}
+          ref={iframeRef}
+          onLoad={handlerLoad}
+          src="/luckysheet.html"
+        />
+      )}
+
       <Drawer
         title="历史版本"
         placement="right"
@@ -335,6 +405,15 @@ const PSRDetail = () => {
         onCancel={() => setOpen(false)}
         onOk={handlerSaveOther}
       />
+      <CompareModal
+        list={data?.list}
+        open={compareOpen}
+        onOk={(values) => {
+          setCompareValues(values);
+          setCompareOpen(false);
+        }}
+        onCancel={() => setCompareOpen(false)}
+      />
     </PageContent>
   );
 };

+ 6 - 0
src/pages/PSRManage/index.less

@@ -27,3 +27,9 @@
   margin-bottom: 20px;
   justify-content: space-between;
 }
+.exportDiv{
+  display: flex;
+  flex-direction: row-reverse;
+  margin-bottom: 20px;
+}
+

+ 10 - 0
src/services/psr.js

@@ -80,3 +80,13 @@ export const queryPsrExcel = async (data) => {
     data,
   });
 };
+
+/**
+ * 获取最新psr和现金流接口
+ * @param {*} project_id
+ */
+export const queryPsrMonthLast = async (data) => {
+  return await request(`/api/v1/purchase/psr/month/last`, {
+    params: data,
+  });
+};

+ 436 - 0
src/utils/exportExcl.js

@@ -0,0 +1,436 @@
+// import { createCellPos } from './translateNumToLetter'
+const Excel = require('exceljs');
+
+import FileSaver from 'file-saver';
+
+export function getExcelBolob(luckysheet) {
+  // 参数为luckysheet.getluckysheetfile()获取的对象
+  // 1.创建工作簿,可以为工作簿添加属性
+  const workbook = new Excel.Workbook();
+  // 2.创建表格,第二个参数可以配置创建什么样的工作表
+  if (Object.prototype.toString.call(luckysheet) === '[object Object]') {
+    luckysheet = [luckysheet];
+  }
+  luckysheet.forEach(function (table) {
+    if (table.data.length === 0) return true;
+    // ws.getCell('B2').fill = fills.
+    const worksheet = workbook.addWorksheet(table.name);
+    const merge = (table.config && table.config.merge) || {};
+    const borderInfo = (table.config && table.config.borderInfo) || {};
+    // 3.设置单元格合并,设置单元格边框,设置单元格样式,设置值
+    setStyleAndValue(table.data, worksheet);
+    setMerge(merge, worksheet);
+    setBorder(borderInfo, worksheet);
+    return true;
+  });
+  const buffer = workbook.xlsx.writeBuffer().then((data) => {
+    // console.log('data', data)
+    const blob = new Blob([data], {
+      type: 'application/vnd.ms-excel;charset=utf-8',
+    });
+    return blob;
+  });
+  return buffer;
+}
+
+export function exportExcel(luckysheet, value) {
+  // return
+  // 4.写入 buffer
+  getExcelBolob(luckysheet).then((blob) => {
+    console.log('导出成功!');
+    FileSaver.saveAs(blob, `${value}.xlsx`);
+  });
+}
+
+var setMerge = function (luckyMerge = {}, worksheet) {
+  const mergearr = Object.values(luckyMerge);
+  mergearr.forEach(function (elem) {
+    // elem格式:{r: 0, c: 0, rs: 1, cs: 2}
+    // 按开始行,开始列,结束行,结束列合并(相当于 K10:M12)
+    worksheet.mergeCells(
+      elem.r + 1,
+      elem.c + 1,
+      elem.r + elem.rs,
+      elem.c + elem.cs,
+    );
+  });
+};
+
+var setBorder = function (luckyBorderInfo, worksheet) {
+  if (!Array.isArray(luckyBorderInfo)) return;
+  // console.log('luckyBorderInfo', luckyBorderInfo)
+  luckyBorderInfo.forEach(function (elem) {
+    // 现在只兼容到borderType 为range的情况
+    // console.log('ele', elem)
+    if (elem.rangeType === 'range') {
+      let border = borderConvert(elem.borderType, elem.style, elem.color);
+      let rang = elem.range[0];
+      // console.log('range', rang)
+      let row = rang.row;
+      let column = rang.column;
+      for (let i = row[0] + 1; i < row[1] + 2; i++) {
+        for (let y = column[0] + 1; y < column[1] + 2; y++) {
+          worksheet.getCell(i, y).border = border;
+        }
+      }
+    }
+    if (elem.rangeType === 'cell') {
+      // col_index: 2
+      // row_index: 1
+      // b: {
+      //   color: '#d0d4e3'
+      //   style: 1
+      // }
+      const { col_index, row_index } = elem.value;
+      const borderData = Object.assign({}, elem.value);
+      delete borderData.col_index;
+      delete borderData.row_index;
+      let border = addborderToCell(borderData, row_index, col_index);
+      // console.log('bordre', border, borderData)
+      if (row_index === undefined || col_index === undefined) return;
+      worksheet.getCell(row_index + 1, col_index + 1).border = border;
+    }
+    // console.log(rang.column_focus + 1, rang.row_focus + 1)
+    // worksheet.getCell(rang.row_focus + 1, rang.column_focus + 1).border = border
+  });
+};
+var setStyleAndValue = function (cellArr, worksheet) {
+  if (!Array.isArray(cellArr)) return;
+  cellArr.forEach(function (row, rowid) {
+    row.every(function (cell, columnid) {
+      if (!cell) return true;
+      let fill = fillConvert(cell.bg);
+
+      let font = fontConvert(
+        cell.ff,
+        cell.fc,
+        cell.bl,
+        cell.it,
+        cell.fs,
+        cell.cl,
+        cell.ul,
+      );
+      let alignment = alignmentConvert(cell.vt, cell.ht, cell.tb, cell.tr);
+      let value = '';
+
+      if (cell.f) {
+        value = { formula: cell.f, result: cell.v };
+      } else if (!cell.v && cell.ct && cell.ct.s) {
+        // xls转为xlsx之后,内部存在不同的格式,都会进到富文本里,即值不存在与cell.v,而是存在于cell.ct.s之后
+        // value = cell.ct.s[0].v
+        cell.ct.s.forEach((arr) => {
+          value += arr.v;
+        });
+      } else {
+        value = cell.v;
+      }
+      //  style 填入到_value中可以实现填充色
+      let letter = createCellPos(columnid);
+      let target = worksheet.getCell(letter + (rowid + 1));
+      // console.log('1233', letter + (rowid + 1))
+      for (const key in fill) {
+        target.fill = fill;
+        break;
+      }
+      target.font = font;
+      target.alignment = alignment;
+      target.value = value;
+
+      return true;
+    });
+  });
+};
+
+var fillConvert = function (bg) {
+  if (!bg) {
+    return {};
+  }
+  // const bgc = bg.replace('#', '')
+  let fill = {
+    type: 'pattern',
+    pattern: 'solid',
+    fgColor: { argb: bg.replace('#', '') },
+  };
+  return fill;
+};
+
+var fontConvert = function (
+  ff = 0,
+  fc = '#000000',
+  bl = 0,
+  it = 0,
+  fs = 10,
+  cl = 0,
+  ul = 0,
+) {
+  // luckysheet:ff(样式), fc(颜色), bl(粗体), it(斜体), fs(大小), cl(删除线), ul(下划线)
+  const luckyToExcel = {
+    0: '微软雅黑',
+    1: '宋体(Song)',
+    2: '黑体(ST Heiti)',
+    3: '楷体(ST Kaiti)',
+    4: '仿宋(ST FangSong)',
+    5: '新宋体(ST Song)',
+    6: '华文新魏',
+    7: '华文行楷',
+    8: '华文隶书',
+    9: 'Arial',
+    10: 'Times New Roman ',
+    11: 'Tahoma ',
+    12: 'Verdana',
+    num2bl: function (num) {
+      return num === 0 ? false : true;
+    },
+  };
+  // 出现Bug,导入的时候ff为luckyToExcel的val
+
+  let font = {
+    name: typeof ff === 'number' ? luckyToExcel[ff] : ff,
+    family: 1,
+    size: fs,
+    color: { argb: fc.replace('#', '') },
+    bold: luckyToExcel.num2bl(bl),
+    italic: luckyToExcel.num2bl(it),
+    underline: luckyToExcel.num2bl(ul),
+    strike: luckyToExcel.num2bl(cl),
+  };
+
+  return font;
+};
+
+var alignmentConvert = function (
+  vt = 'default',
+  ht = 'default',
+  tb = 'default',
+  tr = 'default',
+) {
+  // luckysheet:vt(垂直), ht(水平), tb(换行), tr(旋转)
+  const luckyToExcel = {
+    vertical: {
+      0: 'middle',
+      1: 'top',
+      2: 'bottom',
+      default: 'top',
+    },
+    horizontal: {
+      0: 'center',
+      1: 'left',
+      2: 'right',
+      default: 'left',
+    },
+    wrapText: {
+      0: false,
+      1: false,
+      2: true,
+      default: false,
+    },
+    textRotation: {
+      0: 0,
+      1: 45,
+      2: -45,
+      3: 'vertical',
+      4: 90,
+      5: -90,
+      default: 0,
+    },
+  };
+
+  let alignment = {
+    vertical: luckyToExcel.vertical[vt],
+    horizontal: luckyToExcel.horizontal[ht],
+    wrapText: luckyToExcel.wrapText[tb],
+    textRotation: luckyToExcel.textRotation[tr],
+  };
+  return alignment;
+};
+
+var borderConvert = function (borderType, style = 1, color = '#000') {
+  // 对应luckysheet的config中borderinfo的的参数
+  if (!borderType) {
+    return {};
+  }
+  const luckyToExcel = {
+    type: {
+      'border-all': 'all',
+      'border-top': 'top',
+      'border-right': 'right',
+      'border-bottom': 'bottom',
+      'border-left': 'left',
+    },
+    style: {
+      0: 'none',
+      1: 'thin',
+      2: 'hair',
+      3: 'dotted',
+      4: 'dashDot', // 'Dashed',
+      5: 'dashDot',
+      6: 'dashDotDot',
+      7: 'double',
+      8: 'medium',
+      9: 'mediumDashed',
+      10: 'mediumDashDot',
+      11: 'mediumDashDotDot',
+      12: 'slantDashDot',
+      13: 'thick',
+    },
+  };
+  let template = {
+    style: luckyToExcel.style[style],
+    color: { argb: color.replace('#', '') },
+  };
+  let border = {};
+  if (luckyToExcel.type[borderType] === 'all') {
+    border['top'] = template;
+    border['right'] = template;
+    border['bottom'] = template;
+    border['left'] = template;
+  } else {
+    border[luckyToExcel.type[borderType]] = template;
+  }
+  // console.log('border', border)
+  return border;
+};
+
+function addborderToCell(borders, row_index, col_index) {
+  let border = {};
+  const luckyExcel = {
+    type: {
+      l: 'left',
+      r: 'right',
+      b: 'bottom',
+      t: 'top',
+    },
+    style: {
+      0: 'none',
+      1: 'thin',
+      2: 'hair',
+      3: 'dotted',
+      4: 'dashDot', // 'Dashed',
+      5: 'dashDot',
+      6: 'dashDotDot',
+      7: 'double',
+      8: 'medium',
+      9: 'mediumDashed',
+      10: 'mediumDashDot',
+      11: 'mediumDashDotDot',
+      12: 'slantDashDot',
+      13: 'thick',
+    },
+  };
+  // console.log('borders', borders)
+  for (const bor in borders) {
+    if (borders[bor].color?.indexOf('rgb') === -1) {
+      border[luckyExcel.type[bor]] = {
+        style: luckyExcel.style[borders[bor].style],
+        color: { argb: borders[bor].color.replace('#', '') },
+      };
+    } else {
+      border[luckyExcel.type[bor]] = {
+        style: luckyExcel.style[borders[bor].style],
+        color: { argb: borders[bor].color },
+      };
+    }
+  }
+
+  return border;
+}
+
+function createCellPos(n) {
+  let ordA = 'A'.charCodeAt(0);
+
+  let ordZ = 'Z'.charCodeAt(0);
+  let len = ordZ - ordA + 1;
+  let s = '';
+  while (n >= 0) {
+    s = String.fromCharCode((n % len) + ordA) + s;
+
+    n = Math.floor(n / len) - 1;
+  }
+  return s;
+}
+
+function isEmpty(item) {
+  return (item?.v?.v ?? '') === '' && !item?.v?.ct?.s;
+}
+
+// 切换比对状态
+export const toggleCompare = (currentData, compareData, callback) => {
+  const DIFF_COLOR = '#ff0000';
+  const ADD_COLOR = '#00ff00';
+  let diff = [];
+  let add = [];
+  currentData.forEach((sheet, index) => {
+    let celldata1 = sheet.celldata;
+    let celldata2 = compareData[index]?.celldata || [];
+
+    celldata1.forEach((item) => {
+      // 不判断空字符串
+      if (isEmpty(item)) return;
+      var d2Item = celldata2.find((item2) => item2.v.cid == item.v.cid);
+      if (d2Item && !isEmpty(d2Item)) {
+        // v.ct.s相同,不做处理
+        if (
+          item.v.ct?.s &&
+          JSON.stringify(item.v.ct?.s) == JSON.stringify(d2Item.v.ct?.s)
+        )
+          return;
+        // v.v相同,不做处理
+        if (d2Item.v.v == item.v.v) return;
+        // 内容不同,标记diff颜色
+        diff.push({
+          ...item,
+          sheetOrder: index,
+        });
+        item.v.bg = DIFF_COLOR;
+        // luckysheet.setCellFormat(item.r, item.c, 'bg', DIFF_COLOR);
+      } else {
+        // 找不到同cid的单元格,标记add颜色
+        add.push({
+          ...item,
+          sheetOrder: index,
+        });
+        item.v.bg = ADD_COLOR;
+        // luckysheet.setCellFormat(item.r, item.c, 'bg', ADD_COLOR);
+      }
+    });
+  });
+  console.log(diff, add);
+
+  callback &&
+    callback({
+      diff: diff.length,
+      add: add.length,
+    });
+  return currentData;
+};
+
+export function getUUID(len = 8, radix = 16) {
+  var chars =
+    '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
+  var uuid = [],
+    i;
+  radix = radix || chars.length;
+
+  if (len) {
+    // Compact form
+    for (i = 0; i < len; i++) uuid[i] = chars[0 | (Math.random() * radix)];
+  } else {
+    // rfc4122, version 4 form
+    var r;
+
+    // rfc4122 requires these characters
+    uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
+    uuid[14] = '4';
+
+    // Fill in random data.  At i==19 set the high bits of clock sequence as
+    // per rfc4122, sec. 4.1.5
+    for (i = 0; i < 36; i++) {
+      if (!uuid[i]) {
+        r = 0 | (Math.random() * 16);
+        uuid[i] = chars[i == 19 ? (r & 0x3) | 0x8 : r];
+      }
+    }
+  }
+
+  return uuid.join('');
+}

+ 404 - 11
yarn.lock

@@ -1865,6 +1865,31 @@
   resolved "https://registry.npmmirror.com/@eslint/js/-/js-8.35.0.tgz#b7569632b0b788a0ca0e438235154e45d42813a7"
   integrity sha512-JXdzbRiWclLVoD8sNUjR443VVlYqiYmDVT6rGUEIEHU5YJW0gaVZwV2xgM7D4arkvASqD0IlLUVjHiFuxaftRw==
 
+"@fast-csv/format@4.3.5":
+  version "4.3.5"
+  resolved "https://registry.npmmirror.com/@fast-csv/format/-/format-4.3.5.tgz#90d83d1b47b6aaf67be70d6118f84f3e12ee1ff3"
+  integrity sha512-8iRn6QF3I8Ak78lNAa+Gdl5MJJBM5vRHivFtMRUWINdevNo00K7OXxS2PshawLKTejVwieIlPmK5YlLu6w4u8A==
+  dependencies:
+    "@types/node" "^14.0.1"
+    lodash.escaperegexp "^4.1.2"
+    lodash.isboolean "^3.0.3"
+    lodash.isequal "^4.5.0"
+    lodash.isfunction "^3.0.9"
+    lodash.isnil "^4.0.0"
+
+"@fast-csv/parse@4.3.6":
+  version "4.3.6"
+  resolved "https://registry.npmmirror.com/@fast-csv/parse/-/parse-4.3.6.tgz#ee47d0640ca0291034c7aa94039a744cfb019264"
+  integrity sha512-uRsLYksqpbDmWaSmzvJcuApSEe38+6NQZBUsuAyMZKqHxH0g1wcJgsKUvN3WC8tewaqFjBMMGrkHmC+T7k8LvA==
+  dependencies:
+    "@types/node" "^14.0.1"
+    lodash.escaperegexp "^4.1.2"
+    lodash.groupby "^4.6.0"
+    lodash.isfunction "^3.0.9"
+    lodash.isnil "^4.0.0"
+    lodash.isundefined "^3.0.1"
+    lodash.uniq "^4.5.0"
+
 "@floating-ui/core@^0.6.2":
   version "0.6.2"
   resolved "https://registry.npmmirror.com/@floating-ui/core/-/core-0.6.2.tgz#f2813f0e5f3d5ed7af5029e1a082203dadf02b7d"
@@ -2464,6 +2489,11 @@
   resolved "https://registry.npmmirror.com/@types/node/-/node-18.15.11.tgz#b3b790f09cb1696cffcec605de025b088fa4225f"
   integrity sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==
 
+"@types/node@^14.0.1":
+  version "14.18.52"
+  resolved "https://registry.npmmirror.com/@types/node/-/node-14.18.52.tgz#214674cbff9f86fad4bf0c25f31ab9b9fa31110f"
+  integrity sha512-DGhiXKOHSFVVm+PJD+9Y0ObxXLeG6qwc0HoOn+ooQKeNNu+T2mEJCM5UBDUREKAggl9MHYjb5E71PAmx6MbzIg==
+
 "@types/normalize-package-data@^2.4.0":
   version "2.4.1"
   resolved "https://registry.npmmirror.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301"
@@ -3265,6 +3295,35 @@ anymatch@^3.0.3, anymatch@~3.1.2:
     normalize-path "^3.0.0"
     picomatch "^2.0.4"
 
+archiver-utils@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.npmmirror.com/archiver-utils/-/archiver-utils-2.1.0.tgz#e8a460e94b693c3e3da182a098ca6285ba9249e2"
+  integrity sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==
+  dependencies:
+    glob "^7.1.4"
+    graceful-fs "^4.2.0"
+    lazystream "^1.0.0"
+    lodash.defaults "^4.2.0"
+    lodash.difference "^4.5.0"
+    lodash.flatten "^4.4.0"
+    lodash.isplainobject "^4.0.6"
+    lodash.union "^4.6.0"
+    normalize-path "^3.0.0"
+    readable-stream "^2.0.0"
+
+archiver@^5.0.0:
+  version "5.3.1"
+  resolved "https://registry.npmmirror.com/archiver/-/archiver-5.3.1.tgz#21e92811d6f09ecfce649fbefefe8c79e57cbbb6"
+  integrity sha512-8KyabkmbYrH+9ibcTScQ1xCJC/CGcugdVIwB+53f5sZziXgwUh3iXlAlANMxcZyDEfTHMe6+Z5FofV8nopXP7w==
+  dependencies:
+    archiver-utils "^2.1.0"
+    async "^3.2.3"
+    buffer-crc32 "^0.2.1"
+    readable-stream "^3.6.0"
+    readdir-glob "^1.0.0"
+    tar-stream "^2.2.0"
+    zip-stream "^4.1.0"
+
 argparse@^1.0.7, argparse@~1.0.3:
   version "1.0.10"
   resolved "https://registry.npmmirror.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911"
@@ -3367,6 +3426,11 @@ async-validator@^4.1.0:
   resolved "https://registry.npmmirror.com/async-validator/-/async-validator-4.2.5.tgz#c96ea3332a521699d0afaaceed510a54656c6339"
   integrity sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==
 
+async@^3.2.3:
+  version "3.2.4"
+  resolved "https://registry.npmmirror.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c"
+  integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==
+
 asynckit@^0.4.0:
   version "0.4.0"
   resolved "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
@@ -3535,11 +3599,16 @@ balanced-match@^2.0.0:
   resolved "https://registry.npmmirror.com/balanced-match/-/balanced-match-2.0.0.tgz#dc70f920d78db8b858535795867bf48f820633d9"
   integrity sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==
 
-base64-js@^1.0.2:
+base64-js@^1.0.2, base64-js@^1.3.1:
   version "1.5.1"
   resolved "https://registry.npmmirror.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
   integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
 
+big-integer@^1.6.17:
+  version "1.6.51"
+  resolved "https://registry.npmmirror.com/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686"
+  integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==
+
 big.js@^5.2.2:
   version "5.2.2"
   resolved "https://registry.npmmirror.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
@@ -3550,7 +3619,24 @@ binary-extensions@^2.0.0:
   resolved "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
   integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
 
-bluebird@~3.4.0:
+binary@~0.3.0:
+  version "0.3.0"
+  resolved "https://registry.npmmirror.com/binary/-/binary-0.3.0.tgz#9f60553bc5ce8c3386f3b553cff47462adecaa79"
+  integrity sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==
+  dependencies:
+    buffers "~0.1.1"
+    chainsaw "~0.1.0"
+
+bl@^4.0.3:
+  version "4.1.0"
+  resolved "https://registry.npmmirror.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a"
+  integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==
+  dependencies:
+    buffer "^5.5.0"
+    inherits "^2.0.4"
+    readable-stream "^3.4.0"
+
+bluebird@~3.4.0, bluebird@~3.4.1:
   version "3.4.7"
   resolved "https://registry.npmmirror.com/bluebird/-/bluebird-3.4.7.tgz#f72d760be09b7f76d08ed8fae98b289a8d05fab3"
   integrity sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==
@@ -3578,6 +3664,13 @@ brace-expansion@^1.1.7:
     balanced-match "^1.0.0"
     concat-map "0.0.1"
 
+brace-expansion@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae"
+  integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==
+  dependencies:
+    balanced-match "^1.0.0"
+
 braces@^3.0.2, braces@~3.0.2:
   version "3.0.2"
   resolved "https://registry.npmmirror.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
@@ -3668,11 +3761,21 @@ bser@2.1.1:
   dependencies:
     node-int64 "^0.4.0"
 
+buffer-crc32@^0.2.1, buffer-crc32@^0.2.13:
+  version "0.2.13"
+  resolved "https://registry.npmmirror.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
+  integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==
+
 buffer-from@^1.0.0:
   version "1.1.2"
   resolved "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
   integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
 
+buffer-indexof-polyfill@~1.0.0:
+  version "1.0.2"
+  resolved "https://registry.npmmirror.com/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz#d2732135c5999c64b277fcf9b1abe3498254729c"
+  integrity sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==
+
 buffer-xor@^1.0.3:
   version "1.0.3"
   resolved "https://registry.npmmirror.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9"
@@ -3687,6 +3790,19 @@ buffer@^4.3.0:
     ieee754 "^1.1.4"
     isarray "^1.0.0"
 
+buffer@^5.5.0:
+  version "5.7.1"
+  resolved "https://registry.npmmirror.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
+  integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==
+  dependencies:
+    base64-js "^1.3.1"
+    ieee754 "^1.1.13"
+
+buffers@~0.1.1:
+  version "0.1.1"
+  resolved "https://registry.npmmirror.com/buffers/-/buffers-0.1.1.tgz#b24579c3bed4d6d396aeee6d9a8ae7f5482ab7bb"
+  integrity sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==
+
 builtin-status-codes@^3.0.0:
   version "3.0.0"
   resolved "https://registry.npmmirror.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8"
@@ -3750,6 +3866,13 @@ cfb@~0.12.0:
     commander "~2.11.0"
     printj "~1.1.0"
 
+chainsaw@~0.1.0:
+  version "0.1.0"
+  resolved "https://registry.npmmirror.com/chainsaw/-/chainsaw-0.1.0.tgz#5eab50b28afe58074d0d58291388828b5e5fbc98"
+  integrity sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==
+  dependencies:
+    traverse ">=0.3.0 <0.4"
+
 chalk@5.2.0:
   version "5.2.0"
   resolved "https://registry.npmmirror.com/chalk/-/chalk-5.2.0.tgz#249623b7d66869c673699fb66d65723e54dfcfb3"
@@ -3965,6 +4088,16 @@ common-path-prefix@^3.0.0:
   resolved "https://registry.npmmirror.com/common-path-prefix/-/common-path-prefix-3.0.0.tgz#7d007a7e07c58c4b4d5f433131a19141b29f11e0"
   integrity sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==
 
+compress-commons@^4.1.0:
+  version "4.1.1"
+  resolved "https://registry.npmmirror.com/compress-commons/-/compress-commons-4.1.1.tgz#df2a09a7ed17447642bad10a85cc9a19e5c42a7d"
+  integrity sha512-QLdDLCKNV2dtoTorqgxngQCMA+gWXkM/Nwu7FpeBhk/RdkzimqC3jueb/FDmaZeXh+uby1jkBqE3xArsLBE5wQ==
+  dependencies:
+    buffer-crc32 "^0.2.13"
+    crc32-stream "^4.0.2"
+    normalize-path "^3.0.0"
+    readable-stream "^3.6.0"
+
 compute-scroll-into-view@^1.0.20:
   version "1.0.20"
   resolved "https://registry.npmmirror.com/compute-scroll-into-view/-/compute-scroll-into-view-1.0.20.tgz#1768b5522d1172754f5d0c9b02de3af6be506a43"
@@ -4062,6 +4195,11 @@ cosmiconfig@^7.0.1:
     path-type "^4.0.0"
     yaml "^1.10.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==
+
 crc-32@~1.1.0:
   version "1.1.1"
   resolved "https://registry.npmmirror.com/crc-32/-/crc-32-1.1.1.tgz#5d739d5e4c6e352ad8304d73223d483fe55adb8d"
@@ -4070,6 +4208,14 @@ crc-32@~1.1.0:
     exit-on-epipe "~1.0.1"
     printj "~1.1.0"
 
+crc32-stream@^4.0.2:
+  version "4.0.2"
+  resolved "https://registry.npmmirror.com/crc32-stream/-/crc32-stream-4.0.2.tgz#c922ad22b38395abe9d3870f02fa8134ed709007"
+  integrity sha512-DxFZ/Hk473b/muq1VJ///PMNLj0ZMnzye9thBpmjpJKCc5eMgB95aK8zCGrGfQ90cWo561Te6HK9D+j4KPdM6w==
+  dependencies:
+    crc-32 "^1.2.0"
+    readable-stream "^3.4.0"
+
 create-ecdh@^4.0.0:
   version "4.0.4"
   resolved "https://registry.npmmirror.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e"
@@ -4277,6 +4423,11 @@ dayjs@1.x, dayjs@^1.11.1, dayjs@^1.11.4, dayjs@^1.11.7:
   resolved "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.7.tgz#4b296922642f70999544d1144a2c25730fce63e2"
   integrity sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ==
 
+dayjs@^1.8.34:
+  version "1.11.8"
+  resolved "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.8.tgz#4282f139c8c19dd6d0c7bd571e30c2d0ba7698ea"
+  integrity sha512-LcgxzFoWMEPO7ggRv1Y2N31hUf2R0Vj7fuy/m+Bg1K8rr+KAs1AEy4y9jd5DXe8pbHgX+srkHNS7TH6Q6ZhYeQ==
+
 debug@^3.2.6:
   version "3.2.7"
   resolved "https://registry.npmmirror.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
@@ -4462,6 +4613,13 @@ duck@^0.1.12:
   dependencies:
     underscore "^1.13.1"
 
+duplexer2@~0.1.4:
+  version "0.1.4"
+  resolved "https://registry.npmmirror.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1"
+  integrity sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==
+  dependencies:
+    readable-stream "^2.0.2"
+
 duplexify@^4.1.2:
   version "4.1.2"
   resolved "https://registry.npmmirror.com/duplexify/-/duplexify-4.1.2.tgz#18b4f8d28289132fa0b9573c898d9f903f81c7b0"
@@ -4954,6 +5112,21 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3:
     md5.js "^1.3.4"
     safe-buffer "^5.1.1"
 
+exceljs@^4.3.0:
+  version "4.3.0"
+  resolved "https://registry.npmmirror.com/exceljs/-/exceljs-4.3.0.tgz#939bc0d4c59c200acadb7051be34d25c109853c4"
+  integrity sha512-hTAeo5b5TPvf8Z02I2sKIT4kSfCnOO2bCxYX8ABqODCdAjppI3gI9VYiGCQQYVcBaBSKlFDMKlAQRqC+kV9O8w==
+  dependencies:
+    archiver "^5.0.0"
+    dayjs "^1.8.34"
+    fast-csv "^4.3.1"
+    jszip "^3.5.0"
+    readable-stream "^3.6.0"
+    saxes "^5.0.1"
+    tmp "^0.2.0"
+    unzipper "^0.10.11"
+    uuid "^8.3.0"
+
 execa@^5.1.1:
   version "5.1.1"
   resolved "https://registry.npmmirror.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd"
@@ -5003,6 +5176,14 @@ ext@^1.1.2:
   dependencies:
     type "^2.7.2"
 
+fast-csv@^4.3.1:
+  version "4.3.6"
+  resolved "https://registry.npmmirror.com/fast-csv/-/fast-csv-4.3.6.tgz#70349bdd8fe4d66b1130d8c91820b64a21bc4a63"
+  integrity sha512-2RNSpuwwsJGP0frGsOmTb9oUF+VkFSM4SyLTDgwf2ciHWTarN0lQTC+F2f/t5J9QjW+c65VFIAAu85GsvMIusw==
+  dependencies:
+    "@fast-csv/format" "4.3.5"
+    "@fast-csv/parse" "4.3.6"
+
 fast-deep-equal@3.1.3, fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
   version "3.1.3"
   resolved "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
@@ -5073,6 +5254,11 @@ file-entry-cache@^6.0.1:
   dependencies:
     flat-cache "^3.0.4"
 
+file-saver@^2.0.5:
+  version "2.0.5"
+  resolved "https://registry.npmmirror.com/file-saver/-/file-saver-2.0.5.tgz#d61cfe2ce059f414d899e9dd6d4107ee25670c38"
+  integrity sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==
+
 fill-range@^7.0.1:
   version "7.0.1"
   resolved "https://registry.npmmirror.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
@@ -5175,6 +5361,11 @@ fraction.js@^4.2.0:
   resolved "https://registry.npmmirror.com/fraction.js/-/fraction.js-4.2.0.tgz#448e5109a313a3527f5a3ab2119ec4cf0e0e2950"
   integrity sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==
 
+fs-constants@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.npmmirror.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
+  integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==
+
 fs-extra@^10.0.0:
   version "10.1.0"
   resolved "https://registry.npmmirror.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf"
@@ -5204,6 +5395,16 @@ fsevents@^2.3.2, fsevents@~2.3.2:
   resolved "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
   integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
 
+fstream@^1.0.12:
+  version "1.0.12"
+  resolved "https://registry.npmmirror.com/fstream/-/fstream-1.0.12.tgz#4e8ba8ee2d48be4f7d0de505455548eae5932045"
+  integrity sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==
+  dependencies:
+    graceful-fs "^4.1.2"
+    inherits "~2.0.0"
+    mkdirp ">=0.5 0"
+    rimraf "2"
+
 function-bind@^1.1.1:
   version "1.1.1"
   resolved "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
@@ -5400,7 +5601,7 @@ gopd@^1.0.1:
   dependencies:
     get-intrinsic "^1.1.3"
 
-graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.9:
+graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.2, graceful-fs@^4.2.4, graceful-fs@^4.2.9:
   version "4.2.11"
   resolved "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
   integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
@@ -5636,7 +5837,7 @@ identity-obj-proxy@3.0.0:
   dependencies:
     harmony-reflect "^1.4.6"
 
-ieee754@^1.1.4:
+ieee754@^1.1.13, ieee754@^1.1.4:
   version "1.2.1"
   resolved "https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
   integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
@@ -5651,6 +5852,11 @@ image-size@~0.5.0:
   resolved "https://registry.npmmirror.com/image-size/-/image-size-0.5.5.tgz#09dfd4ab9d20e29eb1c3e80b8990378df9e3cb9c"
   integrity sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==
 
+immediate@~3.0.5:
+  version "3.0.6"
+  resolved "https://registry.npmmirror.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
+  integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==
+
 immer@^8.0.4:
   version "8.0.4"
   resolved "https://registry.npmmirror.com/immer/-/immer-8.0.4.tgz#3a21605a4e2dded852fb2afd208ad50969737b7a"
@@ -5699,7 +5905,7 @@ inflight@^1.0.4:
     once "^1.3.0"
     wrappy "1"
 
-inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3:
+inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3:
   version "2.0.4"
   resolved "https://registry.npmmirror.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
   integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
@@ -6290,6 +6496,16 @@ jsonfile@^6.0.1:
     array-includes "^3.1.5"
     object.assign "^4.1.3"
 
+jszip@^3.5.0:
+  version "3.10.1"
+  resolved "https://registry.npmmirror.com/jszip/-/jszip-3.10.1.tgz#34aee70eb18ea1faec2f589208a157d1feb091c2"
+  integrity sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==
+  dependencies:
+    lie "~3.3.0"
+    pako "~1.0.2"
+    readable-stream "~2.3.6"
+    setimmediate "^1.0.5"
+
 jszip@~2.5.0:
   version "2.5.0"
   resolved "https://registry.npmmirror.com/jszip/-/jszip-2.5.0.tgz#7444fd8551ddf3e5da7198fea0c91bc8308cc274"
@@ -6312,6 +6528,13 @@ kolorist@^1.6.0:
   resolved "https://registry.npmmirror.com/kolorist/-/kolorist-1.7.0.tgz#8e22bc470ea2d2743dbd461808f8b5246b19f5f4"
   integrity sha512-ymToLHqL02udwVdbkowNpzjFd6UzozMtshPQKVi5k1EjKRqKqBrOnE9QbLEb0/pV76SAiIT13hdL8R6suc+f3g==
 
+lazystream@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.npmmirror.com/lazystream/-/lazystream-1.0.1.tgz#494c831062f1f9408251ec44db1cba29242a2638"
+  integrity sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==
+  dependencies:
+    readable-stream "^2.0.5"
+
 less@4.1.3:
   version "4.1.3"
   resolved "https://registry.npmmirror.com/less/-/less-4.1.3.tgz#175be9ddcbf9b250173e0a00b4d6920a5b770246"
@@ -6337,6 +6560,13 @@ levn@^0.4.1:
     prelude-ls "^1.2.1"
     type-check "~0.4.0"
 
+lie@~3.3.0:
+  version "3.3.0"
+  resolved "https://registry.npmmirror.com/lie/-/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a"
+  integrity sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==
+  dependencies:
+    immediate "~3.0.5"
+
 lightningcss-darwin-arm64@1.19.0:
   version "1.19.0"
   resolved "https://registry.npmmirror.com/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.19.0.tgz#56ab071e932f845dbb7667f44f5b78441175a343"
@@ -6422,6 +6652,11 @@ lint-staged@^13.0.3:
     string-argv "^0.3.1"
     yaml "^2.2.1"
 
+listenercount@~1.0.1:
+  version "1.0.1"
+  resolved "https://registry.npmmirror.com/listenercount/-/listenercount-1.0.1.tgz#84c8a72ab59c4725321480c975e6508342e70937"
+  integrity sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==
+
 listr2@^5.0.7:
   version "5.0.8"
   resolved "https://registry.npmmirror.com/listr2/-/listr2-5.0.8.tgz#a9379ffeb4bd83a68931a65fb223a11510d6ba23"
@@ -6483,6 +6718,61 @@ lodash.debounce@^4.0.8:
   resolved "https://registry.npmmirror.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
   integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==
 
+lodash.defaults@^4.2.0:
+  version "4.2.0"
+  resolved "https://registry.npmmirror.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c"
+  integrity sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==
+
+lodash.difference@^4.5.0:
+  version "4.5.0"
+  resolved "https://registry.npmmirror.com/lodash.difference/-/lodash.difference-4.5.0.tgz#9ccb4e505d486b91651345772885a2df27fd017c"
+  integrity sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==
+
+lodash.escaperegexp@^4.1.2:
+  version "4.1.2"
+  resolved "https://registry.npmmirror.com/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz#64762c48618082518ac3df4ccf5d5886dae20347"
+  integrity sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==
+
+lodash.flatten@^4.4.0:
+  version "4.4.0"
+  resolved "https://registry.npmmirror.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f"
+  integrity sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==
+
+lodash.groupby@^4.6.0:
+  version "4.6.0"
+  resolved "https://registry.npmmirror.com/lodash.groupby/-/lodash.groupby-4.6.0.tgz#0b08a1dcf68397c397855c3239783832df7403d1"
+  integrity sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==
+
+lodash.isboolean@^3.0.3:
+  version "3.0.3"
+  resolved "https://registry.npmmirror.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6"
+  integrity sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==
+
+lodash.isequal@^4.5.0:
+  version "4.5.0"
+  resolved "https://registry.npmmirror.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
+  integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==
+
+lodash.isfunction@^3.0.9:
+  version "3.0.9"
+  resolved "https://registry.npmmirror.com/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz#06de25df4db327ac931981d1bdb067e5af68d051"
+  integrity sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==
+
+lodash.isnil@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.npmmirror.com/lodash.isnil/-/lodash.isnil-4.0.0.tgz#49e28cd559013458c814c5479d3c663a21bfaa6c"
+  integrity sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng==
+
+lodash.isplainobject@^4.0.6:
+  version "4.0.6"
+  resolved "https://registry.npmmirror.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
+  integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==
+
+lodash.isundefined@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.npmmirror.com/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz#23ef3d9535565203a66cefd5b830f848911afb48"
+  integrity sha512-MXB1is3s899/cD8jheYYE2V9qTHwKvt+npCwpD+1Sxm3Q3cECXCiYHjeHWXNwr6Q0SOBPrYUDxendrO6goVTEA==
+
 lodash.merge@^4.6.2:
   version "4.6.2"
   resolved "https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
@@ -6503,6 +6793,16 @@ lodash.truncate@^4.4.2:
   resolved "https://registry.npmmirror.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193"
   integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==
 
+lodash.union@^4.6.0:
+  version "4.6.0"
+  resolved "https://registry.npmmirror.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88"
+  integrity sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==
+
+lodash.uniq@^4.5.0:
+  version "4.5.0"
+  resolved "https://registry.npmmirror.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
+  integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==
+
 lodash@^3.5.0:
   version "3.10.1"
   resolved "https://registry.npmmirror.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"
@@ -6749,6 +7049,13 @@ minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
   dependencies:
     brace-expansion "^1.1.7"
 
+minimatch@^5.1.0:
+  version "5.1.6"
+  resolved "https://registry.npmmirror.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96"
+  integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==
+  dependencies:
+    brace-expansion "^2.0.1"
+
 minimist-options@4.1.0:
   version "4.1.0"
   resolved "https://registry.npmmirror.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619"
@@ -6758,11 +7065,18 @@ minimist-options@4.1.0:
     is-plain-obj "^1.1.0"
     kind-of "^6.0.3"
 
-minimist@^1.2.0:
+minimist@^1.2.0, minimist@^1.2.6:
   version "1.2.8"
   resolved "https://registry.npmmirror.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
   integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
 
+"mkdirp@>=0.5 0":
+  version "0.5.6"
+  resolved "https://registry.npmmirror.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6"
+  integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==
+  dependencies:
+    minimist "^1.2.6"
+
 ml-array-max@^1.2.4:
   version "1.2.4"
   resolved "https://registry.npmmirror.com/ml-array-max/-/ml-array-max-1.2.4.tgz#2373e2b7e51c8807e456cc0ef364c5863713623b"
@@ -7160,7 +7474,7 @@ pako@~0.2.5:
   resolved "https://registry.npmmirror.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75"
   integrity sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==
 
-pako@~1.0.5:
+pako@~1.0.2, pako@~1.0.5:
   version "1.0.11"
   resolved "https://registry.npmmirror.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf"
   integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==
@@ -8656,7 +8970,7 @@ read-pkg@^5.2.0:
     parse-json "^5.0.0"
     type-fest "^0.6.0"
 
-readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.3.3, readable-stream@^2.3.6:
+readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6:
   version "2.3.8"
   resolved "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b"
   integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==
@@ -8669,7 +8983,7 @@ readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.3.3, readable
     string_decoder "~1.1.1"
     util-deprecate "~1.0.1"
 
-readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.6.0:
+readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0:
   version "3.6.2"
   resolved "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967"
   integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==
@@ -8678,6 +8992,13 @@ readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.6.0:
     string_decoder "^1.1.1"
     util-deprecate "^1.0.1"
 
+readdir-glob@^1.0.0:
+  version "1.1.3"
+  resolved "https://registry.npmmirror.com/readdir-glob/-/readdir-glob-1.1.3.tgz#c3d831f51f5e7bfa62fa2ffbe4b508c640f09584"
+  integrity sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==
+  dependencies:
+    minimatch "^5.1.0"
+
 readdirp@~3.6.0:
   version "3.6.0"
   resolved "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
@@ -8871,7 +9192,14 @@ rfdc@^1.3.0:
   resolved "https://registry.npmmirror.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b"
   integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==
 
-rimraf@^3.0.2:
+rimraf@2:
+  version "2.7.1"
+  resolved "https://registry.npmmirror.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
+  integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
+  dependencies:
+    glob "^7.1.3"
+
+rimraf@^3.0.0, rimraf@^3.0.2:
   version "3.0.2"
   resolved "https://registry.npmmirror.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
   integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
@@ -8963,6 +9291,13 @@ sax@~1.1.1:
   resolved "https://registry.npmmirror.com/sax/-/sax-1.1.6.tgz#5d616be8a5e607d54e114afae55b7eaf2fcc3240"
   integrity sha512-8zci48uUQyfqynGDSkUMD7FCJB96hwLnlZOXlgs1l3TX+LW27t3psSWKUxC0fxVgA86i8tL4NwGcY1h/6t3ESg==
 
+saxes@^5.0.1:
+  version "5.0.1"
+  resolved "https://registry.npmmirror.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d"
+  integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==
+  dependencies:
+    xmlchars "^2.2.0"
+
 scheduler@^0.22.0:
   version "0.22.0"
   resolved "https://registry.npmmirror.com/scheduler/-/scheduler-0.22.0.tgz#83a5d63594edf074add9a7198b1bae76c3db01b8"
@@ -9022,7 +9357,7 @@ semver@^7.3.4, semver@^7.3.5, semver@^7.3.7:
   dependencies:
     lru-cache "^6.0.0"
 
-setimmediate@^1.0.4:
+setimmediate@^1.0.4, setimmediate@^1.0.5, setimmediate@~1.0.4:
   version "1.0.5"
   resolved "https://registry.npmmirror.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
   integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==
@@ -9590,6 +9925,17 @@ tapable@^2.0.0, tapable@^2.2.0, tapable@^2.2.1:
   resolved "https://registry.npmmirror.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0"
   integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==
 
+tar-stream@^2.2.0:
+  version "2.2.0"
+  resolved "https://registry.npmmirror.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287"
+  integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==
+  dependencies:
+    bl "^4.0.3"
+    end-of-stream "^1.4.1"
+    fs-constants "^1.0.0"
+    inherits "^2.0.3"
+    readable-stream "^3.1.1"
+
 terser@^5.10.0:
   version "5.16.8"
   resolved "https://registry.npmmirror.com/terser/-/terser-5.16.8.tgz#ccde583dabe71df3f4ed02b65eb6532e0fae15d5"
@@ -9656,6 +10002,13 @@ tinycolor2@^1.4.1, tinycolor2@^1.4.2:
   resolved "https://registry.npmmirror.com/tinycolor2/-/tinycolor2-1.6.0.tgz#f98007460169b0263b97072c5ae92484ce02d09e"
   integrity sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==
 
+tmp@^0.2.0:
+  version "0.2.1"
+  resolved "https://registry.npmmirror.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14"
+  integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==
+  dependencies:
+    rimraf "^3.0.0"
+
 tmpl@1.0.5:
   version "1.0.5"
   resolved "https://registry.npmmirror.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc"
@@ -9688,6 +10041,11 @@ toposort@^2.0.2:
   resolved "https://registry.npmmirror.com/toposort/-/toposort-2.0.2.tgz#ae21768175d1559d48bef35420b2f4962f09c330"
   integrity sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==
 
+"traverse@>=0.3.0 <0.4":
+  version "0.3.9"
+  resolved "https://registry.npmmirror.com/traverse/-/traverse-0.3.9.tgz#717b8f220cc0bb7b44e40514c22b2e8bbc70d8b9"
+  integrity sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==
+
 trim-newlines@^3.0.0:
   version "3.0.1"
   resolved "https://registry.npmmirror.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144"
@@ -9866,6 +10224,22 @@ universalify@^2.0.0:
   resolved "https://registry.npmmirror.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717"
   integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==
 
+unzipper@^0.10.11:
+  version "0.10.14"
+  resolved "https://registry.npmmirror.com/unzipper/-/unzipper-0.10.14.tgz#d2b33c977714da0fbc0f82774ad35470a7c962b1"
+  integrity sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==
+  dependencies:
+    big-integer "^1.6.17"
+    binary "~0.3.0"
+    bluebird "~3.4.1"
+    buffer-indexof-polyfill "~1.0.0"
+    duplexer2 "~0.1.4"
+    fstream "^1.0.12"
+    graceful-fs "^4.2.2"
+    listenercount "~1.0.1"
+    readable-stream "~2.3.6"
+    setimmediate "~1.0.4"
+
 update-browserslist-db@^1.0.10:
   version "1.0.10"
   resolved "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz#0f54b876545726f17d00cd9a2561e6dade943ff3"
@@ -9938,6 +10312,11 @@ utility-types@^3.10.0:
   resolved "https://registry.npmmirror.com/utility-types/-/utility-types-3.10.0.tgz#ea4148f9a741015f05ed74fd615e1d20e6bed82b"
   integrity sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==
 
+uuid@^8.3.0:
+  version "8.3.2"
+  resolved "https://registry.npmmirror.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
+  integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
+
 v8-compile-cache@^2.3.0:
   version "2.3.0"
   resolved "https://registry.npmmirror.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee"
@@ -10153,6 +10532,11 @@ xmlbuilder@~2.6.4:
   dependencies:
     lodash "^3.5.0"
 
+xmlchars@^2.2.0:
+  version "2.2.0"
+  resolved "https://registry.npmmirror.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"
+  integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==
+
 xtend@^4.0.0:
   version "4.0.2"
   resolved "https://registry.npmmirror.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
@@ -10210,3 +10594,12 @@ yocto-queue@^0.1.0:
   version "0.1.0"
   resolved "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
   integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
+
+zip-stream@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.npmmirror.com/zip-stream/-/zip-stream-4.1.0.tgz#51dd326571544e36aa3f756430b313576dc8fc79"
+  integrity sha512-zshzwQW7gG7hjpBlgeQP9RuyPGNxvJdzR8SUM3QhxCnLjWN2E7j3dOvpeDcQoETfHx0urRS7EtmVToql7YpU4A==
+  dependencies:
+    archiver-utils "^2.1.0"
+    compress-commons "^4.1.0"
+    readable-stream "^3.6.0"