Browse Source

Merge branch 'master' of http://git.lcpcp.cc/root/lcoco

py 1 year ago
parent
commit
51d6442df9
96 changed files with 4357 additions and 579 deletions
  1. 1 1
      api/common.js
  2. 10 0
      api/discovery.js
  3. 2 2
      api/goods.js
  4. 2 2
      api/login.js
  5. 29 28
      components/dgex-tantan/dgex-tantan.vue
  6. 51 0
      js_sdk/tencentcloud-plugin-cos/choose-and-upload-image.js
  7. 49 0
      js_sdk/tencentcloud-plugin-cos/choose-and-upload-video.js
  8. 45 0
      js_sdk/tencentcloud-plugin-cos/choose-file.js
  9. 33 0
      js_sdk/tencentcloud-plugin-cos/get-file-access-url.js
  10. 101 0
      js_sdk/tencentcloud-plugin-cos/upload-file.js
  11. 33 0
      js_sdk/tencentcloud-plugin-cos/utils.js
  12. 20 12
      manifest.json
  13. 6 195
      package-lock.json
  14. 1 0
      package.json
  15. 1 5
      pages.json
  16. 63 33
      pages/common/dynamic/dynamic-items.vue
  17. 1 2
      pages/common/img/img-one.vue
  18. 1 2
      pages/common/img/img-three.vue
  19. 1 2
      pages/common/img/img-two.vue
  20. 14 2
      pages/index/model/attachment-list.vue
  21. 3 2
      pages/index/model/index-nav.vue
  22. 72 35
      pages/index/model/slide-item.vue
  23. 24 9
      pages/index/model/user-content.vue
  24. 1 0
      pages/login/index.vue
  25. 2 3
      pages/login/localPhoneLogin.vue
  26. 28 9
      pages/login/otherPhoneLogin.vue
  27. 8 4
      pages/perfect/cards.vue
  28. 0 1
      pages/perfect/nickname.vue
  29. 4 1
      pages/perfect/sexAndAge.vue
  30. 9 8
      service/ajax.js
  31. 129 1
      service/tools.js
  32. 24 112
      service/txOssSts.js
  33. 121 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-base-plugin/index.js
  34. 147 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/asr/asrRealtimeSdk.js
  35. 61 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/asr/index.js
  36. 55 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/asr/utils.js
  37. 23 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/captcha/config.js
  38. 65 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/captcha/index.js
  39. 53 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/captcha/utils.js
  40. 76 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/common.js
  41. 24 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/config.js
  42. 24 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/cos/config.js
  43. 62 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/cos/get-object-url.js
  44. 32 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/cos/index.js
  45. 60 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/cos/sign-post-object-api.js
  46. 29 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/faceid/config.js
  47. 37 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/faceid/index.js
  48. 59 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/faceid/utils.js
  49. 23 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/httpdns/config.js
  50. 114 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/httpdns/index.js
  51. 29 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/iai/config.js
  52. 37 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/iai/index.js
  53. 59 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/iai/utils.js
  54. 41 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/ims/image-moderation.js
  55. 94 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/ims/image-sample.js
  56. 27 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/ims/index.js
  57. 52 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/ims/utils.js
  58. 58 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/index.js
  59. 28 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/ocr/config.js
  60. 46 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/ocr/get-ocr-result.js
  61. 23 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/ocr/index.js
  62. 55 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/ocr/utils.js
  63. 63 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/package-lock.json
  64. 8 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/package.json
  65. 98 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/sms/check-verification-code.js
  66. 28 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/sms/config.js
  67. 42 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/sms/get-packages-statistics.js
  68. 33 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/sms/index.js
  69. 46 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/sms/send-sms.js
  70. 69 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/sms/send-verification-code.js
  71. 51 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/sms/utils.js
  72. 20 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/soe/config.js
  73. 66 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/soe/get-voice-point.js
  74. 23 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/soe/index.js
  75. 51 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/soe/utils.js
  76. 26 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/tiia/config.js
  77. 45 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/tiia/index.js
  78. 55 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/tiia/utils.js
  79. 27 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/tms/index.js
  80. 39 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/tms/text-moderation.js
  81. 85 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/tms/text-sample.js
  82. 52 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/tms/utils.js
  83. 29 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/tmt/config.js
  84. 41 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/tmt/index.js
  85. 58 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/tmt/utils.js
  86. 29 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/tts/config.js
  87. 37 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/tts/index.js
  88. 58 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/tts/utils.js
  89. 24 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/vod/config.js
  90. 48 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/vod/get-anti-theft-url.js
  91. 49 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/vod/get-media-info.js
  92. 52 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/vod/get-upload-signature.js
  93. 27 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/vod/index.js
  94. 51 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/vod/utils.js
  95. 54 0
      uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/yarn.lock
  96. 491 108
      yarn.lock

+ 1 - 1
api/common.js

@@ -1,6 +1,6 @@
 import {request} from '@/service/ajax'
 
-const commonUrl = '/utility/'
+const commonUrl = '/admin/utility/'
 // 发送短信
 export const commonSend = (data) => request(commonUrl + 'send', 'post', { ...data })
 // 获取sts

+ 10 - 0
api/discovery.js

@@ -0,0 +1,10 @@
+import { request } from '@/service/ajax'
+// 商品模块路由
+const goodsUrl = '/admin/discovery/'
+// 猜你喜欢
+export const getMoments = (data) => request(goodsUrl + 'moments', 'get', { ...data })
+export const getDiscoverList = (data) => request(goodsUrl + 'list', 'get', { ...data })
+export const getNearbyList = (data) => request(goodsUrl + 'nearby', 'get', { ...data })
+export const setLike = (data) => request(goodsUrl + 'right/'+data, 'get', {  })
+export const setDislike = (data) => request(goodsUrl + 'left/'+data, 'get', {  })
+

+ 2 - 2
api/goods.js

@@ -1,6 +1,6 @@
-import { request } from '../service/ajax.js'
+import { request } from '@/service/ajax'
 // 商品模块路由
-const goodsUrl = '/api/goods/'
+const goodsUrl = '/admin/goods/'
 // 猜你喜欢
 export const likeGoods = (data) => request(goodsUrl + 'like_goods', 'post', { ...data })
 

+ 2 - 2
api/login.js

@@ -1,10 +1,10 @@
 import {request} from '@/service/ajax'
 
-const newsUrl = '/utility/'
+const newsUrl = '/admin/utility/'
 // 登陆
 // export const login = (data) => request(newsUrl + 'one_click_login', 'post', { ...data },false)
 export const login = (data) => request('/auth/oauth2/token', 'post', { ...data },false)
 export const forgetPassword = (data) => request(newsUrl + 'forget_password', 'post', { ...data },false)
 export const register = (data) => request(newsUrl + 'register', 'post', { ...data },true)
-export const filUserInfo = (data) => request(newsUrl + 'fill_user_info', 'post', { ...data },true)
+export const filUserInfo = (data) => request(newsUrl + 'fillUserInfo', 'post', { ...data },true)
 export const setCard = (data) => request(newsUrl + 'card', 'post', { ...data },true)

+ 29 - 28
components/dgex-tantan/dgex-tantan.vue

@@ -38,7 +38,7 @@
                 <image     :style="{height: item.boxHeight + 'px',width:item.boxWidth+'px'}"  mode="aspectFill" :src="item.image" ></image>
 
               </view>
-              <view class="user-authentication">
+              <view class="user-authentication" v-show="item.verified">
                 <image  class="authentication-img"     mode="aspectFill" src="/static/img/index/authentication.png" ></image>
                 <view class="authentication-text sys-color-white sys-weight-600">真实头像</view>
               </view>
@@ -51,28 +51,28 @@
               </view>
               <view class="slide-data">
                 <!--                用户个性消息-->
-                <view class="on-line-box">
+                <view class="on-line-box" v-show="item.online">
                   <image  class="on-line-img"     mode="aspectFill" src="/static/img/index/on-line.png" ></image>
                   <view class="on-line-text sys-color-black-0 sys-weight-400">当前在线</view>
                 </view>
                 <view class="user-data">
-                  <text class="user-item sys-color-white sys-weight-600">Maple</text>
+                  <text class="user-item sys-color-white sys-weight-600">{{item.title}}</text>
                   <text class="user-item sys-color-white sys-weight-600">,</text>
-                  <text class="user-item sys-color-white sys-weight-600">19</text>
+                  <text class="user-item sys-color-white sys-weight-600">{{item.age}}</text>
                 </view>
                 <view class="user-city">
-                  <text class="city-item sys-color-yellow sys-weight-600">17.16</text>
+                  <text class="city-item sys-color-yellow sys-weight-600">{{item.distance}}</text>
                   <text class="city-item sys-color-white sys-weight-400">km</text>
-                  <text class="city-item sys-color-white sys-weight-600">重庆市九龙坡</text>
+                  <text class="city-item sys-color-white sys-weight-600">{{item.city}}</text>
                 </view>
                 <view class="open-wechat">
                   <image  class="open-wechat-img"     mode="aspectFill" src="/static/img/index/wechat.png" ></image>
-                  <view class="open-wechat-text sys-color-white sys-weight-400">yxk********</view>
+                  <view class="open-wechat-text sys-color-white sys-weight-400">{{item.weChat}}</view>
                   <image  class="lock-wechat-img"     mode="aspectFill" src="/static/img/index/lock.png" ></image>
                 </view>
               </view>
-              <view class="user-content">
-                <user-content></user-content>
+              <view class="user-content" v-if="currentIndex===index">
+                <user-content :user-item="item"></user-content>
               </view>
               </scroll-view>
             </view>
@@ -103,6 +103,7 @@
 <script>
 	import tools from "@/service/tools";
   import UserContent from "@/pages/index/model/user-content";
+  import {setDislike, setLike} from "@/api/discovery";
   export default {
 		name: "slide",
     components: {UserContent},
@@ -156,7 +157,6 @@
 
 		mounted() {
 			const res = uni.getSystemInfoSync()
-      console.log(res)
 			this.winWidth = res.windowWidth
 			this.winHeight = res.windowHeight-84
 
@@ -177,7 +177,9 @@
           }
         }
       },
-      setData(list){
+      async setData(list){
+
+        let isNew=false
         list.forEach((item)=>{
           item.imgIndex=0
           item.moveX=0
@@ -186,22 +188,21 @@
           if( this.list.length<=0){
             item.boxWidth=this.winWidth-18
             item.boxHeight=this.winHeight
+            isNew=true
           }else {
             item.boxWidth=this.winWidth-28
             item.boxHeight=this.winHeight-30
           }
-
           this.list.push(item)
         })
+        if(isNew && this.list.length>0){
+          this.list[0].distance=await tools.getDistance(this.list[0].latitude,this.list[0].longitude)
+        }
       },
       setImgKey(item, type){
         if(this.animationData){
           return false
         }
-
-        // this.touchEndNext(this.currentIndex)
-        // return;
-        console.log('type:'+type)
         if( this.overturnType<=0){
           let imgNum=item.images.length-1
           if(type===0){
@@ -222,7 +223,6 @@
           setTimeout(()=>{
             this.animationData=null
           },300)
-          console.log('item.imgIndex:'+item.imgIndex)
         }
       },
       setOverturnImg(item){
@@ -308,8 +308,6 @@
 				}
         this.x.move = e.touches[0].pageX;
         this.y.move = e.touches[0].pageY;
-        // console.log('this.x.start:'+this.x.start+'this.x.move:'+this.x.move)
-        // console.log('this.y.start:'+this.y.start+'this.y.move:'+this.y.move)
         this.list[index].x = this.x.move - this.x.start
         this.list[index].y = this.y.move - this.y.start
         let moveX= Math.abs(this.list[index].x)
@@ -368,26 +366,29 @@
           this.setBox(index+1,3)
 				}
 			},
