huangguoce пре 5 месеци
родитељ
комит
1b2bb8f7e3
32 измењених фајлова са 9921 додато и 5702 уклоњено
  1. 1 0
      .gitignore
  2. 206 154
      api/sys/OSSService.js
  3. 327 0
      pages/common/UploadComponent.vue
  4. 1267 1272
      pages/cw/invoice/InvoiceFormTask.vue
  5. 90 88
      pages/cw/reimbursementApproval/info/CwReportChoose.vue
  6. 1213 1170
      pages/cw/reimbursementApproval/info/ReimbursementForm.vue
  7. 539 584
      pages/dailyOfficeWork/holiday/HolidayForm.vue
  8. 337 331
      pages/generateForm/InvoiceModule.vue
  9. 682 673
      pages/materialManagement/collect/CollectForm.vue
  10. 810 798
      pages/materialManagement/purchase/PurchaseForm.vue
  11. 649 630
      pages/workbench/task/TaskForm.vue
  12. 4 0
      uni_modules/mumu-h5office/changelog.md
  13. 142 0
      uni_modules/mumu-h5office/components/mumu-h5office/app-get-file.vue
  14. 19 0
      uni_modules/mumu-h5office/components/mumu-h5office/config.js
  15. 753 0
      uni_modules/mumu-h5office/components/mumu-h5office/index.umd.js
  16. 2 0
      uni_modules/mumu-h5office/components/mumu-h5office/jsencrypt.min.js
  17. 219 0
      uni_modules/mumu-h5office/components/mumu-h5office/md5.js
  18. 405 0
      uni_modules/mumu-h5office/components/mumu-h5office/mumu-h5office.vue
  19. 127 0
      uni_modules/mumu-h5office/components/mumu-h5office/tool.js
  20. 84 0
      uni_modules/mumu-h5office/package.json
  21. 94 0
      uni_modules/mumu-h5office/readme.md
  22. 9 0
      uni_modules/mumu-previewOffce/changelog.md
  23. 215 0
      uni_modules/mumu-previewOffce/components/mumu-previewOffce/mumu-previewOffce.vue
  24. 85 0
      uni_modules/mumu-previewOffce/package.json
  25. 85 0
      uni_modules/mumu-previewOffce/readme.md
  26. 7 0
      uni_modules/xe-upload/changelog.md
  27. 285 0
      uni_modules/xe-upload/components/xe-upload/xe-upload.vue
  28. 80 0
      uni_modules/xe-upload/package.json
  29. 78 0
      uni_modules/xe-upload/readme.md
  30. 183 0
      uni_modules/xe-upload/tools/apis.js
  31. 180 0
      uni_modules/xe-upload/tools/tools.js
  32. 744 2
      yarn.lock

+ 1 - 0
.gitignore

@@ -0,0 +1 @@
+node_modules

+ 206 - 154
api/sys/OSSService.js

@@ -1,151 +1,163 @@
-import OSS from 'ali-oss'
+import OSS from "ali-oss";
 // import { ElMessage } from 'element-plus'
-import request from "../../common/request"
+import request from "../../common/request";
 import { PUBLIC_MODULES_PATH as prefix } from "@/api/AppPath";
 
 export default class OSSSerive {
-  disposeXmlFile (data) {
+  disposeXmlFile(data) {
     return request({
-      url: prefix + '/oss/file/disposeXmlFile',
-      method: 'post',
-      data: data
-    })
+      url: prefix + "/oss/file/disposeXmlFile",
+      method: "post",
+      data: data,
+    });
   }
-  downLoadFileDisposeXmlFile (url) {
+  downLoadFileDisposeXmlFile(url) {
     return request({
-      url: prefix + '/oss/file/downLoadFileDisposeXmlFile',
-      method: 'post',
-      params: {file: url}
-    })
+      url: prefix + "/oss/file/downLoadFileDisposeXmlFile",
+      method: "post",
+      params: { file: url },
+    });
   }
-	queryById (id) {
-		return request({
-			url: prefix + '/oss/file/queryById',
-			method: 'get',
-			params: { id: id },
-		})
-	}
-  saveMsg (param) {
+  queryById(id) {
     return request({
-      url: prefix + '/oss/file/saveMsg',
-      method: 'post',
-      data: param
-    })
+      url: prefix + "/oss/file/queryById",
+      method: "get",
+      params: { id: id },
+    });
   }
-  findFileList (attachmentId) {
+  saveMsg(param) {
     return request({
-		  url: prefix + '/oss/file/findFileList',
-      method: 'get',
-      params: {attachmentId: attachmentId}
-    })
+      url: prefix + "/oss/file/saveMsg",
+      method: "post",
+      data: param,
+    });
   }
-  getTemporaryUrl (url) {
+  findFileList(attachmentId) {
     return request({
-      url: prefix + '/oss/file/getTemporaryUrl',
-      method: 'get',
-      params: {url: url}
-    })
+      url: prefix + "/oss/file/findFileList",
+      method: "get",
+      params: { attachmentId: attachmentId },
+    });
   }
-  getFileSizeByUrl (url) {
+  getTemporaryUrl(url) {
     return request({
-      url: prefix + '/oss/file/getFileSizeByUrl',
-      method: 'get',
-      params: {url: url}
-    })
+      url: prefix + "/oss/file/getTemporaryUrl",
+      method: "get",
+      params: { url: url },
+    });
   }
-  deleteMsgById (id) {
+  getFileSizeByUrl(url) {
     return request({
-      url: prefix + '/oss/file/deleteMsgById',
-      method: 'get',
-      params: {id: id}
-    })
+      url: prefix + "/oss/file/getFileSizeByUrl",
+      method: "get",
+      params: { url: url },
+    });
   }
-  downLoadAttach (url) {
+  deleteMsgById(id) {
     return request({
-      url: prefix + '/oss/file/downLoadAttach',
-      method: 'get',
-      responseType: 'blob',
-      params: {file: url}
-    })
+      url: prefix + "/oss/file/deleteMsgById",
+      method: "get",
+      params: { id: id },
+    });
+  }
+  downLoadAttach(url) {
+    return request({
+      url: prefix + "/oss/file/downLoadAttach",
+      method: "get",
+      responseType: "blob",
+      params: { file: url },
+    });
   }
 }
 
 export const client = new OSS({
-  region: 'oss-cn-hangzhou', // oss地址
-  accessKeyId: 'LTAI5tQDWoM9c1WyJNPs86rX', // 通过阿里云控制台创建的AccessKey ID。
-  accessKeySecret: '84dDIx4edT1n78KUOqqSmDZ35pchJv', // 通过阿里云控制台创建的AccessKey Secret。
-  bucket: 'xgxm-test', // 仓库名字
-	// bucket: 'xg-pg', // 仓库名字
+  // region: 'oss-cn-hangzhou', // oss地址
+  // accessKeyId: 'LTAI5tQDWoM9c1WyJNPs86rX', // 通过阿里云控制台创建的AccessKey ID。
+  // accessKeySecret: '84dDIx4edT1n78KUOqqSmDZ35pchJv', // 通过阿里云控制台创建的AccessKey Secret。
+  // bucket: 'xgxm-test', // 仓库名字
+  region: "oss-cn-hangzhou", // oss地址
+  accessKeyId: "LTAI5tKa6kzGr5EyPWJB4EcD", // 通过阿里云控制台创建的AccessKey ID。
+  accessKeySecret: "arHxB7ZPhizrBYf4844TtyaRctPMgW", // 通过阿里云控制台创建的AccessKey Secret。
+  bucket: "xg-ccpm", // 仓库名字
+  // bucket: 'xg-pg', // 仓库名字
   useFetch: true, // 支持上传大于100KB的文件
-  secure: false // 返回的url为https
-})
+  secure: false, // 返回的url为https
+});
 
 export const headers = {
   // 指定该Object被下载时网页的缓存行为。
-  'Cache-Control': 'no-cache',
+  "Cache-Control": "no-cache",
   // 指定该Object被下载时的名称。
-  'Content-Disposition': 'inline',
+  "Content-Disposition": "inline",
   // 指定该Object被下载时的内容编码格式。
-  'Content-Encoding': 'UTF-8',
+  "Content-Encoding": "UTF-8",
   // 指定过期时间。
-  Expires: 'Wed, 08 Jul 2023 16:57:01 GMT',
+  Expires: "Wed, 08 Jul 2023 16:57:01 GMT",
   // 指定Object的存储类型。
-  'x-oss-storage-class': 'Standard',
+  "x-oss-storage-class": "Standard",
   // 指定Object的访问权限。
   // 'x-oss-object-acl': 'private',
   // 设置Object的标签,可同时设置多个标签。
-  'x-oss-tagging': 'Tag1=1&Tag2=2',
+  "x-oss-tagging": "Tag1=1&Tag2=2",
   // 指定CopyObject操作时是否覆盖同名目标Object。此处设置为true,表示禁止覆盖同名Object。
-  'x-oss-forbid-overwrite': 'true'
+  "x-oss-forbid-overwrite": "true",
   // 'secure': 'true'
-}
+};
 
