From c62d15b288c3417fc81aa0ff8be278c95e9e74df Mon Sep 17 00:00:00 2001 From: fm453 <1280880631@qq.com> Date: Tue, 13 Aug 2024 18:32:37 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A6=96=E6=AC=A1=E5=AE=8C=E6=95=B4=E6=8E=A8?= =?UTF-8?q?=E9=80=81=EF=BC=8C=20V=EF=BC=9A1.20240808.006?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 + .hbuilderx/launch.json | 15 + App.vue | 101 + androidPrivacy.json | 38 + apis/ctms/_utils/auth.js | 13 + apis/ctms/_utils/constant.js | 55 + apis/ctms/_utils/errorCode.json | 10 + apis/ctms/_utils/request.js | 113 + apis/ctms/_utils/storage.js | 95 + apis/ctms/_utils/upload.js | 113 + apis/ctms/ads.js | 29 + apis/ctms/apis copy.json | 53 + apis/ctms/apis.json | 59 + apis/ctms/fab.js | 85 + apis/ctms/index.js | 55 + apis/ctms/news.js | 209 + apis/ctms/notice.js | 69 + apis/ctms/order.js | 220 + apis/ctms/orderpre.js | 183 + apis/ctms/sync.js | 77 + apis/ctms/user.js | 319 + apis/ctms/vcode.js | 47 + app.config.js | 55 + common/appInit.js | 166 + common/car-p.js | 24 + common/graceChecker.js | 97 + common/html-parser.js | 352 ++ common/letter.js | 84 + common/openApp.js | 36 + common/permission.js | 245 + common/province.js | 503 ++ common/util.js | 160 + components/refreshBox/refreshBox.vue | 95 + config/ctms.config.js | 38 + config/im.config.js | 5 + hybrid/html/404.html | 36 + hybrid/html/local.html | 88 + index.html | 22 + lang/en.js | 191 + lang/i18n.js | 100 + lang/zh-Hans.js | 198 + main.js | 30 + manifest.json | 254 + node_modules/.package-lock.json | 13 + node_modules/qrcodejs2/.npmignore | 4 + node_modules/qrcodejs2/LICENSE | 14 + node_modules/qrcodejs2/README.md | 43 + node_modules/qrcodejs2/bower.json | 18 + node_modules/qrcodejs2/index-svg.html | 47 + node_modules/qrcodejs2/index.html | 44 + node_modules/qrcodejs2/index.svg | 37 + node_modules/qrcodejs2/jquery.min.js | 2 + node_modules/qrcodejs2/package.json | 21 + node_modules/qrcodejs2/qrcode.js | 627 ++ node_modules/qrcodejs2/qrcode.min.js | 1 + package.json | 83 + pages.json | 559 ++ pages/common/textview/index.vue | 43 + pages/common/webview/index.vue | 104 + pages/ctms/about/about.vue | 270 + pages/ctms/index/index.vue | 148 + pages/ctms/login/login.css | 95 + pages/ctms/login/login.vue | 281 + pages/ctms/login/loginSms.vue | 391 ++ pages/ctms/login/reg.vue | 347 ++ pages/ctms/me/index.vue | 261 + pages/ctms/news/detail/detail.css | 50 + pages/ctms/news/detail/detail.vue | 360 ++ pages/ctms/news/list/list.css | 54 + pages/ctms/news/list/list.vue | 310 + pages/ctms/order/create/create.vue | 467 ++ pages/ctms/order/detail/check.vue | 409 ++ pages/ctms/order/detail/detail.css | 50 + pages/ctms/order/detail/detail.vue | 163 + pages/ctms/order/index/index.vue | 207 + pages/ctms/order/list/list.css | 54 + pages/ctms/order/list/list.vue | 232 + pages/ctms/order/yanche/yanche.vue | 938 +++ pages/ctms/orderpre/create/create.vue | 517 ++ pages/ctms/orderpre/detail/detail.css | 50 + pages/ctms/orderpre/detail/detail.vue | 152 + pages/ctms/orderpre/list/list.css | 54 + pages/ctms/orderpre/list/list.vue | 239 + pages/ctms/price/index.vue | 21 + pages/ctms/scan/scan.vue | 166 + pages/ctms/statusBar.css | 15 + pages/ctms/tabbar/index/index.vue | 148 + pages/ctms/tabbar/me/index.vue | 264 + pages/ctms/tabbar/mid/index.vue | 203 + pages/ctms/tabbar/notice/index.vue | 83 + pages/ctms/tabbar/order/index.vue | 211 + pages/index/index.vue | 167 + pages/uni-agree/uni-agree.nvue | 140 + pages/uni-agree/utils/uni-agree.js | 11 + pages/uni-starter/list/detail.vue | 385 ++ pages/uni-starter/list/list.nvue | 256 + pages/uni-starter/list/search/search.nvue | 505 ++ pages/uni-starter/news/detail/detail.uvue | 223 + pages/uni-starter/news/detail/detail.vue | 217 + pages/uni-starter/news/detail/preview.vue | 255 + pages/uni-starter/news/list/list.nvue | 295 + pages/uni-starter/news/list/list.uvue | 33 + pages/uni-starter/news/search/search.nvue | 738 +++ pages/uni-starter/news/search/search.uvue | 363 ++ pages/uni-starter/ucenter/about/about.vue | 246 + pages/uni-starter/ucenter/invite/invite.vue | 179 + .../ucenter/read-news-log/read-news-log.vue | 77 + .../ucenter/settings/dc-push/push.js | 118 + .../uni-starter/ucenter/settings/settings.vue | 325 ++ pages/uni-starter/ucenter/ucenter.vue | 506 ++ plugins/auth.js | 60 + plugins/index.js | 23 + plugins/modal.js | 74 + plugins/tab.js | 30 + static/app-plus/sharemenu/copyurl.png | Bin 0 -> 920 bytes static/app-plus/sharemenu/more.png | Bin 0 -> 1436 bytes static/app-plus/sharemenu/mp_weixin.png | Bin 0 -> 4776 bytes static/app-plus/sharemenu/qq.png | Bin 0 -> 1605 bytes static/app-plus/sharemenu/wechatfriend.png | Bin 0 -> 2024 bytes static/app-plus/sharemenu/wechatmoments.png | Bin 0 -> 1758 bytes static/app-plus/sharemenu/weibo.png | Bin 0 -> 2274 bytes static/css/uni-nvue.css | 136 + static/css/uni.css | 1711 ++++++ static/css/uni.ttf | Bin 0 -> 26164 bytes static/fab/c1.png | Bin 0 -> 401 bytes static/fab/c2.png | Bin 0 -> 470 bytes static/fab/c3.png | Bin 0 -> 511 bytes static/fab/c4.png | Bin 0 -> 476 bytes static/fab/c5.png | Bin 0 -> 472 bytes static/fab/c6.png | Bin 0 -> 545 bytes static/fab/c7.png | Bin 0 -> 365 bytes static/fab/c8.png | Bin 0 -> 587 bytes static/fab/c9.png | Bin 0 -> 565 bytes static/fab/draft-active.png | Bin 0 -> 5089 bytes static/fab/draft.png | Bin 0 -> 5951 bytes static/fab/guanzhu.png | Bin 0 -> 16585 bytes static/fab/guanzhuactive.png | Bin 0 -> 19828 bytes static/fab/home.png | Bin 0 -> 15938 bytes static/fab/homeactive.png | Bin 0 -> 18580 bytes static/fab/me.png | Bin 0 -> 16309 bytes static/fab/meactive.png | Bin 0 -> 19746 bytes static/fab/news.png | Bin 0 -> 15654 bytes static/fab/newsactive.png | Bin 0 -> 17387 bytes static/favicon.ico | Bin 0 -> 67646 bytes static/font/iconfont.css | 90 + static/font/iconfont.ttf | Bin 0 -> 6724 bytes static/h5/download-app/android.png | Bin 0 -> 1033 bytes static/h5/download-app/ios.png | Bin 0 -> 1206 bytes static/h5/download-app/openImg.png | Bin 0 -> 12894 bytes static/img/banner/banner.jpg | Bin 0 -> 102568 bytes static/img/kp/kp1.png | Bin 0 -> 205989 bytes static/img/profile.jpg | Bin 0 -> 71537 bytes static/img/tabbar/default/add.png | Bin 0 -> 15737 bytes static/img/tabbar/default/addactive.png | Bin 0 -> 15259 bytes static/img/tabbar/default/guanzhu.png | Bin 0 -> 16585 bytes static/img/tabbar/default/guanzhuactive.png | Bin 0 -> 19828 bytes static/img/tabbar/default/home.png | Bin 0 -> 15938 bytes static/img/tabbar/default/homeactive.png | Bin 0 -> 18580 bytes static/img/tabbar/default/me.png | Bin 0 -> 16309 bytes static/img/tabbar/default/meactive.png | Bin 0 -> 19746 bytes static/img/tabbar/default/news.png | Bin 0 -> 15654 bytes static/img/tabbar/default/newsactive.png | Bin 0 -> 17387 bytes static/img/tabbar/more/edit.png | Bin 0 -> 5188 bytes static/img/tabbar/more/qa.png | Bin 0 -> 18513 bytes static/img/tabbar/more/scan.png | Bin 0 -> 12404 bytes static/img/tabbar/more/search.png | Bin 0 -> 13359 bytes static/index.html | 20 + static/logo.png | Bin 0 -> 48267 bytes static/scss/colorui.css | 5142 +++++++++++++++++ static/scss/global.scss | 90 + static/scss/index.scss | 6 + static/uni-center/headers.png | Bin 0 -> 20399 bytes static/uni-load-state/disconnection.png | Bin 0 -> 5687 bytes store/index.js | 30 + store/modules/user.js | 61 + store/modules/userCloud.js | 61 + uni.scss | 127 + uni_modules/Sansnn-uQRCode/changelog.md | 12 + uni_modules/Sansnn-uQRCode/common/cache.js | 1 + uni_modules/Sansnn-uQRCode/common/queue.js | 41 + .../Sansnn-uQRCode/common/types/cache.d.ts | 3 + .../Sansnn-uQRCode/common/types/queue.d.ts | 4 + .../components/u-qrcode/u-qrcode.vue | 1131 ++++ .../components/uqrcode/uqrcode.vue | 1131 ++++ .../js_sdk/gcanvas/bridge/bridge-weex.js | 241 + .../context-2d/FillStyleLinearGradient.js | 18 + .../gcanvas/context-2d/FillStylePattern.js | 8 + .../context-2d/FillStyleRadialGradient.js | 17 + .../gcanvas/context-2d/RenderingContext.js | 666 +++ .../gcanvas/context-webgl/ActiveInfo.js | 11 + .../js_sdk/gcanvas/context-webgl/Buffer.js | 21 + .../gcanvas/context-webgl/Framebuffer.js | 21 + .../js_sdk/gcanvas/context-webgl/GLenum.js | 298 + .../js_sdk/gcanvas/context-webgl/GLmethod.js | 142 + .../js_sdk/gcanvas/context-webgl/GLtype.js | 23 + .../js_sdk/gcanvas/context-webgl/Program.js | 21 + .../gcanvas/context-webgl/Renderbuffer.js | 21 + .../gcanvas/context-webgl/RenderingContext.js | 1191 ++++ .../js_sdk/gcanvas/context-webgl/Shader.js | 22 + .../context-webgl/ShaderPrecisionFormat.js | 11 + .../js_sdk/gcanvas/context-webgl/Texture.js | 22 + .../gcanvas/context-webgl/UniformLocation.js | 22 + .../gcanvas/context-webgl/classUtils.js | 3 + .../js_sdk/gcanvas/env/canvas.js | 74 + .../js_sdk/gcanvas/env/image.js | 96 + .../Sansnn-uQRCode/js_sdk/gcanvas/env/tool.js | 24 + .../Sansnn-uQRCode/js_sdk/gcanvas/index.js | 39 + .../js_sdk/uqrcode/package.json | 12 + .../Sansnn-uQRCode/js_sdk/uqrcode/uqrcode.js | 34 + uni_modules/Sansnn-uQRCode/license.md | 201 + uni_modules/Sansnn-uQRCode/package.json | 80 + uni_modules/Sansnn-uQRCode/readme.md | 392 ++ uni_modules/json-gps/changelog.md | 2 + uni_modules/json-gps/js_sdk/gps.js | 132 + .../js_sdk/wa-permission/permission.js | 272 + uni_modules/json-gps/package.json | 76 + uni_modules/json-gps/readme.md | 30 + .../json-interceptor-chooseImage/changelog.md | 6 + .../js_sdk/main.js | 70 + .../json-interceptor-chooseImage/package.json | 76 + .../json-interceptor-chooseImage/readme.md | 30 + uni_modules/m-start-ad/changelog.md | 4 + .../components/m-start-ad/m-start-ad.vue | 228 + uni_modules/m-start-ad/package.json | 81 + uni_modules/m-start-ad/readme.md | 78 + uni_modules/uni-badge/changelog.md | 33 + .../components/uni-badge/uni-badge.vue | 268 + uni_modules/uni-badge/package.json | 85 + uni_modules/uni-badge/readme.md | 10 + uni_modules/uni-breadcrumb/changelog.md | 6 + .../uni-breadcrumb-item.vue | 121 + .../uni-breadcrumb/uni-breadcrumb.vue | 41 + uni_modules/uni-breadcrumb/package.json | 85 + uni_modules/uni-breadcrumb/readme.md | 66 + uni_modules/uni-calendar/changelog.md | 28 + .../components/uni-calendar/calendar.js | 546 ++ .../components/uni-calendar/i18n/en.json | 12 + .../components/uni-calendar/i18n/index.js | 8 + .../components/uni-calendar/i18n/zh-Hans.json | 12 + .../components/uni-calendar/i18n/zh-Hant.json | 12 + .../uni-calendar/uni-calendar-item.vue | 187 + .../components/uni-calendar/uni-calendar.vue | 567 ++ .../components/uni-calendar/util.js | 360 ++ uni_modules/uni-calendar/package.json | 85 + uni_modules/uni-calendar/readme.md | 103 + uni_modules/uni-captcha/changelog.md | 49 + .../components/uni-captcha/uni-captcha.uvue | 180 + .../components/uni-captcha/uni-captcha.vue | 167 + .../uni-popup-captcha/uni-popup-captcha.uvue | 130 + .../uni-popup-captcha/uni-popup-captcha.vue | 140 + uni_modules/uni-captcha/package.json | 83 + uni_modules/uni-captcha/readme.md | 3 + uni_modules/uni-captcha/static/run.gif | Bin 0 -> 31089 bytes .../common/uni-captcha/LICENSE.md | 201 + .../common/uni-captcha/fonts/font.ttf | Bin 0 -> 7080 bytes .../common/uni-captcha/index.js | 1 + .../common/uni-captcha/package.json | 16 + .../cloudfunctions/uni-captcha-co/config.js | 17 + .../uni-captcha-co/index.obj.js | 35 + .../uni-captcha-co/package.json | 10 + .../database/opendb-verify-codes.schema.json | 45 + uni_modules/uni-card/changelog.md | 26 + .../uni-card/components/uni-card/uni-card.vue | 270 + uni_modules/uni-card/package.json | 90 + uni_modules/uni-card/readme.md | 12 + uni_modules/uni-cloud-s2s/changelog.md | 2 + uni_modules/uni-cloud-s2s/package.json | 83 + uni_modules/uni-cloud-s2s/readme.md | 3 + .../common/uni-cloud-s2s/index.js | 1 + .../common/uni-cloud-s2s/package.json | 11 + uni_modules/uni-cms-article/changelog.md | 41 + .../uni-cms-article/common/parse-image-url.js | 68 + .../common/parse-image-url.uts | 80 + .../common/parse-scan-result.js | 28 + .../common/parse-scan-result.uts | 28 + .../uni-cms-article/common/publish-time.js | 93 + .../uni-cms-article/common/publish-time.uts | 93 + .../components/list-template/not-cover.uvue | 55 + .../components/list-template/not-cover.vue | 46 + .../list-template/right-small-cover.uvue | 50 + .../list-template/right-small-cover.vue | 46 + .../components/list-template/style.scss | 63 + .../components/list-template/three-cover.uvue | 58 + .../components/list-template/three-cover.vue | 52 + .../components/refresh-box/refreshBox.nvue | 99 + .../components/refresh-box/refreshBox.uvue | 99 + .../render-article-detail/image.uvue | 167 + .../render-article-detail/image.vue | 131 + .../render-article-detail/index.vue | 129 + .../components/render-article-detail/list.vue | 50 + .../components/render-article-detail/text.vue | 160 + .../render-article-detail/unlock-content.vue | 216 + .../render-article-detail/video.vue | 44 + .../uni-cms-article-icons.uvue | 54 + .../uni-cms-article-list.vue | 371 ++ .../uni-cms-article-search-bar.uvue | 195 + .../components/uni-load-state/i18n/en.json | 6 + .../components/uni-load-state/i18n/index.js | 6 + .../uni-load-state/i18n/zh-Hans.json | 6 + .../components/uni-load-state/readme.md | 3 + .../uni-load-state/uni-load-state.vue | 171 + uni_modules/uni-cms-article/license.md | 35 + uni_modules/uni-cms-article/package.json | 90 + .../uni-cms-article/pages/detail/detail.uvue | 223 + .../uni-cms-article/pages/detail/detail.vue | 217 + .../uni-cms-article/pages/detail/preview.vue | 255 + .../uni-cms-article/pages/list/list.nvue | 298 + .../uni-cms-article/pages/list/list.uvue | 33 + .../uni-cms-article/pages/search/search.nvue | 752 +++ .../uni-cms-article/pages/search/search.uvue | 363 ++ .../pages/webview/webview.uvue | 48 + .../uni-cms-article/pages/webview/webview.vue | 35 + uni_modules/uni-cms-article/readme.md | 24 + .../uni-cms-article/static/disconnection.png | Bin 0 -> 5687 bytes .../uni-cms-article/static/uniicons.ttf | Bin 0 -> 35760 bytes .../common/quill-delta-converter/core.js | 1 + .../common/quill-delta-converter/index.js | 257 + .../common/quill-delta-converter/package.json | 5 + .../uni-cms-unlock-callback/index.js | 58 + .../uni-cms-unlock-callback/package.json | 7 + .../uniCloud/database/db_init.json | 23 + .../database/opendb-search-hot.schema.json | 31 + .../database/opendb-search-log.schema.json | 45 + .../database/opendb-search-logs.schema.json | 45 + .../database/uni-cms-articles.schema.ext.js | 350 ++ .../database/uni-cms-articles.schema.json | 172 + .../uni-cms-unlock-record.schema.json | 49 + uni_modules/uni-collapse/changelog.md | 38 + .../uni-collapse-item/uni-collapse-item.vue | 402 ++ .../components/uni-collapse/uni-collapse.vue | 147 + uni_modules/uni-collapse/package.json | 86 + uni_modules/uni-collapse/readme.md | 12 + uni_modules/uni-combox/changelog.md | 15 + .../components/uni-combox/uni-combox.vue | 275 + uni_modules/uni-combox/package.json | 90 + uni_modules/uni-combox/readme.md | 11 + uni_modules/uni-config-center/changelog.md | 6 + uni_modules/uni-config-center/package.json | 81 + uni_modules/uni-config-center/readme.md | 93 + .../common/uni-config-center/index.js | 1 + .../common/uni-config-center/package.json | 9 + .../uni-config-center/uni-ad/config.json | 3 + .../uni-open-bridge/config.json | 12 + uni_modules/uni-countdown/changelog.md | 26 + .../components/uni-countdown/i18n/en.json | 6 + .../components/uni-countdown/i18n/index.js | 8 + .../uni-countdown/i18n/zh-Hans.json | 6 + .../uni-countdown/i18n/zh-Hant.json | 6 + .../uni-countdown/uni-countdown.vue | 281 + uni_modules/uni-countdown/package.json | 83 + uni_modules/uni-countdown/readme.md | 10 + uni_modules/uni-data-checkbox/changelog.md | 49 + .../components/uni-data-checkbox/clientdb.js | 316 + .../uni-data-checkbox/uni-data-checkbox.vue | 849 +++ uni_modules/uni-data-checkbox/package.json | 84 + uni_modules/uni-data-checkbox/readme.md | 18 + uni_modules/uni-data-picker/changelog.md | 77 + .../components/uni-data-picker/config.json | 12 + .../components/uni-data-picker/keypress.js | 45 + .../uni-data-picker/uni-data-picker.uvue | 380 ++ .../uni-data-picker/uni-data-picker.vue | 551 ++ .../uni-data-pickerview/loading.uts | 1 + .../uni-data-pickerview/uni-data-picker.js | 622 ++ .../uni-data-pickerview/uni-data-picker.uts | 693 +++ .../uni-data-pickerview.css | 76 + .../uni-data-pickerview.uvue | 69 + .../uni-data-pickerview.vue | 323 ++ uni_modules/uni-data-picker/package.json | 91 + uni_modules/uni-data-picker/readme.md | 22 + uni_modules/uni-data-select/changelog.md | 39 + .../uni-data-select/uni-data-select.vue | 562 ++ uni_modules/uni-data-select/package.json | 86 + uni_modules/uni-data-select/readme.md | 8 + uni_modules/uni-dateformat/changelog.md | 10 + .../components/uni-dateformat/date-format.js | 200 + .../uni-dateformat/uni-dateformat.vue | 88 + uni_modules/uni-dateformat/package.json | 88 + uni_modules/uni-dateformat/readme.md | 11 + uni_modules/uni-datetime-picker/changelog.md | 160 + .../uni-datetime-picker/calendar-item.vue | 177 + .../uni-datetime-picker/calendar.js | 546 ++ .../uni-datetime-picker/calendar.vue | 947 +++ .../uni-datetime-picker/i18n/en.json | 22 + .../uni-datetime-picker/i18n/index.js | 8 + .../uni-datetime-picker/i18n/zh-Hans.json | 22 + .../uni-datetime-picker/i18n/zh-Hant.json | 22 + .../uni-datetime-picker/keypress.js | 45 + .../uni-datetime-picker/time-picker.vue | 940 +++ .../uni-datetime-picker.vue | 1057 ++++ .../components/uni-datetime-picker/util.js | 421 ++ uni_modules/uni-datetime-picker/package.json | 88 + uni_modules/uni-datetime-picker/readme.md | 21 + uni_modules/uni-drawer/changelog.md | 13 + .../components/uni-drawer/keypress.js | 45 + .../components/uni-drawer/uni-drawer.vue | 183 + uni_modules/uni-drawer/package.json | 87 + uni_modules/uni-drawer/readme.md | 10 + uni_modules/uni-easyinput/changelog.md | 115 + .../components/uni-easyinput/common.js | 54 + .../uni-easyinput/uni-easyinput.vue | 676 +++ uni_modules/uni-easyinput/package.json | 88 + uni_modules/uni-easyinput/readme.md | 11 + uni_modules/uni-fab/changelog.md | 23 + .../uni-fab/components/uni-fab/uni-fab.vue | 491 ++ .../components/uni-fab/uni-fab.vue.bak | 383 ++ uni_modules/uni-fab/package.json | 84 + uni_modules/uni-fab/readme.md | 9 + uni_modules/uni-fav/changelog.md | 19 + .../uni-fav/components/uni-fav/i18n/en.json | 4 + .../uni-fav/components/uni-fav/i18n/index.js | 8 + .../components/uni-fav/i18n/zh-Hans.json | 4 + .../components/uni-fav/i18n/zh-Hant.json | 4 + .../uni-fav/components/uni-fav/uni-fav.vue | 161 + uni_modules/uni-fav/package.json | 89 + uni_modules/uni-fav/readme.md | 10 + uni_modules/uni-feedback/changelog.md | 10 + .../js_sdk/validator/opendb-feedback.js | 98 + uni_modules/uni-feedback/package.json | 90 + .../pages/opendb-feedback/detail.vue | 113 + .../pages/opendb-feedback/edit.vue | 167 + .../pages/opendb-feedback/list.vue | 70 + .../pages/opendb-feedback/opendb-feedback.vue | 141 + uni_modules/uni-feedback/readme.md | 1 + .../database/opendb-feedback.schema.json | 72 + uni_modules/uni-file-picker/changelog.md | 75 + .../uni-file-picker/choose-and-upload-file.js | 287 + .../uni-file-picker/uni-file-picker.vue | 678 +++ .../uni-file-picker/upload-file.vue | 325 ++ .../uni-file-picker/upload-image.vue | 292 + .../components/uni-file-picker/utils.js | 110 + uni_modules/uni-file-picker/package.json | 83 + uni_modules/uni-file-picker/readme.md | 11 + uni_modules/uni-forms/changelog.md | 94 + .../uni-forms-item/uni-forms-item.vue | 627 ++ .../components/uni-forms/uni-forms.vue | 397 ++ .../uni-forms/components/uni-forms/utils.js | 293 + .../components/uni-forms/validate.js | 486 ++ uni_modules/uni-forms/package.json | 88 + uni_modules/uni-forms/readme.md | 23 + uni_modules/uni-goods-nav/changelog.md | 18 + .../components/uni-goods-nav/i18n/en.json | 6 + .../components/uni-goods-nav/i18n/index.js | 8 + .../uni-goods-nav/i18n/zh-Hans.json | 6 + .../uni-goods-nav/i18n/zh-Hant.json | 6 + .../uni-goods-nav/uni-goods-nav.vue | 229 + uni_modules/uni-goods-nav/package.json | 88 + uni_modules/uni-goods-nav/readme.md | 10 + uni_modules/uni-grid/changelog.md | 13 + .../uni-grid-item/uni-grid-item.vue | 127 + .../uni-grid/components/uni-grid/uni-grid.vue | 142 + uni_modules/uni-grid/package.json | 86 + uni_modules/uni-grid/readme.md | 11 + uni_modules/uni-group/changelog.md | 16 + .../components/uni-group/uni-group.vue | 134 + uni_modules/uni-group/package.json | 87 + uni_modules/uni-group/readme.md | 9 + uni_modules/uni-icons/changelog.md | 42 + .../uni-icons/components/uni-icons/icons.js | 1169 ++++ .../components/uni-icons/uni-icons.uvue | 91 + .../components/uni-icons/uni-icons.vue | 110 + .../uni-icons/components/uni-icons/uni.ttf | Bin 0 -> 26164 bytes .../components/uni-icons/uniicons.css | 664 +++ .../components/uni-icons/uniicons.ttf | Bin 0 -> 35824 bytes .../components/uni-icons/uniicons_file.ts | 664 +++ .../components/uni-icons/uniicons_file_vue.js | 649 +++ uni_modules/uni-icons/package.json | 89 + uni_modules/uni-icons/readme.md | 8 + uni_modules/uni-id-common/changelog.md | 36 + uni_modules/uni-id-common/package.json | 85 + uni_modules/uni-id-common/readme.md | 3 + .../common/uni-id-common/index.js | 1 + .../common/uni-id-common/package.json | 16 + uni_modules/uni-id-pages/changelog.md | 181 + .../uni-id-pages/common/check-id-card.js | 16 + .../uni-id-pages/common/login-page.mixin.js | 95 + .../uni-id-pages/common/login-page.scss | 126 + uni_modules/uni-id-pages/common/password.js | 85 + uni_modules/uni-id-pages/common/store.js | 174 + .../components/cloud-image/cloud-image.vue | 73 + .../uni-id-pages-agreements.vue | 167 + .../uni-id-pages-avatar.vue | 198 + .../uni-id-pages-bind-mobile.vue | 160 + .../uni-id-pages-email-form.vue | 248 + .../uni-id-pages-fab-login.vue | 568 ++ .../uni-id-pages-sms-form.vue | 242 + .../uni-id-pages-user-profile.vue | 171 + uni_modules/uni-id-pages/config copy.js | 67 + uni_modules/uni-id-pages/config.js | 20 + uni_modules/uni-id-pages/init.js | 95 + uni_modules/uni-id-pages/package.json | 103 + .../pages/common/webview/webview.vue | 35 + .../pages/login/login-smscode.vue | 120 + .../pages/login/login-withoutpwd.vue | 257 + .../pages/login/login-withpwd.vue | 176 + .../pages/register/register-admin.vue | 178 + .../pages/register/register-by-email.vue | 216 + .../uni-id-pages/pages/register/register.vue | 181 + .../uni-id-pages/pages/register/validator.js | 56 + .../pages/retrieve/retrieve-by-email.vue | 218 + .../uni-id-pages/pages/retrieve/retrieve.vue | 241 + .../userinfo/bind-mobile/bind-mobile.vue | 131 + .../pages/userinfo/change_pwd/change_pwd.vue | 130 + .../pages/userinfo/cropImage/cropImage.vue | 39 + .../userinfo/cropImage/limeClipper/README.md | 227 + .../cropImage/limeClipper/images/photo.svg | 19 + .../cropImage/limeClipper/images/rotate.svg | 15 + .../userinfo/cropImage/limeClipper/index.css | 160 + .../cropImage/limeClipper/limeClipper.vue | 820 +++ .../userinfo/cropImage/limeClipper/utils.js | 244 + .../pages/userinfo/deactivate/deactivate.vue | 117 + .../realname-verify/face-verify-icon.svg | 1 + .../realname-verify/realname-verify.vue | 315 + .../pages/userinfo/set-pwd/set-pwd.vue | 171 + .../uni-id-pages/pages/userinfo/userinfo.vue | 272 + uni_modules/uni-id-pages/readme.md | 15 + .../uni-id-pages/static/app-plus/apple.png | Bin 0 -> 10282 bytes .../static/app-plus/uni-fab-login/alipay.png | Bin 0 -> 3978 bytes .../static/app-plus/uni-fab-login/apple.png | Bin 0 -> 3226 bytes .../static/app-plus/uni-fab-login/douyin.png | Bin 0 -> 3163 bytes .../app-plus/uni-fab-login/facebook.png | Bin 0 -> 3065 bytes .../static/app-plus/uni-fab-login/google.png | Bin 0 -> 4333 bytes .../static/app-plus/uni-fab-login/qq.png | Bin 0 -> 3449 bytes .../app-plus/uni-fab-login/sinaweibo.png | Bin 0 -> 4081 bytes .../static/app-plus/uni-fab-login/taobao.png | Bin 0 -> 4339 bytes .../app-plus/uni-fab-login/univerify.png | Bin 0 -> 3365 bytes .../uni-id-pages/static/limeClipper/photo.svg | 19 + .../static/limeClipper/rotate.svg | 15 + .../static/login/uni-fab-login/sms.png | Bin 0 -> 4285 bytes .../static/login/uni-fab-login/user.png | Bin 0 -> 2997 bytes .../static/login/uni-fab-login/weixin.png | Bin 0 -> 3934 bytes .../uni-id-pages/static/login/weixin.png | Bin 0 -> 11483 bytes .../uni-id-co/common/constants.js | 108 + .../cloudfunctions/uni-id-co/common/error.js | 70 + .../uni-id-co/common/sensitive-aes-cipher.js | 64 + .../uni-id-co/common/universal.js | 47 + .../cloudfunctions/uni-id-co/common/utils.js | 263 + .../uni-id-co/common/validator.js | 443 ++ .../uni-id-co/config/permission.js | 90 + .../cloudfunctions/uni-id-co/index.obj.js | 696 +++ .../cloudfunctions/uni-id-co/lang/en.js | 62 + .../cloudfunctions/uni-id-co/lang/index.js | 22 + .../cloudfunctions/uni-id-co/lang/zh-hans.js | 64 + .../cloudfunctions/uni-id-co/lib/README.md | 3 + .../cloudfunctions/uni-id-co/lib/npm/index.js | 3 + .../lib/third-party/alipay/account/index.js | 16 + .../third-party/alipay/account/protocols.js | 10 + .../lib/third-party/alipay/alipayBase.js | 231 + .../lib/third-party/apple/account/index.js | 79 + .../third-party/apple/rsa-public-key-pem.js | 64 + .../uni-id-co/lib/third-party/index.js | 36 + .../lib/third-party/qq/account/index.js | 97 + .../lib/third-party/qq/account/protocol.js | 0 .../uni-id-co/lib/third-party/qq/normalize.js | 85 + .../lib/third-party/share/create-api.js | 73 + .../lib/third-party/weixin/account/index.js | 111 + .../lib/third-party/weixin/normalize.js | 95 + .../uni-id-co/lib/third-party/weixin/utils.js | 87 + .../uni-id-co/lib/utils/account.js | 98 + .../uni-id-co/lib/utils/captcha.js | 76 + .../uni-id-co/lib/utils/config.js | 137 + .../uni-id-co/lib/utils/fission.js | 192 + .../uni-id-co/lib/utils/login.js | 246 + .../uni-id-co/lib/utils/logout.js | 49 + .../uni-id-co/lib/utils/password.js | 261 + .../cloudfunctions/uni-id-co/lib/utils/qq.js | 154 + .../uni-id-co/lib/utils/register.js | 231 + .../uni-id-co/lib/utils/relate.js | 166 + .../cloudfunctions/uni-id-co/lib/utils/sms.js | 79 + .../uni-id-co/lib/utils/unified-login.js | 106 + .../uni-id-co/lib/utils/univerify.js | 27 + .../uni-id-co/lib/utils/update-user-info.js | 25 + .../uni-id-co/lib/utils/utils.js | 18 + .../uni-id-co/lib/utils/verify-code.js | 152 + .../uni-id-co/lib/utils/weixin.js | 236 + .../uni-id-co/middleware/access-control.js | 59 + .../uni-id-co/middleware/auth.js | 17 + .../uni-id-co/middleware/index.js | 8 + .../uni-id-co/middleware/rbac.js | 39 + .../uni-id-co/middleware/uni-id-log.js | 39 + .../uni-id-co/middleware/validate.js | 7 + .../middleware/verify-request-sign.js | 85 + .../uni-id-co/module/account/close-account.js | 16 + .../module/account/get-account-info.js | 69 + .../module/account/get-realname-info.js | 45 + .../uni-id-co/module/account/index.js | 9 + .../module/account/reset-pwd-by-email.js | 128 + .../module/account/reset-pwd-by-sms.js | 128 + .../uni-id-co/module/account/set-pwd.js | 83 + .../uni-id-co/module/account/update-pwd.js | 69 + .../uni-id-co/module/admin/add-user.js | 131 + .../uni-id-co/module/admin/index.js | 4 + .../uni-id-co/module/admin/update-user.js | 138 + .../module/dev/get-supported-login-type.js | 70 + .../uni-id-co/module/dev/index.js | 3 + .../uni-id-co/module/external/index.js | 5 + .../uni-id-co/module/external/login.js | 68 + .../uni-id-co/module/external/register.js | 93 + .../module/external/update-user-info.js | 208 + .../get-auth-result.js | 136 + .../get-certify-id.js | 99 + .../module/facial-recognition-verify/index.js | 4 + .../uni-id-co/module/fission/accept-invite.js | 25 + .../module/fission/get-invited-user.js | 80 + .../uni-id-co/module/fission/index.js | 4 + .../uni-id-co/module/login/index.js | 20 + .../uni-id-co/module/login/login-by-alipay.js | 70 + .../uni-id-co/module/login/login-by-apple.js | 77 + .../uni-id-co/module/login/login-by-baidu.js | 9 + .../module/login/login-by-dingtalk.js | 9 + .../uni-id-co/module/login/login-by-douyin.js | 9 + .../module/login/login-by-email-code.js | 9 + .../module/login/login-by-email-link.js | 9 + .../module/login/login-by-facebook.js | 9 + .../uni-id-co/module/login/login-by-google.js | 9 + .../uni-id-co/module/login/login-by-qq.js | 167 + .../uni-id-co/module/login/login-by-sms.js | 99 + .../uni-id-co/module/login/login-by-taobao.js | 9 + .../module/login/login-by-toutiao.js | 9 + .../module/login/login-by-univerify.js | 69 + .../uni-id-co/module/login/login-by-weibo.js | 9 + .../module/login/login-by-weixin-mobile.js | 106 + .../uni-id-co/module/login/login-by-weixin.js | 176 + .../uni-id-co/module/login/login.js | 94 + .../uni-id-co/module/logout/index.js | 3 + .../uni-id-co/module/logout/logout.js | 15 + .../module/multi-end/authorize-app-login.js | 37 + .../uni-id-co/module/multi-end/index.js | 5 + .../module/multi-end/remove-authorized-app.js | 30 + .../module/multi-end/set-authorized-app.js | 36 + .../uni-id-co/module/multi-end/utils.js | 38 + .../uni-id-co/module/register/index.js | 5 + .../module/register/register-admin.js | 72 + .../module/register/register-user-by-email.js | 87 + .../module/register/register-user.js | 68 + .../uni-id-co/module/relate/bind-alipay.js | 63 + .../uni-id-co/module/relate/bind-apple.js | 62 + .../module/relate/bind-mobile-by-mp-weixin.js | 104 + .../module/relate/bind-mobile-by-sms.js | 92 + .../module/relate/bind-mobile-by-univerify.js | 70 + .../uni-id-co/module/relate/bind-qq.js | 110 + .../uni-id-co/module/relate/bind-weixin.js | 100 + .../uni-id-co/module/relate/index.js | 13 + .../uni-id-co/module/relate/unbind-alipay.js | 32 + .../uni-id-co/module/relate/unbind-apple.js | 32 + .../uni-id-co/module/relate/unbind-qq.js | 33 + .../uni-id-co/module/relate/unbind-weixin.js | 38 + .../uni-id-co/module/utils/index.js | 5 + .../uni-id-co/module/utils/refresh-token.js | 24 + .../secure-network-handshake-by-weixin.js | 73 + .../uni-id-co/module/utils/set-push-cid.js | 132 + .../uni-id-co/module/verify/create-captcha.js | 35 + .../uni-id-co/module/verify/index.js | 7 + .../module/verify/refresh-captcha.js | 36 + .../module/verify/send-email-code.js | 60 + .../module/verify/send-email-link.js | 12 + .../uni-id-co/module/verify/send-sms-code.js | 71 + .../cloudfunctions/uni-id-co/package.json | 23 + .../database/opendb-device.schema.json | 142 + .../database/opendb-frv-logs.schema.json | 44 + .../database/uni-id-device.schema.json | 83 + .../uniCloud/database/uni-id-log.schema.json | 71 + .../database/uni-id-permissions.schema.json | 52 + .../database/uni-id-roles.schema.json | 50 + .../database/uni-id-users.schema.json | 473 ++ uni_modules/uni-image-menu/changelog.md | 0 .../uni-image-menu/js_sdk/uni-image-menu.js | 169 + uni_modules/uni-image-menu/package.json | 76 + uni_modules/uni-image-menu/readme.md | 19 + uni_modules/uni-indexed-list/changelog.md | 17 + .../uni-indexed-list-item.vue | 144 + .../uni-indexed-list/uni-indexed-list.vue | 367 ++ uni_modules/uni-indexed-list/package.json | 89 + uni_modules/uni-indexed-list/readme.md | 11 + uni_modules/uni-installApk/changelog.md | 12 + uni_modules/uni-installApk/package.json | 94 + uni_modules/uni-installApk/readme.md | 37 + .../utssdk/app-android/AndroidManifest.xml | 5 + .../utssdk/app-android/index.uts | 98 + uni_modules/uni-installApk/utssdk/index.d.ts | 78 + .../uni-installApk/utssdk/interface.uts | 80 + .../uni-installApk/utssdk/unierror.uts | 25 + uni_modules/uni-link/changelog.md | 17 + .../uni-link/components/uni-link/uni-link.vue | 128 + uni_modules/uni-link/package.json | 87 + uni_modules/uni-link/readme.md | 11 + uni_modules/uni-list/changelog.md | 46 + .../components/uni-list-ad/uni-list-ad.vue | 107 + .../uni-list-chat/uni-list-chat.scss | 58 + .../uni-list-chat/uni-list-chat.vue | 593 ++ .../uni-list-item/uni-list-item.vue | 534 ++ .../components/uni-list/uni-list - 副本.vue | 106 + .../uni-list/components/uni-list/uni-list.vue | 123 + .../components/uni-list/uni-refresh.vue | 65 + .../components/uni-list/uni-refresh.wxs | 87 + uni_modules/uni-list/package.json | 88 + uni_modules/uni-list/readme.md | 346 ++ uni_modules/uni-load-more/changelog.md | 19 + .../components/uni-load-more/i18n/en.json | 5 + .../components/uni-load-more/i18n/index.js | 8 + .../uni-load-more/i18n/zh-Hans.json | 5 + .../uni-load-more/i18n/zh-Hant.json | 5 + .../uni-load-more/uni-load-more.vue | 399 ++ uni_modules/uni-load-more/package.json | 86 + uni_modules/uni-load-more/readme.md | 14 + uni_modules/uni-nav-bar/changelog.md | 51 + .../components/uni-nav-bar/uni-nav-bar.vue | 357 ++ .../components/uni-nav-bar/uni-status-bar.vue | 24 + uni_modules/uni-nav-bar/package.json | 86 + uni_modules/uni-nav-bar/readme.md | 15 + uni_modules/uni-notice-bar/changelog.md | 20 + .../uni-notice-bar/uni-notice-bar.vue | 431 ++ uni_modules/uni-notice-bar/package.json | 87 + uni_modules/uni-notice-bar/readme.md | 13 + uni_modules/uni-number-box/changelog.md | 39 + .../uni-number-box/uni-number-box.vue | 232 + uni_modules/uni-number-box/package.json | 83 + uni_modules/uni-number-box/readme.md | 13 + .../uni-open-bridge-common/changelog.md | 25 + .../uni-open-bridge-common/package.json | 84 + uni_modules/uni-open-bridge-common/readme.md | 5 + .../uni-open-bridge-common/bridge-error.js | 26 + .../common/uni-open-bridge-common/config.js | 124 + .../common/uni-open-bridge-common/consts.js | 30 + .../common/uni-open-bridge-common/index.js | 317 + .../uni-open-bridge-common/package.json | 15 + .../common/uni-open-bridge-common/storage.js | 111 + .../uni-open-bridge-common/uni-cloud-cache.js | 324 ++ .../uni-open-bridge-common/validator.js | 31 + .../uni-open-bridge-common/weixin-server.js | 203 + .../database/opendb-open-data.schema.json | 19 + uni_modules/uni-open-bridge/changelog.md | 0 uni_modules/uni-open-bridge/package.json | 84 + uni_modules/uni-open-bridge/readme.md | 577 ++ .../cloudfunctions/uni-open-bridge/basic.js | 131 + .../cloudfunctions/uni-open-bridge/config.js | 197 + .../cloudfunctions/uni-open-bridge/consts.js | 19 + .../uni-open-bridge/index.mp-weixin.js | 93 + .../uni-open-bridge/index.obj.js | 62 + .../uni-open-bridge/index.task.js | 86 + .../uni-open-bridge/package.json | 10 + .../uni-open-bridge/task-h5-weixin.js | 67 + .../uni-open-bridge/task-mp-weixin.js | 41 + uni_modules/uni-pagination/changelog.md | 27 + .../components/uni-pagination/i18n/en.json | 5 + .../components/uni-pagination/i18n/es.json | 5 + .../components/uni-pagination/i18n/fr.json | 5 + .../components/uni-pagination/i18n/index.js | 12 + .../uni-pagination/i18n/zh-Hans.json | 5 + .../uni-pagination/i18n/zh-Hant.json | 5 + .../uni-pagination/uni-pagination.vue | 465 ++ uni_modules/uni-pagination/package.json | 83 + uni_modules/uni-pagination/readme.md | 11 + uni_modules/uni-popup/changelog.md | 84 + .../components/uni-popup-dialog/keypress.js | 45 + .../uni-popup-dialog/uni-popup-dialog.vue | 316 + .../uni-popup-message/uni-popup-message.vue | 143 + .../uni-popup-share/uni-popup-share.vue | 187 + .../components/uni-popup/i18n/en.json | 7 + .../components/uni-popup/i18n/index.js | 8 + .../components/uni-popup/i18n/zh-Hans.json | 7 + .../components/uni-popup/i18n/zh-Hant.json | 7 + .../components/uni-popup/keypress.js | 45 + .../uni-popup/components/uni-popup/message.js | 22 + .../uni-popup/components/uni-popup/popup.js | 26 + .../uni-popup/components/uni-popup/share.js | 16 + .../components/uni-popup/uni-popup.uvue | 90 + .../components/uni-popup/uni-popup.vue | 503 ++ uni_modules/uni-popup/package.json | 88 + uni_modules/uni-popup/readme.md | 17 + uni_modules/uni-rate/changelog.md | 25 + .../uni-rate/components/uni-rate/uni-rate.vue | 361 ++ uni_modules/uni-rate/package.json | 88 + uni_modules/uni-rate/readme.md | 12 + uni_modules/uni-row/changelog.md | 10 + .../uni-row/components/uni-col/uni-col.vue | 317 + .../uni-row/components/uni-row/uni-row.vue | 190 + uni_modules/uni-row/package.json | 87 + uni_modules/uni-row/readme.md | 10 + uni_modules/uni-scss/changelog.md | 8 + uni_modules/uni-scss/index.scss | 1 + uni_modules/uni-scss/package.json | 82 + uni_modules/uni-scss/readme.md | 4 + uni_modules/uni-scss/styles/index.scss | 7 + .../uni-scss/styles/setting/_border.scss | 3 + .../uni-scss/styles/setting/_color.scss | 66 + .../uni-scss/styles/setting/_radius.scss | 55 + .../uni-scss/styles/setting/_space.scss | 56 + .../uni-scss/styles/setting/_styles.scss | 167 + .../uni-scss/styles/setting/_text.scss | 24 + .../uni-scss/styles/setting/_variables.scss | 146 + .../uni-scss/styles/tools/functions.scss | 19 + uni_modules/uni-scss/theme.scss | 31 + uni_modules/uni-scss/variables.scss | 62 + uni_modules/uni-search-bar/changelog.md | 47 + .../components/uni-search-bar/i18n/en.json | 4 + .../components/uni-search-bar/i18n/index.js | 8 + .../uni-search-bar/i18n/zh-Hans.json | 4 + .../uni-search-bar/i18n/zh-Hant.json | 4 + .../uni-search-bar/uni-search-bar.vue | 309 + uni_modules/uni-search-bar/package.json | 87 + uni_modules/uni-search-bar/readme.md | 14 + uni_modules/uni-section/changelog.md | 2 + .../components/uni-section/uni-section.vue | 167 + uni_modules/uni-section/package.json | 87 + uni_modules/uni-section/readme.md | 8 + .../uni-segmented-control/changelog.md | 15 + .../uni-segmented-control.vue | 146 + .../uni-segmented-control/package.json | 85 + uni_modules/uni-segmented-control/readme.md | 13 + uni_modules/uni-share/changelog.md | 18 + .../uni-share/js_sdk/uni-image-menu.js | 204 + uni_modules/uni-share/js_sdk/uni-share.js | 98 + uni_modules/uni-share/package.json | 80 + uni_modules/uni-share/readme.md | 95 + uni_modules/uni-sign-in/changelog.md | 16 + .../components/uni-sign-in/uni-sign-in.vue | 310 + uni_modules/uni-sign-in/package.json | 82 + uni_modules/uni-sign-in/pages/demo/demo.vue | 15 + uni_modules/uni-sign-in/readme.md | 80 + uni_modules/uni-sign-in/static/background.png | Bin 0 -> 30068 bytes .../cloudfunctions/common/sign-in/index.js | 106 + .../common/sign-in/package.json | 12 + .../rewarded-video-ad-notify-url/index.js | 64 + .../node_modules/sign-in/index.js | 106 + .../node_modules/sign-in/package.json | 12 + .../node_modules/uni-config-center/index.js | 1 + .../uni-config-center/package.json | 9 + .../uni-config-center/uni-ad/config.json | 3 + .../uni-config-center/uni-id/config.json | 52 + .../package-lock.json | 12 + .../rewarded-video-ad-notify-url/package.json | 15 + .../uni-clientDB-actions/signIn.js | 90 + .../database/opendb-sign-in.schema.json | 41 + uni_modules/uni-sign-in/utils/ad.js | 253 + uni_modules/uni-steps/changelog.md | 18 + .../components/uni-steps/uni-steps.vue | 280 + uni_modules/uni-steps/package.json | 87 + uni_modules/uni-steps/readme.md | 13 + uni_modules/uni-swipe-action/changelog.md | 47 + .../uni-swipe-action-item/bindingx.js | 302 + .../uni-swipe-action-item/index.wxs | 323 ++ .../components/uni-swipe-action-item/isPC.js | 12 + .../uni-swipe-action-item/mpalipay.js | 195 + .../uni-swipe-action-item/mpother.js | 260 + .../components/uni-swipe-action-item/mpwxs.js | 84 + .../uni-swipe-action-item/render.js | 270 + .../uni-swipe-action-item.vue | 348 ++ .../components/uni-swipe-action-item/wx.wxs | 341 ++ .../uni-swipe-action/uni-swipe-action.vue | 60 + uni_modules/uni-swipe-action/package.json | 84 + uni_modules/uni-swipe-action/readme.md | 11 + uni_modules/uni-swiper-dot/changelog.md | 12 + .../uni-swiper-dot/uni-swiper-dot.vue | 218 + uni_modules/uni-swiper-dot/package.json | 87 + uni_modules/uni-swiper-dot/readme.md | 11 + uni_modules/uni-table/changelog.md | 29 + .../components/uni-table/uni-table.vue | 455 ++ .../components/uni-tbody/uni-tbody.vue | 29 + .../uni-table/components/uni-td/uni-td.vue | 90 + .../components/uni-th/filter-dropdown.vue | 511 ++ .../uni-table/components/uni-th/uni-th.vue | 285 + .../components/uni-thead/uni-thead.vue | 129 + .../components/uni-tr/table-checkbox.vue | 179 + .../uni-table/components/uni-tr/uni-tr.vue | 175 + uni_modules/uni-table/i18n/en.json | 9 + uni_modules/uni-table/i18n/es.json | 9 + uni_modules/uni-table/i18n/fr.json | 9 + uni_modules/uni-table/i18n/index.js | 12 + uni_modules/uni-table/i18n/zh-Hans.json | 9 + uni_modules/uni-table/i18n/zh-Hant.json | 9 + uni_modules/uni-table/package.json | 83 + uni_modules/uni-table/readme.md | 13 + uni_modules/uni-tag/changelog.md | 23 + .../uni-tag/components/uni-tag/uni-tag.vue | 252 + uni_modules/uni-tag/package.json | 84 + uni_modules/uni-tag/readme.md | 13 + uni_modules/uni-title/changelog.md | 10 + .../components/uni-title/uni-title.vue | 171 + uni_modules/uni-title/package.json | 88 + uni_modules/uni-title/readme.md | 14 + uni_modules/uni-tooltip/changelog.md | 16 + .../components/uni-tooltip/uni-tooltip.vue | 108 + uni_modules/uni-tooltip/package.json | 86 + uni_modules/uni-tooltip/readme.md | 8 + uni_modules/uni-transition/changelog.md | 24 + .../uni-transition/createAnimation.js | 131 + .../uni-transition/uni-transition.vue | 286 + uni_modules/uni-transition/package.json | 85 + uni_modules/uni-transition/readme.md | 11 + .../uni-upgrade-center-app/changelog.md | 98 + .../uni-upgrade-center-app.uvue | 485 ++ .../images/app_update_close.png | Bin 0 -> 7644 bytes .../uni-upgrade-center-app/images/bg_top.png | Bin 0 -> 30486 bytes .../uni-upgrade-center-app/package.json | 86 + .../pages/upgrade-popup.vue | 611 ++ uni_modules/uni-upgrade-center-app/readme.md | 1 + .../static/app_update_close.png | Bin 0 -> 7644 bytes .../uni-upgrade-center-app/static/bg_top.png | Bin 0 -> 30486 bytes .../uniCloud/database/db_init.json | 1 + .../utils/call-check-version.js | 32 + .../utils/call-check-version.ts | 120 + .../utils/check-update-nvue.js | 184 + .../utils/check-update.js | 158 + .../utils/check-update.ts | 191 + uni_modules/uni-upgrade-center/changelog.md | 55 + .../js_sdk/validator/opendb-app-list.js | 44 + .../js_sdk/validator/opendb-app-versions.js | 151 + uni_modules/uni-upgrade-center/menu.json | 10 + uni_modules/uni-upgrade-center/package.json | 92 + .../pages/components/show-info.vue | 52 + .../pages/mixin/version_add_detail_mixin.js | 209 + uni_modules/uni-upgrade-center/pages/utils.js | 26 + .../uni-upgrade-center/pages/version/add.vue | 478 ++ .../pages/version/detail.vue | 401 ++ .../uni-upgrade-center/pages/version/list.vue | 364 ++ uni_modules/uni-upgrade-center/readme.md | 233 + .../database/opendb-app-list.schema.json | 329 ++ uni_modules/uts-openSchema/changelog.md | 2 + uni_modules/uts-openSchema/package.json | 82 + uni_modules/uts-openSchema/readme.md | 6 + .../utssdk/app-android/config.json | 3 + .../utssdk/app-android/index.uts | 15 + .../uts-openSchema/utssdk/app-ios/config.json | 3 + .../uts-openSchema/utssdk/app-ios/index.uts | 14 + .../uts-openSchema/utssdk/interface.uts | 1 + .../uts-progressNotification/changelog.md | 24 + .../uts-progressNotification/package.json | 83 + .../uts-progressNotification/readme.md | 71 + .../utssdk/app-android/AndroidManifest.xml | 11 + .../app-android/TransparentActivity.uts | 62 + .../utssdk/app-android/callbacks.uts | 4 + .../utssdk/app-android/config.json | 3 + .../utssdk/app-android/constant.uts | 2 + .../utssdk/app-android/index.uts | 159 + .../values/notification_progress_styles.xml | 11 + .../utssdk/interface.uts | 46 + .../utssdk/unierror.uts | 0 utils/common.js | 73 + utils/common2.js | 69 + 939 files changed, 111777 insertions(+) create mode 100644 .hbuilderx/launch.json create mode 100644 App.vue create mode 100644 androidPrivacy.json create mode 100644 apis/ctms/_utils/auth.js create mode 100644 apis/ctms/_utils/constant.js create mode 100644 apis/ctms/_utils/errorCode.json create mode 100644 apis/ctms/_utils/request.js create mode 100644 apis/ctms/_utils/storage.js create mode 100644 apis/ctms/_utils/upload.js create mode 100644 apis/ctms/ads.js create mode 100644 apis/ctms/apis copy.json create mode 100644 apis/ctms/apis.json create mode 100644 apis/ctms/fab.js create mode 100644 apis/ctms/index.js create mode 100644 apis/ctms/news.js create mode 100644 apis/ctms/notice.js create mode 100644 apis/ctms/order.js create mode 100644 apis/ctms/orderpre.js create mode 100644 apis/ctms/sync.js create mode 100644 apis/ctms/user.js create mode 100644 apis/ctms/vcode.js create mode 100644 app.config.js create mode 100644 common/appInit.js create mode 100644 common/car-p.js create mode 100644 common/graceChecker.js create mode 100644 common/html-parser.js create mode 100644 common/letter.js create mode 100644 common/openApp.js create mode 100644 common/permission.js create mode 100644 common/province.js create mode 100644 common/util.js create mode 100644 components/refreshBox/refreshBox.vue create mode 100644 config/ctms.config.js create mode 100644 config/im.config.js create mode 100644 hybrid/html/404.html create mode 100644 hybrid/html/local.html create mode 100644 index.html create mode 100644 lang/en.js create mode 100644 lang/i18n.js create mode 100644 lang/zh-Hans.js create mode 100644 main.js create mode 100644 manifest.json create mode 100644 node_modules/.package-lock.json create mode 100644 node_modules/qrcodejs2/.npmignore create mode 100644 node_modules/qrcodejs2/LICENSE create mode 100644 node_modules/qrcodejs2/README.md create mode 100644 node_modules/qrcodejs2/bower.json create mode 100644 node_modules/qrcodejs2/index-svg.html create mode 100644 node_modules/qrcodejs2/index.html create mode 100644 node_modules/qrcodejs2/index.svg create mode 100644 node_modules/qrcodejs2/jquery.min.js create mode 100644 node_modules/qrcodejs2/package.json create mode 100644 node_modules/qrcodejs2/qrcode.js create mode 100644 node_modules/qrcodejs2/qrcode.min.js create mode 100644 package.json create mode 100644 pages.json create mode 100644 pages/common/textview/index.vue create mode 100644 pages/common/webview/index.vue create mode 100644 pages/ctms/about/about.vue create mode 100644 pages/ctms/index/index.vue create mode 100644 pages/ctms/login/login.css create mode 100644 pages/ctms/login/login.vue create mode 100644 pages/ctms/login/loginSms.vue create mode 100644 pages/ctms/login/reg.vue create mode 100644 pages/ctms/me/index.vue create mode 100644 pages/ctms/news/detail/detail.css create mode 100644 pages/ctms/news/detail/detail.vue create mode 100644 pages/ctms/news/list/list.css create mode 100644 pages/ctms/news/list/list.vue create mode 100644 pages/ctms/order/create/create.vue create mode 100644 pages/ctms/order/detail/check.vue create mode 100644 pages/ctms/order/detail/detail.css create mode 100644 pages/ctms/order/detail/detail.vue create mode 100644 pages/ctms/order/index/index.vue create mode 100644 pages/ctms/order/list/list.css create mode 100644 pages/ctms/order/list/list.vue create mode 100644 pages/ctms/order/yanche/yanche.vue create mode 100644 pages/ctms/orderpre/create/create.vue create mode 100644 pages/ctms/orderpre/detail/detail.css create mode 100644 pages/ctms/orderpre/detail/detail.vue create mode 100644 pages/ctms/orderpre/list/list.css create mode 100644 pages/ctms/orderpre/list/list.vue create mode 100644 pages/ctms/price/index.vue create mode 100644 pages/ctms/scan/scan.vue create mode 100644 pages/ctms/statusBar.css create mode 100644 pages/ctms/tabbar/index/index.vue create mode 100644 pages/ctms/tabbar/me/index.vue create mode 100644 pages/ctms/tabbar/mid/index.vue create mode 100644 pages/ctms/tabbar/notice/index.vue create mode 100644 pages/ctms/tabbar/order/index.vue create mode 100644 pages/index/index.vue create mode 100644 pages/uni-agree/uni-agree.nvue create mode 100644 pages/uni-agree/utils/uni-agree.js create mode 100644 pages/uni-starter/list/detail.vue create mode 100644 pages/uni-starter/list/list.nvue create mode 100644 pages/uni-starter/list/search/search.nvue create mode 100644 pages/uni-starter/news/detail/detail.uvue create mode 100644 pages/uni-starter/news/detail/detail.vue create mode 100644 pages/uni-starter/news/detail/preview.vue create mode 100644 pages/uni-starter/news/list/list.nvue create mode 100644 pages/uni-starter/news/list/list.uvue create mode 100644 pages/uni-starter/news/search/search.nvue create mode 100644 pages/uni-starter/news/search/search.uvue create mode 100644 pages/uni-starter/ucenter/about/about.vue create mode 100644 pages/uni-starter/ucenter/invite/invite.vue create mode 100644 pages/uni-starter/ucenter/read-news-log/read-news-log.vue create mode 100644 pages/uni-starter/ucenter/settings/dc-push/push.js create mode 100644 pages/uni-starter/ucenter/settings/settings.vue create mode 100644 pages/uni-starter/ucenter/ucenter.vue create mode 100644 plugins/auth.js create mode 100644 plugins/index.js create mode 100644 plugins/modal.js create mode 100644 plugins/tab.js create mode 100644 static/app-plus/sharemenu/copyurl.png create mode 100644 static/app-plus/sharemenu/more.png create mode 100644 static/app-plus/sharemenu/mp_weixin.png create mode 100644 static/app-plus/sharemenu/qq.png create mode 100644 static/app-plus/sharemenu/wechatfriend.png create mode 100644 static/app-plus/sharemenu/wechatmoments.png create mode 100644 static/app-plus/sharemenu/weibo.png create mode 100644 static/css/uni-nvue.css create mode 100644 static/css/uni.css create mode 100644 static/css/uni.ttf create mode 100644 static/fab/c1.png create mode 100644 static/fab/c2.png create mode 100644 static/fab/c3.png create mode 100644 static/fab/c4.png create mode 100644 static/fab/c5.png create mode 100644 static/fab/c6.png create mode 100644 static/fab/c7.png create mode 100644 static/fab/c8.png create mode 100644 static/fab/c9.png create mode 100644 static/fab/draft-active.png create mode 100644 static/fab/draft.png create mode 100644 static/fab/guanzhu.png create mode 100644 static/fab/guanzhuactive.png create mode 100644 static/fab/home.png create mode 100644 static/fab/homeactive.png create mode 100644 static/fab/me.png create mode 100644 static/fab/meactive.png create mode 100644 static/fab/news.png create mode 100644 static/fab/newsactive.png create mode 100755 static/favicon.ico create mode 100644 static/font/iconfont.css create mode 100644 static/font/iconfont.ttf create mode 100644 static/h5/download-app/android.png create mode 100644 static/h5/download-app/ios.png create mode 100644 static/h5/download-app/openImg.png create mode 100644 static/img/banner/banner.jpg create mode 100644 static/img/kp/kp1.png create mode 100644 static/img/profile.jpg create mode 100644 static/img/tabbar/default/add.png create mode 100644 static/img/tabbar/default/addactive.png create mode 100644 static/img/tabbar/default/guanzhu.png create mode 100644 static/img/tabbar/default/guanzhuactive.png create mode 100644 static/img/tabbar/default/home.png create mode 100644 static/img/tabbar/default/homeactive.png create mode 100644 static/img/tabbar/default/me.png create mode 100644 static/img/tabbar/default/meactive.png create mode 100644 static/img/tabbar/default/news.png create mode 100644 static/img/tabbar/default/newsactive.png create mode 100755 static/img/tabbar/more/edit.png create mode 100644 static/img/tabbar/more/qa.png create mode 100755 static/img/tabbar/more/scan.png create mode 100755 static/img/tabbar/more/search.png create mode 100644 static/index.html create mode 100644 static/logo.png create mode 100644 static/scss/colorui.css create mode 100644 static/scss/global.scss create mode 100644 static/scss/index.scss create mode 100644 static/uni-center/headers.png create mode 100644 static/uni-load-state/disconnection.png create mode 100644 store/index.js create mode 100644 store/modules/user.js create mode 100644 store/modules/userCloud.js create mode 100644 uni.scss create mode 100644 uni_modules/Sansnn-uQRCode/changelog.md create mode 100644 uni_modules/Sansnn-uQRCode/common/cache.js create mode 100644 uni_modules/Sansnn-uQRCode/common/queue.js create mode 100644 uni_modules/Sansnn-uQRCode/common/types/cache.d.ts create mode 100644 uni_modules/Sansnn-uQRCode/common/types/queue.d.ts create mode 100644 uni_modules/Sansnn-uQRCode/components/u-qrcode/u-qrcode.vue create mode 100644 uni_modules/Sansnn-uQRCode/components/uqrcode/uqrcode.vue create mode 100644 uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/bridge/bridge-weex.js create mode 100644 uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-2d/FillStyleLinearGradient.js create mode 100644 uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-2d/FillStylePattern.js create mode 100644 uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-2d/FillStyleRadialGradient.js create mode 100644 uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-2d/RenderingContext.js create mode 100644 uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/ActiveInfo.js create mode 100644 uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/Buffer.js create mode 100644 uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/Framebuffer.js create mode 100644 uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/GLenum.js create mode 100644 uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/GLmethod.js create mode 100644 uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/GLtype.js create mode 100644 uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/Program.js create mode 100644 uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/Renderbuffer.js create mode 100644 uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/RenderingContext.js create mode 100644 uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/Shader.js create mode 100644 uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/ShaderPrecisionFormat.js create mode 100644 uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/Texture.js create mode 100644 uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/UniformLocation.js create mode 100644 uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/context-webgl/classUtils.js create mode 100644 uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/env/canvas.js create mode 100644 uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/env/image.js create mode 100644 uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/env/tool.js create mode 100644 uni_modules/Sansnn-uQRCode/js_sdk/gcanvas/index.js create mode 100644 uni_modules/Sansnn-uQRCode/js_sdk/uqrcode/package.json create mode 100644 uni_modules/Sansnn-uQRCode/js_sdk/uqrcode/uqrcode.js create mode 100644 uni_modules/Sansnn-uQRCode/license.md create mode 100644 uni_modules/Sansnn-uQRCode/package.json create mode 100644 uni_modules/Sansnn-uQRCode/readme.md create mode 100644 uni_modules/json-gps/changelog.md create mode 100644 uni_modules/json-gps/js_sdk/gps.js create mode 100644 uni_modules/json-gps/js_sdk/wa-permission/permission.js create mode 100644 uni_modules/json-gps/package.json create mode 100644 uni_modules/json-gps/readme.md create mode 100644 uni_modules/json-interceptor-chooseImage/changelog.md create mode 100644 uni_modules/json-interceptor-chooseImage/js_sdk/main.js create mode 100644 uni_modules/json-interceptor-chooseImage/package.json create mode 100644 uni_modules/json-interceptor-chooseImage/readme.md create mode 100644 uni_modules/m-start-ad/changelog.md create mode 100644 uni_modules/m-start-ad/components/m-start-ad/m-start-ad.vue create mode 100644 uni_modules/m-start-ad/package.json create mode 100644 uni_modules/m-start-ad/readme.md create mode 100644 uni_modules/uni-badge/changelog.md create mode 100644 uni_modules/uni-badge/components/uni-badge/uni-badge.vue create mode 100644 uni_modules/uni-badge/package.json create mode 100644 uni_modules/uni-badge/readme.md create mode 100644 uni_modules/uni-breadcrumb/changelog.md create mode 100644 uni_modules/uni-breadcrumb/components/uni-breadcrumb-item/uni-breadcrumb-item.vue create mode 100644 uni_modules/uni-breadcrumb/components/uni-breadcrumb/uni-breadcrumb.vue create mode 100644 uni_modules/uni-breadcrumb/package.json create mode 100644 uni_modules/uni-breadcrumb/readme.md create mode 100644 uni_modules/uni-calendar/changelog.md create mode 100644 uni_modules/uni-calendar/components/uni-calendar/calendar.js create mode 100644 uni_modules/uni-calendar/components/uni-calendar/i18n/en.json create mode 100644 uni_modules/uni-calendar/components/uni-calendar/i18n/index.js create mode 100644 uni_modules/uni-calendar/components/uni-calendar/i18n/zh-Hans.json create mode 100644 uni_modules/uni-calendar/components/uni-calendar/i18n/zh-Hant.json create mode 100644 uni_modules/uni-calendar/components/uni-calendar/uni-calendar-item.vue create mode 100644 uni_modules/uni-calendar/components/uni-calendar/uni-calendar.vue create mode 100644 uni_modules/uni-calendar/components/uni-calendar/util.js create mode 100644 uni_modules/uni-calendar/package.json create mode 100644 uni_modules/uni-calendar/readme.md create mode 100644 uni_modules/uni-captcha/changelog.md create mode 100644 uni_modules/uni-captcha/components/uni-captcha/uni-captcha.uvue create mode 100644 uni_modules/uni-captcha/components/uni-captcha/uni-captcha.vue create mode 100644 uni_modules/uni-captcha/components/uni-popup-captcha/uni-popup-captcha.uvue create mode 100644 uni_modules/uni-captcha/components/uni-popup-captcha/uni-popup-captcha.vue create mode 100644 uni_modules/uni-captcha/package.json create mode 100644 uni_modules/uni-captcha/readme.md create mode 100644 uni_modules/uni-captcha/static/run.gif create mode 100644 uni_modules/uni-captcha/uniCloud/cloudfunctions/common/uni-captcha/LICENSE.md create mode 100644 uni_modules/uni-captcha/uniCloud/cloudfunctions/common/uni-captcha/fonts/font.ttf create mode 100644 uni_modules/uni-captcha/uniCloud/cloudfunctions/common/uni-captcha/index.js create mode 100644 uni_modules/uni-captcha/uniCloud/cloudfunctions/common/uni-captcha/package.json create mode 100644 uni_modules/uni-captcha/uniCloud/cloudfunctions/uni-captcha-co/config.js create mode 100644 uni_modules/uni-captcha/uniCloud/cloudfunctions/uni-captcha-co/index.obj.js create mode 100644 uni_modules/uni-captcha/uniCloud/cloudfunctions/uni-captcha-co/package.json create mode 100644 uni_modules/uni-captcha/uniCloud/database/opendb-verify-codes.schema.json create mode 100644 uni_modules/uni-card/changelog.md create mode 100644 uni_modules/uni-card/components/uni-card/uni-card.vue create mode 100644 uni_modules/uni-card/package.json create mode 100644 uni_modules/uni-card/readme.md create mode 100644 uni_modules/uni-cloud-s2s/changelog.md create mode 100644 uni_modules/uni-cloud-s2s/package.json create mode 100644 uni_modules/uni-cloud-s2s/readme.md create mode 100644 uni_modules/uni-cloud-s2s/uniCloud/cloudfunctions/common/uni-cloud-s2s/index.js create mode 100644 uni_modules/uni-cloud-s2s/uniCloud/cloudfunctions/common/uni-cloud-s2s/package.json create mode 100644 uni_modules/uni-cms-article/changelog.md create mode 100644 uni_modules/uni-cms-article/common/parse-image-url.js create mode 100644 uni_modules/uni-cms-article/common/parse-image-url.uts create mode 100644 uni_modules/uni-cms-article/common/parse-scan-result.js create mode 100644 uni_modules/uni-cms-article/common/parse-scan-result.uts create mode 100644 uni_modules/uni-cms-article/common/publish-time.js create mode 100644 uni_modules/uni-cms-article/common/publish-time.uts create mode 100644 uni_modules/uni-cms-article/components/list-template/not-cover.uvue create mode 100644 uni_modules/uni-cms-article/components/list-template/not-cover.vue create mode 100644 uni_modules/uni-cms-article/components/list-template/right-small-cover.uvue create mode 100644 uni_modules/uni-cms-article/components/list-template/right-small-cover.vue create mode 100644 uni_modules/uni-cms-article/components/list-template/style.scss create mode 100644 uni_modules/uni-cms-article/components/list-template/three-cover.uvue create mode 100644 uni_modules/uni-cms-article/components/list-template/three-cover.vue create mode 100644 uni_modules/uni-cms-article/components/refresh-box/refreshBox.nvue create mode 100644 uni_modules/uni-cms-article/components/refresh-box/refreshBox.uvue create mode 100644 uni_modules/uni-cms-article/components/render-article-detail/image.uvue create mode 100644 uni_modules/uni-cms-article/components/render-article-detail/image.vue create mode 100644 uni_modules/uni-cms-article/components/render-article-detail/index.vue create mode 100644 uni_modules/uni-cms-article/components/render-article-detail/list.vue create mode 100644 uni_modules/uni-cms-article/components/render-article-detail/text.vue create mode 100644 uni_modules/uni-cms-article/components/render-article-detail/unlock-content.vue create mode 100644 uni_modules/uni-cms-article/components/render-article-detail/video.vue create mode 100644 uni_modules/uni-cms-article/components/uni-cms-article-icons/uni-cms-article-icons.uvue create mode 100644 uni_modules/uni-cms-article/components/uni-cms-article-list/uni-cms-article-list.vue create mode 100644 uni_modules/uni-cms-article/components/uni-cms-article-search-bar/uni-cms-article-search-bar.uvue create mode 100644 uni_modules/uni-cms-article/components/uni-load-state/i18n/en.json create mode 100644 uni_modules/uni-cms-article/components/uni-load-state/i18n/index.js create mode 100644 uni_modules/uni-cms-article/components/uni-load-state/i18n/zh-Hans.json create mode 100644 uni_modules/uni-cms-article/components/uni-load-state/readme.md create mode 100644 uni_modules/uni-cms-article/components/uni-load-state/uni-load-state.vue create mode 100644 uni_modules/uni-cms-article/license.md create mode 100644 uni_modules/uni-cms-article/package.json create mode 100644 uni_modules/uni-cms-article/pages/detail/detail.uvue create mode 100644 uni_modules/uni-cms-article/pages/detail/detail.vue create mode 100644 uni_modules/uni-cms-article/pages/detail/preview.vue create mode 100644 uni_modules/uni-cms-article/pages/list/list.nvue create mode 100644 uni_modules/uni-cms-article/pages/list/list.uvue create mode 100644 uni_modules/uni-cms-article/pages/search/search.nvue create mode 100644 uni_modules/uni-cms-article/pages/search/search.uvue create mode 100644 uni_modules/uni-cms-article/pages/webview/webview.uvue create mode 100644 uni_modules/uni-cms-article/pages/webview/webview.vue create mode 100644 uni_modules/uni-cms-article/readme.md create mode 100644 uni_modules/uni-cms-article/static/disconnection.png create mode 100644 uni_modules/uni-cms-article/static/uniicons.ttf create mode 100644 uni_modules/uni-cms-article/uniCloud/cloudfunctions/common/quill-delta-converter/core.js create mode 100644 uni_modules/uni-cms-article/uniCloud/cloudfunctions/common/quill-delta-converter/index.js create mode 100644 uni_modules/uni-cms-article/uniCloud/cloudfunctions/common/quill-delta-converter/package.json create mode 100644 uni_modules/uni-cms-article/uniCloud/cloudfunctions/uni-cms-unlock-callback/index.js create mode 100644 uni_modules/uni-cms-article/uniCloud/cloudfunctions/uni-cms-unlock-callback/package.json create mode 100644 uni_modules/uni-cms-article/uniCloud/database/db_init.json create mode 100644 uni_modules/uni-cms-article/uniCloud/database/opendb-search-hot.schema.json create mode 100644 uni_modules/uni-cms-article/uniCloud/database/opendb-search-log.schema.json create mode 100644 uni_modules/uni-cms-article/uniCloud/database/opendb-search-logs.schema.json create mode 100644 uni_modules/uni-cms-article/uniCloud/database/uni-cms-articles.schema.ext.js create mode 100644 uni_modules/uni-cms-article/uniCloud/database/uni-cms-articles.schema.json create mode 100644 uni_modules/uni-cms-article/uniCloud/database/uni-cms-unlock-record.schema.json create mode 100644 uni_modules/uni-collapse/changelog.md create mode 100644 uni_modules/uni-collapse/components/uni-collapse-item/uni-collapse-item.vue create mode 100644 uni_modules/uni-collapse/components/uni-collapse/uni-collapse.vue create mode 100644 uni_modules/uni-collapse/package.json create mode 100644 uni_modules/uni-collapse/readme.md create mode 100644 uni_modules/uni-combox/changelog.md create mode 100644 uni_modules/uni-combox/components/uni-combox/uni-combox.vue create mode 100644 uni_modules/uni-combox/package.json create mode 100644 uni_modules/uni-combox/readme.md create mode 100644 uni_modules/uni-config-center/changelog.md create mode 100644 uni_modules/uni-config-center/package.json create mode 100644 uni_modules/uni-config-center/readme.md create mode 100644 uni_modules/uni-config-center/uniCloud/cloudfunctions/common/uni-config-center/index.js create mode 100644 uni_modules/uni-config-center/uniCloud/cloudfunctions/common/uni-config-center/package.json create mode 100644 uni_modules/uni-config-center/uniCloud/cloudfunctions/common/uni-config-center/uni-ad/config.json create mode 100644 uni_modules/uni-config-center/uniCloud/cloudfunctions/common/uni-config-center/uni-open-bridge/config.json create mode 100644 uni_modules/uni-countdown/changelog.md create mode 100644 uni_modules/uni-countdown/components/uni-countdown/i18n/en.json create mode 100644 uni_modules/uni-countdown/components/uni-countdown/i18n/index.js create mode 100644 uni_modules/uni-countdown/components/uni-countdown/i18n/zh-Hans.json create mode 100644 uni_modules/uni-countdown/components/uni-countdown/i18n/zh-Hant.json create mode 100644 uni_modules/uni-countdown/components/uni-countdown/uni-countdown.vue create mode 100644 uni_modules/uni-countdown/package.json create mode 100644 uni_modules/uni-countdown/readme.md create mode 100644 uni_modules/uni-data-checkbox/changelog.md create mode 100644 uni_modules/uni-data-checkbox/components/uni-data-checkbox/clientdb.js create mode 100644 uni_modules/uni-data-checkbox/components/uni-data-checkbox/uni-data-checkbox.vue create mode 100644 uni_modules/uni-data-checkbox/package.json create mode 100644 uni_modules/uni-data-checkbox/readme.md create mode 100644 uni_modules/uni-data-picker/changelog.md create mode 100644 uni_modules/uni-data-picker/components/uni-data-picker/config.json create mode 100644 uni_modules/uni-data-picker/components/uni-data-picker/keypress.js create mode 100644 uni_modules/uni-data-picker/components/uni-data-picker/uni-data-picker.uvue create mode 100644 uni_modules/uni-data-picker/components/uni-data-picker/uni-data-picker.vue create mode 100644 uni_modules/uni-data-picker/components/uni-data-pickerview/loading.uts create mode 100644 uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-picker.js create mode 100644 uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-picker.uts create mode 100644 uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-pickerview.css create mode 100644 uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-pickerview.uvue create mode 100644 uni_modules/uni-data-picker/components/uni-data-pickerview/uni-data-pickerview.vue create mode 100644 uni_modules/uni-data-picker/package.json create mode 100644 uni_modules/uni-data-picker/readme.md create mode 100644 uni_modules/uni-data-select/changelog.md create mode 100644 uni_modules/uni-data-select/components/uni-data-select/uni-data-select.vue create mode 100644 uni_modules/uni-data-select/package.json create mode 100644 uni_modules/uni-data-select/readme.md create mode 100644 uni_modules/uni-dateformat/changelog.md create mode 100644 uni_modules/uni-dateformat/components/uni-dateformat/date-format.js create mode 100644 uni_modules/uni-dateformat/components/uni-dateformat/uni-dateformat.vue create mode 100644 uni_modules/uni-dateformat/package.json create mode 100644 uni_modules/uni-dateformat/readme.md create mode 100644 uni_modules/uni-datetime-picker/changelog.md create mode 100644 uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar-item.vue create mode 100644 uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar.js create mode 100644 uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar.vue create mode 100644 uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/en.json create mode 100644 uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/index.js create mode 100644 uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hans.json create mode 100644 uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hant.json create mode 100644 uni_modules/uni-datetime-picker/components/uni-datetime-picker/keypress.js create mode 100644 uni_modules/uni-datetime-picker/components/uni-datetime-picker/time-picker.vue create mode 100644 uni_modules/uni-datetime-picker/components/uni-datetime-picker/uni-datetime-picker.vue create mode 100644 uni_modules/uni-datetime-picker/components/uni-datetime-picker/util.js create mode 100644 uni_modules/uni-datetime-picker/package.json create mode 100644 uni_modules/uni-datetime-picker/readme.md create mode 100644 uni_modules/uni-drawer/changelog.md create mode 100644 uni_modules/uni-drawer/components/uni-drawer/keypress.js create mode 100644 uni_modules/uni-drawer/components/uni-drawer/uni-drawer.vue create mode 100644 uni_modules/uni-drawer/package.json create mode 100644 uni_modules/uni-drawer/readme.md create mode 100644 uni_modules/uni-easyinput/changelog.md create mode 100644 uni_modules/uni-easyinput/components/uni-easyinput/common.js create mode 100644 uni_modules/uni-easyinput/components/uni-easyinput/uni-easyinput.vue create mode 100644 uni_modules/uni-easyinput/package.json create mode 100644 uni_modules/uni-easyinput/readme.md create mode 100644 uni_modules/uni-fab/changelog.md create mode 100644 uni_modules/uni-fab/components/uni-fab/uni-fab.vue create mode 100644 uni_modules/uni-fab/components/uni-fab/uni-fab.vue.bak create mode 100644 uni_modules/uni-fab/package.json create mode 100644 uni_modules/uni-fab/readme.md create mode 100644 uni_modules/uni-fav/changelog.md create mode 100644 uni_modules/uni-fav/components/uni-fav/i18n/en.json create mode 100644 uni_modules/uni-fav/components/uni-fav/i18n/index.js create mode 100644 uni_modules/uni-fav/components/uni-fav/i18n/zh-Hans.json create mode 100644 uni_modules/uni-fav/components/uni-fav/i18n/zh-Hant.json create mode 100644 uni_modules/uni-fav/components/uni-fav/uni-fav.vue create mode 100644 uni_modules/uni-fav/package.json create mode 100644 uni_modules/uni-fav/readme.md create mode 100644 uni_modules/uni-feedback/changelog.md create mode 100644 uni_modules/uni-feedback/js_sdk/validator/opendb-feedback.js create mode 100644 uni_modules/uni-feedback/package.json create mode 100644 uni_modules/uni-feedback/pages/opendb-feedback/detail.vue create mode 100644 uni_modules/uni-feedback/pages/opendb-feedback/edit.vue create mode 100644 uni_modules/uni-feedback/pages/opendb-feedback/list.vue create mode 100644 uni_modules/uni-feedback/pages/opendb-feedback/opendb-feedback.vue create mode 100644 uni_modules/uni-feedback/readme.md create mode 100644 uni_modules/uni-feedback/uniCloud/database/opendb-feedback.schema.json create mode 100644 uni_modules/uni-file-picker/changelog.md create mode 100644 uni_modules/uni-file-picker/components/uni-file-picker/choose-and-upload-file.js create mode 100644 uni_modules/uni-file-picker/components/uni-file-picker/uni-file-picker.vue create mode 100644 uni_modules/uni-file-picker/components/uni-file-picker/upload-file.vue create mode 100644 uni_modules/uni-file-picker/components/uni-file-picker/upload-image.vue create mode 100644 uni_modules/uni-file-picker/components/uni-file-picker/utils.js create mode 100644 uni_modules/uni-file-picker/package.json create mode 100644 uni_modules/uni-file-picker/readme.md create mode 100644 uni_modules/uni-forms/changelog.md create mode 100644 uni_modules/uni-forms/components/uni-forms-item/uni-forms-item.vue create mode 100644 uni_modules/uni-forms/components/uni-forms/uni-forms.vue create mode 100644 uni_modules/uni-forms/components/uni-forms/utils.js create mode 100644 uni_modules/uni-forms/components/uni-forms/validate.js create mode 100644 uni_modules/uni-forms/package.json create mode 100644 uni_modules/uni-forms/readme.md create mode 100644 uni_modules/uni-goods-nav/changelog.md create mode 100644 uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/en.json create mode 100644 uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/index.js create mode 100644 uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/zh-Hans.json create mode 100644 uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/zh-Hant.json create mode 100644 uni_modules/uni-goods-nav/components/uni-goods-nav/uni-goods-nav.vue create mode 100644 uni_modules/uni-goods-nav/package.json create mode 100644 uni_modules/uni-goods-nav/readme.md create mode 100644 uni_modules/uni-grid/changelog.md create mode 100644 uni_modules/uni-grid/components/uni-grid-item/uni-grid-item.vue create mode 100644 uni_modules/uni-grid/components/uni-grid/uni-grid.vue create mode 100644 uni_modules/uni-grid/package.json create mode 100644 uni_modules/uni-grid/readme.md create mode 100644 uni_modules/uni-group/changelog.md create mode 100644 uni_modules/uni-group/components/uni-group/uni-group.vue create mode 100644 uni_modules/uni-group/package.json create mode 100644 uni_modules/uni-group/readme.md create mode 100644 uni_modules/uni-icons/changelog.md create mode 100644 uni_modules/uni-icons/components/uni-icons/icons.js create mode 100644 uni_modules/uni-icons/components/uni-icons/uni-icons.uvue create mode 100644 uni_modules/uni-icons/components/uni-icons/uni-icons.vue create mode 100644 uni_modules/uni-icons/components/uni-icons/uni.ttf create mode 100644 uni_modules/uni-icons/components/uni-icons/uniicons.css create mode 100644 uni_modules/uni-icons/components/uni-icons/uniicons.ttf create mode 100644 uni_modules/uni-icons/components/uni-icons/uniicons_file.ts create mode 100644 uni_modules/uni-icons/components/uni-icons/uniicons_file_vue.js create mode 100644 uni_modules/uni-icons/package.json create mode 100644 uni_modules/uni-icons/readme.md create mode 100644 uni_modules/uni-id-common/changelog.md create mode 100644 uni_modules/uni-id-common/package.json create mode 100644 uni_modules/uni-id-common/readme.md create mode 100644 uni_modules/uni-id-common/uniCloud/cloudfunctions/common/uni-id-common/index.js create mode 100644 uni_modules/uni-id-common/uniCloud/cloudfunctions/common/uni-id-common/package.json create mode 100644 uni_modules/uni-id-pages/changelog.md create mode 100644 uni_modules/uni-id-pages/common/check-id-card.js create mode 100644 uni_modules/uni-id-pages/common/login-page.mixin.js create mode 100644 uni_modules/uni-id-pages/common/login-page.scss create mode 100644 uni_modules/uni-id-pages/common/password.js create mode 100644 uni_modules/uni-id-pages/common/store.js create mode 100644 uni_modules/uni-id-pages/components/cloud-image/cloud-image.vue create mode 100644 uni_modules/uni-id-pages/components/uni-id-pages-agreements/uni-id-pages-agreements.vue create mode 100644 uni_modules/uni-id-pages/components/uni-id-pages-avatar/uni-id-pages-avatar.vue create mode 100644 uni_modules/uni-id-pages/components/uni-id-pages-bind-mobile/uni-id-pages-bind-mobile.vue create mode 100644 uni_modules/uni-id-pages/components/uni-id-pages-email-form/uni-id-pages-email-form.vue create mode 100644 uni_modules/uni-id-pages/components/uni-id-pages-fab-login/uni-id-pages-fab-login.vue create mode 100644 uni_modules/uni-id-pages/components/uni-id-pages-sms-form/uni-id-pages-sms-form.vue create mode 100644 uni_modules/uni-id-pages/components/uni-id-pages-user-profile/uni-id-pages-user-profile.vue create mode 100644 uni_modules/uni-id-pages/config copy.js create mode 100644 uni_modules/uni-id-pages/config.js create mode 100644 uni_modules/uni-id-pages/init.js create mode 100644 uni_modules/uni-id-pages/package.json create mode 100644 uni_modules/uni-id-pages/pages/common/webview/webview.vue create mode 100644 uni_modules/uni-id-pages/pages/login/login-smscode.vue create mode 100644 uni_modules/uni-id-pages/pages/login/login-withoutpwd.vue create mode 100644 uni_modules/uni-id-pages/pages/login/login-withpwd.vue create mode 100644 uni_modules/uni-id-pages/pages/register/register-admin.vue create mode 100644 uni_modules/uni-id-pages/pages/register/register-by-email.vue create mode 100644 uni_modules/uni-id-pages/pages/register/register.vue create mode 100644 uni_modules/uni-id-pages/pages/register/validator.js create mode 100644 uni_modules/uni-id-pages/pages/retrieve/retrieve-by-email.vue create mode 100644 uni_modules/uni-id-pages/pages/retrieve/retrieve.vue create mode 100644 uni_modules/uni-id-pages/pages/userinfo/bind-mobile/bind-mobile.vue create mode 100644 uni_modules/uni-id-pages/pages/userinfo/change_pwd/change_pwd.vue create mode 100644 uni_modules/uni-id-pages/pages/userinfo/cropImage/cropImage.vue create mode 100644 uni_modules/uni-id-pages/pages/userinfo/cropImage/limeClipper/README.md create mode 100644 uni_modules/uni-id-pages/pages/userinfo/cropImage/limeClipper/images/photo.svg create mode 100644 uni_modules/uni-id-pages/pages/userinfo/cropImage/limeClipper/images/rotate.svg create mode 100644 uni_modules/uni-id-pages/pages/userinfo/cropImage/limeClipper/index.css create mode 100644 uni_modules/uni-id-pages/pages/userinfo/cropImage/limeClipper/limeClipper.vue create mode 100644 uni_modules/uni-id-pages/pages/userinfo/cropImage/limeClipper/utils.js create mode 100644 uni_modules/uni-id-pages/pages/userinfo/deactivate/deactivate.vue create mode 100644 uni_modules/uni-id-pages/pages/userinfo/realname-verify/face-verify-icon.svg create mode 100644 uni_modules/uni-id-pages/pages/userinfo/realname-verify/realname-verify.vue create mode 100644 uni_modules/uni-id-pages/pages/userinfo/set-pwd/set-pwd.vue create mode 100644 uni_modules/uni-id-pages/pages/userinfo/userinfo.vue create mode 100644 uni_modules/uni-id-pages/readme.md create mode 100644 uni_modules/uni-id-pages/static/app-plus/apple.png create mode 100644 uni_modules/uni-id-pages/static/app-plus/uni-fab-login/alipay.png create mode 100644 uni_modules/uni-id-pages/static/app-plus/uni-fab-login/apple.png create mode 100644 uni_modules/uni-id-pages/static/app-plus/uni-fab-login/douyin.png create mode 100644 uni_modules/uni-id-pages/static/app-plus/uni-fab-login/facebook.png create mode 100644 uni_modules/uni-id-pages/static/app-plus/uni-fab-login/google.png create mode 100644 uni_modules/uni-id-pages/static/app-plus/uni-fab-login/qq.png create mode 100644 uni_modules/uni-id-pages/static/app-plus/uni-fab-login/sinaweibo.png create mode 100644 uni_modules/uni-id-pages/static/app-plus/uni-fab-login/taobao.png create mode 100644 uni_modules/uni-id-pages/static/app-plus/uni-fab-login/univerify.png create mode 100644 uni_modules/uni-id-pages/static/limeClipper/photo.svg create mode 100644 uni_modules/uni-id-pages/static/limeClipper/rotate.svg create mode 100644 uni_modules/uni-id-pages/static/login/uni-fab-login/sms.png create mode 100644 uni_modules/uni-id-pages/static/login/uni-fab-login/user.png create mode 100644 uni_modules/uni-id-pages/static/login/uni-fab-login/weixin.png create mode 100644 uni_modules/uni-id-pages/static/login/weixin.png create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/common/constants.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/common/error.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/common/sensitive-aes-cipher.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/common/universal.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/common/utils.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/common/validator.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/config/permission.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/index.obj.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lang/en.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lang/index.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lang/zh-hans.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/README.md create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/npm/index.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/alipay/account/index.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/alipay/account/protocols.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/alipay/alipayBase.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/apple/account/index.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/apple/rsa-public-key-pem.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/index.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/qq/account/index.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/qq/account/protocol.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/qq/normalize.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/share/create-api.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/weixin/account/index.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/weixin/normalize.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/weixin/utils.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/account.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/captcha.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/config.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/fission.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/login.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/logout.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/password.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/qq.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/register.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/relate.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/sms.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/unified-login.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/univerify.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/update-user-info.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/utils.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/verify-code.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/weixin.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/middleware/access-control.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/middleware/auth.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/middleware/index.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/middleware/rbac.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/middleware/uni-id-log.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/middleware/validate.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/middleware/verify-request-sign.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/account/close-account.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/account/get-account-info.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/account/get-realname-info.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/account/index.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/account/reset-pwd-by-email.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/account/reset-pwd-by-sms.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/account/set-pwd.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/account/update-pwd.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/admin/add-user.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/admin/index.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/admin/update-user.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/dev/get-supported-login-type.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/dev/index.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/external/index.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/external/login.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/external/register.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/external/update-user-info.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/facial-recognition-verify/get-auth-result.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/facial-recognition-verify/get-certify-id.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/facial-recognition-verify/index.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/fission/accept-invite.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/fission/get-invited-user.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/fission/index.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/index.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-alipay.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-apple.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-baidu.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-dingtalk.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-douyin.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-email-code.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-email-link.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-facebook.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-google.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-qq.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-sms.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-taobao.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-toutiao.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-univerify.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-weibo.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-weixin-mobile.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-weixin.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/logout/index.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/logout/logout.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/multi-end/authorize-app-login.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/multi-end/index.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/multi-end/remove-authorized-app.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/multi-end/set-authorized-app.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/multi-end/utils.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/register/index.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/register/register-admin.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/register/register-user-by-email.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/register/register-user.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/bind-alipay.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/bind-apple.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/bind-mobile-by-mp-weixin.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/bind-mobile-by-sms.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/bind-mobile-by-univerify.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/bind-qq.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/bind-weixin.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/index.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/unbind-alipay.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/unbind-apple.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/unbind-qq.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/unbind-weixin.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/utils/index.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/utils/refresh-token.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/utils/secure-network-handshake-by-weixin.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/utils/set-push-cid.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/verify/create-captcha.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/verify/index.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/verify/refresh-captcha.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/verify/send-email-code.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/verify/send-email-link.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/verify/send-sms-code.js create mode 100644 uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/package.json create mode 100644 uni_modules/uni-id-pages/uniCloud/database/opendb-device.schema.json create mode 100644 uni_modules/uni-id-pages/uniCloud/database/opendb-frv-logs.schema.json create mode 100644 uni_modules/uni-id-pages/uniCloud/database/uni-id-device.schema.json create mode 100644 uni_modules/uni-id-pages/uniCloud/database/uni-id-log.schema.json create mode 100644 uni_modules/uni-id-pages/uniCloud/database/uni-id-permissions.schema.json create mode 100644 uni_modules/uni-id-pages/uniCloud/database/uni-id-roles.schema.json create mode 100644 uni_modules/uni-id-pages/uniCloud/database/uni-id-users.schema.json create mode 100644 uni_modules/uni-image-menu/changelog.md create mode 100644 uni_modules/uni-image-menu/js_sdk/uni-image-menu.js create mode 100644 uni_modules/uni-image-menu/package.json create mode 100644 uni_modules/uni-image-menu/readme.md create mode 100644 uni_modules/uni-indexed-list/changelog.md create mode 100644 uni_modules/uni-indexed-list/components/uni-indexed-list/uni-indexed-list-item.vue create mode 100644 uni_modules/uni-indexed-list/components/uni-indexed-list/uni-indexed-list.vue create mode 100644 uni_modules/uni-indexed-list/package.json create mode 100644 uni_modules/uni-indexed-list/readme.md create mode 100644 uni_modules/uni-installApk/changelog.md create mode 100644 uni_modules/uni-installApk/package.json create mode 100644 uni_modules/uni-installApk/readme.md create mode 100644 uni_modules/uni-installApk/utssdk/app-android/AndroidManifest.xml create mode 100644 uni_modules/uni-installApk/utssdk/app-android/index.uts create mode 100644 uni_modules/uni-installApk/utssdk/index.d.ts create mode 100644 uni_modules/uni-installApk/utssdk/interface.uts create mode 100644 uni_modules/uni-installApk/utssdk/unierror.uts create mode 100644 uni_modules/uni-link/changelog.md create mode 100644 uni_modules/uni-link/components/uni-link/uni-link.vue create mode 100644 uni_modules/uni-link/package.json create mode 100644 uni_modules/uni-link/readme.md create mode 100644 uni_modules/uni-list/changelog.md create mode 100644 uni_modules/uni-list/components/uni-list-ad/uni-list-ad.vue create mode 100644 uni_modules/uni-list/components/uni-list-chat/uni-list-chat.scss create mode 100644 uni_modules/uni-list/components/uni-list-chat/uni-list-chat.vue create mode 100644 uni_modules/uni-list/components/uni-list-item/uni-list-item.vue create mode 100644 uni_modules/uni-list/components/uni-list/uni-list - 副本.vue create mode 100644 uni_modules/uni-list/components/uni-list/uni-list.vue create mode 100644 uni_modules/uni-list/components/uni-list/uni-refresh.vue create mode 100644 uni_modules/uni-list/components/uni-list/uni-refresh.wxs create mode 100644 uni_modules/uni-list/package.json create mode 100644 uni_modules/uni-list/readme.md create mode 100644 uni_modules/uni-load-more/changelog.md create mode 100644 uni_modules/uni-load-more/components/uni-load-more/i18n/en.json create mode 100644 uni_modules/uni-load-more/components/uni-load-more/i18n/index.js create mode 100644 uni_modules/uni-load-more/components/uni-load-more/i18n/zh-Hans.json create mode 100644 uni_modules/uni-load-more/components/uni-load-more/i18n/zh-Hant.json create mode 100644 uni_modules/uni-load-more/components/uni-load-more/uni-load-more.vue create mode 100644 uni_modules/uni-load-more/package.json create mode 100644 uni_modules/uni-load-more/readme.md create mode 100644 uni_modules/uni-nav-bar/changelog.md create mode 100644 uni_modules/uni-nav-bar/components/uni-nav-bar/uni-nav-bar.vue create mode 100644 uni_modules/uni-nav-bar/components/uni-nav-bar/uni-status-bar.vue create mode 100644 uni_modules/uni-nav-bar/package.json create mode 100644 uni_modules/uni-nav-bar/readme.md create mode 100644 uni_modules/uni-notice-bar/changelog.md create mode 100644 uni_modules/uni-notice-bar/components/uni-notice-bar/uni-notice-bar.vue create mode 100644 uni_modules/uni-notice-bar/package.json create mode 100644 uni_modules/uni-notice-bar/readme.md create mode 100644 uni_modules/uni-number-box/changelog.md create mode 100644 uni_modules/uni-number-box/components/uni-number-box/uni-number-box.vue create mode 100644 uni_modules/uni-number-box/package.json create mode 100644 uni_modules/uni-number-box/readme.md create mode 100644 uni_modules/uni-open-bridge-common/changelog.md create mode 100644 uni_modules/uni-open-bridge-common/package.json create mode 100644 uni_modules/uni-open-bridge-common/readme.md create mode 100644 uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/bridge-error.js create mode 100644 uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/config.js create mode 100644 uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/consts.js create mode 100644 uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/index.js create mode 100644 uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/package.json create mode 100644 uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/storage.js create mode 100644 uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/uni-cloud-cache.js create mode 100644 uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/validator.js create mode 100644 uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/weixin-server.js create mode 100644 uni_modules/uni-open-bridge-common/uniCloud/database/opendb-open-data.schema.json create mode 100644 uni_modules/uni-open-bridge/changelog.md create mode 100644 uni_modules/uni-open-bridge/package.json create mode 100644 uni_modules/uni-open-bridge/readme.md create mode 100644 uni_modules/uni-open-bridge/uniCloud/cloudfunctions/uni-open-bridge/basic.js create mode 100644 uni_modules/uni-open-bridge/uniCloud/cloudfunctions/uni-open-bridge/config.js create mode 100644 uni_modules/uni-open-bridge/uniCloud/cloudfunctions/uni-open-bridge/consts.js create mode 100644 uni_modules/uni-open-bridge/uniCloud/cloudfunctions/uni-open-bridge/index.mp-weixin.js create mode 100644 uni_modules/uni-open-bridge/uniCloud/cloudfunctions/uni-open-bridge/index.obj.js create mode 100644 uni_modules/uni-open-bridge/uniCloud/cloudfunctions/uni-open-bridge/index.task.js create mode 100644 uni_modules/uni-open-bridge/uniCloud/cloudfunctions/uni-open-bridge/package.json create mode 100644 uni_modules/uni-open-bridge/uniCloud/cloudfunctions/uni-open-bridge/task-h5-weixin.js create mode 100644 uni_modules/uni-open-bridge/uniCloud/cloudfunctions/uni-open-bridge/task-mp-weixin.js create mode 100644 uni_modules/uni-pagination/changelog.md create mode 100644 uni_modules/uni-pagination/components/uni-pagination/i18n/en.json create mode 100644 uni_modules/uni-pagination/components/uni-pagination/i18n/es.json create mode 100644 uni_modules/uni-pagination/components/uni-pagination/i18n/fr.json create mode 100644 uni_modules/uni-pagination/components/uni-pagination/i18n/index.js create mode 100644 uni_modules/uni-pagination/components/uni-pagination/i18n/zh-Hans.json create mode 100644 uni_modules/uni-pagination/components/uni-pagination/i18n/zh-Hant.json create mode 100644 uni_modules/uni-pagination/components/uni-pagination/uni-pagination.vue create mode 100644 uni_modules/uni-pagination/package.json create mode 100644 uni_modules/uni-pagination/readme.md create mode 100644 uni_modules/uni-popup/changelog.md create mode 100644 uni_modules/uni-popup/components/uni-popup-dialog/keypress.js create mode 100644 uni_modules/uni-popup/components/uni-popup-dialog/uni-popup-dialog.vue create mode 100644 uni_modules/uni-popup/components/uni-popup-message/uni-popup-message.vue create mode 100644 uni_modules/uni-popup/components/uni-popup-share/uni-popup-share.vue create mode 100644 uni_modules/uni-popup/components/uni-popup/i18n/en.json create mode 100644 uni_modules/uni-popup/components/uni-popup/i18n/index.js create mode 100644 uni_modules/uni-popup/components/uni-popup/i18n/zh-Hans.json create mode 100644 uni_modules/uni-popup/components/uni-popup/i18n/zh-Hant.json create mode 100644 uni_modules/uni-popup/components/uni-popup/keypress.js create mode 100644 uni_modules/uni-popup/components/uni-popup/message.js create mode 100644 uni_modules/uni-popup/components/uni-popup/popup.js create mode 100644 uni_modules/uni-popup/components/uni-popup/share.js create mode 100644 uni_modules/uni-popup/components/uni-popup/uni-popup.uvue create mode 100644 uni_modules/uni-popup/components/uni-popup/uni-popup.vue create mode 100644 uni_modules/uni-popup/package.json create mode 100644 uni_modules/uni-popup/readme.md create mode 100644 uni_modules/uni-rate/changelog.md create mode 100644 uni_modules/uni-rate/components/uni-rate/uni-rate.vue create mode 100644 uni_modules/uni-rate/package.json create mode 100644 uni_modules/uni-rate/readme.md create mode 100644 uni_modules/uni-row/changelog.md create mode 100644 uni_modules/uni-row/components/uni-col/uni-col.vue create mode 100644 uni_modules/uni-row/components/uni-row/uni-row.vue create mode 100644 uni_modules/uni-row/package.json create mode 100644 uni_modules/uni-row/readme.md create mode 100644 uni_modules/uni-scss/changelog.md create mode 100644 uni_modules/uni-scss/index.scss create mode 100644 uni_modules/uni-scss/package.json create mode 100644 uni_modules/uni-scss/readme.md create mode 100644 uni_modules/uni-scss/styles/index.scss create mode 100644 uni_modules/uni-scss/styles/setting/_border.scss create mode 100644 uni_modules/uni-scss/styles/setting/_color.scss create mode 100644 uni_modules/uni-scss/styles/setting/_radius.scss create mode 100644 uni_modules/uni-scss/styles/setting/_space.scss create mode 100644 uni_modules/uni-scss/styles/setting/_styles.scss create mode 100644 uni_modules/uni-scss/styles/setting/_text.scss create mode 100644 uni_modules/uni-scss/styles/setting/_variables.scss create mode 100644 uni_modules/uni-scss/styles/tools/functions.scss create mode 100644 uni_modules/uni-scss/theme.scss create mode 100644 uni_modules/uni-scss/variables.scss create mode 100644 uni_modules/uni-search-bar/changelog.md create mode 100644 uni_modules/uni-search-bar/components/uni-search-bar/i18n/en.json create mode 100644 uni_modules/uni-search-bar/components/uni-search-bar/i18n/index.js create mode 100644 uni_modules/uni-search-bar/components/uni-search-bar/i18n/zh-Hans.json create mode 100644 uni_modules/uni-search-bar/components/uni-search-bar/i18n/zh-Hant.json create mode 100644 uni_modules/uni-search-bar/components/uni-search-bar/uni-search-bar.vue create mode 100644 uni_modules/uni-search-bar/package.json create mode 100644 uni_modules/uni-search-bar/readme.md create mode 100644 uni_modules/uni-section/changelog.md create mode 100644 uni_modules/uni-section/components/uni-section/uni-section.vue create mode 100644 uni_modules/uni-section/package.json create mode 100644 uni_modules/uni-section/readme.md create mode 100644 uni_modules/uni-segmented-control/changelog.md create mode 100644 uni_modules/uni-segmented-control/components/uni-segmented-control/uni-segmented-control.vue create mode 100644 uni_modules/uni-segmented-control/package.json create mode 100644 uni_modules/uni-segmented-control/readme.md create mode 100644 uni_modules/uni-share/changelog.md create mode 100644 uni_modules/uni-share/js_sdk/uni-image-menu.js create mode 100644 uni_modules/uni-share/js_sdk/uni-share.js create mode 100644 uni_modules/uni-share/package.json create mode 100644 uni_modules/uni-share/readme.md create mode 100644 uni_modules/uni-sign-in/changelog.md create mode 100644 uni_modules/uni-sign-in/components/uni-sign-in/uni-sign-in.vue create mode 100644 uni_modules/uni-sign-in/package.json create mode 100644 uni_modules/uni-sign-in/pages/demo/demo.vue create mode 100644 uni_modules/uni-sign-in/readme.md create mode 100644 uni_modules/uni-sign-in/static/background.png create mode 100644 uni_modules/uni-sign-in/uniCloud/cloudfunctions/common/sign-in/index.js create mode 100644 uni_modules/uni-sign-in/uniCloud/cloudfunctions/common/sign-in/package.json create mode 100644 uni_modules/uni-sign-in/uniCloud/cloudfunctions/rewarded-video-ad-notify-url/index.js create mode 100644 uni_modules/uni-sign-in/uniCloud/cloudfunctions/rewarded-video-ad-notify-url/node_modules/sign-in/index.js create mode 100644 uni_modules/uni-sign-in/uniCloud/cloudfunctions/rewarded-video-ad-notify-url/node_modules/sign-in/package.json create mode 100644 uni_modules/uni-sign-in/uniCloud/cloudfunctions/rewarded-video-ad-notify-url/node_modules/uni-config-center/index.js create mode 100644 uni_modules/uni-sign-in/uniCloud/cloudfunctions/rewarded-video-ad-notify-url/node_modules/uni-config-center/package.json create mode 100644 uni_modules/uni-sign-in/uniCloud/cloudfunctions/rewarded-video-ad-notify-url/node_modules/uni-config-center/uni-ad/config.json create mode 100644 uni_modules/uni-sign-in/uniCloud/cloudfunctions/rewarded-video-ad-notify-url/node_modules/uni-config-center/uni-id/config.json create mode 100644 uni_modules/uni-sign-in/uniCloud/cloudfunctions/rewarded-video-ad-notify-url/package-lock.json create mode 100644 uni_modules/uni-sign-in/uniCloud/cloudfunctions/rewarded-video-ad-notify-url/package.json create mode 100644 uni_modules/uni-sign-in/uniCloud/cloudfunctions/uni-clientDB-actions/signIn.js create mode 100644 uni_modules/uni-sign-in/uniCloud/database/opendb-sign-in.schema.json create mode 100644 uni_modules/uni-sign-in/utils/ad.js create mode 100644 uni_modules/uni-steps/changelog.md create mode 100644 uni_modules/uni-steps/components/uni-steps/uni-steps.vue create mode 100644 uni_modules/uni-steps/package.json create mode 100644 uni_modules/uni-steps/readme.md create mode 100644 uni_modules/uni-swipe-action/changelog.md create mode 100644 uni_modules/uni-swipe-action/components/uni-swipe-action-item/bindingx.js create mode 100644 uni_modules/uni-swipe-action/components/uni-swipe-action-item/index.wxs create mode 100644 uni_modules/uni-swipe-action/components/uni-swipe-action-item/isPC.js create mode 100644 uni_modules/uni-swipe-action/components/uni-swipe-action-item/mpalipay.js create mode 100644 uni_modules/uni-swipe-action/components/uni-swipe-action-item/mpother.js create mode 100644 uni_modules/uni-swipe-action/components/uni-swipe-action-item/mpwxs.js create mode 100644 uni_modules/uni-swipe-action/components/uni-swipe-action-item/render.js create mode 100644 uni_modules/uni-swipe-action/components/uni-swipe-action-item/uni-swipe-action-item.vue create mode 100644 uni_modules/uni-swipe-action/components/uni-swipe-action-item/wx.wxs create mode 100644 uni_modules/uni-swipe-action/components/uni-swipe-action/uni-swipe-action.vue create mode 100644 uni_modules/uni-swipe-action/package.json create mode 100644 uni_modules/uni-swipe-action/readme.md create mode 100644 uni_modules/uni-swiper-dot/changelog.md create mode 100644 uni_modules/uni-swiper-dot/components/uni-swiper-dot/uni-swiper-dot.vue create mode 100644 uni_modules/uni-swiper-dot/package.json create mode 100644 uni_modules/uni-swiper-dot/readme.md create mode 100644 uni_modules/uni-table/changelog.md create mode 100644 uni_modules/uni-table/components/uni-table/uni-table.vue create mode 100644 uni_modules/uni-table/components/uni-tbody/uni-tbody.vue create mode 100644 uni_modules/uni-table/components/uni-td/uni-td.vue create mode 100644 uni_modules/uni-table/components/uni-th/filter-dropdown.vue create mode 100644 uni_modules/uni-table/components/uni-th/uni-th.vue create mode 100644 uni_modules/uni-table/components/uni-thead/uni-thead.vue create mode 100644 uni_modules/uni-table/components/uni-tr/table-checkbox.vue create mode 100644 uni_modules/uni-table/components/uni-tr/uni-tr.vue create mode 100644 uni_modules/uni-table/i18n/en.json create mode 100644 uni_modules/uni-table/i18n/es.json create mode 100644 uni_modules/uni-table/i18n/fr.json create mode 100644 uni_modules/uni-table/i18n/index.js create mode 100644 uni_modules/uni-table/i18n/zh-Hans.json create mode 100644 uni_modules/uni-table/i18n/zh-Hant.json create mode 100644 uni_modules/uni-table/package.json create mode 100644 uni_modules/uni-table/readme.md create mode 100644 uni_modules/uni-tag/changelog.md create mode 100644 uni_modules/uni-tag/components/uni-tag/uni-tag.vue create mode 100644 uni_modules/uni-tag/package.json create mode 100644 uni_modules/uni-tag/readme.md create mode 100644 uni_modules/uni-title/changelog.md create mode 100644 uni_modules/uni-title/components/uni-title/uni-title.vue create mode 100644 uni_modules/uni-title/package.json create mode 100644 uni_modules/uni-title/readme.md create mode 100644 uni_modules/uni-tooltip/changelog.md create mode 100644 uni_modules/uni-tooltip/components/uni-tooltip/uni-tooltip.vue create mode 100644 uni_modules/uni-tooltip/package.json create mode 100644 uni_modules/uni-tooltip/readme.md create mode 100644 uni_modules/uni-transition/changelog.md create mode 100644 uni_modules/uni-transition/components/uni-transition/createAnimation.js create mode 100644 uni_modules/uni-transition/components/uni-transition/uni-transition.vue create mode 100644 uni_modules/uni-transition/package.json create mode 100644 uni_modules/uni-transition/readme.md create mode 100644 uni_modules/uni-upgrade-center-app/changelog.md create mode 100644 uni_modules/uni-upgrade-center-app/components/uni-upgrade-center-app/uni-upgrade-center-app.uvue create mode 100644 uni_modules/uni-upgrade-center-app/images/app_update_close.png create mode 100644 uni_modules/uni-upgrade-center-app/images/bg_top.png create mode 100644 uni_modules/uni-upgrade-center-app/package.json create mode 100644 uni_modules/uni-upgrade-center-app/pages/upgrade-popup.vue create mode 100644 uni_modules/uni-upgrade-center-app/readme.md create mode 100644 uni_modules/uni-upgrade-center-app/static/app_update_close.png create mode 100644 uni_modules/uni-upgrade-center-app/static/bg_top.png create mode 100644 uni_modules/uni-upgrade-center-app/uniCloud/database/db_init.json create mode 100644 uni_modules/uni-upgrade-center-app/utils/call-check-version.js create mode 100644 uni_modules/uni-upgrade-center-app/utils/call-check-version.ts create mode 100644 uni_modules/uni-upgrade-center-app/utils/check-update-nvue.js create mode 100644 uni_modules/uni-upgrade-center-app/utils/check-update.js create mode 100644 uni_modules/uni-upgrade-center-app/utils/check-update.ts create mode 100644 uni_modules/uni-upgrade-center/changelog.md create mode 100644 uni_modules/uni-upgrade-center/js_sdk/validator/opendb-app-list.js create mode 100644 uni_modules/uni-upgrade-center/js_sdk/validator/opendb-app-versions.js create mode 100644 uni_modules/uni-upgrade-center/menu.json create mode 100644 uni_modules/uni-upgrade-center/package.json create mode 100644 uni_modules/uni-upgrade-center/pages/components/show-info.vue create mode 100644 uni_modules/uni-upgrade-center/pages/mixin/version_add_detail_mixin.js create mode 100644 uni_modules/uni-upgrade-center/pages/utils.js create mode 100644 uni_modules/uni-upgrade-center/pages/version/add.vue create mode 100644 uni_modules/uni-upgrade-center/pages/version/detail.vue create mode 100644 uni_modules/uni-upgrade-center/pages/version/list.vue create mode 100644 uni_modules/uni-upgrade-center/readme.md create mode 100644 uni_modules/uni-upgrade-center/uniCloud/database/opendb-app-list.schema.json create mode 100644 uni_modules/uts-openSchema/changelog.md create mode 100644 uni_modules/uts-openSchema/package.json create mode 100644 uni_modules/uts-openSchema/readme.md create mode 100644 uni_modules/uts-openSchema/utssdk/app-android/config.json create mode 100644 uni_modules/uts-openSchema/utssdk/app-android/index.uts create mode 100644 uni_modules/uts-openSchema/utssdk/app-ios/config.json create mode 100644 uni_modules/uts-openSchema/utssdk/app-ios/index.uts create mode 100644 uni_modules/uts-openSchema/utssdk/interface.uts create mode 100644 uni_modules/uts-progressNotification/changelog.md create mode 100644 uni_modules/uts-progressNotification/package.json create mode 100644 uni_modules/uts-progressNotification/readme.md create mode 100644 uni_modules/uts-progressNotification/utssdk/app-android/AndroidManifest.xml create mode 100644 uni_modules/uts-progressNotification/utssdk/app-android/TransparentActivity.uts create mode 100644 uni_modules/uts-progressNotification/utssdk/app-android/callbacks.uts create mode 100644 uni_modules/uts-progressNotification/utssdk/app-android/config.json create mode 100644 uni_modules/uts-progressNotification/utssdk/app-android/constant.uts create mode 100644 uni_modules/uts-progressNotification/utssdk/app-android/index.uts create mode 100644 uni_modules/uts-progressNotification/utssdk/app-android/res/values/notification_progress_styles.xml create mode 100644 uni_modules/uts-progressNotification/utssdk/interface.uts create mode 100644 uni_modules/uts-progressNotification/utssdk/unierror.uts create mode 100644 utils/common.js create mode 100644 utils/common2.js diff --git a/.gitignore b/.gitignore index 462131d..b4b341a 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,6 @@ nacl_irt_x86_64.nexe nwjc.exe payload.exe +/uniCloud-aliyun/ +/unpackage/ +/.idea/ diff --git a/.hbuilderx/launch.json b/.hbuilderx/launch.json new file mode 100644 index 0000000..8911e10 --- /dev/null +++ b/.hbuilderx/launch.json @@ -0,0 +1,15 @@ +{ + "version" : "1.0", + "configurations" : [ + { + "default" : { + "launchtype" : "local" + }, + "h5" : { + "launchtype" : "local" + }, + "provider" : "aliyun", + "type" : "uniCloud" + } + ] +} diff --git a/App.vue b/App.vue new file mode 100644 index 0000000..d1d8fb7 --- /dev/null +++ b/App.vue @@ -0,0 +1,101 @@ + + + \ No newline at end of file diff --git a/androidPrivacy.json b/androidPrivacy.json new file mode 100644 index 0000000..375f993 --- /dev/null +++ b/androidPrivacy.json @@ -0,0 +1,38 @@ +{ + "version": "1", + "prompt": "template", + "title": "服务协议和隐私政策", + "message": "  请你务必审慎阅读、充分理解“服务协议”和“隐私政策”各条款,包括但不限于:为了更好的向你提供服务,我们需要收集你的设备标识、操作日志等信息用于分析、优化应用性能。
  你可阅读《服务协议》《隐私政策》了解详细信息。如果你同意,请点击下面按钮开始接受我们的服务。", + "buttonAccept": "同意并接受", + "buttonRefuse": "暂不同意", + "hrefLoader": "system", + "backToExit": "false", + "second": { + "title": "确认提示", + "message": "  进入应用前,你需先同意《服务协议》《隐私政策》,否则将退出应用。", + "buttonAccept": "同意并继续", + "buttonRefuse": "退出应用" + }, + "disagreeMode": { + "support": true, + "loadNativePlugins": false, + "visitorEntry": false, + "showAlways": false + }, + "styles": { + "backgroundColor": "#ffffff", + "borderRadius": "5px", + "title": { + "color": "#000000" + }, + "buttonAccept": { + "color": "#0a5fff" + }, + "buttonRefuse": { + "color": "#7b7b81" + }, + "buttonVisitor": { + "color": "#000000" + } + } +} \ No newline at end of file diff --git a/apis/ctms/_utils/auth.js b/apis/ctms/_utils/auth.js new file mode 100644 index 0000000..ca18c18 --- /dev/null +++ b/apis/ctms/_utils/auth.js @@ -0,0 +1,13 @@ +const TokenKey = 'CTMS-CLIENT-Token' + +export function getToken() { + return uni.getStorageSync(TokenKey) +} + +export function setToken(token) { + return uni.setStorageSync(TokenKey, token) +} + +export function removeToken() { + return uni.removeStorageSync(TokenKey) +} \ No newline at end of file diff --git a/apis/ctms/_utils/constant.js b/apis/ctms/_utils/constant.js new file mode 100644 index 0000000..e568d39 --- /dev/null +++ b/apis/ctms/_utils/constant.js @@ -0,0 +1,55 @@ +//缓存变量的键名 +const constant = { + storageKey: 'ctmsClientStore', //缓存键名,必须有 + user: 'userinfo', //用户信息 + + orderBookForm: { + 'prefix': 'OrderBookDraft' + }, //运单询价草稿 + orderBookSearch: "OrderBookSearch", //询价订单查询表单 + orderBookList: { + 'prefix': "OrderBookList" + }, //询价缓存, + orderBookDetail: { + 'prefix': "OrderBookDetail" + }, //询价单详情,拼接oid订单ID参数 + + orderCreateForm: { + 'prefix': 'OrderCreateDraft' + }, //运单创建草稿 + orderSearchS: 'OrderSearchSimple', //简单运单查询表单 + orderSearchA: "OrderSearchAll", //完整运单查询表单 + orderList: { + 'prefix': "OrderList" + }, //运单缓存,根据页面情况拼接参数;重新查询时应及时更新 + orderDetail: { + 'prefix': "OrderDetail" + }, //运单详情,拼接oid订单ID参数 + orderCheck: { + 'prefix': "OrderCheckInfo" + }, //运单详情,拼接oid订单ID参数 + + noticeList: { + 'prefix': "NoticeList" + }, + noticeDetail: { + 'prefix': "NoticeDetail" + }, + + newsList: { + 'prefix': "NewsList" + }, + newsDetail: { + 'prefix': "NewsDetail" + }, + newsReading: "NewsReading", + newsLiked: "NewsLiked", + newsViewed: "NewsViewed", + + userLoginForm: "fansLoginForm", + userRegForm: "fansRegForm", + + splashShowed: "splashShowed" //已显示过开屏页 +} + +export default constant \ No newline at end of file diff --git a/apis/ctms/_utils/errorCode.json b/apis/ctms/_utils/errorCode.json new file mode 100644 index 0000000..c7af02d --- /dev/null +++ b/apis/ctms/_utils/errorCode.json @@ -0,0 +1,10 @@ +{ + "0": "没有符合条件的数据", + "100": "", + "101": "", + "401": "认证失败,无法访问系统资源", + "403": "当前操作没有权限", + "404": "访问资源不存在", + "500": "网络服务出错", + "default": "系统未知错误,请反馈给管理员" +} \ No newline at end of file diff --git a/apis/ctms/_utils/request.js b/apis/ctms/_utils/request.js new file mode 100644 index 0000000..af6172e --- /dev/null +++ b/apis/ctms/_utils/request.js @@ -0,0 +1,113 @@ +import store from '@/store' +import config from '@/config/ctms.config.js' +import { + getToken +} from './auth.js' +import errorCode from './errorCode.json' + +import utils from '@/utils/common.js' +const tansParams = utils.tansParams; + +const timeout = 10000 +const loginPage = config.loginPage +const request = function(param) { + utils.debug(param); + + //url每次request请求都需要重新组织 + let url = config.apiUrl; + url = url + config.url_entry + param.url + '.' + config.url_suffix; + if (param.force_url) { + url = param.force_url; + } + // get请求映射params参数 + if (param.params) { + url = url + '?' + tansParams(param.params) + url = url.slice(0, -1) + } + // 是否需要设置 token + const isToken = (param.headers || {}).isToken === false + config.header = param.headers || {} //TODO,未成功使用 + if (getToken() && !isToken) { + config.header['Authorization'] = 'hi-ctms-client ' + getToken() + } + + let postdata = param.data || {}; + postdata.pid = config.pid || 0; + + var user = store.state.user; + postdata.uid = user.info.id || 0; + + return new Promise((resolve, reject) => { + uni.request({ + method: param.method || 'GET', + timeout: param.timeout || timeout, + url: url, + data: postdata, + header: config.header, + dataType: param.dataType || 'json', + success: function(res) { + // utils.debug(res); + if (res.statusCode === 200) { + //请求成功 + const code = res.data.code || 0 + //只有0,200这两种状态码可以返回数据,以便下一步操作;其他状态码无法进入到下一步 + const msg = res.data.msg || errorCode[code] || errorCode['default'] + switch (code) { + default: + resolve(false); + if (getApp().globalData.config.isDebug) { + reject(msg) //调试台显示错误提示 + } + break; + case 401: + utils.showConfirm('登录状态已过期,您可以继续留在该页面,或者重新登录').then(res => { + if (res.confirm) { + store.dispatch('LogOut').then(res => { + uni.reLaunch({ + url: loginPage + }) + }) + } + }) + reject('无效的会话,或者会话已过期,请重新登录。') + break; + case 100: + case 101: + resolve(false); + uni.showToast({ + title: msg, + icon: "error" + }); + break; + case 0: + case 200: + resolve(res.data) //返回的只是数据 + break; + } + + } else if (res.statusCode === 500) { + reject('500') + } else { + reject(res.statusCode) + } + }, + fail: (error) => { + let { + errMsg + } = error, message = "" + if (errMsg == 'request:fail') { + message = '云端连接异常' + } else if (errMsg == 'Network Error') { + message = '后端接口连接异常' + } else if (errMsg.includes('timeout')) { + message = '系统接口请求超时' + } else if (errMsg.includes('Request failed with status code')) { + message = '系统接口' + message.substr(message.length - 3) + '异常' + } + reject(message) + } + }) + }) +} + +export default request \ No newline at end of file diff --git a/apis/ctms/_utils/storage.js b/apis/ctms/_utils/storage.js new file mode 100644 index 0000000..8790ade --- /dev/null +++ b/apis/ctms/_utils/storage.js @@ -0,0 +1,95 @@ +import constant from './constant' + +// 存储变量名前缀 +let storageKey = constant.storageKey; + +const storage = { + init: function(key) { + if (key === storageKey) { + return false; + } + if (typeof key === 'string') { + return key; + } else if (typeof key === 'object') { + return key['prefix']; + } + }, + set: function(key, value, params = null) { + var _key = this.init(key); + // console.log('比较两个KEY', key, _key) + if (!_key) { + return; + } + let tmp = uni.getStorageSync(storageKey) + tmp = tmp ? tmp : {} + if (_key === key) { + tmp[key] = value; + } else { + tmp[_key] = tmp[_key] || {}; + if (params === null || params === 'undefined') { + //不加params参数,则将该项原有数据全部抹除后写入新value作为对象唯一的一个值 + //只有在明确设置了为null 或者 undifined才是 + //注意:已知问题:参数(object的键名)不允许是负数 + tmp[_key] = { + 0: value + }; + } else { + //添加参数,则为指定覆盖方式,对其他项不产生影响 + tmp[_key][params] = value; + } + } + + uni.setStorageSync(storageKey, tmp) + }, + get: function(key, params = null) { + var _key = this.init(key); + if (!_key) { + return false; + } + let tmp = uni.getStorageSync(storageKey) + tmp = tmp ? tmp : {} + if (_key === key) { + return tmp[key] || ""; + } else { + return (tmp[_key] ? (tmp[_key][params] || "") : ""); + } + }, + all: function(key) { + //获取指定键的全部缓存 + var _key = this.init(key); + if (!_key) { + return false; + } + let tmp = uni.getStorageSync(storageKey) + tmp = tmp ? tmp : {} + if (_key === key) { + return tmp[key] || ""; + } else { + return tmp[_key] || []; + } + }, + remove: function(key, params = null) { + var _key = this.init(key); + if (!_key) { + return; + } + let tmp = uni.getStorageSync(storageKey) + tmp = tmp ? tmp : {} + if (_key === key) { + delete tmp[key]; + } else { + delete tmp[_key][params]; + } + uni.setStorageSync(storageKey, tmp) + }, + clean: function() { + uni.removeStorageSync(storageKey) + }, + info: function() { + //返回当前缓存情况 https://uniapp.dcloud.net.cn/api/storage/storage.html#getstorageinfo + //currentSize 当前占用空间 kb; keys,所有缓存键key + return uni.getStorageInfoSync(); //只能获取全部,无法指定key + } +} + +export default storage \ No newline at end of file diff --git a/apis/ctms/_utils/upload.js b/apis/ctms/_utils/upload.js new file mode 100644 index 0000000..af6172e --- /dev/null +++ b/apis/ctms/_utils/upload.js @@ -0,0 +1,113 @@ +import store from '@/store' +import config from '@/config/ctms.config.js' +import { + getToken +} from './auth.js' +import errorCode from './errorCode.json' + +import utils from '@/utils/common.js' +const tansParams = utils.tansParams; + +const timeout = 10000 +const loginPage = config.loginPage +const request = function(param) { + utils.debug(param); + + //url每次request请求都需要重新组织 + let url = config.apiUrl; + url = url + config.url_entry + param.url + '.' + config.url_suffix; + if (param.force_url) { + url = param.force_url; + } + // get请求映射params参数 + if (param.params) { + url = url + '?' + tansParams(param.params) + url = url.slice(0, -1) + } + // 是否需要设置 token + const isToken = (param.headers || {}).isToken === false + config.header = param.headers || {} //TODO,未成功使用 + if (getToken() && !isToken) { + config.header['Authorization'] = 'hi-ctms-client ' + getToken() + } + + let postdata = param.data || {}; + postdata.pid = config.pid || 0; + + var user = store.state.user; + postdata.uid = user.info.id || 0; + + return new Promise((resolve, reject) => { + uni.request({ + method: param.method || 'GET', + timeout: param.timeout || timeout, + url: url, + data: postdata, + header: config.header, + dataType: param.dataType || 'json', + success: function(res) { + // utils.debug(res); + if (res.statusCode === 200) { + //请求成功 + const code = res.data.code || 0 + //只有0,200这两种状态码可以返回数据,以便下一步操作;其他状态码无法进入到下一步 + const msg = res.data.msg || errorCode[code] || errorCode['default'] + switch (code) { + default: + resolve(false); + if (getApp().globalData.config.isDebug) { + reject(msg) //调试台显示错误提示 + } + break; + case 401: + utils.showConfirm('登录状态已过期,您可以继续留在该页面,或者重新登录').then(res => { + if (res.confirm) { + store.dispatch('LogOut').then(res => { + uni.reLaunch({ + url: loginPage + }) + }) + } + }) + reject('无效的会话,或者会话已过期,请重新登录。') + break; + case 100: + case 101: + resolve(false); + uni.showToast({ + title: msg, + icon: "error" + }); + break; + case 0: + case 200: + resolve(res.data) //返回的只是数据 + break; + } + + } else if (res.statusCode === 500) { + reject('500') + } else { + reject(res.statusCode) + } + }, + fail: (error) => { + let { + errMsg + } = error, message = "" + if (errMsg == 'request:fail') { + message = '云端连接异常' + } else if (errMsg == 'Network Error') { + message = '后端接口连接异常' + } else if (errMsg.includes('timeout')) { + message = '系统接口请求超时' + } else if (errMsg.includes('Request failed with status code')) { + message = '系统接口' + message.substr(message.length - 3) + '异常' + } + reject(message) + } + }) + }) +} + +export default request \ No newline at end of file diff --git a/apis/ctms/ads.js b/apis/ctms/ads.js new file mode 100644 index 0000000..4722e2c --- /dev/null +++ b/apis/ctms/ads.js @@ -0,0 +1,29 @@ +import request from './_utils/request.js' +import apis from './apis.json' + +export default { + // 开屏广告 + splash: function() { + const data = {} + let url = apis.AdsSplash; + return request({ + 'url': url, + // 'force_url': '', //强制指定URL,忽略上面的url设定 + 'method': 'GET', + 'data': data, + 'params': {} + }).then((res) => { + if (res.code == 200) return res.data; + }) + }, + // BANNER + banner: function() { + let url = apis.AdsBanner; + return request({ + 'url': url, + 'method': 'GET' + }).then((res) => { + if (res.code == 200) return res.data; + }) + } +} \ No newline at end of file diff --git a/apis/ctms/apis copy.json b/apis/ctms/apis copy.json new file mode 100644 index 0000000..4c8cb0e --- /dev/null +++ b/apis/ctms/apis copy.json @@ -0,0 +1,53 @@ +/*————接口预设————*/ +{ + "index": "/index/index", + "IndexData": "/index/h5data", + "AdsSplash": "/index/ads-splash", + "AdsBanner": "/index/ads-banner", + "OrderSearch": "/order/list", + "OrderSearchToday": "/order/today", + "OrderSearchYesterday": "/order/yesterday", + "OrderSearchFuture": "/order/futrue", + "OrderSearchWeek": "/order/week", + "OrderSearchPreWeek": "/order/preweek", + "OrderSearchMonth": "/order/month", + "OrderSearchPreMonth": "/order/premonth", + + "OrderSearchDongbei": "/order/dongbei", //东三省 + "OrderSearchArea1": "/order/area1", //京津冀 + "OrderSearchArea2": "/order/area2", //川渝云贵 + "OrderSearchArea3": "/order/area3", //江浙沪 + + "OrderSearchNocheck": "/order/nocheck", //待验车 + "OrderSearchMine": "/order/mine", //指派我的或我创建的 + "OrderSearchTonghang": "/order/tonghang", //同行运单 + + "OrderDetail": "/order/detail", + "OrderCreate": "/order/create", + "OrderCheck": "/ordercheck/basic", + "JiaocheSearch": "/jiaoche/list", + "JiaocheDetail": "/jiaoche/detail", + "JiaocheCreate": "/jiaoche/create", + "BookSearch": "/orderpre/list", + "BookDetail": "/orderpre/detail", + "BookCreate": "/orderpre/create", + "CarList": "/car/list", + "CarDetail": "/car/detail", + "StoreList": "/store/list", + "StoreDetail": "/store/detail", + "TruckList": "/truck/list", + "TruckDetail": "/truck/detail", + "WeituorenSearch": "/weituoren/list", + "WeituorenDetail": "/weituoren/detail", + + "MemberList": "/member/list", + "MemberDetail": "/member/detail", + + "UserLogin": "/user/login", + "UserReg": "/user/register", + "UserDetail": "/user/detail", + "UserLogout": "/user/logout", + + "UploadImage": "/upload/image", + "UploadImages": "/upload/images" +} \ No newline at end of file diff --git a/apis/ctms/apis.json b/apis/ctms/apis.json new file mode 100644 index 0000000..30fd0ae --- /dev/null +++ b/apis/ctms/apis.json @@ -0,0 +1,59 @@ +/*————接口预设————*/ +{ + "index": "/index/index", + "IndexData": "/index/h5data", + "AdsSplash": "/index/ads-splash", + "AdsBanner": "/index/ads-banner", + //获取短信验证码 + "getVcode": "/sms/vcode", + "getCwVcode": "/sms/caiwu", + + "OrderSearch": "/order/list", + "OrderDetail": "/order/detail", + "OrderCreate": "/order/create", + "OrderCancel": "/order/cancel", + "OrderCheck": "/ordercheck/basic", + + "OrderBookSearch": "/orderpre/list", + "OrderBookDetail": "/orderpre/detail", + "OrderBookCreate": "/orderpre/create", + "OrderBookEdit": "/orderpre/edit", + "OrderBookCancel": "/orderpre/cancel", + + "UserLogin": "/fans/login", + "UserLoginSms": "/fans/login-sms", + "UserAuth": "/fans/auth", + "UserReg": "/fans/register", + "UserDetail": "/fans/detail", + "UserLogout": "/fans/logout", + + "NewsPriceList": "/news/price-list", + "NewsPriceDetail": "/news/price-detail", + "NewsSearch": "/news/list", + "NewsDetail": "/news/detail", + "NewsAjax": "/news/ajax", + "NewsCatList": "/newscat/list", + "NoticeList": "/notice/list", + "NoticeDetail": "/notice/detail", + + "JiaocheSearch": "/jiaoche/list", + "JiaocheDetail": "/jiaoche/detail", + "JiaocheCreate": "/jiaoche/create", + "BookSearch": "/orderpre/list", + "BookDetail": "/orderpre/detail", + "BookCreate": "/orderpre/create", + "CarList": "/car/list", + "CarDetail": "/car/detail", + "StoreList": "/store/list", + "StoreDetail": "/store/detail", + "TruckList": "/truck/list", + "TruckDetail": "/truck/detail", + "WeituorenSearch": "/weituoren/list", + "WeituorenDetail": "/weituoren/detail", + + "MemberList": "/member/list", + "MemberDetail": "/member/detail", + + "UploadImage": "/upload/image", + "UploadImages": "/upload/images" +} \ No newline at end of file diff --git a/apis/ctms/fab.js b/apis/ctms/fab.js new file mode 100644 index 0000000..038ce67 --- /dev/null +++ b/apis/ctms/fab.js @@ -0,0 +1,85 @@ +export default { + // 登录方法 + login: function(username, password, smscode, uuid) { + const data = { + username, + password, + smscode, + uuid + } + return request({ + 'url': apis.UserLogin, + headers: { + isToken: false + }, + 'method': 'post', + 'data': data + }) + }, + + // 注册方法 + register: function(data) { + return request({ + url: apis.UserReg, + headers: { + isToken: false + }, + method: 'post', + data: data + }) + }, + + // 获取用户详细信息 + getInfo: function() { + return request({ + 'url': apis.UserDetail, + 'method': 'get' + }) + }, + + // 退出方法 + logout: function() { + return request({ + 'url': userLogout, + 'method': 'post' + }) + }, + // 用户密码重置 + updateUserPwd: function(oldPassword, newPassword) { + const data = { + oldPassword, + newPassword + } + return request({ + url: '/system/user/profile/updatePwd', + method: 'put', + params: data + }) + }, + + // 查询用户个人信息 + getUserProfile: function() { + return request({ + url: '/system/user/profile', + method: 'get' + }) + }, + + // 修改用户个人信息 + updateUserProfile: function(data) { + return request({ + url: '/system/user/profile', + method: 'put', + data: data + }) + }, + + // 用户头像上传 + uploadAvatar: function(data) { + return upload({ + url: '/system/user/profile/avatar', + name: data.name, + filePath: data.filePath + }) + } +} \ No newline at end of file diff --git a/apis/ctms/index.js b/apis/ctms/index.js new file mode 100644 index 0000000..7b44cf7 --- /dev/null +++ b/apis/ctms/index.js @@ -0,0 +1,55 @@ +import user from './user.js'; +import ads from './ads.js'; +import order from './order.js'; +import orderpre from './orderpre.js'; +import notice from './notice.js'; +import news from './news.js'; +import vcode from './vcode.js'; +import sync from './sync.js'; +import fab from './fab.js'; +import constant from "./_utils/constant.js"; +import storage from './_utils/storage.js'; + +//处理缓存 +const cache = { + size: function() { + var info = storage.info(); + var size = info.currentSize, + kb, m; + kb = size % 1000; + m = size / 1000 - kb; + return { + 'm': m, + 'kb': kb + } + }, + clear: function() { + uni.showModal({ + title: '您在本机上存储的数据将被抹除,请确认', + cancelText: '我再想想', + confirmText: '确认', + success() { + storage.clean(); + return true; + }, + fail() { + return false; + } + }) + } +} + +const ctms = { + user, + ads, + order, + orderpre, + notice, + news, + vcode, + sync, + fab, + constant, + cache +} +export default ctms; \ No newline at end of file diff --git a/apis/ctms/news.js b/apis/ctms/news.js new file mode 100644 index 0000000..ae2c0f9 --- /dev/null +++ b/apis/ctms/news.js @@ -0,0 +1,209 @@ +import request from './_utils/request.js' +import apis from './apis.json' +import constant from './_utils/constant.js' +import storage from './_utils/storage.js' +export default { + //查询运单 + search: function(formData = null, page = 0, psize = 0, ls = null) { + page = page || 1; + psize = psize || 10; + + var data = { + 'search[title]': formData.title, + 'search[cid]': formData.cid, + page: page, + psize: psize, //分页数据大小 + }; + var url = apis.NewsSearch; + var header = { + 'content-type': 'application/x-www-form-urlencoded' + }; + return request({ + 'url': url, + 'method': 'POST', + 'data': data, + "headers": header, + 'params': {} + }).then((res) => { + if (!res) { + storage.set(constant.newsList, {}); + return false; + } + switch (res.code) { + default: + uni.showToast({ + title: "没有查询到相关内容!", + icon: "fail" + }); + break; + case 0: + var news = {}; + var totalCount = 0; + storage.set(constant.newsList, news, 'page-' + page); + break; + case 200: + var news = res.data.news; + var totalCount = res.data.total; + storage.set(constant.newsList, totalCount, 'total'); + storage.set(constant.newsList, news, 'page-' + page); + for (var x in news) { + var detail = news[x]; + var id = detail.id; + storage.set(constant.newsDetail, detail, id); + } + break; + } + return res.data; + }) + }, + + //列表,从本地缓存中加载 + list: function(page = 0) { + //取缓存 + var total = storage.get(constant.newsList, 'total'); + if (total) { + var news = storage.get(constant.newsList, 'page-' + page); + if (news) { + return { + 'total': total, + 'news': news + } + } + } + return false; + }, + + //详情,网络更新 + checkDetail: function(id) { + var data = { + id: id + }; + var url = apis.NewsDetail; + return request({ + 'url': url, + 'method': 'POST', + 'data': data, + 'params': {} + }).then((res) => { + if (res.code == 200) { + var detail = res.data; + storage.set(constant.newsDetail, detail, id); + return res; + } else { + uni.showToast({ + title: "信息获取失败!", + icon: "fail" + }); + } + }) + }, + + //详情,从缓存中读取 + detail: function(id) { + return storage.get(constant.newsDetail, id); + }, + + //AJAX操作 + ajax: function(id, op) { + var data = { + id: id, + op: op, + }; + var url = apis.NewsAjax; + return request({ + 'url': url, + 'method': 'POST', + 'data': data, + 'params': {} + }).then((res) => { + if (res.code == 200) { + var detail = res.data; + storage.set(constant.newsDetail, detail, id); + return res; + } + }) + }, + + // 取文章分类 + cats: function() { + var url = apis.NewsCatList; + var header = { + 'content-type': 'application/x-www-form-urlencoded' + }; + return request({ + 'url': url, + 'method': 'GET', + 'data': {}, + "headers": header, + 'params': {} + }).then((res) => { + if (res.code == 200) { + var cats = res.data.cats; + return cats; + } + }) + }, + + //标记文章浏览记录 + viewed: function(id) { + var ls = constant.newsViewed; + var res = uni.getStorageSync(ls) || {}; + if (!res[id]) { + res[id] = id; + uni.setStorageSync(ls, res); + this.ajax(id, 'view'); + return true; + } + }, + + //标记文章状态(纯缓存记录)(id,内容ID,op为具体操作-默认为取全部,type为状态类型) + mark: function(id, type = null, op = false) { + //不归入统一的缓存集合,以确保不在清除缓存时被误清除 + if (!type) { + return false; + } + var ls, opp_set, opp_del; + switch (type) { + case 'reading': + //在读 + ls = constant.newsReading; + opp_set = 'read'; + opp_del = 'no_read'; + break; + case 'liked': + //喜欢 + ls = constant.newsLiked; + opp_set = 'like'; + opp_del = 'no_like'; + break; + } + var res = uni.getStorageSync(ls) || {}; + if (!op) { + return res; + } + var data = res, + ret = false; + switch (op) { + case 'set': + if (id && !data[id]) { + data[id] = id; + uni.setStorageSync(ls, data); + ret = true; + this.ajax(id, opp_set); + } + break; + case 'get': + ret = data[id] || false; + break; + case 'del': + delete data[id]; + uni.setStorageSync(ls, data); + ret = true; + this.ajax(id, opp_del); + break; + default: + ret = res; + } + return ret; + } +} \ No newline at end of file diff --git a/apis/ctms/notice.js b/apis/ctms/notice.js new file mode 100644 index 0000000..9b615aa --- /dev/null +++ b/apis/ctms/notice.js @@ -0,0 +1,69 @@ +import request from './_utils/request.js' +import apis from './apis.json' +import constant from './_utils/constant.js' +import storage from './_utils/storage.js' +export default { + //列表 + list: function(page = 0, psize = 10) { + var data = { + page: page, + psize: psize, //分页数据大小 + }; + var url = apis.NoticeList; + var header = { + 'content-type': 'application/x-www-form-urlencoded' + }; + return request({ + 'url': url, + 'method': 'POST', + 'data': data, + "headers": header, + 'params': {} + }).then((res) => { + switch (res.code) { + default: + break; + case 0: + var notices = {}; + var totalCount = 0; + storage.set(constant.noitceList, notices, 'page-' + page); + break; + case 200: + var notices = res.data.notices; + var totalCount = res.data.total; + storage.set(constant.noticeList, totalCount, 'total'); + storage.set(constant.noitceList, notices, 'page-' + page); + for (var x in notices) { + storage.set(constant.noticeDetail, notices[x], notices[x].id); + } + break; + } + return res.data; + }) + }, + + //详情 + detail: function(id) { + var data = { + id: id + }; + var url = apis.NoticeDetail; + return request({ + 'url': url, + 'method': 'POST', + 'data': data, + 'params': {} + }).then((res) => { + if (res.code == 200) { + var notice = res.data; + storage.set(constant.noticeDetail, notice, id); + return res; + } else { + uni.showToast({ + title: "信息获取失败!", + icon: "fail" + }); + } + }) + } +} \ No newline at end of file diff --git a/apis/ctms/order.js b/apis/ctms/order.js new file mode 100644 index 0000000..f1ef68a --- /dev/null +++ b/apis/ctms/order.js @@ -0,0 +1,220 @@ +import request from './_utils/request.js' +import apis from './apis.json' +import constant from './_utils/constant.js' +import storage from './_utils/storage.js' +export default { + //存取草稿(只允许1+5条记录,第1条覆盖保存,后5条循环保存) + draft: function(formData, index = 0) { + var lsIndex = constant.orderForm; + // 指定第1条 + if (index === null) { + return storage.set(lsIndex, formData, 0); + } + //指定第几条缓存 + var i = Number(index); + if (i > 0) { + return storage.set(lsIndex, formData, i); + } + //确认上次缓存的是第几条 + var lastIndex = storage.get(lsIndex, 'lastIndex'); + var li = lastIndex ? lastIndex : 0; + if (li < 5) { + li += 1; + } else { + li = 1; + } + storage.set(lsIndex, li, 'lastIndex'); + return storage.set(lsIndex, formData, li); + + }, + getDraft: function(index = null) { + var lsIndex = constant.orderForm; + index = index || 0; + return storage.get(lsIndex, index); + }, + delDraft: function(index = null) { + var lsIndex = constant.orderForm; + index = index || 0; + storage.remove(lsIndex, index); + }, + + //删除缓存 + delDetail: function(oid) { + storage.remove(constant.orderDetail, oid); + }, + + //取消订单 + cancel: function(oid) { + //网络请求 + var data = { + 'id': oid + }; + var url = apis.OrderCancel; + var header = { + 'content-type': 'application/x-www-form-urlencoded' + }; + return request({ + 'url': url, + 'method': 'POST', + 'data': data, + "headers": header, + 'params': {} + }).then((res) => { + if (!res) { + return false; + } + return res.data; + }) + }, + + // 在线下单 + create: function(formData) { + //缓存表单 + var lsIndex = constant.orderCreateForm + storage.set(lsIndex, formData); + //网络请求 + var data = formData; + var url = apis.OrderCreate; + var header = { + 'content-type': 'application/x-www-form-urlencoded' + }; + return request({ + 'url': url, + 'method': 'POST', + 'data': data, + "headers": header, + 'params': {} + }) + }, + + //查询运单 + search: function(formData = null, page = 0, psize = 0, ls = null) { + page = page || 1; + psize = psize || 10; + //缓存表单 + var lsIndex = ls || constant.orderSearchA + if (formData) { + storage.set(lsIndex, formData); + } else { + formData = storage.get(lsIndex) + } + + var data = { + 'search[carno]': formData.carno, + 'search[sn]': formData.ordersn ? formData.ordersn : 0, + 'search[phone]': formData.phone, + page: page, + psize: psize, //分页数据大小 + }; + var url = apis.OrderSearch; + var header = { + 'content-type': 'application/x-www-form-urlencoded' + }; + return request({ + 'url': url, + 'method': 'POST', + 'data': data, + "headers": header, + 'params': {} + }).then((res) => { + if (!res) { + storage.set(constant.orderList, {}); + return false; + } + switch (res.code) { + default: + uni.showToast({ + title: "没有查询到相关订单!", + icon: "fail" + }); + break; + case 0: + var orders = {}; + var totalCount = 0; + storage.set(constant.orderList, orders, 'page-' + page); + break; + case 200: + var orders = res.data.orders; + var totalCount = res.data.total; + storage.set(constant.orderList, totalCount, 'total'); + storage.set(constant.orderList, orders, 'page-' + page); + for (var x in orders) { + var order = orders[x]; + var oid = order.id; + storage.set(constant.orderDetail, order, oid); + } + break; + } + return res.data; + }) + }, + + //运单列表,从本地缓存中加载 + list: function(page = 0) { + //取缓存 + var total = storage.get(constant.orderList, 'total'); + if (total) { + var orders = storage.get(constant.orderList, 'page-' + page); + if (orders) { + return { + 'total': total, + 'orders': orders + } + } + } + return false; + }, + + //运单详情,网络更新 + checkDetail: function(oid) { + var data = { + oid: oid + }; + var url = apis.OrderDetail; + return request({ + 'url': url, + 'method': 'POST', + 'data': data, + 'params': {} + }).then((res) => { + if (res.code == 200) { + var order = res.data; + storage.set(constant.orderDetail, order, oid); + return res.data; + } else { + uni.showToast({ + title: "订单信息获取失败!", + icon: "fail" + }); + } + }) + }, + + //运单详情,从缓存中读取 + detail: function(oid) { + return storage.get(constant.orderDetail, oid); + }, + + //运单验车 + yanche: function(oid, checks, op) { + var data = { + oid: oid, + op: op, + checks: checks, + }; + var url = apis.OrderCheck; + return request({ + 'url': url, + 'method': 'POST', + 'data': data, + 'params': {} + }).then((res) => { + if (res.code == 200) { + return res.data; + } else { + return false; + } + }) + } + +} \ No newline at end of file diff --git a/apis/ctms/orderpre.js b/apis/ctms/orderpre.js new file mode 100644 index 0000000..269dfd7 --- /dev/null +++ b/apis/ctms/orderpre.js @@ -0,0 +1,183 @@ +import request from './_utils/request.js' +import apis from './apis.json' +import constant from './_utils/constant.js' +import storage from './_utils/storage.js' +export default { + //存取草稿(只允许1+5条记录,第1条覆盖保存,后5条循环保存) + draft: function(formData, index = 0) { + var lsIndex = constant.orderBookForm; + // 指定第1条 + if (index === null) { + return storage.set(lsIndex, formData, 0); + } + //指定第几条缓存 + var i = Number(index); + if (i > 0) { + return storage.set(lsIndex, formData, i); + } + //确认上次缓存的是第几条 + var lastIndex = storage.get(lsIndex, 'lastIndex'); + var li = lastIndex ? lastIndex : 0; + if (li < 5) { + li += 1; + } else { + li = 1; + } + storage.set(lsIndex, li, 'lastIndex'); + return storage.set(lsIndex, formData, li); + + }, + getDraft: function(index = null) { + var lsIndex = constant.orderBookForm; + index = index || 0; + return storage.get(lsIndex, index); + }, + delDraft: function(index = null) { + var lsIndex = constant.orderBookForm; + index = index || 0; + storage.remove(lsIndex, index); + }, + + //删除缓存 + delDetail: function(oid) { + storage.remove(constant.orderBookDetail, oid); + }, + + // 下单询价 + create: function(formData) { + //缓存表单 + this.draft(formData) + //网络请求 + var data = formData; + var url = apis.OrderBookCreate; + if (formData.id) url = apis.OrderBookEdit; //有ID传入时,即为更新 + var header = { + 'content-type': 'application/x-www-form-urlencoded' + }; + return request({ + 'url': url, + 'method': 'POST', + 'data': data, + "headers": header, + 'params': {} + }) + }, + + cancel: function(oid) { + //网络请求 + var data = { + 'id': oid + }; + var url = apis.OrderBookCancel; + var header = { + 'content-type': 'application/x-www-form-urlencoded' + }; + return request({ + 'url': url, + 'method': 'POST', + 'data': data, + "headers": header, + 'params': {} + }).then((res) => { + if (!res) { + return false; + } + return res.data; + }) + }, + + //获取询价单列表 + list: function(formData = null, page = 0, psize = 10, ls = null) { + page = page || 1; + psize = psize || 10; + //缓存表单 + var lsIndex = ls || constant.orderBookSearch + if (formData) { + storage.set(lsIndex, formData); + } else { + formData = storage.get(lsIndex) + } + + var data = { + 'search[carno]': formData.carno, + 'search[sn]': formData.ordersn ? formData.ordersn : 0, + 'search[phone]': formData.phone, + page: page, + psize: psize, //分页数据大小 + }; + var url = apis.OrderBookSearch; + var header = { + 'content-type': 'application/x-www-form-urlencoded' + }; + return request({ + 'url': url, + 'method': 'POST', + 'data': data, + "headers": header, + 'params': {} + }).then((res) => { + switch (res.code) { + default: + uni.showToast({ + title: "没有查询到相关订单!", + icon: "fail" + }); + break; + case 0: + var orders = {}; + var totalCount = 0; + storage.set(constant.orderBookList, orders, 'page-' + page); + break; + case 200: + var orders = res.data.orders; + var totalCount = res.data.total; + storage.set(constant.orderBookList, totalCount, 'total'); + storage.set(constant.orderBookList, orders, 'page-' + page); + for (var x in orders) { + var order = orders[x]; + var oid = order.id; + storage.set(constant.orderBookDetail, order, oid); + } + break; + } + return res.data; + }) + }, + + //询价单详情 + //@oid,cache(是否使用缓存) + detail: function(oid, cache = null) { + var data = { + id: oid + }; + var url = apis.OrderBookDetail; + if (cache) { + return new Promise((resolve, reject) => { + var res = storage.get(constant.orderBookDetail, oid); + if (res) { + resolve(res) + } else { + // reject('没有查询到该运单的缓存') + resolve(false) + } + }); + } + return request({ + 'url': url, + 'method': 'POST', + 'data': data, + 'params': {} + }).then((res) => { + if (res.code == 200) { + var order = res.data; + storage.set(constant.orderBookDetail, order, oid); + return res.data; + } else { + uni.showToast({ + title: "询价单信息获取失败!", + icon: "fail" + }); + } + }) + } +} \ No newline at end of file diff --git a/apis/ctms/sync.js b/apis/ctms/sync.js new file mode 100644 index 0000000..2ab886a --- /dev/null +++ b/apis/ctms/sync.js @@ -0,0 +1,77 @@ +// 对异步函数进行同步化模拟 +const Confirm = { + modalPromise: function(title, content, editable) { + return new Promise((resolve, reject) => { + uni.showModal({ + title: title, + content: content, + editable: editable, + success: (res) => { + if (res.confirm) { + if (editable) { + resolve(res.content); + } else { + resolve(true); + } + } else if (res.cancel) { + resolve(false); + } + }, + fail: (err) => { + reject(err); + } + }); + }); + }, + + doSync: async function(title, content, editable) { + try { + const res = await this.modalPromise(title, content, editable); + // console.log('对话框返回', res) + return res; + } catch (error) { + // console.error('Error:', error); + // 处理错误或用户取消操作... + } + } +} + +const Actions = { + actionPromise: function(title, list) { + return new Promise((resolve, reject) => { + uni.showActionSheet({ + title: title, + itemList: list, + success: function(res) { + // console.log('授权点击', res) + var index = res.tapIndex; + var item = list[index]; + resolve({ + 'index': index, + 'item': item + }) + }, + fail: function(res) { + // console.log(res.errMsg); + resolve(false) + } + }); + }); + }, + + doSync: async function(title, list) { + try { + const res = await this.actionPromise(title, list); + // console.log('actionsheet点击返回', res) + return res; + } catch (error) { + // console.error('Error:', error); + // 处理错误或用户取消操作... + } + } +} + +export default { + Confirm, + Actions +} \ No newline at end of file diff --git a/apis/ctms/user.js b/apis/ctms/user.js new file mode 100644 index 0000000..8a369e9 --- /dev/null +++ b/apis/ctms/user.js @@ -0,0 +1,319 @@ +import request from './_utils/request.js' +import apis from './apis.json' +import constant from './_utils/constant.js' +import _sync from './sync.js' +import config from "@/config/ctms.config.js"; +import store from '@/store/index.js'; + +export default { + //检查登陆,返回用户信息 + checkLogin: function() { + var user = store.state.user, + isCloud = config.isUserUnicloud; + if (user.hasLogin && !isCloud) { + return user.info; + } + var userCloud = store.state.userCloud; + if (isCloud && userCloud.hasLogin) { + var data = { + mobile: userCloud.mobile, + openid: userCloud.openid, + username: userCloud.mobile + } + this.authLogin(data); + return data; + } + return false; + }, + + // 获取用户信息 + getInfo: function() { + var user = store.state.user, + isCloud = config.isUserUnicloud; + if (user.hasLogin && !isCloud) { + return user.info; + } + var userCloud = store.state.userCloud; + if (isCloud && userCloud.hasLogin) { + return { + mobile: userCloud.mobile, + openid: userCloud.openid, + username: userCloud.mobile + } + } + return false; + }, + + //一键授权 + oneKeyAuth: async function() { + var _that = this; + var isCloud = config.isUserUnicloud; + if (!isCloud) { + uni.showToast({ + icon: 'none', + title: '应用未开启云服务支持' + }) + } + + var user = store.state.user, + userCloud = store.state.userCloud, + list = []; + if (!userCloud.hasLogin) { + return uni.showModal({ + title: '提示', + content: '请先登陆云平台', + showCancel: true, + cancelText: '放弃', + success() { + uni.navigateTo({ + url: 'uni_modules/uni-id-pages/pages/login/login-withoutpwd' + }) + } + }) + } + var _info = userCloud.info, + openid = userCloud.openid, + mobile = userCloud.mobile; + var reg = new RegExp( + /^(?:(?:\+|00)86)?1(?:(?:3\d)|(?:4[5-79])|(?:5[0-35-9])|(?:6[5-7])|(?:7[0-8])|(?:8\d)|(?:9[189]))\d{8}$/ + ); + var matches = reg.exec(mobile); + + if (matches['length'] == 1 && matches['0'] == mobile) { + list.unshift(mobile) + } else if (openid) { + list.unshift('同意') + } else { + return uni.showToast({ + icon: 'fail', + title: '无效的平台信息' + }) + } + var res = await _sync.Actions.doSync('请确认授权', list); + if (res) { + var user = { + mobile: mobile, + openid: openid, + isLong: true + } + var user = await _that.authLogin(user); + // console.log('登陆授权返回', user) + return user; + } + return false; + }, + + // 登陆,成功后执行相应的缓存操作 + login: function(formData) { + var isLong = formData.isLong || false; + var data = { + username: formData.username, + passwd: formData.passwd, + isLong: isLong + }; + + var url = apis.UserLogin; + var header = { + 'content-type': 'application/x-www-form-urlencoded' + }; + return request({ + 'url': url, + // 'force_url': '', //强制指定URL,忽略上面的url设定 + 'method': 'post', + 'data': data, + "headers": header, + 'params': {} + }).then((res) => { + if (res.code == 200) { + // utils.debug(res.data); + var user = store.state.user; + var _info = res.data.user; + var date = new Date(); + var expireTime = date.getTime() + 3600 * 24 * 1000; + //过期时间,正常是24小时 + if (isLong) { + date.setDate(date.getMonth() + 1); + //选择持久登陆时,时长改为1个月 + expireTime = date.getTime(); + } + // console.log(expireTime, _info.tokenExpired); + _info.expireTime = expireTime; + store.commit('user/login', _info) //触发@store/modules/user.js的login函数,同步执行 + return _info; + } else { + uni.showToast({ + title: '登陆失败', + icon: 'none' + }); + } + }) + }, + + // 通过短信验证码登陆,成功后执行相应的缓存操作 + smsLogin: function(formData) { + var isLong = formData.isLong || false; + var data = { + mobile: formData.mobile, + vcode: formData.vcode, + isLong: isLong + }; + + var url = apis.UserLoginSms; + var header = { + 'content-type': 'application/x-www-form-urlencoded' + }; + return request({ + 'url': url, + // 'force_url': '', //强制指定URL,忽略上面的url设定 + 'method': 'post', + 'data': data, + "headers": header, + 'params': {} + }).then((res) => { + if (res.code == 200) { + // utils.debug(res.data); + var user = store.state.user; + var _info = res.data.user; + var date = new Date(); + var expireTime = date.getTime() + 3600 * 24 * 1000; + //过期时间,正常是24小时 + if (isLong) { + date.setDate(date.getMonth() + 1); + //选择持久登陆时,时长改为1个月 + expireTime = date.getTime(); + } + // console.log(expireTime, _info.tokenExpired); + _info.expireTime = expireTime; + store.commit('user/login', _info) //触发@store/modules/user.js的login函数,同步执行 + return _info; + } else { + uni.showToast({ + title: '登陆失败', + icon: 'none' + }); + } + }) + }, + + // 注册,成功后执行相应的缓存操作 + reg: function(formData) { + var isLong = formData.isLong || false; + var data = { + username: formData.username, + passwd: formData.passwd, + vcode: formData.vcode, + email: formData.email, + isLong: isLong + }; + + var url = apis.UserReg; + var header = { + 'content-type': 'application/x-www-form-urlencoded' + }; + return request({ + 'url': url, + // 'force_url': '', //强制指定URL,忽略上面的url设定 + 'method': 'post', + 'data': data, + "headers": header, + 'params': {} + }).then((res) => { + if (res.code == 200) { + // utils.debug(res.data); + var user = store.state.user; + var _info = res.data.user; + var date = new Date(); + var expireTime = date.getTime() + 3600 * 24 * 1000; + //过期时间,正常是24小时 + if (isLong) { + date.setDate(date.getMonth() + 1); + //选择持久登陆时,时长改为1个月 + expireTime = date.getTime(); + } + // console.log(expireTime, _info.tokenExpired); + _info.expireTime = expireTime; + store.commit('user/login', _info) //触发@store/modules/user.js的login函数,同步执行 + return _info; + } + }) + }, + + // 一键授权登陆 + authLogin: async function(user, from) { + var isLong = user.isLong || false; + var plat = getApp().globalData.config.openplat; + var from = from || plat; + var data = { + mobile: user.mobile, + openid: user.openid, + isLong: isLong, + from: from + }; + var url = apis.UserAuth; + var header = { + 'content-type': 'application/x-www-form-urlencoded' + }; + return await request({ + 'url': url, + 'method': 'post', + 'data': data, + "headers": header, + 'params': {} + }).then((res) => { + if (res.code == 200) { + // utils.debug(res.data); + var user = store.state.user; + var _info = res.data.user; + var date = new Date(); + var expireTime = date.getTime() + 3600 * 24 * 1000; + //过期时间,正常是24小时 + if (isLong) { + date.setDate(date.getMonth() + 1); + //选择持久登陆时,时长改为1个月 + expireTime = date.getTime(); + } + // console.log(expireTime, _info.tokenExpired); + _info.expireTime = expireTime; + store.commit('user/login', _info) //触发@store/modules/user.js的login函数,同步执行 + return _info; + } + }) + }, + + logout: async function() { + var isCloud = config.isUserUnicloud; + var res = await _sync.Confirm.doSync('提示', '您正在注销,请确认继续', false); + if (res) { + store.commit('user/logout') //触发@store/modules/user.js的logout函数,同步执行 + if (isCloud) store.commit('userCloud/logout') + uni.showToast({ + title: '您已注销,现在重新打开APP', + icon: 'none' + }); + return true; + } + return false; + }, + + loginDraft(formData = {}, isGet = false) { + var ls = constant.userLoginForm; + var data = uni.getStorageSync(ls); + if (isGet) return data; + var data = { + ...data, + ...formData + }; + uni.setStorageSync(ls, data); + }, + + regDraft(formData = {}, isGet = false) { + var ls = constant.userRegForm; + var data = uni.getStorageSync(ls); + if (isGet) return data; + var data = { + ...data, + ...formData + }; + uni.setStorageSync(ls, data); + } +} \ No newline at end of file diff --git a/apis/ctms/vcode.js b/apis/ctms/vcode.js new file mode 100644 index 0000000..c322fff --- /dev/null +++ b/apis/ctms/vcode.js @@ -0,0 +1,47 @@ +import request from './_utils/request.js' +import apis from './apis.json' +import constant from './_utils/constant.js' +import storage from './_utils/storage.js' +export default { + //手机短信验证码相关操作 + + // 获取验证码 + //scence使用场景,phone发送手机号 + get: function(scence, phone) { + //网络请求 + var data = { + 'scence': scence, + 'phone': phone + }; + var url = apis.getVcode; + if (scence === 'caiwu') { + url = apis.getCwVcode; + } + var header = { + 'content-type': 'application/x-www-form-urlencoded' + }; + return request({ + 'url': url, + 'method': 'POST', + 'data': data, + "headers": header, + 'params': {} + }).then( + function(res) { + var data = {}; + if (res.msg) { + data.msg = res.msg; + } + if (res.code == 200) { + return { + ...res.data, + ...data + } + } else { + return false; + } + }) + }, + + +} \ No newline at end of file diff --git a/app.config.js b/app.config.js new file mode 100644 index 0000000..5e088e6 --- /dev/null +++ b/app.config.js @@ -0,0 +1,55 @@ +// 应用全局配置,App.vue挂载到getApp().globalData.config +export default { + //主界面(注意是登陆成功或者开屏广告之后的页面) + "mainPage": "/pages/ctms/tabbar/index/index", + //开启调试 + "isDebug": false, + //定义开放平台 + "openplat": 'uniCloud', + //关于应用 + "about": { + //应用名称 + "appName": "运车助手", + //应用logo + "logo": "/static/logo.png", + //公司名称 + "company": "安徽安邮车联运输有限公司", + //口号 + "slogan": "安邮车联汽车托运管理平台-值得您依赖的运车管家。", + //应用的链接,用于分享到第三方平台和生成关于我们页的二维码 + "download": "https://ctms.hiluker.cn", + //version + "version": "1.20240808.006", //用于非app端显示,app端自动获取 + "agreements": { + serviceUrl: 'https://public.hiluker.com/ctms/client/service.html', + privacyUrl: 'https://public.hiluker.com/ctms/client/private.html' + } + }, + + "h5": { + "url": "https://ctms.hiluker.cn", // 前端网页托管的域名 + // 在h5端全局悬浮引导用户下载app的功能 更多自定义要求在/common/openApp.js中修改 + // "openApp": { + // //点击悬浮下载栏后打开的网页链接 + // "openUrl": '/#/pages/uni-starter/ucenter/invite/invite', + // //左侧显示的应用名称 + // "appname": '安邮车联运车平台', + // //应用的图标 + // "logo": './static/logo.png', + // } + }, + "download": { //用于生成二合一下载页面 + "ios": "", + "android": "" + }, + //用于打开应用市场评分界面 + "marketId": { + "ios": "", + "android": "" + }, + + //配置多语言国际化。 + "i18n": { + "enable": false //默认关闭,国际化。如果你想使用国际化相关功能,请改为true + } +} \ No newline at end of file diff --git a/common/appInit.js b/common/appInit.js new file mode 100644 index 0000000..0b59f4e --- /dev/null +++ b/common/appInit.js @@ -0,0 +1,166 @@ +import _app_Config from '@/app.config.js'; +//应用初始化页 +// #ifdef APP-PLUS +import checkUpdate from '@/uni_modules/uni-upgrade-center-app/utils/check-update'; +import callCheckVersion from '@/uni_modules/uni-upgrade-center-app/utils/call-check-version'; + +// 实现,路由拦截。当应用无访问摄像头/相册权限,引导跳到设置界面 https://ext.dcloud.net.cn/plugin?id=5095 +import interceptorChooseImage from '@/uni_modules/json-interceptor-chooseImage/js_sdk/main.js'; +interceptorChooseImage() + +// #endif +const db = uniCloud.database() +export default async function() { + const debug = _app_Config.debug; + + // _app_Config挂载到getApp().globalData.config + setTimeout(() => { + getApp({ + allowDefault: true + }).globalData.config = _app_Config; + }, 1) + + + // 初始化appVersion(仅app生效) + initAppVersion(); + + //clientDB的错误提示 + function onDBError({ + code, // 错误码详见https://uniapp.dcloud.net.cn/uniCloud/clientdb?id=returnvalue + message + }) { + console.log('onDBError', { + code, + message + }); + // 处理错误 + console.error(code, message); + } + // 绑定clientDB错误事件 + db.on('error', onDBError) + + + //拦截云对象请求 + uniCloud.interceptObject({ + async invoke({ + objectName, // 云对象名称 + methodName, // 云对象的方法名称 + params // 参数列表 + }) { + // console.log('interceptObject',{ + // objectName, // 云对象名称 + // methodName, // 云对象的方法名称 + // params // 参数列表 + // }); + if (objectName == "uni-id-co" && (methodName.includes('loginBy') || ['login', + 'registerUser' + ].includes(methodName))) { + // console.log('执行登录相关云对象'); + params[0].inviteCode = await new Promise((callBack) => { + uni.getClipboardData({ + success: function(res) { + // console.log('剪切板内容:' + res.data); + if (res.data.slice(0, 18) == 'uniInvitationCode:') { + let uniInvitationCode = res.data.slice(18, 38) + // console.log('当前用户是其他用户推荐下载的,推荐者的code是:' +uniInvitationCode); + // uni.showModal({ + // content: '当前用户是其他用户推荐下载的,推荐者的code是:'+uniInvitationCode, + // showCancel: false + // }); + callBack(uniInvitationCode) + //当前用户是其他用户推荐下载的。这里登记他的推荐者id 为当前用户的myInviteCode。判断如果是注册 + } else { + callBack() + } + }, + fail() { + // console.log('error--'); + callBack() + }, + complete() { + // #ifdef MP-WEIXIN + uni.hideToast() + // #endif + } + }); + }) + // console.log(params); + } + // console.log(params); + }, + success(e) { + // console.log(e); + }, + complete() { + + }, + fail(e) { + // console.error(e); + // if (debug) { + // uni.showModal({ + // content: JSON.stringify(e), + // showCancel: false + // }); + // }else{ + // uni.showToast({ + // title: '系统错误请稍后再试', + // icon:'error' + // }); + // } + } + }) + + + // #ifdef APP-PLUS + // 监听并提示设备网络状态变化 + uni.onNetworkStatusChange(res => { + // console.log(res.isConnected); + // console.log(res.networkType); + if (res.networkType != 'none') { + uni.showToast({ + title: '当前网络类型:' + res.networkType, + icon: 'none', + duration: 3000 + }) + } else { + uni.showToast({ + title: '网络类型:' + res.networkType, + icon: 'none', + duration: 3000 + }) + } + }); + // #endif + +} +/** + * // 初始化appVersion + */ +function initAppVersion() { + // #ifdef APP-PLUS + let appid = plus.runtime.appid; + plus.runtime.getProperty(appid, (wgtInfo) => { + let appVersion = plus.runtime; + let currentVersion = appVersion.versionCode > wgtInfo.versionCode ? appVersion : wgtInfo; + getApp({ + allowDefault: true + }).appVersion = { + ...currentVersion, + appid, + hasNew: false + } + // 检查更新小红点 + callCheckVersion().then(res => { + // console.log('检查是否有可以更新的版本', res); + if (res.result.code > 0) { + // 有新版本 + getApp({ + allowDefault: true + }).appVersion.hasNew = true; + // console.log(checkUpdate()); + } + }) + }); + // 检查更新 + // #endif +} \ No newline at end of file diff --git a/common/car-p.js b/common/car-p.js new file mode 100644 index 0000000..8dee77a --- /dev/null +++ b/common/car-p.js @@ -0,0 +1,24 @@ +export default { + list0: [ + '京', '津', '冀', '晋', '蒙', '辽', '吉', + '黑', '沪', '苏', '浙', '皖', '闽', '赣', + '鲁', '豫', '鄂', '湘', '粤', '桂', + '琼', '渝', '川', '贵', '云', '藏', + '陕', '甘', '青', '宁', '新', '港', '澳', '台' + ], + //默认排序 + + list: [ + '琼', + '京', '津', '冀', + '黑', '吉', '辽', + '川', '渝', '贵', + '晋', '蒙', '鲁', '豫', + '陕', '甘', '青', '宁', '新', + '沪', '苏', '浙', + '皖', '闽', '赣', '鄂', '湘', '粤', '桂', + '云', '藏', + '港', '澳', '台' + ], + // 自定义排序 +} \ No newline at end of file diff --git a/common/graceChecker.js b/common/graceChecker.js new file mode 100644 index 0000000..b3db2d7 --- /dev/null +++ b/common/graceChecker.js @@ -0,0 +1,97 @@ +/** +数据验证(表单验证) +来自 grace.hcoder.net +作者 hcoder 深海 +*/ +export default { + error:'', + check : function (data, rule){ + for(var i = 0; i < rule.length; i++){ + if (!rule[i].checkType){return true;} + if (!rule[i].name) {return true;} + if (!rule[i].errorMsg) {return true;} + if (!data[rule[i].name]) {this.error = rule[i].errorMsg; return false;} + switch (rule[i].checkType){ + case 'string': + var reg = new RegExp('^.{' + rule[i].checkRule + '}$'); + if(!reg.test(data[rule[i].name])) {this.error = rule[i].errorMsg; return false;} + break; + case 'int': + var reg = new RegExp('^(-[1-9]|[1-9])[0-9]{' + rule[i].checkRule + '}$'); + if(!reg.test(data[rule[i].name])) {this.error = rule[i].errorMsg; return false;} + break; + break; + case 'between': + if (!this.isNumber(data[rule[i].name])){ + this.error = rule[i].errorMsg; + return false; + } + var minMax = rule[i].checkRule.split(','); + minMax[0] = Number(minMax[0]); + minMax[1] = Number(minMax[1]); + if (data[rule[i].name] > minMax[1] || data[rule[i].name] < minMax[0]) { + this.error = rule[i].errorMsg; + return false; + } + break; + case 'betweenD': + var reg = /^-?[1-9][0-9]?$/; + if (!reg.test(data[rule[i].name])) { this.error = rule[i].errorMsg; return false; } + var minMax = rule[i].checkRule.split(','); + minMax[0] = Number(minMax[0]); + minMax[1] = Number(minMax[1]); + if (data[rule[i].name] > minMax[1] || data[rule[i].name] < minMax[0]) { + this.error = rule[i].errorMsg; + return false; + } + break; + case 'betweenF': + var reg = /^-?[0-9][0-9]?.+[0-9]+$/; + if (!reg.test(data[rule[i].name])){this.error = rule[i].errorMsg; return false;} + var minMax = rule[i].checkRule.split(','); + minMax[0] = Number(minMax[0]); + minMax[1] = Number(minMax[1]); + if (data[rule[i].name] > minMax[1] || data[rule[i].name] < minMax[0]) { + this.error = rule[i].errorMsg; + return false; + } + break; + case 'same': + if (data[rule[i].name] != rule[i].checkRule) { this.error = rule[i].errorMsg; return false;} + break; + case 'notsame': + if (data[rule[i].name] == rule[i].checkRule) { this.error = rule[i].errorMsg; return false; } + break; + case 'email': + var reg = /^\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/; + if (!reg.test(data[rule[i].name])) { this.error = rule[i].errorMsg; return false; } + break; + case 'phoneno': + var reg = /^1[0-9]{10,10}$/; + if (!reg.test(data[rule[i].name])) { this.error = rule[i].errorMsg; return false; } + break; + case 'zipcode': + var reg = /^[0-9]{6}$/; + if (!reg.test(data[rule[i].name])) { this.error = rule[i].errorMsg; return false; } + break; + case 'reg': + var reg = new RegExp(rule[i].checkRule); + if (!reg.test(data[rule[i].name])) { this.error = rule[i].errorMsg; return false; } + break; + case 'in': + if(rule[i].checkRule.indexOf(data[rule[i].name]) == -1){ + this.error = rule[i].errorMsg; return false; + } + break; + case 'notnull': + if(data[rule[i].name] == null || data[rule[i].name].length < 1){this.error = rule[i].errorMsg; return false;} + break; + } + } + return true; + }, + isNumber : function (checkVal){ + var reg = /^-?[1-9][0-9]?.?[0-9]*$/; + return reg.test(checkVal); + } +} \ No newline at end of file diff --git a/common/html-parser.js b/common/html-parser.js new file mode 100644 index 0000000..20a89b2 --- /dev/null +++ b/common/html-parser.js @@ -0,0 +1,352 @@ +/* + * HTML5 Parser By Sam Blowes + * + * Designed for HTML5 documents + * + * Original code by John Resig (ejohn.org) + * http://ejohn.org/blog/pure-javascript-html-parser/ + * Original code by Erik Arvidsson, Mozilla Public License + * http://erik.eae.net/simplehtmlparser/simplehtmlparser.js + * + * ---------------------------------------------------------------------------- + * License + * ---------------------------------------------------------------------------- + * + * This code is triple licensed using Apache Software License 2.0, + * Mozilla Public License or GNU Public License + * + * //////////////////////////////////////////////////////////////////////////// + * + * 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 + * + * //////////////////////////////////////////////////////////////////////////// + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the + * License for the specific language governing rights and limitations + * under the License. + * + * The Original Code is Simple HTML Parser. + * + * The Initial Developer of the Original Code is Erik Arvidsson. + * Portions created by Erik Arvidssson are Copyright (C) 2004. All Rights + * Reserved. + * + * //////////////////////////////////////////////////////////////////////////// + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ---------------------------------------------------------------------------- + * Usage + * ---------------------------------------------------------------------------- + * + * // Use like so: + * HTMLParser(htmlString, { + * start: function(tag, attrs, unary) {}, + * end: function(tag) {}, + * chars: function(text) {}, + * comment: function(text) {} + * }); + * + * // or to get an XML string: + * HTMLtoXML(htmlString); + * + * // or to get an XML DOM Document + * HTMLtoDOM(htmlString); + * + * // or to inject into an existing document/DOM node + * HTMLtoDOM(htmlString, document); + * HTMLtoDOM(htmlString, document.body); + * + */ +// Regular Expressions for parsing tags and attributes +var startTag = /^<([-A-Za-z0-9_]+)((?:\s+[a-zA-Z_:][-a-zA-Z0-9_:.]*(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/; +var endTag = /^<\/([-A-Za-z0-9_]+)[^>]*>/; +var attr = /([a-zA-Z_:][-a-zA-Z0-9_:.]*)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g; // Empty Elements - HTML 5 + +var empty = makeMap('area,base,basefont,br,col,frame,hr,img,input,link,meta,param,embed,command,keygen,source,track,wbr'); // Block Elements - HTML 5 +// fixed by xxx 将 ins 标签从块级名单中移除 + +var block = makeMap('a,address,article,applet,aside,audio,blockquote,button,canvas,center,dd,del,dir,div,dl,dt,fieldset,figcaption,figure,footer,form,frameset,h1,h2,h3,h4,h5,h6,header,hgroup,hr,iframe,isindex,li,map,menu,noframes,noscript,object,ol,output,p,pre,section,script,table,tbody,td,tfoot,th,thead,tr,ul,video'); // Inline Elements - HTML 5 + +var inline = makeMap('abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,code,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,textarea,tt,u,var'); // Elements that you can, intentionally, leave open +// (and which close themselves) + +var closeSelf = makeMap('colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr'); // Attributes that have their values filled in disabled="disabled" + +var fillAttrs = makeMap('checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected'); // Special Elements (can contain anything) + +var special = makeMap('script,style'); +function HTMLParser(html, handler) { + var index; + var chars; + var match; + var stack = []; + var last = html; + + stack.last = function () { + return this[this.length - 1]; + }; + + while (html) { + chars = true; // Make sure we're not in a script or style element + + if (!stack.last() || !special[stack.last()]) { + // Comment + if (html.indexOf(''); + + if (index >= 0) { + if (handler.comment) { + handler.comment(html.substring(4, index)); + } + + html = html.substring(index + 3); + chars = false; + } // end tag + + } else if (html.indexOf(']*>'), function (all, text) { + text = text.replace(/|/g, '$1$2'); + + if (handler.chars) { + handler.chars(text); + } + + return ''; + }); + parseEndTag('', stack.last()); + } + + if (html == last) { + throw 'Parse Error: ' + html; + } + + last = html; + } // Clean up any remaining tags + + + parseEndTag(); + + function parseStartTag(tag, tagName, rest, unary) { + tagName = tagName.toLowerCase(); + + if (block[tagName]) { + while (stack.last() && inline[stack.last()]) { + parseEndTag('', stack.last()); + } + } + + if (closeSelf[tagName] && stack.last() == tagName) { + parseEndTag('', tagName); + } + + unary = empty[tagName] || !!unary; + + if (!unary) { + stack.push(tagName); + } + + if (handler.start) { + var attrs = []; + rest.replace(attr, function (match, name) { + var value = arguments[2] ? arguments[2] : arguments[3] ? arguments[3] : arguments[4] ? arguments[4] : fillAttrs[name] ? name : ''; + attrs.push({ + name: name, + value: value, + escaped: value.replace(/(^|[^\\])"/g, '$1\\\"') // " + + }); + }); + + if (handler.start) { + handler.start(tagName, attrs, unary); + } + } + } + + function parseEndTag(tag, tagName) { + // If no tag name is provided, clean shop + if (!tagName) { + var pos = 0; + } // Find the closest opened tag of the same type + else { + for (var pos = stack.length - 1; pos >= 0; pos--) { + if (stack[pos] == tagName) { + break; + } + } + } + + if (pos >= 0) { + // Close all the open elements, up the stack + for (var i = stack.length - 1; i >= pos; i--) { + if (handler.end) { + handler.end(stack[i]); + } + } // Remove the open elements from the stack + + + stack.length = pos; + } + } +} + +function makeMap(str) { + var obj = {}; + var items = str.split(','); + + for (var i = 0; i < items.length; i++) { + obj[items[i]] = true; + } + + return obj; +} + +function removeDOCTYPE(html) { + return html.replace(/<\?xml.*\?>\n/, '').replace(/\n/, '').replace(/\n/, ''); +} + +function parseAttrs(attrs) { + return attrs.reduce(function (pre, attr) { + var value = attr.value; + var name = attr.name; + + if (pre[name]) { + pre[name] = pre[name] + " " + value; + } else { + pre[name] = value; + } + + return pre; + }, {}); +} + +function parseHtml(html) { + html = removeDOCTYPE(html); + var stacks = []; + var results = { + node: 'root', + children: [] + }; + HTMLParser(html, { + start: function start(tag, attrs, unary) { + var node = { + name: tag + }; + + if (attrs.length !== 0) { + node.attrs = parseAttrs(attrs); + } + + if (unary) { + var parent = stacks[0] || results; + + if (!parent.children) { + parent.children = []; + } + + parent.children.push(node); + } else { + stacks.unshift(node); + } + }, + end: function end(tag) { + var node = stacks.shift(); + if (node.name !== tag) console.error('invalid state: mismatch end tag'); + + if (stacks.length === 0) { + results.children.push(node); + } else { + var parent = stacks[0]; + + if (!parent.children) { + parent.children = []; + } + + parent.children.push(node); + } + }, + chars: function chars(text) { + var node = { + type: 'text', + text: text + }; + + if (stacks.length === 0) { + results.children.push(node); + } else { + var parent = stacks[0]; + + if (!parent.children) { + parent.children = []; + } + + parent.children.push(node); + } + }, + comment: function comment(text) { + var node = { + node: 'comment', + text: text + }; + var parent = stacks[0]; + + if (!parent.children) { + parent.children = []; + } + + parent.children.push(node); + } + }); + return results.children; +} + +export default parseHtml; diff --git a/common/letter.js b/common/letter.js new file mode 100644 index 0000000..1d9cb20 --- /dev/null +++ b/common/letter.js @@ -0,0 +1,84 @@ +export default { + list: [ + 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', 'J', 'K', 'L', 'M', 'N', + 'O', 'P', 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', 'Y', 'Z' + ], + listDatacom: [{ + value: 'A', + text: "A" + }, { + value: 'B', + text: "B" + }, { + value: 'C', + text: "C" + }, { + value: 'D', + text: "D" + }, { + value: 'E', + text: "F" + }, { + value: 'G', + text: "G" + }, { + value: 'H', + text: "H" + }, { + value: 'I', + text: "I" + }, { + value: 'J', + text: "J" + }, { + value: 'K', + text: "K" + }, { + value: 'L', + text: "L" + }, { + value: 'M', + text: "M" + }, { + value: 'N', + text: "N" + }, { + value: 'O', + text: "O" + }, { + value: 'P', + text: "P" + }, { + value: 'Q', + text: "Q" + }, { + value: 'R', + text: "R" + }, { + value: 'S', + text: "S" + }, { + value: 'T', + text: "T" + }, { + value: 'U', + text: "U" + }, { + value: 'V', + text: "V" + }, { + value: 'W', + text: "W" + }, { + value: 'X', + text: "X" + }, { + value: 'Y', + text: "Y" + }, { + value: 'Z', + text: "Z" + }] +} \ No newline at end of file diff --git a/common/openApp.js b/common/openApp.js new file mode 100644 index 0000000..43f2b0d --- /dev/null +++ b/common/openApp.js @@ -0,0 +1,36 @@ +/* + 创建在h5端全局悬浮引导用户下载app的功能, + 如不需要本功能直接移除配置文件app.config.js下的h5/openApp即可 +*/ + +import CONFIG from '../app.config.js'; + +const CONFIG_OPEN = CONFIG.h5.openApp || {}; +// 仅H5端添加"打开APP" +export default function() { + // #ifdef H5 + if (!CONFIG_OPEN.openUrl) return; + + let openLogo = CONFIG_OPEN.logo ? + `` : ''; + let openApp = document.createElement("div"); + openApp.id = 'openApp'; + openApp.style = + 'position: fixed;background:#FFFFFF;box-shadow: #eeeeee 1px 1px 9px; ;top: 0;left: 0;right: 0;z-index: 999;width: 100%;height: 45px;display: flex;flex-direction: row;justify-content: space-between;align-items: center;box-sizing: border-box;padding: 0 0.5rem;' + openApp.innerHTML = ` +
+ ${openLogo} +
${CONFIG_OPEN.appname || ''}
+
+
下载app
+ `; + document.body.insertBefore(openApp, document.body.firstChild); + document.body.style = 'height:calc(100% - 45px); margin-top:45px;'; + openApp.addEventListener('click', e => { + var target = e.target || e.srcElement; + if (target.className.indexOf('openBtn') >= 0) { + window.location.href = CONFIG_OPEN.openUrl; + } + }) + //#endif +} \ No newline at end of file diff --git a/common/permission.js b/common/permission.js new file mode 100644 index 0000000..cf8ab06 --- /dev/null +++ b/common/permission.js @@ -0,0 +1,245 @@ +/// null = 未请求,1 = 已允许,0 = 拒绝|受限, 2 = 系统未开启 + +var isIOS + +function album() { + var result = 0; + var PHPhotoLibrary = plus.ios.import("PHPhotoLibrary"); + var authStatus = PHPhotoLibrary.authorizationStatus(); + if (authStatus === 0) { + result = null; + } else if (authStatus == 3) { + result = 1; + } else { + result = 0; + } + plus.ios.deleteObject(PHPhotoLibrary); + return result; +} + +function camera() { + var result = 0; + var AVCaptureDevice = plus.ios.import("AVCaptureDevice"); + var authStatus = AVCaptureDevice.authorizationStatusForMediaType('vide'); + if (authStatus === 0) { + result = null; + } else if (authStatus == 3) { + result = 1; + } else { + result = 0; + } + plus.ios.deleteObject(AVCaptureDevice); + return result; +} + +function location() { + var result = 0; + var cllocationManger = plus.ios.import("CLLocationManager"); + var enable = cllocationManger.locationServicesEnabled(); + var status = cllocationManger.authorizationStatus(); + if (!enable) { + result = 2; + } else if (status === 0) { + result = null; + } else if (status === 3 || status === 4) { + result = 1; + } else { + result = 0; + } + plus.ios.deleteObject(cllocationManger); + return result; +} + +function push() { + var result = 0; + var UIApplication = plus.ios.import("UIApplication"); + var app = UIApplication.sharedApplication(); + var enabledTypes = 0; + if (app.currentUserNotificationSettings) { + var settings = app.currentUserNotificationSettings(); + enabledTypes = settings.plusGetAttribute("types"); + if (enabledTypes == 0) { + result = 0; + console.log("推送权限没有开启"); + } else { + result = 1; + console.log("已经开启推送功能!") + } + plus.ios.deleteObject(settings); + } else { + enabledTypes = app.enabledRemoteNotificationTypes(); + if (enabledTypes == 0) { + result = 3; + console.log("推送权限没有开启!"); + } else { + result = 4; + console.log("已经开启推送功能!") + } + } + plus.ios.deleteObject(app); + plus.ios.deleteObject(UIApplication); + return result; +} + +function contact() { + var result = 0; + var CNContactStore = plus.ios.import("CNContactStore"); + var cnAuthStatus = CNContactStore.authorizationStatusForEntityType(0); + if (cnAuthStatus === 0) { + result = null; + } else if (cnAuthStatus == 3) { + result = 1; + } else { + result = 0; + } + plus.ios.deleteObject(CNContactStore); + return result; +} + +function record() { + var result = null; + var avaudiosession = plus.ios.import("AVAudioSession"); + var avaudio = avaudiosession.sharedInstance(); + var status = avaudio.recordPermission(); + // console.log("permissionStatus:" + status); + if (status === 1970168948) { + result = null; + } else if (status === 1735552628) { + result = 1; + } else { + result = 0; + } + plus.ios.deleteObject(avaudiosession); + return result; +} + +function calendar() { + var result = null; + var EKEventStore = plus.ios.import("EKEventStore"); + var ekAuthStatus = EKEventStore.authorizationStatusForEntityType(0); + if (ekAuthStatus == 3) { + result = 1; + console.log("日历权限已经开启"); + } else { + console.log("日历权限没有开启"); + } + plus.ios.deleteObject(EKEventStore); + return result; +} + +function memo() { + var result = null; + var EKEventStore = plus.ios.import("EKEventStore"); + var ekAuthStatus = EKEventStore.authorizationStatusForEntityType(1); + if (ekAuthStatus == 3) { + result = 1; + console.log("备忘录权限已经开启"); + } else { + console.log("备忘录权限没有开启"); + } + plus.ios.deleteObject(EKEventStore); + return result; +} + + +function requestIOS(permissionID) { + return new Promise((resolve, reject) => { + switch (permissionID) { + case "push": + resolve(push()); + break; + case "location": + resolve(location()); + break; + case "record": + resolve(record()); + break; + case "camera": + resolve(camera()); + break; + case "album": + resolve(album()); + break; + case "contact": + resolve(contact()); + break; + case "calendar": + resolve(calendar()); + break; + case "memo": + resolve(memo()); + break; + default: + resolve(0); + break; + } + }); +} + +function requestAndroid(permissionID) { + return new Promise((resolve, reject) => { + plus.android.requestPermissions( + [permissionID], + function(resultObj) { + var result = 0; + for (var i = 0; i < resultObj.granted.length; i++) { + var grantedPermission = resultObj.granted[i]; + console.log('已获取的权限:' + grantedPermission); + result = 1 + } + for (var i = 0; i < resultObj.deniedPresent.length; i++) { + var deniedPresentPermission = resultObj.deniedPresent[i]; + console.log('拒绝本次申请的权限:' + deniedPresentPermission); + result = 0 + } + for (var i = 0; i < resultObj.deniedAlways.length; i++) { + var deniedAlwaysPermission = resultObj.deniedAlways[i]; + console.log('永久拒绝申请的权限:' + deniedAlwaysPermission); + result = -1 + } + resolve(result); + }, + function(error) { + console.log('result error: ' + error.message) + resolve({ + code: error.code, + message: error.message + }); + } + ); + }); +} + +function gotoAppPermissionSetting() { + if (permission.isIOS) { + var UIApplication = plus.ios.import("UIApplication"); + var application2 = UIApplication.sharedApplication(); + var NSURL2 = plus.ios.import("NSURL"); + var setting2 = NSURL2.URLWithString("app-settings:"); + application2.openURL(setting2); + plus.ios.deleteObject(setting2); + plus.ios.deleteObject(NSURL2); + plus.ios.deleteObject(application2); + } else { + var Intent = plus.android.importClass("android.content.Intent"); + var Settings = plus.android.importClass("android.provider.Settings"); + var Uri = plus.android.importClass("android.net.Uri"); + var mainActivity = plus.android.runtimeMainActivity(); + var intent = new Intent(); + intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); + var uri = Uri.fromParts("package", mainActivity.getPackageName(), null); + intent.setData(uri); + mainActivity.startActivity(intent); + } +} + +const permission = { + get isIOS() { + return typeof isIOS === 'boolean' ? isIOS : (isIOS = uni.getSystemInfoSync().platform === 'ios') + }, + requestIOS: requestIOS, + requestAndroid: requestAndroid, + gotoAppSetting: gotoAppPermissionSetting +} + +export default permission \ No newline at end of file diff --git a/common/province.js b/common/province.js new file mode 100644 index 0000000..d3545e1 --- /dev/null +++ b/common/province.js @@ -0,0 +1,503 @@ +export default { + listByCode: { + '110000': "北京", + '120000': "天津", + '130000': "河北省", + '140000': "山西省", + '150000': "内蒙古自治区", + '210000': "辽宁省", + '220000': "吉林省", + '230000': "黑龙江省", + '310000': "上海", + '320000': "江苏省", + '330000': "浙江省", + '340000': "安徽省", + '350000': "福建省", + '360000': "江西省", + '370000': "山东省", + '410000': "河南省", + '420000': "湖北省", + '430000': "湖南省", + '440000': "广东省", + '450000': "广西壮族自治区", + '460000': "海南省", + '500000': "重庆", + '510000': "四川省", + '520000': "贵州省", + '530000': "云南省", + '540000': "西藏自治区", + '610000': "陕西省", + '620000': "甘肃省", + '630000': "青海省", + '640000': "宁夏回族自治区", + '650000': "新疆维吾尔自治区", + '710000': "台湾省", + '810000': "香港特别行政区", + '820000': "澳门特别行政区", + '990000': "海外", + }, + listByTitle: { + "北京": '110000', + "天津": '120000', + "河北省": '130000', + "山西省": '140000', + "内蒙古自治区": '150000', + "辽宁省": '210000', + "吉林省": '220000', + "黑龙江省": '230000', + "上海": '310000', + "江苏省": '320000', + "浙江省": '330000', + "安徽省": '340000', + "福建省": '350000', + "江西省": '360000', + "山东省": '370000', + "河南省": '410000', + "湖北省": '420000', + "湖南省": '430000', + "广东省": '440000', + "广西壮族自治区": '450000', + "海南省": '460000', + "重庆": '500000', + "四川省": '510000', + "贵州省": '520000', + "云南省": '530000', + "西藏自治区": '540000', + "陕西省": '610000', + "甘肃省": '620000', + "青海省": '630000', + "宁夏回族自治区": '640000', + "新疆维吾尔自治区": '650000', + "台湾省": '710000', + "香港特别行政区": '810000', + "澳门特别行政区": '820000', + "海外": '990000', + }, + listByChar: { + beijing: { + k: '110000', + v: "北京" + }, + tianjin: { + k: '120000', + v: "天津" + }, + hebei: { + k: '130000', + v: "河北省" + }, + shanxi1: { + k: '140000', + v: "山西省" + }, + neimenggu: { + k: '150000', + v: "内蒙古自治区" + }, + liaoning: { + k: '210000', + v: "辽宁省" + }, + jilin: { + k: '220000', + v: "吉林省" + }, + heilongjiang: { + k: '230000', + v: "黑龙江省" + }, + shanghai: { + k: '310000', + v: "上海" + }, + jiangsu: { + k: '320000', + v: "江苏省" + }, + zhejiang: { + k: '330000', + v: "浙江省" + }, + anhui: { + k: '340000', + v: "安徽省" + }, + fujian: { + k: '350000', + v: "福建省" + }, + jiangxi: { + k: '360000', + v: "江西省" + }, + shandong: { + k: '370000', + v: "山东省" + }, + henan: { + k: '410000', + v: "河南省" + }, + hubei: { + k: '420000', + v: "湖北省" + }, + hunan: { + k: '430000', + v: "湖南省" + }, + guangdong: { + k: '440000', + v: "广东省" + }, + guangxi: { + k: '450000', + v: "广西壮族自治区" + }, + hainan: { + k: '460000', + v: "海南省" + }, + chongqing: { + k: '500000', + v: "重庆" + }, + sichuan: { + k: '510000', + v: "四川省" + }, + guizhou: { + k: '520000', + v: "贵州省" + }, + yunnan: { + k: '530000', + v: "云南省" + }, + xizang: { + k: '540000', + v: "西藏自治区" + }, + shanxi2: { + k: '610000', + v: "陕西省" + }, + gansu: { + k: '620000', + v: "甘肃省" + }, + qinghai: { + k: '630000', + v: "青海省" + }, + ningxia: { + k: '640000', + v: "宁夏回族自治区" + }, + xinjiang: { + k: '650000', + v: "新疆维吾尔自治区" + }, + taiwan: { + k: '710000', + v: "台湾省" + }, + xianggang: { + k: '810000', + v: "香港特别行政区" + }, + aomen: { + k: '820000', + v: "澳门特别行政区" + }, + haiwai: { + k: '990000', + v: "海外" + }, + }, + + listDatacom: [{ + value: '110000', + text: "北京" + }, + { + value: '120000', + text: "天津" + }, + { + value: '130000', + text: "河北省" + }, + { + value: '140000', + text: "山西省" + }, + { + value: '150000', + text: "内蒙古自治区" + }, + { + value: '210000', + text: "辽宁省" + }, + { + value: '220000', + text: "吉林省" + }, + { + value: '230000', + text: "黑龙江省" + }, + { + value: '310000', + text: "上海" + }, + { + value: '320000', + text: "江苏省" + }, + { + value: '330000', + text: "浙江省" + }, + { + value: '340000', + text: "安徽省" + }, + { + value: '350000', + text: "福建省" + }, + { + value: '360000', + text: "江西省" + }, + { + value: '370000', + text: "山东省" + }, + { + value: '410000', + text: "河南省" + }, + { + value: '420000', + text: "湖北省" + }, + { + value: '430000', + text: "湖南省" + }, + { + value: '440000', + text: "广东省" + }, + { + value: '450000', + text: "广西壮族自治区" + }, + { + value: '460000', + text: "海南省" + }, + { + value: '500000', + text: "重庆" + }, + { + value: '510000', + text: "四川省" + }, + { + value: '520000', + text: "贵州省" + }, + { + value: '530000', + text: "云南省" + }, + { + value: '540000', + text: "西藏自治区" + }, + { + value: '610000', + text: "陕西省" + }, + { + value: '620000', + text: "甘肃省" + }, + { + value: '630000', + text: "青海省" + }, + { + value: '640000', + text: "宁夏回族自治区" + }, + { + value: '650000', + text: "新疆维吾尔自治区" + }, + { + value: '710000', + text: "台湾省" + }, + { + value: '810000', + text: "香港特别行政区" + }, + { + value: '820000', + text: "澳门特别行政区" + }, + { + value: '990000', + text: "海外" + }, + ], + + listCarP: [{ + value: '京', + text: "京" + }, + { + value: '津', + text: "津" + }, + { + value: '冀', + text: "冀" + }, + { + value: '晋', + text: "晋" + }, + { + value: '蒙', + text: "蒙" + }, + { + value: '辽', + text: "辽" + }, + { + value: '220000', + text: "吉林省" + }, + { + value: '230000', + text: "黑龙江省" + }, + { + value: '310000', + text: "上海" + }, + { + value: '320000', + text: "江苏省" + }, + { + value: '330000', + text: "浙江省" + }, + { + value: '340000', + text: "安徽省" + }, + { + value: '350000', + text: "福建省" + }, + { + value: '360000', + text: "江西省" + }, + { + value: '370000', + text: "山东省" + }, + { + value: '410000', + text: "河南省" + }, + { + value: '420000', + text: "湖北省" + }, + { + value: '430000', + text: "湖南省" + }, + { + value: '440000', + text: "广东省" + }, + { + value: '450000', + text: "广西壮族自治区" + }, + { + value: '460000', + text: "海南省" + }, + { + value: '500000', + text: "重庆" + }, + { + value: '510000', + text: "四川省" + }, + { + value: '520000', + text: "贵州省" + }, + { + value: '530000', + text: "云南省" + }, + { + value: '540000', + text: "西藏自治区" + }, + { + value: '610000', + text: "陕西省" + }, + { + value: '620000', + text: "甘肃省" + }, + { + value: '630000', + text: "青海省" + }, + { + value: '640000', + text: "宁夏回族自治区" + }, + { + value: '650000', + text: "新疆维吾尔自治区" + }, + { + value: '710000', + text: "台湾省" + }, + { + value: '810000', + text: "香港特别行政区" + }, + { + value: '820000', + text: "澳门特别行政区" + }, + { + value: '990000', + text: "海外" + }, + ], + +} diff --git a/common/util.js b/common/util.js new file mode 100644 index 0000000..d835f47 --- /dev/null +++ b/common/util.js @@ -0,0 +1,160 @@ +import permission from "./permission" + +function formatTime(time) { + if (typeof time !== 'number' || time < 0) { + return time + } + + var hour = parseInt(time / 3600) + time = time % 3600 + var minute = parseInt(time / 60) + time = time % 60 + var second = time + + return ([hour, minute, second]).map(function(n) { + n = n.toString() + return n[1] ? n : '0' + n + }).join(':') +} + +function formatLocation(longitude, latitude) { + if (typeof longitude === 'string' && typeof latitude === 'string') { + longitude = parseFloat(longitude) + latitude = parseFloat(latitude) + } + + longitude = longitude.toFixed(2) + latitude = latitude.toFixed(2) + + return { + longitude: longitude.toString().split('.'), + latitude: latitude.toString().split('.') + } +} +var dateUtils = { + UNITS: { + '年': 31557600000, + '月': 2629800000, + '天': 86400000, + '小时': 3600000, + '分钟': 60000, + '秒': 1000 + }, + humanize: function(milliseconds) { + var humanize = ''; + for (var key in this.UNITS) { + if (milliseconds >= this.UNITS[key]) { + humanize = Math.floor(milliseconds / this.UNITS[key]) + key + '前'; + break; + } + } + return humanize || '刚刚'; + }, + format: function(dateStr) { + var date = this.parse(dateStr) + var diff = Date.now() - date.getTime(); + if (diff < this.UNITS['天']) { + return this.humanize(diff); + } + var _format = function(number) { + return (number < 10 ? ('0' + number) : number); + }; + return date.getFullYear() + '/' + _format(date.getMonth() + 1) + '/' + _format(date.getDate()) + '-' + + _format(date.getHours()) + ':' + _format(date.getMinutes()); + }, + parse: function(str) { //将"yyyy-mm-dd HH:MM:ss"格式的字符串,转化为一个Date对象 + var a = str.split(/[^0-9]/); + return new Date(a[0], a[1] - 1, a[2], a[3], a[4], a[5]); + } +}; + +// 验证并返回手机号或null +function isPhone(phone) { + var pattern = /^(?:(?:\+|00)86)?1(?:3\d|4[5-79]|5[0-35-9]|6[5-7]|7[0-8]|8\d|9[189])\d{8}$/; + + var regex = new RegExp(pattern); + var res = regex.exec(phone); + if (res) { + return res[0]; + } + return false; +} + +function tel(phone) { + permission.requestAndroid('contact').then( + (res) => { + if (res !== 1) { + uni.showModal({ + title: '权限申请', + content: '拨打电话需要您的授权', + success: function(res) { + if (res.confirm) { + permission.gotoAppSetting(); + } else if (res.cancel) { + // console.log('用户点击取消'); + } + } + }) + } else { + uni.makePhoneCall({ + phoneNumber: phone + }) + } + } + ); +} + +const captcha = { + ls: 'LocalCaptcha', + get: function(scence = 'vcode', useLetter = 'false') { + let digits = '0123456789'; + let upperCaseLetters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'; + let code = '', + charType = digits; + for (let i = 0; i < 4; i++) { + // 随机选择生成数字还是大写字母 + if (useLetter === true) charType = Math.random() < 0.5 ? digits : upperCaseLetters; + code += charType[Math.floor(Math.random() * charType.length)]; + } + var codes = uni.getStorageSync(this.ls) || {}; + codes[scence] = code; + uni.getStorageSync(this.ls, codes); + return code; + }, + + check: function(scence = 'vcode', code) { + var codes = uni.getStorageSync(this.ls) || {}; + if (codes[scence] === code) { + return true; + } + return false; + }, + + pop: function(scence = 'vcode', useLetter = 'false') { + var code = this.get(scence, useLetter); + uni.showModal({ + title: '请输入数字 ' + code + ' 验证', + editable: true, + success: function(res) { + if (res.confirm) { + // console.log('数字码', res.content) + if (res.content === code) return true; + return false; + } else if (res.cancel) { + // console.log('用户点击取消'); + return false; + } + } + }) + } +}; + + +export default { + formatTime, + formatLocation, + dateUtils, + tel, + captcha, + isPhone +} \ No newline at end of file diff --git a/components/refreshBox/refreshBox.vue b/components/refreshBox/refreshBox.vue new file mode 100644 index 0000000..1165d06 --- /dev/null +++ b/components/refreshBox/refreshBox.vue @@ -0,0 +1,95 @@ + + + + diff --git a/config/ctms.config.js b/config/ctms.config.js new file mode 100644 index 0000000..aeba4a9 --- /dev/null +++ b/config/ctms.config.js @@ -0,0 +1,38 @@ +// 应用子项目配置 +export default { + /*接口设置*/ + apiUrl: 'https://api.ctms.hiluker.cn/', + // apiUrl: 'http://11.22.33.48:10032/', + //网址后缀 + url_suffix: '453', + //入口脚本文件 + url_entry: 'index.php/client/v1', + // url_entry: 'index_test.php/client/v1', + //运单H5预览地址 + h5_view: 'https://h5.ctms.hiluker.cn/pages/order/detail.html', + //应用ID,对应到vapp-sass轻应用系统 + appid: 1, + //归属商户ID(即平台ID) + pid: 1, + //请求头配置 + headers: { + isToken: true, + Authorization: 'hiCtmsClientXXXXXXXXXXXXXX', //请求授权的token示范 + params: [], //TODO,暂未想好如何配置 + }, + // loginPage: 'uni_modules/uni-id-pages/pages/login/login-withoutpwd', + loginPage: 'pages/ctms/login/loginSms', + pageDir: "/pages/ctms/", //子项目页面目录 + kfPhone: "13211111058", //客服手机号 + "version": "1.20240808.008", //内置版本号 + isUserUnicloud: false, + //是否使用 用户uni云服务 + demoCarno: '皖ABBBBB', + //示例车牌号 + 'poweredBy': '安徽安邮车联运输有限公司', + //版权归属 + 'supportedBy': 'Hiluker & Fm453', + //技术支持 + 'thanksFor': ['北京数字天堂科技 DCloud', '阿里云 aliyun.com'], + //鸣谢 +} \ No newline at end of file diff --git a/config/im.config.js b/config/im.config.js new file mode 100644 index 0000000..2512bcb --- /dev/null +++ b/config/im.config.js @@ -0,0 +1,5 @@ +// 应用子项目配置,App.vue挂载到getApp().globalData.config +export default { + apiUrl: 'https://im.hiluker.cn/', + +} \ No newline at end of file diff --git a/hybrid/html/404.html b/hybrid/html/404.html new file mode 100644 index 0000000..3643853 --- /dev/null +++ b/hybrid/html/404.html @@ -0,0 +1,36 @@ + + + + + + 页面走丢了 + + + +

啊,抱歉,您要访问的页面找不到了。

+ + \ No newline at end of file diff --git a/hybrid/html/local.html b/hybrid/html/local.html new file mode 100644 index 0000000..9a21210 --- /dev/null +++ b/hybrid/html/local.html @@ -0,0 +1,88 @@ + + + + + + 本地网页 + + + +

web-view 组件加载本地 html 示例,仅在 App 环境下生效。点击下列按钮,跳转至其它页面。

+
+ + + + + +
+

网页向应用发送消息。注意:小程序端应用会在此页面后退时接收到消息。

+
+ +
+ + + + + diff --git a/index.html b/index.html new file mode 100644 index 0000000..c6a0d97 --- /dev/null +++ b/index.html @@ -0,0 +1,22 @@ + + + + + + + + + + +
+ + + \ No newline at end of file diff --git a/lang/en.js b/lang/en.js new file mode 100644 index 0000000..14e0cd7 --- /dev/null +++ b/lang/en.js @@ -0,0 +1,191 @@ +export default { + tabbar: 'Home,Work,Mine', + agreementsTitle: 'User service agreement,Privacy policy', + common: { + wechatFriends: "friends", + wechatBbs: "bbs", + weibo: "weibo", + more: "more", + agree: "agree", + copy: "copy", + wechatApplet: "applet", + cancelShare: "cancel sharing", + updateSucceeded: "update succeeded", + phonePlaceholder: "Please enter your mobile phone number", + verifyCodePlaceholder: "Please enter the verification code", + newPasswordPlaceholder: "Please enter a new password", + confirmNewPasswordPlaceholder: "Please confirm the new password", + confirmPassword: "Please confirm the password", + verifyCodeSend: "Verification code has been sent to via SMS", + passwordDigits: "The password is 6 - 20 digits", + getVerifyCode: "Get Code", + noAgree: "You have not agreed to the privacy policy agreement", + gotIt: "got it", + login: "sign in", + error: "error", + complete: "complete", + submit: "Submit", + formatErr: "Incorrect mobile phone number format", + sixDigitCode: "Please enter a 6-digit verification code", + resetNavTitle: "Reset password" + }, + list: { + inputPlaceholder: "Please enter the search content", + }, + search: { + cancelText: "cancel", + searchHistory: "search history", + searchDiscovery: "search discovery", + deleteAll: "delete all", + delete: "delete", + deleteTip: "Are you sure to clear the search history ?", + complete: "complete", + searchHiddenTip: "Current search found hidden", + }, + grid: { + grid: "Grid Assembly", + visibleToAll: "Visible to all", + invisibleToTourists: "Invisible to tourists", + adminVisible: "Admin visible", + clickTip: "Click the", + clickTipGrid: "grid", + }, + mine: { + showText: "Text", + signIn: "Check In Reward", + signInByAd: "Check In Reward By AD", + toEvaluate: "To Evaluate", + readArticles: "Read Articles", + myScore: "My Score", + invite: "Invite Friends", + feedback: "Problems And Feedback", + settings: "Settings", + about: "About", + checkUpdate: "Check for Updates", + clicked: "You Clicked", + checkScore: "Please check your points after logging in", + currentScore: "The current score is ", + noScore: "There are currently no points", + notLogged: "not logged in", + }, + userinfo: { + navigationBarTitle: "My Profile", + ProfilePhoto: "Profile Photo", + nickname: "Nickname", + notSet: "not set", + phoneNumber: "Phone Number", + notSpecified: "Not Specified", + setNickname: "Set Nickname ", + setNicknamePlaceholder: "Please enter a nickname to set", + bindPhoneNumber: "One click binding of local number", + bindOtherLogin: "Other number binding", + noChange: "No change", + uploading: "uploading", + requestFail: "Request for service failed", + setting: "setting", + deleteSucceeded: "Delete succeeded", + setSucceeded: "Set successfully", + }, + smsCode: { + resendVerifyCode: "resend", + phoneErrTip: "Mobile phone number format error", + sendSuccessTip: "SMS verification code sent successfully", + }, + loadMore: { + noData: "No Data", + noNetwork: "Network error", + toSet: "Go to settings", + error: "error", + }, + uniFeedback: { + navigationBarTitle: "Problems and feedback", + msgTitle: "Message content", + imgTitle: "Picture list", + contacts: "contacts", + phone: "contact number", + submit: "submit", + }, + settings: { + navigationBarTitle: "Settings", + userInfo: "Personal Data", + changePassword: "change password", + clearTmp: "clean cache", + pushServer: "push function", + fingerPrint: "fingerprint unlock", + facial: "face unlock", + deactivate: "Deactivate", + logOut: "Logout", + login: "Login", + changeLanguage: "Language", + please: "please", + successText: "success", + failTip: "Authentication failed. Please try again", + authFailed: "authentication failed", + deviceNoOpen: "The device is not turned on", + fail: "fail", + tips: "tips", + exitLogin: "Do you want to log out?", + cancelText: "cancel", + confirmText: "confirm", + clearing: "clearing", + clearedSuccessed: "Cleared successfully", + }, + deactivate: { + cancelText: "cancel", + nextStep: "next step", + navigationBarTitle: "Logout prompt" + }, + about: { + sacnQR: "Scan the QR Code and your friends can also download it", + client: "applCantion", + and: "And", + about: "About", + }, + invite: { + download: "Download", + }, + login: { + phoneLogin: "After logging in, you can show yourself", + phoneLoginTip: "Unregistered mobile phone numbers will be automatically registered after verification", + getVerifyCode: "Get Code", + }, + uniQuickLogin: { + accountLogin: "Account", + SMSLogin: "SMS", + wechatLogin: "wechat", + appleLogin: "Apple", + oneClickLogin: "One click login", + QQLogin: "QQ", + xiaomiLogin: "Xiaomi", + getProviderFail: "Failed to get service provider", + loginErr: "Login service initialization error", + chooseOtherLogin: "Click the third-party login", + }, + pwdLogin: { + pwdLogin: "User name password login", + placeholder: "Please enter mobile number / user name", + passwordPlaceholder: "Please input a password", + verifyCodePlaceholder: "Please enter the verification code", + login: "sign in", + forgetPassword: "Forget password", + register: "Registered account", + }, + register: { + navigationBarTitle: "register", + usernamePlaceholder: "Please enter user name", + nicknamePlaceholder: "Please enter user nickname", + passwordDigitsPlaceholder: "Please enter a 6-20 digit password", + passwordAgain: "Enter the password again", + registerAndLogin: "Register and log in", + }, + listDetail: { + follow: "Click follow", + newsErr: "Error, news ID is empty", + }, + newsLog: { + navigationBarTitle: "Reading Log" + }, + bindMobile: { + navigationBarTitle: "Bind Mobile" + } +} \ No newline at end of file diff --git a/lang/i18n.js b/lang/i18n.js new file mode 100644 index 0000000..2cce7e3 --- /dev/null +++ b/lang/i18n.js @@ -0,0 +1,100 @@ +import langEn from './en' +import zhHans from './zh-Hans' +import _app_Config from '../app.config.js' +const { + i18n: { + enable: i18nEnable + } +} = _app_Config +const messages = { + 'en': langEn, + 'zh-Hans': zhHans +} +let currentLang +if (i18nEnable) { + currentLang = uni.getStorageSync('CURRENT_LANG') +} else { + currentLang = "zh-Hans" +} +// console.log(uni.getStorageSync('CURRENT_LANG'),currentLang); +if (!currentLang) { + if (uni.getLocale) { + console.log('获取应用语言:', uni.getLocale()); + let language = 'en' + if (uni.getLocale() != 'en') { + language = 'zh-Hans' + } + uni.setStorageSync('CURRENT_LANG', language) + currentLang = language + } else { + uni.getSystemInfo({ + success: function(res) { + console.log('获取设备信息:', res); + let language = 'zh-Hans' + if (res.language == 'en') { + language = 'en' + } + uni.setStorageSync('CURRENT_LANG', language) + currentLang = language + }, + fail: (err) => { + console.error(err) + } + }) + } +} +let i18nConfig = { + locale: currentLang, // set locale + messages // set locale messages +} + +// #ifdef VUE2 +import Vue from 'vue' +import VueI18n from 'vue-i18n' +Vue.use(VueI18n) +const i18n = new VueI18n(i18nConfig) +// #endif + +// #ifdef VUE3 +import { + createI18n +} from 'vue-i18n' +const i18n = createI18n(i18nConfig) +// #endif + +export default i18n + + +if (i18nEnable) { + console.log(` + 你已开启多语言国际化,将自动根据语言获取【lang/en.js】或【lang/en.js】文件中配置的tabbar的值, + 覆盖你在pages.json中的tabbar的值 + 如果你不需要多语言国际化,请打开配置文件app.config.js找到 -> i18n -> enable把值设置为false +`); + let initLanguageAfter = () => { + function $i18n(e) { + // #ifdef VUE3 + return i18n.global.messages[i18n.global.locale][e] + // #endif + return i18n.messages[i18n.locale][e] + } + setTimeout(function() { + //底部tabbar更新 + $i18n('tabbar').split(',').forEach((text, index) => { + // console.log(text); + uni.setTabBarItem({ + index, + text, + complete: e => { + // console.log("e: " + JSON.stringify(e)); + } + }) + }) + }, 1) + } + initLanguageAfter() + uni.$on('changeLanguage', e => { + console.log('changeLanguage', e); + initLanguageAfter(e) + }) +} \ No newline at end of file diff --git a/lang/zh-Hans.js b/lang/zh-Hans.js new file mode 100644 index 0000000..dbaee5d --- /dev/null +++ b/lang/zh-Hans.js @@ -0,0 +1,198 @@ +export default { + tabbar: '首页,功能,我的', + agreementsTitle: '用户服务协议,隐私政策', + common: { + wechatFriends: "微信好友", + wechatBbs: "微信朋友圈", + weibo: "微博", + more: "更多", + agree: "同意", + copy: "复制", + wechatApplet: "微信小程序", + cancelShare: "取消分享", + updateSucceeded: "更新成功", + phonePlaceholder: "请输入手机号", + verifyCodePlaceholder: "请输入验证码", + newPasswordPlaceholder: "请输入新密码", + confirmNewPasswordPlaceholder: "请确认新密码", + confirmPassword: "请确认密码", + verifyCodeSend: "验证码已通过短信发送至", + passwordDigits: "密码为6 - 20位", + getVerifyCode: "获取验证码", + noAgree: "你未同意隐私政策协议", + gotIt: "知道了", + login: "登录", + error: "错误", + complete: "完成", + submit: "提交", + formatErr: "手机号码格式不正确", + sixDigitCode: "请输入6位验证码", + resetNavTitle: "重置密码" + + }, + list: { + inputPlaceholder: "请输入搜索内容", + }, + search: { + cancelText: '取消', + searchHistory: "搜索历史", + searchDiscovery: "搜索发现", + deleteAll: "全部删除", + delete: "删除", + deleteTip: "确认清空搜索历史吗?", + complete: "完成", + searchHiddenTip: "当前搜索发现已隐藏", + }, + grid: { + grid: "宫格组件", + visibleToAll: "所有人可见", + invisibleToTourists: "游客不可见", + adminVisible: "管理员可见", + clickTip: "点击第", + clickTipGrid: "个宫格", + }, + mine: { + showText: "文字", + scanText: "扫码", + appsText: "应用", + scoreText: "积分", + noticeText: "消息", + signIn: "普通签到", + signInByAd: "看广告签到", + toEvaluate: "去评分", + readArticles: "阅读过的文章", + myScore: "我的积分", + invite: "分销推荐", + feedback: "问题与反馈", + settings: "设置", + checkUpdate: "检查更新", + about: "关于", + clicked: "你点击了", + checkScore: "请登录后查看积分", + currentScore: "当前积分为", + noScore: "当前无积分", + notLogged: "未登录", + newsList: "新闻", + articleList: "文章", + }, + userinfo: { + navigationBarTitle: "个人资料", + ProfilePhoto: "头像", + nickname: "昵称", + notSet: "未设置", + phoneNumber: "手机号", + notSpecified: "未绑定", + setNickname: "设置昵称", + setNicknamePlaceholder: "请输入要设置的昵称", + bindPhoneNumber: "本机号码一键绑定", + bindOtherLogin: "其他号码绑定", + noChange: "没有变化", + uploading: "正在上传", + requestFail: "请求服务失败", + setting: "设置中", + deleteSucceeded: "删除成功", + setSucceeded: "设置成功", + }, + smsCode: { + resendVerifyCode: "重新发送", + phoneErrTip: "手机号格式错误", + sendSuccessTip: "短信验证码发送成功", + }, + loadMore: { + noData: "暂无数据", + noNetwork: "网络异常", + toSet: "前往设置", + error: "错误", + }, + uniFeedback: { + navigationBarTitle: "问题与反馈", + msgTitle: "留言内容", + imgTitle: "图片列表", + contacts: "联系人", + phone: "联系电话", + submit: "提交", + }, + settings: { + navigationBarTitle: "设置", + userInfo: "账号资料", + changePassword: "修改密码", + clearTmp: "清理缓存", + pushServer: "推送功能", + fingerPrint: "指纹解锁", + facial: "人脸解锁", + deactivate: "注销账号", + logOut: "退出登录", + login: "登录", + failTip: "认证失败请重试", + authFailed: "认证失败", + changeLanguage: "切换语言", + please: "请用", + successText: "成功", + deviceNoOpen: "设备未开启", + fail: "失败", + tips: "提示", + exitLogin: "是否退出登录?", + clearing: "清除中", + clearedSuccessed: "清除成功", + confirmText: "确定", + cancelText: '取消', + }, + deactivate: { + cancelText: '取消', + nextStep: "下一步", + navigationBarTitle: "注销提示" + }, + about: { + sacnQR: "扫描二维码,您的朋友也可以下载", + client: "客户端", + and: "和", + about: "关于", + }, + invite: { + download: "下载", + }, + login: { + phoneLogin: "登录后即可展示自己", + phoneLoginTip: "未注册的手机号验证通过后将自动注册", + getVerifyCode: "获取验证码", + }, + uniQuickLogin: { + accountLogin: "账号登录", + SMSLogin: "短信验证码", + wechatLogin: "微信登录", + appleLogin: "苹果登录", + oneClickLogin: "一键登录", + QQLogin: "QQ登录", + xiaomiLogin: "小米登录", + getProviderFail: "获取服务供应商失败", + loginErr: "登录服务初始化错误", + chooseOtherLogin: "点击了第三方登录", + }, + pwdLogin: { + pwdLogin: "用户名密码登录", + placeholder: "请输入手机号/用户名", + passwordPlaceholder: "请输入密码", + verifyCodePlaceholder: "请输入验证码", + login: "登录", + forgetPassword: "忘记密码", + register: "注册账号", + }, + register: { + navigationBarTitle: "注册", + usernamePlaceholder: "请输入用户名", + nicknamePlaceholder: "请输入用户昵称", + registerAndLogin: "注册并登录", + passwordDigitsPlaceholder: "请输入6-20位密码", + passwordAgain: "再次输入密码", + }, + listDetail: { + follow: "点击关注", + newsErr: "出错了,新闻ID为空", + }, + newsLog: { + navigationBarTitle: "阅读记录" + }, + bindMobile: { + navigationBarTitle: "绑定手机号码" + } +} \ No newline at end of file diff --git a/main.js b/main.js new file mode 100644 index 0000000..322e873 --- /dev/null +++ b/main.js @@ -0,0 +1,30 @@ +import App from './App' +import i18n from './lang/i18n' +import utils from "@/utils/common.js" //自定义函数 + +// #ifdef VUE2 +import Vue from 'vue' +Vue.config.productionTip = false +Vue.mixin(utils); //vue2方式混入 //未测试可用性 +App.mpType = 'app' +const app = new Vue({ + i18n, + ...App +}) +app.$mount() +// #endif + + +// #ifdef VUE3 +import { + createSSRApp +} from 'vue' + +export function createApp() { + const app = createSSRApp(App) + app.use(i18n); + return { + app + } +} +// #endif \ No newline at end of file diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..fadec38 --- /dev/null +++ b/manifest.json @@ -0,0 +1,254 @@ +{ + "name" : "运车助手", + "appid" : "__UNI__B63B6BD", + "description" : "ctms运车平台用户前端,为用户提供下单、查询及相关售后跟进等服务", + "versionName" : "1.20240808.006", + "versionCode" : 106, + "transformPx" : false, + "app-plus" : { + "usingComponents" : true, + "nvueCompiler" : "uni-app", + "splashscreen" : { + "alwaysShowBeforeRender" : true, + "waiting" : true, + "autoclose" : true, + "delay" : 0 + }, + "modules" : { + "Barcode" : {}, + "Camera" : {}, + "Geolocation" : {}, + "Maps" : {}, + "Share" : {}, + "OAuth" : {}, + "Push" : {} + }, + "distribute" : { + "android" : { + "permissions" : [ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + "schemes" : "ctms,ctms-client", + "minSdkVersion" : 26, + "targetSdkVersion" : 33, + "abiFilters" : [ "armeabi-v7a", "arm64-v8a" ] + }, + "ios" : { + "urltypes" : "ctms,ctms-client", + "capabilities" : { + "entitlements" : { + "com.apple.developer.associated-domains" : [] + } + }, + "UIBackgroundModes" : "location", + "dSYMs" : false, + "idfa" : false + }, + "sdkConfigs" : { + "geolocation" : { + "system" : { + "__platform__" : [ "ios", "android" ] + }, + "amap" : { + "name" : "map_18608981880QdibHebq", + "__platform__" : [ "ios", "android" ], + "appkey_ios" : "f83067a97f0523331c3b06cf4ea28775", + "appkey_android" : "0467567b45ec78d05357615209c7e69d" + } + }, + "maps" : { + "amap" : { + "name" : "map_18608981880QdibHebq", + "appkey_ios" : "f83067a97f0523331c3b06cf4ea28775", + "appkey_android" : "0467567b45ec78d05357615209c7e69d" + } + }, + "push" : { + "unipush" : { + "version" : "2", + "offline" : false, + "icons" : { + "small" : { + "hdpi" : "unpackage/res/push/icon-36.png" + } + } + } + }, + "share" : {}, + "speech" : {}, + "statics" : {}, + "ad" : {}, + "oauth" : {} + }, + "icons" : { + "android" : { + "hdpi" : "unpackage/res/icons/72x72.png", + "xhdpi" : "unpackage/res/icons/96x96.png", + "xxhdpi" : "unpackage/res/icons/144x144.png", + "xxxhdpi" : "unpackage/res/icons/192x192.png" + }, + "ios" : { + "appstore" : "unpackage/res/icons/1024x1024.png", + "ipad" : { + "app" : "unpackage/res/icons/76x76.png", + "app@2x" : "unpackage/res/icons/152x152.png", + "notification" : "unpackage/res/icons/20x20.png", + "notification@2x" : "unpackage/res/icons/40x40.png", + "proapp@2x" : "unpackage/res/icons/167x167.png", + "settings" : "unpackage/res/icons/29x29.png", + "settings@2x" : "unpackage/res/icons/58x58.png", + "spotlight" : "unpackage/res/icons/40x40.png", + "spotlight@2x" : "unpackage/res/icons/80x80.png" + }, + "iphone" : { + "app@2x" : "unpackage/res/icons/120x120.png", + "app@3x" : "unpackage/res/icons/180x180.png", + "notification@2x" : "unpackage/res/icons/40x40.png", + "notification@3x" : "unpackage/res/icons/60x60.png", + "settings@2x" : "unpackage/res/icons/58x58.png", + "settings@3x" : "unpackage/res/icons/87x87.png", + "spotlight@2x" : "unpackage/res/icons/80x80.png", + "spotlight@3x" : "unpackage/res/icons/120x120.png" + } + } + }, + "splashscreen" : { + "androidStyle" : "default", + "android" : { + "hdpi" : "unpackage/res/splash/splash-480X762.png", + "xhdpi" : "unpackage/res/splash/splash-720X1242.png", + "xxhdpi" : "unpackage/res/splash/splash-1080X1882.png" + }, + "useOriginalMsgbox" : true + } + }, + "error" : { + "url" : "hybrid/html/404.html" + }, + "uniStatistics" : { + "enable" : true + } + }, + "quickapp" : {}, + "mp-weixin" : { + "appid" : "wxccd7e2a0911b3397", + "setting" : { + "urlCheck" : false, + "es6" : false, + "minified" : true, + "postcss" : true + }, + "optimization" : { + "subPackages" : true + }, + "usingComponents" : true, + "uniStatistics" : { + "enable" : false + }, + "unipush" : { + "enable" : true + } + }, + "vueVersion" : "3", + "h5" : { + "template" : "static/index.html", + "devServer" : { + "port" : 9090, + "https" : false + }, + "title" : "汽车托运助手", + "router" : { + "mode" : "hash", + "base" : "./" + }, + "unipush" : { + "enable" : true + }, + "sdkConfigs" : { + "maps" : { + "amap" : { + "key" : "e3df95513a8c0ca18fdecbac3c4c7bc4", + "securityJsCode" : "d21c9d60fe573e0ceb8e23396fe29233", + "serviceHost" : "" + } + } + }, + "uniStatistics" : { + "enable" : true + }, + "optimization" : { + "treeShaking" : { + "enable" : true + } + } + }, + "locale" : "zh-Hans", + "fallbackLocale" : "zh-Hans", + "uniStatistics" : { + "version" : "2", + "enable" : false + }, + "mp-alipay" : { + "uniStatistics" : { + "enable" : false + } + }, + "mp-baidu" : { + "uniStatistics" : { + "enable" : false + } + }, + "mp-jd" : { + "uniStatistics" : { + "enable" : false + } + }, + "mp-kuaishou" : { + "uniStatistics" : { + "enable" : false + } + }, + "mp-lark" : { + "uniStatistics" : { + "enable" : false + } + }, + "mp-qq" : { + "uniStatistics" : { + "enable" : false + } + }, + "mp-toutiao" : { + "uniStatistics" : { + "enable" : false + } + }, + "quickapp-webview-huawei" : { + "uniStatistics" : { + "enable" : false + } + }, + "quickapp-webview-union" : { + "uniStatistics" : { + "enable" : false + } + } +} diff --git a/node_modules/.package-lock.json b/node_modules/.package-lock.json new file mode 100644 index 0000000..e9f9412 --- /dev/null +++ b/node_modules/.package-lock.json @@ -0,0 +1,13 @@ +{ + "name": "uniStarter", + "version": "2.2.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "node_modules/qrcodejs2": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/qrcodejs2/-/qrcodejs2-0.0.2.tgz", + "integrity": "sha512-+Y4HA+cb6qUzdgvI3KML8GYpMFwB24dFwzMkS/yXq6hwtUGNUnZQdUnksrV1XGMc2mid5ROw5SAuY9XhI3ValA==" + } + } +} diff --git a/node_modules/qrcodejs2/.npmignore b/node_modules/qrcodejs2/.npmignore new file mode 100644 index 0000000..f4bd643 --- /dev/null +++ b/node_modules/qrcodejs2/.npmignore @@ -0,0 +1,4 @@ +.DS_Store + +.idea +.project diff --git a/node_modules/qrcodejs2/LICENSE b/node_modules/qrcodejs2/LICENSE new file mode 100644 index 0000000..93c3323 --- /dev/null +++ b/node_modules/qrcodejs2/LICENSE @@ -0,0 +1,14 @@ +The MIT License (MIT) +--------------------- +Copyright (c) 2012 davidshimjs + +Permission is hereby granted, free of charge, +to any person obtaining a copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, +including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/node_modules/qrcodejs2/README.md b/node_modules/qrcodejs2/README.md new file mode 100644 index 0000000..ce0da3b --- /dev/null +++ b/node_modules/qrcodejs2/README.md @@ -0,0 +1,43 @@ +# QRCode.js +QRCode.js is javascript library for making QRCode. QRCode.js supports Cross-browser with HTML5 Canvas and table tag in DOM. +QRCode.js has no dependencies. + +## Basic Usages +``` +
+ +``` + +or with some options + +``` +var qrcode = new QRCode("test", { + text: "http://jindo.dev.naver.com/collie", + width: 128, + height: 128, + colorDark : "#000000", + colorLight : "#ffffff", + correctLevel : QRCode.CorrectLevel.H +}); +``` + +and you can use some methods + +``` +qrcode.clear(); // clear the code. +qrcode.makeCode("http://naver.com"); // make another code. +``` + +## Browser Compatibility +IE6~10, Chrome, Firefox, Safari, Opera, Mobile Safari, Android, Windows Mobile, ETC. + +## License +MIT License + +## Contact +twitter @davidshimjs + +[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/davidshimjs/qrcodejs/trend.png)](https://bitdeli.com/free "Bitdeli Badge") + diff --git a/node_modules/qrcodejs2/bower.json b/node_modules/qrcodejs2/bower.json new file mode 100644 index 0000000..0509be6 --- /dev/null +++ b/node_modules/qrcodejs2/bower.json @@ -0,0 +1,18 @@ +{ + "name": "qrcode.js", + "version": "0.0.1", + "homepage": "https://github.com/davidshimjs/qrcodejs", + "authors": [ + "Sangmin Shim", "Sangmin Shim (http://jaguarjs.com)" + ], + "description": "Cross-browser QRCode generator for javascript", + "main": "qrcode.js", + "ignore": [ + "bower_components", + "node_modules", + "index.html", + "index.svg", + "jquery.min.js", + "qrcode.min.js" + ] +} diff --git a/node_modules/qrcodejs2/index-svg.html b/node_modules/qrcodejs2/index-svg.html new file mode 100644 index 0000000..025a135 --- /dev/null +++ b/node_modules/qrcodejs2/index-svg.html @@ -0,0 +1,47 @@ + + + + Cross-Browser QRCode generator for Javascript + + + + + + + + + + + + + diff --git a/node_modules/qrcodejs2/index.html b/node_modules/qrcodejs2/index.html new file mode 100644 index 0000000..fc16f3d --- /dev/null +++ b/node_modules/qrcodejs2/index.html @@ -0,0 +1,44 @@ + + + +Cross-Browser QRCode generator for Javascript + + + + + + +
+
+ + + \ No newline at end of file diff --git a/node_modules/qrcodejs2/index.svg b/node_modules/qrcodejs2/index.svg new file mode 100644 index 0000000..fabe56a --- /dev/null +++ b/node_modules/qrcodejs2/index.svg @@ -0,0 +1,37 @@ + + + + + +
+ +
+ + + +
+
diff --git a/node_modules/qrcodejs2/jquery.min.js b/node_modules/qrcodejs2/jquery.min.js new file mode 100644 index 0000000..2740cc4 --- /dev/null +++ b/node_modules/qrcodejs2/jquery.min.js @@ -0,0 +1,2 @@ +/*! jQuery v1.8.3 jquery.com | jquery.org/license */ +(function(e,t){function _(e){var t=M[e]={};return v.each(e.split(y),function(e,n){t[n]=!0}),t}function H(e,n,r){if(r===t&&e.nodeType===1){var i="data-"+n.replace(P,"-$1").toLowerCase();r=e.getAttribute(i);if(typeof r=="string"){try{r=r==="true"?!0:r==="false"?!1:r==="null"?null:+r+""===r?+r:D.test(r)?v.parseJSON(r):r}catch(s){}v.data(e,n,r)}else r=t}return r}function B(e){var t;for(t in e){if(t==="data"&&v.isEmptyObject(e[t]))continue;if(t!=="toJSON")return!1}return!0}function et(){return!1}function tt(){return!0}function ut(e){return!e||!e.parentNode||e.parentNode.nodeType===11}function at(e,t){do e=e[t];while(e&&e.nodeType!==1);return e}function ft(e,t,n){t=t||0;if(v.isFunction(t))return v.grep(e,function(e,r){var i=!!t.call(e,r,e);return i===n});if(t.nodeType)return v.grep(e,function(e,r){return e===t===n});if(typeof t=="string"){var r=v.grep(e,function(e){return e.nodeType===1});if(it.test(t))return v.filter(t,r,!n);t=v.filter(t,r)}return v.grep(e,function(e,r){return v.inArray(e,t)>=0===n})}function lt(e){var t=ct.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}function Lt(e,t){return e.getElementsByTagName(t)[0]||e.appendChild(e.ownerDocument.createElement(t))}function At(e,t){if(t.nodeType!==1||!v.hasData(e))return;var n,r,i,s=v._data(e),o=v._data(t,s),u=s.events;if(u){delete o.handle,o.events={};for(n in u)for(r=0,i=u[n].length;r").appendTo(i.body),n=t.css("display");t.remove();if(n==="none"||n===""){Pt=i.body.appendChild(Pt||v.extend(i.createElement("iframe"),{frameBorder:0,width:0,height:0}));if(!Ht||!Pt.createElement)Ht=(Pt.contentWindow||Pt.contentDocument).document,Ht.write(""),Ht.close();t=Ht.body.appendChild(Ht.createElement(e)),n=Dt(t,"display"),i.body.removeChild(Pt)}return Wt[e]=n,n}function fn(e,t,n,r){var i;if(v.isArray(t))v.each(t,function(t,i){n||sn.test(e)?r(e,i):fn(e+"["+(typeof i=="object"?t:"")+"]",i,n,r)});else if(!n&&v.type(t)==="object")for(i in t)fn(e+"["+i+"]",t[i],n,r);else r(e,t)}function Cn(e){return function(t,n){typeof t!="string"&&(n=t,t="*");var r,i,s,o=t.toLowerCase().split(y),u=0,a=o.length;if(v.isFunction(n))for(;u)[^>]*$|#([\w\-]*)$)/,E=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,S=/^[\],:{}\s]*$/,x=/(?:^|:|,)(?:\s*\[)+/g,T=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,N=/"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g,C=/^-ms-/,k=/-([\da-z])/gi,L=function(e,t){return(t+"").toUpperCase()},A=function(){i.addEventListener?(i.removeEventListener("DOMContentLoaded",A,!1),v.ready()):i.readyState==="complete"&&(i.detachEvent("onreadystatechange",A),v.ready())},O={};v.fn=v.prototype={constructor:v,init:function(e,n,r){var s,o,u,a;if(!e)return this;if(e.nodeType)return this.context=this[0]=e,this.length=1,this;if(typeof e=="string"){e.charAt(0)==="<"&&e.charAt(e.length-1)===">"&&e.length>=3?s=[null,e,null]:s=w.exec(e);if(s&&(s[1]||!n)){if(s[1])return n=n instanceof v?n[0]:n,a=n&&n.nodeType?n.ownerDocument||n:i,e=v.parseHTML(s[1],a,!0),E.test(s[1])&&v.isPlainObject(n)&&this.attr.call(e,n,!0),v.merge(this,e);o=i.getElementById(s[2]);if(o&&o.parentNode){if(o.id!==s[2])return r.find(e);this.length=1,this[0]=o}return this.context=i,this.selector=e,this}return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e)}return v.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),v.makeArray(e,this))},selector:"",jquery:"1.8.3",length:0,size:function(){return this.length},toArray:function(){return l.call(this)},get:function(e){return e==null?this.toArray():e<0?this[this.length+e]:this[e]},pushStack:function(e,t,n){var r=v.merge(this.constructor(),e);return r.prevObject=this,r.context=this.context,t==="find"?r.selector=this.selector+(this.selector?" ":"")+n:t&&(r.selector=this.selector+"."+t+"("+n+")"),r},each:function(e,t){return v.each(this,e,t)},ready:function(e){return v.ready.promise().done(e),this},eq:function(e){return e=+e,e===-1?this.slice(e):this.slice(e,e+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(l.apply(this,arguments),"slice",l.call(arguments).join(","))},map:function(e){return this.pushStack(v.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:[].sort,splice:[].splice},v.fn.init.prototype=v.fn,v.extend=v.fn.extend=function(){var e,n,r,i,s,o,u=arguments[0]||{},a=1,f=arguments.length,l=!1;typeof u=="boolean"&&(l=u,u=arguments[1]||{},a=2),typeof u!="object"&&!v.isFunction(u)&&(u={}),f===a&&(u=this,--a);for(;a0)return;r.resolveWith(i,[v]),v.fn.trigger&&v(i).trigger("ready").off("ready")},isFunction:function(e){return v.type(e)==="function"},isArray:Array.isArray||function(e){return v.type(e)==="array"},isWindow:function(e){return e!=null&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return e==null?String(e):O[h.call(e)]||"object"},isPlainObject:function(e){if(!e||v.type(e)!=="object"||e.nodeType||v.isWindow(e))return!1;try{if(e.constructor&&!p.call(e,"constructor")&&!p.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}var r;for(r in e);return r===t||p.call(e,r)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw new Error(e)},parseHTML:function(e,t,n){var r;return!e||typeof e!="string"?null:(typeof t=="boolean"&&(n=t,t=0),t=t||i,(r=E.exec(e))?[t.createElement(r[1])]:(r=v.buildFragment([e],t,n?null:[]),v.merge([],(r.cacheable?v.clone(r.fragment):r.fragment).childNodes)))},parseJSON:function(t){if(!t||typeof t!="string")return null;t=v.trim(t);if(e.JSON&&e.JSON.parse)return e.JSON.parse(t);if(S.test(t.replace(T,"@").replace(N,"]").replace(x,"")))return(new Function("return "+t))();v.error("Invalid JSON: "+t)},parseXML:function(n){var r,i;if(!n||typeof n!="string")return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(s){r=t}return(!r||!r.documentElement||r.getElementsByTagName("parsererror").length)&&v.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&g.test(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(C,"ms-").replace(k,L)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,n,r){var i,s=0,o=e.length,u=o===t||v.isFunction(e);if(r){if(u){for(i in e)if(n.apply(e[i],r)===!1)break}else for(;s0&&e[0]&&e[a-1]||a===0||v.isArray(e));if(f)for(;u-1)a.splice(n,1),i&&(n<=o&&o--,n<=u&&u--)}),this},has:function(e){return v.inArray(e,a)>-1},empty:function(){return a=[],this},disable:function(){return a=f=n=t,this},disabled:function(){return!a},lock:function(){return f=t,n||c.disable(),this},locked:function(){return!f},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],a&&(!r||f)&&(i?f.push(t):l(t)),this},fire:function(){return c.fireWith(this,arguments),this},fired:function(){return!!r}};return c},v.extend({Deferred:function(e){var t=[["resolve","done",v.Callbacks("once memory"),"resolved"],["reject","fail",v.Callbacks("once memory"),"rejected"],["notify","progress",v.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return v.Deferred(function(n){v.each(t,function(t,r){var s=r[0],o=e[t];i[r[1]](v.isFunction(o)?function(){var e=o.apply(this,arguments);e&&v.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[s+"With"](this===i?n:this,[e])}:n[s])}),e=null}).promise()},promise:function(e){return e!=null?v.extend(e,r):r}},i={};return r.pipe=r.then,v.each(t,function(e,s){var o=s[2],u=s[3];r[s[1]]=o.add,u&&o.add(function(){n=u},t[e^1][2].disable,t[2][2].lock),i[s[0]]=o.fire,i[s[0]+"With"]=o.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=l.call(arguments),r=n.length,i=r!==1||e&&v.isFunction(e.promise)?r:0,s=i===1?e:v.Deferred(),o=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?l.call(arguments):r,n===u?s.notifyWith(t,n):--i||s.resolveWith(t,n)}},u,a,f;if(r>1){u=new Array(r),a=new Array(r),f=new Array(r);for(;t
a",n=p.getElementsByTagName("*"),r=p.getElementsByTagName("a")[0];if(!n||!r||!n.length)return{};s=i.createElement("select"),o=s.appendChild(i.createElement("option")),u=p.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t={leadingWhitespace:p.firstChild.nodeType===3,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(r.getAttribute("style")),hrefNormalized:r.getAttribute("href")==="/a",opacity:/^0.5/.test(r.style.opacity),cssFloat:!!r.style.cssFloat,checkOn:u.value==="on",optSelected:o.selected,getSetAttribute:p.className!=="t",enctype:!!i.createElement("form").enctype,html5Clone:i.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",boxModel:i.compatMode==="CSS1Compat",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},u.checked=!0,t.noCloneChecked=u.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!o.disabled;try{delete p.test}catch(d){t.deleteExpando=!1}!p.addEventListener&&p.attachEvent&&p.fireEvent&&(p.attachEvent("onclick",h=function(){t.noCloneEvent=!1}),p.cloneNode(!0).fireEvent("onclick"),p.detachEvent("onclick",h)),u=i.createElement("input"),u.value="t",u.setAttribute("type","radio"),t.radioValue=u.value==="t",u.setAttribute("checked","checked"),u.setAttribute("name","t"),p.appendChild(u),a=i.createDocumentFragment(),a.appendChild(p.lastChild),t.checkClone=a.cloneNode(!0).cloneNode(!0).lastChild.checked,t.appendChecked=u.checked,a.removeChild(u),a.appendChild(p);if(p.attachEvent)for(l in{submit:!0,change:!0,focusin:!0})f="on"+l,c=f in p,c||(p.setAttribute(f,"return;"),c=typeof p[f]=="function"),t[l+"Bubbles"]=c;return v(function(){var n,r,s,o,u="padding:0;margin:0;border:0;display:block;overflow:hidden;",a=i.getElementsByTagName("body")[0];if(!a)return;n=i.createElement("div"),n.style.cssText="visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px",a.insertBefore(n,a.firstChild),r=i.createElement("div"),n.appendChild(r),r.innerHTML="
t
",s=r.getElementsByTagName("td"),s[0].style.cssText="padding:0;margin:0;border:0;display:none",c=s[0].offsetHeight===0,s[0].style.display="",s[1].style.display="none",t.reliableHiddenOffsets=c&&s[0].offsetHeight===0,r.innerHTML="",r.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",t.boxSizing=r.offsetWidth===4,t.doesNotIncludeMarginInBodyOffset=a.offsetTop!==1,e.getComputedStyle&&(t.pixelPosition=(e.getComputedStyle(r,null)||{}).top!=="1%",t.boxSizingReliable=(e.getComputedStyle(r,null)||{width:"4px"}).width==="4px",o=i.createElement("div"),o.style.cssText=r.style.cssText=u,o.style.marginRight=o.style.width="0",r.style.width="1px",r.appendChild(o),t.reliableMarginRight=!parseFloat((e.getComputedStyle(o,null)||{}).marginRight)),typeof r.style.zoom!="undefined"&&(r.innerHTML="",r.style.cssText=u+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=r.offsetWidth===3,r.style.display="block",r.style.overflow="visible",r.innerHTML="
",r.firstChild.style.width="5px",t.shrinkWrapBlocks=r.offsetWidth!==3,n.style.zoom=1),a.removeChild(n),n=r=s=o=null}),a.removeChild(p),n=r=s=o=u=a=p=null,t}();var D=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,P=/([A-Z])/g;v.extend({cache:{},deletedIds:[],uuid:0,expando:"jQuery"+(v.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(e){return e=e.nodeType?v.cache[e[v.expando]]:e[v.expando],!!e&&!B(e)},data:function(e,n,r,i){if(!v.acceptData(e))return;var s,o,u=v.expando,a=typeof n=="string",f=e.nodeType,l=f?v.cache:e,c=f?e[u]:e[u]&&u;if((!c||!l[c]||!i&&!l[c].data)&&a&&r===t)return;c||(f?e[u]=c=v.deletedIds.pop()||v.guid++:c=u),l[c]||(l[c]={},f||(l[c].toJSON=v.noop));if(typeof n=="object"||typeof n=="function")i?l[c]=v.extend(l[c],n):l[c].data=v.extend(l[c].data,n);return s=l[c],i||(s.data||(s.data={}),s=s.data),r!==t&&(s[v.camelCase(n)]=r),a?(o=s[n],o==null&&(o=s[v.camelCase(n)])):o=s,o},removeData:function(e,t,n){if(!v.acceptData(e))return;var r,i,s,o=e.nodeType,u=o?v.cache:e,a=o?e[v.expando]:v.expando;if(!u[a])return;if(t){r=n?u[a]:u[a].data;if(r){v.isArray(t)||(t in r?t=[t]:(t=v.camelCase(t),t in r?t=[t]:t=t.split(" ")));for(i=0,s=t.length;i1,null,!1))},removeData:function(e){return this.each(function(){v.removeData(this,e)})}}),v.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=v._data(e,t),n&&(!r||v.isArray(n)?r=v._data(e,t,v.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=v.queue(e,t),r=n.length,i=n.shift(),s=v._queueHooks(e,t),o=function(){v.dequeue(e,t)};i==="inprogress"&&(i=n.shift(),r--),i&&(t==="fx"&&n.unshift("inprogress"),delete s.stop,i.call(e,o,s)),!r&&s&&s.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return v._data(e,n)||v._data(e,n,{empty:v.Callbacks("once memory").add(function(){v.removeData(e,t+"queue",!0),v.removeData(e,n,!0)})})}}),v.fn.extend({queue:function(e,n){var r=2;return typeof e!="string"&&(n=e,e="fx",r--),arguments.length1)},removeAttr:function(e){return this.each(function(){v.removeAttr(this,e)})},prop:function(e,t){return v.access(this,v.prop,e,t,arguments.length>1)},removeProp:function(e){return e=v.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,s,o,u;if(v.isFunction(e))return this.each(function(t){v(this).addClass(e.call(this,t,this.className))});if(e&&typeof e=="string"){t=e.split(y);for(n=0,r=this.length;n=0)r=r.replace(" "+n[s]+" "," ");i.className=e?v.trim(r):""}}}return this},toggleClass:function(e,t){var n=typeof e,r=typeof t=="boolean";return v.isFunction(e)?this.each(function(n){v(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if(n==="string"){var i,s=0,o=v(this),u=t,a=e.split(y);while(i=a[s++])u=r?u:!o.hasClass(i),o[u?"addClass":"removeClass"](i)}else if(n==="undefined"||n==="boolean")this.className&&v._data(this,"__className__",this.className),this.className=this.className||e===!1?"":v._data(this,"__className__")||""})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;n=0)return!0;return!1},val:function(e){var n,r,i,s=this[0];if(!arguments.length){if(s)return n=v.valHooks[s.type]||v.valHooks[s.nodeName.toLowerCase()],n&&"get"in n&&(r=n.get(s,"value"))!==t?r:(r=s.value,typeof r=="string"?r.replace(R,""):r==null?"":r);return}return i=v.isFunction(e),this.each(function(r){var s,o=v(this);if(this.nodeType!==1)return;i?s=e.call(this,r,o.val()):s=e,s==null?s="":typeof s=="number"?s+="":v.isArray(s)&&(s=v.map(s,function(e){return e==null?"":e+""})),n=v.valHooks[this.type]||v.valHooks[this.nodeName.toLowerCase()];if(!n||!("set"in n)||n.set(this,s,"value")===t)this.value=s})}}),v.extend({valHooks:{option:{get:function(e){var t=e.attributes.value;return!t||t.specified?e.value:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,s=e.type==="select-one"||i<0,o=s?null:[],u=s?i+1:r.length,a=i<0?u:s?i:0;for(;a=0}),n.length||(e.selectedIndex=-1),n}}},attrFn:{},attr:function(e,n,r,i){var s,o,u,a=e.nodeType;if(!e||a===3||a===8||a===2)return;if(i&&v.isFunction(v.fn[n]))return v(e)[n](r);if(typeof e.getAttribute=="undefined")return v.prop(e,n,r);u=a!==1||!v.isXMLDoc(e),u&&(n=n.toLowerCase(),o=v.attrHooks[n]||(X.test(n)?F:j));if(r!==t){if(r===null){v.removeAttr(e,n);return}return o&&"set"in o&&u&&(s=o.set(e,r,n))!==t?s:(e.setAttribute(n,r+""),r)}return o&&"get"in o&&u&&(s=o.get(e,n))!==null?s:(s=e.getAttribute(n),s===null?t:s)},removeAttr:function(e,t){var n,r,i,s,o=0;if(t&&e.nodeType===1){r=t.split(y);for(;o=0}})});var $=/^(?:textarea|input|select)$/i,J=/^([^\.]*|)(?:\.(.+)|)$/,K=/(?:^|\s)hover(\.\S+|)\b/,Q=/^key/,G=/^(?:mouse|contextmenu)|click/,Y=/^(?:focusinfocus|focusoutblur)$/,Z=function(e){return v.event.special.hover?e:e.replace(K,"mouseenter$1 mouseleave$1")};v.event={add:function(e,n,r,i,s){var o,u,a,f,l,c,h,p,d,m,g;if(e.nodeType===3||e.nodeType===8||!n||!r||!(o=v._data(e)))return;r.handler&&(d=r,r=d.handler,s=d.selector),r.guid||(r.guid=v.guid++),a=o.events,a||(o.events=a={}),u=o.handle,u||(o.handle=u=function(e){return typeof v=="undefined"||!!e&&v.event.triggered===e.type?t:v.event.dispatch.apply(u.elem,arguments)},u.elem=e),n=v.trim(Z(n)).split(" ");for(f=0;f=0&&(y=y.slice(0,-1),a=!0),y.indexOf(".")>=0&&(b=y.split("."),y=b.shift(),b.sort());if((!s||v.event.customEvent[y])&&!v.event.global[y])return;n=typeof n=="object"?n[v.expando]?n:new v.Event(y,n):new v.Event(y),n.type=y,n.isTrigger=!0,n.exclusive=a,n.namespace=b.join("."),n.namespace_re=n.namespace?new RegExp("(^|\\.)"+b.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,h=y.indexOf(":")<0?"on"+y:"";if(!s){u=v.cache;for(f in u)u[f].events&&u[f].events[y]&&v.event.trigger(n,r,u[f].handle.elem,!0);return}n.result=t,n.target||(n.target=s),r=r!=null?v.makeArray(r):[],r.unshift(n),p=v.event.special[y]||{};if(p.trigger&&p.trigger.apply(s,r)===!1)return;m=[[s,p.bindType||y]];if(!o&&!p.noBubble&&!v.isWindow(s)){g=p.delegateType||y,l=Y.test(g+y)?s:s.parentNode;for(c=s;l;l=l.parentNode)m.push([l,g]),c=l;c===(s.ownerDocument||i)&&m.push([c.defaultView||c.parentWindow||e,g])}for(f=0;f=0:v.find(h,this,null,[s]).length),u[h]&&f.push(c);f.length&&w.push({elem:s,matches:f})}d.length>m&&w.push({elem:this,matches:d.slice(m)});for(r=0;r0?this.on(t,null,e,n):this.trigger(t)},Q.test(t)&&(v.event.fixHooks[t]=v.event.keyHooks),G.test(t)&&(v.event.fixHooks[t]=v.event.mouseHooks)}),function(e,t){function nt(e,t,n,r){n=n||[],t=t||g;var i,s,a,f,l=t.nodeType;if(!e||typeof e!="string")return n;if(l!==1&&l!==9)return[];a=o(t);if(!a&&!r)if(i=R.exec(e))if(f=i[1]){if(l===9){s=t.getElementById(f);if(!s||!s.parentNode)return n;if(s.id===f)return n.push(s),n}else if(t.ownerDocument&&(s=t.ownerDocument.getElementById(f))&&u(t,s)&&s.id===f)return n.push(s),n}else{if(i[2])return S.apply(n,x.call(t.getElementsByTagName(e),0)),n;if((f=i[3])&&Z&&t.getElementsByClassName)return S.apply(n,x.call(t.getElementsByClassName(f),0)),n}return vt(e.replace(j,"$1"),t,n,r,a)}function rt(e){return function(t){var n=t.nodeName.toLowerCase();return n==="input"&&t.type===e}}function it(e){return function(t){var n=t.nodeName.toLowerCase();return(n==="input"||n==="button")&&t.type===e}}function st(e){return N(function(t){return t=+t,N(function(n,r){var i,s=e([],n.length,t),o=s.length;while(o--)n[i=s[o]]&&(n[i]=!(r[i]=n[i]))})})}function ot(e,t,n){if(e===t)return n;var r=e.nextSibling;while(r){if(r===t)return-1;r=r.nextSibling}return 1}function ut(e,t){var n,r,s,o,u,a,f,l=L[d][e+" "];if(l)return t?0:l.slice(0);u=e,a=[],f=i.preFilter;while(u){if(!n||(r=F.exec(u)))r&&(u=u.slice(r[0].length)||u),a.push(s=[]);n=!1;if(r=I.exec(u))s.push(n=new m(r.shift())),u=u.slice(n.length),n.type=r[0].replace(j," ");for(o in i.filter)(r=J[o].exec(u))&&(!f[o]||(r=f[o](r)))&&(s.push(n=new m(r.shift())),u=u.slice(n.length),n.type=o,n.matches=r);if(!n)break}return t?u.length:u?nt.error(e):L(e,a).slice(0)}function at(e,t,r){var i=t.dir,s=r&&t.dir==="parentNode",o=w++;return t.first?function(t,n,r){while(t=t[i])if(s||t.nodeType===1)return e(t,n,r)}:function(t,r,u){if(!u){var a,f=b+" "+o+" ",l=f+n;while(t=t[i])if(s||t.nodeType===1){if((a=t[d])===l)return t.sizset;if(typeof a=="string"&&a.indexOf(f)===0){if(t.sizset)return t}else{t[d]=l;if(e(t,r,u))return t.sizset=!0,t;t.sizset=!1}}}else while(t=t[i])if(s||t.nodeType===1)if(e(t,r,u))return t}}function ft(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function lt(e,t,n,r,i){var s,o=[],u=0,a=e.length,f=t!=null;for(;u-1&&(s[f]=!(o[f]=c))}}else g=lt(g===o?g.splice(d,g.length):g),i?i(null,o,g,a):S.apply(o,g)})}function ht(e){var t,n,r,s=e.length,o=i.relative[e[0].type],u=o||i.relative[" "],a=o?1:0,f=at(function(e){return e===t},u,!0),l=at(function(e){return T.call(t,e)>-1},u,!0),h=[function(e,n,r){return!o&&(r||n!==c)||((t=n).nodeType?f(e,n,r):l(e,n,r))}];for(;a1&&ft(h),a>1&&e.slice(0,a-1).join("").replace(j,"$1"),n,a0,s=e.length>0,o=function(u,a,f,l,h){var p,d,v,m=[],y=0,w="0",x=u&&[],T=h!=null,N=c,C=u||s&&i.find.TAG("*",h&&a.parentNode||a),k=b+=N==null?1:Math.E;T&&(c=a!==g&&a,n=o.el);for(;(p=C[w])!=null;w++){if(s&&p){for(d=0;v=e[d];d++)if(v(p,a,f)){l.push(p);break}T&&(b=k,n=++o.el)}r&&((p=!v&&p)&&y--,u&&x.push(p))}y+=w;if(r&&w!==y){for(d=0;v=t[d];d++)v(x,m,a,f);if(u){if(y>0)while(w--)!x[w]&&!m[w]&&(m[w]=E.call(l));m=lt(m)}S.apply(l,m),T&&!u&&m.length>0&&y+t.length>1&&nt.uniqueSort(l)}return T&&(b=k,c=N),x};return o.el=0,r?N(o):o}function dt(e,t,n){var r=0,i=t.length;for(;r2&&(f=u[0]).type==="ID"&&t.nodeType===9&&!s&&i.relative[u[1].type]){t=i.find.ID(f.matches[0].replace($,""),t,s)[0];if(!t)return n;e=e.slice(u.shift().length)}for(o=J.POS.test(e)?-1:u.length-1;o>=0;o--){f=u[o];if(i.relative[l=f.type])break;if(c=i.find[l])if(r=c(f.matches[0].replace($,""),z.test(u[0].type)&&t.parentNode||t,s)){u.splice(o,1),e=r.length&&u.join("");if(!e)return S.apply(n,x.call(r,0)),n;break}}}return a(e,h)(r,t,s,n,z.test(e)),n}function mt(){}var n,r,i,s,o,u,a,f,l,c,h=!0,p="undefined",d=("sizcache"+Math.random()).replace(".",""),m=String,g=e.document,y=g.documentElement,b=0,w=0,E=[].pop,S=[].push,x=[].slice,T=[].indexOf||function(e){var t=0,n=this.length;for(;ti.cacheLength&&delete e[t.shift()],e[n+" "]=r},e)},k=C(),L=C(),A=C(),O="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",_=M.replace("w","w#"),D="([*^$|!~]?=)",P="\\["+O+"*("+M+")"+O+"*(?:"+D+O+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+_+")|)|)"+O+"*\\]",H=":("+M+")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:"+P+")|[^:]|\\\\.)*|.*))\\)|)",B=":(even|odd|eq|gt|lt|nth|first|last)(?:\\("+O+"*((?:-\\d)?\\d*)"+O+"*\\)|)(?=[^-]|$)",j=new RegExp("^"+O+"+|((?:^|[^\\\\])(?:\\\\.)*)"+O+"+$","g"),F=new RegExp("^"+O+"*,"+O+"*"),I=new RegExp("^"+O+"*([\\x20\\t\\r\\n\\f>+~])"+O+"*"),q=new RegExp(H),R=/^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,U=/^:not/,z=/[\x20\t\r\n\f]*[+~]/,W=/:not\($/,X=/h\d/i,V=/input|select|textarea|button/i,$=/\\(?!\\)/g,J={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),NAME:new RegExp("^\\[name=['\"]?("+M+")['\"]?\\]"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+P),PSEUDO:new RegExp("^"+H),POS:new RegExp(B,"i"),CHILD:new RegExp("^:(only|nth|first|last)-child(?:\\("+O+"*(even|odd|(([+-]|)(\\d*)n|)"+O+"*(?:([+-]|)"+O+"*(\\d+)|))"+O+"*\\)|)","i"),needsContext:new RegExp("^"+O+"*[>+~]|"+B,"i")},K=function(e){var t=g.createElement("div");try{return e(t)}catch(n){return!1}finally{t=null}},Q=K(function(e){return e.appendChild(g.createComment("")),!e.getElementsByTagName("*").length}),G=K(function(e){return e.innerHTML="",e.firstChild&&typeof e.firstChild.getAttribute!==p&&e.firstChild.getAttribute("href")==="#"}),Y=K(function(e){e.innerHTML="";var t=typeof e.lastChild.getAttribute("multiple");return t!=="boolean"&&t!=="string"}),Z=K(function(e){return e.innerHTML="",!e.getElementsByClassName||!e.getElementsByClassName("e").length?!1:(e.lastChild.className="e",e.getElementsByClassName("e").length===2)}),et=K(function(e){e.id=d+0,e.innerHTML="
",y.insertBefore(e,y.firstChild);var t=g.getElementsByName&&g.getElementsByName(d).length===2+g.getElementsByName(d+0).length;return r=!g.getElementById(d),y.removeChild(e),t});try{x.call(y.childNodes,0)[0].nodeType}catch(tt){x=function(e){var t,n=[];for(;t=this[e];e++)n.push(t);return n}}nt.matches=function(e,t){return nt(e,null,null,t)},nt.matchesSelector=function(e,t){return nt(t,null,null,[e]).length>0},s=nt.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(i===1||i===9||i===11){if(typeof e.textContent=="string")return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=s(e)}else if(i===3||i===4)return e.nodeValue}else for(;t=e[r];r++)n+=s(t);return n},o=nt.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?t.nodeName!=="HTML":!1},u=nt.contains=y.contains?function(e,t){var n=e.nodeType===9?e.documentElement:e,r=t&&t.parentNode;return e===r||!!(r&&r.nodeType===1&&n.contains&&n.contains(r))}:y.compareDocumentPosition?function(e,t){return t&&!!(e.compareDocumentPosition(t)&16)}:function(e,t){while(t=t.parentNode)if(t===e)return!0;return!1},nt.attr=function(e,t){var n,r=o(e);return r||(t=t.toLowerCase()),(n=i.attrHandle[t])?n(e):r||Y?e.getAttribute(t):(n=e.getAttributeNode(t),n?typeof e[t]=="boolean"?e[t]?t:null:n.specified?n.value:null:null)},i=nt.selectors={cacheLength:50,createPseudo:N,match:J,attrHandle:G?{}:{href:function(e){return e.getAttribute("href",2)},type:function(e){return e.getAttribute("type")}},find:{ID:r?function(e,t,n){if(typeof t.getElementById!==p&&!n){var r=t.getElementById(e);return r&&r.parentNode?[r]:[]}}:function(e,n,r){if(typeof n.getElementById!==p&&!r){var i=n.getElementById(e);return i?i.id===e||typeof i.getAttributeNode!==p&&i.getAttributeNode("id").value===e?[i]:t:[]}},TAG:Q?function(e,t){if(typeof t.getElementsByTagName!==p)return t.getElementsByTagName(e)}:function(e,t){var n=t.getElementsByTagName(e);if(e==="*"){var r,i=[],s=0;for(;r=n[s];s++)r.nodeType===1&&i.push(r);return i}return n},NAME:et&&function(e,t){if(typeof t.getElementsByName!==p)return t.getElementsByName(name)},CLASS:Z&&function(e,t,n){if(typeof t.getElementsByClassName!==p&&!n)return t.getElementsByClassName(e)}},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace($,""),e[3]=(e[4]||e[5]||"").replace($,""),e[2]==="~="&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),e[1]==="nth"?(e[2]||nt.error(e[0]),e[3]=+(e[3]?e[4]+(e[5]||1):2*(e[2]==="even"||e[2]==="odd")),e[4]=+(e[6]+e[7]||e[2]==="odd")):e[2]&&nt.error(e[0]),e},PSEUDO:function(e){var t,n;if(J.CHILD.test(e[0]))return null;if(e[3])e[2]=e[3];else if(t=e[4])q.test(t)&&(n=ut(t,!0))&&(n=t.indexOf(")",t.length-n)-t.length)&&(t=t.slice(0,n),e[0]=e[0].slice(0,n)),e[2]=t;return e.slice(0,3)}},filter:{ID:r?function(e){return e=e.replace($,""),function(t){return t.getAttribute("id")===e}}:function(e){return e=e.replace($,""),function(t){var n=typeof t.getAttributeNode!==p&&t.getAttributeNode("id");return n&&n.value===e}},TAG:function(e){return e==="*"?function(){return!0}:(e=e.replace($,"").toLowerCase(),function(t){return t.nodeName&&t.nodeName.toLowerCase()===e})},CLASS:function(e){var t=k[d][e+" "];return t||(t=new RegExp("(^|"+O+")"+e+"("+O+"|$)"))&&k(e,function(e){return t.test(e.className||typeof e.getAttribute!==p&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r,i){var s=nt.attr(r,e);return s==null?t==="!=":t?(s+="",t==="="?s===n:t==="!="?s!==n:t==="^="?n&&s.indexOf(n)===0:t==="*="?n&&s.indexOf(n)>-1:t==="$="?n&&s.substr(s.length-n.length)===n:t==="~="?(" "+s+" ").indexOf(n)>-1:t==="|="?s===n||s.substr(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r){return e==="nth"?function(e){var t,i,s=e.parentNode;if(n===1&&r===0)return!0;if(s){i=0;for(t=s.firstChild;t;t=t.nextSibling)if(t.nodeType===1){i++;if(e===t)break}}return i-=r,i===n||i%n===0&&i/n>=0}:function(t){var n=t;switch(e){case"only":case"first":while(n=n.previousSibling)if(n.nodeType===1)return!1;if(e==="first")return!0;n=t;case"last":while(n=n.nextSibling)if(n.nodeType===1)return!1;return!0}}},PSEUDO:function(e,t){var n,r=i.pseudos[e]||i.setFilters[e.toLowerCase()]||nt.error("unsupported pseudo: "+e);return r[d]?r(t):r.length>1?(n=[e,e,"",t],i.setFilters.hasOwnProperty(e.toLowerCase())?N(function(e,n){var i,s=r(e,t),o=s.length;while(o--)i=T.call(e,s[o]),e[i]=!(n[i]=s[o])}):function(e){return r(e,0,n)}):r}},pseudos:{not:N(function(e){var t=[],n=[],r=a(e.replace(j,"$1"));return r[d]?N(function(e,t,n,i){var s,o=r(e,null,i,[]),u=e.length;while(u--)if(s=o[u])e[u]=!(t[u]=s)}):function(e,i,s){return t[0]=e,r(t,null,s,n),!n.pop()}}),has:N(function(e){return function(t){return nt(e,t).length>0}}),contains:N(function(e){return function(t){return(t.textContent||t.innerText||s(t)).indexOf(e)>-1}}),enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return t==="input"&&!!e.checked||t==="option"&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},parent:function(e){return!i.pseudos.empty(e)},empty:function(e){var t;e=e.firstChild;while(e){if(e.nodeName>"@"||(t=e.nodeType)===3||t===4)return!1;e=e.nextSibling}return!0},header:function(e){return X.test(e.nodeName)},text:function(e){var t,n;return e.nodeName.toLowerCase()==="input"&&(t=e.type)==="text"&&((n=e.getAttribute("type"))==null||n.toLowerCase()===t)},radio:rt("radio"),checkbox:rt("checkbox"),file:rt("file"),password:rt("password"),image:rt("image"),submit:it("submit"),reset:it("reset"),button:function(e){var t=e.nodeName.toLowerCase();return t==="input"&&e.type==="button"||t==="button"},input:function(e){return V.test(e.nodeName)},focus:function(e){var t=e.ownerDocument;return e===t.activeElement&&(!t.hasFocus||t.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},active:function(e){return e===e.ownerDocument.activeElement},first:st(function(){return[0]}),last:st(function(e,t){return[t-1]}),eq:st(function(e,t,n){return[n<0?n+t:n]}),even:st(function(e,t){for(var n=0;n=0;)e.push(r);return e}),gt:st(function(e,t,n){for(var r=n<0?n+t:n;++r",e.querySelectorAll("[selected]").length||i.push("\\["+O+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),e.querySelectorAll(":checked").length||i.push(":checked")}),K(function(e){e.innerHTML="

",e.querySelectorAll("[test^='']").length&&i.push("[*^$]="+O+"*(?:\"\"|'')"),e.innerHTML="",e.querySelectorAll(":enabled").length||i.push(":enabled",":disabled")}),i=new RegExp(i.join("|")),vt=function(e,r,s,o,u){if(!o&&!u&&!i.test(e)){var a,f,l=!0,c=d,h=r,p=r.nodeType===9&&e;if(r.nodeType===1&&r.nodeName.toLowerCase()!=="object"){a=ut(e),(l=r.getAttribute("id"))?c=l.replace(n,"\\$&"):r.setAttribute("id",c),c="[id='"+c+"'] ",f=a.length;while(f--)a[f]=c+a[f].join("");h=z.test(e)&&r.parentNode||r,p=a.join(",")}if(p)try{return S.apply(s,x.call(h.querySelectorAll(p),0)),s}catch(v){}finally{l||r.removeAttribute("id")}}return t(e,r,s,o,u)},u&&(K(function(t){e=u.call(t,"div");try{u.call(t,"[test!='']:sizzle"),s.push("!=",H)}catch(n){}}),s=new RegExp(s.join("|")),nt.matchesSelector=function(t,n){n=n.replace(r,"='$1']");if(!o(t)&&!s.test(n)&&!i.test(n))try{var a=u.call(t,n);if(a||e||t.document&&t.document.nodeType!==11)return a}catch(f){}return nt(n,null,null,[t]).length>0})}(),i.pseudos.nth=i.pseudos.eq,i.filters=mt.prototype=i.pseudos,i.setFilters=new mt,nt.attr=v.attr,v.find=nt,v.expr=nt.selectors,v.expr[":"]=v.expr.pseudos,v.unique=nt.uniqueSort,v.text=nt.getText,v.isXMLDoc=nt.isXML,v.contains=nt.contains}(e);var nt=/Until$/,rt=/^(?:parents|prev(?:Until|All))/,it=/^.[^:#\[\.,]*$/,st=v.expr.match.needsContext,ot={children:!0,contents:!0,next:!0,prev:!0};v.fn.extend({find:function(e){var t,n,r,i,s,o,u=this;if(typeof e!="string")return v(e).filter(function(){for(t=0,n=u.length;t0)for(i=r;i=0:v.filter(e,this).length>0:this.filter(e).length>0)},closest:function(e,t){var n,r=0,i=this.length,s=[],o=st.test(e)||typeof e!="string"?v(e,t||this.context):0;for(;r-1:v.find.matchesSelector(n,e)){s.push(n);break}n=n.parentNode}}return s=s.length>1?v.unique(s):s,this.pushStack(s,"closest",e)},index:function(e){return e?typeof e=="string"?v.inArray(this[0],v(e)):v.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.prevAll().length:-1},add:function(e,t){var n=typeof e=="string"?v(e,t):v.makeArray(e&&e.nodeType?[e]:e),r=v.merge(this.get(),n);return this.pushStack(ut(n[0])||ut(r[0])?r:v.unique(r))},addBack:function(e){return this.add(e==null?this.prevObject:this.prevObject.filter(e))}}),v.fn.andSelf=v.fn.addBack,v.each({parent:function(e){var t=e.parentNode;return t&&t.nodeType!==11?t:null},parents:function(e){return v.dir(e,"parentNode")},parentsUntil:function(e,t,n){return v.dir(e,"parentNode",n)},next:function(e){return at(e,"nextSibling")},prev:function(e){return at(e,"previousSibling")},nextAll:function(e){return v.dir(e,"nextSibling")},prevAll:function(e){return v.dir(e,"previousSibling")},nextUntil:function(e,t,n){return v.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return v.dir(e,"previousSibling",n)},siblings:function(e){return v.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return v.sibling(e.firstChild)},contents:function(e){return v.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:v.merge([],e.childNodes)}},function(e,t){v.fn[e]=function(n,r){var i=v.map(this,t,n);return nt.test(e)||(r=n),r&&typeof r=="string"&&(i=v.filter(r,i)),i=this.length>1&&!ot[e]?v.unique(i):i,this.length>1&&rt.test(e)&&(i=i.reverse()),this.pushStack(i,e,l.call(arguments).join(","))}}),v.extend({filter:function(e,t,n){return n&&(e=":not("+e+")"),t.length===1?v.find.matchesSelector(t[0],e)?[t[0]]:[]:v.find.matches(e,t)},dir:function(e,n,r){var i=[],s=e[n];while(s&&s.nodeType!==9&&(r===t||s.nodeType!==1||!v(s).is(r)))s.nodeType===1&&i.push(s),s=s[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)e.nodeType===1&&e!==t&&n.push(e);return n}});var ct="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",ht=/ jQuery\d+="(?:null|\d+)"/g,pt=/^\s+/,dt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,vt=/<([\w:]+)/,mt=/]","i"),Et=/^(?:checkbox|radio)$/,St=/checked\s*(?:[^=]|=\s*.checked.)/i,xt=/\/(java|ecma)script/i,Tt=/^\s*\s*$/g,Nt={option:[1,""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]},Ct=lt(i),kt=Ct.appendChild(i.createElement("div"));Nt.optgroup=Nt.option,Nt.tbody=Nt.tfoot=Nt.colgroup=Nt.caption=Nt.thead,Nt.th=Nt.td,v.support.htmlSerialize||(Nt._default=[1,"X
","
"]),v.fn.extend({text:function(e){return v.access(this,function(e){return e===t?v.text(this):this.empty().append((this[0]&&this[0].ownerDocument||i).createTextNode(e))},null,e,arguments.length)},wrapAll:function(e){if(v.isFunction(e))return this.each(function(t){v(this).wrapAll(e.call(this,t))});if(this[0]){var t=v(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&e.firstChild.nodeType===1)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return v.isFunction(e)?this.each(function(t){v(this).wrapInner(e.call(this,t))}):this.each(function(){var t=v(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=v.isFunction(e);return this.each(function(n){v(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){v.nodeName(this,"body")||v(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(e){(this.nodeType===1||this.nodeType===11)&&this.appendChild(e)})},prepend:function(){return this.domManip(arguments,!0,function(e){(this.nodeType===1||this.nodeType===11)&&this.insertBefore(e,this.firstChild)})},before:function(){if(!ut(this[0]))return this.domManip(arguments,!1,function(e){this.parentNode.insertBefore(e,this)});if(arguments.length){var e=v.clean(arguments);return this.pushStack(v.merge(e,this),"before",this.selector)}},after:function(){if(!ut(this[0]))return this.domManip(arguments,!1,function(e){this.parentNode.insertBefore(e,this.nextSibling)});if(arguments.length){var e=v.clean(arguments);return this.pushStack(v.merge(this,e),"after",this.selector)}},remove:function(e,t){var n,r=0;for(;(n=this[r])!=null;r++)if(!e||v.filter(e,[n]).length)!t&&n.nodeType===1&&(v.cleanData(n.getElementsByTagName("*")),v.cleanData([n])),n.parentNode&&n.parentNode.removeChild(n);return this},empty:function(){var e,t=0;for(;(e=this[t])!=null;t++){e.nodeType===1&&v.cleanData(e.getElementsByTagName("*"));while(e.firstChild)e.removeChild(e.firstChild)}return this},clone:function(e,t){return e=e==null?!1:e,t=t==null?e:t,this.map(function(){return v.clone(this,e,t)})},html:function(e){return v.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return n.nodeType===1?n.innerHTML.replace(ht,""):t;if(typeof e=="string"&&!yt.test(e)&&(v.support.htmlSerialize||!wt.test(e))&&(v.support.leadingWhitespace||!pt.test(e))&&!Nt[(vt.exec(e)||["",""])[1].toLowerCase()]){e=e.replace(dt,"<$1>");try{for(;r1&&typeof f=="string"&&St.test(f))return this.each(function(){v(this).domManip(e,n,r)});if(v.isFunction(f))return this.each(function(i){var s=v(this);e[0]=f.call(this,i,n?s.html():t),s.domManip(e,n,r)});if(this[0]){i=v.buildFragment(e,this,l),o=i.fragment,s=o.firstChild,o.childNodes.length===1&&(o=s);if(s){n=n&&v.nodeName(s,"tr");for(u=i.cacheable||c-1;a0?this.clone(!0):this).get(),v(o[i])[t](r),s=s.concat(r);return this.pushStack(s,e,o.selector)}}),v.extend({clone:function(e,t,n){var r,i,s,o;v.support.html5Clone||v.isXMLDoc(e)||!wt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(kt.innerHTML=e.outerHTML,kt.removeChild(o=kt.firstChild));if((!v.support.noCloneEvent||!v.support.noCloneChecked)&&(e.nodeType===1||e.nodeType===11)&&!v.isXMLDoc(e)){Ot(e,o),r=Mt(e),i=Mt(o);for(s=0;r[s];++s)i[s]&&Ot(r[s],i[s])}if(t){At(e,o);if(n){r=Mt(e),i=Mt(o);for(s=0;r[s];++s)At(r[s],i[s])}}return r=i=null,o},clean:function(e,t,n,r){var s,o,u,a,f,l,c,h,p,d,m,g,y=t===i&&Ct,b=[];if(!t||typeof t.createDocumentFragment=="undefined")t=i;for(s=0;(u=e[s])!=null;s++){typeof u=="number"&&(u+="");if(!u)continue;if(typeof u=="string")if(!gt.test(u))u=t.createTextNode(u);else{y=y||lt(t),c=t.createElement("div"),y.appendChild(c),u=u.replace(dt,"<$1>"),a=(vt.exec(u)||["",""])[1].toLowerCase(),f=Nt[a]||Nt._default,l=f[0],c.innerHTML=f[1]+u+f[2];while(l--)c=c.lastChild;if(!v.support.tbody){h=mt.test(u),p=a==="table"&&!h?c.firstChild&&c.firstChild.childNodes:f[1]===""&&!h?c.childNodes:[];for(o=p.length-1;o>=0;--o)v.nodeName(p[o],"tbody")&&!p[o].childNodes.length&&p[o].parentNode.removeChild(p[o])}!v.support.leadingWhitespace&&pt.test(u)&&c.insertBefore(t.createTextNode(pt.exec(u)[0]),c.firstChild),u=c.childNodes,c.parentNode.removeChild(c)}u.nodeType?b.push(u):v.merge(b,u)}c&&(u=c=y=null);if(!v.support.appendChecked)for(s=0;(u=b[s])!=null;s++)v.nodeName(u,"input")?_t(u):typeof u.getElementsByTagName!="undefined"&&v.grep(u.getElementsByTagName("input"),_t);if(n){m=function(e){if(!e.type||xt.test(e.type))return r?r.push(e.parentNode?e.parentNode.removeChild(e):e):n.appendChild(e)};for(s=0;(u=b[s])!=null;s++)if(!v.nodeName(u,"script")||!m(u))n.appendChild(u),typeof u.getElementsByTagName!="undefined"&&(g=v.grep(v.merge([],u.getElementsByTagName("script")),m),b.splice.apply(b,[s+1,0].concat(g)),s+=g.length)}return b},cleanData:function(e,t){var n,r,i,s,o=0,u=v.expando,a=v.cache,f=v.support.deleteExpando,l=v.event.special;for(;(i=e[o])!=null;o++)if(t||v.acceptData(i)){r=i[u],n=r&&a[r];if(n){if(n.events)for(s in n.events)l[s]?v.event.remove(i,s):v.removeEvent(i,s,n.handle);a[r]&&(delete a[r],f?delete i[u]:i.removeAttribute?i.removeAttribute(u):i[u]=null,v.deletedIds.push(r))}}}}),function(){var e,t;v.uaMatch=function(e){e=e.toLowerCase();var t=/(chrome)[ \/]([\w.]+)/.exec(e)||/(webkit)[ \/]([\w.]+)/.exec(e)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(e)||/(msie) ([\w.]+)/.exec(e)||e.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(e)||[];return{browser:t[1]||"",version:t[2]||"0"}},e=v.uaMatch(o.userAgent),t={},e.browser&&(t[e.browser]=!0,t.version=e.version),t.chrome?t.webkit=!0:t.webkit&&(t.safari=!0),v.browser=t,v.sub=function(){function e(t,n){return new e.fn.init(t,n)}v.extend(!0,e,this),e.superclass=this,e.fn=e.prototype=this(),e.fn.constructor=e,e.sub=this.sub,e.fn.init=function(r,i){return i&&i instanceof v&&!(i instanceof e)&&(i=e(i)),v.fn.init.call(this,r,i,t)},e.fn.init.prototype=e.fn;var t=e(i);return e}}();var Dt,Pt,Ht,Bt=/alpha\([^)]*\)/i,jt=/opacity=([^)]*)/,Ft=/^(top|right|bottom|left)$/,It=/^(none|table(?!-c[ea]).+)/,qt=/^margin/,Rt=new RegExp("^("+m+")(.*)$","i"),Ut=new RegExp("^("+m+")(?!px)[a-z%]+$","i"),zt=new RegExp("^([-+])=("+m+")","i"),Wt={BODY:"block"},Xt={position:"absolute",visibility:"hidden",display:"block"},Vt={letterSpacing:0,fontWeight:400},$t=["Top","Right","Bottom","Left"],Jt=["Webkit","O","Moz","ms"],Kt=v.fn.toggle;v.fn.extend({css:function(e,n){return v.access(this,function(e,n,r){return r!==t?v.style(e,n,r):v.css(e,n)},e,n,arguments.length>1)},show:function(){return Yt(this,!0)},hide:function(){return Yt(this)},toggle:function(e,t){var n=typeof e=="boolean";return v.isFunction(e)&&v.isFunction(t)?Kt.apply(this,arguments):this.each(function(){(n?e:Gt(this))?v(this).show():v(this).hide()})}}),v.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Dt(e,"opacity");return n===""?"1":n}}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":v.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(!e||e.nodeType===3||e.nodeType===8||!e.style)return;var s,o,u,a=v.camelCase(n),f=e.style;n=v.cssProps[a]||(v.cssProps[a]=Qt(f,a)),u=v.cssHooks[n]||v.cssHooks[a];if(r===t)return u&&"get"in u&&(s=u.get(e,!1,i))!==t?s:f[n];o=typeof r,o==="string"&&(s=zt.exec(r))&&(r=(s[1]+1)*s[2]+parseFloat(v.css(e,n)),o="number");if(r==null||o==="number"&&isNaN(r))return;o==="number"&&!v.cssNumber[a]&&(r+="px");if(!u||!("set"in u)||(r=u.set(e,r,i))!==t)try{f[n]=r}catch(l){}},css:function(e,n,r,i){var s,o,u,a=v.camelCase(n);return n=v.cssProps[a]||(v.cssProps[a]=Qt(e.style,a)),u=v.cssHooks[n]||v.cssHooks[a],u&&"get"in u&&(s=u.get(e,!0,i)),s===t&&(s=Dt(e,n)),s==="normal"&&n in Vt&&(s=Vt[n]),r||i!==t?(o=parseFloat(s),r||v.isNumeric(o)?o||0:s):s},swap:function(e,t,n){var r,i,s={};for(i in t)s[i]=e.style[i],e.style[i]=t[i];r=n.call(e);for(i in t)e.style[i]=s[i];return r}}),e.getComputedStyle?Dt=function(t,n){var r,i,s,o,u=e.getComputedStyle(t,null),a=t.style;return u&&(r=u.getPropertyValue(n)||u[n],r===""&&!v.contains(t.ownerDocument,t)&&(r=v.style(t,n)),Ut.test(r)&&qt.test(n)&&(i=a.width,s=a.minWidth,o=a.maxWidth,a.minWidth=a.maxWidth=a.width=r,r=u.width,a.width=i,a.minWidth=s,a.maxWidth=o)),r}:i.documentElement.currentStyle&&(Dt=function(e,t){var n,r,i=e.currentStyle&&e.currentStyle[t],s=e.style;return i==null&&s&&s[t]&&(i=s[t]),Ut.test(i)&&!Ft.test(t)&&(n=s.left,r=e.runtimeStyle&&e.runtimeStyle.left,r&&(e.runtimeStyle.left=e.currentStyle.left),s.left=t==="fontSize"?"1em":i,i=s.pixelLeft+"px",s.left=n,r&&(e.runtimeStyle.left=r)),i===""?"auto":i}),v.each(["height","width"],function(e,t){v.cssHooks[t]={get:function(e,n,r){if(n)return e.offsetWidth===0&&It.test(Dt(e,"display"))?v.swap(e,Xt,function(){return tn(e,t,r)}):tn(e,t,r)},set:function(e,n,r){return Zt(e,n,r?en(e,t,r,v.support.boxSizing&&v.css(e,"boxSizing")==="border-box"):0)}}}),v.support.opacity||(v.cssHooks.opacity={get:function(e,t){return jt.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":t?"1":""},set:function(e,t){var n=e.style,r=e.currentStyle,i=v.isNumeric(t)?"alpha(opacity="+t*100+")":"",s=r&&r.filter||n.filter||"";n.zoom=1;if(t>=1&&v.trim(s.replace(Bt,""))===""&&n.removeAttribute){n.removeAttribute("filter");if(r&&!r.filter)return}n.filter=Bt.test(s)?s.replace(Bt,i):s+" "+i}}),v(function(){v.support.reliableMarginRight||(v.cssHooks.marginRight={get:function(e,t){return v.swap(e,{display:"inline-block"},function(){if(t)return Dt(e,"marginRight")})}}),!v.support.pixelPosition&&v.fn.position&&v.each(["top","left"],function(e,t){v.cssHooks[t]={get:function(e,n){if(n){var r=Dt(e,t);return Ut.test(r)?v(e).position()[t]+"px":r}}}})}),v.expr&&v.expr.filters&&(v.expr.filters.hidden=function(e){return e.offsetWidth===0&&e.offsetHeight===0||!v.support.reliableHiddenOffsets&&(e.style&&e.style.display||Dt(e,"display"))==="none"},v.expr.filters.visible=function(e){return!v.expr.filters.hidden(e)}),v.each({margin:"",padding:"",border:"Width"},function(e,t){v.cssHooks[e+t]={expand:function(n){var r,i=typeof n=="string"?n.split(" "):[n],s={};for(r=0;r<4;r++)s[e+$t[r]+t]=i[r]||i[r-2]||i[0];return s}},qt.test(e)||(v.cssHooks[e+t].set=Zt)});var rn=/%20/g,sn=/\[\]$/,on=/\r?\n/g,un=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,an=/^(?:select|textarea)/i;v.fn.extend({serialize:function(){return v.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?v.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||an.test(this.nodeName)||un.test(this.type))}).map(function(e,t){var n=v(this).val();return n==null?null:v.isArray(n)?v.map(n,function(e,n){return{name:t.name,value:e.replace(on,"\r\n")}}):{name:t.name,value:n.replace(on,"\r\n")}}).get()}}),v.param=function(e,n){var r,i=[],s=function(e,t){t=v.isFunction(t)?t():t==null?"":t,i[i.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};n===t&&(n=v.ajaxSettings&&v.ajaxSettings.traditional);if(v.isArray(e)||e.jquery&&!v.isPlainObject(e))v.each(e,function(){s(this.name,this.value)});else for(r in e)fn(r,e[r],n,s);return i.join("&").replace(rn,"+")};var ln,cn,hn=/#.*$/,pn=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,dn=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,vn=/^(?:GET|HEAD)$/,mn=/^\/\//,gn=/\?/,yn=/)<[^<]*)*<\/script>/gi,bn=/([?&])_=[^&]*/,wn=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,En=v.fn.load,Sn={},xn={},Tn=["*/"]+["*"];try{cn=s.href}catch(Nn){cn=i.createElement("a"),cn.href="",cn=cn.href}ln=wn.exec(cn.toLowerCase())||[],v.fn.load=function(e,n,r){if(typeof e!="string"&&En)return En.apply(this,arguments);if(!this.length)return this;var i,s,o,u=this,a=e.indexOf(" ");return a>=0&&(i=e.slice(a,e.length),e=e.slice(0,a)),v.isFunction(n)?(r=n,n=t):n&&typeof n=="object"&&(s="POST"),v.ajax({url:e,type:s,dataType:"html",data:n,complete:function(e,t){r&&u.each(r,o||[e.responseText,t,e])}}).done(function(e){o=arguments,u.html(i?v("
").append(e.replace(yn,"")).find(i):e)}),this},v.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(e,t){v.fn[t]=function(e){return this.on(t,e)}}),v.each(["get","post"],function(e,n){v[n]=function(e,r,i,s){return v.isFunction(r)&&(s=s||i,i=r,r=t),v.ajax({type:n,url:e,data:r,success:i,dataType:s})}}),v.extend({getScript:function(e,n){return v.get(e,t,n,"script")},getJSON:function(e,t,n){return v.get(e,t,n,"json")},ajaxSetup:function(e,t){return t?Ln(e,v.ajaxSettings):(t=e,e=v.ajaxSettings),Ln(e,t),e},ajaxSettings:{url:cn,isLocal:dn.test(ln[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":Tn},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":e.String,"text html":!0,"text json":v.parseJSON,"text xml":v.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:Cn(Sn),ajaxTransport:Cn(xn),ajax:function(e,n){function T(e,n,s,a){var l,y,b,w,S,T=n;if(E===2)return;E=2,u&&clearTimeout(u),o=t,i=a||"",x.readyState=e>0?4:0,s&&(w=An(c,x,s));if(e>=200&&e<300||e===304)c.ifModified&&(S=x.getResponseHeader("Last-Modified"),S&&(v.lastModified[r]=S),S=x.getResponseHeader("Etag"),S&&(v.etag[r]=S)),e===304?(T="notmodified",l=!0):(l=On(c,w),T=l.state,y=l.data,b=l.error,l=!b);else{b=T;if(!T||e)T="error",e<0&&(e=0)}x.status=e,x.statusText=(n||T)+"",l?d.resolveWith(h,[y,T,x]):d.rejectWith(h,[x,T,b]),x.statusCode(g),g=t,f&&p.trigger("ajax"+(l?"Success":"Error"),[x,c,l?y:b]),m.fireWith(h,[x,T]),f&&(p.trigger("ajaxComplete",[x,c]),--v.active||v.event.trigger("ajaxStop"))}typeof e=="object"&&(n=e,e=t),n=n||{};var r,i,s,o,u,a,f,l,c=v.ajaxSetup({},n),h=c.context||c,p=h!==c&&(h.nodeType||h instanceof v)?v(h):v.event,d=v.Deferred(),m=v.Callbacks("once memory"),g=c.statusCode||{},b={},w={},E=0,S="canceled",x={readyState:0,setRequestHeader:function(e,t){if(!E){var n=e.toLowerCase();e=w[n]=w[n]||e,b[e]=t}return this},getAllResponseHeaders:function(){return E===2?i:null},getResponseHeader:function(e){var n;if(E===2){if(!s){s={};while(n=pn.exec(i))s[n[1].toLowerCase()]=n[2]}n=s[e.toLowerCase()]}return n===t?null:n},overrideMimeType:function(e){return E||(c.mimeType=e),this},abort:function(e){return e=e||S,o&&o.abort(e),T(0,e),this}};d.promise(x),x.success=x.done,x.error=x.fail,x.complete=m.add,x.statusCode=function(e){if(e){var t;if(E<2)for(t in e)g[t]=[g[t],e[t]];else t=e[x.status],x.always(t)}return this},c.url=((e||c.url)+"").replace(hn,"").replace(mn,ln[1]+"//"),c.dataTypes=v.trim(c.dataType||"*").toLowerCase().split(y),c.crossDomain==null&&(a=wn.exec(c.url.toLowerCase()),c.crossDomain=!(!a||a[1]===ln[1]&&a[2]===ln[2]&&(a[3]||(a[1]==="http:"?80:443))==(ln[3]||(ln[1]==="http:"?80:443)))),c.data&&c.processData&&typeof c.data!="string"&&(c.data=v.param(c.data,c.traditional)),kn(Sn,c,n,x);if(E===2)return x;f=c.global,c.type=c.type.toUpperCase(),c.hasContent=!vn.test(c.type),f&&v.active++===0&&v.event.trigger("ajaxStart");if(!c.hasContent){c.data&&(c.url+=(gn.test(c.url)?"&":"?")+c.data,delete c.data),r=c.url;if(c.cache===!1){var N=v.now(),C=c.url.replace(bn,"$1_="+N);c.url=C+(C===c.url?(gn.test(c.url)?"&":"?")+"_="+N:"")}}(c.data&&c.hasContent&&c.contentType!==!1||n.contentType)&&x.setRequestHeader("Content-Type",c.contentType),c.ifModified&&(r=r||c.url,v.lastModified[r]&&x.setRequestHeader("If-Modified-Since",v.lastModified[r]),v.etag[r]&&x.setRequestHeader("If-None-Match",v.etag[r])),x.setRequestHeader("Accept",c.dataTypes[0]&&c.accepts[c.dataTypes[0]]?c.accepts[c.dataTypes[0]]+(c.dataTypes[0]!=="*"?", "+Tn+"; q=0.01":""):c.accepts["*"]);for(l in c.headers)x.setRequestHeader(l,c.headers[l]);if(!c.beforeSend||c.beforeSend.call(h,x,c)!==!1&&E!==2){S="abort";for(l in{success:1,error:1,complete:1})x[l](c[l]);o=kn(xn,c,n,x);if(!o)T(-1,"No Transport");else{x.readyState=1,f&&p.trigger("ajaxSend",[x,c]),c.async&&c.timeout>0&&(u=setTimeout(function(){x.abort("timeout")},c.timeout));try{E=1,o.send(b,T)}catch(k){if(!(E<2))throw k;T(-1,k)}}return x}return x.abort()},active:0,lastModified:{},etag:{}});var Mn=[],_n=/\?/,Dn=/(=)\?(?=&|$)|\?\?/,Pn=v.now();v.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Mn.pop()||v.expando+"_"+Pn++;return this[e]=!0,e}}),v.ajaxPrefilter("json jsonp",function(n,r,i){var s,o,u,a=n.data,f=n.url,l=n.jsonp!==!1,c=l&&Dn.test(f),h=l&&!c&&typeof a=="string"&&!(n.contentType||"").indexOf("application/x-www-form-urlencoded")&&Dn.test(a);if(n.dataTypes[0]==="jsonp"||c||h)return s=n.jsonpCallback=v.isFunction(n.jsonpCallback)?n.jsonpCallback():n.jsonpCallback,o=e[s],c?n.url=f.replace(Dn,"$1"+s):h?n.data=a.replace(Dn,"$1"+s):l&&(n.url+=(_n.test(f)?"&":"?")+n.jsonp+"="+s),n.converters["script json"]=function(){return u||v.error(s+" was not called"),u[0]},n.dataTypes[0]="json",e[s]=function(){u=arguments},i.always(function(){e[s]=o,n[s]&&(n.jsonpCallback=r.jsonpCallback,Mn.push(s)),u&&v.isFunction(o)&&o(u[0]),u=o=t}),"script"}),v.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(e){return v.globalEval(e),e}}}),v.ajaxPrefilter("script",function(e){e.cache===t&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),v.ajaxTransport("script",function(e){if(e.crossDomain){var n,r=i.head||i.getElementsByTagName("head")[0]||i.documentElement;return{send:function(s,o){n=i.createElement("script"),n.async="async",e.scriptCharset&&(n.charset=e.scriptCharset),n.src=e.url,n.onload=n.onreadystatechange=function(e,i){if(i||!n.readyState||/loaded|complete/.test(n.readyState))n.onload=n.onreadystatechange=null,r&&n.parentNode&&r.removeChild(n),n=t,i||o(200,"success")},r.insertBefore(n,r.firstChild)},abort:function(){n&&n.onload(0,1)}}}});var Hn,Bn=e.ActiveXObject?function(){for(var e in Hn)Hn[e](0,1)}:!1,jn=0;v.ajaxSettings.xhr=e.ActiveXObject?function(){return!this.isLocal&&Fn()||In()}:Fn,function(e){v.extend(v.support,{ajax:!!e,cors:!!e&&"withCredentials"in e})}(v.ajaxSettings.xhr()),v.support.ajax&&v.ajaxTransport(function(n){if(!n.crossDomain||v.support.cors){var r;return{send:function(i,s){var o,u,a=n.xhr();n.username?a.open(n.type,n.url,n.async,n.username,n.password):a.open(n.type,n.url,n.async);if(n.xhrFields)for(u in n.xhrFields)a[u]=n.xhrFields[u];n.mimeType&&a.overrideMimeType&&a.overrideMimeType(n.mimeType),!n.crossDomain&&!i["X-Requested-With"]&&(i["X-Requested-With"]="XMLHttpRequest");try{for(u in i)a.setRequestHeader(u,i[u])}catch(f){}a.send(n.hasContent&&n.data||null),r=function(e,i){var u,f,l,c,h;try{if(r&&(i||a.readyState===4)){r=t,o&&(a.onreadystatechange=v.noop,Bn&&delete Hn[o]);if(i)a.readyState!==4&&a.abort();else{u=a.status,l=a.getAllResponseHeaders(),c={},h=a.responseXML,h&&h.documentElement&&(c.xml=h);try{c.text=a.responseText}catch(p){}try{f=a.statusText}catch(p){f=""}!u&&n.isLocal&&!n.crossDomain?u=c.text?200:404:u===1223&&(u=204)}}}catch(d){i||s(-1,d)}c&&s(u,f,c,l)},n.async?a.readyState===4?setTimeout(r,0):(o=++jn,Bn&&(Hn||(Hn={},v(e).unload(Bn)),Hn[o]=r),a.onreadystatechange=r):r()},abort:function(){r&&r(0,1)}}}});var qn,Rn,Un=/^(?:toggle|show|hide)$/,zn=new RegExp("^(?:([-+])=|)("+m+")([a-z%]*)$","i"),Wn=/queueHooks$/,Xn=[Gn],Vn={"*":[function(e,t){var n,r,i=this.createTween(e,t),s=zn.exec(t),o=i.cur(),u=+o||0,a=1,f=20;if(s){n=+s[2],r=s[3]||(v.cssNumber[e]?"":"px");if(r!=="px"&&u){u=v.css(i.elem,e,!0)||n||1;do a=a||".5",u/=a,v.style(i.elem,e,u+r);while(a!==(a=i.cur()/o)&&a!==1&&--f)}i.unit=r,i.start=u,i.end=s[1]?u+(s[1]+1)*n:n}return i}]};v.Animation=v.extend(Kn,{tweener:function(e,t){v.isFunction(e)?(t=e,e=["*"]):e=e.split(" ");var n,r=0,i=e.length;for(;r-1,f={},l={},c,h;a?(l=i.position(),c=l.top,h=l.left):(c=parseFloat(o)||0,h=parseFloat(u)||0),v.isFunction(t)&&(t=t.call(e,n,s)),t.top!=null&&(f.top=t.top-s.top+c),t.left!=null&&(f.left=t.left-s.left+h),"using"in t?t.using.call(e,f):i.css(f)}},v.fn.extend({position:function(){if(!this[0])return;var e=this[0],t=this.offsetParent(),n=this.offset(),r=er.test(t[0].nodeName)?{top:0,left:0}:t.offset();return n.top-=parseFloat(v.css(e,"marginTop"))||0,n.left-=parseFloat(v.css(e,"marginLeft"))||0,r.top+=parseFloat(v.css(t[0],"borderTopWidth"))||0,r.left+=parseFloat(v.css(t[0],"borderLeftWidth"))||0,{top:n.top-r.top,left:n.left-r.left}},offsetParent:function(){return this.map(function(){var e=this.offsetParent||i.body;while(e&&!er.test(e.nodeName)&&v.css(e,"position")==="static")e=e.offsetParent;return e||i.body})}}),v.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,n){var r=/Y/.test(n);v.fn[e]=function(i){return v.access(this,function(e,i,s){var o=tr(e);if(s===t)return o?n in o?o[n]:o.document.documentElement[i]:e[i];o?o.scrollTo(r?v(o).scrollLeft():s,r?s:v(o).scrollTop()):e[i]=s},e,i,arguments.length,null)}}),v.each({Height:"height",Width:"width"},function(e,n){v.each({padding:"inner"+e,content:n,"":"outer"+e},function(r,i){v.fn[i]=function(i,s){var o=arguments.length&&(r||typeof i!="boolean"),u=r||(i===!0||s===!0?"margin":"border");return v.access(this,function(n,r,i){var s;return v.isWindow(n)?n.document.documentElement["client"+e]:n.nodeType===9?(s=n.documentElement,Math.max(n.body["scroll"+e],s["scroll"+e],n.body["offset"+e],s["offset"+e],s["client"+e])):i===t?v.css(n,r,i,u):v.style(n,r,i,u)},n,o?i:t,o,null)}})}),e.jQuery=e.$=v,typeof define=="function"&&define.amd&&define.amd.ajQuery&&define("jquery",[],function(){return v})})(window); \ No newline at end of file diff --git a/node_modules/qrcodejs2/package.json b/node_modules/qrcodejs2/package.json new file mode 100644 index 0000000..f986382 --- /dev/null +++ b/node_modules/qrcodejs2/package.json @@ -0,0 +1,21 @@ +{ + "name": "qrcodejs2", + "version": "0.0.2", + "description": "Javsacript QRCode for all browsers", + "main": "qrcode.js", + "repository": { + "type": "git", + "url": "git://github.com/davidshimjs/qrcodejs.git" + }, + "keywords": [ + "qrcode", + "javascript" + ], + "author": "davidshimjs ", + "license": "MIT", + "gitHead": "06c7a5e134f116402699f03cda5819e10a0e5787", + "readmeFilename": "README.md", + "bugs": { + "url": "https://github.com/davidshimjs/qrcodejs/issues" + } +} diff --git a/node_modules/qrcodejs2/qrcode.js b/node_modules/qrcodejs2/qrcode.js new file mode 100644 index 0000000..9f7082b --- /dev/null +++ b/node_modules/qrcodejs2/qrcode.js @@ -0,0 +1,627 @@ +/** + * @fileoverview + * - Using the 'QRCode for Javascript library' + * - Fixed dataset of 'QRCode for Javascript library' for support full-spec. + * - this library has no dependencies. + * + * @author davidshimjs + * @see http://www.d-project.com/ + * @see http://jeromeetienne.github.com/jquery-qrcode/ + */ +var QRCode; + +(function (root, factory) { + + /* CommonJS */ + if (typeof exports == 'object') module.exports = factory() + + /* AMD module */ + else if (typeof define == 'function' && define.amd) define(factory) + + /* Global */ + else root.QRCode = factory() + +}(this, function () { //--------------------------------------------------------------------- + // QRCode for JavaScript + // + // Copyright (c) 2009 Kazuhiko Arase + // + // URL: http://www.d-project.com/ + // + // Licensed under the MIT license: + // http://www.opensource.org/licenses/mit-license.php + // + // The word "QR Code" is registered trademark of + // DENSO WAVE INCORPORATED + // http://www.denso-wave.com/qrcode/faqpatent-e.html + // + //--------------------------------------------------------------------- + function QR8bitByte(data) { + this.mode = QRMode.MODE_8BIT_BYTE; + this.data = data; + this.parsedData = []; + + // Added to support UTF-8 Characters + for (var i = 0, l = this.data.length; i < l; i++) { + var byteArray = []; + var code = this.data.charCodeAt(i); + + if (code > 0x10000) { + byteArray[0] = 0xF0 | ((code & 0x1C0000) >>> 18); + byteArray[1] = 0x80 | ((code & 0x3F000) >>> 12); + byteArray[2] = 0x80 | ((code & 0xFC0) >>> 6); + byteArray[3] = 0x80 | (code & 0x3F); + } else if (code > 0x800) { + byteArray[0] = 0xE0 | ((code & 0xF000) >>> 12); + byteArray[1] = 0x80 | ((code & 0xFC0) >>> 6); + byteArray[2] = 0x80 | (code & 0x3F); + } else if (code > 0x80) { + byteArray[0] = 0xC0 | ((code & 0x7C0) >>> 6); + byteArray[1] = 0x80 | (code & 0x3F); + } else { + byteArray[0] = code; + } + + this.parsedData.push(byteArray); + } + + this.parsedData = Array.prototype.concat.apply([], this.parsedData); + + if (this.parsedData.length != this.data.length) { + this.parsedData.unshift(191); + this.parsedData.unshift(187); + this.parsedData.unshift(239); + } + } + + QR8bitByte.prototype = { + getLength: function (buffer) { + return this.parsedData.length; + }, + write: function (buffer) { + for (var i = 0, l = this.parsedData.length; i < l; i++) { + buffer.put(this.parsedData[i], 8); + } + } + }; + + function QRCodeModel(typeNumber, errorCorrectLevel) { + this.typeNumber = typeNumber; + this.errorCorrectLevel = errorCorrectLevel; + this.modules = null; + this.moduleCount = 0; + this.dataCache = null; + this.dataList = []; + } + + QRCodeModel.prototype={addData:function(data){var newData=new QR8bitByte(data);this.dataList.push(newData);this.dataCache=null;},isDark:function(row,col){if(row<0||this.moduleCount<=row||col<0||this.moduleCount<=col){throw new Error(row+","+col);} + return this.modules[row][col];},getModuleCount:function(){return this.moduleCount;},make:function(){this.makeImpl(false,this.getBestMaskPattern());},makeImpl:function(test,maskPattern){this.moduleCount=this.typeNumber*4+17;this.modules=new Array(this.moduleCount);for(var row=0;row=7){this.setupTypeNumber(test);} + if(this.dataCache==null){this.dataCache=QRCodeModel.createData(this.typeNumber,this.errorCorrectLevel,this.dataList);} + this.mapData(this.dataCache,maskPattern);},setupPositionProbePattern:function(row,col){for(var r=-1;r<=7;r++){if(row+r<=-1||this.moduleCount<=row+r)continue;for(var c=-1;c<=7;c++){if(col+c<=-1||this.moduleCount<=col+c)continue;if((0<=r&&r<=6&&(c==0||c==6))||(0<=c&&c<=6&&(r==0||r==6))||(2<=r&&r<=4&&2<=c&&c<=4)){this.modules[row+r][col+c]=true;}else{this.modules[row+r][col+c]=false;}}}},getBestMaskPattern:function(){var minLostPoint=0;var pattern=0;for(var i=0;i<8;i++){this.makeImpl(true,i);var lostPoint=QRUtil.getLostPoint(this);if(i==0||minLostPoint>lostPoint){minLostPoint=lostPoint;pattern=i;}} + return pattern;},createMovieClip:function(target_mc,instance_name,depth){var qr_mc=target_mc.createEmptyMovieClip(instance_name,depth);var cs=1;this.make();for(var row=0;row>i)&1)==1);this.modules[Math.floor(i/3)][i%3+this.moduleCount-8-3]=mod;} + for(var i=0;i<18;i++){var mod=(!test&&((bits>>i)&1)==1);this.modules[i%3+this.moduleCount-8-3][Math.floor(i/3)]=mod;}},setupTypeInfo:function(test,maskPattern){var data=(this.errorCorrectLevel<<3)|maskPattern;var bits=QRUtil.getBCHTypeInfo(data);for(var i=0;i<15;i++){var mod=(!test&&((bits>>i)&1)==1);if(i<6){this.modules[i][8]=mod;}else if(i<8){this.modules[i+1][8]=mod;}else{this.modules[this.moduleCount-15+i][8]=mod;}} + for(var i=0;i<15;i++){var mod=(!test&&((bits>>i)&1)==1);if(i<8){this.modules[8][this.moduleCount-i-1]=mod;}else if(i<9){this.modules[8][15-i-1+1]=mod;}else{this.modules[8][15-i-1]=mod;}} + this.modules[this.moduleCount-8][8]=(!test);},mapData:function(data,maskPattern){var inc=-1;var row=this.moduleCount-1;var bitIndex=7;var byteIndex=0;for(var col=this.moduleCount-1;col>0;col-=2){if(col==6)col--;while(true){for(var c=0;c<2;c++){if(this.modules[row][col-c]==null){var dark=false;if(byteIndex>>bitIndex)&1)==1);} + var mask=QRUtil.getMask(maskPattern,row,col-c);if(mask){dark=!dark;} + this.modules[row][col-c]=dark;bitIndex--;if(bitIndex==-1){byteIndex++;bitIndex=7;}}} + row+=inc;if(row<0||this.moduleCount<=row){row-=inc;inc=-inc;break;}}}}};QRCodeModel.PAD0=0xEC;QRCodeModel.PAD1=0x11;QRCodeModel.createData=function(typeNumber,errorCorrectLevel,dataList){var rsBlocks=QRRSBlock.getRSBlocks(typeNumber,errorCorrectLevel);var buffer=new QRBitBuffer();for(var i=0;itotalDataCount*8){throw new Error("code length overflow. (" + +buffer.getLengthInBits() + +">" + +totalDataCount*8 + +")");} + if(buffer.getLengthInBits()+4<=totalDataCount*8){buffer.put(0,4);} + while(buffer.getLengthInBits()%8!=0){buffer.putBit(false);} + while(true){if(buffer.getLengthInBits()>=totalDataCount*8){break;} + buffer.put(QRCodeModel.PAD0,8);if(buffer.getLengthInBits()>=totalDataCount*8){break;} + buffer.put(QRCodeModel.PAD1,8);} + return QRCodeModel.createBytes(buffer,rsBlocks);};QRCodeModel.createBytes=function(buffer,rsBlocks){var offset=0;var maxDcCount=0;var maxEcCount=0;var dcdata=new Array(rsBlocks.length);var ecdata=new Array(rsBlocks.length);for(var r=0;r=0)?modPoly.get(modIndex):0;}} + var totalCodeCount=0;for(var i=0;i=0){d^=(QRUtil.G15<<(QRUtil.getBCHDigit(d)-QRUtil.getBCHDigit(QRUtil.G15)));} + return((data<<10)|d)^QRUtil.G15_MASK;},getBCHTypeNumber:function(data){var d=data<<12;while(QRUtil.getBCHDigit(d)-QRUtil.getBCHDigit(QRUtil.G18)>=0){d^=(QRUtil.G18<<(QRUtil.getBCHDigit(d)-QRUtil.getBCHDigit(QRUtil.G18)));} + return(data<<12)|d;},getBCHDigit:function(data){var digit=0;while(data!=0){digit++;data>>>=1;} + return digit;},getPatternPosition:function(typeNumber){return QRUtil.PATTERN_POSITION_TABLE[typeNumber-1];},getMask:function(maskPattern,i,j){switch(maskPattern){case QRMaskPattern.PATTERN000:return(i+j)%2==0;case QRMaskPattern.PATTERN001:return i%2==0;case QRMaskPattern.PATTERN010:return j%3==0;case QRMaskPattern.PATTERN011:return(i+j)%3==0;case QRMaskPattern.PATTERN100:return(Math.floor(i/2)+Math.floor(j/3))%2==0;case QRMaskPattern.PATTERN101:return(i*j)%2+(i*j)%3==0;case QRMaskPattern.PATTERN110:return((i*j)%2+(i*j)%3)%2==0;case QRMaskPattern.PATTERN111:return((i*j)%3+(i+j)%2)%2==0;default:throw new Error("bad maskPattern:"+maskPattern);}},getErrorCorrectPolynomial:function(errorCorrectLength){var a=new QRPolynomial([1],0);for(var i=0;i5){lostPoint+=(3+sameCount-5);}}} + for(var row=0;row=256){n-=255;} + return QRMath.EXP_TABLE[n];},EXP_TABLE:new Array(256),LOG_TABLE:new Array(256)};for(var i=0;i<8;i++){QRMath.EXP_TABLE[i]=1<>>(7-index%8))&1)==1;},put:function(num,length){for(var i=0;i>>(length-i-1))&1)==1);}},getLengthInBits:function(){return this.length;},putBit:function(bit){var bufIndex=Math.floor(this.length/8);if(this.buffer.length<=bufIndex){this.buffer.push(0);} + if(bit){this.buffer[bufIndex]|=(0x80>>>(this.length%8));} + this.length++;}};var QRCodeLimitLength=[[17,14,11,7],[32,26,20,14],[53,42,32,24],[78,62,46,34],[106,84,60,44],[134,106,74,58],[154,122,86,64],[192,152,108,84],[230,180,130,98],[271,213,151,119],[321,251,177,137],[367,287,203,155],[425,331,241,177],[458,362,258,194],[520,412,292,220],[586,450,322,250],[644,504,364,280],[718,560,394,310],[792,624,442,338],[858,666,482,382],[929,711,509,403],[1003,779,565,439],[1091,857,611,461],[1171,911,661,511],[1273,997,715,535],[1367,1059,751,593],[1465,1125,805,625],[1528,1190,868,658],[1628,1264,908,698],[1732,1370,982,742],[1840,1452,1030,790],[1952,1538,1112,842],[2068,1628,1168,898],[2188,1722,1228,958],[2303,1809,1283,983],[2431,1911,1351,1051],[2563,1989,1423,1093],[2699,2099,1499,1139],[2809,2213,1579,1219],[2953,2331,1663,1273]]; + + function _isSupportCanvas() { + return typeof CanvasRenderingContext2D != "undefined"; + } + + // android 2.x doesn't support Data-URI spec + function _getAndroid() { + var android = false; + var sAgent = navigator.userAgent; + + if (/android/i.test(sAgent)) { // android + android = true; + var aMat = sAgent.toString().match(/android ([0-9]\.[0-9])/i); + + if (aMat && aMat[1]) { + android = parseFloat(aMat[1]); + } + } + + return android; + } + + var svgDrawer = (function() { + + var Drawing = function (el, htOption) { + this._el = el; + this._htOption = htOption; + }; + + Drawing.prototype.draw = function (oQRCode) { + var _htOption = this._htOption; + var _el = this._el; + var nCount = oQRCode.getModuleCount(); + var nWidth = Math.floor(_htOption.width / nCount); + var nHeight = Math.floor(_htOption.height / nCount); + + this.clear(); + + function makeSVG(tag, attrs) { + var el = document.createElementNS('http://www.w3.org/2000/svg', tag); + for (var k in attrs) + if (attrs.hasOwnProperty(k)) el.setAttribute(k, attrs[k]); + return el; + } + + var svg = makeSVG("svg" , {'viewBox': '0 0 ' + String(nCount) + " " + String(nCount), 'width': '100%', 'height': '100%', 'fill': _htOption.colorLight}); + svg.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:xlink", "http://www.w3.org/1999/xlink"); + _el.appendChild(svg); + + svg.appendChild(makeSVG("rect", {"fill": _htOption.colorLight, "width": "100%", "height": "100%"})); + svg.appendChild(makeSVG("rect", {"fill": _htOption.colorDark, "width": "1", "height": "1", "id": "template"})); + + for (var row = 0; row < nCount; row++) { + for (var col = 0; col < nCount; col++) { + if (oQRCode.isDark(row, col)) { + var child = makeSVG("use", {"x": String(col), "y": String(row)}); + child.setAttributeNS("http://www.w3.org/1999/xlink", "href", "#template") + svg.appendChild(child); + } + } + } + }; + Drawing.prototype.clear = function () { + while (this._el.hasChildNodes()) + this._el.removeChild(this._el.lastChild); + }; + return Drawing; + })(); + + var useSVG = document.documentElement.tagName.toLowerCase() === "svg"; + + // Drawing in DOM by using Table tag + var Drawing = useSVG ? svgDrawer : !_isSupportCanvas() ? (function () { + var Drawing = function (el, htOption) { + this._el = el; + this._htOption = htOption; + }; + + /** + * Draw the QRCode + * + * @param {QRCode} oQRCode + */ + Drawing.prototype.draw = function (oQRCode) { + var _htOption = this._htOption; + var _el = this._el; + var nCount = oQRCode.getModuleCount(); + var nWidth = Math.floor(_htOption.width / nCount); + var nHeight = Math.floor(_htOption.height / nCount); + var aHTML = ['
']; + + for (var row = 0; row < nCount; row++) { + aHTML.push(''); + + for (var col = 0; col < nCount; col++) { + aHTML.push(''); + } + + aHTML.push(''); + } + + aHTML.push('
'); + _el.innerHTML = aHTML.join(''); + + // Fix the margin values as real size. + var elTable = _el.childNodes[0]; + var nLeftMarginTable = (_htOption.width - elTable.offsetWidth) / 2; + var nTopMarginTable = (_htOption.height - elTable.offsetHeight) / 2; + + if (nLeftMarginTable > 0 && nTopMarginTable > 0) { + elTable.style.margin = nTopMarginTable + "px " + nLeftMarginTable + "px"; + } + }; + + /** + * Clear the QRCode + */ + Drawing.prototype.clear = function () { + this._el.innerHTML = ''; + }; + + return Drawing; + })() : (function () { // Drawing in Canvas + function _onMakeImage() { + this._elImage.src = this._elCanvas.toDataURL("image/png"); + this._elImage.style.display = "block"; + this._elCanvas.style.display = "none"; + } + + // Android 2.1 bug workaround + // http://code.google.com/p/android/issues/detail?id=5141 + if (this._android && this._android <= 2.1) { + var factor = 1 / window.devicePixelRatio; + var drawImage = CanvasRenderingContext2D.prototype.drawImage; + CanvasRenderingContext2D.prototype.drawImage = function (image, sx, sy, sw, sh, dx, dy, dw, dh) { + if (("nodeName" in image) && /img/i.test(image.nodeName)) { + for (var i = arguments.length - 1; i >= 1; i--) { + arguments[i] = arguments[i] * factor; + } + } else if (typeof dw == "undefined") { + arguments[1] *= factor; + arguments[2] *= factor; + arguments[3] *= factor; + arguments[4] *= factor; + } + + drawImage.apply(this, arguments); + }; + } + + /** + * Check whether the user's browser supports Data URI or not + * + * @private + * @param {Function} fSuccess Occurs if it supports Data URI + * @param {Function} fFail Occurs if it doesn't support Data URI + */ + function _safeSetDataURI(fSuccess, fFail) { + var self = this; + self._fFail = fFail; + self._fSuccess = fSuccess; + + // Check it just once + if (self._bSupportDataURI === null) { + var el = document.createElement("img"); + var fOnError = function() { + self._bSupportDataURI = false; + + if (self._fFail) { + self._fFail.call(self); + } + }; + var fOnSuccess = function() { + self._bSupportDataURI = true; + + if (self._fSuccess) { + self._fSuccess.call(self); + } + }; + + el.onabort = fOnError; + el.onerror = fOnError; + el.onload = fOnSuccess; + el.src = "data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="; // the Image contains 1px data. + return; + } else if (self._bSupportDataURI === true && self._fSuccess) { + self._fSuccess.call(self); + } else if (self._bSupportDataURI === false && self._fFail) { + self._fFail.call(self); + } + }; + + /** + * Drawing QRCode by using canvas + * + * @constructor + * @param {HTMLElement} el + * @param {Object} htOption QRCode Options + */ + var Drawing = function (el, htOption) { + this._bIsPainted = false; + this._android = _getAndroid(); + + this._htOption = htOption; + this._elCanvas = document.createElement("canvas"); + this._elCanvas.width = htOption.width; + this._elCanvas.height = htOption.height; + el.appendChild(this._elCanvas); + this._el = el; + this._oContext = this._elCanvas.getContext("2d"); + this._bIsPainted = false; + this._elImage = document.createElement("img"); + this._elImage.alt = "Scan me!"; + this._elImage.style.display = "none"; + this._el.appendChild(this._elImage); + this._bSupportDataURI = null; + }; + + /** + * Draw the QRCode + * + * @param {QRCode} oQRCode + */ + Drawing.prototype.draw = function (oQRCode) { + var _elImage = this._elImage; + var _oContext = this._oContext; + var _htOption = this._htOption; + + var nCount = oQRCode.getModuleCount(); + var nWidth = _htOption.width / nCount; + var nHeight = _htOption.height / nCount; + var nRoundedWidth = Math.round(nWidth); + var nRoundedHeight = Math.round(nHeight); + + _elImage.style.display = "none"; + this.clear(); + + for (var row = 0; row < nCount; row++) { + for (var col = 0; col < nCount; col++) { + var bIsDark = oQRCode.isDark(row, col); + var nLeft = col * nWidth; + var nTop = row * nHeight; + _oContext.strokeStyle = bIsDark ? _htOption.colorDark : _htOption.colorLight; + _oContext.lineWidth = 1; + _oContext.fillStyle = bIsDark ? _htOption.colorDark : _htOption.colorLight; + _oContext.fillRect(nLeft, nTop, nWidth, nHeight); + + // 안티 앨리어싱 방지 처리 + _oContext.strokeRect( + Math.floor(nLeft) + 0.5, + Math.floor(nTop) + 0.5, + nRoundedWidth, + nRoundedHeight + ); + + _oContext.strokeRect( + Math.ceil(nLeft) - 0.5, + Math.ceil(nTop) - 0.5, + nRoundedWidth, + nRoundedHeight + ); + } + } + + this._bIsPainted = true; + }; + + /** + * Make the image from Canvas if the browser supports Data URI. + */ + Drawing.prototype.makeImage = function () { + if (this._bIsPainted) { + _safeSetDataURI.call(this, _onMakeImage); + } + }; + + /** + * Return whether the QRCode is painted or not + * + * @return {Boolean} + */ + Drawing.prototype.isPainted = function () { + return this._bIsPainted; + }; + + /** + * Clear the QRCode + */ + Drawing.prototype.clear = function () { + this._oContext.clearRect(0, 0, this._elCanvas.width, this._elCanvas.height); + this._bIsPainted = false; + }; + + /** + * @private + * @param {Number} nNumber + */ + Drawing.prototype.round = function (nNumber) { + if (!nNumber) { + return nNumber; + } + + return Math.floor(nNumber * 1000) / 1000; + }; + + return Drawing; + })(); + + /** + * Get the type by string length + * + * @private + * @param {String} sText + * @param {Number} nCorrectLevel + * @return {Number} type + */ + function _getTypeNumber(sText, nCorrectLevel) { + var nType = 1; + var length = _getUTF8Length(sText); + + for (var i = 0, len = QRCodeLimitLength.length; i <= len; i++) { + var nLimit = 0; + + switch (nCorrectLevel) { + case QRErrorCorrectLevel.L : + nLimit = QRCodeLimitLength[i][0]; + break; + case QRErrorCorrectLevel.M : + nLimit = QRCodeLimitLength[i][1]; + break; + case QRErrorCorrectLevel.Q : + nLimit = QRCodeLimitLength[i][2]; + break; + case QRErrorCorrectLevel.H : + nLimit = QRCodeLimitLength[i][3]; + break; + } + + if (length <= nLimit) { + break; + } else { + nType++; + } + } + + if (nType > QRCodeLimitLength.length) { + throw new Error("Too long data"); + } + + return nType; + } + + function _getUTF8Length(sText) { + var replacedText = encodeURI(sText).toString().replace(/\%[0-9a-fA-F]{2}/g, 'a'); + return replacedText.length + (replacedText.length != sText ? 3 : 0); + } + + /** + * @class QRCode + * @constructor + * @example + * new QRCode(document.getElementById("test"), "http://jindo.dev.naver.com/collie"); + * + * @example + * var oQRCode = new QRCode("test", { + * text : "http://naver.com", + * width : 128, + * height : 128 + * }); + * + * oQRCode.clear(); // Clear the QRCode. + * oQRCode.makeCode("http://map.naver.com"); // Re-create the QRCode. + * + * @param {HTMLElement|String} el target element or 'id' attribute of element. + * @param {Object|String} vOption + * @param {String} vOption.text QRCode link data + * @param {Number} [vOption.width=256] + * @param {Number} [vOption.height=256] + * @param {String} [vOption.colorDark="#000000"] + * @param {String} [vOption.colorLight="#ffffff"] + * @param {QRCode.CorrectLevel} [vOption.correctLevel=QRCode.CorrectLevel.H] [L|M|Q|H] + */ + QRCode = function (el, vOption) { + this._htOption = { + width : 256, + height : 256, + typeNumber : 4, + colorDark : "#000000", + colorLight : "#ffffff", + correctLevel : QRErrorCorrectLevel.H + }; + + if (typeof vOption === 'string') { + vOption = { + text : vOption + }; + } + + // Overwrites options + if (vOption) { + for (var i in vOption) { + this._htOption[i] = vOption[i]; + } + } + + if (typeof el == "string") { + el = document.getElementById(el); + } + + if (this._htOption.useSVG) { + Drawing = svgDrawer; + } + + this._android = _getAndroid(); + this._el = el; + this._oQRCode = null; + this._oDrawing = new Drawing(this._el, this._htOption); + + if (this._htOption.text) { + this.makeCode(this._htOption.text); + } + }; + + /** + * Make the QRCode + * + * @param {String} sText link data + */ + QRCode.prototype.makeCode = function (sText) { + this._oQRCode = new QRCodeModel(_getTypeNumber(sText, this._htOption.correctLevel), this._htOption.correctLevel); + this._oQRCode.addData(sText); + this._oQRCode.make(); + this._el.title = sText; + this._oDrawing.draw(this._oQRCode); + this.makeImage(); + }; + + /** + * Make the Image from Canvas element + * - It occurs automatically + * - Android below 3 doesn't support Data-URI spec. + * + * @private + */ + QRCode.prototype.makeImage = function () { + if (typeof this._oDrawing.makeImage == "function" && (!this._android || this._android >= 3)) { + this._oDrawing.makeImage(); + } + }; + + /** + * Clear the QRCode + */ + QRCode.prototype.clear = function () { + this._oDrawing.clear(); + }; + + /** + * @name QRCode.CorrectLevel + */ + QRCode.CorrectLevel = QRErrorCorrectLevel; + + return QRCode; + +})); diff --git a/node_modules/qrcodejs2/qrcode.min.js b/node_modules/qrcodejs2/qrcode.min.js new file mode 100644 index 0000000..993e88f --- /dev/null +++ b/node_modules/qrcodejs2/qrcode.min.js @@ -0,0 +1 @@ +var QRCode;!function(){function a(a){this.mode=c.MODE_8BIT_BYTE,this.data=a,this.parsedData=[];for(var b=[],d=0,e=this.data.length;e>d;d++){var f=this.data.charCodeAt(d);f>65536?(b[0]=240|(1835008&f)>>>18,b[1]=128|(258048&f)>>>12,b[2]=128|(4032&f)>>>6,b[3]=128|63&f):f>2048?(b[0]=224|(61440&f)>>>12,b[1]=128|(4032&f)>>>6,b[2]=128|63&f):f>128?(b[0]=192|(1984&f)>>>6,b[1]=128|63&f):b[0]=f,this.parsedData=this.parsedData.concat(b)}this.parsedData.length!=this.data.length&&(this.parsedData.unshift(191),this.parsedData.unshift(187),this.parsedData.unshift(239))}function b(a,b){this.typeNumber=a,this.errorCorrectLevel=b,this.modules=null,this.moduleCount=0,this.dataCache=null,this.dataList=[]}function i(a,b){if(void 0==a.length)throw new Error(a.length+"/"+b);for(var c=0;c=f;f++){var h=0;switch(b){case d.L:h=l[f][0];break;case d.M:h=l[f][1];break;case d.Q:h=l[f][2];break;case d.H:h=l[f][3]}if(h>=e)break;c++}if(c>l.length)throw new Error("Too long data");return c}function s(a){var b=encodeURI(a).toString().replace(/\%[0-9a-fA-F]{2}/g,"a");return b.length+(b.length!=a?3:0)}a.prototype={getLength:function(){return this.parsedData.length},write:function(a){for(var b=0,c=this.parsedData.length;c>b;b++)a.put(this.parsedData[b],8)}},b.prototype={addData:function(b){var c=new a(b);this.dataList.push(c),this.dataCache=null},isDark:function(a,b){if(0>a||this.moduleCount<=a||0>b||this.moduleCount<=b)throw new Error(a+","+b);return this.modules[a][b]},getModuleCount:function(){return this.moduleCount},make:function(){this.makeImpl(!1,this.getBestMaskPattern())},makeImpl:function(a,c){this.moduleCount=4*this.typeNumber+17,this.modules=new Array(this.moduleCount);for(var d=0;d=7&&this.setupTypeNumber(a),null==this.dataCache&&(this.dataCache=b.createData(this.typeNumber,this.errorCorrectLevel,this.dataList)),this.mapData(this.dataCache,c)},setupPositionProbePattern:function(a,b){for(var c=-1;7>=c;c++)if(!(-1>=a+c||this.moduleCount<=a+c))for(var d=-1;7>=d;d++)-1>=b+d||this.moduleCount<=b+d||(this.modules[a+c][b+d]=c>=0&&6>=c&&(0==d||6==d)||d>=0&&6>=d&&(0==c||6==c)||c>=2&&4>=c&&d>=2&&4>=d?!0:!1)},getBestMaskPattern:function(){for(var a=0,b=0,c=0;8>c;c++){this.makeImpl(!0,c);var d=f.getLostPoint(this);(0==c||a>d)&&(a=d,b=c)}return b},createMovieClip:function(a,b,c){var d=a.createEmptyMovieClip(b,c),e=1;this.make();for(var f=0;f=g;g++)for(var h=-2;2>=h;h++)this.modules[d+g][e+h]=-2==g||2==g||-2==h||2==h||0==g&&0==h?!0:!1}},setupTypeNumber:function(a){for(var b=f.getBCHTypeNumber(this.typeNumber),c=0;18>c;c++){var d=!a&&1==(1&b>>c);this.modules[Math.floor(c/3)][c%3+this.moduleCount-8-3]=d}for(var c=0;18>c;c++){var d=!a&&1==(1&b>>c);this.modules[c%3+this.moduleCount-8-3][Math.floor(c/3)]=d}},setupTypeInfo:function(a,b){for(var c=this.errorCorrectLevel<<3|b,d=f.getBCHTypeInfo(c),e=0;15>e;e++){var g=!a&&1==(1&d>>e);6>e?this.modules[e][8]=g:8>e?this.modules[e+1][8]=g:this.modules[this.moduleCount-15+e][8]=g}for(var e=0;15>e;e++){var g=!a&&1==(1&d>>e);8>e?this.modules[8][this.moduleCount-e-1]=g:9>e?this.modules[8][15-e-1+1]=g:this.modules[8][15-e-1]=g}this.modules[this.moduleCount-8][8]=!a},mapData:function(a,b){for(var c=-1,d=this.moduleCount-1,e=7,g=0,h=this.moduleCount-1;h>0;h-=2)for(6==h&&h--;;){for(var i=0;2>i;i++)if(null==this.modules[d][h-i]){var j=!1;g>>e));var k=f.getMask(b,d,h-i);k&&(j=!j),this.modules[d][h-i]=j,e--,-1==e&&(g++,e=7)}if(d+=c,0>d||this.moduleCount<=d){d-=c,c=-c;break}}}},b.PAD0=236,b.PAD1=17,b.createData=function(a,c,d){for(var e=j.getRSBlocks(a,c),g=new k,h=0;h8*l)throw new Error("code length overflow. ("+g.getLengthInBits()+">"+8*l+")");for(g.getLengthInBits()+4<=8*l&&g.put(0,4);0!=g.getLengthInBits()%8;)g.putBit(!1);for(;;){if(g.getLengthInBits()>=8*l)break;if(g.put(b.PAD0,8),g.getLengthInBits()>=8*l)break;g.put(b.PAD1,8)}return b.createBytes(g,e)},b.createBytes=function(a,b){for(var c=0,d=0,e=0,g=new Array(b.length),h=new Array(b.length),j=0;j=0?p.get(q):0}}for(var r=0,m=0;mm;m++)for(var j=0;jm;m++)for(var j=0;j=0;)b^=f.G15<=0;)b^=f.G18<>>=1;return b},getPatternPosition:function(a){return f.PATTERN_POSITION_TABLE[a-1]},getMask:function(a,b,c){switch(a){case e.PATTERN000:return 0==(b+c)%2;case e.PATTERN001:return 0==b%2;case e.PATTERN010:return 0==c%3;case e.PATTERN011:return 0==(b+c)%3;case e.PATTERN100:return 0==(Math.floor(b/2)+Math.floor(c/3))%2;case e.PATTERN101:return 0==b*c%2+b*c%3;case e.PATTERN110:return 0==(b*c%2+b*c%3)%2;case e.PATTERN111:return 0==(b*c%3+(b+c)%2)%2;default:throw new Error("bad maskPattern:"+a)}},getErrorCorrectPolynomial:function(a){for(var b=new i([1],0),c=0;a>c;c++)b=b.multiply(new i([1,g.gexp(c)],0));return b},getLengthInBits:function(a,b){if(b>=1&&10>b)switch(a){case c.MODE_NUMBER:return 10;case c.MODE_ALPHA_NUM:return 9;case c.MODE_8BIT_BYTE:return 8;case c.MODE_KANJI:return 8;default:throw new Error("mode:"+a)}else if(27>b)switch(a){case c.MODE_NUMBER:return 12;case c.MODE_ALPHA_NUM:return 11;case c.MODE_8BIT_BYTE:return 16;case c.MODE_KANJI:return 10;default:throw new Error("mode:"+a)}else{if(!(41>b))throw new Error("type:"+b);switch(a){case c.MODE_NUMBER:return 14;case c.MODE_ALPHA_NUM:return 13;case c.MODE_8BIT_BYTE:return 16;case c.MODE_KANJI:return 12;default:throw new Error("mode:"+a)}}},getLostPoint:function(a){for(var b=a.getModuleCount(),c=0,d=0;b>d;d++)for(var e=0;b>e;e++){for(var f=0,g=a.isDark(d,e),h=-1;1>=h;h++)if(!(0>d+h||d+h>=b))for(var i=-1;1>=i;i++)0>e+i||e+i>=b||(0!=h||0!=i)&&g==a.isDark(d+h,e+i)&&f++;f>5&&(c+=3+f-5)}for(var d=0;b-1>d;d++)for(var e=0;b-1>e;e++){var j=0;a.isDark(d,e)&&j++,a.isDark(d+1,e)&&j++,a.isDark(d,e+1)&&j++,a.isDark(d+1,e+1)&&j++,(0==j||4==j)&&(c+=3)}for(var d=0;b>d;d++)for(var e=0;b-6>e;e++)a.isDark(d,e)&&!a.isDark(d,e+1)&&a.isDark(d,e+2)&&a.isDark(d,e+3)&&a.isDark(d,e+4)&&!a.isDark(d,e+5)&&a.isDark(d,e+6)&&(c+=40);for(var e=0;b>e;e++)for(var d=0;b-6>d;d++)a.isDark(d,e)&&!a.isDark(d+1,e)&&a.isDark(d+2,e)&&a.isDark(d+3,e)&&a.isDark(d+4,e)&&!a.isDark(d+5,e)&&a.isDark(d+6,e)&&(c+=40);for(var k=0,e=0;b>e;e++)for(var d=0;b>d;d++)a.isDark(d,e)&&k++;var l=Math.abs(100*k/b/b-50)/5;return c+=10*l}},g={glog:function(a){if(1>a)throw new Error("glog("+a+")");return g.LOG_TABLE[a]},gexp:function(a){for(;0>a;)a+=255;for(;a>=256;)a-=255;return g.EXP_TABLE[a]},EXP_TABLE:new Array(256),LOG_TABLE:new Array(256)},h=0;8>h;h++)g.EXP_TABLE[h]=1<h;h++)g.EXP_TABLE[h]=g.EXP_TABLE[h-4]^g.EXP_TABLE[h-5]^g.EXP_TABLE[h-6]^g.EXP_TABLE[h-8];for(var h=0;255>h;h++)g.LOG_TABLE[g.EXP_TABLE[h]]=h;i.prototype={get:function(a){return this.num[a]},getLength:function(){return this.num.length},multiply:function(a){for(var b=new Array(this.getLength()+a.getLength()-1),c=0;cf;f++)for(var g=c[3*f+0],h=c[3*f+1],i=c[3*f+2],k=0;g>k;k++)e.push(new j(h,i));return e},j.getRsBlockTable=function(a,b){switch(b){case d.L:return j.RS_BLOCK_TABLE[4*(a-1)+0];case d.M:return j.RS_BLOCK_TABLE[4*(a-1)+1];case d.Q:return j.RS_BLOCK_TABLE[4*(a-1)+2];case d.H:return j.RS_BLOCK_TABLE[4*(a-1)+3];default:return void 0}},k.prototype={get:function(a){var b=Math.floor(a/8);return 1==(1&this.buffer[b]>>>7-a%8)},put:function(a,b){for(var c=0;b>c;c++)this.putBit(1==(1&a>>>b-c-1))},getLengthInBits:function(){return this.length},putBit:function(a){var b=Math.floor(this.length/8);this.buffer.length<=b&&this.buffer.push(0),a&&(this.buffer[b]|=128>>>this.length%8),this.length++}};var l=[[17,14,11,7],[32,26,20,14],[53,42,32,24],[78,62,46,34],[106,84,60,44],[134,106,74,58],[154,122,86,64],[192,152,108,84],[230,180,130,98],[271,213,151,119],[321,251,177,137],[367,287,203,155],[425,331,241,177],[458,362,258,194],[520,412,292,220],[586,450,322,250],[644,504,364,280],[718,560,394,310],[792,624,442,338],[858,666,482,382],[929,711,509,403],[1003,779,565,439],[1091,857,611,461],[1171,911,661,511],[1273,997,715,535],[1367,1059,751,593],[1465,1125,805,625],[1528,1190,868,658],[1628,1264,908,698],[1732,1370,982,742],[1840,1452,1030,790],[1952,1538,1112,842],[2068,1628,1168,898],[2188,1722,1228,958],[2303,1809,1283,983],[2431,1911,1351,1051],[2563,1989,1423,1093],[2699,2099,1499,1139],[2809,2213,1579,1219],[2953,2331,1663,1273]],o=function(){var a=function(a,b){this._el=a,this._htOption=b};return a.prototype.draw=function(a){function g(a,b){var c=document.createElementNS("http://www.w3.org/2000/svg",a);for(var d in b)b.hasOwnProperty(d)&&c.setAttribute(d,b[d]);return c}var b=this._htOption,c=this._el,d=a.getModuleCount();Math.floor(b.width/d),Math.floor(b.height/d),this.clear();var h=g("svg",{viewBox:"0 0 "+String(d)+" "+String(d),width:"100%",height:"100%",fill:b.colorLight});h.setAttributeNS("http://www.w3.org/2000/xmlns/","xmlns:xlink","http://www.w3.org/1999/xlink"),c.appendChild(h),h.appendChild(g("rect",{fill:b.colorDark,width:"1",height:"1",id:"template"}));for(var i=0;d>i;i++)for(var j=0;d>j;j++)if(a.isDark(i,j)){var k=g("use",{x:String(i),y:String(j)});k.setAttributeNS("http://www.w3.org/1999/xlink","href","#template"),h.appendChild(k)}},a.prototype.clear=function(){for(;this._el.hasChildNodes();)this._el.removeChild(this._el.lastChild)},a}(),p="svg"===document.documentElement.tagName.toLowerCase(),q=p?o:m()?function(){function a(){this._elImage.src=this._elCanvas.toDataURL("image/png"),this._elImage.style.display="block",this._elCanvas.style.display="none"}function d(a,b){var c=this;if(c._fFail=b,c._fSuccess=a,null===c._bSupportDataURI){var d=document.createElement("img"),e=function(){c._bSupportDataURI=!1,c._fFail&&_fFail.call(c)},f=function(){c._bSupportDataURI=!0,c._fSuccess&&c._fSuccess.call(c)};return d.onabort=e,d.onerror=e,d.onload=f,d.src="data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==",void 0}c._bSupportDataURI===!0&&c._fSuccess?c._fSuccess.call(c):c._bSupportDataURI===!1&&c._fFail&&c._fFail.call(c)}if(this._android&&this._android<=2.1){var b=1/window.devicePixelRatio,c=CanvasRenderingContext2D.prototype.drawImage;CanvasRenderingContext2D.prototype.drawImage=function(a,d,e,f,g,h,i,j){if("nodeName"in a&&/img/i.test(a.nodeName))for(var l=arguments.length-1;l>=1;l--)arguments[l]=arguments[l]*b;else"undefined"==typeof j&&(arguments[1]*=b,arguments[2]*=b,arguments[3]*=b,arguments[4]*=b);c.apply(this,arguments)}}var e=function(a,b){this._bIsPainted=!1,this._android=n(),this._htOption=b,this._elCanvas=document.createElement("canvas"),this._elCanvas.width=b.width,this._elCanvas.height=b.height,a.appendChild(this._elCanvas),this._el=a,this._oContext=this._elCanvas.getContext("2d"),this._bIsPainted=!1,this._elImage=document.createElement("img"),this._elImage.style.display="none",this._el.appendChild(this._elImage),this._bSupportDataURI=null};return e.prototype.draw=function(a){var b=this._elImage,c=this._oContext,d=this._htOption,e=a.getModuleCount(),f=d.width/e,g=d.height/e,h=Math.round(f),i=Math.round(g);b.style.display="none",this.clear();for(var j=0;e>j;j++)for(var k=0;e>k;k++){var l=a.isDark(j,k),m=k*f,n=j*g;c.strokeStyle=l?d.colorDark:d.colorLight,c.lineWidth=1,c.fillStyle=l?d.colorDark:d.colorLight,c.fillRect(m,n,f,g),c.strokeRect(Math.floor(m)+.5,Math.floor(n)+.5,h,i),c.strokeRect(Math.ceil(m)-.5,Math.ceil(n)-.5,h,i)}this._bIsPainted=!0},e.prototype.makeImage=function(){this._bIsPainted&&d.call(this,a)},e.prototype.isPainted=function(){return this._bIsPainted},e.prototype.clear=function(){this._oContext.clearRect(0,0,this._elCanvas.width,this._elCanvas.height),this._bIsPainted=!1},e.prototype.round=function(a){return a?Math.floor(1e3*a)/1e3:a},e}():function(){var a=function(a,b){this._el=a,this._htOption=b};return a.prototype.draw=function(a){for(var b=this._htOption,c=this._el,d=a.getModuleCount(),e=Math.floor(b.width/d),f=Math.floor(b.height/d),g=[''],h=0;d>h;h++){g.push("");for(var i=0;d>i;i++)g.push('');g.push("")}g.push("
"),c.innerHTML=g.join("");var j=c.childNodes[0],k=(b.width-j.offsetWidth)/2,l=(b.height-j.offsetHeight)/2;k>0&&l>0&&(j.style.margin=l+"px "+k+"px")},a.prototype.clear=function(){this._el.innerHTML=""},a}();QRCode=function(a,b){if(this._htOption={width:256,height:256,typeNumber:4,colorDark:"#000000",colorLight:"#ffffff",correctLevel:d.H},"string"==typeof b&&(b={text:b}),b)for(var c in b)this._htOption[c]=b[c];"string"==typeof a&&(a=document.getElementById(a)),this._android=n(),this._el=a,this._oQRCode=null,this._oDrawing=new q(this._el,this._htOption),this._htOption.text&&this.makeCode(this._htOption.text)},QRCode.prototype.makeCode=function(a){this._oQRCode=new b(r(a,this._htOption.correctLevel),this._htOption.correctLevel),this._oQRCode.addData(a),this._oQRCode.make(),this._el.title=a,this._oDrawing.draw(this._oQRCode),this.makeImage()},QRCode.prototype.makeImage=function(){"function"==typeof this._oDrawing.makeImage&&(!this._android||this._android>=3)&&this._oDrawing.makeImage()},QRCode.prototype.clear=function(){this._oDrawing.clear()},QRCode.CorrectLevel=d}(); \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..9c88a6e --- /dev/null +++ b/package.json @@ -0,0 +1,83 @@ +{ + "id": "ctms-client", + "name": "ctms-client", + "displayName": "ctms-client 运车助手", + "version": "110", + "description": "基于UNI-APP开发制作的CTMS运车助手App。主要面向终端运车客户提供下单查单、询价、资讯获取等功能及服务。", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": "https://gitea.hiluker.com/lukegzs/ctms-client", + "keywords": [ + "hello-uniapp", + "uni-app", + "uni-ui", + "示例工程" + ], + "author": "", + "license": "MIT", + "bugs": { + "url": "https://gitea.hiluker.com/lukegzs/ctms-client" + }, + "homepage": "https://gitea.hiluker.com/lukegzs/ctms-client#readme", + "dependencies": {}, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y", + "京东": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + }, + + "uni-app": { + "scripts": { + "mp-dingtalk": { + "title": "钉钉小程序", + "env": { + "UNI_PLATFORM": "mp-alipay" + }, + "define": { + "MP-DINGTALK": true + } + } + } + } +} diff --git a/pages.json b/pages.json new file mode 100644 index 0000000..9aaee3f --- /dev/null +++ b/pages.json @@ -0,0 +1,559 @@ +{ + //主包,即放置默认启动页面/TabBar 页面,以及一些所有分包都需用到公共资源/JS 脚本;分包则是根据pages.json的配置进行划分;APP上是不起啥用的 + "pages": [{ + "path": "pages/index/index", + "style": { + "navigationBarTitleText": "首页", + "enablePullDownRefresh": true, + "titleNView": false + } + }, { + "path": "pages/ctms/tabbar/index/index", + "style": { + "enablePullDownRefresh": true + } + }, { + "path": "pages/ctms/tabbar/order/index", + "style": {} + }, { + "path": "pages/ctms/tabbar/mid/index", + "style": {} + }, { + "path": "pages/ctms/tabbar/notice/index", + "style": { + "enablePullDownRefresh": true + } + }, { + "path": "pages/ctms/tabbar/me/index", + "style": { + "h5": { + "bounce": "none", + "titleNView": { + "buttons": [{ + "text": "\ue616 ", + "fontSrc": "/static/font/iconfont.ttf", + "fontSize": "20px" + // "color": "red" + }] + } + } + } + }, { + "path": "uni_modules/uni-upgrade-center-app/pages/upgrade-popup", + "style": { + "disableScroll": true, + "app-plus": { + "backgroundColorTop": "transparent", + "background": "transparent", + "titleNView": false, + "scrollIndicator": false, + "popGesture": "none", + "animationType": "fade-in", + "animationDuration": 200 + + } + } + }, { + "path": "pages/uni-agree/uni-agree", + "style": { + "navigationStyle": "custom", + "app-plus": { + "popGesture": "none" + } + } + }, { + "path": "pages/common/webview/index", + "style": { + "navigationBarTitleText": "", + "app-plus": { + "bounce": "none", + "titleNView": { + "buttons": [{ + "text": "\ue739 ", //分享 + "fontSrc": "/static/font/iconfont.ttf", + "fontSize": "20px" + }, { + "text": "\ue616 ", //问号 + "fontSrc": "/static/font/iconfont.ttf", + "fontSize": "20px" + }, { + "text": "\ue604 ", //刷新 + "fontSrc": "/static/font/iconfont.ttf", + "fontSize": "20px" + }] + } + }, + "h5": { + "bounce": "none", + "titleNView": { + "buttons": [{ + "text": "\ue616 ", //盾牌 + "fontSrc": "/static/font/iconfont.ttf", + "fontSize": "20px" + }, { + "text": "\ue604 ", //刷新 + "fontSrc": "/static/font/iconfont.ttf", + "fontSize": "20px" + }, { + "text": "\ue699 ", + "fontSrc": "/static/font/iconfont.ttf", + "fontSize": "20px" + }] + } + } + }, + "needLogin": false + }, { + "path": "pages/common/textview/index", + "style": { + "navigationBarTitleText": "" + }, + "needLogin": false + }], + "subPackages": [{ + "root": "pages/uni-starter", + "pages": [{ + "path": "list/list", + "style": { + // #ifndef APP-PLUS + "enablePullDownRefresh": true + // #endif + // "navigationStyle": "custom" + } + }, { + "path": "list/search/search", + "style": { + "navigationBarTitleText": "搜索" + } + }, { + "path": "list/detail", + "style": { + "app-plus": { + "titleNView": { + "buttons": [{ + "type": "share" + }], + "type": "transparent" + } + }, + "h5": { + "titleNView": { + "type": "transparent" + } + }, + "navigationBarTitleText": "文章详情" + } + }, { + "path": "news/list/list", + "style": { + // #ifndef APP-PLUS + "enablePullDownRefresh": true + // #endif + // "navigationStyle": "custom" + } + }, { + "path": "news/search/search", + "style": { + "navigationBarTitleText": "搜索" + } + }, { + "path": "news/detail/detail", + "style": { + "app-plus": { + "titleNView": { + "buttons": [{ + "type": "share" + }], + "type": "transparent" + } + }, + "h5": { + "titleNView": { + "type": "transparent" + } + }, + "navigationBarTitleText": "文章详情" + } + }, { + "path": "ucenter/ucenter", + "style": { + "navigationStyle": "custom" + } + }, { + "path": "ucenter/settings/settings", + "style": { + "navigationBarTitleText": "设置" + } + }, { + "path": "ucenter/read-news-log/read-news-log", + "style": { + "navigationBarTitleText": "阅读记录", + "enablePullDownRefresh": true + } + } + // #ifdef APP-PLUS + , { + "path": "ucenter/about/about", + "style": { + "navigationBarTitleText": "关于", + "app-plus": { + "titleNView": { + "buttons": [{ + "type": "share" + }] + } + } + } + }, + + { + "path": "ucenter/invite/invite", + "style": { + "navigationStyle": "custom", + "enablePullDownRefresh": false + } + } + // #endif + ] + }, { + "root": "uni_modules/uni-feedback", + "pages": [{ + "path": "pages/opendb-feedback/opendb-feedback", + "style": { + "navigationBarTitleText": "意见反馈", + "enablePullDownRefresh": false + } + }] + }, { + "root": "uni_modules/uni-id-pages/pages", + "pages": [{ + "path": "userinfo/userinfo", + "style": { + "navigationBarTitleText": "个人资料" + } + }, { + "path": "userinfo/realname-verify/realname-verify", + "style": { + "enablePullDownRefresh": false, + "navigationBarTitleText": "实名认证" + } + }, { + "path": "login/login-withoutpwd" + }, { + "path": "login/login-withpwd" + }, { + "path": "userinfo/deactivate/deactivate", + "style": { + "navigationBarTitleText": "注销账号" + } + }, { + "path": "userinfo/bind-mobile/bind-mobile", + "style": { + "navigationBarTitleText": "绑定手机号码" + } + }, { + "path": "login/login-smscode", + "style": { + "navigationBarTitleText": "手机验证码登录" + } + }, { + "path": "register/register", + "style": { + "navigationBarTitleText": "注册" + } + }, { + "path": "retrieve/retrieve", + "style": { + "navigationBarTitleText": "重置密码" + } + }, { + "path": "common/webview/webview", + "style": { + "enablePullDownRefresh": false, + "navigationBarTitleText": "" + } + }, { + "path": "userinfo/change_pwd/change_pwd", + "style": { + "enablePullDownRefresh": false, + "navigationBarTitleText": "修改密码" + } + }, { + "path": "register/register-by-email", + "style": { + "navigationBarTitleText": "邮箱验证码注册" + } + }, { + "path": "retrieve/retrieve-by-email", + "style": { + "navigationBarTitleText": "通过邮箱重置密码" + } + }, { + "path": "userinfo/set-pwd/set-pwd", + "style": { + "enablePullDownRefresh": false, + "navigationBarTitleText": "设置密码" + } + } + // #ifdef H5 + , { + "path": "userinfo/cropImage/cropImage" + }, { + "path": "register/register-admin", + "style": { + "enablePullDownRefresh": false, + "navigationBarTitleText": "注册管理员账号" + } + } + // #endif + ] + }, { + "root": "uni_modules/uni-cms-article/pages", + "pages": [{ + "path": "list/list", + "style": { + "backgroundColor": "#FFFFFF", + "disableScroll": true + // "navigationStyle": "custom" + } + }, { + "path": "search/search", + "style": { + "navigationBarTitleText": "搜索" + } + }, { + "path": "detail/detail", + "style": { + "backgroundColor": "#FFFFFF", + "navigationBarTitleText": "详情" + } + }, { + "path": "detail/preview", + "style": { + "backgroundColor": "#FFFFFF", + "navigationBarTitleText": "预览" + } + }] + }, { + "root": "pages/ctms", + "pages": [{ + "path": "index/index", + "style": { + "navigationBarTitleText": "", + "navigationStyle": "custom", + "enablePullDownRefresh": true + } + }, { + "path": "login/login", + "style": { + "navigationBarTitleText": "登录" + } + }, { + "path": "login/loginSms", + "style": { + "navigationBarTitleText": "登陆" + } + }, { + "path": "login/reg", + "style": { + "navigationBarTitleText": "注册" + } + }, { + "path": "about/about", + "style": { + "navigationBarTitleText": "关于APP", + "enablePullDownRefresh": false + } + }, { + "path": "me/index", + "style": { + "navigationBarTitleText": "个人中心", + "enablePullDownRefresh": true + } + }, { + "path": "order/list/list", + "style": { + "navigationBarTitleText": "运单列表", + "enablePullDownRefresh": true + } + }, { + "path": "order/detail/detail", + "style": { + "navigationBarTitleText": "运单详情", + "enablePullDownRefresh": true, + "app-plus": { + "bounce": "none", + "titleNView": { + "buttons": [ + //#ifdef APP + { + "type": "share" + } + //#endif + ] + } + } + } + }, { + "path": "order/detail/check", + "style": { + "navigationBarTitleText": "运单验车详情", + "enablePullDownRefresh": true + } + + }, { + "path": "order/index/index", + "style": { + "navigationBarTitleText": "运单查询", + "enablePullDownRefresh": false + } + + }, { + "path": "order/create/create", + "style": { + "navigationBarTitleText": "在线下单", + "enablePullDownRefresh": false + } + }, { + "path": "orderpre/list/list", + "style": { + "navigationBarTitleText": "询价记录", + "enablePullDownRefresh": true + } + }, { + "path": "orderpre/detail/detail", + "style": { + "navigationBarTitleText": "询价详情", + "enablePullDownRefresh": true, + "app-plus": { + "bounce": "none", + "titleNView": { + "buttons": [ + //#ifdef APP + { + "type": "share" + } + //#endif + ] + } + } + } + }, { + "path": "orderpre/create/create", + "style": { + "navigationBarTitleText": "获取报价", + "enablePullDownRefresh": false + }, + "needLogin": false + }, { + "path": "news/list/list", + "style": { + "navigationBarTitleText": "资讯中心", + "enablePullDownRefresh": true + } + }, { + "path": "news/detail/detail", + "style": { + "navigationBarTitleText": "", + "enablePullDownRefresh": true, + "app-plus": { + "bounce": "none", + "titleNView": { + "buttons": [ + //#ifdef APP + { + "type": "share" + } + //#endif + ] + } + } + } + }, + { + "path": "order/yanche/yanche", + "style": { + "navigationBarTitleText": "验车上传" + } + }, + { + "path": "scan/scan", + "style": { + "navigationBarTitleText": "" + } + } + ] + }], + "globalStyle": { + // #ifdef H5 + "h5": { + "titleNView": true //为false的在H5界面不显示导航栏 + }, + // #endif + "navigationBarTextStyle": "black", + "navigationBarTitleText": "运车助手", + "navigationBarBackgroundColor": "#FFFFFF", + "backgroundColor": "#F8F8F8", + "enablePullDownRefresh": false, + // "maxWidth":375, + "rpxCalcMaxDeviceWidth": 375, + "rpxCalcBaseDeviceWidth": 375 + // "rpxCalcIncludeWidth":0 + }, + "tabBar": { + "borderStyle": "black", + "backgroundColor": "#333", + "color": "#8F8F94", + "selectedColor": "#f33e54", + "list": [{ + "pagePath": "pages/ctms/tabbar/index/index", + "iconPath": "static/img/tabbar/default/home.png", + "selectedIconPath": "static/img/tabbar/default/homeactive.png", + "text": "首页" + }, + { + "pagePath": "pages/ctms/tabbar/order/index", + "iconPath": "static/img/tabbar/default/guanzhu.png", + "selectedIconPath": "static/img/tabbar/default/guanzhuactive.png", + "text": "查单" + }, + { + "pagePath": "pages/ctms/tabbar/mid/index", + "iconPath": "static/img/tabbar/default/add.png", + "selectedIconPath": "static/img/tabbar/default/addactive.png" + }, + { + "pagePath": "pages/ctms/tabbar/notice/index", + "iconPath": "static/img/tabbar/default/news.png", + "selectedIconPath": "static/img/tabbar/default/newsactive.png", + "text": "消息" + }, + { + "pagePath": "pages/ctms/tabbar/me/index", + "iconPath": "static/img/tabbar/default/me.png", + "selectedIconPath": "static/img/tabbar/default/meactive.png", + "text": "我" + } + ] + }, + "uniIdRouter": { + //自动路由 + // "loginPage": "uni_modules/uni-id-pages/pages/login/login-withoutpwd", + "loginPage": "pages/ctms/login/loginSms", + + // 需要登录的页面路径 + "needLogin": [ + // 需要登录才可访问的页面列表,可以使用正则语法 + "uni_modules/uni-id-pages/pages/userinfo/userinfo", + "pages/uni-starter/.*", + "pages/ctms/order/.*", + //order目录下全部需要登陆 + "pages/ctms/orderpre/.*", + "pages/ctms/tabbar/order/.*", + "pages/ctms/me/.*" + ], + // 客户端未登录或登录状态过期的判断: uni_id_token失效 + "resToLogin": true + // 自动解析云对象及clientDB的错误码,如果是客户端token不正确或token过期则自动跳转配置的登录页面 + } +} \ No newline at end of file diff --git a/pages/common/textview/index.vue b/pages/common/textview/index.vue new file mode 100644 index 0000000..e9b05fb --- /dev/null +++ b/pages/common/textview/index.vue @@ -0,0 +1,43 @@ + + + + + diff --git a/pages/common/webview/index.vue b/pages/common/webview/index.vue new file mode 100644 index 0000000..7e8e8c1 --- /dev/null +++ b/pages/common/webview/index.vue @@ -0,0 +1,104 @@ + + + + \ No newline at end of file diff --git a/pages/ctms/about/about.vue b/pages/ctms/about/about.vue new file mode 100644 index 0000000..d7d7b8a --- /dev/null +++ b/pages/ctms/about/about.vue @@ -0,0 +1,270 @@ + + + + + \ No newline at end of file diff --git a/pages/ctms/index/index.vue b/pages/ctms/index/index.vue new file mode 100644 index 0000000..9f1ef10 --- /dev/null +++ b/pages/ctms/index/index.vue @@ -0,0 +1,148 @@ + + + + + + \ No newline at end of file diff --git a/pages/ctms/login/login.css b/pages/ctms/login/login.css new file mode 100644 index 0000000..9b2e0a5 --- /dev/null +++ b/pages/ctms/login/login.css @@ -0,0 +1,95 @@ +body { + /* background-color: #e6e6e60e; */ +} + +radio, +checkbox, +switch { + transform: scale(1.5); + margin-left: 1rem; + margin-right: 1rem; + margin-top: 36rpx; +} + +input { + margin-left: 1rem; + margin-right: 1rem; + margin-top: 66rpx; + border-bottom: 1upx #333 dashed; +} + +.content { + padding-bottom: 50rpx; +} + +.header { + text-align: center; +} + +.header>image { + width: 300rpx; + height: 300rpx; +} + +.uni-form { + width: 94%; + margin: 0rpx 3% 0 3%; +} + +.uni-form-item { + width: 100%; + display: inline-flex; + height: 96rpx; + line-height: 96rpx; +} + +.uni-form-item .title, +.uni-form-item .data {} + +/**不生效**/ +.uni-form-item .title { + width: 180rpx; + padding: 40rpx 20rpx; + text-align: justify; + text-align-last: justify; +} + +.uni-form .uni-btn-v { + margin-top: 50rpx; +} + +.radio { + width: auto; +} + +.btn { + margin: 40rpx 30rpx; +} + +.btn-success { + background-color: #09BB07; + color: #fff; +} + +.btn-error { + background-color: #fc0107; + color: #fff; +} + +.footer { + display: block; + /* width: 100%; */ + font-size: 1rem; + height: 1rem; + line-height: 1.5rem; + margin: 1.5rem; +} + +.help-block { + display: block; + width: 100%; + color: #666666; + font-size: 0.8rem; + line-height: 1rem; + margin: 1.5rem; +} \ No newline at end of file diff --git a/pages/ctms/login/login.vue b/pages/ctms/login/login.vue new file mode 100644 index 0000000..ae9dcf2 --- /dev/null +++ b/pages/ctms/login/login.vue @@ -0,0 +1,281 @@ + + + + + \ No newline at end of file diff --git a/pages/ctms/login/loginSms.vue b/pages/ctms/login/loginSms.vue new file mode 100644 index 0000000..f07d28d --- /dev/null +++ b/pages/ctms/login/loginSms.vue @@ -0,0 +1,391 @@ + + + + + \ No newline at end of file diff --git a/pages/ctms/login/reg.vue b/pages/ctms/login/reg.vue new file mode 100644 index 0000000..3ac2354 --- /dev/null +++ b/pages/ctms/login/reg.vue @@ -0,0 +1,347 @@ + + + + + \ No newline at end of file diff --git a/pages/ctms/me/index.vue b/pages/ctms/me/index.vue new file mode 100644 index 0000000..37673e6 --- /dev/null +++ b/pages/ctms/me/index.vue @@ -0,0 +1,261 @@ + + + + + \ No newline at end of file diff --git a/pages/ctms/news/detail/detail.css b/pages/ctms/news/detail/detail.css new file mode 100644 index 0000000..5cf0dd1 --- /dev/null +++ b/pages/ctms/news/detail/detail.css @@ -0,0 +1,50 @@ +.container { + overflow: hidden; + } + + .custom-cover { + flex: 1; + flex-direction: row; + position: relative; + } + + .cover-content { + position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 40px; + background-color: rgba($color: #000000, $alpha: 0.4); + display: flex; + flex-direction: row; + align-items: center; + padding-left: 15px; + font-size: 14px; + color: #fff; + } + + .card-actions { + display: flex; + flex-direction: row; + justify-content: space-around; + align-items: center; + height: 45px; + border-top: 1px #eee solid; + } + .card-actions-item { + display: flex; + flex-direction: row; + align-items: center; + } + .card-actions-item-text { + font-size: 12px; + color: #666; + margin-left: 5px; + } + .cover-image { + flex: 1; + height: 150px; + } + .no-border { + border-width: 0; + } \ No newline at end of file diff --git a/pages/ctms/news/detail/detail.vue b/pages/ctms/news/detail/detail.vue new file mode 100644 index 0000000..f9f36e2 --- /dev/null +++ b/pages/ctms/news/detail/detail.vue @@ -0,0 +1,360 @@ + + + + + \ No newline at end of file diff --git a/pages/ctms/news/list/list.css b/pages/ctms/news/list/list.css new file mode 100644 index 0000000..8c920fa --- /dev/null +++ b/pages/ctms/news/list/list.css @@ -0,0 +1,54 @@ +.container { + overflow: hidden; + } + + .custom-cover { + flex: 1; + flex-direction: row; + position: relative; + } + + .cover-content { + position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 40px; + background-color: rgba($color: #000000, $alpha: 0.4); + display: flex; + flex-direction: row; + align-items: center; + padding-left: 15px; + font-size: 14px; + color: #fff; + } + + .card-actions { + display: flex; + flex-direction: row; + justify-content: space-around; + align-items: center; + height: 45px; + border-top: 1px #eee solid; + } + .card-actions-item { + display: flex; + flex-direction: row; + align-items: center; + } + .card-actions-item-text { + font-size: 12px; + color: #666; + margin-left: 5px; + } + .cover-image { + flex: 1; + height: 150px; + } + .no-border { + border-width: 0; + } + + .pagination{ + margin:20px 20px; + } \ No newline at end of file diff --git a/pages/ctms/news/list/list.vue b/pages/ctms/news/list/list.vue new file mode 100644 index 0000000..1dfb472 --- /dev/null +++ b/pages/ctms/news/list/list.vue @@ -0,0 +1,310 @@ + + + + + \ No newline at end of file diff --git a/pages/ctms/order/create/create.vue b/pages/ctms/order/create/create.vue new file mode 100644 index 0000000..3349388 --- /dev/null +++ b/pages/ctms/order/create/create.vue @@ -0,0 +1,467 @@ + + + + + \ No newline at end of file diff --git a/pages/ctms/order/detail/check.vue b/pages/ctms/order/detail/check.vue new file mode 100644 index 0000000..bfebeb3 --- /dev/null +++ b/pages/ctms/order/detail/check.vue @@ -0,0 +1,409 @@ + + + + + \ No newline at end of file diff --git a/uni_modules/uni-easyinput/package.json b/uni_modules/uni-easyinput/package.json new file mode 100644 index 0000000..2939256 --- /dev/null +++ b/uni_modules/uni-easyinput/package.json @@ -0,0 +1,88 @@ +{ + "id": "uni-easyinput", + "displayName": "uni-easyinput 增强输入框", + "version": "1.1.19", + "description": "Easyinput 组件是对原生input组件的增强", + "keywords": [ + "uni-ui", + "uniui", + "input", + "uni-easyinput", + "输入框" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": [ + "uni-scss", + "uni-icons" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "n" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-easyinput/readme.md b/uni_modules/uni-easyinput/readme.md new file mode 100644 index 0000000..f1faf8f --- /dev/null +++ b/uni_modules/uni-easyinput/readme.md @@ -0,0 +1,11 @@ + + +### Easyinput 增强输入框 +> **组件名:uni-easyinput** +> 代码块: `uEasyinput` + + +easyinput 组件是对原生input组件的增强 ,是专门为配合表单组件[uni-forms](https://ext.dcloud.net.cn/plugin?id=2773)而设计的,easyinput 内置了边框,图标等,同时包含 input 所有功能 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-easyinput) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 \ No newline at end of file diff --git a/uni_modules/uni-fab/changelog.md b/uni_modules/uni-fab/changelog.md new file mode 100644 index 0000000..9bd4729 --- /dev/null +++ b/uni_modules/uni-fab/changelog.md @@ -0,0 +1,23 @@ +## 1.2.5(2023-03-29) +- 新增 pattern.icon 属性,可自定义图标 +## 1.2.4(2022-09-07) +小程序端由于 style 使用了对象导致报错,[详情](https://ask.dcloud.net.cn/question/152790?item_id=211778&rf=false) +## 1.2.3(2022-09-05) +- 修复 nvue 环境下,具有 tabBar 时,fab 组件下部位置无法正常获取 --window-bottom 的bug,详见:[https://ask.dcloud.net.cn/question/110638?notification_id=826310](https://ask.dcloud.net.cn/question/110638?notification_id=826310) +## 1.2.2(2021-12-29) +- 更新 组件依赖 +## 1.2.1(2021-11-19) +- 修复 阴影颜色不正确的bug +## 1.2.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-fab](https://uniapp.dcloud.io/component/uniui/uni-fab) +## 1.1.1(2021-11-09) +- 新增 提供组件设计资源,组件样式调整 +## 1.1.0(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.0.7(2021-05-12) +- 新增 组件示例地址 +## 1.0.6(2021-02-05) +- 调整为uni_modules目录规范 +- 优化 按钮背景色调整 +- 优化 兼容pc端 diff --git a/uni_modules/uni-fab/components/uni-fab/uni-fab.vue b/uni_modules/uni-fab/components/uni-fab/uni-fab.vue new file mode 100644 index 0000000..dfa65c1 --- /dev/null +++ b/uni_modules/uni-fab/components/uni-fab/uni-fab.vue @@ -0,0 +1,491 @@ + + + + + diff --git a/uni_modules/uni-fab/components/uni-fab/uni-fab.vue.bak b/uni_modules/uni-fab/components/uni-fab/uni-fab.vue.bak new file mode 100644 index 0000000..9df4dcd --- /dev/null +++ b/uni_modules/uni-fab/components/uni-fab/uni-fab.vue.bak @@ -0,0 +1,383 @@ + + + + + diff --git a/uni_modules/uni-fab/package.json b/uni_modules/uni-fab/package.json new file mode 100644 index 0000000..18c0810 --- /dev/null +++ b/uni_modules/uni-fab/package.json @@ -0,0 +1,84 @@ +{ + "id": "uni-fab", + "displayName": "uni-fab 悬浮按钮", + "version": "1.2.5", + "description": "悬浮按钮 fab button ,点击可展开一个图标按钮菜单。", + "keywords": [ + "uni-ui", + "uniui", + "按钮", + "悬浮按钮", + "fab" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": ["uni-scss","uni-icons"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} diff --git a/uni_modules/uni-fab/readme.md b/uni_modules/uni-fab/readme.md new file mode 100644 index 0000000..9a444e8 --- /dev/null +++ b/uni_modules/uni-fab/readme.md @@ -0,0 +1,9 @@ +## Fab 悬浮按钮 +> **组件名:uni-fab** +> 代码块: `uFab` + + +点击可展开一个图形按钮菜单 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-fab) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 \ No newline at end of file diff --git a/uni_modules/uni-fav/changelog.md b/uni_modules/uni-fav/changelog.md new file mode 100644 index 0000000..d8a08d4 --- /dev/null +++ b/uni_modules/uni-fav/changelog.md @@ -0,0 +1,19 @@ +## 1.2.1(2022-05-30) +- 新增 stat 属性 ,是否开启uni统计功能 +## 1.2.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-fav](https://uniapp.dcloud.io/component/uniui/uni-fav) +## 1.1.1(2021-08-24) +- 新增 支持国际化 +## 1.1.0(2021-07-13) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.0.6(2021-05-12) +- 新增 组件示例地址 +## 1.0.5(2021-04-21) +- 优化 添加依赖 uni-icons, 导入后自动下载依赖 +## 1.0.4(2021-02-05) +- 优化 组件引用关系,通过uni_modules引用组件 +## 1.0.3(2021-02-05) +- 优化 组件引用关系,通过uni_modules引用组件 +## 1.0.2(2021-02-05) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-fav/components/uni-fav/i18n/en.json b/uni_modules/uni-fav/components/uni-fav/i18n/en.json new file mode 100644 index 0000000..9a0759e --- /dev/null +++ b/uni_modules/uni-fav/components/uni-fav/i18n/en.json @@ -0,0 +1,4 @@ +{ + "uni-fav.collect": "collect", + "uni-fav.collected": "collected" +} diff --git a/uni_modules/uni-fav/components/uni-fav/i18n/index.js b/uni_modules/uni-fav/components/uni-fav/i18n/index.js new file mode 100644 index 0000000..de7509c --- /dev/null +++ b/uni_modules/uni-fav/components/uni-fav/i18n/index.js @@ -0,0 +1,8 @@ +import en from './en.json' +import zhHans from './zh-Hans.json' +import zhHant from './zh-Hant.json' +export default { + en, + 'zh-Hans': zhHans, + 'zh-Hant': zhHant +} diff --git a/uni_modules/uni-fav/components/uni-fav/i18n/zh-Hans.json b/uni_modules/uni-fav/components/uni-fav/i18n/zh-Hans.json new file mode 100644 index 0000000..67c89bf --- /dev/null +++ b/uni_modules/uni-fav/components/uni-fav/i18n/zh-Hans.json @@ -0,0 +1,4 @@ +{ + "uni-fav.collect": "收藏", + "uni-fav.collected": "已收藏" +} diff --git a/uni_modules/uni-fav/components/uni-fav/i18n/zh-Hant.json b/uni_modules/uni-fav/components/uni-fav/i18n/zh-Hant.json new file mode 100644 index 0000000..67c89bf --- /dev/null +++ b/uni_modules/uni-fav/components/uni-fav/i18n/zh-Hant.json @@ -0,0 +1,4 @@ +{ + "uni-fav.collect": "收藏", + "uni-fav.collected": "已收藏" +} diff --git a/uni_modules/uni-fav/components/uni-fav/uni-fav.vue b/uni_modules/uni-fav/components/uni-fav/uni-fav.vue new file mode 100644 index 0000000..d2c58df --- /dev/null +++ b/uni_modules/uni-fav/components/uni-fav/uni-fav.vue @@ -0,0 +1,161 @@ + + + + + diff --git a/uni_modules/uni-fav/package.json b/uni_modules/uni-fav/package.json new file mode 100644 index 0000000..cc14697 --- /dev/null +++ b/uni_modules/uni-fav/package.json @@ -0,0 +1,89 @@ +{ + "id": "uni-fav", + "displayName": "uni-fav 收藏按钮", + "version": "1.2.1", + "description": " Fav 收藏组件,可自定义颜色、大小。", + "keywords": [ + "fav", + "uni-ui", + "uniui", + "收藏" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": [ + "uni-scss", + "uni-icons" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} diff --git a/uni_modules/uni-fav/readme.md b/uni_modules/uni-fav/readme.md new file mode 100644 index 0000000..4de125d --- /dev/null +++ b/uni_modules/uni-fav/readme.md @@ -0,0 +1,10 @@ + + +## Fav 收藏按钮 +> **组件名:uni-fav** +> 代码块: `uFav` + +用于收藏功能,可点击切换选中、不选中的状态。 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-fav) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 \ No newline at end of file diff --git a/uni_modules/uni-feedback/changelog.md b/uni_modules/uni-feedback/changelog.md new file mode 100644 index 0000000..a2decbb --- /dev/null +++ b/uni_modules/uni-feedback/changelog.md @@ -0,0 +1,10 @@ +## 1.1.1(2024-03-26) +支持支付宝小程序云 +## 1.1.0(2022-07-13) +新增,应用[pages_init](https://uniapp.dcloud.io/plugin/publish.html#pages-init)当导入插件到项目工程时,自动合并本插件的页面路由到项目的pages.json +## 1.0.5(2022-07-13) +新增,应用[pages_init](https://uniapp.dcloud.io/plugin/publish.html#pages-init)当导入插件到项目工程时,自动合并本插件的页面路由到项目的pages.json +## 1.0.4(2021-09-26) +为了数据安全,`opendb-feedback`表的`permission`中`delete`,`update`的值默认为`false` +## 1.0.3(2021-08-26) +删除多余的云函数test2 \ No newline at end of file diff --git a/uni_modules/uni-feedback/js_sdk/validator/opendb-feedback.js b/uni_modules/uni-feedback/js_sdk/validator/opendb-feedback.js new file mode 100644 index 0000000..be3d330 --- /dev/null +++ b/uni_modules/uni-feedback/js_sdk/validator/opendb-feedback.js @@ -0,0 +1,98 @@ +// 表单校验规则由 schema2code 生成,不建议直接修改校验规则,而建议通过 schema2code 生成, 详情: https://uniapp.dcloud.net.cn/uniCloud/schema + + +const validator = { + "content": { + "rules": [ + { + "required": true + }, + { + "format": "string" + } + ], + "label": "留言内容/回复内容" + }, + "imgs": { + "rules": [ + { + "format": "array" + }, + { + "arrayType": "file" + }, + { + "maxLength": 6 + } + ], + "label": "图片列表" + }, + "contact": { + "rules": [ + { + "format": "string" + } + ], + "label": "联系人" + }, + "mobile": { + "rules": [ + { + "format": "string" + }, + { + "pattern": "^\\+?[0-9-]{3,20}$" + } + ], + "label": "联系电话" + } +} + +const enumConverter = {} + +function filterToWhere(filter, command) { + let where = {} + for (let field in filter) { + let { type, value } = filter[field] + switch (type) { + case "search": + if (typeof value === 'string' && value.length) { + where[field] = new RegExp(value) + } + break; + case "select": + if (value.length) { + let selectValue = [] + for (let s of value) { + selectValue.push(command.eq(s)) + } + where[field] = command.or(selectValue) + } + break; + case "range": + if (value.length) { + let gt = value[0] + let lt = value[1] + where[field] = command.and([command.gte(gt), command.lte(lt)]) + } + break; + case "date": + if (value.length) { + let [s, e] = value + let startDate = new Date(s) + let endDate = new Date(e) + where[field] = command.and([command.gte(startDate), command.lte(endDate)]) + } + break; + case "timestamp": + if (value.length) { + let [startDate, endDate] = value + where[field] = command.and([command.gte(startDate), command.lte(endDate)]) + } + break; + } + } + return where +} + +export { validator, enumConverter, filterToWhere } diff --git a/uni_modules/uni-feedback/package.json b/uni_modules/uni-feedback/package.json new file mode 100644 index 0000000..090a704 --- /dev/null +++ b/uni_modules/uni-feedback/package.json @@ -0,0 +1,90 @@ +{ + "id": "uni-feedback", + "displayName": "问题反馈页面模板", + "version": "1.1.1", + "description": "问题反馈页面模板,方便开发者快速搭建问题反馈界面", + "keywords": [ + "问题反馈页面模板" +], + "repository": "", + "engines": { + "HBuilderX": "^3.1.0" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "", + "type": "unicloud-template-page" + }, + "uni_modules": { + "dependencies": [ + "uni-forms", + "uni-file-picker", + "uni-easyinput", + "uni-load-more", + "uni-list", + "uni-fab", + "uni-link" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "u" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "u", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y", + "钉钉": "u", + "快手": "u", + "飞书": "u", + "京东": "u" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-feedback/pages/opendb-feedback/detail.vue b/uni_modules/uni-feedback/pages/opendb-feedback/detail.vue new file mode 100644 index 0000000..fa28fca --- /dev/null +++ b/uni_modules/uni-feedback/pages/opendb-feedback/detail.vue @@ -0,0 +1,113 @@ + + + + + diff --git a/uni_modules/uni-feedback/pages/opendb-feedback/edit.vue b/uni_modules/uni-feedback/pages/opendb-feedback/edit.vue new file mode 100644 index 0000000..bd65125 --- /dev/null +++ b/uni_modules/uni-feedback/pages/opendb-feedback/edit.vue @@ -0,0 +1,167 @@ + + + + + diff --git a/uni_modules/uni-feedback/pages/opendb-feedback/list.vue b/uni_modules/uni-feedback/pages/opendb-feedback/list.vue new file mode 100644 index 0000000..1131edb --- /dev/null +++ b/uni_modules/uni-feedback/pages/opendb-feedback/list.vue @@ -0,0 +1,70 @@ + + + + + diff --git a/uni_modules/uni-feedback/pages/opendb-feedback/opendb-feedback.vue b/uni_modules/uni-feedback/pages/opendb-feedback/opendb-feedback.vue new file mode 100644 index 0000000..f8bc77e --- /dev/null +++ b/uni_modules/uni-feedback/pages/opendb-feedback/opendb-feedback.vue @@ -0,0 +1,141 @@ + + + + + diff --git a/uni_modules/uni-feedback/readme.md b/uni_modules/uni-feedback/readme.md new file mode 100644 index 0000000..65d9192 --- /dev/null +++ b/uni_modules/uni-feedback/readme.md @@ -0,0 +1 @@ +这是一个问题反馈客户端插件,admin端插件:[https://ext.dcloud.net.cn/plugin?id=4992](https://ext.dcloud.net.cn/plugin?id=4992) \ No newline at end of file diff --git a/uni_modules/uni-feedback/uniCloud/database/opendb-feedback.schema.json b/uni_modules/uni-feedback/uniCloud/database/opendb-feedback.schema.json new file mode 100644 index 0000000..e13d874 --- /dev/null +++ b/uni_modules/uni-feedback/uniCloud/database/opendb-feedback.schema.json @@ -0,0 +1,72 @@ +{ + "bsonType": "object", + "required": ["content"], + "permission": { + "create": "auth.uid != null", + "read": true, + "delete": false, + "update": false + }, + "properties": { + "_id": { + "description": "ID,系统自动生成" + }, + "user_id": { + "bsonType": "string", + "description": "留言反馈用户ID\/回复留言用户ID,参考uni-id-users表", + "foreignKey": "uni-id-users._id", + "forceDefaultValue": { + "$env": "uid" + } + }, + "create_date": { + "bsonType": "timestamp", + "title": "留言时间\/回复留言时间", + "forceDefaultValue": { + "$env": "now" + } + }, + "content": { + "bsonType": "string", + "title": "留言内容\/回复内容", + "componentForEdit": { + "name": "textarea" + }, + "trim": "right" + }, + "imgs": { + "bsonType": "array", + "arrayType": "file", + "maxLength": 6, + "fileMediaType": "image", + "title": "图片列表" + }, + "is_reply": { + "bsonType": "bool", + "title": "是否是回复类型" + }, + "feedback_id": { + "bsonType": "string", + "title": "被回复留言ID" + }, + "contact": { + "bsonType": "string", + "title": "联系人", + "trim": "both" + }, + "mobile": { + "bsonType": "string", + "title": "联系电话", + "pattern": "^\\+?[0-9-]{3,20}$", + "trim": "both" + }, + "reply_count": { + "permission": { + "write": false + }, + "bsonType": "int", + "title": "被回复条数", + "defaultValue": 0 + } + } +} diff --git a/uni_modules/uni-file-picker/changelog.md b/uni_modules/uni-file-picker/changelog.md new file mode 100644 index 0000000..81e43b9 --- /dev/null +++ b/uni_modules/uni-file-picker/changelog.md @@ -0,0 +1,75 @@ +## 1.0.8(2024-03-20) +- 补充 删除文件时返回文件下标 +## 1.0.7(2024-02-21) +- 新增 微信小程序选择视频时改用chooseMedia,并返回视频缩略图 +## 1.0.6(2024-01-06) +- 新增 微信小程序不再调用chooseImage,而是调用chooseMedia +## 1.0.5(2024-01-03) +- 新增 上传文件至云存储携带本地文件名称 +## 1.0.4(2023-03-29) +- 修复 手动上传删除一个文件后不能再上传的bug +## 1.0.3(2022-12-19) +- 新增 sourceType 属性, 可以自定义图片和视频选择的来源 +## 1.0.2(2022-07-04) +- 修复 在uni-forms下样式不生效的bug +## 1.0.1(2021-11-23) +- 修复 参数为对象的情况下,url在某些情况显示错误的bug +## 1.0.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-file-picker](https://uniapp.dcloud.io/component/uniui/uni-file-picker) +## 0.2.16(2021-11-08) +- 修复 传入空对象 ,显示错误的Bug +## 0.2.15(2021-08-30) +- 修复 return-type="object" 时且存在v-model时,无法删除文件的Bug +## 0.2.14(2021-08-23) +- 新增 参数中返回 fileID 字段 +## 0.2.13(2021-08-23) +- 修复 腾讯云传入fileID 不能回显的bug +- 修复 选择图片后,不能放大的问题 +## 0.2.12(2021-08-17) +- 修复 由于 0.2.11 版本引起的不能回显图片的Bug +## 0.2.11(2021-08-16) +- 新增 clearFiles(index) 方法,可以手动删除指定文件 +- 修复 v-model 值设为 null 报错的Bug +## 0.2.10(2021-08-13) +- 修复 return-type="object" 时,无法删除文件的Bug +## 0.2.9(2021-08-03) +- 修复 auto-upload 属性失效的Bug +## 0.2.8(2021-07-31) +- 修复 fileExtname属性不指定值报错的Bug +## 0.2.7(2021-07-31) +- 修复 在某种场景下图片不回显的Bug +## 0.2.6(2021-07-30) +- 修复 return-type为object下,返回值不正确的Bug +## 0.2.5(2021-07-30) +- 修复(重要) H5 平台下如果和uni-forms组件一同使用导致页面卡死的问题 +## 0.2.3(2021-07-28) +- 优化 调整示例代码 +## 0.2.2(2021-07-27) +- 修复 vue3 下赋值错误的Bug +- 优化 h5平台下上传文件导致页面卡死的问题 +## 0.2.0(2021-07-13) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 0.1.1(2021-07-02) +- 修复 sourceType 缺少默认值导致 ios 无法选择文件 +## 0.1.0(2021-06-30) +- 优化 解耦与uniCloud的强绑定关系 ,如不绑定服务空间,默认autoUpload为false且不可更改 +## 0.0.11(2021-06-30) +- 修复 由 0.0.10 版本引发的 returnType 属性失效的问题 +## 0.0.10(2021-06-29) +- 优化 文件上传后进度条消失时机 +## 0.0.9(2021-06-29) +- 修复 在uni-forms 中,删除文件 ,获取的值不对的Bug +## 0.0.8(2021-06-15) +- 修复 删除文件时无法触发 v-model 的Bug +## 0.0.7(2021-05-12) +- 新增 组件示例地址 +## 0.0.6(2021-04-09) +- 修复 选择的文件非 file-extname 字段指定的扩展名报错的Bug +## 0.0.5(2021-04-09) +- 优化 更新组件示例 +## 0.0.4(2021-04-09) +- 优化 file-extname 字段支持字符串写法,多个扩展名需要用逗号分隔 +## 0.0.3(2021-02-05) +- 调整为uni_modules目录规范 +- 修复 微信小程序不指定 fileExtname 属性选择失败的Bug diff --git a/uni_modules/uni-file-picker/components/uni-file-picker/choose-and-upload-file.js b/uni_modules/uni-file-picker/components/uni-file-picker/choose-and-upload-file.js new file mode 100644 index 0000000..9c6bcdf --- /dev/null +++ b/uni_modules/uni-file-picker/components/uni-file-picker/choose-and-upload-file.js @@ -0,0 +1,287 @@ +'use strict'; + +const ERR_MSG_OK = 'chooseAndUploadFile:ok'; +const ERR_MSG_FAIL = 'chooseAndUploadFile:fail'; + +function chooseImage(opts) { + const { + count, + sizeType = ['original', 'compressed'], + sourceType, + extension + } = opts + return new Promise((resolve, reject) => { + // 微信由于旧接口不再维护,针对微信小程序平台改用chooseMedia接口 + // #ifdef MP-WEIXIN + uni.chooseMedia({ + count, + sizeType, + sourceType, + mediaType: ['image'], + extension, + success(res) { + res.tempFiles.forEach(item => { + item.path = item.tempFilePath; + }) + resolve(normalizeChooseAndUploadFileRes(res, 'image')); + }, + fail(res) { + reject({ + errMsg: res.errMsg.replace('chooseImage:fail', ERR_MSG_FAIL), + }); + }, + }) + // #endif + // #ifndef MP-WEIXIN + uni.chooseImage({ + count, + sizeType, + sourceType, + extension, + success(res) { + resolve(normalizeChooseAndUploadFileRes(res, 'image')); + }, + fail(res) { + reject({ + errMsg: res.errMsg.replace('chooseImage:fail', ERR_MSG_FAIL), + }); + }, + }); + // #endif + + }); +} + +function chooseVideo(opts) { + const { + count, + camera, + compressed, + maxDuration, + sourceType, + extension + } = opts; + return new Promise((resolve, reject) => { + // 微信由于旧接口不再维护,针对微信小程序平台改用chooseMedia接口 + // #ifdef MP-WEIXIN + uni.chooseMedia({ + count, + compressed, + maxDuration, + sourceType, + extension, + mediaType: ['video'], + success(res) { + const { + tempFiles, + } = res; + resolve(normalizeChooseAndUploadFileRes({ + errMsg: 'chooseVideo:ok', + tempFiles: tempFiles.map(item => { + return { + name: item.name || '', + path: item.tempFilePath, + thumbTempFilePath: item.thumbTempFilePath, + size:item.size, + type: (res.tempFile && res.tempFile.type) || '', + width:item.width, + height:item.height, + duration:item.duration, + fileType: 'video', + cloudPath: '', + } + }), + }, 'video')); + }, + fail(res) { + reject({ + errMsg: res.errMsg.replace('chooseVideo:fail', ERR_MSG_FAIL), + }); + }, + }) + // #endif + // #ifndef MP-WEIXIN + uni.chooseVideo({ + camera, + compressed, + maxDuration, + sourceType, + extension, + success(res) { + const { + tempFilePath, + duration, + size, + height, + width + } = res; + resolve(normalizeChooseAndUploadFileRes({ + errMsg: 'chooseVideo:ok', + tempFilePaths: [tempFilePath], + tempFiles: [{ + name: (res.tempFile && res.tempFile.name) || '', + path: tempFilePath, + size, + type: (res.tempFile && res.tempFile.type) || '', + width, + height, + duration, + fileType: 'video', + cloudPath: '', + }, ], + }, 'video')); + }, + fail(res) { + reject({ + errMsg: res.errMsg.replace('chooseVideo:fail', ERR_MSG_FAIL), + }); + }, + }); + // #endif + }); +} + +function chooseAll(opts) { + const { + count, + extension + } = opts; + return new Promise((resolve, reject) => { + let chooseFile = uni.chooseFile; + if (typeof wx !== 'undefined' && + typeof wx.chooseMessageFile === 'function') { + chooseFile = wx.chooseMessageFile; + } + if (typeof chooseFile !== 'function') { + return reject({ + errMsg: ERR_MSG_FAIL + ' 请指定 type 类型,该平台仅支持选择 image 或 video。', + }); + } + chooseFile({ + type: 'all', + count, + extension, + success(res) { + resolve(normalizeChooseAndUploadFileRes(res)); + }, + fail(res) { + reject({ + errMsg: res.errMsg.replace('chooseFile:fail', ERR_MSG_FAIL), + }); + }, + }); + }); +} + +function normalizeChooseAndUploadFileRes(res, fileType) { + res.tempFiles.forEach((item, index) => { + if (!item.name) { + item.name = item.path.substring(item.path.lastIndexOf('/') + 1); + } + if (fileType) { + item.fileType = fileType; + } + item.cloudPath = + Date.now() + '_' + index + item.name.substring(item.name.lastIndexOf('.')); + }); + if (!res.tempFilePaths) { + res.tempFilePaths = res.tempFiles.map((file) => file.path); + } + return res; +} + +function uploadCloudFiles(files, max = 5, onUploadProgress) { + files = JSON.parse(JSON.stringify(files)) + const len = files.length + let count = 0 + let self = this + return new Promise(resolve => { + while (count < max) { + next() + } + + function next() { + let cur = count++ + if (cur >= len) { + !files.find(item => !item.url && !item.errMsg) && resolve(files) + return + } + const fileItem = files[cur] + const index = self.files.findIndex(v => v.uuid === fileItem.uuid) + fileItem.url = '' + delete fileItem.errMsg + + uniCloud + .uploadFile({ + filePath: fileItem.path, + cloudPath: fileItem.cloudPath, + fileType: fileItem.fileType, + onUploadProgress: res => { + res.index = index + onUploadProgress && onUploadProgress(res) + } + }) + .then(res => { + fileItem.url = res.fileID + fileItem.index = index + if (cur < len) { + next() + } + }) + .catch(res => { + fileItem.errMsg = res.errMsg || res.message + fileItem.index = index + if (cur < len) { + next() + } + }) + } + }) +} + + + + + +function uploadFiles(choosePromise, { + onChooseFile, + onUploadProgress +}) { + return choosePromise + .then((res) => { + if (onChooseFile) { + const customChooseRes = onChooseFile(res); + if (typeof customChooseRes !== 'undefined') { + return Promise.resolve(customChooseRes).then((chooseRes) => typeof chooseRes === 'undefined' ? + res : chooseRes); + } + } + return res; + }) + .then((res) => { + if (res === false) { + return { + errMsg: ERR_MSG_OK, + tempFilePaths: [], + tempFiles: [], + }; + } + return res + }) +} + +function chooseAndUploadFile(opts = { + type: 'all' +}) { + if (opts.type === 'image') { + return uploadFiles(chooseImage(opts), opts); + } else if (opts.type === 'video') { + return uploadFiles(chooseVideo(opts), opts); + } + return uploadFiles(chooseAll(opts), opts); +} + +export { + chooseAndUploadFile, + uploadCloudFiles +}; diff --git a/uni_modules/uni-file-picker/components/uni-file-picker/uni-file-picker.vue b/uni_modules/uni-file-picker/components/uni-file-picker/uni-file-picker.vue new file mode 100644 index 0000000..fb83f63 --- /dev/null +++ b/uni_modules/uni-file-picker/components/uni-file-picker/uni-file-picker.vue @@ -0,0 +1,678 @@ + + + + + diff --git a/uni_modules/uni-file-picker/components/uni-file-picker/upload-file.vue b/uni_modules/uni-file-picker/components/uni-file-picker/upload-file.vue new file mode 100644 index 0000000..625d92e --- /dev/null +++ b/uni_modules/uni-file-picker/components/uni-file-picker/upload-file.vue @@ -0,0 +1,325 @@ + + + + + diff --git a/uni_modules/uni-file-picker/components/uni-file-picker/upload-image.vue b/uni_modules/uni-file-picker/components/uni-file-picker/upload-image.vue new file mode 100644 index 0000000..2a29bc2 --- /dev/null +++ b/uni_modules/uni-file-picker/components/uni-file-picker/upload-image.vue @@ -0,0 +1,292 @@ + + + + + diff --git a/uni_modules/uni-file-picker/components/uni-file-picker/utils.js b/uni_modules/uni-file-picker/components/uni-file-picker/utils.js new file mode 100644 index 0000000..1bc9259 --- /dev/null +++ b/uni_modules/uni-file-picker/components/uni-file-picker/utils.js @@ -0,0 +1,110 @@ +/** + * 获取文件名和后缀 + * @param {String} name + */ +export const get_file_ext = (name) => { + const last_len = name.lastIndexOf('.') + const len = name.length + return { + name: name.substring(0, last_len), + ext: name.substring(last_len + 1, len) + } +} + +/** + * 获取扩展名 + * @param {Array} fileExtname + */ +export const get_extname = (fileExtname) => { + if (!Array.isArray(fileExtname)) { + let extname = fileExtname.replace(/(\[|\])/g, '') + return extname.split(',') + } else { + return fileExtname + } + return [] +} + +/** + * 获取文件和检测是否可选 + */ +export const get_files_and_is_max = (res, _extname) => { + let filePaths = [] + let files = [] + if(!_extname || _extname.length === 0){ + return { + filePaths, + files + } + } + res.tempFiles.forEach(v => { + let fileFullName = get_file_ext(v.name) + const extname = fileFullName.ext.toLowerCase() + if (_extname.indexOf(extname) !== -1) { + files.push(v) + filePaths.push(v.path) + } + }) + if (files.length !== res.tempFiles.length) { + uni.showToast({ + title: `当前选择了${res.tempFiles.length}个文件 ,${res.tempFiles.length - files.length} 个文件格式不正确`, + icon: 'none', + duration: 5000 + }) + } + + return { + filePaths, + files + } +} + + +/** + * 获取图片信息 + * @param {Object} filepath + */ +export const get_file_info = (filepath) => { + return new Promise((resolve, reject) => { + uni.getImageInfo({ + src: filepath, + success(res) { + resolve(res) + }, + fail(err) { + reject(err) + } + }) + }) +} +/** + * 获取封装数据 + */ +export const get_file_data = async (files, type = 'image') => { + // 最终需要上传数据库的数据 + let fileFullName = get_file_ext(files.name) + const extname = fileFullName.ext.toLowerCase() + let filedata = { + name: files.name, + uuid: files.uuid, + extname: extname || '', + cloudPath: files.cloudPath, + fileType: files.fileType, + thumbTempFilePath: files.thumbTempFilePath, + url: files.path || files.path, + size: files.size, //单位是字节 + image: {}, + path: files.path, + video: {} + } + if (type === 'image') { + const imageinfo = await get_file_info(files.path) + delete filedata.video + filedata.image.width = imageinfo.width + filedata.image.height = imageinfo.height + filedata.image.location = imageinfo.path + } else { + delete filedata.image + } + return filedata +} diff --git a/uni_modules/uni-file-picker/package.json b/uni_modules/uni-file-picker/package.json new file mode 100644 index 0000000..004d330 --- /dev/null +++ b/uni_modules/uni-file-picker/package.json @@ -0,0 +1,83 @@ +{ + "id": "uni-file-picker", + "displayName": "uni-file-picker 文件选择上传", + "version": "1.0.8", + "description": "文件选择上传组件,可以选择图片、视频等任意文件并上传到当前绑定的服务空间", + "keywords": [ + "uni-ui", + "uniui", + "图片上传", + "文件上传" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": ["uni-scss"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "n" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} diff --git a/uni_modules/uni-file-picker/readme.md b/uni_modules/uni-file-picker/readme.md new file mode 100644 index 0000000..c8399a5 --- /dev/null +++ b/uni_modules/uni-file-picker/readme.md @@ -0,0 +1,11 @@ + +## FilePicker 文件选择上传 + +> **组件名:uni-file-picker** +> 代码块: `uFilePicker` + + +文件选择上传组件,可以选择图片、视频等任意文件并上传到当前绑定的服务空间 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-file-picker) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 \ No newline at end of file diff --git a/uni_modules/uni-forms/changelog.md b/uni_modules/uni-forms/changelog.md new file mode 100644 index 0000000..3d998bc --- /dev/null +++ b/uni_modules/uni-forms/changelog.md @@ -0,0 +1,94 @@ +## 1.4.10(2023-11-03) +- 优化 labelWidth 描述错误 +## 1.4.9(2023-02-10) +- 修复 required 参数无法动态绑定 +## 1.4.8(2022-08-23) +- 优化 根据 rules 自动添加 required 的问题 +## 1.4.7(2022-08-22) +- 修复 item 未设置 require 属性,rules 设置 require 后,星号也显示的 bug,详见:[https://ask.dcloud.net.cn/question/151540](https://ask.dcloud.net.cn/question/151540) +## 1.4.6(2022-07-13) +- 修复 model 需要校验的值没有声明对应字段时,导致第一次不触发校验的bug +## 1.4.5(2022-07-05) +- 新增 更多表单示例 +- 优化 子表单组件过期提示的问题 +- 优化 子表单组件uni-datetime-picker、uni-data-select、uni-data-picker的显示样式 +## 1.4.4(2022-07-04) +- 更新 删除组件日志 +## 1.4.3(2022-07-04) +- 修复 由 1.4.0 引发的 label 插槽不生效的bug +## 1.4.2(2022-07-04) +- 修复 子组件找不到 setValue 报错的bug +## 1.4.1(2022-07-04) +- 修复 uni-data-picker 在 uni-forms-item 中报错的bug +- 修复 uni-data-picker 在 uni-forms-item 中宽度不正确的bug +## 1.4.0(2022-06-30) +- 【重要】组件逻辑重构,部分用法用旧版本不兼容,请注意兼容问题 +- 【重要】组件使用 Provide/Inject 方式注入依赖,提供了自定义表单组件调用 uni-forms 校验表单的能力 +- 新增 model 属性,等同于原 value/modelValue 属性,旧属性即将废弃 +- 新增 validateTrigger 属性的 blur 值,仅 uni-easyinput 生效 +- 新增 onFieldChange 方法,可以对子表单进行校验,可替代binddata方法 +- 新增 子表单的 setRules 方法,配合自定义校验函数使用 +- 新增 uni-forms-item 的 setRules 方法,配置动态表单使用可动态更新校验规则 +- 优化 动态表单校验方式,废弃拼接name的方式 +## 1.3.3(2022-06-22) +- 修复 表单校验顺序无序问题 +## 1.3.2(2021-12-09) +- +## 1.3.1(2021-11-19) +- 修复 label 插槽不生效的bug +## 1.3.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-forms](https://uniapp.dcloud.io/component/uniui/uni-forms) +## 1.2.7(2021-08-13) +- 修复 没有添加校验规则的字段依然报错的Bug +## 1.2.6(2021-08-11) +- 修复 重置表单错误信息无法清除的问题 +## 1.2.5(2021-08-11) +- 优化 组件文档 +## 1.2.4(2021-08-11) +- 修复 表单验证只生效一次的问题 +## 1.2.3(2021-07-30) +- 优化 vue3下事件警告的问题 +## 1.2.2(2021-07-26) +- 修复 vue2 下条件编译导致destroyed生命周期失效的Bug +- 修复 1.2.1 引起的示例在小程序平台报错的Bug +## 1.2.1(2021-07-22) +- 修复 动态校验表单,默认值为空的情况下校验失效的Bug +- 修复 不指定name属性时,运行报错的Bug +- 优化 label默认宽度从65调整至70,使required为true且四字时不换行 +- 优化 组件示例,新增动态校验示例代码 +- 优化 组件文档,使用方式更清晰 +## 1.2.0(2021-07-13) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.1.2(2021-06-25) +- 修复 pattern 属性在微信小程序平台无效的问题 +## 1.1.1(2021-06-22) +- 修复 validate-trigger属性为submit且err-show-type属性为toast时不能弹出的Bug +## 1.1.0(2021-06-22) +- 修复 只写setRules方法而导致校验不生效的Bug +- 修复 由上个办法引发的错误提示文字错位的Bug +## 1.0.48(2021-06-21) +- 修复 不设置 label 属性 ,无法设置label插槽的问题 +## 1.0.47(2021-06-21) +- 修复 不设置label属性,label-width属性不生效的bug +- 修复 setRules 方法与rules属性冲突的问题 +## 1.0.46(2021-06-04) +- 修复 动态删减数据导致报错的问题 +## 1.0.45(2021-06-04) +- 新增 modelValue 属性 ,value 即将废弃 +## 1.0.44(2021-06-02) +- 新增 uni-forms-item 可以设置单独的 rules +- 新增 validate 事件增加 keepitem 参数,可以选择那些字段不过滤 +- 优化 submit 事件重命名为 validate +## 1.0.43(2021-05-12) +- 新增 组件示例地址 +## 1.0.42(2021-04-30) +- 修复 自定义检验器失效的问题 +## 1.0.41(2021-03-05) +- 更新 校验器 +- 修复 表单规则设置类型为 number 的情况下,值为0校验失败的Bug +## 1.0.40(2021-03-04) +- 修复 动态显示uni-forms-item的情况下,submit 方法获取值错误的Bug +## 1.0.39(2021-02-05) +- 调整为uni_modules目录规范 +- 修复 校验器传入 int 等类型 ,返回String类型的Bug diff --git a/uni_modules/uni-forms/components/uni-forms-item/uni-forms-item.vue b/uni_modules/uni-forms/components/uni-forms-item/uni-forms-item.vue new file mode 100644 index 0000000..0aef9cc --- /dev/null +++ b/uni_modules/uni-forms/components/uni-forms-item/uni-forms-item.vue @@ -0,0 +1,627 @@ + + + + + diff --git a/uni_modules/uni-forms/components/uni-forms/uni-forms.vue b/uni_modules/uni-forms/components/uni-forms/uni-forms.vue new file mode 100644 index 0000000..9bb9ae7 --- /dev/null +++ b/uni_modules/uni-forms/components/uni-forms/uni-forms.vue @@ -0,0 +1,397 @@ + + + + + diff --git a/uni_modules/uni-forms/components/uni-forms/utils.js b/uni_modules/uni-forms/components/uni-forms/utils.js new file mode 100644 index 0000000..6da2421 --- /dev/null +++ b/uni_modules/uni-forms/components/uni-forms/utils.js @@ -0,0 +1,293 @@ +/** + * 简单处理对象拷贝 + * @param {Obejct} 被拷贝对象 + * @@return {Object} 拷贝对象 + */ +export const deepCopy = (val) => { + return JSON.parse(JSON.stringify(val)) +} +/** + * 过滤数字类型 + * @param {String} format 数字类型 + * @@return {Boolean} 返回是否为数字类型 + */ +export const typeFilter = (format) => { + return format === 'int' || format === 'double' || format === 'number' || format === 'timestamp'; +} + +/** + * 把 value 转换成指定的类型,用于处理初始值,原因是初始值需要入库不能为 undefined + * @param {String} key 字段名 + * @param {any} value 字段值 + * @param {Object} rules 表单校验规则 + */ +export const getValue = (key, value, rules) => { + const isRuleNumType = rules.find(val => val.format && typeFilter(val.format)); + const isRuleBoolType = rules.find(val => (val.format && val.format === 'boolean') || val.format === 'bool'); + // 输入类型为 number + if (!!isRuleNumType) { + if (!value && value !== 0) { + value = null + } else { + value = isNumber(Number(value)) ? Number(value) : value + } + } + + // 输入类型为 boolean + if (!!isRuleBoolType) { + value = isBoolean(value) ? value : false + } + + return value; +} + +/** + * 获取表单数据 + * @param {String|Array} name 真实名称,需要使用 realName 获取 + * @param {Object} data 原始数据 + * @param {any} value 需要设置的值 + */ +export const setDataValue = (field, formdata, value) => { + formdata[field] = value + return value || '' +} + +/** + * 获取表单数据 + * @param {String|Array} field 真实名称,需要使用 realName 获取 + * @param {Object} data 原始数据 + */ +export const getDataValue = (field, data) => { + return objGet(data, field) +} + +/** + * 获取表单类型 + * @param {String|Array} field 真实名称,需要使用 realName 获取 + */ +export const getDataValueType = (field, data) => { + const value = getDataValue(field, data) + return { + type: type(value), + value + } +} + +/** + * 获取表单可用的真实name + * @param {String|Array} name 表单name + * @@return {String} 表单可用的真实name + */ +export const realName = (name, data = {}) => { + const base_name = _basePath(name) + if (typeof base_name === 'object' && Array.isArray(base_name) && base_name.length > 1) { + const realname = base_name.reduce((a, b) => a += `#${b}`, '_formdata_') + return realname + } + return base_name[0] || name +} + +/** + * 判断是否表单可用的真实name + * @param {String|Array} name 表单name + * @@return {String} 表单可用的真实name + */ +export const isRealName = (name) => { + const reg = /^_formdata_#*/ + return reg.test(name) +} + +/** + * 获取表单数据的原始格式 + * @@return {Object|Array} object 需要解析的数据 + */ +export const rawData = (object = {}, name) => { + let newData = JSON.parse(JSON.stringify(object)) + let formData = {} + for(let i in newData){ + let path = name2arr(i) + objSet(formData,path,newData[i]) + } + return formData +} + +/** + * 真实name还原为 array + * @param {*} name + */ +export const name2arr = (name) => { + let field = name.replace('_formdata_#', '') + field = field.split('#').map(v => (isNumber(v) ? Number(v) : v)) + return field +} + +/** + * 对象中设置值 + * @param {Object|Array} object 源数据 + * @param {String| Array} path 'a.b.c' 或 ['a',0,'b','c'] + * @param {String} value 需要设置的值 + */ +export const objSet = (object, path, value) => { + if (typeof object !== 'object') return object; + _basePath(path).reduce((o, k, i, _) => { + if (i === _.length - 1) { + // 若遍历结束直接赋值 + o[k] = value + return null + } else if (k in o) { + // 若存在对应路径,则返回找到的对象,进行下一次遍历 + return o[k] + } else { + // 若不存在对应路径,则创建对应对象,若下一路径是数字,新对象赋值为空数组,否则赋值为空对象 + o[k] = /^[0-9]{1,}$/.test(_[i + 1]) ? [] : {} + return o[k] + } + }, object) + // 返回object + return object; +} + +// 处理 path, path有三种形式:'a[0].b.c'、'a.0.b.c' 和 ['a','0','b','c'],需要统一处理成数组,便于后续使用 +function _basePath(path) { + // 若是数组,则直接返回 + if (Array.isArray(path)) return path + // 若有 '[',']',则替换成将 '[' 替换成 '.',去掉 ']' + return path.replace(/\[/g, '.').replace(/\]/g, '').split('.') +} + +/** + * 从对象中获取值 + * @param {Object|Array} object 源数据 + * @param {String| Array} path 'a.b.c' 或 ['a',0,'b','c'] + * @param {String} defaultVal 如果无法从调用链中获取值的默认值 + */ +export const objGet = (object, path, defaultVal = 'undefined') => { + // 先将path处理成统一格式 + let newPath = _basePath(path) + // 递归处理,返回最后结果 + let val = newPath.reduce((o, k) => { + return (o || {})[k] + }, object); + return !val || val !== undefined ? val : defaultVal +} + + +/** + * 是否为 number 类型 + * @param {any} num 需要判断的值 + * @return {Boolean} 是否为 number + */ +export const isNumber = (num) => { + return !isNaN(Number(num)) +} + +/** + * 是否为 boolean 类型 + * @param {any} bool 需要判断的值 + * @return {Boolean} 是否为 boolean + */ +export const isBoolean = (bool) => { + return (typeof bool === 'boolean') +} +/** + * 是否有必填字段 + * @param {Object} rules 规则 + * @return {Boolean} 是否有必填字段 + */ +export const isRequiredField = (rules) => { + let isNoField = false; + for (let i = 0; i < rules.length; i++) { + const ruleData = rules[i]; + if (ruleData.required) { + isNoField = true; + break; + } + } + return isNoField; +} + + +/** + * 获取数据类型 + * @param {Any} obj 需要获取数据类型的值 + */ +export const type = (obj) => { + var class2type = {}; + + // 生成class2type映射 + "Boolean Number String Function Array Date RegExp Object Error".split(" ").map(function(item, index) { + class2type["[object " + item + "]"] = item.toLowerCase(); + }) + if (obj == null) { + return obj + ""; + } + return typeof obj === "object" || typeof obj === "function" ? + class2type[Object.prototype.toString.call(obj)] || "object" : + typeof obj; +} + +/** + * 判断两个值是否相等 + * @param {any} a 值 + * @param {any} b 值 + * @return {Boolean} 是否相等 + */ +export const isEqual = (a, b) => { + //如果a和b本来就全等 + if (a === b) { + //判断是否为0和-0 + return a !== 0 || 1 / a === 1 / b; + } + //判断是否为null和undefined + if (a == null || b == null) { + return a === b; + } + //接下来判断a和b的数据类型 + var classNameA = toString.call(a), + classNameB = toString.call(b); + //如果数据类型不相等,则返回false + if (classNameA !== classNameB) { + return false; + } + //如果数据类型相等,再根据不同数据类型分别判断 + switch (classNameA) { + case '[object RegExp]': + case '[object String]': + //进行字符串转换比较 + return '' + a === '' + b; + case '[object Number]': + //进行数字转换比较,判断是否为NaN + if (+a !== +a) { + return +b !== +b; + } + //判断是否为0或-0 + return +a === 0 ? 1 / +a === 1 / b : +a === +b; + case '[object Date]': + case '[object Boolean]': + return +a === +b; + } + //如果是对象类型 + if (classNameA == '[object Object]') { + //获取a和b的属性长度 + var propsA = Object.getOwnPropertyNames(a), + propsB = Object.getOwnPropertyNames(b); + if (propsA.length != propsB.length) { + return false; + } + for (var i = 0; i < propsA.length; i++) { + var propName = propsA[i]; + //如果对应属性对应值不相等,则返回false + if (a[propName] !== b[propName]) { + return false; + } + } + return true; + } + //如果是数组类型 + if (classNameA == '[object Array]') { + if (a.toString() == b.toString()) { + return true; + } + return false; + } +} diff --git a/uni_modules/uni-forms/components/uni-forms/validate.js b/uni_modules/uni-forms/components/uni-forms/validate.js new file mode 100644 index 0000000..1834c6c --- /dev/null +++ b/uni_modules/uni-forms/components/uni-forms/validate.js @@ -0,0 +1,486 @@ +var pattern = { + email: /^\S+?@\S+?\.\S+?$/, + idcard: /^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/, + url: new RegExp( + "^(?!mailto:)(?:(?:http|https|ftp)://|//)(?:\\S+(?::\\S*)?@)?(?:(?:(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[0-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-*)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-*)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))|localhost)(?::\\d{2,5})?(?:(/|\\?|#)[^\\s]*)?$", + 'i') +}; + +const FORMAT_MAPPING = { + "int": 'integer', + "bool": 'boolean', + "double": 'number', + "long": 'number', + "password": 'string' + // "fileurls": 'array' +} + +function formatMessage(args, resources = '') { + var defaultMessage = ['label'] + defaultMessage.forEach((item) => { + if (args[item] === undefined) { + args[item] = '' + } + }) + + let str = resources + for (let key in args) { + let reg = new RegExp('{' + key + '}') + str = str.replace(reg, args[key]) + } + return str +} + +function isEmptyValue(value, type) { + if (value === undefined || value === null) { + return true; + } + + if (typeof value === 'string' && !value) { + return true; + } + + if (Array.isArray(value) && !value.length) { + return true; + } + + if (type === 'object' && !Object.keys(value).length) { + return true; + } + + return false; +} + +const types = { + integer(value) { + return types.number(value) && parseInt(value, 10) === value; + }, + string(value) { + return typeof value === 'string'; + }, + number(value) { + if (isNaN(value)) { + return false; + } + return typeof value === 'number'; + }, + "boolean": function(value) { + return typeof value === 'boolean'; + }, + "float": function(value) { + return types.number(value) && !types.integer(value); + }, + array(value) { + return Array.isArray(value); + }, + object(value) { + return typeof value === 'object' && !types.array(value); + }, + date(value) { + return value instanceof Date; + }, + timestamp(value) { + if (!this.integer(value) || Math.abs(value).toString().length > 16) { + return false + } + return true; + }, + file(value) { + return typeof value.url === 'string'; + }, + email(value) { + return typeof value === 'string' && !!value.match(pattern.email) && value.length < 255; + }, + url(value) { + return typeof value === 'string' && !!value.match(pattern.url); + }, + pattern(reg, value) { + try { + return new RegExp(reg).test(value); + } catch (e) { + return false; + } + }, + method(value) { + return typeof value === 'function'; + }, + idcard(value) { + return typeof value === 'string' && !!value.match(pattern.idcard); + }, + 'url-https'(value) { + return this.url(value) && value.startsWith('https://'); + }, + 'url-scheme'(value) { + return value.startsWith('://'); + }, + 'url-web'(value) { + return false; + } +} + +class RuleValidator { + + constructor(message) { + this._message = message + } + + async validateRule(fieldKey, fieldValue, value, data, allData) { + var result = null + + let rules = fieldValue.rules + + let hasRequired = rules.findIndex((item) => { + return item.required + }) + if (hasRequired < 0) { + if (value === null || value === undefined) { + return result + } + if (typeof value === 'string' && !value.length) { + return result + } + } + + var message = this._message + + if (rules === undefined) { + return message['default'] + } + + for (var i = 0; i < rules.length; i++) { + let rule = rules[i] + let vt = this._getValidateType(rule) + + Object.assign(rule, { + label: fieldValue.label || `["${fieldKey}"]` + }) + + if (RuleValidatorHelper[vt]) { + result = RuleValidatorHelper[vt](rule, value, message) + if (result != null) { + break + } + } + + if (rule.validateExpr) { + let now = Date.now() + let resultExpr = rule.validateExpr(value, allData, now) + if (resultExpr === false) { + result = this._getMessage(rule, rule.errorMessage || this._message['default']) + break + } + } + + if (rule.validateFunction) { + result = await this.validateFunction(rule, value, data, allData, vt) + if (result !== null) { + break + } + } + } + + if (result !== null) { + result = message.TAG + result + } + + return result + } + + async validateFunction(rule, value, data, allData, vt) { + let result = null + try { + let callbackMessage = null + const res = await rule.validateFunction(rule, value, allData || data, (message) => { + callbackMessage = message + }) + if (callbackMessage || (typeof res === 'string' && res) || res === false) { + result = this._getMessage(rule, callbackMessage || res, vt) + } + } catch (e) { + result = this._getMessage(rule, e.message, vt) + } + return result + } + + _getMessage(rule, message, vt) { + return formatMessage(rule, message || rule.errorMessage || this._message[vt] || message['default']) + } + + _getValidateType(rule) { + var result = '' + if (rule.required) { + result = 'required' + } else if (rule.format) { + result = 'format' + } else if (rule.arrayType) { + result = 'arrayTypeFormat' + } else if (rule.range) { + result = 'range' + } else if (rule.maximum !== undefined || rule.minimum !== undefined) { + result = 'rangeNumber' + } else if (rule.maxLength !== undefined || rule.minLength !== undefined) { + result = 'rangeLength' + } else if (rule.pattern) { + result = 'pattern' + } else if (rule.validateFunction) { + result = 'validateFunction' + } + return result + } +} + +const RuleValidatorHelper = { + required(rule, value, message) { + if (rule.required && isEmptyValue(value, rule.format || typeof value)) { + return formatMessage(rule, rule.errorMessage || message.required); + } + + return null + }, + + range(rule, value, message) { + const { + range, + errorMessage + } = rule; + + let list = new Array(range.length); + for (let i = 0; i < range.length; i++) { + const item = range[i]; + if (types.object(item) && item.value !== undefined) { + list[i] = item.value; + } else { + list[i] = item; + } + } + + let result = false + if (Array.isArray(value)) { + result = (new Set(value.concat(list)).size === list.length); + } else { + if (list.indexOf(value) > -1) { + result = true; + } + } + + if (!result) { + return formatMessage(rule, errorMessage || message['enum']); + } + + return null + }, + + rangeNumber(rule, value, message) { + if (!types.number(value)) { + return formatMessage(rule, rule.errorMessage || message.pattern.mismatch); + } + + let { + minimum, + maximum, + exclusiveMinimum, + exclusiveMaximum + } = rule; + let min = exclusiveMinimum ? value <= minimum : value < minimum; + let max = exclusiveMaximum ? value >= maximum : value > maximum; + + if (minimum !== undefined && min) { + return formatMessage(rule, rule.errorMessage || message['number'][exclusiveMinimum ? + 'exclusiveMinimum' : 'minimum' + ]) + } else if (maximum !== undefined && max) { + return formatMessage(rule, rule.errorMessage || message['number'][exclusiveMaximum ? + 'exclusiveMaximum' : 'maximum' + ]) + } else if (minimum !== undefined && maximum !== undefined && (min || max)) { + return formatMessage(rule, rule.errorMessage || message['number'].range) + } + + return null + }, + + rangeLength(rule, value, message) { + if (!types.string(value) && !types.array(value)) { + return formatMessage(rule, rule.errorMessage || message.pattern.mismatch); + } + + let min = rule.minLength; + let max = rule.maxLength; + let val = value.length; + + if (min !== undefined && val < min) { + return formatMessage(rule, rule.errorMessage || message['length'].minLength) + } else if (max !== undefined && val > max) { + return formatMessage(rule, rule.errorMessage || message['length'].maxLength) + } else if (min !== undefined && max !== undefined && (val < min || val > max)) { + return formatMessage(rule, rule.errorMessage || message['length'].range) + } + + return null + }, + + pattern(rule, value, message) { + if (!types['pattern'](rule.pattern, value)) { + return formatMessage(rule, rule.errorMessage || message.pattern.mismatch); + } + + return null + }, + + format(rule, value, message) { + var customTypes = Object.keys(types); + var format = FORMAT_MAPPING[rule.format] ? FORMAT_MAPPING[rule.format] : (rule.format || rule.arrayType); + + if (customTypes.indexOf(format) > -1) { + if (!types[format](value)) { + return formatMessage(rule, rule.errorMessage || message.typeError); + } + } + + return null + }, + + arrayTypeFormat(rule, value, message) { + if (!Array.isArray(value)) { + return formatMessage(rule, rule.errorMessage || message.typeError); + } + + for (let i = 0; i < value.length; i++) { + const element = value[i]; + let formatResult = this.format(rule, element, message) + if (formatResult !== null) { + return formatResult + } + } + + return null + } +} + +class SchemaValidator extends RuleValidator { + + constructor(schema, options) { + super(SchemaValidator.message); + + this._schema = schema + this._options = options || null + } + + updateSchema(schema) { + this._schema = schema + } + + async validate(data, allData) { + let result = this._checkFieldInSchema(data) + if (!result) { + result = await this.invokeValidate(data, false, allData) + } + return result.length ? result[0] : null + } + + async validateAll(data, allData) { + let result = this._checkFieldInSchema(data) + if (!result) { + result = await this.invokeValidate(data, true, allData) + } + return result + } + + async validateUpdate(data, allData) { + let result = this._checkFieldInSchema(data) + if (!result) { + result = await this.invokeValidateUpdate(data, false, allData) + } + return result.length ? result[0] : null + } + + async invokeValidate(data, all, allData) { + let result = [] + let schema = this._schema + for (let key in schema) { + let value = schema[key] + let errorMessage = await this.validateRule(key, value, data[key], data, allData) + if (errorMessage != null) { + result.push({ + key, + errorMessage + }) + if (!all) break + } + } + return result + } + + async invokeValidateUpdate(data, all, allData) { + let result = [] + for (let key in data) { + let errorMessage = await this.validateRule(key, this._schema[key], data[key], data, allData) + if (errorMessage != null) { + result.push({ + key, + errorMessage + }) + if (!all) break + } + } + return result + } + + _checkFieldInSchema(data) { + var keys = Object.keys(data) + var keys2 = Object.keys(this._schema) + if (new Set(keys.concat(keys2)).size === keys2.length) { + return '' + } + + var noExistFields = keys.filter((key) => { + return keys2.indexOf(key) < 0; + }) + var errorMessage = formatMessage({ + field: JSON.stringify(noExistFields) + }, SchemaValidator.message.TAG + SchemaValidator.message['defaultInvalid']) + return [{ + key: 'invalid', + errorMessage + }] + } +} + +function Message() { + return { + TAG: "", + default: '验证错误', + defaultInvalid: '提交的字段{field}在数据库中并不存在', + validateFunction: '验证无效', + required: '{label}必填', + 'enum': '{label}超出范围', + timestamp: '{label}格式无效', + whitespace: '{label}不能为空', + typeError: '{label}类型无效', + date: { + format: '{label}日期{value}格式无效', + parse: '{label}日期无法解析,{value}无效', + invalid: '{label}日期{value}无效' + }, + length: { + minLength: '{label}长度不能少于{minLength}', + maxLength: '{label}长度不能超过{maxLength}', + range: '{label}必须介于{minLength}和{maxLength}之间' + }, + number: { + minimum: '{label}不能小于{minimum}', + maximum: '{label}不能大于{maximum}', + exclusiveMinimum: '{label}不能小于等于{minimum}', + exclusiveMaximum: '{label}不能大于等于{maximum}', + range: '{label}必须介于{minimum}and{maximum}之间' + }, + pattern: { + mismatch: '{label}格式不匹配' + } + }; +} + + +SchemaValidator.message = new Message(); + +export default SchemaValidator diff --git a/uni_modules/uni-forms/package.json b/uni_modules/uni-forms/package.json new file mode 100644 index 0000000..464b4e6 --- /dev/null +++ b/uni_modules/uni-forms/package.json @@ -0,0 +1,88 @@ +{ + "id": "uni-forms", + "displayName": "uni-forms 表单", + "version": "1.4.10", + "description": "由输入框、选择器、单选框、多选框等控件组成,用以收集、校验、提交数据", + "keywords": [ + "uni-ui", + "表单", + "校验", + "表单校验", + "表单验证" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": [ + "uni-scss", + "uni-icons" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y", + "京东": "u" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} diff --git a/uni_modules/uni-forms/readme.md b/uni_modules/uni-forms/readme.md new file mode 100644 index 0000000..63d5a04 --- /dev/null +++ b/uni_modules/uni-forms/readme.md @@ -0,0 +1,23 @@ + + +## Forms 表单 + +> **组件名:uni-forms** +> 代码块: `uForms`、`uni-forms-item` +> 关联组件:`uni-forms-item`、`uni-easyinput`、`uni-data-checkbox`、`uni-group`。 + + +uni-app的内置组件已经有了 `
`组件,用于提交表单内容。 + +然而几乎每个表单都需要做表单验证,为了方便做表单验证,减少重复开发,`uni ui` 又基于 ``组件封装了 ``组件,内置了表单验证功能。 + +`` 提供了 `rules`属性来描述校验规则、``子组件来包裹具体的表单项,以及给原生或三方组件提供了 `binddata()` 来设置表单值。 + +每个要校验的表单项,不管input还是checkbox,都必须放在``组件中,且一个``组件只能放置一个表单项。 + +``组件内部预留了显示error message的区域,默认是在表单项的底部。 + +另外,``组件下面的各个表单项,可以通过``包裹为不同的分组。同一``下的不同表单项目将聚拢在一起,同其他group保持垂直间距。``仅影响视觉效果。 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-forms) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 \ No newline at end of file diff --git a/uni_modules/uni-goods-nav/changelog.md b/uni_modules/uni-goods-nav/changelog.md new file mode 100644 index 0000000..c6264c6 --- /dev/null +++ b/uni_modules/uni-goods-nav/changelog.md @@ -0,0 +1,18 @@ +## 1.2.1(2022-05-30) +- 新增 stat属性,是否开启uni统计功能 +## 1.2.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-goods-nav](https://uniapp.dcloud.io/component/uniui/uni-goods-nav) +## 1.1.1(2021-08-24) +- 新增 支持国际化 +## 1.1.0(2021-07-13) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.0.7(2021-05-12) +- 新增 组件示例地址 +## 1.0.6(2021-04-21) +- 优化 添加依赖 uni-icons, 导入后自动下载依赖 +## 1.0.5(2021-02-05) +- 优化 组件引用关系,通过uni_modules引用组件 + +## 1.0.4(2021-02-05) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/en.json b/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/en.json new file mode 100644 index 0000000..dcdba41 --- /dev/null +++ b/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/en.json @@ -0,0 +1,6 @@ +{ + "uni-goods-nav.options.shop": "shop", + "uni-goods-nav.options.cart": "cart", + "uni-goods-nav.buttonGroup.addToCart": "add to cart", + "uni-goods-nav.buttonGroup.buyNow": "buy now" +} diff --git a/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/index.js b/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/index.js new file mode 100644 index 0000000..de7509c --- /dev/null +++ b/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/index.js @@ -0,0 +1,8 @@ +import en from './en.json' +import zhHans from './zh-Hans.json' +import zhHant from './zh-Hant.json' +export default { + en, + 'zh-Hans': zhHans, + 'zh-Hant': zhHant +} diff --git a/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/zh-Hans.json b/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/zh-Hans.json new file mode 100644 index 0000000..48ee344 --- /dev/null +++ b/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/zh-Hans.json @@ -0,0 +1,6 @@ +{ + "uni-goods-nav.options.shop": "店铺", + "uni-goods-nav.options.cart": "购物车", + "uni-goods-nav.buttonGroup.addToCart": "加入购物车", + "uni-goods-nav.buttonGroup.buyNow": "立即购买" +} diff --git a/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/zh-Hant.json b/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/zh-Hant.json new file mode 100644 index 0000000..d0a0255 --- /dev/null +++ b/uni_modules/uni-goods-nav/components/uni-goods-nav/i18n/zh-Hant.json @@ -0,0 +1,6 @@ +{ + "uni-goods-nav.options.shop": "店鋪", + "uni-goods-nav.options.cart": "購物車", + "uni-goods-nav.buttonGroup.addToCart": "加入購物車", + "uni-goods-nav.buttonGroup.buyNow": "立即購買" +} diff --git a/uni_modules/uni-goods-nav/components/uni-goods-nav/uni-goods-nav.vue b/uni_modules/uni-goods-nav/components/uni-goods-nav/uni-goods-nav.vue new file mode 100644 index 0000000..8a16b17 --- /dev/null +++ b/uni_modules/uni-goods-nav/components/uni-goods-nav/uni-goods-nav.vue @@ -0,0 +1,229 @@ + + + + + diff --git a/uni_modules/uni-goods-nav/package.json b/uni_modules/uni-goods-nav/package.json new file mode 100644 index 0000000..636e45e --- /dev/null +++ b/uni_modules/uni-goods-nav/package.json @@ -0,0 +1,88 @@ +{ + "id": "uni-goods-nav", + "displayName": "uni-goods-nav 商品导航", + "version": "1.2.1", + "description": "商品导航组件主要用于电商类应用底部导航,可自定义加入购物车,购买等操作", + "keywords": [ + "uni-ui", + "uniui", + "商品导航" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": [ + "uni-scss", + "uni-icons" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} diff --git a/uni_modules/uni-goods-nav/readme.md b/uni_modules/uni-goods-nav/readme.md new file mode 100644 index 0000000..07df93f --- /dev/null +++ b/uni_modules/uni-goods-nav/readme.md @@ -0,0 +1,10 @@ + + +## GoodsNav 商品导航 +> **组件名:uni-goods-nav** +> 代码块: `uGoodsNav` + +商品加入购物车,立即购买等。 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-goods-nav) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 \ No newline at end of file diff --git a/uni_modules/uni-grid/changelog.md b/uni_modules/uni-grid/changelog.md new file mode 100644 index 0000000..d301166 --- /dev/null +++ b/uni_modules/uni-grid/changelog.md @@ -0,0 +1,13 @@ +## 1.4.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-grid](https://uniapp.dcloud.io/component/uniui/uni-grid) +## 1.3.2(2021-11-09) +- 新增 提供组件设计资源,组件样式调整 +## 1.3.1(2021-07-30) +- 优化 vue3下事件警告的问题 +## 1.3.0(2021-07-13) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.2.4(2021-05-12) +- 新增 组件示例地址 +## 1.2.3(2021-02-05) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-grid/components/uni-grid-item/uni-grid-item.vue b/uni_modules/uni-grid/components/uni-grid-item/uni-grid-item.vue new file mode 100644 index 0000000..19c08d7 --- /dev/null +++ b/uni_modules/uni-grid/components/uni-grid-item/uni-grid-item.vue @@ -0,0 +1,127 @@ + + + + + diff --git a/uni_modules/uni-grid/components/uni-grid/uni-grid.vue b/uni_modules/uni-grid/components/uni-grid/uni-grid.vue new file mode 100644 index 0000000..0edc7ff --- /dev/null +++ b/uni_modules/uni-grid/components/uni-grid/uni-grid.vue @@ -0,0 +1,142 @@ + + + + + diff --git a/uni_modules/uni-grid/package.json b/uni_modules/uni-grid/package.json new file mode 100644 index 0000000..ccb2c91 --- /dev/null +++ b/uni_modules/uni-grid/package.json @@ -0,0 +1,86 @@ +{ + "id": "uni-grid", + "displayName": "uni-grid 宫格", + "version": "1.4.0", + "description": "Grid 宫格组件,提供移动端常见的宫格布局,如九宫格。", + "keywords": [ + "uni-ui", + "uniui", + "九宫格", + "表格" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": ["uni-scss","uni-icons"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} diff --git a/uni_modules/uni-grid/readme.md b/uni_modules/uni-grid/readme.md new file mode 100644 index 0000000..0aa44cc --- /dev/null +++ b/uni_modules/uni-grid/readme.md @@ -0,0 +1,11 @@ + + +## Grid 宫格 +> **组件名:uni-grid** +> 代码块: `uGrid` + + +宫格组件。 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-grid) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 \ No newline at end of file diff --git a/uni_modules/uni-group/changelog.md b/uni_modules/uni-group/changelog.md new file mode 100644 index 0000000..a7024fd --- /dev/null +++ b/uni_modules/uni-group/changelog.md @@ -0,0 +1,16 @@ +## 1.2.2(2022-05-30) +- 新增 stat属性,是否开启uni统计功能 +## 1.2.1(2021-11-22) +- 修复 vue3中某些scss变量无法找到的问题 +## 1.2.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-group](https://uniapp.dcloud.io/component/uniui/uni-group) +## 1.1.7(2021-11-08) +## 1.1.0(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +- 优化 组件文档 +## 1.0.3(2021-05-12) +- 新增 组件示例地址 +## 1.0.2(2021-02-05) +- 调整为uni_modules目录规范 +- 优化 兼容 nvue 页面 diff --git a/uni_modules/uni-group/components/uni-group/uni-group.vue b/uni_modules/uni-group/components/uni-group/uni-group.vue new file mode 100644 index 0000000..3425ecd --- /dev/null +++ b/uni_modules/uni-group/components/uni-group/uni-group.vue @@ -0,0 +1,134 @@ + + + + diff --git a/uni_modules/uni-group/package.json b/uni_modules/uni-group/package.json new file mode 100644 index 0000000..ea00a08 --- /dev/null +++ b/uni_modules/uni-group/package.json @@ -0,0 +1,87 @@ +{ + "id": "uni-group", + "displayName": "uni-group 分组", + "version": "1.2.2", + "description": "分组组件可用于将组件用于分组,添加间隔,以产生明显的区块", + "keywords": [ + "uni-ui", + "uniui", + "group", + "分组", + "" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": ["uni-scss"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-group/readme.md b/uni_modules/uni-group/readme.md new file mode 100644 index 0000000..bae67f4 --- /dev/null +++ b/uni_modules/uni-group/readme.md @@ -0,0 +1,9 @@ + +## Group 分组 +> **组件名:uni-group** +> 代码块: `uGroup` + +分组组件可用于将组件分组,添加间隔,以产生明显的区块。 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-group) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 \ No newline at end of file diff --git a/uni_modules/uni-icons/changelog.md b/uni_modules/uni-icons/changelog.md new file mode 100644 index 0000000..0261131 --- /dev/null +++ b/uni_modules/uni-icons/changelog.md @@ -0,0 +1,42 @@ +## 2.0.10(2024-06-07) +- 优化 uni-app x 中,size 属性的类型 +## 2.0.9(2024-01-12) +fix: 修复图标大小默认值错误的问题 +## 2.0.8(2023-12-14) +- 修复 项目未使用 ts 情况下,打包报错的bug +## 2.0.7(2023-12-14) +- 修复 size 属性为 string 时,不加单位导致尺寸异常的bug +## 2.0.6(2023-12-11) +- 优化 兼容老版本icon类型,如 top ,bottom 等 +## 2.0.5(2023-12-11) +- 优化 兼容老版本icon类型,如 top ,bottom 等 +## 2.0.4(2023-12-06) +- 优化 uni-app x 下示例项目图标排序 +## 2.0.3(2023-12-06) +- 修复 nvue下引入组件报错的bug +## 2.0.2(2023-12-05) +-优化 size 属性支持单位 +## 2.0.1(2023-12-05) +- 新增 uni-app x 支持定义图标 +## 1.3.5(2022-01-24) +- 优化 size 属性可以传入不带单位的字符串数值 +## 1.3.4(2022-01-24) +- 优化 size 支持其他单位 +## 1.3.3(2022-01-17) +- 修复 nvue 有些图标不显示的bug,兼容老版本图标 +## 1.3.2(2021-12-01) +- 优化 示例可复制图标名称 +## 1.3.1(2021-11-23) +- 优化 兼容旧组件 type 值 +## 1.3.0(2021-11-19) +- 新增 更多图标 +- 优化 自定义图标使用方式 +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-icons](https://uniapp.dcloud.io/component/uniui/uni-icons) +## 1.1.7(2021-11-08) +## 1.2.0(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.1.5(2021-05-12) +- 新增 组件示例地址 +## 1.1.4(2021-02-05) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-icons/components/uni-icons/icons.js b/uni_modules/uni-icons/components/uni-icons/icons.js new file mode 100644 index 0000000..7889936 --- /dev/null +++ b/uni_modules/uni-icons/components/uni-icons/icons.js @@ -0,0 +1,1169 @@ +export default { + "id": "2852637", + "name": "uniui图标库", + "font_family": "uniicons", + "css_prefix_text": "uniui-", + "description": "", + "glyphs": [ + { + "icon_id": "25027049", + "name": "yanse", + "font_class": "color", + "unicode": "e6cf", + "unicode_decimal": 59087 + }, + { + "icon_id": "25027048", + "name": "wallet", + "font_class": "wallet", + "unicode": "e6b1", + "unicode_decimal": 59057 + }, + { + "icon_id": "25015720", + "name": "settings-filled", + "font_class": "settings-filled", + "unicode": "e6ce", + "unicode_decimal": 59086 + }, + { + "icon_id": "25015434", + "name": "shimingrenzheng-filled", + "font_class": "auth-filled", + "unicode": "e6cc", + "unicode_decimal": 59084 + }, + { + "icon_id": "24934246", + "name": "shop-filled", + "font_class": "shop-filled", + "unicode": "e6cd", + "unicode_decimal": 59085 + }, + { + "icon_id": "24934159", + "name": "staff-filled-01", + "font_class": "staff-filled", + "unicode": "e6cb", + "unicode_decimal": 59083 + }, + { + "icon_id": "24932461", + "name": "VIP-filled", + "font_class": "vip-filled", + "unicode": "e6c6", + "unicode_decimal": 59078 + }, + { + "icon_id": "24932462", + "name": "plus_circle_fill", + "font_class": "plus-filled", + "unicode": "e6c7", + "unicode_decimal": 59079 + }, + { + "icon_id": "24932463", + "name": "folder_add-filled", + "font_class": "folder-add-filled", + "unicode": "e6c8", + "unicode_decimal": 59080 + }, + { + "icon_id": "24932464", + "name": "yanse-filled", + "font_class": "color-filled", + "unicode": "e6c9", + "unicode_decimal": 59081 + }, + { + "icon_id": "24932465", + "name": "tune-filled", + "font_class": "tune-filled", + "unicode": "e6ca", + "unicode_decimal": 59082 + }, + { + "icon_id": "24932455", + "name": "a-rilidaka-filled", + "font_class": "calendar-filled", + "unicode": "e6c0", + "unicode_decimal": 59072 + }, + { + "icon_id": "24932456", + "name": "notification-filled", + "font_class": "notification-filled", + "unicode": "e6c1", + "unicode_decimal": 59073 + }, + { + "icon_id": "24932457", + "name": "wallet-filled", + "font_class": "wallet-filled", + "unicode": "e6c2", + "unicode_decimal": 59074 + }, + { + "icon_id": "24932458", + "name": "paihangbang-filled", + "font_class": "medal-filled", + "unicode": "e6c3", + "unicode_decimal": 59075 + }, + { + "icon_id": "24932459", + "name": "gift-filled", + "font_class": "gift-filled", + "unicode": "e6c4", + "unicode_decimal": 59076 + }, + { + "icon_id": "24932460", + "name": "fire-filled", + "font_class": "fire-filled", + "unicode": "e6c5", + "unicode_decimal": 59077 + }, + { + "icon_id": "24928001", + "name": "refreshempty", + "font_class": "refreshempty", + "unicode": "e6bf", + "unicode_decimal": 59071 + }, + { + "icon_id": "24926853", + "name": "location-ellipse", + "font_class": "location-filled", + "unicode": "e6af", + "unicode_decimal": 59055 + }, + { + "icon_id": "24926735", + "name": "person-filled", + "font_class": "person-filled", + "unicode": "e69d", + "unicode_decimal": 59037 + }, + { + "icon_id": "24926703", + "name": "personadd-filled", + "font_class": "personadd-filled", + "unicode": "e698", + "unicode_decimal": 59032 + }, + { + "icon_id": "24923351", + "name": "back", + "font_class": "back", + "unicode": "e6b9", + "unicode_decimal": 59065 + }, + { + "icon_id": "24923352", + "name": "forward", + "font_class": "forward", + "unicode": "e6ba", + "unicode_decimal": 59066 + }, + { + "icon_id": "24923353", + "name": "arrowthinright", + "font_class": "arrow-right", + "unicode": "e6bb", + "unicode_decimal": 59067 + }, + { + "icon_id": "24923353", + "name": "arrowthinright", + "font_class": "arrowthinright", + "unicode": "e6bb", + "unicode_decimal": 59067 + }, + { + "icon_id": "24923354", + "name": "arrowthinleft", + "font_class": "arrow-left", + "unicode": "e6bc", + "unicode_decimal": 59068 + }, + { + "icon_id": "24923354", + "name": "arrowthinleft", + "font_class": "arrowthinleft", + "unicode": "e6bc", + "unicode_decimal": 59068 + }, + { + "icon_id": "24923355", + "name": "arrowthinup", + "font_class": "arrow-up", + "unicode": "e6bd", + "unicode_decimal": 59069 + }, + { + "icon_id": "24923355", + "name": "arrowthinup", + "font_class": "arrowthinup", + "unicode": "e6bd", + "unicode_decimal": 59069 + }, + { + "icon_id": "24923356", + "name": "arrowthindown", + "font_class": "arrow-down", + "unicode": "e6be", + "unicode_decimal": 59070 + },{ + "icon_id": "24923356", + "name": "arrowthindown", + "font_class": "arrowthindown", + "unicode": "e6be", + "unicode_decimal": 59070 + }, + { + "icon_id": "24923349", + "name": "arrowdown", + "font_class": "bottom", + "unicode": "e6b8", + "unicode_decimal": 59064 + },{ + "icon_id": "24923349", + "name": "arrowdown", + "font_class": "arrowdown", + "unicode": "e6b8", + "unicode_decimal": 59064 + }, + { + "icon_id": "24923346", + "name": "arrowright", + "font_class": "right", + "unicode": "e6b5", + "unicode_decimal": 59061 + }, + { + "icon_id": "24923346", + "name": "arrowright", + "font_class": "arrowright", + "unicode": "e6b5", + "unicode_decimal": 59061 + }, + { + "icon_id": "24923347", + "name": "arrowup", + "font_class": "top", + "unicode": "e6b6", + "unicode_decimal": 59062 + }, + { + "icon_id": "24923347", + "name": "arrowup", + "font_class": "arrowup", + "unicode": "e6b6", + "unicode_decimal": 59062 + }, + { + "icon_id": "24923348", + "name": "arrowleft", + "font_class": "left", + "unicode": "e6b7", + "unicode_decimal": 59063 + }, + { + "icon_id": "24923348", + "name": "arrowleft", + "font_class": "arrowleft", + "unicode": "e6b7", + "unicode_decimal": 59063 + }, + { + "icon_id": "24923334", + "name": "eye", + "font_class": "eye", + "unicode": "e651", + "unicode_decimal": 58961 + }, + { + "icon_id": "24923335", + "name": "eye-filled", + "font_class": "eye-filled", + "unicode": "e66a", + "unicode_decimal": 58986 + }, + { + "icon_id": "24923336", + "name": "eye-slash", + "font_class": "eye-slash", + "unicode": "e6b3", + "unicode_decimal": 59059 + }, + { + "icon_id": "24923337", + "name": "eye-slash-filled", + "font_class": "eye-slash-filled", + "unicode": "e6b4", + "unicode_decimal": 59060 + }, + { + "icon_id": "24923305", + "name": "info-filled", + "font_class": "info-filled", + "unicode": "e649", + "unicode_decimal": 58953 + }, + { + "icon_id": "24923299", + "name": "reload-01", + "font_class": "reload", + "unicode": "e6b2", + "unicode_decimal": 59058 + }, + { + "icon_id": "24923195", + "name": "mic_slash_fill", + "font_class": "micoff-filled", + "unicode": "e6b0", + "unicode_decimal": 59056 + }, + { + "icon_id": "24923165", + "name": "map-pin-ellipse", + "font_class": "map-pin-ellipse", + "unicode": "e6ac", + "unicode_decimal": 59052 + }, + { + "icon_id": "24923166", + "name": "map-pin", + "font_class": "map-pin", + "unicode": "e6ad", + "unicode_decimal": 59053 + }, + { + "icon_id": "24923167", + "name": "location", + "font_class": "location", + "unicode": "e6ae", + "unicode_decimal": 59054 + }, + { + "icon_id": "24923064", + "name": "starhalf", + "font_class": "starhalf", + "unicode": "e683", + "unicode_decimal": 59011 + }, + { + "icon_id": "24923065", + "name": "star", + "font_class": "star", + "unicode": "e688", + "unicode_decimal": 59016 + }, + { + "icon_id": "24923066", + "name": "star-filled", + "font_class": "star-filled", + "unicode": "e68f", + "unicode_decimal": 59023 + }, + { + "icon_id": "24899646", + "name": "a-rilidaka", + "font_class": "calendar", + "unicode": "e6a0", + "unicode_decimal": 59040 + }, + { + "icon_id": "24899647", + "name": "fire", + "font_class": "fire", + "unicode": "e6a1", + "unicode_decimal": 59041 + }, + { + "icon_id": "24899648", + "name": "paihangbang", + "font_class": "medal", + "unicode": "e6a2", + "unicode_decimal": 59042 + }, + { + "icon_id": "24899649", + "name": "font", + "font_class": "font", + "unicode": "e6a3", + "unicode_decimal": 59043 + }, + { + "icon_id": "24899650", + "name": "gift", + "font_class": "gift", + "unicode": "e6a4", + "unicode_decimal": 59044 + }, + { + "icon_id": "24899651", + "name": "link", + "font_class": "link", + "unicode": "e6a5", + "unicode_decimal": 59045 + }, + { + "icon_id": "24899652", + "name": "notification", + "font_class": "notification", + "unicode": "e6a6", + "unicode_decimal": 59046 + }, + { + "icon_id": "24899653", + "name": "staff", + "font_class": "staff", + "unicode": "e6a7", + "unicode_decimal": 59047 + }, + { + "icon_id": "24899654", + "name": "VIP", + "font_class": "vip", + "unicode": "e6a8", + "unicode_decimal": 59048 + }, + { + "icon_id": "24899655", + "name": "folder_add", + "font_class": "folder-add", + "unicode": "e6a9", + "unicode_decimal": 59049 + }, + { + "icon_id": "24899656", + "name": "tune", + "font_class": "tune", + "unicode": "e6aa", + "unicode_decimal": 59050 + }, + { + "icon_id": "24899657", + "name": "shimingrenzheng", + "font_class": "auth", + "unicode": "e6ab", + "unicode_decimal": 59051 + }, + { + "icon_id": "24899565", + "name": "person", + "font_class": "person", + "unicode": "e699", + "unicode_decimal": 59033 + }, + { + "icon_id": "24899566", + "name": "email-filled", + "font_class": "email-filled", + "unicode": "e69a", + "unicode_decimal": 59034 + }, + { + "icon_id": "24899567", + "name": "phone-filled", + "font_class": "phone-filled", + "unicode": "e69b", + "unicode_decimal": 59035 + }, + { + "icon_id": "24899568", + "name": "phone", + "font_class": "phone", + "unicode": "e69c", + "unicode_decimal": 59036 + }, + { + "icon_id": "24899570", + "name": "email", + "font_class": "email", + "unicode": "e69e", + "unicode_decimal": 59038 + }, + { + "icon_id": "24899571", + "name": "personadd", + "font_class": "personadd", + "unicode": "e69f", + "unicode_decimal": 59039 + }, + { + "icon_id": "24899558", + "name": "chatboxes-filled", + "font_class": "chatboxes-filled", + "unicode": "e692", + "unicode_decimal": 59026 + }, + { + "icon_id": "24899559", + "name": "contact", + "font_class": "contact", + "unicode": "e693", + "unicode_decimal": 59027 + }, + { + "icon_id": "24899560", + "name": "chatbubble-filled", + "font_class": "chatbubble-filled", + "unicode": "e694", + "unicode_decimal": 59028 + }, + { + "icon_id": "24899561", + "name": "contact-filled", + "font_class": "contact-filled", + "unicode": "e695", + "unicode_decimal": 59029 + }, + { + "icon_id": "24899562", + "name": "chatboxes", + "font_class": "chatboxes", + "unicode": "e696", + "unicode_decimal": 59030 + }, + { + "icon_id": "24899563", + "name": "chatbubble", + "font_class": "chatbubble", + "unicode": "e697", + "unicode_decimal": 59031 + }, + { + "icon_id": "24881290", + "name": "upload-filled", + "font_class": "upload-filled", + "unicode": "e68e", + "unicode_decimal": 59022 + }, + { + "icon_id": "24881292", + "name": "upload", + "font_class": "upload", + "unicode": "e690", + "unicode_decimal": 59024 + }, + { + "icon_id": "24881293", + "name": "weixin", + "font_class": "weixin", + "unicode": "e691", + "unicode_decimal": 59025 + }, + { + "icon_id": "24881274", + "name": "compose", + "font_class": "compose", + "unicode": "e67f", + "unicode_decimal": 59007 + }, + { + "icon_id": "24881275", + "name": "qq", + "font_class": "qq", + "unicode": "e680", + "unicode_decimal": 59008 + }, + { + "icon_id": "24881276", + "name": "download-filled", + "font_class": "download-filled", + "unicode": "e681", + "unicode_decimal": 59009 + }, + { + "icon_id": "24881277", + "name": "pengyouquan", + "font_class": "pyq", + "unicode": "e682", + "unicode_decimal": 59010 + }, + { + "icon_id": "24881279", + "name": "sound", + "font_class": "sound", + "unicode": "e684", + "unicode_decimal": 59012 + }, + { + "icon_id": "24881280", + "name": "trash-filled", + "font_class": "trash-filled", + "unicode": "e685", + "unicode_decimal": 59013 + }, + { + "icon_id": "24881281", + "name": "sound-filled", + "font_class": "sound-filled", + "unicode": "e686", + "unicode_decimal": 59014 + }, + { + "icon_id": "24881282", + "name": "trash", + "font_class": "trash", + "unicode": "e687", + "unicode_decimal": 59015 + }, + { + "icon_id": "24881284", + "name": "videocam-filled", + "font_class": "videocam-filled", + "unicode": "e689", + "unicode_decimal": 59017 + }, + { + "icon_id": "24881285", + "name": "spinner-cycle", + "font_class": "spinner-cycle", + "unicode": "e68a", + "unicode_decimal": 59018 + }, + { + "icon_id": "24881286", + "name": "weibo", + "font_class": "weibo", + "unicode": "e68b", + "unicode_decimal": 59019 + }, + { + "icon_id": "24881288", + "name": "videocam", + "font_class": "videocam", + "unicode": "e68c", + "unicode_decimal": 59020 + }, + { + "icon_id": "24881289", + "name": "download", + "font_class": "download", + "unicode": "e68d", + "unicode_decimal": 59021 + }, + { + "icon_id": "24879601", + "name": "help", + "font_class": "help", + "unicode": "e679", + "unicode_decimal": 59001 + }, + { + "icon_id": "24879602", + "name": "navigate-filled", + "font_class": "navigate-filled", + "unicode": "e67a", + "unicode_decimal": 59002 + }, + { + "icon_id": "24879603", + "name": "plusempty", + "font_class": "plusempty", + "unicode": "e67b", + "unicode_decimal": 59003 + }, + { + "icon_id": "24879604", + "name": "smallcircle", + "font_class": "smallcircle", + "unicode": "e67c", + "unicode_decimal": 59004 + }, + { + "icon_id": "24879605", + "name": "minus-filled", + "font_class": "minus-filled", + "unicode": "e67d", + "unicode_decimal": 59005 + }, + { + "icon_id": "24879606", + "name": "micoff", + "font_class": "micoff", + "unicode": "e67e", + "unicode_decimal": 59006 + }, + { + "icon_id": "24879588", + "name": "closeempty", + "font_class": "closeempty", + "unicode": "e66c", + "unicode_decimal": 58988 + }, + { + "icon_id": "24879589", + "name": "clear", + "font_class": "clear", + "unicode": "e66d", + "unicode_decimal": 58989 + }, + { + "icon_id": "24879590", + "name": "navigate", + "font_class": "navigate", + "unicode": "e66e", + "unicode_decimal": 58990 + }, + { + "icon_id": "24879591", + "name": "minus", + "font_class": "minus", + "unicode": "e66f", + "unicode_decimal": 58991 + }, + { + "icon_id": "24879592", + "name": "image", + "font_class": "image", + "unicode": "e670", + "unicode_decimal": 58992 + }, + { + "icon_id": "24879593", + "name": "mic", + "font_class": "mic", + "unicode": "e671", + "unicode_decimal": 58993 + }, + { + "icon_id": "24879594", + "name": "paperplane", + "font_class": "paperplane", + "unicode": "e672", + "unicode_decimal": 58994 + }, + { + "icon_id": "24879595", + "name": "close", + "font_class": "close", + "unicode": "e673", + "unicode_decimal": 58995 + }, + { + "icon_id": "24879596", + "name": "help-filled", + "font_class": "help-filled", + "unicode": "e674", + "unicode_decimal": 58996 + }, + { + "icon_id": "24879597", + "name": "plus-filled", + "font_class": "paperplane-filled", + "unicode": "e675", + "unicode_decimal": 58997 + }, + { + "icon_id": "24879598", + "name": "plus", + "font_class": "plus", + "unicode": "e676", + "unicode_decimal": 58998 + }, + { + "icon_id": "24879599", + "name": "mic-filled", + "font_class": "mic-filled", + "unicode": "e677", + "unicode_decimal": 58999 + }, + { + "icon_id": "24879600", + "name": "image-filled", + "font_class": "image-filled", + "unicode": "e678", + "unicode_decimal": 59000 + }, + { + "icon_id": "24855900", + "name": "locked-filled", + "font_class": "locked-filled", + "unicode": "e668", + "unicode_decimal": 58984 + }, + { + "icon_id": "24855901", + "name": "info", + "font_class": "info", + "unicode": "e669", + "unicode_decimal": 58985 + }, + { + "icon_id": "24855903", + "name": "locked", + "font_class": "locked", + "unicode": "e66b", + "unicode_decimal": 58987 + }, + { + "icon_id": "24855884", + "name": "camera-filled", + "font_class": "camera-filled", + "unicode": "e658", + "unicode_decimal": 58968 + }, + { + "icon_id": "24855885", + "name": "chat-filled", + "font_class": "chat-filled", + "unicode": "e659", + "unicode_decimal": 58969 + }, + { + "icon_id": "24855886", + "name": "camera", + "font_class": "camera", + "unicode": "e65a", + "unicode_decimal": 58970 + }, + { + "icon_id": "24855887", + "name": "circle", + "font_class": "circle", + "unicode": "e65b", + "unicode_decimal": 58971 + }, + { + "icon_id": "24855888", + "name": "checkmarkempty", + "font_class": "checkmarkempty", + "unicode": "e65c", + "unicode_decimal": 58972 + }, + { + "icon_id": "24855889", + "name": "chat", + "font_class": "chat", + "unicode": "e65d", + "unicode_decimal": 58973 + }, + { + "icon_id": "24855890", + "name": "circle-filled", + "font_class": "circle-filled", + "unicode": "e65e", + "unicode_decimal": 58974 + }, + { + "icon_id": "24855891", + "name": "flag", + "font_class": "flag", + "unicode": "e65f", + "unicode_decimal": 58975 + }, + { + "icon_id": "24855892", + "name": "flag-filled", + "font_class": "flag-filled", + "unicode": "e660", + "unicode_decimal": 58976 + }, + { + "icon_id": "24855893", + "name": "gear-filled", + "font_class": "gear-filled", + "unicode": "e661", + "unicode_decimal": 58977 + }, + { + "icon_id": "24855894", + "name": "home", + "font_class": "home", + "unicode": "e662", + "unicode_decimal": 58978 + }, + { + "icon_id": "24855895", + "name": "home-filled", + "font_class": "home-filled", + "unicode": "e663", + "unicode_decimal": 58979 + }, + { + "icon_id": "24855896", + "name": "gear", + "font_class": "gear", + "unicode": "e664", + "unicode_decimal": 58980 + }, + { + "icon_id": "24855897", + "name": "smallcircle-filled", + "font_class": "smallcircle-filled", + "unicode": "e665", + "unicode_decimal": 58981 + }, + { + "icon_id": "24855898", + "name": "map-filled", + "font_class": "map-filled", + "unicode": "e666", + "unicode_decimal": 58982 + }, + { + "icon_id": "24855899", + "name": "map", + "font_class": "map", + "unicode": "e667", + "unicode_decimal": 58983 + }, + { + "icon_id": "24855825", + "name": "refresh-filled", + "font_class": "refresh-filled", + "unicode": "e656", + "unicode_decimal": 58966 + }, + { + "icon_id": "24855826", + "name": "refresh", + "font_class": "refresh", + "unicode": "e657", + "unicode_decimal": 58967 + }, + { + "icon_id": "24855808", + "name": "cloud-upload", + "font_class": "cloud-upload", + "unicode": "e645", + "unicode_decimal": 58949 + }, + { + "icon_id": "24855809", + "name": "cloud-download-filled", + "font_class": "cloud-download-filled", + "unicode": "e646", + "unicode_decimal": 58950 + }, + { + "icon_id": "24855810", + "name": "cloud-download", + "font_class": "cloud-download", + "unicode": "e647", + "unicode_decimal": 58951 + }, + { + "icon_id": "24855811", + "name": "cloud-upload-filled", + "font_class": "cloud-upload-filled", + "unicode": "e648", + "unicode_decimal": 58952 + }, + { + "icon_id": "24855813", + "name": "redo", + "font_class": "redo", + "unicode": "e64a", + "unicode_decimal": 58954 + }, + { + "icon_id": "24855814", + "name": "images-filled", + "font_class": "images-filled", + "unicode": "e64b", + "unicode_decimal": 58955 + }, + { + "icon_id": "24855815", + "name": "undo-filled", + "font_class": "undo-filled", + "unicode": "e64c", + "unicode_decimal": 58956 + }, + { + "icon_id": "24855816", + "name": "more", + "font_class": "more", + "unicode": "e64d", + "unicode_decimal": 58957 + }, + { + "icon_id": "24855817", + "name": "more-filled", + "font_class": "more-filled", + "unicode": "e64e", + "unicode_decimal": 58958 + }, + { + "icon_id": "24855818", + "name": "undo", + "font_class": "undo", + "unicode": "e64f", + "unicode_decimal": 58959 + }, + { + "icon_id": "24855819", + "name": "images", + "font_class": "images", + "unicode": "e650", + "unicode_decimal": 58960 + }, + { + "icon_id": "24855821", + "name": "paperclip", + "font_class": "paperclip", + "unicode": "e652", + "unicode_decimal": 58962 + }, + { + "icon_id": "24855822", + "name": "settings", + "font_class": "settings", + "unicode": "e653", + "unicode_decimal": 58963 + }, + { + "icon_id": "24855823", + "name": "search", + "font_class": "search", + "unicode": "e654", + "unicode_decimal": 58964 + }, + { + "icon_id": "24855824", + "name": "redo-filled", + "font_class": "redo-filled", + "unicode": "e655", + "unicode_decimal": 58965 + }, + { + "icon_id": "24841702", + "name": "list", + "font_class": "list", + "unicode": "e644", + "unicode_decimal": 58948 + }, + { + "icon_id": "24841489", + "name": "mail-open-filled", + "font_class": "mail-open-filled", + "unicode": "e63a", + "unicode_decimal": 58938 + }, + { + "icon_id": "24841491", + "name": "hand-thumbsdown-filled", + "font_class": "hand-down-filled", + "unicode": "e63c", + "unicode_decimal": 58940 + }, + { + "icon_id": "24841492", + "name": "hand-thumbsdown", + "font_class": "hand-down", + "unicode": "e63d", + "unicode_decimal": 58941 + }, + { + "icon_id": "24841493", + "name": "hand-thumbsup-filled", + "font_class": "hand-up-filled", + "unicode": "e63e", + "unicode_decimal": 58942 + }, + { + "icon_id": "24841494", + "name": "hand-thumbsup", + "font_class": "hand-up", + "unicode": "e63f", + "unicode_decimal": 58943 + }, + { + "icon_id": "24841496", + "name": "heart-filled", + "font_class": "heart-filled", + "unicode": "e641", + "unicode_decimal": 58945 + }, + { + "icon_id": "24841498", + "name": "mail-open", + "font_class": "mail-open", + "unicode": "e643", + "unicode_decimal": 58947 + }, + { + "icon_id": "24841488", + "name": "heart", + "font_class": "heart", + "unicode": "e639", + "unicode_decimal": 58937 + }, + { + "icon_id": "24839963", + "name": "loop", + "font_class": "loop", + "unicode": "e633", + "unicode_decimal": 58931 + }, + { + "icon_id": "24839866", + "name": "pulldown", + "font_class": "pulldown", + "unicode": "e632", + "unicode_decimal": 58930 + }, + { + "icon_id": "24813798", + "name": "scan", + "font_class": "scan", + "unicode": "e62a", + "unicode_decimal": 58922 + }, + { + "icon_id": "24813786", + "name": "bars", + "font_class": "bars", + "unicode": "e627", + "unicode_decimal": 58919 + }, + { + "icon_id": "24813788", + "name": "cart-filled", + "font_class": "cart-filled", + "unicode": "e629", + "unicode_decimal": 58921 + }, + { + "icon_id": "24813790", + "name": "checkbox", + "font_class": "checkbox", + "unicode": "e62b", + "unicode_decimal": 58923 + }, + { + "icon_id": "24813791", + "name": "checkbox-filled", + "font_class": "checkbox-filled", + "unicode": "e62c", + "unicode_decimal": 58924 + }, + { + "icon_id": "24813794", + "name": "shop", + "font_class": "shop", + "unicode": "e62f", + "unicode_decimal": 58927 + }, + { + "icon_id": "24813795", + "name": "headphones", + "font_class": "headphones", + "unicode": "e630", + "unicode_decimal": 58928 + }, + { + "icon_id": "24813796", + "name": "cart", + "font_class": "cart", + "unicode": "e631", + "unicode_decimal": 58929 + } + ] +} diff --git a/uni_modules/uni-icons/components/uni-icons/uni-icons.uvue b/uni_modules/uni-icons/components/uni-icons/uni-icons.uvue new file mode 100644 index 0000000..8740559 --- /dev/null +++ b/uni_modules/uni-icons/components/uni-icons/uni-icons.uvue @@ -0,0 +1,91 @@ + + + + + diff --git a/uni_modules/uni-icons/components/uni-icons/uni-icons.vue b/uni_modules/uni-icons/components/uni-icons/uni-icons.vue new file mode 100644 index 0000000..7da5356 --- /dev/null +++ b/uni_modules/uni-icons/components/uni-icons/uni-icons.vue @@ -0,0 +1,110 @@ + + + + + diff --git a/uni_modules/uni-icons/components/uni-icons/uni.ttf b/uni_modules/uni-icons/components/uni-icons/uni.ttf new file mode 100644 index 0000000000000000000000000000000000000000..60a1968d08cc6056c70b5402b2effac43c6f96a3 GIT binary patch literal 26164 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 literal 0 HcmV?d00001 diff --git a/uni_modules/uni-icons/components/uni-icons/uniicons.css b/uni_modules/uni-icons/components/uni-icons/uniicons.css new file mode 100644 index 0000000..0a6b6fe --- /dev/null +++ b/uni_modules/uni-icons/components/uni-icons/uniicons.css @@ -0,0 +1,664 @@ + +.uniui-cart-filled:before { + content: "\e6d0"; +} + +.uniui-gift-filled:before { + content: "\e6c4"; +} + +.uniui-color:before { + content: "\e6cf"; +} + +.uniui-wallet:before { + content: "\e6b1"; +} + +.uniui-settings-filled:before { + content: "\e6ce"; +} + +.uniui-auth-filled:before { + content: "\e6cc"; +} + +.uniui-shop-filled:before { + content: "\e6cd"; +} + +.uniui-staff-filled:before { + content: "\e6cb"; +} + +.uniui-vip-filled:before { + content: "\e6c6"; +} + +.uniui-plus-filled:before { + content: "\e6c7"; +} + +.uniui-folder-add-filled:before { + content: "\e6c8"; +} + +.uniui-color-filled:before { + content: "\e6c9"; +} + +.uniui-tune-filled:before { + content: "\e6ca"; +} + +.uniui-calendar-filled:before { + content: "\e6c0"; +} + +.uniui-notification-filled:before { + content: "\e6c1"; +} + +.uniui-wallet-filled:before { + content: "\e6c2"; +} + +.uniui-medal-filled:before { + content: "\e6c3"; +} + +.uniui-fire-filled:before { + content: "\e6c5"; +} + +.uniui-refreshempty:before { + content: "\e6bf"; +} + +.uniui-location-filled:before { + content: "\e6af"; +} + +.uniui-person-filled:before { + content: "\e69d"; +} + +.uniui-personadd-filled:before { + content: "\e698"; +} + +.uniui-arrowthinleft:before { + content: "\e6d2"; +} + +.uniui-arrowthinup:before { + content: "\e6d3"; +} + +.uniui-arrowthindown:before { + content: "\e6d4"; +} + +.uniui-back:before { + content: "\e6b9"; +} + +.uniui-forward:before { + content: "\e6ba"; +} + +.uniui-arrow-right:before { + content: "\e6bb"; +} + +.uniui-arrow-left:before { + content: "\e6bc"; +} + +.uniui-arrow-up:before { + content: "\e6bd"; +} + +.uniui-arrow-down:before { + content: "\e6be"; +} + +.uniui-arrowthinright:before { + content: "\e6d1"; +} + +.uniui-down:before { + content: "\e6b8"; +} + +.uniui-bottom:before { + content: "\e6b8"; +} + +.uniui-arrowright:before { + content: "\e6d5"; +} + +.uniui-right:before { + content: "\e6b5"; +} + +.uniui-up:before { + content: "\e6b6"; +} + +.uniui-top:before { + content: "\e6b6"; +} + +.uniui-left:before { + content: "\e6b7"; +} + +.uniui-arrowup:before { + content: "\e6d6"; +} + +.uniui-eye:before { + content: "\e651"; +} + +.uniui-eye-filled:before { + content: "\e66a"; +} + +.uniui-eye-slash:before { + content: "\e6b3"; +} + +.uniui-eye-slash-filled:before { + content: "\e6b4"; +} + +.uniui-info-filled:before { + content: "\e649"; +} + +.uniui-reload:before { + content: "\e6b2"; +} + +.uniui-micoff-filled:before { + content: "\e6b0"; +} + +.uniui-map-pin-ellipse:before { + content: "\e6ac"; +} + +.uniui-map-pin:before { + content: "\e6ad"; +} + +.uniui-location:before { + content: "\e6ae"; +} + +.uniui-starhalf:before { + content: "\e683"; +} + +.uniui-star:before { + content: "\e688"; +} + +.uniui-star-filled:before { + content: "\e68f"; +} + +.uniui-calendar:before { + content: "\e6a0"; +} + +.uniui-fire:before { + content: "\e6a1"; +} + +.uniui-medal:before { + content: "\e6a2"; +} + +.uniui-font:before { + content: "\e6a3"; +} + +.uniui-gift:before { + content: "\e6a4"; +} + +.uniui-link:before { + content: "\e6a5"; +} + +.uniui-notification:before { + content: "\e6a6"; +} + +.uniui-staff:before { + content: "\e6a7"; +} + +.uniui-vip:before { + content: "\e6a8"; +} + +.uniui-folder-add:before { + content: "\e6a9"; +} + +.uniui-tune:before { + content: "\e6aa"; +} + +.uniui-auth:before { + content: "\e6ab"; +} + +.uniui-person:before { + content: "\e699"; +} + +.uniui-email-filled:before { + content: "\e69a"; +} + +.uniui-phone-filled:before { + content: "\e69b"; +} + +.uniui-phone:before { + content: "\e69c"; +} + +.uniui-email:before { + content: "\e69e"; +} + +.uniui-personadd:before { + content: "\e69f"; +} + +.uniui-chatboxes-filled:before { + content: "\e692"; +} + +.uniui-contact:before { + content: "\e693"; +} + +.uniui-chatbubble-filled:before { + content: "\e694"; +} + +.uniui-contact-filled:before { + content: "\e695"; +} + +.uniui-chatboxes:before { + content: "\e696"; +} + +.uniui-chatbubble:before { + content: "\e697"; +} + +.uniui-upload-filled:before { + content: "\e68e"; +} + +.uniui-upload:before { + content: "\e690"; +} + +.uniui-weixin:before { + content: "\e691"; +} + +.uniui-compose:before { + content: "\e67f"; +} + +.uniui-qq:before { + content: "\e680"; +} + +.uniui-download-filled:before { + content: "\e681"; +} + +.uniui-pyq:before { + content: "\e682"; +} + +.uniui-sound:before { + content: "\e684"; +} + +.uniui-trash-filled:before { + content: "\e685"; +} + +.uniui-sound-filled:before { + content: "\e686"; +} + +.uniui-trash:before { + content: "\e687"; +} + +.uniui-videocam-filled:before { + content: "\e689"; +} + +.uniui-spinner-cycle:before { + content: "\e68a"; +} + +.uniui-weibo:before { + content: "\e68b"; +} + +.uniui-videocam:before { + content: "\e68c"; +} + +.uniui-download:before { + content: "\e68d"; +} + +.uniui-help:before { + content: "\e679"; +} + +.uniui-navigate-filled:before { + content: "\e67a"; +} + +.uniui-plusempty:before { + content: "\e67b"; +} + +.uniui-smallcircle:before { + content: "\e67c"; +} + +.uniui-minus-filled:before { + content: "\e67d"; +} + +.uniui-micoff:before { + content: "\e67e"; +} + +.uniui-closeempty:before { + content: "\e66c"; +} + +.uniui-clear:before { + content: "\e66d"; +} + +.uniui-navigate:before { + content: "\e66e"; +} + +.uniui-minus:before { + content: "\e66f"; +} + +.uniui-image:before { + content: "\e670"; +} + +.uniui-mic:before { + content: "\e671"; +} + +.uniui-paperplane:before { + content: "\e672"; +} + +.uniui-close:before { + content: "\e673"; +} + +.uniui-help-filled:before { + content: "\e674"; +} + +.uniui-paperplane-filled:before { + content: "\e675"; +} + +.uniui-plus:before { + content: "\e676"; +} + +.uniui-mic-filled:before { + content: "\e677"; +} + +.uniui-image-filled:before { + content: "\e678"; +} + +.uniui-locked-filled:before { + content: "\e668"; +} + +.uniui-info:before { + content: "\e669"; +} + +.uniui-locked:before { + content: "\e66b"; +} + +.uniui-camera-filled:before { + content: "\e658"; +} + +.uniui-chat-filled:before { + content: "\e659"; +} + +.uniui-camera:before { + content: "\e65a"; +} + +.uniui-circle:before { + content: "\e65b"; +} + +.uniui-checkmarkempty:before { + content: "\e65c"; +} + +.uniui-chat:before { + content: "\e65d"; +} + +.uniui-circle-filled:before { + content: "\e65e"; +} + +.uniui-flag:before { + content: "\e65f"; +} + +.uniui-flag-filled:before { + content: "\e660"; +} + +.uniui-gear-filled:before { + content: "\e661"; +} + +.uniui-home:before { + content: "\e662"; +} + +.uniui-home-filled:before { + content: "\e663"; +} + +.uniui-gear:before { + content: "\e664"; +} + +.uniui-smallcircle-filled:before { + content: "\e665"; +} + +.uniui-map-filled:before { + content: "\e666"; +} + +.uniui-map:before { + content: "\e667"; +} + +.uniui-refresh-filled:before { + content: "\e656"; +} + +.uniui-refresh:before { + content: "\e657"; +} + +.uniui-cloud-upload:before { + content: "\e645"; +} + +.uniui-cloud-download-filled:before { + content: "\e646"; +} + +.uniui-cloud-download:before { + content: "\e647"; +} + +.uniui-cloud-upload-filled:before { + content: "\e648"; +} + +.uniui-redo:before { + content: "\e64a"; +} + +.uniui-images-filled:before { + content: "\e64b"; +} + +.uniui-undo-filled:before { + content: "\e64c"; +} + +.uniui-more:before { + content: "\e64d"; +} + +.uniui-more-filled:before { + content: "\e64e"; +} + +.uniui-undo:before { + content: "\e64f"; +} + +.uniui-images:before { + content: "\e650"; +} + +.uniui-paperclip:before { + content: "\e652"; +} + +.uniui-settings:before { + content: "\e653"; +} + +.uniui-search:before { + content: "\e654"; +} + +.uniui-redo-filled:before { + content: "\e655"; +} + +.uniui-list:before { + content: "\e644"; +} + +.uniui-mail-open-filled:before { + content: "\e63a"; +} + +.uniui-hand-down-filled:before { + content: "\e63c"; +} + +.uniui-hand-down:before { + content: "\e63d"; +} + +.uniui-hand-up-filled:before { + content: "\e63e"; +} + +.uniui-hand-up:before { + content: "\e63f"; +} + +.uniui-heart-filled:before { + content: "\e641"; +} + +.uniui-mail-open:before { + content: "\e643"; +} + +.uniui-heart:before { + content: "\e639"; +} + +.uniui-loop:before { + content: "\e633"; +} + +.uniui-pulldown:before { + content: "\e632"; +} + +.uniui-scan:before { + content: "\e62a"; +} + +.uniui-bars:before { + content: "\e627"; +} + +.uniui-checkbox:before { + content: "\e62b"; +} + +.uniui-checkbox-filled:before { + content: "\e62c"; +} + +.uniui-shop:before { + content: "\e62f"; +} + +.uniui-headphones:before { + content: "\e630"; +} + +.uniui-cart:before { + content: "\e631"; +} diff --git a/uni_modules/uni-icons/components/uni-icons/uniicons.ttf b/uni_modules/uni-icons/components/uni-icons/uniicons.ttf new file mode 100644 index 0000000000000000000000000000000000000000..14696d038d828073edac09ea4e5ba1dec2f58115 GIT binary patch literal 35824 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 F{2S@rCxHL} literal 0 HcmV?d00001 diff --git a/uni_modules/uni-icons/components/uni-icons/uniicons_file.ts b/uni_modules/uni-icons/components/uni-icons/uniicons_file.ts new file mode 100644 index 0000000..98e93aa --- /dev/null +++ b/uni_modules/uni-icons/components/uni-icons/uniicons_file.ts @@ -0,0 +1,664 @@ + +export type IconsData = { + id : string + name : string + font_family : string + css_prefix_text : string + description : string + glyphs : Array +} + +export type IconsDataItem = { + font_class : string + unicode : string +} + + +export const fontData = [ + { + "font_class": "arrow-down", + "unicode": "\ue6be" + }, + { + "font_class": "arrow-left", + "unicode": "\ue6bc" + }, + { + "font_class": "arrow-right", + "unicode": "\ue6bb" + }, + { + "font_class": "arrow-up", + "unicode": "\ue6bd" + }, + { + "font_class": "auth", + "unicode": "\ue6ab" + }, + { + "font_class": "auth-filled", + "unicode": "\ue6cc" + }, + { + "font_class": "back", + "unicode": "\ue6b9" + }, + { + "font_class": "bars", + "unicode": "\ue627" + }, + { + "font_class": "calendar", + "unicode": "\ue6a0" + }, + { + "font_class": "calendar-filled", + "unicode": "\ue6c0" + }, + { + "font_class": "camera", + "unicode": "\ue65a" + }, + { + "font_class": "camera-filled", + "unicode": "\ue658" + }, + { + "font_class": "cart", + "unicode": "\ue631" + }, + { + "font_class": "cart-filled", + "unicode": "\ue6d0" + }, + { + "font_class": "chat", + "unicode": "\ue65d" + }, + { + "font_class": "chat-filled", + "unicode": "\ue659" + }, + { + "font_class": "chatboxes", + "unicode": "\ue696" + }, + { + "font_class": "chatboxes-filled", + "unicode": "\ue692" + }, + { + "font_class": "chatbubble", + "unicode": "\ue697" + }, + { + "font_class": "chatbubble-filled", + "unicode": "\ue694" + }, + { + "font_class": "checkbox", + "unicode": "\ue62b" + }, + { + "font_class": "checkbox-filled", + "unicode": "\ue62c" + }, + { + "font_class": "checkmarkempty", + "unicode": "\ue65c" + }, + { + "font_class": "circle", + "unicode": "\ue65b" + }, + { + "font_class": "circle-filled", + "unicode": "\ue65e" + }, + { + "font_class": "clear", + "unicode": "\ue66d" + }, + { + "font_class": "close", + "unicode": "\ue673" + }, + { + "font_class": "closeempty", + "unicode": "\ue66c" + }, + { + "font_class": "cloud-download", + "unicode": "\ue647" + }, + { + "font_class": "cloud-download-filled", + "unicode": "\ue646" + }, + { + "font_class": "cloud-upload", + "unicode": "\ue645" + }, + { + "font_class": "cloud-upload-filled", + "unicode": "\ue648" + }, + { + "font_class": "color", + "unicode": "\ue6cf" + }, + { + "font_class": "color-filled", + "unicode": "\ue6c9" + }, + { + "font_class": "compose", + "unicode": "\ue67f" + }, + { + "font_class": "contact", + "unicode": "\ue693" + }, + { + "font_class": "contact-filled", + "unicode": "\ue695" + }, + { + "font_class": "down", + "unicode": "\ue6b8" + }, + { + "font_class": "bottom", + "unicode": "\ue6b8" + }, + { + "font_class": "download", + "unicode": "\ue68d" + }, + { + "font_class": "download-filled", + "unicode": "\ue681" + }, + { + "font_class": "email", + "unicode": "\ue69e" + }, + { + "font_class": "email-filled", + "unicode": "\ue69a" + }, + { + "font_class": "eye", + "unicode": "\ue651" + }, + { + "font_class": "eye-filled", + "unicode": "\ue66a" + }, + { + "font_class": "eye-slash", + "unicode": "\ue6b3" + }, + { + "font_class": "eye-slash-filled", + "unicode": "\ue6b4" + }, + { + "font_class": "fire", + "unicode": "\ue6a1" + }, + { + "font_class": "fire-filled", + "unicode": "\ue6c5" + }, + { + "font_class": "flag", + "unicode": "\ue65f" + }, + { + "font_class": "flag-filled", + "unicode": "\ue660" + }, + { + "font_class": "folder-add", + "unicode": "\ue6a9" + }, + { + "font_class": "folder-add-filled", + "unicode": "\ue6c8" + }, + { + "font_class": "font", + "unicode": "\ue6a3" + }, + { + "font_class": "forward", + "unicode": "\ue6ba" + }, + { + "font_class": "gear", + "unicode": "\ue664" + }, + { + "font_class": "gear-filled", + "unicode": "\ue661" + }, + { + "font_class": "gift", + "unicode": "\ue6a4" + }, + { + "font_class": "gift-filled", + "unicode": "\ue6c4" + }, + { + "font_class": "hand-down", + "unicode": "\ue63d" + }, + { + "font_class": "hand-down-filled", + "unicode": "\ue63c" + }, + { + "font_class": "hand-up", + "unicode": "\ue63f" + }, + { + "font_class": "hand-up-filled", + "unicode": "\ue63e" + }, + { + "font_class": "headphones", + "unicode": "\ue630" + }, + { + "font_class": "heart", + "unicode": "\ue639" + }, + { + "font_class": "heart-filled", + "unicode": "\ue641" + }, + { + "font_class": "help", + "unicode": "\ue679" + }, + { + "font_class": "help-filled", + "unicode": "\ue674" + }, + { + "font_class": "home", + "unicode": "\ue662" + }, + { + "font_class": "home-filled", + "unicode": "\ue663" + }, + { + "font_class": "image", + "unicode": "\ue670" + }, + { + "font_class": "image-filled", + "unicode": "\ue678" + }, + { + "font_class": "images", + "unicode": "\ue650" + }, + { + "font_class": "images-filled", + "unicode": "\ue64b" + }, + { + "font_class": "info", + "unicode": "\ue669" + }, + { + "font_class": "info-filled", + "unicode": "\ue649" + }, + { + "font_class": "left", + "unicode": "\ue6b7" + }, + { + "font_class": "link", + "unicode": "\ue6a5" + }, + { + "font_class": "list", + "unicode": "\ue644" + }, + { + "font_class": "location", + "unicode": "\ue6ae" + }, + { + "font_class": "location-filled", + "unicode": "\ue6af" + }, + { + "font_class": "locked", + "unicode": "\ue66b" + }, + { + "font_class": "locked-filled", + "unicode": "\ue668" + }, + { + "font_class": "loop", + "unicode": "\ue633" + }, + { + "font_class": "mail-open", + "unicode": "\ue643" + }, + { + "font_class": "mail-open-filled", + "unicode": "\ue63a" + }, + { + "font_class": "map", + "unicode": "\ue667" + }, + { + "font_class": "map-filled", + "unicode": "\ue666" + }, + { + "font_class": "map-pin", + "unicode": "\ue6ad" + }, + { + "font_class": "map-pin-ellipse", + "unicode": "\ue6ac" + }, + { + "font_class": "medal", + "unicode": "\ue6a2" + }, + { + "font_class": "medal-filled", + "unicode": "\ue6c3" + }, + { + "font_class": "mic", + "unicode": "\ue671" + }, + { + "font_class": "mic-filled", + "unicode": "\ue677" + }, + { + "font_class": "micoff", + "unicode": "\ue67e" + }, + { + "font_class": "micoff-filled", + "unicode": "\ue6b0" + }, + { + "font_class": "minus", + "unicode": "\ue66f" + }, + { + "font_class": "minus-filled", + "unicode": "\ue67d" + }, + { + "font_class": "more", + "unicode": "\ue64d" + }, + { + "font_class": "more-filled", + "unicode": "\ue64e" + }, + { + "font_class": "navigate", + "unicode": "\ue66e" + }, + { + "font_class": "navigate-filled", + "unicode": "\ue67a" + }, + { + "font_class": "notification", + "unicode": "\ue6a6" + }, + { + "font_class": "notification-filled", + "unicode": "\ue6c1" + }, + { + "font_class": "paperclip", + "unicode": "\ue652" + }, + { + "font_class": "paperplane", + "unicode": "\ue672" + }, + { + "font_class": "paperplane-filled", + "unicode": "\ue675" + }, + { + "font_class": "person", + "unicode": "\ue699" + }, + { + "font_class": "person-filled", + "unicode": "\ue69d" + }, + { + "font_class": "personadd", + "unicode": "\ue69f" + }, + { + "font_class": "personadd-filled", + "unicode": "\ue698" + }, + { + "font_class": "personadd-filled-copy", + "unicode": "\ue6d1" + }, + { + "font_class": "phone", + "unicode": "\ue69c" + }, + { + "font_class": "phone-filled", + "unicode": "\ue69b" + }, + { + "font_class": "plus", + "unicode": "\ue676" + }, + { + "font_class": "plus-filled", + "unicode": "\ue6c7" + }, + { + "font_class": "plusempty", + "unicode": "\ue67b" + }, + { + "font_class": "pulldown", + "unicode": "\ue632" + }, + { + "font_class": "pyq", + "unicode": "\ue682" + }, + { + "font_class": "qq", + "unicode": "\ue680" + }, + { + "font_class": "redo", + "unicode": "\ue64a" + }, + { + "font_class": "redo-filled", + "unicode": "\ue655" + }, + { + "font_class": "refresh", + "unicode": "\ue657" + }, + { + "font_class": "refresh-filled", + "unicode": "\ue656" + }, + { + "font_class": "refreshempty", + "unicode": "\ue6bf" + }, + { + "font_class": "reload", + "unicode": "\ue6b2" + }, + { + "font_class": "right", + "unicode": "\ue6b5" + }, + { + "font_class": "scan", + "unicode": "\ue62a" + }, + { + "font_class": "search", + "unicode": "\ue654" + }, + { + "font_class": "settings", + "unicode": "\ue653" + }, + { + "font_class": "settings-filled", + "unicode": "\ue6ce" + }, + { + "font_class": "shop", + "unicode": "\ue62f" + }, + { + "font_class": "shop-filled", + "unicode": "\ue6cd" + }, + { + "font_class": "smallcircle", + "unicode": "\ue67c" + }, + { + "font_class": "smallcircle-filled", + "unicode": "\ue665" + }, + { + "font_class": "sound", + "unicode": "\ue684" + }, + { + "font_class": "sound-filled", + "unicode": "\ue686" + }, + { + "font_class": "spinner-cycle", + "unicode": "\ue68a" + }, + { + "font_class": "staff", + "unicode": "\ue6a7" + }, + { + "font_class": "staff-filled", + "unicode": "\ue6cb" + }, + { + "font_class": "star", + "unicode": "\ue688" + }, + { + "font_class": "star-filled", + "unicode": "\ue68f" + }, + { + "font_class": "starhalf", + "unicode": "\ue683" + }, + { + "font_class": "trash", + "unicode": "\ue687" + }, + { + "font_class": "trash-filled", + "unicode": "\ue685" + }, + { + "font_class": "tune", + "unicode": "\ue6aa" + }, + { + "font_class": "tune-filled", + "unicode": "\ue6ca" + }, + { + "font_class": "undo", + "unicode": "\ue64f" + }, + { + "font_class": "undo-filled", + "unicode": "\ue64c" + }, + { + "font_class": "up", + "unicode": "\ue6b6" + }, + { + "font_class": "top", + "unicode": "\ue6b6" + }, + { + "font_class": "upload", + "unicode": "\ue690" + }, + { + "font_class": "upload-filled", + "unicode": "\ue68e" + }, + { + "font_class": "videocam", + "unicode": "\ue68c" + }, + { + "font_class": "videocam-filled", + "unicode": "\ue689" + }, + { + "font_class": "vip", + "unicode": "\ue6a8" + }, + { + "font_class": "vip-filled", + "unicode": "\ue6c6" + }, + { + "font_class": "wallet", + "unicode": "\ue6b1" + }, + { + "font_class": "wallet-filled", + "unicode": "\ue6c2" + }, + { + "font_class": "weibo", + "unicode": "\ue68b" + }, + { + "font_class": "weixin", + "unicode": "\ue691" + } +] as IconsDataItem[] + +// export const fontData = JSON.parse(fontDataJson) diff --git a/uni_modules/uni-icons/components/uni-icons/uniicons_file_vue.js b/uni_modules/uni-icons/components/uni-icons/uniicons_file_vue.js new file mode 100644 index 0000000..1cd11e1 --- /dev/null +++ b/uni_modules/uni-icons/components/uni-icons/uniicons_file_vue.js @@ -0,0 +1,649 @@ + +export const fontData = [ + { + "font_class": "arrow-down", + "unicode": "\ue6be" + }, + { + "font_class": "arrow-left", + "unicode": "\ue6bc" + }, + { + "font_class": "arrow-right", + "unicode": "\ue6bb" + }, + { + "font_class": "arrow-up", + "unicode": "\ue6bd" + }, + { + "font_class": "auth", + "unicode": "\ue6ab" + }, + { + "font_class": "auth-filled", + "unicode": "\ue6cc" + }, + { + "font_class": "back", + "unicode": "\ue6b9" + }, + { + "font_class": "bars", + "unicode": "\ue627" + }, + { + "font_class": "calendar", + "unicode": "\ue6a0" + }, + { + "font_class": "calendar-filled", + "unicode": "\ue6c0" + }, + { + "font_class": "camera", + "unicode": "\ue65a" + }, + { + "font_class": "camera-filled", + "unicode": "\ue658" + }, + { + "font_class": "cart", + "unicode": "\ue631" + }, + { + "font_class": "cart-filled", + "unicode": "\ue6d0" + }, + { + "font_class": "chat", + "unicode": "\ue65d" + }, + { + "font_class": "chat-filled", + "unicode": "\ue659" + }, + { + "font_class": "chatboxes", + "unicode": "\ue696" + }, + { + "font_class": "chatboxes-filled", + "unicode": "\ue692" + }, + { + "font_class": "chatbubble", + "unicode": "\ue697" + }, + { + "font_class": "chatbubble-filled", + "unicode": "\ue694" + }, + { + "font_class": "checkbox", + "unicode": "\ue62b" + }, + { + "font_class": "checkbox-filled", + "unicode": "\ue62c" + }, + { + "font_class": "checkmarkempty", + "unicode": "\ue65c" + }, + { + "font_class": "circle", + "unicode": "\ue65b" + }, + { + "font_class": "circle-filled", + "unicode": "\ue65e" + }, + { + "font_class": "clear", + "unicode": "\ue66d" + }, + { + "font_class": "close", + "unicode": "\ue673" + }, + { + "font_class": "closeempty", + "unicode": "\ue66c" + }, + { + "font_class": "cloud-download", + "unicode": "\ue647" + }, + { + "font_class": "cloud-download-filled", + "unicode": "\ue646" + }, + { + "font_class": "cloud-upload", + "unicode": "\ue645" + }, + { + "font_class": "cloud-upload-filled", + "unicode": "\ue648" + }, + { + "font_class": "color", + "unicode": "\ue6cf" + }, + { + "font_class": "color-filled", + "unicode": "\ue6c9" + }, + { + "font_class": "compose", + "unicode": "\ue67f" + }, + { + "font_class": "contact", + "unicode": "\ue693" + }, + { + "font_class": "contact-filled", + "unicode": "\ue695" + }, + { + "font_class": "down", + "unicode": "\ue6b8" + }, + { + "font_class": "bottom", + "unicode": "\ue6b8" + }, + { + "font_class": "download", + "unicode": "\ue68d" + }, + { + "font_class": "download-filled", + "unicode": "\ue681" + }, + { + "font_class": "email", + "unicode": "\ue69e" + }, + { + "font_class": "email-filled", + "unicode": "\ue69a" + }, + { + "font_class": "eye", + "unicode": "\ue651" + }, + { + "font_class": "eye-filled", + "unicode": "\ue66a" + }, + { + "font_class": "eye-slash", + "unicode": "\ue6b3" + }, + { + "font_class": "eye-slash-filled", + "unicode": "\ue6b4" + }, + { + "font_class": "fire", + "unicode": "\ue6a1" + }, + { + "font_class": "fire-filled", + "unicode": "\ue6c5" + }, + { + "font_class": "flag", + "unicode": "\ue65f" + }, + { + "font_class": "flag-filled", + "unicode": "\ue660" + }, + { + "font_class": "folder-add", + "unicode": "\ue6a9" + }, + { + "font_class": "folder-add-filled", + "unicode": "\ue6c8" + }, + { + "font_class": "font", + "unicode": "\ue6a3" + }, + { + "font_class": "forward", + "unicode": "\ue6ba" + }, + { + "font_class": "gear", + "unicode": "\ue664" + }, + { + "font_class": "gear-filled", + "unicode": "\ue661" + }, + { + "font_class": "gift", + "unicode": "\ue6a4" + }, + { + "font_class": "gift-filled", + "unicode": "\ue6c4" + }, + { + "font_class": "hand-down", + "unicode": "\ue63d" + }, + { + "font_class": "hand-down-filled", + "unicode": "\ue63c" + }, + { + "font_class": "hand-up", + "unicode": "\ue63f" + }, + { + "font_class": "hand-up-filled", + "unicode": "\ue63e" + }, + { + "font_class": "headphones", + "unicode": "\ue630" + }, + { + "font_class": "heart", + "unicode": "\ue639" + }, + { + "font_class": "heart-filled", + "unicode": "\ue641" + }, + { + "font_class": "help", + "unicode": "\ue679" + }, + { + "font_class": "help-filled", + "unicode": "\ue674" + }, + { + "font_class": "home", + "unicode": "\ue662" + }, + { + "font_class": "home-filled", + "unicode": "\ue663" + }, + { + "font_class": "image", + "unicode": "\ue670" + }, + { + "font_class": "image-filled", + "unicode": "\ue678" + }, + { + "font_class": "images", + "unicode": "\ue650" + }, + { + "font_class": "images-filled", + "unicode": "\ue64b" + }, + { + "font_class": "info", + "unicode": "\ue669" + }, + { + "font_class": "info-filled", + "unicode": "\ue649" + }, + { + "font_class": "left", + "unicode": "\ue6b7" + }, + { + "font_class": "link", + "unicode": "\ue6a5" + }, + { + "font_class": "list", + "unicode": "\ue644" + }, + { + "font_class": "location", + "unicode": "\ue6ae" + }, + { + "font_class": "location-filled", + "unicode": "\ue6af" + }, + { + "font_class": "locked", + "unicode": "\ue66b" + }, + { + "font_class": "locked-filled", + "unicode": "\ue668" + }, + { + "font_class": "loop", + "unicode": "\ue633" + }, + { + "font_class": "mail-open", + "unicode": "\ue643" + }, + { + "font_class": "mail-open-filled", + "unicode": "\ue63a" + }, + { + "font_class": "map", + "unicode": "\ue667" + }, + { + "font_class": "map-filled", + "unicode": "\ue666" + }, + { + "font_class": "map-pin", + "unicode": "\ue6ad" + }, + { + "font_class": "map-pin-ellipse", + "unicode": "\ue6ac" + }, + { + "font_class": "medal", + "unicode": "\ue6a2" + }, + { + "font_class": "medal-filled", + "unicode": "\ue6c3" + }, + { + "font_class": "mic", + "unicode": "\ue671" + }, + { + "font_class": "mic-filled", + "unicode": "\ue677" + }, + { + "font_class": "micoff", + "unicode": "\ue67e" + }, + { + "font_class": "micoff-filled", + "unicode": "\ue6b0" + }, + { + "font_class": "minus", + "unicode": "\ue66f" + }, + { + "font_class": "minus-filled", + "unicode": "\ue67d" + }, + { + "font_class": "more", + "unicode": "\ue64d" + }, + { + "font_class": "more-filled", + "unicode": "\ue64e" + }, + { + "font_class": "navigate", + "unicode": "\ue66e" + }, + { + "font_class": "navigate-filled", + "unicode": "\ue67a" + }, + { + "font_class": "notification", + "unicode": "\ue6a6" + }, + { + "font_class": "notification-filled", + "unicode": "\ue6c1" + }, + { + "font_class": "paperclip", + "unicode": "\ue652" + }, + { + "font_class": "paperplane", + "unicode": "\ue672" + }, + { + "font_class": "paperplane-filled", + "unicode": "\ue675" + }, + { + "font_class": "person", + "unicode": "\ue699" + }, + { + "font_class": "person-filled", + "unicode": "\ue69d" + }, + { + "font_class": "personadd", + "unicode": "\ue69f" + }, + { + "font_class": "personadd-filled", + "unicode": "\ue698" + }, + { + "font_class": "personadd-filled-copy", + "unicode": "\ue6d1" + }, + { + "font_class": "phone", + "unicode": "\ue69c" + }, + { + "font_class": "phone-filled", + "unicode": "\ue69b" + }, + { + "font_class": "plus", + "unicode": "\ue676" + }, + { + "font_class": "plus-filled", + "unicode": "\ue6c7" + }, + { + "font_class": "plusempty", + "unicode": "\ue67b" + }, + { + "font_class": "pulldown", + "unicode": "\ue632" + }, + { + "font_class": "pyq", + "unicode": "\ue682" + }, + { + "font_class": "qq", + "unicode": "\ue680" + }, + { + "font_class": "redo", + "unicode": "\ue64a" + }, + { + "font_class": "redo-filled", + "unicode": "\ue655" + }, + { + "font_class": "refresh", + "unicode": "\ue657" + }, + { + "font_class": "refresh-filled", + "unicode": "\ue656" + }, + { + "font_class": "refreshempty", + "unicode": "\ue6bf" + }, + { + "font_class": "reload", + "unicode": "\ue6b2" + }, + { + "font_class": "right", + "unicode": "\ue6b5" + }, + { + "font_class": "scan", + "unicode": "\ue62a" + }, + { + "font_class": "search", + "unicode": "\ue654" + }, + { + "font_class": "settings", + "unicode": "\ue653" + }, + { + "font_class": "settings-filled", + "unicode": "\ue6ce" + }, + { + "font_class": "shop", + "unicode": "\ue62f" + }, + { + "font_class": "shop-filled", + "unicode": "\ue6cd" + }, + { + "font_class": "smallcircle", + "unicode": "\ue67c" + }, + { + "font_class": "smallcircle-filled", + "unicode": "\ue665" + }, + { + "font_class": "sound", + "unicode": "\ue684" + }, + { + "font_class": "sound-filled", + "unicode": "\ue686" + }, + { + "font_class": "spinner-cycle", + "unicode": "\ue68a" + }, + { + "font_class": "staff", + "unicode": "\ue6a7" + }, + { + "font_class": "staff-filled", + "unicode": "\ue6cb" + }, + { + "font_class": "star", + "unicode": "\ue688" + }, + { + "font_class": "star-filled", + "unicode": "\ue68f" + }, + { + "font_class": "starhalf", + "unicode": "\ue683" + }, + { + "font_class": "trash", + "unicode": "\ue687" + }, + { + "font_class": "trash-filled", + "unicode": "\ue685" + }, + { + "font_class": "tune", + "unicode": "\ue6aa" + }, + { + "font_class": "tune-filled", + "unicode": "\ue6ca" + }, + { + "font_class": "undo", + "unicode": "\ue64f" + }, + { + "font_class": "undo-filled", + "unicode": "\ue64c" + }, + { + "font_class": "up", + "unicode": "\ue6b6" + }, + { + "font_class": "top", + "unicode": "\ue6b6" + }, + { + "font_class": "upload", + "unicode": "\ue690" + }, + { + "font_class": "upload-filled", + "unicode": "\ue68e" + }, + { + "font_class": "videocam", + "unicode": "\ue68c" + }, + { + "font_class": "videocam-filled", + "unicode": "\ue689" + }, + { + "font_class": "vip", + "unicode": "\ue6a8" + }, + { + "font_class": "vip-filled", + "unicode": "\ue6c6" + }, + { + "font_class": "wallet", + "unicode": "\ue6b1" + }, + { + "font_class": "wallet-filled", + "unicode": "\ue6c2" + }, + { + "font_class": "weibo", + "unicode": "\ue68b" + }, + { + "font_class": "weixin", + "unicode": "\ue691" + } +] + +// export const fontData = JSON.parse(fontDataJson) diff --git a/uni_modules/uni-icons/package.json b/uni_modules/uni-icons/package.json new file mode 100644 index 0000000..6b681b4 --- /dev/null +++ b/uni_modules/uni-icons/package.json @@ -0,0 +1,89 @@ +{ + "id": "uni-icons", + "displayName": "uni-icons 图标", + "version": "2.0.10", + "description": "图标组件,用于展示移动端常见的图标,可自定义颜色、大小。", + "keywords": [ + "uni-ui", + "uniui", + "icon", + "图标" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "^3.2.14" + }, + "directories": { + "example": "../../temps/example_temps" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": ["uni-scss"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "n" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y", + "app-uvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y", + "钉钉": "y", + "快手": "y", + "飞书": "y", + "京东": "y" + }, + "快应用": { + "华为": "y", + "联盟": "y" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} diff --git a/uni_modules/uni-icons/readme.md b/uni_modules/uni-icons/readme.md new file mode 100644 index 0000000..86234ba --- /dev/null +++ b/uni_modules/uni-icons/readme.md @@ -0,0 +1,8 @@ +## Icons 图标 +> **组件名:uni-icons** +> 代码块: `uIcons` + +用于展示 icons 图标 。 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-icons) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 diff --git a/uni_modules/uni-id-common/changelog.md b/uni_modules/uni-id-common/changelog.md new file mode 100644 index 0000000..1ef0882 --- /dev/null +++ b/uni_modules/uni-id-common/changelog.md @@ -0,0 +1,36 @@ +## 1.0.18(2024-07-08) +- checkToken时如果传入的token为空则返回uni-id-check-token-failed错误码以便uniIdRouter能正常跳转 +## 1.0.17(2024-04-26) +- 兼容uni-app-x对客户端uniPlatform的调整(uni-app-x内uniPlatform区分app-android、app-ios) +## 1.0.16(2023-04-25) +- 新增maxTokenLength配置,用于限制数据库用户记录token数组的最大长度 +## 1.0.15(2023-04-06) +- 修复部分语言国际化出错的Bug +## 1.0.14(2023-03-07) +- 修复 admin用户包含其他角色时未包含在token的Bug +## 1.0.13(2022-07-21) +- 修复 创建token时未传角色权限信息生成的token不正确的bug +## 1.0.12(2022-07-15) +- 提升与旧版本uni-id的兼容性(补充读取配置文件时回退平台app-plus、h5),但是仍推荐使用新平台名进行配置(app、web) +## 1.0.11(2022-07-14) +- 修复 部分情况下报`read property 'reduce' of undefined`的错误 +## 1.0.10(2022-07-11) +- 将token存储在用户表的token字段内,与旧版本uni-id保持一致 +## 1.0.9(2022-07-01) +- checkToken兼容token内未缓存角色权限的情况,此时将查库获取角色权限 +## 1.0.8(2022-07-01) +- 修复clientDB默认依赖时部分情况下获取不到uni-id配置的Bug +## 1.0.7(2022-06-30) +- 修复config文件不合法时未抛出具体错误的Bug +## 1.0.6(2022-06-28) +- 移除插件内的数据表schema +## 1.0.5(2022-06-27) +- 修复使用多应用配置时报`Cannot read property 'appId' of undefined`的Bug +## 1.0.4(2022-06-27) +- 修复使用自定义token内容功能报错的Bug [详情](https://ask.dcloud.net.cn/question/147945) +## 1.0.2(2022-06-23) +- 对齐旧版本uni-id默认配置 +## 1.0.1(2022-06-22) +- 补充对uni-config-center的依赖 +## 1.0.0(2022-06-21) +- 提供uni-id token创建、校验、刷新接口,简化旧版uni-id公共模块 diff --git a/uni_modules/uni-id-common/package.json b/uni_modules/uni-id-common/package.json new file mode 100644 index 0000000..3f77a7f --- /dev/null +++ b/uni_modules/uni-id-common/package.json @@ -0,0 +1,85 @@ +{ + "id": "uni-id-common", + "displayName": "uni-id-common", + "version": "1.0.18", + "description": "包含uni-id token生成、校验、刷新功能的云函数公共模块", + "keywords": [ + "uni-id-common", + "uniCloud", + "token", + "权限" + ], + "repository": "https://gitcode.net/dcloud/uni-id-common", + "engines": { + "HBuilderX": "^3.1.0" + }, + "dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "", + "type": "unicloud-template-function" + }, + "uni_modules": { + "dependencies": ["uni-config-center"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "n" + }, + "client": { + "Vue": { + "vue2": "u", + "vue3": "u" + }, + "App": { + "app-vue": "u", + "app-nvue": "u" + }, + "H5-mobile": { + "Safari": "u", + "Android Browser": "u", + "微信浏览器(Android)": "u", + "QQ浏览器(Android)": "u" + }, + "H5-pc": { + "Chrome": "u", + "IE": "u", + "Edge": "u", + "Firefox": "u", + "Safari": "u" + }, + "小程序": { + "微信": "u", + "阿里": "u", + "百度": "u", + "字节跳动": "u", + "QQ": "u", + "钉钉": "u", + "快手": "u", + "飞书": "u", + "京东": "u" + }, + "快应用": { + "华为": "u", + "联盟": "u" + } + } + } + } +} diff --git a/uni_modules/uni-id-common/readme.md b/uni_modules/uni-id-common/readme.md new file mode 100644 index 0000000..5f6a37a --- /dev/null +++ b/uni_modules/uni-id-common/readme.md @@ -0,0 +1,3 @@ +# uni-id-common + +文档请参考:[uni-id-common](https://uniapp.dcloud.net.cn/uniCloud/uni-id-common.html) \ No newline at end of file diff --git a/uni_modules/uni-id-common/uniCloud/cloudfunctions/common/uni-id-common/index.js b/uni_modules/uni-id-common/uniCloud/cloudfunctions/common/uni-id-common/index.js new file mode 100644 index 0000000..a8b99d0 --- /dev/null +++ b/uni_modules/uni-id-common/uniCloud/cloudfunctions/common/uni-id-common/index.js @@ -0,0 +1 @@ +"use strict";var e,t=(e=require("crypto"))&&"object"==typeof e&&"default"in e?e.default:e;const n={TOKEN_EXPIRED:"uni-id-token-expired",CHECK_TOKEN_FAILED:"uni-id-check-token-failed",PARAM_REQUIRED:"uni-id-param-required",ACCOUNT_EXISTS:"uni-id-account-exists",ACCOUNT_NOT_EXISTS:"uni-id-account-not-exists",ACCOUNT_CONFLICT:"uni-id-account-conflict",ACCOUNT_BANNED:"uni-id-account-banned",ACCOUNT_AUDITING:"uni-id-account-auditing",ACCOUNT_AUDIT_FAILED:"uni-id-account-audit-failed",ACCOUNT_CLOSED:"uni-id-account-closed"};function i(e){return!!e&&("object"==typeof e||"function"==typeof e)&&"function"==typeof e.then}function r(e){if(!e)return;const t=e.match(/^(\d+).(\d+).(\d+)/);return t?t.slice(1,4).map(e=>parseInt(e)):void 0}function o(e,t){const n=r(e),i=r(t);return n?i?function(e,t){const n=Math.max(e.length,t.length);for(let i=0;ir)return 1;if(n=e)throw new Error("Config error, tokenExpiresThreshold should be less than tokenExpiresIn");t>e/2&&console.warn(`Please check whether the tokenExpiresThreshold configuration is set too large, tokenExpiresThreshold: ${t}, tokenExpiresIn: ${e}`)}get customToken(){return this.uniId.interceptorMap.get("customToken")}isTokenInDb(e){return o(e,"1.0.10")>=0}async getUserRecord(){if(this.userRecord)return this.userRecord;const e=await C.doc(this.uid).get();if(this.userRecord=e.data[0],!this.userRecord)throw{errCode:n.ACCOUNT_NOT_EXISTS};switch(this.userRecord.status){case void 0:case 0:break;case 1:throw{errCode:n.ACCOUNT_BANNED};case 2:throw{errCode:n.ACCOUNT_AUDITING};case 3:throw{errCode:n.ACCOUNT_AUDIT_FAILED};case 4:throw{errCode:n.ACCOUNT_CLOSED}}if(this.oldTokenPayload){if(this.isTokenInDb(this.oldTokenPayload.uniIdVersion)){if(-1===(this.userRecord.token||[]).indexOf(this.oldToken))throw{errCode:n.CHECK_TOKEN_FAILED}}if(this.userRecord.valid_token_date&&this.userRecord.valid_token_date>1e3*this.oldTokenPayload.iat)throw{errCode:n.TOKEN_EXPIRED}}return this.userRecord}async updateUserRecord(e){await C.doc(this.uid).update(e)}async getUserPermission(){if(this.userPermission)return this.userPermission;const e=(await this.getUserRecord()).role||[];if(0===e.length)return this.userPermission={role:[],permission:[]},this.userPermission;if(e.includes("admin"))return this.userPermission={role:e,permission:[]},this.userPermission;const t=await T.where({role_id:I.in(e)}).get(),n=(i=t.data.reduce((e,t)=>(t.permission&&e.push(...t.permission),e),[]),Array.from(new Set(i)));var i;return this.userPermission={role:e,permission:n},this.userPermission}async _createToken({uid:e,role:t,permission:i}={}){if(!t||!i){const e=await this.getUserPermission();t=e.role,i=e.permission}let r={uid:e,role:t,permission:i};if(this.uniId.interceptorMap.has("customToken")){const n=this.uniId.interceptorMap.get("customToken");if("function"!=typeof n)throw new Error("Invalid custom token file");r=await n({uid:e,role:t,permission:i})}const o=Date.now(),{tokenSecret:s,tokenExpiresIn:c,maxTokenLength:a=10}=this.config,u=g({...r,uniIdVersion:"1.0.18"},s,{expiresIn:c}),d=await this.getUserRecord(),l=(d.token||[]).filter(e=>{try{const t=this._checkToken(e);if(d.valid_token_date&&d.valid_token_date>1e3*t.iat)return!1}catch(e){if(e.errCode===n.TOKEN_EXPIRED)return!1}return!0});return l.push(u),l.length>a&&l.splice(0,l.length-a),await this.updateUserRecord({last_login_ip:this.clientInfo.clientIP,last_login_date:o,token:l}),{token:u,tokenExpired:o+1e3*c}}async createToken({uid:e,role:t,permission:i}={}){if(!e)throw{errCode:n.PARAM_REQUIRED,errMsgValue:{param:"uid"}};this.uid=e;const{token:r,tokenExpired:o}=await this._createToken({uid:e,role:t,permission:i});return{errCode:0,token:r,tokenExpired:o}}async refreshToken({token:e}={}){if(!e)throw{errCode:n.PARAM_REQUIRED,errMsgValue:{param:"token"}};this.oldToken=e;const t=this._checkToken(e);this.uid=t.uid,this.oldTokenPayload=t;const{uid:i}=t,{role:r,permission:o}=await this.getUserPermission(),{token:s,tokenExpired:c}=await this._createToken({uid:i,role:r,permission:o});return{errCode:0,token:s,tokenExpired:c}}_checkToken(e){const{tokenSecret:t}=this.config;let i;try{i=k(e,t)}catch(e){if("TokenExpiredError"===e.name)throw{errCode:n.TOKEN_EXPIRED};throw{errCode:n.CHECK_TOKEN_FAILED}}return i}async checkToken(e,{autoRefresh:t=!0}={}){if(!e)throw{errCode:n.CHECK_TOKEN_FAILED};this.oldToken=e;const i=this._checkToken(e);this.uid=i.uid,this.oldTokenPayload=i;const{tokenExpiresThreshold:r}=this.config,{uid:o,role:s,permission:c}=i,a={role:s,permission:c};if(!s&&!c){const{role:e,permission:t}=await this.getUserPermission();a.role=e,a.permission=t}if(!r||!t){const e={code:0,errCode:0,...i,...a};return delete e.uniIdVersion,e}const u=Date.now();let d={};1e3*i.exp-u<1e3*r&&(d=await this._createToken({uid:o}));const l={code:0,errCode:0,...i,...a,...d};return delete l.uniIdVersion,l}}var m=Object.freeze({__proto__:null,checkToken:async function(e,{autoRefresh:t=!0}={}){return new E({uniId:this}).checkToken(e,{autoRefresh:t})},createToken:async function({uid:e,role:t,permission:n}={}){return new E({uniId:this}).createToken({uid:e,role:t,permission:n})},refreshToken:async function({token:e}={}){return new E({uniId:this}).refreshToken({token:e})}});const w=require("uni-config-center")({pluginId:"uni-id"});class x{constructor({context:e,clientInfo:t,config:n}={}){this._clientInfo=e?function(e){return{appId:e.APPID,platform:e.PLATFORM,locale:e.LOCALE,clientIP:e.CLIENTIP,deviceId:e.DEVICEID}}(e):t,this._config=n,this.config=this._getOriginConfig(),this.interceptorMap=new Map,w.hasFile("custom-token.js")&&this.setInterceptor("customToken",require(w.resolve("custom-token.js")));this._i18n=uniCloud.initI18n({locale:this._clientInfo.locale,fallbackLocale:"zh-Hans",messages:JSON.parse(JSON.stringify(d))}),d[this._i18n.locale]||this._i18n.setLocale("zh-Hans")}setInterceptor(e,t){this.interceptorMap.set(e,t)}_t(...e){return this._i18n.t(...e)}_parseOriginConfig(e){return Array.isArray(e)?e:e[0]?Object.values(e):e}_getOriginConfig(){if(this._config)return this._config;if(w.hasFile("config.json")){let e;try{e=w.config()}catch(e){throw new Error("Invalid uni-id config file\n"+e.message)}return this._parseOriginConfig(e)}try{return this._parseOriginConfig(require("uni-id/config.json"))}catch(e){throw new Error("Invalid uni-id config file")}}_getAppConfig(){const e=this._getOriginConfig();return Array.isArray(e)?e.find(e=>e.dcloudAppid===this._clientInfo.appId)||e.find(e=>e.isDefaultConfig):e}_getPlatformConfig(){const e=this._getAppConfig();if(!e)throw new Error(`Config for current app (${this._clientInfo.appId}) was not found, please check your config file or client appId`);let t;switch(["app-plus","app-android","app-ios"].indexOf(this._clientInfo.platform)>-1&&(this._clientInfo.platform="app"),"h5"===this._clientInfo.platform&&(this._clientInfo.platform="web"),this._clientInfo.platform){case"web":t="h5";break;case"app":t="app-plus"}const n=[{tokenExpiresIn:7200,tokenExpiresThreshold:1200,passwordErrorLimit:6,passwordErrorRetryTime:3600},e];t&&e[t]&&n.push(e[t]),n.push(e[this._clientInfo.platform]);const i=Object.assign(...n);return["tokenSecret","tokenExpiresIn"].forEach(e=>{if(!i||!i[e])throw new Error(`Config parameter missing, ${e} is required`)}),i}_getConfig(){return this._getPlatformConfig()}}for(const e in m)x.prototype[e]=m[e];function y(e){const t=new x(e);return new Proxy(t,{get(e,t){if(t in e&&0!==t.indexOf("_")){if("function"==typeof e[t])return(n=e[t],function(){let e;try{e=n.apply(this,arguments)}catch(e){if(a(e))return c.call(this,e),e;throw e}return i(e)?e.then(e=>(a(e)&&c.call(this,e),e),e=>{if(a(e))return c.call(this,e),e;throw e}):(a(e)&&c.call(this,e),e)}).bind(e);if("context"!==t&&"config"!==t)return e[t]}var n}})}x.prototype.createInstance=y;const O={createInstance:y};module.exports=O; diff --git a/uni_modules/uni-id-common/uniCloud/cloudfunctions/common/uni-id-common/package.json b/uni_modules/uni-id-common/uniCloud/cloudfunctions/common/uni-id-common/package.json new file mode 100644 index 0000000..f95ae5f --- /dev/null +++ b/uni_modules/uni-id-common/uniCloud/cloudfunctions/common/uni-id-common/package.json @@ -0,0 +1,16 @@ +{ + "name": "uni-id-common", + "version": "1.0.18", + "description": "uni-id token生成、校验、刷新", + "main": "index.js", + "homepage": "https://uniapp.dcloud.io/uniCloud/uni-id-common.html", + "repository": { + "type": "git", + "url": "git+https://gitee.com/dcloud/uni-id-common.git" + }, + "author": "DCloud", + "license": "Apache-2.0", + "dependencies": { + "uni-config-center": "file:../../../../../uni-config-center/uniCloud/cloudfunctions/common/uni-config-center" + } +} \ No newline at end of file diff --git a/uni_modules/uni-id-pages/changelog.md b/uni_modules/uni-id-pages/changelog.md new file mode 100644 index 0000000..b0646d7 --- /dev/null +++ b/uni_modules/uni-id-pages/changelog.md @@ -0,0 +1,181 @@ +## 1.1.20(2024-04-28) +- uni-id-co 兼容uni-app-x对客户端uniPlatform的调整(uni-app-x内uniPlatform区分app-android、app-ios) +## 1.1.19(2024-03-20) +- uni-id-co 修复 实人认证的认证照片在阿里云服务空间没有保存到指定路径下的Bug +- uni-id-co 修复 云对象开发依赖未移除的Bug +## 1.1.18(2024-02-20) +- 修复 PC设置头像无效的问题 +## 1.1.17(2023-12-14) +- uni-id-co 移除一键登录、短信的调用凭据 +## 1.1.16(2023-10-18) +- 修复 当不满足一键登录时页面回退无法继续登录的问题 +## 1.1.15(2023-07-13) +- uni-id-co 修复 QQ登录时不存在头像时报错的问题 +## 1.1.14(2023-05-19) +- 修复 退出登录不会跳转至登录页的问题 +## 1.1.13(2023-05-10) +- 修复 启用摇树优化 报错的问题 +## 1.1.12(2023-05-05) +- uni-id-co 新增 调用 add-user 接口创建用户时允许触发 beforeRegister 钩子方法,beforeRegister 钩子[详见](https://uniapp.dcloud.net.cn/uniCloud/uni-id-summary.html#before-register) +- uni-id-co 新增 自无 unionid 到有 unionid 状态进行登录时为用户补充 unionid 字段 +- uni-id-co 修复 i18n 在特定场景下报错的 bug +- uni-id-co 修复 跨平台解绑微信/QQ时无法解绑的 bug +- uni-id-co 修复 微信小程序等平台创建验证码时无法展示的 bug +- uni-id-co 修复 更新 push_clientid 时因 device_id 没有变化导致无法更新 +## 1.1.11(2023-03-24) +- 修复 tabbar页面因为token无效而强制跳转至登录页面(url参数包含`uniIdRedirectUrl`)后无法返回的问题 +## 1.1.10(2023-03-24) +- 修复 PC微信扫码登录跳转地址错误 +- uni-id-co 新增 请求鉴权支持 uni-cloud-s2s 模块验证签名 [uni-cloud-s2s文档](https://uniapp.dcloud.net.cn/uniCloud/uni-cloud-s2s.html) +## 1.1.9(2023-03-24) +- 修复 跳转至登录页面的url参数包含`uniIdRedirectUrl`后无法返回的问题 +## 1.1.8(2023-03-02) +- 修复 调试模式下没有对微信授权手机号登录方式进行配置检测 +## 1.1.7(2023-02-27) +- 【重要】新增 实名认证功能 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-summary.html#frv) +## 1.1.6(2023-02-24) +- uni-id-co 新增 注册用户时允许配置默认角色 [文档](https://uniapp.dcloud.net.cn/uniCloud/uni-id-summary.html#config-defult-role) +- uni-id-co 优化 `updateUserInfoByExternal`接口,允许修改头像、性别 +- uni-id-co 修复 请求签名密钥字段 `requestAuthSecret` 缺少为空判断 +- uni-id-co 修复 `externalRegister`接口头像未使用`avatar_file`字段保存 +- 修复 web微信登录回调地址不正确 +## 1.1.5(2023-02-23) +- 更新 微信小程序端 更新头像信息,如果是使用微信的头像则不再调用裁剪接口 +## 1.1.4(2023-02-21) +- 修复 部分情况下 `uniIdRedirectUrl` 参数无效的问题 +## 1.1.3(2023-02-20) +- 修复 非微信小程序端报`TypeError: uni.hideHomeButton is not a function`的问题 +## 1.1.2(2023-02-10) +- 新增 微信小程序端 首页需强制登录时,隐藏返回首页按钮 +- uni-id-co 新增 外部联登后修改用户信息接口(updateUserInfoByExternal) [文档](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#external-update-userinfo) +- uni-id-co 优化外部联登接口(登录、注册)逻辑 +## 1.1.1(2023-02-02) +- 新增 微信小程序端 支持选择使用微信资料的“头像”和“昵称” 设置用户资料 [详情参考](https://wdoc-76491.picgzc.qpic.cn/MTY4ODg1MDUyNzQyMDUxNw_21263_rTNhg68FTngQGdvQ_1647431233?w=1280&h=695.7176470588236) +## 1.1.0(2023-01-31) +- 【重要】优化 小程序端资源包大小(运行时大小为:731KB,发行后为:583KB;注:可以直接将本插件作为分包使用) +- 更新 微信小程序端 上传头像功能 用`wx.cropImage`实现图片裁剪 +- 修复 选择一键登录时会先显示 非密码登录页面的问题 +- 修复 一键登录 点击右上角的关闭按钮没有返回上一页的问题 +## 1.0.41(2023-01-16) +- 优化 压缩依赖的文件资源大小 +## 1.0.40(2023-01-16) +- 更新依赖的 验证码插件`uni-captcha`版本的版本为 0.6.4 修复 部分情况下APP端无法获取验证码的问题 [详情参考](https://ext.dcloud.net.cn/plugin?id=4048) +- 修复 客户端token过期后,点击退出登录按钮报错的问题 +- uni-id-co 修复 updateUser 接口`手机号`和`邮箱`参数值为空字符串时,修改无效的问题 +## 1.0.39(2022-12-28) +- uni-id-co 修复 URL化时第三方登录无法获取 uniPlatform 参数 +- uni-id-co 修复 validator error +## 1.0.38(2022-12-26) +- uni-id-co 优化 手机号与邮箱验证规则为空字符串时不校验 +## 1.0.37(2022-12-09) +- 优化admin端样式 +## 1.0.36(2022-12-08) +- uni-id-co 修复 `updateUser` 接口部分参数为空时数据修改异常 +## 1.0.35(2022-11-30) +- uni-id-co 新增 匹配到的用户不可在当前应用登录时的错误码 `uni-id-account-not-exists-in-current-app` [错误码说明](https://uniapp.dcloud.net.cn/uniCloud/uni-id-summary.html#errcode) +## 1.0.34(2022-11-29) +- 优化 toast 错误提示时间为3秒 +- uni-id-co 修复 无法从 clientInfo 中获取 uniIdToken +## 1.0.33(2022-11-25) +- uni-id-co 新增 外部系统联登接口,可为外部系统创建与uni-id相对应的账号,使该账号可以使用依赖uniId的系统及功能 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#external) +- uni-id-co 新增 URL化请求时鉴权签名验证 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#http-reqeust-auth) +- uni-id-co 修复 微信登录时用户未设置头像的报错问题 +## 1.0.32(2022-11-21) +- 新增 设置密码页面 +- 新增 登录后跳转设置密码页面配置项`setPasswordAfterLogin` [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#set-pwd-after-login) +- uni-id-co 新增 设置密码接口 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#set-pwd) +## 1.0.31(2022-11-16) +- uni-id-co 修复 验证码可能无法收到的bug +## 1.0.30(2022-11-11) +- uni-id-co 修复 用户只有openid时绑定微信/QQ报错 +## 1.0.29(2022-11-10) +- uni-id-co 支持URL化方式请求 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#adapter-http) +## 1.0.28(2022-11-09) +- uni-id-co 升级密码加密算法,支持hmac-sha256加密 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-summary.html#password-safe) +- uni-id-co 新增 开发者可以自定义密码加密规则 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-summary.html#custom-password-encrypt) +- uni-id-co 新增 支持将其他系统用户迁移至uni-id [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-summary.html#move-users-to-uni-id) +## 1.0.27(2022-10-26) +- uni-id-co 新增 secureNetworkHandshakeByWeixin 接口,用于建立和微信小程序的安全网络连接 +## 1.0.26(2022-10-18) +- 修复 uni-id-pages 导入时异常的Bug +## 1.0.25(2022-10-14) +- uni-id-co 增加 微信授权手机号登录方式 [文档](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#login-by-weixin-mobile) +- uni-id-co 增加 解绑第三方平台账号 [文档](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#unbind-third-account) +- uni-id-co 微信绑定手机号支持通过`getPhoneNumber`事件回调的`code`绑定 [文档](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#bind-mobile-by-mp-weixin) +- 修复 sendSmsCode 接口未在参数内传递 templateId 时 未能从配置文件读取 templateId 的Bug +## 1.0.24(2022-10-08) +- 修复 报uni-id-users表schema内错误的bug +## 1.0.23(2022-10-08) +- 修复 vue3下vite编译发行打包失败 +- 修复 某些情况下注册账号,报TypeErroe:Cannot read properties of undefined (reading ’showToast‘)的错误 +## 1.0.22(2022-09-23) +- 修复 某些情况下,修改密码报“两次输入密码不一致”的bug +## 1.0.21(2022-09-21) +- 修复 store.hasLogin的值在某些情况下会出错的bug +## 1.0.20(2022-09-21) +- 新增 store 账号信息状态管理,详情:用户中心页面 路径:`/uni_modules/uni-id-pages/pages/userinfo/userinfo` +## 1.0.19(2022-09-20) +- 修复 小程序端,使用将自定义节点设置成[虚拟节点](https://uniapp.dcloud.net.cn/tutorial/vue-api.html#%E5%85%B6%E4%BB%96%E9%85%8D%E7%BD%AE)的uni-ui组件,样式错乱的问题 +## 1.0.18(2022-09-20) +- 修复 微信小程序端 WXSS 编译报错的bug +## 1.0.17(2022.09-19) +- 修复 无法退出登录的bug +## 1.0.16(2022-09-19) +- 修复 在 Edge 浏览器下 input[type="password"] 会出现浏览器自带的密码查看按钮 +- 优化 退出登录重定向页面为 uniIdRouter.loginPage +- 新增 注册账号页面支持返回登录页面 +## 1.0.15(2022-09-19) +- 更新表结构,解决在uni-admin中部分clientDB操作没有权限的问题 +## 1.0.14(2022-09-16) +- 修改 配置项`isAdmin`默认值为`false` +## 1.0.13(2022-09-16) +- 新增 管理员注册页面 +- 新增 配置项`isAdmin`区分是否为管理端 +- 新增 登录成功后自动跳转;跳转优先级:路由携带(`uniIdRedirectUrl`参数) > 返回上一路由 > 跳转首页 +- uni-id-co 优化 注册管理员时管理员存在提示文案 +## 1.0.12(2022-09-07) +- 修复 getSupportedLoginType判断是否支持微信公众号、PC网页微信扫码登录方式报错的Bug +- 优化 适配pc端样式 +- 新增 邮箱验证码注册 +- 新增 邮箱验证码找回密码 +- 新增 退出登录(全局)回调事件:`uni-id-pages-logout`,支持通过[uni.$on](https://uniapp.dcloud.net.cn/api/window/communication.html#on)监听; +- 调整 抽离退出登录方法至`/uni_modules/uni-id-pages/common/common.js`中,方便在项目其他页面中调用 +- 调整 用户中心(路径:`/uni_modules/uni-id-pages/pages/userinfo/userinfo`)默认不再显示退出登录按钮。支持页面传参数`showLoginManage=true`恢复显示 +## 1.0.11(2022-09-01) +- 修复 iOS端,一键登录功能卡在showLoading的问题 +- 更新 合并密码强度与长度配置 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#config) +- uni-id-co 修复 调用 removeAuthorizedApp 接口报错的Bug +- uni-id-co 新增 管理端接口 updateUser [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#update-user) +- uni-id-co 调整 为兼容旧版本,未配置密码强度时提供最简单的密码规则校验(长度大于6即可) +- uni-id-co 调整 注册、登录时如果携带了token则尝试对此token进行登出操作 +- uni-id-co 调整 管理端接口 addUser 增加 mobile、email等参数 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#add-user) +## 1.0.10(2022-08-25) +- 修复 导入uni-id-pages插件时未自动导入uni-open-bridge-common的Bug +## 1.0.9(2022-08-23) +- 修复 uni-id-co 缺失uni-open-bridge-common依赖的Bug +## 1.0.8(2022-08-23) +- 新增 H5端支持微信登录(含微信公众号内的网页授权登录 和 普通浏览器内网页生成二维码,实现手机微信扫码登录)[详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#weixinlogin) +- 新增 登录成功(全局)回调事件:`uni-id-pages-login-success`,支持通过[uni.$on](https://uniapp.dcloud.net.cn/api/window/communication.html#on)监听; +- 新增 密码强度(是否必须包含大小写字母、数字和特殊符号以及长度)配置 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#config) +- 调整 uni-id-co 密码规则调整,废除之前的简单校验,允许配置密码强度 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-summary.html#password-strength) +- 调整 uni-id-co 存储用户 openid 时同时以客户端 AppId 为 Key 的副本,参考:[微信登录](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#login-by-weixin)、[QQ登录](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#login-by-qq) +- 调整 uni-id-co 依赖 uni-open-bridge-common 存储用户 session_key、access_token 等信息 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-summary.html#save-user-token) +- 新增 uni-id-co 增加 beforeRegister 钩子用户在注册前向用户记录内添加一些数据 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-summary.html#before-register) +## 1.0.7(2022-07-19) +- 修复 uni-id-co接口 logout时没有删除token的Bug +## 1.0.6(2022-07-13) +- 新增 允许覆盖内置校验规则 [详情](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#custom-validator) +- 修复 app端clientInfo.appVersionCode为数字导致校验无法通过的Bug +## 1.0.5(2022-07-11) +修复 微信小程序调用uni-id-co接口报错的Bug [详情](https://ask.dcloud.net.cn/question/148877) +## 1.0.4(2022-07-06) +- uni-id-co增加clientInfo字段类型校验 +- 监听token更新时机,同步客户端push_clientid至uni-id-device表,改为:同步客户端push_clientid至uni-id-device表和opendb-device表 +## 1.0.3(2022-07-05) +新增监听token更新时机,同步客户端push_clientid至uni-id-device表 +## 1.0.2(2022-07-04) +修复微信小程序登录时无unionid报错的Bug [详情](https://ask.dcloud.net.cn/question/148016) +## 1.0.1(2022-06-28) +添加相关uni-id表 +## 1.0.0(2022-06-23) +正式版 diff --git a/uni_modules/uni-id-pages/common/check-id-card.js b/uni_modules/uni-id-pages/common/check-id-card.js new file mode 100644 index 0000000..b3ca0c3 --- /dev/null +++ b/uni_modules/uni-id-pages/common/check-id-card.js @@ -0,0 +1,16 @@ +function checkIdCard (idCardNumber) { + if (!idCardNumber || typeof idCardNumber !== 'string' || idCardNumber.length !== 18) return false + + const coefficient = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2] + const checkCode = [1, 0, 'x', 9, 8, 7, 6, 5, 4, 3, 2] + const code = idCardNumber.substring(17) + + let sum = 0 + for (let i = 0; i < 17; i++) { + sum += Number(idCardNumber.charAt(i)) * coefficient[i] + } + + return checkCode[sum % 11].toString() === code.toLowerCase() +} + +export default checkIdCard diff --git a/uni_modules/uni-id-pages/common/login-page.mixin.js b/uni_modules/uni-id-pages/common/login-page.mixin.js new file mode 100644 index 0000000..2a26f7e --- /dev/null +++ b/uni_modules/uni-id-pages/common/login-page.mixin.js @@ -0,0 +1,95 @@ +import { + mutations +} from '@/uni_modules/uni-id-pages/common/store.js' +import config from '@/uni_modules/uni-id-pages/config.js' +const mixin = { + data() { + return { + config, + uniIdRedirectUrl: '', + isMounted: false + } + }, + onUnload() { + // #ifdef H5 + document.onkeydown = false + // #endif + }, + mounted() { + this.isMounted = true + }, + onLoad(e) { + if (e.is_weixin_redirect) { + uni.showLoading({ + mask: true + }) + + if (window.location.href.includes('#')) { + // 将url通过 ? 分割获取后面的参数字符串 再通过 & 将每一个参数单独分割出来 + const paramsArr = window.location.href.split('?')[1].split('&') + paramsArr.forEach(item => { + const arr = item.split('=') + if (arr[0] == 'code') { + e.code = arr[1] + } + }) + } + this.$nextTick(n => { + // console.log(this.$refs.uniFabLogin); + this.$refs.uniFabLogin.login({ + code: e.code + }, 'weixin') + }) + } + + if (e.uniIdRedirectUrl) { + this.uniIdRedirectUrl = decodeURIComponent(e.uniIdRedirectUrl) + } + + // #ifdef MP-WEIXIN + if (getCurrentPages().length === 1) { + uni.hideHomeButton() + console.log('已隐藏:返回首页按钮'); + } + // #endif + }, + computed: { + needAgreements() { + if (this.isMounted) { + if (this.$refs.agreements) { + return this.$refs.agreements.needAgreements + } else { + return false + } + } + }, + agree: { + get() { + if (this.isMounted) { + if (this.$refs.agreements) { + return this.$refs.agreements.isAgree + } else { + return true + } + } + }, + set(agree) { + if (this.$refs.agreements) { + this.$refs.agreements.isAgree = agree + } else { + console.log('不存在 隐私政策协议组件'); + } + } + } + }, + methods: { + loginSuccess(e) { + mutations.loginSuccess({ + ...e, + uniIdRedirectUrl: this.uniIdRedirectUrl + }) + } + } +} + +export default mixin diff --git a/uni_modules/uni-id-pages/common/login-page.scss b/uni_modules/uni-id-pages/common/login-page.scss new file mode 100644 index 0000000..5de6899 --- /dev/null +++ b/uni_modules/uni-id-pages/common/login-page.scss @@ -0,0 +1,126 @@ +// 隐藏 edge 浏览器的密码查看按钮 + +/* #ifdef H5 */ +.input-box ::v-deep{ + .uni-input-input[type="password"] { + &::-ms-reveal { + display: none; + } + } +} +/* #endif */ + +.uni-content { + padding: 0 60rpx; +} + +.login-logo { + display: none; +} + +/* #ifndef APP-NVUE */ +@media screen and (min-width: 690px) { + .uni-content { + /* #ifndef H5 */ + padding: 0; + max-width: 300px; + margin-left: calc(50% - 200px); + /* #endif */ + /* #ifdef H5 */ + margin: 0 auto; + position: relative; + top: 100px; + padding: 30px 40px 80px 40px; + max-width: 450px; + max-height: 450px; + border-radius: 10px; + box-shadow: 0 0 20px #efefef; + background-color: #FFF; + /* #endif */ + } + /* #ifdef H5 */ + .login-logo { + display: flex; + justify-content: center; + } + + .login-logo image { + width: 60px; + height: 60px; + } + + .register-back{ + display: none; + } + + uni-button{ + padding-bottom: 1px; + } + + /* #endif */ +} + +.uni-content view { + box-sizing: border-box; +} +/* #endif */ + + + +.title { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + padding: 18px 0; + font-weight: 800; + flex-direction: column; +} + +.tip { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + color: #BDBDC0; + font-size: 11px; + margin: 6px 0; +} + + +/* #ifndef APP-NVUE */ +// 解决小程序端开启虚拟节点virtualHost引起的 class = input-box丢失的问题 [详情参考](https://uniapp.dcloud.net.cn/matter.html#%E5%90%84%E5%AE%B6%E5%B0%8F%E7%A8%8B%E5%BA%8F%E5%AE%9E%E7%8E%B0%E6%9C%BA%E5%88%B6%E4%B8%8D%E5%90%8C-%E5%8F%AF%E8%83%BD%E5%AD%98%E5%9C%A8%E7%9A%84%E5%B9%B3%E5%8F%B0%E5%85%BC%E5%AE%B9%E9%97%AE%E9%A2%98) +.uni-content ::v-deep .uni-easyinput__content, +/* #endif */ + +.input-box { + height: 44px; + background-color: #F8F8F8 !important; + border-radius: 0; + font-size: 14px; + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex: 1; +} + +.link { + color: #04498c; + cursor: pointer; +} + +.uni-content ::v-deep .uni-forms-item__inner { + padding-bottom: 8px; +} + +.uni-btn { + text-align: center; + height: 40px; + line-height: 40px; + margin: 15px 0 10px 0; + color: #FFF !important; + border-radius: 5px; + font-size: 16px; +} + +.uni-body.uni_modules-uni-id-pages-pages-login-login-withoutpwd{ + height: auto !important; +} diff --git a/uni_modules/uni-id-pages/common/password.js b/uni_modules/uni-id-pages/common/password.js new file mode 100644 index 0000000..196c240 --- /dev/null +++ b/uni_modules/uni-id-pages/common/password.js @@ -0,0 +1,85 @@ +// 导入配置 +import config from '@/uni_modules/uni-id-pages/config.js' + +const {passwordStrength} = config + +// 密码强度表达式 +const passwordRules = { + // 密码必须包含大小写字母、数字和特殊符号 + super: /^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[~!@#$%^&*_\-+=`|\\(){}[\]:;"'<>,.?/])[0-9a-zA-Z~!@#$%^&*_\-+=`|\\(){}[\]:;"'<>,.?/]{8,16}$/, + // 密码必须包含字母、数字和特殊符号 + strong: /^(?=.*[0-9])(?=.*[a-zA-Z])(?=.*[~!@#$%^&*_\-+=`|\\(){}[\]:;"'<>,.?/])[0-9a-zA-Z~!@#$%^&*_\-+=`|\\(){}[\]:;"'<>,.?/]{8,16}$/, + // 密码必须为字母、数字和特殊符号任意两种的组合 + medium: /^(?![0-9]+$)(?![a-zA-Z]+$)(?![~!@#$%^&*_\-+=`|\\(){}[\]:;"'<>,.?/]+$)[0-9a-zA-Z~!@#$%^&*_\-+=`|\\(){}[\]:;"'<>,.?/]{8,16}$/, + // 密码必须包含字母和数字 + weak: /^(?=.*[0-9])(?=.*[a-zA-Z])[0-9a-zA-Z~!@#$%^&*_\-+=`|\\(){}[\]:;"'<>,.?/]{6,16}$/ +} + +const ERROR = { + normal: { + noPwd: '请输入密码', + noRePwd: '再次输入密码', + rePwdErr: '两次输入密码不一致' + }, + passwordStrengthError: { + super: '密码必须包含大小写字母、数字和特殊符号,密码长度必须在8-16位之间', + strong: '密码必须包含字母、数字和特殊符号,密码长度必须在8-16位之间', + medium: '密码必须为字母、数字和特殊符号任意两种的组合,密码长度必须在8-16位之间', + weak: '密码必须包含字母,密码长度必须在6-16位之间' + } +} + +function validPwd(password) { + //强度校验 + if (passwordStrength && passwordRules[passwordStrength]) { + if (!new RegExp(passwordRules[passwordStrength]).test(password)) { + return ERROR.passwordStrengthError[passwordStrength] + } + } + return true +} + +function getPwdRules(pwdName = 'password', rePwdName = 'password2') { + const rules = {} + rules[pwdName] = { + rules: [{ + required: true, + errorMessage: ERROR.normal.noPwd, + }, + { + validateFunction: function(rule, value, data, callback) { + const checkRes = validPwd(value) + if (checkRes !== true) { + callback(checkRes) + } + return true + } + } + ] + } + + if (rePwdName) { + rules[rePwdName] = { + rules: [{ + required: true, + errorMessage: ERROR.normal.noRePwd, + }, + { + validateFunction: function(rule, value, data, callback) { + if (value != data[pwdName]) { + callback(ERROR.normal.rePwdErr) + } + return true + } + } + ] + } + } + return rules +} + +export default { + ERROR, + validPwd, + getPwdRules +} diff --git a/uni_modules/uni-id-pages/common/store.js b/uni_modules/uni-id-pages/common/store.js new file mode 100644 index 0000000..83fdd1e --- /dev/null +++ b/uni_modules/uni-id-pages/common/store.js @@ -0,0 +1,174 @@ +import pagesJson from '@/pages.json' +import config from '@/uni_modules/uni-id-pages/config.js' + +const uniIdCo = uniCloud.importObject("uni-id-co") +const db = uniCloud.database(); +const usersTable = db.collection('uni-id-users') + +let hostUserInfo = uni.getStorageSync('uni-id-pages-userInfo')||{} +// console.log( hostUserInfo); +const data = { + userInfo: hostUserInfo, + hasLogin: Object.keys(hostUserInfo).length != 0 +} + +// console.log('data', data); +// 定义 mutations, 修改属性 +export const mutations = { + // data不为空,表示传递要更新的值(注意不是覆盖是合并),什么也不传时,直接查库获取更新 + async updateUserInfo(data = false) { + if (data) { + usersTable.where('_id==$env.uid').update(data).then(e => { + // console.log(e); + if (e.result.updated) { + uni.showToast({ + title: "更新成功", + icon: 'none', + duration: 3000 + }); + this.setUserInfo(data) + } else { + uni.showToast({ + title: "没有改变", + icon: 'none', + duration: 3000 + }); + } + }) + + } else { + const uniIdCo = uniCloud.importObject("uni-id-co", { + customUI: true + }) + try { + let res = await usersTable.where("'_id' == $cloudEnv_uid") + .field('mobile,nickname,username,email,avatar_file') + .get() + + const realNameRes = await uniIdCo.getRealNameInfo() + + // console.log('fromDbData',res.result.data); + this.setUserInfo({ + ...res.result.data[0], + realNameAuth: realNameRes + }) + } catch (e) { + this.setUserInfo({},{cover:true}) + console.error(e.message, e.errCode); + } + } + }, + async setUserInfo(data, {cover}={cover:false}) { + // console.log('set-userInfo', data); + let userInfo = cover?data:Object.assign(store.userInfo,data) + store.userInfo = Object.assign({},userInfo) + store.hasLogin = Object.keys(store.userInfo).length != 0 + // console.log('store.userInfo', store.userInfo); + uni.setStorageSync('uni-id-pages-userInfo', store.userInfo) + return data + }, + async logout() { + // 1. 已经过期就不需要调用服务端的注销接口 2.即使调用注销接口失败,不能阻塞客户端 + if(uniCloud.getCurrentUserInfo().tokenExpired > Date.now()){ + try{ + await uniIdCo.logout() + }catch(e){ + console.error(e); + } + } + uni.removeStorageSync('uni_id_token'); + uni.setStorageSync('uni_id_token_expired', 0) + uni.redirectTo({ + url: `/${pagesJson.uniIdRouter && pagesJson.uniIdRouter.loginPage ? pagesJson.uniIdRouter.loginPage: 'uni_modules/uni-id-pages/pages/login/login-withoutpwd'}`, + }); + uni.$emit('uni-id-pages-logout') + this.setUserInfo({},{cover:true}) + }, + + loginBack (e = {}) { + const {uniIdRedirectUrl = ''} = e + let delta = 0; //判断需要返回几层 + let pages = getCurrentPages(); + // console.log(pages); + pages.forEach((page, index) => { + if (pages[pages.length - index - 1].route.split('/')[3] == 'login') { + delta++ + } + }) + // console.log('判断需要返回几层:', delta); + if (uniIdRedirectUrl) { + return uni.redirectTo({ + url: uniIdRedirectUrl, + fail: (err1) => { + uni.switchTab({ + url:uniIdRedirectUrl, + fail: (err2) => { + console.log(err1,err2) + } + }) + } + }) + } + // #ifdef H5 + if (e.loginType == 'weixin') { + // console.log('window.history', window.history); + return window.history.go(-3) + } + // #endif + + if (delta) { + const page = pagesJson.pages[0] + return uni.reLaunch({ + url: `/${page.path}` + }) + } + + uni.navigateBack({ + delta + }) + }, + loginSuccess(e = {}){ + const { + showToast = true, toastText = '登录成功', autoBack = true, uniIdRedirectUrl = '', passwordConfirmed + } = e + // console.log({toastText,autoBack}); + if (showToast) { + uni.showToast({ + title: toastText, + icon: 'none', + duration: 3000 + }); + } + this.updateUserInfo() + + uni.$emit('uni-id-pages-login-success') + + if (config.setPasswordAfterLogin && !passwordConfirmed) { + return uni.redirectTo({ + url: uniIdRedirectUrl ? `/uni_modules/uni-id-pages/pages/userinfo/set-pwd/set-pwd?uniIdRedirectUrl=${uniIdRedirectUrl}&loginType=${e.loginType}`: `/uni_modules/uni-id-pages/pages/userinfo/set-pwd/set-pwd?loginType=${e.loginType}`, + fail: (err) => { + console.log(err) + } + }) + } + + if (autoBack) { + this.loginBack({uniIdRedirectUrl}) + } + } + +} + +// #ifdef VUE2 +import Vue from 'vue' +// 通过Vue.observable创建一个可响应的对象 +export const store = Vue.observable(data) +// #endif + +// #ifdef VUE3 +import { + reactive +} from 'vue' +// 通过Vue.observable创建一个可响应的对象 +export const store = reactive(data) +// #endif diff --git a/uni_modules/uni-id-pages/components/cloud-image/cloud-image.vue b/uni_modules/uni-id-pages/components/cloud-image/cloud-image.vue new file mode 100644 index 0000000..d714a3a --- /dev/null +++ b/uni_modules/uni-id-pages/components/cloud-image/cloud-image.vue @@ -0,0 +1,73 @@ + + + \ No newline at end of file diff --git a/uni_modules/uni-id-pages/components/uni-id-pages-agreements/uni-id-pages-agreements.vue b/uni_modules/uni-id-pages/components/uni-id-pages-agreements/uni-id-pages-agreements.vue new file mode 100644 index 0000000..31ab0b7 --- /dev/null +++ b/uni_modules/uni-id-pages/components/uni-id-pages-agreements/uni-id-pages-agreements.vue @@ -0,0 +1,167 @@ + + + + + diff --git a/uni_modules/uni-id-pages/components/uni-id-pages-avatar/uni-id-pages-avatar.vue b/uni_modules/uni-id-pages/components/uni-id-pages-avatar/uni-id-pages-avatar.vue new file mode 100644 index 0000000..0f259aa --- /dev/null +++ b/uni_modules/uni-id-pages/components/uni-id-pages-avatar/uni-id-pages-avatar.vue @@ -0,0 +1,198 @@ + + + + + diff --git a/uni_modules/uni-id-pages/components/uni-id-pages-bind-mobile/uni-id-pages-bind-mobile.vue b/uni_modules/uni-id-pages/components/uni-id-pages-bind-mobile/uni-id-pages-bind-mobile.vue new file mode 100644 index 0000000..56edaec --- /dev/null +++ b/uni_modules/uni-id-pages/components/uni-id-pages-bind-mobile/uni-id-pages-bind-mobile.vue @@ -0,0 +1,160 @@ + + + + + diff --git a/uni_modules/uni-id-pages/components/uni-id-pages-email-form/uni-id-pages-email-form.vue b/uni_modules/uni-id-pages/components/uni-id-pages-email-form/uni-id-pages-email-form.vue new file mode 100644 index 0000000..b10d7dc --- /dev/null +++ b/uni_modules/uni-id-pages/components/uni-id-pages-email-form/uni-id-pages-email-form.vue @@ -0,0 +1,248 @@ + + + + + diff --git a/uni_modules/uni-id-pages/components/uni-id-pages-fab-login/uni-id-pages-fab-login.vue b/uni_modules/uni-id-pages/components/uni-id-pages-fab-login/uni-id-pages-fab-login.vue new file mode 100644 index 0000000..9688abf --- /dev/null +++ b/uni_modules/uni-id-pages/components/uni-id-pages-fab-login/uni-id-pages-fab-login.vue @@ -0,0 +1,568 @@ + + + + diff --git a/uni_modules/uni-id-pages/components/uni-id-pages-sms-form/uni-id-pages-sms-form.vue b/uni_modules/uni-id-pages/components/uni-id-pages-sms-form/uni-id-pages-sms-form.vue new file mode 100644 index 0000000..1d38b87 --- /dev/null +++ b/uni_modules/uni-id-pages/components/uni-id-pages-sms-form/uni-id-pages-sms-form.vue @@ -0,0 +1,242 @@ + + + + + diff --git a/uni_modules/uni-id-pages/components/uni-id-pages-user-profile/uni-id-pages-user-profile.vue b/uni_modules/uni-id-pages/components/uni-id-pages-user-profile/uni-id-pages-user-profile.vue new file mode 100644 index 0000000..7b78f9b --- /dev/null +++ b/uni_modules/uni-id-pages/components/uni-id-pages-user-profile/uni-id-pages-user-profile.vue @@ -0,0 +1,171 @@ + + + + + diff --git a/uni_modules/uni-id-pages/config copy.js b/uni_modules/uni-id-pages/config copy.js new file mode 100644 index 0000000..9097f4b --- /dev/null +++ b/uni_modules/uni-id-pages/config copy.js @@ -0,0 +1,67 @@ +export default { + // 调试模式 + debug: false, + /* + 登录类型 未列举到的或运行环境不支持的,将被自动隐藏。 + 如果需要在不同平台有不同的配置,直接用条件编译即可 + */ + isAdmin: false, // 区分管理端与用户端 + loginTypes: [ + // "qq", + // "xiaomi", + // "sinaweibo", + // "taobao", + // "facebook", + // "google", + // "alipay", + // "douyin", + + // #ifdef APP + 'univerify', + // #endif + // 'weixin', + 'username', + // #ifdef APP + // 'apple', + // #endif + 'smsCode' + ], + // 政策协议 + agreements: { + serviceUrl: 'https://public.hiluker.com/private.html', // 用户服务协议链接 + privacyUrl: 'https://public.hiluker.com/private.html', // 隐私政策条款链接 + // 哪些场景下显示,1.注册(包括登录并注册,如:微信登录、苹果登录、短信验证码登录)、2.登录(如:用户名密码登录) + scope: [ + 'register', 'login', 'realNameVerify' + ] + }, + // 提供各类服务接入(如微信登录服务)的应用id + appid: { + weixin: { + // 微信公众号的appid,来源:登录微信公众号(https://mp.weixin.qq.com)-> 设置与开发 -> 基本配置 -> 公众号开发信息 -> AppID + // h5: 'xxxxxx', + // 微信开放平台的appid,来源:登录微信开放平台(https://open.weixin.qq.com) -> 管理中心 -> 网站应用 -> 选择对应的应用名称,点击查看 -> AppID + // web: 'xxxxxx' + } + }, + /** + * 密码强度 + * super(超强:密码必须包含大小写字母、数字和特殊符号,长度范围:8-16位之间) + * strong(强: 密密码必须包含字母、数字和特殊符号,长度范围:8-16位之间) + * medium (中:密码必须为字母、数字和特殊符号任意两种的组合,长度范围:8-16位之间) + * weak(弱:密码必须包含字母和数字,长度范围:6-16位之间) + * 为空或false则不验证密码强度 + */ + passwordStrength: 'weak', + /** + * 登录后允许用户设置密码(只针对未设置密码得用户) + * 开启此功能将 setPasswordAfterLogin 设置为 true 即可 + * "setPasswordAfterLogin": false + * + * 如果允许用户跳过设置密码 将 allowSkip 设置为 true + * "setPasswordAfterLogin": { + * "allowSkip": true + * } + * */ + setPasswordAfterLogin: false +} \ No newline at end of file diff --git a/uni_modules/uni-id-pages/config.js b/uni_modules/uni-id-pages/config.js new file mode 100644 index 0000000..48bc991 --- /dev/null +++ b/uni_modules/uni-id-pages/config.js @@ -0,0 +1,20 @@ +export default { + debug: false, + isAdmin: false, + loginTypes: [ + 'smsCode', + // #ifdef APP + 'univerify', + // #endif + 'username' + ], + agreements: { + serviceUrl: 'https://public.hiluker.com/ctms/client/service.html', + privacyUrl: 'https://public.hiluker.com/ctms/client/private.html', + scope: [ + 'register', 'login', 'realNameVerify' + ] + }, + passwordStrength: 'weak', + setPasswordAfterLogin: false +} \ No newline at end of file diff --git a/uni_modules/uni-id-pages/init.js b/uni_modules/uni-id-pages/init.js new file mode 100644 index 0000000..1d9b29d --- /dev/null +++ b/uni_modules/uni-id-pages/init.js @@ -0,0 +1,95 @@ +// 导入配置 +import config from '@/uni_modules/uni-id-pages/config.js' +// uni-id的云对象 +const uniIdCo = uniCloud.importObject('uni-id-co', { + customUI: true +}) +// 用户配置的登录方式、是否打开调试模式 +const { + loginTypes, + debug +} = config + +export default async function () { + // 有打开调试模式的情况下 + if (debug) { + // 1. 检查本地uni-id-pages中配置的登录方式,服务器端是否已经配置正确。否则提醒并引导去配置 + // 调用云对象,获取服务端已正确配置的登录方式 + const { + supportedLoginType + } = await uniIdCo.getSupportedLoginType() + console.log('supportedLoginType: ' + JSON.stringify(supportedLoginType)) + // 登录方式,服务端和客户端的映射关系 + const data = { + smsCode: 'mobile-code', + univerify: 'univerify', + username: 'username-password', + weixin: 'weixin', + qq: 'qq', + xiaomi: 'xiaomi', + sinaweibo: 'sinaweibo', + taobao: 'taobao', + facebook: 'facebook', + google: 'google', + alipay: 'alipay', + apple: 'apple', + weixinMobile: 'weixin' + } + // 遍历客户端配置的登录方式,与服务端比对。并在错误时抛出错误提示 + const list = loginTypes.filter(type => !supportedLoginType.includes(data[type])) + if (list.length) { + console.error( + `错误:前端启用的登录方式:${list.join(',')};没有在服务端完成配置。配置文件路径:"/uni_modules/uni-config-center/uniCloud/cloudfunctions/common/uni-config-center/uni-id/config.json"` + ) + } + } + + // #ifdef APP-PLUS + // 如果uni-id-pages配置的登录功能有一键登录,有则执行预登录(异步) + if (loginTypes.includes('univerify')) { + uni.preLogin({ + provider: 'univerify', + complete: e => { + // console.log(e); + } + }) + } + // #endif + + // 3. 绑定clientDB错误事件 + // clientDB对象 + const db = uniCloud.database() + db.on('error', onDBError) + // clientDB的错误提示 + function onDBError ({ + code, // 错误码详见https://uniapp.dcloud.net.cn/uniCloud/clientdb?id=returnvalue + message + }) { + // console.error('onDBError', {code,message}); + } + // 解绑clientDB错误事件 + // db.off('error', onDBError) + + // 4. 同步客户端push_clientid至device表 + if (uniCloud.onRefreshToken) { + uniCloud.onRefreshToken(() => { + // console.log('onRefreshToken'); + if (uni.getPushClientId) { + uni.getPushClientId({ + success: async function (e) { + // console.log(e) + const pushClientId = e.cid + // console.log(pushClientId); + const res = await uniIdCo.setPushCid({ + pushClientId + }) + // console.log('getPushClientId', res); + }, + fail (e) { + // console.log(e) + } + }) + } + }) + } +} diff --git a/uni_modules/uni-id-pages/package.json b/uni_modules/uni-id-pages/package.json new file mode 100644 index 0000000..bac7d60 --- /dev/null +++ b/uni_modules/uni-id-pages/package.json @@ -0,0 +1,103 @@ +{ + "id": "uni-id-pages", + "displayName": "uni-id-pages", + "version": "1.1.20", + "description": "云端一体简单、统一、可扩展的用户中心页面模版", + "keywords": [ + "用户管理", + "用户中心", + "短信验证码", + "login", + "登录" + ], + "repository": "https://gitcode.net/dcloud/hello_uni-id-pages", + "engines": { + "HBuilderX": "^3.4.17" + }, + "dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "", + "type": "unicloud-template-page" + }, + "uni_modules": { + "dependencies": [ + "uni-captcha", + "uni-config-center", + "uni-data-checkbox", + "uni-easyinput", + "uni-forms", + "uni-icons", + "uni-id-common", + "uni-list", + "uni-load-more", + "uni-popup", + "uni-scss", + "uni-transition", + "uni-open-bridge-common", + "uni-cloud-s2s" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "y" + }, + "client": { + "Vue": { + "vue2": "y", + "vue3": "y" + }, + "App": { + "app-vue": "y", + "app-nvue": "u" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "u", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "u", + "百度": "u", + "字节跳动": "u", + "QQ": "u", + "钉钉": "u", + "快手": "u", + "飞书": "u", + "京东": "u" + }, + "快应用": { + "华为": "u", + "联盟": "u" + } + } + } + }, + "dependencies": { + } +} diff --git a/uni_modules/uni-id-pages/pages/common/webview/webview.vue b/uni_modules/uni-id-pages/pages/common/webview/webview.vue new file mode 100644 index 0000000..71ff55c --- /dev/null +++ b/uni_modules/uni-id-pages/pages/common/webview/webview.vue @@ -0,0 +1,35 @@ + + + + diff --git a/uni_modules/uni-id-pages/pages/login/login-smscode.vue b/uni_modules/uni-id-pages/pages/login/login-smscode.vue new file mode 100644 index 0000000..f847cc5 --- /dev/null +++ b/uni_modules/uni-id-pages/pages/login/login-smscode.vue @@ -0,0 +1,120 @@ + + + + diff --git a/uni_modules/uni-id-pages/pages/login/login-withoutpwd.vue b/uni_modules/uni-id-pages/pages/login/login-withoutpwd.vue new file mode 100644 index 0000000..2f576c8 --- /dev/null +++ b/uni_modules/uni-id-pages/pages/login/login-withoutpwd.vue @@ -0,0 +1,257 @@ + + + + + + \ No newline at end of file diff --git a/uni_modules/uni-id-pages/pages/login/login-withpwd.vue b/uni_modules/uni-id-pages/pages/login/login-withpwd.vue new file mode 100644 index 0000000..de7e977 --- /dev/null +++ b/uni_modules/uni-id-pages/pages/login/login-withpwd.vue @@ -0,0 +1,176 @@ + + + + + + diff --git a/uni_modules/uni-id-pages/pages/register/register-admin.vue b/uni_modules/uni-id-pages/pages/register/register-admin.vue new file mode 100644 index 0000000..be2c1d3 --- /dev/null +++ b/uni_modules/uni-id-pages/pages/register/register-admin.vue @@ -0,0 +1,178 @@ + + + + + + diff --git a/uni_modules/uni-id-pages/pages/register/register-by-email.vue b/uni_modules/uni-id-pages/pages/register/register-by-email.vue new file mode 100644 index 0000000..6f05c8a --- /dev/null +++ b/uni_modules/uni-id-pages/pages/register/register-by-email.vue @@ -0,0 +1,216 @@ + + + + + + diff --git a/uni_modules/uni-id-pages/pages/register/register.vue b/uni_modules/uni-id-pages/pages/register/register.vue new file mode 100644 index 0000000..20de1ad --- /dev/null +++ b/uni_modules/uni-id-pages/pages/register/register.vue @@ -0,0 +1,181 @@ + + + + + + diff --git a/uni_modules/uni-id-pages/pages/register/validator.js b/uni_modules/uni-id-pages/pages/register/validator.js new file mode 100644 index 0000000..9173e3c --- /dev/null +++ b/uni_modules/uni-id-pages/pages/register/validator.js @@ -0,0 +1,56 @@ +import passwordMod from '@/uni_modules/uni-id-pages/common/password.js' +export default { + "username": { + "rules": [{ + required: true, + errorMessage: '请输入用户名', + }, + { + minLength: 3, + maxLength: 32, + errorMessage: '用户名长度在 {minLength} 到 {maxLength} 个字符', + }, + { + validateFunction: function(rule, value, data, callback) { + // console.log(value); + if (/^1\d{10}$/.test(value) || /^(\w-*\.*)+@(\w-?)+(\.\w{2,})+$/.test(value)) { + callback('用户名不能是:手机号或邮箱') + }; + if (/^\d+$/.test(value)) { + callback('用户名不能为纯数字') + }; + if(/[\u4E00-\u9FA5\uF900-\uFA2D]{1,}/.test(value)){ + callback('用户名不能包含中文') + } + return true + } + } + ], + "label": "用户名" + }, + "nickname": { + "rules": [{ + minLength: 3, + maxLength: 32, + errorMessage: '昵称长度在 {minLength} 到 {maxLength} 个字符', + }, + { + validateFunction: function(rule, value, data, callback) { + // console.log(value); + if (/^1\d{10}$/.test(value) || /^(\w-*\.*)+@(\w-?)+(\.\w{2,})+$/.test(value)) { + callback('昵称不能是:手机号或邮箱') + }; + if (/^\d+$/.test(value)) { + callback('昵称不能为纯数字') + }; + if(/[\u4E00-\u9FA5\uF900-\uFA2D]{1,}/.test(value)){ + callback('昵称不能包含中文') + } + return true + } + } + ], + "label": "昵称" + }, + ...passwordMod.getPwdRules() +} diff --git a/uni_modules/uni-id-pages/pages/retrieve/retrieve-by-email.vue b/uni_modules/uni-id-pages/pages/retrieve/retrieve-by-email.vue new file mode 100644 index 0000000..b6e7704 --- /dev/null +++ b/uni_modules/uni-id-pages/pages/retrieve/retrieve-by-email.vue @@ -0,0 +1,218 @@ + + + + + + diff --git a/uni_modules/uni-id-pages/pages/retrieve/retrieve.vue b/uni_modules/uni-id-pages/pages/retrieve/retrieve.vue new file mode 100644 index 0000000..6e1976b --- /dev/null +++ b/uni_modules/uni-id-pages/pages/retrieve/retrieve.vue @@ -0,0 +1,241 @@ + + + + + + diff --git a/uni_modules/uni-id-pages/pages/userinfo/bind-mobile/bind-mobile.vue b/uni_modules/uni-id-pages/pages/userinfo/bind-mobile/bind-mobile.vue new file mode 100644 index 0000000..a6b2b1f --- /dev/null +++ b/uni_modules/uni-id-pages/pages/userinfo/bind-mobile/bind-mobile.vue @@ -0,0 +1,131 @@ + + + + + diff --git a/uni_modules/uni-id-pages/pages/userinfo/change_pwd/change_pwd.vue b/uni_modules/uni-id-pages/pages/userinfo/change_pwd/change_pwd.vue new file mode 100644 index 0000000..2992623 --- /dev/null +++ b/uni_modules/uni-id-pages/pages/userinfo/change_pwd/change_pwd.vue @@ -0,0 +1,130 @@ + + + + + + diff --git a/uni_modules/uni-id-pages/pages/userinfo/cropImage/cropImage.vue b/uni_modules/uni-id-pages/pages/userinfo/cropImage/cropImage.vue new file mode 100644 index 0000000..2317b9b --- /dev/null +++ b/uni_modules/uni-id-pages/pages/userinfo/cropImage/cropImage.vue @@ -0,0 +1,39 @@ + + + + + \ No newline at end of file diff --git a/uni_modules/uni-id-pages/pages/userinfo/cropImage/limeClipper/README.md b/uni_modules/uni-id-pages/pages/userinfo/cropImage/limeClipper/README.md new file mode 100644 index 0000000..9219f81 --- /dev/null +++ b/uni_modules/uni-id-pages/pages/userinfo/cropImage/limeClipper/README.md @@ -0,0 +1,227 @@ +> 插件来源:[https://ext.dcloud.net.cn/plugin?id=3594](https://ext.dcloud.net.cn/plugin?id=3594) +##### 以下是作者写的插件介绍: + +# Clipper 图片裁剪 +> uniapp 图片裁剪,可用于图片头像等裁剪处理 +> [查看更多](http://liangei.gitee.io/limeui/#/clipper)
+> Q群:458377637 + + +## 平台兼容 + +| H5 | 微信小程序 | 支付宝小程序 | 百度小程序 | 头条小程序 | QQ 小程序 | App | +| --- | ---------- | ------------ | ---------- | ---------- | --------- | --- | +| √ | √ | √ | 未测 | √ | √ | √ | + + +## 代码演示 +### 基本用法 +`@success` 事件点击 👉 **确定** 后会返回生成的图片信息,包含 `url`、`width`、`height` + +```html + + + +``` + +```js +// 非uni_modules引入 +import lClipper from '@/components/lime-clipper/' +// uni_modules引入 +import lClipper from '@/uni_modules/lime-clipper/components/lime-clipper/' +export default { + components: {lClipper}, + data() { + return { + show: false, + url: '', + } + } +} +``` + + +### 传入图片 +`image-url`可传入**相对路径**、**临时路径**、**本地路径**、**网络图片**
+ +* **当为网络地址时** +* H5:👉 需要解决跨域问题。
+* 小程序:👉 需要配置 downloadFile 域名
+ + +```html + + + +``` + +```js +export default { + components: {lClipper}, + data() { + return { + imageUrl: 'https://img12.360buyimg.com/pop/s1180x940_jfs/t1/97205/26/1142/87801/5dbac55aEf795d962/48a4d7a63ff80b8b.jpg', + show: false, + url: '', + } + } +} +``` + + +### 确定按钮颜色 +样式变量名:`--l-clipper-confirm-color` +可放到全局样式的 `page` 里或节点的 `style` +```html + +``` +```css +// css 中为组件设置 CSS 变量 +.clipper { + --l-clipper-confirm-color: linear-gradient(to right, #ff6034, #ee0a24) +} +// 全局 +page { + --l-clipper-confirm-color: linear-gradient(to right, #ff6034, #ee0a24) +} +``` + + +### 使用插槽 +共五个插槽 `cancel` 取消按钮、 `photo` 选择图片按钮、 `rotate` 旋转按钮、 `confirm` 确定按钮和默认插槽。 + +```html + + + + 取消 + 选择图片 + 旋转 + 确定 + + + 显示取消按钮 + + + 显示选择图片按钮 + + + 显示旋转按钮 + + + 显示确定按钮 + + + 锁定裁剪框宽度 + + + 锁定裁剪框高度 + + + 锁定裁剪框比例 + + + 限制移动范围 + + + 禁止缩放 + + + 禁止旋转 + + + + + +``` + +```js +export default { + components: {lClipper}, + data() { + return { + show: false, + url: '', + isLockWidth: false, + isLockHeight: false, + isLockRatio: true, + isLimitMove: false, + isDisableScale: false, + isDisableRotate: false, + isShowCancelBtn: true, + isShowPhotoBtn: true, + isShowRotateBtn: true, + isShowConfirmBtn: true + } + } +} +``` + + +## API + +### Props + +| 参数 | 说明 | 类型 | 默认值 | +| ------------- | ------------ | ---------------- | ------------ | +| image-url | 图片路径 | string | | +| quality | 图片的质量,取值范围为 [0, 1],不在范围内时当作1处理 | number | `1` | +| source | `{album: '从相册中选择'}`key为图片来源类型,value为选项说明 | Object | | +| width | 裁剪框宽度,单位为 `rpx` | number | `400` | +| height | 裁剪框高度 | number | `400` | +| min-width | 裁剪框最小宽度 | number | `200` | +| min-height |裁剪框最小高度 | number | `200` | +| max-width | 裁剪框最大宽度 | number | `600` | +| max-height | 裁剪框最大宽度 | number | `600` | +| min-ratio | 图片最小缩放比 | number | `0.5` | +| max-ratio | 图片最大缩放比 | number | `2` | +| rotate-angle | 旋转按钮每次旋转的角度 | number | `90` | +| scale-ratio | 生成图片相对于裁剪框的比例, **比例越高生成图片越清晰** | number | `1` | +| is-lock-width | 是否锁定裁剪框宽度 | boolean | `false` | +| is-lock-height | 是否锁定裁剪框高度上 | boolean | `false` | +| is-lock-ratio | 是否锁定裁剪框比例 | boolean | `true` | +| is-disable-scale | 是否禁止缩放 | boolean | `false` | +| is-disable-rotate | 是否禁止旋转 | boolean | `false` | +| is-limit-move | 是否限制移动范围 | boolean | `false` | +| is-show-photo-btn | 是否显示选择图片按钮 | boolean | `true` | +| is-show-rotate-btn | 是否显示转按钮 | boolean | `true` | +| is-show-confirm-btn | 是否显示确定按钮 | boolean | `true` | +| is-show-cancel-btn | 是否显示关闭按钮 | boolean | `true` | + + + +### 事件 Events + +| 事件名 | 说明 | 回调 | +| ------- | ------------ | -------------- | +| success | 生成图片成功 | {`width`, `height`, `url`} | +| fail | 生成图片失败 | `error` | +| cancel | 关闭 | `false` | +| ready | 图片加载完成 | {`width`, `height`, `path`, `orientation`, `type`} | +| change | 图片大小改变时触发 | {`width`, `height`} | +| rotate | 图片旋转时触发 | `angle` | + +## 常见问题 +> 1、H5端使用网络图片需要解决跨域问题。
+> 2、小程序使用网络图片需要去公众平台增加下载白名单!二级域名也需要配!
+> 3、H5端生成图片是base64,有时显示只有一半可以使用原生标签``
+> 4、IOS APP 请勿使用HBX2.9.3.20201014的版本!这个版本无法生成图片。
+> 5、APP端无成功反馈、也无失败反馈时,请更新基座和HBX。
+ + +## 打赏 +如果你觉得本插件,解决了你的问题,赠人玫瑰,手留余香。
+![输入图片说明](https://images.gitee.com/uploads/images/2020/1122/222521_bb543f96_518581.jpeg "微信图片编辑_20201122220352.jpg") \ No newline at end of file diff --git a/uni_modules/uni-id-pages/pages/userinfo/cropImage/limeClipper/images/photo.svg b/uni_modules/uni-id-pages/pages/userinfo/cropImage/limeClipper/images/photo.svg new file mode 100644 index 0000000..7b4b590 --- /dev/null +++ b/uni_modules/uni-id-pages/pages/userinfo/cropImage/limeClipper/images/photo.svg @@ -0,0 +1,19 @@ + + + + + + + + + diff --git a/uni_modules/uni-id-pages/pages/userinfo/cropImage/limeClipper/images/rotate.svg b/uni_modules/uni-id-pages/pages/userinfo/cropImage/limeClipper/images/rotate.svg new file mode 100644 index 0000000..0143706 --- /dev/null +++ b/uni_modules/uni-id-pages/pages/userinfo/cropImage/limeClipper/images/rotate.svg @@ -0,0 +1,15 @@ + + + + + + + + + + diff --git a/uni_modules/uni-id-pages/pages/userinfo/cropImage/limeClipper/index.css b/uni_modules/uni-id-pages/pages/userinfo/cropImage/limeClipper/index.css new file mode 100644 index 0000000..ce542bf --- /dev/null +++ b/uni_modules/uni-id-pages/pages/userinfo/cropImage/limeClipper/index.css @@ -0,0 +1,160 @@ +.flex-auto { + flex: auto; +} +.bg-transparent { + background-color: rgba(0,0,0,0.9); + transition-duration: 0.35s; +} +.l-clipper { + width: 100vw; + height: calc(100vh - var(--window-top)); + background-color: rgba(0,0,0,0.9); + position: fixed; + top: var(--window-top); + left: 0; + z-index: 1; +} +.l-clipper-mask { + position: relative; + z-index: 2; + pointer-events: none; +} +.l-clipper__content { + pointer-events: none; + position: absolute; + border: 1rpx solid rgba(255,255,255,0.3); + box-sizing: border-box; + box-shadow: rgba(0,0,0,0.5) 0 0 0 80vh; + background: transparent; +} +.l-clipper__content::before, +.l-clipper__content::after { + content: ''; + position: absolute; + border: 1rpx dashed rgba(255,255,255,0.3); +} +.l-clipper__content::before { + width: 100%; + top: 33.33%; + height: 33.33%; + border-left: none; + border-right: none; +} +.l-clipper__content::after { + width: 33.33%; + left: 33.33%; + height: 100%; + border-top: none; + border-bottom: none; +} +.l-clipper__edge { + position: absolute; + width: 34rpx; + height: 34rpx; + border: 6rpx solid #fff; + pointer-events: auto; +} +.l-clipper__edge::before { + content: ''; + position: absolute; + width: 40rpx; + height: 40rpx; + background-color: transparent; +} +.l-clipper__edge:nth-child(1) { + left: -6rpx; + top: -6rpx; + border-bottom-width: 0 !important; + border-right-width: 0 !important; +} +.l-clipper__edge:nth-child(1):before { + top: -50%; + left: -50%; +} +.l-clipper__edge:nth-child(2) { + right: -6rpx; + top: -6rpx; + border-bottom-width: 0 !important; + border-left-width: 0 !important; +} +.l-clipper__edge:nth-child(2):before { + top: -50%; + left: 50%; +} +.l-clipper__edge:nth-child(3) { + left: -6rpx; + bottom: -6rpx; + border-top-width: 0 !important; + border-right-width: 0 !important; +} +.l-clipper__edge:nth-child(3):before { + bottom: -50%; + left: -50%; +} +.l-clipper__edge:nth-child(4) { + right: -6rpx; + bottom: -6rpx; + border-top-width: 0 !important; + border-left-width: 0 !important; +} +.l-clipper__edge:nth-child(4):before { + bottom: -50%; + left: 50%; +} +.l-clipper-image { + width: 100%; + border-style: none; + position: absolute; + top: 0; + left: 0; + z-index: 1; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + transform-origin: center; +} +.l-clipper-canvas { + position: fixed; + z-index: 10; + left: -200vw; + top: -200vw; + pointer-events: none; +} +.l-clipper-tools { + position: fixed; + left: 0; + bottom: 10px; + width: 100%; + z-index: 99; + color: #fff; +} +.l-clipper-tools__btns { + font-weight: bold; + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + padding: 20rpx 40rpx; + box-sizing: border-box; +} +.l-clipper-tools__btns .cancel { + width: 112rpx; + height: 60rpx; + text-align: center; + line-height: 60rpx; +} +.l-clipper-tools__btns .confirm { + width: 112rpx; + height: 60rpx; + line-height: 60rpx; + background-color: #07c160; + border-radius: 6rpx; + text-align: center; +} +.l-clipper-tools__btns image { + display: block; + width: 60rpx; + height: 60rpx; +} +.l-clipper-tools__btns { + flex-direction: row; +} diff --git a/uni_modules/uni-id-pages/pages/userinfo/cropImage/limeClipper/limeClipper.vue b/uni_modules/uni-id-pages/pages/userinfo/cropImage/limeClipper/limeClipper.vue new file mode 100644 index 0000000..4fc62b3 --- /dev/null +++ b/uni_modules/uni-id-pages/pages/userinfo/cropImage/limeClipper/limeClipper.vue @@ -0,0 +1,820 @@ + + + + + \ No newline at end of file diff --git a/uni_modules/uni-id-pages/pages/userinfo/cropImage/limeClipper/utils.js b/uni_modules/uni-id-pages/pages/userinfo/cropImage/limeClipper/utils.js new file mode 100644 index 0000000..980c439 --- /dev/null +++ b/uni_modules/uni-id-pages/pages/userinfo/cropImage/limeClipper/utils.js @@ -0,0 +1,244 @@ +/** + * 判断手指触摸位置 + */ +export function determineDirection(clipX, clipY, clipWidth, clipHeight, currentX, currentY) { + /* + * (右下>>1 右上>>2 左上>>3 左下>>4) + */ + let corner; + /** + * 思路:(利用直角坐标系) + * 1.找出裁剪框中心点 + * 2.如点击坐标在上方点与左方点区域内,则点击为左上角 + * 3.如点击坐标在下方点与右方点区域内,则点击为右下角 + * 4.其他角同理 + */ + const mainPoint = [clipX + clipWidth / 2, clipY + clipHeight / 2]; // 中心点 + const currentPoint = [currentX, currentY]; // 触摸点 + + if (currentPoint[0] <= mainPoint[0] && currentPoint[1] <= mainPoint[1]) { + corner = 3; // 左上 + } else if (currentPoint[0] >= mainPoint[0] && currentPoint[1] <= mainPoint[1]) { + corner = 2; // 右上 + } else if (currentPoint[0] <= mainPoint[0] && currentPoint[1] >= mainPoint[1]) { + corner = 4; // 左下 + } else if (currentPoint[0] >= mainPoint[0] && currentPoint[1] >= mainPoint[1]) { + corner = 1; // 右下 + } + + return corner; +} + +/** + * 图片边缘检测检测时,计算图片偏移量 + */ +export function calcImageOffset(data, scale) { + let left = data.imageLeft; + let top = data.imageTop; + scale = scale || data.scale; + + let imageWidth = data.imageWidth; + let imageHeight = data.imageHeight; + if ((data.angle / 90) % 2) { + imageWidth = data.imageHeight; + imageHeight = data.imageWidth; + } + const { + clipX, + clipWidth, + clipY, + clipHeight + } = data; + + // 当前图片宽度/高度 + const currentImageSize = (size) => (size * scale) / 2; + const currentImageWidth = currentImageSize(imageWidth); + const currentImageHeight = currentImageSize(imageHeight); + + left = clipX + currentImageWidth >= left ? left : clipX + currentImageWidth; + left = clipX + clipWidth - currentImageWidth <= left ? left : clipX + clipWidth - currentImageWidth; + top = clipY + currentImageHeight >= top ? top : clipY + currentImageHeight; + top = clipY + clipHeight - currentImageHeight <= top ? top : clipY + clipHeight - currentImageHeight; + return { + left, + top, + scale + }; +} + +/** + * 图片边缘检测时,计算图片缩放比例 + */ +export function calcImageScale(data, scale) { + scale = scale || data.scale; + let { + imageWidth, + imageHeight, + clipWidth, + clipHeight, + angle + } = data + if ((angle / 90) % 2) { + imageWidth = imageHeight; + imageHeight = imageWidth; + } + if (imageWidth * scale < clipWidth) { + scale = clipWidth / imageWidth; + } + if (imageHeight * scale < clipHeight) { + scale = Math.max(scale, clipHeight / imageHeight); + } + return scale; +} + +/** + * 计算图片尺寸 + */ +export function calcImageSize(width, height, data) { + let imageWidth = width, + imageHeight = height; + let { + clipWidth, + clipHeight, + sysinfo, + width: originWidth, + height: originHeight + } = data + if (imageWidth && imageHeight) { + if (imageWidth / imageHeight > (clipWidth || originWidth) / (clipWidth || originHeight)) { + imageHeight = clipHeight || originHeight; + imageWidth = (width / height) * imageHeight; + } else { + imageWidth = clipWidth || originWidth; + imageHeight = (height / width) * imageWidth; + } + } else { + let sys = sysinfo || uni.getSystemInfoSync(); + imageWidth = sys.windowWidth; + imageHeight = 0; + } + return { + imageWidth, + imageHeight + }; +} + +/** + * 勾股定理求斜边 + */ +export function calcPythagoreanTheorem(width, height) { + return Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2)); +} + +/** + * 拖动裁剪框时计算 + */ +export function clipTouchMoveOfCalculate(data, event) { + const clientX = event.touches[0].clientX; + const clientY = event.touches[0].clientY; + + let { + clipWidth, + clipHeight, + clipY: oldClipY, + clipX: oldClipX, + clipStart, + isLockRatio, + maxWidth, + minWidth, + maxHeight, + minHeight + } = data; + maxWidth = maxWidth / 2; + minWidth = minWidth / 2; + minHeight = minHeight / 2; + maxHeight = maxHeight / 2; + + let width = clipWidth, + height = clipHeight, + clipY = oldClipY, + clipX = oldClipX, + // 获取裁剪框实际宽度/高度 + // 如果大于最大值则使用最大值 + // 如果小于最小值则使用最小值 + sizecorrect = () => { + width = width <= maxWidth ? (width >= minWidth ? width : minWidth) : maxWidth; + height = height <= maxHeight ? (height >= minHeight ? height : minHeight) : maxHeight; + }, + sizeinspect = () => { + sizecorrect(); + if ((width > maxWidth || width < minWidth || height > maxHeight || height < minHeight) && isLockRatio) { + return false; + } else { + return true; + } + }; + //if (clipStart.corner) { + height = clipStart.height + (clipStart.corner > 1 && clipStart.corner < 4 ? 1 : -1) * (clipStart.y - clientY); + //} + switch (clipStart.corner) { + case 1: + width = clipStart.width - clipStart.x + clientX; + if (isLockRatio) { + height = width / (clipWidth / clipHeight); + } + if (!sizeinspect()) return; + break; + case 2: + width = clipStart.width - clipStart.x + clientX; + if (isLockRatio) { + height = width / (clipWidth / clipHeight); + } + if (!sizeinspect()) { + return; + } else { + clipY = clipStart.clipY - (height - clipStart.height); + } + + break; + case 3: + width = clipStart.width + clipStart.x - clientX; + if (isLockRatio) { + height = width / (clipWidth / clipHeight); + } + if (!sizeinspect()) { + return; + } else { + clipY = clipStart.clipY - (height - clipStart.height); + clipX = clipStart.clipX - (width - clipStart.width); + } + + break; + case 4: + width = clipStart.width + clipStart.x - clientX; + if (isLockRatio) { + height = width / (clipWidth / clipHeight); + } + if (!sizeinspect()) { + return; + } else { + clipX = clipStart.clipX - (width - clipStart.width); + } + break; + default: + break; + } + return { + width, + height, + clipX, + clipY + }; +} + +/** + * 单指拖动图片计算偏移 + */ +export function imageTouchMoveOfCalcOffset(data, clientXForLeft, clientYForLeft) { + let left = clientXForLeft - data.touchRelative[0].x, + top = clientYForLeft - data.touchRelative[0].y; + return { + left, + top + }; +} diff --git a/uni_modules/uni-id-pages/pages/userinfo/deactivate/deactivate.vue b/uni_modules/uni-id-pages/pages/userinfo/deactivate/deactivate.vue new file mode 100644 index 0000000..1b666de --- /dev/null +++ b/uni_modules/uni-id-pages/pages/userinfo/deactivate/deactivate.vue @@ -0,0 +1,117 @@ + + + + + + diff --git a/uni_modules/uni-id-pages/pages/userinfo/realname-verify/face-verify-icon.svg b/uni_modules/uni-id-pages/pages/userinfo/realname-verify/face-verify-icon.svg new file mode 100644 index 0000000..df30eb4 --- /dev/null +++ b/uni_modules/uni-id-pages/pages/userinfo/realname-verify/face-verify-icon.svg @@ -0,0 +1 @@ + diff --git a/uni_modules/uni-id-pages/pages/userinfo/realname-verify/realname-verify.vue b/uni_modules/uni-id-pages/pages/userinfo/realname-verify/realname-verify.vue new file mode 100644 index 0000000..001fa5a --- /dev/null +++ b/uni_modules/uni-id-pages/pages/userinfo/realname-verify/realname-verify.vue @@ -0,0 +1,315 @@ + + + + + diff --git a/uni_modules/uni-id-pages/pages/userinfo/set-pwd/set-pwd.vue b/uni_modules/uni-id-pages/pages/userinfo/set-pwd/set-pwd.vue new file mode 100644 index 0000000..94ed02a --- /dev/null +++ b/uni_modules/uni-id-pages/pages/userinfo/set-pwd/set-pwd.vue @@ -0,0 +1,171 @@ + + + + + + diff --git a/uni_modules/uni-id-pages/pages/userinfo/userinfo.vue b/uni_modules/uni-id-pages/pages/userinfo/userinfo.vue new file mode 100644 index 0000000..729484a --- /dev/null +++ b/uni_modules/uni-id-pages/pages/userinfo/userinfo.vue @@ -0,0 +1,272 @@ + + + + diff --git a/uni_modules/uni-id-pages/readme.md b/uni_modules/uni-id-pages/readme.md new file mode 100644 index 0000000..1650e45 --- /dev/null +++ b/uni_modules/uni-id-pages/readme.md @@ -0,0 +1,15 @@ +# 文档已移至uni-id-pages文档[https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html](https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html) + + + +关于插件更新的说明: + +所有uni_modules,在HBuilderX里点右键都可以直接升级。或者在插件市场导入覆盖。 + +覆盖时HBuilderX会弹出代码差异比对,可以决定接受哪些更改、拒绝哪些更改。 + +当拒绝局部修改时,注意可能产生兼容性问题。 + +你需要二次开发uni-id-pages的前端页面, +- 如果改动不大,那么每次更新uni-id-pages时,在HBuilderX的对比界面对比一下就好 +- 如果改动较大,建议复制一套前端页面到自己工程的pages目录下,pages.json里只引用根目录pages下的页面,不引用uni_modules下的页面。然后每次uni-id-pages更新,你对比下比上一版uni-id-pages改了什么,看你是否需要再合并到你自己的pages里。pages.json里不引用uni_modules里的页面的话,打包时不会把这些页面打包进去,不影响发行后的包体积 \ No newline at end of file diff --git a/uni_modules/uni-id-pages/static/app-plus/apple.png b/uni_modules/uni-id-pages/static/app-plus/apple.png new file mode 100644 index 0000000000000000000000000000000000000000..556f68666ecdb2a0414fb7f2a5a0d7fa484d6df8 GIT binary patch literal 10282 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 Z5FW-2!fSWBq{tqLd+-?8> literal 0 HcmV?d00001 diff --git a/uni_modules/uni-id-pages/static/app-plus/uni-fab-login/alipay.png b/uni_modules/uni-id-pages/static/app-plus/uni-fab-login/alipay.png new file mode 100644 index 0000000000000000000000000000000000000000..5256d7a60afcb882681f6a681ae0e10f977d1a4a GIT binary patch literal 3978 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 EU-sXgSpWb4 literal 0 HcmV?d00001 diff --git a/uni_modules/uni-id-pages/static/app-plus/uni-fab-login/douyin.png b/uni_modules/uni-id-pages/static/app-plus/uni-fab-login/douyin.png new file mode 100644 index 0000000000000000000000000000000000000000..f218f68d952a8e4753792b0529132b4522f5e8a1 GIT binary patch literal 3163 zcmb_e3s4mI89$#vL?wudNX*VjP|>~H-P^sr-GB~oAUJ@Crx4W9eI0jlce~!LaQJA0 zlu|-{h1gaKwG>eqtByeflv0hRwSd}CS_vXD1d*l;8pa1>4buMschSU0C)1nR+uiT~ zJ$~Qs`@a8w3z8C7O!E%(RwxwH;$rp5;BIqWUK7CoK;HERg~DsL*^nxyCdAX6XjL)1 z$O4cL)wlNl#$b1ZOs&yDWi;7CJi$=2rEI5)^L_%@VQCgJJYGF);j=)Vwp!FiR%~irUy}O%vku&r3x9%}Y zLm&)Gz>FX;Fpl61tV4}Bti_EiW+W*s$>`kP;%p9?v2lXS8=zWc2A(Jm({PA@!5STb z!90nRFpE(bENDs0Xe4zgfot9NtL$blkc{Q^s3;WU5sV{XQosyxjqoIkGKj0r8Xb&t3 zZDd`9;}io^It>Yf^jMY?5RNzUFSjNLLZdO_Mi`@jA&v?ttm6SO6bOs!z$>ezp0OUu zVUz;(tGODPP&(G^;x2nE=QYAsPxjErP|^(N+HAs=oNLMiUO6TaboXw6QZxbf4y}7P_#x28Xyr_dEnNU7 zmsWyS4rQk{M~u(GF98xA{iao;ln=AxA{u#B@D+j%x7YIqU@+ z;K>e-a>YBl?}E>ODN^iv4uxXsOxHC|QCt$BP>h3;;tkQm!^1mw?rdvo>*?v~?(VLr zsHm^62SQ*papJ`BDGG=A{&5-&f7Tiv3fpkpDc*;=Q-NF#L5hyP+z7 zbFh&On$%I>u=T-vPvzgP8n+_jZADVhhm-e)&N!WW!5}ri`OUynA~JsJiU;_noBtTj zKXFB>3~4lG4c)qUA#Jg$`JXKr7n>A=Lw!vh?`nRX0`I+5e5n)tI{n7r-3K%79qE_e zeUtC*{(E7_uDCg^KZb7n>%gUr#@O=dyJ9Z%o=T}YyIRL*toHe!Z+X_)AgO6e+M}A> z;618ooplD`lbhzFX(x-FSkLOXS%azCYPuagTJ_<;-J^4>g-u5`tX)*t0UPF~oW1t( zXK!iXfrgTI5*wusTjEMN1UvR+$J*`FQ*Q5j6miwZl(d%FP}S!*r%jx_uW@Pb-o4dl zwW~@SCSH%KYwx|eL)A;KidN_EpWGE#vZLAfC;O7$+_1GRHYvKUSnq@vm1Kl%-4t?R z{-shW^FQ*BW>2qZ{5-*T0TW=0ZuD+`P+yy!)3Pxx_ff#L-><*8Ja?D>g00ZC;AL+G zeO=1u?C@WB9@ouowoUy@EcAhZt7^vSxju?(w!o%r$73A?(~QS$nlCmnyFK`Sfbq7(I}A~)~;FArsm AbN~PV literal 0 HcmV?d00001 diff --git a/uni_modules/uni-id-pages/static/app-plus/uni-fab-login/facebook.png b/uni_modules/uni-id-pages/static/app-plus/uni-fab-login/facebook.png new file mode 100644 index 0000000000000000000000000000000000000000..1331b61ffef8e169b1a11974a5749a89427bdcce GIT binary patch literal 3065 zcmb_e4Nz3q6<%Xf&5!6*o0=bWT`5t++kNltd%N##zyP}-C`%BiA~ZJd=Pvt{-M86Y ze#bPK*hZsi{h@{;VrmH(Ley%bpu{-Vj>KeQ5T_aiM;)|k0%aIPWC%IE57>=K{pqyb znY;Vmz2|)A`_4V*-hDGYZT^Huo_s{7(@jW8c4XjRuQtYy#os-@IelEG8~?bHl^x7> zrrIRcV-#dnM8;x|53lQVG0zwK1Zf2d>P3{Ncw-ISO@|G7MUFKrVV$JYXGd-&xzvv` zOVhHX(iIXf8=jx1k14ic01pZZ`eIMMH()D{H4Nk0@Uu2;GU$g*f-7PT@mfHAwliIC zSN%xO8ZCrGF`VAY8z~Dznk~_KnxuFW$(blKK~gr-Vq>lP(T@Q~^UE$4*f6FAVDqjHU@E%v3D+Oq7w-Bn=zNQrON{;LjfpE=wkqk37g53}9O-Z0mEY zK{eo3?*biO{$2o_TBkD{tpiylK4i*30kyy{p~ON=bC#yY zXS4f}5LEqHs+u3#sPqtJJ&jfCXD?B_vRV|FbDINl2tgEUph=n{D4HPoEQ+;JRvXDY zOL8`n95HpOvf?TUn`&E2P*fI8VhbB-{+=l=8CeJl_cfL!n@jb31T0wb2zkil^X3`! z;WXOpYQE~nfiXMt?ei&id%9nBDfxIIkTE|&pOR>|P`t%L&_-&QuG49As8WHpg(;3u zHOs1`$hPm`8It5UGbIrcDI-Fl8Ij;wi;J*G0-_{=wu%fLLQC-mf`V5<8X8WuQNc)* zBqKy3D`BR1nvi*hB1D-c35t<8QX&P)%!ncX7yJrtNFo3Jtayr+Nm^nFE5cZ;RYSE} zF-cAm1l}dMc!ANH;5!{5%y`T%fcHNz+GGhr-sn@lGEXBdW74E-6)Az#_Swu4j3n@c z$jKH0Su8v&Q!K;ELX1i4DC{)SthafaNr4f~G$I(8wGxb#Jd0fph@u_ z-m+QXSsdFXB7tBSULbhRY$dQfQIrrV$u9Zs-YmTcj}?jfZ}Z|K07R-)8N0MKAKj^ zEqL<~zPU_>|3S=u)S)9R!}|~t^TVU<|0ia+G)F4T^uyvCUc2pb!y7p~g>OvlF??0y zm+)neyf{yOe3jo=$n3>`10MDzFAeB)k51Oc7~PhwQ+2vA5$UN}i2!iD4=PVWObs~d zz*Y@&EAbp8)xm;#NZ0`vdcd+BqN{+}4)bcDcL3VD!LbAS2Vr0klIx)GFwCxmjNQ;T z2swLT@gCr+A!|2~?|`=n=yxEd8m{(1`4?cU0=WS$^}yf|9J&O(gK)kZ8qPynJ*+$m z%l5$#KvV_9?SQx%$ZvwQoe)_8sr9h#I7}}G{~RG)&OA*k+Y$;as$6U<1<99s3&;%1%h0Wrl9pXGk2YfJ76ZRC{Kno=J1d=+)% z#We@tUv54(A%5I@YjUm2kHpQ&Z0PxOF2IbX%40`2uD>F^?M`T(xOrvgR@1jD-<0<) zuHXN`&vTY)`oCB@e?9p0j3}$J?8M-?kK$S$ zD(dqX|G99**iSx~@Z81yX|1=mMSXg#YTASuEeo1GKl`TpOUL!bj-k@ut$W2Xax*HZr2&(&gvA>26H3pLp>){nJHL_L-V9{&BInvJX|HUDy&;-}1)B zvrqIaYiQfBdO_7-8TnY*`NWwub!(68YrY!&(l4gZE!bAOu)Vx>UR>9b_>ad;|F*Qb z_T-ed^y*U=8~f}2(3yFKz&daCk%wXef|?DDS3hKRApvR9C7jcNaO zUvf1|X{*}S?W!)!+*Z-vw(>yx%`=VbC;h0Qw5!Sf)-#8l zM|I~~8kb3tN2)yPZsX&XU%ywG$QR$Z*7|149@~Y`GD>gi_`qt#~rCrZo16{d(`{vbO Tf3E$d*QF$;Id&zy^!mR5pSY!V literal 0 HcmV?d00001 diff --git a/uni_modules/uni-id-pages/static/app-plus/uni-fab-login/google.png b/uni_modules/uni-id-pages/static/app-plus/uni-fab-login/google.png new file mode 100644 index 0000000000000000000000000000000000000000..9155046e524adfada43323cdf81d8817b881edce GIT binary patch literal 4333 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 U;eq}@^S=~+Ucubc9x<8!1nf~DE&u=k literal 0 HcmV?d00001 diff --git a/uni_modules/uni-id-pages/static/app-plus/uni-fab-login/qq.png b/uni_modules/uni-id-pages/static/app-plus/uni-fab-login/qq.png new file mode 100644 index 0000000000000000000000000000000000000000..f170691dc4d52d65bc604720d56a5a498170f1a4 GIT binary patch literal 3449 zcmb_f3se(V8jf0Bq>7dzQruz;^@U6_Gnq+df>;p{sA6f%B5m2)c?AOGVG^DmA5^JQ zMP-$yky0O&x`?ivgBF##F2*!PL`o5U) z6K1G&;5m=C{P_$W#Uyh^KFuYlG{J(Ns>=etiVYTQz@y=4T_~5Rip$BD=n5<-m zWChQ!;4jaR0RS~;r1%-?6pcZa5iA(Nm4R#LvPi%mF)=0w3qqU$`Ek)P{7|i)5K0ri+DIeS!!fNq~WhP!s|dkRelJq%t6l!G9bfj59ELmCmTrYWPk>iq@tYg9U); zs06jn1*EQEN!l z{tC3~@y`N))J8|UV*DZ(wb~WJU<^+KGR6|}i`E83rj8TIIfFJ;&v4;sfTq7wjZPM- z=P09Auh42!#tRiQPMMDa)%+zJR2o*BZdf|OfeWLIT(AIzQ3OIz2qqN>E<>a;7+VGt zG8i5;jn=X%Wv0v2Sy~7|6bLE9Ntqb=nJH))mNHVmY0NS*rB<(|fMAuHO5j8~O@e^$ zN~0`Po1)c&z<`~2;(AnQXpCN~RHc9i26@CPepGm<1R*672o)kDbfcqXVOl2DS;DBW z@or|djEa^01dp&%mV}iAM9?q|ku=6Z6iSm22GazGqa4FZv2nC0jloE17|w|XQY};g zBpQQRnq_c^V+bXLV;BjMh(rvbm;|F(6vH{h<^Q5y1qPBz`E6Dtg0e6QP^BCSL%0+} zAX+MAAckNlid0fclER!r@RN>l%n-oO0P1fT8?seY!q`=cYK4zqiKeN8CJ>ux;KvFVzClaLMRCwF@!@Pf(3~o zL0F6cx3q+u;60*YGzFr^=W1j^Wsxdp?lQ+Tuje+o)bmG%QbsY(%_dmDIH!zb1!K#q zU+4^6%(7B3%|QgG1Y^Qt63||#5|WUhu{cDD(Nb>QKRuBHppTip(dY)P(wI)^xsU|V z)4w_0qF*C#pwb>s9RWSYk)R=@1cpH@@o4Q^*(>ydhEI3&6+`*c`p<&sPdH~VZ(_c{A z-rhS9Th=)K1Zn`_f9wPjb^GcL82HFafOYID!AtUA`t)QS%FupU%qW}cJ->_hLXlkk6eqq ztf{H1Pf^xX*4%ZZzGIige!0z&XS-M3=HTbsEcYC@yZim}Yi8$KChw@;VX^Bjw3=$$ z8#?=zAE|c?3{Bfn-Pt!dEvMQe*BUW<_hwM6Nk;j)^+P-E$J%(jDQr|&h$3-n_u-*6 zEh~L&Go#PXmj5OB*i1Ea?VG7xYf@=*@fSw|?}t6J&cVT6`PmlI;x@14&bGfRnrqJ? zrk;@4N0xTe3R8!D;H$y9Ki~LxUtZoLI3jCr-XUc`{F_^si3`j}rzD-+zIp+7ZSB>C z<*UupeRB>i6+dPAhcCA%(M$fp^|1KK$%o8H@uF=VqTVyiiR`DpRlc2M^$sSM1kO!f z$b;hqd4=B;Zz3z>Z}Hs-vqcXAhjXV2ZCg8#Aqcsn2J()+aqtp_(BSWd{Y$^1VI3 zVAh4LOBd{WRT?m*@%eUqpC{4@JIpK<@l>=k+F%}sL) zix#-gOOx2G9``(;TPwWd$_wF#70(uzKmAC>nus?xS+Bktv3rA8=<&e4^P1mzC+=uo z#7t|$@LNH4$$M1hkEi0+9|~`3@4K41GBRQAmbJy_tzVZvxAji^-Y4ArXK&c(oe{OW z?c|e71E=5h-g(zeB`n`}g?F`x_$b}+xp|H}&ePI*`vO6KIb+EzOWlLTDJy1_o_lBV zOFVn5-1?L}=b_)uW=r1z z{{9=6@VYY{^CxXWhl}n+eNHV)aQj#3GVaK_Nvlv+Hbz3sliO{5wBHB)5>o6CYJ1!HO99l&bhKd zYwK>YnYNTzKd#rk+fw!Yq(+^1Gqm_bZ|0BIyJvJnaIWS3n>A;nPv)F>K|bX|ulwQ- zQ~4|ArDr3ArPmMju8BK+-Rlc~lR|lIVBx7)rF(b&ngDV851z5h#5s>XSy*7!Z=GHi iq5e8?TgdPqlKkKA`|8urK5_nIiV9yB_P159zwvLfty_Tr literal 0 HcmV?d00001 diff --git a/uni_modules/uni-id-pages/static/app-plus/uni-fab-login/sinaweibo.png b/uni_modules/uni-id-pages/static/app-plus/uni-fab-login/sinaweibo.png new file mode 100644 index 0000000000000000000000000000000000000000..12cd03812cf81febc94a44c5591ae5b27229dfe9 GIT binary patch literal 4081 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 Tshq94|0>)zZ*#fi6maw(JX>;f literal 0 HcmV?d00001 diff --git a/uni_modules/uni-id-pages/static/app-plus/uni-fab-login/univerify.png b/uni_modules/uni-id-pages/static/app-plus/uni-fab-login/univerify.png new file mode 100644 index 0000000000000000000000000000000000000000..aa0b9f5b56d36120c07016585195093aedf3ba59 GIT binary patch literal 3365 zcmb_f2~-nj9*k`kiK;fc2CxoqYgEcM5ocbaYE0Ona+)zynIzabC9c*% z41(#E1gS=J7_8O3szhOg)WCWTqJ?0@0P74mq3pO+z?+@r4U>|`b@&2ri7J!RX)|av z1qB7_0FsZEe&y5wy0?D=iZSw_RI>@vj_!&>D?hJ|2q2V(D_>aHhBbjgEjkRN9d}-^OVsaSmago#9ek0Orsz8=E1? z&QVUmo-GLZor%iqgsemXYvmuNnysu*;CQ8*0+&oVxkMETqX>kc5KLwxxB(#yFg6m_ z8(_Fy)F`lKetwr|IJFRhWFw>j2SVhpL_x{0l#_a*Fv}Qt!ET`dVY7wGHu1eVz zMnjU2FW7-&fKJp8Jr~d19GiXRE|H-IG1Z82A!6AY}VF)KM z1fmIoffzkQQ6x|CB!z{W;1?d9Hpc*b4v>FGN0W`EaywS}=22nLEC8*ADU8;l9E7nr zpi1C+h$L7&L^B}&lpdi_m`>1yI|?YZH*0roR!fmMj6yukQ4ofa6h!K^1Ox!mG{eCR z%d?Nx#&KM$>Q7)?!m+B3ZSzBNi)&ey(9#^F=Xg-)ET)4<8s#A!spoJG;W0X# z+jg@BCJq>VO#iftIRxHWK-sx5xu7ba*q@q5fp<`@$3sUzm2f1hg9tsSNEF6kh{g#V zVi=NS^f*T|{C`95-p`=>b)EhEd!ToOg)vdqTn@}0jq3N4`QLi%H+kqzru)a~|3{{+ zG}|jo^V{NUJL@){KqA|=z|0FTyM`@z=^EFZ6~xI7hH>(a>7Ri?9%)OR?ocTDMu+bn zijTI&C=@;NGSjnDLZOf(hu*3MkC3xkJh3-VRo^2G{$feNBNSb<&DDiS_u)ngVvO z=x&v>-Tno&(&5{|P$+1v7Ed$>ci#$>H3e(^a)Tt#1b}66S)G*Q_AjoLifScM4mAhl zYQJ3C7$~Zd=6L+?`=pANV6jiS+!DMc$#s6YQIc!?@-(;KFNb`R>=Whvw*&i|0>_$z zrMCc{K(Q}yFA(y!%FTh$TAx%@Bc5*wUTl#!Hw3Ev!OYA4;s)t(b8usWwB8rUsq$B~ z$`$v5+ZqC=?gf|COT1U~w92%{UsNl;RSNB>R7`lb1Q z!gE|RBITXuYMqTkMj|5#&t9M&w2jlwewX;iBJbNc~ z$Ea3@oN{4!^`)FxxJ#u4mu-|hJW|j?o@!HVy152c_f&1@OUY_N< zapdP6DUn9aklM(tS;kOYi?Moq#lRo<*f>kwA&-swc-xrKrL&q&)SfxBdhE64575M` ztDO&qTbk`Jo*EqAIHaO};sh0Wuk78L(p5*#*zJMyQ|IiC!79X)z1^v&E;3)W#A=Fj z&zp*0;SS>u8bxnto%;Rw)9EGV1tTWJxhs#KIJO}6j&**`>x#kYvK9QPn+f#`Sbb=~jRn!M=H)P~RcG!6X1=-{%5 zqMy&yCM?IJRcE(d^;Han4#zDWIrJ>9_3o>2&O8_TG2+0h9~gt1U-@D4f#VS7A+9~w5O z5^Li5^Oam<3}IMZSwIh;z^NO0zD=AnmgT!*;*mAcyT)z!Zt|+=U4u7duIZ^xKbXBi z`{DMMo{>wP<8Q3pJ?~|2SdmW-ZiPhW~SzX}K=?PJd;_mPj~q&<^*z zhF;6Pe9!bnOHJBOGkV-yDGxYg{&1xnDGR~!E62Z!kXDEP-K3>tB<~$NXVHHEmZfmT literal 0 HcmV?d00001 diff --git a/uni_modules/uni-id-pages/static/limeClipper/photo.svg b/uni_modules/uni-id-pages/static/limeClipper/photo.svg new file mode 100644 index 0000000..7b4b590 --- /dev/null +++ b/uni_modules/uni-id-pages/static/limeClipper/photo.svg @@ -0,0 +1,19 @@ + + + + + + + + + diff --git a/uni_modules/uni-id-pages/static/limeClipper/rotate.svg b/uni_modules/uni-id-pages/static/limeClipper/rotate.svg new file mode 100644 index 0000000..0143706 --- /dev/null +++ b/uni_modules/uni-id-pages/static/limeClipper/rotate.svg @@ -0,0 +1,15 @@ + + + + + + + + + + diff --git a/uni_modules/uni-id-pages/static/login/uni-fab-login/sms.png b/uni_modules/uni-id-pages/static/login/uni-fab-login/sms.png new file mode 100644 index 0000000000000000000000000000000000000000..5533743a21e39f8a89ae2faee1d89ab9581bf29a GIT binary patch literal 4285 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 F^iP+OC9nVh literal 0 HcmV?d00001 diff --git a/uni_modules/uni-id-pages/static/login/uni-fab-login/user.png b/uni_modules/uni-id-pages/static/login/uni-fab-login/user.png new file mode 100644 index 0000000000000000000000000000000000000000..268420b7343d9b2c7c58233e024927363928dc5f GIT binary patch literal 2997 zcmbVO2~ZSg79Q7Q5j7HTFL4?sjZKC!yFv1+pG=~_>DjuLo zfDl(ij7fx8u6Tt-Nzi4Hu!xAt5{5EB6j>Gtmy{wZ(YHMuqe*$BwyXMj|M!3Id++_P z|4t1FUOvg$)mb1AObQI}3+3-d`{^{AzxM91Rr0s-=70#RKrmsd{TwC8&Y2|;jH}jb z!?|!xkeW6bL?mOPSW$w(%%cSY&m{?Fl8$CMgkmH0MlWGcafuMoGhV`X38Og9tQL#omj$(bx#lPPATP$46d5eXkPvhW?fkuk+v7dgfODMr4IEqV9 zjL_n8HLg@+vOi%eHHHmAH6}){+vI@SON-*T7AMql9j%9L^yN+=l>O3{4%Nfk~?Fv?SG?-NPb+&NKlJuT#}324IQA&k$WvSR9cU7wB?W>}9w9+mf8dWYcc@wn z<$r|IH=rdGs(kFms-=ThPbd>Y`2whvLA4*0AyDcHAYcl+A;V0;r>)ArW8zYJ%W#AKa~hrmb)_0P5C4ZxuXj zgXT0C=z{j+aNPnWQ=qpJ>NmjEx1pyLn%;*>3A7!ChB#U zU1y=I82TEZ{Uo#>h1UI0uYY^g_u*n>Z?k z{|Rv7LW9G4?a2aYoumIIM<5t24fONYMvd*tj$hfb)TO*-N=vqC@3c^4N8-J;X=_jP z|JczFV`hH0ziIm^X>!K=+`c6Pmp%VmaV~#_J2m>WQ`*+7>FuxY`Luq#c3 zy*B@=T-nL{os(}kA8s#d4sttKu)5(&?)$N0%5Hix?-r&jF5kT7N{fqhr*d!2n<@EF z=X$?pa#kskQuH9Vy5`C*&qtN9qvuaLd*FEgwVa$F?Bs2y)Z^)Y2MX>Ge4<`<-ox2=Km@qdoqV;Ioq17IzT@JN* zd~r`>KKL(dajWy+K1gfdS@fCccCE+OIr*PfO}}uy!;<(Dx7WsPiOD--%J4SnH!ff6&$@*jp4v-KynP|LBB|0x|-1ceXu&G~~YZsmGKV~%$NYig$?XqEcA?1`DPvM!3srsetOS7NCzHfQWw^oTZ=%-C~( c-EA0EajwApWc_(h`;T>?f3V*PpY_}R2U3RH`~Uy| literal 0 HcmV?d00001 diff --git a/uni_modules/uni-id-pages/static/login/uni-fab-login/weixin.png b/uni_modules/uni-id-pages/static/login/uni-fab-login/weixin.png new file mode 100644 index 0000000000000000000000000000000000000000..af7175b6fed8670ce6e6d1a7a64a4812a4db8cfe GIT binary patch literal 3934 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 CN=BRj literal 0 HcmV?d00001 diff --git a/uni_modules/uni-id-pages/static/login/weixin.png b/uni_modules/uni-id-pages/static/login/weixin.png new file mode 100644 index 0000000000000000000000000000000000000000..df70aac5092588d7ccb87d75f024d0c9dc804607 GIT binary patch literal 11483 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 literal 0 HcmV?d00001 diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/common/constants.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/common/constants.js new file mode 100644 index 0000000..afce8b8 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/common/constants.js @@ -0,0 +1,108 @@ +const db = uniCloud.database() +const dbCmd = db.command +const userCollectionName = 'uni-id-users' +const userCollection = db.collection(userCollectionName) +const verifyCollectionName = 'opendb-verify-codes' +const verifyCollection = db.collection(verifyCollectionName) +const deviceCollectionName = 'uni-id-device' +const deviceCollection = db.collection(deviceCollectionName) +const openDataCollectionName = 'opendb-open-data' +const openDataCollection = db.collection(openDataCollectionName) +const frvLogsCollectionName = 'opendb-frv-logs' +const frvLogsCollection = db.collection(frvLogsCollectionName) + +const USER_IDENTIFIER = { + _id: 'uid', + username: 'username', + mobile: 'mobile', + email: 'email', + wx_unionid: 'wechat-account', + 'wx_openid.app': 'wechat-account', + 'wx_openid.mp': 'wechat-account', + 'wx_openid.h5': 'wechat-account', + 'wx_openid.web': 'wechat-account', + qq_unionid: 'qq-account', + 'qq_openid.app': 'qq-account', + 'qq_openid.mp': 'qq-account', + ali_openid: 'alipay-account', + apple_openid: 'alipay-account', + identities: 'idp' +} + +const USER_STATUS = { + NORMAL: 0, + BANNED: 1, + AUDITING: 2, + AUDIT_FAILED: 3, + CLOSED: 4 +} + +const CAPTCHA_SCENE = { + REGISTER: 'register', + LOGIN_BY_PWD: 'login-by-pwd', + LOGIN_BY_SMS: 'login-by-sms', + RESET_PWD_BY_SMS: 'reset-pwd-by-sms', + RESET_PWD_BY_EMAIL: 'reset-pwd-by-email', + SEND_SMS_CODE: 'send-sms-code', + SEND_EMAIL_CODE: 'send-email-code', + BIND_MOBILE_BY_SMS: 'bind-mobile-by-sms', + SET_PWD_BY_SMS: 'set-pwd-by-sms' +} + +const LOG_TYPE = { + LOGOUT: 'logout', + LOGIN: 'login', + REGISTER: 'register', + RESET_PWD_BY_SMS: 'reset-pwd', + RESET_PWD_BY_EMAIL: 'reset-pwd', + BIND_MOBILE: 'bind-mobile', + BIND_WEIXIN: 'bind-weixin', + BIND_QQ: 'bind-qq', + BIND_APPLE: 'bind-apple', + BIND_ALIPAY: 'bind-alipay', + UNBIND_WEIXIN: 'unbind-weixin', + UNBIND_QQ: 'unbind-qq', + UNBIND_ALIPAY: 'unbind-alipay', + UNBIND_APPLE: 'unbind-apple' +} + +const SMS_SCENE = { + LOGIN_BY_SMS: 'login-by-sms', + RESET_PWD_BY_SMS: 'reset-pwd-by-sms', + BIND_MOBILE_BY_SMS: 'bind-mobile-by-sms', + SET_PWD_BY_SMS: 'set-pwd-by-sms' +} + +const EMAIL_SCENE = { + REGISTER: 'register', + LOGIN_BY_EMAIL: 'login-by-email', + RESET_PWD_BY_EMAIL: 'reset-pwd-by-email', + BIND_EMAIL: 'bind-email' +} + +const REAL_NAME_STATUS = { + NOT_CERTIFIED: 0, + WAITING_CERTIFIED: 1, + CERTIFIED: 2, + CERTIFY_FAILED: 3 +} + +const EXTERNAL_DIRECT_CONNECT_PROVIDER = 'externalDirectConnect' + +module.exports = { + db, + dbCmd, + userCollection, + verifyCollection, + deviceCollection, + openDataCollection, + frvLogsCollection, + USER_IDENTIFIER, + USER_STATUS, + CAPTCHA_SCENE, + LOG_TYPE, + SMS_SCENE, + EMAIL_SCENE, + REAL_NAME_STATUS, + EXTERNAL_DIRECT_CONNECT_PROVIDER +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/common/error.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/common/error.js new file mode 100644 index 0000000..1e33845 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/common/error.js @@ -0,0 +1,70 @@ +const ERROR = { + ACCOUNT_EXISTS: 'uni-id-account-exists', + ACCOUNT_NOT_EXISTS: 'uni-id-account-not-exists', + ACCOUNT_NOT_EXISTS_IN_CURRENT_APP: 'uni-id-account-not-exists-in-current-app', + ACCOUNT_CONFLICT: 'uni-id-account-conflict', + ACCOUNT_BANNED: 'uni-id-account-banned', + ACCOUNT_AUDITING: 'uni-id-account-auditing', + ACCOUNT_AUDIT_FAILED: 'uni-id-account-audit-failed', + ACCOUNT_CLOSED: 'uni-id-account-closed', + CAPTCHA_REQUIRED: 'uni-id-captcha-required', + PASSWORD_ERROR: 'uni-id-password-error', + PASSWORD_ERROR_EXCEED_LIMIT: 'uni-id-password-error-exceed-limit', + INVALID_USERNAME: 'uni-id-invalid-username', + INVALID_PASSWORD: 'uni-id-invalid-password', + INVALID_PASSWORD_SUPER: 'uni-id-invalid-password-super', + INVALID_PASSWORD_STRONG: 'uni-id-invalid-password-strong', + INVALID_PASSWORD_MEDIUM: 'uni-id-invalid-password-medium', + INVALID_PASSWORD_WEAK: 'uni-id-invalid-password-weak', + INVALID_MOBILE: 'uni-id-invalid-mobile', + INVALID_EMAIL: 'uni-id-invalid-email', + INVALID_NICKNAME: 'uni-id-invalid-nickname', + INVALID_PARAM: 'uni-id-invalid-param', + PARAM_REQUIRED: 'uni-id-param-required', + GET_THIRD_PARTY_ACCOUNT_FAILED: 'uni-id-get-third-party-account-failed', + GET_THIRD_PARTY_USER_INFO_FAILED: 'uni-id-get-third-party-user-info-failed', + MOBILE_VERIFY_CODE_ERROR: 'uni-id-mobile-verify-code-error', + EMAIL_VERIFY_CODE_ERROR: 'uni-id-email-verify-code-error', + ADMIN_EXISTS: 'uni-id-admin-exists', + PERMISSION_ERROR: 'uni-id-permission-error', + SYSTEM_ERROR: 'uni-id-system-error', + SET_INVITE_CODE_FAILED: 'uni-id-set-invite-code-failed', + INVALID_INVITE_CODE: 'uni-id-invalid-invite-code', + CHANGE_INVITER_FORBIDDEN: 'uni-id-change-inviter-forbidden', + BIND_CONFLICT: 'uni-id-bind-conflict', + UNBIND_FAIL: 'uni-id-unbind-failed', + UNBIND_NOT_SUPPORTED: 'uni-id-unbind-not-supported', + UNBIND_UNIQUE_LOGIN: 'uni-id-unbind-unique-login', + UNBIND_PASSWORD_NOT_EXISTS: 'uni-id-unbind-password-not-exists', + UNBIND_MOBILE_NOT_EXISTS: 'uni-id-unbind-mobile-not-exists', + UNSUPPORTED_REQUEST: 'uni-id-unsupported-request', + ILLEGAL_REQUEST: 'uni-id-illegal-request', + CONFIG_FIELD_REQUIRED: 'uni-id-config-field-required', + CONFIG_FIELD_INVALID: 'uni-id-config-field-invalid', + FRV_FAIL: 'uni-id-frv-fail', + FRV_PROCESSING: 'uni-id-frv-processing', + REAL_NAME_VERIFIED: 'uni-id-realname-verified', + ID_CARD_EXISTS: 'uni-id-idcard-exists', + INVALID_ID_CARD: 'uni-id-invalid-idcard', + INVALID_REAL_NAME: 'uni-id-invalid-realname', + UNKNOWN_ERROR: 'uni-id-unknown-error', + REAL_NAME_VERIFY_UPPER_LIMIT: 'uni-id-realname-verify-upper-limit' +} + +function isUniIdError (errCode) { + return Object.values(ERROR).includes(errCode) +} + +class UniCloudError extends Error { + constructor (options) { + super(options.message) + this.errMsg = options.message || '' + this.errCode = options.code + } +} + +module.exports = { + ERROR, + isUniIdError, + UniCloudError +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/common/sensitive-aes-cipher.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/common/sensitive-aes-cipher.js new file mode 100644 index 0000000..b2d6d95 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/common/sensitive-aes-cipher.js @@ -0,0 +1,64 @@ +const crypto = require('crypto') +const { ERROR } = require('./error') + +function checkSecret (secret) { + if (!secret) { + throw { + errCode: ERROR.CONFIG_FIELD_REQUIRED, + errMsgValue: { + field: 'sensitiveInfoEncryptSecret' + } + } + } + + if (secret.length !== 32) { + throw { + errCode: ERROR.CONFIG_FIELD_INVALID, + errMsgValue: { + field: 'sensitiveInfoEncryptSecret' + } + } + } +} +function encryptData (text = '') { + if (!text) return text + + const encryptSecret = this.config.sensitiveInfoEncryptSecret + + checkSecret(encryptSecret) + + const iv = encryptSecret.slice(-16) + + const cipher = crypto.createCipheriv('aes-256-cbc', encryptSecret, iv) + + const encrypted = Buffer.concat([ + cipher.update(Buffer.from(text, 'utf-8')), + cipher.final() + ]) + + return encrypted.toString('base64') +} + +function decryptData (text = '') { + if (!text) return text + + const encryptSecret = this.config.sensitiveInfoEncryptSecret + + checkSecret(encryptSecret) + + const iv = encryptSecret.slice(-16) + + const cipher = crypto.createDecipheriv('aes-256-cbc', encryptSecret, iv) + + const decrypted = Buffer.concat([ + cipher.update(Buffer.from(text, 'base64')), + cipher.final() + ]) + + return decrypted.toString('utf-8') +} + +module.exports = { + encryptData, + decryptData +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/common/universal.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/common/universal.js new file mode 100644 index 0000000..4bf46a0 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/common/universal.js @@ -0,0 +1,47 @@ +const { ERROR } = require('./error') + +function getHttpClientInfo () { + const requestId = this.getUniCloudRequestId() + const { clientIP, userAgent, source, secretType = 'none' } = this.getClientInfo() + const { clientInfo = {} } = JSON.parse(this.getHttpInfo().body) + + return { + ...clientInfo, + clientIP, + userAgent, + source, + secretType, + requestId + } +} + +function getHttpUniIdToken () { + const { uniIdToken = '' } = JSON.parse(this.getHttpInfo().body) + + return uniIdToken +} + +function verifyHttpMethod () { + const { headers, httpMethod } = this.getHttpInfo() + + if (!/^application\/json/.test(headers['content-type']) || httpMethod.toUpperCase() !== 'POST') { + throw { + errCode: ERROR.UNSUPPORTED_REQUEST, + errMsg: 'unsupported request' + } + } +} + +function universal () { + if (this.getClientInfo().source === 'http') { + verifyHttpMethod.call(this) + this.getParams()[0] = JSON.parse(this.getHttpInfo().body).params + this.getUniversalClientInfo = getHttpClientInfo.bind(this) + this.getUniversalUniIdToken = getHttpUniIdToken.bind(this) + } else { + this.getUniversalClientInfo = this.getClientInfo + this.getUniversalUniIdToken = this.getUniIdToken + } +} + +module.exports = universal diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/common/utils.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/common/utils.js new file mode 100644 index 0000000..6360d83 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/common/utils.js @@ -0,0 +1,263 @@ +function batchFindObjctValue (obj = {}, keys = []) { + const values = {} + for (let i = 0; i < keys.length; i++) { + const key = keys[i] + const keyPath = key.split('.') + let currentKey = keyPath.shift() + let result = obj + while (currentKey) { + if (!result) { + break + } + result = result[currentKey] + currentKey = keyPath.shift() + } + values[key] = result + } + return values +} + +function getType (val) { + return Object.prototype.toString.call(val).slice(8, -1).toLowerCase() +} + +function hasOwn (obj, key) { + return Object.prototype.hasOwnProperty.call(obj, key) +} + +function isValidString (val) { + return val && getType(val) === 'string' +} + +function isPlainObject (obj) { + return getType(obj) === 'object' +} + +function isFn (fn) { + // 务必注意AsyncFunction + return typeof fn === 'function' +} + +// 获取文件后缀,只添加几种图片类型供客服消息接口使用 +const mime2ext = { + 'image/png': 'png', + 'image/jpeg': 'jpg', + 'image/gif': 'gif', + 'image/svg+xml': 'svg', + 'image/bmp': 'bmp', + 'image/webp': 'webp' +} + +function getExtension (contentType) { + return mime2ext[contentType] +} + +const isSnakeCase = /_(\w)/g +const isCamelCase = /[A-Z]/g + +function snake2camel (value) { + return value.replace(isSnakeCase, (_, c) => (c ? c.toUpperCase() : '')) +} + +function camel2snake (value) { + return value.replace(isCamelCase, str => '_' + str.toLowerCase()) +} + +function parseObjectKeys (obj, type) { + let parserReg, parser + switch (type) { + case 'snake2camel': + parser = snake2camel + parserReg = isSnakeCase + break + case 'camel2snake': + parser = camel2snake + parserReg = isCamelCase + break + } + for (const key in obj) { + if (hasOwn(obj, key)) { + if (parserReg.test(key)) { + const keyCopy = parser(key) + obj[keyCopy] = obj[key] + delete obj[key] + if (isPlainObject(obj[keyCopy])) { + obj[keyCopy] = parseObjectKeys(obj[keyCopy], type) + } else if (Array.isArray(obj[keyCopy])) { + obj[keyCopy] = obj[keyCopy].map((item) => { + return parseObjectKeys(item, type) + }) + } + } + } + } + return obj +} + +function snake2camelJson (obj) { + return parseObjectKeys(obj, 'snake2camel') +} + +function camel2snakeJson (obj) { + return parseObjectKeys(obj, 'camel2snake') +} + +function getOffsetDate (offset) { + return new Date( + Date.now() + (new Date().getTimezoneOffset() + (offset || 0) * 60) * 60000 + ) +} + +function getDateStr (date, separator = '-') { + date = date || new Date() + const dateArr = [] + dateArr.push(date.getFullYear()) + dateArr.push(('00' + (date.getMonth() + 1)).substr(-2)) + dateArr.push(('00' + date.getDate()).substr(-2)) + return dateArr.join(separator) +} + +function getTimeStr (date, separator = ':') { + date = date || new Date() + const timeArr = [] + timeArr.push(('00' + date.getHours()).substr(-2)) + timeArr.push(('00' + date.getMinutes()).substr(-2)) + timeArr.push(('00' + date.getSeconds()).substr(-2)) + return timeArr.join(separator) +} + +function getFullTimeStr (date) { + date = date || new Date() + return getDateStr(date) + ' ' + getTimeStr(date) +} + +function getDistinctArray (arr) { + return Array.from(new Set(arr)) +} + +/** + * 拼接url + * @param {string} base 基础路径 + * @param {string} path 在基础路径上拼接的路径 + * @returns + */ +function resolveUrl (base, path) { + if (/^https?:/.test(path)) { + return path + } + return base + path +} + +function getVerifyCode (len = 6) { + let code = '' + for (let i = 0; i < len; i++) { + code += Math.floor(Math.random() * 10) + } + return code +} + +function coverMobile (mobile) { + if (typeof mobile !== 'string') { + return mobile + } + return mobile.slice(0, 3) + '****' + mobile.slice(7) +} + +function getNonceStr (length = 16) { + let str = '' + while (str.length < length) { + str += Math.random().toString(32).substring(2) + } + return str.substring(0, length) +} + +function isMatchUserApp (userAppList, matchAppList) { + if (userAppList === undefined || userAppList === null) { + return true + } + if (getType(userAppList) !== 'array') { + return false + } + if (userAppList.includes('*')) { + return true + } + if (getType(matchAppList) === 'string') { + matchAppList = [matchAppList] + } + return userAppList.some(item => matchAppList.includes(item)) +} + +function checkIdCard (idCardNumber) { + if (!idCardNumber || typeof idCardNumber !== 'string' || idCardNumber.length !== 18) return false + + const coefficient = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2] + const checkCode = [1, 0, 'x', 9, 8, 7, 6, 5, 4, 3, 2] + const code = idCardNumber.substring(17) + + let sum = 0 + for (let i = 0; i < 17; i++) { + sum += Number(idCardNumber.charAt(i)) * coefficient[i] + } + + return checkCode[sum % 11].toString() === code.toLowerCase() +} + +function catchAwait (fn, finallyFn) { + if (!fn) return [new Error('no function')] + + if (Promise.prototype.finally === undefined) { + // eslint-disable-next-line no-extend-native + Promise.prototype.finally = function (finallyFn) { + return this.then( + res => Promise.resolve(finallyFn()).then(() => res), + error => Promise.resolve(finallyFn()).then(() => { throw error }) + ) + } + } + + return fn + .then((data) => [undefined, data]) + .catch((error) => [error]) + .finally(() => typeof finallyFn === 'function' && finallyFn()) +} + +function dataDesensitization (value = '', options = {}) { + const { onlyLast = false } = options + const [firstIndex, middleIndex, lastIndex] = onlyLast ? [0, 0, -1] : [0, 1, -1] + + if (!value) return value + const first = value.slice(firstIndex, middleIndex) + const middle = value.slice(middleIndex, lastIndex) + const last = value.slice(lastIndex) + const star = Array.from(new Array(middle.length), (v) => '*').join('') + + return first + star + last +} + +function getCurrentDateTimestamp (date = Date.now(), targetTimezone = 8) { + const oneHour = 60 * 60 * 1000 + return parseInt((date + targetTimezone * oneHour) / (24 * oneHour)) * (24 * oneHour) - targetTimezone * oneHour +} + +module.exports = { + getType, + isValidString, + batchFindObjctValue, + isPlainObject, + isFn, + getDistinctArray, + getFullTimeStr, + resolveUrl, + getOffsetDate, + camel2snakeJson, + snake2camelJson, + getExtension, + getVerifyCode, + coverMobile, + getNonceStr, + isMatchUserApp, + checkIdCard, + catchAwait, + dataDesensitization, + getCurrentDateTimestamp +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/common/validator.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/common/validator.js new file mode 100644 index 0000000..4b02104 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/common/validator.js @@ -0,0 +1,443 @@ +const { + isValidString, + getType +} = require('./utils.js') +const { + ERROR +} = require('./error') + +const baseValidator = Object.create(null) + +baseValidator.username = function (username) { + const errCode = ERROR.INVALID_USERNAME + if (!isValidString(username)) { + return { + errCode + } + } + if (/^\d+$/.test(username)) { + // 用户名不能为纯数字 + return { + errCode + } + }; + if (!/^[a-zA-Z0-9_-]+$/.test(username)) { + // 用户名仅能使用数字、字母、“_”及“-” + return { + errCode + } + } +} + +baseValidator.password = function (password) { + const errCode = ERROR.INVALID_PASSWORD + if (!isValidString(password)) { + return { + errCode + } + } + if (password.length < 6) { + // 密码长度不能小于6 + return { + errCode + } + } +} + +baseValidator.mobile = function (mobile) { + const errCode = ERROR.INVALID_MOBILE + if (getType(mobile) !== 'string') { + return { + errCode + } + } + if (mobile && !/^1\d{10}$/.test(mobile)) { + return { + errCode + } + } +} + +baseValidator.email = function (email) { + const errCode = ERROR.INVALID_EMAIL + if (getType(email) !== 'string') { + return { + errCode + } + } + if (email && !/@/.test(email)) { + return { + errCode + } + } +} + +baseValidator.nickname = function (nickname) { + const errCode = ERROR.INVALID_NICKNAME + if (nickname.indexOf('@') !== -1) { + // 昵称不允许含@ + return { + errCode + } + }; + if (/^\d+$/.test(nickname)) { + // 昵称不能为纯数字 + return { + errCode + } + }; + if (nickname.length > 100) { + // 昵称不可超过100字符 + return { + errCode + } + } +} + +const baseType = ['string', 'boolean', 'number', 'null'] // undefined不会由客户端提交上来 + +baseType.forEach((type) => { + baseValidator[type] = function (val) { + if (getType(val) === type) { + return + } + return { + errCode: ERROR.INVALID_PARAM + } + } +}) + +function tokenize(name) { + let i = 0 + const result = [] + let token = '' + while (i < name.length) { + const char = name[i] + switch (char) { + case '|': + case '<': + case '>': + token && result.push(token) + result.push(char) + token = '' + break + default: + token += char + break + } + i++ + if (i === name.length && token) { + result.push(token) + } + } + return result +} + +/** + * 处理validator名 + * @param {string} name + */ +function parseValidatorName(name) { + const tokenList = tokenize(name) + let i = 0 + let currentToken = tokenList[i] + const result = { + type: 'root', + children: [], + parent: null + } + let lastRealm = result + while (currentToken) { + switch (currentToken) { + case 'array': { + const currentRealm = { + type: 'array', + children: [], + parent: lastRealm + } + lastRealm.children.push(currentRealm) + lastRealm = currentRealm + break + } + case '<': + if (lastRealm.type !== 'array') { + throw new Error('Invalid validator token "<"') + } + break + case '>': + if (lastRealm.type !== 'array') { + throw new Error('Invalid validator token ">"') + } + lastRealm = lastRealm.parent + break + case '|': + if (lastRealm.type !== 'array' && lastRealm.type !== 'root') { + throw new Error('Invalid validator token "|"') + } + break + default: + lastRealm.children.push({ + type: currentToken + }) + break + } + i++ + currentToken = tokenList[i] + } + return result +} + +function getRuleCategory(rule) { + switch (rule.type) { + case 'array': + return 'array' + case 'root': + return 'root' + default: + return 'base' + } +} + + +// 特殊符号 https://www.ibm.com/support/pages/password-strength-rules ~!@#$%^&*_-+=`|\(){}[]:;"'<>,.?/ +// const specialChar = '~!@#$%^&*_-+=`|\(){}[]:;"\'<>,.?/' +// const specialCharRegExp = /^[~!@#$%^&*_\-+=`|\\(){}[\]:;"'<>,.?/]$/ +// for (let i = 0, arr = specialChar.split(''); i < arr.length; i++) { +// const char = arr[i] +// if (!specialCharRegExp.test(char)) { +// throw new Error('check special character error: ' + char) +// } +// } + +// 密码强度表达式 +const passwordRules = { + // 密码必须包含大小写字母、数字和特殊符号 + super: /^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[~!@#$%^&*_\-+=`|\\(){}[\]:;"'<>,.?/])[0-9a-zA-Z~!@#$%^&*_\-+=`|\\(){}[\]:;"'<>,.?/]{8,16}$/, + // 密码必须包含字母、数字和特殊符号 + strong: /^(?=.*[0-9])(?=.*[a-zA-Z])(?=.*[~!@#$%^&*_\-+=`|\\(){}[\]:;"'<>,.?/])[0-9a-zA-Z~!@#$%^&*_\-+=`|\\(){}[\]:;"'<>,.?/]{8,16}$/, + // 密码必须为字母、数字和特殊符号任意两种的组合 + medium: /^(?![0-9]+$)(?![a-zA-Z]+$)(?![~!@#$%^&*_\-+=`|\\(){}[\]:;"'<>,.?/]+$)[0-9a-zA-Z~!@#$%^&*_\-+=`|\\(){}[\]:;"'<>,.?/]{8,16}$/, + // 密码必须包含字母和数字 + weak: /^(?=.*[0-9])(?=.*[a-zA-Z])[0-9a-zA-Z~!@#$%^&*_\-+=`|\\(){}[\]:;"'<>,.?/]{6,16}$/, + +} + +function createPasswordVerifier({ + passwordStrength = '' +} = {}) { + return function (password) { + const passwordRegExp = passwordRules[passwordStrength] + if (!passwordRegExp) { + throw new Error('Invalid password strength config: ' + passwordStrength) + } + const errCode = ERROR.INVALID_PASSWORD + if (!isValidString(password)) { + return { + errCode + } + } + if (!passwordRegExp.test(password)) { + return { + errCode: errCode + '-' + passwordStrength + } + } + } +} + +function isEmpty(value) { + return value === undefined || + value === null || + (typeof value === 'string' && value.trim() === '') +} + +class Validator { + constructor({ + passwordStrength = '' + } = {}) { + this.baseValidator = baseValidator + this.customValidator = Object.create(null) + if (passwordStrength) { + this.mixin( + 'password', + createPasswordVerifier({ + passwordStrength + }) + ) + } + } + + mixin(type, handler) { + this.customValidator[type] = handler + } + + getRealBaseValidator(type) { + return this.customValidator[type] || this.baseValidator[type] + } + + + _isMatchUnionType(val, rule) { + if (!rule.children || rule.children.length === 0) { + return true + } + const children = rule.children + for (let i = 0; i < children.length; i++) { + const child = children[i] + const category = getRuleCategory(child) + let pass = false + switch (category) { + case 'base': + pass = this._isMatchBaseType(val, child) + break + case 'array': + pass = this._isMatchArrayType(val, child) + break + default: + break + } + if (pass) { + return true + } + } + return false + } + + _isMatchBaseType(val, rule) { + const method = this.getRealBaseValidator(rule.type) + if (typeof method !== 'function') { + throw new Error(`invalid schema type: ${rule.type}`) + } + const validateRes = method(val) + if (validateRes && validateRes.errCode) { + return false + } + return true + } + + _isMatchArrayType(arr, rule) { + if (getType(arr) !== 'array') { + return false + } + if (rule.children && rule.children.length && arr.some(item => !this._isMatchUnionType(item, rule))) { + return false + } + return true + } + + get validator() { + const _this = this + return new Proxy({}, { + get: (_, prop) => { + if (typeof prop !== 'string') { + return + } + const realBaseValidator = this.getRealBaseValidator(prop) + if (realBaseValidator) { + return realBaseValidator + } + const rule = parseValidatorName(prop) + return function (val) { + if (!_this._isMatchUnionType(val, rule)) { + return { + errCode: ERROR.INVALID_PARAM + } + } + } + } + }) + } + + validate(value = {}, schema = {}) { + for (const schemaKey in schema) { + let schemaValue = schema[schemaKey] + if (getType(schemaValue) === 'string') { + schemaValue = { + required: true, + type: schemaValue + } + } + const { + required, + type + } = schemaValue + // value内未传入了schemaKey或对应值为undefined + if (isEmpty(value[schemaKey])) { + if (required) { + return { + errCode: ERROR.PARAM_REQUIRED, + errMsgValue: { + param: schemaKey + }, + schemaKey + } + } else { + //delete value[schemaKey] + continue + } + } + const validateMethod = this.validator[type] + if (!validateMethod) { + throw new Error(`invalid schema type: ${type}`) + } + const validateRes = validateMethod(value[schemaKey]) + if (validateRes) { + validateRes.schemaKey = schemaKey + return validateRes + } + } + } +} + +function checkClientInfo(clientInfo) { + const stringNotRequired = { + required: false, + type: 'string' + } + const numberNotRequired = { + required: false, + type: 'number' + } + const numberOrStringNotRequired = { + required: false, + type: 'number|string' + } + const schema = { + uniPlatform: 'string', + appId: 'string', + deviceId: stringNotRequired, + osName: stringNotRequired, + locale: stringNotRequired, + clientIP: stringNotRequired, + appName: stringNotRequired, + appVersion: stringNotRequired, + appVersionCode: numberOrStringNotRequired, + channel: numberOrStringNotRequired, + userAgent: stringNotRequired, + uniIdToken: stringNotRequired, + deviceBrand: stringNotRequired, + deviceModel: stringNotRequired, + osVersion: stringNotRequired, + osLanguage: stringNotRequired, + osTheme: stringNotRequired, + romName: stringNotRequired, + romVersion: stringNotRequired, + devicePixelRatio: numberNotRequired, + windowWidth: numberNotRequired, + windowHeight: numberNotRequired, + screenWidth: numberNotRequired, + screenHeight: numberNotRequired + } + const validateRes = new Validator().validate(clientInfo, schema) + if (validateRes) { + if (validateRes.errCode === ERROR.PARAM_REQUIRED) { + console.warn('- 如果使用HBuilderX运行本地云函数/云对象功能时出现此提示,请改为使用客户端调用本地云函数方式调试,或更新HBuilderX到3.4.12及以上版本。\n- 如果是缺少clientInfo.appId,请检查项目manifest.json内是否配置了DCloud AppId') + throw new Error(`"clientInfo.${validateRes.schemaKey}" is required.`) + } else { + throw new Error(`Invalid client info: clienInfo.${validateRes.schemaKey}`) + } + } +} + +module.exports = { + Validator, + checkClientInfo +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/config/permission.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/config/permission.js new file mode 100644 index 0000000..229a264 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/config/permission.js @@ -0,0 +1,90 @@ +// 各接口权限配置,未配置接口表示允许任何用户访问(包括未登录用户) +module.exports = { + // 管理接口 + addUser: { + // auth: true // 已登录用户方可操作,配置角色或权限时此项可不写 + role: ['admin'] // 允许进行此操作的角色,包含任一角色均可操作。 + // permission: [] // 允许进行此操作的权限,包含任一权限均可操作。 + // 权限角色均配置时,用户拥有任一权限或任一角色均可操作 + }, + updateUser: { + role: ['admin'] + }, + authorizeAppLogin: { + role: ['admin'] + }, + removeAuthorizedApp: { + role: ['admin'] + }, + setAuthorizedApp: { + role: ['admin'] + }, + + // 用户接口 + closeAccount: { + auth: true + }, + updatePwd: { + auth: true + }, + logout: { + auth: true + }, + bindMobileBySms: { + auth: true + }, + bindMobileByUniverify: { + auth: true + }, + bindMobileByMpWeixin: { + auth: true + }, + bindAlipay: { + auth: true + }, + bindApple: { + auth: true + }, + bindQQ: { + auth: true + }, + bindWeixin: { + auth: true + }, + acceptInvite: { + auth: true + }, + getInvitedUser: { + auth: true + }, + setPushCid: { + auth: true + }, + getAccountInfo: { + auth: true + }, + unbindWeixin: { + auth: true + }, + unbindAlipay: { + auth: true + }, + unbindQQ: { + auth: true + }, + unbindApple: { + auth: true + }, + setPwd: { + auth: true + }, + getFrvCertifyId: { + auth: true + }, + getFrvAuthResult: { + auth: true + }, + getRealNameInfo: { + auth: true + } +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/index.obj.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/index.obj.js new file mode 100644 index 0000000..6f1a5f7 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/index.obj.js @@ -0,0 +1,696 @@ +const uniIdCommon = require('uni-id-common') +const uniCaptcha = require('uni-captcha') +const { + getType, + checkIdCard +} = require('./common/utils') +const { + checkClientInfo, + Validator +} = require('./common/validator') +const ConfigUtils = require('./lib/utils/config') +const { + isUniIdError, + ERROR +} = require('./common/error') +const middleware = require('./middleware/index') +const universal = require('./common/universal') + +const { + registerAdmin, + registerUser, + registerUserByEmail +} = require('./module/register/index') +const { + addUser, + updateUser +} = require('./module/admin/index') +const { + login, + loginBySms, + loginByUniverify, + loginByWeixin, + loginByAlipay, + loginByQQ, + loginByApple, + loginByWeixinMobile +} = require('./module/login/index') +const { + logout +} = require('./module/logout/index') +const { + bindMobileBySms, + bindMobileByUniverify, + bindMobileByMpWeixin, + bindAlipay, + bindApple, + bindQQ, + bindWeixin, + unbindWeixin, + unbindAlipay, + unbindQQ, + unbindApple +} = require('./module/relate/index') +const { + setPwd, + updatePwd, + resetPwdBySms, + resetPwdByEmail, + closeAccount, + getAccountInfo, + getRealNameInfo +} = require('./module/account/index') +const { + createCaptcha, + refreshCaptcha, + sendSmsCode, + sendEmailCode +} = require('./module/verify/index') +const { + refreshToken, + setPushCid, + secureNetworkHandshakeByWeixin +} = require('./module/utils/index') +const { + getInvitedUser, + acceptInvite +} = require('./module/fission') +const { + authorizeAppLogin, + removeAuthorizedApp, + setAuthorizedApp +} = require('./module/multi-end') +const { + getSupportedLoginType +} = require('./module/dev/index') +const { + externalRegister, + externalLogin, + updateUserInfoByExternal +} = require('./module/external') +const { + getFrvCertifyId, + getFrvAuthResult +} = require('./module/facial-recognition-verify') + +module.exports = { + async _before () { + // 支持 callFunction 与 URL化 + universal.call(this) + + const clientInfo = this.getUniversalClientInfo() + /** + * 检查clientInfo,无appId和uniPlatform时本云对象无法正常运行 + * 此外需要保证用到的clientInfo字段均经过类型检查 + * clientInfo由客户端上传并非完全可信,clientInfo内除clientIP、userAgent、source外均为客户端上传参数 + * 否则可能会出现一些意料外的情况 + */ + checkClientInfo(clientInfo) + let clientPlatform = clientInfo.uniPlatform + // 统一platform名称 + switch (clientPlatform) { + case 'app': + case 'app-plus': + case 'app-android': + case 'app-ios': + clientPlatform = 'app' + break + case 'web': + case 'h5': + clientPlatform = 'web' + break + default: + break + } + + this.clientPlatform = clientPlatform + + // 挂载uni-id实例到this上,方便后续调用 + this.uniIdCommon = uniIdCommon.createInstance({ + clientInfo + }) + + // 包含uni-id配置合并等功能的工具集 + this.configUtils = new ConfigUtils({ + context: this + }) + this.config = this.configUtils.getPlatformConfig() + this.hooks = this.configUtils.getHooks() + + this.validator = new Validator({ + passwordStrength: this.config.passwordStrength + }) + + // 扩展 validator 增加 验证身份证号码合法性 + this.validator.mixin('idCard', function (idCard) { + if (!checkIdCard(idCard)) { + return { + errCode: ERROR.INVALID_ID_CARD + } + } + }) + this.validator.mixin('realName', function (realName) { + if ( + typeof realName !== 'string' || + realName.length < 2 || + !/^[\u4e00-\u9fa5]{1,10}(·?[\u4e00-\u9fa5]{1,10}){0,5}$/.test(realName) + ) { + return { + errCode: ERROR.INVALID_REAL_NAME + } + } + }) + /** + * 示例:覆盖密码验证规则 + */ + // this.validator.mixin('password', function (password) { + // if (typeof password !== 'string' || password.length < 10) { + // // 调整为密码长度不能小于10 + // return { + // errCode: ERROR.INVALID_PASSWORD + // } + // } + // }) + /** + * 示例:新增验证规则 + */ + // this.validator.mixin('timestamp', function (timestamp) { + // if (typeof timestamp !== 'number' || timestamp > Date.now()) { + // return { + // errCode: ERROR.INVALID_PARAM + // } + // } + // }) + // // 新增规则同样可以在数组验证规则中使用 + // this.validator.validate({ + // timestamp: 123456789 + // }, { + // timestamp: 'timestamp' + // }) + // this.validator.validate({ + // timestampList: [123456789, 123123123123] + // }, { + // timestampList: 'array' + // }) + // // 甚至更复杂的写法 + // this.validator.validate({ + // timestamp: [123456789123123123, 123123123123] + // }, { + // timestamp: 'timestamp|array' + // }) + + // 挂载uni-captcha到this上,方便后续调用 + this.uniCaptcha = uniCaptcha + Object.defineProperty(this, 'uniOpenBridge', { + get () { + return require('uni-open-bridge-common') + } + }) + + // 挂载中间件 + this.middleware = {} + for (const mwName in middleware) { + this.middleware[mwName] = middleware[mwName].bind(this) + } + + // 国际化 + const messages = require('./lang/index') + const fallbackLocale = 'zh-Hans' + const i18n = uniCloud.initI18n({ + locale: clientInfo.locale, + fallbackLocale, + messages: JSON.parse(JSON.stringify(messages)) + }) + if (!messages[i18n.locale]) { + i18n.setLocale(fallbackLocale) + } + this.t = i18n.t.bind(i18n) + + this.response = {} + + // 请求鉴权验证 + await this.middleware.verifyRequestSign() + + // 通用权限校验模块 + await this.middleware.accessControl() + }, + _after (error, result) { + if (error) { + // 处理中间件内抛出的标准响应对象 + if (error.errCode && getType(error) === 'object') { + const errCode = error.errCode + if (!isUniIdError(errCode)) { + return error + } + return { + errCode, + errMsg: error.errMsg || this.t(errCode, error.errMsgValue) + } + } + throw error + } + return Object.assign(this.response, result) + }, + /** + * 注册管理员 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#register-admin + * @param {Object} params + * @param {String} params.username 用户名 + * @param {String} params.password 密码 + * @param {String} params.nickname 昵称 + * @returns + */ + registerAdmin, + /** + * 新增用户 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#add-user + * @param {Object} params + * @param {String} params.username 用户名 + * @param {String} params.password 密码 + * @param {String} params.nickname 昵称 + * @param {Array} params.authorizedApp 允许登录的AppID列表 + * @param {Array} params.role 用户角色列表 + * @param {String} params.mobile 手机号 + * @param {String} params.email 邮箱 + * @param {Array} params.tags 用户标签 + * @param {Number} params.status 用户状态 + * @returns + */ + addUser, + /** + * 修改用户 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#update-user + * @param {Object} params + * @param {String} params.id 要更新的用户id + * @param {String} params.username 用户名 + * @param {String} params.password 密码 + * @param {String} params.nickname 昵称 + * @param {Array} params.authorizedApp 允许登录的AppID列表 + * @param {Array} params.role 用户角色列表 + * @param {String} params.mobile 手机号 + * @param {String} params.email 邮箱 + * @param {Array} params.tags 用户标签 + * @param {Number} params.status 用户状态 + * @returns + */ + updateUser, + /** + * 授权用户登录应用 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#authorize-app-login + * @param {Object} params + * @param {String} params.uid 用户id + * @param {String} params.appId 授权的应用的AppId + * @returns + */ + authorizeAppLogin, + /** + * 移除用户登录授权 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#remove-authorized-app + * @param {Object} params + * @param {String} params.uid 用户id + * @param {String} params.appId 取消授权的应用的AppId + * @returns + */ + removeAuthorizedApp, + /** + * 设置用户允许登录的应用列表 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#set-authorized-app + * @param {Object} params + * @param {String} params.uid 用户id + * @param {Array} params.appIdList 允许登录的应用AppId列表 + * @returns + */ + setAuthorizedApp, + /** + * 注册普通用户 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#register-user + * @param {Object} params + * @param {String} params.username 用户名 + * @param {String} params.password 密码 + * @param {String} params.captcha 图形验证码 + * @param {String} params.nickname 昵称 + * @param {String} params.inviteCode 邀请码 + * @returns + */ + registerUser, + /** + * 通过邮箱+验证码注册用户 + * @param {Object} params + * @param {String} params.email 邮箱 + * @param {String} params.password 密码 + * @param {String} params.nickname 昵称 + * @param {String} params.code 邮箱验证码 + * @param {String} params.inviteCode 邀请码 + * @returns + */ + registerUserByEmail, + /** + * 用户名密码登录 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#login + * @param {Object} params + * @param {String} params.username 用户名 + * @param {String} params.mobile 手机号 + * @param {String} params.email 邮箱 + * @param {String} params.password 密码 + * @param {String} params.captcha 图形验证码 + * @returns + */ + login, + /** + * 短信验证码登录 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#login-by-sms + * @param {Object} params + * @param {String} params.mobile 手机号 + * @param {String} params.code 短信验证码 + * @param {String} params.captcha 图形验证码 + * @param {String} params.inviteCode 邀请码 + * @returns + */ + loginBySms, + /** + * App端一键登录 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#login-by-univerify + * @param {Object} params + * @param {String} params.access_token APP端一键登录返回的access_token + * @param {String} params.openid APP端一键登录返回的openid + * @param {String} params.inviteCode 邀请码 + * @returns + */ + loginByUniverify, + /** + * 微信登录 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#login-by-weixin + * @param {Object} params + * @param {String} params.code 微信登录返回的code + * @param {String} params.inviteCode 邀请码 + * @returns + */ + loginByWeixin, + /** + * 支付宝登录 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#login-by-alipay + * @param {Object} params + * @param {String} params.code 支付宝小程序客户端登录返回的code + * @param {String} params.inviteCode 邀请码 + * @returns + */ + loginByAlipay, + /** + * QQ登录 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#login-by-qq + * @param {Object} params + * @param {String} params.code QQ小程序登录返回的code参数 + * @param {String} params.accessToken App端QQ登录返回的accessToken参数 + * @param {String} params.accessTokenExpired accessToken过期时间,由App端QQ登录返回的expires_in参数计算而来,单位:毫秒 + * @param {String} params.inviteCode 邀请码 + * @returns + */ + loginByQQ, + /** + * 苹果登录 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#login-by-apple + * @param {Object} params + * @param {String} params.identityToken 苹果登录返回的identityToken + * @param {String} params.nickname 用户昵称 + * @param {String} params.inviteCode 邀请码 + * @returns + */ + loginByApple, + loginByWeixinMobile, + /** + * 用户退出登录 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#logout + * @returns + */ + logout, + /** + * 通过短信验证码绑定手机号 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#bind-mobile-by-sms + * @param {Object} params + * @param {String} params.mobile 手机号 + * @param {String} params.code 短信验证码 + * @param {String} params.captcha 图形验证码 + * @returns + */ + bindMobileBySms, + /** + * 通过一键登录绑定手机号 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#bind-mobile-by-univerify + * @param {Object} params + * @param {String} params.openid APP端一键登录返回的openid + * @param {String} params.access_token APP端一键登录返回的access_token + * @returns + */ + bindMobileByUniverify, + /** + * 通过微信绑定手机号 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#bind-mobile-by-mp-weixin + * @param {Object} params + * @param {String} params.encryptedData 微信获取手机号返回的加密信息 + * @param {String} params.iv 微信获取手机号返回的初始向量 + * @returns + */ + bindMobileByMpWeixin, + /** + * 绑定微信 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#bind-weixin + * @param {Object} params + * @param {String} params.code 微信登录返回的code + * @returns + */ + bindWeixin, + /** + * 绑定QQ + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#bind-qq + * @param {Object} params + * @param {String} params.code 小程序端QQ登录返回的code + * @param {String} params.accessToken APP端QQ登录返回的accessToken + * @param {String} params.accessTokenExpired accessToken过期时间,由App端QQ登录返回的expires_in参数计算而来,单位:毫秒 + * @returns + */ + bindQQ, + /** + * 绑定支付宝账号 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#bind-alipay + * @param {Object} params + * @param {String} params.code 支付宝小程序登录返回的code参数 + * @returns + */ + bindAlipay, + /** + * 绑定苹果账号 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#bind-apple + * @param {Object} params + * @param {String} params.identityToken 苹果登录返回identityToken + * @returns + */ + bindApple, + /** + * 更新密码 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#update-pwd + * @param {object} params + * @param {string} params.oldPassword 旧密码 + * @param {string} params.newPassword 新密码 + * @returns {object} + */ + updatePwd, + /** + * 通过短信验证码重置密码 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#reset-pwd-by-sms + * @param {object} params + * @param {string} params.mobile 手机号 + * @param {string} params.mobile 短信验证码 + * @param {string} params.password 密码 + * @param {string} params.captcha 图形验证码 + * @returns {object} + */ + resetPwdBySms, + /** + * 通过邮箱验证码重置密码 + * @param {object} params + * @param {string} params.email 邮箱 + * @param {string} params.code 邮箱验证码 + * @param {string} params.password 密码 + * @param {string} params.captcha 图形验证码 + * @returns {object} + */ + resetPwdByEmail, + /** + * 注销账户 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#close-account + * @returns + */ + closeAccount, + /** + * 获取账户账户简略信息 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#get-account-info + */ + getAccountInfo, + /** + * 创建图形验证码 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#create-captcha + * @param {Object} params + * @param {String} params.scene 图形验证码使用场景 + * @returns + */ + createCaptcha, + /** + * 刷新图形验证码 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#refresh-captcha + * @param {Object} params + * @param {String} params.scene 图形验证码使用场景 + * @returns + */ + refreshCaptcha, + /** + * 发送短信验证码 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#send-sms-code + * @param {Object} params + * @param {String} params.mobile 手机号 + * @param {String} params.captcha 图形验证码 + * @param {String} params.scene 短信验证码使用场景 + * @returns + */ + sendSmsCode, + /** + * 发送邮箱验证码 + * @tutorial 需自行实现功能 + * @param {Object} params + * @param {String} params.email 邮箱 + * @param {String} params.captcha 图形验证码 + * @param {String} params.scene 短信验证码使用场景 + * @returns + */ + sendEmailCode, + /** + * 刷新token + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#refresh-token + */ + refreshToken, + /** + * 接受邀请 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#accept-invite + * @param {Object} params + * @param {String} params.inviteCode 邀请码 + * @returns + */ + acceptInvite, + /** + * 获取受邀用户 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#get-invited-user + * @param {Object} params + * @param {Number} params.level 获取受邀用户的级数,1表示直接邀请的用户 + * @param {Number} params.limit 返回数据大小 + * @param {Number} params.offset 返回数据偏移 + * @param {Boolean} params.needTotal 是否需要返回总数 + * @returns + */ + getInvitedUser, + /** + * 更新device表的push_clien_id + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#set-push-cid + * @param {object} params + * @param {string} params.pushClientId 客户端pushClientId + * @returns + */ + setPushCid, + /** + * 获取支持的登录方式 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#get-supported-login-type + * @returns + */ + getSupportedLoginType, + + /** + * 解绑微信 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#unbind-weixin + * @returns + */ + unbindWeixin, + /** + * 解绑支付宝 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#unbind-alipay + * @returns + */ + unbindAlipay, + /** + * 解绑QQ + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#unbind-qq + * @returns + */ + unbindQQ, + /** + * 解绑Apple + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#unbind-apple + * @returns + */ + unbindApple, + /** + * 安全网络握手,目前仅处理微信小程序安全网络握手 + */ + secureNetworkHandshakeByWeixin, + /** + * 设置密码 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#set-pwd + * @returns + */ + setPwd, + /** + * 外部注册用户 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#external-register + * @param {object} params + * @param {string} params.externalUid 业务系统的用户id + * @param {string} params.nickname 昵称 + * @param {string} params.gender 性别 + * @param {string} params.avatar 头像 + * @returns {object} + */ + externalRegister, + /** + * 外部用户登录 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#external-login + * @param {object} params + * @param {string} params.userId uni-id体系用户id + * @param {string} params.externalUid 业务系统的用户id + * @returns {object} + */ + externalLogin, + /** + * 使用 userId 或 externalUid 获取用户信息 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#external-update-userinfo + * @param {object} params + * @param {string} params.userId uni-id体系的用户id + * @param {string} params.externalUid 业务系统的用户id + * @param {string} params.nickname 昵称 + * @param {string} params.gender 性别 + * @param {string} params.avatar 头像 + * @returns {object} + */ + updateUserInfoByExternal, + /** + * 获取认证ID + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#get-frv-certify-id + * @param {Object} params + * @param {String} params.realName 真实姓名 + * @param {String} params.idCard 身份证号码 + * @returns + */ + getFrvCertifyId, + /** + * 查询认证结果 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#get-frv-auth-result + * @param {Object} params + * @param {String} params.certifyId 认证ID + * @param {String} params.needAlivePhoto 是否获取认证照片,Y_O (原始图片)、Y_M(虚化,背景马赛克)、N(不返图) + * @returns + */ + getFrvAuthResult, + /** + * 获取实名信息 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#get-realname-info + * @param {Object} params + * @param {Boolean} params.decryptData 是否解密数据 + * @returns + */ + getRealNameInfo +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lang/en.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lang/en.js new file mode 100644 index 0000000..6825461 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lang/en.js @@ -0,0 +1,62 @@ +const word = { + login: 'login', + 'verify-mobile': 'verify phone number' +} + +const sentence = { + 'uni-id-account-exists': 'Account exists', + 'uni-id-account-not-exists': 'Account does not exists', + 'uni-id-account-not-exists-in-current-app': 'Account does not exists in current app', + 'uni-id-account-conflict': 'User account conflict', + 'uni-id-account-banned': 'Account has been banned', + 'uni-id-account-auditing': 'Account audit in progress', + 'uni-id-account-audit-failed': 'Account audit failed', + 'uni-id-account-closed': 'Account has been closed', + 'uni-id-captcha-required': 'Captcha required', + 'uni-id-password-error': 'Password error', + 'uni-id-password-error-exceed-limit': 'The number of password errors is excessive', + 'uni-id-invalid-username': 'Invalid username', + 'uni-id-invalid-password': 'invalid password', + 'uni-id-invalid-password-super': 'Passwords must have 8-16 characters and contain uppercase letters, lowercase letters, numbers, and symbols.', + 'uni-id-invalid-password-strong': 'Passwords must have 8-16 characters and contain letters, numbers and symbols.', + 'uni-id-invalid-password-medium': 'Passwords must have 8-16 characters and contain at least two of the following: letters, numbers, and symbols.', + 'uni-id-invalid-password-weak': 'Passwords must have 6-16 characters and contain letters and numbers.', + 'uni-id-invalid-mobile': 'Invalid mobile phone number', + 'uni-id-invalid-email': 'Invalid email address', + 'uni-id-invalid-nickname': 'Invalid nickname', + 'uni-id-invalid-param': 'Invalid parameter', + 'uni-id-param-required': 'Parameter required: {param}', + 'uni-id-get-third-party-account-failed': 'Get third party account failed', + 'uni-id-get-third-party-user-info-failed': 'Get third party user info failed', + 'uni-id-mobile-verify-code-error': 'Verify code error or expired', + 'uni-id-email-verify-code-error': 'Verify code error or expired', + 'uni-id-admin-exists': 'Administrator exists', + 'uni-id-permission-error': 'Permission denied', + 'uni-id-system-error': 'System error', + 'uni-id-set-invite-code-failed': 'Set invite code failed', + 'uni-id-invalid-invite-code': 'Invalid invite code', + 'uni-id-change-inviter-forbidden': 'Change inviter is not allowed', + 'uni-id-bind-conflict': 'This account has been bound', + 'uni-id-admin-exist-in-other-apps': 'Administrator is registered in other consoles', + 'uni-id-unbind-failed': 'Please bind first and then unbind', + 'uni-id-unbind-not-supported': 'Unbinding is not supported', + 'uni-id-unbind-mobile-not-exists': 'This is the only way to login at the moment, please bind your phone number and then try to unbind', + 'uni-id-unbind-password-not-exists': 'Please set a password first', + 'uni-id-unsupported-request': 'Unsupported request', + 'uni-id-illegal-request': 'Illegal request', + 'uni-id-config-field-required': 'Config field required: {field}', + 'uni-id-config-field-invalid': 'Config field: {field} is invalid', + 'uni-id-frv-fail': 'Real name certify failed', + 'uni-id-frv-processing': 'Waiting for face recognition', + 'uni-id-realname-verified': 'This account has been verified', + 'uni-id-idcard-exists': 'The ID number has been bound to the account', + 'uni-id-invalid-idcard': 'ID number is invalid', + 'uni-id-invalid-realname': 'The name can only be Chinese characters', + 'uni-id-unknown-error': 'unknown error', + 'uni-id-realname-verify-upper-limit': 'The number of real-name certify on the day has reached the upper limit' +} + +module.exports = { + ...word, + ...sentence +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lang/index.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lang/index.js new file mode 100644 index 0000000..1f22998 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lang/index.js @@ -0,0 +1,22 @@ +let lang = { + 'zh-Hans': require('./zh-hans'), + en: require('./en') +} + +function mergeLanguage (lang1, lang2) { + const localeList = Object.keys(lang1) + localeList.push(...Object.keys(lang2)) + const result = {} + for (let i = 0; i < localeList.length; i++) { + const locale = localeList[i] + result[locale] = Object.assign({}, lang1[locale], lang2[locale]) + } + return result +} + +try { + const langPath = require.resolve('uni-config-center/uni-id/lang/index.js') + lang = mergeLanguage(lang, require(langPath)) +} catch (error) { } + +module.exports = lang diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lang/zh-hans.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lang/zh-hans.js new file mode 100644 index 0000000..911ce20 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lang/zh-hans.js @@ -0,0 +1,64 @@ +const word = { + login: '登录', + 'verify-mobile': '验证手机号' +} + +const sentence = { + 'uni-id-token-expired': '登录状态失效,token已过期', + 'uni-id-check-token-failed': 'token校验未通过', + 'uni-id-account-exists': '此账号已注册', + 'uni-id-account-not-exists': '此账号未注册', + 'uni-id-account-not-exists-in-current-app': '此账号未在该应用注册', + 'uni-id-account-conflict': '用户账号冲突', + 'uni-id-account-banned': '此账号已封禁', + 'uni-id-account-auditing': '此账号正在审核中', + 'uni-id-account-audit-failed': '此账号审核失败', + 'uni-id-account-closed': '此账号已注销', + 'uni-id-captcha-required': '请输入图形验证码', + 'uni-id-password-error': '密码错误', + 'uni-id-password-error-exceed-limit': '密码错误次数过多,请稍后再试', + 'uni-id-invalid-username': '用户名不合法', + 'uni-id-invalid-password': '密码不合法', + 'uni-id-invalid-password-super': '密码必须包含大小写字母、数字和特殊符号,长度8-16位', + 'uni-id-invalid-password-strong': '密码必须包含字母、数字和特殊符号,长度8-16位不合法', + 'uni-id-invalid-password-medium': '密码必须为字母、数字和特殊符号任意两种的组合,长度8-16位', + 'uni-id-invalid-password-weak': '密码必须包含字母和数字,长度6-16位', + 'uni-id-invalid-mobile': '手机号码不合法', + 'uni-id-invalid-email': '邮箱不合法', + 'uni-id-invalid-nickname': '昵称不合法', + 'uni-id-invalid-param': '参数错误', + 'uni-id-param-required': '缺少参数: {param}', + 'uni-id-get-third-party-account-failed': '获取第三方账号失败', + 'uni-id-get-third-party-user-info-failed': '获取用户信息失败', + 'uni-id-mobile-verify-code-error': '手机验证码错误或已过期', + 'uni-id-email-verify-code-error': '邮箱验证码错误或已过期', + 'uni-id-admin-exists': '超级管理员已存在', + 'uni-id-permission-error': '权限错误', + 'uni-id-system-error': '系统错误', + 'uni-id-set-invite-code-failed': '设置邀请码失败', + 'uni-id-invalid-invite-code': '邀请码不可用', + 'uni-id-change-inviter-forbidden': '禁止修改邀请人', + 'uni-id-bind-conflict': '此账号已被绑定', + 'uni-id-admin-exist-in-other-apps': '超级管理员已在其他控制台注册', + 'uni-id-unbind-failed': '请先绑定后再解绑', + 'uni-id-unbind-not-supported': '不支持解绑', + 'uni-id-unbind-mobile-not-exists': '这是当前唯一登录方式,请绑定手机号后再尝试解绑', + 'uni-id-unbind-password-not-exists': '请先设置密码在尝试解绑', + 'uni-id-unsupported-request': '不支持的请求方式', + 'uni-id-illegal-request': '非法请求', + 'uni-id-frv-fail': '实名认证失败', + 'uni-id-frv-processing': '等待人脸识别', + 'uni-id-realname-verified': '该账号已实名认证', + 'uni-id-idcard-exists': '该证件号码已绑定账号', + 'uni-id-invalid-idcard': '身份证号码不合法', + 'uni-id-invalid-realname': '姓名只能是汉字', + 'uni-id-unknown-error': '未知错误', + 'uni-id-realname-verify-upper-limit': '当日实名认证次数已达上限', + 'uni-id-config-field-required': '缺少配置项: {field}', + 'uni-id-config-field-invalid': '配置项: {field}无效' +} + +module.exports = { + ...word, + ...sentence +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/README.md b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/README.md new file mode 100644 index 0000000..47d8c4c --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/README.md @@ -0,0 +1,3 @@ +# 说明 + +此目录内为uni-id-co基础能力,不建议直接修改。如果你发现有些逻辑加入会更好,或者此部分代码有Bug可以向我们提交PR,仓库地址:[]()。如果有特殊的需求也可以在[论坛](https://ask.dcloud.net.cn/)提出,我们可以讨论下如何实现。 \ No newline at end of file diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/npm/index.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/npm/index.js new file mode 100644 index 0000000..0edfaf4 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/npm/index.js @@ -0,0 +1,3 @@ +"use strict";var e=require("buffer"),r=require("stream"),t=require("util"),n=require("crypto"),o="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function i(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var a={},s={exports:{}}; +/*! safe-buffer. MIT License. Feross Aboukhadijeh */ +!function(r,t){var n=e,o=n.Buffer;function i(e,r){for(var t in e)r[t]=e[t]}function a(e,r,t){return o(e,r,t)}o.from&&o.alloc&&o.allocUnsafe&&o.allocUnsafeSlow?r.exports=n:(i(n,t),t.Buffer=a),a.prototype=Object.create(o.prototype),i(o,a),a.from=function(e,r,t){if("number"==typeof e)throw new TypeError("Argument must not be a number");return o(e,r,t)},a.alloc=function(e,r,t){if("number"!=typeof e)throw new TypeError("Argument must be a number");var n=o(e);return void 0!==r?"string"==typeof t?n.fill(r,t):n.fill(r):n.fill(0),n},a.allocUnsafe=function(e){if("number"!=typeof e)throw new TypeError("Argument must be a number");return o(e)},a.allocUnsafeSlow=function(e){if("number"!=typeof e)throw new TypeError("Argument must be a number");return n.SlowBuffer(e)}}(s,s.exports);var u=s.exports,c=u.Buffer,f=r;function l(e){if(this.buffer=null,this.writable=!0,this.readable=!0,!e)return this.buffer=c.alloc(0),this;if("function"==typeof e.pipe)return this.buffer=c.alloc(0),e.pipe(this),this;if(e.length||"object"==typeof e)return this.buffer=e,this.writable=!1,process.nextTick(function(){this.emit("end",e),this.readable=!1,this.emit("close")}.bind(this)),this;throw new TypeError("Unexpected data type ("+typeof e+")")}t.inherits(l,f),l.prototype.write=function(e){this.buffer=c.concat([this.buffer,c.from(e)]),this.emit("data",e)},l.prototype.end=function(e){e&&this.write(e),this.emit("end",e),this.emit("close"),this.writable=!1,this.readable=!1};var p=l,h=e.Buffer,v=e.SlowBuffer,d=y;function y(e,r){if(!h.isBuffer(e)||!h.isBuffer(r))return!1;if(e.length!==r.length)return!1;for(var t=0,n=0;n=E&&--n,n}var A={derToJose:function(e,r){e=x(e);var t=_(r),n=t+1,o=e.length,i=0;if(48!==e[i++])throw new Error('Could not find expected "seq"');var a=e[i++];if(a===(1|E)&&(a=e[i++]),o-i=1.5*t;return Math.round(e/t)+" "+n+(o?"s":"")}var Le=function(e,r){r=r||{};var t=typeof e;if("string"===t&&e.length>0)return function(e){if((e=String(e)).length>100)return;var r=/^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(e);if(!r)return;var t=parseFloat(r[1]);switch((r[2]||"ms").toLowerCase()){case"years":case"year":case"yrs":case"yr":case"y":return t*He;case"weeks":case"week":case"w":return t*Ce;case"days":case"day":case"d":return t*qe;case"hours":case"hour":case"hrs":case"hr":case"h":return t*De;case"minutes":case"minute":case"mins":case"min":case"m":return t*ze;case"seconds":case"second":case"secs":case"sec":case"s":return t*Me;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return t;default:return}}(e);if("number"===t&&isFinite(e))return r.long?function(e){var r=Math.abs(e);if(r>=qe)return Ue(e,r,qe,"day");if(r>=De)return Ue(e,r,De,"hour");if(r>=ze)return Ue(e,r,ze,"minute");if(r>=Me)return Ue(e,r,Me,"second");return e+" ms"}(e):function(e){var r=Math.abs(e);if(r>=qe)return Math.round(e/qe)+"d";if(r>=De)return Math.round(e/De)+"h";if(r>=ze)return Math.round(e/ze)+"m";if(r>=Me)return Math.round(e/Me)+"s";return e+"ms"}(e);throw new Error("val is not a non-empty string or a valid number. val="+JSON.stringify(e))},Ge=function(e,r){var t=r||Math.floor(Date.now()/1e3);if("string"==typeof e){var n=Le(e);if(void 0===n)return;return Math.floor(t+n/1e3)}return"number"==typeof e?t+e:void 0},Ke={exports:{}};!function(e,r){var t;r=Ke.exports=Y,t="object"==typeof process&&process.env&&process.env.NODE_DEBUG&&/\bsemver\b/i.test(process.env.NODE_DEBUG)?function(){var e=Array.prototype.slice.call(arguments,0);e.unshift("SEMVER"),console.log.apply(console,e)}:function(){},r.SEMVER_SPEC_VERSION="2.0.0";var n=256,o=Number.MAX_SAFE_INTEGER||9007199254740991,i=n-6,a=r.re=[],s=r.safeRe=[],u=r.src=[],c=0,f="[a-zA-Z0-9-]",l=[["\\s",1],["\\d",n],[f,i]];function p(e){for(var r=0;r)?=?)";var k=c++;u[k]=u[v]+"|x|X|\\*";var P=c++;u[P]=u[h]+"|x|X|\\*";var R=c++;u[R]="[v=\\s]*("+u[P]+")(?:\\.("+u[P]+")(?:\\.("+u[P]+")(?:"+u[w]+")?"+u[_]+"?)?)?";var B=c++;u[B]="[v=\\s]*("+u[k]+")(?:\\.("+u[k]+")(?:\\.("+u[k]+")(?:"+u[j]+")?"+u[_]+"?)?)?";var $=c++;u[$]="^"+u[T]+"\\s*"+u[R]+"$";var I=c++;u[I]="^"+u[T]+"\\s*"+u[B]+"$";var V=c++;u[V]="(?:^|[^\\d])(\\d{1,16})(?:\\.(\\d{1,16}))?(?:\\.(\\d{1,16}))?(?:$|[^\\d])";var N=c++;u[N]="(?:~>?)";var M=c++;u[M]="(\\s*)"+u[N]+"\\s+",a[M]=new RegExp(u[M],"g"),s[M]=new RegExp(p(u[M]),"g");var z=c++;u[z]="^"+u[N]+u[R]+"$";var D=c++;u[D]="^"+u[N]+u[B]+"$";var q=c++;u[q]="(?:\\^)";var C=c++;u[C]="(\\s*)"+u[q]+"\\s+",a[C]=new RegExp(u[C],"g"),s[C]=new RegExp(p(u[C]),"g");var H=c++;u[H]="^"+u[q]+u[R]+"$";var U=c++;u[U]="^"+u[q]+u[B]+"$";var L=c++;u[L]="^"+u[T]+"\\s*("+O+")$|^$";var G=c++;u[G]="^"+u[T]+"\\s*("+x+")$|^$";var K=c++;u[K]="(\\s*)"+u[T]+"\\s*("+O+"|"+u[R]+")",a[K]=new RegExp(u[K],"g"),s[K]=new RegExp(p(u[K]),"g");var F=c++;u[F]="^\\s*("+u[R]+")\\s+-\\s+("+u[R]+")\\s*$";var J=c++;u[J]="^\\s*("+u[B]+")\\s+-\\s+("+u[B]+")\\s*$";var W=c++;u[W]="(<|>)?=?\\s*\\*";for(var Z=0;Z<35;Z++)t(Z,u[Z]),a[Z]||(a[Z]=new RegExp(u[Z]),s[Z]=new RegExp(p(u[Z])));function X(e,r){if(r&&"object"==typeof r||(r={loose:!!r,includePrerelease:!1}),e instanceof Y)return e;if("string"!=typeof e)return null;if(e.length>n)return null;if(!(r.loose?s[A]:s[E]).test(e))return null;try{return new Y(e,r)}catch(e){return null}}function Y(e,r){if(r&&"object"==typeof r||(r={loose:!!r,includePrerelease:!1}),e instanceof Y){if(e.loose===r.loose)return e;e=e.version}else if("string"!=typeof e)throw new TypeError("Invalid Version: "+e);if(e.length>n)throw new TypeError("version is longer than "+n+" characters");if(!(this instanceof Y))return new Y(e,r);t("SemVer",e,r),this.options=r,this.loose=!!r.loose;var i=e.trim().match(r.loose?s[A]:s[E]);if(!i)throw new TypeError("Invalid Version: "+e);if(this.raw=e,this.major=+i[1],this.minor=+i[2],this.patch=+i[3],this.major>o||this.major<0)throw new TypeError("Invalid major version");if(this.minor>o||this.minor<0)throw new TypeError("Invalid minor version");if(this.patch>o||this.patch<0)throw new TypeError("Invalid patch version");i[4]?this.prerelease=i[4].split(".").map((function(e){if(/^[0-9]+$/.test(e)){var r=+e;if(r>=0&&r=0;)"number"==typeof this.prerelease[t]&&(this.prerelease[t]++,t=-2);-1===t&&this.prerelease.push(0)}r&&(this.prerelease[0]===r?isNaN(this.prerelease[1])&&(this.prerelease=[r,0]):this.prerelease=[r,0]);break;default:throw new Error("invalid increment argument: "+e)}return this.format(),this.raw=this.version,this},r.inc=function(e,r,t,n){"string"==typeof t&&(n=t,t=void 0);try{return new Y(e,t).inc(r,n).version}catch(e){return null}},r.diff=function(e,r){if(oe(e,r))return null;var t=X(e),n=X(r),o="";if(t.prerelease.length||n.prerelease.length){o="pre";var i="prerelease"}for(var a in t)if(("major"===a||"minor"===a||"patch"===a)&&t[a]!==n[a])return o+a;return i},r.compareIdentifiers=ee;var Q=/^[0-9]+$/;function ee(e,r){var t=Q.test(e),n=Q.test(r);return t&&n&&(e=+e,r=+r),e===r?0:t&&!n?-1:n&&!t?1:e0}function ne(e,r,t){return re(e,r,t)<0}function oe(e,r,t){return 0===re(e,r,t)}function ie(e,r,t){return 0!==re(e,r,t)}function ae(e,r,t){return re(e,r,t)>=0}function se(e,r,t){return re(e,r,t)<=0}function ue(e,r,t,n){switch(r){case"===":return"object"==typeof e&&(e=e.version),"object"==typeof t&&(t=t.version),e===t;case"!==":return"object"==typeof e&&(e=e.version),"object"==typeof t&&(t=t.version),e!==t;case"":case"=":case"==":return oe(e,t,n);case"!=":return ie(e,t,n);case">":return te(e,t,n);case">=":return ae(e,t,n);case"<":return ne(e,t,n);case"<=":return se(e,t,n);default:throw new TypeError("Invalid operator: "+r)}}function ce(e,r){if(r&&"object"==typeof r||(r={loose:!!r,includePrerelease:!1}),e instanceof ce){if(e.loose===!!r.loose)return e;e=e.value}if(!(this instanceof ce))return new ce(e,r);e=e.trim().split(/\s+/).join(" "),t("comparator",e,r),this.options=r,this.loose=!!r.loose,this.parse(e),this.semver===fe?this.value="":this.value=this.operator+this.semver.version,t("comp",this)}r.rcompareIdentifiers=function(e,r){return ee(r,e)},r.major=function(e,r){return new Y(e,r).major},r.minor=function(e,r){return new Y(e,r).minor},r.patch=function(e,r){return new Y(e,r).patch},r.compare=re,r.compareLoose=function(e,r){return re(e,r,!0)},r.rcompare=function(e,r,t){return re(r,e,t)},r.sort=function(e,t){return e.sort((function(e,n){return r.compare(e,n,t)}))},r.rsort=function(e,t){return e.sort((function(e,n){return r.rcompare(e,n,t)}))},r.gt=te,r.lt=ne,r.eq=oe,r.neq=ie,r.gte=ae,r.lte=se,r.cmp=ue,r.Comparator=ce;var fe={};function le(e,r){if(r&&"object"==typeof r||(r={loose:!!r,includePrerelease:!1}),e instanceof le)return e.loose===!!r.loose&&e.includePrerelease===!!r.includePrerelease?e:new le(e.raw,r);if(e instanceof ce)return new le(e.value,r);if(!(this instanceof le))return new le(e,r);if(this.options=r,this.loose=!!r.loose,this.includePrerelease=!!r.includePrerelease,this.raw=e.trim().split(/\s+/).join(" "),this.set=this.raw.split("||").map((function(e){return this.parseRange(e.trim())}),this).filter((function(e){return e.length})),!this.set.length)throw new TypeError("Invalid SemVer Range: "+this.raw);this.format()}function pe(e){return!e||"x"===e.toLowerCase()||"*"===e}function he(e,r,t,n,o,i,a,s,u,c,f,l,p){return((r=pe(t)?"":pe(n)?">="+t+".0.0":pe(o)?">="+t+"."+n+".0":">="+r)+" "+(s=pe(u)?"":pe(c)?"<"+(+u+1)+".0.0":pe(f)?"<"+u+"."+(+c+1)+".0":l?"<="+u+"."+c+"."+f+"-"+l:"<="+s)).trim()}function ve(e,r,n){for(var o=0;o0){var i=e[o].semver;if(i.major===r.major&&i.minor===r.minor&&i.patch===r.patch)return!0}return!1}return!0}function de(e,r,t){try{r=new le(r,t)}catch(e){return!1}return r.test(e)}function ye(e,r,t,n){var o,i,a,s,u;switch(e=new Y(e,n),r=new le(r,n),t){case">":o=te,i=se,a=ne,s=">",u=">=";break;case"<":o=ne,i=ae,a=te,s="<",u="<=";break;default:throw new TypeError('Must provide a hilo val of "<" or ">"')}if(de(e,r,n))return!1;for(var c=0;c=0.0.0")),l=l||e,p=p||e,o(e.semver,l.semver,n)?l=e:a(e.semver,p.semver,n)&&(p=e)})),l.operator===s||l.operator===u)return!1;if((!p.operator||p.operator===s)&&i(e,p.semver))return!1;if(p.operator===u&&a(e,p.semver))return!1}return!0}ce.prototype.parse=function(e){var r=this.options.loose?s[L]:s[G],t=e.match(r);if(!t)throw new TypeError("Invalid comparator: "+e);this.operator=t[1],"="===this.operator&&(this.operator=""),t[2]?this.semver=new Y(t[2],this.options.loose):this.semver=fe},ce.prototype.toString=function(){return this.value},ce.prototype.test=function(e){return t("Comparator.test",e,this.options.loose),this.semver===fe||("string"==typeof e&&(e=new Y(e,this.options)),ue(e,this.operator,this.semver,this.options))},ce.prototype.intersects=function(e,r){if(!(e instanceof ce))throw new TypeError("a Comparator is required");var t;if(r&&"object"==typeof r||(r={loose:!!r,includePrerelease:!1}),""===this.operator)return t=new le(e.value,r),de(this.value,t,r);if(""===e.operator)return t=new le(this.value,r),de(e.semver,t,r);var n=!(">="!==this.operator&&">"!==this.operator||">="!==e.operator&&">"!==e.operator),o=!("<="!==this.operator&&"<"!==this.operator||"<="!==e.operator&&"<"!==e.operator),i=this.semver.version===e.semver.version,a=!(">="!==this.operator&&"<="!==this.operator||">="!==e.operator&&"<="!==e.operator),s=ue(this.semver,"<",e.semver,r)&&(">="===this.operator||">"===this.operator)&&("<="===e.operator||"<"===e.operator),u=ue(this.semver,">",e.semver,r)&&("<="===this.operator||"<"===this.operator)&&(">="===e.operator||">"===e.operator);return n||o||i&&a||s||u},r.Range=le,le.prototype.format=function(){return this.range=this.set.map((function(e){return e.join(" ").trim()})).join("||").trim(),this.range},le.prototype.toString=function(){return this.range},le.prototype.parseRange=function(e){var r=this.options.loose,n=r?s[J]:s[F];e=e.replace(n,he),t("hyphen replace",e),e=e.replace(s[K],"$1$2$3"),t("comparator trim",e,s[K]),e=(e=e.replace(s[M],"$1~")).replace(s[C],"$1^");var o=r?s[L]:s[G],i=e.split(" ").map((function(e){return function(e,r){return t("comp",e,r),e=function(e,r){return e.trim().split(/\s+/).map((function(e){return function(e,r){t("caret",e,r);var n=r.loose?s[U]:s[H];return e.replace(n,(function(r,n,o,i,a){var s;return t("caret",e,r,n,o,i,a),pe(n)?s="":pe(o)?s=">="+n+".0.0 <"+(+n+1)+".0.0":pe(i)?s="0"===n?">="+n+"."+o+".0 <"+n+"."+(+o+1)+".0":">="+n+"."+o+".0 <"+(+n+1)+".0.0":a?(t("replaceCaret pr",a),s="0"===n?"0"===o?">="+n+"."+o+"."+i+"-"+a+" <"+n+"."+o+"."+(+i+1):">="+n+"."+o+"."+i+"-"+a+" <"+n+"."+(+o+1)+".0":">="+n+"."+o+"."+i+"-"+a+" <"+(+n+1)+".0.0"):(t("no pr"),s="0"===n?"0"===o?">="+n+"."+o+"."+i+" <"+n+"."+o+"."+(+i+1):">="+n+"."+o+"."+i+" <"+n+"."+(+o+1)+".0":">="+n+"."+o+"."+i+" <"+(+n+1)+".0.0"),t("caret return",s),s}))}(e,r)})).join(" ")}(e,r),t("caret",e),e=function(e,r){return e.trim().split(/\s+/).map((function(e){return function(e,r){var n=r.loose?s[D]:s[z];return e.replace(n,(function(r,n,o,i,a){var s;return t("tilde",e,r,n,o,i,a),pe(n)?s="":pe(o)?s=">="+n+".0.0 <"+(+n+1)+".0.0":pe(i)?s=">="+n+"."+o+".0 <"+n+"."+(+o+1)+".0":a?(t("replaceTilde pr",a),s=">="+n+"."+o+"."+i+"-"+a+" <"+n+"."+(+o+1)+".0"):s=">="+n+"."+o+"."+i+" <"+n+"."+(+o+1)+".0",t("tilde return",s),s}))}(e,r)})).join(" ")}(e,r),t("tildes",e),e=function(e,r){return t("replaceXRanges",e,r),e.split(/\s+/).map((function(e){return function(e,r){e=e.trim();var n=r.loose?s[I]:s[$];return e.replace(n,(function(r,n,o,i,a,s){t("xRange",e,r,n,o,i,a,s);var u=pe(o),c=u||pe(i),f=c||pe(a);return"="===n&&f&&(n=""),u?r=">"===n||"<"===n?"<0.0.0":"*":n&&f?(c&&(i=0),a=0,">"===n?(n=">=",c?(o=+o+1,i=0,a=0):(i=+i+1,a=0)):"<="===n&&(n="<",c?o=+o+1:i=+i+1),r=n+o+"."+i+"."+a):c?r=">="+o+".0.0 <"+(+o+1)+".0.0":f&&(r=">="+o+"."+i+".0 <"+o+"."+(+i+1)+".0"),t("xRange return",r),r}))}(e,r)})).join(" ")}(e,r),t("xrange",e),e=function(e,r){return t("replaceStars",e,r),e.trim().replace(s[W],"")}(e,r),t("stars",e),e}(e,this.options)}),this).join(" ").split(/\s+/);return this.options.loose&&(i=i.filter((function(e){return!!e.match(o)}))),i=i.map((function(e){return new ce(e,this.options)}),this)},le.prototype.intersects=function(e,r){if(!(e instanceof le))throw new TypeError("a Range is required");return this.set.some((function(t){return t.every((function(t){return e.set.some((function(e){return e.every((function(e){return t.intersects(e,r)}))}))}))}))},r.toComparators=function(e,r){return new le(e,r).set.map((function(e){return e.map((function(e){return e.value})).join(" ").trim().split(" ")}))},le.prototype.test=function(e){if(!e)return!1;"string"==typeof e&&(e=new Y(e,this.options));for(var r=0;r":0===r.prerelease.length?r.patch++:r.prerelease.push(0),r.raw=r.format();case"":case">=":t&&!te(t,r)||(t=r);break;case"<":case"<=":break;default:throw new Error("Unexpected operation: "+e.operator)}}))}if(t&&e.test(t))return t;return null},r.validRange=function(e,r){try{return new le(e,r).range||"*"}catch(e){return null}},r.ltr=function(e,r,t){return ye(e,r,"<",t)},r.gtr=function(e,r,t){return ye(e,r,">",t)},r.outside=ye,r.prerelease=function(e,r){var t=X(e,r);return t&&t.prerelease.length?t.prerelease:null},r.intersects=function(e,r,t){return e=new le(e,t),r=new le(r,t),e.intersects(r)},r.coerce=function(e){if(e instanceof Y)return e;if("string"!=typeof e)return null;var r=e.match(s[V]);if(null==r)return null;return X(r[1]+"."+(r[2]||"0")+"."+(r[3]||"0"))}}(0,Ke.exports);var Fe=Ke.exports.satisfies(process.version,"^6.12.0 || >=8.0.0"),Je=Pe,We=$e,Ze=Ne,Xe=Te,Ye=Ge,Qe=a,er=["RS256","RS384","RS512","ES256","ES384","ES512"],rr=["RS256","RS384","RS512"],tr=["HS256","HS384","HS512"];Fe&&(er.splice(3,0,"PS256","PS384","PS512"),rr.splice(3,0,"PS256","PS384","PS512"));var nr=1/0,or=9007199254740991,ir=17976931348623157e292,ar=NaN,sr="[object Arguments]",ur="[object Function]",cr="[object GeneratorFunction]",fr="[object String]",lr="[object Symbol]",pr=/^\s+|\s+$/g,hr=/^[-+]0x[0-9a-f]+$/i,vr=/^0b[01]+$/i,dr=/^0o[0-7]+$/i,yr=/^(?:0|[1-9]\d*)$/,mr=parseInt;function gr(e){return e!=e}function br(e,r){return function(e,r){for(var t=-1,n=e?e.length:0,o=Array(n);++t-1&&e%1==0&&e-1&&e%1==0&&e<=or}(e.length)&&!function(e){var r=$r(e)?Er.call(e):"";return r==ur||r==cr}(e)}function $r(e){var r=typeof e;return!!e&&("object"==r||"function"==r)}function Ir(e){return!!e&&"object"==typeof e}var Vr=function(e,r,t,n){var o;e=Br(e)?e:(o=e)?br(o,function(e){return Br(e)?Tr(e):kr(e)}(o)):[],t=t&&!n?function(e){var r=function(e){if(!e)return 0===e?e:0;if(e=function(e){if("number"==typeof e)return e;if(function(e){return"symbol"==typeof e||Ir(e)&&Er.call(e)==lr}(e))return ar;if($r(e)){var r="function"==typeof e.valueOf?e.valueOf():e;e=$r(r)?r+"":r}if("string"!=typeof e)return 0===e?e:+e;e=e.replace(pr,"");var t=vr.test(e);return t||dr.test(e)?mr(e.slice(2),t?2:8):hr.test(e)?ar:+e}(e),e===nr||e===-nr){return(e<0?-1:1)*ir}return e==e?e:0}(e),t=r%1;return r==r?t?r-t:r:0}(t):0;var i=e.length;return t<0&&(t=Ar(i+t,0)),function(e){return"string"==typeof e||!Rr(e)&&Ir(e)&&Er.call(e)==fr}(e)?t<=i&&e.indexOf(r,t)>-1:!!i&&function(e,r,t){if(r!=r)return function(e,r,t,n){for(var o=e.length,i=t+(n?1:-1);n?i--:++i-1},Nr=Object.prototype.toString;var Mr=function(e){return!0===e||!1===e||function(e){return!!e&&"object"==typeof e}(e)&&"[object Boolean]"==Nr.call(e)},zr=1/0,Dr=17976931348623157e292,qr=NaN,Cr="[object Symbol]",Hr=/^\s+|\s+$/g,Ur=/^[-+]0x[0-9a-f]+$/i,Lr=/^0b[01]+$/i,Gr=/^0o[0-7]+$/i,Kr=parseInt,Fr=Object.prototype.toString;function Jr(e){var r=typeof e;return!!e&&("object"==r||"function"==r)}var Wr=function(e){return"number"==typeof e&&e==function(e){var r=function(e){if(!e)return 0===e?e:0;if(e=function(e){if("number"==typeof e)return e;if(function(e){return"symbol"==typeof e||function(e){return!!e&&"object"==typeof e}(e)&&Fr.call(e)==Cr}(e))return qr;if(Jr(e)){var r="function"==typeof e.valueOf?e.valueOf():e;e=Jr(r)?r+"":r}if("string"!=typeof e)return 0===e?e:+e;e=e.replace(Hr,"");var t=Lr.test(e);return t||Gr.test(e)?Kr(e.slice(2),t?2:8):Ur.test(e)?qr:+e}(e),e===zr||e===-zr){return(e<0?-1:1)*Dr}return e==e?e:0}(e),t=r%1;return r==r?t?r-t:r:0}(e)},Zr=Object.prototype.toString;var Xr=function(e){return"number"==typeof e||function(e){return!!e&&"object"==typeof e}(e)&&"[object Number]"==Zr.call(e)};var Yr=Function.prototype,Qr=Object.prototype,et=Yr.toString,rt=Qr.hasOwnProperty,tt=et.call(Object),nt=Qr.toString,ot=function(e,r){return function(t){return e(r(t))}}(Object.getPrototypeOf,Object);var it=function(e){if(!function(e){return!!e&&"object"==typeof e}(e)||"[object Object]"!=nt.call(e)||function(e){var r=!1;if(null!=e&&"function"!=typeof e.toString)try{r=!!(e+"")}catch(e){}return r}(e))return!1;var r=ot(e);if(null===r)return!0;var t=rt.call(r,"constructor")&&r.constructor;return"function"==typeof t&&t instanceof t&&et.call(t)==tt},at=Object.prototype.toString,st=Array.isArray;var ut=function(e){return"string"==typeof e||!st(e)&&function(e){return!!e&&"object"==typeof e}(e)&&"[object String]"==at.call(e)},ct="Expected a function",ft=1/0,lt=17976931348623157e292,pt=NaN,ht="[object Symbol]",vt=/^\s+|\s+$/g,dt=/^[-+]0x[0-9a-f]+$/i,yt=/^0b[01]+$/i,mt=/^0o[0-7]+$/i,gt=parseInt,bt=Object.prototype.toString;function wt(e,r){var t;if("function"!=typeof r)throw new TypeError(ct);return e=function(e){var r=function(e){if(!e)return 0===e?e:0;if(e=function(e){if("number"==typeof e)return e;if(function(e){return"symbol"==typeof e||function(e){return!!e&&"object"==typeof e}(e)&&bt.call(e)==ht}(e))return pt;if(jt(e)){var r="function"==typeof e.valueOf?e.valueOf():e;e=jt(r)?r+"":r}if("string"!=typeof e)return 0===e?e:+e;e=e.replace(vt,"");var t=yt.test(e);return t||mt.test(e)?gt(e.slice(2),t?2:8):dt.test(e)?pt:+e}(e),e===ft||e===-ft){return(e<0?-1:1)*lt}return e==e?e:0}(e),t=r%1;return r==r?t?r-t:r:0}(e),function(){return--e>0&&(t=r.apply(this,arguments)),e<=1&&(r=void 0),t}}function jt(e){var r=typeof e;return!!e&&("object"==r||"function"==r)}var St=function(e){return wt(2,e)},_t=Ge,Et=a,xt=Vr,Ot=Mr,At=Wr,Tt=Xr,kt=it,Pt=ut,Rt=St,Bt=["RS256","RS384","RS512","ES256","ES384","ES512","HS256","HS384","HS512","none"];Fe&&Bt.splice(3,0,"PS256","PS384","PS512");var $t={expiresIn:{isValid:function(e){return At(e)||Pt(e)&&e},message:'"expiresIn" should be a number of seconds or string representing a timespan'},notBefore:{isValid:function(e){return At(e)||Pt(e)&&e},message:'"notBefore" should be a number of seconds or string representing a timespan'},audience:{isValid:function(e){return Pt(e)||Array.isArray(e)},message:'"audience" must be a string or array'},algorithm:{isValid:xt.bind(null,Bt),message:'"algorithm" must be a valid string enum value'},header:{isValid:kt,message:'"header" must be an object'},encoding:{isValid:Pt,message:'"encoding" must be a string'},issuer:{isValid:Pt,message:'"issuer" must be a string'},subject:{isValid:Pt,message:'"subject" must be a string'},jwtid:{isValid:Pt,message:'"jwtid" must be a string'},noTimestamp:{isValid:Ot,message:'"noTimestamp" must be a boolean'},keyid:{isValid:Pt,message:'"keyid" must be a string'},mutatePayload:{isValid:Ot,message:'"mutatePayload" must be a boolean'}},It={iat:{isValid:Tt,message:'"iat" should be a number of seconds'},exp:{isValid:Tt,message:'"exp" should be a number of seconds'},nbf:{isValid:Tt,message:'"nbf" should be a number of seconds'}};function Vt(e,r,t,n){if(!kt(t))throw new Error('Expected "'+n+'" to be a plain object.');Object.keys(t).forEach((function(o){var i=e[o];if(i){if(!i.isValid(t[o]))throw new Error(i.message)}else if(!r)throw new Error('"'+o+'" is not allowed in "'+n+'"')}))}var Nt={audience:"aud",issuer:"iss",subject:"sub",jwtid:"jti"},Mt=["expiresIn","notBefore","noTimestamp","audience","issuer","subject","jwtid"],zt={decode:Te,verify:function(e,r,t,n){var o;if("function"!=typeof t||n||(n=t,t={}),t||(t={}),t=Object.assign({},t),o=n||function(e,r){if(e)throw e;return r},t.clockTimestamp&&"number"!=typeof t.clockTimestamp)return o(new Je("clockTimestamp must be a number"));if(void 0!==t.nonce&&("string"!=typeof t.nonce||""===t.nonce.trim()))return o(new Je("nonce must be a non-empty string"));var i=t.clockTimestamp||Math.floor(Date.now()/1e3);if(!e)return o(new Je("jwt must be provided"));if("string"!=typeof e)return o(new Je("jwt must be a string"));var a,s=e.split(".");if(3!==s.length)return o(new Je("jwt malformed"));try{a=Xe(e,{complete:!0})}catch(e){return o(e)}if(!a)return o(new Je("invalid token"));var u,c=a.header;if("function"==typeof r){if(!n)return o(new Je("verify must be called asynchronous if secret or public key is provided as a callback"));u=r}else u=function(e,t){return t(null,r)};return u(c,(function(r,n){if(r)return o(new Je("error in secret or public key callback: "+r.message));var u,f=""!==s[2].trim();if(!f&&n)return o(new Je("jwt signature is required"));if(f&&!n)return o(new Je("secret or public key must be provided"));if(f||t.algorithms||(t.algorithms=["none"]),t.algorithms||(t.algorithms=~n.toString().indexOf("BEGIN CERTIFICATE")||~n.toString().indexOf("BEGIN PUBLIC KEY")?er:~n.toString().indexOf("BEGIN RSA PUBLIC KEY")?rr:tr),!~t.algorithms.indexOf(a.header.alg))return o(new Je("invalid algorithm"));try{u=Qe.verify(e,a.header.alg,n)}catch(e){return o(e)}if(!u)return o(new Je("invalid signature"));var l=a.payload;if(void 0!==l.nbf&&!t.ignoreNotBefore){if("number"!=typeof l.nbf)return o(new Je("invalid nbf value"));if(l.nbf>i+(t.clockTolerance||0))return o(new We("jwt not active",new Date(1e3*l.nbf)))}if(void 0!==l.exp&&!t.ignoreExpiration){if("number"!=typeof l.exp)return o(new Je("invalid exp value"));if(i>=l.exp+(t.clockTolerance||0))return o(new Ze("jwt expired",new Date(1e3*l.exp)))}if(t.audience){var p=Array.isArray(t.audience)?t.audience:[t.audience];if(!(Array.isArray(l.aud)?l.aud:[l.aud]).some((function(e){return p.some((function(r){return r instanceof RegExp?r.test(e):r===e}))})))return o(new Je("jwt audience invalid. expected: "+p.join(" or ")))}if(t.issuer&&("string"==typeof t.issuer&&l.iss!==t.issuer||Array.isArray(t.issuer)&&-1===t.issuer.indexOf(l.iss)))return o(new Je("jwt issuer invalid. expected: "+t.issuer));if(t.subject&&l.sub!==t.subject)return o(new Je("jwt subject invalid. expected: "+t.subject));if(t.jwtid&&l.jti!==t.jwtid)return o(new Je("jwt jwtid invalid. expected: "+t.jwtid));if(t.nonce&&l.nonce!==t.nonce)return o(new Je("jwt nonce invalid. expected: "+t.nonce));if(t.maxAge){if("number"!=typeof l.iat)return o(new Je("iat required when maxAge is specified"));var h=Ye(t.maxAge,l.iat);if(void 0===h)return o(new Je('"maxAge" should be a number of seconds or string representing a timespan eg: "1d", "20h", 60'));if(i>=h+(t.clockTolerance||0))return o(new Ze("maxAge exceeded",new Date(1e3*h)))}if(!0===t.complete){var v=a.signature;return o(null,{header:c,payload:l,signature:v})}return o(null,l)}))},sign:function(e,r,t,n){"function"==typeof t?(n=t,t={}):t=t||{};var o="object"==typeof e&&!Buffer.isBuffer(e),i=Object.assign({alg:t.algorithm||"HS256",typ:o?"JWT":void 0,kid:t.keyid},t.header);function a(e){if(n)return n(e);throw e}if(!r&&"none"!==t.algorithm)return a(new Error("secretOrPrivateKey must have a value"));if(void 0===e)return a(new Error("payload is required"));if(o){try{!function(e){Vt(It,!0,e,"payload")}(e)}catch(e){return a(e)}t.mutatePayload||(e=Object.assign({},e))}else{var s=Mt.filter((function(e){return void 0!==t[e]}));if(s.length>0)return a(new Error("invalid "+s.join(",")+" option for "+typeof e+" payload"))}if(void 0!==e.exp&&void 0!==t.expiresIn)return a(new Error('Bad "options.expiresIn" option the payload already has an "exp" property.'));if(void 0!==e.nbf&&void 0!==t.notBefore)return a(new Error('Bad "options.notBefore" option the payload already has an "nbf" property.'));try{!function(e){Vt($t,!1,e,"options")}(t)}catch(e){return a(e)}var u=e.iat||Math.floor(Date.now()/1e3);if(t.noTimestamp?delete e.iat:o&&(e.iat=u),void 0!==t.notBefore){try{e.nbf=_t(t.notBefore,u)}catch(e){return a(e)}if(void 0===e.nbf)return a(new Error('"notBefore" should be a number of seconds or string representing a timespan eg: "1d", "20h", 60'))}if(void 0!==t.expiresIn&&"object"==typeof e){try{e.exp=_t(t.expiresIn,u)}catch(e){return a(e)}if(void 0===e.exp)return a(new Error('"expiresIn" should be a number of seconds or string representing a timespan eg: "1d", "20h", 60'))}Object.keys(Nt).forEach((function(r){var n=Nt[r];if(void 0!==t[r]){if(void 0!==e[n])return a(new Error('Bad "options.'+r+'" option. The payload already has an "'+n+'" property.'));e[n]=t[r]}}));var c=t.encoding||"utf8";if("function"!=typeof n)return Et.sign({header:i,payload:e,secret:r,encoding:c});n=n&&Rt(n),Et.createSign({header:i,privateKey:r,payload:e,encoding:c}).once("error",n).once("done",(function(e){n(null,e)}))},JsonWebTokenError:Pe,NotBeforeError:$e,TokenExpiredError:Ne},Dt={exports:{}};!function(e,r){var t="__lodash_hash_undefined__",n=9007199254740991,i="[object Arguments]",a="[object AsyncFunction]",s="[object Function]",u="[object GeneratorFunction]",c="[object Null]",f="[object Object]",l="[object Proxy]",p="[object Undefined]",h=/^\[object .+?Constructor\]$/,v=/^(?:0|[1-9]\d*)$/,d={};d["[object Float32Array]"]=d["[object Float64Array]"]=d["[object Int8Array]"]=d["[object Int16Array]"]=d["[object Int32Array]"]=d["[object Uint8Array]"]=d["[object Uint8ClampedArray]"]=d["[object Uint16Array]"]=d["[object Uint32Array]"]=!0,d[i]=d["[object Array]"]=d["[object ArrayBuffer]"]=d["[object Boolean]"]=d["[object DataView]"]=d["[object Date]"]=d["[object Error]"]=d[s]=d["[object Map]"]=d["[object Number]"]=d[f]=d["[object RegExp]"]=d["[object Set]"]=d["[object String]"]=d["[object WeakMap]"]=!1;var y="object"==typeof o&&o&&o.Object===Object&&o,m="object"==typeof self&&self&&self.Object===Object&&self,g=y||m||Function("return this")(),b=r&&!r.nodeType&&r,w=b&&e&&!e.nodeType&&e,j=w&&w.exports===b,S=j&&y.process,_=function(){try{var e=w&&w.require&&w.require("util").types;return e||S&&S.binding&&S.binding("util")}catch(e){}}(),E=_&&_.isTypedArray;var x,O=Array.prototype,A=Function.prototype,T=Object.prototype,k=g["__core-js_shared__"],P=A.toString,R=T.hasOwnProperty,B=(x=/[^.]+$/.exec(k&&k.keys&&k.keys.IE_PROTO||""))?"Symbol(src)_1."+x:"",$=T.toString,I=P.call(Object),V=RegExp("^"+P.call(R).replace(/[\\^$.*+?()[\]{}|]/g,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$"),N=j?g.Buffer:void 0,M=g.Symbol,z=g.Uint8Array,D=N?N.allocUnsafe:void 0,q=function(e,r){return function(t){return e(r(t))}}(Object.getPrototypeOf,Object),C=Object.create,H=T.propertyIsEnumerable,U=O.splice,L=M?M.toStringTag:void 0,G=function(){try{var e=me(Object,"defineProperty");return e({},"",{}),e}catch(e){}}(),K=N?N.isBuffer:void 0,F=Math.max,J=Date.now,W=me(g,"Map"),Z=me(Object,"create"),X=function(){function e(){}return function(r){if(!ke(r))return{};if(C)return C(r);e.prototype=r;var t=new e;return e.prototype=void 0,t}}();function Y(e){var r=-1,t=null==e?0:e.length;for(this.clear();++r-1},Q.prototype.set=function(e,r){var t=this.__data__,n=ie(t,e);return n<0?(++this.size,t.push([e,r])):t[n][1]=r,this},ee.prototype.clear=function(){this.size=0,this.__data__={hash:new Y,map:new(W||Q),string:new Y}},ee.prototype.delete=function(e){var r=ye(this,e).delete(e);return this.size-=r?1:0,r},ee.prototype.get=function(e){return ye(this,e).get(e)},ee.prototype.has=function(e){return ye(this,e).has(e)},ee.prototype.set=function(e,r){var t=ye(this,e),n=t.size;return t.set(e,r),this.size+=t.size==n?0:1,this},re.prototype.clear=function(){this.__data__=new Q,this.size=0},re.prototype.delete=function(e){var r=this.__data__,t=r.delete(e);return this.size=r.size,t},re.prototype.get=function(e){return this.__data__.get(e)},re.prototype.has=function(e){return this.__data__.has(e)},re.prototype.set=function(e,r){var t=this.__data__;if(t instanceof Q){var n=t.__data__;if(!W||n.length<199)return n.push([e,r]),this.size=++t.size,this;t=this.__data__=new ee(n)}return t.set(e,r),this.size=t.size,this};var se,ue=function(e,r,t){for(var n=-1,o=Object(e),i=t(e),a=i.length;a--;){var s=i[se?a:++n];if(!1===r(o[s],s,o))break}return e};function ce(e){return null==e?void 0===e?p:c:L&&L in Object(e)?function(e){var r=R.call(e,L),t=e[L];try{e[L]=void 0;var n=!0}catch(e){}var o=$.call(e);n&&(r?e[L]=t:delete e[L]);return o}(e):function(e){return $.call(e)}(e)}function fe(e){return Pe(e)&&ce(e)==i}function le(e){return!(!ke(e)||function(e){return!!B&&B in e}(e))&&(Ae(e)?V:h).test(function(e){if(null!=e){try{return P.call(e)}catch(e){}try{return e+""}catch(e){}}return""}(e))}function pe(e){if(!ke(e))return function(e){var r=[];if(null!=e)for(var t in Object(e))r.push(t);return r}(e);var r=be(e),t=[];for(var n in e)("constructor"!=n||!r&&R.call(e,n))&&t.push(n);return t}function he(e,r,t,n,o){e!==r&&ue(r,(function(i,a){if(o||(o=new re),ke(i))!function(e,r,t,n,o,i,a){var s=we(e,t),u=we(r,t),c=a.get(u);if(c)return void ne(e,t,c);var l=i?i(s,u,t+"",e,r,a):void 0,p=void 0===l;if(p){var h=Ee(u),v=!h&&Oe(u),d=!h&&!v&&Re(u);l=u,h||v||d?Ee(s)?l=s:Pe(w=s)&&xe(w)?l=function(e,r){var t=-1,n=e.length;r||(r=Array(n));for(;++t-1&&e%1==0&&e0){if(++r>=800)return arguments[0]}else r=0;return e.apply(void 0,arguments)}}(de);function Se(e,r){return e===r||e!=e&&r!=r}var _e=fe(function(){return arguments}())?fe:function(e){return Pe(e)&&R.call(e,"callee")&&!H.call(e,"callee")},Ee=Array.isArray;function xe(e){return null!=e&&Te(e.length)&&!Ae(e)}var Oe=K||function(){return!1};function Ae(e){if(!ke(e))return!1;var r=ce(e);return r==s||r==u||r==a||r==l}function Te(e){return"number"==typeof e&&e>-1&&e%1==0&&e<=n}function ke(e){var r=typeof e;return null!=e&&("object"==r||"function"==r)}function Pe(e){return null!=e&&"object"==typeof e}var Re=E?function(e){return function(r){return e(r)}}(E):function(e){return Pe(e)&&Te(e.length)&&!!d[ce(e)]};function Be(e){return xe(e)?te(e,!0):pe(e)}var $e,Ie=($e=function(e,r,t){he(e,r,t)},ve((function(e,r){var t=-1,n=r.length,o=n>1?r[n-1]:void 0,i=n>2?r[2]:void 0;for(o=$e.length>3&&"function"==typeof o?(n--,o):void 0,i&&function(e,r,t){if(!ke(t))return!1;var n=typeof r;return!!("number"==n?xe(t)&&ge(r,t.length):"string"==n&&r in t)&&Se(t[r],e)}(r[0],r[1],i)&&(o=n<3?void 0:o,n=1),e=Object(e);++t -1) { + const val = encodeURIComponent(params[key]) + requestUrl = `${requestUrl}${requestUrl.includes('?') ? '&' : '?' + }${key}=${val}` + // 删除 postData 中对应的数据 + delete params[key] + } + } + + return { execParams: params, url: requestUrl } + } + + _getSign (method, params) { + const bizContent = params.bizContent || null + delete params.bizContent + + const signParams = Object.assign({ + method, + appId: this.options.appId, + charset: this.options.charset, + version: this.options.version, + signType: this.options.signType, + timestamp: getFullTimeStr(getOffsetDate(this.options.timeOffset)) + }, params) + + if (bizContent) { + signParams.bizContent = JSON.stringify(camel2snakeJson(bizContent)) + } + + // params key 驼峰转下划线 + const decamelizeParams = camel2snakeJson(signParams) + + // 排序 + const signStr = Object.keys(decamelizeParams) + .sort() + .map((key) => { + let data = decamelizeParams[key] + if (Array.prototype.toString.call(data) !== '[object String]') { + data = JSON.stringify(data) + } + return `${key}=${data}` + }) + .join('&') + + // 计算签名 + const sign = crypto + .createSign(ALIPAY_ALGORITHM_MAPPING[this.options.signType]) + .update(signStr, 'utf8') + .sign(this.options.privateKey, 'base64') + + return Object.assign(decamelizeParams, { sign }) + } + + async _exec (method, params = {}, option = {}) { + // 计算签名 + const signData = this._getSign(method, params) + const { url, execParams } = this._formatUrl(this.options.gateway, signData) + const { status, data } = await uniCloud.httpclient.request(url, { + method: 'POST', + data: execParams, + // 按 text 返回(为了验签) + dataType: 'text', + timeout: this.options.timeout + }) + if (status !== 200) throw new Error('request fail') + /** + * 示例响应格式 + * {"alipay_trade_precreate_response": + * {"code": "10000","msg": "Success","out_trade_no": "111111","qr_code": "https:\/\/"}, + * "sign": "abcde=" + * } + * 或者 + * {"error_response": + * {"code":"40002","msg":"Invalid Arguments","sub_code":"isv.code-invalid","sub_msg":"授权码code无效"}, + * } + */ + const result = JSON.parse(data) + const responseKey = `${method.replace(/\./g, '_')}_response` + const response = result[responseKey] + const errorResponse = result.error_response + if (response) { + // 按字符串验签 + const validateSuccess = option.validateSign ? this._checkResponseSign(data, responseKey) : true + if (validateSuccess) { + if (!response.code || response.code === '10000') { + const errCode = 0 + const errMsg = response.msg || '' + return { + errCode, + errMsg, + ...snake2camelJson(response) + } + } + const msg = response.sub_code ? `${response.sub_code} ${response.sub_msg}` : `${response.msg || 'unkonwn error'}` + throw new Error(msg) + } else { + throw new Error('check sign error') + } + } else if (errorResponse) { + throw new Error(errorResponse.sub_msg || errorResponse.msg || 'request fail') + } + + throw new Error('request fail') + } + + _checkResponseSign (signStr, responseKey) { + if (!this.options.alipayPublicKey || this.options.alipayPublicKey === '') { + console.warn('options.alipayPublicKey is empty') + // 支付宝公钥不存在时不做验签 + return true + } + + // 带验签的参数不存在时返回失败 + if (!signStr) { return false } + + // 根据服务端返回的结果截取需要验签的目标字符串 + const validateStr = this._getSignStr(signStr, responseKey) + // 服务端返回的签名 + const serverSign = JSON.parse(signStr).sign + + // 参数存在,并且是正常的结果(不包含 sub_code)时才验签 + const verifier = crypto.createVerify(ALIPAY_ALGORITHM_MAPPING[this.options.signType]) + verifier.update(validateStr, 'utf8') + return verifier.verify(this.options.alipayPublicKey, serverSign, 'base64') + } + + _getSignStr (originStr, responseKey) { + // 待签名的字符串 + let validateStr = originStr.trim() + // 找到 xxx_response 开始的位置 + const startIndex = originStr.indexOf(`${responseKey}"`) + // 找到最后一个 “"sign"” 字符串的位置(避免) + const lastIndex = originStr.lastIndexOf('"sign"') + + /** + * 删除 xxx_response 及之前的字符串 + * 假设原始字符串为 + * {"xxx_response":{"code":"10000"},"sign":"jumSvxTKwn24G5sAIN"} + * 删除后变为 + * :{"code":"10000"},"sign":"jumSvxTKwn24G5sAIN"} + */ + validateStr = validateStr.substr(startIndex + responseKey.length + 1) + + /** + * 删除最后一个 "sign" 及之后的字符串 + * 删除后变为 + * :{"code":"10000"}, + * {} 之间就是待验签的字符串 + */ + validateStr = validateStr.substr(0, lastIndex) + + // 删除第一个 { 之前的任何字符 + validateStr = validateStr.replace(/^[^{]*{/g, '{') + + // 删除最后一个 } 之后的任何字符 + validateStr = validateStr.replace(/\}([^}]*)$/g, '}') + + return validateStr + } +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/apple/account/index.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/apple/account/index.js new file mode 100644 index 0000000..52edc01 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/apple/account/index.js @@ -0,0 +1,79 @@ +const rsaPublicKeyPem = require('../rsa-public-key-pem') +const { + jwtVerify +} = require('../../../npm/index') +let authKeysCache = null + +module.exports = class Auth { + constructor (options) { + this.options = Object.assign({ + baseUrl: 'https://appleid.apple.com', + timeout: 10000 + }, options) + } + + async _fetch (url, options) { + const { baseUrl } = this.options + return uniCloud.httpclient.request(baseUrl + url, options) + } + + async verifyIdentityToken (identityToken) { + // 解密出kid,拿取key + const jwtHeader = identityToken.split('.')[0] + const { kid } = JSON.parse(Buffer.from(jwtHeader, 'base64').toString()) + let authKeys + if (authKeysCache) { + authKeys = authKeysCache + } else { + authKeys = await this.getAuthKeys() + authKeysCache = authKeys + } + const usedKey = authKeys.find(item => item.kid === kid) + + /** + * identityToken 格式 + * + * { + * iss: 'https://appleid.apple.com', + * aud: 'io.dcloud.hellouniapp', + * exp: 1610626724, + * iat: 1610540324, + * sub: '000628.30119d332d9b45a3be4a297f9391fd5c.0403', + * c_hash: 'oFfgewoG36cJX00KUbj45A', + * email: 'x2awmap99s@privaterelay.appleid.com', + * email_verified: 'true', + * is_private_email: 'true', + * auth_time: 1610540324, + * nonce_supported: true + * } + */ + const payload = jwtVerify( + identityToken, + rsaPublicKeyPem(usedKey.n, usedKey.e), + { + algorithms: usedKey.alg + } + ) + + if (payload.iss !== 'https://appleid.apple.com' || payload.aud !== this.options.bundleId) { + throw new Error('Invalid identity token') + } + + return { + openid: payload.sub, + email: payload.email, + emailVerified: payload.email_verified === 'true', + isPrivateEmail: payload.is_private_email === 'true' + } + } + + async getAuthKeys () { + const { status, data } = await this._fetch('/auth/keys', { + method: 'GET', + dataType: 'json', + timeout: this.options.timeout + }) + if (status !== 200) throw new Error('request https://appleid.apple.com/auth/keys fail') + return data.keys + } +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/apple/rsa-public-key-pem.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/apple/rsa-public-key-pem.js new file mode 100644 index 0000000..e1dbb31 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/apple/rsa-public-key-pem.js @@ -0,0 +1,64 @@ +// http://stackoverflow.com/questions/18835132/xml-to-pem-in-node-js +/* eslint-disable camelcase */ +function rsaPublicKeyPem (modulus_b64, exponent_b64) { + const modulus = Buffer.from(modulus_b64, 'base64') + const exponent = Buffer.from(exponent_b64, 'base64') + + let modulus_hex = modulus.toString('hex') + let exponent_hex = exponent.toString('hex') + + modulus_hex = prepadSigned(modulus_hex) + exponent_hex = prepadSigned(exponent_hex) + + const modlen = modulus_hex.length / 2 + const explen = exponent_hex.length / 2 + + const encoded_modlen = encodeLengthHex(modlen) + const encoded_explen = encodeLengthHex(explen) + const encoded_pubkey = '30' + + encodeLengthHex( + modlen + + explen + + encoded_modlen.length / 2 + + encoded_explen.length / 2 + 2 + ) + + '02' + encoded_modlen + modulus_hex + + '02' + encoded_explen + exponent_hex + + const der_b64 = Buffer.from(encoded_pubkey, 'hex').toString('base64') + + const pem = '-----BEGIN RSA PUBLIC KEY-----\n' + + der_b64.match(/.{1,64}/g).join('\n') + + '\n-----END RSA PUBLIC KEY-----\n' + + return pem +} + +function prepadSigned (hexStr) { + const msb = hexStr[0] + if (msb < '0' || msb > '7') { + return '00' + hexStr + } else { + return hexStr + } +} + +function toHex (number) { + const nstr = number.toString(16) + if (nstr.length % 2) return '0' + nstr + return nstr +} + +// encode ASN.1 DER length field +// if <=127, short form +// if >=128, long form +function encodeLengthHex (n) { + if (n <= 127) return toHex(n) + else { + const n_hex = toHex(n) + const length_of_length_byte = 128 + n_hex.length / 2 // 0x80+numbytes + return toHex(length_of_length_byte) + n_hex + } +} + +module.exports = rsaPublicKeyPem diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/index.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/index.js new file mode 100644 index 0000000..149c7de --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/index.js @@ -0,0 +1,36 @@ +const WxAccount = require('./weixin/account/index') +const QQAccount = require('./qq/account/index') +const AliAccount = require('./alipay/account/index') +const AppleAccount = require('./apple/account/index') + +const createApi = require('./share/create-api') + +module.exports = { + initWeixin: function () { + const oauthConfig = this.configUtils.getOauthConfig({ provider: 'weixin' }) + return createApi(WxAccount, { + appId: oauthConfig.appid, + secret: oauthConfig.appsecret + }) + }, + initQQ: function () { + const oauthConfig = this.configUtils.getOauthConfig({ provider: 'qq' }) + return createApi(QQAccount, { + appId: oauthConfig.appid, + secret: oauthConfig.appsecret + }) + }, + initAlipay: function () { + const oauthConfig = this.configUtils.getOauthConfig({ provider: 'alipay' }) + return createApi(AliAccount, { + appId: oauthConfig.appid, + privateKey: oauthConfig.privateKey + }) + }, + initApple: function () { + const oauthConfig = this.configUtils.getOauthConfig({ provider: 'apple' }) + return createApi(AppleAccount, { + bundleId: oauthConfig.bundleId + }) + } +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/qq/account/index.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/qq/account/index.js new file mode 100644 index 0000000..9b4879a --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/qq/account/index.js @@ -0,0 +1,97 @@ +const { + UniCloudError +} = require('../../../../common/error') +const { + resolveUrl +} = require('../../../../common/utils') +const { + callQQOpenApi +} = require('../normalize') + +module.exports = class Auth { + constructor (options) { + this.options = Object.assign({ + baseUrl: 'https://graph.qq.com', + timeout: 5000 + }, options) + } + + async _requestQQOpenapi ({ name, url, data, options }) { + const defaultOptions = { + method: 'GET', + dataType: 'json', + dataAsQueryString: true, + timeout: this.options.timeout + } + const result = await callQQOpenApi({ + name: `auth.${name}`, + url: resolveUrl(this.options.baseUrl, url), + data, + options, + defaultOptions + }) + return result + } + + async getUserInfo ({ + accessToken, + openid + } = {}) { + const url = '/user/get_user_info' + const result = await this._requestQQOpenapi({ + name: 'getUserInfo', + url, + data: { + oauthConsumerKey: this.options.appId, + accessToken, + openid + } + }) + return { + nickname: result.nickname, + avatar: result.figureurl_qq_1 + } + } + + async getOpenidByToken ({ + accessToken + } = {}) { + const url = '/oauth2.0/me' + const result = await this._requestQQOpenapi({ + name: 'getOpenidByToken', + url, + data: { + accessToken, + unionid: 1, + fmt: 'json' + } + }) + if (result.clientId !== this.options.appId) { + throw new UniCloudError({ + code: 'APPID_NOT_MATCH', + message: 'appid not match' + }) + } + return { + openid: result.openid, + unionid: result.unionid + } + } + + async code2Session ({ + code + } = {}) { + const url = 'https://api.q.qq.com/sns/jscode2session' + const result = await this._requestQQOpenapi({ + name: 'getOpenidByToken', + url, + data: { + grant_type: 'authorization_code', + appid: this.options.appId, + secret: this.options.secret, + js_code: code + } + }) + return result + } +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/qq/account/protocol.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/qq/account/protocol.js new file mode 100644 index 0000000..e69de29 diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/qq/normalize.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/qq/normalize.js new file mode 100644 index 0000000..fcfdc1e --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/qq/normalize.js @@ -0,0 +1,85 @@ +const { + UniCloudError +} = require('../../../common/error') +const { + camel2snakeJson, + snake2camelJson +} = require('../../../common/utils') + +function generateApiResult (apiName, data) { + if (data.ret || data.error) { + // 这三种都是qq的错误码规范 + const code = data.ret || data.error || data.errcode || -2 + const message = data.msg || data.error_description || data.errmsg || `${apiName} fail` + throw new UniCloudError({ + code, + message + }) + } else { + delete data.ret + delete data.msg + delete data.error + delete data.error_description + delete data.errcode + delete data.errmsg + return { + ...data, + errMsg: `${apiName} ok`, + errCode: 0 + } + } +} + +function nomalizeError (apiName, error) { + throw new UniCloudError({ + code: error.code || -2, + message: error.message || `${apiName} fail` + }) +} + +async function callQQOpenApi ({ + name, + url, + data, + options, + defaultOptions +}) { + options = Object.assign({}, defaultOptions, options, { data: camel2snakeJson(Object.assign({}, data)) }) + let result + try { + result = await uniCloud.httpclient.request(url, options) + } catch (e) { + return nomalizeError(name, e) + } + let resData = result.data + const contentType = result.headers['content-type'] + if ( + Buffer.isBuffer(resData) && + (contentType.indexOf('text/plain') === 0 || + contentType.indexOf('application/json') === 0) + ) { + try { + resData = JSON.parse(resData.toString()) + } catch (e) { + resData = resData.toString() + } + } else if (Buffer.isBuffer(resData)) { + resData = { + buffer: resData, + contentType + } + } + return snake2camelJson( + generateApiResult( + name, + resData || { + errCode: -2, + errMsg: 'Request failed' + } + ) + ) +} + +module.exports = { + callQQOpenApi +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/share/create-api.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/share/create-api.js new file mode 100644 index 0000000..c58f1e8 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/share/create-api.js @@ -0,0 +1,73 @@ +const { + isFn, + isPlainObject +} = require('../../../common/utils') + +// 注意:不进行递归处理 +function parseParams (params = {}, rule) { + if (!rule || !params) { + return params + } + const internalKeys = ['_pre', '_purify', '_post'] + // 转换之前的处理 + if (rule._pre) { + params = rule._pre(params) + } + // 净化参数 + let purify = { shouldDelete: new Set([]) } + if (rule._purify) { + const _purify = rule._purify + for (const purifyKey in _purify) { + _purify[purifyKey] = new Set(_purify[purifyKey]) + } + purify = Object.assign(purify, _purify) + } + if (isPlainObject(rule)) { + for (const key in rule) { + const parser = rule[key] + if (isFn(parser) && internalKeys.indexOf(key) === -1) { + params[key] = parser(params) + } else if (typeof parser === 'string' && internalKeys.indexOf(key) === -1) { + // 直接转换属性名称的删除旧属性名 + params[key] = params[parser] + purify.shouldDelete.add(parser) + } + } + } else if (isFn(rule)) { + params = rule(params) + } + + if (purify.shouldDelete) { + for (const item of purify.shouldDelete) { + delete params[item] + } + } + + // 转换之后的处理 + if (rule._post) { + params = rule._post(params) + } + + return params +} + +function createApi (ApiClass, options) { + const apiInstance = new ApiClass(options) + return new Proxy(apiInstance, { + get: function (obj, prop) { + if (typeof obj[prop] === 'function' && prop.indexOf('_') !== 0 && obj._protocols && obj._protocols[prop]) { + const protocol = obj._protocols[prop] + return async function (params) { + params = parseParams(params, protocol.args) + let result = await obj[prop](params) + result = parseParams(result, protocol.returnValue) + return result + } + } else { + return obj[prop] + } + } + }) +} + +module.exports = createApi diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/weixin/account/index.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/weixin/account/index.js new file mode 100644 index 0000000..7ecea84 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/weixin/account/index.js @@ -0,0 +1,111 @@ +const { + callWxOpenApi, + buildUrl +} = require('../normalize') + +module.exports = class Auth { + constructor (options) { + this.options = Object.assign({ + baseUrl: 'https://api.weixin.qq.com', + timeout: 5000 + }, options) + } + + async _requestWxOpenapi ({ name, url, data, options }) { + const defaultOptions = { + method: 'GET', + dataType: 'json', + dataAsQueryString: true, + timeout: this.options.timeout + } + const result = await callWxOpenApi({ + name: `auth.${name}`, + url: `${this.options.baseUrl}${buildUrl(url, data)}`, + data, + options, + defaultOptions + }) + return result + } + + async code2Session (code) { + const url = '/sns/jscode2session' + const result = await this._requestWxOpenapi({ + name: 'code2Session', + url, + data: { + grant_type: 'authorization_code', + appid: this.options.appId, + secret: this.options.secret, + js_code: code + } + }) + return result + } + + async getOauthAccessToken (code) { + const url = '/sns/oauth2/access_token' + const result = await this._requestWxOpenapi({ + name: 'getOauthAccessToken', + url, + data: { + grant_type: 'authorization_code', + appid: this.options.appId, + secret: this.options.secret, + code + } + }) + if (result.expiresIn) { + result.expired = Date.now() + result.expiresIn * 1000 + // delete result.expiresIn + } + return result + } + + async getUserInfo ({ + accessToken, + openid + } = {}) { + const url = '/sns/userinfo' + const { + nickname, + headimgurl: avatar + } = await this._requestWxOpenapi({ + name: 'getUserInfo', + url, + data: { + accessToken, + openid, + appid: this.options.appId, + secret: this.options.secret, + scope: 'snsapi_userinfo' + } + }) + return { + nickname, + avatar + } + } + + async getPhoneNumber (accessToken, code) { + const url = `/wxa/business/getuserphonenumber?access_token=${accessToken}` + const { phoneInfo } = await this._requestWxOpenapi({ + name: 'getPhoneNumber', + url, + data: { + code + }, + options: { + method: 'POST', + dataAsQueryString: false, + headers: { + 'content-type': 'application/json' + } + } + }) + + return { + purePhoneNumber: phoneInfo.purePhoneNumber + } + } +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/weixin/normalize.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/weixin/normalize.js new file mode 100644 index 0000000..908d916 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/weixin/normalize.js @@ -0,0 +1,95 @@ +const { + UniCloudError +} = require('../../../common/error') +const { + camel2snakeJson, snake2camelJson +} = require('../../../common/utils') + +function generateApiResult (apiName, data) { + if (data.errcode) { + throw new UniCloudError({ + code: data.errcode || -2, + message: data.errmsg || `${apiName} fail` + }) + } else { + delete data.errcode + delete data.errmsg + return { + ...data, + errMsg: `${apiName} ok`, + errCode: 0 + } + } +} + +function nomalizeError (apiName, error) { + throw new UniCloudError({ + code: error.code || -2, + message: error.message || `${apiName} fail` + }) +} + +// 微信openapi接口接收蛇形(snake case)参数返回蛇形参数,这里进行转化,如果是formdata里面的参数需要在对应api实现时就转为蛇形 +async function callWxOpenApi ({ + name, + url, + data, + options, + defaultOptions +}) { + let result = {} + // 获取二维码的接口wxacode.get和wxacode.getUnlimited不可以传入access_token(可能有其他接口也不可以),否则会返回data format error + const dataCopy = camel2snakeJson(Object.assign({}, data)) + if (dataCopy && dataCopy.access_token) { + delete dataCopy.access_token + } + try { + options = Object.assign({}, defaultOptions, options, { data: dataCopy }) + result = await uniCloud.httpclient.request(url, options) + } catch (e) { + return nomalizeError(name, e) + } + + // 有几个接口成功返回buffer失败返回json,对这些接口统一成返回buffer,然后分别解析 + let resData = result.data + const contentType = result.headers['content-type'] + if ( + Buffer.isBuffer(resData) && + (contentType.indexOf('text/plain') === 0 || + contentType.indexOf('application/json') === 0) + ) { + try { + resData = JSON.parse(resData.toString()) + } catch (e) { + resData = resData.toString() + } + } else if (Buffer.isBuffer(resData)) { + resData = { + buffer: resData, + contentType + } + } + return snake2camelJson( + generateApiResult( + name, + resData || { + errCode: -2, + errMsg: 'Request failed' + } + ) + ) +} + +function buildUrl (url, data) { + let query = '' + if (data && data.accessToken) { + const divider = url.indexOf('?') > -1 ? '&' : '?' + query = `${divider}access_token=${data.accessToken}` + } + return `${url}${query}` +} + +module.exports = { + callWxOpenApi, + buildUrl +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/weixin/utils.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/weixin/utils.js new file mode 100644 index 0000000..c141016 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/third-party/weixin/utils.js @@ -0,0 +1,87 @@ +const crypto = require('crypto') +const { + isPlainObject +} = require('../../../common/utils') + +// 退款通知解密key=md5(key) +function decryptData (encryptedData, key, iv = '') { + // 解密 + const decipher = crypto.createDecipheriv('aes-128-cbc', key, iv) + // 设置自动 padding 为 true,删除填充补位 + decipher.setAutoPadding(true) + let decoded = decipher.update(encryptedData, 'base64', 'utf8') + decoded += decipher.final('utf8') + return decoded +} + +function md5 (str, encoding = 'utf8') { + return crypto + .createHash('md5') + .update(str, encoding) + .digest('hex') +} + +function sha256 (str, key, encoding = 'utf8') { + return crypto + .createHmac('sha256', key) + .update(str, encoding) + .digest('hex') +} + +function getSignStr (obj) { + return Object.keys(obj) + .filter(key => key !== 'sign' && obj[key] !== undefined && obj[key] !== '') + .sort() + .map(key => key + '=' + obj[key]) + .join('&') +} + +function getNonceStr (length = 16) { + let str = '' + while (str.length < length) { + str += Math.random().toString(32).substring(2) + } + return str.substring(0, length) +} + +// 简易版Object转XML,只可在微信支付时使用,不支持嵌套 +function buildXML (obj, rootName = 'xml') { + const content = Object.keys(obj).map(item => { + if (isPlainObject(obj[item])) { + return `<${item}>` + } else { + return `<${item}>` + } + }) + return `<${rootName}>${content.join('')}` +} + +function isXML (str) { + const reg = /^(<\?xml.*\?>)?(\r?\n)*(.|\r?\n)*<\/xml>$/i + return reg.test(str.trim()) +}; + +// 简易版XML转Object,只可在微信支付时使用,不支持嵌套 +function parseXML (xml) { + const xmlReg = /<(?:xml|root).*?>([\s|\S]*)<\/(?:xml|root)>/ + const str = xmlReg.exec(xml)[1] + const obj = {} + const nodeReg = /<(.*?)>(?:){0,1}<\/.*?>/g + let matches = null + // eslint-disable-next-line no-cond-assign + while ((matches = nodeReg.exec(str))) { + obj[matches[1]] = matches[2] + } + return obj +} + +module.exports = { + decryptData, + md5, + sha256, + getSignStr, + getNonceStr, + buildXML, + parseXML, + isXML +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/account.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/account.js new file mode 100644 index 0000000..1fd25f0 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/account.js @@ -0,0 +1,98 @@ +const { + dbCmd, + userCollection +} = require('../../common/constants') +const { + USER_IDENTIFIER +} = require('../../common/constants') +const { + batchFindObjctValue, + getType, + isMatchUserApp +} = require('../../common/utils') + +/** + * 查询满足条件的用户 + * @param {Object} params + * @param {Object} params.userQuery 用户唯一标识组成的查询条件 + * @param {Object} params.authorizedApp 用户允许登录的应用 + * @returns userMatched 满足条件的用户列表 + */ +async function findUser (params = {}) { + const { + userQuery, + authorizedApp = [] + } = params + const condition = getUserQueryCondition(userQuery) + if (condition.length === 0) { + throw new Error('Invalid user query') + } + const authorizedAppType = getType(authorizedApp) + if (authorizedAppType !== 'string' && authorizedAppType !== 'array') { + throw new Error('Invalid authorized app') + } + + let finalQuery + + if (condition.length === 1) { + finalQuery = condition[0] + } else { + finalQuery = dbCmd.or(condition) + } + const userQueryRes = await userCollection.where(finalQuery).get() + return { + total: userQueryRes.data.length, + userMatched: userQueryRes.data.filter(item => { + return isMatchUserApp(item.dcloud_appid, authorizedApp) + }) + } +} + +function getUserIdentifier (userRecord = {}) { + const keys = Object.keys(USER_IDENTIFIER) + return batchFindObjctValue(userRecord, keys) +} + +function getUserQueryCondition (userRecord = {}) { + const userIdentifier = getUserIdentifier(userRecord) + const condition = [] + for (const key in userIdentifier) { + const value = userIdentifier[key] + if (!value) { + // 过滤所有value为假值的条件,在查询用户时没有意义 + continue + } + const queryItem = { + [key]: value + } + // 为兼容用户老数据用户名及邮箱需要同时查小写及原始大小写数据 + if (key === 'mobile') { + queryItem.mobile_confirmed = 1 + } else if (key === 'email') { + queryItem.email_confirmed = 1 + const email = userIdentifier.email + if (email.toLowerCase() !== email) { + condition.push({ + email: email.toLowerCase(), + email_confirmed: 1 + }) + } + } else if (key === 'username') { + const username = userIdentifier.username + if (username.toLowerCase() !== username) { + condition.push({ + username: username.toLowerCase() + }) + } + } else if (key === 'identities') { + queryItem.identities = dbCmd.elemMatch(value) + } + condition.push(queryItem) + } + return condition +} + +module.exports = { + findUser, + getUserIdentifier +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/captcha.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/captcha.js new file mode 100644 index 0000000..0dd620e --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/captcha.js @@ -0,0 +1,76 @@ +const { + ERROR +} = require('../../common/error') + +async function getNeedCaptcha ({ + uid, + username, + mobile, + email, + type = 'login', + limitDuration = 7200000, // 两小时 + limitTimes = 3 // 记录次数 +} = {}) { + const db = uniCloud.database() + const dbCmd = db.command + // 当用户最近“2小时内(limitDuration)”登录失败达到3次(limitTimes)时。要求用户提交验证码 + const now = Date.now() + const uniIdLogCollection = db.collection('uni-id-log') + const userIdentifier = { + user_id: uid, + username, + mobile, + email + } + + let totalKey = 0; let deleteKey = 0 + for (const key in userIdentifier) { + totalKey++ + if (!userIdentifier[key] || typeof userIdentifier[key] !== 'string') { + deleteKey++ + delete userIdentifier[key] + } + } + + if (deleteKey === totalKey) { + throw new Error('System error') // 正常情况下不会进入此条件,但是考虑到后续会有其他开发者修改此云对象,在此处做一个判断 + } + + const { + data: recentRecord + } = await uniIdLogCollection.where({ + ip: this.getUniversalClientInfo().clientIP, + ...userIdentifier, + type, + create_date: dbCmd.gt(now - limitDuration) + }) + .orderBy('create_date', 'desc') + .limit(limitTimes) + .get() + return recentRecord.length === limitTimes && recentRecord.every(item => item.state === 0) +} + +async function verifyCaptcha (params = {}) { + const { + captcha, + scene + } = params + if (!captcha) { + throw { + errCode: ERROR.CAPTCHA_REQUIRED + } + } + const payload = await this.uniCaptcha.verify({ + deviceId: this.getUniversalClientInfo().deviceId, + captcha, + scene + }) + if (payload.errCode) { + throw payload + } +} + +module.exports = { + getNeedCaptcha, + verifyCaptcha +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/config.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/config.js new file mode 100644 index 0000000..5526081 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/config.js @@ -0,0 +1,137 @@ +const { + getWeixinPlatform +} = require('./weixin') +const createConfig = require('uni-config-center') + +const requiredConfig = { + 'web.weixin-h5': ['appid', 'appsecret'], + 'web.weixin-web': ['appid', 'appsecret'], + 'app.weixin': ['appid', 'appsecret'], + 'mp-weixin.weixin': ['appid', 'appsecret'], + 'app.qq': ['appid', 'appsecret'], + 'mp-alipay.alipay': ['appid', 'privateKey'], + 'app.apple': ['bundleId'] +} + +const uniIdConfig = createConfig({ + pluginId: 'uni-id' +}) + +class ConfigUtils { + constructor({ + context + } = {}) { + this.context = context + this.clientInfo = context.getUniversalClientInfo() + const { + appId, + uniPlatform + } = this.clientInfo + this.appId = appId + switch (uniPlatform) { + case 'app': + case 'app-plus': + case 'app-android': + case 'app-ios': + this.platform = 'app' + break + case 'web': + case 'h5': + this.platform = 'web' + break + default: + this.platform = uniPlatform + break + } + } + + getConfigArray() { + let configContent + try { + configContent = require('uni-config-center/uni-id/config.json') + } catch (error) { + throw new Error('Invalid config file\n' + error.message) + } + if (configContent[0]) { + return Object.values(configContent) + } + configContent.isDefaultConfig = true + return [configContent] + } + + getAppConfig() { + const configArray = this.getConfigArray() + return configArray.find(item => item.dcloudAppid === this.appId) || configArray.find(item => item.isDefaultConfig) + } + + getPlatformConfig() { + const appConfig = this.getAppConfig() + if (!appConfig) { + throw new Error( + `Config for current app (${this.appId}) was not found, please check your config file or client appId`) + } + const platform = this.platform + if ( + (this.platform === 'app' && appConfig['app-plus']) || + (this.platform === 'web' && appConfig.h5) + ) { + throw new Error( + `Client platform is ${this.platform}, but ${this.platform === 'web' ? 'h5' : 'app-plus'} was found in config. Please refer to: https://uniapp.dcloud.net.cn/uniCloud/uni-id-summary?id=m-to-co` + ) + } + + const defaultConfig = { + tokenExpiresIn: 7200, + tokenExpiresThreshold: 1200, + passwordErrorLimit: 6, + passwordErrorRetryTime: 3600 + } + return Object.assign(defaultConfig, appConfig, appConfig[platform]) + } + + getOauthProvider({ + provider + } = {}) { + const clientPlatform = this.platform + let oatuhProivder = provider + if (provider === 'weixin' && clientPlatform === 'web') { + const weixinPlatform = getWeixinPlatform.call(this.context) + if (weixinPlatform === 'h5' || weixinPlatform === 'web') { + oatuhProivder = 'weixin-' + weixinPlatform // weixin-h5 公众号,weixin-web pc端 + } + } + return oatuhProivder + } + + getOauthConfig({ + provider + } = {}) { + const config = this.getPlatformConfig() + const clientPlatform = this.platform + const oatuhProivder = this.getOauthProvider({ + provider + }) + const requireConfigKey = requiredConfig[`${clientPlatform}.${oatuhProivder}`] || [] + if (!config.oauth || !config.oauth[oatuhProivder]) { + throw new Error(`Config param required: ${clientPlatform}.oauth.${oatuhProivder}`) + } + const oauthConfig = config.oauth[oatuhProivder] + requireConfigKey.forEach((item) => { + if (!oauthConfig[item]) { + throw new Error(`Config param required: ${clientPlatform}.oauth.${oatuhProivder}.${item}`) + } + }) + return oauthConfig + } + + getHooks() { + if (uniIdConfig.hasFile('hooks/index.js')) { + return require( + uniIdConfig.resolve('hooks/index.js') + ) + } + return {} + } +} + +module.exports = ConfigUtils \ No newline at end of file diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/fission.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/fission.js new file mode 100644 index 0000000..84233c3 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/fission.js @@ -0,0 +1,192 @@ +const { + dbCmd, + userCollection +} = require('../../common/constants') +const { + ERROR +} = require('../../common/error') +/** + * 获取随机邀请码,邀请码由大写字母加数字组成,由于存在手动输入邀请码的场景,从可选字符中去除 0、1、I、O + * @param {number} len 邀请码长度,默认6位 + * @returns {string} 随机邀请码 + */ +function getRandomInviteCode (len = 6) { + const charArr = ['2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'] + let code = '' + for (let i = 0; i < len; i++) { + code += charArr[Math.floor(Math.random() * charArr.length)] + } + return code +} + +/** + * 获取可用的邀请码,至多尝试十次以获取可用邀请码。从10亿可选值中随机,碰撞概率较低 + * 也有其他方案可以尝试,比如在数据库内设置一个从0开始计数的数字,每次调用此方法时使用updateAndReturn使数字加1并返回加1后的值,根据这个值生成对应的邀请码,比如(22222A + 1 == 22222B),此方式性能理论更好,但是不适用于旧项目 + * @param {object} param + * @param {string} param.inviteCode 初始随机邀请码 + */ +async function getValidInviteCode () { + let retry = 10 + let code + let codeValid = false + while (retry > 0 && !codeValid) { + retry-- + code = getRandomInviteCode() + const getUserRes = await userCollection.where({ + my_invite_code: code + }).limit(1).get() + if (getUserRes.data.length === 0) { + codeValid = true + break + } + } + if (!codeValid) { + throw { + errCode: ERROR.SET_INVITE_CODE_FAILED + } + } + return code +} + +/** + * 根据邀请码查询邀请人 + * @param {object} param + * @param {string} param.inviteCode 邀请码 + * @param {string} param.queryUid 受邀人id,非空时校验不可被下家或自己邀请 + * @returns + */ +async function findUserByInviteCode ({ + inviteCode, + queryUid +} = {}) { + if (typeof inviteCode !== 'string') { + throw { + errCode: ERROR.SYSTEM_ERROR + } + } + // 根据邀请码查询邀请人 + let getInviterRes + if (queryUid) { + getInviterRes = await userCollection.where({ + _id: dbCmd.neq(queryUid), + inviter_uid: dbCmd.not(dbCmd.all([queryUid])), + my_invite_code: inviteCode + }).get() + } else { + getInviterRes = await userCollection.where({ + my_invite_code: inviteCode + }).get() + } + if (getInviterRes.data.length > 1) { + // 正常情况下不可能进入此条件,以防用户自行修改数据库出错,在此做出判断 + throw { + errCode: ERROR.SYSTEM_ERROR + } + } + const inviterRecord = getInviterRes.data[0] + if (!inviterRecord) { + throw { + errCode: ERROR.INVALID_INVITE_CODE + } + } + return inviterRecord +} + +/** + * 根据邀请码生成邀请信息 + * @param {object} param + * @param {string} param.inviteCode 邀请码 + * @param {string} param.queryUid 受邀人id,非空时校验不可被下家或自己邀请 + * @returns + */ +async function generateInviteInfo ({ + inviteCode, + queryUid +} = {}) { + const inviterRecord = await findUserByInviteCode({ + inviteCode, + queryUid + }) + // 倒叙拼接当前用户邀请链 + const inviterUid = inviterRecord.inviter_uid || [] + inviterUid.unshift(inviterRecord._id) + return { + inviterUid, + inviteTime: Date.now() + } +} + +/** + * 检查当前用户是否可以接受邀请,如果可以返回用户记录 + * @param {string} uid + */ +async function checkInviteInfo (uid) { + // 检查当前用户是否已有邀请人 + const getUserRes = await userCollection.doc(uid).field({ + my_invite_code: true, + inviter_uid: true + }).get() + const userRecord = getUserRes.data[0] + if (!userRecord) { + throw { + errCode: ERROR.ACCOUNT_NOT_EXISTS + } + } + if (userRecord.inviter_uid && userRecord.inviter_uid.length > 0) { + throw { + errCode: ERROR.CHANGE_INVITER_FORBIDDEN + } + } + return userRecord +} + +/** + * 指定用户接受邀请码邀请 + * @param {object} param + * @param {string} param.uid 用户uid + * @param {string} param.inviteCode 邀请人的邀请码 + * @returns + */ +async function acceptInvite ({ + uid, + inviteCode +} = {}) { + await checkInviteInfo(uid) + const { + inviterUid, + inviteTime + } = await generateInviteInfo({ + inviteCode, + queryUid: uid + }) + + if (inviterUid === uid) { + throw { + errCode: ERROR.INVALID_INVITE_CODE + } + } + + // 更新当前用户的邀请人信息 + await userCollection.doc(uid).update({ + inviter_uid: inviterUid, + invite_time: inviteTime + }) + + // 更新当前用户邀请的用户的邀请人信息,这步可能较为耗时 + await userCollection.where({ + inviter_uid: uid + }).update({ + inviter_uid: dbCmd.push(inviterUid) + }) + + return { + errCode: 0, + errMsg: '' + } +} + +module.exports = { + acceptInvite, + generateInviteInfo, + getValidInviteCode +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/login.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/login.js new file mode 100644 index 0000000..ea8532d --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/login.js @@ -0,0 +1,246 @@ +const { + findUser +} = require('./account') +const { + userCollection, + LOG_TYPE +} = require('../../common/constants') +const { + ERROR +} = require('../../common/error') +const { + logout +} = require('./logout') +const PasswordUtils = require('./password') + +async function realPreLogin (params = {}) { + const { + user + } = params + const appId = this.getUniversalClientInfo().appId + const { + total, + userMatched + } = await findUser({ + userQuery: user, + authorizedApp: appId + }) + if (userMatched.length === 0) { + if (total > 0) { + throw { + errCode: ERROR.ACCOUNT_NOT_EXISTS_IN_CURRENT_APP + } + } + throw { + errCode: ERROR.ACCOUNT_NOT_EXISTS + } + } else if (userMatched.length > 1) { + throw { + errCode: ERROR.ACCOUNT_CONFLICT + } + } + const userRecord = userMatched[0] + checkLoginUserRecord(userRecord) + return userRecord +} + +async function preLogin (params = {}) { + const { + user + } = params + try { + const user = await realPreLogin.call(this, params) + return user + } catch (error) { + await this.middleware.uniIdLog({ + success: false, + data: user, + type: LOG_TYPE.LOGIN + }) + throw error + } +} + +async function preLoginWithPassword (params = {}) { + const { + user, + password + } = params + try { + const userRecord = await realPreLogin.call(this, params) + const { + passwordErrorLimit, + passwordErrorRetryTime + } = this.config + const { + clientIP + } = this.getUniversalClientInfo() + // 根据ip地址,密码错误次数过多,锁定登录 + let loginIPLimit = userRecord.login_ip_limit || [] + // 清理无用记录 + loginIPLimit = loginIPLimit.filter(item => item.last_error_time > Date.now() - passwordErrorRetryTime * 1000) + let currentIPLimit = loginIPLimit.find(item => item.ip === clientIP) + if (currentIPLimit && currentIPLimit.error_times >= passwordErrorLimit) { + throw { + errCode: ERROR.PASSWORD_ERROR_EXCEED_LIMIT + } + } + const passwordUtils = new PasswordUtils({ + userRecord, + clientInfo: this.getUniversalClientInfo(), + passwordSecret: this.config.passwordSecret + }) + + const { + success: checkPasswordSuccess, + refreshPasswordInfo + } = passwordUtils.checkUserPassword({ + password + }) + if (!checkPasswordSuccess) { + // 更新用户ip对应的密码错误记录 + if (!currentIPLimit) { + currentIPLimit = { + ip: clientIP, + error_times: 1, + last_error_time: Date.now() + } + loginIPLimit.push(currentIPLimit) + } else { + currentIPLimit.error_times++ + currentIPLimit.last_error_time = Date.now() + } + await userCollection.doc(userRecord._id).update({ + login_ip_limit: loginIPLimit + }) + throw { + errCode: ERROR.PASSWORD_ERROR + } + } + const extraData = {} + if (refreshPasswordInfo) { + extraData.password = refreshPasswordInfo.passwordHash + extraData.password_secret_version = refreshPasswordInfo.version + } + + const currentIPLimitIndex = loginIPLimit.indexOf(currentIPLimit) + if (currentIPLimitIndex > -1) { + loginIPLimit.splice(currentIPLimitIndex, 1) + } + extraData.login_ip_limit = loginIPLimit + return { + user: userRecord, + extraData + } + } catch (error) { + await this.middleware.uniIdLog({ + success: false, + data: user, + type: LOG_TYPE.LOGIN + }) + throw error + } +} + +function checkLoginUserRecord (user) { + switch (user.status) { + case undefined: + case 0: + break + case 1: + throw { + errCode: ERROR.ACCOUNT_BANNED + } + case 2: + throw { + errCode: ERROR.ACCOUNT_AUDITING + } + case 3: + throw { + errCode: ERROR.ACCOUNT_AUDIT_FAILED + } + case 4: + throw { + errCode: ERROR.ACCOUNT_CLOSED + } + default: + break + } +} + +async function thirdPartyLogin (params = {}) { + const { + user + } = params + return { + mobileConfirmed: !!user.mobile_confirmed, + emailConfirmed: !!user.email_confirmed + } +} + +async function postLogin (params = {}) { + const { + user, + extraData, + isThirdParty = false + } = params + const { + clientIP + } = this.getUniversalClientInfo() + const uniIdToken = this.getUniversalUniIdToken() + const uid = user._id + const updateData = { + last_login_date: Date.now(), + last_login_ip: clientIP, + ...extraData + } + const createTokenRes = await this.uniIdCommon.createToken({ + uid + }) + + const { + errCode, + token, + tokenExpired + } = createTokenRes + if (errCode) { + throw createTokenRes + } + + if (uniIdToken) { + try { + await logout.call(this) + } catch (error) {} + } + + await userCollection.doc(uid).update(updateData) + await this.middleware.uniIdLog({ + data: { + user_id: uid + }, + type: LOG_TYPE.LOGIN + }) + return { + errCode: 0, + newToken: { + token, + tokenExpired + }, + uid, + ...( + isThirdParty + ? thirdPartyLogin({ + user + }) + : {} + ), + passwordConfirmed: !!user.password + } +} + +module.exports = { + preLogin, + postLogin, + checkLoginUserRecord, + preLoginWithPassword +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/logout.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/logout.js new file mode 100644 index 0000000..ddcbb97 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/logout.js @@ -0,0 +1,49 @@ +const { + dbCmd, + LOG_TYPE, + deviceCollection, + userCollection +} = require('../../common/constants') + +async function logout () { + const { + deviceId + } = this.getUniversalClientInfo() + const uniIdToken = this.getUniversalUniIdToken() + const payload = await this.uniIdCommon.checkToken( + uniIdToken, + { + autoRefresh: false + } + ) + if (payload.errCode) { + throw payload + } + const uid = payload.uid + + // 删除token + await userCollection.doc(uid).update({ + token: dbCmd.pull(uniIdToken) + }) + + // 仅当device表的device_id和user_id均对应时才进行更新 + await deviceCollection.where({ + device_id: deviceId, + user_id: uid + }).update({ + token_expired: 0 + }) + await this.middleware.uniIdLog({ + data: { + user_id: uid + }, + type: LOG_TYPE.LOGOUT + }) + return { + errCode: 0 + } +} + +module.exports = { + logout +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/password.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/password.js new file mode 100644 index 0000000..0e46757 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/password.js @@ -0,0 +1,261 @@ +const { + getType +} = require('../../common/utils') +const crypto = require('crypto') +const createConfig = require('uni-config-center') +const shareConfig = createConfig({ + pluginId: 'uni-id' +}) +let customPassword = {} +if (shareConfig.hasFile('custom-password.js')) { + customPassword = shareConfig.requireFile('custom-password.js') || {} +} + +const passwordAlgorithmMap = { + UNI_ID_HMAC_SHA1: 'hmac-sha1', + UNI_ID_HMAC_SHA256: 'hmac-sha256', + UNI_ID_CUSTOM: 'custom' +} + +const passwordAlgorithmKeyMap = Object.keys(passwordAlgorithmMap).reduce((res, item) => { + res[passwordAlgorithmMap[item]] = item + return res +}, {}) + +const passwordExtMethod = { + [passwordAlgorithmMap.UNI_ID_HMAC_SHA1]: { + verify ({ password }) { + const { password_secret_version: passwordSecretVersion } = this.userRecord + + const passwordSecret = this._getSecretByVersion({ + version: passwordSecretVersion + }) + + const { passwordHash } = this.encrypt({ + password, + passwordSecret + }) + + return passwordHash === this.userRecord.password + }, + encrypt ({ password, passwordSecret }) { + const { value: secret, version } = passwordSecret + const hmac = crypto.createHmac('sha1', secret.toString('ascii')) + + hmac.update(password) + + return { + passwordHash: hmac.digest('hex'), + version + } + } + }, + [passwordAlgorithmMap.UNI_ID_HMAC_SHA256]: { + verify ({ password }) { + const parse = this._parsePassword() + const passwordHash = crypto.createHmac(parse.algorithm, parse.salt).update(password).digest('hex') + + return passwordHash === parse.hash + }, + encrypt ({ password, passwordSecret }) { + const { version } = passwordSecret + + // 默认使用 sha256 加密算法 + const salt = crypto.randomBytes(10).toString('hex') + const sha256Hash = crypto.createHmac(passwordAlgorithmMap.UNI_ID_HMAC_SHA256.substring(5), salt).update(password).digest('hex') + const algorithm = passwordAlgorithmKeyMap[passwordAlgorithmMap.UNI_ID_HMAC_SHA256] + // B 为固定值,对应 PasswordMethodMaps 中的 sha256算法 + // hash 格式 $[PasswordMethodFlagMapsKey]$[salt size]$[salt][Hash] + const passwordHash = `$${algorithm}$${salt.length}$${salt}${sha256Hash}` + + return { + passwordHash, + version + } + } + }, + [passwordAlgorithmMap.UNI_ID_CUSTOM]: { + verify ({ password, passwordSecret }) { + if (!customPassword.verifyPassword) throw new Error('verifyPassword method not found in custom password file') + + // return true or false + return customPassword.verifyPassword({ + password, + passwordSecret, + userRecord: this.userRecord, + clientInfo: this.clientInfo + }) + }, + encrypt ({ password, passwordSecret }) { + if (!customPassword.encryptPassword) throw new Error('encryptPassword method not found in custom password file') + + // return object<{passwordHash: string, version: number}> + return customPassword.encryptPassword({ + password, + passwordSecret, + clientInfo: this.clientInfo + }) + } + } +} + +class PasswordUtils { + constructor ({ + userRecord = {}, + clientInfo, + passwordSecret + } = {}) { + if (!clientInfo) throw new Error('Invalid clientInfo') + if (!passwordSecret) throw new Error('Invalid password secret') + + this.clientInfo = clientInfo + this.userRecord = userRecord + this.passwordSecret = this.prePasswordSecret(passwordSecret) + } + + /** + * passwordSecret 预处理 + * @param passwordSecret + * @return {*[]} + */ + prePasswordSecret (passwordSecret) { + const newPasswordSecret = [] + if (getType(passwordSecret) === 'string') { + newPasswordSecret.push({ + value: passwordSecret, + type: passwordAlgorithmMap.UNI_ID_HMAC_SHA1 + }) + } else if (getType(passwordSecret) === 'array') { + for (const secret of passwordSecret.sort((a, b) => a.version - b.version)) { + newPasswordSecret.push({ + ...secret, + // 没有 type 设置默认 type hmac-sha1 + type: secret.type || passwordAlgorithmMap.UNI_ID_HMAC_SHA1 + }) + } + } else { + throw new Error('Invalid password secret') + } + + return newPasswordSecret + } + + /** + * 获取最新加密密钥 + * @return {*} + * @private + */ + _getLastestSecret () { + return this.passwordSecret[this.passwordSecret.length - 1] + } + + _getOldestSecret () { + return this.passwordSecret[0] + } + + _getSecretByVersion ({ version } = {}) { + if (!version && version !== 0) { + return this._getOldestSecret() + } + if (this.passwordSecret.length === 1) { + return this.passwordSecret[0] + } + return this.passwordSecret.find(item => item.version === version) + } + + /** + * 获取密码的验证/加密方法 + * @param passwordSecret + * @return {*[]} + * @private + */ + _getPasswordExt (passwordSecret) { + const ext = passwordExtMethod[passwordSecret.type] + if (!ext) { + throw new Error(`暂不支持 ${passwordSecret.type} 类型的加密算法`) + } + + const passwordExt = Object.create(null) + + for (const key in ext) { + passwordExt[key] = ext[key].bind(Object.assign(this, Object.keys(ext).reduce((res, item) => { + if (item !== key) { + res[item] = ext[item].bind(this) + } + return res + }, {}))) + } + + return passwordExt + } + + _parsePassword () { + const [algorithmKey = '', cost = 0, hashStr = ''] = this.userRecord.password.split('$').filter(key => key) + const algorithm = passwordAlgorithmMap[algorithmKey] ? passwordAlgorithmMap[algorithmKey].substring(5) : null + const salt = hashStr.substring(0, Number(cost)) + const hash = hashStr.substring(Number(cost)) + + return { + algorithm, + salt, + hash + } + } + + /** + * 生成加密后的密码 + * @param {String} password 密码 + */ + generatePasswordHash ({ password }) { + if (!password) throw new Error('Invalid password') + + const passwordSecret = this._getLastestSecret() + const ext = this._getPasswordExt(passwordSecret) + + const { passwordHash, version } = ext.encrypt({ + password, + passwordSecret + }) + + return { + passwordHash, + version + } + } + + /** + * 密码校验 + * @param {String} password + * @param {Boolean} autoRefresh + * @return {{refreshPasswordInfo: {version: *, passwordHash: *}, success: boolean}|{success: boolean}} + */ + checkUserPassword ({ password, autoRefresh = true }) { + if (!password) throw new Error('Invalid password') + + const { password_secret_version: passwordSecretVersion } = this.userRecord + const passwordSecret = this._getSecretByVersion({ + version: passwordSecretVersion + }) + const ext = this._getPasswordExt(passwordSecret) + + const success = ext.verify({ password, passwordSecret }) + + if (!success) { + return { + success: false + } + } + + let refreshPasswordInfo + if (autoRefresh && passwordSecretVersion !== this._getLastestSecret().version) { + refreshPasswordInfo = this.generatePasswordHash({ password }) + } + + return { + success: true, + refreshPasswordInfo + } + } +} + +module.exports = PasswordUtils diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/qq.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/qq.js new file mode 100644 index 0000000..5a73218 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/qq.js @@ -0,0 +1,154 @@ +const { + userCollection +} = require('../../common/constants') +const { + ERROR +} = require('../../common/error') + +function getQQPlatform () { + const platform = this.clientPlatform + switch (platform) { + case 'app': + case 'app-plus': + case 'app-android': + case 'app-ios': + return 'app' + case 'mp-qq': + return 'mp' + default: + throw new Error('Unsupported qq platform') + } +} + +async function saveQQUserKey ({ + openid, + sessionKey, // QQ小程序用户sessionKey + accessToken, // App端QQ用户accessToken + accessTokenExpired // App端QQ用户accessToken过期时间 +} = {}) { + // 微信公众平台、开放平台refreshToken有效期均为30天(微信没有在网络请求里面返回30天这个值,务必注意未来可能出现调整,需及时更新此处逻辑)。 + // 此前QQ开放平台有调整过accessToken的过期时间:[access_token有效期由90天缩短至30天](https://wiki.connect.qq.com/%E3%80%90qq%E4%BA%92%E8%81%94%E3%80%91access_token%E6%9C%89%E6%95%88%E6%9C%9F%E8%B0%83%E6%95%B4) + const appId = this.getUniversalClientInfo().appId + const qqPlatform = getQQPlatform.call(this) + const keyObj = { + dcloudAppid: appId, + openid, + platform: 'qq-' + qqPlatform + } + switch (qqPlatform) { + case 'mp': + await this.uniOpenBridge.setSessionKey(keyObj, { + session_key: sessionKey + }, 30 * 24 * 60 * 60) + break + case 'app': + case 'h5': + case 'web': + await this.uniOpenBridge.setUserAccessToken(keyObj, { + access_token: accessToken, + access_token_expired: accessTokenExpired + }, accessTokenExpired + ? Math.floor((accessTokenExpired - Date.now()) / 1000) + : 30 * 24 * 60 * 60 + ) + break + default: + break + } +} + +function generateQQCache ({ + sessionKey, // QQ小程序用户sessionKey + accessToken, // App端QQ用户accessToken + accessTokenExpired // App端QQ用户accessToken过期时间 +} = {}) { + const platform = getQQPlatform.call(this) + let cache + switch (platform) { + case 'app': + cache = { + access_token: accessToken, + access_token_expired: accessTokenExpired + } + break + case 'mp': + cache = { + session_key: sessionKey + } + break + default: + throw new Error('Unsupported qq platform') + } + return { + third_party: { + [`${platform}_qq`]: cache + } + } +} + +function getQQOpenid ({ + userRecord +} = {}) { + const qqPlatform = getQQPlatform.call(this) + const appId = this.getUniversalClientInfo().appId + const qqOpenidObj = userRecord.qq_openid + if (!qqOpenidObj) { + return + } + return qqOpenidObj[`${qqPlatform}_${appId}`] || qqOpenidObj[qqPlatform] +} + +async function getQQCacheFallback ({ + userRecord, + key +} = {}) { + const platform = getQQPlatform.call(this) + const thirdParty = userRecord && userRecord.third_party + if (!thirdParty) { + return + } + const qqCache = thirdParty[`${platform}_qq`] + return qqCache && qqCache[key] +} + +async function getQQCache ({ + uid, + userRecord, + key +} = {}) { + const qqPlatform = getQQPlatform.call(this) + const appId = this.getUniversalClientInfo().appId + + if (!userRecord) { + const getUserRes = await userCollection.doc(uid).get() + userRecord = getUserRes.data[0] + } + if (!userRecord) { + throw { + errCode: ERROR.ACCOUNT_NOT_EXISTS + } + } + const openid = getQQOpenid.call(this, { + userRecord + }) + const getCacheMethod = qqPlatform === 'mp' ? 'getSessionKey' : 'getUserAccessToken' + const userKey = await this.uniOpenBridge[getCacheMethod]({ + dcloudAppid: appId, + platform: 'qq-' + qqPlatform, + openid + }) + if (userKey) { + return userKey[key] + } + return getQQCacheFallback({ + userRecord, + key + }) +} + +module.exports = { + getQQPlatform, + generateQQCache, + getQQCache, + saveQQUserKey +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/register.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/register.js new file mode 100644 index 0000000..0fd8641 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/register.js @@ -0,0 +1,231 @@ +const { + userCollection, + LOG_TYPE +} = require('../../common/constants') +const { + ERROR +} = require('../../common/error') +const { + findUser +} = require('./account') +const { + getValidInviteCode, + generateInviteInfo +} = require('./fission') +const { + logout +} = require('./logout') +const PasswordUtils = require('./password') +const { + merge +} = require('../npm/index') + +async function realPreRegister (params = {}) { + const { + user + } = params + const { + userMatched + } = await findUser({ + userQuery: user, + authorizedApp: this.getUniversalClientInfo().appId + }) + if (userMatched.length > 0) { + throw { + errCode: ERROR.ACCOUNT_EXISTS + } + } +} + +async function preRegister (params = {}) { + try { + await realPreRegister.call(this, params) + } catch (error) { + await this.middleware.uniIdLog({ + success: false, + type: LOG_TYPE.REGISTER + }) + throw error + } +} + +async function preRegisterWithPassword (params = {}) { + const { + user, + password + } = params + await preRegister.call(this, { + user + }) + const passwordUtils = new PasswordUtils({ + clientInfo: this.getUniversalClientInfo(), + passwordSecret: this.config.passwordSecret + }) + const { + passwordHash, + version + } = passwordUtils.generatePasswordHash({ + password + }) + const extraData = { + password: passwordHash, + password_secret_version: version + } + return { + user, + extraData + } +} + +async function thirdPartyRegister ({ + user = {} +} = {}) { + return { + mobileConfirmed: !!(user.mobile && user.mobile_confirmed) || false, + emailConfirmed: !!(user.email && user.email_confirmed) || false + } +} + +async function postRegister (params = {}) { + const { + user, + extraData = {}, + isThirdParty = false, + inviteCode + } = params + const { + appId, + appName, + appVersion, + appVersionCode, + channel, + scene, + clientIP, + osName + } = this.getUniversalClientInfo() + const uniIdToken = this.getUniversalUniIdToken() + + merge(user, extraData) + + const registerChannel = channel || scene + user.register_env = { + appid: appId || '', + uni_platform: this.clientPlatform || '', + os_name: osName || '', + app_name: appName || '', + app_version: appVersion || '', + app_version_code: appVersionCode || '', + channel: registerChannel ? registerChannel + '' : '', // channel可能为数字,统一存为字符串 + client_ip: clientIP || '' + } + + user.register_date = Date.now() + user.dcloud_appid = [appId] + + if (user.username) { + user.username = user.username.toLowerCase() + } + if (user.email) { + user.email = user.email.toLowerCase() + } + + const { + autoSetInviteCode, // 注册时自动设置邀请码 + forceInviteCode, // 必须有邀请码才允许注册,注意此逻辑不可对admin生效 + userRegisterDefaultRole // 用户注册时配置的默认角色 + } = this.config + if (autoSetInviteCode) { + user.my_invite_code = await getValidInviteCode() + } + + // 如果用户注册默认角色配置存在且不为空数组 + if (userRegisterDefaultRole && userRegisterDefaultRole.length) { + // 将用户已有的角色和配置的默认角色合并成一个数组,并去重 + user.role = Array.from(new Set([...(user.role || []), ...userRegisterDefaultRole])) + } + + const isAdmin = user.role && user.role.includes('admin') + + if (forceInviteCode && !isAdmin && !inviteCode) { + throw { + errCode: ERROR.INVALID_INVITE_CODE + } + } + + if (inviteCode) { + const { + inviterUid, + inviteTime + } = await generateInviteInfo({ + inviteCode + }) + user.inviter_uid = inviterUid + user.invite_time = inviteTime + } + + if (uniIdToken) { + try { + await logout.call(this) + } catch (error) { } + } + + const beforeRegister = this.hooks.beforeRegister + let userRecord = user + if (beforeRegister) { + userRecord = await beforeRegister({ + userRecord, + clientInfo: this.getUniversalClientInfo() + }) + } + + const { + id: uid + } = await userCollection.add(userRecord) + + const createTokenRes = await this.uniIdCommon.createToken({ + uid + }) + + const { + errCode, + token, + tokenExpired + } = createTokenRes + + if (errCode) { + throw createTokenRes + } + + await this.middleware.uniIdLog({ + data: { + user_id: uid + }, + type: LOG_TYPE.REGISTER + }) + + return { + errCode: 0, + uid, + newToken: { + token, + tokenExpired + }, + ...( + isThirdParty + ? thirdPartyRegister({ + user: { + ...userRecord, + _id: uid + } + }) + : {} + ), + passwordConfirmed: !!userRecord.password + } +} + +module.exports = { + preRegister, + preRegisterWithPassword, + postRegister +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/relate.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/relate.js new file mode 100644 index 0000000..f855422 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/relate.js @@ -0,0 +1,166 @@ +const { + findUser +} = require('./account') +const { + ERROR +} = require('../../common/error') +const { + userCollection, dbCmd, USER_IDENTIFIER +} = require('../../common/constants') +const { + getUserIdentifier +} = require('../../lib/utils/account') + +const { + batchFindObjctValue +} = require('../../common/utils') +const { + merge +} = require('../npm/index') + +/** + * + * @param {object} param + * @param {string} param.uid 用户id + * @param {string} param.bindAccount 要绑定的三方账户、手机号或邮箱 + */ +async function preBind ({ + uid, + bindAccount, + logType +} = {}) { + const { + userMatched + } = await findUser({ + userQuery: bindAccount, + authorizedApp: this.getUniversalClientInfo().appId + }) + if (userMatched.length > 0) { + await this.middleware.uniIdLog({ + data: { + user_id: uid + }, + type: logType, + success: false + }) + throw { + errCode: ERROR.BIND_CONFLICT + } + } +} + +async function postBind ({ + uid, + extraData = {}, + bindAccount, + logType +} = {}) { + await userCollection.doc(uid).update(merge(bindAccount, extraData)) + await this.middleware.uniIdLog({ + data: { + user_id: uid + }, + type: logType + }) + return { + errCode: 0 + } +} + +async function preUnBind ({ + uid, + unBindAccount, + logType +}) { + const notUnBind = ['username', 'mobile', 'email'] + const userIdentifier = getUserIdentifier(unBindAccount) + const condition = Object.keys(userIdentifier).reduce((res, key) => { + if (userIdentifier[key]) { + if (notUnBind.includes(key)) { + throw { + errCode: ERROR.UNBIND_NOT_SUPPORTED + } + } + + res.push({ + [key]: userIdentifier[key] + }) + } + + return res + }, []) + const currentUnBindAccount = Object.keys(userIdentifier).reduce((res, key) => { + if (userIdentifier[key]) { + res.push(key) + } + return res + }, []) + const { data: users } = await userCollection.where(dbCmd.and( + { _id: uid }, + dbCmd.or(condition) + )).get() + + if (users.length <= 0) { + await this.middleware.uniIdLog({ + data: { + user_id: uid + }, + type: logType, + success: false + }) + throw { + errCode: ERROR.UNBIND_FAIL + } + } + + const [user] = users + const otherAccounts = batchFindObjctValue(user, Object.keys(USER_IDENTIFIER).filter(key => !notUnBind.includes(key) && !currentUnBindAccount.includes(key))) + let hasOtherAccountBind = false + + for (const key in otherAccounts) { + if (otherAccounts[key]) { + hasOtherAccountBind = true + break + } + } + + // 如果没有其他第三方登录方式 + if (!hasOtherAccountBind) { + // 存在用户名或者邮箱但是没有设置过没密码就提示设置密码 + if ((user.username || user.email) && !user.password) { + throw { + errCode: ERROR.UNBIND_PASSWORD_NOT_EXISTS + } + } + // 账号任何登录方式都没有就优先绑定手机号 + if (!user.mobile) { + throw { + errCode: ERROR.UNBIND_MOBILE_NOT_EXISTS + } + } + } +} + +async function postUnBind ({ + uid, + unBindAccount, + logType +}) { + await userCollection.doc(uid).update(unBindAccount) + await this.middleware.uniIdLog({ + data: { + user_id: uid + }, + type: logType + }) + return { + errCode: 0 + } +} + +module.exports = { + preBind, + postBind, + preUnBind, + postUnBind +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/sms.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/sms.js new file mode 100644 index 0000000..ae9b7af --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/sms.js @@ -0,0 +1,79 @@ +const { + setMobileVerifyCode +} = require('./verify-code') +const { + getVerifyCode +} = require('../../common/utils') + +/** + * 发送短信 + * @param {object} param + * @param {string} param.mobile 手机号 + * @param {object} param.code 可选,验证码 + * @param {object} param.scene 短信场景 + * @param {object} param.templateId 可选,短信模板id + * @returns + */ +async function sendSmsCode ({ + mobile, + code, + scene, + templateId +} = {}) { + const requiredParams = [ + 'name', + 'codeExpiresIn' + ] + const smsConfig = (this.config.service && this.config.service.sms) || {} + for (let i = 0; i < requiredParams.length; i++) { + const key = requiredParams[i] + if (!smsConfig[key]) { + throw new Error(`Missing config param: service.sms.${key}`) + } + } + if (!code) { + code = getVerifyCode() + } + let action + switch (scene) { + case 'login-by-sms': + action = this.t('login') + break + default: + action = this.t('verify-mobile') + break + } + const sceneConfig = (smsConfig.scene || {})[scene] || {} + if (!templateId) { + templateId = sceneConfig.templateId + } + if (!templateId) { + throw new Error('"templateId" is required') + } + const codeExpiresIn = sceneConfig.codeExpiresIn || smsConfig.codeExpiresIn + await setMobileVerifyCode.call(this, { + mobile, + code, + expiresIn: codeExpiresIn, + scene + }) + await uniCloud.sendSms({ + smsKey: smsConfig.smsKey, + smsSecret: smsConfig.smsSecret, + phone: mobile, + templateId, + data: { + name: smsConfig.name, + code, + action, + expMinute: '' + Math.round(codeExpiresIn / 60) + } + }) + return { + errCode: 0 + } +} + +module.exports = { + sendSmsCode +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/unified-login.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/unified-login.js new file mode 100644 index 0000000..eac7a51 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/unified-login.js @@ -0,0 +1,106 @@ +const { + checkLoginUserRecord, + postLogin +} = require('./login') +const { + postRegister +} = require('./register') +const { + findUser +} = require('./account') +const { + ERROR +} = require('../../common/error') + +async function realPreUnifiedLogin (params = {}) { + const { + user, + type + } = params + const appId = this.getUniversalClientInfo().appId + const { + total, + userMatched + } = await findUser({ + userQuery: user, + authorizedApp: appId + }) + if (userMatched.length === 0) { + if (type === 'login') { + if (total > 0) { + throw { + errCode: ERROR.ACCOUNT_NOT_EXISTS_IN_CURRENT_APP + } + } + throw { + errCode: ERROR.ACCOUNT_NOT_EXISTS + } + } + return { + type: 'register', + user + } + } if (userMatched.length === 1) { + if (type === 'register') { + throw { + errCode: ERROR.ACCOUNT_EXISTS + } + } + const userRecord = userMatched[0] + checkLoginUserRecord(userRecord) + return { + type: 'login', + user: userRecord + } + } else if (userMatched.length > 1) { + throw { + errCode: ERROR.ACCOUNT_CONFLICT + } + } +} + +async function preUnifiedLogin (params = {}) { + try { + const result = await realPreUnifiedLogin.call(this, params) + return result + } catch (error) { + await this.middleware.uniIdLog({ + success: false + }) + throw error + } +} + +async function postUnifiedLogin (params = {}) { + const { + user, + extraData = {}, + isThirdParty = false, + type, + inviteCode + } = params + let result + if (type === 'login') { + result = await postLogin.call(this, { + user, + extraData, + isThirdParty + }) + } else if (type === 'register') { + result = await postRegister.call(this, { + user, + extraData, + isThirdParty, + inviteCode + }) + } + return { + ...result, + type + } +} + +module.exports = { + preUnifiedLogin, + postUnifiedLogin +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/univerify.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/univerify.js new file mode 100644 index 0000000..b64bef9 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/univerify.js @@ -0,0 +1,27 @@ +async function getPhoneNumber ({ + // eslint-disable-next-line camelcase + access_token, + openid +} = {}) { + const requiredParams = [] + const univerifyConfig = (this.config.service && this.config.service.univerify) || {} + for (let i = 0; i < requiredParams.length; i++) { + const key = requiredParams[i] + if (!univerifyConfig[key]) { + throw new Error(`Missing config param: service.univerify.${key}`) + } + } + return uniCloud.getPhoneNumber({ + provider: 'univerify', + appid: this.getUniversalClientInfo().appId, + apiKey: univerifyConfig.apiKey, + apiSecret: univerifyConfig.apiSecret, + // eslint-disable-next-line camelcase + access_token, + openid + }) +} + +module.exports = { + getPhoneNumber +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/update-user-info.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/update-user-info.js new file mode 100644 index 0000000..ced33b9 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/update-user-info.js @@ -0,0 +1,25 @@ +const { + userCollection +} = require('../../common/constants') +const { + USER_STATUS +} = require('../../common/constants') +async function setUserStatus (uid, status) { + const updateData = { + status + } + if (status !== USER_STATUS.NORMAL) { + updateData.valid_token_date = Date.now() + } + await userCollection.doc(uid).update({ + status + }) + // TODO 此接口尚不完善,例如注销后其他客户端可能存在有效token,支持Redis后此处会补充额外逻辑 + return { + errCode: 0 + } +} + +module.exports = { + setUserStatus +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/utils.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/utils.js new file mode 100644 index 0000000..7d3e0f3 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/utils.js @@ -0,0 +1,18 @@ +let redisEnable = null +function getRedisEnable() { + // 未用到的时候不调用redis接口,节省一些连接数 + if (redisEnable !== null) { + return redisEnable + } + try { + uniCloud.redis() + redisEnable = true + } catch (error) { + redisEnable = false + } + return redisEnable +} + +module.exports = { + getRedisEnable +} \ No newline at end of file diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/verify-code.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/verify-code.js new file mode 100644 index 0000000..b11bc02 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/verify-code.js @@ -0,0 +1,152 @@ +const { + dbCmd, + verifyCollection +} = require('../../common/constants') +const { + ERROR +} = require('../../common/error') +const { + getVerifyCode +} = require('../../common/utils') + +async function setVerifyCode ({ + mobile, + email, + code, + expiresIn, + scene +} = {}) { + const now = Date.now() + const record = { + mobile, + email, + scene, + code: code || getVerifyCode(), + state: 0, + ip: this.getUniversalClientInfo().clientIP, + created_date: now, + expired_date: now + expiresIn * 1000 + } + await verifyCollection.add(record) + return { + errCode: 0 + } +} + +async function setEmailVerifyCode ({ + email, + code, + expiresIn, + scene +} = {}) { + email = email && email.trim() + if (!email) { + throw { + errCode: ERROR.INVALID_EMAIL + } + } + email = email.toLowerCase() + return setVerifyCode.call(this, { + email, + code, + expiresIn, + scene + }) +} + +async function setMobileVerifyCode ({ + mobile, + code, + expiresIn, + scene +} = {}) { + mobile = mobile && mobile.trim() + if (!mobile) { + throw { + errCode: ERROR.INVALID_MOBILE + } + } + return setVerifyCode.call(this, { + mobile, + code, + expiresIn, + scene + }) +} + +async function verifyEmailCode ({ + email, + code, + scene +} = {}) { + email = email && email.trim() + if (!email) { + throw { + errCode: ERROR.INVALID_EMAIL + } + } + email = email.toLowerCase() + const { + data: codeRecord + } = await verifyCollection.where({ + email, + scene, + code, + state: 0, + expired_date: dbCmd.gt(Date.now()) + }).limit(1).get() + + if (codeRecord.length === 0) { + throw { + errCode: ERROR.EMAIL_VERIFY_CODE_ERROR + } + } + await verifyCollection.doc(codeRecord[0]._id).update({ + state: 1 + }) + return { + errCode: 0 + } +} + +async function verifyMobileCode ({ + mobile, + code, + scene +} = {}) { + mobile = mobile && mobile.trim() + if (!mobile) { + throw { + errCode: ERROR.INVALID_MOBILE + } + } + const { + data: codeRecord + } = await verifyCollection.where({ + mobile, + scene, + code, + state: 0, + expired_date: dbCmd.gt(Date.now()) + }).limit(1).get() + + if (codeRecord.length === 0) { + throw { + errCode: ERROR.MOBILE_VERIFY_CODE_ERROR + } + } + + await verifyCollection.doc(codeRecord[0]._id).update({ + state: 1 + }) + return { + errCode: 0 + } +} + +module.exports = { + verifyEmailCode, + verifyMobileCode, + setEmailVerifyCode, + setMobileVerifyCode +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/weixin.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/weixin.js new file mode 100644 index 0000000..8ab34b1 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/lib/utils/weixin.js @@ -0,0 +1,236 @@ +const crypto = require('crypto') +const { + userCollection +} = require('../../common/constants') +const { + ERROR +} = require('../../common/error') +const { + getRedisEnable +} = require('./utils') +const { + openDataCollection +} = require('../../common/constants') + +function decryptWeixinData ({ + encryptedData, + sessionKey, + iv +} = {}) { + const oauthConfig = this.configUtils.getOauthConfig({ + provider: 'weixin' + }) + const decipher = crypto.createDecipheriv( + 'aes-128-cbc', + Buffer.from(sessionKey, 'base64'), + Buffer.from(iv, 'base64') + ) + // 设置自动 padding 为 true,删除填充补位 + decipher.setAutoPadding(true) + let decoded + decoded = decipher.update(encryptedData, 'base64', 'utf8') + decoded += decipher.final('utf8') + decoded = JSON.parse(decoded) + if (decoded.watermark.appid !== oauthConfig.appid) { + throw new Error('Invalid wechat appid in decode content') + } + return decoded +} + +function getWeixinPlatform () { + const platform = this.clientPlatform + const userAgent = this.getUniversalClientInfo().userAgent + switch (platform) { + case 'app': + case 'app-plus': + case 'app-android': + case 'app-ios': + return 'app' + case 'mp-weixin': + return 'mp' + case 'h5': + case 'web': + return userAgent.indexOf('MicroMessenger') > -1 ? 'h5' : 'web' + default: + throw new Error('Unsupported weixin platform') + } +} + +async function saveWeixinUserKey ({ + openid, + sessionKey, // 微信小程序用户sessionKey + accessToken, // App端微信用户accessToken + refreshToken, // App端微信用户refreshToken + accessTokenExpired // App端微信用户accessToken过期时间 +} = {}) { + // 微信公众平台、开放平台refreshToken有效期均为30天(微信没有在网络请求里面返回30天这个值,务必注意未来可能出现调整,需及时更新此处逻辑)。 + // 此前QQ开放平台有调整过accessToken的过期时间:[access_token有效期由90天缩短至30天](https://wiki.connect.qq.com/%E3%80%90qq%E4%BA%92%E8%81%94%E3%80%91access_token%E6%9C%89%E6%95%88%E6%9C%9F%E8%B0%83%E6%95%B4) + + const appId = this.getUniversalClientInfo().appId + const weixinPlatform = getWeixinPlatform.call(this) + const keyObj = { + dcloudAppid: appId, + openid, + platform: 'weixin-' + weixinPlatform + } + switch (weixinPlatform) { + case 'mp': + await this.uniOpenBridge.setSessionKey(keyObj, { + session_key: sessionKey + }, 30 * 24 * 60 * 60) + break + case 'app': + case 'h5': + case 'web': + await this.uniOpenBridge.setUserAccessToken(keyObj, { + access_token: accessToken, + refresh_token: refreshToken, + access_token_expired: accessTokenExpired + }, 30 * 24 * 60 * 60) + break + default: + break + } +} + +async function saveSecureNetworkCache ({ + code, + openid, + unionid, + sessionKey +}) { + const { + appId + } = this.getUniversalClientInfo() + const key = `uni-id:${appId}:weixin-mp:code:${code}:secure-network-cache` + const value = JSON.stringify({ + openid, + unionid, + session_key: sessionKey + }) + // 此处存储的是code的缓存,设置有效期和token一致 + const expiredSeconds = this.config.tokenExpiresIn || 3 * 24 * 60 * 60 + + await openDataCollection.doc(key).set({ + value, + expired: Date.now() + expiredSeconds * 1000 + }) + const isRedisEnable = getRedisEnable() + if (isRedisEnable) { + const redis = uniCloud.redis() + await redis.set(key, value, 'EX', expiredSeconds) + } +} + +function generateWeixinCache ({ + sessionKey, // 微信小程序用户sessionKey + accessToken, // App端微信用户accessToken + refreshToken, // App端微信用户refreshToken + accessTokenExpired // App端微信用户accessToken过期时间 +} = {}) { + const platform = getWeixinPlatform.call(this) + let cache + switch (platform) { + case 'app': + case 'h5': + case 'web': + cache = { + access_token: accessToken, + refresh_token: refreshToken, + access_token_expired: accessTokenExpired + } + break + case 'mp': + cache = { + session_key: sessionKey + } + break + default: + throw new Error('Unsupported weixin platform') + } + return { + third_party: { + [`${platform}_weixin`]: cache + } + } +} + +function getWeixinOpenid ({ + userRecord +} = {}) { + const weixinPlatform = getWeixinPlatform.call(this) + const appId = this.getUniversalClientInfo().appId + const wxOpenidObj = userRecord.wx_openid + if (!wxOpenidObj) { + return + } + return wxOpenidObj[`${weixinPlatform}_${appId}`] || wxOpenidObj[weixinPlatform] +} + +async function getWeixinCacheFallback ({ + userRecord, + key +} = {}) { + const platform = getWeixinPlatform.call(this) + const thirdParty = userRecord && userRecord.third_party + if (!thirdParty) { + return + } + const weixinCache = thirdParty[`${platform}_weixin`] + return weixinCache && weixinCache[key] +} + +async function getWeixinCache ({ + uid, + userRecord, + key +} = {}) { + const weixinPlatform = getWeixinPlatform.call(this) + const appId = this.getUniversalClientInfo().appId + if (!userRecord) { + const getUserRes = await userCollection.doc(uid).get() + userRecord = getUserRes.data[0] + } + if (!userRecord) { + throw { + errCode: ERROR.ACCOUNT_NOT_EXISTS + } + } + const openid = getWeixinOpenid.call(this, { + userRecord + }) + const getCacheMethod = weixinPlatform === 'mp' ? 'getSessionKey' : 'getUserAccessToken' + const userKey = await this.uniOpenBridge[getCacheMethod]({ + dcloudAppid: appId, + platform: 'weixin-' + weixinPlatform, + openid + }) + if (userKey) { + return userKey[key] + } + return getWeixinCacheFallback({ + userRecord, + key + }) +} + +async function getWeixinAccessToken () { + const weixinPlatform = getWeixinPlatform.call(this) + const appId = this.getUniversalClientInfo().appId + + const cache = await this.uniOpenBridge.getAccessToken({ + dcloudAppid: appId, + platform: 'weixin-' + weixinPlatform + }) + + return cache.access_token +} +module.exports = { + decryptWeixinData, + getWeixinPlatform, + generateWeixinCache, + getWeixinCache, + saveWeixinUserKey, + getWeixinAccessToken, + saveSecureNetworkCache +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/middleware/access-control.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/middleware/access-control.js new file mode 100644 index 0000000..e333fe0 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/middleware/access-control.js @@ -0,0 +1,59 @@ +const methodPermission = require('../config/permission') +const { + ERROR +} = require('../common/error') + +function isAccessAllowed (user, setting) { + const { + role: userRole = [], + permission: userPermission = [] + } = user + const { + role: settingRole = [], + permission: settingPermission = [] + } = setting + if (userRole.includes('admin')) { + return + } + if ( + settingRole.length > 0 && + settingRole.every(item => !userRole.includes(item)) + ) { + throw { + errCode: ERROR.PERMISSION_ERROR + } + } + if ( + settingPermission.length > 0 && + settingPermission.every(item => !userPermission.includes(item)) + ) { + throw { + errCode: ERROR.PERMISSION_ERROR + } + } +} + +module.exports = async function () { + const methodName = this.getMethodName() + if (!(methodName in methodPermission)) { + return + } + const { + auth, + role, + permission + } = methodPermission[methodName] + if (auth || role || permission) { + await this.middleware.auth() + } + if (role && role.length === 0) { + throw new Error('[AccessControl]Empty role array is not supported') + } + if (permission && permission.length === 0) { + throw new Error('[AccessControl]Empty permission array is not supported') + } + return isAccessAllowed(this.authInfo, { + role, + permission + }) +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/middleware/auth.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/middleware/auth.js new file mode 100644 index 0000000..1915335 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/middleware/auth.js @@ -0,0 +1,17 @@ +module.exports = async function () { + if (this.authInfo) { // 多次执行auth时如果第一次成功后续不再执行 + return + } + const token = this.getUniversalUniIdToken() + const payload = await this.uniIdCommon.checkToken(token) + if (payload.errCode) { + throw payload + } + this.authInfo = payload + if (payload.token) { + this.response.newToken = { + token: payload.token, + tokenExpired: payload.tokenExpired + } + } +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/middleware/index.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/middleware/index.js new file mode 100644 index 0000000..9f7c958 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/middleware/index.js @@ -0,0 +1,8 @@ +module.exports = { + auth: require('./auth'), + uniIdLog: require('./uni-id-log'), + validate: require('./validate'), + accessControl: require('./access-control'), + verifyRequestSign: require('./verify-request-sign'), + ...require('./rbac') +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/middleware/rbac.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/middleware/rbac.js new file mode 100644 index 0000000..f42ef8d --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/middleware/rbac.js @@ -0,0 +1,39 @@ +const { + ERROR +} = require('../common/error') + +function hasRole (...roleList) { + const userRole = this.authInfo.role || [] + if (userRole.includes('admin')) { + return + } + const isMatch = roleList.every(roleItem => { + return userRole.includes(roleItem) + }) + if (!isMatch) { + throw { + errCode: ERROR.PERMISSION_ERROR + } + } +} + +function hasPermission (...permissionList) { + const userRole = this.authInfo.role || [] + const userPermission = this.authInfo.permission || [] + if (userRole.includes('admin')) { + return + } + const isMatch = permissionList.every(permissionItem => { + return userPermission.includes(permissionItem) + }) + if (!isMatch) { + throw { + errCode: ERROR.PERMISSION_ERROR + } + } +} + +module.exports = { + hasRole, + hasPermission +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/middleware/uni-id-log.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/middleware/uni-id-log.js new file mode 100644 index 0000000..ca6927d --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/middleware/uni-id-log.js @@ -0,0 +1,39 @@ +const db = uniCloud.database() +module.exports = async function ({ + data = {}, + success = true, + type = 'login' +} = {}) { + const now = Date.now() + const uniIdLogCollection = db.collection('uni-id-log') + const requiredDataKeyList = ['user_id', 'username', 'email', 'mobile'] + const dataCopy = {} + for (let i = 0; i < requiredDataKeyList.length; i++) { + const key = requiredDataKeyList[i] + if (key in data && typeof data[key] === 'string') { + dataCopy[key] = data[key] + } + } + const { + appId, + clientIP, + deviceId, + userAgent + } = this.getUniversalClientInfo() + const logData = { + appid: appId, + device_id: deviceId, + ip: clientIP, + type, + ua: userAgent, + create_date: now, + ...dataCopy + } + + if (success) { + logData.state = 1 + } else { + logData.state = 0 + } + return uniIdLogCollection.add(logData) +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/middleware/validate.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/middleware/validate.js new file mode 100644 index 0000000..52ff047 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/middleware/validate.js @@ -0,0 +1,7 @@ +module.exports = function (value = {}, schema = {}) { + const validateRes = this.validator.validate(value, schema) + if (validateRes) { + delete validateRes.schemaKey + throw validateRes + } +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/middleware/verify-request-sign.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/middleware/verify-request-sign.js new file mode 100644 index 0000000..56e421a --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/middleware/verify-request-sign.js @@ -0,0 +1,85 @@ +const crypto = require('crypto') +const createConfig = require('uni-config-center') +const { verifyHttpInfo } = require('uni-cloud-s2s') + +const { ERROR } = require('../common/error') +const s2sConfig = createConfig({ + pluginId: 'uni-cloud-s2s' +}) +const needSignFunctions = new Set([ + 'externalRegister', + 'externalLogin', + 'updateUserInfoByExternal' +]) + +module.exports = function () { + const methodName = this.getMethodName() + const { source } = this.getUniversalClientInfo() + + // 指定接口需要鉴权 + if (!needSignFunctions.has(methodName)) return + + // 非 HTTP 方式请求拒绝访问 + if (source !== 'http') { + throw { + errCode: ERROR.ILLEGAL_REQUEST + } + } + + // 支持 uni-cloud-s2s 验证请求 + if (s2sConfig.hasFile('config.json')) { + try { + if (!verifyHttpInfo(this.getHttpInfo())) { + throw { + errCode: ERROR.ILLEGAL_REQUEST + } + } + } catch (e) { + if (e.errSubject === 'uni-cloud-s2s') { + throw { + errCode: ERROR.ILLEGAL_REQUEST, + errMsg: e.errMsg + } + } + throw e + } + + return + } + + if (!this.config.requestAuthSecret || typeof this.config.requestAuthSecret !== 'string') { + throw { + errCode: ERROR.CONFIG_FIELD_REQUIRED, + errMsgValue: { + field: 'requestAuthSecret' + } + } + } + + const timeout = 20 * 1000 // 请求超过20秒不能再请求,防止重放攻击 + const { headers, body: _body } = this.getHttpInfo() + const { 'uni-id-nonce': nonce, 'uni-id-timestamp': timestamp, 'uni-id-signature': signature } = headers + const body = JSON.parse(_body).params || {} + const bodyStr = Object.keys(body) + .sort() + .filter(item => typeof body[item] !== 'object') + .map(item => `${item}=${body[item]}`) + .join('&') + + if (isNaN(Number(timestamp)) || (Number(timestamp) + timeout) < Date.now()) { + console.error('[timestamp error], timestamp:', timestamp, 'timeout:', timeout) + + throw { + errCode: ERROR.ILLEGAL_REQUEST + } + } + + const reSignature = crypto.createHmac('sha256', `${this.config.requestAuthSecret + nonce}`).update(`${timestamp}${bodyStr}`).digest('hex') + + if (signature !== reSignature.toUpperCase()) { + console.error('[signature error], signature:', signature, 'reSignature:', reSignature.toUpperCase(), 'requestAuthSecret:', this.config.requestAuthSecret) + throw { + errCode: ERROR.ILLEGAL_REQUEST + } + } +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/account/close-account.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/account/close-account.js new file mode 100644 index 0000000..f1bdf96 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/account/close-account.js @@ -0,0 +1,16 @@ +const { + setUserStatus +} = require('../../lib/utils/update-user-info') +const { + USER_STATUS +} = require('../../common/constants') + +/** + * 注销账户 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#close-account + * @returns + */ +module.exports = async function () { + const { uid } = this.authInfo + return setUserStatus(uid, USER_STATUS.CLOSED) +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/account/get-account-info.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/account/get-account-info.js new file mode 100644 index 0000000..7b8599a --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/account/get-account-info.js @@ -0,0 +1,69 @@ +const { + userCollection +} = require('../../common/constants') +const { + ERROR +} = require('../../common/error') + +function isUsernameSet (userRecord) { + return !!userRecord.username +} +function isNicknameSet (userRecord) { + return !!userRecord.nickname +} +function isPasswordSet (userRecord) { + return !!userRecord.password +} +function isMobileBound (userRecord) { + return !!(userRecord.mobile && userRecord.mobile_confirmed) +} +function isEmailBound (userRecord) { + return !!(userRecord.email && userRecord.email_confirmed) +} +function isWeixinBound (userRecord) { + return !!( + userRecord.wx_unionid || + Object.keys(userRecord.wx_openid || {}).length + ) +} +function isQQBound (userRecord) { + return !!( + userRecord.qq_unionid || + Object.keys(userRecord.qq_openid || {}).length + ) +} +function isAlipayBound (userRecord) { + return !!userRecord.ali_openid +} +function isAppleBound (userRecord) { + return !!userRecord.apple_openid +} + +/** + * 获取账户账户简略信息 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#get-account-info + */ +module.exports = async function () { + const { + uid + } = this.authInfo + const getUserRes = await userCollection.doc(uid).get() + const userRecord = getUserRes && getUserRes.data && getUserRes.data[0] + if (!userRecord) { + throw { + errCode: ERROR.ACCOUNT_NOT_EXISTS + } + } + return { + errCode: 0, + isUsernameSet: isUsernameSet(userRecord), + isNicknameSet: isNicknameSet(userRecord), + isPasswordSet: isPasswordSet(userRecord), + isMobileBound: isMobileBound(userRecord), + isEmailBound: isEmailBound(userRecord), + isWeixinBound: isWeixinBound(userRecord), + isQQBound: isQQBound(userRecord), + isAlipayBound: isAlipayBound(userRecord), + isAppleBound: isAppleBound(userRecord) + } +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/account/get-realname-info.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/account/get-realname-info.js new file mode 100644 index 0000000..0ea8f05 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/account/get-realname-info.js @@ -0,0 +1,45 @@ +const { userCollection } = require('../../common/constants') +const { ERROR } = require('../../common/error') +const { decryptData } = require('../../common/sensitive-aes-cipher') +const { dataDesensitization } = require('../../common/utils') + +/** + * 获取实名信息 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#get-realname-info + * @param {Object} params + * @param {Boolean} params.decryptData 是否解密数据 + * @returns + */ +module.exports = async function (params = {}) { + const schema = { + decryptData: { + required: false, + type: 'boolean' + } + } + + this.middleware.validate(params, schema) + + const { decryptData: isDecryptData = true } = params + + const { + uid + } = this.authInfo + const getUserRes = await userCollection.doc(uid).get() + const userRecord = getUserRes && getUserRes.data && getUserRes.data[0] + if (!userRecord) { + throw { + errCode: ERROR.ACCOUNT_NOT_EXISTS + } + } + + const { realname_auth: realNameAuth = {} } = userRecord + + return { + errCode: 0, + type: realNameAuth.type, + authStatus: realNameAuth.auth_status, + realName: isDecryptData ? dataDesensitization(decryptData.call(this, realNameAuth.real_name), { onlyLast: true }) : realNameAuth.real_name, + identity: isDecryptData ? dataDesensitization(decryptData.call(this, realNameAuth.identity)) : realNameAuth.identity + } +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/account/index.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/account/index.js new file mode 100644 index 0000000..0e55385 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/account/index.js @@ -0,0 +1,9 @@ +module.exports = { + setPwd: require('./set-pwd'), + updatePwd: require('./update-pwd'), + resetPwdBySms: require('./reset-pwd-by-sms'), + resetPwdByEmail: require('./reset-pwd-by-email'), + closeAccount: require('./close-account'), + getAccountInfo: require('./get-account-info'), + getRealNameInfo: require('./get-realname-info') +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/account/reset-pwd-by-email.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/account/reset-pwd-by-email.js new file mode 100644 index 0000000..20c6219 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/account/reset-pwd-by-email.js @@ -0,0 +1,128 @@ +const { + ERROR +} = require('../../common/error') +const { + getNeedCaptcha, + verifyCaptcha +} = require('../../lib/utils/captcha') +const { + verifyEmailCode +} = require('../../lib/utils/verify-code') +const { + userCollection, + EMAIL_SCENE, + CAPTCHA_SCENE, + LOG_TYPE +} = require('../../common/constants') +const { + findUser +} = require('../../lib/utils/account') +const PasswordUtils = require('../../lib/utils/password') + +/** + * 通过邮箱验证码重置密码 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#reset-pwd-by-email + * @param {object} params + * @param {string} params.email 邮箱 + * @param {string} params.code 邮箱验证码 + * @param {string} params.password 密码 + * @param {string} params.captcha 图形验证码 + * @returns {object} + */ +module.exports = async function (params = {}) { + const schema = { + email: 'email', + code: 'string', + password: 'password', + captcha: { + required: false, + type: 'string' + } + } + this.middleware.validate(params, schema) + const { + email, + code, + password, + captcha + } = params + + const needCaptcha = await getNeedCaptcha.call(this, { + email, + type: LOG_TYPE.RESET_PWD_BY_EMAIL + }) + if (needCaptcha) { + await verifyCaptcha.call(this, { + captcha, + scene: CAPTCHA_SCENE.RESET_PWD_BY_EMAIL + }) + } + try { + // 验证手机号验证码,验证不通过时写入失败日志 + await verifyEmailCode({ + email, + code, + scene: EMAIL_SCENE.RESET_PWD_BY_EMAIL + }) + } catch (error) { + await this.middleware.uniIdLog({ + data: { + email + }, + type: LOG_TYPE.RESET_PWD_BY_EMAIL, + success: false + }) + throw error + } + // 根据手机号查找匹配的用户 + const { + total, + userMatched + } = await findUser.call(this, { + userQuery: { + email + }, + authorizedApp: [this.getUniversalClientInfo().appId] + }) + if (userMatched.length === 0) { + if (total > 0) { + throw { + errCode: ERROR.ACCOUNT_NOT_EXISTS_IN_CURRENT_APP + } + } + throw { + errCode: ERROR.ACCOUNT_NOT_EXISTS + } + } else if (userMatched.length > 1) { + throw { + errCode: ERROR.ACCOUNT_CONFLICT + } + } + const { _id: uid } = userMatched[0] + const { + passwordHash, + version + } = new PasswordUtils({ + clientInfo: this.getUniversalClientInfo(), + passwordSecret: this.config.passwordSecret + }).generatePasswordHash({ + password + }) + // 更新用户密码 + await userCollection.doc(uid).update({ + password: passwordHash, + password_secret_version: version, + valid_token_date: Date.now() + }) + + // 写入成功日志 + await this.middleware.uniIdLog({ + data: { + email + }, + type: LOG_TYPE.RESET_PWD_BY_SMS + }) + return { + errCode: 0 + } +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/account/reset-pwd-by-sms.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/account/reset-pwd-by-sms.js new file mode 100644 index 0000000..bc10dc8 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/account/reset-pwd-by-sms.js @@ -0,0 +1,128 @@ +const { + ERROR +} = require('../../common/error') +const { + getNeedCaptcha, + verifyCaptcha +} = require('../../lib/utils/captcha') +const { + verifyMobileCode +} = require('../../lib/utils/verify-code') +const { + userCollection, + SMS_SCENE, + CAPTCHA_SCENE, + LOG_TYPE +} = require('../../common/constants') +const { + findUser +} = require('../../lib/utils/account') +const PasswordUtils = require('../../lib/utils/password') + +/** + * 通过短信验证码重置密码 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#reset-pwd-by-sms + * @param {object} params + * @param {string} params.mobile 手机号 + * @param {string} params.mobile 短信验证码 + * @param {string} params.password 密码 + * @param {string} params.captcha 图形验证码 + * @returns {object} + */ +module.exports = async function (params = {}) { + const schema = { + mobile: 'mobile', + code: 'string', + password: 'password', + captcha: { + required: false, + type: 'string' + } + } + this.middleware.validate(params, schema) + const { + mobile, + code, + password, + captcha + } = params + + const needCaptcha = await getNeedCaptcha.call(this, { + mobile, + type: LOG_TYPE.RESET_PWD_BY_SMS + }) + if (needCaptcha) { + await verifyCaptcha.call(this, { + captcha, + scene: CAPTCHA_SCENE.RESET_PWD_BY_SMS + }) + } + try { + // 验证手机号验证码,验证不通过时写入失败日志 + await verifyMobileCode({ + mobile, + code, + scene: SMS_SCENE.RESET_PWD_BY_SMS + }) + } catch (error) { + await this.middleware.uniIdLog({ + data: { + mobile + }, + type: LOG_TYPE.RESET_PWD_BY_SMS, + success: false + }) + throw error + } + // 根据手机号查找匹配的用户 + const { + total, + userMatched + } = await findUser.call(this, { + userQuery: { + mobile + }, + authorizedApp: [this.getUniversalClientInfo().appId] + }) + if (userMatched.length === 0) { + if (total > 0) { + throw { + errCode: ERROR.ACCOUNT_NOT_EXISTS_IN_CURRENT_APP + } + } + throw { + errCode: ERROR.ACCOUNT_NOT_EXISTS + } + } else if (userMatched.length > 1) { + throw { + errCode: ERROR.ACCOUNT_CONFLICT + } + } + const { _id: uid } = userMatched[0] + const { + passwordHash, + version + } = new PasswordUtils({ + clientInfo: this.getUniversalClientInfo(), + passwordSecret: this.config.passwordSecret + }).generatePasswordHash({ + password + }) + // 更新用户密码 + await userCollection.doc(uid).update({ + password: passwordHash, + password_secret_version: version, + valid_token_date: Date.now() + }) + + // 写入成功日志 + await this.middleware.uniIdLog({ + data: { + mobile + }, + type: LOG_TYPE.RESET_PWD_BY_SMS + }) + return { + errCode: 0 + } +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/account/set-pwd.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/account/set-pwd.js new file mode 100644 index 0000000..f33c6f4 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/account/set-pwd.js @@ -0,0 +1,83 @@ +const { userCollection, SMS_SCENE, LOG_TYPE, CAPTCHA_SCENE } = require('../../common/constants') +const { ERROR } = require('../../common/error') +const { verifyMobileCode } = require('../../lib/utils/verify-code') +const PasswordUtils = require('../../lib/utils/password') +const { getNeedCaptcha, verifyCaptcha } = require('../../lib/utils/captcha') + +module.exports = async function (params = {}) { + const schema = { + password: 'password', + code: 'string', + captcha: { + required: false, + type: 'string' + } + } + this.middleware.validate(params, schema) + + const { password, code, captcha } = params + const uid = this.authInfo.uid + const getUserRes = await userCollection.doc(uid).get() + const userRecord = getUserRes.data[0] + if (!userRecord) { + throw { + errCode: ERROR.ACCOUNT_NOT_EXISTS + } + } + + const needCaptcha = await getNeedCaptcha.call(this, { + mobile: userRecord.mobile + }) + + if (needCaptcha) { + await verifyCaptcha.call(this, { + captcha, + scene: CAPTCHA_SCENE.SET_PWD_BY_SMS + }) + } + + try { + // 验证手机号验证码,验证不通过时写入失败日志 + await verifyMobileCode({ + mobile: userRecord.mobile, + code, + scene: SMS_SCENE.SET_PWD_BY_SMS + }) + } catch (error) { + await this.middleware.uniIdLog({ + data: { + mobile: userRecord.mobile + }, + type: LOG_TYPE.SET_PWD_BY_SMS, + success: false + }) + throw error + } + + const { + passwordHash, + version + } = new PasswordUtils({ + clientInfo: this.getUniversalClientInfo(), + passwordSecret: this.config.passwordSecret + }).generatePasswordHash({ + password + }) + + // 更新用户密码 + await userCollection.doc(uid).update({ + password: passwordHash, + password_secret_version: version + }) + + await this.middleware.uniIdLog({ + data: { + mobile: userRecord.mobile + }, + type: LOG_TYPE.SET_PWD_BY_SMS + }) + + return { + errCode: 0 + } +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/account/update-pwd.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/account/update-pwd.js new file mode 100644 index 0000000..97fd1be --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/account/update-pwd.js @@ -0,0 +1,69 @@ +const { + userCollection +} = require('../../common/constants') +const { + ERROR +} = require('../../common/error') +const PasswordUtils = require('../../lib/utils/password') +/** + * 更新密码 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#update-pwd + * @param {object} params + * @param {string} params.oldPassword 旧密码 + * @param {string} params.newPassword 新密码 + * @returns {object} + */ +module.exports = async function (params = {}) { + const schema = { + oldPassword: 'string', // 防止密码规则调整导致旧密码无法更新 + newPassword: 'password' + } + this.middleware.validate(params, schema) + const uid = this.authInfo.uid + const getUserRes = await userCollection.doc(uid).get() + const userRecord = getUserRes.data[0] + if (!userRecord) { + throw { + errCode: ERROR.ACCOUNT_NOT_EXISTS + } + } + const { + oldPassword, + newPassword + } = params + const passwordUtils = new PasswordUtils({ + userRecord, + clientInfo: this.getUniversalClientInfo(), + passwordSecret: this.config.passwordSecret + }) + + const { + success: checkPasswordSuccess + } = passwordUtils.checkUserPassword({ + password: oldPassword, + autoRefresh: false + }) + + if (!checkPasswordSuccess) { + throw { + errCode: ERROR.PASSWORD_ERROR + } + } + + const { + passwordHash, + version + } = passwordUtils.generatePasswordHash({ + password: newPassword + }) + + await userCollection.doc(uid).update({ + password: passwordHash, + password_secret_version: version, + valid_token_date: Date.now() // refreshToken时会校验,如果创建token时间在此时间点之前,则拒绝下发新token,返回token失效错误码 + }) + // 执行更新密码操作后客户端应将用户退出重新登录 + return { + errCode: 0 + } +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/admin/add-user.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/admin/add-user.js new file mode 100644 index 0000000..330fd37 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/admin/add-user.js @@ -0,0 +1,131 @@ +const { + findUser +} = require('../../lib/utils/account') +const { + ERROR +} = require('../../common/error') +const { + userCollection +} = require('../../common/constants') +const PasswordUtils = require('../../lib/utils/password') + +/** + * 新增用户 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#add-user + * @param {Object} params + * @param {String} params.username 用户名 + * @param {String} params.password 密码 + * @param {String} params.nickname 昵称 + * @param {Array} params.authorizedApp 允许登录的AppID列表 + * @param {Array} params.role 用户角色列表 + * @param {String} params.mobile 手机号 + * @param {String} params.email 邮箱 + * @param {Array} params.tags 用户标签 + * @param {Number} params.status 用户状态 + * @returns + */ +module.exports = async function (params = {}) { + const schema = { + username: 'username', + password: 'password', + authorizedApp: { + required: false, + type: 'array' + }, // 指定允许登录的app,传空数组或不传时表示可以不可以在任何端登录 + nickname: { + required: false, + type: 'nickname' + }, + role: { + require: false, + type: 'array' + }, + mobile: { + required: false, + type: 'mobile' + }, + email: { + required: false, + type: 'email' + }, + tags: { + required: false, + type: 'array' + }, + status: { + required: false, + type: 'number' + } + } + this.middleware.validate(params, schema) + const { + username, + password, + authorizedApp, + nickname, + role, + mobile, + email, + tags, + status + } = params + const { + userMatched + } = await findUser({ + userQuery: { + username, + mobile, + email + }, + authorizedApp + }) + if (userMatched.length) { + throw { + errCode: ERROR.ACCOUNT_EXISTS + } + } + const passwordUtils = new PasswordUtils({ + clientInfo: this.getUniversalClientInfo(), + passwordSecret: this.config.passwordSecret + }) + const { + passwordHash, + version + } = passwordUtils.generatePasswordHash({ + password + }) + const data = { + username, + password: passwordHash, + password_secret_version: version, + dcloud_appid: authorizedApp || [], + nickname, + role: role || [], + mobile, + email, + tags: tags || [], + status + } + if (email) { + data.email_confirmed = 1 + } + if (mobile) { + data.mobile_confirmed = 1 + } + + // 触发 beforeRegister 钩子 + const beforeRegister = this.hooks.beforeRegister + let userRecord = data + if (beforeRegister) { + userRecord = await beforeRegister({ + userRecord, + clientInfo: this.getUniversalClientInfo() + }) + } + + await userCollection.add(userRecord) + return { + errCode: 0, + errMsg: '' + } +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/admin/index.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/admin/index.js new file mode 100644 index 0000000..c8830f5 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/admin/index.js @@ -0,0 +1,4 @@ +module.exports = { + addUser: require('./add-user'), + updateUser: require('./update-user') +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/admin/update-user.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/admin/update-user.js new file mode 100644 index 0000000..ed2f7b6 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/admin/update-user.js @@ -0,0 +1,138 @@ +const { + findUser +} = require('../../lib/utils/account') +const { + ERROR +} = require('../../common/error') +const { + userCollection +} = require('../../common/constants') +const PasswordUtils = require('../../lib/utils/password') + +/** + * 修改用户 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#update-user + * @param {Object} params + * @param {String} params.uid 要更新的用户id + * @param {String} params.username 用户名 + * @param {String} params.password 密码 + * @param {String} params.nickname 昵称 + * @param {Array} params.authorizedApp 允许登录的AppID列表 + * @param {Array} params.role 用户角色列表 + * @param {String} params.mobile 手机号 + * @param {String} params.email 邮箱 + * @param {Array} params.tags 用户标签 + * @param {Number} params.status 用户状态 + * @returns + */ +module.exports = async function (params = {}) { + const schema = { + uid: 'string', + username: 'username', + password: { + required: false, + type: 'password' + }, + authorizedApp: { + required: false, + type: 'array' + }, // 指定允许登录的app,传空数组或不传时表示可以不可以在任何端登录 + nickname: { + required: false, + type: 'nickname' + }, + role: { + require: false, + type: 'array' + }, + mobile: { + required: false, + type: 'mobile' + }, + email: { + required: false, + type: 'email' + }, + tags: { + required: false, + type: 'array' + }, + status: { + required: false, + type: 'number' + } + } + + this.middleware.validate(params, schema) + + const { + uid, + username, + password, + authorizedApp, + nickname, + role, + mobile, + email, + tags, + status + } = params + + // 更新的用户数据字段 + const data = { + username, + dcloud_appid: authorizedApp, + nickname, + role, + mobile, + email, + tags, + status + } + + const realData = Object.keys(data).reduce((res, key) => { + const item = data[key] + if (item !== undefined) { + res[key] = item + } + return res + }, {}) + + // 更新用户名时验证用户名是否重新 + if (username) { + const { + userMatched + } = await findUser({ + userQuery: { + username + }, + authorizedApp + }) + if (userMatched.filter(user => user._id !== uid).length) { + throw { + errCode: ERROR.ACCOUNT_EXISTS + } + } + } + if (password) { + const passwordUtils = new PasswordUtils({ + clientInfo: this.getUniversalClientInfo(), + passwordSecret: this.config.passwordSecret + }) + const { + passwordHash, + version + } = passwordUtils.generatePasswordHash({ + password + }) + + realData.password = passwordHash + realData.password_secret_version = version + } + + await userCollection.doc(uid).update(realData) + + return { + errCode: 0 + } +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/dev/get-supported-login-type.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/dev/get-supported-login-type.js new file mode 100644 index 0000000..c28f3bd --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/dev/get-supported-login-type.js @@ -0,0 +1,70 @@ +function isMobileCodeSupported () { + const config = this.config + return !!(config.service && config.service.sms && config.service.sms.smsKey) +} + +function isUniverifySupport () { + return true +} + +function isWeixinSupported () { + this.configUtils.getOauthConfig({ + provider: 'weixin' + }) + return true +} + +function isQQSupported () { + this.configUtils.getOauthConfig({ + provider: 'qq' + }) + return true +} + +function isAppleSupported () { + this.configUtils.getOauthConfig({ + provider: 'apple' + }) + return true +} + +function isAlipaySupported () { + this.configUtils.getOauthConfig({ + provider: 'alipay' + }) + return true +} + +const loginTypeTester = { + 'mobile-code': isMobileCodeSupported, + univerify: isUniverifySupport, + weixin: isWeixinSupported, + qq: isQQSupported, + apple: isAppleSupported, + alipay: isAlipaySupported +} + +/** + * 获取支持的登录方式 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#get-supported-login-type + * @returns + */ +module.exports = async function () { + const supportedLoginType = [ + 'username-password', + 'mobile-password', + 'email-password' + ] + for (const type in loginTypeTester) { + try { + if (loginTypeTester[type].call(this)) { + supportedLoginType.push(type) + } + } catch (error) { } + } + return { + errCode: 0, + errMsg: '', + supportedLoginType + } +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/dev/index.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/dev/index.js new file mode 100644 index 0000000..e22f9f2 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/dev/index.js @@ -0,0 +1,3 @@ +module.exports = { + getSupportedLoginType: require('./get-supported-login-type') +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/external/index.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/external/index.js new file mode 100644 index 0000000..6fa597f --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/external/index.js @@ -0,0 +1,5 @@ +module.exports = { + externalRegister: require('./register'), + externalLogin: require('./login'), + updateUserInfoByExternal: require('./update-user-info') +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/external/login.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/external/login.js new file mode 100644 index 0000000..af13013 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/external/login.js @@ -0,0 +1,68 @@ +const { preLogin, postLogin } = require('../../lib/utils/login') +const { EXTERNAL_DIRECT_CONNECT_PROVIDER } = require('../../common/constants') +const { ERROR } = require('../../common/error') + +/** + * 外部用户登录 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#external-login + * @param {object} params + * @param {string} params.uid uni-id体系用户id + * @param {string} params.externalUid 业务系统的用户id + * @returns {object} + */ +module.exports = async function (params = {}) { + const schema = { + uid: { + required: false, + type: 'string' + }, + externalUid: { + required: false, + type: 'string' + } + } + + this.middleware.validate(params, schema) + + const { + uid, + externalUid + } = params + + if (!uid && !externalUid) { + throw { + errCode: ERROR.PARAM_REQUIRED, + errMsgValue: { + param: 'uid or externalUid' + } + } + } + + let query + if (uid) { + query = { + _id: uid + } + } else { + query = { + identities: { + provider: EXTERNAL_DIRECT_CONNECT_PROVIDER, + uid: externalUid + } + } + } + + const user = await preLogin.call(this, { + user: query + }) + + const result = await postLogin.call(this, { + user + }) + + return { + errCode: result.errCode, + newToken: result.newToken, + uid: result.uid + } +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/external/register.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/external/register.js new file mode 100644 index 0000000..1b2279c --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/external/register.js @@ -0,0 +1,93 @@ +const url = require('url') +const { preRegister, postRegister } = require('../../lib/utils/register') +const { EXTERNAL_DIRECT_CONNECT_PROVIDER } = require('../../common/constants') + +/** + * 外部注册用户 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#external-register + * @param {object} params + * @param {string} params.externalUid 业务系统的用户id + * @param {string} params.nickname 昵称 + * @param {number} params.gender 性别 + * @param {string} params.avatar 头像 + * @returns {object} + */ +module.exports = async function (params = {}) { + const schema = { + externalUid: 'string', + nickname: { + required: false, + type: 'nickname' + }, + gender: { + required: false, + type: 'number' + }, + avatar: { + required: false, + type: 'string' + } + } + + this.middleware.validate(params, schema) + + const { + externalUid, + avatar, + gender, + nickname + } = params + + await preRegister.call(this, { + user: { + identities: { + provider: EXTERNAL_DIRECT_CONNECT_PROVIDER, + uid: externalUid + } + } + }) + + const extraData = {} + + if (avatar) { + // eslint-disable-next-line n/no-deprecated-api + const avatarPath = url.parse(avatar).pathname + const extName = avatarPath.indexOf('.') > -1 ? avatarPath.split('.').pop() : '' + + extraData.avatar_file = { + name: avatarPath, + extname: extName, + url: avatar + } + } + + const result = await postRegister.call(this, { + user: { + avatar, + gender, + nickname, + identities: [ + { + provider: EXTERNAL_DIRECT_CONNECT_PROVIDER, + userInfo: { + avatar, + gender, + nickname + }, + uid: externalUid + } + ] + }, + extraData + }) + + return { + errCode: result.errCode, + newToken: result.newToken, + externalUid, + avatar, + gender, + nickname, + uid: result.uid + } +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/external/update-user-info.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/external/update-user-info.js new file mode 100644 index 0000000..a91fe9f --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/external/update-user-info.js @@ -0,0 +1,208 @@ +const url = require('url') +const { userCollection, EXTERNAL_DIRECT_CONNECT_PROVIDER } = require('../../common/constants') +const { ERROR } = require('../../common/error') +const { findUser } = require('../../lib/utils/account') +const PasswordUtils = require('../../lib/utils/password') + +/** + * 使用 uid 或 externalUid 获取用户信息 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#external-update-userinfo + * @param {object} params + * @param {string} params.uid uni-id体系的用户id + * @param {string} params.externalUid 业务系统的用户id + * @param {string} params.nickname 昵称 + * @param {string} params.gender 性别 + * @param {string} params.avatar 头像 + * @returns {object} + */ +module.exports = async function (params = {}) { + const schema = { + uid: { + required: false, + type: 'string' + }, + externalUid: { + required: false, + type: 'string' + }, + username: { + required: false, + type: 'string' + }, + password: { + required: false, + type: 'password' + }, + authorizedApp: { + required: false, + type: 'array' + }, // 指定允许登录的app,传空数组或不传时表示可以不可以在任何端登录 + nickname: { + required: false, + type: 'nickname' + }, + role: { + require: false, + type: 'array' + }, + mobile: { + required: false, + type: 'mobile' + }, + email: { + required: false, + type: 'email' + }, + tags: { + required: false, + type: 'array' + }, + status: { + required: false, + type: 'number' + }, + gender: { + required: false, + type: 'number' + }, + avatar: { + required: false, + type: 'string' + } + } + + this.middleware.validate(params, schema) + + const { + uid, + externalUid, + username, + password, + authorizedApp, + nickname, + role, + mobile, + email, + tags, + status, + avatar, + gender + } = params + + if (!uid && !externalUid) { + throw { + errCode: ERROR.PARAM_REQUIRED, + errMsgValue: { + param: 'uid or externalUid' + } + } + } + + let query + if (uid) { + query = { + _id: uid + } + } else { + query = { + identities: { + provider: EXTERNAL_DIRECT_CONNECT_PROVIDER, + uid: externalUid + } + } + } + + const users = await userCollection.where(query).get() + const user = users.data && users.data[0] + if (!user) { + throw { + errCode: ERROR.ACCOUNT_NOT_EXISTS + } + } + + // 更新的用户数据字段 + const data = { + username, + dcloud_appid: authorizedApp, + nickname, + role, + mobile, + email, + tags, + status, + avatar, + gender + } + + const realData = Object.keys(data).reduce((res, key) => { + const item = data[key] + if (item !== undefined) { + res[key] = item + } + return res + }, {}) + + // 更新用户名时验证用户名是否重新 + if (username) { + const { + userMatched + } = await findUser({ + userQuery: { + username + }, + authorizedApp + }) + if (userMatched.filter(user => user._id !== uid).length) { + throw { + errCode: ERROR.ACCOUNT_EXISTS + } + } + } + if (password) { + const passwordUtils = new PasswordUtils({ + clientInfo: this.getUniversalClientInfo(), + passwordSecret: this.config.passwordSecret + }) + const { + passwordHash, + version + } = passwordUtils.generatePasswordHash({ + password + }) + + realData.password = passwordHash + realData.password_secret_version = version + } + + if (avatar) { + // eslint-disable-next-line n/no-deprecated-api + const avatarPath = url.parse(avatar).pathname + const extName = avatarPath.indexOf('.') > -1 ? avatarPath.split('.').pop() : '' + + realData.avatar_file = { + name: avatarPath, + extname: extName, + url: avatar + } + } + + if (user.identities.length) { + const identity = user.identities.find(item => item.provider === EXTERNAL_DIRECT_CONNECT_PROVIDER) + + if (identity) { + identity.userInfo = { + avatar, + gender, + nickname + } + } + + realData.identities = user.identities + } + + await userCollection.where(query).update(realData) + + return { + errCode: 0 + } +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/facial-recognition-verify/get-auth-result.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/facial-recognition-verify/get-auth-result.js new file mode 100644 index 0000000..7017c33 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/facial-recognition-verify/get-auth-result.js @@ -0,0 +1,136 @@ +const { userCollection, REAL_NAME_STATUS, frvLogsCollection } = require('../../common/constants') +const { dataDesensitization, catchAwait } = require('../../common/utils') +const { encryptData, decryptData } = require('../../common/sensitive-aes-cipher') +const { ERROR } = require('../../common/error') + +/** + * 查询认证结果 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#get-frv-auth-result + * @param {Object} params + * @param {String} params.certifyId 认证ID + * @returns + */ +module.exports = async function (params) { + const schema = { + certifyId: 'string' + } + + this.middleware.validate(params, schema) + + const { uid } = this.authInfo // 从authInfo中取出uid属性 + const { certifyId } = params // 从params中取出certifyId属性 + + const user = await userCollection.doc(uid).get() // 根据uid查询用户信息 + const userInfo = user.data && user.data[0] // 从查询结果中获取userInfo对象 + + // 如果用户不存在,抛出账户不存在的错误 + if (!userInfo) { + throw { + errCode: ERROR.ACCOUNT_NOT_EXISTS + } + } + + const { realname_auth: realNameAuth = {} } = userInfo + + // 如果用户已经实名认证,抛出已实名认证的错误 + if (realNameAuth.auth_status === REAL_NAME_STATUS.CERTIFIED) { + throw { + errCode: ERROR.REAL_NAME_VERIFIED + } + } + + // 初始化实人认证服务 + const frvManager = uniCloud.getFacialRecognitionVerifyManager({ + requestId: this.getUniCloudRequestId() + }) + + // 调用frvManager的getAuthResult方法,获取认证结果 + const [error, res] = await catchAwait(frvManager.getAuthResult({ + certifyId + })) + + // 如果出现错误,抛出未知错误并打印日志 + if (error) { + console.log(ERROR.UNKNOWN_ERROR, 'error: ', error) + throw error + } + + // 如果认证状态为“PROCESSING”,抛出认证正在处理中的错误 + if (res.authState === 'PROCESSING') { + throw { + errCode: ERROR.FRV_PROCESSING + } + } + + // 如果认证状态为“FAIL”,更新认证日志的状态并抛出认证失败的错误 + if (res.authState === 'FAIL') { + await frvLogsCollection.where({ + certify_id: certifyId + }).update({ + status: REAL_NAME_STATUS.CERTIFY_FAILED + }) + + console.log(ERROR.FRV_FAIL, 'error: ', res) + throw { + errCode: ERROR.FRV_FAIL + } + } + + // 如果认证状态不为“SUCCESS”,抛出未知错误并打印日志 + if (res.authState !== 'SUCCESS') { + console.log(ERROR.UNKNOWN_ERROR, 'source res: ', res) + throw { + errCode: ERROR.UNKNOWN_ERROR + } + } + + // 根据certifyId查询认证记录 + const frvLogs = await frvLogsCollection.where({ + certify_id: certifyId + }).get() + + const log = frvLogs.data && frvLogs.data[0] + + const updateData = { + realname_auth: { + auth_status: REAL_NAME_STATUS.CERTIFIED, + real_name: log.real_name, + identity: log.identity, + auth_date: Date.now(), + type: 0 + } + } + + // 如果获取到了认证照片的地址,则会对其进行下载,并使用uniCloud.uploadFile方法将其上传到云存储,并将上传后的fileID保存起来。 + if (res.pictureUrl) { + const pictureRes = await uniCloud.httpclient.request(res.pictureUrl) + if (pictureRes.status < 400) { + const { + fileID + } = await uniCloud.uploadFile({ + cloudPath: `user/id-card/${uid}.b64`, + cloudPathAsRealPath: true, + fileContent: Buffer.from(encryptData.call(this, pictureRes.data.toString('base64'))) + }) + updateData.realname_auth.in_hand = fileID + } + } + + await Promise.all([ + // 更新用户认证状态 + userCollection.doc(uid).update(updateData), + // 更新实人认证记录状态 + frvLogsCollection.where({ + certify_id: certifyId + }).update({ + status: REAL_NAME_STATUS.CERTIFIED + }) + ]) + + return { + errCode: 0, + authStatus: REAL_NAME_STATUS.CERTIFIED, + realName: dataDesensitization(decryptData.call(this, log.real_name), { onlyLast: true }), // 对姓名进行脱敏处理 + identity: dataDesensitization(decryptData.call(this, log.identity)) // 对身份证号进行脱敏处理 + } +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/facial-recognition-verify/get-certify-id.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/facial-recognition-verify/get-certify-id.js new file mode 100644 index 0000000..cb8b48b --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/facial-recognition-verify/get-certify-id.js @@ -0,0 +1,99 @@ +const { userCollection, REAL_NAME_STATUS, frvLogsCollection, dbCmd } = require('../../common/constants') +const { ERROR } = require('../../common/error') +const { encryptData } = require('../../common/sensitive-aes-cipher') +const { getCurrentDateTimestamp } = require('../../common/utils') + +// const CertifyIdExpired = 25 * 60 * 1000 // certifyId 过期时间为30分钟,在25分时置为过期 + +/** + * 获取认证ID + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#get-frv-certify-id + * @param {Object} params + * @param {String} params.realName 真实姓名 + * @param {String} params.idCard 身份证号码 + * @param {String} params.metaInfo 客户端初始化时返回的metaInfo + * @returns + */ +module.exports = async function (params) { + const schema = { + realName: 'realName', + idCard: 'idCard', + metaInfo: 'string' + } + + this.middleware.validate(params, schema) + + const { realName: originalRealName, idCard: originalIdCard, metaInfo } = params // 解构出传入参数的真实姓名、身份证号码、其他元数据 + const realName = encryptData.call(this, originalRealName) // 对真实姓名进行加密处理 + const idCard = encryptData.call(this, originalIdCard) // 对身份证号码进行加密处理 + + const { uid } = this.authInfo // 获取当前用户的 ID + const idCardCertifyLimit = this.config.idCardCertifyLimit || 1 // 获取身份证认证限制次数,默认为1次 + const realNameCertifyLimit = this.config.realNameCertifyLimit || 5 // 获取实名认证限制次数,默认为5次 + const frvNeedAlivePhoto = this.config.frvNeedAlivePhoto || false // 是否需要拍摄活体照片,默认为 false + + const user = await userCollection.doc(uid).get() // 获取用户信息 + const userInfo = user.data && user.data[0] // 获取用户信息对象中的实名认证信息 + const { realname_auth: realNameAuth = {} } = userInfo // 解构出实名认证信息中的认证状态对象,默认为空对象 + + // 如果用户已经实名认证过,不能再次认证 + if (realNameAuth.auth_status === REAL_NAME_STATUS.CERTIFIED) { + throw { + errCode: ERROR.REAL_NAME_VERIFIED + } + } + + // 查询已经使用同一个身份证认证的账号数量,如果超过限制则不能认证 + const idCardAccount = await userCollection.where({ + realname_auth: { + type: 0, // 用户认证状态是个人 + auth_status: REAL_NAME_STATUS.CERTIFIED, // 认证状态为已认证 + identity: idCard // 身份证号码和传入参数的身份证号码相同 + } + }).get() + if (idCardAccount.data.length >= idCardCertifyLimit) { + throw { + errCode: ERROR.ID_CARD_EXISTS + } + } + + // 查询用户今天已经进行的实名认证次数,如果超过限制则不能认证 + const userFrvLogs = await frvLogsCollection.where({ + user_id: uid, + created_date: dbCmd.gt(getCurrentDateTimestamp()) // 查询今天的认证记录 + }).get() + + // 限制用户每日认证次数 + if (userFrvLogs.data && userFrvLogs.data.length >= realNameCertifyLimit) { + throw { + errCode: ERROR.REAL_NAME_VERIFY_UPPER_LIMIT + } + } + + // 初始化实人认证服务 + const frvManager = uniCloud.getFacialRecognitionVerifyManager({ + requestId: this.getUniCloudRequestId() // 获取当前 + }) + // 调用实人认证服务,获取认证 ID + const res = await frvManager.getCertifyId({ + realName: originalRealName, + idCard: originalIdCard, + needPicture: frvNeedAlivePhoto, + metaInfo + }) + + // 将认证记录插入到实名认证日志中 + await frvLogsCollection.add({ + user_id: uid, + certify_id: res.certifyId, + real_name: realName, + identity: idCard, + status: REAL_NAME_STATUS.WAITING_CERTIFIED, + created_date: Date.now() + }) + + // 返回认证ID + return { + certifyId: res.certifyId + } +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/facial-recognition-verify/index.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/facial-recognition-verify/index.js new file mode 100644 index 0000000..63f6b1f --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/facial-recognition-verify/index.js @@ -0,0 +1,4 @@ +module.exports = { + getFrvCertifyId: require('./get-certify-id'), + getFrvAuthResult: require('./get-auth-result') +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/fission/accept-invite.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/fission/accept-invite.js new file mode 100644 index 0000000..2461e06 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/fission/accept-invite.js @@ -0,0 +1,25 @@ +const { + acceptInvite +} = require('../../lib/utils/fission') + +/** + * 接受邀请 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#accept-invite + * @param {Object} params + * @param {String} params.inviteCode 邀请码 + * @returns + */ +module.exports = async function (params = {}) { + const schema = { + inviteCode: 'string' + } + this.middleware.validate(params, schema) + const { + inviteCode + } = params + const uid = this.authInfo.uid + return acceptInvite({ + uid, + inviteCode + }) +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/fission/get-invited-user.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/fission/get-invited-user.js new file mode 100644 index 0000000..93d4671 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/fission/get-invited-user.js @@ -0,0 +1,80 @@ +const { + userCollection +} = require('../../common/constants') +const { + coverMobile +} = require('../../common/utils') + +/** + * 获取受邀用户 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#get-invited-user + * @param {Object} params + * @param {Number} params.level 获取受邀用户的级数,1表示直接邀请的用户 + * @param {Number} params.limit 返回数据大小 + * @param {Number} params.offset 返回数据偏移 + * @param {Boolean} params.needTotal 是否需要返回总数 + * @returns + */ +module.exports = async function (params = {}) { + const schema = { + level: 'number', + limit: { + required: false, + type: 'number' + }, + offset: { + required: false, + type: 'number' + }, + needTotal: { + required: false, + type: 'boolean' + } + } + this.middleware.validate(params, schema) + const { + level, + limit = 20, + offset = 0, + needTotal = false + } = params + const uid = this.authInfo.uid + const query = { + [`inviter_uid.${level - 1}`]: uid + } + const getUserRes = await userCollection.where(query) + .field({ + _id: true, + avatar: true, + avatar_file: true, + username: true, + nickname: true, + mobile: true, + invite_time: true + }) + .orderBy('invite_time', 'desc') + .skip(offset) + .limit(limit) + .get() + + const invitedUser = getUserRes.data.map(item => { + return { + uid: item._id, + username: item.username, + nickname: item.nickname, + mobile: coverMobile(item.mobile), + inviteTime: item.invite_time, + avatar: item.avatar, + avatarFile: item.avatar_file + } + }) + const result = { + errCode: 0, + invitedUser + } + if (needTotal) { + const getTotalRes = await userCollection.where(query).count() + result.total = getTotalRes.total + } + return result +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/fission/index.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/fission/index.js new file mode 100644 index 0000000..4a9bee1 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/fission/index.js @@ -0,0 +1,4 @@ +module.exports = { + acceptInvite: require('./accept-invite'), + getInvitedUser: require('./get-invited-user') +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/index.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/index.js new file mode 100644 index 0000000..f65f58b --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/index.js @@ -0,0 +1,20 @@ +module.exports = { + login: require('./login'), + loginBySms: require('./login-by-sms'), + loginByUniverify: require('./login-by-univerify'), + loginByWeixin: require('./login-by-weixin'), + loginByAlipay: require('./login-by-alipay'), + loginByQQ: require('./login-by-qq'), + loginByApple: require('./login-by-apple'), + loginByBaidu: require('./login-by-baidu'), + loginByDingtalk: require('./login-by-dingtalk'), + loginByToutiao: require('./login-by-toutiao'), + loginByDouyin: require('./login-by-douyin'), + loginByWeibo: require('./login-by-weibo'), + loginByTaobao: require('./login-by-taobao'), + loginByEmailLink: require('./login-by-email-link'), + loginByEmailCode: require('./login-by-email-code'), + loginByFacebook: require('./login-by-facebook'), + loginByGoogle: require('./login-by-google'), + loginByWeixinMobile: require('./login-by-weixin-mobile') +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-alipay.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-alipay.js new file mode 100644 index 0000000..d5d4631 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-alipay.js @@ -0,0 +1,70 @@ +const { + initAlipay +} = require('../../lib/third-party/index') +const { + ERROR +} = require('../../common/error') +const { + preUnifiedLogin, + postUnifiedLogin +} = require('../../lib/utils/unified-login') +const { + LOG_TYPE +} = require('../../common/constants') + +/** + * 支付宝登录 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#login-by-alipay + * @param {Object} params + * @param {String} params.code 支付宝小程序客户端登录返回的code + * @param {String} params.inviteCode 邀请码 + * @returns + */ +module.exports = async function (params = {}) { + const schema = { + code: 'string', + inviteCode: { + type: 'string', + required: false + } + } + this.middleware.validate(params, schema) + const { + code, + inviteCode + } = params + const alipayApi = initAlipay.call(this) + let getAlipayAccountResult + try { + getAlipayAccountResult = await alipayApi.code2Session(code) + } catch (error) { + console.error(error) + await this.middleware.uniIdLog({ + success: false, + type: LOG_TYPE.LOGIN + }) + throw { + errCode: ERROR.GET_THIRD_PARTY_ACCOUNT_FAILED + } + } + + const { + openid + } = getAlipayAccountResult + + const { + type, + user + } = await preUnifiedLogin.call(this, { + user: { + ali_openid: openid + } + }) + return postUnifiedLogin.call(this, { + user, + extraData: {}, + isThirdParty: true, + type, + inviteCode + }) +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-apple.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-apple.js new file mode 100644 index 0000000..5f39e62 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-apple.js @@ -0,0 +1,77 @@ +const { + initApple +} = require('../../lib/third-party/index') +const { + ERROR +} = require('../../common/error') +const { + preUnifiedLogin, + postUnifiedLogin +} = require('../../lib/utils/unified-login') +const { + LOG_TYPE +} = require('../../common/constants') + +/** + * 苹果登录 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#login-by-apple + * @param {Object} params + * @param {String} params.identityToken 苹果登录返回的identityToken + * @param {String} params.nickname 用户昵称 + * @param {String} params.inviteCode 邀请码 + * @returns + */ +module.exports = async function (params = {}) { + const schema = { + identityToken: 'string', + nickname: { + required: false, + type: 'nickname' + }, + inviteCode: { + required: false, + type: 'string' + } + } + this.middleware.validate(params, schema) + const { + identityToken, + nickname, + inviteCode + } = params + const appleApi = initApple.call(this) + let verifyResult + try { + verifyResult = await appleApi.verifyIdentityToken(identityToken) + } catch (error) { + console.error(error) + await this.middleware.uniIdLog({ + success: false, + type: LOG_TYPE.LOGIN + }) + throw { + errCode: ERROR.GET_THIRD_PARTY_ACCOUNT_FAILED + } + } + const { + openid + } = verifyResult + + const { + type, + user + } = await preUnifiedLogin.call(this, { + user: { + apple_openid: openid + } + }) + return postUnifiedLogin.call(this, { + user, + extraData: { + nickname + }, + isThirdParty: true, + type, + inviteCode + }) +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-baidu.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-baidu.js new file mode 100644 index 0000000..856449d --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-baidu.js @@ -0,0 +1,9 @@ +/** + * 百度登录 + * @param {Object} params + * @returns + */ +module.exports = async function (params = {}) { + // 此接口暂未实现,欢迎向我们提交pr + throw new Error('api[loginByBaidu] is not yet implemented') +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-dingtalk.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-dingtalk.js new file mode 100644 index 0000000..afe1f01 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-dingtalk.js @@ -0,0 +1,9 @@ +/** + * 钉钉登录 + * @param {Object} params + * @returns + */ +module.exports = async function (params = {}) { + // 此接口暂未实现,欢迎向我们提交pr + throw new Error('api[loginByDingtalk] is not yet implemented') +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-douyin.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-douyin.js new file mode 100644 index 0000000..8cd4ab5 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-douyin.js @@ -0,0 +1,9 @@ +/** + * 抖音登录 + * @param {Object} params + * @returns + */ +module.exports = async function (params = {}) { + // 此接口暂未实现,欢迎向我们提交pr + throw new Error('api[loginByDouyin] is not yet implemented') +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-email-code.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-email-code.js new file mode 100644 index 0000000..c3af08f --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-email-code.js @@ -0,0 +1,9 @@ +/** + * 邮箱验证码登录 + * @param {Object} params + * @returns + */ +module.exports = async function (params = {}) { + // 此接口暂未实现,欢迎向我们提交pr + throw new Error('api[loginByEmailCode] is not yet implemented') +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-email-link.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-email-link.js new file mode 100644 index 0000000..0ebbf3a --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-email-link.js @@ -0,0 +1,9 @@ +/** + * 邮箱点击链接登录 + * @param {Object} params + * @returns + */ +module.exports = async function (params = {}) { + // 此接口暂未实现,欢迎向我们提交pr + throw new Error('api[loginByEmailLink] is not yet implemented') +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-facebook.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-facebook.js new file mode 100644 index 0000000..5c93bd4 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-facebook.js @@ -0,0 +1,9 @@ +/** + * Facebook登录 + * @param {Object} params + * @returns + */ +module.exports = async function (params = {}) { + // 此接口暂未实现,欢迎向我们提交pr + throw new Error('api[loginByFacebook] is not yet implemented') +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-google.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-google.js new file mode 100644 index 0000000..8054ece --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-google.js @@ -0,0 +1,9 @@ +/** + * Google登录 + * @param {Object} params + * @returns + */ +module.exports = async function (params = {}) { + // 此接口暂未实现,欢迎向我们提交pr + throw new Error('api[loginByGoogle] is not yet implemented') +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-qq.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-qq.js new file mode 100644 index 0000000..1eaa7ba --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-qq.js @@ -0,0 +1,167 @@ +const { + initQQ +} = require('../../lib/third-party/index') +const { + ERROR +} = require('../../common/error') +const { + preUnifiedLogin, + postUnifiedLogin +} = require('../../lib/utils/unified-login') +const { + LOG_TYPE +} = require('../../common/constants') +const { + getQQPlatform, + generateQQCache, + saveQQUserKey +} = require('../../lib/utils/qq') +const url = require('url') + +/** + * QQ登录 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#login-by-qq + * @param {Object} params + * @param {String} params.code QQ小程序登录返回的code参数 + * @param {String} params.accessToken App端QQ登录返回的accessToken参数 + * @param {String} params.accessTokenExpired accessToken过期时间,由App端QQ登录返回的expires_in参数计算而来,单位:毫秒 + * @param {String} params.inviteCode 邀请码 + * @returns + */ +module.exports = async function (params = {}) { + const schema = { + code: { + type: 'string', + required: false + }, + accessToken: { + type: 'string', + required: false + }, + accessTokenExpired: { + type: 'number', + required: false + }, + inviteCode: { + type: 'string', + required: false + } + } + this.middleware.validate(params, schema) + const { + code, + accessToken, + accessTokenExpired, + inviteCode + } = params + const { + appId + } = this.getUniversalClientInfo() + const qqApi = initQQ.call(this) + const qqPlatform = getQQPlatform.call(this) + let apiName + switch (qqPlatform) { + case 'mp': + apiName = 'code2Session' + break + case 'app': + apiName = 'getOpenidByToken' + break + default: + throw new Error('Unsupported qq platform') + } + let getQQAccountResult + try { + getQQAccountResult = await qqApi[apiName]({ + code, + accessToken + }) + } catch (error) { + console.error(error) + await this.middleware.uniIdLog({ + success: false, + type: LOG_TYPE.LOGIN + }) + throw { + errCode: ERROR.GET_THIRD_PARTY_ACCOUNT_FAILED + } + } + + const { + openid, + unionid, + // 保存下面的字段 + sessionKey // QQ小程序用户sessionKey + } = getQQAccountResult + + const { + type, + user + } = await preUnifiedLogin.call(this, { + user: { + qq_openid: { + [qqPlatform]: openid + }, + qq_unionid: unionid + } + }) + const extraData = { + qq_openid: { + [`${qqPlatform}_${appId}`]: openid + }, + qq_unionid: unionid + } + if (type === 'register' && qqPlatform !== 'mp') { + const { + nickname, + avatar + } = await qqApi.getUserInfo({ + accessToken, + openid + }) + if (avatar) { + // eslint-disable-next-line n/no-deprecated-api + const extName = url.parse(avatar).pathname.split('.').pop() + const cloudPath = `user/avatar/${openid.slice(-8) + Date.now()}-avatar.${extName}` + const getAvatarRes = await uniCloud.httpclient.request(avatar) + if (getAvatarRes.status >= 400) { + throw { + errCode: ERROR.GET_THIRD_PARTY_USER_INFO_FAILED + } + } + const { + fileID + } = await uniCloud.uploadFile({ + cloudPath, + fileContent: getAvatarRes.data + }) + extraData.avatar_file = { + name: cloudPath, + extname: extName, + url: fileID + } + } + extraData.nickname = nickname + } + await saveQQUserKey.call(this, { + openid, + sessionKey, + accessToken, + accessTokenExpired + }) + return postUnifiedLogin.call(this, { + user, + extraData: { + ...extraData, + ...generateQQCache.call(this, { + openid, + sessionKey, // QQ小程序用户sessionKey + accessToken, // App端QQ用户accessToken + accessTokenExpired // App端QQ用户accessToken过期时间 + }) + }, + isThirdParty: true, + type, + inviteCode + }) +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-sms.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-sms.js new file mode 100644 index 0000000..915e9b6 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-sms.js @@ -0,0 +1,99 @@ +const { + getNeedCaptcha, + verifyCaptcha +} = require('../../lib/utils/captcha') +const { + verifyMobileCode +} = require('../../lib/utils/verify-code') +const { + preUnifiedLogin, + postUnifiedLogin +} = require('../../lib/utils/unified-login') +const { + CAPTCHA_SCENE, + SMS_SCENE, + LOG_TYPE +} = require('../../common/constants') + +/** + * 短信验证码登录 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#login-by-sms + * @param {Object} params + * @param {String} params.mobile 手机号 + * @param {String} params.code 短信验证码 + * @param {String} params.captcha 图形验证码 + * @param {String} params.inviteCode 邀请码 + * @returns + */ +module.exports = async function (params = {}) { + const schema = { + mobile: 'mobile', + code: 'string', + captcha: { + required: false, + type: 'string' + }, + inviteCode: { + required: false, + type: 'string' + } + } + this.middleware.validate(params, schema) + const { + mobile, + code, + captcha, + inviteCode + } = params + + const needCaptcha = await getNeedCaptcha.call(this, { + mobile + }) + + if (needCaptcha) { + await verifyCaptcha.call(this, { + captcha, + scene: CAPTCHA_SCENE.LOGIN_BY_SMS + }) + } + + try { + await verifyMobileCode({ + mobile, + code, + scene: SMS_SCENE.LOGIN_BY_SMS + }) + } catch (error) { + console.log(error, { + mobile, + code, + type: SMS_SCENE.LOGIN_BY_SMS + }) + await this.middleware.uniIdLog({ + success: false, + data: { + mobile + }, + type: LOG_TYPE.LOGIN + }) + throw error + } + + const { + type, + user + } = await preUnifiedLogin.call(this, { + user: { + mobile + } + }) + return postUnifiedLogin.call(this, { + user, + extraData: { + mobile_confirmed: 1 + }, + isThirdParty: false, + type, + inviteCode + }) +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-taobao.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-taobao.js new file mode 100644 index 0000000..6a6d599 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-taobao.js @@ -0,0 +1,9 @@ +/** + * 淘宝登录 + * @param {Object} params + * @returns + */ +module.exports = async function (params = {}) { + // 此接口暂未实现,欢迎向我们提交pr + throw new Error('api[loginByTaobao] is not yet implemented') +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-toutiao.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-toutiao.js new file mode 100644 index 0000000..133aadb --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-toutiao.js @@ -0,0 +1,9 @@ +/** + * 头条登录 + * @param {Object} params + * @returns + */ +module.exports = async function (params = {}) { + // 此接口暂未实现,欢迎向我们提交pr + throw new Error('api[loginByToutiao] is not yet implemented') +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-univerify.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-univerify.js new file mode 100644 index 0000000..53e681c --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-univerify.js @@ -0,0 +1,69 @@ +const { + getPhoneNumber +} = require('../../lib/utils/univerify') +const { + preUnifiedLogin, + postUnifiedLogin +} = require('../../lib/utils/unified-login') +const { + LOG_TYPE +} = require('../../common/constants') + +/** + * App端一键登录 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#login-by-univerify + * @param {Object} params + * @param {String} params.access_token APP端一键登录返回的access_token + * @param {String} params.openid APP端一键登录返回的openid + * @param {String} params.inviteCode 邀请码 + * @returns + */ +module.exports = async function (params = {}) { + const schema = { + access_token: 'string', + openid: 'string', + inviteCode: { + required: false, + type: 'string' + } + } + this.middleware.validate(params, schema) + const { + // eslint-disable-next-line camelcase + access_token, + openid, + inviteCode + } = params + + let mobile + try { + const phoneInfo = await getPhoneNumber.call(this, { + // eslint-disable-next-line camelcase + access_token, + openid + }) + mobile = phoneInfo.phoneNumber + } catch (error) { + await this.middleware.uniIdLog({ + success: false, + type: LOG_TYPE.LOGIN + }) + throw error + } + const { + user, + type + } = await preUnifiedLogin.call(this, { + user: { + mobile + } + }) + return postUnifiedLogin.call(this, { + user, + extraData: { + mobile_confirmed: 1 + }, + type, + inviteCode + }) +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-weibo.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-weibo.js new file mode 100644 index 0000000..496cdb4 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-weibo.js @@ -0,0 +1,9 @@ +/** + * 微博登录 + * @param {Object} params + * @returns + */ +module.exports = async function (params = {}) { + // 此接口暂未实现,欢迎向我们提交pr + throw new Error('api[loginByWeibo] is not yet implemented') +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-weixin-mobile.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-weixin-mobile.js new file mode 100644 index 0000000..c27c2b2 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-weixin-mobile.js @@ -0,0 +1,106 @@ +const { + initWeixin +} = require('../../lib/third-party/index') +const { + getWeixinAccessToken +} = require('../../lib/utils/weixin') +const { + ERROR +} = require('../../common/error') +const { + preUnifiedLogin, + postUnifiedLogin +} = require('../../lib/utils/unified-login') +const { + LOG_TYPE +} = require('../../common/constants') +const { + preBind, + postBind +} = require('../../lib/utils/relate') + +/** + * 微信授权手机号登录 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#login-by-weixin-mobile + * @param {Object} params + * @param {String} params.phoneCode 微信手机号返回的code + * @param {String} params.inviteCode 邀请码 + * @returns + */ +module.exports = async function (params = {}) { + const schema = { + phoneCode: 'string', + inviteCode: { + type: 'string', + required: false + } + } + + this.middleware.validate(params, schema) + + const { phoneCode, inviteCode } = params + + const weixinApi = initWeixin.call(this) + let mobile + + try { + const accessToken = await getWeixinAccessToken.call(this) + const mobileRes = await weixinApi.getPhoneNumber(accessToken, phoneCode) + mobile = mobileRes.purePhoneNumber + } catch (error) { + console.error(error) + await this.middleware.uniIdLog({ + success: false, + type: LOG_TYPE.LOGIN + }) + throw { + errCode: ERROR.GET_THIRD_PARTY_ACCOUNT_FAILED + } + } + + const { type, user } = await preUnifiedLogin.call(this, { + user: { + mobile + } + }) + + let extraData = { + mobile_confirmed: 1 + } + + if (type === 'login') { + // 绑定手机号 + if (!user.mobile_confirmed) { + const bindAccount = { + mobile + } + await preBind.call(this, { + uid: user._id, + bindAccount, + logType: LOG_TYPE.BIND_MOBILE + }) + await postBind.call(this, { + uid: user._id, + bindAccount, + extraData: { + mobile_confirmed: 1 + }, + logType: LOG_TYPE.BIND_MOBILE + }) + extraData = { + ...extraData, + ...bindAccount + } + } + } + + return postUnifiedLogin.call(this, { + user, + extraData: { + ...extraData + }, + isThirdParty: false, + type, + inviteCode + }) +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-weixin.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-weixin.js new file mode 100644 index 0000000..b50f9a5 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login-by-weixin.js @@ -0,0 +1,176 @@ +const { + initWeixin +} = require('../../lib/third-party/index') +const { + ERROR +} = require('../../common/error') +const { + preUnifiedLogin, + postUnifiedLogin +} = require('../../lib/utils/unified-login') +const { + generateWeixinCache, + getWeixinPlatform, + saveWeixinUserKey, + saveSecureNetworkCache +} = require('../../lib/utils/weixin') +const { + LOG_TYPE +} = require('../../common/constants') +const url = require('url') + +/** + * 微信登录 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#login-by-weixin + * @param {Object} params + * @param {String} params.code 微信登录返回的code + * @param {String} params.inviteCode 邀请码 + * @returns + */ +module.exports = async function (params = {}) { + const schema = { + code: 'string', + inviteCode: { + type: 'string', + required: false + } + } + this.middleware.validate(params, schema) + const { + code, + inviteCode, + // 内部参数,暂不暴露 + secureNetworkCache = false + } = params + const { + appId + } = this.getUniversalClientInfo() + const weixinApi = initWeixin.call(this) + const weixinPlatform = getWeixinPlatform.call(this) + let apiName + switch (weixinPlatform) { + case 'mp': + apiName = 'code2Session' + break + case 'app': + case 'h5': + case 'web': + apiName = 'getOauthAccessToken' + break + default: + throw new Error('Unsupported weixin platform') + } + let getWeixinAccountResult + try { + getWeixinAccountResult = await weixinApi[apiName](code) + } catch (error) { + console.error(error) + await this.middleware.uniIdLog({ + success: false, + type: LOG_TYPE.LOGIN + }) + throw { + errCode: ERROR.GET_THIRD_PARTY_ACCOUNT_FAILED + } + } + + const { + openid, + unionid, + // 保存下面四个字段 + sessionKey, // 微信小程序用户sessionKey + accessToken, // App端微信用户accessToken + refreshToken, // App端微信用户refreshToken + expired: accessTokenExpired // App端微信用户accessToken过期时间 + } = getWeixinAccountResult + + if (secureNetworkCache) { + if (weixinPlatform !== 'mp') { + throw new Error('Unsupported weixin platform, expect mp-weixin') + } + await saveSecureNetworkCache.call(this, { + code, + openid, + unionid, + sessionKey + }) + } + + const { + type, + user + } = await preUnifiedLogin.call(this, { + user: { + wx_openid: { + [weixinPlatform]: openid + }, + wx_unionid: unionid + } + }) + const extraData = { + wx_openid: { + [`${weixinPlatform}_${appId}`]: openid + }, + wx_unionid: unionid + } + if (type === 'register' && weixinPlatform !== 'mp') { + const { + nickname, + avatar + } = await weixinApi.getUserInfo({ + accessToken, + openid + }) + + if (avatar) { + // eslint-disable-next-line n/no-deprecated-api + const avatarPath = url.parse(avatar).pathname + const extName = avatarPath.indexOf('.') > -1 ? url.parse(avatar).pathname.split('.').pop() : 'jpg' + const cloudPath = `user/avatar/${openid.slice(-8) + Date.now()}-avatar.${extName}` + const getAvatarRes = await uniCloud.httpclient.request(avatar) + if (getAvatarRes.status >= 400) { + throw { + errCode: ERROR.GET_THIRD_PARTY_USER_INFO_FAILED + } + } + + const { + fileID + } = await uniCloud.uploadFile({ + cloudPath, + fileContent: getAvatarRes.data + }) + + extraData.avatar_file = { + name: cloudPath, + extname: extName, + url: fileID + } + } + + extraData.nickname = nickname + } + await saveWeixinUserKey.call(this, { + openid, + sessionKey, + accessToken, + refreshToken, + accessTokenExpired + }) + return postUnifiedLogin.call(this, { + user, + extraData: { + ...extraData, + ...generateWeixinCache.call(this, { + openid, + sessionKey, // 微信小程序用户sessionKey + accessToken, // App端微信用户accessToken + refreshToken, // App端微信用户refreshToken + accessTokenExpired // App端微信用户accessToken过期时间 + }) + }, + isThirdParty: true, + type, + inviteCode + }) +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login.js new file mode 100644 index 0000000..97e9cfe --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/login/login.js @@ -0,0 +1,94 @@ +const { + preLoginWithPassword, + postLogin +} = require('../../lib/utils/login') +const { + getNeedCaptcha, + verifyCaptcha +} = require('../../lib/utils/captcha') +const { + CAPTCHA_SCENE +} = require('../../common/constants') +const { + ERROR +} = require('../../common/error') + +/** + * 用户名密码登录 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#login + * @param {Object} params + * @param {String} params.username 用户名 + * @param {String} params.mobile 手机号 + * @param {String} params.email 邮箱 + * @param {String} params.password 密码 + * @param {String} params.captcha 图形验证码 + * @returns + */ +module.exports = async function (params = {}) { + const schema = { + username: { + required: false, + type: 'username' + }, + mobile: { + required: false, + type: 'mobile' + }, + email: { + required: false, + type: 'email' + }, + password: 'password', + captcha: { + required: false, + type: 'string' + } + } + this.middleware.validate(params, schema) + const { + username, + mobile, + email, + password, + captcha + } = params + if (!username && !mobile && !email) { + throw { + errCode: ERROR.INVALID_USERNAME + } + } else if ( + (username && email) || + (username && mobile) || + (email && mobile) + ) { + throw { + errCode: ERROR.INVALID_PARAM + } + } + const needCaptcha = await getNeedCaptcha.call(this, { + username, + mobile, + email + }) + if (needCaptcha) { + await verifyCaptcha.call(this, { + captcha, + scene: CAPTCHA_SCENE.LOGIN_BY_PWD + }) + } + const { + user, + extraData + } = await preLoginWithPassword.call(this, { + user: { + username, + mobile, + email + }, + password + }) + return postLogin.call(this, { + user, + extraData + }) +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/logout/index.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/logout/index.js new file mode 100644 index 0000000..544be2b --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/logout/index.js @@ -0,0 +1,3 @@ +module.exports = { + logout: require('./logout') +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/logout/logout.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/logout/logout.js new file mode 100644 index 0000000..7d491c6 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/logout/logout.js @@ -0,0 +1,15 @@ +const { + logout +} = require('../../lib/utils/logout') + +/** + * 用户退出登录 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#logout + * @returns + */ +module.exports = async function () { + await logout.call(this) + return { + errCode: 0 + } +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/multi-end/authorize-app-login.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/multi-end/authorize-app-login.js new file mode 100644 index 0000000..8f8a167 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/multi-end/authorize-app-login.js @@ -0,0 +1,37 @@ +const { + isAuthorizeApproved +} = require('./utils') +const { + dbCmd, + userCollection +} = require('../../common/constants') + +/** + * 授权用户登录应用 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#authorize-app-login + * @param {Object} params + * @param {String} params.uid 用户id + * @param {String} params.appId 授权的应用的AppId + * @returns + */ +module.exports = async function (params = {}) { + const schema = { + uid: 'string', + appId: 'string' + } + this.middleware.validate(params, schema) + const { + uid, + appId + } = params + await isAuthorizeApproved({ + uid, + appIdList: [appId] + }) + await userCollection.doc(uid).update({ + dcloud_appid: dbCmd.push(appId) + }) + return { + errCode: 0 + } +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/multi-end/index.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/multi-end/index.js new file mode 100644 index 0000000..ce9cc7b --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/multi-end/index.js @@ -0,0 +1,5 @@ +module.exports = { + authorizeAppLogin: require('./authorize-app-login'), + removeAuthorizedApp: require('./remove-authorized-app'), + setAuthorizedApp: require('./set-authorized-app') +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/multi-end/remove-authorized-app.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/multi-end/remove-authorized-app.js new file mode 100644 index 0000000..df82184 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/multi-end/remove-authorized-app.js @@ -0,0 +1,30 @@ +const { + dbCmd, + userCollection +} = require('../../common/constants') + +/** + * 移除用户登录授权 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#remove-authorized-app + * @param {Object} params + * @param {String} params.uid 用户id + * @param {String} params.appId 取消授权的应用的AppId + * @returns + */ +module.exports = async function (params = {}) { + const schema = { + uid: 'string', + appId: 'string' + } + this.middleware.validate(params, schema) + const { + uid, + appId + } = params + await userCollection.doc(uid).update({ + dcloud_appid: dbCmd.pull(appId) + }) + return { + errCode: 0 + } +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/multi-end/set-authorized-app.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/multi-end/set-authorized-app.js new file mode 100644 index 0000000..a438ef9 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/multi-end/set-authorized-app.js @@ -0,0 +1,36 @@ +const { + isAuthorizeApproved +} = require('./utils') +const { + userCollection +} = require('../../common/constants') + +/** + * 设置用户允许登录的应用列表 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#set-authorized-app + * @param {Object} params + * @param {String} params.uid 用户id + * @param {Array} params.appIdList 允许登录的应用AppId列表 + * @returns + */ +module.exports = async function (params = {}) { + const schema = { + uid: 'string', + appIdList: 'array' + } + this.middleware.validate(params, schema) + const { + uid, + appIdList + } = params + await isAuthorizeApproved({ + uid, + appIdList + }) + await userCollection.doc(uid).update({ + dcloud_appid: appIdList + }) + return { + errCode: 0 + } +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/multi-end/utils.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/multi-end/utils.js new file mode 100644 index 0000000..4ee4e26 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/multi-end/utils.js @@ -0,0 +1,38 @@ +const { + userCollection +} = require('../../common/constants') +const { + ERROR +} = require('../../common/error') +const { + findUser +} = require('../../lib/utils/account') + +async function isAuthorizeApproved ({ + uid, + appIdList +} = {}) { + const getUserRes = await userCollection.doc(uid).get() + const userRecord = getUserRes.data[0] + if (!userRecord) { + throw { + errCode: ERROR.ACCOUNT_NOT_EXISTS + } + } + const { + userMatched + } = await findUser({ + userQuery: userRecord, + authorizedApp: appIdList + }) + + if (userMatched.some(item => item._id !== uid)) { + throw { + errCode: ERROR.ACCOUNT_CONFLICT + } + } +} + +module.exports = { + isAuthorizeApproved +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/register/index.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/register/index.js new file mode 100644 index 0000000..64ff603 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/register/index.js @@ -0,0 +1,5 @@ +module.exports = { + registerUser: require('./register-user'), + registerAdmin: require('./register-admin'), + registerUserByEmail: require('./register-user-by-email') +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/register/register-admin.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/register/register-admin.js new file mode 100644 index 0000000..5e122ab --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/register/register-admin.js @@ -0,0 +1,72 @@ +const { + userCollection +} = require('../../common/constants') +const { + ERROR +} = require('../../common/error') +const { + preRegisterWithPassword, + postRegister +} = require('../../lib/utils/register') + +/** + * 注册管理员 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#register-admin + * @param {Object} params + * @param {String} params.username 用户名 + * @param {String} params.password 密码 + * @param {String} params.nickname 昵称 + * @returns + */ +module.exports = async function (params = {}) { + const schema = { + username: 'username', + password: 'password', + nickname: { + type: 'nickname', + required: false + } + } + this.middleware.validate(params, schema) + const { + username, + password, + nickname + } = params + const getAdminRes = await userCollection.where({ + role: 'admin' + }).limit(1).get() + if (getAdminRes.data.length > 0) { + const [admin] = getAdminRes.data + const appId = this.getUniversalClientInfo().appId + + if (!admin.dcloud_appid || (admin.dcloud_appid && admin.dcloud_appid.includes(appId))) { + return { + errCode: ERROR.ADMIN_EXISTS, + errMsg: this.t('uni-id-admin-exists') + } + } else { + return { + errCode: ERROR.ADMIN_EXISTS, + errMsg: this.t('uni-id-admin-exist-in-other-apps') + } + } + } + const { + user, + extraData + } = await preRegisterWithPassword.call(this, { + user: { + username + }, + password + }) + return postRegister.call(this, { + user, + extraData: { + ...extraData, + nickname, + role: ['admin'] + } + }) +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/register/register-user-by-email.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/register/register-user-by-email.js new file mode 100644 index 0000000..b52c1d2 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/register/register-user-by-email.js @@ -0,0 +1,87 @@ +const { + postRegister, + preRegisterWithPassword +} = require('../../lib/utils/register') +const { + verifyCaptcha +} = require('../../lib/utils/captcha') +const { + CAPTCHA_SCENE, + EMAIL_SCENE, + LOG_TYPE +} = require('../../common/constants') +const { + verifyEmailCode +} = require('../../lib/utils/verify-code') + +/** + * 通过邮箱+验证码注册普通用户 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#register-user-by-email + * @param {Object} params + * @param {String} params.email 邮箱 + * @param {String} params.password 密码 + * @param {String} params.nickname 昵称 + * @param {String} params.code 邮箱验证码 + * @param {String} params.inviteCode 邀请码 + * @returns + */ +module.exports = async function (params = {}) { + const schema = { + email: 'email', + password: 'password', + nickname: { + required: false, + type: 'nickname' + }, + code: 'string', + inviteCode: { + required: false, + type: 'string' + } + } + this.middleware.validate(params, schema) + const { + email, + password, + nickname, + code, + inviteCode + } = params + + try { + // 验证邮箱验证码,验证不通过时写入失败日志 + await verifyEmailCode({ + email, + code, + scene: EMAIL_SCENE.REGISTER + }) + } catch (error) { + await this.middleware.uniIdLog({ + data: { + email + }, + type: LOG_TYPE.REGISTER, + success: false + }) + throw error + } + + const { + user, + extraData + } = await preRegisterWithPassword.call(this, { + user: { + email + }, + password + }) + return postRegister.call(this, { + user, + extraData: { + ...extraData, + nickname, + email_confirmed: 1 + }, + inviteCode + }) +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/register/register-user.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/register/register-user.js new file mode 100644 index 0000000..130dece --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/register/register-user.js @@ -0,0 +1,68 @@ +const { + postRegister, + preRegisterWithPassword +} = require('../../lib/utils/register') +const { + verifyCaptcha +} = require('../../lib/utils/captcha') +const { + CAPTCHA_SCENE +} = require('../../common/constants') + +/** + * 注册普通用户 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#register-user + * @param {Object} params + * @param {String} params.username 用户名 + * @param {String} params.password 密码 + * @param {String} params.captcha 图形验证码 + * @param {String} params.nickname 昵称 + * @param {String} params.inviteCode 邀请码 + * @returns + */ +module.exports = async function (params = {}) { + const schema = { + username: 'username', + password: 'password', + captcha: 'string', + nickname: { + required: false, + type: 'nickname' + }, + inviteCode: { + required: false, + type: 'string' + } + } + this.middleware.validate(params, schema) + const { + username, + password, + nickname, + captcha, + inviteCode + } = params + + await verifyCaptcha.call(this, { + captcha, + scene: CAPTCHA_SCENE.REGISTER + }) + + const { + user, + extraData + } = await preRegisterWithPassword.call(this, { + user: { + username + }, + password + }) + return postRegister.call(this, { + user, + extraData: { + ...extraData, + nickname + }, + inviteCode + }) +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/bind-alipay.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/bind-alipay.js new file mode 100644 index 0000000..bdb451b --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/bind-alipay.js @@ -0,0 +1,63 @@ +const { + preBind, + postBind +} = require('../../lib/utils/relate') +const { + LOG_TYPE +} = require('../../common/constants') +const { + ERROR +} = require('../../common/error') +const { + initAlipay +} = require('../../lib/third-party/index') + +/** + * 绑定支付宝账号 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#bind-alipay + * @param {Object} params + * @param {String} params.code 支付宝小程序登录返回的code参数 + * @returns + */ +module.exports = async function (params = {}) { + const schema = { + code: 'string' + } + this.middleware.validate(params, schema) + const uid = this.authInfo.uid + const { + code + } = params + const alipayApi = initAlipay.call(this) + let getAlipayAccountResult + try { + getAlipayAccountResult = await alipayApi().code2Session(code) + } catch (error) { + await this.middleware.uniIdLog({ + success: false, + type: LOG_TYPE.BIND_ALIPAY + }) + throw { + errCode: ERROR.GET_THIRD_PARTY_ACCOUNT_FAILED + } + } + + const { + openid + } = getAlipayAccountResult + + const bindAccount = { + ali_openid: openid + } + await preBind.call(this, { + uid, + bindAccount, + logType: LOG_TYPE.BIND_APPLE + }) + return postBind.call(this, { + uid, + bindAccount, + extraData: {}, + logType: LOG_TYPE.BIND_APPLE + }) +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/bind-apple.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/bind-apple.js new file mode 100644 index 0000000..eb87f8b --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/bind-apple.js @@ -0,0 +1,62 @@ +const { + preBind, + postBind +} = require('../../lib/utils/relate') +const { + LOG_TYPE +} = require('../../common/constants') +const { + ERROR +} = require('../../common/error') +const { + initApple +} = require('../../lib/third-party/index') + +/** + * 绑定苹果账号 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#bind-apple + * @param {Object} params + * @param {String} params.identityToken 苹果登录返回identityToken + * @returns + */ +module.exports = async function (params = {}) { + const schema = { + identityToken: 'string' + } + this.middleware.validate(params, schema) + const uid = this.authInfo.uid + const { + identityToken + } = params + const appleApi = initApple.call(this) + let verifyResult + try { + verifyResult = await appleApi.verifyIdentityToken(identityToken) + } catch (error) { + await this.middleware.uniIdLog({ + success: false, + type: LOG_TYPE.BIND_APPLE + }) + throw { + errCode: ERROR.GET_THIRD_PARTY_ACCOUNT_FAILED + } + } + const { + openid + } = verifyResult + + const bindAccount = { + apple_openid: openid + } + await preBind.call(this, { + uid, + bindAccount, + logType: LOG_TYPE.BIND_APPLE + }) + return postBind.call(this, { + uid, + bindAccount, + extraData: {}, + logType: LOG_TYPE.BIND_APPLE + }) +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/bind-mobile-by-mp-weixin.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/bind-mobile-by-mp-weixin.js new file mode 100644 index 0000000..f4c2bd0 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/bind-mobile-by-mp-weixin.js @@ -0,0 +1,104 @@ +const { + preBind, + postBind +} = require('../../lib/utils/relate') +const { + LOG_TYPE +} = require('../../common/constants') +const { + decryptWeixinData, + getWeixinCache, getWeixinAccessToken +} = require('../../lib/utils/weixin') +const { initWeixin } = require('../../lib/third-party') +const { ERROR } = require('../../common/error') + +/** + * 通过微信绑定手机号 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#bind-mobile-by-mp-weixin + * @param {Object} params + * @param {String} params.encryptedData 微信获取手机号返回的加密信息 + * @param {String} params.iv 微信获取手机号返回的初始向量 + * @param {String} params.code 微信获取手机号返回的code + * @returns + */ +module.exports = async function (params = {}) { + /** + * 微信小程序的规则是客户端应先使用checkSession接口检测上次获取的sessionKey是否仍有效 + * 如果有效则直接使用上次存储的sessionKey即可 + * 如果无效应重新调用login接口再次刷新sessionKey + * 因此此接口不应直接使用客户端login获取的code,只能使用缓存的sessionKey + */ + const schema = { + encryptedData: { + required: false, + type: 'string' + }, + iv: { + required: false, + type: 'string' + }, + code: { + required: false, + type: 'string' + } + } + const { + encryptedData, + iv, + code + } = params + this.middleware.validate(params, schema) + + if ((!encryptedData && !iv) && !code) { + return { + errCode: ERROR.INVALID_PARAM + } + } + + const uid = this.authInfo.uid + + let mobile + if (code) { + // 区分客户端类型 小程序还是App + const accessToken = await getWeixinAccessToken.call(this) + const weixinApi = initWeixin.call(this) + const res = await weixinApi.getPhoneNumber(accessToken, code) + + mobile = res.purePhoneNumber + } else { + const sessionKey = await getWeixinCache.call(this, { + uid, + key: 'session_key' + }) + if (!sessionKey) { + throw new Error('Session key not found') + } + const res = decryptWeixinData.call(this, { + encryptedData, + sessionKey, + iv + }) + + mobile = res.purePhoneNumber + } + + const bindAccount = { + mobile + } + await preBind.call(this, { + uid, + bindAccount, + logType: LOG_TYPE.BIND_MOBILE + }) + await postBind.call(this, { + uid, + bindAccount, + extraData: { + mobile_confirmed: 1 + }, + logType: LOG_TYPE.BIND_MOBILE + }) + return { + errCode: 0 + } +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/bind-mobile-by-sms.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/bind-mobile-by-sms.js new file mode 100644 index 0000000..1640c2d --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/bind-mobile-by-sms.js @@ -0,0 +1,92 @@ +const { + getNeedCaptcha, + verifyCaptcha +} = require('../../lib/utils/captcha') +const { + LOG_TYPE, + SMS_SCENE, + CAPTCHA_SCENE +} = require('../../common/constants') +const { + verifyMobileCode +} = require('../../lib/utils/verify-code') +const { + preBind, + postBind +} = require('../../lib/utils/relate') + +/** + * 通过短信验证码绑定手机号 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#bind-mobile-by-sms + * @param {Object} params + * @param {String} params.mobile 手机号 + * @param {String} params.code 短信验证码 + * @param {String} params.captcha 图形验证码 + * @returns + */ +module.exports = async function (params = {}) { + const schema = { + mobile: 'mobile', + code: 'string', + captcha: { + type: 'string', + required: false + } + } + const { + mobile, + code, + captcha + } = params + this.middleware.validate(params, schema) + const uid = this.authInfo.uid + + // 判断是否需要验证码 + const needCaptcha = await getNeedCaptcha.call(this, { + uid, + type: LOG_TYPE.BIND_MOBILE + }) + if (needCaptcha) { + await verifyCaptcha.call(this, { + captcha, + scene: CAPTCHA_SCENE.BIND_MOBILE_BY_SMS + }) + } + + try { + // 验证手机号验证码,验证不通过时写入失败日志 + await verifyMobileCode({ + mobile, + code, + scene: SMS_SCENE.BIND_MOBILE_BY_SMS + }) + } catch (error) { + await this.middleware.uniIdLog({ + data: { + user_id: uid + }, + type: LOG_TYPE.BIND_MOBILE, + success: false + }) + throw error + } + const bindAccount = { + mobile + } + await preBind.call(this, { + uid, + bindAccount, + logType: LOG_TYPE.BIND_MOBILE + }) + await postBind.call(this, { + uid, + bindAccount, + extraData: { + mobile_confirmed: 1 + }, + logType: LOG_TYPE.BIND_MOBILE + }) + return { + errCode: 0 + } +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/bind-mobile-by-univerify.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/bind-mobile-by-univerify.js new file mode 100644 index 0000000..2970c61 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/bind-mobile-by-univerify.js @@ -0,0 +1,70 @@ +const { + getPhoneNumber +} = require('../../lib/utils/univerify') +const { + LOG_TYPE +} = require('../../common/constants') +const { + preBind, + postBind +} = require('../../lib/utils/relate') + +/** + * 通过一键登录绑定手机号 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#bind-mobile-by-univerify + * @param {Object} params + * @param {String} params.openid APP端一键登录返回的openid + * @param {String} params.access_token APP端一键登录返回的access_token + * @returns + */ +module.exports = async function (params = {}) { + const schema = { + openid: 'string', + access_token: 'string' + } + const { + openid, + // eslint-disable-next-line camelcase + access_token + } = params + this.middleware.validate(params, schema) + const uid = this.authInfo.uid + let mobile + try { + const phoneInfo = await getPhoneNumber.call(this, { + // eslint-disable-next-line camelcase + access_token, + openid + }) + mobile = phoneInfo.phoneNumber + } catch (error) { + await this.middleware.uniIdLog({ + success: false, + data: { + user_id: uid + }, + type: LOG_TYPE.BIND_MOBILE + }) + throw error + } + + const bindAccount = { + mobile + } + await preBind.call(this, { + uid, + bindAccount, + logType: LOG_TYPE.BIND_MOBILE + }) + await postBind.call(this, { + uid, + bindAccount, + extraData: { + mobile_confirmed: 1 + }, + logType: LOG_TYPE.BIND_MOBILE + }) + return { + errCode: 0 + } +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/bind-qq.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/bind-qq.js new file mode 100644 index 0000000..574f917 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/bind-qq.js @@ -0,0 +1,110 @@ +const { + preBind, + postBind +} = require('../../lib/utils/relate') +const { + LOG_TYPE +} = require('../../common/constants') +const { + ERROR +} = require('../../common/error') +const { + initQQ +} = require('../../lib/third-party/index') +const { + generateQQCache, + getQQPlatform, + saveQQUserKey +} = require('../../lib/utils/qq') + +/** + * 绑定QQ + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#bind-qq + * @param {Object} params + * @param {String} params.code 小程序端QQ登录返回的code + * @param {String} params.accessToken APP端QQ登录返回的accessToken + * @param {String} params.accessTokenExpired accessToken过期时间,由App端QQ登录返回的expires_in参数计算而来,单位:毫秒 + * @returns + */ +module.exports = async function (params = {}) { + const schema = { + code: { + type: 'string', + required: false + }, + accessToken: { + type: 'string', + required: false + }, + accessTokenExpired: { + type: 'number', + required: false + } + } + this.middleware.validate(params, schema) + const uid = this.authInfo.uid + const { + code, + accessToken, + accessTokenExpired + } = params + const qqPlatform = getQQPlatform.call(this) + const appId = this.getUniversalClientInfo().appId + const qqApi = initQQ.call(this) + const clientPlatform = this.clientPlatform + const apiName = clientPlatform === 'mp-qq' ? 'code2Session' : 'getOpenidByToken' + let getQQAccountResult + try { + getQQAccountResult = await qqApi[apiName]({ + code, + accessToken + }) + } catch (error) { + await this.middleware.uniIdLog({ + success: false, + type: LOG_TYPE.BIND_QQ + }) + throw { + errCode: ERROR.GET_THIRD_PARTY_ACCOUNT_FAILED + } + } + + const { + openid, + unionid, + // 保存下面四个字段 + sessionKey // 微信小程序用户sessionKey + } = getQQAccountResult + + const bindAccount = { + qq_openid: { + [qqPlatform]: openid + }, + qq_unionid: unionid + } + await preBind.call(this, { + uid, + bindAccount, + logType: LOG_TYPE.BIND_QQ + }) + await saveQQUserKey.call(this, { + openid, + sessionKey, + accessToken, + accessTokenExpired + }) + return postBind.call(this, { + uid, + bindAccount, + extraData: { + qq_openid: { + [`${qqPlatform}_${appId}`]: openid + }, + ...generateQQCache.call(this, { + openid, + sessionKey + }) + }, + logType: LOG_TYPE.BIND_QQ + }) +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/bind-weixin.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/bind-weixin.js new file mode 100644 index 0000000..d649478 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/bind-weixin.js @@ -0,0 +1,100 @@ +const { + preBind, + postBind +} = require('../../lib/utils/relate') +const { + LOG_TYPE +} = require('../../common/constants') +const { + generateWeixinCache, + saveWeixinUserKey, + getWeixinPlatform +} = require('../../lib/utils/weixin') +const { + initWeixin +} = require('../../lib/third-party/index') +const { + ERROR +} = require('../../common/error') + +/** + * 绑定微信 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#bind-weixin + * @param {Object} params + * @param {String} params.code 微信登录返回的code + * @returns + */ +module.exports = async function (params = {}) { + const schema = { + code: 'string' + } + this.middleware.validate(params, schema) + const uid = this.authInfo.uid + const { + code + } = params + const weixinPlatform = getWeixinPlatform.call(this) + const appId = this.getUniversalClientInfo().appId + + const weixinApi = initWeixin.call(this) + const clientPlatform = this.clientPlatform + const apiName = clientPlatform === 'mp-weixin' ? 'code2Session' : 'getOauthAccessToken' + let getWeixinAccountResult + try { + getWeixinAccountResult = await weixinApi[apiName](code) + } catch (error) { + await this.middleware.uniIdLog({ + success: false, + type: LOG_TYPE.BIND_WEIXIN + }) + throw { + errCode: ERROR.GET_THIRD_PARTY_ACCOUNT_FAILED + } + } + + const { + openid, + unionid, + // 保存下面四个字段 + sessionKey, // 微信小程序用户sessionKey + accessToken, // App端微信用户accessToken + refreshToken, // App端微信用户refreshToken + expired: accessTokenExpired // App端微信用户accessToken过期时间 + } = getWeixinAccountResult + + const bindAccount = { + wx_openid: { + [weixinPlatform]: openid + }, + wx_unionid: unionid + } + await preBind.call(this, { + uid, + bindAccount, + logType: LOG_TYPE.BIND_WEIXIN + }) + await saveWeixinUserKey.call(this, { + openid, + sessionKey, + accessToken, + refreshToken, + accessTokenExpired + }) + return postBind.call(this, { + uid, + bindAccount, + extraData: { + wx_openid: { + [`${weixinPlatform}_${appId}`]: openid + }, + ...generateWeixinCache.call(this, { + openid, + sessionKey, // 微信小程序用户sessionKey + accessToken, // App端微信用户accessToken + refreshToken, // App端微信用户refreshToken + accessTokenExpired // App端微信用户accessToken过期时间 + }) + }, + logType: LOG_TYPE.BIND_WEIXIN + }) +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/index.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/index.js new file mode 100644 index 0000000..4d99c02 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/index.js @@ -0,0 +1,13 @@ +module.exports = { + bindMobileBySms: require('./bind-mobile-by-sms'), + bindMobileByUniverify: require('./bind-mobile-by-univerify'), + bindMobileByMpWeixin: require('./bind-mobile-by-mp-weixin'), + bindAlipay: require('./bind-alipay'), + bindApple: require('./bind-apple'), + bindQQ: require('./bind-qq'), + bindWeixin: require('./bind-weixin'), + unbindWeixin: require('./unbind-weixin'), + unbindAlipay: require('./unbind-alipay'), + unbindQQ: require('./unbind-qq'), + unbindApple: require('./unbind-apple') +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/unbind-alipay.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/unbind-alipay.js new file mode 100644 index 0000000..67bb43b --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/unbind-alipay.js @@ -0,0 +1,32 @@ +const { + preUnBind, + postUnBind +} = require('../../lib/utils/relate') +const { + LOG_TYPE, dbCmd +} = require('../../common/constants') + +/** + * 解绑支付宝 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#unbind-alipay + * @returns + */ +module.exports = async function () { + const { uid } = this.authInfo + + await preUnBind.call(this, { + uid, + unBindAccount: { + ali_openid: dbCmd.exists(true) + }, + logType: LOG_TYPE.UNBIND_ALIPAY + }) + + return await postUnBind.call(this, { + uid, + unBindAccount: { + ali_openid: dbCmd.remove() + }, + logType: LOG_TYPE.UNBIND_ALIPAY + }) +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/unbind-apple.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/unbind-apple.js new file mode 100644 index 0000000..111c1bf --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/unbind-apple.js @@ -0,0 +1,32 @@ +const { + preUnBind, + postUnBind +} = require('../../lib/utils/relate') +const { + LOG_TYPE, dbCmd +} = require('../../common/constants') + +/** + * 解绑apple + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#unbind-apple + * @returns + */ +module.exports = async function () { + const { uid } = this.authInfo + + await preUnBind.call(this, { + uid, + unBindAccount: { + apple_openid: dbCmd.exists(true) + }, + logType: LOG_TYPE.UNBIND_APPLE + }) + + return await postUnBind.call(this, { + uid, + unBindAccount: { + apple_openid: dbCmd.remove() + }, + logType: LOG_TYPE.UNBIND_APPLE + }) +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/unbind-qq.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/unbind-qq.js new file mode 100644 index 0000000..0c9704c --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/unbind-qq.js @@ -0,0 +1,33 @@ +const { + preUnBind, + postUnBind +} = require('../../lib/utils/relate') +const { + LOG_TYPE, dbCmd +} = require('../../common/constants') +/** + * 解绑QQ + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#unbind-qq + * @returns + */ +module.exports = async function () { + const { uid } = this.authInfo + + await preUnBind.call(this, { + uid, + unBindAccount: { + qq_openid: dbCmd.exists(true), + qq_unionid: dbCmd.exists(true) + }, + logType: LOG_TYPE.UNBIND_QQ + }) + + return await postUnBind.call(this, { + uid, + unBindAccount: { + qq_openid: dbCmd.remove(), + qq_unionid: dbCmd.remove() + }, + logType: LOG_TYPE.UNBIND_QQ + }) +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/unbind-weixin.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/unbind-weixin.js new file mode 100644 index 0000000..2248327 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/relate/unbind-weixin.js @@ -0,0 +1,38 @@ +const { + preUnBind, + postUnBind +} = require('../../lib/utils/relate') +const { + LOG_TYPE, dbCmd +} = require('../../common/constants') +const { + getWeixinPlatform +} = require('../../lib/utils/weixin') + +/** + * 解绑微信 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#unbind-weixin + * @returns + */ +module.exports = async function () { + const { uid } = this.authInfo + // const weixinPlatform = getWeixinPlatform.call(this) + + await preUnBind.call(this, { + uid, + unBindAccount: { + wx_openid: dbCmd.exists(true), + wx_unionid: dbCmd.exists(true) + }, + logType: LOG_TYPE.UNBIND_WEIXIN + }) + + return await postUnBind.call(this, { + uid, + unBindAccount: { + wx_openid: dbCmd.remove(), + wx_unionid: dbCmd.remove() + }, + logType: LOG_TYPE.UNBIND_WEIXIN + }) +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/utils/index.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/utils/index.js new file mode 100644 index 0000000..0ec67a5 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/utils/index.js @@ -0,0 +1,5 @@ +module.exports = { + refreshToken: require('./refresh-token'), + setPushCid: require('./set-push-cid'), + secureNetworkHandshakeByWeixin: require('./secure-network-handshake-by-weixin') +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/utils/refresh-token.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/utils/refresh-token.js new file mode 100644 index 0000000..b12f1f0 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/utils/refresh-token.js @@ -0,0 +1,24 @@ +/** + * 刷新token + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#refresh-token + */ +module.exports = async function () { + const refreshTokenRes = await this.uniIdCommon.refreshToken({ + token: this.getUniversalUniIdToken() + }) + const { + errCode, + token, + tokenExpired + } = refreshTokenRes + if (errCode) { + throw refreshTokenRes + } + return { + errCode: 0, + newToken: { + token, + tokenExpired + } + } +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/utils/secure-network-handshake-by-weixin.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/utils/secure-network-handshake-by-weixin.js new file mode 100644 index 0000000..82ea0b3 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/utils/secure-network-handshake-by-weixin.js @@ -0,0 +1,73 @@ +const { + ERROR +} = require('../../common/error') +const { + initWeixin +} = require('../../lib/third-party/index') +const { + saveWeixinUserKey, + saveSecureNetworkCache +} = require('../../lib/utils/weixin') +const loginByWeixin = require('../login/login-by-weixin') +/** + * 微信安全网络握手 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#set-push-cid + * @param {object} params + * @param {string} params.code 微信登录返回的code + * @param {boolean} params.callLoginByWeixin 是否同时调用一次微信登录 + * @returns + */ +module.exports = async function (params = {}) { + const schema = { + code: 'string', + callLoginByWeixin: { + type: 'boolean', + required: false + } + } + this.middleware.validate(params, schema) + let platform = this.clientPlatform + if (platform !== 'mp-weixin') { + throw new Error(`[secureNetworkHandshake] platform ${platform} is not supported`) + } + const { + code, + callLoginByWeixin = false + } = params + if (callLoginByWeixin) { + return loginByWeixin.call(this, { + code, + secureNetworkCache: true + }) + } + + const weixinApi = initWeixin.call(this) + let getWeixinAccountResult + try { + getWeixinAccountResult = await weixinApi.code2Session(code) + } catch (error) { + console.error(error) + throw { + errCode: ERROR.GET_THIRD_PARTY_ACCOUNT_FAILED + } + } + const { + openid, + unionid, + sessionKey // 微信小程序用户sessionKey + } = getWeixinAccountResult + await saveSecureNetworkCache.call(this, { + code, + openid, + unionid, + sessionKey + }) + await saveWeixinUserKey.call(this, { + openid, + sessionKey + }) + + return { + errCode: 0 + } +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/utils/set-push-cid.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/utils/set-push-cid.js new file mode 100644 index 0000000..9e08183 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/utils/set-push-cid.js @@ -0,0 +1,132 @@ +const { + deviceCollection +} = require('../../common/constants') +const { + ERROR +} = require('../../common/error') + +async function setOpendbDevice ({ + pushClientId +} = {}) { + // 仅新增,如果存在进行更新操作 + const { + appId, + deviceId, + deviceBrand, + deviceModel, + osName, + osVersion, + osLanguage, + osTheme, + devicePixelRatio, + windowWidth, + windowHeight, + screenWidth, + screenHeight, + romName, + romVersion + } = this.getUniversalClientInfo() + const platform = this.clientPlatform + const now = Date.now() + + const db = uniCloud.database() + const opendbDeviceCollection = db.collection('opendb-device') + const getDeviceRes = await opendbDeviceCollection.where({ + device_id: deviceId + }).get() + const data = { + appid: appId, + device_id: deviceId, + vendor: deviceBrand, + model: deviceModel, + uni_platform: platform, + os_name: osName, + os_version: osVersion, + os_language: osLanguage, + os_theme: osTheme, + pixel_ratio: devicePixelRatio, + window_width: windowWidth, + window_height: windowHeight, + screen_width: screenWidth, + screen_height: screenHeight, + rom_name: romName, + rom_version: romVersion, + last_update_date: now, + push_clientid: pushClientId + } + if (getDeviceRes.data.length > 0) { + await opendbDeviceCollection.where({ + device_id: deviceId + }).update(data) + return + } + data.create_date = now + await opendbDeviceCollection.add(data) +} + +/** + * 更新device表的push_clien_id + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#set-push-cid + * @param {object} params + * @param {string} params.pushClientId 客户端pushClientId + * @returns + */ +module.exports = async function (params = {}) { + const schema = { + pushClientId: 'string' + } + this.middleware.validate(params, schema) + const { + deviceId, + appId, + osName + } = this.getUniversalClientInfo() + let platform = this.clientPlatform + if (platform === 'app') { + platform += osName + } + + const { + uid, + exp + } = this.authInfo + const { pushClientId } = params + const tokenExpired = exp * 1000 + const getDeviceRes = await deviceCollection.where({ + device_id: deviceId + }).get() + // console.log(getDeviceRes) + if (getDeviceRes.data.length > 1) { + return { + errCode: ERROR.SYSTEM_ERROR + } + } + const deviceRecord = getDeviceRes.data[0] + await setOpendbDevice.call(this, { + pushClientId + }) + if (!deviceRecord) { + await deviceCollection.add({ + user_id: uid, + device_id: deviceId, + token_expired: tokenExpired, + push_clientid: pushClientId, + appid: appId + }) + return { + errCode: 0 + } + } + + await deviceCollection.where({ + device_id: deviceId + }).update({ + user_id: uid, + token_expired: tokenExpired, + push_clientid: pushClientId, + appid: appId + }) + return { + errCode: 0 + } +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/verify/create-captcha.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/verify/create-captcha.js new file mode 100644 index 0000000..c3f7d81 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/verify/create-captcha.js @@ -0,0 +1,35 @@ +const { + CAPTCHA_SCENE +} = require('../../common/constants') +const { + ERROR +} = require('../../common/error') + +/** + * 创建图形验证码 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#create-captcha + * @param {Object} params + * @param {String} params.scene 图形验证码使用场景 + * @returns + */ +module.exports = async function (params = {}) { + const schema = { + scene: 'string' + } + this.middleware.validate(params, schema) + + const { deviceId, platform } = this.getUniversalClientInfo() + const { + scene + } = params + if (!(Object.values(CAPTCHA_SCENE).includes(scene))) { + throw { + errCode: ERROR.INVALID_PARAM + } + } + return this.uniCaptcha.create({ + deviceId, + scene, + uniPlatform: platform + }) +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/verify/index.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/verify/index.js new file mode 100644 index 0000000..fba3524 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/verify/index.js @@ -0,0 +1,7 @@ +module.exports = { + createCaptcha: require('./create-captcha'), + refreshCaptcha: require('./refresh-captcha'), + sendSmsCode: require('./send-sms-code'), + sendEmailLink: require('./send-email-link'), + sendEmailCode: require('./send-email-code') +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/verify/refresh-captcha.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/verify/refresh-captcha.js new file mode 100644 index 0000000..fafdc6b --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/verify/refresh-captcha.js @@ -0,0 +1,36 @@ +const { + CAPTCHA_SCENE +} = require('../../common/constants') +const { + ERROR +} = require('../../common/error') + +/** + * 刷新图形验证码 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#refresh-captcha + * @param {Object} params + * @param {String} params.scene 图形验证码使用场景 + * @returns + */ +module.exports = async function (params = {}) { + const schema = { + scene: 'string' + } + this.middleware.validate(params, schema) + + const { deviceId, platform } = this.getUniversalClientInfo() + + const { + scene + } = params + if (!(Object.values(CAPTCHA_SCENE).includes(scene))) { + throw { + errCode: ERROR.INVALID_PARAM + } + } + return this.uniCaptcha.refresh({ + deviceId, + scene, + uniPlatform: platform + }) +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/verify/send-email-code.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/verify/send-email-code.js new file mode 100644 index 0000000..1a6304d --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/verify/send-email-code.js @@ -0,0 +1,60 @@ +const { + verifyCaptcha +} = require('../../lib/utils/captcha') +const { + EMAIL_SCENE +} = require('../../common/constants') +const { + ERROR +} = require('../../common/error') +/** + * 发送邮箱验证码,可用于登录、注册、绑定邮箱、修改密码等操作 + * @tutorial + * @param {Object} params + * @param {String} params.email 邮箱 + * @param {String} params.captcha 图形验证码 + * @param {String} params.scene 使用场景 + * @returns + */ +module.exports = async function (params = {}) { + const schema = { + email: 'email', + captcha: 'string', + scene: 'string' + } + this.middleware.validate(params, schema) + + const { + email, + captcha, + scene + } = params + + if (!(Object.values(EMAIL_SCENE).includes(scene))) { + throw { + errCode: ERROR.INVALID_PARAM + } + } + + await verifyCaptcha.call(this, { + scene: 'send-email-code', + captcha + }) + + // -- 测试代码 + await require('../../lib/utils/verify-code') + .setEmailVerifyCode.call(this, { + email, + code: '123456', + expiresIn: 180, + scene + }) + return { + errCode: 'uni-id-invalid-mail-template', + errMsg: `已启动测试模式,直接使用:123456作为邮箱验证码即可。\n如果是正式项目,需自行实现发送邮件的相关功能` + } + // -- 测试代码 + + + //发送邮件--需自行实现 +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/verify/send-email-link.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/verify/send-email-link.js new file mode 100644 index 0000000..f643434 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/verify/send-email-link.js @@ -0,0 +1,12 @@ +/** + * 发送邮箱链接,可用于登录、注册、绑定邮箱、修改密码等操作 + * @tutorial + * @param {Object} params + * @param {String} params.email 邮箱 + * @param {String} params.scene 使用场景 + * @returns + */ +module.exports = async function (params = {}) { + // 此接口暂未实现,欢迎向我们提交pr + throw new Error('api[sendEmailLink] is not yet implemented') +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/verify/send-sms-code.js b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/verify/send-sms-code.js new file mode 100644 index 0000000..6525276 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/module/verify/send-sms-code.js @@ -0,0 +1,71 @@ +const { + sendSmsCode +} = require('../../lib/utils/sms') +const { + verifyCaptcha +} = require('../../lib/utils/captcha') +const { + SMS_SCENE +} = require('../../common/constants') +const { + ERROR +} = require('../../common/error') + +/** + * 发送短信验证码 + * @tutorial https://uniapp.dcloud.net.cn/uniCloud/uni-id-pages.html#send-sms-code + * @param {Object} params + * @param {String} params.mobile 手机号 + * @param {String} params.captcha 图形验证码 + * @param {String} params.scene 短信验证码使用场景 + * @returns + */ +module.exports = async function (params = {}) { + const schema = { + mobile: 'mobile', + captcha: 'string', + scene: 'string' + } + this.middleware.validate(params, schema) + const { + mobile, + captcha, + scene + } = params + if (!(Object.values(SMS_SCENE).includes(scene))) { + throw { + errCode: ERROR.INVALID_PARAM + } + } + await verifyCaptcha.call(this, { + scene: 'send-sms-code', + captcha + }) + + // -- 测试代码 + const { + templateId + } = (this.config.service && + this.config.service.sms && + this.config.service.sms.scene && + this.config.service.sms.scene[scene]) || {} + if (!templateId || !templateId.replace(/[^0-9a-zA-Z]/g, '')) { + await require('../../lib/utils/verify-code') + .setMobileVerifyCode.call(this, { + mobile: params.mobile, + code: '123456', + expiresIn: 180, + scene + }) + return { + errCode: 'uni-id-invalid-sms-template-id', + errMsg: `未找到scene=${scene},的短信模版templateId。\n已启动测试模式,直接使用:123456作为短信验证码即可。\n如果是正式项目,请在路径:/common/uni-config-center/uni-id/config.json中service->sms中配置密钥等信息\n更多详情:https://uniapp.dcloud.io/uniCloud/uni-id.html#config` + } + } + // -- 测试代码 + + return sendSmsCode.call(this, { + mobile, + scene + }) +} diff --git a/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/package.json b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/package.json new file mode 100644 index 0000000..fe4fa04 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/cloudfunctions/uni-id-co/package.json @@ -0,0 +1,23 @@ +{ + "name": "uni-id-co", + "version": "1.1.20", + "description": "", + "main": "index.js", + "keywords": [], + "author": "DCloud", + "dependencies": { + "uni-captcha": "file:../../../../uni-captcha/uniCloud/cloudfunctions/common/uni-captcha", + "uni-config-center": "file:../../../../uni-config-center/uniCloud/cloudfunctions/common/uni-config-center", + "uni-id-common": "file:../../../../uni-id-common/uniCloud/cloudfunctions/common/uni-id-common", + "uni-open-bridge-common": "file:../../../../uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common", + "uni-cloud-s2s": "file:../../../../uni-cloud-s2s/uniCloud/cloudfunctions/common/uni-cloud-s2s" + }, + "extensions": { + "uni-cloud-redis": {}, + "uni-cloud-sms": {}, + "uni-cloud-verify": {} + }, + "cloudfunction-config": { + "keepRunningAfterReturn": false + } +} \ No newline at end of file diff --git a/uni_modules/uni-id-pages/uniCloud/database/opendb-device.schema.json b/uni_modules/uni-id-pages/uniCloud/database/opendb-device.schema.json new file mode 100644 index 0000000..c3591cc --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/database/opendb-device.schema.json @@ -0,0 +1,142 @@ +{ + "bsonType": "object", + "required": [], + "permission": { + "read": false, + "create": true, + "update": false, + "delete": false + }, + "properties": { + "_id": { + "description": "ID,系统自动生成" + }, + "appid": { + "bsonType": "string", + "description": "DCloud appid" + }, + "device_id": { + "bsonType": "string", + "description": "设备唯一标识" + }, + "vendor": { + "bsonType": "string", + "description": "设备厂商" + }, + "push_clientid": { + "bsonType": "string", + "description": "推送设备客户端标识" + }, + "imei": { + "bsonType": "string", + "description": "国际移动设备识别码IMEI(International Mobile Equipment Identity)" + }, + "oaid": { + "bsonType": "string", + "description": "移动智能设备标识公共服务平台提供的匿名设备标识符(OAID)" + }, + "idfa": { + "bsonType": "string", + "description": "iOS平台配置应用使用广告标识(IDFA)" + }, + "imsi": { + "bsonType": "string", + "description": "国际移动用户识别码(International Mobile Subscriber Identification Number)" + }, + "model": { + "bsonType": "string", + "description": "设备型号" + }, + "platform": { + "bsonType": "string", + "description": "平台类型" + }, + "uni_platform": { + "bsonType": "string", + "description": "uni-app 运行平台,与条件编译平台相同。" + }, + "os_name": { + "bsonType": "string", + "description": "ios|android|windows|mac|linux " + }, + "os_version": { + "bsonType": "string", + "description": "操作系统版本号 " + }, + "os_language": { + "bsonType": "string", + "description": "操作系统语言 " + }, + "os_theme": { + "bsonType": "string", + "description": "操作系统主题 light|dark" + }, + "pixel_ratio": { + "bsonType": "string", + "description": "设备像素比 " + }, + "network_model": { + "bsonType": "string", + "description": "设备网络型号wifi\/3G\/4G\/" + }, + "window_width": { + "bsonType": "string", + "description": "设备窗口宽度 " + }, + "window_height": { + "bsonType": "string", + "description": "设备窗口高度" + }, + "screen_width": { + "bsonType": "string", + "description": "设备屏幕宽度" + }, + "screen_height": { + "bsonType": "string", + "description": "设备屏幕高度" + }, + "rom_name": { + "bsonType": "string", + "description": "rom 名称" + }, + "rom_version": { + "bsonType": "string", + "description": "rom 版本" + }, + "location_latitude": { + "bsonType": "double", + "description": "纬度" + }, + "location_longitude": { + "bsonType": "double", + "description": "经度" + }, + "location_country": { + "bsonType": "string", + "description": "国家" + }, + "location_province": { + "bsonType": "string", + "description": "省份" + }, + "location_city": { + "bsonType": "string", + "description": "城市" + }, + "create_date": { + "bsonType": "timestamp", + "description": "创建时间", + "forceDefaultValue": { + "$env": "now" + } + }, + "last_update_date": { + "bsonType": "timestamp", + "description": "最后一次修改时间", + "forceDefaultValue": { + "$env": "now" + } + } + }, + "version": "0.0.1" +} \ No newline at end of file diff --git a/uni_modules/uni-id-pages/uniCloud/database/opendb-frv-logs.schema.json b/uni_modules/uni-id-pages/uniCloud/database/opendb-frv-logs.schema.json new file mode 100644 index 0000000..239fd82 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/database/opendb-frv-logs.schema.json @@ -0,0 +1,44 @@ +{ + "bsonType": "object", + "permission": { + "read": "doc._id == auth.uid || 'CREATE_UNI_ID_USERS' in auth.permission", + "create": "'CREATE_UNI_ID_USERS' in auth.permission", + "update": "doc._id == auth.uid || 'UPDATE_UNI_ID_USERS' in auth.permission", + "delete": "'DELETE_UNI_ID_USERS' in auth.permission" + }, + "properties": { + "_id": { + "description": "存储文档 ID(用户 ID),系统自动生成" + }, + "certify_id": { + "bsonType": "string", + "description": "认证id" + }, + "user_id": { + "bsonType": "string", + "description": "用户id" + }, + "real_name": { + "bsonType": "string", + "description": "姓名" + }, + "identity": { + "bsonType": "string", + "description": "身份证号码" + }, + "status": { + "bsonType": "int", + "description": "认证状态:0 未认证 1 等待认证 2 认证通过 3 认证失败", + "maximum": 3, + "minimum": 0 + }, + "created_date": { + "bsonType": "timestamp", + "description": "创建时间", + "forceDefaultValue": { + "$env": "now" + } + } + }, + "required": [] +} diff --git a/uni_modules/uni-id-pages/uniCloud/database/uni-id-device.schema.json b/uni_modules/uni-id-pages/uniCloud/database/uni-id-device.schema.json new file mode 100644 index 0000000..4981d75 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/database/uni-id-device.schema.json @@ -0,0 +1,83 @@ +{ + "bsonType": "object", + "required": [ + "user_id" + ], + "properties": { + "_id": { + "description": "ID,系统自动生成" + }, + "user_id": { + "bsonType": "string", + "description": "用户id,参考uni-id-users表" + }, + "ua": { + "bsonType": "string", + "description": "userAgent" + }, + "uuid": { + "bsonType": "string", + "description": "设备唯一标识(需要加密存储)" + }, + "os_name": { + "bsonType": "string", + "description": "ios|android|windows|mac|linux " + }, + "os_version": { + "bsonType": "string", + "description": "操作系统版本号 " + }, + "os_language": { + "bsonType": "string", + "description": "操作系统语言 " + }, + "os_theme": { + "bsonType": "string", + "description": "操作系统主题 light|dark" + }, + "vendor": { + "bsonType": "string", + "description": "设备厂商" + }, + "push_clientid": { + "bsonType": "string", + "description": "推送设备客户端标识" + }, + "imei": { + "bsonType": "string", + "description": "国际移动设备识别码IMEI(International Mobile Equipment Identity)" + }, + "oaid": { + "bsonType": "string", + "description": "移动智能设备标识公共服务平台提供的匿名设备标识符(OAID)" + }, + "idfa": { + "bsonType": "string", + "description": "iOS平台配置应用使用广告标识(IDFA)" + }, + "model": { + "bsonType": "string", + "description": "设备型号" + }, + "platform": { + "bsonType": "string", + "description": "平台类型" + }, + "create_date": { + "bsonType": "timestamp", + "description": "创建时间", + "forceDefaultValue": { + "$env": "now" + } + }, + "last_active_date": { + "bsonType": "timestamp", + "description": "最后登录时间" + }, + "last_active_ip": { + "bsonType": "string", + "description": "最后登录IP" + } + }, + "version": "0.0.1" +} \ No newline at end of file diff --git a/uni_modules/uni-id-pages/uniCloud/database/uni-id-log.schema.json b/uni_modules/uni-id-pages/uniCloud/database/uni-id-log.schema.json new file mode 100644 index 0000000..ff4f797 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/database/uni-id-log.schema.json @@ -0,0 +1,71 @@ +{ + "bsonType": "object", + "required": ["user_id"], + "permission": { + "read": "'READ_UNI_ID_LOG' in auth.permission" + }, + "properties": { + "_id": { + "description": "ID,系统自动生成" + }, + "create_date": { + "bsonType": "timestamp", + "description": "创建时间", + "forceDefaultValue": { + "$env": "now" + } + }, + "device_uuid": { + "bsonType": "string", + "description": "设备唯一标识" + }, + "ip": { + "bsonType": "string", + "description": "ip地址" + }, + "state": { + "bsonType": "int", + "description": "结果:0 失败、1 成功" + }, + "type": { + "bsonType": "string", + "description": "操作类型", + "enum": [ + "logout", + "login", + "register", + "reset-pwd", + "bind-mobile", + "bind-weixin", + "bind-qq", + "bind-apple", + "bind-alipay" + ] + }, + "ua": { + "bsonType": "string", + "description": "userAgent" + }, + "user_id": { + "bsonType": "string", + "foreignKey": "uni-id-users._id", + "description": "用户id,参考uni-id-users表" + }, + "username": { + "bsonType": "string", + "description": "用户名" + }, + "email": { + "bsonType": "string", + "description": "邮箱" + }, + "mobile": { + "bsonType": "string", + "description": "手机号" + }, + "appid": { + "bsonType": "string", + "description": "客户端DCloud AppId" + } + } +} diff --git a/uni_modules/uni-id-pages/uniCloud/database/uni-id-permissions.schema.json b/uni_modules/uni-id-pages/uniCloud/database/uni-id-permissions.schema.json new file mode 100644 index 0000000..25209cb --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/database/uni-id-permissions.schema.json @@ -0,0 +1,52 @@ +{ + "bsonType": "object", + "required": ["permission_id", "permission_name"], + "permission": { + "read": "'READ_UNI_ID_PERMISSIONS' in auth.permission", + "create": "'CREATE_UNI_ID_PERMISSIONS' in auth.permission", + "update": "'UPDATE_UNI_ID_PERMISSIONS' in auth.permission", + "delete": "'DELETE_UNI_ID_PERMISSIONS' in auth.permission" + }, + "properties": { + "_id": { + "description": "存储文档 ID,系统自动生成" + }, + "comment": { + "bsonType": "string", + "component": { + "name": "textarea" + }, + "description": "备注", + "label": "备注", + "title": "备注", + "trim": "both" + }, + "create_date": { + "bsonType": "timestamp", + "description": "创建时间", + "forceDefaultValue": { + "$env": "now" + } + }, + "permission_id": { + "bsonType": "string", + "component": { + "name": "input" + }, + "description": "权限唯一标识,不可修改,不允许重复", + "label": "权限标识", + "title": "权限ID", + "trim": "both" + }, + "permission_name": { + "bsonType": "string", + "component": { + "name": "input" + }, + "description": "权限名称", + "label": "权限名称", + "title": "权限名称", + "trim": "both" + } + } +} diff --git a/uni_modules/uni-id-pages/uniCloud/database/uni-id-roles.schema.json b/uni_modules/uni-id-pages/uniCloud/database/uni-id-roles.schema.json new file mode 100644 index 0000000..e2fe322 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/database/uni-id-roles.schema.json @@ -0,0 +1,50 @@ +{ + "bsonType": "object", + "required": ["role_id", "role_name"], + "permission": { + "read": "'READ_UNI_ID_ROLES' in auth.permission", + "create": "'CREATE_UNI_ID_ROLES' in auth.permission", + "update": "'UPDATE_UNI_ID_ROLES' in auth.permission", + "delete": "'DELETE_UNI_ID_ROLES' in auth.permission" + }, + "properties": { + "_id": { + "description": "存储文档 ID,系统自动生成" + }, + "comment": { + "title": "备注", + "bsonType": "string", + "description": "备注", + "trim": "both" + }, + "create_date": { + "bsonType": "timestamp", + "description": "创建时间", + "forceDefaultValue": { + "$env": "now" + } + }, + "permission": { + "title": "权限", + "bsonType": "array", + "foreignKey": "uni-id-permissions.permission_id", + "description": "角色拥有的权限列表", + "enum": { + "collection": "uni-id-permissions", + "field": "permission_name as text, permission_id as value" + } + }, + "role_id": { + "title": "唯一ID", + "bsonType": "string", + "description": "角色唯一标识,不可修改,不允许重复", + "trim": "both" + }, + "role_name": { + "title": "名称", + "bsonType": "string", + "description": "角色名称", + "trim": "both" + } + } +} diff --git a/uni_modules/uni-id-pages/uniCloud/database/uni-id-users.schema.json b/uni_modules/uni-id-pages/uniCloud/database/uni-id-users.schema.json new file mode 100644 index 0000000..b5aea02 --- /dev/null +++ b/uni_modules/uni-id-pages/uniCloud/database/uni-id-users.schema.json @@ -0,0 +1,473 @@ +{ + "bsonType": "object", + "permission": { + "read": true, + "create": "'CREATE_UNI_ID_USERS' in auth.permission", + "update": "doc._id == auth.uid || 'UPDATE_UNI_ID_USERS' in auth.permission", + "delete": "'DELETE_UNI_ID_USERS' in auth.permission" + }, + "properties": { + "_id": { + "description": "存储文档 ID(用户 ID),系统自动生成" + }, + "ali_openid": { + "bsonType": "string", + "description": "支付宝平台openid", + "permission": { + "read": "'READ_UNI_ID_USERS' in auth.permission", + "write": "'CREATE_UNI_ID_USERS' in auth.permission || 'UPDATE_UNI_ID_USERS' in auth.permission" + } + }, + "apple_openid": { + "bsonType": "string", + "description": "苹果登录openid", + "permission": { + "read": "'READ_UNI_ID_USERS' in auth.permission", + "write": "'CREATE_UNI_ID_USERS' in auth.permission || 'UPDATE_UNI_ID_USERS' in auth.permission" + } + }, + "avatar": { + "bsonType": "string", + "description": "头像地址", + "title": "头像地址", + "trim": "both", + "permission": { + "read": true, + "write": "doc._id == auth.uid || 'CREATE_UNI_ID_USERS' in auth.permission || 'UPDATE_UNI_ID_USERS' in auth.permission" + } + }, + "avatar_file": { + "bsonType": "file", + "description": "用file类型方便使用uni-file-picker组件", + "title": "头像文件", + "permission": { + "read": true, + "write": "doc._id == auth.uid || 'CREATE_UNI_ID_USERS' in auth.permission || 'UPDATE_UNI_ID_USERS' in auth.permission" + } + }, + "comment": { + "bsonType": "string", + "description": "备注", + "title": "备注", + "trim": "both", + "permission": { + "read": "'READ_UNI_ID_USERS' in auth.permission", + "write": "'CREATE_UNI_ID_USERS' in auth.permission || 'UPDATE_UNI_ID_USERS' in auth.permission" + } + }, + "dcloud_appid": { + "bsonType": "array", + "description": "允许登录的客户端的appid列表", + "foreignKey": "opendb-app-list.appid", + "permission": { + "read": "'READ_UNI_ID_USERS' in auth.permission", + "write": "'CREATE_UNI_ID_USERS' in auth.permission || 'UPDATE_UNI_ID_USERS' in auth.permission" + } + }, + "department_id": { + "bsonType": "array", + "description": "部门ID", + "enum": { + "collection": "opendb-department", + "field": "_id as value, name as text", + "orderby": "name asc" + }, + "enumType": "tree", + "title": "部门", + "permission": { + "read": "doc._id == auth.uid || 'READ_UNI_ID_USERS' in auth.permission", + "write": "'CREATE_UNI_ID_USERS' in auth.permission || 'UPDATE_UNI_ID_USERS' in auth.permission" + } + }, + "email": { + "bsonType": "string", + "description": "邮箱地址", + "format": "email", + "title": "邮箱", + "trim": "both", + "permission": { + "read": "doc._id == auth.uid || 'READ_UNI_ID_USERS' in auth.permission", + "write": "'CREATE_UNI_ID_USERS' in auth.permission || 'UPDATE_UNI_ID_USERS' in auth.permission" + } + }, + "email_confirmed": { + "bsonType": "int", + "defaultValue": 0, + "description": "邮箱验证状态:0 未验证 1 已验证", + "enum": [{ + "text": "未验证", + "value": 0 + }, + { + "text": "已验证", + "value": 1 + } + ], + "title": "邮箱验证状态", + "permission": { + "read": "doc._id == auth.uid || 'READ_UNI_ID_USERS' in auth.permission", + "write": "'CREATE_UNI_ID_USERS' in auth.permission || 'UPDATE_UNI_ID_USERS' in auth.permission" + } + }, + "gender": { + "bsonType": "int", + "defaultValue": 0, + "description": "用户性别:0 未知 1 男性 2 女性", + "enum": [{ + "text": "未知", + "value": 0 + }, + { + "text": "男", + "value": 1 + }, + { + "text": "女", + "value": 2 + } + ], + "title": "性别", + "permission": { + "read": "doc._id == auth.uid || 'READ_UNI_ID_USERS' in auth.permission", + "write": "'CREATE_UNI_ID_USERS' in auth.permission || 'UPDATE_UNI_ID_USERS' in auth.permission" + } + }, + "invite_time": { + "bsonType": "timestamp", + "description": "受邀时间", + "permission": { + "read": "doc._id == auth.uid || 'READ_UNI_ID_USERS' in auth.permission", + "write": "'CREATE_UNI_ID_USERS' in auth.permission || 'UPDATE_UNI_ID_USERS' in auth.permission" + } + }, + "inviter_uid": { + "bsonType": "array", + "description": "用户全部上级邀请者", + "trim": "both", + "permission": { + "read": "doc._id == auth.uid || 'READ_UNI_ID_USERS' in auth.permission", + "write": "'CREATE_UNI_ID_USERS' in auth.permission || 'UPDATE_UNI_ID_USERS' in auth.permission" + } + }, + "last_login_date": { + "bsonType": "timestamp", + "description": "最后登录时间", + "permission": { + "read": "doc._id == auth.uid || 'READ_UNI_ID_USERS' in auth.permission", + "write": "'CREATE_UNI_ID_USERS' in auth.permission || 'UPDATE_UNI_ID_USERS' in auth.permission" + } + }, + "last_login_ip": { + "bsonType": "string", + "description": "最后登录时 IP 地址", + "permission": { + "read": "doc._id == auth.uid || 'READ_UNI_ID_USERS' in auth.permission", + "write": "'CREATE_UNI_ID_USERS' in auth.permission || 'UPDATE_UNI_ID_USERS' in auth.permission" + } + }, + "mobile": { + "bsonType": "string", + "description": "手机号码", + "pattern": "^\\+?[0-9-]{3,20}$", + "title": "手机号码", + "trim": "both", + "permission": { + "read": "doc._id == auth.uid || 'READ_UNI_ID_USERS' in auth.permission", + "write": "'CREATE_UNI_ID_USERS' in auth.permission || 'UPDATE_UNI_ID_USERS' in auth.permission" + } + }, + "mobile_confirmed": { + "bsonType": "int", + "defaultValue": 0, + "description": "手机号验证状态:0 未验证 1 已验证", + "enum": [{ + "text": "未验证", + "value": 0 + }, + { + "text": "已验证", + "value": 1 + } + ], + "title": "手机号验证状态", + "permission": { + "read": "doc._id == auth.uid || 'READ_UNI_ID_USERS' in auth.permission", + "write": "'CREATE_UNI_ID_USERS' in auth.permission || 'UPDATE_UNI_ID_USERS' in auth.permission" + } + }, + "my_invite_code": { + "bsonType": "string", + "description": "用户自身邀请码", + "permission": { + "read": "doc._id == auth.uid || 'READ_UNI_ID_USERS' in auth.permission", + "write": "'CREATE_UNI_ID_USERS' in auth.permission || 'UPDATE_UNI_ID_USERS' in auth.permission" + } + }, + "nickname": { + "bsonType": "string", + "description": "用户昵称", + "title": "昵称", + "trim": "both", + "permission": { + "read": true, + "write": "doc._id == auth.uid || 'CREATE_UNI_ID_USERS' in auth.permission || 'UPDATE_UNI_ID_USERS' in auth.permission" + } + }, + "password": { + "bsonType": "password", + "description": "密码,加密存储", + "title": "密码", + "trim": "both" + }, + "password_secret_version": { + "bsonType": "int", + "description": "密码使用的passwordSecret版本", + "title": "passwordSecret", + "permission": { + "read": false, + "write": false + } + }, + "realname_auth": { + "bsonType": "object", + "description": "实名认证信息", + "permission": { + "read": "doc._id == auth.uid || 'READ_UNI_ID_USERS' in auth.permission", + "write": "'CREATE_UNI_ID_USERS' in auth.permission || 'UPDATE_UNI_ID_USERS' in auth.permission" + }, + "properties": { + "auth_date": { + "bsonType": "timestamp", + "description": "认证通过时间" + }, + "auth_status": { + "bsonType": "int", + "description": "认证状态:0 未认证 1 等待认证 2 认证通过 3 认证失败", + "maximum": 3, + "minimum": 0 + }, + "contact_email": { + "bsonType": "string", + "description": "联系人邮箱" + }, + "contact_mobile": { + "bsonType": "string", + "description": "联系人手机号码" + }, + "contact_person": { + "bsonType": "string", + "description": "联系人姓名" + }, + "id_card_back": { + "bsonType": "string", + "description": "身份证反面照 URL" + }, + "id_card_front": { + "bsonType": "string", + "description": "身份证正面照 URL" + }, + "identity": { + "bsonType": "string", + "description": "身份证号码/营业执照号码" + }, + "in_hand": { + "bsonType": "string", + "description": "手持身份证照片 URL" + }, + "license": { + "bsonType": "string", + "description": "营业执照 URL" + }, + "real_name": { + "bsonType": "string", + "description": "真实姓名/企业名称" + }, + "type": { + "bsonType": "int", + "description": "用户类型:0 个人用户 1 企业用户", + "maximum": 1, + "minimum": 0 + } + }, + "required": [ + "type", + "auth_status" + ] + }, + "register_date": { + "bsonType": "timestamp", + "description": "注册时间", + "forceDefaultValue": { + "$env": "now" + }, + "permission": { + "read": "doc._id == auth.uid || 'READ_UNI_ID_USERS' in auth.permission", + "write": "'CREATE_UNI_ID_USERS' in auth.permission || 'UPDATE_UNI_ID_USERS' in auth.permission" + } + }, + "register_ip": { + "bsonType": "string", + "description": "注册时 IP 地址", + "forceDefaultValue": { + "$env": "clientIP" + }, + "permission": { + "read": "doc._id == auth.uid || 'READ_UNI_ID_USERS' in auth.permission", + "write": "'CREATE_UNI_ID_USERS' in auth.permission || 'UPDATE_UNI_ID_USERS' in auth.permission" + } + }, + "role": { + "bsonType": "array", + "description": "用户角色", + "enum": { + "collection": "uni-id-roles", + "field": "role_id as value, role_name as text" + }, + "foreignKey": "uni-id-roles.role_id", + "permission": { + "read": "doc._id == auth.uid || 'READ_UNI_ID_USERS' in auth.permission", + "write": "'CREATE_UNI_ID_USERS' in auth.permission || 'UPDATE_UNI_ID_USERS' in auth.permission" + }, + "title": "角色" + }, + "tags":{ + "bsonType": "array", + "description": "用户标签", + "permission": { + "read": "doc._id == auth.uid || 'READ_UNI_ID_USERS' in auth.permission", + "write": "'CREATE_UNI_ID_USERS' in auth.permission || 'UPDATE_UNI_ID_USERS' in auth.permission" + }, + "title": "标签" + }, + "score": { + "bsonType": "int", + "description": "用户积分,积分变更记录可参考:uni-id-scores表定义", + "permission": { + "read": "doc._id == auth.uid || 'READ_UNI_ID_USERS' in auth.permission", + "write": "'CREATE_UNI_ID_USERS' in auth.permission || 'UPDATE_UNI_ID_USERS' in auth.permission" + } + }, + "status": { + "bsonType": "int", + "defaultValue": 0, + "description": "用户状态:0 正常 1 禁用 2 审核中 3 审核拒绝", + "permission": { + "read": "doc._id == auth.uid || 'READ_UNI_ID_USERS' in auth.permission", + "write": "'CREATE_UNI_ID_USERS' in auth.permission || 'UPDATE_UNI_ID_USERS' in auth.permission" + }, + "enum": [{ + "text": "正常", + "value": 0 + }, + { + "text": "禁用", + "value": 1 + }, + { + "text": "审核中", + "value": 2 + }, + { + "text": "审核拒绝", + "value": 3 + } + ], + "title": "用户状态" + }, + "token": { + "bsonType": "array", + "description": "用户token", + "permission": { + "read": false, + "write": "'CREATE_UNI_ID_USERS' in auth.permission || 'UPDATE_UNI_ID_USERS' in auth.permission" + } + }, + "username": { + "bsonType": "string", + "description": "用户名,不允许重复", + "title": "用户名", + "trim": "both", + "permission": { + "read": "doc._id == auth.uid || 'READ_UNI_ID_USERS' in auth.permission", + "write": "'CREATE_UNI_ID_USERS' in auth.permission || 'UPDATE_UNI_ID_USERS' in auth.permission" + } + }, + "wx_openid": { + "bsonType": "object", + "description": "微信各个平台openid", + "properties": { + "app": { + "bsonType": "string", + "description": "app平台微信openid" + }, + "mp": { + "bsonType": "string", + "description": "微信小程序平台openid" + }, + "h5": { + "bsonType": "string", + "description": "微信公众号登录openid" + }, + "web": { + "bsonType": "string", + "description": "PC页面扫码登录openid" + } + }, + "permission": { + "read": "'READ_UNI_ID_USERS' in auth.permission", + "write": "'CREATE_UNI_ID_USERS' in auth.permission || 'UPDATE_UNI_ID_USERS' in auth.permission" + } + }, + "wx_unionid": { + "bsonType": "string", + "description": "微信unionid", + "permission": { + "read": "'READ_UNI_ID_USERS' in auth.permission", + "write": "'CREATE_UNI_ID_USERS' in auth.permission || 'UPDATE_UNI_ID_USERS' in auth.permission" + } + }, + "qq_openid": { + "bsonType": "object", + "description": "QQ各个平台openid", + "properties": { + "app": { + "bsonType": "string", + "description": "app平台QQ openid" + }, + "mp": { + "bsonType": "string", + "description": "QQ小程序平台openid" + } + }, + "permission": { + "read": "'READ_UNI_ID_USERS' in auth.permission", + "write": "'CREATE_UNI_ID_USERS' in auth.permission || 'UPDATE_UNI_ID_USERS' in auth.permission" + } + }, + "qq_unionid": { + "bsonType": "string", + "description": "QQ unionid", + "permission": { + "read": "'READ_UNI_ID_USERS' in auth.permission", + "write": "'CREATE_UNI_ID_USERS' in auth.permission || 'UPDATE_UNI_ID_USERS' in auth.permission" + } + }, + "third_party": { + "bsonType": "object", + "description": "三方平台凭证", + "permission": { + "read": false, + "write": false + } + }, + "identities": { + "bsonType": "array", + "description": "三方平台身份信息;一个对象代表一个身份,参数支持: provider 身份源, userInfo 三方用户信息, openid 三方openid, unionid 三方unionid, uid 三方uid", + "permission": { + "read": "'READ_UNI_ID_USERS' in auth.permission", + "write": "'CREATE_UNI_ID_USERS' in auth.permission || 'UPDATE_UNI_ID_USERS' in auth.permission" + } + } + }, + "required": [] +} diff --git a/uni_modules/uni-image-menu/changelog.md b/uni_modules/uni-image-menu/changelog.md new file mode 100644 index 0000000..e69de29 diff --git a/uni_modules/uni-image-menu/js_sdk/uni-image-menu.js b/uni_modules/uni-image-menu/js_sdk/uni-image-menu.js new file mode 100644 index 0000000..b482b83 --- /dev/null +++ b/uni_modules/uni-image-menu/js_sdk/uni-image-menu.js @@ -0,0 +1,169 @@ +var nvMask,nvImageMenu; +export default { + show({list,cancelText},callback){ + console.log(789789879); + if(!list){ + list = [{ + "img":"/static/sharemenu/wechatfriend.png", + "text":"图标文字" + }] + } + //以下为计算菜单的nview绘制布局,为固定算法,使用者无关关心 + var screenWidth = plus.screen.resolutionWidth + //以360px宽度屏幕为例,上下左右边距及2排按钮边距留25像素,图标宽度55像素,同行图标间的间距在360宽的屏幕是30px,但需要动态计算,以此原则计算4列图标分别的left位置 + //图标下的按钮文字距离图标5像素,文字大小12像素 + //底部取消按钮高度固定为44px + //TODO 未处理横屏和pad,这些情况6个图标应该一排即可 + var margin = 20, + iconWidth = 60, + icontextSpace = 5, + textHeight = 12 + var left1 = margin / 360 * screenWidth + var iconSpace = (screenWidth - (left1 * 2) - (iconWidth * 4)) / 3 //屏幕宽度减去左右留白间距,再减去4个图标的宽度,就是3个同行图标的间距 + if (iconSpace <= 5) { //屏幕过窄时,缩小边距和图标大小,再算一次 + margin = 15 + iconWidth = 40 + left1 = margin / 360 * screenWidth + iconSpace = (screenWidth - (left1 * 2) - (iconWidth * 4)) / 3 //屏幕宽度减去左右留白间距,再减去4个图标的宽度,就是3个同行图标的间距 + } + var left2 = left1 + iconWidth + iconSpace + var left3 = left1 + (iconWidth + iconSpace) * 2 + var left4 = left1 + (iconWidth + iconSpace) * 3 + var top1 = left1 + var top2 = top1 + iconWidth + icontextSpace + textHeight + left1 + + const TOP = {top1,top2}, LEFT = {left1,left2,left3,left4}; + + nvMask = new plus.nativeObj.View("nvMask", { //先创建遮罩层 + top: '0px', + left: '0px', + height: '100%', + width: '100%', + backgroundColor: 'rgba(0,0,0,0.2)' + }); + nvMask.addEventListener("click", function() { //处理遮罩层点击 + nvMask.hide(); + nvImageMenu.hide(); + }) + nvImageMenu = new plus.nativeObj.View("nvImageMenu", { //创建底部图标菜单 + bottom: '0px', + left: '0px', + height: (iconWidth + textHeight + 2 * margin)*Math.ceil(list.length/4) +44+'px',//'264px', + width: '100%', + backgroundColor: 'rgb(255,255,255)' + }); + + let myList = [] + list.forEach((item,i)=>{ + myList.push({ + tag: 'img', + src: item.img, + position: { + top: TOP['top'+( parseInt(i/4) +1)], + left: LEFT['left'+(1+i%4)], + width: iconWidth, + height: iconWidth + } + }) + myList.push({ + tag: 'font', + text: item.text, + textStyles: { + size: textHeight + }, + position: { + top: TOP['top'+(parseInt(i/4)+1)] + iconWidth + icontextSpace, + left: LEFT['left'+(1+i%4)], + width: iconWidth, + height: textHeight + } + }) + }) + + //绘制底部图标菜单的内容 + nvImageMenu.draw([ + { + tag: 'rect',//菜单顶部的分割灰线 + color: '#e7e7e7', + position: { + top: '0px', + height: '1px' + } + }, + { + tag: 'font', + text: cancelText,//底部取消按钮的文字 + textStyles: { + size: '14px' + }, + position: { + bottom: '0px', + height: '44px' + } + }, + { + tag: 'rect',//底部取消按钮的顶部边线 + color: '#e7e7e7', + position: { + bottom: '45px', + height: '1px' + } + }, + ...myList + ]) + nvMask.show() + nvImageMenu.show() //5+应支持从底部向上弹出的动画 + + + + nvImageMenu.addEventListener("click",e=>{ //处理底部图标菜单的点击事件,根据点击位置触发不同的逻辑 + // console.log("click menu"+JSON.stringify(e)); + if (e.screenY > plus.screen.resolutionHeight - 44) { //点击了底部取消按钮 + nvMask.hide(); + nvImageMenu.hide(); + } else if (e.clientX < 5 || e.clientX > screenWidth - 5 || e.clientY < 5) { + //屏幕左右边缘5像素及菜单顶部5像素不处理点击 + } else { //点击了图标按钮 + var iClickIndex = -1 //点击的图标按钮序号,第一个图标按钮的index为0 + var iRow = e.clientY < (top2 - (left1 / 2)) ? 0 : 1 + var iCol = -1 + if (e.clientX < (left2 - (iconSpace / 2))) { + iCol = 0 + } else if (e.clientX < (left3 - (iconSpace / 2))) { + iCol = 1 + } else if (e.clientX < (left4 - (iconSpace / 2))) { + iCol = 2 + } else { + iCol = 3 + } + if (iRow == 0) { + iClickIndex = iCol + } else { + iClickIndex = iCol + 4 + } + // console.log("点击按钮的序号: " + iClickIndex); + // if (iClickIndex >= 0 && iClickIndex <= 5) { //处理具体的点击逻辑,此处也可以自行定义逻辑。如果增减了按钮,此处也需要跟着修改 + // } + callback(iClickIndex) + this.hide() + } + }) + /* nvImageMenu.addEventListener("touchstart", function(e) { + if (e.screenY > (plus.screen.resolutionHeight - 44)) { + //TODO 这里可以处理按下背景变灰的效果 + } + }) + nvImageMenu.addEventListener("touchmove", function(e) { + //TODO 这里可以处理按下背景变灰的效果 + if (e.screenY > plus.screen.resolutionHeight - 44) {} + }) + nvImageMenu.addEventListener("touchend", function(e) { + //TODO 这里可以处理释放背景恢复的效果 + }) + */ + }, + hide(){ + nvMask.hide() + nvImageMenu.hide() + } +} \ No newline at end of file diff --git a/uni_modules/uni-image-menu/package.json b/uni_modules/uni-image-menu/package.json new file mode 100644 index 0000000..5938aa9 --- /dev/null +++ b/uni_modules/uni-image-menu/package.json @@ -0,0 +1,76 @@ +{ + "id": "uni-image-menu", + "displayName": "uni-image-menu", + "version": "1.0.0", + "description": "uni-image-menu", + "keywords": [ + "uni-image-menu" +], + "repository": "", + "engines": { + "HBuilderX": "^3.1.0" + }, + "dcloudext": { + "category": [ + "JS SDK", + "JS SDK" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "", + "data": "", + "permissions": "" + }, + "npmurl": "" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "u", + "aliyun": "u" + }, + "client": { + "App": { + "app-vue": "u", + "app-nvue": "u" + }, + "H5-mobile": { + "Safari": "u", + "Android Browser": "u", + "微信浏览器(Android)": "u", + "QQ浏览器(Android)": "u" + }, + "H5-pc": { + "Chrome": "u", + "IE": "u", + "Edge": "u", + "Firefox": "u", + "Safari": "u" + }, + "小程序": { + "微信": "u", + "阿里": "u", + "百度": "u", + "字节跳动": "u", + "QQ": "u" + }, + "快应用": { + "华为": "u", + "联盟": "u" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-image-menu/readme.md b/uni_modules/uni-image-menu/readme.md new file mode 100644 index 0000000..ecca1b0 --- /dev/null +++ b/uni_modules/uni-image-menu/readme.md @@ -0,0 +1,19 @@ +# uni-image-menu + +使用示例: +``` +import uniImageMenu from 'uni_modules/uni-image-menu/js_sdk/uni-image-menu.js'; +uniImageMenu.show({ + list:[{ + "img": "/static/sharemenu/wechatfriend.png", + "text": "微信好友" + }, + { + "img": "/static/sharemenu/wechatmoments.png", + "text": "微信朋友圈" + }], + cancelText:param.cancelText +}, e => { + console.log(e) +}) +``` \ No newline at end of file diff --git a/uni_modules/uni-indexed-list/changelog.md b/uni_modules/uni-indexed-list/changelog.md new file mode 100644 index 0000000..08fa71c --- /dev/null +++ b/uni_modules/uni-indexed-list/changelog.md @@ -0,0 +1,17 @@ +## 1.2.1(2021-11-22) +- 修复 vue3中某些scss变量无法找到的问题 +## 1.2.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-indexed-list](https://uniapp.dcloud.io/component/uniui/uni-indexed-list) +## 1.1.0(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.0.11(2021-05-12) +- 新增 组件示例地址 +## 1.0.10(2021-04-21) +- 优化 添加依赖 uni-icons, 导入后自动下载依赖 +## 1.0.9(2021-02-05) +- 优化 组件引用关系,通过uni_modules引用组件 + +## 1.0.8(2021-02-05) +- 调整为uni_modules目录规范 +- 新增 支持 PC 端 diff --git a/uni_modules/uni-indexed-list/components/uni-indexed-list/uni-indexed-list-item.vue b/uni_modules/uni-indexed-list/components/uni-indexed-list/uni-indexed-list-item.vue new file mode 100644 index 0000000..19284bd --- /dev/null +++ b/uni_modules/uni-indexed-list/components/uni-indexed-list/uni-indexed-list-item.vue @@ -0,0 +1,144 @@ + + + + + diff --git a/uni_modules/uni-indexed-list/components/uni-indexed-list/uni-indexed-list.vue b/uni_modules/uni-indexed-list/components/uni-indexed-list/uni-indexed-list.vue new file mode 100644 index 0000000..ee3a7ec --- /dev/null +++ b/uni_modules/uni-indexed-list/components/uni-indexed-list/uni-indexed-list.vue @@ -0,0 +1,367 @@ + + + diff --git a/uni_modules/uni-indexed-list/package.json b/uni_modules/uni-indexed-list/package.json new file mode 100644 index 0000000..125c0e7 --- /dev/null +++ b/uni_modules/uni-indexed-list/package.json @@ -0,0 +1,89 @@ +{ + "id": "uni-indexed-list", + "displayName": "uni-indexed-list 索引列表", + "version": "1.2.1", + "description": "索引列表组件,右侧带索引的列表,方便快速定位到具体内容,通常用于城市/机场选择等场景", + "keywords": [ + "uni-ui", + "索引列表", + "索引", + "列表" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": [ + "uni-scss", + "uni-icons" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} diff --git a/uni_modules/uni-indexed-list/readme.md b/uni_modules/uni-indexed-list/readme.md new file mode 100644 index 0000000..44ad84b --- /dev/null +++ b/uni_modules/uni-indexed-list/readme.md @@ -0,0 +1,11 @@ + + +## IndexedList 索引列表 +> **组件名:uni-indexed-list** +> 代码块: `uIndexedList` + + +用于展示索引列表。 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-indexed-list) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 diff --git a/uni_modules/uni-installApk/changelog.md b/uni_modules/uni-installApk/changelog.md new file mode 100644 index 0000000..774de0e --- /dev/null +++ b/uni_modules/uni-installApk/changelog.md @@ -0,0 +1,12 @@ +## 1.0.5(2024-04-28) +解决Android API 24版本以下设备,安装apk失败的问题。 +## 1.0.4(2023-12-08) +兼容asset目录文件的处理 +## 1.0.3(2023-10-27) +遵循UniError规范 +## 1.0.2(2023-10-27) +修改文档 +## 1.0.1(2023-10-27) +支持js层调用 +## 1.0.0(2023-10-26) +安装apk的插件 diff --git a/uni_modules/uni-installApk/package.json b/uni_modules/uni-installApk/package.json new file mode 100644 index 0000000..dab4d31 --- /dev/null +++ b/uni_modules/uni-installApk/package.json @@ -0,0 +1,94 @@ +{ + "id": "uni-installApk", + "displayName": "uni-installApk", + "version": "1.0.5", + "description": "uni-installApk", + "keywords": [ + "uni-installApk" + ], + "repository": "", + "engines": { + "HBuilderX": "^3.94" + }, + "dcloudext": { + "type": "uts", + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "android.permission.REQUEST_INSTALL_PACKAGES" + }, + "npmurl": "" + }, + "uni_modules": { + "dependencies": [], + "uni-ext-api": { + "uni": { + "installApk": { + "name": "installApk", + "app": { + "js": true, + "kotlin": true, + "swift": false + } + } + } + }, + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "n" + }, + "client": { + "Vue": { + "vue2": "y", + "vue3": "y" + }, + "App": { + "app-android": "y", + "app-ios": "n" + }, + "H5-mobile": { + "Safari": "n", + "Android Browser": "n", + "微信浏览器(Android)": "n", + "QQ浏览器(Android)": "n" + }, + "H5-pc": { + "Chrome": "n", + "IE": "n", + "Edge": "n", + "Firefox": "n", + "Safari": "n" + }, + "小程序": { + "微信": "n", + "阿里": "n", + "百度": "n", + "字节跳动": "n", + "QQ": "n", + "钉钉": "n", + "快手": "n", + "飞书": "n", + "京东": "n" + }, + "快应用": { + "华为": "n", + "联盟": "n" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-installApk/readme.md b/uni_modules/uni-installApk/readme.md new file mode 100644 index 0000000..898834d --- /dev/null +++ b/uni_modules/uni-installApk/readme.md @@ -0,0 +1,37 @@ +# uni-installApk + +## 使用说明 + +Android平台安装Apk + +**注意: 3.95以下需要自定义基座** + +### uni.installApk(options : InstallApkOptions):void + +安装apk + +参数说明 + +``` +type InstallApkOptions = { + /** + * apk文件地址 + */ + filePath : string, + /** + * 接口调用成功的回调函数 + * @defaultValue null + */ + success ?: (res : any) => void, + /** + * 接口调用失败的回调函数 + * @defaultValue null + */ + fail ?: (err : any) => void, + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + * @defaultValue null + */ + complete ?: (res : any) => void, +} +``` \ No newline at end of file diff --git a/uni_modules/uni-installApk/utssdk/app-android/AndroidManifest.xml b/uni_modules/uni-installApk/utssdk/app-android/AndroidManifest.xml new file mode 100644 index 0000000..24e5daa --- /dev/null +++ b/uni_modules/uni-installApk/utssdk/app-android/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/uni_modules/uni-installApk/utssdk/app-android/index.uts b/uni_modules/uni-installApk/utssdk/app-android/index.uts new file mode 100644 index 0000000..42ac922 --- /dev/null +++ b/uni_modules/uni-installApk/utssdk/app-android/index.uts @@ -0,0 +1,98 @@ +import { InstallApkOptions, InstallApkSuccess } from "../interface.uts" +import { InstallApkFailImpl } from "../unierror.uts" +import Intent from 'android.content.Intent'; +import Build from 'android.os.Build'; +import File from 'java.io.File'; +import FileProvider from 'androidx.core.content.FileProvider'; +import Context from 'android.content.Context'; +import Uri from 'android.net.Uri'; +import FileOutputStream from 'java.io.FileOutputStream'; +import IOException from 'java.io.IOException'; + +export function installApk(options : InstallApkOptions) : void { + const context = UTSAndroid.getAppContext() as Context + var filePath = UTSAndroid.convert2AbsFullPath(options.filePath) + var apkFile : File | null = null; + if (filePath.startsWith("/android_asset/")) { + filePath = filePath.replace("/android_asset/", "") + apkFile = copyAssetFileToPrivateDir(context, filePath) + } else { + apkFile = new File(filePath) + } + + if (apkFile != null && !apkFile.exists() && !apkFile.isFile()) { + let error = new InstallApkFailImpl(1300002); + options.fail?.(error) + options.complete?.(error) + return + } + const intent = new Intent() + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + intent.setAction(Intent.ACTION_VIEW) + + if (Build.VERSION.SDK_INT >= 24) { + const authority = context.getPackageName() + ".dc.fileprovider" + const apkUri = FileProvider.getUriForFile(context, authority, apkFile!!) + intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + intent.setDataAndType(apkUri, "application/vnd.android.package-archive"); + } else { + intent.setDataAndType(Uri.fromFile(apkFile!!), "application/vnd.android.package-archive"); + } + + context.startActivity(intent) + const success : InstallApkSuccess = { + errMsg: "success" + } + options.success?.(success) + options.complete?.(success) +} + + +function copyAssetFileToPrivateDir(context : Context, fileName : string) : File | null { + try { + const destPath = context.getCacheDir().getPath() + "/apks/" + fileName + const outFile = new File(destPath) + const parentFile = outFile.getParentFile() + if (parentFile != null) { + if (!parentFile.exists()) { + parentFile.mkdirs() + } + } + if (!outFile.exists()) { + outFile.createNewFile() + } + const inputStream = context.getAssets().open(fileName) + const outputStream = new FileOutputStream(outFile) + let buffer = new ByteArray(1024); + do { + let len = inputStream.read(buffer); + if (len == -1) { + break; + } + outputStream.write(buffer, 0, len) + } while (true) + + inputStream.close() + outputStream.close() + + + if (Build.VERSION.SDK_INT < 24) { + changePermissionRecursive(new File(context.getCacheDir().getPath() + "/apks/")) + } + + return outFile + } catch (e : Exception) { + e.printStackTrace() + } + + return null +} + +function changePermissionRecursive(file: File){ + const cmd = "chmod -R 777 " + file.getAbsolutePath() + const runtime = Runtime.getRuntime() + try { + runtime.exec(cmd) + } catch (e: IOException) { + } +} diff --git a/uni_modules/uni-installApk/utssdk/index.d.ts b/uni_modules/uni-installApk/utssdk/index.d.ts new file mode 100644 index 0000000..05583cb --- /dev/null +++ b/uni_modules/uni-installApk/utssdk/index.d.ts @@ -0,0 +1,78 @@ +declare namespace UniNamespace { + +interface InstallApkSuccess { + /** + * 安装成功消息 + */ + errMsg : string +} + +type InstallApkErrorCode = 1300002 +interface InstallApkFail { + errCode : InstallApkErrorCode +} + +type InstallApkComplete = any + +type InstallApkSuccessCallback = (res : InstallApkSuccess) => void +type InstallApkFailCallback = (err : InstallApkFail) => void +type InstallApkCompleteCallback = (res : InstallApkComplete) => void + +interface InstallApkOptions { + /** + * apk文件地址 + */ + filePath : string, + /** + * 接口调用成功的回调函数 + * @defaultValue null + */ + success ?: InstallApkSuccessCallback | null, + /** + * 接口调用失败的回调函数 + * @defaultValue null + */ + fail ?: InstallApkFailCallback | null, + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + * @defaultValue null + */ + complete ?: InstallApkCompleteCallback | null +} + +} + + +declare interface Uni { + /** + * installApk() + * @description + * 安装apk + * @param {InstallApkOptions} + * @return {void} + * @uniPlatform { + * "app": { + * "android": { + * "osVer": "5.0", + * "uniVer": "3.94+", + * "unixVer": "3.94+" + * }, + * "ios": { + * "osVer": "x", + * "uniVer": "x", + * "unixVer": "x" + * } + * } + * } + * @example + ```typescript + uni.installApk({ + filePath: "/xx/xx/xx.apk", + complete: (res: any) => { + console.log("complete => " + JSON.stringify(res)); + } + }); + ``` + */ + installApk(options : UniNamespace.InstallApkOptions) : void +} diff --git a/uni_modules/uni-installApk/utssdk/interface.uts b/uni_modules/uni-installApk/utssdk/interface.uts new file mode 100644 index 0000000..c21e443 --- /dev/null +++ b/uni_modules/uni-installApk/utssdk/interface.uts @@ -0,0 +1,80 @@ +export interface Uni { + /** + * installApk() + * @description + * 安装apk + * @param {InstallApkOptions} + * @return {void} + * @uniPlatform { + * "app": { + * "android": { + * "osVer": "5.0", + * "uniVer": "3.94+", + * "unixVer": "3.94+" + * }, + * "ios": { + * "osVer": "x", + * "uniVer": "x", + * "unixVer": "x" + * } + * }, + * "web": { + * "uniVer": "x", + * "unixVer": "x" + * } + * } + * @example + ```typescript + uni.installApk({ + filePath: "/xx/xx/xx.apk", + complete: (res: any) => { + console.log("complete => " + JSON.stringify(res)); + } + }); + ``` + */ + installApk(options : InstallApkOptions) : void +} +export type InstallApkSuccess = { + /** + * 安装成功消息 + */ + errMsg : string +} +export type InstallApkComplete = any +export type InstallApkSuccessCallback = (res : InstallApkSuccess) => void +/** + * 错误码 + * - 1300002 找不到文件 + */ +export type InstallApkErrorCode = 1300002 +/** + * 网络请求失败的错误回调参数 + */ +export interface InstallApkFail extends IUniError { + errCode : InstallApkErrorCode +}; +export type InstallApkFailCallback = (err : InstallApkFail) => void +export type InstallApkCompleteCallback = (res : InstallApkComplete) => void + +export type InstallApkOptions = { + /** + * apk文件地址 + */ + filePath : string, + /** + * 接口调用成功的回调函数 + * @defaultValue null + */ + success ?: InstallApkSuccessCallback | null, + /** + * 接口调用失败的回调函数 + * @defaultValue null + */ + fail ?: InstallApkFailCallback | null, + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + * @defaultValue null + */ + complete ?: InstallApkCompleteCallback | null +} \ No newline at end of file diff --git a/uni_modules/uni-installApk/utssdk/unierror.uts b/uni_modules/uni-installApk/utssdk/unierror.uts new file mode 100644 index 0000000..4f13e32 --- /dev/null +++ b/uni_modules/uni-installApk/utssdk/unierror.uts @@ -0,0 +1,25 @@ +import { InstallApkErrorCode, InstallApkFail } from "./interface.uts" + +/** + * 错误主题 + */ +export const UniErrorSubject = 'uni-installApk'; +/** + * 错误码 + * @UniError + */ +export const UniErrors : Map = new Map([ + /** + * 找不到文件 + */ + [1300002, 'No such file'], +]); + +export class InstallApkFailImpl extends UniError implements InstallApkFail { + constructor(errCode : InstallApkErrorCode) { + super(); + this.errSubject = UniErrorSubject; + this.errCode = errCode; + this.errMsg = UniErrors[errCode] ?? ""; + } +} \ No newline at end of file diff --git a/uni_modules/uni-link/changelog.md b/uni_modules/uni-link/changelog.md new file mode 100644 index 0000000..2cfbf59 --- /dev/null +++ b/uni_modules/uni-link/changelog.md @@ -0,0 +1,17 @@ +## 1.0.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-link](https://uniapp.dcloud.io/component/uniui/uni-link) +## 1.1.7(2021-11-08) +## 0.0.7(2021-09-03) +- 修复 在 nvue 下不显示的 bug +## 0.0.6(2021-07-30) +- 新增 支持自定义插槽 +## 0.0.5(2021-06-21) +- 新增 download 属性,H5平台下载文件名 +## 0.0.4(2021-05-12) +- 新增 组件示例地址 +## 0.0.3(2021-03-09) +- 新增 href 属性支持 tel:|mailto: + +## 0.0.2(2021-02-05) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-link/components/uni-link/uni-link.vue b/uni_modules/uni-link/components/uni-link/uni-link.vue new file mode 100644 index 0000000..27c5468 --- /dev/null +++ b/uni_modules/uni-link/components/uni-link/uni-link.vue @@ -0,0 +1,128 @@ + + + + + diff --git a/uni_modules/uni-link/package.json b/uni_modules/uni-link/package.json new file mode 100644 index 0000000..77b1986 --- /dev/null +++ b/uni_modules/uni-link/package.json @@ -0,0 +1,87 @@ +{ + "id": "uni-link", + "displayName": "uni-link 超链接", + "version": "1.0.0", + "description": "uni-link是一个外部网页超链接组件,在小程序内复制url,在app内打开外部浏览器,在h5端打", + "keywords": [ + "uni-ui", + "uniui", + "link", + "超链接", + "" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": ["uni-scss"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "y", + "联盟": "y" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-link/readme.md b/uni_modules/uni-link/readme.md new file mode 100644 index 0000000..7f09e94 --- /dev/null +++ b/uni_modules/uni-link/readme.md @@ -0,0 +1,11 @@ + + +## Link 链接 +> **组件名:uni-link** +> 代码块: `uLink` + + +uni-link是一个外部网页超链接组件,在小程序内复制url,在app内打开外部浏览器,在h5端打开新网页。 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-link) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 \ No newline at end of file diff --git a/uni_modules/uni-list/changelog.md b/uni_modules/uni-list/changelog.md new file mode 100644 index 0000000..8254a18 --- /dev/null +++ b/uni_modules/uni-list/changelog.md @@ -0,0 +1,46 @@ +## 1.2.14(2023-04-14) +- 优化 uni-list-chat 具名插槽`header` 非app端套一层元素,方便使用时通过外层元素定位实现样式修改 +## 1.2.13(2023-03-03) +- uni-list-chat 新增 支持具名插槽`header` +## 1.2.12(2023-02-01) +- 新增 列表图标新增 customPrefix 属性 ,用法 [详见](https://uniapp.dcloud.net.cn/component/uniui/uni-icons.html#icons-props) +## 1.2.11(2023-01-31) +- 修复 无反馈效果呈现的bug +## 1.2.9(2022-11-22) +- 修复 uni-list-chat 在vue3下跳转报错的bug +## 1.2.8(2022-11-21) +- 修复 uni-list-chat avatar属性 值为本地路径时错误的问题 +## 1.2.7(2022-11-21) +- 修复 uni-list-chat avatar属性 在腾讯云版uniCloud下错误的问题 +## 1.2.6(2022-11-18) +- 修复 uni-list-chat note属性 支持:“草稿”字样功能 文本少1位的问题 +## 1.2.5(2022-11-15) +- 修复 uni-list-item 的 customStyle 属性 padding值在 H5端 无效的bug +## 1.2.4(2022-11-15) +- 修复 uni-list-item 的 customStyle 属性 padding值在nvue(vue2)下无效的bug +## 1.2.3(2022-11-14) +- uni-list-chat 新增 avatar 支持 fileId +## 1.2.2(2022-11-11) +- uni-list 新增属性 render-reverse 详情参考:[https://uniapp.dcloud.net.cn/component/list.html](https://uniapp.dcloud.net.cn/component/list.html) +- uni-list-chat note属性 支持:“草稿”字样 加红显示 详情参考uni-im:[https://ext.dcloud.net.cn/plugin?name=uni-im](https://ext.dcloud.net.cn/plugin?name=uni-im) +- uni-list-item 新增属性 customStyle 支持设置padding、backgroundColor +## 1.2.1(2022-03-30) +- 删除无用文件 +## 1.2.0(2021-11-23) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-list](https://uniapp.dcloud.io/component/uniui/uni-list) +## 1.1.3(2021-08-30) +- 修复 在vue3中to属性在发行应用的时候报错的bug +## 1.1.2(2021-07-30) +- 优化 vue3下事件警告的问题 +## 1.1.1(2021-07-21) +- 修复 与其他组件嵌套使用时,点击失效的Bug +## 1.1.0(2021-07-13) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.0.17(2021-05-12) +- 新增 组件示例地址 +## 1.0.16(2021-02-05) +- 优化 组件引用关系,通过uni_modules引用组件 +## 1.0.15(2021-02-05) +- 调整为uni_modules目录规范 +- 修复 uni-list-chat 角标显示不正常的问题 diff --git a/uni_modules/uni-list/components/uni-list-ad/uni-list-ad.vue b/uni_modules/uni-list/components/uni-list-ad/uni-list-ad.vue new file mode 100644 index 0000000..b9349c2 --- /dev/null +++ b/uni_modules/uni-list/components/uni-list-ad/uni-list-ad.vue @@ -0,0 +1,107 @@ + + + + + diff --git a/uni_modules/uni-list/components/uni-list-chat/uni-list-chat.scss b/uni_modules/uni-list/components/uni-list-chat/uni-list-chat.scss new file mode 100644 index 0000000..311f8d9 --- /dev/null +++ b/uni_modules/uni-list/components/uni-list-chat/uni-list-chat.scss @@ -0,0 +1,58 @@ +/** + * 这里是 uni-list 组件内置的常用样式变量 + * 如果需要覆盖样式,这里提供了基本的组件样式变量,您可以尝试修改这里的变量,去完成样式替换,而不用去修改源码 + * + */ + +// 背景色 +$background-color : #fff; +// 分割线颜色 +$divide-line-color : #e5e5e5; + +// 默认头像大小,如需要修改此值,注意同步修改 js 中的值 const avatarWidth = xx ,目前只支持方形头像 +// nvue 页面不支持修改头像大小 +$avatar-width : 45px ; + +// 头像边框 +$avatar-border-radius: 5px; +$avatar-border-color: #eee; +$avatar-border-width: 1px; + +// 标题文字样式 +$title-size : 16px; +$title-color : #3b4144; +$title-weight : normal; + +// 描述文字样式 +$note-size : 12px; +$note-color : #999; +$note-weight : normal; + +// 右侧额外内容默认样式 +$right-text-size : 12px; +$right-text-color : #999; +$right-text-weight : normal; + +// 角标样式 +// nvue 页面不支持修改圆点位置以及大小 +// 角标在左侧时,角标的位置,默认为 0 ,负数左/下移动,正数右/上移动 +$badge-left: 0px; +$badge-top: 0px; + +// 显示圆点时,圆点大小 +$dot-width: 10px; +$dot-height: 10px; + +// 显示角标时,角标大小和字体大小 +$badge-size : 18px; +$badge-font : 12px; +// 显示角标时,角标前景色 +$badge-color : #fff; +// 显示角标时,角标背景色 +$badge-background-color : #ff5a5f; +// 显示角标时,角标左右间距 +$badge-space : 6px; + +// 状态样式 +// 选中颜色 +$hover : #f5f5f5; diff --git a/uni_modules/uni-list/components/uni-list-chat/uni-list-chat.vue b/uni_modules/uni-list/components/uni-list-chat/uni-list-chat.vue new file mode 100644 index 0000000..d49fd7c --- /dev/null +++ b/uni_modules/uni-list/components/uni-list-chat/uni-list-chat.vue @@ -0,0 +1,593 @@ + + + + + diff --git a/uni_modules/uni-list/components/uni-list-item/uni-list-item.vue b/uni_modules/uni-list/components/uni-list-item/uni-list-item.vue new file mode 100644 index 0000000..a274ac8 --- /dev/null +++ b/uni_modules/uni-list/components/uni-list-item/uni-list-item.vue @@ -0,0 +1,534 @@ + + + + + \ No newline at end of file diff --git a/uni_modules/uni-list/components/uni-list/uni-list - 副本.vue b/uni_modules/uni-list/components/uni-list/uni-list - 副本.vue new file mode 100644 index 0000000..1c85003 --- /dev/null +++ b/uni_modules/uni-list/components/uni-list/uni-list - 副本.vue @@ -0,0 +1,106 @@ + + + + diff --git a/uni_modules/uni-list/components/uni-list/uni-list.vue b/uni_modules/uni-list/components/uni-list/uni-list.vue new file mode 100644 index 0000000..6ef5972 --- /dev/null +++ b/uni_modules/uni-list/components/uni-list/uni-list.vue @@ -0,0 +1,123 @@ + + + + diff --git a/uni_modules/uni-list/components/uni-list/uni-refresh.vue b/uni_modules/uni-list/components/uni-list/uni-refresh.vue new file mode 100644 index 0000000..3b4c5a2 --- /dev/null +++ b/uni_modules/uni-list/components/uni-list/uni-refresh.vue @@ -0,0 +1,65 @@ + + + + + diff --git a/uni_modules/uni-list/components/uni-list/uni-refresh.wxs b/uni_modules/uni-list/components/uni-list/uni-refresh.wxs new file mode 100644 index 0000000..818a6b7 --- /dev/null +++ b/uni_modules/uni-list/components/uni-list/uni-refresh.wxs @@ -0,0 +1,87 @@ +var pullDown = { + threshold: 95, + maxHeight: 200, + callRefresh: 'onrefresh', + callPullingDown: 'onpullingdown', + refreshSelector: '.uni-refresh' +}; + +function ready(newValue, oldValue, ownerInstance, instance) { + var state = instance.getState() + state.canPullDown = newValue; + // console.log(newValue); +} + +function touchStart(e, instance) { + var state = instance.getState(); + state.refreshInstance = instance.selectComponent(pullDown.refreshSelector); + state.canPullDown = (state.refreshInstance != null && state.refreshInstance != undefined); + if (!state.canPullDown) { + return + } + + // console.log("touchStart"); + + state.height = 0; + state.touchStartY = e.touches[0].pageY || e.changedTouches[0].pageY; + state.refreshInstance.setStyle({ + 'height': 0 + }); + state.refreshInstance.callMethod("onchange", true); +} + +function touchMove(e, ownerInstance) { + var instance = e.instance; + var state = instance.getState(); + if (!state.canPullDown) { + return + } + + var oldHeight = state.height; + var endY = e.touches[0].pageY || e.changedTouches[0].pageY; + var height = endY - state.touchStartY; + if (height > pullDown.maxHeight) { + return; + } + + var refreshInstance = state.refreshInstance; + refreshInstance.setStyle({ + 'height': height + 'px' + }); + + height = height < pullDown.maxHeight ? height : pullDown.maxHeight; + state.height = height; + refreshInstance.callMethod(pullDown.callPullingDown, { + height: height + }); +} + +function touchEnd(e, ownerInstance) { + var state = e.instance.getState(); + if (!state.canPullDown) { + return + } + + state.refreshInstance.callMethod("onchange", false); + + var refreshInstance = state.refreshInstance; + if (state.height > pullDown.threshold) { + refreshInstance.callMethod(pullDown.callRefresh); + return; + } + + refreshInstance.setStyle({ + 'height': 0 + }); +} + +function propObserver(newValue, oldValue, instance) { + pullDown = newValue; +} + +module.exports = { + touchmove: touchMove, + touchstart: touchStart, + touchend: touchEnd, + propObserver: propObserver +} diff --git a/uni_modules/uni-list/package.json b/uni_modules/uni-list/package.json new file mode 100644 index 0000000..8350efc --- /dev/null +++ b/uni_modules/uni-list/package.json @@ -0,0 +1,88 @@ +{ + "id": "uni-list", + "displayName": "uni-list 列表", + "version": "1.2.14", + "description": "List 组件 ,帮助使用者快速构建列表。", + "keywords": [ + "", + "uni-ui", + "uniui", + "列表", + "", + "list" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": [ + "uni-badge", + "uni-icons" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-list/readme.md b/uni_modules/uni-list/readme.md new file mode 100644 index 0000000..32c2865 --- /dev/null +++ b/uni_modules/uni-list/readme.md @@ -0,0 +1,346 @@ +## List 列表 +> **组件名:uni-list** +> 代码块: `uList`、`uListItem` +> 关联组件:`uni-list-item`、`uni-badge`、`uni-icons`、`uni-list-chat`、`uni-list-ad` + + +List 列表组件,包含基本列表样式、可扩展插槽机制、长列表性能优化、多端兼容。 + +在vue页面里,它默认使用页面级滚动。在app-nvue页面里,它默认使用原生list组件滚动。这样的长列表,在滚动出屏幕外后,系统会回收不可见区域的渲染内存资源,不会造成滚动越长手机越卡的问题。 + +uni-list组件是父容器,里面的核心是uni-list-item子组件,它代表列表中的一个可重复行,子组件可以无限循环。 + +uni-list-item有很多风格,uni-list-item组件通过内置的属性,满足一些常用的场景。当内置属性不满足需求时,可以通过扩展插槽来自定义列表内容。 + +内置属性可以覆盖的场景包括:导航列表、设置列表、小图标列表、通信录列表、聊天记录列表。 + +涉及很多大图或丰富内容的列表,比如类今日头条的新闻列表、类淘宝的电商列表,需要通过扩展插槽实现。 + +下文均有样例给出。 + +uni-list不包含下拉刷新和上拉翻页。上拉翻页另见组件:[uni-load-more](https://ext.dcloud.net.cn/plugin?id=29) + + +### 安装方式 + +本组件符合[easycom](https://uniapp.dcloud.io/collocation/pages?id=easycom)规范,`HBuilderX 2.5.5`起,只需将本组件导入项目,在页面`template`中即可直接使用,无需在页面中`import`和注册`components`。 + +如需通过`npm`方式使用`uni-ui`组件,另见文档:[https://ext.dcloud.net.cn/plugin?id=55](https://ext.dcloud.net.cn/plugin?id=55) + +> **注意事项** +> 为了避免错误使用,给大家带来不好的开发体验,请在使用组件前仔细阅读下面的注意事项,可以帮你避免一些错误。 +> - 组件需要依赖 `sass` 插件 ,请自行手动安装 +> - 组件内部依赖 `'uni-icons'` 、`uni-badge` 组件 +> - `uni-list` 和 `uni-list-item` 需要配套使用,暂不支持单独使用 `uni-list-item` +> - 只有开启点击反馈后,会有点击选中效果 +> - 使用插槽时,可以完全自定义内容 +> - note 、rightText 属性暂时没做限制,不支持文字溢出隐藏,使用时应该控制长度显示或通过默认插槽自行扩展 +> - 支付宝小程序平台需要在支付宝小程序开发者工具里开启 component2 编译模式,开启方式: 详情 --> 项目配置 --> 启用 component2 编译 +> - 如果需要修改 `switch`、`badge` 样式,请使用插槽自定义 +> - 在 `HBuilderX` 低版本中,可能会出现组件显示 `undefined` 的问题,请升级最新的 `HBuilderX` 或者 `cli` +> - 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 + + +### 基本用法 + +- 设置 `title` 属性,可以显示列表标题 +- 设置 `disabled` 属性,可以禁用当前项 + +```html + + + + + +``` + +### 多行内容显示 + +- 设置 `note` 属性 ,可以在第二行显示描述文本信息 + +```html + + + + + +``` + +### 右侧显示角标、switch + +- 设置 `show-badge` 属性 ,可以显示角标内容 +- 设置 `show-switch` 属性,可以显示 switch 开关 + +```html + + + + + +``` + +### 左侧显示略缩图、图标 + +- 设置 `thumb` 属性 ,可以在列表左侧显示略缩图 +- 设置 `show-extra-icon` 属性,并指定 `extra-icon` 可以在左侧显示图标 + +```html + + + + +``` + +### 开启点击反馈和右侧箭头 +- 设置 `clickable` 为 `true` ,则表示这是一个可点击的列表,会默认给一个点击效果,并可以监听 `click` 事件 +- 设置 `link` 属性,会自动开启点击反馈,并给列表右侧添加一个箭头 +- 设置 `to` 属性,可以跳转页面,`link` 的值表示跳转方式,如果不指定,默认为 `navigateTo` + +```html + + + + + + + +``` + + +### 聊天列表示例 +- 设置 `clickable` 为 `true` ,则表示这是一个可点击的列表,会默认给一个点击效果,并可以监听 `click` 事件 +- 设置 `link` 属性,会自动开启点击反馈,`link` 的值表示跳转方式,如果不指定,默认为 `navigateTo` +- 设置 `to` 属性,可以跳转页面 +- `time` 属性,通常会设置成时间显示,但是这个属性不仅仅可以设置时间,你可以传入任何文本,注意文本长度可能会影响显示 +- `avatar` 和 `avatarList` 属性同时只会有一个生效,同时设置的话,`avatarList` 属性的长度大于1 ,`avatar` 属性将失效 +- 可以通过默认插槽自定义列表右侧内容 + +```html + + + + + + + + + + + + + + + + + 刚刚 + + + + + + + +``` + +```javascript + +export default { + components: {}, + data() { + return { + avatarList: [{ + url: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/460d46d0-4fcc-11eb-8ff1-d5dcf8779628.png' + }, { + url: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/460d46d0-4fcc-11eb-8ff1-d5dcf8779628.png' + }, { + url: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/460d46d0-4fcc-11eb-8ff1-d5dcf8779628.png' + }] + } + } +} + +``` + + +```css + +.chat-custom-right { + flex: 1; + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: column; + justify-content: space-between; + align-items: flex-end; +} + +.chat-custom-text { + font-size: 12px; + color: #999; +} + +``` + +## API + +### List Props + +属性名 |类型 |默认值 | 说明 +:-: |:-: |:-: | :-: +border |Boolean |true | 是否显示边框 + + +### ListItem Props + +属性名 |类型 |默认值 | 说明 +:-: |:-: |:-: | :-: +title |String |- | 标题 +note |String |- | 描述 +ellipsis |Number |0 | title 是否溢出隐藏,可选值,0:默认; 1:显示一行; 2:显示两行;【nvue 暂不支持】 +thumb |String |- | 左侧缩略图,若thumb有值,则不会显示扩展图标 +thumbSize |String |medium | 略缩图尺寸,可选值,lg:大图; medium:一般; sm:小图; +showBadge |Boolean |false | 是否显示数字角标 +badgeText |String |- | 数字角标内容 +badgeType |String |- | 数字角标类型,参考[uni-icons](https://ext.dcloud.net.cn/plugin?id=21) +badgeStyle |Object |- | 数字角标样式,使用uni-badge的custom-style参数 +rightText |String |- | 右侧文字内容 +disabled |Boolean |false | 是否禁用 +showArrow |Boolean |true | 是否显示箭头图标 +link |String |navigateTo | 新页面跳转方式,可选值见下表 +to |String |- | 新页面跳转地址,如填写此属性,click 会返回页面是否跳转成功 +clickable |Boolean |false | 是否开启点击反馈 +showSwitch |Boolean |false | 是否显示Switch +switchChecked |Boolean |false | Switch是否被选中 +showExtraIcon |Boolean |false | 左侧是否显示扩展图标 +extraIcon |Object |- | 扩展图标参数,格式为 ``{color: '#4cd964',size: '22',type: 'spinner'}``,参考 [uni-icons](https://ext.dcloud.net.cn/plugin?id=28) +direction | String |row | 排版方向,可选值,row:水平排列; column:垂直排列; 3个插槽是水平排还是垂直排,也受此属性控制 + + +#### Link Options + +属性名 | 说明 +:-: | :-: +navigateTo | 同 uni.navigateTo() +redirectTo | 同 uni.reLaunch() +reLaunch | 同 uni.reLaunch() +switchTab | 同 uni.switchTab() + +### ListItem Events + +事件称名 |说明 |返回参数 +:-: |:-: |:-: +click |点击 uniListItem 触发事件,需开启点击反馈 |- +switchChange |点击切换 Switch 时触发,需显示 switch |e={value:checked} + + + +### ListItem Slots + +名称 | 说明 +:-: | :-: +header | 左/上内容插槽,可完全自定义默认显示 +body | 中间内容插槽,可完全自定义中间内容 +footer | 右/下内容插槽,可完全自定义右侧内容 + + +> **通过插槽扩展** +> 需要注意的是当使用插槽时,内置样式将会失效,只保留排版样式,此时的样式需要开发者自己实现 +> 如果 `uni-list-item` 组件内置属性样式无法满足需求,可以使用插槽来自定义uni-list-item里的内容。 +> uni-list-item提供了3个可扩展的插槽:`header`、`body`、`footer` +> - 当 `direction` 属性为 `row` 时表示水平排列,此时 `header` 表示列表的左边部分,`body` 表示列表的中间部分,`footer` 表示列表的右边部分 +> - 当 `direction` 属性为 `column` 时表示垂直排列,此时 `header` 表示列表的上边部分,`body` 表示列表的中间部分,`footer` 表示列表的下边部分 +> 开发者可以只用1个插槽,也可以3个一起使用。在插槽中可自主编写view标签,实现自己所需的效果。 + + +**示例** + +```html + + + + + + + + + 自定义插槽 + + + + +``` + + + + + +### ListItemChat Props + +属性名 |类型 |默认值 | 说明 +:-: |:-: |:-: | :-: +title |String |- | 标题 +note |String |- | 描述 +clickable |Boolean |false | 是否开启点击反馈 +badgeText |String |- | 数字角标内容,设置为 `dot` 将显示圆点 +badgePositon |String |right | 角标位置 +link |String |navigateTo | 是否展示右侧箭头并开启点击反馈,可选值见下表 +clickable |Boolean |false | 是否开启点击反馈 +to |String |- | 跳转页面地址,如填写此属性,click 会返回页面是否跳转成功 +time |String |- | 右侧时间显示 +avatarCircle |Boolean |false | 是否显示圆形头像 +avatar |String |- | 头像地址,avatarCircle 不填时生效 +avatarList |Array |- | 头像组,格式为 [{url:''}] + +#### Link Options + +属性名 | 说明 +:-: | :-: +navigateTo | 同 uni.navigateTo() +redirectTo | 同 uni.reLaunch() +reLaunch | 同 uni.reLaunch() +switchTab | 同 uni.switchTab() + +### ListItemChat Slots + +名称 | 说明 +:- | :- +default | 自定义列表右侧内容(包括时间和角标显示) + +### ListItemChat Events +事件称名 | 说明 | 返回参数 +:-: | :-: | :-: +@click | 点击 uniListChat 触发事件 | {data:{}} ,如有 to 属性,会返回页面跳转信息 + + + + + + +## 基于uni-list扩展的页面模板 + +通过扩展插槽,可实现多种常见样式的列表 + +**新闻列表类** + +1. 云端一体混合布局:[https://ext.dcloud.net.cn/plugin?id=2546](https://ext.dcloud.net.cn/plugin?id=2546) +2. 云端一体垂直布局,大图模式:[https://ext.dcloud.net.cn/plugin?id=2583](https://ext.dcloud.net.cn/plugin?id=2583) +3. 云端一体垂直布局,多行图文混排:[https://ext.dcloud.net.cn/plugin?id=2584](https://ext.dcloud.net.cn/plugin?id=2584) +4. 云端一体垂直布局,多图模式:[https://ext.dcloud.net.cn/plugin?id=2585](https://ext.dcloud.net.cn/plugin?id=2585) +5. 云端一体水平布局,左图右文:[https://ext.dcloud.net.cn/plugin?id=2586](https://ext.dcloud.net.cn/plugin?id=2586) +6. 云端一体水平布局,左文右图:[https://ext.dcloud.net.cn/plugin?id=2587](https://ext.dcloud.net.cn/plugin?id=2587) +7. 云端一体垂直布局,无图模式,主标题+副标题:[https://ext.dcloud.net.cn/plugin?id=2588](https://ext.dcloud.net.cn/plugin?id=2588) + +**商品列表类** + +1. 云端一体列表/宫格视图互切:[https://ext.dcloud.net.cn/plugin?id=2651](https://ext.dcloud.net.cn/plugin?id=2651) +2. 云端一体列表(宫格模式):[https://ext.dcloud.net.cn/plugin?id=2671](https://ext.dcloud.net.cn/plugin?id=2671) +3. 云端一体列表(列表模式):[https://ext.dcloud.net.cn/plugin?id=2672](https://ext.dcloud.net.cn/plugin?id=2672) + +## 组件示例 + +点击查看:[https://hellouniapp.dcloud.net.cn/pages/extUI/list/list](https://hellouniapp.dcloud.net.cn/pages/extUI/list/list) \ No newline at end of file diff --git a/uni_modules/uni-load-more/changelog.md b/uni_modules/uni-load-more/changelog.md new file mode 100644 index 0000000..8f03f1d --- /dev/null +++ b/uni_modules/uni-load-more/changelog.md @@ -0,0 +1,19 @@ +## 1.3.3(2022-01-20) +- 新增 showText属性 ,是否显示文本 +## 1.3.2(2022-01-19) +- 修复 nvue 平台下不显示文本的bug +## 1.3.1(2022-01-19) +- 修复 微信小程序平台样式选择器报警告的问题 +## 1.3.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-load-more](https://uniapp.dcloud.io/component/uniui/uni-load-more) +## 1.2.1(2021-08-24) +- 新增 支持国际化 +## 1.2.0(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.1.8(2021-05-12) +- 新增 组件示例地址 +## 1.1.7(2021-03-30) +- 修复 uni-load-more 在首页使用时,h5 平台报 'uni is not defined' 的 bug +## 1.1.6(2021-02-05) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-load-more/components/uni-load-more/i18n/en.json b/uni_modules/uni-load-more/components/uni-load-more/i18n/en.json new file mode 100644 index 0000000..a4f14a5 --- /dev/null +++ b/uni_modules/uni-load-more/components/uni-load-more/i18n/en.json @@ -0,0 +1,5 @@ +{ + "uni-load-more.contentdown": "Pull up to show more", + "uni-load-more.contentrefresh": "loading...", + "uni-load-more.contentnomore": "No more data" +} diff --git a/uni_modules/uni-load-more/components/uni-load-more/i18n/index.js b/uni_modules/uni-load-more/components/uni-load-more/i18n/index.js new file mode 100644 index 0000000..de7509c --- /dev/null +++ b/uni_modules/uni-load-more/components/uni-load-more/i18n/index.js @@ -0,0 +1,8 @@ +import en from './en.json' +import zhHans from './zh-Hans.json' +import zhHant from './zh-Hant.json' +export default { + en, + 'zh-Hans': zhHans, + 'zh-Hant': zhHant +} diff --git a/uni_modules/uni-load-more/components/uni-load-more/i18n/zh-Hans.json b/uni_modules/uni-load-more/components/uni-load-more/i18n/zh-Hans.json new file mode 100644 index 0000000..f15d510 --- /dev/null +++ b/uni_modules/uni-load-more/components/uni-load-more/i18n/zh-Hans.json @@ -0,0 +1,5 @@ +{ + "uni-load-more.contentdown": "上拉显示更多", + "uni-load-more.contentrefresh": "正在加载...", + "uni-load-more.contentnomore": "没有更多数据了" +} diff --git a/uni_modules/uni-load-more/components/uni-load-more/i18n/zh-Hant.json b/uni_modules/uni-load-more/components/uni-load-more/i18n/zh-Hant.json new file mode 100644 index 0000000..a255c6d --- /dev/null +++ b/uni_modules/uni-load-more/components/uni-load-more/i18n/zh-Hant.json @@ -0,0 +1,5 @@ +{ + "uni-load-more.contentdown": "上拉顯示更多", + "uni-load-more.contentrefresh": "正在加載...", + "uni-load-more.contentnomore": "沒有更多數據了" +} diff --git a/uni_modules/uni-load-more/components/uni-load-more/uni-load-more.vue b/uni_modules/uni-load-more/components/uni-load-more/uni-load-more.vue new file mode 100644 index 0000000..e5eff4d --- /dev/null +++ b/uni_modules/uni-load-more/components/uni-load-more/uni-load-more.vue @@ -0,0 +1,399 @@ + + + + + diff --git a/uni_modules/uni-load-more/package.json b/uni_modules/uni-load-more/package.json new file mode 100644 index 0000000..2fa6f04 --- /dev/null +++ b/uni_modules/uni-load-more/package.json @@ -0,0 +1,86 @@ +{ + "id": "uni-load-more", + "displayName": "uni-load-more 加载更多", + "version": "1.3.3", + "description": "LoadMore 组件,常用在列表里面,做滚动加载使用。", + "keywords": [ + "uni-ui", + "uniui", + "加载更多", + "load-more" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": ["uni-scss"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-load-more/readme.md b/uni_modules/uni-load-more/readme.md new file mode 100644 index 0000000..54dc1fa --- /dev/null +++ b/uni_modules/uni-load-more/readme.md @@ -0,0 +1,14 @@ + + +### LoadMore 加载更多 +> **组件名:uni-load-more** +> 代码块: `uLoadMore` + + +用于列表中,做滚动加载使用,展示 loading 的各种状态。 + + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-load-more) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 + + diff --git a/uni_modules/uni-nav-bar/changelog.md b/uni_modules/uni-nav-bar/changelog.md new file mode 100644 index 0000000..0f9a2f1 --- /dev/null +++ b/uni_modules/uni-nav-bar/changelog.md @@ -0,0 +1,51 @@ +## 1.3.11(2023-03-29) +- 修复 自定义状态栏高度闪动BUG +## 1.3.10(2023-03-29) +- 修复 暗黑模式下边线颜色错误的bug +## 1.3.9(2022-10-13) +- 修复 条件编译错误的bug +## 1.3.8(2022-10-12) +- 修复 nvue 环境 fixed 为 true 的情况下,无法置顶的 bug +## 1.3.7(2022-08-11) +- 修复 nvue 环境下 fixed 为 true 的情况下,无法置顶的 bug +## 1.3.6(2022-06-30) +- 修复 组件示例中插槽用法无法显示内容的bug +## 1.3.5(2022-05-24) +- 新增 stat 属性 ,可开启统计title 上报 ,仅使用了title 属性且项目开启了uni统计生效 +## 1.3.4(2022-01-24) +- 更新 组件示例 +## 1.3.3(2022-01-24) +- 新增 left-width/right-width属性 ,可修改左右两侧的宽度 +## 1.3.2(2022-01-18) +- 修复 在vue下,标题不垂直居中的bug +## 1.3.1(2022-01-18) +- 修复 height 属性类型错误 +## 1.3.0(2022-01-18) +- 新增 height 属性,可修改组件高度 +- 新增 dark 属性可可开启暗黑模式 +- 优化 标题字数过多显示省略号 +- 优化 插槽,插入内容可完全覆盖 +## 1.2.1(2022-01-10) +- 修复 color 属性不生效的bug +## 1.2.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-nav-bar](https://uniapp.dcloud.io/component/uniui/uni-nav-bar) +## 1.1.0(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.0.11(2021-05-12) +- 新增 组件示例地址 +## 1.0.10(2021-04-30) +- 修复 在nvue下fixed为true,宽度不能撑满的Bug +## 1.0.9(2021-04-21) +- 优化 添加依赖 uni-icons, 导入后自动下载依赖 +## 1.0.8(2021-04-14) +- uni-ui 修复 uni-nav-bar 当 fixed 属性为 true 时铺不满屏幕的 bug + +## 1.0.7(2021-02-25) +- 修复 easycom 下,找不到 uni-status-bar 的bug + +## 1.0.6(2021-02-05) +- 优化 组件引用关系,通过uni_modules引用组件 + +## 1.0.5(2021-02-05) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-nav-bar/components/uni-nav-bar/uni-nav-bar.vue b/uni_modules/uni-nav-bar/components/uni-nav-bar/uni-nav-bar.vue new file mode 100644 index 0000000..c890860 --- /dev/null +++ b/uni_modules/uni-nav-bar/components/uni-nav-bar/uni-nav-bar.vue @@ -0,0 +1,357 @@ + + + + + diff --git a/uni_modules/uni-nav-bar/components/uni-nav-bar/uni-status-bar.vue b/uni_modules/uni-nav-bar/components/uni-nav-bar/uni-status-bar.vue new file mode 100644 index 0000000..4ac73ae --- /dev/null +++ b/uni_modules/uni-nav-bar/components/uni-nav-bar/uni-status-bar.vue @@ -0,0 +1,24 @@ + + + + + diff --git a/uni_modules/uni-nav-bar/package.json b/uni_modules/uni-nav-bar/package.json new file mode 100644 index 0000000..240ae95 --- /dev/null +++ b/uni_modules/uni-nav-bar/package.json @@ -0,0 +1,86 @@ +{ + "id": "uni-nav-bar", + "displayName": "uni-nav-bar 自定义导航栏", + "version": "1.3.11", + "description": "自定义导航栏组件,主要用于头部导航。", + "keywords": [ + "uni-ui", + "导航", + "导航栏", + "自定义导航栏" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": [ + "uni-scss", + "uni-icons" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-nav-bar/readme.md b/uni_modules/uni-nav-bar/readme.md new file mode 100644 index 0000000..3934b32 --- /dev/null +++ b/uni_modules/uni-nav-bar/readme.md @@ -0,0 +1,15 @@ + + +## NavBar 导航栏 +> **组件名:uni-nav-bar** +> 代码块: `uNavBar` + +导航栏组件,主要用于头部导航。 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-nav-bar) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 + + + + + diff --git a/uni_modules/uni-notice-bar/changelog.md b/uni_modules/uni-notice-bar/changelog.md new file mode 100644 index 0000000..ce50674 --- /dev/null +++ b/uni_modules/uni-notice-bar/changelog.md @@ -0,0 +1,20 @@ +## 1.2.2(2023-12-20) +- 修复动态绑定title时,滚动速度不一致的问题 +## 1.2.1(2022-09-05) +- 新增 属性 fontSize,可修改文字大小。 +## 1.2.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-notice-bar](https://uniapp.dcloud.io/component/uniui/uni-notice-bar) +## 1.1.1(2021-11-09) +- 新增 提供组件设计资源,组件样式调整 +## 1.1.0(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.0.9(2021-05-12) +- 新增 组件示例地址 +## 1.0.8(2021-04-21) +- 优化 添加依赖 uni-icons, 导入后自动下载依赖 +## 1.0.7(2021-02-05) +- 优化 组件引用关系,通过uni_modules引用组件 + +## 1.0.6(2021-02-05) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-notice-bar/components/uni-notice-bar/uni-notice-bar.vue b/uni_modules/uni-notice-bar/components/uni-notice-bar/uni-notice-bar.vue new file mode 100644 index 0000000..47fb9b3 --- /dev/null +++ b/uni_modules/uni-notice-bar/components/uni-notice-bar/uni-notice-bar.vue @@ -0,0 +1,431 @@ + + + + + diff --git a/uni_modules/uni-notice-bar/package.json b/uni_modules/uni-notice-bar/package.json new file mode 100644 index 0000000..1e9762c --- /dev/null +++ b/uni_modules/uni-notice-bar/package.json @@ -0,0 +1,87 @@ +{ + "id": "uni-notice-bar", + "displayName": "uni-notice-bar 通告栏", + "version": "1.2.2", + "description": "NoticeBar 通告栏组件,常用于展示公告信息,可设为滚动公告", + "keywords": [ + "uni-ui", + "uniui", + "通告栏", + "公告", + "跑马灯" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": [ + "uni-scss", + "uni-icons" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} diff --git a/uni_modules/uni-notice-bar/readme.md b/uni_modules/uni-notice-bar/readme.md new file mode 100644 index 0000000..fb2ede2 --- /dev/null +++ b/uni_modules/uni-notice-bar/readme.md @@ -0,0 +1,13 @@ + + +## NoticeBar 通告栏 +> **组件名:uni-notice-bar** +> 代码块: `uNoticeBar` + + +通告栏组件 。 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-notice-bar) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 + + diff --git a/uni_modules/uni-number-box/changelog.md b/uni_modules/uni-number-box/changelog.md new file mode 100644 index 0000000..adf9221 --- /dev/null +++ b/uni_modules/uni-number-box/changelog.md @@ -0,0 +1,39 @@ +## 1.2.8(2024-04-26) +- 修复 在vue2下H5黑边的bug +## 1.2.7(2024-04-26) +- 修复 在vue2手动输入后失焦导致清空数值的严重bug +## 1.2.6(2024-02-22) +- 新增 设置宽度属性width(单位:px) +## 1.2.5(2024-02-21) +- 修复 step步长小于1时,键盘类型为number的bug +## 1.2.4(2024-02-02) +- 修复 加减号垂直位置偏移样式问题 +## 1.2.3(2023-05-23) +- 更新示例工程 +## 1.2.2(2023-05-08) +- 修复 change 事件执行顺序错误的问题 +## 1.2.1(2021-11-22) +- 修复 vue3中某些scss变量无法找到的问题 +## 1.2.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-number-box](https://uniapp.dcloud.io/component/uniui/uni-number-box) +## 1.1.2(2021-11-09) +- 新增 提供组件设计资源,组件样式调整 +## 1.1.1(2021-07-30) +- 优化 vue3下事件警告的问题 +## 1.1.0(2021-07-13) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.0.7(2021-05-12) +- 新增 组件示例地址 +## 1.0.6(2021-04-20) +- 修复 uni-number-box 浮点数运算不精确的 bug +- 修复 uni-number-box change 事件触发不正确的 bug +- 新增 uni-number-box v-model 双向绑定 +## 1.0.5(2021-02-05) +- 调整为uni_modules目录规范 + +## 1.0.7(2021-02-05) +- 调整为uni_modules目录规范 +- 新增 支持 v-model +- 新增 支持 focus、blur 事件 +- 新增 支持 PC 端 diff --git a/uni_modules/uni-number-box/components/uni-number-box/uni-number-box.vue b/uni_modules/uni-number-box/components/uni-number-box/uni-number-box.vue new file mode 100644 index 0000000..4e203cc --- /dev/null +++ b/uni_modules/uni-number-box/components/uni-number-box/uni-number-box.vue @@ -0,0 +1,232 @@ + + + diff --git a/uni_modules/uni-number-box/package.json b/uni_modules/uni-number-box/package.json new file mode 100644 index 0000000..4ac9047 --- /dev/null +++ b/uni_modules/uni-number-box/package.json @@ -0,0 +1,83 @@ +{ + "id": "uni-number-box", + "displayName": "uni-number-box 数字输入框", + "version": "1.2.8", + "description": "NumberBox 带加减按钮的数字输入框组件,用户可以控制每次点击增加的数值,支持小数。", + "keywords": [ + "uni-ui", + "uniui", + "数字输入框" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": ["uni-scss"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "n" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-number-box/readme.md b/uni_modules/uni-number-box/readme.md new file mode 100644 index 0000000..affc56f --- /dev/null +++ b/uni_modules/uni-number-box/readme.md @@ -0,0 +1,13 @@ + + +## NumberBox 数字输入框 +> **组件名:uni-number-box** +> 代码块: `uNumberBox` + + +带加减按钮的数字输入框。 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-number-box) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 + + diff --git a/uni_modules/uni-open-bridge-common/changelog.md b/uni_modules/uni-open-bridge-common/changelog.md new file mode 100644 index 0000000..e97c535 --- /dev/null +++ b/uni_modules/uni-open-bridge-common/changelog.md @@ -0,0 +1,25 @@ +## 1.2.0(2023-04-27) +- 优化 微信小程序平台 使用微信新增 API getStableAccessToken 获取 access_token, access_token 有效期内重复调用该接口不会更新 access_token, [详情](https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-access-token/getStableAccessToken.html) +## 1.1.5(2023-03-27) +- 修复 微信小程序平台 某些情况下 encrypt_key 插入错误的问题 +## 1.1.4(2023-03-13) +- 修复 平台 weixin-web +## 1.1.3(2023-03-13) +- 新增 支持旧版本 uni-id 配置 +- 新增 支持平台 weixin-app|qq-mp|qq-app +## 1.1.2(2023-02-28) +- 新增 config 配置错误提示语 +## 1.1.1(2023-02-28) +- 新增 支持 provider 参数,和 platform 保持一致 +## 1.1.0(2023-02-27) +- 重要更新 调整数据库key格式,兼容旧版本API,如果开发者通过手动拼接key查询数据库需要修改现有逻辑 + + 原格式: uni-id:[dcloudAppid]:[platform]:[openid]:[access-token|user-access-token|session-key|encrypt-key-version|ticket] + + 新格式: uni-id:[provider]:[appid]:[openid]:[access-token|user-access-token|session-key|encrypt-key-version|ticket] +## 1.0.4(2022-09-21) +- 新增 支持使用阿里云固定IP获取微信公众号H5凭据 access_token、ticket,开发者需要在微信公众平台配置阿里云固定IP,[固定IP详情](https://uniapp.dcloud.net.cn/uniCloud/cf-functions.html#aliyun-eip) +## 1.0.3(2022-09-06) +- 修复 过期时间问题,容错 AccessToken 默认 fallback 逻辑,当微信服务器没有返回过期时间时设置为2小时后过期 +## 1.0.2(2022-09-02) +- 新增 依赖数据表schema opendb-open-data +## 1.0.0(2022-08-22) +- 首次发布 diff --git a/uni_modules/uni-open-bridge-common/package.json b/uni_modules/uni-open-bridge-common/package.json new file mode 100644 index 0000000..30f2620 --- /dev/null +++ b/uni_modules/uni-open-bridge-common/package.json @@ -0,0 +1,84 @@ +{ + "id": "uni-open-bridge-common", + "displayName": "uni-open-bridge-common", + "version": "1.2.0", + "description": "统一接管微信等三方平台认证凭据", + "keywords": [ + "uni-open-bridge-common", + "access_token", + "session_key", + "ticket" +], + "repository": "", + "engines": { + "HBuilderX": "^3.5.2" + }, + "dcloudext": { + "type": "unicloud-template-function", + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "Vue": { + "vue2": "u", + "vue3": "u" + }, + "App": { + "app-vue": "u", + "app-nvue": "u" + }, + "H5-mobile": { + "Safari": "u", + "Android Browser": "u", + "微信浏览器(Android)": "u", + "QQ浏览器(Android)": "u" + }, + "H5-pc": { + "Chrome": "u", + "IE": "u", + "Edge": "u", + "Firefox": "u", + "Safari": "u" + }, + "小程序": { + "微信": "u", + "阿里": "u", + "百度": "u", + "字节跳动": "u", + "QQ": "u", + "钉钉": "u", + "快手": "u", + "飞书": "u", + "京东": "u" + }, + "快应用": { + "华为": "u", + "联盟": "u" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-open-bridge-common/readme.md b/uni_modules/uni-open-bridge-common/readme.md new file mode 100644 index 0000000..3892384 --- /dev/null +++ b/uni_modules/uni-open-bridge-common/readme.md @@ -0,0 +1,5 @@ +# uni-open-bridge-common + +`uni-open-bridge-common` 是统一接管微信等三方平台认证凭据(包括但不限于`access_token`、`session_key`、`encrypt_key`、`ticket`)的开源库。 + +文档链接 [https://uniapp.dcloud.net.cn/uniCloud/uni-open-bridge#common](https://uniapp.dcloud.net.cn/uniCloud/uni-open-bridge#common) diff --git a/uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/bridge-error.js b/uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/bridge-error.js new file mode 100644 index 0000000..95160a4 --- /dev/null +++ b/uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/bridge-error.js @@ -0,0 +1,26 @@ +'use strict'; + +class BridgeError extends Error { + + constructor(code, message) { + super(message) + + this._code = code + } + + get code() { + return this._code + } + + get errCode() { + return this._code + } + + get errMsg() { + return this.message + } +} + +module.exports = { + BridgeError +} diff --git a/uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/config.js b/uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/config.js new file mode 100644 index 0000000..d083f00 --- /dev/null +++ b/uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/config.js @@ -0,0 +1,124 @@ +'use strict'; + +const { + ProviderType +} = require('./consts.js') + +const configCenter = require('uni-config-center') + +// 多维数据为兼容uni-id以前版本配置 +const OauthConfig = { + 'weixin-app': [ + ['app', 'oauth', 'weixin'], + ['app-plus', 'oauth', 'weixin'] + ], + 'weixin-mp': [ + ['mp-weixin', 'oauth', 'weixin'] + ], + 'weixin-h5': [ + ['web', 'oauth', 'weixin-h5'], + ['h5-weixin', 'oauth', 'weixin'], + ['h5', 'oauth', 'weixin'] + ], + 'weixin-web': [ + ['web', 'oauth', 'weixin-web'] + ], + 'qq-app': [ + ['app', 'oauth', 'qq'], + ['app-plus', 'oauth', 'qq'] + ], + 'qq-mp': [ + ['mp-qq', 'oauth', 'qq'] + ] +} + +const Support_Platforms = [ + ProviderType.WEIXIN_MP, + ProviderType.WEIXIN_H5, + ProviderType.WEIXIN_APP, + ProviderType.WEIXIN_WEB, + ProviderType.QQ_MP, + ProviderType.QQ_APP +] + +class ConfigBase { + + constructor() { + const uniIdConfigCenter = configCenter({ + pluginId: 'uni-id' + }) + + this._uniIdConfig = uniIdConfigCenter.config() + } + + getAppConfig(appid) { + if (Array.isArray(this._uniIdConfig)) { + return this._uniIdConfig.find((item) => { + return (item.dcloudAppid === appid) + }) + } + return this._uniIdConfig + } +} + +class AppConfig extends ConfigBase { + + constructor() { + super() + } + + get(appid, platform) { + if (!this.isSupport(platform)) { + return null + } + + let appConfig = this.getAppConfig(appid) + if (!appConfig) { + return null + } + + return this.getOauthConfig(appConfig, platform) + } + + isSupport(platformName) { + return (Support_Platforms.indexOf(platformName) >= 0) + } + + getOauthConfig(appConfig, platformName) { + let treePath = OauthConfig[platformName] + let node = this.findNode(appConfig, treePath) + if (node && node.appid && node.appsecret) { + return { + appid: node.appid, + secret: node.appsecret + } + } + return null + } + + findNode(treeNode, arrayPath) { + let node = treeNode + for (let treePath of arrayPath) { + for (let name of treePath) { + const currentNode = node[name] + if (currentNode) { + node = currentNode + } else { + node = null + break + } + } + if (node === null) { + node = treeNode + } else { + break + } + } + return node + } +} + + +module.exports = { + AppConfig +}; \ No newline at end of file diff --git a/uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/consts.js b/uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/consts.js new file mode 100644 index 0000000..4c666e9 --- /dev/null +++ b/uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/consts.js @@ -0,0 +1,30 @@ +'use strict'; + +const TAG = "UNI_OPEN_BRIDGE" + +const HTTP_STATUS = { + SUCCESS: 200 +} + +const ProviderType = { + WEIXIN_MP: 'weixin-mp', + WEIXIN_H5: 'weixin-h5', + WEIXIN_APP: 'weixin-app', + WEIXIN_WEB: 'weixin-web', + QQ_MP: 'qq-mp', + QQ_APP: 'qq-app' +} + +// old +const PlatformType = ProviderType + +const ErrorCodeType = { + SYSTEM_ERROR: TAG + "_SYSTEM_ERROR" +} + +module.exports = { + HTTP_STATUS, + ProviderType, + PlatformType, + ErrorCodeType +} diff --git a/uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/index.js b/uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/index.js new file mode 100644 index 0000000..fc23cd9 --- /dev/null +++ b/uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/index.js @@ -0,0 +1,317 @@ +'use strict'; + +const { + PlatformType, + ProviderType, + ErrorCodeType +} = require('./consts.js') + +const { + AppConfig +} = require('./config.js') + +const { + Storage +} = require('./storage.js') + +const { + BridgeError +} = require('./bridge-error.js') + +const { + WeixinServer +} = require('./weixin-server.js') + +const appConfig = new AppConfig() + +class AccessToken extends Storage { + + constructor() { + super('access-token', ['provider', 'appid']) + } + + async update(key) { + super.update(key) + + const result = await this.getByWeixinServer(key) + + return this.set(key, result.value, result.duration) + } + + async fallback(key) { + return this.getByWeixinServer(key) + } + + async getByWeixinServer(key) { + const oauthConfig = appConfig.get(key.dcloudAppid, key.provider) + let methodName + if (key.provider === ProviderType.WEIXIN_MP) { + methodName = 'GetMPAccessTokenData' + } else if (key.provider === ProviderType.WEIXIN_H5) { + methodName = 'GetH5AccessTokenData' + } else { + throw new BridgeError(ErrorCodeType.SYSTEM_ERROR, "provider invalid") + } + + const responseData = await WeixinServer[methodName](oauthConfig) + + const duration = responseData.expires_in || (60 * 60 * 2) + delete responseData.expires_in + + return { + value: responseData, + duration + } + } +} + +class UserAccessToken extends Storage { + + constructor() { + super('user-access-token', ['provider', 'appid', 'openid']) + } +} + +class SessionKey extends Storage { + + constructor() { + super('session-key', ['provider', 'appid', 'openid']) + } +} + +class Encryptkey extends Storage { + + constructor() { + super('encrypt-key', ['provider', 'appid', 'openid']) + } + + async update(key) { + super.update(key) + + const result = await this.getByWeixinServer(key) + + return this.set(key, result.value, result.duration) + } + + getKeyString(key) { + return `${super.getKeyString(key)}-${key.version}` + } + + getExpiresIn(value) { + if (value <= 0) { + return 60 + } + return value + } + + async fallback(key) { + return this.getByWeixinServer(key) + } + + async getByWeixinServer(key) { + const accessToken = await Factory.Get(AccessToken, key) + const userSession = await Factory.Get(SessionKey, key) + + const responseData = await WeixinServer.GetUserEncryptKeyData({ + openid: key.openid, + access_token: accessToken.access_token, + session_key: userSession.session_key + }) + + const keyInfo = responseData.key_info_list.find((item) => { + return item.version === key.version + }) + + if (!keyInfo) { + throw new BridgeError(ErrorCodeType.SYSTEM_ERROR, 'key version invalid') + } + + const value = { + encrypt_key: keyInfo.encrypt_key, + iv: keyInfo.iv + } + + return { + value, + duration: keyInfo.expire_in + } + } +} + +class Ticket extends Storage { + + constructor() { + super('ticket', ['provider', 'appid']) + } + + async update(key) { + super.update(key) + + const result = await this.getByWeixinServer(key) + + return this.set(key, result.value, result.duration) + } + + async fallback(key) { + return this.getByWeixinServer(key) + } + + async getByWeixinServer(key) { + const accessToken = await Factory.Get(AccessToken, { + dcloudAppid: key.dcloudAppid, + provider: ProviderType.WEIXIN_H5 + }) + + const responseData = await WeixinServer.GetH5TicketData(accessToken) + + const duration = responseData.expires_in || (60 * 60 * 2) + delete responseData.expires_in + delete responseData.errcode + delete responseData.errmsg + + return { + value: responseData, + duration + } + } +} + + +const Factory = { + + async Get(T, key, fallback) { + Factory.FixOldKey(key) + return Factory.MakeUnique(T).get(key, fallback) + }, + + async Set(T, key, value, expiresIn) { + Factory.FixOldKey(key) + return Factory.MakeUnique(T).set(key, value, expiresIn) + }, + + async Remove(T, key) { + Factory.FixOldKey(key) + return Factory.MakeUnique(T).remove(key) + }, + + async Update(T, key) { + Factory.FixOldKey(key) + return Factory.MakeUnique(T).update(key) + }, + + FixOldKey(key) { + if (!key.provider) { + key.provider = key.platform + } + + const configData = appConfig.get(key.dcloudAppid, key.provider) + if (!configData) { + throw new BridgeError(ErrorCodeType.SYSTEM_ERROR, 'appid or provider invalid') + } + key.appid = configData.appid + }, + + MakeUnique(T) { + return new T() + } +} + + +// exports + +async function getAccessToken(key, fallback) { + return Factory.Get(AccessToken, key, fallback) +} + +async function setAccessToken(key, value, expiresIn) { + return Factory.Set(AccessToken, key, value, expiresIn) +} + +async function removeAccessToken(key) { + return Factory.Remove(AccessToken, key) +} + +async function updateAccessToken(key) { + return Factory.Update(AccessToken, key) +} + +async function getUserAccessToken(key, fallback) { + return Factory.Get(UserAccessToken, key, fallback) +} + +async function setUserAccessToken(key, value, expiresIn) { + return Factory.Set(UserAccessToken, key, value, expiresIn) +} + +async function removeUserAccessToken(key) { + return Factory.Remove(UserAccessToken, key) +} + +async function getSessionKey(key, fallback) { + return Factory.Get(SessionKey, key, fallback) +} + +async function setSessionKey(key, value, expiresIn) { + return Factory.Set(SessionKey, key, value, expiresIn) +} + +async function removeSessionKey(key) { + return Factory.Remove(SessionKey, key) +} + +async function getEncryptKey(key, fallback) { + return Factory.Get(Encryptkey, key, fallback) +} + +async function setEncryptKey(key, value, expiresIn) { + return Factory.Set(Encryptkey, key, value, expiresIn) +} + +async function removeEncryptKey(key) { + return Factory.Remove(Encryptkey, key) +} + +async function updateEncryptKey(key) { + return Factory.Update(Encryptkey, key) +} + +async function getTicket(key, fallback) { + return Factory.Get(Ticket, key, fallback) +} + +async function setTicket(key, value, expiresIn) { + return Factory.Set(Ticket, key, value, expiresIn) +} + +async function removeTicket(key) { + return Factory.Remove(Ticket, key) +} + +async function updateTicket(key) { + return Factory.Update(Ticket, key) +} + +module.exports = { + getAccessToken, + setAccessToken, + removeAccessToken, + updateAccessToken, + getUserAccessToken, + setUserAccessToken, + removeUserAccessToken, + getSessionKey, + setSessionKey, + removeSessionKey, + getEncryptKey, + setEncryptKey, + removeEncryptKey, + updateEncryptKey, + getTicket, + setTicket, + removeTicket, + updateTicket, + ProviderType, + PlatformType, + WeixinServer, + ErrorCodeType +} diff --git a/uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/package.json b/uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/package.json new file mode 100644 index 0000000..a017b49 --- /dev/null +++ b/uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/package.json @@ -0,0 +1,15 @@ +{ + "name": "uni-open-bridge-common", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "uni-config-center": "file:../../../../../uni-config-center/uniCloud/cloudfunctions/common/uni-config-center" + } +} \ No newline at end of file diff --git a/uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/storage.js b/uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/storage.js new file mode 100644 index 0000000..bfb13a1 --- /dev/null +++ b/uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/storage.js @@ -0,0 +1,111 @@ +'use strict'; + +const { + Validator +} = require('./validator.js') + +const { + CacheKeyCascade +} = require('./uni-cloud-cache.js') + +const { + BridgeError +} = require('./bridge-error.js') + +class Storage { + + constructor(type, keys) { + this._type = type || null + this._keys = keys || [] + } + + async get(key, fallback) { + this.validateKey(key) + const result = await this.create(key, fallback).get() + return result.value + } + + async set(key, value, expiresIn) { + this.validateKey(key) + this.validateValue(value) + const expires_in = this.getExpiresIn(expiresIn) + if (expires_in !== 0) { + await this.create(key).set(this.getValue(value), expires_in) + } + } + + async remove(key) { + this.validateKey(key) + await this.create(key).remove() + } + + // virtual + async update(key) { + this.validateKey(key) + } + + async ttl(key) { + this.validateKey(key) + // 后续考虑支持 + } + + async fallback(key) {} + + getKeyString(key) { + const keyArray = [Storage.Prefix] + this._keys.forEach((name) => { + keyArray.push(key[name]) + }) + keyArray.push(this._type) + return keyArray.join(':') + } + + getValue(value) { + return value + } + + getExpiresIn(value) { + if (value !== undefined) { + return value + } + return -1 + } + + validateKey(key) { + Validator.Key(this._keys, key) + } + + validateValue(value) { + Validator.Value(value) + } + + create(key, fallback) { + const keyString = this.getKeyString(key) + const options = { + layers: [{ + type: 'database', + key: keyString + }, { + type: 'redis', + key: keyString + }] + } + + const _this = this + return new CacheKeyCascade({ + ...options, + fallback: async function() { + if (fallback) { + return fallback(key) + } else if (_this.fallback) { + return _this.fallback(key) + } + } + }) + } +} +Storage.Prefix = "uni-id" + +module.exports = { + Storage +}; diff --git a/uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/uni-cloud-cache.js b/uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/uni-cloud-cache.js new file mode 100644 index 0000000..2e4286b --- /dev/null +++ b/uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/uni-cloud-cache.js @@ -0,0 +1,324 @@ +const db = uniCloud.database() + +function getType(value) { + return Object.prototype.toString.call(value).slice(8, -1).toLowerCase() +} + +const validator = { + key: function(value) { + const err = new Error('Invalid key') + if (typeof value !== 'string') { + throw err + } + const valueTrim = value.trim() + if (!valueTrim || valueTrim !== value) { + throw err + } + }, + value: function(value) { + // 仅作简单校验 + const type = getType(value) + const validValueType = ['null', 'number', 'string', 'array', 'object'] + if (validValueType.indexOf(type) === -1) { + throw new Error('Invalid value type') + } + }, + duration: function(value) { + const err = new Error('Invalid duration') + if (value === undefined) { + return + } + if (typeof value !== 'number' || value === 0) { + throw err + } + } +} + +/** + * 入库时 expired 为过期时间对应的时间戳,永不过期用-1表示 + * 返回结果时 与redis对齐,-1表示永不过期,-2表示已过期或不存在 + */ +class DatabaseCache { + constructor({ + collection = 'opendb-open-data' + } = {}) { + this.type = 'db' + this.collection = db.collection(collection) + } + + _serializeValue(value) { + return value === undefined ? null : JSON.stringify(value) + } + + _deserializeValue(value) { + return value ? JSON.parse(value) : value + } + + async set(key, value, duration) { + validator.key(key) + validator.value(value) + validator.duration(duration) + value = this._serializeValue(value) + await this.collection.doc(key).set({ + value, + expired: duration && duration !== -1 ? Date.now() + (duration * 1000) : -1 + }) + } + + async _getWithDuration(key) { + const getKeyRes = await this.collection.doc(key).get() + const record = getKeyRes.data[0] + if (!record) { + return { + value: null, + duration: -2 + } + } + const value = this._deserializeValue(record.value) + const expired = record.expired + if (expired === -1) { + return { + value, + duration: -1 + } + } + const duration = expired - Date.now() + if (duration <= 0) { + await this.remove(key) + return { + value: null, + duration: -2 + } + } + return { + value, + duration: Math.floor(duration / 1000) + } + } + + async get(key, { + withDuration = true + } = {}) { + const result = await this._getWithDuration(key) + if (!withDuration) { + delete result.duration + } + return result + } + + async remove(key) { + await this.collection.doc(key).remove() + } +} + +class RedisCache { + constructor() { + this.type = 'redis' + this.redis = uniCloud.redis() + } + + _serializeValue(value) { + return value === undefined ? null : JSON.stringify(value) + } + + _deserializeValue(value) { + return value ? JSON.parse(value) : value + } + + async set(key, value, duration) { + validator.key(key) + validator.value(value) + validator.duration(duration) + value = this._serializeValue(value) + if (!duration || duration === -1) { + await this.redis.set(key, value) + } else { + await this.redis.set(key, value, 'EX', duration) + } + } + + async get(key, { + withDuration = false + } = {}) { + let value = await this.redis.get(key) + value = this._deserializeValue(value) + if (!withDuration) { + return { + value + } + } + const durationSecond = await this.redis.ttl(key) + let duration + switch (durationSecond) { + case -1: + duration = -1 + break + case -2: + duration = -2 + break + default: + duration = durationSecond + break + } + return { + value, + duration + } + } + + async remove(key) { + await this.redis.del(key) + } +} + +class Cache { + constructor({ + type, + collection + } = {}) { + if (type === 'database') { + return new DatabaseCache({ + collection + }) + } else if (type === 'redis') { + return new RedisCache() + } else { + throw new Error('Invalid cache type') + } + } +} + +class CacheKey { + constructor({ + type, + collection, + cache, + key, + fallback + } = {}) { + this.cache = cache || new Cache({ + type, + collection + }) + this.key = key + this.fallback = fallback + } + + async set(value, duration) { + await this.cache.set(this.key, value, duration) + } + + async setWithSync(value, duration, syncMethod) { + await Promise.all([ + this.set(this.key, value, duration), + syncMethod(value, duration) + ]) + } + + async get() { + let { + value, + duration + } = await this.cache.get(this.key) + if (value !== null && value !== undefined) { + return { + value, + duration + } + } + if (!this.fallback) { + return { + value: null, + duration: -2 + } + } + const fallbackResult = await this.fallback() + value = fallbackResult.value + duration = fallbackResult.duration + if (value !== null && duration !== undefined) { + await this.cache.set(this.key, value, duration) + } + return { + value, + duration + } + } + + async remove() { + await this.cache.remove(this.key) + } +} + +class CacheKeyCascade { + constructor({ + layers, // [{cache, type, collection, key}] 从低级到高级排序,[DbCacheKey, RedisCacheKey] + fallback + } = {}) { + this.layers = layers + this.cacheLayers = [] + let lastCacheKey + for (let i = 0; i < layers.length; i++) { + const { + type, + cache, + collection, + key + } = layers[i] + const lastCacheKeyTemp = lastCacheKey + try { + const currentCacheKey = new CacheKey({ + type, + collection, + cache, + key, + fallback: i === 0 ? fallback : function() { + return lastCacheKeyTemp.get() + } + }) + this.cacheLayers.push(currentCacheKey) + lastCacheKey = currentCacheKey + } catch (e) {} + } + this.highLevelCache = lastCacheKey + } + + async set(value, duration) { + return Promise.all( + this.cacheLayers.map(item => { + return item.set(value, duration) + }) + ) + } + + async setWithSync(value, duration, syncMethod) { + const setPromise = this.cacheLayers.map(item => { + return item.set(value, duration) + }) + return Promise.all( + [ + ...setPromise, + syncMethod(value, duration) + ] + ) + } + + async get() { + return this.highLevelCache.get() + } + + async remove() { + await Promise.all( + this.cacheLayers.map(cacheKeyItem => { + return cacheKeyItem.remove() + }) + ) + } +} + +module.exports = { + Cache, + DatabaseCache, + RedisCache, + CacheKey, + CacheKeyCascade +} diff --git a/uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/validator.js b/uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/validator.js new file mode 100644 index 0000000..47a455b --- /dev/null +++ b/uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/validator.js @@ -0,0 +1,31 @@ +const Validator = { + + Key(keyArray, parameters) { + for (let i = 0; i < keyArray.length; i++) { + const keyName = keyArray[i] + if (typeof parameters[keyName] !== 'string') { + Validator.ThrowNewError(`Invalid ${keyName}`) + } + if (parameters[keyName].length < 1) { + Validator.ThrowNewError(`Invalid ${keyName}`) + } + } + }, + + Value(value) { + if (value === undefined) { + Validator.ThrowNewError('Invalid Value') + } + if (typeof value !== 'object') { + Validator.ThrowNewError('Invalid Value Type') + } + }, + + ThrowNewError(message) { + throw new Error(message) + } +} + +module.exports = { + Validator +} diff --git a/uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/weixin-server.js b/uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/weixin-server.js new file mode 100644 index 0000000..ef476f1 --- /dev/null +++ b/uni_modules/uni-open-bridge-common/uniCloud/cloudfunctions/common/uni-open-bridge-common/weixin-server.js @@ -0,0 +1,203 @@ +'use strict'; + +const crypto = require('crypto') + +const { + HTTP_STATUS +} = require('./consts.js') + +const { + BridgeError +} = require('./bridge-error.js') + +class WeixinServer { + + constructor(options = {}) { + this._appid = options.appid + this._secret = options.secret + } + + getAccessToken() { + return uniCloud.httpclient.request(WeixinServer.AccessToken_Url, { + dataType: 'json', + method: 'POST', + contentType: 'json', + data: { + appid: this._appid, + secret: this._secret, + grant_type: "client_credential" + } + }) + } + + // 使用客户端获取的 code 从微信服务器换取 openid,code 仅可使用一次 + codeToSession(code) { + return uniCloud.httpclient.request(WeixinServer.Code2Session_Url, { + dataType: 'json', + data: { + appid: this._appid, + secret: this._secret, + js_code: code, + grant_type: 'authorization_code' + } + }) + } + + getUserEncryptKey({ + access_token, + openid, + session_key + }) { + console.log(access_token, openid, session_key); + const signature = crypto.createHmac('sha256', session_key).update('').digest('hex') + return uniCloud.httpclient.request(WeixinServer.User_Encrypt_Key_Url, { + dataType: 'json', + method: 'POST', + dataAsQueryString: true, + data: { + access_token, + openid: openid, + signature: signature, + sig_method: 'hmac_sha256' + } + }) + } + + getH5AccessToken() { + return uniCloud.httpclient.request(WeixinServer.AccessToken_H5_Url, { + dataType: 'json', + method: 'GET', + data: { + appid: this._appid, + secret: this._secret, + grant_type: "client_credential" + } + }) + } + + getH5Ticket(access_token) { + return uniCloud.httpclient.request(WeixinServer.Ticket_Url, { + dataType: 'json', + dataAsQueryString: true, + method: 'POST', + data: { + access_token + } + }) + } + + getH5AccessTokenForEip() { + return uniCloud.httpProxyForEip.postForm(WeixinServer.AccessToken_H5_Url, { + appid: this._appid, + secret: this._secret, + grant_type: "client_credential" + }, { + dataType: 'json' + }) + } + + getH5TicketForEip(access_token) { + return uniCloud.httpProxyForEip.postForm(WeixinServer.Ticket_Url, { + access_token + }, { + dataType: 'json', + dataAsQueryString: true + }) + } +} + +WeixinServer.AccessToken_Url = 'https://api.weixin.qq.com/cgi-bin/stable_token' +WeixinServer.Code2Session_Url = 'https://api.weixin.qq.com/sns/jscode2session' +WeixinServer.User_Encrypt_Key_Url = 'https://api.weixin.qq.com/wxa/business/getuserencryptkey' +WeixinServer.AccessToken_H5_Url = 'https://api.weixin.qq.com/cgi-bin/token' +WeixinServer.Ticket_Url = 'https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi' + +WeixinServer.GetMPAccessToken = function(options) { + return new WeixinServer(options).getAccessToken() +} + +WeixinServer.GetCodeToSession = function(options) { + return new WeixinServer(options).codeToSession(options.code) +} + +WeixinServer.GetUserEncryptKey = function(options) { + return new WeixinServer(options).getUserEncryptKey(options) +} + +WeixinServer.GetH5AccessToken = function(options) { + return new WeixinServer(options).getH5AccessToken() +} + +WeixinServer.GetH5Ticket = function(options) { + return new WeixinServer(options).getH5Ticket(options.access_token) +} + +//////////////////////////////////////////////////////////////// + +function isAliyun() { + return (uniCloud.getCloudInfos()[0].provider === 'aliyun') +} + +WeixinServer.GetResponseData = function(response) { + console.log("WeixinServer::response", response) + + if (!(response.status === HTTP_STATUS.SUCCESS || response.statusCodeValue === HTTP_STATUS.SUCCESS)) { + throw new BridgeError(response.status || response.statusCodeValue, response.status || response.statusCodeValue) + } + + const responseData = response.data || response.body + + if (responseData.errcode !== undefined && responseData.errcode !== 0) { + throw new BridgeError(responseData.errcode, responseData.errmsg) + } + + return responseData +} + +WeixinServer.GetMPAccessTokenData = async function(options) { + const response = await new WeixinServer(options).getAccessToken() + return WeixinServer.GetResponseData(response) +} + +WeixinServer.GetCodeToSessionData = async function(options) { + const response = await new WeixinServer(options).codeToSession(options.code) + return WeixinServer.GetResponseData(response) +} + +WeixinServer.GetUserEncryptKeyData = async function(options) { + const response = await new WeixinServer(options).getUserEncryptKey(options) + return WeixinServer.GetResponseData(response) +} + +WeixinServer.GetH5AccessTokenData = async function(options) { + const ws = new WeixinServer(options) + let response + if (isAliyun()) { + response = await ws.getH5AccessTokenForEip() + if (typeof response === 'string') { + response = JSON.parse(response) + } + } else { + response = await ws.getH5AccessToken() + } + return WeixinServer.GetResponseData(response) +} + +WeixinServer.GetH5TicketData = async function(options) { + const ws = new WeixinServer(options) + let response + if (isAliyun()) { + response = await ws.getH5TicketForEip(options.access_token) + if (typeof response === 'string') { + response = JSON.parse(response) + } + } else { + response = await ws.getH5Ticket(options.access_token) + } + return WeixinServer.GetResponseData(response) +} + + +module.exports = { + WeixinServer +} diff --git a/uni_modules/uni-open-bridge-common/uniCloud/database/opendb-open-data.schema.json b/uni_modules/uni-open-bridge-common/uniCloud/database/opendb-open-data.schema.json new file mode 100644 index 0000000..bc25098 --- /dev/null +++ b/uni_modules/uni-open-bridge-common/uniCloud/database/opendb-open-data.schema.json @@ -0,0 +1,19 @@ +// 文档教程: https://uniapp.dcloud.net.cn/uniCloud/schema +{ + "bsonType": "object", + "required": ["_id", "value"], + "properties": { + "_id": { + "bsonType": "string", + "description": "key,格式:uni-id:[provider]:[appid]:[openid]:[access-token|user-access-token|session-key|encrypt-key-version|ticket]" + }, + "value": { + "bsonType": "object", + "description": "字段_id对应的值" + }, + "expired": { + "bsonType": "date", + "description": "过期时间" + } + } +} diff --git a/uni_modules/uni-open-bridge/changelog.md b/uni_modules/uni-open-bridge/changelog.md new file mode 100644 index 0000000..e69de29 diff --git a/uni_modules/uni-open-bridge/package.json b/uni_modules/uni-open-bridge/package.json new file mode 100644 index 0000000..e88a0a7 --- /dev/null +++ b/uni_modules/uni-open-bridge/package.json @@ -0,0 +1,84 @@ +{ + "id": "uni-open-bridge", + "displayName": "uni-open-bridge", + "version": "1.0.0", + "description": "uni-open-bridge", + "keywords": [ + "uni-open-bridge" +], + "repository": "", + "engines": { + "HBuilderX": "^3.1.0" + }, + "dcloudext": { + "category": [ + "uniCloud", + "云函数模板" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "", + "data": "", + "permissions": "" + }, + "npmurl": "" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "u", + "aliyun": "u" + }, + "client": { + "Vue": { + "vue2": "u", + "vue3": "u" + }, + "App": { + "app-vue": "u", + "app-nvue": "u" + }, + "H5-mobile": { + "Safari": "u", + "Android Browser": "u", + "微信浏览器(Android)": "u", + "QQ浏览器(Android)": "u" + }, + "H5-pc": { + "Chrome": "u", + "IE": "u", + "Edge": "u", + "Firefox": "u", + "Safari": "u" + }, + "小程序": { + "微信": "u", + "阿里": "u", + "百度": "u", + "字节跳动": "u", + "QQ": "u", + "钉钉": "u", + "快手": "u", + "飞书": "u", + "京东": "u" + }, + "快应用": { + "华为": "u", + "联盟": "u" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-open-bridge/readme.md b/uni_modules/uni-open-bridge/readme.md new file mode 100644 index 0000000..9f4f9f7 --- /dev/null +++ b/uni_modules/uni-open-bridge/readme.md @@ -0,0 +1,577 @@ +# uni-open-bridge + +`uni-open-bridge` + + +## config.json + +```json +{ + "schedule": { + "__UNI__xxxxxx": { + "enable": true, + "mp-weixin": { + "enable": true, + "tasks": ["accessToken"] + }, + "h5-weixin": { + "enable": false, + "tasks": ["ticket"] + } + } + }, + "ipWhitelist": ["0.0.0.0"] +} +``` + +## http 调用 + +请求类型 `POST`, 需要配置IP白名单字段 `ipWhitelist`,参见 `config.json` + +### getAccessToken + +Url + +``` +https://xxxxxxxx-xxxx-4xxx-xxxx-xxxxxxxxxxxx.bspapp.com/uni-open-bridge/getAccessToken +``` + +参数 + +```json +{ + "dcloudAppid": "__UNI__xxx", + "platform": "mp-weixin" +} +``` + +### setAccessToken + +Url + +``` +https://xxxxxxxx-xxxx-4xxx-xxxx-xxxxxxxxxxxx.bspapp.com/uni-open-bridge/setAccessToken +``` + +```json +{ + "dcloudAppid": "__UNI__xxx", + "platform": "mp-weixin", + "value": { + "access_token": "" + }, + "expiresIn": 7200 +} +``` + +### removeAccessToken + +Url + +``` +https://xxxxxxxx-xxxx-4xxx-xxxx-xxxxxxxxxxxx.bspapp.com/uni-open-bridge/removeAccessToken +``` + +参数 + +```json +{ + "dcloudAppid": "__UNI__xxx", + "platform": "mp-weixin" +} +``` + +### getUserKey + +Url + +``` +https://xxxxxxxx-xxxx-4xxx-xxxx-xxxxxxxxxxxx.bspapp.com/uni-open-bridge/getUserKey +``` + +参数 + +```json +{ + "dcloudAppid": "__UNI__xxx", + "platform": "mp-weixin", + "openid": "" +} +``` + +### setUserKey + +Url + +``` +https://xxxxxxxx-xxxx-4xxx-xxxx-xxxxxxxxxxxx.bspapp.com/uni-open-bridge/setUserKey +``` + +参数 + +```json +{ + "dcloudAppid": "__UNI__xxx", + "platform": "mp-weixin", + "openid": "", + "value": { + "session_key": "" + }, + "expiresIn": 7200 +} +``` + +### removeUserKey + +Url + +``` +https://xxxxxxxx-xxxx-4xxx-xxxx-xxxxxxxxxxxx.bspapp.com/uni-open-bridge/removeUserKey +``` + +参数 + +```json +{ + "dcloudAppid": "__UNI__xxx", + "platform": "mp-weixin", + "openid": "" +} +``` + +### getTicket + +Url + +``` +https://xxxxxxxx-xxxx-4xxx-xxxx-xxxxxxxxxxxx.bspapp.com/uni-open-bridge/getTicket +``` + +参数 + +```json +{ + "dcloudAppid": "__UNI__xxx", + "platform": "mp-weixin" +} +``` + + +### setTicket + +Url + +``` +https://xxxxxxxx-xxxx-4xxx-xxxx-xxxxxxxxxxxx.bspapp.com/uni-open-bridge/setTicket +``` + +参数 + +```json +{ + "dcloudAppid": "__UNI__xxx", + "platform": "mp-weixin", + "value": { + "ticket": "" + }, + "expiresIn": 7200 +} +``` + +### removeTicket + +Url + +``` +https://xxxxxxxx-xxxx-4xxx-xxxx-xxxxxxxxxxxx.bspapp.com/uni-open-bridge/removeTicket +``` + +参数 + +```json +{ + "dcloudAppid": "__UNI__xxx", + "platform": "mp-weixin" +} +``` + + + + + +# uni-open-bridge-common + +`uni-open-bridge-common` 是 `uni-id` 体系中用于 `开放平台数据` 管理的公共模块。 + +> `云函数公共模块`是不同云函数共享代码的一种方式。如果你不了解什么是`云函数公共模块`,请另读文档[公共模块](https://uniapp.dcloud.io/uniCloud/cf-common) + +`uni-open-bridge-common` 提供了 `access_token`、`session_key`、`encrypt_key`、`ticket` 的读取、写入、删除操作。 + +`uni-open-bridge-common` 支持多层 读取 / 写入 机制,`redis -> database -> fallback`,优先级如下: + +如果用户没有开通 `redis` 或者操作失败,透传到 `database`,`database` 失败后,如果用户配置了 `fallback`,继续调用 `fallback` 方法,否则抛出 `Error` + +`database` 对应的表为: `opendb-open-data` + + +## access_token + +`access_token` 是微信小程序全局唯一后台接口调用凭据,调用绝大多数后台接口时都需使用。开发者可以通过 getAccessToken 接口获取并进行妥善保存。[详情](https://developers.weixin.qq.com/miniprogram/dev/framework/server-ability/backend-api.html#access_token) + + +### getAccessToken(key: Object) + +读取 access_token + + +### setAccessToken(key: Object, value: Object, expiresIn: Number) + +写入 access_token + + +### removeAccessToken(key: Object) + +删除 access_token + + +### key 属性 + +|参数 |类型 |必填 |描述 | +|:-: |:-: |:-: |:-: | +|dcloudAppid|String |是 |DCloud应用appid。[详情](https://ask.dcloud.net.cn/article/35907) | +|platform |String |是 |[详情](#platform) | +|fallback |Function |否 |[详情](#fallback) | + +### value 属性 + +|参数 |类型 |描述 | +|:-: |:-: |:-: | +|access_token |String | | + +### expiresIn + +有效时间(秒) + + +### 示例代码 + +```js +'use strict'; + +const { + getAccessToken, + setAccessToken, + removeAccessToken +} = require('uni-open-bridge-common') + +exports.main = async (event, context) => { + const key = { + dcloudAppid: '', + platform: '' + } + const value = { + access_token: '' + } + const expiresIn = 7200 + + // 写入 (redis / 数据库) + await setAccessToken(key, value, expiresIn) + + // 读取 (redis / 数据库) + let result1 = await getAccessToken(key) + + // 删除 + await removeAccessToken(key) + + + // 删除后读取, 返回 null + let result2 = await getAccessToken(key) + console.log(result2) // null + + return null +}; +``` + + +## user_key + +平台对应的值 + +|平台 |值 |描述 | +|:-: |:-: |:-: | +|微信小程序 |session_key|微信小程序会话密钥。[详情](https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/user-login/code2Session.html) | + + +### getUserKey(key: Object) + +读取 user_key + + +### setUserKey(key: Object, value: Object, expiresIn: Number) + +写入 user_key + + +### removeUserKey(key: Object) + +删除 user_key + + +### key 属性 + +|参数 |类型 |必填 |描述 | +|:-: |:-: |:-: |:-: | +|dcloudAppid|String |是 |DCloud应用appid。[详情](https://ask.dcloud.net.cn/article/35907) | +|platform |String |是 |[详情](#platform) | +|openid |String |是 | | +|fallback |Function |否 |[详情](#fallback) | + +### value 属性 + +|参数 |类型 |描述 | +|:-: |:-: |:-: | +|session_key|String |微信小程序会话密钥 | + +### expiresIn + +有效时间(秒) + + +### 示例代码 + +```js +'use strict'; + +const { + getUserKey, + setUserKey, + removeUserKey, +} = require('uni-open-bridge-common') + +exports.main = async (event, context) => { + const key = { + dcloudAppid: '', + platform: '', + openid: '' + } + const value = { + 'session_key': '' + } + const expiresIn = 7200 + + // 写入 (redis / 数据库) + await setUserKey(key, value, expiresIn) + + // 读取 (redis / 数据库) + let result1 = await getUserKey(key) + + // 删除 + await removeUserKey(key) + + + // 删除后读取, 返回 null + let result2 = await getUserKey(key) + console.log(result2) // null + + return null +}; +``` + + +## encrypt_key + +为了避免小程序与开发者后台通信时数据被截取和篡改,微信侧维护了一个用户维度的可靠key,用于小程序和后台通信时进行加密和签名。[详情](https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/user-encryptkey.html) + +开发者可以分别通过小程序前端和微信后台提供的接口,获取用户的加密 key。 + + +### getEncryptKey(key: Object) + +读取 encrypt_key + + +### setEncryptKey(key: Object, value: Object, expiresIn: Number) + +写入 encrypt_key + + +### removeEncryptKey(key: Object) + +删除 encrypt_key + + +### key 属性 + +|参数 |类型 |必填 |描述 | +|:-: |:-: |:-: |:-: | +|dcloudAppid|String |是 |DCloud应用appid。[详情](https://ask.dcloud.net.cn/article/35907) | +|platform |String |是 |[详情](#platform) | +|openid |String |是 | | +|version |Number |是 |版本 | +|fallback |Function |否 |[详情](#fallback) | + + +### value 属性 + +|参数 |类型 |描述 | +|:-: |:-: |:-: | +|encrypt_key|String |加密 key | +|iv |String |加密 iv | + +### expiresIn + +有效时间(秒) + + +### 示例代码 + +```js +'use strict'; + +const { + getEncryptKey, + setEncryptKey, + removeEncryptKey +} = require('uni-open-bridge-common') + +exports.main = async (event, context) => { + const key = { + dcloudAppid: '', + platform: '', + openid: '', + version: 1 + } + const value = { + encrypt_key: '', + iv: '' + } + const expiresIn = 7200 + + // 写入 (redis / 数据库) + await setEncryptKey(key, value, expiresIn) + + // 读取 (redis / 数据库) + let result1 = await getEncryptKey(key) + + // 删除 + await removeEncryptKey(key) + + // 删除后读取, 返回 null + let result2 = await getEncryptKey(key) + console.log(result2) // null + + return null +}; +``` + + +## ticket + +`ticket` 是公众号用于调用微信 JS 接口的临时票据。[详情](https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#62) + + +### getTicket(key: Object) + +读取 ticket + + +### setTicket(key: Object, value: Object, expiresIn: Number) + +写入 ticket + + +### removeTicket(key: Object) + +删除 ticket + + +### key 属性 + +|参数 |类型 |必填 |描述 | +|:-: |:-: |:-: |:-: | +|dcloudAppid|String |是 |DCloud应用appid。[详情](https://ask.dcloud.net.cn/article/35907) | +|platform |String |是 |[详情](#platform) | +|fallback |Function |否 |[详情](#fallback) | + +### value 属性 + +|参数 |类型 |描述 | +|:-: |:-: |:-: | +|ticket |String | | + +### expiresIn + +有效时间(秒) + + +### 示例代码 + +```js +'use strict'; + +const { + getTicket, + setTicket, + removeTicket +} = require('uni-open-bridge-common') + +exports.main = async (event, context) => { + const key = { + dcloudAppid: '', + platform: '' + } + const value = { + ticket: '' + } + const expiresIn = 7200 + + // 写入 (redis / 数据库) + await setTicket(key, value, expiresIn) + + // 读取 (redis / 数据库) + let result1 = await getTicket(key) + + // 删除 + await removeTicket(key) + + + // 删除后读取, 返回 null + let result2 = await getTicket(key) + console.log(result2) // null + + return null +}; +``` + + +## Platform@platform + +平台对应的值 + +|值 |描述 | +|:-: |:-: | +|mp-weixin |微信小程序 | +|app-weixin |微信 App | +|h5-weixin |微信公众号 | +|web-weixin |微信pc网页 | +|mp-qq |QQ 小程序 | +|app-qq |QQ App | + + +## fallback@fallback + +可选 `async function fallback()`,当 `reids -> database` 都找不到对应 `key` 时,调用此方法,需要返回数据格式如下 + +```json +{ + value: null, + duration: 1 +} +``` + + + +注意事项 + +- 所有方法类型为 `async`,需要使用 `await` +- 所有方法校验 `key` 属性是否有效,无效则 `throw new Error()`,对 `value` 仅校验是否为 `undefined` diff --git a/uni_modules/uni-open-bridge/uniCloud/cloudfunctions/uni-open-bridge/basic.js b/uni_modules/uni-open-bridge/uniCloud/cloudfunctions/uni-open-bridge/basic.js new file mode 100644 index 0000000..b59f040 --- /dev/null +++ b/uni_modules/uni-open-bridge/uniCloud/cloudfunctions/uni-open-bridge/basic.js @@ -0,0 +1,131 @@ +'use strict'; + +class Command { + + constructor() { + this._registered = {} + } + + async execute(name, args) { + if (this._registered[name]) { + return await this._registered[name].execute(args) + } + } + + canExecute(name, args) { + if (this._registered[name] && this._registered[name].canExecute) { + this._registered[name].canExecute(args) + } + return false + } + + register(name, execute, canExecute) { + this._registered[name] = { + execute, + canExecute + } + } +} + +class Task { + + constructor(id) { + this._id = id || this._newTaskId() + + this._state = Task.TASK_STATE.WAITING + } + + async run() {} + + async cancel() {} + + async taskAction() {} + + _newTaskId() { + let guid = '' + const format = 'xxxxxxxx-xxxx-4xxx-xxxx-xxxxxxxxxxxx' + for (let i = 0; i < format.length; i++) { + if (format[i] === 'x') { + guid += (Math.random() * 16 | 0).toString(16) + } else { + guid += format[i] + } + } + return guid.toUpperCase() + } + + get id() { + return this._id + } + set id(value) { + this._id = value + } + + get state() { + return this._state + } + set state(value) { + this._state = value + } +} + +Task.TASK_STATE = { + WAITING: "WAITING", + RESOLVING: "RESOLVING", + RESOLVED: "RESOLVED", + EXECUTING: "EXECUTING", + ERROR: "ERROR", + COMPLETED: "COMPLETED", + CANCELLING: "CANCELLING", + CANCELLED: "CANCELLED" +} + +class TaskManager { + + constructor() { + this._tasks = [] + } + + get tasks() { + return this._tasks + } + + clear() { + this._tasks.length = 0 + } + + getTask(id) { + return this._tasks.find((item) => { + return (item.id == id) + }) + } + + addTask(task) { + this._tasks.push(task) + } + + deleteTask(id) { + const index = this.findIndex(id) + if (index < 0) { + return + } + + this._tasks[index].cancel() + + if (index > -1) { + this._tasks.splice(index, 1) + } + } + + findIndex(id) { + return this._tasks.findIndex((item) => { + return (item.id == id) + }) + } +} + +module.exports = { + Command, + Task, + TaskManager +}; diff --git a/uni_modules/uni-open-bridge/uniCloud/cloudfunctions/uni-open-bridge/config.js b/uni_modules/uni-open-bridge/uniCloud/cloudfunctions/uni-open-bridge/config.js new file mode 100644 index 0000000..83adffa --- /dev/null +++ b/uni_modules/uni-open-bridge/uniCloud/cloudfunctions/uni-open-bridge/config.js @@ -0,0 +1,197 @@ +'use strict'; + +const { + PlatformType +} = require('./consts.js') + +const configCenter = require('uni-config-center') + +const OauthConfig = { + 'mp-weixin': ['oauth', 'weixin'], + 'h5-weixin': ['oauth', 'weixin'] +} + +class TaskConfig { + + constructor(options) { + this._dcloudAppid = options.dcloudAppid + this._appid = options.appid + this._secret = options.secret + this._platform = options.platform + this._tasks = options.tasks + this._timeout = 1000 * 10 + } + + get dcloudAppid() { + return this._dcloudAppid + } + + get appid() { + return this._appid + } + + get secret() { + return this._secret + } + + get platform() { + return this._platform + } + + get tasks() { + return this._tasks + } +} + +class ConfigBase { + + constructor() { + const uniIdConfig = configCenter({ + pluginId: 'uni-id' + }) + const openBridgeConfig = configCenter({ + pluginId: 'uni-open-bridge' + }) + + this._uniId = uniIdConfig.config() + this._openBridge = openBridgeConfig.config() + + this._ready = true + } + + getAppConfig(appid) { + if (Array.isArray(this._uniId)) { + return this._uniId.find((item) => { + return (item.dcloudAppid === appid) + }) + } + + if (this._uniId.dcloudAppid === appid) { + return this._uniId + } + + return null + } + + inWhitelist(ip) { + return (this.ipWhitelist.indexOf(ip) > -1) + } + + get openBridge() { + return this._openBridge + } + + get ipWhitelist() { + return this._openBridge.ipWhitelist + } + + get ready() { + return this._ready + } +} + +class OpenBridgeConfig extends ConfigBase { + + constructor() { + super() + + this._tasks = [] + + this.resolve() + } + + get tasks() { + return this._tasks + } + + resolve() { + this._tasks.length = 0 + + const appids = Object.keys(this.openBridge.schedule) + + for (let i = 0; i < appids.length; i++) { + const appid = appids[i] + let appConfig = this.getAppConfig(appid) + + if (appConfig != null) { + const schedule = this.openBridge.schedule[appid] + if (schedule) { + this.resolveSchedule(appid, appConfig, schedule) + } + } + } + } + + resolveSchedule(dcloudAppid, appConfig, schedule) { + if (schedule.enable !== true) { + return + } + + const schedulePlatforms = Object.keys(schedule) + + for (let i = 0; i < schedulePlatforms.length; i++) { + const platformName = schedulePlatforms[i] + const scheduleTask = schedule[platformName] + + if (!scheduleTask.enable) { + continue + } + + if (!this.isSupport(platformName)) { + continue + } + + const oauthConfig = this.getOauthConfig(appConfig, platformName) + if (!oauthConfig) { + continue + } + + this._tasks.push({ + platform: platformName, + tasks: scheduleTask.tasks, + dcloudAppid: dcloudAppid, + appid: oauthConfig.appid, + secret: oauthConfig.secret + }) + } + } + + isSupport(platformName) { + return (OpenBridgeConfig.Support_Platforms.indexOf(platformName) >= 0) + } + + getOauthConfig(appConfig, platformName) { + const platformConfig = appConfig[platformName] + if (!platformConfig) { + return null + } + + let tree = OauthConfig[platformName] + let node = platformConfig + for (let i = 0; i < tree.length; i++) { + let nodeName = tree[i] + if (node[nodeName]) { + node = node[nodeName] + } else { + node = null + break + } + } + + if (node && node.appid && node.appsecret) { + return { + appid: node.appid, + secret: node.appsecret + } + } + + return null + } +} + +OpenBridgeConfig.Support_Platforms = ['mp-weixin', 'h5-weixin'] + + +module.exports = { + OpenBridgeConfig +}; diff --git a/uni_modules/uni-open-bridge/uniCloud/cloudfunctions/uni-open-bridge/consts.js b/uni_modules/uni-open-bridge/uniCloud/cloudfunctions/uni-open-bridge/consts.js new file mode 100644 index 0000000..c163e96 --- /dev/null +++ b/uni_modules/uni-open-bridge/uniCloud/cloudfunctions/uni-open-bridge/consts.js @@ -0,0 +1,19 @@ +'use strict'; + +const HTTP_STATUS = { + SUCCESS: 200 +} + +const PlatformType = { + MP_WEIXIN: 'mp-weixin', + H5_WEIXIN: 'h5-weixin', + APP_WEIXIN: 'app-weixin', + WEB_WEIXIN: 'web-weixin', + MP_QQ: 'mp-qq', + APP_QQ: 'app-qq' +} + +module.exports = { + HTTP_STATUS, + PlatformType +} diff --git a/uni_modules/uni-open-bridge/uniCloud/cloudfunctions/uni-open-bridge/index.mp-weixin.js b/uni_modules/uni-open-bridge/uniCloud/cloudfunctions/uni-open-bridge/index.mp-weixin.js new file mode 100644 index 0000000..25556ef --- /dev/null +++ b/uni_modules/uni-open-bridge/uniCloud/cloudfunctions/uni-open-bridge/index.mp-weixin.js @@ -0,0 +1,93 @@ +'use strict'; + +const { + getAccessToken, + setAccessToken, + removeAccessToken, + getUserKey, + setUserKey, + removeUserKey, + getEncryptKey, + setEncryptKey, + removeEncryptKey, + getTicket, + setTicket, + removeTicket +} = require('uni-open-bridge-common') + +const { + Command +} = require('./basic.js'); + +const { + OpenBridgeConfig +} = require('./config.js') + +const openBridgeConfig = new OpenBridgeConfig() + +class MainFrame extends Command { + + constructor() { + super() + + MainFrame.Commands.forEach((name) => { + this.register(name, this[name].bind(this)) + }) + } + + async getAccessToken(parameters) { + return await getAccessToken(parameters) + } + + async setAccessToken(parameters) { + return await setAccessToken(parameters, parameters.value, parameters.expiresIn) + } + + async removeAccessToken(parameters) { + return await removeAccessToken(parameters) + } + + async getUserKey(parameters) { + return await getUserKey(parameters, null) + } + + async setUserKey(parameters) { + return await setUserKey(parameters, parameters.value, parameters.expiresIn) + } + + async removeUserKey(parameters) { + return await removeUserKey(parameters) + } + + async getTicket(parameters) { + return await getTicket(parameters, null) + } + + async setTicket(parameters) { + return await setTicket(parameters, parameters.value, parameters.expiresIn) + } + + async removeTicket(parameters) { + return await removeTicket(parameters) + } + + checkIP(ip) { + return openBridgeConfig.inWhitelist(ip) + } +} + +MainFrame.Commands = [ + 'getAccessToken', + 'setAccessToken', + 'removeAccessToken', + 'getUserKey', + 'setUserKey', + 'removeUserKey', + 'getTicket', + 'setTicket', + 'removeTicket' +]; + +const commands = new MainFrame(); + +module.exports = commands; diff --git a/uni_modules/uni-open-bridge/uniCloud/cloudfunctions/uni-open-bridge/index.obj.js b/uni_modules/uni-open-bridge/uniCloud/cloudfunctions/uni-open-bridge/index.obj.js new file mode 100644 index 0000000..71d6ae9 --- /dev/null +++ b/uni_modules/uni-open-bridge/uniCloud/cloudfunctions/uni-open-bridge/index.obj.js @@ -0,0 +1,62 @@ +'use strict'; + +const { + PlatformType +} = require('./consts.js') + +const runTask = require('./index.task.js') +const weixinCommand = require('./index.mp-weixin.js') + +async function executeCommand() { + const methodName = this.getMethodName() + const parameters = JSON.parse(this.getHttpInfo().body) + + if (parameters.platform === PlatformType.MP_WEIXIN) { + return await weixinCommand.execute(methodName, parameters) + } + + throw new Error('Invalid Platform') +} + +module.exports = { + async _timing() { + console.log('triggered by timing') + await runTask() + }, + async _before() { + const clientInfo = this.getClientInfo() + if (!weixinCommand.checkIP(clientInfo.clientIP)) { + throw new Error("Invalid IP:" + clientInfo.clientIP) + } + }, + // async runTask() { + // await runTask() + // }, + async getAccessToken() { + return await executeCommand.call(this) + }, + async setAccessToken() { + return await executeCommand.call(this) + }, + async removeAccessToken() { + return await executeCommand.call(this) + }, + async getUserKey() { + return await executeCommand.call(this) + }, + async setUserKey() { + return await executeCommand.call(this) + }, + async removeUserKey() { + return await executeCommand.call(this) + }, + async getTicket() { + return await executeCommand.call(this) + }, + async setTicket() { + return await executeCommand.call(this) + }, + async removeTicket() { + return await executeCommand.call(this) + } +} diff --git a/uni_modules/uni-open-bridge/uniCloud/cloudfunctions/uni-open-bridge/index.task.js b/uni_modules/uni-open-bridge/uniCloud/cloudfunctions/uni-open-bridge/index.task.js new file mode 100644 index 0000000..017dc88 --- /dev/null +++ b/uni_modules/uni-open-bridge/uniCloud/cloudfunctions/uni-open-bridge/index.task.js @@ -0,0 +1,86 @@ +'use strict'; + +const { + OpenBridgeConfig +} = require('./config.js') + +const { + TaskManager +} = require('./basic.js') + +const { + TaskAccessTokenMP +} = require('./task-mp-weixin.js') + +const { + TaskAccessTokenH5, + TaskTicket +} = require('./task-h5-weixin.js') + +const TaskMapping = { + 'mp-weixin': { + 'accessToken': TaskAccessTokenMP + }, + 'h5-weixin': { + 'accessToken': TaskAccessTokenH5, + 'ticket': TaskTicket + } +} + +class ScheduleManager extends TaskManager { + + constructor() { + super() + } + + async runAll() { + for (let i = 0; i < this.tasks.length; i++) { + const task = this.tasks[i] + + try { + await task.run() + } catch (e) { + console.log("task.run::", e) + } + } + } + + newTask(T, config) { + const newTask = new T(config) + + super.addTask(newTask) + + return newTask + } +} + +ScheduleManager.instance = function() { + if (!ScheduleManager._instance) { + ScheduleManager._instance = new ScheduleManager() + } + return ScheduleManager._instance +} + +async function main() { + const openBridgeConfig = new OpenBridgeConfig() + + ScheduleManager.instance().clear() + + for (let taskConfig of openBridgeConfig.tasks) { + let { + platform, + tasks + } = taskConfig + + for (let taskName of tasks) { + const platformTask = TaskMapping[platform] + if (platformTask) { + ScheduleManager.instance().newTask(platformTask[taskName], taskConfig) + } + } + } + + await ScheduleManager.instance().runAll() +} + +module.exports = main; diff --git a/uni_modules/uni-open-bridge/uniCloud/cloudfunctions/uni-open-bridge/package.json b/uni_modules/uni-open-bridge/uniCloud/cloudfunctions/uni-open-bridge/package.json new file mode 100644 index 0000000..7aad3d9 --- /dev/null +++ b/uni_modules/uni-open-bridge/uniCloud/cloudfunctions/uni-open-bridge/package.json @@ -0,0 +1,10 @@ +{ + "name": "uni-open-bridge", + "dependencies": { + "uni-open-bridge-common": "file:../common/uni-open-bridge-common", + "uni-config-center": "file:../../../../uni-config-center/uniCloud/cloudfunctions/common/uni-config-center" + }, + "extensions": { + "uni-cloud-jql": {} + } +} \ No newline at end of file diff --git a/uni_modules/uni-open-bridge/uniCloud/cloudfunctions/uni-open-bridge/task-h5-weixin.js b/uni_modules/uni-open-bridge/uniCloud/cloudfunctions/uni-open-bridge/task-h5-weixin.js new file mode 100644 index 0000000..c5950ae --- /dev/null +++ b/uni_modules/uni-open-bridge/uniCloud/cloudfunctions/uni-open-bridge/task-h5-weixin.js @@ -0,0 +1,67 @@ +'use strict'; + +const { + getAccessToken, + setAccessToken, + removeAccessToken, + getTicket, + setTicket, + removeTicket +} = require('uni-open-bridge-common') + +const { + Task +} = require('./basic.js') + +const { + PlatformType +} = require('./consts.js') + +class TaskAccessTokenH5 extends Task { + + constructor(config) { + super() + + this._config = config || null + } + + async run() { + const key = { + dcloudAppid: this._config.dcloudAppid, + platform: PlatformType.H5_WEIXIN + } + + const result = await getAccessToken(key) + + console.log("setAccessToken...", key, result) + } +} + +TaskAccessTokenH5.ID = 'TaskAccessTokenH5' + +class TaskTicket extends Task { + + constructor(config) { + super() + + this._config = config || null + } + + async run() { + const key = { + dcloudAppid: this._config.dcloudAppid, + platform: PlatformType.H5_WEIXIN + } + + const result = await getTicket(key) + + console.log("setTicket...", key, result) + } +} + +TaskTicket.ID = 'TaskTicket' + +module.exports = { + TaskAccessTokenH5, + TaskTicket +} diff --git a/uni_modules/uni-open-bridge/uniCloud/cloudfunctions/uni-open-bridge/task-mp-weixin.js b/uni_modules/uni-open-bridge/uniCloud/cloudfunctions/uni-open-bridge/task-mp-weixin.js new file mode 100644 index 0000000..547aaa7 --- /dev/null +++ b/uni_modules/uni-open-bridge/uniCloud/cloudfunctions/uni-open-bridge/task-mp-weixin.js @@ -0,0 +1,41 @@ +'use strict'; + +const { + getAccessToken, + setAccessToken, + removeAccessToken +} = require('uni-open-bridge-common') + +const { + Task +} = require('./basic.js') + +const { + PlatformType +} = require('./consts.js') + +class TaskAccessTokenMP extends Task { + + constructor(config) { + super() + + this._config = config || null + } + + async run() { + const key = { + dcloudAppid: this._config.dcloudAppid, + platform: PlatformType.MP_WEIXIN + } + + const result = await getAccessToken(key) + + console.log("setAccessToken...", key, result) + } +} + +TaskAccessTokenMP.ID = 'TaskAccessTokenMP' + +module.exports = { + TaskAccessTokenMP +} diff --git a/uni_modules/uni-pagination/changelog.md b/uni_modules/uni-pagination/changelog.md new file mode 100644 index 0000000..2e94adc --- /dev/null +++ b/uni_modules/uni-pagination/changelog.md @@ -0,0 +1,27 @@ +## 1.2.4(2022-09-19) +- 修复,未对主题色设置默认色,导致未引入 uni-scss 变量文件报错。 +- 修复,未对移动端当前页文字做主题色适配。 +## 1.2.3(2022-09-15) +- 修复未使用 uni-scss 主题色的 bug。 +## 1.2.2(2022-07-06) +- 修复 es 语言 i18n 错误 +## 1.2.1(2021-11-22) +- 修复 vue3中某些scss变量无法找到的问题 +## 1.2.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-pagination](https://uniapp.dcloud.io/component/uniui/uni-pagination) +## 1.1.2(2021-10-08) +- 修复 current 、value 属性未监听,导致高亮样式失效的 bug +## 1.1.1(2021-08-20) +- 新增 支持国际化 +## 1.1.0(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.0.7(2021-05-12) +- 新增 组件示例地址 +## 1.0.6(2021-04-12) +- 新增 PC 和 移动端适配不同的 ui +## 1.0.5(2021-02-05) +- 优化 组件引用关系,通过uni_modules引用组件 + +## 1.0.4(2021-02-05) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-pagination/components/uni-pagination/i18n/en.json b/uni_modules/uni-pagination/components/uni-pagination/i18n/en.json new file mode 100644 index 0000000..d6e2897 --- /dev/null +++ b/uni_modules/uni-pagination/components/uni-pagination/i18n/en.json @@ -0,0 +1,5 @@ +{ + "uni-pagination.prevText": "prev", + "uni-pagination.nextText": "next", + "uni-pagination.piecePerPage": "piece/page" +} diff --git a/uni_modules/uni-pagination/components/uni-pagination/i18n/es.json b/uni_modules/uni-pagination/components/uni-pagination/i18n/es.json new file mode 100644 index 0000000..604a113 --- /dev/null +++ b/uni_modules/uni-pagination/components/uni-pagination/i18n/es.json @@ -0,0 +1,5 @@ +{ + "uni-pagination.prevText": "anterior", + "uni-pagination.nextText": "prxima", + "uni-pagination.piecePerPage": "Artculo/Pgina" +} diff --git a/uni_modules/uni-pagination/components/uni-pagination/i18n/fr.json b/uni_modules/uni-pagination/components/uni-pagination/i18n/fr.json new file mode 100644 index 0000000..a7a0c77 --- /dev/null +++ b/uni_modules/uni-pagination/components/uni-pagination/i18n/fr.json @@ -0,0 +1,5 @@ +{ + "uni-pagination.prevText": "précédente", + "uni-pagination.nextText": "suivante", + "uni-pagination.piecePerPage": "Articles/Pages" +} diff --git a/uni_modules/uni-pagination/components/uni-pagination/i18n/index.js b/uni_modules/uni-pagination/components/uni-pagination/i18n/index.js new file mode 100644 index 0000000..2469dd0 --- /dev/null +++ b/uni_modules/uni-pagination/components/uni-pagination/i18n/index.js @@ -0,0 +1,12 @@ +import en from './en.json' +import es from './es.json' +import fr from './fr.json' +import zhHans from './zh-Hans.json' +import zhHant from './zh-Hant.json' +export default { + en, + es, + fr, + 'zh-Hans': zhHans, + 'zh-Hant': zhHant +} diff --git a/uni_modules/uni-pagination/components/uni-pagination/i18n/zh-Hans.json b/uni_modules/uni-pagination/components/uni-pagination/i18n/zh-Hans.json new file mode 100644 index 0000000..782bbe4 --- /dev/null +++ b/uni_modules/uni-pagination/components/uni-pagination/i18n/zh-Hans.json @@ -0,0 +1,5 @@ +{ + "uni-pagination.prevText": "上一页", + "uni-pagination.nextText": "下一页", + "uni-pagination.piecePerPage": "条/页" +} diff --git a/uni_modules/uni-pagination/components/uni-pagination/i18n/zh-Hant.json b/uni_modules/uni-pagination/components/uni-pagination/i18n/zh-Hant.json new file mode 100644 index 0000000..180fddb --- /dev/null +++ b/uni_modules/uni-pagination/components/uni-pagination/i18n/zh-Hant.json @@ -0,0 +1,5 @@ +{ + "uni-pagination.prevText": "上一頁", + "uni-pagination.nextText": "下一頁", + "uni-pagination.piecePerPage": "條/頁" +} diff --git a/uni_modules/uni-pagination/components/uni-pagination/uni-pagination.vue b/uni_modules/uni-pagination/components/uni-pagination/uni-pagination.vue new file mode 100644 index 0000000..5305b5f --- /dev/null +++ b/uni_modules/uni-pagination/components/uni-pagination/uni-pagination.vue @@ -0,0 +1,465 @@ + + + + + diff --git a/uni_modules/uni-pagination/package.json b/uni_modules/uni-pagination/package.json new file mode 100644 index 0000000..862d5ab --- /dev/null +++ b/uni_modules/uni-pagination/package.json @@ -0,0 +1,83 @@ +{ + "id": "uni-pagination", + "displayName": "uni-pagination 分页器", + "version": "1.2.4", + "description": "Pagination 分页器组件,用于展示页码、请求数据等。", + "keywords": [ + "uni-ui", + "uniui", + "分页器", + "页码" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": ["uni-scss","uni-icons"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-pagination/readme.md b/uni_modules/uni-pagination/readme.md new file mode 100644 index 0000000..97ea1d6 --- /dev/null +++ b/uni_modules/uni-pagination/readme.md @@ -0,0 +1,11 @@ + + +## Pagination 分页器 +> **组件名:uni-pagination** +> 代码块: `uPagination` + + +分页器组件,用于展示页码、请求数据等。 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-pagination) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 diff --git a/uni_modules/uni-popup/changelog.md b/uni_modules/uni-popup/changelog.md new file mode 100644 index 0000000..decd775 --- /dev/null +++ b/uni_modules/uni-popup/changelog.md @@ -0,0 +1,84 @@ +## 1.9.1(2024-04-02) +- 修复 uni-popup-dialog vue3下使用value无法进行绑定的bug(双向绑定兼容旧写法) +## 1.9.0(2024-03-28) +- 修复 uni-popup-dialog 双向绑定时初始化逻辑修正 +## 1.8.9(2024-03-20) +- 修复 uni-popup-dialog 数据输入时修正为双向绑定 +## 1.8.8(2024-02-20) +- 修复 uni-popup 在微信小程序下出现文字向上闪动的bug +## 1.8.7(2024-02-02) +- 新增 uni-popup-dialog 新增属性focus:input模式下,是否自动自动聚焦 +## 1.8.6(2024-01-30) +- 新增 uni-popup-dialog 新增属性maxLength:限制输入框字数 +## 1.8.5(2024-01-26) +- 新增 uni-popup-dialog 新增属性showClose:控制关闭按钮的显示 +## 1.8.4(2023-11-15) +- 新增 uni-popup 支持uni-app-x 注意暂时仅支持 `maskClick` `@open` `@close` +## 1.8.3(2023-04-17) +- 修复 uni-popup 重复打开时的 bug +## 1.8.2(2023-02-02) +- uni-popup-dialog 组件新增 inputType 属性 +## 1.8.1(2022-12-01) +- 修复 nvue 下 v-show 报错 +## 1.8.0(2022-11-29) +- 优化 主题样式 +## 1.7.9(2022-04-02) +- 修复 弹出层内部无法滚动的bug +## 1.7.8(2022-03-28) +- 修复 小程序中高度错误的bug +## 1.7.7(2022-03-17) +- 修复 快速调用open出现问题的Bug +## 1.7.6(2022-02-14) +- 修复 safeArea 属性不能设置为false的bug +## 1.7.5(2022-01-19) +- 修复 isMaskClick 失效的bug +## 1.7.4(2022-01-19) +- 新增 cancelText \ confirmText 属性 ,可自定义文本 +- 新增 maskBackgroundColor 属性 ,可以修改蒙版颜色 +- 优化 maskClick属性 更新为 isMaskClick ,解决微信小程序警告的问题 +## 1.7.3(2022-01-13) +- 修复 设置 safeArea 属性不生效的bug +## 1.7.2(2021-11-26) +- 优化 组件示例 +## 1.7.1(2021-11-26) +- 修复 vuedoc 文字错误 +## 1.7.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-popup](https://uniapp.dcloud.io/component/uniui/uni-popup) +## 1.6.2(2021-08-24) +- 新增 支持国际化 +## 1.6.1(2021-07-30) +- 优化 vue3下事件警告的问题 +## 1.6.0(2021-07-13) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.5.0(2021-06-23) +- 新增 mask-click 遮罩层点击事件 +## 1.4.5(2021-06-22) +- 修复 nvue 平台中间弹出后,点击内容,再点击遮罩无法关闭的Bug +## 1.4.4(2021-06-18) +- 修复 H5平台中间弹出后,点击内容,再点击遮罩无法关闭的Bug +## 1.4.3(2021-06-08) +- 修复 错误的 watch 字段 +- 修复 safeArea 属性不生效的问题 +- 修复 点击内容,再点击遮罩无法关闭的Bug +## 1.4.2(2021-05-12) +- 新增 组件示例地址 +## 1.4.1(2021-04-29) +- 修复 组件内放置 input 、textarea 组件,无法聚焦的问题 +## 1.4.0 (2021-04-29) +- 新增 type 属性的 left\right 值,支持左右弹出 +- 新增 open(String:type) 方法参数 ,可以省略 type 属性 ,直接传入类型打开指定弹窗 +- 新增 backgroundColor 属性,可定义主窗口背景色,默认不显示背景色 +- 新增 safeArea 属性,是否适配底部安全区 +- 修复 App\h5\微信小程序底部安全区占位不对的Bug +- 修复 App 端弹出等待的Bug +- 优化 提升低配设备性能,优化动画卡顿问题 +- 优化 更简单的组件自定义方式 +## 1.2.9(2021-02-05) +- 优化 组件引用关系,通过uni_modules引用组件 +## 1.2.8(2021-02-05) +- 调整为uni_modules目录规范 +## 1.2.7(2021-02-05) +- 调整为uni_modules目录规范 +- 新增 支持 PC 端 +- 新增 uni-popup-message 、uni-popup-dialog扩展组件支持 PC 端 diff --git a/uni_modules/uni-popup/components/uni-popup-dialog/keypress.js b/uni_modules/uni-popup/components/uni-popup-dialog/keypress.js new file mode 100644 index 0000000..6ef26a2 --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup-dialog/keypress.js @@ -0,0 +1,45 @@ +// #ifdef H5 +export default { + name: 'Keypress', + props: { + disable: { + type: Boolean, + default: false + } + }, + mounted () { + const keyNames = { + esc: ['Esc', 'Escape'], + tab: 'Tab', + enter: 'Enter', + space: [' ', 'Spacebar'], + up: ['Up', 'ArrowUp'], + left: ['Left', 'ArrowLeft'], + right: ['Right', 'ArrowRight'], + down: ['Down', 'ArrowDown'], + delete: ['Backspace', 'Delete', 'Del'] + } + const listener = ($event) => { + if (this.disable) { + return + } + const keyName = Object.keys(keyNames).find(key => { + const keyName = $event.key + const value = keyNames[key] + return value === keyName || (Array.isArray(value) && value.includes(keyName)) + }) + if (keyName) { + // 避免和其他按键事件冲突 + setTimeout(() => { + this.$emit(keyName, {}) + }, 0) + } + } + document.addEventListener('keyup', listener) + this.$once('hook:beforeDestroy', () => { + document.removeEventListener('keyup', listener) + }) + }, + render: () => {} +} +// #endif diff --git a/uni_modules/uni-popup/components/uni-popup-dialog/uni-popup-dialog.vue b/uni_modules/uni-popup/components/uni-popup-dialog/uni-popup-dialog.vue new file mode 100644 index 0000000..08707d4 --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup-dialog/uni-popup-dialog.vue @@ -0,0 +1,316 @@ + + + + + diff --git a/uni_modules/uni-popup/components/uni-popup-message/uni-popup-message.vue b/uni_modules/uni-popup/components/uni-popup-message/uni-popup-message.vue new file mode 100644 index 0000000..91370a8 --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup-message/uni-popup-message.vue @@ -0,0 +1,143 @@ + + + + diff --git a/uni_modules/uni-popup/components/uni-popup-share/uni-popup-share.vue b/uni_modules/uni-popup/components/uni-popup-share/uni-popup-share.vue new file mode 100644 index 0000000..f7e667c --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup-share/uni-popup-share.vue @@ -0,0 +1,187 @@ + + + + diff --git a/uni_modules/uni-popup/components/uni-popup/i18n/en.json b/uni_modules/uni-popup/components/uni-popup/i18n/en.json new file mode 100644 index 0000000..7f1bd06 --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup/i18n/en.json @@ -0,0 +1,7 @@ +{ + "uni-popup.cancel": "cancel", + "uni-popup.ok": "ok", + "uni-popup.placeholder": "pleace enter", + "uni-popup.title": "Hint", + "uni-popup.shareTitle": "Share to" +} diff --git a/uni_modules/uni-popup/components/uni-popup/i18n/index.js b/uni_modules/uni-popup/components/uni-popup/i18n/index.js new file mode 100644 index 0000000..de7509c --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup/i18n/index.js @@ -0,0 +1,8 @@ +import en from './en.json' +import zhHans from './zh-Hans.json' +import zhHant from './zh-Hant.json' +export default { + en, + 'zh-Hans': zhHans, + 'zh-Hant': zhHant +} diff --git a/uni_modules/uni-popup/components/uni-popup/i18n/zh-Hans.json b/uni_modules/uni-popup/components/uni-popup/i18n/zh-Hans.json new file mode 100644 index 0000000..5e3003c --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup/i18n/zh-Hans.json @@ -0,0 +1,7 @@ +{ + "uni-popup.cancel": "取消", + "uni-popup.ok": "确定", + "uni-popup.placeholder": "请输入", + "uni-popup.title": "提示", + "uni-popup.shareTitle": "分享到" +} diff --git a/uni_modules/uni-popup/components/uni-popup/i18n/zh-Hant.json b/uni_modules/uni-popup/components/uni-popup/i18n/zh-Hant.json new file mode 100644 index 0000000..13e39eb --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup/i18n/zh-Hant.json @@ -0,0 +1,7 @@ +{ + "uni-popup.cancel": "取消", + "uni-popup.ok": "確定", + "uni-popup.placeholder": "請輸入", + "uni-popup.title": "提示", + "uni-popup.shareTitle": "分享到" +} diff --git a/uni_modules/uni-popup/components/uni-popup/keypress.js b/uni_modules/uni-popup/components/uni-popup/keypress.js new file mode 100644 index 0000000..62dda46 --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup/keypress.js @@ -0,0 +1,45 @@ +// #ifdef H5 +export default { + name: 'Keypress', + props: { + disable: { + type: Boolean, + default: false + } + }, + mounted () { + const keyNames = { + esc: ['Esc', 'Escape'], + tab: 'Tab', + enter: 'Enter', + space: [' ', 'Spacebar'], + up: ['Up', 'ArrowUp'], + left: ['Left', 'ArrowLeft'], + right: ['Right', 'ArrowRight'], + down: ['Down', 'ArrowDown'], + delete: ['Backspace', 'Delete', 'Del'] + } + const listener = ($event) => { + if (this.disable) { + return + } + const keyName = Object.keys(keyNames).find(key => { + const keyName = $event.key + const value = keyNames[key] + return value === keyName || (Array.isArray(value) && value.includes(keyName)) + }) + if (keyName) { + // 避免和其他按键事件冲突 + setTimeout(() => { + this.$emit(keyName, {}) + }, 0) + } + } + document.addEventListener('keyup', listener) + // this.$once('hook:beforeDestroy', () => { + // document.removeEventListener('keyup', listener) + // }) + }, + render: () => {} +} +// #endif diff --git a/uni_modules/uni-popup/components/uni-popup/message.js b/uni_modules/uni-popup/components/uni-popup/message.js new file mode 100644 index 0000000..0ff9a02 --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup/message.js @@ -0,0 +1,22 @@ +export default { + created() { + if (this.type === 'message') { + // 不显示遮罩 + this.maskShow = false + // 获取子组件对象 + this.childrenMsg = null + } + }, + methods: { + customOpen() { + if (this.childrenMsg) { + this.childrenMsg.open() + } + }, + customClose() { + if (this.childrenMsg) { + this.childrenMsg.close() + } + } + } +} diff --git a/uni_modules/uni-popup/components/uni-popup/popup.js b/uni_modules/uni-popup/components/uni-popup/popup.js new file mode 100644 index 0000000..c4e5781 --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup/popup.js @@ -0,0 +1,26 @@ + +export default { + data() { + return { + + } + }, + created(){ + this.popup = this.getParent() + }, + methods:{ + /** + * 获取父元素实例 + */ + getParent(name = 'uniPopup') { + let parent = this.$parent; + let parentName = parent.$options.name; + while (parentName !== name) { + parent = parent.$parent; + if (!parent) return false + parentName = parent.$options.name; + } + return parent; + }, + } +} diff --git a/uni_modules/uni-popup/components/uni-popup/share.js b/uni_modules/uni-popup/components/uni-popup/share.js new file mode 100644 index 0000000..462bb83 --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup/share.js @@ -0,0 +1,16 @@ +export default { + created() { + if (this.type === 'share') { + // 关闭点击 + this.mkclick = false + } + }, + methods: { + customOpen() { + console.log('share 打开了'); + }, + customClose() { + console.log('share 关闭了'); + } + } +} diff --git a/uni_modules/uni-popup/components/uni-popup/uni-popup.uvue b/uni_modules/uni-popup/components/uni-popup/uni-popup.uvue new file mode 100644 index 0000000..5eb8d5b --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup/uni-popup.uvue @@ -0,0 +1,90 @@ + + + + + \ No newline at end of file diff --git a/uni_modules/uni-popup/components/uni-popup/uni-popup.vue b/uni_modules/uni-popup/components/uni-popup/uni-popup.vue new file mode 100644 index 0000000..8349e99 --- /dev/null +++ b/uni_modules/uni-popup/components/uni-popup/uni-popup.vue @@ -0,0 +1,503 @@ + + + + diff --git a/uni_modules/uni-popup/package.json b/uni_modules/uni-popup/package.json new file mode 100644 index 0000000..3cfa384 --- /dev/null +++ b/uni_modules/uni-popup/package.json @@ -0,0 +1,88 @@ +{ + "id": "uni-popup", + "displayName": "uni-popup 弹出层", + "version": "1.9.1", + "description": " Popup 组件,提供常用的弹层", + "keywords": [ + "uni-ui", + "弹出层", + "弹窗", + "popup", + "弹框" + ], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": [ + "uni-scss", + "uni-transition" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "n" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} diff --git a/uni_modules/uni-popup/readme.md b/uni_modules/uni-popup/readme.md new file mode 100644 index 0000000..fdad4b3 --- /dev/null +++ b/uni_modules/uni-popup/readme.md @@ -0,0 +1,17 @@ + + +## Popup 弹出层 +> **组件名:uni-popup** +> 代码块: `uPopup` +> 关联组件:`uni-transition` + + +弹出层组件,在应用中弹出一个消息提示窗口、提示框等 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-popup) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 + + + + + diff --git a/uni_modules/uni-rate/changelog.md b/uni_modules/uni-rate/changelog.md new file mode 100644 index 0000000..8a98a61 --- /dev/null +++ b/uni_modules/uni-rate/changelog.md @@ -0,0 +1,25 @@ +## 1.3.1(2022-02-25) +- 修复 条件判断 `NaN` 错误的 bug +## 1.3.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-rate](https://uniapp.dcloud.io/component/uniui/uni-rate) +## 1.2.2(2021-09-10) +- 优化 默认值修改为 0 颗星 +## 1.2.1(2021-07-30) +- 优化 vue3下事件警告的问题 +## 1.2.0(2021-07-13) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.1.2(2021-05-12) +- 新增 组件示例地址 +## 1.1.1(2021-04-21) +- 修复 布局变化后 uni-rate 星星计算不准确的 bug +- 优化 添加依赖 uni-icons, 导入 uni-rate 自动下载依赖 +## 1.1.0(2021-04-16) +- 修复 uni-rate 属性 margin 值为 string 组件失效的 bug + +## 1.0.9(2021-02-05) +- 优化 组件引用关系,通过uni_modules引用组件 + +## 1.0.8(2021-02-05) +- 调整为uni_modules目录规范 +- 支持 pc 端 diff --git a/uni_modules/uni-rate/components/uni-rate/uni-rate.vue b/uni_modules/uni-rate/components/uni-rate/uni-rate.vue new file mode 100644 index 0000000..857f5f9 --- /dev/null +++ b/uni_modules/uni-rate/components/uni-rate/uni-rate.vue @@ -0,0 +1,361 @@ + + + + + diff --git a/uni_modules/uni-rate/package.json b/uni_modules/uni-rate/package.json new file mode 100644 index 0000000..64e8e33 --- /dev/null +++ b/uni_modules/uni-rate/package.json @@ -0,0 +1,88 @@ +{ + "id": "uni-rate", + "displayName": "uni-rate 评分", + "version": "1.3.1", + "description": "Rate 评分组件,可自定义评分星星图标的大小、间隔、评分数。", + "keywords": [ + "uni-ui", + "uniui", + "评分" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": [ + "uni-scss", + "uni-icons" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} diff --git a/uni_modules/uni-rate/readme.md b/uni_modules/uni-rate/readme.md new file mode 100644 index 0000000..eae7b5c --- /dev/null +++ b/uni_modules/uni-rate/readme.md @@ -0,0 +1,12 @@ + + +## Rate 评分 +> **组件名:uni-rate** +> 代码块: `uRate` +> 关联组件:`uni-icons` + + +评分组件,多用于购买商品后,对商品进行评价等场景 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-rate) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 \ No newline at end of file diff --git a/uni_modules/uni-row/changelog.md b/uni_modules/uni-row/changelog.md new file mode 100644 index 0000000..5b465bc --- /dev/null +++ b/uni_modules/uni-row/changelog.md @@ -0,0 +1,10 @@ +## 1.0.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-row](https://uniapp.dcloud.io/component/uniui/uni-row) +## 0.1.0(2021-07-13) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 0.0.4(2021-05-12) +- 新增 组件示例地址 +## 0.0.3(2021-02-05) +- 调整为uni_modules目录规范 +- 新增uni-row组件 diff --git a/uni_modules/uni-row/components/uni-col/uni-col.vue b/uni_modules/uni-row/components/uni-col/uni-col.vue new file mode 100644 index 0000000..84e2deb --- /dev/null +++ b/uni_modules/uni-row/components/uni-col/uni-col.vue @@ -0,0 +1,317 @@ + + + + + diff --git a/uni_modules/uni-row/components/uni-row/uni-row.vue b/uni_modules/uni-row/components/uni-row/uni-row.vue new file mode 100644 index 0000000..f8e8542 --- /dev/null +++ b/uni_modules/uni-row/components/uni-row/uni-row.vue @@ -0,0 +1,190 @@ + + + + + diff --git a/uni_modules/uni-row/package.json b/uni_modules/uni-row/package.json new file mode 100644 index 0000000..3f52fa6 --- /dev/null +++ b/uni_modules/uni-row/package.json @@ -0,0 +1,87 @@ +{ + "id": "uni-row", + "displayName": "uni-row 布局-行", + "version": "1.0.0", + "description": "流式栅格系统,随着屏幕或视口分为 24 份,可以迅速简便地创建布局。", + "keywords": [ + "uni-ui", + "uniui", + "栅格", + "布局", + "layout" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": ["uni-scss"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "u" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-row/readme.md b/uni_modules/uni-row/readme.md new file mode 100644 index 0000000..3c9c8b9 --- /dev/null +++ b/uni_modules/uni-row/readme.md @@ -0,0 +1,10 @@ +## Layout 布局 + +> **组件名 uni-row、uni-col** +> 代码块: `uRow`、`uCol` + + +流式栅格系统,随着屏幕或视口分为 24 份,可以迅速简便地创建布局。 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-row) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 \ No newline at end of file diff --git a/uni_modules/uni-scss/changelog.md b/uni_modules/uni-scss/changelog.md new file mode 100644 index 0000000..b863bb0 --- /dev/null +++ b/uni_modules/uni-scss/changelog.md @@ -0,0 +1,8 @@ +## 1.0.3(2022-01-21) +- 优化 组件示例 +## 1.0.2(2021-11-22) +- 修复 / 符号在 vue 不同版本兼容问题引起的报错问题 +## 1.0.1(2021-11-22) +- 修复 vue3中scss语法兼容问题 +## 1.0.0(2021-11-18) +- init diff --git a/uni_modules/uni-scss/index.scss b/uni_modules/uni-scss/index.scss new file mode 100644 index 0000000..1744a5f --- /dev/null +++ b/uni_modules/uni-scss/index.scss @@ -0,0 +1 @@ +@import './styles/index.scss'; diff --git a/uni_modules/uni-scss/package.json b/uni_modules/uni-scss/package.json new file mode 100644 index 0000000..7cc0ccb --- /dev/null +++ b/uni_modules/uni-scss/package.json @@ -0,0 +1,82 @@ +{ + "id": "uni-scss", + "displayName": "uni-scss 辅助样式", + "version": "1.0.3", + "description": "uni-sass是uni-ui提供的一套全局样式 ,通过一些简单的类名和sass变量,实现简单的页面布局操作,比如颜色、边距、圆角等。", + "keywords": [ + "uni-scss", + "uni-ui", + "辅助样式" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "^3.1.0" + }, + "dcloudext": { + "category": [ + "JS SDK", + "通用 SDK" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "u" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "n", + "联盟": "n" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} diff --git a/uni_modules/uni-scss/readme.md b/uni_modules/uni-scss/readme.md new file mode 100644 index 0000000..b7d1c25 --- /dev/null +++ b/uni_modules/uni-scss/readme.md @@ -0,0 +1,4 @@ +`uni-sass` 是 `uni-ui`提供的一套全局样式 ,通过一些简单的类名和`sass`变量,实现简单的页面布局操作,比如颜色、边距、圆角等。 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-sass) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 \ No newline at end of file diff --git a/uni_modules/uni-scss/styles/index.scss b/uni_modules/uni-scss/styles/index.scss new file mode 100644 index 0000000..ffac4fe --- /dev/null +++ b/uni_modules/uni-scss/styles/index.scss @@ -0,0 +1,7 @@ +@import './setting/_variables.scss'; +@import './setting/_border.scss'; +@import './setting/_color.scss'; +@import './setting/_space.scss'; +@import './setting/_radius.scss'; +@import './setting/_text.scss'; +@import './setting/_styles.scss'; diff --git a/uni_modules/uni-scss/styles/setting/_border.scss b/uni_modules/uni-scss/styles/setting/_border.scss new file mode 100644 index 0000000..12a11c3 --- /dev/null +++ b/uni_modules/uni-scss/styles/setting/_border.scss @@ -0,0 +1,3 @@ +.uni-border { + border: 1px $uni-border-1 solid; +} \ No newline at end of file diff --git a/uni_modules/uni-scss/styles/setting/_color.scss b/uni_modules/uni-scss/styles/setting/_color.scss new file mode 100644 index 0000000..1ededd9 --- /dev/null +++ b/uni_modules/uni-scss/styles/setting/_color.scss @@ -0,0 +1,66 @@ + +// TODO 暂时不需要 class ,需要用户使用变量实现 ,如果使用类名其实并不推荐 +// @mixin get-styles($k,$c) { +// @if $k == size or $k == weight{ +// font-#{$k}:#{$c} +// }@else{ +// #{$k}:#{$c} +// } +// } +$uni-ui-color:( + // 主色 + primary: $uni-primary, + primary-disable: $uni-primary-disable, + primary-light: $uni-primary-light, + // 辅助色 + success: $uni-success, + success-disable: $uni-success-disable, + success-light: $uni-success-light, + warning: $uni-warning, + warning-disable: $uni-warning-disable, + warning-light: $uni-warning-light, + error: $uni-error, + error-disable: $uni-error-disable, + error-light: $uni-error-light, + info: $uni-info, + info-disable: $uni-info-disable, + info-light: $uni-info-light, + // 中性色 + main-color: $uni-main-color, + base-color: $uni-base-color, + secondary-color: $uni-secondary-color, + extra-color: $uni-extra-color, + // 背景色 + bg-color: $uni-bg-color, + // 边框颜色 + border-1: $uni-border-1, + border-2: $uni-border-2, + border-3: $uni-border-3, + border-4: $uni-border-4, + // 黑色 + black:$uni-black, + // 白色 + white:$uni-white, + // 透明 + transparent:$uni-transparent +) !default; +@each $key, $child in $uni-ui-color { + .uni-#{"" + $key} { + color: $child; + } + .uni-#{"" + $key}-bg { + background-color: $child; + } +} +.uni-shadow-sm { + box-shadow: $uni-shadow-sm; +} +.uni-shadow-base { + box-shadow: $uni-shadow-base; +} +.uni-shadow-lg { + box-shadow: $uni-shadow-lg; +} +.uni-mask { + background-color:$uni-mask; +} diff --git a/uni_modules/uni-scss/styles/setting/_radius.scss b/uni_modules/uni-scss/styles/setting/_radius.scss new file mode 100644 index 0000000..9a0428b --- /dev/null +++ b/uni_modules/uni-scss/styles/setting/_radius.scss @@ -0,0 +1,55 @@ +@mixin radius($r,$d:null ,$important: false){ + $radius-value:map-get($uni-radius, $r) if($important, !important, null); + // Key exists within the $uni-radius variable + @if (map-has-key($uni-radius, $r) and $d){ + @if $d == t { + border-top-left-radius:$radius-value; + border-top-right-radius:$radius-value; + }@else if $d == r { + border-top-right-radius:$radius-value; + border-bottom-right-radius:$radius-value; + }@else if $d == b { + border-bottom-left-radius:$radius-value; + border-bottom-right-radius:$radius-value; + }@else if $d == l { + border-top-left-radius:$radius-value; + border-bottom-left-radius:$radius-value; + }@else if $d == tl { + border-top-left-radius:$radius-value; + }@else if $d == tr { + border-top-right-radius:$radius-value; + }@else if $d == br { + border-bottom-right-radius:$radius-value; + }@else if $d == bl { + border-bottom-left-radius:$radius-value; + } + }@else{ + border-radius:$radius-value; + } +} + +@each $key, $child in $uni-radius { + @if($key){ + .uni-radius-#{"" + $key} { + @include radius($key) + } + }@else{ + .uni-radius { + @include radius($key) + } + } +} + +@each $direction in t, r, b, l,tl, tr, br, bl { + @each $key, $child in $uni-radius { + @if($key){ + .uni-radius-#{"" + $direction}-#{"" + $key} { + @include radius($key,$direction,false) + } + }@else{ + .uni-radius-#{$direction} { + @include radius($key,$direction,false) + } + } + } +} diff --git a/uni_modules/uni-scss/styles/setting/_space.scss b/uni_modules/uni-scss/styles/setting/_space.scss new file mode 100644 index 0000000..3c89528 --- /dev/null +++ b/uni_modules/uni-scss/styles/setting/_space.scss @@ -0,0 +1,56 @@ + +@mixin fn($space,$direction,$size,$n) { + @if $n { + #{$space}-#{$direction}: #{$size*$uni-space-root}px + } @else { + #{$space}-#{$direction}: #{-$size*$uni-space-root}px + } +} +@mixin get-styles($direction,$i,$space,$n){ + @if $direction == t { + @include fn($space, top,$i,$n); + } + @if $direction == r { + @include fn($space, right,$i,$n); + } + @if $direction == b { + @include fn($space, bottom,$i,$n); + } + @if $direction == l { + @include fn($space, left,$i,$n); + } + @if $direction == x { + @include fn($space, left,$i,$n); + @include fn($space, right,$i,$n); + } + @if $direction == y { + @include fn($space, top,$i,$n); + @include fn($space, bottom,$i,$n); + } + @if $direction == a { + @if $n { + #{$space}:#{$i*$uni-space-root}px; + } @else { + #{$space}:#{-$i*$uni-space-root}px; + } + } +} + +@each $orientation in m,p { + $space: margin; + @if $orientation == m { + $space: margin; + } @else { + $space: padding; + } + @for $i from 0 through 16 { + @each $direction in t, r, b, l, x, y, a { + .uni-#{$orientation}#{$direction}-#{$i} { + @include get-styles($direction,$i,$space,true); + } + .uni-#{$orientation}#{$direction}-n#{$i} { + @include get-styles($direction,$i,$space,false); + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-scss/styles/setting/_styles.scss b/uni_modules/uni-scss/styles/setting/_styles.scss new file mode 100644 index 0000000..689afec --- /dev/null +++ b/uni_modules/uni-scss/styles/setting/_styles.scss @@ -0,0 +1,167 @@ +/* #ifndef APP-NVUE */ + +$-color-white:#fff; +$-color-black:#000; +@mixin base-style($color) { + color: #fff; + background-color: $color; + border-color: mix($-color-black, $color, 8%); + &:not([hover-class]):active { + background: mix($-color-black, $color, 10%); + border-color: mix($-color-black, $color, 20%); + color: $-color-white; + outline: none; + } +} +@mixin is-color($color) { + @include base-style($color); + &[loading] { + @include base-style($color); + &::before { + margin-right:5px; + } + } + &[disabled] { + &, + &[loading], + &:not([hover-class]):active { + color: $-color-white; + border-color: mix(darken($color,10%), $-color-white); + background-color: mix($color, $-color-white); + } + } + +} +@mixin base-plain-style($color) { + color:$color; + background-color: mix($-color-white, $color, 90%); + border-color: mix($-color-white, $color, 70%); + &:not([hover-class]):active { + background: mix($-color-white, $color, 80%); + color: $color; + outline: none; + border-color: mix($-color-white, $color, 50%); + } +} +@mixin is-plain($color){ + &[plain] { + @include base-plain-style($color); + &[loading] { + @include base-plain-style($color); + &::before { + margin-right:5px; + } + } + &[disabled] { + &, + &:active { + color: mix($-color-white, $color, 40%); + background-color: mix($-color-white, $color, 90%); + border-color: mix($-color-white, $color, 80%); + } + } + } +} + + +.uni-btn { + margin: 5px; + color: #393939; + border:1px solid #ccc; + font-size: 16px; + font-weight: 200; + background-color: #F9F9F9; + // TODO 暂时处理边框隐藏一边的问题 + overflow: visible; + &::after{ + border: none; + } + + &:not([type]),&[type=default] { + color: #999; + &[loading] { + background: none; + &::before { + margin-right:5px; + } + } + + + + &[disabled]{ + color: mix($-color-white, #999, 60%); + &, + &[loading], + &:active { + color: mix($-color-white, #999, 60%); + background-color: mix($-color-white,$-color-black , 98%); + border-color: mix($-color-white, #999, 85%); + } + } + + &[plain] { + color: #999; + background: none; + border-color: $uni-border-1; + &:not([hover-class]):active { + background: none; + color: mix($-color-white, $-color-black, 80%); + border-color: mix($-color-white, $-color-black, 90%); + outline: none; + } + &[disabled]{ + &, + &[loading], + &:active { + background: none; + color: mix($-color-white, #999, 60%); + border-color: mix($-color-white, #999, 85%); + } + } + } + } + + &:not([hover-class]):active { + color: mix($-color-white, $-color-black, 50%); + } + + &[size=mini] { + font-size: 16px; + font-weight: 200; + border-radius: 8px; + } + + + + &.uni-btn-small { + font-size: 14px; + } + &.uni-btn-mini { + font-size: 12px; + } + + &.uni-btn-radius { + border-radius: 999px; + } + &[type=primary] { + @include is-color($uni-primary); + @include is-plain($uni-primary) + } + &[type=success] { + @include is-color($uni-success); + @include is-plain($uni-success) + } + &[type=error] { + @include is-color($uni-error); + @include is-plain($uni-error) + } + &[type=warning] { + @include is-color($uni-warning); + @include is-plain($uni-warning) + } + &[type=info] { + @include is-color($uni-info); + @include is-plain($uni-info) + } +} +/* #endif */ diff --git a/uni_modules/uni-scss/styles/setting/_text.scss b/uni_modules/uni-scss/styles/setting/_text.scss new file mode 100644 index 0000000..a34d08f --- /dev/null +++ b/uni_modules/uni-scss/styles/setting/_text.scss @@ -0,0 +1,24 @@ +@mixin get-styles($k,$c) { + @if $k == size or $k == weight{ + font-#{$k}:#{$c} + }@else{ + #{$k}:#{$c} + } +} + +@each $key, $child in $uni-headings { + /* #ifndef APP-NVUE */ + .uni-#{$key} { + @each $k, $c in $child { + @include get-styles($k,$c) + } + } + /* #endif */ + /* #ifdef APP-NVUE */ + .container .uni-#{$key} { + @each $k, $c in $child { + @include get-styles($k,$c) + } + } + /* #endif */ +} diff --git a/uni_modules/uni-scss/styles/setting/_variables.scss b/uni_modules/uni-scss/styles/setting/_variables.scss new file mode 100644 index 0000000..557d3d7 --- /dev/null +++ b/uni_modules/uni-scss/styles/setting/_variables.scss @@ -0,0 +1,146 @@ +// @use "sass:math"; +@import '../tools/functions.scss'; +// 间距基础倍数 +$uni-space-root: 2 !default; +// 边框半径默认值 +$uni-radius-root:5px !default; +$uni-radius: () !default; +// 边框半径断点 +$uni-radius: map-deep-merge( + ( + 0: 0, + // TODO 当前版本暂时不支持 sm 属性 + // 'sm': math.div($uni-radius-root, 2), + null: $uni-radius-root, + 'lg': $uni-radius-root * 2, + 'xl': $uni-radius-root * 6, + 'pill': 9999px, + 'circle': 50% + ), + $uni-radius +); +// 字体家族 +$body-font-family: 'Roboto', sans-serif !default; +// 文本 +$heading-font-family: $body-font-family !default; +$uni-headings: () !default; +$letterSpacing: -0.01562em; +$uni-headings: map-deep-merge( + ( + 'h1': ( + size: 32px, + weight: 300, + line-height: 50px, + // letter-spacing:-0.01562em + ), + 'h2': ( + size: 28px, + weight: 300, + line-height: 40px, + // letter-spacing: -0.00833em + ), + 'h3': ( + size: 24px, + weight: 400, + line-height: 32px, + // letter-spacing: normal + ), + 'h4': ( + size: 20px, + weight: 400, + line-height: 30px, + // letter-spacing: 0.00735em + ), + 'h5': ( + size: 16px, + weight: 400, + line-height: 24px, + // letter-spacing: normal + ), + 'h6': ( + size: 14px, + weight: 500, + line-height: 18px, + // letter-spacing: 0.0125em + ), + 'subtitle': ( + size: 12px, + weight: 400, + line-height: 20px, + // letter-spacing: 0.00937em + ), + 'body': ( + font-size: 14px, + font-weight: 400, + line-height: 22px, + // letter-spacing: 0.03125em + ), + 'caption': ( + 'size': 12px, + 'weight': 400, + 'line-height': 20px, + // 'letter-spacing': 0.03333em, + // 'text-transform': false + ) + ), + $uni-headings +); + + + +// 主色 +$uni-primary: #2979ff !default; +$uni-primary-disable:lighten($uni-primary,20%) !default; +$uni-primary-light: lighten($uni-primary,25%) !default; + +// 辅助色 +// 除了主色外的场景色,需要在不同的场景中使用(例如危险色表示危险的操作)。 +$uni-success: #18bc37 !default; +$uni-success-disable:lighten($uni-success,20%) !default; +$uni-success-light: lighten($uni-success,25%) !default; + +$uni-warning: #f3a73f !default; +$uni-warning-disable:lighten($uni-warning,20%) !default; +$uni-warning-light: lighten($uni-warning,25%) !default; + +$uni-error: #e43d33 !default; +$uni-error-disable:lighten($uni-error,20%) !default; +$uni-error-light: lighten($uni-error,25%) !default; + +$uni-info: #8f939c !default; +$uni-info-disable:lighten($uni-info,20%) !default; +$uni-info-light: lighten($uni-info,25%) !default; + +// 中性色 +// 中性色用于文本、背景和边框颜色。通过运用不同的中性色,来表现层次结构。 +$uni-main-color: #3a3a3a !default; // 主要文字 +$uni-base-color: #6a6a6a !default; // 常规文字 +$uni-secondary-color: #909399 !default; // 次要文字 +$uni-extra-color: #c7c7c7 !default; // 辅助说明 + +// 边框颜色 +$uni-border-1: #F0F0F0 !default; +$uni-border-2: #EDEDED !default; +$uni-border-3: #DCDCDC !default; +$uni-border-4: #B9B9B9 !default; + +// 常规色 +$uni-black: #000000 !default; +$uni-white: #ffffff !default; +$uni-transparent: rgba($color: #000000, $alpha: 0) !default; + +// 背景色 +$uni-bg-color: #f7f7f7 !default; + +/* 水平间距 */ +$uni-spacing-sm: 8px !default; +$uni-spacing-base: 15px !default; +$uni-spacing-lg: 30px !default; + +// 阴影 +$uni-shadow-sm:0 0 5px rgba($color: #d8d8d8, $alpha: 0.5) !default; +$uni-shadow-base:0 1px 8px 1px rgba($color: #a5a5a5, $alpha: 0.2) !default; +$uni-shadow-lg:0px 1px 10px 2px rgba($color: #a5a4a4, $alpha: 0.5) !default; + +// 蒙版 +$uni-mask: rgba($color: #000000, $alpha: 0.4) !default; diff --git a/uni_modules/uni-scss/styles/tools/functions.scss b/uni_modules/uni-scss/styles/tools/functions.scss new file mode 100644 index 0000000..ac6f63e --- /dev/null +++ b/uni_modules/uni-scss/styles/tools/functions.scss @@ -0,0 +1,19 @@ +// 合并 map +@function map-deep-merge($parent-map, $child-map){ + $result: $parent-map; + @each $key, $child in $child-map { + $parent-has-key: map-has-key($result, $key); + $parent-value: map-get($result, $key); + $parent-type: type-of($parent-value); + $child-type: type-of($child); + $parent-is-map: $parent-type == map; + $child-is-map: $child-type == map; + + @if (not $parent-has-key) or ($parent-type != $child-type) or (not ($parent-is-map and $child-is-map)){ + $result: map-merge($result, ( $key: $child )); + }@else { + $result: map-merge($result, ( $key: map-deep-merge($parent-value, $child) )); + } + } + @return $result; +}; diff --git a/uni_modules/uni-scss/theme.scss b/uni_modules/uni-scss/theme.scss new file mode 100644 index 0000000..80ee62f --- /dev/null +++ b/uni_modules/uni-scss/theme.scss @@ -0,0 +1,31 @@ +// 间距基础倍数 +$uni-space-root: 2; +// 边框半径默认值 +$uni-radius-root:5px; +// 主色 +$uni-primary: #2979ff; +// 辅助色 +$uni-success: #4cd964; +// 警告色 +$uni-warning: #f0ad4e; +// 错误色 +$uni-error: #dd524d; +// 描述色 +$uni-info: #909399; +// 中性色 +$uni-main-color: #303133; +$uni-base-color: #606266; +$uni-secondary-color: #909399; +$uni-extra-color: #C0C4CC; +// 背景色 +$uni-bg-color: #f5f5f5; +// 边框颜色 +$uni-border-1: #DCDFE6; +$uni-border-2: #E4E7ED; +$uni-border-3: #EBEEF5; +$uni-border-4: #F2F6FC; + +// 常规色 +$uni-black: #000000; +$uni-white: #ffffff; +$uni-transparent: rgba($color: #000000, $alpha: 0); diff --git a/uni_modules/uni-scss/variables.scss b/uni_modules/uni-scss/variables.scss new file mode 100644 index 0000000..1c062d4 --- /dev/null +++ b/uni_modules/uni-scss/variables.scss @@ -0,0 +1,62 @@ +@import './styles/setting/_variables.scss'; +// 间距基础倍数 +$uni-space-root: 2; +// 边框半径默认值 +$uni-radius-root:5px; + +// 主色 +$uni-primary: #2979ff; +$uni-primary-disable:mix(#fff,$uni-primary,50%); +$uni-primary-light: mix(#fff,$uni-primary,80%); + +// 辅助色 +// 除了主色外的场景色,需要在不同的场景中使用(例如危险色表示危险的操作)。 +$uni-success: #18bc37; +$uni-success-disable:mix(#fff,$uni-success,50%); +$uni-success-light: mix(#fff,$uni-success,80%); + +$uni-warning: #f3a73f; +$uni-warning-disable:mix(#fff,$uni-warning,50%); +$uni-warning-light: mix(#fff,$uni-warning,80%); + +$uni-error: #e43d33; +$uni-error-disable:mix(#fff,$uni-error,50%); +$uni-error-light: mix(#fff,$uni-error,80%); + +$uni-info: #8f939c; +$uni-info-disable:mix(#fff,$uni-info,50%); +$uni-info-light: mix(#fff,$uni-info,80%); + +// 中性色 +// 中性色用于文本、背景和边框颜色。通过运用不同的中性色,来表现层次结构。 +$uni-main-color: #3a3a3a; // 主要文字 +$uni-base-color: #6a6a6a; // 常规文字 +$uni-secondary-color: #909399; // 次要文字 +$uni-extra-color: #c7c7c7; // 辅助说明 + +// 边框颜色 +$uni-border-1: #F0F0F0; +$uni-border-2: #EDEDED; +$uni-border-3: #DCDCDC; +$uni-border-4: #B9B9B9; + +// 常规色 +$uni-black: #000000; +$uni-white: #ffffff; +$uni-transparent: rgba($color: #000000, $alpha: 0); + +// 背景色 +$uni-bg-color: #f7f7f7; + +/* 水平间距 */ +$uni-spacing-sm: 8px; +$uni-spacing-base: 15px; +$uni-spacing-lg: 30px; + +// 阴影 +$uni-shadow-sm:0 0 5px rgba($color: #d8d8d8, $alpha: 0.5); +$uni-shadow-base:0 1px 8px 1px rgba($color: #a5a5a5, $alpha: 0.2); +$uni-shadow-lg:0px 1px 10px 2px rgba($color: #a5a4a4, $alpha: 0.5); + +// 蒙版 +$uni-mask: rgba($color: #000000, $alpha: 0.4); diff --git a/uni_modules/uni-search-bar/changelog.md b/uni_modules/uni-search-bar/changelog.md new file mode 100644 index 0000000..2c6571c --- /dev/null +++ b/uni_modules/uni-search-bar/changelog.md @@ -0,0 +1,47 @@ +## 1.3.0(2024-04-22) +- 修复 textColor默认值导致的文字不显示的bug +## 1.2.9(2024-04-17) +- 修复 textColor不生效的bug +## 1.2.8(2024-02-22) +- 修复 清空按钮emit值错误的bug +## 1.2.7(2024-02-21) +- 新增 设置输入框字体颜色:textColor +## 1.2.6(2024-02-20) +- 修复 uni-search-bar在支付宝小程序下样式兼容问题 +## 1.2.5(2024-01-31) +- 修复 uni-search-bar居中问题,现在默认居左,并修复样式偏移问题 +## 1.2.4(2023-05-09) +- 修复 i18n 国际化不正确的 Bug +## 1.2.3(2022-05-24) +- 新增 readonly 属性,组件只读 +## 1.2.2(2022-05-06) +- 修复 vue3 input 事件不生效的bug +## 1.2.1(2022-05-06) +- 修复 多余代码导致的bug +## 1.2.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-search-bar](https://uniapp.dcloud.io/component/uniui/uni-search-bar) +## 1.1.2(2021-08-30) +- 修复 value 属性与 modelValue 属性不兼容的Bug +## 1.1.1(2021-08-24) +- 新增 支持国际化 +## 1.1.0(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.0.9(2021-05-12) +- 新增 项目示例地址 +## 1.0.8(2021-04-21) +- 优化 添加依赖 uni-icons, 导入后自动下载依赖 +## 1.0.7(2021-04-15) +- uni-ui 新增 uni-search-bar 的 focus 事件 + +## 1.0.6(2021-02-05) +- 优化 组件引用关系,通过uni_modules引用组件 + +## 1.0.5(2021-02-05) +- 调整为uni_modules目录规范 +- 新增 支持双向绑定 +- 更改 input 事件的返回值,e={value:Number} --> e=value +- 新增 支持图标插槽 +- 新增 支持 clear、blur 事件 +- 新增 支持 focus 属性 +- 去掉组件背景色 diff --git a/uni_modules/uni-search-bar/components/uni-search-bar/i18n/en.json b/uni_modules/uni-search-bar/components/uni-search-bar/i18n/en.json new file mode 100644 index 0000000..dd083a5 --- /dev/null +++ b/uni_modules/uni-search-bar/components/uni-search-bar/i18n/en.json @@ -0,0 +1,4 @@ +{ + "uni-search-bar.cancel": "cancel", + "uni-search-bar.placeholder": "Search enter content" +} \ No newline at end of file diff --git a/uni_modules/uni-search-bar/components/uni-search-bar/i18n/index.js b/uni_modules/uni-search-bar/components/uni-search-bar/i18n/index.js new file mode 100644 index 0000000..de7509c --- /dev/null +++ b/uni_modules/uni-search-bar/components/uni-search-bar/i18n/index.js @@ -0,0 +1,8 @@ +import en from './en.json' +import zhHans from './zh-Hans.json' +import zhHant from './zh-Hant.json' +export default { + en, + 'zh-Hans': zhHans, + 'zh-Hant': zhHant +} diff --git a/uni_modules/uni-search-bar/components/uni-search-bar/i18n/zh-Hans.json b/uni_modules/uni-search-bar/components/uni-search-bar/i18n/zh-Hans.json new file mode 100644 index 0000000..d2a1ced --- /dev/null +++ b/uni_modules/uni-search-bar/components/uni-search-bar/i18n/zh-Hans.json @@ -0,0 +1,4 @@ +{ + "uni-search-bar.cancel": "取消", + "uni-search-bar.placeholder": "请输入搜索内容" +} diff --git a/uni_modules/uni-search-bar/components/uni-search-bar/i18n/zh-Hant.json b/uni_modules/uni-search-bar/components/uni-search-bar/i18n/zh-Hant.json new file mode 100644 index 0000000..f1c96bc --- /dev/null +++ b/uni_modules/uni-search-bar/components/uni-search-bar/i18n/zh-Hant.json @@ -0,0 +1,4 @@ +{ + "uni-search-bar.cancel": "取消", + "uni-search-bar.placeholder": "請輸入搜索內容" +} diff --git a/uni_modules/uni-search-bar/components/uni-search-bar/uni-search-bar.vue b/uni_modules/uni-search-bar/components/uni-search-bar/uni-search-bar.vue new file mode 100644 index 0000000..6b9b9c1 --- /dev/null +++ b/uni_modules/uni-search-bar/components/uni-search-bar/uni-search-bar.vue @@ -0,0 +1,309 @@ + + + + + diff --git a/uni_modules/uni-search-bar/package.json b/uni_modules/uni-search-bar/package.json new file mode 100644 index 0000000..1730d9d --- /dev/null +++ b/uni_modules/uni-search-bar/package.json @@ -0,0 +1,87 @@ +{ + "id": "uni-search-bar", + "displayName": "uni-search-bar 搜索栏", + "version": "1.3.0", + "description": "搜索栏组件,通常用于搜索商品、文章等", + "keywords": [ + "uni-ui", + "uniui", + "搜索框", + "搜索栏" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": [ + "uni-scss", + "uni-icons" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "n" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-search-bar/readme.md b/uni_modules/uni-search-bar/readme.md new file mode 100644 index 0000000..253092f --- /dev/null +++ b/uni_modules/uni-search-bar/readme.md @@ -0,0 +1,14 @@ + + +## SearchBar 搜索栏 + +> **组件名:uni-search-bar** +> 代码块: `uSearchBar` + + +搜索栏组件 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-search-bar) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 + + diff --git a/uni_modules/uni-section/changelog.md b/uni_modules/uni-section/changelog.md new file mode 100644 index 0000000..738f2b3 --- /dev/null +++ b/uni_modules/uni-section/changelog.md @@ -0,0 +1,2 @@ +## 0.0.1(2022-07-22) +- 初始化 diff --git a/uni_modules/uni-section/components/uni-section/uni-section.vue b/uni_modules/uni-section/components/uni-section/uni-section.vue new file mode 100644 index 0000000..9a52e0b --- /dev/null +++ b/uni_modules/uni-section/components/uni-section/uni-section.vue @@ -0,0 +1,167 @@ + + + + diff --git a/uni_modules/uni-section/package.json b/uni_modules/uni-section/package.json new file mode 100644 index 0000000..0a31fb5 --- /dev/null +++ b/uni_modules/uni-section/package.json @@ -0,0 +1,87 @@ +{ + "id": "uni-section", + "displayName": "uni-section 标题栏", + "version": "0.0.1", + "description": "标题栏组件", + "keywords": [ + "uni-ui", + "uniui", + "标题栏" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": [ + "uni-scss" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-section/readme.md b/uni_modules/uni-section/readme.md new file mode 100644 index 0000000..d47faab --- /dev/null +++ b/uni_modules/uni-section/readme.md @@ -0,0 +1,8 @@ +## Section 标题栏 +> **组件名:uni-section** +> 代码块: `uSection` + +uni-section 组件主要用于文章、列表详情等标题展示 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-section) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 diff --git a/uni_modules/uni-segmented-control/changelog.md b/uni_modules/uni-segmented-control/changelog.md new file mode 100644 index 0000000..02d0c8a --- /dev/null +++ b/uni_modules/uni-segmented-control/changelog.md @@ -0,0 +1,15 @@ +## 1.2.3(2024-04-02) +- 修复 修复在微信小程序下inactiveColor失效bug +## 1.2.2(2024-03-28) +- 修复 在vue2下:style动态绑定导致编译失败的bug +## 1.2.1(2024-03-20) +- 新增 inActiveColor属性,可供配置未激活时的颜色 +## 1.2.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-segmented-control](https://uniapp.dcloud.io/component/uniui/uni-segmented-control) +## 1.1.0(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.0.5(2021-05-12) +- 新增 项目示例地址 +## 1.0.4(2021-02-05) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-segmented-control/components/uni-segmented-control/uni-segmented-control.vue b/uni_modules/uni-segmented-control/components/uni-segmented-control/uni-segmented-control.vue new file mode 100644 index 0000000..a69366a --- /dev/null +++ b/uni_modules/uni-segmented-control/components/uni-segmented-control/uni-segmented-control.vue @@ -0,0 +1,146 @@ + + + + + diff --git a/uni_modules/uni-segmented-control/package.json b/uni_modules/uni-segmented-control/package.json new file mode 100644 index 0000000..49f9eff --- /dev/null +++ b/uni_modules/uni-segmented-control/package.json @@ -0,0 +1,85 @@ +{ + "id": "uni-segmented-control", + "displayName": "uni-segmented-control 分段器", + "version": "1.2.3", + "description": "分段器由至少 2 个分段控件组成,用作不同视图的显示", + "keywords": [ + "uni-ui", + "uniui", + "分段器", + "segement", + "顶部选择" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": ["uni-scss"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "n" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-segmented-control/readme.md b/uni_modules/uni-segmented-control/readme.md new file mode 100644 index 0000000..3527b03 --- /dev/null +++ b/uni_modules/uni-segmented-control/readme.md @@ -0,0 +1,13 @@ + + +## SegmentedControl 分段器 +> **组件名:uni-segmented-control** +> 代码块: `uSegmentedControl` + + +用作不同视图的显示 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-segmented-control) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 + + diff --git a/uni_modules/uni-share/changelog.md b/uni_modules/uni-share/changelog.md new file mode 100644 index 0000000..c858c67 --- /dev/null +++ b/uni_modules/uni-share/changelog.md @@ -0,0 +1,18 @@ +## 2.0.2(2021-12-16) +修复在某些情况下报:`Cannot read property 'hide' of undefined`的问题 +## 2.0.1(2021-11-29) +修改错误的scene值WXSenceTimeline(分享到朋友圈)更正为WXSceneTimeline +## 2.0.0(2021-10-14) +支持监听返回操作(如:物理返回,全面屏手机侧滑)关闭分享弹窗 +## 1.0.6(2021-08-25) +兼容vue3 +## 1.0.5(2021-08-05) +优化代码实现,修改原来用`eval()`函数实现的逻辑 +## 1.0.4(2021-06-07) +为符合苹果应用市场的审核,只显示存在对应的分享客户端的选项。如:配置包含微信分享,但是用户手机上并没有安装微信,就不显示微信分享。 +## 1.0.2(2021-05-06) +修复错误的提示:“打包时未添加oauth模块” +## 1.0.1(2021-04-30) +新增完整示例 +## 1.0.0(2021-04-28) +第1版发布 diff --git a/uni_modules/uni-share/js_sdk/uni-image-menu.js b/uni_modules/uni-share/js_sdk/uni-image-menu.js new file mode 100644 index 0000000..c03e51b --- /dev/null +++ b/uni_modules/uni-share/js_sdk/uni-image-menu.js @@ -0,0 +1,204 @@ +var nvMask, nvImageMenu; +class NvImageMenu { + constructor(arg) { + this.isShow = false + } + show({ + list, + cancelText + }, callback) { + if (!list) { + list = [{ + "img": "/static/sharemenu/wechatfriend.png", + "text": "图标文字" + }] + } + //以下为计算菜单的nview绘制布局,为固定算法,使用者无关关心 + var screenWidth = plus.screen.resolutionWidth + //以360px宽度屏幕为例,上下左右边距及2排按钮边距留25像素,图标宽度55像素,同行图标间的间距在360宽的屏幕是30px,但需要动态计算,以此原则计算4列图标分别的left位置 + //图标下的按钮文字距离图标5像素,文字大小12像素 + //底部取消按钮高度固定为44px + //TODO 未处理横屏和pad,这些情况6个图标应该一排即可 + var margin = 20, + iconWidth = 60, + icontextSpace = 5, + textHeight = 12 + var left1 = margin / 360 * screenWidth + var iconSpace = (screenWidth - (left1 * 2) - (iconWidth * 4)) / 3 //屏幕宽度减去左右留白间距,再减去4个图标的宽度,就是3个同行图标的间距 + if (iconSpace <= 5) { //屏幕过窄时,缩小边距和图标大小,再算一次 + margin = 15 + iconWidth = 40 + left1 = margin / 360 * screenWidth + iconSpace = (screenWidth - (left1 * 2) - (iconWidth * 4)) / 3 //屏幕宽度减去左右留白间距,再减去4个图标的宽度,就是3个同行图标的间距 + } + var left2 = left1 + iconWidth + iconSpace + var left3 = left1 + (iconWidth + iconSpace) * 2 + var left4 = left1 + (iconWidth + iconSpace) * 3 + var top1 = left1 + var top2 = top1 + iconWidth + icontextSpace + textHeight + left1 + + const TOP = { + top1, + top2 + }, + LEFT = { + left1, + left2, + left3, + left4 + }; + + nvMask = new plus.nativeObj.View("nvMask", { //先创建遮罩层 + top: '0px', + left: '0px', + height: '100%', + width: '100%', + backgroundColor: 'rgba(0,0,0,0.2)' + }); + nvImageMenu = new plus.nativeObj.View("nvImageMenu", { //创建底部图标菜单 + bottom: '0px', + left: '0px', + height: (iconWidth + textHeight + 2 * margin) * Math.ceil(list.length / 4) + 44 + + 'px', //'264px', + width: '100%', + backgroundColor: 'rgb(255,255,255)' + }); + nvMask.addEventListener("click", () => { //处理遮罩层点击 + // console.log('处理遮罩层点击'); + this.hide() + callback({ + event: "clickMask" + }) + }) + let myList = [] + list.forEach((item, i) => { + myList.push({ + tag: 'img', + src: item.img, + position: { + top: TOP['top' + (parseInt(i / 4) + 1)], + left: LEFT['left' + (1 + i % 4)], + width: iconWidth, + height: iconWidth + } + }) + myList.push({ + tag: 'font', + text: item.text, + textStyles: { + size: textHeight + }, + position: { + top: TOP['top' + (parseInt(i / 4) + 1)] + iconWidth + icontextSpace, + left: LEFT['left' + (1 + i % 4)], + width: iconWidth, + height: textHeight + } + }) + }) + + //绘制底部图标菜单的内容 + nvImageMenu.draw([{ + tag: 'rect', //菜单顶部的分割灰线 + color: '#e7e7e7', + position: { + top: '0px', + height: '1px' + } + }, + { + tag: 'font', + text: cancelText, //底部取消按钮的文字 + textStyles: { + size: '14px' + }, + position: { + bottom: '0px', + height: '44px' + } + }, + { + tag: 'rect', //底部取消按钮的顶部边线 + color: '#e7e7e7', + position: { + bottom: '45px', + height: '1px' + } + }, + ...myList + ]) + nvMask.show() + nvImageMenu.show() + // 开始动画 + /* + plus.nativeObj.View.startAnimation({ + type: 'slide-in-bottom', + duration: 300 + }, nvImageMenu, {}, function() { + console.log('plus.nativeObj.View.startAnimation动画结束'); + // 关闭原生动画 + plus.nativeObj.View.clearAnimation(); + nvImageMenu.show() + }); + */ + + + this.isShow = true + nvImageMenu.addEventListener("click", e => { //处理底部图标菜单的点击事件,根据点击位置触发不同的逻辑 + // console.log("click menu"+JSON.stringify(e)); + if (e.screenY > plus.screen.resolutionHeight - 44) { //点击了底部取消按钮 + // callback({event:"clickCancelButton"}) + this.hide() + } else if (e.clientX < 5 || e.clientX > screenWidth - 5 || e.clientY < 5) { + //屏幕左右边缘5像素及菜单顶部5像素不处理点击 + } else { //点击了图标按钮 + var iClickIndex = -1 //点击的图标按钮序号,第一个图标按钮的index为0 + var iRow = e.clientY < (top2 - (left1 / 2)) ? 0 : 1 + var iCol = -1 + if (e.clientX < (left2 - (iconSpace / 2))) { + iCol = 0 + } else if (e.clientX < (left3 - (iconSpace / 2))) { + iCol = 1 + } else if (e.clientX < (left4 - (iconSpace / 2))) { + iCol = 2 + } else { + iCol = 3 + } + if (iRow == 0) { + iClickIndex = iCol + } else { + iClickIndex = iCol + 4 + } + // console.log("点击按钮的序号: " + iClickIndex); + // if (iClickIndex >= 0 && iClickIndex <= 5) { //处理具体的点击逻辑,此处也可以自行定义逻辑。如果增减了按钮,此处也需要跟着修改 + // } + callback({ + event: "clickMenu", + index: iClickIndex + }) + } + }) + /* nvImageMenu.addEventListener("touchstart", function(e) { + if (e.screenY > (plus.screen.resolutionHeight - 44)) { + //TODO 这里可以处理按下背景变灰的效果 + } + }) + nvImageMenu.addEventListener("touchmove", function(e) { + //TODO 这里可以处理按下背景变灰的效果 + if (e.screenY > plus.screen.resolutionHeight - 44) {} + }) + nvImageMenu.addEventListener("touchend", function(e) { + //TODO 这里可以处理释放背景恢复的效果 + }) + */ + } + + hide() { + if (this.isShow) { + nvMask.hide() + nvImageMenu.hide() + this.isShow = false + } + } +} +export default NvImageMenu \ No newline at end of file diff --git a/uni_modules/uni-share/js_sdk/uni-share.js b/uni_modules/uni-share/js_sdk/uni-share.js new file mode 100644 index 0000000..8af8631 --- /dev/null +++ b/uni_modules/uni-share/js_sdk/uni-share.js @@ -0,0 +1,98 @@ +import UniImageMenu from './uni-image-menu.js'; +class UniShare extends UniImageMenu{ + constructor(arg) { + super() + this.isShow = super.isShow + } + async show(param, callback){ + var menus = [] + plus.share.getServices(services => { //只显示有服务的项目 + services = services.filter(item => item.nativeClient) + let servicesList = services.map(e => e.id) + param.menus.forEach(item => { + if (servicesList.includes(item.share.provider) || typeof(item.share) == 'string') { + menus.push(item) + } + }) + super.show({ + list: menus, + cancelText: param.cancelText + }, e => { + callback(e) + if(e.event == 'clickMenu'){ + if (typeof(menus[e.index]['share']) == 'string') { + this[menus[e.index]['share']](param) + } else { + uni.share({ + ...param.content, + ...menus[e.index].share, + success: res=> { + console.log("success:" + JSON.stringify(res)); + super.hide() + }, + fail: function(err) { + console.log("fail:" + JSON.stringify(err)); + uni.showModal({ + content: JSON.stringify(err), + showCancel: false, + confirmText: "知道了" + }); + } + }) + } + } + }) + }, err => { + uni.showModal({ + title: '获取服务供应商失败:' + JSON.stringify(err), + showCancel: false, + confirmText: '知道了' + }); + console.error('获取服务供应商失败:' + JSON.stringify(err)); + }) + } + hide(){ + super.hide() + } + copyurl(param) { + console.log('copyurl',param); + uni.setClipboardData({ + data: param.content.href, + success: ()=>{ + console.log('success'); + uni.hideToast() //关闭自带的toast + uni.showToast({ + title: '复制成功', + icon: 'none' + }); + super.hide(); + }, + fail: (err) => { + uni.showModal({ + content: JSON.stringify(err), + showCancel: false + }); + } + }); + } + // 使用系统分享发送分享消息 + shareSystem(param) { + console.log('shareSystem',param); + plus.share.sendWithSystem({ + type: 'text', + content: param.content.title + param.content.summary || '', + href: param.content.href, + }, (e)=> { + console.log('分享成功'); + super.hide() + }, (err)=> { + console.log('分享失败:' + JSON.stringify(err)); + uni.showModal({ + title: '获取服务供应商失败:' + JSON.stringify(err), + showCancel: false, + confirmText: '知道了' + }); + }); + } +} +export default UniShare \ No newline at end of file diff --git a/uni_modules/uni-share/package.json b/uni_modules/uni-share/package.json new file mode 100644 index 0000000..fa43a0e --- /dev/null +++ b/uni_modules/uni-share/package.json @@ -0,0 +1,80 @@ +{ + "id": "uni-share", + "displayName": "uni-share", + "version": "2.0.2", + "description": "底部弹出宫格图标式的分享菜单,可覆盖原生组件。", + "keywords": [ + "分享菜单" +], + "repository": "", + "engines": { + "HBuilderX": "^3.1.0" + }, + "dcloudext": { + "category": [ + "JS SDK", + "通用 SDK" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "n", + "Android Browser": "n", + "微信浏览器(Android)": "n", + "QQ浏览器(Android)": "n" + }, + "H5-pc": { + "Chrome": "n", + "IE": "n", + "Edge": "n", + "Firefox": "n", + "Safari": "n" + }, + "小程序": { + "微信": "n", + "阿里": "n", + "百度": "n", + "字节跳动": "n", + "QQ": "n" + }, + "快应用": { + "华为": "n", + "联盟": "n" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-share/readme.md b/uni_modules/uni-share/readme.md new file mode 100644 index 0000000..dced6d6 --- /dev/null +++ b/uni_modules/uni-share/readme.md @@ -0,0 +1,95 @@ +#### 本功能基于[底部图标菜单](https://ext.dcloud.net.cn/plugin?id=4858)封装而成。 +### 示例代码 +``` + + + +``` \ No newline at end of file diff --git a/uni_modules/uni-sign-in/changelog.md b/uni_modules/uni-sign-in/changelog.md new file mode 100644 index 0000000..5a0c9bb --- /dev/null +++ b/uni_modules/uni-sign-in/changelog.md @@ -0,0 +1,16 @@ +## 1.0.7(2024-04-30) +更新 组件uni-sign-in的style添加scoped +## 1.0.6(2024-03-26) +支持支付宝小程序云 +## 1.0.5(2021-12-09) +修复插件没自动安装依赖的`uni-popup`和`uni-icons`组件的问题 +## 1.0.4(2021-11-29) +修复在某些情况下,签到不连续7天,也获得60积分的问题 +## 1.0.3(2021-11-20) +新增支持看激励视频广告签到 +## 1.0.2(2021-08-25) +修复时区问题 +## 1.0.1(2021-08-23) +调整签到逻辑,支持查出积分的收入支出历史记录 +## 1.0.0(2021-08-05) +1.0.0版本发布 diff --git a/uni_modules/uni-sign-in/components/uni-sign-in/uni-sign-in.vue b/uni_modules/uni-sign-in/components/uni-sign-in/uni-sign-in.vue new file mode 100644 index 0000000..ffda7a5 --- /dev/null +++ b/uni_modules/uni-sign-in/components/uni-sign-in/uni-sign-in.vue @@ -0,0 +1,310 @@ + + + + + diff --git a/uni_modules/uni-sign-in/package.json b/uni_modules/uni-sign-in/package.json new file mode 100644 index 0000000..f55cdc0 --- /dev/null +++ b/uni_modules/uni-sign-in/package.json @@ -0,0 +1,82 @@ +{ + "id": "uni-sign-in", + "displayName": "uni-starter签到插件 提升留存,激励视频变现", + "version": "1.0.7", + "description": "培养用户习惯,提升用户粘性,支持广告流量变现的签到得积分功能", + "keywords": [ + "uni-sign-in", + "签到", + "营销", + "变现", + "积分" +], + "repository": "", + "engines": { + "HBuilderX": "^3.1.0" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "", + "type": "unicloud-template-page" + }, + "uni_modules": { + "dependencies": ["uni-popup","uni-icons"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "u" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "u", + "微信浏览器(Android)": "u", + "QQ浏览器(Android)": "u" + }, + "H5-pc": { + "Chrome": "y", + "IE": "u", + "Edge": "u", + "Firefox": "u", + "Safari": "u" + }, + "小程序": { + "微信": "y", + "阿里": "u", + "百度": "u", + "字节跳动": "u", + "QQ": "u" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-sign-in/pages/demo/demo.vue b/uni_modules/uni-sign-in/pages/demo/demo.vue new file mode 100644 index 0000000..1131d3c --- /dev/null +++ b/uni_modules/uni-sign-in/pages/demo/demo.vue @@ -0,0 +1,15 @@ + + \ No newline at end of file diff --git a/uni_modules/uni-sign-in/readme.md b/uni_modules/uni-sign-in/readme.md new file mode 100644 index 0000000..98225a1 --- /dev/null +++ b/uni_modules/uni-sign-in/readme.md @@ -0,0 +1,80 @@ +#### 简介:培养用户习惯,提升用户粘性,支持广告流量变现的签到得积分功能。 +#### 功能支持: +1. 每日签到奖励 (支持:普通签到、看广告签到) +2. 周期性连续7日签到,奖励翻倍 + +### 使用看广告签到功能必读 +1.`普通签到`是通过clientDb实现,如果你要使用`看广告签到`的方式, + 为了防止刷量需要修改`opendb-sign-in.schema`中`permission` -> `create` 的值设置为`false` + +> 文件路径 :`uni_modules/uni-sign-in/uniCloud/database/opendb-sign-in.schema.json` + +示例: + +```javascript +{ + "bsonType": "object", + "required": [], + "permission": { + "read": "auth.uid == doc.user_id", + "create": false, + "update": false, + "delete": false + } +} +``` + +2. 你需要看激励视频广告相关文档 +详情:[https://uniapp.dcloud.net.cn/api/a-d/rewarded-video](https://uniapp.dcloud.net.cn/api/a-d/rewarded-video) + +##### 使用方式 + +```js + + +``` + +> 详情参考[uni-starter](https://ext.dcloud.net.cn/plugin?id=5057) + + +##### 插件组成 +1. 前端组件 + + + +2. `DB Schema`表结构, + - 描述签到表字段及含义以及读写权限。 + - 路径:`/uniCloud/database/opendb-sign-in.schema.json` +> 更多表结构说明详情:[https://uniapp.dcloud.io/uniCloud/schema](https://uniapp.dcloud.io/uniCloud/schema) + +3. `uni-clientDB-actions` 一个可编程的 `clientDB` 前置后置操作 + - 前置操作,添加操作时检查今日是否未签到,否则拦截 + - 后置操作,判断是否已经连续签到7天,决定本次签到用户可得积分 + - 后置操作,输出本轮已签到几天,当前积分,已签到的日期数组,本轮签到可得多少分 +4. 两个api接口 + 普通签到`this.$refs.signIn.open()` + 看激励视频广告签到`this.$refs.signIn.showRewardedVideoAd()` + +#### 常见问题 +1. 是否支持配置积分数 + + 答:暂不支持,今后的版本有计划支持 + +2. 有没有更多玩法 + + 答:计划今后推出 + (2.1)需要看广告才能签到 --- 已支持 + (2.2)补签的玩法 \ No newline at end of file diff --git a/uni_modules/uni-sign-in/static/background.png b/uni_modules/uni-sign-in/static/background.png new file mode 100644 index 0000000000000000000000000000000000000000..c17b7ee15b8cadd798efbc1aa306a716ded4a06b GIT binary patch literal 30068 zcmd3Nby!sGz9=D~ga}BZgdjCc&oG2Yhkytw9Rmy@F#`-RAV_xz(gG?VDw5JMw1g6( zD5Z2rN(qRl>yF?4zJ2z&XYX_NeeV6^4$qort#|#p-&u=TLjx_AQ+%hWsHj+UwAGEM zsHm+ehYKSuWo03*Qi-zO>vG+cXsWL#Z;yAE#X8{aaIz$K0_8pxm7+3N+KpmeTY6m4d?8l9pHsC4luZGAK+?_aS&0yB&0}^rx0+*5wSuf zcQ+4jd6JUIA9Up@=f9W1B0_(F5M7l-RDLrQGSxQ}Qp0=Ugpjf*kUbP3CxphxLQx0^ z93>+JgFrE0h#VLS2SK3n5R^O;E%fI{gu>0s!BO5wUGq;~lq)3>XCjdx4+i`B`N{gh zW$|84U?>Jdp#cNKU?2(v$lKq8h$VqMyhZ<_ppNsl_i`Z+UGN@4zbRtv@IFK(5sIXL zQ^B3^cd{Pdf0~J6Ffa*A07GRVzg7AJ(82!ia0DMOw?CLW*n@FyICq=}(VGGb{W~nd z8BfG}JLCTa>A&CpPYfug)z|+!kAEtQyZhf+coQ{zDQf&_kbes8echjc0~_JI@jhPm zI1OKln4*7a<4rWe{f9mO4Ri|lAIJoGH7^{Ni1)gV$GiRIWy8Pp6;f0C!>d9P`dE7x zkKaa+{LS)DESx%)h*J`w_+1VJg@T}{>rj|HTuvT>k%mydA%6qa$2+(<`u_zA`yCu0 z80I=uP9BPsha>(IC?)6|uteH53$sLV;ki&_Bf0*O%Av@FrqC>~T8kN+J|p zWnEkx>~k@of=I08#?fg=J1!rD8+K#n*Z1_#B#F-WMx@5K2J&VNx+q3GvLal7}Q ziK&Wp`t!`qqBJ0Ka5)tiMqL#FRe`F&P!Kp;T@#}DUokLnmr{314s^Ij|SnO2pmWbhqMDhP%uY(IR_-l-U0Kkv0~(sfvWCX~?0V zFens?Qu`-<_)A|55@%=ch^B=-yc0|6Xu8EW#d(l|z7_7)l;N z?I=lrh1yfn3Qmb+I1YsA z0>{CyI1CI5qNs$SRC{|AC7+QxQLdjYX41FC2k%yw>q5sFK z{2rwKbIbjk5&j*G?C~DHIIn+*ECSXGOBv>HUfxO~j$U|oAuNI5=33hy4D>pu=GyNCy-a20}YRu^_+}tqHJ+~pZ>p~%0D)3K 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 EADV1ZBme*a literal 0 HcmV?d00001 diff --git a/uni_modules/uni-sign-in/uniCloud/cloudfunctions/common/sign-in/index.js b/uni_modules/uni-sign-in/uniCloud/cloudfunctions/common/sign-in/index.js new file mode 100644 index 0000000..1cd87bd --- /dev/null +++ b/uni_modules/uni-sign-in/uniCloud/cloudfunctions/common/sign-in/index.js @@ -0,0 +1,106 @@ +// 开发文档:https://uniapp.dcloud.io/uniCloud/clientdb?id=action +const db = uniCloud.database(); +const dbCmd = db.command +const signInTable = db.collection('opendb-sign-in'); +const scoresTable = db.collection('uni-id-scores'); +module.exports = async function({user_id,ip}) { + let date = todayTimestamp() + let { + total + } = await signInTable.where({ + user_id, + date, + isDelete: false + }).count() + console.log(total); + if (total) { + throw new Error("今天已经签到") + } + // state.newData.date = date + // state.newData.isDelete = false + + await signInTable.add({ + ip, + date, + user_id, + create_date:Date.now(), + isDelete:false, + }) + + + // after ------------------------- + //查最近7天的签到情况 + let { + data: signInData + } = await signInTable.where({ + user_id, + date: dbCmd.gte(date - 3600 * 24 * 6 * 1000), + isDelete: false + }).get() + + let allDate = signInData.map(item => item.date) + + //今天是本轮签到的第几天 + const n = (date - Math.min(...allDate)) / 3600 / 24 / 1000 + 1; + //换成数字--第几天 + let days = signInData.map(item => { + return (n * 10000 - (date - item.date) / 3600 / 24 / 1000 * 10000) / 10000 - 1 + }) + + //查出来用户当前有多少积分 + let { + data: [userScore] + } = await scoresTable + .where({ + user_id + }) + .orderBy("create_date", "desc") + .limit(1) + .get() + let balance = 0 + if (userScore) { + balance = userScore.balance + } + + if (n == 7) { //如果已经满一轮就软删除之前的内容 + let setIsDeleteRes = await signInTable.where({ + user_id, + date: dbCmd.neq(date) + }).update({ + isDelete: true + }) + console.log({ + setIsDeleteRes + }); + } + //给加积分 + let score = n+days.length==14?60:10 //如果连续签到7天就多加50分,也就是60分 + balance += score + let addScores = await scoresTable.add({ + user_id, + balance, + score, + type: 1, + create_date: Date.now() + }) + console.log({ + addScores + }); + return { + score: balance, + signInData, + n, + days + } +} + +function todayTimestamp() { + //时区 + let timeZone = new Date().getTimezoneOffset() / 60 + //获得相对于北京时间的时间戳 + let timestamp = Date.now() + 3600 * 1000 * (8 + timeZone) + //一天一共多少毫秒 + const D = 3600 * 24 * 1000 + //去掉余数,再减去东8区的8小时 得到当天凌晨的时间戳 + return parseInt(timestamp / D) * D - 3600 * 1000 * 8 +} diff --git a/uni_modules/uni-sign-in/uniCloud/cloudfunctions/common/sign-in/package.json b/uni_modules/uni-sign-in/uniCloud/cloudfunctions/common/sign-in/package.json new file mode 100644 index 0000000..7cdbf1d --- /dev/null +++ b/uni_modules/uni-sign-in/uniCloud/cloudfunctions/common/sign-in/package.json @@ -0,0 +1,12 @@ +{ + "name": "sign-in", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC" +} diff --git a/uni_modules/uni-sign-in/uniCloud/cloudfunctions/rewarded-video-ad-notify-url/index.js b/uni_modules/uni-sign-in/uniCloud/cloudfunctions/rewarded-video-ad-notify-url/index.js new file mode 100644 index 0000000..969eef5 --- /dev/null +++ b/uni_modules/uni-sign-in/uniCloud/cloudfunctions/rewarded-video-ad-notify-url/index.js @@ -0,0 +1,64 @@ +'use strict'; +const uniADConfig = require('uni-config-center')({ + pluginId: 'uni-ad' +}).config() +const signIn = require('sign-in') +let ip = null +async function nextFn(data) { + //写自己的业务逻辑 + switch (data.extra) { + case "uniSignIn": //签到 + let { + user_id + } = data; + await signIn({ + user_id, + ip + }) + break; + default: + break; + } + return { + "isValid": true //如果不返回,广点通会2次调用本云函数 + } +} + +const crypto = require('crypto'); +const db = uniCloud.database(); +exports.main = async (event, context) => { + ip = context.CLIENTIP; + //event为客户端上传的参数 + console.log('event : ', event); + const { + path, + queryStringParameters + } = event; + const data = { + adpid: event.adpid, + platform: event.platform, + provider: event.provider, + trans_id: event.trans_id, + sign: event.sign, + user_id: event.user_id, + extra: event.extra, + } + // 注意::必须验签请求来源 + const trans_id = event.trans_id; + //去uni-config-center通过adpid获取secret + const secret = uniADConfig[event.adpid] + const sign2 = crypto.createHash('sha256').update(`${secret}:${trans_id}`).digest('hex'); + if (event.sign !== sign2) { + console.log('验签失败'); + return null; + } + //自己的逻辑 + try { + return await nextFn(data) + } catch (e) { + console.error(e) + return { + "isValid": false + } + } +}; \ No newline at end of file diff --git a/uni_modules/uni-sign-in/uniCloud/cloudfunctions/rewarded-video-ad-notify-url/node_modules/sign-in/index.js b/uni_modules/uni-sign-in/uniCloud/cloudfunctions/rewarded-video-ad-notify-url/node_modules/sign-in/index.js new file mode 100644 index 0000000..1cd87bd --- /dev/null +++ b/uni_modules/uni-sign-in/uniCloud/cloudfunctions/rewarded-video-ad-notify-url/node_modules/sign-in/index.js @@ -0,0 +1,106 @@ +// 开发文档:https://uniapp.dcloud.io/uniCloud/clientdb?id=action +const db = uniCloud.database(); +const dbCmd = db.command +const signInTable = db.collection('opendb-sign-in'); +const scoresTable = db.collection('uni-id-scores'); +module.exports = async function({user_id,ip}) { + let date = todayTimestamp() + let { + total + } = await signInTable.where({ + user_id, + date, + isDelete: false + }).count() + console.log(total); + if (total) { + throw new Error("今天已经签到") + } + // state.newData.date = date + // state.newData.isDelete = false + + await signInTable.add({ + ip, + date, + user_id, + create_date:Date.now(), + isDelete:false, + }) + + + // after ------------------------- + //查最近7天的签到情况 + let { + data: signInData + } = await signInTable.where({ + user_id, + date: dbCmd.gte(date - 3600 * 24 * 6 * 1000), + isDelete: false + }).get() + + let allDate = signInData.map(item => item.date) + + //今天是本轮签到的第几天 + const n = (date - Math.min(...allDate)) / 3600 / 24 / 1000 + 1; + //换成数字--第几天 + let days = signInData.map(item => { + return (n * 10000 - (date - item.date) / 3600 / 24 / 1000 * 10000) / 10000 - 1 + }) + + //查出来用户当前有多少积分 + let { + data: [userScore] + } = await scoresTable + .where({ + user_id + }) + .orderBy("create_date", "desc") + .limit(1) + .get() + let balance = 0 + if (userScore) { + balance = userScore.balance + } + + if (n == 7) { //如果已经满一轮就软删除之前的内容 + let setIsDeleteRes = await signInTable.where({ + user_id, + date: dbCmd.neq(date) + }).update({ + isDelete: true + }) + console.log({ + setIsDeleteRes + }); + } + //给加积分 + let score = n+days.length==14?60:10 //如果连续签到7天就多加50分,也就是60分 + balance += score + let addScores = await scoresTable.add({ + user_id, + balance, + score, + type: 1, + create_date: Date.now() + }) + console.log({ + addScores + }); + return { + score: balance, + signInData, + n, + days + } +} + +function todayTimestamp() { + //时区 + let timeZone = new Date().getTimezoneOffset() / 60 + //获得相对于北京时间的时间戳 + let timestamp = Date.now() + 3600 * 1000 * (8 + timeZone) + //一天一共多少毫秒 + const D = 3600 * 24 * 1000 + //去掉余数,再减去东8区的8小时 得到当天凌晨的时间戳 + return parseInt(timestamp / D) * D - 3600 * 1000 * 8 +} diff --git a/uni_modules/uni-sign-in/uniCloud/cloudfunctions/rewarded-video-ad-notify-url/node_modules/sign-in/package.json b/uni_modules/uni-sign-in/uniCloud/cloudfunctions/rewarded-video-ad-notify-url/node_modules/sign-in/package.json new file mode 100644 index 0000000..7cdbf1d --- /dev/null +++ b/uni_modules/uni-sign-in/uniCloud/cloudfunctions/rewarded-video-ad-notify-url/node_modules/sign-in/package.json @@ -0,0 +1,12 @@ +{ + "name": "sign-in", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC" +} diff --git a/uni_modules/uni-sign-in/uniCloud/cloudfunctions/rewarded-video-ad-notify-url/node_modules/uni-config-center/index.js b/uni_modules/uni-sign-in/uniCloud/cloudfunctions/rewarded-video-ad-notify-url/node_modules/uni-config-center/index.js new file mode 100644 index 0000000..e14fb3b --- /dev/null +++ b/uni_modules/uni-sign-in/uniCloud/cloudfunctions/rewarded-video-ad-notify-url/node_modules/uni-config-center/index.js @@ -0,0 +1 @@ +"use strict";var t=require("fs"),r=require("path");function e(t){return t&&"object"==typeof t&&"default"in t?t:{default:t}}var n=e(t),o=e(r),i="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};var u=function(t){var r={exports:{}};return t(r,r.exports),r.exports}((function(t,r){var e="__lodash_hash_undefined__",n=9007199254740991,o="[object Arguments]",u="[object Function]",c="[object Object]",a=/^\[object .+?Constructor\]$/,f=/^(?:0|[1-9]\d*)$/,s={};s["[object Float32Array]"]=s["[object Float64Array]"]=s["[object Int8Array]"]=s["[object Int16Array]"]=s["[object Int32Array]"]=s["[object Uint8Array]"]=s["[object Uint8ClampedArray]"]=s["[object Uint16Array]"]=s["[object Uint32Array]"]=!0,s[o]=s["[object Array]"]=s["[object ArrayBuffer]"]=s["[object Boolean]"]=s["[object DataView]"]=s["[object Date]"]=s["[object Error]"]=s[u]=s["[object Map]"]=s["[object Number]"]=s[c]=s["[object RegExp]"]=s["[object Set]"]=s["[object String]"]=s["[object WeakMap]"]=!1;var l="object"==typeof i&&i&&i.Object===Object&&i,h="object"==typeof self&&self&&self.Object===Object&&self,p=l||h||Function("return this")(),_=r&&!r.nodeType&&r,v=_&&t&&!t.nodeType&&t,d=v&&v.exports===_,y=d&&l.process,g=function(){try{var t=v&&v.require&&v.require("util").types;return t||y&&y.binding&&y.binding("util")}catch(t){}}(),b=g&&g.isTypedArray;function j(t,r,e){switch(e.length){case 0:return t.call(r);case 1:return t.call(r,e[0]);case 2:return t.call(r,e[0],e[1]);case 3:return t.call(r,e[0],e[1],e[2])}return t.apply(r,e)}var w,O,m,A=Array.prototype,z=Function.prototype,M=Object.prototype,x=p["__core-js_shared__"],C=z.toString,F=M.hasOwnProperty,U=(w=/[^.]+$/.exec(x&&x.keys&&x.keys.IE_PROTO||""))?"Symbol(src)_1."+w:"",S=M.toString,I=C.call(Object),P=RegExp("^"+C.call(F).replace(/[\\^$.*+?()[\]{}|]/g,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$"),T=d?p.Buffer:void 0,q=p.Symbol,E=p.Uint8Array,$=T?T.allocUnsafe:void 0,D=(O=Object.getPrototypeOf,m=Object,function(t){return O(m(t))}),k=Object.create,B=M.propertyIsEnumerable,N=A.splice,L=q?q.toStringTag:void 0,R=function(){try{var t=_t(Object,"defineProperty");return t({},"",{}),t}catch(t){}}(),G=T?T.isBuffer:void 0,V=Math.max,W=Date.now,H=_t(p,"Map"),J=_t(Object,"create"),K=function(){function t(){}return function(r){if(!Mt(r))return{};if(k)return k(r);t.prototype=r;var e=new t;return t.prototype=void 0,e}}();function Q(t){var r=-1,e=null==t?0:t.length;for(this.clear();++r-1},X.prototype.set=function(t,r){var e=this.__data__,n=nt(e,t);return n<0?(++this.size,e.push([t,r])):e[n][1]=r,this},Y.prototype.clear=function(){this.size=0,this.__data__={hash:new Q,map:new(H||X),string:new Q}},Y.prototype.delete=function(t){var r=pt(this,t).delete(t);return this.size-=r?1:0,r},Y.prototype.get=function(t){return pt(this,t).get(t)},Y.prototype.has=function(t){return pt(this,t).has(t)},Y.prototype.set=function(t,r){var e=pt(this,t),n=e.size;return e.set(t,r),this.size+=e.size==n?0:1,this},Z.prototype.clear=function(){this.__data__=new X,this.size=0},Z.prototype.delete=function(t){var r=this.__data__,e=r.delete(t);return this.size=r.size,e},Z.prototype.get=function(t){return this.__data__.get(t)},Z.prototype.has=function(t){return this.__data__.has(t)},Z.prototype.set=function(t,r){var e=this.__data__;if(e instanceof X){var n=e.__data__;if(!H||n.length<199)return n.push([t,r]),this.size=++e.size,this;e=this.__data__=new Y(n)}return e.set(t,r),this.size=e.size,this};var it,ut=function(t,r,e){for(var n=-1,o=Object(t),i=e(t),u=i.length;u--;){var c=i[it?u:++n];if(!1===r(o[c],c,o))break}return t};function ct(t){return null==t?void 0===t?"[object Undefined]":"[object Null]":L&&L in Object(t)?function(t){var r=F.call(t,L),e=t[L];try{t[L]=void 0;var n=!0}catch(t){}var o=S.call(t);n&&(r?t[L]=e:delete t[L]);return o}(t):function(t){return S.call(t)}(t)}function at(t){return xt(t)&&ct(t)==o}function ft(t){return!(!Mt(t)||function(t){return!!U&&U in t}(t))&&(At(t)?P:a).test(function(t){if(null!=t){try{return C.call(t)}catch(t){}try{return t+""}catch(t){}}return""}(t))}function st(t){if(!Mt(t))return function(t){var r=[];if(null!=t)for(var e in Object(t))r.push(e);return r}(t);var r=dt(t),e=[];for(var n in t)("constructor"!=n||!r&&F.call(t,n))&&e.push(n);return e}function lt(t,r,e,n,o){t!==r&&ut(r,(function(i,u){if(o||(o=new Z),Mt(i))!function(t,r,e,n,o,i,u){var a=yt(t,e),f=yt(r,e),s=u.get(f);if(s)return void rt(t,e,s);var l=i?i(a,f,e+"",t,r,u):void 0,h=void 0===l;if(h){var p=wt(f),_=!p&&mt(f),v=!p&&!_&&Ct(f);l=f,p||_||v?wt(a)?l=a:xt(j=a)&&Ot(j)?l=function(t,r){var e=-1,n=t.length;r||(r=Array(n));for(;++e-1&&t%1==0&&t0){if(++r>=800)return arguments[0]}else r=0;return t.apply(void 0,arguments)}}(R?function(t,r){return R(t,"toString",{configurable:!0,enumerable:!1,value:(e=r,function(){return e}),writable:!0});var e}:It);function bt(t,r){return t===r||t!=t&&r!=r}var jt=at(function(){return arguments}())?at:function(t){return xt(t)&&F.call(t,"callee")&&!B.call(t,"callee")},wt=Array.isArray;function Ot(t){return null!=t&&zt(t.length)&&!At(t)}var mt=G||function(){return!1};function At(t){if(!Mt(t))return!1;var r=ct(t);return r==u||"[object GeneratorFunction]"==r||"[object AsyncFunction]"==r||"[object Proxy]"==r}function zt(t){return"number"==typeof t&&t>-1&&t%1==0&&t<=n}function Mt(t){var r=typeof t;return null!=t&&("object"==r||"function"==r)}function xt(t){return null!=t&&"object"==typeof t}var Ct=b?function(t){return function(r){return t(r)}}(b):function(t){return xt(t)&&zt(t.length)&&!!s[ct(t)]};function Ft(t){return Ot(t)?tt(t,!0):st(t)}var Ut,St=(Ut=function(t,r,e){lt(t,r,e)},ht((function(t,r){var e=-1,n=r.length,o=n>1?r[n-1]:void 0,i=n>2?r[2]:void 0;for(o=Ut.length>3&&"function"==typeof o?(n--,o):void 0,i&&function(t,r,e){if(!Mt(e))return!1;var n=typeof r;return!!("number"==n?Ot(e)&&vt(r,e.length):"string"==n&&r in e)&&bt(e[r],t)}(r[0],r[1],i)&&(o=n<3?void 0:o,n=1),t=Object(t);++ec.call(t,r);class f{constructor({pluginId:t,defaultConfig:r={},customMerge:e,root:n}){this.pluginId=t,this.defaultConfig=r,this.pluginConfigPath=o.default.resolve(n||__dirname,t),this.customMerge=e,this._config=void 0}resolve(t){return o.default.resolve(this.pluginConfigPath,t)}hasFile(t){return n.default.existsSync(this.resolve(t))}requireFile(t){try{return require(this.resolve(t))}catch(t){if("MODULE_NOT_FOUND"===t.code)return;throw t}}_getUserConfig(){return this.requireFile("config.json")}config(t,r){this._config||(this._config=(this.customMerge||u)(this.defaultConfig,this._getUserConfig()));let e=this._config;return t?function(t,r,e){if("number"==typeof r)return t[r];if("symbol"==typeof r)return a(t,r)?t[r]:e;const n="string"!=typeof(o=r)?o:o.split(".").reduce(((t,r)=>(r.split(/\[([^}]+)\]/g).forEach((r=>r&&t.push(r))),t)),[]);var o;let i=t;for(let t=0;t { + // console.log({state}); + if(state.type == 'create'){ + let date = todayTimestamp() + let {total} = await signInTable.where({ + user_id:state.auth.uid, + date, + isDelete:false + }).count() + console.log(total); + if(total){ + throw new Error("今天已经签到") + } + state.newData.date = date + state.newData.isDelete = false + } + }, + after: async (state, event, error, result) => { + if (error) { + throw error + } + let date = todayTimestamp() + //查最近7天的签到情况 + let {data:signInData} = await signInTable.where({ + user_id:state.auth.uid, + date:dbCmd.gte(date-3600*24*6*1000), + isDelete:false + }).get() + + let allDate = signInData.map(item=>item.date) + + //今天是本轮签到的第几天 + const n = ( date - Math.min(...allDate) )/3600/24/1000+1; + //换成数字--第几天 + let days = signInData.map(item=>{ + return (n*10000 - (date - item.date)/3600/24/1000*10000)/10000 -1 + }) + + //查出来用户当前有多少积分 + let {data: [userScore]} = await scoresTable + .where({user_id:state.auth.uid}) + .orderBy("create_date", "desc") + .limit(1) + .get() + let balance = 0 + if(userScore){ + balance = userScore.balance + } + + if(state.type == 'create'){ + if(n == 7){ //如果已经满一轮就软删除之前的内容 + let setIsDeleteRes = await signInTable.where({ + user_id:state.auth.uid, + date:dbCmd.neq(date) + }).update({isDelete:true}) + console.log({setIsDeleteRes}); + } + //给加积分 + let score = n+days.length==14?60:10 //如果连续签到7天就多加50分,也就是60分 + balance += score + let addScores = await scoresTable.add({ + user_id:state.auth.uid, + balance, + score, + type:1, + create_date:Date.now() + }) + console.log({addScores}); + } + return {...result,score:balance,signInData,n,days} + } +} + + +function todayTimestamp(){ + //时区 + let timeZone = new Date().getTimezoneOffset()/60 + //获得相对于北京时间的时间戳 + let timestamp = Date.now()+3600*1000*(8+timeZone) + //一天一共多少毫秒 + const D = 3600*24*1000 + //去掉余数,再减去东8区的8小时 得到当天凌晨的时间戳 + return parseInt(timestamp/D)*D - 3600*1000*8 +} \ No newline at end of file diff --git a/uni_modules/uni-sign-in/uniCloud/database/opendb-sign-in.schema.json b/uni_modules/uni-sign-in/uniCloud/database/opendb-sign-in.schema.json new file mode 100644 index 0000000..d70fb39 --- /dev/null +++ b/uni_modules/uni-sign-in/uniCloud/database/opendb-sign-in.schema.json @@ -0,0 +1,41 @@ +// 文档教程: https://uniapp.dcloud.net.cn/uniCloud/schema +{ + "bsonType": "object", + "required": [], + "permission": { + "read": "auth.uid == doc.user_id", + "create": "auth.uid != null && 'signIn' in action", //如果你要使用`看广告签到`的方式请将这里的值改为:false + "update": false, + "delete": false + }, + "properties": { + "_id": { + "description": "ID,系统自动生成" + }, + "user_id":{ + "forceDefaultValue":{ + "$env":"uid" + } + }, + "date":{ + "bsonType":"timestamp", + "description":"签到的日期时间戳", + "permission":{ + "write":false + } + }, + "create_date":{ + "bsonType":"timestamp", + "description":"签到的时间戳", + "forceDefaultValue":{ + "$env":"now" + } + }, + "ip":{ + "bsonType":"string", + "forceDefaultValue":{ + "$env":"clientIP" + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-sign-in/utils/ad.js b/uni_modules/uni-sign-in/utils/ad.js new file mode 100644 index 0000000..7a77ca5 --- /dev/null +++ b/uni_modules/uni-sign-in/utils/ad.js @@ -0,0 +1,253 @@ +// ad.js +const ADType = { + RewardedVideo: "RewardedVideo", + FullScreenVideo: "FullScreenVideo" +} + +class AdHelper { + + constructor() { + this._ads = {} + } + + load(options, onload, onerror) { + let ops = this._fixOldOptions(options) + let { + adpid + } = ops + + if (!adpid || this.isBusy(adpid)) { + return + } + + this.get(ops).load(onload, onerror) + } + + show(options, onsuccess, onfail) { + let ops = this._fixOldOptions(options) + let { + adpid + } = ops + + if (!adpid) { + return + } + + uni.showLoading({ + mask: true + }) + + var ad = this.get(ops) + + ad.load(() => { + uni.hideLoading() + ad.show((e) => { + onsuccess && onsuccess(e) + }) + }, (err) => { + uni.hideLoading() + onfail && onfail(err) + }) + } + + isBusy(adpid) { + return (this._ads[adpid] && this._ads[adpid].isLoading) + } + + get(options) { + const { + adpid, + singleton = true + } = options + if (singleton === false) { + if (this._ads[adpid]) { + this._ads[adpid].destroy() + delete this._ads[adpid] + } + } + delete options.singleton + if (!this._ads[adpid]) { + this._ads[adpid] = this._createAdInstance(options) + } + + return this._ads[adpid] + } + + _createAdInstance(options) { + const adType = options.adType || ADType.RewardedVideo + delete options.adType + + let ad = null; + if (adType === ADType.RewardedVideo) { + ad = new RewardedVideo(options) + } else if (adType === ADType.FullScreenVideo) { + ad = new FullScreenVideo(options) + } + + return ad + } + + _fixOldOptions(options) { + return (typeof options === "string") ? { + adpid: options + } : options + } +} + +const EXPIRED_TIME = 1000 * 60 * 30 +const ProviderType = { + CSJ: 'csj', + GDT: 'gdt' +} + +const RETRY_COUNT = 1 + +class AdBase { + constructor(adInstance, options = {}) { + this._isLoad = false + this._isLoading = false + this._lastLoadTime = 0 + this._lastError = null + this._retryCount = 0 + + this._loadCallback = null + this._closeCallback = null + this._errorCallback = null + + const ad = this._ad = adInstance + ad.onLoad((e) => { + this._isLoading = false + this._isLoad = true + this._lastLoadTime = Date.now() + + this.onLoad() + }) + ad.onClose((e) => { + this._isLoad = false + this.onClose(e) + }) + ad.onVerify && ad.onVerify((e) => { + // e.isValid + }) + ad.onError(({ + code, + message + }) => { + this._isLoading = false + const data = { + code: code, + errMsg: message + } + + if (code === -5008) { + this._loadAd() + return + } + + if (this._retryCount < RETRY_COUNT) { + this._retryCount += 1 + this._loadAd() + return + } + + this._lastError = data + this.onError(data) + }) + } + + get isExpired() { + return (this._lastLoadTime !== 0 && (Math.abs(Date.now() - this._lastLoadTime) > EXPIRED_TIME)) + } + + get isLoading() { + return this._isLoading + } + + getProvider() { + return this._ad.getProvider() + } + + load(onload, onerror) { + this._loadCallback = onload + this._errorCallback = onerror + + if (this._isLoading) { + return + } + + if (this._isLoad) { + this.onLoad() + return + } + + this._retryCount = 0 + + this._loadAd() + } + + show(onclose) { + this._closeCallback = onclose + + if (this._isLoading || !this._isLoad) { + return + } + + if (this._lastError !== null) { + this.onError(this._lastError) + return + } + + const provider = this.getProvider() + if (provider === ProviderType.CSJ && this.isExpired) { + this._loadAd() + return + } + + this._ad.show() + } + + onLoad(e) { + if (this._loadCallback != null) { + this._loadCallback() + } + } + + onClose(e) { + if (this._closeCallback != null) { + this._closeCallback({ + isEnded: e.isEnded + }) + } + } + + onError(e) { + if (this._errorCallback != null) { + this._errorCallback(e) + } + } + + destroy() { + this._ad.destroy() + } + + _loadAd() { + this._isLoad = false + this._isLoading = true + this._lastError = null + this._ad.load() + } +} + +class RewardedVideo extends AdBase { + constructor(options = {}) { + super(plus.ad.createRewardedVideoAd(options), options) + } +} + +class FullScreenVideo extends AdBase { + constructor(options = {}) { + super(plus.ad.createFullScreenVideoAd(options), options) + } +} + +export default new AdHelper() \ No newline at end of file diff --git a/uni_modules/uni-steps/changelog.md b/uni_modules/uni-steps/changelog.md new file mode 100644 index 0000000..04367d8 --- /dev/null +++ b/uni_modules/uni-steps/changelog.md @@ -0,0 +1,18 @@ +## 1.1.2(2024-03-28) +- 修复 uni-steps为竖排列时,文本长度过长引起点错乱的bug +## 1.1.1(2021-11-22) +- 修复 vue3中某些scss变量无法找到的问题 +## 1.1.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-steps](https://uniapp.dcloud.io/component/uniui/uni-steps) +## 1.0.8(2021-05-12) +- 新增 项目示例地址 +## 1.0.7(2021-05-06) +- 修复 uni-steps 横向布局时,多行文字高度不合理的 bug +## 1.0.6(2021-04-21) +- 优化 添加依赖 uni-icons, 导入后自动下载依赖 +## 1.0.5(2021-02-05) +- 优化 组件引用关系,通过uni_modules引用组件 + +## 1.0.4(2021-02-05) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-steps/components/uni-steps/uni-steps.vue b/uni_modules/uni-steps/components/uni-steps/uni-steps.vue new file mode 100644 index 0000000..81017fc --- /dev/null +++ b/uni_modules/uni-steps/components/uni-steps/uni-steps.vue @@ -0,0 +1,280 @@ + + + + + diff --git a/uni_modules/uni-steps/package.json b/uni_modules/uni-steps/package.json new file mode 100644 index 0000000..4145ce9 --- /dev/null +++ b/uni_modules/uni-steps/package.json @@ -0,0 +1,87 @@ +{ + "id": "uni-steps", + "displayName": "uni-steps 步骤条", + "version": "1.1.2", + "description": "步骤条组件,提供横向和纵向两种布局格式。", + "keywords": [ + "uni-ui", + "uniui", + "步骤条", + "时间轴" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": [ + "uni-scss", + "uni-icons" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "n" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-steps/readme.md b/uni_modules/uni-steps/readme.md new file mode 100644 index 0000000..da7a4bf --- /dev/null +++ b/uni_modules/uni-steps/readme.md @@ -0,0 +1,13 @@ + + +## Steps 步骤条 +> **组件名:uni-steps** +> 代码块: `uSteps` + + +步骤条,常用于显示进度 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-steps) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 + + diff --git a/uni_modules/uni-swipe-action/changelog.md b/uni_modules/uni-swipe-action/changelog.md new file mode 100644 index 0000000..e28472f --- /dev/null +++ b/uni_modules/uni-swipe-action/changelog.md @@ -0,0 +1,47 @@ +## 1.3.10(2024-01-17) +- 修复 点击按钮时,按钮会被点击穿透导致自动收缩的 bug(兼容阿里/百度/抖音小程序) +## 1.3.9(2024-01-17) +- 修复 点击按钮时,按钮会被点击穿透导致自动收缩的 bug +## 1.3.8(2023-04-13) +- 修复`uni-swipe-action`和`uni-swipe-action-item`不同时使用导致 closeOther 方法报错的 bug +## 1.3.7(2022-06-06) +- 修复 vue3 下使用组件不能正常运行的Bug +## 1.3.6(2022-05-31) +- 修复 h5端点击click触发两次的Bug +## 1.3.5(2022-05-23) +- 修复 isPC 找不到的Bug +## 1.3.4(2022-05-19) +- 修复 在 nvue 下 disabled 失效的bug +## 1.3.3(2022-03-31) +- 修复 按钮字体大小不能设置的bug +## 1.3.2(2022-03-16) +- 修复 h5和app端下报el错误的bug +## 1.3.1(2022-03-07) +- 修复 HBuilderX 1.4.X 版本中,h5和app端下报错的bug +## 1.3.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-swipe-action](https://uniapp.dcloud.io/component/uniui/uni-swipe-action) +## 1.2.4(2021-08-20) +- 优化 close-all 方法 +## 1.2.3(2021-08-20) +- 新增 close-all 方法,关闭所有已打开的组件 +## 1.2.2(2021-08-17) +- 新增 resize() 方法,在非微信小程序、h5、app-vue端出现不能滑动的问题的时候,重置组件 +- 修复 app 端偶尔出现类似 Page[x][-x,xx;-x,xx,x,x-x] 的问题 +- 优化 微信小程序、h5、app-vue 滑动逻辑,避免出现动态新增组件后不能滑动的问题 +## 1.2.1(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +- 修复 跨页面修改组件数据 ,导致不能滑动的问题 +## 1.1.10(2021-06-17) +- 修复 按钮点击执行两次的bug +## 1.1.9(2021-05-12) +- 新增 项目示例地址 +## 1.1.8(2021-03-26) +- 修复 微信小程序 nv_navigator is not defined 报错的bug +## 1.1.7(2021-02-05) +- 调整为uni_modules目录规范 +- 新增 左侧滑动 +- 新增 插槽使用方式 +- 新增 threshold 属性,可以控制滑动缺省值 +- 优化 长列表滚动性能 +- 修复 滚动页面时触发组件滑动的Bug diff --git a/uni_modules/uni-swipe-action/components/uni-swipe-action-item/bindingx.js b/uni_modules/uni-swipe-action/components/uni-swipe-action-item/bindingx.js new file mode 100644 index 0000000..707e432 --- /dev/null +++ b/uni_modules/uni-swipe-action/components/uni-swipe-action-item/bindingx.js @@ -0,0 +1,302 @@ +let bindIngXMixins = {} + +// #ifdef APP-NVUE +const BindingX = uni.requireNativePlugin('bindingx'); +const dom = uni.requireNativePlugin('dom'); +const animation = uni.requireNativePlugin('animation'); + +bindIngXMixins = { + data() { + return {} + }, + + watch: { + show(newVal) { + if (this.autoClose) return + if (this.stop) return + this.stop = true + if (newVal) { + this.open(newVal) + } else { + this.close() + } + }, + leftOptions() { + this.getSelectorQuery() + this.init() + }, + rightOptions(newVal) { + this.init() + } + }, + created() { + this.swipeaction = this.getSwipeAction() + if (this.swipeaction && Array.isArray(this.swipeaction.children)) { + this.swipeaction.children.push(this) + } + }, + mounted() { + this.box = this.getEl(this.$refs['selector-box--hock']) + this.selector = this.getEl(this.$refs['selector-content--hock']); + this.leftButton = this.getEl(this.$refs['selector-left-button--hock']); + this.rightButton = this.getEl(this.$refs['selector-right-button--hock']); + this.init() + }, + // beforeDestroy() { + // this.swipeaction.children.forEach((item, index) => { + // if (item === this) { + // this.swipeaction.children.splice(index, 1) + // } + // }) + // }, + methods: { + init() { + this.$nextTick(() => { + this.x = 0 + this.button = { + show: false + } + setTimeout(() => { + this.getSelectorQuery() + }, 200) + }) + }, + onClick(index, item, position) { + this.$emit('click', { + content: item, + index, + position + }) + }, + touchstart(e) { + // fix by mehaotian 禁止滑动 + if (this.disabled) return + // 每次只触发一次,避免多次监听造成闪烁 + if (this.stop) return + this.stop = true + if (this.autoClose && this.swipeaction) { + this.swipeaction.closeOther(this) + } + + const leftWidth = this.button.left.width + const rightWidth = this.button.right.width + let expression = this.range(this.x, -rightWidth, leftWidth) + let leftExpression = this.range(this.x - leftWidth, -leftWidth, 0) + let rightExpression = this.range(this.x + rightWidth, 0, rightWidth) + + this.eventpan = BindingX.bind({ + anchor: this.box, + eventType: 'pan', + props: [{ + element: this.selector, + property: 'transform.translateX', + expression + }, { + element: this.leftButton, + property: 'transform.translateX', + expression: leftExpression + }, { + element: this.rightButton, + property: 'transform.translateX', + expression: rightExpression + }, ] + }, (e) => { + // nope + if (e.state === 'end') { + this.x = e.deltaX + this.x; + this.isclick = true + this.bindTiming(e.deltaX) + } + }); + }, + touchend(e) { + if (this.isopen !== 'none' && !this.isclick) { + this.open('none') + } + }, + bindTiming(x) { + const left = this.x + const leftWidth = this.button.left.width + const rightWidth = this.button.right.width + const threshold = this.threshold + if (!this.isopen || this.isopen === 'none') { + if (left > threshold) { + this.open('left') + } else if (left < -threshold) { + this.open('right') + } else { + this.open('none') + } + } else { + if ((x > -leftWidth && x < 0) || x > rightWidth) { + if ((x > -threshold && x < 0) || (x - rightWidth > threshold)) { + this.open('left') + } else { + this.open('none') + } + } else { + if ((x < threshold && x > 0) || (x + leftWidth < -threshold)) { + this.open('right') + } else { + this.open('none') + } + } + } + }, + + /** + * 移动范围 + * @param {Object} num + * @param {Object} mix + * @param {Object} max + */ + range(num, mix, max) { + return `min(max(x+${num}, ${mix}), ${max})` + }, + + /** + * 开启swipe + */ + open(type) { + this.animation(type) + }, + + /** + * 关闭swipe + */ + close() { + this.animation('none') + }, + + /** + * 开启关闭动画 + * @param {Object} type + */ + animation(type) { + const time = 300 + const leftWidth = this.button.left.width + const rightWidth = this.button.right.width + if (this.eventpan && this.eventpan.token) { + BindingX.unbind({ + token: this.eventpan.token, + eventType: 'pan' + }) + } + + switch (type) { + case 'left': + Promise.all([ + this.move(this.selector, leftWidth), + this.move(this.leftButton, 0), + this.move(this.rightButton, rightWidth * 2) + ]).then(() => { + this.setEmit(leftWidth, type) + }) + break + case 'right': + Promise.all([ + this.move(this.selector, -rightWidth), + this.move(this.leftButton, -leftWidth * 2), + this.move(this.rightButton, 0) + ]).then(() => { + this.setEmit(-rightWidth, type) + }) + break + default: + Promise.all([ + this.move(this.selector, 0), + this.move(this.leftButton, -leftWidth), + this.move(this.rightButton, rightWidth) + ]).then(() => { + this.setEmit(0, type) + }) + + } + }, + setEmit(x, type) { + const leftWidth = this.button.left.width + const rightWidth = this.button.right.width + this.isopen = this.isopen || 'none' + this.stop = false + this.isclick = false + // 只有状态不一致才会返回结果 + if (this.isopen !== type && this.x !== x) { + if (type === 'left' && leftWidth > 0) { + this.$emit('change', 'left') + } + if (type === 'right' && rightWidth > 0) { + this.$emit('change', 'right') + } + if (type === 'none') { + this.$emit('change', 'none') + } + } + this.x = x + this.isopen = type + }, + move(ref, value) { + return new Promise((resolve, reject) => { + animation.transition(ref, { + styles: { + transform: `translateX(${value})`, + }, + duration: 150, //ms + timingFunction: 'linear', + needLayout: false, + delay: 0 //ms + }, function(res) { + resolve(res) + }) + }) + + }, + + /** + * 获取ref + * @param {Object} el + */ + getEl(el) { + return el.ref + }, + /** + * 获取节点信息 + */ + getSelectorQuery() { + Promise.all([ + this.getDom('left'), + this.getDom('right'), + ]).then((data) => { + let show = 'none' + if (this.autoClose) { + show = 'none' + } else { + show = this.show + } + + if (show === 'none') { + // this.close() + } else { + this.open(show) + } + + }) + + }, + getDom(str) { + return new Promise((resolve, reject) => { + dom.getComponentRect(this.$refs[`selector-${str}-button--hock`], (data) => { + if (data) { + this.button[str] = data.size + resolve(data) + } else { + reject() + } + }) + }) + } + } +} + +// #endif + +export default bindIngXMixins diff --git a/uni_modules/uni-swipe-action/components/uni-swipe-action-item/index.wxs b/uni_modules/uni-swipe-action/components/uni-swipe-action-item/index.wxs new file mode 100644 index 0000000..10ddb56 --- /dev/null +++ b/uni_modules/uni-swipe-action/components/uni-swipe-action-item/index.wxs @@ -0,0 +1,323 @@ +var MIN_DISTANCE = 10; + +/** + * 判断当前是否为H5、app-vue + */ +var IS_HTML5 = false +if (typeof window === 'object') IS_HTML5 = true + +/** + * 监听页面内值的变化,主要用于动态开关swipe-action + * @param {Object} newValue + * @param {Object} oldValue + * @param {Object} ownerInstance + * @param {Object} instance + */ +function sizeReady(newValue, oldValue, ownerInstance, instance) { + var state = instance.getState() + var buttonPositions = JSON.parse(newValue) + if (!buttonPositions || !buttonPositions.data || buttonPositions.data.length === 0) return + state.leftWidth = buttonPositions.data[0].width + state.rightWidth = buttonPositions.data[1].width + state.threshold = instance.getDataset().threshold + + if (buttonPositions.show && buttonPositions.show !== 'none') { + openState(buttonPositions.show, instance, ownerInstance) + return + } + + if (state.left) { + openState('none', instance, ownerInstance) + } + resetTouchStatus(instance) +} + +/** + * 开始触摸操作 + * @param {Object} e + * @param {Object} ins + */ +function touchstart(e, ins) { + var instance = e.instance; + var disabled = instance.getDataset().disabled + var state = instance.getState(); + // fix by mehaotian, TODO 兼容 app-vue 获取dataset为字符串 , h5 获取 为 undefined 的问题,待框架修复 + disabled = (typeof(disabled) === 'string' ? JSON.parse(disabled) : disabled) || false; + if (disabled) return + // 开始触摸时移除动画类 + instance.requestAnimationFrame(function() { + instance.removeClass('ani'); + ins.callMethod('closeSwipe'); + }) + + // 记录上次的位置 + state.x = state.left || 0 + // 计算滑动开始位置 + stopTouchStart(e, ins) +} + +/** + * 开始滑动操作 + * @param {Object} e + * @param {Object} ownerInstance + */ +function touchmove(e, ownerInstance) { + var instance = e.instance; + var disabled = instance.getDataset().disabled + var state = instance.getState() + // fix by mehaotian, TODO 兼容 app-vue 获取dataset为字符串 , h5 获取 为 undefined 的问题,待框架修复 + disabled = (typeof(disabled) === 'string' ? JSON.parse(disabled) : disabled) || false; + if (disabled) return + // 是否可以滑动页面 + stopTouchMove(e); + if (state.direction !== 'horizontal') { + return; + } + + if (e.preventDefault) { + // 阻止页面滚动 + e.preventDefault() + } + + move(state.x + state.deltaX, instance, ownerInstance) +} + +/** + * 结束触摸操作 + * @param {Object} e + * @param {Object} ownerInstance + */ +function touchend(e, ownerInstance) { + var instance = e.instance; + var disabled = instance.getDataset().disabled + var state = instance.getState() + // fix by mehaotian, TODO 兼容 app-vue 获取dataset为字符串 , h5 获取 为 undefined 的问题,待框架修复 + disabled = (typeof(disabled) === 'string' ? JSON.parse(disabled) : disabled) || false; + + if (disabled) return + // 滑动过程中触摸结束,通过阙值判断是开启还是关闭 + // fixed by mehaotian 定时器解决点击按钮,touchend 触发比 click 事件时机早的问题 ,主要是 ios13 + moveDirection(state.left, instance, ownerInstance) + +} + +/** + * 设置移动距离 + * @param {Object} value + * @param {Object} instance + * @param {Object} ownerInstance + */ +function move(value, instance, ownerInstance) { + value = value || 0 + var state = instance.getState() + var leftWidth = state.leftWidth + var rightWidth = state.rightWidth + // 获取可滑动范围 + state.left = range(value, -rightWidth, leftWidth); + instance.requestAnimationFrame(function() { + instance.setStyle({ + transform: 'translateX(' + state.left + 'px)', + '-webkit-transform': 'translateX(' + state.left + 'px)' + }) + }) + +} + +/** + * 获取范围 + * @param {Object} num + * @param {Object} min + * @param {Object} max + */ +function range(num, min, max) { + return Math.min(Math.max(num, min), max); +} + + +/** + * 移动方向判断 + * @param {Object} left + * @param {Object} value + * @param {Object} ownerInstance + * @param {Object} ins + */ +function moveDirection(left, ins, ownerInstance) { + var state = ins.getState() + var threshold = state.threshold + var position = state.position + var isopen = state.isopen || 'none' + var leftWidth = state.leftWidth + var rightWidth = state.rightWidth + if (state.deltaX === 0) { + openState('none', ins, ownerInstance) + return + } + if ((isopen === 'none' && rightWidth > 0 && -left > threshold) || (isopen !== 'none' && rightWidth > 0 && + rightWidth + + left < threshold)) { + // right + openState('right', ins, ownerInstance) + } else if ((isopen === 'none' && leftWidth > 0 && left > threshold) || (isopen !== 'none' && leftWidth > 0 && + leftWidth - left < threshold)) { + // left + openState('left', ins, ownerInstance) + } else { + // default + openState('none', ins, ownerInstance) + } +} + + +/** + * 开启状态 + * @param {Boolean} type + * @param {Object} ins + * @param {Object} ownerInstance + */ +function openState(type, ins, ownerInstance) { + var state = ins.getState() + var position = state.position + var leftWidth = state.leftWidth + var rightWidth = state.rightWidth + var left = '' + state.isopen = state.isopen ? state.isopen : 'none' + switch (type) { + case "left": + left = leftWidth + break + case "right": + left = -rightWidth + break + default: + left = 0 + } + + // && !state.throttle + + if (state.isopen !== type) { + state.throttle = true + ownerInstance.callMethod('change', { + open: type + }) + + } + + state.isopen = type + // 添加动画类 + ins.requestAnimationFrame(function() { + ins.addClass('ani'); + move(left, ins, ownerInstance) + }) + // 设置最终移动位置,理论上只要进入到这个函数,肯定是要打开的 +} + + +function getDirection(x, y) { + if (x > y && x > MIN_DISTANCE) { + return 'horizontal'; + } + if (y > x && y > MIN_DISTANCE) { + return 'vertical'; + } + return ''; +} + +/** + * 重置滑动状态 + * @param {Object} event + */ +function resetTouchStatus(instance) { + var state = instance.getState(); + state.direction = ''; + state.deltaX = 0; + state.deltaY = 0; + state.offsetX = 0; + state.offsetY = 0; +} + +/** + * 设置滑动开始位置 + * @param {Object} event + */ +function stopTouchStart(event) { + var instance = event.instance; + var state = instance.getState(); + resetTouchStatus(instance); + var touch = event.touches[0]; + if (IS_HTML5 && isPC()) { + touch = event; + } + state.startX = touch.clientX; + state.startY = touch.clientY; +} + +/** + * 滑动中,是否禁止打开 + * @param {Object} event + */ +function stopTouchMove(event) { + var instance = event.instance; + var state = instance.getState(); + var touch = event.touches[0]; + if (IS_HTML5 && isPC()) { + touch = event; + } + state.deltaX = touch.clientX - state.startX; + state.deltaY = touch.clientY - state.startY; + state.offsetY = Math.abs(state.deltaY); + state.offsetX = Math.abs(state.deltaX); + state.direction = state.direction || getDirection(state.offsetX, state.offsetY); +} + +function isPC() { + var userAgentInfo = navigator.userAgent; + var Agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"]; + var flag = true; + for (var v = 0; v < Agents.length - 1; v++) { + if (userAgentInfo.indexOf(Agents[v]) > 0) { + flag = false; + break; + } + } + return flag; +} + +var movable = false + +function mousedown(e, ins) { + if (!IS_HTML5) return + if (!isPC()) return + touchstart(e, ins) + movable = true +} + +function mousemove(e, ins) { + if (!IS_HTML5) return + if (!isPC()) return + if (!movable) return + touchmove(e, ins) +} + +function mouseup(e, ins) { + if (!IS_HTML5) return + if (!isPC()) return + touchend(e, ins) + movable = false +} + +function mouseleave(e, ins) { + if (!IS_HTML5) return + if (!isPC()) return + movable = false +} + +module.exports = { + sizeReady: sizeReady, + touchstart: touchstart, + touchmove: touchmove, + touchend: touchend, + mousedown: mousedown, + mousemove: mousemove, + mouseup: mouseup, + mouseleave: mouseleave +} diff --git a/uni_modules/uni-swipe-action/components/uni-swipe-action-item/isPC.js b/uni_modules/uni-swipe-action/components/uni-swipe-action-item/isPC.js new file mode 100644 index 0000000..917cb48 --- /dev/null +++ b/uni_modules/uni-swipe-action/components/uni-swipe-action-item/isPC.js @@ -0,0 +1,12 @@ +export function isPC() { + var userAgentInfo = navigator.userAgent; + var Agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"]; + var flag = true; + for (let v = 0; v < Agents.length - 1; v++) { + if (userAgentInfo.indexOf(Agents[v]) > 0) { + flag = false; + break; + } + } + return flag; +} diff --git a/uni_modules/uni-swipe-action/components/uni-swipe-action-item/mpalipay.js b/uni_modules/uni-swipe-action/components/uni-swipe-action-item/mpalipay.js new file mode 100644 index 0000000..35c796b --- /dev/null +++ b/uni_modules/uni-swipe-action/components/uni-swipe-action-item/mpalipay.js @@ -0,0 +1,195 @@ +export default { + data() { + return { + x: 0, + transition: false, + width: 0, + viewWidth: 0, + swipeShow: 0 + } + }, + watch: { + show(newVal) { + if (this.autoClose) return + if (newVal && newVal !== 'none') { + this.transition = true + this.open(newVal) + } else { + this.close() + } + } + }, + created() { + this.swipeaction = this.getSwipeAction() + if (this.swipeaction && Array.isArray(this.swipeaction.children)) { + this.swipeaction.children.push(this) + } + }, + mounted() { + this.isopen = false + setTimeout(() => { + this.getQuerySelect() + }, 50) + }, + methods: { + appTouchStart(e) { + const { + clientX + } = e.changedTouches[0] + this.clientX = clientX + this.timestamp = new Date().getTime() + }, + appTouchEnd(e, index, item, position) { + const { + clientX + } = e.changedTouches[0] + // fixed by xxxx 模拟点击事件,解决 ios 13 点击区域错位的问题 + let diff = Math.abs(this.clientX - clientX) + let time = (new Date().getTime()) - this.timestamp + if (diff < 40 && time < 300) { + this.$emit('click', { + content: item, + index, + position + }) + } + }, + /** + * 移动触发 + * @param {Object} e + */ + onChange(e) { + this.moveX = e.detail.x + this.isclose = false + }, + touchstart(e) { + this.transition = false + this.isclose = true + if (this.autoClose && this.swipeaction) { + this.swipeaction.closeOther(this) + } + }, + touchmove(e) {}, + touchend(e) { + // 0的位置什么都不执行 + if (this.isclose && this.isopen === 'none') return + if (this.isclose && this.isopen !== 'none') { + this.transition = true + this.close() + } else { + this.move(this.moveX + this.leftWidth) + } + }, + + /** + * 移动 + * @param {Object} moveX + */ + move(moveX) { + // 打开关闭的处理逻辑不太一样 + this.transition = true + // 未打开状态 + if (!this.isopen || this.isopen === 'none') { + if (moveX > this.threshold) { + this.open('left') + } else if (moveX < -this.threshold) { + this.open('right') + } else { + this.close() + } + } else { + if (moveX < 0 && moveX < this.rightWidth) { + const rightX = this.rightWidth + moveX + if (rightX < this.threshold) { + this.open('right') + } else { + this.close() + } + } else if (moveX > 0 && moveX < this.leftWidth) { + const leftX = this.leftWidth - moveX + if (leftX < this.threshold) { + this.open('left') + } else { + this.close() + } + } + + } + + }, + + /** + * 打开 + */ + open(type) { + this.x = this.moveX + this.animation(type) + }, + + /** + * 关闭 + */ + close() { + this.x = this.moveX + // TODO 解决 x 值不更新的问题,所以会多触发一次 nextTick ,待优化 + this.$nextTick(() => { + this.x = -this.leftWidth + if (this.isopen !== 'none') { + this.$emit('change', 'none') + } + this.isopen = 'none' + }) + }, + + /** + * 执行结束动画 + * @param {Object} type + */ + animation(type) { + this.$nextTick(() => { + if (type === 'left') { + this.x = 0 + } else { + this.x = -this.rightWidth - this.leftWidth + } + + if (this.isopen !== type) { + this.$emit('change', type) + } + this.isopen = type + }) + + }, + getSlide(x) {}, + getQuerySelect() { + const query = uni.createSelectorQuery().in(this); + query.selectAll('.movable-view--hock').boundingClientRect(data => { + this.leftWidth = data[1].width + this.rightWidth = data[2].width + this.width = data[0].width + this.viewWidth = this.width + this.rightWidth + this.leftWidth + if (this.leftWidth === 0) { + // TODO 疑似bug ,初始化的时候如果x 是0,会导致移动位置错误,所以让元素超出一点 + this.x = -0.1 + } else { + this.x = -this.leftWidth + } + this.moveX = this.x + this.$nextTick(() => { + this.swipeShow = 1 + }) + + if (!this.buttonWidth) { + this.disabledView = true + } + + if (this.autoClose) return + if (this.show !== 'none') { + this.transition = true + this.open(this.shows) + } + }).exec(); + + } + } +} diff --git a/uni_modules/uni-swipe-action/components/uni-swipe-action-item/mpother.js b/uni_modules/uni-swipe-action/components/uni-swipe-action-item/mpother.js new file mode 100644 index 0000000..d389bce --- /dev/null +++ b/uni_modules/uni-swipe-action/components/uni-swipe-action-item/mpother.js @@ -0,0 +1,260 @@ +let otherMixins = {} + +// #ifndef APP-PLUS|| MP-WEIXIN || H5 +const MIN_DISTANCE = 10; +otherMixins = { + data() { + // TODO 随机生生元素ID,解决百度小程序获取同一个元素位置信息的bug + const elClass = `Uni_${Math.ceil(Math.random() * 10e5).toString(36)}` + return { + uniShow: false, + left: 0, + buttonShow: 'none', + ani: false, + moveLeft: '', + elClass + } + }, + watch: { + show(newVal) { + if (this.autoClose) return + this.openState(newVal) + }, + left() { + this.moveLeft = `translateX(${this.left}px)` + }, + buttonShow(newVal) { + if (this.autoClose) return + this.openState(newVal) + }, + leftOptions() { + this.init() + }, + rightOptions() { + this.init() + } + }, + mounted() { + this.swipeaction = this.getSwipeAction() + if (this.swipeaction && Array.isArray(this.swipeaction.children)) { + this.swipeaction.children.push(this) + } + this.init() + }, + methods: { + init() { + clearTimeout(this.timer) + this.timer = setTimeout(() => { + this.getSelectorQuery() + }, 100) + // 移动距离 + this.left = 0 + this.x = 0 + }, + + closeSwipe(e) { + if (this.autoClose && this.swipeaction) { + this.swipeaction.closeOther(this) + } + }, + appTouchStart(e) { + const { + clientX + } = e.changedTouches[0] + this.clientX = clientX + this.timestamp = new Date().getTime() + }, + appTouchEnd(e, index, item, position) { + const { + clientX + } = e.changedTouches[0] + // fixed by xxxx 模拟点击事件,解决 ios 13 点击区域错位的问题 + let diff = Math.abs(this.clientX - clientX) + let time = (new Date().getTime()) - this.timestamp + if (diff < 40 && time < 300) { + this.$emit('click', { + content: item, + index, + position + }) + } + }, + touchstart(e) { + if (this.disabled) return + this.ani = false + this.x = this.left || 0 + this.stopTouchStart(e) + this.autoClose && this.closeSwipe() + }, + touchmove(e) { + if (this.disabled) return + // 是否可以滑动页面 + this.stopTouchMove(e); + if (this.direction !== 'horizontal') { + return; + } + this.move(this.x + this.deltaX) + return false + }, + touchend() { + if (this.disabled) return + this.moveDirection(this.left) + }, + /** + * 设置移动距离 + * @param {Object} value + */ + move(value) { + value = value || 0 + const leftWidth = this.leftWidth + const rightWidth = this.rightWidth + // 获取可滑动范围 + this.left = this.range(value, -rightWidth, leftWidth); + }, + + /** + * 获取范围 + * @param {Object} num + * @param {Object} min + * @param {Object} max + */ + range(num, min, max) { + return Math.min(Math.max(num, min), max); + }, + /** + * 移动方向判断 + * @param {Object} left + * @param {Object} value + */ + moveDirection(left) { + const threshold = this.threshold + const isopen = this.isopen || 'none' + const leftWidth = this.leftWidth + const rightWidth = this.rightWidth + if (this.deltaX === 0) { + this.openState('none') + return + } + if ((isopen === 'none' && rightWidth > 0 && -left > threshold) || (isopen !== 'none' && rightWidth > + 0 && rightWidth + + left < threshold)) { + // right + this.openState('right') + } else if ((isopen === 'none' && leftWidth > 0 && left > threshold) || (isopen !== 'none' && leftWidth > + 0 && + leftWidth - left < threshold)) { + // left + this.openState('left') + } else { + // default + this.openState('none') + } + }, + + /** + * 开启状态 + * @param {Boolean} type + */ + openState(type) { + const leftWidth = this.leftWidth + const rightWidth = this.rightWidth + let left = '' + this.isopen = this.isopen ? this.isopen : 'none' + switch (type) { + case "left": + left = leftWidth + break + case "right": + left = -rightWidth + break + default: + left = 0 + } + + + if (this.isopen !== type) { + this.throttle = true + this.$emit('change', type) + } + + this.isopen = type + // 添加动画类 + this.ani = true + this.$nextTick(() => { + this.move(left) + }) + // 设置最终移动位置,理论上只要进入到这个函数,肯定是要打开的 + }, + close() { + this.openState('none') + }, + getDirection(x, y) { + if (x > y && x > MIN_DISTANCE) { + return 'horizontal'; + } + if (y > x && y > MIN_DISTANCE) { + return 'vertical'; + } + return ''; + }, + + /** + * 重置滑动状态 + * @param {Object} event + */ + resetTouchStatus() { + this.direction = ''; + this.deltaX = 0; + this.deltaY = 0; + this.offsetX = 0; + this.offsetY = 0; + }, + + /** + * 设置滑动开始位置 + * @param {Object} event + */ + stopTouchStart(event) { + this.resetTouchStatus(); + const touch = event.touches[0]; + this.startX = touch.clientX; + this.startY = touch.clientY; + }, + + /** + * 滑动中,是否禁止打开 + * @param {Object} event + */ + stopTouchMove(event) { + const touch = event.touches[0]; + this.deltaX = touch.clientX - this.startX; + this.deltaY = touch.clientY - this.startY; + this.offsetX = Math.abs(this.deltaX); + this.offsetY = Math.abs(this.deltaY); + this.direction = this.direction || this.getDirection(this.offsetX, this.offsetY); + }, + + getSelectorQuery() { + const views = uni.createSelectorQuery().in(this) + views + .selectAll('.' + this.elClass) + .boundingClientRect(data => { + if (data.length === 0) return + let show = 'none' + if (this.autoClose) { + show = 'none' + } else { + show = this.show + } + this.leftWidth = data[0].width || 0 + this.rightWidth = data[1].width || 0 + this.buttonShow = show + }) + .exec() + } + } +} + +// #endif + +export default otherMixins diff --git a/uni_modules/uni-swipe-action/components/uni-swipe-action-item/mpwxs.js b/uni_modules/uni-swipe-action/components/uni-swipe-action-item/mpwxs.js new file mode 100644 index 0000000..08de1c9 --- /dev/null +++ b/uni_modules/uni-swipe-action/components/uni-swipe-action-item/mpwxs.js @@ -0,0 +1,84 @@ +let mpMixins = {} +let is_pc = null +// #ifdef H5 +import { + isPC +} from "./isPC" +is_pc = isPC() +// #endif +// #ifdef APP-VUE|| MP-WEIXIN || H5 + +mpMixins = { + data() { + return { + is_show: 'none' + } + }, + watch: { + show(newVal) { + this.is_show = this.show + } + }, + created() { + this.swipeaction = this.getSwipeAction() + if (this.swipeaction && Array.isArray(this.swipeaction.children)) { + this.swipeaction.children.push(this) + } + }, + mounted() { + this.is_show = this.show + }, + methods: { + // wxs 中调用 + closeSwipe(e) { + if (this.autoClose && this.swipeaction) { + this.swipeaction.closeOther(this) + } + }, + + change(e) { + this.$emit('change', e.open) + if (this.is_show !== e.open) { + this.is_show = e.open + } + }, + + appTouchStart(e) { + if (is_pc) return + const { + clientX + } = e.changedTouches[0] + this.clientX = clientX + this.timestamp = new Date().getTime() + }, + appTouchEnd(e, index, item, position) { + if (is_pc) return + const { + clientX + } = e.changedTouches[0] + // fixed by xxxx 模拟点击事件,解决 ios 13 点击区域错位的问题 + let diff = Math.abs(this.clientX - clientX) + let time = (new Date().getTime()) - this.timestamp + if (diff < 40 && time < 300) { + this.$emit('click', { + content: item, + index, + position + }) + } + }, + onClickForPC(index, item, position) { + if (!is_pc) return + // #ifdef H5 + this.$emit('click', { + content: item, + index, + position + }) + // #endif + } + } +} + +// #endif +export default mpMixins diff --git a/uni_modules/uni-swipe-action/components/uni-swipe-action-item/render.js b/uni_modules/uni-swipe-action/components/uni-swipe-action-item/render.js new file mode 100644 index 0000000..78f0ec6 --- /dev/null +++ b/uni_modules/uni-swipe-action/components/uni-swipe-action-item/render.js @@ -0,0 +1,270 @@ +const MIN_DISTANCE = 10; +export default { + showWatch(newVal, oldVal, ownerInstance, instance, self) { + var state = self.state + var $el = ownerInstance.$el || ownerInstance.$vm && ownerInstance.$vm.$el + if (!$el) return + this.getDom(instance, ownerInstance, self) + if (newVal && newVal !== 'none') { + this.openState(newVal, instance, ownerInstance, self) + return + } + + if (state.left) { + this.openState('none', instance, ownerInstance, self) + } + this.resetTouchStatus(instance, self) + }, + + /** + * 开始触摸操作 + * @param {Object} e + * @param {Object} ins + */ + touchstart(e, ownerInstance, self) { + let instance = e.instance; + let disabled = instance.getDataset().disabled + let state = self.state; + this.getDom(instance, ownerInstance, self) + // fix by mehaotian, TODO 兼容 app-vue 获取dataset为字符串 , h5 获取 为 undefined 的问题,待框架修复 + disabled = this.getDisabledType(disabled) + if (disabled) return + // 开始触摸时移除动画类 + instance.requestAnimationFrame(function() { + instance.removeClass('ani'); + ownerInstance.callMethod('closeSwipe'); + }) + + // 记录上次的位置 + state.x = state.left || 0 + // 计算滑动开始位置 + this.stopTouchStart(e, ownerInstance, self) + }, + + /** + * 开始滑动操作 + * @param {Object} e + * @param {Object} ownerInstance + */ + touchmove(e, ownerInstance, self) { + let instance = e.instance; + // 删除之后已经那不到实例了 + if (!instance) return; + let disabled = instance.getDataset().disabled + let state = self.state + // fix by mehaotian, TODO 兼容 app-vue 获取dataset为字符串 , h5 获取 为 undefined 的问题,待框架修复 + disabled = this.getDisabledType(disabled) + if (disabled) return + // 是否可以滑动页面 + this.stopTouchMove(e, self); + if (state.direction !== 'horizontal') { + return; + } + if (e.preventDefault) { + // 阻止页面滚动 + e.preventDefault() + } + let x = state.x + state.deltaX + this.move(x, instance, ownerInstance, self) + }, + + /** + * 结束触摸操作 + * @param {Object} e + * @param {Object} ownerInstance + */ + touchend(e, ownerInstance, self) { + let instance = e.instance; + let disabled = instance.getDataset().disabled + let state = self.state + // fix by mehaotian, TODO 兼容 app-vue 获取dataset为字符串 , h5 获取 为 undefined 的问题,待框架修复 + disabled = this.getDisabledType(disabled) + + if (disabled) return + // 滑动过程中触摸结束,通过阙值判断是开启还是关闭 + // fixed by mehaotian 定时器解决点击按钮,touchend 触发比 click 事件时机早的问题 ,主要是 ios13 + this.moveDirection(state.left, instance, ownerInstance, self) + + }, + + /** + * 设置移动距离 + * @param {Object} value + * @param {Object} instance + * @param {Object} ownerInstance + */ + move(value, instance, ownerInstance, self) { + value = value || 0 + let state = self.state + let leftWidth = state.leftWidth + let rightWidth = state.rightWidth + // 获取可滑动范围 + state.left = this.range(value, -rightWidth, leftWidth); + instance.requestAnimationFrame(function() { + instance.setStyle({ + transform: 'translateX(' + state.left + 'px)', + '-webkit-transform': 'translateX(' + state.left + 'px)' + }) + }) + + }, + + /** + * 获取元素信息 + * @param {Object} instance + * @param {Object} ownerInstance + */ + getDom(instance, ownerInstance, self) { + var state = self.state + var $el = ownerInstance.$el || ownerInstance.$vm && ownerInstance.$vm.$el + var leftDom = $el.querySelector('.button-group--left') + var rightDom = $el.querySelector('.button-group--right') + + state.leftWidth = leftDom.offsetWidth || 0 + state.rightWidth = rightDom.offsetWidth || 0 + state.threshold = instance.getDataset().threshold + }, + + getDisabledType(value) { + return (typeof(value) === 'string' ? JSON.parse(value) : value) || false; + }, + + /** + * 获取范围 + * @param {Object} num + * @param {Object} min + * @param {Object} max + */ + range(num, min, max) { + return Math.min(Math.max(num, min), max); + }, + + + /** + * 移动方向判断 + * @param {Object} left + * @param {Object} value + * @param {Object} ownerInstance + * @param {Object} ins + */ + moveDirection(left, ins, ownerInstance, self) { + var state = self.state + var threshold = state.threshold + var position = state.position + var isopen = state.isopen || 'none' + var leftWidth = state.leftWidth + var rightWidth = state.rightWidth + if (state.deltaX === 0) { + this.openState('none', ins, ownerInstance, self) + return + } + if ((isopen === 'none' && rightWidth > 0 && -left > threshold) || (isopen !== 'none' && rightWidth > 0 && + rightWidth + + left < threshold)) { + // right + this.openState('right', ins, ownerInstance, self) + } else if ((isopen === 'none' && leftWidth > 0 && left > threshold) || (isopen !== 'none' && leftWidth > 0 && + leftWidth - left < threshold)) { + // left + this.openState('left', ins, ownerInstance, self) + } else { + // default + this.openState('none', ins, ownerInstance, self) + } + }, + + + /** + * 开启状态 + * @param {Boolean} type + * @param {Object} ins + * @param {Object} ownerInstance + */ + openState(type, ins, ownerInstance, self) { + let state = self.state + let leftWidth = state.leftWidth + let rightWidth = state.rightWidth + let left = '' + state.isopen = state.isopen ? state.isopen : 'none' + switch (type) { + case "left": + left = leftWidth + break + case "right": + left = -rightWidth + break + default: + left = 0 + } + + // && !state.throttle + + if (state.isopen !== type) { + state.throttle = true + ownerInstance.callMethod('change', { + open: type + }) + + } + + state.isopen = type + // 添加动画类 + ins.requestAnimationFrame(() => { + ins.addClass('ani'); + this.move(left, ins, ownerInstance, self) + }) + }, + + + getDirection(x, y) { + if (x > y && x > MIN_DISTANCE) { + return 'horizontal'; + } + if (y > x && y > MIN_DISTANCE) { + return 'vertical'; + } + return ''; + }, + + /** + * 重置滑动状态 + * @param {Object} event + */ + resetTouchStatus(instance, self) { + let state = self.state; + state.direction = ''; + state.deltaX = 0; + state.deltaY = 0; + state.offsetX = 0; + state.offsetY = 0; + }, + + /** + * 设置滑动开始位置 + * @param {Object} event + */ + stopTouchStart(event, ownerInstance, self) { + let instance = event.instance; + let state = self.state + this.resetTouchStatus(instance, self); + var touch = event.touches[0]; + state.startX = touch.clientX; + state.startY = touch.clientY; + }, + + /** + * 滑动中,是否禁止打开 + * @param {Object} event + */ + stopTouchMove(event, self) { + let instance = event.instance; + let state = self.state; + let touch = event.touches[0]; + + state.deltaX = touch.clientX - state.startX; + state.deltaY = touch.clientY - state.startY; + state.offsetY = Math.abs(state.deltaY); + state.offsetX = Math.abs(state.deltaX); + state.direction = state.direction || this.getDirection(state.offsetX, state.offsetY); + } +} diff --git a/uni_modules/uni-swipe-action/components/uni-swipe-action-item/uni-swipe-action-item.vue b/uni_modules/uni-swipe-action/components/uni-swipe-action-item/uni-swipe-action-item.vue new file mode 100644 index 0000000..a816e92 --- /dev/null +++ b/uni_modules/uni-swipe-action/components/uni-swipe-action-item/uni-swipe-action-item.vue @@ -0,0 +1,348 @@ + + + + + + diff --git a/uni_modules/uni-swipe-action/components/uni-swipe-action-item/wx.wxs b/uni_modules/uni-swipe-action/components/uni-swipe-action-item/wx.wxs new file mode 100644 index 0000000..b394244 --- /dev/null +++ b/uni_modules/uni-swipe-action/components/uni-swipe-action-item/wx.wxs @@ -0,0 +1,341 @@ +var MIN_DISTANCE = 10; + +/** + * 判断当前是否为H5、app-vue + */ +var IS_HTML5 = false +if (typeof window === 'object') IS_HTML5 = true + +/** + * 监听页面内值的变化,主要用于动态开关swipe-action + * @param {Object} newValue + * @param {Object} oldValue + * @param {Object} ownerInstance + * @param {Object} instance + */ +function showWatch(newVal, oldVal, ownerInstance, instance) { + var state = instance.getState() + getDom(instance, ownerInstance) + if (newVal && newVal !== 'none') { + openState(newVal, instance, ownerInstance) + return + } + + if (state.left) { + openState('none', instance, ownerInstance) + } + resetTouchStatus(instance) +} + +/** + * 开始触摸操作 + * @param {Object} e + * @param {Object} ins + */ +function touchstart(e, ownerInstance) { + var instance = e.instance; + var disabled = instance.getDataset().disabled + var state = instance.getState(); + getDom(instance, ownerInstance) + // fix by mehaotian, TODO 兼容 app-vue 获取dataset为字符串 , h5 获取 为 undefined 的问题,待框架修复 + disabled = (typeof(disabled) === 'string' ? JSON.parse(disabled) : disabled) || false; + if (disabled) return + // 开始触摸时移除动画类 + instance.requestAnimationFrame(function() { + instance.removeClass('ani'); + ownerInstance.callMethod('closeSwipe'); + }) + + // 记录上次的位置 + state.x = state.left || 0 + // 计算滑动开始位置 + stopTouchStart(e, ownerInstance) +} + +/** + * 开始滑动操作 + * @param {Object} e + * @param {Object} ownerInstance + */ +function touchmove(e, ownerInstance) { + var instance = e.instance; + var disabled = instance.getDataset().disabled + var state = instance.getState() + // fix by mehaotian, TODO 兼容 app-vue 获取dataset为字符串 , h5 获取 为 undefined 的问题,待框架修复 + disabled = (typeof(disabled) === 'string' ? JSON.parse(disabled) : disabled) || false; + if (disabled) return + // 是否可以滑动页面 + stopTouchMove(e); + if (state.direction !== 'horizontal') { + return; + } + + if (e.preventDefault) { + // 阻止页面滚动 + e.preventDefault() + } + + move(state.x + state.deltaX, instance, ownerInstance) +} + +/** + * 结束触摸操作 + * @param {Object} e + * @param {Object} ownerInstance + */ +function touchend(e, ownerInstance) { + var instance = e.instance; + var disabled = instance.getDataset().disabled + var state = instance.getState() + // fix by mehaotian, TODO 兼容 app-vue 获取dataset为字符串 , h5 获取 为 undefined 的问题,待框架修复 + disabled = (typeof(disabled) === 'string' ? JSON.parse(disabled) : disabled) || false; + + if (disabled) return + // 滑动过程中触摸结束,通过阙值判断是开启还是关闭 + // fixed by mehaotian 定时器解决点击按钮,touchend 触发比 click 事件时机早的问题 ,主要是 ios13 + moveDirection(state.left, instance, ownerInstance) + +} + +/** + * 设置移动距离 + * @param {Object} value + * @param {Object} instance + * @param {Object} ownerInstance + */ +function move(value, instance, ownerInstance) { + value = value || 0 + var state = instance.getState() + var leftWidth = state.leftWidth + var rightWidth = state.rightWidth + // 获取可滑动范围 + state.left = range(value, -rightWidth, leftWidth); + instance.requestAnimationFrame(function() { + instance.setStyle({ + transform: 'translateX(' + state.left + 'px)', + '-webkit-transform': 'translateX(' + state.left + 'px)' + }) + }) + +} + +/** + * 获取元素信息 + * @param {Object} instance + * @param {Object} ownerInstance + */ +function getDom(instance, ownerInstance) { + var state = instance.getState() + var leftDom = ownerInstance.selectComponent('.button-group--left') + var rightDom = ownerInstance.selectComponent('.button-group--right') + var leftStyles = { + width: 0 + } + var rightStyles = { + width: 0 + } + leftStyles = leftDom.getBoundingClientRect() + rightStyles = rightDom.getBoundingClientRect() + + state.leftWidth = leftStyles.width || 0 + state.rightWidth = rightStyles.width || 0 + state.threshold = instance.getDataset().threshold +} + +/** + * 获取范围 + * @param {Object} num + * @param {Object} min + * @param {Object} max + */ +function range(num, min, max) { + return Math.min(Math.max(num, min), max); +} + + +/** + * 移动方向判断 + * @param {Object} left + * @param {Object} value + * @param {Object} ownerInstance + * @param {Object} ins + */ +function moveDirection(left, ins, ownerInstance) { + var state = ins.getState() + var threshold = state.threshold + var position = state.position + var isopen = state.isopen || 'none' + var leftWidth = state.leftWidth + var rightWidth = state.rightWidth + if (state.deltaX === 0) { + openState('none', ins, ownerInstance) + return + } + if ((isopen === 'none' && rightWidth > 0 && -left > threshold) || (isopen !== 'none' && rightWidth > 0 && + rightWidth + + left < threshold)) { + // right + openState('right', ins, ownerInstance) + } else if ((isopen === 'none' && leftWidth > 0 && left > threshold) || (isopen !== 'none' && leftWidth > 0 && + leftWidth - left < threshold)) { + // left + openState('left', ins, ownerInstance) + } else { + // default + openState('none', ins, ownerInstance) + } +} + + +/** + * 开启状态 + * @param {Boolean} type + * @param {Object} ins + * @param {Object} ownerInstance + */ +function openState(type, ins, ownerInstance) { + var state = ins.getState() + var leftWidth = state.leftWidth + var rightWidth = state.rightWidth + var left = '' + state.isopen = state.isopen ? state.isopen : 'none' + switch (type) { + case "left": + left = leftWidth + break + case "right": + left = -rightWidth + break + default: + left = 0 + } + + // && !state.throttle + + if (state.isopen !== type) { + state.throttle = true + ownerInstance.callMethod('change', { + open: type + }) + + } + + state.isopen = type + // 添加动画类 + ins.requestAnimationFrame(function() { + ins.addClass('ani'); + move(left, ins, ownerInstance) + }) + // 设置最终移动位置,理论上只要进入到这个函数,肯定是要打开的 +} + + +function getDirection(x, y) { + if (x > y && x > MIN_DISTANCE) { + return 'horizontal'; + } + if (y > x && y > MIN_DISTANCE) { + return 'vertical'; + } + return ''; +} + +/** + * 重置滑动状态 + * @param {Object} event + */ +function resetTouchStatus(instance) { + var state = instance.getState(); + state.direction = ''; + state.deltaX = 0; + state.deltaY = 0; + state.offsetX = 0; + state.offsetY = 0; +} + +/** + * 设置滑动开始位置 + * @param {Object} event + */ +function stopTouchStart(event) { + var instance = event.instance; + var state = instance.getState(); + resetTouchStatus(instance); + var touch = event.touches[0]; + if (IS_HTML5 && isPC()) { + touch = event; + } + state.startX = touch.clientX; + state.startY = touch.clientY; +} + +/** + * 滑动中,是否禁止打开 + * @param {Object} event + */ +function stopTouchMove(event) { + var instance = event.instance; + var state = instance.getState(); + var touch = event.touches[0]; + if (IS_HTML5 && isPC()) { + touch = event; + } + state.deltaX = touch.clientX - state.startX; + state.deltaY = touch.clientY - state.startY; + state.offsetY = Math.abs(state.deltaY); + state.offsetX = Math.abs(state.deltaX); + state.direction = state.direction || getDirection(state.offsetX, state.offsetY); +} + +function isPC() { + var userAgentInfo = navigator.userAgent; + var Agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"]; + var flag = true; + for (var v = 0; v < Agents.length - 1; v++) { + if (userAgentInfo.indexOf(Agents[v]) > 0) { + flag = false; + break; + } + } + return flag; +} + +var movable = false + +function mousedown(e, ins) { + if (!IS_HTML5) return + if (!isPC()) return + touchstart(e, ins) + movable = true +} + +function mousemove(e, ins) { + if (!IS_HTML5) return + if (!isPC()) return + if (!movable) return + touchmove(e, ins) +} + +function mouseup(e, ins) { + if (!IS_HTML5) return + if (!isPC()) return + touchend(e, ins) + movable = false +} + +function mouseleave(e, ins) { + if (!IS_HTML5) return + if (!isPC()) return + movable = false +} + +module.exports = { + showWatch: showWatch, + touchstart: touchstart, + touchmove: touchmove, + touchend: touchend, + mousedown: mousedown, + mousemove: mousemove, + mouseup: mouseup, + mouseleave: mouseleave +} diff --git a/uni_modules/uni-swipe-action/components/uni-swipe-action/uni-swipe-action.vue b/uni_modules/uni-swipe-action/components/uni-swipe-action/uni-swipe-action.vue new file mode 100644 index 0000000..4971782 --- /dev/null +++ b/uni_modules/uni-swipe-action/components/uni-swipe-action/uni-swipe-action.vue @@ -0,0 +1,60 @@ + + + + + diff --git a/uni_modules/uni-swipe-action/package.json b/uni_modules/uni-swipe-action/package.json new file mode 100644 index 0000000..fc5dd8a --- /dev/null +++ b/uni_modules/uni-swipe-action/package.json @@ -0,0 +1,84 @@ +{ + "id": "uni-swipe-action", + "displayName": "uni-swipe-action 滑动操作", + "version": "1.3.10", + "description": "SwipeAction 滑动操作操作组件", + "keywords": [ + "", + "uni-ui", + "uniui", + "滑动删除", + "侧滑删除" + ], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": ["uni-scss"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "y", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} diff --git a/uni_modules/uni-swipe-action/readme.md b/uni_modules/uni-swipe-action/readme.md new file mode 100644 index 0000000..93a5cac --- /dev/null +++ b/uni_modules/uni-swipe-action/readme.md @@ -0,0 +1,11 @@ + + +## SwipeAction 滑动操作 +> **组件名:uni-swipe-action** +> 代码块: `uSwipeAction`、`uSwipeActionItem` + + +通过滑动触发选项的容器 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-swipe-action) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 \ No newline at end of file diff --git a/uni_modules/uni-swiper-dot/changelog.md b/uni_modules/uni-swiper-dot/changelog.md new file mode 100644 index 0000000..85cf54d --- /dev/null +++ b/uni_modules/uni-swiper-dot/changelog.md @@ -0,0 +1,12 @@ +## 1.2.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-swiper-dot](https://uniapp.dcloud.io/component/uniui/uni-swiper-dot) +## 1.1.0(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.0.6(2021-05-12) +- 新增 示例地址 +- 修复 示例项目缺少组件的Bug +## 1.0.5(2021-02-05) +- 调整为uni_modules目录规范 +- 新增 clickItem 事件,支持指示点控制轮播 +- 新增 支持 pc 可用 diff --git a/uni_modules/uni-swiper-dot/components/uni-swiper-dot/uni-swiper-dot.vue b/uni_modules/uni-swiper-dot/components/uni-swiper-dot/uni-swiper-dot.vue new file mode 100644 index 0000000..e66b6c7 --- /dev/null +++ b/uni_modules/uni-swiper-dot/components/uni-swiper-dot/uni-swiper-dot.vue @@ -0,0 +1,218 @@ + + + + + diff --git a/uni_modules/uni-swiper-dot/package.json b/uni_modules/uni-swiper-dot/package.json new file mode 100644 index 0000000..f2dd8d2 --- /dev/null +++ b/uni_modules/uni-swiper-dot/package.json @@ -0,0 +1,87 @@ +{ + "id": "uni-swiper-dot", + "displayName": "uni-swiper-dot 轮播图指示点", + "version": "1.2.0", + "description": "自定义轮播图指示点组件", + "keywords": [ + "uni-ui", + "uniui", + "轮播图指示点", + "dot", + "swiper" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": ["uni-scss"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-swiper-dot/readme.md b/uni_modules/uni-swiper-dot/readme.md new file mode 100644 index 0000000..7d397e2 --- /dev/null +++ b/uni_modules/uni-swiper-dot/readme.md @@ -0,0 +1,11 @@ + + +## SwiperDot 轮播图指示点 +> **组件名:uni-swiper-dot** +> 代码块: `uSwiperDot` + + +自定义轮播图指示点 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-swiper-dot) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 \ No newline at end of file diff --git a/uni_modules/uni-table/changelog.md b/uni_modules/uni-table/changelog.md new file mode 100644 index 0000000..842211c --- /dev/null +++ b/uni_modules/uni-table/changelog.md @@ -0,0 +1,29 @@ +## 1.2.4(2023-12-19) +- 修复 uni-tr只有一列时minWidth计算错误,列变化实时计算更新 +## 1.2.3(2023-03-28) +- 修复 在vue3模式下可能会出现错误的问题 +## 1.2.2(2022-11-29) +- 优化 主题样式 +## 1.2.1(2022-06-06) +- 修复 微信小程序存在无使用组件的问题 +## 1.2.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-table](https://uniapp.dcloud.io/component/uniui/uni-table) +## 1.1.0(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.0.7(2021-07-08) +- 新增 uni-th 支持 date 日期筛选范围 +## 1.0.6(2021-07-05) +- 新增 uni-th 支持 range 筛选范围 +## 1.0.5(2021-06-28) +- 新增 uni-th 筛选功能 +## 1.0.4(2021-05-12) +- 新增 示例地址 +- 修复 示例项目缺少组件的Bug +## 1.0.3(2021-04-16) +- 新增 sortable 属性,是否开启单列排序 +- 优化 表格多选逻辑 +## 1.0.2(2021-03-22) +- uni-tr 添加 disabled 属性,用于 type=selection 时,设置某行是否可由全选按钮控制 +## 1.0.1(2021-02-05) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-table/components/uni-table/uni-table.vue b/uni_modules/uni-table/components/uni-table/uni-table.vue new file mode 100644 index 0000000..21d9527 --- /dev/null +++ b/uni_modules/uni-table/components/uni-table/uni-table.vue @@ -0,0 +1,455 @@ + + + + + diff --git a/uni_modules/uni-table/components/uni-tbody/uni-tbody.vue b/uni_modules/uni-table/components/uni-tbody/uni-tbody.vue new file mode 100644 index 0000000..fbe1bdc --- /dev/null +++ b/uni_modules/uni-table/components/uni-tbody/uni-tbody.vue @@ -0,0 +1,29 @@ + + + + + diff --git a/uni_modules/uni-table/components/uni-td/uni-td.vue b/uni_modules/uni-table/components/uni-td/uni-td.vue new file mode 100644 index 0000000..9ce93e9 --- /dev/null +++ b/uni_modules/uni-table/components/uni-td/uni-td.vue @@ -0,0 +1,90 @@ + + + + + diff --git a/uni_modules/uni-table/components/uni-th/filter-dropdown.vue b/uni_modules/uni-table/components/uni-th/filter-dropdown.vue new file mode 100644 index 0000000..df22a71 --- /dev/null +++ b/uni_modules/uni-table/components/uni-th/filter-dropdown.vue @@ -0,0 +1,511 @@ + + + + + diff --git a/uni_modules/uni-table/components/uni-th/uni-th.vue b/uni_modules/uni-table/components/uni-th/uni-th.vue new file mode 100644 index 0000000..14889dd --- /dev/null +++ b/uni_modules/uni-table/components/uni-th/uni-th.vue @@ -0,0 +1,285 @@ + + + + + diff --git a/uni_modules/uni-table/components/uni-thead/uni-thead.vue b/uni_modules/uni-table/components/uni-thead/uni-thead.vue new file mode 100644 index 0000000..0dd18cd --- /dev/null +++ b/uni_modules/uni-table/components/uni-thead/uni-thead.vue @@ -0,0 +1,129 @@ + + + + + diff --git a/uni_modules/uni-table/components/uni-tr/table-checkbox.vue b/uni_modules/uni-table/components/uni-tr/table-checkbox.vue new file mode 100644 index 0000000..1089187 --- /dev/null +++ b/uni_modules/uni-table/components/uni-tr/table-checkbox.vue @@ -0,0 +1,179 @@ + + + + + diff --git a/uni_modules/uni-table/components/uni-tr/uni-tr.vue b/uni_modules/uni-table/components/uni-tr/uni-tr.vue new file mode 100644 index 0000000..4cb85f5 --- /dev/null +++ b/uni_modules/uni-table/components/uni-tr/uni-tr.vue @@ -0,0 +1,175 @@ + + + + + diff --git a/uni_modules/uni-table/i18n/en.json b/uni_modules/uni-table/i18n/en.json new file mode 100644 index 0000000..e32023c --- /dev/null +++ b/uni_modules/uni-table/i18n/en.json @@ -0,0 +1,9 @@ +{ + "filter-dropdown.reset": "Reset", + "filter-dropdown.search": "Search", + "filter-dropdown.submit": "Submit", + "filter-dropdown.filter": "Filter", + "filter-dropdown.gt": "Greater or equal to", + "filter-dropdown.lt": "Less than or equal to", + "filter-dropdown.date": "Date" +} diff --git a/uni_modules/uni-table/i18n/es.json b/uni_modules/uni-table/i18n/es.json new file mode 100644 index 0000000..9afd04b --- /dev/null +++ b/uni_modules/uni-table/i18n/es.json @@ -0,0 +1,9 @@ +{ + "filter-dropdown.reset": "Reiniciar", + "filter-dropdown.search": "Búsqueda", + "filter-dropdown.submit": "Entregar", + "filter-dropdown.filter": "Filtrar", + "filter-dropdown.gt": "Mayor o igual a", + "filter-dropdown.lt": "Menos que o igual a", + "filter-dropdown.date": "Fecha" +} diff --git a/uni_modules/uni-table/i18n/fr.json b/uni_modules/uni-table/i18n/fr.json new file mode 100644 index 0000000..b006237 --- /dev/null +++ b/uni_modules/uni-table/i18n/fr.json @@ -0,0 +1,9 @@ +{ + "filter-dropdown.reset": "Réinitialiser", + "filter-dropdown.search": "Chercher", + "filter-dropdown.submit": "Soumettre", + "filter-dropdown.filter": "Filtre", + "filter-dropdown.gt": "Supérieur ou égal à", + "filter-dropdown.lt": "Inférieur ou égal à", + "filter-dropdown.date": "Date" +} diff --git a/uni_modules/uni-table/i18n/index.js b/uni_modules/uni-table/i18n/index.js new file mode 100644 index 0000000..2469dd0 --- /dev/null +++ b/uni_modules/uni-table/i18n/index.js @@ -0,0 +1,12 @@ +import en from './en.json' +import es from './es.json' +import fr from './fr.json' +import zhHans from './zh-Hans.json' +import zhHant from './zh-Hant.json' +export default { + en, + es, + fr, + 'zh-Hans': zhHans, + 'zh-Hant': zhHant +} diff --git a/uni_modules/uni-table/i18n/zh-Hans.json b/uni_modules/uni-table/i18n/zh-Hans.json new file mode 100644 index 0000000..862af17 --- /dev/null +++ b/uni_modules/uni-table/i18n/zh-Hans.json @@ -0,0 +1,9 @@ +{ + "filter-dropdown.reset": "重置", + "filter-dropdown.search": "搜索", + "filter-dropdown.submit": "确定", + "filter-dropdown.filter": "筛选", + "filter-dropdown.gt": "大于等于", + "filter-dropdown.lt": "小于等于", + "filter-dropdown.date": "日期范围" +} diff --git a/uni_modules/uni-table/i18n/zh-Hant.json b/uni_modules/uni-table/i18n/zh-Hant.json new file mode 100644 index 0000000..64f8061 --- /dev/null +++ b/uni_modules/uni-table/i18n/zh-Hant.json @@ -0,0 +1,9 @@ +{ + "filter-dropdown.reset": "重置", + "filter-dropdown.search": "搜索", + "filter-dropdown.submit": "確定", + "filter-dropdown.filter": "篩選", + "filter-dropdown.gt": "大於等於", + "filter-dropdown.lt": "小於等於", + "filter-dropdown.date": "日期範圍" +} diff --git a/uni_modules/uni-table/package.json b/uni_modules/uni-table/package.json new file mode 100644 index 0000000..a52821b --- /dev/null +++ b/uni_modules/uni-table/package.json @@ -0,0 +1,83 @@ +{ + "id": "uni-table", + "displayName": "uni-table 表格", + "version": "1.2.4", + "description": "表格组件,多用于展示多条结构类似的数据,如", + "keywords": [ + "uni-ui", + "uniui", + "table", + "表格" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": ["uni-scss","uni-datetime-picker"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "n" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "n", + "QQ": "y" + }, + "快应用": { + "华为": "n", + "联盟": "n" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-table/readme.md b/uni_modules/uni-table/readme.md new file mode 100644 index 0000000..bb08c79 --- /dev/null +++ b/uni_modules/uni-table/readme.md @@ -0,0 +1,13 @@ + + +## Table 表单 +> 组件名:``uni-table``,代码块: `uTable`。 + +用于展示多条结构类似的数据 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-table) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 + + + + diff --git a/uni_modules/uni-tag/changelog.md b/uni_modules/uni-tag/changelog.md new file mode 100644 index 0000000..ddee87a --- /dev/null +++ b/uni_modules/uni-tag/changelog.md @@ -0,0 +1,23 @@ +## 2.1.1(2024-03-20) +- 优化 app下边框过窄导致不显示的bug +## 2.1.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-tag](https://uniapp.dcloud.io/component/uniui/uni-tag) +## 2.0.0(2021-11-09) +- 新增 提供组件设计资源,组件样式调整 +- 移除 插槽 +- 移除 type 属性的 royal 选项 +## 1.1.1(2021-08-11) +- type 不是 default 时,size 为 small 字体大小显示不正确 +## 1.1.0(2021-07-30) +- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.0.7(2021-06-18) +- 修复 uni-tag 在字节跳动小程序上 css 类名编译错误的 bug +## 1.0.6(2021-06-04) +- 修复 未定义 sass 变量 "$uni-color-royal" 的bug +## 1.0.5(2021-05-10) +- 修复 royal 类型无效的bug +- 修复 uni-tag 宽度不自适应的bug +- 新增 uni-tag 支持属性 custom-style 自定义样式 +## 1.0.4(2021-02-05) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-tag/components/uni-tag/uni-tag.vue b/uni_modules/uni-tag/components/uni-tag/uni-tag.vue new file mode 100644 index 0000000..7274436 --- /dev/null +++ b/uni_modules/uni-tag/components/uni-tag/uni-tag.vue @@ -0,0 +1,252 @@ + + + + + diff --git a/uni_modules/uni-tag/package.json b/uni_modules/uni-tag/package.json new file mode 100644 index 0000000..71b41eb --- /dev/null +++ b/uni_modules/uni-tag/package.json @@ -0,0 +1,84 @@ +{ + "id": "uni-tag", + "displayName": "uni-tag 标签", + "version": "2.1.1", + "description": "Tag 组件,用于展示1个或多个文字标签,可点击切换选中、不选中的状态。", + "keywords": [ + "uni-ui", + "uniui", + "", + "tag", + "标签" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": ["uni-scss"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-tag/readme.md b/uni_modules/uni-tag/readme.md new file mode 100644 index 0000000..6e78ff5 --- /dev/null +++ b/uni_modules/uni-tag/readme.md @@ -0,0 +1,13 @@ + + +## Tag 标签 +> **组件名:uni-tag** +> 代码块: `uTag` + + +用于展示1个或多个文字标签,可点击切换选中、不选中的状态 。 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-tag) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 + + diff --git a/uni_modules/uni-title/changelog.md b/uni_modules/uni-title/changelog.md new file mode 100644 index 0000000..7626216 --- /dev/null +++ b/uni_modules/uni-title/changelog.md @@ -0,0 +1,10 @@ +## 1.1.1(2022-05-19) +- 修改组件描述 +## 1.1.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-title](https://uniapp.dcloud.io/component/uniui/uni-title) +## 1.0.2(2021-05-12) +- 新增 示例地址 +- 修复 示例项目缺少组件的Bug +## 1.0.1(2021-02-05) +- 调整为uni_modules目录规范 diff --git a/uni_modules/uni-title/components/uni-title/uni-title.vue b/uni_modules/uni-title/components/uni-title/uni-title.vue new file mode 100644 index 0000000..bf4f926 --- /dev/null +++ b/uni_modules/uni-title/components/uni-title/uni-title.vue @@ -0,0 +1,171 @@ + + + + + diff --git a/uni_modules/uni-title/package.json b/uni_modules/uni-title/package.json new file mode 100644 index 0000000..2249f5a --- /dev/null +++ b/uni_modules/uni-title/package.json @@ -0,0 +1,88 @@ +{ + "id": "uni-title", + "displayName": "uni-title 章节标题", + "version": "1.1.1", + "description": "章节标题,通常用于记录页面标题,使用当前组件,uni-app 如果开启统计,将会自动统计页面标题", + "keywords": [ + "uni-ui", + "uniui", + "标题", + "章节", + "章节标题", + "" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, + "dcloudext": { + "category": [ + "前端组件", + "通用组件" + ], + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui" + }, + "uni_modules": { + "dependencies": ["uni-scss"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-title/readme.md b/uni_modules/uni-title/readme.md new file mode 100644 index 0000000..0e60b1b --- /dev/null +++ b/uni_modules/uni-title/readme.md @@ -0,0 +1,14 @@ + + +## Title 标题 +> **组件名:uni-title** +> 代码块: `uTitle` + + +章节标题,通常用于记录页面标题,使用当前组件,uni-app 如果开启统计,将会自动统计页面标题 。 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-title) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 + + + diff --git a/uni_modules/uni-tooltip/changelog.md b/uni_modules/uni-tooltip/changelog.md new file mode 100644 index 0000000..285b676 --- /dev/null +++ b/uni_modules/uni-tooltip/changelog.md @@ -0,0 +1,16 @@ +## 0.2.4(2024-04-23) +- 修复 弹出位置默认值不一致导致的错位 +## 0.2.3(2024-03-20) +- 修复 弹出位置修正 +## 0.2.2(2024-01-15) +- 新增 placement支持设置四个方向:top bottom left right +## 0.2.1(2022-05-09) +- 修复 content 为空时仍然弹出的bug +## 0.2.0(2022-05-07) +**注意:破坏性更新** +- 更新 text 属性变更为 content +- 更新 移除 width 属性 +## 0.1.1(2022-04-27) +- 修复 组件根 text 嵌套组件 warning +## 0.1.0(2022-04-21) +- 初始化 diff --git a/uni_modules/uni-tooltip/components/uni-tooltip/uni-tooltip.vue b/uni_modules/uni-tooltip/components/uni-tooltip/uni-tooltip.vue new file mode 100644 index 0000000..476a7dd --- /dev/null +++ b/uni_modules/uni-tooltip/components/uni-tooltip/uni-tooltip.vue @@ -0,0 +1,108 @@ + + + + + + diff --git a/uni_modules/uni-tooltip/package.json b/uni_modules/uni-tooltip/package.json new file mode 100644 index 0000000..44158e1 --- /dev/null +++ b/uni_modules/uni-tooltip/package.json @@ -0,0 +1,86 @@ +{ + "id": "uni-tooltip", + "displayName": "uni-tooltip 提示文字", + "version": "0.2.4", + "description": "Tooltip 提示文字", + "keywords": [ + "uni-tooltip", + "uni-ui", + "tooltip", + "tip", + "文字提示" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无 ", + "data": "无", + "permissions": "无" + }, + "npmurl": "", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "n" + }, + "client": { + "Vue": { + "vue2": "y", + "vue3": "y" + }, + "App": { + "app-vue": "y", + "app-nvue": "u" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "u", + "百度": "u", + "字节跳动": "u", + "QQ": "u", + "京东": "u" + }, + "快应用": { + "华为": "u", + "联盟": "u" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-tooltip/readme.md b/uni_modules/uni-tooltip/readme.md new file mode 100644 index 0000000..faafa2e --- /dev/null +++ b/uni_modules/uni-tooltip/readme.md @@ -0,0 +1,8 @@ +## Badge 数字角标 +> **组件名:uni-tooltip** +> 代码块: `uTooltip` + +数字角标一般和其它控件(列表、9宫格等)配合使用,用于进行数量提示,默认为实心灰色背景, + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-tooltip) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 diff --git a/uni_modules/uni-transition/changelog.md b/uni_modules/uni-transition/changelog.md new file mode 100644 index 0000000..faaf336 --- /dev/null +++ b/uni_modules/uni-transition/changelog.md @@ -0,0 +1,24 @@ +## 1.3.3(2024-04-23) +- 修复 当元素会受变量影响自动隐藏的bug +## 1.3.2(2023-05-04) +- 修复 NVUE 平台报错的问题 +## 1.3.1(2021-11-23) +- 修复 init 方法初始化问题 +## 1.3.0(2021-11-19) +- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource) +- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-transition](https://uniapp.dcloud.io/component/uniui/uni-transition) +## 1.2.1(2021-09-27) +- 修复 init 方法不生效的 Bug +## 1.2.0(2021-07-30) +- 组件兼容 vue3,如何创建 vue3 项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834) +## 1.1.1(2021-05-12) +- 新增 示例地址 +- 修复 示例项目缺少组件的 Bug +## 1.1.0(2021-04-22) +- 新增 通过方法自定义动画 +- 新增 custom-class 非 NVUE 平台支持自定义 class 定制样式 +- 优化 动画触发逻辑,使动画更流畅 +- 优化 支持单独的动画类型 +- 优化 文档示例 +## 1.0.2(2021-02-05) +- 调整为 uni_modules 目录规范 diff --git a/uni_modules/uni-transition/components/uni-transition/createAnimation.js b/uni_modules/uni-transition/components/uni-transition/createAnimation.js new file mode 100644 index 0000000..8f89b18 --- /dev/null +++ b/uni_modules/uni-transition/components/uni-transition/createAnimation.js @@ -0,0 +1,131 @@ +// const defaultOption = { +// duration: 300, +// timingFunction: 'linear', +// delay: 0, +// transformOrigin: '50% 50% 0' +// } +// #ifdef APP-NVUE +const nvueAnimation = uni.requireNativePlugin('animation') +// #endif +class MPAnimation { + constructor(options, _this) { + this.options = options + // 在iOS10+QQ小程序平台下,传给原生的对象一定是个普通对象而不是Proxy对象,否则会报parameter should be Object instead of ProxyObject的错误 + this.animation = uni.createAnimation({ + ...options + }) + this.currentStepAnimates = {} + this.next = 0 + this.$ = _this + + } + + _nvuePushAnimates(type, args) { + let aniObj = this.currentStepAnimates[this.next] + let styles = {} + if (!aniObj) { + styles = { + styles: {}, + config: {} + } + } else { + styles = aniObj + } + if (animateTypes1.includes(type)) { + if (!styles.styles.transform) { + styles.styles.transform = '' + } + let unit = '' + if(type === 'rotate'){ + unit = 'deg' + } + styles.styles.transform += `${type}(${args+unit}) ` + } else { + styles.styles[type] = `${args}` + } + this.currentStepAnimates[this.next] = styles + } + _animateRun(styles = {}, config = {}) { + let ref = this.$.$refs['ani'].ref + if (!ref) return + return new Promise((resolve, reject) => { + nvueAnimation.transition(ref, { + styles, + ...config + }, res => { + resolve() + }) + }) + } + + _nvueNextAnimate(animates, step = 0, fn) { + let obj = animates[step] + if (obj) { + let { + styles, + config + } = obj + this._animateRun(styles, config).then(() => { + step += 1 + this._nvueNextAnimate(animates, step, fn) + }) + } else { + this.currentStepAnimates = {} + typeof fn === 'function' && fn() + this.isEnd = true + } + } + + step(config = {}) { + // #ifndef APP-NVUE + this.animation.step(config) + // #endif + // #ifdef APP-NVUE + this.currentStepAnimates[this.next].config = Object.assign({}, this.options, config) + this.currentStepAnimates[this.next].styles.transformOrigin = this.currentStepAnimates[this.next].config.transformOrigin + this.next++ + // #endif + return this + } + + run(fn) { + // #ifndef APP-NVUE + this.$.animationData = this.animation.export() + this.$.timer = setTimeout(() => { + typeof fn === 'function' && fn() + }, this.$.durationTime) + // #endif + // #ifdef APP-NVUE + this.isEnd = false + let ref = this.$.$refs['ani'] && this.$.$refs['ani'].ref + if(!ref) return + this._nvueNextAnimate(this.currentStepAnimates, 0, fn) + this.next = 0 + // #endif + } +} + + +const animateTypes1 = ['matrix', 'matrix3d', 'rotate', 'rotate3d', 'rotateX', 'rotateY', 'rotateZ', 'scale', 'scale3d', + 'scaleX', 'scaleY', 'scaleZ', 'skew', 'skewX', 'skewY', 'translate', 'translate3d', 'translateX', 'translateY', + 'translateZ' +] +const animateTypes2 = ['opacity', 'backgroundColor'] +const animateTypes3 = ['width', 'height', 'left', 'right', 'top', 'bottom'] +animateTypes1.concat(animateTypes2, animateTypes3).forEach(type => { + MPAnimation.prototype[type] = function(...args) { + // #ifndef APP-NVUE + this.animation[type](...args) + // #endif + // #ifdef APP-NVUE + this._nvuePushAnimates(type, args) + // #endif + return this + } +}) + +export function createAnimation(option, _this) { + if(!_this) return + clearTimeout(_this.timer) + return new MPAnimation(option, _this) +} diff --git a/uni_modules/uni-transition/components/uni-transition/uni-transition.vue b/uni_modules/uni-transition/components/uni-transition/uni-transition.vue new file mode 100644 index 0000000..f3ddd1f --- /dev/null +++ b/uni_modules/uni-transition/components/uni-transition/uni-transition.vue @@ -0,0 +1,286 @@ + + + + + diff --git a/uni_modules/uni-transition/package.json b/uni_modules/uni-transition/package.json new file mode 100644 index 0000000..d5c20e1 --- /dev/null +++ b/uni_modules/uni-transition/package.json @@ -0,0 +1,85 @@ +{ + "id": "uni-transition", + "displayName": "uni-transition 过渡动画", + "version": "1.3.3", + "description": "元素的简单过渡动画", + "keywords": [ + "uni-ui", + "uniui", + "动画", + "过渡", + "过渡动画" +], + "repository": "https://github.com/dcloudio/uni-ui", + "engines": { + "HBuilderX": "" + }, + "directories": { + "example": "../../temps/example_temps" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", + "type": "component-vue" + }, + "uni_modules": { + "dependencies": ["uni-scss"], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "n" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uni-transition/readme.md b/uni_modules/uni-transition/readme.md new file mode 100644 index 0000000..2f8a77e --- /dev/null +++ b/uni_modules/uni-transition/readme.md @@ -0,0 +1,11 @@ + + +## Transition 过渡动画 +> **组件名:uni-transition** +> 代码块: `uTransition` + + +元素过渡动画 + +### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-transition) +#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 \ No newline at end of file diff --git a/uni_modules/uni-upgrade-center-app/changelog.md b/uni_modules/uni-upgrade-center-app/changelog.md new file mode 100644 index 0000000..555df08 --- /dev/null +++ b/uni_modules/uni-upgrade-center-app/changelog.md @@ -0,0 +1,98 @@ +## 0.8.1(2024-04-28) +- 修复 在 HX 4.0.3+ uni-app x 项目运行到 Android 调不起安装的Bug +## 0.8.0(2024-04-15) +- 修复 更新弹窗 data 中新增初始化字段 +## 0.7.9(2024-03-15) +- 移除无用代码 +- 调整 is_silently 类型为可为 null +## 0.7.8(2024-01-04) +- 新增 移除无用代码 +## 0.7.7(2024-01-04) +- 新增 uni-app x 项目中新增 @show 回调 +## 0.7.6(2023-12-21) +- 修复 iOS使用升级中心云打包时报错(使用新版的 [uts-progressNotification](https://ext.dcloud.net.cn/plugin?name=uts-progressNotification) 插件,如果之前下载过请删除 `uts-progressNotification\utssdk\app-ios` 文件夹) +## 0.7.5(2023-12-12) +- 新增 通知栏进度条使用 uts-progressNotification 插件 +- 新增 依赖 uni-installApk、uts-progressNotification。使用前要安装插件三方依赖 +## 0.7.4(2023-11-29) +- 修复 uni-app-x 项目中由上版引发的无法升级的Bug +## 0.7.3(2023-11-27) +- 修复 在 uni-app x 中无更新时报错的Bug +## 0.7.2(2023-11-20) +- 新增 插件根目录 utils 文件夹中新增 check-update-nvue.js 文件(vue2 的 nvue 页面请引用该文件) +## 0.7.1(2023-11-17) +- 修复 运行至浏览器 ts 语法报错 +## 0.7.0(2023-11-10) +- 新增 兼容 uni-app x 项目 [详情](https://uniapp.dcloud.net.cn/uniCloud/upgrade-center.html) +## 0.6.5(2023-10-27) +- 修复 安装 wgt 报错 manifest.json 文件不存在的Bug +## 0.6.4(2023-09-01) +chore: 优化代码结构 +## 0.6.3(2023-08-30) +- 修复 下载 wgt 时如果后缀名不正确,重命名后安装 +## 0.6.2(2022-11-21) +- 处理 cloudfunctions 目录 +## 0.6.1(2022-08-17) +- 修复 后台添加应用市场,但都没有启用的情况下报错的Bug (需要 uni-admin 1.9.3+) +## 0.6.0(2022-07-19) +- 新增 支持多应用商店配置(需要 uni-admin 1.9.3+) +## 0.4.1(2022-05-27) +- 修复 上版引出的报错问题 +## 0.4.0(2022-05-27) +- 新增 Android 支持跳转手机自带商店,填写升级包地址时请填写跳转商店链接 +- 新增 改为云对象调用方式,使用更直观 +## 0.3.3(2022-04-14) +- 修复 调用 check-update,当 code 为 0 时没有回调 +## 0.3.2(2022-01-12) +- 优化显示逻辑 +## 0.3.1(2021-11-24) +- 修复 vue3 上图片不显示的Bug +## 0.3.0(2021-11-18) +- 移除 wgt 安装成功后提示,防止重启过快弹框不消失 +## 0.2.2(2021-08-25) +- 兼容vue3.0 +## 0.2.1(2021-07-26) +- 修复 使用腾讯云并手动填写地址时,导致下载链接失效的bug +## 0.2.0(2021-07-13) +- 更新文档 关于报错local_storage_key 为空,请不要将页面路径设置为pages.json中第一项 +## 0.1.9(2021-06-28) +- 更新文档 +- 修复 wgt安装失败时,按钮状态不对 +## 0.1.8(2021-06-16) +- 修复 跳转安装时,导致上次下载的apk还没安装就被删掉的bug +## 0.1.7(2021-06-03) +- 修改 移除static中的图片 +## 0.1.6(2021-06-03) +- 修改 下载更新按钮使用CSS渐变色 +## 0.1.5(2021-04-22) +- 更新check-update函数。现在返回一个Promise,有更新时成功回调,其他情况错误回调 +## 0.1.4(2021-04-13) +- 更新文档。明确云函数调用结果 +## 0.1.3(2021-04-13) +- 解耦云函数与弹框处理。utils中新增 call-check-version.js,可用于单独检测是否有更新 +## 0.1.2(2021-04-07) +- 更新版本对比函数 compare +## 0.1.1(2021-04-07) +- 修复 腾讯云空间下载链接不能下载问题 +## 0.1.0(2021-04-07) +- 新增使用uni.showModal提示升级示例 +- 修改iOS升级提示方式 +## 0.0.7(2021-04-02) +- 修复在iOS上打开弹框报错 +## 0.0.6(2021-04-01) +- 兼容旧版本安卓 +## 0.0.5(2021-04-01) +- 修复低版本安卓上进度条错位 +## 0.0.4(2021-04-01) +- 更新readme +- 修复check-update语法错误 +## 0.0.3(2021-04-01) +- 新增前台更新弹框,详见readme +- 更新前台检查更新方法 + +## 0.0.2(2021-03-29) +- 更新文档 +- 移除 dependencies + +## 0.0.1(2021-03-25) +- 升级中心前台检查更新 diff --git a/uni_modules/uni-upgrade-center-app/components/uni-upgrade-center-app/uni-upgrade-center-app.uvue b/uni_modules/uni-upgrade-center-app/components/uni-upgrade-center-app/uni-upgrade-center-app.uvue new file mode 100644 index 0000000..6ad2810 --- /dev/null +++ b/uni_modules/uni-upgrade-center-app/components/uni-upgrade-center-app/uni-upgrade-center-app.uvue @@ -0,0 +1,485 @@ + + + + + diff --git a/uni_modules/uni-upgrade-center-app/images/app_update_close.png b/uni_modules/uni-upgrade-center-app/images/app_update_close.png new file mode 100644 index 0000000000000000000000000000000000000000..8b2ffe62cba2466f184ea9f8ee4f9395ed8cf37a GIT binary patch literal 7644 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 U2>e_sb7~9VO{|RT486$z2cgKqd;kCd literal 0 HcmV?d00001 diff --git a/uni_modules/uni-upgrade-center-app/images/bg_top.png b/uni_modules/uni-upgrade-center-app/images/bg_top.png new file mode 100644 index 0000000000000000000000000000000000000000..015f698cdd8c8caa63486a2bd87f5a96f17df630 GIT binary patch literal 30486 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 literal 0 HcmV?d00001 diff --git a/uni_modules/uni-upgrade-center-app/package.json b/uni_modules/uni-upgrade-center-app/package.json new file mode 100644 index 0000000..e647f39 --- /dev/null +++ b/uni_modules/uni-upgrade-center-app/package.json @@ -0,0 +1,86 @@ +{ + "id": "uni-upgrade-center-app", + "displayName": "升级中心 uni-upgrade-center - App", + "version": "0.8.1", + "description": "uni升级中心 - 客户端检查更新", + "keywords": [ + "uniCloud", + "update", + "升级", + "wgt" +], + "repository": "https://gitee.com/dcloud/uni-upgrade-center/tree/master/uni_modules/uni-upgrade-center-app", + "engines": { + "HBuilderX": "^4.03" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "插件不采集任何数据", + "permissions": "无" + }, + "npmurl": "", + "type": "unicloud-template-page" + }, + "uni_modules": { + "dependencies": [ + "uni-installApk", + "uts-progressNotification", + "uts-openSchema" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "u" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "u", + "阿里": "u", + "百度": "u", + "字节跳动": "u", + "QQ": "u", + "京东": "u" + }, + "快应用": { + "华为": "u", + "联盟": "u" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} diff --git a/uni_modules/uni-upgrade-center-app/pages/upgrade-popup.vue b/uni_modules/uni-upgrade-center-app/pages/upgrade-popup.vue new file mode 100644 index 0000000..86fba10 --- /dev/null +++ b/uni_modules/uni-upgrade-center-app/pages/upgrade-popup.vue @@ -0,0 +1,611 @@ + + + + + diff --git a/uni_modules/uni-upgrade-center-app/readme.md b/uni_modules/uni-upgrade-center-app/readme.md new file mode 100644 index 0000000..53ff781 --- /dev/null +++ b/uni_modules/uni-upgrade-center-app/readme.md @@ -0,0 +1 @@ +文档已移至 [uni-upgrade-center](https://uniapp.dcloud.net.cn/uniCloud/upgrade-center.html) \ No newline at end of file diff --git a/uni_modules/uni-upgrade-center-app/static/app_update_close.png b/uni_modules/uni-upgrade-center-app/static/app_update_close.png new file mode 100644 index 0000000000000000000000000000000000000000..8b2ffe62cba2466f184ea9f8ee4f9395ed8cf37a GIT binary patch literal 7644 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 U2>e_sb7~9VO{|RT486$z2cgKqd;kCd literal 0 HcmV?d00001 diff --git a/uni_modules/uni-upgrade-center-app/static/bg_top.png b/uni_modules/uni-upgrade-center-app/static/bg_top.png new file mode 100644 index 0000000000000000000000000000000000000000..015f698cdd8c8caa63486a2bd87f5a96f17df630 GIT binary patch literal 30486 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 literal 0 HcmV?d00001 diff --git a/uni_modules/uni-upgrade-center-app/uniCloud/database/db_init.json b/uni_modules/uni-upgrade-center-app/uniCloud/database/db_init.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/uni_modules/uni-upgrade-center-app/uniCloud/database/db_init.json @@ -0,0 +1 @@ +{} diff --git a/uni_modules/uni-upgrade-center-app/utils/call-check-version.js b/uni_modules/uni-upgrade-center-app/utils/call-check-version.js new file mode 100644 index 0000000..8c982ad --- /dev/null +++ b/uni_modules/uni-upgrade-center-app/utils/call-check-version.js @@ -0,0 +1,32 @@ +export default function() { + // #ifdef APP-PLUS + return new Promise((resolve, reject) => { + plus.runtime.getProperty(plus.runtime.appid, function(widgetInfo) { + let data = { + action: 'checkVersion', + appid: plus.runtime.appid, + appVersion: plus.runtime.version, + wgtVersion: widgetInfo.version + } + uniCloud.callFunction({ + name: 'uni-upgrade-center', + data, + success: (e) => { + console.log("e: ", e); + resolve(e) + }, + fail: (error) => { + reject(error) + } + }) + }) + }) + // #endif + // #ifndef APP-PLUS + return new Promise((resolve, reject) => { + reject({ + message: '请在App中使用' + }) + }) + // #endif +} diff --git a/uni_modules/uni-upgrade-center-app/utils/call-check-version.ts b/uni_modules/uni-upgrade-center-app/utils/call-check-version.ts new file mode 100644 index 0000000..4a0af00 --- /dev/null +++ b/uni_modules/uni-upgrade-center-app/utils/call-check-version.ts @@ -0,0 +1,120 @@ +export type StoreListItem = { + enable : boolean + id : string + name : string + scheme : string + priority : number // 优先级 +} + +export type UniUpgradeCenterResult = { + _id : string + appid : string + name : string + title : string + contents : string + url : string // 安装包下载地址 + platform : Array // Array<'Android' | 'iOS'> + version : string // 版本号 1.0.0 + uni_platform : string // "android" | "ios" // 版本号 1.0.0 + stable_publish : boolean // 是否是稳定版 + is_mandatory : boolean // 是否强制更新 + is_silently : boolean | null // 是否静默更新 + create_env : string // "upgrade-center" + create_date : number + message : string + code : number + + type : string // "native_app" | "wgt" + store_list : StoreListItem[] | null + min_uni_version : string | null // 升级 wgt 的最低 uni-app 版本 +} + +export default function () : Promise { + // #ifdef APP + return new Promise((resolve, reject) => { + const systemInfo = uni.getSystemInfoSync() + const appId = systemInfo.appId + const appVersion = systemInfo.appVersion //systemInfo.appVersion + // #ifndef UNI-APP-X + if (typeof appId === 'string' && typeof appVersion === 'string' && appId.length > 0 && appVersion.length > 0) { + plus.runtime.getProperty(appId, function (widgetInfo) { + if (widgetInfo.version) { + let data = { + action: 'checkVersion', + appid: appId, + appVersion: appVersion, + wgtVersion: widgetInfo.version + } + uniCloud.callFunction({ + name: 'uni-upgrade-center', + data, + success: (e) => { + resolve(e.result as UniUpgradeCenterResult) + }, + fail: (error) => { + reject(error) + } + }) + } else { + reject('widgetInfo.version is EMPTY') + } + }) + } else { + reject('plus.runtime.appid is EMPTY') + } + // #endif + // #ifdef UNI-APP-X + if (typeof appId === 'string' && typeof appVersion === 'string' && appId.length > 0 && appVersion.length > 0) { + let data = { + action: 'checkVersion', + appid: appId, + appVersion: appVersion, + is_uniapp_x: true, + wgtVersion: '0.0.0.0.0.1' + } + try { + uniCloud.callFunction({ + name: 'uni-upgrade-center', + data: data + }).then(res => { + const code = res.result['code'] + const codeIsNumber = ['Int', 'Long', 'number'].includes(typeof code) + if (codeIsNumber) { + if ((code as number) == 0) { + reject({ + code: res.result['code'], + message: res.result['message'] + }) + } else if ((code as number) < 0) { + reject({ + code: res.result['code'], + message: res.result['message'] + }) + } else { + const result = JSON.parse(JSON.stringify(res.result)) as UniUpgradeCenterResult + resolve(result) + } + } + }).catch((err : any | null) => { + const error = err as UniCloudError + if (error.errMsg == '未匹配到云函数[uni-upgrade-center]') + error.errMsg = '【uni-upgrade-center-app】未配置uni-upgrade-center,无法升级。参考: https://uniapp.dcloud.net.cn/uniCloud/upgrade-center.html' + reject(error.errMsg) + }) + } catch (e) { + reject(e.message) + } + } else { + reject('invalid appid or appVersion') + } + // #endif + }) + // #endif + // #ifndef APP + return new Promise((resolve, reject) => { + reject({ + message: '请在App中使用' + }) + }) + // #endif +} diff --git a/uni_modules/uni-upgrade-center-app/utils/check-update-nvue.js b/uni_modules/uni-upgrade-center-app/utils/check-update-nvue.js new file mode 100644 index 0000000..6c0d4bc --- /dev/null +++ b/uni_modules/uni-upgrade-center-app/utils/check-update-nvue.js @@ -0,0 +1,184 @@ +function callCheckVersion() { + // #ifdef APP-PLUS + return new Promise((resolve, reject) => { + plus.runtime.getProperty(plus.runtime.appid, function(widgetInfo) { + let data = { + action: 'checkVersion', + appid: plus.runtime.appid, + appVersion: plus.runtime.version, + wgtVersion: widgetInfo.version + } + uniCloud.callFunction({ + name: 'uni-upgrade-center', + data, + success: (e) => { + resolve(e) + }, + fail: (error) => { + reject(error) + } + }) + }) + }) + // #endif + // #ifndef APP-PLUS + return new Promise((resolve, reject) => {}) + // #endif +} + +// 推荐再App.vue中使用 +const PACKAGE_INFO_KEY = '__package_info__' + +export default function() { + // #ifdef APP-PLUS + return new Promise((resolve, reject) => { + callCheckVersion().then(async (e) => { + if (!e.result) return; + const { + code, + message, + is_silently, // 是否静默更新 + url, // 安装包下载地址 + platform, // 安装包平台 + type // 安装包类型 + } = e.result; + + // 此处逻辑仅为实例,可自行编写 + if (code > 0) { + // 腾讯云和阿里云下载链接不同,需要处理一下,阿里云会原样返回 + const { + fileList + } = await uniCloud.getTempFileURL({ + fileList: [url] + }); + if (fileList[0].tempFileURL) + e.result.url = fileList[0].tempFileURL; + + resolve(e) + + // 静默更新,只有wgt有 + if (is_silently) { + uni.downloadFile({ + url: e.result.url, + success: res => { + if (res.statusCode == 200) { + // 下载好直接安装,下次启动生效 + plus.runtime.install(res.tempFilePath, { + force: false + }); + } + } + }); + return; + } + + /** + * 提示升级一 + * 使用 uni.showModal + */ + // return updateUseModal(e.result) + + /** + * 提示升级二 + * 官方适配的升级弹窗,可自行替换资源适配UI风格 + */ + uni.setStorageSync(PACKAGE_INFO_KEY, e.result) + uni.navigateTo({ + url: `/uni_modules/uni-upgrade-center-app/pages/upgrade-popup?local_storage_key=${PACKAGE_INFO_KEY}`, + fail: (err) => { + console.error('更新弹框跳转失败', err) + uni.removeStorageSync(PACKAGE_INFO_KEY) + } + }) + + return + } else if (code < 0) { + // TODO 云函数报错处理 + console.error(message) + return reject(e) + } + return resolve(e) + }).catch(err => { + // TODO 云函数报错处理 + console.error(err.message) + reject(err) + }) + }); + // #endif +} + +/** + * 使用 uni.showModal 升级 + */ +function updateUseModal(packageInfo) { + const { + title, // 标题 + contents, // 升级内容 + is_mandatory, // 是否强制更新 + url, // 安装包下载地址 + platform, // 安装包平台 + type // 安装包类型 + } = packageInfo; + + let isWGT = type === 'wgt' + let isiOS = !isWGT ? platform.includes('iOS') : false; + let confirmText = isiOS ? '立即跳转更新' : '立即下载更新' + + return uni.showModal({ + title, + content: contents, + showCancel: !is_mandatory, + confirmText, + success: res => { + if (res.cancel) return; + + // 安装包下载 + if (isiOS) { + plus.runtime.openURL(url); + return; + } + + uni.showToast({ + title: '后台下载中……', + duration: 1000 + }); + + // wgt 和 安卓下载更新 + downloadTask = uni.downloadFile({ + url, + success: res => { + if (res.statusCode !== 200) { + console.error('下载安装包失败', err); + return; + } + // 下载好直接安装,下次启动生效 + plus.runtime.install(res.tempFilePath, { + force: false + }, () => { + if (is_mandatory) { + //更新完重启app + plus.runtime.restart(); + return; + } + uni.showModal({ + title: '安装成功是否重启?', + success: res => { + if (res.confirm) { + //更新完重启app + plus.runtime.restart(); + } + } + }); + }, err => { + uni.showModal({ + title: '更新失败', + content: err + .message, + showCancel: false + }); + }); + } + }); + } + }); +} diff --git a/uni_modules/uni-upgrade-center-app/utils/check-update.js b/uni_modules/uni-upgrade-center-app/utils/check-update.js new file mode 100644 index 0000000..38fe7b0 --- /dev/null +++ b/uni_modules/uni-upgrade-center-app/utils/check-update.js @@ -0,0 +1,158 @@ +import callCheckVersion from './call-check-version' + +// 推荐再App.vue中使用 +const PACKAGE_INFO_KEY = '__package_info__' + +export default function() { + // #ifdef APP-PLUS + return new Promise((resolve, reject) => { + callCheckVersion().then(async (e) => { + if (!e.result) return; + const { + code, + message, + is_silently, // 是否静默更新 + url, // 安装包下载地址 + platform, // 安装包平台 + type // 安装包类型 + } = e.result; + + // 此处逻辑仅为实例,可自行编写 + if (code > 0) { + // 腾讯云和阿里云下载链接不同,需要处理一下,阿里云会原样返回 + const { + fileList + } = await uniCloud.getTempFileURL({ + fileList: [url] + }); + if (fileList[0].tempFileURL) + e.result.url = fileList[0].tempFileURL; + + resolve(e) + + // 静默更新,只有wgt有 + if (is_silently) { + uni.downloadFile({ + url: e.result.url, + success: res => { + if (res.statusCode == 200) { + // 下载好直接安装,下次启动生效 + plus.runtime.install(res.tempFilePath, { + force: false + }); + } + } + }); + return; + } + + /** + * 提示升级一 + * 使用 uni.showModal + */ + // return updateUseModal(e.result) + + /** + * 提示升级二 + * 官方适配的升级弹窗,可自行替换资源适配UI风格 + */ + uni.setStorageSync(PACKAGE_INFO_KEY, e.result) + uni.navigateTo({ + url: `/uni_modules/uni-upgrade-center-app/pages/upgrade-popup?local_storage_key=${PACKAGE_INFO_KEY}`, + fail: (err) => { + console.error('更新弹框跳转失败', err) + uni.removeStorageSync(PACKAGE_INFO_KEY) + } + }) + + return + } else if (code < 0) { + // TODO 云函数报错处理 + console.error(message) + return reject(e) + } + return resolve(e) + }).catch(err => { + // TODO 云函数报错处理 + console.error(err.message) + reject(err) + }) + }); + // #endif +} + +/** + * 使用 uni.showModal 升级 + */ +function updateUseModal(packageInfo) { + const { + title, // 标题 + contents, // 升级内容 + is_mandatory, // 是否强制更新 + url, // 安装包下载地址 + platform, // 安装包平台 + type // 安装包类型 + } = packageInfo; + + let isWGT = type === 'wgt' + let isiOS = !isWGT ? platform.includes('iOS') : false; + let confirmText = isiOS ? '立即跳转更新' : '立即下载更新' + + return uni.showModal({ + title, + content: contents, + showCancel: !is_mandatory, + confirmText, + success: res => { + if (res.cancel) return; + + // 安装包下载 + if (isiOS) { + plus.runtime.openURL(url); + return; + } + + uni.showToast({ + title: '后台下载中……', + duration: 1000 + }); + + // wgt 和 安卓下载更新 + downloadTask = uni.downloadFile({ + url, + success: res => { + if (res.statusCode !== 200) { + console.error('下载安装包失败', err); + return; + } + // 下载好直接安装,下次启动生效 + plus.runtime.install(res.tempFilePath, { + force: false + }, () => { + if (is_mandatory) { + //更新完重启app + plus.runtime.restart(); + return; + } + uni.showModal({ + title: '安装成功是否重启?', + success: res => { + if (res.confirm) { + //更新完重启app + plus.runtime.restart(); + } + } + }); + }, err => { + uni.showModal({ + title: '更新失败', + content: err + .message, + showCancel: false + }); + }); + } + }); + } + }); +} diff --git a/uni_modules/uni-upgrade-center-app/utils/check-update.ts b/uni_modules/uni-upgrade-center-app/utils/check-update.ts new file mode 100644 index 0000000..bbaed13 --- /dev/null +++ b/uni_modules/uni-upgrade-center-app/utils/check-update.ts @@ -0,0 +1,191 @@ +import callCheckVersion, { UniUpgradeCenterResult } from "./call-check-version" +// #ifdef UNI-APP-X +import { openSchema } from '@/uni_modules/uts-openSchema' +// #endif + +// 推荐再App.vue中使用 +const PACKAGE_INFO_KEY = '__package_info__' + +// uni-app 项目无法从 vue 中导出 ComponentPublicInstance 类型,故使用条件编译 +// #ifdef UNI-APP-X +export default function (component : ComponentPublicInstance | null = null) : Promise { +// #endif +// #ifndef UNI-APP-X +export default function () : Promise { +// #endif + return new Promise((resolve, reject) => { + callCheckVersion().then(async (uniUpgradeCenterResult) => { + // NOTE uni-app x 3.96 解构有问题 + const code = uniUpgradeCenterResult.code + const message = uniUpgradeCenterResult.message + const url = uniUpgradeCenterResult.url // 安装包下载地址 + + // 此处逻辑仅为示例,可自行编写 + if (code > 0) { + // 腾讯云和阿里云下载链接不同,需要处理一下,阿里云会原样返回 + const tcbRes = await uniCloud.getTempFileURL({ fileList: [url] }); + if (typeof tcbRes.fileList[0].tempFileURL !== 'undefined') uniUpgradeCenterResult.url = tcbRes.fileList[0].tempFileURL; + + /** + * 提示升级一 + * 使用 uni.showModal + */ + // return updateUseModal(uniUpgradeCenterResult) + + // #ifndef UNI-APP-X + // 静默更新,只有wgt有 + if (uniUpgradeCenterResult.is_silently) { + uni.downloadFile({ + url, + success: res => { + if (res.statusCode == 200) { + // 下载好直接安装,下次启动生效 + plus.runtime.install(res.tempFilePath, { + force: false + }); + } + } + }); + return; + } + // #endif + + /** + * 提示升级二 + * 官方适配的升级弹窗,可自行替换资源适配UI风格 + */ + // #ifndef UNI-APP-X + uni.setStorageSync(PACKAGE_INFO_KEY, uniUpgradeCenterResult) + uni.navigateTo({ + url: `/uni_modules/uni-upgrade-center-app/pages/upgrade-popup?local_storage_key=${PACKAGE_INFO_KEY}`, + fail: (err) => { + console.error('更新弹框跳转失败', err) + uni.removeStorageSync(PACKAGE_INFO_KEY) + } + }) + // #endif + // #ifdef UNI-APP-X + component?.$callMethod('show', true, uniUpgradeCenterResult) + // #endif + + return resolve(uniUpgradeCenterResult) + } else if (code < 0) { + console.error(message) + return reject(uniUpgradeCenterResult) + } + return resolve(uniUpgradeCenterResult) + }).catch((err) => { + reject(err) + }) + }); + } + +/** + * 使用 uni.showModal 升级 + */ +function updateUseModal(packageInfo : UniUpgradeCenterResult) : void { + // #ifdef APP + const { + title, // 标题 + contents, // 升级内容 + is_mandatory, // 是否强制更新 + url, // 安装包下载地址 + type, + platform + } = packageInfo; + + let isWGT = type === 'wgt' + let isiOS = !isWGT ? platform.includes('iOS') : false; + + // #ifndef UNI-APP-X + let confirmText = isiOS ? '立即跳转更新' : '立即下载更新' + // #endif + // #ifdef UNI-APP-X + let confirmText = '立即下载更新' + // #endif + + return uni.showModal({ + title, + content: contents, + showCancel: !is_mandatory, + confirmText, + success: res => { + if (res.cancel) return; + + if (isiOS) { + // iOS 平台跳转 AppStore + // #ifndef UNI-APP-X + plus.runtime.openURL(url); + // #endif + // #ifdef UNI-APP-X + openSchema(url) + // #endif + return; + } + + uni.showToast({ + title: '后台下载中……', + duration: 1000 + }); + + // wgt 和 安卓下载更新 + uni.downloadFile({ + url, + success: res => { + if (res.statusCode !== 200) { + console.error('下载安装包失败'); + return; + } + // 下载好直接安装,下次启动生效 + // uni-app x 项目没有 plus5+ 故使用条件编译 + // #ifndef UNI-APP-X + plus.runtime.install(res.tempFilePath, { + force: false + }, () => { + if (is_mandatory) { + //更新完重启app + plus.runtime.restart(); + return; + } + uni.showModal({ + title: '安装成功是否重启?', + success: res => { + if (res.confirm) { + //更新完重启app + plus.runtime.restart(); + } + } + }); + }, err => { + uni.showModal({ + title: '更新失败', + content: err + .message, + showCancel: false + }); + }); + // #endif + + // #ifdef UNI-APP-X + uni.installApk({ + filePath: res.tempFilePath, + success: () => { + uni.showModal({ + title: '安装成功请手动重启' + }); + }, + fail: err => { + uni.showModal({ + title: '更新失败', + content: err.message, + showCancel: false + }); + } + }); + // #endif + } + }); + } + }); + // #endif +} diff --git a/uni_modules/uni-upgrade-center/changelog.md b/uni_modules/uni-upgrade-center/changelog.md new file mode 100644 index 0000000..c5f2301 --- /dev/null +++ b/uni_modules/uni-upgrade-center/changelog.md @@ -0,0 +1,55 @@ +## 0.6.2(2024-04-11) +- 更新 支持支付宝小程序云 +## 0.6.1(2023-11-02) +- 修复 输入更新内容时有长度限制的Bug +## 0.6.0(2023-02-24) +- 修复 升级中心安卓应用市场不显示的Bug +## 0.5.1(2022-07-06) +- 修复 上版带出云函数不存在的Bug +- 升级 uni-admin 大于等于 1.9.0 务必更新至此版本。uni-admin 版本小于 1.9.0 请不要更新,历史版本在 Gitee 有发行版。后续 uni-admin 会集成升级中心 +## 0.5.0(2022-07-05) +- 修复 版本列表默认显示全部版本的Bug +- 升级 uni-admin 1.9.0 务必更新至此版本。uni-admin 版本小于 1.9.0 请不要更新,后续 uni-admin 会集成升级中心 + +## 0.4.2(2021-12-07) +- 更新 优化 list 页面显示,修复 list 页面报错 +## 0.4.1(2021-12-01) +- 修复 0.4.0版本带出来,发布新版时 appid、name 不会自动填充的Bug +## 0.4.0(2021-11-26) +- 更新 升级中心移除应用管理,现在由uni-admin接管。旧版本若没有应用管理请做升级处理 +## 0.3.0(2021-11-18) +- 兼容 uni-admin 新版内置 $request 函数改动 +## 0.2.2(2021-09-06) +- 解决 opendb-app-list表对应的schema名称冲突的问题 +## 0.2.1(2021-09-03) +- 修复 一个在添加菜单时报错,createdate不与默认值匹配 的Bug +## 0.2.0(2021-08-25) +- 兼容vue3.0 +## 0.1.9(2021-08-13) +- 更新 uni-forms使用validate校验字段 +- 修复 报错dirty_data、create_date在数据库中并不存在 +## 0.1.8(2021-08-09) +- 修复 默认配置项配置错误 +## 0.1.7(2021-08-09) +- 移除测试时配置项 +## 0.1.6(2021-08-09) +- 修复 修改版本信息时,上传时间丢失问题 +## 0.1.5(2021-07-21) +- 更新 :value.sync 改为 :value 和 @update:value +## 0.1.4(2021-07-13) +- 修复 uni-easyinput去除输入字符长度限制 +- 更新文档 关于 uni-id缺少配置信息 错误。请查看安装指引第13条 +## 0.1.3(2021-06-15) +- 修复 wgt更新某些情况下获取数据错误 +## 0.1.2(2021-06-04) +- 修复 上传包时根据平台筛选文件 +- 更新 文档 +## 0.1.1(2021-05-18) +- 更新uni-table中uni-tr组件的selectable属性为disabled +## 0.1.0(2021-04-07) +- 更新版本对比函数 compare +## 0.0.6(2021-04-01) +- 调整db_init.json +## 0.0.5(2021-03-25) +- 调整为uni_modules目录 +- 升级中心后台管理系统拆分为 Admin 后台管理 和 前台检查更新(uni-upgrade-center-app) diff --git a/uni_modules/uni-upgrade-center/js_sdk/validator/opendb-app-list.js b/uni_modules/uni-upgrade-center/js_sdk/validator/opendb-app-list.js new file mode 100644 index 0000000..e3ebf83 --- /dev/null +++ b/uni_modules/uni-upgrade-center/js_sdk/validator/opendb-app-list.js @@ -0,0 +1,44 @@ + +// 表单校验规则由 schema2code 生成,不建议直接修改校验规则,而建议通过 schema2code 生成, 详情: https://uniapp.dcloud.net.cn/uniCloud/schema + + + +const validator = { + "appid": { + "rules": [ + { + "required": true + }, + { + "format": "string" + } + ], + "label": "AppID" + }, + "name": { + "rules": [ + { + "required": true + }, + { + "format": "string" + } + ], + "label": "应用名称" + }, + "description": { + "rules": [ + { + "required": true + }, + { + "format": "string" + } + ], + "label": "应用描述" + } +} + +const enumConverter = {} + +export { validator, enumConverter } diff --git a/uni_modules/uni-upgrade-center/js_sdk/validator/opendb-app-versions.js b/uni_modules/uni-upgrade-center/js_sdk/validator/opendb-app-versions.js new file mode 100644 index 0000000..553f113 --- /dev/null +++ b/uni_modules/uni-upgrade-center/js_sdk/validator/opendb-app-versions.js @@ -0,0 +1,151 @@ +// 表单校验规则由 schema2code 生成,不建议直接修改校验规则,而建议通过 schema2code 生成, 详情: https://uniapp.dcloud.net.cn/uniCloud/schema + + + +const validator = { + "appid": { + "rules": [{ + "required": true + }, + { + "format": "string" + } + ], + "label": "AppID" + }, + "name": { + "rules": [{ + "format": "string" + }], + "label": "应用名称" + }, + "title": { + "rules": [{ + "format": "string" + }], + "label": "更新标题" + }, + "contents": { + "rules": [{ + "required": true + }, + { + "format": "string" + } + ], + "label": "更新内容" + }, + "platform": { + "rules": [{ + "required": true + }, + /* 此处不校验数据类型,因为platform在发布app端是单选,在发布wgt时可能是多选 + { + "format": "array" + }, */ + { + "range": [{ + "value": "Android", + "text": "安卓" + }, + { + "value": "iOS", + "text": "苹果" + } + ] + } + ], + "label": "平台" + }, + "type": { + "rules": [{ + "required": true + }, { + "format": "string" + }, + { + "range": [{ + "value": "native_app", + "text": "原生App安装包" + }, + { + "value": "wgt", + "text": "wgt资源包" + } + ] + } + ], + "label": "安装包类型" + }, + "version": { + "rules": [{ + "required": true + }, + { + "format": "string" + } + ], + "label": "版本号" + }, + "min_uni_version": { + "rules": [{ + "format": "string" + }], + "label": "原生App最低版本" + }, + "url": { + "rules": [{ + "required": true + }, { + "format": "string" + }], + "label": "包地址" + }, + "stable_publish": { + "rules": [{ + "format": "bool" + }], + "label": "上线发行" + }, + "create_date": { + "rules": [{ + "format": "timestamp" + }], + "label": "上传时间" + }, + "is_silently": { + "rules": [{ + "format": "bool" + }], + "label": "静默更新", + "defaultValue": false + }, + "is_mandatory": { + "rules": [{ + "format": "bool" + }], + "label": "强制更新", + "defaultValue": false + } +} + +const enumConverter = { + "platform_valuetotext": [{ + "value": "Android", + "text": "安卓" + }, + { + "value": "iOS", + "text": "苹果" + } + ], + "type_valuetotext": { + "native_app": "原生App安装包", + "wgt": "wgt资源包" + } +} + +export { + validator, + enumConverter +} diff --git a/uni_modules/uni-upgrade-center/menu.json b/uni_modules/uni-upgrade-center/menu.json new file mode 100644 index 0000000..49fd9fa --- /dev/null +++ b/uni_modules/uni-upgrade-center/menu.json @@ -0,0 +1,10 @@ +[{ + "menu_id": "system_update", + "name": "升级中心", + "icon": "uni-icons-cloud-upload", + "url": "uni_modules/uni-upgrade-center/pages/version/list", + "sort": 1050, + "parent_id": "system_management", + "permission": [], + "enable": true +}] diff --git a/uni_modules/uni-upgrade-center/package.json b/uni_modules/uni-upgrade-center/package.json new file mode 100644 index 0000000..bd0658a --- /dev/null +++ b/uni_modules/uni-upgrade-center/package.json @@ -0,0 +1,92 @@ +{ + "id": "uni-upgrade-center", + "displayName": "升级中心 uni-upgrade-center - Admin", + "version": "0.6.2", + "description": "uni升级中心 - 后台管理系统", + "keywords": [ + "uniCloud", + "admin", + "update", + "升级", + "wgt" +], + "repository": "https://gitee.com/dcloud/uni-upgrade-center/tree/master/uni_modules/uni-upgrade-center", + "engines": { + "HBuilderX": "^3.3.10" + }, +"dcloudext": { + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "插件不采集任何数据", + "permissions": "无" + }, + "npmurl": "", + "type": "unicloud-admin" + }, + "uni_modules": { + "dependencies": [ + "uni-data-checkbox", + "uni-data-picker", + "uni-dateformat", + "uni-easyinput", + "uni-file-picker", + "uni-forms", + "uni-icons", + "uni-pagination", + "uni-table" + ], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "y" + }, + "client": { + "App": { + "app-vue": "y", + "app-nvue": "y" + }, + "H5-mobile": { + "Safari": "y", + "Android Browser": "y", + "微信浏览器(Android)": "y", + "QQ浏览器(Android)": "y" + }, + "H5-pc": { + "Chrome": "y", + "IE": "y", + "Edge": "y", + "Firefox": "y", + "Safari": "y" + }, + "小程序": { + "微信": "y", + "阿里": "y", + "百度": "y", + "字节跳动": "y", + "QQ": "y" + }, + "快应用": { + "华为": "y", + "联盟": "y" + }, + "Vue": { + "vue2": "y", + "vue3": "y" + } + } + } + } +} diff --git a/uni_modules/uni-upgrade-center/pages/components/show-info.vue b/uni_modules/uni-upgrade-center/pages/components/show-info.vue new file mode 100644 index 0000000..459eda6 --- /dev/null +++ b/uni_modules/uni-upgrade-center/pages/components/show-info.vue @@ -0,0 +1,52 @@ + + + + + diff --git a/uni_modules/uni-upgrade-center/pages/mixin/version_add_detail_mixin.js b/uni_modules/uni-upgrade-center/pages/mixin/version_add_detail_mixin.js new file mode 100644 index 0000000..7dc3324 --- /dev/null +++ b/uni_modules/uni-upgrade-center/pages/mixin/version_add_detail_mixin.js @@ -0,0 +1,209 @@ +import { + validator, + enumConverter +} from '@/js_sdk/validator/opendb-app-versions.js'; + +const platform_iOS = 'iOS'; +const platform_Android = 'Android'; +const db = uniCloud.database(); + +function getValidator(fields) { + let reuslt = {} + for (let key in validator) { + if (fields.includes(key)) { + reuslt[key] = validator[key] + } + } + return reuslt +} + +export const fields = + 'appid,name,title,contents,platform,type,version,min_uni_version,url,stable_publish,is_silently,is_mandatory,create_date,store_list' + +export default { + data() { + return { + labelWidth: '100px', + enableiOSWgt: true, // 是否开启iOS的wgt更新 + silentlyContent: '静默更新:App升级时会在后台下载wgt包并自行安装。新功能在下次启动App时生效', + mandatoryContent: '强制更新:App升级弹出框不可取消', + stablePublishContent: '同时只可有一个线上发行版,线上发行不可更设为下线。\n未上线可以设为上线发行并自动替换当前线上发行版', + stablePublishContent2: '使用本包替换当前线上发行版', + uploadFileContent: '可下载安装包地址。上传文件到云存储自动填写,也可以手动填写', + minUniVersionContent: '上次使用新Api或打包新模块的App版本', + priorityContent: '检查更新时,按照优先级从大到小依次尝试跳转商店。如果都跳转失败,则会打开浏览器使用下载链接下载apk安装包', + latestStableData: [], // 库中最新已上线版 + appFileList: null, // 上传包 + type_valuetotext: enumConverter.type_valuetotext, + preUrl: '', + formData: { + "appid": "", + "name": "", + "title": "", + "contents": "", + "platform": [], + "store_list": [], + "type": "", + "version": "", + "min_uni_version": "", + "url": "", + "stable_publish": false, + "create_date": null + }, + formOptions: { + "platform_localdata": [{ + "value": "Android", + "text": "安卓" + }, + { + "value": "iOS", + "text": "苹果" + } + ], + "type_localdata": [{ + "value": "native_app", + "text": "原生App安装包" + }, + { + "value": "wgt", + "text": "App资源包" + } + ] + }, + rules: { + ...getValidator([ + "appid", "contents", "platform", "type", + "version", "min_uni_version", "url", "stable_publish", + "title", "name", "is_silently", "is_mandatory", "store_list" + ]) + } + } + }, + onReady() { + this.$refs.form.setRules(this.rules) + }, + computed: { + isWGT() { + return this.formData.type === 'wgt' + }, + isiOS() { + return !this.isWGT ? this.formData.platform.includes(platform_iOS) : false; + }, + hasPackage() { + return this.appFileList && !!Object.keys(this.appFileList).length + }, + fileExtname() { + return this.isWGT ? ['wgt'] : ['apk'] + }, + platformLocaldata() { + return !this.isWGT ? this.formOptions.platform_localdata : this.enableiOSWgt ? this.formOptions + .platform_localdata : [this.formOptions.platform_localdata[0]] + }, + uni_platform() { + return (this.isiOS ? platform_iOS : platform_Android).toLocaleLowerCase() + } + }, + methods: { + getStoreList(appid) { + return db.collection('opendb-app-list') + .where({ + appid + }) + .get() + .then(res => { + const data = res.result.data[0] + return data ? data.store_list || [] : [] + }) + }, + packageUploadSuccess(res) { + uni.showToast({ + icon: 'success', + title: '上传成功', + duration: 800 + }) + this.preUrl = this.formData.url + this.formData.url = res.tempFilePaths[0] + }, + deleteFile(fileList) { + return this.$request('deleteFile', { + fileList + }, { + functionName: 'uni-upgrade-center' + }) + }, + async packageDelete(res) { + if (!this.hasPackage) return; + await this.deleteFile([res.tempFilePath]) + uni.showToast({ + icon: 'success', + title: '删除成功', + duration: 800 + }) + this.formData.url = this.preUrl + this.$refs.form.clearValidate('url') + }, + selectFile() { + if (this.hasPackage) { + uni.showToast({ + icon: 'none', + title: '只可上传一个文件,请删除已上传后重试', + duration: 1000 + }); + } + }, + createCenterRecord(value) { + return { + ...value, + uni_platform: this.uni_platform, + create_env: 'upgrade-center' + } + }, + createCenterQuery({ + appid + }) { + return { + appid, + create_env: 'upgrade-center' + } + }, + createStatQuery({ + appid, + type, + version, + uni_platform + }) { + return { + appid, + type, + version, + uni_platform: uni_platform ? uni_platform : this.uni_platform, + create_env: 'uni-stat', + stable_publish: false + } + }, + toUrl(url){ + // #ifdef H5 + window.open(url); + // #endif + // #ifndef H5 + uni.showToast({ + title: '请在浏览器中打开', + icon: 'none' + }); + // #endif + }, + getCloudStorageConfig(){ + return uni.getStorageSync('uni-admin-cloud-storage-config') || {}; + }, + setCloudStorageConfig(data={}){ + uni.setStorageSync('uni-admin-cloud-storage-config', data); + }, + // 临时方法,后面会优化 + setCloudStorage(data){ + // uniCloud.setCloudStorage 不是标准的API,临时挂载在uniCloud对象上的,后面会优化 + if (typeof uniCloud.setCloudStorage === "function") { + uniCloud.setCloudStorage(data); + } + } + } +} diff --git a/uni_modules/uni-upgrade-center/pages/utils.js b/uni_modules/uni-upgrade-center/pages/utils.js new file mode 100644 index 0000000..08a0926 --- /dev/null +++ b/uni_modules/uni-upgrade-center/pages/utils.js @@ -0,0 +1,26 @@ +// 判断arr是否为一个数组,返回一个bool值 +function isArray(arr) { + return Object.prototype.toString.call(arr) === '[object Array]'; +} + +// 深度克隆 +export function deepClone(obj) { + // 对常见的“非”值,直接返回原来值 + if ([null, undefined, NaN, false].includes(obj)) return obj; + if (typeof obj !== "object" && typeof obj !== 'function') { + //原始类型直接返回 + return obj; + } + let o = isArray(obj) ? [] : {}; + for (let i in obj) { + if (obj.hasOwnProperty(i)) { + o[i] = typeof obj[i] === "object" ? deepClone(obj[i]) : obj[i]; + } + } + return o; +} + +export const appListDbName = 'opendb-app-list' +export const appVersionListDbName = 'opendb-app-versions' +// 版本列表默认显示应用Appid +export const defaultDisplayApp = '' diff --git a/uni_modules/uni-upgrade-center/pages/version/add.vue b/uni_modules/uni-upgrade-center/pages/version/add.vue new file mode 100644 index 0000000..8b3ebbb --- /dev/null +++ b/uni_modules/uni-upgrade-center/pages/version/add.vue @@ -0,0 +1,478 @@ + + + + + diff --git a/uni_modules/uni-upgrade-center/pages/version/detail.vue b/uni_modules/uni-upgrade-center/pages/version/detail.vue new file mode 100644 index 0000000..ffcf11f --- /dev/null +++ b/uni_modules/uni-upgrade-center/pages/version/detail.vue @@ -0,0 +1,401 @@ + + + + diff --git a/uni_modules/uni-upgrade-center/pages/version/list.vue b/uni_modules/uni-upgrade-center/pages/version/list.vue new file mode 100644 index 0000000..fbc344e --- /dev/null +++ b/uni_modules/uni-upgrade-center/pages/version/list.vue @@ -0,0 +1,364 @@ + + + + diff --git a/uni_modules/uni-upgrade-center/readme.md b/uni_modules/uni-upgrade-center/readme.md new file mode 100644 index 0000000..46ec891 --- /dev/null +++ b/uni_modules/uni-upgrade-center/readme.md @@ -0,0 +1,233 @@ +## uni-admin 1.9.3+ 已内置,此插件不再维护 [点击查看文档](https://uniapp.dcloud.net.cn/uniCloud/upgrade-center.html) + +- `uni-admin < 1.9.0`:请前往 [Gitee](https://gitee.com/dcloud/uni-upgrade-center/releases) 下载 `tag v0.4.2` 版本使用 +-`1.9.0 <= uni-admin < 1.9.2` :请前往 [Gitee](https://gitee.com/dcloud/uni-upgrade-center/releases) 下载 `tag v0.5.1` 版本使用 +- `uni-admin >= 1.9.3` :uni-admin 已内置 升级中心,直接使用即可 [详情](https://uniapp.dcloud.io/uniCloud/admin.html#app-manager)。并且云函数 `upgrade-center` 废弃,使用 `uni-upgrade-center` 云函数。 + +# uni-upgrade-center - Admin + +### 概述 + +> 统一管理App及App在`Android`、`iOS`平台上`App安装包`和`wgt资源包`的发布升级 + +> 本插件为uni升级中心后台管理系统,客户端检查更新插件请点击查看 [uni-upgrade-center-app](https://ext.dcloud.net.cn/plugin?id=4542) + +### 基于uniCloud的App升级中心,本插件具有如下特征: + - 云端基于uniCloud云函数实现 + - 数据库遵循opendb规范 + - 遵循uni-Admin框架规范,可直接导入uni-admin项目中 + - 支持App整包升级及wgt资源包升级 + +## 升级中心解决了什么问题? + +升级中心是一款uni-admin插件,负责App版本更新业务。包含后台管理界面、更新检查逻辑,App内只要调用弹出提示即可。 + +升级中心有以下功能点: + +- 应用管理,对App的信息记录和应用版本管理 +- 版本管理,可以发布新版,也可方便直观的对当前App历史版本以及线上发行版本进行查看、编辑和删除操作 +- 版本发布信息管理,包括 更新标题,更新内容,版本号,静默更新,强制更新,灵活上线发行 的设置和修改 +- 原生App安装包,发布Apk更新,用于App的整包更新,可设置是否强制更新 +- wgt资源包,发布wgt更新,用于App的热更新,可设置是否强制更新,静默更新 +- App管理列表及App版本记录列表搜索 + +只需导入插件,初始化数据库即可拥有上述功能。 + +您也可以自己修改逻辑自定义数据库字段,和随意定制 UI 样式。 + +## 安装指引 + +1. 使用`HBuilderX 3.1.0+`,因为要使用到`uni_modules` + +2. 使用已有`uniCloud-admin`项目或新建项目:`打开HBuilderX` -> `文件` -> `新建` -> `项目` -> `uni-app` 选择 `uniCloud admin`模板,键入一个名字,确定 + +3. 鼠标右键选择`关联云服务空间`和`运行云服务空间初始化向导` + +3. 在插件市场打开本插件页面,在右侧点击`使用 HBuilderX 导入插件`,选择 `uniCloud admin` 项目点击确定 + +4. 等待下载安装完毕。由于本插件依赖一些uni-ui插件,下载完成后会显示合并插件页面,自行选择即可 + +5. 找到`/uni_modules/uni-upgrade-center/uniCloud/cloudfunctions/upgrade-center`,右键上传部署 + +7. 在`pages.json`中添加页面路径 +```json +//此结构与uniCloud admin中的pages.json结构一致 +{ + "pages": [ + // ……其他页面配置 + { + "path": "uni_modules/uni-upgrade-center/pages/version/list", + "style": { + "navigationBarTitleText": "版本列表" + } + }, { + "path": "uni_modules/uni-upgrade-center/pages/version/add", + "style": { + "navigationBarTitleText": "新版发布" + } + }, { + "path": "uni_modules/uni-upgrade-center/pages/version/detail", + "style": { + "navigationBarTitleText": "版本信息查看" + } + } + ] +} +``` + +8. 在`manifest.json -> 源码视图`中添加以下配置: + ```js + "networkTimeout":{ + "uploadFile":1200000 //ms, 如果不配置,上传大文件可能会超时 + } + ``` + +9. 运行项目到`Chrome` + +10. 添加菜单 + - `vue2` + + 运行起来uniCloud admin,菜单管理模块会自动读取`/uni_modules/uni-upgrade-center/menu.json`文件中的菜单配置,生成【待添加菜单】,选中升级中心,点击`添加选中的菜单`即可 +
+ +
+ - `vue3` + + 可将 `/uni_modules/uni-upgrade-center/menu.json` 拷贝至 `uniCloud/database/db_init.json` 中的 `opendb-admin-menus` 节点下,并右键初始化数据库即可。 +11. 添加成功后,就可以在左侧的菜单栏中找到`升级中心`菜单 + +
+ +
+ +12. 在进入`升级中心`之前: + 1. 需要到`uni-admin`的`应用管理`中添加一个应用,才可以在`升级中心`中发布对应应用的版本。 + 2. 当你有多个应用时,可以在`/uni_modules/uni-upgrade-center/pages/utils.js`中修改`defaultDisplayApp`字段来设置默认显示应用的`appid`。 + 3. 如果不设置或设置应用不存在则默认从数据库中查出来的第一个应用。 + +13. 由于插件依赖的uni-ui的一些组件,建议右键`/uni_modules/uni-upgrade-center`安装一下第三方依赖,否则可能会出现一些问题 + +14. 运行在`uniCloud`,由于本插件使用了`clientDB`,因此可能需要配置一下`uni-config-center插件`关于`uni-id`的配置信息。如提示`公用模块uni-id缺少配置信息`请这样做: + 1. 点击[uni-config-center](https://ext.dcloud.net.cn/plugin?id=4425)导入插件 + 2. 在`/uniCloud/cloudfunctions/common/uni-config-center/`下创建`uni-id`文件夹,文件夹内创建`config.json`文件。 + 3. 点击[config.json默认配置](https://uniapp.dcloud.net.cn/uniCloud/uni-id?id=start)。将内容拷贝至`config.json`中。**注:一定要把注释去除!** + +## 使用指南 + +### 升级中心 + +#### 应用列表 + +1. 点击菜单 `应用管理`,这里展示你所添加的 App,点击右上角 `新增` 可以新增一个 App + +
+ +
+ +2. 将App的信息都填写完善后,你可以在列表的操作列进行`修改`应用信息或者`删除`该应用。 + +**Tips** +- 删除应用会把该应用的所有版本记录同时删除 + +#### 版本管理 +1. 在版本管理list的右上角点击`发布新版`,可以发布`原生App安装包`和`wgt资源包`。在左上角点击`下拉列表`,可以切换展示应用。 + +
+ +
+ +- #### 发布原生App安装包 + 1. 在上传安装包界面填写此次发版信息 + +
+ +
+ + 2. `包地址` + - 可以选择手动上传一个文件到 `云存储`,会自动将地址填入该项 + + - 也可以手动填写一个地址,就可以不用再上传文件 + + - 如果是发布`苹果`版本,包地址则为 应用在`AppStore的链接` + + 3. `强制更新` + - 如果使用强制更新,App端接收到该字段后,App升级弹出框不可取消 + + 4. `上线发行` + - 可设置当前包是否上线发行,只有已上线才会进行更新检测 + + - 同时只可有一个线上发行版,线上发行不可更设为下线。未上线可以设为上线发行并自动替换当前线上发行版 + + - 修改当前包为上线发行,自动替换当前线上发行版 + + **注:版本号请填写以`.`分隔字符串,例如:`0.0.1`** +- #### 发布wgt资源包 + 1. 大部分配置与发布 `原生App安装包` 一致 + +
+ +
+ + 2. `原生App最低版本` + - 上次使用新Api或打包新模块的App版本 + + - 如果此次打包wgt使用了`新的api`或者打包了`新的模块`,则在发布 `wgt资源包` 的时候,将此版本更新为本次版本 + + - 如果已有正式版`wgt资源包`,则本次新增会自动带出 + + 2. `静默更新` + - App升级时会在后台下载wgt包并自行安装。新功能在下次启动App时生效 + - **静默更新后不重启应用,可能会导致正在访问的应用的页面数据错乱,请谨慎使用!** + + **注:版本号请填写以`.`分隔字符串,例如:`0.0.1`** + +- #### 发布完成页面 + +
+ +
+ +**Tips** + +1. `pages/system/upgradecenter/version/add.vue`中有版本对比函数(compare)。 + - 使用多段式版本格式(如:"3.0.0.0.0.1.0.1", "3.0.0.0.0.1")。如果不满足对比规则,请自行修改。 + +## 项目代码说明 + +### uniCloud 数据表 + +数据表基于 [openDB](https://gitee.com/dcloud/opendb/tree/master) 规范,它约定了一个标准用户表的表名和字段定义,并且基于 nosql 的特性,可以由开发者自行扩展字段。 + +本项目用到了 2 个表: + +- opendb-app-list:app管理列表。记录应用的 appid、name、description 用于展示。[详见](https://gitee.com/dcloud/opendb/tree/master/collection/opendb-app-list) +- opendb-app-versions:应用版本管理表。记录管理应用的版本信息。[详见](https://gitee.com/dcloud/opendb/tree/master/collection/opendb-app-versions) + +### 前端页面 + +点击`升级中心`,会进入应用管理列表,在这里你可以新增应用,或者在`应用详情`中查看、修改或删除一个已经录入的应用。 + +在应用管理列表中点击某个应用的`版本管理`,进入该应用的所有版本记录。列表排序为:先排序已上线版本,剩下已下线版本根据创建时间排列。 + +在应用版本列表中点击`详情`,即可进入该版本的信息详情中查看、修改或删除该记录。 + +**Tips** +- 升级中心设计之初就支持iOS的wgt更新 +- iOS的wgt更新肯定是违反apple政策的,注意事项: + - 审核期间请不要弹窗升级 + - 升级完后尽量不要自行重启 + - 尽量使用静默更新 +- 可以通过以下修改支持iOS的wgt更新: + > \uni_modules\uni-upgrade-center\pages\mixin\version_add_detail_mixin.js + > + > 将 `data` 中的 `enableiOSWgt: false` 中 改为 `enableiOSWgt: true` + +**常见问题** +- 以下问题可以通过升级插件版本解决: + - createdate不与默认值匹配 + - ["create_date"]在数据库中并不存在 + - 提交的字段["dirty_data"]在数据库中并不存在 + - 集合[opendb-app-list]对应的schema内存在错误,详细信息:opendb-app-list表对应的schema名称冲突,这是什么意思呢 + +- 没有/找不到 [opendb-app-list] 集合/表。**解决方案:**升级 admin 至 1.6.0+ 即可 +- 测试时发布了高版本的包,测试完了发布包提示需要大于版本号 (x.x.x)。**解决方案:**直接在控制台修改数据库 \ No newline at end of file diff --git a/uni_modules/uni-upgrade-center/uniCloud/database/opendb-app-list.schema.json b/uni_modules/uni-upgrade-center/uniCloud/database/opendb-app-list.schema.json new file mode 100644 index 0000000..346c073 --- /dev/null +++ b/uni_modules/uni-upgrade-center/uniCloud/database/opendb-app-list.schema.json @@ -0,0 +1,329 @@ +{ + "bsonType": "object", + "required": [ + "appid", + "name" + ], + "permission": { + "read": false, + "create": false, + "update": false, + "delete": false + }, + "properties": { + "_id": { + "description": "ID,系统自动生成" + }, + "appid": { + "bsonType": "string", + "description": "应用的AppID", + "label": "AppID", + "componentForEdit": { + "name": "uni-easyinput", + "props": { + ":disabled": true + } + } + }, + "name": { + "bsonType": "string", + "description": "应用名称", + "label": "应用名称", + "componentForEdit": { + "name": "uni-easyinput", + "props": { + ":disabled": true + } + } + }, + "description": { + "bsonType": "string", + "description": "应用描述", + "label": "应用描述", + "componentForEdit": { + "name": "textarea" + }, + "componentForShow": { + "name": "textarea", + "props": { + ":disabled": true + } + } + }, + "creator_uid": { + "description": "创建者的user_id,创建者必然是用户,不随应用转让而改变", + "bsonType": "string" + }, + "owner_type": { + "bsonType": "int", + "description": "应用当前归属者类型,1:个人,2:企业" + }, + "owner_id": { + "bsonType": "string", + "description": "应用当前归属者的id,user_id or enterprise_id" + }, + "managers": { + "bsonType": "array", + "description": "应用管理员ID列表" + }, + "members": { + "bsonType": "array", + "description": "团队成员ID列表" + }, + "icon_url": { + "bsonType": "string", + "trim": "both", + "description": "应用图标链接", + "label": "应用图标" + }, + "introduction": { + "bsonType": "string", + "trim": "both", + "description": "应用简介", + "label": "应用简介", + "componentForEdit": { + "name": "uni-easyinput", + "props": { + "disabled": true + } + } + }, + "screenshot": { + "bsonType": "array", + "description": "应用截图", + "label": "应用截图" + }, + "app_android": { + "bsonType": "object", + "description": "安卓 App 相关信息", + "properties": { + "name": { + "bsonType": "string", + "description": "快应用名称", + "label": "快应用名称" + }, + "url": { + "bsonType": "string", + "description": "安卓可下载安装包地址", + "label": "安卓下载地址" + } + } + }, + "app_ios": { + "bsonType": "object", + "description": "苹果 App 相关信息", + "properties": { + "name": { + "bsonType": "string", + "description": "快应用名称", + "label": "快应用名称" + }, + "url": { + "bsonType": "string", + "description": "AppStore 上架地址", + "label": "AppStore 地址" + } + } + }, + "mp_weixin": { + "bsonType": "object", + "description": "微信小程序相关信息", + "label": "微信小程序", + "properties": { + "name": { + "bsonType": "string", + "description": "小程序名字" + }, + "qrcode_url": { + "bsonType": "string", + "description": "二维码url" + } + } + }, + "mp_alipay": { + "bsonType": "object", + "description": "支付宝小程序相关信息", + "label": "支付宝小程序", + "properties": { + "name": { + "bsonType": "string", + "description": "小程序名字" + }, + "qrcode_url": { + "bsonType": "string", + "description": "二维码url" + } + } + }, + "mp_baidu": { + "bsonType": "object", + "description": "百度小程序相关信息", + "label": "百度小程序", + "properties": { + "name": { + "bsonType": "string", + "description": "小程序名字" + }, + "qrcode_url": { + "bsonType": "string", + "description": "二维码url" + } + } + }, + "mp_toutiao": { + "bsonType": "object", + "description": "头条小程序相关信息", + "label": "头条小程序", + "properties": { + "name": { + "bsonType": "string", + "description": "小程序名字" + }, + "qrcode_url": { + "bsonType": "string", + "description": "二维码url" + } + } + }, + "mp_qq": { + "bsonType": "object", + "description": "QQ小程序相关信息", + "label": "QQ小程序", + "properties": { + "name": { + "bsonType": "string", + "description": "小程序名字" + }, + "qrcode_url": { + "bsonType": "string", + "description": "二维码url" + } + } + }, + "mp_kuaishou": { + "bsonType": "object", + "description": "快手小程序相关信息", + "label": "快手小程序", + "properties": { + "name": { + "bsonType": "string", + "description": "小程序名字" + }, + "qrcode_url": { + "bsonType": "string", + "description": "二维码url" + } + } + }, + "mp_lark": { + "bsonType": "object", + "description": "飞书小程序相关信息", + "label": "飞书小程序", + "properties": { + "name": { + "bsonType": "string", + "description": "小程序名字" + }, + "qrcode_url": { + "bsonType": "string", + "description": "二维码url" + } + } + }, + "mp_jd": { + "bsonType": "object", + "description": "京东小程序相关信息", + "label": "京东小程序", + "properties": { + "name": { + "bsonType": "string", + "description": "小程序名字" + }, + "qrcode_url": { + "bsonType": "string", + "description": "二维码url" + } + } + }, + "mp_dingtalk": { + "bsonType": "object", + "description": "钉钉小程序相关信息", + "label": "钉钉小程序", + "properties": { + "name": { + "bsonType": "string", + "description": "小程序名字" + }, + "qrcode_url": { + "bsonType": "string", + "description": "二维码url" + } + } + }, + "h5": { + "bsonType": "object", + "properties": { + "url": { + "bsonType": "string", + "description": "H5 可访问链接" + } + } + }, + "quickapp": { + "bsonType": "object", + "properties": { + "name": { + "bsonType": "string", + "description": "快应用名称", + "label": "快应用名称" + }, + "qrcode_url": { + "bsonType": "string", + "description": "快应用二维码url" + } + } + }, + "store_list": { + "bsonType": "array", + "description": "发布的应用市场", + "label": "应用市场", + "properties": { + "id": { + "bsonType": "string", + "description": "应用id,自动生成", + "label": "id" + }, + "name": { + "bsonType": "string", + "description": "应用名称", + "label": "应用名称" + }, + "scheme": { + "bsonType": "string", + "description": "应用 scheme", + "label": "应用 scheme" + }, + "enable": { + "bsonType": "bool", + "description": "是否启用" + }, + "priority": { + "bsonType": "int", + "description": "按照从大到小排序", + "label": "优先级" + } + } + }, + "create_date": { + "bsonType": "timestamp", + "label": "创建时间", + "forceDefaultValue": { + "$env": "now" + }, + "componentForEdit": { + "name": "uni-dateformat" + } + } + }, + "version": "0.0.1" +} \ No newline at end of file diff --git a/uni_modules/uts-openSchema/changelog.md b/uni_modules/uts-openSchema/changelog.md new file mode 100644 index 0000000..cb0bf4d --- /dev/null +++ b/uni_modules/uts-openSchema/changelog.md @@ -0,0 +1,2 @@ +## 1.0.0(2024-04-25) +- 更新 在 Android 和 iOS 上打开链接的 UTS API diff --git a/uni_modules/uts-openSchema/package.json b/uni_modules/uts-openSchema/package.json new file mode 100644 index 0000000..78c3ee3 --- /dev/null +++ b/uni_modules/uts-openSchema/package.json @@ -0,0 +1,82 @@ +{ + "id": "uts-openSchema", + "displayName": "uts-openSchema", + "version": "1.0.0", + "description": "在 Android 和 iOS 上打开链接的 UTS API", + "keywords": [ + "uts-openSchema" +], + "repository": "", + "engines": { + "HBuilderX": "^4.0" + }, + "dcloudext": { + "type": "uts", + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "无", + "permissions": "无" + }, + "npmurl": "" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "y" + }, + "client": { + "Vue": { + "vue2": "n", + "vue3": "y" + }, + "App": { + "app-android": "y", + "app-ios": "y" + }, + "H5-mobile": { + "Safari": "n", + "Android Browser": "n", + "微信浏览器(Android)": "n", + "QQ浏览器(Android)": "n" + }, + "H5-pc": { + "Chrome": "n", + "IE": "n", + "Edge": "n", + "Firefox": "n", + "Safari": "n" + }, + "小程序": { + "微信": "n", + "阿里": "n", + "百度": "n", + "字节跳动": "n", + "QQ": "n", + "钉钉": "n", + "快手": "n", + "飞书": "n", + "京东": "n" + }, + "快应用": { + "华为": "n", + "联盟": "n" + } + } + } + } +} \ No newline at end of file diff --git a/uni_modules/uts-openSchema/readme.md b/uni_modules/uts-openSchema/readme.md new file mode 100644 index 0000000..7bda602 --- /dev/null +++ b/uni_modules/uts-openSchema/readme.md @@ -0,0 +1,6 @@ +# uts-openSchema +### 开发文档 +[UTS 语法](https://uniapp.dcloud.net.cn/tutorial/syntax-uts.html) +[UTS API插件](https://uniapp.dcloud.net.cn/plugin/uts-plugin.html) +[UTS 组件插件](https://uniapp.dcloud.net.cn/plugin/uts-component.html) +[Hello UTS](https://gitcode.net/dcloud/hello-uts) \ No newline at end of file diff --git a/uni_modules/uts-openSchema/utssdk/app-android/config.json b/uni_modules/uts-openSchema/utssdk/app-android/config.json new file mode 100644 index 0000000..bf95925 --- /dev/null +++ b/uni_modules/uts-openSchema/utssdk/app-android/config.json @@ -0,0 +1,3 @@ +{ + "minSdkVersion": "21" +} \ No newline at end of file diff --git a/uni_modules/uts-openSchema/utssdk/app-android/index.uts b/uni_modules/uts-openSchema/utssdk/app-android/index.uts new file mode 100644 index 0000000..660c99e --- /dev/null +++ b/uni_modules/uts-openSchema/utssdk/app-android/index.uts @@ -0,0 +1,15 @@ +import Intent from 'android.content.Intent' +import Uri from 'android.net.Uri' +import { OpenSchema } from '../interface.uts' + +export const openSchema: OpenSchema = function (url: string) { + if (typeof url === 'string' && url.length > 0) { + const context = UTSAndroid.getUniActivity()! + const uri = Uri.parse(url) + const intent = new Intent(Intent.ACTION_VIEW, uri) + intent.setData(uri) + context.startActivity(intent) + } else { + console.error('url param ERROR:', JSON.stringify(url)) + } +} diff --git a/uni_modules/uts-openSchema/utssdk/app-ios/config.json b/uni_modules/uts-openSchema/utssdk/app-ios/config.json new file mode 100644 index 0000000..f272043 --- /dev/null +++ b/uni_modules/uts-openSchema/utssdk/app-ios/config.json @@ -0,0 +1,3 @@ +{ + "deploymentTarget": "12.0" +} \ No newline at end of file diff --git a/uni_modules/uts-openSchema/utssdk/app-ios/index.uts b/uni_modules/uts-openSchema/utssdk/app-ios/index.uts new file mode 100644 index 0000000..6483e4f --- /dev/null +++ b/uni_modules/uts-openSchema/utssdk/app-ios/index.uts @@ -0,0 +1,14 @@ +import { OpenSchema } from '../interface.uts' + +export const openSchema: OpenSchema = function(url: string): void { + if (typeof url == 'string' && url.length > 0) { + let uri = new URL(string = url) + if (uri != null && UIApplication.shared.canOpenURL(uri!)) { + UIApplication.shared.open(uri!, options = new Map(), completionHandler = null) + }else { + console.error('url param Error: ', url) + } + }else { + console.error('url param Error: ', url) + } +} \ No newline at end of file diff --git a/uni_modules/uts-openSchema/utssdk/interface.uts b/uni_modules/uts-openSchema/utssdk/interface.uts new file mode 100644 index 0000000..e790e61 --- /dev/null +++ b/uni_modules/uts-openSchema/utssdk/interface.uts @@ -0,0 +1 @@ +export type OpenSchema = (url: string) => void diff --git a/uni_modules/uts-progressNotification/changelog.md b/uni_modules/uts-progressNotification/changelog.md new file mode 100644 index 0000000..8f90d3d --- /dev/null +++ b/uni_modules/uts-progressNotification/changelog.md @@ -0,0 +1,24 @@ +## 1.1.0(2024-03-08) +修复uniapp打包报错问题 +## 1.0.9(2024-02-29) +去除代码过时警告 +## 1.0.8(2023-12-21) +去除app-ios目录 +## 1.0.7(2023-12-11) +去除无用代码 +## 1.0.6(2023-12-11) +修改文档 +## 1.0.5(2023-12-11) +1.修改插件名称 +2.修改插件引入方式为import导入 +## 1.0.4(2023-11-30) +1. createNotificationProgress增加`onClick`回调 +2.修复在小米部分系统上,通知消息会归类于不重要通知的bug +## 1.0.3(2023-11-28) +更新截图 +## 1.0.2(2023-11-28) +修改资源的包名 +## 1.0.1(2023-11-28) +更新文档 +## 1.0.0(2023-11-28) +Android通知栏显示进度插件 diff --git a/uni_modules/uts-progressNotification/package.json b/uni_modules/uts-progressNotification/package.json new file mode 100644 index 0000000..d3a6dcc --- /dev/null +++ b/uni_modules/uts-progressNotification/package.json @@ -0,0 +1,83 @@ +{ + "id": "uts-progressNotification", + "displayName": "uts-progressNotification", + "version": "1.1.0", + "description": "uts-progressNotification", + "keywords": [ + "uts-progressNotification" +], + "repository": "", + "engines": { + "HBuilderX": "^3.91" + }, + "dcloudext": { + "type": "uts", + "sale": { + "regular": { + "price": "0.00" + }, + "sourcecode": { + "price": "0.00" + } + }, + "contact": { + "qq": "" + }, + "declaration": { + "ads": "无", + "data": "插件不采集任何数据", + "permissions": "TargetSDKVersion33以上时需配置\n`android.permission.POST_NOTIFICATIONS`" + }, + "npmurl": "" + }, + "uni_modules": { + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y" + }, + "client": { + "Vue": { + "vue2": "y", + "vue3": "y" + }, + "App": { + "app-android": { + "minVersion": "19" + }, + "app-ios": "n" + }, + "H5-mobile": { + "Safari": "n", + "Android Browser": "n", + "微信浏览器(Android)": "n", + "QQ浏览器(Android)": "n" + }, + "H5-pc": { + "Chrome": "n", + "IE": "n", + "Edge": "n", + "Firefox": "n", + "Safari": "n" + }, + "小程序": { + "微信": "n", + "阿里": "n", + "百度": "n", + "字节跳动": "n", + "QQ": "n", + "钉钉": "n", + "快手": "n", + "飞书": "n", + "京东": "n" + }, + "快应用": { + "华为": "n", + "联盟": "n" + } + } + } + } +} diff --git a/uni_modules/uts-progressNotification/readme.md b/uni_modules/uts-progressNotification/readme.md new file mode 100644 index 0000000..70e8d79 --- /dev/null +++ b/uni_modules/uts-progressNotification/readme.md @@ -0,0 +1,71 @@ +# uts-progressNotification + +## 使用说明 + +Android平台创建显示进度的通知栏消息 + +**注意: 需要自定义基座,否则点击通知栏消息不会拉起应用** + +### 导入 + +需要import导入插件 + +### createNotificationProgress(options : CreateNotificationProgressOptions) : void, + +创建显示进度的通知栏消息 + +参数说明 + +``` +export type CreateNotificationProgressOptions = { + /** + * 通知标题 + * @defaultValue 应用名称 + */ + title ?: string | null + /** + * 通知内容 + */ + content : string, + /** + * 进度 + */ + progress : number, + /** + * 点击通知消息回调 + * @defaultValue null + */ + onClick? : (() => void) | null +} +``` + +### finishNotificationProgress(options: FinishNotificationProgressOptions) : void + +完成时调用的API,比如下载完成后需要显示下载完成并隐藏进度时调用。 + +参数说明 + + +``` +export type FinishNotificationProgressOptions = { + /** + * 通知标题 + * @defaultValue 应用名称 + */ + title ?: string | null + /** + * 通知内容 + */ + content : string, + /** + * 点击通知消息回调 + */ + onClick : () => void +} +``` + + +### cancelNotificationProgress() : void + +取消通知消息显示 + diff --git a/uni_modules/uts-progressNotification/utssdk/app-android/AndroidManifest.xml b/uni_modules/uts-progressNotification/utssdk/app-android/AndroidManifest.xml new file mode 100644 index 0000000..bcc33ab --- /dev/null +++ b/uni_modules/uts-progressNotification/utssdk/app-android/AndroidManifest.xml @@ -0,0 +1,11 @@ + + + + + + + + diff --git a/uni_modules/uts-progressNotification/utssdk/app-android/TransparentActivity.uts b/uni_modules/uts-progressNotification/utssdk/app-android/TransparentActivity.uts new file mode 100644 index 0000000..5f521c0 --- /dev/null +++ b/uni_modules/uts-progressNotification/utssdk/app-android/TransparentActivity.uts @@ -0,0 +1,62 @@ +import Activity from "android.app.Activity"; +import Bundle from 'android.os.Bundle'; +import Build from 'android.os.Build'; +import View from 'android.view.View'; +import Color from 'android.graphics.Color'; +import WindowManager from 'android.view.WindowManager'; +import { globalNotificationProgressFinishCallBack, globalNotificationProgressCallBack } from './callbacks.uts'; +import { ACTION_DOWNLOAD_FINISH, ACTION_DOWNLOAD_PROGRESS } from "./constant.uts" + + +export class TransparentActivity extends Activity { + constructor() { + super() + } + + @Suppress("DEPRECATION") + override onCreate(savedInstanceState : Bundle | null) { + super.onCreate(savedInstanceState) + this.fullScreen(this) + const action = this.getIntent().getAction() + if (action == ACTION_DOWNLOAD_FINISH) { + setTimeout(() => { + globalNotificationProgressFinishCallBack() + globalNotificationProgressFinishCallBack = () => { } + }, 100) + this.overridePendingTransition(0, 0) + } + + if (action == ACTION_DOWNLOAD_PROGRESS) { + setTimeout(() => { + globalNotificationProgressCallBack?.() + globalNotificationProgressCallBack = () => { } + }, 100) + this.overridePendingTransition(0, 0) + } + + setTimeout(() => { + this.finish() + }, 20) + } + + + @Suppress("DEPRECATION") + private fullScreen(activity : Activity) { + if (Build.VERSION.SDK_INT >= 19) { + if (Build.VERSION.SDK_INT >= 21) { + const window = activity.getWindow(); + const decorView = window.getDecorView(); + const option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE; + decorView.setSystemUiVisibility(option); + window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + window.setStatusBarColor(Color.TRANSPARENT); + } else { + const window = activity.getWindow(); + const attributes = window.getAttributes(); + const flagTranslucentStatus = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; + attributes.flags |= flagTranslucentStatus; + window.setAttributes(attributes); + } + } + } +} diff --git a/uni_modules/uts-progressNotification/utssdk/app-android/callbacks.uts b/uni_modules/uts-progressNotification/utssdk/app-android/callbacks.uts new file mode 100644 index 0000000..410adf6 --- /dev/null +++ b/uni_modules/uts-progressNotification/utssdk/app-android/callbacks.uts @@ -0,0 +1,4 @@ + + +export let globalNotificationProgressCallBack : (() => void) | null = () => { } +export let globalNotificationProgressFinishCallBack = () => { } diff --git a/uni_modules/uts-progressNotification/utssdk/app-android/config.json b/uni_modules/uts-progressNotification/utssdk/app-android/config.json new file mode 100644 index 0000000..7ed4299 --- /dev/null +++ b/uni_modules/uts-progressNotification/utssdk/app-android/config.json @@ -0,0 +1,3 @@ +{ + "minSdkVersion": "19" +} \ No newline at end of file diff --git a/uni_modules/uts-progressNotification/utssdk/app-android/constant.uts b/uni_modules/uts-progressNotification/utssdk/app-android/constant.uts new file mode 100644 index 0000000..e89c210 --- /dev/null +++ b/uni_modules/uts-progressNotification/utssdk/app-android/constant.uts @@ -0,0 +1,2 @@ +export const ACTION_DOWNLOAD_FINISH = "ACTION_DOWNLOAD_FINISH" +export const ACTION_DOWNLOAD_PROGRESS = "ACTION_DOWNLOAD_PROGRESS" \ No newline at end of file diff --git a/uni_modules/uts-progressNotification/utssdk/app-android/index.uts b/uni_modules/uts-progressNotification/utssdk/app-android/index.uts new file mode 100644 index 0000000..941c35a --- /dev/null +++ b/uni_modules/uts-progressNotification/utssdk/app-android/index.uts @@ -0,0 +1,159 @@ +import Build from 'android.os.Build'; +import Context from 'android.content.Context'; +import NotificationManager from 'android.app.NotificationManager'; +import NotificationChannel from 'android.app.NotificationChannel'; +import Notification from 'android.app.Notification'; +import Intent from 'android.content.Intent'; +import ComponentName from 'android.content.ComponentName'; +import PendingIntent from 'android.app.PendingIntent'; +import { CreateNotificationProgressOptions, FinishNotificationProgressOptions } from '../interface.uts'; +import { ACTION_DOWNLOAD_FINISH, ACTION_DOWNLOAD_PROGRESS } from "./constant.uts" + +import { globalNotificationProgressFinishCallBack, globalNotificationProgressCallBack } from './callbacks.uts'; + +export { TransparentActivity } from './TransparentActivity.uts'; + + +const DOWNLOAD_PROGRESS_NOTIFICATION_ID : Int = 7890 +const DC_DOWNLOAD_CHANNEL_ID = "下载文件" +const DC_DOWNLOAD_CHANNEL_NAME = "用于显示现在进度的渠道" + + +let notificationBuilder : Notification.Builder | null = null + +let timeId = -1 + +let histroyProgress = 0 + +let isProgress = false + + + +export function createNotificationProgress(options : CreateNotificationProgressOptions) : void { + const { content, progress, onClick } = options + + if (progress == 100) { + clearTimeout(timeId) + const context = UTSAndroid.getAppContext() as Context + realCreateNotificationProgress(options.title ?? getAppName(context), content, progress, onClick) + reset() + return + } + + histroyProgress = progress + if (timeId != -1) { + return + } + + const context = UTSAndroid.getAppContext() as Context + if (!isProgress) { + realCreateNotificationProgress(options.title ?? getAppName(context), content, histroyProgress, onClick) + isProgress = true + } else { + timeId = setTimeout(() => { + realCreateNotificationProgress(options.title ?? getAppName(context), content, histroyProgress, onClick) + timeId = -1 + }, 1000) + } +} + + +export function cancelNotificationProgress() : void { + const context = UTSAndroid.getAppContext() as Context + const notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + notificationManager.cancel(DOWNLOAD_PROGRESS_NOTIFICATION_ID) + reset() +} + + +function realCreateNotificationProgress(title : string, content : string, progress : number, cb : (() => void) | null) : void { + globalNotificationProgressCallBack = cb + const context = UTSAndroid.getAppContext() as Context + const notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + createDownloadChannel(notificationManager) + const builder = createNotificationBuilder(context) + builder.setProgress(100, progress.toInt(), false) + builder.setContentTitle(title) + builder.setContentText(content) + builder.setContentIntent(createPendingIntent(context, ACTION_DOWNLOAD_PROGRESS)); + notificationManager.notify(DOWNLOAD_PROGRESS_NOTIFICATION_ID, builder.build()) +} + + +export function finishNotificationProgress(options : FinishNotificationProgressOptions) { + globalNotificationProgressFinishCallBack = options.onClick + const context = UTSAndroid.getAppContext() as Context + const notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + createDownloadChannel(notificationManager) + const builder = createNotificationBuilder(context) + builder.setProgress(0, 0, false) + builder.setContentTitle(options.title ?? getAppName(context)) + builder.setContentText(options.content) + //小米rom setOngoing未false的时候,会被通知管理器归为不重要通知 + // builder.setOngoing(false) + builder.setAutoCancel(true); + builder.setContentIntent(createPendingIntent(context, ACTION_DOWNLOAD_FINISH)); + notificationManager.notify(DOWNLOAD_PROGRESS_NOTIFICATION_ID, builder.build()) + reset() +} + +function reset() { + isProgress = false + notificationBuilder = null + histroyProgress = 0 + if (timeId != -1) { + clearTimeout(timeId) + timeId = -1 + } +} + + + +function createPendingIntent(context : Context, action : string) : PendingIntent { + const i = new Intent(action); + i.setComponent(new ComponentName(context.getPackageName(), "uts.sdk.modules.utsProgressNotification.TransparentActivity")); + let flags = PendingIntent.FLAG_ONE_SHOT; + if (Build.VERSION.SDK_INT >= 23) { + flags = PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_IMMUTABLE; + } + return PendingIntent.getActivity(context, DOWNLOAD_PROGRESS_NOTIFICATION_ID, i, flags); +} + + +function createDownloadChannel(notificationManager : NotificationManager) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + const channel = new NotificationChannel( + DC_DOWNLOAD_CHANNEL_ID, + DC_DOWNLOAD_CHANNEL_NAME, + NotificationManager.IMPORTANCE_LOW + ) + notificationManager.createNotificationChannel(channel) + } +} +@Suppress("DEPRECATION") +function createNotificationBuilder(context : Context) : Notification.Builder { + if (notificationBuilder == null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + notificationBuilder = new Notification.Builder(context, DC_DOWNLOAD_CHANNEL_ID) + } else { + notificationBuilder = new Notification.Builder(context) + } + notificationBuilder!.setSmallIcon(context.getApplicationInfo().icon) + notificationBuilder!.setOngoing(true) + notificationBuilder!.setSound(null) + } + return notificationBuilder! +} + +@Suppress("DEPRECATION") +function getAppName(context : Context) : string { + let appName = "" + try { + const packageManager = context.getPackageManager() + const applicationInfo = packageManager.getApplicationInfo(context.getPackageName(), 0) + appName = packageManager.getApplicationLabel(applicationInfo) as string + } catch (e : Exception) { + e.printStackTrace() + } + return appName +} diff --git a/uni_modules/uts-progressNotification/utssdk/app-android/res/values/notification_progress_styles.xml b/uni_modules/uts-progressNotification/utssdk/app-android/res/values/notification_progress_styles.xml new file mode 100644 index 0000000..cc01105 --- /dev/null +++ b/uni_modules/uts-progressNotification/utssdk/app-android/res/values/notification_progress_styles.xml @@ -0,0 +1,11 @@ + + + + \ No newline at end of file diff --git a/uni_modules/uts-progressNotification/utssdk/interface.uts b/uni_modules/uts-progressNotification/utssdk/interface.uts new file mode 100644 index 0000000..7d68ffe --- /dev/null +++ b/uni_modules/uts-progressNotification/utssdk/interface.uts @@ -0,0 +1,46 @@ +export type CreateNotificationProgressOptions = { + /** + * 通知标题 + * @defaultValue 应用名称 + */ + title ?: string | null + /** + * 通知内容 + */ + content : string, + /** + * 进度 + */ + progress : number, + /** + * 点击通知消息回调 + * @defaultValue null + */ + onClick? : (() => void) | null +} + + +export type FinishNotificationProgressOptions = { + /** + * 通知标题 + * @defaultValue 应用名称 + */ + title ?: string | null + /** + * 通知内容 + */ + content : string, + /** + * 点击通知消息回调 + */ + onClick : () => void +} + + +export type CreateNotificationProgress = (options : CreateNotificationProgressOptions) => void; + + +export type CancelNotificationProgress = () => void; + + +export type FinishNotificationProgress = (options: FinishNotificationProgressOptions) => void diff --git a/uni_modules/uts-progressNotification/utssdk/unierror.uts b/uni_modules/uts-progressNotification/utssdk/unierror.uts new file mode 100644 index 0000000..e69de29 diff --git a/utils/common.js b/utils/common.js new file mode 100644 index 0000000..953ad25 --- /dev/null +++ b/utils/common.js @@ -0,0 +1,73 @@ +import config from "@/app.config.js"; +export default { + /** + * 显示消息提示框 + * @param content 提示的标题 + */ + toast: function(content) { + uni.showToast({ + icon: 'none', + title: content + }) + }, + + /** + * 显示模态弹窗 + * @param content 提示的标题 + */ + + showConfirm: function(content) { + return new Promise((resolve, reject) => { + uni.showModal({ + title: '提示', + content: content, + cancelText: '取消', + confirmText: '确定', + success: function(res) { + resolve(res) + } + }) + }) + }, + + /** + * 参数处理,拼接request网址请求参数 + * @param params 参数 + */ + tansParams: function(params) { + let result = '' + for (const propName of Object.keys(params)) { + const value = params[propName] + var part = encodeURIComponent(propName) + "=" + if (value !== null && value !== "" && typeof(value) !== "undefined") { + if (typeof value === 'object') { + for (const key of Object.keys(value)) { + if (value[key] !== null && value[key] !== "" && typeof(value[key]) !== 'undefined') { + let params = propName + '[' + key + ']' + var subPart = encodeURIComponent(params) + "=" + result += subPart + encodeURIComponent(value[key]) + "&" + } + } + } else { + result += part + encodeURIComponent(value) + "&" + } + } + } + return result + }, + + /** + * 自定义调试模式 + */ + debug: function(e) { + if (config.isDebug) { + if (typeof(e) === "object") { + console.log("%c" + JSON.stringify(e), "color:#0095F6"); + console.log("%c >>>原始数据如下:", "color:#f00"); + console.log(e) + } else { + console.log("%c" + e, "color:#0095F6"); + } + } + } +} \ No newline at end of file diff --git a/utils/common2.js b/utils/common2.js new file mode 100644 index 0000000..6849b51 --- /dev/null +++ b/utils/common2.js @@ -0,0 +1,69 @@ +import config from "@/app.config.js"; + +/** + * 显示消息提示框 + * @param content 提示的标题 + */ +function toast(content) { + uni.showToast({ + icon: 'none', + title: content + }) +} +/** + * 显示模态弹窗 + * @param content 提示的标题 + */ + +function showConfirm(content) { + return new Promise((resolve, reject) => { + uni.showModal({ + title: '提示', + content: content, + cancelText: '取消', + confirmText: '确定', + success: function(res) { + resolve(res) + } + }) + }) +} + +/** + * 参数处理,拼接request网址请求参数 + * @param params 参数 + */ +function tansParams(params) { + let result = '' + for (const propName of Object.keys(params)) { + const value = params[propName] + var part = encodeURIComponent(propName) + "=" + if (value !== null && value !== "" && typeof(value) !== "undefined") { + if (typeof value === 'object') { + for (const key of Object.keys(value)) { + if (value[key] !== null && value[key] !== "" && typeof(value[key]) !== 'undefined') { + let params = propName + '[' + key + ']' + var subPart = encodeURIComponent(params) + "=" + result += subPart + encodeURIComponent(value[key]) + "&" + } + } + } else { + result += part + encodeURIComponent(value) + "&" + } + } + } + return result +} + +/** + * 自定义调试模式 + */ +function debug(e) { + if (config.isDebug) { + console.log("%c" + e, "#0095F6"); + } +} + +export default { + debug +} \ No newline at end of file