From 5aacab27ad2840b8be5ce67b321a59c6b7a6005b Mon Sep 17 00:00:00 2001 From: luyu Date: Mon, 14 Jul 2025 17:14:58 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App.vue | 32 + LICENSE | 21 + README.md | 57 +- androidPrivacy.json | 3 + index.html | 17 + jsconfig.json | 9 + main.js | 15 + manifest.json | 237 ++ package.json | 103 + pages.json | 712 ++++++ pages/activity/groupon/detail.vue | 539 +++++ pages/activity/groupon/list.vue | 225 ++ pages/activity/groupon/order.vue | 240 ++ pages/activity/index.vue | 214 ++ pages/activity/point/list.vue | 76 + pages/activity/seckill/list.vue | 460 ++++ pages/app/sign.vue | 452 ++++ pages/chat/components/goods.vue | 21 + pages/chat/components/messageInput.vue | 185 ++ pages/chat/components/messageList.vue | 293 +++ pages/chat/components/messageListItem.vue | 300 +++ pages/chat/components/order.vue | 114 + pages/chat/components/select-popup.vue | 151 ++ pages/chat/components/toolsPopup.vue | 166 ++ pages/chat/index.vue | 222 ++ pages/chat/util/constants.js | 19 + pages/chat/util/emoji.js | 58 + pages/chat/util/useWebSocket.js | 150 ++ pages/commission/commission-ranking.vue | 239 ++ pages/commission/components/account-info.vue | 125 + .../components/account-type-select.vue | 171 ++ .../commission/components/commission-auth.vue | 101 + .../commission/components/commission-info.vue | 114 + .../commission/components/commission-log.vue | 181 ++ .../commission/components/commission-menu.vue | 145 ++ pages/commission/goods.vue | 165 ++ pages/commission/index.vue | 57 + pages/commission/order.vue | 332 +++ pages/commission/promoter.vue | 288 +++ pages/commission/team.vue | 604 +++++ pages/commission/wallet.vue | 643 +++++ pages/commission/withdraw.vue | 484 ++++ pages/coupon/detail.vue | 390 +++ pages/coupon/list.vue | 222 ++ pages/goods/comment/add.vue | 190 ++ pages/goods/comment/list.vue | 168 ++ .../goods/components/detail/comment-item.vue | 94 + .../components/detail/detail-activity-tip.vue | 99 + .../components/detail/detail-cell-sku.vue | 31 + pages/goods/components/detail/detail-cell.vue | 60 + .../components/detail/detail-comment-card.vue | 106 + .../components/detail/detail-content-card.vue | 54 + .../goods/components/detail/detail-navbar.vue | 257 ++ .../components/detail/detail-progress.vue | 40 + .../components/detail/detail-skeleton.vue | 177 ++ .../goods/components/detail/detail-tabbar.vue | 169 ++ .../components/groupon/groupon-card-list.vue | 140 ++ .../goods/components/list/list-goods-card.vue | 103 + pages/goods/components/list/list-navbar.vue | 93 + pages/goods/groupon.vue | 562 +++++ pages/goods/index.vue | 672 ++++++ pages/goods/list.vue | 407 ++++ pages/goods/point.vue | 483 ++++ pages/goods/seckill.vue | 575 +++++ pages/index/cart.vue | 315 +++ pages/index/category.vue | 240 ++ pages/index/components/first-one.vue | 26 + pages/index/components/first-two.vue | 66 + pages/index/components/second-one.vue | 80 + pages/index/index.vue | 93 + pages/index/login.vue | 39 + pages/index/page.vue | 51 + pages/index/search.vue | 119 + pages/index/user.vue | 47 + pages/order/addressSelection.vue | 282 +++ pages/order/aftersale/apply.vue | 349 +++ pages/order/aftersale/detail.vue | 379 +++ pages/order/aftersale/list.vue | 209 ++ pages/order/aftersale/log-item.vue | 83 + pages/order/aftersale/log.vue | 38 + pages/order/aftersale/return-delivery.vue | 195 ++ pages/order/confirm.vue | 529 +++++ pages/order/detail.vue | 671 ++++++ pages/order/express/log.vue | 162 ++ pages/order/list.vue | 504 ++++ pages/order/pickUpVerify.vue | 260 ++ pages/pay/index.vue | 305 +++ pages/pay/recharge-log.vue | 167 ++ pages/pay/recharge.vue | 277 +++ pages/pay/result.vue | 339 +++ pages/public/error.vue | 60 + pages/public/faq.vue | 118 + pages/public/richtext.vue | 54 + pages/public/setting.vue | 236 ++ pages/public/webview.vue | 18 + pages/user/address/edit.vue | 305 +++ pages/user/address/list.vue | 165 ++ pages/user/goods-collect.vue | 233 ++ pages/user/goods-log.vue | 308 +++ pages/user/goods_details_store/index.vue | 282 +++ pages/user/info.vue | 467 ++++ pages/user/wallet/money.vue | 379 +++ pages/user/wallet/score.vue | 282 +++ sheep/api/index.js | 11 + sheep/api/infra/file.js | 67 + sheep/api/member/address.js | 53 + sheep/api/member/auth.js | 132 ++ sheep/api/member/point.js | 19 + sheep/api/member/signin.js | 37 + sheep/api/member/social.js | 76 + sheep/api/member/user.js | 85 + sheep/api/migration/app.js | 21 + sheep/api/migration/third.js | 18 + sheep/api/pay/channel.js | 14 + sheep/api/pay/order.js | 22 + sheep/api/pay/transfer.js | 14 + sheep/api/pay/wallet.js | 68 + sheep/api/product/category.js | 21 + sheep/api/product/comment.js | 22 + sheep/api/product/favorite.js | 54 + sheep/api/product/history.js | 39 + sheep/api/product/spu.js | 53 + sheep/api/promotion/activity.js | 16 + sheep/api/promotion/article.js | 12 + sheep/api/promotion/combination.js | 78 + sheep/api/promotion/coupon.js | 84 + sheep/api/promotion/diy.js | 38 + sheep/api/promotion/kefu.js | 31 + sheep/api/promotion/point.js | 30 + sheep/api/promotion/rewardActivity.js | 14 + sheep/api/promotion/seckill.js | 44 + sheep/api/system/area.js | 13 + sheep/api/system/dict.js | 16 + sheep/api/trade/afterSale.js | 63 + sheep/api/trade/brokerage.js | 111 + sheep/api/trade/cart.js | 50 + sheep/api/trade/config.js | 16 + sheep/api/trade/delivery.js | 31 + sheep/api/trade/order.js | 168 ++ .../s-activity-pop/s-activity-pop.vue | 256 ++ .../s-address-item/s-address-item.vue | 110 + .../s-auth-modal/components/account-login.vue | 111 + .../s-auth-modal/components/change-mobile.vue | 127 + .../components/change-password.vue | 106 + .../components/mp-authorization.vue | 152 ++ .../components/reset-password.vue | 119 + .../s-auth-modal/components/sms-login.vue | 138 ++ sheep/components/s-auth-modal/index.scss | 151 ++ .../components/s-auth-modal/s-auth-modal.vue | 301 +++ .../components/s-block-item/s-block-item.vue | 83 + sheep/components/s-block/s-block.vue | 54 + .../components/s-count-down/s-count-down.vue | 173 ++ .../s-coupon-block/s-coupon-block.vue | 192 ++ .../s-coupon-card/s-coupon-card.vue | 102 + .../components/s-coupon-get/s-coupon-get.vue | 109 + .../s-coupon-list/s-coupon-list.vue | 195 ++ .../s-coupon-select/s-coupon-select.vue | 149 ++ .../components/navbar-item.vue | 67 + .../s-custom-navbar/components/navbar.vue | 314 +++ .../s-custom-navbar/s-custom-navbar.vue | 207 ++ .../s-discount-list/s-discount-list.vue | 96 + sheep/components/s-empty/s-empty.vue | 93 + .../components/s-float-menu/s-float-menu.vue | 88 + .../components/s-goods-card/s-goods-card.vue | 306 +++ .../s-goods-column/s-goods-column.vue | 1026 ++++++++ .../components/s-goods-item/s-goods-item.vue | 189 ++ .../s-goods-scroll/s-goods-scroll.vue | 33 + .../s-goods-shelves/s-goods-shelves.vue | 147 ++ .../s-groupon-block/s-groupon-block.vue | 327 +++ .../s-hotzone-block/s-hotzone-block.vue | 46 + .../s-image-banner/s-image-banner.vue | 51 + .../s-image-block/s-image-block.vue | 27 + .../components/s-image-cube/s-image-cube.vue | 110 + sheep/components/s-layout/s-layout.vue | 258 ++ .../components/s-line-block/s-line-block.vue | 15 + .../components/s-live-block/s-live-block.vue | 144 ++ sheep/components/s-live-card/s-live-card.vue | 234 ++ .../s-menu-button/s-menu-button.vue | 343 +++ sheep/components/s-menu-grid/s-menu-grid.vue | 104 + sheep/components/s-menu-list/s-menu-list.vue | 66 + .../components/s-menu-tools/s-menu-tools.vue | 118 + .../s-notice-block/s-notice-block.vue | 38 + .../components/s-order-card/s-order-card.vue | 132 ++ .../s-point-block/s-point-block.vue | 328 +++ .../components/s-point-card/s-point-card.vue | 383 +++ .../s-popup-image/s-popup-image.vue | 85 + .../s-richtext-block/s-richtext-block.vue | 46 + .../s-search-block/s-search-block.vue | 164 ++ .../s-seckill-block/s-seckill-block.vue | 327 +++ .../s-select-groupon-sku.vue | 508 ++++ .../s-select-seckill-sku.vue | 453 ++++ .../components/s-select-sku/s-select-sku.vue | 464 ++++ .../s-share-modal/canvas-poster/index.vue | 168 ++ .../canvas-poster/poster/goods.js | 125 + .../canvas-poster/poster/groupon.js | 125 + .../canvas-poster/poster/index.js | 39 + .../canvas-poster/poster/user.js | 75 + .../s-share-modal/s-share-modal.vue | 195 ++ sheep/components/s-statusbar/s-statusbar.vue | 10 + sheep/components/s-tabbar/s-tabbar.vue | 94 + .../s-title-block/s-title-block.vue | 111 + .../s-uploader/choose-and-upload-file.js | 304 +++ sheep/components/s-uploader/s-uploader.vue | 677 ++++++ sheep/components/s-uploader/upload-file.vue | 335 +++ sheep/components/s-uploader/upload-image.vue | 306 +++ sheep/components/s-uploader/utils.js | 109 + sheep/components/s-user-card/s-user-card.vue | 184 ++ .../s-video-block/s-video-block.vue | 32 + .../s-wallet-card/s-wallet-card.vue | 119 + sheep/config/index.js | 31 + sheep/config/zIndex.js | 20 + sheep/helper/const.js | 153 ++ sheep/helper/digit.js | 168 ++ sheep/helper/index.js | 700 ++++++ sheep/helper/test.js | 285 +++ sheep/helper/throttle.js | 31 + sheep/helper/tools.js | 67 + sheep/helper/utils.js | 336 +++ sheep/hooks/useGoods.js | 499 ++++ sheep/hooks/useModal.js | 142 ++ sheep/index.js | 52 + sheep/libs/mplive-manifest-plugin.js | 32 + sheep/libs/permission.js | 244 ++ sheep/libs/sdk-h5-weixin.js | 194 ++ sheep/platform/index.js | 175 ++ sheep/platform/pay.js | 406 ++++ sheep/platform/provider/apple/app.js | 36 + sheep/platform/provider/apple/index.js | 9 + sheep/platform/provider/wechat/index.js | 15 + sheep/platform/provider/wechat/miniProgram.js | 241 ++ .../provider/wechat/officialAccount.js | 105 + .../platform/provider/wechat/openPlatform.js | 64 + sheep/platform/share.js | 213 ++ sheep/request/index.js | 304 +++ sheep/router/index.js | 185 ++ sheep/router/utils/strip-json-comments.js | 79 + sheep/router/utils/uni-read-pages-v3.js | 103 + sheep/scss/_main.scss | 354 +++ sheep/scss/_mixins.scss | 61 + sheep/scss/_tools.scss | 286 +++ sheep/scss/_var.scss | 163 ++ sheep/scss/font/OPPOSANS-M-subfont.ttf | Bin 0 -> 9832 bytes sheep/scss/icon/_coloricon.scss | 1340 +++++++++++ sheep/scss/icon/_icon.scss | 181 ++ sheep/scss/icon/_sheepicon.scss | 94 + sheep/scss/icon/_style.scss | 43 + sheep/scss/index.scss | 27 + sheep/scss/style/_avatar.scss | 0 sheep/scss/style/_background.scss | 204 ++ sheep/scss/style/_border.scss | 140 ++ sheep/scss/style/_button.scss | 87 + sheep/scss/style/_card.scss | 353 +++ sheep/scss/style/_code.scss | 55 + sheep/scss/style/_flex.scss | 79 + sheep/scss/style/_form.scss | 121 + sheep/scss/style/_grid.scss | 103 + sheep/scss/style/_markdown.scss | 62 + sheep/scss/style/_menu.scss | 54 + sheep/scss/style/_shadow.scss | 90 + sheep/scss/style/_table.scss | 133 ++ sheep/scss/style/_tag.scss | 0 sheep/scss/style/_text.scss | 104 + sheep/scss/theme/_dark.scss | 39 + sheep/scss/theme/_light.scss | 39 + sheep/scss/theme/_style.scss | 68 + sheep/scss/ui.scss | 19 + sheep/store/app.js | 152 ++ sheep/store/cart.js | 121 + sheep/store/index.js | 20 + sheep/store/modal.js | 29 + sheep/store/sys.js | 32 + sheep/store/user.js | 164 ++ sheep/ui/su-coupon/su-coupon.vue | 320 +++ .../ui/su-data-checkbox/su-data-checkbox.vue | 894 +++++++ sheep/ui/su-dialog/su-dialog.vue | 269 +++ sheep/ui/su-fixed/su-fixed.vue | 217 ++ sheep/ui/su-image/su-image.vue | 130 + sheep/ui/su-inner-navbar/su-inner-navbar.vue | 365 +++ sheep/ui/su-navbar/su-navbar.vue | 483 ++++ sheep/ui/su-notice-bar/su-notice-bar.vue | 473 ++++ sheep/ui/su-number-box/su-number-box.vue | 225 ++ sheep/ui/su-popover/su-popover.vue | 314 +++ sheep/ui/su-popup/keypress.js | 45 + sheep/ui/su-popup/su-popup.vue | 589 +++++ sheep/ui/su-progress/su-progress.vue | 203 ++ sheep/ui/su-radio/su-radio.vue | 301 +++ .../ui/su-region-picker/su-region-picker.vue | 247 ++ sheep/ui/su-status-bar/su-status-bar.vue | 16 + sheep/ui/su-sticky/su-sticky.vue | 266 +++ sheep/ui/su-subline/su-subline.vue | 62 + sheep/ui/su-swiper/su-swiper.vue | 502 ++++ sheep/ui/su-switch/su-switch.vue | 100 + sheep/ui/su-tab-item/su-tab-item.vue | 169 ++ sheep/ui/su-tab/su-tab.vue | 474 ++++ sheep/ui/su-tabbar-item/su-tabbar-item.vue | 237 ++ sheep/ui/su-tabbar/su-tabbar.vue | 227 ++ sheep/ui/su-tabs-item/props.js | 3 + sheep/ui/su-tabs-item/su-tabs-item.vue | 26 + sheep/ui/su-tabs/su-tabs.vue | 435 ++++ sheep/ui/su-time-line/su-time-line.vue | 37 + .../ui/su-timeline-item/su-timeline-item.vue | 76 + sheep/ui/su-toolbar/su-toolbar.vue | 129 + sheep/ui/su-video/components/dom-video.vue | 213 ++ sheep/ui/su-video/su-video.vue | 227 ++ sheep/url/index.js | 200 ++ sheep/validate/form.js | 164 ++ static/activity-left.png | Bin 0 -> 440 bytes static/activity-right.png | Bin 0 -> 1400 bytes static/cart-empty.png | Bin 0 -> 5933 bytes static/collect-empty.png | Bin 0 -> 5849 bytes static/comment-empty.png | Bin 0 -> 5603 bytes static/coupon-empty.png | Bin 0 -> 5245 bytes static/data-empty.png | Bin 0 -> 5053 bytes static/goods-empty.png | Bin 0 -> 3192 bytes static/internet-empty.png | Bin 0 -> 7003 bytes static/order-empty.png | Bin 0 -> 4406 bytes static/soldout-empty.png | Bin 0 -> 7012 bytes uni.scss | 76 + uni_modules/lime-painter/changelog.md | 225 ++ .../components/common/relation.js | 150 ++ .../l-painter-image/l-painter-image.vue | 28 + .../l-painter-qrcode/l-painter-qrcode.vue | 27 + .../l-painter-text/l-painter-text.vue | 33 + .../l-painter-view/l-painter-view.vue | 34 + .../components/l-painter/l-painter.vue | 461 ++++ .../lime-painter/components/l-painter/nvue.js | 214 ++ .../components/l-painter/painter.js | 1 + .../components/l-painter/props.js | 56 + .../components/l-painter/single.js | 1 + .../components/l-painter/utils.js | 368 +++ .../components/lime-painter/lime-painter.vue | 235 ++ .../lime-painter/hybrid/html/index.html | 119 + .../lime-painter/hybrid/html/painter.js | 1 + .../hybrid/html/uni.webview.1.5.3.js | 1 + uni_modules/lime-painter/package.json | 93 + uni_modules/lime-painter/parser.js | 388 +++ uni_modules/lime-painter/readme.md | 961 ++++++++ uni_modules/mp-html/README.md | 194 ++ uni_modules/mp-html/changelog.md | 150 ++ .../mp-html/components/mp-html/mp-html.vue | 498 ++++ .../mp-html/components/mp-html/node/node.vue | 587 +++++ .../mp-html/components/mp-html/parser.js | 1395 +++++++++++ uni_modules/mp-html/package.json | 76 + .../static/app-plus/mp-html/js/handler.js | 1 + .../app-plus/mp-html/js/uni.webview.min.js | 1 + .../static/app-plus/mp-html/local.html | 1 + 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-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-collapse/changelog.md | 36 + .../uni-collapse-item/uni-collapse-item.vue | 402 ++++ .../components/uni-collapse/uni-collapse.vue | 147 ++ uni_modules/uni-collapse/package.json | 89 + 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-countdown/changelog.md | 24 + .../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 | 271 +++ uni_modules/uni-countdown/package.json | 86 + uni_modules/uni-countdown/readme.md | 10 + uni_modules/uni-data-checkbox/changelog.md | 45 + .../uni-data-checkbox/uni-data-checkbox.vue | 817 +++++++ uni_modules/uni-data-checkbox/package.json | 84 + uni_modules/uni-data-checkbox/readme.md | 18 + uni_modules/uni-data-picker/changelog.md | 58 + .../components/uni-data-picker/keypress.js | 45 + .../uni-data-picker/uni-data-picker.vue | 539 +++++ .../uni-data-pickerview/uni-data-picker.js | 563 +++++ .../uni-data-pickerview.vue | 333 +++ uni_modules/uni-data-picker/package.json | 92 + uni_modules/uni-data-picker/readme.md | 22 + 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 | 89 + .../uni-datetime-picker/calendar-item.vue | 185 ++ .../uni-datetime-picker/calendar.js | 546 +++++ .../uni-datetime-picker/calendar.vue | 897 +++++++ .../uni-datetime-picker/i18n/en.json | 19 + .../uni-datetime-picker/i18n/index.js | 8 + .../uni-datetime-picker/i18n/zh-Hans.json | 19 + .../uni-datetime-picker/i18n/zh-Hant.json | 19 + .../uni-datetime-picker/keypress.js | 45 + .../uni-datetime-picker/time-picker.vue | 927 ++++++++ .../uni-datetime-picker.vue | 995 ++++++++ .../components/uni-datetime-picker/util.js | 410 ++++ uni_modules/uni-datetime-picker/package.json | 90 + 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 | 41 + .../components/uni-easyinput/common.js | 54 + .../uni-easyinput/uni-easyinput.vue | 538 +++++ uni_modules/uni-easyinput/package.json | 90 + uni_modules/uni-easyinput/readme.md | 11 + uni_modules/uni-fab/changelog.md | 17 + .../uni-fab/components/uni-fab/uni-fab.vue | 528 +++++ uni_modules/uni-fab/package.json | 87 + 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-forms/changelog.md | 60 + .../uni-forms-item/uni-forms-item.vue | 536 +++++ .../components/uni-forms/uni-forms.vue | 472 ++++ .../components/uni-forms/validate.js | 486 ++++ uni_modules/uni-forms/package.json | 90 + 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 | 140 ++ .../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 | 22 + .../uni-icons/components/uni-icons/icons.js | 1169 +++++++++ .../components/uni-icons/uni-icons.vue | 96 + .../uni-icons/components/uni-icons/uni.ttf | Bin 0 -> 26164 bytes .../components/uni-icons/uniicons.css | 663 ++++++ .../components/uni-icons/uniicons.ttf | Bin 0 -> 35760 bytes uni_modules/uni-icons/package.json | 86 + uni_modules/uni-icons/readme.md | 8 + 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 | 20 + .../components/uni-list-ad/uni-list-ad.vue | 107 + .../uni-list-chat/uni-list-chat.scss | 58 + .../uni-list-chat/uni-list-chat.vue | 538 +++++ .../uni-list-item/uni-list-item.vue | 473 ++++ .../uni-list/components/uni-list/uni-list.vue | 108 + .../components/uni-list/uni-refresh.vue | 65 + .../components/uni-list/uni-refresh.wxs | 87 + uni_modules/uni-list/package.json | 91 + 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 | 39 + .../components/uni-nav-bar/uni-nav-bar.vue | 348 +++ .../components/uni-nav-bar/uni-status-bar.vue | 27 + uni_modules/uni-nav-bar/package.json | 89 + uni_modules/uni-nav-bar/readme.md | 15 + uni_modules/uni-notice-bar/changelog.md | 16 + .../uni-notice-bar/uni-notice-bar.vue | 395 ++++ uni_modules/uni-notice-bar/package.json | 90 + uni_modules/uni-notice-bar/readme.md | 13 + uni_modules/uni-number-box/changelog.md | 25 + .../uni-number-box/uni-number-box.vue | 220 ++ uni_modules/uni-number-box/package.json | 85 + uni_modules/uni-number-box/readme.md | 13 + 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/manifest.json | 95 + 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 | 33 + .../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 | 298 +++ uni_modules/uni-search-bar/package.json | 89 + uni_modules/uni-search-bar/readme.md | 14 + .../uni-segmented-control/changelog.md | 9 + .../uni-segmented-control.vue | 145 ++ .../uni-segmented-control/package.json | 87 + uni_modules/uni-segmented-control/readme.md | 13 + uni_modules/uni-steps/changelog.md | 16 + .../components/uni-steps/uni-steps.vue | 269 +++ uni_modules/uni-steps/package.json | 89 + uni_modules/uni-steps/readme.md | 13 + 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-tag/changelog.md | 21 + .../uni-tag/components/uni-tag/uni-tag.vue | 252 ++ uni_modules/uni-tag/package.json | 87 + 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 | 10 + .../components/uni-tooltip/uni-tooltip.vue | 68 + uni_modules/uni-tooltip/package.json | 83 + uni_modules/uni-tooltip/readme.md | 8 + uni_modules/uni-transition/changelog.md | 20 + .../uni-transition/createAnimation.js | 128 + .../uni-transition/uni-transition.vue | 299 +++ uni_modules/uni-transition/package.json | 87 + uni_modules/uni-transition/readme.md | 11 + uni_modules/z-paging/types/comps.d.ts | 11 + uni_modules/z-paging/types/comps/_common.d.ts | 9 + .../z-paging/types/comps/z-paging-cell.d.ts | 29 + .../types/comps/z-paging-empty-view.d.ts | 95 + .../types/comps/z-paging-swiper-item.d.ts | 95 + .../z-paging/types/comps/z-paging-swiper.d.ts | 89 + .../z-paging/types/comps/z-paging.d.ts | 2083 +++++++++++++++++ uni_modules/z-paging/types/index.d.ts | 24 + utils/textUtils.js | 34 + vite.config.js | 34 + 561 files changed, 89472 insertions(+), 2 deletions(-) create mode 100644 App.vue create mode 100644 LICENSE create mode 100644 androidPrivacy.json create mode 100644 index.html create mode 100644 jsconfig.json create mode 100644 main.js create mode 100644 manifest.json create mode 100644 package.json create mode 100644 pages.json create mode 100644 pages/activity/groupon/detail.vue create mode 100644 pages/activity/groupon/list.vue create mode 100644 pages/activity/groupon/order.vue create mode 100644 pages/activity/index.vue create mode 100644 pages/activity/point/list.vue create mode 100644 pages/activity/seckill/list.vue create mode 100644 pages/app/sign.vue create mode 100644 pages/chat/components/goods.vue create mode 100644 pages/chat/components/messageInput.vue create mode 100644 pages/chat/components/messageList.vue create mode 100644 pages/chat/components/messageListItem.vue create mode 100644 pages/chat/components/order.vue create mode 100644 pages/chat/components/select-popup.vue create mode 100644 pages/chat/components/toolsPopup.vue create mode 100644 pages/chat/index.vue create mode 100644 pages/chat/util/constants.js create mode 100644 pages/chat/util/emoji.js create mode 100644 pages/chat/util/useWebSocket.js create mode 100644 pages/commission/commission-ranking.vue create mode 100644 pages/commission/components/account-info.vue create mode 100644 pages/commission/components/account-type-select.vue create mode 100644 pages/commission/components/commission-auth.vue create mode 100644 pages/commission/components/commission-info.vue create mode 100644 pages/commission/components/commission-log.vue create mode 100644 pages/commission/components/commission-menu.vue create mode 100644 pages/commission/goods.vue create mode 100644 pages/commission/index.vue create mode 100644 pages/commission/order.vue create mode 100644 pages/commission/promoter.vue create mode 100644 pages/commission/team.vue create mode 100644 pages/commission/wallet.vue create mode 100644 pages/commission/withdraw.vue create mode 100644 pages/coupon/detail.vue create mode 100644 pages/coupon/list.vue create mode 100644 pages/goods/comment/add.vue create mode 100644 pages/goods/comment/list.vue create mode 100644 pages/goods/components/detail/comment-item.vue create mode 100644 pages/goods/components/detail/detail-activity-tip.vue create mode 100644 pages/goods/components/detail/detail-cell-sku.vue create mode 100644 pages/goods/components/detail/detail-cell.vue create mode 100644 pages/goods/components/detail/detail-comment-card.vue create mode 100644 pages/goods/components/detail/detail-content-card.vue create mode 100644 pages/goods/components/detail/detail-navbar.vue create mode 100644 pages/goods/components/detail/detail-progress.vue create mode 100644 pages/goods/components/detail/detail-skeleton.vue create mode 100644 pages/goods/components/detail/detail-tabbar.vue create mode 100644 pages/goods/components/groupon/groupon-card-list.vue create mode 100644 pages/goods/components/list/list-goods-card.vue create mode 100644 pages/goods/components/list/list-navbar.vue create mode 100644 pages/goods/groupon.vue create mode 100644 pages/goods/index.vue create mode 100644 pages/goods/list.vue create mode 100644 pages/goods/point.vue create mode 100644 pages/goods/seckill.vue create mode 100644 pages/index/cart.vue create mode 100644 pages/index/category.vue create mode 100644 pages/index/components/first-one.vue create mode 100644 pages/index/components/first-two.vue create mode 100644 pages/index/components/second-one.vue create mode 100644 pages/index/index.vue create mode 100644 pages/index/login.vue create mode 100644 pages/index/page.vue create mode 100644 pages/index/search.vue create mode 100644 pages/index/user.vue create mode 100644 pages/order/addressSelection.vue create mode 100644 pages/order/aftersale/apply.vue create mode 100644 pages/order/aftersale/detail.vue create mode 100644 pages/order/aftersale/list.vue create mode 100644 pages/order/aftersale/log-item.vue create mode 100644 pages/order/aftersale/log.vue create mode 100644 pages/order/aftersale/return-delivery.vue create mode 100644 pages/order/confirm.vue create mode 100644 pages/order/detail.vue create mode 100644 pages/order/express/log.vue create mode 100644 pages/order/list.vue create mode 100644 pages/order/pickUpVerify.vue create mode 100644 pages/pay/index.vue create mode 100644 pages/pay/recharge-log.vue create mode 100644 pages/pay/recharge.vue create mode 100644 pages/pay/result.vue create mode 100644 pages/public/error.vue create mode 100644 pages/public/faq.vue create mode 100644 pages/public/richtext.vue create mode 100644 pages/public/setting.vue create mode 100644 pages/public/webview.vue create mode 100644 pages/user/address/edit.vue create mode 100644 pages/user/address/list.vue create mode 100644 pages/user/goods-collect.vue create mode 100644 pages/user/goods-log.vue create mode 100644 pages/user/goods_details_store/index.vue create mode 100644 pages/user/info.vue create mode 100644 pages/user/wallet/money.vue create mode 100644 pages/user/wallet/score.vue create mode 100644 sheep/api/index.js create mode 100644 sheep/api/infra/file.js create mode 100644 sheep/api/member/address.js create mode 100644 sheep/api/member/auth.js create mode 100644 sheep/api/member/point.js create mode 100644 sheep/api/member/signin.js create mode 100644 sheep/api/member/social.js create mode 100644 sheep/api/member/user.js create mode 100644 sheep/api/migration/app.js create mode 100644 sheep/api/migration/third.js create mode 100644 sheep/api/pay/channel.js create mode 100644 sheep/api/pay/order.js create mode 100644 sheep/api/pay/transfer.js create mode 100644 sheep/api/pay/wallet.js create mode 100644 sheep/api/product/category.js create mode 100644 sheep/api/product/comment.js create mode 100644 sheep/api/product/favorite.js create mode 100644 sheep/api/product/history.js create mode 100644 sheep/api/product/spu.js create mode 100644 sheep/api/promotion/activity.js create mode 100644 sheep/api/promotion/article.js create mode 100644 sheep/api/promotion/combination.js create mode 100644 sheep/api/promotion/coupon.js create mode 100644 sheep/api/promotion/diy.js create mode 100644 sheep/api/promotion/kefu.js create mode 100644 sheep/api/promotion/point.js create mode 100644 sheep/api/promotion/rewardActivity.js create mode 100644 sheep/api/promotion/seckill.js create mode 100644 sheep/api/system/area.js create mode 100644 sheep/api/system/dict.js create mode 100644 sheep/api/trade/afterSale.js create mode 100644 sheep/api/trade/brokerage.js create mode 100644 sheep/api/trade/cart.js create mode 100644 sheep/api/trade/config.js create mode 100644 sheep/api/trade/delivery.js create mode 100644 sheep/api/trade/order.js create mode 100644 sheep/components/s-activity-pop/s-activity-pop.vue create mode 100644 sheep/components/s-address-item/s-address-item.vue create mode 100644 sheep/components/s-auth-modal/components/account-login.vue create mode 100644 sheep/components/s-auth-modal/components/change-mobile.vue create mode 100644 sheep/components/s-auth-modal/components/change-password.vue create mode 100644 sheep/components/s-auth-modal/components/mp-authorization.vue create mode 100644 sheep/components/s-auth-modal/components/reset-password.vue create mode 100644 sheep/components/s-auth-modal/components/sms-login.vue create mode 100644 sheep/components/s-auth-modal/index.scss create mode 100644 sheep/components/s-auth-modal/s-auth-modal.vue create mode 100644 sheep/components/s-block-item/s-block-item.vue create mode 100644 sheep/components/s-block/s-block.vue create mode 100644 sheep/components/s-count-down/s-count-down.vue create mode 100644 sheep/components/s-coupon-block/s-coupon-block.vue create mode 100644 sheep/components/s-coupon-card/s-coupon-card.vue create mode 100644 sheep/components/s-coupon-get/s-coupon-get.vue create mode 100644 sheep/components/s-coupon-list/s-coupon-list.vue create mode 100644 sheep/components/s-coupon-select/s-coupon-select.vue create mode 100644 sheep/components/s-custom-navbar/components/navbar-item.vue create mode 100644 sheep/components/s-custom-navbar/components/navbar.vue create mode 100644 sheep/components/s-custom-navbar/s-custom-navbar.vue create mode 100644 sheep/components/s-discount-list/s-discount-list.vue create mode 100644 sheep/components/s-empty/s-empty.vue create mode 100644 sheep/components/s-float-menu/s-float-menu.vue create mode 100644 sheep/components/s-goods-card/s-goods-card.vue create mode 100644 sheep/components/s-goods-column/s-goods-column.vue create mode 100644 sheep/components/s-goods-item/s-goods-item.vue create mode 100644 sheep/components/s-goods-scroll/s-goods-scroll.vue create mode 100644 sheep/components/s-goods-shelves/s-goods-shelves.vue create mode 100644 sheep/components/s-groupon-block/s-groupon-block.vue create mode 100644 sheep/components/s-hotzone-block/s-hotzone-block.vue create mode 100644 sheep/components/s-image-banner/s-image-banner.vue create mode 100644 sheep/components/s-image-block/s-image-block.vue create mode 100644 sheep/components/s-image-cube/s-image-cube.vue create mode 100644 sheep/components/s-layout/s-layout.vue create mode 100644 sheep/components/s-line-block/s-line-block.vue create mode 100644 sheep/components/s-live-block/s-live-block.vue create mode 100644 sheep/components/s-live-card/s-live-card.vue create mode 100644 sheep/components/s-menu-button/s-menu-button.vue create mode 100644 sheep/components/s-menu-grid/s-menu-grid.vue create mode 100644 sheep/components/s-menu-list/s-menu-list.vue create mode 100644 sheep/components/s-menu-tools/s-menu-tools.vue create mode 100644 sheep/components/s-notice-block/s-notice-block.vue create mode 100644 sheep/components/s-order-card/s-order-card.vue create mode 100644 sheep/components/s-point-block/s-point-block.vue create mode 100644 sheep/components/s-point-card/s-point-card.vue create mode 100644 sheep/components/s-popup-image/s-popup-image.vue create mode 100644 sheep/components/s-richtext-block/s-richtext-block.vue create mode 100644 sheep/components/s-search-block/s-search-block.vue create mode 100644 sheep/components/s-seckill-block/s-seckill-block.vue create mode 100644 sheep/components/s-select-groupon-sku/s-select-groupon-sku.vue create mode 100644 sheep/components/s-select-seckill-sku/s-select-seckill-sku.vue create mode 100644 sheep/components/s-select-sku/s-select-sku.vue create mode 100644 sheep/components/s-share-modal/canvas-poster/index.vue create mode 100644 sheep/components/s-share-modal/canvas-poster/poster/goods.js create mode 100644 sheep/components/s-share-modal/canvas-poster/poster/groupon.js create mode 100644 sheep/components/s-share-modal/canvas-poster/poster/index.js create mode 100644 sheep/components/s-share-modal/canvas-poster/poster/user.js create mode 100644 sheep/components/s-share-modal/s-share-modal.vue create mode 100644 sheep/components/s-statusbar/s-statusbar.vue create mode 100644 sheep/components/s-tabbar/s-tabbar.vue create mode 100644 sheep/components/s-title-block/s-title-block.vue create mode 100644 sheep/components/s-uploader/choose-and-upload-file.js create mode 100644 sheep/components/s-uploader/s-uploader.vue create mode 100644 sheep/components/s-uploader/upload-file.vue create mode 100644 sheep/components/s-uploader/upload-image.vue create mode 100644 sheep/components/s-uploader/utils.js create mode 100644 sheep/components/s-user-card/s-user-card.vue create mode 100644 sheep/components/s-video-block/s-video-block.vue create mode 100644 sheep/components/s-wallet-card/s-wallet-card.vue create mode 100644 sheep/config/index.js create mode 100644 sheep/config/zIndex.js create mode 100644 sheep/helper/const.js create mode 100644 sheep/helper/digit.js create mode 100644 sheep/helper/index.js create mode 100644 sheep/helper/test.js create mode 100644 sheep/helper/throttle.js create mode 100644 sheep/helper/tools.js create mode 100644 sheep/helper/utils.js create mode 100644 sheep/hooks/useGoods.js create mode 100644 sheep/hooks/useModal.js create mode 100644 sheep/index.js create mode 100644 sheep/libs/mplive-manifest-plugin.js create mode 100644 sheep/libs/permission.js create mode 100644 sheep/libs/sdk-h5-weixin.js create mode 100644 sheep/platform/index.js create mode 100644 sheep/platform/pay.js create mode 100644 sheep/platform/provider/apple/app.js create mode 100644 sheep/platform/provider/apple/index.js create mode 100644 sheep/platform/provider/wechat/index.js create mode 100644 sheep/platform/provider/wechat/miniProgram.js create mode 100644 sheep/platform/provider/wechat/officialAccount.js create mode 100644 sheep/platform/provider/wechat/openPlatform.js create mode 100644 sheep/platform/share.js create mode 100644 sheep/request/index.js create mode 100644 sheep/router/index.js create mode 100644 sheep/router/utils/strip-json-comments.js create mode 100644 sheep/router/utils/uni-read-pages-v3.js create mode 100644 sheep/scss/_main.scss create mode 100644 sheep/scss/_mixins.scss create mode 100644 sheep/scss/_tools.scss create mode 100644 sheep/scss/_var.scss create mode 100644 sheep/scss/font/OPPOSANS-M-subfont.ttf create mode 100644 sheep/scss/icon/_coloricon.scss create mode 100644 sheep/scss/icon/_icon.scss create mode 100644 sheep/scss/icon/_sheepicon.scss create mode 100644 sheep/scss/icon/_style.scss create mode 100644 sheep/scss/index.scss create mode 100644 sheep/scss/style/_avatar.scss create mode 100644 sheep/scss/style/_background.scss create mode 100644 sheep/scss/style/_border.scss create mode 100644 sheep/scss/style/_button.scss create mode 100644 sheep/scss/style/_card.scss create mode 100644 sheep/scss/style/_code.scss create mode 100644 sheep/scss/style/_flex.scss create mode 100644 sheep/scss/style/_form.scss create mode 100644 sheep/scss/style/_grid.scss create mode 100644 sheep/scss/style/_markdown.scss create mode 100644 sheep/scss/style/_menu.scss create mode 100644 sheep/scss/style/_shadow.scss create mode 100644 sheep/scss/style/_table.scss create mode 100644 sheep/scss/style/_tag.scss create mode 100644 sheep/scss/style/_text.scss create mode 100644 sheep/scss/theme/_dark.scss create mode 100644 sheep/scss/theme/_light.scss create mode 100644 sheep/scss/theme/_style.scss create mode 100644 sheep/scss/ui.scss create mode 100644 sheep/store/app.js create mode 100644 sheep/store/cart.js create mode 100644 sheep/store/index.js create mode 100644 sheep/store/modal.js create mode 100644 sheep/store/sys.js create mode 100644 sheep/store/user.js create mode 100644 sheep/ui/su-coupon/su-coupon.vue create mode 100644 sheep/ui/su-data-checkbox/su-data-checkbox.vue create mode 100644 sheep/ui/su-dialog/su-dialog.vue create mode 100644 sheep/ui/su-fixed/su-fixed.vue create mode 100644 sheep/ui/su-image/su-image.vue create mode 100644 sheep/ui/su-inner-navbar/su-inner-navbar.vue create mode 100644 sheep/ui/su-navbar/su-navbar.vue create mode 100644 sheep/ui/su-notice-bar/su-notice-bar.vue create mode 100644 sheep/ui/su-number-box/su-number-box.vue create mode 100644 sheep/ui/su-popover/su-popover.vue create mode 100644 sheep/ui/su-popup/keypress.js create mode 100644 sheep/ui/su-popup/su-popup.vue create mode 100644 sheep/ui/su-progress/su-progress.vue create mode 100644 sheep/ui/su-radio/su-radio.vue create mode 100644 sheep/ui/su-region-picker/su-region-picker.vue create mode 100644 sheep/ui/su-status-bar/su-status-bar.vue create mode 100644 sheep/ui/su-sticky/su-sticky.vue create mode 100644 sheep/ui/su-subline/su-subline.vue create mode 100644 sheep/ui/su-swiper/su-swiper.vue create mode 100644 sheep/ui/su-switch/su-switch.vue create mode 100644 sheep/ui/su-tab-item/su-tab-item.vue create mode 100644 sheep/ui/su-tab/su-tab.vue create mode 100644 sheep/ui/su-tabbar-item/su-tabbar-item.vue create mode 100644 sheep/ui/su-tabbar/su-tabbar.vue create mode 100644 sheep/ui/su-tabs-item/props.js create mode 100644 sheep/ui/su-tabs-item/su-tabs-item.vue create mode 100644 sheep/ui/su-tabs/su-tabs.vue create mode 100644 sheep/ui/su-time-line/su-time-line.vue create mode 100644 sheep/ui/su-timeline-item/su-timeline-item.vue create mode 100644 sheep/ui/su-toolbar/su-toolbar.vue create mode 100644 sheep/ui/su-video/components/dom-video.vue create mode 100644 sheep/ui/su-video/su-video.vue create mode 100644 sheep/url/index.js create mode 100644 sheep/validate/form.js create mode 100644 static/activity-left.png create mode 100644 static/activity-right.png create mode 100644 static/cart-empty.png create mode 100644 static/collect-empty.png create mode 100644 static/comment-empty.png create mode 100644 static/coupon-empty.png create mode 100644 static/data-empty.png create mode 100644 static/goods-empty.png create mode 100644 static/internet-empty.png create mode 100644 static/order-empty.png create mode 100644 static/soldout-empty.png create mode 100644 uni.scss create mode 100644 uni_modules/lime-painter/changelog.md create mode 100644 uni_modules/lime-painter/components/common/relation.js create mode 100644 uni_modules/lime-painter/components/l-painter-image/l-painter-image.vue create mode 100644 uni_modules/lime-painter/components/l-painter-qrcode/l-painter-qrcode.vue create mode 100644 uni_modules/lime-painter/components/l-painter-text/l-painter-text.vue create mode 100644 uni_modules/lime-painter/components/l-painter-view/l-painter-view.vue create mode 100644 uni_modules/lime-painter/components/l-painter/l-painter.vue create mode 100644 uni_modules/lime-painter/components/l-painter/nvue.js create mode 100644 uni_modules/lime-painter/components/l-painter/painter.js create mode 100644 uni_modules/lime-painter/components/l-painter/props.js create mode 100644 uni_modules/lime-painter/components/l-painter/single.js create mode 100644 uni_modules/lime-painter/components/l-painter/utils.js create mode 100644 uni_modules/lime-painter/components/lime-painter/lime-painter.vue create mode 100644 uni_modules/lime-painter/hybrid/html/index.html create mode 100644 uni_modules/lime-painter/hybrid/html/painter.js create mode 100644 uni_modules/lime-painter/hybrid/html/uni.webview.1.5.3.js create mode 100644 uni_modules/lime-painter/package.json create mode 100644 uni_modules/lime-painter/parser.js create mode 100644 uni_modules/lime-painter/readme.md create mode 100644 uni_modules/mp-html/README.md create mode 100644 uni_modules/mp-html/changelog.md create mode 100644 uni_modules/mp-html/components/mp-html/mp-html.vue create mode 100644 uni_modules/mp-html/components/mp-html/node/node.vue create mode 100644 uni_modules/mp-html/components/mp-html/parser.js create mode 100644 uni_modules/mp-html/package.json create mode 100644 uni_modules/mp-html/static/app-plus/mp-html/js/handler.js create mode 100644 uni_modules/mp-html/static/app-plus/mp-html/js/uni.webview.min.js create mode 100644 uni_modules/mp-html/static/app-plus/mp-html/local.html 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-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-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-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/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/keypress.js 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/uni-data-picker.js 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-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/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-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/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.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/package.json create mode 100644 uni_modules/uni-icons/readme.md 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-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-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/manifest.json 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-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-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-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-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/z-paging/types/comps.d.ts create mode 100644 uni_modules/z-paging/types/comps/_common.d.ts create mode 100644 uni_modules/z-paging/types/comps/z-paging-cell.d.ts create mode 100644 uni_modules/z-paging/types/comps/z-paging-empty-view.d.ts create mode 100644 uni_modules/z-paging/types/comps/z-paging-swiper-item.d.ts create mode 100644 uni_modules/z-paging/types/comps/z-paging-swiper.d.ts create mode 100644 uni_modules/z-paging/types/comps/z-paging.d.ts create mode 100644 uni_modules/z-paging/types/index.d.ts create mode 100644 utils/textUtils.js create mode 100644 vite.config.js diff --git a/App.vue b/App.vue new file mode 100644 index 0000000..50c799b --- /dev/null +++ b/App.vue @@ -0,0 +1,32 @@ + + + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..9799627 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 lidongtony + +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. diff --git a/README.md b/README.md index 7acd1be..ba30293 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,56 @@ -# hake-admin-ui +**严肃声明:现在、未来都不会有商业版本,所有代码全部开源!!** + +**「我喜欢写代码,乐此不疲」** +**「我喜欢做开源,以此为乐」** + +我 🐶 在上海艰苦奋斗,早中晚在 top3 大厂认真搬砖,夜里为开源做贡献。 + +如果这个项目让你有所收获,记得 Star 关注哦,这对我是非常不错的鼓励与支持。 + +## 🐶 新手必读 + +* 演示地址: +* 启动文档: +* 视频教程: + +## 🐯 商城简介 + +**芋道商城**,基于 [芋道开发平台](https://github.com/YunaiV/ruoyi-vue-pro) 构建,以开发者为中心,打造中国第一流的 Java 开源商城系统,全部开源,个人与企业可 100% 免费使用。 + +> 有任何问题,或者想要的功能,可以在 Issues 中提给艿艿。 +> +> 😜 给项目点点 Star 吧,这对我们真的很重要! + +![功能图](/.image/common/mall-feature.png) + +* 基于 uni-app + Vue3 开发,支持微信小程序、微信公众号、H5 移动端,未来会支持支付宝小程序、抖音小程序等 +* 支持 SaaS 多租户,可满足商品、订单、支付、会员、优惠券、秒杀、拼团、砍价、分销、积分等多种经营需求 + +## 🔥 后端架构 + +支持 Spring Boot、Spring Cloud 两种架构: + +① Spring Boot 单体架构: + +![架构图](/.image/common/ruoyi-vue-pro-architecture.png) + +② Spring Cloud 微服务架构: + +![架构图](/.image/common/yudao-cloud-architecture.png) + +## 🐱 移动端预览 + +![移动端预览](/.image/common/mall-preview.png) + +## 🐶 管理端预览 + +![店铺装修](/.image/mall/店铺装修.png) + +![会员详情](/.image/mall/会员详情.png) + +![商品详情](/.image/mall/商品详情.png) + +![订单详情](/.image/mall/订单详情.png) + +![营销中心](/.image/mall/营销中心.png) -12 \ No newline at end of file diff --git a/androidPrivacy.json b/androidPrivacy.json new file mode 100644 index 0000000..0d726ca --- /dev/null +++ b/androidPrivacy.json @@ -0,0 +1,3 @@ +{ + "prompt" : "template" +} diff --git a/index.html b/index.html new file mode 100644 index 0000000..2269a69 --- /dev/null +++ b/index.html @@ -0,0 +1,17 @@ + + + + + + + + + + +
+ + + diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000..b1968ee --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "jsx": "preserve", + "baseUrl": ".", + "paths": { + "@/*": ["./*"] + } + } +} diff --git a/main.js b/main.js new file mode 100644 index 0000000..2680ac6 --- /dev/null +++ b/main.js @@ -0,0 +1,15 @@ +import App from './App'; +import { createSSRApp } from 'vue'; +import { setupPinia } from './sheep/store'; + + +export function createApp() { + + const app = createSSRApp(App); + + setupPinia(app); + + return { + app, + }; +} diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..532718d --- /dev/null +++ b/manifest.json @@ -0,0 +1,237 @@ +{ + "name": "哈客商城", + "appid": "__UNI__460BC4C", + "description": "基于 uni-app + Vue3 技术驱动的在线商城系统,内含诸多功能与丰富的活动,期待您的使用和反馈。", + "versionName": "2.1.0", + "versionCode": "183", + "transformPx": false, + "app-plus": { + "usingComponents": true, + "nvueCompiler": "uni-app", + "nvueStyleCompiler": "uni-app", + "compilerVersion": 3, + "nvueLaunchMode": "fast", + "splashscreen": { + "alwaysShowBeforeRender": true, + "waiting": true, + "autoclose": true, + "delay": 0 + }, + "safearea": { + "bottom": { + "offset": "none" + } + }, + "modules": { + "Payment": {}, + "Share": {}, + "VideoPlayer": {}, + "OAuth": {} + }, + "distribute": { + "android": { + "permissions": [ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + "minSdkVersion": 21, + "schemes": "shopro" + }, + "ios": { + "urlschemewhitelist": [ + "baidumap", + "iosamap" + ], + "dSYMs": false, + "privacyDescription": { + "NSPhotoLibraryUsageDescription": "需要同意访问您的相册选取图片才能完善该条目", + "NSPhotoLibraryAddUsageDescription": "需要同意访问您的相册才能保存该图片", + "NSCameraUsageDescription": "需要同意访问您的摄像头拍摄照片才能完善该条目", + "NSUserTrackingUsageDescription": "开启追踪并不会获取您在其它站点的隐私信息,该行为仅用于标识设备,保障服务安全和提升浏览体验" + }, + "urltypes": "shopro", + "capabilities": { + "entitlements": { + "com.apple.developer.associated-domains": [ + "applinks:shopro.sheepjs.com" + ] + } + }, + "idfa": true + }, + "sdkConfigs": { + "speech": {}, + "ad": {}, + "oauth": { + "apple": {}, + "weixin": { + "appid": "wxae7a0c156da9383b", + "UniversalLinks": "https://shopro.sheepjs.com/uni-universallinks/__UNI__082C0BA/" + } + }, + "payment": { + "weixin": { + "__platform__": [ + "ios", + "android" + ], + "appid": "wxae7a0c156da9383b", + "UniversalLinks": "https://shopro.sheepjs.com/uni-universallinks/__UNI__082C0BA/" + }, + "alipay": { + "__platform__": [ + "ios", + "android" + ] + } + }, + "share": { + "weixin": { + "appid": "wxae7a0c156da9383b", + "UniversalLinks": "https://shopro.sheepjs.com/uni-universallinks/__UNI__082C0BA/" + } + } + }, + "orientation": [ + "portrait-primary" + ], + "splashscreen": { + "androidStyle": "common", + "iosStyle": "common", + "useOriginalMsgbox": true + }, + "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" + } + } + } + } + }, + "quickapp": {}, + "quickapp-native": { + "icon": "/static/logo.png", + "package": "com.example.demo", + "features": [ + { + "name": "system.clipboard" + } + ] + }, + "quickapp-webview": { + "icon": "/static/logo.png", + "package": "com.example.demo", + "minPlatformVersion": 1070, + "versionName": "1.0.0", + "versionCode": 100 + }, + "mp-weixin": { + "appid": "wx66186af0759f47c9", + "setting": { + "urlCheck": false, + "minified": true, + "postcss": true + }, + "optimization": { + "subPackages": true + }, + "plugins": {}, + "lazyCodeLoading": "requiredComponents", + "usingComponents": {}, + "permission": {}, + "requiredPrivateInfos": [ + "chooseAddress" + ] + }, + "mp-alipay": { + "usingComponents": true + }, + "mp-baidu": { + "usingComponents": true + }, + "mp-toutiao": { + "usingComponents": true + }, + "mp-jd": { + "usingComponents": true + }, + "h5": { + "template": "index.html", + "router": { + "mode": "history", + "base": "/" + }, + "sdkConfigs": { + "maps": {} + }, + "async": { + "timeout": 20000 + }, + "title": "哈客商城", + "optimization": { + "treeShaking": { + "enable": true + } + } + }, + "vueVersion": "3", + "_spaceID": "192b4892-5452-4e1d-9f09-eee1ece40639", + "locale": "zh-Hans", + "fallbackLocale": "zh-Hans" +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..811c958 --- /dev/null +++ b/package.json @@ -0,0 +1,103 @@ +{ + "id": "shopro", + "name": "shopro", + "displayName": "哈客商城", + "version": "2.6.0", + "description": "哈客商城,一套代码,同时发行到iOS、Android、H5、微信小程序多个平台,请使用手机扫码快速体验强大功能", + "scripts": { + "prettier": "prettier --write \"{pages,sheep}/**/*.{js,json,tsx,css,less,scss,vue,html,md}\"" + }, + "repository": "https://github.com/sheepjs/shop.git", + "keywords": [ + "商城", + "B2C", + "商城模板" + ], + "author": "", + "license": "MIT", + "bugs": { + "url": "https://github.com/sheepjs/shop/issues" + }, + "homepage": "https://github.com/dcloudio/hello-uniapp#readme", + "dcloudext": { + "category": [ + "前端页面模板", + "uni-app前端项目模板" + ], + "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": "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" + }, + "Vue": { + "vue2": "u", + "vue3": "y" + } + } + } + }, + "dependencies": { + "dayjs": "^1.11.7", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "luch-request": "^3.0.8", + "pinia": "^2.0.33", + "pinia-plugin-persist-uni": "^1.2.0", + "weixin-js-sdk": "^1.6.0" + }, + "devDependencies": { + "prettier": "^2.8.7", + "vconsole": "^3.15.0" + } +} \ No newline at end of file diff --git a/pages.json b/pages.json new file mode 100644 index 0000000..b81404a --- /dev/null +++ b/pages.json @@ -0,0 +1,712 @@ +{ + "easycom": { + "autoscan": true, + "custom": { + "^s-(.*)": "@/sheep/components/s-$1/s-$1.vue", + "^su-(.*)": "@/sheep/ui/su-$1/su-$1.vue" + } + }, + "pages": [ + { + "path": "pages/index/index", + "aliasPath": "/", + "style": { + "navigationBarTitleText": "首页", + "enablePullDownRefresh": true + }, + "meta": { + "auth": false, + "sync": true, + "title": "首页", + "group": "商城" + } + }, + { + "path": "pages/index/user", + "style": { + "navigationBarTitleText": "个人中心", + "enablePullDownRefresh": true + }, + "meta": { + "sync": true, + "title": "个人中心", + "group": "商城" + } + }, + { + "path": "pages/index/category", + "style": { + "navigationBarTitleText": "商品分类" + }, + "meta": { + "sync": true, + "title": "商品分类", + "group": "商城" + } + }, + { + "path": "pages/index/cart", + "style": { + "navigationBarTitleText": "购物车" + }, + "meta": { + "sync": true, + "title": "购物车", + "group": "商城" + } + }, + { + "path": "pages/index/login", + "style": { + "navigationBarTitleText": "登录" + } + }, + { + "path": "pages/index/search", + "style": { + "navigationBarTitleText": "搜索" + }, + "meta": { + "sync": true, + "title": "搜索", + "group": "商城" + } + }, + { + "path": "pages/index/page", + "style": { + "navigationBarTitleText": "" + }, + "meta": { + "auth": false, + "sync": true, + "title": "自定义页面", + "group": "商城" + } + } + ], + "subPackages": [ + { + "root": "pages/goods", + "pages": [ + { + "path": "index", + "style": { + "navigationBarTitleText": "商品详情" + }, + "meta": { + "sync": true, + "title": "普通商品", + "group": "商品" + } + }, + { + "path": "groupon", + "style": { + "navigationBarTitleText": "拼团商品" + }, + "meta": { + "sync": true, + "title": "拼团商品", + "group": "商品" + } + }, + { + "path": "seckill", + "style": { + "navigationBarTitleText": "秒杀商品" + }, + "meta": { + "sync": true, + "title": "秒杀商品", + "group": "商品" + } + }, + { + "path": "point", + "style": { + "navigationBarTitleText": "积分商品" + }, + "meta": { + "sync": true, + "title": "积分商品", + "group": "商品" + } + }, + { + "path": "list", + "style": { + "navigationBarTitleText": "商品列表" + }, + "meta": { + "sync": true, + "title": "商品列表", + "group": "商品" + } + }, + { + "path": "comment/add", + "style": { + "navigationBarTitleText": "评价商品" + }, + "meta": { + "auth": true + } + }, + { + "path": "comment/list", + "style": { + "navigationBarTitleText": "商品评价" + } + } + ] + }, + { + "root": "pages/order", + "pages": [ + { + "path": "detail", + "style": { + "navigationBarTitleText": "订单详情" + }, + "meta": { + "auth": true, + "title": "订单详情" + } + }, + { + "path": "confirm", + "style": { + "navigationBarTitleText": "确认订单" + }, + "meta": { + "auth": true, + "title": "确认订单" + } + }, + { + "path": "list", + "style": { + "navigationBarTitleText": "我的订单", + "enablePullDownRefresh": true + }, + "meta": { + "auth": true, + "sync": true, + "title": "用户订单", + "group": "订单中心" + } + }, + { + "path": "aftersale/apply", + "style": { + "navigationBarTitleText": "申请售后" + }, + "meta": { + "auth": true, + "title": "申请售后" + } + }, + { + "path": "aftersale/return-delivery", + "style": { + "navigationBarTitleText": "退货物流" + }, + "meta": { + "auth": true, + "title": "退货物流" + } + }, + { + "path": "aftersale/list", + "style": { + "navigationBarTitleText": "售后列表" + }, + "meta": { + "auth": true, + "sync": true, + "title": "售后订单", + "group": "订单中心" + } + }, + { + "path": "aftersale/detail", + "style": { + "navigationBarTitleText": "售后详情" + }, + "meta": { + "auth": true, + "title": "售后详情" + } + }, + { + "path": "aftersale/log", + "style": { + "navigationBarTitleText": "售后进度" + }, + "meta": { + "auth": true, + "title": "售后进度" + } + }, + { + "path": "express/log", + "style": { + "navigationBarTitleText": "物流轨迹" + }, + "meta": { + "auth": true, + "title": "物流轨迹" + } + } + ] + }, + { + "root": "pages/user", + "pages": [ + { + "path": "info", + "style": { + "navigationBarTitleText": "我的信息" + }, + "meta": { + "auth": true, + "sync": true, + "title": "用户信息", + "group": "用户中心" + } + }, + { + "path": "goods-collect", + "style": { + "navigationBarTitleText": "我的收藏" + }, + "meta": { + "auth": true, + "sync": true, + "title": "商品收藏", + "group": "用户中心" + } + }, + { + "path": "goods-log", + "style": { + "navigationBarTitleText": "我的足迹" + }, + "meta": { + "auth": true, + "sync": true, + "title": "浏览记录", + "group": "用户中心" + } + }, + { + "path": "address/list", + "style": { + "navigationBarTitleText": "收货地址" + }, + "meta": { + "auth": true, + "sync": true, + "title": "地址管理", + "group": "用户中心" + } + }, + { + "path": "address/edit", + "style": { + "navigationBarTitleText": "编辑地址" + }, + "meta": { + "auth": true, + "title": "编辑地址" + } + }, + { + "path": "goods_details_store/index", + "style": { + "navigationBarTitleText": "自提门店" + }, + "meta": { + "auth": true, + "sync": true, + "title": "地址管理", + "group": "用户中心" + } + }, + { + "path": "wallet/money", + "style": { + "navigationBarTitleText": "我的余额" + }, + "meta": { + "auth": true, + "sync": true, + "title": "用户余额", + "group": "用户中心" + } + }, + { + "path": "wallet/score", + "style": { + "navigationBarTitleText": "我的积分" + }, + "meta": { + "auth": true, + "sync": true, + "title": "用户积分", + "group": "用户中心" + } + } + ] + }, + { + "root": "pages/commission", + "pages": [ + { + "path": "index", + "style": { + "navigationBarTitleText": "分销" + }, + "meta": { + "auth": true, + "sync": true, + "title": "分销中心", + "group": "分销商城" + } + }, + { + "path": "wallet", + "style": { + "navigationBarTitleText": "我的佣金" + }, + "meta": { + "auth": true, + "sync": true, + "title": "用户佣金", + "group": "分销中心" + } + }, + { + "path": "goods", + "style": { + "navigationBarTitleText": "推广商品" + }, + "meta": { + "auth": true, + "sync": true, + "title": "推广商品", + "group": "分销商城" + } + }, + { + "path": "order", + "style": { + "navigationBarTitleText": "分销订单" + }, + "meta": { + "auth": true, + "sync": true, + "title": "分销订单", + "group": "分销商城" + } + }, + { + "path": "team", + "style": { + "navigationBarTitleText": "我的团队" + }, + "meta": { + "auth": true, + "sync": true, + "title": "我的团队", + "group": "分销商城" + } + }, + { + "path": "promoter", + "style": { + "navigationBarTitleText": "推广人排行榜" + }, + "meta": { + "auth": true, + "sync": true, + "title": "推广人排行榜", + "group": "分销商城" + } + }, + { + "path": "commission-ranking", + "style": { + "navigationBarTitleText": "佣金排行榜" + }, + "meta": { + "auth": true, + "sync": true, + "title": "佣金排行榜", + "group": "分销商城" + } + }, + { + "path": "withdraw", + "style": { + "navigationBarTitleText": "申请提现" + }, + "meta": { + "auth": true, + "sync": true, + "title": "申请提现", + "group": "分销商城" + } + } + ] + }, + { + "root": "pages/app", + "pages": [ + { + "path": "sign", + "style": { + "navigationBarTitleText": "签到中心" + }, + "meta": { + "auth": true, + "sync": true, + "title": "签到中心", + "group": "应用" + } + } + ] + }, + { + "root": "pages/public", + "pages": [ + { + "path": "setting", + "style": { + "navigationBarTitleText": "系统设置" + }, + "meta": { + "sync": true, + "title": "系统设置", + "group": "通用" + } + }, + { + "path": "richtext", + "style": { + "navigationBarTitleText": "富文本" + }, + "meta": { + "sync": true, + "title": "富文本", + "group": "通用" + } + }, + { + "path": "faq", + "style": { + "navigationBarTitleText": "常见问题" + }, + "meta": { + "sync": true, + "title": "常见问题", + "group": "通用" + } + }, + { + "path": "error", + "style": { + "navigationBarTitleText": "错误页面" + } + }, + { + "path": "webview", + "style": { + "navigationBarTitleText": "" + } + } + ] + }, + { + "root": "pages/coupon", + "pages": [ + { + "path": "list", + "style": { + "navigationBarTitleText": "领券中心" + }, + "meta": { + "sync": true, + "title": "领券中心", + "group": "优惠券" + } + }, + { + "path": "detail", + "style": { + "navigationBarTitleText": "优惠券" + }, + "meta": { + "auth": false, + "sync": true, + "title": "优惠券详情", + "group": "优惠券" + } + } + ] + }, + { + "root": "pages/chat", + "pages": [ + { + "path": "index", + "style": { + "navigationBarTitleText": "客服", + "app-plus": { + "softinputMode": "adjustResize" + } + }, + "meta": { + "auth": true, + "sync": true, + "title": "客服", + "group": "客服" + } + } + ] + }, + { + "root": "pages/pay", + "pages": [ + { + "path": "index", + "style": { + "navigationBarTitleText": "收银台" + } + }, + { + "path": "result", + "style": { + "navigationBarTitleText": "支付结果" + } + }, + { + "path": "recharge", + "style": { + "navigationBarTitleText": "充值余额" + }, + "meta": { + "auth": true, + "sync": true, + "title": "充值余额", + "group": "支付" + } + }, + { + "path": "recharge-log", + "style": { + "navigationBarTitleText": "充值记录" + }, + "meta": { + "auth": true, + "sync": true, + "title": "充值记录", + "group": "支付" + } + } + ] + }, + { + "root": "pages/activity", + "pages": [ + { + "path": "groupon/detail", + "style": { + "navigationBarTitleText": "拼团详情" + } + }, + { + "path": "groupon/order", + "style": { + "navigationBarTitleText": "我的拼团", + "enablePullDownRefresh": true + }, + "meta": { + "auth": true, + "sync": true, + "title": "拼团订单", + "group": "营销活动" + } + }, + { + "path": "index", + "style": { + "navigationBarTitleText": "营销商品" + }, + "meta": { + "sync": true, + "title": "营销商品", + "group": "营销活动" + } + }, + { + "path": "groupon/list", + "style": { + "navigationBarTitleText": "拼团活动" + }, + "meta": { + "sync": true, + "title": "拼团活动", + "group": "营销活动" + } + }, + { + "path": "seckill/list", + "style": { + "navigationBarTitleText": "秒杀活动" + }, + "meta": { + "sync": true, + "title": "秒杀活动", + "group": "营销活动" + } + }, + { + "path": "point/list", + "style": { + "navigationBarTitleText": "积分商城" + }, + "meta": { + "sync": true, + "title": "积分商城", + "group": "营销活动" + } + } + ] + } + ], + "globalStyle": { + "navigationBarTextStyle": "black", + "navigationBarTitleText": "哈客商城", + "navigationBarBackgroundColor": "#FFFFFF", + "backgroundColor": "#FFFFFF", + "navigationStyle": "custom" + }, + "tabBar": { + "list": [ + { + "pagePath": "pages/index/index" + }, + { + "pagePath": "pages/index/category" + }, + { + "pagePath": "pages/index/cart" + }, + { + "pagePath": "pages/index/user" + } + ] + } +} \ No newline at end of file diff --git a/pages/activity/groupon/detail.vue b/pages/activity/groupon/detail.vue new file mode 100644 index 0000000..f9ef781 --- /dev/null +++ b/pages/activity/groupon/detail.vue @@ -0,0 +1,539 @@ + + + + + + diff --git a/pages/activity/groupon/list.vue b/pages/activity/groupon/list.vue new file mode 100644 index 0000000..0cd8bc9 --- /dev/null +++ b/pages/activity/groupon/list.vue @@ -0,0 +1,225 @@ + + + + diff --git a/pages/activity/groupon/order.vue b/pages/activity/groupon/order.vue new file mode 100644 index 0000000..a93cc6e --- /dev/null +++ b/pages/activity/groupon/order.vue @@ -0,0 +1,240 @@ + + + + + + diff --git a/pages/activity/index.vue b/pages/activity/index.vue new file mode 100644 index 0000000..9573915 --- /dev/null +++ b/pages/activity/index.vue @@ -0,0 +1,214 @@ + + + + diff --git a/pages/activity/point/list.vue b/pages/activity/point/list.vue new file mode 100644 index 0000000..7630fd2 --- /dev/null +++ b/pages/activity/point/list.vue @@ -0,0 +1,76 @@ + + + diff --git a/pages/activity/seckill/list.vue b/pages/activity/seckill/list.vue new file mode 100644 index 0000000..16eca3b --- /dev/null +++ b/pages/activity/seckill/list.vue @@ -0,0 +1,460 @@ + + + + diff --git a/pages/app/sign.vue b/pages/app/sign.vue new file mode 100644 index 0000000..e026374 --- /dev/null +++ b/pages/app/sign.vue @@ -0,0 +1,452 @@ + + + + + + diff --git a/pages/chat/components/goods.vue b/pages/chat/components/goods.vue new file mode 100644 index 0000000..eabdd21 --- /dev/null +++ b/pages/chat/components/goods.vue @@ -0,0 +1,21 @@ + + + + diff --git a/pages/chat/components/messageInput.vue b/pages/chat/components/messageInput.vue new file mode 100644 index 0000000..ace4727 --- /dev/null +++ b/pages/chat/components/messageInput.vue @@ -0,0 +1,185 @@ + + + + + diff --git a/pages/chat/components/messageList.vue b/pages/chat/components/messageList.vue new file mode 100644 index 0000000..e021e02 --- /dev/null +++ b/pages/chat/components/messageList.vue @@ -0,0 +1,293 @@ + + + + + diff --git a/pages/chat/components/messageListItem.vue b/pages/chat/components/messageListItem.vue new file mode 100644 index 0000000..f3832f7 --- /dev/null +++ b/pages/chat/components/messageListItem.vue @@ -0,0 +1,300 @@ + + + + + diff --git a/pages/chat/components/order.vue b/pages/chat/components/order.vue new file mode 100644 index 0000000..15f2720 --- /dev/null +++ b/pages/chat/components/order.vue @@ -0,0 +1,114 @@ + + + + + diff --git a/pages/chat/components/select-popup.vue b/pages/chat/components/select-popup.vue new file mode 100644 index 0000000..60711af --- /dev/null +++ b/pages/chat/components/select-popup.vue @@ -0,0 +1,151 @@ + + + + + diff --git a/pages/chat/components/toolsPopup.vue b/pages/chat/components/toolsPopup.vue new file mode 100644 index 0000000..f47ce3f --- /dev/null +++ b/pages/chat/components/toolsPopup.vue @@ -0,0 +1,166 @@ + + + + + diff --git a/pages/chat/index.vue b/pages/chat/index.vue new file mode 100644 index 0000000..2c7d817 --- /dev/null +++ b/pages/chat/index.vue @@ -0,0 +1,222 @@ + + + + + diff --git a/pages/chat/util/constants.js b/pages/chat/util/constants.js new file mode 100644 index 0000000..1b8eba9 --- /dev/null +++ b/pages/chat/util/constants.js @@ -0,0 +1,19 @@ +export const KeFuMessageContentTypeEnum = { + TEXT: 1, // 文本消息 + IMAGE: 2, // 图片消息 + VOICE: 3, // 语音消息 + VIDEO: 4, // 视频消息 + SYSTEM: 5, // 系统消息 + // ========== 商城特殊消息 ========== + PRODUCT: 10,// 商品消息 + ORDER: 11,// 订单消息" +}; +export const UserTypeEnum = { + MEMBER: 1, // 会员 面向 c 端,普通用户 + ADMIN: 2, // 管理员 面向 b 端,管理后台 +}; +// Promotion 的 WebSocket 消息类型枚举类 +export const WebSocketMessageTypeConstants = { + KEFU_MESSAGE_TYPE: 'kefu_message_type', // 客服消息类型 + KEFU_MESSAGE_ADMIN_READ: 'kefu_message_read_status_change' // 客服消息管理员已读 +} diff --git a/pages/chat/util/emoji.js b/pages/chat/util/emoji.js new file mode 100644 index 0000000..83e6e84 --- /dev/null +++ b/pages/chat/util/emoji.js @@ -0,0 +1,58 @@ +export const emojiList = [ + { name: '[笑掉牙]', file: 'xiaodiaoya.png' }, + { name: '[可爱]', file: 'keai.png' }, + { name: '[冷酷]', file: 'lengku.png' }, + { name: '[闭嘴]', file: 'bizui.png' }, + { name: '[生气]', file: 'shengqi.png' }, + { name: '[惊恐]', file: 'jingkong.png' }, + { name: '[瞌睡]', file: 'keshui.png' }, + { name: '[大笑]', file: 'daxiao.png' }, + { name: '[爱心]', file: 'aixin.png' }, + { name: '[坏笑]', file: 'huaixiao.png' }, + { name: '[飞吻]', file: 'feiwen.png' }, + { name: '[疑问]', file: 'yiwen.png' }, + { name: '[开心]', file: 'kaixin.png' }, + { name: '[发呆]', file: 'fadai.png' }, + { name: '[流泪]', file: 'liulei.png' }, + { name: '[汗颜]', file: 'hanyan.png' }, + { name: '[惊悚]', file: 'jingshu.png' }, + { name: '[困~]', file: 'kun.png' }, + { name: '[心碎]', file: 'xinsui.png' }, + { name: '[天使]', file: 'tianshi.png' }, + { name: '[晕]', file: 'yun.png' }, + { name: '[啊]', file: 'a.png' }, + { name: '[愤怒]', file: 'fennu.png' }, + { name: '[睡着]', file: 'shuizhuo.png' }, + { name: '[面无表情]', file: 'mianwubiaoqing.png' }, + { name: '[难过]', file: 'nanguo.png' }, + { name: '[犯困]', file: 'fankun.png' }, + { name: '[好吃]', file: 'haochi.png' }, + { name: '[呕吐]', file: 'outu.png' }, + { name: '[龇牙]', file: 'ziya.png' }, + { name: '[懵比]', file: 'mengbi.png' }, + { name: '[白眼]', file: 'baiyan.png' }, + { name: '[饿死]', file: 'esi.png' }, + { name: '[凶]', file: 'xiong.png' }, + { name: '[感冒]', file: 'ganmao.png' }, + { name: '[流汗]', file: 'liuhan.png' }, + { name: '[笑哭]', file: 'xiaoku.png' }, + { name: '[流口水]', file: 'liukoushui.png' }, + { name: '[尴尬]', file: 'ganga.png' }, + { name: '[惊讶]', file: 'jingya.png' }, + { name: '[大惊]', file: 'dajing.png' }, + { name: '[不好意思]', file: 'buhaoyisi.png' }, + { name: '[大闹]', file: 'danao.png' }, + { name: '[不可思议]', file: 'bukesiyi.png' }, + { name: '[爱你]', file: 'aini.png' }, + { name: '[红心]', file: 'hongxin.png' }, + { name: '[点赞]', file: 'dianzan.png' }, + { name: '[恶魔]', file: 'emo.png' }, +]; + +export let emojiPage = {}; +emojiList.forEach((item, index) => { + if (!emojiPage[Math.floor(index / 30) + 1]) { + emojiPage[Math.floor(index / 30) + 1] = []; + } + emojiPage[Math.floor(index / 30) + 1].push(item); +}); diff --git a/pages/chat/util/useWebSocket.js b/pages/chat/util/useWebSocket.js new file mode 100644 index 0000000..4b59276 --- /dev/null +++ b/pages/chat/util/useWebSocket.js @@ -0,0 +1,150 @@ +import { onBeforeUnmount, reactive, ref } from 'vue'; +import { baseUrl, websocketPath } from '@/sheep/config'; +import { copyValueToTarget } from '@/sheep/helper/utils'; +import { getRefreshToken } from '@/sheep/request'; + +/** + * WebSocket 创建 hook + * @param opt 连接配置 + * @return {{options: *}} + */ +export function useWebSocket(opt) { + const options = reactive({ + url: (baseUrl + websocketPath).replace('http', 'ws') + '?token=' + getRefreshToken(), // ws 地址 + isReconnecting: false, // 正在重新连接 + reconnectInterval: 3000, // 重连间隔,单位毫秒 + heartBeatInterval: 5000, // 心跳间隔,单位毫秒 + pingTimeoutDuration: 1000, // 超过这个时间,后端没有返回pong,则判定后端断线了。 + heartBeatTimer: null, // 心跳计时器 + destroy: false, // 是否销毁 + pingTimeout: null, // 心跳检测定时器 + reconnectTimeout: null, // 重连定时器ID的属性 + onConnected: () => {}, // 连接成功时触发 + onClosed: () => {}, // 连接关闭时触发 + onMessage: (data) => {}, // 收到消息 + }); + const SocketTask = ref(null); // SocketTask 由 uni.connectSocket() 接口创建 + + const initEventListeners = () => { + // 监听 WebSocket 连接打开事件 + SocketTask.value.onOpen(() => { + console.log('WebSocket 连接成功'); + // 连接成功时触发 + options.onConnected(); + // 开启心跳检查 + startHeartBeat(); + }); + // 监听 WebSocket 接受到服务器的消息事件 + SocketTask.value.onMessage((res) => { + try { + if (res.data === 'pong') { + // 收到心跳重置心跳超时检查 + resetPingTimeout(); + } else { + options.onMessage(JSON.parse(res.data)); + } + } catch (error) { + console.error(error); + } + }); + // 监听 WebSocket 连接关闭事件 + SocketTask.value.onClose((event) => { + // 情况一:实例销毁 + if (options.destroy) { + options.onClosed(); + } else { + // 情况二:连接失败重连 + // 停止心跳检查 + stopHeartBeat(); + // 重连 + reconnect(); + } + }); + }; + + // 发送消息 + const sendMessage = (message) => { + if (SocketTask.value && !options.destroy) { + SocketTask.value.send({ data: message }); + } + }; + // 开始心跳检查 + const startHeartBeat = () => { + options.heartBeatTimer = setInterval(() => { + sendMessage('ping'); + options.pingTimeout = setTimeout(() => { + // 如果在超时时间内没有收到 pong,则认为连接断开 + reconnect(); + }, options.pingTimeoutDuration); + }, options.heartBeatInterval); + }; + // 停止心跳检查 + const stopHeartBeat = () => { + clearInterval(options.heartBeatTimer); + resetPingTimeout(); + }; + + // WebSocket 重连 + const reconnect = () => { + if (options.destroy || !SocketTask.value) { + // 如果WebSocket已被销毁或尚未完全关闭,不进行重连 + return; + } + + // 重连中 + options.isReconnecting = true; + + // 清除现有的重连标志,以避免多次重连 + if (options.reconnectTimeout) { + clearTimeout(options.reconnectTimeout); + } + + // 设置重连延迟 + options.reconnectTimeout = setTimeout(() => { + // 检查组件是否仍在运行和WebSocket是否关闭 + if (!options.destroy) { + // 重置重连标志 + options.isReconnecting = false; + // 初始化新的WebSocket连接 + initSocket(); + } + }, options.reconnectInterval); + }; + + const resetPingTimeout = () => { + if (options.pingTimeout) { + clearTimeout(options.pingTimeout); + options.pingTimeout = null; // 清除超时ID + } + }; + + const close = () => { + options.destroy = true; + stopHeartBeat(); + if (options.reconnectTimeout) { + clearTimeout(options.reconnectTimeout); + } + if (SocketTask.value) { + SocketTask.value.close(); + SocketTask.value = null; + } + }; + + const initSocket = () => { + options.destroy = false; + copyValueToTarget(options, opt); + SocketTask.value = uni.connectSocket({ + url: options.url, + complete: () => {}, + success: () => {}, + }); + initEventListeners(); + }; + + initSocket(); + + onBeforeUnmount(() => { + close(); + }); + return { options }; +} diff --git a/pages/commission/commission-ranking.vue b/pages/commission/commission-ranking.vue new file mode 100644 index 0000000..50fc76f --- /dev/null +++ b/pages/commission/commission-ranking.vue @@ -0,0 +1,239 @@ + + + + + diff --git a/pages/commission/components/account-info.vue b/pages/commission/components/account-info.vue new file mode 100644 index 0000000..d904b8d --- /dev/null +++ b/pages/commission/components/account-info.vue @@ -0,0 +1,125 @@ + + + + + + \ No newline at end of file diff --git a/pages/commission/components/account-type-select.vue b/pages/commission/components/account-type-select.vue new file mode 100644 index 0000000..c39c7a7 --- /dev/null +++ b/pages/commission/components/account-type-select.vue @@ -0,0 +1,171 @@ + + + + + + diff --git a/pages/commission/components/commission-auth.vue b/pages/commission/components/commission-auth.vue new file mode 100644 index 0000000..9a484fa --- /dev/null +++ b/pages/commission/components/commission-auth.vue @@ -0,0 +1,101 @@ + + + + + + diff --git a/pages/commission/components/commission-info.vue b/pages/commission/components/commission-info.vue new file mode 100644 index 0000000..79f7cd8 --- /dev/null +++ b/pages/commission/components/commission-info.vue @@ -0,0 +1,114 @@ + + + + + + diff --git a/pages/commission/components/commission-log.vue b/pages/commission/components/commission-log.vue new file mode 100644 index 0000000..cee3b3c --- /dev/null +++ b/pages/commission/components/commission-log.vue @@ -0,0 +1,181 @@ + + + + + + diff --git a/pages/commission/components/commission-menu.vue b/pages/commission/components/commission-menu.vue new file mode 100644 index 0000000..42498f5 --- /dev/null +++ b/pages/commission/components/commission-menu.vue @@ -0,0 +1,145 @@ + + + + + + diff --git a/pages/commission/goods.vue b/pages/commission/goods.vue new file mode 100644 index 0000000..d868879 --- /dev/null +++ b/pages/commission/goods.vue @@ -0,0 +1,165 @@ + + + + + + diff --git a/pages/commission/index.vue b/pages/commission/index.vue new file mode 100644 index 0000000..28c077c --- /dev/null +++ b/pages/commission/index.vue @@ -0,0 +1,57 @@ + + + + + + diff --git a/pages/commission/order.vue b/pages/commission/order.vue new file mode 100644 index 0000000..94be9b7 --- /dev/null +++ b/pages/commission/order.vue @@ -0,0 +1,332 @@ + + + + + + diff --git a/pages/commission/promoter.vue b/pages/commission/promoter.vue new file mode 100644 index 0000000..2dfcc2b --- /dev/null +++ b/pages/commission/promoter.vue @@ -0,0 +1,288 @@ + + + + + diff --git a/pages/commission/team.vue b/pages/commission/team.vue new file mode 100644 index 0000000..ba86814 --- /dev/null +++ b/pages/commission/team.vue @@ -0,0 +1,604 @@ + + + + + + diff --git a/pages/commission/wallet.vue b/pages/commission/wallet.vue new file mode 100644 index 0000000..73024db --- /dev/null +++ b/pages/commission/wallet.vue @@ -0,0 +1,643 @@ + + + + + + diff --git a/pages/commission/withdraw.vue b/pages/commission/withdraw.vue new file mode 100644 index 0000000..0f722b0 --- /dev/null +++ b/pages/commission/withdraw.vue @@ -0,0 +1,484 @@ + + + + + + diff --git a/pages/coupon/detail.vue b/pages/coupon/detail.vue new file mode 100644 index 0000000..96114a3 --- /dev/null +++ b/pages/coupon/detail.vue @@ -0,0 +1,390 @@ + + + + + + diff --git a/pages/coupon/list.vue b/pages/coupon/list.vue new file mode 100644 index 0000000..2afffd7 --- /dev/null +++ b/pages/coupon/list.vue @@ -0,0 +1,222 @@ + + + + + diff --git a/pages/goods/comment/add.vue b/pages/goods/comment/add.vue new file mode 100644 index 0000000..c52024c --- /dev/null +++ b/pages/goods/comment/add.vue @@ -0,0 +1,190 @@ + + + + + + diff --git a/pages/goods/comment/list.vue b/pages/goods/comment/list.vue new file mode 100644 index 0000000..ed55988 --- /dev/null +++ b/pages/goods/comment/list.vue @@ -0,0 +1,168 @@ + + + + + + diff --git a/pages/goods/components/detail/comment-item.vue b/pages/goods/components/detail/comment-item.vue new file mode 100644 index 0000000..518db2a --- /dev/null +++ b/pages/goods/components/detail/comment-item.vue @@ -0,0 +1,94 @@ + + + + + + diff --git a/pages/goods/components/detail/detail-activity-tip.vue b/pages/goods/components/detail/detail-activity-tip.vue new file mode 100644 index 0000000..e80fe39 --- /dev/null +++ b/pages/goods/components/detail/detail-activity-tip.vue @@ -0,0 +1,99 @@ + + + + + diff --git a/pages/goods/components/detail/detail-cell-sku.vue b/pages/goods/components/detail/detail-cell-sku.vue new file mode 100644 index 0000000..11f1b20 --- /dev/null +++ b/pages/goods/components/detail/detail-cell-sku.vue @@ -0,0 +1,31 @@ + + + diff --git a/pages/goods/components/detail/detail-cell.vue b/pages/goods/components/detail/detail-cell.vue new file mode 100644 index 0000000..b4c56dd --- /dev/null +++ b/pages/goods/components/detail/detail-cell.vue @@ -0,0 +1,60 @@ + + + + + + diff --git a/pages/goods/components/detail/detail-comment-card.vue b/pages/goods/components/detail/detail-comment-card.vue new file mode 100644 index 0000000..cc48dfd --- /dev/null +++ b/pages/goods/components/detail/detail-comment-card.vue @@ -0,0 +1,106 @@ + + + + + + diff --git a/pages/goods/components/detail/detail-content-card.vue b/pages/goods/components/detail/detail-content-card.vue new file mode 100644 index 0000000..38d45f9 --- /dev/null +++ b/pages/goods/components/detail/detail-content-card.vue @@ -0,0 +1,54 @@ + + + + + diff --git a/pages/goods/components/detail/detail-navbar.vue b/pages/goods/components/detail/detail-navbar.vue new file mode 100644 index 0000000..d454ec0 --- /dev/null +++ b/pages/goods/components/detail/detail-navbar.vue @@ -0,0 +1,257 @@ + + + + + + diff --git a/pages/goods/components/detail/detail-progress.vue b/pages/goods/components/detail/detail-progress.vue new file mode 100644 index 0000000..a6210b1 --- /dev/null +++ b/pages/goods/components/detail/detail-progress.vue @@ -0,0 +1,40 @@ + + + + + + diff --git a/pages/goods/components/detail/detail-skeleton.vue b/pages/goods/components/detail/detail-skeleton.vue new file mode 100644 index 0000000..5eebf50 --- /dev/null +++ b/pages/goods/components/detail/detail-skeleton.vue @@ -0,0 +1,177 @@ + + + + + diff --git a/pages/goods/components/detail/detail-tabbar.vue b/pages/goods/components/detail/detail-tabbar.vue new file mode 100644 index 0000000..bcae5be --- /dev/null +++ b/pages/goods/components/detail/detail-tabbar.vue @@ -0,0 +1,169 @@ + + + + + + diff --git a/pages/goods/components/groupon/groupon-card-list.vue b/pages/goods/components/groupon/groupon-card-list.vue new file mode 100644 index 0000000..2c75214 --- /dev/null +++ b/pages/goods/components/groupon/groupon-card-list.vue @@ -0,0 +1,140 @@ + + + + + + diff --git a/pages/goods/components/list/list-goods-card.vue b/pages/goods/components/list/list-goods-card.vue new file mode 100644 index 0000000..d02519f --- /dev/null +++ b/pages/goods/components/list/list-goods-card.vue @@ -0,0 +1,103 @@ + + + + + + diff --git a/pages/goods/components/list/list-navbar.vue b/pages/goods/components/list/list-navbar.vue new file mode 100644 index 0000000..c21e679 --- /dev/null +++ b/pages/goods/components/list/list-navbar.vue @@ -0,0 +1,93 @@ + + + + + + diff --git a/pages/goods/groupon.vue b/pages/goods/groupon.vue new file mode 100644 index 0000000..0fc81cf --- /dev/null +++ b/pages/goods/groupon.vue @@ -0,0 +1,562 @@ + + + + + + diff --git a/pages/goods/index.vue b/pages/goods/index.vue new file mode 100644 index 0000000..901c891 --- /dev/null +++ b/pages/goods/index.vue @@ -0,0 +1,672 @@ + + + + + diff --git a/pages/goods/list.vue b/pages/goods/list.vue new file mode 100644 index 0000000..8a5259a --- /dev/null +++ b/pages/goods/list.vue @@ -0,0 +1,407 @@ + + + + + diff --git a/pages/goods/point.vue b/pages/goods/point.vue new file mode 100644 index 0000000..631d940 --- /dev/null +++ b/pages/goods/point.vue @@ -0,0 +1,483 @@ + + + + + + diff --git a/pages/goods/seckill.vue b/pages/goods/seckill.vue new file mode 100644 index 0000000..c76e948 --- /dev/null +++ b/pages/goods/seckill.vue @@ -0,0 +1,575 @@ + + + + + + diff --git a/pages/index/cart.vue b/pages/index/cart.vue new file mode 100644 index 0000000..8f02d5d --- /dev/null +++ b/pages/index/cart.vue @@ -0,0 +1,315 @@ + + + + + diff --git a/pages/index/category.vue b/pages/index/category.vue new file mode 100644 index 0000000..1dfa5d8 --- /dev/null +++ b/pages/index/category.vue @@ -0,0 +1,240 @@ + + + + + + diff --git a/pages/index/components/first-one.vue b/pages/index/components/first-one.vue new file mode 100644 index 0000000..2decc86 --- /dev/null +++ b/pages/index/components/first-one.vue @@ -0,0 +1,26 @@ + + + + + + diff --git a/pages/index/components/first-two.vue b/pages/index/components/first-two.vue new file mode 100644 index 0000000..801cb8d --- /dev/null +++ b/pages/index/components/first-two.vue @@ -0,0 +1,66 @@ + + + + + + diff --git a/pages/index/components/second-one.vue b/pages/index/components/second-one.vue new file mode 100644 index 0000000..86b7078 --- /dev/null +++ b/pages/index/components/second-one.vue @@ -0,0 +1,80 @@ + + + + + + diff --git a/pages/index/index.vue b/pages/index/index.vue new file mode 100644 index 0000000..d79e66d --- /dev/null +++ b/pages/index/index.vue @@ -0,0 +1,93 @@ + + + + + + diff --git a/pages/index/login.vue b/pages/index/login.vue new file mode 100644 index 0000000..e498870 --- /dev/null +++ b/pages/index/login.vue @@ -0,0 +1,39 @@ + + + + diff --git a/pages/index/page.vue b/pages/index/page.vue new file mode 100644 index 0000000..b5d8ef3 --- /dev/null +++ b/pages/index/page.vue @@ -0,0 +1,51 @@ + + + + + + diff --git a/pages/index/search.vue b/pages/index/search.vue new file mode 100644 index 0000000..def16cc --- /dev/null +++ b/pages/index/search.vue @@ -0,0 +1,119 @@ + + + + + + diff --git a/pages/index/user.vue b/pages/index/user.vue new file mode 100644 index 0000000..42d769b --- /dev/null +++ b/pages/index/user.vue @@ -0,0 +1,47 @@ + + + + + + diff --git a/pages/order/addressSelection.vue b/pages/order/addressSelection.vue new file mode 100644 index 0000000..4adf258 --- /dev/null +++ b/pages/order/addressSelection.vue @@ -0,0 +1,282 @@ + + + + + + diff --git a/pages/order/aftersale/apply.vue b/pages/order/aftersale/apply.vue new file mode 100644 index 0000000..635b7dd --- /dev/null +++ b/pages/order/aftersale/apply.vue @@ -0,0 +1,349 @@ + + + + + + diff --git a/pages/order/aftersale/detail.vue b/pages/order/aftersale/detail.vue new file mode 100644 index 0000000..6b7bfcc --- /dev/null +++ b/pages/order/aftersale/detail.vue @@ -0,0 +1,379 @@ + + + + + + diff --git a/pages/order/aftersale/list.vue b/pages/order/aftersale/list.vue new file mode 100644 index 0000000..e1a0ac5 --- /dev/null +++ b/pages/order/aftersale/list.vue @@ -0,0 +1,209 @@ + + + + + + diff --git a/pages/order/aftersale/log-item.vue b/pages/order/aftersale/log-item.vue new file mode 100644 index 0000000..2a5d20d --- /dev/null +++ b/pages/order/aftersale/log-item.vue @@ -0,0 +1,83 @@ + + + + diff --git a/pages/order/aftersale/log.vue b/pages/order/aftersale/log.vue new file mode 100644 index 0000000..a8df8a2 --- /dev/null +++ b/pages/order/aftersale/log.vue @@ -0,0 +1,38 @@ + + + + + + diff --git a/pages/order/aftersale/return-delivery.vue b/pages/order/aftersale/return-delivery.vue new file mode 100644 index 0000000..fa3ecec --- /dev/null +++ b/pages/order/aftersale/return-delivery.vue @@ -0,0 +1,195 @@ + + + + \ No newline at end of file diff --git a/pages/order/confirm.vue b/pages/order/confirm.vue new file mode 100644 index 0000000..59abcce --- /dev/null +++ b/pages/order/confirm.vue @@ -0,0 +1,529 @@ + + + + + diff --git a/pages/order/detail.vue b/pages/order/detail.vue new file mode 100644 index 0000000..f6aad23 --- /dev/null +++ b/pages/order/detail.vue @@ -0,0 +1,671 @@ + + + + + + diff --git a/pages/order/express/log.vue b/pages/order/express/log.vue new file mode 100644 index 0000000..c852edc --- /dev/null +++ b/pages/order/express/log.vue @@ -0,0 +1,162 @@ + + + + + + diff --git a/pages/order/list.vue b/pages/order/list.vue new file mode 100644 index 0000000..2675199 --- /dev/null +++ b/pages/order/list.vue @@ -0,0 +1,504 @@ + + + + + + diff --git a/pages/order/pickUpVerify.vue b/pages/order/pickUpVerify.vue new file mode 100644 index 0000000..6c36578 --- /dev/null +++ b/pages/order/pickUpVerify.vue @@ -0,0 +1,260 @@ + + + + + diff --git a/pages/pay/index.vue b/pages/pay/index.vue new file mode 100644 index 0000000..1106903 --- /dev/null +++ b/pages/pay/index.vue @@ -0,0 +1,305 @@ + + + + + diff --git a/pages/pay/recharge-log.vue b/pages/pay/recharge-log.vue new file mode 100644 index 0000000..8de792a --- /dev/null +++ b/pages/pay/recharge-log.vue @@ -0,0 +1,167 @@ + + + + + + diff --git a/pages/pay/recharge.vue b/pages/pay/recharge.vue new file mode 100644 index 0000000..4ac646c --- /dev/null +++ b/pages/pay/recharge.vue @@ -0,0 +1,277 @@ + + + + + + diff --git a/pages/pay/result.vue b/pages/pay/result.vue new file mode 100644 index 0000000..ba708bf --- /dev/null +++ b/pages/pay/result.vue @@ -0,0 +1,339 @@ + + + + + + diff --git a/pages/public/error.vue b/pages/public/error.vue new file mode 100644 index 0000000..3f20878 --- /dev/null +++ b/pages/public/error.vue @@ -0,0 +1,60 @@ + + + + + + diff --git a/pages/public/faq.vue b/pages/public/faq.vue new file mode 100644 index 0000000..627f4d5 --- /dev/null +++ b/pages/public/faq.vue @@ -0,0 +1,118 @@ + + + + + + diff --git a/pages/public/richtext.vue b/pages/public/richtext.vue new file mode 100644 index 0000000..b1fee46 --- /dev/null +++ b/pages/public/richtext.vue @@ -0,0 +1,54 @@ + + + + + + diff --git a/pages/public/setting.vue b/pages/public/setting.vue new file mode 100644 index 0000000..34da0df --- /dev/null +++ b/pages/public/setting.vue @@ -0,0 +1,236 @@ + + + + + diff --git a/pages/public/webview.vue b/pages/public/webview.vue new file mode 100644 index 0000000..1327295 --- /dev/null +++ b/pages/public/webview.vue @@ -0,0 +1,18 @@ + + + + + + diff --git a/pages/user/address/edit.vue b/pages/user/address/edit.vue new file mode 100644 index 0000000..b88ee07 --- /dev/null +++ b/pages/user/address/edit.vue @@ -0,0 +1,305 @@ + + + + + + diff --git a/pages/user/address/list.vue b/pages/user/address/list.vue new file mode 100644 index 0000000..15ce34e --- /dev/null +++ b/pages/user/address/list.vue @@ -0,0 +1,165 @@ + + + + + + diff --git a/pages/user/goods-collect.vue b/pages/user/goods-collect.vue new file mode 100644 index 0000000..08d7e45 --- /dev/null +++ b/pages/user/goods-collect.vue @@ -0,0 +1,233 @@ + + + + + + diff --git a/pages/user/goods-log.vue b/pages/user/goods-log.vue new file mode 100644 index 0000000..4def00f --- /dev/null +++ b/pages/user/goods-log.vue @@ -0,0 +1,308 @@ + + + + + + diff --git a/pages/user/goods_details_store/index.vue b/pages/user/goods_details_store/index.vue new file mode 100644 index 0000000..f1b0c84 --- /dev/null +++ b/pages/user/goods_details_store/index.vue @@ -0,0 +1,282 @@ + + + + diff --git a/pages/user/info.vue b/pages/user/info.vue new file mode 100644 index 0000000..00e20a4 --- /dev/null +++ b/pages/user/info.vue @@ -0,0 +1,467 @@ + + + + + + diff --git a/pages/user/wallet/money.vue b/pages/user/wallet/money.vue new file mode 100644 index 0000000..39a5d29 --- /dev/null +++ b/pages/user/wallet/money.vue @@ -0,0 +1,379 @@ + + + + + + diff --git a/pages/user/wallet/score.vue b/pages/user/wallet/score.vue new file mode 100644 index 0000000..7fcee87 --- /dev/null +++ b/pages/user/wallet/score.vue @@ -0,0 +1,282 @@ + + + + + + diff --git a/sheep/api/index.js b/sheep/api/index.js new file mode 100644 index 0000000..1affd1e --- /dev/null +++ b/sheep/api/index.js @@ -0,0 +1,11 @@ +// 目的:解决微信小程序的「代码质量」在「JS 文件」提示:主包内,不应该存在主包未使用的 JS 文件 +const files = import.meta.glob('./*/*.js', { eager: true }); +let api = {}; +Object.keys(files).forEach((key) => { + api = { + ...api, + [key.replace(/(.*\/)*([^.]+).*/gi, '$2')]: files[key].default, + }; +}); + +export default api; diff --git a/sheep/api/infra/file.js b/sheep/api/infra/file.js new file mode 100644 index 0000000..8835595 --- /dev/null +++ b/sheep/api/infra/file.js @@ -0,0 +1,67 @@ +import { baseUrl, apiPath, tenantId } from '@/sheep/config'; +import request, { getAccessToken } from '@/sheep/request'; + +const FileApi = { + // 上传文件 + uploadFile: (file, directory) => { + uni.showLoading({ + title: '上传中', + }); + return new Promise((resolve, reject) => { + uni.uploadFile({ + url: baseUrl + apiPath + '/infra/file/upload', + filePath: file, + name: 'file', + header: { + Accept: '*/*', + 'tenant-id': tenantId, + Authorization: 'Bearer ' + getAccessToken(), + }, + formData: { + directory, + }, + success: (uploadFileRes) => { + let result = JSON.parse(uploadFileRes.data); + if (result.error === 1) { + uni.showToast({ + icon: 'none', + title: result.msg, + }); + } else { + return resolve(result); + } + }, + fail: (error) => { + console.log('上传失败:', error); + return resolve(false); + }, + complete: () => { + uni.hideLoading(); + }, + }); + }); + }, + + // 获取文件预签名地址 + getFilePresignedUrl: (name, directory) => { + return request({ + url: '/infra/file/presigned-url', + method: 'GET', + params: { + name, + directory, + }, + }); + }, + + // 创建文件 + createFile: (data) => { + return request({ + url: '/infra/file/create', // 请求的 URL + method: 'POST', // 请求方法 + data: data, // 要发送的数据 + }); + }, +}; + +export default FileApi; diff --git a/sheep/api/member/address.js b/sheep/api/member/address.js new file mode 100644 index 0000000..d0c16ce --- /dev/null +++ b/sheep/api/member/address.js @@ -0,0 +1,53 @@ +import request from '@/sheep/request'; + +const AddressApi = { + // 获得用户收件地址列表 + getAddressList: () => { + return request({ + url: '/member/address/list', + method: 'GET' + }); + }, + // 创建用户收件地址 + createAddress: (data) => { + return request({ + url: '/member/address/create', + method: 'POST', + data, + custom: { + showSuccess: true, + successMsg: '保存成功' + }, + }); + }, + // 更新用户收件地址 + updateAddress: (data) => { + return request({ + url: '/member/address/update', + method: 'PUT', + data, + custom: { + showSuccess: true, + successMsg: '更新成功' + }, + }); + }, + // 获得用户收件地址 + getAddress: (id) => { + return request({ + url: '/member/address/get', + method: 'GET', + params: { id } + }); + }, + // 删除用户收件地址 + deleteAddress: (id) => { + return request({ + url: '/member/address/delete', + method: 'DELETE', + params: { id } + }); + }, +}; + +export default AddressApi; diff --git a/sheep/api/member/auth.js b/sheep/api/member/auth.js new file mode 100644 index 0000000..ccd75a4 --- /dev/null +++ b/sheep/api/member/auth.js @@ -0,0 +1,132 @@ +import request from '@/sheep/request'; + +const AuthUtil = { + // 使用手机 + 密码登录 + login: (data) => { + return request({ + url: '/member/auth/login', + method: 'POST', + data, + custom: { + showSuccess: true, + loadingMsg: '登录中', + successMsg: '登录成功', + }, + }); + }, + // 使用手机 + 验证码登录 + smsLogin: (data) => { + return request({ + url: '/member/auth/sms-login', + method: 'POST', + data, + custom: { + showSuccess: true, + loadingMsg: '登录中', + successMsg: '登录成功', + }, + }); + }, + // 发送手机验证码 + sendSmsCode: (mobile, scene) => { + return request({ + url: '/member/auth/send-sms-code', + method: 'POST', + data: { + mobile, + scene, + }, + custom: { + loadingMsg: '发送中', + showSuccess: true, + successMsg: '发送成功', + }, + }); + }, + // 登出系统 + logout: () => { + return request({ + url: '/member/auth/logout', + method: 'POST', + }); + }, + // 刷新令牌 + refreshToken: (refreshToken) => { + return request({ + url: '/member/auth/refresh-token', + method: 'POST', + params: { + refreshToken, + }, + custom: { + showLoading: false, // 不用加载中 + showError: false, // 不展示错误提示 + }, + }); + }, + // 社交授权的跳转 + socialAuthRedirect: (type, redirectUri) => { + return request({ + url: '/member/auth/social-auth-redirect', + method: 'GET', + params: { + type, + redirectUri, + }, + custom: { + showSuccess: true, + loadingMsg: '登陆中', + }, + }); + }, + // 社交快捷登录 + socialLogin: (type, code, state) => { + return request({ + url: '/member/auth/social-login', + method: 'POST', + data: { + type, + code, + state, + }, + custom: { + showSuccess: true, + loadingMsg: '登陆中', + }, + }); + }, + // 微信小程序的一键登录 + weixinMiniAppLogin: (phoneCode, loginCode, state) => { + return request({ + url: '/member/auth/weixin-mini-app-login', + method: 'POST', + data: { + phoneCode, + loginCode, + state, + }, + custom: { + showSuccess: true, + loadingMsg: '登陆中', + successMsg: '登录成功', + }, + }); + }, + // 创建微信 JS SDK 初始化所需的签名 + createWeixinMpJsapiSignature: (url) => { + return request({ + url: '/member/auth/create-weixin-jsapi-signature', + method: 'POST', + params: { + url, + }, + custom: { + showError: false, + showLoading: false, + }, + }); + }, + // +}; + +export default AuthUtil; diff --git a/sheep/api/member/point.js b/sheep/api/member/point.js new file mode 100644 index 0000000..188ffd4 --- /dev/null +++ b/sheep/api/member/point.js @@ -0,0 +1,19 @@ +import request from '@/sheep/request'; + +const PointApi = { + // 获得用户积分记录分页 + getPointRecordPage: (params) => { + if (params.addStatus === undefined) { + delete params.addStatus + } + const queryString = Object.keys(params) + .map((key) => encodeURIComponent(key) + '=' + params[key]) + .join('&'); + return request({ + url: `/member/point/record/page?${queryString}`, + method: 'GET', + }); + } +}; + +export default PointApi; diff --git a/sheep/api/member/signin.js b/sheep/api/member/signin.js new file mode 100644 index 0000000..35169ef --- /dev/null +++ b/sheep/api/member/signin.js @@ -0,0 +1,37 @@ +import request from '@/sheep/request'; + +const SignInApi = { + // 获得签到规则列表 + getSignInConfigList: () => { + return request({ + url: '/member/sign-in/config/list', + method: 'GET', + }); + }, + // 获得个人签到统计 + getSignInRecordSummary: () => { + return request({ + url: '/member/sign-in/record/get-summary', + method: 'GET', + }); + }, + // 签到 + createSignInRecord: () => { + return request({ + url: '/member/sign-in/record/create', + method: 'POST', + }); + }, + // 获得签到记录分页 + getSignRecordPage: (params) => { + const queryString = Object.keys(params) + .map((key) => encodeURIComponent(key) + '=' + params[key]) + .join('&'); + return request({ + url: `/member/sign-in/record/page?${queryString}`, + method: 'GET', + }); + }, +}; + +export default SignInApi; \ No newline at end of file diff --git a/sheep/api/member/social.js b/sheep/api/member/social.js new file mode 100644 index 0000000..14e6edf --- /dev/null +++ b/sheep/api/member/social.js @@ -0,0 +1,76 @@ +import request from '@/sheep/request'; + +const SocialApi = { + // 获得社交用户 + getSocialUser: (type) => { + return request({ + url: '/member/social-user/get', + method: 'GET', + params: { + type + }, + custom: { + showLoading: false, + }, + }); + }, + // 社交绑定 + socialBind: (type, code, state) => { + return request({ + url: '/member/social-user/bind', + method: 'POST', + data: { + type, + code, + state + }, + custom: { + custom: { + showSuccess: true, + loadingMsg: '绑定中', + successMsg: '绑定成功', + }, + }, + }); + }, + // 社交绑定 + socialUnbind: (type, openid) => { + return request({ + url: '/member/social-user/unbind', + method: 'DELETE', + data: { + type, + openid + }, + custom: { + showLoading: false, + loadingMsg: '解除绑定', + successMsg: '解绑成功', + }, + }); + }, + // 获取订阅消息模板列表 + getSubscribeTemplateList: () => + request({ + url: '/member/social-user/get-subscribe-template-list', + method: 'GET', + custom: { + showError: false, + showLoading: false, + }, + }), + // 获取微信小程序码 + getWxaQrcode: async (path, query) => { + return await request({ + url: '/member/social-user/wxa-qrcode', + method: 'POST', + data: { + scene: query, + path, + checkPath: false, // TODO 开发环境暂不检查 path 是否存在 + }, + }); + }, +}; + +export default SocialApi; \ No newline at end of file diff --git a/sheep/api/member/user.js b/sheep/api/member/user.js new file mode 100644 index 0000000..5f06e42 --- /dev/null +++ b/sheep/api/member/user.js @@ -0,0 +1,85 @@ +import request from '@/sheep/request'; + +const UserApi = { + // 获得基本信息 + getUserInfo: () => { + return request({ + url: '/member/user/get', + method: 'GET', + custom: { + showLoading: false, + auth: true, + }, + }); + }, + // 修改基本信息 + updateUser: (data) => { + return request({ + url: '/member/user/update', + method: 'PUT', + data, + custom: { + auth: true, + showSuccess: true, + successMsg: '更新成功' + }, + }); + }, + // 修改用户手机 + updateUserMobile: (data) => { + return request({ + url: '/member/user/update-mobile', + method: 'PUT', + data, + custom: { + loadingMsg: '验证中', + showSuccess: true, + successMsg: '修改成功' + }, + }); + }, + // 基于微信小程序的授权码,修改用户手机 + updateUserMobileByWeixin: (code) => { + return request({ + url: '/member/user/update-mobile-by-weixin', + method: 'PUT', + data: { + code + }, + custom: { + showSuccess: true, + loadingMsg: '获取中', + successMsg: '修改成功' + }, + }); + }, + // 修改密码 + updateUserPassword: (data) => { + return request({ + url: '/member/user/update-password', + method: 'PUT', + data, + custom: { + loadingMsg: '验证中', + showSuccess: true, + successMsg: '修改成功' + }, + }); + }, + // 重置密码 + resetUserPassword: (data) => { + return request({ + url: '/member/user/reset-password', + method: 'PUT', + data, + custom: { + loadingMsg: '验证中', + showSuccess: true, + successMsg: '修改成功' + } + }); + }, + +}; + +export default UserApi; diff --git a/sheep/api/migration/app.js b/sheep/api/migration/app.js new file mode 100644 index 0000000..bea2e56 --- /dev/null +++ b/sheep/api/migration/app.js @@ -0,0 +1,21 @@ +import request from '@/sheep/request'; + +// TODO 芋艿:【直播】小程序直播还不支持 +export default { + //小程序直播 + mplive: { + getRoomList: (ids) => + request({ + url: 'app/mplive/getRoomList', + method: 'GET', + params: { + ids: ids.join(','), + }, + }), + getMpLink: () => + request({ + url: 'app/mplive/getMpLink', + method: 'GET', + }), + }, +}; diff --git a/sheep/api/migration/third.js b/sheep/api/migration/third.js new file mode 100644 index 0000000..325638c --- /dev/null +++ b/sheep/api/migration/third.js @@ -0,0 +1,18 @@ +import request from '@/sheep/request'; + +export default { + // 苹果相关 + apple: { + // 第三方登录 + login: (data) => + request({ + url: 'third/apple/login', + method: 'POST', + data, + custom: { + showSuccess: true, + loadingMsg: '登陆中', + }, + }), + }, +}; diff --git a/sheep/api/pay/channel.js b/sheep/api/pay/channel.js new file mode 100644 index 0000000..4e7bfc5 --- /dev/null +++ b/sheep/api/pay/channel.js @@ -0,0 +1,14 @@ +import request from '@/sheep/request'; + +const PayChannelApi = { + // 获得指定应用的开启的支付渠道编码列表 + getEnableChannelCodeList: (appId) => { + return request({ + url: '/pay/channel/get-enable-code-list', + method: 'GET', + params: { appId } + }); + }, +}; + +export default PayChannelApi; diff --git a/sheep/api/pay/order.js b/sheep/api/pay/order.js new file mode 100644 index 0000000..f7bef28 --- /dev/null +++ b/sheep/api/pay/order.js @@ -0,0 +1,22 @@ +import request from '@/sheep/request'; + +const PayOrderApi = { + // 获得支付订单 + getOrder: (id, sync) => { + return request({ + url: '/pay/order/get', + method: 'GET', + params: { id, sync }, + }); + }, + // 提交支付订单 + submitOrder: (data) => { + return request({ + url: '/pay/order/submit', + method: 'POST', + data, + }); + }, +}; + +export default PayOrderApi; diff --git a/sheep/api/pay/transfer.js b/sheep/api/pay/transfer.js new file mode 100644 index 0000000..5e23305 --- /dev/null +++ b/sheep/api/pay/transfer.js @@ -0,0 +1,14 @@ +import request from '@/sheep/request'; + +const PayTransferApi = { + // 同步转账单 + syncTransfer: (id) => { + return request({ + url: '/pay/transfer/sync', + method: 'GET', + params: { id }, + }); + }, +}; + +export default PayTransferApi; diff --git a/sheep/api/pay/wallet.js b/sheep/api/pay/wallet.js new file mode 100644 index 0000000..023902a --- /dev/null +++ b/sheep/api/pay/wallet.js @@ -0,0 +1,68 @@ +import request from '@/sheep/request'; + +const PayWalletApi = { + // 获取钱包 + getPayWallet() { + return request({ + url: '/pay/wallet/get', + method: 'GET', + custom: { + showLoading: false, + auth: true, + }, + }); + }, + // 获得钱包流水分页 + getWalletTransactionPage: (params) => { + const queryString = Object.keys(params) + .map((key) => encodeURIComponent(key) + '=' + params[key]) + .join('&'); + return request({ + url: `/pay/wallet-transaction/page?${queryString}`, + method: 'GET', + }); + }, + // 获得钱包流水统计 + getWalletTransactionSummary: (params) => { + const queryString = `createTime=${params.createTime[0]}&createTime=${params.createTime[1]}`; + return request({ + url: `/pay/wallet-transaction/get-summary?${queryString}`, + // url: `/pay/wallet-transaction/get-summary`, + method: 'GET', + // params: params + }); + }, + // 获得钱包充值套餐列表 + getWalletRechargePackageList: () => { + return request({ + url: '/pay/wallet-recharge-package/list', + method: 'GET', + custom: { + showError: false, + showLoading: false, + }, + }); + }, + // 创建钱包充值记录(发起充值) + createWalletRecharge: (data) => { + return request({ + url: '/pay/wallet-recharge/create', + method: 'POST', + data, + }); + }, + // 获得钱包充值记录分页 + getWalletRechargePage: (params) => { + return request({ + url: '/pay/wallet-recharge/page', + method: 'GET', + params, + custom: { + showError: false, + showLoading: false, + }, + }); + }, +}; + +export default PayWalletApi; diff --git a/sheep/api/product/category.js b/sheep/api/product/category.js new file mode 100644 index 0000000..3b6f8f0 --- /dev/null +++ b/sheep/api/product/category.js @@ -0,0 +1,21 @@ +import request from '@/sheep/request'; + +const CategoryApi = { + // 查询分类列表 + getCategoryList: () => { + return request({ + url: '/product/category/list', + method: 'GET', + }); + }, + // 查询分类列表,指定编号 + getCategoryListByIds: (ids) => { + return request({ + url: '/product/category/list-by-ids', + method: 'GET', + params: { ids }, + }); + }, +}; + +export default CategoryApi; diff --git a/sheep/api/product/comment.js b/sheep/api/product/comment.js new file mode 100644 index 0000000..2bbffd1 --- /dev/null +++ b/sheep/api/product/comment.js @@ -0,0 +1,22 @@ +import request from '@/sheep/request'; + +const CommentApi = { + // 获得商品评价分页 + getCommentPage: (spuId, pageNo, pageSize, type) => { + return request({ + url: '/product/comment/page', + method: 'GET', + params: { + spuId, + pageNo, + pageSize, + type, + }, + custom: { + showLoading: false, + showError: false, + }, + }); + }, +}; +export default CommentApi; diff --git a/sheep/api/product/favorite.js b/sheep/api/product/favorite.js new file mode 100644 index 0000000..134c231 --- /dev/null +++ b/sheep/api/product/favorite.js @@ -0,0 +1,54 @@ +import request from '@/sheep/request'; + +const FavoriteApi = { + // 获得商品收藏分页 + getFavoritePage: (data) => { + return request({ + url: '/product/favorite/page', + method: 'GET', + params: data, + }); + }, + // 检查是否收藏过商品 + isFavoriteExists: (spuId) => { + return request({ + url: '/product/favorite/exits', + method: 'GET', + params: { + spuId, + }, + }); + }, + // 添加商品收藏 + createFavorite: (spuId) => { + return request({ + url: '/product/favorite/create', + method: 'POST', + data: { + spuId, + }, + custom: { + auth: true, + showSuccess: true, + successMsg: '收藏成功', + }, + }); + }, + // 取消商品收藏 + deleteFavorite: (spuId) => { + return request({ + url: '/product/favorite/delete', + method: 'DELETE', + data: { + spuId, + }, + custom: { + auth: true, + showSuccess: true, + successMsg: '取消成功', + }, + }); + }, +}; + +export default FavoriteApi; diff --git a/sheep/api/product/history.js b/sheep/api/product/history.js new file mode 100644 index 0000000..9ed53e3 --- /dev/null +++ b/sheep/api/product/history.js @@ -0,0 +1,39 @@ +import request from '@/sheep/request'; + +const SpuHistoryApi = { + // 删除商品浏览记录 + deleteBrowseHistory: (spuIds) => { + return request({ + url: '/product/browse-history/delete', + method: 'DELETE', + data: { spuIds }, + custom: { + showSuccess: true, + successMsg: '删除成功', + }, + }); + }, + // 清空商品浏览记录 + cleanBrowseHistory: () => { + return request({ + url: '/product/browse-history/clean', + method: 'DELETE', + custom: { + showSuccess: true, + successMsg: '清空成功', + }, + }); + }, + // 获得商品浏览记录分页 + getBrowseHistoryPage: (data) => { + return request({ + url: '/product/browse-history/page', + method: 'GET', + data, + custom: { + showLoading: false + }, + }); + }, +}; +export default SpuHistoryApi; diff --git a/sheep/api/product/spu.js b/sheep/api/product/spu.js new file mode 100644 index 0000000..65a62e9 --- /dev/null +++ b/sheep/api/product/spu.js @@ -0,0 +1,53 @@ +import request from '@/sheep/request'; + +const SpuApi = { + // 获得商品 SPU 列表 + getSpuListByIds: (ids) => { + return request({ + url: '/product/spu/list-by-ids', + method: 'GET', + params: { ids }, + custom: { + showLoading: false, + showError: false, + }, + }); + }, + // 获得商品结算信息 + getSettlementProduct: (spuIds) => { + return request({ + url: '/trade/order/settlement-product', + method: 'GET', + params: { spuIds }, + custom: { + showLoading: false, + showError: false, + }, + }); + }, + // 获得商品 SPU 分页 + getSpuPage: (params) => { + return request({ + url: '/product/spu/page', + method: 'GET', + params, + custom: { + showLoading: false, + showError: false, + }, + }); + }, + // 查询商品 + getSpuDetail: (id) => { + return request({ + url: '/product/spu/get-detail', + method: 'GET', + params: { id }, + custom: { + showLoading: false, + showError: false, + }, + }); + }, +}; +export default SpuApi; diff --git a/sheep/api/promotion/activity.js b/sheep/api/promotion/activity.js new file mode 100644 index 0000000..2449ebb --- /dev/null +++ b/sheep/api/promotion/activity.js @@ -0,0 +1,16 @@ +import request from '@/sheep/request'; + +const ActivityApi = { + // 获得单个商品,进行中的拼团、秒杀、砍价活动信息 + getActivityListBySpuId: (spuId) => { + return request({ + url: '/promotion/activity/list-by-spu-id', + method: 'GET', + params: { + spuId, + }, + }); + }, +}; + +export default ActivityApi; diff --git a/sheep/api/promotion/article.js b/sheep/api/promotion/article.js new file mode 100644 index 0000000..ded5fb1 --- /dev/null +++ b/sheep/api/promotion/article.js @@ -0,0 +1,12 @@ +import request from '@/sheep/request'; + +export default { + // 获得文章详情 + getArticle: (id, title) => { + return request({ + url: '/promotion/article/get', + method: 'GET', + params: { id, title } + }); + } +} diff --git a/sheep/api/promotion/combination.js b/sheep/api/promotion/combination.js new file mode 100644 index 0000000..8e8436c --- /dev/null +++ b/sheep/api/promotion/combination.js @@ -0,0 +1,78 @@ +import request from '@/sheep/request'; + +// 拼团 API +const CombinationApi = { + // 获得拼团活动分页 + getCombinationActivityPage: (params) => { + return request({ + url: '/promotion/combination-activity/page', + method: 'GET', + params, + }); + }, + + // 获得拼团活动明细 + getCombinationActivity: (id) => { + return request({ + url: '/promotion/combination-activity/get-detail', + method: 'GET', + params: { + id, + }, + }); + }, + + // 获得拼团活动列表,基于活动编号数组 + getCombinationActivityListByIds: (ids) => { + return request({ + url: '/promotion/combination-activity/list-by-ids', + method: 'GET', + params: { + ids, + }, + }); + }, + + // 获得最近 n 条拼团记录(团长发起的) + getHeadCombinationRecordList: (activityId, status, count) => { + return request({ + url: '/promotion/combination-record/get-head-list', + method: 'GET', + params: { + activityId, + status, + count, + }, + }); + }, + + // 获得我的拼团记录分页 + getCombinationRecordPage: (params) => { + return request({ + url: '/promotion/combination-record/page', + method: 'GET', + params, + }); + }, + + // 获得拼团记录明细 + getCombinationRecordDetail: (id) => { + return request({ + url: '/promotion/combination-record/get-detail', + method: 'GET', + params: { + id, + }, + }); + }, + + // 获得拼团记录的概要信息 + getCombinationRecordSummary: () => { + return request({ + url: '/promotion/combination-record/get-summary', + method: 'GET', + }); + }, +}; + +export default CombinationApi; diff --git a/sheep/api/promotion/coupon.js b/sheep/api/promotion/coupon.js new file mode 100644 index 0000000..f0382ee --- /dev/null +++ b/sheep/api/promotion/coupon.js @@ -0,0 +1,84 @@ +import request from '@/sheep/request'; + +const CouponApi = { + // 获得优惠劵模板列表 + getCouponTemplateListByIds: (ids) => { + return request({ + url: '/promotion/coupon-template/list-by-ids', + method: 'GET', + params: { ids }, + custom: { + showLoading: false, // 不展示 Loading,避免领取优惠劵时,不成功提示 + showError: false, + }, + }); + }, + // 获得优惠劵模版列表 + getCouponTemplateList: (spuId, productScope, count) => { + return request({ + url: '/promotion/coupon-template/list', + method: 'GET', + params: { spuId, productScope, count }, + }); + }, + // 获得优惠劵模版分页 + getCouponTemplatePage: (params) => { + return request({ + url: '/promotion/coupon-template/page', + method: 'GET', + params, + }); + }, + // 获得优惠劵模版 + getCouponTemplate: (id) => { + return request({ + url: '/promotion/coupon-template/get', + method: 'GET', + params: { id }, + }); + }, + // 我的优惠劵列表 + getCouponPage: (params) => { + return request({ + url: '/promotion/coupon/page', + method: 'GET', + params, + }); + }, + // 领取优惠券 + takeCoupon: (templateId) => { + return request({ + url: '/promotion/coupon/take', + method: 'POST', + data: { templateId }, + custom: { + auth: true, + showLoading: true, + loadingMsg: '领取中', + showSuccess: true, + successMsg: '领取成功', + }, + }); + }, + // 获得优惠劵 + getCoupon: (id) => { + return request({ + url: '/promotion/coupon/get', + method: 'GET', + params: { id }, + }); + }, + // 获得未使用的优惠劵数量 + getUnusedCouponCount: () => { + return request({ + url: '/promotion/coupon/get-unused-count', + method: 'GET', + custom: { + showLoading: false, + auth: true, + }, + }); + }, +}; + +export default CouponApi; diff --git a/sheep/api/promotion/diy.js b/sheep/api/promotion/diy.js new file mode 100644 index 0000000..e524f69 --- /dev/null +++ b/sheep/api/promotion/diy.js @@ -0,0 +1,38 @@ +import request from '@/sheep/request'; + +const DiyApi = { + getUsedDiyTemplate: () => { + return request({ + url: '/promotion/diy-template/used', + method: 'GET', + custom: { + showError: false, + showLoading: false, + }, + }); + }, + getDiyTemplate: (id) => { + return request({ + url: '/promotion/diy-template/get', + method: 'GET', + params: { + id + }, + custom: { + showError: false, + showLoading: false, + }, + }); + }, + getDiyPage: (id) => { + return request({ + url: '/promotion/diy-page/get', + method: 'GET', + params: { + id + } + }); + }, +}; + +export default DiyApi; diff --git a/sheep/api/promotion/kefu.js b/sheep/api/promotion/kefu.js new file mode 100644 index 0000000..6b512f7 --- /dev/null +++ b/sheep/api/promotion/kefu.js @@ -0,0 +1,31 @@ +import request from '@/sheep/request'; + +const KeFuApi = { + sendKefuMessage: (data) => { + return request({ + url: '/promotion/kefu-message/send', + method: 'POST', + data, + custom: { + auth: true, + showLoading: true, + loadingMsg: '发送中', + showSuccess: true, + successMsg: '发送成功', + }, + }); + }, + getKefuMessageList: (params) => { + return request({ + url: '/promotion/kefu-message/list', + method: 'GET', + params, + custom: { + auth: true, + showLoading: false, + }, + }); + }, +}; + +export default KeFuApi; diff --git a/sheep/api/promotion/point.js b/sheep/api/promotion/point.js new file mode 100644 index 0000000..d4e17b8 --- /dev/null +++ b/sheep/api/promotion/point.js @@ -0,0 +1,30 @@ +import request from '@/sheep/request'; + +const PointApi = { + // 获得积分商城活动分页 + getPointActivityPage: (params) => { + return request({ url: 'promotion/point-activity/page', method: 'GET', params }); + }, + + // 获得积分商城活动列表,基于活动编号数组 + getPointActivityListByIds: (ids) => { + return request({ + url: '/promotion/point-activity/list-by-ids', + method: 'GET', + params: { + ids, + }, + }); + }, + + // 获得积分商城活动明细 + getPointActivity: (id) => { + return request({ + url: 'promotion/point-activity/get-detail', + method: 'GET', + params: { id }, + }); + }, +}; + +export default PointApi; diff --git a/sheep/api/promotion/rewardActivity.js b/sheep/api/promotion/rewardActivity.js new file mode 100644 index 0000000..5f74db7 --- /dev/null +++ b/sheep/api/promotion/rewardActivity.js @@ -0,0 +1,14 @@ +import request from '@/sheep/request'; + +const RewardActivityApi = { + // 获得满减送活动 + getRewardActivity: (id) => { + return request({ + url: '/promotion/reward-activity/get', + method: 'GET', + params: { id }, + }); + } +}; + +export default RewardActivityApi; \ No newline at end of file diff --git a/sheep/api/promotion/seckill.js b/sheep/api/promotion/seckill.js new file mode 100644 index 0000000..f57e054 --- /dev/null +++ b/sheep/api/promotion/seckill.js @@ -0,0 +1,44 @@ +import request from '@/sheep/request'; + +const SeckillApi = { + // 获得秒杀时间段列表 + getSeckillConfigList: () => { + return request({ url: 'promotion/seckill-config/list', method: 'GET' }); + }, + + // 获得当前秒杀活动 + getNowSeckillActivity: () => { + return request({ url: 'promotion/seckill-activity/get-now', method: 'GET' }); + }, + + // 获得秒杀活动分页 + getSeckillActivityPage: (params) => { + return request({ url: 'promotion/seckill-activity/page', method: 'GET', params }); + }, + + // 获得秒杀活动列表,基于活动编号数组 + getSeckillActivityListByIds: (ids) => { + return request({ + url: '/promotion/seckill-activity/list-by-ids', + method: 'GET', + params: { + ids, + }, + }); + }, + + /** + * 获得秒杀活动明细 + * @param {number} id 秒杀活动编号 + * @return {*} + */ + getSeckillActivity: (id) => { + return request({ + url: 'promotion/seckill-activity/get-detail', + method: 'GET', + params: { id }, + }); + }, +}; + +export default SeckillApi; diff --git a/sheep/api/system/area.js b/sheep/api/system/area.js new file mode 100644 index 0000000..7c41eff --- /dev/null +++ b/sheep/api/system/area.js @@ -0,0 +1,13 @@ +import request from '@/sheep/request'; + +const AreaApi = { + // 获得地区树 + getAreaTree: () => { + return request({ + url: '/system/area/tree', + method: 'GET' + }); + }, +}; + +export default AreaApi; diff --git a/sheep/api/system/dict.js b/sheep/api/system/dict.js new file mode 100644 index 0000000..cab64f7 --- /dev/null +++ b/sheep/api/system/dict.js @@ -0,0 +1,16 @@ +import request from '@/sheep/request'; + +const DictApi = { + // 根据字典类型查询字典数据信息 + getDictDataListByType: (type) => { + return request({ + url: `/system/dict-data/type`, + method: 'GET', + params: { + type, + }, + }); + }, +}; + +export default DictApi; diff --git a/sheep/api/trade/afterSale.js b/sheep/api/trade/afterSale.js new file mode 100644 index 0000000..44c4dd3 --- /dev/null +++ b/sheep/api/trade/afterSale.js @@ -0,0 +1,63 @@ +import request from '@/sheep/request'; + +const AfterSaleApi = { + // 获得售后分页 + getAfterSalePage: (params) => { + return request({ + url: `/trade/after-sale/page`, + method: 'GET', + params, + custom: { + showLoading: false, + }, + }); + }, + // 创建售后 + createAfterSale: (data) => { + return request({ + url: `/trade/after-sale/create`, + method: 'POST', + data, + }); + }, + // 获得售后 + getAfterSale: (id) => { + return request({ + url: `/trade/after-sale/get`, + method: 'GET', + params: { + id, + }, + }); + }, + // 取消售后 + cancelAfterSale: (id) => { + return request({ + url: `/trade/after-sale/cancel`, + method: 'DELETE', + params: { + id, + }, + }); + }, + // 获得售后日志列表 + getAfterSaleLogList: (afterSaleId) => { + return request({ + url: `/trade/after-sale-log/list`, + method: 'GET', + params: { + afterSaleId, + }, + }); + }, + // 退回货物 + deliveryAfterSale: (data) => { + return request({ + url: `/trade/after-sale/delivery`, + method: 'PUT', + data, + }); + } +}; + +export default AfterSaleApi; diff --git a/sheep/api/trade/brokerage.js b/sheep/api/trade/brokerage.js new file mode 100644 index 0000000..da55790 --- /dev/null +++ b/sheep/api/trade/brokerage.js @@ -0,0 +1,111 @@ +import request from '@/sheep/request'; + +const BrokerageApi = { + // 绑定分销用户 + bindBrokerageUser: (data) => { + return request({ + url: '/trade/brokerage-user/bind', + method: 'PUT', + data, + }); + }, + // 获得个人分销信息 + getBrokerageUser: () => { + return request({ + url: '/trade/brokerage-user/get', + method: 'GET', + }); + }, + // 获得个人分销统计 + getBrokerageUserSummary: () => { + return request({ + url: '/trade/brokerage-user/get-summary', + method: 'GET', + }); + }, + // 获得分销记录分页 + getBrokerageRecordPage: (params) => { + if (params.status === undefined) { + delete params.status; + } + const queryString = Object.keys(params) + .map((key) => encodeURIComponent(key) + '=' + params[key]) + .join('&'); + return request({ + url: `/trade/brokerage-record/page?${queryString}`, + method: 'GET', + }); + }, + // 创建分销提现 + createBrokerageWithdraw: (data) => { + return request({ + url: '/trade/brokerage-withdraw/create', + method: 'POST', + data, + }); + }, + // 获得分销提现分页 + getBrokerageWithdrawPage: (params) => { + const queryString = Object.keys(params) + .map((key) => encodeURIComponent(key) + '=' + params[key]) + .join('&'); + return request({ + url: `/trade/brokerage-withdraw/page?${queryString}`, + method: 'GET', + }); + }, + // 获得分销提现详情 + getBrokerageWithdraw: (id) => { + return request({ + url: `/trade/brokerage-withdraw/get`, + method: 'GET', + params: { id }, + }); + }, + // 获得商品的分销金额 + getProductBrokeragePrice: (spuId) => { + return request({ + url: '/trade/brokerage-record/get-product-brokerage-price', + method: 'GET', + params: { spuId }, + }); + }, + // 获得分销用户排行(基于佣金) + getRankByPrice: (params) => { + const queryString = `times=${params.times[0]}×=${params.times[1]}`; + return request({ + url: `/trade/brokerage-user/get-rank-by-price?${queryString}`, + method: 'GET', + }); + }, + // 获得分销用户排行分页(基于佣金) + getBrokerageUserChildSummaryPageByPrice: (params) => { + const queryString = Object.keys(params) + .map((key) => encodeURIComponent(key) + '=' + params[key]) + .join('&'); + return request({ + url: `/trade/brokerage-user/rank-page-by-price?${queryString}`, + method: 'GET', + }); + }, + // 获得分销用户排行分页(基于用户量) + getBrokerageUserRankPageByUserCount: (params) => { + const queryString = Object.keys(params) + .map((key) => encodeURIComponent(key) + '=' + params[key]) + .join('&'); + return request({ + url: `/trade/brokerage-user/rank-page-by-user-count?${queryString}`, + method: 'GET', + }); + }, + // 获得下级分销统计分页 + getBrokerageUserChildSummaryPage: (params) => { + return request({ + url: '/trade/brokerage-user/child-summary-page', + method: 'GET', + params, + }); + }, +}; + +export default BrokerageApi; diff --git a/sheep/api/trade/cart.js b/sheep/api/trade/cart.js new file mode 100644 index 0000000..63cea18 --- /dev/null +++ b/sheep/api/trade/cart.js @@ -0,0 +1,50 @@ +import request from '@/sheep/request'; + +const CartApi = { + addCart: (data) => { + return request({ + url: '/trade/cart/add', + method: 'POST', + data: data, + custom: { + showSuccess: true, + successMsg: '已添加到购物车~', + } + }); + }, + updateCartCount: (data) => { + return request({ + url: '/trade/cart/update-count', + method: 'PUT', + data: data + }); + }, + updateCartSelected: (data) => { + return request({ + url: '/trade/cart/update-selected', + method: 'PUT', + data: data + }); + }, + deleteCart: (ids) => { + return request({ + url: '/trade/cart/delete', + method: 'DELETE', + params: { + ids + } + }); + }, + getCartList: () => { + return request({ + url: '/trade/cart/list', + method: 'GET', + custom: { + showLoading: false, + auth: true, + }, + }); + }, +}; + +export default CartApi; \ No newline at end of file diff --git a/sheep/api/trade/config.js b/sheep/api/trade/config.js new file mode 100644 index 0000000..d49235e --- /dev/null +++ b/sheep/api/trade/config.js @@ -0,0 +1,16 @@ +import request from '@/sheep/request'; + +const TradeConfigApi = { + // 获得交易配置 + getTradeConfig: () => { + return request({ + url: `/trade/config/get`, + method: 'GET', + custom: { + showLoading: false, + }, + }); + }, +}; + +export default TradeConfigApi; diff --git a/sheep/api/trade/delivery.js b/sheep/api/trade/delivery.js new file mode 100644 index 0000000..a6551da --- /dev/null +++ b/sheep/api/trade/delivery.js @@ -0,0 +1,31 @@ +import request from '@/sheep/request'; + +const DeliveryApi = { + // 获得快递公司列表 + getDeliveryExpressList: () => { + return request({ + url: `/trade/delivery/express/list`, + method: 'get', + }); + }, + // 获得自提门店列表 + getDeliveryPickUpStoreList: (params) => { + return request({ + url: `/trade/delivery/pick-up-store/list`, + method: 'GET', + params, + }); + }, + // 获得自提门店 + getDeliveryPickUpStore: (id) => { + return request({ + url: `/trade/delivery/pick-up-store/get`, + method: 'GET', + params: { + id, + }, + }); + }, +}; + +export default DeliveryApi; diff --git a/sheep/api/trade/order.js b/sheep/api/trade/order.js new file mode 100644 index 0000000..10cb635 --- /dev/null +++ b/sheep/api/trade/order.js @@ -0,0 +1,168 @@ +import request from '@/sheep/request'; +import { isEmpty } from '@/sheep/helper/utils'; + +const OrderApi = { + // 计算订单信息 + settlementOrder: (data) => { + const data2 = { + ...data, + }; + // 移除多余字段 + if (!(data.couponId > 0)) { + delete data2.couponId; + } + if (!(data.addressId > 0)) { + delete data2.addressId; + } + if (!(data.pickUpStoreId > 0)) { + delete data2.pickUpStoreId; + } + if (isEmpty(data.receiverName)) { + delete data2.receiverName; + } + if (isEmpty(data.receiverMobile)) { + delete data2.receiverMobile; + } + if (!(data.combinationActivityId > 0)) { + delete data2.combinationActivityId; + } + if (!(data.combinationHeadId > 0)) { + delete data2.combinationHeadId; + } + if (!(data.seckillActivityId > 0)) { + delete data2.seckillActivityId; + } + if (!(data.pointActivityId > 0)) { + delete data2.pointActivityId; + } + if (!(data.deliveryType > 0)) { + delete data2.deliveryType; + } + // 解决 SpringMVC 接受 List 参数的问题 + delete data2.items; + for (let i = 0; i < data.items.length; i++) { + data2[encodeURIComponent('items[' + i + '' + '].skuId')] = data.items[i].skuId + ''; + data2[encodeURIComponent('items[' + i + '' + '].count')] = data.items[i].count + ''; + if (data.items[i].cartId) { + data2[encodeURIComponent('items[' + i + '' + '].cartId')] = data.items[i].cartId + ''; + } + } + const queryString = Object.keys(data2) + .map((key) => key + '=' + data2[key]) + .join('&'); + return request({ + url: `/trade/order/settlement?${queryString}`, + method: 'GET', + custom: { + showError: true, + showLoading: true, + }, + }); + }, + // 获得商品结算信息 + getSettlementProduct: (spuIds) => { + return request({ + url: '/trade/order/settlement-product', + method: 'GET', + params: { spuIds }, + custom: { + showLoading: false, + showError: false, + }, + }); + }, + // 创建订单 + createOrder: (data) => { + return request({ + url: `/trade/order/create`, + method: 'POST', + data, + }); + }, + // 获得订单详细:sync 是可选参数 + getOrderDetail: (id, sync) => { + return request({ + url: `/trade/order/get-detail`, + method: 'GET', + params: { + id, + sync, + }, + custom: { + showLoading: false, + }, + }); + }, + // 订单列表 + getOrderPage: (params) => { + return request({ + url: '/trade/order/page', + method: 'GET', + params, + custom: { + showLoading: false, + }, + }); + }, + // 确认收货 + receiveOrder: (id) => { + return request({ + url: `/trade/order/receive`, + method: 'PUT', + params: { + id, + }, + }); + }, + // 取消订单 + cancelOrder: (id) => { + return request({ + url: `/trade/order/cancel`, + method: 'DELETE', + params: { + id, + }, + }); + }, + // 删除订单 + deleteOrder: (id) => { + return request({ + url: `/trade/order/delete`, + method: 'DELETE', + params: { + id, + }, + }); + }, + // 获得交易订单的物流轨迹 + getOrderExpressTrackList: (id) => { + return request({ + url: `/trade/order/get-express-track-list`, + method: 'GET', + params: { + id, + }, + }); + }, + // 获得交易订单数量 + getOrderCount: () => { + return request({ + url: '/trade/order/get-count', + method: 'GET', + custom: { + showLoading: false, + auth: true, + }, + }); + }, + // 创建单个评论 + createOrderItemComment: (data) => { + return request({ + url: `/trade/order/item/create-comment`, + method: 'POST', + data, + }); + }, +}; + +export default OrderApi; diff --git a/sheep/components/s-activity-pop/s-activity-pop.vue b/sheep/components/s-activity-pop/s-activity-pop.vue new file mode 100644 index 0000000..e6ee26b --- /dev/null +++ b/sheep/components/s-activity-pop/s-activity-pop.vue @@ -0,0 +1,256 @@ + + + + diff --git a/sheep/components/s-address-item/s-address-item.vue b/sheep/components/s-address-item/s-address-item.vue new file mode 100644 index 0000000..45e361f --- /dev/null +++ b/sheep/components/s-address-item/s-address-item.vue @@ -0,0 +1,110 @@ + + + + + + diff --git a/sheep/components/s-auth-modal/components/account-login.vue b/sheep/components/s-auth-modal/components/account-login.vue new file mode 100644 index 0000000..d5d6056 --- /dev/null +++ b/sheep/components/s-auth-modal/components/account-login.vue @@ -0,0 +1,111 @@ + + + + + + diff --git a/sheep/components/s-auth-modal/components/change-mobile.vue b/sheep/components/s-auth-modal/components/change-mobile.vue new file mode 100644 index 0000000..f4dd5f1 --- /dev/null +++ b/sheep/components/s-auth-modal/components/change-mobile.vue @@ -0,0 +1,127 @@ + + + + + + diff --git a/sheep/components/s-auth-modal/components/change-password.vue b/sheep/components/s-auth-modal/components/change-password.vue new file mode 100644 index 0000000..b02ea02 --- /dev/null +++ b/sheep/components/s-auth-modal/components/change-password.vue @@ -0,0 +1,106 @@ + + + + + + diff --git a/sheep/components/s-auth-modal/components/mp-authorization.vue b/sheep/components/s-auth-modal/components/mp-authorization.vue new file mode 100644 index 0000000..df5867f --- /dev/null +++ b/sheep/components/s-auth-modal/components/mp-authorization.vue @@ -0,0 +1,152 @@ + + + + + + diff --git a/sheep/components/s-auth-modal/components/reset-password.vue b/sheep/components/s-auth-modal/components/reset-password.vue new file mode 100644 index 0000000..bc1be8b --- /dev/null +++ b/sheep/components/s-auth-modal/components/reset-password.vue @@ -0,0 +1,119 @@ + + + + + + diff --git a/sheep/components/s-auth-modal/components/sms-login.vue b/sheep/components/s-auth-modal/components/sms-login.vue new file mode 100644 index 0000000..8b728ac --- /dev/null +++ b/sheep/components/s-auth-modal/components/sms-login.vue @@ -0,0 +1,138 @@ + + + + + + diff --git a/sheep/components/s-auth-modal/index.scss b/sheep/components/s-auth-modal/index.scss new file mode 100644 index 0000000..c4424e7 --- /dev/null +++ b/sheep/components/s-auth-modal/index.scss @@ -0,0 +1,151 @@ +@keyframes title-animation { + 0% { + font-size: 32rpx; + } + 100% { + font-size: 36rpx; + } +} + +.login-wrap { + padding: 50rpx 34rpx; + min-height: 500rpx; + background-color: #fff; + border-radius: 20rpx 20rpx 0 0; +} + +.head-box { + .head-title { + min-width: 160rpx; + font-size: 36rpx; + font-weight: bold; + color: #333333; + line-height: 36rpx; + } + .head-title-active { + width: 160rpx; + font-size: 32rpx; + font-weight: 600; + color: #999; + line-height: 36rpx; + } + .head-title-animation { + animation-name: title-animation; + animation-duration: 0.1s; + animation-timing-function: ease-out; + animation-fill-mode: forwards; + } + .head-title-line { + position: relative; + &::before { + content: ''; + width: 1rpx; + height: 34rpx; + background-color: #e4e7ed; + position: absolute; + left: -30rpx; + top: 50%; + transform: translateY(-50%); + } + } + .head-subtitle { + font-size: 26rpx; + font-weight: 400; + color: #afb6c0; + text-align: left; + display: flex; + } +} + +// .code-btn[disabled] { +// background-color: #fff; +// } +.code-btn-start { + width: 160rpx; + height: 56rpx; + line-height: normal; + border: 2rpx solid var(--ui-BG-Main); + border-radius: 28rpx; + font-size: 26rpx; + font-weight: 400; + color: var(--ui-BG-Main); + opacity: 1; +} + +.forgot-btn { + width: 160rpx; + line-height: 56rpx; + font-size: 30rpx; + font-weight: 500; + color: #999; +} + +.login-btn-start { + width: 158rpx; + height: 56rpx; + line-height: normal; + background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient)); + border-radius: 28rpx; + font-size: 26rpx; + font-weight: 500; + color: #fff; +} + +.type-btn { + padding: 20rpx; + margin: 40rpx auto; + width: 200rpx; + font-size: 30rpx; + font-weight: 500; + color: #999999; +} + +.auto-login-box { + width: 100%; + .auto-login-btn { + width: 68rpx; + height: 68rpx; + border-radius: 50%; + margin: 0 30rpx; + } + .auto-login-img { + width: 68rpx; + height: 68rpx; + border-radius: 50%; + } +} + +.agreement-box { + margin: 80rpx auto 0; + .protocol-check { + transform: scale(0.7); + } + .agreement-text { + font-size: 26rpx; + font-weight: 500; + color: #999999; + .tcp-text { + color: var(--ui-BG-Main); + } + } +} + +// 修改密码 +.editPwd-btn-box { + .save-btn { + width: 690rpx; + line-height: 70rpx; + background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient)); + border-radius: 35rpx; + font-size: 28rpx; + font-weight: 500; + color: #ffffff; + } + .forgot-btn { + width: 690rpx; + line-height: 70rpx; + font-size: 28rpx; + font-weight: 500; + color: #999999; + } +} diff --git a/sheep/components/s-auth-modal/s-auth-modal.vue b/sheep/components/s-auth-modal/s-auth-modal.vue new file mode 100644 index 0000000..2b39689 --- /dev/null +++ b/sheep/components/s-auth-modal/s-auth-modal.vue @@ -0,0 +1,301 @@ + + + + + diff --git a/sheep/components/s-block-item/s-block-item.vue b/sheep/components/s-block-item/s-block-item.vue new file mode 100644 index 0000000..5272dcd --- /dev/null +++ b/sheep/components/s-block-item/s-block-item.vue @@ -0,0 +1,83 @@ + + + + + diff --git a/sheep/components/s-block/s-block.vue b/sheep/components/s-block/s-block.vue new file mode 100644 index 0000000..152b8a6 --- /dev/null +++ b/sheep/components/s-block/s-block.vue @@ -0,0 +1,54 @@ + + + + diff --git a/sheep/components/s-count-down/s-count-down.vue b/sheep/components/s-count-down/s-count-down.vue new file mode 100644 index 0000000..98b3a1f --- /dev/null +++ b/sheep/components/s-count-down/s-count-down.vue @@ -0,0 +1,173 @@ + + + + + \ No newline at end of file diff --git a/sheep/components/s-coupon-block/s-coupon-block.vue b/sheep/components/s-coupon-block/s-coupon-block.vue new file mode 100644 index 0000000..3078247 --- /dev/null +++ b/sheep/components/s-coupon-block/s-coupon-block.vue @@ -0,0 +1,192 @@ + + + + + + diff --git a/sheep/components/s-coupon-card/s-coupon-card.vue b/sheep/components/s-coupon-card/s-coupon-card.vue new file mode 100644 index 0000000..dac69e3 --- /dev/null +++ b/sheep/components/s-coupon-card/s-coupon-card.vue @@ -0,0 +1,102 @@ + + + + + + \ No newline at end of file diff --git a/sheep/components/s-coupon-get/s-coupon-get.vue b/sheep/components/s-coupon-get/s-coupon-get.vue new file mode 100644 index 0000000..6df6b55 --- /dev/null +++ b/sheep/components/s-coupon-get/s-coupon-get.vue @@ -0,0 +1,109 @@ + + + + diff --git a/sheep/components/s-coupon-list/s-coupon-list.vue b/sheep/components/s-coupon-list/s-coupon-list.vue new file mode 100644 index 0000000..17b083e --- /dev/null +++ b/sheep/components/s-coupon-list/s-coupon-list.vue @@ -0,0 +1,195 @@ + + + + + diff --git a/sheep/components/s-coupon-select/s-coupon-select.vue b/sheep/components/s-coupon-select/s-coupon-select.vue new file mode 100644 index 0000000..d0711f8 --- /dev/null +++ b/sheep/components/s-coupon-select/s-coupon-select.vue @@ -0,0 +1,149 @@ + + + + diff --git a/sheep/components/s-custom-navbar/components/navbar-item.vue b/sheep/components/s-custom-navbar/components/navbar-item.vue new file mode 100644 index 0000000..7d299b2 --- /dev/null +++ b/sheep/components/s-custom-navbar/components/navbar-item.vue @@ -0,0 +1,67 @@ + + + + + + diff --git a/sheep/components/s-custom-navbar/components/navbar.vue b/sheep/components/s-custom-navbar/components/navbar.vue new file mode 100644 index 0000000..36050ec --- /dev/null +++ b/sheep/components/s-custom-navbar/components/navbar.vue @@ -0,0 +1,314 @@ + + + + + diff --git a/sheep/components/s-custom-navbar/s-custom-navbar.vue b/sheep/components/s-custom-navbar/s-custom-navbar.vue new file mode 100644 index 0000000..62f6e21 --- /dev/null +++ b/sheep/components/s-custom-navbar/s-custom-navbar.vue @@ -0,0 +1,207 @@ + + + + + + diff --git a/sheep/components/s-discount-list/s-discount-list.vue b/sheep/components/s-discount-list/s-discount-list.vue new file mode 100644 index 0000000..0526a73 --- /dev/null +++ b/sheep/components/s-discount-list/s-discount-list.vue @@ -0,0 +1,96 @@ + + + diff --git a/sheep/components/s-empty/s-empty.vue b/sheep/components/s-empty/s-empty.vue new file mode 100644 index 0000000..27f8c4a --- /dev/null +++ b/sheep/components/s-empty/s-empty.vue @@ -0,0 +1,93 @@ + + + + + diff --git a/sheep/components/s-float-menu/s-float-menu.vue b/sheep/components/s-float-menu/s-float-menu.vue new file mode 100644 index 0000000..b53db00 --- /dev/null +++ b/sheep/components/s-float-menu/s-float-menu.vue @@ -0,0 +1,88 @@ + + + + diff --git a/sheep/components/s-goods-card/s-goods-card.vue b/sheep/components/s-goods-card/s-goods-card.vue new file mode 100644 index 0000000..ca9b64f --- /dev/null +++ b/sheep/components/s-goods-card/s-goods-card.vue @@ -0,0 +1,306 @@ + + + + + + diff --git a/sheep/components/s-goods-column/s-goods-column.vue b/sheep/components/s-goods-column/s-goods-column.vue new file mode 100644 index 0000000..2f07705 --- /dev/null +++ b/sheep/components/s-goods-column/s-goods-column.vue @@ -0,0 +1,1026 @@ + + + + + + diff --git a/sheep/components/s-goods-item/s-goods-item.vue b/sheep/components/s-goods-item/s-goods-item.vue new file mode 100644 index 0000000..a29bd1a --- /dev/null +++ b/sheep/components/s-goods-item/s-goods-item.vue @@ -0,0 +1,189 @@ + + + + + diff --git a/sheep/components/s-goods-scroll/s-goods-scroll.vue b/sheep/components/s-goods-scroll/s-goods-scroll.vue new file mode 100644 index 0000000..175cc66 --- /dev/null +++ b/sheep/components/s-goods-scroll/s-goods-scroll.vue @@ -0,0 +1,33 @@ + + + + + + diff --git a/sheep/components/s-goods-shelves/s-goods-shelves.vue b/sheep/components/s-goods-shelves/s-goods-shelves.vue new file mode 100644 index 0000000..e2ce60a --- /dev/null +++ b/sheep/components/s-goods-shelves/s-goods-shelves.vue @@ -0,0 +1,147 @@ + + + + + + diff --git a/sheep/components/s-groupon-block/s-groupon-block.vue b/sheep/components/s-groupon-block/s-groupon-block.vue new file mode 100644 index 0000000..cf92496 --- /dev/null +++ b/sheep/components/s-groupon-block/s-groupon-block.vue @@ -0,0 +1,327 @@ + + + + + + diff --git a/sheep/components/s-hotzone-block/s-hotzone-block.vue b/sheep/components/s-hotzone-block/s-hotzone-block.vue new file mode 100644 index 0000000..246d6d4 --- /dev/null +++ b/sheep/components/s-hotzone-block/s-hotzone-block.vue @@ -0,0 +1,46 @@ + + + + + + diff --git a/sheep/components/s-image-banner/s-image-banner.vue b/sheep/components/s-image-banner/s-image-banner.vue new file mode 100644 index 0000000..a4e9a01 --- /dev/null +++ b/sheep/components/s-image-banner/s-image-banner.vue @@ -0,0 +1,51 @@ + + + + + + diff --git a/sheep/components/s-image-block/s-image-block.vue b/sheep/components/s-image-block/s-image-block.vue new file mode 100644 index 0000000..c898749 --- /dev/null +++ b/sheep/components/s-image-block/s-image-block.vue @@ -0,0 +1,27 @@ + + + + + + diff --git a/sheep/components/s-image-cube/s-image-cube.vue b/sheep/components/s-image-cube/s-image-cube.vue new file mode 100644 index 0000000..9794d8f --- /dev/null +++ b/sheep/components/s-image-cube/s-image-cube.vue @@ -0,0 +1,110 @@ + + + + + diff --git a/sheep/components/s-layout/s-layout.vue b/sheep/components/s-layout/s-layout.vue new file mode 100644 index 0000000..df15c31 --- /dev/null +++ b/sheep/components/s-layout/s-layout.vue @@ -0,0 +1,258 @@ + + + + + diff --git a/sheep/components/s-line-block/s-line-block.vue b/sheep/components/s-line-block/s-line-block.vue new file mode 100644 index 0000000..c0628f9 --- /dev/null +++ b/sheep/components/s-line-block/s-line-block.vue @@ -0,0 +1,15 @@ + + + + + + diff --git a/sheep/components/s-live-block/s-live-block.vue b/sheep/components/s-live-block/s-live-block.vue new file mode 100644 index 0000000..6e0b6c9 --- /dev/null +++ b/sheep/components/s-live-block/s-live-block.vue @@ -0,0 +1,144 @@ + + + diff --git a/sheep/components/s-live-card/s-live-card.vue b/sheep/components/s-live-card/s-live-card.vue new file mode 100644 index 0000000..9cefee0 --- /dev/null +++ b/sheep/components/s-live-card/s-live-card.vue @@ -0,0 +1,234 @@ + + + + diff --git a/sheep/components/s-menu-button/s-menu-button.vue b/sheep/components/s-menu-button/s-menu-button.vue new file mode 100644 index 0000000..ef647b8 --- /dev/null +++ b/sheep/components/s-menu-button/s-menu-button.vue @@ -0,0 +1,343 @@ + + + + + + \ No newline at end of file diff --git a/sheep/components/s-menu-grid/s-menu-grid.vue b/sheep/components/s-menu-grid/s-menu-grid.vue new file mode 100644 index 0000000..d05a49d --- /dev/null +++ b/sheep/components/s-menu-grid/s-menu-grid.vue @@ -0,0 +1,104 @@ + + + + + + \ No newline at end of file diff --git a/sheep/components/s-menu-list/s-menu-list.vue b/sheep/components/s-menu-list/s-menu-list.vue new file mode 100644 index 0000000..ecbb396 --- /dev/null +++ b/sheep/components/s-menu-list/s-menu-list.vue @@ -0,0 +1,66 @@ + + + + + + diff --git a/sheep/components/s-menu-tools/s-menu-tools.vue b/sheep/components/s-menu-tools/s-menu-tools.vue new file mode 100644 index 0000000..ee2058c --- /dev/null +++ b/sheep/components/s-menu-tools/s-menu-tools.vue @@ -0,0 +1,118 @@ + + + + + + diff --git a/sheep/components/s-notice-block/s-notice-block.vue b/sheep/components/s-notice-block/s-notice-block.vue new file mode 100644 index 0000000..e7d74a0 --- /dev/null +++ b/sheep/components/s-notice-block/s-notice-block.vue @@ -0,0 +1,38 @@ + + + + + diff --git a/sheep/components/s-order-card/s-order-card.vue b/sheep/components/s-order-card/s-order-card.vue new file mode 100644 index 0000000..b3d851b --- /dev/null +++ b/sheep/components/s-order-card/s-order-card.vue @@ -0,0 +1,132 @@ + + + + + + diff --git a/sheep/components/s-point-block/s-point-block.vue b/sheep/components/s-point-block/s-point-block.vue new file mode 100644 index 0000000..c2cf117 --- /dev/null +++ b/sheep/components/s-point-block/s-point-block.vue @@ -0,0 +1,328 @@ + + + + + + diff --git a/sheep/components/s-point-card/s-point-card.vue b/sheep/components/s-point-card/s-point-card.vue new file mode 100644 index 0000000..1c12c03 --- /dev/null +++ b/sheep/components/s-point-card/s-point-card.vue @@ -0,0 +1,383 @@ + + + + + + diff --git a/sheep/components/s-popup-image/s-popup-image.vue b/sheep/components/s-popup-image/s-popup-image.vue new file mode 100644 index 0000000..b10f477 --- /dev/null +++ b/sheep/components/s-popup-image/s-popup-image.vue @@ -0,0 +1,85 @@ + + + + + diff --git a/sheep/components/s-richtext-block/s-richtext-block.vue b/sheep/components/s-richtext-block/s-richtext-block.vue new file mode 100644 index 0000000..4e67a6d --- /dev/null +++ b/sheep/components/s-richtext-block/s-richtext-block.vue @@ -0,0 +1,46 @@ + + + + diff --git a/sheep/components/s-search-block/s-search-block.vue b/sheep/components/s-search-block/s-search-block.vue new file mode 100644 index 0000000..1e9c2b5 --- /dev/null +++ b/sheep/components/s-search-block/s-search-block.vue @@ -0,0 +1,164 @@ + + + + + diff --git a/sheep/components/s-seckill-block/s-seckill-block.vue b/sheep/components/s-seckill-block/s-seckill-block.vue new file mode 100644 index 0000000..d7e8013 --- /dev/null +++ b/sheep/components/s-seckill-block/s-seckill-block.vue @@ -0,0 +1,327 @@ + + + + + + diff --git a/sheep/components/s-select-groupon-sku/s-select-groupon-sku.vue b/sheep/components/s-select-groupon-sku/s-select-groupon-sku.vue new file mode 100644 index 0000000..9a1090d --- /dev/null +++ b/sheep/components/s-select-groupon-sku/s-select-groupon-sku.vue @@ -0,0 +1,508 @@ + + + + + diff --git a/sheep/components/s-select-seckill-sku/s-select-seckill-sku.vue b/sheep/components/s-select-seckill-sku/s-select-seckill-sku.vue new file mode 100644 index 0000000..ba6f7bf --- /dev/null +++ b/sheep/components/s-select-seckill-sku/s-select-seckill-sku.vue @@ -0,0 +1,453 @@ + + + + + + diff --git a/sheep/components/s-select-sku/s-select-sku.vue b/sheep/components/s-select-sku/s-select-sku.vue new file mode 100644 index 0000000..7920a90 --- /dev/null +++ b/sheep/components/s-select-sku/s-select-sku.vue @@ -0,0 +1,464 @@ + + + + + diff --git a/sheep/components/s-share-modal/canvas-poster/index.vue b/sheep/components/s-share-modal/canvas-poster/index.vue new file mode 100644 index 0000000..9e67943 --- /dev/null +++ b/sheep/components/s-share-modal/canvas-poster/index.vue @@ -0,0 +1,168 @@ + + + + + + diff --git a/sheep/components/s-share-modal/canvas-poster/poster/goods.js b/sheep/components/s-share-modal/canvas-poster/poster/goods.js new file mode 100644 index 0000000..883e4be --- /dev/null +++ b/sheep/components/s-share-modal/canvas-poster/poster/goods.js @@ -0,0 +1,125 @@ +import sheep from '@/sheep'; +import { formatImageUrlProtocol, getWxaQrcode } from './index'; + +const goods = async (poster) => { + const width = poster.width; + const userInfo = sheep.$store('user').userInfo; + const wxa_qrcode = await getWxaQrcode(poster.shareInfo.path, poster.shareInfo.query); + return [ + { + type: 'image', + src: formatImageUrlProtocol(sheep.$url.cdn(sheep.$store('app').platform.share.posterInfo.goods_bg)), + css: { + width, + position: 'fixed', + 'object-fit': 'contain', + top: '0', + left: '0', + zIndex: -1, + }, + }, + { + type: 'text', + text: userInfo.nickname, + css: { + color: '#333', + fontSize: 16, + fontFamily: 'sans-serif', + position: 'fixed', + top: width * 0.06, + left: width * 0.22, + }, + }, + { + type: 'image', + src: formatImageUrlProtocol(sheep.$url.cdn(userInfo.avatar)), + css: { + position: 'fixed', + left: width * 0.04, + top: width * 0.04, + width: width * 0.14, + height: width * 0.14, + }, + }, + { + type: 'image', + src: formatImageUrlProtocol(poster.shareInfo.poster.image), + css: { + position: 'fixed', + left: width * 0.03, + top: width * 0.21, + width: width * 0.94, + height: width * 0.94, + }, + }, + { + type: 'text', + text: poster.shareInfo.poster.title, + css: { + position: 'fixed', + left: width * 0.04, + top: width * 1.18, + color: '#333', + fontSize: 14, + lineHeight: 15, + maxWidth: width * 0.91, + }, + }, + { + type: 'text', + text: '¥' + poster.shareInfo.poster.price, + css: { + position: 'fixed', + left: width * 0.04, + top: width * 1.31, + fontSize: 20, + fontFamily: 'OPPOSANS', + color: '#333', + }, + }, + { + type: 'text', + text: + poster.shareInfo.poster.original_price > 0 + ? '¥' + poster.shareInfo.poster.original_price + : '', + css: { + position: 'fixed', + left: width * 0.3, + top: width * 1.33, + color: '#999', + fontSize: 10, + fontFamily: 'OPPOSANS', + textDecoration: 'line-through', + }, + }, + // #ifndef MP-WEIXIN + { + type: 'qrcode', + text: poster.shareInfo.link, + css: { + position: 'fixed', + left: width * 0.75, + top: width * 1.3, + width: width * 0.2, + height: width * 0.2, + }, + }, + // #endif + // #ifdef MP-WEIXIN + { + type: 'image', + src: wxa_qrcode, + css: { + position: 'fixed', + left: width * 0.75, + top: width * 1.3, + width: width * 0.2, + height: width * 0.2, + }, + }, + // #endif + ]; +}; + +export default goods; diff --git a/sheep/components/s-share-modal/canvas-poster/poster/groupon.js b/sheep/components/s-share-modal/canvas-poster/poster/groupon.js new file mode 100644 index 0000000..c182d3f --- /dev/null +++ b/sheep/components/s-share-modal/canvas-poster/poster/groupon.js @@ -0,0 +1,125 @@ +import sheep from '@/sheep'; +import { formatImageUrlProtocol, getWxaQrcode } from './index'; + +const groupon = async (poster) => { + debugger; + const width = poster.width; + const userInfo = sheep.$store('user').userInfo; + const wxa_qrcode = await getWxaQrcode(poster.shareInfo.path, poster.shareInfo.query); + return [ + { + type: 'image', + src: formatImageUrlProtocol( + sheep.$url.cdn(sheep.$store('app').platform.share.posterInfo.groupon_bg), + ), + css: { + width, + position: 'fixed', + 'object-fit': 'contain', + top: '0', + left: '0', + zIndex: -1, + }, + }, + { + type: 'text', + text: userInfo.nickname, + css: { + color: '#333', + fontSize: 16, + fontFamily: 'sans-serif', + position: 'fixed', + top: width * 0.06, + left: width * 0.22, + }, + }, + { + type: 'image', + src: formatImageUrlProtocol(sheep.$url.cdn(userInfo.avatar)), + css: { + position: 'fixed', + left: width * 0.04, + top: width * 0.04, + width: width * 0.14, + height: width * 0.14, + }, + }, + { + type: 'image', + src: formatImageUrlProtocol(poster.shareInfo.poster.image), + css: { + position: 'fixed', + left: width * 0.03, + top: width * 0.21, + width: width * 0.94, + height: width * 0.94, + borderRadius: 10, + }, + }, + { + type: 'text', + text: poster.shareInfo.poster.title, + css: { + color: '#333', + fontSize: 14, + position: 'fixed', + top: width * 1.18, + left: width * 0.04, + maxWidth: width * 0.91, + lineHeight: 5, + }, + }, + { + type: 'text', + text: '¥' + poster.shareInfo.poster.price, + css: { + color: '#ff0000', + fontSize: 20, + fontFamily: 'OPPOSANS', + position: 'fixed', + top: width * 1.3, + left: width * 0.04, + }, + }, + { + type: 'text', + text: poster.shareInfo.poster.grouponNum + '人团', + css: { + color: '#fff', + fontSize: 12, + fontFamily: 'OPPOSANS', + position: 'fixed', + left: width * 0.84, + top: width * 1.3, + }, + }, + // #ifndef MP-WEIXIN + { + type: 'qrcode', + text: poster.shareInfo.link, + css: { + position: 'fixed', + left: width * 0.75, + top: width * 1.4, + width: width * 0.2, + height: width * 0.2, + }, + }, + // #endif + // #ifdef MP-WEIXIN + { + type: 'image', + src: wxa_qrcode, + css: { + position: 'fixed', + left: width * 0.75, + top: width * 1.4, + width: width * 0.2, + height: width * 0.2, + }, + }, + // #endif + ]; +}; + +export default groupon; diff --git a/sheep/components/s-share-modal/canvas-poster/poster/index.js b/sheep/components/s-share-modal/canvas-poster/poster/index.js new file mode 100644 index 0000000..0724e7f --- /dev/null +++ b/sheep/components/s-share-modal/canvas-poster/poster/index.js @@ -0,0 +1,39 @@ +import user from './user'; +import goods from './goods'; +import groupon from './groupon'; +import SocialApi from '@/sheep/api/member/social'; + +export function getPosterData(options) { + switch (options.shareInfo.poster.type) { + case 'user': + return user(options); + case 'goods': + return goods(options); + case 'groupon': + return groupon(options); + } +} + +export function formatImageUrlProtocol(url) { + // #ifdef H5 + // H5平台 https协议下需要转换 + if (window.location.protocol === 'https:' && url.indexOf('http:') === 0) { + url = url.replace('http:', 'https:'); + } + // #endif + + // #ifdef MP-WEIXIN + // 小程序平台 需要强制转换为https协议 + if (url.indexOf('http:') === 0) { + url = url.replace('http:', 'https:'); + } + // #endif + + return url; +} + +// 获得微信小程序码 (Base64 image) +export async function getWxaQrcode(path, query) { + const res = await SocialApi.getWxaQrcode(path, query); + return 'data:image/png;base64,' + res.data; +} diff --git a/sheep/components/s-share-modal/canvas-poster/poster/user.js b/sheep/components/s-share-modal/canvas-poster/poster/user.js new file mode 100644 index 0000000..aa73922 --- /dev/null +++ b/sheep/components/s-share-modal/canvas-poster/poster/user.js @@ -0,0 +1,75 @@ +import sheep from '@/sheep'; +import { formatImageUrlProtocol, getWxaQrcode } from './index'; +import { measureTextWidth } from '@/utils/textUtils'; // 引入新封装的方法 +const user = async (poster) => { + const width = poster.width; + const userInfo = sheep.$store('user').userInfo; + const wxa_qrcode = await getWxaQrcode(poster.shareInfo.path, poster.shareInfo.query); + const widthNickName = measureTextWidth(userInfo.nickname, 14); // 使用新方法 + return [ + { + type: 'image', + src: formatImageUrlProtocol(sheep.$url.cdn(sheep.$store('app').platform.share.posterInfo.user_bg)), + css: { + width, + position: 'fixed', + 'object-fit': 'contain', + top: '0', + left: '0', + zIndex: -1, + }, + }, + { + type: 'text', + text: userInfo.nickname, + css: { + color: '#333', + fontSize: 14, + textAlign: 'center', + fontFamily: 'sans-serif', + position: 'fixed', + top: width * 0.4, + left: (width-widthNickName) / 2, + }, + }, + { + type: 'image', + src: formatImageUrlProtocol(sheep.$url.cdn(userInfo.avatar)), + css: { + position: 'fixed', + left: width * 0.4, + top: width * 0.16, + width: width * 0.2, + height: width * 0.2, + }, + }, + // #ifndef MP-WEIXIN + { + type: 'qrcode', + text: poster.shareInfo.link, + css: { + position: 'fixed', + left: width * 0.35, + top: width * 0.84, + width: width * 0.3, + height: width * 0.3, + }, + }, + // #endif + // #ifdef MP-WEIXIN + { + type: 'image', + src: wxa_qrcode, + css: { + position: 'fixed', + left: width * 0.35, + top: width * 0.84, + width: width * 0.3, + height: width * 0.3, + }, + }, + // #endif + ]; +}; + +export default user; diff --git a/sheep/components/s-share-modal/s-share-modal.vue b/sheep/components/s-share-modal/s-share-modal.vue new file mode 100644 index 0000000..0f0b92d --- /dev/null +++ b/sheep/components/s-share-modal/s-share-modal.vue @@ -0,0 +1,195 @@ + + + + + diff --git a/sheep/components/s-statusbar/s-statusbar.vue b/sheep/components/s-statusbar/s-statusbar.vue new file mode 100644 index 0000000..8b58c97 --- /dev/null +++ b/sheep/components/s-statusbar/s-statusbar.vue @@ -0,0 +1,10 @@ + + + diff --git a/sheep/components/s-tabbar/s-tabbar.vue b/sheep/components/s-tabbar/s-tabbar.vue new file mode 100644 index 0000000..4ff2340 --- /dev/null +++ b/sheep/components/s-tabbar/s-tabbar.vue @@ -0,0 +1,94 @@ + + + + + diff --git a/sheep/components/s-title-block/s-title-block.vue b/sheep/components/s-title-block/s-title-block.vue new file mode 100644 index 0000000..1f39f01 --- /dev/null +++ b/sheep/components/s-title-block/s-title-block.vue @@ -0,0 +1,111 @@ + + + + + + diff --git a/sheep/components/s-uploader/choose-and-upload-file.js b/sheep/components/s-uploader/choose-and-upload-file.js new file mode 100644 index 0000000..cea2626 --- /dev/null +++ b/sheep/components/s-uploader/choose-and-upload-file.js @@ -0,0 +1,304 @@ +'use strict'; +import FileApi from '@/sheep/api/infra/file'; + +const ERR_MSG_OK = 'chooseAndUploadFile:ok'; +const ERR_MSG_FAIL = 'chooseAndUploadFile:fail'; + +function chooseImage(opts) { + const { + count, + sizeType = ['original', 'compressed'], + sourceType = ['album', 'camera'], + extension, + } = opts; + return new Promise((resolve, reject) => { + uni.chooseImage({ + count, + sizeType, + sourceType, + extension, + success(res) { + resolve(normalizeChooseAndUploadFileRes(res, 'image')); + }, + fail(res) { + reject({ + errMsg: res.errMsg.replace('chooseImage:fail', ERR_MSG_FAIL), + }); + }, + }); + }); +} + +function chooseVideo(opts) { + const { camera, compressed, maxDuration, sourceType = ['album', 'camera'], extension } = opts; + return new Promise((resolve, reject) => { + 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), + }); + }, + }); + }); +} + +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; +} + +async function readFile(uniFile) { + // 微信小程序 + if (uni.getFileSystemManager) { + const fs = uni.getFileSystemManager(); + return fs.readFileSync(uniFile.path); + } + // H5 等 + return uniFile.arrayBuffer(); +} + +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 uploadFilesFromPath(path, directory) { + // 目的:用于微信小程序,选择图片时,只有 path + return uploadFiles( + Promise.resolve({ + tempFiles: [ + { + path, + type: 'image/jpeg', + name: path.includes('/') ? path.substring(path.lastIndexOf('/') + 1) : path, + }, + ], + }), + { + directory, + }, + ); +} + +async function uploadFiles(choosePromise, { onChooseFile, onUploadProgress, directory }) { + // 获取选择的文件 + const res = await choosePromise; + // 处理文件选择回调 + let files = res.tempFiles || []; + if (onChooseFile) { + const customChooseRes = onChooseFile(res); + if (typeof customChooseRes !== 'undefined') { + files = await Promise.resolve(customChooseRes); + if (typeof files === 'undefined') { + files = res.tempFiles || []; // Fallback + } + } + } + + // 如果是前端直连上传 + if (UPLOAD_TYPE.CLIENT === import.meta.env.SHOPRO_UPLOAD_TYPE) { + // 为上传创建一组 Promise + const uploadPromises = files.map(async (file) => { + try { + // 1.1 获取文件预签名地址 + const { data: presignedInfo } = await FileApi.getFilePresignedUrl(file.name, directory); + // 1.2 获取二进制文件对象 + const fileBuffer = await readFile(file); + + // 返回上传的 Promise + return new Promise((resolve, reject) => { + // 1.3. 上传文件到 S3 + uni.request({ + url: presignedInfo.uploadUrl, + method: 'PUT', + header: { + 'Content-Type': file.type, + }, + data: fileBuffer, + success: (res) => { + // 1.4. 记录文件信息到后端(异步) + createFile(presignedInfo, file); + // 1.5. 重新赋值 + file.url = presignedInfo.url; + resolve(file); + }, + fail: (err) => { + reject(err); + }, + }); + }); + } catch (error) { + console.error('上传失败:', error); + throw error; + } + }); + + // 等待所有上传完成 + return await Promise.all(uploadPromises); // 返回已上传的文件列表 + } else { + // 后端上传 + for (let file of files) { + const { data } = await FileApi.uploadFile(file.path); + file.url = data; + } + + return files; + } +} + +function chooseAndUploadFile( + opts = { + type: 'all', + directory: undefined, + }, +) { + if (opts.type === 'image') { + return uploadFiles(chooseImage(opts), opts); + } else if (opts.type === 'video') { + return uploadFiles(chooseVideo(opts), opts); + } + return uploadFiles(chooseAll(opts), opts); +} + +/** + * 创建文件信息 + * @param vo 文件预签名信息 + * @param file 文件 + */ +function createFile(vo, file) { + const fileVo = { + configId: vo.configId, + url: vo.url, + path: vo.path, + name: file.name, + type: file.fileType, + size: file.size, + }; + FileApi.createFile(fileVo); + return fileVo; +} + +/** + * 上传类型 + */ +const UPLOAD_TYPE = { + // 客户端直接上传(只支持S3服务) + CLIENT: 'client', + // 客户端发送到后端上传 + SERVER: 'server', +}; + +export { chooseAndUploadFile, uploadCloudFiles, uploadFilesFromPath }; diff --git a/sheep/components/s-uploader/s-uploader.vue b/sheep/components/s-uploader/s-uploader.vue new file mode 100644 index 0000000..fb72cf3 --- /dev/null +++ b/sheep/components/s-uploader/s-uploader.vue @@ -0,0 +1,677 @@ + + + + + + diff --git a/sheep/components/s-uploader/upload-file.vue b/sheep/components/s-uploader/upload-file.vue new file mode 100644 index 0000000..233d281 --- /dev/null +++ b/sheep/components/s-uploader/upload-file.vue @@ -0,0 +1,335 @@ + + + + + diff --git a/sheep/components/s-uploader/upload-image.vue b/sheep/components/s-uploader/upload-image.vue new file mode 100644 index 0000000..b66956a --- /dev/null +++ b/sheep/components/s-uploader/upload-image.vue @@ -0,0 +1,306 @@ + + + + + diff --git a/sheep/components/s-uploader/utils.js b/sheep/components/s-uploader/utils.js new file mode 100644 index 0000000..65a2116 --- /dev/null +++ b/sheep/components/s-uploader/utils.js @@ -0,0 +1,109 @@ +/** + * 获取文件名和后缀 + * @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; + } +}; + +/** + * 获取文件和检测是否可选 + */ +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, + url: files.url || 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/sheep/components/s-user-card/s-user-card.vue b/sheep/components/s-user-card/s-user-card.vue new file mode 100644 index 0000000..6637add --- /dev/null +++ b/sheep/components/s-user-card/s-user-card.vue @@ -0,0 +1,184 @@ + + + + + + diff --git a/sheep/components/s-video-block/s-video-block.vue b/sheep/components/s-video-block/s-video-block.vue new file mode 100644 index 0000000..38e5b84 --- /dev/null +++ b/sheep/components/s-video-block/s-video-block.vue @@ -0,0 +1,32 @@ + + + + + + diff --git a/sheep/components/s-wallet-card/s-wallet-card.vue b/sheep/components/s-wallet-card/s-wallet-card.vue new file mode 100644 index 0000000..aff0bf8 --- /dev/null +++ b/sheep/components/s-wallet-card/s-wallet-card.vue @@ -0,0 +1,119 @@ + + + + + + + diff --git a/sheep/config/index.js b/sheep/config/index.js new file mode 100644 index 0000000..d0dfb57 --- /dev/null +++ b/sheep/config/index.js @@ -0,0 +1,31 @@ +import packageInfo from '@/package.json'; + +const { version } = packageInfo; + +// 开发环境配置 +export let baseUrl; +if (process.env.NODE_ENV === 'development') { + baseUrl = import.meta.env.SHOPRO_DEV_BASE_URL; +} else { + baseUrl = import.meta.env.SHOPRO_BASE_URL; +} +if (typeof baseUrl === 'undefined') { + console.error('请检查.env配置文件是否存在'); +} else { + console.log(`[哈客商城 ${version}] https://doc.iocoder.cn`); +} + +export const apiPath = import.meta.env.SHOPRO_API_PATH; +export const staticUrl = import.meta.env.SHOPRO_STATIC_URL; +export const tenantId = import.meta.env.SHOPRO_TENANT_ID; +export const websocketPath = import.meta.env.SHOPRO_WEBSOCKET_PATH; +export const h5Url = import.meta.env.SHOPRO_H5_URL; + +export default { + baseUrl, + apiPath, + staticUrl, + tenantId, + websocketPath, + h5Url, +}; diff --git a/sheep/config/zIndex.js b/sheep/config/zIndex.js new file mode 100644 index 0000000..6652d48 --- /dev/null +++ b/sheep/config/zIndex.js @@ -0,0 +1,20 @@ +// uniapp在H5中各API的z-index值如下: +/** + * actionsheet: 999 + * modal: 999 + * navigate: 998 + * tabbar: 998 + * toast: 999 + */ + +export default { + toast: 10090, + noNetwork: 10080, + popup: 10075, // popup包含popup,actionsheet,keyboard,picker的值 + mask: 10070, + navbar: 980, + topTips: 975, + sticky: 970, + indexListSticky: 965, + popover: 960, +}; diff --git a/sheep/helper/const.js b/sheep/helper/const.js new file mode 100644 index 0000000..42e623c --- /dev/null +++ b/sheep/helper/const.js @@ -0,0 +1,153 @@ +// ========== COMMON - 公共模块 ========== + +/** + * 与后端Terminal枚举一一对应 + */ +export const TerminalEnum = { + UNKNOWN: 0, // 未知, 目的:在无法解析到 terminal 时,使用它 + WECHAT_MINI_PROGRAM: 10, //微信小程序 + WECHAT_WAP: 11, // 微信公众号 + H5: 20, // H5 网页 + APP: 31, // 手机 App +}; + +/** + * 将 uni-app 提供的平台转换为后端所需的 terminal值 + * + * @return 终端 + */ +export const getTerminal = () => { + const platformType = uni.getAppBaseInfo().uniPlatform; + // 与后端terminal枚举一一对应 + switch (platformType) { + case 'app': + return TerminalEnum.APP; + case 'web': + return TerminalEnum.H5; + case 'mp-weixin': + return TerminalEnum.WECHAT_MINI_PROGRAM; + default: + return TerminalEnum.UNKNOWN; + } +}; + +// ========== MALL - 营销模块 ========== + +import dayjs from 'dayjs'; + +/** + * 优惠类型枚举 + */ +export const PromotionDiscountTypeEnum = { + PRICE: { + type: 1, + name: '满减', + }, + PERCENT: { + type: 2, + name: '折扣', + }, +}; + +/** + * 优惠劵模板的有限期类型的枚举 + */ +export const CouponTemplateValidityTypeEnum = { + DATE: { + type: 1, + name: '固定日期可用', + }, + TERM: { + type: 2, + name: '领取之后可用', + }, +}; + +// 时间段的状态枚举 +export const TimeStatusEnum = { + WAIT_START: '即将开始', + STARTED: '进行中', + END: '已结束', +}; + +/** + * 微信小程序的订阅模版 + */ +export const WxaSubscribeTemplate = { + TRADE_ORDER_DELIVERY: '订单发货通知', + PROMOTION_COMBINATION_SUCCESS: '拼团结果通知', + PAY_WALLET_RECHARGER_SUCCESS: '充值成功通知', +}; +export const PromotionActivityTypeEnum = { + NORMAL: { + type: 0, + name: '普通', + }, + SECKILL: { + type: 1, + name: '秒杀', + }, + BARGAIN: { + type: 2, + name: '砍价', + }, + COMBINATION: { + type: 3, + name: '拼团', + }, + POINT: { + type: 4, + name: '积分商城', + }, +}; +/** 配送方式枚举 */ +export const DeliveryTypeEnum = { + EXPRESS: { type: 1, name: '快递发货' }, + PICK_UP: { type: 2, name: '用户自提' }, +}; +export const getTimeStatusEnum = (startTime, endTime) => { + const now = dayjs(); + if (now.isBefore(startTime)) { + return TimeStatusEnum.WAIT_START; + } else if (now.isAfter(endTime)) { + return TimeStatusEnum.END; + } else { + return TimeStatusEnum.STARTED; + } +}; +/** + * 分享页枚举 + * 按需扩展 + * */ +export const SharePageEnum = { + HOME: { + name: '首页', + page: '/pages/index/index', + value: '1', + }, + GOODS: { + name: '普通商品页', + page: '/pages/goods/index', + value: '2', + }, + GROUPON: { + name: '拼团商品页', + page: '/pages/goods/groupon', + value: '3', + }, + SECKILL: { + name: '秒杀商品页', + page: '/pages/goods/seckill', + value: '4', + }, + GROUPON_DETAIL: { + name: '参与拼团页', + page: '/pages/activity/groupon/detail', + value: '5', + }, + POINT: { + name: '积分商品页', + page: '/pages/goods/point', + value: '6', + }, +}; diff --git a/sheep/helper/digit.js b/sheep/helper/digit.js new file mode 100644 index 0000000..be50b32 --- /dev/null +++ b/sheep/helper/digit.js @@ -0,0 +1,168 @@ +let _boundaryCheckingState = true; // 是否进行越界检查的全局开关 + +/** + * 把错误的数据转正 + * @private + * @example strip(0.09999999999999998)=0.1 + */ +function strip(num, precision = 15) { + return +parseFloat(Number(num).toPrecision(precision)); +} + +/** + * Return digits length of a number + * @private + * @param {*number} num Input number + */ +function digitLength(num) { + // Get digit length of e + const eSplit = num.toString().split(/[eE]/); + const len = (eSplit[0].split('.')[1] || '').length - +(eSplit[1] || 0); + return len > 0 ? len : 0; +} + +/** + * 把小数转成整数,如果是小数则放大成整数 + * @private + * @param {*number} num 输入数 + */ +function float2Fixed(num) { + if (num.toString().indexOf('e') === -1) { + return Number(num.toString().replace('.', '')); + } + const dLen = digitLength(num); + return dLen > 0 ? strip(Number(num) * Math.pow(10, dLen)) : Number(num); +} + +/** + * 检测数字是否越界,如果越界给出提示 + * @private + * @param {*number} num 输入数 + */ +function checkBoundary(num) { + if (_boundaryCheckingState) { + if (num > Number.MAX_SAFE_INTEGER || num < Number.MIN_SAFE_INTEGER) { + console.warn(`${num} 超出了精度限制,结果可能不正确`); + } + } +} + +/** + * 把递归操作扁平迭代化 + * @param {number[]} arr 要操作的数字数组 + * @param {function} operation 迭代操作 + * @private + */ +function iteratorOperation(arr, operation) { + const [num1, num2, ...others] = arr; + let res = operation(num1, num2); + + others.forEach((num) => { + res = operation(res, num); + }); + + return res; +} + +/** + * 高精度乘法 + * @export + */ +export function times(...nums) { + if (nums.length > 2) { + return iteratorOperation(nums, times); + } + + const [num1, num2] = nums; + const num1Changed = float2Fixed(num1); + const num2Changed = float2Fixed(num2); + const baseNum = digitLength(num1) + digitLength(num2); + const leftValue = num1Changed * num2Changed; + + checkBoundary(leftValue); + + return leftValue / Math.pow(10, baseNum); +} + +/** + * 高精度加法 + * @export + */ +export function plus(...nums) { + if (nums.length > 2) { + return iteratorOperation(nums, plus); + } + + const [num1, num2] = nums; + // 取最大的小数位 + const baseNum = Math.pow(10, Math.max(digitLength(num1), digitLength(num2))); + // 把小数都转为整数然后再计算 + return (times(num1, baseNum) + times(num2, baseNum)) / baseNum; +} + +/** + * 高精度减法 + * @export + */ +export function minus(...nums) { + if (nums.length > 2) { + return iteratorOperation(nums, minus); + } + + const [num1, num2] = nums; + const baseNum = Math.pow(10, Math.max(digitLength(num1), digitLength(num2))); + return (times(num1, baseNum) - times(num2, baseNum)) / baseNum; +} + +/** + * 高精度除法 + * @export + */ +export function divide(...nums) { + if (nums.length > 2) { + return iteratorOperation(nums, divide); + } + + const [num1, num2] = nums; + const num1Changed = float2Fixed(num1); + const num2Changed = float2Fixed(num2); + checkBoundary(num1Changed); + checkBoundary(num2Changed); + // 重要,这里必须用strip进行修正 + return times( + num1Changed / num2Changed, + strip(Math.pow(10, digitLength(num2) - digitLength(num1))), + ); +} + +/** + * 四舍五入 + * @export + */ +export function round(num, ratio) { + const base = Math.pow(10, ratio); + let result = divide(Math.round(Math.abs(times(num, base))), base); + if (num < 0 && result !== 0) { + result = times(result, -1); + } + // 位数不足则补0 + return result; +} + +/** + * 是否进行边界检查,默认开启 + * @param flag 标记开关,true 为开启,false 为关闭,默认为 true + * @export + */ +export function enableBoundaryChecking(flag = true) { + _boundaryCheckingState = flag; +} + +export default { + times, + plus, + minus, + divide, + round, + enableBoundaryChecking, +}; diff --git a/sheep/helper/index.js b/sheep/helper/index.js new file mode 100644 index 0000000..328949a --- /dev/null +++ b/sheep/helper/index.js @@ -0,0 +1,700 @@ +import test from './test.js'; +import { round } from './digit.js'; +/** + * @description 如果value小于min,取min;如果value大于max,取max + * @param {number} min + * @param {number} max + * @param {number} value + */ +function range(min = 0, max = 0, value = 0) { + return Math.max(min, Math.min(max, Number(value))); +} + +/** + * @description 用于获取用户传递值的px值 如果用户传递了"xxpx"或者"xxrpx",取出其数值部分,如果是"xxxrpx"还需要用过uni.upx2px进行转换 + * @param {number|string} value 用户传递值的px值 + * @param {boolean} unit + * @returns {number|string} + */ +export function getPx(value, unit = false) { + if (test.number(value)) { + return unit ? `${value}px` : Number(value); + } + // 如果带有rpx,先取出其数值部分,再转为px值 + if (/(rpx|upx)$/.test(value)) { + return unit ? `${uni.upx2px(parseInt(value))}px` : Number(uni.upx2px(parseInt(value))); + } + return unit ? `${parseInt(value)}px` : parseInt(value); +} + +/** + * @description 进行延时,以达到可以简写代码的目的 + * @param {number} value 堵塞时间 单位ms 毫秒 + * @returns {Promise} 返回promise + */ +export function sleep(value = 30) { + return new Promise((resolve) => { + setTimeout(() => { + resolve(); + }, value); + }); +} +/** + * @description 运行期判断平台 + * @returns {string} 返回所在平台(小写) + * @link 运行期判断平台 https://uniapp.dcloud.io/frame?id=判断平台 + */ +export function os() { + return uni.getDeviceInfo().platform.toLowerCase(); +} + +/** + * @description 取一个区间数 + * @param {Number} min 最小值 + * @param {Number} max 最大值 + */ +function random(min, max) { + if (min >= 0 && max > 0 && max >= min) { + const gab = max - min + 1; + return Math.floor(Math.random() * gab + min); + } + return 0; +} + +/** + * @param {Number} len uuid的长度 + * @param {Boolean} firstU 将返回的首字母置为"u" + * @param {Nubmer} radix 生成uuid的基数(意味着返回的字符串都是这个基数),2-二进制,8-八进制,10-十进制,16-十六进制 + */ +export function guid(len = 32, firstU = true, radix = null) { + const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split(''); + const uuid = []; + radix = radix || chars.length; + + if (len) { + // 如果指定uuid长度,只是取随机的字符,0|x为位运算,能去掉x的小数位,返回整数位 + for (let i = 0; i < len; i++) uuid[i] = chars[0 | (Math.random() * radix)]; + } else { + let r; + // rfc4122标准要求返回的uuid中,某些位为固定的字符 + uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-'; + uuid[14] = '4'; + + for (let i = 0; i < 36; i++) { + if (!uuid[i]) { + r = 0 | (Math.random() * 16); + uuid[i] = chars[i == 19 ? (r & 0x3) | 0x8 : r]; + } + } + } + // 移除第一个字符,并用u替代,因为第一个字符为数值时,该guuid不能用作id或者class + if (firstU) { + uuid.shift(); + return `u${uuid.join('')}`; + } + return uuid.join(''); +} + +/** +* @description 获取父组件的参数,因为支付宝小程序不支持provide/inject的写法 + this.$parent在非H5中,可以准确获取到父组件,但是在H5中,需要多次this.$parent.$parent.xxx + 这里默认值等于undefined有它的含义,因为最顶层元素(组件)的$parent就是undefined,意味着不传name + 值(默认为undefined),就是查找最顶层的$parent +* @param {string|undefined} name 父组件的参数名 +*/ +export function $parent(name = undefined) { + let parent = this.$parent; + // 通过while历遍,这里主要是为了H5需要多层解析的问题 + while (parent) { + // 父组件 + if (parent.$options && parent.$options.name !== name) { + // 如果组件的name不相等,继续上一级寻找 + parent = parent.$parent; + } else { + return parent; + } + } + return false; +} + +/** + * @description 样式转换 + * 对象转字符串,或者字符串转对象 + * @param {object | string} customStyle 需要转换的目标 + * @param {String} target 转换的目的,object-转为对象,string-转为字符串 + * @returns {object|string} + */ +export function addStyle(customStyle, target = 'object') { + // 字符串转字符串,对象转对象情形,直接返回 + if ( + test.empty(customStyle) || + (typeof customStyle === 'object' && target === 'object') || + (target === 'string' && typeof customStyle === 'string') + ) { + return customStyle; + } + // 字符串转对象 + if (target === 'object') { + // 去除字符串样式中的两端空格(中间的空格不能去掉,比如padding: 20px 0如果去掉了就错了),空格是无用的 + customStyle = trim(customStyle); + // 根据";"将字符串转为数组形式 + const styleArray = customStyle.split(';'); + const style = {}; + // 历遍数组,拼接成对象 + for (let i = 0; i < styleArray.length; i++) { + // 'font-size:20px;color:red;',如此最后字符串有";"的话,会导致styleArray最后一个元素为空字符串,这里需要过滤 + if (styleArray[i]) { + const item = styleArray[i].split(':'); + style[trim(item[0])] = trim(item[1]); + } + } + return style; + } + // 这里为对象转字符串形式 + let string = ''; + for (const i in customStyle) { + // 驼峰转为中划线的形式,否则css内联样式,无法识别驼峰样式属性名 + const key = i.replace(/([A-Z])/g, '-$1').toLowerCase(); + string += `${key}:${customStyle[i]};`; + } + // 去除两端空格 + return trim(string); +} + +/** + * @description 添加单位,如果有rpx,upx,%,px等单位结尾或者值为auto,直接返回,否则加上px单位结尾 + * @param {string|number} value 需要添加单位的值 + * @param {string} unit 添加的单位名 比如px + */ +export function addUnit(value = 'auto', unit = 'px') { + value = String(value); + return test.number(value) ? `${value}${unit}` : value; +} + +/** + * @description 深度克隆 + * @param {object} obj 需要深度克隆的对象 + * @returns {*} 克隆后的对象或者原值(不是对象) + */ +function deepClone(obj) { + // 对常见的“非”值,直接返回原来值 + if ([null, undefined, NaN, false].includes(obj)) return obj; + if (typeof obj !== 'object' && typeof obj !== 'function') { + // 原始类型直接返回 + return obj; + } + const o = test.array(obj) ? [] : {}; + for (const i in obj) { + if (obj.hasOwnProperty(i)) { + o[i] = typeof obj[i] === 'object' ? deepClone(obj[i]) : obj[i]; + } + } + return o; +} + +/** + * @description JS对象深度合并 + * @param {object} target 需要拷贝的对象 + * @param {object} source 拷贝的来源对象 + * @returns {object|boolean} 深度合并后的对象或者false(入参有不是对象) + */ +export function deepMerge(target = {}, source = {}) { + target = deepClone(target); + if (typeof target !== 'object' || typeof source !== 'object') return false; + for (const prop in source) { + if (!source.hasOwnProperty(prop)) continue; + if (prop in target) { + if (typeof target[prop] !== 'object') { + target[prop] = source[prop]; + } else if (typeof source[prop] !== 'object') { + target[prop] = source[prop]; + } else if (target[prop].concat && source[prop].concat) { + target[prop] = target[prop].concat(source[prop]); + } else { + target[prop] = deepMerge(target[prop], source[prop]); + } + } else { + target[prop] = source[prop]; + } + } + return target; +} + +/** + * @description error提示 + * @param {*} err 错误内容 + */ +function error(err) { + // 开发环境才提示,生产环境不会提示 + if (process.env.NODE_ENV === 'development') { + console.error(`SheepJS:${err}`); + } +} + +/** + * @description 打乱数组 + * @param {array} array 需要打乱的数组 + * @returns {array} 打乱后的数组 + */ +function randomArray(array = []) { + // 原理是sort排序,Math.random()产生0<= x < 1之间的数,会导致x-0.05大于或者小于0 + return array.sort(() => Math.random() - 0.5); +} + +// padStart 的 polyfill,因为某些机型或情况,还无法支持es7的padStart,比如电脑版的微信小程序 +// 所以这里做一个兼容polyfill的兼容处理 +if (!String.prototype.padStart) { + // 为了方便表示这里 fillString 用了ES6 的默认参数,不影响理解 + String.prototype.padStart = function (maxLength, fillString = ' ') { + if (Object.prototype.toString.call(fillString) !== '[object String]') { + throw new TypeError('fillString must be String'); + } + const str = this; + // 返回 String(str) 这里是为了使返回的值是字符串字面量,在控制台中更符合直觉 + if (str.length >= maxLength) return String(str); + + const fillLength = maxLength - str.length; + let times = Math.ceil(fillLength / fillString.length); + while ((times >>= 1)) { + fillString += fillString; + if (times === 1) { + fillString += fillString; + } + } + return fillString.slice(0, fillLength) + str; + }; +} + +/** + * @description 格式化时间 + * @param {String|Number} dateTime 需要格式化的时间戳 + * @param {String} fmt 格式化规则 yyyy:mm:dd|yyyy:mm|yyyy年mm月dd日|yyyy年mm月dd日 hh时MM分等,可自定义组合 默认yyyy-mm-dd + * @returns {string} 返回格式化后的字符串 + */ +function timeFormat(dateTime = null, formatStr = 'yyyy-mm-dd') { + let date; + // 若传入时间为假值,则取当前时间 + if (!dateTime) { + date = new Date(); + } + // 若为unix秒时间戳,则转为毫秒时间戳(逻辑有点奇怪,但不敢改,以保证历史兼容) + else if (/^\d{10}$/.test(dateTime?.toString().trim())) { + date = new Date(dateTime * 1000); + } + // 若用户传入字符串格式时间戳,new Date无法解析,需做兼容 + else if (typeof dateTime === 'string' && /^\d+$/.test(dateTime.trim())) { + date = new Date(Number(dateTime)); + } + // 其他都认为符合 RFC 2822 规范 + else { + // 处理平台性差异,在Safari/Webkit中,new Date仅支持/作为分割符的字符串时间 + date = new Date(typeof dateTime === 'string' ? dateTime.replace(/-/g, '/') : dateTime); + } + + const timeSource = { + y: date.getFullYear().toString(), // 年 + m: (date.getMonth() + 1).toString().padStart(2, '0'), // 月 + d: date.getDate().toString().padStart(2, '0'), // 日 + h: date.getHours().toString().padStart(2, '0'), // 时 + M: date.getMinutes().toString().padStart(2, '0'), // 分 + s: date.getSeconds().toString().padStart(2, '0'), // 秒 + // 有其他格式化字符需求可以继续添加,必须转化成字符串 + }; + + for (const key in timeSource) { + const [ret] = new RegExp(`${key}+`).exec(formatStr) || []; + if (ret) { + // 年可能只需展示两位 + const beginIndex = key === 'y' && ret.length === 2 ? 2 : 0; + formatStr = formatStr.replace(ret, timeSource[key].slice(beginIndex)); + } + } + + return formatStr; +} + +/** + * @description 时间戳转为多久之前 + * @param {String|Number} timestamp 时间戳 + * @param {String|Boolean} format + * 格式化规则如果为时间格式字符串,超出一定时间范围,返回固定的时间格式; + * 如果为布尔值false,无论什么时间,都返回多久以前的格式 + * @returns {string} 转化后的内容 + */ +function timeFrom(timestamp = null, format = 'yyyy-mm-dd') { + if (timestamp == null) timestamp = Number(new Date()); + timestamp = parseInt(timestamp); + // 判断用户输入的时间戳是秒还是毫秒,一般前端js获取的时间戳是毫秒(13位),后端传过来的为秒(10位) + if (timestamp.toString().length == 10) timestamp *= 1000; + let timer = new Date().getTime() - timestamp; + timer = parseInt(timer / 1000); + // 如果小于5分钟,则返回"刚刚",其他以此类推 + let tips = ''; + switch (true) { + case timer < 300: + tips = '刚刚'; + break; + case timer >= 300 && timer < 3600: + tips = `${parseInt(timer / 60)}分钟前`; + break; + case timer >= 3600 && timer < 86400: + tips = `${parseInt(timer / 3600)}小时前`; + break; + case timer >= 86400 && timer < 2592000: + tips = `${parseInt(timer / 86400)}天前`; + break; + default: + // 如果format为false,则无论什么时间戳,都显示xx之前 + if (format === false) { + if (timer >= 2592000 && timer < 365 * 86400) { + tips = `${parseInt(timer / (86400 * 30))}个月前`; + } else { + tips = `${parseInt(timer / (86400 * 365))}年前`; + } + } else { + tips = timeFormat(timestamp, format); + } + } + return tips; +} + +/** + * @description 去除空格 + * @param String str 需要去除空格的字符串 + * @param String pos both(左右)|left|right|all 默认both + */ +function trim(str, pos = 'both') { + str = String(str); + if (pos == 'both') { + return str.replace(/^\s+|\s+$/g, ''); + } + if (pos == 'left') { + return str.replace(/^\s*/, ''); + } + if (pos == 'right') { + return str.replace(/(\s*$)/g, ''); + } + if (pos == 'all') { + return str.replace(/\s+/g, ''); + } + return str; +} + +/** + * @description 对象转url参数 + * @param {object} data,对象 + * @param {Boolean} isPrefix,是否自动加上"?" + * @param {string} arrayFormat 规则 indices|brackets|repeat|comma + */ +function queryParams(data = {}, isPrefix = true, arrayFormat = 'brackets') { + const prefix = isPrefix ? '?' : ''; + const _result = []; + if (['indices', 'brackets', 'repeat', 'comma'].indexOf(arrayFormat) == -1) + arrayFormat = 'brackets'; + for (const key in data) { + const value = data[key]; + // 去掉为空的参数 + if (['', undefined, null].indexOf(value) >= 0) { + continue; + } + // 如果值为数组,另行处理 + if (value.constructor === Array) { + // e.g. {ids: [1, 2, 3]} + switch (arrayFormat) { + case 'indices': + // 结果: ids[0]=1&ids[1]=2&ids[2]=3 + for (let i = 0; i < value.length; i++) { + _result.push(`${key}[${i}]=${value[i]}`); + } + break; + case 'brackets': + // 结果: ids[]=1&ids[]=2&ids[]=3 + value.forEach((_value) => { + _result.push(`${key}[]=${_value}`); + }); + break; + case 'repeat': + // 结果: ids=1&ids=2&ids=3 + value.forEach((_value) => { + _result.push(`${key}=${_value}`); + }); + break; + case 'comma': + // 结果: ids=1,2,3 + let commaStr = ''; + value.forEach((_value) => { + commaStr += (commaStr ? ',' : '') + _value; + }); + _result.push(`${key}=${commaStr}`); + break; + default: + value.forEach((_value) => { + _result.push(`${key}[]=${_value}`); + }); + } + } else { + _result.push(`${key}=${value}`); + } + } + return _result.length ? prefix + _result.join('&') : ''; +} + +/** + * 显示消息提示框 + * @param {String} title 提示的内容,长度与 icon 取值有关。 + * @param {Number} duration 提示的延迟时间,单位毫秒,默认:2000 + */ +function toast(title, duration = 2000) { + uni.showToast({ + title: String(title), + icon: 'none', + duration, + }); +} + +/** + * @description 根据主题type值,获取对应的图标 + * @param {String} type 主题名称,primary|info|error|warning|success + * @param {boolean} fill 是否使用fill填充实体的图标 + */ +function type2icon(type = 'success', fill = false) { + // 如果非预置值,默认为success + if (['primary', 'info', 'error', 'warning', 'success'].indexOf(type) == -1) type = 'success'; + let iconName = ''; + // 目前(2019-12-12),info和primary使用同一个图标 + switch (type) { + case 'primary': + iconName = 'info-circle'; + break; + case 'info': + iconName = 'info-circle'; + break; + case 'error': + iconName = 'close-circle'; + break; + case 'warning': + iconName = 'error-circle'; + break; + case 'success': + iconName = 'checkmark-circle'; + break; + default: + iconName = 'checkmark-circle'; + } + // 是否是实体类型,加上-fill,在icon组件库中,实体的类名是后面加-fill的 + if (fill) iconName += '-fill'; + return iconName; +} + +/** + * @description 数字格式化 + * @param {number|string} number 要格式化的数字 + * @param {number} decimals 保留几位小数 + * @param {string} decimalPoint 小数点符号 + * @param {string} thousandsSeparator 千分位符号 + * @returns {string} 格式化后的数字 + */ +function priceFormat(number, decimals = 0, decimalPoint = '.', thousandsSeparator = ',') { + number = `${number}`.replace(/[^0-9+-Ee.]/g, ''); + const n = !isFinite(+number) ? 0 : +number; + const prec = !isFinite(+decimals) ? 0 : Math.abs(decimals); + const sep = typeof thousandsSeparator === 'undefined' ? ',' : thousandsSeparator; + const dec = typeof decimalPoint === 'undefined' ? '.' : decimalPoint; + let s = ''; + + s = (prec ? round(n, prec) + '' : `${Math.round(n)}`).split('.'); + const re = /(-?\d+)(\d{3})/; + while (re.test(s[0])) { + s[0] = s[0].replace(re, `$1${sep}$2`); + } + + if ((s[1] || '').length < prec) { + s[1] = s[1] || ''; + s[1] += new Array(prec - s[1].length + 1).join('0'); + } + return s.join(dec); +} + +/** + * @description 获取duration值 + * 如果带有ms或者s直接返回,如果大于一定值,认为是ms单位,小于一定值,认为是s单位 + * 比如以30位阈值,那么300大于30,可以理解为用户想要的是300ms,而不是想花300s去执行一个动画 + * @param {String|number} value 比如: "1s"|"100ms"|1|100 + * @param {boolean} unit 提示: 如果是false 默认返回number + * @return {string|number} + */ +function getDuration(value, unit = true) { + const valueNum = parseInt(value); + if (unit) { + if (/s$/.test(value)) return value; + return value > 30 ? `${value}ms` : `${value}s`; + } + if (/ms$/.test(value)) return valueNum; + if (/s$/.test(value)) return valueNum > 30 ? valueNum : valueNum * 1000; + return valueNum; +} + +/** + * @description 日期的月或日补零操作 + * @param {String} value 需要补零的值 + */ +function padZero(value) { + return `00${value}`.slice(-2); +} + +/** + * @description 获取某个对象下的属性,用于通过类似'a.b.c'的形式去获取一个对象的的属性的形式 + * @param {object} obj 对象 + * @param {string} key 需要获取的属性字段 + * @returns {*} + */ +function getProperty(obj, key) { + if (!obj) { + return; + } + if (typeof key !== 'string' || key === '') { + return ''; + } + if (key.indexOf('.') !== -1) { + const keys = key.split('.'); + let firstObj = obj[keys[0]] || {}; + + for (let i = 1; i < keys.length; i++) { + if (firstObj) { + firstObj = firstObj[keys[i]]; + } + } + return firstObj; + } + return obj[key]; +} + +/** + * @description 设置对象的属性值,如果'a.b.c'的形式进行设置 + * @param {object} obj 对象 + * @param {string} key 需要设置的属性 + * @param {string} value 设置的值 + */ +function setProperty(obj, key, value) { + if (!obj) { + return; + } + // 递归赋值 + const inFn = function (_obj, keys, v) { + // 最后一个属性key + if (keys.length === 1) { + _obj[keys[0]] = v; + return; + } + // 0~length-1个key + while (keys.length > 1) { + const k = keys[0]; + if (!_obj[k] || typeof _obj[k] !== 'object') { + _obj[k] = {}; + } + const key = keys.shift(); + // 自调用判断是否存在属性,不存在则自动创建对象 + inFn(_obj[k], keys, v); + } + }; + + if (typeof key !== 'string' || key === '') { + } else if (key.indexOf('.') !== -1) { + // 支持多层级赋值操作 + const keys = key.split('.'); + inFn(obj, keys, value); + } else { + obj[key] = value; + } +} + +/** + * @description 获取当前页面路径 + */ +function page() { + const pages = getCurrentPages(); + // 某些特殊情况下(比如页面进行redirectTo时的一些时机),pages可能为空数组 + return `/${pages[pages.length - 1]?.route || ''}`; +} + +/** + * @description 获取当前路由栈实例数组 + */ +function pages() { + const pages = getCurrentPages(); + return pages; +} + +/** + * 获取H5-真实根地址 兼容hash+history模式 + */ +export function getRootUrl() { + let url = ''; + // #ifdef H5 + url = location.origin + location.pathname; + + if (location.hash !== '') { + url += '#/'; + } + // #endif + return url; +} + +/** + * copyText 多端复制文本 + */ +export function copyText(text) { + // #ifndef H5 + uni.setClipboardData({ + data: text, + success: function () { + toast('复制成功!'); + }, + fail: function () { + toast('复制失败!'); + }, + }); + // #endif + // #ifdef H5 + var createInput = document.createElement('textarea'); + createInput.value = text; + document.body.appendChild(createInput); + createInput.select(); + document.execCommand('Copy'); + createInput.className = 'createInput'; + createInput.style.display = 'none'; + toast('复制成功'); + // #endif +} + +export default { + range, + getPx, + sleep, + os, + random, + guid, + $parent, + addStyle, + addUnit, + deepClone, + deepMerge, + error, + randomArray, + timeFormat, + timeFrom, + trim, + queryParams, + toast, + type2icon, + priceFormat, + getDuration, + padZero, + getProperty, + setProperty, + page, + pages, + test, + getRootUrl, + copyText, +}; diff --git a/sheep/helper/test.js b/sheep/helper/test.js new file mode 100644 index 0000000..ca550a1 --- /dev/null +++ b/sheep/helper/test.js @@ -0,0 +1,285 @@ +/** + * 验证电子邮箱格式 + */ +function email(value) { + return /^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/.test(value); +} + +/** + * 验证手机格式 + */ +function mobile(value) { + return /^1[23456789]\d{9}$/.test(value); +} + +/** + * 验证URL格式 + */ +function url(value) { + return /^((https|http|ftp|rtsp|mms):\/\/)(([0-9a-zA-Z_!~*'().&=+$%-]+: )?[0-9a-zA-Z_!~*'().&=+$%-]+@)?(([0-9]{1,3}.){3}[0-9]{1,3}|([0-9a-zA-Z_!~*'()-]+.)*([0-9a-zA-Z][0-9a-zA-Z-]{0,61})?[0-9a-zA-Z].[a-zA-Z]{2,6})(:[0-9]{1,4})?((\/?)|(\/[0-9a-zA-Z_!~*'().;?:@&=+$,%#-]+)+\/?)$/.test( + value, + ); +} + +/** + * 验证日期格式 + */ +function date(value) { + if (!value) return false; + // 判断是否数值或者字符串数值(意味着为时间戳),转为数值,否则new Date无法识别字符串时间戳 + if (number(value)) value = +value; + return !/Invalid|NaN/.test(new Date(value).toString()); +} + +/** + * 验证ISO类型的日期格式 + */ +function dateISO(value) { + return /^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$/.test(value); +} + +/** + * 验证十进制数字 + */ +function number(value) { + return /^[\+-]?(\d+\.?\d*|\.\d+|\d\.\d+e\+\d+)$/.test(value); +} + +/** + * 验证字符串 + */ +function string(value) { + return typeof value === 'string'; +} + +/** + * 验证整数 + */ +function digits(value) { + return /^\d+$/.test(value); +} + +/** + * 验证身份证号码 + */ +function idCard(value) { + return /^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/.test(value); +} + +/** + * 是否车牌号 + */ +function carNo(value) { + // 新能源车牌 + const xreg = + /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}(([0-9]{5}[DF]$)|([DF][A-HJ-NP-Z0-9][0-9]{4}$))/; + // 旧车牌 + const creg = + /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}[A-HJ-NP-Z0-9]{4}[A-HJ-NP-Z0-9挂学警港澳]{1}$/; + if (value.length === 7) { + return creg.test(value); + } + if (value.length === 8) { + return xreg.test(value); + } + return false; +} + +/** + * 金额,只允许2位小数 + */ +function amount(value) { + // 金额,只允许保留两位小数 + return /^[1-9]\d*(,\d{3})*(\.\d{1,2})?$|^0\.\d{1,2}$/.test(value); +} + +/** + * 中文 + */ +function chinese(value) { + const reg = /^[\u4e00-\u9fa5]+$/gi; + return reg.test(value); +} + +/** + * 只能输入字母 + */ +function letter(value) { + return /^[a-zA-Z]*$/.test(value); +} + +/** + * 只能是字母或者数字 + */ +function enOrNum(value) { + // 英文或者数字 + const reg = /^[0-9a-zA-Z]*$/g; + return reg.test(value); +} + +/** + * 验证是否包含某个值 + */ +function contains(value, param) { + return value.indexOf(param) >= 0; +} + +/** + * 验证一个值范围[min, max] + */ +function range(value, param) { + return value >= param[0] && value <= param[1]; +} + +/** + * 验证一个长度范围[min, max] + */ +function rangeLength(value, param) { + return value.length >= param[0] && value.length <= param[1]; +} + +/** + * 是否固定电话 + */ +function landline(value) { + const reg = /^\d{3,4}-\d{7,8}(-\d{3,4})?$/; + return reg.test(value); +} + +/** + * 判断是否为空 + */ +function empty(value) { + switch (typeof value) { + case 'undefined': + return true; + case 'string': + if (value.replace(/(^[ \t\n\r]*)|([ \t\n\r]*$)/g, '').length == 0) return true; + break; + case 'boolean': + if (!value) return true; + break; + case 'number': + if (value === 0 || isNaN(value)) return true; + break; + case 'object': + if (value === null || value.length === 0) return true; + for (const i in value) { + return false; + } + return true; + } + return false; +} + +/** + * 是否json字符串 + */ +function jsonString(value) { + if (typeof value === 'string') { + try { + const obj = JSON.parse(value); + if (typeof obj === 'object' && obj) { + return true; + } + return false; + } catch (e) { + return false; + } + } + return false; +} + +/** + * 是否数组 + */ +function array(value) { + if (typeof Array.isArray === 'function') { + return Array.isArray(value); + } + return Object.prototype.toString.call(value) === '[object Array]'; +} + +/** + * 是否对象 + */ +function object(value) { + return Object.prototype.toString.call(value) === '[object Object]'; +} + +/** + * 是否短信验证码 + */ +function code(value, len = 6) { + return new RegExp(`^\\d{${len}}$`).test(value); +} + +/** + * 是否函数方法 + * @param {Object} value + */ +function func(value) { + return typeof value === 'function'; +} + +/** + * 是否promise对象 + * @param {Object} value + */ +function promise(value) { + return object(value) && func(value.then) && func(value.catch); +} + +/** 是否图片格式 + * @param {Object} value + */ +function image(value) { + const newValue = value.split('?')[0]; + const IMAGE_REGEXP = /\.(jpeg|jpg|gif|png|svg|webp|jfif|bmp|dpg)/i; + return IMAGE_REGEXP.test(newValue); +} + +/** + * 是否视频格式 + * @param {Object} value + */ +function video(value) { + const VIDEO_REGEXP = /\.(mp4|mpg|mpeg|dat|asf|avi|rm|rmvb|mov|wmv|flv|mkv|m3u8)/i; + return VIDEO_REGEXP.test(value); +} + +/** + * 是否为正则对象 + * @param {Object} + * @return {Boolean} + */ +function regExp(o) { + return o && Object.prototype.toString.call(o) === '[object RegExp]'; +} + +export default { + email, + mobile, + url, + date, + dateISO, + number, + digits, + idCard, + carNo, + amount, + chinese, + letter, + enOrNum, + contains, + range, + rangeLength, + empty, + isEmpty: empty, + isNumber: number, + jsonString, + landline, + object, + array, + code, +}; diff --git a/sheep/helper/throttle.js b/sheep/helper/throttle.js new file mode 100644 index 0000000..c318127 --- /dev/null +++ b/sheep/helper/throttle.js @@ -0,0 +1,31 @@ +let timer; +let flag; +/** + * 节流原理:在一定时间内,只能触发一次 + * + * @param {Function} func 要执行的回调函数 + * @param {Number} wait 延时的时间 + * @param {Boolean} immediate 是否立即执行 + * @return null + */ +function throttle(func, wait = 500, immediate = true) { + if (immediate) { + if (!flag) { + flag = true; + // 如果是立即执行,则在wait毫秒内开始时执行 + typeof func === 'function' && func(); + timer = setTimeout(() => { + flag = false; + }, wait); + } else { + } + } else if (!flag) { + flag = true; + // 如果是非立即执行,则在wait毫秒内的结束处执行 + timer = setTimeout(() => { + flag = false; + typeof func === 'function' && func(); + }, wait); + } +} +export default throttle; diff --git a/sheep/helper/tools.js b/sheep/helper/tools.js new file mode 100644 index 0000000..49efcec --- /dev/null +++ b/sheep/helper/tools.js @@ -0,0 +1,67 @@ +import router from '@/sheep/router'; +export default { + /** + * 打电话 + * @param {String} phoneNumber - 数字字符串 + */ + callPhone(phoneNumber = '') { + let num = phoneNumber.toString(); + uni.makePhoneCall({ + phoneNumber: num, + fail(err) { + console.log('makePhoneCall出错', err); + }, + }); + }, + + /** + * 微信头像 + * @param {String} url -图片地址 + */ + checkMPUrl(url) { + // #ifdef MP + if ( + url.substring(0, 4) === 'http' && + url.substring(0, 5) !== 'https' && + url.substring(0, 12) !== 'http://store' && + url.substring(0, 10) !== 'http://tmp' && + url.substring(0, 10) !== 'http://usr' + ) { + url = 'https' + url.substring(4, url.length); + } + // #endif + return url; + }, + + /** + * getUuid 生成唯一id + */ + getUuid(len = 32, firstU = true, radix = null) { + const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split(''); + const uuid = []; + radix = radix || chars.length; + + if (len) { + // 如果指定uuid长度,只是取随机的字符,0|x为位运算,能去掉x的小数位,返回整数位 + for (let i = 0; i < len; i++) uuid[i] = chars[0 | (Math.random() * radix)]; + } else { + let r; + // rfc4122标准要求返回的uuid中,某些位为固定的字符 + uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-'; + uuid[14] = '4'; + + for (let i = 0; i < 36; i++) { + if (!uuid[i]) { + r = 0 | (Math.random() * 16); + uuid[i] = chars[i == 19 ? (r & 0x3) | 0x8 : r]; + } + } + } + // 移除第一个字符,并用u替代,因为第一个字符为数值时,该guuid不能用作id或者class + if (firstU) { + uuid.shift(); + return `u${uuid.join('')}`; + } + return uuid.join(''); + }, +}; diff --git a/sheep/helper/utils.js b/sheep/helper/utils.js new file mode 100644 index 0000000..2cdc0f6 --- /dev/null +++ b/sheep/helper/utils.js @@ -0,0 +1,336 @@ +export function isArray(value) { + if (typeof Array.isArray === 'function') { + return Array.isArray(value); + } else { + return Object.prototype.toString.call(value) === '[object Array]'; + } +} + +export function isObject(value) { + return Object.prototype.toString.call(value) === '[object Object]'; +} + +export function isNumber(value) { + return !isNaN(Number(value)); +} + +export function isFunction(value) { + return typeof value == 'function'; +} + +export function isString(value) { + return typeof value == 'string'; +} + +export function isEmpty(value) { + if (value === '' || value === undefined || value === null) { + return true; + } + + if (isArray(value)) { + return value.length === 0; + } + + if (isObject(value)) { + return Object.keys(value).length === 0; + } + + return false; +} + +export function isBoolean(value) { + return typeof value === 'boolean'; +} + +export function last(data) { + if (isArray(data) || isString(data)) { + return data[data.length - 1]; + } +} + +export function cloneDeep(obj) { + const d = isArray(obj) ? [...obj] : {}; + + if (isObject(obj)) { + for (const key in obj) { + if (obj[key]) { + if (obj[key] && typeof obj[key] === 'object') { + d[key] = cloneDeep(obj[key]); + } else { + d[key] = obj[key]; + } + } + } + } + + return d; +} + +export function clone(obj) { + return Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj)); +} + +export function deepMerge(a, b) { + let k; + for (k in b) { + a[k] = a[k] && a[k].toString() === '[object Object]' ? deepMerge(a[k], b[k]) : (a[k] = b[k]); + } + return a; +} + +export function contains(parent, node) { + while (node && (node = node.parentNode)) if (node === parent) return true; + return false; +} + +export function orderBy(list, key) { + return list.sort((a, b) => a[key] - b[key]); +} + +export function deepTree(list) { + const newList = []; + const map = {}; + + list.forEach((e) => (map[e.id] = e)); + + list.forEach((e) => { + const parent = map[e.parentId]; + + if (parent) { + (parent.children || (parent.children = [])).push(e); + } else { + newList.push(e); + } + }); + + const fn = (list) => { + list.map((e) => { + if (e.children instanceof Array) { + e.children = orderBy(e.children, 'orderNum'); + + fn(e.children); + } + }); + }; + + fn(newList); + + return orderBy(newList, 'orderNum'); +} + +export function revDeepTree(list = []) { + const d = []; + let id = 0; + + const deep = (list, parentId) => { + list.forEach((e) => { + if (!e.id) { + e.id = id++; + } + + e.parentId = parentId; + + d.push(e); + + if (e.children && isArray(e.children)) { + deep(e.children, e.id); + } + }); + }; + + deep(list || [], null); + + return d; +} + +export function basename(path) { + let index = path.lastIndexOf('/'); + index = index > -1 ? index : path.lastIndexOf('\\'); + if (index < 0) { + return path; + } + return path.substring(index + 1); +} + +export function isWxBrowser() { + const ua = navigator.userAgent.toLowerCase(); + if (ua.match(/MicroMessenger/i) == 'micromessenger') { + return true; + } else { + return false; + } +} + +/** + * @description 如果value小于min,取min;如果value大于max,取max + * @param {number} min + * @param {number} max + * @param {number} value + */ +export function range(min = 0, max = 0, value = 0) { + return Math.max(min, Math.min(max, Number(value))); +} + +import dayjs from 'dayjs'; + +/** + * 将一个整数转换为分数保留两位小数 + * @param {number | string | undefined} num 整数 + * @return {number} 分数 + */ +export const formatToFraction = (num) => { + if (typeof num === 'undefined') return 0; + const parsedNumber = typeof num === 'string' ? parseFloat(num) : num; + return parseFloat((parsedNumber / 100).toFixed(2)); +}; + +/** + * 将一个数转换为 1.00 这样 + * 数据呈现的时候使用 + * + * @param {number | string | undefined} num 整数 + * @return {string} 分数 + */ +export const floatToFixed2 = (num) => { + let str = '0.00'; + if (typeof num === 'undefined') { + return str; + } + const f = formatToFraction(num); + const decimalPart = f.toString().split('.')[1]; + const len = decimalPart ? decimalPart.length : 0; + switch (len) { + case 0: + str = f.toString() + '.00'; + break; + case 1: + str = f.toString() + '.0'; + break; + case 2: + str = f.toString(); + break; + } + return str; +}; + +/** + * 时间日期转换 + * @param {dayjs.ConfigType} date 当前时间,new Date() 格式 + * @param {string} format 需要转换的时间格式字符串 + * @description format 字符串随意,如 `YYYY-mm、YYYY-mm-dd` + * @description format 季度:"YYYY-mm-dd HH:MM:SS QQQQ" + * @description format 星期:"YYYY-mm-dd HH:MM:SS WWW" + * @description format 几周:"YYYY-mm-dd HH:MM:SS ZZZ" + * @description format 季度 + 星期 + 几周:"YYYY-mm-dd HH:MM:SS WWW QQQQ ZZZ" + * @returns {string} 返回拼接后的时间字符串 + */ +export function formatDate(date, format = 'YYYY-MM-DD HH:mm:ss') { + // 日期不存在,则返回空 + if (!date) { + return ''; + } + // 日期存在,则进行格式化 + if (format === undefined) { + format = 'YYYY-MM-DD HH:mm:ss'; + } + return dayjs(date).format(format); +} + +/** + * 构造树型结构数据 + * + * @param {*} data 数据源 + * @param {*} id id字段 默认 'id' + * @param {*} parentId 父节点字段 默认 'parentId' + * @param {*} children 孩子节点字段 默认 'children' + * @param {*} rootId 根Id 默认 0 + */ +export function handleTree( + data, + id = 'id', + parentId = 'parentId', + children = 'children', + rootId = 0, +) { + // 对源数据深度克隆 + const cloneData = JSON.parse(JSON.stringify(data)); + // 循环所有项 + const treeData = cloneData.filter((father) => { + let branchArr = cloneData.filter((child) => { + //返回每一项的子级数组 + return father[id] === child[parentId]; + }); + branchArr.length > 0 ? (father.children = branchArr) : ''; + //返回第一层 + return father[parentId] === rootId; + }); + return treeData !== '' ? treeData : data; +} + +/** + * 重置分页对象 + * + * @param pagination 分页对象 + */ +export function resetPagination(pagination) { + pagination.list = []; + pagination.total = 0; + pagination.pageNo = 1; +} + +/** + * 将值复制到目标对象,且以目标对象属性为准,例:target: {a:1} source:{a:2,b:3} 结果为:{a:2} + * @param target 目标对象 + * @param source 源对象 + */ +export const copyValueToTarget = (target, source) => { + const newObj = Object.assign({}, target, source); + // 删除多余属性 + Object.keys(newObj).forEach((key) => { + // 如果不是target中的属性则删除 + if (Object.keys(target).indexOf(key) === -1) { + delete newObj[key]; + } + }); + // 更新目标对象值 + Object.assign(target, newObj); +}; + +/** + * 解析 JSON 字符串 + * + * @param str + */ +export function jsonParse(str) { + try { + return JSON.parse(str); + } catch (e) { + console.warn(`str[${str}] 不是一个 JSON 字符串`); + return str; + } +} + +/** + * 获得当前周的开始和结束时间 + */ +export function getWeekTimes() { + const today = new Date(); + const dayOfWeek = today.getDay(); + return [ + new Date(today.getFullYear(), today.getMonth(), today.getDate() - dayOfWeek, 0, 0, 0), + new Date(today.getFullYear(), today.getMonth(), today.getDate() + (6 - dayOfWeek), 23, 59, 59), + ]; +} + +/** + * 获得当前月的开始和结束时间 + */ +export function getMonthTimes() { + const today = new Date(); + const year = today.getFullYear(); + const month = today.getMonth(); + const startDate = new Date(year, month, 1, 0, 0, 0); + const nextMonth = new Date(year, month + 1, 1); + const endDate = new Date(nextMonth.getTime() - 1); + return [startDate, endDate]; +} diff --git a/sheep/hooks/useGoods.js b/sheep/hooks/useGoods.js new file mode 100644 index 0000000..27932cb --- /dev/null +++ b/sheep/hooks/useGoods.js @@ -0,0 +1,499 @@ +import { ref } from 'vue'; +import dayjs from 'dayjs'; +import $url from '@/sheep/url'; +import { formatDate } from '@/sheep/helper/utils'; + +/** + * 格式化销量 + * @param {'exact' | string} type 格式类型:exact=精确值,其它=大致数量 + * @param {number} num 销量 + * @return {string} 格式化后的销量字符串 + */ +export function formatSales(type, num) { + let prefix = type !== 'exact' && num < 10 ? '销量' : '已售'; + return formatNum(prefix, type, num); +} + +/** + * 格式化兑换量 + * @param {'exact' | string} type 格式类型:exact=精确值,其它=大致数量 + * @param {number} num 销量 + * @return {string} 格式化后的销量字符串 + */ +export function formatExchange(type, num) { + return formatNum('已兑换', type, num); +} + +/** + * 格式化库存 + * @param {'exact' | any} type 格式类型:exact=精确值,其它=大致数量 + * @param {number} num 销量 + * @return {string} 格式化后的销量字符串 + */ +export function formatStock(type, num) { + return formatNum('库存', type, num); +} + +/** + * 格式化数字 + * @param {string} prefix 前缀 + * @param {'exact' | string} type 格式类型:exact=精确值,其它=大致数量 + * @param {number} num 销量 + * @return {string} 格式化后的销量字符串 + */ +export function formatNum(prefix, type, num) { + num = num || 0; + // 情况一:精确数值 + if (type === 'exact') { + return prefix + num; + } + // 情况二:小于等于 10 + if (num < 10) { + return `${prefix}≤10`; + } + // 情况三:大于 10,除第一位外,其它位都显示为0 + // 例如:100 - 199 显示为 100+ + // 9000 - 9999 显示为 9000+ + const numStr = num.toString(); + const first = numStr[0]; + const other = '0'.repeat(numStr.length - 1); + return `${prefix}${first}${other}+`; +} + +// 格式化价格 +export function formatPrice(e) { + return e.length === 1 ? e[0] : e.join('~'); +} + +// 视频格式后缀列表 +const VIDEO_SUFFIX_LIST = ['.avi', '.mp4']; + +/** + * 转换商品轮播的链接列表:根据链接的后缀,判断是视频链接还是图片链接 + * + * @param {string[]} urlList 链接列表 + * @return {{src: string, type: 'video' | 'image' }[]} 转换后的链接列表 + */ +export function formatGoodsSwiper(urlList) { + return ( + urlList + ?.filter((url) => url) + .map((url, key) => { + const isVideo = VIDEO_SUFFIX_LIST.some((suffix) => url.includes(suffix)); + const type = isVideo ? 'video' : 'image'; + const src = $url.cdn(url); + return { + type, + src, + }; + }) || [] + ); +} + +/** + * 格式化订单状态的颜色 + * + * @param order 订单 + * @return {string} 颜色的 class 名称 + */ +export function formatOrderColor(order) { + if (order.status === 0) { + return 'info-color'; + } + if (order.status === 10 || order.status === 20 || (order.status === 30 && !order.commentStatus)) { + return 'warning-color'; + } + if (order.status === 30 && order.commentStatus) { + return 'success-color'; + } + return 'danger-color'; +} + +/** + * 格式化订单状态 + * + * @param order 订单 + */ +export function formatOrderStatus(order) { + if (order.status === 0) { + return '待付款'; + } + if (order.status === 10 && order.deliveryType === 1) { + return '待发货'; + } + if (order.status === 10 && order.deliveryType === 2) { + return '待核销'; + } + if (order.status === 20) { + return '待收货'; + } + if (order.status === 30 && !order.commentStatus) { + return '待评价'; + } + if (order.status === 30 && order.commentStatus) { + return '已完成'; + } + return '已关闭'; +} + +/** + * 格式化订单状态的描述 + * + * @param order 订单 + */ +export function formatOrderStatusDescription(order) { + if (order.status === 0) { + return `请在 ${formatDate(order.payExpireTime)} 前完成支付`; + } + if (order.status === 10) { + return '商家未发货,请耐心等待'; + } + if (order.status === 20) { + return '商家已发货,请耐心等待'; + } + if (order.status === 30 && !order.commentStatus) { + return '已收货,快去评价一下吧'; + } + if (order.status === 30 && order.commentStatus) { + return '交易完成,感谢您的支持'; + } + return '交易关闭'; +} + +/** + * 处理订单的 button 操作按钮数组 + * + * @param order 订单 + */ +export function handleOrderButtons(order) { + order.buttons = []; + if (order.type === 3) { + // 查看拼团 + order.buttons.push('combination'); + } + if (order.status === 20) { + // 确认收货 + order.buttons.push('confirm'); + } + if (order.logisticsId > 0) { + // 查看物流 + order.buttons.push('express'); + } + if (order.status === 0) { + // 取消订单 / 发起支付 + order.buttons.push('cancel'); + order.buttons.push('pay'); + } + if (order.status === 30 && !order.commentStatus) { + // 发起评价 + order.buttons.push('comment'); + } + if (order.status === 40) { + // 删除订单 + order.buttons.push('delete'); + } +} + +/** + * 格式化售后状态 + * + * @param afterSale 售后 + */ +export function formatAfterSaleStatus(afterSale) { + if (afterSale.status === 10) { + return '申请售后'; + } + if (afterSale.status === 20) { + return '商品待退货'; + } + if (afterSale.status === 30) { + return '商家待收货'; + } + if (afterSale.status === 40) { + return '等待退款'; + } + if (afterSale.status === 50) { + return '退款成功'; + } + if (afterSale.status === 61) { + return '买家取消'; + } + if (afterSale.status === 62) { + return '商家拒绝'; + } + if (afterSale.status === 63) { + return '商家拒收货'; + } + return '未知状态'; +} + +/** + * 格式化售后状态的描述 + * + * @param afterSale 售后 + */ +export function formatAfterSaleStatusDescription(afterSale) { + if (afterSale.status === 10) { + return '退款申请待商家处理'; + } + if (afterSale.status === 20) { + return '请退货并填写物流信息'; + } + if (afterSale.status === 30) { + return '退货退款申请待商家处理'; + } + if (afterSale.status === 40) { + return '等待退款'; + } + if (afterSale.status === 50) { + return '退款成功'; + } + if (afterSale.status === 61) { + return '退款关闭'; + } + if (afterSale.status === 62) { + return `商家不同意退款申请,拒绝原因:${afterSale.auditReason}`; + } + if (afterSale.status === 63) { + return `商家拒绝收货,不同意退款,拒绝原因:${afterSale.auditReason}`; + } + return '未知状态'; +} + +/** + * 处理售后的 button 操作按钮数组 + * + * @param afterSale 售后 + */ +export function handleAfterSaleButtons(afterSale) { + afterSale.buttons = []; + if ([10, 20, 30].includes(afterSale.status)) { + // 取消订单 + afterSale.buttons.push('cancel'); + } + if (afterSale.status === 20) { + // 退货信息 + afterSale.buttons.push('delivery'); + } +} + +/** + * 倒计时 + * @param toTime 截止时间 + * @param fromTime 起始时间,默认当前时间 + * @return {{s: string, ms: number, h: string, m: string}} 持续时间 + */ +export function useDurationTime(toTime, fromTime = '') { + toTime = getDayjsTime(toTime); + if (fromTime === '') { + fromTime = dayjs(); + } + let duration = ref(toTime - fromTime); + if (duration.value > 0) { + setTimeout(() => { + if (duration.value > 0) { + duration.value -= 1000; + } + }, 1000); + } + + let durationTime = dayjs.duration(duration.value); + return { + h: (durationTime.months() * 30 * 24 + durationTime.days() * 24 + durationTime.hours()) + .toString() + .padStart(2, '0'), + m: durationTime.minutes().toString().padStart(2, '0'), + s: durationTime.seconds().toString().padStart(2, '0'), + ms: durationTime.$ms, + }; +} + +/** + * 转换为 Dayjs + * @param {any} time 时间 + * @return {dayjs.Dayjs} + */ +function getDayjsTime(time) { + time = time.toString(); + if (time.indexOf('-') > 0) { + // 'date' + return dayjs(time); + } + if (time.length > 10) { + // 'timestamp' + return dayjs(parseInt(time)); + } + if (time.length === 10) { + // 'unixTime' + return dayjs.unix(parseInt(time)); + } +} + +/** + * 将分转成元 + * + * @param price 分,例如说 100 分 + * @returns {string} 元,例如说 1.00 元 + */ +export function fen2yuan(price) { + return (price / 100.0).toFixed(2); +} + +/** + * 将分转成元 + * + * 如果没有小数点,则不展示小数点部分 + * + * @param price 分,例如说 100 分 + * @returns {string} 元,例如说 1 元 + */ +export function fen2yuanSimple(price) { + return fen2yuan(price).replace(/\.?0+$/, ''); +} + +/** + * 将折扣百分比转化为“打x者”的 x 部分 + * + * @param discountPercent + */ +export function formatDiscountPercent(discountPercent) { + return (discountPercent / 10.0).toFixed(1).replace(/\.?0+$/, ''); +} + +/** + * 从商品 SKU 数组中,转换出商品属性的数组 + * + * 类似结构:[{ + * id: // 属性的编号 + * name: // 属性的名字 + * values: [{ + * id: // 属性值的编号 + * name: // 属性值的名字 + * }] + * }] + * + * @param skus 商品 SKU 数组 + */ +export function convertProductPropertyList(skus) { + let result = []; + for (const sku of skus) { + if (!sku.properties) { + continue; + } + for (const property of sku.properties) { + // ① 先处理属性 + let resultProperty = result.find((item) => item.id === property.propertyId); + if (!resultProperty) { + resultProperty = { + id: property.propertyId, + name: property.propertyName, + values: [], + }; + result.push(resultProperty); + } + // ② 再处理属性值 + let resultValue = resultProperty.values.find((item) => item.id === property.valueId); + if (!resultValue) { + resultProperty.values.push({ + id: property.valueId, + name: property.valueName, + }); + } + } + } + return result; +} + +export function appendSettlementProduct(spus, settlementInfos) { + if (!settlementInfos || settlementInfos.length === 0) { + return; + } + for (const spu of spus) { + const settlementInfo = settlementInfos.find((info) => info.spuId === spu.id); + if (!settlementInfo) { + return; + } + // 选择价格最小的 SKU 设置到 SPU 上 + const settlementSku = settlementInfo.skus + .filter((sku) => sku.promotionPrice > 0) + .reduce((prev, curr) => (prev.promotionPrice < curr.promotionPrice ? prev : curr), []); + if (settlementSku) { + spu.promotionType = settlementSku.promotionType; + spu.promotionPrice = settlementSku.promotionPrice; + } + // 设置【满减送】活动 + if (settlementInfo.rewardActivity) { + spu.rewardActivity = settlementInfo.rewardActivity; + } + } +} + +// 获得满减送活动的规则描述(group) +export function getRewardActivityRuleGroupDescriptions(activity) { + if (!activity || !activity.rules || activity.rules.length === 0) { + return []; + } + const result = [ + { name: '满减', values: [] }, + { name: '赠品', values: [] }, + { name: '包邮', values: [] }, + ]; + activity.rules.forEach((rule) => { + const conditionTypeStr = + activity.conditionType === 10 ? `满 ${fen2yuanSimple(rule.limit)} 元` : `满 ${rule.limit} 件`; + // 满减 + if (rule.limit) { + result[0].values.push(`${conditionTypeStr} 减 ${fen2yuanSimple(rule.discountPrice)} 元`); + } + // 赠品 + if (rule.point || (rule.giveCouponTemplateCounts && rule.giveCouponTemplateCounts.length > 0)) { + let tips = []; + if (rule.point) { + tips.push(`送 ${rule.point} 积分`); + } + if (rule.giveCouponTemplateCounts && rule.giveCouponTemplateCounts.length > 0) { + tips.push(`送 ${rule.giveCouponTemplateCounts.length} 张优惠券`); + } + result[1].values.push(`${conditionTypeStr} ${tips.join('、')}`); + } + // 包邮 + if (rule.freeDelivery) { + result[2].values.push(`${conditionTypeStr} 包邮`); + } + }); + // 移除 values 为空的元素 + result.forEach((item) => { + if (item.values.length === 0) { + result.splice(result.indexOf(item), 1); + } + }); + return result; +} + +// 获得满减送活动的规则描述(item) +export function getRewardActivityRuleItemDescriptions(activity) { + if (!activity || !activity.rules || activity.rules.length === 0) { + return []; + } + const result = []; + activity.rules.forEach((rule) => { + const conditionTypeStr = + activity.conditionType === 10 ? `满${fen2yuanSimple(rule.limit)}元` : `满${rule.limit}件`; + // 满减 + if (rule.limit) { + result.push(`${conditionTypeStr}减${fen2yuanSimple(rule.discountPrice)}元`); + } + // 赠品 + if (rule.point) { + result.push(`${conditionTypeStr}送${rule.point}积分`); + } + if (rule.giveCouponTemplateCounts && rule.giveCouponTemplateCounts.length > 0) { + result.push(`${conditionTypeStr}送${rule.giveCouponTemplateCounts.length}张优惠券`); + } + // 包邮 + if (rule.freeDelivery) { + result.push(`${conditionTypeStr}包邮`); + } + }); + return result; +} diff --git a/sheep/hooks/useModal.js b/sheep/hooks/useModal.js new file mode 100644 index 0000000..5b4d163 --- /dev/null +++ b/sheep/hooks/useModal.js @@ -0,0 +1,142 @@ +import $store from '@/sheep/store'; +import $helper from '@/sheep/helper'; +import dayjs from 'dayjs'; +import { ref } from 'vue'; +import test from '@/sheep/helper/test.js'; +import AuthUtil from '@/sheep/api/member/auth'; + +// 打开授权弹框 +export function showAuthModal(type = 'smsLogin') { + const modal = $store('modal'); + // #ifdef H5 + closeAuthModal(); + setTimeout(() => { + modal.$patch((state) => { + state.auth = type; + }); + }, 200); + // #endif + + // #ifndef H5 + modal.$patch((state) => { + state.auth = type; + }); + // #endif +} + +// 关闭授权弹框 +export function closeAuthModal() { + $store('modal').$patch((state) => { + state.auth = ''; + }); +} + +// 打开分享弹框 +export function showShareModal() { + $store('modal').$patch((state) => { + state.share = true; + }); +} + +// 关闭分享弹框 +export function closeShareModal() { + $store('modal').$patch((state) => { + state.share = false; + }); +} + +// 打开快捷菜单 +export function showMenuTools() { + $store('modal').$patch((state) => { + state.menu = true; + }); +} + +// 关闭快捷菜单 +export function closeMenuTools() { + $store('modal').$patch((state) => { + state.menu = false; + }); +} + +// 发送短信验证码 60秒 +export function getSmsCode(event, mobile) { + const modalStore = $store('modal'); + const lastSendTimer = modalStore.lastTimer[event]; + if (typeof lastSendTimer === 'undefined') { + $helper.toast('短信发送事件错误'); + return; + } + + const duration = dayjs().unix() - lastSendTimer; + const canSend = duration >= 60; + if (!canSend) { + $helper.toast('请稍后再试'); + return; + } + // 只有 mobile 非空时才校验。因为部分场景(修改密码),不需要输入手机 + if (mobile && !test.mobile(mobile)) { + $helper.toast('手机号码格式不正确'); + return; + } + + // 发送验证码 + 更新上次发送验证码时间 + let scene = -1; + switch (event) { + case 'resetPassword': + scene = 4; + break; + case 'changePassword': + scene = 3; + break; + case 'changeMobile': + scene = 2; + break; + case 'smsLogin': + scene = 1; + break; + } + AuthUtil.sendSmsCode(mobile, scene).then((res) => { + if (res.code === 0) { + modalStore.$patch((state) => { + state.lastTimer[event] = dayjs().unix(); + }); + } + }); +} + +// 获取短信验证码倒计时 -- 60秒 +export function getSmsTimer(event, mobile = '') { + const modalStore = $store('modal'); + const lastSendTimer = modalStore.lastTimer[event]; + + if (typeof lastSendTimer === 'undefined') { + $helper.toast('短信发送事件错误'); + return; + } + + const duration = ref(dayjs().unix() - lastSendTimer - 60); + const canSend = duration.value >= 0; + + if (canSend) { + return '获取验证码'; + } + + if (!canSend) { + setTimeout(() => { + duration.value++; + }, 1000); + return -duration.value.toString() + ' 秒'; + } +} + +// 记录广告弹框历史 +export function saveAdvHistory(adv) { + const modal = $store('modal'); + + modal.$patch((state) => { + if (!state.advHistory.includes(adv.imgUrl)) { + state.advHistory.push(adv.imgUrl); + } + }); +} diff --git a/sheep/index.js b/sheep/index.js new file mode 100644 index 0000000..c135334 --- /dev/null +++ b/sheep/index.js @@ -0,0 +1,52 @@ +import $api from '@/sheep/api'; +import $url from '@/sheep/url'; +import $router from '@/sheep/router'; +import $platform from '@/sheep/platform'; +import $helper from '@/sheep/helper'; +import zIndex from '@/sheep/config/zIndex.js'; +import $store from '@/sheep/store'; +import dayjs from 'dayjs'; +import relativeTime from 'dayjs/plugin/relativeTime'; +import duration from 'dayjs/plugin/duration'; +import 'dayjs/locale/zh-cn'; + +dayjs.locale('zh-cn'); +dayjs.extend(relativeTime); +dayjs.extend(duration); + +const sheep = { + $api, + $store, + $url, + $router, + $platform, + $helper, + $zIndex: zIndex, +}; + +// 加载Shopro底层依赖 +export async function ShoproInit() { + // 应用初始化 + await $store('app').init(); + + // 平台初始化加载(各平台provider提供不同的加载流程) + $platform.load(); + + if (process.env.NODE_ENV === 'development') { + ShoproDebug(); + } +} + +// 开发模式 +function ShoproDebug() { + // 开发环境引入vconsole调试 + // #ifdef H5 + // import("vconsole").then(vconsole => { + // new vconsole.default(); + // }); + // #endif + // 同步前端页面到后端 + // console.log(ROUTES) +} + +export default sheep; diff --git a/sheep/libs/mplive-manifest-plugin.js b/sheep/libs/mplive-manifest-plugin.js new file mode 100644 index 0000000..d1df9bf --- /dev/null +++ b/sheep/libs/mplive-manifest-plugin.js @@ -0,0 +1,32 @@ +const fs = require('fs'); + +const manifestPath = process.env.UNI_INPUT_DIR + '/manifest.json'; + +let Manifest = fs.readFileSync(manifestPath, { + encoding: 'utf-8' +}); + +function mpliveMainfestPlugin(isOpen) { + if (process.env.UNI_PLATFORM !== 'mp-weixin') return; + + const manifestData = JSON.parse(Manifest) + + if (isOpen === '0') { + delete manifestData['mp-weixin'].plugins['live-player-plugin']; + } + + if (isOpen === '1') { + manifestData['mp-weixin'].plugins['live-player-plugin'] = { + "version": "1.3.5", + "provider": "wx2b03c6e691cd7370" + } + } + + Manifest = JSON.stringify(manifestData, null, 2) + + fs.writeFileSync(manifestPath, Manifest, { + "flag": "w" + }) +} + +export default mpliveMainfestPlugin diff --git a/sheep/libs/permission.js b/sheep/libs/permission.js new file mode 100644 index 0000000..3e05a26 --- /dev/null +++ b/sheep/libs/permission.js @@ -0,0 +1,244 @@ +/// 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.getDeviceInfo().platform === 'ios'); + }, + requestIOS: requestIOS, + requestAndroid: requestAndroid, + gotoAppSetting: gotoAppPermissionSetting, +}; + +export default permission; diff --git a/sheep/libs/sdk-h5-weixin.js b/sheep/libs/sdk-h5-weixin.js new file mode 100644 index 0000000..115aece --- /dev/null +++ b/sheep/libs/sdk-h5-weixin.js @@ -0,0 +1,194 @@ +/** + * 本模块封装微信浏览器下的一些方法。 + * 更多微信网页开发sdk方法,详见:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html + * 有 the permission value is offline verifying 报错请参考 @see https://segmentfault.com/a/1190000042289419 解决 + */ + +import jweixin from 'weixin-js-sdk'; +import $helper from '@/sheep/helper'; +import AuthUtil from '@/sheep/api/member/auth'; + +let configSuccess = false; + +export default { + // 判断是否在微信中 + isWechat() { + const ua = window.navigator.userAgent.toLowerCase(); + // noinspection EqualityComparisonWithCoercionJS + return ua.match(/micromessenger/i) == 'micromessenger'; + }, + + isReady(api) { + jweixin.ready(api); + }, + + // 初始化 JSSDK + async init(callback) { + if (!this.isWechat()) { + $helper.toast('请使用微信网页浏览器打开'); + return; + } + + // 调用后端接口,获得 JSSDK 初始化所需的签名 + const url = location.href.split('#')[0]; + const { code, data } = await AuthUtil.createWeixinMpJsapiSignature(url); + if (code === 0) { + jweixin.config({ + debug: false, + appId: data.appId, + timestamp: data.timestamp, + nonceStr: data.nonceStr, + signature: data.signature, + jsApiList: [ + 'chooseWXPay', + 'openLocation', + 'getLocation', + 'updateTimelineShareData', + 'scanQRCode', + ], // TODO 芋艿:后续可以设置更多权限; + openTagList: data.openTagList, + }); + } else { + console.log('请求 JSSDK 配置失败,错误码:', code); + } + + // 监听结果 + configSuccess = true; + jweixin.error((err) => { + debugger; + configSuccess = false; + console.error('微信 JSSDK 初始化失败', err); + // $helper.toast('微信JSSDK:' + err.errMsg); + }); + jweixin.ready(() => { + debugger; + if (configSuccess) { + console.log('微信 JSSDK 初始化成功'); + } + }); + + // 回调 + if (callback) { + callback(data); + } + }, + + //在需要定位页面调用 TODO 芋艿:未测试 + getLocation(callback) { + this.isReady(() => { + jweixin.getLocation({ + type: 'gcj02', // 默认为wgs84的gps坐标,如果要返回直接给openLocation用的火星坐标,可传入'gcj02' + success: function (res) { + callback(res); + }, + fail: function (res) { + console.log('%c微信H5sdk,getLocation失败:', 'color:green;background:yellow'); + }, + }); + }); + }, + + // 获取微信收货地址 + openAddress(callback) { + this.isReady(() => { + jweixin.openAddress({ + success: function (res) { + callback.success && callback.success(res); + }, + fail: function (err) { + callback.error && callback.error(err); + console.log('%c微信H5sdk,openAddress失败:', 'color:green;background:yellow'); + }, + complete: function (res) {}, + }); + }); + }, + + // 微信扫码 TODO 芋艿:未测试 + scanQRCode(callback) { + this.isReady(() => { + jweixin.scanQRCode({ + needResult: 1, // 默认为0,扫描结果由微信处理,1则直接返回扫描结果, + scanType: ['qrCode', 'barCode'], // 可以指定扫二维码还是一维码,默认二者都有 + success: function (res) { + callback(res); + }, + fail: function (res) { + console.log('%c微信H5sdk,scanQRCode失败:', 'color:green;background:yellow'); + }, + }); + }); + }, + + // 更新微信分享信息 + updateShareInfo(data, callback = null) { + this.isReady(() => { + const shareData = { + title: data.title, + desc: data.desc, + link: data.link, + imgUrl: data.image, + success: function (res) { + if (callback) { + callback(res); + } + // 分享后的一些操作,比如分享统计等等 + }, + cancel: function (res) {}, + }; + + // 新版 分享聊天api + jweixin.updateAppMessageShareData(shareData); + // 新版 分享到朋友圈api + jweixin.updateTimelineShareData(shareData); + }); + }, + + // 打开坐标位置 TODO 芋艿:未测试 + openLocation(data, callback) { + this.isReady(() => { + jweixin.openLocation({ + ...data, + success: function (res) { + console.log(res); + }, + }); + }); + }, + + // 选择图片 TODO 芋艿:未测试 + chooseImage(callback) { + this.isReady(() => { + jweixin.chooseImage({ + count: 1, + sizeType: ['compressed'], + sourceType: ['album'], + success: function (rs) { + callback(rs); + }, + }); + }); + }, + + // 微信支付 + wxpay(data, callback) { + this.isReady(() => { + jweixin.chooseWXPay({ + timestamp: data.timeStamp, // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符 + nonceStr: data.nonceStr, // 支付签名随机串,不长于 32 位 + package: data.packageValue, // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=\*\*\*) + signType: data.signType, // 签名方式,默认为'SHA1',使用新版支付需传入'MD5' + paySign: data.paySign, // 支付签名 + success: function (res) { + callback.success && callback.success(res); + }, + fail: function (err) { + callback.fail && callback.fail(err); + }, + cancel: function (err) { + callback.cancel && callback.cancel(err); + }, + }); + }); + }, +}; diff --git a/sheep/platform/index.js b/sheep/platform/index.js new file mode 100644 index 0000000..bf48e52 --- /dev/null +++ b/sheep/platform/index.js @@ -0,0 +1,175 @@ +/** + * Shopro 第三方平台功能聚合 + * @version 1.0.3 + * @author lidongtony + * @param {String} name - 厂商+平台名称 + * @param {String} provider - 厂商 + * @param {String} platform - 平台名称 + * @param {String} os - 系统型号 + * @param {Object} device - 设备信息 + */ + +import { isEmpty } from 'lodash-es'; +// #ifdef H5 +import { isWxBrowser } from '@/sheep/helper/utils'; +// #endif +import wechat from './provider/wechat/index.js'; +import apple from './provider/apple'; +import share from './share'; +import Pay from './pay'; + +const device = uni.getWindowInfo(); + +const os = uni.getDeviceInfo().platform; + +let name = ''; +let provider = ''; +let platform = ''; +let isWechatInstalled = true; + +// #ifdef H5 +if (isWxBrowser()) { + name = 'WechatOfficialAccount'; + provider = 'wechat'; + platform = 'officialAccount'; +} else { + name = 'H5'; + platform = 'h5'; +} +// #endif + +// #ifdef APP-PLUS +name = 'App'; +platform = 'openPlatform'; +// 检查微信客户端是否安装,否则AppleStore会因此拒绝上架 +if (os === 'ios') { + isWechatInstalled = plus.ios.import('WXApi').isWXAppInstalled(); +} +// #endif + +// #ifdef MP-WEIXIN +name = 'WechatMiniProgram'; +platform = 'miniProgram'; +provider = 'wechat'; +// #endif + +if (isEmpty(name)) { + uni.showToast({ + title: '暂不支持该平台', + icon: 'none', + }); +} + +// 加载当前平台前置行为 +const load = () => { + if (provider === 'wechat') { + wechat.load(); + } +}; + +// 使用厂商独占sdk name = 'wechat' | 'alipay' | 'apple' +const useProvider = (_provider = '') => { + if (_provider === '') _provider = provider; + if (_provider === 'wechat') return wechat; + if (_provider === 'apple') return apple; +}; + +// 支付服务转发 +const pay = (payment, orderType, orderSN) => { + return new Pay(payment, orderType, orderSN); +}; + +/** + * 检查更新 (只检查小程序和App) + * @param {Boolean} silence - 静默检查 + */ +const checkUpdate = (silence = false) => { + let canUpdate; + // #ifdef MP-WEIXIN + useProvider().checkUpdate(silence); + // #endif + + // #ifdef APP-PLUS + // TODO: 热更新 + // #endif +}; + +/** + * 检查网络 + * @param {Boolean} silence - 静默检查 + */ +async function checkNetwork() { + const networkStatus = await uni.getNetworkType(); + if (networkStatus.networkType == 'none') { + return Promise.resolve(false); + } + return Promise.resolve(true); +} + +// 获取小程序胶囊信息 +const getCapsule = () => { + // #ifdef MP + let capsule = uni.getMenuButtonBoundingClientRect(); + if (!capsule) { + capsule = { + bottom: 56, + height: 32, + left: 278, + right: 365, + top: 24, + width: 87, + }; + } + return capsule; + // #endif + + // #ifndef MP + return { + bottom: 56, + height: 32, + left: 278, + right: 365, + top: 24, + width: 87, + }; + // #endif +}; + +const capsule = getCapsule(); + +// 标题栏高度 +const getNavBar = () => { + return device.statusBarHeight + 44; +}; +const navbar = getNavBar(); + +function getLandingPage() { + let page = ''; + // #ifdef H5 + page = location.href.split('?')[0]; + // #endif + return page; +} + +// 设置ios+公众号网页落地页 解决微信sdk签名问题 +const landingPage = getLandingPage(); + +const _platform = { + name, + device, + os, + provider, + platform, + useProvider, + checkUpdate, + checkNetwork, + pay, + share, + load, + capsule, + navbar, + landingPage, + isWechatInstalled, +}; + +export default _platform; diff --git a/sheep/platform/pay.js b/sheep/platform/pay.js new file mode 100644 index 0000000..f0b5cc0 --- /dev/null +++ b/sheep/platform/pay.js @@ -0,0 +1,406 @@ +import sheep from '@/sheep'; +// #ifdef H5 +import $wxsdk from '@/sheep/libs/sdk-h5-weixin'; +// #endif +import { getRootUrl } from '@/sheep/helper'; +import PayOrderApi from '@/sheep/api/pay/order'; + +/** + * 支付 + * + * @param {String} payment = ['wechat','alipay','wallet','mock'] - 支付方式 + * @param {String} orderType = ['goods','recharge','groupon'] - 订单类型 + * @param {String} id - 订单号 + */ + +export default class SheepPay { + constructor(payment, orderType, id) { + this.payment = payment; + this.id = id; + this.orderType = orderType; + this.payAction(); + } + + payAction() { + const payAction = { + WechatOfficialAccount: { + wechat: () => { + this.wechatOfficialAccountPay(); + }, + alipay: () => { + this.redirectPay(); // 现在公众号可以直接跳转支付宝页面 + }, + wallet: () => { + this.walletPay(); + }, + mock: () => { + this.mockPay(); + }, + }, + WechatMiniProgram: { + wechat: () => { + this.wechatMiniProgramPay(); + }, + alipay: () => { + this.copyPayLink(); + }, + wallet: () => { + this.walletPay(); + }, + mock: () => { + this.mockPay(); + }, + }, + App: { + wechat: () => { + this.wechatAppPay(); + }, + alipay: () => { + this.alipay(); + }, + wallet: () => { + this.walletPay(); + }, + mock: () => { + this.mockPay(); + }, + }, + H5: { + wechat: () => { + this.wechatWapPay(); + }, + alipay: () => { + this.redirectPay(); + }, + wallet: () => { + this.walletPay(); + }, + mock: () => { + this.mockPay(); + }, + }, + }; + return payAction[sheep.$platform.name][this.payment](); + } + + // 预支付 + prepay(channel) { + return new Promise(async (resolve, reject) => { + let data = { + id: this.id, + channelCode: channel, + channelExtras: {}, + }; + // 特殊逻辑:微信公众号、小程序支付时,必须传入 openid + if (['wx_pub', 'wx_lite'].includes(channel)) { + const openid = await sheep.$platform.useProvider('wechat').getOpenid(); + // 如果获取不到 openid,微信无法发起支付,此时需要引导 + if (!openid) { + this.bindWeixin(); + return; + } + data.channelExtras.openid = openid; + } + // 发起预支付 API 调用 + PayOrderApi.submitOrder(data).then((res) => { + // 成功时 + res.code === 0 && resolve(res); + // 失败时 + if (res.code !== 0 && res.msg.indexOf('无效的openid') >= 0) { + // 特殊逻辑:微信公众号、小程序支付时,必须传入 openid 不正确的情况 + if ( + res.msg.indexOf('无效的openid') >= 0 || // 获取的 openid 不正确时,或者随便输入了个 openid + res.msg.indexOf('下单账号与支付账号不一致') >= 0 + ) { + // https://developers.weixin.qq.com/community/develop/doc/00008c53c347804beec82aed051c00 + this.bindWeixin(); + } + } + }); + }); + } + // #ifdef H5 + // 微信公众号 JSSDK 支付 + async wechatOfficialAccountPay() { + let { code, data } = await this.prepay('wx_pub'); + if (code !== 0) { + return; + } + const payConfig = JSON.parse(data.displayContent); + $wxsdk.wxpay(payConfig, { + success: () => { + this.payResult('success'); + }, + cancel: () => { + sheep.$helper.toast('支付已手动取消'); + }, + fail: (error) => { + if (error.errMsg.indexOf('chooseWXPay:没有此SDK或暂不支持此SDK模拟') >= 0) { + sheep.$helper.toast( + '发起微信支付失败,原因:可能是微信开发者工具不支持,建议使用微信打开网页后支付', + ); + return; + } + this.payResult('fail'); + }, + }); + } + + // 浏览器微信 H5 支付 TODO 芋艿:待接入(注意:H5 支付是给普通浏览器,不是微信公众号的支付,绝大多数人用不到,可以忽略) + async wechatWapPay() { + const { error, data } = await this.prepay(); + if (error === 0) { + const redirect_url = `${getRootUrl()}pages/pay/result?id=${this.id}&payment=${ + this.payment + }&orderType=${this.orderType}`; + location.href = `${data.pay_data.h5_url}&redirect_url=${encodeURIComponent(redirect_url)}`; + } + } + + // 支付链接(支付宝 wap 支付) + async redirectPay() { + let { code, data } = await this.prepay('alipay_wap'); + if (code !== 0) { + return; + } + location.href = data.displayContent; + } + + // #endif + + // 微信小程序支付 + async wechatMiniProgramPay() { + // let that = this; + let { code, data } = await this.prepay('wx_lite'); + if (code !== 0) { + return; + } + // 调用微信小程序支付 + const payConfig = JSON.parse(data.displayContent); + uni.requestPayment({ + provider: 'wxpay', + timeStamp: payConfig.timeStamp, + nonceStr: payConfig.nonceStr, + package: payConfig.packageValue, + signType: payConfig.signType, + paySign: payConfig.paySign, + success: (res) => { + this.payResult('success'); + }, + fail: (err) => { + if (err.errMsg === 'requestPayment:fail cancel') { + sheep.$helper.toast('支付已手动取消'); + } else { + this.payResult('fail'); + } + }, + }); + } + + // 余额支付 + async walletPay() { + const { code } = await this.prepay('wallet'); + code === 0 && this.payResult('success'); + } + + // 模拟支付 + async mockPay() { + const { code } = await this.prepay('mock'); + code === 0 && this.payResult('success'); + } + + // 支付宝复制链接支付(通过支付宝 wap 支付实现) + async copyPayLink() { + let { code, data } = await this.prepay('alipay_wap'); + if (code !== 0) { + return; + } + // 引入 showModal 点击确认:复制链接; + uni.showModal({ + title: '支付宝支付', + content: '复制链接到外部浏览器', + confirmText: '复制链接', + success: (res) => { + if (res.confirm) { + sheep.$helper.copyText(data.displayContent); + } + }, + }); + } + + // 支付宝支付(App) + async alipay() { + let that = this; + const { code, data } = await this.prepay('alipay_app'); + if (code !== 0) { + return; + } + + uni.requestPayment({ + provider: 'alipay', + orderInfo: data.displayContent, // 直接使用返回的支付参数 + success: (res) => { + that.payResult('success'); + }, + fail: (err) => { + if (err.errMsg === 'requestPayment:fail [paymentAlipay:62001]user cancel') { + sheep.$helper.toast('支付已手动取消'); + } else { + that.payResult('fail'); + } + }, + }); + } + + // 微信支付(App) + async wechatAppPay() { + let that = this; + // 获取预支付信息 + let { code, data } = await this.prepay('wx_app'); + if (code !== 0) { + sheep.$helper.toast('获取支付信息失败'); + return; + } + + // 解析支付参数 + const payConfig = JSON.parse(data.displayContent); + + // 调用微信支付 + uni.requestPayment({ + provider: 'wxpay', + timeStamp: payConfig.timeStamp, + nonceStr: payConfig.nonceStr, + package: payConfig.packageValue, + signType: payConfig.signType, + paySign: payConfig.paySign, + success: (res) => { + that.payResult('success'); + }, + fail: (err) => { + if (err.errMsg === 'requestPayment:fail cancel') { + sheep.$helper.toast('支付已手动取消'); + } else { + sheep.$helper.toast('支付失败:' + err.errMsg); + that.payResult('fail'); + } + }, + }); + } + + // 支付结果跳转,success:成功,fail:失败 + payResult(resultType) { + goPayResult(this.id, this.orderType, resultType); + } + + // 引导绑定微信 + bindWeixin() { + goBindWeixin(); + } +} + +export function getPayMethods(channels) { + const payMethods = [ + { + icon: '/static/img/shop/pay/wechat.png', + title: '微信支付', + value: 'wechat', + disabled: true, + }, + { + icon: '/static/img/shop/pay/alipay.png', + title: '支付宝支付', + value: 'alipay', + disabled: true, + }, + { + icon: '/static/img/shop/pay/wallet.png', + title: '余额支付', + value: 'wallet', + disabled: true, + }, + { + icon: '/static/img/shop/pay/apple.png', + title: 'Apple Pay', + value: 'apple', + disabled: true, + }, + { + icon: '/static/img/shop/pay/wallet.png', + title: '模拟支付', + value: 'mock', + disabled: true, + }, + ]; + const platform = sheep.$platform.name; + + // 1. 处理【微信支付】 + const wechatMethod = payMethods[0]; + if ( + (platform === 'WechatOfficialAccount' && channels.includes('wx_pub')) || + (platform === 'WechatMiniProgram' && channels.includes('wx_lite')) || + (platform === 'App' && channels.includes('wx_app')) + ) { + wechatMethod.disabled = false; + } + + // 2. 处理【支付宝支付】 + const alipayMethod = payMethods[1]; + if ( + (platform === 'H5' && channels.includes('alipay_wap')) || + (platform === 'WechatOfficialAccount' && channels.includes('alipay_wap')) || + (platform === 'WechatMiniProgram' && channels.includes('alipay_wap')) || + (platform === 'App' && channels.includes('alipay_app')) + ) { + alipayMethod.disabled = false; + } + // 3. 处理【余额支付】 + const walletMethod = payMethods[2]; + if (channels.includes('wallet')) { + walletMethod.disabled = false; + } + // 4. 处理【苹果支付】TODO 芋艿:未来接入 + // 5. 处理【模拟支付】 + const mockMethod = payMethods[4]; + if (channels.includes('mock')) { + mockMethod.disabled = false; + } + return payMethods; +} + +// 支付结果跳转,success:成功,fail:失败 +export function goPayResult(id, orderType, resultType) { + sheep.$router.redirect('/pages/pay/result', { + id, + orderType, + payState: resultType, + }); +} + +export function goBindWeixin() { + uni.showModal({ + title: '微信支付', + content: '请先绑定微信再使用微信支付', + success: function (res) { + if (res.confirm) { + sheep.$platform.useProvider('wechat').bind(); + } + }, + }); +} + +// 获取微信支付渠道码 +export function getWeixinPayChannelCode() { + const platform = sheep.$platform.name; + switch (platform) { + case 'WechatOfficialAccount': + return 'wx_pub'; + case 'WechatMiniProgram': + return 'wx_lite'; + case 'App': + return 'wx_app'; + case 'H5': + return 'wx_wap'; + default: + return ''; + } +} diff --git a/sheep/platform/provider/apple/app.js b/sheep/platform/provider/apple/app.js new file mode 100644 index 0000000..c1c9149 --- /dev/null +++ b/sheep/platform/provider/apple/app.js @@ -0,0 +1,36 @@ +// import third from '@/sheep/api/third'; +// TODO 芋艿:等后面搞 App 再弄 + +const login = () => { + return new Promise(async (resolve, reject) => { + const loginRes = await uni.login({ + provider: 'apple', + success: () => { + uni.getUserInfo({ + provider: 'apple', + success: async (res) => { + if (res.errMsg === 'getUserInfo:ok') { + const payload = res.userInfo; + const { error } = await third.apple.login({ + payload, + shareInfo: uni.getStorageSync('shareLog') || {}, + }); + if (error === 0) { + resolve(true); + } else { + resolve(false); + } + } + }, + }); + }, + fail: (err) => { + resolve(false); + }, + }); + }); +}; + +export default { + login, +}; diff --git a/sheep/platform/provider/apple/index.js b/sheep/platform/provider/apple/index.js new file mode 100644 index 0000000..388c093 --- /dev/null +++ b/sheep/platform/provider/apple/index.js @@ -0,0 +1,9 @@ +// #ifdef APP-PLUS +import service from './app'; +// #endif + +let apple = {}; +if (typeof service !== 'undefined') { + apple = service; +} +export default apple; diff --git a/sheep/platform/provider/wechat/index.js b/sheep/platform/provider/wechat/index.js new file mode 100644 index 0000000..3bb2c7f --- /dev/null +++ b/sheep/platform/provider/wechat/index.js @@ -0,0 +1,15 @@ +// #ifdef H5 +import service from './officialAccount'; +// #endif + +// #ifdef MP-WEIXIN +import service from './miniProgram'; +// #endif + +// #ifdef APP-PLUS +import service from './openPlatform'; +// #endif + +const wechat = service; + +export default wechat; diff --git a/sheep/platform/provider/wechat/miniProgram.js b/sheep/platform/provider/wechat/miniProgram.js new file mode 100644 index 0000000..1fe1809 --- /dev/null +++ b/sheep/platform/provider/wechat/miniProgram.js @@ -0,0 +1,241 @@ +import AuthUtil from '@/sheep/api/member/auth'; +import SocialApi from '@/sheep/api/member/social'; +import UserApi from '@/sheep/api/member/user'; +import sheep from '@/sheep'; + +const socialType = 34; // 社交类型 - 微信小程序 + +let subscribeEventList = []; + +// 加载微信小程序 +function load() { + checkUpdate(); + getSubscribeTemplate(); +} + +// 微信小程序静默授权登陆 +const login = async () => { + return new Promise(async (resolve, reject) => { + // 1. 获得微信 code + const codeResult = await uni.login(); + if (codeResult.errMsg !== 'login:ok') { + return resolve(false); + } + + // 2. 社交登录 + const loginResult = await AuthUtil.socialLogin(socialType, codeResult.code, 'default'); + if (loginResult.code === 0) { + setOpenid(loginResult.data.openid); + return resolve(true); + } else { + return resolve(false); + } + }); +}; + +// 微信小程序手机号授权登陆 +const mobileLogin = async (e) => { + return new Promise(async (resolve, reject) => { + if (e.errMsg !== 'getPhoneNumber:ok') { + return resolve(false); + } + + // 1. 获得微信 code + const codeResult = await uni.login(); + if (codeResult.errMsg !== 'login:ok') { + return resolve(false); + } + + // 2. 一键登录 + const loginResult = await AuthUtil.weixinMiniAppLogin(e.code, codeResult.code, 'default'); + if (loginResult.code === 0) { + setOpenid(loginResult.data.openid); + return resolve(true); + } else { + return resolve(false); + } + }); +}; + +// 微信小程序绑定 +const bind = () => { + return new Promise(async (resolve, reject) => { + // 1. 获得微信 code + const codeResult = await uni.login(); + if (codeResult.errMsg !== 'login:ok') { + return resolve(false); + } + + // 2. 绑定账号 + const bindResult = await SocialApi.socialBind(socialType, codeResult.code, 'default'); + if (bindResult.code === 0) { + setOpenid(bindResult.data); + return resolve(true); + } else { + return resolve(false); + } + }); +}; + +// 微信小程序解除绑定 +const unbind = async (openid) => { + const { code } = await SocialApi.socialUnbind(socialType, openid); + return code === 0; +}; + +// 绑定用户手机号 +const bindUserPhoneNumber = (e) => { + return new Promise(async (resolve, reject) => { + const { code } = await UserApi.updateUserMobileByWeixin(e.code); + if (code === 0) { + resolve(true); + } + resolve(false); + }); +}; + +// 设置 openid 到本地存储,目前只有 pay 支付时会使用 +function setOpenid(openid) { + uni.setStorageSync('openid', openid); +} + +// 获得 openid +async function getOpenid(force = false) { + let openid = uni.getStorageSync('openid'); + if (!openid && force) { + const info = await getInfo(); + if (info && info.openid) { + openid = info.openid; + setOpenid(openid); + } + } + return openid; +} + +// 获得社交信息 +async function getInfo() { + const { code, data } = await SocialApi.getSocialUser(socialType); + if (code !== 0) { + return undefined; + } + return data; +} + +// ========== 非登录相关的逻辑 ========== + +// 小程序更新 +const checkUpdate = async (silence = true) => { + if (uni.canIUse('getUpdateManager')) { + const updateManager = uni.getUpdateManager(); + updateManager.onCheckForUpdate(function (res) { + // 请求完新版本信息的回调 + if (res.hasUpdate) { + updateManager.onUpdateReady(function () { + uni.showModal({ + title: '更新提示', + content: '新版本已经准备好,是否重启应用?', + success: function (res) { + if (res.confirm) { + // 新的版本已经下载好,调用 applyUpdate 应用新版本并重启 + updateManager.applyUpdate(); + } + }, + }); + }); + updateManager.onUpdateFailed(function () { + // 新的版本下载失败 + // uni.showModal({ + // title: '已经有新版本了哟~', + // content: '新版本已经上线啦,请您删除当前小程序,重新搜索打开~', + // }); + }); + } else { + if (!silence) { + uni.showModal({ + title: '当前为最新版本', + showCancel: false, + }); + } + } + }); + } +}; + +// 获取订阅消息模板 +async function getSubscribeTemplate() { + const { code, data } = await SocialApi.getSubscribeTemplateList(); + if (code === 0) { + subscribeEventList = data; + } +} + +// 订阅消息 +function subscribeMessage(event, callback = undefined) { + let tmplIds = []; + if (typeof event === 'string') { + const temp = subscribeEventList.find((item) => item.title.includes(event)); + if (temp) { + tmplIds.push(temp.id); + } + } + if (typeof event === 'object') { + event.forEach((e) => { + const temp = subscribeEventList.find((item) => item.title.includes(e)); + if (temp) { + tmplIds.push(temp.id); + } + }); + } + if (tmplIds.length === 0) return; + + uni.requestSubscribeMessage({ + tmplIds, + success: () => { + // 不管是拒绝还是同意都触发 + callback && callback(); + }, + fail: (err) => { + console.log(err); + }, + }); +} + +// 商家转账用户确认模式下,拉起页面请求用户确认收款 Transfer +function requestMerchantTransfer(mchId, packageInfo, successCallback, failCallback) { + if (!wx.canIUse('requestMerchantTransfer')) { + wx.showModal({ + content: '你的微信版本过低,请更新至最新版本。', + showCancel: false, + }); + return; + } + wx.requestMerchantTransfer({ + mchId: mchId, + appId: wx.getAccountInfoSync().miniProgram.appId, + package: packageInfo, + success: (res) => { + // res.err_msg 将在页面展示成功后返回应用时返回 ok,并不代表付款成功 + console.log('success:', res); + successCallback && successCallback(res); + }, + fail: (res) => { + console.log('fail:', res); + sheep.$helper.toast(res.errMsg); + failCallback && failCallback(res); + }, + }); +} + +export default { + load, + login, + bind, + unbind, + bindUserPhoneNumber, + mobileLogin, + getInfo, + getOpenid, + subscribeMessage, + checkUpdate, + requestMerchantTransfer, +}; diff --git a/sheep/platform/provider/wechat/officialAccount.js b/sheep/platform/provider/wechat/officialAccount.js new file mode 100644 index 0000000..18adbe0 --- /dev/null +++ b/sheep/platform/provider/wechat/officialAccount.js @@ -0,0 +1,105 @@ +import $wxsdk from '@/sheep/libs/sdk-h5-weixin'; +import { getRootUrl } from '@/sheep/helper'; +import AuthUtil from '@/sheep/api/member/auth'; +import SocialApi from '@/sheep/api/member/social'; + +const socialType = 31; // 社交类型 - 微信公众号 + +// 加载微信公众号JSSDK +async function load() { + $wxsdk.init(); +} + +// 微信公众号登陆 +async function login(code = '', state = '') { + // 情况一:没有 code 时,去获取 code + if (!code) { + const loginUrl = await getLoginUrl(); + if (loginUrl) { + uni.setStorageSync('returnUrl', location.href); + window.location = loginUrl; + } + // 情况二:有 code 时,使用 code 去自动登录 + } else { + // 解密 code 发起登陆 + const loginResult = await AuthUtil.socialLogin(socialType, code, state); + if (loginResult.code === 0) { + setOpenid(loginResult.data.openid); + return loginResult; + } + } + return false; +} + +// 微信公众号绑定 +async function bind(code = '', state = '') { + // 情况一:没有 code 时,去获取 code + if (code === '') { + const loginUrl = await getLoginUrl('bind'); + if (loginUrl) { + uni.setStorageSync('returnUrl', location.href); + window.location = loginUrl; + } + } else { + // 情况二:有 code 时,使用 code 去自动绑定 + const loginResult = await SocialApi.socialBind(socialType, code, state); + if (loginResult.code === 0) { + setOpenid(loginResult.data); + return loginResult; + } + } + return false; +} + +// 微信公众号解除绑定 +const unbind = async (openid) => { + const { code } = await SocialApi.socialUnbind(socialType, openid); + return code === 0; +}; + +// 获取公众号登陆地址 +async function getLoginUrl(event = 'login') { + const page = getRootUrl() + 'pages/index/login' + '?event=' + event; // event 目的,区分是 login 还是 bind + const { code, data } = await AuthUtil.socialAuthRedirect(socialType, page); + if (code !== 0) { + return undefined; + } + return data; +} + +// 设置 openid 到本地存储,目前只有 pay 支付时会使用 +function setOpenid(openid) { + uni.setStorageSync('openid', openid); +} + +// 获得 openid +async function getOpenid(force = false) { + let openid = uni.getStorageSync('openid'); + if (!openid && force) { + const info = await getInfo(); + if (info && info.openid) { + openid = info.openid; + setOpenid(openid); + } + } + return openid; +} + +// 获得社交信息 +async function getInfo() { + const { code, data } = await SocialApi.getSocialUser(socialType); + if (code !== 0) { + return undefined; + } + return data; +} + +export default { + load, + login, + bind, + unbind, + getInfo, + getOpenid, + jsWxSdk: $wxsdk, +}; diff --git a/sheep/platform/provider/wechat/openPlatform.js b/sheep/platform/provider/wechat/openPlatform.js new file mode 100644 index 0000000..a80f0d7 --- /dev/null +++ b/sheep/platform/provider/wechat/openPlatform.js @@ -0,0 +1,64 @@ +// 登录 +import third from '@/sheep/api/migration/third'; +import SocialApi from '@/sheep/api/member/social'; +import $share from '@/sheep/platform/share'; + +// TODO 芋艿:等后面搞 App 再弄 +const socialType = 32; // 社交类型 - 微信开放平台 + +const load = async () => {}; + +// 微信开放平台移动应用授权登陆 +const login = () => { + return new Promise(async (resolve, reject) => { + const loginRes = await uni.login({ + provider: 'weixin', + onlyAuthorize: true, + }); + debugger + if (loginRes.errMsg == 'login:ok') { + // TODO third.wechat.login 函数未实现 + const res = await third.wechat.login({ + platform: 'openPlatform', + shareInfo: uni.getStorageSync('shareLog') || {}, + payload: encodeURIComponent( + JSON.stringify({ + code: loginRes.code, + }), + ), + }); + + if (res.error === 0) { + $share.bindBrokerageUser() + resolve(true); + } + } else { + uni.showToast({ + icon: 'none', + title: loginRes.errMsg, + }); + } + resolve(false); + }); +}; + +// 微信 App 解除绑定 +const unbind = async (openid) => { + const { code } = await SocialApi.socialUnbind(socialType, openid); + return code === 0; +}; + +// 获得社交信息 +async function getInfo() { + const { code, data } = await SocialApi.getSocialUser(socialType); + if (code !== 0) { + return undefined; + } + return data; +} + +export default { + load, + login, + getInfo +}; diff --git a/sheep/platform/share.js b/sheep/platform/share.js new file mode 100644 index 0000000..b566a9d --- /dev/null +++ b/sheep/platform/share.js @@ -0,0 +1,213 @@ +import $store from '@/sheep/store'; +import $platform from '@/sheep/platform'; +import $router from '@/sheep/router'; +import $url from '@/sheep/url'; +import BrokerageApi from '@/sheep/api/trade/brokerage'; +import { SharePageEnum } from '@/sheep/helper/const'; + +// #ifdef H5 +import $wxsdk from '@/sheep/libs/sdk-h5-weixin'; +// #endif + +// 设置分享的平台渠道: 1=H5,2=微信公众号网页,3=微信小程序,4=App,...按需扩展 +const platformMap = ['H5', 'WechatOfficialAccount', 'WechatMiniProgram', 'App']; + +// 设置分享方式: 1=直接转发,2=海报,3=复制链接,...按需扩展 +const fromMap = ['forward', 'poster', 'link']; + +// 设置分享信息参数 +const getShareInfo = ( + scene = { + title: '', // 自定义分享标题 + desc: '', // 自定义描述 + image: '', // 自定义分享图片 + params: {}, // 自定义分享参数 + }, + poster = { + // 自定义海报数据 + type: 'user', + }, +) => { + const shareInfo = { + title: '', // 分享标题 + desc: '', // 描述 + image: '', // 分享图片 + path: '', // 分享页面+参数 + link: '', // 分享Url+参数 + query: '', // 分享参数 + poster, // 海报所需数据 + forward: {}, // 转发所需参数 + }; + shareInfo.title = scene.title; + shareInfo.image = $url.cdn(scene.image); + shareInfo.desc = scene.desc; + + const app = $store('app'); + const shareConfig = app.platform.share; + + // 自动拼接分享用户参数 + const query = buildSpmQuery(scene.params); + shareInfo.query = query; + + // 配置分享链接地址 + shareInfo.link = buildSpmLink(query, shareConfig.linkAddress); + // 配置页面地址带参数 + shareInfo.path = buildSpmPath(); + + // 配置页面转发参数 + if (shareConfig.methods.includes('forward')) { + shareInfo.forward.path = buildSpmPath(query); + } + + return shareInfo; +}; + +/** + * 构造 spm 分享参数 + * + * @param params json 格式,其中包含:1)shareId 分享用户的编号;2)page 页面类型;3)query 页面 ID(参数);4)platform 平台类型;5)from 分享来源类型。 + * @return 分享串 `spm=${shareId}.${page}.${query}.${platform}.${from}` + */ +const buildSpmQuery = (params) => { + const user = $store('user'); + let shareId = '0'; // 设置分享者用户ID + if (typeof params.shareId === 'undefined') { + if (user.isLogin) { + shareId = user.userInfo.id; + } + } + let page = SharePageEnum.HOME.value; // 页面类型,默认首页 + if (typeof params.page !== 'undefined') { + page = params.page; + } + let query = '0'; // 设置页面ID: 如商品ID、拼团ID等 + if (typeof params.query !== 'undefined') { + query = params.query; + } + let platform = platformMap.indexOf($platform.name) + 1; + let from = '1'; + if (typeof params.from !== 'undefined') { + from = platformMap.indexOf(params.from) + 1; + } + // spmParams = ... 可按需扩展 + return `spm=${shareId}.${page}.${query}.${platform}.${from}`; +}; + +// 构造页面分享参数: 所有的分享都先到首页进行 spm 参数解析 +const buildSpmPath = (query) => { + // 默认是主页,页面 page,例如 pages/index/index,根路径前不要填加 /, + // 不能携带参数(参数请放在scene字段里),如果不填写这个字段,默认跳主页面。scancode_time为系统保留参数,不允许配置 + // 页面分享时参数使用 ? 拼接 + return typeof query === 'undefined' ? `pages/index/index` : `pages/index/index?${query}`; +}; + +// 构造分享链接 +const buildSpmLink = (query, linkAddress = '') => { + return `${linkAddress}?${query}`; +}; + +// 解析Spm +const decryptSpm = (spm) => { + const user = $store('user'); + let shareParamsArray = spm.split('.'); + let shareParams = { + spm, + shareId: 0, + page: '', + query: {}, + platform: '', + from: '', + }; + let query; + shareParams.shareId = shareParamsArray[0]; + switch (shareParamsArray[1]) { + case SharePageEnum.HOME.value: + // 默认首页不跳转 + shareParams.page = SharePageEnum.HOME.page; + break; + case SharePageEnum.GOODS.value: + // 普通商品 + shareParams.page = SharePageEnum.GOODS.page; + shareParams.query = { + id: shareParamsArray[2], // 设置活动编号 + }; + break; + case SharePageEnum.GROUPON.value: + // 拼团商品 + shareParams.page = SharePageEnum.GROUPON.page; + shareParams.query = { + id: shareParamsArray[2], // 设置活动编号 + }; + break; + case SharePageEnum.SECKILL.value: + // 秒杀商品 + shareParams.page = SharePageEnum.SECKILL.page; + shareParams.query = { + id: shareParamsArray[2], // 设置活动编号 + }; + break; + case SharePageEnum.GROUPON_DETAIL.value: + // 参与拼团 + shareParams.page = SharePageEnum.GROUPON_DETAIL.page; + shareParams.query = { + id: shareParamsArray[2], // 设置活动编号 + }; + break; + case SharePageEnum.POINT.value: + // 积分商品 + shareParams.page = SharePageEnum.POINT.page; + shareParams.query = { + id: shareParamsArray[2], // 设置活动编号 + }; + break; + } + shareParams.platform = platformMap[shareParamsArray[3] - 1]; + shareParams.from = fromMap[shareParamsArray[4] - 1]; + if (shareParams.shareId !== 0) { + // 记录分享者编号 + uni.setStorageSync('shareId', shareParams.shareId); + // 已登录 绑定推广员 + if (!!user.isLogin) { + bindBrokerageUser(shareParams.shareId); + } + } + + if (shareParams.page !== SharePageEnum.HOME.page) { + $router.go(shareParams.page, shareParams.query); + } + return shareParams; +}; + +// 绑定推广员 +const bindBrokerageUser = async (val = undefined) => { + try { + const shareId = val || uni.getStorageSync('shareId'); + if (!shareId) { + return; + } + // 绑定成功返回 true,失败返回 false + const { data } = await BrokerageApi.bindBrokerageUser({ bindUserId: shareId }); + // 绑定成功后清除缓存 + if (data) { + uni.removeStorageSync('shareId'); + } + } catch (e) { + console.error(e); + } +}; + +// 更新公众号分享sdk +const updateShareInfo = (shareInfo) => { + // #ifdef H5 + if ($platform.name === 'WechatOfficialAccount') { + $wxsdk.updateShareInfo(shareInfo); + } + // #endif +}; + +export default { + getShareInfo, + updateShareInfo, + decryptSpm, + bindBrokerageUser, +}; diff --git a/sheep/request/index.js b/sheep/request/index.js new file mode 100644 index 0000000..03eb576 --- /dev/null +++ b/sheep/request/index.js @@ -0,0 +1,304 @@ +/** + * Shopro-request + * @description api模块管理,loading配置,请求拦截,错误处理 + */ + +import Request from 'luch-request'; +import { apiPath, baseUrl, tenantId } from '@/sheep/config'; +import $store from '@/sheep/store'; +import $platform from '@/sheep/platform'; +import { showAuthModal } from '@/sheep/hooks/useModal'; +import AuthUtil from '@/sheep/api/member/auth'; +import { getTerminal } from '@/sheep/helper/const'; + +const options = { + // 显示操作成功消息 默认不显示 + showSuccess: false, + // 成功提醒 默认使用后端返回值 + successMsg: '', + // 显示失败消息 默认显示 + showError: true, + // 失败提醒 默认使用后端返回信息 + errorMsg: '', + // 显示请求时loading模态框 默认显示 + showLoading: true, + // loading提醒文字 + loadingMsg: '加载中', + // 需要授权才能请求 默认放开 + auth: false, + // ... +}; + +// Loading全局实例 +let LoadingInstance = { + target: null, + count: 0, +}; + +/** + * 关闭loading + */ +function closeLoading() { + if (LoadingInstance.count > 0) LoadingInstance.count--; + if (LoadingInstance.count === 0) uni.hideLoading(); +} + +/** + * @description 请求基础配置 可直接使用访问自定义请求 + */ +const http = new Request({ + baseURL: baseUrl + apiPath, + timeout: 8000, + method: 'GET', + header: { + Accept: 'text/json', + 'Content-Type': 'application/json;charset=UTF-8', + platform: $platform.name, + }, + // #ifdef APP-PLUS + sslVerify: false, + // #endif + // #ifdef H5 + // 跨域请求时是否携带凭证(cookies)仅H5支持(HBuilderX 2.6.15+) + withCredentials: false, + // #endif + custom: options, +}); + +/** + * @description 请求拦截器 + */ +http.interceptors.request.use( + (config) => { + // 自定义处理【auth 授权】:必须登录的接口,则跳出 AuthModal 登录弹窗 + if (config.custom.auth && !$store('user').isLogin) { + showAuthModal(); + return Promise.reject(); + } + + // 自定义处理【loading 加载中】:如果需要显示 loading,则显示 loading + if (config.custom.showLoading) { + LoadingInstance.count++; + LoadingInstance.count === 1 && + uni.showLoading({ + title: config.custom.loadingMsg, + mask: true, + fail: () => { + uni.hideLoading(); + }, + }); + } + + // 增加 token 令牌、terminal 终端、tenant 租户的请求头 + const token = getAccessToken(); + if (token) { + config.header['Authorization'] = token; + } + config.header['terminal'] = getTerminal(); + + config.header['Accept'] = '*/*'; + config.header['tenant-id'] = tenantId; + return config; + }, + (error) => { + return Promise.reject(error); + }, +); + +/** + * @description 响应拦截器 + */ +http.interceptors.response.use( + (response) => { + // 约定:如果是 /auth/ 下的 URL 地址,并且返回了 accessToken 说明是登录相关的接口,则自动设置登陆令牌 + if (response.config.url.indexOf('/member/auth/') >= 0 && response.data?.data?.accessToken) { + $store('user').setToken(response.data.data.accessToken, response.data.data.refreshToken); + } + + // 自定处理【loading 加载中】:如果需要显示 loading,则关闭 loading + response.config.custom.showLoading && closeLoading(); + + // 自定义处理【error 错误提示】:如果需要显示错误提示,则显示错误提示 + if (response.data.code !== 0) { + // 特殊:如果 401 错误码,则跳转到登录页 or 刷新令牌 + if (response.data.code === 401) { + return refreshToken(response.config); + } + // 特殊:处理分销用户绑定失败的提示 + if ((response.data.code + '').includes('1011007')) { + console.error(`分销用户绑定失败,原因:${response.data.msg}`); + } else if (response.config.custom.showError) { + // 错误提示 + uni.showToast({ + title: response.data.msg || '服务器开小差啦,请稍后再试~', + icon: 'none', + mask: true, + }); + } + } + + // 自定义处理【showSuccess 成功提示】:如果需要显示成功提示,则显示成功提示 + if ( + response.config.custom.showSuccess && + response.config.custom.successMsg !== '' && + response.data.code === 0 + ) { + uni.showToast({ + title: response.config.custom.successMsg, + icon: 'none', + }); + } + + // 返回结果:包括 code + data + msg + return Promise.resolve(response.data); + }, + (error) => { + const userStore = $store('user'); + const isLogin = userStore.isLogin; + let errorMessage = '网络请求出错'; + if (error !== undefined) { + switch (error.statusCode) { + case 400: + errorMessage = '请求错误'; + break; + case 401: + errorMessage = isLogin ? '您的登陆已过期' : '请先登录'; + // 正常情况下,后端不会返回 401 错误,所以这里不处理 handleAuthorized + break; + case 403: + errorMessage = '拒绝访问'; + break; + case 404: + errorMessage = '请求出错'; + break; + case 408: + errorMessage = '请求超时'; + break; + case 429: + errorMessage = '请求频繁, 请稍后再访问'; + break; + case 500: + errorMessage = '服务器开小差啦,请稍后再试~'; + break; + case 501: + errorMessage = '服务未实现'; + break; + case 502: + errorMessage = '网络错误'; + break; + case 503: + errorMessage = '服务不可用'; + break; + case 504: + errorMessage = '网络超时'; + break; + case 505: + errorMessage = 'HTTP 版本不受支持'; + break; + } + if (error.errMsg.includes('timeout')) errorMessage = '请求超时'; + // #ifdef H5 + if (error.errMsg.includes('Network')) + errorMessage = window.navigator.onLine ? '服务器异常' : '请检查您的网络连接'; + // #endif + } + + if (error && error.config) { + if (error.config.custom.showError) { + uni.showToast({ + title: error.data?.msg || errorMessage, + icon: 'none', + mask: true, + }); + } + error.config.custom.showLoading && closeLoading(); + } + + return false; + }, +); + +// Axios 无感知刷新令牌,参考 https://www.dashingdog.cn/article/11 与 https://segmentfault.com/a/1190000020210980 实现 +let requestList = []; // 请求队列 +let isRefreshToken = false; // 是否正在刷新中 +const refreshToken = async (config) => { + // 如果当前已经是 refresh-token 的 URL 地址,并且还是 401 错误,说明是刷新令牌失败了,直接返回 Promise.reject(error) + if (config.url.indexOf('/member/auth/refresh-token') >= 0) { + return Promise.reject('error'); + } + + // 如果未认证,并且未进行刷新令牌,说明可能是访问令牌过期了 + if (!isRefreshToken) { + isRefreshToken = true; + // 1. 如果获取不到刷新令牌,则只能执行登出操作 + const refreshToken = getRefreshToken(); + if (!refreshToken) { + return handleAuthorized(); + } + // 2. 进行刷新访问令牌 + try { + const refreshTokenResult = await AuthUtil.refreshToken(refreshToken); + if (refreshTokenResult.code !== 0) { + // 如果刷新不成功,直接抛出 e 触发 2.2 的逻辑 + // noinspection ExceptionCaughtLocallyJS + throw new Error('刷新令牌失败'); + } + // 2.1 刷新成功,则回放队列的请求 + 当前请求 + config.header.Authorization = 'Bearer ' + getAccessToken(); + requestList.forEach((cb) => { + cb(); + }); + requestList = []; + return request(config); + } catch (e) { + // 为什么需要 catch 异常呢?刷新失败时,请求因为 Promise.reject 触发异常。 + // 2.2 刷新失败,只回放队列的请求 + requestList.forEach((cb) => { + cb(); + }); + // 提示是否要登出。即不回放当前请求!不然会形成递归 + return handleAuthorized(); + } finally { + requestList = []; + isRefreshToken = false; + } + } else { + // 添加到队列,等待刷新获取到新的令牌 + return new Promise((resolve) => { + requestList.push(() => { + config.header.Authorization = 'Bearer ' + getAccessToken(); // 让每个请求携带自定义token 请根据实际情况自行修改 + resolve(request(config)); + }); + }); + } +}; + +/** + * 处理 401 未登录的错误 + */ +const handleAuthorized = () => { + const userStore = $store('user'); + userStore.logout(true); + showAuthModal(); + // 登录超时 + return Promise.reject({ + code: 401, + msg: userStore.isLogin ? '您的登陆已过期' : '请先登录', + }); +}; + +/** 获得访问令牌 */ +export const getAccessToken = () => { + return uni.getStorageSync('token'); +}; + +/** 获得刷新令牌 */ +export const getRefreshToken = () => { + return uni.getStorageSync('refresh-token'); +}; + +const request = (config) => { + return http.middleware(config); +}; + +export default request; diff --git a/sheep/router/index.js b/sheep/router/index.js new file mode 100644 index 0000000..0a4ecc3 --- /dev/null +++ b/sheep/router/index.js @@ -0,0 +1,185 @@ +import $store from '@/sheep/store'; +import { showAuthModal, showShareModal } from '@/sheep/hooks/useModal'; +import { isNumber, isString, isEmpty, startsWith, isObject, isNil, clone } from 'lodash-es'; +import throttle from '@/sheep/helper/throttle'; + +const _go = ( + path, + params = {}, + options = { + redirect: false, + }, +) => { + let page = ''; // 跳转页面 + let query = ''; // 页面参数 + let url = ''; // 跳转页面完整路径 + + if (isString(path)) { + // 判断跳转类型是 path | 还是http + if (startsWith(path, 'http')) { + // #ifdef H5 + window.location = path; + return; + // #endif + // #ifndef H5 + page = `/pages/public/webview`; + query = `url=${encodeURIComponent(path)}`; + // #endif + } else if (startsWith(path, 'action:')) { + handleAction(path); + return; + } else { + [page, query] = path.split('?'); + } + if (!isEmpty(params)) { + let query2 = paramsToQuery(params); + if (isEmpty(query)) { + query = query2; + } else { + query += '&' + query2; + } + } + } + + if (isObject(path)) { + page = path.url; + if (!isNil(path.params)) { + query = paramsToQuery(path.params); + } + } + + const nextRoute = ROUTES_MAP[page]; + + // 未找到指定跳转页面 + // mark: 跳转404页 + if (!nextRoute) { + console.log(`%c跳转路径参数错误<${page || 'EMPTY'}>`, 'color:red;background:yellow'); + return; + } + + // 页面登录拦截 + if (nextRoute.meta?.auth && !$store('user').isLogin) { + showAuthModal(); + return; + } + + url = page; + if (!isEmpty(query)) { + url += `?${query}`; + } + + // 跳转底部导航 + if (TABBAR.includes(page)) { + uni.switchTab({ + url, + }); + return; + } + + // 使用redirect跳转 + if (options.redirect) { + uni.redirectTo({ + url, + }); + return; + } + + uni.navigateTo({ + url, + }); +}; + +// 限流 防止重复点击跳转 +function go(...args) { + throttle(() => { + _go(...args); + }); +} + +function paramsToQuery(params) { + if (isEmpty(params)) { + return ''; + } + // return new URLSearchParams(Object.entries(params)).toString(); + let query = []; + for (let key in params) { + query.push(key + '=' + params[key]); + } + + return query.join('&'); +} + +function back() { + // #ifdef H5 + history.back(); + // #endif + + // #ifndef H5 + uni.navigateBack(); + // #endif +} + +function redirect(path, params = {}) { + go(path, params, { + redirect: true, + }); +} + +// 检测是否有浏览器历史 +function hasHistory() { + // #ifndef H5 + const pages = getCurrentPages(); + if (pages.length > 1) { + return true; + } + return false; + // #endif + + // #ifdef H5 + return !!history.state.back; + // #endif +} + +function getCurrentRoute(field = '') { + let currentPage = getCurrentPage(); + // #ifdef MP + currentPage.$page['route'] = currentPage.route; + currentPage.$page['options'] = currentPage.options; + // #endif + if (field !== '') { + return currentPage.$page[field]; + } else { + return currentPage.$page; + } +} + +function getCurrentPage() { + let pages = getCurrentPages(); + return pages[pages.length - 1]; +} + +function handleAction(path) { + const action = path.split(':'); + switch (action[1]) { + case 'showShareModal': + showShareModal(); + break; + } +} + +function error(errCode, errMsg = '') { + redirect('/pages/public/error', { + errCode, + errMsg, + }); +} + +export default { + go, + back, + hasHistory, + redirect, + getCurrentPage, + getCurrentRoute, + error, +}; diff --git a/sheep/router/utils/strip-json-comments.js b/sheep/router/utils/strip-json-comments.js new file mode 100644 index 0000000..5995992 --- /dev/null +++ b/sheep/router/utils/strip-json-comments.js @@ -0,0 +1,79 @@ +const singleComment = Symbol('singleComment'); +const multiComment = Symbol('multiComment'); + +const stripWithoutWhitespace = () => ''; +const stripWithWhitespace = (string, start, end) => string.slice(start, end).replace(/\S/g, ' '); + +const isEscaped = (jsonString, quotePosition) => { + let index = quotePosition - 1; + let backslashCount = 0; + + while (jsonString[index] === '\\') { + index -= 1; + backslashCount += 1; + } + + return Boolean(backslashCount % 2); +}; + +export default function stripJsonComments(jsonString, { whitespace = true } = {}) { + if (typeof jsonString !== 'string') { + throw new TypeError( + `Expected argument \`jsonString\` to be a \`string\`, got \`${typeof jsonString}\``, + ); + } + + const strip = whitespace ? stripWithWhitespace : stripWithoutWhitespace; + + let isInsideString = false; + let isInsideComment = false; + let offset = 0; + let result = ''; + + for (let index = 0; index < jsonString.length; index++) { + const currentCharacter = jsonString[index]; + const nextCharacter = jsonString[index + 1]; + + if (!isInsideComment && currentCharacter === '"') { + const escaped = isEscaped(jsonString, index); + if (!escaped) { + isInsideString = !isInsideString; + } + } + + if (isInsideString) { + continue; + } + + if (!isInsideComment && currentCharacter + nextCharacter === '//') { + result += jsonString.slice(offset, index); + offset = index; + isInsideComment = singleComment; + index++; + } else if (isInsideComment === singleComment && currentCharacter + nextCharacter === '\r\n') { + index++; + isInsideComment = false; + result += strip(jsonString, offset, index); + offset = index; + continue; + } else if (isInsideComment === singleComment && currentCharacter === '\n') { + isInsideComment = false; + result += strip(jsonString, offset, index); + offset = index; + } else if (!isInsideComment && currentCharacter + nextCharacter === '/*') { + result += jsonString.slice(offset, index); + offset = index; + isInsideComment = multiComment; + index++; + continue; + } else if (isInsideComment === multiComment && currentCharacter + nextCharacter === '*/') { + index++; + isInsideComment = false; + result += strip(jsonString, offset, index + 1); + offset = index + 1; + continue; + } + } + + return result + (isInsideComment ? strip(jsonString.slice(offset)) : jsonString.slice(offset)); +} diff --git a/sheep/router/utils/uni-read-pages-v3.js b/sheep/router/utils/uni-read-pages-v3.js new file mode 100644 index 0000000..303f10a --- /dev/null +++ b/sheep/router/utils/uni-read-pages-v3.js @@ -0,0 +1,103 @@ +'use strict'; +Object.defineProperty(exports, '__esModule', { + value: true, +}); +const fs = require('fs'); +import stripJsonComments from './strip-json-comments'; +import { isArray, isEmpty } from 'lodash'; + +class TransformPages { + constructor({ includes, pagesJsonDir }) { + this.includes = includes; + this.uniPagesJSON = JSON.parse(stripJsonComments(fs.readFileSync(pagesJsonDir, 'utf-8'))); + this.routes = this.getPagesRoutes().concat(this.getSubPackagesRoutes()); + this.tabbar = this.getTabbarRoutes(); + this.routesMap = this.transformPathToKey(this.routes); + } + /** + * 通过读取pages.json文件 生成直接可用的routes + */ + getPagesRoutes(pages = this.uniPagesJSON.pages, rootPath = null) { + let routes = []; + for (let i = 0; i < pages.length; i++) { + const item = pages[i]; + let route = {}; + for (let j = 0; j < this.includes.length; j++) { + const key = this.includes[j]; + let value = item[key]; + if (key === 'path') { + value = rootPath ? `/${rootPath}/${value}` : `/${value}`; + } + if (key === 'aliasPath' && i == 0 && rootPath == null) { + route[key] = route[key] || '/'; + } else if (value !== undefined) { + route[key] = value; + } + } + routes.push(route); + } + return routes; + } + /** + * 解析小程序分包路径 + */ + getSubPackagesRoutes() { + if (!(this.uniPagesJSON && this.uniPagesJSON.subPackages)) { + return []; + } + const subPackages = this.uniPagesJSON.subPackages; + let routes = []; + for (let i = 0; i < subPackages.length; i++) { + const subPages = subPackages[i].pages; + const root = subPackages[i].root; + const subRoutes = this.getPagesRoutes(subPages, root); + routes = routes.concat(subRoutes); + } + return routes; + } + + getTabbarRoutes() { + if (!(this.uniPagesJSON && this.uniPagesJSON.tabBar && this.uniPagesJSON.tabBar.list)) { + return []; + } + const tabbar = this.uniPagesJSON.tabBar.list; + let tabbarMap = []; + tabbar.forEach((bar) => { + tabbarMap.push('/' + bar.pagePath); + }); + return tabbarMap; + } + + transformPathToKey(list) { + if (!isArray(list) || isEmpty(list)) { + return []; + } + let map = {}; + list.forEach((i) => { + map[i.path] = i; + }); + return map; + } +} + +function uniReadPagesV3Plugin({ pagesJsonDir, includes }) { + let defaultIncludes = ['path', 'aliasPath', 'name']; + includes = [...defaultIncludes, ...includes]; + let pages = new TransformPages({ + pagesJsonDir, + includes, + }); + return { + name: 'uni-read-pages-v3', + config(config) { + return { + define: { + ROUTES: pages.routes, + ROUTES_MAP: pages.routesMap, + TABBAR: pages.tabbar, + }, + }; + }, + }; +} +exports.default = uniReadPagesV3Plugin; diff --git a/sheep/scss/_main.scss b/sheep/scss/_main.scss new file mode 100644 index 0000000..999513a --- /dev/null +++ b/sheep/scss/_main.scss @@ -0,0 +1,354 @@ +body { + color: var(--text-a); + background-color: var(--ui-BG-1) !important; + font-family: system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', + sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; +} + +/* ================== + 初始化 + ==================== */ +.ui-link { + cursor: pointer; +} +navigator { + display: inline-flex; +} +navigator.navigator-hover { + background-color: inherit; + transform: translate(1rpx, 1rpx); + // opacity: 1; +} + +/* ================== + 辅助类 + ==================== */ +.none { + display: none !important; +} +.inline { + display: inline !important; +} +.inline-block { + display: inline-block !important; +} +.block { + display: block !important; +} +.touch-none { + pointer-events: none; +} +.touch-all { + pointer-events: all; +} +.flex { + display: flex !important; +} +.inline-flex { + display: inline-flex !important; +} +.w-100 { + width: 100%; +} +/* -- 浮动 -- */ +.cf::after, +.cf::before { + content: ''; + display: table; +} +.cf::after { + clear: both; +} +.fl { + float: left; +} +.fr { + float: right; +} +.position-center { + @include position-center; +} +.position-relative { + position: relative; +} +/* -- 工具类 -- */ +@function negativify-map($map) { + $result: (); + @each $key, $value in $map { + @if $key != 0 { + $result: map-merge($result, ('n' + $key: (-$value))); + } + } + @return $result; +} + +$utilities: () !default; +$utilities: map-merge( + ( + 'margin': ( + responsive: true, + property: margin, + class: m, + values: + map-merge( + $spacers, + ( + auto: auto, + ) + ), + ), + 'margin-x': ( + property: margin-right margin-left, + class: mx, + values: + map-merge( + $spacers, + ( + auto: auto, + ) + ), + ), + 'margin-y': ( + property: margin-top margin-bottom, + class: my, + values: + map-merge( + $spacers, + ( + auto: auto, + ) + ), + ), + 'margin-top': ( + property: margin-top, + class: mt, + values: + map-merge( + $spacers, + ( + auto: auto, + ) + ), + ), + 'margin-right': ( + property: margin-right, + class: mr, + values: + map-merge( + $spacers, + ( + auto: auto, + ) + ), + ), + 'margin-bottom': ( + property: margin-bottom, + class: mb, + values: + map-merge( + $spacers, + ( + auto: auto, + ) + ), + ), + 'margin-left': ( + property: margin-left, + class: ml, + values: + map-merge( + $spacers, + ( + auto: auto, + ) + ), + ), + 'padding': ( + responsive: true, + property: padding, + class: p, + values: $spacers, + ), + 'padding-x': ( + property: padding-right padding-left, + class: px, + values: $spacers, + ), + 'padding-y': ( + property: padding-top padding-bottom, + class: py, + values: $spacers, + ), + 'padding-top': ( + property: padding-top, + class: pt, + values: $spacers, + ), + 'padding-right': ( + property: padding-right, + class: pr, + values: $spacers, + ), + 'padding-bottom': ( + property: padding-bottom, + class: pb, + values: $spacers, + ), + 'padding-left': ( + property: padding-left, + class: pl, + values: $spacers, + ), + 'font-weight': ( + property: font-weight, + class: text, + values: ( + light: $font-weight-light, + lighter: $font-weight-lighter, + normal: $font-weight-normal, + bold: $font-weight-bold, + bolder: $font-weight-bolder, + ), + ), + 'text-align': ( + property: text-align, + class: text, + values: left right center, + ), + 'font-color': ( + property: color, + class: text, + values: + map-merge( + $colors, + map-merge( + $grays, + map-merge( + $darks, + ( + 'reset': inherit, + ) + ) + ) + ), + ), + 'line-height': ( + property: line-height, + class: lh, + values: ( + 1: 1, + sm: $line-height-sm, + base: $line-height-base, + lg: $line-height-lg, + ), + ), + 'white-space': ( + property: white-space, + class: text, + values: ( + nowrap: nowrap, + ), + ), + 'radius': ( + property: border-radius, + class: radius, + values: ( + null: $radius, + sm: $radius-sm, + lg: $radius-lg, + 0: 0, + ), + ), + 'round': ( + property: border-radius, + class: round, + values: ( + null: $round-pill, + circle: 50%, + ), + ), + 'radius-top': ( + property: border-top-left-radius border-top-right-radius, + class: radius-top, + values: ( + null: $radius, + ), + ), + 'radius-right': ( + property: border-top-right-radius border-bottom-right-radius, + class: radius-right, + values: ( + null: $radius, + ), + ), + 'radius-bottom': ( + property: border-bottom-right-radius border-bottom-left-radius, + class: radius-bottom, + values: ( + null: $radius, + ), + ), + 'radius-left': ( + property: border-bottom-left-radius border-top-left-radius, + class: radius-left, + values: ( + null: $radius, + ), + ), + 'radius-lr': ( + property: border-top-left-radius border-bottom-right-radius, + class: radius-lr, + values: ( + null: $radius, + ), + ), + 'radius-lrs': ( + property: border-top-right-radius border-bottom-left-radius, + class: radius-lr, + values: ( + null: 0, + ), + ), + 'radius-rl': ( + property: border-top-right-radius border-bottom-left-radius, + class: radius-rl, + values: ( + null: $radius, + ), + ), + 'radius-rls': ( + property: border-top-left-radius border-bottom-right-radius, + class: radius-rl, + values: ( + null: 0, + ), + ), + ), + $utilities +); +@each $key, $utility in $utilities { + @if type-of($utility) == 'map' { + $values: map-get($utility, values); + @if type-of($values) == 'string' or type-of(nth($values, 1)) != 'list' { + $values: zip($values, $values); + } + @each $key, $value in $values { + $properties: map-get($utility, property); + @if type-of($properties) == 'string' { + $properties: append((), $properties); + } + $property-class: if( + map-has-key($utility, class), + map-get($utility, class), + nth($properties, 1) + ); + $property-class: if($property-class == null, '', $property-class); + $property-class-modifier: if($key, if($property-class == '', '', '-') + $key, ''); + .#{$property-class + $property-class-modifier} { + @each $property in $properties { + #{$property}: $value !important; + } + } + } + } +} diff --git a/sheep/scss/_mixins.scss b/sheep/scss/_mixins.scss new file mode 100644 index 0000000..299f7b1 --- /dev/null +++ b/sheep/scss/_mixins.scss @@ -0,0 +1,61 @@ +@mixin bg-square { + background: { + color: #fff; + image: linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%), + linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%); + size: 40rpx 40rpx; + position: 0 0, 20rpx 20rpx; + } +} + +@mixin flex($direction: row) { + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + flex-direction: $direction; +} +@mixin flex-bar { + position: relative; + display: flex; + align-items: center; + justify-content: space-between; +} +@mixin flex-center { + display: flex; + align-items: center; + justify-content: center; +} + +@mixin arrow { + content: ''; + height: 0; + width: 0; + position: absolute; +} +@mixin arrow-top { + @include arrow; + // border-color: transparent transparent $ui-BG; + border-style: none solid solid; + border-width: 0 20rpx 20rpx; +} + +@mixin arrow-right { + @include arrow; + // border-color: transparent $ui-BG transparent; + border-style: solid solid solid none; + border-width: 20rpx 20rpx 20rpx 0; +} +@mixin position-center { + position: absolute !important; + top: 0; + right: 0; + bottom: 0; + left: 0; + margin: auto; +} + +@mixin blur { + -webkit-backdrop-filter: blur(20px); + backdrop-filter: blur(20px); + color: var(--ui-TC); +} diff --git a/sheep/scss/_tools.scss b/sheep/scss/_tools.scss new file mode 100644 index 0000000..e1fb636 --- /dev/null +++ b/sheep/scss/_tools.scss @@ -0,0 +1,286 @@ +/* ================== + 常用工具 + ==================== */ + +.ss-bg-opactity-block { + background-color: rgba(#000, 0.2); + color: #fff; +} + +/* ================== + flex布局 + ==================== */ + +.ss-flex { + display: flex; + flex-direction: row; + align-items: center; +} + +.ss-flex-1 { + flex: 1; +} + +.ss-flex-col { + display: flex; + flex-direction: column; +} + +.ss-flex-wrap { + flex-wrap: wrap; +} + +.ss-flex-nowrap { + flex-wrap: nowrap; +} + +.ss-col-center { + align-items: center; +} + +.ss-col-top { + align-items: flex-start; +} + +.ss-col-bottom { + align-items: flex-end; +} + +.ss-col-stretch { + align-items: stretch; +} + +.ss-row-center { + justify-content: center; +} + +.ss-row-left { + justify-content: flex-start; +} + +.ss-row-right { + justify-content: flex-end; +} + +.ss-row-between { + justify-content: space-between; +} + +.ss-row-around { + justify-content: space-around; +} + +.ss-self-start { + align-self: flex-start; +} + +.ss-self-end { + align-self: flex-end; +} + +.ss-self-center { + align-self: center; +} +.ss-h-100 { + height: 100%; +} +.ss-w-100 { + width: 100%; +} + +/* ================== + + margin padding: 内外边距 + + ==================== */ +@for $i from 0 through 100 { + // 只要双数和能被5除尽的数 + @if $i % 2==0 or $i % 5==0 { + // 得出:u-margin-30或者u-m-30 + .ss-margin-#{$i}, + .ss-m-#{$i} { + margin: $i + rpx; + } + .ss-m-x-#{$i} { + margin-left: $i + rpx; + margin-right: $i + rpx; + } + .ss-m-y-#{$i} { + margin-top: $i + rpx; + margin-bottom: $i + rpx; + } + + // 得出:u-padding-30或者u-p-30 + .ss-padding-#{$i}, + .ss-p-#{$i} { + padding: $i + rpx; + } + .ss-p-x-#{$i} { + padding-left: $i + rpx; + padding-right: $i + rpx; + } + .ss-p-y-#{$i} { + padding-top: $i + rpx; + padding-bottom: $i + rpx; + } + + @each $short, $long in l left, t top, r right, b bottom { + // 缩写版,结果如: u-m-l-30 + // 定义外边距 + .ss-m-#{$short}-#{$i} { + margin-#{$long}: $i + rpx; + } + + // 定义内边距 + .ss-p-#{$short}-#{$i} { + padding-#{$long}: $i + rpx; + } + + // 完整版,结果如:u-margin-left-30 + // 定义外边距 + .ss-margin-#{$long}-#{$i} { + margin-#{$long}: $i + rpx; + } + + // 定义内边距 + .ss-padding-#{$long}-#{$i} { + padding-#{$long}: $i + rpx; + } + } + } +} + +/* ================== + + radius + + ==================== */ +@for $i from 0 through 100 { + // 只要双数和能被5除尽的数 + @if $i % 2==0 or $i % 5==0 { + .ss-radius-#{$i}, + .ss-r-#{$i} { + border-radius: $i + rpx; + } + + .ss-r-t-#{$i} { + border-top-left-radius: $i + rpx; + border-top-right-radius: $i + rpx; + } + + .ss-r-b-#{$i} { + border-bottom-left-radius: $i + rpx; + border-bottom-right-radius: $i + rpx; + } + + @each $short, $long in tl 'top-left', tr 'top-right', bl 'bottom-right', br 'bottom-right' { + // 定义外边距 + .ss-r-#{$short}-#{$i} { + border-#{$long}-radius: $i + rpx; + } + + // 定义内边距 + .ss-radius-#{$long}-#{$i} { + border-#{$long}-radius: $i + rpx; + } + } + } +} + +/* ================== + + 溢出省略号 + @param {Number} 行数 + + ==================== */ +@mixin ellipsis($rowCount: 1) { + // @if $rowCount <=1 { + // overflow: hidden; + // text-overflow: ellipsis; + // white-space: nowrap; + // } @else { + // min-width: 0; + // overflow: hidden; + // text-overflow: ellipsis; + // display: -webkit-box; + // -webkit-line-clamp: $rowCount; + // -webkit-box-orient: vertical; + // } + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-line-clamp: $rowCount; + -webkit-box-orient: vertical; +} + +@for $i from 1 through 6 { + .ss-line-#{$i} { + @include ellipsis($i); + } +} + +/* ================== + hover + ==================== */ +.ss-hover-class { + background-color: $gray-c; + opacity: 0.6; +} +.ss-hover-btn { + transform: translate(1px, 1px); +} + +/* ================== + 底部安全区域 + ==================== */ + +.ss-safe-bottom { + padding-bottom: 0; + padding-bottom: calc(constant(safe-area-inset-bottom) / 5 * 3); + padding-bottom: calc(env(safe-area-inset-bottom) / 5 * 3); +} + +/* ================== + + 字体大小 + + ==================== */ + +@for $i from 20 through 50 { + .ss-font-#{$i} { + font-size: $i + rpx; + } +} + +/* ================== + 按钮 + ==================== */ +.ss-reset-button { + padding: 0; + margin: 0; + font-size: inherit; + background-color: transparent; + color: inherit; + position: relative; + border: 0rpx; + /* #ifndef APP-NVUE */ + display: flex; + /* #endif */ + align-items: center; + justify-content: center; + box-sizing: border-box; + text-align: center; + text-decoration: none; + white-space: nowrap; + vertical-align: baseline; + transform: translate(0, 0); +} +.ss-reset-button.button-hover { + transform: translate(1px, 1px); + background: none; +} + +.ss-reset-button::after { + border: none; +} diff --git a/sheep/scss/_var.scss b/sheep/scss/_var.scss new file mode 100644 index 0000000..28d7b82 --- /dev/null +++ b/sheep/scss/_var.scss @@ -0,0 +1,163 @@ +@import './mixins'; + +//颜色 ,渐变背景60% +$red: #d10019; // 中国红 +$orange: #f37b1d; // 桔橙 +$gold: #fbbd08; // 明黄 +$green: #8dc63f; // 橄榄绿 +$cyan: #1cbbb4; // 天青 +$blue: #0081ff; // 海蓝 +$purple: #6739b6; // 姹紫 +$brightRed: #e54d42; // 嫣红 +$forestGreen: #39b54a; // 森绿 +$mauve: #9c26b0; // 木槿 +$pink: #e03997; // 桃粉 +$brown: #a5673f; // 棕褐 +$grey: #8799a3; // 玄灰 +$gray: #aaaaaa; // 草灰 +$black: #333333; // 墨黑 + +$colors: (); +$colors: map-merge( + ( + 'red':$red, + 'orange':$orange, + 'gold':$gold, + 'green':$green, + 'cyan':$cyan, + 'blue':$blue, + 'purple':$purple, + 'brightRed':$brightRed, + 'forestGreen':$forestGreen, + 'mauve':$mauve, + 'pink':$pink, + 'brown':$brown, + 'grey':$grey, + 'gray':$gray, + 'black':$black, + ), + $colors +); + +//灰度 +$bg-page: #f6f6f6; +$white: #ffffff; +$gray-f: #f8f9fa; +$gray-e: #eeeeee; +$gray-d: #dddddd; +$gray-c: #cccccc; +$gray-b: #bbbbbb; +$gray-a: #aaaaaa; +$dark-9: #999999; +$dark-8: #888888; +$dark-7: #777777; +$dark-6: #666666; +$dark-5: #555555; +$dark-4: #484848; //ss-黑 +$dark-3: #333333; +$dark-2: #222222; +$dark-1: #111111; +$black: #000000; + +$grays: (); +$grays: map-merge( + ( + 'white': $white, + 'gray-f': $gray-f, + 'gray-e': $gray-e, + 'gray-d': $gray-d, + 'gray-c': $gray-c, + 'gray-b': $gray-b, + 'gray-a': $gray-a, + 'gray': $gray-a, + ), + $grays +); + +$darks: (); +$darks: map-merge( + ( + 'dark-9': $dark-9, + 'dark-8': $dark-8, + 'dark-7': $dark-7, + 'dark-6': $dark-6, + 'dark-5': $dark-5, + 'dark-4': $dark-4, + 'dark-3': $dark-3, + 'dark-2': $dark-2, + 'dark-1': $dark-1, + 'black': $black, + ), + $darks +); + +// 边框 +$border-width: 1rpx !default; // 边框大小 +$border-color: $gray-d !default; // 边框颜色 + +// 圆角 +$radius: 10rpx !default; // 默认圆角大小 +$radius-lg: 40rpx !default; // 大圆角 +$radius-sm: 6rpx !default; // 小圆角 +$round-pill: 1000rpx !default; // 半圆 + +// 动画过渡 +$transition-base: all 0.2s ease-in-out !default; // 默认过渡 +$transition-base-out: all 0.04s ease-in-out !default; // 进场过渡 +$transition-fade: opacity 0.15s linear !default; // 透明过渡 +$transition-collapse: height 0.35s ease !default; // 收缩过渡 + +// 间距 +$spacer: 20rpx !default; +$spacers: () !default; +$spacers: map-merge( + ( + 0: 0, + 1: $spacer * 0.25, + 2: $spacer * 0.5, + 3: $spacer, + 4: $spacer * 1.5, + 5: $spacer * 3, + 6: $spacer * 5, + ), + $spacers +); +// 字形 +$font-weight-lighter: lighter !default; +$font-weight-light: 300 !default; +$font-weight-normal: 400 !default; +$font-weight-bold: 700 !default; +$font-weight-bolder: 900 !default; +$fontsize: () !default; +$fontsize: map-merge( + ( + xs: 20, + sm: 24, + df: 28, + lg: 32, + xl: 36, + xxl: 44, + sl: 80, + xsl: 120, + ), + $fontsize +); +// 段落 +$line-height-base: 1.5 !default; +$line-height-lg: 2 !default; +$line-height-sm: 1.25 !default; +// 图标 +$iconsize: () !default; +$iconsize: map-merge( + ( + xs: 0.5, + sm: 0.75, + df: 1, + lg: 1.25, + xl: 1.5, + xxl: 2, + sl: 6, + xsl: 10, + ), + $iconsize +); diff --git a/sheep/scss/font/OPPOSANS-M-subfont.ttf b/sheep/scss/font/OPPOSANS-M-subfont.ttf new file mode 100644 index 0000000000000000000000000000000000000000..88ff8353fd5fb53cf1a4d7055ba7e09a12a94514 GIT binary patch literal 9832 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 literal 0 HcmV?d00001 diff --git a/sheep/scss/icon/_coloricon.scss b/sheep/scss/icon/_coloricon.scss new file mode 100644 index 0000000..f391ca4 --- /dev/null +++ b/sheep/scss/icon/_coloricon.scss @@ -0,0 +1,1340 @@ +@font-face { + font-family: 'coloricon'; + src: url('data:application/x-font-woff2;charset=utf-8;base64,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') + format('woff2'); + /* #ifdef MP-ALIPAY */ + src: url('//at.alicdn.com/t/font_1656945_d66u4pxvlq6.woff2') format('woff2'), + url('//at.alicdn.com/t/font_1656945_d66u4pxvlq6.woff') format('woff'), + url('//at.alicdn.com/t/font_1656945_d66u4pxvlq6.ttf') format('truetype'); + /* #endif */ + font-weight: normal; + font-style: normal; +} +[class*='cicon-'] { + font-family: 'coloricon'; + display: inline-block; +} + +.cicon-Aa:before { + content: '\e7a1'; +} +.cicon-accounts:before { + content: '\e681'; +} +.cicon-accounts-o:before { + content: '\e686'; +} +.cicon-add:before { + content: '\e6e4'; +} +.cicon-add-round:before { + content: '\e717'; +} +.cicon-add-round-o:before { + content: '\e718'; +} +.cicon-alarm:before { + content: '\e61e'; +} +.cicon-album:before { + content: '\e759'; +} +.cicon-alipay:before { + content: '\e6e1'; +} +.cicon-android:before { + content: '\e6e2'; +} +.cicon-angle:before { + content: '\e605'; +} +.cicon-apple:before { + content: '\e8e7'; +} +.cicon-apps:before { + content: '\e737'; +} +.cicon-archive:before { + content: '\e7ae'; +} +.cicon-archive-o:before { + content: '\e7ad'; +} +.cicon-arrow:before { + content: '\e608'; +} +.cicon-at-line:before { + content: '\e75c'; +} +.cicon-avatar:before { + content: '\e663'; +} +.cicon-avatar-o:before { + content: '\e665'; +} +.cicon-avatars:before { + content: '\e67e'; +} +.cicon-avatars-o:before { + content: '\e680'; +} +.cicon-back:before { + content: '\e600'; +} +.cicon-backspace:before { + content: '\e6a9'; +} +.cicon-backup:before { + content: '\e61f'; +} +.cicon-backup-restore:before { + content: '\e62d'; +} +.cicon-barcode:before { + content: '\e71f'; +} +.cicon-book:before { + content: '\e6a2'; +} +.cicon-bookmark:before { + content: '\e6a3'; +} +.cicon-bookmark-o:before { + content: '\e697'; +} +.cicon-bookmarks:before { + content: '\e6a6'; +} +.cicon-box:before { + content: '\e714'; +} +.cicon-box-block:before { + content: '\e6ac'; +} +.cicon-box-right:before { + content: '\e6a0'; +} +.cicon-brand:before { + content: '\e726'; +} +.cicon-brand-o:before { + content: '\e727'; +} +.cicon-building:before { + content: '\e6c3'; +} +.cicon-building-o:before { + content: '\e6c7'; +} +.cicon-camera:before { + content: '\e6fa'; +} +.cicon-camera-add:before { + content: '\e736'; +} +.cicon-camera-add-o:before { + content: '\e735'; +} +.cicon-camera-lens:before { + content: '\e68f'; +} +.cicon-camera-lens-o:before { + content: '\e68e'; +} +.cicon-camera-o:before { + content: '\e6fb'; +} +.cicon-camera-rotate:before { + content: '\e71e'; +} +.cicon-card:before { + content: '\e744'; +} +.cicon-cardboard:before { + content: '\e7a9'; +} +.cicon-cardboard-o:before { + content: '\e7aa'; +} +.cicon-cardboard-off-o:before { + content: '\e7af'; +} +.cicon-cart:before { + content: '\e70b'; +} +.cicon-cart-o:before { + content: '\e708'; +} +.cicon-chat:before { + content: '\e739'; +} +.cicon-chat-bubble:before { + content: '\e69b'; +} +.cicon-chat-bubble-o:before { + content: '\e6a7'; +} +.cicon-chat-list:before { + content: '\e69d'; +} +.cicon-chat-list-o:before { + content: '\e6aa'; +} +.cicon-chat-o:before { + content: '\e73c'; +} +.cicon-chat-smile:before { + content: '\e779'; +} +.cicon-chat-smile-o:before { + content: '\e78e'; +} +.cicon-chat-smiles:before { + content: '\e76b'; +} +.cicon-chat-smiles-o:before { + content: '\e74a'; +} +.cicon-check:before { + content: '\e69f'; +} +.cicon-checkbox:before { + content: '\e713'; +} +.cicon-checkbox-o:before { + content: '\e715'; +} +.cicon-check-round:before { + content: '\e6f1'; +} +.cicon-check-round-o:before { + content: '\e6f2'; +} +.cicon-choiceness:before { + content: '\e728'; +} +.cicon-choiceness-o:before { + content: '\e729'; +} +.cicon-chrome:before { + content: '\e6e3'; +} +.cicon-circle:before { + content: '\e7b0'; +} +.cicon-circle-o:before { + content: '\e7b1'; +} +.cicon-close:before { + content: '\e6ed'; +} +.cicon-close-round:before { + content: '\e6f3'; +} +.cicon-close-round-o:before { + content: '\e6f4'; +} +.cicon-clothes:before { + content: '\e72a'; +} +.cicon-clothes-o:before { + content: '\e72b'; +} +.cicon-cloud:before { + content: '\e64e'; +} +.cicon-cloud-done:before { + content: '\e641'; +} +.cicon-cloud-download:before { + content: '\e647'; +} +.cicon-cloud-o:before { + content: '\e646'; +} +.cicon-cloud-off:before { + content: '\e64b'; +} +.cicon-cloud-upload:before { + content: '\e687'; +} +.cicon-code-box:before { + content: '\e7c3'; +} +.cicon-coin:before { + content: '\e78a'; +} +.cicon-coin-o:before { + content: '\e79d'; +} +.cicon-comment:before { + content: '\e738'; +} +.cicon-comment-o:before { + content: '\e70e'; +} +.cicon-community:before { + content: '\e742'; +} +.cicon-community-o:before { + content: '\e743'; +} +.cicon-countdown:before { + content: '\e722'; +} +.cicon-countdown-o:before { + content: '\e723'; +} +.cicon-creative:before { + content: '\e72c'; +} +.cicon-creative-o:before { + content: '\e72d'; +} +.cicon-crop:before { + content: '\e6d9'; +} +.cicon-crown:before { + content: '\e776'; +} +.cicon-crown-o:before { + content: '\e777'; +} +.cicon-cut:before { + content: '\e74b'; +} +.cicon-DarkMode:before { + content: '\e7c4'; +} +.cicon-dashboard:before { + content: '\e62e'; +} +.cicon-delete:before { + content: '\e6bd'; +} +.cicon-delete-close:before { + content: '\e6ae'; +} +.cicon-delete-line:before { + content: '\e707'; +} +.cicon-delete-line-o:before { + content: '\e709'; +} +.cicon-delete-o:before { + content: '\e69a'; +} +.cicon-deliver:before { + content: '\e7f7'; +} +.cicon-deliver-o:before { + content: '\e6ff'; +} +.cicon-demo:before { + content: '\e916'; +} +.cicon-discover:before { + content: '\e70c'; +} +.cicon-discover-o:before { + content: '\e702'; +} +.cicon-discuss-fill:before { + content: '\e790'; +} +.cicon-discuss-line:before { + content: '\e78f'; +} +.cicon-dollar:before { + content: '\e79f'; +} +.cicon-dollar-o:before { + content: '\e79e'; +} +.cicon-done:before { + content: '\e633'; +} +.cicon-done-all:before { + content: '\e62a'; +} +.cicon-douyin:before { + content: '\e6e7'; +} +.cicon-drop-down:before { + content: '\e61c'; +} +.cicon-drop-up:before { + content: '\e61d'; +} +.cicon-eject:before { + content: '\e63a'; +} +.cicon-ellipse:before { + content: '\e74c'; +} +.cicon-emoji:before { + content: '\e78d'; +} +.cicon-emoji-o:before { + content: '\e6ee'; +} +.cicon-equalizer:before { + content: '\e802'; +} +.cicon-eraser:before { + content: '\e770'; +} +.cicon-eraser-o:before { + content: '\e772'; +} +.cicon-evaluate:before { + content: '\e7f0'; +} +.cicon-evaluate-o:before { + content: '\e700'; +} +.cicon-event-close:before { + content: '\e6a5'; +} +.cicon-event-done:before { + content: '\e6b2'; +} +.cicon-event-list:before { + content: '\e6b8'; +} +.cicon-explore:before { + content: '\e628'; +} +.cicon-explore-line:before { + content: '\e719'; +} +.cicon-explore-line-o:before { + content: '\e710'; +} +.cicon-explore-o:before { + content: '\e626'; +} +.cicon-extension:before { + content: '\e620'; +} +.cicon-extension-o:before { + content: '\e63f'; +} +.cicon-eye:before { + content: '\e740'; +} +.cicon-eye-favor:before { + content: '\e7b4'; +} +.cicon-eye-favor-o:before { + content: '\e7b5'; +} +.cicon-eye-o:before { + content: '\e741'; +} +.cicon-eye-off:before { + content: '\e7b3'; +} +.cicon-eye-off-o:before { + content: '\e7b2'; +} +.cicon-facebook:before { + content: '\e6ea'; +} +.cicon-favorite:before { + content: '\e623'; +} +.cicon-favorite-o:before { + content: '\e621'; +} +.cicon-female:before { + content: '\e72f'; +} +.cicon-file:before { + content: '\e857'; +} +.cicon-file-copy:before { + content: '\e85c'; +} +.cicon-file-copy-o:before { + content: '\e7bc'; +} +.cicon-file-o:before { + content: '\e7bb'; +} +.cicon-file-text:before { + content: '\e858'; +} +.cicon-file-text-o:before { + content: '\e7b9'; +} +.cicon-filter:before { + content: '\e6ec'; +} +.cicon-fingerprint:before { + content: '\e63b'; +} +.cicon-first-page:before { + content: '\e60c'; +} +.cicon-flag:before { + content: '\e64d'; +} +.cicon-flag-o:before { + content: '\e64c'; +} +.cicon-flash-close:before { + content: '\e73b'; +} +.cicon-flash-off:before { + content: '\e6d5'; +} +.cicon-flash-on:before { + content: '\e6dc'; +} +.cicon-flash-open:before { + content: '\e74f'; +} +.cicon-folder:before { + content: '\e6a1'; +} +.cicon-folder-add:before { + content: '\e6b4'; +} +.cicon-folder-o:before { + content: '\e6b0'; +} +.cicon-folder-special:before { + content: '\e65c'; +} +.cicon-forward:before { + content: '\e601'; +} +.cicon-fullscreen:before { + content: '\e915'; +} +.cicon-fullscreen-exit:before { + content: '\e914'; +} +.cicon-game:before { + content: '\e6c0'; +} +.cicon-game-o:before { + content: '\e6d1'; +} +.cicon-git-commit:before { + content: '\e7be'; +} +.cicon-git-commit-o:before { + content: '\e7bd'; +} +.cicon-github:before { + content: '\e6e9'; +} +.cicon-github-circle:before { + content: '\ead8'; +} +.cicon-goods:before { + content: '\e778'; +} +.cicon-goodsnew:before { + content: '\e7bf'; +} +.cicon-goodsnew-o:before { + content: '\e7c0'; +} +.cicon-goods-o:before { + content: '\e70f'; +} +.cicon-GooglePlaylogo:before { + content: '\e6e5'; +} +.cicon-grid:before { + content: '\e6ce'; +} +.cicon-grid-o:before { + content: '\e6cc'; +} +.cicon-group:before { + content: '\e7f5'; +} +.cicon-group-o:before { + content: '\e753'; +} +.cicon-guanli:before { + content: '\e750'; +} +.cicon-headset:before { + content: '\e6a4'; +} +.cicon-headset-mic:before { + content: '\e6b1'; +} +.cicon-help:before { + content: '\e66b'; +} +.cicon-help-o:before { + content: '\e65e'; +} +.cicon-home:before { + content: '\e70d'; +} +.cicon-home-2:before { + content: '\e6fd'; +} +.cicon-home-2-o:before { + content: '\e6cf'; +} +.cicon-home-3:before { + content: '\e6fc'; +} +.cicon-home-3-o:before { + content: '\e6e0'; +} +.cicon-home-4:before { + content: '\e732'; +} +.cicon-home-4-o:before { + content: '\e6e6'; +} +.cicon-home-community:before { + content: '\e799'; +} +.cicon-home-dot:before { + content: '\e794'; +} +.cicon-home-dot-o:before { + content: '\e797'; +} +.cicon-home-line:before { + content: '\e793'; +} +.cicon-home-line-o:before { + content: '\e792'; +} +.cicon-home-o:before { + content: '\e70a'; +} +.cicon-home-sm:before { + content: '\e798'; +} +.cicon-home-smile:before { + content: '\e79c'; +} +.cicon-home-smile-o:before { + content: '\e7a0'; +} +.cicon-home-smline:before { + content: '\e791'; +} +.cicon-home-smline-o:before { + content: '\e731'; +} +.cicon-home-sm-o:before { + content: '\e79b'; +} +.cicon-hotel:before { + content: '\e7a8'; +} +.cicon-hotel-o:before { + content: '\e7a3'; +} +.cicon-huohu:before { + content: '\e72e'; +} +.cicon-IE:before { + content: '\e922'; +} +.cicon-image-text:before { + content: '\e781'; +} +.cicon-image-text-o:before { + content: '\e758'; +} +.cicon-import-export:before { + content: '\e615'; +} +.cicon-info:before { + content: '\e6ef'; +} +.cicon-info-o:before { + content: '\e705'; +} +.cicon-input:before { + content: '\e75f'; +} +.cicon-input-o:before { + content: '\e6c8'; +} +.cicon-keyboard:before { + content: '\e6b6'; +} +.cicon-kinds:before { + content: '\e748'; +} +.cicon-last-page:before { + content: '\e60d'; +} +.cicon-layout:before { + content: '\e7e8'; +} +.cicon-layout-o:before { + content: '\e7e7'; +} +.cicon-LightMode:before { + content: '\e7ba'; +} +.cicon-link:before { + content: '\e6ab'; +} +.cicon-link-off:before { + content: '\e6b9'; +} +.cicon-loader-fill:before { + content: '\e76d'; +} +.cicon-loading:before { + content: '\e746'; +} +.cicon-loading1:before { + content: '\e749'; +} +.cicon-loading2:before { + content: '\e7f1'; +} +.cicon-location-off:before { + content: '\e671'; +} +.cicon-location-off-o:before { + content: '\e66d'; +} +.cicon-location-on:before { + content: '\e65f'; +} +.cicon-location-on-o:before { + content: '\e661'; +} +.cicon-lock:before { + content: '\e6ad'; +} +.cicon-lock-o:before { + content: '\e6b3'; +} +.cicon-lock-open:before { + content: '\e6ba'; +} +.cicon-logout:before { + content: '\e76e'; +} +.cicon-loop:before { + content: '\e616'; +} +.cicon-magic:before { + content: '\e6b7'; +} +.cicon-magic-o:before { + content: '\e6c2'; +} +.cicon-mail:before { + content: '\e6be'; +} +.cicon-mail-o:before { + content: '\e6bc'; +} +.cicon-male:before { + content: '\e730'; +} +.cicon-mic:before { + content: '\e656'; +} +.cicon-mic-none:before { + content: '\e642'; +} +.cicon-mic-off:before { + content: '\e652'; +} +.cicon-miniprogram:before { + content: '\e7d6'; +} +.cicon-mobile:before { + content: '\e854'; +} +.cicon-mobile-o:before { + content: '\e7b6'; +} +.cicon-moneybag:before { + content: '\e7ce'; +} +.cicon-moneybag-o:before { + content: '\e7d1'; +} +.cicon-more:before { + content: '\e688'; +} +.cicon-more-tag:before { + content: '\e672'; +} +.cicon-move:before { + content: '\e768'; +} +.cicon-move-round:before { + content: '\e602'; +} +.cicon-move-round-o:before { + content: '\e603'; +} +.cicon-music:before { + content: '\e795'; +} +.cicon-music-off:before { + content: '\e796'; +} +.cicon-my:before { + content: '\e78c'; +} +.cicon-my-o:before { + content: '\e78b'; +} +.cicon-near-me:before { + content: '\e654'; +} +.cicon-near-me-o:before { + content: '\e649'; +} +.cicon-not:before { + content: '\e667'; +} +.cicon-notice:before { + content: '\e666'; +} +.cicon-notice-active:before { + content: '\e66f'; +} +.cicon-notice-active-o:before { + content: '\e65d'; +} +.cicon-notice-o:before { + content: '\e664'; +} +.cicon-notice-off:before { + content: '\e6b5'; +} +.cicon-notice-off-o:before { + content: '\e6bb'; +} +.cicon-numcode:before { + content: '\e755'; +} +.cicon-order:before { + content: '\e786'; +} +.cicon-order-o:before { + content: '\e7b8'; +} +.cicon-paint:before { + content: '\e75d'; +} +.cicon-paint-o:before { + content: '\e75a'; +} +.cicon-palette:before { + content: '\e696'; +} +.cicon-palette-o:before { + content: '\e691'; +} +.cicon-pause:before { + content: '\e669'; +} +.cicon-pause-circle:before { + content: '\e678'; +} +.cicon-person:before { + content: '\e679'; +} +.cicon-person-add:before { + content: '\e668'; +} +.cicon-person-add-o:before { + content: '\e66a'; +} +.cicon-person-o:before { + content: '\e67d'; +} +.cicon-person-pin-circle:before { + content: '\e66c'; +} +.cicon-person-pin-circle-o:before { + content: '\e670'; +} +.cicon-phone:before { + content: '\e6f0'; +} +.cicon-phone-call:before { + content: '\e6d7'; +} +.cicon-pic:before { + content: '\e756'; +} +.cicon-pic-o:before { + content: '\e69e'; +} +.cicon-pin-drop:before { + content: '\e648'; +} +.cicon-pin-drop-o:before { + content: '\e655'; +} +.cicon-place:before { + content: '\e651'; +} +.cicon-place-o:before { + content: '\e650'; +} +.cicon-play-arrow:before { + content: '\e66e'; +} +.cicon-play-circle:before { + content: '\e674'; +} +.cicon-play-circle-o:before { + content: '\e67f'; +} +.cicon-popover:before { + content: '\e74e'; +} +.cicon-popover-o:before { + content: '\e757'; +} +.cicon-present:before { + content: '\e73a'; +} +.cicon-present-o:before { + content: '\e711'; +} +.cicon-progress:before { + content: '\e784'; +} +.cicon-qq:before { + content: '\e7d9'; +} +.cicon-qr-code-fill:before { + content: '\e767'; +} +.cicon-qr-code-line:before { + content: '\e75e'; +} +.cicon-quill:before { + content: '\e760'; +} +.cicon-quill-o:before { + content: '\e761'; +} +.cicon-radio:before { + content: '\e6d4'; +} +.cicon-radiobox:before { + content: '\e763'; +} +.cicon-radiobox-o:before { + content: '\e75b'; +} +.cicon-recharge:before { + content: '\e71c'; +} +.cicon-recharge-o:before { + content: '\e71d'; +} +.cicon-record:before { + content: '\e7a4'; +} +.cicon-record-o:before { + content: '\e7a6'; +} +.cicon-redo:before { + content: '\e612'; +} +.cicon-redpacket:before { + content: '\e7d3'; +} +.cicon-redpacket-o:before { + content: '\e71a'; +} +.cicon-refresh:before { + content: '\e611'; +} +.cicon-repair:before { + content: '\e73f'; +} +.cicon-repair-o:before { + content: '\e73e'; +} +.cicon-repeat:before { + content: '\e617'; +} +.cicon-replay:before { + content: '\e619'; +} +.cicon-reply:before { + content: '\e618'; +} +.cicon-reply-all:before { + content: '\e614'; +} +.cicon-road-map:before { + content: '\e769'; +} +.cicon-road-map-o:before { + content: '\e76a'; +} +.cicon-round:before { + content: '\e716'; +} +.cicon-round-angle:before { + content: '\e6f5'; +} +.cicon-round-angle-o:before { + content: '\e6f6'; +} +.cicon-round-arrow-line:before { + content: '\e734'; +} +.cicon-round-box:before { + content: '\e604'; +} +.cicon-safe:before { + content: '\e77f'; +} +.cicon-safe-check:before { + content: '\e875'; +} +.cicon-safe-check-o:before { + content: '\e876'; +} +.cicon-safe-flash:before { + content: '\e783'; +} +.cicon-safe-flash-o:before { + content: '\e775'; +} +.cicon-safe-key:before { + content: '\e76c'; +} +.cicon-safe-key-o:before { + content: '\e766'; +} +.cicon-safe-o:before { + content: '\e77e'; +} +.cicon-save:before { + content: '\e677'; +} +.cicon-save-o:before { + content: '\e684'; +} +.cicon-scan:before { + content: '\e703'; +} +.cicon-scissors:before { + content: '\e762'; +} +.cicon-search:before { + content: '\e6f7'; +} +.cicon-search-line:before { + content: '\e771'; +} +.cicon-searchlist:before { + content: '\e720'; +} +.cicon-search-o:before { + content: '\e782'; +} +.cicon-search-sm:before { + content: '\e631'; +} +.cicon-service:before { + content: '\e73d'; +} +.cicon-service-fill:before { + content: '\e704'; +} +.cicon-service-o:before { + content: '\e721'; +} +.cicon-set:before { + content: '\e773'; +} +.cicon-set-list:before { + content: '\e76f'; +} +.cicon-set-o:before { + content: '\e774'; +} +.cicon-settings:before { + content: '\e77a'; +} +.cicon-settings-o:before { + content: '\e780'; +} +.cicon-share:before { + content: '\e6c5'; +} +.cicon-share-line-o:before { + content: '\e74d'; +} +.cicon-shengji:before { + content: '\e747'; +} +.cicon-shopping-cart:before { + content: '\e685'; +} +.cicon-shopping-cart-o:before { + content: '\e676'; +} +.cicon-show:before { + content: '\e785'; +} +.cicon-show-o:before { + content: '\e787'; +} +.cicon-shuffle:before { + content: '\e61a'; +} +.cicon-sip:before { + content: '\e764'; +} +.cicon-sip-o:before { + content: '\e765'; +} +.cicon-skip-next:before { + content: '\e6dd'; +} +.cicon-skip-previous:before { + content: '\e6d6'; +} +.cicon-slack:before { + content: '\e87b'; +} +.cicon-slack-square:before { + content: '\e891'; +} +.cicon-sort:before { + content: '\e6bf'; +} +.cicon-sort-order:before { + content: '\e6fe'; +} +.cicon-sound:before { + content: '\e77b'; +} +.cicon-sponsor:before { + content: '\e77c'; +} +.cicon-sponsor-o:before { + content: '\e77d'; +} +.cicon-star:before { + content: '\e683'; +} +.cicon-star-half:before { + content: '\e67c'; +} +.cicon-star-o:before { + content: '\e67b'; +} +.cicon-stock:before { + content: '\e789'; +} +.cicon-stop:before { + content: '\e6db'; +} +.cicon-store:before { + content: '\e7ac'; +} +.cicon-store-0:before { + content: '\e7ab'; +} +.cicon-store-2:before { + content: '\e7a7'; +} +.cicon-store-2-o:before { + content: '\e7a5'; +} +.cicon-sub-left:before { + content: '\e60b'; +} +.cicon-sub-right:before { + content: '\e60f'; +} +.cicon-subtitles:before { + content: '\e6da'; +} +.cicon-subtitles-o:before { + content: '\e6d8'; +} +.cicon-sync-alt:before { + content: '\e613'; +} +.cicon-tag:before { + content: '\e751'; +} +.cicon-tag-o:before { + content: '\e752'; +} +.cicon-taobao:before { + content: '\e712'; +} +.cicon-terminal:before { + content: '\e7c1'; +} +.cicon-terminal-o:before { + content: '\e7c2'; +} +.cicon-thumb-down:before { + content: '\e6c1'; +} +.cicon-thumb-down-o:before { + content: '\e6c9'; +} +.cicon-thumb-up:before { + content: '\e6c6'; +} +.cicon-thumb-up-line:before { + content: '\e71b'; +} +.cicon-thumb-up-line-o:before { + content: '\e6eb'; +} +.cicon-thumb-up-o:before { + content: '\e6cb'; +} +.cicon-ticket:before { + content: '\e800'; +} +.cicon-ticket-o:before { + content: '\e701'; +} +.cicon-time:before { + content: '\e6f8'; +} +.cicon-time-o:before { + content: '\e6f9'; +} +.cicon-timer:before { + content: '\e69c'; +} +.cicon-title:before { + content: '\e82f'; +} +.cicon-titles:before { + content: '\e745'; +} +.cicon-toggle:before { + content: '\e706'; +} +.cicon-toggle-o:before { + content: '\e733'; +} +.cicon-topbar:before { + content: '\e788'; +} +.cicon-translate:before { + content: '\e79a'; +} +.cicon-tree:before { + content: '\e659'; +} +.cicon-Tt:before { + content: '\e7a2'; +} +.cicon-twiter:before { + content: '\e6e8'; +} +.cicon-cicon-community-o:before { + content: '\e6df'; +} +.cicon-undo:before { + content: '\e61b'; +} +.cicon-unfold-less:before { + content: '\e60e'; +} +.cicon-unfold-more:before { + content: '\e609'; +} +.cicon-upstage:before { + content: '\e724'; +} +.cicon-upstage-o:before { + content: '\e725'; +} +.cicon-view-agenda:before { + content: '\e639'; +} +.cicon-view-array:before { + content: '\e636'; +} +.cicon-view-carousel:before { + content: '\e638'; +} +.cicon-view-column:before { + content: '\e632'; +} +.cicon-view-day:before { + content: '\e627'; +} +.cicon-view-headline:before { + content: '\e62b'; +} +.cicon-view-list:before { + content: '\e63c'; +} +.cicon-view-module:before { + content: '\e629'; +} +.cicon-view-quilt:before { + content: '\e630'; +} +.cicon-volume:before { + content: '\e6c4'; +} +.cicon-volume-off:before { + content: '\e6cd'; +} +.cicon-warn:before { + content: '\e662'; +} +.cicon-warn-o:before { + content: '\e675'; +} +.cicon-wechat-pay:before { + content: '\e7e6'; +} +.cicon-weibo-fill:before { + content: '\e7e4'; +} +.cicon-weibo-o:before { + content: '\e7e3'; +} +.cicon-weixin:before { + content: '\e6de'; +} +.cicon-whatshot:before { + content: '\e6ca'; +} +.cicon-whatshot-o:before { + content: '\e6d0'; +} +.cicon-wifi:before { + content: '\e6d2'; +} +.cicon-wifi-off:before { + content: '\e6d3'; +} +.cicon-yamaxun:before { + content: '\e7b7'; +} +.cicon-zuoji:before { + content: '\e754'; +} diff --git a/sheep/scss/icon/_icon.scss b/sheep/scss/icon/_icon.scss new file mode 100644 index 0000000..f277fc8 --- /dev/null +++ b/sheep/scss/icon/_icon.scss @@ -0,0 +1,181 @@ +@font-face { + font-family: 'colorui'; /* Project id 2620914 */ + src: url('data:application/x-font-woff2;charset=utf-8;base64,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') + format('woff2'); + /* #ifdef MP-ALIPAY */ + src: url('//at.alicdn.com/t/font_2620914_57y9q5zpbel.woff?t=1624238023908') format('woff'), + url('//at.alicdn.com/t/font_2620914_57y9q5zpbel.ttf?t=1624238023908') format('truetype'); + /* #endif */ +} + +[class*='_icon-'] { + font-family: 'colorui' !important; + display: inline-block; +} +@font-face { + font-family: 'ui-num'; + src: url('data:application/x-font-ttf;base64,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') + format('woff2'); + font-weight: normal; + font-style: normal; +} + +._icon-checkbox:before { + content: '\e713'; +} + +._icon-box:before { + content: '\e714'; +} + +._icon-checkbox-o:before { + content: '\e715'; +} + +._icon-round:before { + content: '\e716'; +} + +._icon-home-o:before { + content: '\e70a'; +} + +._icon-home:before { + content: '\e70d'; +} + +._icon-edit:before { + content: '\e649'; +} + +._icon-close:before { + content: '\e6ed'; +} + +._icon-check-round:before { + content: '\e6f1'; +} + +._icon-check-round-o:before { + content: '\e6f2'; +} + +._icon-close-round:before { + content: '\e6f3'; +} + +._icon-close-round-o:before { + content: '\e6f4'; +} + +._icon-waiting:before { + content: '\e6f8'; +} + +._icon-waiting-o:before { + content: '\e6f9'; +} + +._icon-warn:before { + content: '\e662'; +} + +._icon-warn-o:before { + content: '\e675'; +} + +._icon-more:before { + content: '\e688'; +} + +._icon-delete:before { + content: '\e707'; +} + +._icon-delete-o:before { + content: '\e709'; +} + +._icon-add-round:before { + content: '\e717'; +} + +._icon-add-round-o:before { + content: '\e718'; +} + +._icon-add:before { + content: '\e6e4'; +} + +._icon-info:before { + content: '\e6ef'; +} + +._icon-info-o:before { + content: '\e705'; +} + +._icon-move:before { + content: '\e768'; +} + +._icon-title:before { + content: '\e82f'; +} + +._icon-titles:before { + content: '\e745'; +} + +._icon-loading:before { + content: '\e746'; +} + +._icon-copy-o:before { + content: '\e7bc'; +} + +._icon-copy:before { + content: '\e85c'; +} + +._icon-loader:before { + content: '\e76d'; +} + +._icon-search:before { + content: '\e782'; +} + +._icon-back:before { + content: '\e600'; +} + +._icon-forward:before { + content: '\e601'; +} + +._icon-arrow:before { + content: '\e608'; +} + +._icon-drop-down:before { + content: '\e61c'; +} + +._icon-drop-up:before { + content: '\e61d'; +} + +._icon-check:before { + content: '\e69f'; +} + +._icon-move-round:before { + content: '\e602'; +} + +._icon-move-round-o:before { + content: '\e603'; +} diff --git a/sheep/scss/icon/_sheepicon.scss b/sheep/scss/icon/_sheepicon.scss new file mode 100644 index 0000000..cf7ea08 --- /dev/null +++ b/sheep/scss/icon/_sheepicon.scss @@ -0,0 +1,94 @@ +@font-face { + font-family: 'sheepicon'; + src: url('data:application/x-font-woff2;charset=utf-8;base64,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') + format('woff2'); +} + +[class*='sicon-'] { + font-family: 'sheepicon'; + display: inline-block; +} + +.sicon-edit:before { + content: '\e711'; +} + +.sicon-basic:before { + content: '\e712'; +} + +.sicon-home:before { + content: '\e70c'; +} + +.sicon-more:before { + content: '\e707'; +} + +.sicon-check-line:before { + content: '\e708'; +} + +.sicon-transport:before { + content: '\e709'; +} + +.sicon-goods-card:before { + content: '\e70a'; +} + +.sicon-collect:before { + content: '\e70b'; +} + +.sicon-warning-line:before { + content: '\e70d'; +} + +.sicon-score1:before { + content: '\e70e'; +} + +.sicon-score2:before { + content: '\e70f'; +} + +.sicon-goods-list:before { + content: '\e710'; +} + +.sicon-back:before { + content: '\e706'; +} + +.sicon-unchecked:before { + content: '\e703'; +} + +.sicon-warning-outline:before { + content: '\e6ff'; +} + +.sicon-question-outline:before { + content: '\e700'; +} + +.sicon-circlecheck:before { + content: '\e701'; +} + +.sicon-circleclose:before { + content: '\e702'; +} + +.sicon-delivery:before { + content: '\e6fd'; +} + +.sicon-orders:before { + content: '\e6fe'; +} + +.sicon-qrcode:before { + content: '\e6f9'; +} diff --git a/sheep/scss/icon/_style.scss b/sheep/scss/icon/_style.scss new file mode 100644 index 0000000..a2c4dc8 --- /dev/null +++ b/sheep/scss/icon/_style.scss @@ -0,0 +1,43 @@ +@import './icon'; //核心图标库 +@import './coloricon'; //扩展图标库 +@import './sheepicon'; +.icon-spin { + animation: icon-spin 2s infinite linear; +} + +.icon-pulse { + animation: icon-spin 1s infinite steps(8); +} + +@keyframes icon-spin { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(359deg); + } +} +.icon-90 { + transform: rotate(90deg); +} +.icon-180 { + transform: rotate(180deg); +} +.icon-270 { + transform: rotate(270deg); +} +.icon-x { + transform: scale(-1, 1); +} +.icon-y { + transform: scale(1, -1); +} +.icon-fw { + width: calc(18em / 14); + text-align: center; +} +@each $class, $value in $iconsize { + .icon-#{$class} { + transform: scale(#{$value}); + } +} diff --git a/sheep/scss/index.scss b/sheep/scss/index.scss new file mode 100644 index 0000000..c841956 --- /dev/null +++ b/sheep/scss/index.scss @@ -0,0 +1,27 @@ +@import './tools'; +@import './ui'; + +/* 字体文件 */ +@font-face { + font-family: OPPOSANS; + src: url('~@/sheep/scss/font/OPPOSANS-M-subfont.ttf'); +} +.font-OPPOSANS { + font-family: OPPOSANS; +} +page { + -webkit-overflow-scrolling: touch; // 解决ios滑动不流畅 + height: 100%; + width: 100%; + // font-family: OPPOSANS; + word-break: break-all; //英文文本不换行 + white-space: normal; + background-color: $bg-page; + color: $dark-3; +} +::-webkit-scrollbar { + width: 0; + height: 0; + color: transparent; + display: none; +} diff --git a/sheep/scss/style/_avatar.scss b/sheep/scss/style/_avatar.scss new file mode 100644 index 0000000..e69de29 diff --git a/sheep/scss/style/_background.scss b/sheep/scss/style/_background.scss new file mode 100644 index 0000000..775f37f --- /dev/null +++ b/sheep/scss/style/_background.scss @@ -0,0 +1,204 @@ +/* ================== + 背景 + ==================== */ +/* -- 基础色 -- */ +@each $color, $value in map-merge($colors, $darks) { + .bg-#{$color} { + background-color: $value !important; + @if $color == 'yellow' { + color: #333333 !important; + } @else { + color: #ffffff !important; + } + } +} + +/* -- 浅色 -- */ +@each $color, $value in $colors { + .bg-#{$color}-light { + background-image: linear-gradient(45deg, white, mix(white, $value, 85%)) !important; + color: $value !important; + } + + .bg-#{$color}-thin { + background-color: rgba($value, var(--ui-BG-opacity)) !important; + color: $value !important; + } +} + +/* -- 渐变色 -- */ + +@each $color, $value in $colors { + @each $colorsub, $valuesub in $colors { + @if $color != $colorsub { + .bg-#{$color}-#{$colorsub} { + // background-color: $value !important; + background-image: linear-gradient(130deg, $value, $valuesub) !important; + color: #ffffff !important; + } + } + } +} +.bg-yellow-gradient { + background-image: linear-gradient(45deg, #f5fe00, #ff6600) !important; + color: $dark-3 !important; +} +.bg-orange-gradient { + background-image: linear-gradient(90deg, #ff6000, #fe832a) !important; + color: $white !important; +} +.bg-red-gradient { + background-image: linear-gradient(45deg, #f33a41, #ed0586) !important; + color: $white !important; +} +.bg-pink-gradient { + background-image: linear-gradient(45deg, #fea894, #ff1047) !important; + color: $white !important; +} +.bg-mauve-gradient { + background-image: linear-gradient(45deg, #c01f95, #7115cc) !important; + color: $white !important; +} +.bg-purple-gradient { + background-image: linear-gradient(45deg, #9829ea, #5908fb) !important; + color: $white !important; +} +.bg-blue-gradient { + background-image: linear-gradient(45deg, #00b8f9, #0166eb) !important; + color: $white !important; +} +.bg-cyan-gradient { + background-image: linear-gradient(45deg, #06edfe, #48b2fe) !important; + color: $white !important; +} +.bg-green-gradient { + background-image: linear-gradient(45deg, #3ab54a, #8cc63f) !important; + color: $white !important; +} +.bg-olive-gradient { + background-image: linear-gradient(45deg, #90e630, #39d266) !important; + color: $white !important; +} +.bg-grey-gradient { + background-image: linear-gradient(45deg, #9aadb9, #354855) !important; + color: $white !important; +} +.bg-brown-gradient { + background-image: linear-gradient(45deg, #ca6f2e, #cb1413) !important; + color: $white !important; +} + +@each $color, $value in $grays { + .bg-#{$color} { + background-color: $value !important; + color: #333333 !important; + } +} + +.bg-square { + @include bg-square; +} +.bg-none { + background: transparent !important; + color: inherit !important; +} + +[class*='bg-mask'] { + position: relative; + //background: transparent !important; + color: #ffffff !important; + > view, + > text { + position: relative; + z-index: 1; + color: #ffffff; + } + &::before { + content: ''; + border-radius: inherit; + width: 100%; + height: 100%; + @include position-center; + background-color: rgba(0, 0, 0, 0.4); + z-index: 0; + } + @at-root .bg-mask-80::before { + background: rgba(0, 0, 0, 0.8) !important; + } + @at-root .bg-mask-50::before { + background: rgba(0, 0, 0, 0.5) !important; + } + @at-root .bg-mask-20::before { + background: rgba(0, 0, 0, 0.2) !important; + } + @at-root .bg-mask-top::before { + background-color: rgba(0, 0, 0, 0); + background-image: linear-gradient(rgba(0, 0, 0, 1), rgba(0, 0, 0, 0.618), rgba(0, 0, 0, 0.01)); + } + @at-root .bg-white-top { + background-color: rgba(0, 0, 0, 0); + background-image: linear-gradient(rgba(255, 255, 255, 1), rgba(255, 255, 255, 0.3)); + } + @at-root .bg-mask-bottom::before { + background-color: rgba(0, 0, 0, 0); + background-image: linear-gradient(rgba(0, 0, 0, 0.01), rgba(0, 0, 0, 0.618), rgba(0, 0, 0, 1)); + } +} +.bg-img { + background-size: cover; + background-position: center; + background-repeat: no-repeat; +} + +[class*='bg-blur'] { + position: relative; + > view, + > text { + position: relative; + z-index: 1; + } + &::before { + content: ''; + width: 100%; + height: 100%; + @include position-center; + border-radius: inherit; + transform-origin: 0 0; + pointer-events: none; + box-sizing: border-box; + } +} +@supports (-webkit-backdrop-filter: blur(20px)) or (backdrop-filter: blur(20px)) { + .bg-blur::before { + @include blur; + background-color: var(--ui-Blur-1); + } + .bg-blur-1::before { + @include blur; + background-color: var(--ui-Blur-2); + } + .bg-blur-2::before { + @include blur; + background-color: var(--ui-Blur-3); + } +} +@supports not (backdrop-filter: blur(5px)) { + .bg-blur { + color: var(--ui-TC); + &::before { + background-color: var(--ui-BG); + } + } + .bg-blur-1 { + color: var(--ui-TC); + &::before { + background-color: var(--ui-BG-1); + } + } + .bg-blur-2 { + color: var(--ui-TC); + &::before { + background-color: var(--ui-BG-2); + } + } +} diff --git a/sheep/scss/style/_border.scss b/sheep/scss/style/_border.scss new file mode 100644 index 0000000..4ef1d54 --- /dev/null +++ b/sheep/scss/style/_border.scss @@ -0,0 +1,140 @@ +/* ================== + 边框 + ==================== */ +/* -- 实线 -- */ +.border { + overflow: initial !important; + @at-root [class*='border'], + [class*='dashed'] { + position: relative; + &.dline { + --ui-Border: var(--ui-BG-3); + } + &::after { + content: ' '; + width: 200%; + height: 200%; + position: absolute; + z-index: 0; + top: 0; + left: 0; + transform: scale(0.5); + transform-origin: 0 0; + pointer-events: none; + box-sizing: border-box; + border-radius: inherit; + } + &.radius::after { + border-radius: calc(#{$radius} * 2); + } + &.round::after { + border-radius: #{$round-pill}; + } + } + &::after { + border: 1px solid var(--ui-Border); + } + &s::after { + border: 4rpx solid var(--ui-Border); + } + &ss::after { + border: 8rpx solid var(--ui-Border); + } + @each $value in (top, right, bottom, left) { + &-#{$value}::after { + border-#{$value}: 1px solid var(--ui-Border); + } + &s-#{$value}::after { + border-#{$value}: 4rpx solid var(--ui-Border); + } + &ss-#{$value}::after { + border-#{$value}: 8rpx solid var(--ui-Border); + } + } +} +/* -- 虚线 -- */ +.dashed { + &::after { + border: 4rpx dashed var(--ui-Border); + } + &s::after { + border: 6rpx dashed var(--ui-Border); + } + @each $value in (top, right, bottom, left) { + &-#{$value}::after { + border-#{$value}: 4rpx dashed var(--ui-Border); + } + &s-#{$value}::after { + border-#{$value}: 6rpx dashed var(--ui-Border); + } + } +} +@each $color, $value in map-merge($colors, map-merge($darks, $grays)) { + .border-#{$color}::after, + .border-#{$color}[class*='-shine']::before { + border-color: $value !important; + } +} +@each $value in (a, b, c, d, e) { + .main-#{$value}-border::after, + .main-#{$value}-border[class*='-shine']::before { + border-color: var(--main-#{$value}) !important; + } +} +.dashed-shine, +.dasheds-shine { + position: relative; + overflow: hidden; + &::after, + &::before { + border-style: dashed; + border-color: var(--ui-Border); + animation: shineafter 1s infinite linear; + width: calc(200% + 40px); + height: 200%; + border-width: 2px 0; + } + &::before { + content: ' '; + position: absolute; + transform: scale(0.5); + transform-origin: 0 0; + pointer-events: none; + box-sizing: border-box; + animation: shinebefore 1s infinite linear; + width: 200%; + height: calc(200% + 40px); + border-width: 0 2px; + } +} +.dasheds-shine { + &::after, + &::before { + border-width: 4px 0; + } + &::before { + border-width: 0 4px; + } +} + +@keyframes shineafter { + 0% { + top: 0; + left: -22px; + } + 100% { + top: 0px; + left: 0px; + } +} + +@keyframes shinebefore { + 0% { + top: -22px; + left: 0; + } + 100% { + top: 0px; + left: 0px; + } +} diff --git a/sheep/scss/style/_button.scss b/sheep/scss/style/_button.scss new file mode 100644 index 0000000..7069345 --- /dev/null +++ b/sheep/scss/style/_button.scss @@ -0,0 +1,87 @@ +.ui-btn-box { + display: inline-block; +} +.ui-btn { + position: relative; + border: 0rpx; + display: inline-block; + align-items: center; + justify-content: center; + box-sizing: border-box; + padding: 0.7857em 1.5em 0.7857em; + font-size: 28rpx; + line-height: 1em; + text-align: center; + text-decoration: none; + overflow: visible; + margin: 0 0.25em 0 0; + transform: translate(0rpx, 0rpx); + border-radius: $radius; + white-space: nowrap; + color: var(--text-a); + background-color: var(--ui-BG); + vertical-align: baseline; + &:first-child:last-child { + margin: 0; + } + &:not([class*='round'])::after { + border-radius: calc(#{$radius} * 2); + } + &:not([class*='border'])::after { + // content: ' '; + // width: 200%; + // height: 200%; + // display: block; + // position: absolute; + // z-index: 0; + // top: 0; + // left: 0; + // transform: scale(0.5); + // transform-origin: 0 0; + // pointer-events: none; + // box-sizing: border-box; + display: none; + } + &.round::after { + border-radius: #{$round-pill}; + } + &.icon { + padding: 0.8em 0.8em; + } + + &.sm { + font-size: 24rpx; + } + + &.lg { + font-size: 32rpx; + } + + &.xl { + font-size: 36rpx; + } + + &.block { + width: 100%; + display: block; + font-size: 32rpx; + } + + &[disabled] { + opacity: 0.6; + } + + &.none-style { + background-color: transparent !important; + position: absolute; + width: 100%; + height: 100%; + top: 0; + left: 0; + display: flex; + } +} + +.ui-btn:not(.icon) [class*='icon-'] { + margin: 0 0.25em; +} diff --git a/sheep/scss/style/_card.scss b/sheep/scss/style/_card.scss new file mode 100644 index 0000000..17aa6b3 --- /dev/null +++ b/sheep/scss/style/_card.scss @@ -0,0 +1,353 @@ +/* ================== + 卡片 + ==================== */ + +.ui-cards { + display: block; + overflow: hidden; + & .ui-btn.badge { + top: 0; + right: 0; + font-size: 24rpx; + padding: 0rpx 15rpx; + height: 40rpx; + } + &.no-card > .ui-item { + margin: 0rpx; + border-radius: 0rpx; + } + & > .ui-item { + display: block; + overflow: hidden; + border-radius: 10rpx; + margin: 30rpx; + } + & > .ui-item.shadow-blur { + overflow: initial; + } + .grid.grid-square { + margin-bottom: -20rpx; + } + &.article { + display: block; + & > .ui-item { + padding: 30rpx; + background-color: var(--box-bg); + display: flex; + align-items: flex-start; + } + & > .time { + padding: 30rpx 0 0 30rpx; + } + & > .ui-item .title { + font-size: 30rpx; + font-weight: 900; + color: #333333; + } + & > .ui-item .content { + flex: 1; + } + & > .ui-item > image { + width: 240rpx; + height: 6.4em; + margin-left: 20rpx; + border-radius: 6rpx; + } + & > .ui-item .content .desc { + font-size: 12px; + color: var(--text-c); + } + & > .ui-item .content .text-content { + font-size: 28rpx; + color: #888; + } + } + &.case { + .image { + position: relative; + image { + width: 100%; + display: block; + } + .ui-tag { + position: absolute; + right: 0; + top: 0; + } + .ui-bar { + position: absolute; + bottom: 0; + width: 100%; + background-color: transparent; + padding: 0rpx 30rpx; + } + .bg-black { + position: absolute; + bottom: 0; + width: 100%; + background-color: rgba(0, 0, 0, 0.6); + } + } + &.no-card .image { + margin: 30rpx 30rpx 0; + overflow: hidden; + border-radius: 10rpx; + } + } + &.dynamic { + display: block; + & > .ui-item { + display: block; + overflow: hidden; + & > .text-content { + padding: 0 30rpx 0; + font-size: 30rpx; + margin-bottom: 20rpx; + } + & .square-img { + width: 100%; + height: 200rpx; + border-radius: 6rpx; + } + & .only-img { + width: 100%; + height: 320rpx; + border-radius: 6rpx; + } + } + } + &.goods { + display: block; + & > .ui-item { + padding: 30rpx; + display: flex; + position: relative; + background-color: var(--ui-BG); + & + .ui-item { + border-top: 1rpx solid #eeeeee; + } + .content { + width: 410rpx; + padding: 0rpx; + } + .title { + font-size: 30rpx; + font-weight: 900; + color: #333333; + line-height: 1.4; + height: 1.4em; + overflow: hidden; + } + } + &.col-goods.col-twice { + display: flex; + flex-wrap: wrap; + padding-bottom: 30rpx; + & > .ui-item { + width: calc(50% - 30rpx); + margin: 20rpx 20rpx 0rpx 20rpx; + .content { + padding: 20rpx; + } + } + & > .ui-item:nth-child(2n) { + margin-left: 0rpx; + } + } + &.col-goods > .ui-item { + padding: 0rpx; + display: block; + border: 0px; + .content { + width: 100%; + padding: 30rpx; + } + } + &.no-card > .ui-item .content { + width: 470rpx; + padding: 0rpx; + } + &.no-card > .ui-item .title, + &.col-goods > .ui-item .title { + height: 3em; + overflow: hidden; + } + & > .ui-item .text-linecut-2 { + -webkit-line-clamp: 1; + } + &.no-card > .ui-item .text-linecut-2, + &.col-goods > .ui-item .text-linecut-2 { + -webkit-line-clamp: 2; + line-height: 1.6em; + height: 3.2em; + } + & > .ui-item > image { + width: 200rpx; + height: 200rpx; + margin-right: 20rpx; + border-radius: 6rpx; + } + &.no-card > .ui-item > image { + width: 220rpx; + height: 170rpx; + } + &.col-goods > .ui-item > image { + width: 100%; + height: 340rpx; + border-bottom-left-radius: 0rpx; + border-bottom-right-radius: 0rpx; + display: block; + } + &.col-goods.col-twice > .ui-item > image { + height: 236rpx; + } + } + &.loan { + display: block; + & > .ui-item { + padding: 30rpx 0 30rpx 30rpx; + display: flex; + position: relative; + background-color: var(--box-bg); + + .content { + width: 450rpx; + padding: 0rpx; + .tag-list { + width: 450rpx; + display: flex; + flex-wrap: wrap; + font-size: 12px; + margin-top: 18rpx; + } + } + .action { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + } + } + } + &.houses { + display: block; + & > .ui-item { + padding: 20rpx; + display: flex; + position: relative; + background-color: var(--box-bg); + .image { + width: 230rpx; + height: 180rpx; + margin-right: 20rpx; + border-radius: 6rpx; + } + .content { + width: 400rpx; + padding: 0rpx; + .tag-list { + width: 400rpx; + display: flex; + flex-wrap: wrap; + font-size: 12px; + margin-top: 10rpx; + .ui-item { + height: 20px; + line-height: 20px; + } + } + } + .action { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + } + } + } + + &.product { + display: flex; + flex-wrap: wrap; + padding-bottom: 30rpx; + & > .ui-item { + width: calc(100% - 15rpx); + margin: 20rpx 20rpx 0rpx 20rpx; + background-color: var(--box-bg); + position: relative; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); + // display: flex; + // flex-wrap: wrap; + .content { + padding: 20rpx; + // width: calc(100% - 345rpx); + .text-cut { + font-size: 16px; + } + } + .image { + width: 100%; + height: 240rpx; + border-radius: 6rpx 0 0 6rpx; + display: block; + } + .ui-progress-tag { + width: 4em; + text-align: right; + font-size: 12px; + } + .border-top { + width: 100%; + } + .ui-tag { + position: absolute; + top: 0; + left: 0; + border-radius: 6rpx 0 6rpx 0; + } + } + // & > .ui-item:nth-child(2n) { + // margin-left: 0rpx; + // } + } + &.shop { + display: flex; + flex-wrap: wrap; + padding-bottom: 30rpx; + & > .ui-item { + width: calc(50% - 30rpx); + margin: 20rpx 20rpx 0rpx 20rpx; + background-color: var(--box-bg); + padding: 20rpx; + .content { + margin-top: 15rpx; + } + .image { + width: 100%; + height: 285rpx; + border-radius: 6rpx; + display: block; + } + } + & > .ui-item:nth-child(2n) { + margin-left: 0rpx; + } + } + + &.orders .ui-item { + margin-top: 30rpx; + .address-box { + padding: 15rpx; + margin: 0 30rpx 30rpx; + border: 1px solid; + border-color: var(--main-a); + border-radius: 10px; + position: relative; + .ui-form-group { + min-height: 10px; + } + } + } +} diff --git a/sheep/scss/style/_code.scss b/sheep/scss/style/_code.scss new file mode 100644 index 0000000..5221c88 --- /dev/null +++ b/sheep/scss/style/_code.scss @@ -0,0 +1,55 @@ +.ui-code { + font-family: Monaco, Menlo, Consolas, 'Courier New'; + font-size: 90%; + position: relative; + z-index: 1; + color: var(--ui-TC); + .ui-rich-text { + display: inline-block; + } + + &.code { + display: inline-block; + padding: 0 10rpx; + margin: 0 10rpx; + border-radius: $radius-sm; + line-height: 1.6; + vertical-align: baseline; + } + + &.pre { + display: block; + margin: 1em 0; + line-height: 1.6; + &.hasTitle { + margin: 3.2em 0 1em; + } + // border-radius: $radius-sm; + .ui-code-title { + position: absolute; + top: -2.2em; + color: var(--ui-TC-2); + left: 0; + } + .ui-rich-text { + padding: 40rpx; + white-space: pre-wrap; + word-break: break-all; + word-wrap: break-word; + } + .ui-scroll-view { + &.ui-scroll { + max-height: 500px; + white-space: pre; + } + } + .ui-copy-btn { + position: absolute; + z-index: 2; + top: 0; + right: 0; + padding: 0.8em; + border-radius: 0 $radius-sm 0 $radius-sm; + } + } +} diff --git a/sheep/scss/style/_flex.scss b/sheep/scss/style/_flex.scss new file mode 100644 index 0000000..1daa45b --- /dev/null +++ b/sheep/scss/style/_flex.scss @@ -0,0 +1,79 @@ +/* ================== + 弹性布局 + ==================== */ +.flex { + display: flex !important; + &-sub { + flex: 1 !important; + } + &-twice { + flex: 2 !important; + } + &-treble { + flex: 3 !important; + } + &-column { + flex-direction: column !important; + } + &-row { + flex-direction: row !important; + } + &-column-reverse { + flex-direction: column-reverse !important; + } + &-row-reverse { + flex-direction: row-reverse !important; + } + &-wrap { + flex-wrap: wrap !important; + } + &-center { + @include flex-center; + } + &-bar { + @include flex-bar; + } +} +.basis { + @each $class, $value in (xs: 20%, sm: 40%, df: 50%, lg: 60%, xl: 80%) { + &-#{$class} { + flex-basis: $value !important; + } + } +} +.align { + @each $class, + $value + in (start: flex-start, end: flex-end, center: center, stretch: stretch, baseline: baseline) + { + &-#{$class} { + align-items: $value !important; + } + } +} +.self { + @each $class, + $value + in (start: flex-start, end: flex-end, center: center, stretch: stretch, baseline: baseline) + { + &-#{$class} { + align-self: $value !important; + } + } +} +.justify { + @each $class, + $value + in ( + start: flex-start, + end: flex-end, + center: center, + between: space-between, + around: space-around + ) + { + &-#{$class} { + justify-content: $value !important; + } + } +} diff --git a/sheep/scss/style/_form.scss b/sheep/scss/style/_form.scss new file mode 100644 index 0000000..91d3eb3 --- /dev/null +++ b/sheep/scss/style/_form.scss @@ -0,0 +1,121 @@ +/* ================== + 表单 + ==================== */ +.ui-form-item { + padding: 1rpx 24rpx; + display: flex; + align-items: center; + min-height: 100rpx; + justify-content: space-between; + .title { + text-align: justify; + padding-right: 30rpx; + font-size: 30rpx; + position: relative; + height: 60rpx; + line-height: 60rpx; + } + .content { + flex: 1; + } + input, + ui-input { + flex: 1; + font-size: 30rpx; + color: #555; + padding-right: 20rpx; + } + text[class*='icon-'] { + font-size: 36rpx; + padding: 0; + box-sizing: border-box; + } + textarea { + margin: 32rpx 0 30rpx; + height: 4.6em; + width: 100%; + line-height: 1.2em; + flex: 1; + font-size: 28rpx; + padding: 0; + } + picker, + .arrow { + flex: 1; + padding-right: 40rpx; + overflow: hidden; + position: relative; + } + picker .picker, + .arrow > view { + line-height: 100rpx; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + width: 100%; + } + picker::after, + .arrow::after { + font-family: 'ui'; + display: block; + content: '\e605'; + position: absolute; + font-size: 34rpx; + color: #8799a3; + line-height: 100rpx; + width: 60rpx; + text-align: center; + top: 0; + bottom: 0; + right: -20rpx; + margin: auto; + } + textarea[disabled], + textarea[disabled] .placeholder { + color: transparent; + } + &.align-start .title { + height: 1em; + margin-top: 32rpx; + line-height: 1em; + } + .grid-square { + > view { + background-color: #f8f8f8; + border-radius: 12rpx; + .mask { + background-color: rgba(0, 0, 0, 0.6); + position: absolute; + font-size: 20rpx; + color: #ffffff; + width: 100%; + bottom: 0; + text-align: center; + padding: 6rpx 0; + &.red-mask { + background-color: rgba(255, 80, 80, 0.6); + } + } + [class*='icon'] { + position: absolute; + width: 100%; + height: 100%; + display: flex; + align-items: center; + transform: scale(1.5); + justify-content: center; + } + .text-gray { + position: absolute; + width: 100%; + font-size: 24rpx; + text-align: center; + bottom: 20rpx; + } + } + } +} +.disabled { + opacity: 0.6; + cursor: not-allowed !important; +} diff --git a/sheep/scss/style/_grid.scss b/sheep/scss/style/_grid.scss new file mode 100644 index 0000000..b1b5230 --- /dev/null +++ b/sheep/scss/style/_grid.scss @@ -0,0 +1,103 @@ +/* ================== + 栅栏 + ==================== */ +@use 'sass:math'; + +@mixin make_col($screen) { + @for $i from 1 through 12 { + .ui-col-#{$screen}-#{$i} { + width: calc(100% / 12 * #{$i}); + } + .ui-cols-#{$screen}-#{$i} .ui-item { + width: calc(100% / #{$i}); + } + } +} +.ui-container { + box-sizing: border-box; + margin-left: auto; + margin-right: auto; + padding-left: 30rpx; + padding-right: 30rpx; + width: 100%; + max-width: 1440px; + &-fluid { + max-width: 100%; + padding-left: 0; + padding-right: 0; + } +} +.ui-grid { + display: flex; + flex-wrap: wrap; + &.multi-column { + display: block; + column-count: 2; + column-width: 0px; + column-gap: 0px; + > .ui-item { + break-inside: avoid; + padding: 0.001em; + } + } + &.grid-square { + overflow: hidden; + > .ui-item { + margin-right: 20rpx; + margin-bottom: 20rpx; + position: relative; + overflow: hidden; + } + @for $i from 1 through 12 { + &.ui-cols-#{$i} > .ui-item { + padding-bottom: calc((100% - #{20rpx * ($i - 1)}) / #{$i}); + height: 0; + width: calc((100% - #{20rpx * ($i - 1)}) / #{$i}); + } + } + @for $i from 1 through 12 { + &.ui-cols-#{$i} > .ui-item:nth-child(#{$i}n) { + margin-right: 0; + } + } + } +} +@for $i from 1 through 12 { + .ui-cols-#{$i} .ui-item { + width: calc(100% / #{$i}); + } +} +@for $i from 1 through 12 { + .ui-col-#{$i} { + width: calc(100% / 12 * #{$i}); + } +} +// 小屏 +@media screen and (min-width: 0px) { + @include make_col('xs'); +} + +// 小屏 +@media screen and (min-width: 320px) { + @include make_col('sm'); +} + +// 中屏 +@media screen and (min-width: 768px) { + @include make_col('md'); +} + +// 普通屏 +@media screen and (min-width: 1025px) { + @include make_col('lg'); +} + +// 大屏 +@media screen and (min-width: 1440px) { + @include make_col('xl'); +} + +// 超大屏 +@media screen and (min-width: 1920px) { + @include make_col('xxl'); +} diff --git a/sheep/scss/style/_markdown.scss b/sheep/scss/style/_markdown.scss new file mode 100644 index 0000000..37b023d --- /dev/null +++ b/sheep/scss/style/_markdown.scss @@ -0,0 +1,62 @@ +.cu-markdown { + position: relative; + z-index: 1; + &.selectable { + cursor: auto; + user-select: text; + } + inline { + display: inline-block; + } + + .list { + .list-item { + line-height: 1.8; + .list { + margin-left: 1.28571em; + .ui-title { + transform: scale(0.6); + &:before { + content: '\e716'; + } + } + } + } + .list-item-p { + position: relative; + padding-left: 1.5em; + .list-item-t { + display: block; + width: 1.3em; + text-align: center; + position: absolute; + left: 0; + } + } + } + .md-table + .md-table { + margin-top: 30rpx; + } +} + +.paragraph { + margin: 0 0 40rpx; + line-height: 1.8; +} + +.blockquote { + @extend .paragraph; + padding: 20rpx 30rpx; + border-left-style: solid; + border-left-width: 10rpx; + border-color: var(--ui-Border); + background: none repeat scroll 0 0 rgba(102, 128, 153, 0.05); + + .paragraph { + margin-bottom: 30rpx; + } + + .paragraph:last-child { + margin-bottom: 0; + } +} diff --git a/sheep/scss/style/_menu.scss b/sheep/scss/style/_menu.scss new file mode 100644 index 0000000..a4a8282 --- /dev/null +++ b/sheep/scss/style/_menu.scss @@ -0,0 +1,54 @@ +.ui-menu { + background-color: var(--ui-BG); +} + +.ui-menu-item { + position: relative; + @include flex-bar; + min-height: 4em; + padding: 0 30rpx; + .ui-menu-item-icon { + width: 1.7em; + margin-right: 0.3em; + position: relative; + display: flex; + align-items: center; + justify-content: center; + transform: scale(1.3); + } + .ui-menu-item-icon .ui-menu-item-image { + width: 1.2em; + height: 1.2em; + display: inline-block; + } + .ui-menu-item-content { + flex: 1; + position: relative; + @include flex-bar; + } + .ui-menu-item-arrow { + width: 1.6em; + text-align: center; + color: var(--ui-TC-3); + } + &::after { + content: ' '; + width: calc(200% - 120rpx); + left: 30rpx; + position: absolute; + top: 0; + box-sizing: border-box; + height: 200%; + border-top: 1px solid var(--ui-Border); + border-radius: inherit; + transform: scale(1); + transform-origin: 0 0; + pointer-events: none; + } + &.first-item::after { + display: none; + } + &:first-child::after { + display: none; + } +} diff --git a/sheep/scss/style/_shadow.scss b/sheep/scss/style/_shadow.scss new file mode 100644 index 0000000..27cb3f6 --- /dev/null +++ b/sheep/scss/style/_shadow.scss @@ -0,0 +1,90 @@ +/* ================== + 阴影 + ==================== */ + +.shadow { + box-shadow: var(--ui-Shadow); + &-sm { + box-shadow: var(--ui-Shadow-sm); + } + &-lg { + box-shadow: var(--ui-Shadow-lg); + } + &-inset { + box-shadow: var(--ui-Shadow-inset); + } + @each $color, $value in $colors { + @at-root .shadow-#{$color} { + box-shadow: 0 0.5em 1em rgba($value, var(--ui-Shadow-opacity)); + } + &-sm.shadow-#{$color} { + box-shadow: 0 0.125em 0.25em rgba($value, var(--ui-Shadow-opacity)); + } + &-lg.shadow-#{$color} { + box-shadow: 0 1em 3em rgba($value, var(--ui-Shadow-opacity-lg)); + } + } + + &-warp { + position: relative; + } + &-warp:before, + &-warp:after { + position: absolute; + content: ''; + bottom: -10rpx; + left: 20rpx; + width: calc(50% - #{40rpx}); + height: 30rpx; + transform: skew(0deg, -6deg); + transform-origin: 50% 50%; + background-color: rgba(0, 0, 0, var(--ui-Shadow-opacity)); + filter: blur(20rpx); + z-index: -1; + opacity: 0.5; + } + &-warp:after { + right: 20rpx; + left: auto; + transform: skew(0deg, 6deg); + } + &-blur { + position: relative; + } + &-blur::before { + content: ''; + display: block; + background: inherit; + filter: blur(20rpx); + position: absolute; + width: 100%; + height: 100%; + top: 0.5em; + left: 0.5em; + z-index: -1; + opacity: var(--ui-Shadow-opacity-lg); + transform-origin: 0 0; + border-radius: inherit; + transform: scale(1, 1); + } +} +.drop-shadow { + filter: drop-shadow(0 0 30rpx rgba(0, 0, 0, 0.1)); + &-sm { + filter: drop-shadow(0 4rpx 4rpx rgba(0, 0, 0, 0.06)); + } + &-lg { + filter: drop-shadow(0 30rpx 60rpx rgba(0, 0, 0, 0.2)); + } + @each $color, $value in $colors { + @at-root .drop-shadow-#{$color} { + filter: drop-shadow(0 15rpx 15rpx rgba(darken($value, 10%), 0.3)); + } + &-sm.drop-shadow-#{$color} { + filter: drop-shadow(0 4rpx 4rpx rgba(darken($value, 10%), 0.3)); + } + &-lg.drop-shadow-#{$color} { + filter: drop-shadow(0 50rpx 100rpx rgba(darken($value, 10%), 0.2)); + } + } +} diff --git a/sheep/scss/style/_table.scss b/sheep/scss/style/_table.scss new file mode 100644 index 0000000..ad5effa --- /dev/null +++ b/sheep/scss/style/_table.scss @@ -0,0 +1,133 @@ +.ui-table { + background-color: var(--ui-BG); + max-width: 100%; + display: table; + &.table-full { + width: 100%; + } + &.table-radius { + border-radius: $radius; + .ui-table-header { + .ui-table-tr { + border-top-left-radius: $radius; + border-top-right-radius: $radius; + } + .ui-table-th { + &:first-child { + border-top-left-radius: $radius; + } + &:last-child { + border-top-right-radius: $radius; + } + } + } + } + .ui-table-header { + display: table-header-group; + .ui-table-th { + font-weight: bold; + border-bottom: 1px solid var(--ui-Border); + white-space: nowrap; + + padding: 1em 0.8em; + } + } + + .ui-table-tr { + display: table-row; + z-index: 1; + } + + .ui-table-body { + display: table-row-group; + position: relative; + .ui-table-tr:hover { + background-color: var(--ui-BG-1) !important; + } + .ui-table-loading { + min-height: 300px; + position: absolute !important; + width: 100%; + height: 100%; + z-index: 2; + display: flex; + align-items: center; + justify-content: center; + border: 1px solid var(--ui-Border); + } + } + + .ui-table-td, + .ui-table-th { + display: table-cell; + text-align: unset; + padding: 0.5em 0.8em; + // font-size: 90%; + vertical-align: middle; + } +} + +.ui-table.table-border { + &, + & .ui-table-td, + & .ui-table-th { + position: relative; + &::after { + content: ' '; + width: 200%; + height: 200%; + position: absolute; + top: 0; + left: 0; + border-radius: inherit; + transform: scale(0.5); + transform-origin: 0 0; + pointer-events: none; + box-sizing: border-box; + border: 1px solid var(--ui-Border); + z-index: 1; + } + } + .ui-table-td, + .ui-table-th { + &::after { + border-width: 1px 1px 0 0; + } + &:last-child::after { + border-right: none; + } + } +} +.ui-table.table-radius { + &::after { + border-radius: calc(#{$radius} * 2); + } + & .ui-table-tr .ui-table-th:first-child { + border-top-left-radius: calc(#{$radius} * 2); + } + & .ui-table-tr .ui-table-th:last-child { + border-top-right-radius: calc(#{$radius} * 2); + } + & .ui-table-tr:last-child .ui-table-td:first-child { + border-bottom-left-radius: #{$radius}; + } + & .ui-table-tr:last-child .ui-table-td:last-child { + border-bottom-right-radius: #{$radius}; + } +} +.ui-table.table-striped > .ui-table-body > .ui-table-tr:nth-child(2n + 1), +.ui-table.table-striped > .ui-table-body > .ui-table-tr:nth-child(2n + 1) { + background-color: var(--ui-BG-1); +} + +.table-responsive { + width: inherit; + height: 100%; + max-width: 100%; + overflow: hidden; + box-sizing: border-box; + .table-responsive-box { + position: relative; + overflow: hidden; + } +} diff --git a/sheep/scss/style/_tag.scss b/sheep/scss/style/_tag.scss new file mode 100644 index 0000000..e69de29 diff --git a/sheep/scss/style/_text.scss b/sheep/scss/style/_text.scss new file mode 100644 index 0000000..8249022 --- /dev/null +++ b/sheep/scss/style/_text.scss @@ -0,0 +1,104 @@ +/* ================== + 文本 + ==================== */ +@use 'sass:math'; +.font-0 { + font-size: 24rpx; + --textSize: -4rpx; +} +.font-1 { + font-size: 28rpx; + --textSize: 0rpx; +} +.font-2 { + font-size: 32rpx; + --textSize: 4rpx; +} +.font-3 { + font-size: 36rpx; + --textSize: 8rpx; +} +.font-4 { + font-size: 40rpx; + --textSize: 12rpx; +} +.text { + @each $class, $value in $fontsize { + &-#{$class}, + &-#{math.div($value ,2)} { + font-size: calc(#{$value}rpx + var(--textSize)) !important; + } + } + &-cut { + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + } + @at-root [class*='text-linecut'] { + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + overflow: hidden; + word-break: break-all; + } + @for $i from 2 through 10 { + &-linecut-#{$i} { + -webkit-line-clamp: #{$i}; + } + } + &-justify { + text-align: justify; + } + &-justify-line { + text-align: justify; + line-height: 0.5em; + margin-top: 0.5em; + &::after { + content: '.'; + display: inline-block; + width: 100%; + } + } + + &-Abc { + text-transform: Capitalize !important; + } + &-ABC { + text-transform: Uppercase !important; + } + &-abc { + text-transform: Lowercase !important; + } + &-del, + &-line { + text-decoration: line-through !important; + } + &-bottomline { + text-decoration: underline !important; + } + &-italic { + font-style: italic !important; + } + &-style-none { + text-decoration: none !important; + } + &-break { + word-break: break-word !important; + overflow-wrap: break-word !important; + } + &-reset { + color: inherit !important; + } + &-price::before { + content: '¥'; + font-size: 80%; + margin-right: 4rpx; + } + &-hide { + font: 0/0 a; + color: transparent; + text-shadow: none; + background-color: transparent; + border: 0; + } +} diff --git a/sheep/scss/theme/_dark.scss b/sheep/scss/theme/_dark.scss new file mode 100644 index 0000000..8caad17 --- /dev/null +++ b/sheep/scss/theme/_dark.scss @@ -0,0 +1,39 @@ +// 核心主题样式文件 +@mixin theme-dark { + // 背景色 + --ui-BG: #393939; + --ui-BG-1: #333333; + --ui-BG-2: #2c2c2c; + --ui-BG-3: #292929; + --ui-BG-4: #222222; + + // 文本色 + --ui-TC: #ffffff; + --ui-TC-1: #d4d4d4; + --ui-TC-2: #919191; + --ui-TC-3: #6a6a6a; + --ui-TC-4: #474747; + + // 模糊 + --ui-Blur: rgba(38, 38, 38, 0.98); + --ui-Blur-1: rgba(38, 38, 38, 0.75); + --ui-Blur-2: rgba(38, 38, 38, 0.25); + --ui-Blur-3: rgba(38, 38, 38, 0.05); + + // 边框 + --ui-Border: rgba(119, 119, 119, 0.25); + --ui-Outline: rgba(255, 255, 255, 0.1); + --ui-Line: rgba(119, 119, 119, 0.25); + + // 透明与阴影 + --ui-Shadow: 0 0.5em 1em rgba(0, 0, 0, 0.45); + --ui-Shadow-sm: 0 0.125em 0.25em rgba(0, 0, 0, 0.475); + --ui-Shadow-lg: 0 1em 3em rgba(0, 0, 0, 0.475); + --ui-Shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.475); + + --ui-Shadow-opacity: 0.55; + --ui-Shadow-opacity-sm: 0.175; + --ui-Shadow-opacity-lg: 0.75; + + --ui-BG-opacity: 0.1; +} diff --git a/sheep/scss/theme/_light.scss b/sheep/scss/theme/_light.scss new file mode 100644 index 0000000..af5f245 --- /dev/null +++ b/sheep/scss/theme/_light.scss @@ -0,0 +1,39 @@ +// 核心主题样式文件 +@mixin theme-light { + // 背景色 + --ui-BG: #ffffff; + --ui-BG-1: #f6f6f6; + --ui-BG-2: #f1f1f1; + --ui-BG-3: #e8e8e8; + --ui-BG-4: #e0e0e0; + + // 文本色 + --ui-TC: #303030; + --ui-TC-1: #525252; + --ui-TC-2: #777777; + --ui-TC-3: #9e9e9e; + --ui-TC-4: #c6c6c6; + + // 模糊 + --ui-Blur: rgba(255, 255, 255, 0.98); + --ui-Blur-1: rgba(255, 255, 255, 0.75); + --ui-Blur-2: rgba(255, 255, 255, 0.25); + --ui-Blur-3: rgba(255, 255, 255, 0.05); + + // 边框 + --ui-Border: rgba(119, 119, 119, 0.25); + --ui-Outline: rgba(0, 0, 0, 0.1); + --ui-Line: rgba(119, 119, 119, 0.25); + + // 透明与阴影 + --ui-Shadow: 0 0.5em 1em rgba(0, 0, 0, 0.15); + --ui-Shadow-sm: 0 0.125em 0.25em rgba(0, 0, 0, 0.075); + --ui-Shadow-lg: 0 1em 3em rgba(0, 0, 0, 0.175); + --ui-Shadow-inset: inset 0 0.1em 0.2em rgba(0, 0, 0, 0.075); + + --ui-Shadow-opacity: 0.45; + --ui-Shadow-opacity-sm: 0.075; + --ui-Shadow-opacity-lg: 0.65; + + --ui-BG-opacity: 0.1; +} diff --git a/sheep/scss/theme/_style.scss b/sheep/scss/theme/_style.scss new file mode 100644 index 0000000..1eef587 --- /dev/null +++ b/sheep/scss/theme/_style.scss @@ -0,0 +1,68 @@ +@import './light'; //浅蓝主题 +@import './dark'; //深蓝主题 +// 多主题 +.theme-light { + @include theme-light; +} +.theme-dark { + @include theme-dark; +} +.theme-auto { + @include theme-light; +} +@media (prefers-color-scheme: dark) { + .theme-auto { + @include theme-dark; + } +} + +@each $value in ('', '-1', '-2', '-3', '-4') { + // 背景色 + 文字色 : 白色 + 默认色; + .ui-BG#{$value} { + background-color: var(--ui-BG#{$value}) !important; + color: var(--ui-TC); + } + // 文字颜色 + .ui-TC#{$value} { + color: var(--ui-TC#{$value}) !important; + } + // 主题色背景 + .ui-BG-Main#{$value} { + background-color: var(--ui-BG-Main#{$value}) !important; + color: var(--ui-BG-Main-TC) !important; + } + // 主题色渐变,横向 + .ui-BG-Main-Gradient { + background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient)) !important; + color: var(--ui-BG-Main-TC) !important; + } + // 主题色文字 + .ui-TC-Main#{$value} { + color: var(--ui-BG-Main#{$value}) !important; + } + // 主题色阴影 + .ui-Shadow-Main { + box-shadow: var(--ui-Main-box-shadow) !important; + } + .ui-BG-Main-light { + background: var(----ui-BG-Main-light) !important; + color: var(--ui-BG-Main#{$value}) !important; + } +} + +@each $color, $value in $colors { + .main-#{$color} { + --ui-BG-Main: #{$value}; + --ui-BG-Main-tag: #{rgba($value, 0.05)}; + --ui-BG-Main-gradient: #{rgba($value, 0.6)}; + --ui-BG-Main-light: #{rgba($value, 0.2)}; + --ui-BG-Main-opacity-1: #{rgba($value, 0.1)}; + --ui-BG-Main-opacity-4: #{rgba($value, 0.4)}; + --ui-Main-box-shadow: 0 0.2em 0.5em #{rgba($value, var(--ui-Shadow-opacity))}; + --ui-BG-Main-1: #{mix(rgba(255, 255, 255, 0.7), desaturate($value, 20%), 10%)}; + --ui-BG-Main-2: #{mix(rgba(255, 255, 255, 0.6), desaturate($value, 40%), 20%)}; + --ui-BG-Main-3: #{mix(rgba(119, 119, 119, 0.2), desaturate($value, 40%), 40%)}; + --ui-BG-Main-4: #{mix(rgba(119, 119, 119, 0.1), desaturate($value, 40%), 60%)}; + --ui-BG-Main-TC: #ffffff !important; + } +} diff --git a/sheep/scss/ui.scss b/sheep/scss/ui.scss new file mode 100644 index 0000000..b9b7381 --- /dev/null +++ b/sheep/scss/ui.scss @@ -0,0 +1,19 @@ +@import './theme/style'; //系统主题 +@import './main'; //主样式* + +@import './style/background'; //背景 +@import './style/grid'; //列 +@import './style/flex'; //布局 +@import './style/border'; //边框 +@import './style/text'; //文本 +@import './style/shadow'; //阴影 +@import './icon/style'; //图标 +@import './style/tag'; //标签 +@import './style/button'; //按钮 +@import './style/avatar'; //头像 +@import './style/table'; //表格 +@import './style/code'; //代码片段 +@import './style/form'; //表单 +@import './style/menu'; //表单 +@import './style/markdown'; //表单 +@import './style/card'; //表单 diff --git a/sheep/store/app.js b/sheep/store/app.js new file mode 100644 index 0000000..5b2939d --- /dev/null +++ b/sheep/store/app.js @@ -0,0 +1,152 @@ +import DiyApi from '@/sheep/api/promotion/diy'; +import { defineStore } from 'pinia'; +import $platform from '@/sheep/platform'; +import $router from '@/sheep/router'; +import user from './user'; +import sys from './sys'; +import { baseUrl, h5Url } from '@/sheep/config'; + +const app = defineStore({ + id: 'app', + state: () => ({ + info: { + // 应用信息 + name: '', // 商城名称 + logo: '', // logo + version: '', // 版本号 + copyright: '', // 版权信息 I + copytime: '', // 版权信息 II + + cdnurl: '', // 云存储域名 + filesystem: '', // 云存储平台 + }, + platform: { + share: { + methods: [], // 支持的分享方式 + forwardInfo: {}, // 默认转发信息 + posterInfo: {}, // 海报信息 + linkAddress: '', // 复制链接地址 + }, + bind_mobile: 0, // 登陆后绑定手机号提醒 (弱提醒,可手动关闭) + }, + template: { + // 店铺装修模板 + basic: {}, // 基本信息 + home: { + // 首页模板 + style: {}, + data: [], + }, + user: { + // 个人中心模板 + style: {}, + data: [], + }, + }, + shareInfo: {}, // 全局分享信息 + has_wechat_trade_managed: 0, // 小程序发货信息管理 0 没有 || 1 有 + }), + actions: { + // 获取Shopro应用配置和模板 + async init(templateId = null) { + // 检查网络 + const networkStatus = await $platform.checkNetwork(); + if (!networkStatus) { + $router.error('NetworkError'); + } + + // 检查配置 + if (typeof baseUrl === 'undefined') { + $router.error('EnvError'); + } + + // 加载装修配置 + await adaptTemplate(this.template, templateId); + + // TODO 芋艿:【初始化优化】未来支持管理后台可配;对应 https://api.shopro.sheepjs.com/shop/api/init + if (true) { + this.info = { + name: '哈客商城', + logo: 'https://static.iocoder.cn/ruoyi-vue-pro-logo.png', + version: '2.6.0', + copyright: '全部开源,个人与企业可 100% 免费使用', + copytime: 'Copyright© 2018-2025', + + cdnurl: 'https://file.sheepjs.com', // 云存储域名 + filesystem: 'qcloud', // 云存储平台 + }; + this.platform = { + share: { + methods: ['forward', 'poster', 'link'], + linkAddress: h5Url, + posterInfo: { + user_bg: '/static/img/shop/config/user-poster-bg.png', + goods_bg: '/static/img/shop/config/goods-poster-bg.png', + groupon_bg: '/static/img/shop/config/groupon-poster-bg.png', + }, + forwardInfo: { + title: '', + image: '', + desc: '', + }, + }, + bind_mobile: 0, + }; + this.has_wechat_trade_managed = 0; + + // 加载主题 + const sysStore = sys(); + sysStore.setTheme(); + + // 模拟用户登录 + const userStore = user(); + if (userStore.isLogin) { + userStore.loginAfter(); + } + return Promise.resolve(true); + } else { + $router.error('InitError', res.msg || '加载失败'); + } + }, + }, + persist: { + enabled: true, + strategies: [ + { + key: 'app-store', + }, + ], + }, +}); + +const adaptTemplate = async (appTemplate, templateId) => { + const { data: diyTemplate } = templateId + ? // 查询指定模板,一般是预览时使用 + await DiyApi.getDiyTemplate(templateId) + : await DiyApi.getUsedDiyTemplate(); + // 模板不存在 + if (!diyTemplate) { + $router.error('TemplateError'); + return; + } + + const tabBar = diyTemplate?.property?.tabBar; + if (tabBar) { + appTemplate.basic.tabbar = tabBar; + // TODO 商城装修没有对 tabBar 进行角标配置,测试角标需打开以下注释 + // appTemplate.basic.tabbar.items.forEach((tabBar) => { + // tabBar.dot = false + // tabBar.badge = 100 + // }) + // appTemplate.basic.tabbar.badgeStyle = { + // backgroundColor: '#882222', + // } + if (tabBar?.theme) { + appTemplate.basic.theme = tabBar?.theme; + } + } + appTemplate.home = diyTemplate?.home; + appTemplate.user = diyTemplate?.user; +}; + +export default app; diff --git a/sheep/store/cart.js b/sheep/store/cart.js new file mode 100644 index 0000000..ab96a2b --- /dev/null +++ b/sheep/store/cart.js @@ -0,0 +1,121 @@ +import { defineStore } from 'pinia'; +import CartApi from '@/sheep/api/trade/cart'; + +const cart = defineStore({ + id: 'cart', + state: () => ({ + list: [], // 购物车列表(invalidList + validList) + selectedIds: [], // 已选列表 + isAllSelected: false, // 是否全选 + totalPriceSelected: 0, // 选中项总金额 + newList: [], // 除去已下架的购物车列表(validList) + editMode: false, // 是否是编辑模式 + }), + actions: { + // 获取购物车列表 + async getList() { + const { data, code } = await CartApi.getCartList(); + if (code === 0) { + this.list = [...data.validList, ...data.invalidList]; + this.newList = data.validList; + + // 计算各种关联属性 + this.selectedIds = []; + this.isAllSelected = true; + this.totalPriceSelected = 0; + (this.editMode ? this.list : this.newList).forEach((item) => { + if (item.selected) { + this.selectedIds.push(item.id); + this.totalPriceSelected += item.count * item.sku?.price; + } else { + this.isAllSelected = false; + } + }); + } + }, + + onChangeEditMode(flag) { + this.editMode = flag; + this.selectedIds = []; + this.getList(); + }, + + // 添加购物车 + async add(goodsInfo) { + // 添加购物项 + const { code } = await CartApi.addCart({ + skuId: goodsInfo.id, + count: goodsInfo.goods_num, + }); + // 刷新购物车列表 + if (code === 0) { + await this.getList(); + } + }, + + // 更新购物车 + async update(goodsInfo) { + const { code } = await CartApi.updateCartCount({ + id: goodsInfo.goods_id, + count: goodsInfo.goods_num, + }); + if (code === 0) { + await this.getList(); + } + }, + + // 移除购物车 + async delete(ids) { + let idsTemp = ''; + if (Array.isArray(ids)) { + idsTemp = ids.join(','); + } else { + idsTemp = ids; + } + const { code } = await CartApi.deleteCart(idsTemp); + if (code === 0) { + await this.getList(); + } + }, + + // 单选购物车商品 + async selectSingle(goodsId) { + const { code } = await CartApi.updateCartSelected({ + ids: [goodsId], + selected: !this.selectedIds.includes(goodsId), // 取反 + }); + if (code === 0) { + await this.getList(); + } + }, + + // 全选购物车商品 + async selectAll(flag) { + const { code } = await CartApi.updateCartSelected({ + ids: this.list.map((item) => item.id), + selected: flag, + }); + if (code === 0) { + await this.getList(); + } + }, + + // 清空购物车。注意,仅用于用户退出时,重置数据 + emptyList() { + this.list = []; + this.selectedIds = []; + this.isAllSelected = true; + this.totalPriceSelected = 0; + }, + }, + persist: { + enabled: true, + strategies: [ + { + key: 'cart-store', + }, + ], + }, +}); + +export default cart; diff --git a/sheep/store/index.js b/sheep/store/index.js new file mode 100644 index 0000000..3d06698 --- /dev/null +++ b/sheep/store/index.js @@ -0,0 +1,20 @@ +import { createPinia } from 'pinia'; +import piniaPersist from 'pinia-plugin-persist-uni'; + +// 自动注入所有pinia模块 +const files = import.meta.glob('./*.js', { eager: true }); +const modules = {}; +Object.keys(files).forEach((key) => { + modules[key.replace(/(.*\/)*([^.]+).*/gi, '$2')] = files[key].default; +}); + +export const setupPinia = (app) => { + const pinia = createPinia(); + pinia.use(piniaPersist); + + app.use(pinia); +}; + +export default (name) => { + return modules[name](); +}; diff --git a/sheep/store/modal.js b/sheep/store/modal.js new file mode 100644 index 0000000..bde9e0a --- /dev/null +++ b/sheep/store/modal.js @@ -0,0 +1,29 @@ +import { defineStore } from 'pinia'; + +const modal = defineStore({ + id: 'modal', + state: () => ({ + auth: '', // 授权弹框 accountLogin|smsLogin|resetPassword|changeMobile|changePassword|changeUsername + share: false, // 分享弹框 + menu: false, // 快捷菜单弹框 + advHistory: [], // 广告弹框记录 + lastTimer: { + // 短信验证码计时器,为了防止刷新请求做了持久化 + smsLogin: 0, + changeMobile: 0, + resetPassword: 0, + changePassword: 0, + } + }), + persist: { + enabled: true, + strategies: [ + { + key: 'modal-store', + paths: ['lastTimer', 'advHistory'], + }, + ], + }, +}); + +export default modal; diff --git a/sheep/store/sys.js b/sheep/store/sys.js new file mode 100644 index 0000000..f7151e0 --- /dev/null +++ b/sheep/store/sys.js @@ -0,0 +1,32 @@ +import { defineStore } from 'pinia'; +import app from './app'; + +const sys = defineStore({ + id: 'sys', + state: () => ({ + theme: '', // 主题, + mode: 'light', // 明亮模式、暗黑模式(暂未支持) + modeAuto: false, // 跟随系统 + fontSize: 1, // 设置默认字号等级(0-4) + }), + getters: {}, + actions: { + setTheme(theme = '') { + if (theme === '') { + this.theme = app().template?.basic.theme || 'orange'; + } else { + this.theme = theme; + } + }, + }, + persist: { + enabled: true, + strategies: [ + { + key: 'sys-store', + }, + ], + }, +}); + +export default sys; diff --git a/sheep/store/user.js b/sheep/store/user.js new file mode 100644 index 0000000..fceb584 --- /dev/null +++ b/sheep/store/user.js @@ -0,0 +1,164 @@ +import { defineStore } from 'pinia'; +import $share from '@/sheep/platform/share'; +import { clone, cloneDeep } from 'lodash-es'; +import cart from './cart'; +import app from './app'; +import { showAuthModal } from '@/sheep/hooks/useModal'; +import UserApi from '@/sheep/api/member/user'; +import PayWalletApi from '@/sheep/api/pay/wallet'; +import OrderApi from '@/sheep/api/trade/order'; +import CouponApi from '@/sheep/api/promotion/coupon'; + +// 默认用户信息 +const defaultUserInfo = { + avatar: '', // 头像 + nickname: '', // 昵称 + gender: 0, // 性别 + mobile: '', // 手机号 + point: 0, // 积分 +}; + +// 默认钱包信息 +const defaultUserWallet = { + balance: 0, // 余额 +}; + +// 默认订单、优惠券等其他资产信息 +const defaultNumData = { + unusedCouponCount: 0, + orderCount: { + allCount: 0, + unpaidCount: 0, + undeliveredCount: 0, + deliveredCount: 0, + uncommentedCount: 0, + afterSaleCount: 0, + }, +}; + +const user = defineStore({ + id: 'user', + state: () => ({ + userInfo: clone(defaultUserInfo), // 用户信息 + userWallet: clone(defaultUserWallet), // 用户钱包信息 + isLogin: !!uni.getStorageSync('token'), // 登录状态 + numData: cloneDeep(defaultNumData), // 用户其他数据 + lastUpdateTime: 0, // 上次更新时间 + }), + + actions: { + // 获取用户信息 + async getInfo() { + const { code, data } = await UserApi.getUserInfo(); + if (code !== 0) { + return; + } + this.userInfo = data; + return Promise.resolve(data); + }, + + // 获得用户钱包 + async getWallet() { + const { code, data } = await PayWalletApi.getPayWallet(); + if (code !== 0) { + return; + } + this.userWallet = data; + }, + + // 获取订单、优惠券等其他资产信息 + getNumData() { + OrderApi.getOrderCount().then((res) => { + if (res.code === 0) { + this.numData.orderCount = res.data; + } + }); + CouponApi.getUnusedCouponCount().then((res) => { + if (res.code === 0) { + this.numData.unusedCouponCount = res.data; + } + }); + }, + + // 设置 token + setToken(token = '', refreshToken = '') { + if (token === '') { + this.isLogin = false; + uni.removeStorageSync('token'); + uni.removeStorageSync('refresh-token'); + } else { + this.isLogin = true; + uni.setStorageSync('token', token); + uni.setStorageSync('refresh-token', refreshToken); + this.loginAfter(); + } + return this.isLogin; + }, + + // 更新用户相关信息 (手动限流,5 秒之内不刷新) + async updateUserData() { + if (!this.isLogin) { + this.resetUserData(); + return; + } + // 防抖,5 秒之内不刷新 + const nowTime = new Date().getTime(); + if (this.lastUpdateTime + 5000 > nowTime) { + return; + } + this.lastUpdateTime = nowTime; + + // 获取最新信息 + await this.getInfo(); + this.getWallet(); + this.getNumData(); + return this.userInfo; + }, + + // 重置用户默认数据 + resetUserData() { + // 清空 token + this.setToken(); + // 清空用户相关的缓存 + this.userInfo = clone(defaultUserInfo); + this.userWallet = clone(defaultUserWallet); + this.numData = cloneDeep(defaultNumData); + // 清空购物车的缓存 + cart().emptyList(); + }, + + // 登录后,加载各种信息 + async loginAfter() { + await this.updateUserData(); + + // 加载购物车 + cart().getList(); + // 登录后设置全局分享参数 + $share.getShareInfo(); + + // 提醒绑定手机号 + if (app().platform.bind_mobile && !this.userInfo.mobile) { + showAuthModal('changeMobile'); + } + + // 绑定推广员 + $share.bindBrokerageUser(); + }, + + // 登出系统 + async logout() { + this.resetUserData(); + return !this.isLogin; + }, + }, + persist: { + enabled: true, + strategies: [ + { + key: 'user-store', + }, + ], + }, +}); + +export default user; diff --git a/sheep/ui/su-coupon/su-coupon.vue b/sheep/ui/su-coupon/su-coupon.vue new file mode 100644 index 0000000..23a76de --- /dev/null +++ b/sheep/ui/su-coupon/su-coupon.vue @@ -0,0 +1,320 @@ + + + + + diff --git a/sheep/ui/su-data-checkbox/su-data-checkbox.vue b/sheep/ui/su-data-checkbox/su-data-checkbox.vue new file mode 100644 index 0000000..537ead5 --- /dev/null +++ b/sheep/ui/su-data-checkbox/su-data-checkbox.vue @@ -0,0 +1,894 @@ + + + + + diff --git a/sheep/ui/su-dialog/su-dialog.vue b/sheep/ui/su-dialog/su-dialog.vue new file mode 100644 index 0000000..b53e9ce --- /dev/null +++ b/sheep/ui/su-dialog/su-dialog.vue @@ -0,0 +1,269 @@ + + + + + diff --git a/sheep/ui/su-fixed/su-fixed.vue b/sheep/ui/su-fixed/su-fixed.vue new file mode 100644 index 0000000..e2a9808 --- /dev/null +++ b/sheep/ui/su-fixed/su-fixed.vue @@ -0,0 +1,217 @@ + + + + + diff --git a/sheep/ui/su-image/su-image.vue b/sheep/ui/su-image/su-image.vue new file mode 100644 index 0000000..35f8410 --- /dev/null +++ b/sheep/ui/su-image/su-image.vue @@ -0,0 +1,130 @@ + + + + + diff --git a/sheep/ui/su-inner-navbar/su-inner-navbar.vue b/sheep/ui/su-inner-navbar/su-inner-navbar.vue new file mode 100644 index 0000000..9fc102d --- /dev/null +++ b/sheep/ui/su-inner-navbar/su-inner-navbar.vue @@ -0,0 +1,365 @@ + + + + + diff --git a/sheep/ui/su-navbar/su-navbar.vue b/sheep/ui/su-navbar/su-navbar.vue new file mode 100644 index 0000000..af96f24 --- /dev/null +++ b/sheep/ui/su-navbar/su-navbar.vue @@ -0,0 +1,483 @@ + + + + + + diff --git a/sheep/ui/su-notice-bar/su-notice-bar.vue b/sheep/ui/su-notice-bar/su-notice-bar.vue new file mode 100644 index 0000000..fc5075a --- /dev/null +++ b/sheep/ui/su-notice-bar/su-notice-bar.vue @@ -0,0 +1,473 @@ + + + + + + diff --git a/sheep/ui/su-number-box/su-number-box.vue b/sheep/ui/su-number-box/su-number-box.vue new file mode 100644 index 0000000..6b662cd --- /dev/null +++ b/sheep/ui/su-number-box/su-number-box.vue @@ -0,0 +1,225 @@ + + + diff --git a/sheep/ui/su-popover/su-popover.vue b/sheep/ui/su-popover/su-popover.vue new file mode 100644 index 0000000..adff127 --- /dev/null +++ b/sheep/ui/su-popover/su-popover.vue @@ -0,0 +1,314 @@ + + + + + diff --git a/sheep/ui/su-popup/keypress.js b/sheep/ui/su-popup/keypress.js new file mode 100644 index 0000000..6141c4c --- /dev/null +++ b/sheep/ui/su-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/sheep/ui/su-popup/su-popup.vue b/sheep/ui/su-popup/su-popup.vue new file mode 100644 index 0000000..b55b007 --- /dev/null +++ b/sheep/ui/su-popup/su-popup.vue @@ -0,0 +1,589 @@ + + + + diff --git a/sheep/ui/su-progress/su-progress.vue b/sheep/ui/su-progress/su-progress.vue new file mode 100644 index 0000000..4612705 --- /dev/null +++ b/sheep/ui/su-progress/su-progress.vue @@ -0,0 +1,203 @@ + + + + + diff --git a/sheep/ui/su-radio/su-radio.vue b/sheep/ui/su-radio/su-radio.vue new file mode 100644 index 0000000..6fc4f74 --- /dev/null +++ b/sheep/ui/su-radio/su-radio.vue @@ -0,0 +1,301 @@ + + + + + diff --git a/sheep/ui/su-region-picker/su-region-picker.vue b/sheep/ui/su-region-picker/su-region-picker.vue new file mode 100644 index 0000000..958fd11 --- /dev/null +++ b/sheep/ui/su-region-picker/su-region-picker.vue @@ -0,0 +1,247 @@ + + + + + + diff --git a/sheep/ui/su-status-bar/su-status-bar.vue b/sheep/ui/su-status-bar/su-status-bar.vue new file mode 100644 index 0000000..9af07f9 --- /dev/null +++ b/sheep/ui/su-status-bar/su-status-bar.vue @@ -0,0 +1,16 @@ + + + + + diff --git a/sheep/ui/su-sticky/su-sticky.vue b/sheep/ui/su-sticky/su-sticky.vue new file mode 100644 index 0000000..959318d --- /dev/null +++ b/sheep/ui/su-sticky/su-sticky.vue @@ -0,0 +1,266 @@ + + + + + diff --git a/sheep/ui/su-subline/su-subline.vue b/sheep/ui/su-subline/su-subline.vue new file mode 100644 index 0000000..c11d176 --- /dev/null +++ b/sheep/ui/su-subline/su-subline.vue @@ -0,0 +1,62 @@ + + + + + diff --git a/sheep/ui/su-swiper/su-swiper.vue b/sheep/ui/su-swiper/su-swiper.vue new file mode 100644 index 0000000..7465db1 --- /dev/null +++ b/sheep/ui/su-swiper/su-swiper.vue @@ -0,0 +1,502 @@ + + + + + diff --git a/sheep/ui/su-switch/su-switch.vue b/sheep/ui/su-switch/su-switch.vue new file mode 100644 index 0000000..d63e800 --- /dev/null +++ b/sheep/ui/su-switch/su-switch.vue @@ -0,0 +1,100 @@ + + + + + + diff --git a/sheep/ui/su-tab-item/su-tab-item.vue b/sheep/ui/su-tab-item/su-tab-item.vue new file mode 100644 index 0000000..615df2f --- /dev/null +++ b/sheep/ui/su-tab-item/su-tab-item.vue @@ -0,0 +1,169 @@ + + + + + + + diff --git a/sheep/ui/su-tab/su-tab.vue b/sheep/ui/su-tab/su-tab.vue new file mode 100644 index 0000000..17a7983 --- /dev/null +++ b/sheep/ui/su-tab/su-tab.vue @@ -0,0 +1,474 @@ + + + + + + + diff --git a/sheep/ui/su-tabbar-item/su-tabbar-item.vue b/sheep/ui/su-tabbar-item/su-tabbar-item.vue new file mode 100644 index 0000000..6807dfe --- /dev/null +++ b/sheep/ui/su-tabbar-item/su-tabbar-item.vue @@ -0,0 +1,237 @@ + + + + + + diff --git a/sheep/ui/su-tabbar/su-tabbar.vue b/sheep/ui/su-tabbar/su-tabbar.vue new file mode 100644 index 0000000..92e0352 --- /dev/null +++ b/sheep/ui/su-tabbar/su-tabbar.vue @@ -0,0 +1,227 @@ + + + + + + diff --git a/sheep/ui/su-tabs-item/props.js b/sheep/ui/su-tabs-item/props.js new file mode 100644 index 0000000..3908c36 --- /dev/null +++ b/sheep/ui/su-tabs-item/props.js @@ -0,0 +1,3 @@ +export default { + props: {}, +}; diff --git a/sheep/ui/su-tabs-item/su-tabs-item.vue b/sheep/ui/su-tabs-item/su-tabs-item.vue new file mode 100644 index 0000000..7139684 --- /dev/null +++ b/sheep/ui/su-tabs-item/su-tabs-item.vue @@ -0,0 +1,26 @@ + + + + + diff --git a/sheep/ui/su-tabs/su-tabs.vue b/sheep/ui/su-tabs/su-tabs.vue new file mode 100644 index 0000000..0cd0b93 --- /dev/null +++ b/sheep/ui/su-tabs/su-tabs.vue @@ -0,0 +1,435 @@ + + + + + diff --git a/sheep/ui/su-time-line/su-time-line.vue b/sheep/ui/su-time-line/su-time-line.vue new file mode 100644 index 0000000..9c3f8c1 --- /dev/null +++ b/sheep/ui/su-time-line/su-time-line.vue @@ -0,0 +1,37 @@ + + + + + diff --git a/sheep/ui/su-timeline-item/su-timeline-item.vue b/sheep/ui/su-timeline-item/su-timeline-item.vue new file mode 100644 index 0000000..ddf7a0b --- /dev/null +++ b/sheep/ui/su-timeline-item/su-timeline-item.vue @@ -0,0 +1,76 @@ + + + + + diff --git a/sheep/ui/su-toolbar/su-toolbar.vue b/sheep/ui/su-toolbar/su-toolbar.vue new file mode 100644 index 0000000..17351ec --- /dev/null +++ b/sheep/ui/su-toolbar/su-toolbar.vue @@ -0,0 +1,129 @@ + + + + + diff --git a/sheep/ui/su-video/components/dom-video.vue b/sheep/ui/su-video/components/dom-video.vue new file mode 100644 index 0000000..6f79a50 --- /dev/null +++ b/sheep/ui/su-video/components/dom-video.vue @@ -0,0 +1,213 @@ + + + + + + + + diff --git a/sheep/ui/su-video/su-video.vue b/sheep/ui/su-video/su-video.vue new file mode 100644 index 0000000..f52965f --- /dev/null +++ b/sheep/ui/su-video/su-video.vue @@ -0,0 +1,227 @@ + + + diff --git a/sheep/url/index.js b/sheep/url/index.js new file mode 100644 index 0000000..a7b82e4 --- /dev/null +++ b/sheep/url/index.js @@ -0,0 +1,200 @@ +import $store from '@/sheep/store'; +import { staticUrl } from '@/sheep/config'; + +const cdn = (url = '', cdnurl = '') => { + if (!url) return ''; + if (url.indexOf('http') === 0) { + return url; + } + if (cdnurl === '') { + cdnurl = $store('app').info.cdnurl; + } + return cdnurl + url; +}; +export default { + // 添加cdn域名前缀 + cdn, + // 对象存储自动剪裁缩略图 + thumb: (url = '', params) => { + url = cdn(url); + return append_thumbnail_params(url, params); + }, + // 静态资源地址 + static: (url = '', staticurl = '') => { + if (staticurl === '') { + staticurl = staticUrl; + } + if (staticurl !== 'local') { + url = cdn(url, staticurl); + } + return url; + }, + // css背景图片地址 + css: (url = '', staticurl = '') => { + if (staticurl === '') { + staticurl = staticUrl; + } + if (staticurl !== 'local') { + url = cdn(url, staticurl); + } + // #ifdef APP-PLUS + if (staticurl === 'local') { + url = plus.io.convertLocalFileSystemURL(url); + } + // #endif + return `url(${url})`; + }, +}; + +/** + * 追加对象存储自动裁剪/压缩参数 + * + * @return string + */ +function append_thumbnail_params(url, params) { + const filesystem = $store('app').info.filesystem; + if (filesystem === 'public') { + return url; + } + let width = params.width || '200'; // 宽度 + let height = params.height || '200'; // 高度 + let mode = params.mode || 'lfit'; // 缩放模式 + let quality = params.quality || 90; // 压缩质量 + let gravity = params.gravity || 'center'; // 剪裁质量 + let suffix = ''; + let crop_str = ''; + let quality_str = ''; + let size = width + 'x' + height; + switch (filesystem) { + case 'aliyun': + // 裁剪 + if (!gravity && gravity != 'center') { + // 指定了裁剪区域 + mode = 'mfit'; + crop_str = + '/crop,g_' + gravityFormatter('aliyun', gravity) + ',w_' + width + ',h_' + height; + } + + // 质量压缩 + if (quality > 0 && quality < 100) { + quality_str = '/quality,q_' + quality; + } + + // 缩放参数 + suffix = 'x-oss-process=image/resize,m_' + mode + ',w_' + width + ',h_' + height; + + // 拼接裁剪和质量压缩 + suffix += crop_str + quality_str; + break; + case 'qcloud': + let mode_str = 'thumbnail'; + if (mode == 'fill' || (!gravity && gravity != 'center')) { + // 指定了裁剪区域 + mode_str = 'crop'; + mode = 'fill'; + crop_str = '/gravity/' + gravityFormatter('qcloud', gravity); + } + + // 质量压缩 + if (quality > 0 && quality < 100) { + quality_str = '/rquality/' + quality; + } + + switch (mode) { + case 'lfit': + size = '' + size + '>'; + break; + case 'mfit': + size = '!' + size + 'r'; + case 'fill': + break; + case 'pad': + size = size + '/pad/1'; + break; + case 'fixed': + size = size + '!'; + break; + } + + suffix = 'imageMogr2/' + mode_str + '/' + size + crop_str + quality_str; + break; + case 'qiniu': + if (mode == 'fill' || (!gravity && gravity != 'center')) { + // 指定了裁剪区域,全部转为 mfit + mode = 'mfit'; + crop_str = '/gravity/' + gravityFormatter('qiniu', gravity) + '/crop/' + size; + } + // 质量压缩 + if (quality > 0 && quality < 100) { + quality_str = '/quality/' + quality; + } + + switch (mode) { + case 'lfit': + case 'pad': // 七牛不支持在缩放之后,尺寸不足时,填充背景色,所以这里和 lfit 模式一样 + size = size + '>'; + break; + case 'mfit': + size = '!' + size + 'r'; + break; + case 'fill': + // 会被转为 mfit + break; + case 'fixed': + size = size + '!'; + break; + } + + suffix = 'imageMogr2/thumbnail/' + size + crop_str + quality_str; + break; + } + return url + '?' + suffix; +} + +/** + * 裁剪区域格式转换 + * + * @param string $type aliyun|qcloud|qiniu + * @param string $gravity 统一的裁剪区域字符 + * + * @return string + */ +function gravityFormatter(type, gravity) { + let gravityFormatMap = { + aliyun: { + north_west: 'nw', // 左上 + north: 'north', // 中上 + north_east: 'ne', // 右上 + west: 'west', // 左中 + center: 'center', // 中部 + east: 'east', // 右中 + south_west: 'sw', // 左下 + south: 'south', // 中下 + south_east: 'se', // 右下 + }, + qcloud: { + northwest: 'nw', // 左上 + north: 'north', // 中上 + northeast: 'ne', // 右上 + west: 'west', // 左中 + center: 'center', // 中部 + east: 'east', // 右中 + southwest: 'sw', // 左下 + south: 'south', // 中下 + southeast: 'se', // 右下 + }, + qiniu: { + NorthWest: 'nw', // 左上 + North: 'north', // 中上 + NorthEast: 'ne', // 右上 + West: 'west', // 左中 + Center: 'center', // 中部 + East: 'east', // 右中 + SouthWest: 'sw', // 左下 + South: 'south', // 中下 + SouthEast: 'se', // 右下 + }, + }; + + return gravityFormatMap[type][gravity]; +} diff --git a/sheep/validate/form.js b/sheep/validate/form.js new file mode 100644 index 0000000..07b5725 --- /dev/null +++ b/sheep/validate/form.js @@ -0,0 +1,164 @@ +/** + * Validate v1.0.0 通用验证 + * @description 项目中用到的表单验证规则 + */ +import test from '@/sheep/helper/test.js'; + +// 手机号 +export const mobile = { + rules: [ + { + required: true, + errorMessage: '请输入手机号', + }, + { + validateFunction: function (rule, value, data, callback) { + if (!test.mobile(value)) { + callback('手机号码格式不正确'); + } + return true; + }, + }, + ], +}; + +// 密码 +export const password = { + rules: [ + { + required: true, + errorMessage: '请输入密码', + }, + { + validateFunction: function (rule, value, data, callback) { + if (!/^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]+\S{5,12}$/.test(value)) { + callback('需包含字母和数字,长度在6-12之间'); + } + return true; + }, + }, + ], +}; + +// 短信验证码 +export const code = { + rules: [ + { + required: true, + errorMessage: '请输入验证码', + }, + ], +}; + +// 真实姓名 +export const realName = { + rules: [ + { + required: true, + errorMessage: '请输入姓名', + }, + { + validateFunction: function (rule, value, data, callback) { + if (!test.chinese(value)) { + callback('请输入汉字'); + } + return true; + }, + }, + ], +}; + +export const taxName = { + rules: [ + { + required: true, + errorMessage: '请输入发票抬头名称', + }, + { + validateFunction: function (rule, value, data, callback) { + if (!test.chinese(value)) { + callback('请输入汉字'); + } + return true; + }, + }, + ], +}; + +// 税号 +export const taxNo = { + rules: [ + { + required: true, + errorMessage: '请输入税号', + }, + ], +}; + +// 开户行 +export const bankName = { + rules: [ + { + required: true, + errorMessage: '请输入开户行', + }, + { + validateFunction: function (rule, value, data, callback) { + if (!test.chinese(value)) { + callback('请输入汉字'); + } + return true; + }, + }, + ], +}; +// 银行卡号 +export const bankCode = { + rules: [ + { + required: true, + errorMessage: '请输入银行卡号', + }, + { + validateFunction: function (rule, value, data, callback) { + if (!test.number(value)) { + callback('请输入正确账号'); + } + return true; + }, + }, + ], +}; + +// 支付宝账号 +export const alipayAccount = { + rules: [ + { + required: true, + errorMessage: '请输入支付宝账号', + }, + { + validateFunction: function (rule, value, data, callback) { + let isEmail = test.email(value); + let isMobile = test.mobile(value); + + if (!isEmail && !isMobile) { + callback('请输入正确账号'); + } + return true; + }, + }, + ], +}; + +export default { + mobile, + alipayAccount, + bankCode, + bankName, + realName, + password, + code, + taxNo, + taxName, +}; diff --git a/static/activity-left.png b/static/activity-left.png new file mode 100644 index 0000000000000000000000000000000000000000..62e3b835ee5313f86e55b0a058df04c1729c280b GIT binary patch literal 440 zcmeAS@N?(olHy`uVBq!ia0vp^B|z-K!3-n?{wN*>QceLrA+A9B|MTnrpIwKN_s{=- zegn#e2tL04|M8XoPjCExcH{rkt6&CD{f+-mZvB691*p-^&EqD}6w#6(zhDN1`}6M! z#Mi&yUl8DMZhgb~efzopr0LnVe8UO$Q literal 0 HcmV?d00001 diff --git a/static/activity-right.png b/static/activity-right.png new file mode 100644 index 0000000000000000000000000000000000000000..e18a96c8314d3cb8e66478c8e06ed07688f2eb6a GIT binary patch literal 1400 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 KF{w53Nc)6RoWE+fOFf+`? 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 literal 0 HcmV?d00001 diff --git a/static/comment-empty.png b/static/comment-empty.png new file mode 100644 index 0000000000000000000000000000000000000000..810a3b1c00626a828b3102dd113d7336218588f6 GIT binary patch literal 5603 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 W$rPvHafW|CYfKC+4L<9+Cj1}5&Fp9Z literal 0 HcmV?d00001 diff --git a/static/coupon-empty.png b/static/coupon-empty.png new file mode 100644 index 0000000000000000000000000000000000000000..9e14d9d1907a882ea19c88e4d7392f94c4a20213 GIT binary patch literal 5245 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 Wu;Hoc{`Y%=!P3;uq}j+j_5T2+^XVf1 literal 0 HcmV?d00001 diff --git a/static/goods-empty.png b/static/goods-empty.png new file mode 100644 index 0000000000000000000000000000000000000000..bd42ecaa0e3ce2a02a7fbdb1e20857cb2f034158 GIT binary patch literal 3192 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 literal 0 HcmV?d00001 diff --git a/static/internet-empty.png b/static/internet-empty.png new file mode 100644 index 0000000000000000000000000000000000000000..55ac2f64c9d7a6480706339905a078fc1d05b7e1 GIT binary patch literal 7003 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 OATiW6(W$%#j`$y$>BZdu literal 0 HcmV?d00001 diff --git a/uni.scss b/uni.scss new file mode 100644 index 0000000..eadc5cd --- /dev/null +++ b/uni.scss @@ -0,0 +1,76 @@ +/** + * 这里是uni-app内置的常用样式变量 + * + * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量 + * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App + * + */ +@import '@/sheep/scss/_var.scss'; +/** + * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能 + * + * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件 + */ + +/* 颜色变量 */ + +/* 行为相关颜色 */ +$uni-color-primary: #007aff; +$uni-color-success: #4cd964; +$uni-color-warning: #f0ad4e; +$uni-color-error: #dd524d; + +/* 文字基本颜色 */ +$uni-text-color:#333;//基本色 +$uni-text-color-inverse:#fff;//反色 +$uni-text-color-grey:#999;//辅助灰色,如加载更多的提示信息 +$uni-text-color-placeholder: #808080; +$uni-text-color-disable:#c0c0c0; + +/* 背景颜色 */ +$uni-bg-color:#ffffff; +$uni-bg-color-grey:#f8f8f8; +$uni-bg-color-hover:#f1f1f1;//点击状态颜色 +$uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色 + +/* 边框颜色 */ +$uni-border-color:#e5e5e5; + +/* 尺寸变量 */ + +/* 文字尺寸 */ +$uni-font-size-sm:12px; +$uni-font-size-base:14px; +$uni-font-size-lg:16; + +/* 图片尺寸 */ +$uni-img-size-sm:20px; +$uni-img-size-base:26px; +$uni-img-size-lg:40px; + +/* Border Radius */ +$uni-border-radius-sm: 2px; +$uni-border-radius-base: 3px; +$uni-border-radius-lg: 6px; +$uni-border-radius-circle: 50%; + +/* 水平间距 */ +$uni-spacing-row-sm: 5px; +$uni-spacing-row-base: 10px; +$uni-spacing-row-lg: 15px; + +/* 垂直间距 */ +$uni-spacing-col-sm: 4px; +$uni-spacing-col-base: 8px; +$uni-spacing-col-lg: 12px; + +/* 透明度 */ +$uni-opacity-disabled: 0.3; // 组件禁用态的透明度 + +/* 文章场景相关 */ +$uni-color-title: #2C405A; // 文章标题颜色 +$uni-font-size-title:20px; +$uni-color-subtitle: #555555; // 二级标题颜色 +$uni-font-size-subtitle:26px; +$uni-color-paragraph: #3F536E; // 文章段落颜色 +$uni-font-size-paragraph:15px; diff --git a/uni_modules/lime-painter/changelog.md b/uni_modules/lime-painter/changelog.md new file mode 100644 index 0000000..588fa02 --- /dev/null +++ b/uni_modules/lime-painter/changelog.md @@ -0,0 +1,225 @@ +## 1.9.6.6(2024-09-25) +- fix: 修复background-position无效的问题 +## 1.9.6.5(2024-04-14) +- fix: 修复`nvue`无法生图的问题 +## 1.9.6.4(2024-03-10) +- fix: 修复代理ctx导致H5不能使用ctx.save +## 1.9.6.3(2024-03-08) +- fix: 修复支付宝真机无法使用的问题 +## 1.9.6.2(2024-02-22) +- fix: 修复使用render函数报错的问题 +## 1.9.6.1(2023-12-22) +- fix: 修复字节小程序非2d字体偏移 +- fix: 修复`canvasToTempFilePathSync`会触发两次的问题 +- fix: 修复`parser`图片没有宽度的问题 +## 1.9.6(2023-12-06) +- fix: 修复背景图受padding影响 +- fix: 修复因字节报错改了代理实现导致微信报错 +- 1.9.5.8(2023-11-16) +- fix: 修复margin问题 +- fix: 修复borderWidth问题 +- fix: 修复textBox问题 +- fix: 修复字节开发工具报`could not be cloned.`问题 +## 1.9.5.7(2023-07-27) +- fix: 去掉多余的方法 +- chore: 更新文档,增加自定义字体说明 +## 1.9.5.6(2023-07-21) +- feat: 有限的支持富文本 +- feat: H5和APP 增加 `hidpi` prop,主要用于大尺寸无法生成图片时用 +- fix: 修复 钉钉小程序 缺少 `measureText` 方法 +- chore: 由于微信小程序 pc 端的 canvas 2d 时不时抽风,故不使用canvas 2d +## 1.9.5.5(2023-06-27) +- fix: 修复把`emoji`表情字符拆分成多个字符的情况 +## 1.9.5.4(2023-06-05) +- fix: 修复因`canvasToTempFilePathSync`监听导致重复调用 +## 1.9.5.3(2023-05-23) +- fix: 因isPc错写成了isPC导致小程序PC不能生成图片 +## 1.9.5.2(2023-05-22) +- feat: 删除多余文件 +## 1.9.5.1(2023-05-22) +- fix: 修复 文字行数与`line-clamp`相同但不满一行时也加了省略号的问题 +## 1.9.5(2023-05-14) +- feat: 增加 `text-indent` 和 `calc` 方法 +- feat: 优化 布局时间 +## 1.9.4.4(2023-04-15) +- fix: 修复无法匹配负值 +- fix: 修复 Nvue IOS getImageInfo `useCORS` 为 undefined +## 1.9.4.3(2023-04-01) +- feat: 增加支持文字描边 `text-stroke: '5rpx #fff'` +## 1.9.4.2(2023-03-30) +- fix: 修复 支付宝小程序 isPC 在手机也为true的问题 +- feat: 由 微信开发工具 3060 版 无法获取图片尺寸,现 微信开发工具 3220 版 修复该问题,故还原上一版的获取图片方式。 +## 1.9.4.1(2023-03-28) +- fix: 修复固定高度不正确问题 +## 1.9.4(2023-03-17) +- fix: nvue ios getImageInfo缺少this报错 +- fix: pathType 非2d无效问题 +- fix: 修复 小米9se 可能会存在多次init 导致画面多次放大 +- fix: 修复 border 分开写 width style无效问题 +- fix: 修复 支付宝小程序IOS 再次进入不渲染的问题 +- fix: 修复 支付宝小程序安卓Zindex排序错乱问题 +- fix: 修复 微信开发工具 3060 版 无法获取图片的问题 +- feat: 把 for in 改为 forEach +- feat: 增加 hidden +- feat: 根节点 box-sizing 默认 `border-box` +- feat: 增加支持 `vw` `wh` +- chore: pathType 取消 默认值,因为字节开发工具不能显示 +- chore: 支付宝小程序开发工具不支持 生成图片 请以真机调试为准 +- bug: 企业微信 2.20.3无法使用 +## 1.9.3.5(2022-06-29) +- feat: justifyContent 增加 `space-around`、`space-between` +- feat: canvas 2d 也使用`getImageInfo` +- fix: 修复 `text`的 `text-decoration`错位 +## 1.9.3.4(2022-06-20) +- fix: 修复 因创建节点速度问题导致顺序出错。 +- fix: 修复 微信小程序 PC 无法显示本地图片 +- fix: 修复 flex-box 对齐问题 +- feat: 增加 `text-shadow` +- feat: 重写 `text` 对齐方式 +- chore: 更新文档 +## 1.9.3.3(2022-06-17) +- fix: 修复 支付宝小程序 canvas 2d 存在ctx.draw问题导致报错 +- fix: 修复 支付宝小程序 toDataURL 存在权限问题改用 `toTempFilePath` +- fix: 修复 支付宝小程序 image size 问题导致 `objectFit` 无效 +## 1.9.3.2(2022-06-14) +- fix: 修复 image 设置背景色不生效问题 +- fix: 修复 nvue 环境判断缺少参数问题 +## 1.9.3.1(2022-06-14) +- fix: 修复 bottom 定位不对问题 +- fix: 修复 因小数导致计算出错换行问题 +- feat: 增加 `useCORS` h5端图片跨域 在设置请求头无效果后试一下设置这个值 +- chore: 更新文档 +## 1.9.3(2022-06-13) +- feat: 增加 `zIndex` +- feat: 增加 `flex-box` 该功能处于原始阶段,非常简陋。 +- tips: QQ小程序 vue3 不支持, 为 uni 官方BUG +## 1.9.2.9(2022-06-10) +- fix: 修复`text-align`及`margin`居中问题 +## 1.9.2.8(2022-06-10) +- fix: 修复 Nvue `canvasToTempFilePathSync` 不生效问题 +## 1.9.2.7(2022-06-10) +- fix: 修复 margin及padding的bug +- fix: 修复 Nvue `isCanvasToTempFilePath` 不生效问题 +## 1.9.2.6(2022-06-09) +- fix: 修复 Nvue 不显示 +- feat: 增加支持字体渐变 +```html + +``` +## 1.9.2.5(2022-06-09) +- chore: 更变获取父级宽度的设定 +- chore: `pathType` 在canvas 2d 默认为 `url` +## 1.9.2.4(2022-06-08) +- fix: 修复 `pathType` 不生效问题 +## 1.9.2.3(2022-06-08) +- fix: 修复 `canvasToTempFilePath` 漏写 `success` 参数 +## 1.9.2.2(2022-06-07) +- chore: 更新文档 +## 1.9.2.1(2022-06-07) +- fix: 修复 vue3 赋值给this再传入导致image无法绘制 +- fix: 修复 `canvasToTempFilePathSync` 时机问题 +- feat: canvas 2d 更改图片生成方式 `toDataURL` +## 1.9.2(2022-05-30) +- fix: 修复 `canvasToTempFilePathSync` 在 vue3 下只生成一次 +## 1.9.1.7(2022-05-28) +- fix: 修复 `qrcode`显示不全问题 +## 1.9.1.6(2022-05-28) +- fix: 修复 `canvasToTempFilePathSync` 会重复多次问题 +- fix: 修复 `view` css `backgroundImage` 图片下载失败导致 子节点不渲染 +## 1.9.1.5(2022-05-27) +- fix: 修正支付宝小程序 canvas 2d版本号 2.7.15 +## 1.9.1.4(2022-05-22) +- fix: 修复字节小程序无法使用xml方式 +- fix: 修复字节小程序无法使用base64(非2D情况下工具上无法显示) +- fix: 修复支付宝小程序 `canvasToTempFilePath` 报错 +## 1.9.1.3(2022-04-29) +- fix: 修复vue3打包后uni对象为空后的报错 +## 1.9.1.2(2022-04-25) +- fix: 删除多余文件 +## 1.9.1.1(2022-04-25) +- fix: 修复图片不显示问题 +## 1.9.1(2022-04-12) +- fix: 因四舍五入导致有些机型错位 +- fix: 修复无views报错 +- chore: nvue下因ios无法读取插件内static文件,改由下载方式 +## 1.9.0(2022-03-20) +- fix: 因无法固定尺寸导致生成图片不全 +- fix: 特定情况下text判断无效 +- chore: 本地化APP Nvue webview +## 1.8.9(2022-02-20) +- fix: 修复 小程序下载最多10次并发的问题 +- fix: 修复 APP端无法获取本地图片 +- fix: 修复 APP Nvue端不执行问题 +- chore: 增加图片缓存机制 +## 1.8.8.8(2022-01-27) +- fix: 修复 主动调用尺寸问题 +## 1.8.8.6(2022-01-26) +- fix: 修复 nvue 下无宽度时获取父级宽度 +- fix: 修复 ios app 无法渲染问题 +## 1.8.8(2022-01-23) +- fix: 修复 主动调用时无节点问题 +- fix: 修复 `box-shadow` 颜色问题 +- fix: 修复 `transform:rotate` 角度位置问题 +- feat: 增加 `overflow:hidden` +## 1.8.7(2022-01-07) +- fix: 修复 image 方向为 `right` 时原始宽高问题 +- feat: 支持 view 设置背景图 `background-image: url(xxx)` +- chore: 去掉可选链 +## 1.8.6(2021-11-28) +- feat: 支持`view`对`inline-block`的子集使用`text-align` +## 1.8.5.5(2021-08-17) +- chore: 更新文档,删除 replace +- fix: 修复 text 值为 number时报错 +## 1.8.5.4(2021-08-16) +- fix: 字节小程序兼容 +## 1.8.5.3(2021-08-15) +- fix: 修复线性渐变与css现实效果不一致的问题 +- chore: 更新文档 +## 1.8.5.2(2021-08-13) +- chore: 增加`background-image`、`background-repeat` 能力,主要用于背景纹理的绘制,并不是代替`image`。例如:大面积的重复平铺的水印 +- 注意:这个功能H5暂时无法使用,因为[官方的API有BUG](https://ask.dcloud.net.cn/question/128793),待官方修复!!! +## 1.8.5.1(2021-08-10) +- fix: 修复因`margin`报错问题 +## 1.8.5(2021-08-09) +- chore: 增加margin支持`auto`,以达到居中效果 +## 1.8.4(2021-08-06) +- chore: 增加判断缓存文件条件 +- fix: 修复css 多余空格报错问题 +## 1.8.3(2021-08-04) +- tips: 1.6.x 以下的版本升级到1.8.x后要为每个元素都加上定位:position: 'absolute' +- fix: 修复只有一个view子元素时不计算高度的问题 +## 1.8.2(2021-08-03) +- fix: 修复 path-type 为 `url` 无效问题 +- fix: 修复 qrcode `text` 为空时报错问题 +- fix: 修复 image `src` 动态设置时不生效问题 +- feat: 增加 css 属性 `min-width` `max-width` +## 1.8.1(2021-08-02) +- fix: 修复无法加载本地图片 +## 1.8.0(2021-08-02) +- chore 文档更新 +- 使用旧版的同学不要升级! +## 1.8.0-beta(2021-07-30) +- ## 全新布局方式 不兼容旧版! +- chore: 布局方式变更 +- tips: 微信canvas 2d 不支持真机调试 +## 1.6.6(2021-07-09) +- chore: 统一命名规范,无须主动引入组件 +## 1.6.5(2021-06-08) +- chore: 去掉console +## 1.6.4(2021-06-07) +- fix: 修复 数字 为纯字符串时不转换的BUG +## 1.6.3(2021-06-06) +- fix: 修复 PC 端放大的BUG +## 1.6.2(2021-05-31) +- fix: 修复 报`adaptor is not a function`错误 +- fix: 修复 text 多行高度 +- fix: 优化 默认文字的基准线 +- feat: `@progress`事件,监听绘制进度 +## 1.6.1(2021-02-28) +- 删除多余节点 +## 1.6.0(2021-02-26) +- 调整为uni_modules目录规范 +- 修复:transform的rotate不能为负数问题 +- 新增:`pathType` 指定生成图片返回的路径类型,可选值有 `base64`、`url` diff --git a/uni_modules/lime-painter/components/common/relation.js b/uni_modules/lime-painter/components/common/relation.js new file mode 100644 index 0000000..6ed37e8 --- /dev/null +++ b/uni_modules/lime-painter/components/common/relation.js @@ -0,0 +1,150 @@ +const styles = (v ='') => v.split(';').filter(v => v && !/^[\n\s]+$/.test(v)).map(v => { + const key = v.slice(0, v.indexOf(':')) + const value = v.slice(v.indexOf(':')+1) + return { + [key + .replace(/-([a-z])/g, function() { return arguments[1].toUpperCase()}) + .replace(/\s+/g, '') + ]: value.replace(/^\s+/, '').replace(/\s+$/, '') || '' + } + }) +export function parent(parent) { + return { + provide() { + return { + [parent]: this + } + }, + data() { + return { + el: { + id: null, + css: {}, + views: [] + }, + } + }, + watch: { + css: { + handler(v) { + if(this.canvasId) { + this.el.css = (typeof v == 'object' ? v : v && Object.assign(...styles(v))) || {} + this.canvasWidth = this.el.css && this.el.css.width || this.canvasWidth + this.canvasHeight = this.el.css && this.el.css.height || this.canvasHeight + } + }, + immediate: true + } + } + } +} +export function children(parent, options = {}) { + const indexKey = options.indexKey || 'index' + return { + inject: { + [parent]: { + default: null + } + }, + watch: { + el: { + handler(v, o) { + if(JSON.stringify(v) != JSON.stringify(o)) + this.bindRelation() + }, + deep: true, + immediate: true + }, + src: { + handler(v, o) { + if(v != o) + this.bindRelation() + }, + immediate: true + }, + text: { + handler(v, o) { + if(v != o) this.bindRelation() + }, + immediate: true + }, + css: { + handler(v, o) { + if(v != o) + this.el.css = (typeof v == 'object' ? v : v && Object.assign(...styles(v))) || {} + }, + immediate: true + }, + replace: { + handler(v, o) { + if(JSON.stringify(v) != JSON.stringify(o)) + this.bindRelation() + }, + deep: true, + immediate: true + } + }, + created() { + if(!this._uid) { + this._uid = this._.uid + } + Object.defineProperty(this, 'parent', { + get: () => this[parent] || [], + }) + Object.defineProperty(this, 'index', { + get: () => { + this.bindRelation(); + const {parent: {el: {views=[]}={}}={}} = this + return views.indexOf(this.el) + }, + }); + this.el.type = this.type + if(this.uid) { + this.el.uid = this.uid + } + this.bindRelation() + }, + // #ifdef VUE3 + beforeUnmount() { + this.removeEl() + }, + // #endif + // #ifdef VUE2 + beforeDestroy() { + this.removeEl() + }, + // #endif + methods: { + removeEl() { + if (this.parent) { + this.parent.el.views = this.parent.el.views.filter( + (item) => item._uid !== this._uid + ); + } + }, + bindRelation() { + if(!this.el._uid) { + this.el._uid = this._uid + } + if(['text','qrcode'].includes(this.type)) { + this.el.text = this.$slots && this.$slots.default && this.$slots.default[0].text || `${this.text || ''}`.replace(/\\n/g, '\n') + } + if(this.type == 'image') { + this.el.src = this.src + } + if (!this.parent) { + return; + } + let views = this.parent.el.views || []; + if(views.indexOf(this.el) !== -1) { + this.parent.el.views = views.map(v => v._uid == this._uid ? this.el : v) + } else { + this.parent.el.views = [...views, this.el]; + } + } + }, + mounted() { + // this.bindRelation() + }, + } +} \ No newline at end of file diff --git a/uni_modules/lime-painter/components/l-painter-image/l-painter-image.vue b/uni_modules/lime-painter/components/l-painter-image/l-painter-image.vue new file mode 100644 index 0000000..e24e3aa --- /dev/null +++ b/uni_modules/lime-painter/components/l-painter-image/l-painter-image.vue @@ -0,0 +1,28 @@ + + + + + diff --git a/uni_modules/lime-painter/components/l-painter-qrcode/l-painter-qrcode.vue b/uni_modules/lime-painter/components/l-painter-qrcode/l-painter-qrcode.vue new file mode 100644 index 0000000..a73e5ed --- /dev/null +++ b/uni_modules/lime-painter/components/l-painter-qrcode/l-painter-qrcode.vue @@ -0,0 +1,27 @@ + + + + + diff --git a/uni_modules/lime-painter/components/l-painter-text/l-painter-text.vue b/uni_modules/lime-painter/components/l-painter-text/l-painter-text.vue new file mode 100644 index 0000000..b332b02 --- /dev/null +++ b/uni_modules/lime-painter/components/l-painter-text/l-painter-text.vue @@ -0,0 +1,33 @@ + + + + + diff --git a/uni_modules/lime-painter/components/l-painter-view/l-painter-view.vue b/uni_modules/lime-painter/components/l-painter-view/l-painter-view.vue new file mode 100644 index 0000000..94596e5 --- /dev/null +++ b/uni_modules/lime-painter/components/l-painter-view/l-painter-view.vue @@ -0,0 +1,34 @@ + + + + + diff --git a/uni_modules/lime-painter/components/l-painter/l-painter.vue b/uni_modules/lime-painter/components/l-painter/l-painter.vue new file mode 100644 index 0000000..83926fd --- /dev/null +++ b/uni_modules/lime-painter/components/l-painter/l-painter.vue @@ -0,0 +1,461 @@ + + + + diff --git a/uni_modules/lime-painter/components/l-painter/nvue.js b/uni_modules/lime-painter/components/l-painter/nvue.js new file mode 100644 index 0000000..25645fb --- /dev/null +++ b/uni_modules/lime-painter/components/l-painter/nvue.js @@ -0,0 +1,214 @@ +// #ifdef APP-NVUE +import { + sleep, + getImageInfo, + isBase64, + networkReg +} from './utils'; +const dom = weex.requireModule('dom') +import { + version +} from '../../package.json' + +export default { + data() { + return { + tempFilePath: [], + isInitFile: false, + osName: uni.getSystemInfoSync().osName + } + }, + methods: { + getParentWeith() { + return new Promise(resolve => { + dom.getComponentRect(this.$refs.limepainter, (res) => { + this.parentWidth = Math.ceil(res.size.width) + this.canvasWidth = this.canvasWidth || this.parentWidth || 300 + this.canvasHeight = res.size.height || this.canvasHeight || 150 + resolve(res.size) + }) + }) + }, + onPageFinish() { + this.webview = this.$refs.webview + this.webview.evalJS(`init(${this.dpr})`) + }, + onMessage(e) { + const res = e.detail.data[0] || null; + if (res.event) { + if (res.event == 'inited') { + this.inited = true + } + if (res.event == 'fail') { + this.$emit('fail', res) + } + if (res.event == 'layoutChange') { + const data = typeof res.data == 'string' ? JSON.parse(res.data) : res.data + this.canvasWidth = Math.ceil(data.width); + this.canvasHeight = Math.ceil(data.height); + } + if (res.event == 'progressChange') { + this.progress = res.data * 1 + } + if (res.event == 'file') { + this.tempFilePath.push(res.data) + if (this.tempFilePath.length > 7) { + this.tempFilePath.shift() + } + return + } + if (res.event == 'success') { + if (res.data) { + this.tempFilePath.push(res.data) + if (this.tempFilePath.length > 8) { + this.tempFilePath.shift() + } + if (this.isCanvasToTempFilePath) { + this.setFilePath(this.tempFilePath.join(''), { + isEmit: true + }) + } + } else { + this.$emit('fail', 'canvas no data') + } + return + } + this.$emit(res.event, JSON.parse(res.data)); + } else if (res.file) { + this.file = res.data; + } else { + console.info(res[0]) + } + }, + getWebViewInited() { + if (this.inited) return Promise.resolve(this.inited); + return new Promise((resolve) => { + this.$watch( + 'inited', + async val => { + if (val) { + resolve(val) + } + }, { + immediate: true + } + ); + }) + }, + getTempFilePath() { + if (this.tempFilePath.length == 8) return Promise.resolve(this.tempFilePath) + return new Promise((resolve) => { + this.$watch( + 'tempFilePath', + async val => { + if (val.length == 8) { + resolve(val.join('')) + } + }, { + deep: true + } + ); + }) + }, + getWebViewDone() { + if (this.progress == 1) return Promise.resolve(this.progress); + return new Promise((resolve) => { + this.$watch( + 'progress', + async val => { + if (val == 1) { + this.$emit('done') + this.done = true + this.runTask() + resolve(val) + } + }, { + immediate: true + } + ); + }) + }, + async render(args) { + try { + await this.getSize(args) + const { + width + } = args.css || args + if (!width && this.parentWidth) { + Object.assign(args, { + width: this.parentWidth + }) + } + const newNode = await this.calcImage(args); + await this.getWebViewInited() + this.webview.evalJS(`source(${JSON.stringify(newNode)})`) + await this.getWebViewDone() + await sleep(this.afterDelay) + if (this.isCanvasToTempFilePath) { + const params = { + fileType: this.fileType, + quality: this.quality + } + this.webview.evalJS(`save(${JSON.stringify(params)})`) + } + return Promise.resolve() + } catch (e) { + this.$emit('fail', e) + } + }, + async calcImage(args) { + let node = JSON.parse(JSON.stringify(args)) + const urlReg = /url\((.+)\)/ + const { + backgroundImage + } = node.css || {} + const isBG = backgroundImage && urlReg.exec(backgroundImage)[1] + const url = node.url || node.src || isBG + if (['text', 'qrcode'].includes(node.type)) { + return node + } + if ((node.type === "image" || isBG) && url && !isBase64(url) && (this.osName == 'ios' || !networkReg + .test(url))) { + let { + path + } = await getImageInfo(url, true) + if (isBG) { + node.css.backgroundImage = `url(${path})` + } else { + node.src = path + } + } else if (node.views && node.views.length) { + for (let i = 0; i < node.views.length; i++) { + node.views[i] = await this.calcImage(node.views[i]) + } + } + return node + }, + async canvasToTempFilePath(args = {}) { + if (!this.inited) { + return this.$emit('fail', 'no init') + } + this.tempFilePath = [] + if (args.fileType == 'jpg') { + args.fileType = 'jpeg' + } + + this.webview.evalJS(`save(${JSON.stringify(args)})`) + try { + let tempFilePath = await this.getTempFilePath() + + tempFilePath = await this.setFilePath(tempFilePath, args) + args.success({ + errMsg: "canvasToTempFilePath:ok", + tempFilePath + }) + } catch (e) { + console.log('e', e) + args.fail({ + error: e + }) + } + } + } +} +// #endif \ No newline at end of file diff --git a/uni_modules/lime-painter/components/l-painter/painter.js b/uni_modules/lime-painter/components/l-painter/painter.js new file mode 100644 index 0000000..a8e3483 --- /dev/null +++ b/uni_modules/lime-painter/components/l-painter/painter.js @@ -0,0 +1 @@ +var t=function(){return t=Object.assign||function(t){for(var e,i=1,n=arguments.length;i0&&r[r.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!r||o[1]>r[0]&&o[1]=360&&(s-=360);s<0&&(s+=360);if(0===(s=Math.round(s)))return{x0:Math.round(e/2)+n,y0:i+r,x1:Math.round(e/2)+n,y1:r};if(180===s)return{x0:Math.round(e/2)+n,y0:r,x1:Math.round(e/2)+n,y1:i+r};if(90===s)return{x0:n,y0:Math.round(i/2)+r,x1:e+n,y1:Math.round(i/2)+r};if(270===s)return{x0:e+n,y0:Math.round(i/2)+r,x1:n,y1:Math.round(i/2)+r};var a=Math.round(180*Math.asin(e/Math.sqrt(Math.pow(e,2)+Math.pow(i,2)))/Math.PI);if(s===a)return{x0:n,y0:i+r,x1:e+n,y1:r};if(s===180-a)return{x0:n,y0:r,x1:e+n,y1:i+r};if(s===180+a)return{x0:e+n,y0:r,x1:n,y1:i+r};if(s===360-a)return{x0:e+n,y0:i+r,x1:n,y1:r};var h=0,c=0,f=0,l=0;if(s180-a&&s<180||s>180&&s<180+a||s>360-a){var d=s*Math.PI/180,u=s360-a?i/2:-i/2,p=Math.tan(d)*u,g=s180-a&&s<180?e/2-p:-e/2-p;h=-(f=p+(v=Math.pow(Math.sin(d),2)*g)),c=-(l=u+v/Math.tan(d))}if(s>a&&s<90||s>90&&s<90+a||s>180+a&&s<270||s>270&&s<360-a){var v;d=(90-s)*Math.PI/180,p=s>a&&s<90||s>90&&s<90+a?e/2:-e/2,u=Math.tan(d)*p,g=s>a&&s<90||s>270&&s<360-a?i/2-u:-i/2-u;h=-(f=p+(v=Math.pow(Math.sin(d),2)*g)/Math.tan(d)),c=-(l=u+v)}return h=Math.round(h+e/2)+n,c=Math.round(i/2-c)+r,f=Math.round(f+e/2)+n,l=Math.round(i/2-l)+r,{x0:h,y0:c,x1:f,y1:l}}(r,t,e,i,n),a=s.x0,h=s.y0,c=s.x1,f=s.y1,l=o.createLinearGradient(a,h,c,f),d=r.match(/linear-gradient\((.+)\)/)[1],u=R(d.substring(d.indexOf(",")+1)),p=0;pt.length)&&(e=t.length);for(var i=0,n=new Array(e);i=t.length?{done:!0}:{done:!1,value:t[n++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function H(t){return"number"==typeof t}function D(t){return"auto"===t||null===t}function $(t){return/%$/.test(t)}var Y=p,U=u,N=d,X=g,_=y,q=w,G=m;function V(t){return t.replace(/-([a-z])/g,(function(t,e){return e.toUpperCase()}))}function J(t,e){var i,n,o=function(t){var e=t.match(/([a-z]+)/)[1];return[e,V(t.split(e)[1])]}(t),s=o[0],a=o[1],h=e.split(" ");if(a)return(i={})[s+a]=e,i;if(h.length&&!a){var c=h[0],f=h[1],l=h[2],d=h[3];return(n={})[s+r[0]]=c,n[s+r[1]]=f||c,n[s+r[2]]=l||c,n[s+r[3]]=d||f||c,n}}function Q(t){t=t.trim();for(var e=new Array,i="+",n="",r=t.length,o=0;o0;)"("===t[a+=1]&&(s+=1),")"===t[a]&&(s-=1);n="".concat(Q(t.slice(o+1,a))),o=a}if(isNaN(Number(t[o]))&&"."!==t[o]||o===r-1){var h=parseFloat(n);switch(i){case"+":e.push(h);break;case"-":e.push(-h);break;case"*":e.push(e.pop()*h);break;case"/":e.push(e.pop()/h)}i=t[o],n=""}}for(var c=0;e.length;)c+=e.pop();return c}var Z,K=0,et=function(){function t(){F(this,"elements",[]),F(this,"afterElements",[]),F(this,"beforeElements",[]),F(this,"ids",[]),F(this,"width",0),F(this,"height",0),F(this,"top",0),F(this,"left",0),F(this,"pre",null),F(this,"offsetX",0),F(this,"offsetY",0),K++,this.id=K}var e=t.prototype;return e.fixedBind=function(t,e){void 0===e&&(e=0),this.container=e?t.parent:t.root,this.container.fixedLine=this,this.fixedAdd(t)},e.fixedAdd=function(t){if(!this.ids.includes(t.id)){this.ids.push(t.id),this.elements.push(t);var e=t.computedStyle.zIndex;(void 0===e?0:e)>=0?this.afterElements.push(t):this.beforeElements.push(t),this.refreshLayout()}},e.bind=function(t){this.container=t.parent,this.container.line=null,this.container.lines?(this.container.lines.push(this),this.pre=this.getPreLine(),this.top=this.pre.top+this.pre.height,this.left=this.container.contentSize.left):(this.top=this.container.contentSize.top,this.left=this.container.contentSize.left,this.container.lines=[this]),this.isInline=t.isInline(),this.container.line=this,this.outerWidth=t.parent&&t.parent.contentSize.width?t.parent.contentSize.width:1/0,this.add(t)},e.getPreLine=function(){return this.container.lines[this.container.lines.length-2]},e.canIEnter=function(t){return!((100*t.offsetSize.width+100*this.width)/100>this.outerWidth)||(this.closeLine(),!1)},e.closeLine=function(){delete this.container.line},e.add=function(t){this.ids.includes(t.id)||(this.ids.push(t.id),this.elements.push(t),this.refreshWidthHeight(t))},e.refreshWidthHeight=function(t){t.offsetSize.height>this.height&&(this.height=t.offsetSize.height),this.width+=t.offsetSize.width||0,(this.container.lineMaxWidth||0)this[this.key.height]&&(this.container[this.key.lineMaxHeight]=this[this.key.height]=i),this[this.key.width]+=this.getWidth(t.offsetSize);var n=Math.min(this.getWidth(this),!this.getWidth(this.container.contentSize)&&1/0);(this.container[this.key.lineMaxWidth]||0)1)return 0;var e=t.style.alignSelf,i=this.getHeight(this.container.contentSize),n=i-this.getHeight(t.offsetSize);return"flex-end"===e?n:"center"===e?n/2:"stretch"===e?(n&&t.name==d&&(t.style[this.key.width]=this.getWidth(t.offsetSize),t.style[this.key.height]=i,delete t.line,delete t.lines,t.getBoxWidthHeight()),0):0},r.layout=function(t,e){var i=this;this.refreshXAlign(),this.pre?(this.top=this.pre.top+this.pre.height+this.offsetY,this.left=e+this.offsetX):(this.top=Math.max(this.top,this.container.contentSize.top,t)+this.offsetY,this.left=Math.max(this.left,this.container.contentSize.left,e)+this.offsetX),this.elements.forEach((function(t,e){i.setIndent(t);var n=i.elements[e-1],r=i.getOffsetY(t);t.style[i.key.top]=i[i.key.top]+r,t.style[i.key.left]=n?n.offsetSize[i.key.left]+i.getWidth(n.offsetSize):i[i.key.left],t.getBoxPosition()}))},n}(et),rt=p,ot=u,st=d,at=v,ht=y,ct=b,ft=w,lt=m,dt=0,ut={left:null,top:null,width:null,height:null},pt=new Map,gt=function(){function t(t,e,i,n){var o=this;F(this,"id",dt++),F(this,"style",{left:null,top:null,width:null,height:null}),F(this,"computedStyle",{}),F(this,"originStyle",{}),F(this,"children",{}),F(this,"layoutBox",A({},ut)),F(this,"contentSize",A({},ut)),F(this,"clientSize",A({},ut)),F(this,"borderSize",A({},ut)),F(this,"offsetSize",A({},ut)),this.ctx=n,this.root=i,e&&(this.parent=e),this.name=t.type||t.name,this.attributes=this.getAttributes(t);var s=function(t,e){var i,n=["color","fontSize","lineHeight","verticalAlign","fontWeight","textAlign"],o=t.type,s=void 0===o?N:o,a=t.styles,h=void 0===a?{}:a,c=(e||{}).computedStyle,f=Object.assign({},S);if([U,Y,X].includes(s)&&!h.display&&(f.display=_),c)for(var l=0;l=0&&l<0,$=c>=0&&u<0;return i==a[0]&&(this[i].left=t.left+s+v+E+(D?2*-l:0),this[i].top=t.top+c+x+W+($?2*-u:0),this[i].width=t.width+(this[i].widthAdd?0:C),this[i].height=t.height+(this[i].heightAdd?0:H),this[i].widthAdd=C,this[i].heightAdd=H),i==a[1]&&(this[i].left=t.left+s+E+(D<0?-l:0),this[i].top=t.top+c+W+($?-u:0),this[i].width=t.width+v+w,this[i].height=t.height+x+S),i==a[2]&&(this[i].left=t.left+s+E/2+(D<0?-l:0),this[i].top=t.top+c+W/2+($?-u:0),this[i].width=t.width+v+w+E/2+F/2,this[i].height=t.height+x+S+T/2+W/2),i==a[3]&&(this[i].left=t.left+(D<0?-l:0),this[i].top=t.top+($?-u:0),this[i].width=t.width+v+w+E+F+s+l,this[i].height=t.height+x+S+T+W+u+c),this[i]},e.layoutBoxUpdate=function(t,e,i,n){var r=this;if(void 0===i&&(i=-1),"border-box"==e.boxSizing){var o=e||{},s=o.border,h=(s=void 0===s?{}:s).borderWidth,c=void 0===h?0:h,f=o.borderTop,l=(f=void 0===f?{}:f).borderTopWidth,d=void 0===l?c:l,u=o.borderBottom,p=(u=void 0===u?{}:u).borderBottomWidth,g=void 0===p?c:p,v=o.borderRight,y=(v=void 0===v?{}:v).borderRightWidth,x=void 0===y?c:y,b=o.borderLeft,w=(b=void 0===b?{}:b).borderLeftWidth,m=void 0===w?c:w,S=o.padding,z=(S=void 0===S?{}:S).paddingTop,I=void 0===z?0:z,M=S.paddingRight,k=void 0===M?0:M,B=S.paddingBottom,W=void 0===B?0:B,P=S.paddingLeft,O=void 0===P?0:P;i||(t.width-=O+k+x+m),1!==i||n||(t.height-=I+W+d+g)}this.layoutBox&&(a.forEach((function(i){return r.layoutBox[i]=r.getOffsetSize(t,e,i)})),this.layoutBox=Object.assign({},this.layoutBox,this.layoutBox.borderSize))},e.getBoxPosition=function(){var t=this.computedStyle,e=this.fixedLine,i=this.lines,n=t.left,r=void 0===n?0:n,o=t.top,s=void 0===o?0:o,a=A({},this.contentSize,{left:r,top:s}),h=this.contentSize.top-this.offsetSize.top,c=this.contentSize.left-this.offsetSize.left;if(this.root.fixedLine&&!this.root.isDone){this.root.isDone=!0;for(var f,l=C(this.root.fixedLine.elements);!(f=l()).done;){var d=f.value;d.setPosition(d,this.root.offsetSize),d.getBoxPosition()}}if(e)for(var u,p=C(e.elements);!(u=p()).done;){var g=u.value,v=A({},this.borderSize,{left:r,top:s});g.setPosition(g,v);var y=this.borderSize.top-this.offsetSize.top,x=this.borderSize.left-this.offsetSize.left;g.style.left+=r+x,g.style.top+=s+y,g.getBoxPosition()}if(i)for(var b,w=C(i);!(b=w()).done;){b.value.layout(a.top+h,a.left+c)}return this.layoutBoxUpdate(a,t),this.layoutBox},e.getBoxState=function(t,e){return this.isBlock(t)||this.isBlock(e)},e.isBlock=function(t){return void 0===t&&(t=this),t&&t.style.display==at},e.isFlex=function(t){return void 0===t&&(t=this),t&&t.style.display==ct},e.isInFlow=function(){return!(this.isAbsolute||this.isFixed)},e.inFlexBox=function(t){return void 0===t&&(t=this),!!t.isInFlow()&&(!!t.parent&&(!(!t.parent||t.parent.style.display!==ct)||void 0))},e.isInline=function(t){return void 0===t&&(t=this),t&&t.style.display==ht},e.contrastSize=function(t,e,i){var n=t;return i&&(n=Math.min(n,i)),e&&(n=Math.max(n,e)),n},e.measureText=function(t,e){var i=this.ctx.measureText(t),n=i.width,r=i.actualBoundingBoxAscent,o=i.actualBoundingBoxDescent;return{ascent:r,descent:o,width:n,fontHeight:r+o||.7*e+1}},e.getParentSize=function(t,e){if(void 0===t&&(t=this),void 0===e&&(e=!1),t&&t.parent){if(t.parent.contentSize.width)return t.parent.contentSize;if(e)return this.getParentSize(t.parent,e)}return null},e.getBoxWidthHeight=function(){var t=this,e=this.name,i=this.computedStyle,n=this.attributes,r=this.parent,o=void 0===r?{}:r,s=this.ctx,a=this.getChildren(),h=i.left,c=void 0===h?0:h,f=i.top,l=void 0===f?0:f,d=i.bottom,u=i.right,p=i.width,g=void 0===p?0:p,v=i.minWidth,y=i.maxWidth,x=i.minHeight,b=i.maxHeight,w=i.height,m=void 0===w?0:w,S=i.fontSize,z=i.fontWeight,I=i.fontFamily,M=i.fontStyle,k=i.position;i.textIndent;var B=i.lineClamp,P=i.lineHeight,O=i.padding,T=void 0===O?{}:O,L=i.margin,R=void 0===L?{}:L,F=i.border,A=(F=void 0===F?{}:F).borderWidth,j=void 0===A?0:A,E=i.borderRight,C=(E=void 0===E?{}:E).borderRightWidth,H=void 0===C?j:C,Y=i.borderLeft,U=(Y=void 0===Y?{}:Y).borderLeftWidth,N=void 0===U?j:U,X=o.contentSize&&o.contentSize.width,_=o.contentSize&&o.contentSize.height;if($(g)&&X&&(g=W(g,X)),$(g)&&!X&&(g=null),$(m)&&_&&(m=W(m,_)),$(m)&&!_&&(m=null),$(v)&&X&&(v=W(v,X)),$(y)&&X&&(y=W(y,X)),$(x)&&_&&(x=W(x,_)),$(b)&&_&&(b=W(b,_)),i.padding&&X)for(var q in i.padding)Object.hasOwnProperty.call(T,q)&&(T[q]=W(T[q],X));var G=T.paddingRight,V=void 0===G?0:G,J=T.paddingLeft,Q=void 0===J?0:J;if(i.margin&&[R.marginLeft,R.marginRight].includes("auto"))if(g){var Z=X&&X-g-V-Q-N-H||0;R.marginLeft==R.marginRight?R.marginLeft=R.marginRight=Z/2:D(R.marginLeft)?R.marginLeft=Z:R.marginRight=Z}else R.marginLeft=R.marginRight=0;var K=R.marginRight,tt=void 0===K?0:K,it=R.marginLeft,at={width:g,height:m,left:0,top:0},ht=Q+V+N+H+(void 0===it?0:it)+tt;if(this.offsetWidth=ht,e==ot&&!this.attributes.widths){var ct=n.text||"";s.save(),s.setFonts({fontFamily:I,fontSize:S,fontWeight:z,fontStyle:M}),ct.length,"\n"==ct&&(ct="",this.isBr=!0),(""+ct).split("\n").map((function(e){var i=Array.from(e).map((function(e){var i=""+(/^[\u4e00-\u9fa5]+$/.test(e)?"cn":e)+I+S+z+M,n=pt.get(i);if(n)return{width:n,text:e};var r=t.measureText(e,S).width;return pt.set(i,r),{width:r,text:e}})),n=t.measureText(e,S),r=n.fontHeight,o=n.ascent,s=n.descent;t.attributes.fontHeight=r,t.attributes.ascent=o,t.attributes.descent=s,t.attributes.widths||(t.attributes.widths=[]),t.attributes.widths.push({widths:i,total:i.reduce((function(t,e){return t+e.width}),0)})})),s.restore()}if(e==rt&&null==g){var lt=n.width,dt=n.height;at.width=this.contrastSize(Math.round(lt*m/dt)||0,v,y),this.layoutBoxUpdate(at,i,0)}if(e==ot&&null==g){var ut=this.attributes.widths,gt=Math.max.apply(Math,ut.map((function(t){return t.total})));if(o&&X>0&&(gt>X||this.isBlock(this))&&!this.isAbsolute&&!this.isFixed)gt=X;at.width=this.contrastSize(gt,v,y),this.layoutBoxUpdate(at,i,0)}if(e==ot&&(o.style.flex||!this.attributes.lines)){var vt=this.attributes.widths.length;this.attributes.widths.forEach((function(t){return t.widths.reduce((function(t,e,i){return t+e.width>at.width?(vt++,e.width):t+e.width}),0)})),vt=B&&vt>B?B:vt,this.attributes.lines=vt}if(e==rt&&null==m){var yt=n.width,xt=n.height;n.text,at.height=this.contrastSize(W(at.width*xt/yt)||0,x,b),this.layoutBoxUpdate(at,i,1)}e==ot&&null==m&&(P=W(P,S),at.height=this.contrastSize(W(this.attributes.lines*P),x,b),this.layoutBoxUpdate(at,i,1,!0)),!g&&o&&o.children&&X&&(!this.isFlex(o)||o.isFlexCalc)&&([st,ot].includes(e)&&this.isFlex()||e==st&&this.isBlock(this)&&this.isInFlow())&&(at.width=this.contrastSize(X-(o.isFlexCalc?0:ht),v,y),this.layoutBoxUpdate(at,i)),g&&!$(g)&&(at.width=this.contrastSize(g,v,y),this.layoutBoxUpdate(at,i,0)),m&&!$(m)&&(at.height=this.contrastSize(at.height,x,b),this.layoutBoxUpdate(at,i,1));var bt=0;if(a.length){var wt=null,mt=!1;a.forEach((function(e,n){e.getBoxWidthHeight();var r=a[n+1];if(r&&r.isInFlow()&&(e.next=r),!t.line||!t.line.ids.includes(e.id))if(e.isInFlow()&&!e.inFlexBox()){var o=t.getBoxState(wt,e);if(e.isBr)return mt=!0;t.line&&t.line.canIEnter(e)&&!o&&!mt?t.line.add(e):(mt=!1,(new et).bind(e)),wt=e}else e.inFlexBox()?t.line&&(t.line.canIEnter(e)||"nowrap"==i.flexWrap)?t.line.add(e):(new nt).bind(e):e.isFixed?t.root.fixedLine?t.root.fixedLine.fixedAdd(e):(new et).fixedBind(e):t.fixedLine?t.fixedLine.fixedAdd(e):(new et).fixedBind(e,1)})),this.lines&&(bt=this.lines.reduce((function(t,e){return t+e.height}),0))}var St=0,zt=0;if(!g&&(this.isAbsolute||this.isFixed)&&X){var It=k==ft?X:this.root.width,Mt=It-($(c)?W(c,It):c)-($(u)?W(u,It):u);St=i.left?Mt:this.lineMaxWidth}if(!m&&(null!=l?l:this.isAbsolute||this.isFixed&&_)){var kt=k==ft?_:this.root.height,Bt=kt-($(l)?W(l,kt):l)-($(d)?W(d,kt):d);zt=i.top?Bt:0}if(g&&!$(g)||at.width||(at.width=St||this.contrastSize((this.isBlock(this)&&!this.isInFlow()?X||o.lineMaxWidth:this.lineMaxWidth)||this.lineMaxWidth,v,y),this.layoutBoxUpdate(at,i,0)),m||!bt&&!zt||(at.height=zt||this.contrastSize(bt,x,b),this.layoutBoxUpdate(at,i)),i.borderRadius&&this.borderSize&&this.borderSize.width)for(var q in i.borderRadius)Object.hasOwnProperty.call(i.borderRadius,q)&&(i.borderRadius[q]=W(i.borderRadius[q],this.borderSize.width));return this.layoutBox},e.layout=function(){return this.getBoxWidthHeight(),this.root.offsetSize=this.offsetSize,this.root.contentSize=this.contentSize,this.getBoxPosition(),this.offsetSize},t}(),vt=function(){var t,e,i,n,r,o,s=[0,11,15,19,23,27,31,16,18,20,22,24,26,28,20,22,24,24,26,28,28,22,24,24,26,26,28,28,24,24,26,26,26,28,28,24,26,26,26,28,28],a=[3220,1468,2713,1235,3062,1890,2119,1549,2344,2936,1117,2583,1330,2470,1667,2249,2028,3780,481,4011,142,3098,831,3445,592,2517,1776,2234,1951,2827,1070,2660,1345,3177],h=[30660,29427,32170,30877,26159,25368,27713,26998,21522,20773,24188,23371,17913,16590,20375,19104,13663,12392,16177,14854,9396,8579,11994,11245,5769,5054,7399,6608,1890,597,3340,2107],c=[1,0,19,7,1,0,16,10,1,0,13,13,1,0,9,17,1,0,34,10,1,0,28,16,1,0,22,22,1,0,16,28,1,0,55,15,1,0,44,26,2,0,17,18,2,0,13,22,1,0,80,20,2,0,32,18,2,0,24,26,4,0,9,16,1,0,108,26,2,0,43,24,2,2,15,18,2,2,11,22,2,0,68,18,4,0,27,16,4,0,19,24,4,0,15,28,2,0,78,20,4,0,31,18,2,4,14,18,4,1,13,26,2,0,97,24,2,2,38,22,4,2,18,22,4,2,14,26,2,0,116,30,3,2,36,22,4,4,16,20,4,4,12,24,2,2,68,18,4,1,43,26,6,2,19,24,6,2,15,28,4,0,81,20,1,4,50,30,4,4,22,28,3,8,12,24,2,2,92,24,6,2,36,22,4,6,20,26,7,4,14,28,4,0,107,26,8,1,37,22,8,4,20,24,12,4,11,22,3,1,115,30,4,5,40,24,11,5,16,20,11,5,12,24,5,1,87,22,5,5,41,24,5,7,24,30,11,7,12,24,5,1,98,24,7,3,45,28,15,2,19,24,3,13,15,30,1,5,107,28,10,1,46,28,1,15,22,28,2,17,14,28,5,1,120,30,9,4,43,26,17,1,22,28,2,19,14,28,3,4,113,28,3,11,44,26,17,4,21,26,9,16,13,26,3,5,107,28,3,13,41,26,15,5,24,30,15,10,15,28,4,4,116,28,17,0,42,26,17,6,22,28,19,6,16,30,2,7,111,28,17,0,46,28,7,16,24,30,34,0,13,24,4,5,121,30,4,14,47,28,11,14,24,30,16,14,15,30,6,4,117,30,6,14,45,28,11,16,24,30,30,2,16,30,8,4,106,26,8,13,47,28,7,22,24,30,22,13,15,30,10,2,114,28,19,4,46,28,28,6,22,28,33,4,16,30,8,4,122,30,22,3,45,28,8,26,23,30,12,28,15,30,3,10,117,30,3,23,45,28,4,31,24,30,11,31,15,30,7,7,116,30,21,7,45,28,1,37,23,30,19,26,15,30,5,10,115,30,19,10,47,28,15,25,24,30,23,25,15,30,13,3,115,30,2,29,46,28,42,1,24,30,23,28,15,30,17,0,115,30,10,23,46,28,10,35,24,30,19,35,15,30,17,1,115,30,14,21,46,28,29,19,24,30,11,46,15,30,13,6,115,30,14,23,46,28,44,7,24,30,59,1,16,30,12,7,121,30,12,26,47,28,39,14,24,30,22,41,15,30,6,14,121,30,6,34,47,28,46,10,24,30,2,64,15,30,17,4,122,30,29,14,46,28,49,10,24,30,24,46,15,30,4,18,122,30,13,32,46,28,48,14,24,30,42,32,15,30,20,4,117,30,40,7,47,28,43,22,24,30,10,67,15,30,19,6,118,30,18,31,47,28,34,34,24,30,20,61,15,30],f=[255,0,1,25,2,50,26,198,3,223,51,238,27,104,199,75,4,100,224,14,52,141,239,129,28,193,105,248,200,8,76,113,5,138,101,47,225,36,15,33,53,147,142,218,240,18,130,69,29,181,194,125,106,39,249,185,201,154,9,120,77,228,114,166,6,191,139,98,102,221,48,253,226,152,37,179,16,145,34,136,54,208,148,206,143,150,219,189,241,210,19,92,131,56,70,64,30,66,182,163,195,72,126,110,107,58,40,84,250,133,186,61,202,94,155,159,10,21,121,43,78,212,229,172,115,243,167,87,7,112,192,247,140,128,99,13,103,74,222,237,49,197,254,24,227,165,153,119,38,184,180,124,17,68,146,217,35,32,137,46,55,63,209,91,149,188,207,205,144,135,151,178,220,252,190,97,242,86,211,171,20,42,93,158,132,60,57,83,71,109,65,162,31,45,67,216,183,123,164,118,196,23,73,236,127,12,111,246,108,161,59,82,41,157,85,170,251,96,134,177,187,204,62,90,203,89,95,176,156,169,160,81,11,245,22,235,122,117,44,215,79,174,213,233,230,231,173,232,116,214,244,234,168,80,88,175],l=[1,2,4,8,16,32,64,128,29,58,116,232,205,135,19,38,76,152,45,90,180,117,234,201,143,3,6,12,24,48,96,192,157,39,78,156,37,74,148,53,106,212,181,119,238,193,159,35,70,140,5,10,20,40,80,160,93,186,105,210,185,111,222,161,95,190,97,194,153,47,94,188,101,202,137,15,30,60,120,240,253,231,211,187,107,214,177,127,254,225,223,163,91,182,113,226,217,175,67,134,17,34,68,136,13,26,52,104,208,189,103,206,129,31,62,124,248,237,199,147,59,118,236,197,151,51,102,204,133,23,46,92,184,109,218,169,79,158,33,66,132,21,42,84,168,77,154,41,82,164,85,170,73,146,57,114,228,213,183,115,230,209,191,99,198,145,63,126,252,229,215,179,123,246,241,255,227,219,171,75,150,49,98,196,149,55,110,220,165,87,174,65,130,25,50,100,200,141,7,14,28,56,112,224,221,167,83,166,81,162,89,178,121,242,249,239,195,155,43,86,172,69,138,9,18,36,72,144,61,122,244,245,247,243,251,235,203,139,11,22,44,88,176,125,250,233,207,131,27,54,108,216,173,71,142,0],d=[],u=[],p=[],g=[],v=[],y=2;function x(t,e){var i;t>e&&(i=t,t=e,e=i),i=e,i*=e,i+=e,i>>=1,g[i+=t]=1}function b(t,i){var n;for(p[t+e*i]=1,n=-2;n<2;n++)p[t+n+e*(i-2)]=1,p[t-2+e*(i+n+1)]=1,p[t+2+e*(i+n)]=1,p[t+n+1+e*(i+2)]=1;for(n=0;n<2;n++)x(t-1,i+n),x(t+1,i-n),x(t-n,i-1),x(t+n,i+1)}function w(t){for(;t>=255;)t=((t-=255)>>8)+(255&t);return t}var m=[];function S(t,e,i,n){var r,o,s;for(r=0;re&&(i=t,t=e,e=i),i=e,i+=e*e,i>>=1,g[i+=t]}function I(t){var i,n,r,o;switch(t){case 0:for(n=0;n>1&1,i=0;i=5&&(i+=3+v[e]-5);for(e=3;et||3*v[e-3]>=4*v[e]||3*v[e+3]>=4*v[e])&&(i+=40);return i}function k(){var t,i,n,r,o,s=0,a=0;for(i=0;ie*e;)h-=e*e,c++;for(s+=10*c,t=0;t1)for(P=s[t],B=e-7;;){for(M=e-7;M>P-3&&(b(M,B),!(M6)for(P=a[t-7],W=17,M=0;M<6;M++)for(B=0;B<3;B++,W--)1&(W>11?t>>W-12:P>>W)?(p[5-M+e*(2-B+e-11)]=1,p[2-B+e-11+e*(5-M)]=1):(x(5-M,2-B+e-11),x(2-B+e-11,5-M));for(B=0;B=(M=r*(i+n)+n)-2&&(O=M-2,t>9&&O--),T=O,t>9){for(d[T+2]=0,d[T+3]=0;T--;)P=d[T],d[T+3]|=255&P<<4,d[T+2]=P>>4;d[2]|=255&O<<4,d[1]=O>>4,d[0]=64|O>>12}else{for(d[T+1]=0,d[T+2]=0;T--;)P=d[T],d[T+2]|=255&P<<4,d[T+1]=P>>4;d[1]|=255&O<<4,d[0]=64|O>>4}for(T=O+3-(t<10);T0;L--)m[L]=m[L]?m[L-1]^l[w(f[m[L]]+T)]:m[L-1];m[0]=l[w(f[m[0]]+T)]}for(T=0;T<=o;T++)m[T]=f[m[T]];for(W=M,B=0,T=0;T>=1)1&B&&(p[e-1-W+8*e]=1,W<6?p[8+e*W]=1:p[8+e*(W+1)]=1);for(W=0;W<7;W++,B>>=1)1&B&&(p[8+e*(e-7+W)]=1,W?p[6-W+8*e]=1:p[7+8*e]=1);return p}(v)},utf16to8:function(t){var e,i,n,r;for(e="",n=t.length,i=0;i=1&&r<=127?e+=t.charAt(i):r>2047?(e+=String.fromCharCode(224|r>>12&15),e+=String.fromCharCode(128|r>>6&63),e+=String.fromCharCode(128|r>>0&63)):(e+=String.fromCharCode(192|r>>6&31),e+=String.fromCharCode(128|r>>0&63));return e},draw:function(t,i,n,r,o){i.drawView(n,r);var s=i.ctx,a=n.contentSize,h=a.width,c=a.height,f=a.left,l=a.top;r.borderRadius,r.backgroundColor;var d=r.color,u=void 0===d?"#000000":d;r.border,n.contentSize.left,n.borderSize.left,n.contentSize.top,n.borderSize.top;if(y=o||y,s){s.save(),i.setOpacity(r),i.setTransform(n,r);var p=Math.min(h,c);t=this.utf16to8(t);var g=this.getFrame(t),v=p/e;s.setFillStyle(u);for(var x=0;x=s||n==c&&o=s)&&(a=e.width/i.width);var f=i.width*a,l=i.height*a,d=r||[],u=d[0],p=d[1],g=O(u)?W(u,e.width):(e.width-f)*(P(u)?W(u,1):{left:0,center:.5,right:1}[u||"center"]),v=O(p)?W(p,e.height):(e.height-l)*(P(p)?W(p,1):{top:0,center:.5,bottom:1}[p||"center"]),y=function(t,e){return[(t-g)/a,(e-v)/a]},x=y(0,0),b=x[0],w=x[1],m=y(e.width,e.height),S=m[0],z=m[1],I=Math.max,M=Math.min;return{sx:I(b,0),sy:I(w,0),sw:M(S-b,i.width),sh:M(z-w,i.height),dx:I(g,0),dy:I(v,0),dw:M(f,e.width),dh:M(l,e.height)}}({objectFit:u,objectPosition:v},r.contentSize,t),o=i.sx,s=i.sy,a=i.sh,h=i.sw,c=i.dx,f=i.dy,l=i.dh,d=i.dw;I==n.MP_BAIDU?e.drawImage(t.src,c+m,f+S,d,l,o,s,h,a):e.drawImage(t.src,o,s,h,a,c+m,f+S,d,l)}else e.drawImage(t.src,m,S,b,w)},B=function(){e.restore(),L.drawView(r,o,!1,!0,!1),h(1)},T=function(t){k(t),B()},T(t),[2]}))}))}))];case 1:return h.sent(),[2]}}))}))},r.prototype.drawText=function(t,e,i,n){var r=this,o=this.ctx,s=e.borderSize,a=e.contentSize,h=e.left,c=e.top,f=a.width,l=a.height,d=a.left-s.left||0,u=a.top-s.top||0,p=i.color,g=i.lineHeight,v=i.fontSize,y=i.fontWeight,x=i.fontFamily,b=i.fontStyle,w=i.textIndent,m=void 0===w?0:w,S=i.textAlign,z=i.textStroke,I=i.verticalAlign,M=void 0===I?St:I,k=i.backgroundColor,P=i.lineClamp,O=i.backgroundClip,T=i.textShadow,L=i.textDecoration;if(m=B(m)?m:0,this.drawView(e,i,O!=xt),g=W(g,v),t){o.save(),h+=d,c+=u;var R=n.fontHeight,F=n.descent,A=void 0===F?0:F,j=n.ascent,E=A+(void 0===j?0:j);switch(o.setFonts({fontFamily:x,fontSize:v,fontWeight:y,fontStyle:b}),o.setTextBaseline(St),o.setTextAlign(S),O?this.setBackground(k,f,l,h,c):o.setFillStyle(p),S){case It:break;case Mt:h+=.5*f;break;case kt:h+=f}var C=n.lines*g,H=Math.ceil((l-C)/2);switch(H<0&&(H=0),M){case mt:break;case St:c+=H;break;case zt:c+=2*H}var D=(g-R)/2,$=g/2,Y=function(t){var e=o.measureText(t),i=e.actualBoundingBoxDescent,n=void 0===i?0:i,r=e.actualBoundingBoxAscent;return M==mt?{fix:E?void 0===r?0:r:$-D/2,lineY:E?0:D-D/2}:M==St?{fix:E?$+n/4:$,lineY:E?0:D}:M==zt?{fix:E?g-n:$+D/2,lineY:E?2*D:D+D/2}:{fix:0,height:0,lineY:0}},U=function(t,e,i){var r=t;switch(S){case It:r+=i;break;case Mt:r=(t-=i/2)+i;break;case kt:r=t,t-=i}if(L){o.setLineWidth(v/13),o.beginPath();var s=.1*n.fontHeight;/\bunderline\b/.test(L)&&(o.moveTo(t,e+n.fontHeight+s),o.lineTo(r,e+n.fontHeight+s)),/\boverline\b/.test(L)&&(o.moveTo(t,e-s),o.lineTo(r,e-s)),/\bline-through\b/.test(L)&&(o.moveTo(t,e+.5*n.fontHeight),o.lineTo(r,e+.5*n.fontHeight)),o.closePath(),o.setStrokeStyle(p),o.stroke()}},N=function(t,e,i){var n=function(){o.setLineWidth(z.width),o.setStrokeStyle(z.color),o.strokeText(t,e,i)},s="outset";z&&z.type!==s?(o.save(),r.setShadow({boxShadow:T}),o.fillText(t,e,i),o.restore(),n()):z&&z.type==s?(o.save(),r.setShadow({boxShadow:T}),n(),o.restore(),o.save(),o.fillText(t,e,i),o.restore()):(r.setShadow({boxShadow:T}),o.fillText(t,e,i))};if(!n.widths||1==n.widths.length&&n.widths[0].total+m<=a.width){var X=Y(t),_=X.fix,q=void 0===_?0:_,G=X.lineY;return N(t,h+m,c+q),U(h+m,c+G,n&&n.widths&&n.widths[0].total||n.text),c+=g,o.restore(),void this.setBorder(e,i)}for(var V=c,J=h,Q="",Z=0,K=o.measureText("...").width,tt=n.widths,et=0;eta.width){Z>=P&&(Q+="…"),Z++,nt=0;var ct=Y(Q);q=ct.fix,G=ct.lineY;N(Q,J,c+q),U(J,c+G,nt),c+=g,Q=""}else if(rt==it.length-1){et!=tt.length-1&&Z==P&&K+ntV+l||Z>P)break}}o.restore()}},r.prototype.source=function(t){return e(this,void 0,void 0,(function(){var e,n,r,o,s=this;return i(this,(function(i){switch(i.label){case 0:if(this.node=null,e=+new Date,"{}"==JSON.stringify(t))return[2];if(t.styles=t.styles||t.css||{},!t.type)for(n in t.type=wt,t)["views","children","type","css","styles"].includes(n)||(t.styles[n]=t[n],delete t[n]);return t.styles.boxSizing||(t.styles.boxSizing="border-box"),[4,this.create(t)];case 1:return(r=i.sent())?(o=r.layout()||{},this.size=o,this.node=r,this.onEffectFinished().then((function(t){return s.lifecycle("onEffectSuccess",t)})).catch((function(t){return s.lifecycle("onEffectFail",t)})),this.performance&&console.log("布局用时:"+(+new Date-e)+"ms"),[2,this.size]):[2,console.warn("no node")]}}))}))},r.prototype.getImageInfo=function(t){return this.imageBus[t]||(this.imageBus[t]=this.createImage(t,this.useCORS)),this.imageBus[t]},r.prototype.create=function(n,r){return e(this,void 0,void 0,(function(){function e(i,n,r){void 0===n&&(n={}),void 0===r&&(r=!0);var o=[];return i.forEach((function(i){var s=i.styles,a=void 0===s?{}:s,h=i.css,c=void 0===h?{}:h,f=i.children,l=void 0===f?[]:f,d=i.views,u=void 0===d?[]:d,p=i.text,g=void 0===p?"":p,v=i.type,y=void 0===v?"":v;!l&&u&&(i.children=l=u);var x={};x=t(t(r?t({},n):{},a),c);var b={},w={},m={};Object.keys(x).map((function(t){if(t.includes("padding")||t.includes("margin")){var e=J(t,x[t]);Object.keys(e).map((function(t){t.includes("Left")?w[t]=e[t]:t.includes("Right")?m[t]=e[t]:b[t]=e[t]}))}}));if(x.textIndent&&(w.textIndent=x.textIndent,delete n.textIndent),""!==g){var S=Array.from(g);S.forEach((function(t,e){var i=Object.assign({},x,b);0===e?Object.assign(i,w):e==S.length-1&&Object.assign(i,m),delete i.padding,delete i.margin,o.push({type:"text",text:t,styles:i})}))}if(y==yt||y==bt)o.push(i);else if("block"===a.display&&l.length>0){var z=e(l,x,!1);i.children=z,i.flattened=!0,o.push(i)}else if(l.length>0){z=e(l,x,r);o=o.concat(z)}})),o}var o,s,a,h,c,f,l,d,u,p,g,v,y,b,w,m,S,z,I,M,k,B,W,P;return i(this,(function(i){switch(i.label){case 0:if(!n)return[2];if(n.styles||(n.styles=n.css||{}),o=n.type,s=n.show,a=void 0===s||s,h=o==yt,c=[xt,bt].includes(o),f="textBox"==o,l=n.styles||{},d=l.backgroundImage,u=l.display,h&&!n.src&&!n.url)return[2];if(u==x||!a)return[2];if(c||f){if(p=n.children,g=n.views,!p&&g&&(n.children=p=g),!n.text&&(!p||p&&!p.length))return[2];p&&p.length&&!n.flattened&&(v=e(n.children||n.views),n.type="view",n.children=v)}if(!(h||n.type==wt&&d))return[3,4];y=h?n.src:"",b=/url\(['"]?(.*?)['"]?\)/.exec(d),d&&b&&b[1]&&(y=b[1]||""),i.label=1;case 1:return i.trys.push([1,3,,4]),[4,this.getImageInfo(y)];case 2:return w=i.sent(),m=w.width,S=w.height,!(z=w.path)&&h?[2]:(z&&(n.attributes=Object.assign(n.attributes||{},{width:m,height:S,path:z,src:z,naturalSrc:y})),[3,4]);case 3:return I=i.sent(),n.type!=wt?[2]:(this.lifecycle("onEffectFail",t(t({},I),{src:y})),[3,4]);case 4:if(this.count+=1,M=new gt(n,r,this.root,this.ctx),!(k=n.children||n.views))return[3,8];B=0,i.label=5;case 5:return B0&&r[r.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!r||o[1]>r[0]&&o[1]=360&&(s-=360);s<0&&(s+=360);if(0===(s=Math.round(s)))return{x0:Math.round(e/2)+n,y0:i+r,x1:Math.round(e/2)+n,y1:r};if(180===s)return{x0:Math.round(e/2)+n,y0:r,x1:Math.round(e/2)+n,y1:i+r};if(90===s)return{x0:n,y0:Math.round(i/2)+r,x1:e+n,y1:Math.round(i/2)+r};if(270===s)return{x0:e+n,y0:Math.round(i/2)+r,x1:n,y1:Math.round(i/2)+r};var h=Math.round(180*Math.asin(e/Math.sqrt(Math.pow(e,2)+Math.pow(i,2)))/Math.PI);if(s===h)return{x0:n,y0:i+r,x1:e+n,y1:r};if(s===180-h)return{x0:n,y0:r,x1:e+n,y1:i+r};if(s===180+h)return{x0:e+n,y0:r,x1:n,y1:i+r};if(s===360-h)return{x0:e+n,y0:i+r,x1:n,y1:r};var a=0,l=0,d=0,c=0;if(s180-h&&s<180||s>180&&s<180+h||s>360-h){var f=s*Math.PI/180,u=s360-h?i/2:-i/2,p=Math.tan(f)*u,g=s180-h&&s<180?e/2-p:-e/2-p;a=-(d=p+(v=Math.pow(Math.sin(f),2)*g)),l=-(c=u+v/Math.tan(f))}if(s>h&&s<90||s>90&&s<90+h||s>180+h&&s<270||s>270&&s<360-h){var v;f=(90-s)*Math.PI/180,p=s>h&&s<90||s>90&&s<90+h?e/2:-e/2,u=Math.tan(f)*p,g=s>h&&s<90||s>270&&s<360-h?i/2-u:-i/2-u;a=-(d=p+(v=Math.pow(Math.sin(f),2)*g)/Math.tan(f)),l=-(c=u+v)}return a=Math.round(a+e/2)+n,l=Math.round(i/2-l)+r,d=Math.round(d+e/2)+n,c=Math.round(i/2-c)+r,{x0:a,y0:l,x1:d,y1:c}}(r,t,e,i,n),h=s.x0,a=s.y0,l=s.x1,d=s.y1,c=o.createLinearGradient(h,a,l,d),f=r.match(/linear-gradient\((.+)\)/)[1],u=R(f.substring(f.indexOf(",")+1)),p=0;pt.length)&&(e=t.length);for(var i=0,n=new Array(e);i=t.length?{done:!0}:{done:!1,value:t[n++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function C(t){return"number"==typeof t}function D(t){return"auto"===t||null===t}function $(t){return/%$/.test(t)}var Y=p,U=u,N=f,X=g,_=y,q=w,G=m;function V(t){return t.replace(/-([a-z])/g,(function(t,e){return e.toUpperCase()}))}function J(t,e){var i,n,o=function(t){var e=t.match(/([a-z]+)/)[1];return[e,V(t.split(e)[1])]}(t),s=o[0],h=o[1],a=e.split(" ");if(h)return(i={})[s+h]=e,i;if(a.length&&!h){var l=a[0],d=a[1],c=a[2],f=a[3];return(n={})[s+r[0]]=l,n[s+r[1]]=d||l,n[s+r[2]]=c||l,n[s+r[3]]=f||d||l,n}}function Q(t){t=t.trim();for(var e=new Array,i="+",n="",r=t.length,o=0;o0;)"("===t[h+=1]&&(s+=1),")"===t[h]&&(s-=1);n="".concat(Q(t.slice(o+1,h))),o=h}if(isNaN(Number(t[o]))&&"."!==t[o]||o===r-1){var a=parseFloat(n);switch(i){case"+":e.push(a);break;case"-":e.push(-a);break;case"*":e.push(e.pop()*a);break;case"/":e.push(e.pop()/a)}i=t[o],n=""}}for(var l=0;e.length;)l+=e.pop();return l}var Z,K=0,et=function(){function t(){F(this,"elements",[]),F(this,"afterElements",[]),F(this,"beforeElements",[]),F(this,"ids",[]),F(this,"width",0),F(this,"height",0),F(this,"top",0),F(this,"left",0),F(this,"pre",null),F(this,"offsetX",0),F(this,"offsetY",0),K++,this.id=K}var e=t.prototype;return e.fixedBind=function(t,e){void 0===e&&(e=0),this.container=e?t.parent:t.root,this.container.fixedLine=this,this.fixedAdd(t)},e.fixedAdd=function(t){if(!this.ids.includes(t.id)){this.ids.push(t.id),this.elements.push(t);var e=t.computedStyle.zIndex;(void 0===e?0:e)>=0?this.afterElements.push(t):this.beforeElements.push(t),this.refreshLayout()}},e.bind=function(t){this.container=t.parent,this.container.line=null,this.container.lines?(this.container.lines.push(this),this.pre=this.getPreLine(),this.top=this.pre.top+this.pre.height,this.left=this.container.contentSize.left):(this.top=this.container.contentSize.top,this.left=this.container.contentSize.left,this.container.lines=[this]),this.isInline=t.isInline(),this.container.line=this,this.outerWidth=t.parent&&t.parent.contentSize.width?t.parent.contentSize.width:1/0,this.add(t)},e.getPreLine=function(){return this.container.lines[this.container.lines.length-2]},e.canIEnter=function(t){return!((100*t.offsetSize.width+100*this.width)/100>this.outerWidth)||(this.closeLine(),!1)},e.closeLine=function(){delete this.container.line},e.add=function(t){this.ids.includes(t.id)||(this.ids.push(t.id),this.elements.push(t),this.refreshWidthHeight(t))},e.refreshWidthHeight=function(t){t.offsetSize.height>this.height&&(this.height=t.offsetSize.height),this.width+=t.offsetSize.width||0,(this.container.lineMaxWidth||0)this[this.key.height]&&(this.container[this.key.lineMaxHeight]=this[this.key.height]=i),this[this.key.width]+=this.getWidth(t.offsetSize);var n=Math.min(this.getWidth(this),!this.getWidth(this.container.contentSize)&&1/0);(this.container[this.key.lineMaxWidth]||0)1)return 0;var e=t.style.alignSelf,i=this.getHeight(this.container.contentSize),n=i-this.getHeight(t.offsetSize);return"flex-end"===e?n:"center"===e?n/2:"stretch"===e?(n&&t.name==f&&(t.style[this.key.width]=this.getWidth(t.offsetSize),t.style[this.key.height]=i,delete t.line,delete t.lines,t.getBoxWidthHeight()),0):0},r.layout=function(t,e){var i=this;this.refreshXAlign(),this.pre?(this.top=this.pre.top+this.pre.height+this.offsetY,this.left=e+this.offsetX):(this.top=Math.max(this.top,this.container.contentSize.top,t)+this.offsetY,this.left=Math.max(this.left,this.container.contentSize.left,e)+this.offsetX),this.elements.forEach((function(t,e){i.setIndent(t);var n=i.elements[e-1],r=i.getOffsetY(t);t.style[i.key.top]=i[i.key.top]+r,t.style[i.key.left]=n?n.offsetSize[i.key.left]+i.getWidth(n.offsetSize):i[i.key.left],t.getBoxPosition()}))},n}(et),rt=p,ot=u,st=f,ht=v,at=y,lt=b,dt=w,ct=m,ft=0,ut={left:null,top:null,width:null,height:null},pt=new Map,gt=function(){function t(t,e,i,n){var o=this;F(this,"id",ft++),F(this,"style",{left:null,top:null,width:null,height:null}),F(this,"computedStyle",{}),F(this,"originStyle",{}),F(this,"children",{}),F(this,"layoutBox",A({},ut)),F(this,"contentSize",A({},ut)),F(this,"clientSize",A({},ut)),F(this,"borderSize",A({},ut)),F(this,"offsetSize",A({},ut)),this.ctx=n,this.root=i,e&&(this.parent=e),this.name=t.type||t.name,this.attributes=this.getAttributes(t);var s=function(t,e){var i,n=["color","fontSize","lineHeight","verticalAlign","fontWeight","textAlign"],o=t.type,s=void 0===o?N:o,h=t.styles,a=void 0===h?{}:h,l=(e||{}).computedStyle,d=Object.assign({},S);if([U,Y,X].includes(s)&&!a.display&&(d.display=_),l)for(var c=0;c=0&&c<0,$=l>=0&&u<0;return i==h[0]&&(this[i].left=t.left+s+v+E+(D?2*-c:0),this[i].top=t.top+l+x+k+($?2*-u:0),this[i].width=t.width+(this[i].widthAdd?0:H),this[i].height=t.height+(this[i].heightAdd?0:C),this[i].widthAdd=H,this[i].heightAdd=C),i==h[1]&&(this[i].left=t.left+s+E+(D<0?-c:0),this[i].top=t.top+l+k+($?-u:0),this[i].width=t.width+v+w,this[i].height=t.height+x+S),i==h[2]&&(this[i].left=t.left+s+E/2+(D<0?-c:0),this[i].top=t.top+l+k/2+($?-u:0),this[i].width=t.width+v+w+E/2+F/2,this[i].height=t.height+x+S+T/2+k/2),i==h[3]&&(this[i].left=t.left+(D<0?-c:0),this[i].top=t.top+($?-u:0),this[i].width=t.width+v+w+E+F+s+c,this[i].height=t.height+x+S+T+k+u+l),this[i]},e.layoutBoxUpdate=function(t,e,i,n){var r=this;if(void 0===i&&(i=-1),"border-box"==e.boxSizing){var o=e||{},s=o.border,a=(s=void 0===s?{}:s).borderWidth,l=void 0===a?0:a,d=o.borderTop,c=(d=void 0===d?{}:d).borderTopWidth,f=void 0===c?l:c,u=o.borderBottom,p=(u=void 0===u?{}:u).borderBottomWidth,g=void 0===p?l:p,v=o.borderRight,y=(v=void 0===v?{}:v).borderRightWidth,x=void 0===y?l:y,b=o.borderLeft,w=(b=void 0===b?{}:b).borderLeftWidth,m=void 0===w?l:w,S=o.padding,z=(S=void 0===S?{}:S).paddingTop,I=void 0===z?0:z,M=S.paddingRight,B=void 0===M?0:M,W=S.paddingBottom,k=void 0===W?0:W,P=S.paddingLeft,O=void 0===P?0:P;i||(t.width-=O+B+x+m),1!==i||n||(t.height-=I+k+f+g)}this.layoutBox&&(h.forEach((function(i){return r.layoutBox[i]=r.getOffsetSize(t,e,i)})),this.layoutBox=Object.assign({},this.layoutBox,this.layoutBox.borderSize))},e.getBoxPosition=function(){var t=this.computedStyle,e=this.fixedLine,i=this.lines,n=t.left,r=void 0===n?0:n,o=t.top,s=void 0===o?0:o,h=A({},this.contentSize,{left:r,top:s}),a=this.contentSize.top-this.offsetSize.top,l=this.contentSize.left-this.offsetSize.left;if(this.root.fixedLine&&!this.root.isDone){this.root.isDone=!0;for(var d,c=H(this.root.fixedLine.elements);!(d=c()).done;){var f=d.value;f.setPosition(f,this.root.offsetSize),f.getBoxPosition()}}if(e)for(var u,p=H(e.elements);!(u=p()).done;){var g=u.value,v=A({},this.borderSize,{left:r,top:s});g.setPosition(g,v);var y=this.borderSize.top-this.offsetSize.top,x=this.borderSize.left-this.offsetSize.left;g.style.left+=r+x,g.style.top+=s+y,g.getBoxPosition()}if(i)for(var b,w=H(i);!(b=w()).done;){b.value.layout(h.top+a,h.left+l)}return this.layoutBoxUpdate(h,t),this.layoutBox},e.getBoxState=function(t,e){return this.isBlock(t)||this.isBlock(e)},e.isBlock=function(t){return void 0===t&&(t=this),t&&t.style.display==ht},e.isFlex=function(t){return void 0===t&&(t=this),t&&t.style.display==lt},e.isInFlow=function(){return!(this.isAbsolute||this.isFixed)},e.inFlexBox=function(t){return void 0===t&&(t=this),!!t.isInFlow()&&(!!t.parent&&(!(!t.parent||t.parent.style.display!==lt)||void 0))},e.isInline=function(t){return void 0===t&&(t=this),t&&t.style.display==at},e.contrastSize=function(t,e,i){var n=t;return i&&(n=Math.min(n,i)),e&&(n=Math.max(n,e)),n},e.measureText=function(t,e){var i=this.ctx.measureText(t),n=i.width,r=i.actualBoundingBoxAscent,o=i.actualBoundingBoxDescent;return{ascent:r,descent:o,width:n,fontHeight:r+o||.7*e+1}},e.getParentSize=function(t,e){if(void 0===t&&(t=this),void 0===e&&(e=!1),t&&t.parent){if(t.parent.contentSize.width)return t.parent.contentSize;if(e)return this.getParentSize(t.parent,e)}return null},e.getBoxWidthHeight=function(){var t=this,e=this.name,i=this.computedStyle,n=this.attributes,r=this.parent,o=void 0===r?{}:r,s=this.ctx,h=this.getChildren(),a=i.left,l=void 0===a?0:a,d=i.top,c=void 0===d?0:d,f=i.bottom,u=i.right,p=i.width,g=void 0===p?0:p,v=i.minWidth,y=i.maxWidth,x=i.minHeight,b=i.maxHeight,w=i.height,m=void 0===w?0:w,S=i.fontSize,z=i.fontWeight,I=i.fontFamily,M=i.fontStyle,B=i.position;i.textIndent;var W=i.lineClamp,P=i.lineHeight,O=i.padding,T=void 0===O?{}:O,L=i.margin,R=void 0===L?{}:L,F=i.border,A=(F=void 0===F?{}:F).borderWidth,j=void 0===A?0:A,E=i.borderRight,H=(E=void 0===E?{}:E).borderRightWidth,C=void 0===H?j:H,Y=i.borderLeft,U=(Y=void 0===Y?{}:Y).borderLeftWidth,N=void 0===U?j:U,X=o.contentSize&&o.contentSize.width,_=o.contentSize&&o.contentSize.height;if($(g)&&X&&(g=k(g,X)),$(g)&&!X&&(g=null),$(m)&&_&&(m=k(m,_)),$(m)&&!_&&(m=null),$(v)&&X&&(v=k(v,X)),$(y)&&X&&(y=k(y,X)),$(x)&&_&&(x=k(x,_)),$(b)&&_&&(b=k(b,_)),i.padding&&X)for(var q in i.padding)Object.hasOwnProperty.call(T,q)&&(T[q]=k(T[q],X));var G=T.paddingRight,V=void 0===G?0:G,J=T.paddingLeft,Q=void 0===J?0:J;if(i.margin&&[R.marginLeft,R.marginRight].includes("auto"))if(g){var Z=X&&X-g-V-Q-N-C||0;R.marginLeft==R.marginRight?R.marginLeft=R.marginRight=Z/2:D(R.marginLeft)?R.marginLeft=Z:R.marginRight=Z}else R.marginLeft=R.marginRight=0;var K=R.marginRight,tt=void 0===K?0:K,it=R.marginLeft,ht={width:g,height:m,left:0,top:0},at=Q+V+N+C+(void 0===it?0:it)+tt;if(this.offsetWidth=at,e==ot&&!this.attributes.widths){var lt=n.text||"";s.save(),s.setFonts({fontFamily:I,fontSize:S,fontWeight:z,fontStyle:M}),lt.length,"\n"==lt&&(lt="",this.isBr=!0),(""+lt).split("\n").map((function(e){var i=Array.from(e).map((function(e){var i=""+(/^[\u4e00-\u9fa5]+$/.test(e)?"cn":e)+I+S+z+M,n=pt.get(i);if(n)return{width:n,text:e};var r=t.measureText(e,S).width;return pt.set(i,r),{width:r,text:e}})),n=t.measureText(e,S),r=n.fontHeight,o=n.ascent,s=n.descent;t.attributes.fontHeight=r,t.attributes.ascent=o,t.attributes.descent=s,t.attributes.widths||(t.attributes.widths=[]),t.attributes.widths.push({widths:i,total:i.reduce((function(t,e){return t+e.width}),0)})})),s.restore()}if(e==rt&&null==g){var ct=n.width,ft=n.height;ht.width=this.contrastSize(Math.round(ct*m/ft)||0,v,y),this.layoutBoxUpdate(ht,i,0)}if(e==ot&&null==g){var ut=this.attributes.widths,gt=Math.max.apply(Math,ut.map((function(t){return t.total})));if(o&&X>0&&(gt>X||this.isBlock(this))&&!this.isAbsolute&&!this.isFixed)gt=X;ht.width=this.contrastSize(gt,v,y),this.layoutBoxUpdate(ht,i,0)}if(e==ot&&(o.style.flex||!this.attributes.lines)){var vt=this.attributes.widths.length;this.attributes.widths.forEach((function(t){return t.widths.reduce((function(t,e,i){return t+e.width>ht.width?(vt++,e.width):t+e.width}),0)})),vt=W&&vt>W?W:vt,this.attributes.lines=vt}if(e==rt&&null==m){var yt=n.width,xt=n.height;n.text,ht.height=this.contrastSize(k(ht.width*xt/yt)||0,x,b),this.layoutBoxUpdate(ht,i,1)}e==ot&&null==m&&(P=k(P,S),ht.height=this.contrastSize(k(this.attributes.lines*P),x,b),this.layoutBoxUpdate(ht,i,1,!0)),!g&&o&&o.children&&X&&(!this.isFlex(o)||o.isFlexCalc)&&([st,ot].includes(e)&&this.isFlex()||e==st&&this.isBlock(this)&&this.isInFlow())&&(ht.width=this.contrastSize(X-(o.isFlexCalc?0:at),v,y),this.layoutBoxUpdate(ht,i)),g&&!$(g)&&(ht.width=this.contrastSize(g,v,y),this.layoutBoxUpdate(ht,i,0)),m&&!$(m)&&(ht.height=this.contrastSize(ht.height,x,b),this.layoutBoxUpdate(ht,i,1));var bt=0;if(h.length){var wt=null,mt=!1;h.forEach((function(e,n){e.getBoxWidthHeight();var r=h[n+1];if(r&&r.isInFlow()&&(e.next=r),!t.line||!t.line.ids.includes(e.id))if(e.isInFlow()&&!e.inFlexBox()){var o=t.getBoxState(wt,e);if(e.isBr)return mt=!0;t.line&&t.line.canIEnter(e)&&!o&&!mt?t.line.add(e):(mt=!1,(new et).bind(e)),wt=e}else e.inFlexBox()?t.line&&(t.line.canIEnter(e)||"nowrap"==i.flexWrap)?t.line.add(e):(new nt).bind(e):e.isFixed?t.root.fixedLine?t.root.fixedLine.fixedAdd(e):(new et).fixedBind(e):t.fixedLine?t.fixedLine.fixedAdd(e):(new et).fixedBind(e,1)})),this.lines&&(bt=this.lines.reduce((function(t,e){return t+e.height}),0))}var St=0,zt=0;if(!g&&(this.isAbsolute||this.isFixed)&&X){var It=B==dt?X:this.root.width,Mt=It-($(l)?k(l,It):l)-($(u)?k(u,It):u);St=i.left?Mt:this.lineMaxWidth}if(!m&&(null!=c?c:this.isAbsolute||this.isFixed&&_)){var Bt=B==dt?_:this.root.height,Wt=Bt-($(c)?k(c,Bt):c)-($(f)?k(f,Bt):f);zt=i.top?Wt:0}if(g&&!$(g)||ht.width||(ht.width=St||this.contrastSize((this.isBlock(this)&&!this.isInFlow()?X||o.lineMaxWidth:this.lineMaxWidth)||this.lineMaxWidth,v,y),this.layoutBoxUpdate(ht,i,0)),m||!bt&&!zt||(ht.height=zt||this.contrastSize(bt,x,b),this.layoutBoxUpdate(ht,i)),i.borderRadius&&this.borderSize&&this.borderSize.width)for(var q in i.borderRadius)Object.hasOwnProperty.call(i.borderRadius,q)&&(i.borderRadius[q]=k(i.borderRadius[q],this.borderSize.width));return this.layoutBox},e.layout=function(){return this.getBoxWidthHeight(),this.root.offsetSize=this.offsetSize,this.root.contentSize=this.contentSize,this.getBoxPosition(),this.offsetSize},t}(),vt=p,yt=u,xt=g,bt=f,wt=d.TOP,mt=d.MIDDLE,St=d.BOTTOM,zt=c.LEFT,It=c.CENTER,Mt=c.RIGHT,Bt=function(){function r(t){var e,i,r=this;this.v="1.9.5.1",this.id=null,this.pixelRatio=1,this.width=0,this.height=0,this.sleep=1e3/30,this.count=0,this.isRate=!1,this.isDraw=!0,this.isCache=!0,this.fixed="",this.useCORS=!1,this.performance=!1,this.imageBus=[],this.createImage=function(t,e){return new Promise((function(i,n){var o=null;window||r.canvas.createImage?(o=r.canvas&&r.canvas.createImage?r.canvas.createImage():new Image,e&&o.setAttribute("crossOrigin","Anonymous"),o.src=t,o.onload=function(){i({width:o.naturalWidth||o.width,height:o.naturalHeight||o.height,path:o,src:this.src})},o.onerror=function(t){n(t)}):n({fail:"getImageInfo fail",src:t})}))},this.options=t,Object.assign(this,t),this.ctx=(e=t.context,i={get:function(t,i){if("setFonts"===i)return function(t){var i=t.fontFamily,r=void 0===i?"sans-serif":i,o=t.fontSize,s=void 0===o?14:o,h=t.fontWeight,a=void 0===h?"normal":h,l=t.fontStyle,d=void 0===l?"normal":l;I==n.MP_TOUTIAO&&(a="bold"==a?"bold":"",d="italic"==d?"italic":""),e.font="".concat(d," ").concat(a," ").concat(Math.round(s),"px ").concat(r)};if(!e.draw||!e.setFillStyle){if("setFillStyle"===i)return function(t){e.fillStyle=t};if("setStrokeStyle"===i)return function(t){e.strokeStyle=t};if("setLineWidth"===i)return function(t){e.lineWidth=t};if("setLineCap"===i)return function(t){e.lineCap=t};if("setFontSize"===i)return function(t){e.font="".concat(String(t),"px sans-serif")};if("setGlobalAlpha"===i)return function(t){e.globalAlpha=t};if("setLineJoin"===i)return function(t){e.lineJoin=t};if("setTextAlign"===i)return function(t){e.textAlign=t};if("setMiterLimit"===i)return function(t){e.miterLimit=t};if("setShadow"===i)return function(t,i,n,r){e.shadowOffsetX=t,e.shadowOffsetY=i,e.shadowBlur=n,e.shadowColor=r};if("setTextBaseline"===i)return function(t){e.textBaseline=t};if("createCircularGradient"===i)return function(){};if("draw"===i)return function(){};if("function"==typeof e[i])return function(){for(var t=[],n=0;n=s||n==l&&o=s)&&(h=e.width/i.width);var d=i.width*h,c=i.height*h,f=r||[],u=f[0],p=f[1],g=O(u)?k(u,e.width):(e.width-d)*(P(u)?k(u,1):{left:0,center:.5,right:1}[u||"center"]),v=O(p)?k(p,e.height):(e.height-c)*(P(p)?k(p,1):{top:0,center:.5,bottom:1}[p||"center"]),y=function(t,e){return[(t-g)/h,(e-v)/h]},x=y(0,0),b=x[0],w=x[1],m=y(e.width,e.height),S=m[0],z=m[1],I=Math.max,M=Math.min;return{sx:I(b,0),sy:I(w,0),sw:M(S-b,i.width),sh:M(z-w,i.height),dx:I(g,0),dy:I(v,0),dw:M(d,e.width),dh:M(c,e.height)}}({objectFit:u,objectPosition:v},r.contentSize,t),o=i.sx,s=i.sy,h=i.sh,a=i.sw,l=i.dx,d=i.dy,c=i.dh,f=i.dw;I==n.MP_BAIDU?e.drawImage(t.src,l+m,d+S,f,c,o,s,a,h):e.drawImage(t.src,o,s,a,h,l+m,d+S,f,c)}else e.drawImage(t.src,m,S,b,w)},W=function(){e.restore(),L.drawView(r,o,!1,!0,!1),a(1)},T=function(t){B(t),W()},T(t),[2]}))}))}))];case 1:return a.sent(),[2]}}))}))},r.prototype.drawText=function(t,e,i,n){var r=this,o=this.ctx,s=e.borderSize,h=e.contentSize,a=e.left,l=e.top,d=h.width,c=h.height,f=h.left-s.left||0,u=h.top-s.top||0,p=i.color,g=i.lineHeight,v=i.fontSize,y=i.fontWeight,x=i.fontFamily,b=i.fontStyle,w=i.textIndent,m=void 0===w?0:w,S=i.textAlign,z=i.textStroke,I=i.verticalAlign,M=void 0===I?mt:I,B=i.backgroundColor,P=i.lineClamp,O=i.backgroundClip,T=i.textShadow,L=i.textDecoration;if(m=W(m)?m:0,this.drawView(e,i,O!=yt),g=k(g,v),t){o.save(),a+=f,l+=u;var R=n.fontHeight,F=n.descent,A=void 0===F?0:F,j=n.ascent,E=A+(void 0===j?0:j);switch(o.setFonts({fontFamily:x,fontSize:v,fontWeight:y,fontStyle:b}),o.setTextBaseline(mt),o.setTextAlign(S),O?this.setBackground(B,d,c,a,l):o.setFillStyle(p),S){case zt:break;case It:a+=.5*d;break;case Mt:a+=d}var H=n.lines*g,C=Math.ceil((c-H)/2);switch(C<0&&(C=0),M){case wt:break;case mt:l+=C;break;case St:l+=2*C}var D=(g-R)/2,$=g/2,Y=function(t){var e=o.measureText(t),i=e.actualBoundingBoxDescent,n=void 0===i?0:i,r=e.actualBoundingBoxAscent;return M==wt?{fix:E?void 0===r?0:r:$-D/2,lineY:E?0:D-D/2}:M==mt?{fix:E?$+n/4:$,lineY:E?0:D}:M==St?{fix:E?g-n:$+D/2,lineY:E?2*D:D+D/2}:{fix:0,height:0,lineY:0}},U=function(t,e,i){var r=t;switch(S){case zt:r+=i;break;case It:r=(t-=i/2)+i;break;case Mt:r=t,t-=i}if(L){o.setLineWidth(v/13),o.beginPath();var s=.1*n.fontHeight;/\bunderline\b/.test(L)&&(o.moveTo(t,e+n.fontHeight+s),o.lineTo(r,e+n.fontHeight+s)),/\boverline\b/.test(L)&&(o.moveTo(t,e-s),o.lineTo(r,e-s)),/\bline-through\b/.test(L)&&(o.moveTo(t,e+.5*n.fontHeight),o.lineTo(r,e+.5*n.fontHeight)),o.closePath(),o.setStrokeStyle(p),o.stroke()}},N=function(t,e,i){var n=function(){o.setLineWidth(z.width),o.setStrokeStyle(z.color),o.strokeText(t,e,i)},s="outset";z&&z.type!==s?(o.save(),r.setShadow({boxShadow:T}),o.fillText(t,e,i),o.restore(),n()):z&&z.type==s?(o.save(),r.setShadow({boxShadow:T}),n(),o.restore(),o.save(),o.fillText(t,e,i),o.restore()):(r.setShadow({boxShadow:T}),o.fillText(t,e,i))};if(!n.widths||1==n.widths.length&&n.widths[0].total+m<=h.width){var X=Y(t),_=X.fix,q=void 0===_?0:_,G=X.lineY;return N(t,a+m,l+q),U(a+m,l+G,n&&n.widths&&n.widths[0].total||n.text),l+=g,o.restore(),void this.setBorder(e,i)}for(var V=l,J=a,Q="",Z=0,K=o.measureText("...").width,tt=n.widths,et=0;eth.width){Z>=P&&(Q+="…"),Z++,nt=0;var lt=Y(Q);q=lt.fix,G=lt.lineY;N(Q,J,l+q),U(J,l+G,nt),l+=g,Q=""}else if(rt==it.length-1){et!=tt.length-1&&Z==P&&K+ntV+c||Z>P)break}}o.restore()}},r.prototype.source=function(t){return e(this,void 0,void 0,(function(){var e,n,r,o,s=this;return i(this,(function(i){switch(i.label){case 0:if(this.node=null,e=+new Date,"{}"==JSON.stringify(t))return[2];if(!t.type)for(n in t.type=bt,t.styles=t.styles||t.css||{},t)["views","children","type","css","styles"].includes(n)||(t.styles[n]=t[n],delete t[n]);return t.styles.boxSizing||(t.styles.boxSizing="border-box"),[4,this.create(t)];case 1:return(r=i.sent())?(o=r.layout()||{},this.size=o,this.node=r,this.onEffectFinished().then((function(t){return s.lifecycle("onEffectSuccess",t)})).catch((function(t){return s.lifecycle("onEffectFail",t)})),this.performance&&console.log("布局用时:"+(+new Date-e)+"ms"),[2,this.size]):[2,console.warn("no node")]}}))}))},r.prototype.getImageInfo=function(t){return this.imageBus[t]||(this.imageBus[t]=this.createImage(t,this.useCORS)),this.imageBus[t]},r.prototype.create=function(n,r){return e(this,void 0,void 0,(function(){function e(i,n,r){void 0===n&&(n={}),void 0===r&&(r=!0);var o=[];return i.forEach((function(i){var s=i.styles,h=void 0===s?{}:s,a=i.children,l=void 0===a?[]:a,d=i.text,c=void 0===d?"":d,f=i.type,u=void 0===f?"":f,p={};p=t(r?t({},n):{},h);var g={},v={},y={};Object.keys(p).map((function(t){if(t.includes("padding")||t.includes("margin")){var e=J(t,p[t]);Object.keys(e).map((function(t){t.includes("Left")?v[t]=e[t]:t.includes("Right")?y[t]=e[t]:g[t]=e[t]}))}}));if(p.textIndent&&(v.textIndent=p.textIndent,delete n.textIndent),""!==c){var x=Array.from(c);x.forEach((function(t,e){var i=Object.assign({},p,g);0===e?Object.assign(i,v):e==x.length-1&&Object.assign(i,y),delete i.padding,delete i.margin,o.push({type:"text",text:t,styles:i})}))}if(u==vt||u==xt)o.push(i);else if("block"===h.display&&l.length>0){var b=e(l,p,!1);i.children=b,i.flattened=!0,o.push(i)}else if(l.length>0){b=e(l,p,r);o=o.concat(b)}})),o}var o,s,h,a,l,d,c,f,u,p,g,v,y,b,w,m,S,z,I,M,B;return i(this,(function(i){switch(i.label){case 0:if(!n)return[2];if(n.styles||(n.styles=n.css||{}),o=n.type,s=o==vt,h=[yt,xt].includes(o),a="textBox"==o,l=n.styles||{},d=l.backgroundImage,c=l.display,s&&!n.src&&!n.url)return[2];if(c==x)return[2];if(h||a){if(f=n.children,!n.text&&(!f||f&&!f.length))return[2];f&&f.length&&!n.flattened&&(u=e(n.children),n.type="view",n.children=u)}if(!(s||n.type==bt&&d))return[3,4];p=s?n.src:"",g=/url\(['"]?(.*?)['"]?\)/.exec(d),d&&g&&g[1]&&(p=g[1]||""),i.label=1;case 1:return i.trys.push([1,3,,4]),[4,this.getImageInfo(p)];case 2:return v=i.sent(),y=v.width,b=v.height,!(w=v.path)&&s?[2]:(w&&(n.attributes=Object.assign(n.attributes||{},{width:y,height:b,path:w,src:w,naturalSrc:p})),[3,4]);case 3:return m=i.sent(),n.type!=bt?[2]:(this.lifecycle("onEffectFail",t(t({},m),{src:p})),[3,4]);case 4:if(this.count+=1,S=new gt(n,r,this.root,this.ctx),!(z=n.views||n.children))return[3,8];I=0,i.label=5;case 5:return I /^data:image\/(\w+);base64/.test(path); +export function sleep(delay) { + return new Promise(resolve => setTimeout(resolve, delay)) +} +let {platform, SDKVersion} = uni.getSystemInfoSync() +export const isPC = /windows|mac/.test(platform) +// 缓存图片 +let cache = {} +export function isNumber(value) { + return /^-?\d+(\.\d+)?$/.test(value); +} +export function toPx(value, baseSize, isDecimal = false) { + // 如果是数字 + if (typeof value === 'number') { + return value + } + // 如果是字符串数字 + if (isNumber(value)) { + return value * 1 + } + // 如果有单位 + if (typeof value === 'string') { + const reg = /^-?([0-9]+)?([.]{1}[0-9]+){0,1}(em|rpx|px|%)$/g + const results = reg.exec(value); + if (!value || !results) { + return 0; + } + const unit = results[3]; + value = parseFloat(value); + let res = 0; + if (unit === 'rpx') { + res = uni.upx2px(value); + } else if (unit === 'px') { + res = value * 1; + } else if (unit === '%') { + res = value * toPx(baseSize) / 100; + } else if (unit === 'em') { + res = value * toPx(baseSize || 14); + } + return isDecimal ? res.toFixed(2) * 1 : Math.round(res); + } + return 0 +} + +// 计算版本 +export function compareVersion(v1, v2) { + v1 = v1.split('.') + v2 = v2.split('.') + const len = Math.max(v1.length, v2.length) + while (v1.length < len) { + v1.push('0') + } + while (v2.length < len) { + v2.push('0') + } + for (let i = 0; i < len; i++) { + const num1 = parseInt(v1[i], 10) + const num2 = parseInt(v2[i], 10) + + if (num1 > num2) { + return 1 + } else if (num1 < num2) { + return -1 + } + } + return 0 +} + +function gte(version) { + // #ifdef MP-ALIPAY + SDKVersion = my.SDKVersion + // #endif + return compareVersion(SDKVersion, version) >= 0; +} +export function canIUseCanvas2d() { + // #ifdef MP-WEIXIN + return gte('2.9.2'); + // #endif + // #ifdef MP-ALIPAY + return gte('2.7.15'); + // #endif + // #ifdef MP-TOUTIAO + return gte('1.78.0'); + // #endif + return false +} + +// #ifdef MP +export const prefix = () => { + // #ifdef MP-TOUTIAO + return tt + // #endif + // #ifdef MP-WEIXIN + return wx + // #endif + // #ifdef MP-BAIDU + return swan + // #endif + // #ifdef MP-ALIPAY + return my + // #endif + // #ifdef MP-QQ + return qq + // #endif + // #ifdef MP-360 + return qh + // #endif +} +// #endif + + + +/** + * base64转路径 + * @param {Object} base64 + */ +export function base64ToPath(base64) { + const [, format] = /^data:image\/(\w+);base64,/.exec(base64) || []; + + return new Promise((resolve, reject) => { + // #ifdef MP + const fs = uni.getFileSystemManager() + //自定义文件名 + if (!format) { + reject(new Error('ERROR_BASE64SRC_PARSE')) + } + const time = new Date().getTime(); + let pre = prefix() + // #ifdef MP-TOUTIAO + const filePath = `${pre.getEnvInfoSync().common.USER_DATA_PATH}/${time}.${format}` + // #endif + // #ifndef MP-TOUTIAO + const filePath = `${pre.env.USER_DATA_PATH}/${time}.${format}` + // #endif + fs.writeFile({ + filePath, + data: base64.split(',')[1], + encoding: 'base64', + success() { + resolve(filePath) + }, + fail(err) { + console.error(err) + reject(err) + } + }) + // #endif + + // #ifdef H5 + // mime类型 + let mimeString = base64.split(',')[0].split(':')[1].split(';')[0]; + //base64 解码 + let byteString = atob(base64.split(',')[1]); + //创建缓冲数组 + let arrayBuffer = new ArrayBuffer(byteString.length); + //创建视图 + let intArray = new Uint8Array(arrayBuffer); + for (let i = 0; i < byteString.length; i++) { + intArray[i] = byteString.charCodeAt(i); + } + resolve(URL.createObjectURL(new Blob([intArray], { + type: mimeString + }))) + // #endif + + // #ifdef APP-PLUS + const bitmap = new plus.nativeObj.Bitmap('bitmap' + Date.now()) + bitmap.loadBase64Data(base64, () => { + if (!format) { + reject(new Error('ERROR_BASE64SRC_PARSE')) + } + const time = new Date().getTime(); + const filePath = `_doc/uniapp_temp/${time}.${format}` + bitmap.save(filePath, {}, + () => { + bitmap.clear() + resolve(filePath) + }, + (error) => { + bitmap.clear() + reject(error) + }) + }, (error) => { + bitmap.clear() + reject(error) + }) + // #endif + }) +} + +/** + * 路径转base64 + * @param {Object} string + */ +export function pathToBase64(path) { + if (/^data:/.test(path)) return path + return new Promise((resolve, reject) => { + // #ifdef H5 + let image = new Image(); + image.setAttribute("crossOrigin", 'Anonymous'); + image.onload = function() { + let canvas = document.createElement('canvas'); + canvas.width = this.naturalWidth; + canvas.height = this.naturalHeight; + canvas.getContext('2d').drawImage(image, 0, 0); + let result = canvas.toDataURL('image/png') + resolve(result); + canvas.height = canvas.width = 0 + } + image.src = path + '?v=' + Math.random() + image.onerror = (error) => { + reject(error); + }; + // #endif + + // #ifdef MP + if (uni.canIUse('getFileSystemManager')) { + uni.getFileSystemManager().readFile({ + filePath: path, + encoding: 'base64', + success: (res) => { + resolve('data:image/png;base64,' + res.data) + }, + fail: (error) => { + console.error({error, path}) + reject(error) + } + }) + } + // #endif + + // #ifdef APP-PLUS + plus.io.resolveLocalFileSystemURL(getLocalFilePath(path), (entry) => { + entry.file((file) => { + const fileReader = new plus.io.FileReader() + fileReader.onload = (data) => { + resolve(data.target.result) + } + fileReader.onerror = (error) => { + reject(error) + } + fileReader.readAsDataURL(file) + }, reject) + }, reject) + // #endif + }) +} + + + +export function getImageInfo(path, useCORS) { + const isCanvas2D = this && this.canvas && this.canvas.createImage + return new Promise(async (resolve, reject) => { + // let time = +new Date() + let src = path.replace(/^@\//,'/') + if (cache[path] && cache[path].errMsg) { + resolve(cache[path]) + } else { + try { + // #ifdef MP || APP-PLUS + if (isBase64(path) && (isCanvas2D ? isPC : true)) { + src = await base64ToPath(path) + } + // #endif + // #ifdef H5 + if(useCORS) { + src = await pathToBase64(path) + } + // #endif + } catch (error) { + reject({ + ...error, + src + }) + } + // #ifndef APP-NVUE + if(isCanvas2D && !isPC) { + const img = this.canvas.createImage() + img.onload = function() { + const image = { + path: img, + width: img.width, + height: img.height + } + cache[path] = image + resolve(cache[path]) + } + img.onerror = function(err) { + reject({err,path}) + } + img.src = src + return + } + // #endif + uni.getImageInfo({ + src, + success: (image) => { + const localReg = /^\.|^\/(?=[^\/])/; + // #ifdef MP-WEIXIN || MP-BAIDU || MP-QQ || MP-TOUTIAO + image.path = localReg.test(src) ? `/${image.path}` : image.path; + // #endif + if(isCanvas2D) { + const img = this.canvas.createImage() + img.onload = function() { + image.path = img + cache[path] = image + resolve(cache[path]) + } + img.onerror = function(err) { + reject({err,path}) + } + img.src = src + return + } + // #ifdef APP-PLUS + // console.log('getImageInfo', +new Date() - time) + // ios 比较严格 可能需要设置跨域 + if(uni.getSystemInfoSync().osName == 'ios' && useCORS) { + pathToBase64(image.path).then(base64 => { + image.path = base64 + cache[path] = image + resolve(cache[path]) + }).catch(err => { + console.error({err, path}) + reject({err,path}) + }) + return + } + // #endif + cache[path] = image + resolve(cache[path]) + }, + fail(err) { + console.error({err, path}) + reject({err,path}) + } + }) + } + }) +} + + +// #ifdef APP-PLUS +const getLocalFilePath = (path) => { + if (path.indexOf('_www') === 0 || path.indexOf('_doc') === 0 || path.indexOf('_documents') === 0 || path + .indexOf('_downloads') === 0) { + return path + } + if (path.indexOf('file://') === 0) { + return path + } + if (path.indexOf('/storage/emulated/0/') === 0) { + return path + } + if (path.indexOf('/') === 0) { + const localFilePath = plus.io.convertAbsoluteFileSystem(path) + if (localFilePath !== path) { + return localFilePath + } else { + path = path.substr(1) + } + } + return '_www/' + path +} +// #endif + + diff --git a/uni_modules/lime-painter/components/lime-painter/lime-painter.vue b/uni_modules/lime-painter/components/lime-painter/lime-painter.vue new file mode 100644 index 0000000..f3b614b --- /dev/null +++ b/uni_modules/lime-painter/components/lime-painter/lime-painter.vue @@ -0,0 +1,235 @@ + + + + + diff --git a/uni_modules/lime-painter/hybrid/html/index.html b/uni_modules/lime-painter/hybrid/html/index.html new file mode 100644 index 0000000..fdf884e --- /dev/null +++ b/uni_modules/lime-painter/hybrid/html/index.html @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/uni_modules/lime-painter/hybrid/html/painter.js b/uni_modules/lime-painter/hybrid/html/painter.js new file mode 100644 index 0000000..de93edb --- /dev/null +++ b/uni_modules/lime-painter/hybrid/html/painter.js @@ -0,0 +1 @@ +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).Painter={})}(this,(function(t){"use strict";var e=function(){return e=Object.assign||function(t){for(var e,i=1,n=arguments.length;i0&&r[r.length-1])||6!==o[0]&&2!==o[0])){s=0;continue}if(3===o[0]&&(!r||o[1]>r[0]&&o[1]=360&&(s-=360);s<0&&(s+=360);if(0===(s=Math.round(s)))return{x0:Math.round(e/2)+n,y0:i+r,x1:Math.round(e/2)+n,y1:r};if(180===s)return{x0:Math.round(e/2)+n,y0:r,x1:Math.round(e/2)+n,y1:i+r};if(90===s)return{x0:n,y0:Math.round(i/2)+r,x1:e+n,y1:Math.round(i/2)+r};if(270===s)return{x0:e+n,y0:Math.round(i/2)+r,x1:n,y1:Math.round(i/2)+r};var a=Math.round(180*Math.asin(e/Math.sqrt(Math.pow(e,2)+Math.pow(i,2)))/Math.PI);if(s===a)return{x0:n,y0:i+r,x1:e+n,y1:r};if(s===180-a)return{x0:n,y0:r,x1:e+n,y1:i+r};if(s===180+a)return{x0:e+n,y0:r,x1:n,y1:i+r};if(s===360-a)return{x0:e+n,y0:i+r,x1:n,y1:r};var h=0,c=0,f=0,d=0;if(s180-a&&s<180||s>180&&s<180+a||s>360-a){var l=s*Math.PI/180,u=s360-a?i/2:-i/2,p=Math.tan(l)*u,g=s180-a&&s<180?e/2-p:-e/2-p;h=-(f=p+(v=Math.pow(Math.sin(l),2)*g)),c=-(d=u+v/Math.tan(l))}if(s>a&&s<90||s>90&&s<90+a||s>180+a&&s<270||s>270&&s<360-a){var v;l=(90-s)*Math.PI/180,p=s>a&&s<90||s>90&&s<90+a?e/2:-e/2,u=Math.tan(l)*p,g=s>a&&s<90||s>270&&s<360-a?i/2-u:-i/2-u;h=-(f=p+(v=Math.pow(Math.sin(l),2)*g)/Math.tan(l)),c=-(d=u+v)}return h=Math.round(h+e/2)+n,c=Math.round(i/2-c)+r,f=Math.round(f+e/2)+n,d=Math.round(i/2-d)+r,{x0:h,y0:c,x1:f,y1:d}}(r,t,e,i,n),a=s.x0,h=s.y0,c=s.x1,f=s.y1,d=o.createLinearGradient(a,h,c,f),l=r.match(/linear-gradient\((.+)\)/)[1],u=q(l.substring(l.indexOf(",")+1)),p=0;pt.length)&&(e=t.length);for(var i=0,n=new Array(e);i=t.length?{done:!0}:{done:!1,value:t[n++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function K(t){return"number"==typeof t}function et(t){return"auto"===t||null===t}function it(t){return/%$/.test(t)}var nt=I,rt=z,ot=S,st=M,at=B,ht=O,ct=T;function ft(t){return t.replace(/-([a-z])/g,(function(t,e){return e.toUpperCase()}))}function dt(t,e){var i,n,r=function(t){var e=t.match(/([a-z]+)/)[1];return[e,ft(t.split(e)[1])]}(t),o=r[0],s=r[1],a=e.split(" ");if(s)return(i={})[o+s]=e,i;if(a.length&&!s){var h=a[0],c=a[1],f=a[2],l=a[3];return(n={})[o+d[0]]=h,n[o+d[1]]=c||h,n[o+d[2]]=f||h,n[o+d[3]]=l||c||h,n}}function lt(t){t=t.trim();for(var e=new Array,i="+",n="",r=t.length,o=0;o0;)"("===t[a+=1]&&(s+=1),")"===t[a]&&(s-=1);n="".concat(lt(t.slice(o+1,a))),o=a}if(isNaN(Number(t[o]))&&"."!==t[o]||o===r-1){var h=parseFloat(n);switch(i){case"+":e.push(h);break;case"-":e.push(-h);break;case"*":e.push(e.pop()*h);break;case"/":e.push(e.pop()/h)}i=t[o],n=""}}for(var c=0;e.length;)c+=e.pop();return c}var ut,pt=0,gt=function(){function t(){G(this,"elements",[]),G(this,"afterElements",[]),G(this,"beforeElements",[]),G(this,"ids",[]),G(this,"width",0),G(this,"height",0),G(this,"top",0),G(this,"left",0),G(this,"pre",null),G(this,"offsetX",0),G(this,"offsetY",0),pt++,this.id=pt}var e=t.prototype;return e.fixedBind=function(t,e){void 0===e&&(e=0),this.container=e?t.parent:t.root,this.container.fixedLine=this,this.fixedAdd(t)},e.fixedAdd=function(t){if(!this.ids.includes(t.id)){this.ids.push(t.id),this.elements.push(t);var e=t.computedStyle.zIndex;(void 0===e?0:e)>=0?this.afterElements.push(t):this.beforeElements.push(t),this.refreshLayout()}},e.bind=function(t){this.container=t.parent,this.container.line=null,this.container.lines?(this.container.lines.push(this),this.pre=this.getPreLine(),this.top=this.pre.top+this.pre.height,this.left=this.container.contentSize.left):(this.top=this.container.contentSize.top,this.left=this.container.contentSize.left,this.container.lines=[this]),this.isInline=t.isInline(),this.container.line=this,this.outerWidth=t.parent&&t.parent.contentSize.width?t.parent.contentSize.width:1/0,this.add(t)},e.getPreLine=function(){return this.container.lines[this.container.lines.length-2]},e.canIEnter=function(t){return!((100*t.offsetSize.width+100*this.width)/100>this.outerWidth)||(this.closeLine(),!1)},e.closeLine=function(){delete this.container.line},e.add=function(t){this.ids.includes(t.id)||(this.ids.push(t.id),this.elements.push(t),this.refreshWidthHeight(t))},e.refreshWidthHeight=function(t){t.offsetSize.height>this.height&&(this.height=t.offsetSize.height),this.width+=t.offsetSize.width||0,(this.container.lineMaxWidth||0)this[this.key.height]&&(this.container[this.key.lineMaxHeight]=this[this.key.height]=i),this[this.key.width]+=this.getWidth(t.offsetSize);var n=Math.min(this.getWidth(this),!this.getWidth(this.container.contentSize)&&1/0);(this.container[this.key.lineMaxWidth]||0)1)return 0;var e=t.style.alignSelf,i=this.getHeight(this.container.contentSize),n=i-this.getHeight(t.offsetSize);return e===f?n:e===h?n/2:"stretch"===e?(n&&t.name==S&&(t.style[this.key.width]=this.getWidth(t.offsetSize),t.style[this.key.height]=i,delete t.line,delete t.lines,t.getBoxWidthHeight()),0):0},r.layout=function(t,e){var i=this;this.refreshXAlign(),this.pre?(this.top=this.pre.top+this.pre.height+this.offsetY,this.left=e+this.offsetX):(this.top=Math.max(this.top,this.container.contentSize.top,t)+this.offsetY,this.left=Math.max(this.left,this.container.contentSize.left,e)+this.offsetX),this.elements.forEach((function(t,e){i.setIndent(t);var n=i.elements[e-1],r=i.getOffsetY(t);t.style[i.key.top]=i[i.key.top]+r,t.style[i.key.left]=n?n.offsetSize[i.key.left]+i.getWidth(n.offsetSize):i[i.key.left],t.getBoxPosition()}))},n}(gt),wt=I,mt=z,St=S,zt=k,It=B,Mt=P,kt=O,Bt=T,Wt=0,Pt={left:null,top:null,width:null,height:null},Ot=new Map,Tt=function(){function t(t,e,i,n){var r=this;G(this,"id",Wt++),G(this,"style",{left:null,top:null,width:null,height:null}),G(this,"computedStyle",{}),G(this,"originStyle",{}),G(this,"children",{}),G(this,"layoutBox",V({},Pt)),G(this,"contentSize",V({},Pt)),G(this,"clientSize",V({},Pt)),G(this,"borderSize",V({},Pt)),G(this,"offsetSize",V({},Pt)),this.ctx=n,this.root=i,e&&(this.parent=e),this.name=t.type||t.name,this.attributes=this.getAttributes(t);var o=function(t,e){var i,n=["color","fontSize","lineHeight","verticalAlign","fontWeight","textAlign"],r=t.type,o=void 0===r?ot:r,s=t.styles,h=void 0===s?{}:s,c=(e||{}).computedStyle,f=Object.assign({},F);if([rt,nt,st].includes(o)&&!h.display&&(f.display=at),c)for(var l=0;l=0&&f<0,$=h>=0&&l<0;return i==y[0]&&(this[i].left=t.left+s+g+j+(D?2*-f:0),this[i].top=t.top+h+b+W+($?2*-l:0),this[i].width=t.width+(this[i].widthAdd?0:C),this[i].height=t.height+(this[i].heightAdd?0:H),this[i].widthAdd=C,this[i].heightAdd=H),i==y[1]&&(this[i].left=t.left+s+j+(D<0?-f:0),this[i].top=t.top+h+W+($?-l:0),this[i].width=t.width+g+w,this[i].height=t.height+b+S),i==y[2]&&(this[i].left=t.left+s+j/2+(D<0?-f:0),this[i].top=t.top+h+W/2+($?-l:0),this[i].width=t.width+g+w+j/2+F/2,this[i].height=t.height+b+S+T/2+W/2),i==y[3]&&(this[i].left=t.left+(D<0?-f:0),this[i].top=t.top+($?-l:0),this[i].width=t.width+g+w+j+F+s+f,this[i].height=t.height+b+S+T+W+l+h),this[i]},e.layoutBoxUpdate=function(t,e,i,n){var r=this;if(void 0===i&&(i=-1),"border-box"==e.boxSizing){var o=e||{},s=o.border,a=(s=void 0===s?{}:s).borderWidth,h=void 0===a?0:a,c=o.borderTop,f=(c=void 0===c?{}:c).borderTopWidth,d=void 0===f?h:f,l=o.borderBottom,u=(l=void 0===l?{}:l).borderBottomWidth,p=void 0===u?h:u,g=o.borderRight,v=(g=void 0===g?{}:g).borderRightWidth,b=void 0===v?h:v,x=o.borderLeft,w=(x=void 0===x?{}:x).borderLeftWidth,m=void 0===w?h:w,S=o.padding,z=(S=void 0===S?{}:S).paddingTop,I=void 0===z?0:z,M=S.paddingRight,k=void 0===M?0:M,B=S.paddingBottom,W=void 0===B?0:B,P=S.paddingLeft,O=void 0===P?0:P;i||(t.width-=O+k+b+m),1!==i||n||(t.height-=I+W+d+p)}this.layoutBox&&(y.forEach((function(i){return r.layoutBox[i]=r.getOffsetSize(t,e,i)})),this.layoutBox=Object.assign({},this.layoutBox,this.layoutBox.borderSize))},e.getBoxPosition=function(){var t=this.computedStyle,e=this.fixedLine,i=this.lines,n=t.left,r=void 0===n?0:n,o=t.top,s=void 0===o?0:o,a=V({},this.contentSize,{left:r,top:s}),h=this.contentSize.top-this.offsetSize.top,c=this.contentSize.left-this.offsetSize.left;if(this.root.fixedLine&&!this.root.isDone){this.root.isDone=!0;for(var f,d=Z(this.root.fixedLine.elements);!(f=d()).done;){var l=f.value;l.setPosition(l,this.root.offsetSize),l.getBoxPosition()}}if(e)for(var u,p=Z(e.elements);!(u=p()).done;){var g=u.value,v=V({},this.borderSize,{left:r,top:s});g.setPosition(g,v);var y=this.borderSize.top-this.offsetSize.top,b=this.borderSize.left-this.offsetSize.left;g.style.left+=r+b,g.style.top+=s+y,g.getBoxPosition()}if(i)for(var x,w=Z(i);!(x=w()).done;){x.value.layout(a.top+h,a.left+c)}return this.layoutBoxUpdate(a,t),this.layoutBox},e.getBoxState=function(t,e){return this.isBlock(t)||this.isBlock(e)},e.isBlock=function(t){return void 0===t&&(t=this),t&&t.style.display==zt},e.isFlex=function(t){return void 0===t&&(t=this),t&&t.style.display==Mt},e.isInFlow=function(){return!(this.isAbsolute||this.isFixed)},e.inFlexBox=function(t){return void 0===t&&(t=this),!!t.isInFlow()&&(!!t.parent&&(!(!t.parent||t.parent.style.display!==Mt)||void 0))},e.isInline=function(t){return void 0===t&&(t=this),t&&t.style.display==It},e.contrastSize=function(t,e,i){var n=t;return i&&(n=Math.min(n,i)),e&&(n=Math.max(n,e)),n},e.measureText=function(t,e){var i=this.ctx.measureText(t),n=i.width,r=i.actualBoundingBoxAscent,o=i.actualBoundingBoxDescent;return{ascent:r,descent:o,width:n,fontHeight:r+o||.7*e+1}},e.getParentSize=function(t,e){if(void 0===t&&(t=this),void 0===e&&(e=!1),t&&t.parent){if(t.parent.contentSize.width)return t.parent.contentSize;if(e)return this.getParentSize(t.parent,e)}return null},e.getBoxWidthHeight=function(){var t=this,e=this.name,i=this.computedStyle,n=this.attributes,r=this.parent,o=void 0===r?{}:r,s=this.ctx,a=this.getChildren(),h=i.left,c=void 0===h?0:h,f=i.top,d=void 0===f?0:f,l=i.bottom,u=i.right,p=i.width,g=void 0===p?0:p,v=i.minWidth,y=i.maxWidth,b=i.minHeight,x=i.maxHeight,w=i.height,m=void 0===w?0:w,S=i.fontSize,z=i.fontWeight,I=i.fontFamily,M=i.fontStyle,k=i.position;i.textIndent;var B=i.lineClamp,W=i.lineHeight,P=i.padding,O=void 0===P?{}:P,T=i.margin,L=void 0===T?{}:T,R=i.border,F=(R=void 0===R?{}:R).borderWidth,A=void 0===F?0:F,E=i.borderRight,j=(E=void 0===E?{}:E).borderRightWidth,C=void 0===j?A:j,H=i.borderLeft,D=(H=void 0===H?{}:H).borderLeftWidth,$=void 0===D?A:D,U=o.contentSize&&o.contentSize.width,N=o.contentSize&&o.contentSize.height;if(it(g)&&U&&(g=Y(g,U)),it(g)&&!U&&(g=null),it(m)&&N&&(m=Y(m,N)),it(m)&&!N&&(m=null),it(v)&&U&&(v=Y(v,U)),it(y)&&U&&(y=Y(y,U)),it(b)&&N&&(b=Y(b,N)),it(x)&&N&&(x=Y(x,N)),i.padding&&U)for(var _ in i.padding)Object.hasOwnProperty.call(O,_)&&(O[_]=Y(O[_],U));var X=O.paddingRight,q=void 0===X?0:X,G=O.paddingLeft,V=void 0===G?0:G;if(i.margin&&[L.marginLeft,L.marginRight].includes("auto"))if(g){var J=U&&U-g-q-V-$-C||0;L.marginLeft==L.marginRight?L.marginLeft=L.marginRight=J/2:et(L.marginLeft)?L.marginLeft=J:L.marginRight=J}else L.marginLeft=L.marginRight=0;var Q=L.marginRight,Z=void 0===Q?0:Q,K=L.marginLeft,tt={width:g,height:m,left:0,top:0},nt=V+q+$+C+(void 0===K?0:K)+Z;if(this.offsetWidth=nt,e==mt&&!this.attributes.widths){var rt=n.text||"";s.save(),s.setFonts({fontFamily:I,fontSize:S,fontWeight:z,fontStyle:M}),rt.length,"\n"==rt&&(rt="",this.isBr=!0),(""+rt).split("\n").map((function(e){var i=Array.from(e).map((function(e){var i=""+(/^[\u4e00-\u9fa5]+$/.test(e)?"cn":e)+I+S+z+M,n=Ot.get(i);if(n)return{width:n,text:e};var r=t.measureText(e,S).width;return Ot.set(i,r),{width:r,text:e}})),n=t.measureText(e,S),r=n.fontHeight,o=n.ascent,s=n.descent;t.attributes.fontHeight=r,t.attributes.ascent=o,t.attributes.descent=s,t.attributes.widths||(t.attributes.widths=[]),t.attributes.widths.push({widths:i,total:i.reduce((function(t,e){return t+e.width}),0)})})),s.restore()}if(e==wt&&null==g){var ot=n.width,st=n.height;tt.width=this.contrastSize(Math.round(ot*m/st)||0,v,y),this.layoutBoxUpdate(tt,i,0)}if(e==mt&&null==g){var at=this.attributes.widths,ht=Math.max.apply(Math,at.map((function(t){return t.total})));if(o&&U>0&&(ht>U||this.isBlock(this))&&!this.isAbsolute&&!this.isFixed)ht=U;tt.width=this.contrastSize(ht,v,y),this.layoutBoxUpdate(tt,i,0)}if(e==mt&&(o.style.flex||!this.attributes.lines)){var ct=this.attributes.widths.length;this.attributes.widths.forEach((function(t){return t.widths.reduce((function(t,e,i){return t+e.width>tt.width?(ct++,e.width):t+e.width}),0)})),ct=B&&ct>B?B:ct,this.attributes.lines=ct}if(e==wt&&null==m){var ft=n.width,dt=n.height;n.text,tt.height=this.contrastSize(Y(tt.width*dt/ft)||0,b,x),this.layoutBoxUpdate(tt,i,1)}e==mt&&null==m&&(W=Y(W,S),tt.height=this.contrastSize(Y(this.attributes.lines*W),b,x),this.layoutBoxUpdate(tt,i,1,!0)),!g&&o&&o.children&&U&&(!this.isFlex(o)||o.isFlexCalc)&&([St,mt].includes(e)&&this.isFlex()||e==St&&this.isBlock(this)&&this.isInFlow())&&(tt.width=this.contrastSize(U-(o.isFlexCalc?0:nt),v,y),this.layoutBoxUpdate(tt,i)),g&&!it(g)&&(tt.width=this.contrastSize(g,v,y),this.layoutBoxUpdate(tt,i,0)),m&&!it(m)&&(tt.height=this.contrastSize(tt.height,b,x),this.layoutBoxUpdate(tt,i,1));var lt=0;if(a.length){var ut=null,pt=!1;a.forEach((function(e,n){e.getBoxWidthHeight();var r=a[n+1];if(r&&r.isInFlow()&&(e.next=r),!t.line||!t.line.ids.includes(e.id))if(e.isInFlow()&&!e.inFlexBox()){var o=t.getBoxState(ut,e);if(e.isBr)return pt=!0;t.line&&t.line.canIEnter(e)&&!o&&!pt?t.line.add(e):(pt=!1,(new gt).bind(e)),ut=e}else e.inFlexBox()?t.line&&(t.line.canIEnter(e)||"nowrap"==i.flexWrap)?t.line.add(e):(new xt).bind(e):e.isFixed?t.root.fixedLine?t.root.fixedLine.fixedAdd(e):(new gt).fixedBind(e):t.fixedLine?t.fixedLine.fixedAdd(e):(new gt).fixedBind(e,1)})),this.lines&&(lt=this.lines.reduce((function(t,e){return t+e.height}),0))}var vt=0,yt=0;if(!g&&(this.isAbsolute||this.isFixed)&&U){var bt=k==kt?U:this.root.width,zt=bt-(it(c)?Y(c,bt):c)-(it(u)?Y(u,bt):u);vt=i.left?zt:this.lineMaxWidth}if(!m&&(null!=d?d:this.isAbsolute||this.isFixed&&N)){var It=k==kt?N:this.root.height,Mt=It-(it(d)?Y(d,It):d)-(it(l)?Y(l,It):l);yt=i.top?Mt:0}if(g&&!it(g)||tt.width||(tt.width=vt||this.contrastSize((this.isBlock(this)&&!this.isInFlow()?U||o.lineMaxWidth:this.lineMaxWidth)||this.lineMaxWidth,v,y),this.layoutBoxUpdate(tt,i,0)),m||!lt&&!yt||(tt.height=yt||this.contrastSize(lt,b,x),this.layoutBoxUpdate(tt,i)),i.borderRadius&&this.borderSize&&this.borderSize.width)for(var _ in i.borderRadius)Object.hasOwnProperty.call(i.borderRadius,_)&&(i.borderRadius[_]=Y(i.borderRadius[_],this.borderSize.width));return this.layoutBox},e.layout=function(){return this.getBoxWidthHeight(),this.root.offsetSize=this.offsetSize,this.root.contentSize=this.contentSize,this.getBoxPosition(),this.offsetSize},t}(),Lt=function(){var t,e,i,n,r,o,s=[0,11,15,19,23,27,31,16,18,20,22,24,26,28,20,22,24,24,26,28,28,22,24,24,26,26,28,28,24,24,26,26,26,28,28,24,26,26,26,28,28],a=[3220,1468,2713,1235,3062,1890,2119,1549,2344,2936,1117,2583,1330,2470,1667,2249,2028,3780,481,4011,142,3098,831,3445,592,2517,1776,2234,1951,2827,1070,2660,1345,3177],h=[30660,29427,32170,30877,26159,25368,27713,26998,21522,20773,24188,23371,17913,16590,20375,19104,13663,12392,16177,14854,9396,8579,11994,11245,5769,5054,7399,6608,1890,597,3340,2107],c=[1,0,19,7,1,0,16,10,1,0,13,13,1,0,9,17,1,0,34,10,1,0,28,16,1,0,22,22,1,0,16,28,1,0,55,15,1,0,44,26,2,0,17,18,2,0,13,22,1,0,80,20,2,0,32,18,2,0,24,26,4,0,9,16,1,0,108,26,2,0,43,24,2,2,15,18,2,2,11,22,2,0,68,18,4,0,27,16,4,0,19,24,4,0,15,28,2,0,78,20,4,0,31,18,2,4,14,18,4,1,13,26,2,0,97,24,2,2,38,22,4,2,18,22,4,2,14,26,2,0,116,30,3,2,36,22,4,4,16,20,4,4,12,24,2,2,68,18,4,1,43,26,6,2,19,24,6,2,15,28,4,0,81,20,1,4,50,30,4,4,22,28,3,8,12,24,2,2,92,24,6,2,36,22,4,6,20,26,7,4,14,28,4,0,107,26,8,1,37,22,8,4,20,24,12,4,11,22,3,1,115,30,4,5,40,24,11,5,16,20,11,5,12,24,5,1,87,22,5,5,41,24,5,7,24,30,11,7,12,24,5,1,98,24,7,3,45,28,15,2,19,24,3,13,15,30,1,5,107,28,10,1,46,28,1,15,22,28,2,17,14,28,5,1,120,30,9,4,43,26,17,1,22,28,2,19,14,28,3,4,113,28,3,11,44,26,17,4,21,26,9,16,13,26,3,5,107,28,3,13,41,26,15,5,24,30,15,10,15,28,4,4,116,28,17,0,42,26,17,6,22,28,19,6,16,30,2,7,111,28,17,0,46,28,7,16,24,30,34,0,13,24,4,5,121,30,4,14,47,28,11,14,24,30,16,14,15,30,6,4,117,30,6,14,45,28,11,16,24,30,30,2,16,30,8,4,106,26,8,13,47,28,7,22,24,30,22,13,15,30,10,2,114,28,19,4,46,28,28,6,22,28,33,4,16,30,8,4,122,30,22,3,45,28,8,26,23,30,12,28,15,30,3,10,117,30,3,23,45,28,4,31,24,30,11,31,15,30,7,7,116,30,21,7,45,28,1,37,23,30,19,26,15,30,5,10,115,30,19,10,47,28,15,25,24,30,23,25,15,30,13,3,115,30,2,29,46,28,42,1,24,30,23,28,15,30,17,0,115,30,10,23,46,28,10,35,24,30,19,35,15,30,17,1,115,30,14,21,46,28,29,19,24,30,11,46,15,30,13,6,115,30,14,23,46,28,44,7,24,30,59,1,16,30,12,7,121,30,12,26,47,28,39,14,24,30,22,41,15,30,6,14,121,30,6,34,47,28,46,10,24,30,2,64,15,30,17,4,122,30,29,14,46,28,49,10,24,30,24,46,15,30,4,18,122,30,13,32,46,28,48,14,24,30,42,32,15,30,20,4,117,30,40,7,47,28,43,22,24,30,10,67,15,30,19,6,118,30,18,31,47,28,34,34,24,30,20,61,15,30],f=[255,0,1,25,2,50,26,198,3,223,51,238,27,104,199,75,4,100,224,14,52,141,239,129,28,193,105,248,200,8,76,113,5,138,101,47,225,36,15,33,53,147,142,218,240,18,130,69,29,181,194,125,106,39,249,185,201,154,9,120,77,228,114,166,6,191,139,98,102,221,48,253,226,152,37,179,16,145,34,136,54,208,148,206,143,150,219,189,241,210,19,92,131,56,70,64,30,66,182,163,195,72,126,110,107,58,40,84,250,133,186,61,202,94,155,159,10,21,121,43,78,212,229,172,115,243,167,87,7,112,192,247,140,128,99,13,103,74,222,237,49,197,254,24,227,165,153,119,38,184,180,124,17,68,146,217,35,32,137,46,55,63,209,91,149,188,207,205,144,135,151,178,220,252,190,97,242,86,211,171,20,42,93,158,132,60,57,83,71,109,65,162,31,45,67,216,183,123,164,118,196,23,73,236,127,12,111,246,108,161,59,82,41,157,85,170,251,96,134,177,187,204,62,90,203,89,95,176,156,169,160,81,11,245,22,235,122,117,44,215,79,174,213,233,230,231,173,232,116,214,244,234,168,80,88,175],d=[1,2,4,8,16,32,64,128,29,58,116,232,205,135,19,38,76,152,45,90,180,117,234,201,143,3,6,12,24,48,96,192,157,39,78,156,37,74,148,53,106,212,181,119,238,193,159,35,70,140,5,10,20,40,80,160,93,186,105,210,185,111,222,161,95,190,97,194,153,47,94,188,101,202,137,15,30,60,120,240,253,231,211,187,107,214,177,127,254,225,223,163,91,182,113,226,217,175,67,134,17,34,68,136,13,26,52,104,208,189,103,206,129,31,62,124,248,237,199,147,59,118,236,197,151,51,102,204,133,23,46,92,184,109,218,169,79,158,33,66,132,21,42,84,168,77,154,41,82,164,85,170,73,146,57,114,228,213,183,115,230,209,191,99,198,145,63,126,252,229,215,179,123,246,241,255,227,219,171,75,150,49,98,196,149,55,110,220,165,87,174,65,130,25,50,100,200,141,7,14,28,56,112,224,221,167,83,166,81,162,89,178,121,242,249,239,195,155,43,86,172,69,138,9,18,36,72,144,61,122,244,245,247,243,251,235,203,139,11,22,44,88,176,125,250,233,207,131,27,54,108,216,173,71,142,0],l=[],u=[],p=[],g=[],v=[],y=2;function b(t,e){var i;t>e&&(i=t,t=e,e=i),i=e,i*=e,i+=e,i>>=1,g[i+=t]=1}function x(t,i){var n;for(p[t+e*i]=1,n=-2;n<2;n++)p[t+n+e*(i-2)]=1,p[t-2+e*(i+n+1)]=1,p[t+2+e*(i+n)]=1,p[t+n+1+e*(i+2)]=1;for(n=0;n<2;n++)b(t-1,i+n),b(t+1,i-n),b(t-n,i-1),b(t+n,i+1)}function w(t){for(;t>=255;)t=((t-=255)>>8)+(255&t);return t}var m=[];function S(t,e,i,n){var r,o,s;for(r=0;re&&(i=t,t=e,e=i),i=e,i+=e*e,i>>=1,g[i+=t]}function I(t){var i,n,r,o;switch(t){case 0:for(n=0;n>1&1,i=0;i=5&&(i+=3+v[e]-5);for(e=3;et||3*v[e-3]>=4*v[e]||3*v[e+3]>=4*v[e])&&(i+=40);return i}function k(){var t,i,n,r,o,s=0,a=0;for(i=0;ie*e;)h-=e*e,c++;for(s+=10*c,t=0;t1)for(P=s[t],B=e-7;;){for(M=e-7;M>P-3&&(x(M,B),!(M6)for(P=a[t-7],W=17,M=0;M<6;M++)for(B=0;B<3;B++,W--)1&(W>11?t>>W-12:P>>W)?(p[5-M+e*(2-B+e-11)]=1,p[2-B+e-11+e*(5-M)]=1):(b(5-M,2-B+e-11),b(2-B+e-11,5-M));for(B=0;B=(M=r*(i+n)+n)-2&&(O=M-2,t>9&&O--),T=O,t>9){for(l[T+2]=0,l[T+3]=0;T--;)P=l[T],l[T+3]|=255&P<<4,l[T+2]=P>>4;l[2]|=255&O<<4,l[1]=O>>4,l[0]=64|O>>12}else{for(l[T+1]=0,l[T+2]=0;T--;)P=l[T],l[T+2]|=255&P<<4,l[T+1]=P>>4;l[1]|=255&O<<4,l[0]=64|O>>4}for(T=O+3-(t<10);T0;L--)m[L]=m[L]?m[L-1]^d[w(f[m[L]]+T)]:m[L-1];m[0]=d[w(f[m[0]]+T)]}for(T=0;T<=o;T++)m[T]=f[m[T]];for(W=M,B=0,T=0;T>=1)1&B&&(p[e-1-W+8*e]=1,W<6?p[8+e*W]=1:p[8+e*(W+1)]=1);for(W=0;W<7;W++,B>>=1)1&B&&(p[8+e*(e-7+W)]=1,W?p[6-W+8*e]=1:p[7+8*e]=1);return p}(v)},utf16to8:function(t){var e,i,n,r;for(e="",n=t.length,i=0;i=1&&r<=127?e+=t.charAt(i):r>2047?(e+=String.fromCharCode(224|r>>12&15),e+=String.fromCharCode(128|r>>6&63),e+=String.fromCharCode(128|r>>0&63)):(e+=String.fromCharCode(192|r>>6&31),e+=String.fromCharCode(128|r>>0&63));return e},draw:function(t,i,n,r,o){i.drawView(n,r);var s=i.ctx,a=n.contentSize,h=a.width,c=a.height,f=a.left,d=a.top;r.borderRadius,r.backgroundColor;var l=r.color,u=void 0===l?"#000000":l;r.border,n.contentSize.left,n.borderSize.left,n.contentSize.top,n.borderSize.top;if(y=o||y,s){s.save(),i.setOpacity(r),i.setTransform(n,r);var p=Math.min(h,c);t=this.utf16to8(t);var g=this.getFrame(t),v=p/e;s.setFillStyle(u);for(var b=0;b=s||n==c&&o=s)&&(a=e.width/i.width);var f=i.width*a,d=i.height*a,l=r||[],u=l[0],p=l[1],g=N(u)?Y(u,e.width):(e.width-f)*(U(u)?Y(u,1):{left:0,center:.5,right:1}[u||"center"]),v=N(p)?Y(p,e.height):(e.height-d)*(U(p)?Y(p,1):{top:0,center:.5,bottom:1}[p||"center"]),y=function(t,e){return[(t-g)/a,(e-v)/a]},b=y(0,0),x=b[0],w=b[1],m=y(e.width,e.height),S=m[0],z=m[1],I=Math.max,M=Math.min;return{sx:I(x,0),sy:I(w,0),sw:M(S-x,i.width),sh:M(z-w,i.height),dx:I(g,0),dy:I(v,0),dw:M(f,e.width),dh:M(d,e.height)}}({objectFit:u,objectPosition:v},e.contentSize,t),o=n.sx,s=n.sy,a=n.sh,h=n.sw,c=n.dx,f=n.dy,d=n.dh,l=n.dw;C==r.MP_BAIDU?i.drawImage(t.src,c+m,f+S,l,d,o,s,h,a):i.drawImage(t.src,o,s,h,a,c+m,f+S,l,d)}else i.drawImage(t.src,m,S,x,w)},k=function(){i.restore(),W.drawView(e,o,!1,!0,!1),h(1)},B=function(t){M(t),k()},B(t),[2]}))}))}))];case 1:return h.sent(),[2]}}))}))},t.prototype.drawText=function(t,e,i,n){var r=this,o=this.ctx,s=e.borderSize,a=e.contentSize,h=e.left,c=e.top,f=a.width,d=a.height,l=a.left-s.left||0,u=a.top-s.top||0,p=i.color,g=i.lineHeight,v=i.fontSize,y=i.fontWeight,b=i.fontFamily,x=i.fontStyle,w=i.textIndent,m=void 0===w?0:w,S=i.textAlign,z=i.textStroke,I=i.verticalAlign,M=void 0===I?Ct:I,k=i.backgroundColor,B=i.lineClamp,W=i.backgroundClip,P=i.textShadow,O=i.textDecoration;if(m=$(m)?m:0,this.drawView(e,i,W!=Ft),g=Y(g,v),t){o.save(),h+=l,c+=u;var T=n.fontHeight,L=n.descent,R=void 0===L?0:L,F=n.ascent,A=R+(void 0===F?0:F);switch(o.setFonts({fontFamily:b,fontSize:v,fontWeight:y,fontStyle:x}),o.setTextBaseline(Ct),o.setTextAlign(S),W?this.setBackground(k,f,d,h,c):o.setFillStyle(p),S){case Dt:break;case $t:h+=.5*f;break;case Yt:h+=f}var E=n.lines*g,j=Math.ceil((d-E)/2);switch(j<0&&(j=0),M){case jt:break;case Ct:c+=j;break;case Ht:c+=2*j}var C=(g-T)/2,H=g/2,D=function(t){var e=o.measureText(t),i=e.actualBoundingBoxDescent,n=void 0===i?0:i,r=e.actualBoundingBoxAscent;return M==jt?{fix:A?void 0===r?0:r:H-C/2,lineY:A?0:C-C/2}:M==Ct?{fix:A?H+n/4:H,lineY:A?0:C}:M==Ht?{fix:A?g-n:H+C/2,lineY:A?2*C:C+C/2}:{fix:0,height:0,lineY:0}},U=function(t,e,i){var r=t;switch(S){case Dt:r+=i;break;case $t:r=(t-=i/2)+i;break;case Yt:r=t,t-=i}if(O){o.setLineWidth(v/13),o.beginPath();var s=.1*n.fontHeight;/\bunderline\b/.test(O)&&(o.moveTo(t,e+n.fontHeight+s),o.lineTo(r,e+n.fontHeight+s)),/\boverline\b/.test(O)&&(o.moveTo(t,e-s),o.lineTo(r,e-s)),/\bline-through\b/.test(O)&&(o.moveTo(t,e+.5*n.fontHeight),o.lineTo(r,e+.5*n.fontHeight)),o.closePath(),o.setStrokeStyle(p),o.stroke()}},N=function(t,e,i){var n=function(){o.setLineWidth(z.width),o.setStrokeStyle(z.color),o.strokeText(t,e,i)},s="outset";z&&z.type!==s?(o.save(),r.setShadow({boxShadow:P}),o.fillText(t,e,i),o.restore(),n()):z&&z.type==s?(o.save(),r.setShadow({boxShadow:P}),n(),o.restore(),o.save(),o.fillText(t,e,i),o.restore()):(r.setShadow({boxShadow:P}),o.fillText(t,e,i))};if(!n.widths||1==n.widths.length&&n.widths[0].total+m<=a.width){var _=D(t),X=_.fix,q=void 0===X?0:X,G=_.lineY;return N(t,h+m,c+q),U(h+m,c+G,n&&n.widths&&n.widths[0].total||n.text),c+=g,o.restore(),void this.setBorder(e,i)}for(var V=c,J=h,Q="",Z=0,K=o.measureText("...").width,tt=n.widths,et=0;eta.width){Z>=B&&(Q+="…"),Z++,nt=0;var ct=D(Q);q=ct.fix,G=ct.lineY;N(Q,J,c+q),U(J,c+G,nt),c+=g,Q=""}else if(rt==it.length-1){et!=tt.length-1&&Z==B&&K+ntV+d||Z>B)break}}o.restore()}},t.prototype.source=function(t){return i(this,void 0,void 0,(function(){var e,i,r,o,s=this;return n(this,(function(n){switch(n.label){case 0:if(this.node=null,e=+new Date,"{}"==JSON.stringify(t))return[2];if(t.styles=t.styles||t.css||{},!t.type)for(i in t.type=Et,t)["views","children","type","css","styles"].includes(i)||(t.styles[i]=t[i],delete t[i]);return t.styles.boxSizing||(t.styles.boxSizing="border-box"),[4,this.create(t)];case 1:return(r=n.sent())?(o=r.layout()||{},this.size=o,this.node=r,this.onEffectFinished().then((function(t){return s.lifecycle("onEffectSuccess",t)})).catch((function(t){return s.lifecycle("onEffectFail",t)})),this.performance&&console.log("布局用时:"+(+new Date-e)+"ms"),[2,this.size]):[2,console.warn("no node")]}}))}))},t.prototype.getImageInfo=function(t){return this.imageBus[t]||(this.imageBus[t]=this.createImage(t,this.useCORS)),this.imageBus[t]},t.prototype.create=function(t,r){return i(this,void 0,void 0,(function(){function i(t,n,r){void 0===n&&(n={}),void 0===r&&(r=!0);var o=[];return t.forEach((function(t){var s=t.styles,a=void 0===s?{}:s,h=t.css,c=void 0===h?{}:h,f=t.children,d=void 0===f?[]:f,l=t.views,u=void 0===l?[]:l,p=t.text,g=void 0===p?"":p,v=t.type,y=void 0===v?"":v;!d&&u&&(t.children=d=u);var b={};b=e(e(r?e({},n):{},a),c);var x={},w={},m={};Object.keys(b).map((function(t){if(t.includes("padding")||t.includes("margin")){var e=dt(t,b[t]);Object.keys(e).map((function(t){t.includes("Left")?w[t]=e[t]:t.includes("Right")?m[t]=e[t]:x[t]=e[t]}))}}));if(b.textIndent&&(w.textIndent=b.textIndent,delete n.textIndent),""!==g){var S=Array.from(g);S.forEach((function(t,e){var i=Object.assign({},b,x);0===e?Object.assign(i,w):e==S.length-1&&Object.assign(i,m),delete i.padding,delete i.margin,o.push({type:"text",text:t,styles:i})}))}if(y==Rt||y==At)o.push(t);else if("block"===a.display&&d.length>0){var z=i(d,b,!1);t.children=z,t.flattened=!0,o.push(t)}else if(d.length>0){z=i(d,b,r);o=o.concat(z)}})),o}var o,s,a,h,c,f,d,l,u,p,g,v,y,b,x,w,m,S,z,I,M,k,B,P;return n(this,(function(n){switch(n.label){case 0:if(!t)return[2];if(t.styles||(t.styles=t.css||{}),o=t.type,s=t.show,a=void 0===s||s,h=o==Rt,c=[Ft,At].includes(o),f="textBox"==o,d=t.styles||{},l=d.backgroundImage,u=d.display,h&&!t.src&&!t.url)return[2];if(u==W||!a)return[2];if(c||f){if(p=t.children,g=t.views,!p&&g&&(t.children=p=g),!t.text&&(!p||p&&!p.length))return[2];p&&p.length&&!t.flattened&&(v=i(t.children||t.views),t.type="view",t.children=v)}if(!(h||t.type==Et&&l))return[3,4];y=h?t.src:"",b=/url\(['"]?(.*?)['"]?\)/.exec(l),l&&b&&b[1]&&(y=b[1]||""),n.label=1;case 1:return n.trys.push([1,3,,4]),[4,this.getImageInfo(y)];case 2:return x=n.sent(),w=x.width,m=x.height,!(S=x.path)&&h?[2]:(S&&(t.attributes=Object.assign(t.attributes||{},{width:w,height:m,path:S,src:S,naturalSrc:y})),[3,4]);case 3:return z=n.sent(),t.type!=Et?[2]:(this.lifecycle("onEffectFail",e(e({},z),{src:y})),[3,4]);case 4:if(this.count+=1,I=new Tt(t,r,this.root,this.ctx),!(M=t.children||t.views))return[3,8];k=0,n.label=5;case 5:return k0&&void 0!==arguments[0]?arguments[0]:{},n=e.url;r("navigateTo",{url:encodeURI(n)})},navigateBack:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.delta;r("navigateBack",{delta:parseInt(n)||1})},switchTab:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.url;r("switchTab",{url:encodeURI(n)})},reLaunch:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.url;r("reLaunch",{url:encodeURI(n)})},redirectTo:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},n=e.url;r("redirectTo",{url:encodeURI(n)})},getEnv:function(e){o()?e({nvue:!0}):window.plus?e({plus:!0}):e({h5:!0})},postMessage:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};r("postMessage",e.data||{})}},d=/uni-app/i.test(navigator.userAgent),s=/Html5Plus/i.test(navigator.userAgent),w=/complete|loaded|interactive/;var u=window.my&&navigator.userAgent.indexOf("AlipayClient")>-1;var g=window.swan&&window.swan.webView&&/swan/i.test(navigator.userAgent);var c=window.qq&&window.qq.miniProgram&&/QQ/i.test(navigator.userAgent)&&/miniProgram/i.test(navigator.userAgent);var v=window.tt&&window.tt.miniProgram&&/toutiaomicroapp/i.test(navigator.userAgent);var m=window.wx&&window.wx.miniProgram&&/micromessenger/i.test(navigator.userAgent)&&/miniProgram/i.test(navigator.userAgent);var p=window.qa&&/quickapp/i.test(navigator.userAgent);var f=window.ks&&window.ks.miniProgram&&/micromessenger/i.test(navigator.userAgent)&&/miniProgram/i.test(navigator.userAgent);var l=window.tt&&window.tt.miniProgram&&/Lark|Feishu/i.test(navigator.userAgent);var _=window.jd&&window.jd.miniProgram&&/micromessenger/i.test(navigator.userAgent)&&/miniProgram/i.test(navigator.userAgent);for(var E,b=function(){window.UniAppJSBridge=!0,document.dispatchEvent(new CustomEvent("UniAppJSBridgeReady",{bubbles:!0,cancelable:!0}))},h=[function(e){if(d||s)return window.__dcloud_weex_postMessage||window.__dcloud_weex_?document.addEventListener("DOMContentLoaded",e):window.plus&&w.test(document.readyState)?setTimeout(e,0):document.addEventListener("plusready",e),a},function(e){if(m)return window.WeixinJSBridge&&window.WeixinJSBridge.invoke?setTimeout(e,0):document.addEventListener("WeixinJSBridgeReady",e),window.wx.miniProgram},function(e){if(c)return window.QQJSBridge&&window.QQJSBridge.invoke?setTimeout(e,0):document.addEventListener("QQJSBridgeReady",e),window.qq.miniProgram},function(e){if(u){document.addEventListener("DOMContentLoaded",e);var n=window.my;return{navigateTo:n.navigateTo,navigateBack:n.navigateBack,switchTab:n.switchTab,reLaunch:n.reLaunch,redirectTo:n.redirectTo,postMessage:n.postMessage,getEnv:n.getEnv}}},function(e){if(g)return document.addEventListener("DOMContentLoaded",e),window.swan.webView},function(e){if(v)return document.addEventListener("DOMContentLoaded",e),window.tt.miniProgram},function(e){if(p){window.QaJSBridge&&window.QaJSBridge.invoke?setTimeout(e,0):document.addEventListener("QaJSBridgeReady",e);var n=window.qa;return{navigateTo:n.navigateTo,navigateBack:n.navigateBack,switchTab:n.switchTab,reLaunch:n.reLaunch,redirectTo:n.redirectTo,postMessage:n.postMessage,getEnv:n.getEnv}}},function(e){if(f)return window.WeixinJSBridge&&window.WeixinJSBridge.invoke?setTimeout(e,0):document.addEventListener("WeixinJSBridgeReady",e),window.ks.miniProgram},function(e){if(l)return document.addEventListener("DOMContentLoaded",e),window.tt.miniProgram},function(e){if(_)return window.JDJSBridgeReady&&window.JDJSBridgeReady.invoke?setTimeout(e,0):document.addEventListener("JDJSBridgeReady",e),window.jd.miniProgram},function(e){return document.addEventListener("DOMContentLoaded",e),a}],y=0;y\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 convertStyleStringToJSON(styleString) { + var styles = styleString.split(";"); // 通过分号将样式字符串分割为多个样式声明 + var result = {}; + + styles.forEach(function(style) { + var styleParts = style.split(":"); // 通过冒号将样式声明分割为属性和值 + var property = styleParts[0].trim(); + var value = styleParts[1] && styleParts[1].trim(); + + if (property && value) { + result[property] = value; // 将属性和值添加到结果对象中 + } + }); + + return result; +} +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); + node.styles = node.attrs.style ? convertStyleStringToJSON(node.attrs.style) : {} + } + + if(!node.type) { + if(inline[node.name] && node.name !== 'img' ) { + node.type = 'text'; + if(node.name == 'br') { + node.text = '\n' + } else if(node.name == 'strong'){ + node.styles.fontWeight = 'bold' + } + } else if(node.name == 'img'){ + node.type = 'image' + node.src = node.attrs.src + } else { + node.type = 'view' + if(['h1','h2','h3','h4','h5','h6'].includes(node.name)) { + node.styles.fontWeight = 'bold' + } + } + } + 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); + } + const isTextBox = node.children && node.children.length > 1 && node.children.every(child => { + return ['text','image'].includes(child.type) + }) + if(isTextBox) { + node.type = 'textBox' + } + }, + 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; \ No newline at end of file diff --git a/uni_modules/lime-painter/readme.md b/uni_modules/lime-painter/readme.md new file mode 100644 index 0000000..b05000c --- /dev/null +++ b/uni_modules/lime-painter/readme.md @@ -0,0 +1,961 @@ +# Painter 画板 测试版 + +> uniapp 海报画板,更优雅的海报生成方案 +> [查看更多](https://limeui.qcoon.cn/#/painter) + +## 平台兼容 + +| H5 | 微信小程序 | 支付宝小程序 | 百度小程序 | 头条小程序 | QQ 小程序 | App | +| --- | ---------- | ------------ | ---------- | ---------- | --------- | --- | +| √ | √ | √ | 未测 | √ | √ | √ | + +## 安装 +在市场导入**[海报画板](https://ext.dcloud.net.cn/plugin?id=2389)uni_modules**版本的即可,无需`import` + +## 代码演示 + +### 插件demo +- lime-painter 为 demo +- 位于 uni_modules/lime-painter/components/lime-painter +- 导入插件后直接使用可查看demo +```vue + +``` + + +### 基本用法 + +- 插件提供 JSON 及 Template 的方式绘制海报 +- 参考 css 块状流布局模拟 css schema。 +- 另外flex布局还不是成完善,请谨慎使用,普通的流布局我觉得已经够用了。 + +#### 方式一 Template + +- 提供`l-painter-view`、`l-painter-text`、`l-painter-image`、`l-painter-qrcode`四种类型组件 +- 通过 `css` 属性绘制样式,与 style 使用方式保持一致。 +```html + + //如果使用Template出现顺序错乱,可使用`template` 等所有变量完成再显示 +