-export function onChange (file, files) {
+export function onChange(file, files) {
   // console.log(file, 'file', files, 'Filest')
-  return files
+  return files;
 }
 
 // eslint-disable-next-line no-unused-vars
-export function handleUploadSuccess (response, file, fileS) {
+export function handleUploadSuccess(response, file, fileS) {
   // console.log(response, file, fileS, 'response, file, fileList')
 }
 
-export function handleRemove (e, files) {
-  return files
+export function handleRemove(e, files) {
+  return files;
 }
 
-export function fileName (file) {
+export function fileName(file) {
   // console.log('文件名称处理')
-  const tmpcnt = file.file.name.lastIndexOf('.')
-  let fileName = file.file.name.substring(0, tmpcnt)
+  const tmpcnt = file.file.name.lastIndexOf(".");
+  let fileName = file.file.name.substring(0, tmpcnt);
   // 将单引号‘’都转换成',将双引号“”都转换成"
-  fileName = fileName.replace(/’|‘/g, '\'').replace(/“|”/g, '')
+  fileName = fileName.replace(/’|‘/g, "'").replace(/“|”/g, "");
   // 将中括号【】转换成[],将大括号{}转换成{}
-  fileName = fileName.replace(/【/g, '(').replace(/】/g, ')').replace(/{/g, '(').replace(/}/g, ')')
-  fileName = fileName.replace(/\[/g, '(').replace(/]/g, ')').replace(/{/g, '(').replace(/}/g, ')')
+  fileName = fileName
+    .replace(/【/g, "(")
+    .replace(/】/g, ")")
+    .replace(/{/g, "(")
+    .replace(/}/g, ")");
+  fileName = fileName
+    .replace(/\[/g, "(")
+    .replace(/]/g, ")")
+    .replace(/{/g, "(")
+    .replace(/}/g, ")");
   // 将逗号,转换成,,将:转换成:
-  fileName = fileName.replace(/,/g, ',').replace(/:/g, ':')
+  fileName = fileName.replace(/,/g, ",").replace(/:/g, ":");
   // 将中文——转换为英文-
-  fileName = fileName.replace(/—/g, '-')
-  fileName = fileName.replace(/……/g, '')
-  fileName = fileName.replace(/±/g, '')
-  fileName = fileName.replace(/#/g, '')
-  fileName = fileName.replace(/%/g, '')
-  fileName = fileName.replace(/Π/g, '')
-  fileName = fileName.replace(/π/g, '')
-  fileName = fileName.replace(/·/g, '.')
+  fileName = fileName.replace(/—/g, "-");
+  fileName = fileName.replace(/……/g, "");
+  fileName = fileName.replace(/±/g, "");
+  fileName = fileName.replace(/#/g, "");
+  fileName = fileName.replace(/%/g, "");
+  fileName = fileName.replace(/Π/g, "");
+  fileName = fileName.replace(/π/g, "");
+  fileName = fileName.replace(/·/g, ".");
   // console.log('文件名处理结果', fileName)
-  return fileName
+  return fileName;
 }
 
-export function beforeAvatarUpload (file, fileList, maxValue) {
+export function beforeAvatarUpload(file, fileList, maxValue) {
   // 文件大小校验
-  const isLt2M = file.size / 1024 / 1024 < maxValue
+  const isLt2M = file.size / 1024 / 1024 < maxValue;
   if (!isLt2M) {
-    fileList.splice(fileList.length - 1, 1)
+    fileList.splice(fileList.length - 1, 1);
     // console.log('文件大小不能超过 ' + maxValue + ' MB!')
   }
-  return isLt2M
+  return isLt2M;
 }
 
 // export function exnameFix (file, isShow, names) {
@@ -164,96 +176,136 @@ export function beforeAvatarUpload (file, fileList, maxValue) {
 //   return true
 // }
 
-export async function httpRequest (file, name, type, maxValue) { // 阿里云OSS上传
+export async function httpRequest(file, name, type, maxValue) {
+  // 阿里云OSS上传
   if (!beforeAvatarUpload(file.file, [], maxValue)) {
-    return
+    return;
   }
   // console.log('开始上传')
   // 判断扩展名
-  const tmpcnt = file.file.name.lastIndexOf('.') // 获取.的下标
-  const exname = file.file.name.substring(tmpcnt + 1) // 获取后缀名
+  const tmpcnt = file.file.name.lastIndexOf("."); // 获取.的下标
+  const exname = file.file.name.substring(tmpcnt + 1); // 获取后缀名
   // console.log(exname, '后缀名')
-  const now = new Date()
-  const year = now.getFullYear()
-  const month = now.getMonth() + 1
-  const day = now.getDate() > 10 ? now.getDate() : '0' + now.getDate()
-  const filePath = '/attachment-file/assess/' + type + '/' + year + '/' + month + '/' + day + '/' + now.getTime()
+  const now = new Date();
+  const year = now.getFullYear();
+  const month = now.getMonth() + 1;
+  const day = now.getDate() > 10 ? now.getDate() : "0" + now.getDate();
+  const filePath =
+    "/attachment-file/assess/" +
+    type +
+    "/" +
+    year +
+    "/" +
+    month +
+    "/" +
+    day +
+    "/" +
+    now.getTime();
   // console.log(filePath, '文件存储路径')
-  const fileName = filePath + name + '.' + exname
+  const fileName = filePath + name + "." + exname;
   // console.log(fileName, '文件名')
 
-  await client.multipartUpload(fileName, file.file, {
-	  // eslint-disable-next-line no-unused-vars
-    progress: await function (p, checkpoint) {
-      file.onProgress({percent: Math.floor(p * 100)}) // 触发el-upload组件的onProgress方法
-    }
-    // mime: type,
-  }).then(await function (result) {
-    // console.log(result)
-    if (result.res.status === 200) {
-      // file.onSuccess(result) // 触发el-upload组件的onSuccess方法
-      file.file.url = fileName
-    }
-	  // eslint-disable-next-line handle-callback-err,no-unused-vars
-  }).catch(function (err) {
-    // console.log(err)
-    file.onError('上传失败') // 触发el-upload组件的onError方法,此方法会移除文件列表
-  })
+  await client
+    .multipartUpload(fileName, file.file, {
+      // eslint-disable-next-line no-unused-vars
+      progress: await function(p, checkpoint) {
+        file.onProgress({ percent: Math.floor(p * 100) }); // 触发el-upload组件的onProgress方法
+      },
+      // mime: type,
+    })
+    .then(
+      await function(result) {
+        // console.log(result)
+        if (result.res.status === 200) {
+          // file.onSuccess(result) // 触发el-upload组件的onSuccess方法
+          file.file.url = fileName;
+        }
+        // eslint-disable-next-line handle-callback-err,no-unused-vars
+      }
+    )
+    .catch(function(err) {
+      // console.log(err)
+      file.onError("上传失败"); // 触发el-upload组件的onError方法,此方法会移除文件列表
+    });
 }
 
 // eslint-disable-next-line no-unused-vars
-function getTemporaryByUrl (url) {
+function getTemporaryByUrl(url) {
   return request({
-    url: prefix + '/oss/file/getTemporaryUrl',
-    method: 'get',
-    params: {url: url}
-  })
+    url: prefix + "/oss/file/getTemporaryUrl",
+    method: "get",
+    params: { url: url },
+  });
 }
 
-export async function openWindowOnUrl (row) {
-  if (row.url === undefined || row.url === null || row.url === '') {
+export async function openWindowOnUrl(row) {
+  if (row.url === undefined || row.url === null || row.url === "") {
     // Message.error('没有获取到文件的url')
-    return
+    return;
   }
-  let rowUrl = ''
-  let suffix = row.name.substring(row.name.lastIndexOf('.') + 1)
-  if (suffix === 'jpg' || suffix === 'png' || suffix === 'gif' || suffix === 'bmp' || suffix === 'jpeg') {
+  let rowUrl = "";
+  let suffix = row.name.substring(row.name.lastIndexOf(".") + 1);
+  if (
+    suffix === "jpg" ||
+    suffix === "png" ||
+    suffix === "gif" ||
+    suffix === "bmp" ||
+    suffix === "jpeg"
+  ) {
     await getTemporaryByUrl(row.url).then((data) => {
       // eslint-disable-next-line no-undef
-      onPreview(data)
-    })
-    return
+      onPreview(data);
+    });
+    return;
   }
   await getTemporaryByUrl(row.url).then((data) => {
-    rowUrl = data
-  })
-  if (suffix === 'pdf') {
-    window.open('https://view.xdocin.com/xdoc?_xdoc=' + encodeURIComponent(rowUrl), '_blank')
-  } else if (suffix === 'rar' || suffix === 'zip' || suffix === 'jar' || suffix === '7z') {
-    window.open('https://view.xdocin.com/xdoc?_xdoc=' + encodeURIComponent(rowUrl), '_blank')
+    rowUrl = data;
+  });
+  if (suffix === "pdf") {
+    window.open(
+      "https://view.xdocin.com/xdoc?_xdoc=" + encodeURIComponent(rowUrl),
+      "_blank"
+    );
+  } else if (
+    suffix === "rar" ||
+    suffix === "zip" ||
+    suffix === "jar" ||
+    suffix === "7z"
+  ) {
+    window.open(
+      "https://view.xdocin.com/xdoc?_xdoc=" + encodeURIComponent(rowUrl),
+      "_blank"
+    );
   } else {
-    window.open('https://view.officeapps.live.com/op/view.aspx?src=' + encodeURIComponent(rowUrl), '_blank')
+    window.open(
+      "https://view.officeapps.live.com/op/view.aspx?src=" +
+        encodeURIComponent(rowUrl),
+      "_blank"
+    );
   }
 }
 
-export async function toHref (row) {
-  if (row.url === null || row.url === undefined || row.url === '') {
+export async function toHref(row) {
+  if (row.url === null || row.url === undefined || row.url === "") {
     // Message.error('没有获取到文件的url')
-    return
+    return;
   }
-  const link = document.createElement('a')
+  const link = document.createElement("a");
   await getTemporaryByUrl(row.url).then((data) => {
-    const url = data // 完整的url则直接使用
+    const url = data; // 完整的url则直接使用
     // 这里是将url转成blob地址,
-    fetch(url).then(res => res.blob()).then(blob => { // 将链接地址字符内容转变成blob地址
-      link.href = URL.createObjectURL(blob)
-      link.download = row.name || '' // 下载文件的名字
-      // a.download = url.split('/')[url.split('/').length -1] //  // 下载文件的名字
-      document.body.appendChild(link)
-      link.click()
-      // 在资源下载完成后 清除 占用的缓存资源
-      window.URL.revokeObjectURL(link.href)
-      document.body.removeChild(link)
-    })
-  })
+    fetch(url)
+      .then((res) => res.blob())
+      .then((blob) => {
+        // 将链接地址字符内容转变成blob地址
+        link.href = URL.createObjectURL(blob);
+        link.download = row.name || ""; // 下载文件的名字
+        // a.download = url.split('/')[url.split('/').length -1] //  // 下载文件的名字
+        document.body.appendChild(link);
+        link.click();
+        // 在资源下载完成后 清除 占用的缓存资源
+        window.URL.revokeObjectURL(link.href);
+        document.body.removeChild(link);
+      });
+  });
 }

+ 327 - 0
pages/common/UploadComponent.vue

@@ -0,0 +1,327 @@
+<template>
+    <view style="width: 100%;height: 100%;">
+        <view class="upload-wrap">
+            <view class="btn-click mgb-16 upload-btn" @click="handleUploadClick()" v-show="isUpload === false">
+                <!-- <image :src="icons.upload" mode="aspectFill" class="upload-icon" /> -->
+                <i class="el-icon-upload2"></i>
+                <text class="upload-text">上传附件</text>
+                <text class="upload-text1">{{ `只能上传不超过${this.limit}个文件` }}</text>
+            </view>
+            <view class="mgb-16 file-wrap" v-for="(item, index) in fileList" :key="index">
+                <view class="btn-click file-line" @click="handlePreview(item)">
+                    <!-- <view class="btn-click file-line" @click="handleUploadFile(item)"> -->
+                    <view class="file-info">
+                        <i :class="`el-icon-${icons[getFileType(item.name) || 'file']}`"></i>
+                        <text class="file-name">{{ item.name || title[type] }}</text>
+                    </view>
+
+                    <i :class='`el-icon-${icons.close}`' v-if="isDelete === false"
+                        @click.stop="handleDeleteFile(index)"></i>
+                </view>
+            </view>
+            <view class="mgb-16 file-wrap" v-if="fileList.length === 0">
+                <view class="file-line">
+                    <text class="file-empty">暂无数据</text>
+                </view>
+            </view>
+        </view>
+        <xe-upload ref="XeUpload" :options="{ url: uploadUrl }" @callback="handleUploadCallback"></xe-upload>
+
+        <u-action-sheet @close="uploadShow = false" cancelText="取消" :actions="uploadList"
+            @select="handleChangeUploadType" :title="uploadTitle" :show="uploadShow"></u-action-sheet>
+        <mumu-previewOffce :fileType="prviewFileType" :fileUrl='prviewFileUrl'
+            v-model='showPreview'></mumu-previewOffce>
+        <u-popup v-if="videoShow" @close="handleCloseVideo" mode="top" :overlay="true" :show="videoShow"
+            :closeOnClickOverlay="true">
+            <view v-if="videoShow" style="width: 100%;height: 100%;">
+                <video v-if="videoShow" style="width: 100%;" :src="videoUrl" controls preload="auto" autoPlay={true} />
+            </view>
+        </u-popup>
+
+    </view>
+</template>
+
+<script>
+import MumuPreviewOffce from '@/uni_modules/mumu-previewOffce/components/mumu-previewOffce/mumu-previewOffce.vue'
+import { mapState, mapMutations, mapActions } from 'vuex'
+import OSSService from "@/api/sys/OSSService"
+
+export default {
+    components: {
+        MumuPreviewOffce
+    },
+    computed: mapState({
+        userInfo: (state) => state.user.userInfo,
+    }),
+    created() {
+        this.ossService = new OSSService()
+    },
+    props: {
+        //上传地址
+        uploadUrl: {
+            type: String,
+            default: ''
+        },
+        // 文件列表
+        fileList: {
+            type: Array,
+            default: () => []
+        },
+        // 最大上传个数
+        limit: {
+            type: Number,
+            default: 9
+        },
+        // 是否可以删除
+        isDelete: {
+            type: Boolean,
+            default: false
+        },
+        // 是否可以上传
+        isUpload: {
+            type: Boolean,
+            default: false
+        },
+    },
+    ossService: null,
+    data() {
+        return {
+            uploadType: '',
+            videoShow: false,
+            videoUrl: '',
+            prviewFileType: '',
+            prviewFileUrl: '',
+            showPreview: false,
+            icons: {
+                close: 'circle-close',
+                image: 'picture-outline',
+                video: 'video-camera',
+                file: 'document',
+                audio: 'bell',
+                unknown: 'document'
+            },
+            uploadFile: '',
+            uploadShow: false,
+            uploadTitle: '附件上传',
+            uploadList: [
+                {
+                    name: '视频上传',
+                },
+                {
+                    name: '图片上传',
+                },
+                {
+                    name: '文件上传',
+                }
+            ],
+        }
+
+    },
+    methods: {
+        handleCloseVideo() {
+            this.videoUrl = ""
+            this.videoShow = false
+        },
+        // 预览
+        handlePreview(row) {
+            const fileType = this.getFileType(row.name);
+            const flieArr = row.name.split('.');
+            let suffix = flieArr[flieArr.length - 1];
+            this.ossService.getTemporaryUrl("/" + row.url).then((data) => {
+                if (fileType == "image") {
+                    return uni.previewImage({
+                        current: data,
+                        urls: [data],
+                    });
+
+                }
+                if (fileType == "video") {
+                    this.videoUrl = data
+                    this.videoShow = true
+                    return
+                }
+                if (fileType == "file") {
+                    this.prviewFileUrl = data
+                    this.prviewFileType = suffix
+                    this.showPreview = true
+                    return
+                }
+                uni.showModal({
+                    title: '提示',
+                    content: data,
+                    showCancel: false,
+                });
+            })
+
+
+        },
+        handleDeleteFile(index) {
+            console.log();
+
+            const deletedFile = this.fileList[index]; // 获取被删除的文件
+            // this.fileList.splice(index, 1); // 删除文件
+            this.$emit("onRemove", deletedFile, this.fileList, index); // 传递剩余文件列表和被删除的文件
+        },
+        getFileType(fileName = '') {
+            const flieArr = fileName.split('.');
+            let suffix = flieArr[flieArr.length - 1];
+            if (!suffix) return '';
+            suffix = suffix.toLocaleLowerCase();
+            const image = ['png', 'jpg', 'jpeg', 'bmp', 'gif', 'webp'];
+            if (image.includes(suffix)) return 'image';
+            const video = ['mp4', 'm4v'];
+            if (video.includes(suffix)) return 'video';
+            const audio = ['mp3', 'm4a', 'wav', 'aac'];
+            if (audio.includes(suffix)) return 'audio';
+            const office = ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'plain'];
+            if (office.includes(suffix)) return 'file';
+            return 'unknown';
+        },
+        handleUploadClick() {
+            if (this.fileList.length >= this.limit) {
+                uni.showModal({
+                    title: '提示',
+                    content: `只能上传不超过${this.limit}个文件`,
+                    showCancel: false,
+                });
+                return
+            }
+            this.uploadShow = true
+            this.uploadType = 'type'
+        },
+        handleChangeUploadType(index) {
+            if (index.name == "视频上传") {
+                this.$refs.XeUpload.upload('video', {});
+            }
+            if (index.name == "图片上传") {
+                this.$refs.XeUpload.upload('image', {});
+            }
+            if (index.name == "文件上传") {
+                this.$refs.XeUpload.upload('file', {});
+            }
+        },
+        handleUploadCallback(e) {
+            this.uploadShow = false
+            const file = e.data[0]
+            this.fileList.push({
+                name: file.name,
+                size: file.size,
+                url: file.response.url,
+                lsUrl: file.response.lsUrl,
+                createBy: this.userInfo,
+                by: this.userInfo.id,
+                createTime: this.formatDateNew(new Date())
+            })
+            console.log(this.fileList);
+
+            this.$emit("onSuccess", file, this.fileList)
+        },
+        formatDateNew(date) {
+            const year = date.getFullYear();
+            const month = (date.getMonth() + 1).toString().padStart(2, '0');
+            const day = date.getDate().toString().padStart(2, '0');
+            const hours = date.getHours().toString().padStart(2, '0');
+            const minutes = date.getMinutes().toString().padStart(2, '0');
+            const seconds = date.getSeconds().toString().padStart(2, '0');
+            return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
+        },
+
+    }
+}
+</script>
+
+<style scoped lang="scss">
+.btn-click {
+    transition: all 0.3s;
+    opacity: 1;
+}
+
+.btn-click:active {
+    opacity: 0.5;
+}
+
+.mgb-16 {
+    margin-bottom: 16rpx;
+
+    &:last-child {
+        margin-bottom: 0;
+    }
+}
+
+.upload-wrap {
+    width: 100%;
+    border-radius: 16rpx;
+    background: white;
+
+    .upload-btn {
+        width: 100%;
+        height: 176rpx;
+        border: 2rpx dashed;
+        color: #409EFF;
+        border-color: #b3d8ff;
+        background: #ecf5ff;
+        border-radius: 16rpx;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        flex-direction: column;
+
+        .upload-icon {
+            width: 48rpx;
+            height: 48rpx;
+            margin-bottom: 8rpx;
+        }
+
+        .upload-text {
+            font-size: 26rpx;
+            line-height: 40rpx;
+        }
+
+        .upload-text1 {
+            font-size: 20rpx;
+            line-height: 40rpx;
+        }
+    }
+
+    .file-wrap {
+        .file-line {
+            width: 100%;
+            border-color: #b3d8ff;
+            background: #ecf5ff;
+            border-radius: 8rpx;
+            padding: 16rpx;
+            font-size: 26rpx;
+            color: #409EFF;
+
+            line-height: 40rpx;
+            display: flex;
+            align-items: center;
+            justify-content: space-between;
+
+            .file-info {
+                width: 90%;
+                display: flex;
+                align-items: center;
+
+                .file-name {
+                    max-width: 80%;
+                    padding-left: 16rpx;
+                    overflow: hidden;
+                    text-overflow: ellipsis;
+                    white-space: nowrap;
+                }
+            }
+
+            .file-icon {
+                width: 40rpx;
+                height: 40rpx;
+                flex-shrink: 0;
+            }
+
+            .file-empty {
+                color: #999999;
+            }
+        }
+    }
+}
+</style>

Разлика између датотеке није приказан због своје велике величине
+ 1267 - 1272
pages/cw/invoice/InvoiceFormTask.vue


+ 90 - 88
pages/cw/reimbursementApproval/info/CwReportChoose.vue

@@ -1,22 +1,24 @@
 <template>
 	<view style="width: 100%;">
-<!--		<view style="display: flex; align-items: center;">-->
-<!--			<view>{{ labels }}</view>-->
-<!--			<view style="color: #909399; margin-left: 5px;">-->
-<!--				<image src="YOUR_ARROW_RIGHT_ICON_URL" style="width: 16px; height: 16px;" />-->
-<!--			</view>-->
-<!--		</view>-->
+		<!--		<view style="display: flex; align-items: center;">-->
+		<!--			<view>{{ labels }}</view>-->
+		<!--			<view style="color: #909399; margin-left: 5px;">-->
+		<!--				<image src="YOUR_ARROW_RIGHT_ICON_URL" style="width: 16px; height: 16px;" />-->
+		<!--			</view>-->
+		<!--		</view>-->
 
 		<u-action-sheet style="min-height: 200px" :show="show" @close="show = false">
 			<view class="cu-bar bg-white">
-				<view class="action text-blue"  @tap="onClose">取消</view>
+				<view class="action text-blue" @tap="onClose">取消</view>
 				<view class="action text-green" @tap="selectUsers">确定</view>
 			</view>
-<!--			overflow-y: auto;  滚动效果-->
+			<!--			overflow-y: auto;  滚动效果-->
 			<view style="max-height: 300px; overflow-y: auto;">
 				<view v-for="item in data" :key="item.id" style="padding: 10px;">
 					<view @tap="onItemClick(item)" style="display: flex; align-items: center;">
-						<view style="flex: 1;text-align: left; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">{{ item.reportNo }}</view>
+						<view
+							style="flex: 1;text-align: left; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
+							{{ item.reportNo }}</view>
 						<view v-if="item.checked" style="color: #409eff;">已选择</view>
 					</view>
 				</view>
@@ -26,90 +28,90 @@
 </template>
 
 <script>
-	import reimbursementApprovalService from '@/api/cw/reimbursementApproval/ReimbursementApprovalService'
+import reimbursementApprovalService from '@/api/cw/reimbursementApproval/ReimbursementApprovalService'
 
-	export default {
-		data() {
-			return {
-				show: false,
-				labels:'',
-				index:'',
-				projectId:'',
-				data: [],
-				localMode: '', // 声明一个本地数据属性
-			};
+export default {
+	data() {
+		return {
+			show: false,
+			labels: '',
+			index: '',
+			projectId: '',
+			data: [],
+			localMode: '', // 声明一个本地数据属性
+		};
+	},
+	props: {
+		value: String,
+		placeholder: {
+			type: String,
+			default: '请选择项目'
 		},
-		props: {
-			value: String,
-			placeholder: {
-				type: String,
-				default: '请选择项目'
-			},
-			readonly: {
-				type: Boolean,
-				default: false
-			},
-			disabled: {
-				type: Boolean,
-				default: false
-			},
+		readonly: {
+			type: Boolean,
+			default: false
 		},
-		mounted() {
-			// this.loadData();
+		disabled: {
+			type: Boolean,
+			default: false
 		},
-		methods: {
-			init(index,projectId){
-				this.show = true;
-				if (!this.projectId) {
-					this.index = index;
-					this.projectId = projectId;
-					//选项的选择模式,单选或多选,默认为多选
-					this.localMode = 'single'
-					this.loadData();
-				}
-			},
-			onItemClick(item) {
-				if (this.localMode  === 'single') {
-					this.data.forEach(node => {
-						node.checked = node === item;
-					});
-				} else {
-					item.checked = !item.checked;
-				}
-			},
-			selectUsers() {
-				let ids = this.data.filter(item => item.checked).map(item => item.id).join(',');
-				let reportNo = this.data.filter(item => item.checked).map(item => item.reportNo).join(',');
-				this.labels = reportNo;
-				this.$emit('input', ids, reportNo, this.index);
-				this.show = false;
-			},
-			loadData() {
-				reimbursementApprovalService.reportNoList({ status: 5, projectId: this.projectId })
-						.then(data => {
-							this.data = data.records.map(item => ({ ...item, checked: false }));
-							if (this.value) {
-								let keys = this.value.split(',');
-								this.data.forEach(node => {
-									if (keys.includes(node.id)) {
-										node.checked = true;
-									}
-								});
-								this.labels = this.data.filter(node => node.checked).map(node => node.label).join(',');
+	},
+	mounted() {
+		// this.loadData();
+	},
+	methods: {
+		init(index, projectId) {
+			this.show = true;
+			if (!this.projectId) {
+				this.index = index;
+				this.projectId = projectId;
+				//选项的选择模式,单选或多选,默认为多选
+				this.localMode = 'single'
+				this.loadData();
+			}
+		},
+		onItemClick(item) {
+			if (this.localMode === 'single') {
+				this.data.forEach(node => {
+					node.checked = node === item;
+				});
+			} else {
+				item.checked = !item.checked;
+			}
+		},
+		selectUsers() {
+			let ids = this.data.filter(item => item.checked).map(item => item.id).join(',');
+			let reportNo = this.data.filter(item => item.checked).map(item => item.reportNo).join(',');
+			this.labels = reportNo;
+			this.$emit('input', ids, reportNo, this.index);
+			this.show = false;
+		},
+		loadData() {
+			reimbursementApprovalService.reportNoList({ status: 5, projectId: this.projectId })
+				.then(data => {
+					this.data = data.records.map(item => ({ ...item, checked: false }));
+					if (this.value) {
+						let keys = this.value.split(',');
+						this.data.forEach(node => {
+							if (keys.includes(node.id)) {
+								node.checked = true;
 							}
-						})
-						.catch(e => {
-							throw e;
 						});
-			},
-			onClose() {
-				// 在关闭操作中清除已选择标记
-				this.data.forEach(item => {
-					item.checked = false;
+						this.labels = this.data.filter(node => node.checked).map(node => node.label).join(',');
+					}
+				})
+				.catch(e => {
+					throw e;
 				});
-				this.labels = ''; // 清空标签
-				this.show = false;
-			},
-		}
-	};
+		},
+		onClose() {
+			// 在关闭操作中清除已选择标记
+			this.data.forEach(item => {
+				item.checked = false;
+			});
+			this.labels = ''; // 清空标签
+			this.show = false;
+		},
+	}
+};
 </script>

Разлика између датотеке није приказан због своје велике величине
+ 1213 - 1170
pages/cw/reimbursementApproval/info/ReimbursementForm.vue


Разлика између датотеке није приказан због своје велике величине
+ 539 - 584
pages/dailyOfficeWork/holiday/HolidayForm.vue


+ 337 - 331
pages/generateForm/InvoiceModule.vue

@@ -1,35 +1,42 @@
 <template>
     <view>
-        <u--form :model="inputForm" labelWidth="100px" class="u-form" labelPosition="left" :rules="rules" ref="inputForm">
+        <u--form :model="inputForm" labelWidth="100px" class="u-form" labelPosition="left" :rules="rules"
+            ref="inputForm">
             <!-- 动态生成发票基本信息 -->
-            <el-row :gutter="15" v-for="(item, index_experience) in inputForm.workInvoiceProjectRelationList" :key="index_experience">
+            <el-row :gutter="15" v-for="(item, index_experience) in inputForm.workInvoiceProjectRelationList"
+                :key="index_experience">
                 <el-col :span="24">
                     <u-form-item label="">
                         <u-divider :text="'基本信息 ' + (index_experience + 1)"></u-divider>
                     </u-form-item>
                 </el-col>
                 <el-col :span="24">
-                    <u-form-item label="项目名称" :prop="'workInvoiceProjectRelationList[' + index_experience + '].projectName'">
+                    <u-form-item label="项目名称"
+                        :prop="'workInvoiceProjectRelationList[' + index_experience + '].projectName'">
                         <u--input v-model="item.projectName" :disabled="true" clearable></u--input>
                     </u-form-item>
                 </el-col>
                 <el-col :span="24">
-                    <u-form-item label="合同名称" :prop="'workInvoiceProjectRelationList[' + index_experience + '].workContractName'">
+                    <u-form-item label="合同名称"
+                        :prop="'workInvoiceProjectRelationList[' + index_experience + '].workContractName'">
                         <u--input v-model="item.workContractName" :disabled="true" clearable></u--input>
                     </u-form-item>
                 </el-col>
                 <el-col :span="24">
-                    <u-form-item label="项目编号" :prop="'workInvoiceProjectRelationList[' + index_experience + '].projectNum'">
+                    <u-form-item label="项目编号"
+                        :prop="'workInvoiceProjectRelationList[' + index_experience + '].projectNum'">
                         <u--input v-model="item.projectNum" :disabled="true" clearable></u--input>
                     </u-form-item>
                 </el-col>
                 <el-col :span="24">
-                    <u-form-item label="委托方" :prop="'workInvoiceProjectRelationList[' + index_experience + '].clientName'">
+                    <u-form-item label="委托方"
+                        :prop="'workInvoiceProjectRelationList[' + index_experience + '].clientName'">
                         <u--input v-model="item.clientName" :disabled="true" clearable></u--input>
                     </u-form-item>
                 </el-col>
                 <el-col :span="24">
-                    <u-form-item label="报告号" :prop="'workInvoiceProjectRelationList[' + index_experience + '].reportDataNum'">
+                    <u-form-item label="报告号"
+                        :prop="'workInvoiceProjectRelationList[' + index_experience + '].reportDataNum'">
                         <u--input v-model="item.reportDataNum" :disabled="true" clearable></u--input>
                     </u-form-item>
                 </el-col>
@@ -77,25 +84,13 @@
                 <u--input v-model="inputForm.drawerName" :disabled="true" clearable></u--input>
             </u-form-item>
             <u-form-item label="开票时间" prop="invoiceDate" :required="true">
-                <el-date-picker
-                        v-model="inputForm.invoiceDate"
-                        type="date"
-                        placeholder="选择开票时间"
-                        style="width:100%"
-                        size="default"
-                        placement="bottom-start"
-                        clearable>
+                <el-date-picker v-model="inputForm.invoiceDate" type="date" placeholder="选择开票时间" style="width:100%"
+                    size="default" placement="bottom-start" clearable>
                 </el-date-picker>
             </u-form-item>
             <u-form-item label="领票时间" prop="takeDate">
-                <el-date-picker
-                        v-model="inputForm.takeDate"
-                        type="date"
-                        placeholder="选择领票时间"
-                        style="width:100%"
-                        size="default"
-                        placement="bottom-start"
-                        clearable>
+                <el-date-picker v-model="inputForm.takeDate" type="date" placeholder="选择领票时间" style="width:100%"
+                    size="default" placement="bottom-start" clearable>
                 </el-date-picker>
             </u-form-item>
             <u-form-item label="实际开票人" prop="actualDrawerName">
@@ -114,81 +109,88 @@
                 <u--input v-model="inputForm.remarks" :disabled="true" clearable></u--input>
             </u-form-item>
 
-            <el-row  :gutter="15" :key="item.id" v-for="(item,index_experience) in this.inputForm.workAccountList">
+            <el-row :gutter="15" :key="item.id" v-for="(item, index_experience) in this.inputForm.workAccountList">
                 <el-col :span="24">
-                    <u-form-item label="" >
-                        <el-divider content-position="left"> 发票明细详情 {{index_experience + 1}}</el-divider>
+                    <u-form-item label="">
+                        <el-divider content-position="left"> 发票明细详情 {{ index_experience + 1 }}</el-divider>
                     </u-form-item>
                 </el-col>
 
                 <el-col :span="24">
-                    <u-form-item label="发票代码" :prop="'workAccountList[' + index_experience + '].code'"
-                                 :rules="[
-				     ]">
+                    <u-form-item label="发票代码" :prop="'workAccountList[' + index_experience + '].code'" :rules="[
+                    ]">
                         <u--input v-model="item.code" placeholder="请填写发票代码" clearable></u--input>
                     </u-form-item>
                 </el-col>
                 <el-col :span="24">
                     <u-form-item label="发票号" :prop="'workAccountList[' + index_experience + '].number'" :required="true"
-                                 :rules="[
-				     ]">
-                        <u--input v-model="item.number" placeholder="请填写发票号" @blur="validateNumber(index_experience)" clearable></u--input>
+                        :rules="[
+                        ]">
+                        <u--input v-model="item.number" placeholder="请填写发票号" @blur="validateNumber(index_experience)"
+                            clearable></u--input>
                     </u-form-item>
                 </el-col>
                 <el-col :span="24">
-                    <u-form-item label="开票金额(元)" :prop="'workAccountList[' + index_experience + '].account'" :required="true"
-                                 :rules="[
-                    { validator: validateAmount, trigger: 'blur' }
-                 ]">
-                        <u-input v-model="item.account" placeholder="请填写开票金额(元)" clearable @input="handleInput($event, index_experience)" @blur="handleBlur($event, index_experience)"></u-input>
+                    <u-form-item label="开票金额(元)" :prop="'workAccountList[' + index_experience + '].account'"
+                        :required="true" :rules="[
+                            { validator: validateAmount, trigger: 'blur' }
+                        ]">
+                        <u-input v-model="item.account" placeholder="请填写开票金额(元)" clearable
+                            @input="handleInput($event, index_experience)"
+                            @blur="handleBlur($event, index_experience)"></u-input>
                     </u-form-item>
                 </el-col>
                 <el-col :span="24">
-                    <u-form-item label="税率(%)" :prop="'workAccountList[' + index_experience + '].rate'"
-                                 :rules="[
-				     ]">
-                        <u-input v-model="item.rate" placeholder="请填写税率(%)" @input="handleRateInput($event, index_experience)" @blur="checkRate($event, index_experience)" clearable></u-input>
+                    <u-form-item label="税率(%)" :prop="'workAccountList[' + index_experience + '].rate'" :rules="[
+                    ]">
+                        <u-input v-model="item.rate" placeholder="请填写税率(%)"
+                            @input="handleRateInput($event, index_experience)"
+                            @blur="checkRate($event, index_experience)" clearable></u-input>
                     </u-form-item>
                 </el-col>
                 <el-col :span="24">
-                    <u-form-item label="金额" :prop="'workAccountList[' + index_experience + '].amount'"
-                                 :rules="[
-				     ]">
+                    <u-form-item label="金额" :prop="'workAccountList[' + index_experience + '].amount'" :rules="[
+                    ]">
                         <u-input v-model="item.amount" placeholder="请填写金额" readonly clearable></u-input>
                     </u-form-item>
                 </el-col>
                 <el-col :span="24">
-                    <u-form-item label="税额" :prop="'workAccountList[' + index_experience + '].tax'"
-                                 :rules="[
-				     ]">
+                    <u-form-item label="税额" :prop="'workAccountList[' + index_experience + '].tax'" :rules="[
+                    ]">
                         <u-input v-model="item.tax" placeholder="请填写税额" readonly clearable></u-input>
                     </u-form-item>
                 </el-col>
 
                 <el-col :span="24" style="text-align: center">
-                    <u-form-item label="" >
-                        <el-button style="width: 100%" type="danger"  @click="removeRow(index_experience)" plain>删除发票明细 {{index_experience + 1}}</el-button>
+                    <u-form-item label="">
+                        <el-button style="width: 100%" type="danger" @click="removeRow(index_experience)" plain>删除发票明细
+                            {{ index_experience + 1 }}</el-button>
                     </u-form-item>
                 </el-col>
             </el-row>
-            <u-form-item label="" >
+            <u-form-item label="">
                 <el-button style="width: 100%" type="primary" @click="addRow()" plain>新增发票明细</el-button>
             </u-form-item>
 
             <u-form-item label="附件">
-                <el-upload
-                        class="upload-demo"
-                        :action="`http://cpaoa.xgccpm.com/api/public-modules-server/oss/file/webUpload/upload`"
-                        :on-remove="(file, fileList) => handleRemove(file, fileList,'')"
-                        :file-list="inputForm.files"
-                        :on-success="(response, file, fileList) => handleUploadSuccess(response, file, fileList,'')"
-                        :limit="3">
+                <!-- <el-upload class="upload-demo"
+                    :action="`http://cpaoa.xgccpm.com/api/public-modules-server/oss/file/webUpload/upload`"
+                    :on-remove="(file, fileList) => handleRemove(file, fileList, '')" :file-list="inputForm.files"
+                    :on-success="(response, file, fileList) => handleUploadSuccess(response, file, fileList, '')"
+                    :limit="3">
                     <el-button size="small" type="primary" v-if="false">点击上传</el-button>
                     <div slot="tip" class="el-upload__tip">只能上传不超过 3 个文件</div>
                     <template slot="file" slot-scope="{ file }" v-if="shouldShowFile(file)">
                         <span @click="handleFileClick(file)">{{ file.name }}</span>
                     </template>
-                </el-upload>
+</el-upload> -->
+
+                <!-- <UploadComponent
+                    :uploadUrl="`http://127.0.0.1:8000/api/public-modules-server/oss/file/webUpload/upload`"
+                    @onRemove="(file, fileList, fileIndex) => handleRemove(file, fileList, '', '', fileIndex)"
+                    @onSuccess="(file, fileList) => handleUploadSuccess(file, fileList, '', '')"
+                    :fileList="inputForm.files" :limit="3" :isDelete="!testFlag" :isUpload="!testFlag">
+                </UploadComponent> -->
             </u-form-item>
         </u--form>
     </view>
@@ -196,314 +198,318 @@
 
 
 <script>
-    import ccpmService from '@/api/centerservice/ccpm/CcpmService'
-    import OSSService from "@/api/sys/OSSService";
-    import moment from "moment";
+import ccpmService from '@/api/centerservice/ccpm/CcpmService'
+import OSSService from "@/api/sys/OSSService";
+import moment from "moment";
+// import UploadComponent from '@/pages/common/UploadComponent.vue';
 
-    export default {
-        name: 'DynamicForm',
-        props: {
-            invoice: {
-                type: Array,
-                required: true
+export default {
+    name: 'DynamicForm',
+    props: {
+        invoice: {
+            type: Array,
+            required: true
+        },
+    },
+    conponents: {
+        // UploadComponent
+    },
+    data() {
+        return {
+            inputForm: {
+                workInvoiceProjectRelationList: [],
+                workAccountList: [],
+                invoiceTypeName: "",
+                newDrawerName: "",
+                clientName: "",
+                taxpayerIdentificationNo: "",
+                address: "",
+                telephone: "",
+                bank: "",
+                bankNumber: "",
+                chargeType: "",
+                billingContent: "",
+                money: "",
+                content: "",
+                drawerName: "",
+                invoiceDate: "",
+                takeDate: "",
+                actualDrawerName: "",
+                actualDrawerEmailAddress: "",
+                accountCheckingUserName: "",
+                area: "",
+                remarks: "",
+                files: []
+            },
+            rules: {
+                invoiceDate: [
+                    {
+                        required: true,
+                        message: '开票时间不能为空',
+                        trigger: ['blur', 'change']
+                    }
+                ],
+            },
+            isDeletingRow: false,  // 标志变量
+        };
+    },
+    ossService: null,
+    created() {
+        this.ossService = new OSSService();
+    },
+    watch: {
+        invoice: {
+            handler(newVal) {
+                this.assignment();
             },
+            deep: true,
+            immediate: true
+        }
+    },
+    methods: {
+        getInputForm() {
+            return this.inputForm;
+        },
+        assignment() {
+            if (this.isDeletingRow) {
+                return;  // 如果正在删除行,则不执行此方法
+            } else {
+                if (Array.isArray(this.invoice) && this.invoice.length > 0) {
+                    this.inputForm = this.recover(this.inputForm, this.invoice[0])
+                    // this.inputForm = this.invoice[0];
+                    if (this.isEmpty(this.inputForm.invoiceDate)) {
+                        this.inputForm.invoiceDate = moment(new Date()).format('YYYY-MM-DD');
+                    }
+                    if (this.isEmpty(this.inputForm.workAccountList) || this.inputForm.workAccountList.length === 0) {
+                        this.inputForm.workAccountList.push({
+                            code: '',
+                            number: '',
+                            account: this.inputForm.money,
+                            rate: '',
+                            amount: '',
+                            tax: '',
+                            allAmount: ''
+                        })
+                    }
+                }
+            }
+
         },
-        data() {
-            return {
-                inputForm: {
-                    workInvoiceProjectRelationList: [],
-                    workAccountList: [],
-                    invoiceTypeName: "",
-                    newDrawerName: "",
-                    clientName: "",
-                    taxpayerIdentificationNo: "",
-                    address: "",
-                    telephone: "",
-                    bank: "",
-                    bankNumber: "",
-                    chargeType: "",
-                    billingContent: "",
-                    money: "",
-                    content: "",
-                    drawerName: "",
-                    invoiceDate: "",
-                    takeDate: "",
-                    actualDrawerName: "",
-                    actualDrawerEmailAddress: "",
-                    accountCheckingUserName: "",
-                    area: "",
-                    remarks: "",
-                    files: []
-                },
-                rules: {
-                    invoiceDate: [
-                        {
-                            required: true,
-                            message: '开票时间不能为空',
-                            trigger: ['blur', 'change']
-                        }
-                    ],
-                },
-                isDeletingRow: false,  // 标志变量
-            };
+        isEmpty(value) {
+            return value == null || value === undefined || (typeof value === 'string' && value.trim() === '') || (Array.isArray(value) && value.length === 0);
         },
-        ossService: null,
-        created() {
-            this.ossService = new OSSService();
+        isNotEmpty(value) {
+            return !this.isEmpty(value);
         },
-        watch: {
-            invoice: {
-                handler(newVal) {
-                    this.assignment();
-                },
-                deep: true,
-                immediate: true
+        shouldShowFile(file) {
+            if (this.inputForm.files && this.inputForm.files.length > 0) {
+                return this.inputForm.files.indexOf(file) !== -1;
             }
+            return false;
         },
-        methods: {
-            getInputForm() {
-                return this.inputForm;
-            },
-            assignment() {
-                if (this.isDeletingRow) {
-                    return;  // 如果正在删除行,则不执行此方法
-                } else {
-                    if (Array.isArray(this.invoice) && this.invoice.length > 0) {
-                        this.inputForm = this.recover(this.inputForm, this.invoice[0])
-                        // this.inputForm = this.invoice[0];
-                        if (this.isEmpty(this.inputForm.invoiceDate)) {
-                            this.inputForm.invoiceDate = moment(new Date()).format('YYYY-MM-DD');
-                        }
-                        if(this.isEmpty(this.inputForm.workAccountList) || this.inputForm.workAccountList.length === 0 ) {
-                            this.inputForm.workAccountList.push({
-                                code: '',
-                                number: '',
-                                account: this.inputForm.money,
-                                rate: '',
-                                amount: '',
-                                tax: '',
-                                allAmount: ''
-                            })
-                        }
-                    }
-                }
+        async handleFileClick(file) {
+            await this.ossService.getTemporaryUrl(file.url).then((data) => {
+                file.lsUrl = data;
+            });
+            if (this.isImage(file.name)) {
+                this.handleImageClick(file.lsUrl);
+            } else {
+                window.location.href = file.lsUrl;
+            }
+        },
+        isImage(fileName) {
+            const ext = fileName.toLowerCase().split('.').pop();
+            return ['jpg', 'jpeg', 'png', 'gif', 'bmp'].includes(ext);
+        },
+        handleImageClick(url) {
+            uni.previewImage({
+                current: url,
+                urls: [url],
+            });
+        },
+        handleDelete(file) {
+            const index = this.inputForm.files.indexOf(file);
+            if (index !== -1) {
+                this.inputForm.files.splice(index, 1);
+            }
+        },
+        async validateNumber(index) {
+            const numberString = this.inputForm.workAccountList[index].number.trim(); // 去除空格
+            let errorDetected = false; // 布尔变量用于检测是否有错误发生
 
-            },
-            isEmpty(value) {
-                return value == null || value === undefined || (typeof value === 'string' && value.trim() === '') || (Array.isArray(value) && value.length === 0);
-            },
-            isNotEmpty(value) {
-                return !this.isEmpty(value);
-            },
-            shouldShowFile(file) {
-                if (this.inputForm.files && this.inputForm.files.length > 0) {
-                    return this.inputForm.files.indexOf(file) !== -1;
-                }
-                return false;
-            },
-            async handleFileClick(file) {
-                await this.ossService.getTemporaryUrl(file.url).then((data) => {
-                    file.lsUrl = data;
-                });
-                if (this.isImage(file.name)) {
-                    this.handleImageClick(file.lsUrl);
-                } else {
-                    window.location.href = file.lsUrl;
+            if (this.isNotEmpty(numberString)) {
+                if (!/^\d+$/.test(numberString)) { // 使用正则表达式检查是否只包含数字字符
+                    uni.showToast({
+                        title: '发票号只能输入整数',
+                        icon: 'none',
+                        duration: 2000
+                    });
+                    errorDetected = true; // 如果有错误,设置为 true
                 }
-            },
-            isImage(fileName) {
-                const ext = fileName.toLowerCase().split('.').pop();
-                return ['jpg', 'jpeg', 'png', 'gif', 'bmp'].includes(ext);
-            },
-            handleImageClick(url) {
-                uni.previewImage({
-                    current: url,
-                    urls: [url],
-                });
-            },
-            handleDelete(file) {
-                const index = this.inputForm.files.indexOf(file);
-                if (index !== -1) {
-                    this.inputForm.files.splice(index, 1);
+
+                // 验证发票号是否大于 8 位
+                if (parseInt(numberString) > 99999999) {
+                    uni.showToast({
+                        title: '“发票号” 不可以大于 8 位,请重新输入',
+                        icon: 'none',
+                        duration: 2000
+                    });
+                    errorDetected = true; // 如果有错误,设置为 true
                 }
-            },
-            async validateNumber(index) {
-                const numberString = this.inputForm.workAccountList[index].number.trim(); // 去除空格
-                let errorDetected = false; // 布尔变量用于检测是否有错误发生
 
-                if (this.isNotEmpty(numberString)) {
-                    if (!/^\d+$/.test(numberString)) { // 使用正则表达式检查是否只包含数字字符
+                // 验证是否重复
+                for (let i = 0; i < this.inputForm.workAccountList.length; i++) {
+                    if (index !== i && numberString === this.inputForm.workAccountList[i].number) {
                         uni.showToast({
-                            title: '发票号只能输入整数',
+                            title: '“发票号” 已存在,请重新输入',
                             icon: 'none',
                             duration: 2000
                         });
                         errorDetected = true; // 如果有错误,设置为 true
+                        break; // 找到重复即可跳出循环
                     }
+                }
 
-                    // 验证发票号是否大于 8 位
-                    if (parseInt(numberString) > 99999999) {
+                // 查询是否已存在
+                await ccpmService.queryByNumber(numberString).then((data) => {
+                    if (data === true) {
                         uni.showToast({
-                            title: '“发票号” 不可以大于 8 位,请重新输入',
+                            title: '“发票号” 已存在,请重新输入',
                             icon: 'none',
                             duration: 2000
                         });
                         errorDetected = true; // 如果有错误,设置为 true
                     }
+                });
 
-                    // 验证是否重复
-                    for (let i = 0; i < this.inputForm.workAccountList.length; i++) {
-                        if (index !== i && numberString === this.inputForm.workAccountList[i].number) {
-                            uni.showToast({
-                                title: '“发票号” 已存在,请重新输入',
-                                icon: 'none',
-                                duration: 2000
-                            });
-                            errorDetected = true; // 如果有错误,设置为 true
-                            break; // 找到重复即可跳出循环
-                        }
-                    }
-
-                    // 查询是否已存在
-                    await ccpmService.queryByNumber(numberString).then((data) => {
-                        if (data === true) {
-                            uni.showToast({
-                                title: '“发票号” 已存在,请重新输入',
-                                icon: 'none',
-                                duration: 2000
-                            });
-                            errorDetected = true; // 如果有错误,设置为 true
-                        }
-                    });
-
-                    // 只在发生错误时清空输入字段
-                    if (errorDetected) {
-                        this.$set(this.inputForm.workAccountList[index], 'number', '');
-                    }
+                // 只在发生错误时清空输入字段
+                if (errorDetected) {
+                    this.$set(this.inputForm.workAccountList[index], 'number', '');
                 }
+            }
 
-            },
-            // 根据开票金额和税率计算出金额: 开票金额-税率*开票金额
-            getAmount(rate,rowIndex) {
-                let amount = this.inputForm.workAccountList[rowIndex].amount
-                let account = this.inputForm.workAccountList[rowIndex].account
-                if (!this.isEmpty(account) && !this.isEmpty(rate)) {
-                    amount = parseFloat((parseFloat(account) - parseFloat((parseFloat(account) * parseFloat((parseFloat(rate) / 100).toFixed(4))).toFixed(4))).toFixed(2))
-                } else {
-                    amount = ''
-                }
-                this.$set(this.inputForm.workAccountList[rowIndex], 'amount', amount)
-            },
-            // 根据开票金额和税率计算出税额: 税率*开票金额
-            getTax(rate,rowIndex) {
-                let tax = this.inputForm.workAccountList[rowIndex].tax
-                let account = this.inputForm.workAccountList[rowIndex].account
-                if (!this.isEmpty(account) && !this.isEmpty(rate)) {
-                    tax = parseFloat((parseFloat(account) * parseFloat((parseFloat(rate) / 100).toFixed(4))).toFixed(2))
-                } else {
-                    tax = ''
-                }
-                this.$set(this.inputForm.workAccountList[rowIndex], 'tax', tax)
-            },
-            validateAmount(rule, value, callback) {
-                const reg = /^\d+(\.\d{1,2})?$/;
-                if (!value) {
-                    callback(new Error('请输入开票金额'));
-                } else if (!reg.test(value)) {
-                    callback(new Error('请输入有效的开票金额,最多两位小数'));
-                } else {
-                    callback();
-                }
-            },
-            handleInput(event, index) {
-                const value = event;
-                const reg = /^\d*\.?\d{0,2}$/;
-                if (!reg.test(value)) {
-                    // 保留符合规则的部分
-                    const validValue = value.slice(0, -1);
+        },
+        // 根据开票金额和税率计算出金额: 开票金额-税率*开票金额
+        getAmount(rate, rowIndex) {
+            let amount = this.inputForm.workAccountList[rowIndex].amount
+            let account = this.inputForm.workAccountList[rowIndex].account
+            if (!this.isEmpty(account) && !this.isEmpty(rate)) {
+                amount = parseFloat((parseFloat(account) - parseFloat((parseFloat(account) * parseFloat((parseFloat(rate) / 100).toFixed(4))).toFixed(4))).toFixed(2))
+            } else {
+                amount = ''
+            }
+            this.$set(this.inputForm.workAccountList[rowIndex], 'amount', amount)
+        },
+        // 根据开票金额和税率计算出税额: 税率*开票金额
+        getTax(rate, rowIndex) {
+            let tax = this.inputForm.workAccountList[rowIndex].tax
+            let account = this.inputForm.workAccountList[rowIndex].account
+            if (!this.isEmpty(account) && !this.isEmpty(rate)) {
+                tax = parseFloat((parseFloat(account) * parseFloat((parseFloat(rate) / 100).toFixed(4))).toFixed(2))
+            } else {
+                tax = ''
+            }
+            this.$set(this.inputForm.workAccountList[rowIndex], 'tax', tax)
+        },
+        validateAmount(rule, value, callback) {
+            const reg = /^\d+(\.\d{1,2})?$/;
+            if (!value) {
+                callback(new Error('请输入开票金额'));
+            } else if (!reg.test(value)) {
+                callback(new Error('请输入有效的开票金额,最多两位小数'));
+            } else {
+                callback();
+            }
+        },
+        handleInput(event, index) {
+            const value = event;
+            const reg = /^\d*\.?\d{0,2}$/;
+            if (!reg.test(value)) {
+                // 保留符合规则的部分
+                const validValue = value.slice(0, -1);
 
-                    this.$nextTick(() => {
-                        this.$set(this.inputForm.workAccountList[index], 'account', validValue);
-                    });
-                } else {
-                    this.$set(this.inputForm.workAccountList[index], 'account', value);
-                }
-            },
-            handleBlur(event, index) {
-                const value = event;
-                if (!value || isNaN(value)) {
-                    this.$set(this.inputForm.workAccountList[index], 'account', '');
-                } else {
-                    const formattedValue = parseFloat(value).toFixed(2);
+                this.$nextTick(() => {
+                    this.$set(this.inputForm.workAccountList[index], 'account', validValue);
+                });
+            } else {
+                this.$set(this.inputForm.workAccountList[index], 'account', value);
+            }
+        },
+        handleBlur(event, index) {
+            const value = event;
+            if (!value || isNaN(value)) {
+                this.$set(this.inputForm.workAccountList[index], 'account', '');
+            } else {
+                const formattedValue = parseFloat(value).toFixed(2);
 
-                    this.$nextTick(() => {
-                        this.$set(this.inputForm.workAccountList[index], 'account', formattedValue);
-                    });
-                }
-            },
-            handleRateInput(event, rowIndex) {
-                const value = event;
-                const reg = /^\d*\.?\d{0,2}$/;
-                if (!reg.test(value)) {
-                    // 保留符合规则的部分
-                    const validValue = value.slice(0, -1);
+                this.$nextTick(() => {
+                    this.$set(this.inputForm.workAccountList[index], 'account', formattedValue);
+                });
+            }
+        },
+        handleRateInput(event, rowIndex) {
+            const value = event;
+            const reg = /^\d*\.?\d{0,2}$/;
+            if (!reg.test(value)) {
+                // 保留符合规则的部分
+                const validValue = value.slice(0, -1);
 
-                    this.$nextTick(() => {
-                        this.$set(this.inputForm.workAccountList[rowIndex], 'rate', validValue);
-                    });
-                } else {
-                    this.$set(this.inputForm.workAccountList[rowIndex], 'rate', value);
-                }
-            },
-            checkRate(event, rowIndex) {
-                const value = event;
-                if (!value || isNaN(value)) {
-                    this.$set(this.inputForm.workAccountList[rowIndex], 'rate', '');
-                } else {
-                    let validValue = parseFloat(value);
-                    if (!this.isEmpty(value)) {
-                        if (validValue < 1 || validValue > 100) {
-                            uni.showToast({
-                                title: '“税率” 请填写 1 到 100 之间的数字,请重新输入',
-                                icon: 'none',
-                                duration: 2000
-                            });
-                            validValue = '';
-                        } else {
-                            validValue = validValue.toFixed(2);
-                        }
+                this.$nextTick(() => {
+                    this.$set(this.inputForm.workAccountList[rowIndex], 'rate', validValue);
+                });
+            } else {
+                this.$set(this.inputForm.workAccountList[rowIndex], 'rate', value);
+            }
+        },
+        checkRate(event, rowIndex) {
+            const value = event;
+            if (!value || isNaN(value)) {
+                this.$set(this.inputForm.workAccountList[rowIndex], 'rate', '');
+            } else {
+                let validValue = parseFloat(value);
+                if (!this.isEmpty(value)) {
+                    if (validValue < 1 || validValue > 100) {
+                        uni.showToast({
+                            title: '“税率” 请填写 1 到 100 之间的数字,请重新输入',
+                            icon: 'none',
+                            duration: 2000
+                        });
+                        validValue = '';
+                    } else {
+                        validValue = validValue.toFixed(2);
                     }
-
-                    this.$nextTick(() => {
-                        this.$set(this.inputForm.workAccountList[rowIndex], 'rate', validValue);
-                    });
-                    this.getAmount(validValue, rowIndex);
-                    this.getTax(validValue, rowIndex);
                 }
 
-            },
-            addRow() {
-                this.inputForm.workAccountList.push({
-                    code: '',
-                    number: '',
-                    account: '',
-                    rate: '',
-                    amount: '',
-                    tax: '',
-                    allAmount: ''
-                })
-            },
-            removeRow(index) {
-                this.isDeletingRow = true;  // 设置标志变量
-                this.inputForm.workAccountList.splice(index, 1);
-            },
-        }
-    };
+                this.$nextTick(() => {
+                    this.$set(this.inputForm.workAccountList[rowIndex], 'rate', validValue);
+                });
+                this.getAmount(validValue, rowIndex);
+                this.getTax(validValue, rowIndex);
+            }
+
+        },
+        addRow() {
+            this.inputForm.workAccountList.push({
+                code: '',
+                number: '',
+                account: '',
+                rate: '',
+                amount: '',
+                tax: '',
+                allAmount: ''
+            })
+        },
+        removeRow(index) {
+            this.isDeletingRow = true;  // 设置标志变量
+            this.inputForm.workAccountList.splice(index, 1);
+        },
+    }
+};
 </script>
 
 
 <style scoped>
-    .upload-demo {
-        margin-top: 10px;
-    }
+.upload-demo {
+    margin-top: 10px;
+}
 </style>

Разлика између датотеке није приказан због своје велике величине
+ 682 - 673
pages/materialManagement/collect/CollectForm.vue


Разлика између датотеке није приказан због своје велике величине
+ 810 - 798
pages/materialManagement/purchase/PurchaseForm.vue


Разлика између датотеке није приказан због своје велике величине
+ 649 - 630
pages/workbench/task/TaskForm.vue


+ 4 - 0
uni_modules/mumu-h5office/changelog.md

@@ -0,0 +1,4 @@
+## 1.0.1(2023-02-17)
+文档更新
+## 1.0.0(2023-02-15)
+版本上架

+ 142 - 0
uni_modules/mumu-h5office/components/mumu-h5office/app-get-file.vue

@@ -0,0 +1,142 @@
+<template>
+	<view class="appGetFile" id="appGetFile"></view>
+</template>
+
+<script>
+import { BASE_URL } from './config.js'
+import { aesEncrypt } from './tool.js'
+
+var wv //计划创建的webview
+let inter
+export default {
+	props: {
+		appid: {
+			type: String
+		},
+		currentWebview: {
+			type: Object
+		},
+		btnStyle: {
+			type: String,
+			default: 'font-size: 14px;background-color: aqua;'
+		},
+		content: {
+			type: String,
+			default: '<button>按钮</button>'
+		},
+		isShow: {
+			type: Boolean,
+			default: true
+		}
+	},
+	watch: {
+		appid: {
+			handler() {
+				this.appidKey = aesEncrypt(this.appid)
+			},
+			immediate: true
+		},
+		isShow: {
+			handler(newVal) {
+				wv.setVisible(newVal)
+			}
+		}
+	},
+	mounted() {
+		// #ifdef APP-PLUS
+		const params = {
+			btnStyle: this.btnStyle,
+			content: this.content
+		}
+
+		this.$nextTick(() => {
+			wv = plus.webview.create('', 'custom-webview', {
+				cachemode: 'noCache'
+			})
+			plus.globalEvent.addEventListener('plusMessage', e => {
+				if (wv && wv['__uuid__'] === e.originId) {
+					this.childEvent(e.data.args.data.arg)
+				}
+			})
+
+			wv.loadURL(this.BASE_URL + this.API + '?params=' + encodeURIComponent(JSON.stringify(params)))
+			this.currentWebview.append(wv)
+			this.setPosition()
+
+			setTimeout(() => {
+				wv.evalJS("getAppidKey({type:'event',appidKey:'" + this.appidKey + "'})")
+			}, 500)
+		})
+		// #endif
+	},
+
+	data() {
+		return {
+			BASE_URL: BASE_URL,
+			API: '/appWebViewUploadFile',
+			appidKey: '',
+			top: 100
+		}
+	},
+	methods: {
+		// appGetFileClick() {
+		//   wv.evalJS("onGetFile({type:'event',appidKey:'" + this.appidKey + "'})")
+		// },
+
+		childEvent(data) {
+			if (data.type && data.type === 'webOffice') {
+				switch (data.event) {
+					case 'error':
+						//this.showErrorMsg(data.msg)
+						this.$emit('error', data.msg)
+						break
+					case 'success':
+						this.$emit('success', data.msg)
+						break
+					case 'readFile':
+						this.$emit('readFile', data.msg)
+						break
+					case 'complete':
+						this.$emit('complete', data.msg)
+						break
+				}
+			}
+		},
+		showErrorMsg(msg) {
+			uni.showModal({
+				title: '系统异常!',
+				content: msg,
+				showCancel: false,
+				success: () => {
+					//this.closePre()
+				}
+			})
+		},
+		setPosition() {
+			inter && clearInterval(inter)
+			const h = uni.getSystemInfoSync().screenHeight - uni.getSystemInfoSync().windowHeight
+			inter = setInterval(() => {
+				const query = uni.createSelectorQuery()
+				query.select('#appGetFile').boundingClientRect()
+				query.selectViewport().scrollOffset()
+				query.exec(res => {
+					const dome = res[0]
+					wv.setStyle({
+						top: h + dome.top,
+						left: dome.left,
+						height: dome.height,
+						width: dome.width
+					})
+				})
+			}, 15)
+		}
+	}
+}
+</script>
+
+<style>
+.appGetFile {
+	width: 200px;
+	height: 80px;
+}
+</style>

+ 19 - 0
uni_modules/mumu-h5office/components/mumu-h5office/config.js

@@ -0,0 +1,19 @@
+/* 支持的文件 */
+export const FILE_TYPES = ['csv', 'djvu', 'doc', 'docm', 'docx', 'docxf', 'dot', 'dotm', 'dotx', 'epub', 'fb2', 'fodp',
+	'fods', 'fodt', 'htm', 'html', 'mht', 'odp', 'ods', 'odt', 'oform', 'otp', 'ots', 'ott', 'oxps', 'pdf', 'pot',
+	'potm', 'potx', 'pps', 'ppsm', 'ppsx', 'ppt', 'pptm', 'pptx', 'rtf', 'txt', 'xls', 'xlsb', 'xlsm', 'xlsx',
+	'xlt',
+	'xltm', 'xltx', 'xml', 'xps'
+]
+
+/*  */
+export const BASE_URL = 'https://tool.h5office.cn/index.php'
+
+export const AES_KEY = "68d7eec0ba694a68"
+
+export const PUBLIC_KEY = `-----BEGIN PUBLIC KEY-----
+MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCmkANmC849IOntYQQdSgLvMMGm
+8V/u838ATHaoZwvweoYyd+/7Wx+bx5bdktJb46YbqS1vz3VRdXsyJIWhpNcmtKhY
+inwcl83aLtzJeKsznppqMyAIseaKIeAm6tT8uttNkr2zOymL/PbMpByTQeEFlyy1
+poLBwrol0F4USc+owwIDAQAB
+-----END PUBLIC KEY-----`

+ 753 - 0
uni_modules/mumu-h5office/components/mumu-h5office/index.umd.js

@@ -0,0 +1,753 @@
+(function (global, factory) {
+	typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
+	typeof define === 'function' && define.amd ? define(factory) :
+	(global.browserMD5File = factory());
+}(this, (function () { 'use strict';
+
+	function createCommonjsModule(fn, module) {
+		return module = { exports: {} }, fn(module, module.exports), module.exports;
+	}
+
+	var sparkMd5 = createCommonjsModule(function (module, exports) {
+	(function (factory) {
+	    {
+	        // Node/CommonJS
+	        module.exports = factory();
+	    }
+	}(function (undefined) {
+
+	    /*
+	     * Fastest md5 implementation around (JKM md5).
+	     * Credits: Joseph Myers
+	     *
+	     * @see http://www.myersdaily.org/joseph/javascript/md5-text.html
+	     * @see http://jsperf.com/md5-shootout/7
+	     */
+
+	    /* this function is much faster,
+	      so if possible we use it. Some IEs
+	      are the only ones I know of that
+	      need the idiotic second function,
+	      generated by an if clause.  */
+	    var add32 = function (a, b) {
+	        return (a + b) & 0xFFFFFFFF;
+	    },
+	        hex_chr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
+
+
+	    function cmn(q, a, b, x, s, t) {
+	        a = add32(add32(a, q), add32(x, t));
+	        return add32((a << s) | (a >>> (32 - s)), b);
+	    }
+
+	    function ff(a, b, c, d, x, s, t) {
+	        return cmn((b & c) | ((~b) & d), a, b, x, s, t);
+	    }
+
+	    function gg(a, b, c, d, x, s, t) {
+	        return cmn((b & d) | (c & (~d)), a, b, x, s, t);
+	    }
+
+	    function hh(a, b, c, d, x, s, t) {
+	        return cmn(b ^ c ^ d, a, b, x, s, t);
+	    }
+
+	    function ii(a, b, c, d, x, s, t) {
+	        return cmn(c ^ (b | (~d)), a, b, x, s, t);
+	    }
+
+	    function md5cycle(x, k) {
+	        var a = x[0],
+	            b = x[1],
+	            c = x[2],
+	            d = x[3];
+
+	        a = ff(a, b, c, d, k[0], 7, -680876936);
+	        d = ff(d, a, b, c, k[1], 12, -389564586);
+	        c = ff(c, d, a, b, k[2], 17, 606105819);
+	        b = ff(b, c, d, a, k[3], 22, -1044525330);
+	        a = ff(a, b, c, d, k[4], 7, -176418897);
+	        d = ff(d, a, b, c, k[5], 12, 1200080426);
+	        c = ff(c, d, a, b, k[6], 17, -1473231341);
+	        b = ff(b, c, d, a, k[7], 22, -45705983);
+	        a = ff(a, b, c, d, k[8], 7, 1770035416);
+	        d = ff(d, a, b, c, k[9], 12, -1958414417);
+	        c = ff(c, d, a, b, k[10], 17, -42063);
+	        b = ff(b, c, d, a, k[11], 22, -1990404162);
+	        a = ff(a, b, c, d, k[12], 7, 1804603682);
+	        d = ff(d, a, b, c, k[13], 12, -40341101);
+	        c = ff(c, d, a, b, k[14], 17, -1502002290);
+	        b = ff(b, c, d, a, k[15], 22, 1236535329);
+
+	        a = gg(a, b, c, d, k[1], 5, -165796510);
+	        d = gg(d, a, b, c, k[6], 9, -1069501632);
+	        c = gg(c, d, a, b, k[11], 14, 643717713);
+	        b = gg(b, c, d, a, k[0], 20, -373897302);
+	        a = gg(a, b, c, d, k[5], 5, -701558691);
+	        d = gg(d, a, b, c, k[10], 9, 38016083);
+	        c = gg(c, d, a, b, k[15], 14, -660478335);
+	        b = gg(b, c, d, a, k[4], 20, -405537848);
+	        a = gg(a, b, c, d, k[9], 5, 568446438);
+	        d = gg(d, a, b, c, k[14], 9, -1019803690);
+	        c = gg(c, d, a, b, k[3], 14, -187363961);
+	        b = gg(b, c, d, a, k[8], 20, 1163531501);
+	        a = gg(a, b, c, d, k[13], 5, -1444681467);
+	        d = gg(d, a, b, c, k[2], 9, -51403784);
+	        c = gg(c, d, a, b, k[7], 14, 1735328473);
+	        b = gg(b, c, d, a, k[12], 20, -1926607734);
+
+	        a = hh(a, b, c, d, k[5], 4, -378558);
+	        d = hh(d, a, b, c, k[8], 11, -2022574463);
+	        c = hh(c, d, a, b, k[11], 16, 1839030562);
+	        b = hh(b, c, d, a, k[14], 23, -35309556);
+	        a = hh(a, b, c, d, k[1], 4, -1530992060);
+	        d = hh(d, a, b, c, k[4], 11, 1272893353);
+	        c = hh(c, d, a, b, k[7], 16, -155497632);
+	        b = hh(b, c, d, a, k[10], 23, -1094730640);
+	        a = hh(a, b, c, d, k[13], 4, 681279174);
+	        d = hh(d, a, b, c, k[0], 11, -358537222);
+	        c = hh(c, d, a, b, k[3], 16, -722521979);
+	        b = hh(b, c, d, a, k[6], 23, 76029189);
+	        a = hh(a, b, c, d, k[9], 4, -640364487);
+	        d = hh(d, a, b, c, k[12], 11, -421815835);
+	        c = hh(c, d, a, b, k[15], 16, 530742520);
+	        b = hh(b, c, d, a, k[2], 23, -995338651);
+
+	        a = ii(a, b, c, d, k[0], 6, -198630844);
+	        d = ii(d, a, b, c, k[7], 10, 1126891415);
+	        c = ii(c, d, a, b, k[14], 15, -1416354905);
+	        b = ii(b, c, d, a, k[5], 21, -57434055);
+	        a = ii(a, b, c, d, k[12], 6, 1700485571);
+	        d = ii(d, a, b, c, k[3], 10, -1894986606);
+	        c = ii(c, d, a, b, k[10], 15, -1051523);
+	        b = ii(b, c, d, a, k[1], 21, -2054922799);
+	        a = ii(a, b, c, d, k[8], 6, 1873313359);
+	        d = ii(d, a, b, c, k[15], 10, -30611744);
+	        c = ii(c, d, a, b, k[6], 15, -1560198380);
+	        b = ii(b, c, d, a, k[13], 21, 1309151649);
+	        a = ii(a, b, c, d, k[4], 6, -145523070);
+	        d = ii(d, a, b, c, k[11], 10, -1120210379);
+	        c = ii(c, d, a, b, k[2], 15, 718787259);
+	        b = ii(b, c, d, a, k[9], 21, -343485551);
+
+	        x[0] = add32(a, x[0]);
+	        x[1] = add32(b, x[1]);
+	        x[2] = add32(c, x[2]);
+	        x[3] = add32(d, x[3]);
+	    }
+
+	    function md5blk(s) {
+	        var md5blks = [],
+	            i; /* Andy King said do it this way. */
+
+	        for (i = 0; i < 64; i += 4) {
+	            md5blks[i >> 2] = s.charCodeAt(i) + (s.charCodeAt(i + 1) << 8) + (s.charCodeAt(i + 2) << 16) + (s.charCodeAt(i + 3) << 24);
+	        }
+	        return md5blks;
+	    }
+
+	    function md5blk_array(a) {
+	        var md5blks = [],
+	            i; /* Andy King said do it this way. */
+
+	        for (i = 0; i < 64; i += 4) {
+	            md5blks[i >> 2] = a[i] + (a[i + 1] << 8) + (a[i + 2] << 16) + (a[i + 3] << 24);
+	        }
+	        return md5blks;
+	    }
+
+	    function md51(s) {
+	        var n = s.length,
+	            state = [1732584193, -271733879, -1732584194, 271733878],
+	            i,
+	            length,
+	            tail,
+	            tmp,
+	            lo,
+	            hi;
+
+	        for (i = 64; i <= n; i += 64) {
+	            md5cycle(state, md5blk(s.substring(i - 64, i)));
+	        }
+	        s = s.substring(i - 64);
+	        length = s.length;
+	        tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+	        for (i = 0; i < length; i += 1) {
+	            tail[i >> 2] |= s.charCodeAt(i) << ((i % 4) << 3);
+	        }
+	        tail[i >> 2] |= 0x80 << ((i % 4) << 3);
+	        if (i > 55) {
+	            md5cycle(state, tail);
+	            for (i = 0; i < 16; i += 1) {
+	                tail[i] = 0;
+	            }
+	        }
+
+	        // Beware that the final length might not fit in 32 bits so we take care of that
+	        tmp = n * 8;
+	        tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);
+	        lo = parseInt(tmp[2], 16);
+	        hi = parseInt(tmp[1], 16) || 0;
+
+	        tail[14] = lo;
+	        tail[15] = hi;
+
+	        md5cycle(state, tail);
+	        return state;
+	    }
+
+	    function md51_array(a) {
+	        var n = a.length,
+	            state = [1732584193, -271733879, -1732584194, 271733878],
+	            i,
+	            length,
+	            tail,
+	            tmp,
+	            lo,
+	            hi;
+
+	        for (i = 64; i <= n; i += 64) {
+	            md5cycle(state, md5blk_array(a.subarray(i - 64, i)));
+	        }
+
+	        // Not sure if it is a bug, however IE10 will always produce a sub array of length 1
+	        // containing the last element of the parent array if the sub array specified starts
+	        // beyond the length of the parent array - weird.
+	        // https://connect.microsoft.com/IE/feedback/details/771452/typed-array-subarray-issue
+	        a = (i - 64) < n ? a.subarray(i - 64) : new Uint8Array(0);
+
+	        length = a.length;
+	        tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+	        for (i = 0; i < length; i += 1) {
+	            tail[i >> 2] |= a[i] << ((i % 4) << 3);
+	        }
+
+	        tail[i >> 2] |= 0x80 << ((i % 4) << 3);
+	        if (i > 55) {
+	            md5cycle(state, tail);
+	            for (i = 0; i < 16; i += 1) {
+	                tail[i] = 0;
+	            }
+	        }
+
+	        // Beware that the final length might not fit in 32 bits so we take care of that
+	        tmp = n * 8;
+	        tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);
+	        lo = parseInt(tmp[2], 16);
+	        hi = parseInt(tmp[1], 16) || 0;
+
+	        tail[14] = lo;
+	        tail[15] = hi;
+
+	        md5cycle(state, tail);
+
+	        return state;
+	    }
+
+	    function rhex(n) {
+	        var s = '',
+	            j;
+	        for (j = 0; j < 4; j += 1) {
+	            s += hex_chr[(n >> (j * 8 + 4)) & 0x0F] + hex_chr[(n >> (j * 8)) & 0x0F];
+	        }
+	        return s;
+	    }
+
+	    function hex(x) {
+	        var i;
+	        for (i = 0; i < x.length; i += 1) {
+	            x[i] = rhex(x[i]);
+	        }
+	        return x.join('');
+	    }
+
+	    // In some cases the fast add32 function cannot be used..
+	    if (hex(md51('hello')) !== '5d41402abc4b2a76b9719d911017c592') {
+	        add32 = function (x, y) {
+	            var lsw = (x & 0xFFFF) + (y & 0xFFFF),
+	                msw = (x >> 16) + (y >> 16) + (lsw >> 16);
+	            return (msw << 16) | (lsw & 0xFFFF);
+	        };
+	    }
+
+	    // ---------------------------------------------------
+
+	    /**
+	     * ArrayBuffer slice polyfill.
+	     *
+	     * @see https://github.com/ttaubert/node-arraybuffer-slice
+	     */
+
+	    if (typeof ArrayBuffer !== 'undefined' && !ArrayBuffer.prototype.slice) {
+	        (function () {
+	            function clamp(val, length) {
+	                val = (val | 0) || 0;
+
+	                if (val < 0) {
+	                    return Math.max(val + length, 0);
+	                }
+
+	                return Math.min(val, length);
+	            }
+
+	            ArrayBuffer.prototype.slice = function (from, to) {
+	                var length = this.byteLength,
+	                    begin = clamp(from, length),
+	                    end = length,
+	                    num,
+	                    target,
+	                    targetArray,
+	                    sourceArray;
+
+	                if (to !== undefined) {
+	                    end = clamp(to, length);
+	                }
+
+	                if (begin > end) {
+	                    return new ArrayBuffer(0);
+	                }
+
+	                num = end - begin;
+	                target = new ArrayBuffer(num);
+	                targetArray = new Uint8Array(target);
+
+	                sourceArray = new Uint8Array(this, begin, num);
+	                targetArray.set(sourceArray);
+
+	                return target;
+	            };
+	        })();
+	    }
+
+	    // ---------------------------------------------------
+
+	    /**
+	     * Helpers.
+	     */
+
+	    function toUtf8(str) {
+	        if (/[\u0080-\uFFFF]/.test(str)) {
+	            str = unescape(encodeURIComponent(str));
+	        }
+
+	        return str;
+	    }
+
+	    function utf8Str2ArrayBuffer(str, returnUInt8Array) {
+	        var length = str.length,
+	           buff = new ArrayBuffer(length),
+	           arr = new Uint8Array(buff),
+	           i;
+
+	        for (i = 0; i < length; i += 1) {
+	            arr[i] = str.charCodeAt(i);
+	        }
+
+	        return returnUInt8Array ? arr : buff;
+	    }
+
+	    function arrayBuffer2Utf8Str(buff) {
+	        return String.fromCharCode.apply(null, new Uint8Array(buff));
+	    }
+
+	    function concatenateArrayBuffers(first, second, returnUInt8Array) {
+	        var result = new Uint8Array(first.byteLength + second.byteLength);
+
+	        result.set(new Uint8Array(first));
+	        result.set(new Uint8Array(second), first.byteLength);
+
+	        return returnUInt8Array ? result : result.buffer;
+	    }
+
+	    function hexToBinaryString(hex) {
+	        var bytes = [],
+	            length = hex.length,
+	            x;
+
+	        for (x = 0; x < length - 1; x += 2) {
+	            bytes.push(parseInt(hex.substr(x, 2), 16));
+	        }
+
+	        return String.fromCharCode.apply(String, bytes);
+	    }
+
+	    // ---------------------------------------------------
+
+	    /**
+	     * SparkMD5 OOP implementation.
+	     *
+	     * Use this class to perform an incremental md5, otherwise use the
+	     * static methods instead.
+	     */
+
+	    function SparkMD5() {
+	        // call reset to init the instance
+	        this.reset();
+	    }
+
+	    /**
+	     * Appends a string.
+	     * A conversion will be applied if an utf8 string is detected.
+	     *
+	     * @param {String} str The string to be appended
+	     *
+	     * @return {SparkMD5} The instance itself
+	     */
+	    SparkMD5.prototype.append = function (str) {
+	        // Converts the string to utf8 bytes if necessary
+	        // Then append as binary
+	        this.appendBinary(toUtf8(str));
+
+	        return this;
+	    };
+
+	    /**
+	     * Appends a binary string.
+	     *
+	     * @param {String} contents The binary string to be appended
+	     *
+	     * @return {SparkMD5} The instance itself
+	     */
+	    SparkMD5.prototype.appendBinary = function (contents) {
+	        this._buff += contents;
+	        this._length += contents.length;
+
+	        var length = this._buff.length,
+	            i;
+
+	        for (i = 64; i <= length; i += 64) {
+	            md5cycle(this._hash, md5blk(this._buff.substring(i - 64, i)));
+	        }
+
+	        this._buff = this._buff.substring(i - 64);
+
+	        return this;
+	    };
+
+	    /**
+	     * Finishes the incremental computation, reseting the internal state and
+	     * returning the result.
+	     *
+	     * @param {Boolean} raw True to get the raw string, false to get the hex string
+	     *
+	     * @return {String} The result
+	     */
+	    SparkMD5.prototype.end = function (raw) {
+	        var buff = this._buff,
+	            length = buff.length,
+	            i,
+	            tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+	            ret;
+
+	        for (i = 0; i < length; i += 1) {
+	            tail[i >> 2] |= buff.charCodeAt(i) << ((i % 4) << 3);
+	        }
+
+	        this._finish(tail, length);
+	        ret = hex(this._hash);
+
+	        if (raw) {
+	            ret = hexToBinaryString(ret);
+	        }
+
+	        this.reset();
+
+	        return ret;
+	    };
+
+	    /**
+	     * Resets the internal state of the computation.
+	     *
+	     * @return {SparkMD5} The instance itself
+	     */
+	    SparkMD5.prototype.reset = function () {
+	        this._buff = '';
+	        this._length = 0;
+	        this._hash = [1732584193, -271733879, -1732584194, 271733878];
+
+	        return this;
+	    };
+
+	    /**
+	     * Gets the internal state of the computation.
+	     *
+	     * @return {Object} The state
+	     */
+	    SparkMD5.prototype.getState = function () {
+	        return {
+	            buff: this._buff,
+	            length: this._length,
+	            hash: this._hash
+	        };
+	    };
+
+	    /**
+	     * Gets the internal state of the computation.
+	     *
+	     * @param {Object} state The state
+	     *
+	     * @return {SparkMD5} The instance itself
+	     */
+	    SparkMD5.prototype.setState = function (state) {
+	        this._buff = state.buff;
+	        this._length = state.length;
+	        this._hash = state.hash;
+
+	        return this;
+	    };
+
+	    /**
+	     * Releases memory used by the incremental buffer and other additional
+	     * resources. If you plan to use the instance again, use reset instead.
+	     */
+	    SparkMD5.prototype.destroy = function () {
+	        delete this._hash;
+	        delete this._buff;
+	        delete this._length;
+	    };
+
+	    /**
+	     * Finish the final calculation based on the tail.
+	     *
+	     * @param {Array}  tail   The tail (will be modified)
+	     * @param {Number} length The length of the remaining buffer
+	     */
+	    SparkMD5.prototype._finish = function (tail, length) {
+	        var i = length,
+	            tmp,
+	            lo,
+	            hi;
+
+	        tail[i >> 2] |= 0x80 << ((i % 4) << 3);
+	        if (i > 55) {
+	            md5cycle(this._hash, tail);
+	            for (i = 0; i < 16; i += 1) {
+	                tail[i] = 0;
+	            }
+	        }
+
+	        // Do the final computation based on the tail and length
+	        // Beware that the final length may not fit in 32 bits so we take care of that
+	        tmp = this._length * 8;
+	        tmp = tmp.toString(16).match(/(.*?)(.{0,8})$/);
+	        lo = parseInt(tmp[2], 16);
+	        hi = parseInt(tmp[1], 16) || 0;
+
+	        tail[14] = lo;
+	        tail[15] = hi;
+	        md5cycle(this._hash, tail);
+	    };
+
+	    /**
+	     * Performs the md5 hash on a string.
+	     * A conversion will be applied if utf8 string is detected.
+	     *
+	     * @param {String}  str The string
+	     * @param {Boolean} raw True to get the raw string, false to get the hex string
+	     *
+	     * @return {String} The result
+	     */
+	    SparkMD5.hash = function (str, raw) {
+	        // Converts the string to utf8 bytes if necessary
+	        // Then compute it using the binary function
+	        return SparkMD5.hashBinary(toUtf8(str), raw);
+	    };
+
+	    /**
+	     * Performs the md5 hash on a binary string.
+	     *
+	     * @param {String}  content The binary string
+	     * @param {Boolean} raw     True to get the raw string, false to get the hex string
+	     *
+	     * @return {String} The result
+	     */
+	    SparkMD5.hashBinary = function (content, raw) {
+	        var hash = md51(content),
+	            ret = hex(hash);
+
+	        return raw ? hexToBinaryString(ret) : ret;
+	    };
+
+	    // ---------------------------------------------------
+
+	    /**
+	     * SparkMD5 OOP implementation for array buffers.
+	     *
+	     * Use this class to perform an incremental md5 ONLY for array buffers.
+	     */
+	    SparkMD5.ArrayBuffer = function () {
+	        // call reset to init the instance
+	        this.reset();
+	    };
+
+	    /**
+	     * Appends an array buffer.
+	     *
+	     * @param {ArrayBuffer} arr The array to be appended
+	     *
+	     * @return {SparkMD5.ArrayBuffer} The instance itself
+	     */
+	    SparkMD5.ArrayBuffer.prototype.append = function (arr) {
+	        var buff = concatenateArrayBuffers(this._buff.buffer, arr, true),
+	            length = buff.length,
+	            i;
+
+	        this._length += arr.byteLength;
+
+	        for (i = 64; i <= length; i += 64) {
+	            md5cycle(this._hash, md5blk_array(buff.subarray(i - 64, i)));
+	        }
+
+	        this._buff = (i - 64) < length ? new Uint8Array(buff.buffer.slice(i - 64)) : new Uint8Array(0);
+
+	        return this;
+	    };
+
+	    /**
+	     * Finishes the incremental computation, reseting the internal state and
+	     * returning the result.
+	     *
+	     * @param {Boolean} raw True to get the raw string, false to get the hex string
+	     *
+	     * @return {String} The result
+	     */
+	    SparkMD5.ArrayBuffer.prototype.end = function (raw) {
+	        var buff = this._buff,
+	            length = buff.length,
+	            tail = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+	            i,
+	            ret;
+
+	        for (i = 0; i < length; i += 1) {
+	            tail[i >> 2] |= buff[i] << ((i % 4) << 3);
+	        }
+
+	        this._finish(tail, length);
+	        ret = hex(this._hash);
+
+	        if (raw) {
+	            ret = hexToBinaryString(ret);
+	        }
+
+	        this.reset();
+
+	        return ret;
+	    };
+
+	    /**
+	     * Resets the internal state of the computation.
+	     *
+	     * @return {SparkMD5.ArrayBuffer} The instance itself
+	     */
+	    SparkMD5.ArrayBuffer.prototype.reset = function () {
+	        this._buff = new Uint8Array(0);
+	        this._length = 0;
+	        this._hash = [1732584193, -271733879, -1732584194, 271733878];
+
+	        return this;
+	    };
+
+	    /**
+	     * Gets the internal state of the computation.
+	     *
+	     * @return {Object} The state
+	     */
+	    SparkMD5.ArrayBuffer.prototype.getState = function () {
+	        var state = SparkMD5.prototype.getState.call(this);
+
+	        // Convert buffer to a string
+	        state.buff = arrayBuffer2Utf8Str(state.buff);
+
+	        return state;
+	    };
+
+	    /**
+	     * Gets the internal state of the computation.
+	     *
+	     * @param {Object} state The state
+	     *
+	     * @return {SparkMD5.ArrayBuffer} The instance itself
+	     */
+	    SparkMD5.ArrayBuffer.prototype.setState = function (state) {
+	        // Convert string to buffer
+	        state.buff = utf8Str2ArrayBuffer(state.buff, true);
+
+	        return SparkMD5.prototype.setState.call(this, state);
+	    };
+
+	    SparkMD5.ArrayBuffer.prototype.destroy = SparkMD5.prototype.destroy;
+
+	    SparkMD5.ArrayBuffer.prototype._finish = SparkMD5.prototype._finish;
+
+	    /**
+	     * Performs the md5 hash on an array buffer.
+	     *
+	     * @param {ArrayBuffer} arr The array buffer
+	     * @param {Boolean}     raw True to get the raw string, false to get the hex one
+	     *
+	     * @return {String} The result
+	     */
+	    SparkMD5.ArrayBuffer.hash = function (arr, raw) {
+	        var hash = md51_array(new Uint8Array(arr)),
+	            ret = hex(hash);
+
+	        return raw ? hexToBinaryString(ret) : ret;
+	    };
+
+	    return SparkMD5;
+	}));
+	});
+
+	class BMF {
+	  md5(file, md5Fn, progressFn) {
+	    this.aborted = false;
+	    this.progress = 0;
+	    let currentChunk = 0;
+	    const blobSlice =
+	      File.prototype.slice ||
+	      File.prototype.mozSlice ||
+	      File.prototype.webkitSlice;
+	    const chunkSize = 2097152;
+	    const chunks = Math.ceil(file.size / chunkSize);
+	    const spark = new sparkMd5.ArrayBuffer();
+	    const reader = new FileReader();
+
+	    loadNext();
+
+	    reader.onloadend = e => {
+	      spark.append(e.target.result); // Append array buffer
+	      currentChunk++;
+	      this.progress = currentChunk / chunks;
+
+	      if (progressFn && typeof progressFn === 'function') {
+	        progressFn(this.progress);
+	      }
+
+	      if (this.aborted) {
+	        md5Fn('aborted');
+	        return
+	      }
+
+	      if (currentChunk < chunks) {
+	        loadNext();
+	      } else {
+	        md5Fn(null, spark.end());
+	      }
+	    };
+
+	    /////////////////////////
+	    function loadNext() {
+	      const start = currentChunk * chunkSize;
+	      const end = start + chunkSize >= file.size ? file.size : start + chunkSize;
+	      reader.readAsArrayBuffer(blobSlice.call(file, start, end));
+	    }
+	  }
+
+	  abort() {
+	    this.aborted = true;
+	  }
+	}
+
+	return BMF;
+
+})));

Разлика између датотеке није приказан због своје велике величине
+ 2 - 0
uni_modules/mumu-h5office/components/mumu-h5office/jsencrypt.min.js


+ 219 - 0
uni_modules/mumu-h5office/components/mumu-h5office/md5.js

@@ -0,0 +1,219 @@
+
+var hexcase = 0; 
+var b64pad  = ""; 
+var chrsz   = 8; 
+
+
+export function hex_md5(s){ return binl2hex(core_md5(str2binl(s), s.length * chrsz));}
+function b64_md5(s){ return binl2b64(core_md5(str2binl(s), s.length * chrsz));}
+function str_md5(s){ return binl2str(core_md5(str2binl(s), s.length * chrsz));}
+function hex_hmac_md5(key, data) { return binl2hex(core_hmac_md5(key, data)); }
+function b64_hmac_md5(key, data) { return binl2b64(core_hmac_md5(key, data)); }
+function str_hmac_md5(key, data) { return binl2str(core_hmac_md5(key, data)); }
+
+
+function md5_vm_test()
+{
+  return hex_md5("abc") == "900150983cd24fb0d6963f7d28e17f72";
+}
+
+
+function core_md5(x, len)
+{
+  x[len >> 5] |= 0x80 << ((len) % 32);
+  x[(((len + 64) >>> 9) << 4) + 14] = len;
+
+  var a =  1732584193;
+  var b = -271733879;
+  var c = -1732584194;
+  var d =  271733878;
+
+  for(var i = 0; i < x.length; i += 16)
+  {
+    var olda = a;
+    var oldb = b;
+    var oldc = c;
+    var oldd = d;
+
+    a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
+    d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
+    c = md5_ff(c, d, a, b, x[i+ 2], 17,  606105819);
+    b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
+    a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
+    d = md5_ff(d, a, b, c, x[i+ 5], 12,  1200080426);
+    c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
+    b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
+    a = md5_ff(a, b, c, d, x[i+ 8], 7 ,  1770035416);
+    d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
+    c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
+    b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
+    a = md5_ff(a, b, c, d, x[i+12], 7 ,  1804603682);
+    d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
+    c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
+    b = md5_ff(b, c, d, a, x[i+15], 22,  1236535329);
+
+    a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
+    d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
+    c = md5_gg(c, d, a, b, x[i+11], 14,  643717713);
+    b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
+    a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
+    d = md5_gg(d, a, b, c, x[i+10], 9 ,  38016083);
+    c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
+    b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
+    a = md5_gg(a, b, c, d, x[i+ 9], 5 ,  568446438);
+    d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
+    c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
+    b = md5_gg(b, c, d, a, x[i+ 8], 20,  1163531501);
+    a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
+    d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
+    c = md5_gg(c, d, a, b, x[i+ 7], 14,  1735328473);
+    b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
+
+    a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
+    d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
+    c = md5_hh(c, d, a, b, x[i+11], 16,  1839030562);
+    b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
+    a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
+    d = md5_hh(d, a, b, c, x[i+ 4], 11,  1272893353);
+    c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
+    b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
+    a = md5_hh(a, b, c, d, x[i+13], 4 ,  681279174);
+    d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
+    c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
+    b = md5_hh(b, c, d, a, x[i+ 6], 23,  76029189);
+    a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
+    d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
+    c = md5_hh(c, d, a, b, x[i+15], 16,  530742520);
+    b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
+
+    a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
+    d = md5_ii(d, a, b, c, x[i+ 7], 10,  1126891415);
+    c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
+    b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
+    a = md5_ii(a, b, c, d, x[i+12], 6 ,  1700485571);
+    d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
+    c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
+    b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
+    a = md5_ii(a, b, c, d, x[i+ 8], 6 ,  1873313359);
+    d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
+    c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
+    b = md5_ii(b, c, d, a, x[i+13], 21,  1309151649);
+    a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
+    d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
+    c = md5_ii(c, d, a, b, x[i+ 2], 15,  718787259);
+    b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
+
+    a = safe_add(a, olda);
+    b = safe_add(b, oldb);
+    c = safe_add(c, oldc);
+    d = safe_add(d, oldd);
+  }
+  return Array(a, b, c, d);
+
+}
+
+
+function md5_cmn(q, a, b, x, s, t)
+{
+  return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
+}
+function md5_ff(a, b, c, d, x, s, t)
+{
+  return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
+}
+function md5_gg(a, b, c, d, x, s, t)
+{
+  return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
+}
+function md5_hh(a, b, c, d, x, s, t)
+{
+  return md5_cmn(b ^ c ^ d, a, b, x, s, t);
+}
+function md5_ii(a, b, c, d, x, s, t)
+{
+  return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
+}
+
+
+function core_hmac_md5(key, data)
+{
+  var bkey = str2binl(key);
+  if(bkey.length > 16) bkey = core_md5(bkey, key.length * chrsz);
+
+  var ipad = Array(16), opad = Array(16);
+  for(var i = 0; i < 16; i++)
+  {
+    ipad[i] = bkey[i] ^ 0x36363636;
+    opad[i] = bkey[i] ^ 0x5C5C5C5C;
+  }
+
+  var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz);
+  return core_md5(opad.concat(hash), 512 + 128);
+}
+
+
+function safe_add(x, y)
+{
+  var lsw = (x & 0xFFFF) + (y & 0xFFFF);
+  var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
+  return (msw << 16) | (lsw & 0xFFFF);
+}
+
+
+function bit_rol(num, cnt)
+{
+  return (num << cnt) | (num >>> (32 - cnt));
+}
+
+
+function str2binl(str)
+{
+  var bin = Array();
+  var mask = (1 << chrsz) - 1;
+  for(var i = 0; i < str.length * chrsz; i += chrsz)
+    bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32);
+  return bin;
+}
+
+
+function binl2str(bin)
+{
+  var str = "";
+  var mask = (1 << chrsz) - 1;
+  for(var i = 0; i < bin.length * 32; i += chrsz)
+    str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask);
+  return str;
+}
+
+
+function binl2hex(binarray)
+{
+  var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
+  var str = "";
+  for(var i = 0; i < binarray.length * 4; i++)
+  {
+    str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) +
+           hex_tab.charAt((binarray[i>>2] >> ((i%4)*8  )) & 0xF);
+  }
+  return str;
+}
+
+
+function binl2b64(binarray)
+{
+  var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+  var str = "";
+  for(var i = 0; i < binarray.length * 4; i += 3)
+  {
+    var triplet = (((binarray[i   >> 2] >> 8 * ( i   %4)) & 0xFF) << 16)
+                | (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 )
+                |  ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF);
+    for(var j = 0; j < 4; j++)
+    {
+      if(i * 8 + j * 6 > binarray.length * 32) str += b64pad;
+      else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);
+    }
+  }
+  return str;
+}
+

+ 405 - 0
uni_modules/mumu-h5office/components/mumu-h5office/mumu-h5office.vue

@@ -0,0 +1,405 @@
+<template>
+	<view class="preview" v-if="isShow">
+		<template v-if="src">
+			<!-- #ifdef APP -->
+			<web-view :src="src" @message="webMess"></web-view>
+			<!-- #endif -->
+			<!-- #ifdef H5 -->
+			<iframe
+				id="if"
+				:src="src"
+				align="top"
+				frameborder="0"
+				name="frameEditor"
+				allowfullscreen=""
+				onmousewheel=""
+				allow="autoplay; camera; microphone; display-capture; clipboard-write;"
+				style="position: fixed; overflow: hidden;"
+			></iframe>
+			<!-- #endif -->
+		</template>
+		<!-- 		<template v-else>
+			{{ tps }}
+		</template> -->
+	</view>
+</template>
+<script>
+import { FILE_TYPES, BASE_URL } from './config.js'
+import { hex_md5 } from './md5.js'
+import {
+	browserFileToMd5,
+	browserUploadFile,
+	request,
+	plus_io_getFileInfo,
+	plus_uploader,
+	aesEncrypt,
+	colorRGBtoHex
+} from './tool.js'
+
+export default {
+	model: {
+		prop: 'isShow',
+		event: 'change'
+	},
+	props: {
+		isShow: {
+			type: Boolean,
+			default: false
+		},
+		appid: {
+			type: String
+		},
+		// #ifdef APP-PLUS
+		file: {
+			type: [String],
+			default: ''
+		},
+		// #endif
+		// #ifdef H5
+		file: {
+			type: [String, File],
+			default: ''
+		},
+		// #endif
+		fileName: {
+			type: String,
+			default: ''
+		},
+		privilege: {
+			type: Array,
+			default: () => ['copy', 'download', 'print']
+		}
+	},
+	watch: {
+		isShow: {
+			async handler() {
+				this.src = ''
+				if (!this.isShow) return
+				if (!this.appid) return this.showErrorMsg('请填写appid')
+				this.$emit('create')
+
+				let params = {
+					fileUrl: '',
+					fileName: '',
+					fileExit: '',
+					digest: '',
+					type: '',
+					privilege: this.privilege,
+					appid: this.appidKey
+				}
+
+				if (typeof this.file === 'string') {
+					if (this.file.indexOf('http://') === 0 || this.file.indexOf('https://') === 0) {
+						// 走网络文件
+						const res = this.getHttpFileInfo(this.file, this.fileName)
+						if (typeof res === 'string') return this.showErrorMsg(res)
+						else
+							params = {
+								...params,
+								...res
+							}
+					} else if (this.file.indexOf('md5::') === 0) {
+						// app 外部上传
+						const res = await this.getAppFileInfoToMd5(this.file)
+						if (typeof res === 'string') return this.showErrorMsg(res)
+						else
+							params = {
+								...params,
+								...res
+							}
+					} else {
+						// 走 app  内部本地上传
+						try {
+							const res = await this.getAppFileInfo(this.file)
+							if (typeof res === 'string') return this.showErrorMsg(res)
+							else
+								params = {
+									...params,
+									...res
+								}
+						} catch (e) {
+							return this.showErrorMsg('本地文件异常 001:' + e.msg ?? e.message)
+						}
+					}
+				} else {
+					// 走浏览器本地上传
+					try {
+						if (this.file.lastModifiedDate) {
+							const res = await this.getInputFileInfo(this.file)
+							if (typeof res === 'string') return this.showErrorMsg(res)
+							else
+								params = {
+									...params,
+									...res
+								}
+						} else {
+							return this.showErrorMsg('本地文件异常 002')
+						}
+					} catch (e) {
+						return this.showErrorMsg('本地文件异常 001:' + e.msg ?? e.message)
+					}
+				}
+				this.src = this.paseUrl(params)
+			},
+			immediate: true
+		},
+		appid: {
+			handler() {
+				this.appidKey = aesEncrypt(this.appid)
+			},
+			immediate: true
+		},
+		src: {
+			handler(newVal) {
+				if (newVal) {
+					this.$emit('created')
+				}
+			}
+		}
+	},
+	mounted() {
+		// #ifdef H5
+		this.addH5Event()
+		// #endif
+	},
+	data() {
+		return {
+			src: '',
+			tps: '文件读取中...',
+			appidKey: ''
+		}
+	},
+	methods: {
+		closePre() {
+			this.$emit('change', false)
+			//this.$emit('update:openLocalFile', '')
+			this.src = ''
+		},
+		paseUrl(param) {
+			param['host'] = 'app'
+			// #ifdef H5
+			param['host'] = origin
+			// #endif
+			param = encodeURIComponent(JSON.stringify(param))
+			return BASE_URL + '?param=' + param
+		},
+		getHttpFileInfo(src, fileName) {
+			if (!fileName) {
+				let path = src.split('/')
+				if (!path.length) return '传入文件路径格式不正确或者文件名称后缀不正确'
+				fileName = path.pop()
+			}
+			let exit = fileName.split('.')
+			if (!exit.length) return '传入文件名称未添加后缀类型'
+			exit = exit.pop()
+			if (!FILE_TYPES.includes(exit)) return '传入文件格式不正确或者文件名称后缀不正确'
+			return {
+				fileUrl: src,
+				fileName: fileName,
+				fileExit: exit,
+				digest: hex_md5(src),
+				type: 'src'
+			}
+		},
+		async getInputFileInfo(fileLocal) {
+			try {
+				const fileName = fileLocal.name
+				let exit = fileName.split('.')
+				if (!exit.length) return '传入文件名称未添加后缀类型'
+				exit = exit.pop()
+				if (!FILE_TYPES.includes(exit)) return '传入文件格式不正确或者文件名称后缀不正确'
+				const fileMd5 = await browserFileToMd5(fileLocal)
+				const fileInfo = {
+					size: fileLocal.size,
+					fileMd5: fileMd5,
+					appid: this.appidKey
+				}
+				const checkRes = await request({
+					url: '/checkFileInfo',
+					type: 'POST',
+					data: fileInfo
+				})
+
+				if (checkRes.msg === 0) {
+					const res = await browserUploadFile('/uploadLocalFile', fileLocal, {
+						fileMd5: fileMd5,
+						appid: this.appidKey,
+						size: fileLocal.size,
+						fileExit: exit,
+						fileName: fileName + '(无缓存)',
+						host: origin
+					})
+				}
+				const fileUrl = 'http://localhost/' + fileMd5 + '.' + exit
+				return {
+					fileMd5: fileMd5,
+					fileUrl: fileUrl,
+					fileName: fileName,
+					fileExit: exit,
+					digest: hex_md5(fileUrl),
+					type: 'localhost'
+				}
+			} catch (e) {
+				throw e
+			}
+		},
+		async getAppFileInfo(fileLocalPath) {
+			try {
+				let { size, digest: fileMd5 } = await plus_io_getFileInfo(fileLocalPath)
+				fileMd5 = fileMd5.toLowerCase()
+				const fileName = fileLocalPath.split('/').pop()
+				let exit = fileName.split('.')
+				if (!exit.length) return '传入文件名称未添加后缀类型'
+				exit = exit.pop()
+				if (!FILE_TYPES.includes(exit)) return '传入文件格式不正确或者文件名称后缀不正确'
+
+				const fileInfo = {
+					size: size,
+					fileMd5: fileMd5,
+					appid: this.appidKey
+				}
+				const checkRes = await request({
+					url: '/checkFileInfo',
+					type: 'POST',
+					data: fileInfo
+				})
+				if (checkRes.msg === 0) {
+					const res = await plus_uploader('/uploadLocalFile', fileLocalPath, {
+						fileMd5: fileMd5,
+						appid: this.appidKey,
+						size: size + '',
+						fileExit: exit,
+						fileName: fileName + '(无缓存)',
+						host: 'app'
+					})
+				}
+
+				const fileUrl = 'http://localhost/' + fileMd5 + '.' + exit
+				return {
+					fileMd5: fileMd5,
+					fileUrl: fileUrl,
+					fileName: fileName,
+					fileExit: exit,
+					digest: hex_md5(fileUrl),
+					type: 'localhost'
+				}
+			} catch (e) {
+				console.log(e, 'err')
+				throw e
+			}
+		},
+		getAppFileInfoToMd5(fileLocalPath) {
+			const fileName = fileLocalPath.replace('md5::', '')
+			let exit = fileName.split('.')
+			if (!exit.length) return '传入文件名称未添加后缀类型'
+			const fileMd5 = exit[0]
+			exit = exit.pop()
+			if (!FILE_TYPES.includes(exit)) return '传入文件格式不正确或者文件名称后缀不正确'
+
+			const fileUrl = 'http://localhost/' + fileMd5 + '.' + exit
+			return {
+				fileMd5: fileMd5,
+				fileUrl: fileUrl,
+				fileName: fileName,
+				fileExit: exit,
+				digest: hex_md5(fileUrl),
+				type: 'localhost'
+			}
+		},
+
+		addH5Event() {
+			window.addEventListener(
+				'message',
+				e => {
+					if (e.data && typeof e.data === 'string') {
+						const data = JSON.parse(e.data)
+						this.childEvent(data)
+					}
+				},
+				false
+			)
+		},
+		webMess(e) {
+			this.childEvent(e.detail.data[0])
+		},
+		childEvent(data) {
+			if (data.type && data.type === 'webOffice') {
+				switch (data.event) {
+					case 'close':
+						this.closePre()
+						this.$emit('close')
+						break
+					case 'error':
+						this.showErrorMsg(data.msg)
+						break
+					case 'download':
+						this.$emit('download', data.msg)
+						break
+					case 'init':
+						let params = JSON.parse(data.msg)
+						params.backgroundColor = colorRGBtoHex(params.backgroundColor)
+						this.$emit('init', params)
+						break
+					case 'inited':
+						let paramss = JSON.parse(data.msg)
+						paramss.backgroundColor = colorRGBtoHex(paramss.backgroundColor)
+						this.$emit('inited', paramss)
+						break
+					case 'readFiled':
+						this.$emit('readFiled', data.msg)
+						break
+				}
+			}
+		},
+		showErrorMsg(msg) {
+			this.$emit('error', msg)
+			uni.showModal({
+				title: '系统异常!',
+				content: msg,
+				showCancel: false,
+				success: () => {
+					this.closePre()
+				}
+			})
+		}
+	}
+}
+</script>
+<style lang="scss" scoped>
+.preview {
+	position: fixed;
+	top: var(--window-top);
+	bottom: 0;
+	left: 0;
+	right: 0;
+	z-index: 100;
+	background-color: #fff;
+
+	.error {
+		position: absolute;
+		top: 50%;
+		left: 50%;
+		transform: translate(-50%, -50%);
+		color: #ccc;
+	}
+
+	.close {
+		position: absolute;
+		top: 5px;
+		right: 10px;
+		background-color: #ccc;
+		color: #fff;
+		padding: 5px 10px;
+		border-radius: 5px;
+		font-size: 12px;
+		z-index: 102;
+	}
+
+	#if {
+		position: relative;
+		z-index: 101;
+		width: 100%;
+		height: calc(100% - var(--window-top));
+	}
+}
+</style>

+ 127 - 0
uni_modules/mumu-h5office/components/mumu-h5office/tool.js

@@ -0,0 +1,127 @@
+import FileMd5 from './index.umd.js'
+import {
+	BASE_URL,
+	AES_KEY as aa,
+	PUBLIC_KEY
+} from './config.js'
+import JSEncrypt from './jsencrypt.min.js'
+import {
+	hex_md5
+} from './md5.js'
+
+
+
+/** 浏览器获取文件 md5 */
+export const browserFileToMd5 = (file) => {
+	return new Promise((resove, reject) => {
+		const bmf = new FileMd5()
+		bmf.md5(file, (err, md5) => {
+			if (err) reject(err)
+			resove(md5)
+		})
+	})
+}
+
+/** 浏览器上传文件 */
+export const browserUploadFile = (url, file, addData = {}) => {
+	return new Promise((resove, reject) => {
+		uni.uploadFile({
+			url: BASE_URL + url,
+			file,
+			name: 'file',
+			formData: addData,
+			timeout: 1000 * 60 * 100,
+			success({
+				data
+			}) {
+				data = JSON.parse(data)
+				if (data.code !== 200) reject(data)
+				resove(data)
+			},
+			fail(err) {
+				reject(err)
+			}
+		})
+	})
+}
+
+/* 网络请求 */
+export const request = (options = {
+	type: 'GET',
+	url: '',
+	data: {}
+}) => {
+	return new Promise((resove, reject) => {
+		uni.request({
+			url: BASE_URL + options.url,
+			method: options.type,
+			data: options.data,
+			success({
+				data
+			}) {
+				if (data.code !== 200) reject(data)
+				resove(data)
+			},
+			fail(err) {
+				reject(err)
+			}
+		})
+	})
+}
+//
+/* 获取app文件信息 */
+export const plus_io_getFileInfo = (filePath) => {
+	return new Promise((resove, reject) => {
+		plus.io.getFileInfo({
+			filePath,
+			digestAlgorithm: 'md5',
+			success: res => {
+				resove(res)
+			},
+			fail: (err) => {
+				reject(err)
+			}
+		})
+	})
+}
+
+/* app 文件上传 */
+export const plus_uploader = (url, filePath, addData = {}) => {
+	return new Promise((resove, reject) => {
+		const task = plus.uploader.createUpload(BASE_URL + url, {
+				method: "POST",
+				priority: 0
+			},
+			function(res, status) {
+				if (status !== 200) reject(res)
+				res = JSON.parse(res.responseText)
+				if (res.code !== 200) reject(res)
+				resove(res)
+			}
+		);
+		task.addFile(filePath, {
+			key: "file"
+		});
+		for (let key in addData) {
+			task.addData(key, addData[key]);
+		}
+		task.start();
+	})
+}
+
+export const aesEncrypt = (value) => {
+	const e = new JSEncrypt()
+	e.setPublicKey(PUBLIC_KEY);
+	const s = e.encrypt(value)
+	return s
+}
+
+// rgb 转 16
+export function colorRGBtoHex(color) {
+	var rgb = color.split(',');
+	var r = parseInt(rgb[0].split('(')[1]);
+	var g = parseInt(rgb[1]);
+	var b = parseInt(rgb[2].split(')')[0]);
+	var hex = "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
+	return hex;
+}

+ 84 - 0
uni_modules/mumu-h5office/package.json

@@ -0,0 +1,84 @@
+{
+  "id": "mumu-h5office",
+  "displayName": "h5office。预览office文件,预览文档,打开PDF WORD PPT EXCEL 文件",
+  "version": "1.0.1",
+  "description": "h5office是兼容 `浏览器端(h5)`  `iOS app端`  `Android app端` 的文档预览工具。支持权限配置 `下载`  `拷贝文字` `打印`",
+  "keywords": [
+    "excel",
+    "PDF",
+    "PDF预览",
+    "文档预览"
+],
+  "repository": "",
+  "engines": {
+    "HBuilderX": "^3.1.0"
+  },
+  "dcloudext": {
+    "type": "component-vue",
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+      "ads": "无",
+      "data": "无",
+      "permissions": "无"
+    },
+    "npmurl": ""
+  },
+  "uni_modules": {
+    "dependencies": [],
+    "encrypt": [],
+    "platforms": {
+      "cloud": {
+        "tcb": "y",
+        "aliyun": "y"
+      },
+      "client": {
+        "Vue": {
+          "vue2": "y",
+          "vue3": "u"
+        },
+        "App": {
+          "app-vue": "y",
+          "app-nvue": "n"
+        },
+        "H5-mobile": {
+          "Safari": "y",
+          "Android Browser": "y",
+          "微信浏览器(Android)": "y",
+          "QQ浏览器(Android)": "y"
+        },
+        "H5-pc": {
+          "Chrome": "y",
+          "IE": "y",
+          "Edge": "y",
+          "Firefox": "y",
+          "Safari": "y"
+        },
+        "小程序": {
+          "微信": "n",
+          "阿里": "n",
+          "百度": "n",
+          "字节跳动": "n",
+          "QQ": "n",
+          "钉钉": "n",
+          "快手": "n",
+          "飞书": "n",
+          "京东": "n"
+        },
+        "快应用": {
+          "华为": "n",
+          "联盟": "n"
+        }
+      }
+    }
+  }
+}

+ 94 - 0
uni_modules/mumu-h5office/readme.md

@@ -0,0 +1,94 @@
+# mumu-h5office
+
+## 说明
+
+h5office是兼容 `浏览器端(h5)`  `iOS app端`  `Android app端` 的文档预览工具。支持权限配置 `下载`  `拷贝文字` `打印`。支持app端本地文件预览,网络文件预览。手机端支持触屏操作双指放大缩小,pc端UI美观好看
+
+### 必读
+
+服务器资源有限,每个appid都会有次数限制,
+
+使用次数可以在小程序中进行刷新,刷新次数是没有限制的(需观看广告)
+
+小程序中可以设置次数不足邮件提醒,当次数低于设置的数量时会通过邮件进行提示
+
+当前插件的后端服务是共用一台 2核心、4G内存、30M带宽 服务器
+
+可能在高峰期的时候文件预览速度会变慢。
+
+如果对预览文件速度、预览次数有大量的需求,或者大文件预览。请联系开发者部署私有化的版本。
+
+#### 本地文件预览功能说明
+
+本地文件预览的本质,其实是先把文件上传到服务器,再通过服务器进行解析。
+
+如果对文件隐私有需求,只有选择服务器端的服务进行私有部署。
+
+[私有部署说明](https://h5office.cn/price)
+
+### Appid 申请方法
+
+![img](https://h5office.cn/images/getAppid.jpg)
+
+
+
+## 简单使用	
+
+**插件已支持 uni_modules 支持组件easycom,以下代码演示的是普通使用**
+
+```html
+<!-- HTML -->
+<mumu-h5office v-model="isShow" :file="inputFile" appid="你申请的Appid"></mumu-h5office>
+<button @click="openFile">打开网络文件</button>
+```
+
+```javascript
+// script
+import mumuH5office 
+from "@/uni_modules/mumu-h5office/components/mumu-h5office/mumu-h5office.vue"
+export default {
+  components: { mumuH5office },
+  data() {
+    inputFile: '',
+    isShow: false,
+  },
+  methods: {
+    openFile() {
+      this.inputFile = "https://h5plugin.mumudev.top/public/previewOffce/333.docx"
+      this.isShow = true
+    }
+  }
+}
+```
+
+
+
+## [官方文档:https://h5office.cn](https://h5office.cn)
+
+## 预览
+
+[浏览器中直接打开:https://h5plugin.mumudev.top/#/pages/h5office/h5office](https://h5plugin.mumudev.top/#/pages/h5office/h5office)
+
+
+
+扫码预览:
+
+![h5office预览](https://h5plugin.mumudev.top/public/h5office/qrcode.png)
+
+下载完整演示代码、安卓app:
+
+[官网下载页面:https://h5office.cn/uni-app/%E5%AE%89%E8%A3%85](https://h5office.cn/uni-app/%E5%AE%89%E8%A3%85)
+
+
+
+## 属性、事件、其他组件
+
+请前往官网进行查看:
+
+[属性和事件文档说明:https://h5office.cn/uni-app/%E5%B1%9E%E6%80%A7%E5%92%8C%E4%BA%8B%E4%BB%B6](https://h5office.cn/uni-app/%E5%B1%9E%E6%80%A7%E5%92%8C%E4%BA%8B%E4%BB%B6)
+
+
+
+## 支持作者
+
+![支持作者](https://student.mumudev.top/wxMP.jpg)

+ 9 - 0
uni_modules/mumu-previewOffce/changelog.md

@@ -0,0 +1,9 @@
+## 1.0.3(2023-02-22)
+本插件将于2023/11/30停止提供服务
+## 1.0.2(2022-07-19)
+- 【添加】 兼容大部分小程序
+- 【修复】 微信小程序中直接打开问题
+## 1.0.1(2022-07-02)
+修复在 ios 下背景出现大量标题,主题,创作者等等数据
+## 1.0.0(2022-05-25)
+版本上线

+ 215 - 0
uni_modules/mumu-previewOffce/components/mumu-previewOffce/mumu-previewOffce.vue

@@ -0,0 +1,215 @@
+<template>
+	<view class="preview" v-if="value && isMp === false">
+		<view class="right" :class="previewType"></view>
+		<view class="left" :class="previewType"></view>
+		<iframe :src="previewUrl" width="100%" frameborder="0" id="if"></iframe>
+		<view class="error" v-if="isError">传入文件格式不正确</view>
+		<view class="loading">预览模块加载中...</view>
+		<view class="close" @click="closePre()">关闭</view>
+	</view>
+</template>
+<script>
+export default {
+	model: {
+		event: 'change'
+	},
+	props: {
+		fileUrl: {
+			type: String,
+			default: ''
+		},
+		fileType: {
+			type: String,
+			default: ''
+		},
+		value: {
+			type: Boolean
+		},
+	},
+	data() {
+		return {
+			previewUrl: ``,
+			isError: false,
+			isMp: false
+		}
+	},
+	watch: {
+		fileUrl: {
+			handler(newValue) {
+				if (!newValue) return
+				//#ifdef MP
+				this.isMp = true
+				uni.downloadFile({
+					url: newValue,
+					success: res => {
+						const filePath = res.tempFilePath
+						uni.openDocument({
+							filePath: filePath,
+							success: res => {
+								console.log('打开文档成功')
+							}
+						})
+					}
+				})
+
+				//#endif
+
+				//#ifndef MP
+				let exit = newValue.split('.')
+				if (!exit.length) return (this.isError = true)
+				exit = exit.pop()
+				const arr = ['pptx', 'ppt', 'docx', 'doc', 'xlsx', 'xls', 'pdf']
+				if (arr.indexOf(this.fileType) === -1) return (this.isError = true)
+				this.isError = false
+				if (this.fileType === 'pdf') return (this.previewUrl = this.pasePdfUrl(newValue))
+				this.previewUrl = this.paseOfficeUrl(newValue)
+				//#endif
+			},
+			immediate: true
+		}
+	},
+	methods: {
+		closePre() {
+			this.fileUrl = ""
+			this.$emit('change', false)
+		},
+		paseOfficeUrl(url) {
+			url = encodeURIComponent(url)
+			return `https://view.officeapps.live.com/op/embed.aspx?src=${url}`
+		},
+		pasePdfUrl(url) {
+			//url = encodeURIComponent(url)
+			return 'http://www.pfile.com.cn/api/profile/onlinePreview?url=' + encodeURIComponent(url)
+		}
+	},
+	computed: {
+		previewType() {
+			let exit = this.fileUrl.split('.')
+			if (!exit.length) return console.log('传入文件格式不正确')
+			exit = exit.pop()
+			console.log(this.fileType);
+			switch (this.fileType) {
+				case 'pptx':
+					return 'ppt'
+				case 'ppt':
+					return 'ppt'
+				case 'docx':
+					return 'word'
+				case 'doc':
+					return 'word'
+				case 'xlsx':
+					return 'excel'
+				case 'xls':
+					return 'excel'
+				case 'pdf':
+					return 'pdf'
+				default:
+					console.log('传入文件格式不正确')
+			}
+		}
+	}
+}
+</script>
+<style lang="scss" scoped>
+.preview {
+	position: fixed;
+	top: 0;
+	bottom: 0;
+	left: 0;
+	right: 0;
+	background-color: #fff;
+	z-index: 998;
+
+	.right,
+	.left {
+		position: absolute;
+		z-index: 99999;
+	}
+
+	.ppt {
+		height: 20px;
+		width: 80px;
+		background-color: #444444;
+
+		&.right {
+			bottom: 2px;
+			right: 4px;
+		}
+
+		&.left {
+			left: 4px;
+			bottom: 2px;
+			width: 50px;
+		}
+	}
+
+	.word {
+		position: absolute;
+		width: 80px;
+		height: 17px;
+		background-color: #ffffff;
+
+		&.right {
+			bottom: 2px;
+			right: 2px;
+		}
+
+		&.left {
+			left: 2px;
+			bottom: 2px;
+			width: 40px;
+		}
+	}
+
+	.excel {
+		position: absolute;
+		width: 90px;
+		height: 23px;
+		background-image: linear-gradient(#3f4244, #36383a);
+
+		&.right {
+			bottom: 2px;
+			right: 2px;
+		}
+
+		&.left {
+			left: 2px;
+			bottom: 2px;
+		}
+	}
+
+	.close {
+		position: absolute;
+		top: 100rpx;
+		right: 20rpx;
+		background-color: #ccc;
+		color: #fff;
+		padding: 5px 10px;
+		border-radius: 5px;
+		font-size: 12px;
+		z-index: 999;
+	}
+
+	.error {
+		position: absolute;
+		top: 50%;
+		left: 50%;
+		transform: translate(-50%, -50%);
+		color: #ccc;
+	}
+
+	.loading {
+		position: absolute;
+		top: 50%;
+		left: 50%;
+		transform: translate(-50%, -50%);
+		color: #ccc;
+	}
+
+	#if {
+		height: 100%;
+		position: relative;
+		z-index: 101;
+	}
+}
+</style>

+ 85 - 0
uni_modules/mumu-previewOffce/package.json

@@ -0,0 +1,85 @@
+{
+  "id": "mumu-previewOffce",
+  "displayName": "预览offce文件。可以打开 PDF WORD PPT EXCEL 文件",
+  "version": "1.0.3",
+  "description": "在程序中直接打开 offce 文件进行预览。可以打开 PDF WORD PPT EXCEL 文件",
+  "keywords": [
+    "PDF",
+    "WORD",
+    "PPT",
+    "EXCEL",
+    "offce"
+],
+  "repository": "",
+  "engines": {
+    "HBuilderX": "^3.1.0"
+  },
+"dcloudext": {
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+      "ads": "无",
+      "data": "无",
+      "permissions": "无"
+    },
+    "npmurl": "",
+    "type": "component-vue"
+  },
+  "uni_modules": {
+    "dependencies": [],
+    "encrypt": [],
+    "platforms": {
+      "cloud": {
+        "tcb": "y",
+        "aliyun": "y"
+      },
+      "client": {
+        "Vue": {
+          "vue2": "y",
+          "vue3": "y"
+        },
+        "App": {
+          "app-vue": "y",
+          "app-nvue": "n"
+        },
+        "H5-mobile": {
+          "Safari": "y",
+          "Android Browser": "y",
+          "微信浏览器(Android)": "y",
+          "QQ浏览器(Android)": "y"
+        },
+        "H5-pc": {
+          "Chrome": "y",
+          "IE": "n",
+          "Edge": "y",
+          "Firefox": "y",
+          "Safari": "y"
+        },
+        "小程序": {
+          "微信": "y",
+          "阿里": "y",
+          "百度": "y",
+          "字节跳动": "y",
+          "QQ": "y",
+          "钉钉": "y",
+          "快手": "u",
+          "飞书": "y",
+        "京东": "y"
+        },
+        "快应用": {
+          "华为": "u",
+          "联盟": "u"
+        }
+      }
+    }
+  }
+}

+ 85 - 0
uni_modules/mumu-previewOffce/readme.md

@@ -0,0 +1,85 @@
+# 插件介绍
+
+** 预览的文件地址必须是可以通过互联网访问的!!! **
+
+** 预览的文件地址必须是可以直接复制在浏览器地址中访问的!!! **
+
+## 本插件将于2023/11/30停止提供服务
+
+文档预览工具新版本已上线
+
+对比当前版本支持了手机端双手缩放操作,office文件不在依赖微软的解析接口,支持服务端完全私有化部署(内网部署),UI界面有单独的电脑端、ios端、安卓端。
+
+新版地址:[h5office。预览office文件,预览文档,打开PDF WORD PPT EXCEL 文件 - DCloud 插件市场](https://ext.dcloud.net.cn/plugin?id=10895)
+
+## 插件原理
+
+> pdf 文件预览是通过 `pdf.js` 开源库,搭建了一个pdf预览的网站。前端只需要使用 iframe 加载这个网站即可。[pdf.js 官网](http://mozilla.github.io/pdf.js/api/draft/index.html)
+>
+> offce 文件的预览是通过微软offce在线接口进行解析的。offce在线地址:https://view.officeapps.live.com/op/embed.aspx?src=‘你的文件网络地址’
+>
+> 在微信小程序中,是通过小程序中的API进行预览的。[小程序文档](https://developers.weixin.qq.com/miniprogram/dev/api/file/wx.openDocument.html)
+
+## 使用环境
+
+** 不支持nvue 。小程序中使用官方提供的api。h5与其他环境是通过上面介绍的插件原理加载。**
+
+## 插件使用
+
+**插件已支持 uni_modules 支持组件easycom,以下代码演示的是普通使用**
+
+``` html
+<!-- HTML -->
+<mumu-previewOffce :fileUrl='fileUrl' v-model='showPreview'></mumu-previewOffce>
+
+<button @click='showPreview = true'></button>
+```
+
+``` js
+	import MumuPreviewOffce from '@/uni_modules/mumu-previewOffce/components/mumu-previewOffce/mumu-previewOffce.vue'
+	export default {
+		components: {
+			MumuPreviewOffce
+		},
+    data() {
+			return {
+				showPreview: false,
+				fileUrl: 'https://h5plugin.mumudev.top/public/previewOffce/333.docx'
+			}
+		},
+     methods: {
+     
+			},
+    }
+```
+
+## 相关 API
+
+### 可传属性(Props)
+
+| 参数    | 说明                     | 类型    | 默认值 | 可选         |
+| ------- | ------------------------ | ------- | ------ | ------------ |
+| v-model | 双向绑定,显示或隐藏组件 | Boolean | false  | false / true |
+| fileUrl | 预览文件的网络地址       | String  | -      | -            |
+
+
+
+## 打开本地预览
+
+本地预览功能还在开发中...
+
+开发思路是:
+
+> 选择打开本地文件,上传到服务器。获取到服务器中的文件地址,传递给当前组件展示。
+
+没有办法直接在本地打开,所有采取这种方案。有条件的同学可以自己开发。我也会尽快把这个功能做出来。
+
+
+
+## 案例演示
+
+![](https://h5plugin.mumudev.top/public/previewOffce/qrcode.png)
+
+## 支持作者
+
+![支持作者](https://student.mumudev.top/wxMP.jpg)

+ 7 - 0
uni_modules/xe-upload/changelog.md

@@ -0,0 +1,7 @@
+## 1.0.2(2023-11-01)
+更换App端转换本地链接的方法;
+添加App端文件拓展名过滤;
+## 1.0.1(2023-09-04)
+优化部分逻辑
+## 1.0.0(2023-09-03)
+支持图片、视频选择上传;H5、微信小程序,App支持文件选择上传

+ 285 - 0
uni_modules/xe-upload/components/xe-upload/xe-upload.vue

@@ -0,0 +1,285 @@
+<!-- eslint-disable -->
+<template>
+  <view>
+    <!-- #ifdef APP-PLUS -->
+    <view class="xe-upload" v-html="renderInput" :props="mergeProps" :change:props="XeUpload.renderProps"></view>
+    <!-- #endif -->
+  </view>
+</template>
+
+<script>
+import {
+  chooseMedia,
+  chooseFile,
+  chooseMessageFile,
+  uploadFile,
+} from '../../tools/apis';
+import {
+  deepMerge,
+  awaitWrap,
+  base64ToPath,
+  isArray,
+} from '../../tools/tools';
+
+export default {
+  name: 'XeUpload',
+  props: {
+    options: {
+      default: () => ({}),
+      type: Object,
+    },
+  },
+  data() {
+    return {
+      id: 0, // APP上传框ID
+      renderInput: '', // APP上传框
+    };
+  },
+  computed: {
+    mergeOptions({ options = {} }) {
+      const tmpOptions = {
+        name: 'file',
+      };
+      return deepMerge(tmpOptions, options);
+    },
+    mergeProps({ id, renderInput, mergeOptions }) {
+      return {
+        id,
+        renderInput,
+        upload: mergeOptions,
+      };
+    },
+  },
+  methods: {
+    // 上传事件
+    async upload(type, config = {}) {
+      let tmpResult = [];
+      if (['image', 'video'].includes(type)) {
+        const [err, res] = await chooseMedia(type, config);
+        if (err) return this.handleError(err);
+        tmpResult = res?.tempFiles || [];
+      }
+      // H5 || APP-PLUS || MP-WEIXIN
+      if (['file'].includes(type)) {
+        let tmpFiles = {};
+        let tmpErr = null;
+        // #ifdef H5
+        [tmpErr, tmpFiles] = await chooseFile(config);
+        // #endif
+        // #ifdef MP-WEIXIN
+        [tmpErr, tmpFiles] = await chooseMessageFile(config);
+        // #endif
+        // #ifdef APP-PLUS
+        this.id = Math.floor(Math.random() * 100000000 + 1);
+        this.initInput(config.extension);
+        // #endif
+        if (tmpErr) return this.handleError(tmpErr);
+        tmpResult = tmpFiles?.tempFiles || [];
+      }
+      this.handleUpload(tmpResult);
+    },
+    // 初始化上传框
+    initInput(extension) {
+      const { id } = this;
+      let accept = extension;
+      if (isArray(extension)) {
+        accept = extension.join(',');
+      }
+      this.renderInput = `<input type="file" id="xe-upload-${id}" name="xe-upload" ${accept ? 'accept="' + accept + '"' : ''} />`;
+    },
+    // 文件上传(没有传入上传url时返回本地链接)
+    async handleUpload(files = []) {
+      if (files.filter((e) => Boolean(e)).length === 0) return;
+      const { mergeOptions } = this;
+      if (!mergeOptions.url) {
+        return this.handleEmits({
+          type: 'choose',
+          data: files,
+        });
+      }
+      const tmpUploads = files.map((e) =>
+        uploadFile(
+          {
+            ...mergeOptions,
+            filePath: e.tempFilePath,
+          },
+          e,
+        )
+      );
+      const [err, res] = await awaitWrap(Promise.all(tmpUploads));
+      if (err) return this.handleError(err);
+      this.handleEmits({
+        type: 'success',
+        data: res,
+      });
+    },
+    // 处理失败事件
+    handleError(error) {
+      this.handleEmits({
+        type: 'warning',
+        data: error,
+      });
+    },
+    // 处理响应事件
+    async handleEmits(e) {
+      // #ifdef APP-PLUS
+      if (e.type === 'choose') {
+        // 将base64转为本地链接
+        for (let i = 0; i < e.data.length; i += 1) {
+          const item = e.data[i];
+          if (!item.base64Url) {
+            continue;
+          }
+          const [parseError, parseUrl] = await awaitWrap(base64ToPath(item.base64Url, item.name));
+          if (!parseError) {
+            e.data[i].tempFilePath = parseUrl;
+          } else {
+            e.data[i].tempFilePath = item.base64Url;
+          }
+          delete e.data[i].base64Url;
+        }
+      }
+      // #endif
+      this.$emit('callback', e);
+    },
+  },
+};
+</script>
+
+<!-- #ifdef APP-PLUS -->
+<script module="XeUpload" lang="renderjs">
+import {
+  appUploadFile,
+} from '../../tools/apis';
+import {
+  awaitWrap,
+  fileToBase64,
+} from '../../tools/tools';
+
+export default {
+  data() {
+    return {
+      id: 0, // 上传框ID
+      uploadOptions: {}, // 上传配置
+    };
+  },
+  methods: {
+    // 处理 XeUpload 传入 renderjs 数据,以及调起上传框
+    renderProps(info) {
+      const { id, renderInput, upload } = info;
+      if (!renderInput) return;
+      this.id = id;
+      this.uploadOptions = upload;
+      this.$nextTick(() => {
+        const dom = document.getElementById(`xe-upload-${id}`);
+        dom.addEventListener('change', () => {
+          this.handleUpload();
+        });
+        dom?.click?.();
+      });
+    },
+    // 处理文件上传(没有传入url时返回本地链接)
+    async handleUpload() {
+      const {
+        url,
+        name,
+        header = {},
+        formData = {},
+      } = this.uploadOptions || {};
+      const dom = document.getElementById(`xe-upload-${this.id}`);
+      if (!dom.files[0]) return;
+      const tmpFileList = Array.from(dom.files);
+      const tmpUploads = [];
+      for (let i = 0; i < tmpFileList.length; i += 1) {
+        const e = tmpFileList[i];
+        let tmpType = 'file';
+        if (e.type.includes('image')) {
+          tmpType = 'image';
+        }
+        if (e.type.includes('video')) {
+          tmpType = 'video';
+        }
+        const tmpExts = {
+          size: e.size,
+          name: e.name,
+          type: e.type,
+          fileType: tmpType,
+          tempFilePath: '',
+          base64Url: '',
+        };
+        // 没有传入上传url时,直接返回本地链接
+        if (!url) {
+          const [parseError, parseUrl] = await awaitWrap(fileToBase64(dom.files[i]));
+          if (!parseError) {
+            tmpExts.base64Url = parseUrl;
+          }
+          tmpUploads.push(tmpExts);
+          continue;
+        };
+        const tmpData = new FormData();
+        tmpData.append(name, dom.files[i], e.name);
+        for (let key in formData) {
+          tmpData.append(key, formData[key]);
+        }
+        // 上传进度
+        const onprogress = (ev) => {
+          if(ev.lengthComputable) {
+            var result = (ev.loaded / ev.total) * 100;
+            this.handleRenderEmits({
+              type: 'onprogress',
+              data: {
+                progress: Math.floor(result),
+                current: i + 1,
+                total: tmpFileList.length,
+              },
+            });
+          };
+        }
+        tmpUploads.push(appUploadFile({
+          url,
+          header,
+          formData: tmpData
+        }, tmpExts, onprogress ));
+      }
+      // 没有传入上传url时,直接返回本地链接列表
+      if (!url) {
+        return this.handleRenderEmits({
+          type: 'choose',
+          data: tmpUploads,
+        });
+      }
+      this.handleRenderEmits({
+        type: 'onprogress',
+        data: {
+          progress: 0,
+          current: 1,
+          total: tmpFileList.length,
+        },
+      });
+      // 处理上传文件
+      const [err, res] = await awaitWrap(Promise.all(tmpUploads));
+      if (err) {
+        return this.handleRenderEmits({
+          type: 'warning',
+          data: err,
+        });
+      }
+      this.handleRenderEmits({
+        type: 'success',
+        data: res,
+      });
+    },
+    // 数据传输到XeUpload组件
+    handleRenderEmits(data) {
+      this.$ownerInstance.callMethod('handleEmits', data);
+    },
+  },
+};
+</script>
+<!-- #endif -->
+
+<style scoped>
+.xe-upload {
+  display: none;
+}
+</style>

+ 80 - 0
uni_modules/xe-upload/package.json

@@ -0,0 +1,80 @@
+{
+  "id": "xe-upload",
+  "displayName": "文件选择、文件上传组件(图片,视频,文件等)",
+  "version": "1.0.2",
+  "description": "H5、微信小程序、App端支持图片,视频,文件选择上传;其他端暂不支持文件选择上传",
+  "keywords": [
+    "App、H5、微信小程序、图片,视频,文件上传"
+],
+  "repository": "",
+"engines": {
+  },
+  "dcloudext": {
+    "type": "component-vue",
+    "sale": {
+      "regular": {
+        "price": "0.00"
+      },
+      "sourcecode": {
+        "price": "0.00"
+      }
+    },
+    "contact": {
+      "qq": ""
+    },
+    "declaration": {
+      "ads": "无",
+      "data": "无",
+      "permissions": "无"
+    },
+    "npmurl": ""
+  },
+  "uni_modules": {
+    "dependencies": [],
+    "encrypt": [],
+    "platforms": {
+      "cloud": {
+        "tcb": "y",
+        "aliyun": "y"
+      },
+      "client": {
+        "Vue": {
+          "vue2": "y",
+          "vue3": "u"
+        },
+        "App": {
+          "app-vue": "y",
+          "app-nvue": "u"
+        },
+        "H5-mobile": {
+          "Safari": "y",
+          "Android Browser": "y",
+          "微信浏览器(Android)": "y",
+          "QQ浏览器(Android)": "y"
+        },
+        "H5-pc": {
+          "Chrome": "y",
+          "IE": "u",
+          "Edge": "y",
+          "Firefox": "y",
+          "Safari": "y"
+        },
+        "小程序": {
+          "微信": "y",
+          "阿里": "y",
+          "百度": "y",
+          "字节跳动": "y",
+          "QQ": "y",
+          "钉钉": "y",
+          "快手": "y",
+          "飞书": "y",
+          "京东": "y"
+        },
+        "快应用": {
+          "华为": "y",
+          "联盟": "y"
+        }
+      }
+    }
+  }
+}

+ 78 - 0
uni_modules/xe-upload/readme.md

@@ -0,0 +1,78 @@
+# xe-upload
+
+## 说明
+
+不占用页面位置的上传组件;
+
+H5、APP、微信小程序中可上传图片,视频和文件;其他端暂时只能上传图片和视频
+
+> 上传图片通过[chooseMedia](https://uniapp.dcloud.net.cn/api/media/video.html#choosemedia)及[chooseImage](https://uniapp.dcloud.net.cn/api/media/image.html#chooseimage)实现
+
+> 上传视频通过[chooseMedia](https://uniapp.dcloud.net.cn/api/media/video.html#choosemedia)及[chooseVideo](https://uniapp.dcloud.net.cn/api/media/video.html#choosevideo)实现
+
+> H5端上传文件通过[chooseFile](https://uniapp.dcloud.net.cn/api/media/file.html#wx-choosemessagefile)实现
+
+> APP上传文件通过[renderjs](https://uniapp.dcloud.net.cn/tutorial/renderjs.html#renderjs)实现
+
+> 微信小程序上传文件通过[chooseMessageFile](https://developers.weixin.qq.com/miniprogram/dev/api/media/image/wx.chooseMessageFile.html)实现
+
+
+## 使用
+
+Attributes
+
+| 参数      | 说明 | 类型	 | 默认值 |
+| ----------- | ----------- | ----------- | ----------- |
+| options   | 请求配置(参数与uni.uploadFile的参数一致)        | object | { name: 'file' } |
+
+Events
+
+| 事件名      | 说明 | 参数	 | 
+| ----------- | ----------- | ----------- |
+| callback | 接收数据 | { type, data } |
+
+callback type
+
+| 参数      | 说明 |
+| ----------- | ----------- |
+| warning | 提示信息(下文称warning回调) |
+| success | 上传成功(下文称success回调) |
+| choose | 选择文件(下文称choose回调) |
+
+callback data
+
+```
+'callback.type === success' : [
+    {
+        "size": 176579, // 选择的文件的大小
+        "name": "Kafka.pdf", // 选择的文件的名称(小程序端可能会没有)
+        "type": "application/pdf",
+        "tempFilePath": "blob:http://192.168.137.1:8080/2585769b-3195-4f3d-b9f8-d9e99f55deec", // 临时路路径
+        "fileType": "file", // 文件类型[image, video, file]
+        "response": {
+            "result": {
+                "fileName": "Kafka.pdf",
+                "filePath": `http://localhost:3000/upload/e51d814b649122fc64892d0bc6383d07.pdf`,
+            },
+            "success": true,
+        }, // 上传返回的信息
+    }
+]
+
+'callback.type === choose' : [
+    {
+        "size": 176579, // 选择的文件的大小
+        "name": "Kafka.pdf", // 选择的文件的名称(小程序端可能会没有)
+        "type": "application/pdf",
+        "tempFilePath": "blob:http://192.168.137.1:8080/4204e460-f185-4fc9-9f4d-1bc50ab06981", // 文件临时路径
+        "fileType": "file", // 文件类型[image, video, file]
+    }
+]
+```
+
+## 注意事项
+#### 1、options入参中如果url为空,则choose回调的data列表中只有选择文件能得到的信息和临时路径,临时路径可用于自定义上传方法(APP除外);传入url选择文件后会自动上传到服务器,此时choose回调不会触发,而是执行success回调,success回调的data列表会包括选择文件能得到的信息
+#### 2、APP端文件建议直接上传到服务器,拿到文件上传后的地址再进行其他操作(目前测试APP端file转换后的Blob Url无法用于uni.uploadFile,所以建议APP文件直接上传)
+#### 3、APP端文件暂时支持单个上传
+#### 4、当uni.chooseMedia可用时,会优先使用uni.chooseMedia
+#### 5、具体使用可下载示例项目运行查看完整示例

+ 183 - 0
uni_modules/xe-upload/tools/apis.js

@@ -0,0 +1,183 @@
+// eslint-disable
+import { awaitWrap } from "./tools";
+/**
+ * 从本地相册选择图片或使用相机拍照
+ * @param {object} config 参数详情 => https://uniapp.dcloud.net.cn/api/media/image.html#chooseimage
+ * @returns
+ */
+export const chooseImage = (config) => {
+  return awaitWrap(
+    new Promise((r, j) => {
+      uni.chooseImage({
+        ...config,
+        success: (res) => {
+          const tmpFiles = res?.tempFiles.map((e) => ({
+            tempFilePath: e.path,
+            tempFile: e,
+            size: e.size,
+            name: e.name,
+            type: e.type,
+            fileType: "image",
+          }));
+          return r({ type: "image", ...res, tempFiles: tmpFiles });
+        },
+        fail: (err) => j({ mode: "chooseImage", data: err }),
+      });
+    })
+  );
+};
+
+/**
+ * 拍摄视频或从手机相册中选视频,返回视频的临时文件路径
+ * @param {object} config 参数详情 => https://uniapp.dcloud.net.cn/api/media/video.html#choosevideo
+ * @returns
+ */
+export const chooseVideo = (config) => {
+  return awaitWrap(
+    new Promise((r, j) => {
+      uni.chooseVideo({
+        ...config,
+        success: (res) => {
+          const tmpFiles = [
+            {
+              ...res,
+              tempFilePath: res.tempFilePath,
+              tempFile: res.tempFile ?? {},
+              size: res.size,
+              name: res.name,
+              type: res.tempFile?.type,
+              fileType: "video",
+            },
+          ];
+          return r({ type: "video", tempFiles: tmpFiles });
+        },
+        fail: (err) => j({ mode: "chooseVideo", data: err }),
+      });
+    })
+  );
+};
+
+/**
+ * 拍摄或从手机相册中选择图片或视频
+ * @param {object} config 参数详情 => https://uniapp.dcloud.net.cn/api/media/video.html#choosemedia
+ * @returns
+ */
+export const chooseMedia = (type, config) => {
+  if (!type) return console.error("chooseMedia type cannot be empty");
+  if (!uni.chooseMedia && type === "image") return chooseImage(config);
+  if (!uni.chooseMedia && type === "video") return chooseVideo(config);
+  return awaitWrap(
+    new Promise((r, j) => {
+      uni.chooseMedia({
+        ...config,
+        mediaType: [type],
+        success: (res) => r(res),
+        fail: (err) => j({ mode: "chooseMedia", data: err }),
+      });
+    })
+  );
+};
+
+/**
+ * 从本地选择文件(h5)
+ * @param {object} config 参数详情 => https://uniapp.dcloud.net.cn/api/media/file.html#wx-choosemessagefile
+ * @returns
+ */
+export const chooseFile = (config) => {
+  return awaitWrap(
+    new Promise((r, j) => {
+      uni.chooseFile({
+        ...config,
+        count: 1, //选择数量
+        success: (res) => {
+          const tmpFiles = res?.tempFiles.map((e) => {
+            let tmpType = "file";
+            if (e.type.includes("image")) {
+              tmpType = "image";
+            }
+            if (e.type.includes("video")) {
+              tmpType = "video";
+            }
+            return {
+              tempFilePath: e.path,
+              tempFile: e,
+              size: e.size,
+              name: e.name,
+              type: e.type,
+              fileType: tmpType,
+            };
+          });
+          return r({ type: "file", ...res, tempFiles: tmpFiles });
+        },
+        fail: (err) => j({ mode: "chooseFile", data: err }),
+      });
+    })
+  );
+};
+
+/**
+ * 从本地选择文件(微信小程序)
+ * @param {object} config 参数详情 => https://developers.weixin.qq.com/miniprogram/dev/api/media/image/wx.chooseMessageFile.html
+ * @returns
+ */
+export const chooseMessageFile = (config) => {
+  return awaitWrap(
+    new Promise((r, j) => {
+      wx.chooseMessageFile({
+        ...config,
+        success: (res) => {
+          const tmpFiles = res?.tempFiles.map((e) => ({
+            ...e,
+            tempFilePath: e.path,
+            fileType: e.type ?? "file",
+          }));
+          return r({ type: "file", ...res, tempFiles: tmpFiles });
+        },
+        fail: (err) => j({ mode: "chooseMessageFile", data: err }),
+      });
+    })
+  );
+};
+
+/**
+ * 上传
+ * @param {object} config 参数详情 => https://uniapp.dcloud.net.cn/api/request/network-file.html#uploadfile
+ * @param {object} exts 选择的文件的数据
+ * @returns {object} exts + response
+ */
+export const uploadFile = (config, exts = {}) => {
+  return new Promise((r, j) => {
+    uni.uploadFile({
+      ...config,
+      success: (res) => r({ ...exts, response: JSON.parse(res.data) }),
+      fail: (err) => j({ mode: "uploadFile", data: err }),
+    });
+  });
+};
+
+export const appUploadFile = (config, exts = {}, onprogress) => {
+  const { url, header, formData } = config;
+  return new Promise((r, j) => {
+    const xhr = new XMLHttpRequest();
+    xhr.open("POST", url, true);
+    for (let key in header) {
+      xhr.setRequestHeader(key, header[key]);
+    }
+    if (onprogress) {
+      xhr.upload.onprogress = onprogress;
+    }
+    xhr.onreadystatechange = function() {
+      if (xhr.readyState === 4) {
+        if (xhr.status === 200) {
+          r({ ...exts, response: JSON.parse(xhr.responseText) });
+        } else {
+          j({
+            mode: "uploadFile",
+            data: { data: xhr.responseText, errMsg: "uploadFile fail." },
+          });
+        }
+      }
+    };
+    xhr.send(formData);
+  });
+};

+ 180 - 0
uni_modules/xe-upload/tools/tools.js

@@ -0,0 +1,180 @@
+// eslint-disable
+export const isObject = (obj) => {
+  return obj
+    ? Object.prototype.toString.call(obj) === "[object Object]"
+    : false;
+};
+export const isArray = (arr) => {
+  return arr ? Array.isArray(arr) : false;
+};
+/**
+ * handle async await
+ * @param {*} promise promise
+ */
+export const awaitWrap = (promise) =>
+  promise.then((res) => [null, res]).catch((err) => [err, {}]);
+/**
+ * 深拷贝
+ * @param {*} source
+ */
+export const deepClone = (source) => {
+  if (!isObject(source) && !isArray(source)) return source;
+  const targetObj = isArray(source) ? [] : {}; // 判断复制的目标是数组还是对象
+  for (let keys in source) {
+    // 遍历目标
+    if (source.hasOwnProperty(keys)) {
+      if (source[keys] && typeof source[keys] === "object") {
+        // 如果值是对象,就递归一下
+        targetObj[keys] = isArray(source[keys]) ? [] : {};
+        targetObj[keys] = deepClone(source[keys]);
+      } else {
+        // 如果不是,就直接赋值
+        targetObj[keys] = source[keys];
+      }
+    }
+  }
+  return targetObj;
+};
+/**
+ * @description JS对象深度合并
+ * @param {object} target 需要拷贝的对象
+ * @param {object} source 拷贝的来源对象
+ * @returns {object|boolean} 深度合并后的对象或者false(入参有不是对象)
+ */
+export const deepMerge = (target = {}, source = {}) => {
+  target = deepClone(target);
+  if (typeof target !== "object" || typeof source !== "object") return false;
+  for (const prop in source) {
+    if (!source.hasOwnProperty(prop)) continue;
+    if (prop in target) {
+      if (typeof target[prop] !== "object") {
+        target[prop] = source[prop];
+      } else if (typeof source[prop] !== "object") {
+        target[prop] = source[prop];
+      } else if (target[prop].concat && source[prop].concat) {
+        target[prop] = target[prop].concat(source[prop]);
+      } else {
+        target[prop] = deepMerge(target[prop], source[prop]);
+      }
+    } else {
+      target[prop] = source[prop];
+    }
+  }
+  return target;
+};
+/**
+ * 将File对象转为 Blob Url
+ * @param {File} File对象
+ * @returns Blob Url
+ */
+export const fileToBlob = (file) => {
+  if (!file) return;
+  const fileType = file.type;
+  const blob = new Blob([file], { type: fileType || 'application/*' });
+  const blobUrl = window.URL.createObjectURL(blob);
+  return blobUrl;
+};
+/**
+ * 将File对象转为 base64
+ * @param {File} File对象
+ * @returns base64
+ */
+export const fileToBase64 = (file) => {
+  if (!file) return;
+  return new Promise((r, j) => {
+    const reader = new FileReader();
+    reader.onloadend = () => {
+      const base64String = reader.result;
+      r(base64String);
+    };
+    reader.onerror = () => {
+      j({ mode: 'fileToBase64', data: { errMsg: 'File to base64 fail.' } });
+    };
+    reader.readAsDataURL(file);
+  });
+};
+/**
+ * base64转临时路径(改自https://github.com/zhetengbiji/image-tools/blob/master/index.js)
+ * @param base64
+ * @returns
+ */
+function dataUrlToBase64(str) {
+  var array = str.split(',');
+  return array[array.length - 1];
+};
+function biggerThan(v1, v2) {
+  var v1Array = v1.split('.');
+  var v2Array = v2.split('.');
+  var update = false;
+  for (var index = 0; index < v2Array.length; index++) {
+    var diff = v1Array[index] - v2Array[index];
+    if (diff !== 0) {
+      update = diff > 0;
+      break;
+    }
+  }
+  return update;
+};
+var index = 0;
+function getNewFileId() {
+  return Date.now() + String(index++);
+};
+export const base64ToPath = (base64, name = '') => {
+  return new Promise((r, j) => {
+    if (typeof plus !== 'object') {
+      return j(new Error('not support'));
+    }
+    var fileName = '';
+    if (name) {
+      const names = name.split('.');
+      const extName = names.splice(-1);
+      fileName = `${names.join('.')}-${getNewFileId()}.${extName}`;
+    } else {
+      const names = base64.split(',')[0].match(/data\:\S+\/(\S+);/);
+      if (!names) {
+        j(new Error('base64 error'));
+      }
+      const extName = names[1];
+      fileName = `${getNewFileId()}.${extName}`;
+    }
+    var basePath = '_doc';
+    var dirPath = 'uniapp_temp';
+    var filePath = `${basePath}/${dirPath}/${fileName}`;
+    if (!biggerThan(plus.os.name === 'Android' ? '1.9.9.80627' : '1.9.9.80472', plus.runtime.innerVersion)) {
+      plus.io.resolveLocalFileSystemURL(basePath, function (entry) {
+        entry.getDirectory(dirPath, {
+          create: true,
+          exclusive: false,
+        }, function (entry) {
+          entry.getFile(fileName, {
+            create: true,
+            exclusive: false,
+          }, function (entry) {
+            entry.createWriter(function (writer) {
+              writer.onwrite = function () {
+                r(filePath);
+              }
+              writer.onerror = j;
+              writer.seek(0);
+              writer.writeAsBinary(dataUrlToBase64(base64));
+            }, j)
+          }, j)
+        }, j)
+      }, j)
+      return;
+    }
+    var bitmap = new plus.nativeObj.Bitmap(fileName);
+    bitmap.loadBase64Data(base64, function () {
+      bitmap.save(filePath, {}, function () {
+        bitmap.clear();
+        r(filePath);
+      }, function (error) {
+        bitmap.clear();
+        j(error);
+      });
+    }, function (error) {
+      bitmap.clear();
+      j(error);
+    });
+  });
+};

+ 744 - 2
yarn.lock

@@ -2,6 +2,114 @@
 # yarn lockfile v1
 
 
+"@vxe-ui/core@^4.0.16":
+  version "4.0.16"
+  resolved "https://registry.npmmirror.com/@vxe-ui/core/-/core-4.0.16.tgz#e6a54a3e449ce8168c778837b73fffbe09f5dfac"
+  integrity sha512-7d8485RYAv3tgr5+ckz5iK+Tt3yPjcXBaWcRdxRrxnDJNYVq6A4jAFT+Z9eHoFxqx60ASTOVCqnS8Tav/oPutA==
+  dependencies:
+    dom-zindex "^1.0.6"
+    xe-utils "^3.5.31"
+
+address@^1.2.2:
+  version "1.2.2"
+  resolved "https://registry.npmmirror.com/address/-/address-1.2.2.tgz#2b5248dac5485a6390532c6a517fda2e3faac89e"
+  integrity sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==
+
+agentkeepalive@^3.4.1:
+  version "3.5.3"
+  resolved "https://registry.npmmirror.com/agentkeepalive/-/agentkeepalive-3.5.3.tgz#c210afce942b4287e2df2fbfe6c0d57eda2ce634"
+  integrity sha512-yqXL+k5rr8+ZRpOAntkaaRgWgE5o8ESAj5DyRmVTCSoZxXmqemb9Dd7T4i5UzwuERdLAJUy6XzR9zFVuf0kzkw==
+  dependencies:
+    humanize-ms "^1.2.1"
+
+ali-oss@^6.18.1:
+  version "6.21.0"
+  resolved "https://registry.npmmirror.com/ali-oss/-/ali-oss-6.21.0.tgz#48053f563d31e8c92c8fc6eacceb68f919282b6a"
+  integrity sha512-dRvKWO/GJEa6dlsCnvmgHIbU5+yE/SmZsE4kZRGNU7Uotr9uIkQWGqv4szLTxRSxWv3YgL+BZgt+swIgitYGjA==
+  dependencies:
+    address "^1.2.2"
+    agentkeepalive "^3.4.1"
+    bowser "^1.6.0"
+    copy-to "^2.0.1"
+    dateformat "^2.0.0"
+    debug "^4.3.4"
+    destroy "^1.0.4"
+    end-or-error "^1.0.1"
+    get-ready "^1.0.0"
+    humanize-ms "^1.2.0"
+    is-type-of "^1.4.0"
+    js-base64 "^2.5.2"
+    jstoxml "^2.0.0"
+    lodash "^4.17.21"
+    merge-descriptors "^1.0.1"
+    mime "^2.4.5"
+    platform "^1.3.1"
+    pump "^3.0.0"
+    qs "^6.4.0"
+    sdk-base "^2.0.1"
+    stream-http "2.8.2"
+    stream-wormhole "^1.0.4"
+    urllib "^2.44.0"
+    utility "^1.18.0"
+    xml2js "^0.6.2"
+
+any-promise@^1.0.0, any-promise@^1.3.0:
+  version "1.3.0"
+  resolved "https://registry.npmmirror.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f"
+  integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==
+
+async-validator@~1.8.1:
+  version "1.8.5"
+  resolved "https://registry.npmmirror.com/async-validator/-/async-validator-1.8.5.tgz#dc3e08ec1fd0dddb67e60842f02c0cd1cec6d7f0"
+  integrity sha512-tXBM+1m056MAX0E8TL2iCjg8WvSyXu0Zc8LNtYqrVeyoL3+esHRZ4SieE9fKQyyU09uONjnMEjrNBMqT0mbvmA==
+  dependencies:
+    babel-runtime "6.x"
+
+autoprefixer@^10.4.20:
+  version "10.4.20"
+  resolved "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.4.20.tgz#5caec14d43976ef42e32dcb4bd62878e96be5b3b"
+  integrity sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==
+  dependencies:
+    browserslist "^4.23.3"
+    caniuse-lite "^1.0.30001646"
+    fraction.js "^4.3.7"
+    normalize-range "^0.1.2"
+    picocolors "^1.0.1"
+    postcss-value-parser "^4.2.0"
+
+babel-helper-vue-jsx-merge-props@^2.0.0:
+  version "2.0.3"
+  resolved "https://registry.npmmirror.com/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-2.0.3.tgz#22aebd3b33902328e513293a8e4992b384f9f1b6"
+  integrity sha512-gsLiKK7Qrb7zYJNgiXKpXblxbV5ffSwR0f5whkPAaBAR4fhi6bwRZxX9wBlIc5M/v8CCkXUbXZL4N/nSE97cqg==
+
+babel-runtime@6.x:
+  version "6.26.0"
+  resolved "https://registry.npmmirror.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
+  integrity sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==
+  dependencies:
+    core-js "^2.4.0"
+    regenerator-runtime "^0.11.0"
+
+bowser@^1.6.0:
+  version "1.9.4"
+  resolved "https://registry.npmmirror.com/bowser/-/bowser-1.9.4.tgz#890c58a2813a9d3243704334fa81b96a5c150c9a"
+  integrity sha512-9IdMmj2KjigRq6oWhmwv1W36pDuA4STQZ8q6YO9um+x07xgYNCD3Oou+WP/3L1HNz7iqythGet3/p4wvc8AAwQ==
+
+browserslist@^4.23.3:
+  version "4.24.2"
+  resolved "https://registry.npmmirror.com/browserslist/-/browserslist-4.24.2.tgz#f5845bc91069dbd55ee89faf9822e1d885d16580"
+  integrity sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==
+  dependencies:
+    caniuse-lite "^1.0.30001669"
+    electron-to-chromium "^1.5.41"
+    node-releases "^2.0.18"
+    update-browserslist-db "^1.1.1"
+
+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"
+  integrity sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==
+
 call-bind@^1.0.0:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c"
@@ -10,11 +118,178 @@ call-bind@^1.0.0:
     function-bind "^1.1.1"
     get-intrinsic "^1.0.2"
 
+call-bind@^1.0.7:
+  version "1.0.7"
+  resolved "https://registry.npmmirror.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9"
+  integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==
+  dependencies:
+    es-define-property "^1.0.0"
+    es-errors "^1.3.0"
+    function-bind "^1.1.2"
+    get-intrinsic "^1.2.4"
+    set-function-length "^1.2.1"
+
+caniuse-lite@^1.0.30001646, caniuse-lite@^1.0.30001651, caniuse-lite@^1.0.30001669:
+  version "1.0.30001680"
+  resolved "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001680.tgz#5380ede637a33b9f9f1fc6045ea99bd142f3da5e"
+  integrity sha512-rPQy70G6AGUMnbwS1z6Xg+RkHYPAi18ihs47GH0jcxIG7wArmPgY3XbS2sRdBbxJljp3thdT8BIqv9ccCypiPA==
+
+content-type@^1.0.2:
+  version "1.0.5"
+  resolved "https://registry.npmmirror.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918"
+  integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==
+
+copy-to@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.npmmirror.com/copy-to/-/copy-to-2.0.1.tgz#2680fbb8068a48d08656b6098092bdafc906f4a5"
+  integrity sha512-3DdaFaU/Zf1AnpLiFDeNCD4TOWe3Zl2RZaTzUvWiIk5ERzcCodOE20Vqq4fzCbNoHURFHT4/us/Lfq+S2zyY4w==
+
+core-js@^2.4.0:
+  version "2.6.12"
+  resolved "https://registry.npmmirror.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec"
+  integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==
+
+core-util-is@^1.0.2, core-util-is@~1.0.0:
+  version "1.0.3"
+  resolved "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85"
+  integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==
+
+dateformat@^2.0.0:
+  version "2.2.0"
+  resolved "https://registry.npmmirror.com/dateformat/-/dateformat-2.2.0.tgz#4065e2013cf9fb916ddfd82efb506ad4c6769062"
+  integrity sha512-GODcnWq3YGoTnygPfi02ygEiRxqUxpJwuRHjdhJYuxpcZmDq4rjBiXYmbCCzStxo176ixfLT6i4NPwQooRySnw==
+
+debug@^4.3.4:
+  version "4.3.7"
+  resolved "https://registry.npmmirror.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52"
+  integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==
+  dependencies:
+    ms "^2.1.3"
+
+deepmerge@^1.2.0:
+  version "1.5.2"
+  resolved "https://registry.npmmirror.com/deepmerge/-/deepmerge-1.5.2.tgz#10499d868844cdad4fee0842df8c7f6f0c95a753"
+  integrity sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ==
+
+default-user-agent@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.npmmirror.com/default-user-agent/-/default-user-agent-1.0.0.tgz#16c46efdcaba3edc45f24f2bd4868b01b7c2adc6"
+  integrity sha512-bDF7bg6OSNcSwFWPu4zYKpVkJZQYVrAANMYB8bc9Szem1D0yKdm4sa/rOCs2aC9+2GMqQ7KnwtZRvDhmLF0dXw==
+  dependencies:
+    os-name "~1.0.3"
+
+define-data-property@^1.1.4:
+  version "1.1.4"
+  resolved "https://registry.npmmirror.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e"
+  integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==
+  dependencies:
+    es-define-property "^1.0.0"
+    es-errors "^1.3.0"
+    gopd "^1.0.1"
+
+destroy@^1.0.4:
+  version "1.2.0"
+  resolved "https://registry.npmmirror.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015"
+  integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==
+
+digest-header@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.npmmirror.com/digest-header/-/digest-header-1.1.0.tgz#e16ab6cf4545bc4eea878c8c35acd1b89664d800"
+  integrity sha512-glXVh42vz40yZb9Cq2oMOt70FIoWiv+vxNvdKdU8CwjLad25qHM3trLxhl9bVjdr6WaslIXhWpn0NO8T/67Qjg==
+
+dom-zindex@^1.0.6:
+  version "1.0.6"
+  resolved "https://registry.npmmirror.com/dom-zindex/-/dom-zindex-1.0.6.tgz#0f911b39d3e542232ad91307638dbb869643f89e"
+  integrity sha512-FKWIhiU96bi3xpP9ewRMgANsoVmMUBnMnmpCT6dPMZOunVYJQmJhSRruoI0XSPoHeIif3kyEuiHbFrOJwEJaEA==
+
+ee-first@~1.1.1:
+  version "1.1.1"
+  resolved "https://registry.npmmirror.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
+  integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
+
+electron-to-chromium@^1.5.41:
+  version "1.5.63"
+  resolved "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.63.tgz#69444d592fbbe628d129866c2355691ea93eda3e"
+  integrity sha512-ddeXKuY9BHo/mw145axlyWjlJ1UBt4WK3AlvkT7W2AbqfRQoacVoRUCF6wL3uIx/8wT9oLKXzI+rFqHHscByaA==
+
+element-ui@^2.15.14:
+  version "2.15.14"
+  resolved "https://registry.npmmirror.com/element-ui/-/element-ui-2.15.14.tgz#3c34df79467636592812d720d2e6784e7a6ec2ea"
+  integrity sha512-2v9fHL0ZGINotOlRIAJD5YuVB8V7WKxrE9Qy7dXhRipa035+kF7WuU/z+tEmLVPBcJ0zt8mOu1DKpWcVzBK8IA==
+  dependencies:
+    async-validator "~1.8.1"
+    babel-helper-vue-jsx-merge-props "^2.0.0"
+    deepmerge "^1.2.0"
+    normalize-wheel "^1.0.1"
+    resize-observer-polyfill "^1.5.0"
+    throttle-debounce "^1.0.1"
+
+end-of-stream@^1.1.0:
+  version "1.4.4"
+  resolved "https://registry.npmmirror.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
+  integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
+  dependencies:
+    once "^1.4.0"
+
+end-or-error@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.npmmirror.com/end-or-error/-/end-or-error-1.0.1.tgz#dc7a6210fe78d372fee24a8b4899dbd155414dcb"
+  integrity sha512-OclLMSug+k2A0JKuf494im25ANRBVW8qsjmwbgX7lQ8P82H21PQ1PWkoYwb9y5yMBS69BPlwtzdIFClo3+7kOQ==
+
+es-define-property@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845"
+  integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==
+  dependencies:
+    get-intrinsic "^1.2.4"
+
+es-errors@^1.3.0:
+  version "1.3.0"
+  resolved "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f"
+  integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==
+
+escalade@^3.2.0:
+  version "3.2.0"
+  resolved "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5"
+  integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==
+
+escape-html@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
+  integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==
+
+extend-shallow@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.npmmirror.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f"
+  integrity sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==
+  dependencies:
+    is-extendable "^0.1.0"
+
+formstream@^1.1.0:
+  version "1.5.1"
+  resolved "https://registry.npmmirror.com/formstream/-/formstream-1.5.1.tgz#b25f8121aa434cc82e8b36cdd765338b7b8df4de"
+  integrity sha512-q7ORzFqotpwn3Y/GBK2lK7PjtZZwJHz9QE9Phv8zb5IrL9ftGLyi2zjGURON3voK8TaZ+mqJKERYN4lrHYTkUQ==
+  dependencies:
+    destroy "^1.0.4"
+    mime "^2.5.2"
+    node-hex "^1.0.1"
+    pause-stream "~0.0.11"
+
+fraction.js@^4.3.7:
+  version "4.3.7"
+  resolved "https://registry.npmmirror.com/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7"
+  integrity sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==
+
 function-bind@^1.1.1:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
   integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
 
+function-bind@^1.1.2:
+  version "1.1.2"
+  resolved "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
+  integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
+
 get-intrinsic@^1.0.2:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.0.tgz#7ad1dc0535f3a2904bba075772763e5051f6d05f"
@@ -24,6 +299,41 @@ get-intrinsic@^1.0.2:
     has "^1.0.3"
     has-symbols "^1.0.3"
 
+get-intrinsic@^1.1.3, get-intrinsic@^1.2.4:
+  version "1.2.4"
+  resolved "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd"
+  integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==
+  dependencies:
+    es-errors "^1.3.0"
+    function-bind "^1.1.2"
+    has-proto "^1.0.1"
+    has-symbols "^1.0.3"
+    hasown "^2.0.0"
+
+get-ready@^1.0.0, get-ready@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.npmmirror.com/get-ready/-/get-ready-1.0.0.tgz#f91817f1e9adecfea13a562adfc8de883ab34782"
+  integrity sha512-mFXCZPJIlcYcth+N8267+mghfYN9h3EhsDa6JSnbA3Wrhh/XFpuowviFcsDeYZtKspQyWyJqfs4O6P8CHeTwzw==
+
+gopd@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.npmmirror.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c"
+  integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==
+  dependencies:
+    get-intrinsic "^1.1.3"
+
+has-property-descriptors@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.npmmirror.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854"
+  integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==
+  dependencies:
+    es-define-property "^1.0.0"
+
+has-proto@^1.0.1:
+  version "1.0.3"
+  resolved "https://registry.npmmirror.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd"
+  integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==
+
 has-symbols@^1.0.3:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8"
@@ -36,31 +346,226 @@ has@^1.0.3:
   dependencies:
     function-bind "^1.1.1"
 
+hasown@^2.0.0:
+  version "2.0.2"
+  resolved "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003"
+  integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==
+  dependencies:
+    function-bind "^1.1.2"
+
+humanize-ms@^1.2.0, humanize-ms@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.npmmirror.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed"
+  integrity sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==
+  dependencies:
+    ms "^2.0.0"
+
+iconv-lite@^0.6.3:
+  version "0.6.3"
+  resolved "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
+  integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==
+  dependencies:
+    safer-buffer ">= 2.1.2 < 3.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==
+
+is-class-hotfix@~0.0.6:
+  version "0.0.6"
+  resolved "https://registry.npmmirror.com/is-class-hotfix/-/is-class-hotfix-0.0.6.tgz#a527d31fb23279281dde5f385c77b5de70a72435"
+  integrity sha512-0n+pzCC6ICtVr/WXnN2f03TK/3BfXY7me4cjCAqT8TYXEl0+JBRoqBo94JJHXcyDSLUeWbNX8Fvy5g5RJdAstQ==
+
+is-extendable@^0.1.0:
+  version "0.1.1"
+  resolved "https://registry.npmmirror.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89"
+  integrity sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==
+
+is-type-of@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.npmmirror.com/is-type-of/-/is-type-of-1.4.0.tgz#3ed175a0eee888b1da4983332e7714feb8a8fb2b"
+  integrity sha512-EddYllaovi5ysMLMEN7yzHEKh8A850cZ7pykrY1aNRQGn/CDjRDE9qEWbIdt7xGEVJmjBXzU/fNnC4ABTm8tEQ==
+  dependencies:
+    core-util-is "^1.0.2"
+    is-class-hotfix "~0.0.6"
+    isstream "~0.1.2"
+
+isarray@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.npmmirror.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
+  integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==
+
+isstream@~0.1.2:
+  version "0.1.2"
+  resolved "https://registry.npmmirror.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
+  integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==
+
+js-base64@^2.5.2:
+  version "2.6.4"
+  resolved "https://registry.npmmirror.com/js-base64/-/js-base64-2.6.4.tgz#f4e686c5de1ea1f867dbcad3d46d969428df98c4"
+  integrity sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==
+
+jstoxml@^2.0.0:
+  version "2.2.9"
+  resolved "https://registry.npmmirror.com/jstoxml/-/jstoxml-2.2.9.tgz#2eebd5e55383fe66a375022ca0aa88f77bc4fb84"
+  integrity sha512-OYWlK0j+roh+eyaMROlNbS5cd5R25Y+IUpdl7cNdB8HNrkgwQzIS7L9MegxOiWNBj9dQhA/yAxiMwCC5mwNoBw==
+
 lodash.pick@^4.4.0:
   version "4.4.0"
   resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3"
   integrity sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==
 
-lodash@^4.17.20:
+lodash@^4.17.20, lodash@^4.17.21:
   version "4.17.21"
-  resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
+  resolved "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
   integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
 
+merge-descriptors@^1.0.1:
+  version "1.0.3"
+  resolved "https://registry.npmmirror.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5"
+  integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==
+
+mime@^2.4.5, mime@^2.5.2:
+  version "2.6.0"
+  resolved "https://registry.npmmirror.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367"
+  integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==
+
+minimist@^1.1.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.1:
+  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"
+
 moment@^2.27.0:
   version "2.29.4"
   resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108"
   integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==
 
+ms@^2.0.0, ms@^2.1.3:
+  version "2.1.3"
+  resolved "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
+  integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
+
+mz@^2.7.0:
+  version "2.7.0"
+  resolved "https://registry.npmmirror.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32"
+  integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==
+  dependencies:
+    any-promise "^1.0.0"
+    object-assign "^4.0.1"
+    thenify-all "^1.0.0"
+
+node-hex@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.npmmirror.com/node-hex/-/node-hex-1.0.1.tgz#606208e91f9c02b9b81531b692b9f1da4860fb24"
+  integrity sha512-iwpZdvW6Umz12ICmu9IYPRxg0tOLGmU3Tq2tKetejCj3oZd7b2nUXwP3a7QA5M9glWy8wlPS1G3RwM/CdsUbdQ==
+
+node-releases@^2.0.18:
+  version "2.0.18"
+  resolved "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f"
+  integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==
+
+normalize-range@^0.1.2:
+  version "0.1.2"
+  resolved "https://registry.npmmirror.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942"
+  integrity sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==
+
+normalize-wheel@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.npmmirror.com/normalize-wheel/-/normalize-wheel-1.0.1.tgz#aec886affdb045070d856447df62ecf86146ec45"
+  integrity sha512-1OnlAPZ3zgrk8B91HyRj+eVv+kS5u+Z0SCsak6Xil/kmgEia50ga7zfkumayonZrImffAxPU/5WcyGhzetHNPA==
+
+object-assign@^4.0.1:
+  version "4.1.1"
+  resolved "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
+  integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
+
+object-inspect@^1.13.1:
+  version "1.13.3"
+  resolved "https://registry.npmmirror.com/object-inspect/-/object-inspect-1.13.3.tgz#f14c183de51130243d6d18ae149375ff50ea488a"
+  integrity sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==
+
 object-inspect@^1.9.0:
   version "1.12.3"
   resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9"
   integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==
 
+once@^1.3.1, once@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.npmmirror.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
+  integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
+  dependencies:
+    wrappy "1"
+
+os-name@~1.0.3:
+  version "1.0.3"
+  resolved "https://registry.npmmirror.com/os-name/-/os-name-1.0.3.tgz#1b379f64835af7c5a7f498b357cb95215c159edf"
+  integrity sha512-f5estLO2KN8vgtTRaILIgEGBoBrMnZ3JQ7W9TMZCnOIGwHe8TRGSpcagnWDo+Dfhd/z08k9Xe75hvciJJ8Qaew==
+  dependencies:
+    osx-release "^1.0.0"
+    win-release "^1.0.0"
+
+osx-release@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.npmmirror.com/osx-release/-/osx-release-1.1.0.tgz#f217911a28136949af1bf9308b241e2737d3cd6c"
+  integrity sha512-ixCMMwnVxyHFQLQnINhmIpWqXIfS2YOXchwQrk+OFzmo6nDjQ0E4KXAyyUh0T0MZgV4bUhkRrAbVqlE4yLVq4A==
+  dependencies:
+    minimist "^1.1.0"
+
+pause-stream@~0.0.11:
+  version "0.0.11"
+  resolved "https://registry.npmmirror.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445"
+  integrity sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==
+  dependencies:
+    through "~2.3"
+
+picocolors@^1.0.1, picocolors@^1.1.0:
+  version "1.1.1"
+  resolved "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
+  integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
+
+platform@^1.3.1:
+  version "1.3.6"
+  resolved "https://registry.npmmirror.com/platform/-/platform-1.3.6.tgz#48b4ce983164b209c2d45a107adb31f473a6e7a7"
+  integrity sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==
+
+postcss-value-parser@^4.2.0:
+  version "4.2.0"
+  resolved "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
+  integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
+
 prettier@^1.12.1:
   version "1.19.1"
   resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb"
   integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==
 
+process-nextick-args@~2.0.0:
+  version "2.0.1"
+  resolved "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
+  integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
+
+pump@^3.0.0:
+  version "3.0.2"
+  resolved "https://registry.npmmirror.com/pump/-/pump-3.0.2.tgz#836f3edd6bc2ee599256c924ffe0d88573ddcbf8"
+  integrity sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==
+  dependencies:
+    end-of-stream "^1.1.0"
+    once "^1.3.1"
+
+qs@^6.4.0:
+  version "6.13.1"
+  resolved "https://registry.npmmirror.com/qs/-/qs-6.13.1.tgz#3ce5fc72bd3a8171b85c99b93c65dd20b7d1b16e"
+  integrity sha512-EJPeIn0CYrGu+hli1xilKAPXODtJ12T0sP63Ijx2/khC2JtuaN3JyNIpvmnkmaEtha9ocbG4A4cMcr+TvqvwQg==
+  dependencies:
+    side-channel "^1.0.6"
+
 qs@^6.9.4:
   version "6.11.0"
   resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a"
@@ -68,6 +573,68 @@ qs@^6.9.4:
   dependencies:
     side-channel "^1.0.4"
 
+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==
+  dependencies:
+    core-util-is "~1.0.0"
+    inherits "~2.0.3"
+    isarray "~1.0.0"
+    process-nextick-args "~2.0.0"
+    safe-buffer "~5.1.1"
+    string_decoder "~1.1.1"
+    util-deprecate "~1.0.1"
+
+regenerator-runtime@^0.11.0:
+  version "0.11.1"
+  resolved "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
+  integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
+
+resize-observer-polyfill@^1.5.0:
+  version "1.5.1"
+  resolved "https://registry.npmmirror.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464"
+  integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==
+
+safe-buffer@~5.1.0, safe-buffer@~5.1.1:
+  version "5.1.2"
+  resolved "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
+  integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
+
+"safer-buffer@>= 2.1.2 < 3.0.0":
+  version "2.1.2"
+  resolved "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
+  integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
+
+sax@>=0.6.0:
+  version "1.4.1"
+  resolved "https://registry.npmmirror.com/sax/-/sax-1.4.1.tgz#44cc8988377f126304d3b3fc1010c733b929ef0f"
+  integrity sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==
+
+sdk-base@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.npmmirror.com/sdk-base/-/sdk-base-2.0.1.tgz#ba40289e8bdf272ed11dd9ea97eaf98e036d24c6"
+  integrity sha512-eeG26wRwhtwYuKGCDM3LixCaxY27Pa/5lK4rLKhQa7HBjJ3U3Y+f81MMZQRsDw/8SC2Dao/83yJTXJ8aULuN8Q==
+  dependencies:
+    get-ready "~1.0.0"
+
+semver@^5.0.1:
+  version "5.7.2"
+  resolved "https://registry.npmmirror.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8"
+  integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==
+
+set-function-length@^1.2.1:
+  version "1.2.2"
+  resolved "https://registry.npmmirror.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449"
+  integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==
+  dependencies:
+    define-data-property "^1.1.4"
+    es-errors "^1.3.0"
+    function-bind "^1.1.2"
+    get-intrinsic "^1.2.4"
+    gopd "^1.0.1"
+    has-property-descriptors "^1.0.2"
+
 side-channel@^1.0.4:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf"
@@ -76,3 +643,178 @@ side-channel@^1.0.4:
     call-bind "^1.0.0"
     get-intrinsic "^1.0.2"
     object-inspect "^1.9.0"
+
+side-channel@^1.0.6:
+  version "1.0.6"
+  resolved "https://registry.npmmirror.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2"
+  integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==
+  dependencies:
+    call-bind "^1.0.7"
+    es-errors "^1.3.0"
+    get-intrinsic "^1.2.4"
+    object-inspect "^1.13.1"
+
+statuses@^1.3.1:
+  version "1.5.0"
+  resolved "https://registry.npmmirror.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
+  integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==
+
+stream-http@2.8.2:
+  version "2.8.2"
+  resolved "https://registry.npmmirror.com/stream-http/-/stream-http-2.8.2.tgz#4126e8c6b107004465918aa2fc35549e77402c87"
+  integrity sha512-QllfrBhqF1DPcz46WxKTs6Mz1Bpc+8Qm6vbqOpVav5odAXwbyzwnEczoWqtxrsmlO+cJqtPrp/8gWKWjaKLLlA==
+  dependencies:
+    builtin-status-codes "^3.0.0"
+    inherits "^2.0.1"
+    readable-stream "^2.3.6"
+    to-arraybuffer "^1.0.0"
+    xtend "^4.0.0"
+
+stream-wormhole@^1.0.4:
+  version "1.1.0"
+  resolved "https://registry.npmmirror.com/stream-wormhole/-/stream-wormhole-1.1.0.tgz#300aff46ced553cfec642a05251885417693c33d"
+  integrity sha512-gHFfL3px0Kctd6Po0M8TzEvt3De/xu6cnRrjlfYNhwbhLPLwigI2t1nc6jrzNuaYg5C4YF78PPFuQPzRiqn9ew==
+
+string_decoder@~1.1.1:
+  version "1.1.1"
+  resolved "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
+  integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
+  dependencies:
+    safe-buffer "~5.1.0"
+
+thenify-all@^1.0.0:
+  version "1.6.0"
+  resolved "https://registry.npmmirror.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726"
+  integrity sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==
+  dependencies:
+    thenify ">= 3.1.0 < 4"
+
+"thenify@>= 3.1.0 < 4":
+  version "3.3.1"
+  resolved "https://registry.npmmirror.com/thenify/-/thenify-3.3.1.tgz#8932e686a4066038a016dd9e2ca46add9838a95f"
+  integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==
+  dependencies:
+    any-promise "^1.0.0"
+
+throttle-debounce@^1.0.1:
+  version "1.1.0"
+  resolved "https://registry.npmmirror.com/throttle-debounce/-/throttle-debounce-1.1.0.tgz#51853da37be68a155cb6e827b3514a3c422e89cd"
+  integrity sha512-XH8UiPCQcWNuk2LYePibW/4qL97+ZQ1AN3FNXwZRBNPPowo/NRU5fAlDCSNBJIYCKbioZfuYtMhG4quqoJhVzg==
+
+through@~2.3:
+  version "2.3.8"
+  resolved "https://registry.npmmirror.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
+  integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==
+
+to-arraybuffer@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.npmmirror.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43"
+  integrity sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA==
+
+unescape@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.npmmirror.com/unescape/-/unescape-1.0.1.tgz#956e430f61cad8a4d57d82c518f5e6cc5d0dda96"
+  integrity sha512-O0+af1Gs50lyH1nUu3ZyYS1cRh01Q/kUKatTOkSs7jukXE6/NebucDVxyiDsA9AQ4JC1V1jUH9EO8JX2nMDgGQ==
+  dependencies:
+    extend-shallow "^2.0.1"
+
+update-browserslist-db@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz#80846fba1d79e82547fb661f8d141e0945755fe5"
+  integrity sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==
+  dependencies:
+    escalade "^3.2.0"
+    picocolors "^1.1.0"
+
+urllib@^2.44.0:
+  version "2.44.0"
+  resolved "https://registry.npmmirror.com/urllib/-/urllib-2.44.0.tgz#0da4b037550bdc03eb9a408de498fb4025ddc0b4"
+  integrity sha512-zRCJqdfYllRDA9bXUtx+vccyRqtJPKsw85f44zH7zPD28PIvjMqIgw9VwoTLV7xTBWZsbebUFVHU5ghQcWku2A==
+  dependencies:
+    any-promise "^1.3.0"
+    content-type "^1.0.2"
+    default-user-agent "^1.0.0"
+    digest-header "^1.0.0"
+    ee-first "~1.1.1"
+    formstream "^1.1.0"
+    humanize-ms "^1.2.0"
+    iconv-lite "^0.6.3"
+    pump "^3.0.0"
+    qs "^6.4.0"
+    statuses "^1.3.1"
+    utility "^1.16.1"
+
+util-deprecate@~1.0.1:
+  version "1.0.2"
+  resolved "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
+  integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
+
+utility@^1.16.1, utility@^1.18.0:
+  version "1.18.0"
+  resolved "https://registry.npmmirror.com/utility/-/utility-1.18.0.tgz#af55f62e6d5a272e0cb02b0ab3e7f37c46435f36"
+  integrity sha512-PYxZDA+6QtvRvm//++aGdmKG/cI07jNwbROz0Ql+VzFV1+Z0Dy55NI4zZ7RHc9KKpBePNFwoErqIuqQv/cjiTA==
+  dependencies:
+    copy-to "^2.0.1"
+    escape-html "^1.0.3"
+    mkdirp "^0.5.1"
+    mz "^2.7.0"
+    unescape "^1.0.1"
+
+uview-ui@^2.0.36:
+  version "2.0.38"
+  resolved "https://registry.npmmirror.com/uview-ui/-/uview-ui-2.0.38.tgz#02a0585f2c966a81c2997c79cfff0e8538a89c61"
+  integrity sha512-6egHDf9lXHKpG3hEjRE0vMx4+VWwKk/ReTf5x18KrIKqdvdPRqO3+B8Unh7vYYwrIxzAWIlmhZ9RJpKI/4UqPQ==
+
+vxe-pc-ui@^4.2.50:
+  version "4.3.2"
+  resolved "https://registry.npmmirror.com/vxe-pc-ui/-/vxe-pc-ui-4.3.2.tgz#62cf8f298e8d38d4c05cefc543b9e6269297023e"
+  integrity sha512-gH7r1bOKvEJ5Tdok9AsZ/FvxnumKFSD3qebj9h09cJqlpZdIygOs9c+HbTJevgAVkHhwy9o3KdE43ze8wRP2tQ==
+  dependencies:
+    "@vxe-ui/core" "^4.0.16"
+
+vxe-table@^4.5.12:
+  version "4.8.10"
+  resolved "https://registry.npmmirror.com/vxe-table/-/vxe-table-4.8.10.tgz#b950c375a0d7e252048eb85ef1ee58a4622e6cda"
+  integrity sha512-GSZSIcu4Mi3eX5Xkah2FySx83h9ySrDIg7H7BhUrpv5s5xZUUC5pQeaB0ZFXl826Uh1vcD7cqzTEiOUkw+oE5w==
+  dependencies:
+    vxe-pc-ui "^4.2.50"
+
+weixin-js-sdk@^1.6.5:
+  version "1.6.5"
+  resolved "https://registry.npmmirror.com/weixin-js-sdk/-/weixin-js-sdk-1.6.5.tgz#01fe5220b91dbfe089fc0730d061be0e68271e6a"
+  integrity sha512-Gph1WAWB2YN/lMOFB/ymb+hbU/wYazzJgu6PMMktCy9cSCeW5wA6Zwt0dpahJbJ+RJEwtTv2x9iIu0U4enuVSQ==
+
+win-release@^1.0.0:
+  version "1.1.1"
+  resolved "https://registry.npmmirror.com/win-release/-/win-release-1.1.1.tgz#5fa55e02be7ca934edfc12665632e849b72e5209"
+  integrity sha512-iCRnKVvGxOQdsKhcQId2PXV1vV3J/sDPXKA4Oe9+Eti2nb2ESEsYHRYls/UjoUW3bIc5ZDO8dTH50A/5iVN+bw==
+  dependencies:
+    semver "^5.0.1"
+
+wrappy@1:
+  version "1.0.2"
+  resolved "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+  integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
+
+xe-utils@^3.5.31:
+  version "3.5.31"
+  resolved "https://registry.npmmirror.com/xe-utils/-/xe-utils-3.5.31.tgz#b9217553de4069559dacb778c16950acbac63b9a"
+  integrity sha512-oS4yv8qktvlE0wc9yYkitDidEmThc5qN0UTRvKCvrWnejxbTyIxbwfrdZmPKdKGZtB+/U8cEAMFywLJjHtD11A==
+
+xml2js@^0.6.2:
+  version "0.6.2"
+  resolved "https://registry.npmmirror.com/xml2js/-/xml2js-0.6.2.tgz#dd0b630083aa09c161e25a4d0901e2b2a929b499"
+  integrity sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==
+  dependencies:
+    sax ">=0.6.0"
+    xmlbuilder "~11.0.0"
+
+xmlbuilder@~11.0.0:
+  version "11.0.1"
+  resolved "https://registry.npmmirror.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3"
+  integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==
+
+xtend@^4.0.0:
+  version "4.0.2"
+  resolved "https://registry.npmmirror.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
+  integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==