From 98baaab9bc54495425593b0838a11e8e09a9bbcd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=90=BD=E6=97=A5=E6=99=9A=E9=A3=8E?= <1811466536@qq.com>
Date: Wed, 13 Dec 2023 11:35:02 +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
---
.gitignore | 12 +
.prettierignore | 6 +
.prettierrc | 10 +
App.vue | 46 +
LICENSE | 21 +
README.md | 190 +++
androidPrivacy.json | 3 +
index.html | 17 +
jsconfig.json | 9 +
main.js | 15 +
manifest.json | 240 +++
package.json | 105 ++
pages.json | 763 ++++++++++
pages/activity/groupon/detail.vue | 507 +++++++
pages/activity/groupon/list.vue | 254 ++++
pages/activity/groupon/order.vue | 298 ++++
pages/activity/index.vue | 191 +++
pages/activity/seckill/list.vue | 249 +++
pages/app/score-shop.vue | 77 +
pages/app/sign.vue | 512 +++++++
pages/chat/components/goods.vue | 63 +
pages/chat/components/order.vue | 122 ++
pages/chat/components/select-popup.vue | 151 ++
pages/chat/emoji.js | 58 +
pages/chat/index.vue | 870 +++++++++++
pages/chat/socket.js | 821 ++++++++++
pages/commission/apply.vue | 290 ++++
pages/commission/components/account-info.vue | 108 ++
.../commission/components/commission-auth.vue | 184 +++
.../components/commission-condition.vue | 173 +++
.../commission/components/commission-info.vue | 126 ++
.../commission/components/commission-log.vue | 184 +++
.../commission/components/commission-menu.vue | 153 ++
pages/commission/goods.vue | 137 ++
pages/commission/index.vue | 61 +
pages/commission/order.vue | 417 +++++
pages/commission/share-log.vue | 173 +++
pages/commission/team.vue | 257 ++++
pages/coupon/detail.vue | 372 +++++
pages/coupon/list.vue | 261 ++++
pages/goods/comment/add.vue | 209 +++
pages/goods/comment/list.vue | 167 ++
.../goods/components/detail/comment-item.vue | 94 ++
.../components/detail/detail-activity-tip.vue | 101 ++
.../components/detail/detail-cell-params.vue | 115 ++
.../components/detail/detail-cell-service.vue | 121 ++
.../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 | 51 +
.../goods/components/detail/detail-navbar.vue | 255 ++++
.../components/detail/detail-progress.vue | 39 +
.../components/detail/detail-skeleton.vue | 177 +++
.../goods/components/detail/detail-tabbar.vue | 171 +++
.../components/groupon/groupon-card-list.vue | 137 ++
.../goods/components/list/list-goods-card.vue | 103 ++
pages/goods/components/list/list-navbar.vue | 92 ++
pages/goods/groupon.vue | 597 ++++++++
pages/goods/index.vue | 384 +++++
pages/goods/list.vue | 383 +++++
pages/goods/score.vue | 368 +++++
pages/goods/seckill.vue | 534 +++++++
pages/index/cart.vue | 200 +++
pages/index/category.vue | 236 +++
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 | 90 ++
pages/index/login.vue | 39 +
pages/index/page.vue | 52 +
pages/index/search.vue | 113 ++
pages/index/user.vue | 41 +
pages/order/aftersale/apply.vue | 318 ++++
pages/order/aftersale/detail.vue | 355 +++++
pages/order/aftersale/list.vue | 238 +++
pages/order/aftersale/log-item.vue | 99 ++
pages/order/aftersale/log.vue | 54 +
pages/order/confirm.vue | 414 +++++
pages/order/detail.vue | 692 +++++++++
pages/order/dispatch/content.vue | 84 ++
pages/order/express/list.vue | 104 ++
pages/order/express/log.vue | 174 +++
pages/order/invoice.vue | 329 ++++
pages/order/list.vue | 586 +++++++
pages/pay/components/account-info-modal.vue | 237 +++
pages/pay/components/account-type-select.vue | 178 +++
pages/pay/index.vue | 356 +++++
pages/pay/recharge-log.vue | 171 +++
pages/pay/recharge.vue | 250 +++
pages/pay/result.vue | 285 ++++
pages/pay/withdraw-log.vue | 187 +++
pages/pay/withdraw.vue | 380 +++++
pages/public/error.vue | 57 +
pages/public/faq.vue | 110 ++
pages/public/feedback.vue | 226 +++
pages/public/richtext.vue | 47 +
pages/public/setting.vue | 239 +++
pages/public/webview.vue | 15 +
pages/user/address/edit.vue | 261 ++++
pages/user/address/list.vue | 147 ++
pages/user/goods-collect.vue | 242 +++
pages/user/goods-log.vue | 289 ++++
pages/user/info.vue | 510 +++++++
pages/user/invoice/edit.vue | 277 ++++
pages/user/invoice/list.vue | 82 +
pages/user/set.vue | 88 ++
pages/user/wallet/commission.vue | 510 +++++++
pages/user/wallet/money.vue | 345 +++++
pages/user/wallet/score.vue | 279 ++++
sheep/api/activity.js | 43 +
sheep/api/app.js | 146 ++
sheep/api/cart.js | 42 +
sheep/api/category.js | 10 +
sheep/api/chat.js | 13 +
sheep/api/commission.js | 65 +
sheep/api/coupon.js | 45 +
sheep/api/data.js | 25 +
sheep/api/goods.js | 80 +
sheep/api/index.js | 10 +
sheep/api/index2.js | 14 +
sheep/api/order.js | 192 +++
sheep/api/pay.js | 44 +
sheep/api/product/category.js | 12 +
sheep/api/product/comment.js | 18 +
sheep/api/product/spu.js | 37 +
sheep/api/promotion/activity.js | 16 +
sheep/api/promotion/article.js | 12 +
sheep/api/promotion/combination.js | 67 +
sheep/api/promotion/coupon.js | 20 +
sheep/api/promotion/diy/template.js | 20 +
sheep/api/promotion/seckill.js | 33 +
sheep/api/third.js | 127 ++
sheep/api/trade.js | 37 +
sheep/api/user.js | 459 ++++++
.../s-activity-pop/s-activity-pop.vue | 96 ++
.../s-address-item/s-address-item.vue | 106 ++
.../s-auth-modal/components/account-login.vue | 107 ++
.../s-auth-modal/components/change-mobile.vue | 120 ++
.../components/change-password.vue | 121 ++
.../components/change-username.vue | 72 +
.../components/mp-authorization.vue | 145 ++
.../components/reset-password.vue | 114 ++
.../s-auth-modal/components/sms-login.vue | 115 ++
.../s-auth-modal/components/sms-register.vue | 130 ++
sheep/components/s-auth-modal/index.scss | 151 ++
.../components/s-auth-modal/s-auth-modal.vue | 263 ++++
.../components/s-block-item/s-block-item.vue | 83 +
sheep/components/s-block/s-block.vue | 53 +
.../s-coupon-block/s-coupon-block.vue | 151 ++
.../s-coupon-card/s-coupon-card.vue | 78 +
.../components/s-coupon-get/s-coupon-get.vue | 108 ++
.../s-coupon-list/s-coupon-list.vue | 195 +++
.../s-coupon-select/s-coupon-select.vue | 128 ++
.../components/navbar-item.vue | 62 +
.../s-custom-navbar/components/navbar.vue | 314 ++++
.../s-custom-navbar/s-custom-navbar.vue | 195 +++
.../s-discount-list/s-discount-list.vue | 114 ++
sheep/components/s-empty/s-empty.vue | 93 ++
.../components/s-float-menu/s-float-menu.vue | 99 ++
.../components/s-goods-card/s-goods-card.vue | 285 ++++
.../s-goods-column/s-goods-column.vue | 729 +++++++++
.../components/s-goods-item/s-goods-item.vue | 188 +++
.../s-goods-scroll/s-goods-scroll.vue | 32 +
.../s-goods-shelves/s-goods-shelves.vue | 146 ++
.../s-groupon-block/s-groupon-block.vue | 156 ++
.../s-hotzone-block/s-hotzone-block.vue | 45 +
.../s-image-banner/s-image-banner.vue | 43 +
.../s-image-block/s-image-block.vue | 26 +
.../components/s-image-cube/s-image-cube.vue | 109 ++
.../s-invoice-item/s-invoice-item.vue | 96 ++
sheep/components/s-layout/s-layout.vue | 242 +++
.../components/s-line-block/s-line-block.vue | 14 +
.../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 | 362 +++++
sheep/components/s-menu-grid/s-menu-grid.vue | 81 +
sheep/components/s-menu-list/s-menu-list.vue | 65 +
.../components/s-menu-tools/s-menu-tools.vue | 122 ++
.../s-notice-block/s-notice-block.vue | 38 +
.../components/s-order-card/s-order-card.vue | 104 ++
.../s-popup-image/s-popup-image.vue | 85 ++
.../s-richtext-block/s-richtext-block.vue | 36 +
.../s-score-block/s-score-block.vue | 202 +++
.../components/s-score-card/s-score-card.vue | 459 ++++++
.../s-search-block/s-search-block.vue | 164 ++
.../s-seckill-block/s-seckill-block.vue | 162 ++
.../s-select-groupon-sku.vue | 598 ++++++++
.../s-select-seckill-sku.vue | 440 ++++++
.../components/s-select-sku/s-select-sku.vue | 406 +++++
.../s-share-modal/canvas-poster/index.vue | 157 ++
.../canvas-poster/poster/goods.js | 121 ++
.../canvas-poster/poster/groupon.js | 114 ++
.../canvas-poster/poster/index.js | 32 +
.../canvas-poster/poster/user.js | 61 +
.../s-share-modal/canvas-poster/useCanvas.js | 89 ++
.../s-share-modal/s-share-modal.vue | 201 +++
sheep/components/s-statusbar/s-statusbar.vue | 10 +
sheep/components/s-tabbar/s-tabbar.vue | 90 ++
.../s-title-block/s-title-block.vue | 114 ++
.../s-uploader/choose-and-upload-file.js | 213 +++
sheep/components/s-uploader/s-uploader.vue | 674 +++++++++
sheep/components/s-uploader/upload-file.vue | 335 +++++
sheep/components/s-uploader/upload-image.vue | 306 ++++
sheep/components/s-uploader/utils.js | 110 ++
sheep/components/s-user-card/s-user-card.vue | 168 +++
.../s-video-block/s-video-block.vue | 32 +
.../s-wallet-card/s-wallet-card.vue | 95 ++
sheep/config/index.js | 19 +
sheep/config/zIndex.js | 20 +
sheep/helper/digit.js | 168 +++
sheep/helper/index.js | 708 +++++++++
sheep/helper/test.js | 285 ++++
sheep/helper/throttle.js | 31 +
sheep/helper/tools.js | 67 +
sheep/helper/utils.js | 168 +++
sheep/hooks/useApp.js | 0
sheep/hooks/useGoods.js | 212 +++
sheep/hooks/useModal.js | 128 ++
sheep/index.js | 53 +
sheep/libs/mplive-manifest-plugin.js | 32 +
sheep/libs/permission.js | 246 +++
sheep/libs/sdk-h5-weixin.js | 186 +++
sheep/platform/index.js | 175 +++
sheep/platform/pay.js | 255 ++++
sheep/platform/provider/apple/app.js | 35 +
sheep/platform/provider/apple/index.js | 9 +
sheep/platform/provider/wechat/index.js | 15 +
sheep/platform/provider/wechat/miniProgram.js | 251 +++
.../provider/wechat/officialAccount.js | 112 ++
.../platform/provider/wechat/openPlatform.js | 41 +
sheep/platform/share.js | 182 +++
sheep/request/index.js | 221 +++
sheep/request2/index.js | 224 +++
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 | 162 ++
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 | 125 ++
sheep/store/cart.js | 108 ++
sheep/store/index.js | 20 +
sheep/store/modal.js | 29 +
sheep/store/sys.js | 32 +
sheep/store/user.js | 194 +++
sheep/ui/su-coupon/su-coupon.vue | 319 ++++
.../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 | 482 ++++++
sheep/ui/su-notice-bar/su-notice-bar.vue | 472 ++++++
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 | 200 +++
sheep/ui/su-radio/su-radio.vue | 301 ++++
.../ui/su-region-picker/su-region-picker.vue | 246 +++
sheep/ui/su-status-bar/su-status-bar.vue | 16 +
sheep/ui/su-sticky/su-sticky.vue | 264 ++++
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 | 233 +++
sheep/ui/su-tabbar/su-tabbar.vue | 226 +++
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 | 434 ++++++
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/su-video.vue | 199 +++
sheep/url/index.js | 199 +++
sheep/util/const.js | 47 +
sheep/util/index.js | 102 ++
sheep/validate/form.js | 230 +++
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/mp-html/README.md | 191 +++
uni_modules/mp-html/changelog.md | 121 ++
.../mp-html/components/mp-html/mp-html.vue | 498 ++++++
.../mp-html/components/mp-html/node/node.vue | 576 +++++++
.../mp-html/components/mp-html/parser.js | 1333 ++++++++++++++++
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 | 29 +
.../components/uni-badge/uni-badge.vue | 268 ++++
uni_modules/uni-badge/package.json | 88 ++
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 | 41 +
.../uni-data-checkbox/uni-data-checkbox.vue | 817 ++++++++++
uni_modules/uni-data-checkbox/package.json | 87 ++
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 | 529 +++++++
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 +
vite.config.js | 34 +
533 files changed, 82087 insertions(+)
create mode 100644 .gitignore
create mode 100644 .prettierignore
create mode 100644 .prettierrc
create mode 100644 App.vue
create mode 100644 LICENSE
create mode 100644 README.md
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/seckill/list.vue
create mode 100644 pages/app/score-shop.vue
create mode 100644 pages/app/sign.vue
create mode 100644 pages/chat/components/goods.vue
create mode 100644 pages/chat/components/order.vue
create mode 100644 pages/chat/components/select-popup.vue
create mode 100644 pages/chat/emoji.js
create mode 100644 pages/chat/index.vue
create mode 100644 pages/chat/socket.js
create mode 100644 pages/commission/apply.vue
create mode 100644 pages/commission/components/account-info.vue
create mode 100644 pages/commission/components/commission-auth.vue
create mode 100644 pages/commission/components/commission-condition.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/share-log.vue
create mode 100644 pages/commission/team.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-params.vue
create mode 100644 pages/goods/components/detail/detail-cell-service.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/score.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/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/confirm.vue
create mode 100644 pages/order/detail.vue
create mode 100644 pages/order/dispatch/content.vue
create mode 100644 pages/order/express/list.vue
create mode 100644 pages/order/express/log.vue
create mode 100644 pages/order/invoice.vue
create mode 100644 pages/order/list.vue
create mode 100644 pages/pay/components/account-info-modal.vue
create mode 100644 pages/pay/components/account-type-select.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/pay/withdraw-log.vue
create mode 100644 pages/pay/withdraw.vue
create mode 100644 pages/public/error.vue
create mode 100644 pages/public/faq.vue
create mode 100644 pages/public/feedback.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/info.vue
create mode 100644 pages/user/invoice/edit.vue
create mode 100644 pages/user/invoice/list.vue
create mode 100644 pages/user/set.vue
create mode 100644 pages/user/wallet/commission.vue
create mode 100644 pages/user/wallet/money.vue
create mode 100644 pages/user/wallet/score.vue
create mode 100644 sheep/api/activity.js
create mode 100644 sheep/api/app.js
create mode 100644 sheep/api/cart.js
create mode 100644 sheep/api/category.js
create mode 100644 sheep/api/chat.js
create mode 100644 sheep/api/commission.js
create mode 100644 sheep/api/coupon.js
create mode 100644 sheep/api/data.js
create mode 100644 sheep/api/goods.js
create mode 100644 sheep/api/index.js
create mode 100644 sheep/api/index2.js
create mode 100644 sheep/api/order.js
create mode 100644 sheep/api/pay.js
create mode 100644 sheep/api/product/category.js
create mode 100644 sheep/api/product/comment.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/template.js
create mode 100644 sheep/api/promotion/seckill.js
create mode 100644 sheep/api/third.js
create mode 100644 sheep/api/trade.js
create mode 100644 sheep/api/user.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/change-username.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/components/sms-register.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-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-invoice-item/s-invoice-item.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-popup-image/s-popup-image.vue
create mode 100644 sheep/components/s-richtext-block/s-richtext-block.vue
create mode 100644 sheep/components/s-score-block/s-score-block.vue
create mode 100644 sheep/components/s-score-card/s-score-card.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/canvas-poster/useCanvas.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/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/useApp.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/request2/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/su-video.vue
create mode 100644 sheep/url/index.js
create mode 100644 sheep/util/const.js
create mode 100644 sheep/util/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/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 vite.config.js
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..0670590a
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,12 @@
+unpackage/*
+node_modules/*
+.idea/*
+deploy.sh
+.hbuilderx/
+.vscode/
+**/.DS_Store
+.env
+yarn.lock
+package-lock.json
+*.keystore
+pnpm-lock.yaml
diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 00000000..7384df06
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1,6 @@
+/unpackage/*
+/node_modules/**
+/uni_modules/**
+/public/*
+**/*.svg
+**/*.sh
diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 00000000..fe0a3f94
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,10 @@
+{
+ "printWidth": 100,
+ "semi": true,
+ "vueIndentScriptAndStyle": true,
+ "singleQuote": true,
+ "trailingComma": "all",
+ "proseWrap": "never",
+ "htmlWhitespaceSensitivity": "strict",
+ "endOfLine": "auto"
+}
diff --git a/App.vue b/App.vue
new file mode 100644
index 00000000..9ee06f8d
--- /dev/null
+++ b/App.vue
@@ -0,0 +1,46 @@
+
+
+
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 00000000..97996277
--- /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
new file mode 100644
index 00000000..620d9be9
--- /dev/null
+++ b/README.md
@@ -0,0 +1,190 @@
+## 简介
+
+
+
+
+
+[](https://gitee.com/sheepjs/shopro-uniapp.git)
+[](https://gitee.com/sheepjs/shopro-uniapp.git)
+[](https://gitee.com/sheepjs/shopro-uniapp.git)
+[](https://gitee.com/sheepjs/shopro-uniapp.git)
+
+[官方网站](https://www.shopro.top/) | [H5 演示](http://shopro.sheepjs.com/) | [管理系统](https://shopro.sheepjs.com/admin/) | [问题反馈](https://gitee.com/sheepjs/shopro-uniapp/issues)
+
+
+
+## 特性
+
+
+
+- **支持主题色+自定义头部导航+自定义底部导航**
+- **内含沉浸式头部、通用头部导航示例,支持后端自定义配置底部导航背景和样式**
+- **店铺装修组件(轮播、标题栏、优惠券、商品组、宫格导航、列表导航+广告魔方+富文本、搜索栏等众多组件)**
+- **内置微信公众号分享 jssdk+微信小程序分享卡片+微信 App 分享+海报分享统一封装**
+- **内置微信公众号登录+微信小程序手机号登录+微信 App 开放平台登录+账号密码登录+iOS 登录统一封装**
+- **内置余额支付+微信公众号 jssdk 支付+微信小程序支付+微信 App 支付+支付宝网页支付+支付宝 App 支付统一封装**
+- **支持第三方 cdn 图片资源地址,并支持阿里云、腾讯云、七牛云图片缩放参数**
+- **严格适配多终端场景并支持 App 审核上架**
+
+
+## 技术栈
+
+- **前端技术栈:uni-app、ES6、Vue3、Vite、Pinia;**
+## 安装
+
+```bash
+# 1.克隆项目
+$ git clone https://gitee.com/sheepjs/shopro-uniapp.git
+```
+
+```bash
+# 2.拷贝env示例配置文件 重命名为.env
+$ cd shopro-uniapp
+$ cp env .env
+```
+
+```bash
+# 3.安装依赖 (需安装nodejs环境, 使用npm国内镜像)
+$ npm install --registry=https://registry.npmmirror.com
+```
+
+```bash
+# 4.使用HbuilderX 运行...
+```
+
+## 体验
+
+
+
+客户端演示地址:[https://shopro.sheepjs.com](https://shopro.sheepjs.com)
+
+演示账号: shopro
+
+演示密码: a123456
+
+管理端演示地址:[https://shopro.sheepjs.com/admin/](https://shopro.sheepjs.com/admin/)
+
+演示账号: shopro
+
+演示密码: 123456
+
+_(注意:演示环境已屏蔽管理权限和相关操作)_
+
+
+## 项目结构
+
+```
+├── pages // 页面
+│ ├── index // 入口页面
+│ ├── user // 用户相关
+│ ├── public // 公共页面
+│ ├── activity // 活动页面
+│ ├── app // 积分、签到页面
+│ ├── chat // 客服页面
+│ ├── commission // 分销页面
+│ ├── coupon // 优惠券页面
+│ ├── goods // 商品页面
+│ ├── order // 订单页面
+│ ├── pay // 支付页面
+├── sheep // 底层依赖/工具库
+│ ├── api // 服务端接口
+│ ├── components // 自定义功能组件
+│ ├── config // 配置文件
+│ ├── helper // 助手函数
+│ ├── hooks // vue-hooks
+│ ├── libs // 自定义依赖
+│ ├── platform // 第三方平台登录、分享、支付
+│ ├── request // 请求类库
+│ ├── router // 自定义路由跳转
+│ ├── scss // 主样式库
+│ ├── store // pinia状态管理模块
+│ ├── ui // 自定义UI组件
+│ ├── url // cdn图片地址格式化
+│ ├── validate // 通用验证器
+│ ├── index.js // Shopro入口文件
+├── uni_modules // dcloud第三方插件
+
+```
+
+
+## 更新
+
+### 近期计划
+
+- [ ] Typescript 重构;
+
+### V1.8.3 更新简介 2023/10/25
+1. 对接微信小程序发货管理
+2. 修复路由模式为history时,微信公众号使用微信登录时跳转白屏bug
+
+### V1.8.2 更新简介 2023/09/4
+1. 添加 图片热区组件
+2. 添加 商品评论商家回复功能
+3. 优化 购物车性能
+4. 优化 搜索组件
+5. 优化 动态添加直播组件
+6. 优化 轮播图组件
+7. 优化 微信小程序订阅消息提醒时机
+8. 优化 移动小程序端客服bug
+9. 优化 h5支付拉起微信或者支付宝客户端时,支付单查询过早的问题
+10. 优化 标题栏组件
+11. 优化 二级分类组件
+12. 优化 规格弹框,手动输入数量无法改变数量问题
+13. 优化 绑定手机号
+14. 重构 瀑布流商品
+15. 重构 小程序快捷登录
+16. 海报图片协议转换,自动识别https协议
+17. 升级依赖版本
+
+### V1.8.1 更新简介 2023/03/18
+
+1. 优化搜索组件
+
+2. 添加多端直播组件,动态加载直播插件
+
+3. 添加多种配送方式(货到付款、手动发货)
+
+4. 添加发货内容详情展示
+
+5. 优化`radio`点击效果bug
+
+6. 商品轮播图添加视频播放
+
+6. 修复部分页面样式显示问题
+
+
+### V1.8.0 更新简介 2023/02/07
+
+1. 引入`luch-request`,替换`libs`中的`request`
+
+2. 兼容`HbulderX`版本更新小程序端`v-bind`无法使用多层对象的问题
+
+3. 优化分页数据相关页面代码
+
+4. 富文本渲染组件使用`mp-html`替换原`su-parse`
+
+5. 修复阶梯拼团弹框点击规格自动关闭问题
+
+6. 自定义页面头部添加返回按钮及快捷菜单
+
+7. 优化筛选时间可以任意选择时间问题(改为只能筛选当天及以前)
+
+8. 修复部分页面样式显示问题
+
+### V1.7.1 更新简介 2022/12/09
+
+1. 更新插件市场忽略文件问题
+
+2. 更改客服聊天图片样式问题
+
+### V1.5 更新简介 2022/12/07
+
+- [x] 服务保障icon 变形问题;
+- [x] 确认订单 可用优惠券逻辑修改;
+- [x] `su-image`组件中`customStyle`添加`width`属性;
+
+---
+
+**如果您觉得我们的开源项目很有帮助,请点击 :star: Star(https://gitee.com/sheepjs/shopro-uniapp.git) 支持 SheepJS 开源团队:heart:
**
+
+---
diff --git a/androidPrivacy.json b/androidPrivacy.json
new file mode 100644
index 00000000..0d726ca9
--- /dev/null
+++ b/androidPrivacy.json
@@ -0,0 +1,3 @@
+{
+ "prompt" : "template"
+}
diff --git a/index.html b/index.html
new file mode 100644
index 00000000..2269a693
--- /dev/null
+++ b/index.html
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/jsconfig.json b/jsconfig.json
new file mode 100644
index 00000000..b1968eea
--- /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 00000000..2680ac67
--- /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 00000000..9f8a8007
--- /dev/null
+++ b/manifest.json
@@ -0,0 +1,240 @@
+{
+ "name": "星品",
+ "appid": "__UNI__082C0BA",
+ "description": "Shopro是由SheepJS团队开发,使用Uniapp+Vue3技术驱动的在线商城系统,内含诸多功能与丰富的活动,期待您的使用和反馈。",
+ "versionName": "1.8.3",
+ "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": {
+ "ifly": {}
+ },
+ "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": "wx43051b2afa4ed3d0",
+ "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": "hash",
+ "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 00000000..2cacc03d
--- /dev/null
+++ b/package.json
@@ -0,0 +1,105 @@
+{
+ "id": "shopro",
+ "name": "shopro",
+ "displayName": "星品购",
+ "version": "1.0.1",
+ "description": "Shopro-B2C商城,一套代码,同时发行到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",
+ "shopro",
+ "商城模板"
+ ],
+ "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": {
+ "@hyoga/uni-socket.io": "^1.0.1",
+ "dayjs": "^1.11.7",
+ "lodash": "^4.17.21",
+ "luch-request": "^3.0.8",
+ "pinia": "^2.0.33",
+ "pinia-plugin-persist-uni": "^1.2.0",
+ "qs-canvas": "^1.0.11",
+ "weixin-js-sdk": "^1.6.0"
+ },
+ "devDependencies": {
+ "prettier": "^2.8.7",
+ "vconsole": "^3.15.0"
+ }
+}
diff --git a/pages.json b/pages.json
new file mode 100644
index 00000000..56d1201e
--- /dev/null
+++ b/pages.json
@@ -0,0 +1,763 @@
+{
+ "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": "score",
+ "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": "invoice",
+ "style": {
+ "navigationBarTitleText": "发票详情"
+ },
+ "meta": {
+ "auth": true,
+ "title": "发票详情"
+ }
+ },
+ {
+ "path": "dispatch/content",
+ "style": {
+ "navigationBarTitleText": "发货内容"
+ },
+ "meta": {
+ "auth": true,
+ "title": "发货内容"
+ }
+ },
+ {
+ "path": "aftersale/apply",
+ "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": "物流轨迹"
+ }
+ },
+ {
+ "path": "express/list",
+ "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": "invoice/list",
+ "style": {
+ "navigationBarTitleText": "发票管理"
+ },
+ "meta": {
+ "auth": true,
+ "sync": true,
+ "title": "发票管理",
+ "group": "用户中心"
+ }
+ },
+ {
+ "path": "invoice/edit",
+ "style": {
+ "navigationBarTitleText": "编辑发票"
+ },
+ "meta": {
+ "auth": true,
+ "title": "编辑发票"
+ }
+ },
+ {
+ "path": "wallet/money",
+ "style": {
+ "navigationBarTitleText": "我的余额"
+ },
+ "meta": {
+ "auth": true,
+ "sync": true,
+ "title": "用户余额",
+ "group": "用户中心"
+ }
+ },
+ {
+ "path": "wallet/commission",
+ "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": "apply",
+ "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": "share-log",
+ "style": {
+ "navigationBarTitleText": "分享记录"
+ },
+ "meta": {
+ "auth": true,
+ "sync": true,
+ "title": "分享记录",
+ "group": "分销商城"
+ }
+ },
+ {
+ "path": "team",
+ "style": {
+ "navigationBarTitleText": "我的团队"
+ },
+ "meta": {
+ "auth": true,
+ "sync": true,
+ "title": "我的团队",
+ "group": "分销商城"
+ }
+ }
+ ]
+ },
+ {
+ "root": "pages/app",
+ "pages": [
+ {
+ "path": "sign",
+ "style": {
+ "navigationBarTitleText": "签到中心"
+ },
+ "meta": {
+ "auth": true,
+ "sync": true,
+ "title": "签到中心",
+ "group": "应用"
+ }
+ },
+ {
+ "path": "score-shop",
+ "style": {
+ "navigationBarTitleText": "积分商城"
+ },
+ "meta": {
+ "auth": false,
+ "sync": true,
+ "title": "积分商城",
+ "group": "应用"
+ }
+ }
+ ]
+ },
+ {
+ "root": "pages/public",
+ "pages": [
+ {
+ "path": "setting",
+ "style": {
+ "navigationBarTitleText": "系统设置"
+ },
+ "meta": {
+ "sync": true,
+ "title": "系统设置",
+ "group": "通用"
+ }
+ },
+ {
+ "path": "feedback",
+ "style": {
+ "navigationBarTitleText": "问题反馈"
+ },
+ "meta": {
+ "auth": true,
+ "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": "客服"
+ },
+ "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": "支付"
+ }
+ },
+ {
+ "path": "withdraw",
+ "style": {
+ "navigationBarTitleText": "申请提现"
+ },
+ "meta": {
+ "auth": true,
+ "sync": true,
+ "title": "申请提现",
+ "group": "支付"
+ }
+ },
+ {
+ "path": "withdraw-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": "营销活动"
+ }
+ }
+ ]
+ }
+ ],
+ "globalStyle": {
+ "navigationBarTextStyle": "black",
+ "navigationBarTitleText": "星品购",
+ "navigationBarBackgroundColor": "#FFFFFF",
+ "backgroundColor": "#FFFFFF",
+ "navigationStyle": "custom"
+ },
+ "tabBar": {
+ "list": [
+ {
+ "pagePath": "pages/index/index"
+ },
+ {
+ "pagePath": "pages/index/cart"
+ },
+ {
+ "pagePath": "pages/index/user"
+ }
+ ]
+ }
+}
diff --git a/pages/activity/groupon/detail.vue b/pages/activity/groupon/detail.vue
new file mode 100644
index 00000000..c04e8d92
--- /dev/null
+++ b/pages/activity/groupon/detail.vue
@@ -0,0 +1,507 @@
+
+
+
+
+
+
+
+
+ {{ state.data.num }}人团
+ 已拼{{ state.data.goods.sales }}件
+
+
+
+
+
+
+
+
+
+ 恭喜您~拼团成功
+
+
+
+
+
+ 抱歉~该团已满员
+
+
+
+
+
+
+ {{ state.data.my ? '拼团超时,已自动退款' : '该团已解散' }}
+
+
+
+
+
+
+
+ 拼团已结束,请关注下次活动
+
+
+
+
+ 还差
+ {{ state.data.num - state.data.current_num }}人
+ 拼团成功
+
+ {{ endTime.h }}
+ :
+
+ {{ endTime.m }}
+
+ :
+
+ {{ endTime.s }}
+
+
+
+
+
+
+
+
+
+
+
+
+ 玩法
+
+
+ {{ state.data.activity?.richtext_title }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/activity/groupon/list.vue b/pages/activity/groupon/list.vue
new file mode 100644
index 00000000..037412a9
--- /dev/null
+++ b/pages/activity/groupon/list.vue
@@ -0,0 +1,254 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/activity/groupon/order.vue b/pages/activity/groupon/order.vue
new file mode 100644
index 00000000..74b34fcb
--- /dev/null
+++ b/pages/activity/groupon/order.vue
@@ -0,0 +1,298 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ order.num }}人团
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/activity/index.vue b/pages/activity/index.vue
new file mode 100644
index 00000000..0a33581c
--- /dev/null
+++ b/pages/activity/index.vue
@@ -0,0 +1,191 @@
+
+
+
+
+ {{ state.activityInfo.type_text }}:
+
+
+ {{ item }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/activity/seckill/list.vue b/pages/activity/seckill/list.vue
new file mode 100644
index 00000000..b7f3980f
--- /dev/null
+++ b/pages/activity/seckill/list.vue
@@ -0,0 +1,249 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/app/score-shop.vue b/pages/app/score-shop.vue
new file mode 100644
index 00000000..e00605fe
--- /dev/null
+++ b/pages/app/score-shop.vue
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/app/sign.vue b/pages/app/sign.vue
new file mode 100644
index 00000000..8d0947cb
--- /dev/null
+++ b/pages/app/sign.vue
@@ -0,0 +1,512 @@
+
+
+
+
+
+
+
+
+ 签到日历
+
+ 已连续签到
+ {{ state.continue_days }}
+ 天
+
+
+
+
+
+
+ {{ state.cur_year || '--' }} 年 {{ state.cur_month || '--' }} 月
+
+
+
+
+
+
+ {{ item.title }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.day < 10 ? '0' + item.day : item.day }}
+
+
+
+
+
+ {{ item.day < 10 ? '0' + item.day : item.day }}
+
+
+
+ {{ item.day < 10 ? '0' + item.day : item.day }}
+
+
+
+
+
+
+
+
+
+
+
+
+ 签到说明
+
+ 1、每日签到固定 {{ state.data.rules.everyday }} 积分
+
+ ,次日递增奖励 {{ state.data.rules.inc_num }} 积分,直到
+ {{ state.data.rules.until_day }} 天之后不再增加
+
+
+
+ 2、
+ 连续签到 {{ i.full }} 天,奖励 {{ i.value }} 积分;
+
+
+
+ {{ state.data.rules.discounts?.length > 0 ? '3' : '2' }}、用户在
+ {{ state.data.rules.replenish_limit }} 天内,可补签
+ {{ state.data.rules.replenish_days }} 天,每次补签消耗
+ {{ state.data.rules.replenish_num }}积分
+
+
+
+
+
+
+
+
+
+ {{ state.signin.score }}积分
+
+ 已连续打卡{{ state.continue_days }}天
+
+
+
+ 签到成功
+
+
+
+
+
+
+
+
+
+
+ 消耗{{ state.data?.rules.replenish_num }}积分
+
+ 已连续打卡{{ state.continue_days }}天
+
+
+
+ 确认补签
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/chat/components/goods.vue b/pages/chat/components/goods.vue
new file mode 100644
index 00000000..7fbc95c7
--- /dev/null
+++ b/pages/chat/components/goods.vue
@@ -0,0 +1,63 @@
+
+
+
+
+
+ {{ goodsData.title }}
+
+
+ {{ goodsData.subtitle }}
+
+
+ ¥{{ isArray(goodsData.price) ? goodsData.price[0] : goodsData.price }}
+
+
+
+
+
+
+
+
diff --git a/pages/chat/components/order.vue b/pages/chat/components/order.vue
new file mode 100644
index 00000000..cf57f63b
--- /dev/null
+++ b/pages/chat/components/order.vue
@@ -0,0 +1,122 @@
+
+
+
+ {{ orderData.order_sn }}
+ {{ orderData.create_time.split(' ')[1] }}
+
+
+
+
+
+
+ {{ item.goods_title }}
+
+ 数量:{{ item.goods_num }}
+
+ ¥{{ item.goods_price }}
+ {{ orderData.status_text }}
+
+
+
+
+
+
+
+
+
+ {{ item.goods_title }}
+
+
+ 共{{ orderData.items.length }}件商品
+ 合计 ¥{{ orderData.pay_fee }}
+
+
+ {{ orderData.status_text }}
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/chat/components/select-popup.vue b/pages/chat/components/select-popup.vue
new file mode 100644
index 00000000..ba0f96aa
--- /dev/null
+++ b/pages/chat/components/select-popup.vue
@@ -0,0 +1,151 @@
+
+
+
+
+
+
+
+
+
diff --git a/pages/chat/emoji.js b/pages/chat/emoji.js
new file mode 100644
index 00000000..83e6e842
--- /dev/null
+++ b/pages/chat/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/index.vue b/pages/chat/index.vue
new file mode 100644
index 00000000..1d3dfac6
--- /dev/null
+++ b/pages/chat/index.vue
@@ -0,0 +1,870 @@
+
+
+
+ {{ socketState.isConnect ? customerServiceInfo.title : '网络已断开,请检查网络后刷新重试' }}
+
+
+
+
+
+
+
+
+
+ {{ formatTime(item.date) }}
+
+
+
+ {{ item.content.text }}
+
+
+
+
+ 猜你想问
+
+ * {{ item.title }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 图片
+
+
+
+ 商品
+
+
+
+ 订单
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/chat/socket.js b/pages/chat/socket.js
new file mode 100644
index 00000000..840931cf
--- /dev/null
+++ b/pages/chat/socket.js
@@ -0,0 +1,821 @@
+import { reactive, ref, unref } from 'vue';
+import sheep from '@/sheep';
+import chat from '@/sheep/api/chat';
+import dayjs from 'dayjs';
+import io from '@hyoga/uni-socket.io';
+
+export function useChatWebSocket(socketConfig) {
+ let SocketIo = null;
+
+ // chat状态数据
+ const state = reactive({
+ chatDotNum: 0, //总状态红点
+ chatList: [], //会话信息
+ customerUserInfo: {}, //用户信息
+ customerServerInfo: {
+ //客服信息
+ title: '连接中...',
+ state: 'connecting',
+ avatar: null,
+ nickname: '',
+ },
+ socketState: {
+ isConnect: true, //是否连接成功
+ isConnecting: false, //重连中,不允许新的socket开启。
+ tip: '',
+ },
+ chatHistoryPagination: {
+ page: 0, //当前页
+ list_rows: 10, //每页条数
+ last_id: 0, //最后条ID
+ lastPage: 0, //总共多少页
+ loadStatus: 'loadmore', //loadmore-加载前的状态,loading-加载中的状态,nomore-没有更多的状态
+ },
+ templateChatList: [], //猜你想问
+
+ chatConfig: {}, // 配置信息
+
+ isSendSucces: -1, // 是否发送成功 -1=发送中|0=发送成功|1发送失败
+ });
+
+ /**
+ * 连接初始化
+ * @param {Object} config - 配置信息
+ * @param {Function} callBack -回调函数,有新消息接入,保持底部
+ */
+ const socketInit = (config, callBack) => {
+ state.chatConfig = config;
+ if (SocketIo && SocketIo.connected) return; // 如果socket已经连接,返回false
+ if (state.socketState.isConnecting) return; // 重连中,返回false
+
+ // 启动初始化
+ SocketIo = io(config.chat_domain, {
+ reconnection: true, // 默认 true 是否断线重连
+ reconnectionAttempts: 5, // 默认无限次 断线尝试次数
+ reconnectionDelay: 1000, // 默认 1000,进行下一次重连的间隔。
+ reconnectionDelayMax: 5000, // 默认 5000, 重新连接等待的最长时间 默认 5000
+ randomizationFactor: 0.5, // 默认 0.5 [0-1],随机重连延迟时间
+ timeout: 20000, // 默认 20s
+ transports: ['websocket', 'polling'], // websocket | polling,
+ ...config,
+ });
+
+ // 监听连接
+ SocketIo.on('connect', async (res) => {
+ socketReset(callBack);
+ // socket连接
+ // 用户登录
+ // 顾客登录
+ console.log('socket:connect');
+ });
+ // 监听消息
+ SocketIo.on('message', (res) => {
+ if (res.error === 0) {
+ const { message, sender } = res.data;
+ state.chatList.push(formatMessage(res.data.message));
+
+ // 告诉父级页面
+ // window.parent.postMessage({
+ // chatDotNum: ++state.chatDotNum
+ // })
+ callBack && callBack();
+ }
+ });
+ // 监听客服接入成功
+ SocketIo.on('customer_service_access', (res) => {
+ if (res.error === 0) {
+ editCustomerServerInfo({
+ title: res.data.customer_service.name,
+ state: 'online',
+ avatar: res.data.customer_service.avatar,
+ });
+ state.chatList.push(formatMessage(res.data.message));
+ // callBack && callBack()
+ }
+ });
+ // 监听排队等待
+ SocketIo.on('waiting_queue', (res) => {
+ if (res.error === 0) {
+ editCustomerServerInfo({
+ title: res.data.title,
+ state: 'waiting',
+ avatar: '',
+ });
+ // callBack && callBack()
+ }
+ });
+ // 监听没有客服在线
+ SocketIo.on('no_customer_service', (res) => {
+ if (res.error === 0) {
+ editCustomerServerInfo({
+ title: '暂无客服在线...',
+ state: 'waiting',
+ avatar: '',
+ });
+ }
+ state.chatList.push(formatMessage(res.data.message));
+ // callBack && callBack()
+ });
+ // 监听客服上线
+ SocketIo.on('customer_service_online', (res) => {
+ if (res.error === 0) {
+ editCustomerServerInfo({
+ title: res.data.customer_service.name,
+ state: 'online',
+ avatar: res.data.customer_service.avatar,
+ });
+ }
+ });
+ // 监听客服下线
+ SocketIo.on('customer_service_offline', (res) => {
+ if (res.error === 0) {
+ editCustomerServerInfo({
+ title: res.data.customer_service.name,
+ state: 'offline',
+ avatar: res.data.customer_service.avatar,
+ });
+ }
+ });
+ // 监听客服忙碌
+ SocketIo.on('customer_service_busy', (res) => {
+ if (res.error === 0) {
+ editCustomerServerInfo({
+ title: res.data.customer_service.name,
+ state: 'busy',
+ avatar: res.data.customer_service.avatar,
+ });
+ }
+ });
+ // 监听客服断开链接
+ SocketIo.on('customer_service_break', (res) => {
+ if (res.error === 0) {
+ editCustomerServerInfo({
+ title: '客服服务结束',
+ state: 'offline',
+ avatar: '',
+ });
+ state.socketState.isConnect = false;
+ state.socketState.tip = '当前服务已结束';
+ }
+ state.chatList.push(formatMessage(res.data.message));
+ // callBack && callBack()
+ });
+ // 监听自定义错误 custom_error
+ SocketIo.on('custom_error', (error) => {
+ editCustomerServerInfo({
+ title: error.msg,
+ state: 'offline',
+ avatar: '',
+ });
+ console.log('custom_error:', error);
+ });
+ // 监听错误 error
+ SocketIo.on('error', (error) => {
+ console.log('error:', error);
+ });
+ // 重连失败 connect_error
+ SocketIo.on('connect_error', (error) => {
+ console.log('connect_error');
+ });
+ // 连接上,但无反应 connect_timeout
+ SocketIo.on('connect_timeout', (error) => {
+ console.log(error, 'connect_timeout');
+ });
+ // 服务进程销毁 disconnect
+ SocketIo.on('disconnect', (error) => {
+ console.log(error, 'disconnect');
+ });
+ // 服务重启重连上reconnect
+ SocketIo.on('reconnect', (error) => {
+ console.log(error, 'reconnect');
+ });
+ // 开始重连reconnect_attempt
+ SocketIo.on('reconnect_attempt', (error) => {
+ state.socketState.isConnect = false;
+ state.socketState.isConnecting = true;
+ editCustomerServerInfo({
+ title: `重连中,第${error}次尝试...`,
+ state: 'waiting',
+ avatar: '',
+ });
+ console.log(error, 'reconnect_attempt');
+ });
+ // 重新连接中reconnecting
+ SocketIo.on('reconnecting', (error) => {
+ console.log(error, 'reconnecting');
+ });
+ // 重新连接错误reconnect_error
+ SocketIo.on('reconnect_error', (error) => {
+ console.log('reconnect_error');
+ });
+ // 重新连接失败reconnect_failed
+ SocketIo.on('reconnect_failed', (error) => {
+ state.socketState.isConnecting = false;
+ editCustomerServerInfo({
+ title: `重连失败,请刷新重试~`,
+ state: 'waiting',
+ avatar: '',
+ });
+ console.log(error, 'reconnect_failed');
+
+ // setTimeout(() => {
+ state.isSendSucces = 1;
+ // }, 500)
+ });
+ };
+
+ // 重置socket
+ const socketReset = (callBack) => {
+ state.chatList = [];
+ state.chatHistoryList = [];
+ state.chatHistoryPagination = {
+ page: 0,
+ per_page: 10,
+ last_id: 0,
+ totalPage: 0,
+ };
+ socketConnection(callBack); // 连接
+ };
+
+ // 退出连接
+ const socketClose = () => {
+ SocketIo.emit('customer_logout', {}, (res) => {
+ console.log('socket:退出', res);
+ });
+ };
+
+ // 测试事件
+ const socketTest = () => {
+ SocketIo.emit('test', {}, (res) => {
+ console.log('test:test', res);
+ });
+ };
+
+ // 发送消息
+ const socketSendMsg = (data, sendMsgCallBack) => {
+ state.isSendSucces = -1;
+ state.chatList.push(data);
+ sendMsgCallBack && sendMsgCallBack();
+ SocketIo.emit(
+ 'message',
+ {
+ message: formatInput(data),
+ ...data.customData,
+ },
+ (res) => {
+ // setTimeout(() => {
+ state.isSendSucces = res.error;
+ // }, 500)
+
+ // console.log(res, 'socket:send');
+ // sendMsgCallBack && sendMsgCallBack()
+ },
+ );
+ };
+
+ // 连接socket,存入sessionId
+ const socketConnection = (callBack) => {
+ SocketIo.emit(
+ 'connection',
+ {
+ auth: 'user',
+ token: uni.getStorageSync('socketUserToken') || '',
+ session_id: uni.getStorageSync('socketSessionId') || '',
+ },
+ (res) => {
+ if (res.error === 0) {
+ socketCustomerLogin(callBack);
+ uni.setStorageSync('socketSessionId', res.data.session_id);
+ // uni.getStorageSync('socketUserToken') && socketLogin(uni.getStorageSync(
+ // 'socketUserToken')) // 如果有用户token,绑定
+ state.customerUserInfo = res.data.chat_user;
+ state.socketState.isConnect = true;
+ } else {
+ editCustomerServerInfo({
+ title: `服务器异常!`,
+ state: 'waiting',
+ avatar: '',
+ });
+ state.socketState.isConnect = false;
+ }
+ },
+ );
+ };
+
+ // 用户id,获取token
+ const getUserToken = async (id) => {
+ const res = await chat.unifiedToken();
+ if (res.error === 0) {
+ uni.setStorageSync('socketUserToken', res.data.token);
+ // SocketIo && SocketIo.connected && socketLogin(res.data.token)
+ }
+ return res;
+ };
+
+ // 用户登录
+ const socketLogin = (token) => {
+ SocketIo.emit(
+ 'login',
+ {
+ token: token,
+ },
+ (res) => {
+ console.log(res, 'socket:login');
+ state.customerUserInfo = res.data.chat_user;
+ },
+ );
+ };
+
+ // 顾客登录
+ const socketCustomerLogin = (callBack) => {
+ SocketIo.emit(
+ 'customer_login',
+ {
+ room_id: state.chatConfig.room_id,
+ },
+ (res) => {
+ state.templateChatList = res.data.questions.length ? res.data.questions : [];
+ state.chatList.push({
+ from: 'customer_service', // 用户customer右 | 顾客customer_service左 | 系统system中间
+ mode: 'template', // goods,order,image,text,system
+ date: new Date().getTime(), //时间
+ content: {
+ //内容
+ list: state.templateChatList,
+ },
+ });
+ res.error === 0 && socketHistoryList(callBack);
+ },
+ );
+ };
+
+ // 获取历史消息
+ const socketHistoryList = (historyCallBack) => {
+ state.chatHistoryPagination.loadStatus = 'loading';
+ state.chatHistoryPagination.page += 1;
+ SocketIo.emit('messages', state.chatHistoryPagination, (res) => {
+ if (res.error === 0) {
+ state.chatHistoryPagination.total = res.data.messages.total;
+ state.chatHistoryPagination.lastPage = res.data.messages.last_page;
+ state.chatHistoryPagination.page = res.data.messages.current_page;
+ res.data.messages.data.forEach((item) => {
+ item.message_type && state.chatList.unshift(formatMessage(item));
+ });
+ state.chatHistoryPagination.loadStatus =
+ state.chatHistoryPagination.page < state.chatHistoryPagination.lastPage
+ ? 'loadmore'
+ : 'nomore';
+ if (state.chatHistoryPagination.last_id == 0) {
+ state.chatHistoryPagination.last_id = res.data.messages.data.length
+ ? res.data.messages.data[0].id
+ : 0;
+ }
+ state.chatHistoryPagination.page === 1 && historyCallBack && historyCallBack();
+ }
+
+ // 历史记录之后,猜你想问
+ // state.chatList.push({
+ // from: 'customer_service', // 用户customer右 | 顾客customer_service左 | 系统system中间
+ // mode: 'template', // goods,order,image,text,system
+ // date: new Date().getTime(), //时间
+ // content: { //内容
+ // list: state.templateChatList
+ // }
+ // })
+ });
+ };
+
+ // 修改客服信息
+ const editCustomerServerInfo = (data) => {
+ state.customerServerInfo = {
+ ...state.customerServerInfo,
+ ...data,
+ };
+ };
+
+ /**
+ * ================
+ * 工具函数 ↓
+ * ===============
+ */
+
+ /**
+ * 是否显示时间
+ * @param {*} item - 数据
+ * @param {*} index - 索引
+ */
+ const showTime = (item, index) => {
+ if (unref(state.chatList)[index + 1]) {
+ let dateString = dayjs(unref(state.chatList)[index + 1].date).fromNow();
+ if (dateString === dayjs(unref(item).date).fromNow()) {
+ return false;
+ } else {
+ dateString = dayjs(unref(item).date).fromNow();
+ return true;
+ }
+ }
+ return false;
+ };
+
+ /**
+ * 格式化时间
+ * @param {*} time - 时间戳
+ */
+ const formatTime = (time) => {
+ let diffTime = new Date().getTime() - time;
+ if (diffTime > 28 * 24 * 60 * 1000) {
+ return dayjs(time).format('MM/DD HH:mm');
+ }
+ if (diffTime > 360 * 28 * 24 * 60 * 1000) {
+ return dayjs(time).format('YYYY/MM/DD HH:mm');
+ }
+ return dayjs(time).fromNow();
+ };
+
+ /**
+ * 获取焦点
+ * @param {*} virtualNode - 节点信息 ref
+ */
+ const getFocus = (virtualNode) => {
+ if (window.getSelection) {
+ let chatInput = unref(virtualNode);
+ chatInput.focus();
+ let range = window.getSelection();
+ range.selectAllChildren(chatInput);
+ range.collapseToEnd();
+ } else if (document.selection) {
+ let range = document.selection.createRange();
+ range.moveToElementText(chatInput);
+ range.collapse(false);
+ range.select();
+ }
+ };
+
+ /**
+ * 文件上传
+ * @param {Blob} file -文件数据流
+ * @return {path,fullPath}
+ */
+
+ const upload = (name, file) => {
+ return new Promise((resolve, reject) => {
+ let data = new FormData();
+ data.append('file', file, name);
+ data.append('group', 'chat');
+ ajax({
+ url: '/upload',
+ method: 'post',
+ headers: {
+ 'Content-Type': 'multipart/form-data',
+ },
+ data,
+ success: function (res) {
+ resolve(res);
+ },
+ error: function (err) {
+ reject(err);
+ },
+ });
+ });
+ };
+
+ /**
+ * 粘贴到输入框
+ * @param {*} e - 粘贴内容
+ * @param {*} uploadHttp - 上传图片地址
+ */
+ const onPaste = async (e) => {
+ let paste = e.clipboardData || window.clipboardData;
+ let filesArr = Array.from(paste.files);
+ filesArr.forEach(async (child) => {
+ if (child && child.type.includes('image')) {
+ e.preventDefault(); //阻止默认
+ let file = child;
+ const img = await readImg(file);
+ const blob = await compressImg(img, file.type);
+ const { data } = await upload(file.name, blob);
+ let image = `
`;
+ document.execCommand('insertHTML', false, image);
+ } else {
+ document.execCommand('insertHTML', false, paste.getData('text'));
+ }
+ });
+ };
+
+ /**
+ * 拖拽到输入框
+ * @param {*} e - 粘贴内容
+ * @param {*} uploadHttp - 上传图片地址
+ */
+ const onDrop = async (e) => {
+ e.preventDefault(); //阻止默认
+ let filesArr = Array.from(e.dataTransfer.files);
+ filesArr.forEach(async (child) => {
+ if (child && child.type.includes('image')) {
+ let file = child;
+ const img = await readImg(file);
+ const blob = await compressImg(img, file.type);
+ const { data } = await upload(file.name, blob);
+ let image = `
`;
+ document.execCommand('insertHTML', false, image);
+ } else {
+ ElMessage({
+ message: '禁止拖拽非图片资源',
+ type: 'warning',
+ });
+ }
+ });
+ };
+
+ /**
+ * 解析富文本输入框内容
+ * @param {*} virtualNode -节点信息
+ * @param {Function} formatInputCallBack - cb 回调
+ */
+ const formatChatInput = (virtualNode, formatInputCallBack) => {
+ let res = '';
+ let elemArr = Array.from(virtualNode.childNodes);
+ elemArr.forEach((child, index) => {
+ if (child.nodeName === '#text') {
+ //如果为文本节点
+ res += child.nodeValue;
+ if (
+ //文本节点的后面是图片,并且不是emoji,分开发送。输入框中的图片和文本表情分开。
+ elemArr[index + 1] &&
+ elemArr[index + 1].nodeName === 'IMG' &&
+ elemArr[index + 1] &&
+ elemArr[index + 1].name !== 'emoji'
+ ) {
+ const data = {
+ from: 'customer',
+ mode: 'text',
+ date: new Date().getTime(),
+ content: {
+ text: filterXSS(res),
+ },
+ };
+ formatInputCallBack && formatInputCallBack(data);
+ res = '';
+ }
+ } else if (child.nodeName === 'BR') {
+ res += '
';
+ } else if (child.nodeName === 'IMG') {
+ // 有emjio 和 一般图片
+ // 图片解析后直接发送,不跟文字表情一组
+ if (child.name !== 'emoji') {
+ let srcReg = /src=[\'\']?([^\'\']*)[\'\']?/i;
+ let src = child.outerHTML.match(srcReg);
+ const data = {
+ from: 'customer',
+ mode: 'image',
+ date: new Date().getTime(),
+ content: {
+ url: src[1],
+ path: src[1].replace(/http:\/\/[^\/]*/, ''),
+ },
+ };
+ formatInputCallBack && formatInputCallBack(data);
+ } else {
+ // 非表情图片跟文字一起发送
+ res += child.outerHTML;
+ }
+ } else if (child.nodeName === 'DIV') {
+ res += `${child.outerHTML}
`;
+ }
+ });
+ if (res) {
+ const data = {
+ from: 'customer',
+ mode: 'text',
+ date: new Date().getTime(),
+ content: {
+ text: filterXSS(res),
+ },
+ };
+ formatInputCallBack && formatInputCallBack(data);
+ }
+ unref(virtualNode).innerHTML = '';
+ };
+
+ /**
+ * 状态回调
+ * @param {*} res -接口返回数据
+ */
+ const callBackNotice = (res) => {
+ ElNotification({
+ title: 'socket',
+ message: res.msg,
+ showClose: true,
+ type: res.error === 0 ? 'success' : 'warning',
+ duration: 1200,
+ });
+ };
+
+ /**
+ * 格式化发送信息
+ * @param {Object} message
+ * @returns obj - 消息对象
+ */
+ const formatInput = (message) => {
+ let obj = {};
+ switch (message.mode) {
+ case 'text':
+ obj = {
+ message_type: 'text',
+ message: message.content.text,
+ };
+ break;
+ case 'image':
+ obj = {
+ message_type: 'image',
+ message: message.content.path,
+ };
+ break;
+ case 'goods':
+ obj = {
+ message_type: 'goods',
+ message: message.content.item,
+ };
+ break;
+ case 'order':
+ obj = {
+ message_type: 'order',
+ message: message.content.item,
+ };
+ break;
+ default:
+ break;
+ }
+ return obj;
+ };
+ /**
+ * 格式化接收信息
+ * @param {*} message
+ * @returns obj - 消息对象
+ */
+ const formatMessage = (message) => {
+ let obj = {};
+ switch (message.message_type) {
+ case 'system':
+ obj = {
+ from: 'system', // 用户customer左 | 顾客customer_service右 | 系统system中间
+ mode: 'system', // goods,order,image,text,system
+ date: message.create_time * 1000, //时间
+ content: {
+ //内容
+ text: message.message,
+ },
+ };
+ break;
+ case 'text':
+ obj = {
+ from: message.sender_identify,
+ mode: message.message_type,
+ date: message.create_time * 1000, //时间
+ sender: message.sender,
+ content: {
+ text: message.message,
+ messageId: message.id,
+ },
+ };
+ break;
+ case 'image':
+ obj = {
+ from: message.sender_identify,
+ mode: message.message_type,
+ date: message.create_time * 1000, //时间
+ sender: message.sender,
+ content: {
+ url: sheep.$url.cdn(message.message),
+ messageId: message.id,
+ },
+ };
+ break;
+ case 'goods':
+ obj = {
+ from: message.sender_identify,
+ mode: message.message_type,
+ date: message.create_time * 1000, //时间
+ sender: message.sender,
+ content: {
+ item: message.message,
+ messageId: message.id,
+ },
+ };
+ break;
+ case 'order':
+ obj = {
+ from: message.sender_identify,
+ mode: message.message_type,
+ date: message.create_time * 1000, //时间
+ sender: message.sender,
+ content: {
+ item: message.message,
+ messageId: message.id,
+ },
+ };
+ break;
+ default:
+ break;
+ }
+ return obj;
+ };
+
+ /**
+ * file 转换为 img
+ * @param {*} file - file 文件
+ * @returns img - img标签
+ */
+ const readImg = (file) => {
+ return new Promise((resolve, reject) => {
+ const img = new Image();
+ const reader = new FileReader();
+ reader.onload = function (e) {
+ img.src = e.target.result;
+ };
+ reader.onerror = function (e) {
+ reject(e);
+ };
+ reader.readAsDataURL(file);
+ img.onload = function () {
+ resolve(img);
+ };
+ img.onerror = function (e) {
+ reject(e);
+ };
+ });
+ };
+
+ /**
+ * 压缩图片
+ *@param img -被压缩的img对象
+ * @param type -压缩后转换的文件类型
+ * @param mx -触发压缩的图片最大宽度限制
+ * @param mh -触发压缩的图片最大高度限制
+ * @returns blob - 文件流
+ */
+ const compressImg = (img, type = 'image/jpeg', mx = 1000, mh = 1000, quality = 1) => {
+ return new Promise((resolve, reject) => {
+ const canvas = document.createElement('canvas');
+ const context = canvas.getContext('2d');
+ const { width: originWidth, height: originHeight } = img;
+ // 最大尺寸限制
+ const maxWidth = mx;
+ const maxHeight = mh;
+ // 目标尺寸
+ let targetWidth = originWidth;
+ let targetHeight = originHeight;
+ if (originWidth > maxWidth || originHeight > maxHeight) {
+ if (originWidth / originHeight > 1) {
+ // 宽图片
+ targetWidth = maxWidth;
+ targetHeight = Math.round(maxWidth * (originHeight / originWidth));
+ } else {
+ // 高图片
+ targetHeight = maxHeight;
+ targetWidth = Math.round(maxHeight * (originWidth / originHeight));
+ }
+ }
+ canvas.width = targetWidth;
+ canvas.height = targetHeight;
+ context.clearRect(0, 0, targetWidth, targetHeight);
+ // 图片绘制
+ context.drawImage(img, 0, 0, targetWidth, targetHeight);
+ canvas.toBlob(
+ function (blob) {
+ resolve(blob);
+ },
+ type,
+ quality,
+ );
+ });
+ };
+
+ return {
+ compressImg,
+ readImg,
+ formatMessage,
+ formatInput,
+ callBackNotice,
+
+ socketInit,
+ socketSendMsg,
+ socketClose,
+ socketHistoryList,
+
+ getFocus,
+ formatChatInput,
+ onDrop,
+ onPaste,
+ upload,
+
+ getUserToken,
+
+ state,
+
+ socketTest,
+
+ showTime,
+ formatTime,
+ };
+}
\ No newline at end of file
diff --git a/pages/commission/apply.vue b/pages/commission/apply.vue
new file mode 100644
index 00000000..7f0662b8
--- /dev/null
+++ b/pages/commission/apply.vue
@@ -0,0 +1,290 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/commission/components/account-info.vue b/pages/commission/components/account-info.vue
new file mode 100644
index 00000000..d0463e61
--- /dev/null
+++ b/pages/commission/components/account-info.vue
@@ -0,0 +1,108 @@
+
+
+
+
+
+
+
+
+ 总收益(元)
+
+ {{ state.showMoney ? agentInfo.total_income || '0.00' : '***' }}
+
+
+
+ 我的佣金(元)
+
+ {{ state.showMoney ? userInfo.commission || '0.00' : '***' }}
+
+
+
+ 我的消费(元)
+
+ {{ state.showMoney ? userInfo.total_consume || '0.00' : '***' }}
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/commission/components/commission-auth.vue b/pages/commission/components/commission-auth.vue
new file mode 100644
index 00000000..964f6618
--- /dev/null
+++ b/pages/commission/components/commission-auth.vue
@@ -0,0 +1,184 @@
+
+
+
+
+
+
+
+ {{ state.event.title }}
+ {{ state.event.subtitle }}
+
+
+
+
+
+
+
+
+
diff --git a/pages/commission/components/commission-condition.vue b/pages/commission/components/commission-condition.vue
new file mode 100644
index 00000000..f8ef2db2
--- /dev/null
+++ b/pages/commission/components/commission-condition.vue
@@ -0,0 +1,173 @@
+
+
+
+
+
+
+
+
+
+ {{ item.subtitle }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ errorData.value }}元
+
+ {{ userInfo.total_consume }}元
+
+
+
+ * 购买指定商品即可成为分销商
+ * 满足累计消费即可成为分销商
+
+
+
+
+
+
+
diff --git a/pages/commission/components/commission-info.vue b/pages/commission/components/commission-info.vue
new file mode 100644
index 00000000..fea225fd
--- /dev/null
+++ b/pages/commission/components/commission-info.vue
@@ -0,0 +1,126 @@
+
+
+
+
+
+
+
+
+
+
+ {{ userInfo.nickname }}
+
+
+
+
+ {{ agentInfo.level_info?.name }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/commission/components/commission-log.vue b/pages/commission/components/commission-log.vue
new file mode 100644
index 00000000..15b83853
--- /dev/null
+++ b/pages/commission/components/commission-log.vue
@@ -0,0 +1,184 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.remark }}
+
+
+ {{ dayjs(item.create_time).fromNow() }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/commission/components/commission-menu.vue b/pages/commission/components/commission-menu.vue
new file mode 100644
index 00000000..5a16c451
--- /dev/null
+++ b/pages/commission/components/commission-menu.vue
@@ -0,0 +1,153 @@
+
+
+
+
+
+
+
+
diff --git a/pages/commission/goods.vue b/pages/commission/goods.vue
new file mode 100644
index 00000000..7b186f02
--- /dev/null
+++ b/pages/commission/goods.vue
@@ -0,0 +1,137 @@
+
+
+
+
+
+
+
+ 预计佣金:¥{{ item.commission }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/commission/index.vue b/pages/commission/index.vue
new file mode 100644
index 00000000..809e74f4
--- /dev/null
+++ b/pages/commission/index.vue
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/commission/order.vue b/pages/commission/order.vue
new file mode 100644
index 00000000..4e92e778
--- /dev/null
+++ b/pages/commission/order.vue
@@ -0,0 +1,417 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 佣金:
+ {{ item.rewards[0]?.commission }}
+
+
+ {{ item.commission_order_status_text }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/commission/share-log.vue b/pages/commission/share-log.vue
new file mode 100644
index 00000000..db883c98
--- /dev/null
+++ b/pages/commission/share-log.vue
@@ -0,0 +1,173 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.user?.nickname }}
+
+
+
+
+
+
+
+ {{ item.ext?.memo }}
+
+
+
+
+ {{ dayjs(item.create_time).fromNow() }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/commission/team.vue b/pages/commission/team.vue
new file mode 100644
index 00000000..8ee4d95b
--- /dev/null
+++ b/pages/commission/team.vue
@@ -0,0 +1,257 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.agent.level_info.name }}
+
+
+ {{ item.create_time }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/coupon/detail.vue b/pages/coupon/detail.vue
new file mode 100644
index 00000000..4052228d
--- /dev/null
+++ b/pages/coupon/detail.vue
@@ -0,0 +1,372 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{ state.list.name }}
+ {{ state.list.amount_text }}
+
+
+ 领取时间:{{ state.list.get_start_time }}至{{ state.list.get_end_time }}
+
+
+ 有效期:{{ state.list.use_start_time }}至{{ state.list.use_end_time }}
+
+
+
+
+
+ 优惠券类型
+ {{ state.list.type_text }}
+
+
+
+
+ {{ state.list.description }}
+
+
+
+
+
+
+
+
+ {{ state.list.use_scope_text }}
+
+
+
+ {{ state.list.use_scope_text }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/coupon/list.vue b/pages/coupon/list.vue
new file mode 100644
index 00000000..4bb02a0e
--- /dev/null
+++ b/pages/coupon/list.vue
@@ -0,0 +1,261 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/goods/comment/add.vue b/pages/goods/comment/add.vue
new file mode 100644
index 00000000..a0902194
--- /dev/null
+++ b/pages/goods/comment/add.vue
@@ -0,0 +1,209 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 商品质量
+
+
+
+
+
+
+ 服务态度
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/goods/comment/list.vue b/pages/goods/comment/list.vue
new file mode 100644
index 00000000..01526b00
--- /dev/null
+++ b/pages/goods/comment/list.vue
@@ -0,0 +1,167 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/goods/components/detail/comment-item.vue b/pages/goods/components/detail/comment-item.vue
new file mode 100644
index 00000000..518db2a8
--- /dev/null
+++ b/pages/goods/components/detail/comment-item.vue
@@ -0,0 +1,94 @@
+
+
+
+
+
+
+
+
+ {{ item.userNickname }}
+
+
+
+
+ {{ item.content }}
+
+
+
+
+
+
+
+
+
+
+
+ 商家回复:
+ {{ item.replyContent }}
+
+
+
+
+
+
+
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 00000000..755d1bd3
--- /dev/null
+++ b/pages/goods/components/detail/detail-activity-tip.vue
@@ -0,0 +1,101 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ 该商品正在参与{{ activity.name }}活动
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/goods/components/detail/detail-cell-params.vue b/pages/goods/components/detail/detail-cell-params.vue
new file mode 100644
index 00000000..d7f31c4a
--- /dev/null
+++ b/pages/goods/components/detail/detail-cell-params.vue
@@ -0,0 +1,115 @@
+
+
+
+
+
+
+
+
+ {{ item.title }}
+ {{ item.content }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/goods/components/detail/detail-cell-service.vue b/pages/goods/components/detail/detail-cell-service.vue
new file mode 100644
index 00000000..f24431e0
--- /dev/null
+++ b/pages/goods/components/detail/detail-cell-service.vue
@@ -0,0 +1,121 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.name }}
+ {{ item.description }}
+
+
+
+
+
+
+
+
+
+
+
+
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 00000000..31a8b462
--- /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 00000000..b29f3669
--- /dev/null
+++ b/pages/goods/components/detail/detail-cell.vue
@@ -0,0 +1,60 @@
+
+
+ {{ label }}
+ {{ value }}
+
+
+
+
+
+
+
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 00000000..cc48dfde
--- /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 00000000..d2944879
--- /dev/null
+++ b/pages/goods/components/detail/detail-content-card.vue
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/goods/components/detail/detail-navbar.vue b/pages/goods/components/detail/detail-navbar.vue
new file mode 100644
index 00000000..d90b5e10
--- /dev/null
+++ b/pages/goods/components/detail/detail-navbar.vue
@@ -0,0 +1,255 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/goods/components/detail/detail-progress.vue b/pages/goods/components/detail/detail-progress.vue
new file mode 100644
index 00000000..d4a6dd0d
--- /dev/null
+++ b/pages/goods/components/detail/detail-progress.vue
@@ -0,0 +1,39 @@
+
+
+ 已抢{{ percent }}%
+
+
+
+
+
+
+
+
+
diff --git a/pages/goods/components/detail/detail-skeleton.vue b/pages/goods/components/detail/detail-skeleton.vue
new file mode 100644
index 00000000..5eebf50c
--- /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 00000000..aec21ede
--- /dev/null
+++ b/pages/goods/components/detail/detail-tabbar.vue
@@ -0,0 +1,171 @@
+
+
+
+
+
+
+
+ 已收藏
+
+
+
+ 收藏
+
+
+
+
+ 客服
+
+
+
+ 分享
+
+
+
+
+
+
+
+
+
+
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 00000000..0688a86b
--- /dev/null
+++ b/pages/goods/components/groupon/groupon-card-list.vue
@@ -0,0 +1,137 @@
+
+
+
+ 已有{{ modelValue.sales }}人参与活动
+
+
+
+
+
+ {{ item.leader.nickname }}
+
+
+
+
+ 还差
+ {{ item.num - item.current_num }}人
+ 成团
+
+ {{ endTime(item.expire_time) }}
+
+
+
+
+
+
+
+
+
+
+
+
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 00000000..8858125e
--- /dev/null
+++ b/pages/goods/components/list/list-goods-card.vue
@@ -0,0 +1,103 @@
+
+
+
+
+
+
+
+ {{ title }}
+ {{ subTitle }}
+
+ {{ price }}
+ {{ originPrice }}
+ 已售{{ sales }}件
+
+
+
+
+
+
+
+
diff --git a/pages/goods/components/list/list-navbar.vue b/pages/goods/components/list/list-navbar.vue
new file mode 100644
index 00000000..15597d5d
--- /dev/null
+++ b/pages/goods/components/list/list-navbar.vue
@@ -0,0 +1,92 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/goods/groupon.vue b/pages/goods/groupon.vue
new file mode 100644
index 00000000..051b71b1
--- /dev/null
+++ b/pages/goods/groupon.vue
@@ -0,0 +1,597 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ goodsPrice }}
+
+
+
+
+
+
+
+ 拼团价
+
+
+
+
+ 单买价:
+
+ {{ state.goodsInfo.original_goods_price[0] || state.goodsInfo.original_price }}
+
+
+
+
+
+
+ 距结束仅剩
+
+ {{ endTime.h }}
+ :
+ {{ endTime.m }}
+ :
+ {{ endTime.s }}
+
+
+ 活动已结束
+
+
+ {{ state.goodsInfo.title }}
+ {{ state.goodsInfo.subtitle }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/goods/index.vue b/pages/goods/index.vue
new file mode 100644
index 00000000..19601af1
--- /dev/null
+++ b/pages/goods/index.vue
@@ -0,0 +1,384 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ fen2yuan(state.selectedSku.price || state.goodsInfo.price) }}
+
+
+ {{ fen2yuan(state.selectedSku.marketPrice || state.goodsInfo.marketPrice) }}
+
+
+
+ {{ formatSales('exact', state.goodsInfo.salesCount) }}
+
+
+
+
+
+
+
+ {{ promos.title }}
+
+
+
+
+
+
+ 领券
+
+
+
+ {{ state.goodsInfo.name }}
+ {{ state.goodsInfo.introduction }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/goods/list.vue b/pages/goods/list.vue
new file mode 100644
index 00000000..70584422
--- /dev/null
+++ b/pages/goods/list.vue
@@ -0,0 +1,383 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.label }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/goods/score.vue b/pages/goods/score.vue
new file mode 100644
index 00000000..ee3707c0
--- /dev/null
+++ b/pages/goods/score.vue
@@ -0,0 +1,368 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ¥{{ goodsPrice.price }}
+ +
+
+
+ {{ goodsPrice.score }}
+
+
+
+ {{ formatExchange(state.goodsInfo.sales_show_type, state.goodsInfo.sales) }}
+
+
+
+ 原价:¥{{ state.selectedSkuPrice.original_price || state.goodsInfo.original_price }}
+
+ {{ state.goodsInfo.title }}
+ {{ state.goodsInfo.subtitle }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/goods/seckill.vue b/pages/goods/seckill.vue
new file mode 100644
index 00000000..abe94b70
--- /dev/null
+++ b/pages/goods/seckill.vue
@@ -0,0 +1,534 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ state.selectedSkuPrice.price || formatPrice(state.goodsInfo.price) }}
+
+
+
+
+
+ 秒杀价
+
+
+
+ 距结束仅剩
+
+ {{ endTime.h }}
+ :
+ {{ endTime.m }}
+ :
+ {{ endTime.s }}
+
+
+ 活动已结束
+
+
+
+ 原价
+
+ {{ state.selectedSkuPrice.original_price || state.goodsInfo.original_price }}
+
+
+
+
+
+ {{ state.goodsInfo.title }}
+ {{ state.goodsInfo.subtitle }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/index/cart.vue b/pages/index/cart.vue
new file mode 100644
index 00000000..02c052f1
--- /dev/null
+++ b/pages/index/cart.vue
@@ -0,0 +1,200 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/index/category.vue b/pages/index/category.vue
new file mode 100644
index 00000000..2317d1d9
--- /dev/null
+++ b/pages/index/category.vue
@@ -0,0 +1,236 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/index/components/first-one.vue b/pages/index/components/first-one.vue
new file mode 100644
index 00000000..2decc86f
--- /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 00000000..482931cc
--- /dev/null
+++ b/pages/index/components/first-two.vue
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+ {{ item.title }}
+ ¥{{ fen2yuan(item.price) }}
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/index/components/second-one.vue b/pages/index/components/second-one.vue
new file mode 100644
index 00000000..86b70782
--- /dev/null
+++ b/pages/index/components/second-one.vue
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+ {{ props.data[activeMenu].name }}
+
+
+
+
+
+
+
+ {{ item.name }}
+
+
+
+
+
+
+
+
+
diff --git a/pages/index/index.vue b/pages/index/index.vue
new file mode 100644
index 00000000..68a00bd8
--- /dev/null
+++ b/pages/index/index.vue
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/index/login.vue b/pages/index/login.vue
new file mode 100644
index 00000000..d2f8ff50
--- /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 00000000..ed7fe3af
--- /dev/null
+++ b/pages/index/page.vue
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/index/search.vue b/pages/index/search.vue
new file mode 100644
index 00000000..3e9b7fcd
--- /dev/null
+++ b/pages/index/search.vue
@@ -0,0 +1,113 @@
+
+
+
+
+
+
+
+ 搜索历史
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/index/user.vue b/pages/index/user.vue
new file mode 100644
index 00000000..37e6291b
--- /dev/null
+++ b/pages/index/user.vue
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/order/aftersale/apply.vue b/pages/order/aftersale/apply.vue
new file mode 100644
index 00000000..6c620df7
--- /dev/null
+++ b/pages/order/aftersale/apply.vue
@@ -0,0 +1,318 @@
+
+
+
+
+
+
+
+
+
+
+
+ 售后类型
+
+
+
+
+
+
+
+
+ 申请原因
+
+ {{ formData.reason }}
+ 请选择申请原因~
+
+
+
+
+
+ 联系方式
+
+
+
+
+
+
+
+ 相关描述
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 申请原因
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/order/aftersale/detail.vue b/pages/order/aftersale/detail.vue
new file mode 100644
index 00000000..ddd8269c
--- /dev/null
+++ b/pages/order/aftersale/detail.vue
@@ -0,0 +1,355 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.title }}
+
+
+
+
+
+
+
+
+
+
+ 退款总额
+ ¥{{ state.info.refundPrice/100 }}
+
+
+
+
+
+
+
+
+
+
+ 服务单号:
+ {{ state.info.no }}
+
+
+
+ 申请时间:
+
+ {{ sheep.$helper.timeFormat(state.info.createTime, 'yyyy-mm-dd hh:MM:ss') }}
+
+
+
+ 售后类型:
+ {{ status2[state.info.way] }}
+
+
+ 申请原因:
+ {{ state.info.applyReason }}
+
+
+ 相关描述:
+ {{ state.info.applyDescription }}
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/order/aftersale/list.vue b/pages/order/aftersale/list.vue
new file mode 100644
index 00000000..4cf824b0
--- /dev/null
+++ b/pages/order/aftersale/list.vue
@@ -0,0 +1,238 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 服务单号:{{ order.no }}
+ {{ status[order.status] }}
+
+
+
+
+
+ {{ status2[order.way] }}
+
+ {{ order.applyReason }}
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/order/aftersale/log-item.vue b/pages/order/aftersale/log-item.vue
new file mode 100644
index 00000000..edd28c5e
--- /dev/null
+++ b/pages/order/aftersale/log-item.vue
@@ -0,0 +1,99 @@
+
+
+
+
+
+
+
+ {{ item.log_type_text }}
+
+
+
+
+
+
+
+
+
+
+ {{ item.create_time }}
+
+
+
+
+
diff --git a/pages/order/aftersale/log.vue b/pages/order/aftersale/log.vue
new file mode 100644
index 00000000..419d98df
--- /dev/null
+++ b/pages/order/aftersale/log.vue
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/order/confirm.vue b/pages/order/confirm.vue
new file mode 100644
index 00000000..7f866a4c
--- /dev/null
+++ b/pages/order/confirm.vue
@@ -0,0 +1,414 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 配送方式
+
+ {{ item.dispatch_type_text }}
+
+
+
+
+
+
+ 订单备注
+
+
+
+
+
+
+
+
+
+ 商品金额
+
+ ¥{{ state.orderInfo.goods_amount }}
+
+
+
+ 扣除积分
+
+
+ {{ state.orderInfo.score_amount }}
+
+
+
+ 运费
+
+ +¥{{ state.orderInfo.dispatch_amount }}
+
+
+
+
+ 优惠券
+
+ -¥{{ state.orderInfo.coupon_discount_fee }}
+ {{
+ state.couponInfo.can_use?.length > 0
+ ? state.couponInfo.can_use?.length + '张可用'
+ : '暂无可用优惠券'
+ }}
+
+
+
+
+
+
+ 活动优惠
+
+ -¥{{ state.orderInfo.promo_discount_fee }}
+
+
+
+
+
+
+
+
+
+ 发票申请
+
+ {{ state.invoiceInfo.name || '无需开具发票' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/order/detail.vue b/pages/order/detail.vue
new file mode 100644
index 00000000..c6b370fc
--- /dev/null
+++ b/pages/order/detail.vue
@@ -0,0 +1,692 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ state.orderInfo.status_text }}
+
+ {{ state.orderInfo.status_desc }}
+
+
+
+
+
+
+ {{ state.orderInfo.address.consignee }}
+
+ {{ state.orderInfo.address.mobile }}
+
+ {{ addressText }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 订单编号:
+ {{ state.orderInfo.order_sn }}
+
+
+
+
+ 下单时间:
+ {{ state.orderInfo.create_time }}
+
+
+ 支付时间:
+ {{ state.orderInfo.paid_time || '-' }}
+
+
+ 支付方式:
+ {{ state.orderInfo.pay_types_text?.join(',') || '-' }}
+
+
+
+
+
+
+ 商品总额
+
+ ¥{{ state.orderInfo.goods_amount }}
+ +
+
+
+ {{ state.orderInfo.score_amount }}
+
+
+
+
+ 运费
+ ¥{{ state.orderInfo.dispatch_amount }}
+
+
+ 优惠金额
+ ¥{{ state.orderInfo.total_discount_fee }}
+
+
+ {{
+ ['paid', 'completed'].includes(state.orderInfo.status) ? '已付款' : '需付款'
+ }}
+ ¥{{ state.orderInfo.pay_fee }}
+ +
+
+
+ {{ state.orderInfo.score_amount }}
+
+
+
+ 已退款
+ ¥{{ refundFee.toFixed(2) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/order/dispatch/content.vue b/pages/order/dispatch/content.vue
new file mode 100644
index 00000000..5095c6c0
--- /dev/null
+++ b/pages/order/dispatch/content.vue
@@ -0,0 +1,84 @@
+
+
+
+
+
+
+
+
+
+
+ 发货信息
+
+
+ {{ item.title }}: {{ item.content }}
+
+
+ {{ state.data.ext?.dispatch_content }}
+
+
+
+
+
diff --git a/pages/order/express/list.vue b/pages/order/express/list.vue
new file mode 100644
index 00000000..5e285a27
--- /dev/null
+++ b/pages/order/express/list.vue
@@ -0,0 +1,104 @@
+
+
+
+
+
+
+
+
+
+
+ {{ item.logs[0]?.content }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/order/express/log.vue b/pages/order/express/log.vue
new file mode 100644
index 00000000..1dd80910
--- /dev/null
+++ b/pages/order/express/log.vue
@@ -0,0 +1,174 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 物流状态:
+ {{ state.info.status_text }}
+
+ 快递单号:{{ state.info.express_no }}
+ 快递公司:{{ state.info.express_name }}
+
+
+
+
+
+
+
+
+
+
+ {{ item.status_text }}
+ {{ item.content }}
+ {{ item.change_date }}
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/order/invoice.vue b/pages/order/invoice.vue
new file mode 100644
index 00000000..dd33fa19
--- /dev/null
+++ b/pages/order/invoice.vue
@@ -0,0 +1,329 @@
+
+
+
+
+
+
+
+ {{ state.data.status_text }}
+
+
+ 预计可开发票金额:
+ ¥{{ state.data.amount }}
+
+
+
+ 增值税电子普通发票
+
+
+
+
+
+
+
+ {{ item.title }}
+
+
+
+
+
+ 发票类型
+ {{ state.data.type_text }}
+
+
+ 发票抬头
+ {{ state.data.name }}
+
+
+ 发票税号
+ {{ state.data.tax_no }}
+
+
+ 实开金额
+ ¥{{ state.data.invoice_amount }}
+
+
+ 开票时间
+ {{ state.data.finish_time }}
+
+
+ 申请时间
+ {{ state.data.create_time }}
+
+
+
+
+
+
+
+
+ 共{{ state.numImage }}张
+ 点击预览发票
+
+
+
+
+
+
+
+
+
+ 订单状态
+ {{ state.data.order?.status_text }}
+
+
+ 订单编号
+ {{ state.data.order?.order_sn }}
+
+
+ 下单时间
+ {{ state.data.order?.create_time }}
+
+
+
+
+
+
+
+
+
diff --git a/pages/order/list.vue b/pages/order/list.vue
new file mode 100644
index 00000000..5cc39aae
--- /dev/null
+++ b/pages/order/list.vue
@@ -0,0 +1,586 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 共{{count}}件商品,总金额:
+
+ ¥{{ order.payPrice/100 }}
+ +
+
+
+
+ {{ order.score_amount }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/pay/components/account-info-modal.vue b/pages/pay/components/account-info-modal.vue
new file mode 100644
index 00000000..7b33280d
--- /dev/null
+++ b/pages/pay/components/account-info-modal.vue
@@ -0,0 +1,237 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/pay/components/account-type-select.vue b/pages/pay/components/account-type-select.vue
new file mode 100644
index 00000000..8bb351cb
--- /dev/null
+++ b/pages/pay/components/account-type-select.vue
@@ -0,0 +1,178 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/pay/index.vue b/pages/pay/index.vue
new file mode 100644
index 00000000..8b11d90e
--- /dev/null
+++ b/pages/pay/index.vue
@@ -0,0 +1,356 @@
+
+
+
+
+
+
+ 选择支付方式
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/pay/recharge-log.vue b/pages/pay/recharge-log.vue
new file mode 100644
index 00000000..2361f61a
--- /dev/null
+++ b/pages/pay/recharge-log.vue
@@ -0,0 +1,171 @@
+
+
+
+
+
+
+ 充值金额
+ {{ item.pay_fee }}元
+
+
+ 支付状态
+ {{ item.status_text }}
+
+
+ 充值渠道
+ {{ item.platform_text }}
+
+
+ 充值单号
+ {{ item.order_sn }}
+
+
+ 充值时间
+ {{ item.paid_time }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/pay/recharge.vue b/pages/pay/recharge.vue
new file mode 100644
index 00000000..ccd8adff
--- /dev/null
+++ b/pages/pay/recharge.vue
@@ -0,0 +1,250 @@
+
+
+
+
+ 当前余额(元)
+ {{ userInfo.money }}
+
+
+
+
+
+ 充值金额
+
+ ¥
+
+
+
+
+
+
+
+
+ 关闭充值
+
+
+
+
+
+
+
diff --git a/pages/pay/result.vue b/pages/pay/result.vue
new file mode 100644
index 00000000..5b18ba8e
--- /dev/null
+++ b/pages/pay/result.vue
@@ -0,0 +1,285 @@
+
+
+
+
+
+
+
+ {{
+ state.orderInfo.pay_mode === 'offline' ? '下单成功' : '支付成功'
+ }}
+ 支付失败
+ 该订单已关闭
+ 检测支付结果...
+
+ ¥{{ state.orderInfo.pay_fee }}
+ +
+
+
+ {{ state.orderInfo.score_amount }}
+
+
+
+
+
+
+
+
+
+
+
+ 获取实时发货信息与订单状态
+ 立即订阅
+
+
+
+
+
+
+
+
+
diff --git a/pages/pay/withdraw-log.vue b/pages/pay/withdraw-log.vue
new file mode 100644
index 00000000..3661d4d9
--- /dev/null
+++ b/pages/pay/withdraw-log.vue
@@ -0,0 +1,187 @@
+
+
+
+
+
+
+ {{
+ item.withdraw_type === 'bank'
+ ? '提现至银行卡'
+ : item.withdraw_type === 'alipay'
+ ? '提现至支付宝'
+ : '提现至微信'
+ }}
+ {{ item.amount }}元
+
+
+ 申请状态
+ {{ item.status_text }}
+
+
+ 账户信息
+ {{ item.withdraw_info_hidden.开户行 }}[{{ item.withdraw_info_hidden.银行卡号 }}]
+
+ 支付宝[{{ item.withdraw_info_hidden.支付宝账户 }}]
+
+ 微信零钱
+
+
+ 提现单号
+ {{ item.withdraw_sn }}
+
+
+ 手续费
+ {{ item.charge_fee }}元
+
+
+ 申请时间
+ {{ item.create_time }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/pay/withdraw.vue b/pages/pay/withdraw.vue
new file mode 100644
index 00000000..d09cd588
--- /dev/null
+++ b/pages/pay/withdraw.vue
@@ -0,0 +1,380 @@
+
+
+
+
+
+ 可提现金额(元)
+ {{ userInfo.commission }}
+
+
+
+
+
+ 提现金额
+
+ ¥
+
+
+
+ 提现至
+
+ 请选择提现方式
+ 微信零钱
+ 支付宝账户
+ 银行卡转账
+
+
+
+
+
+ {{ state.accountInfo.account_header }}|{{ state.accountInfo.account_name }}
+
+ 暂无提现账户
+
+
+
+
+
+
+
+ 提现说明
+
+ {{ index + 1 }}.{{ rule }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/public/error.vue b/pages/public/error.vue
new file mode 100644
index 00000000..a16d4d5c
--- /dev/null
+++ b/pages/public/error.vue
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/public/faq.vue b/pages/public/faq.vue
new file mode 100644
index 00000000..9c9ac6ca
--- /dev/null
+++ b/pages/public/faq.vue
@@ -0,0 +1,110 @@
+
+
+
+
+
+
+
+
+ {{ item.content }}
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/public/feedback.vue b/pages/public/feedback.vue
new file mode 100644
index 00000000..f482d60d
--- /dev/null
+++ b/pages/public/feedback.vue
@@ -0,0 +1,226 @@
+
+
+
+
+ 请选择类型
+
+
+
+
+
+
+
+ 相关描述
+
+
+
+
+
+
+ 联系方式
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/public/richtext.vue b/pages/public/richtext.vue
new file mode 100644
index 00000000..bb280ce9
--- /dev/null
+++ b/pages/public/richtext.vue
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
diff --git a/pages/public/setting.vue b/pages/public/setting.vue
new file mode 100644
index 00000000..c3cfc1f3
--- /dev/null
+++ b/pages/public/setting.vue
@@ -0,0 +1,239 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/public/webview.vue b/pages/public/webview.vue
new file mode 100644
index 00000000..74e9d204
--- /dev/null
+++ b/pages/public/webview.vue
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
diff --git a/pages/user/address/edit.vue b/pages/user/address/edit.vue
new file mode 100644
index 00000000..eb5e8929
--- /dev/null
+++ b/pages/user/address/edit.vue
@@ -0,0 +1,261 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 设为默认地址
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/user/address/list.vue b/pages/user/address/list.vue
new file mode 100644
index 00000000..f8421a2f
--- /dev/null
+++ b/pages/user/address/list.vue
@@ -0,0 +1,147 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/user/goods-collect.vue b/pages/user/goods-collect.vue
new file mode 100644
index 00000000..178d5d24
--- /dev/null
+++ b/pages/user/goods-collect.vue
@@ -0,0 +1,242 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/user/goods-log.vue b/pages/user/goods-log.vue
new file mode 100644
index 00000000..76043db7
--- /dev/null
+++ b/pages/user/goods-log.vue
@@ -0,0 +1,289 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/user/info.vue b/pages/user/info.vue
new file mode 100644
index 00000000..b6251178
--- /dev/null
+++ b/pages/user/info.vue
@@ -0,0 +1,510 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 第三方账号绑定
+
+
+
+
+ 微信公众号
+
+
+
+ 微信小程序
+
+
+
+ 微信开放平台
+
+
+
+
+ {{ state.thirdOauthInfo.nickname }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/user/invoice/edit.vue b/pages/user/invoice/edit.vue
new file mode 100644
index 00000000..704d2fce
--- /dev/null
+++ b/pages/user/invoice/edit.vue
@@ -0,0 +1,277 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/user/invoice/list.vue b/pages/user/invoice/list.vue
new file mode 100644
index 00000000..bb3be36c
--- /dev/null
+++ b/pages/user/invoice/list.vue
@@ -0,0 +1,82 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/user/set.vue b/pages/user/set.vue
new file mode 100644
index 00000000..191f074c
--- /dev/null
+++ b/pages/user/set.vue
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/user/wallet/commission.vue b/pages/user/wallet/commission.vue
new file mode 100644
index 00000000..dc35b0dc
--- /dev/null
+++ b/pages/user/wallet/commission.vue
@@ -0,0 +1,510 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ 总收入¥{{ state.pagination.income.toFixed(2) }}
+ 总支出¥{{ (-state.pagination.expense).toFixed(2) }}
+
+
+
+
+
+
+
+
+ 转余额
+ 将您的佣金转到余额中继续消费
+
+ ¥
+
+
+
+
+
+
+
+
+
+
+ {{ item.event_text }}{{ item.memo ? '-' + item.memo : '' }}
+
+ +{{ item.amount }}
+ {{ item.amount }}
+
+
+ {{ item.create_time }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pages/user/wallet/money.vue b/pages/user/wallet/money.vue
new file mode 100644
index 00000000..3b6a9143
--- /dev/null
+++ b/pages/user/wallet/money.vue
@@ -0,0 +1,345 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.title }}
+
+ +{{ item.amount||item.price }}
+ {{ item.price }}
+
+
+ {{ item.createTime }}
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/user/wallet/score.vue b/pages/user/wallet/score.vue
new file mode 100644
index 00000000..fc75d75a
--- /dev/null
+++ b/pages/user/wallet/score.vue
@@ -0,0 +1,279 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.title }}{{ item.description ? '-' + item.description : '' }}
+ {{ sheep.$helper.timeFormat(item.createTime, 'yyyy-mm-dd hh:MM:ss')}}
+
+ +{{ parseInt(item.point) }}
+ {{ parseInt(item.point) }}
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sheep/api/activity.js b/sheep/api/activity.js
new file mode 100644
index 00000000..83d620be
--- /dev/null
+++ b/sheep/api/activity.js
@@ -0,0 +1,43 @@
+import request from '@/sheep/request';
+
+export default {
+ myGroupon: (params) =>
+ request({
+ url: 'activity/groupon/myGroupons',
+ method: 'GET',
+ params,
+ }),
+ getGrouponList: (params) =>
+ request({
+ url: 'activity/groupon',
+ method: 'GET',
+ params,
+ }),
+ grouponDetail: (id) =>
+ request({
+ url: 'activity/groupon/' + id,
+ method: 'GET',
+ }),
+ signList: (params) =>
+ request({
+ url: 'activity/signin',
+ method: 'GET',
+ params,
+ }),
+ signAdd: () =>
+ request({
+ url: 'activity/signin',
+ method: 'POST',
+ }),
+ replenish: (data) =>
+ request({
+ url: 'activity/signin/replenish',
+ method: 'POST',
+ data,
+ }),
+ activity: (id) =>
+ request({
+ url: 'activity/activity/' + id,
+ method: 'GET',
+ }),
+};
diff --git a/sheep/api/app.js b/sheep/api/app.js
new file mode 100644
index 00000000..2bb92c6c
--- /dev/null
+++ b/sheep/api/app.js
@@ -0,0 +1,146 @@
+import request from '@/sheep/request';
+import { baseUrl } from '@/sheep/config';
+
+export default {
+ // TODO 芋艿:测试
+ test: () =>
+ request({
+ url: '/app-api/promotion/decorate/list',
+ params: {
+ page: 1
+ },
+ custom: {
+ showError: false,
+ showLoading: false,
+ },
+ }),
+ // 系统初始化
+ init: (templateId) =>
+ request({
+ url: 'init',
+ params: {
+ templateId,
+ },
+ custom: {
+ showError: false,
+ showLoading: false,
+ },
+ }),
+ // 同步客户端页面到后端
+ pageSync: (pages) =>
+ request({
+ url: 'pageSync',
+ method: 'POST',
+ data: {
+ pages,
+ },
+ custom: {
+ showError: false,
+ showLoading: false,
+ },
+ }),
+ // 发送短信
+ sendSms: (data) =>
+ request({
+ url: 'sendSms',
+ method: 'POST',
+ data,
+ custom: {
+ showSuccess: true,
+ loadingMsg: '发送中',
+ },
+ }),
+ //意见反馈
+ feedback: (data) =>
+ request({
+ url: 'feedback',
+ method: 'POST',
+ data,
+ }),
+ // 自定义页面
+ page: (id) =>
+ request({
+ url: 'page/' + id,
+ method: 'GET',
+ }),
+ //积分商城
+ scoreShop: {
+ list: (params) =>
+ request({
+ url: 'app/scoreShop',
+ method: 'GET',
+ params,
+ }),
+ ids: (params = {}) =>
+ request({
+ url: 'app/scoreShop/ids',
+ method: 'GET',
+ params,
+ }),
+ detail: (id) =>
+ request({
+ url: 'app/scoreShop/' + id,
+ method: 'GET',
+ }),
+ },
+ //小程序直播
+ mplive: {
+ getRoomList: (ids) =>
+ request({
+ url: 'app/mplive/getRoomList',
+ method: 'GET',
+ params: {
+ ids: ids.join(','),
+ }
+ }),
+ getMpLink: () =>
+ request({
+ url: 'app/mplive/getMpLink',
+ method: 'GET'
+ }),
+ },
+
+ //上传
+ upload: (file, group = 'ugc', callback) => {
+ const token = uni.getStorageSync('token');
+ uni.showLoading({
+ title: '上传中',
+ });
+ return new Promise((resolve, reject) => {
+ // 此处先换成yudao
+ uni.uploadFile({
+ url: 'http://api-dashboard.yudao.iocoder.cn' + '/app-api/infra/file/upload',
+ filePath: file,
+ name: 'file',
+ formData: {
+ group,
+ },
+ header: {
+ // Accept: 'text/json',
+ // Authorization: token,
+ Accept : '*/*',
+ 'tenant-id' :'1',
+ Authorization: 'Bearer test247',
+ },
+ success: (uploadFileRes) => {
+ let result = JSON.parse(uploadFileRes.data);
+ if (result.error === 1) {
+ uni.showToast({
+ icon: 'none',
+ title: result.msg,
+ });
+ } else {
+ return resolve(result.data);
+ }
+ },
+ fail: (error) => {
+ console.log('上传失败:', error);
+ return resolve(false);
+ },
+ complete: () => {
+ uni.hideLoading();
+ },
+ });
+ });
+ },
+};
diff --git a/sheep/api/cart.js b/sheep/api/cart.js
new file mode 100644
index 00000000..f4aacaa3
--- /dev/null
+++ b/sheep/api/cart.js
@@ -0,0 +1,42 @@
+import request from '@/sheep/request';
+import request2 from '@/sheep/request2';
+
+export default {
+ list: (data) =>
+ request2({
+ url: 'trade/cart/list',
+ method: 'GET',
+ custom: {
+ showLoading: false,
+ auth: true,
+ },
+ }),
+ append: (data) =>
+ request({
+ url: 'cart',
+ method: 'POST',
+ custom: {
+ showSuccess: true,
+ successMsg: '已添加到购物车~',
+ },
+ data: {
+ ...data,
+ type: 'inc',
+ },
+ }),
+ // 删除购物车
+ delete: (ids) =>
+ request2({
+ url: 'trade/cart/delete?ids=' + ids,
+ method: 'DELETE',
+ }),
+ update: (data) =>
+ request2({
+ url: 'trade/cart/update-count',
+ method: 'PUT',
+ data: {
+ ...data,
+ type: 'cover',
+ },
+ }),
+};
\ No newline at end of file
diff --git a/sheep/api/category.js b/sheep/api/category.js
new file mode 100644
index 00000000..9c825069
--- /dev/null
+++ b/sheep/api/category.js
@@ -0,0 +1,10 @@
+import request2 from '@/sheep/request2';
+
+export default {
+ list: (params) =>
+ request2({
+ url: 'product/category/list',
+ method: 'GET',
+ params,
+ }),
+};
diff --git a/sheep/api/chat.js b/sheep/api/chat.js
new file mode 100644
index 00000000..5a4a544d
--- /dev/null
+++ b/sheep/api/chat.js
@@ -0,0 +1,13 @@
+import request from '@/sheep/request';
+
+export default {
+ // 获取聊天token
+ unifiedToken: () =>
+ request({
+ url: 'unifiedToken',
+ custom: {
+ showError: false,
+ showLoading: false,
+ },
+ }),
+};
diff --git a/sheep/api/commission.js b/sheep/api/commission.js
new file mode 100644
index 00000000..e3b44c75
--- /dev/null
+++ b/sheep/api/commission.js
@@ -0,0 +1,65 @@
+import request from '@/sheep/request';
+
+export default {
+ // 分销商详情
+ agent: () =>
+ request({
+ url: 'commission/agent',
+ method: 'GET',
+ custom: {
+ showLoading: false,
+ showError: false,
+ },
+ }),
+ // 分销表单
+ form: () =>
+ request({
+ url: 'commission/agent/form',
+ method: 'GET',
+ }),
+ // 申请分销商
+ apply: (data) =>
+ request({
+ url: 'commission/agent/apply',
+ method: 'POST',
+ data,
+ custom: {
+ showSuccess: true,
+ },
+ }),
+ // 分销动态
+ log: (params) =>
+ request({
+ url: 'commission/log',
+ method: 'GET',
+ params,
+ }),
+ // 分销订单
+ order: (params) =>
+ request({
+ url: 'commission/order',
+ method: 'GET',
+ params,
+ }),
+ // 分销商品
+ goods: (params) =>
+ request({
+ url: 'commission/goods',
+ method: 'GET',
+ params,
+ }),
+ // 我的团队
+ team: (params) =>
+ request({
+ url: 'commission/agent/team',
+ method: 'GET',
+ params,
+ }),
+ // 佣金转余额
+ transfer: (data) =>
+ request({
+ url: 'commission/agent/transfer',
+ method: 'POST',
+ data,
+ }),
+};
diff --git a/sheep/api/coupon.js b/sheep/api/coupon.js
new file mode 100644
index 00000000..3f5e8e6e
--- /dev/null
+++ b/sheep/api/coupon.js
@@ -0,0 +1,45 @@
+import request from '@/sheep/request';
+import request2 from '@/sheep/request2';
+
+export default {
+ // 我的拼团
+ list: (params) =>
+ request({
+ url: 'coupon',
+ method: 'GET',
+ params,
+ custom: {
+ showLoading: false,
+ },
+ }),
+ userCoupon: (params) =>
+ request2({
+ url: 'promotion/coupon/page',
+ method: 'GET',
+ params,
+ }),
+ // userCoupon: (params) =>
+ // request({
+ // url: 'user/coupon',
+ // method: 'GET',
+ // params,
+ // }),
+ detail: (id, user_coupon_id) =>
+ request({
+ url: 'coupon/' + id,
+ method: 'GET',
+ params: {
+ user_coupon_id,
+ },
+ }),
+ get: (id) =>
+ request({
+ url: 'coupon/get/' + id,
+ method: 'POST',
+ }),
+ listByGoods: (id) =>
+ request({
+ url: 'coupon/listByGoods/' + id,
+ method: 'GET',
+ }),
+};
\ No newline at end of file
diff --git a/sheep/api/data.js b/sheep/api/data.js
new file mode 100644
index 00000000..6a5b08b1
--- /dev/null
+++ b/sheep/api/data.js
@@ -0,0 +1,25 @@
+import request from '@/sheep/request';
+import request2 from '@/sheep/request2';
+
+export default {
+ area: () =>
+ request2({
+ url: 'system/area/tree',
+ method: 'GET',
+ }),
+ // area: () =>
+ // request({
+ // url: 'data/area',
+ // method: 'GET',
+ // }),
+ faq: () =>
+ request({
+ url: 'data/faq',
+ method: 'GET',
+ }),
+ richtext: (id) =>
+ request({
+ url: 'data/richtext/' + id,
+ method: 'GET',
+ }),
+};
diff --git a/sheep/api/goods.js b/sheep/api/goods.js
new file mode 100644
index 00000000..61f422a6
--- /dev/null
+++ b/sheep/api/goods.js
@@ -0,0 +1,80 @@
+import request from '@/sheep/request';
+import request2 from '@/sheep/request2';
+
+export default {
+ // 商品详情
+ detail: (id, params = {}) =>
+ request2({
+ url: 'product/spu/get-detail?id=' + id,
+ method: 'GET',
+ params,
+ custom: {
+ showLoading: false,
+ showError: false,
+ },
+ }),
+
+ // 商品列表
+ list: (params) =>
+ request2({
+ url: 'product/spu/page',
+ method: 'GET',
+ params,
+ custom: {
+ showLoading: false,
+ showError: false,
+ },
+ }),
+
+ // 商品查询
+ ids: (params = {}) =>
+ request({
+ url: 'goods/goods/ids',
+ method: 'GET',
+ params,
+ custom: {
+ showLoading: false,
+ showError: false,
+ },
+ }),
+
+ // 商品评价列表
+ comment: (id, params = {}) =>
+ request2({
+ url: 'product/comment/list?spuId=' + id,
+ method: 'GET',
+ params,
+ custom: {
+ showLoading: false,
+ showError: false,
+ },
+ }),
+ // 商品评价类型
+ getType: (id) =>
+ request({
+ url: 'goods/comment/getType/' + id,
+ method: 'GET',
+ custom: {
+ showLoading: false,
+ showError: false,
+ },
+ }),
+ // 活动商品查询
+ // 商品查询
+ activity: (params = {}) =>
+ request({
+ url: 'goods/goods/activity',
+ method: 'GET',
+ params,
+ custom: {
+ showLoading: false,
+ showError: false,
+ },
+ }),
+ activityList: (params = {}) =>
+ request({
+ url: 'goods/goods/activityList',
+ method: 'GET',
+ params,
+ }),
+};
diff --git a/sheep/api/index.js b/sheep/api/index.js
new file mode 100644
index 00000000..c25a3493
--- /dev/null
+++ b/sheep/api/index.js
@@ -0,0 +1,10 @@
+const files = import.meta.globEager('./*.js');
+let api = {};
+Object.keys(files).forEach((key) => {
+ api = {
+ ...api,
+ [key.replace(/(.*\/)*([^.]+).*/gi, '$2')]: files[key].default,
+ };
+});
+
+export default api;
diff --git a/sheep/api/index2.js b/sheep/api/index2.js
new file mode 100644
index 00000000..1c068bf8
--- /dev/null
+++ b/sheep/api/index2.js
@@ -0,0 +1,14 @@
+import request2 from '@/sheep/request2';
+
+export default {
+ decorate: () =>
+ request2({
+ url: 'promotion/decorate/list?page=1',
+ method: 'GET',
+ }),
+ spids: () =>
+ request2({
+ url: 'product/spu/page?recommendType=best&pageNo=1&pageSize=10',
+ method: 'GET',
+ }),
+};
diff --git a/sheep/api/order.js b/sheep/api/order.js
new file mode 100644
index 00000000..8fb48cad
--- /dev/null
+++ b/sheep/api/order.js
@@ -0,0 +1,192 @@
+import request from '@/sheep/request';
+import request2 from '@/sheep/request2';
+
+export default {
+ // 订单详情
+ detail: (id, params) =>
+ request2({
+ url: 'trade/order/get-detail?id=' + id,
+ method: 'GET',
+ params,
+ }),
+ // detail: (id, params) =>
+ // request({
+ // url: 'order/order/' + id,
+ // method: 'GET',
+ // params,
+ // }),
+ // 发票详情
+ invoice: (id) =>
+ request({
+ url: 'order/invoice/' + id,
+ method: 'GET',
+ }),
+ // 获取支付结果
+ payResult: (id) =>
+ request({
+ url: 'order/order/' + id,
+ method: 'GET',
+ custom: {
+ showLoading: false,
+ },
+ }),
+ itemDetail: (id, itemId) =>
+ request({
+ url: 'order/order/itemDetail/' + id + '/' + itemId,
+ method: 'GET',
+ custom: {
+ showLoading: false,
+ },
+ }),
+ // 订单列表
+ list: (params) =>
+ request2({
+ url: 'trade/order/page',
+ method: 'GET',
+ params,
+ custom: {
+ showLoading: false,
+ },
+ }),
+ // list: (params) =>
+ // request({
+ // url: 'order/order',
+ // method: 'GET',
+ // params,
+ // custom: {
+ // showLoading: false,
+ // },
+ // }),
+ // 计算订单信息
+ calc: (data) => {
+ const data2 = {
+ ...data,
+ }
+ // 解决 SpringMVC 接受 List- 参数的问题
+ delete data2.items
+ for (let i = 0; i < data.items.length; i++) {
+ // data2['items[' + i + '' + '].skuId'] = data.items[i].skuId + '';
+ // data2['items[' + i + '' + '].count'] = data.items[i].count + '';
+ // data2['items[' + i + '' + '].cartId'] = data.items[i].cartId + '';
+ data2['items' + `%5B${i}%5D` + '.skuId'] = data.items[i].skuId + '';
+ data2['items' + `%5B${i}%5D` + '.count'] = data.items[i].count + '';
+ data2['items' + `%5B${i}%5D` + '.cartId'] = data.items[i].cartId + '';
+ }
+ console.log(data2, '对比数据')
+ return request2({
+ url: 'trade/order/settlement',
+ method: 'GET',
+ // data,
+ params: data2
+ })
+ },
+ // calc: (data) =>
+ // request({
+ // url: 'order/order/calc',
+ // method: 'POST',
+ // data,
+ // }),
+ // 创建订单
+ create: (data) =>
+ request({
+ url: 'order/order/create',
+ method: 'POST',
+ data,
+ }),
+ //订单可用优惠券
+ coupons: (data) =>
+ request({
+ url: 'order/order/coupons',
+ method: 'POST',
+ data,
+ }),
+ // 确认收货
+ confirm: (id) =>
+ request({
+ url: 'order/order/confirm/' + id,
+ method: 'PUT',
+ }),
+ // 评价订单
+ comment: (data) =>
+ request2({
+ url: 'trade/order/item/create-comment',
+ method: 'POST',
+ data,
+ }),
+ // comment: (id, data) =>
+ // request({
+ // url: 'order/order/comment/' + id,
+ // method: 'POST',
+ // data,
+ // }),
+ // 申请退款
+ applyRefund: (id) =>
+ request({
+ url: 'order/order/applyRefund/' + id,
+ method: 'PUT',
+ }),
+ // 取消订单
+ cancel: (id) =>
+ request({
+ url: 'order/order/cancel/' + id,
+ method: 'PUT',
+ }),
+ // 删除订单
+ delete: (id) =>
+ request({
+ url: 'order/order/' + id,
+ method: 'DELETE',
+ }),
+ // 售后
+ aftersale: {
+ // 申请售后
+ apply: (data) =>
+ request({
+ url: 'order/aftersale',
+ method: 'POST',
+ data,
+ }),
+ list: (params) =>
+ request2({
+ url: 'trade/after-sale/page',
+ method: 'GET',
+ params,
+ custom: {
+ showLoading: false,
+ },
+ }),
+ // list: (params) =>
+ // request({
+ // url: 'order/aftersale',
+ // method: 'GET',
+ // params,
+ // custom: {
+ // showLoading: false,
+ // },
+ // }),
+ //取消售后
+ cancel: (id) =>
+ request({
+ url: 'order/aftersale/cancel/' + id,
+ method: 'PUT',
+ }),
+ //删除售后单
+ delete: (id) =>
+ request({
+ url: 'order/aftersale/' + id,
+ method: 'DELETE',
+ }),
+ // 售后详情
+ detail: (id) =>
+ request2({
+ url: 'trade/after-sale/get?id=' + id,
+ method: 'GET',
+ }),
+ },
+ //订单包裹
+ express: (id, orderId) =>
+ request({
+ url: 'order/express/' + id + `${orderId ? '/' + orderId : ''}`,
+ method: 'GET',
+ }),
+};
\ No newline at end of file
diff --git a/sheep/api/pay.js b/sheep/api/pay.js
new file mode 100644
index 00000000..8cee0205
--- /dev/null
+++ b/sheep/api/pay.js
@@ -0,0 +1,44 @@
+import request from '@/sheep/request';
+
+export default {
+ // 预支付
+ prepay: (data) =>
+ request({
+ url: 'pay/prepay',
+ method: 'POST',
+ data,
+ custom: {
+ loadingMsg: '支付中',
+ },
+ }),
+ // 发起提现
+ withdraw: {
+ list: (params) =>
+ request({
+ url: 'withdraw',
+ method: 'GET',
+ params,
+ custom: {
+ auth: true,
+ },
+ }),
+ rules: () =>
+ request({
+ url: 'withdraw/rules',
+ method: 'GET',
+ custom: {
+ auth: true,
+ },
+ }),
+ apply: (data) =>
+ request({
+ url: 'withdraw/apply',
+ method: 'POST',
+ data,
+ custom: {
+ loadingMsg: '申请中',
+ auth: true,
+ },
+ }),
+ },
+};
diff --git a/sheep/api/product/category.js b/sheep/api/product/category.js
new file mode 100644
index 00000000..6549acf5
--- /dev/null
+++ b/sheep/api/product/category.js
@@ -0,0 +1,12 @@
+import request from '@/sheep/request';
+
+const CategoryApi = {
+ // 查询分类列表
+ getCategoryList: () => {
+ return request({
+ url: '/app-api/product/category/list',
+ method: 'GET'
+ });
+ }
+};
+export default CategoryApi;
diff --git a/sheep/api/product/comment.js b/sheep/api/product/comment.js
new file mode 100644
index 00000000..01ce25c2
--- /dev/null
+++ b/sheep/api/product/comment.js
@@ -0,0 +1,18 @@
+import request from '@/sheep/request';
+
+const CommentApi = {
+ // 获得商品评价分页
+ getCommentPage: (spuId, pageNo, pageSize, type) => {
+ return request({
+ url: '/app-api/product/comment/page',
+ method: 'GET',
+ params: {
+ spuId,
+ pageNo,
+ pageSize,
+ type
+ },
+ });
+ },
+};
+export default CommentApi;
diff --git a/sheep/api/product/spu.js b/sheep/api/product/spu.js
new file mode 100644
index 00000000..87bf1fa5
--- /dev/null
+++ b/sheep/api/product/spu.js
@@ -0,0 +1,37 @@
+import request from '@/sheep/request';
+
+const SpuApi = {
+ // 获得商品 SPU 列表
+ getSpuList: (recommendType) => {
+ return request({
+ url: '/app-api/product/spu/list',
+ method: 'GET',
+ params: {recommendType},
+ });
+ },
+ // 获得商品 SPU 列表
+ getSpuListByIds: (ids) => {
+ return request({
+ url: '/app-api/product/spu/list-by-ids',
+ method: 'GET',
+ params: {ids},
+ });
+ },
+ // 获得商品 SPU 分页
+ getSpuPage: (data) => {
+ return request({
+ url: '/app-api/product/spu/page',
+ method: 'GET',
+ data
+ });
+ },
+ // 查询商品
+ getSpuDetail: (id) => {
+ return request({
+ url: '/app-api/product/spu/get-detail',
+ method: 'GET',
+ params: { id },
+ });
+ }
+};
+export default SpuApi;
diff --git a/sheep/api/promotion/activity.js b/sheep/api/promotion/activity.js
new file mode 100644
index 00000000..efa0e1e9
--- /dev/null
+++ b/sheep/api/promotion/activity.js
@@ -0,0 +1,16 @@
+import request2 from '@/sheep/request2';
+
+const ActivityApi = {
+ // 获得单个商品,近期参与的每个活动
+ getActivityListBySpuId: (spuId) => {
+ return request2({
+ url: '/app-api/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 00000000..36b037d0
--- /dev/null
+++ b/sheep/api/promotion/article.js
@@ -0,0 +1,12 @@
+import request from '@/sheep/request';
+
+export default {
+ // 获得文章详情
+ getArticle: (id) => {
+ return request({
+ url: '/app-api/promotion/article/get',
+ method: 'GET',
+ params: { id }
+ });
+ }
+}
diff --git a/sheep/api/promotion/combination.js b/sheep/api/promotion/combination.js
new file mode 100644
index 00000000..eeeb9a0a
--- /dev/null
+++ b/sheep/api/promotion/combination.js
@@ -0,0 +1,67 @@
+import request2 from "@/sheep/request2";
+
+// 拼团 API
+const CombinationApi = {
+ // 获得拼团活动列表
+ getCombinationActivityList: (count) => {
+ return request2({
+ url: "promotion/combination-activity/list",
+ method: 'GET',
+ params: {count}
+ });
+ },
+
+ // 获得拼团活动分页
+ getCombinationActivityPage: (params) => {
+ return request2({
+ url: "promotion/combination-activity/page",
+ method: 'GET',
+ params
+ });
+ },
+
+ // 获得拼团活动明细
+ getCombinationActivity: (id) => {
+ return request2({
+ url: "promotion/combination-activity/get-detail",
+ method: 'GET',
+ params: {
+ id
+ }
+ });
+ },
+
+ // 获得最近 n 条拼团记录(团长发起的)
+ getHeadCombinationRecordList: (activityId, status, count) => {
+ return request2({
+ url: "promotion/combination-record/get-head-list",
+ method: 'GET',
+ params: {
+ activityId,
+ status,
+ count
+ }
+ });
+ },
+
+ // 获得拼团记录明细
+ getCombinationRecordDetail: (id) => {
+ return request2({
+ url: "promotion/combination-record/get-detail",
+ method: 'GET',
+ params: {
+ id
+ }
+ });
+ },
+
+ // 获得拼团记录的概要信息
+ getCombinationRecordSummary: () => {
+ return request2({
+ 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 00000000..9bdbb19e
--- /dev/null
+++ b/sheep/api/promotion/coupon.js
@@ -0,0 +1,20 @@
+import request from '@/sheep/request';
+
+export default {
+ // 获得优惠劵模板列表
+ getCouponTemplateListByIds: (ids) => {
+ return request({
+ url: '/app-api/promotion/coupon-template/list-by-ids',
+ method: 'GET',
+ params: { ids },
+ });
+ },
+ // 获得优惠劵模版列表
+ getCouponTemplateList: (spuId, productScope, count) => {
+ return request({
+ url: '/app-api/promotion/coupon-template/list',
+ method: 'GET',
+ params: { spuId, productScope, count },
+ });
+ },
+};
diff --git a/sheep/api/promotion/diy/template.js b/sheep/api/promotion/diy/template.js
new file mode 100644
index 00000000..054dffae
--- /dev/null
+++ b/sheep/api/promotion/diy/template.js
@@ -0,0 +1,20 @@
+import request from '@/sheep/request';
+
+ const DiyTemplateApi = {
+ // TODO 芋艿:测试
+ getUsedDiyTemplate: () => {
+ return request({
+ url: '/app-api/promotion/diy-template/used',
+ method: 'GET',
+ });
+ },
+ getDiyTemplate: (id) => {
+ return request({
+ url: '/app-api/promotion/diy-template/get',
+ method: 'GET',
+ params: { id }
+ });
+ },
+};
+
+export default DiyTemplateApi;
diff --git a/sheep/api/promotion/seckill.js b/sheep/api/promotion/seckill.js
new file mode 100644
index 00000000..ecbb4c1f
--- /dev/null
+++ b/sheep/api/promotion/seckill.js
@@ -0,0 +1,33 @@
+import request2 from "@/sheep/request2";
+
+const SeckillApi = {
+ // 获得秒杀时间段列表
+ getSeckillConfigList: () => {
+ return request2({ url: 'promotion/seckill-config/list', method: 'GET' });
+ },
+
+ // 获得当前秒杀活动
+ getNowSeckillActivity: () => {
+ return request2({ url: 'promotion/seckill-activity/get-now', method: 'GET' });
+ },
+
+ // 获得秒杀活动分页
+ getSeckillActivityPage: () => {
+ return request2({ url: 'promotion/seckill-activity/page', method: 'GET' });
+ },
+
+ /**
+ * 获得秒杀活动明细
+ * @param {number} id 秒杀活动编号
+ * @return {*}
+ */
+ getSeckillActivity: (id) => {
+ return request2({
+ url: 'promotion/seckill-activity/get-detail',
+ method: 'GET',
+ params: { id }
+ });
+ }
+}
+
+export default SeckillApi;
diff --git a/sheep/api/third.js b/sheep/api/third.js
new file mode 100644
index 00000000..72b83bff
--- /dev/null
+++ b/sheep/api/third.js
@@ -0,0 +1,127 @@
+import request from '@/sheep/request';
+import { baseUrl, apiPath } from '@/sheep/config';
+
+export default {
+ // 微信相关
+ wechat: {
+ // 第三方登录
+ login: (data) =>
+ request({
+ url: 'third/wechat/login',
+ method: 'POST',
+ data,
+ custom: {
+ showSuccess: true,
+ loadingMsg: '登陆中',
+ },
+ }),
+
+ // 绑定微信
+ bind: (data) =>
+ request({
+ url: 'third/wechat/bind',
+ method: 'POST',
+ data,
+ custom: {
+ showSuccess: true,
+ loadingMsg: '绑定中',
+ },
+ }),
+
+ // 解除绑定微信
+ unbind: (data) =>
+ request({
+ url: 'third/wechat/unbind',
+ method: 'POST',
+ data,
+ custom: {
+ showSuccess: true,
+ loadingMsg: '解除绑定',
+ },
+ }),
+
+ // 公众号授权
+ oauthLogin: (data) =>
+ request({
+ url: 'third/wechat/oauthLogin',
+ method: 'GET',
+ data,
+ custom: {
+ showSuccess: true,
+ loadingMsg: '登陆中',
+ },
+ }),
+
+ // 获取小程序sessionKey(后端不会给前端返回真实的sessionKey)
+ getSessionId: (data) =>
+ request({
+ url: 'third/wechat/getSessionId',
+ method: 'POST',
+ data,
+ custom: {
+ showLoading: false,
+ },
+ }),
+
+ // 微信小程序 绑定一键获取的手机号
+ bindUserPhoneNumber: (data) =>
+ request({
+ url: 'third/wechat/bindUserPhoneNumber',
+ method: 'POST',
+ data,
+ custom: {
+ showSuccess: true,
+ loadingMsg: '获取中',
+ },
+ }),
+
+ // 网页jssdk
+ jssdk: (data) =>
+ request({
+ url: 'third/wechat/jssdk',
+ method: 'GET',
+ data,
+ custom: {
+ showError: false,
+ showLoading: false,
+ },
+ }),
+
+ // 小程序订阅消息
+ subscribeTemplate: (params) =>
+ request({
+ url: 'third/wechat/subscribeTemplate',
+ method: 'GET',
+ params: {
+ platform: 'miniProgram',
+ },
+ custom: {
+ showError: false,
+ showLoading: false,
+ },
+ }),
+
+ // 获取微信小程序码
+ getWxacode: (path) =>
+ `${baseUrl}${apiPath}third/wechat/wxacode?platform=miniProgram&payload=${encodeURIComponent(
+ JSON.stringify({
+ path,
+ }),
+ )}`,
+ },
+
+ // 苹果相关
+ apple: {
+ // 第三方登录
+ login: (data) =>
+ request({
+ url: 'third/apple/login',
+ method: 'POST',
+ data,
+ custom: {
+ showSuccess: true,
+ loadingMsg: '登陆中',
+ },
+ }),
+ },
+};
diff --git a/sheep/api/trade.js b/sheep/api/trade.js
new file mode 100644
index 00000000..af9c335d
--- /dev/null
+++ b/sheep/api/trade.js
@@ -0,0 +1,37 @@
+import request from '@/sheep/request';
+
+export default {
+ order: (id) =>
+ request({
+ url: 'trade/order/' + id,
+ method: 'GET',
+ custom: {
+ showLoading: false,
+ },
+ }),
+ orderLog: (params) =>
+ request({
+ url: 'trade/order',
+ method: 'GET',
+ params,
+ custom: {
+ showLoading: false,
+ },
+ }),
+
+ rechargeRules: () =>
+ request({
+ url: 'trade/order/rechargeRules',
+ method: 'GET',
+ custom: {
+ showError: false,
+ showLoading: false,
+ },
+ }),
+ recharge: (data) =>
+ request({
+ url: 'trade/order/recharge',
+ method: 'POST',
+ data,
+ }),
+};
diff --git a/sheep/api/user.js b/sheep/api/user.js
new file mode 100644
index 00000000..fb3e809c
--- /dev/null
+++ b/sheep/api/user.js
@@ -0,0 +1,459 @@
+import request from '@/sheep/request';
+import request2 from '@/sheep/request2';
+import $platform from '@/sheep/platform';
+
+export default {
+ getUnused: () =>
+ request2({
+ url: 'promotion/coupon/get-unused-count',
+ method: 'GET',
+ custom: {
+ showLoading: false,
+ auth: true,
+ },
+ }),
+ profile: () =>
+ request2({
+ url: 'member/user/get',
+ method: 'GET',
+ custom: {
+ showLoading: false,
+ auth: true,
+ },
+ }),
+ balance: () =>
+ request2({
+ url: '/app-api/pay/wallet/get',
+ method: 'GET',
+ custom: {
+ showLoading: false,
+ auth: true,
+ },
+ }),
+ // profile: () =>
+ // request({
+ // url: '/user/api/user/profile',
+ // method: 'GET',
+ // custom: {
+ // showLoading: false,
+ // auth: true,
+ // },
+ // }),
+ // update: (data) =>
+ // request({
+ // url: '/user/api/user/update',
+ // method: 'POST',
+ // custom: {
+ // showSuccess: true,
+ // auth: true,
+ // },
+ // data,
+ // }),
+ update: (data) =>
+ request2({
+ url: 'member/user/update',
+ method: 'PUT',
+ custom: {
+ showSuccess: true,
+ auth: true,
+ },
+ data,
+ }),
+ // 账号登录
+ accountLogin: (data) =>
+ request({
+ url: '/user/api/user/accountLogin',
+ method: 'POST',
+ data,
+ custom: {
+ showSuccess: true,
+ loadingMsg: '登录中',
+ },
+ }),
+ // 短信登录
+ smsLogin: (data) =>
+ request({
+ url: '/user/api/user/smsLogin',
+ method: 'POST',
+ data,
+ custom: {
+ showSuccess: true,
+ loadingMsg: '登录中',
+ },
+ }),
+ // 短信注册
+ smsRegister: (data) =>
+ request({
+ url: '/user/api/user/smsRegister',
+ method: 'POST',
+ data,
+ custom: {
+ showSuccess: true,
+ loadingMsg: '正在注册',
+ },
+ }),
+ // 重置密码
+ resetPassword: (data) =>
+ request({
+ url: '/user/api/user/resetPassword',
+ method: 'POST',
+ data,
+ custom: {
+ showSuccess: true,
+ loadingMsg: '验证中',
+ },
+ }),
+
+ // 修改密码
+ changePassword: (data) =>
+ request({
+ url: '/user/api/user/changePassword',
+ method: 'POST',
+ data,
+ custom: {
+ showSuccess: true,
+ loadingMsg: '验证中',
+ },
+ }),
+
+ // 绑定、更换手机号
+ changeMobile: (data) =>
+ request({
+ url: '/user/api/user/changeMobile',
+ method: 'POST',
+ data,
+ custom: {
+ showSuccess: true,
+ loadingMsg: '验证中',
+ },
+ }),
+
+ // 修改用户名
+ changeUsername: (data) =>
+ request({
+ url: '/user/api/user/changeUsername',
+ method: 'POST',
+ data,
+ custom: {
+ showSuccess: true,
+ loadingMsg: '验证中',
+ },
+ }),
+
+ // 更新小程序信息
+ updateMpUserInfo: (data) =>
+ request({
+ url: '/user/api/user/updateMpUserInfo',
+ method: 'POST',
+ data,
+ }),
+
+ // 第三方授权信息
+ thirdOauthInfo: () =>
+ request({
+ url: '/user/api/user/thirdOauth',
+ method: 'GET',
+ params: {
+ provider: $platform.provider,
+ platform: $platform.platform,
+ },
+ custom: {
+ showLoading: false,
+ },
+ }),
+
+ // 添加分享记录
+ addShareLog: (data) =>
+ request({
+ url: 'share/add',
+ method: 'POST',
+ data,
+ custom: {
+ showError: false,
+ },
+ }),
+ share: {
+ list: (params) =>
+ request({
+ url: 'share/list',
+ method: 'GET',
+ params,
+ }),
+ },
+ // 账号登出
+ logout: (data) =>
+ request({
+ url: '/user/api/user/logout',
+ method: 'POST',
+ data,
+ }),
+ // 账号注销
+ logoff: (data) =>
+ request({
+ url: '/user/api/user/logoff',
+ method: 'POST',
+ data,
+ }),
+
+ address: {
+ // default: () =>
+ // request({
+ // url: 'user/address/default',
+ // method: 'GET',
+ // custom: {
+ // showError: false,
+ // },
+ // }),
+ default: () =>
+ request2({
+ url: 'member/address/get-default',
+ method: 'GET',
+ custom: {
+ showError: false,
+ },
+ }),
+ list: () =>
+ request2({
+ url: 'member/address/list',
+ method: 'GET',
+ custom: {},
+ }),
+ // list: () =>
+ // request({
+ // url: 'user/address',
+ // method: 'GET',
+ // custom: {},
+ // }),
+ create: (data) =>
+ request2({
+ url: 'member/address/create',
+ method: 'POST',
+ data,
+ custom: {
+ showSuccess: true,
+ },
+ }),
+ // create: (data) =>
+ // request({
+ // url: 'user/address',
+ // method: 'POST',
+ // data,
+ // custom: {
+ // showSuccess: true,
+ // },
+ // }),
+ update: (data) =>
+ request2({
+ url: 'member/address/update',
+ method: 'PUT',
+ data,
+ custom: {
+ showSuccess: true,
+ },
+ }),
+ // update: (id, data) =>
+ // request({
+ // url: 'user/address/' + id,
+ // method: 'PUT',
+ // data,
+ // custom: {
+ // showSuccess: true,
+ // },
+ // }),
+ detail: (id) =>
+ request2({
+ url: 'member/address/get?id=' + id,
+ method: 'GET',
+ }),
+ // detail: (id) =>
+ // request({
+ // url: 'user/address/' + id,
+ // method: 'GET',
+ // }),
+ delete: (id) =>
+ request2({
+ url: 'member/address/delete?id=' + id,
+ method: 'DELETE',
+ }),
+ // delete: (id) =>
+ // request({
+ // url: 'user/address/' + id,
+ // method: 'DELETE',
+ // }),
+ },
+ invoice: {
+ list: () =>
+ request({
+ url: 'user/invoice',
+ method: 'GET',
+ custom: {},
+ }),
+ create: (data) =>
+ request({
+ url: 'user/invoice',
+ method: 'POST',
+ data,
+ custom: {
+ showSuccess: true,
+ },
+ }),
+ update: (id, data) =>
+ request({
+ url: 'user/invoice/' + id,
+ method: 'PUT',
+ data,
+ custom: {
+ showSuccess: true,
+ },
+ }),
+ detail: (id) =>
+ request({
+ url: 'user/invoice/' + id,
+ method: 'GET',
+ }),
+ delete: (id) =>
+ request({
+ url: 'user/invoice/' + id,
+ method: 'DELETE',
+ }),
+ },
+ favorite: {
+ list: (params) =>
+ request2({
+ url: 'product/favorite/page',
+ method: 'GET',
+ params,
+ }),
+ do: (id) =>
+ request({
+ url: 'user/goodsLog/favorite',
+ method: 'POST',
+ data: {
+ goods_id: id,
+ },
+ custom: {
+ showSuccess: true,
+ auth: true,
+ },
+ }),
+ // 取消收藏
+ cancel: (id) =>
+ request2({
+ url: 'product/favorite/delete-list',
+ method: 'DELETE',
+ data: {
+ spuIds: id.split(',').map(item => item * 1),
+ // spuIds: id.split(',').join(','),
+ },
+ custom: {
+ showSuccess: true,
+ auth: true,
+ },
+ }),
+ // cancel: (id) =>
+ // request({
+ // url: 'user/goodsLog/favorite',
+ // method: 'POST',
+ // data: {
+ // goods_ids: id,
+ // },
+ // custom: {
+ // showSuccess: true,
+ // auth: true,
+ // },
+ // }),
+ },
+ view: {
+ list: (params) =>
+ request({
+ url: 'user/goodsLog/views',
+ method: 'GET',
+ params,
+ custom: {},
+ }),
+ delete: (data) =>
+ request({
+ url: 'user/goodsLog/viewDel',
+ method: 'DELETE',
+ data,
+ custom: {
+ showSuccess: true,
+ },
+ }),
+ },
+ wallet: {
+ log: (params) =>
+ request2({
+ // url: 'member/point/record/page',
+ url: 'pay/wallet-transaction/page',
+ method: 'GET',
+ params,
+ custom: {},
+ }),
+ log2: (params) =>
+ request2({
+ url: 'member/point/record/page',
+ // url: 'pay/wallet-transaction/page',
+ method: 'GET',
+ params,
+ custom: {},
+ }),
+ // log: (params) =>
+ // request({
+ // url: '/user/api/walletLog',
+ // method: 'GET',
+ // params,
+ // custom: {},
+ // }),
+ },
+ account: {
+ info: (params) =>
+ request({
+ url: 'user/account',
+ method: 'GET',
+ params,
+ custom: {
+ showError: false,
+ auth: true,
+ },
+ }),
+ save: (data) =>
+ request({
+ url: 'user/account',
+ method: 'POST',
+ data,
+ custom: {
+ showSuccess: true,
+ auth: true,
+ },
+ }),
+ },
+ //数量接口
+ // data: () =>
+ // request({
+ // url: 'user/user/data',
+ // method: 'GET',
+ // custom: {
+ // showLoading: false,
+ // auth: true,
+ // },
+ // }),
+ data: () =>
+ request2({
+ url: 'trade/order/get-count',
+ method: 'GET',
+ custom: {
+ showLoading: false,
+ auth: true,
+ },
+ }),
+ data2: () =>
+ request2({
+ url: 'trade/after-sale/get-applying-count',
+ method: 'GET',
+ custom: {
+ showLoading: false,
+ auth: true,
+ },
+ }),
+};
\ No newline at end of file
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 00000000..551053db
--- /dev/null
+++ b/sheep/components/s-activity-pop/s-activity-pop.vue
@@ -0,0 +1,96 @@
+
+
+
+ 营销活动
+
+
+
+
+ {{ item.type_text }}
+
+
+ {{ text }}
+
+
+
+
+
+
+
+
+
+
+
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 00000000..37c2a9aa
--- /dev/null
+++ b/sheep/components/s-address-item/s-address-item.vue
@@ -0,0 +1,106 @@
+
+
+
+
+
+ {{ props.item.areaName }}
+
+
+ {{ props.item.detailAddress }}
+ {{ props.item.name }} {{ props.item.mobile }}
+
+ 请选择收货地址
+
+
+
+
+
+
+
+
+
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 00000000..7663204b
--- /dev/null
+++ b/sheep/components/s-auth-modal/components/account-login.vue
@@ -0,0 +1,107 @@
+
+
+
+
+
+
+ 账号登录
+
+ 短信登录
+
+
+ 如果未设置过密码,请点击忘记密码
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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 00000000..2241173d
--- /dev/null
+++ b/sheep/components/s-auth-modal/components/change-mobile.vue
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+ {{ userInfo.verification.mobile ? '更换手机号' : '绑定手机号' }}
+
+ 为了您的账号安全,请使用本人手机号码
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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 00000000..0e0af5cc
--- /dev/null
+++ b/sheep/components/s-auth-modal/components/change-password.vue
@@ -0,0 +1,121 @@
+
+
+
+
+
+ 修改密码
+ 如密码丢失或未设置,请点击忘记密码重新设置
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-auth-modal/components/change-username.vue b/sheep/components/s-auth-modal/components/change-username.vue
new file mode 100644
index 00000000..82d3de95
--- /dev/null
+++ b/sheep/components/s-auth-modal/components/change-username.vue
@@ -0,0 +1,72 @@
+
+
+
+
+
+ 修改用户名
+ 用户名仅限修改一次
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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 00000000..7c35144d
--- /dev/null
+++ b/sheep/components/s-auth-modal/components/mp-authorization.vue
@@ -0,0 +1,145 @@
+
+
+
+
+
+
+ 授权信息
+
+ 完善您的头像、昵称、手机号
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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 00000000..81858ad9
--- /dev/null
+++ b/sheep/components/s-auth-modal/components/reset-password.vue
@@ -0,0 +1,114 @@
+
+
+
+
+
+ 重置密码
+ 为了您的账号安全,设置密码前请先进行安全验证
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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 00000000..567866c5
--- /dev/null
+++ b/sheep/components/s-auth-modal/components/sms-login.vue
@@ -0,0 +1,115 @@
+
+
+
+
+
+
+ 账号登录
+ 短信登录
+
+ 未注册手机号请先点击下方立即注册
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-auth-modal/components/sms-register.vue b/sheep/components/s-auth-modal/components/sms-register.vue
new file mode 100644
index 00000000..648102a2
--- /dev/null
+++ b/sheep/components/s-auth-modal/components/sms-register.vue
@@ -0,0 +1,130 @@
+
+
+
+
+
+ 注册
+ 请使用本人手机号完成注册
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-auth-modal/index.scss b/sheep/components/s-auth-modal/index.scss
new file mode 100644
index 00000000..c4424e73
--- /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 00000000..ab3ef690
--- /dev/null
+++ b/sheep/components/s-auth-modal/s-auth-modal.vue
@@ -0,0 +1,263 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 还没有账号?
+
+ 或
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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 00000000..9821ce23
--- /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 00000000..19638fca
--- /dev/null
+++ b/sheep/components/s-block/s-block.vue
@@ -0,0 +1,53 @@
+
+
+
+
+
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 00000000..af29c097
--- /dev/null
+++ b/sheep/components/s-coupon-block/s-coupon-block.vue
@@ -0,0 +1,151 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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 00000000..5be18c85
--- /dev/null
+++ b/sheep/components/s-coupon-card/s-coupon-card.vue
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
\ 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 00000000..bfa9b9bb
--- /dev/null
+++ b/sheep/components/s-coupon-get/s-coupon-get.vue
@@ -0,0 +1,108 @@
+
+
+
+ 优惠券
+
+ 可使用优惠券
+
+
+
+
+
+
+
+
+
+
+
+
+
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 00000000..648b32da
--- /dev/null
+++ b/sheep/components/s-coupon-list/s-coupon-list.vue
@@ -0,0 +1,195 @@
+
+
+
+
+
+
+ {{ data.name }}
+
+
+ ¥
+ {{ data.amount }}
+ 折
+
+
+
+
+
+ {{'有效期:' + data.use_start_time.substring(0, 11) }}至
+ {{ data.use_end_time.substring(0, 11) }}
+
+
+ 满{{ data.enough }}可用
+
+
+
+
+
+
+
+ {{ data.description }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
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 00000000..928f903a
--- /dev/null
+++ b/sheep/components/s-coupon-select/s-coupon-select.vue
@@ -0,0 +1,128 @@
+
+
+
+ 优惠券
+
+ 可使用优惠券
+
+
+
+
+
+
+
+ 不可使用优惠券
+
+
+
+
+ 不可用原因:
+ {{ item.cannot_use_msg }}
+
+
+
+
+
+
+
+
+
+
+
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 00000000..b8431a0f
--- /dev/null
+++ b/sheep/components/s-custom-navbar/components/navbar-item.vue
@@ -0,0 +1,62 @@
+
+
+
+ {{ data.text }}
+
+
+
+
+
+
+
+
+
+
+
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 00000000..36050ec4
--- /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 00000000..789d2d29
--- /dev/null
+++ b/sheep/components/s-custom-navbar/s-custom-navbar.vue
@@ -0,0 +1,195 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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 00000000..22fbd43c
--- /dev/null
+++ b/sheep/components/s-discount-list/s-discount-list.vue
@@ -0,0 +1,114 @@
+
+
+
+ 活动优惠
+
+
+
+ 共{{ item.goods_ids.length }}件,
+
+ 满{{ item.discount_rule.full }}打{{ item.discount_rule.discount }}折,已减
+
+ 满赠
+
+ 满{{ item.discount_rule.full }}减{{ item.discount_rule.discount }},已减
+
+ ¥{{ item.promo_discount_money || '0.00' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-empty/s-empty.vue b/sheep/components/s-empty/s-empty.vue
new file mode 100644
index 00000000..27f8c4a8
--- /dev/null
+++ b/sheep/components/s-empty/s-empty.vue
@@ -0,0 +1,93 @@
+
+
+
+
+ {{ text }}
+
+
+
+
+
+
+
+
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 00000000..bed65138
--- /dev/null
+++ b/sheep/components/s-float-menu/s-float-menu.vue
@@ -0,0 +1,99 @@
+
+
+
+
+
+
+
+
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 00000000..b51706f7
--- /dev/null
+++ b/sheep/components/s-goods-card/s-goods-card.vue
@@ -0,0 +1,285 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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 00000000..40a9cec9
--- /dev/null
+++ b/sheep/components/s-goods-column/s-goods-column.vue
@@ -0,0 +1,729 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{ data.title || data.name }}
+
+
+ {{ priceUnit }}
+ {{ isArray(data.price) ? data.price[0] : data.price }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ data.title || data.name }}
+
+
+ {{ priceUnit }}
+ {{ isArray(data.price) ? data.price[0] : data.price }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ data.title || data.name }}
+
+
+ {{ data.subtitle || data.introduction }}
+
+
+
+
+ {{ item.title }}
+
+
+
+
+
+ {{ priceUnit }}
+ {{ isArray(data.price) ? data.price[0] : data.price }}
+
+
+
+ {{ priceUnit }}
+ {{ data.original_price||data.marketPrice }}
+
+
+
+
+ {{ salesAndStock }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 秒杀
+
+ 拼团
+
+
+
+
+
+ {{ data.title || data.name }}
+
+
+ {{ data.subtitle || data.introduction }}
+
+
+
+
+
+
+ {{ item.title }}
+
+
+
+
+
+ {{ priceUnit }}
+ {{ isArray(data.price) ? data.price[0] : data.price }}
+
+
+ {{ priceUnit }}
+ {{ data.original_price||data.marketPrice }}
+
+
+
+ {{ salesAndStock }}
+
+
+
+
+ 去购买
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ data.title || data.name }}
+
+
+ {{ data.subtitle || data.introduction }}
+
+
+
+
+
+
+ {{ item.title }}
+
+
+
+
+
+ {{ priceUnit }}
+ {{ isArray(data.price) ? data.price[0] : data.price }}
+
+
+ {{ priceUnit }}
+ {{ data.original_price||data.marketPrice }}
+
+
+
+ {{ salesAndStock }}
+
+
+
+
+ 去购买
+
+
+
+
+
+
+
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 00000000..d599f28e
--- /dev/null
+++ b/sheep/components/s-goods-item/s-goods-item.vue
@@ -0,0 +1,188 @@
+
+
+
+
+
+
+
+
+
+
+ {{ title }}
+ {{ skuString }}
+
+
+
+
+
+
+ ¥{{ price }}
+
+ +
+
+
+ {{ score }}
+
+ x {{ num }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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 00000000..08c12d42
--- /dev/null
+++ b/sheep/components/s-goods-scroll/s-goods-scroll.vue
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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 00000000..01129a33
--- /dev/null
+++ b/sheep/components/s-goods-shelves/s-goods-shelves.vue
@@ -0,0 +1,146 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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 00000000..7ea1c8fd
--- /dev/null
+++ b/sheep/components/s-groupon-block/s-groupon-block.vue
@@ -0,0 +1,156 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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 00000000..1ce25af8
--- /dev/null
+++ b/sheep/components/s-hotzone-block/s-hotzone-block.vue
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
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 00000000..64feffd4
--- /dev/null
+++ b/sheep/components/s-image-banner/s-image-banner.vue
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
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 00000000..ff9348ca
--- /dev/null
+++ b/sheep/components/s-image-block/s-image-block.vue
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
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 00000000..a56669fa
--- /dev/null
+++ b/sheep/components/s-image-cube/s-image-cube.vue
@@ -0,0 +1,109 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-invoice-item/s-invoice-item.vue b/sheep/components/s-invoice-item/s-invoice-item.vue
new file mode 100644
index 00000000..3b4e7d9a
--- /dev/null
+++ b/sheep/components/s-invoice-item/s-invoice-item.vue
@@ -0,0 +1,96 @@
+
+
+
+ {{ props.item.name }}
+ {{ props.item.mobile }}
+
+
+ 请选择收货地址
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-layout/s-layout.vue b/sheep/components/s-layout/s-layout.vue
new file mode 100644
index 00000000..914e7a31
--- /dev/null
+++ b/sheep/components/s-layout/s-layout.vue
@@ -0,0 +1,242 @@
+
+
+
+
+ emits('search', e)"
+ :defaultSearch="defaultSearch"
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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 00000000..97e14f59
--- /dev/null
+++ b/sheep/components/s-line-block/s-line-block.vue
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
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 00000000..9d1ad032
--- /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 00000000..9cefee04
--- /dev/null
+++ b/sheep/components/s-live-card/s-live-card.vue
@@ -0,0 +1,234 @@
+
+
+
+
+
+
+ {{ state.liveStatus[data.status].title }}
+
+
+
+
+ {{ data.name }}
+
+
+ 主播:{{ data.anchor_name }}
+
+
+
+
+
+
+
+ {{ state.liveStatus[data.status].title }}
+
+
+
+
+ {{ data.name }}
+
+
+ 主播:{{ data.anchor_name }}
+
+
+
+
+
+
+
+
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 00000000..50886d0e
--- /dev/null
+++ b/sheep/components/s-menu-button/s-menu-button.vue
@@ -0,0 +1,362 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ state.cur + 1 }} / {{ menuList.length }}
+
+
+
+
+
+
+
+
+
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 00000000..3cf62485
--- /dev/null
+++ b/sheep/components/s-menu-grid/s-menu-grid.vue
@@ -0,0 +1,81 @@
+
+
+
+
+
+
+ {{ item.badge.text }}
+
+
+
+
+
+
+ {{ item.title }}
+
+
+ {{ item.subtitle }}
+
+
+
+
+
+
+
+
+
+
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 00000000..652df903
--- /dev/null
+++ b/sheep/components/s-menu-list/s-menu-list.vue
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
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 00000000..8ad0d05e
--- /dev/null
+++ b/sheep/components/s-menu-tools/s-menu-tools.vue
@@ -0,0 +1,122 @@
+
+
+
+
+ 快捷菜单
+
+
+
+
+ {{ item.title }}
+
+
+
+
+
+
+
+
+
+
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 00000000..c0c76655
--- /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 00000000..8ed36473
--- /dev/null
+++ b/sheep/components/s-order-card/s-order-card.vue
@@ -0,0 +1,104 @@
+
+
+
+
+
+
+
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 00000000..b10f4777
--- /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 00000000..86f801db
--- /dev/null
+++ b/sheep/components/s-richtext-block/s-richtext-block.vue
@@ -0,0 +1,36 @@
+
+
+
+
+
+
diff --git a/sheep/components/s-score-block/s-score-block.vue b/sheep/components/s-score-block/s-score-block.vue
new file mode 100644
index 00000000..569f5d28
--- /dev/null
+++ b/sheep/components/s-score-block/s-score-block.vue
@@ -0,0 +1,202 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-score-card/s-score-card.vue b/sheep/components/s-score-card/s-score-card.vue
new file mode 100644
index 00000000..ae2fd59b
--- /dev/null
+++ b/sheep/components/s-score-card/s-score-card.vue
@@ -0,0 +1,459 @@
+
+
+
+
+
+
+
+ {{ data.title }}
+
+
+ {{ data.subtitle }}
+
+
+
+ {{ Number(data.price[0]) > 0 ? '¥' + data.price[0] + '+' : '' }}
+
+ {{ data.score }}
+
+
+
+ {{ priceUnit }}
+ {{ data.original_price }}
+
+
+
+
+ {{ salesAndStock }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ data.title }}
+
+
+ {{ data.subtitle }}
+
+
+
+
+
+ {{ Number(data.price[0]) > 0 ? '¥' + data.price[0] + '+' : '' }}
+
+ {{ data.score }}
+
+
+ {{ priceUnit }}
+ {{ data.original_price }}
+
+
+
+ {{ salesAndStock }}
+
+
+
+
+ 去兑换
+
+
+
+
+
+
+
+
+ {{ data.title }}
+
+
+ {{ data.subtitle }}
+
+
+
+
+
+
+ {{ item.title }}
+
+
+
+
+
+ {{ Number(data.price[0]) > 0 ? '¥' + data.price[0] + '+' : '' }}
+
+ {{ data.score ? data.score : '' }}
+
+
+ ¥
+ {{ data.original_price }}
+
+
+
+ {{ salesAndStock }}
+
+
+
+
+ 去兑换
+
+
+
+
+
+
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 00000000..1e9c2b51
--- /dev/null
+++ b/sheep/components/s-search-block/s-search-block.vue
@@ -0,0 +1,164 @@
+
+
+
+
+
+ {{ placeholder }}
+
+
+
+
+
+ {{ item }}
+
+
+
+
+
+
+
+
+
+
+
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 00000000..5b317257
--- /dev/null
+++ b/sheep/components/s-seckill-block/s-seckill-block.vue
@@ -0,0 +1,162 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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 00000000..9eb94bb5
--- /dev/null
+++ b/sheep/components/s-select-groupon-sku/s-select-groupon-sku.vue
@@ -0,0 +1,598 @@
+
+
+
+
+
+
+
+
+ 拼团人数
+
+
+
+
+
+ {{ sku1.name }}
+
+
+
+
+
+ 购买数量
+
+
+
+
+
+
+
+
+
+
+
+
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 00000000..d339120d
--- /dev/null
+++ b/sheep/components/s-select-seckill-sku/s-select-seckill-sku.vue
@@ -0,0 +1,440 @@
+
+
+
+
+
+
+
+
+ {{ sku1.name }}
+
+
+
+
+
+ 购买数量
+
+
+
+
+
+
+
+
+
+
+
+
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 00000000..b339d4a1
--- /dev/null
+++ b/sheep/components/s-select-sku/s-select-sku.vue
@@ -0,0 +1,406 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{ property.name }}
+
+
+
+
+
+ 购买数量
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
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 00000000..5c453663
--- /dev/null
+++ b/sheep/components/s-share-modal/canvas-poster/index.vue
@@ -0,0 +1,157 @@
+
+
+
+
+
+
+
+
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 00000000..c11000a0
--- /dev/null
+++ b/sheep/components/s-share-modal/canvas-poster/poster/goods.js
@@ -0,0 +1,121 @@
+import sheep from '@/sheep';
+import { formatImageUrlProtocol } from './index';
+
+const goods = (poster) => {
+ const width = poster.width;
+ const userInfo = sheep.$store('user').userInfo;
+
+ return {
+ background: formatImageUrlProtocol(sheep.$url.cdn(sheep.$store('app').platform.share.posterInfo.goods_bg)),
+ list: [
+ {
+ name: 'nickname',
+ type: 'text',
+ val: userInfo.nickname,
+ x: width * 0.22,
+ y: width * 0.06,
+ paintbrushProps: {
+ fillStyle: '#333',
+ font: {
+ fontSize: 16,
+ fontFamily: 'sans-serif',
+ },
+ },
+ },
+ {
+ name: 'avatar',
+ type: 'image',
+ val: formatImageUrlProtocol(sheep.$url.cdn(userInfo.avatar)),
+ x: width * 0.04,
+ y: width * 0.04,
+ width: width * 0.14,
+ height: width * 0.14,
+ d: width * 0.14,
+ },
+ {
+ name: 'goodsImage',
+ type: 'image',
+ val: formatImageUrlProtocol(poster.shareInfo.poster.image),
+ x: width * 0.03,
+ y: width * 0.21,
+ width: width * 0.94,
+ height: width * 0.94,
+ r: 10,
+ },
+ {
+ name: 'goodsTitle',
+ type: 'text',
+ val: poster.shareInfo.poster.title,
+ x: width * 0.04,
+ y: width * 1.18,
+ maxWidth: width * 0.91,
+ line: 2,
+ lineHeight: 5,
+ paintbrushProps: {
+ fillStyle: '#333',
+ font: {
+ fontSize: 14,
+ },
+ },
+ },
+ {
+ name: 'goodsPrice',
+ type: 'text',
+ val: '¥' + poster.shareInfo.poster.price,
+ x: width * 0.04,
+ y: width * 1.3,
+ paintbrushProps: {
+ fillStyle: '#ff0000',
+ font: {
+ fontSize: 20,
+ fontFamily: 'OPPOSANS',
+ },
+ },
+ },
+ {
+ name: 'goodsOriginalPrice',
+ type: 'text',
+ val:
+ poster.shareInfo.poster.original_price > 0
+ ? '¥' + poster.shareInfo.poster.original_price
+ : '',
+ x: width * 0.3,
+ y: width * 1.32,
+ paintbrushProps: {
+ fillStyle: '#999',
+ font: {
+ fontSize: 10,
+ fontFamily: 'OPPOSANS',
+ },
+ },
+ textDecoration: {
+ line: 'line-through',
+ style: 'solide',
+ },
+ },
+ // #ifndef MP-WEIXIN
+ {
+ name: 'qrcode',
+ type: 'qrcode',
+ val: poster.shareInfo.link,
+ x: width * 0.75,
+ y: width * 1.3,
+ size: width * 0.2,
+ },
+ // #endif
+ // #ifdef MP-WEIXIN
+ {
+ name: 'wxacode',
+ type: 'image',
+ val: sheep.$api.third.wechat.getWxacode(poster.shareInfo.path),
+ x: width * 0.75,
+ y: 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 00000000..33d097e9
--- /dev/null
+++ b/sheep/components/s-share-modal/canvas-poster/poster/groupon.js
@@ -0,0 +1,114 @@
+import sheep from '@/sheep';
+import { formatImageUrlProtocol } from './index';
+
+const groupon = (poster) => {
+ const width = poster.width;
+ const userInfo = sheep.$store('user').userInfo;
+
+ return {
+ background: formatImageUrlProtocol(sheep.$url.cdn(sheep.$store('app').platform.share.posterInfo.groupon_bg)),
+ list: [
+ {
+ name: 'nickname',
+ type: 'text',
+ val: userInfo.nickname,
+ x: width * 0.22,
+ y: width * 0.06,
+ paintbrushProps: {
+ fillStyle: '#333',
+ font: {
+ fontSize: 16,
+ fontFamily: 'sans-serif',
+ },
+ },
+ },
+ {
+ name: 'avatar',
+ type: 'image',
+ val: formatImageUrlProtocol(sheep.$url.cdn(userInfo.avatar)),
+ x: width * 0.04,
+ y: width * 0.04,
+ width: width * 0.14,
+ height: width * 0.14,
+ d: width * 0.14,
+ },
+ {
+ name: 'goodsImage',
+ type: 'image',
+ val: formatImageUrlProtocol(poster.shareInfo.poster.image),
+ x: width * 0.03,
+ y: width * 0.21,
+ width: width * 0.94,
+ height: width * 0.94,
+ r: 10,
+ },
+ {
+ name: 'goodsTitle',
+ type: 'text',
+ val: poster.shareInfo.poster.title,
+ x: width * 0.04,
+ y: width * 1.18,
+ maxWidth: width * 0.91,
+ line: 2,
+ lineHeight: 5,
+ paintbrushProps: {
+ fillStyle: '#333',
+ font: {
+ fontSize: 14,
+ },
+ },
+ },
+ {
+ name: 'goodsPrice',
+ type: 'text',
+ val: '¥' + poster.shareInfo.poster.price,
+ x: width * 0.04,
+ y: width * 1.3,
+ paintbrushProps: {
+ fillStyle: '#ff0000',
+ font: {
+ fontSize: 20,
+ fontFamily: 'OPPOSANS',
+ },
+ },
+ },
+ {
+ name: 'grouponNum',
+ type: 'text',
+ val: '2人团',
+ x: width * 0.3,
+ y: width * 1.32,
+ paintbrushProps: {
+ fillStyle: '#ff0000',
+ font: {
+ fontSize: 10,
+ fontFamily: 'OPPOSANS',
+ },
+ },
+ },
+ // #ifndef MP-WEIXIN
+ {
+ name: 'qrcode',
+ type: 'qrcode',
+ val: poster.shareInfo.link,
+ x: width * 0.75,
+ y: width * 1.3,
+ size: width * 0.2,
+ },
+ // #endif
+ // #ifdef MP-WEIXIN
+ {
+ name: 'wxacode',
+ type: 'image',
+ val: sheep.$api.third.wechat.getWxacode(poster.shareInfo.path),
+ x: width * 0.75,
+ y: width * 1.3,
+ 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 00000000..a547b1e8
--- /dev/null
+++ b/sheep/components/s-share-modal/canvas-poster/poster/index.js
@@ -0,0 +1,32 @@
+import user from './user';
+import goods from './goods';
+import groupon from './groupon';
+
+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;
+}
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 00000000..cf927184
--- /dev/null
+++ b/sheep/components/s-share-modal/canvas-poster/poster/user.js
@@ -0,0 +1,61 @@
+import sheep from '@/sheep';
+import { formatImageUrlProtocol } from './index';
+
+const user = (poster) => {
+ const width = poster.width;
+ const userInfo = sheep.$store('user').userInfo;
+
+ return {
+ background: formatImageUrlProtocol(sheep.$url.cdn(sheep.$store('app').platform.share.posterInfo.user_bg)),
+ list: [
+ {
+ name: 'nickname',
+ type: 'text',
+ val: userInfo.nickname,
+ x: width / 2,
+ y: width * 0.4,
+ paintbrushProps: {
+ textAlign: 'center',
+ fillStyle: '#333',
+ font: {
+ fontSize: 14,
+ fontFamily: 'sans-serif',
+ },
+ },
+ },
+ {
+ name: 'avatar',
+ type: 'image',
+ val: formatImageUrlProtocol(sheep.$url.cdn(userInfo.avatar)),
+ x: width * 0.4,
+ y: width * 0.16,
+ width: width * 0.2,
+ height: width * 0.2,
+ d: width * 0.2,
+ },
+ // #ifndef MP-WEIXIN
+ {
+ name: 'qrcode',
+ type: 'qrcode',
+ val: poster.shareInfo.link,
+ x: width * 0.35,
+ y: width * 0.84,
+ size: width * 0.3,
+ },
+ // #endif
+ // #ifdef MP-WEIXIN
+ {
+ name: 'wxacode',
+ type: 'image',
+ val: sheep.$api.third.wechat.getWxacode(poster.shareInfo.path),
+ x: width * 0.35,
+ y: width * 0.84,
+ width: width * 0.3,
+ height: width * 0.3,
+ },
+ // #endif
+ ],
+ };
+};
+
+export default user;
diff --git a/sheep/components/s-share-modal/canvas-poster/useCanvas.js b/sheep/components/s-share-modal/canvas-poster/useCanvas.js
new file mode 100644
index 00000000..cd779472
--- /dev/null
+++ b/sheep/components/s-share-modal/canvas-poster/useCanvas.js
@@ -0,0 +1,89 @@
+/**
+ * Shopro + qs-canvas 绘制海报
+ * @version 1.0.0
+ * @author lidongtony
+ * @param {Object} options - 海报参数
+ * @param {Object} vm - 自定义组件实例
+ */
+
+import sheep from '@/sheep';
+import QSCanvas from 'qs-canvas';
+import { getPosterData } from './poster';
+
+export default async function useCanvas(options, vm) {
+ const width = options.width;
+ const qsc = new QSCanvas(
+ {
+ canvasId: options.canvasId,
+ width: options.width,
+ height: options.height,
+ setCanvasWH: (canvas) => {
+ options.height = canvas.height;
+ },
+ },
+ vm,
+ );
+
+ let drawer = getPosterData(options);
+
+ // 绘制背景图
+ const background = await qsc.drawImg({
+ type: 'image',
+ val: drawer.background,
+ x: 0,
+ y: 0,
+ width,
+ mode: 'widthFix',
+ zIndex: 0,
+ });
+ await qsc.updateCanvasWH({
+ width: background.width,
+ height: background.bottom,
+ });
+
+ let list = drawer.list;
+
+ for (let i = 0; i < list.length; i++) {
+ let item = list[i];
+ // 绘制文字
+ if (item.type === 'text') {
+ await qsc.drawText(item);
+ }
+ // 绘制图片
+ if (item.type === 'image') {
+ if (item.d) {
+ qsc.setCircle({
+ x: item.x,
+ y: item.y,
+ d: item.d,
+ clip: true,
+ });
+ }
+
+ if (item.r) {
+ qsc.setRect({
+ x: item.x,
+ y: item.y,
+ height: item.height,
+ width: item.width,
+ r: item.r,
+ clip: true,
+ });
+ }
+ await qsc.drawImg(item);
+ qsc.restore();
+ }
+
+ // 绘制二维码
+ if (item.type === 'qrcode') {
+ await qsc.drawQrCode(item);
+ }
+ }
+
+ await qsc.draw();
+ // 延迟执行, 防止不稳定
+ setTimeout(async () => {
+ options.src = await qsc.toImage();
+ }, 100);
+ return options;
+}
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 00000000..83a4a27d
--- /dev/null
+++ b/sheep/components/s-share-modal/s-share-modal.vue
@@ -0,0 +1,201 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-statusbar/s-statusbar.vue b/sheep/components/s-statusbar/s-statusbar.vue
new file mode 100644
index 00000000..97287206
--- /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 00000000..369a8232
--- /dev/null
+++ b/sheep/components/s-tabbar/s-tabbar.vue
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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 00000000..e38e8452
--- /dev/null
+++ b/sheep/components/s-title-block/s-title-block.vue
@@ -0,0 +1,114 @@
+
+
+
+
+ {{
+ data.title.text
+ }}
+
+ {{ data.subtitle.text }}
+
+
+
+ 查看更多
+
+
+
+
+
+
+
+
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 00000000..16db0b4a
--- /dev/null
+++ b/sheep/components/s-uploader/choose-and-upload-file.js
@@ -0,0 +1,213 @@
+'use strict';
+import sheep from '@/sheep';
+
+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;
+}
+
+function uploadCloudFiles(files, max = 5, onUploadProgress) {
+ files = JSON.parse(JSON.stringify(files));
+ const len = files.length;
+ let count = 0;
+ let self = this;
+ return new Promise((resolve) => {
+ while (count < max) {
+ next();
+ }
+
+ function next() {
+ let cur = count++;
+ if (cur >= len) {
+ !files.find((item) => !item.url && !item.errMsg) && resolve(files);
+ return;
+ }
+ const fileItem = files[cur];
+ const index = self.files.findIndex((v) => v.uuid === fileItem.uuid);
+ fileItem.url = '';
+ delete fileItem.errMsg;
+
+ uniCloud
+ .uploadFile({
+ filePath: fileItem.path,
+ cloudPath: fileItem.cloudPath,
+ fileType: fileItem.fileType,
+ onUploadProgress: (res) => {
+ res.index = index;
+ onUploadProgress && onUploadProgress(res);
+ },
+ })
+ .then((res) => {
+ fileItem.url = res.fileID;
+ fileItem.index = index;
+ if (cur < len) {
+ next();
+ }
+ })
+ .catch((res) => {
+ fileItem.errMsg = res.errMsg || res.message;
+ fileItem.index = index;
+ if (cur < len) {
+ next();
+ }
+ });
+ }
+ });
+}
+
+function uploadFiles(choosePromise, { onChooseFile, onUploadProgress }) {
+ return choosePromise
+ .then((res) => {
+ if (onChooseFile) {
+ const customChooseRes = onChooseFile(res);
+ if (typeof customChooseRes !== 'undefined') {
+ return Promise.resolve(customChooseRes).then((chooseRes) =>
+ typeof chooseRes === 'undefined' ? res : chooseRes,
+ );
+ }
+ }
+ return res;
+ })
+ .then((res) => {
+ if (res === false) {
+ return {
+ errMsg: ERR_MSG_OK,
+ tempFilePaths: [],
+ tempFiles: [],
+ };
+ }
+ return res;
+ })
+ .then(async (files) => {
+ for (let file of files.tempFiles) {
+ let { path } = await sheep.$api.app.upload(file.path, 'ugc');
+ file.url = path;
+ }
+ return files;
+ });
+}
+
+function chooseAndUploadFile(
+ opts = {
+ type: 'all',
+ },
+) {
+ if (opts.type === 'image') {
+ return uploadFiles(chooseImage(opts), opts);
+ } else if (opts.type === 'video') {
+ return uploadFiles(chooseVideo(opts), opts);
+ }
+ return uploadFiles(chooseAll(opts), opts);
+}
+
+export { chooseAndUploadFile, uploadCloudFiles };
diff --git a/sheep/components/s-uploader/s-uploader.vue b/sheep/components/s-uploader/s-uploader.vue
new file mode 100644
index 00000000..cd3b4f6d
--- /dev/null
+++ b/sheep/components/s-uploader/s-uploader.vue
@@ -0,0 +1,674 @@
+
+
+
+
+ {{ subtitle }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-uploader/upload-file.vue b/sheep/components/s-uploader/upload-file.vue
new file mode 100644
index 00000000..233d281f
--- /dev/null
+++ b/sheep/components/s-uploader/upload-file.vue
@@ -0,0 +1,335 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.name }}
+
+
+
+
+
+
+
+
+
+ 点击重试
+
+
+
+
+
+
+
+
+
diff --git a/sheep/components/s-uploader/upload-image.vue b/sheep/components/s-uploader/upload-image.vue
new file mode 100644
index 00000000..b66956aa
--- /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 00000000..c1e8073c
--- /dev/null
+++ b/sheep/components/s-uploader/utils.js
@@ -0,0 +1,110 @@
+/**
+ * 获取文件名和后缀
+ * @param {String} name
+ */
+export const get_file_ext = (name) => {
+ const last_len = name.lastIndexOf('.');
+ const len = name.length;
+ return {
+ name: name.substring(0, last_len),
+ ext: name.substring(last_len + 1, len),
+ };
+};
+
+/**
+ * 获取扩展名
+ * @param {Array} fileExtname
+ */
+export const get_extname = (fileExtname) => {
+ if (!Array.isArray(fileExtname)) {
+ let extname = fileExtname.replace(/(\[|\])/g, '');
+ return extname.split(',');
+ } else {
+ return fileExtname;
+ }
+ return [];
+};
+
+/**
+ * 获取文件和检测是否可选
+ */
+export const get_files_and_is_max = (res, _extname) => {
+ let filePaths = [];
+ let files = [];
+ if (!_extname || _extname.length === 0) {
+ return {
+ filePaths,
+ files,
+ };
+ }
+ res.tempFiles.forEach((v) => {
+ let fileFullName = get_file_ext(v.name);
+ const extname = fileFullName.ext.toLowerCase();
+ if (_extname.indexOf(extname) !== -1) {
+ files.push(v);
+ filePaths.push(v.path);
+ }
+ });
+ if (files.length !== res.tempFiles.length) {
+ uni.showToast({
+ title: `当前选择了${res.tempFiles.length}个文件 ,${
+ res.tempFiles.length - files.length
+ } 个文件格式不正确`,
+ icon: 'none',
+ duration: 5000,
+ });
+ }
+
+ return {
+ filePaths,
+ files,
+ };
+};
+
+/**
+ * 获取图片信息
+ * @param {Object} filepath
+ */
+export const get_file_info = (filepath) => {
+ return new Promise((resolve, reject) => {
+ uni.getImageInfo({
+ src: filepath,
+ success(res) {
+ resolve(res);
+ },
+ fail(err) {
+ reject(err);
+ },
+ });
+ });
+};
+/**
+ * 获取封装数据
+ */
+export const get_file_data = async (files, type = 'image') => {
+ // 最终需要上传数据库的数据
+ let fileFullName = get_file_ext(files.name);
+ const extname = fileFullName.ext.toLowerCase();
+ let filedata = {
+ name: files.name,
+ uuid: files.uuid,
+ extname: extname || '',
+ cloudPath: files.cloudPath,
+ fileType: files.fileType,
+ url: files.path || files.path,
+ size: files.size, //单位是字节
+ image: {},
+ path: files.path,
+ video: {},
+ };
+ if (type === 'image') {
+ const imageinfo = await get_file_info(files.path);
+ delete filedata.video;
+ filedata.image.width = imageinfo.width;
+ filedata.image.height = imageinfo.height;
+ filedata.image.location = imageinfo.path;
+ } else {
+ delete filedata.image;
+ }
+ return filedata;
+};
diff --git a/sheep/components/s-user-card/s-user-card.vue b/sheep/components/s-user-card/s-user-card.vue
new file mode 100644
index 00000000..2f007591
--- /dev/null
+++ b/sheep/components/s-user-card/s-user-card.vue
@@ -0,0 +1,168 @@
+
+
+
+
+
+
+
+
+
+
+ {{ userInfo?.nickname || nickname }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
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 00000000..7a659ad8
--- /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 00000000..aa0f1ad0
--- /dev/null
+++ b/sheep/components/s-wallet-card/s-wallet-card.vue
@@ -0,0 +1,95 @@
+
+
+
+
+
+
+
+
+
diff --git a/sheep/config/index.js b/sheep/config/index.js
new file mode 100644
index 00000000..2d6a8703
--- /dev/null
+++ b/sheep/config/index.js
@@ -0,0 +1,19 @@
+// 开发环境配置
+export let baseUrl;
+export let version;
+if (process.env.NODE_ENV === 'development') {
+ baseUrl = import.meta.env.SHOPRO_DEV_BASE_URL;
+} else {
+ baseUrl = import.meta.env.SHOPRO_BASE_URL;
+}
+version = import.meta.env.SHOPRO_VERSION;
+console.log(`[Shopro ${version}] http://api-dashboard.yudao.iocoder.cn/`);
+export const apiPath = import.meta.env.SHOPRO_API_PATH;
+
+export const staticUrl = import.meta.env.SHOPRO_STATIC_URL;
+
+export default {
+ baseUrl,
+ apiPath,
+ staticUrl,
+};
diff --git a/sheep/config/zIndex.js b/sheep/config/zIndex.js
new file mode 100644
index 00000000..6652d484
--- /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/digit.js b/sheep/helper/digit.js
new file mode 100644
index 00000000..be50b32f
--- /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 00000000..38e6afeb
--- /dev/null
+++ b/sheep/helper/index.js
@@ -0,0 +1,708 @@
+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.getSystemInfoSync().platform.toLowerCase();
+}
+/**
+ * @description 获取系统信息同步接口
+ * @link 获取系统信息同步接口 https://uniapp.dcloud.io/api/system/info?id=getsysteminfosync
+ */
+export function sys() {
+ return uni.getSystemInfoSync();
+}
+
+/**
+ * @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 + '/';
+
+ 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,
+ sys,
+ 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 00000000..ca550a14
--- /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 00000000..c3181271
--- /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 00000000..49efcecb
--- /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 00000000..bcb5d25e
--- /dev/null
+++ b/sheep/helper/utils.js
@@ -0,0 +1,168 @@
+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 (isArray(value)) {
+ return value.length === 0;
+ }
+
+ if (isObject(value)) {
+ return Object.keys(value).length === 0;
+ }
+
+ return value === '' || value === undefined || value === null;
+}
+
+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)));
+}
diff --git a/sheep/hooks/useApp.js b/sheep/hooks/useApp.js
new file mode 100644
index 00000000..e69de29b
diff --git a/sheep/hooks/useGoods.js b/sheep/hooks/useGoods.js
new file mode 100644
index 00000000..1be3a60d
--- /dev/null
+++ b/sheep/hooks/useGoods.js
@@ -0,0 +1,212 @@
+import { ref } from 'vue';
+import dayjs from 'dayjs';
+import $url from '@/sheep/url';
+
+/**
+ * 格式化销量
+ * @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+
+ let pow = Math.pow(10, `${num}`.length - 1);
+ return `${prefix}${(num / pow) * pow}+`;
+}
+
+// 格式化价格
+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.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 type 订单类型
+ * @return {string} 颜色的 class 名称
+ */
+export function formatOrderColor(type) {
+ switch (type) {
+ case 'apply_refund':
+ case 'groupon_ing':
+ case 'nocomment':
+ case 'noget':
+ case 'nosend':
+ return 'warning-color';
+ case 'closed':
+ case 'groupon_invalid':
+ case 'cancel':
+ case 'refund_agree':
+ return 'danger-color';
+ case 'completed':
+ return 'success-color';
+ case 'unpaid':
+ return 'info-color';
+ }
+}
+
+/**
+ * 倒计时
+ * @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)
+}
+
+/**
+ * 从商品 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;
+}
\ No newline at end of file
diff --git a/sheep/hooks/useModal.js b/sheep/hooks/useModal.js
new file mode 100644
index 00000000..76bb333c
--- /dev/null
+++ b/sheep/hooks/useModal.js
@@ -0,0 +1,128 @@
+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 $api from '@/sheep/api';
+
+// 打开授权弹框
+export function showAuthModal(type = 'accountLogin') {
+ const modal = $store('modal');
+ if (modal.auth !== '') {
+ closeAuthModal();
+ setTimeout(() => {
+ modal.$patch((state) => {
+ state.auth = type;
+ });
+ }, 100);
+ } else {
+ modal.$patch((state) => {
+ state.auth = type;
+ });
+ }
+}
+
+// 关闭授权弹框
+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;
+ }
+
+ if (!test.mobile(mobile)) {
+ $helper.toast('手机号码格式不正确');
+ return;
+ }
+
+ // 发送验证码 + 更新上次发送验证码时间
+ $api.app.sendSms({ mobile, event }).then((res) => {
+ if (res.error === 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 00000000..26e160e6
--- /dev/null
+++ b/sheep/index.js
@@ -0,0 +1,53 @@
+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
+
+ // 同步前端页面到后端
+ $api.app.pageSync(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 00000000..d1df9bfb
--- /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 00000000..59f9413b
--- /dev/null
+++ b/sheep/libs/permission.js
@@ -0,0 +1,246 @@
+/// null = 未请求,1 = 已允许,0 = 拒绝|受限, 2 = 系统未开启
+
+var isIOS;
+
+function album() {
+ var result = 0;
+ var PHPhotoLibrary = plus.ios.import('PHPhotoLibrary');
+ var authStatus = PHPhotoLibrary.authorizationStatus();
+ if (authStatus === 0) {
+ result = null;
+ } else if (authStatus == 3) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+ plus.ios.deleteObject(PHPhotoLibrary);
+ return result;
+}
+
+function camera() {
+ var result = 0;
+ var AVCaptureDevice = plus.ios.import('AVCaptureDevice');
+ var authStatus = AVCaptureDevice.authorizationStatusForMediaType('vide');
+ if (authStatus === 0) {
+ result = null;
+ } else if (authStatus == 3) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+ plus.ios.deleteObject(AVCaptureDevice);
+ return result;
+}
+
+function location() {
+ var result = 0;
+ var cllocationManger = plus.ios.import('CLLocationManager');
+ var enable = cllocationManger.locationServicesEnabled();
+ var status = cllocationManger.authorizationStatus();
+ if (!enable) {
+ result = 2;
+ } else if (status === 0) {
+ result = null;
+ } else if (status === 3 || status === 4) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+ plus.ios.deleteObject(cllocationManger);
+ return result;
+}
+
+function push() {
+ var result = 0;
+ var UIApplication = plus.ios.import('UIApplication');
+ var app = UIApplication.sharedApplication();
+ var enabledTypes = 0;
+ if (app.currentUserNotificationSettings) {
+ var settings = app.currentUserNotificationSettings();
+ enabledTypes = settings.plusGetAttribute('types');
+ if (enabledTypes == 0) {
+ result = 0;
+ console.log('推送权限没有开启');
+ } else {
+ result = 1;
+ console.log('已经开启推送功能!');
+ }
+ plus.ios.deleteObject(settings);
+ } else {
+ enabledTypes = app.enabledRemoteNotificationTypes();
+ if (enabledTypes == 0) {
+ result = 3;
+ console.log('推送权限没有开启!');
+ } else {
+ result = 4;
+ console.log('已经开启推送功能!');
+ }
+ }
+ plus.ios.deleteObject(app);
+ plus.ios.deleteObject(UIApplication);
+ return result;
+}
+
+function contact() {
+ var result = 0;
+ var CNContactStore = plus.ios.import('CNContactStore');
+ var cnAuthStatus = CNContactStore.authorizationStatusForEntityType(0);
+ if (cnAuthStatus === 0) {
+ result = null;
+ } else if (cnAuthStatus == 3) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+ plus.ios.deleteObject(CNContactStore);
+ return result;
+}
+
+function record() {
+ var result = null;
+ var avaudiosession = plus.ios.import('AVAudioSession');
+ var avaudio = avaudiosession.sharedInstance();
+ var status = avaudio.recordPermission();
+ console.log('permissionStatus:' + status);
+ if (status === 1970168948) {
+ result = null;
+ } else if (status === 1735552628) {
+ result = 1;
+ } else {
+ result = 0;
+ }
+ plus.ios.deleteObject(avaudiosession);
+ return result;
+}
+
+function calendar() {
+ var result = null;
+ var EKEventStore = plus.ios.import('EKEventStore');
+ var ekAuthStatus = EKEventStore.authorizationStatusForEntityType(0);
+ if (ekAuthStatus == 3) {
+ result = 1;
+ console.log('日历权限已经开启');
+ } else {
+ console.log('日历权限没有开启');
+ }
+ plus.ios.deleteObject(EKEventStore);
+ return result;
+}
+
+function memo() {
+ var result = null;
+ var EKEventStore = plus.ios.import('EKEventStore');
+ var ekAuthStatus = EKEventStore.authorizationStatusForEntityType(1);
+ if (ekAuthStatus == 3) {
+ result = 1;
+ console.log('备忘录权限已经开启');
+ } else {
+ console.log('备忘录权限没有开启');
+ }
+ plus.ios.deleteObject(EKEventStore);
+ return result;
+}
+
+function requestIOS(permissionID) {
+ return new Promise((resolve, reject) => {
+ switch (permissionID) {
+ case 'push':
+ resolve(push());
+ break;
+ case 'location':
+ resolve(location());
+ break;
+ case 'record':
+ resolve(record());
+ break;
+ case 'camera':
+ resolve(camera());
+ break;
+ case 'album':
+ resolve(album());
+ break;
+ case 'contact':
+ resolve(contact());
+ break;
+ case 'calendar':
+ resolve(calendar());
+ break;
+ case 'memo':
+ resolve(memo());
+ break;
+ default:
+ resolve(0);
+ break;
+ }
+ });
+}
+
+function requestAndroid(permissionID) {
+ return new Promise((resolve, reject) => {
+ plus.android.requestPermissions(
+ [permissionID],
+ function (resultObj) {
+ var result = 0;
+ for (var i = 0; i < resultObj.granted.length; i++) {
+ var grantedPermission = resultObj.granted[i];
+ console.log('已获取的权限:' + grantedPermission);
+ result = 1;
+ }
+ for (var i = 0; i < resultObj.deniedPresent.length; i++) {
+ var deniedPresentPermission = resultObj.deniedPresent[i];
+ console.log('拒绝本次申请的权限:' + deniedPresentPermission);
+ result = 0;
+ }
+ for (var i = 0; i < resultObj.deniedAlways.length; i++) {
+ var deniedAlwaysPermission = resultObj.deniedAlways[i];
+ console.log('永久拒绝申请的权限:' + deniedAlwaysPermission);
+ result = -1;
+ }
+ resolve(result);
+ },
+ function (error) {
+ console.log('result error: ' + error.message);
+ resolve({
+ code: error.code,
+ message: error.message,
+ });
+ },
+ );
+ });
+}
+
+function gotoAppPermissionSetting() {
+ if (permission.isIOS) {
+ var UIApplication = plus.ios.import('UIApplication');
+ var application2 = UIApplication.sharedApplication();
+ var NSURL2 = plus.ios.import('NSURL');
+ var setting2 = NSURL2.URLWithString('app-settings:');
+ application2.openURL(setting2);
+ plus.ios.deleteObject(setting2);
+ plus.ios.deleteObject(NSURL2);
+ plus.ios.deleteObject(application2);
+ } else {
+ var Intent = plus.android.importClass('android.content.Intent');
+ var Settings = plus.android.importClass('android.provider.Settings');
+ var Uri = plus.android.importClass('android.net.Uri');
+ var mainActivity = plus.android.runtimeMainActivity();
+ var intent = new Intent();
+ intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+ var uri = Uri.fromParts('package', mainActivity.getPackageName(), null);
+ intent.setData(uri);
+ mainActivity.startActivity(intent);
+ }
+}
+
+const permission = {
+ get isIOS() {
+ return typeof isIOS === 'boolean'
+ ? isIOS
+ : (isIOS = uni.getSystemInfoSync().platform === 'ios');
+ },
+ requestIOS: requestIOS,
+ requestAndroid: requestAndroid,
+ gotoAppSetting: gotoAppPermissionSetting,
+};
+
+export default permission;
diff --git a/sheep/libs/sdk-h5-weixin.js b/sheep/libs/sdk-h5-weixin.js
new file mode 100644
index 00000000..8ac8c609
--- /dev/null
+++ b/sheep/libs/sdk-h5-weixin.js
@@ -0,0 +1,186 @@
+/**
+ * 本模块封装微信浏览器下的一些方法。
+ * 更多微信网页开发sdk方法,详见:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html
+ */
+
+import jweixin from 'weixin-js-sdk';
+import $helper from '@/sheep/helper';
+import third from '@/sheep/api/third';
+
+let configSuccess = false;
+
+export default {
+ //判断是否在微信中
+ isWechat() {
+ const ua = window.navigator.userAgent.toLowerCase();
+ if (ua.match(/micromessenger/i) == 'micromessenger') {
+ return true;
+ } else {
+ return false;
+ }
+ },
+
+ isReady(api) {
+ jweixin.ready(api);
+ },
+
+ // 初始化JSSDK
+ async init(callback) {
+ if (!this.isWechat()) {
+ $helper.toast('请使用微信网页浏览器打开');
+ return;
+ }
+
+ const url = location.href.split('#')[0];
+
+ const { error, data } = await third.wechat.jssdk({
+ platform: 'officialAccount',
+ payload: encodeURIComponent(
+ JSON.stringify({
+ url,
+ }),
+ ),
+ });
+
+ if (error === 0) {
+ jweixin.config({
+ debug: false,
+ appId: data.appId,
+ timestamp: data.timestamp,
+ nonceStr: data.nonceStr,
+ signature: data.signature,
+ jsApiList: data.jsApiList,
+ openTagList: data.openTagList,
+ });
+ }
+
+ configSuccess = true;
+
+ jweixin.error((err) => {
+ configSuccess = false;
+ // $helper.toast('微信JSSDK:' + err.errMsg);
+ });
+
+ if (callback) {
+ callback(data);
+ }
+ },
+
+ //在需要定位页面调用
+ 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) {},
+ });
+ });
+ },
+
+ // 微信扫码
+ 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);
+ });
+ },
+
+ // 打开坐标位置
+ openLocation(data, callback) {
+ this.isReady(() => {
+ jweixin.openLocation({
+ //根据传入的坐标打开地图
+ latitude: data.latitude,
+ longitude: data.longitude,
+ });
+ });
+ },
+
+ // 选择图片
+ 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.package, // 统一支付接口返回的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 00000000..36a54fc6
--- /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';
+// #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.getSystemInfoSync();
+
+const os = device.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 00000000..29120038
--- /dev/null
+++ b/sheep/platform/pay.js
@@ -0,0 +1,255 @@
+import sheep from '@/sheep';
+// #ifdef H5
+import $wxsdk from '@/sheep/libs/sdk-h5-weixin';
+// #endif
+import { getRootUrl } from '@/sheep/helper';
+
+/**
+ * 支付
+ *
+ * @param {String} payment = ['wechat','alipay','wallet','offline'] - 支付方式
+ * @param {String} orderType = ['goods','recharge','groupon'] - 订单类型
+ * @param {String} orderSN - 订单号
+ */
+
+export default class SheepPay {
+ constructor(payment, orderType, orderSN) {
+ this.payment = payment;
+ this.orderSN = orderSN;
+ this.orderType = orderType;
+ this.payAction();
+ }
+
+ payAction() {
+ const payAction = {
+ WechatOfficialAccount: {
+ wechat: () => {
+ this.wechatOfficialAccountPay();
+ },
+ alipay: () => {
+ this.redirectPay(); // 现在公众号可以直接跳转支付宝页面
+ },
+ money: () => {
+ this.moneyPay();
+ },
+ offline: () => {
+ this.offlinePay();
+ }
+ },
+ WechatMiniProgram: {
+ wechat: () => {
+ this.wechatMiniProgramPay();
+ },
+ alipay: () => {
+ this.copyPayLink();
+ },
+ money: () => {
+ this.moneyPay();
+ },
+ offline: () => {
+ this.offlinePay();
+ }
+ },
+ App: {
+ wechat: () => {
+ this.wechatAppPay();
+ },
+ alipay: () => {
+ this.alipay();
+ },
+ money: () => {
+ this.moneyPay();
+ },
+ offline: () => {
+ this.offlinePay();
+ }
+ },
+ H5: {
+ wechat: () => {
+ this.wechatWapPay();
+ },
+ alipay: () => {
+ this.redirectPay();
+ },
+ money: () => {
+ this.moneyPay();
+ },
+ offline: () => {
+ this.offlinePay();
+ }
+ },
+ };
+ return payAction[sheep.$platform.name][this.payment]();
+ }
+
+ // 预支付
+ prepay() {
+ return new Promise((resolve, reject) => {
+ let data = {
+ order_sn: this.orderSN,
+ payment: this.payment,
+ };
+ if (uni.getStorageSync('openid')) {
+ data.openid = uni.getStorageSync('openid');
+ }
+ sheep.$api.pay.prepay(data).then((res) => {
+ res.error === 0 && resolve(res);
+ if (res.error === -1 && res.msg === 'miss_openid') {
+ uni.showModal({
+ title: '微信支付',
+ content: '请先绑定微信再使用微信支付',
+ success: function (res) {
+ if (res.confirm) {
+ sheep.$platform.useProvider('wechat').bind();
+ }
+ },
+ });
+ }
+ });
+ });
+ }
+ // #ifdef H5
+ // 微信公众号JSSDK支付
+ async wechatOfficialAccountPay() {
+ let that = this;
+ let { error, data, msg } = await this.prepay();
+ if (error !== 0) {
+ console.log('支付错误', msg);
+ return;
+ }
+ $wxsdk.wxpay(data.pay_data, {
+ success: () => {
+ that.payResult('success');
+ },
+ cancel: () => {
+ sheep.$helper.toast('支付已手动取消');
+ },
+ fail: () => {
+ that.payResult('fail');
+ },
+ });
+ }
+
+ //浏览器微信H5支付
+ async wechatWapPay() {
+ const { error, data } = await this.prepay();
+ if (error === 0) {
+ const redirect_url = `${getRootUrl()}pages/pay/result?orderSN=${this.orderSN}&payment=${this.payment
+ }&orderType=${this.orderType}`;
+ location.href = `${data.pay_data.h5_url}&redirect_url=${encodeURIComponent(redirect_url)}`;
+ }
+ }
+
+ // 支付链接
+ async redirectPay() {
+ let { error, data } = await this.prepay();
+ if (error === 0) {
+ const redirect_url = `${getRootUrl()}pages/pay/result?orderSN=${this.orderSN}&payment=${this.payment
+ }&orderType=${this.orderType}`;
+ location.href = data.pay_data + encodeURIComponent(redirect_url);
+ }
+ }
+
+ // #endif
+
+ // 微信小程序支付
+ async wechatMiniProgramPay() {
+ let that = this;
+ let result = await this.prepay();
+ uni.requestPayment({
+ provider: 'wxpay',
+ ...result.data.pay_data,
+ success: (res) => {
+ that.payResult('success');
+ },
+ fail: (err) => {
+ if (err.errMsg === 'requestPayment:fail cancel') {
+ sheep.$helper.toast('支付已手动取消');
+ } else {
+ that.payResult('fail');
+ }
+ },
+ });
+ }
+
+ // 余额支付
+ async moneyPay() {
+ const { error } = await this.prepay();
+ error === 0 && this.payResult('success');
+ }
+
+ // 货到付款
+ async offlinePay() {
+ const { error } = await this.prepay();
+ error === 0 && this.payResult('success');
+ }
+
+ // 支付宝复制链接支付
+ async copyPayLink() {
+ let that = this;
+ let { error, data } = await this.prepay();
+ if (error === 0) {
+ // 引入showModal 点击确认 复制链接;
+ uni.showModal({
+ title: '支付宝支付',
+ content: '复制链接到外部浏览器',
+ confirmText: '复制链接',
+ success: (res) => {
+ if (res.confirm) {
+ sheep.$helper.copyText(data.pay_data);
+ }
+ },
+ });
+ }
+ }
+
+ // 支付宝支付
+ async alipay() {
+ let that = this;
+ const { error, data } = await this.prepay();
+ if (error === 0) {
+ uni.requestPayment({
+ provider: 'alipay',
+ orderInfo: data.pay_data, //支付宝订单数据
+ success: (res) => {
+ that.payResult('success');
+ },
+ fail: (err) => {
+ if (err.errMsg === 'requestPayment:fail [paymentAlipay:62001]user cancel') {
+ sheep.$helper.toast('支付已手动取消');
+ } else {
+ that.payResult('fail');
+ }
+ },
+ });
+ }
+ }
+
+ // 微信支付
+ async wechatAppPay() {
+ let that = this;
+ let { error, data } = await this.prepay();
+ if (error === 0) {
+ uni.requestPayment({
+ provider: 'wxpay',
+ orderInfo: data.pay_data, //微信订单数据(官方说是string。实测为object)
+ success: (res) => {
+ that.payResult('success');
+ },
+ fail: (err) => {
+ err.errMsg !== 'requestPayment:fail cancel' && that.payResult('fail');
+ },
+ });
+ }
+ }
+
+ // 支付结果跳转,success:成功,fail:失败
+ payResult(resultType) {
+ sheep.$router.redirect('/pages/pay/result', {
+ orderSN: this.orderSN,
+ payment: this.payment, //重新支付的时候使用
+ payState: resultType,
+ orderType: this.orderType,
+ });
+ }
+}
diff --git a/sheep/platform/provider/apple/app.js b/sheep/platform/provider/apple/app.js
new file mode 100644
index 00000000..d73c2e83
--- /dev/null
+++ b/sheep/platform/provider/apple/app.js
@@ -0,0 +1,35 @@
+import third from '@/sheep/api/third';
+
+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 00000000..388c0939
--- /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 00000000..3bb2c7fe
--- /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 00000000..60341752
--- /dev/null
+++ b/sheep/platform/provider/wechat/miniProgram.js
@@ -0,0 +1,251 @@
+import { isEmpty } from 'lodash';
+import third from '@/sheep/api/third';
+import $store from '@/sheep/store';
+
+let sessionId = uni.getStorageSync('sessionId');
+let subscribeEventList = [];
+
+// 加载微信小程序
+function load() {
+ checkUpdate();
+ // const sessionStatus = await checkSession();
+ // 小程序的接口改动太频繁了 强制每次进入都重新获取
+ const sessionStatus = false;
+ if (!sessionStatus) {
+ getSessionId();
+ }
+ getSubscribeTemplate();
+}
+
+// 微信小程序静默授权登陆 TODO-ldh: code > 0 问题 改为error
+const login = async () => {
+ return new Promise(async (resolve, reject) => {
+ const { error } = await third.wechat.login({
+ platform: 'miniProgram',
+ shareInfo: uni.getStorageSync('shareLog') || {},
+ payload: encodeURIComponent(
+ JSON.stringify({
+ sessionId: uni.getStorageSync('sessionId'),
+ }),
+ ),
+ });
+
+ if (error === 0) {
+ resolve(true);
+ }
+
+ if (error === -1) {
+ getSessionId(false);
+ }
+ resolve(false);
+ });
+};
+
+// 微信小程序手机号授权登陆
+const mobileLogin = async (e) => {
+ return new Promise(async (resolve, reject) => {
+ if (e.errMsg !== 'getPhoneNumber:ok') {
+ resolve(false);
+ return;
+ }
+
+ const { error } = await third.wechat.login({
+ platform: 'miniProgram',
+ shareInfo: uni.getStorageSync('shareLog') || {},
+ payload: encodeURIComponent(
+ JSON.stringify({
+ sessionId: uni.getStorageSync('sessionId'),
+ code: e.code,
+ iv: e.iv,
+ encryptedData: e.encryptedData,
+ }),
+ ),
+ });
+
+ if (error === 0) {
+ resolve(true);
+ }
+
+ if (error === -1) {
+ getSessionId(false);
+ }
+ resolve(false);
+ });
+};
+
+// 微信小程序绑定
+const bind = () => {
+ return new Promise(async (resolve, reject) => {
+ const loginRes = await third.wechat.bind({
+ platform: 'miniProgram',
+ payload: encodeURIComponent(
+ JSON.stringify({
+ sessionId: uni.getStorageSync('sessionId'),
+ }),
+ ),
+ });
+
+ if (loginRes.error === -1) {
+ getSessionId(false);
+ } else if (loginRes.error === 0) {
+ resolve(true);
+ } else {
+ reject(false);
+ }
+ });
+};
+
+// 微信小程序解除绑定
+const unbind = async () => {
+ const { error } = await third.wechat.unbind({
+ platform: 'miniProgram',
+ });
+ return !error;
+};
+
+// 获取最新sessionId
+const getSessionId = async (auto_login = null) => {
+ // 获取code
+ let codeStr = '';
+ const loginResult = await uni.login();
+ if (loginResult.errMsg === 'login:ok') {
+ codeStr = loginResult.code;
+ } else {
+ getSessionId(auto_login);
+ return false;
+ }
+ if(auto_login === null) {
+ auto_login = !!($store('app').platform.auto_login && !$store('user').isLogin);
+ }
+
+ const { error, data } = await third.wechat.getSessionId({
+ platform: 'miniProgram',
+ payload: encodeURIComponent(
+ JSON.stringify({
+ code: codeStr,
+ auto_login,
+ }),
+ ),
+ });
+ if (error === 0) {
+ uni.setStorageSync('sessionId', data.session_id);
+ return true;
+ }
+ return false;
+};
+
+// 检查sessionId是否可用
+const checkSession = () => {
+ return new Promise((resolve, reject) => {
+ if (!sessionId) {
+ return resolve(false);
+ }
+ uni.checkSession({
+ success() {
+ return resolve(true);
+ },
+ fail() {
+ uni.removeStorageSync('sessionId');
+ return resolve(false);
+ },
+ });
+ });
+};
+
+// 小程序更新
+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,
+ });
+ }
+ }
+ });
+ }
+};
+
+// 绑定用户手机号
+const bindUserPhoneNumber = (e) => {
+ return new Promise(async (resolve, reject) => {
+ const { error } = await third.wechat.bindUserPhoneNumber({
+ platform: 'miniProgram',
+ payload: encodeURIComponent(
+ JSON.stringify({
+ sessionId: uni.getStorageSync('sessionId'),
+ iv: e.iv,
+ encryptedData: e.encryptedData,
+ code: e.code,
+ }),
+ ),
+ });
+ if (error === 0) {
+ resolve(true);
+ }
+ resolve(false);
+ });
+};
+
+// 获取订阅消息模板
+async function getSubscribeTemplate() {
+ const { error, data } = await third.wechat.subscribeTemplate();
+ if (error === 0) {
+ subscribeEventList = data;
+ }
+}
+
+// 订阅消息
+function subscribeMessage(event) {
+ let tmplIds = [];
+ if (typeof event === 'string') {
+ tmplIds.push(subscribeEventList[event]);
+ }
+ if (typeof event === 'object') {
+ event.forEach((item) => {
+ if (typeof subscribeEventList[item] !== 'undefined') tmplIds.push(subscribeEventList[item]);
+ });
+ }
+ if (tmplIds.length === 0) return;
+
+ uni.requestSubscribeMessage({
+ tmplIds,
+ fail: (err) => {
+ console.log(err);
+ },
+ });
+}
+
+export default {
+ load,
+ login,
+ bind,
+ unbind,
+ checkUpdate,
+ bindUserPhoneNumber,
+ subscribeMessage,
+};
diff --git a/sheep/platform/provider/wechat/officialAccount.js b/sheep/platform/provider/wechat/officialAccount.js
new file mode 100644
index 00000000..f5f90eb3
--- /dev/null
+++ b/sheep/platform/provider/wechat/officialAccount.js
@@ -0,0 +1,112 @@
+import third from '@/sheep/api/third';
+import $wxsdk from '@/sheep/libs/sdk-h5-weixin';
+import $store from '@/sheep/store';
+import $platform from '@/sheep/platform';
+import { getRootUrl } from '@/sheep/helper';
+
+// 加载微信公众号JSSDK
+async function load() {
+ if (
+ $store('app').platform.auto_login &&
+ !$store('user').isLogin &&
+ location.href.search('pages/index/login') === -1
+ ) {
+ // 发起自动登陆
+ login();
+ }
+ $wxsdk.init();
+}
+
+// 微信公众号登陆
+async function login(code = '') {
+ // 获取登陆地址
+ if (!code) {
+ const loginResult = await getLoginUrl();
+ if (loginResult.error === 0 && loginResult.data.login_url) {
+ uni.setStorageSync('returnUrl', location.href);
+ window.location = loginResult.data.login_url;
+ }
+ } else {
+ // 解密code发起登陆
+ const loginResult = await loginByCode(code);
+ if (loginResult.error === 0) {
+ return loginResult;
+ }
+ }
+ return false;
+}
+
+// 微信公众号绑定
+async function bind(code = '') {
+ // 获取绑定地址
+ if (code === '') {
+ const loginResult = await getLoginUrl('bind');
+ if (loginResult.error === 0 && loginResult.data.login_url) {
+ uni.setStorageSync('returnUrl', location.href);
+ window.location = loginResult.data.login_url;
+ }
+ } else {
+ // 解密code发起登陆
+ const loginResult = await bindByCode(code);
+ if (loginResult.error === 0) {
+ return loginResult;
+ }
+ }
+ return false;
+}
+
+// 微信公众号解除绑定
+async function unbind() {
+ const { error } = await third.wechat.unbind({
+ platform: 'officialAccount',
+ });
+ return Promise.resolve(!error);
+}
+
+// 获取公众号登陆地址
+function getLoginUrl(event = 'login') {
+ let page = getRootUrl() + 'pages/index/login';
+
+ return third.wechat.oauthLogin({
+ platform: 'officialAccount',
+ payload: encodeURIComponent(
+ JSON.stringify({
+ page,
+ event,
+ }),
+ ),
+ });
+}
+
+// 此处使用前端发送code在后端解密,防止用户在后端过长时间停留
+function loginByCode(code) {
+ return third.wechat.login({
+ platform: 'officialAccount',
+ shareInfo: uni.getStorageSync('shareLog') || {},
+ payload: encodeURIComponent(
+ JSON.stringify({
+ code,
+ }),
+ ),
+ });
+}
+
+// 此处使用前端发送code在后端解密,防止用户在后端过长时间停留
+function bindByCode(code) {
+ return third.wechat.bind({
+ platform: 'officialAccount',
+ payload: encodeURIComponent(
+ JSON.stringify({
+ code,
+ }),
+ ),
+ });
+}
+
+export default {
+ load,
+ login,
+ bind,
+ unbind,
+ jssdk: $wxsdk,
+};
diff --git a/sheep/platform/provider/wechat/openPlatform.js b/sheep/platform/provider/wechat/openPlatform.js
new file mode 100644
index 00000000..e1e493a9
--- /dev/null
+++ b/sheep/platform/provider/wechat/openPlatform.js
@@ -0,0 +1,41 @@
+// 登录
+import { isEmpty } from 'lodash';
+import third from '@/sheep/api/third';
+
+const load = async () => {};
+
+// 微信开放平台移动应用授权登陆
+const login = () => {
+ return new Promise(async (resolve, reject) => {
+ const loginRes = await uni.login({
+ provider: 'weixin',
+ onlyAuthorize: true,
+ });
+ if (loginRes.errMsg == 'login:ok') {
+ const res = await third.wechat.login({
+ platform: 'openPlatform',
+ shareInfo: uni.getStorageSync('shareLog') || {},
+ payload: encodeURIComponent(
+ JSON.stringify({
+ code: loginRes.code,
+ }),
+ ),
+ });
+
+ if (res.error === 0) {
+ resolve(true);
+ }
+ } else {
+ uni.showToast({
+ icon: 'none',
+ title: loginRes.errMsg,
+ });
+ }
+ resolve(false);
+ });
+};
+
+export default {
+ load,
+ login,
+};
diff --git a/sheep/platform/share.js b/sheep/platform/share.js
new file mode 100644
index 00000000..d6d96073
--- /dev/null
+++ b/sheep/platform/share.js
@@ -0,0 +1,182 @@
+import $store from '@/sheep/store';
+import $platform from '@/sheep/platform';
+import $router from '@/sheep/router';
+import $url from '@/sheep/url';
+// #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',
+ },
+) => {
+ let shareInfo = {
+ title: '', // 分享标题
+ desc: '', // 描述
+ image: '', // 分享图片
+ path: '', // 分享页面+参数
+ link: '', // 分享Url+参数
+ query: '', // 分享参数
+ poster, // 海报所需数据
+ };
+
+ const app = $store('app');
+ const shareConfig = app.platform.share;
+
+ // 自动拼接分享用户参数
+ const query = buildSpmQuery(scene.params);
+ shareInfo.query = query;
+
+ // 配置分享链接地址
+ shareInfo.link = buildSpmLink(query, shareConfig.linkAddress);
+
+ // 配置转发参数
+ if (shareConfig.methods.includes('forward')) {
+ if (shareConfig.forwardInfo.title === '' || shareConfig.forwardInfo.image === '') {
+ console.log('请在平台设置中配置转发信息');
+ }
+ // 设置自定义分享信息
+ shareInfo.title = scene.title || shareConfig.forwardInfo.title;
+ shareInfo.image = $url.cdn(scene.image || shareConfig.forwardInfo.image);
+ shareInfo.desc = scene.desc || shareConfig.forwardInfo.subtitle;
+ shareInfo.path = buildSpmPath(query);
+ }
+
+ return shareInfo;
+};
+
+// 构造spm分享参数
+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 = '1'; // 页面类型: 1=首页(默认),2=商品,3=拼团商品,4=秒杀商品,5=邀请参团...按需扩展
+ 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}`;
+};
+
+// 构造页面分享参数
+const buildSpmPath = (query) => {
+ return `/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 '1':
+ // 默认首页不跳转
+ shareParams.page = '/pages/index/index';
+ break;
+ case '2':
+ // 普通商品
+ shareParams.page = '/pages/goods/index';
+ shareParams.query = {
+ id: shareParamsArray[2],
+ };
+ break;
+ case '3':
+ // 拼团商品
+ shareParams.page = '/pages/goods/groupon';
+ query = shareParamsArray[2].split(',');
+ shareParams.query = {
+ id: query[0],
+ activity_id: query[1],
+ };
+ break;
+ case '4':
+ // 秒杀商品
+ shareParams.page = '/pages/goods/seckill';
+ query = shareParamsArray[2].split(',');
+ shareParams.query = {
+ id: query[0],
+ activity_id: query[1],
+ };
+ break;
+ case '5':
+ // 参与拼团
+ shareParams.page = '/pages/activity/groupon/detail';
+ shareParams.query = {
+ id: shareParamsArray[2],
+ };
+ break;
+ }
+ shareParams.platform = platformMap[shareParamsArray[3] - 1];
+ shareParams.from = fromMap[shareParamsArray[4] - 1];
+ if (shareParams.shareId != 0) {
+ // 已登录 立即添加分享记录
+ if (user.isLogin) {
+ user.addShareLog(shareParams);
+ } else {
+ // 未登录 待用户登录后添加分享记录
+ uni.setStorageSync('shareLog', shareParams);
+ }
+ }
+
+ if (shareParams.page !== '/pages/index/index') {
+ $router.go(shareParams.page, shareParams.query);
+ }
+ return shareParams;
+};
+
+// 更新公众号分享sdk
+const updateShareInfo = (shareInfo) => {
+ // #ifdef H5
+ if ($platform.name === 'WechatOfficialAccount') {
+ $wxsdk.updateShareInfo(shareInfo);
+ }
+ // #endif
+};
+
+export default {
+ getShareInfo,
+ updateShareInfo,
+ decryptSpm,
+};
diff --git a/sheep/request/index.js b/sheep/request/index.js
new file mode 100644
index 00000000..94d5bc08
--- /dev/null
+++ b/sheep/request/index.js
@@ -0,0 +1,221 @@
+/**
+ * Shopro-request
+ * @description api模块管理,loading配置,请求拦截,错误处理
+ */
+
+import Request from 'luch-request';
+import {
+ baseUrl,
+ apiPath
+} from '@/sheep/config';
+import $store from '@/sheep/store';
+import $platform from '@/sheep/platform';
+import {
+ showAuthModal
+} from '@/sheep/hooks/useModal';
+
+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,
+ 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) => {
+ if (config.custom.auth && !$store('user').isLogin) {
+ showAuthModal();
+ return Promise.reject();
+ }
+ if (config.custom.showLoading) {
+ LoadingInstance.count++;
+ LoadingInstance.count === 1 &&
+ uni.showLoading({
+ title: config.custom.loadingMsg,
+ mask: true,
+ fail: () => {
+ uni.hideLoading();
+ },
+ });
+ }
+ const token = uni.getStorageSync('token');
+ if (token) config.header['Authorization'] = token;
+ // TODO 芋艿:特殊处理
+ if (config.url.indexOf('/app-api/') !== -1) {
+ config.header['Accept'] = '*/*'
+ config.header['tenant-id'] = '1';
+ config.header['Authorization'] = 'Bearer test247';
+ }
+ return config;
+ },
+ (error) => {
+ return Promise.reject(error);
+ },
+);
+
+/**
+ * @description 响应拦截器
+ */
+http.interceptors.response.use(
+ (response) => {
+ // 自动设置登陆令牌
+ if (response.header.authorization || response.header.Authorization) {
+ $store('user').setToken(response.header.authorization || response.header.Authorization);
+ }
+
+ response.config.custom.showLoading && closeLoading();
+ if (response.data.error !== 0) {
+ if (response.config.custom.showError)
+ uni.showToast({
+ title: response.data.msg || '服务器开小差啦,请稍后再试~',
+ icon: 'none',
+ mask: true,
+ });
+ return Promise.resolve(response.data);
+ }
+ if (
+ response.data.error === 0 &&
+ response.data.msg !== '' &&
+ response.config.custom.showSuccess
+ ) {
+ uni.showToast({
+ title: response.config.custom.successMsg || response.data.msg,
+ icon: 'none',
+ });
+ }
+ 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:
+ if (isLogin) {
+ errorMessage = '您的登陆已过期';
+ } else {
+ errorMessage = '请先登录';
+ }
+ userStore.logout(true);
+ showAuthModal();
+ 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 === false) {
+ uni.showToast({
+ title: error.data?.msg || errorMessage,
+ icon: 'none',
+ mask: true,
+ });
+ }
+ error.config.custom.showLoading && closeLoading();
+ }
+
+ return false;
+ },
+);
+
+const request = (config) => {
+ if (config.url[0] !== '/') {
+ config.url = apiPath + config.url;
+ }
+ // TODO 芋艿:额外拼接
+ if (config.url.indexOf('/app-api/') >= 0) {
+ config.url = 'http://api-dashboard.yudao.iocoder.cn' + config.url; // 调用【云端】
+ // config.url = 'http://127.0.0.1:48080' + config.url; // 调用【本地】
+ }
+ return http.middleware(config);
+};
+
+export default request;
\ No newline at end of file
diff --git a/sheep/request2/index.js b/sheep/request2/index.js
new file mode 100644
index 00000000..c2a3f903
--- /dev/null
+++ b/sheep/request2/index.js
@@ -0,0 +1,224 @@
+/**
+ * Shopro-request
+ * @description api模块管理,loading配置,请求拦截,错误处理
+ */
+
+import Request from 'luch-request';
+import {
+ baseUrl,
+ apiPath
+} from '@/sheep/config';
+import $store from '@/sheep/store';
+import $platform from '@/sheep/platform';
+import {
+ showAuthModal
+} from '@/sheep/hooks/useModal';
+
+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: 'https://api.shopro.sheepjs.com/',
+ 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) => {
+ // console.log(config);
+ if (config.custom.auth && !$store('user').isLogin) {
+ showAuthModal();
+ return Promise.reject();
+ }
+ if (config.custom.showLoading) {
+ LoadingInstance.count++;
+ LoadingInstance.count === 1 &&
+ uni.showLoading({
+ title: config.custom.loadingMsg,
+ mask: true,
+ fail: () => {
+ uni.hideLoading();
+ },
+ });
+ }
+ const token = uni.getStorageSync('token');
+ if (token) config.header['Authorization'] = token;
+ // TODO 芋艿:特殊处理
+ if (config.url.indexOf('/app-api/') !== -1) {
+ config.header['Accept'] = '*/*'
+ config.header['tenant-id'] = '1';
+ config.header['Authorization'] = 'Bearer test247';
+ }
+ return config;
+ },
+ (error) => {
+ return Promise.reject(error);
+ },
+);
+
+/**
+ * @description 响应拦截器
+ */
+http.interceptors.response.use(
+ (response) => {
+ // 自动设置登陆令牌
+ if (response.header.authorization || response.header.Authorization) {
+ $store('user').setToken(response.header.authorization || response.header.Authorization);
+ }
+
+ response.config.custom.showLoading && closeLoading();
+ if (response.data.code !== 0) {
+ if (response.config.custom.showError)
+ uni.showToast({
+ title: response.data.msg || '服务器开小差啦,请稍后再试~',
+ icon: 'none',
+ mask: true,
+ });
+ return Promise.resolve(response.data);
+ }
+ if (
+ response.data.error === 0 &&
+ response.data.msg !== '' &&
+ response.config.custom.showSuccess
+ ) {
+ uni.showToast({
+ title: response.config.custom.successMsg || response.data.msg,
+ icon: 'none',
+ });
+ }
+ 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:
+ if (isLogin) {
+ errorMessage = '您的登陆已过期';
+ } else {
+ errorMessage = '请先登录';
+ }
+ userStore.logout(true);
+ showAuthModal();
+ 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 === false) {
+ uni.showToast({
+ title: error.data?.msg || errorMessage,
+ icon: 'none',
+ mask: true,
+ });
+ }
+ error.config.custom.showLoading && closeLoading();
+ }
+
+ return false;
+ },
+);
+
+const request = (config) => {
+ if (config.url[0] !== '/') {
+ config.url = '/app-api/' + config.url;
+ }
+ // TODO 芋艿:额外拼接
+ if (config.url.indexOf('/app-api/') >= 0) {
+ // 设置接口地址
+ config.url = 'http://api-dashboard.yudao.iocoder.cn' + config.url; // 调用【云端】
+ // config.url = 'https://app.test.huizhizao.vip/prod-api' + config.url; // 调用【云端】
+ // config.url = 'http://127.0.0.1:48080' + config.url; // 调用【本地】
+ }
+ return http.middleware(config);
+};
+
+export default request;
\ No newline at end of file
diff --git a/sheep/router/index.js b/sheep/router/index.js
new file mode 100644
index 00000000..e4c18c56
--- /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';
+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 00000000..59959922
--- /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 00000000..303f10a5
--- /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 00000000..999513a9
--- /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 00000000..299f7b16
--- /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 00000000..e1fb636e
--- /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 00000000..b0d88c82
--- /dev/null
+++ b/sheep/scss/_var.scss
@@ -0,0 +1,162 @@
+@import './mixins';
+
+//颜色 ,渐变背景60%
+$yellow: #ffc300; //ss-黄
+$orange: #ff6000; //ss-橘
+$red: #ff3000; //ss-红
+$pink: #e03997;
+$mauve: #b745cb;
+$purple: #652abf; //rgba(101, 42, 191, 1); // ss-紫
+$blue: #0081ff;
+$cyan: #37c0fe;
+$green: #2aae67; //ss-绿
+$olive: #8dc63f;
+$grey: #8799a3;
+$brown: #a5673f;
+$black: #484848; //ss-黑
+$golden: #e9b461; //ss-金
+
+$colors: ();
+$colors: map-merge(
+ (
+ 'yellow': $yellow,
+ 'orange': $orange,
+ 'red': $red,
+ 'pink': $pink,
+ 'mauve': $mauve,
+ 'purple': $purple,
+ 'violet': $purple,
+ 'blue': $blue,
+ 'cyan': $cyan,
+ 'green': $green,
+ 'olive': $olive,
+ 'grey': $grey,
+ 'brown': $brown,
+ 'black': $black,
+ 'golden': $golden,
+ ),
+ $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
zcmcI~d3apKm2XwuZnbu)Ey-GJNxfKVZPwQ6MN)T5t(IC+FWUFDTf1y6wj^xX7;Fdx
z8yh?EGMNw(AVY{fc>w|#GO=N71_y=!AsCoIf|+4hJbZ*CBrutYb^HD5cFUHXyf=Tm
z?{$B5Z{4aor_MQb&Z$%9UP1{W(PSSHNOg02QR%{1YzA6hM)8f#`nMhSKN3L*Eg*!s
zCjE=s&>lsIu#9{7hl30k1RAF=<=_dnZ
zkPMMw;v>7s&14U`h1?qWFc1K~8}Z~^ho_iS0FPC|W4{iMe=VNCzk!ls&{9lH#0)Al
zpf{f+p>-We1-0QMouuKO9ylNPL*O@o&jTxg&j|^f3H&1P?}66>zb4y){DPwY=N}FG
zpHa}-w)&2Np>~(s*=293ZZMk6B;JWLTD5g8R76B`$=T$hlT
zv_3f{HElyvV{?D2Z)18!rb?~RZpzBm<>cn&7i=ypDlRG2ml-_6-oF2tfdBs^D4|&4
zNa)XW;D>^reTbTZBtWN>YNcGQRA(g!`ec({NYF-6!M_sDo?h9{mRDZB_)6<{zvub+
zTWu$Qz&^b4qF}bHRGDZY&HmG$#J9zZl!{z&2qQ^>^XwdSSAkhbmRg~uda8|LOeX%+
zJ4MrW(zN+4d-wg|6G46DJ?fxe5pUse0%-!=65!&Y8yY|+#!@ZlDb1isiD8NiN|pLj
zqoFKYn^2aWFFmlO@+pV-EG@GR8)_;VIz{`F*1G!2hgzJ^J?CtB&@pZn)cQfIy*D9Y
zl0Fb1wUyRdF}2Ps{<;ZzWeS{T_p{H)2Fz7zO2%xOLTV_pQGMz9WJR`C6N`y4?co!;
z&g3K0mF#n!56p_cqw%v3bWR@H`^|6eJw#iVPmc=f(bKz*oLc$%Nyvjrc+!CfelH=c@SnZnzx4|0-qrEzFdlw0K^m*Y^)z*gKbfS@vjd{oD;|@E
zhIoGhysJZU{;Qlr+*YbJsM!%T1S7u4XVmFoZ{U4iq2`#t!(
z1dM-Xk`EF!`9h+E{3+n3!|dR%aFJsw$Xo
z<$I&^Av*pBaL`6MCq%EL`UuJr#Row}%`M_7x~qzw5VwMk<8(&6QZT&onc0>{09>IMJAAE!Sbx?2?Qsbcr6EV8>-b^xZ0
zJrd-R>&K&jyQ-~xq3#Frz!TrriO56*!$)2XW?rQ3xmr)nr5Y)Eq?&fas
z3BLDndOIX~6X55GJXxy?yJlFDEz579nN`osH5Hd$CA>F4))1b2Vm|gN?aODC*OMNi
z{U~UUCCQ*&8={;0j0CtI$z|kIT72^)EvTC`kN19li}=9fPM6F1IE@jX6x0=4oc=x0
z;^XweriS|F;2PyY=B_mz~6tbLv)RipjR
zrMf*WTfV-bBWcvt(x+{8?%u`{?W4w4w{AL`yK(zi?bgbHjvSla!P;Pfh>J)`w0{KKVUKcfYkV4&C*$`&Wk9gPb1~
z__-gj@MCg7QVCR`Ea#W$0DD!;ZuAMTMPu4mn%gZPE
z+)m8hE65NFWvzPPY+aZ_EBQ0N198Ky-I2eOAox=d-%=@}yZm
z>OJVSnZ4=84)zb?Bc%rKp}w_~Qi)#$_(dE``3|=h{o3u1mUnE4Z;PxnlzQ?o!9-$P
z{ML5wPKRSBOLbM~a*IskwT^L9cUFaqvv{xx{FH&8`c;19Hwuz#XR>euKlF1M#8~5;
z<6^Y>GkmhKJU4`xpYJJwD63N1%WXjsno4QMN_~fsX02uD>KYboy>=?y&QkolTk
zC5K`I?nQX4Kr1}U4#%>`%dcM8TApNac^;P9bg&O@Bxu&ns}9RiJ6Vo7oZ`o
z)6~Aoc$U=7hTFv__f5)175c#evq6{QsVl*5ov3%G}dWKOUpJF7sbT4
zVO}lf-OBFg`jj9=1g0d(FeQ(rhxd9nFDHAwYO|}mug`awJ}cgvn-lJh>2%R8%}0;&
z;D_^m8af%naln%Y!PkZt$@BvzvmG1U;;X6k3=Yex;`?{bjnccs1C7ot-Ly`$br?CG
z6zFyZ@F2cG%)objR`IJX?`}PHip6kmAV$+a@^Kp2+9<{$j=nCo5Jtbh^~jN}50Bn_
zWcJ9-9EN@-t^-^G{S7Z6SXKa79$V-X+SQ{gJ@btdvoBp(I&yT&KmBvn=bzI&s`>O&
z@m=v>I9<@Ga=;+ILfm7L2rCzVKJC9ebMpHv=E^&7ur^Uif6MV3z(W+qULR9LiFvd`
ze41Vme;D6vq34?|ViiOO8_9?4nh`ff@%)Zl@`%t4n$jK(Ir`E3?I`mau9_#My
z?e2EJ`NE5DzWL${Z?@CMhaYs(#+OwFHzc-`Qm3UlMcA)Uk5#JWJQ&`H6S0uC^PvZ|UL?w;F|UCJI?
zV_$Dm(G6JMi0xFid2*k1jk#szzOW4@tk$lcsiWTCkojz7|>1<0xZw0ki}B#FT+
z+9bre2C7%;ah77Xc^Ka#(p
zyxuuy9qw*)cFB7i>U$rpU#zz8Y-;(|#9Loj%JQqWH+uTpow4qL0lt=Y$is}exVN6Vs7GrnXhOM-)(&eheXtCAR*XKe3ZCqaGA+OJZ@}gY%v?ikoKI9r@ic+H~sHC8U
zSuAy5-}Y!hs;LnZAtG}xR*SV=@}By}-X8jK^SzUAePJ!luioC+umI8h2c+fcA8;d;
z#`pZ6ke85&gzcNwL?Cw~^5D@&g>*%Y%hno*C(k4I2s<_fse-mV9~&Dz9HofN&lzcQ
zICOhUx$d(dUV0gLah_Zo+pMjx&cbKngcm_g@#G~57mps-P??a(
zMFS#M5p`{h9*$N-tij`O9(6c!_9AFxq%m-Yya3%0c)SIdB`DV5no3(t%ap<_jph_9
zUwL6(3cnC}N&FNA&d}drP12DKCN$(Bt~rQU8iyD(NelHc3Tv=Z$0n`AHn*`QX`u&U
zV|w1E)Qu?}8QF0;63ZGbeL!o>H901;8ayR^6$N#r+LVmO$~M1FDL#CS07|lG3XZP7
z!}*-hWQ9(lGwDppI=#Y_OuP2AO-yv$c24z_aw7hzO!~=j+sTu*6d>O?AO;4TX!Jd!tFUUodI5_B
zR6;0{;Hgob?BMyI^_5siY1iSZ#q*7ZU;+g31^k258}?#!OkAusGgBKE8y%CMUDju9
z<+a?bf`Y8vnCRHJR%;(X*`Bm)m8w{kWr~k8WvPo*8*|gNIld;F#-L8mOUo|sTWTmviCWAf+4~lG^Qql?m;WE|%zF()bQF_~G&n+n(>zc}Er?!ucHqRt?
zXa;u9-25|>1?iFsr=_;;(4k15E5}?HN$nfwI{KGm4jznXuhsdK;s=?FKL2g>H5D7He>FPRi*Lv9#4RPS1kMQ?V5f+BC}hHq$smm%lToCE5A&XS{q@&<7cX8s^~%gM
zXQ%0&w(+(np1_4JiGNF{xSS~IAu~+FjNGTwRSXDQndWTkGtac1rMdiimXBo^`!v(g
zPeU?=#QDF6z9br@@@ea_AZNcqnHrb|V8_Snd+z@ZPg_cJUmF)g
zld=q1822qY&hgRTedqq3S6hD0$9X7YMR=8$jt7W4c$$}j`*dW>d0K$$J?xHNYHhB_
zHfmDU>(Vv6YF4MH6Vlc6usquAO<5lwpSe*MSsttfX=tE!X2aU^7y!94_#OJ;cfZ^F
zdh11Mr?cWQ`nt%M9dZ8if{Cp_PI0SoK*E}6Q$RXTE?HpE{E_bo(h2esf-g~|-j_W6
z*y#KFbGqr9U1E5CQBgh${Z^>iv)V8+KRh%)KQufq&LLk~UtLpQUsGK#ejiP9bLbL(
zP;a-_^MZ9T29&Z0_TP9r=mr81
zvvd~C=o5>(k;r~#_u$`UnTpSEJ@&z^xBmOFL+|XWm~xm}l^eRv))BLL#A@i=An%m>
z-Bz31ZL_-R?XMM>Y8IRm_xDWxWapmqGqnbbz2&x#PkilP_sm|{>pRg_S*kZXb~QEa
za_9@Y)$esqPj_}sam5r!5n`^-u!m+MhA6`Vtmd?D47rkd&QOs|;rc=wZ5Rbp68&tZ
zZt{rtrs|fil6?I@WyL_5r~jNGek8v0t1F*nZLTjf6&0Cj?7I#_k885Z+iM)Nx9li1
zHx-w)6&ALZ*#`?Q4t(WDJCCzno5~9{*3AV5!^$ULXuah%Et38Y1-i+Z&|Zc_&d^|J
zC*wL>$vVg-Ar7371g%bgsil2{b({(69PcN;czby3aGd1}5+z*b&2N|V+#%gB96y4L
z%|%%Rybgi)V!RbUzzCPc^MuH3feW}s@RsX-e&GU{rr9vnH%StEh%~Y1iHZFe?n5Yb
zD1X4SNSbH@>7pwnjopj33eqGzhCAAYbYf&Th3@yE?G}>9#z_wQGv2TRrz{gN=ZK2^
zJ&9%iK+NnQDMfLz&A8t~%GfC0zaJr5_EkLJN?cS%YN#x5f$61st8fNmZ$()O!2zBg
z#&ZnO(G!5o`HHBdqOJaIYvsbI;X@V=kmy?X&^Ap6MwBCnClM5M*kMSJKH{Th9Mo~3WF
zFr1*r*kSfEyCe`{y-+5!2~)y8;aTBNvTWIi?6~YnnJCYYTjUe+ee%Qd=j0!SrG&MG
z9S(aW>=Q+_qF7O@7*Xs|998_i;x)zl;j!V>;RE4E!(R;lbwpl-FJec;p@_#L-ir8R
zWLaceSWYYQ7=cm6P+FHiuOe>MemQkFZ!kEOECp8
ze-m>#=F3=_q*;FA;cw#bWB5WsMSPM9hD$B@nu5k&)j!y0s7`UVEDO~cbk`QD3&{D`
zh3Y(KI2fwSk%O2F)x*%f7^*8U-z}kfIDGE6L-lyPmpB=!E8(*rgMCq00s(bTh3beS
z>H1Kekyx4+stcqT--ZOo%P?jtRF{(!x+hc*L;LYiT|uhp$xuBU{9X>#<4HUfLvUs#-;pHr=E-R!;EKfS2(tCkl0
zW8<^_g{`XjiQw!F{<&S#D(C#%(&Fk+@A%mCO|xW_%;Rg1U1WhwV-2Rr65`}=G8%}0
z>`U4i{N1%v#9=$V>#}>grAn^@&j%O
z^!P#dIGL4b+lsb%GJ)$VR3U7dbFvF_sfbgWZwc_L{i}R>CH|+udPnG&k*Z)){_G19$$I$nN>ygYAE~
literal 0
HcmV?d00001
diff --git a/sheep/scss/icon/_coloricon.scss b/sheep/scss/icon/_coloricon.scss
new file mode 100644
index 00000000..f391ca44
--- /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 00000000..f277fc8d
--- /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 00000000..cf7ea082
--- /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,d09GMgABAAAAAA7QAAsAAAAAH7gAAA6AAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACFGAquMKcBATYCJANYCy4ABCAFhGcHghMbGBszo8LGASAo3ovsvzzgDulfoYNbbIQFfeT6cUXKcnp8h6BAxr+OT0PaNfv10KZBODSIpU3sSo3EFhur478eSgk9tB9t3u5+1bOm2u/adRPIVAjdKiUTIiF5p0G7H4liOw9t80+OPDw84PjYX/CsAiMBxaj6kzAy38TIGQsWkbpo6xcu2kX11lw1dxwop50cColKPds3ntdm7TMQ5O5+/WLKMIHXtiAAiNIW12xIQlaYu4Gc3QMp9L00hYPmlmYUAIc59ZTtJbdsO9j2NSK25QLAc0DyxvKXihAq8ZKMoATv/f9zrzYZAHkEYdZa9V6Swn15H145H9pmnAJChmzHijgpJuV0xK6bIiH5+Bk9o+tkhfLTTSUJvfZo7JSOMoWx6YxH4gujkBDUd70OAql9x8x4+frt0KaW2NHmkBOFuKBmcLpD7kZznJkSu0PZHhMdAphlPpPwmn5/fbYxAiNokCc6ffJsOicffk0Me+0L7OXaXBW0bUAALNhes1htvv+VDWGbyikru3MPQOwrBkZTwKn1Plz08HMihm2VwHXeg0GnV6k1WqWCEwAi/AfPSqQyOTFQh+TKAVpqwGCvSnMxpv/A6ED/g9GDeqxChQlGDSowGlADRgtqwShBQzAK0AgMBxqDEYAmIMjRFATQDATQHASztAChCi1BAK1AAK1BAG3AEGgL8Zz0w7gU0MZTgOiRpBsy5RLFCuaCL5xPM0NODq8d2THAOMI4ubYfquKsiEWTK4eh6dLW43a8zOpckM+TjbnHdU1JVlrnxSTBlFHdSMq3EylMQYzHhjubO3aq7d5GobB13euVUhgAmWHC58lnTFrRDCqvztCU9YlrJOoRWxI1TTQmWeFHZ+NL4AbKEAGCSnvFWkQNs09QG4oOPwoNpWp3Igca3Ijt0enb5js0+TSgbcvz9Tps1wO/MfvfIdQN/VsKcnWwUYOTaKVfTMxPs3q66vjgClvbbTYEuoS6SS5OV0RrAdWvsMVLfXlLmcs1lWJhTSVP8rDHqbV5o6jTHIuLhmAA1flCHbspEhMFy864PMEQhCNNHWbhxhk258Iu6jgCf6sh0ILiPgCqts3YJqh/EKNO8AGsmKhmQdqr9at9GVEMbJSj53lt1snRPWqDSpyZxmMEVc9HzZ0oopAvsoXrKhGBfk3BDIABwH1B02U+xWzmM0Djl1mO4vw3BOHrXBr9hNHDMUQKzjQEqXwuDxZcWHRh2VGXJKqPeJO3/AZkVx9v27Q7tlvHnW6av6EgDxCdwsmuOGqBQEBJBve5cgEh+yxKeSj/iGUn9BMLq5EdXKYuW/OBjXp3vKOnKnHtPV8vhm+bYvfmscPd+A5r7efmxFbv1k97dX6Fwuo5nKA8Wg1unahNCDpxH5f4F1e1Avm+J3bkirM/B1AhHkJb4kpDSglrbCUS6Cqxa1VFyupCJ0dNYejan0vb1A8Ka/myLMlEUoiihO6nZuJk5AaJtJSPvodqG/gaybU0QfZtKrRS3O/bidlgV8Z3pKbI4cCo4yVvkkw7RTJ2UNK77Q9PQhPqxex+dB+QEx7whgM5Y3QV/RGOSYliqX7GSzpdq7OoBVTx2PrTMd1+Wd0fdauWJKmBIx995D0bDjNEGwnjvgBQpdML/A1fy1wmurOjsCdwpCei9v3dXPCw/mp6J3bRRPB9J/BPW50dhLadQYN6abeMHYYgl9xP6njrAKPFNiKR9DGX6VaI2bLFUmKKtfsOj5cmUYXWlagKhkFbOArjBptIBaP1RcxULU6yoVsDKN8lLIfQaRX9PIOvgxNsqg9RSccoe+H7LT3aJsOJLTGSgm4BWABvm1gL3igAG/BBHSyADIgUsk69ltYJBhd7rAa1Xrwt0eiCWKPL3U1zN6i0FvTdrbJ3m2qtoG5oQPdWD0c3QfV5WptcB/F2Srp2aHFbOoYcoXuPuXi3f1HUvfenYkQnUUiOVRNj8+RnXgePrgPANUIc4A4Hg0uvD2/GvuCAuhBH/xE3OqRefM8W68GISmsunTYDetfdZgWGjiCjXjBE6u5JiRBELt7MmrRUTYfm/KSFVmKVIk6ozxbI3d3lguxvgpzOJvfIEXzNGfMeXrXvhea5ZO7ZAtKZsKwE+3geP84hcdE+gR8fx090UCXaS3AUtUX7yDCWl2Avygo7dSpsetpwKhhWgO7PndKvF+9a5hyra1mZohfrXOYk1dWpaGZGnujv/usNLOx3nLOW3wpbC+hGcUd8RsgPEFesSP9ftz/f67st6ZdqBeh7ryItSaFS6Cb0HieoRRQZ6X7g4ZrLDbm5kYGmUXz1yOrUwo91/49qMisALeESc954nrnE2ma67UPDEjplso2V3Z9xJ2EJKLAxM80k0Vih2JxokvYv7944UaFIN04z+ZdhTGyZeixzl7i4SNxl74D64hIXmfu7nF1O4RidtL90C50cGO/ruzVOLjt0yMdfaVD8GJ0QyZFETU42avH0mk6RmTg5FEaePkXaPs5Lc2XdYvXOX6wGfcly7aZvvea2Zfl8ALePB1OVvjRmCPsN7421NaQzdRVUqBeiuhZke1mEdqkhOK4a2Av6KuvYS7wlmyXbd7E9AvQ2y4PbKRHXQ1QuBNHyn9wjcXRSdWBQ2su62Z+SBkECP+MgnuVUW7G4CkBV/HGsTxsbw7T7sLAdtqY2R2tci6M9RrO5Scexfq3SJK1+zFbZWlrcSZX5Ootarw3phjEYpl4sBq3Rt5C2El8Tt5JiiDAlui2OYWsgjI9RlLUwJSXMBq1h79WmD1G5+LRBd2cW0hn/oOO27jPw+NbNTciKF1vT7EpMw4zCLIvfjcTJQhFlclguEcGTGhmXzyCTr5Gx88Wh5zzKNeWacUwR6YAjie5N96eLueLlUHkDwv/qo3RCy3Yc+P/q96nzy6qNdz3K0CpE/MWoJ9qW8FtOppYZh+qGRkEEsFaUJZRnuuV4fBdqr0B+DwCsdeIIvTy3+x4Fdlxpab2yfYqI74MYyszySDnEFIc0x+pDcB6cw5EaZg4RxR3Og51H5XKnjiFk4/v5pmmm+MXTNRUKZzo7hR1Zh4w0VomyPQTZwrxGCL9To3HQ4zzLI0+Ue/ZXlV0rUM1LSeVVtfFqUpKreO3dyGQhGFWBU17W1XEpRbxao0Zq3urstlfJe9IaP3lyunrifTS+J9nU3ywwTmhRCFEEDd1oUjw62X6hVsDulmbA0G5Y0KBApcvYinqv9GoqzgC6UcCx+amf39Hg2akQRc+to3PO4usH+Psd+c07FtKDwmjF8CnlyKmiYo+hdXzfXRAconJWOY2PH1G5qIKD7dAEdoJAQn1qbqGHQkY1wUkVUmWprrMwudrU3KL4PJdSKg9JqjEil1N1Bwss9azyLdfpQsCJhxFIjEGhNJXAwhj1vREaEhiJSY4gUsQ9+u7vaydqL68WPnayDAkgsQauI2EscPrq8qXbxvPGLz9+mrec//r1VryXb52+RvxoXzMfExMfM9/NRu31Mbn1N/eLRvFei4T5+cJGIWVpEublCZuB0KANZSh+F4N77vuD5uhXxFHlmOkI4g1RtX+lLC3kNctDQ53ms8ZkjVSDSAC2F4aFHQnhz2jqm/3w1POwQwlk2sIWGs1Hd+Szv4htPWdQfmNzbeqq1uuBN3jYd59PrYsnu8bf/7NNTu6xHmf0zuoYHQoyVlQyOebh645NtqY2EyzyWEOzNs6cxffvfbvUllafm7131z1FrOQR3BJwD0lG9yf4012g8whSrhMIYRGMFtwNUizHmeDkY9mCjkQ3sxhOCUgixQBcZ5x0ZOtGa2AMP4L/UUfMcr8hu7PCpqPT3sau7BhfJduTHQkKixuENuQH2AQ8/rSSNMHW5dNA2Etm+jtogjx7niSDaTEl4F1ChpcMQpU4sZo0Zp0xVEohtKnErSuyKX9MFCF/ZSwwyF7xdIlYYrfMhkLsb46YYSP20Q2XnLyf2bZXBANC3sIUtA3afiuP5U6eIRoQchemIVsSXc4wezTH27ZxKyMTIdlC0wu5jqwZl+e2Pt4+9jAOmYFR9fUwQ2NGlzBdsoe7p7CEGzbIA5wY52ku+oydLzL3dEyD8UDWCRiILoYB19KhGT//u3DlvUeGtja3054Y3FMdtrhmfeHK5/9TziIy6igWMVcaTL67hukZXi94bHBt70iZWSFcnMOUAFzEfqijsXKWwLanNivfnRXVzu4dLjcth33mYMoM7ANjFYuOPlD6gAUHHoOBqFspk1um/AZdWE3dXt1Cr2qh1nxQarSzqNPCzj/agiljFi/nKw5WzkwfyNjpbUW69vVJjKn4E8YoUOWOBwB/AjwFJtCVwFaBLsp23BqW+T5wP2j/+7PvBn91U9LQbLsbbACIaoK4LUh/BSpA+Hd0W3B/5LsqTcP9I5n613r7t723oWOKzeQthy34HSxJi7+a0x47/I9/yTqIf8yEVd/5UfI/5KRmczL6To5oBqR+Eng7qfV1XVydtPSz2tXFi43IAljW2EVwGLu5OJA5hBBjjxantpz0nuk8A6RMgFXPsVhqbxYbpbdgMfYdOIz9sjgwmiDE2H+LU9cqBpZZjWsLToxa0KjioQJQtHYLvdJvdJdac9HOf+QUsF3t81024xUjsg6bdHUHEassU6NevCfDuibVMpUIknuR9rjdWpZNDtR0ToxalssbpXhsHICi1Zf0z/eN7lJrluT/FU6OYF+5spfbSfCvcpSqcU2Kp6s7EFuxag5spzfKS1fBepRKSsvWSoSRP+craO3R1tezsnLevKu5CGROu1pJ92PYZOUUKFSkWMkvum87V6FSlWrFK0GJ4iVIlJ0BmiDDQqcAA08NDhpinIFHqLI6RJwK65haYpk5IpMy0GzGQHWNIIub5hiik1JGCdTofuU6SLZahySDQkM1vUSNiWjWCj66SL/i5nzBJIFiRptDYNAtiIbHL1dTwonBOlyRHyNig5xGZwYy2OkAAAAA')
+ 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 00000000..a2c4dc82
--- /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 00000000..c8419560
--- /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 00000000..e69de29b
diff --git a/sheep/scss/style/_background.scss b/sheep/scss/style/_background.scss
new file mode 100644
index 00000000..775f37f1
--- /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 00000000..4ef1d54b
--- /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 00000000..70693457
--- /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 00000000..17aa6b3a
--- /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 00000000..5221c88c
--- /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 00000000..1daa45b8
--- /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 00000000..91d3eb39
--- /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 00000000..b1b52307
--- /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 00000000..37b023d7
--- /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 00000000..a4a82828
--- /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 00000000..27cb3f63
--- /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 00000000..ad5effa0
--- /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 00000000..e69de29b
diff --git a/sheep/scss/style/_text.scss b/sheep/scss/style/_text.scss
new file mode 100644
index 00000000..82490221
--- /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 00000000..8caad174
--- /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 00000000..af5f245a
--- /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 00000000..1eef5875
--- /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 00000000..b9b73811
--- /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 00000000..28598e3d
--- /dev/null
+++ b/sheep/store/app.js
@@ -0,0 +1,125 @@
+import appApi from '@/sheep/api/app';
+import diyTemplateApi from '@/sheep/api/promotion/diy/template';
+import { defineStore } from 'pinia';
+import $platform from '@/sheep/platform';
+import $router from '@/sheep/router';
+import user from './user';
+import sys from './sys';
+
+const app = defineStore({
+ id: 'app',
+ state: () => ({
+ info: {
+ // 应用信息
+ name: '', // 商城名称
+ logo: '', // logo
+ version: '', // 版本号
+ cdnurl: '', // 云存储域名
+ filesystem: '', // 云存储平台
+ user_protocol: {}, // 用户协议
+ privacy_protocol: {}, // 隐私协议
+ about_us: {}, // 关于我们
+ copyright: '', // 版权信息 I
+ copytime: '', // 版权信息 II
+ },
+ platform: {
+ payment: [], // 支持的支付方式
+ recharge_payment: [], // 支持的充值支付方式
+ share: {
+ methods: [], // 支持的分享方式
+ forwardInfo: {}, // 默认转发信息
+ posterInfo: {}, // 海报信息
+ linkAddress: '', // 复制链接地址
+ },
+ auto_login: 0, // 自动登陆
+ bind_mobile: 0, // 登陆后绑定手机号提醒 (弱提醒,可手动关闭)
+ },
+ chat: {},
+ 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');
+ }
+
+ await adaptTemplate(this.template, templateId)
+ const res = await appApi.init(templateId);
+ if (res.error === 0) {
+ this.info = res.data.app;
+ this.platform = res.data.platform;
+
+ // this.template = res.data.template;
+ this.has_wechat_trade_managed = res.data.has_wechat_trade_managed;
+ // if (!res.data.template) {
+ // $router.error('TemplateError');
+ // }
+ this.chat = res.data.chat;
+
+ // 加载主题
+ 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',
+ },
+ ],
+ },
+});
+
+// todo: @owen 先做数据适配,后期重构
+const adaptTemplate = async (appTemplate, templateId) => {
+ const { data: diyTemplate } = templateId
+ // 查询指定模板,一般是预览时使用
+ ? await diyTemplateApi.getDiyTemplate(templateId)
+ : await diyTemplateApi.getUsedDiyTemplate();
+ // 模板不存在
+ if (!diyTemplate) {
+ $router.error('TemplateError');
+ return
+ }
+
+ const tabBar = diyTemplate?.property?.tabBar;
+ if (tabBar) {
+ appTemplate.basic.tabbar = tabBar
+ 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 00000000..8e076343
--- /dev/null
+++ b/sheep/store/cart.js
@@ -0,0 +1,108 @@
+import { defineStore } from 'pinia';
+import cartApi from '@/sheep/api/cart';
+
+const cart = defineStore({
+ id: 'cart',
+ state: () => ({
+ list: [], // 购物车列表
+ selectedIds: [], // 已选列表
+ isAllSelected: false, //是否全选
+ cartSelectedTotalPrice: '0.00', // 选中项总金额
+ }),
+ getters: {
+ totalPriceSelected: (state) => {
+ let price = 0;
+ if (!state.selectedIds.length) return price.toFixed(2);
+ state.list.forEach((item) => {
+ price += state.selectedIds.includes(item.id)
+ ? Number(item.sku.price/100) * item.count
+ : 0;
+ });
+ return price.toFixed(2);
+ },
+ },
+ actions: {
+ // 获取购物车列表
+ async getList() {
+ const { data, code } = await cartApi.list();
+ if (code === 0) {
+ this.list = data.validList;
+ }
+ },
+ // 添加购物车
+ async add(goodsInfo) {
+ const { error } = await cartApi.append({
+ goods_id: goodsInfo.goods_id,
+ goods_num: goodsInfo.goods_num,
+ goods_sku_price_id: goodsInfo.id,
+ });
+ if (error === 0) {
+ this.getList();
+ }
+ },
+
+ // 更新购物车
+ async update(goodsInfo) {
+ const { error } = await cartApi.update({
+ id: goodsInfo.goods_id,
+ count: goodsInfo.goods_num,
+ goods_sku_price_id: goodsInfo.goods_sku_price_id,
+ });
+ if (error === 0) {
+ // this.getList();
+ }
+ },
+
+ // 移除购物车
+ async delete(ids) {
+ if (typeof ids === 'array') {
+ ids = ids.join(',');
+ }
+ const { code } = await cartApi.delete(ids);
+ if (code === 0) {
+ this.selectAll(false);
+ this.getList();
+ }
+ },
+
+ // 选择购物车商品
+ selectSingle(goodsId) {
+ if (!this.selectedIds.includes(goodsId)) {
+ this.selectedIds.push(goodsId);
+ } else {
+ this.selectedIds.splice(this.selectedIds.indexOf(goodsId), 1);
+ }
+ this.isAllSelected = this.selectedIds.length === this.list.length;
+ },
+
+ // 全选
+ selectAll(flag) {
+ this.isAllSelected = flag;
+ if (!flag) {
+ this.selectedIds = [];
+ } else {
+ this.list.forEach((item) => {
+ this.selectedIds.push(item.id);
+ });
+ }
+ },
+
+ // 清空购物车
+ emptyList() {
+ this.list = [];
+ this.selectedIds = [];
+ this.isAllSelected = false;
+ this.cartSelectedTotalPrice = '0.00';
+ },
+ },
+ 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 00000000..db59bd48
--- /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.globEager('./*.js');
+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 00000000..a665d5d2
--- /dev/null
+++ b/sheep/store/modal.js
@@ -0,0 +1,29 @@
+import { defineStore } from 'pinia';
+
+const modal = defineStore({
+ id: 'modal',
+ state: () => ({
+ auth: '', // 授权弹框 accountLogin|smsLogin|smsRegister|resetPassword|changeMobile|changePassword|changeUsername
+ share: false, // 分享弹框
+ menu: false, // 快捷菜单弹框
+ advHistory: [], // 广告弹框记录
+ lastTimer: {
+ // 短信验证码计时器,为了防止刷新请求做了持久化
+ smsLogin: 0,
+ smsRegister: 0,
+ changeMobile: 0,
+ resetPassword: 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 00000000..f7151e0a
--- /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 00000000..f460565f
--- /dev/null
+++ b/sheep/store/user.js
@@ -0,0 +1,194 @@
+import {
+ defineStore
+} from 'pinia';
+import userApi from '@/sheep/api/user';
+import commissionApi from '@/sheep/api/commission';
+import $share from '@/sheep/platform/share';
+import {
+ isEmpty,
+ cloneDeep,
+ clone
+} from 'lodash';
+import cart from './cart';
+import app from './app';
+import {
+ showAuthModal
+} from '@/sheep/hooks/useModal';
+
+// 默认用户信息
+const defaultUserInfo = {
+ avatar: '', // 头像
+ nickname: '', // 昵称
+ gender: 0, // 性别
+ mobile: '', // 手机号
+ money: '--', // 余额
+ commission: '--', // 佣金
+ score: '--', // 积分
+ verification: {}, // 认证字段
+};
+
+// 默认订单、优惠券等其他资产信息
+const defaultNumData = {
+ coupons_num: '--',
+ order_num: {
+ aftersale: 0,
+ nocomment: 0,
+ noget: 0,
+ nosend: 0,
+ unpaid: 0,
+ },
+};
+
+const user = defineStore({
+ id: 'user',
+ state: () => ({
+ userInfo: clone(defaultUserInfo), // 用户信息
+ isLogin: !!uni.getStorageSync('token'), // 登录状态
+ numData: cloneDeep(defaultNumData), // 用户其他数据
+ agentInfo: {}, // 分销商信息
+ lastUpdateTime: 0, // 上次更新时间
+ }),
+
+ actions: {
+ // 获取个人信息
+ async getInfo() {
+ const {
+ code,
+ data
+ } = await userApi.profile();
+
+ // 为了兼容 获取用户余额 可能还会用到其他参数
+ // 优惠券数量,积分数量 应该在这里
+ const {
+ code: code2,
+ data: data2
+ } = await userApi.balance();
+ if (code !== 0 || code2 != 0) return;
+ data.money = data2.balance / 100;
+ this.userInfo = data;
+ console.log(data2, '信息')
+ return Promise.resolve(data);
+ },
+
+ // 获取分销商信息
+ async getAgentInfo() {
+ const res = await commissionApi.agent();
+ if (res.error === 0) {
+ this.agentInfo = res.data;
+ }
+ return Promise.resolve(res);
+ },
+
+ // 获取订单、优惠券等其他资产信息
+ async getNumData() {
+ const {
+ code,
+ data
+ } = await userApi.data();
+ const data2 = await userApi.data2();
+ let data3 = await userApi.getUnused();
+ console.log(data3.data, '优惠券')
+ if (code === 0 && data2.code === 0) {
+ console.log('订单数据', data);
+ this.numData = {
+ coupons_num: data3.data,
+ order_num: {
+ noget: data.deliveredCount,
+ unpaid: data.unpaidCount,
+ nocomment: data.uncommentedCount,
+ aftersale: data2.data
+ }
+ };
+ }
+ },
+
+ // 添加分享记录
+ async addShareLog(params) {
+ const {
+ error
+ } = await userApi.addShareLog(params);
+ if (error === 0) uni.removeStorageSync('shareLog');
+ },
+
+ // 设置token
+ setToken(token = '') {
+ if (token === '') {
+ this.isLogin = false;
+ uni.removeStorageSync('token');
+ } else {
+ this.isLogin = true;
+ uni.setStorageSync('token', token);
+ this.loginAfter();
+ }
+ return this.isLogin;
+ },
+
+ // 更新用户相关信息 (手动限流 5秒之内不刷新)
+ async updateUserData() {
+ if (!this.isLogin) {
+ this.resetUserData();
+ return;
+ }
+ const nowTime = new Date().getTime();
+ if (this.lastUpdateTime + 5000 > nowTime) return;
+ await this.getInfo();
+ this.getNumData();
+ this.lastUpdateTime = nowTime;
+ return this.userInfo;
+ },
+
+ // 重置用户默认数据
+ resetUserData() {
+ this.setToken();
+ this.userInfo = clone(defaultUserInfo);
+ this.numData = cloneDeep(defaultNumData);
+ this.agentInfo = {};
+ cart().emptyList();
+ },
+
+ // 登录后
+ async loginAfter() {
+ await this.updateUserData();
+ cart().getList();
+ // 登录后设置全局分享参数
+ $share.getShareInfo();
+ // 提醒绑定手机号
+ // if (app().platform.bind_mobile && !this.userInfo.verification?.mobile) {
+ // showAuthModal('changeMobile');
+ // }
+
+ // 添加分享记录
+ const shareLog = uni.getStorageSync('shareLog');
+ if (!isEmpty(shareLog)) {
+ this.addShareLog({
+ ...shareLog,
+ });
+ }
+ },
+
+ // 登出
+ async logout(force = false) {
+ if (!force) {
+ const {
+ error
+ } = await userApi.logout();
+ if (error === 0) {
+ this.resetUserData();
+ }
+ }
+ if (force) {
+ this.resetUserData();
+ }
+
+ return !this.isLogin;
+ },
+ },
+ persist: {
+ enabled: true,
+ strategies: [{
+ key: 'user-store',
+ }, ],
+ },
+});
+
+export default user;
\ No newline at end of file
diff --git a/sheep/ui/su-coupon/su-coupon.vue b/sheep/ui/su-coupon/su-coupon.vue
new file mode 100644
index 00000000..472d17fe
--- /dev/null
+++ b/sheep/ui/su-coupon/su-coupon.vue
@@ -0,0 +1,319 @@
+
+
+
+
+
+
+ {{ type === 'reduce' ? value : Number(value) }}
+ {{ type === 'reduce' ? '元' : '折' }}
+
+ {{ props.title }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ type === 'reduce' ? value : Number(value) }}
+ {{ type === 'reduce' ? '元' : '折' }}
+
+
+ {{ props.title }}
+ 仅剩:{{ props.surplus }}张
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ type === 'reduce' ? value : Number(value) }}
+ {{ type === 'reduce' ? '元' : '折' }}
+
+
+ {{ props.title }}
+ 有效期:{{ props.sellBy }}
+
+
+
+
+
+
+ 仅剩:{{ props.surplus }}张
+
+
+
+
+
+
+
+
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 00000000..537ead5f
--- /dev/null
+++ b/sheep/ui/su-data-checkbox/su-data-checkbox.vue
@@ -0,0 +1,894 @@
+
+
+
+
+
+ {{ mixinDatacomErrorMessage }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/ui/su-dialog/su-dialog.vue b/sheep/ui/su-dialog/su-dialog.vue
new file mode 100644
index 00000000..b53e9ce1
--- /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 00000000..e2a98088
--- /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 00000000..35f84103
--- /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 00000000..9fc102da
--- /dev/null
+++ b/sheep/ui/su-inner-navbar/su-inner-navbar.vue
@@ -0,0 +1,365 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ title }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/ui/su-navbar/su-navbar.vue b/sheep/ui/su-navbar/su-navbar.vue
new file mode 100644
index 00000000..0c8eee9b
--- /dev/null
+++ b/sheep/ui/su-navbar/su-navbar.vue
@@ -0,0 +1,482 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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 00000000..448b442d
--- /dev/null
+++ b/sheep/ui/su-notice-bar/su-notice-bar.vue
@@ -0,0 +1,472 @@
+
+
+
+
+
+
+
+
+ {{ text }}
+
+
+
+
+
+ {{ moreText }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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 00000000..02aa08ff
--- /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 00000000..adff127c
--- /dev/null
+++ b/sheep/ui/su-popover/su-popover.vue
@@ -0,0 +1,314 @@
+
+
+
+
+
+
+
+
+ {{ tips }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/ui/su-popup/keypress.js b/sheep/ui/su-popup/keypress.js
new file mode 100644
index 00000000..6141c4c8
--- /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 00000000..b55b0074
--- /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 00000000..b6aff5af
--- /dev/null
+++ b/sheep/ui/su-progress/su-progress.vue
@@ -0,0 +1,200 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{ percentage }}%
+
+
+
+
+ {{ percentage }}%
+
+
+
+
+ {{ percentage }}%
+
+
+
+
+
+
+
+
+
diff --git a/sheep/ui/su-radio/su-radio.vue b/sheep/ui/su-radio/su-radio.vue
new file mode 100644
index 00000000..6fc4f74e
--- /dev/null
+++ b/sheep/ui/su-radio/su-radio.vue
@@ -0,0 +1,301 @@
+
+
+
+
+
+
+
+ {{ label }}
+
+
+
+
+
+
+
+
+
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 00000000..ab3d3cdc
--- /dev/null
+++ b/sheep/ui/su-region-picker/su-region-picker.vue
@@ -0,0 +1,246 @@
+
+
+
+
+
+
+
+
+ {{ province.name }}
+
+
+
+
+ {{ city.name }}
+
+
+
+
+ {{ district.name }}
+
+
+
+
+
+
+
+
+
+
+
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 00000000..40709d2a
--- /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 00000000..a8831a26
--- /dev/null
+++ b/sheep/ui/su-sticky/su-sticky.vue
@@ -0,0 +1,264 @@
+
+
+
+
+
+
+
+
+
diff --git a/sheep/ui/su-subline/su-subline.vue b/sheep/ui/su-subline/su-subline.vue
new file mode 100644
index 00000000..c11d176a
--- /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 00000000..cff1c4b3
--- /dev/null
+++ b/sheep/ui/su-swiper/su-swiper.vue
@@ -0,0 +1,502 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ state.cur + 1 }} / {{ props.list.length }}
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/ui/su-switch/su-switch.vue b/sheep/ui/su-switch/su-switch.vue
new file mode 100644
index 00000000..d63e8004
--- /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 00000000..615df2f4
--- /dev/null
+++ b/sheep/ui/su-tab-item/su-tab-item.vue
@@ -0,0 +1,169 @@
+
+
+
+
+
+ {{ props.data.title }}
+
+
+ {{ props.data.tag }}
+
+
+ {{ props.data.subtitle }}
+
+
+
+
+
+
+
+
+
diff --git a/sheep/ui/su-tab/su-tab.vue b/sheep/ui/su-tab/su-tab.vue
new file mode 100644
index 00000000..17a79836
--- /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 00000000..2fc5696f
--- /dev/null
+++ b/sheep/ui/su-tabbar-item/su-tabbar-item.vue
@@ -0,0 +1,233 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ text }}
+
+
+
+
+
+
+
+
+
diff --git a/sheep/ui/su-tabbar/su-tabbar.vue b/sheep/ui/su-tabbar/su-tabbar.vue
new file mode 100644
index 00000000..3b6d2459
--- /dev/null
+++ b/sheep/ui/su-tabbar/su-tabbar.vue
@@ -0,0 +1,226 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sheep/ui/su-tabs-item/props.js b/sheep/ui/su-tabs-item/props.js
new file mode 100644
index 00000000..3908c369
--- /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 00000000..71396847
--- /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 00000000..05d7dcb3
--- /dev/null
+++ b/sheep/ui/su-tabs/su-tabs.vue
@@ -0,0 +1,434 @@
+
+
+
+
+
+
+
+
+ {{ item[keyName] }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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 00000000..9c3f8c1b
--- /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 00000000..ddf7a0ba
--- /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 00000000..17351ece
--- /dev/null
+++ b/sheep/ui/su-toolbar/su-toolbar.vue
@@ -0,0 +1,129 @@
+
+
+
+
+ {{ cancelText }}
+
+
+ {{ title }}
+
+
+ {{ confirmText }}
+
+
+
+
+
+
+
+
diff --git a/sheep/ui/su-video/su-video.vue b/sheep/ui/su-video/su-video.vue
new file mode 100644
index 00000000..a75a79c9
--- /dev/null
+++ b/sheep/ui/su-video/su-video.vue
@@ -0,0 +1,199 @@
+
+
+
+
+
+
+
diff --git a/sheep/url/index.js b/sheep/url/index.js
new file mode 100644
index 00000000..61046722
--- /dev/null
+++ b/sheep/url/index.js
@@ -0,0 +1,199 @@
+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_' + gravityFormat('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/' + gravityFormat('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/' + gravityFormat('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 gravityFormatMap(type, gravity) {
+ let gravityFormat = {
+ 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 gravityFormat[type][gravity];
+}
diff --git a/sheep/util/const.js b/sheep/util/const.js
new file mode 100644
index 00000000..9bf6ae22
--- /dev/null
+++ b/sheep/util/const.js
@@ -0,0 +1,47 @@
+// ========== MALL - 营销模块 ==========
+
+/**
+ * 优惠类型枚举
+ */
+export const PromotionDiscountTypeEnum = {
+ PRICE: {
+ type: 1,
+ name: '满减'
+ },
+ PERCENT: {
+ type: 2,
+ name: '折扣'
+ }
+}
+
+/**
+ * 优惠劵模板的有限期类型的枚举
+ */
+export const CouponTemplateValidityTypeEnum = {
+ DATE: {
+ type: 1,
+ name: '固定日期可用'
+ },
+ TERM: {
+ type: 2,
+ name: '领取之后可用'
+ }
+}
+
+/**
+ * 营销的商品范围枚举
+ */
+export const PromotionProductScopeEnum = {
+ ALL: {
+ scope: 1,
+ name: '通用劵'
+ },
+ SPU: {
+ scope: 2,
+ name: '商品劵'
+ },
+ CATEGORY: {
+ scope: 3,
+ name: '品类劵'
+ }
+}
diff --git a/sheep/util/index.js b/sheep/util/index.js
new file mode 100644
index 00000000..2a3ca202
--- /dev/null
+++ b/sheep/util/index.js
@@ -0,0 +1,102 @@
+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 {number | string | undefined} num 分数
+ * @return {number} 整数
+ */
+export const convertToInteger = (num) => {
+ if (typeof num === 'undefined') return 0
+ const parsedNumber = typeof num === 'string' ? parseFloat(num) : num
+ // TODO 分转元后还有小数则四舍五入
+ return Math.round(parsedNumber * 100)
+}
+
+/**
+ * 时间日期转换
+ * @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) {
+ // 日期不存在,则返回空
+ 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;
+}
\ No newline at end of file
diff --git a/sheep/validate/form.js b/sheep/validate/form.js
new file mode 100644
index 00000000..f3e338d6
--- /dev/null
+++ b/sheep/validate/form.js
@@ -0,0 +1,230 @@
+/**
+ * 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 account = {
+ rules: [
+ {
+ required: true,
+ errorMessage: '请输入账号',
+ },
+ {
+ validateFunction: function (rule, value, data, callback) {
+ if (value.length < 5) {
+ callback('账号长度不能小于5位');
+ }
+ 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 username = {
+ rules: [
+ {
+ required: true,
+ errorMessage: '请输入用户名',
+ },
+ {
+ validateFunction: function (rule, value, data, callback) {
+ if (value.length < 5) {
+ callback('用户名长度不能小于5位');
+ }
+ 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 const consignee = {
+ rules: [
+ {
+ required: true,
+ errorMessage: '请输入收货人姓名',
+ },
+ ],
+};
+
+export const region = {
+ rules: [
+ {
+ required: true,
+ errorMessage: '请选择您的位置',
+ },
+ ],
+};
+export const address = {
+ rules: [
+ {
+ required: true,
+ errorMessage: '请输入详细地址',
+ },
+ ],
+};
+
+export default {
+ mobile,
+ alipayAccount,
+ bankCode,
+ bankName,
+ realName,
+ password,
+ code,
+ account,
+ consignee,
+ address,
+ region,
+ 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
zcmYLNcRZEv`&fSHPlip|8>z#4cH|2>Q}K+TkK
zv6704hRVv^))43th$K8=7l$YA?oxJk0FVIyZ6cXWAdvtNi6jzfpFkk(?(P8qx;s02
z#D7fE4uwJ{|KkINuuCTFkO3-Cc6Sem|EwJVG$L`IxC0oW5QzJGdwc&>J0ze%p-_M|
z0SWP64G?w-1i;h|AP3gM0U+++2_ym$AdqnQ-JP9XJbw4|^b~Lvb8?Eo5wQU9J6J3M
zk3R((M@PpPEa4w;1T-*(Jws#hXbb_3A)+uiGzPzgKmj!h{SQbq7LCT8otpiiI@#Vvp)gp)HWDBZcgT1G0pP>8QHX6c
z5{219AQ1>83mKPRb8=LS2=+fra7BB^cEdv0iw*P5SsDF~>jje4s9Dyh7EWnm=IN~-E
z16^8Pgf9KZUs+jMU4%lBNbK^;>cPPwFb-@A5Y*E03gFw};Sm(J3R_-(D
zT3=rW!r9o^+}zy4U@!#Y?)>}$;5G`41LC6W?UTsl?QP`T+#C=ufk4>ZB~MKL0X!TY
z8HFvcO-;}I9vxd<-|Xt{nVFrN{4)idB_OfizW>1C@x6UuAPUIOf#Z{t<<*U$U&BBe
z*EY7yCv+QuJJ1tkZD~UlmtA3LW@2PzpsVMgW38oOcSG&IioCOutBdRvX*bCy;yzwt
z!cYALFGheuqk{u^E+oDx$a|Swd_IG*l%=7r>K*$>`j#(kjcnh3boBI{LWTw=Mrfi&
zohGTzP0y_?z&O>M%xSOb9MswH|3+_A%nT`S4s|5&^cN@iZqM~L=aO&(FAR8h5F0Dw
zL!ZKA_eNr!Ob~SyWf`Eumh^aU2V;eix)&Z6z+r96d+8xYBF;?ocXl?Bakwqm%iWDp
zG!+%sjETW*+b}40qM0dCmx`)S6m^uVpr7>aw*m+K`qws29znridZX!)ODjLv6oAP8
zzkpbB7Ba(&VkfM=-z}T!g4Z^|qMvRF>SIH)FbU}{omXtke`a{>lrP8R+}hzwbGiF07pB~phEmI|M^#40
ze3>!cwYwTd7-VC>YsVfTPtybyn-HOy4HC*sTPB34%j&+PX04X|VVzAA57XA5S|y0<
zxzyyRSK1$Fro?HZm@kz3&Um+mYgbfM==M!a%-lsUuZu=6CpN!@T!kq4-?;CyCR_-8
zlx}r33_qx)%3YwXKH^0yOCZ-es|7Txm&vNFway;7
z6%MIWi=(@&q)|VyO?{bz5+R$el1lG?QkMU#_V*p~-zzLpYj(;gp39WH{_N+W?=E-U
zJ6BTuP@|r&3}NN%8Wg1VieHfBL1W*~7JGrt!*pYK?L#G-+;p=PhSaEIUAlTIVXDiQ
zg#{Bd^LjO&ruCK7oyc?8i`|F5fmnh|U95KpZ_{2b>(^yyp&8Q%l*${NihnDrERY3N
z<@4i`<{@cf`02ZK99Xd0wSSRa=M{rLdL(g3
z;_B~X@h~5JN$-VoiRa5ChR4UNl2l#2z)sXkVPl?wRDFx=gg1QHY?S(kt&-Gw)x2cu
zxc*m`MGsSU2+Ez9#H7e1Cj1Hx;!g?-e<;0uV~2RCdVny(b=T#Xr)IhEq|%A=heoBQCV+8K>ZW?Ga4gl
zrLg2AcOlL`qlyt`yk`)cdecSGb>N~+8(Y_qsMwnXFou%u%2_I#@sV=xjVZX~PCX-i
zvo4}H#YT$1D>pw1_na+Zi#F#>^h+IhQ`C2vGQX#sE4kA?LDt5tZynWT3+Ac!J
ziL=A(^i6-H)i#AhWmEYW_BFN`66as0J)5h#Z=>FcuK)vW9mCdof2A-s`g2#)1rcO9
zc5NYP4bVzP+O5%(=Z}WhzT-k7tN8om{r;56^0T^p)_eY2Q!R9aZho1j(LlSmqZ&-B
zCdiKvY%?2x-e9mg><2H8_IF|n39Ne#dkhRQUtUBi$?(2?_Tdhkm4Tau+Q_?gvCsGs
z1bn7JrdE#os`YWQ%Z~FvQFZ^Q#6XAv`mWzvLrZs~9F=s$tmrq5<
z>l&?YW@!l+^YDVR;YJP8BvXWk6`EJE&mpsl<1eq+&Z#YYFDn&tiR{XF6KNtEe2suX
zgtrHF?~gLU{NDZ`6vFs^Dm;?p^M#j)8Mf2JnwwhJhtYlK!w(&;#&LakXE4jcR>fMs
z;By~KAA8kg%vPeO!y8?HMwcmW+Ee-d^P>j3?C1gTTl-#P&O0uX>%yA+muBE_cR@kr
z9+F_~_0IU-eH+`2aPv51KM@WH@TQ(2Zj~yx^)koO|}W0swEazc%?_?%My;>uovP51)AVntC1q3bloXBG;jCmy&mEV-aPI|`$OH)QMxO@
z>geTmtvu~=KKrv@kp9N461IqmQdX4QQoCj6-TR(H-WEzr=_4iiQ#ivH=Pk+8wY_dW
z@jinQOEKI23w|pBIAmIA;Pvq_m7u2g+XAkPX=8AQvp5z|-P`+$vLkhcb;uA_48lK<
zV3~;*-(T9D(oc7nZH(d%o7dxF8jmX7kZh7+4wL1Z<@$Y(qp&~`I|aI{V40Vhx_9XO
zz(RoT0wzr_rdwk(EQYRv?^uDNxJmjWl2YcPeMoTc(d3OWRc_0Xy!=%n>zux6)xvo%
zw`TM2$k328?2H^-9(ksAr_4h>sv+C#;4)l%A|JzM1dV-35OO$V
zl^-O>TI#=6{{CV+3cLw_SQ7fM|jC8wN98_FP3|sP1BqP_crDv
z^Nt2f)f$Tg$Dp54NK{&~QHXkX1V9hcT%62;)
zzxCrry-GO)7Z2i?Mmt>QW{z+Ox2-
zsA=FSqVTehsZY;qr(vwl6NM)(&IXapH&bQD*cB*0_owwmVrIm`LVBl)FRlwgA;a)u
ze-+`rSf)XX7piYoIy^!=!*i0O^d{cr#hX*;wnr>9{{fJ`rAe?_QV_T4x~7i}@jPx3@T7
znqMgry!6?Os>x)WR^Dg#Ac&Ck#F(8WLXkf=WEj3VZM0k^UA#Y;oto~UbV=t#q7(ii
z!GU$UQo4c9YMvsI{a8i$)Qfn7<407NMTYb>NH|D1xa^NmIon4?{`_!xH~wY?^bG3}
zEA_N~wnED~i*!ICH~;h7@3vVj^g+f-hIgC((sI6;s-@ckbsUkP5%P;SeRY>zYhw
zS5BAoDR*{d71!CLO9gRrS`d3)yL-0r1t9`ijB=_UyIl(;Qf6LbW^c5^8k?LjNm0F`
zcAB;k{fw{ek}n>feqIxML|#*vPpq1Iuqpr-za;4(Ics?7Gplj#J-sYt?j1Us7Wg{Y
z^f6|(17`KT$K_Lq(|Ti3>7%Qk2I*_1b3gqoUgTvp&gvl>4Z!^M&~uvRUrV9F_~Vbs
zMt5B_KM08yiPzXP)2cpxq%Wq$Ub|`K(^mnjzgKqWJx>?=aRX+}y(tn2bC$1Pr1Wg7vjBD$8&>0Nh^_#X#GJ*(my=U{KPzj_64~@vi60@%l
z_E_=JbXc!yoFN#IxGHr2@<&zKn#!b!3SrdTorT;Qhzw6!(*48X<g>xqhA#5`8Xx
z5t-ZaCAi4s$J0==_L;$0E(7Z7$&5*RD@o4V)LO}&-V!3_#+L*Xp=P(W55#(OX&+rm
z-Czmh1ZRXH2Udf8rhe^Rx6r$ls=K!bH;U29WqajC3)yieTe{;ve93pcB4y@tFu~V2
zIF3oJ{V|ZBXyMQA$ZP2E=jMgvc4A1xmqWCg?9GJMDc7?Y(HPrE7~w(INJMv}Pc0|2
zg0`p|E@Q!>?V8|0dnXEvczR4ihMk?>Jf0>%B6Bl&23Z=)vm!!kIg>?lq<&VEx`wb+
zNv%f~{QP)?0$Uw#etAY+McjIQ-%PRMsYrfFJb`KW8&AKMLr+H%r<|AmW3#2xA`#YT
z&mzd&@`ERv0g(jyoSfTO{-i|}Y8Go_Gq;^x$VG7CoPA?U%$a52tQWhxLR-K0kp3@i
zl@Zkh*ajMLI=^}{GdBdw8ZTw2h0W6xT8}xZK$Xv5zFV%~hllnP>lJ(NctKTD*Tz3a
zbcD%sY9xR4#7ly@GtWr49(Or;(YofA$=l}EN&kX-;Fo;R
zi1_S`$S0LJPHwaM#JUV*o3DtC|0@+Dxl;X+Va))^kt-u#+{R%2&PE(u>5n44I;&c*K7;w|dA{R9m_X2JTb)yq3=EN$g^1
z=DHqLS20QdD&}ur-4=gUw7bk>1+b#w)MsT|o`q(Cx#pCh(NABlj3=;-xw>V8#GZ^X
ztR%%_%GMl%(lh8maPcxyyY;UTCLh(Z3oND$a?(;g;>QwUH8YN~wV51ze=p9|e4c#&
z{Qi0Z&jr7>hRnGTCZ^9O;Je;m#OW`Ko?qe|@&98_jdP}6;%$wMfz`P4q&^m7hI}_>
zWVx`g{DB9e<|ZJjn`LWJWxD_CTWFUFH;602e^_btoXBw(?1cL1e)4b|=g#BU6faSJ
ziz~%?U#I*0+43!HxH`u}S|G((=Zn=Ey3`@RwTlLnB?I|=_MTo$A!&WZl`4lh_yr5w
zt<#v|WM7=~ID-Laegt7|Qca_Jl&2i1D>EFFYa`irt<2l7vZw_mQ!hU2c9Vy>QoK?B
z;OxQLC6CUdrpVm_`N~V@Uu!yJB0G`_*`gc$C4;b2<;22+DsoV>Rc*etsVq?Oks0Qy
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
zcmaiZcQ~70__w_&>T6S_s6A@bsH)mUTeWM}teRDO)>f<29<^(aB36S?v-TzuD-t3R
zA@=w^zxRFre?M1pKli!My`IlG&q=Q9NqS+VLr=p=LqtSGucxbN3|w*l`=ufUBy}>!
zPa-0cs}}|)TEK9Be;>1du)mMnJ2*gN0pM{5hZqbFhdaU^pC29_q0zWqGk4CY`D2h8qa4i5JAa2O1Jf1j|syAOmD2m~zlXz%b4kH-VK4h{~0
zRC{|^zz$T|!{7k9i`m}=um=yFME$RvnVnr&SeT!m-`YmPU~t4X63{m`x8~;NYHMp}XXmyM
z+w%*H3k!=tZos*^x_V^v-|XBRq;C)iL85l&=NFKu9Y6y@Iy$@N=I7Ve*C(f@W@l$7
zCMSWZ@$m`N&Mt7@@b!)Ljji>KO~4aCWo6~a$Y|f+A;2;*F}b+72zUal0Q~^^))u0v
zsR>Xgr>22IfCXp_0AhQ4W@cs+fdCoL^6Kj9`uh6x^bDYsaC`#Tj!#aH2*x5fK>oC1_ry|(bYXOI|nd~+(85QH#QD5kKV%q3?PxnjSYYm)cX3y(9j5WAK0IJ
zn_GyDEoA@Sp_W!q8|Y7OZ$B6eZfpdVl$7M>=WFJCdJH@Ooga*iUJ*Su)RKRpDrSC{
zhtvXUag0)iYioL084-!R&jVff=1nKAhKMbfxR4HO8TxgDr
zjf|$j;oQnuOfM{yWQGldLZ4#)B|h9EQ!LEp$<58^Eso+)*Yi+QDNm$vp~;px8&;x|
zb{v8ZZk3ObKA><{RZ;Pb|L4N>HY|W5>xuN6rGtVq<7;?KP9MCDw#~9%Or*;G09O3QHE?OFd1K;peQFMUKjw@`u4uK7
zQ|P(B^?JMG;NlH^A4Onj9G}(s#5Sk}VBpUQbesIcr}_-qGNrk1XIcwqtd@n+|Ba2k
z;DA{B?XK~wbElVUw(!e+o_{nY@>f;4r-HFyaAD_lP@7FrgV>3Q-J^~xN_BRS#FFET
zXA5zD3F}1rZN>j@Q>`VK0CVuvp)`pff3y$~3HsI+UCyerGA)nVYVrTP%vm{J!|grx
zX_S^2e1C(@c=hB?uo!4SA}vcRU;hSNJMWGe$&bumI%eWa$;86mX@Ond{>W8s9G0#d
z4)Ok7uO{*6%acG>-80wSI4p7$5dp2nZz5gDRObRGI8=yo>|=S5oFM1T^>}yXTBMU>{cG%ii$PSUy|1
zQg1Pb0F#WGsGG?755qHgI?^EO;GnC1mHTxLI3nigU#8Rv)P;ZX)-1NC+J5Kxs~TT!
zUi8u&RPBCpiTBlMxT$6Cl6gn_}-~}ZDr_mL9$zpAo<8lE?y7OcHv#9
z38NV-mTL!9XgQ3Y(#XM%m~oWe2L-L`4yDl5P{LwwAdzqEL>%}^*MgFCpj=yc$VxRg
z`azu(;aE=0gQKtaKM>LBItPlDgVzgPC)}DxjA+y)X5mc`Zm1a%ZjnV4)aHJaGF}L@
zZE}VdnFiW$q>YoELfb11%iN#-(g`eFp-yxPXzsZy-e2W*f|GsMgr!t_*Z_XZ$XU``
zhQW4jJkP?vDK;km2r1D4MUo|MB?%^wQQh~bfs_pxbNJsPwg1{!UPmfdg+wFBr*;MeuLn-9
zY-xOZ(jy&_E0S~mP3LzRwRRKRet!8G2xpm7UeKS$FskC&G}}jdLsKjrZ`+?)#EDrW
zf3~u6z%_g?u*=gFWuc4gl7nwzt)6haQJY0Oi_exAYtwSoXJmNERDs0MS+r!
zF4Z5>regyZ^s?jp`Kt1^6t82pk{mbuVxIFNReNr2dTwyMKxnJP83lPt!ZuH4BFdY^4&K+?c-TuOX
zsX$8LM|UGM+us;l8nZhdr*ouDpzQX7KUtHYlrUNofr`>9Kv5(okp(IQ-~LdhFX$)-PaZ
zb-(&jQ&vSu5vo*R