-			touchEndNext(index) {
+
+		async	touchEndNext(index) {
 				this.slideing = true;
 				this.list[index].x = this.list[index].x * 5;
 				this.list[index].y = this.list[index].y * 5;
         this.list[index].moveX *= 5 ;
         this.list[index].angleNum *= 5;
         this.setBox(this.currentIndex+1,1)
-				this.touchEndDone()
+      let newIndex=this.currentIndex+1
+        this.list[newIndex].distance=await tools.getDistance(this.list[newIndex].latitude,this.list[newIndex].longitude)
+
+       await	this.touchEndDone()
 			},
 			touchEndDone() {
-
+        this.$emit('onChange', {
+          currentIndex: this.currentIndex,
+          currentItem: this.list[this.currentIndex],
+          type: this.love !== 0 ? 'love' : 'dislike'
+        })
 				return new Promise((resolve) => {
           this.imgKey=0
-					setTimeout(() => {
+          setTimeout(() => {
 						this.slideing = false
-						this.$emit('onChange', {
-							currentIndex: this.currentIndex,
-							currentItem: this.list[this.currentIndex],
-							type: this.love !== 0 ? 'love' : 'dislike'
-						})
 						this.currentIndex++
 						this.x.move = 0
 						this.y.move = 0

+ 51 - 0
js_sdk/tencentcloud-plugin-cos/choose-and-upload-image.js

@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import uploadFile from './upload-file';
+
+/**
+ * 选择并上传图片到腾讯云COS(需要调用云函数签名,请先配置好云函数)
+ * @async
+ * @return {Promise<string>} 返回成功上传到COS上的文件名称
+ */
+export default async function chooseAndUploadImage() {
+  let [error, res] = await uni.chooseImage({
+    count: 1,
+  });
+  if (error) {
+    throw error;
+  }
+  // #ifdef H5
+  if (!/^image/.test(res.tempFiles[0].type)) {
+    throw new Error('文件类型错误');
+  }
+  // #endif
+  uni.showLoading({
+    mask: true,
+  });
+  try {
+    let file = res.tempFilePaths[0];
+    // #ifdef H5
+    file = res.tempFiles[0];
+    // #endif
+    const key = await uploadFile(file);
+    return key;
+  } catch (error) {
+    throw error;
+  } finally {
+    uni.hideLoading();
+  }
+};

+ 49 - 0
js_sdk/tencentcloud-plugin-cos/choose-and-upload-video.js

@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import uploadFile from './upload-file';
+
+/**
+ * 选择并上传视频到腾讯云COS(需要调用云函数签名,请先配置好云函数)
+ * @async
+ * @return {Promise<string>} 返回成功上传到COS上的文件名称
+ */
+export default async function chooseAndUploadVideo() {
+  let [error, res] = await uni.chooseVideo();
+  if (error) {
+    throw error;
+  }
+  // #ifdef H5
+  if (!/^video/.test(res.tempFile.type)) {
+    throw new Error('文件类型错误');
+  }
+  // #endif
+  uni.showLoading({
+    mask: true,
+  });
+  try {
+    let file = res.tempFilePath;
+    // #ifdef H5
+    file = res.tempFile;
+    // #endif
+    const key = await uploadFile(file);
+    return key;
+  } catch (error) {
+    throw error;
+  } finally {
+    uni.hideLoading();
+  }
+};

+ 45 - 0
js_sdk/tencentcloud-plugin-cos/choose-file.js

@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// #ifdef H5
+const inputElement = document.createElement('input');
+inputElement.type = 'file';
+// #endif
+
+/**
+ * 选择文件 (仅支持在H5平台运行)
+ * @async
+ * @param {string} accept - 允许选择的文件类型,比如'image/*'代表只能选择图片,'image/png'代表只能选择PNG图片等,详情请查阅input元素accept属性的相关文档
+ * @param {boolean} mutiple - 是否允许选择多个文件
+ * @return {Promise<File[]>} 返回用户选择的文件列表
+ */
+export default function chooseFile(accept, mutiple) {
+  return new Promise(async (resolve, reject) => {
+    // #ifdef H5
+    inputElement.accept = accept || '';
+    inputElement.multiple = !!mutiple;
+    inputElement.onchange = () => {
+      const files = [...inputElement.files];
+      inputElement.value = '';
+      resolve(files);
+    };
+    inputElement.click();
+    // #endif
+    // #ifndef H5
+    reject(new Error('此方法仅支持在H5平台调用'));
+    // #endif
+  });
+};

+ 33 - 0
js_sdk/tencentcloud-plugin-cos/get-file-access-url.js

@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * 获取腾讯云COS私有读文件的访问地址(需要调用云函数签名,请先配置好云函数)
+ * @async
+ * @param {string} key - 腾讯云COS上的文件名称
+ * @return {Promise<string>} 腾讯云COS文件key的访问地址
+ */
+export default async function getFileAccessUrl(key) {
+  const { result } = await uniCloud.callFunction({
+    name: 'tencentcloud-plugin',
+    data: {
+      module: 'COS',
+      action: 'getObjectURL',
+      key,
+    },
+  });
+  return result;
+};

+ 101 - 0
js_sdk/tencentcloud-plugin-cos/upload-file.js

@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import UUID from 'uuid';
+import { getMediaType } from './utils';
+
+/**
+ * 上传文件到腾讯云COS(需要调用云函数签名,请先配置好云函数)
+ * @async
+ * @param {string|File} file - 需要上传的文件,小程序环境选择的filePath或H5环境选择的File对象
+ * @param {string?} key - 存储在COS上的文件名称,如果不传,则会生成uuid代替
+ * @param {function?} onProgressUpdate - 上传进度回调,回调参数详见uni.uploadFile API
+ * @return {Promise<string>} 返回成功上传到COS上的文件名称
+ */
+export default async function uploadFile(file, key, onProgressUpdate) {
+  if (!file) {
+    throw new Error('file不能为空');
+  }
+  // 获取签名信息
+  const { result } = await uniCloud.callFunction({
+    name: 'tencentcloud-plugin',
+    data: {
+      module: 'COS',
+      action: 'signPostObjectAPI',
+    },
+  });
+  const signData = result;
+  return new Promise((resolve, reject) => {
+    let filePath = undefined;
+    let fileExt;
+    if (typeof file === 'string') {
+      filePath = file;
+      file = undefined;
+      fileExt = filePath.split('?')[0].split('.').pop();
+    } else {
+      fileExt = file.name.split('.').pop();
+    }
+    // 支付宝小程序上传文件API必传fileType
+    // 通过支付宝小程序环境下选择文件的时候,通过返回的filePath取得的扩展名可能是image/video/audio
+    let fileType = undefined;
+    // ifdef MP-ALIPAY
+    if (fileExt === 'image') {
+      fileExt = 'jpg';
+    } else if (fileExt === 'video') {
+      fileExt = 'mp4';
+    } else if (fileExt === 'audio') {
+      fileExt = 'mp3';
+    }
+    fileType = getMediaType(fileExt);
+    // endif
+    if (!key) {
+      key = `${UUID.v1()}.${fileExt}`;
+    }
+    const uploadTask = uni.uploadFile({
+      url: signData.host,
+      file,
+      filePath,
+      fileType,
+      name: 'file',
+      formData: {
+        key,
+        'q-sign-algorithm': signData.signAlgorithm,
+        'q-ak': signData.ak,
+        'q-key-time': signData.keyTime,
+        'q-signature': signData.signature,
+        'policy': signData.policy
+      },
+      success(response) {
+        if (response.statusCode !== 204) {
+          reject(new Error('文件上传失败'));
+        } else {
+          resolve(key);
+        }
+      },
+      complete(r){
+      },
+      fail(error) {
+        // 支付宝小程序环境下会将返回的204状态码识别为异常而触发fail回调,实际上是上传成功了的
+        if (error.statusCode === 204) {
+          resolve(key);
+        } else {
+          reject(error);
+        }
+      }
+    });
+    onProgressUpdate && uploadTask.onProgressUpdate(onProgressUpdate);
+  });
+};

+ 33 - 0
js_sdk/tencentcloud-plugin-cos/utils.js

@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * 通过文件扩展名获取文件多媒体类型
+ * @param {string} ext - 文件扩展名,不包含"."字符
+ * @return {string} 多媒体类型image/video/audio/other
+ */
+export function getMediaType(ext) {
+  if (/^(jpg|jpeg|png|gif|bmp|webp)$/.test(ext.toLowerCase())) {
+    return 'image';
+  }
+  if (/^(mp4|mpeg|qsv|mov|avi|3gp)$/.test(ext.toLowerCase())) {
+    return 'video';
+  }
+  if (/^(mp3|wma|wav|m4a)$/.test(ext.toLowerCase())) {
+    return 'audio';
+  }
+  return 'other';
+}

+ 20 - 12
manifest.json

@@ -2,7 +2,7 @@
     "name" : "Icoco",
     "appid" : "__UNI__1B420CB",
     "description" : "",
-    "versionName" : "1.0.21",
+    "versionName" : "1.0.24",
     "versionCode" : 101,
     "transformPx" : false,
     /* 5+App特有相关 */
@@ -60,7 +60,12 @@
             /* SDK配置 */
             "sdkConfigs" : {
                 "ad" : {},
-                "maps" : {},
+                "maps" : {
+                    "baidu" : {
+                        "appkey_ios" : "",
+                        "appkey_android" : ""
+                    }
+                },
                 "geolocation" : {},
                 "oauth" : {
                     "univerify" : {},
@@ -129,21 +134,24 @@
         "devServer" : {
             "https" : false,
             "proxy" : {
-                "/utility" : {
-                    "target" : "https://icoco.tech/admin",
+                "/admin" : {
+                    "target" : "https://icoco.tech/",
                     "changeOrigin" : true,
-                    "secure" : false,
-                    "pathRewrite" : {
-                        "^/utility" : "utility"
-                    }
+                    "secure" : false
                 },
                 "/auth" : {
                     "target" : "https://icoco.tech/",
                     "changeOrigin" : true,
-                    "secure" : false,
-                    "pathRewrite" : {
-                        "^/auth" : "auth"
-                    }
+                    "secure" : false
+                }
+            }
+        },
+        "sdkConfigs" : {
+            "maps" : {
+                "amap" : {
+                    "key" : "40bff53b893ac5ccb05e3f018f7315f7",
+                    "securityJsCode" : "1bb5f515ea82f48a6b3ea37a3f88fa21",
+                    "serviceHost" : ""
                 }
             }
         }

+ 6 - 195
package-lock.json

@@ -1,200 +1,6 @@
 {
-  "name": "lcoco",
-  "lockfileVersion": 2,
   "requires": true,
-  "packages": {
-    "": {
-      "dependencies": {
-        "animate.css": "^4.1.1",
-        "cos-js-sdk-v5": "^1.4.18",
-        "inobounce": "^0.2.1",
-        "jquery": "^3.6.4",
-        "uni-read-pages": "^1.0.5",
-        "uni-simple-router": "^2.0.7",
-        "uploading-oss": "^1.0.3"
-      },
-      "devDependencies": {
-        "@types/html5plus": "^1.0.2",
-        "@types/uni-app": "^1.4.4",
-        "vue-waterfall-easy": "^2.4.4"
-      }
-    },
-    "node_modules/@babel/parser": {
-      "version": "7.19.0",
-      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.19.0.tgz",
-      "integrity": "sha512-74bEXKX2h+8rrfQUfsBfuZZHzsEs6Eql4pqy/T4Nn6Y9wNPggQOqD6z6pn5Bl8ZfysKouFZT/UXEH94ummEeQw==",
-      "dev": true,
-      "bin": {
-        "parser": "bin/babel-parser.js"
-      },
-      "engines": {
-        "node": ">=6.0.0"
-      }
-    },
-    "node_modules/@types/html5plus": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/@types/html5plus/-/html5plus-1.0.2.tgz",
-      "integrity": "sha512-OklP5lrmLq8/6TUOLgWc0LndUVvAiTWX5dnyoCFhIUtFW9opWsnCtG/UxPgeuC28Rv2XNbFfft/hEEI39P/4Ag==",
-      "dev": true
-    },
-    "node_modules/@types/uni-app": {
-      "version": "1.4.4",
-      "resolved": "https://registry.npmjs.org/@types/uni-app/-/uni-app-1.4.4.tgz",
-      "integrity": "sha512-ZTXnrCTblZyoLIoKbTv1Whz1nxrTcM7vg0qGXzDpXP8m9MqdjKt48N3FffQT1IsJWNkxbvJ1Eg5UHDaq+k+oBQ==",
-      "dev": true,
-      "dependencies": {
-        "vue": "^2.6.8"
-      }
-    },
-    "node_modules/@vue/compiler-sfc": {
-      "version": "2.7.10",
-      "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.10.tgz",
-      "integrity": "sha512-55Shns6WPxlYsz4WX7q9ZJBL77sKE1ZAYNYStLs6GbhIOMrNtjMvzcob6gu3cGlfpCR4bT7NXgyJ3tly2+Hx8Q==",
-      "dev": true,
-      "dependencies": {
-        "@babel/parser": "^7.18.4",
-        "postcss": "^8.4.14",
-        "source-map": "^0.6.1"
-      }
-    },
-    "node_modules/@xmldom/xmldom": {
-      "version": "0.8.8",
-      "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.8.tgz",
-      "integrity": "sha512-0LNz4EY8B/8xXY86wMrQ4tz6zEHZv9ehFMJPm8u2gq5lQ71cfRKdaKyxfJAx5aUoyzx0qzgURblTisPGgz3d+Q==",
-      "engines": {
-        "node": ">=10.0.0"
-      }
-    },
-    "node_modules/animate.css": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/animate.css/-/animate.css-4.1.1.tgz",
-      "integrity": "sha512-+mRmCTv6SbCmtYJCN4faJMNFVNN5EuCTTprDTAo7YzIGji2KADmakjVA3+8mVDkZ2Bf09vayB35lSQIex2+QaQ=="
-    },
-    "node_modules/cos-js-sdk-v5": {
-      "version": "1.4.18",
-      "resolved": "https://registry.npmjs.org/cos-js-sdk-v5/-/cos-js-sdk-v5-1.4.18.tgz",
-      "integrity": "sha512-vTy8p59qnEoNJH/1chBSU8lSJDEcKV5PeOlAngmPbmHEwoMNJmKmA71nnquGIWHI4KpR4n57yC+RKP6RZkgsBg==",
-      "dependencies": {
-        "@xmldom/xmldom": "^0.8.6"
-      }
-    },
-    "node_modules/crypto-js": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz",
-      "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw=="
-    },
-    "node_modules/csstype": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
-      "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==",
-      "dev": true
-    },
-    "node_modules/inobounce": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/inobounce/-/inobounce-0.2.1.tgz",
-      "integrity": "sha512-dmKhRDbUS3zGD8HDGchsZBuxaXnfFM+2jXrZpnEnBToEWCgcs3lBfCQe0wzkbpIoJwU/lufaMquSyWoX8OXTRw=="
-    },
-    "node_modules/jquery": {
-      "version": "3.6.4",
-      "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.4.tgz",
-      "integrity": "sha512-v28EW9DWDFpzcD9O5iyJXg3R3+q+mET5JhnjJzQUZMHOv67bpSIHq81GEYpPNZHG+XXHsfSme3nxp/hndKEcsQ=="
-    },
-    "node_modules/js-base64": {
-      "version": "2.6.4",
-      "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz",
-      "integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ=="
-    },
-    "node_modules/nanoid": {
-      "version": "3.3.4",
-      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
-      "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
-      "dev": true,
-      "bin": {
-        "nanoid": "bin/nanoid.cjs"
-      },
-      "engines": {
-        "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
-      }
-    },
-    "node_modules/picocolors": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
-      "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
-      "dev": true
-    },
-    "node_modules/postcss": {
-      "version": "8.4.16",
-      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.16.tgz",
-      "integrity": "sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==",
-      "dev": true,
-      "dependencies": {
-        "nanoid": "^3.3.4",
-        "picocolors": "^1.0.0",
-        "source-map-js": "^1.0.2"
-      },
-      "engines": {
-        "node": "^10 || ^12 || >=14"
-      }
-    },
-    "node_modules/source-map": {
-      "version": "0.6.1",
-      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-      "dev": true,
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/source-map-js": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
-      "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
-      "dev": true,
-      "engines": {
-        "node": ">=0.10.0"
-      }
-    },
-    "node_modules/uni-read-pages": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmmirror.com/uni-read-pages/-/uni-read-pages-1.0.5.tgz",
-      "integrity": "sha512-GkrrZ0LX0vn9R5k6RKEi0Ez3Q3e2vUpjXQ8Z6/K/d28KudI9ajqgt8WEjQFlG5EPm1K6uTArN8LlqmZTEixDUA==",
-      "hasInstallScript": true
-    },
-    "node_modules/uni-simple-router": {
-      "version": "2.0.7",
-      "resolved": "https://registry.npmmirror.com/uni-simple-router/-/uni-simple-router-2.0.7.tgz",
-      "integrity": "sha512-8FKv5dw7Eoonm0gkO8udprrxzin0fNUI0+AvIphFkFRH5ZmP5ZWJ2pvnWzb2NiiqQSECTSU5VSB7HhvOSwD5eA=="
-    },
-    "node_modules/uploading-oss": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/uploading-oss/-/uploading-oss-1.0.3.tgz",
-      "integrity": "sha512-aqHh5NCOBcrA4d8yCIKR7B9GrKzH88X7gL8BSvIw18pi79AgWnhDkCoyjQmDqzTvjQqYfKwyFTEgnafzMx/GbQ==",
-      "dependencies": {
-        "crypto-js": "^4.1.1",
-        "js-base64": "^2.6.4"
-      }
-    },
-    "node_modules/vue": {
-      "version": "2.7.10",
-      "resolved": "https://registry.npmjs.org/vue/-/vue-2.7.10.tgz",
-      "integrity": "sha512-HmFC70qarSHPXcKtW8U8fgIkF6JGvjEmDiVInTkKZP0gIlEPhlVlcJJLkdGIDiNkIeA2zJPQTWJUI4iWe+AVfg==",
-      "dev": true,
-      "dependencies": {
-        "@vue/compiler-sfc": "2.7.10",
-        "csstype": "^3.1.0"
-      }
-    },
-    "node_modules/vue-waterfall-easy": {
-      "version": "2.4.4",
-      "resolved": "https://registry.npmjs.org/vue-waterfall-easy/-/vue-waterfall-easy-2.4.4.tgz",
-      "integrity": "sha512-5OkpT2FPNC3rHBy858zk/nmJxqdPaGmj/KVbmA6dgcvtsovKMa+zuf/Z7F+S2NnObeavpIBztTWgcH3S42ZD+g==",
-      "dev": true,
-      "engines": {
-        "node": ">= 6.0.0",
-        "npm": ">= 3.0.0"
-      }
-    }
-  },
+  "lockfileVersion": 1,
   "dependencies": {
     "@babel/parser": {
       "version": "7.19.0",
@@ -246,6 +52,11 @@
         "@xmldom/xmldom": "^0.8.6"
       }
     },
+    "crypto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz",
+      "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig=="
+    },
     "crypto-js": {
       "version": "4.1.1",
       "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz",

+ 1 - 0
package.json

@@ -2,6 +2,7 @@
   "dependencies": {
     "animate.css": "^4.1.1",
     "cos-js-sdk-v5": "^1.4.18",
+    "crypto": "^1.0.1",
     "inobounce": "^0.2.1",
     "jquery": "^3.6.4",
     "uni-read-pages": "^1.0.5",

+ 1 - 5
pages.json

@@ -448,10 +448,6 @@
 	},
 	"condition": {
 		"current": 0,
-		"list": [{
-			"name": "列表调试",
-			"path": "/pages/perfect/cards",
-			"query": ""
-		}]
+		"list": []
 	}
 }

+ 63 - 33
pages/common/dynamic/dynamic-items.vue

@@ -19,12 +19,13 @@
           </view>
       </view>
     </view>
+    {{list.length}}
     <view class="dynamic-item" v-for="(item,itemIndex) in list">
       <view class="dynamic-title">
-        <view class="dynamic-title-left">
-          <text class="dynamic-title-text  sys-weight-600" :style="{'color':textColor}">12</text>
-          <text class="dynamic-title-text  sys-weight-400" :style="{'color':textColor}">04月</text>
-          <text class="dynamic-title-text  sys-weight-400" :style="{'color':textColor}">2020年</text>
+        <view class="dynamic-title-left" v-if="item.dateArr.length>0">
+          <text class="dynamic-title-text  sys-weight-600" :style="{'color':textColor}">{{item.dateArr[2]}}</text>
+          <text class="dynamic-title-text  sys-weight-400" :style="{'color':textColor}">{{item.dateArr[1]}}月</text>
+          <text class="dynamic-title-text  sys-weight-400" :style="{'color':textColor}">{{item.dateArr[0]}}年</text>
         </view>
         <view class="dynamic-title-right">
           <image  class="dynamic-right-img"   mode="aspectFill" src="/static/img/index/dynamic-all.png" ></image>
@@ -32,22 +33,22 @@
       </view>
       <view class="dynamic-data" :style="{'border-left':' 1rpx solid '+divisionColor}">
         <view class="data-text">
-          <text class="text-item" :style="{'color':textColor}">{{item.text}}</text>
+          <text class="text-item" :style="{'color':textColor}">{{item.content}}</text>
         </view>
 
-        <view class="data-img" v-if="item.img.length>0">
-          <view class="one-img" v-if="item.img.length===1">
-            <img-one :file-list="item.img"></img-one>
+        <view class="data-img" v-if="item.galleryUrls">
+          <view class="one-img" v-if="item.galleryUrls.length===1">
+            <img-one :file-list="item.galleryUrls"></img-one>
           </view>
-          <view class="two-img" v-else-if="item.img.length===2 || item.img.length===4">
-            <img-two :file-list="item.img"></img-two>
+          <view class="two-img" v-else-if="item.galleryUrls.length===2 || item.galleryUrls.length===4">
+            <img-two :file-list="item.galleryUrls"></img-two>
           </view>
           <view class="three-img" v-else>
-            <img-three :file-list="item.img"></img-three>
+            <img-three :file-list="item.galleryUrls"></img-three>
           </view>
 
         </view>
-        <view class="data-tag-list" 	>
+        <view class="data-tag-list" v-if="false"	>
           <dynamic-tag :list="item.tag" :tag-bg="tagBg" :tag-color="tagColor"></dynamic-tag>
 
         </view>
@@ -55,15 +56,15 @@
           <view class="operation-item" @click="setLike(itemIndex)">
 <!--            <image  class="operation-img"   mode="aspectFill" :src="'/static/img/index/like-'+(item.isLike?'ok':'no')+'.png'" ></image>-->
             <view class="operation-icon">
-              <text class="iconfont icon-dianzan" v-if="!item.isLike"  :style="{'color':operateColor}"> &#xe8ad;</text>
+              <text class="iconfont icon-dianzan" v-if="!item.liked"  :style="{'color':operateColor}"> &#xe8ad;</text>
               <text class="iconfont icon-dianzan1" v-else  :style="{'color':'#ED301D'}"> &#xe8c3;</text>
             </view>
-            <view class="operation-text sys-weight-400" :style="{'color':operateColor}">{{item.likeNum>0?item.likeNum:'点赞'}}</view>
+            <view class="operation-text sys-weight-400" :style="{'color':operateColor}">{{item.likeCount>0?item.likeCount:'点赞'}}</view>
           </view>
           <view class="operation-item">
 <!--            <image  class="operation-img"   mode="aspectFill" src="/static/img/index/evaluate.png" ></image>-->
             <view class="operation-icon"><text class="iconfont icon-pinglun"  :style="{'color':operateColor}">&#xe891;</text></view>
-            <view class="operation-text sys-weight-400" :style="{'color':operateColor}">{{item.evaluateNum>0?item.evaluateNum:'评论'}}</view>
+            <view class="operation-text sys-weight-400" :style="{'color':operateColor}">{{item.commentCount>0?item.commentCount:'评论'}}</view>
           </view>
         </view>
 
@@ -78,10 +79,15 @@ import ImgOne from "@/pages/common/img/img-one";
 import ImgTwo from "@/pages/common/img/img-two";
 import ImgThree from "@/pages/common/img/img-three";
 import DynamicTag from "@/pages/common/tag/dynamic-tag";
+import {getMoments} from "@/api/discovery";
+import tools from "@/service/tools";
 export default {
   name: "dynamic-items",
   components: {DynamicTag, ImgThree, ImgTwo, ImgOne},
   props: {
+    userId:{
+      default:0
+    },
       textColor:{
         type:String,
         default:'#fff'
@@ -109,31 +115,55 @@ export default {
   },
   data() {
     return {
-      list:[
-        {'text':'我是阳光大男孩我','img':['/static/img/temporary/1.png'],'tag':[{'text':'南亭新都会商场','icon':'1'},{'text':'你好认识一哈','icon':'2'}],'likeNum':1,'evaluateNum':0,'isLike':true},
-        {'text':'我是阳光大男孩我','img':['/static/img/temporary/1.png','/static/img/temporary/2.png','/static/img/temporary/3.png','/static/img/temporary/2.png'],'tag':[{'text':'南亭新都会商场','icon':'1'},{'text':'你好认识一哈','icon':'2'}],'likeNum':0,'evaluateNum':20,'isLike':false},
-        {'text':'我是阳光大男孩我','img':[
-            '/static/img/temporary/1.png',
-            '/static/img/temporary/3.png',
-            '/static/img/temporary/2.png',
-            '/static/img/temporary/1.png',
-            '/static/img/temporary/3.png',
-            '/static/img/temporary/2.png',
-          ],'tag':[{'text':'南亭新都会商场','icon':'1'},{'text':'你好认识一哈','icon':'2'}],'likeNum':10,'evaluateNum':20,'isLike':true},
-      ]
+      list:[],
+      isAjax:false,
+      total:undefined,
+      page:1,
+    }
+  },
+  watch: {
+    'userId':function () {
+      this.startList()
     }
   },
-  watch: {},
   mounted() {
-
+    this.startList()
   },
   methods: {
+    startList(){
+      if(this.userId<=0){
+        // return false
+      }
+      this.list=[]
+      this.isAjax=false
+      this.total=undefined
+      this.page=1
+      this.getMoments()
+    },
+    getMoments(){
+      if(this.isAjax){
+        return
+      }
+      this.isAjax=true
+      let that=this
+      getMoments({'userId':this.userId,'pageNo':this.page,'pageSize':20}).then((res)=>{
+        this.isAjax=false
+        if(res.code===0){
+          res.data.data.forEach((item)=>{
+            item.dateArr=tools.getDateArr(item.createdAt)
+            that.list.push(item )
+          })
+          ++that.page
+          that.total=res.data.total
+        }
+      })
+    },
     setLike(index){
-      this.list[index].isLike=!this.list[index].isLike
-      if(this.list[index].isLike){
-        ++this.list[index].likeNum
+      this.dynamicList[index].liked=!this.dynamicList[index].liked
+      if(this.dynamicListdynamicList[index].liked){
+        ++this.dynamicList[index].likeCount
       }else {
-        --this.list[index].likeNum
+        --this.dynamicList[index].likeCount
       }
     }
   }

+ 1 - 2
pages/common/img/img-one.vue

@@ -1,6 +1,6 @@
 <template>
   <view class="img-box">
-      <image :src="fileItem" @click="previewImage(fileIndex)" v-for="(fileItem,fileIndex) in fileList" mode="widthFix"></image>
+      <image :src="fileItem" @click.stop="previewImage(fileIndex)" v-for="(fileItem,fileIndex) in fileList" mode="widthFix"></image>
   </view>
 </template>
 
@@ -22,7 +22,6 @@ export default {
   },
   methods: {
     previewImage(index){
-      return;
       uni.previewImage({
         urls: this.fileList,
         current:index

+ 1 - 2
pages/common/img/img-three.vue

@@ -1,7 +1,7 @@
 <template>
   <view class="img-box">
     <view class="img-item" v-for="(fileItem,fileIndex) in fileList">
-      <image :src="fileItem" @click="previewImage(fileIndex)" mode="aspectFill"></image>
+      <image :src="fileItem" @click.stop="previewImage(fileIndex)" mode="aspectFill"></image>
     </view>
   </view>
 </template>
@@ -24,7 +24,6 @@ export default {
   },
   methods: {
     previewImage(index){
-      return;
       uni.previewImage({
         urls: this.fileList,
         current:index

+ 1 - 2
pages/common/img/img-two.vue

@@ -1,7 +1,7 @@
 <template>
   <view class="img-box">
     <view class="img-item" v-for="(fileItem,fileIndex) in fileList">
-      <image @click="previewImage(fileIndex)" :src="fileItem"  mode="aspectFill"></image>
+      <image @click.stop="previewImage(fileIndex)" :src="fileItem"  mode="aspectFill"></image>
     </view>
   </view>
 </template>
@@ -24,7 +24,6 @@ export default {
   },
   methods: {
     previewImage(index){
-      return;
       uni.previewImage({
         urls: this.fileList,
         current:index

+ 14 - 2
pages/index/model/attachment-list.vue

@@ -28,13 +28,19 @@
 </template>
 
 <script>
+import {getNearbyList} from "@/api/discovery";
+import tools from "@/service/tools";
+
 export default {
   name: "attachment-list",
   components: {},
   props: {},
   data() {
     return {
-      list:[]
+      page:1,
+      total:undefined,
+      isAjax:false,
+      list:[],
     }
   },
   watch: {},
@@ -42,7 +48,13 @@ export default {
     this.getAttachmentList()
   },
   methods: {
-    getAttachmentList(){
+    async  getAttachmentList(){
+      let locationData=await tools.getLocation()
+      getNearbyList({'pageNo':this.page,'pageSize':20,'longitude':locationData.longitude,'latitude':locationData.latitude}).then((res)=>{
+        if(res.code===0){
+
+        }
+      })
       let list=[
         {'name':'仰看辉煌仰看辉煌仰看辉煌仰看辉煌','age':18,'sex':'女','constellation':'处女座','img':'/static/img/temporary/1.png'},
         {'name':'Maple','age':18,'sex':'女','constellation':'处女座','img':'/static/img/temporary/2.png'},

+ 3 - 2
pages/index/model/index-nav.vue

@@ -11,7 +11,7 @@
         <view class="center-list">
           <view class="center-item sys-weight-600" @click="setTabNum(0)" :class="{'sys-color-black':tabNum===0,'sys-color-gray-9':tabNum!==0,}">发现</view>
           <view class="center-item sys-weight-600" @click="setTabNum(1)"  :class="{'sys-color-black':tabNum===1,'sys-color-gray-9':tabNum!==1,}">附近</view>
-          <view class="center-item sys-weight-600" @click="setTabNum(2)"  :class="{'sys-color-black':tabNum===2,'sys-color-gray-9':tabNum!==2,}">匹配</view>
+<!--          <view class="center-item sys-weight-600" @click="setTabNum(2)"  :class="{'sys-color-black':tabNum===2,'sys-color-gray-9':tabNum!==2,}">匹配</view>-->
         </view>
         <view class="center-bg" :style="{'left':(tabNum*122)+'rpx'}"></view>
       </view>
@@ -121,7 +121,8 @@
       }
     }
     .header-center{
-      width: 366rpx;
+      //width: 366rpx;
+      width: 244rpx;
       height: 64rpx;
       background: #EEEEEE;
       border-radius: 200rpx;

+ 72 - 35
pages/index/model/slide-item.vue

@@ -1,11 +1,13 @@
 <template>
   <view class="slide-box">
-    <tantan ref="jmList"  @onChange="change" @onClickImage="clickImage"></tantan>
+    <tantan ref="jmList"  @onChange="onChange" @onClickImage="clickImage"></tantan>
   </view>
 </template>
 
 <script>
 import tantan from '@/components/dgex-tantan/dgex-tantan.vue'
+import {getDiscoverList, getMoments, setDislike, setLike} from "@/api/discovery";
+import tools from "@/service/tools";
 export default {
   name: "slide-item",
   components: {
@@ -14,48 +16,83 @@ export default {
   props: {},
   data() {
     return {
-      list: []
+      page:1,
+      total:undefined,
+      isAjax:false,
+      list:[],
     }
   },
   watch: {},
 
   mounted() {
-
-    this.change({'currentIndex':1})
+    this.getDiscoverList()
   },
   methods: {
-    change(data) {
-      // 判断倒数
-      let arr=[]
-      // 模拟一下最加数据
-      const tu = [
-        'https://nimg.ws.126.net/?url=http%3A%2F%2Fdingyue.ws.126.net%2F2021%2F0704%2Fc7a27a1ej00qvpu700019c000hs00vlc.jpg&thumbnail=660x2147483647&quality=80&type=jpg',
-        'https://nimg.ws.126.net/?url=http%3A%2F%2Fdingyue.ws.126.net%2F2021%2F0704%2F9f81e6aaj00qvpu70001xc000hs00vmc.jpg&thumbnail=660x2147483647&quality=80&type=jpg',
-        'https://nimg.ws.126.net/?url=http%3A%2F%2Fdingyue.ws.126.net%2F2021%2F0704%2F55bf2cb3j00qvpu70002cc000hs012jc.jpg&thumbnail=660x2147483647&quality=80&type=jpg',
-        'https://nimg.ws.126.net/?url=http%3A%2F%2Fdingyue.ws.126.net%2F2021%2F0704%2F2017725bj00qvpu70001jc000hs00zxc.jpg&thumbnail=660x2147483647&quality=80&type=jpg'
-      ]
-      for (let index = 0; index < 10; index++) {
-        const n = Math.floor(Math.random() * (tu.length - 1))
-        let newdata = {
-          image: tu[n],
-          images: [
-            'https://nimg.ws.126.net/?url=http%3A%2F%2Fdingyue.ws.126.net%2F2021%2F0704%2Fc7a27a1ej00qvpu700019c000hs00vlc.jpg&thumbnail=660x2147483647&quality=80&type=jpg',
-            'https://nimg.ws.126.net/?url=http%3A%2F%2Fdingyue.ws.126.net%2F2021%2F0704%2F9f81e6aaj00qvpu70001xc000hs00vmc.jpg&thumbnail=660x2147483647&quality=80&type=jpg',
-            'https://nimg.ws.126.net/?url=http%3A%2F%2Fdingyue.ws.126.net%2F2021%2F0704%2F55bf2cb3j00qvpu70002cc000hs012jc.jpg&thumbnail=660x2147483647&quality=80&type=jpg',
-            'https://nimg.ws.126.net/?url=http%3A%2F%2Fdingyue.ws.126.net%2F2021%2F0704%2F2017725bj00qvpu70001jc000hs00zxc.jpg&thumbnail=660x2147483647&quality=80&type=jpg'
-          ],
-          imgIndex:0,
-          title: '你好',
-          age: '19',
-          distance: '19.6',
-          city: '香港九龙城',
-          weChat: 'siococos',
-          animation:{}
-        }
-        arr.push(newdata)
+    onChange(data){
+      if(this.type ==='love'){
+        //选择了喜欢
+        this.setLike(data.currentItem.userId)
+      }else {
+        //选择了不喜欢
+        this.setDislike(data.currentItem.userId)
+      }
+      if(this.list.length<data.currentIndex+5){
+        this.getDiscoverList()
       }
-      this.$refs.jmList.setData(arr)
-      // console.log(data);
+    },
+    setLike(userId){
+      setLike(userId).then((res)=>{
+        console.log(res)
+      })
+    },
+    setDislike(userId){
+      setDislike(userId).then((res)=>{
+        console.log(res)
+      })
+    },
+    getDiscoverList(){
+      if(this.isAjax){
+        return
+      }
+      this.isAjax=true
+      getDiscoverList({'count':10}).then((res)=>{
+        this.isAjax=false
+        if(res.code===0){
+          this.list=res
+          let arr=[]
+          res.data.forEach( (item)=>{
+            let galleryUrls=item.galleryUrls
+            let age=tools.getAge(item.dateOfBirth)
+            if(galleryUrls.length>0){
+              if(galleryUrls.length>8){
+                galleryUrls=galleryUrls.slice(0,7)
+              }
+              let newdata = {
+                image: galleryUrls[0],
+                images: galleryUrls,
+                imgIndex:0,
+                title:  item.nickname,
+                userId:  item.userId,
+                age:  age,
+                online:  item.online,
+                longitude:  item.longitude,
+                latitude:  item.latitude,
+                distance: 0,
+                city:  item.province+' '+item.city,
+                weChat:  item.wechatId,
+                likeCount:  item.likeCount,
+                tagList:  item.tagList,
+                slogan:  item.slogan,
+                verified:  item.verified,
+                animation:{}
+              }
+              arr.push(newdata)
+            }
+          })
+          this.$refs.jmList.setData(arr)
+        }
+
+      })
     },
     clickImage(data) {
       console.log(data);

+ 24 - 9
pages/index/model/user-content.vue

@@ -4,14 +4,14 @@
       <view class="top-left">“</view>
       <view class="top-right">
         <image  class="right-img"   mode="aspectFill" src="/static/img/index/like.png" ></image>
-        <view class="right-text sys-color-white sys-weight-400">108喜欢</view>
+        <view class="right-text sys-color-white sys-weight-400">{{userItem.likeCount}}喜欢</view>
       </view>
     </view>
-    <view class="user-signature sys-color-white sys-weight-400">崇尚自由,热爱生活,足以!</view>
-    <view class="user-tag">
-      <view class="tag-item" v-for="i in 5">
+    <view class="user-signature sys-color-white sys-weight-400">{{userItem.slogan}}</view>
+    <view class="user-tag" v-if="userItem.tagList.length>0">
+      <view class="tag-item" v-for="i in userItem.tagList">
         <image  class="tag-img"   mode="aspectFill" src="/static/img/index/tag-img.png" ></image>
-        <view class="tag-text sys-color-gray-3 sys-weight-400">认识朋友</view>
+        <view class="tag-text sys-color-gray-3 sys-weight-400">{{i}}</view>
       </view>
     </view>
     <view class="user-wire"></view>
@@ -37,7 +37,7 @@
     </view>
 
     <view class="dynamic-list">
-      <dynamic-item ></dynamic-item>
+      <dynamic-item :user-id="userItem.userId"></dynamic-item>
 
     </view>
 
@@ -49,10 +49,19 @@ import ImgOne from "@/pages/common/img/img-one";
 import ImgTwo from "@/pages/common/img/img-two";
 import ImgThree from "@/pages/common/img/img-three";
 import DynamicItem from "@/pages/common/dynamic/dynamic-items";
+import {getMoments} from "@/api/discovery";
 export default {
   name: "user-content",
   components: {DynamicItem, ImgThree, ImgTwo, ImgOne},
-  props: {},
+  props: {
+    'userItem':{
+      default:{
+        'userId':0,
+        'likeCount':0,
+        'tagList':[],
+      }
+    }
+  },
   data() {
     return {
       giftList:[
@@ -64,13 +73,19 @@ export default {
         {'num':0,'url':'/static/img/temporary/gift3.png'},
       ],
 
+
     }
   },
-  watch: {},
-  mounted() {
+  watch: {
+    'userId':function () {
 
+    }
+  },
+  mounted() {
+    console.log()
   },
   methods: {
+
     stopTouche(){
       // console.log('滑动事件阻止触发')
       return false

+ 1 - 0
pages/login/index.vue

@@ -34,6 +34,7 @@ export default {
     // #ifdef H5
     this.showContent=true
     //#endif
+
   },
   onShow() {
     // 当app切到后台再切回前台的时候会触发onShow这个时候视频应该继续播放,不这样做视频会暂停的

+ 2 - 3
pages/login/localPhoneLogin.vue

@@ -147,12 +147,11 @@ export default {
       //#endif
     },
     oneClickLogin(mobile){
-      mobile='13900139001'
       login({'mobile':mobile,'grant_type':'mobile','scope':'server'}).then((res)=>{
         tools.setLoginData(res)
         if(res.isNewUser){
-          uni.navigateTo({
-            'url':'/pages/perfect/sexAndAge'
+          uni.switchTab({
+            'url':'/pages/index/index'
           })
         }else {
           setTimeout(()=>{

+ 28 - 9
pages/login/otherPhoneLogin.vue

@@ -34,7 +34,7 @@
 import EnNav from "@/components/en-utils/en-nav/en-nav";
 import tools from "@/service/tools";
 import OtherLogin from "@/pages/login/model/otherLogin";
-import {forgetPassword} from "@/api/login";
+import {forgetPassword, login} from "@/api/login";
 export default {
   name:'otherPhoneLogin',
   components: {OtherLogin, EnNav},
@@ -72,19 +72,38 @@ export default {
         tools.vibrate()
         return false
       }
-      forgetPassword({'mobile':this.phone,'code':this.code}).then((res)=>{
-        if(res.code===0){
-          tools.success('登陆成功')
+      this.oneClickLogin(this.phone)
+      // forgetPassword({'mobile':this.phone,'code':this.code}).then((res)=>{
+      //   if(res.code===0){
+      //     tools.success('登陆成功')
+      //     setTimeout(()=>{
+      //       uni.switchTab({
+      //         'url':'/pages/index/index'
+      //       })
+      //     },1500)
+      //   }else {
+      //     tools.error(res.msg)
+      //   }
+      // })
+
+    },
+    oneClickLogin(mobile){
+      login({'mobile':mobile,'grant_type':'mobile','scope':'server'}).then((res)=>{
+        tools.setLoginData(res)
+        if(res.isNewUser){
+          uni.switchTab({
+            'url':'/pages/index/index'
+          })
+        }else {
           setTimeout(()=>{
-            uni.switchTab({
-              'url':'/pages/index/index'
+            uni.navigateTo({
+              'url':'/pages/perfect/sexAndAge'
             })
           },1500)
-        }else {
-          tools.error(res.msg)
         }
+      }).catch((e)=>{
+        console.log(e)
       })
-
     },
     setShake(type){
       if(type===1){

+ 8 - 4
pages/perfect/cards.vue

@@ -91,23 +91,27 @@ export default {
       this.verifyIsOK()
     }
   },
+  mounted() {
+    tools.hideLoading()
+  },
   methods: {
     openChooseImage(){
       uni.chooseImage({
         count: 6, //默认9
-        sizeType: ['original'], //可以指定是原图还是压缩图,默认二者都有
+        sizeType: ['compressed '], //可以指定是原图还是压缩图,默认二者都有
         sourceType: ['album'], //从相册选择
         success:  (res) =>{
           console.log(res)
           if(res.tempFiles.length>0){
             console.log(res.tempFiles)
             res.tempFiles.forEach((item,key)=>{
-              cosServe.txUploadFile(item,'gallery').then(async (res)=>{
+              cosServe.txUploadFile(item,'gallery').then(async (imgUrl)=>{
+                console.log(res)
                 if(key===0){
-                  this.imgBg=res.Location
+                  this.imgBg=imgUrl
                   this.imgBg=await cosServe.getSignUrl(this.imgBg)
                 }
-                this.imgList.push(res.Location)
+                this.imgList.push(tools.setFileObj(imgUrl,1))
               })
             })
             console.log( this.imgList)

+ 0 - 1
pages/perfect/nickname.vue

@@ -64,7 +64,6 @@ export default {
         let gender=uni.getStorageSync('gender')
         filUserInfo({'mobile':mobile,'dateOfBirth':dateOfBirth,'gender':gender,'nickname':this.nickname,'password':this.password}).then((res)=>{
           if(res.code===0){
-            tools.setLoginData(res.data)
             uni.navigateTo({
               'url':'/pages/perfect/cards'
             })

+ 4 - 1
pages/perfect/sexAndAge.vue

@@ -67,7 +67,7 @@
 <script>
 import EnNav from "@/components/en-utils/en-nav/en-nav";
 import tools from "@/service/tools";
-import $ from 'jquery'
+
 export default {
   components: {EnNav},
   data() {
@@ -97,6 +97,9 @@ export default {
     this.mixYear=year-80
     this.maxYear=year-18
     this.placeholderYear=year-24
+  },
+  mounted() {
+
   },
   watch:{
     'age':function (){

+ 9 - 8
service/ajax.js

@@ -1,10 +1,10 @@
 import tools from "@/service/tools";
 import store from '@/store'
 let BASE_URL =''
-//#ifdef APP-NVUE
+//#ifdef APP-PLUS
 BASE_URL ='https://' + process.uniEnv.baseUrl;
 //#endif
-
+console.log(BASE_URL)
 
 /**
  * post请求封装
@@ -12,6 +12,8 @@ BASE_URL ='https://' + process.uniEnv.baseUrl;
 export const request = (url, method, data, show = false) => {
   console.log(url)
   let token=uni.getStorageSync('token')
+  let userId=uni.getStorageSync('userId')
+  data.userId=userId
   let header={
     'content-type': 'application/json',
     'hversion': '1.0',
@@ -26,27 +28,26 @@ export const request = (url, method, data, show = false) => {
     token= 'Bearer '+token
   }
   header.Authorization=token
-  console.log(header)
+  // console.log(header)
   // data.token = token;
   // data.m_id = uni.getStorageSync('m_id')
   if (show) {
     tools.showLoading();
   }
-  if (data === undefined) {
-    data = {};
-  }
+
   return new Promise((resolve, reject) => {
     // 封装主体:网络请求
-    console.log(url)
+    // console.log(BASE_URL+  url)
+    // console.log(data)
     uni.request({
       url:BASE_URL+  url,
       data: data,
       method: method || 'post',
       header: header,
       success: (res) => {
+        // console.log(res); // 控制台显示数据信息
         if (res.statusCode === 200) {
           // console.log('应答信息-----------------------')
-          // console.log(res.data); // 控制台显示数据信息
           tools.hideLoading();
           if (res.data.code * 1 === 401) {
             uni.clearStorageSync()

+ 129 - 1
service/tools.js

@@ -278,7 +278,8 @@ tools.leftClick = function () {
     }
 }
 
-tools.getDate = function () {
+tools.getDate = function (date) {
+
     let myDate = new Date();
     // let myYear = myDate.getFullYear(); //获取完整的年份(4位,1970-????)
     // let myMonth = myDate.getMonth() + 1; //获取当前月份(0-11,0代表1月)
@@ -290,6 +291,15 @@ tools.getDate = function () {
     return myDate.getFullYear() + '-' + (myDate.getMonth() + 1) + '-' + myDate.getDate()
 }
 
+tools.getDateArr = function (date) {
+
+    let myDate = new Date(date);
+    let myYear = myDate.getFullYear(); //获取完整的年份(4位,1970-????)
+    let myMonth = myDate.getMonth() + 1; //获取当前月份(0-11,0代表1月)
+    let myToday = myDate.getDate(); //获取当前日(1-31)
+    return [myYear, myMonth>9?myMonth:'0'+myMonth , myToday>9?myToday:'0'+myToday]
+}
+
 tools.getRandFileName = function (filePath)
 {
     let extIndex = filePath.lastIndexOf('.');
@@ -354,10 +364,12 @@ tools.getDateYM = ()=>{
  * @param type
  */
 tools.setLoginData = function (data) {
+    uni.setStorageSync('user_id', data.user_id)
     uni.setStorageSync('token', data.access_token)
     uni.setStorageSync('tokenType', data.token_type)
     uni.setStorageSync('refreshToken', data.access_token)
     uni.setStorageSync('mobile', data.username)
+    uni.setStorageSync('userId', data.user_id)
     tools.success('登陆成功')
     // setTimeout(() => {
     //     if (data.status * 1 === 0) {
@@ -389,10 +401,107 @@ tools.delCosToken=function (data){
     uni.removeStorageSync('cosToken')
 }
 
+/**
+ * 定位经纬度
+ * @returns {Promise<unknown>}
+ */
+tools.getLocation=async function (){
+    return new Promise((resolve, reject)=>{
+        //#ifdef APP-PLUS
+        let locationData= uni.getStorageSync('locationData')
+        console.log('locationData:',locationData)
+        if(locationData){
+            resolve(locationData);
+        }else {
+            console.log('执行获取')
+            uni.getLocation({
+                type: 'gcj02',
+                success:async function (res) {
+                    console.log('当前位置的经度:' + res.longitude);
+                    console.log('当前位置的纬度:' + res.latitude);
+                    resolve({'longitude':res.longitude,'latitude':res.latitude});
+                },
+                fail:async function (e) {
+                    console.log(e)
+                    resolve( {'longitude':"121.631691",'latitude':'38.922691'});
+                }
+            });
+        }
+        //#endif
+        //#ifdef H5
+        resolve( {'longitude':"121.631691",'latitude':'38.922691'});
+        //#endif
+    })
 
 
+}
+
+/**
+ * 计算距离
+ * @returns {Promise<void>}
+ */
+tools.getDistance=async function (lat1,  lng1){
+    let locationData=await tools.getLocation()
+    let lat2=locationData.latitude
+    let lng2=locationData.longitude
+    let radLat1 = lat1*Math.PI / 180.0;
+    let radLat2 = lat2*Math.PI / 180.0;
+    let a = radLat1 - radLat2;
+    let  b = lng1*Math.PI / 180.0 - lng2*Math.PI / 180.0;
+    let s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a/2),2) + Math.cos(radLat1)*Math.cos(radLat2)*Math.pow(Math.sin(b/2),2)));
+    s = s *6378.137 ;// EARTH_RADIUS;
+    s = Math.round(s * 10000) / 10000;
+    return s;
+}
+
+/**
+ * 查看大图
+ * @param list
+ * @param index
+ */
+tools.lookBigImg = function (list, index) {
+    uni.previewImage({
+        current: index,//当前所点击预览的图片地址
+        urls: list,//这就是当前行图片数据,注意一定要是数组格式
+        indicator: 'number',
+        loop: true
+    });
+}
+
+/**
+ * 计算年龄
+ * @param birthYearMonthDay
+ * @returns {number}
+ */
+tools.getAge=function (birthYearMonthDay) {
+    birthYearMonthDay=birthYearMonthDay.replace('-','/')
+    //birthYearMonthDay必须为"1995/6/15"这种字符串格式,不可为"2020-6-15",这种格式在Safari中会报错
+    const birthDate = new Date(birthYearMonthDay);
+    const momentDate = new Date();
+    momentDate.setHours(0, 0, 0, 0); //因为new Date()出来的时间是当前的时分秒。我们需要把时分秒重置为0。使后面时间比较更精确
+    const thisYearBirthDate = new Date(
+        momentDate.getFullYear(),
+        birthDate.getMonth(),
+        birthDate.getDate()
+    );
+    const aDate = thisYearBirthDate - birthDate;
+    const bDate = momentDate - birthDate;
+    let tempAge = momentDate.getFullYear() - birthDate.getFullYear();
+    let age = null;
+    if (bDate < aDate) {
+        tempAge = tempAge - 1;
+        age = tempAge < 0 ? 0 : tempAge;
+    } else {
+        age = tempAge;
+    }
+    return age;
+}
 
 
+/**
+ * 获取costoken
+ * @returns {{expiredTime}|any|undefined}
+ */
 tools.getCosToken=function (){
   let cosToken= uni.getStorageSync('cosToken')
     if(!cosToken){
@@ -406,4 +515,23 @@ tools.getCosToken=function (){
     }
 }
 
+/**
+ * 封装文件结构
+ * @param url
+ * @param type
+ * @returns {{serverSideEncryption: string, location, type: (string), version: string, hash: string}}
+ */
+tools.setFileObj=function (url,type){
+    url= url.replace('https://','')
+    url= url.replace('http://','')
+    url= url.replace('icoco-1317650740.cos.ap-guangzhou.myqcloud.com/','')
+    return {
+        "location":url,
+        "hash":"abc123",
+        "version":"1.0",
+        "type":type===1?"PHOTO":"V",
+        "serverSideEncryption":"AES256"
+    }
+}
+
 export default tools

+ 24 - 112
service/txOssSts.js

@@ -12,58 +12,12 @@
  * @param {area} 地区
  * @return {string|boolean} 成功返回文件地址,失败返回false
  */
-import COS from "cos-js-sdk-v5"
-import tools from "./tools";
-import {getTxySts} from "@/api/common";
-
-
-
-// 存储桶名称,由bucketname-appid 组成,appid必须填入,可以在COS控制台查看存储桶名称。 https://console.cloud.tencent.com/cos5/bucket
-let Bucket = 'icoco-1317650740';
-// 存储桶Region可以在COS控制台指定存储桶的概览页查看 https://console.cloud.tencent.com/cos5/bucket/
-// 关于地域的详情见 https://cloud.tencent.com/document/product/436/6224
-let Region = 'ap-guangzhou';
-
-let cos=null;
-function startCos(){
-    console.log('startCos-----------------')
-    cos = new COS({
-        getAuthorization: function (options, callback) {
-            console.log('---------------获取初始化信息')
-            let  cosToken=tools.getCosToken()
-            if(cosToken){
-                callback({
-                    TmpSecretId: cosToken.credentials.tmpSecretId,
-                    TmpSecretKey: cosToken.credentials.tmpSecretKey,
-                    XCosSecurityToken: cosToken.credentials.sessionToken,
-                    StartTime: cosToken.startTime*1, // 时间戳,单位秒,如:1580000000,建议返回服务器时间作为签名的开始时间,避免用户浏览器本地时间偏差过大导致签名错误
-                    ExpiredTime: cosToken.expiredTime*1, // 时间戳,单位秒,如:1580000900
-                })
-            }else {
-                getTxySts().then((res)=>{
 
-                    console.log('---------------获取初始化信息')
-                    if(res.code===0){
-                        tools.setCosToken(res.data)
-                        callback({
-                            TmpSecretId: res.data.credentials.tmpSecretId,
-                            TmpSecretKey: res.data.credentials.tmpSecretKey,
-                            XCosSecurityToken: res.data.credentials.sessionToken,
-                            StartTime: res.data.startTime*1, // 时间戳,单位秒,如:1580000000,建议返回服务器时间作为签名的开始时间,避免用户浏览器本地时间偏差过大导致签名错误
-                            ExpiredTime: res.data.expiredTime*1, // 时间戳,单位秒,如:1580000900
-                        })
-                        setTimeout(()=>{
-                            cos=null
-                            tools.delCosToken()
-                        },(res.data.expiredTime-res.data.startTime))
-                    }
-                })
-            }
 
+import getFileAccessUrl from "@/js_sdk/tencentcloud-plugin-cos/get-file-access-url";
+import tools from "./tools";
+import uploadFile from "@/js_sdk/tencentcloud-plugin-cos/upload-file";
 
-        }
-    });
-}
 let cosServe={}
 /**
  * COS文件上传
@@ -71,81 +25,39 @@ let cosServe={}
  * @param folder 上传目录
  * @returns {Promise<unknown>}
  */
-cosServe.txUploadFile=function (file,folder){
-    if(cos===null){
-        console.log('对象初始化')
-        //初始化对象
-        startCos();
-    }
+cosServe.txUploadFile=async function (file,folder){
     //默认为个人目录gallery
-    folder=folder==='moment'?'moment':'gallery'
     tools.showLoading()
-    return new Promise((resolve, reject) => {
-        console.log('cos-*-------------------------',cos)
-        console.log(file)
-        console.log(folder+'/'+tools.getDate()+tools.getRandFileName(file.name))
-        // 分片上传文件
-        cos.putObject({
-            Bucket: Bucket,
-            Region: Region,
-            Key: folder+'/'+tools.getDate()+'/'+tools.getRandFileName(file.name),
-            Body: file,
-            onHashProgress: function (progressData) {
-                console.log('校验中', JSON.stringify(progressData));
-            },
-            onProgress: function (progressData) {
-                console.log('上传中', JSON.stringify(progressData));
-            },
-        }, function (err, data) {
-            tools.hideLoading()
-            console.log(err);
-            console.log(data);
-            if(data.Location!==undefined){
-                data.Location='https://'+data.Location;
-                resolve( data)
-            }else {
-                resolve( false)
-            }
-
-        });
-    })
-
+    folder=folder==='moment'?'moment':'gallery'
+   let user_id= uni.getStorageSync('user_id')
+    if(!user_id){
+        user_id='00'
+    }
+    if(!file.name){
+        file.name=file.path
+    }
+    let key=folder+'/'+user_id+'/'+tools.getDate()+'/'+tools.getRandFileName(file.name)
+    // #ifdef APP-PLUS
+    file =file.path;
+    //#endif
+    let url = await uploadFile(file,key);
+    tools.hideLoading()
+    return  url
 }
 
 
-cosServe.getSignUrl=  function (url){
+
+cosServe.getSignUrl= async function (url){
     if(!url){
         return  ''
     }
-    if(cos===null){
-        startCos();
-    }
     console.log(url)
     url= url.replace('https://','')
     url= url.replace('http://','')
     url= url.replace('icoco-1317650740.cos.ap-guangzhou.myqcloud.com/','')
-    console.log(url)
-    console.log('开始签名----------------------------'+url)
-    console.log({
-        Bucket: Bucket,
-        Region: Region,
-        Key: url, /* 存储在桶里的对象键(例如1.jpg,a/b/test.txt),支持中文,必须字段 */
-    })
-    return new Promise((resolve, reject) => {
-        cos.getObjectUrl({
-                Bucket: Bucket,
-                Region: Region,
-                Key: url, /* 存储在桶里的对象键(例如1.jpg,a/b/test.txt),支持中文,必须字段 */
-            },
-            function (err, data) {
-                console.log(err || data.Url);
-                if(err){
-                    resolve('')
-                }else {
-                    resolve(data.Url)
-                }
-            })
-    })
+    let signUrl = await getFileAccessUrl(url);
+    console.log(signUrl)
+    return  signUrl
 
 }
 

+ 121 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-base-plugin/index.js

@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const crypto = require('crypto');
+
+/**
+ * 插件模块使用情况统计
+ * @param {object} event - 通过uniCloud.callFunction调用云函数时传入的data对象
+ * @param {string} event.secretId - 配置的secretId
+ * @param {string} event.secretKey - 配置的secretKey
+ * @param {string} event.module - 模块名称
+ * @param {object} event.extraInfo - 附加信息
+ * @return {Promise<void>}
+ */
+exports.main = async ({ secretId, secretKey, module, extraInfo }) => {
+  if (!secretId || !secretKey || !module) {
+    throw new Error('secretId,secretKey和module不能为空');
+  }
+  const db = uniCloud.database();
+  // 暂时未在DCloud找到“判断表是否存在”的API,故每次都调用createCollection
+  try {
+    await db.createCollection('tencentcloud_plugin_report');
+  } catch (error) {
+    if (/DATABASE_COLLECTION_ALREADY_EXIST/.test(error.message)) {
+      // 如果发现表已存在,则忽略错误,继续向下执行
+    } else {
+      // 其它错误,包括但不限于(阿里云不支持代码自动创建表,暂不统计),直接抛出异常
+      throw error;
+    }
+  }
+  const collection = db.collection('tencentcloud_plugin_report');
+  // 查找出第一条记录
+  let {
+    data: [record]
+  } = await collection.limit(1).get();
+  // 如果为空则创建一条记录,该记录的主键_id作为数据上报的siteId使用,不同模块的上报共用一个siteId
+  if (!record) {
+    const { id } = await collection.add({});
+    record = { _id: id };
+  }
+  // 如果module已存在,说明已经上报过,直接返回
+  if (record[module]) {
+    return;
+  }
+  // 获取UserUin
+  const userUin = await getUserUin(secretId, secretKey);
+  // 调用上报接口,此接口不返回上报状态
+  await uniCloud.httpclient.request('https://appdata.qq.com/upload', {
+    method: 'POST',
+    headers: {
+      'Content-Type': 'application/json'
+    },
+    data: {
+      action: 'activate',
+      plugin_type: module.toLowerCase(),
+      data: {
+        site_id: `uniapp_${record._id}`,
+        site_app: 'uni-app',
+        site_url: 'uni-app',
+        uin: userUin,
+        cust_sec_on: 1,
+        others: JSON.stringify(extraInfo)
+      }
+    }
+  });
+  // 保存上报标识,之后不再上报
+  await collection.doc(record._id).update({
+    [module]: JSON.stringify(extraInfo)
+  });
+};
+
+// 获取UserUin
+async function getUserUin(secretId, secretKey) {
+  const payloadHash = crypto.createHash('sha256').update('{}').digest('hex');
+  const requestString = `POST\n/\n\ncontent-type:application/json\nhost:ms.tencentcloudapi.com\n\ncontent-type;host\n${payloadHash}`;
+  const currentDate = new Date();
+  const timestamp = `${Math.floor(currentDate.getTime() / 1000)}`;
+  const dateString = currentDate.toISOString().substr(0, 10);
+  const requestStringHash = crypto.createHash('sha256').update(requestString).digest('hex');
+  const stringToSign = `TC3-HMAC-SHA256\n${timestamp}\n${dateString}/ms/tc3_request\n${requestStringHash}`;
+  const secretDate = crypto.createHmac('sha256', `TC3${secretKey}`).update(dateString).digest();
+  const secretService = crypto.createHmac('sha256', secretDate).update('ms').digest();
+  const secretSigning = crypto.createHmac('sha256', secretService).update('tc3_request').digest();
+  const signature = crypto.createHmac('sha256', secretSigning).update(stringToSign).digest('hex');
+  const authorization = `TC3-HMAC-SHA256 Credential=${secretId}/${dateString}/ms/tc3_request, SignedHeaders=content-type;host, Signature=${signature}`;
+  const { res } = await uniCloud.httpclient.request('https://ms.tencentcloudapi.com', {
+    method: 'POST',
+    headers: {
+      'Content-Type': 'application/json',
+      'X-TC-Action': 'DescribeUserBaseInfoInstance',
+      'X-TC-Version': '2018-04-08',
+      'X-TC-Timestamp': timestamp,
+      Authorization: authorization
+    },
+    data: {},
+    dataType: 'json'
+  });
+  const { status, statusMessage, data } = res;
+  if (status !== 200) {
+    throw new Error('获取UserUin失败');
+  }
+  if (data.Response.Error) {
+    throw new Error(data.Response.Error.Message);
+  }
+  return data.Response.UserUin;
+}

+ 147 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/asr/asrRealtimeSdk.js

@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+const axios = require('axios');
+const crypto = require('crypto');
+const { secretId, secretKey, appId } = require('../config');
+
+class RealTime {
+  /**
+   * 实例化client对象
+   * @param {obejct} query 配置参数
+   */
+  constructor(query) {
+    this.query = query || null;
+  }
+
+  /**
+   * 参数签名
+   * @param {string} signStr 需要签名的参数
+   * @return {string} 签名后返回的数组
+   */
+  sign(signStr) {
+    const hash = crypto.createHmac('sha1', secretKey || '');
+    const sign = hash.update(Buffer.from(signStr, 'utf8')).digest('base64');
+    return sign;
+  }
+
+  /**
+   * 将请求的参数转为stirng类型
+   * @param {object} params
+   * @return {string}
+   */
+  formatSignString(params) {
+    let strParam = '';
+    let signStr = 'POSTasr.cloud.tencent.com/asr/v1/';
+    if (appId) {
+      signStr += appId;
+    }
+    const keys = Object.keys(params);
+    keys.sort();
+    for (const k in keys) {
+      if (Object.prototype.hasOwnProperty.call(keys, k)) {
+        strParam += `&${keys[k]}=${params[keys[k]]}`;
+      }
+    }
+    const strSign = `${signStr}?${strParam.slice(1)}`;
+    return strSign;
+  }
+
+  /**
+   * 请求参数组装
+   */
+  createQuery() {
+    const params = {};
+    const time = new Date().getTime();
+
+    params.projectid = 0;
+    params.secretid = secretId;
+    params.sub_service_type = 1;
+    params.engine_model_type = this.query.engine_model_type;
+    params.res_type = this.query.res_type;
+    params.voice_format = this.query.voice_format;
+    params.source = 0;
+    params.timestamp = `${Math.round(time / 1000)}`;
+    params.expired = Math.round(time / 1000) + 24 * 60 * 60;
+    params.nonce = Math.round(time / 100000);
+    params.voice_id = this.query.voice_id;
+    params.end = this.query.end;
+    params.seq = this.query.seq;
+
+    // 非必填参数
+    this.query.hasOwnProperty('hotword_id') && (params.hotword_id = this.query.hotword_id);
+    this.query.hasOwnProperty('result_text_format') && (params.result_text_format = this.query.result_text_format);
+    this.query.hasOwnProperty('needvad') && (params.needvad = this.query.needvad);
+    this.query.hasOwnProperty('filter_dirty') && (params.filter_dirty = this.query.filter_dirty);
+    this.query.hasOwnProperty('filter_modal') && (params.filter_modal = this.query.filter_modal);
+    this.query.hasOwnProperty('filter_punc') && (params.filter_punc = this.query.filter_punc);
+    this.query.hasOwnProperty('convert_num_mode') && (params.convert_num_mode = this.query.convert_num_mode);
+
+    return params;
+  }
+
+  /**
+   * 发起请求
+   * @param {string} url 请求的url
+   * @param {object} chunk 音频的buffer
+   * @param {object} headers 请求头
+   * @return {Promise<object>} 音频识别结果
+   */
+  async doRequest(url, chunk, headers) {
+    const options = {
+      url,
+      method: 'POST',
+      headers,
+      data: chunk
+    };
+    const response = await axios(options);
+    const { status, statusText, data } = response;
+    if (status !== 200) {
+      throw new Error(`实时语音接口调用失败[${status} - ${statusText}]`);
+    }
+    if (data.code !== 0) {
+      throw new Error(data.message);
+    }
+    return data;
+  }
+
+  /**
+   * 实时语音请求参数组装
+   * @param {object} chunk
+   * @return {Promise<object>} 音频识别结果
+   */
+  sendRequest(chunk) {
+    if (chunk.size === 0) {
+      return '';
+    }
+    const query = this.createQuery();
+    const signStr = this.formatSignString(query);
+    const autho = this.sign(signStr);
+    const datalen = chunk.length;
+
+    const headers = {};
+    headers.Authorization = autho;
+    headers['Content-Length'] = datalen;
+    headers['Content-Type'] = 'application/octet-stream';
+    headers.Host = 'asr.cloud.tencent.com';
+
+    const reqUrl = `http://${signStr.substring(4, signStr.length)}`;
+    const res = this.doRequest(reqUrl, chunk, headers);
+    return res;
+  }
+}
+
+module.exports = RealTime;

+ 61 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/asr/index.js

@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+const { request } = require('./utils.js');
+const RealTime = require('./asrRealtimeSdk');
+/**
+ * 获取语音识别相关接口
+ * @param {object} params - 参数包装对象
+ * @param {string} params.name - 对应图像识别 API的Action值 https://cloud.tencent.com/document/product/1093/35637
+ * @param {object} params.payload - API需要的参数
+ * @return {Promise<object>} API返回的有效数据
+ */
+async function getAsrResult(params) {
+  if (!params.name) {
+    throw new Error('缺少API Action参数');
+  }
+  try {
+    const result = await request(params.name, params.payload);
+    return result;
+  } catch (e) {
+    throw new Error(e);
+  }
+}
+
+/**
+ * 实时语音识别
+ * @param {object} params
+ * @param {object} params.data // 音频文件
+ * @param {object} params.params // 参数
+ * @return {Promise<object>} 语音识别结果
+ */
+async function realTimeVoice({ data: voiceData, params }) {
+  try {
+    // 创建调用实例
+    const asrReq = new RealTime(params);
+    const chunk = Buffer.from(voiceData, 'base64');
+    const result = await asrReq.sendRequest(chunk);
+    return result;
+  } catch (e) {
+    throw new Error(e);
+  }
+}
+
+module.exports = {
+  getAsrResult,
+  realTimeVoice
+};

+ 55 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/asr/utils.js

@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const axios = require('axios');
+const { sign } = require('../common');
+/**
+ * 请求腾讯云语音识别接口公共方法
+ * @param {string} action - 接口请求action
+ * @param {object} payload - 接口请求体
+ * @returns {object} API返回的有效数据
+ */
+async function request(action, payload) {
+  const [timestamp, authorization] = sign('asr', JSON.stringify(payload));
+
+  const options = {
+    url: 'https://asr.tencentcloudapi.com',
+    method: 'POST',
+    headers: {
+      'Content-Type': 'application/json',
+      'X-TC-Action': action,
+      'X-TC-Version': '2019-06-14',
+      'X-TC-Timestamp': timestamp,
+      Authorization: authorization
+    },
+    data: payload
+  };
+  const response = await axios(options);
+  const { status, statusText, data } = response;
+  if (status !== 200) {
+    throw new Error(`${action}接口调用失败[${status} - ${statusText}]`);
+  }
+  if (data.Response.Error) {
+    throw new Error(data.Response.Error.Message);
+  }
+  return data.Response;
+}
+
+module.exports = {
+  request
+};

+ 23 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/captcha/config.js

@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+// 腾讯云uni-app验证码插件依赖云函数的配置
+module.exports = {
+  appId: 0, // 申请的场景 ID integer类型
+  appSecretKey: '' // AppSecretKey用于服务器端校验验证码票据的验证密钥
+};

+ 65 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/captcha/index.js

@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+const { appId, appSecretKey } = require('./config.js');
+const { request } = require('./utils.js');
+const getExtraReportInfo = () => ({ captcha_appid: appId });
+
+/**
+ * 获取配置在云函数的短信appid
+ */
+function getAppId() {
+  return appId;
+}
+
+/**
+ * 核查验证码票据结果
+ * @param {*} params
+ * @param {integer} params.CaptchaType - 固定填值:9。可在控制台配置不同验证码类型。
+ * @param {string} params.Ticket - 前端回调函数返回的用户验证票据
+ * @param {string} params.UserIp - 用户操作来源的外网 IP
+ * @param {string} params.Randstr - 前端回调函数返回的随机字符串
+ * @param {integer} [params.BusinessId] - 业务 ID,网站或应用在多个业务中使用此服务,通过此 ID 区分统计数据
+ * @param {integer} [params.SceneId] - 场景 ID,网站或应用的业务下有多个场景使用此服务,通过此 ID 区分统计数据
+ * @param {string} [params.MacAddress] - mac 地址或设备唯一标识
+ * @param {string} [params.Imei] - 手机设备号
+ * @param {integer} [params.NeedGetCaptchaTime] - 是否返回前端获取验证码时间,取值1:需要返回
+ */
+async function describeCaptchaResult(params) {
+  // 配置校验
+  if (!appId || !appSecretKey) {
+    throw new Error('请在云函数CAPTCHA模块中配置appId和AppSecretKey');
+  }
+  const auth = uniCloud.auth();
+  const userIp = auth.getClientIP();
+  // 删除掉云函数带上来的额外参数
+  delete params.uniIdToken;
+  // 调用核查验证码票据接口
+  const result = await request('DescribeCaptchaResult', {
+    AppSecretKey: appSecretKey,
+    CaptchaAppId: Number(appId),
+    UserIp: userIp,
+    ...params
+  });
+  return result;
+}
+
+module.exports = {
+  getAppId,
+  describeCaptchaResult,
+  getExtraReportInfo
+};

+ 53 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/captcha/utils.js

@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const axios = require('axios');
+const { sign } = require('../common');
+
+/**
+ * 请求腾讯云验证码接口公共方法
+ * @param {string} action - 接口请求action
+ * @param {object} payload - 接口请求体
+ * @returns {object} API返回的有效数据
+ */
+async function request(action, payload) {
+  const [timestamp, authorization] = sign('captcha', JSON.stringify(payload));
+  const options = {
+    url: 'https://captcha.tencentcloudapi.com',
+    method: 'POST',
+    headers: {
+      'Content-Type': 'application/json',
+      'X-TC-Action': action,
+      'X-TC-Version': '2019-07-22',
+      'X-TC-Timestamp': timestamp,
+      Authorization: authorization
+    },
+    data: payload
+  };
+  const response = await axios(options);
+  const { status, statusText, data } = response;
+  if (status !== 200) {
+    throw new Error(`${action}接口调用失败[${status} - ${statusText}]`);
+  }
+  if (data.Response.Error) {
+    throw new Error(data.Response.Error.Message);
+  }
+  return data.Response;
+}
+
+module.exports = { request };

+ 76 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/common.js

@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const crypto = require('crypto');
+const { secretId, secretKey, isReport } = require('./config');
+
+/**
+ * 获取腾讯云API签名方法V3
+ * @param {string} servicename - 服务名称,例如:ocr、sms等
+ * @param {string} payload - 接口请求正文
+ * @return {string[]} 返回API请求接口headers所需要的认证信息
+ */
+function sign(servicename, payload) {
+  // 配置校验
+  if (!secretId || !secretKey) {
+    throw new Error('请检查云函数密钥配置文件!');
+  }
+  if (!servicename) {
+    throw new Error('请填写服务名称');
+  }
+  const payloadHash = crypto.createHash('sha256').update(payload).digest('hex');
+  const requestString = `POST\n/\n\ncontent-type:application/json\nhost:${servicename}.tencentcloudapi.com\n\ncontent-type;host\n${payloadHash}`;
+  const currentDate = new Date();
+  const timestamp = `${Math.floor(currentDate.getTime() / 1000)}`;
+  const dateString = currentDate.toISOString().substr(0, 10);
+  const requestStringHash = crypto.createHash('sha256').update(requestString).digest('hex');
+  const stringToSign = `TC3-HMAC-SHA256\n${timestamp}\n${dateString}/${servicename}/tc3_request\n${requestStringHash}`;
+  const secretDate = crypto.createHmac('sha256', `TC3${secretKey}`).update(dateString).digest();
+  const secretService = crypto.createHmac('sha256', secretDate).update(servicename).digest();
+  const secretSigning = crypto.createHmac('sha256', secretService).update('tc3_request').digest();
+  const signature = crypto.createHmac('sha256', secretSigning).update(stringToSign).digest('hex');
+  return [
+    timestamp,
+    `TC3-HMAC-SHA256 Credential=${secretId}/${dateString}/${servicename}/tc3_request, SignedHeaders=content-type;host, Signature=${signature}`
+  ];
+}
+
+/**
+ * 插件使用统计,异步处理,忽略结果,不阻塞主流程
+ * @param {string} module - 模块名称
+ * @param {object} extraInfo - 附加信息
+ */
+function report(module, extraInfo) {
+  if (!isReport) {
+    return;
+  }
+  if (!secretId || !secretKey) {
+    return;
+  }
+  uniCloud.callFunction({
+    name: 'tencentcloud-base-plugin',
+    data: {
+      secretId,
+      secretKey,
+      module,
+      extraInfo
+    }
+  });
+}
+
+module.exports = { sign, report };

+ 24 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/config.js

@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+// 腾讯云uni-app插件依赖云函数的公共配置
+module.exports = {
+  appId: '1317650740', // 腾讯云appId
+  secretId: 'AKIDdEcnkX8YlmabOzNWSRIB311xjIepyvvl', // 腾讯云secretId
+  secretKey: 'kIeqriT2Xqx7Nf1Qly7FPZlz62mJcibG', // 腾讯云secretKey
+  isReport: true // 是否允许上报插件使用情况(注:仅在首次使用时会上报一次,对正常业务逻辑无影响)
+};

+ 24 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/cos/config.js

@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+// 腾讯云uni-app对象存储COS插件依赖云函数的配置
+module.exports = {
+  bucket: 'icoco-1317650740', // COS存储桶名称
+  region: 'ap-guangzhou', // COS地域
+  expires: 60 // 签名有效期(单位:秒)
+};

+ 62 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/cos/get-object-url.js

@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const crypto = require('crypto');
+const { secretId, secretKey } = require('../config');
+const { bucket, region, expires } = require('./config');
+
+/**
+ * 获取腾讯云COS文件的临时访问地址
+ * @param {object} params - 参数包装对象
+ * @param {string} params.key - 将要访问COS文件的名称
+ * @return {string} COS文件的访问地址(包含临时签名)
+ */
+function getObjectURL({ key }) {
+  // 配置校验
+  if (!secretId || !secretKey) {
+    throw new Error('请云函数配置文件中配置secretId和secretKey');
+  }
+  if (!bucket || !region) {
+    throw new Error('请在云函数COS模块中配置bucket和region');
+  }
+  if (isNaN(expires) || expires <= 0) {
+    throw new Error('请在云函数COS模块中配置有效的expires');
+  }
+  // 生成签名信息
+  const currentDate = new Date();
+  const expirationDate = new Date(currentDate.getTime() + expires * 1000);
+  const keyTime = `${Math.floor(currentDate.getTime() / 1000)};${Math.floor(expirationDate.getTime() / 1000)}`;
+  const signKey = crypto.createHmac('sha1', secretKey).update(keyTime).digest('hex');
+  const httpString = `get\n/${key}\n\n\n`;
+  const httpStringHash = crypto.createHash('sha1').update(httpString).digest('hex');
+  const stringToSign = `sha1\n${keyTime}\n${httpStringHash}\n`;
+  const signature = crypto.createHmac('sha1', signKey).update(stringToSign).digest('hex');
+  return (
+    /* prettier-ignore */
+    `https://${bucket}.cos.${region}.myqcloud.com/${key}`
+    + `?q-sign-algorithm=sha1`
+    + `&q-ak=${secretId}`
+    + `&q-sign-time=${keyTime}`
+    + `&q-key-time=${keyTime}`
+    + `&q-header-list=`
+    + `&q-url-param-list=`
+    + `&q-signature=${signature}`
+  );
+}
+
+module.exports = getObjectURL;

+ 32 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/cos/index.js

@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const { bucket, region } = require('./config');
+
+const getObjectURL = require('./get-object-url');
+const signPostObjectAPI = require('./sign-post-object-api');
+const getExtraReportInfo = () => ({
+  cos_bucket: bucket,
+  cos_region: region
+});
+
+module.exports = {
+  getObjectURL,
+  signPostObjectAPI,
+  getExtraReportInfo
+};

+ 60 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/cos/sign-post-object-api.js

@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const crypto = require('crypto');
+const { secretId, secretKey } = require('../config');
+const { bucket, region, expires } = require('./config');
+
+/**
+ * 为腾讯云COS的POST Object API进行签名
+ * 更多信息请访问 https://cloud.tencent.com/document/product/436/14690
+ * @return {object} 上传URL以及其它签名信息
+ */
+function signPostObjectAPI() {
+  // 配置校验
+  if (!secretId || !secretKey) {
+    throw new Error('请云函数配置文件中配置secretId和secretKey');
+  }
+  if (!bucket || !region) {
+    throw new Error('请在云函数COS模块中配置bucket和region');
+  }
+  if (isNaN(expires) || expires <= 0) {
+    throw new Error('请在云函数COS模块中配置有效的expires');
+  }
+  // 生成签名信息
+  const currentDate = new Date();
+  const expirationDate = new Date(currentDate.getTime() + expires * 1000);
+  const keyTime = `${Math.floor(currentDate.getTime() / 1000)};${Math.floor(expirationDate.getTime() / 1000)}`;
+  const policy = JSON.stringify({
+    expiration: expirationDate.toISOString(),
+    conditions: [{ 'q-sign-algorithm': 'sha1' }, { 'q-ak': secretId }, { 'q-sign-time': keyTime }]
+  });
+  const signKey = crypto.createHmac('sha1', secretKey).update(keyTime).digest('hex');
+  const stringToSign = crypto.createHash('sha1').update(policy).digest('hex');
+  const signature = crypto.createHmac('sha1', signKey).update(stringToSign).digest('hex');
+  return {
+    host: `https://${bucket}.cos.${region}.myqcloud.com`,
+    signAlgorithm: 'sha1',
+    ak: secretId,
+    keyTime,
+    signature,
+    policy: Buffer.from(policy).toString('base64')
+  };
+}
+
+module.exports = signPostObjectAPI;

+ 29 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/faceid/config.js

@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+// 腾讯云uni-app 人脸识别插件依赖云函数的配置
+// 可用地域列表参数
+// 华北地区(北京)	ap-beijing
+// 华南地区(广州)	ap-guangzhou
+// 华东地区(上海)	ap-shanghai
+// 华东地区(南京) ap-nanjing
+// 西南地区(成都)	ap-chengdu
+// 西南地区(重庆) ap-chongqing
+module.exports = {
+  region: 'ap-guangzhou' // 地域列表
+};

+ 37 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/faceid/index.js

@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+const { request } = require('./utils.js');
+
+/**
+ * 获取人脸核身结果
+ * @param {object} params - 参数包装对象
+ * @param {string} params.name - 人脸核身 API的Action值 https://cloud.tencent.com/document/product/1007/31320
+ * @param {object} params.payload - API需要的参数
+ * @return {Promise<object>} API返回数据
+ */
+async function getFaceidResult(params) {
+  if (!params.name) {
+    throw new Error('缺少API Action参数');
+  }
+  const result = await request(params.name, params.payload);
+  return result;
+}
+
+module.exports = {
+  getFaceidResult
+};

+ 59 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/faceid/utils.js

@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const axios = require('axios');
+const { region } = require('./config');
+const { sign } = require('../common');
+
+/**
+ * 请求腾讯云人脸核身公共方法
+ * @param {string} action - 接口请求action
+ * @param {object} payload - 接口请求体
+ * @returns {object} API返回的有效数据
+ */
+async function request(action, payload) {
+  if (!region) {
+    throw new Error('请在云函数FACEID模块中配置region');
+  }
+  const [timestamp, authorization] = sign('faceid', JSON.stringify(payload));
+  const options = {
+    url: 'https://faceid.tencentcloudapi.com',
+    method: 'POST',
+    headers: {
+      'Content-Type': 'application/json',
+      'X-TC-Action': action,
+      'X-TC-Version': '2018-03-01',
+      'X-TC-Timestamp': timestamp,
+      'X-TC-Region': region,
+      Authorization: authorization
+    },
+    responseType: 'json',
+    data: payload
+  };
+  const response = await axios(options);
+  const { status, statusText, data } = response;
+  if (status !== 200) {
+    throw new Error(`${action}接口调用失败[${status} - ${statusText}]`);
+  }
+  if (data.Response.Error) {
+    throw new Error(data.Response.Error.Message);
+  }
+  return data.Response;
+}
+
+module.exports = { request };

+ 23 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/httpdns/config.js

@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+// 腾讯云uni-app移动解析插件依赖云函数的配置
+module.exports = {
+  encId: '', // HTTPDNS企业版本授权ID
+  encKey: '' // HTTPDNS企业版本加密功能的密钥
+};

+ 114 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/httpdns/index.js

@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+const crypto = require('crypto');
+const { encId, encKey } = require('./config.js');
+
+/**
+ * 核查验证码票据结果
+ * @param {*} params
+ * @param {integer} params.domainName - 需要解析的域名
+ * @param {string?} params.ip - 用户IP 默认取用户客户端ip
+ * @param {number?} params.ttl - 是否递归服务器缓存时间 1:返回
+ * @param {number?} params.clientip - 是否返回用户公网出口IP 1:返回
+ * @param {boolean} param.isEnc - 是否加密 true(调用企业版API): 加密 false:不加密(调用免费版API)
+ */
+async function describeDnsResult({ domainName, ip, ttl, clientip, isEnc }) {
+  // 配置校验
+  if (isEnc && (!encId || !encKey)) {
+    throw new Error('请在云函数HTTPDNS模块中配置encId和encKey');
+  }
+  // 如果不传入ip,就取客户端ip
+  if (!ip) {
+    const auth = uniCloud.auth();
+    ip = auth.getClientIP();
+  }
+  // 企业版API调用需加密
+  if (isEnc) {
+    domainName = encrypt(domainName);
+    ip = encrypt(ip);
+  }
+
+  const params = {
+    dn: domainName,
+    ip,
+    ttl,
+    clientip,
+    id: isEnc ? encId : undefined
+  };
+  const { status, statusText, data } = await uniCloud.httpclient.request('http://119.29.29.29/d', {
+    method: 'GET',
+    dataType: 'text',
+    data: params
+  });
+
+  if (status !== 200) {
+    throw new Error(`接口调用失败[${status} - ${statusText}]`);
+  }
+  if (!data) {
+    throw new Error('域名解析失败');
+  }
+  if (isEnc) {
+    return decrypt(data);
+  }
+
+  return data;
+}
+
+/**
+ * 加密
+ * @param {string} encString - 需要加密的字符串
+ * @return {string} 加密数据
+ */
+function encrypt(encString) {
+  try {
+    if (!encString) {
+      throw new Error('请传入待加密数据');
+    }
+    const iv = Buffer.alloc(0);
+    const cipher = crypto.createCipheriv('des-ecb', encKey, iv);
+    let encrypText = cipher.update(encString, 'utf8', 'hex');
+    encrypText += cipher.final('hex');
+    return encrypText;
+  } catch (e) {
+    throw new Error('加密失败');
+  }
+}
+
+/**
+ * 解密
+ * @param {string} decString - 需要解密的字符串
+ * @return {string}  解密数据
+ */
+function decrypt(decString) {
+  try {
+    if (!decString) {
+      throw new Error('请传入待解密数据');
+    }
+    const iv = Buffer.alloc(0);
+    const cipher = crypto.createDecipheriv('des-ecb', encKey, iv);
+    let decryptText = cipher.update(decString, 'hex', 'utf8');
+    decryptText += cipher.final('utf8');
+    return decryptText;
+  } catch (e) {
+    throw new Error('解密失败');
+  }
+}
+
+module.exports = {
+  describeDnsResult
+};

+ 29 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/iai/config.js

@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+// 腾讯云uni-app 人脸识别插件依赖云函数的配置
+// 可用地域列表参数
+// 华北地区(北京)	ap-beijing
+// 华南地区(广州)	ap-guangzhou
+// 华东地区(上海)	ap-shanghai
+// 华东地区(南京) ap-nanjing
+// 西南地区(成都)	ap-chengdu
+// 西南地区(重庆) ap-chongqing
+module.exports = {
+  region: 'ap-guangzhou' // 地域列表
+};

+ 37 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/iai/index.js

@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+const { request } = require('./utils.js');
+
+/**
+ * 获取人脸识别结果
+ * @param {object} params - 参数包装对象
+ * @param {string} params.name - 人脸识别 API的Action值 https://cloud.tencent.com/document/product/867/45023
+ * @param {object} params.payload - API需要的参数
+ * @return {Promise<object>} API返回数据
+ */
+async function getIaiResult(params) {
+  if (!params.name) {
+    throw new Error('缺少API Action参数');
+  }
+  const result = await request(params.name, params.payload);
+  return result;
+}
+
+module.exports = {
+  getIaiResult
+};

+ 59 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/iai/utils.js

@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const axios = require('axios');
+const { region } = require('./config');
+const { sign } = require('../common');
+
+/**
+ * 请求腾讯云人脸识别公共方法
+ * @param {string} action - 接口请求action
+ * @param {object} payload - 接口请求体
+ * @returns {object} API返回的有效数据
+ */
+async function request(action, payload) {
+  if (!region) {
+    throw new Error('请在云函数IAI模块中配置region');
+  }
+  const [timestamp, authorization] = sign('iai', JSON.stringify(payload));
+  const options = {
+    url: 'https://iai.tencentcloudapi.com',
+    method: 'POST',
+    headers: {
+      'Content-Type': 'application/json',
+      'X-TC-Action': action,
+      'X-TC-Version': '2020-03-03',
+      'X-TC-Timestamp': timestamp,
+      'X-TC-Region': region,
+      Authorization: authorization
+    },
+    responseType: 'json',
+    data: payload
+  };
+  const response = await axios(options);
+  const { status, statusText, data } = response;
+  if (status !== 200) {
+    throw new Error(`${action}接口调用失败[${status} - ${statusText}]`);
+  }
+  if (data.Response.Error) {
+    throw new Error(data.Response.Error.Message);
+  }
+  return data.Response;
+}
+
+module.exports = { request };

+ 41 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/ims/image-moderation.js

@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const { request } = require('./utils');
+
+/**
+ * 图片内容检测
+ * 更多信息请访问 https://cloud.tencent.com/document/product/669/34503
+ * @param {object} params - 参数包装对象
+ * @param {string} params.content - 图片文件内容Base64或url地址
+ * @return {Promise<object>} 识别结果
+ */
+async function imageModeration({ content }) {
+  if (!content) {
+    throw new Error('图片内容不能为空');
+  }
+  const isUrl = /^https?:\/\//.test(content);
+  // 调用腾讯云查询接口
+  const { Data } = await request('ImageModeration', {
+    FileContent: isUrl ? undefined : content,
+    FileUrl: isUrl ? content : undefined
+  });
+  return Data;
+}
+
+module.exports = imageModeration;

+ 94 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/ims/image-sample.js

@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const { request } = require('./utils');
+
+/**
+ * 查询图片样本
+ * 更多信息请访问 https://cloud.tencent.com/document/product/669/37187
+ * @param {object} params - 参数包装对象
+ * @param {int} params.limit - 数量限制
+ * @param {int} params.offset - 偏移量
+ * @return {Promise<object>} 图片样本的信息
+ */
+async function listImageSample({ limit = 20, offset = 0 }) {
+  // 调用腾讯云接口
+  const { FileSampleSet, TotalCount } = await request('DescribeFileSample', {
+    Limit: limit,
+    Offset: offset
+  });
+  return {
+    FileSampleSet,
+    TotalCount
+  };
+}
+
+/**
+ * 新增图片样本
+ * 更多信息请访问 https://cloud.tencent.com/document/product/669/37189
+ * @param {object} params - 参数包装对象
+ * @param {string} params.fileName - 图片文件名称
+ * @param {string} params.fileUrl - 图片文件路径
+ * @param {string} params.fileMd5 - 图片文件MD5
+ * @param {string} params.evilType - 恶意类型
+ * @param {string} params.label - 样本类型 1:黑库 2:白库
+ * @return {Promise<int>} 任务状态
+ */
+async function createImageSample({ fileName, fileUrl, fileMd5, evilType, label }) {
+  if (!fileName || !fileUrl || !fileMd5 || !evilType || !label) {
+    throw new Error('fileName/fileUrl/fileMd5/evilType/label不能为空');
+  }
+  // 调用腾讯云接口
+  const { Progress } = await request('CreateFileSample', {
+    Contents: [
+      {
+        FileName: fileName,
+        FileUrl: fileUrl,
+        FileMd5: fileMd5
+      }
+    ],
+    EvilType: evilType,
+    Label: label,
+    FileType: 'image'
+  });
+  return Progress;
+}
+
+/**
+ * 删除图片样本
+ * 更多信息请访问 https://cloud.tencent.com/document/product/669/37188
+ * @param {object} params - 参数包装对象
+ * @param {string[]} params.ids - 唯一标识数组
+ * @return {Promise<int>} 任务状态
+ */
+async function deleteImageSample({ ids }) {
+  if (!ids || !ids.length) {
+    throw new Error('ids不能为空');
+  }
+  // 调用腾讯云接口
+  const { Progress } = await request('DeleteFileSample', {
+    Ids: ids
+  });
+  return Progress;
+}
+
+module.exports = {
+  listImageSample,
+  createImageSample,
+  deleteImageSample
+};

+ 27 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/ims/index.js

@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const imageModeration = require('./image-moderation');
+const { listImageSample, createImageSample, deleteImageSample } = require('./image-sample');
+
+module.exports = {
+  imageModeration,
+  listImageSample,
+  createImageSample,
+  deleteImageSample
+};

+ 52 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/ims/utils.js

@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const { sign } = require('../common');
+
+/**
+ * 请求腾讯云CMS接口公共方法
+ * @param {string} action - 接口请求action
+ * @param {object} payload - 接口请求体
+ * @return {object} API返回的有效数据
+ */
+async function request(action, payload) {
+  const [timestamp, authorization] = sign('cms', JSON.stringify(payload));
+  const { res } = await uniCloud.httpclient.request('https://cms.tencentcloudapi.com', {
+    method: 'POST',
+    headers: {
+      'Content-Type': 'application/json',
+      'X-TC-Action': action,
+      'X-TC-Region': 'ap-guangzhou',
+      'X-TC-Version': '2019-03-21',
+      'X-TC-Timestamp': timestamp,
+      Authorization: authorization
+    },
+    data: payload,
+    dataType: 'json'
+  });
+  const { status, statusMessage, data } = res;
+  if (status !== 200) {
+    throw new Error(`${action}接口调用失败[${status} - ${statusMessage}]`);
+  }
+  if (data.Response.Error) {
+    throw new Error(data.Response.Error.Message);
+  }
+  return data.Response;
+}
+
+module.exports = { request };

+ 58 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/index.js

@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const { report } = require('./common');
+
+const modules = {
+  COS: require('./cos'),
+  SMS: require('./sms'),
+  OCR: require('./ocr'),
+  VOD: require('./vod'),
+  CAPTCHA: require('./captcha'),
+  TIIA: require('./tiia'),
+  TMS: require('./tms'),
+  IMS: require('./ims'),
+  ASR: require('./asr'),
+  SOE: require('./soe'),
+  HTTPDNS: require('./httpdns'),
+  TTS: require('./tts'),
+  TMT: require('./tmt'),
+  IAI: require('./iai'),
+  FACEID: require('./faceid')
+};
+
+/**
+ * 腾讯云uni-app插件依赖的云函数入口
+ * @param {object} event - 通过uniCloud.callFunction调用云函数时传入的data对象
+ * @param {string} event.module - 云函数模块(目前仅支付传入"COS"),必传参数
+ * @param {string} event.action - 云函数模块下的方法名,必传参数
+ * @return {Promise<any>}
+ */
+exports.main = async (event) => {
+  const { module, action, ...params } = event;
+  if (!modules[module] || !modules[module][action]) {
+    throw new Error(`${module}.${action}不存在`);
+  }
+  // 数据统计
+  const getExtraInfoMethod = modules[module].getExtraReportInfo;
+  const extraInfo = getExtraInfoMethod ? getExtraInfoMethod() : {};
+  report(module, extraInfo);
+  // 调用相应的模块
+  const result = await modules[module][action](params);
+  return result;
+};

+ 28 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/ocr/config.js

@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+// 腾讯云uni-app OCR插件依赖云函数的配置
+// 可用地域列表参数
+// 华北地区(北京)	ap-beijing
+// 华南地区(广州)	ap-guangzhou
+// 港澳台地区(中国香港)	ap-hongkong
+// 华东地区(上海)	ap-shanghai
+// 北美地区(多伦多)	na-toronto
+module.exports = {
+  region: 'ap-guangzhou' // 地域列表
+};

+ 46 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/ocr/get-ocr-result.js

@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const { request } = require('./utils');
+
+/**
+ * 获取OCR识别结果
+ * @param {object} params - 参数包装对象
+ * @param {string} params.name - 对应OCR API的Action值 https://cloud.tencent.com/document/api/866/34938
+ * @param {object} params.payload - API需要的参数
+ * @param {string} [params.payload.ImageBase64] - 图片的 Base64 值。支持的图片格式:PNG、JPG、JPEG,暂不支持 GIF 格式。支持的图片大小:所下载图片经 Base64 编码后不超过 3M。图片下载时间不超过 3 秒。
+ * @param {string} [params.payload.ImageUrl] - 图片的 Url 地址。支持的图片格式:PNG、JPG、JPEG,暂不支持 GIF 格式。支持的图片大小:所下载图片经 Base64 编码后不超过 3M。图片下载时间不超过 3 秒。
+ * @returns {object} API返回的有效数据
+ */
+async function getOcrResult(params) {
+  if (!params.name) {
+    throw new Error('缺少API Action参数');
+  }
+  if (!(params.payload.ImageBase64 || params.payload.ImageUrl)) {
+    throw new Error('ImageUrl 和 ImageBase64 必须有一个不为空');
+  }
+  // 调用OCR识别接口
+  try {
+    const result = await request(params.name, params.payload);
+    return result;
+  } catch (e) {
+    throw new Error(e);
+  }
+}
+
+module.exports = getOcrResult;

+ 23 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/ocr/index.js

@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const getOcrResult = require('./get-ocr-result');
+
+module.exports = {
+  getOcrResult
+};

+ 55 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/ocr/utils.js

@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const axios = require('axios');
+const { region } = require('./config');
+const { sign } = require('../common');
+
+/**
+ * 请求腾讯云OCR接口公共方法
+ * @param {string} action - 接口请求action
+ * @param {object} payload - 接口请求体
+ * @returns {object} API返回的有效数据
+ */
+async function request(action, payload) {
+  const [timestamp, authorization] = sign('ocr', JSON.stringify(payload));
+  const options = {
+    url: 'https://ocr.tencentcloudapi.com',
+    method: 'POST',
+    headers: {
+      'Content-Type': 'application/json',
+      'X-TC-Action': action,
+      [region ? 'X-TC-Region' : '']: region,
+      'X-TC-Version': '2018-11-19',
+      'X-TC-Timestamp': timestamp,
+      Authorization: authorization
+    },
+    data: payload
+  };
+  const response = await axios(options);
+  const { status, statusText, data } = response;
+  if (status !== 200) {
+    throw new Error(`${action}接口调用失败[${status} - ${statusText}]`);
+  }
+  if (data.Response.Error) {
+    throw new Error(data.Response.Error.Message);
+  }
+  return data.Response;
+}
+
+module.exports = { request };

+ 63 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/package-lock.json

@@ -0,0 +1,63 @@
+{
+  "name": "tencentcloud-uniapp-plugin-scf-template",
+  "version": "1.0.0",
+  "lockfileVersion": 1,
+  "requires": true,
+  "dependencies": {
+    "asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+    },
+    "axios": {
+      "version": "0.27.2",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
+      "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
+      "requires": {
+        "follow-redirects": "^1.14.9",
+        "form-data": "^4.0.0"
+      }
+    },
+    "combined-stream": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+      "requires": {
+        "delayed-stream": "~1.0.0"
+      }
+    },
+    "delayed-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
+    },
+    "follow-redirects": {
+      "version": "1.15.2",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
+      "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA=="
+    },
+    "form-data": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+      "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+      "requires": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "mime-types": "^2.1.12"
+      }
+    },
+    "mime-db": {
+      "version": "1.52.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
+    },
+    "mime-types": {
+      "version": "2.1.35",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+      "requires": {
+        "mime-db": "1.52.0"
+      }
+    }
+  }
+}

+ 8 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/package.json

@@ -0,0 +1,8 @@
+{
+  "name": "tencentcloud-uniapp-plugin-scf-template",
+  "version": "1.0.0",
+  "main": "index.js",
+  "dependencies": {
+    "axios": "^0.27.2"
+  }
+}

+ 98 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/sms/check-verification-code.js

@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const { verificationCodeCollection, verificationCodeExpires, verificationCodeCheckTimes } = require('./config');
+
+/**
+ * 校验验证码是否正确
+ * @async
+ * @param {object} params - 参数包装对象
+ * @param {string} params.phoneNumber - 手机号码
+ * @param {string} params.verificationCode - 用户输入的验证码
+ * @return {Promise<void>} 验证码核验状态(无异常代表正确)
+ */
+async function checkVerificationCode({ phoneNumber, verificationCode }) {
+  // 配置校验
+  if (!verificationCodeCollection) {
+    throw new Error('请在云函数SMS模块中配置verificationCodeCollection');
+  }
+  if (!verificationCodeExpires || isNaN(verificationCodeExpires) || verificationCodeExpires <= 0) {
+    throw new Error('请在云函数SMS模块中配置有效的verificationCodeExpires');
+  }
+  if (!verificationCodeCheckTimes || isNaN(verificationCodeCheckTimes) || verificationCodeCheckTimes <= 0) {
+    throw new Error('请在云函数SMS模块中配置有效的verificationCodeCheckTimes');
+  }
+  // 参数校验
+  if (!phoneNumber) {
+    throw new Error('手机号码不能为空');
+  }
+  if (!verificationCode) {
+    throw new Error('验证码不能为空');
+  }
+  // 自动为无前缀手机号码添加+86前缀
+  if (!phoneNumber.startsWith('+')) {
+    phoneNumber = `+86${phoneNumber}`;
+  }
+  const db = uniCloud.database();
+  const verificationCodes = db.collection(verificationCodeCollection);
+  // 清理过期验证码记录
+  const result = await verificationCodes
+    .where({
+      createTime: db.command.lt(new Date().getTime() - verificationCodeExpires * 60 * 1000)
+    })
+    .remove();
+  if (result.deleted) {
+    console.log(`已自动清理掉${result.deleted}条过期记录`);
+  }
+  // 验证码查询并核对
+  const {
+    data: [record]
+  } = await verificationCodes
+    .where({
+      phoneNumber
+    })
+    .orderBy('createTime', 'desc')
+    .limit(1)
+    .get();
+  if (!record) {
+    throw new Error('验证码不正确');
+  }
+  // 每个验证码仅支持核验有限次数(防止字典遍历)
+  if (record.checkCounter >= verificationCodeCheckTimes) {
+    throw new Error('验证码不正确');
+  }
+  // 增加验证码核验次数
+  if (record.verificationCode !== verificationCode) {
+    await verificationCodes.doc(record._id).update({
+      checkCounter: record.checkCounter + 1
+    });
+    throw new Error('验证码不正确');
+  }
+  // 验证成功后异步删除验证码记录
+  try {
+    verificationCodes
+      .where({
+        phoneNumber
+      })
+      .remove();
+  } catch (error) {
+    console.log(error);
+  }
+}
+
+module.exports = checkVerificationCode;

+ 28 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/sms/config.js

@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+// 腾讯云uni-app短信SMS插件依赖云函数的配置
+module.exports = {
+  appId: '', // SMS SDK AppID
+  appSign: '', // 短信签名
+  verificationCodeCollection: 'verification_code', // 验证码数据表名(请提前在服务空间云数据库创建此表)
+  verificationCodeTemplateId: '', // 验证码短信模板ID(模板中必需包含且只能包含一个变量值,用于填充验证码)
+  verificationCodeLength: 4, // 验证码长度4~8(注:验证码为纯数字)
+  verificationCodeExpires: 5, // 验证码有效期(单位:分钟)
+  verificationCodeCheckTimes: 5 // 验证码有效次数,即允许用户输入错误次数,达到后自动失效
+};

+ 42 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/sms/get-packages-statistics.js

@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const { request } = require('./utils');
+const { appId } = require('./config');
+
+/**
+ * 获取腾讯云SMS产品套餐包状态
+ * @param {object} params - 参数包装对象
+ * @param {number} params.limit - 最大上限(需要拉取的套餐包个数)
+ * @return {Promise<object>} 短信套餐包状态信息,详见文档 https://cloud.tencent.com/document/api/382/39533
+ */
+async function getPackagesStatistics({ limit = 10 }) {
+  // 配置校验
+  if (!appId) {
+    throw new Error('请在云函数SMS模块中配置appId');
+  }
+  // 调用腾讯云查询SMS状态接口
+  const result = await request('SmsPackagesStatistics', {
+    SmsSdkAppid: appId,
+    Limit: limit,
+    Offset: 0
+  });
+  return result;
+}
+
+module.exports = getPackagesStatistics;

+ 33 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/sms/index.js

@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const { appId } = require('./config');
+
+const sendSMS = require('./send-sms');
+const sendVerificationCode = require('./send-verification-code');
+const checkVerificationCode = require('./check-verification-code');
+const getPackagesStatistics = require('./get-packages-statistics');
+const getExtraReportInfo = () => ({ sms_appid: appId });
+
+module.exports = {
+  sendSMS,
+  sendVerificationCode,
+  checkVerificationCode,
+  getPackagesStatistics,
+  getExtraReportInfo
+};

+ 46 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/sms/send-sms.js

@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const { request } = require('./utils');
+const { appId, appSign } = require('./config');
+
+/**
+ * 发送短信
+ * @param {object} params - 参数包装对象
+ * @param {string[]} params.phoneNumbers - 手机号列表
+ * @param {string} params.templateId - 短信模板ID
+ * @param {string[]} params.templateParams - 短信模板参数列表
+ * @return {Promise<object>} 短信发送状态信息,详见文档 https://cloud.tencent.com/document/api/382/38778
+ */
+async function sendSMS({ phoneNumbers, templateId, templateParams }) {
+  // 配置校验
+  if (!appId || !appSign) {
+    throw new Error('请在云函数SMS模块中配置appId和appSign');
+  }
+  // 调用腾讯云发送SMS接口
+  const result = await request('SendSms', {
+    SmsSdkAppid: appId,
+    Sign: appSign,
+    PhoneNumberSet: phoneNumbers,
+    TemplateID: templateId,
+    TemplateParamSet: templateParams
+  });
+  return result;
+}
+
+module.exports = sendSMS;

+ 69 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/sms/send-verification-code.js

@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const sendSMS = require('./send-sms');
+const { verificationCodeCollection, verificationCodeTemplateId, verificationCodeLength } = require('./config');
+
+/**
+ * 发送短信验证码
+ * @async
+ * @param {object} params - 参数包装对象
+ * @param {string} params.phoneNumber - 手机号码
+ * @return {Promise<void>} 验证码发送状态(无异常代表发送成功)
+ */
+async function sendVerificationCode({ phoneNumber }) {
+  // 配置校验
+  if (!verificationCodeCollection) {
+    throw new Error('请在云函数SMS模块中配置verificationCodeCollection');
+  }
+  if (!verificationCodeTemplateId) {
+    throw new Error('请在云函数SMS模块中配置verificationCodeTemplateId');
+  }
+  if (isNaN(verificationCodeLength) || verificationCodeLength < 4 || verificationCodeLength > 8) {
+    throw new Error('请在云函数SMS模块中配置有效的verificationCodeLength');
+  }
+  // 参数校验
+  if (!phoneNumber) {
+    throw new Error('手机号码不能为空');
+  }
+  // 自动为无前缀手机号码添加+86前缀
+  if (!phoneNumber.startsWith('+')) {
+    phoneNumber = `+86${phoneNumber}`;
+  }
+  // 生成随机验证码并存入云数据库
+  const verificationCode = `${Math.random()}`.substr(2, verificationCodeLength);
+  const db = uniCloud.database();
+  const verificationCodes = db.collection(verificationCodeCollection);
+  await verificationCodes.add({
+    phoneNumber,
+    verificationCode,
+    createTime: new Date().getTime(),
+    checkCounter: 0
+  });
+  // 发送短信
+  const { SendStatusSet } = await sendSMS({
+    phoneNumbers: [phoneNumber],
+    templateId: verificationCodeTemplateId,
+    templateParams: [verificationCode]
+  });
+  if (SendStatusSet[0].Code !== 'Ok') {
+    throw new Error(SendStatusSet[0].Message);
+  }
+}
+
+module.exports = sendVerificationCode;

+ 51 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/sms/utils.js

@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const { sign } = require('../common');
+
+/**
+ * 请求腾讯云SMS接口公共方法
+ * @param {string} action - 接口请求action
+ * @param {object} payload - 接口请求体
+ * @return {object} API返回的有效数据
+ */
+async function request(action, payload) {
+  const [timestamp, authorization] = sign('sms', JSON.stringify(payload));
+  const { res } = await uniCloud.httpclient.request('https://sms.tencentcloudapi.com', {
+    method: 'POST',
+    headers: {
+      'Content-Type': 'application/json',
+      'X-TC-Action': action,
+      'X-TC-Version': '2019-07-11',
+      'X-TC-Timestamp': timestamp,
+      Authorization: authorization
+    },
+    data: payload,
+    dataType: 'json'
+  });
+  const { status, statusMessage, data } = res;
+  if (status !== 200) {
+    throw new Error(`${action}接口调用失败[${status} - ${statusMessage}]`);
+  }
+  if (data.Response.Error) {
+    throw new Error(data.Response.Error.Message);
+  }
+  return data.Response;
+}
+
+module.exports = { request };

+ 20 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/soe/config.js

@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+// 腾讯云uni-app口语评测SOE插件依赖云函数的配置
+module.exports = {};

+ 66 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/soe/get-voice-point.js

@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const { request } = require('./utils');
+
+/**
+ * 获取参数
+ * @param {object} param - 待包装对象
+ * @return {object} 包装参数对象
+ */
+function getParams(param) {
+  const paramsData = {
+    SeqId: param.seqId,
+    IsEnd: param.isEnd,
+    VoiceFileType: 3,
+    VoiceEncodeType: 1,
+    UserVoiceData: param.voiceData,
+    SessionId: param.sessionId,
+    RefText: param.refText,
+    WorkMode: param.workMode,
+    EvalMode: param.evalMode,
+    ScoreCoeff: param.scoreCoeff
+  };
+
+  // 非必填参数
+  param.hasOwnProperty('soeAppId') && (paramsData.SoeAppId = param.soeAppId);
+  param.hasOwnProperty('storageMode') && (paramsData.StorageMode = param.storageMode);
+  param.hasOwnProperty('sentenceInfoEnabled') && (paramsData.SentenceInfoEnabled = param.sentenceInfoEnabled);
+  param.hasOwnProperty('serverType') && (paramsData.ServerType = param.serverType);
+  param.hasOwnProperty('textMode') && (paramsData.TextMode = param.textMode);
+
+  return paramsData;
+}
+
+/**
+ * 获取口语评测信息
+ * 更多信息请访问 https://cloud.tencent.com/document/api/884/32605
+ * @param {object} params - 参数包装对象
+ * @param {object} params.param - 口语评测相关参数
+ * @return {Promise<object>} 评测信息
+ */
+async function getVoicePoint({ param }) {
+  const paramsData = getParams(param);
+
+  // 调用腾讯云发音数据传输接口附带初始化过程接口
+  const evaluateInfo = await request('TransmitOralProcessWithInit', paramsData);
+
+  return evaluateInfo;
+}
+
+module.exports = getVoicePoint;

+ 23 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/soe/index.js

@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const getVoicePoint = require('./get-voice-point');
+
+module.exports = {
+  getVoicePoint
+};

+ 51 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/soe/utils.js

@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const { sign } = require('../common');
+
+/**
+ * 请求腾讯云SOE接口公共方法
+ * @param {string} action - 接口请求action
+ * @param {object} payload - 接口请求体
+ * @return {object} API返回的有效数据
+ */
+async function request(action, payload) {
+  const [timestamp, authorization] = sign('soe', JSON.stringify(payload));
+  const { res } = await uniCloud.httpclient.request('https://soe.tencentcloudapi.com', {
+    method: 'POST',
+    headers: {
+      'Content-Type': 'application/json',
+      'X-TC-Action': action,
+      'X-TC-Version': '2018-07-24',
+      'X-TC-Timestamp': timestamp,
+      Authorization: authorization
+    },
+    data: payload,
+    dataType: 'json'
+  });
+  const { status, statusMessage, data } = res;
+  if (status !== 200) {
+    throw new Error(`${action}接口调用失败[${status} - ${statusMessage}]`);
+  }
+  if (data.Response.Error) {
+    throw new Error(data.Response.Error.Message);
+  }
+  return data.Response;
+}
+
+module.exports = { request };

+ 26 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/tiia/config.js

@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+// 腾讯云uni-app 图像识别插件依赖云函数的配置
+// 可用地域列表参数
+// 华北地区(北京)	ap-beijing
+// 华南地区(广州)	ap-guangzhou
+// 华东地区(上海)	ap-shanghai
+module.exports = {
+  region: 'ap-guangzhou' // 地域列表
+};

+ 45 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/tiia/index.js

@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+const { request } = require('./utils.js');
+
+/**
+ * 获取图像识别结果
+ * @param {object} params - 参数包装对象
+ * @param {string} params.name - 对应图像识别 API的Action值 https://cloud.tencent.com/document/product/865/35462
+ * @param {object} params.payload - API需要的参数
+ * @param {string} [params.payload.ImageBase64] - 图片的 Base64 值。支持的图片格式:PNG、JPG、JPEG,暂不支持 GIF 格式。支持的图片大小:所下载图片经 Base64 编码后不超过 3M。图片下载时间不超过 3 秒。
+ * @param {string} [params.payload.ImageUrl] - 图片的 Url 地址。支持的图片格式:PNG、JPG、JPEG,暂不支持 GIF 格式。支持的图片大小:所下载图片经 Base64 编码后不超过 3M。图片下载时间不超过 3 秒。
+ * @returns {object} API返回的有效数据
+ */
+async function getTiiaResult(params) {
+  if (!params.name) {
+    throw new Error('缺少API Action参数');
+  }
+  if (!(params.payload.ImageBase64 || params.payload.ImageUrl)) {
+    throw new Error('ImageUrl 和 ImageBase64 必须有一个不为空');
+  }
+  // 调用图像识别接口
+  try {
+    const result = await request(params.name, params.payload);
+    return result;
+  } catch (e) {
+    throw new Error(e);
+  }
+}
+
+module.exports = { getTiiaResult };

+ 55 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/tiia/utils.js

@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const axios = require('axios');
+const { region } = require('./config');
+const { sign } = require('../common');
+
+/**
+ * 请求腾讯云图像识别接口公共方法
+ * @param {string} action - 接口请求action
+ * @param {object} payload - 接口请求体
+ * @returns {object} API返回的有效数据
+ */
+async function request(action, payload) {
+  const [timestamp, authorization] = sign('tiia', JSON.stringify(payload));
+  const options = {
+    url: 'https://tiia.tencentcloudapi.com',
+    method: 'POST',
+    headers: {
+      'Content-Type': 'application/json',
+      'X-TC-Action': action,
+      'X-TC-Version': '2019-05-29',
+      'X-TC-Timestamp': timestamp,
+      'X-TC-Region': region,
+      Authorization: authorization
+    },
+    data: payload
+  };
+  const response = await axios(options);
+  const { status, statusText, data } = response;
+  if (status !== 200) {
+    throw new Error(`${action}接口调用失败[${status} - ${statusText}]`);
+  }
+  if (data.Response.Error) {
+    throw new Error(data.Response.Error.Message);
+  }
+  return data.Response;
+}
+
+module.exports = { request };

+ 27 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/tms/index.js

@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const textModeration = require('./text-moderation');
+const { listTextSample, createTextSample, deleteTextSample } = require('./text-sample');
+
+module.exports = {
+  textModeration,
+  listTextSample,
+  createTextSample,
+  deleteTextSample
+};

+ 39 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/tms/text-moderation.js

@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const { request } = require('./utils');
+
+/**
+ * 文本内容检测
+ * 更多信息请访问 https://cloud.tencent.com/document/product/669/34502
+ * @param {object} params - 参数包装对象
+ * @param {string} params.content - 需要识别的文本内容
+ * @return {Promise<object>} 识别结果
+ */
+async function textModeration({ content }) {
+  if (!content) {
+    throw new Error('文本内容不能为空');
+  }
+  // 调用腾讯云查询接口
+  const { Data } = await request('TextModeration', {
+    Content: Buffer.from(content).toString('base64')
+  });
+  return Data;
+}
+
+module.exports = textModeration;

+ 85 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/tms/text-sample.js

@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const { request } = require('./utils');
+
+/**
+ * 查询文本样本
+ * 更多信息请访问 https://cloud.tencent.com/document/product/669/35618
+ * @param {object} params - 参数包装对象
+ * @param {int} params.limit - 数量限制
+ * @param {int} params.offset - 偏移量
+ * @return {Promise<object>} 文本样本的信息
+ */
+async function listTextSample({ limit = 20, offset = 0 }) {
+  // 调用腾讯云接口
+  const { TextSampleSet, TotalCount } = await request('DescribeTextSample', {
+    Limit: limit,
+    Offset: offset
+  });
+  return {
+    TextSampleSet,
+    TotalCount
+  };
+}
+
+/**
+ * 新增文本样本
+ * 更多信息请访问 https://cloud.tencent.com/document/product/669/35620
+ * @param {object} params - 参数包装对象
+ * @param {string[]} params.contents - 关键词数组
+ * @param {string} params.evilType - 恶意类型
+ * @param {string} params.label - 样本类型 1:黑库 2:白库
+ * @return {Promise<int>} 任务状态
+ */
+async function createTextSample({ contents, evilType, label }) {
+  if (!contents || !contents.length || !evilType || !label) {
+    throw new Error('关键词/恶意类型/样本类型不能为空');
+  }
+  // 调用腾讯云接口
+  const { Progress } = await request('CreateTextSample', {
+    Contents: contents,
+    EvilType: evilType,
+    Label: label
+  });
+  return Progress;
+}
+
+/**
+ * 删除文本样本
+ * 更多信息请访问 https://cloud.tencent.com/document/product/669/35619
+ * @param {object} params - 参数包装对象
+ * @param {string[]} params.ids - 唯一标识数组
+ * @return {Promise<int>} 任务状态
+ */
+async function deleteTextSample({ ids }) {
+  if (!ids || !ids.length) {
+    throw new Error('唯一标识不能为空');
+  }
+  // 调用腾讯云接口
+  const { Progress } = await request('DeleteTextSample', {
+    Ids: ids
+  });
+  return Progress;
+}
+
+module.exports = {
+  listTextSample,
+  createTextSample,
+  deleteTextSample
+};

+ 52 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/tms/utils.js

@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const { sign } = require('../common');
+
+/**
+ * 请求腾讯云CMS接口公共方法
+ * @param {string} action - 接口请求action
+ * @param {object} payload - 接口请求体
+ * @return {object} API返回的有效数据
+ */
+async function request(action, payload) {
+  const [timestamp, authorization] = sign('cms', JSON.stringify(payload));
+  const { res } = await uniCloud.httpclient.request('https://cms.tencentcloudapi.com', {
+    method: 'POST',
+    headers: {
+      'Content-Type': 'application/json',
+      'X-TC-Action': action,
+      'X-TC-Region': 'ap-guangzhou',
+      'X-TC-Version': '2019-03-21',
+      'X-TC-Timestamp': timestamp,
+      Authorization: authorization
+    },
+    data: payload,
+    dataType: 'json'
+  });
+  const { status, statusMessage, data } = res;
+  if (status !== 200) {
+    throw new Error(`${action}接口调用失败[${status} - ${statusMessage}]`);
+  }
+  if (data.Response.Error) {
+    throw new Error(data.Response.Error.Message);
+  }
+  return data.Response;
+}
+
+module.exports = { request };

+ 29 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/tmt/config.js

@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+// 腾讯云uni-app 机器翻译插件依赖云函数的配置
+// 可用地域列表参数
+// 华北地区(北京)	ap-beijing
+// 华南地区(广州)	ap-guangzhou
+// 华东地区(上海)	ap-shanghai
+// 华东地区(南京) ap-nanjing
+// 西南地区(成都)	ap-chengdu
+// 西南地区(重庆) ap-chongqing
+module.exports = {
+  region: 'ap-guangzhou' // 地域列表
+};

+ 41 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/tmt/index.js

@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+const { request } = require('./utils.js');
+
+/**
+ * 获取机器翻译结果
+ * @param {object} params - 参数包装对象
+ * @param {string} params.name - 机器翻译 API的Action值 https://cloud.tencent.com/document/product/551/15612
+ * @param {object} params.payload - API需要的参数
+ * @return {Promise<object>} API返回数据
+ */
+async function getTmtResult(params) {
+  if (!params.name) {
+    throw new Error('缺少API Action参数');
+  }
+  try {
+    const result = await request(params.name, params.payload);
+    return result;
+  } catch (e) {
+    throw new Error(e);
+  }
+}
+
+module.exports = {
+  getTmtResult
+};

+ 58 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/tmt/utils.js

@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const axios = require('axios');
+const { region } = require('./config');
+const { sign } = require('../common');
+
+/**
+ * 请求腾讯云机器翻译公共方法
+ * @param {string} action - 接口请求action
+ * @param {object} payload - 接口请求体
+ * @returns {object} API返回的有效数据
+ */
+async function request(action, payload) {
+  if (!region) {
+    throw new Error('请在云函数TMT模块中配置region');
+  }
+  const [timestamp, authorization] = sign('tmt', JSON.stringify(payload));
+  const options = {
+    url: 'https://tmt.tencentcloudapi.com',
+    method: 'POST',
+    headers: {
+      'Content-Type': 'application/json',
+      'X-TC-Action': action,
+      'X-TC-Version': '2018-03-21',
+      'X-TC-Timestamp': timestamp,
+      'X-TC-Region': region,
+      Authorization: authorization
+    },
+    data: payload
+  };
+  const response = await axios(options);
+  const { status, statusText, data } = response;
+  if (status !== 200) {
+    throw new Error(`${action}接口调用失败[${status} - ${statusText}]`);
+  }
+  if (data.Response.Error) {
+    throw new Error(data.Response.Error.Message);
+  }
+  return data.Response;
+}
+
+module.exports = { request };

+ 29 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/tts/config.js

@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+// 腾讯云uni-app 语音合成插件依赖云函数的配置
+// 可用地域列表参数
+// 华北地区(北京)	ap-beijing
+// 华南地区(广州)	ap-guangzhou
+// 华东地区(上海)	ap-shanghai
+// 华东地区(南京) ap-nanjing
+// 西南地区(成都)	ap-chengdu
+// 西南地区(重庆) ap-chongqing
+module.exports = {
+  region: 'ap-guangzhou' // 地域列表
+};

+ 37 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/tts/index.js

@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const { request } = require('./utils');
+
+/**
+ * 获取基础语音合成结果
+ * 更多信息请访问 https://cloud.tencent.com/document/product/1073/37995
+ * @param {object} params - 参数包装对象
+ * @param {object} param - 语音合成接口相关参数
+ * @return {Promise<object>} 评测信息
+ */
+async function getTtsResult({ param }) {
+  // 调用腾讯云基础语音合成接口
+  const result = await request('TextToVoice', param);
+
+  return result;
+}
+
+module.exports = {
+  getTtsResult
+};

+ 58 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/tts/utils.js

@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const axios = require('axios');
+const { region } = require('./config');
+const { sign } = require('../common');
+
+/**
+ * 请求腾讯云基础语音合成公共方法
+ * @param {string} action - 接口请求action
+ * @param {object} payload - 接口请求体
+ * @returns {object} API返回的有效数据
+ */
+async function request(action, payload) {
+  if (!region) {
+    throw new Error('请在云函数TTS模块中配置region');
+  }
+  const [timestamp, authorization] = sign('tts', JSON.stringify(payload));
+  const options = {
+    url: 'https://tts.tencentcloudapi.com',
+    method: 'POST',
+    headers: {
+      'Content-Type': 'application/json',
+      'X-TC-Action': action,
+      'X-TC-Version': '2019-08-23',
+      'X-TC-Timestamp': timestamp,
+      'X-TC-Region': region,
+      Authorization: authorization
+    },
+    data: payload
+  };
+  const response = await axios(options);
+  const { status, statusText, data } = response;
+  if (status !== 200) {
+    throw new Error(`${action}接口调用失败[${status} - ${statusText}]`);
+  }
+  if (data.Response.Error) {
+    throw new Error(data.Response.Error.Message);
+  }
+  return data.Response;
+}
+
+module.exports = { request };

+ 24 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/vod/config.js

@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+// 腾讯云uni-app云点播VOD插件依赖云函数的配置
+module.exports = {
+  uploadExpires: 60, // 上传签名有效期(单位:秒)
+  antiTheftKey: '', // 防盗链key,如果未开启Key防盗链,请留空此项
+  antiTheftExpires: 60 // 防盗链接有效时长(单位:分钟)
+};

+ 48 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/vod/get-anti-theft-url.js

@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const crypto = require('crypto');
+const { antiTheftKey, antiTheftExpires } = require('./config');
+
+/**
+ * 获取云点播文件防盗链链接
+ * 更多信息请访问 https://cloud.tencent.com/document/product/266/14047
+ * @param {object} params - 参数包装对象
+ * @param {string} params.mediaUrl - 云点播文件地址
+ * @return {string} 云点播文件完整url(包含签名)
+ */
+function getAntiTheftURL({ mediaUrl }) {
+  // 配置校验
+  if (!antiTheftKey) {
+    throw new Error('请在云函数VOD模块中配置有效的antiTheftKey');
+  }
+  if (isNaN(antiTheftExpires) || antiTheftExpires <= 0) {
+    throw new Error('请在云函数VOD模块中配置有效的antiTheftExpires');
+  }
+  // 生成包含签名的url
+  const result = new RegExp('^https?://[^/]*(.*/)[^/]*$').exec(mediaUrl);
+  if (!result) {
+    throw new Error('无效的媒体文件Url');
+  }
+  const dir = result[1];
+  const t = Math.floor(new Date().getTime() / 1000 + antiTheftExpires * 60).toString(16);
+  const sign = crypto.createHash('md5').update(`${antiTheftKey}${dir}${t}`).digest('hex');
+  return `${mediaUrl}?t=${t}&sign=${sign}`;
+}
+
+module.exports = getAntiTheftURL;

+ 49 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/vod/get-media-info.js

@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const { request } = require('./utils');
+const getAntiTheftURL = require('./get-anti-theft-url.js');
+const { antiTheftKey } = require('./config');
+
+/**
+ * 获取腾讯云云点播媒体详细信息
+ * 更多信息请访问 https://cloud.tencent.com/document/api/266/31763
+ * @param {object} params - 参数包装对象
+ * @param {string[]} params.mediaId - 媒体ID
+ * @return {Promise<object>} 媒体详细信息
+ */
+async function getUploadSignature({ mediaId }) {
+  if (!mediaId) {
+    throw new Error('待查询媒体ID不能为空');
+  }
+  // 调用腾讯云查询接口
+  const {
+    MediaInfoSet: [mediaInfo]
+  } = await request('DescribeMediaInfos', {
+    FileIds: [mediaId]
+  });
+  // 如果有配置antiTheftKey则自动生成带签名的url
+  if (mediaInfo && antiTheftKey) {
+    mediaInfo.BasicInfo.AntiTheftUrl = getAntiTheftURL({
+      mediaUrl: mediaInfo.BasicInfo.MediaUrl
+    });
+  }
+  return mediaInfo;
+}
+
+module.exports = getUploadSignature;

+ 52 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/vod/get-upload-signature.js

@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const crypto = require('crypto');
+const querystring = require('querystring');
+const { secretId, secretKey } = require('../config');
+const { uploadExpires } = require('./config');
+
+/**
+ * 获取腾讯云云点播文件上传签名
+ * 更多信息请访问 https://cloud.tencent.com/document/product/266/9221
+ * @return {string} 云点播文件上传签名
+ */
+function getUploadSignature() {
+  // 配置校验
+  if (!secretId || !secretKey) {
+    throw new Error('请云函数配置文件中配置secretId和secretKey');
+  }
+  if (isNaN(uploadExpires) || uploadExpires <= 0) {
+    throw new Error('请在云函数VOD模块中配置有效的uploadExpires');
+  }
+  // 生成签名信息
+  const current = Math.floor(new Date().getTime() / 1000);
+  const args = {
+    secretId,
+    currentTimeStamp: current,
+    expireTime: current + uploadExpires,
+    random: Math.round(Math.random() * Math.pow(2, 32))
+  };
+  const orignal = querystring.stringify(args);
+  const orignalBuffer = new Buffer(orignal, 'utf8');
+  const hmacBuffer = crypto.createHmac('sha1', secretKey).update(orignalBuffer).digest();
+  const signature = Buffer.concat([hmacBuffer, orignalBuffer]).toString('base64');
+  return signature;
+}
+
+module.exports = getUploadSignature;

+ 27 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/vod/index.js

@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const getUploadSignature = require('./get-upload-signature');
+const getMediaInfo = require('./get-media-info');
+const getAntiTheftURL = require('./get-anti-theft-url.js');
+
+module.exports = {
+  getUploadSignature,
+  getMediaInfo,
+  getAntiTheftURL
+};

+ 51 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/vod/utils.js

@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2020 Tencent Cloud.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const { sign } = require('../common');
+
+/**
+ * 请求腾讯云VOD接口公共方法
+ * @param {string} action - 接口请求action
+ * @param {object} payload - 接口请求体
+ * @return {object} API返回的有效数据
+ */
+async function request(action, payload) {
+  const [timestamp, authorization] = sign('vod', JSON.stringify(payload));
+  const { res } = await uniCloud.httpclient.request('https://vod.tencentcloudapi.com', {
+    method: 'POST',
+    headers: {
+      'Content-Type': 'application/json',
+      'X-TC-Action': action,
+      'X-TC-Version': '2018-07-17',
+      'X-TC-Timestamp': timestamp,
+      Authorization: authorization
+    },
+    data: payload,
+    dataType: 'json'
+  });
+  const { status, statusMessage, data } = res;
+  if (status !== 200) {
+    throw new Error(`${action}接口调用失败[${status} - ${statusMessage}]`);
+  }
+  if (data.Response.Error) {
+    throw new Error(data.Response.Error.Message);
+  }
+  return data.Response;
+}
+
+module.exports = { request };

+ 54 - 0
uniCloud-aliyun/cloudfunctions/tencentcloud-plugin/yarn.lock

@@ -0,0 +1,54 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+asynckit@^0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
+  integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
+
+axios@^0.27.2:
+  version "0.27.2"
+  resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972"
+  integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==
+  dependencies:
+    follow-redirects "^1.14.9"
+    form-data "^4.0.0"
+
+combined-stream@^1.0.8:
+  version "1.0.8"
+  resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
+  integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
+  dependencies:
+    delayed-stream "~1.0.0"
+
+delayed-stream@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
+  integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
+
+follow-redirects@^1.14.9:
+  version "1.15.2"
+  resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13"
+  integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==
+
+form-data@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
+  integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
+  dependencies:
+    asynckit "^0.4.0"
+    combined-stream "^1.0.8"
+    mime-types "^2.1.12"
+
+mime-db@1.52.0:
+  version "1.52.0"
+  resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
+  integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
+
+mime-types@^2.1.12:
+  version "2.1.35"
+  resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
+  integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
+  dependencies:
+    mime-db "1.52.0"

+ 491 - 108
yarn.lock

@@ -3,129 +3,512 @@
 
 
 "@babel/parser@^7.18.4":
-  "integrity" "sha512-74bEXKX2h+8rrfQUfsBfuZZHzsEs6Eql4pqy/T4Nn6Y9wNPggQOqD6z6pn5Bl8ZfysKouFZT/UXEH94ummEeQw=="
-  "resolved" "https://registry.npmjs.org/@babel/parser/-/parser-7.19.0.tgz"
-  "version" "7.19.0"
+  version "7.19.0"
+  resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.19.0.tgz"
+  integrity sha512-74bEXKX2h+8rrfQUfsBfuZZHzsEs6Eql4pqy/T4Nn6Y9wNPggQOqD6z6pn5Bl8ZfysKouFZT/UXEH94ummEeQw==
 
 "@types/html5plus@^1.0.2":
-  "integrity" "sha512-OklP5lrmLq8/6TUOLgWc0LndUVvAiTWX5dnyoCFhIUtFW9opWsnCtG/UxPgeuC28Rv2XNbFfft/hEEI39P/4Ag=="
-  "resolved" "https://registry.npmjs.org/@types/html5plus/-/html5plus-1.0.2.tgz"
-  "version" "1.0.2"
+  version "1.0.2"
+  resolved "https://registry.npmjs.org/@types/html5plus/-/html5plus-1.0.2.tgz"
+  integrity sha512-OklP5lrmLq8/6TUOLgWc0LndUVvAiTWX5dnyoCFhIUtFW9opWsnCtG/UxPgeuC28Rv2XNbFfft/hEEI39P/4Ag==
 
 "@types/uni-app@^1.4.4":
-  "integrity" "sha512-ZTXnrCTblZyoLIoKbTv1Whz1nxrTcM7vg0qGXzDpXP8m9MqdjKt48N3FffQT1IsJWNkxbvJ1Eg5UHDaq+k+oBQ=="
-  "resolved" "https://registry.npmjs.org/@types/uni-app/-/uni-app-1.4.4.tgz"
-  "version" "1.4.4"
+  version "1.4.4"
+  resolved "https://registry.npmjs.org/@types/uni-app/-/uni-app-1.4.4.tgz"
+  integrity sha512-ZTXnrCTblZyoLIoKbTv1Whz1nxrTcM7vg0qGXzDpXP8m9MqdjKt48N3FffQT1IsJWNkxbvJ1Eg5UHDaq+k+oBQ==
   dependencies:
-    "vue" "^2.6.8"
+    vue "^2.6.8"
 
 "@vue/compiler-sfc@2.7.10":
-  "integrity" "sha512-55Shns6WPxlYsz4WX7q9ZJBL77sKE1ZAYNYStLs6GbhIOMrNtjMvzcob6gu3cGlfpCR4bT7NXgyJ3tly2+Hx8Q=="
-  "resolved" "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.10.tgz"
-  "version" "2.7.10"
+  version "2.7.10"
+  resolved "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.10.tgz"
+  integrity sha512-55Shns6WPxlYsz4WX7q9ZJBL77sKE1ZAYNYStLs6GbhIOMrNtjMvzcob6gu3cGlfpCR4bT7NXgyJ3tly2+Hx8Q==
   dependencies:
     "@babel/parser" "^7.18.4"
-    "postcss" "^8.4.14"
-    "source-map" "^0.6.1"
+    postcss "^8.4.14"
+    source-map "^0.6.1"
 
 "@xmldom/xmldom@^0.8.6":
-  "integrity" "sha512-0LNz4EY8B/8xXY86wMrQ4tz6zEHZv9ehFMJPm8u2gq5lQ71cfRKdaKyxfJAx5aUoyzx0qzgURblTisPGgz3d+Q=="
-  "resolved" "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.8.tgz"
-  "version" "0.8.8"
+  version "0.8.8"
+  resolved "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.8.tgz"
+  integrity sha512-0LNz4EY8B/8xXY86wMrQ4tz6zEHZv9ehFMJPm8u2gq5lQ71cfRKdaKyxfJAx5aUoyzx0qzgURblTisPGgz3d+Q==
 
-"animate.css@^4.1.1":
-  "integrity" "sha512-+mRmCTv6SbCmtYJCN4faJMNFVNN5EuCTTprDTAo7YzIGji2KADmakjVA3+8mVDkZ2Bf09vayB35lSQIex2+QaQ=="
-  "resolved" "https://registry.npmjs.org/animate.css/-/animate.css-4.1.1.tgz"
-  "version" "4.1.1"
+ajv@^6.12.3:
+  version "6.12.6"
+  resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
+  integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
+  dependencies:
+    fast-deep-equal "^3.1.1"
+    fast-json-stable-stringify "^2.0.0"
+    json-schema-traverse "^0.4.1"
+    uri-js "^4.2.2"
+
+animate.css@^4.1.1:
+  version "4.1.1"
+  resolved "https://registry.npmjs.org/animate.css/-/animate.css-4.1.1.tgz"
+  integrity sha512-+mRmCTv6SbCmtYJCN4faJMNFVNN5EuCTTprDTAo7YzIGji2KADmakjVA3+8mVDkZ2Bf09vayB35lSQIex2+QaQ==
+
+asn1@~0.2.3:
+  version "0.2.6"
+  resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d"
+  integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==
+  dependencies:
+    safer-buffer "~2.1.0"
+
+assert-plus@1.0.0, assert-plus@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
+  integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==
+
+async@^2.2.0:
+  version "2.6.4"
+  resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221"
+  integrity sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==
+  dependencies:
+    lodash "^4.17.14"
+
+asynckit@^0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
+  integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
+
+aws-sign2@~0.7.0:
+  version "0.7.0"
+  resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
+  integrity sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==
+
+aws4@^1.8.0:
+  version "1.12.0"
+  resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.12.0.tgz#ce1c9d143389679e253b314241ea9aa5cec980d3"
+  integrity sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==
+
+bcrypt-pbkdf@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e"
+  integrity sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==
+  dependencies:
+    tweetnacl "^0.14.3"
+
+caseless@~0.12.0:
+  version "0.12.0"
+  resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
+  integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==
 
-"cos-js-sdk-v5@^1.4.18":
-  "integrity" "sha512-vTy8p59qnEoNJH/1chBSU8lSJDEcKV5PeOlAngmPbmHEwoMNJmKmA71nnquGIWHI4KpR4n57yC+RKP6RZkgsBg=="
-  "resolved" "https://registry.npmjs.org/cos-js-sdk-v5/-/cos-js-sdk-v5-1.4.18.tgz"
-  "version" "1.4.18"
+combined-stream@^1.0.6, combined-stream@~1.0.6:
+  version "1.0.8"
+  resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
+  integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
+  dependencies:
+    delayed-stream "~1.0.0"
+
+core-util-is@1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
+  integrity sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==
+
+cos-js-sdk-v5@^1.4.18:
+  version "1.4.18"
+  resolved "https://registry.npmjs.org/cos-js-sdk-v5/-/cos-js-sdk-v5-1.4.18.tgz"
+  integrity sha512-vTy8p59qnEoNJH/1chBSU8lSJDEcKV5PeOlAngmPbmHEwoMNJmKmA71nnquGIWHI4KpR4n57yC+RKP6RZkgsBg==
   dependencies:
     "@xmldom/xmldom" "^0.8.6"
 
-"crypto-js@^4.1.1":
-  "integrity" "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw=="
-  "resolved" "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz"
-  "version" "4.1.1"
-
-"csstype@^3.1.0":
-  "integrity" "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
-  "resolved" "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz"
-  "version" "3.1.0"
-
-"inobounce@^0.2.1":
-  "integrity" "sha512-dmKhRDbUS3zGD8HDGchsZBuxaXnfFM+2jXrZpnEnBToEWCgcs3lBfCQe0wzkbpIoJwU/lufaMquSyWoX8OXTRw=="
-  "resolved" "https://registry.npmjs.org/inobounce/-/inobounce-0.2.1.tgz"
-  "version" "0.2.1"
-
-"jquery@^3.6.4":
-  "integrity" "sha512-v28EW9DWDFpzcD9O5iyJXg3R3+q+mET5JhnjJzQUZMHOv67bpSIHq81GEYpPNZHG+XXHsfSme3nxp/hndKEcsQ=="
-  "resolved" "https://registry.npmjs.org/jquery/-/jquery-3.6.4.tgz"
-  "version" "3.6.4"
-
-"js-base64@^2.6.4":
-  "integrity" "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ=="
-  "resolved" "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz"
-  "version" "2.6.4"
-
-"nanoid@^3.3.4":
-  "integrity" "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw=="
-  "resolved" "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz"
-  "version" "3.3.4"
-
-"picocolors@^1.0.0":
-  "integrity" "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
-  "resolved" "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz"
-  "version" "1.0.0"
-
-"postcss@^8.4.14":
-  "integrity" "sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ=="
-  "resolved" "https://registry.npmjs.org/postcss/-/postcss-8.4.16.tgz"
-  "version" "8.4.16"
-  dependencies:
-    "nanoid" "^3.3.4"
-    "picocolors" "^1.0.0"
-    "source-map-js" "^1.0.2"
-
-"source-map-js@^1.0.2":
-  "integrity" "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw=="
-  "resolved" "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz"
-  "version" "1.0.2"
-
-"source-map@^0.6.1":
-  "integrity" "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
-  "resolved" "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz"
-  "version" "0.6.1"
-
-"uni-read-pages@^1.0.5":
-  "integrity" "sha512-GkrrZ0LX0vn9R5k6RKEi0Ez3Q3e2vUpjXQ8Z6/K/d28KudI9ajqgt8WEjQFlG5EPm1K6uTArN8LlqmZTEixDUA=="
-  "resolved" "https://registry.npmmirror.com/uni-read-pages/-/uni-read-pages-1.0.5.tgz"
-  "version" "1.0.5"
-
-"uni-simple-router@^2.0.7":
-  "integrity" "sha512-8FKv5dw7Eoonm0gkO8udprrxzin0fNUI0+AvIphFkFRH5ZmP5ZWJ2pvnWzb2NiiqQSECTSU5VSB7HhvOSwD5eA=="
-  "resolved" "https://registry.npmmirror.com/uni-simple-router/-/uni-simple-router-2.0.7.tgz"
-  "version" "2.0.7"
-
-"uploading-oss@^1.0.3":
-  "integrity" "sha512-aqHh5NCOBcrA4d8yCIKR7B9GrKzH88X7gL8BSvIw18pi79AgWnhDkCoyjQmDqzTvjQqYfKwyFTEgnafzMx/GbQ=="
-  "resolved" "https://registry.npmjs.org/uploading-oss/-/uploading-oss-1.0.3.tgz"
-  "version" "1.0.3"
-  dependencies:
-    "crypto-js" "^4.1.1"
-    "js-base64" "^2.6.4"
-
-"vue-waterfall-easy@^2.4.4":
-  "integrity" "sha512-5OkpT2FPNC3rHBy858zk/nmJxqdPaGmj/KVbmA6dgcvtsovKMa+zuf/Z7F+S2NnObeavpIBztTWgcH3S42ZD+g=="
-  "resolved" "https://registry.npmjs.org/vue-waterfall-easy/-/vue-waterfall-easy-2.4.4.tgz"
-  "version" "2.4.4"
-
-"vue@^2.6.8":
-  "integrity" "sha512-HmFC70qarSHPXcKtW8U8fgIkF6JGvjEmDiVInTkKZP0gIlEPhlVlcJJLkdGIDiNkIeA2zJPQTWJUI4iWe+AVfg=="
-  "resolved" "https://registry.npmjs.org/vue/-/vue-2.7.10.tgz"
-  "version" "2.7.10"
+cos-nodejs-sdk-v5@^1.*:
+  version "1.2.7"
+  resolved "https://registry.yarnpkg.com/cos-nodejs-sdk-v5/-/cos-nodejs-sdk-v5-1.2.7.tgz#b4127b1b3ecd9a605c5a8b461eaebe6bef29c868"
+  integrity sha512-a05KGF9Rtgb8xQYhtIBfRAVjsyAZ21845yw2RExjluGi0+WkMclpYqePTfVauZc2YrsczFx/Zi0sFl1ElwvxoQ==
+  dependencies:
+    async "^2.2.0"
+    eventproxy "^0.3.5"
+    lodash "^4.17.4"
+    request "^2.81.0"
+    xml2js "^0.4.17"
+
+crypto-js@^4.1.1:
+  version "4.1.1"
+  resolved "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz"
+  integrity sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==
+
+crypto@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/crypto/-/crypto-1.0.1.tgz#2af1b7cad8175d24c8a1b0778255794a21803037"
+  integrity sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==
+
+csstype@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz"
+  integrity sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==
+
+dashdash@^1.12.0:
+  version "1.14.1"
+  resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
+  integrity sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==
+  dependencies:
+    assert-plus "^1.0.0"
+
+debug@2.2.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-2.2.0.tgz#f87057e995b1a1f6ae6a4960664137bc56f039da"
+  integrity sha512-X0rGvJcskG1c3TgSCPqHJ0XJgwlcvOC7elJ5Y0hYuKBZoVqWpAMfLOeIh2UI/DCQ5ruodIjvsugZtjUYUw2pUw==
+  dependencies:
+    ms "0.7.1"
+
+delayed-stream@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
+  integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
+
+ecc-jsbn@~0.1.1:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9"
+  integrity sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==
+  dependencies:
+    jsbn "~0.1.0"
+    safer-buffer "^2.1.0"
+
+eventproxy@^0.3.5:
+  version "0.3.5"
+  resolved "https://registry.yarnpkg.com/eventproxy/-/eventproxy-0.3.5.tgz#4db3290dcbc51cf067cbb6752e3c40b5d917212f"
+  integrity sha512-eipueS12OFnjFaFRZ/zwJhqlkJRWQmMGVzmN/738ovyFpFp5vsuB9qNckmJmwTJARkd4cV6nmHY+rrTjSGp11A==
+  dependencies:
+    debug "2.2.0"
+
+extend@~3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
+  integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
+
+extsprintf@1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"
+  integrity sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==
+
+extsprintf@^1.2.0:
+  version "1.4.1"
+  resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07"
+  integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==
+
+fast-deep-equal@^3.1.1:
+  version "3.1.3"
+  resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
+  integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
+
+fast-json-stable-stringify@^2.0.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
+  integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
+
+forever-agent@~0.6.1:
+  version "0.6.1"
+  resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
+  integrity sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==
+
+form-data@~2.3.2:
+  version "2.3.3"
+  resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
+  integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==
+  dependencies:
+    asynckit "^0.4.0"
+    combined-stream "^1.0.6"
+    mime-types "^2.1.12"
+
+getpass@^0.1.1:
+  version "0.1.7"
+  resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
+  integrity sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==
+  dependencies:
+    assert-plus "^1.0.0"
+
+har-schema@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
+  integrity sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==
+
+har-validator@~5.1.3:
+  version "5.1.5"
+  resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd"
+  integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==
+  dependencies:
+    ajv "^6.12.3"
+    har-schema "^2.0.0"
+
+http-signature@~1.2.0:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"
+  integrity sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==
+  dependencies:
+    assert-plus "^1.0.0"
+    jsprim "^1.2.2"
+    sshpk "^1.7.0"
+
+inobounce@^0.2.1:
+  version "0.2.1"
+  resolved "https://registry.npmjs.org/inobounce/-/inobounce-0.2.1.tgz"
+  integrity sha512-dmKhRDbUS3zGD8HDGchsZBuxaXnfFM+2jXrZpnEnBToEWCgcs3lBfCQe0wzkbpIoJwU/lufaMquSyWoX8OXTRw==
+
+is-typedarray@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
+  integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==
+
+isstream@~0.1.2:
+  version "0.1.2"
+  resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
+  integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==
+
+jquery@^3.6.4:
+  version "3.6.4"
+  resolved "https://registry.npmjs.org/jquery/-/jquery-3.6.4.tgz"
+  integrity sha512-v28EW9DWDFpzcD9O5iyJXg3R3+q+mET5JhnjJzQUZMHOv67bpSIHq81GEYpPNZHG+XXHsfSme3nxp/hndKEcsQ==
+
+js-base64@^2.6.4:
+  version "2.6.4"
+  resolved "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz"
+  integrity sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==
+
+jsbn@~0.1.0:
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
+  integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==
+
+json-schema-traverse@^0.4.1:
+  version "0.4.1"
+  resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
+  integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
+
+json-schema@0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5"
+  integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==
+
+json-stringify-safe@~5.0.1:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
+  integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==
+
+jsprim@^1.2.2:
+  version "1.4.2"
+  resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.2.tgz#712c65533a15c878ba59e9ed5f0e26d5b77c5feb"
+  integrity sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==
+  dependencies:
+    assert-plus "1.0.0"
+    extsprintf "1.3.0"
+    json-schema "0.4.0"
+    verror "1.10.0"
+
+lodash@^4.17.14, lodash@^4.17.4:
+  version "4.17.21"
+  resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
+  integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
+
+mime-db@1.52.0:
+  version "1.52.0"
+  resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
+  integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
+
+mime-types@^2.1.12, mime-types@~2.1.19:
+  version "2.1.35"
+  resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
+  integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
+  dependencies:
+    mime-db "1.52.0"
+
+ms@0.7.1:
+  version "0.7.1"
+  resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098"
+  integrity sha512-lRLiIR9fSNpnP6TC4v8+4OU7oStC01esuNowdQ34L+Gk8e5Puoc88IqJ+XAY/B3Mn2ZKis8l8HX90oU8ivzUHg==
+
+nanoid@^3.3.4:
+  version "3.3.4"
+  resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz"
+  integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==
+
+oauth-sign@~0.9.0:
+  version "0.9.0"
+  resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
+  integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==
+
+performance-now@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
+  integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==
+
+picocolors@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz"
+  integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
+
+postcss@^8.4.14:
+  version "8.4.16"
+  resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.16.tgz"
+  integrity sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==
+  dependencies:
+    nanoid "^3.3.4"
+    picocolors "^1.0.0"
+    source-map-js "^1.0.2"
+
+psl@^1.1.28:
+  version "1.9.0"
+  resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7"
+  integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==
+
+punycode@^2.1.0, punycode@^2.1.1:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f"
+  integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==
+
+qs@~6.5.2:
+  version "6.5.3"
+  resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad"
+  integrity sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==
+
+request@^2.81.0:
+  version "2.88.2"
+  resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3"
+  integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==
+  dependencies:
+    aws-sign2 "~0.7.0"
+    aws4 "^1.8.0"
+    caseless "~0.12.0"
+    combined-stream "~1.0.6"
+    extend "~3.0.2"
+    forever-agent "~0.6.1"
+    form-data "~2.3.2"
+    har-validator "~5.1.3"
+    http-signature "~1.2.0"
+    is-typedarray "~1.0.0"
+    isstream "~0.1.2"
+    json-stringify-safe "~5.0.1"
+    mime-types "~2.1.19"
+    oauth-sign "~0.9.0"
+    performance-now "^2.1.0"
+    qs "~6.5.2"
+    safe-buffer "^5.1.2"
+    tough-cookie "~2.5.0"
+    tunnel-agent "^0.6.0"
+    uuid "^3.3.2"
+
+safe-buffer@^5.0.1, safe-buffer@^5.1.2:
+  version "5.2.1"
+  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
+  integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
+
+safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
+  version "2.1.2"
+  resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
+  integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
+
+sax@>=0.6.0:
+  version "1.2.4"
+  resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
+  integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
+
+source-map-js@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz"
+  integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
+
+source-map@^0.6.1:
+  version "0.6.1"
+  resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz"
+  integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
+
+sshpk@^1.7.0:
+  version "1.17.0"
+  resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.17.0.tgz#578082d92d4fe612b13007496e543fa0fbcbe4c5"
+  integrity sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==
+  dependencies:
+    asn1 "~0.2.3"
+    assert-plus "^1.0.0"
+    bcrypt-pbkdf "^1.0.0"
+    dashdash "^1.12.0"
+    ecc-jsbn "~0.1.1"
+    getpass "^0.1.1"
+    jsbn "~0.1.0"
+    safer-buffer "^2.0.2"
+    tweetnacl "~0.14.0"
+
+tough-cookie@~2.5.0:
+  version "2.5.0"
+  resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2"
+  integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==
+  dependencies:
+    psl "^1.1.28"
+    punycode "^2.1.1"
+
+tunnel-agent@^0.6.0:
+  version "0.6.0"
+  resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
+  integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==
+  dependencies:
+    safe-buffer "^5.0.1"
+
+tweetnacl@^0.14.3, tweetnacl@~0.14.0:
+  version "0.14.5"
+  resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"
+  integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==
+
+uni-read-pages@^1.0.5:
+  version "1.0.5"
+  resolved "https://registry.npmmirror.com/uni-read-pages/-/uni-read-pages-1.0.5.tgz"
+  integrity sha512-GkrrZ0LX0vn9R5k6RKEi0Ez3Q3e2vUpjXQ8Z6/K/d28KudI9ajqgt8WEjQFlG5EPm1K6uTArN8LlqmZTEixDUA==
+
+uni-simple-router@^2.0.7:
+  version "2.0.7"
+  resolved "https://registry.npmmirror.com/uni-simple-router/-/uni-simple-router-2.0.7.tgz"
+  integrity sha512-8FKv5dw7Eoonm0gkO8udprrxzin0fNUI0+AvIphFkFRH5ZmP5ZWJ2pvnWzb2NiiqQSECTSU5VSB7HhvOSwD5eA==
+
+uploading-oss@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.npmjs.org/uploading-oss/-/uploading-oss-1.0.3.tgz"
+  integrity sha512-aqHh5NCOBcrA4d8yCIKR7B9GrKzH88X7gL8BSvIw18pi79AgWnhDkCoyjQmDqzTvjQqYfKwyFTEgnafzMx/GbQ==
+  dependencies:
+    crypto-js "^4.1.1"
+    js-base64 "^2.6.4"
+
+uri-js@^4.2.2:
+  version "4.4.1"
+  resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
+  integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==
+  dependencies:
+    punycode "^2.1.0"
+
+uuid@^3.3.2:
+  version "3.4.0"
+  resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
+  integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
+
+verror@1.10.0:
+  version "1.10.0"
+  resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"
+  integrity sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==
+  dependencies:
+    assert-plus "^1.0.0"
+    core-util-is "1.0.2"
+    extsprintf "^1.2.0"
+
+vue-waterfall-easy@^2.4.4:
+  version "2.4.4"
+  resolved "https://registry.npmjs.org/vue-waterfall-easy/-/vue-waterfall-easy-2.4.4.tgz"
+  integrity sha512-5OkpT2FPNC3rHBy858zk/nmJxqdPaGmj/KVbmA6dgcvtsovKMa+zuf/Z7F+S2NnObeavpIBztTWgcH3S42ZD+g==
+
+vue@^2.6.8:
+  version "2.7.10"
+  resolved "https://registry.npmjs.org/vue/-/vue-2.7.10.tgz"
+  integrity sha512-HmFC70qarSHPXcKtW8U8fgIkF6JGvjEmDiVInTkKZP0gIlEPhlVlcJJLkdGIDiNkIeA2zJPQTWJUI4iWe+AVfg==
   dependencies:
     "@vue/compiler-sfc" "2.7.10"
-    "csstype" "^3.1.0"
+    csstype "^3.1.0"
+
+xml2js@^0.4.17:
+  version "0.4.23"
+  resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66"
+  integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==
+  dependencies:
+    sax ">=0.6.0"
+    xmlbuilder "~11.0.0"
+
+xmlbuilder@~11.0.0:
+  version "11.0.1"
+  resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3"
+  integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==