From 3864983604cb75b3b064f95e2cbe71b5745250e0 Mon Sep 17 00:00:00 2001 From: joeyzzeng Date: Mon, 23 Sep 2024 17:00:20 +0800 Subject: [PATCH] feat: add tcb-shop miniprogram example --- miniprogram/tcb-shop/.eslintrc.js | 157 +++ miniprogram/tcb-shop/.gitignore | 16 + miniprogram/tcb-shop/.npmrc | 9 + miniprogram/tcb-shop/.prettierignore | 3 + miniprogram/tcb-shop/.prettierrc.yml | 49 + miniprogram/tcb-shop/LICENSE | 9 + miniprogram/tcb-shop/README.md | 302 ++++++ miniprogram/tcb-shop/app.js | 18 + miniprogram/tcb-shop/app.json | 76 ++ miniprogram/tcb-shop/app.wxss | 3 + miniprogram/tcb-shop/commitlint.config.js | 1 + miniprogram/tcb-shop/common/updateManager.js | 29 + .../components/cloud-template-dialog/index.js | 41 + .../cloud-template-dialog/index.json | 9 + .../cloud-template-dialog/index.wxml | 13 + .../cloud-template-dialog/index.wxss | 27 + .../tcb-shop/components/filter-popup/index.js | 36 + .../components/filter-popup/index.json | 6 + .../components/filter-popup/index.wxml | 18 + .../components/filter-popup/index.wxss | 39 + .../tcb-shop/components/filter/index.js | 84 ++ .../tcb-shop/components/filter/index.json | 6 + .../tcb-shop/components/filter/index.wxml | 37 + .../tcb-shop/components/filter/index.wxss | 50 + .../tcb-shop/components/goods-card/index.js | 141 +++ .../tcb-shop/components/goods-card/index.json | 8 + .../tcb-shop/components/goods-card/index.wxml | 16 + .../tcb-shop/components/goods-card/index.wxss | 136 +++ .../tcb-shop/components/goods-list/index.js | 62 ++ .../tcb-shop/components/goods-list/index.json | 6 + .../tcb-shop/components/goods-list/index.wxml | 16 + .../tcb-shop/components/goods-list/index.wxss | 7 + .../tcb-shop/components/load-more/index.js | 54 + .../tcb-shop/components/load-more/index.json | 7 + .../tcb-shop/components/load-more/index.wxml | 31 + .../tcb-shop/components/load-more/index.wxss | 35 + .../components/loading-content/index.js | 23 + .../components/loading-content/index.json | 6 + .../components/loading-content/index.wxml | 11 + .../components/loading-content/index.wxss | 23 + .../components/loading-dialog/index.js | 8 + .../components/loading-dialog/index.json | 8 + .../components/loading-dialog/index.wxml | 5 + .../components/loading-dialog/index.wxss | 11 + .../tcb-shop/components/price/index.js | 71 ++ .../tcb-shop/components/price/index.json | 4 + .../tcb-shop/components/price/index.wxml | 21 + .../tcb-shop/components/price/index.wxss | 66 ++ .../tcb-shop/components/swipeout/index.js | 79 ++ .../tcb-shop/components/swipeout/index.json | 4 + .../tcb-shop/components/swipeout/index.wxml | 174 ++++ .../tcb-shop/components/swipeout/index.wxss | 18 + .../tcb-shop/components/webp-image/index.js | 86 ++ .../tcb-shop/components/webp-image/index.json | 6 + .../tcb-shop/components/webp-image/index.wxml | 14 + .../tcb-shop/components/webp-image/index.wxss | 0 .../tcb-shop/components/webp-image/utils.wxs | 140 +++ miniprogram/tcb-shop/config/eslintCheck.js | 91 ++ miniprogram/tcb-shop/config/index.js | 6 + miniprogram/tcb-shop/config/model.js | 12 + miniprogram/tcb-shop/custom-tab-bar/data.js | 22 + miniprogram/tcb-shop/custom-tab-bar/index.js | 29 + .../tcb-shop/custom-tab-bar/index.json | 8 + .../tcb-shop/custom-tab-bar/index.wxml | 18 + .../tcb-shop/custom-tab-bar/index.wxss | 9 + miniprogram/tcb-shop/jsconfig.json | 5 + miniprogram/tcb-shop/model/activities.js | 7 + miniprogram/tcb-shop/model/activity.js | 18 + miniprogram/tcb-shop/model/address.js | 31 + miniprogram/tcb-shop/model/cart.js | 324 ++++++ miniprogram/tcb-shop/model/category.js | 206 ++++ miniprogram/tcb-shop/model/comments.js | 338 ++++++ .../tcb-shop/model/comments/queryDetail.js | 14 + miniprogram/tcb-shop/model/coupon.js | 39 + miniprogram/tcb-shop/model/detailsComments.js | 30 + miniprogram/tcb-shop/model/good.js | 25 + miniprogram/tcb-shop/model/goods.js | 7 + .../tcb-shop/model/order/applyService.js | 295 ++++++ .../tcb-shop/model/order/orderConfirm.js | 147 +++ .../tcb-shop/model/order/orderDetail.js | 26 + miniprogram/tcb-shop/model/order/orderList.js | 46 + miniprogram/tcb-shop/model/promotion.js | 21 + miniprogram/tcb-shop/model/search.js | 60 ++ miniprogram/tcb-shop/model/submitComment.js | 58 ++ miniprogram/tcb-shop/model/swiper.js | 39 + miniprogram/tcb-shop/model/usercenter.js | 52 + miniprogram/tcb-shop/package.json | 46 + .../pages/cart/components/cart-bar/index.js | 59 ++ .../pages/cart/components/cart-bar/index.json | 7 + .../pages/cart/components/cart-bar/index.wxml | 14 + .../pages/cart/components/cart-bar/index.wxss | 80 ++ .../pages/cart/components/cart-empty/index.js | 23 + .../cart/components/cart-empty/index.json | 6 + .../cart/components/cart-empty/index.wxml | 6 + .../cart/components/cart-empty/index.wxss | 33 + .../pages/cart/components/cart-group/index.js | 168 +++ .../cart/components/cart-group/index.json | 11 + .../cart/components/cart-group/index.wxml | 35 + .../cart/components/cart-group/index.wxs | 5 + .../cart/components/cart-group/index.wxss | 335 ++++++ .../cart/components/cart-group/utils.wxs | 20 + .../pages/cart/components/goods-card/index.js | 232 +++++ .../cart/components/goods-card/index.json | 9 + .../cart/components/goods-card/index.wxml | 46 + .../cart/components/goods-card/index.wxss | 260 +++++ .../cart/components/specs-popup/index.js | 48 + .../cart/components/specs-popup/index.json | 7 + .../cart/components/specs-popup/index.wxml | 16 + .../cart/components/specs-popup/index.wxss | 68 ++ miniprogram/tcb-shop/pages/cart/index.js | 310 ++++++ miniprogram/tcb-shop/pages/cart/index.json | 11 + miniprogram/tcb-shop/pages/cart/index.wxml | 12 + miniprogram/tcb-shop/pages/cart/index.wxss | 13 + .../coupon/components/coupon-card/index.js | 57 ++ .../coupon/components/coupon-card/index.json | 7 + .../coupon/components/coupon-card/index.wxml | 23 + .../coupon/components/coupon-card/index.wxss | 9 + .../components/floating-button/index.js | 17 + .../components/floating-button/index.json | 6 + .../components/floating-button/index.wxml | 14 + .../components/floating-button/index.wxss | 30 + .../coupon/components/ui-coupon-card/index.js | 87 ++ .../components/ui-coupon-card/index.json | 7 + .../components/ui-coupon-card/index.wxml | 55 + .../components/ui-coupon-card/index.wxss | 147 +++ .../coupon/coupon-activity-goods/index.js | 75 ++ .../coupon/coupon-activity-goods/index.json | 10 + .../coupon/coupon-activity-goods/index.wxml | 40 + .../coupon/coupon-activity-goods/index.wxss | 69 ++ .../pages/coupon/coupon-detail/index.js | 32 + .../pages/coupon/coupon-detail/index.json | 10 + .../pages/coupon/coupon-detail/index.wxml | 45 + .../pages/coupon/coupon-detail/index.wxss | 91 ++ .../pages/coupon/coupon-list/index.js | 77 ++ .../pages/coupon/coupon-list/index.json | 10 + .../pages/coupon/coupon-list/index.wxml | 42 + .../pages/coupon/coupon-list/index.wxss | 78 ++ .../components/c-sidebar/README.md | 95 ++ .../c-sidebar/c-sidebar-item/index.js | 51 + .../c-sidebar/c-sidebar-item/index.json | 4 + .../c-sidebar/c-sidebar-item/index.wxml | 10 + .../c-sidebar/c-sidebar-item/index.wxss | 60 ++ .../components/c-sidebar/index.js | 106 ++ .../components/c-sidebar/index.json | 4 + .../components/c-sidebar/index.wxml | 3 + .../components/c-sidebar/index.wxss | 9 + .../c-tabbar/c-tabbar-more/index.js | 37 + .../c-tabbar/c-tabbar-more/index.json | 4 + .../c-tabbar/c-tabbar-more/index.wxml | 25 + .../c-tabbar/c-tabbar-more/index.wxss | 63 ++ .../components/c-tabbar/index.js | 68 ++ .../components/c-tabbar/index.json | 6 + .../components/c-tabbar/index.wxml | 29 + .../components/c-tabbar/index.wxss | 53 + .../components/goods-category/index.js | 69 ++ .../components/goods-category/index.json | 9 + .../components/goods-category/index.wxml | 24 + .../components/goods-category/index.wxss | 102 ++ .../tcb-shop/pages/goods/category/index.js | 36 + .../tcb-shop/pages/goods/category/index.json | 7 + .../tcb-shop/pages/goods/category/index.wxml | 4 + .../tcb-shop/pages/goods/category/index.wxss | 23 + .../components/images-videos/assets/play.png | Bin 0 -> 1326 bytes .../components/images-videos/index.js | 36 + .../components/images-videos/index.json | 7 + .../components/images-videos/index.wxml | 15 + .../components/images-videos/index.wxss | 68 ++ .../components/my-video/index.js | 55 + .../components/my-video/index.json | 4 + .../components/my-video/index.wxml | 26 + .../components/my-video/index.wxss | 21 + .../components/comments-card/index.js | 51 + .../components/comments-card/index.json | 8 + .../components/comments-card/index.wxml | 27 + .../components/comments-card/index.wxss | 172 ++++ .../pages/goods/comments/create-list/index.js | 56 + .../goods/comments/create-list/index.json | 9 + .../goods/comments/create-list/index.wxml | 22 + .../goods/comments/create-list/index.wxss | 54 + .../pages/goods/comments/create/index.js | 114 +++ .../pages/goods/comments/create/index.json | 13 + .../pages/goods/comments/create/index.wxml | 26 + .../pages/goods/comments/create/index.wxss | 168 +++ .../tcb-shop/pages/goods/comments/index.js | 71 ++ .../tcb-shop/pages/goods/comments/index.json | 8 + .../tcb-shop/pages/goods/comments/index.wxml | 6 + .../tcb-shop/pages/goods/comments/index.wxss | 49 + .../goods/details/components/buy-bar/index.js | 66 ++ .../details/components/buy-bar/index.json | 6 + .../details/components/buy-bar/index.wxml | 38 + .../details/components/buy-bar/index.wxss | 107 ++ .../components/goods-specs-popup/index.js | 303 ++++++ .../components/goods-specs-popup/index.json | 11 + .../components/goods-specs-popup/index.wxml | 63 ++ .../components/goods-specs-popup/index.wxss | 302 ++++++ .../components/promotion-popup/index.js | 35 + .../components/promotion-popup/index.json | 7 + .../components/promotion-popup/index.wxml | 34 + .../components/promotion-popup/index.wxss | 131 +++ .../tcb-shop/pages/goods/details/index.js | 421 ++++++++ .../tcb-shop/pages/goods/details/index.json | 19 + .../tcb-shop/pages/goods/details/index.wxml | 71 ++ .../tcb-shop/pages/goods/details/index.wxss | 344 +++++++ .../tcb-shop/pages/goods/list/index.js | 208 ++++ .../tcb-shop/pages/goods/list/index.json | 12 + .../tcb-shop/pages/goods/list/index.wxml | 10 + .../tcb-shop/pages/goods/list/index.wxss | 99 ++ .../tcb-shop/pages/goods/result/index.js | 242 +++++ .../tcb-shop/pages/goods/result/index.json | 15 + .../tcb-shop/pages/goods/result/index.wxml | 13 + .../tcb-shop/pages/goods/result/index.wxss | 114 +++ .../tcb-shop/pages/goods/search/index.js | 116 +++ .../tcb-shop/pages/goods/search/index.json | 8 + .../tcb-shop/pages/goods/search/index.wxml | 6 + .../tcb-shop/pages/goods/search/index.wxss | 79 ++ miniprogram/tcb-shop/pages/home/home.js | 135 +++ miniprogram/tcb-shop/pages/home/home.json | 20 + miniprogram/tcb-shop/pages/home/home.wxml | 19 + miniprogram/tcb-shop/pages/home/home.wxss | 107 ++ miniprogram/tcb-shop/pages/home/readme | 8 + .../pages/order/after-service-detail/api.js | 34 + .../pages/order/after-service-detail/index.js | 205 ++++ .../order/after-service-detail/index.json | 21 + .../order/after-service-detail/index.wxml | 197 ++++ .../order/after-service-detail/index.wxss | 435 ++++++++ .../pages/order/after-service-list/api.js | 32 + .../pages/order/after-service-list/index.js | 220 ++++ .../pages/order/after-service-list/index.json | 15 + .../pages/order/after-service-list/index.wxml | 61 ++ .../pages/order/after-service-list/index.wxss | 104 ++ .../pages/order/apply-service/index.js | 441 ++++++++ .../pages/order/apply-service/index.json | 19 + .../pages/order/apply-service/index.wxml | 198 ++++ .../pages/order/apply-service/index.wxss | 308 ++++++ .../after-service-button-bar/index.js | 95 ++ .../after-service-button-bar/index.json | 6 + .../after-service-button-bar/index.wxml | 33 + .../after-service-button-bar/index.wxss | 43 + .../components/customer-service/index.js | 38 + .../components/customer-service/index.json | 6 + .../components/customer-service/index.wxml | 23 + .../components/customer-service/index.wxss | 48 + .../order/components/goods-card/index.js | 251 +++++ .../order/components/goods-card/index.json | 8 + .../order/components/goods-card/index.wxml | 40 + .../order/components/goods-card/index.wxss | 254 +++++ .../pages/order/components/noGoods/noGood.wxs | 17 + .../pages/order/components/noGoods/noGoods.js | 57 ++ .../order/components/noGoods/noGoods.json | 8 + .../order/components/noGoods/noGoods.wxml | 53 + .../order/components/noGoods/noGoods.wxss | 68 ++ .../components/order-button-bar/index.js | 274 +++++ .../components/order-button-bar/index.json | 8 + .../components/order-button-bar/index.wxml | 37 + .../components/order-button-bar/index.wxss | 54 + .../order/components/order-card/index.js | 88 ++ .../order/components/order-card/index.json | 7 + .../order/components/order-card/index.wxml | 17 + .../order/components/order-card/index.wxss | 45 + .../components/order-goods-card/index.js | 43 + .../components/order-goods-card/index.json | 7 + .../components/order-goods-card/index.wxml | 29 + .../order/components/reason-sheet/index.js | 114 +++ .../order/components/reason-sheet/index.json | 10 + .../order/components/reason-sheet/index.wxml | 50 + .../order/components/reason-sheet/index.wxss | 47 + .../components/reason-sheet/reasonSheet.js | 25 + .../order/components/selectCoupons/mock.js | 22 + .../components/selectCoupons/selectCoupon.wxs | 16 + .../components/selectCoupons/selectCoupons.js | 160 +++ .../selectCoupons/selectCoupons.json | 10 + .../selectCoupons/selectCoupons.wxml | 43 + .../selectCoupons/selectCoupons.wxss | 104 ++ .../components/specs-goods-card/index.js | 132 +++ .../components/specs-goods-card/index.json | 6 + .../components/specs-goods-card/index.wxml | 40 + .../components/specs-goods-card/index.wxss | 0 miniprogram/tcb-shop/pages/order/config.js | 94 ++ .../pages/order/delivery-detail/index.js | 43 + .../pages/order/delivery-detail/index.json | 11 + .../pages/order/delivery-detail/index.wxml | 91 ++ .../pages/order/delivery-detail/index.wxss | 99 ++ .../pages/order/fill-tracking-no/api.js | 71 ++ .../pages/order/fill-tracking-no/index.js | 190 ++++ .../pages/order/fill-tracking-no/index.json | 14 + .../pages/order/fill-tracking-no/index.wxml | 51 + .../pages/order/fill-tracking-no/index.wxss | 103 ++ .../tcb-shop/pages/order/invoice/index.js | 38 + .../tcb-shop/pages/order/invoice/index.json | 8 + .../tcb-shop/pages/order/invoice/index.wxml | 40 + .../tcb-shop/pages/order/invoice/index.wxss | 31 + .../components/address-card/index.js | 25 + .../components/address-card/index.json | 7 + .../components/address-card/index.wxml | 25 + .../components/address-card/index.wxss | 66 ++ .../pages/order/order-confirm/getNotes.wxs | 11 + .../order/order-confirm/handleInvoice.wxs | 11 + .../pages/order/order-confirm/index.js | 308 ++++++ .../pages/order/order-confirm/index.json | 16 + .../pages/order/order-confirm/index.wxml | 39 + .../pages/order/order-confirm/index.wxss | 221 ++++ .../pages/order/order-confirm/order.wxs | 8 + .../tcb-shop/pages/order/order-confirm/pay.js | 115 +++ .../pages/order/order-detail/index.js | 315 ++++++ .../pages/order/order-detail/index.json | 17 + .../pages/order/order-detail/index.wxml | 60 ++ .../pages/order/order-detail/index.wxss | 245 +++++ .../tcb-shop/pages/order/order-list/index.js | 186 ++++ .../pages/order/order-list/index.json | 17 + .../pages/order/order-list/index.wxml | 36 + .../pages/order/order-list/index.wxss | 117 +++ .../tcb-shop/pages/order/pay-result/index.js | 47 + .../pages/order/pay-result/index.json | 9 + .../pages/order/pay-result/index.wxml | 22 + .../pages/order/pay-result/index.wxss | 54 + .../tcb-shop/pages/order/receipt/index.js | 182 ++++ .../tcb-shop/pages/order/receipt/index.json | 11 + .../tcb-shop/pages/order/receipt/index.wxml | 135 +++ .../tcb-shop/pages/order/receipt/index.wxss | 220 ++++ .../tcb-shop/pages/promotion-detail/index.js | 57 ++ .../pages/promotion-detail/index.json | 10 + .../pages/promotion-detail/index.wxml | 76 ++ .../pages/promotion-detail/index.wxss | 111 ++ .../pages/usercenter/address/edit/index.js | 141 +++ .../pages/usercenter/address/edit/index.json | 16 + .../pages/usercenter/address/edit/index.wxml | 25 + .../pages/usercenter/address/edit/index.wxss | 95 ++ .../pages/usercenter/address/edit/util.js | 33 + .../pages/usercenter/address/list/index.js | 102 ++ .../pages/usercenter/address/list/index.json | 11 + .../pages/usercenter/address/list/index.wxml | 21 + .../pages/usercenter/address/list/index.wxss | 109 ++ .../pages/usercenter/address/list/util.js | 31 + .../components/order-group/index.js | 37 + .../components/order-group/index.json | 9 + .../components/order-group/index.wxml | 37 + .../components/order-group/index.wxss | 56 + .../components/ui-address-item/index.js | 46 + .../components/ui-address-item/index.json | 8 + .../components/ui-address-item/index.wxml | 30 + .../components/ui-address-item/index.wxss | 103 ++ .../components/ui-select-picker/index.js | 73 ++ .../components/ui-select-picker/index.json | 6 + .../components/ui-select-picker/index.wxml | 21 + .../components/ui-select-picker/index.wxss | 102 ++ .../components/user-center-card/index.js | 35 + .../components/user-center-card/index.json | 7 + .../components/user-center-card/index.wxml | 34 + .../components/user-center-card/index.wxss | 48 + .../tcb-shop/pages/usercenter/index.js | 220 ++++ .../tcb-shop/pages/usercenter/index.json | 14 + .../tcb-shop/pages/usercenter/index.wxml | 26 + .../tcb-shop/pages/usercenter/index.wxss | 146 +++ .../pages/usercenter/name-edit/index.js | 19 + .../pages/usercenter/name-edit/index.json | 8 + .../pages/usercenter/name-edit/index.wxml | 14 + .../pages/usercenter/name-edit/index.wxss | 18 + .../pages/usercenter/person-info/index.js | 122 +++ .../pages/usercenter/person-info/index.json | 12 + .../pages/usercenter/person-info/index.wxml | 12 + .../pages/usercenter/person-info/index.wxss | 45 + miniprogram/tcb-shop/project.config.json | 150 +++ miniprogram/tcb-shop/services/_utils/delay.js | 3 + miniprogram/tcb-shop/services/_utils/model.js | 34 + .../tcb-shop/services/_utils/timeout.js | 3 + .../services/activity/fetchActivity.js | 20 + .../services/activity/fetchActivityList.js | 20 + .../tcb-shop/services/address/address.js | 85 ++ .../tcb-shop/services/attrValue/attrValue.js | 30 + miniprogram/tcb-shop/services/cart/cart.js | 120 +++ miniprogram/tcb-shop/services/cate/cate.js | 60 ++ .../tcb-shop/services/comments/comments.js | 109 ++ .../services/comments/fetchComments.js | 18 + .../services/comments/fetchCommentsCount.js | 18 + miniprogram/tcb-shop/services/coupon/index.js | 65 ++ .../good/comments/fetchCommentDetail.js | 20 + .../services/good/fetchCategoryList.js | 18 + .../tcb-shop/services/good/fetchGood.js | 24 + .../good/fetchGoodsDetailsComments.js | 37 + .../tcb-shop/services/good/fetchGoodsList.js | 39 + .../services/good/fetchSearchHistory.js | 35 + .../services/good/fetchSearchResult.js | 38 + miniprogram/tcb-shop/services/good/spu.js | 77 ++ miniprogram/tcb-shop/services/home/home.js | 8 + .../tcb-shop/services/order/applyService.js | 70 ++ miniprogram/tcb-shop/services/order/order.js | 171 ++++ .../tcb-shop/services/order/orderConfirm.js | 69 ++ .../tcb-shop/services/order/orderDetail.js | 39 + .../tcb-shop/services/order/orderItem.js | 77 ++ .../tcb-shop/services/order/orderList.js | 39 + .../services/order/orderSubmitComment.js | 22 + miniprogram/tcb-shop/services/pay/pay.js | 44 + .../tcb-shop/services/promotion/detail.js | 18 + miniprogram/tcb-shop/services/sku/sku.js | 57 ++ .../services/usercenter/fetchPerson.js | 28 + .../services/usercenter/fetchUsercenter.js | 18 + miniprogram/tcb-shop/sitemap.json | 7 + miniprogram/tcb-shop/style/cart-group.wxss | 83 ++ miniprogram/tcb-shop/style/global.wxss | 960 ++++++++++++++++++ miniprogram/tcb-shop/style/goodsList.wxss | 169 +++ miniprogram/tcb-shop/style/iconfont.wxss | 306 ++++++ miniprogram/tcb-shop/style/theme.wxss | 47 + .../tcb-shop/utils/addressListFresh.js | 3 + miniprogram/tcb-shop/utils/cartFresh.js | 3 + .../tcb-shop/utils/cloudImageHandler.js | 30 + miniprogram/tcb-shop/utils/getPermission.js | 45 + miniprogram/tcb-shop/utils/listLoading.js | 9 + miniprogram/tcb-shop/utils/mock.js | 51 + miniprogram/tcb-shop/utils/orderListFresh.js | 3 + miniprogram/tcb-shop/utils/orderOperation.js | 5 + miniprogram/tcb-shop/utils/spuStatus.js | 1 + miniprogram/tcb-shop/utils/util.js | 135 +++ 412 files changed, 26213 insertions(+) create mode 100644 miniprogram/tcb-shop/.eslintrc.js create mode 100644 miniprogram/tcb-shop/.gitignore create mode 100644 miniprogram/tcb-shop/.npmrc create mode 100644 miniprogram/tcb-shop/.prettierignore create mode 100644 miniprogram/tcb-shop/.prettierrc.yml create mode 100644 miniprogram/tcb-shop/LICENSE create mode 100644 miniprogram/tcb-shop/README.md create mode 100644 miniprogram/tcb-shop/app.js create mode 100644 miniprogram/tcb-shop/app.json create mode 100644 miniprogram/tcb-shop/app.wxss create mode 100644 miniprogram/tcb-shop/commitlint.config.js create mode 100644 miniprogram/tcb-shop/common/updateManager.js create mode 100644 miniprogram/tcb-shop/components/cloud-template-dialog/index.js create mode 100644 miniprogram/tcb-shop/components/cloud-template-dialog/index.json create mode 100644 miniprogram/tcb-shop/components/cloud-template-dialog/index.wxml create mode 100644 miniprogram/tcb-shop/components/cloud-template-dialog/index.wxss create mode 100644 miniprogram/tcb-shop/components/filter-popup/index.js create mode 100644 miniprogram/tcb-shop/components/filter-popup/index.json create mode 100644 miniprogram/tcb-shop/components/filter-popup/index.wxml create mode 100644 miniprogram/tcb-shop/components/filter-popup/index.wxss create mode 100644 miniprogram/tcb-shop/components/filter/index.js create mode 100644 miniprogram/tcb-shop/components/filter/index.json create mode 100644 miniprogram/tcb-shop/components/filter/index.wxml create mode 100644 miniprogram/tcb-shop/components/filter/index.wxss create mode 100644 miniprogram/tcb-shop/components/goods-card/index.js create mode 100644 miniprogram/tcb-shop/components/goods-card/index.json create mode 100644 miniprogram/tcb-shop/components/goods-card/index.wxml create mode 100644 miniprogram/tcb-shop/components/goods-card/index.wxss create mode 100644 miniprogram/tcb-shop/components/goods-list/index.js create mode 100644 miniprogram/tcb-shop/components/goods-list/index.json create mode 100644 miniprogram/tcb-shop/components/goods-list/index.wxml create mode 100644 miniprogram/tcb-shop/components/goods-list/index.wxss create mode 100644 miniprogram/tcb-shop/components/load-more/index.js create mode 100644 miniprogram/tcb-shop/components/load-more/index.json create mode 100644 miniprogram/tcb-shop/components/load-more/index.wxml create mode 100644 miniprogram/tcb-shop/components/load-more/index.wxss create mode 100644 miniprogram/tcb-shop/components/loading-content/index.js create mode 100644 miniprogram/tcb-shop/components/loading-content/index.json create mode 100644 miniprogram/tcb-shop/components/loading-content/index.wxml create mode 100644 miniprogram/tcb-shop/components/loading-content/index.wxss create mode 100644 miniprogram/tcb-shop/components/loading-dialog/index.js create mode 100644 miniprogram/tcb-shop/components/loading-dialog/index.json create mode 100644 miniprogram/tcb-shop/components/loading-dialog/index.wxml create mode 100644 miniprogram/tcb-shop/components/loading-dialog/index.wxss create mode 100644 miniprogram/tcb-shop/components/price/index.js create mode 100644 miniprogram/tcb-shop/components/price/index.json create mode 100644 miniprogram/tcb-shop/components/price/index.wxml create mode 100644 miniprogram/tcb-shop/components/price/index.wxss create mode 100644 miniprogram/tcb-shop/components/swipeout/index.js create mode 100644 miniprogram/tcb-shop/components/swipeout/index.json create mode 100644 miniprogram/tcb-shop/components/swipeout/index.wxml create mode 100644 miniprogram/tcb-shop/components/swipeout/index.wxss create mode 100644 miniprogram/tcb-shop/components/webp-image/index.js create mode 100644 miniprogram/tcb-shop/components/webp-image/index.json create mode 100644 miniprogram/tcb-shop/components/webp-image/index.wxml create mode 100644 miniprogram/tcb-shop/components/webp-image/index.wxss create mode 100644 miniprogram/tcb-shop/components/webp-image/utils.wxs create mode 100644 miniprogram/tcb-shop/config/eslintCheck.js create mode 100644 miniprogram/tcb-shop/config/index.js create mode 100644 miniprogram/tcb-shop/config/model.js create mode 100644 miniprogram/tcb-shop/custom-tab-bar/data.js create mode 100644 miniprogram/tcb-shop/custom-tab-bar/index.js create mode 100644 miniprogram/tcb-shop/custom-tab-bar/index.json create mode 100644 miniprogram/tcb-shop/custom-tab-bar/index.wxml create mode 100644 miniprogram/tcb-shop/custom-tab-bar/index.wxss create mode 100644 miniprogram/tcb-shop/jsconfig.json create mode 100644 miniprogram/tcb-shop/model/activities.js create mode 100644 miniprogram/tcb-shop/model/activity.js create mode 100644 miniprogram/tcb-shop/model/address.js create mode 100644 miniprogram/tcb-shop/model/cart.js create mode 100644 miniprogram/tcb-shop/model/category.js create mode 100644 miniprogram/tcb-shop/model/comments.js create mode 100644 miniprogram/tcb-shop/model/comments/queryDetail.js create mode 100644 miniprogram/tcb-shop/model/coupon.js create mode 100644 miniprogram/tcb-shop/model/detailsComments.js create mode 100644 miniprogram/tcb-shop/model/good.js create mode 100644 miniprogram/tcb-shop/model/goods.js create mode 100644 miniprogram/tcb-shop/model/order/applyService.js create mode 100644 miniprogram/tcb-shop/model/order/orderConfirm.js create mode 100644 miniprogram/tcb-shop/model/order/orderDetail.js create mode 100644 miniprogram/tcb-shop/model/order/orderList.js create mode 100644 miniprogram/tcb-shop/model/promotion.js create mode 100644 miniprogram/tcb-shop/model/search.js create mode 100644 miniprogram/tcb-shop/model/submitComment.js create mode 100644 miniprogram/tcb-shop/model/swiper.js create mode 100644 miniprogram/tcb-shop/model/usercenter.js create mode 100644 miniprogram/tcb-shop/package.json create mode 100644 miniprogram/tcb-shop/pages/cart/components/cart-bar/index.js create mode 100644 miniprogram/tcb-shop/pages/cart/components/cart-bar/index.json create mode 100644 miniprogram/tcb-shop/pages/cart/components/cart-bar/index.wxml create mode 100644 miniprogram/tcb-shop/pages/cart/components/cart-bar/index.wxss create mode 100644 miniprogram/tcb-shop/pages/cart/components/cart-empty/index.js create mode 100644 miniprogram/tcb-shop/pages/cart/components/cart-empty/index.json create mode 100644 miniprogram/tcb-shop/pages/cart/components/cart-empty/index.wxml create mode 100644 miniprogram/tcb-shop/pages/cart/components/cart-empty/index.wxss create mode 100644 miniprogram/tcb-shop/pages/cart/components/cart-group/index.js create mode 100644 miniprogram/tcb-shop/pages/cart/components/cart-group/index.json create mode 100644 miniprogram/tcb-shop/pages/cart/components/cart-group/index.wxml create mode 100644 miniprogram/tcb-shop/pages/cart/components/cart-group/index.wxs create mode 100644 miniprogram/tcb-shop/pages/cart/components/cart-group/index.wxss create mode 100644 miniprogram/tcb-shop/pages/cart/components/cart-group/utils.wxs create mode 100644 miniprogram/tcb-shop/pages/cart/components/goods-card/index.js create mode 100644 miniprogram/tcb-shop/pages/cart/components/goods-card/index.json create mode 100644 miniprogram/tcb-shop/pages/cart/components/goods-card/index.wxml create mode 100644 miniprogram/tcb-shop/pages/cart/components/goods-card/index.wxss create mode 100644 miniprogram/tcb-shop/pages/cart/components/specs-popup/index.js create mode 100644 miniprogram/tcb-shop/pages/cart/components/specs-popup/index.json create mode 100644 miniprogram/tcb-shop/pages/cart/components/specs-popup/index.wxml create mode 100644 miniprogram/tcb-shop/pages/cart/components/specs-popup/index.wxss create mode 100644 miniprogram/tcb-shop/pages/cart/index.js create mode 100644 miniprogram/tcb-shop/pages/cart/index.json create mode 100644 miniprogram/tcb-shop/pages/cart/index.wxml create mode 100644 miniprogram/tcb-shop/pages/cart/index.wxss create mode 100644 miniprogram/tcb-shop/pages/coupon/components/coupon-card/index.js create mode 100644 miniprogram/tcb-shop/pages/coupon/components/coupon-card/index.json create mode 100644 miniprogram/tcb-shop/pages/coupon/components/coupon-card/index.wxml create mode 100644 miniprogram/tcb-shop/pages/coupon/components/coupon-card/index.wxss create mode 100644 miniprogram/tcb-shop/pages/coupon/components/floating-button/index.js create mode 100644 miniprogram/tcb-shop/pages/coupon/components/floating-button/index.json create mode 100644 miniprogram/tcb-shop/pages/coupon/components/floating-button/index.wxml create mode 100644 miniprogram/tcb-shop/pages/coupon/components/floating-button/index.wxss create mode 100644 miniprogram/tcb-shop/pages/coupon/components/ui-coupon-card/index.js create mode 100644 miniprogram/tcb-shop/pages/coupon/components/ui-coupon-card/index.json create mode 100644 miniprogram/tcb-shop/pages/coupon/components/ui-coupon-card/index.wxml create mode 100644 miniprogram/tcb-shop/pages/coupon/components/ui-coupon-card/index.wxss create mode 100644 miniprogram/tcb-shop/pages/coupon/coupon-activity-goods/index.js create mode 100644 miniprogram/tcb-shop/pages/coupon/coupon-activity-goods/index.json create mode 100644 miniprogram/tcb-shop/pages/coupon/coupon-activity-goods/index.wxml create mode 100644 miniprogram/tcb-shop/pages/coupon/coupon-activity-goods/index.wxss create mode 100644 miniprogram/tcb-shop/pages/coupon/coupon-detail/index.js create mode 100644 miniprogram/tcb-shop/pages/coupon/coupon-detail/index.json create mode 100644 miniprogram/tcb-shop/pages/coupon/coupon-detail/index.wxml create mode 100644 miniprogram/tcb-shop/pages/coupon/coupon-detail/index.wxss create mode 100644 miniprogram/tcb-shop/pages/coupon/coupon-list/index.js create mode 100644 miniprogram/tcb-shop/pages/coupon/coupon-list/index.json create mode 100644 miniprogram/tcb-shop/pages/coupon/coupon-list/index.wxml create mode 100644 miniprogram/tcb-shop/pages/coupon/coupon-list/index.wxss create mode 100644 miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/README.md create mode 100644 miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/c-sidebar-item/index.js create mode 100644 miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/c-sidebar-item/index.json create mode 100644 miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/c-sidebar-item/index.wxml create mode 100644 miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/c-sidebar-item/index.wxss create mode 100644 miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/index.js create mode 100644 miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/index.json create mode 100644 miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/index.wxml create mode 100644 miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/index.wxss create mode 100644 miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/c-tabbar-more/index.js create mode 100644 miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/c-tabbar-more/index.json create mode 100644 miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/c-tabbar-more/index.wxml create mode 100644 miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/c-tabbar-more/index.wxss create mode 100644 miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/index.js create mode 100644 miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/index.json create mode 100644 miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/index.wxml create mode 100644 miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/index.wxss create mode 100644 miniprogram/tcb-shop/pages/goods/category/components/goods-category/index.js create mode 100644 miniprogram/tcb-shop/pages/goods/category/components/goods-category/index.json create mode 100644 miniprogram/tcb-shop/pages/goods/category/components/goods-category/index.wxml create mode 100644 miniprogram/tcb-shop/pages/goods/category/components/goods-category/index.wxss create mode 100644 miniprogram/tcb-shop/pages/goods/category/index.js create mode 100644 miniprogram/tcb-shop/pages/goods/category/index.json create mode 100644 miniprogram/tcb-shop/pages/goods/category/index.wxml create mode 100644 miniprogram/tcb-shop/pages/goods/category/index.wxss create mode 100644 miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/images-videos/assets/play.png create mode 100644 miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/images-videos/index.js create mode 100644 miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/images-videos/index.json create mode 100644 miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/images-videos/index.wxml create mode 100644 miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/images-videos/index.wxss create mode 100644 miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/my-video/index.js create mode 100644 miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/my-video/index.json create mode 100644 miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/my-video/index.wxml create mode 100644 miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/my-video/index.wxss create mode 100644 miniprogram/tcb-shop/pages/goods/comments/components/comments-card/index.js create mode 100644 miniprogram/tcb-shop/pages/goods/comments/components/comments-card/index.json create mode 100644 miniprogram/tcb-shop/pages/goods/comments/components/comments-card/index.wxml create mode 100644 miniprogram/tcb-shop/pages/goods/comments/components/comments-card/index.wxss create mode 100644 miniprogram/tcb-shop/pages/goods/comments/create-list/index.js create mode 100644 miniprogram/tcb-shop/pages/goods/comments/create-list/index.json create mode 100644 miniprogram/tcb-shop/pages/goods/comments/create-list/index.wxml create mode 100644 miniprogram/tcb-shop/pages/goods/comments/create-list/index.wxss create mode 100644 miniprogram/tcb-shop/pages/goods/comments/create/index.js create mode 100644 miniprogram/tcb-shop/pages/goods/comments/create/index.json create mode 100644 miniprogram/tcb-shop/pages/goods/comments/create/index.wxml create mode 100644 miniprogram/tcb-shop/pages/goods/comments/create/index.wxss create mode 100644 miniprogram/tcb-shop/pages/goods/comments/index.js create mode 100644 miniprogram/tcb-shop/pages/goods/comments/index.json create mode 100644 miniprogram/tcb-shop/pages/goods/comments/index.wxml create mode 100644 miniprogram/tcb-shop/pages/goods/comments/index.wxss create mode 100644 miniprogram/tcb-shop/pages/goods/details/components/buy-bar/index.js create mode 100644 miniprogram/tcb-shop/pages/goods/details/components/buy-bar/index.json create mode 100644 miniprogram/tcb-shop/pages/goods/details/components/buy-bar/index.wxml create mode 100644 miniprogram/tcb-shop/pages/goods/details/components/buy-bar/index.wxss create mode 100644 miniprogram/tcb-shop/pages/goods/details/components/goods-specs-popup/index.js create mode 100644 miniprogram/tcb-shop/pages/goods/details/components/goods-specs-popup/index.json create mode 100644 miniprogram/tcb-shop/pages/goods/details/components/goods-specs-popup/index.wxml create mode 100644 miniprogram/tcb-shop/pages/goods/details/components/goods-specs-popup/index.wxss create mode 100644 miniprogram/tcb-shop/pages/goods/details/components/promotion-popup/index.js create mode 100644 miniprogram/tcb-shop/pages/goods/details/components/promotion-popup/index.json create mode 100644 miniprogram/tcb-shop/pages/goods/details/components/promotion-popup/index.wxml create mode 100644 miniprogram/tcb-shop/pages/goods/details/components/promotion-popup/index.wxss create mode 100644 miniprogram/tcb-shop/pages/goods/details/index.js create mode 100644 miniprogram/tcb-shop/pages/goods/details/index.json create mode 100644 miniprogram/tcb-shop/pages/goods/details/index.wxml create mode 100644 miniprogram/tcb-shop/pages/goods/details/index.wxss create mode 100644 miniprogram/tcb-shop/pages/goods/list/index.js create mode 100644 miniprogram/tcb-shop/pages/goods/list/index.json create mode 100644 miniprogram/tcb-shop/pages/goods/list/index.wxml create mode 100644 miniprogram/tcb-shop/pages/goods/list/index.wxss create mode 100644 miniprogram/tcb-shop/pages/goods/result/index.js create mode 100644 miniprogram/tcb-shop/pages/goods/result/index.json create mode 100644 miniprogram/tcb-shop/pages/goods/result/index.wxml create mode 100644 miniprogram/tcb-shop/pages/goods/result/index.wxss create mode 100644 miniprogram/tcb-shop/pages/goods/search/index.js create mode 100644 miniprogram/tcb-shop/pages/goods/search/index.json create mode 100644 miniprogram/tcb-shop/pages/goods/search/index.wxml create mode 100644 miniprogram/tcb-shop/pages/goods/search/index.wxss create mode 100644 miniprogram/tcb-shop/pages/home/home.js create mode 100644 miniprogram/tcb-shop/pages/home/home.json create mode 100644 miniprogram/tcb-shop/pages/home/home.wxml create mode 100644 miniprogram/tcb-shop/pages/home/home.wxss create mode 100644 miniprogram/tcb-shop/pages/home/readme create mode 100644 miniprogram/tcb-shop/pages/order/after-service-detail/api.js create mode 100644 miniprogram/tcb-shop/pages/order/after-service-detail/index.js create mode 100644 miniprogram/tcb-shop/pages/order/after-service-detail/index.json create mode 100644 miniprogram/tcb-shop/pages/order/after-service-detail/index.wxml create mode 100644 miniprogram/tcb-shop/pages/order/after-service-detail/index.wxss create mode 100644 miniprogram/tcb-shop/pages/order/after-service-list/api.js create mode 100644 miniprogram/tcb-shop/pages/order/after-service-list/index.js create mode 100644 miniprogram/tcb-shop/pages/order/after-service-list/index.json create mode 100644 miniprogram/tcb-shop/pages/order/after-service-list/index.wxml create mode 100644 miniprogram/tcb-shop/pages/order/after-service-list/index.wxss create mode 100644 miniprogram/tcb-shop/pages/order/apply-service/index.js create mode 100644 miniprogram/tcb-shop/pages/order/apply-service/index.json create mode 100644 miniprogram/tcb-shop/pages/order/apply-service/index.wxml create mode 100644 miniprogram/tcb-shop/pages/order/apply-service/index.wxss create mode 100644 miniprogram/tcb-shop/pages/order/components/after-service-button-bar/index.js create mode 100644 miniprogram/tcb-shop/pages/order/components/after-service-button-bar/index.json create mode 100644 miniprogram/tcb-shop/pages/order/components/after-service-button-bar/index.wxml create mode 100644 miniprogram/tcb-shop/pages/order/components/after-service-button-bar/index.wxss create mode 100644 miniprogram/tcb-shop/pages/order/components/customer-service/index.js create mode 100644 miniprogram/tcb-shop/pages/order/components/customer-service/index.json create mode 100644 miniprogram/tcb-shop/pages/order/components/customer-service/index.wxml create mode 100644 miniprogram/tcb-shop/pages/order/components/customer-service/index.wxss create mode 100644 miniprogram/tcb-shop/pages/order/components/goods-card/index.js create mode 100644 miniprogram/tcb-shop/pages/order/components/goods-card/index.json create mode 100644 miniprogram/tcb-shop/pages/order/components/goods-card/index.wxml create mode 100644 miniprogram/tcb-shop/pages/order/components/goods-card/index.wxss create mode 100644 miniprogram/tcb-shop/pages/order/components/noGoods/noGood.wxs create mode 100644 miniprogram/tcb-shop/pages/order/components/noGoods/noGoods.js create mode 100644 miniprogram/tcb-shop/pages/order/components/noGoods/noGoods.json create mode 100644 miniprogram/tcb-shop/pages/order/components/noGoods/noGoods.wxml create mode 100644 miniprogram/tcb-shop/pages/order/components/noGoods/noGoods.wxss create mode 100644 miniprogram/tcb-shop/pages/order/components/order-button-bar/index.js create mode 100644 miniprogram/tcb-shop/pages/order/components/order-button-bar/index.json create mode 100644 miniprogram/tcb-shop/pages/order/components/order-button-bar/index.wxml create mode 100644 miniprogram/tcb-shop/pages/order/components/order-button-bar/index.wxss create mode 100644 miniprogram/tcb-shop/pages/order/components/order-card/index.js create mode 100644 miniprogram/tcb-shop/pages/order/components/order-card/index.json create mode 100644 miniprogram/tcb-shop/pages/order/components/order-card/index.wxml create mode 100644 miniprogram/tcb-shop/pages/order/components/order-card/index.wxss create mode 100644 miniprogram/tcb-shop/pages/order/components/order-goods-card/index.js create mode 100644 miniprogram/tcb-shop/pages/order/components/order-goods-card/index.json create mode 100644 miniprogram/tcb-shop/pages/order/components/order-goods-card/index.wxml create mode 100644 miniprogram/tcb-shop/pages/order/components/reason-sheet/index.js create mode 100644 miniprogram/tcb-shop/pages/order/components/reason-sheet/index.json create mode 100644 miniprogram/tcb-shop/pages/order/components/reason-sheet/index.wxml create mode 100644 miniprogram/tcb-shop/pages/order/components/reason-sheet/index.wxss create mode 100644 miniprogram/tcb-shop/pages/order/components/reason-sheet/reasonSheet.js create mode 100644 miniprogram/tcb-shop/pages/order/components/selectCoupons/mock.js create mode 100644 miniprogram/tcb-shop/pages/order/components/selectCoupons/selectCoupon.wxs create mode 100644 miniprogram/tcb-shop/pages/order/components/selectCoupons/selectCoupons.js create mode 100644 miniprogram/tcb-shop/pages/order/components/selectCoupons/selectCoupons.json create mode 100644 miniprogram/tcb-shop/pages/order/components/selectCoupons/selectCoupons.wxml create mode 100644 miniprogram/tcb-shop/pages/order/components/selectCoupons/selectCoupons.wxss create mode 100644 miniprogram/tcb-shop/pages/order/components/specs-goods-card/index.js create mode 100644 miniprogram/tcb-shop/pages/order/components/specs-goods-card/index.json create mode 100644 miniprogram/tcb-shop/pages/order/components/specs-goods-card/index.wxml create mode 100644 miniprogram/tcb-shop/pages/order/components/specs-goods-card/index.wxss create mode 100644 miniprogram/tcb-shop/pages/order/config.js create mode 100644 miniprogram/tcb-shop/pages/order/delivery-detail/index.js create mode 100644 miniprogram/tcb-shop/pages/order/delivery-detail/index.json create mode 100644 miniprogram/tcb-shop/pages/order/delivery-detail/index.wxml create mode 100644 miniprogram/tcb-shop/pages/order/delivery-detail/index.wxss create mode 100644 miniprogram/tcb-shop/pages/order/fill-tracking-no/api.js create mode 100644 miniprogram/tcb-shop/pages/order/fill-tracking-no/index.js create mode 100644 miniprogram/tcb-shop/pages/order/fill-tracking-no/index.json create mode 100644 miniprogram/tcb-shop/pages/order/fill-tracking-no/index.wxml create mode 100644 miniprogram/tcb-shop/pages/order/fill-tracking-no/index.wxss create mode 100644 miniprogram/tcb-shop/pages/order/invoice/index.js create mode 100644 miniprogram/tcb-shop/pages/order/invoice/index.json create mode 100644 miniprogram/tcb-shop/pages/order/invoice/index.wxml create mode 100644 miniprogram/tcb-shop/pages/order/invoice/index.wxss create mode 100644 miniprogram/tcb-shop/pages/order/order-confirm/components/address-card/index.js create mode 100644 miniprogram/tcb-shop/pages/order/order-confirm/components/address-card/index.json create mode 100644 miniprogram/tcb-shop/pages/order/order-confirm/components/address-card/index.wxml create mode 100644 miniprogram/tcb-shop/pages/order/order-confirm/components/address-card/index.wxss create mode 100644 miniprogram/tcb-shop/pages/order/order-confirm/getNotes.wxs create mode 100644 miniprogram/tcb-shop/pages/order/order-confirm/handleInvoice.wxs create mode 100644 miniprogram/tcb-shop/pages/order/order-confirm/index.js create mode 100644 miniprogram/tcb-shop/pages/order/order-confirm/index.json create mode 100644 miniprogram/tcb-shop/pages/order/order-confirm/index.wxml create mode 100644 miniprogram/tcb-shop/pages/order/order-confirm/index.wxss create mode 100644 miniprogram/tcb-shop/pages/order/order-confirm/order.wxs create mode 100644 miniprogram/tcb-shop/pages/order/order-confirm/pay.js create mode 100644 miniprogram/tcb-shop/pages/order/order-detail/index.js create mode 100644 miniprogram/tcb-shop/pages/order/order-detail/index.json create mode 100644 miniprogram/tcb-shop/pages/order/order-detail/index.wxml create mode 100644 miniprogram/tcb-shop/pages/order/order-detail/index.wxss create mode 100644 miniprogram/tcb-shop/pages/order/order-list/index.js create mode 100644 miniprogram/tcb-shop/pages/order/order-list/index.json create mode 100644 miniprogram/tcb-shop/pages/order/order-list/index.wxml create mode 100644 miniprogram/tcb-shop/pages/order/order-list/index.wxss create mode 100644 miniprogram/tcb-shop/pages/order/pay-result/index.js create mode 100644 miniprogram/tcb-shop/pages/order/pay-result/index.json create mode 100644 miniprogram/tcb-shop/pages/order/pay-result/index.wxml create mode 100644 miniprogram/tcb-shop/pages/order/pay-result/index.wxss create mode 100644 miniprogram/tcb-shop/pages/order/receipt/index.js create mode 100644 miniprogram/tcb-shop/pages/order/receipt/index.json create mode 100644 miniprogram/tcb-shop/pages/order/receipt/index.wxml create mode 100644 miniprogram/tcb-shop/pages/order/receipt/index.wxss create mode 100644 miniprogram/tcb-shop/pages/promotion-detail/index.js create mode 100644 miniprogram/tcb-shop/pages/promotion-detail/index.json create mode 100644 miniprogram/tcb-shop/pages/promotion-detail/index.wxml create mode 100644 miniprogram/tcb-shop/pages/promotion-detail/index.wxss create mode 100644 miniprogram/tcb-shop/pages/usercenter/address/edit/index.js create mode 100644 miniprogram/tcb-shop/pages/usercenter/address/edit/index.json create mode 100644 miniprogram/tcb-shop/pages/usercenter/address/edit/index.wxml create mode 100644 miniprogram/tcb-shop/pages/usercenter/address/edit/index.wxss create mode 100644 miniprogram/tcb-shop/pages/usercenter/address/edit/util.js create mode 100644 miniprogram/tcb-shop/pages/usercenter/address/list/index.js create mode 100644 miniprogram/tcb-shop/pages/usercenter/address/list/index.json create mode 100644 miniprogram/tcb-shop/pages/usercenter/address/list/index.wxml create mode 100644 miniprogram/tcb-shop/pages/usercenter/address/list/index.wxss create mode 100644 miniprogram/tcb-shop/pages/usercenter/address/list/util.js create mode 100644 miniprogram/tcb-shop/pages/usercenter/components/order-group/index.js create mode 100644 miniprogram/tcb-shop/pages/usercenter/components/order-group/index.json create mode 100644 miniprogram/tcb-shop/pages/usercenter/components/order-group/index.wxml create mode 100644 miniprogram/tcb-shop/pages/usercenter/components/order-group/index.wxss create mode 100644 miniprogram/tcb-shop/pages/usercenter/components/ui-address-item/index.js create mode 100644 miniprogram/tcb-shop/pages/usercenter/components/ui-address-item/index.json create mode 100644 miniprogram/tcb-shop/pages/usercenter/components/ui-address-item/index.wxml create mode 100644 miniprogram/tcb-shop/pages/usercenter/components/ui-address-item/index.wxss create mode 100644 miniprogram/tcb-shop/pages/usercenter/components/ui-select-picker/index.js create mode 100644 miniprogram/tcb-shop/pages/usercenter/components/ui-select-picker/index.json create mode 100644 miniprogram/tcb-shop/pages/usercenter/components/ui-select-picker/index.wxml create mode 100644 miniprogram/tcb-shop/pages/usercenter/components/ui-select-picker/index.wxss create mode 100644 miniprogram/tcb-shop/pages/usercenter/components/user-center-card/index.js create mode 100644 miniprogram/tcb-shop/pages/usercenter/components/user-center-card/index.json create mode 100644 miniprogram/tcb-shop/pages/usercenter/components/user-center-card/index.wxml create mode 100644 miniprogram/tcb-shop/pages/usercenter/components/user-center-card/index.wxss create mode 100644 miniprogram/tcb-shop/pages/usercenter/index.js create mode 100644 miniprogram/tcb-shop/pages/usercenter/index.json create mode 100644 miniprogram/tcb-shop/pages/usercenter/index.wxml create mode 100644 miniprogram/tcb-shop/pages/usercenter/index.wxss create mode 100644 miniprogram/tcb-shop/pages/usercenter/name-edit/index.js create mode 100644 miniprogram/tcb-shop/pages/usercenter/name-edit/index.json create mode 100644 miniprogram/tcb-shop/pages/usercenter/name-edit/index.wxml create mode 100644 miniprogram/tcb-shop/pages/usercenter/name-edit/index.wxss create mode 100644 miniprogram/tcb-shop/pages/usercenter/person-info/index.js create mode 100644 miniprogram/tcb-shop/pages/usercenter/person-info/index.json create mode 100644 miniprogram/tcb-shop/pages/usercenter/person-info/index.wxml create mode 100644 miniprogram/tcb-shop/pages/usercenter/person-info/index.wxss create mode 100644 miniprogram/tcb-shop/project.config.json create mode 100644 miniprogram/tcb-shop/services/_utils/delay.js create mode 100644 miniprogram/tcb-shop/services/_utils/model.js create mode 100644 miniprogram/tcb-shop/services/_utils/timeout.js create mode 100644 miniprogram/tcb-shop/services/activity/fetchActivity.js create mode 100644 miniprogram/tcb-shop/services/activity/fetchActivityList.js create mode 100644 miniprogram/tcb-shop/services/address/address.js create mode 100644 miniprogram/tcb-shop/services/attrValue/attrValue.js create mode 100644 miniprogram/tcb-shop/services/cart/cart.js create mode 100644 miniprogram/tcb-shop/services/cate/cate.js create mode 100644 miniprogram/tcb-shop/services/comments/comments.js create mode 100644 miniprogram/tcb-shop/services/comments/fetchComments.js create mode 100644 miniprogram/tcb-shop/services/comments/fetchCommentsCount.js create mode 100644 miniprogram/tcb-shop/services/coupon/index.js create mode 100644 miniprogram/tcb-shop/services/good/comments/fetchCommentDetail.js create mode 100644 miniprogram/tcb-shop/services/good/fetchCategoryList.js create mode 100644 miniprogram/tcb-shop/services/good/fetchGood.js create mode 100644 miniprogram/tcb-shop/services/good/fetchGoodsDetailsComments.js create mode 100644 miniprogram/tcb-shop/services/good/fetchGoodsList.js create mode 100644 miniprogram/tcb-shop/services/good/fetchSearchHistory.js create mode 100644 miniprogram/tcb-shop/services/good/fetchSearchResult.js create mode 100644 miniprogram/tcb-shop/services/good/spu.js create mode 100644 miniprogram/tcb-shop/services/home/home.js create mode 100644 miniprogram/tcb-shop/services/order/applyService.js create mode 100644 miniprogram/tcb-shop/services/order/order.js create mode 100644 miniprogram/tcb-shop/services/order/orderConfirm.js create mode 100644 miniprogram/tcb-shop/services/order/orderDetail.js create mode 100644 miniprogram/tcb-shop/services/order/orderItem.js create mode 100644 miniprogram/tcb-shop/services/order/orderList.js create mode 100644 miniprogram/tcb-shop/services/order/orderSubmitComment.js create mode 100644 miniprogram/tcb-shop/services/pay/pay.js create mode 100644 miniprogram/tcb-shop/services/promotion/detail.js create mode 100644 miniprogram/tcb-shop/services/sku/sku.js create mode 100644 miniprogram/tcb-shop/services/usercenter/fetchPerson.js create mode 100644 miniprogram/tcb-shop/services/usercenter/fetchUsercenter.js create mode 100644 miniprogram/tcb-shop/sitemap.json create mode 100644 miniprogram/tcb-shop/style/cart-group.wxss create mode 100644 miniprogram/tcb-shop/style/global.wxss create mode 100644 miniprogram/tcb-shop/style/goodsList.wxss create mode 100644 miniprogram/tcb-shop/style/iconfont.wxss create mode 100644 miniprogram/tcb-shop/style/theme.wxss create mode 100644 miniprogram/tcb-shop/utils/addressListFresh.js create mode 100644 miniprogram/tcb-shop/utils/cartFresh.js create mode 100644 miniprogram/tcb-shop/utils/cloudImageHandler.js create mode 100644 miniprogram/tcb-shop/utils/getPermission.js create mode 100644 miniprogram/tcb-shop/utils/listLoading.js create mode 100644 miniprogram/tcb-shop/utils/mock.js create mode 100644 miniprogram/tcb-shop/utils/orderListFresh.js create mode 100644 miniprogram/tcb-shop/utils/orderOperation.js create mode 100644 miniprogram/tcb-shop/utils/spuStatus.js create mode 100644 miniprogram/tcb-shop/utils/util.js diff --git a/miniprogram/tcb-shop/.eslintrc.js b/miniprogram/tcb-shop/.eslintrc.js new file mode 100644 index 0000000..21d54f4 --- /dev/null +++ b/miniprogram/tcb-shop/.eslintrc.js @@ -0,0 +1,157 @@ +module.exports = { + env: { + browser: true, + commonjs: true, + es6: true, + }, + parserOptions: { + ecmaVersion: 2020, + // ECMAScript modules 模式 + sourceType: 'module', + }, + extends: ['plugin:prettier/recommended', 'prettier'], + globals: { + wx: true, + App: true, + Page: true, + Component: true, + getApp: true, + getCurrentPages: true, + Behavior: true, + global: true, + __wxConfig: true, + }, + ignorePatterns: ['*.wxs'], + rules: { + 'prettier/prettier': 'warn', + 'no-undef': 'off', + camelcase: 'off', + 'class-name-casing': 'off', + 'no-console': ['warn', { allow: ['warn', 'error'] }], + 'no-debugger': 'error', + 'no-unused-expressions': ['error', { allowShortCircuit: true, allowTernary: true }], + 'no-empty-interface': 'off', + 'no-use-before-define': ['error', { functions: false }], + 'no-useless-constructor': 'error', + 'prefer-const': 'error', + 'prefer-destructuring': [ + 'error', + { + AssignmentExpression: { + array: false, + object: false, + }, + VariableDeclarator: { + array: false, + object: true, + }, + }, + { + enforceForRenamedProperties: false, + }, + ], + 'no-const-assign': 'error', + 'no-new-object': 'error', + 'no-prototype-builtins': 'error', + 'no-array-constructor': 'error', + 'array-callback-return': 'warn', + 'prefer-template': 'error', + 'no-useless-escape': 'error', + 'wrap-iife': ['error', 'outside'], + 'space-before-function-paren': [ + 'warn', + { + anonymous: 'always', + named: 'never', + asyncArrow: 'always', + }, + ], + 'no-param-reassign': [ + 'warn', + { + props: true, + ignorePropertyModificationsFor: [ + 'acc', // for reduce accumulators + 'accumulator', // for reduce accumulators + 'e', // for e.returnvalue + 'ctx', // for Koa routing + 'req', // for Express requests + 'request', // for Express requests + 'res', // for Express responses + 'response', // for Express responses + '$scope', // for Angular 1 scopes + 'staticContext', // for ReactRouter context + 'state', // for Vuex + ], + }, + ], + 'no-confusing-arrow': 'warn', + 'no-dupe-class-members': 'error', + 'no-iterator': 'warn', + 'dot-notation': 'warn', + 'one-var': ['warn', 'never'], + 'no-multi-assign': 'error', + 'no-unused-vars': [ + 'error', + { + args: 'after-used', + ignoreRestSiblings: true, + argsIgnorePattern: '^_.+', + varsIgnorePattern: '^_.+', + }, + ], + eqeqeq: ['warn', 'always'], + 'no-case-declarations': 'error', + 'no-nested-ternary': 'warn', + 'no-unneeded-ternary': 'warn', + 'no-mixed-operators': [ + 'error', + { + groups: [ + ['%', '**'], + ['%', '+'], + ['%', '-'], + ['%', '*'], + ['%', '/'], + ['&', '|', '<<', '>>', '>>>'], + ['==', '!=', '===', '!=='], + ['&&', '||'], + ], + allowSamePrecedence: false, + }, + ], + 'no-else-return': [ + 'warn', + { + allowElseIf: false, + }, + ], + 'no-new-wrappers': 'warn', + indent: [ + 'warn', + 2, + { + SwitchCase: 1, + VariableDeclarator: 1, + outerIIFEBody: 1, + FunctionDeclaration: { + parameters: 1, + body: 1, + }, + FunctionExpression: { + parameters: 1, + body: 1, + }, + CallExpression: { + arguments: 1, + }, + ArrayExpression: 1, + ObjectExpression: 1, + ImportDeclaration: 1, + flatTernaryExpressions: false, + ignoreComments: false, + }, + ], + 'linebreak-style': ['warn', 'unix'], + }, +}; diff --git a/miniprogram/tcb-shop/.gitignore b/miniprogram/tcb-shop/.gitignore new file mode 100644 index 0000000..68a200f --- /dev/null +++ b/miniprogram/tcb-shop/.gitignore @@ -0,0 +1,16 @@ +node_modules/ +yarn-error.log +miniprogram/ +miniprogram_npm/ +miniprogram_dist/ +.DS_Store +$node_modules/ +.history/ +**/dist +components/**/*.lock +components/**/package-lock.json +package-lock.json +yarn.lock +project.private.config.json +.eslintcache +cloudfunctions/ \ No newline at end of file diff --git a/miniprogram/tcb-shop/.npmrc b/miniprogram/tcb-shop/.npmrc new file mode 100644 index 0000000..21b91a6 --- /dev/null +++ b/miniprogram/tcb-shop/.npmrc @@ -0,0 +1,9 @@ +# 去除注释可以使用代理进行安装 +# proxy=http://127.0.0.1:1080 +# https_proxy=http://127.0.0.1:1080 + +# 去除注释可以使用淘宝源 +# registry=https://registry.npm.taobao.org + +# 去除注释可以使用腾讯源 +#registry=http://mirrors.tencent.com/npm/ diff --git a/miniprogram/tcb-shop/.prettierignore b/miniprogram/tcb-shop/.prettierignore new file mode 100644 index 0000000..9ff2448 --- /dev/null +++ b/miniprogram/tcb-shop/.prettierignore @@ -0,0 +1,3 @@ +miniprogram_npm +package.json +project.config.json \ No newline at end of file diff --git a/miniprogram/tcb-shop/.prettierrc.yml b/miniprogram/tcb-shop/.prettierrc.yml new file mode 100644 index 0000000..a13251c --- /dev/null +++ b/miniprogram/tcb-shop/.prettierrc.yml @@ -0,0 +1,49 @@ +# 一行最多 100 字符 +printWidth: 120 +# 使用 2 个空格缩进 +tabWidth: 2 +# 不使用缩进符,而使用空格 +useTabs: false +# 行尾需要分号 +semi: true +# 使用单引号 +singleQuote: true +# 对象的 key 仅在必要时用引号 +quoteProps: as-needed +# jsx 不使用单引号,而使用双引号 +jsxSingleQuote: false +# 末尾需要逗号 +trailingComma: all +# 大括号内的首尾需要空格 +bracketSpacing: true +# jsx 标签的反尖括号需要换行 +jsxBracketSameLine: false +# 箭头函数,只有一个参数的时候,不需要括号 +arrowParens: always +# 每个文件格式化的范围是文件的全部内容 +rangeStart: 0 +# 不需要写文件开头的 @prettier +requirePragma: false +# 不需要自动在文件开头插入 @prettier +insertPragma: false +# 使用默认的折行标准 +proseWrap: preserve +# 根据显示样式决定 html 要不要折行 +htmlWhitespaceSensitivity: css +# 换行符使用 lf +endOfLine: lf +# 后缀文件名特有规则 +overrides: + - files: '*.{wxss,less}' + options: + parser: less + - files: '*.json,.*rc' + options: + parser: json + - files: '*.{wxml,html}' + options: + parser: html + htmlWhitespaceSensitivity: strict + - files: '*.wxs' + options: + parser: babel diff --git a/miniprogram/tcb-shop/LICENSE b/miniprogram/tcb-shop/LICENSE new file mode 100644 index 0000000..789cbde --- /dev/null +++ b/miniprogram/tcb-shop/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) 2021-present TDesign + +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/miniprogram/tcb-shop/README.md b/miniprogram/tcb-shop/README.md new file mode 100644 index 0000000..bcfae25 --- /dev/null +++ b/miniprogram/tcb-shop/README.md @@ -0,0 +1,302 @@ + +
+
+ Logo + +

云开发电商模板

+ +

+ 一键创建零售商城 +
+

+
+ +## 说明 + +云开发电商模板提供了一键创建零售商城小程序的能力。 + +## 快速上手 + +1. 安装本模版 +2. 导入云开发电商模板小程序 + +![](https://qcloudimg.tencent-cloud.cn/raw/5f1962e709c28af3252c0acb583e989b.png) + +3. 填入开通了云开发环境的小程序的 appId + +![](https://qcloudimg.tencent-cloud.cn/raw/800f05945779cb940ef6851d96419f6e.png) + +4. 在 `app.js` 中,填入云开发环境 id + +![](https://qcloudimg.tencent-cloud.cn/raw/1da510c60d9d552119ed7aa79c7a6826.png) + +5. 前往[小程序微信支付模版](https://tcb.cloud.tencent.com/cloud-admin#/cloud-template/detail?appName=wx-pay-v2&solutionId=solution-1sbaF7cyIqcgRj&appType=basic),填入该模版所需参数 + +![]( https://qcloudimg.tencent-cloud.cn/raw/9a11a564b8985883194f19cdd11c3f2b.png) + +6. 安装依赖 + 6.1 在命令行执行 `npm install` + 6.2 点击菜单栏中的「工具 -> 构建 npm」 + 6.3. 详情可参考[npm 支持 | 微信开放文档](https://developers.weixin.qq.com/miniprogram/dev/devtools/npm.html#%E4%BD%BF%E7%94%A8-npm-%E5%8C%85) +7. 编译启动小程序,开始体验 +8. 正式发布前,移除云开发引导弹窗 + 8.1 删除 `components/cloud-template-dialog/` 文件夹 + + ![]( https://qcloudimg.tencent-cloud.cn/raw/fcc15824e38fbfacc1afee040c648099.png) + + 8.2 删除 `pages/home/home.wxml` 中对云开发引导弹窗的引用 + + ![]( https://qcloudimg.tencent-cloud.cn/raw/cb2a20e0f840eaee236a964fac734f28.png) + + 8.3 删除 `pages/home/home.json` 中对云开发引导弹窗的引用 + + ![](https://qcloudimg.tencent-cloud.cn/raw/7c0c32bbb494bc3b0877891d302c096c.png) + +## 页面介绍 + +### 首页 + +![](https://qcloudimg.tencent-cloud.cn/raw/89cf8dedbd3edfdbda31920447bddd51.png) + +首页由三部分组成: + +● 搜索栏 + +● 轮播图 + +● 商品列表 + +#### 搜索栏 + +点击搜索栏,可以输入搜索商品,并展示搜索到的商品列表: + +![](https://qcloudimg.tencent-cloud.cn/raw/94e2ee9666aed0f8147affa180c879c7.png) + +#### 轮播图 + +轮播图可循环展示配置好的图片。 + +![](https://qcloudimg.tencent-cloud.cn/raw/b42bc12d0fa4fe350b83154ace0a613c.png) + +#### 商品列表 + +首页商品列表会列出所有的商品,点击单个商品后,会跳转至对应的商品详情页。 + +首页商品列表: + +![](https://qcloudimg.tencent-cloud.cn/raw/98fca5271899639e1393753e52523fce.png) + +点击商品后跳转商品详情页: + +![](https://qcloudimg.tencent-cloud.cn/raw/54c50176b1af344b23fb61e22e611265.png) + +### 分类页 + +分类页会以侧边栏展示商品的一级分类,主内容展示商品的二级分类。 + +![](https://qcloudimg.tencent-cloud.cn/raw/1ff658bb467a4e8febf34be13ecf80da.png) + +点击二级分类后,会展示当前分类下的商品列表。同样,在此商品列表点击商品后,会跳转至对应商品详情页。 + +水果分类下的商品列表: + +![](https://qcloudimg.tencent-cloud.cn/raw/927b12f16467f1def96e877c50b1a7e8.png) + + +### 商品详情页 + +![](https://qcloudimg.tencent-cloud.cn/raw/ef651d54d5e57d2aacaa50bc283ccfb5.png) + +商品详情页由多个部分组成: + +● 商品轮播图 + +● 商品信息 + +● 规格选择栏 + +● 评价预览 + +● 商品详情 + +● 底部动作栏 + +#### 商品轮播图 + +商品轮播图可循环展示该商品的多个图片: + +![](https://qcloudimg.tencent-cloud.cn/raw/5b59639efcc112ce592b72f6cd77fcb8.png) + +#### 商品信息 + +商品信息会展示商品的名称与最低价格: + +![](https://qcloudimg.tencent-cloud.cn/raw/5d23000c19ccb652ce9eab0afd1fd2e7.png) + +#### 规格选择栏 + +点击规格选择栏后,可在跳出的弹窗中选择商品对应的规格,并选择购买数量。确定商品规格和购买数量后,可加入购物车或选择立即购买: + +![](https://qcloudimg.tencent-cloud.cn/raw/f7aecbc84c8d691a8e6c97070b4af71b.png) + +#### 评价预览 + +当商品存在评价时,会展示评价预览,包括评论数、好评率,以及一条评价: + +![](https://qcloudimg.tencent-cloud.cn/raw/dd5e089f0be8b10dd900d65566bb2778.png) + +#### 商品详情 + +商品详情用于展示商品的详细信息,这部分的内容以富文本的形式呈现: + +![](https://qcloudimg.tencent-cloud.cn/raw/81271f5c219fb5c475493af8e8751d99.png) + +#### 底部动作栏 + +底部动作栏提供了四个按钮,包括首页和购物车页的跳转按钮,点击后会跳转至相应页面;还有加入购物车与立即购买按钮,点击后能唤出规格选择弹窗,选择完商品后就能够执行对应的操作: + +底部动作栏: + +![](https://qcloudimg.tencent-cloud.cn/raw/862684258b666755e5ea1dae45bdecfe.png) + +点击后唤出规格选择弹窗: + +![](https://qcloudimg.tencent-cloud.cn/raw/de44ca5df5e297415d1863eb6cac7c1e.png) + +### 购物车页 + +![](https://qcloudimg.tencent-cloud.cn/raw/eec6aa7cbdf1aefae293a68e867f8501.png) + +购物车页可以查看购物车项。对于每个购物车项,可以进行删除、修改数量的操作。选中购物车项后,可以点击「去结算」按钮前往订单确认页进行订单创建并购买。 + +左滑购物车项进行删除: + +![](https://qcloudimg.tencent-cloud.cn/raw/21311abac70b47bc352734e4e6da0485.png) + +选中购物车项后可进行结算: + +![](https://qcloudimg.tencent-cloud.cn/raw/a2af456542cd7afae411cffaaac9d501.png) + +点击「去结算」按钮后跳转至订单确认页: + +![](https://qcloudimg.tencent-cloud.cn/raw/c5e01f1c2cb4ef6eae6bd0fce0574a95.png) + +### 订单确认页 + +![](https://qcloudimg.tencent-cloud.cn/raw/8789c377b15e0a222475b5253cd45c0a.png) + +订单确认页提供以下功能: + +● 地址选择栏 + +● 订单信息展示 + +● 提交订单并支付 + +#### 地址选择栏 + +地址选择栏展示当前订单的配送地址。如果未选择地址,将无法提交订单。点击地址选择栏后可跳转至地址列表,对地址进行增加、删除、修改和选择的操作。选择完地址后,会返回至此页,届时可继续进行订单提交。 + +地址选择栏: + +![](https://qcloudimg.tencent-cloud.cn/raw/febf20b5ae98eacd2b69a93ecbba684e.png) + +地址列表: + +![](https://qcloudimg.tencent-cloud.cn/raw/c4d74f2749a9029c7a731071165fdd9f.png) + +选择完地址后,跳回订单确认页: + +![](https://qcloudimg.tencent-cloud.cn/raw/4faedcef881787e25926e69f50ec18cd.png) + +#### 订单信息展示 + +在订单确认页会展示当前创建的订单的详细内容,包括订单项、总价等信息。 + +![](https://qcloudimg.tencent-cloud.cn/raw/268fe7c66225fe45d8ace3fd933e6b31.png) + +#### 提交订单并支付 + +在选择完地址后,点击提交订单,即可创建订单,并弹出支付弹窗。若支付流程失败,可在订单列表中查询到待支付的订单,重新支付。 + +![](https://qcloudimg.tencent-cloud.cn/raw/e81466d3fe35a15a1add1467e3b9114a.png) + +### 用户中心页 + +用户中心页展示用户信息、订单列表和地址列表。 + +![](https://qcloudimg.tencent-cloud.cn/raw/f7e8527c81b88c69bb800f3afbaa9e62.png) + +## 数据模型 + +云开发电商模板附带了一系列的数据模型。对于数据模型的能力说明,请查看[云开发数据模型](https://docs.cloudbase.net/model/introduce)。 + +本模版附带的数据模型有: + +|名称|标识|描述| +|---|---|---| +|电商SPU|shop_spu|一件商品,对应电商概念中的 SPU,即 Standard Product Unit。| +|电商SPU分类|shop_spu_cate|商品分类,每个商品分类都可以有自己的子分类、父分类和对应的商品。| +|电商SPU属性名|shop_attr_name|商品的属性名,如数量。| +|电商SPU属性值|shop_attr_value|商品的属性值,如数量对应的值可能为「1 件」、「10 件」。| +|电商SPU评价|shop_comment|用户对商品做出的评价。| +|电商SKU|shop_sku|一件有具体属性的商品销售单位。每个 SPU 都可以对应多个 SKU。举例来说,A 品牌的平板电脑为 SPU,其对应的 SKU 为 A 品牌的 16GB 内存、白色的平板电脑。| +|电商购物车项|shop_cart_item|| +|电商订单|shop_order|| +|电商订单项|shop_order_item|| +|电商收货信息|shop_delivery_info|| +|电商首页轮播图|shop_home_swiper_image|| + +## 应用配置说明 + +### 配置商品 + +#### 配置SPU + +在「电商SPU」数据模型中,填入 SPU 数据。这里我们添加一行「荔枝」的 SPU 数据: + +![](https://qcloudimg.tencent-cloud.cn/raw/adda72dbcf9f96d41236c128cb11522e.png) + +#### 配置SKU + +添加完 SPU 后,我们还需要为其添加 SKU 数据。SKU 可以理解为具有具体规格/属性的 SPU,比如荔枝的属性可以有数量、大小等等。不同数量、不同大小的荔枝会有不同的价格、不同的库存。 + +首先,我们为荔枝配置属性选项。 + +在「电商SPU属性名」数据模型中,我们添加一行「大小」属性: + +![](https://qcloudimg.tencent-cloud.cn/raw/dd45aedca3c2235dce20c749faf6a890.png) + +添加完属性名后,我们还需要为此属性名配置属性值。 + +我们在「电商SPU属性值」配置两个属性值,分别是大、小: + +![](https://qcloudimg.tencent-cloud.cn/raw/e42f566f0622d4825da0d56acc085e25.png) + +![](https://qcloudimg.tencent-cloud.cn/raw/5a7b0b51e9062366f5412ef851b2d606.png) + +至此,我们配置好了「大小」这个属性名,这个属性名下有两个可选的属性值,分别为「大」和「小」。 + +现在,我们可以为「荔枝」SPU 添加 SKU 了,分别是「大荔枝」和「小荔枝」,他们的价格户不相同: + +![](https://qcloudimg.tencent-cloud.cn/raw/a829aae86242a37c5130a0d1e0568d8d.png) + +![](https://qcloudimg.tencent-cloud.cn/raw/a5a6975a715e92f3abf9296cc53a279b.png) + +#### 配置商品分类 + +每个 SPU 可以有多个分类,分类下也可以有子分类。现在我们来为荔枝添加二级分类,分别是「美食」->「水果」。 + +我们在「电商SPU分类」数据模型中添加「美食」分类: + +![](https://qcloudimg.tencent-cloud.cn/raw/b511ddf9b3069097f5c95f19779392f4.png) + +然后再添加「水果」分类,添加时把父分类选为「美食」,并在 SPU 中添加「荔枝」: + +![](https://qcloudimg.tencent-cloud.cn/raw/2a3c6522f9af616740e57a7db80ef94f.png) + +### 配置首页轮播图 + +首页轮播图会展示「电商首页轮播图」数据模型的第一行数据中的图片: + +![](https://qcloudimg.tencent-cloud.cn/raw/73b6b4c119ea526506f683a4cf1f8c13.png) diff --git a/miniprogram/tcb-shop/app.js b/miniprogram/tcb-shop/app.js new file mode 100644 index 0000000..2f1800e --- /dev/null +++ b/miniprogram/tcb-shop/app.js @@ -0,0 +1,18 @@ +import updateManager from './common/updateManager'; +import { init } from '@cloudbase/wx-cloud-client-sdk'; + +wx.cloud.init({ + env: 'your-env-id', // 指定云开发环境 ID +}); + +const client = init(wx.cloud); +const models = client.models; +globalThis.dataModel = models; +// 接下来就可以调用 models 上的数据模型增删改查等方法了 + +App({ + onLaunch: function () {}, + onShow: function () { + updateManager(); + }, +}); diff --git a/miniprogram/tcb-shop/app.json b/miniprogram/tcb-shop/app.json new file mode 100644 index 0000000..b4cea02 --- /dev/null +++ b/miniprogram/tcb-shop/app.json @@ -0,0 +1,76 @@ +{ + "pages": [ + "pages/home/home", + "pages/usercenter/index", + "pages/usercenter/person-info/index", + "pages/usercenter/address/list/index", + "pages/usercenter/address/edit/index", + "pages/goods/list/index", + "pages/goods/details/index", + "pages/goods/category/index", + "pages/goods/search/index", + "pages/goods/result/index", + "pages/cart/index", + "pages/order/order-confirm/index", + "pages/order/receipt/index", + "pages/order/pay-result/index", + "pages/order/order-list/index", + "pages/order/order-detail/index", + "pages/goods/comments/index", + "pages/order/apply-service/index", + "pages/order/after-service-list/index", + "pages/order/after-service-detail/index", + "pages/goods/comments/create/index", + "pages/goods/comments/create-list/index", + "pages/coupon/coupon-list/index", + "pages/coupon/coupon-detail/index", + "pages/coupon/coupon-activity-goods/index", + "pages/promotion-detail/index", + "pages/order/fill-tracking-no/index", + "pages/order/delivery-detail/index", + "pages/order/invoice/index", + "pages/usercenter/name-edit/index" + ], + "tabBar": { + "custom": true, + "color": "#666666", + "selectedColor": "#FF5F15", + "backgroundColor": "#ffffff", + "borderStyle": "black", + "list": [ + { + "pagePath": "pages/home/home", + "text": "首页" + }, + { + "pagePath": "pages/goods/category/index", + "text": "分类" + }, + { + "pagePath": "pages/cart/index", + "text": "购物车" + }, + { + "pagePath": "pages/usercenter/index", + "text": "我的" + } + ] + }, + "requiredPrivateInfos": [ + "chooseAddress" + ], + "lazyCodeLoading": "requiredComponents", + "usingComponents": {}, + "window": { + "backgroundTextStyle": "light", + "navigationBarBackgroundColor": "#fff", + "navigationBarTitleText": "Weixin", + "navigationBarTextStyle": "black" + }, + "sitemapLocation": "sitemap.json", + "permission": { + "scope.userLocation": { + "desc": "你的位置信息将用于小程序位置接口的效果展示" + } + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/app.wxss b/miniprogram/tcb-shop/app.wxss new file mode 100644 index 0000000..9ff2554 --- /dev/null +++ b/miniprogram/tcb-shop/app.wxss @@ -0,0 +1,3 @@ +@import 'style/iconfont.wxss'; + +@import 'style/theme.wxss'; \ No newline at end of file diff --git a/miniprogram/tcb-shop/commitlint.config.js b/miniprogram/tcb-shop/commitlint.config.js new file mode 100644 index 0000000..422b194 --- /dev/null +++ b/miniprogram/tcb-shop/commitlint.config.js @@ -0,0 +1 @@ +module.exports = { extends: ['@commitlint/config-conventional'] }; diff --git a/miniprogram/tcb-shop/common/updateManager.js b/miniprogram/tcb-shop/common/updateManager.js new file mode 100644 index 0000000..c45de6d --- /dev/null +++ b/miniprogram/tcb-shop/common/updateManager.js @@ -0,0 +1,29 @@ +export default () => { + if (!wx.canIUse('getUpdateManager')) { + return; + } + + const updateManager = wx.getUpdateManager(); + + updateManager.onCheckForUpdate(function (res) { + // 请求完新版本信息的回调 + console.log('版本信息', res); + }); + + updateManager.onUpdateReady(function () { + wx.showModal({ + title: '更新提示', + content: '新版本已经准备好,是否重启应用?', + success(res) { + if (res.confirm) { + // 新的版本已经下载好,调用 applyUpdate 应用新版本并重启 + updateManager.applyUpdate(); + } + }, + }); + }); + + updateManager.onUpdateFailed(function () { + // 新版本下载失败 + }); +}; diff --git a/miniprogram/tcb-shop/components/cloud-template-dialog/index.js b/miniprogram/tcb-shop/components/cloud-template-dialog/index.js new file mode 100644 index 0000000..cb0422b --- /dev/null +++ b/miniprogram/tcb-shop/components/cloud-template-dialog/index.js @@ -0,0 +1,41 @@ +const NEVER_SHOW_KEY = 'never_show_cloud_template_init_dialog'; + +Component({ + properties: { + show: { + type: Boolean, + value: true, + }, + url: { + type: String, + value: '', + }, + }, + + data: { checked: false }, + + methods: { + close() { + if (this.data.checked) { + wx.setStorageSync(NEVER_SHOW_KEY, 'true'); + } + }, + copy() { + wx.setClipboardData({ + data: this.data.url, + }); + }, + neverShowChanged({ detail: { checked } }) { + this.setData({ checked }); + }, + }, + + lifetimes: { + attached: function () { + const ifNever = wx.getStorageSync(NEVER_SHOW_KEY); + this.setData({ + show: ifNever !== 'true', + }); + }, + }, +}); diff --git a/miniprogram/tcb-shop/components/cloud-template-dialog/index.json b/miniprogram/tcb-shop/components/cloud-template-dialog/index.json new file mode 100644 index 0000000..677ffc2 --- /dev/null +++ b/miniprogram/tcb-shop/components/cloud-template-dialog/index.json @@ -0,0 +1,9 @@ +{ + "component": true, + "styleIsolation": "shared", + "usingComponents": { + "t-dialog": "tdesign-miniprogram/dialog/dialog", + "t-button": "tdesign-miniprogram/button/button", + "t-radio": "tdesign-miniprogram/radio/radio" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/components/cloud-template-dialog/index.wxml b/miniprogram/tcb-shop/components/cloud-template-dialog/index.wxml new file mode 100644 index 0000000..9e4a11e --- /dev/null +++ b/miniprogram/tcb-shop/components/cloud-template-dialog/index.wxml @@ -0,0 +1,13 @@ + + + + + {{url}} + + + + 复制 + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/components/cloud-template-dialog/index.wxss b/miniprogram/tcb-shop/components/cloud-template-dialog/index.wxss new file mode 100644 index 0000000..9089da6 --- /dev/null +++ b/miniprogram/tcb-shop/components/cloud-template-dialog/index.wxss @@ -0,0 +1,27 @@ +.url-container text { + word-break: break-all; +} + +.btns { + display: flex; + justify-content: center; + align-items: center; + gap: 10px; +} + +.template-btn { + flex-grow: 1; +} + +.template-dialog .t-dialog { + margin-bottom: 20px; +} + + +.template-dialog .t-dialog__footer { + display: none; +} + +.template-dialog .t-radio--block { + padding-left: 0; +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/components/filter-popup/index.js b/miniprogram/tcb-shop/components/filter-popup/index.js new file mode 100644 index 0000000..14c69fd --- /dev/null +++ b/miniprogram/tcb-shop/components/filter-popup/index.js @@ -0,0 +1,36 @@ +Component({ + externalClasses: ['wr-class'], + + options: { + multipleSlots: true, + }, + + properties: { + show: { + type: Boolean, + observer(show) { + this.setData({ visible: show }); + }, + }, + closeBtn: { + type: Boolean, + value: false, + }, + }, + + data: { visible: false }, + + methods: { + reset() { + this.triggerEvent('reset'); + }, + confirm() { + this.triggerEvent('confirm'); + }, + close() { + this.triggerEvent('showFilterPopupClose'); + + this.setData({ visible: false }); + }, + }, +}); diff --git a/miniprogram/tcb-shop/components/filter-popup/index.json b/miniprogram/tcb-shop/components/filter-popup/index.json new file mode 100644 index 0000000..f5d3702 --- /dev/null +++ b/miniprogram/tcb-shop/components/filter-popup/index.json @@ -0,0 +1,6 @@ +{ + "component": true, + "usingComponents": { + "t-popup": "tdesign-miniprogram/popup/popup" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/components/filter-popup/index.wxml b/miniprogram/tcb-shop/components/filter-popup/index.wxml new file mode 100644 index 0000000..2932624 --- /dev/null +++ b/miniprogram/tcb-shop/components/filter-popup/index.wxml @@ -0,0 +1,18 @@ + + + + + 重置 + + 确定 + + + + + diff --git a/miniprogram/tcb-shop/components/filter-popup/index.wxss b/miniprogram/tcb-shop/components/filter-popup/index.wxss new file mode 100644 index 0000000..a206a82 --- /dev/null +++ b/miniprogram/tcb-shop/components/filter-popup/index.wxss @@ -0,0 +1,39 @@ +.content .filter-btns-wrap { + width: 100%; + position: absolute; + bottom: calc(20rpx + env(safe-area-inset-bottom)); + display: flex; + flex-direction: row; + border-radius: 10rpx 0 0 10rpx; + padding: 16rpx 32rpx; + border-top: 1rpx solid #e5e5e5; + box-sizing: border-box; +} + +.filter-btn { + flex: 1; + display: flex; + justify-content: center; + align-items: center; + font-size: 28rpx; + font-weight: 500; + height: 80rpx; +} + +.btn-reset { + color: #fa4126; + background: rgba(255, 255, 255, 1); + position: relative; + border: 1rpx solid #fa4126; + border-radius: 84rpx 0 0 84rpx; +} + +.btn-confirm { + border-radius: 0 84rpx 84rpx 0; + border: 1rpx solid #fa4126; +} + +.btn-confirm { + color: #fff; + background: #fa4126; +} diff --git a/miniprogram/tcb-shop/components/filter/index.js b/miniprogram/tcb-shop/components/filter/index.js new file mode 100644 index 0000000..dc99b95 --- /dev/null +++ b/miniprogram/tcb-shop/components/filter/index.js @@ -0,0 +1,84 @@ +Component({ + externalClasses: ['wr-class'], + + options: { + multipleSlots: true, + }, + + properties: { + overall: { + type: Number, + value: 1, + observer(overall) { + this.setData({ + overall, + }); + }, + }, + layout: { + type: Number, + value: 1, + observer(layout) { + this.setData({ + layout, + }); + }, + }, + sorts: { + type: String, + value: '', + observer(sorts) { + this.setData({ + sorts, + }); + }, + }, + color: { + type: String, + value: '#FA550F', + }, + }, + + data: { + layout: 1, + overall: 1, + sorts: '', + }, + + methods: { + onChangeShowAction() { + const { layout } = this.data; + const nextLayout = layout === 1 ? 0 : 1; + this.triggerEvent('change', { ...this.properties, layout: nextLayout }); + }, + + handlePriseSort() { + const { sorts } = this.data; + this.triggerEvent('change', { + ...this.properties, + overall: 0, + sorts: sorts === 'desc' ? 'asc' : 'desc', + }); + }, + + open() { + this.triggerEvent('showFilterPopup', { + show: true, + }); + }, + + onOverallAction() { + const { overall } = this.data; + const nextOverall = overall === 1 ? 0 : 1; + const nextData = { + sorts: '', + prices: [], + }; + this.triggerEvent('change', { + ...this.properties, + ...nextData, + overall: nextOverall, + }); + }, + }, +}); diff --git a/miniprogram/tcb-shop/components/filter/index.json b/miniprogram/tcb-shop/components/filter/index.json new file mode 100644 index 0000000..7464ae6 --- /dev/null +++ b/miniprogram/tcb-shop/components/filter/index.json @@ -0,0 +1,6 @@ +{ + "component": true, + "usingComponents": { + "t-icon": "tdesign-miniprogram/icon/icon" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/components/filter/index.wxml b/miniprogram/tcb-shop/components/filter/index.wxml new file mode 100644 index 0000000..9048bfd --- /dev/null +++ b/miniprogram/tcb-shop/components/filter/index.wxml @@ -0,0 +1,37 @@ + + + + + 综合 + + + 价格 + + + + + + + 筛选 + + + + + + + diff --git a/miniprogram/tcb-shop/components/filter/index.wxss b/miniprogram/tcb-shop/components/filter/index.wxss new file mode 100644 index 0000000..97728f9 --- /dev/null +++ b/miniprogram/tcb-shop/components/filter/index.wxss @@ -0,0 +1,50 @@ +.filter-wrap { + width: 100%; + height: 88rpx; + display: flex; + justify-content: space-between; + position: relative; + background: #fff; +} + +.filter-right-content { + height: 100%; + flex-basis: 100rpx; + text-align: center; + line-height: 88rpx; +} + +.filter-left-content { + height: 100%; + display: flex; + flex-grow: 2; + flex-flow: row nowrap; + justify-content: space-between; +} + +.filter-left-content .filter-item { + flex: 1; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + font-size: 26rpx; + line-height: 36rpx; + font-weight: 400; + color: rgba(51, 51, 51, 1); +} + +.filter-left-content .filter-item .filter-price { + display: flex; + flex-direction: column; + margin-left: 6rpx; + justify-content: space-between; +} + +.filter-left-content .filter-item .wr-filter { + margin-left: 8rpx; +} + +.filter-left-content .filter-active-item { + color: #fa550f; +} diff --git a/miniprogram/tcb-shop/components/goods-card/index.js b/miniprogram/tcb-shop/components/goods-card/index.js new file mode 100644 index 0000000..d1d63cf --- /dev/null +++ b/miniprogram/tcb-shop/components/goods-card/index.js @@ -0,0 +1,141 @@ +Component({ + options: { + addGlobalClass: true, + }, + + properties: { + id: { + type: String, + value: '', + observer(id) { + this.genIndependentID(id); + if (this.properties.thresholds?.length) { + this.createIntersectionObserverHandle(); + } + }, + }, + data: { + type: Object, + observer(data) { + if (!data) { + return; + } + let isValidityLinePrice = true; + if (data.originPrice && data.price && data.originPrice < data.price) { + isValidityLinePrice = false; + } + this.setData({ goods: data, isValidityLinePrice }); + }, + }, + currency: { + type: String, + value: '¥', + }, + + thresholds: { + type: Array, + value: [], + observer(thresholds) { + if (thresholds && thresholds.length) { + this.createIntersectionObserverHandle(); + } else { + this.clearIntersectionObserverHandle(); + } + }, + }, + }, + + data: { + independentID: '', + goods: { id: '' }, + isValidityLinePrice: false, + }, + + lifetimes: { + ready() { + this.init(); + }, + detached() { + this.clear(); + }, + }, + + pageLifeTimes: {}, + + methods: { + clickHandle() { + this.triggerEvent('click', { goods: this.data.goods }); + }, + + clickThumbHandle() { + this.triggerEvent('thumb', { goods: this.data.goods }); + }, + + addCartHandle(e) { + const { id } = e.currentTarget; + const { id: cardID } = e.currentTarget.dataset; + this.triggerEvent('add-cart', { + ...e.detail, + id, + cardID, + goods: this.data.goods, + }); + }, + + genIndependentID(id) { + let independentID; + if (id) { + independentID = id; + } else { + independentID = `goods-card-${~~(Math.random() * 10 ** 8)}`; + } + this.setData({ independentID }); + }, + + init() { + const { thresholds, id } = this.properties; + this.genIndependentID(id); + if (thresholds && thresholds.length) { + this.createIntersectionObserverHandle(); + } + }, + + clear() { + this.clearIntersectionObserverHandle(); + }, + + intersectionObserverContext: null, + + createIntersectionObserverHandle() { + if (this.intersectionObserverContext || !this.data.independentID) { + return; + } + this.intersectionObserverContext = this.createIntersectionObserver({ + thresholds: this.properties.thresholds, + }).relativeToViewport(); + + this.intersectionObserverContext.observe( + `#${this.data.independentID}`, + (res) => { + this.intersectionObserverCB(res); + }, + ); + }, + + intersectionObserverCB() { + this.triggerEvent('ob', { + goods: this.data.goods, + context: this.intersectionObserverContext, + }); + }, + + clearIntersectionObserverHandle() { + if (this.intersectionObserverContext) { + try { + this.intersectionObserverContext.disconnect(); + } catch (e) { } + this.intersectionObserverContext = null; + } + }, + }, +}); diff --git a/miniprogram/tcb-shop/components/goods-card/index.json b/miniprogram/tcb-shop/components/goods-card/index.json new file mode 100644 index 0000000..f199647 --- /dev/null +++ b/miniprogram/tcb-shop/components/goods-card/index.json @@ -0,0 +1,8 @@ +{ + "component": true, + "usingComponents": { + "price": "/components/price/index", + "t-icon": "tdesign-miniprogram/icon/icon", + "t-image": "/components/webp-image/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/components/goods-card/index.wxml b/miniprogram/tcb-shop/components/goods-card/index.wxml new file mode 100644 index 0000000..b28d772 --- /dev/null +++ b/miniprogram/tcb-shop/components/goods-card/index.wxml @@ -0,0 +1,16 @@ + + + + + + + + {{ goods.name }} + + + + + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/components/goods-card/index.wxss b/miniprogram/tcb-shop/components/goods-card/index.wxss new file mode 100644 index 0000000..6bf6879 --- /dev/null +++ b/miniprogram/tcb-shop/components/goods-card/index.wxss @@ -0,0 +1,136 @@ +.goods-card { + box-sizing: border-box; + font-size: 24rpx; + border-radius: 0 0 16rpx 16rpx; + border-bottom: none; +} + +.goods-card__main { + position: relative; + display: flex; + line-height: 1; + padding: 0; + background: transparent; + width: 342rpx; + border-radius: 0 0 16rpx 16rpx; + align-items: center; + justify-content: center; + margin-bottom: 16rpx; + flex-direction: column; +} + +.goods-card__thumb { + flex-shrink: 0; + position: relative; + width: 340rpx; + height: 340rpx; +} + +.goods-card__thumb:empty { + display: none; + margin: 0; +} + +.goods-card__img { + display: block; + width: 100%; + height: 100%; + border-radius: 16rpx 16rpx 0 0; + overflow: hidden; +} + +.goods-card__body { + display: flex; + /* flex-grow: 1; */ + box-sizing: border-box; + width: 100%; + flex: 1 1 auto; + background: #fff; + border-radius: 0 0 16rpx 16rpx; + padding: 16rpx 24rpx 18rpx; + flex-direction: column; +} + +.goods-card__upper { + display: flex; + flex-direction: column; + overflow: hidden; + flex: 1 1 auto; +} + +.goods-card__title { + flex-shrink: 0; + font-size: 28rpx; + color: #333; + font-weight: 400; + display: -webkit-box; + height: 72rpx; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + overflow: hidden; + word-break: break-word; + line-height: 36rpx; +} + +.goods-card__tags { + display: flex; + flex-direction: row; + flex-wrap: wrap; + margin: 8rpx 0 0 0; +} + +.goods-card__tag { + color: #fa4126; + background: transparent; + font-size: 20rpx; + border: 1rpx solid #fa4126; + padding: 0 8rpx; + border-radius: 16rpx; + line-height: 30rpx; + margin: 0 8rpx 8rpx 0; + display: block; + overflow: hidden; + white-space: nowrap; + word-break: keep-all; + text-overflow: ellipsis; +} + +.goods-card__down { + display: flex; + position: relative; + flex-direction: row; + justify-content: flex-start; + align-items: baseline; + line-height: 32rpx; + margin: 8rpx 0 0 0; +} + +.goods-card__origin-price { + white-space: nowrap; + font-weight: 700; + order: 2; + color: #bbbbbb; + font-size: 24rpx; + margin: 0 0 0 8rpx; +} + +.goods-card__add-cart { + order: 3; + margin: auto 0 0 auto; + position: absolute; + bottom: 0; + right: 0; +} + +.spec-for-price { + font-size: 36rpx; + white-space: nowrap; + font-weight: 700; + order: 1; + color: #fa4126; + margin: 0; +} + +.spec-for-symbol { + font-size: 24rpx; +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/components/goods-list/index.js b/miniprogram/tcb-shop/components/goods-list/index.js new file mode 100644 index 0000000..5184903 --- /dev/null +++ b/miniprogram/tcb-shop/components/goods-list/index.js @@ -0,0 +1,62 @@ +Component({ + externalClasses: ['wr-class'], + + properties: { + goodsList: { + type: Array, + value: [], + }, + id: { + type: String, + value: '', + observer: (id) => { + this.genIndependentID(id); + }, + }, + thresholds: { + type: Array, + value: [], + }, + }, + + data: { + independentID: '', + }, + + lifetimes: { + ready() { + this.init(); + }, + }, + + methods: { + onClickGoods(e) { + const { index } = e.currentTarget.dataset; + this.triggerEvent('click', { ...e.detail, index }); + }, + + onAddCart(e) { + const { index } = e.currentTarget.dataset; + this.triggerEvent('addcart', { ...e.detail, index }); + }, + + onClickGoodsThumb(e) { + const { index } = e.currentTarget.dataset; + this.triggerEvent('thumb', { ...e.detail, index }); + }, + + init() { + this.genIndependentID(this.id || ''); + }, + + genIndependentID(id) { + if (id) { + this.setData({ independentID: id }); + } else { + this.setData({ + independentID: `goods-list-${~~(Math.random() * 10 ** 8)}`, + }); + } + }, + }, +}); diff --git a/miniprogram/tcb-shop/components/goods-list/index.json b/miniprogram/tcb-shop/components/goods-list/index.json new file mode 100644 index 0000000..bdaa23d --- /dev/null +++ b/miniprogram/tcb-shop/components/goods-list/index.json @@ -0,0 +1,6 @@ +{ + "component": true, + "usingComponents": { + "goods-card": "/components/goods-card/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/components/goods-list/index.wxml b/miniprogram/tcb-shop/components/goods-list/index.wxml new file mode 100644 index 0000000..5a9a804 --- /dev/null +++ b/miniprogram/tcb-shop/components/goods-list/index.wxml @@ -0,0 +1,16 @@ + + + + + + diff --git a/miniprogram/tcb-shop/components/goods-list/index.wxss b/miniprogram/tcb-shop/components/goods-list/index.wxss new file mode 100644 index 0000000..7262a4d --- /dev/null +++ b/miniprogram/tcb-shop/components/goods-list/index.wxss @@ -0,0 +1,7 @@ +.goods-list-wrap { + display: flex; + flex-flow: row wrap; + justify-content: space-between; + padding: 0; + background: #fff; +} diff --git a/miniprogram/tcb-shop/components/load-more/index.js b/miniprogram/tcb-shop/components/load-more/index.js new file mode 100644 index 0000000..b71a839 --- /dev/null +++ b/miniprogram/tcb-shop/components/load-more/index.js @@ -0,0 +1,54 @@ +Component({ + externalClasses: ['wr-class', 'wr-class--no-more'], + + options: { multipleSlots: true }, + + properties: { + status: { + type: Number, + value: 0, + }, + loadingText: { + type: String, + value: '加载中...', + }, + noMoreText: { + type: String, + value: '没有更多了', + }, + failedText: { + type: String, + value: '加载失败,点击重试', + }, + color: { + type: String, + value: '#BBBBBB', + }, + failedColor: { + type: String, + value: '#FA550F', + }, + size: { + type: null, + value: '40rpx', + }, + loadingBackgroundColor: { + type: String, + value: '#F5F5F5', + }, + listIsEmpty: { + type: Boolean, + value: false, + }, + }, + + methods: { + /** 点击处理 */ + tapHandle() { + // 失败重试 + if (this.data.status === 3) { + this.triggerEvent('retry'); + } + }, + }, +}); diff --git a/miniprogram/tcb-shop/components/load-more/index.json b/miniprogram/tcb-shop/components/load-more/index.json new file mode 100644 index 0000000..94e107c --- /dev/null +++ b/miniprogram/tcb-shop/components/load-more/index.json @@ -0,0 +1,7 @@ +{ + "component": true, + "usingComponents": { + "t-loading": "tdesign-miniprogram/loading/loading", + "t-divider": "tdesign-miniprogram/divider/divider" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/components/load-more/index.wxml b/miniprogram/tcb-shop/components/load-more/index.wxml new file mode 100644 index 0000000..4b69c86 --- /dev/null +++ b/miniprogram/tcb-shop/components/load-more/index.wxml @@ -0,0 +1,31 @@ + + + + + + + + {{noMoreText}} + + + + + 加载失败 + 刷新 + + + + + diff --git a/miniprogram/tcb-shop/components/load-more/index.wxss b/miniprogram/tcb-shop/components/load-more/index.wxss new file mode 100644 index 0000000..bbaf636 --- /dev/null +++ b/miniprogram/tcb-shop/components/load-more/index.wxss @@ -0,0 +1,35 @@ +.load-more { + font-size: 24rpx; + height: 100rpx; + display: flex; + flex-direction: column; + justify-content: center; +} + +.load-more .t-class-loading { + display: flex; + justify-content: center; + + --td-loading-color: #fa4126; +} + +.load-more .t-class-loading-text { + color: #bbbbbb; +} + +.t-class-divider-content { + margin: 0 10rpx; + color: #bbbbbb; +} +.load-more .t-class-indicator { + color: #b9b9b9 !important; +} + +.load-more__error { + margin: auto; +} + +.load-more__refresh-btn { + margin-left: 16rpx; + color: #fa4126; +} diff --git a/miniprogram/tcb-shop/components/loading-content/index.js b/miniprogram/tcb-shop/components/loading-content/index.js new file mode 100644 index 0000000..4c6b925 --- /dev/null +++ b/miniprogram/tcb-shop/components/loading-content/index.js @@ -0,0 +1,23 @@ +Component({ + externalClasses: ['wr-class'], + properties: { + position: { + type: String, + value: 'static', + }, + noMask: Boolean, + type: { + type: String, + value: 'circular', + }, + vertical: Boolean, + size: { + type: String, + value: '50rpx', + }, + backgroundColor: { + type: String, + value: 'rgba(0, 0, 0, .6)', + }, + }, +}); diff --git a/miniprogram/tcb-shop/components/loading-content/index.json b/miniprogram/tcb-shop/components/loading-content/index.json new file mode 100644 index 0000000..c08b2ef --- /dev/null +++ b/miniprogram/tcb-shop/components/loading-content/index.json @@ -0,0 +1,6 @@ +{ + "component": true, + "usingComponents": { + "t-loading": "tdesign-miniprogram/loading/loading" + } +} diff --git a/miniprogram/tcb-shop/components/loading-content/index.wxml b/miniprogram/tcb-shop/components/loading-content/index.wxml new file mode 100644 index 0000000..add589c --- /dev/null +++ b/miniprogram/tcb-shop/components/loading-content/index.wxml @@ -0,0 +1,11 @@ + + + + + + diff --git a/miniprogram/tcb-shop/components/loading-content/index.wxss b/miniprogram/tcb-shop/components/loading-content/index.wxss new file mode 100644 index 0000000..d3c112b --- /dev/null +++ b/miniprogram/tcb-shop/components/loading-content/index.wxss @@ -0,0 +1,23 @@ +.loading-content { + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.6); + position: relative; +} +.loading-content.absolute { + position: absolute; + z-index: 1; + left: 0; + top: 0; +} +.loading-content.fixed { + position: fixed; + z-index: 1; + left: 0; + top: 0; +} +.loading-content .loading { + width: 100%; + height: 100%; + visibility: visible; +} diff --git a/miniprogram/tcb-shop/components/loading-dialog/index.js b/miniprogram/tcb-shop/components/loading-dialog/index.js new file mode 100644 index 0000000..7c4a2b9 --- /dev/null +++ b/miniprogram/tcb-shop/components/loading-dialog/index.js @@ -0,0 +1,8 @@ +Component({ + properties: { + show: { + type: Boolean, + value: false, + }, + }, +}); diff --git a/miniprogram/tcb-shop/components/loading-dialog/index.json b/miniprogram/tcb-shop/components/loading-dialog/index.json new file mode 100644 index 0000000..95170eb --- /dev/null +++ b/miniprogram/tcb-shop/components/loading-dialog/index.json @@ -0,0 +1,8 @@ +{ + "component": true, + "usingComponents": { + "t-loading": "tdesign-miniprogram/loading/loading", + "t-dialog": "tdesign-miniprogram/dialog/dialog" + }, + "styleIsolation": "shared" +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/components/loading-dialog/index.wxml b/miniprogram/tcb-shop/components/loading-dialog/index.wxml new file mode 100644 index 0000000..44fd109 --- /dev/null +++ b/miniprogram/tcb-shop/components/loading-dialog/index.wxml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/components/loading-dialog/index.wxss b/miniprogram/tcb-shop/components/loading-dialog/index.wxss new file mode 100644 index 0000000..9748562 --- /dev/null +++ b/miniprogram/tcb-shop/components/loading-dialog/index.wxss @@ -0,0 +1,11 @@ +.loading-dialog .t-dialog{ + width: unset; +} + +.loading-dialog .t-dialog__content { + padding: 80rpx; +} + +.loading-dialog .t-dialog__footer { + display: none; +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/components/price/index.js b/miniprogram/tcb-shop/components/price/index.js new file mode 100644 index 0000000..c0218f7 --- /dev/null +++ b/miniprogram/tcb-shop/components/price/index.js @@ -0,0 +1,71 @@ +Component({ + externalClasses: ['wr-class', 'symbol-class', 'decimal-class'], + useStore: [], + properties: { + priceUnit: { + type: String, + value: 'fen', + }, // 价格单位,分 | 元, fen,yuan + price: { + type: null, + value: '', + observer(price) { + this.format(price); + }, + }, // 价格, 以分为单位 + type: { + type: String, + value: '', // + }, // main 粗体, lighter 细体, mini 黑色, del 中划线, delthrough 中划线,包括货币符号 + symbol: { + type: String, + value: '¥', // '¥', + }, // 货币符号,默认是人民币符号¥ + fill: Boolean, // 是否自动补齐两位小数 + decimalSmaller: Boolean, // 小数字号小一点 + lineThroughWidth: { + type: null, + value: '0.12em', + }, // 划线价线条高度 + }, + + data: { + pArr: [], + }, + + methods: { + format(price) { + price = parseFloat(`${price}`); + const pArr = []; + if (!isNaN(price)) { + const isMinus = price < 0; + if (isMinus) { + price = -price; + } + if (this.properties.priceUnit === 'yuan') { + const priceSplit = price.toString().split('.'); + pArr[0] = priceSplit[0]; + pArr[1] = !priceSplit[1] + ? '00' + : priceSplit[1].length === 1 + ? `${priceSplit[1]}0` + : priceSplit[1]; + } else { + price = Math.round(price * 10 ** 8) / 10 ** 8; // 恢复精度丢失 + price = Math.ceil(price); // 向上取整 + pArr[0] = price >= 100 ? `${price}`.slice(0, -2) : '0'; + pArr[1] = `${price + 100}`.slice(-2); + } + if (!this.properties.fill) { + // 如果 fill 为 false, 不显示小数末尾的0 + if (pArr[1] === '00') pArr[1] = ''; + else if (pArr[1][1] === '0') pArr[1] = pArr[1][0]; + } + if (isMinus) { + pArr[0] = `-${pArr[0]}`; + } + } + this.setData({ pArr }); + }, + }, +}); diff --git a/miniprogram/tcb-shop/components/price/index.json b/miniprogram/tcb-shop/components/price/index.json new file mode 100644 index 0000000..a89ef4d --- /dev/null +++ b/miniprogram/tcb-shop/components/price/index.json @@ -0,0 +1,4 @@ +{ + "component": true, + "usingComponents": {} +} diff --git a/miniprogram/tcb-shop/components/price/index.wxml b/miniprogram/tcb-shop/components/price/index.wxml new file mode 100644 index 0000000..7f4f9d9 --- /dev/null +++ b/miniprogram/tcb-shop/components/price/index.wxml @@ -0,0 +1,21 @@ + + var REGEXP = getRegExp('^\d+(\.\d+)?$'); + function addUnit(value) { + if (value == null) { + return ''; + } + return REGEXP.test('' + value) ? value + 'rpx' : value; + } + module.exports = { + addUnit: addUnit + }; + + + + {{symbol}} + + {{pArr[0]}} + .{{pArr[1]}} + + + diff --git a/miniprogram/tcb-shop/components/price/index.wxss b/miniprogram/tcb-shop/components/price/index.wxss new file mode 100644 index 0000000..45da266 --- /dev/null +++ b/miniprogram/tcb-shop/components/price/index.wxss @@ -0,0 +1,66 @@ +:host { + display: inline-block; + display: inline-block; + font-weight: inherit; +} +.inline { + display: inline; + white-space: nowrap; +} +.price { + display: inline; + color: inherit; + font-size: inherit; + text-decoration: inherit; +} + +.lighter { + font-weight: 400; + font-size: 32rpx; +} +.mini { + font-size: 24rpx; + color: #5d5d5d; + font-weight: 400; +} +.del .pprice { + font-size: 32rpx; + color: #9b9b9b; + text-decoration: line-through; + font-weight: 400; +} +.delthrough { + position: relative; +} +.delthrough .line { + position: absolute; + top: 50%; + left: 0; + right: 0; + transform: translateY(-50%); + margin: 0; + background-color: currentColor; +} + +.symbol { + display: inline; + color: inherit; + font-size: inherit; + font-size: 0.8em; +} +.pprice { + display: inline; + margin: 0 0 0 4rpx; +} +.integer { + color: inherit; + font-size: inherit; +} +.decimal { + color: inherit; + font-size: inherit; +} +.decimal.smaller { + font-size: 0.8em; + vertical-align: baseline; +} diff --git a/miniprogram/tcb-shop/components/swipeout/index.js b/miniprogram/tcb-shop/components/swipeout/index.js new file mode 100644 index 0000000..3db6b79 --- /dev/null +++ b/miniprogram/tcb-shop/components/swipeout/index.js @@ -0,0 +1,79 @@ +let ARRAY = []; +Component({ + externalClasses: ['wr-class'], + + options: { + multipleSlots: true, + }, + properties: { + disabled: Boolean, + leftWidth: { + type: Number, + value: 0, + }, + rightWidth: { + type: Number, + value: 0, + }, + asyncClose: Boolean, + }, + attached() { + ARRAY.push(this); + }, + + detached() { + ARRAY = ARRAY.filter((item) => item !== this); + }, + + /** + * Component initial data + */ + data: { + wrapperStyle: '', + asyncClose: false, + closed: true, + }, + + /** + * Component methods + */ + methods: { + open(position) { + this.setData({ closed: false }); + this.triggerEvent('close', { + position, + instance: this, + }); + }, + + close() { + this.setData({ closed: true }); + }, + + closeOther() { + ARRAY.filter((item) => item !== this).forEach((item) => item.close()); + }, + + noop() { + return; + }, + + onClick(event) { + const { key: position = 'outside' } = event.currentTarget.dataset; + this.triggerEvent('click', position); + + if (this.data.closed) { + return; + } + + if (this.data.asyncClose) { + this.triggerEvent('close', { + position, + instance: this, + }); + } else { + this.close(); + } + }, + }, +}); diff --git a/miniprogram/tcb-shop/components/swipeout/index.json b/miniprogram/tcb-shop/components/swipeout/index.json new file mode 100644 index 0000000..a89ef4d --- /dev/null +++ b/miniprogram/tcb-shop/components/swipeout/index.json @@ -0,0 +1,4 @@ +{ + "component": true, + "usingComponents": {} +} diff --git a/miniprogram/tcb-shop/components/swipeout/index.wxml b/miniprogram/tcb-shop/components/swipeout/index.wxml new file mode 100644 index 0000000..353bf72 --- /dev/null +++ b/miniprogram/tcb-shop/components/swipeout/index.wxml @@ -0,0 +1,174 @@ + + var THRESHOLD = 0.3; + var MIN_DISTANCE = 10; + var owner; + var state; + + var getState = function(ownerInstance) { + owner = ownerInstance; + state = owner.getState(); + state.leftWidth = state.leftWidth || 0; + state.rightWidth = state.rightWidth || 0; + state.offset = state.offset || 0; + state.startOffset = state.startOffset || 0; + }; + + var initRightWidth = function(newVal, oldVal, ownerInstance) { + getState(ownerInstance); + state.rightWidth = newVal; + if (state.offset < 0) { + swipeMove(-state.rightWidth); + } + }; + + var initLeftWidth = function(newVal, oldVal, ownerInstance) { + getState(ownerInstance); + state.leftWidth = newVal; + if (state.offset > 0) { + swipeMove(state.leftWidth); + } + } + + var resetTouchStatus = function() { + state.direction = ''; + state.deltaX = 0; + state.deltaY = 0; + state.offsetX = 0; + state.offsetY = 0; + }; + + var touchMove = function(event) { + var touchPoint = event.touches[0]; + state.deltaX = touchPoint.clientX - state.startX; + state.deltaY = touchPoint.clientY - state.startY; + state.offsetX = Math.abs(state.deltaX); + state.offsetY = Math.abs(state.deltaY); + state.direction = state.direction || getDirection(state.offsetX, state.offsetY); + }; + + var getDirection = function(x, y) { + if (x > y && x > MIN_DISTANCE) { + return 'horizontal'; + } + if (y > x && y > MIN_DISTANCE) { + return 'vertical'; + } + return ''; + }; + + var range = function(num, min, max) { + return Math.min(Math.max(num, min), max); + }; + + var swipeMove = function(_offset = 0) { + state.offset = range( + _offset, + -state.rightWidth, + +state.leftWidth, + ); + + var transform = 'translate3d(' + state.offset + 'px, 0, 0)'; + var transition = state.dragging + ? 'none' + : 'transform .6s cubic-bezier(0.18, 0.89, 0.32, 1)'; + owner.selectComponent('#wrapper').setStyle({ + '-webkit-transform': transform, + '-webkit-transition': transition, + 'transform': transform, + 'transition': transition + }); + }; + + var close = function() { + swipeMove(0); + }; + + var onCloseChange = function(newVal, oldVal, ownerInstance) { + getState(ownerInstance); + if (newVal === oldVal) return; + if (newVal) { + close(); + } + }; + + var touchStart = function(event) { + resetTouchStatus(); + state.startOffset = state.offset; + var touchPoint = event.touches[0]; + state.startX = touchPoint.clientX; + state.startY = touchPoint.clientY; + owner.callMethod('closeOther'); + }; + + var startDrag = function(event, ownerInstance) { + getState(ownerInstance); + touchStart(event); + }; + + var onDrag = function(event, ownerInstance) { + getState(ownerInstance); + touchMove(event); + if (state.direction !== 'horizontal') { + return; + } + state.dragging = true; + swipeMove(state.startOffset + state.deltaX); + }; + + var open = function(position) { + var _offset = position === 'left' ? +state.leftWidth : -state.rightWidth; + owner.callMethod('open', { position: position }); + swipeMove(_offset); + }; + + var endDrag = function(event, ownerInstance) { + getState(ownerInstance); + state.dragging = false; + // 左/右侧有可滑动区域,且当前不是已open状态,且滑动幅度超过阈值时open左/右侧(滚动到该侧的最边上) + if (+state.rightWidth > 0 && -state.startOffset < +state.rightWidth && -state.offset > +state.rightWidth * THRESHOLD) { + open('right'); + } else if (+state.leftWidth > 0 && state.startOffset < +state.leftWidth && state.offset > +state.leftWidth * THRESHOLD) { + open('left'); + } else { + // 仅在有发生侧滑的情况下自动关闭(由js控制是否异步关闭) + if (state.startOffset !== state.offset) { + close(); + } + } + }; + + module.exports = { + initLeftWidth: initLeftWidth, + initRightWidth: initRightWidth, + startDrag: startDrag, + onDrag: onDrag, + endDrag: endDrag, + onCloseChange: onCloseChange + }; + + + + + + + + + + + + + diff --git a/miniprogram/tcb-shop/components/swipeout/index.wxss b/miniprogram/tcb-shop/components/swipeout/index.wxss new file mode 100644 index 0000000..1aa2943 --- /dev/null +++ b/miniprogram/tcb-shop/components/swipeout/index.wxss @@ -0,0 +1,18 @@ +.wr-swipeout { + position: relative; + overflow: hidden; +} +.wr-swipeout__left, +.wr-swipeout__right { + position: absolute; + top: 0; + height: 100%; +} +.wr-swipeout__left { + left: 0; + transform: translate3d(-100%, 0, 0); +} +.wr-swipeout__right { + right: 0; + transform: translate3d(100%, 0, 0); +} diff --git a/miniprogram/tcb-shop/components/webp-image/index.js b/miniprogram/tcb-shop/components/webp-image/index.js new file mode 100644 index 0000000..1706049 --- /dev/null +++ b/miniprogram/tcb-shop/components/webp-image/index.js @@ -0,0 +1,86 @@ +/* + * @Author: rileycai + * @Date: 2022-03-14 14:21:26 + * @LastEditTime: 2022-03-14 15:23:04 + * @LastEditors: rileycai + * @Description: webp-image组件对t-image包裹了一层,主要实现图片裁剪、webp压缩功能 + * @FilePath: /tdesign-miniprogram-starter/components/webp-image/index.js + */ +const systemInfo = wx.getSystemInfoSync(); +Component({ + externalClasses: ['t-class', 't-class-load'], + properties: { + loadFailed: { + type: String, + value: 'default', + }, + loading: { + type: String, + value: 'default', + }, + src: { + type: String, + value: '', + }, + mode: { + type: String, + value: 'aspectFill', + }, + webp: { + type: Boolean, + value: true, + }, + lazyLoad: { + type: Boolean, + value: false, + }, + showMenuByLongpress: { + type: Boolean, + value: false, + }, + }, + data: { + thumbHeight: 375, + thumbWidth: 375, + systemInfo, + }, + lifetimes: { + ready() { + const { mode } = this.properties; + // 获取容器的真实宽高,设置图片的裁剪宽度 + this.getRect('.J-image').then((res) => { + if (res) { + const { width, height } = res; + this.setData( + mode === 'heightFix' + ? { + thumbHeight: this.px2rpx(height) || 375, + } + : { + thumbWidth: this.px2rpx(width) || 375, + }, + ); + } + }); + }, + }, + methods: { + px2rpx(px) { + return (750 / (systemInfo.screenWidth || 375)) * px; + }, + getRect(selector) { + return new Promise((resolve) => { + if (!this.selectorQuery) { + this.selectorQuery = this.createSelectorQuery(); + } + this.selectorQuery.select(selector).boundingClientRect(resolve).exec(); + }); + }, + onLoad(e) { + this.triggerEvent('load', e.detail); + }, + onError(e) { + this.triggerEvent('error', e.detail); + }, + }, +}); diff --git a/miniprogram/tcb-shop/components/webp-image/index.json b/miniprogram/tcb-shop/components/webp-image/index.json new file mode 100644 index 0000000..6ffda42 --- /dev/null +++ b/miniprogram/tcb-shop/components/webp-image/index.json @@ -0,0 +1,6 @@ +{ + "component": true, + "usingComponents": { + "t-image": "tdesign-miniprogram/image/image" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/components/webp-image/index.wxml b/miniprogram/tcb-shop/components/webp-image/index.wxml new file mode 100644 index 0000000..1a917b2 --- /dev/null +++ b/miniprogram/tcb-shop/components/webp-image/index.wxml @@ -0,0 +1,14 @@ + + diff --git a/miniprogram/tcb-shop/components/webp-image/index.wxss b/miniprogram/tcb-shop/components/webp-image/index.wxss new file mode 100644 index 0000000..e69de29 diff --git a/miniprogram/tcb-shop/components/webp-image/utils.wxs b/miniprogram/tcb-shop/components/webp-image/utils.wxs new file mode 100644 index 0000000..4f6e5d1 --- /dev/null +++ b/miniprogram/tcb-shop/components/webp-image/utils.wxs @@ -0,0 +1,140 @@ +var isString = function (value) { + return typeof value === 'string'; +}; + +var isNumber = function (value) { + return typeof value === 'number'; +}; + +var getFileExt = function (src) { + var fileUrl = src.split('?')[0]; + var splitUlr = fileUrl.split('/'); + var filepath = splitUlr[splitUlr.length - 1]; + return filepath.split('.')[1] || 'jpg'; +}; + +function isUrl(url) { + // NOCC:ToolNameCheck(非敏感词) + var urlReg = getRegExp( + '/[(http(s)?)://(www.)?a-zA-Z0-9@:%._+~#=]{2,256}.[a-z]{2,6}\b([-a-zA-Z0-9@:%_+.~#?&//=]*)/', + 'ig', + ); + + return urlReg.test(url); +} + +function rpx2px(rpx, screenWidth) { + // px / systemWidth = rpx / 750 + var result = (rpx * (screenWidth || 375)) / 750; + + return Math.round(result); +} + +function imageMogr(url, options) { + if (!isString(url) || !url) return ''; + + if ( + url.indexOf('qlogo.cn') !== -1 || + url.indexOf('wxfile://') === 0 || + url.indexOf('http://tmp/wx') === 0 || + url.indexOf('imageMogr2') !== -1 + ) { + //qlogo.cn域名或者本地图片不做转换 + return url; + } //强制转https + + if (url.indexOf('http://') === 0) { + url = url.replace('http://', 'https://'); + } else if (url.indexOf('//') === 0) { + url = 'https:' + url; + } + + if (!options) return url; + + var width = Math.ceil(options.width), + height = Math.ceil(options.height), + format = options.format, + _optionsQuality = options.quality, + quality = _optionsQuality === undefined ? 70 : _optionsQuality, + _optionsStrip = options.strip, + strip = _optionsStrip === undefined ? true : _optionsStrip, + crop = options.crop; + var isValidWidth = isNumber(width) && width > 0; + var isValidHeight = isNumber(height) && height > 0; + var imageMogrStr = ''; + var size = ''; + + if (isValidWidth && isValidHeight) { + size = ''.concat(width, 'x').concat(height); + } else if (isValidWidth) { + size = ''.concat(width, 'x'); + } else if (isValidHeight) { + size = 'x'.concat(height); + } + + if (size) { + //缩放或者裁剪 + imageMogrStr += '/'.concat(crop ? 'crop' : 'thumbnail', '/').concat(size); + + if (crop) { + //裁剪目前需求只有以图片中心为基准 + imageMogrStr += '/gravity/center'; + } + } + + if (isNumber(quality)) { + //质量变换 + imageMogrStr += '/quality/'.concat(quality); + } + + if (strip) { + //去除元信息 + imageMogrStr += '/strip'; + } + + var ext = getFileExt(url); + + // gif 图片不做格式转换,否则会损坏动图 + if (ext === 'gif') { + imageMogrStr += '/cgif/1'; + } else if (format) { + //格式转换 + imageMogrStr += '/format/'.concat(format); + } + + if (format === 'jpg' || (!format && (ext === 'jpg' || ext === 'jpeg'))) { + //渐进式 jpg 加载 + imageMogrStr += '/interlace/1'; + } + if (!imageMogrStr) return url; + return '' + .concat(url) + .concat(url.indexOf('?') !== -1 ? '&' : '?', 'imageMogr2') + .concat(imageMogrStr); +} +function getSrc(options) { + if (!options.src) return ''; + + if (options.thumbWidth || options.thumbHeight) { + return imageMogr(options.src, { + width: + options.mode !== 'heightFix' + ? rpx2px(options.thumbWidth, options.systemInfo.screenWidth) * + options.systemInfo.pixelRatio + : null, + height: + options.mode !== 'widthFix' + ? rpx2px(options.thumbHeight, options.systemInfo.screenWidth) * + options.systemInfo.pixelRatio + : null, + format: options.webp ? 'webp' : null, + }); + } + + return ''; +} + +module.exports = { + imageMogr: imageMogr, + getSrc: getSrc, +}; diff --git a/miniprogram/tcb-shop/config/eslintCheck.js b/miniprogram/tcb-shop/config/eslintCheck.js new file mode 100644 index 0000000..9294baf --- /dev/null +++ b/miniprogram/tcb-shop/config/eslintCheck.js @@ -0,0 +1,91 @@ +/* eslint-disable prefer-template */ +/** + * 工程代码pre-commit 检查工具 + * @date 2019.9.4 + * @author 310227663@qq.com + */ +const { exec } = require('child_process'); +const chalk = require('chalk'); +const { CLIEngine } = require('eslint'); +const cli = new CLIEngine({}); +const { log } = console; + +function getErrorLevel(number) { + switch (number) { + case 2: + return 'error'; + case 1: + return 'warn'; + default: + } + return 'undefined'; +} +let pass = 0; +exec( + 'git diff --cached --name-only --diff-filter=ACM | grep -Ei "\\.ts$|\\.js$"', + (error, stdout) => { + if (stdout.length) { + const array = stdout.split('\n'); + array.pop(); + const { results } = cli.executeOnFiles(array); + let errorCount = 0; + let warningCount = 0; + results.forEach((result) => { + errorCount += result.errorCount; + warningCount += result.warningCount; + if (result.messages.length > 0) { + log('\n'); + log(result.filePath); + result.messages.forEach((obj) => { + const level = getErrorLevel(obj.severity); + if (level === 'warn') + log( + ' ' + + obj.line + + ':' + + obj.column + + '\t ' + + chalk.yellow(level) + + ' \0 ' + + obj.message + + '\t\t' + + chalk.grey(obj.ruleId) + + '', + ); + if (level === 'error') + log( + ' ' + + obj.line + + ':' + + obj.column + + '\t ' + + chalk.red.bold(level) + + ' \0 ' + + obj.message + + '\t\t ' + + chalk.grey(obj.ruleId) + + '', + ); + if (level === 'error') pass = 1; + }); + } + }); + if (warningCount > 0 || errorCount > 0) { + log( + '\n' + + chalk.bgRed.bold(errorCount + warningCount + ' problems') + + ' (' + + chalk.red.bold(errorCount) + + ' errors, ' + + chalk.yellow(warningCount) + + ' warnings) \0', + ); + } + !pass && log(chalk.green.bold('~~ Done: 代码检验通过,提交成功 ~~')); + process.exit(pass); + } + if (error !== null) { + log(`exec error: ${error}`); + } + }, +); diff --git a/miniprogram/tcb-shop/config/index.js b/miniprogram/tcb-shop/config/index.js new file mode 100644 index 0000000..26ca6ee --- /dev/null +++ b/miniprogram/tcb-shop/config/index.js @@ -0,0 +1,6 @@ +export const config = { + /** 是否使用mock代替api返回 */ + useMock: true, +}; + +export const cdnBase = 'https://we-retail-static-1300977798.cos.ap-guangzhou.myqcloud.com/retail-mp'; diff --git a/miniprogram/tcb-shop/config/model.js b/miniprogram/tcb-shop/config/model.js new file mode 100644 index 0000000..0e7f6b4 --- /dev/null +++ b/miniprogram/tcb-shop/config/model.js @@ -0,0 +1,12 @@ +export const DATA_MODEL_KEY = { + ATTR_VALUE: 'shop_attr_value', + CATE: 'shop_spu_cate', + CART_ITEM: 'shop_cart_item', + COMMENT: 'shop_comment', + DELIVERY_INFO: 'shop_delivery_info', + HOME_SWIPER: 'shop_home_swiper_image', + ORDER: 'shop_order', + ORDER_ITEM: 'shop_order_item', + SKU: 'shop_sku', + SPU: 'shop_spu', +}; diff --git a/miniprogram/tcb-shop/custom-tab-bar/data.js b/miniprogram/tcb-shop/custom-tab-bar/data.js new file mode 100644 index 0000000..ff11d64 --- /dev/null +++ b/miniprogram/tcb-shop/custom-tab-bar/data.js @@ -0,0 +1,22 @@ +export default [ + { + icon: 'home', + text: '首页', + url: 'pages/home/home', + }, + { + icon: 'sort', + text: '分类', + url: 'pages/goods/category/index', + }, + { + icon: 'cart', + text: '购物车', + url: 'pages/cart/index', + }, + { + icon: 'person', + text: '个人中心', + url: 'pages/usercenter/index', + }, +]; diff --git a/miniprogram/tcb-shop/custom-tab-bar/index.js b/miniprogram/tcb-shop/custom-tab-bar/index.js new file mode 100644 index 0000000..90c58df --- /dev/null +++ b/miniprogram/tcb-shop/custom-tab-bar/index.js @@ -0,0 +1,29 @@ +import TabMenu from './data'; +Component({ + data: { + active: 0, + list: TabMenu, + }, + + methods: { + onChange(event) { + this.setData({ active: event.detail.value }); + wx.switchTab({ + url: this.data.list[event.detail.value].url.startsWith('/') + ? this.data.list[event.detail.value].url + : `/${this.data.list[event.detail.value].url}`, + }); + }, + + init() { + const page = getCurrentPages().pop(); + const route = page ? page.route.split('?')[0] : ''; + const active = this.data.list.findIndex( + (item) => + (item.url.startsWith('/') ? item.url.substr(1) : item.url) === + `${route}`, + ); + this.setData({ active }); + }, + }, +}); diff --git a/miniprogram/tcb-shop/custom-tab-bar/index.json b/miniprogram/tcb-shop/custom-tab-bar/index.json new file mode 100644 index 0000000..c8ba0bd --- /dev/null +++ b/miniprogram/tcb-shop/custom-tab-bar/index.json @@ -0,0 +1,8 @@ +{ + "component": true, + "usingComponents": { + "t-tab-bar": "tdesign-miniprogram/tab-bar/tab-bar", + "t-tab-bar-item": "tdesign-miniprogram/tab-bar-item/tab-bar-item", + "t-icon": "tdesign-miniprogram/icon/icon" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/custom-tab-bar/index.wxml b/miniprogram/tcb-shop/custom-tab-bar/index.wxml new file mode 100644 index 0000000..384fd2f --- /dev/null +++ b/miniprogram/tcb-shop/custom-tab-bar/index.wxml @@ -0,0 +1,18 @@ + + + + + {{ item.text }} + + + + diff --git a/miniprogram/tcb-shop/custom-tab-bar/index.wxss b/miniprogram/tcb-shop/custom-tab-bar/index.wxss new file mode 100644 index 0000000..3b855f5 --- /dev/null +++ b/miniprogram/tcb-shop/custom-tab-bar/index.wxss @@ -0,0 +1,9 @@ +.custom-tab-bar-wrapper { + display: flex; + flex-direction: column; + align-items: center; +} + +.custom-tab-bar-wrapper .text { + font-size: 20rpx; +} diff --git a/miniprogram/tcb-shop/jsconfig.json b/miniprogram/tcb-shop/jsconfig.json new file mode 100644 index 0000000..36aa1a4 --- /dev/null +++ b/miniprogram/tcb-shop/jsconfig.json @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + "baseUrl": "." + } +} diff --git a/miniprogram/tcb-shop/model/activities.js b/miniprogram/tcb-shop/model/activities.js new file mode 100644 index 0000000..31bc3b8 --- /dev/null +++ b/miniprogram/tcb-shop/model/activities.js @@ -0,0 +1,7 @@ +import { getActivity } from './activity'; + +export function getActivityList(baseID = 0, length = 10) { + return new Array(length).fill(0).map((_, idx) => getActivity(idx + baseID)); +} + +export const activityList = getActivityList(); diff --git a/miniprogram/tcb-shop/model/activity.js b/miniprogram/tcb-shop/model/activity.js new file mode 100644 index 0000000..34abb07 --- /dev/null +++ b/miniprogram/tcb-shop/model/activity.js @@ -0,0 +1,18 @@ +/** + * @param {string|number} key 唯一值 + */ +export function getActivity(key) { + return { + promotionId: `${key}`, + title: `满减满折回归${key}`, + description: null, + promotionCode: 'MERCHANT', + promotionSubCode: key % 2 === 0 ? 'MYJ' : 'MYG', + tag: '满减', + timeType: 1, + startTime: '1588737710000', + endTime: '1601467070000', + teasingStartTime: null, + activityLadder: [{ label: '满100元减99.9元' }], + }; +} diff --git a/miniprogram/tcb-shop/model/address.js b/miniprogram/tcb-shop/model/address.js new file mode 100644 index 0000000..c7ae8dc --- /dev/null +++ b/miniprogram/tcb-shop/model/address.js @@ -0,0 +1,31 @@ +/** 地址 */ +export function genAddress(id) { + return { + saasId: '88888888', + uid: `8888888820550${id}`, + authToken: null, + id: `${id}`, + addressId: `${id}`, + phone: '17612345678', + name: `测试用户${id}`, + countryName: '中国', + countryCode: 'chn', + provinceName: '甘肃省', + provinceCode: '620000', + cityName: '甘南藏族自治州', + cityCode: '623000', + districtName: '碌曲县', + districtCode: '623026', + detailAddress: `松日鼎盛大厦${id}层${id}号`, + isDefault: `${id}` === '0' ? 1 : 0, + addressTag: id === 0 ? '' : '公司', + latitude: '34.59103', + longitude: '102.48699', + storeId: null, + }; +} + +/** 地址列表 */ +export function genAddressList(len = 10) { + return new Array(len).fill(0).map((_, idx) => genAddress(idx)); +} diff --git a/miniprogram/tcb-shop/model/cart.js b/miniprogram/tcb-shop/model/cart.js new file mode 100644 index 0000000..d0ad6c5 --- /dev/null +++ b/miniprogram/tcb-shop/model/cart.js @@ -0,0 +1,324 @@ +import { mockIp, mockReqId } from '../utils/mock'; + +export function genCartGroupData() { + const resp = { + data: { + isNotEmpty: true, + storeGoods: [ + { + storeId: '1000', + storeName: '云Mall深圳旗舰店', + storeStatus: 1, + totalDiscountSalePrice: '9990', + promotionGoodsList: [ + { + title: '满减满折回归', + promotionCode: 'MERCHANT', + promotionSubCode: 'MYJ', + promotionId: '159174555838121985', + tagText: ['满100元减99.9元'], + promotionStatus: 3, + tag: '满减', + description: '满100元减99.9元,已减99.9元', + doorSillRemain: null, + isNeedAddOnShop: 0, + goodsPromotionList: [ + { + uid: '88888888205468', + saasId: '88888888', + storeId: '1000', + spuId: '12', + skuId: '135691622', + isSelected: 1, + thumb: + 'https://cdn-we-retail.ym.tencent.com/tsr/goods/dz-3a.png', + title: + '腾讯极光盒子4智能网络电视机顶盒6K千兆网络机顶盒4K高分辨率', + primaryImage: + 'https://cdn-we-retail.ym.tencent.com/tsr/goods/dz-3a.png', + quantity: 1, + stockStatus: true, + stockQuantity: 3, + price: '9900', + originPrice: '16900', + tagPrice: null, + titlePrefixTags: [{ text: '新品' }, { text: '火爆' }], + roomId: null, + specInfo: [ + { + specTitle: '颜色', + specValue: '经典白', + }, + { + specTitle: '类型', + specValue: '经典套装', + }, + ], + joinCartTime: '2020-06-29T07:55:40.000+0000', + available: 1, + putOnSale: 1, + etitle: null, + }, + { + uid: '88888888205468', + saasId: '88888888', + storeId: '1000', + spuId: '18', + skuId: '135681631', + isSelected: 1, + thumb: + 'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-09a.png', + title: + '白色短袖连衣裙荷叶边裙摆宽松韩版休闲纯白清爽优雅连衣裙', + primaryImage: + 'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-09a.png', + quantity: 1, + stockStatus: true, + stockQuantity: 177, + price: '29800', + originPrice: '40000', + tagPrice: null, + titlePrefixTags: null, + roomId: null, + specInfo: [ + { + specTitle: '颜色', + specValue: '米色荷叶边', + }, + { + specTitle: '尺码', + specValue: 'M', + }, + ], + joinCartTime: '2020-06-29T07:55:27.000+0000', + available: 1, + putOnSale: 1, + etitle: null, + }, + { + uid: '88888888205468', + saasId: '88888888', + storeId: '1000', + spuId: '13', + skuId: '135698362', + isSelected: 1, + thumb: + 'https://cdn-we-retail.ym.tencent.com/tsr/goods/muy-3a.png', + title: + '带帽午休毯虎年款多功能加厚加大加绒简约多功能午休毯连帽披肩', + primaryImage: + 'https://cdn-we-retail.ym.tencent.com/tsr/goods/muy-3a.png', + quantity: 13, + stockStatus: true, + stockQuantity: 9, + price: '29900', + originPrice: '0', + tagPrice: null, + titlePrefixTags: [{ text: '火爆' }], + roomId: null, + specInfo: [ + { + specTitle: '颜色', + specValue: '浅灰色', + }, + { + specTitle: '尺码', + specValue: 'M', + }, + ], + joinCartTime: '2020-06-29T07:54:43.000+0000', + available: 1, + putOnSale: 1, + etitle: null, + }, + { + uid: '88888888205468', + saasId: '88888888', + storeId: '1000', + spuId: '7', + skuId: '135681625', + isSelected: 1, + thumb: + 'https://cdn-we-retail.ym.tencent.com/tsr/goods/gh-2b.png', + title: + '不锈钢刀叉勺套装家用西餐餐具ins简约耐用不锈钢金色银色可选', + primaryImage: + 'https://cdn-we-retail.ym.tencent.com/tsr/goods/gh-2b.png', + quantity: 1, + stockStatus: true, + stockQuantity: 0, + price: '29900', + originPrice: '29900', + tagPrice: null, + titlePrefixTags: null, + roomId: null, + specInfo: [ + { + specTitle: '颜色', + specValue: '奶黄色', + }, + { + specTitle: '数量', + specValue: '六件套', + }, + ], + joinCartTime: '2020-06-29T07:55:00.000+0000', + available: 1, + putOnSale: 1, + etitle: null, + }, + ], + lastJoinTime: '2020-06-29T07:55:40.000+0000', + }, + { + title: null, + promotionCode: 'EMPTY_PROMOTION', + promotionSubCode: null, + promotionId: null, + tagText: null, + promotionStatus: null, + tag: null, + description: null, + doorSillRemain: null, + isNeedAddOnShop: 0, + goodsPromotionList: [ + { + uid: '88888888205468', + saasId: '88888888', + storeId: '1000', + spuId: '11', + skuId: '135691629', + isSelected: 0, + thumb: + 'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-17a.png', + title: '运动连帽拉链卫衣休闲开衫长袖多色运动细绒面料运动上衣', + primaryImage: + 'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-17a.png', + quantity: 1, + stockStatus: false, + stockQuantity: 0, + price: '25900', + originPrice: '39900', + tagPrice: null, + tagText: null, + roomId: null, + specInfo: [ + { + specTitle: '颜色', + specValue: '军绿色', + }, + { + specTitle: '尺码', + specValue: 'S', + }, + ], + joinCartTime: '2020-04-24T06:26:48.000+0000', + available: 1, + putOnSale: 1, + etitle: null, + }, + { + uid: '88888888205468', + saasId: '88888888', + storeId: '1000', + spuId: '5', + skuId: '135691635', + isSelected: 0, + thumb: + 'https://cdn-we-retail.ym.tencent.com/tsr/goods/dz-2a.png', + title: + '迷你便携高颜值蓝牙无线耳机立体声只能触控式操作简约立体声耳机', + primaryImage: + 'https://cdn-we-retail.ym.tencent.com/tsr/goods/dz-2a.png', + quantity: 1, + stockStatus: true, + stockQuantity: 96, + price: '29000', + originPrice: '29900', + tagPrice: null, + tagText: null, + roomId: null, + specInfo: [ + { + specTitle: '颜色', + specValue: '黑色', + }, + { + specTitle: '类型', + specValue: '简约款', + }, + ], + joinCartTime: '2020-06-29T07:55:17.000+0000', + available: 1, + putOnSale: 1, + etitle: null, + }, + ], + lastJoinTime: null, + }, + ], + lastJoinTime: '2020-06-29T07:55:40.000+0000', + postageFreePromotionVo: { + title: null, + promotionCode: null, + promotionSubCode: null, + promotionId: null, + tagText: null, + promotionStatus: null, + tag: null, + description: null, + doorSillRemain: null, + isNeedAddOnShop: 0, + }, + }, + ], + invalidGoodItems: [ + { + uid: '88888888205468', + saasId: '88888888', + storeId: '1000', + spuId: '1', + skuId: '135691631', + isSelected: 1, + thumb: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-08b.png', + title: '纯色纯棉休闲圆领短袖T恤纯白亲肤厚柔软细腻面料纯白短袖套头T恤', + primaryImage: + 'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-08b.png', + quantity: 8, + stockStatus: true, + stockQuantity: 177, + price: '26900', + originPrice: '31900', + tagPrice: null, + tagText: null, + roomId: null, + specInfo: [ + { + specTitle: '颜色', + specValue: '白色', + }, + { + specTitle: '尺码', + specValue: 'S', + }, + ], + joinCartTime: '2020-04-28T04:03:59.000+0000', + available: 1, + putOnSale: 1, + etitle: null, + }, + ], + isAllSelected: false, + selectedGoodsCount: 16, + totalAmount: '179997', + totalDiscountAmount: '110000', + }, + code: 'Success', + msg: null, + requestId: mockReqId(), + clientIp: mockIp(), + rt: 269, + success: true, + }; + return resp; +} diff --git a/miniprogram/tcb-shop/model/category.js b/miniprogram/tcb-shop/model/category.js new file mode 100644 index 0000000..81b7502 --- /dev/null +++ b/miniprogram/tcb-shop/model/category.js @@ -0,0 +1,206 @@ +export function getCategoryList() { + return [ + { + groupId: '24948', + name: '女装', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/miniapp/category/category-default.png', + children: [ + { + groupId: '249481', + name: '女装', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/miniapp/category/category-default.png', + children: [ + { + groupId: '249480', + name: '卫衣', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-1.png', + }, + { + groupId: '249480', + name: '毛呢外套', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-2.png', + }, + { + groupId: '249480', + name: '雪纺衫', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-3.png', + }, + { + groupId: '249480', + name: '羽绒服', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-4.png', + }, + { + groupId: '249480', + name: '毛衣', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-5.png', + }, + { + groupId: '249480', + name: '棉衣', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-6.png', + }, + { + groupId: '249480', + name: '西装', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-7.png', + }, + { + groupId: '249480', + name: '马甲', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-8.png', + }, + { + groupId: '249480', + name: '连衣裙', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-9.png', + }, + { + groupId: '249480', + name: '半身裙', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-10.png', + }, + { + groupId: '249480', + name: '裤子', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-11.png', + }, + ], + }, + ], + }, + { + groupId: '24948', + name: '男装', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/miniapp/category/category-default.png', + children: [ + { + groupId: '249481', + name: '男装', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/miniapp/category/category-default.png', + children: [ + { + groupId: '249480', + name: '卫衣', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-1.png', + }, + { + groupId: '249480', + name: '裤子', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-11.png', + }, + { + groupId: '249480', + name: '西装', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-7.png', + }, + { + groupId: '249480', + name: '羽绒服', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-4.png', + }, + { + groupId: '249480', + name: '马甲', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-8.png', + }, + ], + }, + ], + }, + { + groupId: '24948', + name: '儿童装', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/miniapp/category/category-default.png', + children: [ + { + groupId: '249481', + name: '儿童装', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/miniapp/category/category-default.png', + children: [ + { + groupId: '249480', + name: '马甲', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-8.png', + }, + { + groupId: '249480', + name: '裤子', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-11.png', + }, + { + groupId: '249480', + name: '连衣裙', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-9.png', + }, + { + groupId: '249480', + name: '其他', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/tsr/goods/muy-3b.png', + }, + ], + }, + ], + }, + { + groupId: '24948', + name: '美妆', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/miniapp/category/category-default.png', + children: [ + { + groupId: '249481', + name: '美妆', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/miniapp/category/category-default.png', + children: [ + { + groupId: '249480', + name: '唇釉', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/tsr/goods/mz-20a1.png', + }, + { + groupId: '249480', + name: '美妆蛋', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/tsr/goods/mz-11a1.png', + }, + { + groupId: '249480', + name: '眼影', + thumbnail: + 'https://cdn-we-retail.ym.tencent.com/tsr/goods/mz-12b.png', + }, + ], + }, + ], + }, + ]; +} diff --git a/miniprogram/tcb-shop/model/comments.js b/miniprogram/tcb-shop/model/comments.js new file mode 100644 index 0000000..78dd61d --- /dev/null +++ b/miniprogram/tcb-shop/model/comments.js @@ -0,0 +1,338 @@ +/** + * * @param {number} spuId + * @param {number} pageNum + * @param {number} pageSize + * @param {number} commentsLevel + * @param {boolean} hasImage + */ +export function getGoodsAllComments(params) { + const { hasImage } = params.queryParameter; + if (hasImage) { + return { + pageNum: 1, + pageSize: 10, + totalCount: '1', + pageList: [ + { + spuId: '1722045', + skuId: '0', + specInfo: '', + commentContent: + '收到货了,第一时间试了一下,很漂亮特别喜欢,大爱大爱,颜色也很好看。棒棒!', + commentResources: [ + { + src: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-08b.png', + type: 'image', + }, + { + src: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/comment-video.mp4', + type: 'video', + coverSrc: + 'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-08b.png', + }, + { + src: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/comment-video.mp4', + type: 'video', + coverSrc: + 'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-08b.png', + }, + { + src: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/comment-video.mp4', + type: 'video', + coverSrc: + 'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-08b.png', + }, + ], + commentScore: 4, + uid: '88881048075', + userName: 'Dean', + userHeadUrl: + 'https://cdn-we-retail.ym.tencent.com/tsr/avatar/avatar1.png', + isAnonymity: false, + commentTime: '1591953561000', + isAutoComment: false, + sellerReply: + '亲,你好,我们会联系商家和厂商给您一个满意的答复请一定妥善保管好发票', + goodsDetailInfo: '颜色:纯净白 尺码:S码', + }, + { + spuId: '1722045', + skuId: '0', + specInfo: '', + commentContent: + '收到货了,第一时间试了一下,很漂亮特别喜欢,大爱大爱,颜色也很好看。棒棒!', + commentResources: [ + { + src: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-08b.png', + type: 'image', + }, + ], + commentScore: 4, + uid: '88881048075', + userName: 'Dean', + userHeadUrl: + 'https://cdn-we-retail.ym.tencent.com/tsr/avatar/avatar1.png', + isAnonymity: false, + commentTime: '1591953561000', + isAutoComment: false, + sellerReply: + '亲,你好,我们会联系商家和厂商给您一个满意的答复请一定妥善保管好发票', + goodsDetailInfo: '颜色:纯净白 尺码:S码', + }, + { + spuId: '1722045', + skuId: '0', + specInfo: '', + commentContent: + '收到货了,第一时间试了一下,很漂亮特别喜欢,大爱大爱,颜色也很好看。棒棒!', + commentResources: [ + { + src: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-08b.png', + type: 'image', + }, + { + src: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/comment-video.mp4', + type: 'video', + coverSrc: + 'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-08b.png', + }, + ], + commentScore: 4, + uid: '88881048075', + userName: 'Dean', + userHeadUrl: + 'https://cdn-we-retail.ym.tencent.com/tsr/avatar/avatar1.png', + isAnonymity: false, + commentTime: '1591953561000', + isAutoComment: false, + sellerReply: + '亲,你好,我们会联系商家和厂商给您一个满意的答复请一定妥善保管好发票', + goodsDetailInfo: '颜色:纯净白 尺码:S码', + }, + { + spuId: '1722045', + skuId: '0', + specInfo: '', + commentContent: + '收到货了,第一时间试了一下,很漂亮特别喜欢,大爱大爱,颜色也很好看。棒棒!', + commentResources: [ + { + src: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-08b.png', + type: 'image', + }, + { + src: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/comment-video.mp4', + type: 'video', + coverSrc: + 'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-08b.png', + }, + { + src: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/comment-video.mp4', + type: 'video', + coverSrc: + 'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-08b.png', + }, + ], + commentScore: 4, + uid: '88881048075', + userName: 'Dean', + userHeadUrl: + 'https://cdn-we-retail.ym.tencent.com/tsr/avatar/avatar1.png', + isAnonymity: false, + commentTime: '1591953561000', + isAutoComment: false, + sellerReply: + '亲,你好,我们会联系商家和厂商给您一个满意的答复请一定妥善保管好发票', + goodsDetailInfo: '颜色:纯净白 尺码:S码', + }, + ], + }; + } + return { + pageNum: 1, + pageSize: 10, + totalCount: '47', + pageList: [ + { + spuId: '1722045', + skuId: '1697694', + specInfo: '很不错', + commentContent: + '收到货了,第一时间试了一下,很漂亮特别喜欢,大爱大爱,颜色也很好看。棒棒!', + commentImageUrls: null, + commentScore: 1, + uid: '88881048075', + userName: 'Dean', + userHeadUrl: + 'https://cdn-we-retail.ym.tencent.com/tsr/avatar/avatar1.png', + isAnonymity: false, + commentTime: '1592224320000', + isAutoComment: false, + sellerReply: + '亲,你好,我们会联系商家和厂商给您一个满意的答复请一定妥善保管好发票', + goodsDetailInfo: '颜色:纯净白 尺码:S码', + }, + { + spuId: '1722045', + skuId: '1697693', + specInfo: '很适合', + commentContent: + '收到货了,第一时间试了一下,很漂亮特别喜欢,大爱大爱,颜色也很好看。棒棒!', + commentImageUrls: null, + commentScore: 1, + uid: '88881048075', + userName: 'Dean', + userHeadUrl: + 'https://cdn-we-retail.ym.tencent.com/tsr/avatar/avatar1.png', + isAnonymity: false, + commentTime: '1592224320000', + isAutoComment: false, + sellerReply: + '亲,你好,我们会联系商家和厂商给您一个满意的答复请一定妥善保管好发票', + goodsDetailInfo: '颜色:纯净白 尺码:S码', + }, + { + spuId: '1722045', + skuId: '1697694', + specInfo: 'NICE', + commentContent: + '收到货了,第一时间试了一下,很漂亮特别喜欢,大爱大爱,颜色也很好看。棒棒!', + commentImageUrls: null, + commentScore: 5, + uid: '88881048075', + userName: 'Dean', + userHeadUrl: + 'https://cdn-we-retail.ym.tencent.com/tsr/avatar/avatar1.png', + isAnonymity: false, + commentTime: '1592218074000', + isAutoComment: true, + sellerReply: + '亲,你好,我们会联系商家和厂商给您一个满意的答复请一定妥善保管好发票', + }, + { + spuId: '1722045', + skuId: '0', + specInfo: '', + commentContent: + '收到货了,第一时间试了一下,很漂亮特别喜欢,大爱大爱,颜色也很好看。棒棒!', + commentImageUrls: null, + commentScore: 5, + uid: '88881048075', + userName: 'Dean', + userHeadUrl: + 'https://cdn-we-retail.ym.tencent.com/tsr/avatar/avatar1.png', + isAnonymity: false, + commentTime: '1592218074000', + isAutoComment: false, + goodsDetailInfo: '颜色:纯净白 尺码:S码', + }, + { + spuId: '1722045', + skuId: '1697694', + specInfo: '测试dr超长:dr专用超长;bwtgg01:fff', + commentContent: + '收到货了,第一时间试了一下,很漂亮特别喜欢,大爱大爱,颜色也很好看。棒棒!', + commentImageUrls: null, + commentScore: 5, + uid: '88881048075', + userName: 'Dean', + userHeadUrl: + 'https://cdn-we-retail.ym.tencent.com/tsr/avatar/avatar1.png', + isAnonymity: false, + commentTime: '1592217607000', + isAutoComment: false, + }, + { + spuId: '1722045', + skuId: '1697693', + specInfo: '测试dr超长:超长测试超长测试1;bwtgg01:bbb', + commentContent: + '收到货了,第一时间试了一下,很漂亮特别喜欢,大爱大爱,颜色也很好看。棒棒!', + commentImageUrls: null, + commentScore: 4, + uid: '88881048075', + userName: 'Dean', + userHeadUrl: + 'https://cdn-we-retail.ym.tencent.com/tsr/avatar/avatar1.png', + isAnonymity: false, + commentTime: '1592217607000', + isAutoComment: false, + }, + { + spuId: '1722045', + skuId: '1697694', + specInfo: '测试dr超长:dr专用超长;bwtgg01:fff', + commentContent: + '收到货了,第一时间试了一下,很漂亮特别喜欢,大爱大爱,颜色也很好看。棒棒!', + commentImageUrls: null, + commentScore: 5, + uid: '88881048075', + userName: 'Dean', + userHeadUrl: + 'https://cdn-we-retail.ym.tencent.com/tsr/avatar/avatar1.png', + isAnonymity: false, + commentTime: '1592205599000', + isAutoComment: false, + }, + { + spuId: '1722045', + skuId: '1697694', + specInfo: '测试dr超长:dr专用超长;bwtgg01:fff', + commentContent: + '收到货了,第一时间试了一下,很漂亮特别喜欢,大爱大爱,颜色也很好看。棒棒!', + commentImageUrls: null, + commentScore: 5, + uid: '88881048075', + userName: 'Dean', + userHeadUrl: + 'https://cdn-we-retail.ym.tencent.com/tsr/avatar/avatar1.png', + isAnonymity: false, + commentTime: '1592188822000', + isAutoComment: false, + }, + { + spuId: '1722045', + skuId: '1697694', + specInfo: '测试dr超长:dr专用超长;bwtgg01:fff', + commentContent: + '收到货了,第一时间试了一下,很漂亮特别喜欢,大爱大爱,颜色也很好看。棒棒!', + commentImageUrls: null, + commentScore: 5, + uid: '88881055835', + userName: 'Max', + userHeadUrl: + 'https://cdn-we-retail.ym.tencent.com/tsr/avatar/avatar1.png', + isAnonymity: false, + commentTime: '1593792002000', + isAutoComment: true, + }, + { + spuId: '1722045', + skuId: '1697694', + specInfo: '测试dr超长:dr专用超长;bwtgg01:fff', + commentContent: '', + commentImageUrls: null, + commentScore: 5, + uid: '88881055835', + userName: 'Max', + userHeadUrl: + 'https://cdn-we-retail.ym.tencent.com/tsr/avatar/avatar1.png', + isAnonymity: false, + commentTime: '1593792001000', + isAutoComment: true, + }, + ], + }; +} + +export function getGoodsCommentsCount() { + return { + commentCount: '47', + badCount: '0', + middleCount: '2', + goodCount: '45', + hasImageCount: '1', + goodRate: 95.7, + uidCount: '0', + }; +} diff --git a/miniprogram/tcb-shop/model/comments/queryDetail.js b/miniprogram/tcb-shop/model/comments/queryDetail.js new file mode 100644 index 0000000..3d78780 --- /dev/null +++ b/miniprogram/tcb-shop/model/comments/queryDetail.js @@ -0,0 +1,14 @@ +const queryDetail = { + commentInfos: [], + logisticsScore: null, + serviceScore: null, +}; + +/** + * @param {string} skuId + * @param {string} spuId + * @param {string} orderNo + */ +export function queryCommentDetail() { + return queryDetail; +} diff --git a/miniprogram/tcb-shop/model/coupon.js b/miniprogram/tcb-shop/model/coupon.js new file mode 100644 index 0000000..a41ff8b --- /dev/null +++ b/miniprogram/tcb-shop/model/coupon.js @@ -0,0 +1,39 @@ +/** + * 优惠券 + * + * @typedef {'default'|'useless'|'disabled'} CouponCardStatus + * @typedef {'discount'|'price'} CouponCardType + * + * @param {number} [id] + * @param {CouponCardStatus} [status] + * @param {CouponCardType} [type] + */ +export function getCoupon(id = 0, status = 'default', type = (id % 2) + 1) { + return { + /** key */ + key: `${id}`, + /** 优惠券状态 */ + status, + /** 优惠券类型 */ + type, + /** 折扣或者满减值 */ + value: type === 2 ? 5.5 : 1800, + /** 标签 */ + tag: '', + /** 描述 */ + desc: parseInt(id) > 0 ? `满${parseInt(id) * 100}元可用` : '无门槛使用', + /** 订单底价,满n元 */ + base: 10000 * (parseInt(id) || 0), + /** 标题 */ + title: type === 2 ? `生鲜折扣券 - ${id}` : `生鲜满减券 - ${id}`, + /** 有效时间限制 */ + timeLimit: '2019.11.18-2023.12.18', + /** 货币符号 */ + currency: '¥', + }; +} + +/** 优惠券列表 */ +export function getCouponList(status = 'default', length = 10) { + return new Array(length).fill(0).map((_, idx) => getCoupon(idx, status)); +} diff --git a/miniprogram/tcb-shop/model/detailsComments.js b/miniprogram/tcb-shop/model/detailsComments.js new file mode 100644 index 0000000..82fc887 --- /dev/null +++ b/miniprogram/tcb-shop/model/detailsComments.js @@ -0,0 +1,30 @@ +export function getGoodsDetailsComments() { + return { + homePageComments: [ + { + spuId: '1722045', + skuId: null, + specInfo: null, + commentContent: + '收到货了,第一时间试了一下,很漂亮特别喜欢,大爱大爱,颜色也很好看。棒棒!', + commentScore: 4, + uid: '88881048075', + userName: 'Dean', + userHeadUrl: + 'https://wx.qlogo.cn/mmopen/vi_32/5mKrvn3ibyDNaDZSZics3aoKlz1cv0icqn4EruVm6gKjsK0xvZZhC2hkUkRWGxlIzOEc4600JkzKn9icOLE6zjgsxw/132', + }, + ], + }; +} + +export function getGoodsDetailsCommentsCount() { + return { + commentCount: '47', + badCount: '0', + middleCount: '2', + goodCount: '45', + hasImageCount: '1', + goodRate: 95.7, + uidCount: '0', + }; +} diff --git a/miniprogram/tcb-shop/model/good.js b/miniprogram/tcb-shop/model/good.js new file mode 100644 index 0000000..84b5ecd --- /dev/null +++ b/miniprogram/tcb-shop/model/good.js @@ -0,0 +1,25 @@ +import { cdnBase } from '../config/index'; +const imgPrefix = cdnBase; + +const defaultDesc = [`${imgPrefix}/goods/details-1.png`]; + +const allGoods = []; + +/** + * @param {string} id + * @param {number} [available] 库存, 默认1 + */ +export function genGood(id, available = 1) { + const specID = ['135681624', '135681628']; + if (specID.indexOf(id) > -1) { + return allGoods.filter((good) => good.spuId === id)[0]; + } + const item = allGoods[id % allGoods.length]; + return { + ...item, + spuId: `${id}`, + available: available, + desc: item?.desc || defaultDesc, + images: item?.images || [item?.primaryImage], + }; +} diff --git a/miniprogram/tcb-shop/model/goods.js b/miniprogram/tcb-shop/model/goods.js new file mode 100644 index 0000000..850e0ba --- /dev/null +++ b/miniprogram/tcb-shop/model/goods.js @@ -0,0 +1,7 @@ +import { genGood } from './good'; + +export function getGoodsList(baseID = 0, length = 10) { + return new Array(length).fill(0).map((_, idx) => genGood(idx + baseID)); +} + +export const goodsList = getGoodsList(); diff --git a/miniprogram/tcb-shop/model/order/applyService.js b/miniprogram/tcb-shop/model/order/applyService.js new file mode 100644 index 0000000..e3a2fdf --- /dev/null +++ b/miniprogram/tcb-shop/model/order/applyService.js @@ -0,0 +1,295 @@ +import { mockIp, mockReqId } from '../../utils/mock'; + +const orderResps = [ + { + data: { + saasId: '88888888', + uid: '88888888205468', + storeId: '1000', + skuId: '135691625', + numOfSku: 1, + numOfSkuAvailable: 1, + refundableAmount: '26900', + refundableDiscountAmount: '0', + shippingFeeIncluded: '0', + paidAmountEach: '26900', + boughtQuantity: 1, + orderNo: '132222623132329291', + goodsInfo: { + goodsName: + '迷你便携高颜值蓝牙无线耳机立体声只能触控式操作简约立体声耳机', + skuImage: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/dz-2a.png', + specInfo: [ + { + specId: '50456', + specTitle: '颜色', + specValue: '黑色', + }, + { + specId: '50459', + specTitle: '尺码', + specValue: '简约款', + }, + ], + }, + }, + code: 'Success', + msg: null, + requestId: mockReqId(), + clientIp: mockIp(), + rt: 36, + success: true, + }, + { + data: { + saasId: '88888888', + uid: '88888888205468', + storeId: '1000', + skuId: '135676631', + numOfSku: 1, + numOfSkuAvailable: 1, + refundableAmount: '26900', + refundableDiscountAmount: '0', + shippingFeeIncluded: '0', + paidAmountEach: '26900', + boughtQuantity: 1, + orderNo: '132222623132329291', + goodsInfo: { + goodsName: '白色短袖连衣裙荷叶边裙摆宽松韩版休闲纯白清爽优雅连衣裙', + skuImage: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-09a.png', + specInfo: [ + { + specId: '50456', + specTitle: '颜色', + specValue: '米色荷叶边', + }, + { + specId: '50459', + specTitle: '尺码', + specValue: 'S', + }, + ], + }, + }, + code: 'Success', + msg: null, + requestId: mockReqId(), + clientIp: mockIp(), + rt: 36, + success: true, + }, + { + data: { + saasId: '88888888', + uid: '88888888205468', + storeId: '1000', + skuId: '135691622', + numOfSku: 1, + numOfSkuAvailable: 1, + refundableAmount: '26900', + refundableDiscountAmount: '0', + shippingFeeIncluded: '0', + paidAmountEach: '26900', + boughtQuantity: 1, + orderNo: '132222623132329291', + goodsInfo: { + goodsName: '腾讯极光盒子4智能网络电视机顶盒6K千兆网络机顶盒4K高分辨率', + skuImage: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/dz-3a.png', + specInfo: [ + { + specId: '50456', + specTitle: '颜色', + specValue: '经典白', + }, + { + specId: '50459', + specTitle: '类型', + specValue: '经典套装', + }, + ], + }, + }, + code: 'Success', + msg: null, + requestId: mockReqId(), + clientIp: mockIp(), + rt: 36, + success: true, + }, + { + data: { + saasId: '88888888', + uid: '88888888205468', + storeId: '1000', + skuId: '135676629', + numOfSku: 1, + numOfSkuAvailable: 1, + refundableAmount: '26900', + refundableDiscountAmount: '0', + shippingFeeIncluded: '0', + paidAmountEach: '26900', + boughtQuantity: 1, + orderNo: '132222623132329291', + goodsInfo: { + goodsName: '带帽午休毯虎年款多功能加厚加大加绒简约多功能午休毯连帽披肩', + skuImage: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/muy-3a.png', + specInfo: [ + { + specId: '50456', + specTitle: '颜色', + specValue: '浅灰色', + }, + { + specId: '50459', + specTitle: '尺码', + specValue: 'S', + }, + ], + }, + }, + code: 'Success', + msg: null, + requestId: mockReqId(), + clientIp: mockIp(), + rt: 36, + success: true, + }, + { + data: { + saasId: '88888888', + uid: '88888888205468', + storeId: '1000', + skuId: '135686631', + numOfSku: 1, + numOfSkuAvailable: 1, + refundableAmount: '26900', + refundableDiscountAmount: '0', + shippingFeeIncluded: '0', + paidAmountEach: '26900', + boughtQuantity: 1, + orderNo: '132222623132329291', + goodsInfo: { + goodsName: '运动连帽拉链卫衣休闲开衫长袖多色运动细绒面料运动上衣', + skuImage: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-17a.png', + specInfo: [ + { + specId: '50456', + specTitle: '颜色', + specValue: '军绿色', + }, + { + specId: '50459', + specTitle: '尺码', + specValue: 'XS', + }, + ], + }, + }, + code: 'Success', + msg: null, + requestId: mockReqId(), + clientIp: mockIp(), + rt: 36, + success: true, + }, + { + data: { + saasId: '88888888', + uid: '88888888205468', + storeId: '1000', + skuId: '19384938948343', + numOfSku: 1, + numOfSkuAvailable: 1, + refundableAmount: '26900', + refundableDiscountAmount: '0', + shippingFeeIncluded: '0', + paidAmountEach: '26900', + boughtQuantity: 1, + orderNo: '130169571554503755', + goodsInfo: { + goodsName: + '纯色纯棉休闲圆领短袖T恤纯白亲肤厚柔软细腻面料纯白短袖套头T恤', + skuImage: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-08b.png', + specInfo: [ + { + specId: '50456', + specTitle: '颜色', + specValue: '军绿色', + }, + { + specId: '50459', + specTitle: '尺码', + specValue: 'XS', + }, + ], + }, + }, + code: 'Success', + msg: null, + requestId: mockReqId(), + clientIp: mockIp(), + rt: 36, + success: true, + }, +]; + +export function genRightsPreview(params) { + const { orderNo, skuId } = params; + const resp = orderResps.find( + (r) => r.data.orderNo === orderNo && r.data.skuId === skuId, + ); + return resp; +} + +export function genApplyReasonList(params) { + const resp = { + data: { + saasId: '70000001', + rightsReasonList: [ + { id: '1', desc: '实际商品与描述不符' }, + { id: '2', desc: '质量问题' }, + { id: '3', desc: '少件/漏发' }, + { id: '4', desc: '包装/商品/污迹/裂痕/变形' }, + { id: '5', desc: '发货太慢' }, + { id: '6', desc: '物流配送太慢' }, + { id: '7', desc: '商家发错货' }, + { id: '8', desc: '不喜欢' }, + ], + }, + code: 'Success', + msg: null, + requestId: mockReqId(), + clientIp: mockIp(), + rt: 6, + success: true, + }; + // 未收货对应的原因列表 + if (params.rightsReasonType === 'REFUND_MONEY') { + resp.data.rightsReasonList = [ + { id: '9', desc: '空包裹' }, + { id: '10', desc: '快递/物流一直未送到' }, + { id: '11', desc: '货物破损已拒签' }, + { id: '12', desc: '不喜欢' }, + ]; + } + return resp; +} + +export function applyService() { + const resp = { + data: { + rightsNo: '123123423', + saasId: '70000001', + uid: '700000011070005', + storeId: '542', + result: null, + }, + code: 'Success', + msg: null, + requestId: mockReqId(), + clientIp: mockIp(), + rt: 269, + success: true, + }; + return resp; +} diff --git a/miniprogram/tcb-shop/model/order/orderConfirm.js b/miniprogram/tcb-shop/model/order/orderConfirm.js new file mode 100644 index 0000000..7170492 --- /dev/null +++ b/miniprogram/tcb-shop/model/order/orderConfirm.js @@ -0,0 +1,147 @@ +import { mockIp, mockReqId } from '../../utils/mock'; + +export const transformGoodsDataToConfirmData = (goodsDataList) => { + const list = []; + + goodsDataList.forEach((goodsData) => { + list.push({ + storeId: goodsData.storeId, + spuId: goodsData.spuId, + skuId: goodsData.skuId, + goodsName: goodsData.title, + image: goodsData.primaryImage, + reminderStock: 119, + quantity: goodsData.quantity, + payPrice: goodsData.price, + totalSkuPrice: goodsData.price, + discountSettlePrice: goodsData.price, + realSettlePrice: goodsData.price, + settlePrice: goodsData.price, + oriPrice: goodsData.originPrice, + tagPrice: null, + tagText: null, + skuSpecLst: goodsData.specInfo, + promotionIds: null, + weight: 0.0, + unit: 'KG', + volume: null, + masterGoodsType: 0, + viceGoodsType: 0, + roomId: goodsData.roomId, + egoodsName: null, + }); + }); + + return list; +}; + +/** 生成结算数据 */ +export function genSettleDetail(params) { + const { userAddressReq, couponList, goodsRequestList } = params; + + const resp = { + data: { + settleType: 0, + userAddress: null, + totalGoodsCount: 3, + packageCount: 1, + totalAmount: '289997', + totalPayAmount: '', + totalDiscountAmount: '110000', + totalPromotionAmount: '1100', + totalCouponAmount: '0', + totalSalePrice: '289997', + totalGoodsAmount: '289997', + totalDeliveryFee: '0', + invoiceRequest: null, + skuImages: null, + deliveryFeeList: null, + storeGoodsList: [ + { + storeId: '1000', + storeName: '云Mall深圳旗舰店', + remark: null, + goodsCount: 1, + deliveryFee: '0', + deliveryWords: null, + storeTotalAmount: '0', + storeTotalPayAmount: '179997', + storeTotalDiscountAmount: '110000', + storeTotalCouponAmount: '0', + skuDetailVos: [], + couponList: [ + { + couponId: 11, + storeId: '1000', + }, + ], + }, + ], + inValidGoodsList: null, + outOfStockGoodsList: null, + limitGoodsList: null, + abnormalDeliveryGoodsList: null, + invoiceSupport: 1, + }, + code: 'Success', + msg: null, + requestId: mockReqId(), + clientIp: mockIp(), + rt: 244, + success: true, + }; + + const list = transformGoodsDataToConfirmData(goodsRequestList); + + // 获取购物车传递的商品数据 + resp.data.storeGoodsList[0].skuDetailVos = list; + + // 判断是否携带优惠券数据 + const discountPrice = []; + + if (couponList && couponList.length > 0) { + couponList.forEach((coupon) => { + if (coupon.status === 'default') { + discountPrice.push({ + type: coupon.type, + value: coupon.value, + }); + } + }); + } + + // 模拟计算场景 + + // 计算总价 + const totalPrice = list.reduce((pre, cur) => { + return pre + cur.quantity * Number(cur.settlePrice); + }, 0); + + // 计算折扣 + const totalDiscountPrice = + discountPrice.length > 0 + ? discountPrice.reduce((pre, cur) => { + if (cur.type === 1) { + return pre + cur.value; + } + if (cur.type === 2) { + return pre + (Number(totalPrice) * cur.value) / 10; + } + + return pre + cur; + }, 0) + : 0; + + resp.data.totalSalePrice = totalPrice; + + resp.data.totalCouponAmount = totalDiscountPrice; + + resp.data.totalPayAmount = + totalPrice - totalDiscountPrice - Number(resp.data.totalPromotionAmount); + + if (userAddressReq) { + resp.data.settleType = 1; + resp.data.userAddress = userAddressReq; + } + return resp; +} diff --git a/miniprogram/tcb-shop/model/order/orderDetail.js b/miniprogram/tcb-shop/model/order/orderDetail.js new file mode 100644 index 0000000..0dcd233 --- /dev/null +++ b/miniprogram/tcb-shop/model/order/orderDetail.js @@ -0,0 +1,26 @@ +import { mockIp, mockReqId } from '../../utils/mock'; + +const orderResps = []; + +export function genOrderDetail(params) { + const { parameter } = params; + const resp = orderResps.find((r) => r.data.orderNo === parameter); + return resp; +} + +export function genBusinessTime() { + const resp = { + data: { + businessTime: ['周一,周二,周三,周四,周五:00:20:00-08:00:00'], + telphone: '18565372257', + saasId: '88888888', + }, + code: 'Success', + msg: null, + requestId: mockReqId(), + clientIp: mockIp(), + rt: 3, + success: true, + }; + return resp; +} diff --git a/miniprogram/tcb-shop/model/order/orderList.js b/miniprogram/tcb-shop/model/order/orderList.js new file mode 100644 index 0000000..c042dec --- /dev/null +++ b/miniprogram/tcb-shop/model/order/orderList.js @@ -0,0 +1,46 @@ +import { mockIp, mockReqId } from '../../utils/mock'; + +export function genOrders(params) { + const resp = { + data: { + pageNum: 1, + pageSize: 10, + totalCount: 7, + orders: [], + }, + code: 'Success', + msg: null, + requestId: mockReqId(), + clientIp: mockIp(), + rt: 113, + success: true, + }; + const { pageNum, pageSize, orderStatus } = params.parameter; + // 实现筛选 + if (orderStatus > -1) { + resp.data.orders = resp.data.orders.filter((order) => order.orderStatus === orderStatus); + } + // 实现分页 + resp.data.pageNum = pageNum; + resp.data.pageSize = pageSize; + resp.data.orders = resp.data.orders.slice((pageNum - 1) * pageSize, pageNum * pageSize); + return resp; +} + +export function genOrdersCount() { + const resp = { + data: [ + { tabType: 5, orderNum: 1 }, + { tabType: 10, orderNum: 1 }, + { tabType: 40, orderNum: 1 }, + { tabType: 50, orderNum: 2 }, + ], + code: 'Success', + msg: null, + requestId: mockReqId(), + clientIp: mockIp(), + rt: 41, + success: true, + }; + return resp; +} diff --git a/miniprogram/tcb-shop/model/promotion.js b/miniprogram/tcb-shop/model/promotion.js new file mode 100644 index 0000000..0aaee61 --- /dev/null +++ b/miniprogram/tcb-shop/model/promotion.js @@ -0,0 +1,21 @@ +import { getGoodsList } from './goods'; + +export function getPromotion(baseID = 0, length = 10) { + return { + list: getGoodsList(baseID, length).map((item) => { + return { + spuId: item.spuId, + thumb: item.primaryImage, + title: item.title, + price: item.minSalePrice, + originPrice: item.maxLinePrice, + tags: item.spuTagList.map((tag) => ({ title: tag.title })), + }; + }), + banner: + 'https://cdn-we-retail.ym.tencent.com/tsr/promotion/banner-promotion.png', + time: 1000 * 60 * 60 * 20, + showBannerDesc: true, + statusTag: 'running', + }; +} diff --git a/miniprogram/tcb-shop/model/search.js b/miniprogram/tcb-shop/model/search.js new file mode 100644 index 0000000..2d2e68d --- /dev/null +++ b/miniprogram/tcb-shop/model/search.js @@ -0,0 +1,60 @@ +import { getGoodsList } from './goods'; + +/** + * @param {number} sort + * @param {number} pageNum + * @param {number} pageSize + * @param {number} minPrice + * @param {number} maxPrice + * @param {string} keyword + */ + +export function getSearchHistory() { + return { + historyWords: [ + '鸡', + '电脑', + 'iPhone12', + '车载手机支架', + '自然堂', + '小米10', + '原浆古井贡酒', + '欧米伽', + '华为', + '针织半身裙', + '氢跑鞋', + '三盒处理器', + ], + }; +} + +export function getSearchPopular() { + return { + popularWords: [ + '鸡', + '电脑', + 'iPhone12', + '车载手机支架', + '自然堂', + '小米10', + '原浆古井贡酒', + '欧米伽', + '华为', + '针织半身裙', + '氢跑鞋', + '三盒处理器', + ], + }; +} + +export function getSearchResult() { + return { + saasId: null, + storeId: null, + pageNum: 1, + pageSize: 30, + totalCount: 1, + spuList: getGoodsList(7), + algId: 0, + }; +} diff --git a/miniprogram/tcb-shop/model/submitComment.js b/miniprogram/tcb-shop/model/submitComment.js new file mode 100644 index 0000000..e620b4f --- /dev/null +++ b/miniprogram/tcb-shop/model/submitComment.js @@ -0,0 +1,58 @@ +export function getGoods() { + return { + goods: [ + { + squid: '1', + checkItems: [ + { + name: '匿名评价', + value: 'anonymous', + checked: false, + }, + ], + detail: { + image: + 'https://wx.qlogo.cn/mmopen/vi_32/51VSMNuy1CyHiaAhAjLJ00kMZVqqnCqXeZduCLXHUBr52zFHRGxwL7kGia3fHj8GSNzFcqFDInQmRGM1eWjtQgqA/132', + title: '', + }, + goodComment: { + /** 商品评价 */ + rate: 0, + /** 评价内容 */ + label: '123', + /** 上传图片 */ + images: [], + }, + }, + { + squid: '2', + checkItems: [ + { + name: '匿名评价', + value: 'anonymous', + checked: false, + }, + ], + detail: { + image: + 'https://wx.qlogo.cn/mmopen/vi_32/51VSMNuy1CyHiaAhAjLJ00kMZVqqnCqXeZduCLXHUBr52zFHRGxwL7kGia3fHj8GSNzFcqFDInQmRGM1eWjtQgqA/132', + title: '评价内容 山姆智利进口', + }, + goodComment: { + /** 商品评价 */ + rate: 0, + /** 评价内容 */ + label: '山姆智利进口', + /** 上传图片 */ + images: [], + }, + }, + ], + storeComment: { + /** 物流评价 */ + logisticsRate: 0, + /** 服务评价 */ + servicesRate: 0, + }, + }; +} diff --git a/miniprogram/tcb-shop/model/swiper.js b/miniprogram/tcb-shop/model/swiper.js new file mode 100644 index 0000000..dd5702c --- /dev/null +++ b/miniprogram/tcb-shop/model/swiper.js @@ -0,0 +1,39 @@ +// const images = [ +// { +// img: 'https://cdn-we-retail.ym.tencent.com/tsr/home/v2/banner1.png', +// text: '1', +// }, +// { +// img: 'https://cdn-we-retail.ym.tencent.com/tsr/home/v2/banner2.png', +// text: '2', +// }, +// { +// img: 'https://cdn-we-retail.ym.tencent.com/tsr/home/v2/banner3.png', +// text: '3', +// }, +// { +// img: 'https://cdn-we-retail.ym.tencent.com/tsr/home/v2/banner4.png', +// text: '4', +// }, +// { +// img: 'https://cdn-we-retail.ym.tencent.com/tsr/home/v2/banner5.png', +// text: '5', +// }, +// { +// img: 'https://cdn-we-retail.ym.tencent.com/tsr/home/v2/banner6.png', +// text: '6', +// }, +// ]; + +const images = [ + 'https://cdn-we-retail.ym.tencent.com/tsr/home/v2/banner1.png', + 'https://cdn-we-retail.ym.tencent.com/tsr/home/v2/banner2.png', + 'https://cdn-we-retail.ym.tencent.com/tsr/home/v2/banner3.png', + 'https://cdn-we-retail.ym.tencent.com/tsr/home/v2/banner4.png', + 'https://cdn-we-retail.ym.tencent.com/tsr/home/v2/banner5.png', + 'https://cdn-we-retail.ym.tencent.com/tsr/home/v2/banner6.png', +]; + +export function genSwiperImageList() { + return images; +} diff --git a/miniprogram/tcb-shop/model/usercenter.js b/miniprogram/tcb-shop/model/usercenter.js new file mode 100644 index 0000000..d143a04 --- /dev/null +++ b/miniprogram/tcb-shop/model/usercenter.js @@ -0,0 +1,52 @@ +const userInfo = { + avatarUrl: + 'https://we-retail-static-1300977798.cos.ap-guangzhou.myqcloud.com/retail-ui/components-exp/avatar/avatar-1.jpg', + nickName: '云开发', + phoneNumber: '13438358888', + gender: 2, +}; +const countsData = [ + { + num: 2, + name: '积分', + type: 'point', + }, + { + num: 10, + name: '优惠券', + type: 'coupon', + }, +]; + +const orderTagInfos = [ + { + orderNum: 1, + tabType: 5, + }, + { + orderNum: 1, + tabType: 10, + }, + { + orderNum: 1, + tabType: 40, + }, + { + orderNum: 0, + tabType: 0, + }, +]; + +const customerServiceInfo = { + servicePhone: '4006336868', + serviceTimeDuration: '每周三至周五 9:00-12:00 13:00-15:00', +}; + +export const genSimpleUserInfo = () => ({ ...userInfo }); + +export const genUsercenter = () => ({ + userInfo, + countsData, + orderTagInfos, + customerServiceInfo, +}); diff --git a/miniprogram/tcb-shop/package.json b/miniprogram/tcb-shop/package.json new file mode 100644 index 0000000..b290b6b --- /dev/null +++ b/miniprogram/tcb-shop/package.json @@ -0,0 +1,46 @@ +{ + "name": "tcb-cloudbase-shop", + "version": "1.0.0", + "description": "", + "main": "app.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "lint": "eslint --cache --fix --ext .js", + "check": "node config/eslintCheck.js", + "prepare": "husky install", + "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s" + }, + "author": "", + "license": "ISC", + "config": { + "commitizen": { + "path": "./node_modules/cz-conventional-changelog" + } + }, + "lint-staged": { + "*.{js, ts}": "eslint --cache --fix", + "*.{js,ts,wxml,html,json,css,less}": [ + "prettier --write" + ] + }, + "dependencies": { + "@cloudbase/wx-cloud-client-sdk": "^1.2.1", + "dayjs": "^1.9.3", + "tdesign-miniprogram": "^1.6.0", + "tslib": "^1.11.1" + }, + "devDependencies": { + "@commitlint/cli": "^17.4.2", + "@commitlint/config-conventional": "^17.4.2", + "commitizen": "^4.3.0", + "conventional-changelog-cli": "^2.2.2", + "cz-conventional-changelog": "^3.3.0", + "eslint": "^6.8.0", + "eslint-config-prettier": "^6.10.0", + "eslint-plugin-import": "^2.20.1", + "eslint-plugin-prettier": "^3.1.2", + "husky": "^8.0.3", + "lint-staged": "^10.0.8", + "prettier": "^2.1.2" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/cart/components/cart-bar/index.js b/miniprogram/tcb-shop/pages/cart/components/cart-bar/index.js new file mode 100644 index 0000000..fb4f99f --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/components/cart-bar/index.js @@ -0,0 +1,59 @@ +Component({ + options: { + addGlobalClass: true, + }, + /** + * 组件的属性列表 + */ + properties: { + isAllSelected: { + type: Boolean, + value: false, + }, + totalAmount: { + type: Number, + value: 1, + }, + totalGoodsNum: { + type: Number, + value: 0, + observer(num) { + const isDisabled = num == 0; + setTimeout(() => { + this.setData({ + isDisabled, + }); + }); + }, + }, + totalDiscountAmount: { + type: Number, + value: 0, + }, + bottomHeight: { + type: Number, + value: 100, + }, + fixed: Boolean, + }, + data: { + isDisabled: true, + }, + + methods: { + handleSelectAll() { + const { isAllSelected } = this.data; + this.setData({ + isAllSelected: !isAllSelected, + }); + this.triggerEvent('handleSelectAll', { + isAllSelected: isAllSelected, + }); + }, + + handleToSettle() { + if (this.data.isDisabled) return; + this.triggerEvent('handleToSettle'); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/cart/components/cart-bar/index.json b/miniprogram/tcb-shop/pages/cart/components/cart-bar/index.json new file mode 100644 index 0000000..c6c4351 --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/components/cart-bar/index.json @@ -0,0 +1,7 @@ +{ + "component": true, + "usingComponents": { + "price": "/components/price/index", + "t-icon": "tdesign-miniprogram/icon/icon" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/cart/components/cart-bar/index.wxml b/miniprogram/tcb-shop/pages/cart/components/cart-bar/index.wxml new file mode 100644 index 0000000..5205e9c --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/components/cart-bar/index.wxml @@ -0,0 +1,14 @@ + + + + 全选 + + + 总计 + + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/cart/components/cart-bar/index.wxss b/miniprogram/tcb-shop/pages/cart/components/cart-bar/index.wxss new file mode 100644 index 0000000..0e63f60 --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/components/cart-bar/index.wxss @@ -0,0 +1,80 @@ +.cart-bar__placeholder { + height: 100rpx; +} +.flex { + display: flex; +} +.flex-v-center { + align-items: center; +} +.flex1 { + flex: 1; +} +.algin-bottom { + text-align: end; +} +.cart-bar--fixed { + position: fixed; + left: 0; + right: 0; + z-index: 99; + bottom: calc(100rpx + env(safe-area-inset-bottom)); +} + +.cart-bar { + height: 112rpx; + background-color: #fff; + border-top: 1rpx solid #e5e5e5; + padding: 16rpx 32rpx; + box-sizing: border-box; + font-size: 24rpx; + line-height: 36rpx; + color: #333; +} + +.cart-bar .cart-bar__check { + margin-right: 12rpx; +} + +.cart-bar .cart-bar__total { + margin-left: 24rpx; +} + +.cart-bar .account-btn { + width: 192rpx; + height: 80rpx; + border-radius: 40rpx; + background-color: #fa4126; + font-size: 28rpx; + font-weight: bold; + line-height: 80rpx; + color: #ffffff; + text-align: center; +} +.cart-bar .disabled-btn { + background-color: #cccccc !important; +} +.cart-bar .hover-btn { + opacity: 0.5; +} + +.cart-bar__total .cart-bar__total--bold { + font-size: 28rpx; + line-height: 40rpx; + color: #333; + font-weight: bold; +} +.cart-bar__total .cart-bar__total--normal { + font-size: 24rpx; + line-height: 32rpx; + color: #999; +} + +.cart-bar__total .cart-bar__total--price { + color: #fa4126; + font-weight: bold; +} + +.text-padding-right { + padding-right: 4rpx; +} diff --git a/miniprogram/tcb-shop/pages/cart/components/cart-empty/index.js b/miniprogram/tcb-shop/pages/cart/components/cart-empty/index.js new file mode 100644 index 0000000..01cf9c3 --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/components/cart-empty/index.js @@ -0,0 +1,23 @@ +Component({ + properties: { + imgUrl: { + type: String, + value: + 'https://cdn-we-retail.ym.tencent.com/miniapp/template/empty-cart.png', + }, + tip: { + type: String, + value: '购物车是空的', + }, + btnText: { + type: String, + value: '去首页', + }, + }, + data: {}, + methods: { + handleClick() { + this.triggerEvent('handleClick'); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/cart/components/cart-empty/index.json b/miniprogram/tcb-shop/pages/cart/components/cart-empty/index.json new file mode 100644 index 0000000..b659310 --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/components/cart-empty/index.json @@ -0,0 +1,6 @@ +{ + "component": true, + "usingComponents": { + "t-image": "/components/webp-image/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/cart/components/cart-empty/index.wxml b/miniprogram/tcb-shop/pages/cart/components/cart-empty/index.wxml new file mode 100644 index 0000000..d0cdd43 --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/components/cart-empty/index.wxml @@ -0,0 +1,6 @@ + + + {{tip}} + {{btnText}} + + diff --git a/miniprogram/tcb-shop/pages/cart/components/cart-empty/index.wxss b/miniprogram/tcb-shop/pages/cart/components/cart-empty/index.wxss new file mode 100644 index 0000000..d074bc3 --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/components/cart-empty/index.wxss @@ -0,0 +1,33 @@ +.cart-empty { + padding: 64rpx 0rpx; + display: flex; + flex-direction: column; + align-items: center; + box-sizing: border-box; + height: calc(100vh - 100rpx); + background-color: #f5f5f5; +} +.cart-empty .cart-img { + width: 160rpx; + height: 160rpx; + margin-bottom: 24rpx; +} + +.cart-empty .tip { + font-size: 28rpx; + line-height: 40rpx; + color: #999; + margin-bottom: 24rpx; +} +.cart-empty .btn { + width: 240rpx; + height: 72rpx; + border-radius: 36rpx; + text-align: center; + line-height: 72rpx; + border: 2rpx solid #fa4126; + color: #fa4126; + background-color: transparent; + font-size: 28rpx; + font-weight: bold; +} diff --git a/miniprogram/tcb-shop/pages/cart/components/cart-group/index.js b/miniprogram/tcb-shop/pages/cart/components/cart-group/index.js new file mode 100644 index 0000000..5389941 --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/components/cart-group/index.js @@ -0,0 +1,168 @@ +import Toast from 'tdesign-miniprogram/toast/index'; + +const shortageImg = 'https://cdn-we-retail.ym.tencent.com/miniapp/cart/shortage.png'; + +Component({ + isSpecsTap: false, // 标记本次点击事件是否因为点击specs触发(由于底层goods-card组件没有catch specs点击事件,只能在此处加状态来避免点击specs时触发跳转商品详情) + externalClasses: ['wr-class'], + properties: { + storeGoods: { + type: Array, + observer(storeGoods) { + for (const store of storeGoods) { + for (const activity of store.promotionGoodsList) { + for (const goods of activity.goodsPromotionList) { + goods.specs = goods.specInfo.map((item) => item.specValue); // 目前仅展示商品已选规格的值 + } + } + for (const goods of store.shortageGoodsList) { + goods.specs = goods.specInfo.map((item) => item.specValue); // 目前仅展示商品已选规格的值 + } + } + + this.setData({ _storeGoods: storeGoods }); + }, + }, + invalidGoodItems: { + type: Array, + observer(invalidGoodItems) { + invalidGoodItems.forEach((goods) => { + goods.specs = goods.specInfo.map((item) => item.specValue); // 目前仅展示商品已选规格的值 + }); + this.setData({ _invalidGoodItems: invalidGoodItems }); + }, + }, + thumbWidth: { type: null }, + thumbHeight: { type: null }, + cartItems: { type: Array }, + }, + + data: { + shortageImg, + isShowSpecs: false, + currentGoods: null, + isShowToggle: false, + _storeGoods: [], + _invalidGoodItems: [], + }, + + methods: { + toast(message) { + Toast({ + context: this, + selector: '#t-toast', + message, + }); + }, + // 删除商品 + deleteGoods(e) { + const { goods } = e.currentTarget.dataset; + this.triggerEvent('delete', { goods }); + }, + + // 清空失效商品 + clearInvalidGoods() { + this.triggerEvent('clearinvalidgoods'); + }, + + // 选中商品 + selectGoods(e) { + const { goods } = e.currentTarget.dataset; + this.triggerEvent('selectgoods', { + goods, + }); + }, + + changeQuantity(count, cartItemId) { + this.triggerEvent('changequantity', { + count, + cartItemId, + }); + }, + changeStepper({ + detail: { value: changeToValue }, + target: { + dataset: { goods }, + }, + }) { + // validation is handled by this.overlimit() + this.changeQuantity(changeToValue, goods._id); + }, + + input(e) { + const { value } = e.detail; + const { goods } = e.currentTarget.dataset; + const num = value; + this.changeQuantity(num, goods); + }, + + overlimit(e) { + const text = e.detail.type === 'minus' ? '不能再少了' : '不能再多了'; + this.toast(text); + }, + + // 去凑单/再逛逛 + gotoBuyMore(e) { + const { promotion, storeId = '' } = e.currentTarget.dataset; + this.triggerEvent('gocollect', { promotion, storeId }); + }, + + // 选中门店 + selectStore(e) { + const { storeIndex } = e.currentTarget.dataset; + const store = this.data.storeGoods[storeIndex]; + const isSelected = !store.isSelected; + if (store.storeStockShortage && isSelected) { + Toast({ + context: this, + selector: '#t-toast', + message: '部分商品库存不足', + }); + return; + } + this.triggerEvent('selectstore', { + store, + isSelected, + }); + }, + + // 展开/收起切换 + showToggle() { + this.setData({ + isShowToggle: !this.data.isShowToggle, + }); + }, + + // 展示规格popup + specsTap({ + target: { + dataset: { goods }, + }, + }) { + this.isSpecsTap = true; + this.setData({ + isShowSpecs: true, + currentGoods: goods, + }); + }, + + hideSpecsPopup() { + this.setData({ + isShowSpecs: false, + }); + }, + + goGoodsDetail(e) { + if (this.isSpecsTap) { + this.isSpecsTap = false; + return; + } + const { goods } = e.currentTarget.dataset; + this.triggerEvent('goodsclick', { goods }); + }, + + gotoCoupons() { + wx.navigateTo({ url: '/pages/coupon/coupon-list/index' }); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/cart/components/cart-group/index.json b/miniprogram/tcb-shop/pages/cart/components/cart-group/index.json new file mode 100644 index 0000000..9b8003d --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/components/cart-group/index.json @@ -0,0 +1,11 @@ +{ + "component": true, + "usingComponents": { + "t-toast": "tdesign-miniprogram/toast/toast", + "t-icon": "tdesign-miniprogram/icon/icon", + "t-stepper": "tdesign-miniprogram/stepper/stepper", + "swipeout": "/components/swipeout/index", + "goods-card": "../../components/goods-card/index", + "specs-popup": "../../components/specs-popup/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/cart/components/cart-group/index.wxml b/miniprogram/tcb-shop/pages/cart/components/cart-group/index.wxml new file mode 100644 index 0000000..9e2102a --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/components/cart-group/index.wxml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + 仅剩{{goods.sku.count}}件 + + + + + + + + + 删除 + + + + + + + + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/cart/components/cart-group/index.wxs b/miniprogram/tcb-shop/pages/cart/components/cart-group/index.wxs new file mode 100644 index 0000000..39f1e0b --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/components/cart-group/index.wxs @@ -0,0 +1,5 @@ +var hasPromotion = function (code) { + return code && code !== 'EMPTY_PROMOTION'; +}; + +module.exports.hasPromotion = hasPromotion; diff --git a/miniprogram/tcb-shop/pages/cart/components/cart-group/index.wxss b/miniprogram/tcb-shop/pages/cart/components/cart-group/index.wxss new file mode 100644 index 0000000..641101f --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/components/cart-group/index.wxss @@ -0,0 +1,335 @@ +.cart-group { + border-radius: 8rpx; +} +.cart-group .goods-wrap { + margin-top: 40rpx; + background-color: #fff; + border-radius: 8rpx; + overflow: hidden; +} +.cart-group .goods-wrap:first-of-type { + margin-top: 0; +} +.cart-group .cart-store { + height: 96rpx; + background-color: #fff; + box-sizing: border-box; + display: flex; + align-items: center; + padding: 0rpx 24rpx 0rpx 36rpx; +} +.cart-group .cart-store .cart-store__check { + padding: 28rpx 32rpx 28rpx 0rpx; +} +.cart-group .cart-store__content { + box-sizing: border-box; + flex: auto; + display: flex; + align-items: center; + justify-content: space-between; +} +.cart-group .cart-store__content .store-title { + flex: auto; + font-size: 28rpx; + line-height: 40rpx; + color: #333333; + display: flex; + align-items: center; + font-weight: bold; + overflow: hidden; +} + +.cart-group .cart-store__content .store-title .wr-store { + font-size: 32rpx; +} +.cart-group .cart-store__content .store-title .store-name { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + margin-left: 12rpx; +} +.cart-group .cart-store__content .get-coupon { + width: 112rpx; + height: 40rpx; + border-radius: 20rpx; + background-color: #ffecf9; + line-height: 40rpx; + text-align: center; + font-size: 26rpx; + color: #fa4126; +} + +.cart-group .promotion-wrap { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0rpx 24rpx 32rpx 36rpx; + background-color: #ffffff; + font-size: 24rpx; + line-height: 36rpx; + color: #222427; +} +.cart-group .promotion-wrap .promotion-title { + font-weight: bold; + flex: auto; + overflow: hidden; + margin-right: 20rpx; + display: flex; + align-items: center; +} +.cart-group .promotion-wrap .promotion-title .promotion-icon { + flex: none; + font-weight: normal; + display: inline-block; + padding: 0 8rpx; + color: #ffffff; + background: #fa4126; + font-size: 20rpx; + height: 32rpx; + line-height: 32rpx; + margin-right: 16rpx; + border-radius: 16rpx; +} +.cart-group .promotion-wrap .promotion-title .promotion-text { + flex: auto; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.cart-group .promotion-wrap .promotion-action { + flex: none; + color: #333333; +} +.cart-group .promotion-line-wrap { + background-color: #fff; + height: 2rpx; + display: flex; + justify-content: flex-end; +} +.cart-group .promotion-line-wrap .promotion-line { + width: 684rpx; + height: 2rpx; + background-color: #e6e6e6; +} +.cart-group .goods-item-info { + display: flex; + background-color: #fff; + align-items: flex-start; +} +.cart-group .goods-item-info .check-wrap { + margin-top: 56rpx; + padding: 20rpx 28rpx 20rpx 36rpx; +} + +.cart-group .goods-item-info .check-wrap .unCheck-icon { + box-sizing: border-box; + width: 36rpx; + height: 36rpx; + border-radius: 20rpx; + background: #f5f5f5; + border: 2rpx solid #bbbbbb; +} + +.cart-group .goods-item-info .goods-sku-info { + padding: 0rpx 32rpx 40rpx 0; + flex-grow: 1; +} +.cart-group .goods-item-info .goods-sku-info .stock-mask { + position: absolute; + color: #fff; + font-size: 24rpx; + bottom: 0rpx; + background-color: rgba(0, 0, 0, 0.5); + width: 100%; + height: 40rpx; + line-height: 40rpx; + text-align: center; +} +.cart-group .goods-item-info .goods-sku-info .goods-stepper { + position: absolute; + right: 0; + bottom: 8rpx; +} +.cart-group .goods-item-info .goods-sku-info .goods-stepper .stepper-tip { + position: absolute; + top: -36rpx; + right: 0; + height: 28rpx; + color: #ff2525; + font-size: 20rpx; + line-height: 28rpx; +} + +.cart-group .shortage-line { + width: 662rpx; + height: 2rpx; + background-color: #e6e6e6; + margin: 0 auto; +} +.cart-group .shortage-goods-wrap { + background-color: #fff; +} +.cart-group .shortage-goods-wrap .shortage-tip-title { + height: 72rpx; + line-height: 72rpx; + padding-left: 28rpx; + font-size: 24rpx; + color: #999; +} +.stepper-info { + margin-left: auto; +} +.invalid-goods-wrap { + background-color: #fff; + border-radius: 8rpx; + margin-top: 40rpx; +} +.invalid-goods-wrap .invalid-head { + display: flex; + justify-content: space-between; + padding: 30rpx 20rpx; + font-size: 24rpx; + border-bottom: 2rpx solid #f6f6f6; +} +.invalid-goods-wrap .invalid-head .invalid-title { + color: #333; + font-size: 28rpx; + font-weight: 600; +} +.invalid-goods-wrap .invalid-head .invalid-clear { + color: #fa4126; +} +.invalid-goods-wrap .toggle { + display: flex; + height: 80rpx; + justify-content: center; + align-items: center; + font-size: 24rpx; + color: #fa4126; +} +.invalid-goods-wrap .toggle .m-r-6 { + margin-right: 6rpx; +} +.invalid-goods-wrap .toggle .top-icon { + display: inline-block; + width: 0; + height: 0; + border-left: 10rpx solid transparent; + border-right: 10rpx solid transparent; + border-bottom: 10rpx solid #fa4126; +} +.invalid-goods-wrap .toggle .down-icon { + display: inline-block; + width: 0; + height: 0; + border-left: 10rpx solid transparent; + border-right: 10rpx solid transparent; + border-top: 10rpx solid #fa4126; +} +.action-btn { + display: flex; + align-items: center; +} +.action-btn .action-btn-arrow { + font-size: 20rpx; + margin-left: 8rpx; +} +.action-btn--active { + opacity: 0.5; +} + +.swiper-right-del { + height: calc(100% - 40rpx); + width: 144rpx; + background-color: #fa4126; + font-size: 28rpx; + color: white; + display: flex; + justify-content: center; + align-items: center; +} +.goods-stepper .stepper { + border: none; + border-radius: 0; + height: auto; + width: 168rpx; + overflow: visible; +} +.goods-stepper .stepper .stepper__minus, +.goods-stepper .stepper .stepper__plus { + width: 44rpx; + height: 44rpx; + background-color: #f5f5f5; +} +.goods-stepper .stepper .stepper__minus--hover, +.goods-stepper .stepper .stepper__plus--hover { + background-color: #f5f5f5; +} +.goods-stepper .stepper .stepper__minus .wr-icon, +.goods-stepper .stepper .stepper__plus .wr-icon { + font-size: 24rpx; +} +.goods-stepper .stepper .stepper__minus { + position: relative; +} +.goods-stepper .stepper .stepper__minus::after { + position: absolute; + display: block; + content: ' '; + left: -20rpx; + right: -5rpx; + top: -20rpx; + bottom: -20rpx; + background-color: transparent; +} +.goods-stepper .stepper .stepper__plus { + position: relative; +} +.goods-stepper .stepper .stepper__plus::after { + position: absolute; + display: block; + content: ' '; + left: -5rpx; + right: -20rpx; + top: -20rpx; + bottom: -20rpx; + background-color: transparent; +} +.goods-stepper .stepper .stepper__input { + width: 72rpx; + height: 44rpx; + background-color: #f5f5f5; + font-size: 24rpx; + color: #222427; + font-weight: 600; + border-left: none; + border-right: none; + min-height: 40rpx; + margin: 0 4rpx; + display: flex; + align-items: center; +} + +.goods-sku-info .no-storage-mask { + position: absolute; + color: #fff; + bottom: 0rpx; + left: 0rpx; + background-color: rgba(0, 0, 0, 0.1); + height: 192rpx; + width: 192rpx; + border-radius: 8rpx; + display: flex; + justify-content: center; + align-items: center; +} + +.no-storage-mask .no-storage-content { + width: 128rpx; + height: 128rpx; + border-radius: 64rpx; + background-color: rgba(0, 0, 0, 0.4); + text-align: center; + line-height: 128rpx; + font-size: 28rpx; +} diff --git a/miniprogram/tcb-shop/pages/cart/components/cart-group/utils.wxs b/miniprogram/tcb-shop/pages/cart/components/cart-group/utils.wxs new file mode 100644 index 0000000..f887eba --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/components/cart-group/utils.wxs @@ -0,0 +1,20 @@ +module.exports.slice = function(arr) { + return arr.slice(0, 2); +}; +module.exports.imgCut = function(url, width, height) { + if (url && (url.slice(0, 5) === 'http:' || url.slice(0, 6) === 'https:' || url.slice(0, 2) === '//')) { + var argsStr = 'imageMogr2/thumbnail/!' + width + 'x' + height + 'r'; + if (url.indexOf('?') > -1) { + url = url + '&' + argsStr; + } else { + url = url + '?' + argsStr; + } + if (url.slice(0, 5) === 'http:') { + url = 'https://' + url.slice(5) + } + if (url.slice(0, 2) === '//') { + url = 'https:' + url + } + } + return url; +}; diff --git a/miniprogram/tcb-shop/pages/cart/components/goods-card/index.js b/miniprogram/tcb-shop/pages/cart/components/goods-card/index.js new file mode 100644 index 0000000..ccb37e4 --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/components/goods-card/index.js @@ -0,0 +1,232 @@ +Component({ + options: { + multipleSlots: true, // 在组件定义时的选项中启用多slot支持 + addGlobalClass: true, + }, + intersectionObserverContext: null, + + externalClasses: [ + 'card-class', + 'title-class', + 'desc-class', + 'num-class', + 'thumb-class', + 'specs-class', + 'price-class', + 'origin-price-class', + 'price-prefix-class', + ], + + properties: { + hidden: { + // 设置为null代表不做类型转换 + type: null, + value: false, + observer(hidden) { + // null就是代表没有设置,没有设置的话不setData,防止祖先组件触发的setHidden操作被覆盖 + if (hidden !== null) { + this.setHidden(!!hidden); + } + }, + }, + id: { + type: String, + // `goods-card-88888888` + // 不能在这里写生成逻辑,如果在这里写,那么假设有多个goods-list时,他们将共享这个值 + value: '', + observer: (id) => { + this.genIndependentID(id); + if (this.properties.thresholds?.length) { + this.createIntersectionObserverHandle(); + } + }, + }, + data: { + type: Object, + observer(goods) { + // 有ID的商品才渲染 + if (!goods?.sku) { + return; + } + + /** 划线价是否有效 */ + let isValidityLinePrice = true; + // 判断一次划线价格是否合理 + if (goods.originPrice && goods.price && goods.originPrice < goods.price) { + isValidityLinePrice = false; + } + + // 敲定换行数量默认值 + if (goods.lineClamp === undefined || goods.lineClamp <= 0) { + // tag数组长度 大于0 且 可见 + // 指定换行为1行 + if ((goods.sku.attr_value.length || 0) > 0) { + goods.lineClamp = 1; + } else { + goods.lineClamp = 2; + } + } + + this.setData({ goods, isValidityLinePrice, attr_str: goods.sku.attr_value.map((x) => x.value).join(',') }); + }, + }, + layout: { + type: String, + value: 'horizontal', + }, + thumbMode: { + type: String, + value: 'aspectFill', + }, + priceFill: { + type: Boolean, + value: true, + }, + currency: { + type: String, + value: '¥', + }, + lazyLoad: { + type: Boolean, + value: false, + }, + centered: { + type: Boolean, + value: false, + }, + pricePrefix: { + type: String, + value: '', + }, + /** 元素可见监控阈值, 数组长度大于0就创建 */ + thresholds: { + type: Array, + value: [], + observer(current) { + if (current && current.length) { + this.createIntersectionObserverHandle(); + } else { + this.clearIntersectionObserverHandle(); + } + }, + }, + specsIconClassPrefix: { + type: String, + value: 'wr', + }, + specsIcon: { + type: String, + value: 'expand_more', + }, + addCartIconClassPrefix: { + type: String, + value: 'wr', + }, + addCartIcon: { + type: String, + value: 'cart', + }, + }, + + data: { + hiddenInData: false, + independentID: '', + goods: { id: '' }, + /** 保证划线价格不小于原价,否则不渲染划线价 */ + isValidityLinePrice: false, + }, + + lifetimes: { + ready() { + this.init(); + }, + detached() { + this.clear(); + }, + }, + + methods: { + clickHandle() { + this.triggerEvent('click', { goods: this.data.goods }); + }, + clickThumbHandle() { + this.triggerEvent('thumb', { goods: this.data.goods }); + }, + clickSpecsHandle() { + this.triggerEvent('specs', { goods: this.data.goods }); + }, + // 加入购物车 + addCartHandle(e) { + const { id } = e.currentTarget; + const { id: cardID } = e.currentTarget.dataset; + this.triggerEvent('add-cart', { + ...e.detail, + id, + cardID, + goods: this.data.goods, + }); + }, + genIndependentID(id, cb) { + let independentID; + if (id) { + independentID = id; + } else { + independentID = `goods-card-${~~(Math.random() * 10 ** 8)}`; + } + this.setData({ independentID }, cb); + }, + + init() { + const { thresholds, id, hidden } = this.properties; + if (hidden !== null) { + this.setHidden(!!hidden); + } + + this.genIndependentID(id || '', () => { + if (thresholds && thresholds.length) { + this.createIntersectionObserverHandle(); + } + }); + }, + + clear() { + this.clearIntersectionObserverHandle(); + }, + + setHidden(hidden) { + this.setData({ hiddenInData: !!hidden }); + }, + + createIntersectionObserverHandle() { + if (this.intersectionObserverContext || !this.data.independentID) { + return; + } + + this.intersectionObserverContext = wx + .createIntersectionObserver(this, { + thresholds: this.properties.thresholds, + }) + .relativeToViewport(); + + this.intersectionObserverContext.observe(`#${this.data.independentID}`, (res) => { + this.intersectionObserverCB(res); + }); + }, + intersectionObserverCB(ob) { + this.triggerEvent('ob', { + goods: this.data.goods, + context: this.intersectionObserverContext, + ob, + }); + }, + clearIntersectionObserverHandle() { + if (this.intersectionObserverContext) { + try { + this.intersectionObserverContext.disconnect(); + } catch (e) {} + + this.intersectionObserverContext = null; + } + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/cart/components/goods-card/index.json b/miniprogram/tcb-shop/pages/cart/components/goods-card/index.json new file mode 100644 index 0000000..d76303b --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/components/goods-card/index.json @@ -0,0 +1,9 @@ +{ + "component": true, + "usingComponents": { + "price": "/components/price/index", + "t-tag": "tdesign-miniprogram/tag/tag", + "t-image": "/components/webp-image/index", + "t-icon": "tdesign-miniprogram/icon/icon" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/cart/components/goods-card/index.wxml b/miniprogram/tcb-shop/pages/cart/components/goods-card/index.wxml new file mode 100644 index 0000000..ff5d792 --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/components/goods-card/index.wxml @@ -0,0 +1,46 @@ + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/cart/components/goods-card/index.wxss b/miniprogram/tcb-shop/pages/cart/components/goods-card/index.wxss new file mode 100644 index 0000000..6a7a4da --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/components/goods-card/index.wxss @@ -0,0 +1,260 @@ +.wr-goods-card { + box-sizing: border-box; + font-size: 24rpx; +} +/* */ +.wr-goods-card__main { + position: relative; + display: flex; + padding: 0; + background: transparent; +} + +.wr-goods-card.center .wr-goods-card__main { + align-items: flex-start; + justify-content: center; +} + +.wr-goods-card__thumb { + flex-shrink: 0; + position: relative; + width: 140rpx; + height: 140rpx; +} + +.wr-goods-card__thumb-com { + width: 192rpx; + height: 192rpx; + border-radius: 8rpx; + overflow: hidden; +} +.wr-goods-card__thumb:empty { + display: none; + margin: 0; +} + +.wr-goods-card__body { + display: flex; + margin: 0 0 0 20rpx; + flex-direction: row; + flex: 1 1 auto; + min-height: 192rpx; +} + +.wr-goods-card__long_content { + display: flex; + flex-direction: column; + overflow: hidden; + flex: 1 1 auto; +} +.wr-goods-card__long_content .goods_tips { + width: 100%; + margin-top: 16rpx; + text-align: right; + color: #fa4126; + font-size: 24rpx; + line-height: 32rpx; + font-weight: bold; +} +.wr-goods-card__title { + flex-shrink: 0; + font-size: 28rpx; + color: #333; + line-height: 40rpx; + font-weight: 400; + display: -webkit-box; + -webkit-box-orient: vertical; + overflow: hidden; + word-break: break-word; +} +.wr-goods-card__title__prefix-tags { + display: inline-flex; +} +.wr-goods-card__title__prefix-tags .prefix-tag { + margin: 0 8rpx 0 0; +} +.wr-goods-card__desc { + font-size: 24rpx; + color: #f5f5f5; + line-height: 40rpx; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + overflow: hidden; +} +.wr-goods-card__specs__desc, +.wr-goods-card__specs__text { + font-size: 24rpx; + height: 32rpx; + line-height: 32rpx; + color: #999999; + margin: 8rpx 0; +} +.wr-goods-card__specs__desc { + display: flex; + align-self: flex-start; + flex-direction: row; + background: #f5f5f5; + border-radius: 8rpx; + padding: 4rpx 8rpx; +} +.wr-goods-card__specs__desc-text { + height: 100%; + max-width: 380rpx; + word-break: break-all; + overflow: hidden; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 1; +} +.wr-goods-card__specs__desc-icon { + line-height: inherit; + margin-left: 8rpx; + font-size: 24rpx; + color: #bbb; +} +.wr-goods-card__specs__text { + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 1; + overflow: hidden; + text-overflow: ellipsis; + word-break: break-all; +} +.wr-goods-card__tags { + display: flex; + flex-direction: row; + flex-wrap: wrap; + margin: 16rpx 0 0 0; +} +.wr-goods-card__tag { + color: #fa550f; + background: transparent; + font-size: 20rpx; + border: 1rpx solid #fa550f; + padding: 0 8rpx; + height: 30rpx; + line-height: 30rpx; + margin: 0 8rpx 8rpx 0; + display: block; + overflow: hidden; + white-space: nowrap; + word-break: keep-all; + text-overflow: ellipsis; +} +.wr-goods-card__short_content { + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: flex-end; + margin: 0 0 0 46rpx; +} +.wr-goods-card__price__prefix { + order: 0; + color: #666; + margin: 0; +} +.wr-goods-card__price { + white-space: nowrap; + font-weight: bold; + order: 1; + color: #fa4126; + font-size: 36rpx; + margin: 0; + line-height: 48rpx; +} +.wr-goods-card__origin-price { + white-space: nowrap; + font-weight: normal; + order: 2; + color: #aaaaaa; + font-size: 24rpx; + margin: 0; +} +.wr-goods-card__num { + white-space: nowrap; + order: 4; + font-size: 24rpx; + color: #999; + margin: 20rpx 0 0 auto; +} +.wr-goods-card__num__prefix { + color: inherit; +} +.wr-goods-card__add-cart { + order: 3; + margin: auto 0 0 auto; +} +.wr-goods-card.horizontal-wrap .wr-goods-card__thumb { + width: 192rpx; + height: 192rpx; + border-radius: 8rpx; + overflow: hidden; +} +.wr-goods-card.horizontal-wrap .wr-goods-card__body { + flex-direction: column; +} +.wr-goods-card.horizontal-wrap .wr-goods-card__short_content { + flex-direction: row; + align-items: center; + margin: 16rpx 0 0 0; +} + +.wr-goods-card.horizontal-wrap .wr-goods-card__num { + margin: 0 0 0 auto; +} +.wr-goods-card.vertical .wr-goods-card__main { + padding: 0 0 22rpx 0; + flex-direction: column; +} +.wr-goods-card.vertical .wr-goods-card__thumb { + width: 340rpx; + height: 340rpx; +} +.wr-goods-card.vertical .wr-goods-card__body { + margin: 20rpx 20rpx 0 20rpx; + flex-direction: column; +} +.wr-goods-card.vertical .wr-goods-card__long_content { + overflow: hidden; +} +.wr-goods-card.vertical .wr-goods-card__title { + line-height: 36rpx; +} +.wr-goods-card.vertical .wr-goods-card__short_content { + margin: 20rpx 0 0 0; +} +.wr-goods-card.vertical .wr-goods-card__price { + order: 2; + color: #fa4126; + margin: 20rpx 0 0 0; +} +.wr-goods-card.vertical .wr-goods-card__origin-price { + order: 1; +} +.wr-goods-card.vertical .wr-goods-card__add-cart { + position: absolute; + bottom: 20rpx; + right: 20rpx; +} + +.wr-goods-card__short_content .no_storage { + display: flex; + align-items: center; + justify-content: space-between; + height: 40rpx; + color: #333; + font-size: 24rpx; + line-height: 32rpx; + width: 100%; +} + +.no_storage .no_storage__right { + width: 80rpx; + height: 40rpx; + border-radius: 20rpx; + border: 2rpx solid #fa4126; + line-height: 40rpx; + text-align: center; + color: #fa4126; +} diff --git a/miniprogram/tcb-shop/pages/cart/components/specs-popup/index.js b/miniprogram/tcb-shop/pages/cart/components/specs-popup/index.js new file mode 100644 index 0000000..5b71038 --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/components/specs-popup/index.js @@ -0,0 +1,48 @@ +Component({ + options: { + addGlobalClass: true, + multipleSlots: true, // 在组件定义时的选项中启用多slot支持 + }, + + properties: { + goods: { + type: Object, + value: null, + observer(goods) { + if (!goods) { + return; + } + this.setData({ + goodsWithoutAttrValue: { + ...goods, + sku: { + ...goods.sku, + attr_value: [], + }, + }, + }); + }, + }, + show: { + type: Boolean, + value: false, + }, + thumbMode: { + type: String, + value: 'aspectFit', + }, + }, + + data: { + goodsWithoutAttrValue: null, + }, + methods: { + onClose() { + this.triggerEvent('close'); + }, + + onCloseOver() { + this.triggerEvent('closeover'); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/cart/components/specs-popup/index.json b/miniprogram/tcb-shop/pages/cart/components/specs-popup/index.json new file mode 100644 index 0000000..d59fc6d --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/components/specs-popup/index.json @@ -0,0 +1,7 @@ +{ + "component": true, + "usingComponents": { + "t-popup": "tdesign-miniprogram/popup/popup", + "goods-card": "../../components/goods-card/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/cart/components/specs-popup/index.wxml b/miniprogram/tcb-shop/pages/cart/components/specs-popup/index.wxml new file mode 100644 index 0000000..8833670 --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/components/specs-popup/index.wxml @@ -0,0 +1,16 @@ + + + + + + 已选规格 + + + {{spec.value}} + + + + + 我知道了 + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/cart/components/specs-popup/index.wxss b/miniprogram/tcb-shop/pages/cart/components/specs-popup/index.wxss new file mode 100644 index 0000000..359c185 --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/components/specs-popup/index.wxss @@ -0,0 +1,68 @@ +.specs-popup { + width: 100vw; + box-sizing: border-box; + padding: 32rpx 32rpx calc(20rpx + env(safe-area-inset-bottom)) 32rpx; + max-height: 80vh; + display: flex; + flex-direction: column; + background-color: white; + border-radius: 20rpx 20rpx 0 0; +} +.specs-popup .section { + margin-top: 44rpx; + flex: auto; + overflow-y: scroll; + overflow-x: hidden; + -webkit-overflow-scrolling: touch; +} +.specs-popup .section .title { + font-size: 26rpx; + color: #4f5356; +} +.specs-popup .section .options { + color: #333333; + font-size: 24rpx; + margin-right: -26rpx; +} +.specs-popup .section .options .option { + display: inline-block; + margin-top: 24rpx; + height: 56rpx; + line-height: 56rpx; + padding: 0 16rpx; + border-radius: 8rpx; + background-color: #f5f5f5; + max-width: 100%; + box-sizing: border-box; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.specs-popup .section .options .option:not(:last-child) { + margin-right: 26rpx; +} +.specs-popup .bottom-btn { + margin-top: 42rpx; + position: relative; + height: 80rpx; + line-height: 80rpx; + text-align: center; + background-color: white; + color: #fa4126; +} +.specs-popup .bottom-btn--active { + opacity: 0.5; +} +.specs-popup .bottom-btn::after { + display: block; + content: ' '; + position: absolute; + left: 0; + top: 0; + width: 200%; + height: 200%; + border: 1px solid #fa4126; + border-radius: 80rpx; + transform: scale(0.5); + transform-origin: left top; +} diff --git a/miniprogram/tcb-shop/pages/cart/index.js b/miniprogram/tcb-shop/pages/cart/index.js new file mode 100644 index 0000000..8e50e3d --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/index.js @@ -0,0 +1,310 @@ +import Dialog from 'tdesign-miniprogram/dialog/index'; +import { fetchCartGroupData, fetchCartItems, deleteCartItem, updateCartItemCount } from '../../services/cart/cart'; +import { getSkuDetail } from '../../services/sku/sku'; +import { objectToParamString } from '../../utils/util'; +import { getSingleCloudImageTempUrl } from '../../utils/cloudImageHandler'; + +let updateCartItemCountTimeout = null; + +Component({ + data: { + cartGroupData: null, + cartItems: [], + selectedCartItemNum: 0, + allSelected: false, + totalAmount: 0, + loading: false, + inited: false, + }, + + observers: { + cartItems: function (cartItems) { + const selectedCartItems = cartItems.filter((x) => x.selected === true); + const selectedCartItemNum = selectedCartItems.length; + const totalAmount = selectedCartItems.reduce((acc, cur) => acc + cur.count * cur.sku.price, 0); + const allSelected = selectedCartItemNum === cartItems.length; + + this.setData({ + selectedCartItemNum, + allSelected, + totalAmount, + }); + }, + }, + + lifetimes: { + attached: async function () { + // console.log('called attached'); + // // 调用自定义tabbar的init函数,使页面与tabbar激活状态保持一致 + // this.getTabBar().init(); + // await this.setLoading(); + // await this.setDataPromise({ inited: true }); + // try { + // await this.init(); + // } finally { + // await this.unsetLoading(); + // } + }, + }, + + pageLifetimes: { + show: async function () { + // 调用自定义tabbar的init函数,使页面与tabbar激活状态保持一致 + this.getTabBar().init(); + + await this.setLoading(); + try { + await this.init(); + } finally { + await this.unsetLoading(); + } + }, + }, + + methods: { + async init() { + const cartItems = (await fetchCartItems()).map((x) => + Object.assign(x, { + selected: false, + }), + ); + await Promise.all( + cartItems.map(async (cartItem) => { + const skuId = cartItem.sku._id; + const sku = await getSkuDetail(skuId); + if (sku.image) { + sku.image = await getSingleCloudImageTempUrl(sku.image); + } + cartItem.sku = sku; + }), + ); + await this.setDataPromise({ + cartItems, + }); + }, + + findGoods(spuId, skuId) { + let currentStore; + let currentActivity; + let currentGoods; + const { storeGoods } = this.data.cartGroupData; + for (const store of storeGoods) { + for (const activity of store.promotionGoodsList) { + for (const goods of activity.goodsPromotionList) { + if (goods.spuId === spuId && goods.skuId === skuId) { + currentStore = store; + currentActivity = currentActivity; + currentGoods = goods; + return { + currentStore, + currentActivity, + currentGoods, + }; + } + } + } + } + return { + currentStore, + currentActivity, + currentGoods, + }; + }, + + // 注:实际场景时应该调用接口获取购物车数据 + getCartGroupData() { + const { cartGroupData } = this.data; + if (!cartGroupData) { + return fetchCartGroupData(); + } + return Promise.resolve({ data: cartGroupData }); + }, + + // 选择单个商品 + // 注:实际场景时应该调用接口更改选中状态 + selectGoodsService({ spuId, skuId, isSelected }) { + this.findGoods(spuId, skuId).currentGoods.isSelected = isSelected; + return Promise.resolve(); + }, + + // 全选门店 + // 注:实际场景时应该调用接口更改选中状态 + selectStoreService({ storeId, isSelected }) { + const currentStore = this.data.cartGroupData.storeGoods.find((s) => s.storeId === storeId); + currentStore.isSelected = isSelected; + currentStore.promotionGoodsList.forEach((activity) => { + activity.goodsPromotionList.forEach((goods) => { + goods.isSelected = isSelected; + }); + }); + return Promise.resolve(); + }, + + // 加购数量变更 + // 注:实际场景时应该调用接口 + changeQuantityService({ spuId, skuId, quantity }) { + this.findGoods(spuId, skuId).currentGoods.quantity = quantity; + return Promise.resolve(); + }, + + // 删除加购商品 + // 注:实际场景时应该调用接口 + deleteGoodsService({ spuId, skuId }) { + function deleteGoods(group) { + for (const gindex in group) { + const goods = group[gindex]; + if (goods.spuId === spuId && goods.skuId === skuId) { + group.splice(gindex, 1); + return gindex; + } + } + return -1; + } + const { storeGoods, invalidGoodItems } = this.data.cartGroupData; + for (const store of storeGoods) { + for (const activity of store.promotionGoodsList) { + if (deleteGoods(activity.goodsPromotionList) > -1) { + return Promise.resolve(); + } + } + if (deleteGoods(store.shortageGoodsList) > -1) { + return Promise.resolve(); + } + } + if (deleteGoods(invalidGoodItems) > -1) { + return Promise.resolve(); + } + return Promise.reject(); + }, + + onGoodsSelect({ detail: { goods } }) { + const { cartItems } = this.data; + const item = cartItems.find((x) => x._id === goods._id); + + if (item == null) { + console.warn('Cart item not found!'); + return; + } + + item.selected = !item.selected; + this.setData({ cartItems }); + }, + + onQuantityChange({ detail: { cartItemId, count } }) { + const { cartItems } = this.data; + const item = cartItems.find((x) => x._id === cartItemId); + if (item == null) { + console.warn('Cart item not found'); + return; + } + item.count = count; + this.setData({ cartItems }); + this.debouncedUpdateCartItemCount({ cartItemId, count }); + }, + + debouncedUpdateCartItemCount({ cartItemId, count }) { + clearTimeout(updateCartItemCountTimeout); + updateCartItemCountTimeout = setTimeout(async () => { + this.setLoading(); + try { + await updateCartItemCount({ cartItemId, count }); + } finally { + this.unsetLoading(); + } + }, 500); + }, + + goCollect() { + /** 活动肯定有一个活动ID,用来获取活动banner,活动商品列表等 */ + const promotionID = '123'; + wx.navigateTo({ + url: `/pages/promotion-detail/index?promotion_id=${promotionID}`, + }); + }, + + goGoodsDetail({ + detail: { + goods: { + sku: { + spu: { _id }, + }, + }, + }, + }) { + wx.navigateTo({ + url: `/pages/goods/details/index?spuId=${_id}`, + }); + }, + + async onGoodsDelete({ + detail: { + goods: { _id }, + }, + }) { + try { + await Dialog.confirm({ + context: this, + closeOnOverlayClick: true, + title: '确认删除该商品吗?', + confirmBtn: '确定', + cancelBtn: '取消', + }); + this.setLoading(); + try { + await deleteCartItem({ cartItemId: _id }); + const { cartItems } = this.data; + this.setData({ + cartItems: cartItems.filter((x) => x._id !== _id), + }); + } finally { + this.unsetLoading(); + } + } catch { + console.warn('deletion is cancelled'); + } + }, + + onSelectAll() { + const { cartItems, allSelected } = this.data; + cartItems.forEach((x) => (x.selected = !allSelected)); + this.setData({ cartItems }); + }, + + onToSettle() { + wx.navigateTo({ + url: `/pages/order/order-confirm/index?${objectToParamString({ + type: 'cart', + cartIds: this.data.cartItems + .filter((x) => x.selected === true) + .map((x) => x._id) + .join(','), + })}`, + }); + }, + onGotoHome() { + wx.switchTab({ url: '/pages/home/home' }); + }, + + setLoading() { + return this.setDataPromise({ + loading: true, + }); + }, + unsetLoading() { + return this.setDataPromise({ + loading: false, + }); + }, + toggleLoading() { + this.setData({ + loading: !this.data.loading, + }); + }, + + setDataPromise(data) { + return new Promise((res) => { + this.setData(data, () => res()); + }); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/cart/index.json b/miniprogram/tcb-shop/pages/cart/index.json new file mode 100644 index 0000000..b78f95a --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/index.json @@ -0,0 +1,11 @@ +{ + "navigationBarTitleText": "购物车", + "usingComponents": { + "cart-group": "./components/cart-group/index", + "cart-empty": "./components/cart-empty/index", + "cart-bar": "./components/cart-bar/index", + "loading-dialog": "../../components/loading-dialog/index", + "t-toast": "tdesign-miniprogram/toast/toast", + "t-dialog": "tdesign-miniprogram/dialog/dialog" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/cart/index.wxml b/miniprogram/tcb-shop/pages/cart/index.wxml new file mode 100644 index 0000000..affa744 --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/index.wxml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/cart/index.wxss b/miniprogram/tcb-shop/pages/cart/index.wxss new file mode 100644 index 0000000..ea19962 --- /dev/null +++ b/miniprogram/tcb-shop/pages/cart/index.wxss @@ -0,0 +1,13 @@ +:host { + padding-bottom: 100rpx; +} + +.gap { + height: 100rpx; + width: 100%; +} + +.t-button { + --td-button-default-color: #000; + --td-button-primary-text-color: #fa4126; +} diff --git a/miniprogram/tcb-shop/pages/coupon/components/coupon-card/index.js b/miniprogram/tcb-shop/pages/coupon/components/coupon-card/index.js new file mode 100644 index 0000000..07785fe --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/components/coupon-card/index.js @@ -0,0 +1,57 @@ +const statusMap = { + default: { text: '去使用', theme: 'primary' }, + useless: { text: '已使用', theme: 'default' }, + disabled: { text: '已过期', theme: 'default' }, +}; +Component({ + options: { + addGlobalClass: true, + multipleSlots: true, // 在组件定义时的选项中启用多slot支持 + }, + + externalClasses: ['coupon-class'], + + properties: { + couponDTO: { + type: Object, + value: {}, // 优惠券数据 + }, + }, + + data: { + btnText: '', + btnTheme: '', + }, + + observers: { + couponDTO: function (couponDTO) { + if (!couponDTO) { + return; + } + const statusInfo = statusMap[couponDTO.status]; + + this.setData({ + btnText: statusInfo.text, + btnTheme: statusInfo.theme, + }); + }, + }, + + attached() {}, + + methods: { + // 跳转到详情页 + gotoDetail() { + wx.navigateTo({ + url: `/pages/coupon/coupon-detail/index?id=${this.data.couponDTO.key}`, + }); + }, + + // 跳转到商品列表 + gotoGoodsList() { + wx.navigateTo({ + url: `/pages/coupon/coupon-activity-goods/index?id=${this.data.couponDTO.key}`, + }); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/coupon/components/coupon-card/index.json b/miniprogram/tcb-shop/pages/coupon/components/coupon-card/index.json new file mode 100644 index 0000000..2d851d1 --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/components/coupon-card/index.json @@ -0,0 +1,7 @@ +{ + "component": true, + "usingComponents": { + "ui-coupon-card": "../ui-coupon-card/index", + "t-button": "tdesign-miniprogram/button/button" + } +} diff --git a/miniprogram/tcb-shop/pages/coupon/components/coupon-card/index.wxml b/miniprogram/tcb-shop/pages/coupon/components/coupon-card/index.wxml new file mode 100644 index 0000000..4dd56e1 --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/components/coupon-card/index.wxml @@ -0,0 +1,23 @@ + + + {{btnText}} + + + diff --git a/miniprogram/tcb-shop/pages/coupon/components/coupon-card/index.wxss b/miniprogram/tcb-shop/pages/coupon/components/coupon-card/index.wxss new file mode 100644 index 0000000..905dc40 --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/components/coupon-card/index.wxss @@ -0,0 +1,9 @@ +.coupon-btn-default { + display: none; +} + +.coupon-btn-primary { + --td-button-extra-small-padding-horizontal: 26rpx; + --td-button-primary-outline-color: #fa4126; + --td-button-primary-outline-border-color: #fa4126; +} diff --git a/miniprogram/tcb-shop/pages/coupon/components/floating-button/index.js b/miniprogram/tcb-shop/pages/coupon/components/floating-button/index.js new file mode 100644 index 0000000..f29bcad --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/components/floating-button/index.js @@ -0,0 +1,17 @@ +Component({ + data: { icon: 'cart' }, + + properties: { + count: { + type: Number, + }, + }, + + methods: { + goToCart() { + wx.switchTab({ + url: '/pages/cart/index', + }); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/coupon/components/floating-button/index.json b/miniprogram/tcb-shop/pages/coupon/components/floating-button/index.json new file mode 100644 index 0000000..fa2d209 --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/components/floating-button/index.json @@ -0,0 +1,6 @@ +{ + "component": true, + "usingComponents": { + "t-icon": "tdesign-miniprogram/icon/icon" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/coupon/components/floating-button/index.wxml b/miniprogram/tcb-shop/pages/coupon/components/floating-button/index.wxml new file mode 100644 index 0000000..7f1a9e7 --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/components/floating-button/index.wxml @@ -0,0 +1,14 @@ + + + + + + {{count}} + + + diff --git a/miniprogram/tcb-shop/pages/coupon/components/floating-button/index.wxss b/miniprogram/tcb-shop/pages/coupon/components/floating-button/index.wxss new file mode 100644 index 0000000..faad943 --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/components/floating-button/index.wxss @@ -0,0 +1,30 @@ +.floating-button { + position: fixed; + right: 20rpx; + bottom: 108rpx; +} + +.floating-button .floating-inner-container { + display: flex; + align-items: center; + justify-content: center; + height: 96rpx; + width: 96rpx; + background-color: rgba(0, 0, 0, 0.8); + opacity: 0.7; + border-radius: 48rpx; +} + +.floating-button .floating-right-top { + position: absolute; + right: 0rpx; + top: 0rpx; + height: 28rpx; + background: #fa4126; + border-radius: 64rpx; + font-weight: bold; + font-size: 22rpx; + line-height: 28rpx; + color: #fff; + padding: 0 8rpx; +} diff --git a/miniprogram/tcb-shop/pages/coupon/components/ui-coupon-card/index.js b/miniprogram/tcb-shop/pages/coupon/components/ui-coupon-card/index.js new file mode 100644 index 0000000..9101be8 --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/components/ui-coupon-card/index.js @@ -0,0 +1,87 @@ +Component({ + options: { + addGlobalClass: true, + multipleSlots: true, + }, + + externalClasses: ['coupon-class'], + + properties: { + mask: { + type: Boolean, + value: false, // 是否添加遮罩 + }, + superposable: { + type: Boolean, + value: false, // 是否可叠加 + }, + type: { + type: String, + value: '', // 优惠券类型:CouponType + }, + value: { + type: String, + value: '', // 优惠金额 + }, + tag: { + type: String, + value: '', // 优惠标签,优惠券名字标签,img + }, + desc: { + type: String, + value: '', // 优惠金额描述,金额下方 + }, + title: { + type: String, // 优惠券名称 + value: '', + }, + timeLimit: { + type: String, // 优惠券时限 + value: '', + }, + ruleDesc: { + type: String, // 优惠券适用规则描述 + value: '', + }, + currency: { + type: String, + value: '¥', // 优惠货币 + }, + status: { + type: String, + value: 'default', + }, + image: { + type: String, + value: '', + }, + }, + + data: { + CouponType: { + MJ_COUPON: 1, + ZK_COUPON: 2, + MJF_COUPON: 3, + GIFT_COUPON: 4, + }, + theme: 'primary', + }, + + observers: { + status: function (value) { + let theme = 'primary'; + // 已过期或已使用的券 颜色置灰 + if (value === 'useless' || value === 'disabled') { + theme = 'weak'; + } + + this.setData({ theme }); + }, + }, + + attached() { + this.setData({ + color: `color${this.properties.colorStyle}`, + }); + }, +}); diff --git a/miniprogram/tcb-shop/pages/coupon/components/ui-coupon-card/index.json b/miniprogram/tcb-shop/pages/coupon/components/ui-coupon-card/index.json new file mode 100644 index 0000000..dd874c8 --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/components/ui-coupon-card/index.json @@ -0,0 +1,7 @@ +{ + "component": true, + "usingComponents": { + "t-icon": "tdesign-miniprogram/icon/icon", + "t-image": "/components/webp-image/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/coupon/components/ui-coupon-card/index.wxml b/miniprogram/tcb-shop/pages/coupon/components/ui-coupon-card/index.wxml new file mode 100644 index 0000000..ec76acb --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/components/ui-coupon-card/index.wxml @@ -0,0 +1,55 @@ + + function isBigValue(value) { + var values = (value + '').split('.'); + if (values[1] && values[0].length >= 3) return true; + else return false + } + + function getBigValues(value) { + return value.split('.'); + } + + module.exports = { isBigValue: isBigValue, getBigValues: getBigValues }; + + + + + {{value}} + + {{desc}} + + + + {{tools.getBigValues(value)[0]}} + .{{tools.getBigValues(value)[1]}} + + {{value / 100}} + + {{desc}} + + + + 免邮 + + {{desc}} + + + + + + + + {{title}} + {{timeLimit}} + + {{ruleDesc}} + + + + + + + + + 可叠加 + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/coupon/components/ui-coupon-card/index.wxss b/miniprogram/tcb-shop/pages/coupon/components/ui-coupon-card/index.wxss new file mode 100644 index 0000000..0ef9648 --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/components/ui-coupon-card/index.wxss @@ -0,0 +1,147 @@ +.wr-coupon { + display: flex; + background-image: url('https://cdn-we-retail.ym.tencent.com/miniapp/coupon/coupon-bg-nocorners.png'); + background-size: 100% 100%; + background-repeat: no-repeat; + position: relative; + margin-bottom: 24rpx; + overflow: hidden; +} +.theme-weak.wr-coupon { + background-image: url('https://cdn-we-retail.ym.tencent.com/miniapp/coupon/coupon-bg-grey2.png'); +} + +.wr-coupon__left { + width: 200rpx; + height: 180rpx; + display: flex; + flex-direction: column; + justify-content: center; + text-align: center; + color: #fa4126; + overflow: hidden; + position: relative; +} +.theme-weak .wr-coupon__left { + color: #333; +} + +.wr-coupon__left--value { + font-size: 64rpx; + line-height: 88rpx; + font-weight: bold; + font-family: 'DIN Alternate', cursive; +} +.wr-coupon__left--value-int { + font-size: 48rpx; + line-height: 88rpx; +} +.wr-coupon__left--value-decimal { + font-size: 36rpx; + line-height: 48rpx; +} +.wr-coupon__left--image { + width: 128rpx; + height: 128rpx; + border-radius: 8px; + margin-top: 30rpx; +} +.wr-coupon__left--unit { + font-size: 24rpx; + line-height: 32rpx; +} +.wr-coupon__left--desc { + font-size: 24rpx; + line-height: 32rpx; + color: #fa4126; +} + +.theme-weak .wr-coupon__left--desc { + color: #333; +} + +.wr-coupon__right { + flex-grow: 1; + padding: 0 20rpx; + height: 180rpx; + box-sizing: border-box; + overflow: hidden; + display: flex; + align-items: center; +} +.wr-coupon__right--title { + display: flex; + -webkit-display: flex; + flex-direction: column; + align-items: flex-start; + color: #999999; + font-size: 24rpx; + flex: 1; +} +.wr-coupon__right--title .coupon-title { + max-width: 320rpx; + color: #333333; + font-size: 28rpx; + line-height: 40rpx; + font-weight: bold; + display: -webkit-box; + -webkit-line-clamp: 1; + -webkit-box-orient: vertical; + overflow: hidden; + white-space: normal; +} +.wr-coupon__right--title .coupon-time { + margin-top: 16rpx; + /* // letter-spacing: -0.05em; */ +} +.wr-coupon__right--title .coupon-desc { + margin-top: 8rpx; +} +.wr-coupon__right--title .coupon-arrow { + font-size: 22rpx; +} +.wr-coupon__right--oper { + display: flex; + justify-content: center; + align-items: center; +} +.wr-coupon__mask { + width: 702rpx; + height: 182rpx; + position: absolute; + top: 0; + left: 0; + background-color: #ffffff; + opacity: 0.5; +} +.wr-coupon__tag { + position: absolute; + top: 8px; + right: -24rpx; + text-align: center; + width: 106rpx; + height: 28rpx; + opacity: 0.9; + font-size: 20rpx; + line-height: 28rpx; + color: #fa4126; + border: 0.5px solid #fa4126; + box-sizing: border-box; + transform: rotate(45deg); +} +.wr-coupon__seal { + width: 128rpx; + height: 128rpx; + position: absolute; + top: 0; + right: 0; + background-size: 100% 100%; +} + +.wr-coupon__seal.seal-useless { + background-image: url('https://cdn-we-retail.ym.tencent.com/miniapp/coupon/seal-used.png'); +} + +.wr-coupon__seal.seal-disabled { + background-image: url('https://cdn-we-retail.ym.tencent.com/miniapp/coupon/coupon-expired.png'); +} diff --git a/miniprogram/tcb-shop/pages/coupon/coupon-activity-goods/index.js b/miniprogram/tcb-shop/pages/coupon/coupon-activity-goods/index.js new file mode 100644 index 0000000..8010400 --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/coupon-activity-goods/index.js @@ -0,0 +1,75 @@ +import { fetchCouponDetail } from '../../../services/coupon/index'; +import Toast from 'tdesign-miniprogram/toast/index'; + +Page({ + data: { + goods: [], + detail: {}, + couponTypeDesc: '', + showStoreInfoList: false, + cartNum: 2, + }, + + id: '', + + onLoad(query) { + const id = parseInt(query.id); + this.id = id; + + this.getCouponDetail(id); + this.getGoodsList(id); + }, + + getCouponDetail(id) { + fetchCouponDetail(id).then(({ detail }) => { + this.setData({ detail }); + if (detail.type === 2) { + if (detail.base > 0) { + this.setData({ + couponTypeDesc: `满${detail.base / 100}元${detail.value}折`, + }); + } else { + this.setData({ couponTypeDesc: `${detail.value}折` }); + } + } else if (detail.type === 1) { + if (detail.base > 0) { + this.setData({ + couponTypeDesc: `满${detail.base / 100}元减${detail.value / 100}元`, + }); + } else { + this.setData({ couponTypeDesc: `减${detail.value / 100}元` }); + } + } + }); + }, + + getGoodsList() { + throw new Error('unimplemented'); + }, + + openStoreList() { + this.setData({ + showStoreInfoList: true, + }); + }, + + closeStoreList() { + this.setData({ + showStoreInfoList: false, + }); + }, + + goodClickHandle(e) { + const { index } = e.detail; + const { spuId } = this.data.goods[index]; + wx.navigateTo({ url: `/pages/goods/details/index?spuId=${spuId}` }); + }, + + cartClickHandle() { + Toast({ + context: this, + selector: '#t-toast', + message: '点击加入购物车', + }); + }, +}); diff --git a/miniprogram/tcb-shop/pages/coupon/coupon-activity-goods/index.json b/miniprogram/tcb-shop/pages/coupon/coupon-activity-goods/index.json new file mode 100644 index 0000000..31cf9c3 --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/coupon-activity-goods/index.json @@ -0,0 +1,10 @@ +{ + "navigationBarTitleText": "活动商品", + "usingComponents": { + "t-icon": "tdesign-miniprogram/icon/icon", + "t-popup": "tdesign-miniprogram/popup/popup", + "t-toast": "tdesign-miniprogram/toast/toast", + "goods-list": "/components/goods-list/index", + "floating-button": "../components/floating-button/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/coupon/coupon-activity-goods/index.wxml b/miniprogram/tcb-shop/pages/coupon/coupon-activity-goods/index.wxml new file mode 100644 index 0000000..1c55b7f --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/coupon-activity-goods/index.wxml @@ -0,0 +1,40 @@ + + + + 以下商品可使用 + {{couponTypeDesc}} + 优惠券 + + + + + + + + + + + 规则详情 + + + 优惠券有效时间 + {{detail.timeLimit}} + + + 优惠券说明 + {{detail.desc}} + + + 使用须知 + {{detail.useNotes}} + + + + + + diff --git a/miniprogram/tcb-shop/pages/coupon/coupon-activity-goods/index.wxss b/miniprogram/tcb-shop/pages/coupon/coupon-activity-goods/index.wxss new file mode 100644 index 0000000..3217203 --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/coupon-activity-goods/index.wxss @@ -0,0 +1,69 @@ +page { + background-color: #f5f5f5; +} + +.coupon-page-container .notice-bar-content { + display: flex; + flex-direction: row; + align-items: center; + padding: 8rpx 0; +} + +.coupon-page-container .notice-bar-text { + font-size: 26rpx; + line-height: 36rpx; + font-weight: 400; + color: #666666; + margin-left: 24rpx; + margin-right: 12rpx; +} + +.coupon-page-container .notice-bar-text .height-light { + color: #fa550f; +} + +.coupon-page-container .popup-content-wrap { + background-color: #fff; + border-top-left-radius: 20rpx; + border-top-right-radius: 20rpx; +} + +.coupon-page-container .popup-content-title { + font-size: 32rpx; + color: #333; + text-align: center; + height: 104rpx; + line-height: 104rpx; + position: relative; +} + +.coupon-page-container .desc-group-wrap { + padding-bottom: env(safe-area-inset-bottom); +} + +.coupon-page-container .desc-group-wrap .item-wrap { + margin: 0 30rpx 30rpx; +} + +.coupon-page-container .desc-group-wrap .item-title { + font-size: 26rpx; + color: #333; + font-weight: 500; +} + +.coupon-page-container .desc-group-wrap .item-label { + font-size: 24rpx; + color: #666; + margin-top: 12rpx; + white-space: pre-line; + word-break: break-all; + line-height: 34rpx; +} + +.coupon-page-container .goods-list-container { + margin: 0 24rpx 24rpx; +} + +.coupon-page-container .goods-list-wrap { + background: #f5f5f5 !important; +} diff --git a/miniprogram/tcb-shop/pages/coupon/coupon-detail/index.js b/miniprogram/tcb-shop/pages/coupon/coupon-detail/index.js new file mode 100644 index 0000000..71ff5d6 --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/coupon-detail/index.js @@ -0,0 +1,32 @@ +import { fetchCouponDetail } from '../../../services/coupon/index'; + +Page({ + data: { + detail: null, + storeInfoList: [], + storeInfoStr: '', + showStoreInfoList: false, + }, + + id: '', + + onLoad(query) { + const id = parseInt(query.id); + this.id = id; + this.getGoodsList(id); + }, + + getGoodsList(id) { + fetchCouponDetail(id).then(({ detail }) => { + this.setData({ + detail, + }); + }); + }, + + navGoodListHandle() { + wx.navigateTo({ + url: `/pages/coupon/coupon-activity-goods/index?id=${this.id}`, + }); + }, +}); diff --git a/miniprogram/tcb-shop/pages/coupon/coupon-detail/index.json b/miniprogram/tcb-shop/pages/coupon/coupon-detail/index.json new file mode 100644 index 0000000..d419a0b --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/coupon-detail/index.json @@ -0,0 +1,10 @@ +{ + "navigationBarTitleText": "优惠券详情", + "usingComponents": { + "coupon-card": "../components/coupon-card/index", + "t-cell": "tdesign-miniprogram/cell/cell", + "t-cell-group": "tdesign-miniprogram/cell-group/cell-group", + "t-button": "tdesign-miniprogram/button/button", + "t-icon": "tdesign-miniprogram/icon/icon" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/coupon/coupon-detail/index.wxml b/miniprogram/tcb-shop/pages/coupon/coupon-detail/index.wxml new file mode 100644 index 0000000..8c447c2 --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/coupon-detail/index.wxml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + 查看可用商品 + + diff --git a/miniprogram/tcb-shop/pages/coupon/coupon-detail/index.wxss b/miniprogram/tcb-shop/pages/coupon/coupon-detail/index.wxss new file mode 100644 index 0000000..8fb1fe7 --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/coupon-detail/index.wxss @@ -0,0 +1,91 @@ +page { + background-color: #f5f5f5; +} + +.coupon-card-wrap { + background-color: #fff; + padding: 32rpx 32rpx 1rpx; +} +.desc-wrap { + margin-top: 24rpx; +} +.desc-wrap .button-wrap { + margin: 50rpx 32rpx 0; +} + +.desc-group-wrap .t-class-cell { + align-items: flex-start; +} + +.desc-group-wrap .t-class-title { + font-size: 26rpx; + width: 140rpx; + flex: none; + color: #888; +} + +.desc-group-wrap .t-class-note { + font-size: 26rpx; + word-break: break-all; + white-space: pre-line; + justify-content: flex-start; + color: #333; +} + +.desc-group-wrap { + border-radius: 8rpx; + overflow: hidden; + + --cell-label-font-size: 26rpx; + --cell-label-line-height: 36rpx; + --cell-label-color: #999; +} + +.desc-group-wrap.in-popup { + border-radius: 0; + overflow: auto; + max-height: 828rpx; +} + +.desc-group-wrap .wr-cell__title { + color: #333; + font-size: 28rpx; +} + +/* .desc-group-wrap .max-width-cell { + overflow: hidden; +} */ + +/* .desc-group-wrap .signal-line-label { + word-break: keep-all; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.desc-group-wrap .multi-line-label { + word-break: break-all; + white-space: pre-line; +} */ + +.popup-content-wrap { + background-color: #fff; + border-top-left-radius: 20rpx; + border-top-right-radius: 20rpx; +} + +.popup-content-title { + font-size: 32rpx; + color: #333; + + text-align: center; + height: 104rpx; + line-height: 104rpx; + + position: relative; +} + +.popup-content-title .close-icon { + position: absolute; + top: 24rpx; + right: 24rpx; +} diff --git a/miniprogram/tcb-shop/pages/coupon/coupon-list/index.js b/miniprogram/tcb-shop/pages/coupon/coupon-list/index.js new file mode 100644 index 0000000..f3b8675 --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/coupon-list/index.js @@ -0,0 +1,77 @@ +import { fetchCouponList } from '../../../services/coupon/index'; + +Page({ + data: { + status: 0, + list: [ + { + text: '可使用', + key: 0, + }, + { + text: '已使用', + key: 1, + }, + { + text: '已失效', + key: 2, + }, + ], + + couponList: [], + }, + + onLoad() { + this.init(); + }, + + init() { + this.fetchList(); + }, + + fetchList(status = this.data.status) { + let statusInFetch = ''; + switch (Number(status)) { + case 0: { + statusInFetch = 'default'; + break; + } + case 1: { + statusInFetch = 'useless'; + break; + } + case 2: { + statusInFetch = 'disabled'; + break; + } + default: { + throw new Error(`unknown fetchStatus: ${statusInFetch}`); + } + } + fetchCouponList(statusInFetch).then((couponList) => { + this.setData({ couponList }); + }); + }, + + tabChange(e) { + const { value } = e.detail; + + this.setData({ status: value }); + this.fetchList(value); + }, + + goCouponCenterHandle() { + wx.showToast({ title: '去领券中心', icon: 'none' }); + }, + + onPullDownRefresh_() { + this.setData( + { + couponList: [], + }, + () => { + this.fetchList(); + }, + ); + }, +}); diff --git a/miniprogram/tcb-shop/pages/coupon/coupon-list/index.json b/miniprogram/tcb-shop/pages/coupon/coupon-list/index.json new file mode 100644 index 0000000..64b7e4b --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/coupon-list/index.json @@ -0,0 +1,10 @@ +{ + "navigationBarTitleText": "优惠券", + "usingComponents": { + "t-pull-down-refresh": "tdesign-miniprogram/pull-down-refresh/pull-down-refresh", + "t-tabs": "tdesign-miniprogram/tabs/tabs", + "t-tab-panel": "tdesign-miniprogram/tab-panel/tab-panel", + "t-icon": "tdesign-miniprogram/icon/icon", + "coupon-card": "../components/coupon-card/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/coupon/coupon-list/index.wxml b/miniprogram/tcb-shop/pages/coupon/coupon-list/index.wxml new file mode 100644 index 0000000..391fe7b --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/coupon-list/index.wxml @@ -0,0 +1,42 @@ + + + + + + + + + + + + 领券中心 + + + + + diff --git a/miniprogram/tcb-shop/pages/coupon/coupon-list/index.wxss b/miniprogram/tcb-shop/pages/coupon/coupon-list/index.wxss new file mode 100644 index 0000000..4e28302 --- /dev/null +++ b/miniprogram/tcb-shop/pages/coupon/coupon-list/index.wxss @@ -0,0 +1,78 @@ +page { + height: 100%; +} + +.tabs-external__inner { + height: 88rpx; + width: 100%; + line-height: 88rpx; + z-index: 100; +} +.tabs-external__inner { + font-size: 26rpx; + color: #333333; + position: fixed; + width: 100vw; + top: 0; + left: 0; +} + +.tabs-external__inner .tabs-external__track { + background: #fa4126 !important; +} + +.tabs-external__inner .tabs-external__item { + color: #666; +} + +.tabs-external__inner .tabs-external__active { + font-size: 28rpx; + color: #fa4126 !important; +} + +.tabs-external__inner.order-nav .order-nav-item .bottom-line { + bottom: 12rpx; +} + +.coupon-list-wrap { + margin-top: 32rpx; + margin-left: 32rpx; + margin-right: 32rpx; + overflow-y: auto; + padding-bottom: 100rpx; + padding-bottom: calc(constant(safe-area-inset-top) + 100rpx); + padding-bottom: calc(env(safe-area-inset-bottom) + 100rpx); + -webkit-overflow-scrolling: touch; +} + +.center-entry { + box-sizing: content-box; + border-top: 1rpx solid #dce0e4; + background-color: #fff; + position: fixed; + bottom: 0; + left: 0; + right: 0; + height: 100rpx; + padding-bottom: 0; + padding-bottom: constant(safe-area-inset-top); + padding-bottom: env(safe-area-inset-bottom); +} + +.center-entry-btn { + color: #fa4126; + font-size: 28rpx; + text-align: center; + line-height: 100rpx; + display: flex; + align-items: center; + justify-content: center; + height: 100rpx; +} + +.coupon-list-wrap .t-pull-down-refresh__bar { + background: #fff !important; +} +.t-class-indicator { + color: #b9b9b9 !important; +} diff --git a/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/README.md b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/README.md new file mode 100644 index 0000000..88d7b0b --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/README.md @@ -0,0 +1,95 @@ +# Sidebar 侧边导航 + +### 引入 + +全局引入,在miniprogram根目录下的`app.json`中配置,局部引入,在需要引入的页面或组件的`index.json`中配置。 + +```json +// app.json 或 index.json +"usingComponents": { + "wr-sidebar": "path/to/components/goods-category/wr-sidebar/index", + "wr-sidebar-item": "path/to/component/goods-category/wr-sidebar/wr-sidebar-item/index" +} +``` + +## 代码演示 + +### 基础用法 + +通过在`wr-sidebar`上设置`activeKey`属性来控制选中项 + +```html + + + + + +``` + +``` javascript +Page({ + data: { + activeKey: 0 + }, + + onChange(event) { + wx.showToast({ + icon: 'none', + title: `切换至第${event.detail}项` + }); + } +}); +``` + +### 提示气泡(暂未实现) + +设置`dot`属性后,会在右上角展示一个小红点。设置`info`属性后,会在右上角展示相应的徽标 + +```html + + + + + +``` + +## API + +### Sidebar Props + +| 参数 | 说明 | 类型 | 默认值 | 版本 | +|-----------|-----------|-----------|-------------|-------------| +| activeKey | 选中项的索引 | *string \| number* | `0` | - | + +### Sidebar Event + +| 事件名 | 说明 | 参数 | +|------|------|------| +| change | 切换选项时触发 | 当前选中选项的索引 | + +### Sidebar 外部样式类 + +| 类名 | 说明 | +|-----------|-----------| +| custom-class | 根节点样式类 | + +### SidebarItem Props + +| 参数 | 说明 | 类型 | 默认值 | 版本 | +|-----------|-----------|-----------|-------------|-------------| +| title | 内容 | *string* | `''` | - | +| disabled | 是否禁用 | | *boolean* | `false` | - | +| dot | 是否显示右上角小红点 | *boolean* | `false` | - | +| info | 提示消息 | *string \| number* | `''` | - | + +### SidebarItem Event + +| 事件名 | 说明 | 参数 | +|------|------|------| +| click | 点击徽章时触发 | 当前徽章的索引 | + +### SidebarItem 外部样式类 + +| 类名 | 说明 | +|-----------|-----------| +| custom-class | 根节点样式类 | diff --git a/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/c-sidebar-item/index.js b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/c-sidebar-item/index.js new file mode 100644 index 0000000..810d5e5 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/c-sidebar-item/index.js @@ -0,0 +1,51 @@ +Component({ + relations: { + '../../c-sidebar/index': { + type: 'ancestor', + linked(target) { + this.parent = target; + }, + }, + }, + + externalClasses: ['custom-class'], + properties: { + title: String, + disabled: Boolean, + }, + + data: { + topRightRadius: false, + bottomRightRadius: false, + }, + + methods: { + setActive(selected) { + return this.setData({ selected }); + }, + onClick() { + const { parent } = this; + + if (!parent || this.properties.disabled) { + return; + } + + const index = parent.children.indexOf(this); + + parent.setActive(index).then(() => { + this.triggerEvent('click', index); + parent.triggerEvent('change', { index }); + }); + }, + setTopRightRadius(val) { + return this.setData({ + topRightRadius: val, + }); + }, + setBottomRightRadius(val) { + return this.setData({ + bottomRightRadius: val, + }); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/c-sidebar-item/index.json b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/c-sidebar-item/index.json new file mode 100644 index 0000000..e8cfaaf --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/c-sidebar-item/index.json @@ -0,0 +1,4 @@ +{ + "component": true, + "usingComponents": {} +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/c-sidebar-item/index.wxml b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/c-sidebar-item/index.wxml new file mode 100644 index 0000000..435f1e9 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/c-sidebar-item/index.wxml @@ -0,0 +1,10 @@ + + + {{ title }} + + diff --git a/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/c-sidebar-item/index.wxss b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/c-sidebar-item/index.wxss new file mode 100644 index 0000000..28d50dc --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/c-sidebar-item/index.wxss @@ -0,0 +1,60 @@ +.c-sidebar-item { + display: flex; + justify-content: center; + text-align: center; + background-color: #f5f5f5; + color: #222427; + padding: 20rpx 0; + font-size: 26rpx; +} + +.c-sidebar-item.active { + position: relative; + background: white; +} + +.c-sidebar-item.active::before { + content: ''; + position: absolute; + width: 6rpx; + height: 48rpx; + background-color: #fa4126; + left: 0; + top: 50%; + transform: translate(0, -50%); + border-radius: 64rpx; +} + +.c-sidebar-item__text { + width: 136rpx; + height: 36rpx; + padding: 8rpx 0; + line-height: 36rpx; + text-align: center; + font-size: 28rpx; + color: #666666; +} + +.c-sidebar-item.active .c-sidebar-item__text { + background-color: white; + border-radius: 36rpx; + color: #fa4126; +} + +.text-overflow { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.top-right-radius { + border-top-right-radius: 16rpx; +} + +.bottom-right-radius { + border-bottom-right-radius: 16rpx; +} + +.c-sidebar-item-container { + background-color: white; +} diff --git a/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/index.js b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/index.js new file mode 100644 index 0000000..b057ad1 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/index.js @@ -0,0 +1,106 @@ +Component({ + relations: { + './c-sidebar-item/index': { + type: 'descendant', + linked(target) { + this.children.push(target); + this.setActive(this.properties.activeKey, true); + }, + unlinked(target) { + this.children = this.children.filter((item) => item !== target); + this.setActive(this.properties.activeKey, true); + }, + }, + }, + + externalClasses: ['custom-class'], + + properties: { + activeKey: { + type: Number, + value: 0, + }, + }, + observers: { + activeKey(newVal) { + this.setActive(newVal); + }, + }, + + created() { + this.children = []; + this.currentActive = -1; + this.topRightRadiusItemIndexs = []; + this.bottomRightRadiusItemIndexs = []; + }, + + methods: { + setActive(activeKey, isChildrenChange) { + const { + children, + currentActive, + topRightRadiusItemIndexs: preTopRightRadiusItemIndexs, + bottomRightRadiusItemIndexs: preBottomRightRadiusItemIndexs, + } = this; + + if (!children.length) { + return Promise.resolve(); + } + + if (activeKey === currentActive && !isChildrenChange) { + return Promise.resolve(); + } + + this.currentActive = activeKey; + this.topRightRadiusItemIndexs = this.getTopRightRadiusItemIndexs( + activeKey, + children, + ); + this.bottomRightRadiusItemIndexs = this.getBottomRightRadiusItemIndexs( + activeKey, + children, + ); + + const stack = []; // 任务列表,存放调用子组件的setActive后返回的一堆promise + + // 将旧的选中项改为false + if (currentActive !== activeKey && children[currentActive]) { + stack.push(children[currentActive].setActive(false)); + } + + // 将新的选中项改为true + if (children[activeKey]) { + stack.push(children[activeKey].setActive(true)); + } + + preTopRightRadiusItemIndexs.forEach((item) => { + stack.push(children[item].setTopRightRadius(false)); + }); + + preBottomRightRadiusItemIndexs.forEach((item) => { + stack.push(children[item].setBottomRightRadius(false)); + }); + + this.topRightRadiusItemIndexs.forEach((item) => { + stack.push(children[item].setTopRightRadius(true)); + }); + + this.bottomRightRadiusItemIndexs.forEach((item) => { + stack.push(children[item].setBottomRightRadius(true)); + }); + + return Promise.all(stack); + }, + getTopRightRadiusItemIndexs(activeKey, children) { + const { length } = children; + if (activeKey !== 0 && activeKey < length - 1) return [0, activeKey + 1]; + if (activeKey !== 0) return [0]; + if (activeKey < length - 1) return [activeKey + 1]; + return []; + }, + getBottomRightRadiusItemIndexs(activeKey) { + if (activeKey !== 0) return [activeKey - 1]; + return []; + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/index.json b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/index.json new file mode 100644 index 0000000..84ff738 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/index.json @@ -0,0 +1,4 @@ +{ + "component": true +} + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/index.wxml b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/index.wxml new file mode 100644 index 0000000..a1fe026 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/index.wxml @@ -0,0 +1,3 @@ + + + diff --git a/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/index.wxss b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/index.wxss new file mode 100644 index 0000000..95e59ff --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-sidebar/index.wxss @@ -0,0 +1,9 @@ +.c-sidebar { + width: 176rpx; + height: 100vh; +} +.c-sidebar::-webkit-scrollbar { + width: 0; + height: 0; + color: transparent; +} diff --git a/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/c-tabbar-more/index.js b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/c-tabbar-more/index.js new file mode 100644 index 0000000..2b6f95d --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/c-tabbar-more/index.js @@ -0,0 +1,37 @@ +Component({ + externalClasses: ['custom-class'], + + properties: { + tabList: Array, + }, + + data: { + unfolded: false, + boardMaxHeight: null, + }, + attached() { + wx.createSelectorQuery() + .in(this) + .select('.c-tabbar-more') + .boundingClientRect((rect) => { + this.setData({ boardMaxHeight: rect.height }); + }) + .exec(); + }, + + methods: { + changeFold() { + this.setData({ + unfolded: !this.data.unfolded, + }); + const { unfolded } = this.data; + this.triggerEvent('change', { unfolded }); + }, + + onSelect(event) { + const activeKey = event.currentTarget.dataset.index; + this.triggerEvent('select', activeKey); + this.changeFold(); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/c-tabbar-more/index.json b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/c-tabbar-more/index.json new file mode 100644 index 0000000..a89ef4d --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/c-tabbar-more/index.json @@ -0,0 +1,4 @@ +{ + "component": true, + "usingComponents": {} +} diff --git a/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/c-tabbar-more/index.wxml b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/c-tabbar-more/index.wxml new file mode 100644 index 0000000..0ea9ad4 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/c-tabbar-more/index.wxml @@ -0,0 +1,25 @@ + + + + + + + + + + {{ item.name }} + + + + + diff --git a/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/c-tabbar-more/index.wxss b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/c-tabbar-more/index.wxss new file mode 100644 index 0000000..d0d08b3 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/c-tabbar-more/index.wxss @@ -0,0 +1,63 @@ +.c-tabbar-more { + width: 100%; + height: calc(100% - var(--tabbar-height, 100rpx)); + position: absolute; + top: var(--tabbar-height, 100rpx); +} +.c-tabbar-more__btn { + position: absolute; + top: calc(0rpx - var(--tabbar-height, 100rpx)); + right: 0; + width: 80rpx; + height: var(--tabbar-height, 100rpx); + line-height: var(--tabbar-height, 100rpx); + background-color: var(--tabbar-background-color, white); + box-shadow: -20rpx 0 20rpx -10rpx var(--tabbar-background-color, white); + text-align: center; +} +.c-tabbar-more__btn .market { + font-size: 20rpx; +} +.t-tabbar-more__boardwrapper { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +} +.t-tabbar-more__mask { + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); +} +.c-tabbar-more__board { + position: absolute; + top: 0; + left: 0; + width: 100%; + max-height: 100%; +} +.c-tabbar-more__boardinner { + padding: 20rpx 0 20rpx 20rpx; + background-color: var(--tabbar-background-color, white); + display: flex; + flex-flow: row wrap; +} +.c-tabbar-more__item { + margin: 0 20rpx 20rpx 0; + flex: 0 0 calc((100% - 60rpx) / 3); + box-sizing: border-box; + padding: 0 10rpx; + border-radius: 30rpx; + height: 60rpx; + line-height: 60rpx; + text-align: center; + font-size: 22rpx; + color: #5d5d5d; + background-color: #eee; +} +.text-overflow { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} diff --git a/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/index.js b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/index.js new file mode 100644 index 0000000..ddf6367 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/index.js @@ -0,0 +1,68 @@ +Component({ + externalClasses: ['custom-class'], + + properties: { + activeKey: { + type: Number, + value: 0, + }, + tabList: { + type: Array, + value: [], + }, + showMore: Boolean, // 是否需要下拉功能 + }, + observers: { + activeKey(newVal) { + if (this.properties.tabList && newVal) { + this.setActive(newVal).catch((e) => { + console.error(e); + }); + } + }, + }, + + data: { + currentActive: -1, + }, + attached() { + this.setActive(this.properties.activeKey).catch((e) => { + console.error(e); + }); + }, + + methods: { + setActive(activeKey) { + if ( + !this.properties.tabList[activeKey] || + this.properties.tabList[activeKey].disabled + ) { + return Promise.reject('数据异常或不可操作'); + } + return new Promise((resolve) => { + this.setData( + { + currentActive: activeKey, + }, + () => resolve(), + ); + }); + }, + onClick(event) { + let activeKey; + if (event.type === 'select') { + activeKey = event.detail; + } else { + activeKey = event.currentTarget.dataset.index; + } + this.setActive(activeKey) + .then(() => { + const { currentActive } = this.data; + this.triggerEvent('change', { index: currentActive }); + }) + .catch((e) => { + console.error(e); + }); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/index.json b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/index.json new file mode 100644 index 0000000..644e632 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/index.json @@ -0,0 +1,6 @@ +{ + "component": true, + "usingComponents": { + "c-tabbar-more": "./c-tabbar-more/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/index.wxml b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/index.wxml new file mode 100644 index 0000000..1227f88 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/index.wxml @@ -0,0 +1,29 @@ + + + + + {{ item.name }} + + + + + + diff --git a/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/index.wxss b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/index.wxss new file mode 100644 index 0000000..d89475f --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/components/c-tabbar/index.wxss @@ -0,0 +1,53 @@ +.c-tabbar { + width: 100%; + height: 100%; + position: relative; + --tabbar-height: 100rpx; + --tabbar-fontsize: 28rpx; + --tabbar-background-color: white; +} +.c-tabbar__inner { + display: flex; + flex-flow: row nowrap; +} +.c-tabbar__scroll { + position: relative; +} +.c-tabbar__scroll::after { + content: ''; + display: block; + position: absolute; + width: 100%; + left: 0; + bottom: -1px; + height: 1px; + background-color: #eee; + z-index: 1; +} +.c-tabbar__inner.c-tabbar__inner_more::after { + content: ''; + display: block; + width: 100rpx; + height: 100rpx; + flex: none; +} +.c-tabbar-item { + flex: none; + height: 100rpx; + color: #282828; + font-size: 28rpx; + padding: 0 20rpx; +} +.c-tabbar-item.active:not(.disabled) { + color: #0071ce; + position: relative; +} +.c-tabbar-item.disabled { + color: #ccc; +} +.c-tabbar-item__text { + width: 100%; + text-align: center; + height: 100rpx; + line-height: 100rpx; +} diff --git a/miniprogram/tcb-shop/pages/goods/category/components/goods-category/index.js b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/index.js new file mode 100644 index 0000000..8ea282f --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/index.js @@ -0,0 +1,69 @@ +Component({ + externalClasses: ['custom-class'], + + properties: { + category: { + type: Array, + }, + initActive: { + type: Array, + value: [], + observer(newVal, oldVal) { + if (newVal[0] !== oldVal[0]) { + this.setActiveKey(newVal[0], 0); + } + }, + }, + isSlotRight: { + type: Boolean, + value: false, + }, + level: { + type: Number, + value: 2, + }, + }, + data: { + activeKey: 0, + subActiveKey: 0, + }, + attached() { + if (this.properties.initActive && this.properties.initActive.length > 0) { + this.setData({ + activeKey: this.properties.initActive[0], + subActiveKey: this.properties.initActive[1] || 0, + }); + } + }, + methods: { + onParentChange(event) { + this.setActiveKey(event.detail.index, 0).then(() => { + this.triggerEvent('change', [this.data.activeKey, this.data.subActiveKey]); + }); + }, + onChildChange(event) { + this.setActiveKey(this.data.activeKey, event.detail.index).then(() => { + this.triggerEvent('change', [this.data.activeKey, this.data.subActiveKey]); + }); + }, + changCategory(event) { + const { item } = event.currentTarget.dataset; + this.triggerEvent('changeCategory', { + item, + }); + }, + setActiveKey(key, subKey) { + return new Promise((resolve) => { + this.setData( + { + activeKey: key, + subActiveKey: subKey, + }, + () => { + resolve(); + }, + ); + }); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/goods/category/components/goods-category/index.json b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/index.json new file mode 100644 index 0000000..8ca3d3c --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/index.json @@ -0,0 +1,9 @@ +{ + "component": true, + "usingComponents": { + "c-tabbar": "./components/c-tabbar/index", + "c-sidebar": "./components/c-sidebar/index", + "c-sidebar-item": "./components/c-sidebar/c-sidebar-item/index", + "t-image": "/components/webp-image/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/category/components/goods-category/index.wxml b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/index.wxml new file mode 100644 index 0000000..c183ff9 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/index.wxml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + {{item.name}} + + + + + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/category/components/goods-category/index.wxss b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/index.wxss new file mode 100644 index 0000000..fbf4569 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/components/goods-category/index.wxss @@ -0,0 +1,102 @@ +.goods-category { + display: flex; +} + +.custom-sidebar { + height: 100%; +} + +.goods-category__right { + height: 100%; + flex: auto; + width: 0; + position: relative; + overflow: scroll; + -webkit-overflow-scrolling: touch; + background-color: white; +} + +.flex { + display: flex; +} + +.goods-category-normal { + margin: 28rpx 34rpx 0rpx 32rpx; +} + +.goods-category-normal-item-title { + font-size: 28rpx; + font-weight: 500; +} + +.goods-category-normal-item-container { + background-color: #fff; + border-radius: 8rpx; + padding-top: 28rpx; + margin-top: -24rpx; + margin-bottom: 30rpx; + display: flex; + flex-wrap: wrap; +} + +.goods-category-normal-item-container-item { + height: 196rpx; + display: flex; + flex-direction: column; + align-items: center; + margin-top: 24rpx; + width: 33.3%; +} + +.goods-category-normal-item-container-item .image { + width: 144rpx; + height: 144rpx; +} + +.goods-category-normal-item-container-item-title { + justify-content: center; + font-size: 24rpx; + color: #666666; + margin-top: 20rpx; +} + +.goods-category .custom-sidebar { + background-color: #f5f5f5; +} + +.custom-sidebar { + width: 180rpx; + height: 100vh; +} + +.custom-sidebar::-webkit-scrollbar { + width: 0; + height: 0; + color: transparent; +} + +.goods-category-normal-item-second-container { + background-color: #fff; + border-radius: 8rpx; + margin-top: 8rpx; + margin-bottom: 30rpx; + display: grid; + grid-template-columns: 33.33% 33.33% 33.33%; +} + +.goods-category-normal-item-second-container-item { + height: 200rpx; + text-align: center; + margin-top: 20rpx; +} + +.goods-category-normal-item-second-container-item .image { + width: 144rpx; + height: 144rpx; +} + +.goods-category-normal-item-second-container-item-title { + justify-content: center; + font-size: 24rpx; + color: #222427; +} diff --git a/miniprogram/tcb-shop/pages/goods/category/index.js b/miniprogram/tcb-shop/pages/goods/category/index.js new file mode 100644 index 0000000..b7867d7 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/index.js @@ -0,0 +1,36 @@ +import Toast from 'tdesign-miniprogram/toast/index'; +import { getCates } from '../../../services/cate/cate'; + +Page({ + data: { + cates: [], + }, + async init() { + try { + const cates = await getCates(); + this.setData({ cates }); + } catch (e) { + console.error('获取商品分类列表失败', e); + Toast({ + context: this, + selector: '#t-toast', + message: '获取商品分类列表失败', + duration: 1000, + icon: '', + }); + } + }, + + onShow() { + this.getTabBar().init(); + }, + onChange(e) { + const cateId = e?.detail?.item?._id; + wx.navigateTo({ + url: `/pages/goods/list/index?cateId=${cateId}`, + }); + }, + onLoad() { + this.init(true); + }, +}); diff --git a/miniprogram/tcb-shop/pages/goods/category/index.json b/miniprogram/tcb-shop/pages/goods/category/index.json new file mode 100644 index 0000000..0bb1263 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/index.json @@ -0,0 +1,7 @@ +{ + "navigationBarTitleText": "分类", + "usingComponents": { + "goods-category": "./components/goods-category/index", + "t-toast": "tdesign-miniprogram/toast/toast" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/category/index.wxml b/miniprogram/tcb-shop/pages/goods/category/index.wxml new file mode 100644 index 0000000..cdfd458 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/index.wxml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/category/index.wxss b/miniprogram/tcb-shop/pages/goods/category/index.wxss new file mode 100644 index 0000000..ac2e0a7 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/category/index.wxss @@ -0,0 +1,23 @@ +.tabbar-position { + position: fixed !important; + bottom: 0; + left: 0; + width: 100%; +} + +.wrap { + height: 100vh; + overflow: hidden; +} +.goods-category-class { + background-color: #f6f6f6 !important; + height: 100%; +} +.goods-category-class .goods-category-normal-item-container-item { + margin-top: 20rpx; +} + +page { + min-height: none; + padding-bottom: 0; +} diff --git a/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/images-videos/assets/play.png b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/images-videos/assets/play.png new file mode 100644 index 0000000000000000000000000000000000000000..d816ef7fd2ae06456b02f08708b3c9db94fbe48d GIT binary patch literal 1326 zcmV+}1=0G6P)&KvET1}FFj+7Uhd^`KX}~ba?$U1-|xNm{W%XQz{A6X#s@zSFArTD zH8CVoVkE^#cs!mI*@TA*gfKe5!-sKrct~ewXB3ae>E`B!uCK2%_f}L?P-SH$)z;Qh zb#*n>)zwi`Qxm=dkr>$^W0`=-@BkaFMWa#L+1a7n+gnS22Sh-kqoYHTIpz_yO2iTY zHaCct8J-OgSzKJi5nlN5 z7H_;tfQ@h9!{_z&bq&Tlf#Km{8X6i(^XV@p5zlvTUILH|YdbhIGecWjTjV5KT3Tpg zVgi#tucTfiAdSbI79r(lct$`>*wEzUWJ!&e zfDMo^W77;!TXMgu(4nX(3g9bZ0JIRgM*x10lcrqU{ z9uV#^bnjM(Wjr9F0Rv_U1>q`l4C6Ln14R@-kD-hKgMfJY4G0h14q;=)3(^BdG->zg z_w@9<9~c<;Kt)8h7v72M0YsE`FfuaovA@4RgmXToB9TaRc6RoJiU35w9Krfaetu3)LC%lO z&CRcNWQDw&ga>qJ%4yWp)c7zz=I7@>Q2`LDF($F)gj^+>o0|jO-Q7Ma3|bkv0J^Sg zR3JbgO(n#^!9k3r6u0_2)R_S6#A&VqXif)lmzS3b-l_lau9~C*fMOXV1p>*csw(BX zhu-PCGM{N?5LiKVAov*I0{#EhB&5(6fMxu=KOFWeXimZ09zrTMf z&!O|RwY5kn6k1hnJkZ$Kh(i*kMB=i>B}C1yp|y# zy)ZU5Ms5YPPgz}@C&3&lUcA00M@L7=ZNLJ9#z%IQI#)FMv#+l&EnMXapjRLo9zU|b zdqzOG=QpI}M2PVku;Zf9L66;_IRdDs{nDIYAPnHt)mYU`FMA}!uxz+(0%n-Vcu00yH+vj1Bcpa1{>07*qoM6N<$f+S8^r~m)} literal 0 HcmV?d00001 diff --git a/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/images-videos/index.js b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/images-videos/index.js new file mode 100644 index 0000000..a0a3c48 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/images-videos/index.js @@ -0,0 +1,36 @@ +// pages/goods/comments/components/comments-card/images-videos/index.js +Component({ + /** + * 组件的属性列表 + */ + properties: { + resources: { + type: Array, + value: [], + }, + }, + + /** + * 组件的初始数据 + */ + data: { + classType: 'single', + }, + + observers: { + resources: function (newVal) { + if (newVal.length <= 1) { + this.setData({ classType: 'single' }); + } else if (newVal.length === 2) { + this.setData({ classType: 'double' }); + } else { + this.setData({ classType: 'multiple' }); + } + }, + }, + + /** + * 组件的方法列表 + */ + methods: {}, +}); diff --git a/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/images-videos/index.json b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/images-videos/index.json new file mode 100644 index 0000000..83597fd --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/images-videos/index.json @@ -0,0 +1,7 @@ +{ + "component": true, + "usingComponents": { + "my-video": "../my-video/index", + "t-image": "/components/webp-image/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/images-videos/index.wxml b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/images-videos/index.wxml new file mode 100644 index 0000000..2515909 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/images-videos/index.wxml @@ -0,0 +1,15 @@ + + + + + + + + + + diff --git a/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/images-videos/index.wxss b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/images-videos/index.wxss new file mode 100644 index 0000000..e7944be --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/images-videos/index.wxss @@ -0,0 +1,68 @@ +.resource-item-single { + width: 360rpx; + height: 360rpx; + border-radius: 8rpx; +} + +.resource-item-double { + width: 334rpx; + height: 334rpx; + border-radius: 8rpx; +} + +.resource-item-multiple { + width: 218rpx; + height: 218rpx; + border-radius: 8rpx; +} + +.resource-container-single { + padding-left: 0; + padding-top: 0; +} + +.resource-container-double { + padding-left: 18rpx; + padding-top: 18rpx; +} + +.resource-container-multiple { + padding-left: 16rpx; + padding-top: 16rpx; +} + +.container-single { + margin-left: 0; +} + +.container-double { + margin-left: -18rpx; + margin-top: -18rpx; +} + +.container-multiple { + margin-left: -16rpx; + margin-top: -16rpx; +} + +.resource-container { + display: flex; +} + +.play-icon { + width: 96rpx; + height: 96rpx; +} + +.images-videos-container { + display: flex; + flex-wrap: wrap; +} + +.image { + border-radius: 8rpx; +} + +.cover-img-container { + background-color: white; +} diff --git a/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/my-video/index.js b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/my-video/index.js new file mode 100644 index 0000000..987cf18 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/my-video/index.js @@ -0,0 +1,55 @@ +Component({ + externalClasses: ['my-video', 'my-cover-img', 'my-play-icon'], + properties: { + videoSrc: { type: String }, + }, + data: { + isShow: true, + }, + + options: { + multipleSlots: true, // 在组件定义时的选项中启用多slot支持 + }, + + attached() { + this.videoContext = wx.createVideoContext('myVideo', this); + }, + + fullScreen: false, + + methods: { + // 点击封面自定义播放按钮时触发 + bindplay(e) { + this.setData({ + isShow: false, + }); + this.videoContext.play(); + this.triggerEvent('play', e); + }, + + bindplayByVideo(e) { + this.setData({ + isShow: false, + }); + this.triggerEvent('play', e); + }, + + // 监听播放到末尾时触发 + bindended(e) { + if (!this.fullScreen) { + this.setData({ + isShow: true, + }); + } + this.triggerEvent('ended', e); + }, + // 监听暂停播放时触发 + bindpause(e) { + this.triggerEvent('pause', e); + }, + bindfullscreenchange(e) { + const fullScreen = e?.detail?.fullScreen; + this.fullScreen = fullScreen; + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/my-video/index.json b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/my-video/index.json new file mode 100644 index 0000000..e8cfaaf --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/my-video/index.json @@ -0,0 +1,4 @@ +{ + "component": true, + "usingComponents": {} +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/my-video/index.wxml b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/my-video/index.wxml new file mode 100644 index 0000000..af38919 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/my-video/index.wxml @@ -0,0 +1,26 @@ + diff --git a/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/my-video/index.wxss b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/my-video/index.wxss new file mode 100644 index 0000000..63b962b --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/components/my-video/index.wxss @@ -0,0 +1,21 @@ +.video .video_cover { + width: 100%; + height: 100%; + position: relative; +} + +.video .video_play_icon { + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + z-index: 5; +} + +.video .video_txt { + margin: 10rpx auto; +} + +.video { + display: flex; +} diff --git a/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/index.js b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/index.js new file mode 100644 index 0000000..53f6103 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/index.js @@ -0,0 +1,51 @@ +Component({ + externalClasses: ['wr-class'], + options: { + multipleSlots: true, + }, + properties: { + goodsDetailInfo: { + type: String, + value: '', + }, + sellerReply: { + type: String, + value: '', + }, + userHeadUrl: { + type: String, + value: '', + }, + userName: { + type: String, + default: '', + }, + commentContent: { + type: String, + value: '', + }, + commentScore: { + type: Number, + value: 0, + }, + commentTime: { + type: String, + value: '', + }, + commentResources: { + type: Array, + value: [], + }, + }, + + data: { + showMoreStatus: false, + showContent: false, + hideText: false, + eleHeight: null, + overText: false, + isDisabled: true, + startColors: ['#FFC51C', '#DDDDDD'], + }, + methods: {}, +}); diff --git a/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/index.json b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/index.json new file mode 100644 index 0000000..1130655 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/index.json @@ -0,0 +1,8 @@ +{ + "component": true, + "usingComponents": { + "t-rate": "tdesign-miniprogram/rate/rate", + "images-videos": "./components/images-videos", + "t-image": "/components/webp-image/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/index.wxml b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/index.wxml new file mode 100644 index 0000000..af86507 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/index.wxml @@ -0,0 +1,27 @@ + + + + + + + {{userName}} + {{commentTime}} + + + + + + {{goodsDetailInfo}} + + + + + + + + + 店家回复: + {{sellerReply}} + + + diff --git a/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/index.wxss b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/index.wxss new file mode 100644 index 0000000..c5e0564 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/components/comments-card/index.wxss @@ -0,0 +1,172 @@ +@import '../../../../../style/theme.wxss'; + +.comments-card-item { + padding: 32rpx; + display: flex; + background-color: #fff; + position: relative; +} + +.comments-card-item::after { + content: ''; + position: absolute; + bottom: 0rpx; + width: 686rpx; + height: 2rpx; + background-color: #f5f5f5; +} + +.comments-card-item-userImg { + display: flex; +} + +.comments-card-item-userImg .userImg { + width: 64rpx; + height: 64rpx; + border-radius: 50%; +} + +.comments-card-item-container { + width: 100%; +} + +.comments-card-item-container-name { + display: flex; + font-size: 28rpx; + color: #333; + font-weight: 600; + align-items: center; +} + +.comments-card-item-container-name .userName { + margin-right: 12rpx; +} + +.comments-card-item-container-date { + font-size: 22rpx; + color: #999; + margin-top: 4rpx; + display: flex; +} + +.comments-card-item-container-content { + margin-top: 16rpx; + position: relative; +} + +.comments-card-item-container-content .content-text { + font-size: 28rpx; + white-space: normal; + word-break: break-all; + font-weight: normal; +} + +.comments-card-item-container-content .hide-text { + overflow: hidden; + text-overflow: ellipsis; + -webkit-line-clamp: 5; + text-align: justify; + display: -webkit-box; + -webkit-box-orient: vertical; +} + +.comments-card-item-container-content .showMore { + position: absolute; + width: 112rpx; + height: 36rpx; + bottom: 0; + right: 0; + background: linear-gradient( + to right, + rgba(255, 255, 255, 0.2) 0, + rgba(255, 255, 255, 0.45) 20%, + rgba(255, 255, 255, 0.7) 25%, + rgba(255, 255, 255, 0.9) 30%, + rgba(255, 255, 255, 0.95) 35%, + #ffffff 50%, + #fff 100% + ); + font-size: 26rpx; + color: #fa550f; + line-height: 36rpx; + text-align: right; +} + +.comments-card-item-container-image { + margin-top: 24rpx; + display: flex; + justify-content: space-between; + flex-wrap: wrap; +} + +.comments-card-item-container-image .commentImg { + border-radius: 8rpx; + margin-top: 12rpx; +} + +.comments-card-item-container-image .commentImg3 { + width: 196rpx; + height: 196rpx; +} + +.comments-card-item-container-image .commentImg2 { + width: 300rpx; + height: 300rpx; +} + +.comments-card-item-container-image .commentImg1 { + width: 404rpx; + height: 404rpx; +} + +.comments-card-item-container .comments-title { + display: flex; + align-items: center; + position: relative; +} + +.comments-title .userName { + font-size: 26rpx; + color: #333333; + margin-left: 24rpx; +} + +.comments-title .commentTime { + font-size: 24rpx; + color: #999999; + position: absolute; + right: 0; +} + +.comments-info { + display: flex; + align-items: center; + margin-top: 18rpx; +} + +.comments-info .rate { + margin-right: 24rpx; +} + +.comments-info .goods-info-text { + font-size: 24rpx; + + color: #999999; +} + +.comments-card-item-container .comments-card-reply { + background-color: #f5f5f5; + padding: 24rpx 16rpx; + margin-top: 24rpx; +} + +.comments-card-item-container .comments-card-reply .prefix { + font-size: 26rpx; + font-weight: bold; + color: #666666; +} + +.comments-card-item-container .comments-card-reply .content { + font-size: 26rpx; + color: #666666; +} diff --git a/miniprogram/tcb-shop/pages/goods/comments/create-list/index.js b/miniprogram/tcb-shop/pages/goods/comments/create-list/index.js new file mode 100644 index 0000000..922ec2c --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/create-list/index.js @@ -0,0 +1,56 @@ +import Toast from 'tdesign-miniprogram/toast/index'; +import { getOrder } from '../../../../services/order/order'; +import { getAllOrderItemsOfAnOrder } from '../../../../services/order/orderItem'; +import { getCloudImageTempUrl } from '../../../../utils/cloudImageHandler'; + +Page({ + data: { + order: null, + }, + + async onLoad(options) { + const orderId = options?.orderId; + if (typeof orderId !== 'string') { + this.failedAndBack('获取订单详情失败'); + return; + } + + try { + const [order, items] = await Promise.all([getOrder(orderId), getAllOrderItemsOfAnOrder({ orderId })]); + + const images = items.map((x) => x.sku.image ?? ''); + try { + const handleImages = await getCloudImageTempUrl(images); + handleImages.forEach((image, index) => (items[index].sku.image = image)); + } catch (e) { + console.error('处理商品图片失败', e); + } + order.orderItems = items; + this.setData({ order }); + } catch (e) { + this.failedAndBack('获取订单详情失败', e); + } + }, + + failedAndBack(message, e) { + e && console.error(e); + Toast({ + context: this, + selector: '#t-toast', + message, + }); + setTimeout(() => wx.navigateBack(), 1000); + }, + + toComment(e) { + const orderItemId = e?.target?.dataset?.orderItem?._id; + if (typeof orderItemId !== 'string') { + this.failedAndBack('获取订单详情失败'); + return; + } + + wx.navigateTo({ + url: `/pages/goods/comments/create/index?orderItemId=${orderItemId}`, + }); + }, +}); diff --git a/miniprogram/tcb-shop/pages/goods/comments/create-list/index.json b/miniprogram/tcb-shop/pages/goods/comments/create-list/index.json new file mode 100644 index 0000000..b594baf --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/create-list/index.json @@ -0,0 +1,9 @@ +{ + "navigationBarTitleText": "选择评价商品", + "usingComponents": { + "order-card": "../../../order/components/order-card", + "specs-goods-card": "../../../order/components/specs-goods-card/index", + "t-button": "tdesign-miniprogram/button/button", + "t-toast": "tdesign-miniprogram/toast/toast" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/comments/create-list/index.wxml b/miniprogram/tcb-shop/pages/goods/comments/create-list/index.wxml new file mode 100644 index 0000000..c2e2a2a --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/create-list/index.wxml @@ -0,0 +1,22 @@ + + + + + 订单号  + {{order._id}} + + + + + + + 评价 + + + + + + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/comments/create-list/index.wxss b/miniprogram/tcb-shop/pages/goods/comments/create-list/index.wxss new file mode 100644 index 0000000..dc90d1f --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/create-list/index.wxss @@ -0,0 +1,54 @@ +:host { + width: 100%; +} +.btn-bar { + display: flex; + justify-content: flex-end; + align-items: center; + line-height: 1; +} +.btn-bar .order-btn { + line-height: 1; + /* border-radius: unset; */ + /* min-width: 160rpx; */ +} + +.btn-bar .right { + display: flex; + align-items: center; +} +.btn-bar .t-button { + width: 160rpx; + font-weight: 400; + margin-left: 24rpx; +} +.btn-bar .t-button--max { + width: 176rpx; + margin-left: 24rpx; + + --td-button-extra-small-height: 72rpx; +} + +.btn-bar .left .delete-btn { + font-size: 22rpx; +} +.btn-bar .left .delete-btn::after { + display: none; +} + +.btn-bar .right .normal { + --td-button-default-color: #333333; + --td-button-default-border-color: #dddddd; +} + +.btn-bar .right .primary { + --td-button-default-color: #fff; + --td-button-default-bg-color: #fa4126; + --td-button-default-border-color: #fa4126; + --td-button-default-active-bg-color: #fa42269c; +} + +.t-button { + --td-button-default-color: #000; + --td-button-primary-text-color: #fa4126; +} diff --git a/miniprogram/tcb-shop/pages/goods/comments/create/index.js b/miniprogram/tcb-shop/pages/goods/comments/create/index.js new file mode 100644 index 0000000..7c3e081 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/create/index.js @@ -0,0 +1,114 @@ +import Toast from 'tdesign-miniprogram/toast/index'; +import { getOrderItem } from '../../../../services/order/orderItem'; +import { getSkuDetail } from '../../../../services/sku/sku'; +import { getCloudImageTempUrl } from '../../../../utils/cloudImageHandler'; +import { createComment } from '../../../../services/comments/comments'; + +Page({ + data: { + sku: null, + }, + + toast(message) { + Toast({ + context: this, + selector: '#t-toast', + message, + }); + }, + + failedAndBack(message, e) { + e && console.error(e); + this.toast(message); + setTimeout(() => { + wx.navigateBack(); + }, 1000); + }, + + async onLoad(options) { + const orderItemId = options.orderItemId; + if (typeof orderItemId !== 'string') return this.failedAndBack('获取订单失败'); + + const orderItem = await getOrderItem(orderItemId); + + const comment = orderItem?.comment; + if (comment != null) return this.failedAndBack('已经评价过了'); + + let sku = orderItem?.sku; + if (sku == null) return this.failedAndBack('获取商品信息失败'); + + try { + sku = await getSkuDetail(sku._id); + sku.image = (await getCloudImageTempUrl([sku.image ?? '']))[0]; + sku.spec = sku.attr_value.map((x) => x.value).join(','); + this.setData({ sku, orderItemId }); + } catch (e) { + return this.failedAndBack('获取商品信息失败', e); + } + }, + + onRateChange(e) { + const { value } = e?.detail; + const item = e?.currentTarget?.dataset?.item; + this.setData({ [item]: value }, () => { + this.updateButtonStatus(); + }); + }, + + onAnonymousChange(e) { + const status = !!e?.detail?.checked; + this.setData({ isAnonymous: status }); + }, + + handleSuccess(e) { + const { files } = e.detail; + + this.setData({ + uploadFiles: files, + }); + }, + + handleRemove(e) { + const { index } = e.detail; + const { uploadFiles } = this.data; + uploadFiles.splice(index, 1); + this.setData({ + uploadFiles, + }); + }, + + onTextAreaChange(e) { + const value = e?.detail?.value; + this.textAreaValue = value; + this.updateButtonStatus(); + }, + + updateButtonStatus() { + const { goodRateValue, isAllowedSubmit } = this.data; + const { textAreaValue } = this; + const temp = Boolean(goodRateValue) && Boolean(textAreaValue); + if (temp !== isAllowedSubmit) this.setData({ isAllowedSubmit: temp }); + }, + + async onSubmitBtnClick() { + const { goodRateValue, isAllowedSubmit, orderItemId, sku } = this.data; + const { textAreaValue } = this; + + if (!isAllowedSubmit) return; + + try { + await createComment({ orderItemId, content: textAreaValue, rating: goodRateValue, spuId: sku.spu._id }); + Toast({ + context: this, + selector: '#t-toast', + message: '评价提交成功', + icon: 'check-circle', + }); + setTimeout(() => { + wx.navigateBack(); + }, 1000); + } catch (e) { + return this.failedAndBack('创建评论失败', e); + } + }, +}); diff --git a/miniprogram/tcb-shop/pages/goods/comments/create/index.json b/miniprogram/tcb-shop/pages/goods/comments/create/index.json new file mode 100644 index 0000000..7c6d3eb --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/create/index.json @@ -0,0 +1,13 @@ +{ + "navigationBarTitleText": "评价商品", + "usingComponents": { + "t-image": "/components/webp-image/index", + "t-rate": "tdesign-miniprogram/rate/rate", + "t-textarea": "tdesign-miniprogram/textarea/textarea", + "t-checkbox": "tdesign-miniprogram/checkbox/checkbox", + "t-button": "tdesign-miniprogram/button/button", + "t-upload": "tdesign-miniprogram/upload/upload", + "t-icon": "tdesign-miniprogram/icon/icon", + "t-toast": "tdesign-miniprogram/toast/toast" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/comments/create/index.wxml b/miniprogram/tcb-shop/pages/goods/comments/create/index.wxml new file mode 100644 index 0000000..2bd1cc8 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/create/index.wxml @@ -0,0 +1,26 @@ + + + + + + + + {{sku.spu.name}} + {{sku.spec}} + + + + 商品评价 + + + + + + + + + + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/comments/create/index.wxss b/miniprogram/tcb-shop/pages/goods/comments/create/index.wxss new file mode 100644 index 0000000..abf20e4 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/create/index.wxss @@ -0,0 +1,168 @@ +page { + background-color: #f5f5f5; +} + +.page-container .comment-card { + padding: 24rpx 32rpx 28rpx; + background-color: #ffffff; +} + +.comment-card .goods-info-container .goods-image { + width: 112rpx; + height: 112rpx; + border-radius: 8rpx; +} + +.comment-card .goods-info-container { + display: flex; + align-items: center; +} + +.comment-card .goods-info-container .goods-title-container { + padding-left: 24rpx; +} + +.comment-card .goods-info-container .goods-title { + font-size: 28rpx; + font-weight: normal; +} + +.comment-card .goods-info-container .goods-detail { + font-size: 24rpx; + font-weight: normal; + color: #999999; + margin-top: 16rpx; +} + +.comment-card .rate-container { + display: flex; + align-items: center; + margin-top: 22rpx; +} + +.comment-card .rate-container .rate-title { + font-size: 28rpx; + font-weight: bold; + margin-right: 12rpx; +} + +.comment-card .textarea-container { + margin-top: 22rpx; +} + +.comment-card .textarea-container .textarea { + height: 294rpx; + background-color: #f5f5f5; + border-radius: 16rpx; + font-size: 28rpx; + font-weight: normal; +} + +.page-container .t-checkbox__bordered { + display: none; +} + +.page-container .anonymous-box { + display: flex; + align-items: center; + padding-top: 52rpx; +} + +.page-container .anonymous-box .name { + font-size: 28rpx; + font-weight: normal; + color: #999999; + padding-left: 28rpx; +} + +.page-container .t-checkbox { + padding: 0rpx !important; +} + +.page-container .t-checkbox__content { + display: none; +} + +.comment-card .convey-comment-title { + font-size: 28rpx; + font-weight: bold; +} + +.convey-card { + background-color: #ffffff; + margin-top: 24rpx; + padding: 32rpx; + padding-bottom: calc(env(safe-area-inset-bottom) + 140rpx); +} + +.convey-card .rate-container .rate-title { + font-weight: normal; +} + +.page-container .t-checkbox__icon-left { + margin-right: 0rpx !important; +} + +.submit-button-container { + padding: 12rpx 32rpx; + display: flex; + width: 100vw; + box-sizing: border-box; + justify-content: center; + position: fixed; + bottom: 0; + padding-bottom: calc(env(safe-area-inset-bottom) + 20rpx); + background-color: #ffffff; + z-index: 99; +} + +.submit-button-container .submit-button { + --td-button-default-color: #fff; + --td-button-default-bg-color: #fa4126; + --td-button-default-border-color: #fa4126; + --td-button-default-active-bg-color: #fa42269c; +} + +.submit-button-container .submit-button-disabled { + --td-button-default-color: #fff; + --td-button-default-bg-color: #ccc; + --td-button-default-border-color: #ccc; + --td-button-default-active-bg-color: rgba(204, 204, 204, 0.789); +} + +.page-container .upload-container { + margin-top: 24rpx; +} + +.page-container .t-upload__wrapper { + border-radius: 8rpx; + overflow: hidden; +} + +.page-container .submmit-bar { + position: fixed; + left: 0; + right: 0; + bottom: 0; + z-index: 12; + padding: 12rpx 32rpx; + padding-bottom: env(safe-area-inset-bottom); + background-color: #fff; + height: 112rpx; +} + +.page-container .submmit-bar-button { + border-radius: 48rpx !important; + padding: 0 !important; +} + +.page-container .t-upload__close-btn { + background-color: rgba(0, 0, 0, 0.4); + border-bottom-left-radius: 8rpx; + width: 36rpx; + height: 36rpx; +} + +.upload-container .upload-addcontent-slot { + font-size: 26rpx; +} diff --git a/miniprogram/tcb-shop/pages/goods/comments/index.js b/miniprogram/tcb-shop/pages/goods/comments/index.js new file mode 100644 index 0000000..4171e0a --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/index.js @@ -0,0 +1,71 @@ +import { getSkuDetail } from '../../../services/sku/sku'; +import { getOrderItem } from '../../../services/order/orderItem'; +import { getCommentsOfSpu } from '../../../services/comments/comments'; +import { LIST_LOADING_STATUS } from '../../../utils/listLoading'; +import dayjs from 'dayjs'; + +const layoutMap = { + 0: 'vertical', +}; +Page({ + data: { + commentList: [], + layoutText: layoutMap[0], + loadMoreStatus: 0, + spuId: null, + }, + + pageNumber: 1, + pageSize: 10, + + async loadComments(refresh = false) { + refresh && (this.pageNumber = 1); + + this.setData({ loadMoreStatus: LIST_LOADING_STATUS.LOADING }); + + const { spuId } = this.data; + const { pageNumber, pageSize } = this; + try { + const { records: newComments, total } = await getCommentsOfSpu({ spuId, pageNumber, pageSize }); + await Promise.all( + newComments.map(async (x) => { + const orderItemId = x.order_item._id; + const { + sku: { _id: skuId }, + } = await getOrderItem(orderItemId); + const sku = await getSkuDetail(skuId); + x.desc = sku.attr_value.map((x) => x.value).join(','); + x.createdTimeString = dayjs(new Date(x.createdAt)).format('YYYY-MM-DD HH:mm:ss'); + x.user = x.createBy.substring(0, 10); + }), + ); + + this.pageNumber++; + const commentList = refresh ? newComments : this.data.commentList.concat(newComments); + if (commentList.length >= total) { + this.setData; + } + this.setData({ + commentList, + loadMoreStatus: commentList.length >= total ? LIST_LOADING_STATUS.NO_MORE : LIST_LOADING_STATUS.READY, + }); + } catch (e) { + console.error(e); + this.setData({ loadMoreStatus: LIST_LOADING_STATUS.FAILED }); + } + }, + + async onLoad(options) { + const spuId = options?.spuId; + if (typeof spuId !== 'string') { + this.setData({ loadMoreStatus: LIST_LOADING_STATUS.FAILED }); + } else { + this.setData({ spuId }); + } + + this.loadComments(true); + }, + onReachBottom() { + if (this.data.loadMoreStatus == LIST_LOADING_STATUS.READY) this.loadComments(false); + }, +}); diff --git a/miniprogram/tcb-shop/pages/goods/comments/index.json b/miniprogram/tcb-shop/pages/goods/comments/index.json new file mode 100644 index 0000000..a6418c9 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/index.json @@ -0,0 +1,8 @@ +{ + "navigationBarTitleText": "全部评价", + "usingComponents": { + "t-tag": "tdesign-miniprogram/tag/tag", + "comments-card": "./components/comments-card/index", + "t-load-more": "/components/load-more/index" + } +} diff --git a/miniprogram/tcb-shop/pages/goods/comments/index.wxml b/miniprogram/tcb-shop/pages/goods/comments/index.wxml new file mode 100644 index 0000000..66a4514 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/index.wxml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/comments/index.wxss b/miniprogram/tcb-shop/pages/goods/comments/index.wxss new file mode 100644 index 0000000..b98f36b --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/comments/index.wxss @@ -0,0 +1,49 @@ +/* 层级定义 +@z-index-0: 1; +@z-index-1: 100; +@z-index-2: 200; +@z-index-5: 500; +@z-index-component: 1000; // 通用组件级别 +@z-index-dropdown: @z-index-component; +@z-index-sticky: @z-index-component + 20; +@z-index-fixed: @z-index-component + 30; +@z-index-modal-backdrop:@z-index-component + 40; +@z-index-modal:@z-index-component + 50; +@z-index-popover:@z-index-component + 60; +@z-index-tooltip:@z-index-component + 70; +*/ +/* var() css变量适配*/ +page { + background-color: #FFFFFF; +} + +.comments-header { + display: flex; + flex-wrap: wrap; + padding: 32rpx 32rpx 0rpx; + background-color: #fff; + margin-top: -24rpx; + margin-left: -24rpx; +} + +.comments-header-tag { + margin-top: 24rpx; + margin-left: 24rpx; + height: 56rpx !important; + font-size: 24rpx !important; + justify-content: center; + background-color: #F5F5F5 !important; + border-radius: 8rpx !important; + border: 1px solid #F5F5F5 !important; +} + +.comments-header-active { + background-color: #FFECE9 !important; + color: #FA4126 !important; + border: 1px solid #FA4126 !important; +} + +.no-more { + padding-left: 20rpx; + padding-right: 20rpx; +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/details/components/buy-bar/index.js b/miniprogram/tcb-shop/pages/goods/details/components/buy-bar/index.js new file mode 100644 index 0000000..ae274e2 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/details/components/buy-bar/index.js @@ -0,0 +1,66 @@ +Component({ + externalClasses: ['wr-sold-out', 'wr-class'], + + options: { multipleSlots: true }, + + properties: { + soldout: { + // 商品是否下架 + type: Boolean, + value: false, + }, + jumpArray: { + type: Array, + value: [], + }, + isStock: { + type: Boolean, + value: true, + }, // 是否有库存 + isSlotButton: { + type: Boolean, + value: false, + }, // 是否开启按钮插槽 + shopCartNum: { + type: Number, // 购物车气泡数量 + }, + buttonType: { + type: Number, + value: 0, + }, + minDiscountPrice: { + type: String, + value: '', + }, + minSalePrice: { + type: String, + value: '', + }, + }, + + data: { + fillPrice: false, + }, + + methods: { + toAddCart() { + const { isStock } = this.properties; + if (!isStock) return; + this.triggerEvent('toAddCart'); + }, + + toBuyNow(e) { + const { isStock } = this.properties; + if (!isStock) return; + this.triggerEvent('toBuyNow', e); + }, + + toNav(e) { + const { url } = e.currentTarget.dataset; + return this.triggerEvent('toNav', { + e, + url, + }); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/goods/details/components/buy-bar/index.json b/miniprogram/tcb-shop/pages/goods/details/components/buy-bar/index.json new file mode 100644 index 0000000..7464ae6 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/details/components/buy-bar/index.json @@ -0,0 +1,6 @@ +{ + "component": true, + "usingComponents": { + "t-icon": "tdesign-miniprogram/icon/icon" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/details/components/buy-bar/index.wxml b/miniprogram/tcb-shop/pages/goods/details/components/buy-bar/index.wxml new file mode 100644 index 0000000..604afb4 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/details/components/buy-bar/index.wxml @@ -0,0 +1,38 @@ + + {{soldout ? '商品已下架' : '商品已售馨'}} + + + + + + + {{shopCartNum > 99 ? '99+' : shopCartNum}} + + + {{item.title}} + + + + + + + 加入购物车 + + + 立即购买 + + + + + + + + diff --git a/miniprogram/tcb-shop/pages/goods/details/components/buy-bar/index.wxss b/miniprogram/tcb-shop/pages/goods/details/components/buy-bar/index.wxss new file mode 100644 index 0000000..44e8ad0 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/details/components/buy-bar/index.wxss @@ -0,0 +1,107 @@ +.footer-cont { + background-color: #fff; + padding: 16rpx; +} + +.icon-warp { + width: 110rpx; + display: flex; + justify-content: center; + align-items: center; + text-align: center; +} + +.operate-wrap { + position: relative; +} + +.bottom-operate-left { + width: 100%; +} + +.bottom-operate-left .icon-warp { + width: 50%; +} + +.tag-cart-num { + display: inline-block; + position: absolute; + left: 50rpx; + right: auto; + top: 6rpx; + color: #fff; + line-height: 24rpx; + text-align: center; + z-index: 99; + white-space: nowrap; + min-width: 28rpx; + border-radius: 14rpx; + background-color: #fa550f !important; + font-size: 20rpx; + font-weight: 400; + padding: 2rpx 6rpx; +} + +.operate-text { + color: #666; + font-size: 20rpx; +} + +.soldout { + height: 80rpx; + background: rgba(170, 170, 170, 1); + width: 100%; + color: #fff; +} + +.addCart-disabled, +.bar-addCart-disabled { + background: rgba(221, 221, 221, 1) !important; + color: #fff !important; + font-size: 28rpx; +} + +.buyNow-disabled, +.bar-buyNow-disabled { + background: rgba(198, 198, 198, 1) !important; + color: #fff !important; + font-size: 28rpx; +} + +.bar-separately, +.bar-buy { + width: 254rpx; + height: 80rpx; + color: #fff; + display: flex; + align-items: center; + justify-content: center; +} + +.bar-separately { + background: #ffece9; + color: #fa4126; + border-radius: 40rpx 0 0 40rpx; +} + +.bar-buy { + background-color: #fa4126; + border-radius: 0rpx 40rpx 40rpx 0rpx; +} + +.flex { + display: flex; + display: -webkit-flex; +} + +.flex-center { + justify-content: center; + -webkit-justify-content: center; + align-items: center; + -webkit-align-items: center; +} + +.flex-between { + justify-content: space-between; + -webkit-justify-content: space-between; +} diff --git a/miniprogram/tcb-shop/pages/goods/details/components/goods-specs-popup/index.js b/miniprogram/tcb-shop/pages/goods/details/components/goods-specs-popup/index.js new file mode 100644 index 0000000..e43fac2 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/details/components/goods-specs-popup/index.js @@ -0,0 +1,303 @@ +const ATTR_VALUE_STATUS = { + PICKED: 'picked', + UNPICKED: 'unpicked', + DISABLED: 'disabled', +}; + +Component({ + options: { + multipleSlots: true, + addGlobalClass: true, + }, + + properties: { + title: String, + show: { + type: Boolean, + value: false, + }, + initProps: { + type: Object, + observer(initProps) { + if (!initProps) { + return; + } + const { skus, spu } = initProps; + + const attrValues = collectAttrValueSet(skus); + const attrList = initAttrList(attrValues); + + this.calculateValue(attrList, skus); + + this.setData({ + skus, + attrList, + spu, + }); + }, + }, + outOperateStatus: { + type: String, + value: 'no', + }, + }, + + data: { + price: 0, + imgSrc: '', + max: 1, + + skus: [], + attrList: [], + spu: null, + pickedSku: null, + value: 1, + }, + + observers: { + 'spu, pickedSku': function (spu, pickedSku) { + const imgSrc = pickedSku?.image ? pickedSku.image : spu.cover_image; + this.setData({ + imgSrc, + }); + }, + 'skus, pickedSku': function (skus, pickedSku) { + let max; + let price; + if (pickedSku != null) { + max = pickedSku.count; + price = pickedSku.price; + } else { + price = skus.reduce((acc, cur) => Math.min(acc, cur.price), Infinity); + max = 1; + } + this.setData({ price, max }); + }, + max: function (max) { + const { value } = this.data; + if (value > max) { + this.setData({ + value: max, + }); + } + }, + 'attrList, skus': function (attrList, skus) { + this.calculateValue(attrList, skus); + const sku = this.pickOnlySku(attrList); + sku && this.triggerEvent('picked'); + this.setData({ pickedSku: sku }); + }, + }, + + methods: { + clickAttrValue({ + target: { + dataset: { attrValueIndex, attrNameIndex }, + }, + }) { + const setAttrListWithCalculation = (attrList) => { + const { skus } = this.data; + this.calculateValue(attrList, skus); + const sku = this.pickOnlySku(attrList); + this.setData({ attrList, pickedSku: sku }); + }; + const { attrList } = this.data; + + const attrName = attrList[attrNameIndex]; + const attrValue = attrName.values[attrValueIndex]; + + switch (attrValue.status) { + case ATTR_VALUE_STATUS.UNPICKED: + // pick it, and set others unpicked + attrName.values.forEach((value) => { + value.status = value === attrValue ? ATTR_VALUE_STATUS.PICKED : ATTR_VALUE_STATUS.UNPICKED; + }); + setAttrListWithCalculation(attrList); + break; + case ATTR_VALUE_STATUS.DISABLED: + // do nothing + break; + case ATTR_VALUE_STATUS.PICKED: + // unpick it + attrValue.status = ATTR_VALUE_STATUS.UNPICKED; + setAttrListWithCalculation(attrList); + break; + default: + // invalid status, skip + return; + } + }, + + /** + * + * @param {[]} attrList + * @param {[]} skus + */ + calculateValue(attrList, skus) { + // Any row of attr values, should be filter out by picked attr values from rest of the rows. + attrList.forEach((attrName, index) => { + const restPickedValues = attrList + .filter((_, i) => i !== index) // exclude current line + .map((x) => x.values.find((y) => y.status === ATTR_VALUE_STATUS.PICKED)) + .filter((x) => x != null); + + const filteredSkus = skus.filter( + (sku) => + sku.count > 0 && + contains({ + container: sku.attrValues, + arr: restPickedValues, + eq: (a, b) => a._id === b._id, + }), + ); + const filteredAttrValues = collectAttrValueSet(filteredSkus); + + attrName.values.forEach((value) => { + if (filteredAttrValues.find((x) => x._id === value._id) == null) { + value.status = ATTR_VALUE_STATUS.DISABLED; + } else { + if (value.status === ATTR_VALUE_STATUS.DISABLED) { + value.status = ATTR_VALUE_STATUS.UNPICKED; + } + } + }); + }); + }, + + pickOnlySku(attrList) { + const pickedAttrValues = attrList + .map((x) => x.values.find((x) => x.status === ATTR_VALUE_STATUS.PICKED)) + .filter((x) => x != null); + if (pickedAttrValues.length !== attrList.length) { + // should pick more + return null; + } + + const { skus } = this.data; + const pickedSku = skus.find( + (sku) => + sku.attrValues.length === pickedAttrValues.length && // a and b have the same size + sku.attrValues.every((x) => pickedAttrValues.find((y) => y._id === x._id) != null), // every item in a can be found in b + ); + if (pickedSku == null) { + console.error( + 'Something went wrong! With enough picked attr values, an sku should be picked! Picked attr values:', + pickedAttrValues, + ); + return null; + } + return pickedSku; + }, + + specsConfirm() { + if (this.data.outOperateStatus === 'cart') { + this.addCart(); + } else if (this.data.outOperateStatus === 'buy') { + this.buyNow(); + } + }, + + handlePopupHide() { + this.triggerEvent('closeSpecsPopup', { + show: false, + }); + }, + + addCart() { + const { pickedSku, max, value } = this.properties; + if (pickedSku == null) { + return; + } + if (value > max) { + return; + } + if (value < 1) { + return; + } + this.triggerEvent('addCart', { pickedSku, count: value }); + }, + + buyNow() { + const { pickedSku, max, value } = this.properties; + if (pickedSku == null) { + return; + } + if (value > max) { + return; + } + if (value < 1) { + return; + } + this.triggerEvent('buyNow', { pickedSku, count: value }); + }, + + handleBuyNumChange({ detail: { value } }) { + this.setData({ value }); + }, + }, + lifetimes: {}, +}); + +/** + * + * @param {Array} attrValues + * @returns {Array} + */ +function initAttrList(attrValues) { + const list = attrValues.reduce((acc, cur) => { + const item = acc.find((x) => x._id === cur.attr_name._id); + if (item != null) { + // already has this attr name, push value to this item + if (item.values.find((x) => x._id === cur._id) != null) { + // already has this attr value, do nothing + } else { + item.values.push({ + value: cur.value, + _id: cur._id, + status: ATTR_VALUE_STATUS.UNPICKED, + }); + } + } else { + // not added attr kind, make a new one + acc.push({ + ...cur.attr_name, + values: [ + { + value: cur.value, + _id: cur._id, + status: ATTR_VALUE_STATUS.UNPICKED, + }, + ], + }); + } + return acc; + }, []); + return list; +} + +/** + * + * @param {{container: Array, arr: Array, eq: (a, b) => boolean}} param0 + * @returns + */ +function contains({ container, arr, eq }) { + return arr.every((itemInArr) => container.findIndex((x) => eq(x, itemInArr)) !== -1); +} + +/** + * + * @param {Array} skus + */ +function collectAttrValueSet(skus) { + const attrValues = skus.reduce((acc, sku) => { + sku.attrValues.forEach((value) => { + if (acc.find((x) => x._id === value._id) != null) { + // exists, skip + } else { + acc.push(Object.assign({}, value)); + } + }); + return acc; + }, []); + return attrValues; +} diff --git a/miniprogram/tcb-shop/pages/goods/details/components/goods-specs-popup/index.json b/miniprogram/tcb-shop/pages/goods/details/components/goods-specs-popup/index.json new file mode 100644 index 0000000..3d351b0 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/details/components/goods-specs-popup/index.json @@ -0,0 +1,11 @@ +{ + "component": true, + "usingComponents": { + "t-popup": "tdesign-miniprogram/popup/popup", + "t-icon": "tdesign-miniprogram/icon/icon", + "t-image": "/components/webp-image/index", + "t-stepper": "tdesign-miniprogram/stepper/stepper", + "t-toast": "tdesign-miniprogram/toast/toast", + "price": "/components/price/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/details/components/goods-specs-popup/index.wxml b/miniprogram/tcb-shop/pages/goods/details/components/goods-specs-popup/index.wxml new file mode 100644 index 0000000..7600498 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/details/components/goods-specs-popup/index.wxml @@ -0,0 +1,63 @@ + + + + + + + + + {{title}} + + + + + + 选择: + + + {{selectedItem.value}} + + + + + + + + + {{item.name}} + + + {{valuesItem.value}} + + + + + + + + 购买数量 + ({{limitBuyInfo}}) + + + + + + + 确定 + + + + + 加入购物车 + + + + + 立即购买 + + + + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/details/components/goods-specs-popup/index.wxss b/miniprogram/tcb-shop/pages/goods/details/components/goods-specs-popup/index.wxss new file mode 100644 index 0000000..41d852d --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/details/components/goods-specs-popup/index.wxss @@ -0,0 +1,302 @@ +.popup-container { + background-color: #ffffff; + position: relative; + z-index: 100; + border-radius: 16rpx 16rpx 0 0; + padding-bottom: calc(env(safe-area-inset-bottom) + 20rpx); +} + +.popup-container .popup-close { + position: absolute; + right: 30rpx; + top: 30rpx; + z-index: 9; + color: #999999; +} + +.popup-sku-header { + display: flex; + padding: 30rpx 28rpx 0 30rpx; +} + +.popup-sku-header .popup-sku-header__img { + width: 176rpx; + height: 176rpx; + border-radius: 8rpx; + background: #d8d8d8; + margin-right: 24rpx; +} + +.popup-sku-header .popup-sku-header__goods-info { + position: relative; + width: 500rpx; +} + +.popup-sku-header .popup-sku-header__goods-info .popup-sku__goods-name { + font-size: 28rpx; + line-height: 40rpx; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + white-space: normal; + overflow: hidden; + width: 430rpx; + text-overflow: ellipsis; +} + +.popup-sku-header .popup-sku-header__goods-info .popup-sku__selected-spec { + display: flex; + color: #333333; + font-size: 26rpx; + line-height: 36rpx; +} + +.popup-sku-header + .popup-sku-header__goods-info + .popup-sku__selected-spec + .popup-sku__selected-item { + margin-right: 10rpx; + margin-left: 10rpx; + display: inline-block; +} + +.popup-sku-body { + margin: 0 30rpx 40rpx; + max-height: 600rpx; + overflow-y: scroll; + -webkit-overflow-scrolling: touch; +} + +.popup-sku-body .popup-sku-group-container .popup-sku-row { + padding: 32rpx 0; + border-bottom: 1rpx solid #f5f5f5; +} + +.popup-sku-body + .popup-sku-group-container + .popup-sku-row + .popup-sku-row__title { + font-size: 26rpx; + color: #333; +} + +.popup-sku-body .popup-sku-group-container .popup-sku-row .popup-sku-row__item { + font-size: 24rpx; + color: #333; + min-width: 128rpx; + height: 56rpx; + background-color: #f5f5f5; + border-radius: 8rpx; + border: 2rpx solid #f5f5f5; + margin: 19rpx 26rpx 0 0; + padding: 0 16rpx; + display: inline-flex; + align-items: center; + justify-content: center; +} + +.popup-sku-body + .popup-sku-group-container + .popup-sku-row + .popup-sku-row__item.popup-sku-row__item--active { + border: 2rpx solid #fa4126; + color: #fa4126; + background: rgba(255, 95, 21, 0.04); +} + +.popup-sku-body + .popup-sku-group-container + .popup-sku-row + .disabled-sku-selected { + background: #f5f5f5 !important; + color: #cccccc; +} + +.popup-sku-body .popup-sku-stepper-stock .popup-sku-stepper-container { + display: flex; + align-items: center; + justify-content: space-between; + margin: 40rpx 0; +} + +.popup-sku-body + .popup-sku-stepper-stock + .popup-sku-stepper-container + .popup-sku__stepper-title { + display: flex; + font-size: 26rpx; + color: #333; +} + +.popup-sku-body + .popup-sku-stepper-stock + .popup-sku-stepper-container + .popup-sku__stepper-title + .limit-text { + margin-left: 10rpx; + color: #999999; +} + +.popup-sku-body + .popup-sku-stepper-stock + .popup-sku-stepper-container + .popup-stepper { + display: flex; + flex-flow: row nowrap; + align-items: center; + font-size: 28px; + height: 48rpx; + line-height: 62rpx; +} + +.popup-sku-body + .popup-sku-stepper-stock + .popup-sku-stepper-container + .popup-stepper + .input-btn, +.popup-sku-body + .popup-sku-stepper-stock + .popup-sku-stepper-container + .popup-stepper + .input-num-wrap { + position: relative; + height: 100%; + text-align: center; + background-color: #f5f5f5; + border-radius: 4rpx; +} + +.popup-sku-body + .popup-sku-stepper-stock + .popup-sku-stepper-container + .popup-stepper + .input-num-wrap { + color: #282828; + display: flex; + max-width: 76rpx; + align-items: center; + justify-content: space-between; +} + +.popup-sku-body + .popup-sku-stepper-stock + .popup-sku-stepper-container + .popup-stepper + .input-num-wrap + .input-num { + height: 100%; + width: auto; + font-weight: 600; + font-size: 30rpx; +} + +.popup-sku-body + .popup-sku-stepper-stock + .popup-sku-stepper-container + .popup-stepper + .input-btn { + width: 48rpx; +} + +.popup-sku-body + .popup-sku-stepper-stock + .popup-sku-stepper-container + .popup-stepper + .popup-stepper__minus { + margin-right: 4rpx; + border-radius: 4rpx; + color: #9a979b; + display: flex; + align-items: center; + justify-content: center; +} + +.popup-sku-body + .popup-sku-stepper-stock + .popup-sku-stepper-container + .popup-stepper + .popup-stepper__plus { + margin-left: 4rpx; + border-radius: 4rpx; + color: #9a979b; + display: flex; + align-items: center; + justify-content: center; +} + +.popup-sku-body + .popup-sku-stepper-stock + .popup-sku-stepper-container + .popup-stepper + .popup-stepper__plus::after { + width: 24rpx; + height: 3rpx; + background-color: #999999; +} + +.popup-sku-body + .popup-sku-stepper-stock + .popup-sku-stepper-container + .popup-stepper + .popup-stepper__plus::before { + width: 3rpx; + height: 24rpx; + background-color: #999999; +} + +.popup-sku-actions { + font-size: 32rpx; + height: 80rpx; + text-align: center; + line-height: 80rpx; + padding: 0 20rpx; +} + +.popup-sku-actions .sku-operate { + height: 80rpx; + width: 50%; + color: #fff; + border-radius: 48rpx; +} + +.popup-sku-actions .sku-operate .sku-operate-addCart { + background-color: #ffece9; + color: #fa4126; + border-radius: 48rpx 0 0 48rpx; +} + +.popup-sku-actions .sku-operate .sku-operate-addCart.disabled { + background: rgb(221, 221, 221); + color: #fff; +} + +.popup-sku-actions .sku-operate .sku-operate-buyNow { + background-color: #fa4126; + border-radius: 0 48rpx 48rpx 0; +} + +.popup-sku-actions .sku-operate .sku-operate-buyNow.disabled { + color: #fff; + background: rgb(198, 198, 198); +} + +.popup-sku-actions .sku-operate .selected-sku-btn { + width: 100%; +} + +.popup-container .single-confirm-btn { + border-radius: 48rpx; + color: #ffffff; + margin: 0 32rpx; + font-size: 32rpx; + height: 80rpx; + text-align: center; + line-height: 88rpx; + background-color: #fa4126; +} + +.popup-container .single-confirm-btn.disabled { + font-size: 32rpx; + color: #fff; + background-color: #dddddd; +} diff --git a/miniprogram/tcb-shop/pages/goods/details/components/promotion-popup/index.js b/miniprogram/tcb-shop/pages/goods/details/components/promotion-popup/index.js new file mode 100644 index 0000000..9743cc1 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/details/components/promotion-popup/index.js @@ -0,0 +1,35 @@ +Component({ + options: { + multipleSlots: true, + }, + + properties: { + list: Array, + title: { + type: String, + value: '促销说明', + }, + show: { + type: Boolean, + }, + }, + + // data: { + // list: [], + // }, + + methods: { + change(e) { + const { index } = e.currentTarget.dataset; + this.triggerEvent('promotionChange', { + index, + }); + }, + + closePromotionPopup() { + this.triggerEvent('closePromotionPopup', { + show: false, + }); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/goods/details/components/promotion-popup/index.json b/miniprogram/tcb-shop/pages/goods/details/components/promotion-popup/index.json new file mode 100644 index 0000000..a9de77d --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/details/components/promotion-popup/index.json @@ -0,0 +1,7 @@ +{ + "component": true, + "usingComponents": { + "t-popup": "tdesign-miniprogram/popup/popup", + "t-icon": "tdesign-miniprogram/icon/icon" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/details/components/promotion-popup/index.wxml b/miniprogram/tcb-shop/pages/goods/details/components/promotion-popup/index.wxml new file mode 100644 index 0000000..c1cea9d --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/details/components/promotion-popup/index.wxml @@ -0,0 +1,34 @@ + + + + + + + {{title}} + + + + + {{item.tag}} + + {{item.label ? item.label : ''}} + + + + + + + + + diff --git a/miniprogram/tcb-shop/pages/goods/details/components/promotion-popup/index.wxss b/miniprogram/tcb-shop/pages/goods/details/components/promotion-popup/index.wxss new file mode 100644 index 0000000..6e0e167 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/details/components/promotion-popup/index.wxss @@ -0,0 +1,131 @@ +.promotion-popup-container { + background-color: #ffffff; + position: relative; + z-index: 100; + border-radius: 16rpx 16rpx 0 0; +} + +.promotion-popup-container .promotion-popup-close { + position: absolute; + right: 30rpx; + top: 30rpx; + z-index: 9; + color: rgba(153, 153, 153, 1); +} + +.promotion-popup-container .promotion-popup-close .market { + font-size: 25rpx; + color: #999; +} + +.promotion-popup-container .promotion-popup-title { + height: 100rpx; + position: relative; + display: flex; + align-items: center; + justify-content: center; +} + +.promotion-popup-container .promotion-popup-title { + font-size: 32rpx; + color: #222427; + font-weight: 600; +} + +.promotion-popup-container .promotion-popup-content { + min-height: 400rpx; + max-height: 600rpx; + padding-bottom: calc(env(safe-area-inset-bottom) + 20rpx); + overflow-y: scroll; + -webkit-overflow-scrolling: touch; +} + +.promotion-popup-container .promotion-popup-content .promotion-detail-list { + margin: 0 30rpx; +} + +.promotion-popup-container + .promotion-popup-content + .promotion-detail-list + .list-item:last-child { + margin-bottom: env(safe-area-inset-bottom); + border-bottom: 0; + padding-bottom: calc(28rpx + env(safe-area-inset-bottom)); +} + +.promotion-popup-container + .promotion-popup-content + .promotion-detail-list + .list-item { + display: flex; + justify-content: space-between; + padding: 10rpx 0 28rpx; + position: relative; + font-size: 24rpx; + color: #222427; +} + +.promotion-popup-container + .promotion-popup-content + .promotion-detail-list + .list-item + .tag { + box-sizing: border-box; + font-size: 20rpx; + line-height: 32rpx; + padding: 2rpx 12rpx; + background-color: #ffece9; + margin-right: 16rpx; + display: inline-flex; + color: #fa4126; + border-radius: 54rpx; + flex-shrink: 0; + position: relative; + top: 2rpx; +} + +.promotion-popup-container + .promotion-popup-content + .promotion-detail-list + .list-item + .content { + font-size: 28rpx; + color: #222427; + flex: 1; + line-height: 40rpx; + display: flex; +} + +.promotion-popup-container + .promotion-popup-content + .promotion-detail-list + .list-item + .content + .list-content { + width: 440rpx; + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + display: inline-block; +} + +.promotion-popup-container + .promotion-popup-content + .promotion-detail-list + .list-item + .collect-btn { + font-size: 24rpx; + flex-shrink: 0; + margin-left: 20rpx; + display: flex; + align-items: center; +} + +.promotion-popup-container + .promotion-popup-content + .promotion-detail-list + .list-item + .collect-btn + .linkText { + margin-right: 8rpx; +} diff --git a/miniprogram/tcb-shop/pages/goods/details/index.js b/miniprogram/tcb-shop/pages/goods/details/index.js new file mode 100644 index 0000000..1172eb5 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/details/index.js @@ -0,0 +1,421 @@ +// 第三方库 +import Toast from 'tdesign-miniprogram/toast/index'; + +// 服务层 +import { getAllAttrValues } from '../../../services/attrValue/attrValue'; +import { handleSpuCloudImage, getSpu } from '../../../services/good/spu'; +import { fetchCartItems, createCartItem, updateCartItemCount } from '../../../services/cart/cart'; +import { getGoodsDetailCommentInfo } from '../../../services/comments/comments'; +import { getAllSku } from '../../../services/sku/sku'; + +// 工具函数 +import { cdnBase } from '../../../config/index'; +import { cartShouldFresh } from '../../../utils/cartFresh'; +import { getCloudImageTempUrl, getSingleCloudImageTempUrl } from '../../../utils/cloudImageHandler'; +import { objectToParamString } from '../../../utils/util'; + +const imgPrefix = `${cdnBase}/`; + +const recLeftImg = `${imgPrefix}common/rec-left.png`; +const recRightImg = `${imgPrefix}common/rec-right.png`; + +async function replaceCloudImageWithTempUrl(text) { + let ret = text; + + // 使用正则表达式匹配所有被双引号包裹的链接 + const regex = /"(cloud:\/\/[^"]*)"/g; + let match; + // 使用一个循环来处理所有匹配的链接 + while ((match = regex.exec(ret)) !== null) { + const originalLink = match[0]; + const pureLink = match[1]; + // 处理链接 + const processedLink = await getSingleCloudImageTempUrl(pureLink); + // 替换文本中的原始链接 + ret = ret.replace(originalLink, `"${processedLink}"`); + } + + return ret; +} + +const OUT_OPERATE_STATUS = { + CART: 'cart', + BUY: 'buy', + NO: 'no', +}; + +Page({ + data: { + commentsList: [], + commentsStatistics: { + badCount: 0, + commentCount: 0, + goodCount: 0, + goodRate: 0, + hasImageCount: 0, + middleCount: 0, + }, + isShowPromotionPop: false, + activityList: [], + recLeftImg, + recRightImg, + details: {}, + jumpArray: [ + { + title: '首页', + url: '/pages/home/home', + iconName: 'home', + }, + { + title: '购物车', + url: '/pages/cart/index', + iconName: 'cart', + showCartNum: true, + }, + ], + isStock: true, + cartNum: 0, + soldout: false, + buttonType: 1, + buyNum: 1, + selectedAttrStr: '', + skuArray: [], + primaryImage: '', + specImg: '', + isSpuSelectPopupShow: false, + isAllSelectedSku: false, + buyType: 0, + outOperateStatus: OUT_OPERATE_STATUS.NO, // 是否外层加入购物车 + operateType: 0, + selectSkuSellsPrice: 0, + maxLinePrice: 0, + minSalePrice: 0, + maxSalePrice: 0, + list: [], + spuId: '', + navigation: { type: 'fraction' }, + current: 0, + autoplay: true, + duration: 500, + interval: 5000, + soldNum: 0, // 已售数量, + loading: false, + }, + + setLoading() { + this.setData({ loading: true }); + }, + unsetLoading() { + this.setData({ loading: false }); + }, + + handlePopupHide() { + this.setData({ + isSpuSelectPopupShow: false, + }); + }, + + onSpecSelectTap() { + this.showSkuSelectPopup(OUT_OPERATE_STATUS.NO); + }, + + showSkuSelectPopup(status) { + this.setData({ + outOperateStatus: status, + isSpuSelectPopupShow: true, + }); + }, + + buyItNow() { + this.showSkuSelectPopup(OUT_OPERATE_STATUS.BUY); + }, + + toAddCart() { + this.showSkuSelectPopup(OUT_OPERATE_STATUS.CART); + }, + + toNav(e) { + const { url } = e.detail; + wx.switchTab({ + url: url, + }); + }, + + showCurImg(e) { + const { index } = e.detail; + const { images } = this.data.details; + wx.previewImage({ + current: images[index], + urls: images, // 需要预览的图片http链接列表 + }); + }, + + onPageScroll({ scrollTop }) { + const goodsTab = this.selectComponent('#goodsTab'); + goodsTab && goodsTab.onScroll(scrollTop); + }, + + chooseSpecItem(e) { + const { specList } = this.data.details; + const { selectedSku, isAllSelectedSku } = e.detail; + if (!isAllSelectedSku) { + this.setData({ + selectSkuSellsPrice: 0, + }); + } + this.setData({ + isAllSelectedSku, + }); + this.getSkuItem(specList, selectedSku); + }, + + getSkuItem(specList, selectedSku) { + const { skuArray, primaryImage } = this.data; + const selectedSkuValues = this.getSelectedSkuValues(specList, selectedSku); + let selectedAttrStr = ` 件 `; + selectedSkuValues.forEach((item) => { + selectedAttrStr += `,${item.specValue} `; + }); + // eslint-disable-next-line array-callback-return + const skuItem = skuArray.filter((item) => { + let status = true; + (item.specInfo || []).forEach((subItem) => { + if (!selectedSku[subItem.specId] || selectedSku[subItem.specId] !== subItem.specValueId) { + status = false; + } + }); + if (status) return item; + }); + this.selectSpecsName(selectedSkuValues.length > 0 ? selectedAttrStr : ''); + if (skuItem) { + this.setData({ + selectItem: skuItem, + selectSkuSellsPrice: skuItem.price || 0, + }); + } else { + this.setData({ + selectItem: null, + selectSkuSellsPrice: 0, + }); + } + this.setData({ + specImg: skuItem && skuItem.skuImage ? skuItem.skuImage : primaryImage, + }); + }, + + // 获取已选择的sku名称 + getSelectedSkuValues(skuTree, selectedSku) { + const normalizedTree = this.normalizeSkuTree(skuTree); + return Object.keys(selectedSku).reduce((selectedValues, skuKeyStr) => { + const skuValues = normalizedTree[skuKeyStr]; + const skuValueId = selectedSku[skuKeyStr]; + if (skuValueId !== '') { + const skuValue = skuValues.filter((value) => { + return value.specValueId === skuValueId; + })[0]; + skuValue && selectedValues.push(skuValue); + } + return selectedValues; + }, []); + }, + + normalizeSkuTree(skuTree) { + const normalizedTree = {}; + skuTree.forEach((treeItem) => { + normalizedTree[treeItem.specId] = treeItem.specValueList; + }); + return normalizedTree; + }, + + selectSpecsName(selectSpecsName) { + if (selectSpecsName) { + this.setData({ + selectedAttrStr: selectSpecsName, + }); + } else { + this.setData({ + selectedAttrStr: '', + }); + } + }, + + toast(message) { + Toast({ + context: this, + selector: '#t-toast', + message, + icon: '', + duration: 1000, + }); + }, + + async addCart({ detail: { count, pickedSku } }) { + const overCount = () => this.toast('超过购买上限了'); + const addCartSucceed = () => { + cartShouldFresh(); + this.handlePopupHide(); + this.toast('加入购物车成功'); + }; + const addCartFail = () => this.toast('加入购物车失败'); + + const records = await fetchCartItems(); + + const cartItem = records.find((x) => x.sku._id === pickedSku._id); + // eslint-disable-next-line eqeqeq + if (cartItem == null) { + // cart item empty, create + if (count <= pickedSku.count) { + await createCartItem({ skuId: pickedSku._id, count }).then(addCartSucceed, addCartFail); + } else { + overCount(); + } + } else { + // cart item exists, update count + const finalCount = cartItem.count + count; + if (finalCount <= pickedSku.count) { + await updateCartItemCount({ cartItemId: cartItem._id, count: finalCount }).then(addCartSucceed, addCartFail); + } else { + overCount(); + } + } + }, + + onPicked() { + this.setData({ isAllSelectedSku: true }); + }, + + gotoBuy(e) { + const overCount = () => this.toast('超过购买上限了'); + const buyCount = e.detail.count; + const skuId = e.detail.pickedSku._id; + const skuCount = e.detail.pickedSku.count; + if (buyCount > skuCount) return overCount(); + + wx.navigateTo({ + url: `/pages/order/order-confirm/index?${objectToParamString({ type: 'direct', count: buyCount, skuId })}`, + }); + }, + + specsConfirm() { + const { buyType } = this.data; + if (buyType === 1) { + this.gotoBuy(); + } else { + this.addCart(); + } + }, + + changeNum(e) { + this.setData({ + buyNum: e.detail.buyNum, + }); + }, + + closePromotionPopup() { + this.setData({ + isShowPromotionPop: false, + }); + }, + + promotionChange(e) { + const { index } = e.detail; + wx.navigateTo({ + url: `/pages/promotion-detail/index?promotion_id=${index}`, + }); + }, + + showPromotionPopup() { + this.setData({ + isShowPromotionPop: true, + }); + }, + + async getInfo(spuId) { + // pics + // min price + // spu title + // attrs => sku => count + price + // spu detail + // comment + const spu = await getSpu(spuId); + + const loadSkus = async () => { + const skus = await getAllSku(spuId); + const minPrice = skus.reduce((acc, current) => Math.min(acc, current.price), Infinity) * 100; + const loadSkuAttrValues = async () => { + return Promise.all( + skus.map(async (sku) => { + const attrValues = await getAllAttrValues(sku._id); + sku.attrValues = attrValues; + }), + ); + }; + const handleSkuImages = async () => { + const images = skus.map((s) => s.image ?? ''); + const handledImages = await getCloudImageTempUrl(images); + handledImages.forEach((image, index) => (skus[index].image = image)); + }; + await Promise.all([handleSkuImages(), loadSkuAttrValues()]); + return { skus, minPrice }; + }; + + const [_x, { skus, minPrice }, commentInfo] = await Promise.all([ + handleSpuCloudImage(spu), + loadSkus(), + getGoodsDetailCommentInfo(spu._id), + ]); + + const [ + { + data: { records, total: commentCount }, + }, + { + data: { total: goodCommentCount }, + }, + ] = commentInfo; + + records.forEach((x) => (x.userNameString = x.createBy.substring(0, 10))); + + const detail = await replaceCloudImageWithTempUrl(spu.detail); + + this.setData({ + details: { + images: spu.swiper_images, + title: spu.name, + }, + minSalePrice: minPrice, + detail, + descPopUpInitProps: { + skus, + minPrice, + spu, + }, + commentsStatistics: { + commentCount, + goodRate: (goodCommentCount / commentCount) * 100, + }, + spu, + commentsList: records, + }); + }, + + /** 跳转到评价列表 */ + navToCommentsListPage() { + wx.navigateTo({ + url: `/pages/goods/comments/index?spuId=${this.data.spu._id}`, + }); + }, + + async onLoad(query) { + const { spuId } = query; + this.setLoading(); + try { + await this.getInfo(spuId); + } catch (e) { + console.error(e); + this.toast('获取商品详情失败'); + } finally { + this.unsetLoading(); + } + }, +}); diff --git a/miniprogram/tcb-shop/pages/goods/details/index.json b/miniprogram/tcb-shop/pages/goods/details/index.json new file mode 100644 index 0000000..f8cab8b --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/details/index.json @@ -0,0 +1,19 @@ +{ + "navigationBarTitleText": "商品详情", + "usingComponents": { + "t-image": "/components/webp-image/index", + "t-tag": "tdesign-miniprogram/tag/tag", + "t-toast": "tdesign-miniprogram/toast/toast", + "t-rate": "tdesign-miniprogram/rate/rate", + "t-swiper": "tdesign-miniprogram/swiper/swiper", + "t-swiper-nav": "tdesign-miniprogram/swiper-nav/swiper-nav", + "t-button": "tdesign-miniprogram/button/button", + "t-icon": "tdesign-miniprogram/icon/icon", + "t-popup": "tdesign-miniprogram/popup/popup", + "price": "/components/price/index", + "buy-bar": "./components/buy-bar/index", + "promotion-popup": "./components/promotion-popup/index", + "goods-specs-popup": "./components/goods-specs-popup/index", + "loading-dialog": "../../../components/loading-dialog/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/details/index.wxml b/miniprogram/tcb-shop/pages/goods/details/index.wxml new file mode 100644 index 0000000..3e438db --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/details/index.wxml @@ -0,0 +1,71 @@ + + + + + + + + + + + + {{details.title}} + + + + 已选 + + + {{selectedAttrStr ? buyNum : ''}}{{selectedAttrStr || '请选择'}} + + + + + + + + 商品评价 + ({{ commentsStatistics.commentCount }}) + + + {{commentsStatistics.goodRate}}% 好评 + + + + + + + + {{commentItem.userNameString}} + + + + {{commentItem.content}} + + + + + + + 详情介绍 + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/details/index.wxss b/miniprogram/tcb-shop/pages/goods/details/index.wxss new file mode 100644 index 0000000..7459f9e --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/details/index.wxss @@ -0,0 +1,344 @@ +@import '../../../style/global.wxss'; + +page { + width: 100vw; +} + +.goods-detail-page .goods-info { + margin: 0 auto; + padding: 26rpx 0 28rpx 30rpx; + background-color: #fff; +} + +.goods-detail-page .swipe-img { + width: 100%; + height: 750rpx; +} + +.goods-detail-page .goods-info .goods-price { + display: flex; + align-items: baseline; +} + +.goods-detail-page .goods-info .goods-price-up { + color: #fa4126; + font-size: 28rpx; + position: relative; + bottom: 4rpx; + left: 8rpx; +} + +.goods-detail-page .goods-info .goods-price .class-goods-price { + font-size: 64rpx; + color: #fa4126; + font-weight: bold; + font-family: DIN Alternate; +} + +.goods-detail-page .goods-info .goods-price .class-goods-symbol { + font-size: 36rpx; + color: #fa4126; +} + +.goods-detail-page .goods-info .goods-price .class-goods-del { + position: relative; + font-weight: normal; + left: 16rpx; + bottom: 2rpx; + color: #999999; + font-size: 32rpx; +} + +.goods-detail-page .goods-info .goods-number { + display: flex; + align-items: center; + justify-content: space-between; +} + +.goods-detail-page .goods-info .goods-number .sold-num { + font-size: 24rpx; + color: #999999; + display: flex; + align-items: flex-end; + margin-right: 32rpx; +} + +.goods-detail-page .goods-info .goods-activity { + display: flex; + margin-top: 16rpx; + justify-content: space-between; +} + +.goods-detail-page .goods-info .goods-activity .tags-container { + display: flex; +} + +.goods-detail-page .goods-info .goods-activity .tags-container .goods-activity-tag { + background: #ffece9; + color: #fa4126; + font-size: 24rpx; + margin-right: 16rpx; + padding: 4rpx 8rpx; + border-radius: 4rpx; +} + +.goods-detail-page .goods-info .goods-activity .activity-show { + display: flex; + justify-content: center; + align-items: center; + color: #fa4126; + font-size: 24rpx; + padding-right: 32rpx; +} + +.goods-detail-page .goods-info .goods-activity .activity-show-text { + line-height: 42rpx; +} + +.goods-detail-page .goods-info .goods-title { + display: flex; + justify-content: space-between; + align-items: center; + margin-top: 20rpx; +} + +.goods-detail-page .goods-info .goods-title .goods-name { + width: 600rpx; + font-weight: 500; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + overflow: hidden; + font-size: 32rpx; + word-break: break-all; + color: #333333; +} + +.goods-detail-page .goods-info .goods-title .goods-tag { + width: 104rpx; + margin-left: 26rpx; +} + +.goods-detail-page .goods-info .goods-title .goods-tag .shareBtn { + border-radius: 200rpx 0px 0px 200rpx; + width: 100rpx; + height: 96rpx; + border: none; + padding-right: 36rpx !important; +} + +.goods-detail-page .goods-info .goods-title .goods-tag .shareBtn::after { + border: none; +} + +.goods-detail-page .goods-info .goods-title .goods-tag .btn-icon { + font-size: 20rpx; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 96rpx; + color: #999; +} + +.goods-detail-page .goods-info .goods-title .goods-tag .btn-icon .share-text { + padding-top: 8rpx; + font-size: 20rpx; + line-height: 24rpx; +} + +.goods-detail-page .goods-info .goods-intro { + font-size: 26rpx; + color: #888; + line-height: 36rpx; + word-break: break-all; + padding-right: 30rpx; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + white-space: normal; + overflow: hidden; +} + +.spu-select { + height: 80rpx; + background-color: #fff; + margin-top: 20rpx; + display: flex; + align-items: center; + padding: 30rpx; + font-size: 28rpx; +} + +.spu-select .label { + margin-right: 30rpx; + text-align: center; + flex-shrink: 0; + color: #999999; + font-weight: normal; +} + +.spu-select .content { + display: flex; + flex: 1; + justify-content: space-between; + align-items: center; +} + +.spu-select .content .tintColor { + color: #aaa; +} + +.goods-detail-page .desc-content { + margin-top: 20rpx; + background-color: #fff; + /* padding-bottom: 120rpx; */ + padding-bottom: 220rpx; +} + +.goods-detail-page .desc-content__title { + font-size: 28rpx; + line-height: 36rpx; + text-align: center; + display: flex; + justify-content: center; + align-items: center; + padding: 30rpx 20rpx; +} + +.goods-detail-page .desc-content__title .img { + width: 206rpx; + height: 10rpx; +} + +.goods-detail-page .desc-content__title--text { + font-size: 26rpx; + margin: 0 32rpx; + font-weight: 600; +} + +.goods-detail-page .desc-content__img { + width: 100%; + height: auto; +} + +.goods-bottom-operation { + position: fixed; + left: 0; + bottom: 0; + width: 100%; + background-color: #fff; + padding-bottom: env(safe-area-inset-bottom); +} + +.popup-sku-header .popup-sku-header__goods-info .popup-sku__price { + display: flex; + align-items: baseline; + color: #fa4126; + margin-top: 48rpx; +} + +.popup-sku-header .popup-sku-header__goods-info .popup-sku__price .popup-sku__price-num { + font-size: 64rpx; + color: #fa4126; + font-weight: bold; + font-family: DIN Alternate; +} + +.popup-sku-header .popup-sku-header__goods-info .popup-sku__price .popup-sku__price-del { + position: relative; + font-weight: normal; + left: 12rpx; + bottom: 2rpx; + color: #999999; + font-size: 32rpx; +} + +.popup-sku-header .popup-sku-header__goods-info .popup-sku__price .popup-sku__price-symbol { + font-size: 36rpx; + color: #fa4126; +} + +.popup-sku-header .popup-sku-header__goods-info .popup-sku__price .popup-sku__price-max-num { + font-size: 48rpx; +} + +.goods-detail-page .goods-head { + --td-swiper-radius: 0; +} + +.t-toast__content { + z-index: 12000 !important; +} + +.comments-wrap { + margin-top: 20rpx; + padding: 32rpx; + background-color: #fff; +} + +.comments-wrap .comments-head { + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; +} + +.comments-wrap .comments-head .comments-title-wrap { + display: flex; +} + +.comments-title-label, +.comments-title-count { + color: #333333; + font-size: 32rpx; + font-weight: 500; + line-height: 48rpx; +} + +.comments-rate-wrap { + display: flex; + justify-content: center; + align-items: center; + font-size: 24rpx; +} + +.comments-rate-wrap .comments-good-rate { + color: #999999; + font-size: 26rpx; + font-weight: 400; + font-style: normal; + line-height: 36rpx; +} + +.comment-item-wrap .comment-item-head { + display: flex; + flex-direction: row; + align-items: center; + margin-top: 32rpx; +} + +.comment-item-wrap .comment-item-head .comment-item-avatar { + width: 64rpx; + height: 64rpx; + border-radius: 64rpx; +} + +.comment-item-wrap .comment-item-head .comment-head-right { + margin-left: 24rpx; +} + +.comment-head-right .comment-username { + font-size: 26rpx; + color: #333333; + line-height: 36rpx; + font-weight: 400; +} + +.comment-item-wrap .comment-item-content { + margin-top: 20rpx; + color: #333333; + line-height: 40rpx; + font-size: 28rpx; + font-weight: 400; +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/list/index.js b/miniprogram/tcb-shop/pages/goods/list/index.js new file mode 100644 index 0000000..d9700a9 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/list/index.js @@ -0,0 +1,208 @@ +/* eslint-disable no-param-reassign */ +import { getPrice } from '../../../services/good/spu'; +import { getAllSpuOfCate } from '../../../services/cate/cate'; +import { getCloudImageTempUrl } from '../../../utils/cloudImageHandler'; +import { LIST_LOADING_STATUS } from '../../../utils/listLoading'; +import Toast from 'tdesign-miniprogram/toast/index'; + +const initFilters = { + overall: 1, + sorts: '', + layout: 0, +}; + +Page({ + data: { + goodsList: [], + layout: 0, + sorts: '', + overall: 1, + show: false, + minVal: '', + maxVal: '', + filter: initFilters, + hasLoaded: false, + loadMoreStatus: 0, + loading: true, + goodsListLoadStatus: LIST_LOADING_STATUS.READY, + }, + + pageNum: 1, + pageSize: 30, + total: 0, + + handleFilterChange(e) { + const { layout, overall, sorts } = e.detail; + this.pageNum = 1; + this.setData({ + layout, + sorts, + overall, + loadMoreStatus: 0, + }); + this.init(true); + }, + + generalQueryData(reset = false) { + const { filter, keywords, minVal, maxVal } = this.data; + const { pageNum, pageSize } = this; + const { sorts, overall } = filter; + const params = { + sort: 0, // 0 综合,1 价格 + pageNum: 1, + pageSize: 30, + keyword: keywords, + }; + + if (sorts) { + params.sort = 1; + params.sortType = sorts === 'desc' ? 1 : 0; + } + + if (overall) { + params.sort = 0; + } else { + params.sort = 1; + } + params.minPrice = minVal ? minVal * 100 : 0; + params.maxPrice = maxVal ? maxVal * 100 : undefined; + if (reset) return params; + return { + ...params, + pageNum: pageNum + 1, + pageSize, + }; + }, + + async init(reset = true) { + this.loadGoodsList(reset); + }, + + async loadGoodsList() { + this.setData({ goodsListLoadStatus: LIST_LOADING_STATUS.LOADING }); + + try { + const { spu: goodsList } = await getAllSpuOfCate(this.cateId); + goodsList.sort((a, b) => (b?.priority ?? 0) - (a?.priority ?? 0)); + const images = goodsList.map((x) => x.cover_image); + const handledImages = await getCloudImageTempUrl(images); + handledImages.forEach((image, index) => (goodsList[index].cover_image = image)); + await Promise.all(goodsList.map(async (spu) => (spu.price = await getPrice(spu._id)))); + + this.setData({ + goodsList, + // TODO: add pagination + goodsListLoadStatus: LIST_LOADING_STATUS.NO_MORE, + }); + } catch (err) { + console.error('error', err); + this.setData({ goodsListLoadStatus: LIST_LOADING_STATUS.FAILED }); + } + }, + cateId: null, + + onLoad(query) { + const { cateId } = query; + if (typeof cateId !== 'string') return; + + this.cateId = cateId; + this.init(true); + }, + + onReachBottom() { + const { goodsList } = this.data; + const { total = 0 } = this; + if (goodsList.length === total) { + this.setData({ + loadMoreStatus: 2, + }); + return; + } + this.init(false); + }, + + handleAddCart() { + Toast({ + context: this, + selector: '#t-toast', + message: '点击加购', + }); + }, + + tagClickHandle() { + Toast({ + context: this, + selector: '#t-toast', + message: '点击标签', + }); + }, + + gotoGoodsDetail(e) { + const spuId = e?.detail?.goods?._id; + if (typeof spuId !== 'string') return; + + wx.navigateTo({ + url: `/pages/goods/details/index?spuId=${spuId}`, + }); + }, + + showFilterPopup() { + this.setData({ + show: true, + }); + }, + + showFilterPopupClose() { + this.setData({ + show: false, + }); + }, + + onMinValAction(e) { + const { value } = e.detail; + this.setData({ minVal: value }); + }, + + onMaxValAction(e) { + const { value } = e.detail; + this.setData({ maxVal: value }); + }, + + reset() { + this.setData({ minVal: '', maxVal: '' }); + }, + + confirm() { + const { minVal, maxVal } = this.data; + let message = ''; + if (minVal && !maxVal) { + message = `价格最小是${minVal}`; + } else if (!minVal && maxVal) { + message = `价格范围是0-${minVal}`; + } else if (minVal && maxVal && minVal <= maxVal) { + message = `价格范围${minVal}-${this.data.maxVal}`; + } else { + message = '请输入正确范围'; + } + if (message) { + Toast({ + context: this, + selector: '#t-toast', + message, + }); + } + this.pageNum = 1; + this.setData( + { + show: false, + minVal: '', + goodsList: [], + loadMoreStatus: 0, + maxVal: '', + }, + () => { + this.init(); + }, + ); + }, +}); diff --git a/miniprogram/tcb-shop/pages/goods/list/index.json b/miniprogram/tcb-shop/pages/goods/list/index.json new file mode 100644 index 0000000..0811511 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/list/index.json @@ -0,0 +1,12 @@ +{ + "navigationBarTitleText": "商品列表", + "usingComponents": { + "t-input": "tdesign-miniprogram/input/input", + "t-empty": "tdesign-miniprogram/empty/empty", + "t-toast": "tdesign-miniprogram/toast/toast", + "goods-list": "/components/goods-list/index", + "filter": "/components/filter/index", + "filter-popup": "/components/filter-popup/index", + "load-more": "/components/load-more/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/list/index.wxml b/miniprogram/tcb-shop/pages/goods/list/index.wxml new file mode 100644 index 0000000..32c4b1f --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/list/index.wxml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/list/index.wxss b/miniprogram/tcb-shop/pages/goods/list/index.wxss new file mode 100644 index 0000000..5073012 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/list/index.wxss @@ -0,0 +1,99 @@ +page { + background-color: #fff; +} + +.goods-list-container { + display: block; +} + +.goods-list-container .t-search { + padding: 0 30rpx; + background-color: #fff; +} + +.goods-list-container .t-class__input-container { + height: 64rpx !important; + border-radius: 32rpx !important; +} + +.goods-list-container .t-search__left-icon { + display: flex; + align-items: center; +} + +.goods-list-container .t-search__input { + font-size: 28rpx !important; + color: rgb(116, 116, 116) !important; +} + +.goods-list-container .category-goods-list { + background-color: #f2f2f2; + overflow-y: scroll; + -webkit-overflow-scrolling: touch; + padding: 20rpx 24rpx; + -webkit-overflow-scrolling: touch; +} + +.goods-list-container .wr-goods-list { + background: #f2f2f2 !important; +} + +.goods-list-container .t-image__mask { + display: flex !important; +} + +.goods-list-container .empty-wrap { + margin-top: 184rpx; + margin-bottom: 120rpx; + height: 300rpx; +} + +.goods-list-container .empty-wrap .empty-tips .empty-content .content-text { + margin-top: 40rpx; +} + +.goods-list-container .price-container { + padding: 32rpx; + height: 100vh; + max-width: 632rpx; + background-color: #fff; + border-radius: 30rpx 0 0 30rpx; + box-sizing: border-box; +} + +.goods-list-container .price-between { + font-size: 26rpx; + font-weight: 500; + color: rgba(51, 51, 51, 1); +} + +.goods-list-container .price-ipts-wrap { + width: 100%; + display: flex; + align-items: center; + justify-content: space-around; + margin-top: 24rpx; + + --td-input-bg-color: rgba(245, 245, 245, 1); + --td-input-vertical-padding: 4rpx; + --td-input-border-color: transparent; +} + +.goods-list-container .price-ipts-wrap .price-divided { + width: 16rpx; + margin: 0 24rpx; + color: #333333; +} + +.goods-list-container .price-ipts-wrap .t-input__wrapper { + margin: 0 !important; +} + +.goods-list-container .price-ipts-wrap .t-input__content, +.goods-list-container .price-ipts-wrap .t-input__placeholder { + font-size: 24rpx !important; +} + +.goods-list-container .price-ipts-wrap .price-ipt { + border-radius: 8rpx; +} diff --git a/miniprogram/tcb-shop/pages/goods/result/index.js b/miniprogram/tcb-shop/pages/goods/result/index.js new file mode 100644 index 0000000..bca4c21 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/result/index.js @@ -0,0 +1,242 @@ +/* eslint-disable no-param-reassign */ +import Toast from 'tdesign-miniprogram/toast/index'; +import { listGood, getPrice } from '../../../services/good/spu'; +import { getCloudImageTempUrl } from '../../../utils/cloudImageHandler'; +import { LIST_LOADING_STATUS } from '../../../utils/listLoading'; + +const initFilters = { + overall: 1, + sorts: '', +}; + +Page({ + data: { + goodsList: [], + sorts: '', + overall: 1, + show: false, + minVal: '', + maxVal: '', + minSalePriceFocus: false, + maxSalePriceFocus: false, + filter: initFilters, + hasLoaded: false, + keywords: '', + loadMoreStatus: 0, + loading: true, + }, + + total: 0, + pageNum: 1, + pageSize: 30, + + onLoad(options) { + const { searchValue = '' } = options || {}; + this.setData( + { + keywords: searchValue, + }, + () => { + this.init(true); + }, + ); + }, + + generalQueryData(reset = false) { + const { filter, keywords, minVal, maxVal } = this.data; + const { pageNum, pageSize } = this; + const { sorts, overall } = filter; + const params = { + sort: 0, // 0 综合,1 价格 + pageNum: 1, + pageSize: 30, + keyword: keywords, + }; + + if (sorts) { + params.sort = 1; + params.sortType = sorts === 'desc' ? 1 : 0; + } + if (overall) { + params.sort = 0; + } else { + params.sort = 1; + } + params.minPrice = minVal ? minVal * 100 : 0; + params.maxPrice = maxVal ? maxVal * 100 : undefined; + if (reset) return params; + return { + ...params, + pageNum: pageNum + 1, + pageSize, + }; + }, + + async init(reset = true) { + return this.loadGoodsList(reset); + }, + + async loadGoodsList(fresh = false) { + this.setData({ loadMoreStatus: LIST_LOADING_STATUS.LOADING }); + + const pageSize = this.pageSize; + const pageIndex = fresh ? 1 : this.pageNum; + + try { + const { records: nextList, total } = await listGood({ + pageNumber: pageIndex, + pageSize, + search: this.data.keywords, + }); + const images = nextList.map((x) => x.cover_image); + const handledImages = await getCloudImageTempUrl(images); + handledImages.forEach((image, index) => (nextList[index].cover_image = image)); + await Promise.all(nextList.map(async (spu) => (spu.price = await getPrice(spu._id)))); + + const goodsList = fresh ? nextList : this.data.goodsList.concat(nextList); + + this.setData({ + goodsList, + loadMoreStatus: goodsList.length >= total ? LIST_LOADING_STATUS.NO_MORE : LIST_LOADING_STATUS.READY, + hasLoaded: true, + }); + + this.pageNum = pageIndex + 1; + } catch (err) { + console.log('error', err); + this.setData({ loadMoreStatus: LIST_LOADING_STATUS.FAILED }); + } + }, + + handleCartTap() { + wx.switchTab({ + url: '/pages/cart/index', + }); + }, + + handleSubmit(e) { + const keywords = e?.detail?.value ?? ''; + this.setData( + { + keywords, + goodsList: [], + loadMoreStatus: 0, + hasLoaded: false, + }, + () => { + this.init(true); + }, + ); + }, + + onReachBottom() { + const { goodsList } = this.data; + const { total = 0 } = this; + if (goodsList.length === total) { + this.setData({ + loadMoreStatus: 2, + }); + return; + } + this.init(false); + }, + + handleAddCart() { + Toast({ + context: this, + selector: '#t-toast', + message: '点击加购', + }); + }, + + gotoGoodsDetail(e) { + wx.navigateTo({ + url: `/pages/goods/details/index?spuId=${e.detail.goods._id}`, + }); + }, + + handleFilterChange(e) { + const { overall, sorts } = e.detail; + const { total } = this; + const _filter = { + sorts, + overall, + }; + this.setData({ + filter: _filter, + sorts, + overall, + }); + + this.pageNum = 1; + this.setData( + { + goodsList: [], + loadMoreStatus: 0, + }, + () => { + total && this.init(true); + }, + ); + }, + + showFilterPopup() { + this.setData({ + show: true, + }); + }, + + showFilterPopupClose() { + this.setData({ + show: false, + }); + }, + + onMinValAction(e) { + const { value } = e.detail; + this.setData({ minVal: value }); + }, + + onMaxValAction(e) { + const { value } = e.detail; + this.setData({ maxVal: value }); + }, + + reset() { + this.setData({ minVal: '', maxVal: '' }); + }, + + confirm() { + const { minVal, maxVal } = this.data; + let message = ''; + if (minVal && !maxVal) { + message = `价格最小是${minVal}`; + } else if (!minVal && maxVal) { + message = `价格范围是0-${minVal}`; + } else if (minVal && maxVal && minVal <= maxVal) { + message = `价格范围${minVal}-${this.data.maxVal}`; + } else { + message = '请输入正确范围'; + } + if (message) { + Toast({ + context: this, + selector: '#t-toast', + message, + }); + } + this.pageNum = 1; + this.setData( + { + show: false, + minVal: '', + goodsList: [], + loadMoreStatus: 0, + maxVal: '', + }, + () => { + this.init(); + }, + ); + }, +}); diff --git a/miniprogram/tcb-shop/pages/goods/result/index.json b/miniprogram/tcb-shop/pages/goods/result/index.json new file mode 100644 index 0000000..130b48d --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/result/index.json @@ -0,0 +1,15 @@ +{ + "navigationBarTitleText": "搜索", + "usingComponents": { + "t-search": "tdesign-miniprogram/search/search", + "t-input": "tdesign-miniprogram/input/input", + "t-empty": "tdesign-miniprogram/empty/empty", + "t-toast": "tdesign-miniprogram/toast/toast", + "goods-list": "/components/goods-list/index", + "filter": "/components/filter/index", + "filter-popup": "/components/filter-popup/index", + "load-more": "/components/load-more/index", + "t-icon": "tdesign-miniprogram/icon/icon" + }, + "onReachBottomDistance": 50 +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/result/index.wxml b/miniprogram/tcb-shop/pages/goods/result/index.wxml new file mode 100644 index 0000000..723ef03 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/result/index.wxml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/result/index.wxss b/miniprogram/tcb-shop/pages/goods/result/index.wxss new file mode 100644 index 0000000..5fee10e --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/result/index.wxss @@ -0,0 +1,114 @@ +page { + background-color: #fff; +} + +page view { + box-sizing: border-box; +} + +.result-container { + display: block; +} + +.result-container .t-search { + padding: 0 30rpx; + background-color: #fff; +} + +.result-container .t-class__input-container { + height: 64rpx !important; + border-radius: 32rpx !important; +} + +.result-container .t-search__left-icon { + display: flex; + align-items: center; +} + +.result-container .t-search__input { + font-size: 28rpx !important; + color: #333 !important; +} + +.result-container .category-goods-list { + background-color: #f2f2f2; + overflow-y: scroll; + padding: 20rpx 24rpx; + -webkit-overflow-scrolling: touch; +} + +.result-container .wr-goods-list { + background: #f2f2f2 !important; +} + +.result-container .t-image__mask { + display: flex !important; +} + +.result-container .empty-wrap { + margin-top: 184rpx; + margin-bottom: 120rpx; + height: 300rpx; +} + +.result-container .empty-wrap .empty-tips .empty-content .content-text { + margin-top: 40rpx; +} + +.result-container .price-container { + padding: 32rpx; + height: 100vh; + max-width: 632rpx; + background-color: #fff; + border-radius: 30rpx 0 0 30rpx; +} + +.result-container .price-between { + font-size: 26rpx; + font-weight: 500; + color: rgba(51, 51, 51, 1); +} + +.result-container .price-ipts-wrap { + width: 100%; + display: flex; + flex-direction: row; + justify-content: space-around; + margin-top: 24rpx; +} + +.result-container .price-ipts-wrap .price-divided { + position: relative; + width: 22rpx; + margin: 0 20rpx; + color: #222427; +} + +.result-container .price-ipts-wrap .price-ipt { + box-sizing: border-box; + width: 246rpx; + font-size: 24rpx; + height: 56rpx; + padding: 0 24rpx; + text-align: center; + border-radius: 8rpx; + color: #333; + background: rgba(245, 245, 245, 1); +} + +.t-class-input { + font-size: 24rpx !important; +} + +.t-search__clear { + font-size: 40rpx !important; +} + +.result-container .price-ipts-wrap .price-ipt::after { + border: none !important; +} + +.result-container .t-input__control { + font-size: 24rpx !important; + text-align: center; +} diff --git a/miniprogram/tcb-shop/pages/goods/search/index.js b/miniprogram/tcb-shop/pages/goods/search/index.js new file mode 100644 index 0000000..1adf407 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/search/index.js @@ -0,0 +1,116 @@ +import { getSearchHistory, getSearchPopular } from '../../../services/good/fetchSearchHistory'; + +Page({ + data: { + historyWords: [], + popularWords: [], + searchValue: '', + dialog: { + title: '确认删除当前历史记录', + showCancelButton: true, + message: '', + }, + dialogShow: false, + }, + + deleteType: 0, + deleteIndex: '', + + onShow() { + this.queryHistory(); + this.queryPopular(); + }, + + async queryHistory() { + try { + const data = await getSearchHistory(); + const code = 'Success'; + if (String(code).toUpperCase() === 'SUCCESS') { + const { historyWords = [] } = data; + this.setData({ + historyWords, + }); + } + } catch (error) { + console.error(error); + } + }, + + async queryPopular() { + try { + const data = await getSearchPopular(); + const code = 'Success'; + if (String(code).toUpperCase() === 'SUCCESS') { + const { popularWords = [] } = data; + this.setData({ + popularWords, + }); + } + } catch (error) { + console.error(error); + } + }, + + confirm() { + const { historyWords } = this.data; + const { deleteType, deleteIndex } = this; + historyWords.splice(deleteIndex, 1); + if (deleteType === 0) { + this.setData({ + historyWords, + dialogShow: false, + }); + } else { + this.setData({ historyWords: [], dialogShow: false }); + } + }, + + close() { + this.setData({ dialogShow: false }); + }, + + handleClearHistory() { + const { dialog } = this.data; + this.deleteType = 1; + this.setData({ + dialog: { + ...dialog, + message: '确认删除所有历史记录', + }, + dialogShow: true, + }); + }, + + deleteCurr(e) { + const { index } = e.currentTarget.dataset; + const { dialog } = this.data; + this.deleteIndex = index; + this.setData({ + dialog: { + ...dialog, + message: '确认删除当前历史记录', + deleteType: 0, + }, + dialogShow: true, + }); + }, + + handleHistoryTap(e) { + const { historyWords } = this.data; + const { dataset } = e.currentTarget; + const _searchValue = historyWords[dataset.index || 0] || ''; + if (_searchValue) { + wx.navigateTo({ + url: `/pages/goods/result/index?searchValue=${_searchValue}`, + }); + } + }, + + handleSubmit(e) { + const value = e?.detail?.value; + if (!value) return; + wx.navigateTo({ + url: `/pages/goods/result/index?searchValue=${value}`, + }); + }, +}); diff --git a/miniprogram/tcb-shop/pages/goods/search/index.json b/miniprogram/tcb-shop/pages/goods/search/index.json new file mode 100644 index 0000000..74abdf7 --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/search/index.json @@ -0,0 +1,8 @@ +{ + "navigationBarTitleText": "搜索", + "usingComponents": { + "t-search": "tdesign-miniprogram/search/search", + "t-icon": "tdesign-miniprogram/icon/icon", + "t-dialog": "tdesign-miniprogram/dialog/dialog" + } +} diff --git a/miniprogram/tcb-shop/pages/goods/search/index.wxml b/miniprogram/tcb-shop/pages/goods/search/index.wxml new file mode 100644 index 0000000..59dd11f --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/search/index.wxml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/goods/search/index.wxss b/miniprogram/tcb-shop/pages/goods/search/index.wxss new file mode 100644 index 0000000..676603e --- /dev/null +++ b/miniprogram/tcb-shop/pages/goods/search/index.wxss @@ -0,0 +1,79 @@ +.search-page { + box-sizing: border-box; + width: 100vw; + height: 100vh; + padding: 0 30rpx; +} + +.search-page .t-class__input-container { + height: 64rpx !important; + border-radius: 32rpx !important; +} + +.search-page .t-search__input { + font-size: 28rpx !important; + color: #333 !important; +} + +.search-page .search-wrap { + margin-top: 44rpx; +} + +.search-page .history-wrap { + margin-bottom: 20px; +} + +.search-page .search-header { + display: flex; + flex-flow: row nowrap; + justify-content: space-between; + align-items: center; +} + +.search-page .search-title { + font-size: 30rpx; + font-family: PingFangSC-Semibold, PingFang SC; + font-weight: 600; + color: rgba(51, 51, 51, 1); + line-height: 42rpx; +} + +.search-page .search-clear { + font-size: 24rpx; + font-family: PingFang SC; + line-height: 32rpx; + color: #999999; + font-weight: normal; +} + +.search-page .search-content { + overflow: hidden; + display: flex; + flex-flow: row wrap; + justify-content: flex-start; + align-items: flex-start; + margin-top: 24rpx; +} + +.search-page .search-item { + color: #333333; + font-size: 24rpx; + line-height: 32rpx; + font-weight: normal; + margin-right: 24rpx; + margin-bottom: 24rpx; + background: #f5f5f5; + border-radius: 38rpx; + padding: 12rpx 24rpx; +} + +.search-page .hover-history-item { + position: relative; + top: 3rpx; + left: 3rpx; + box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.1) inset; +} + +.add-notes__confirm { + color: #fa4126 !important; +} diff --git a/miniprogram/tcb-shop/pages/home/home.js b/miniprogram/tcb-shop/pages/home/home.js new file mode 100644 index 0000000..f5412dd --- /dev/null +++ b/miniprogram/tcb-shop/pages/home/home.js @@ -0,0 +1,135 @@ +/* eslint-disable no-param-reassign */ +import { getHomeSwiper } from '../../services/home/home'; +import { listGood, getPrice } from '../../services/good/spu'; +import { getCloudImageTempUrl } from '../../utils/cloudImageHandler'; +import { LIST_LOADING_STATUS } from '../../utils/listLoading'; + +Page({ + data: { + imgSrcs: [], + tabList: [], + goodsList: [], + goodsListLoadStatus: LIST_LOADING_STATUS.READY, + pageLoading: false, + current: 1, + autoplay: true, + duration: '500', + interval: 5000, + navigation: { type: 'dots' }, + swiperImageProps: { mode: 'scaleToFill' }, + }, + + goodListPagination: { + index: 1, + num: 20, + }, + + privateData: { + tabIndex: 0, + }, + + onShow() { + this.getTabBar().init(); + }, + + onLoad() { + this.init(); + }, + + onReachBottom() { + if (this.data.goodsListLoadStatus === LIST_LOADING_STATUS.READY) { + this.loadGoodsList(); + } + }, + + onPullDownRefresh() { + this.init(); + }, + + async init() { + wx.stopPullDownRefresh(); + + this.setData({ + pageLoading: false, + }); + + this.loadGoodsList(true); + this.loadHomeSwiper(); + }, + + async loadHomeSwiper() { + const { images } = await getHomeSwiper(); + const imgSrcs = ( + await wx.cloud.getTempFileURL({ + fileList: images, + }) + ).fileList.map((x) => x.tempFileURL); + + this.setData({ imgSrcs }); + }, + + onReTry() { + this.loadGoodsList(); + }, + + async loadGoodsList(fresh = false) { + if (fresh) { + wx.pageScrollTo({ + scrollTop: 0, + }); + } + + this.setData({ goodsListLoadStatus: LIST_LOADING_STATUS.LOADING }); + + const pageSize = this.goodListPagination.num; + const pageIndex = fresh ? 1 : this.goodListPagination.num; + + try { + const { records: nextList, total } = await listGood({ pageNumber: pageIndex, pageSize }); + const images = nextList.map((x) => x.cover_image); + const handledImages = await getCloudImageTempUrl(images); + handledImages.forEach((image, index) => (nextList[index].cover_image = image)); + await Promise.all(nextList.map(async (spu) => (spu.price = await getPrice(spu._id)))); + + const goodsList = fresh ? nextList : this.data.goodsList.concat(nextList); + + this.setData({ + goodsList, + goodsListLoadStatus: goodsList.length >= total ? LIST_LOADING_STATUS.NO_MORE : LIST_LOADING_STATUS.READY, + }); + + this.goodListPagination.index = pageIndex + 1; + this.goodListPagination.num = pageSize; + } catch (err) { + console.error('error', err); + this.setData({ goodsListLoadStatus: LIST_LOADING_STATUS.FAILED }); + } + }, + + goodListClickHandle(e) { + const spuId = e?.detail?.goods?._id; + if (typeof spuId !== 'string') return; + wx.navigateTo({ + url: `/pages/goods/details/index?spuId=${spuId}`, + }); + }, + + goodListAddCartHandle(e) { + const spuId = e?.detail?.goods?._id; + if (typeof spuId !== 'string') return; + wx.navigateTo({ + url: `/pages/goods/details/index?spuId=${spuId}`, + }); + }, + + navToSearchPage() { + wx.navigateTo({ url: '/pages/goods/search/index' }); + }, + + navToActivityDetail({ detail }) { + const { index: promotionID = 0 } = detail || {}; + wx.navigateTo({ + url: `/pages/promotion-detail/index?promotion_id=${promotionID}`, + }); + }, +}); diff --git a/miniprogram/tcb-shop/pages/home/home.json b/miniprogram/tcb-shop/pages/home/home.json new file mode 100644 index 0000000..8df2da4 --- /dev/null +++ b/miniprogram/tcb-shop/pages/home/home.json @@ -0,0 +1,20 @@ +{ + "navigationBarTitleText": "首页", + "onReachBottomDistance": 10, + "backgroundTextStyle": "light", + "enablePullDownRefresh": true, + "usingComponents": { + "t-search": "tdesign-miniprogram/search/search", + "t-loading": "tdesign-miniprogram/loading/loading", + "t-swiper": "tdesign-miniprogram/swiper/swiper", + "t-swiper-nav": "tdesign-miniprogram/swiper-nav/swiper-nav", + "t-image": "/components/webp-image/index", + "t-icon": "tdesign-miniprogram/icon/icon", + "t-toast": "tdesign-miniprogram/toast/toast", + "t-tabs": "tdesign-miniprogram/tabs/tabs", + "t-tab-panel": "tdesign-miniprogram/tab-panel/tab-panel", + "goods-list": "/components/goods-list/index", + "load-more": "/components/load-more/index", + "cloud-template-dialog": "/components/cloud-template-dialog/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/home/home.wxml b/miniprogram/tcb-shop/pages/home/home.wxml new file mode 100644 index 0000000..b2bff61 --- /dev/null +++ b/miniprogram/tcb-shop/pages/home/home.wxml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/home/home.wxss b/miniprogram/tcb-shop/pages/home/home.wxss new file mode 100644 index 0000000..e0e7d6f --- /dev/null +++ b/miniprogram/tcb-shop/pages/home/home.wxss @@ -0,0 +1,107 @@ +page { + box-sizing: border-box; + padding-bottom: calc(env(safe-area-inset-bottom) + 96rpx); +} + +.t-tabs.t-tabs--top .t-tabs__scroll { + border-bottom: none !important; +} + +.home-page-header { + background: linear-gradient(#fff, #f5f5f5); +} + +.home-page-container { + background: #f5f5f5; +} + +.home-page-container, +.home-page-header { + display: block; + padding: 0 24rpx; +} + +.home-page-header .t-search__input-container { + border-radius: 32rpx !important; + height: 64rpx !important; +} + +.home-page-header .t-search__input { + font-size: 28rpx !important; + color: rgb(116, 116, 116) !important; +} + +.home-page-header .swiper-wrap { + margin-top: 20rpx; +} + +.home-page-header .t-image__swiper { + width: 100%; + height: 300rpx; + border-radius: 10rpx; +} + +.home-page-container .t-tabs { + background: #f5f5f5 !important; +} + +.home-page-container .t-tabs .t-tabs-nav { + background-color: transparent; + line-height: 80rpx; + font-size: 28rpx; + color: #333; +} + +.home-page-container .t-tabs .t-tabs-scroll { + border: none !important; +} + +/* 半个字 */ +.home-page-container .tab.order-nav .order-nav-item.scroll-width { + min-width: 165rpx; +} +.home-page-container .tab .order-nav-item.active { + color: #fa550f !important; +} + +.home-page-container .tab .bottom-line { + border-radius: 4rpx; +} + +.home-page-container .tab .order-nav-item.active .bottom-line { + background-color: #fa550f !important; +} + +.home-page-container .tabs-external__item { + /* color: #666 !important; */ + font-size: 28rpx; +} + +.home-page-container .tabs-external__active { + color: #333333 !important; + font-size: 32rpx; +} + +.home-page-container .tabs-external__track { + /* background-color: #fa4126 !important; */ + height: 6rpx !important; + border-radius: 4rpx !important; + width: 48rpx !important; +} + +.t-tabs.t-tabs--top .t-tabs__item, +.t-tabs.t-tabs--bottom .t-tabs__item { + height: 86rpx !important; +} + +.home-page-container .goods-list-container { + background: #f5f5f5 !important; + margin-top: 16rpx; +} + +.home-page-tabs { + --td-tab-nav-bg-color: transparent; + --td-tab-border-color: transparent; + --td-tab-item-color: #666; + --td-tab-track-color: red; +} diff --git a/miniprogram/tcb-shop/pages/home/readme b/miniprogram/tcb-shop/pages/home/readme new file mode 100644 index 0000000..fcf75d5 --- /dev/null +++ b/miniprogram/tcb-shop/pages/home/readme @@ -0,0 +1,8 @@ +首页功能设定 +1. loading入场 +2. 下拉刷新 +3. 搜索栏 +4. 分类切换 +5. 商品列表 +6. 规格弹层 +7. 加载更多 \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/after-service-detail/api.js b/miniprogram/tcb-shop/pages/order/after-service-detail/api.js new file mode 100644 index 0000000..22d62fe --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/after-service-detail/api.js @@ -0,0 +1,34 @@ +import { resp } from '../after-service-list/api'; +import dayjs from 'dayjs'; +import { mockIp, mockReqId } from '../../../utils/mock'; + +export const formatTime = (date, template) => dayjs(date).format(template); + +export function getRightsDetail({ rightsNo }) { + const _resq = { + data: {}, + code: 'Success', + msg: null, + requestId: mockReqId(), + clientIp: mockIp(), + rt: 79, + success: true, + }; + _resq.data = + resp.data.dataList.filter((item) => item.rights.rightsNo === rightsNo) || + {}; + return Promise.resolve(_resq); +} + +export function cancelRights() { + const _resq = { + data: {}, + code: 'Success', + msg: null, + requestId: mockReqId(), + clientIp: mockIp(), + rt: 79, + success: true, + }; + return Promise.resolve(_resq); +} diff --git a/miniprogram/tcb-shop/pages/order/after-service-detail/index.js b/miniprogram/tcb-shop/pages/order/after-service-detail/index.js new file mode 100644 index 0000000..d90dba4 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/after-service-detail/index.js @@ -0,0 +1,205 @@ +import Toast from 'tdesign-miniprogram/toast/index'; +import { ServiceType, ServiceTypeDesc, ServiceStatus } from '../config'; +import { formatTime, getRightsDetail } from './api'; + +const TitleConfig = { + [ServiceType.ORDER_CANCEL]: '退款详情', + [ServiceType.ONLY_REFUND]: '退款详情', + [ServiceType.RETURN_GOODS]: '退货退款详情', +}; + +Page({ + data: { + pageLoading: true, + serviceRaw: {}, + service: {}, + deliveryButton: {}, + gallery: { + current: 0, + show: false, + proofs: [], + }, + showProofs: false, + backRefresh: false, + }, + + onLoad(query) { + this.rightsNo = query.rightsNo; + this.inputDialog = this.selectComponent('#input-dialog'); + this.init(); + }, + + onShow() { + // 当从其他页面返回,并且 backRefresh 被置为 true 时,刷新数据 + if (!this.data.backRefresh) return; + this.init(); + this.setData({ backRefresh: false }); + }, + + // 页面刷新,展示下拉刷新 + onPullDownRefresh_(e) { + const { callback } = e.detail; + return this.getService().then(() => callback && callback()); + }, + + init() { + this.setData({ pageLoading: true }); + this.getService().then(() => { + this.setData({ pageLoading: false }); + }); + }, + + getService() { + const params = { rightsNo: this.rightsNo }; + return getRightsDetail(params).then((res) => { + const serviceRaw = res.data[0]; + // 滤掉填写运单号、修改运单号按钮,这两个按钮特殊处理,不在底部按钮栏展示 + if (!serviceRaw.buttonVOs) serviceRaw.buttonVOs = []; + const deliveryButton = {}; + const service = { + id: serviceRaw.rights.rightsNo, + serviceNo: serviceRaw.rights.rightsNo, + storeName: serviceRaw.rights.storeName, + type: serviceRaw.rights.rightsType, + typeDesc: ServiceTypeDesc[serviceRaw.rights.rightsType], + status: serviceRaw.rights.rightsStatus, + statusIcon: this.genStatusIcon(serviceRaw.rights), + statusName: serviceRaw.rights.userRightsStatusName, + statusDesc: serviceRaw.rights.userRightsStatusDesc, + amount: serviceRaw.rights.refundRequestAmount, + goodsList: (serviceRaw.rightsItem || []).map((item, i) => ({ + id: i, + thumb: item.goodsPictureUrl, + title: item.goodsName, + specs: (item.specInfo || []).map((s) => s.specValues || ''), + itemRefundAmount: item.itemRefundAmount, + rightsQuantity: item.rightsQuantity, + })), + orderNo: serviceRaw.rights.orderNo, // 订单编号 + rightsNo: serviceRaw.rights.rightsNo, // 售后服务单号 + rightsReasonDesc: serviceRaw.rights.rightsReasonDesc, // 申请售后原因 + isRefunded: serviceRaw.rights.userRightsStatus === ServiceStatus.REFUNDED, // 是否已退款 + refundMethodList: (serviceRaw.refundMethodList || []).map((m) => ({ + name: m.refundMethodName, + amount: m.refundMethodAmount, + })), // 退款明细 + refundRequestAmount: serviceRaw.rights.refundRequestAmount, // 申请退款金额 + payTraceNo: serviceRaw.rightsRefund.traceNo, // 交易流水号 + createTime: formatTime(parseFloat(`${serviceRaw.rights.createTime}`), 'YYYY-MM-DD HH:mm'), // 申请时间 + logisticsNo: serviceRaw.logisticsVO.logisticsNo, // 退货物流单号 + logisticsCompanyName: serviceRaw.logisticsVO.logisticsCompanyName, // 退货物流公司 + logisticsCompanyCode: serviceRaw.logisticsVO.logisticsCompanyCode, // 退货物流公司 + remark: serviceRaw.logisticsVO.remark, // 退货备注 + receiverName: serviceRaw.logisticsVO.receiverName, // 收货人 + receiverPhone: serviceRaw.logisticsVO.receiverPhone, // 收货人电话 + receiverAddress: this.composeAddress(serviceRaw), // 收货人地址 + applyRemark: serviceRaw.rightsRefund.refundDesc, // 申请退款时的填写的说明 + buttons: serviceRaw.buttonVOs || [], + logistics: serviceRaw.logisticsVO, + }; + const proofs = serviceRaw.rights.rightsImageUrls || []; + this.setData({ + serviceRaw, + service, + deliveryButton, + 'gallery.proofs': proofs, + showProofs: + serviceRaw.rights.userRightsStatus === ServiceStatus.PENDING_VERIFY && + (service.applyRemark || proofs.length > 0), + }); + wx.setNavigationBarTitle({ + title: TitleConfig[service.type], + }); + }); + }, + + composeAddress(service) { + return [ + service.logisticsVO.receiverProvince, + service.logisticsVO.receiverCity, + service.logisticsVO.receiverCountry, + service.logisticsVO.receiverArea, + service.logisticsVO.receiverAddress, + ] + .filter((item) => !!item) + .join(' '); + }, + + onRefresh() { + this.init(); + }, + + editLogistices() { + this.setData({ + inputDialogVisible: true, + }); + this.inputDialog.setData({ + cancelBtn: '取消', + confirmBtn: '确定', + }); + this.inputDialog._onConfirm = () => { + Toast({ + message: '确定填写物流单号', + }); + }; + }, + + onProofTap(e) { + if (this.data.gallery.show) { + this.setData({ + 'gallery.show': false, + }); + return; + } + const { index } = e.currentTarget.dataset; + this.setData({ + 'gallery.show': true, + 'gallery.current': index, + }); + }, + + onGoodsCardTap(e) { + const { index } = e.currentTarget.dataset; + const goods = this.data.serviceRaw.rightsItem[index]; + wx.navigateTo({ url: `/pages/goods/details/index?skuId=${goods.skuId}` }); + }, + + onServiceNoCopy() { + wx.setClipboardData({ + data: this.data.service.serviceNo, + }); + }, + + onAddressCopy() { + wx.setClipboardData({ + data: `${this.data.service.receiverName} ${this.data.service.receiverPhone}\n${this.data.service.receiverAddress}`, + }); + }, + + /** 获取状态ICON */ + genStatusIcon(item) { + const { userRightsStatus, afterSaleRequireType } = item; + switch (userRightsStatus) { + // 退款成功 + case ServiceStatus.REFUNDED: { + return 'succeed'; + } + // 已取消、已关闭 + case ServiceStatus.CLOSED: { + return 'indent_close'; + } + default: { + switch (afterSaleRequireType) { + case 'REFUND_MONEY': { + return 'goods_refund'; + } + case 'REFUND_GOODS_MONEY': + return 'goods_return'; + default: { + return 'goods_return'; + } + } + } + } + }, +}); diff --git a/miniprogram/tcb-shop/pages/order/after-service-detail/index.json b/miniprogram/tcb-shop/pages/order/after-service-detail/index.json new file mode 100644 index 0000000..28e72ee --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/after-service-detail/index.json @@ -0,0 +1,21 @@ +{ + "navigationBarTitleText": "", + "usingComponents": { + "wr-loading-content": "/components/loading-content/index", + "wr-price": "/components/price/index", + "wr-service-goods-card": "../components/order-goods-card/index", + "t-cell": "tdesign-miniprogram/cell/cell", + "t-icon": "tdesign-miniprogram/icon/icon", + "t-cell-group": "tdesign-miniprogram/cell-group/cell-group", + "t-pull-down-refresh": "tdesign-miniprogram/pull-down-refresh/pull-down-refresh", + "t-grid": "tdesign-miniprogram/grid/grid", + "t-grid-item": "tdesign-miniprogram/grid-item/grid-item", + "t-toast": "tdesign-miniprogram/toast/toast", + "t-dialog": "tdesign-miniprogram/dialog/dialog", + "t-input": "tdesign-miniprogram/input/input", + "t-swiper": "tdesign-miniprogram/swiper/swiper", + "t-swiper-nav": "tdesign-miniprogram/swiper-nav/swiper-nav", + "wr-after-service-button-bar": "../components/after-service-button-bar/index", + "t-image": "/components/webp-image/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/after-service-detail/index.wxml b/miniprogram/tcb-shop/pages/order/after-service-detail/index.wxml new file mode 100644 index 0000000..9ad5f79 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/after-service-detail/index.wxml @@ -0,0 +1,197 @@ + + + + + + + + + + {{service.statusName}} + + {{service.statusDesc}} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 复制 + + + {{service.receiverAddress}} + 收货人:{{service.receiverName}} + 收货人手机:{{service.receiverName}} + + + + + + + + + + x {{goods.rightsQuantity}} + + + + + + + + + 复制 + + + + + + + + + + + + + + + + + + + + + + + 物流单号 + + {{amountTip}} + + + diff --git a/miniprogram/tcb-shop/pages/order/after-service-detail/index.wxss b/miniprogram/tcb-shop/pages/order/after-service-detail/index.wxss new file mode 100644 index 0000000..8431507 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/after-service-detail/index.wxss @@ -0,0 +1,435 @@ +:host { + background-color: #f5f5f5; +} + +.service-detail { + position: relative; +} + +.service-detail wr-service-goods-card .wr-goods-card__body { + margin-left: 50rpx; +} + +.order-goods-card-footer { + display: flex; + width: calc(100% - 190rpx); + justify-content: space-between; + position: absolute; + bottom: 20rpx; + left: 190rpx; +} + +.order-goods-card-footer-num { + color: #999; + line-height: 40rpx; +} + +.service-detail .order-goods-card-footer .order-goods-card-footer-price-class { + font-size: 36rpx; + color: #333; + font-family: DIN Alternate; +} + +.service-detail .order-goods-card-footer .order-goods-card-footer-price-decimal { + font-size: 28rpx; + color: #333; + font-family: DIN Alternate; +} + +.service-detail .order-goods-card-footer .order-goods-card-footer-price-symbol { + color: #333; + font-size: 24rpx; + font-family: DIN Alternate; +} + +.service-detail .service-detail__header { + padding: 60rpx 0 48rpx 40rpx; + box-sizing: border-box; + height: 220rpx; + background-color: #fff; +} +.service-detail .service-detail__header .title, +.service-detail .service-detail__header .desc { + overflow: hidden; + display: -webkit-box; + -webkit-box-orient: vertical; +} + +.service-detail .service-detail__header .title { + -webkit-line-clamp: 1; + font-size: 48rpx; + font-weight: bold; + color: #333; + display: flex; +} + +.service-detail .service-detail__header .desc { + -webkit-line-clamp: 2; + margin-top: 10rpx; + font-size: 28rpx; + color: #999; +} + +.service-detail .service-detail__header .desc .count-down { + color: #fff185; + display: inline; +} + +.service-detail .service-section { + margin: 20rpx 0 20rpx 0; + /* padding: 30rpx 32rpx; */ + width: auto; + border-radius: 8rpx; + background-color: white; + overflow: hidden; +} +.service-section__pay { + margin: 0 0 20rpx 0; + width: auto; + border-radius: 8rpx; + background-color: white; + overflow: hidden; +} +.service-detail .service-section__title { + color: #333333; + margin-bottom: 10rpx; + padding-bottom: 18rpx; + height: 224rpx; + position: relative; +} +.service-detail .service-section__title .icon { + margin-right: 16rpx; + font-size: 40rpx !important; +} +.service-detail .service-section__title .right { + flex: none; + font-weight: normal; + font-size: 26rpx; +} +.service-detail .section-content { + margin: 16rpx 0 0 52rpx; +} + +.service-detail .main { + font-size: 28rpx; + color: #222427; + font-weight: bold; +} + +.service-detail .main .phone-num { + margin-left: 16rpx; + display: inline; +} +.service-detail .label { + color: #999999; + font-size: 26rpx; +} + +.service-detail .custom-remark { + font-size: 26rpx; + line-height: 36rpx; + color: #333333; + word-wrap: break-word; +} +.service-detail .proofs { + margin-top: 20rpx; +} + +.service-detail .proofs .proof { + width: 100%; + height: 100%; + background-color: #f9f9f9; +} + +.service-detail .pay-result .t-cell-title, +.service-detail .pay-result .t-cell-value { + color: #666666; + font-size: 28rpx; +} + +.t-class-wrapper { + padding: 10rpx 24rpx !important; +} + +.t-class-wrapper-first-child { + padding: 24rpx !important; +} + +.service-detail .pay-result .wr-cell__value { + font-weight: bold; +} +.service-detail .right { + font-size: 36rpx; + color: #fa550f; + font-weight: bold; +} + +.service-detail .title { + font-weight: bold; +} + +.service-detail .pay-result .service-section__title .right.integer { + font-size: 48rpx; +} +.service-detail .pay-result .split-line { + position: relative; +} + +.service-detail .pay-result .split-line::after { + position: absolute; + display: block; + content: ' '; + height: 1px; + left: -50%; + right: -50%; + transform: scale(0.5); + background-color: #e6e6e6; +} + +.service-detail .pay-result .section-content { + margin-left: 0; +} + +.service-detail .pay-result .section-content .label { + color: #999999; + font-size: 24rpx; +} + +.service-detail .pay-result .wr-cell::after { + left: 0; +} + +.service-detail .footer-bar-wrapper { + height: 100rpx; +} + +.service-detail .footer-bar-wrapper .footer-bar { + position: fixed; + left: 0; + bottom: 0; + height: 100rpx; + width: 100vw; + box-sizing: border-box; + padding: 0 20rpx; + background-color: white; + display: flex; + justify-content: space-between; + align-items: center; +} + +.service-detail .text-btn { + display: inline; + box-sizing: border-box; + color: #333; + border: 2rpx solid #ddd; + border-radius: 32rpx; + margin-left: 10rpx; + padding: 0 16rpx; + font-weight: normal; + font-size: 24rpx; + line-height: 32rpx; +} +.service-detail .text-btn--active { + opacity: 0.5; +} + +.service-detail .specs-popup .bottom-btn { + color: #fa550f; +} +.service-detail .specs-popup .bottom-btn::after { + color: #fa550f; +} + +.dialog .dialog__button-confirm { + color: #fa550f; +} + +.page-container .order-goods-card > wr-goods-card .wr-goods-card__bottom .price { + top: 100rpx; + left: 10rpx; + position: absolute; + color: #333; +} + +.page-container .order-goods-card > wr-goods-card .wr-goods-card__num { + top: 100rpx; + right: 0; + position: absolute; +} + +.page-container .order-goods-card > wr-goods-card .wr-goods-card__bottom .price::before { + display: inline; + content: '退款金额:'; + margin-right: 1em; + font-size: 24rpx; + color: #333333; + font-weight: normal; +} + +.page-container .wr-goods-card__specs { + margin: 14rpx 20rpx 0 0; +} + +.page-container .order-goods-card > wr-goods-card .wr-goods-card__title { + margin-right: 0; + -webkit-line-clamp: 1; + display: -webkit-box; + -webkit-box-orient: vertical; + overflow: hidden; + width: 80%; +} + +.page-container .order-card .header .store-name { + -webkit-line-clamp: 1; + display: -webkit-box; + -webkit-box-orient: vertical; + overflow: hidden; + width: 80%; +} + +.page-container .status-desc { + box-sizing: border-box; + padding: 22rpx 20rpx; + font-size: 26rpx; + line-height: 1.3; + text-align: left; + color: #333333; + background-color: #f5f5f5; + border-radius: 8rpx; + word-wrap: break-word; + margin-top: 40rpx; + margin-bottom: 20rpx; +} + +.page-container .header__right { + font-size: 24rpx; + color: #333333; + display: flex; + align-items: center; +} + +.page-container .header__right__icon { + color: #d05b27; + font-size: 16px !important; + margin-right: 10rpx; +} + +.page-container .wr-goods-card__thumb { + width: 140rpx; +} +.page-container .page-background { + position: absolute; + z-index: -1; + top: 0; + left: 0; + width: 100vw; + color: #fff; + overflow: hidden; +} +.page-container .page-background-img { + width: 100%; + height: 320rpx !important; +} +.page-container .navbar-bg .nav-back, +.page-container .navbar-bg .page-background { + background: linear-gradient(to right, rgba(250, 85, 15, 1) 0%, rgba(250, 85, 15, 0.6) 100%) !important; +} + +.page-container .navigation-bar__btn { + font-size: 40rpx !important; + font-weight: bold !important; + color: #333; +} + +.t-class-title { + color: #000; +} + +.refresh-bar { + background: linear-gradient(90deg, #ff6b44 0%, #ed3427 100%) !important; +} + +.page-container .navigation-bar__inner .navigation-bar__left { + padding-left: 16rpx; +} + +.t-refund-info { + font-size: 26rpx; + color: #666; +} + +.t-refund-grid-image { + width: 212rpx !important; + height: 212rpx !important; +} + +.t-refund-info-img { + width: 100%; + height: 100%; +} + +.t-refund-wrapper { + padding-top: 18rpx !important; + padding-bottom: 18rpx !important; +} + +.t-refund-title { + font-size: 28rpx; + color: #333; + font-weight: bold; +} + +.t-refund-note { + font-size: 26rpx; + color: #333 !important; +} + +.service-detail .logistics { + padding-top: 0; + padding-bottom: 0; + padding-right: 0; +} + +.service-section__title__header { + display: flex; + align-items: center; + color: #333; + font-weight: normal; + font-size: 32rpx; +} + +.safe-bottom { + padding-bottom: env(safe-area-inset-bottom); +} + +.service-section-logistics { + display: flex; + justify-content: center; + color: #fa4126; + align-items: center; + margin-top: 24rpx; +} + +.t-class-indicator { + color: #b9b9b9 !important; +} + +.service-detail .goods-refund-address { + padding-top: 0; + padding-bottom: 0; +} + +.service-detail .goods-refund-address .goods-refund-address-copy-btn { + position: absolute; + top: 22rpx; + right: 32rpx; +} + +.service-detail .service-goods-card-wrap { + padding: 0 32rpx; +} + +.t-button { + --td-button-default-color: #000; + --td-button-primary-text-color: #fa4126; +} diff --git a/miniprogram/tcb-shop/pages/order/after-service-list/api.js b/miniprogram/tcb-shop/pages/order/after-service-list/api.js new file mode 100644 index 0000000..5350229 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/after-service-list/api.js @@ -0,0 +1,32 @@ +/* eslint-disable */ +import { mockIp, mockReqId } from '../../../utils/mock'; + +export const resp = { + data: { + pageNum: 1, + pageSize: 10, + totalCount: 51, + states: { + audit: 1, + approved: 6, + complete: 2, + closed: 1, + }, + dataList: [], + }, + code: 'Success', + msg: null, + requestId: mockReqId(), + clientIp: mockIp(), + rt: 79, + success: true, +}; + +export function getRightsList({ parameter: { afterServiceStatus, pageNum } }) { + const _resq = JSON.parse(JSON.stringify(resp)); + if (pageNum > 3) _resq.data.dataList = []; + if (afterServiceStatus > -1) { + _resq.data.dataList = _resq.data.dataList.filter((item) => item.rights.rightsStatus === afterServiceStatus); + } + return Promise.resolve(_resq); +} diff --git a/miniprogram/tcb-shop/pages/order/after-service-list/index.js b/miniprogram/tcb-shop/pages/order/after-service-list/index.js new file mode 100644 index 0000000..7cd0cd5 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/after-service-list/index.js @@ -0,0 +1,220 @@ +import { getRightsList } from './api'; +import { AfterServiceStatus, ServiceType, ServiceTypeDesc } from '../config'; + +Page({ + page: { + size: 10, + num: 1, + }, + + data: { + tabs: [ + { + key: -1, + text: '全部', + }, + { + key: AfterServiceStatus.TO_AUDIT, + text: '待审核', + }, + { + key: AfterServiceStatus.THE_APPROVED, + text: '已审核', + }, + { + key: AfterServiceStatus.COMPLETE, + text: '已完成', + }, + { + key: AfterServiceStatus.CLOSED, + text: '已关闭', + }, + ], + curTab: -1, + dataList: [], + listLoading: 0, // 0-未加载,1-加载中,2-已全部加载 + pullDownRefreshing: false, // 下拉刷新时不显示load-more + emptyImg: + 'https://cdn-we-retail.ym.tencent.com/miniapp/order/empty-order-list.png', + backRefresh: false, + }, + + onLoad(query) { + let status = parseInt(query.status); + status = this.data.tabs.map((t) => t.key).includes(status) ? status : -1; + this.init(status); + this.pullDownRefresh = this.selectComponent('#wr-pull-down-refresh'); + }, + + onShow() { + // 当从其他页面返回,并且 backRefresh 被置为 true 时,刷新数据 + if (!this.data.backRefresh) return; + this.onRefresh(); + this.setData({ + backRefresh: false, + }); + }, + + onReachBottom() { + if (this.data.listLoading === 0) { + this.getAfterServiceList(this.data.curTab); + } + }, + + onPageScroll(e) { + this.pullDownRefresh && this.pullDownRefresh.onPageScroll(e); + }, + + onPullDownRefresh_(e) { + const { callback } = e.detail; + this.setData({ + pullDownRefreshing: true, + }); // 下拉刷新时不显示load-more + this.refreshList(this.data.curTab) + .then(() => { + this.setData({ + pullDownRefreshing: false, + }); + callback && callback(); + }) + .catch((err) => { + this.setData({ + pullDownRefreshing: false, + }); + Promise.reject(err); + }); + }, + + init(status) { + status = status !== undefined ? status : this.data.curTab; + this.refreshList(status); + }, + + getAfterServiceList(statusCode = -1, reset = false) { + const params = { + parameter: { + pageSize: this.page.size, + pageNum: this.page.num, + }, + }; + if (statusCode !== -1) params.parameter.afterServiceStatus = statusCode; + this.setData({ + listLoading: 1, + }); + return getRightsList(params) + .then((res) => { + this.page.num++; + let dataList = []; + let { tabs } = this.data; + if (res && res.data && res.data.states) { + tabs = this.data.tabs.map((item) => { + switch (item.key) { + case AfterServiceStatus.TO_AUDIT: + item.info = res.data.states.audit; + break; + case AfterServiceStatus.THE_APPROVED: + item.info = res.data.states.approved; + break; + case AfterServiceStatus.COMPLETE: + item.info = res.data.states.complete; + break; + case AfterServiceStatus.CLOSED: + item.info = res.data.states.closed; + break; + } + return item; + }); + } + if (res && res.data && res.data.dataList) { + dataList = (res.data.dataList || []).map((_data) => { + return { + id: _data.rights.rightsNo, + serviceNo: _data.rights.rightsNo, + storeName: _data.rights.storeName, + type: _data.rights.rightsType, + typeDesc: ServiceTypeDesc[_data.rights.rightsType], + typeDescIcon: + _data.rightsType === ServiceType.ONLY_REFUND + ? 'money-circle' + : 'return-goods-1', + status: _data.rights.rightsStatus, + statusName: _data.rights.userRightsStatusName, + statusDesc: _data.rights.userRightsStatusDesc, + amount: _data.rights.refundAmount, + goodsList: _data.rightsItem.map((item, i) => ({ + id: i, + thumb: item.goodsPictureUrl, + title: item.goodsName, + specs: (item.specInfo || []).map((s) => s.specValues || ''), + itemRefundAmount: item.itemRefundAmount, + rightsQuantity: item.itemRefundAmount, + })), + storeId: _data.storeId, + buttons: _data.buttonVOs || [], + logisticsNo: _data.logisticsVO.logisticsNo, // 退货物流单号 + logisticsCompanyName: _data.logisticsVO.logisticsCompanyName, // 退货物流公司 + logisticsCompanyCode: _data.logisticsVO.logisticsCompanyCode, // 退货物流公司 + remark: _data.logisticsVO.remark, // 退货备注 + logisticsVO: _data.logisticsVO, + }; + }); + } + return new Promise((resolve) => { + if (reset) { + this.setData( + { + dataList: [], + }, + () => resolve(), + ); + } else resolve(); + }).then(() => { + this.setData({ + tabs, + dataList: this.data.dataList.concat(dataList), + listLoading: dataList.length > 0 ? 0 : 2, + }); + }); + }) + .catch((err) => { + this.setData({ + listLoading: 3, + }); + return Promise.reject(err); + }); + }, + + onReTryLoad() { + this.getAfterServiceList(this.data.curTab); + }, + + onTabChange(e) { + const { value } = e.detail; + const tab = this.data.tabs.find((v) => v.key === value); + if (!tab) return; + this.refreshList(value); + }, + + refreshList(status = -1) { + this.page = { + size: 10, + num: 1, + }; + this.setData({ + curTab: status, + dataList: [], + }); + return this.getAfterServiceList(status, true); + }, + + onRefresh() { + this.refreshList(this.data.curTab); + }, + + // 点击订单卡片 + onAfterServiceCardTap(e) { + wx.navigateTo({ + url: `/pages/order/after-service-detail/index?rightsNo=${e.currentTarget.dataset.order.id}`, + }); + }, +}); diff --git a/miniprogram/tcb-shop/pages/order/after-service-list/index.json b/miniprogram/tcb-shop/pages/order/after-service-list/index.json new file mode 100644 index 0000000..46b5d09 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/after-service-list/index.json @@ -0,0 +1,15 @@ +{ + "navigationBarTitleText": "退款/售后", + "usingComponents": { + "wr-load-more": "/components/load-more/index", + "wr-after-service-button-bar": "../components/after-service-button-bar/index", + "wr-price": "/components/price/index", + "wr-order-card": "../components/order-card/index", + "wr-goods-card": "../components/goods-card/index", + "t-icon": "tdesign-miniprogram/icon/icon", + "t-toast": "tdesign-miniprogram/toast/toast", + "t-dialog": "tdesign-miniprogram/dialog/dialog", + "t-empty": "tdesign-miniprogram/empty/empty", + "t-pull-down-refresh": "tdesign-miniprogram/pull-down-refresh/pull-down-refresh" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/after-service-list/index.wxml b/miniprogram/tcb-shop/pages/order/after-service-list/index.wxml new file mode 100644 index 0000000..7b4f649 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/after-service-list/index.wxml @@ -0,0 +1,61 @@ + + + + + + + {{order.typeDesc}} + + + + + + x {{goods.rightsQuantity}} + + + + {{order.statusDesc}} + + + + + + + + + 暂无退款或售后申请记录 + + + + + + + diff --git a/miniprogram/tcb-shop/pages/order/after-service-list/index.wxss b/miniprogram/tcb-shop/pages/order/after-service-list/index.wxss new file mode 100644 index 0000000..8a784bf --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/after-service-list/index.wxss @@ -0,0 +1,104 @@ +:host { + background-color: #f5f5f5; +} + +.list-loading { + height: 100rpx; +} + +.empty-wrapper { + height: calc(100vh - 88rpx); +} + +.page-container .order-goods-card-footer { + display: flex; + width: calc(100% - 190rpx); + justify-content: space-between; + position: absolute; + bottom: 20rpx; + left: 190rpx; +} + +.page-container .order-goods-card-footer .order-goods-card-footer-num { + color: #999; + line-height: 40rpx; +} + +.page-container .order-goods-card-footer .order-goods-card-footer-price-class { + font-size: 36rpx; + color: #333; + font-family: DIN Alternate; +} + +.page-container .order-goods-card-footer .order-goods-card-footer-price-decimal { + font-size: 28rpx; + color: #333; + font-family: DIN Alternate; +} + +.page-container .order-goods-card-footer .order-goods-card-footer-price-symbol { + color: #333; + font-size: 24rpx; + font-family: DIN Alternate; +} + +.page-container .wr-goods-card__specs { + margin: 14rpx 20rpx 0 0; +} + +.page-container .order-goods-card > wr-goods-card .wr-goods-card__title { + margin-right: 0; + -webkit-line-clamp: 1; +} + +.page-container .order-card .header .store-name { + width: 80%; + -webkit-line-clamp: 1; +} + +.page-container .order-card .header .store-name > view { + overflow: hidden; + width: 100%; + white-space: nowrap; + text-overflow: ellipsis; +} + +.page-container .status-desc { + box-sizing: border-box; + padding: 22rpx 20rpx; + font-size: 26rpx; + line-height: 1.3; + text-align: left; + color: #333333; + background-color: #f5f5f5; + border-radius: 8rpx; + word-wrap: break-word; + margin-top: 24rpx; + margin-bottom: 20rpx; +} + +.page-container .header__right { + font-size: 24rpx; + color: #fa4126; + display: flex; + align-items: center; +} + +.page-container .header__right__icon { + color: #d05b27; + font-size: 16px !important; + margin-right: 10rpx; +} + +.t-class-indicator { + color: #b9b9b9 !important; +} + +.page-container .header-class { + margin-bottom: 5rpx !important; +} + +.t-button { + --td-button-default-color: #000; + --td-button-primary-text-color: #fa4126; +} diff --git a/miniprogram/tcb-shop/pages/order/apply-service/index.js b/miniprogram/tcb-shop/pages/order/apply-service/index.js new file mode 100644 index 0000000..0642e69 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/apply-service/index.js @@ -0,0 +1,441 @@ +import Dialog from 'tdesign-miniprogram/dialog/index'; +import Toast from 'tdesign-miniprogram/toast/index'; +import { priceFormat } from '../../../utils/util'; +import { OrderStatus, ServiceType, ServiceReceiptStatus } from '../config'; +import reasonSheet from '../components/reason-sheet/reasonSheet'; +import { + fetchRightsPreview, + dispatchConfirmReceived, + fetchApplyReasonList, + dispatchApplyService, +} from '../../../services/order/applyService'; + +Page({ + query: {}, + data: { + uploading: false, // 凭证上传状态 + canApplyReturn: true, // 是否可退货 + goodsInfo: {}, + receiptStatusList: [ + { desc: '未收到货', status: ServiceReceiptStatus.NOT_RECEIPTED }, + { desc: '已收到货', status: ServiceReceiptStatus.RECEIPTED }, + ], + applyReasons: [], + serviceType: null, // 20-仅退款,10-退货退款 + serviceFrom: { + returnNum: 1, + receiptStatus: { desc: '请选择', status: null }, + applyReason: { desc: '请选择', type: null }, + // max-填写上限(单位分),current-当前值(单位分),temp输入框中的值(单位元) + amount: { max: 0, current: 0, temp: 0, focus: false }, + remark: '', + rightsImageUrls: [], + }, + maxApplyNum: 2, // 最大可申请售后的商品数 + amountTip: '', + showReceiptStatusDialog: false, + validateRes: { + valid: false, + msg: '', + }, + submitting: false, + inputDialogVisible: false, + uploadGridConfig: { + column: 3, + width: 212, + height: 212, + }, + serviceRequireType: '', + }, + + setWatcher(key, callback) { + let lastData = this.data; + const keys = key.split('.'); + keys.slice(0, -1).forEach((k) => { + lastData = lastData[k]; + }); + const lastKey = keys[keys.length - 1]; + this.observe(lastData, lastKey, callback); + }, + + observe(data, k, callback) { + let val = data[k]; + Object.defineProperty(data, k, { + configurable: true, + enumerable: true, + set: (value) => { + val = value; + callback(); + }, + get: () => { + return val; + }, + }); + }, + + validate() { + let valid = true; + let msg = ''; + // 检查必填项 + if (!this.data.serviceFrom.applyReason.type) { + valid = false; + msg = '请填写退款原因'; + } else if (!this.data.serviceFrom.amount.current) { + valid = false; + msg = '请填写退款金额'; + } + if (this.data.serviceFrom.amount.current <= 0) { + valid = false; + msg = '退款金额必须大于0'; + } + this.setData({ validateRes: { valid, msg } }); + }, + + onLoad(query) { + this.query = query; + if (!this.checkQuery()) return; + this.setData({ + canApplyReturn: query.canApplyReturn === 'true', + }); + this.init(); + this.inputDialog = this.selectComponent('#input-dialog'); + this.setWatcher('serviceFrom.returnNum', this.validate.bind(this)); + this.setWatcher('serviceFrom.applyReason', this.validate.bind(this)); + this.setWatcher('serviceFrom.amount', this.validate.bind(this)); + this.setWatcher('serviceFrom.rightsImageUrls', this.validate.bind(this)); + }, + + async init() { + try { + await this.refresh(); + } catch (e) {} + }, + + checkQuery() { + const { orderNo, skuId } = this.query; + if (!orderNo) { + Dialog.alert({ + content: '请先选择订单', + }).then(() => { + wx.redirectTo({ url: 'pages/order/order-list/index' }); + }); + return false; + } + if (!skuId) { + Dialog.alert({ + content: '请先选择商品', + }).then(() => { + wx.redirectTo(`pages/order/order-detail/index?orderNo=${orderNo}`); + }); + return false; + } + return true; + }, + + async refresh() { + wx.showLoading({ title: 'loading' }); + try { + const res = await this.getRightsPreview(); + wx.hideLoading(); + const goodsInfo = { + id: res.data.skuId, + thumb: res.data.goodsInfo && res.data.goodsInfo.skuImage, + title: res.data.goodsInfo && res.data.goodsInfo.goodsName, + spuId: res.data.spuId, + skuId: res.data.skuId, + specs: ((res.data.goodsInfo && res.data.goodsInfo.specInfo) || []).map((s) => s.specValue), + paidAmountEach: res.data.paidAmountEach, + boughtQuantity: res.data.boughtQuantity, + }; + this.setData({ + goodsInfo, + 'serviceFrom.amount': { + max: res.data.refundableAmount, + current: res.data.refundableAmount, + }, + 'serviceFrom.returnNum': res.data.numOfSku, + amountTip: `最多可申请退款¥ ${priceFormat(res.data.refundableAmount, 2)},含发货运费¥ ${priceFormat( + res.data.shippingFeeIncluded, + 2, + )}`, + maxApplyNum: res.data.numOfSkuAvailable, + }); + } catch (err) { + wx.hideLoading(); + throw err; + } + }, + + async getRightsPreview() { + const { orderNo, skuId, spuId } = this.query; + const params = { + orderNo, + skuId, + spuId, + numOfSku: this.data.serviceFrom.returnNum, + }; + const res = await fetchRightsPreview(params); + return res; + }, + + onApplyOnlyRefund() { + wx.setNavigationBarTitle({ title: '申请退款' }); + this.setData({ serviceRequireType: 'REFUND_MONEY' }); + this.switchReceiptStatus(0); + }, + + onApplyReturnGoods() { + wx.setNavigationBarTitle({ title: '申请退货退款' }); + this.setData({ serviceRequireType: 'REFUND_GOODS' }); + const orderStatus = parseInt(this.query.orderStatus); + Promise.resolve() + .then(() => { + if (orderStatus === OrderStatus.PENDING_RECEIPT) { + return Dialog.confirm({ + title: '订单商品是否已经收到货', + content: '', + confirmBtn: '确认收货,并申请退货', + cancelBtn: '未收到货', + }).then(() => { + return dispatchConfirmReceived({ + parameter: { + logisticsNo: this.query.logisticsNo, + orderNo: this.query.orderNo, + }, + }); + }); + } + return; + }) + .then(() => { + this.setData({ serviceType: ServiceType.RETURN_GOODS }); + this.switchReceiptStatus(1); + }); + }, + + onApplyReturnGoodsStatus() { + reasonSheet({ + show: true, + title: '选择退款原因', + options: this.data.applyReasons.map((r) => ({ + title: r.desc, + })), + showConfirmButton: true, + showCancelButton: true, + emptyTip: '请选择退款原因', + }).then((indexes) => { + this.setData({ + 'serviceFrom.applyReason': this.data.applyReasons[indexes[0]], + }); + }); + }, + + onChangeReturnNum(e) { + const { value } = e.detail; + this.setData({ + 'serviceFrom.returnNum': value, + }); + }, + + onApplyGoodsStatus() { + reasonSheet({ + show: true, + title: '请选择收货状态', + options: this.data.receiptStatusList.map((r) => ({ + title: r.desc, + })), + showConfirmButton: true, + emptyTip: '请选择收货状态', + }).then((indexes) => { + this.setData({ + 'serviceFrom.receiptStatus': this.data.receiptStatusList[indexes[0]], + }); + }); + }, + + switchReceiptStatus(index) { + const statusItem = this.data.receiptStatusList[index]; + // 没有找到对应的状态,则清空/初始化 + if (!statusItem) { + this.setData({ + showReceiptStatusDialog: false, + 'serviceFrom.receiptStatus': { desc: '请选择', status: null }, + 'serviceFrom.applyReason': { desc: '请选择', type: null }, // 收货状态改变时,初始化申请原因 + applyReasons: [], + }); + return; + } + // 仅选中项与当前项不一致时,才切换申请原因列表applyReasons + if (!statusItem || statusItem.status === this.data.serviceFrom.receiptStatus.status) { + this.setData({ showReceiptStatusDialog: false }); + return; + } + this.getApplyReasons(statusItem.status).then((reasons) => { + this.setData({ + showReceiptStatusDialog: false, + 'serviceFrom.receiptStatus': statusItem, + 'serviceFrom.applyReason': { desc: '请选择', type: null }, // 收货状态改变时,重置申请原因 + applyReasons: reasons, + }); + }); + }, + + getApplyReasons(receiptStatus) { + const params = { rightsReasonType: receiptStatus }; + return fetchApplyReasonList(params) + .then((res) => { + return res.data.rightsReasonList.map((reason) => ({ + type: reason.id, + desc: reason.desc, + })); + }) + .catch(() => { + return []; + }); + }, + + onReceiptStatusDialogConfirm(e) { + const { index } = e.currentTarget.dataset; + this.switchReceiptStatus(index); + }, + + onAmountTap() { + this.setData({ + 'serviceFrom.amount.temp': priceFormat(this.data.serviceFrom.amount.current), + 'serviceFrom.amount.focus': true, + inputDialogVisible: true, + }); + this.inputDialog.setData({ + cancelBtn: '取消', + confirmBtn: '确定', + }); + this.inputDialog._onConfirm = () => { + this.setData({ + 'serviceFrom.amount.current': this.data.serviceFrom.amount.temp * 100, + }); + }; + this.inputDialog._onCancel = () => {}; + }, + + // 对输入的值进行过滤 + onAmountInput(e) { + let { value } = e.detail; + const regRes = value.match(/\d+(\.?\d*)?/); // 输入中,允许末尾为小数点 + value = regRes ? regRes[0] : ''; + this.setData({ 'serviceFrom.amount.temp': value }); + }, + + // 失去焦点时,更严格的过滤并转化为float + onAmountBlur(e) { + let { value } = e.detail; + const regRes = value.match(/\d+(\.?\d+)?/); // 失去焦点时,不允许末尾为小数点 + value = regRes ? regRes[0] : '0'; + value = parseFloat(value) * 100; + if (value > this.data.serviceFrom.amount.max) { + value = this.data.serviceFrom.amount.max; + } + this.setData({ + 'serviceFrom.amount.temp': priceFormat(value), + 'serviceFrom.amount.focus': false, + }); + }, + + onAmountFocus() { + this.setData({ 'serviceFrom.amount.focus': true }); + }, + + onRemarkChange(e) { + const { value } = e.detail; + this.setData({ + 'serviceFrom.remark': value, + }); + }, + + // 发起申请售后请求 + onSubmit() { + this.submitCheck().then(() => { + const params = { + rights: { + orderNo: this.query.orderNo, + refundRequestAmount: this.data.serviceFrom.amount.current, + rightsImageUrls: this.data.serviceFrom.rightsImageUrls, + rightsReasonDesc: this.data.serviceFrom.applyReason.desc, + rightsReasonType: this.data.serviceFrom.receiptStatus.status, + rightsType: this.data.serviceType, + }, + rightsItem: [ + { + itemTotalAmount: this.data.goodsInfo.price * this.data.serviceFrom.returnNum, + rightsQuantity: this.data.serviceFrom.returnNum, + skuId: this.query.skuId, + spuId: this.query.spuId, + }, + ], + refundMemo: this.data.serviceFrom.remark.current, + }; + this.setData({ submitting: true }); + // 发起申请售后请求 + dispatchApplyService(params) + .then((res) => { + Toast({ + context: this, + selector: '#t-toast', + message: '申请成功', + icon: '', + }); + + wx.redirectTo({ + url: `/pages/order/after-service-detail/index?rightsNo=${res.data.rightsNo}`, + }); + }) + .then(() => this.setData({ submitting: false })) + .catch(() => this.setData({ submitting: false })); + }); + }, + + submitCheck() { + return new Promise((resolve) => { + const { msg, valid } = this.data.validateRes; + if (!valid) { + Toast({ + context: this, + selector: '#t-toast', + message: msg, + icon: '', + }); + return; + } + resolve(); + }); + }, + + handleSuccess(e) { + const { files } = e.detail; + this.setData({ + 'sessionFrom.rightsImageUrls': files, + }); + }, + + handleRemove(e) { + const { index } = e.detail; + const { + sessionFrom: { rightsImageUrls }, + } = this.data; + rightsImageUrls.splice(index, 1); + this.setData({ + 'sessionFrom.rightsImageUrls': rightsImageUrls, + }); + }, + + handleComplete() { + this.setData({ + uploading: false, + }); + }, + + handleSelectChange() { + this.setData({ + uploading: true, + }); + }, +}); diff --git a/miniprogram/tcb-shop/pages/order/apply-service/index.json b/miniprogram/tcb-shop/pages/order/apply-service/index.json new file mode 100644 index 0000000..b5c4f62 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/apply-service/index.json @@ -0,0 +1,19 @@ +{ + "navigationBarTitleText": "选择售后类型", + "usingComponents": { + "wr-price": "/components/price/index", + "wr-order-goods-card": "../components/order-goods-card/index", + "wr-reason-sheet": "../components/reason-sheet/index", + "t-cell": "tdesign-miniprogram/cell/cell", + "t-icon": "tdesign-miniprogram/icon/icon", + "t-toast": "tdesign-miniprogram/toast/toast", + "t-dialog": "tdesign-miniprogram/dialog/dialog", + "t-button": "tdesign-miniprogram/button/button", + "t-cell-group": "tdesign-miniprogram/cell-group/cell-group", + "t-stepper": "tdesign-miniprogram/stepper/stepper", + "t-popup": "tdesign-miniprogram/popup/popup", + "t-textarea": "tdesign-miniprogram/textarea/textarea", + "t-input": "tdesign-miniprogram/input/input", + "t-upload": "tdesign-miniprogram/upload/upload" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/apply-service/index.wxml b/miniprogram/tcb-shop/pages/order/apply-service/index.wxml new file mode 100644 index 0000000..b6471ea --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/apply-service/index.wxml @@ -0,0 +1,198 @@ + + + + + + x {{goodsInfo.boughtQuantity}} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 修改 + + + + + + + + 退款说明 + + + + + + + + 上传凭证 + (最多3张) + + + + + + + 提交 + + + + + + + + + + {{item.desc}} + + + 取消 + + + + + + + 退款金额 + + + {{amountTip}} + + + + diff --git a/miniprogram/tcb-shop/pages/order/apply-service/index.wxss b/miniprogram/tcb-shop/pages/order/apply-service/index.wxss new file mode 100644 index 0000000..86fb12f --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/apply-service/index.wxss @@ -0,0 +1,308 @@ +:host { + background-color: #f5f5f5; +} +.select-service .service-form .service-from-group { + margin-top: 20rpx; +} +.select-service .service-form { + padding-bottom: calc(env(safe-area-inset-bottom) + 80rpx); +} + +.order-goods-card-footer { + display: flex; + width: calc(100% - 190rpx); + justify-content: space-between; + position: absolute; + bottom: 0; + left: 190rpx; +} + +.order-goods-card-footer-num { + color: #999; +} + +.select-service .order-goods-card-footer .order-goods-card-footer-price-class { + font-size: 36rpx; + color: #333; + font-family: DIN Alternate; +} +.select-service .order-goods-card-footer .order-goods-card-footer-price-decimal { + font-size: 28rpx; + color: #333; + font-family: DIN Alternate; +} +.select-service .order-goods-card-footer .order-goods-card-footer-price-symbol { + color: #333; + font-size: 24rpx; + font-family: DIN Alternate; +} + +.select-service .remark { + min-height: 110rpx; + border-radius: 10rpx; + margin-top: 20rpx; + background-color: #f5f5f5; +} +.select-service .remark::after { + border: none; +} + +.select-service .special-cell .special-cell-note { + display: flex; + flex-direction: column; +} + +.select-service .special-cell .wr-cell__title { + margin-right: 100rpx; +} + +.select-service .special-cell .special-cell-note-price-class { + font-size: 36rpx; + color: #fa4126; + font-family: DIN Alternate; +} +.select-service .special-cell .special-cell-note-price-decimal { + font-size: 28rpx; + color: #fa4126; + font-family: DIN Alternate; +} +.select-service .special-cell .special-cell-note-price-symbol { + color: #fa4126; + font-size: 24rpx; + font-family: DIN Alternate; +} + +.select-service .bottom-bar__btn { + width: 686rpx; + background-color: #fa4126; + color: white; + font-size: 32rpx; + border-radius: 48rpx; + position: absolute; + left: 50%; + top: 20rpx; + transform: translateX(-50%); +} +.select-service .bottom-bar__btn::after { + border: none; +} +.select-service .bottom-bar__btn.disabled { + background-color: #c6c6c6; + --td-button-default-active-bg-color: #c6c6c6; + --td-button-default-border-bg-color: #c6c6c6; +} +.select-service .bottom-bar__btn.disabled::after { + border: none; +} +.select-service .order-goods-card .wr-goods-card { + padding: 0 30rpx; +} + +.order-goods-card-footer { + display: flex; + width: calc(100% - 190rpx); + justify-content: space-between; + position: absolute; + bottom: 20rpx; + left: 190rpx; +} + +.order-goods-card-footer-num { + color: #999; + line-height: 40rpx; +} + +.order-goods-card-title-class { + width: 10rpx !important; +} + +.input-dialog__content .input-dialog__input { + font-size: 72rpx !important; + height: 64rpx; +} + +.t-input__label { + margin-right: 0 !important; +} + +.input-dialog__label { + font-size: 48rpx; + color: #333; +} + +.input-dialog__content .input-dialog__input, +.input-dialog__label { + height: 64rpx; + line-height: 64rpx !important; +} + +.input-dialog__content .input { + font-size: 48rpx; + padding-left: 0; + padding-right: 0; + --td-input-border-left-space: 0; +} + +.input-dialog__content .tips { + margin-top: 24rpx; + font-size: 24rpx; + color: #999999; +} + +.t-input__name { + width: 10rpx !important; +} + +.input-dialog__title { + color: #333; + font-size: 32rpx; + font-weight: normal; +} + +.dialog--service-status { + background-color: #f3f4f5; + overflow: hidden; +} +.dialog--service-status .options .option { + color: #333333; + font-size: 30rpx; + text-align: center; + height: 100rpx; + line-height: 100rpx; + background-color: white; +} +.dialog--service-status .options .option:not(:last-child) { + border-bottom: 1rpx solid #e6e6e6; +} +.dialog--service-status .options .option--active { + opacity: 0.5; +} +.dialog--service-status .options .option.main { + color: #fa4126; +} +.dialog--service-status .cancel { + color: #333333; + font-size: 30rpx; + text-align: center; + height: 100rpx; + line-height: 100rpx; + background-color: white; + margin-top: 20rpx; +} +.dialog--service-status .cancel--active { + opacity: 0.5; +} +.amount-dialog--focus .popup__content--center, +.remark-dialog--focus .popup__content--center { + top: 100rpx; + transform: translate(-50%, 0); +} +.dialog .dialog__button-confirm { + color: #fa4126; + color: var(--color-primary, #fa4126); +} +.select-service .bottom-bar { + background-color: #fff; + position: fixed; + bottom: 0; + left: 0; + width: 100%; + height: 158rpx; + z-index: 3; +} +.order-goods-card { + background: #fff; + margin-bottom: 24rpx; +} + +.service-from-group__wrapper { + display: flex; + flex-direction: column; + font-family: DIN Alternate; + font-weight: bold; + font-size: 36rpx; + text-align: right; + color: #fa4126; +} +.service-from-group__price { + display: flex; + align-items: center; + color: #bbb; + font-size: 24rpx; + position: relative; + left: 30rpx; +} +.textarea--label { +} +.service-from-group__textarea { + margin-top: 20rpx; + background-color: #fff; + padding: 32rpx 32rpx 24rpx; +} + +.textarea--content { + margin-top: 32rpx; + background: #f5f5f5 !important; + border-radius: 16rpx; +} +.service-from-group__textarea .t-textarea__wrapper .t-textarea__wrapper-textarea { + height: 136rpx; + box-sizing: border-box; +} +.service-from-group__grid { + padding: 0 32rpx 48rpx; + background: #fff; + margin-bottom: 148rpx; +} + +.upload-addcontent-slot { + background-color: #f5f5f5; + height: inherit; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.refund-money__description { + font-size: 24rpx !important; +} + +.upload-desc { + text-align: center; + display: flex; + flex-direction: column; + font-size: 24rpx; + color: #999; +} + +.t-cell__left__icon { + position: relative; + top: -24rpx; + margin-right: 18rpx; +} + +.service-choice .t-cell__title-text { + color: #333; + font-weight: bold; +} + +.service-form .service-from-group .service-from-group__wrapper .refund-money-price-class { + font-size: 36rpx; + font-family: DIN Alternate; +} + +.service-form .service-from-group .service-from-group__wrapper .refund-money-price-decimal { + font-size: 28rpx; + font-family: DIN Alternate; +} + +.service-form .service-from-group .service-from-group__wrapper .refund-money-price-symbol { + font-size: 24rpx; + font-family: DIN Alternate; +} + +.t-button { + --td-button-default-color: #000; + --td-button-primary-text-color: #fa4126; +} diff --git a/miniprogram/tcb-shop/pages/order/components/after-service-button-bar/index.js b/miniprogram/tcb-shop/pages/order/components/after-service-button-bar/index.js new file mode 100644 index 0000000..c1f813d --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/after-service-button-bar/index.js @@ -0,0 +1,95 @@ +import Dialog from 'tdesign-miniprogram/dialog/index'; +import Toast from 'tdesign-miniprogram/toast/index'; + +import { cancelRights } from '../../after-service-detail/api'; +import { ServiceButtonTypes } from '../../config'; + +Component({ + properties: { + service: { + type: Object, + observer(service) { + const buttonsRight = service.buttons || service.buttonVOs || []; + this.setData({ + buttons: { + left: [], + right: buttonsRight, + }, + }); + }, + }, + }, + + data: { + service: {}, + buttons: { + left: [], + right: [], + }, + }, + + methods: { + // 点击【订单操作】按钮,根据按钮类型分发 + onServiceBtnTap(e) { + const { type } = e.currentTarget.dataset; + switch (type) { + case ServiceButtonTypes.REVOKE: + this.onConfirm(this.data.service); + break; + case ServiceButtonTypes.FILL_TRACKING_NO: + this.onFillTrackingNo(this.data.service); + break; + case ServiceButtonTypes.CHANGE_TRACKING_NO: + this.onChangeTrackingNo(this.data.service); + break; + case ServiceButtonTypes.VIEW_DELIVERY: + this.viewDelivery(this.data.service); + break; + } + }, + + onFillTrackingNo(service) { + wx.navigateTo({ + url: `/pages/order/fill-tracking-no/index?rightsNo=${service.id}`, + }); + }, + + viewDelivery(service) { + wx.navigateTo({ + url: `/pages/order/delivery-detail/index?data=${JSON.stringify( + service.logistics || service.logisticsVO, + )}&source=2`, + }); + }, + + onChangeTrackingNo(service) { + wx.navigateTo({ + url: `/pages/order/fill-tracking-no/index?rightsNo=${ + service.id + }&logisticsNo=${service.logisticsNo}&logisticsCompanyName=${ + service.logisticsCompanyName + }&logisticsCompanyCode=${service.logisticsCompanyCode}&remark=${ + service.remark || '' + }`, + }); + }, + + onConfirm() { + Dialog.confirm({ + title: '是否撤销退货申请?', + content: '', + confirmBtn: '撤销申请', + cancelBtn: '不撤销', + }).then(() => { + const params = { rightsNo: this.data.service.id }; + return cancelRights(params).then(() => { + Toast({ + context: this, + selector: '#t-toast', + message: '你确认撤销申请', + }); + }); + }); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/order/components/after-service-button-bar/index.json b/miniprogram/tcb-shop/pages/order/components/after-service-button-bar/index.json new file mode 100644 index 0000000..75d1e24 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/after-service-button-bar/index.json @@ -0,0 +1,6 @@ +{ + "component": true, + "usingComponents": { + "t-button": "tdesign-miniprogram/button/button" + } +} diff --git a/miniprogram/tcb-shop/pages/order/components/after-service-button-bar/index.wxml b/miniprogram/tcb-shop/pages/order/components/after-service-button-bar/index.wxml new file mode 100644 index 0000000..c19c824 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/after-service-button-bar/index.wxml @@ -0,0 +1,33 @@ + + + + {{leftBtn.name}} + + + + + {{rightBtn.name}} + + + diff --git a/miniprogram/tcb-shop/pages/order/components/after-service-button-bar/index.wxss b/miniprogram/tcb-shop/pages/order/components/after-service-button-bar/index.wxss new file mode 100644 index 0000000..e83597f --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/after-service-button-bar/index.wxss @@ -0,0 +1,43 @@ +:host { + width: 100%; +} +.btn-bar { + display: flex; + justify-content: space-between; + align-items: center; + line-height: 1; +} +.btn-bar .order-btn { + background-color: inherit; + font-size: 26rpx; + padding: 16rpx 28rpx; + line-height: 1; + border-radius: unset; + min-width: 160rpx; + border-radius: 32rpx; + height: 60rpx; + margin-right: 10rpx; +} + +.btn-bar .left .order-btn:not(:first-child), +.btn-bar .right .order-btn:not(:first-child) { + margin-left: 20rpx; +} +.btn-bar .left .delete-btn { + font-size: 22rpx; +} +.btn-bar .left .delete-btn::after { + display: none; +} + +.btn-bar .right .normal { + --td-button-default-color: #333333; + --td-button-default-border-color: #dddddd; +} + +.btn-bar .right .primary { + --td-button-default-color: #fff; + --td-button-default-bg-color: #fa4126; + --td-button-default-border-color: #fa4126; + --td-button-default-active-bg-color: #fa42269c; +} diff --git a/miniprogram/tcb-shop/pages/order/components/customer-service/index.js b/miniprogram/tcb-shop/pages/order/components/customer-service/index.js new file mode 100644 index 0000000..419b97f --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/customer-service/index.js @@ -0,0 +1,38 @@ +Component({ + externalClasses: ['wr-class'], + + properties: { + phoneNumber: String, + desc: String, + }, + + data: { + show: false, + }, + + methods: { + onBtnTap() { + this.setData({ + show: true, + }); + }, + + onDialogClose() { + this.setData({ + show: false, + }); + }, + + onCall() { + const { phoneNumber } = this.properties; + wx.makePhoneCall({ + phoneNumber, + }); + }, + onCallOnlineService() { + wx.showToast({ + title: '你点击了在线客服', + }); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/order/components/customer-service/index.json b/miniprogram/tcb-shop/pages/order/components/customer-service/index.json new file mode 100644 index 0000000..6e4c04e --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/customer-service/index.json @@ -0,0 +1,6 @@ +{ + "component": true, + "usingComponents": { + "t-popup": "tdesign-miniprogram/popup/popup" + } +} diff --git a/miniprogram/tcb-shop/pages/order/components/customer-service/index.wxml b/miniprogram/tcb-shop/pages/order/components/customer-service/index.wxml new file mode 100644 index 0000000..f5413a4 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/customer-service/index.wxml @@ -0,0 +1,23 @@ + +联系客服 + + + + + 服务时间: + {{desc}} + + + 呼叫 {{phoneNumber}} + + 在线客服 + 取消 + + + + diff --git a/miniprogram/tcb-shop/pages/order/components/customer-service/index.wxss b/miniprogram/tcb-shop/pages/order/components/customer-service/index.wxss new file mode 100644 index 0000000..75f0363 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/customer-service/index.wxss @@ -0,0 +1,48 @@ +.text-btn { + display: inline; + color: #333; + font-size: 24rpx; +} +.text-btn--active { + opacity: 0.5; +} +.dialog--customer-service { + background-color: #f3f4f5; + overflow: hidden; +} +.dialog--customer-service .content { + font-size: 26rpx; + margin: 32rpx 30rpx; + text-align: center; +} +.dialog--customer-service .content .title { + display: inline; + color: #999999; + font-weight: bold; +} +.dialog--customer-service .content .subtitle { + display: inline; + color: #999999; +} +.dialog--customer-service .options .option { + color: #333333; + font-size: 30rpx; + text-align: center; + height: 100rpx; + line-height: 100rpx; + background-color: white; +} +.dialog--customer-service .options .option:not(:last-child) { + margin-bottom: 20rpx; +} +.dialog--customer-service .options .option--active { + opacity: 0.5; +} +.dialog--customer-service .options .option.main { + color: #333; +} +.dialog--customer-service .options .option.online { + position: relative; + top: -17rpx; + margin-bottom: 2rpx; +} diff --git a/miniprogram/tcb-shop/pages/order/components/goods-card/index.js b/miniprogram/tcb-shop/pages/order/components/goods-card/index.js new file mode 100644 index 0000000..293f3f0 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/goods-card/index.js @@ -0,0 +1,251 @@ +Component({ + options: { + multipleSlots: true, // 在组件定义时的选项中启用多slot支持 + addGlobalClass: true, + }, + intersectionObserverContext: null, + + externalClasses: [ + 'card-class', + 'title-class', + 'desc-class', + 'num-class', + 'thumb-class', + 'specs-class', + 'price-class', + 'origin-price-class', + 'price-prefix-class', + ], + + relations: { + '../order-card/index': { + type: 'ancestor', + linked(target) { + this.parent = target; + }, + }, + }, + + properties: { + hidden: { + // 设置为null代表不做类型转换 + type: null, + value: false, + observer(hidden) { + // null就是代表没有设置,没有设置的话不setData,防止祖先组件触发的setHidden操作被覆盖 + if (hidden !== null) { + this.setHidden(!!hidden); + } + }, + }, + id: { + type: String, + // `goods-card-88888888` + // 不能在这里写生成逻辑,如果在这里写,那么假设有多个goods-list时,他们将共享这个值 + value: '', + observer: (id) => { + this.genIndependentID(id); + if (this.properties.thresholds?.length) { + this.createIntersectionObserverHandle(); + } + }, + }, + data: { + type: Object, + observer(goods) { + // 有ID的商品才渲染 + if (!goods) { + return; + } + + // 敲定换行数量默认值 + if (goods.lineClamp === undefined || goods.lineClamp <= 0) { + // tag数组长度 大于0 且 可见 + // 指定换行为1行 + if ((goods.tags?.length || 0) > 0 && !goods.hideKey?.tags) { + goods.lineClamp = 1; + } else { + goods.lineClamp = 2; + } + } + goods.specs = goods.sku.attr_value.map((v) => v.value).join(','); + + this.setData({ goods }); + }, + }, + layout: { + type: String, + value: 'horizontal', + }, + thumbMode: { + type: String, + value: 'aspectFill', + }, + thumbWidth: Number, + thumbHeight: Number, + priceFill: { + type: Boolean, + value: true, + }, + currency: { + type: String, + value: '¥', + }, + lazyLoad: { + type: Boolean, + value: false, + }, + centered: { + type: Boolean, + value: false, + }, + showCart: { + type: Boolean, + value: false, + }, + pricePrefix: { + type: String, + value: '', + }, + cartSize: { + type: Number, + value: 48, + }, + cartColor: { + type: String, + value: '#FA550F', + }, + /** 元素可见监控阈值, 数组长度大于0就创建 */ + thresholds: { + type: Array, + value: [], + observer(current) { + if (current && current.length) { + this.createIntersectionObserverHandle(); + } else { + this.clearIntersectionObserverHandle(); + } + }, + }, + specsIconClassPrefix: { + type: String, + value: 'wr', + }, + specsIcon: { + type: String, + value: 'expand_more', + }, + addCartIconClassPrefix: { + type: String, + value: 'wr', + }, + addCartIcon: { + type: String, + value: 'cart', + }, + }, + + data: { + hiddenInData: false, + independentID: '', + goods: { id: '' }, + /** 保证划线价格不小于原价,否则不渲染划线价 */ + isValidityLinePrice: false, + }, + + lifetimes: { + ready() { + this.init(); + }, + detached() { + this.clear(); + }, + }, + + methods: { + clickHandle() { + this.triggerEvent('click', { goods: this.data.goods }); + }, + clickThumbHandle() { + this.triggerEvent('thumb', { goods: this.data.goods }); + }, + clickTagHandle(evt) { + const { index } = evt.currentTarget.dataset; + this.triggerEvent('tag', { goods: this.data.goods, index }); + }, + // 加入购物车 + addCartHandle(e) { + const { id } = e.currentTarget; + const { id: cardID } = e.currentTarget.dataset; + this.triggerEvent('add-cart', { + ...e.detail, + id, + cardID, + goods: this.data.goods, + }); + }, + genIndependentID(id, cb) { + let independentID; + if (id) { + independentID = id; + } else { + // `goods-card-88888888` + independentID = `goods-card-${~~(Math.random() * 10 ** 8)}`; + } + this.setData({ independentID }, cb); + }, + + init() { + const { thresholds, id, hidden } = this.properties; + if (hidden !== null) { + this.setHidden(!!hidden); + } + + this.genIndependentID(id || '', () => { + if (thresholds && thresholds.length) { + this.createIntersectionObserverHandle(); + } + }); + }, + + clear() { + this.clearIntersectionObserverHandle(); + }, + + setHidden(hidden) { + this.setData({ hiddenInData: !!hidden }); + }, + + createIntersectionObserverHandle() { + if (this.intersectionObserverContext || !this.data.independentID) { + return; + } + + this.intersectionObserverContext = wx + .createIntersectionObserver(this, { + thresholds: this.properties.thresholds, + }) + .relativeToViewport(); + + this.intersectionObserverContext.observe(`#${this.data.independentID}`, (res) => { + this.intersectionObserverCB(res); + }); + }, + intersectionObserverCB(ob) { + this.triggerEvent('ob', { + goods: this.data.goods, + context: this.intersectionObserverContext, + ob, + }); + }, + clearIntersectionObserverHandle() { + if (this.intersectionObserverContext) { + try { + this.intersectionObserverContext.disconnect(); + } catch (e) {} + + this.intersectionObserverContext = null; + } + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/order/components/goods-card/index.json b/miniprogram/tcb-shop/pages/order/components/goods-card/index.json new file mode 100644 index 0000000..0dbb3d8 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/goods-card/index.json @@ -0,0 +1,8 @@ +{ + "component": true, + "usingComponents": { + "price": "/components/price/index", + "t-image": "/components/webp-image/index", + "t-icon": "tdesign-miniprogram/icon/icon" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/components/goods-card/index.wxml b/miniprogram/tcb-shop/pages/order/components/goods-card/index.wxml new file mode 100644 index 0000000..fab9cce --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/goods-card/index.wxml @@ -0,0 +1,40 @@ + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/components/goods-card/index.wxss b/miniprogram/tcb-shop/pages/order/components/goods-card/index.wxss new file mode 100644 index 0000000..7bcb096 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/goods-card/index.wxss @@ -0,0 +1,254 @@ +.wr-goods-card { + box-sizing: border-box; + font-size: 24rpx; +} +.wr-goods-card__main { + position: relative; + display: flex; + line-height: 1; + flex-direction: row; + background: transparent; + padding: 16rpx 0rpx; +} +.wr-goods-card.center .wr-goods-card__main { + align-items: center; + justify-content: center; +} +.wr-goods-card__thumb { + flex-shrink: 0; + position: relative; + width: 176rpx; + height: 176rpx; +} +.wr-goods-card__thumb-com { + width: 176rpx; + height: 176rpx; + border-radius: 8rpx; + overflow: hidden; +} +.wr-goods-card__thumb:empty { + display: none; + margin: 0; +} + +.wr-goods-card__body { + display: flex; + margin: 0 0 0 16rpx; + flex-direction: row; + flex: 1 1 auto; + min-height: 176rpx; +} +.wr-goods-card__long_content { + display: flex; + flex-direction: column; + overflow: hidden; + flex: 1 1 auto; +} +.wr-goods-card__long_content .goods_tips { + width: 100%; + margin-top: 16rpx; + text-align: right; + color: #fa4126; + font-size: 24rpx; + line-height: 32rpx; + font-weight: bold; +} +.wr-goods-card__title { + flex-shrink: 0; + font-size: 28rpx; + color: #333; + line-height: 40rpx; + font-weight: 400; + display: -webkit-box; + -webkit-box-orient: vertical; + overflow: hidden; + word-break: break-word; +} +.wr-goods-card__title__prefix-tags { + display: inline-flex; +} +.wr-goods-card__title__prefix-tags .prefix-tag { + margin: 0 8rpx 0 0; +} +.wr-goods-card__desc { + font-size: 24rpx; + color: #f5f5f5; + line-height: 40rpx; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + overflow: hidden; +} +.wr-goods-card__specs__desc, +.wr-goods-card__specs__text { + font-size: 24rpx; + height: 32rpx; + line-height: 32rpx; + color: #999999; + margin: 8rpx 0; +} +.wr-goods-card__specs__desc { + display: flex; + align-self: flex-start; + flex-direction: row; +} +.wr-goods-card__specs__desc-text { + height: 100%; + max-width: 380rpx; + word-break: break-all; + overflow: hidden; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 1; +} +.wr-goods-card__specs__desc-icon { + line-height: inherit; + margin-left: 8rpx; + font-size: 24rpx; + color: #bbb; +} +.wr-goods-card__specs__text { + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 1; + overflow: hidden; + text-overflow: ellipsis; + word-break: break-all; +} +.wr-goods-card__tags { + display: flex; + flex-direction: row; + flex-wrap: wrap; + margin: 16rpx 0 0 0; +} +.wr-goods-card__tag { + color: #fa550f; + background: transparent; + font-size: 20rpx; + border: 1rpx solid #fa550f; + padding: 0 8rpx; + height: 30rpx; + line-height: 30rpx; + margin: 0 8rpx 8rpx 0; + display: block; + overflow: hidden; + white-space: nowrap; + word-break: keep-all; + text-overflow: ellipsis; +} +.wr-goods-card__short_content { + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: flex-end; + margin: 0 0 0 46rpx; +} +.wr-goods-card__price__prefix { + order: 0; + color: #666; + margin: 0; +} +.wr-goods-card__price { + white-space: nowrap; + font-weight: bold; + order: 1; + color: #fa4126; + font-size: 36rpx; + margin: 0; + line-height: 48rpx; +} +.wr-goods-card__origin-price { + white-space: nowrap; + font-weight: normal; + order: 2; + color: #aaaaaa; + font-size: 24rpx; + margin: 0; +} +.wr-goods-card__num { + white-space: nowrap; + order: 4; + font-size: 24rpx; + color: #999; + margin: 20rpx 0 0 auto; +} +.wr-goods-card__num__prefix { + color: inherit; +} +.wr-goods-card__add-cart { + order: 3; + margin: auto 0 0 auto; +} +.wr-goods-card.horizontal-wrap .wr-goods-card__thumb { + width: 192rpx; + height: 192rpx; + border-radius: 8rpx; + overflow: hidden; +} +.wr-goods-card.horizontal-wrap .wr-goods-card__body { + flex-direction: column; +} +.wr-goods-card.horizontal-wrap .wr-goods-card__short_content { + flex-direction: row; + align-items: center; + margin: 16rpx 0 0 0; +} + +.wr-goods-card.horizontal-wrap .wr-goods-card__num { + margin: 0 0 0 auto; +} +.wr-goods-card.vertical .wr-goods-card__main { + padding: 0 0 22rpx 0; + flex-direction: column; +} +.wr-goods-card.vertical .wr-goods-card__thumb { + width: 340rpx; + height: 340rpx; +} +.wr-goods-card.vertical .wr-goods-card__body { + margin: 20rpx 20rpx 0 20rpx; + flex-direction: column; +} +.wr-goods-card.vertical .wr-goods-card__long_content { + overflow: hidden; +} +.wr-goods-card.vertical .wr-goods-card__title { + line-height: 36rpx; +} +.wr-goods-card.vertical .wr-goods-card__short_content { + margin: 20rpx 0 0 0; +} +.wr-goods-card.vertical .wr-goods-card__price { + order: 2; + color: #fa4126; + margin: 20rpx 0 0 0; +} +.wr-goods-card.vertical .wr-goods-card__origin-price { + order: 1; +} +.wr-goods-card.vertical .wr-goods-card__add-cart { + position: absolute; + bottom: 20rpx; + right: 20rpx; +} + +.wr-goods-card__short_content .no_storage { + display: flex; + align-items: center; + justify-content: space-between; + height: 40rpx; + color: #333; + font-size: 24rpx; + line-height: 32rpx; + width: 100%; +} + +.no_storage .no_storage__right { + width: 80rpx; + height: 40rpx; + border-radius: 20rpx; + border: 2rpx solid #fa4126; + line-height: 40rpx; + text-align: center; + color: #fa4126; +} diff --git a/miniprogram/tcb-shop/pages/order/components/noGoods/noGood.wxs b/miniprogram/tcb-shop/pages/order/components/noGoods/noGood.wxs new file mode 100644 index 0000000..f9b13d0 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/noGoods/noGood.wxs @@ -0,0 +1,17 @@ +var isOnlyBack = function (data) { + return data.limitGoodsList || (data.inValidGoodsList && !data.storeGoodsList); +}; + +var isShowChangeAddress = function (data) { + return data.abnormalDeliveryGoodsList; +}; + +var isShowKeepPay = function (data) { + return data.outOfStockGoodsList || (data.storeGoodsList && data.inValidGoodsList); +}; + +module.exports = { + isOnlyBack: isOnlyBack, + isShowChangeAddress: isShowChangeAddress, + isShowKeepPay: isShowKeepPay, +}; diff --git a/miniprogram/tcb-shop/pages/order/components/noGoods/noGoods.js b/miniprogram/tcb-shop/pages/order/components/noGoods/noGoods.js new file mode 100644 index 0000000..53b6f86 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/noGoods/noGoods.js @@ -0,0 +1,57 @@ +Component({ + properties: { + settleDetailData: { + type: Object, + value: {}, + observer(settleDetailData) { + const { + outOfStockGoodsList, + abnormalDeliveryGoodsList, + inValidGoodsList, + limitGoodsList, + } = settleDetailData; + // 弹窗逻辑 限购 超出配送范围 失效 库存不足; + const tempList = + limitGoodsList || + abnormalDeliveryGoodsList || + inValidGoodsList || + outOfStockGoodsList || + []; + + tempList.forEach((goods, index) => { + goods.id = index; + goods.unSettlementGoods && + goods.unSettlementGoods.forEach((ele) => { + ele.name = ele.goodsName; + ele.price = ele.payPrice; + ele.imgUrl = ele.image; + }); + }); + this.setData({ + // settleDetailData, + goodsList: tempList, + }); + }, + }, + }, + + data: { + goodList: [], + }, + methods: { + onCard(e) { + const { item } = e.currentTarget.dataset; + if (item === 'cart') { + // 购物车 + Navigator.gotoPage('/cart'); + } else if (item === 'orderSure') { + // 结算页 + this.triggerEvent('change', undefined); + } + }, + onDelive() { + // 修改配送地址 + Navigator.gotoPage('/address', { type: 'orderSure' }); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/order/components/noGoods/noGoods.json b/miniprogram/tcb-shop/pages/order/components/noGoods/noGoods.json new file mode 100644 index 0000000..31f62da --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/noGoods/noGoods.json @@ -0,0 +1,8 @@ +{ + "component": true, + "usingComponents": { + "wr-order-card": "/pages/order/components/order-card/index", + "wr-goods-card": "/components/goods-card/index", + "wr-order-goods-card": "/pages/order/components/order-goods-card/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/components/noGoods/noGoods.wxml b/miniprogram/tcb-shop/pages/order/components/noGoods/noGoods.wxml new file mode 100644 index 0000000..04cc10a --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/noGoods/noGoods.wxml @@ -0,0 +1,53 @@ + + + + + 限购商品信息 + 以下商品限购数量,建议您修改商品数量 + + + 不支持配送 + 以下店铺的商品不支持配送,请更改地址或去掉对应店铺商品再进行结算 + + + 部分商品库存不足或失效 + 请返回购物车重新选择商品,如果继续结算将自动忽略库存不足或失效的商品。 + + + 全部商品库存不足或失效 + 请返回购物车重新选择商品 + + + + + + + + + + + 返回购物车 + + + 修改配送地址 + + + 继续结算 + + + diff --git a/miniprogram/tcb-shop/pages/order/components/noGoods/noGoods.wxss b/miniprogram/tcb-shop/pages/order/components/noGoods/noGoods.wxss new file mode 100644 index 0000000..0331d40 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/noGoods/noGoods.wxss @@ -0,0 +1,68 @@ +/* 层级定义 +@z-index-0: 1; +@z-index-1: 100; +@z-index-2: 200; +@z-index-5: 500; +@z-index-component: 1000; // 通用组件级别 +@z-index-dropdown: @z-index-component; +@z-index-sticky: @z-index-component + 20; +@z-index-fixed: @z-index-component + 30; +@z-index-modal-backdrop:@z-index-component + 40; +@z-index-modal:@z-index-component + 50; +@z-index-popover:@z-index-component + 60; +@z-index-tooltip:@z-index-component + 70; +*/ +/* var() css变量适配*/ +.goods-fail { + display: block; + background: #fff; + font-size: 30rpx; + border-radius: 20rpx 20rpx 0 0; +} +.goods-fail .title { + display: inline-block; + width: 100%; + text-align: center; + margin-top: 30rpx; + line-height: 42rpx; + font-weight: bold; + font-size: 32rpx; +} +.goods-fail .info { + display: block; + font-size: 26rpx; + font-weight: 400; + line-height: 36rpx; + margin: 20rpx auto 10rpx; + text-align: center; + width: 560rpx; + color: #999; +} +.goods-fail .goods-fail-btn { + display: flex; + padding: 30rpx; + justify-content: space-between; + align-items: center; + font-size: 30rpx; +} +.goods-fail .goods-fail-btn .btn { + width: 330rpx; + height: 80rpx; + line-height: 80rpx; + border-radius: 8rpx; + text-align: center; + border: 1rpx solid #999; + background: #fff; + font-size: 32rpx; + color: #666; +} +.goods-fail .goods-fail-btn .btn.origin, +.goods-fail .goods-fail-btn .btn.limit { + color: #fa550f; + color: var(--color-primary, #fa550f); + border: 1rpx solid #fa550f; + border: 1rpx solid var(--color-primary, #fa550f); +} +.goods-fail .goods-fail-btn .btn.limit { + flex-grow: 1; +} diff --git a/miniprogram/tcb-shop/pages/order/components/order-button-bar/index.js b/miniprogram/tcb-shop/pages/order/components/order-button-bar/index.js new file mode 100644 index 0000000..0ea93cd --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/order-button-bar/index.js @@ -0,0 +1,274 @@ +import Dialog from 'tdesign-miniprogram/dialog/index'; +import { ORDER_STATUS, updateOrderStatus } from '../../../../services/order/order'; +import { pay, refund } from '../../../../services/pay/pay'; +import { OrderButtonTypes } from '../../config'; +import { objectToParamString } from '../../../../utils/util'; +import { OPERATION_TYPE } from '../../../../utils/orderOperation'; + +const OPERATION_DONE_EVENT = 'operation'; + +Component({ + options: { + addGlobalClass: true, + }, + properties: { + order: { + type: Object, + observer(order) { + this.init(order); + }, + }, + goodsIndex: { + type: Number, + value: null, + }, + isBtnMax: { + type: Boolean, + value: false, + }, + }, + + data: { + order: {}, + buttons: { + left: [], + right: [], + }, + }, + + methods: { + // 点击【订单操作】按钮,根据按钮类型分发 + onOrderBtnTap(e) { + const { type } = e.currentTarget.dataset; + switch (type) { + case OrderButtonTypes.CANCEL: + this.onCancel(this.data.order); + break; + case OrderButtonTypes.CONFIRM: + this.onConfirm(this.data.order); + break; + case OrderButtonTypes.PAY: + this.onPay(this.data.order); + break; + case OrderButtonTypes.APPLY_REFUND: + this.onApplyRefund(this.data.order); + break; + case OrderButtonTypes.COMMENT: + this.onAddComment(this.data.order); + break; + } + }, + + checkOrder(order, operationType) { + if (order != null) { + return true; + } + this.triggerEvent(OPERATION_DONE_EVENT, { + type: operationType, + message: 'no order', + success: false, + }); + return false; + }, + + async onCancel(order) { + if (!this.checkOrder(order, OPERATION_TYPE.CANCEL)) return; + + // if order is paid, we should first refund + if (order.status !== ORDER_STATUS.TO_PAY) { + try { + await refund(order._id); + } catch (e) { + this.triggerEvent(OPERATION_DONE_EVENT, { + type: OPERATION_TYPE.CANCEL, + message: 'refund failed', + success: false, + detail: e, + }); + return; + } + } + + try { + await updateOrderStatus({ orderId: order._id, status: ORDER_STATUS.CANCELED }); + } catch (e) { + this.triggerEvent(OPERATION_DONE_EVENT, { + type: OPERATION_TYPE.CANCEL, + message: 'update order status failed', + success: false, + detail: e, + }); + return; + } + + this.triggerEvent(OPERATION_DONE_EVENT, { + type: OPERATION_TYPE.CANCEL, + success: true, + }); + }, + + async onConfirm(order) { + if (!this.checkOrder(order, OPERATION_TYPE.CONFIRM)) return; + + try { + await Dialog.confirm({ + title: '确认是否已经收到货?', + content: '', + confirmBtn: '确认收货', + cancelBtn: '取消', + }); + } catch (e) { + this.triggerEvent(OPERATION_DONE_EVENT, { + type: OPERATION_TYPE.CONFIRM, + message: 'confirm dialog failed', + success: false, + detail: e, + }); + return; + } + + try { + await updateOrderStatus({ orderId: order._id, status: ORDER_STATUS.FINISHED }); + } catch (e) { + this.triggerEvent(OPERATION_DONE_EVENT, { + type: OPERATION_TYPE.CONFIRM, + message: 'update order status failed', + success: false, + detail: e, + }); + } + + this.triggerEvent(OPERATION_DONE_EVENT, { + type: OPERATION_TYPE.CONFIRM, + success: true, + }); + }, + + async onPay(order) { + if (!this.checkOrder(order, OPERATION_TYPE.PAY)) return; + + try { + await pay({ id: order._id, totalPrice: order.totalPrice }); + } catch (e) { + this.triggerEvent(OPERATION_DONE_EVENT, { + type: OPERATION_TYPE.PAY, + message: 'pay failed', + success: false, + detail: e, + }); + return; + } + + this.triggerEvent(OPERATION_DONE_EVENT, { + type: OPERATION_TYPE.PAY, + success: true, + }); + }, + + onApplyRefund(order) { + wx.navigateTo({ url: `/pages/order/apply-service/index?${objectToParamString({ orderId: order._id })}` }); + }, + + /** 添加订单评论 */ + onAddComment(order) { + wx.navigateTo({ + url: `/pages/goods/comments/create-list/index?${objectToParamString({ orderId: order._id })}`, + }); + }, + + init(order) { + if (order == null) return; + + if (order.status === ORDER_STATUS.TO_PAY) { + this.setData({ + buttons: { + left: [], + right: [ + { type: OrderButtonTypes.CANCEL, name: '取消订单' }, + { type: OrderButtonTypes.PAY, name: '付款', primary: true }, + ], + }, + }); + return; + } + if (order.status === ORDER_STATUS.TO_SEND) { + this.setData({ + buttons: { + left: [], + right: [{ type: OrderButtonTypes.CANCEL, name: '取消订单' }], + }, + }); + return; + } + if (order.status === ORDER_STATUS.TO_RECEIVE) { + this.setData({ + buttons: { + left: [], + right: [{ type: OrderButtonTypes.CONFIRM, name: '确认收货', primary: true }], + }, + }); + return; + } + if (order.status === ORDER_STATUS.FINISHED) { + this.setData({ + buttons: { + left: [], + right: [{ type: OrderButtonTypes.COMMENT, name: '评价', primary: true }], + }, + }); + return; + } + if (order.status === ORDER_STATUS.CANCELED) { + this.setData({ + buttons: { + left: [], + right: [], + }, + }); + return; + } + if (order.status === ORDER_STATUS.RETURN_APPLIED) { + this.setData({ + buttons: { + left: [], + right: [], + }, + }); + return; + } + if (order.status === ORDER_STATUS.RETURN_REFUSED) { + this.setData({ + buttons: { + left: [], + right: [], + }, + }); + return; + } + if (order.status === ORDER_STATUS.RETURN_FINISH) { + this.setData({ + buttons: { + left: [], + right: [], + }, + }); + return; + } + if (order.status === ORDER_STATUS.RETURN_MONEY_REFUSED) { + this.setData({ + buttons: { + left: [], + right: [], + }, + }); + return; + } + }, + }, + + lifetimes: { + attached() { + this.init(this.data.order); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/order/components/order-button-bar/index.json b/miniprogram/tcb-shop/pages/order/components/order-button-bar/index.json new file mode 100644 index 0000000..3084bb3 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/order-button-bar/index.json @@ -0,0 +1,8 @@ +{ + "component": true, + "usingComponents": { + "t-button": "tdesign-miniprogram/button/button", + "t-toast": "tdesign-miniprogram/toast/toast", + "t-dialog": "tdesign-miniprogram/dialog/dialog" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/components/order-button-bar/index.wxml b/miniprogram/tcb-shop/pages/order/components/order-button-bar/index.wxml new file mode 100644 index 0000000..492b87a --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/order-button-bar/index.wxml @@ -0,0 +1,37 @@ + + + + {{leftBtn.name}} + + + + + {{rightBtn.name}} + + + + + diff --git a/miniprogram/tcb-shop/pages/order/components/order-button-bar/index.wxss b/miniprogram/tcb-shop/pages/order/components/order-button-bar/index.wxss new file mode 100644 index 0000000..73385a2 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/order-button-bar/index.wxss @@ -0,0 +1,54 @@ +:host { + width: 100%; +} +.btn-bar { + display: flex; + justify-content: space-between; + align-items: center; + line-height: 1; +} +.btn-bar .order-btn { + line-height: 1; + /* border-radius: unset; */ + /* min-width: 160rpx; */ +} + +.btn-bar .right { + display: flex; + align-items: center; +} +.btn-bar .t-button { + width: 160rpx; + font-weight: 400; + margin-left: 24rpx; +} +.btn-bar .t-button--max { + width: 176rpx; + margin-left: 24rpx; + + --td-button-extra-small-height: 72rpx; +} + +.btn-bar .left .delete-btn { + font-size: 22rpx; +} +.btn-bar .left .delete-btn::after { + display: none; +} + +.btn-bar .right .normal { + --td-button-default-color: #333333; + --td-button-default-border-color: #dddddd; +} + +.btn-bar .right .primary { + --td-button-default-color: #fff; + --td-button-default-bg-color: #fa4126; + --td-button-default-border-color: #fa4126; + --td-button-default-active-bg-color: #fa42269c; +} + +.t-button { + --td-button-default-color: #000; + --td-button-primary-text-color: #fa4126; +} diff --git a/miniprogram/tcb-shop/pages/order/components/order-card/index.js b/miniprogram/tcb-shop/pages/order/components/order-card/index.js new file mode 100644 index 0000000..9b615d2 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/order-card/index.js @@ -0,0 +1,88 @@ +import { orderStatusToName } from '../../../../services/order/order'; + +Component({ + externalClasses: ['wr-class', 'header-class', 'title-class'], + + options: { + multipleSlots: true, + }, + + relations: { + '../order-goods-card/index': { + type: 'descendant', + linked(target) { + this.children.push(target); + this.setHidden(); + }, + unlinked(target) { + this.children = this.children.filter((item) => item !== target); + }, + }, + '../goods-card/index': { + type: 'descendant', + linked(target) { + this.children.push(target); + this.setHidden(); + }, + unlinked(target) { + this.children = this.children.filter((item) => item !== target); + }, + }, + '../specs-goods-card/index': { + type: 'descendant', + linked(target) { + this.children.push(target); + this.setHidden(); + }, + unlinked(target) { + this.children = this.children.filter((item) => item !== target); + }, + }, + }, + + created() { + this.children = []; + }, + + properties: { + order: { + type: Object, + observer(order) { + const goodsCount = order?.orderItems?.length; + if (typeof goodsCount !== 'number') return; + + this.setData({ + goodsCount, + }); + }, + }, + useTopLeftSlot: Boolean, + useTopRightSlot: Boolean, + // 初始显示的商品数量,超出部分会隐藏。 + defaultShowNum: { + type: null, + value: 10, + }, + }, + + data: { + showAll: true, // 是否展示所有商品,设置为false,可以使用展开更多功能 + goodsCount: 0, + }, + + methods: { + setHidden() { + const isHidden = !this.data.showAll; + this.children.forEach((c, i) => i >= this.properties.defaultShowNum && c.setHidden(isHidden)); + }, + + onOrderCardTap() { + this.triggerEvent('cardtap'); + }, + + onShowMoreTap() { + this.setData({ showAll: true }, () => this.setHidden()); + this.triggerEvent('showall'); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/order/components/order-card/index.json b/miniprogram/tcb-shop/pages/order/components/order-card/index.json new file mode 100644 index 0000000..8c3cde6 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/order-card/index.json @@ -0,0 +1,7 @@ +{ + "component": true, + "usingComponents": { + "t-image": "/components/webp-image/index", + "t-icon": "tdesign-miniprogram/icon/icon" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/components/order-card/index.wxml b/miniprogram/tcb-shop/pages/order/components/order-card/index.wxml new file mode 100644 index 0000000..83258ee --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/order-card/index.wxml @@ -0,0 +1,17 @@ + + + + + + {{order.statusDesc}} + + + + + + + 展开商品信息(共 {{goodsCount}} 个) + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/components/order-card/index.wxss b/miniprogram/tcb-shop/pages/order/components/order-card/index.wxss new file mode 100644 index 0000000..d241796 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/order-card/index.wxss @@ -0,0 +1,45 @@ +.order-card { + margin: 24rpx 0; + padding: 24rpx 32rpx 24rpx; + background-color: white; + border-radius: 8rpx; +} +.order-card .header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 24rpx; +} +.order-card .header .store-name { + font-size: 28rpx; + font-weight: normal; + color: #333333; + display: flex; + align-items: center; + line-height: 40rpx; +} +.order-card .header .store-name__logo { + margin-right: 16rpx; + font-size: 40rpx; + width: 48rpx; + height: 48rpx; +} +.order-card .header .store-name__label { + max-width: 500rpx; + overflow: hidden; + text-overflow: ellipsis; + word-break: break-all; + white-space: nowrap; +} +.order-card .header .order-status { + font-size: 26rpx; + line-height: 40rpx; + color: #fa4126; +} +.order-card .more-mask { + padding: 20rpx 0; + text-align: center; + background-color: white; + color: #fa4126; + font-size: 24rpx; +} diff --git a/miniprogram/tcb-shop/pages/order/components/order-goods-card/index.js b/miniprogram/tcb-shop/pages/order/components/order-goods-card/index.js new file mode 100644 index 0000000..edaf299 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/order-goods-card/index.js @@ -0,0 +1,43 @@ +Component({ + options: { + addGlobalClass: true, + multipleSlots: true, // 在组件定义时的选项中启用多slot支持 + }, + + relations: { + '../order-card/index': { + type: 'ancestor', + linked(target) { + this.parent = target; + }, + }, + }, + + properties: { + goods: Object, + thumbWidth: Number, + thumbHeight: Number, + thumbWidthInPopup: Number, + thumbHeightInPopup: Number, + noTopLine: Boolean, + step: Boolean, + stepDisabled: Boolean, + }, + + data: { + goods: {}, + hidden: false, + }, + + methods: { + setHidden(hidden) { + if (this.data.hidden === hidden) return; + this.setData({ hidden }); + }, + + onNumChange(e) { + const { value } = e.detail; + this.triggerEvent('num-change', { value }); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/order/components/order-goods-card/index.json b/miniprogram/tcb-shop/pages/order/components/order-goods-card/index.json new file mode 100644 index 0000000..7743bba --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/order-goods-card/index.json @@ -0,0 +1,7 @@ +{ + "component": true, + "usingComponents": { + "t-stepper": "tdesign-miniprogram/stepper/stepper", + "goods-card": "../specs-goods-card/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/components/order-goods-card/index.wxml b/miniprogram/tcb-shop/pages/order/components/order-goods-card/index.wxml new file mode 100644 index 0000000..59778e8 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/order-goods-card/index.wxml @@ -0,0 +1,29 @@ + + + + + + + + + + + diff --git a/miniprogram/tcb-shop/pages/order/components/reason-sheet/index.js b/miniprogram/tcb-shop/pages/order/components/reason-sheet/index.js new file mode 100644 index 0000000..e5741e6 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/reason-sheet/index.js @@ -0,0 +1,114 @@ +Component({ + properties: { + show: Boolean, + title: String, + options: { + type: Object, + observer() { + this.init(); + }, + }, + multiple: { + type: Boolean, + observer() { + this.init(); + }, + }, + showConfirmButton: Boolean, + showCloseButton: Boolean, + confirmButtonText: { + type: String, + value: '确定', + }, + cancelButtonText: { + type: String, + value: '取消', + }, + emptyTip: { + type: String, + value: '请选择', + }, + }, + + data: { + _options: [], + checkedIndexes: [], + }, + + methods: { + attached() { + this.toast = this.selectComponent('#t-toast'); + }, + + init() { + const checkedIndexes = []; + const _options = this.properties.options.map((opt, i) => { + const checked = !!opt.checked; + if (checked) { + if (this.properties.multiple) checkedIndexes[0] = i; + else checkedIndexes.push(i); + } + return { + title: opt.title, + checked, + }; + }); + this.setData({ checkedIndexes, _options }); + }, + + onOptionTap(e) { + const { index } = e.currentTarget.dataset; + const { checkedIndexes } = this.data; + let data = {}; + if (this.properties.multiple) { + if (checkedIndexes.includes(index)) { + checkedIndexes.splice(index, 1); + data = { checkedIndexes, [`_options[${index}].checked`]: false }; + } else { + checkedIndexes.push(index); + data = { checkedIndexes, [`_options[${index}].checked`]: true }; + } + } else { + if (checkedIndexes[0] === index) { + // 单选不可取消选择 + return; + } + data = { + [`_options[${index}].checked`]: true, + checkedIndexes: [index], + }; + if (checkedIndexes[0] !== undefined) { + data[`_options[${checkedIndexes[0]}].checked`] = false; + } + } + this.setData(data); + this.triggerEvent('select', { index }); + this._onOptionTap && this._onOptionTap(index); + if (!this.properties.showConfirmButton && !this.properties.multiple) { + // 没有确认按钮且是单选的情况下,选择选项则自动确定 + this._onConfirm && this._onConfirm([index]); + this.setData({ show: false }); + } + }, + + onCancel() { + this.triggerEvent('cancel'); + this._onCancel && this._onCancel(); + this.setData({ show: false }); + }, + + onConfirm() { + if (this.data.checkedIndexes.length === 0) { + this.toast.show({ + icon: '', + text: this.properties.emptyTip, + }); + return; + } + const indexed = this.data.checkedIndexes; + this.triggerEvent('confirm', { indexed }); + this._onConfirm && this._onConfirm(indexed); + this.setData({ show: false }); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/order/components/reason-sheet/index.json b/miniprogram/tcb-shop/pages/order/components/reason-sheet/index.json new file mode 100644 index 0000000..a22be6b --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/reason-sheet/index.json @@ -0,0 +1,10 @@ +{ + "component": true, + "usingComponents": { + "t-icon": "tdesign-miniprogram/icon/icon", + "t-popup": "tdesign-miniprogram/popup/popup", + "t-cell": "tdesign-miniprogram/cell/cell", + "t-toast": "tdesign-miniprogram/toast/toast", + "t-button": "tdesign-miniprogram/button/button" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/components/reason-sheet/index.wxml b/miniprogram/tcb-shop/pages/order/components/reason-sheet/index.wxml new file mode 100644 index 0000000..35a5df2 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/reason-sheet/index.wxml @@ -0,0 +1,50 @@ + + + + {{title}} + + + + + + + + + + + + {{confirmButtonText}} + + + + + + diff --git a/miniprogram/tcb-shop/pages/order/components/reason-sheet/index.wxss b/miniprogram/tcb-shop/pages/order/components/reason-sheet/index.wxss new file mode 100644 index 0000000..ed92d44 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/reason-sheet/index.wxss @@ -0,0 +1,47 @@ +page view { + box-sizing: border-box; +} +.popup-content { + background-color: white; + color: #222427; + border-radius: 20rpx 20rpx 0 0; + overflow: hidden; +} +.popup-content .header { + height: 100rpx; + line-height: 100rpx; + text-align: center; + vertical-align: middle; + font-size: 32rpx; + font-weight: bold; + position: relative; +} +.popup-content .options { + max-height: 60vh; + overflow-y: scroll; + -webkit-overflow-scrolling: touch; +} +.popup-content .options .cell { + height: 100rpx; + align-items: center; + font-size: 30rpx; + color: #333333; +} +.popup-content .button-bar { + width: 100%; + padding: 20rpx 30rpx; + display: flex; + flex-wrap: nowrap; + align-items: center; + justify-content: space-between; + padding-bottom: calc(20rpx + env(safe-area-inset-bottom)); +} +.popup-content .button-bar .btn { + width: 100%; + background: #fa4126; + color: #fff; + border-radius: 48rpx; +} +.button-bar .btnWrapper { + width: 100%; +} diff --git a/miniprogram/tcb-shop/pages/order/components/reason-sheet/reasonSheet.js b/miniprogram/tcb-shop/pages/order/components/reason-sheet/reasonSheet.js new file mode 100644 index 0000000..384f68f --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/reason-sheet/reasonSheet.js @@ -0,0 +1,25 @@ +function getInstance(context, selector = '#wr-reason-sheet') { + if (!context) { + const pages = getCurrentPages(); + const page = pages[pages.length - 1]; + context = page; + } + const instance = context && context.selectComponent(selector); + if (!instance) { + console.warn(`未找到reason-sheet组件,请检查selector是否正确`); + return null; + } + return instance; +} + +export default function (options) { + const { context, selector, ..._options } = options; + return new Promise((resolve, reject) => { + const instance = getInstance(context, selector); + if (instance) { + instance.setData(Object.assign({}, _options)); + instance._onCancel = () => reject(); + instance._onConfirm = (indexes) => resolve(indexes); + } + }); +} diff --git a/miniprogram/tcb-shop/pages/order/components/selectCoupons/mock.js b/miniprogram/tcb-shop/pages/order/components/selectCoupons/mock.js new file mode 100644 index 0000000..6e67895 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/selectCoupons/mock.js @@ -0,0 +1,22 @@ +export const couponsData = { + couponResultList: [ + { + couponVO: { + condition: '满200元可用', + couponId: 11, + endTime: 1584530282686, + name: '折扣券', + profit: '5.5折', + promotionCode: 90, + promotionSubCode: 1, + scopeText: '部分商品可用', + startTime: 1584530282686, + storeId: 90, + value: 550, + type: 2, + }, + status: 0, // 0:未勾选。1:勾选。-1:置灰 + }, + ], + reduce: 1000, +}; diff --git a/miniprogram/tcb-shop/pages/order/components/selectCoupons/selectCoupon.wxs b/miniprogram/tcb-shop/pages/order/components/selectCoupons/selectCoupon.wxs new file mode 100644 index 0000000..8c4ce2e --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/selectCoupons/selectCoupon.wxs @@ -0,0 +1,16 @@ +function formatDays(value) { + if (value < 10) { + return '0' + value; + } + return value; +} +var dateFormat = function (d) { + var date = getDate(+d); + return ( + date.getFullYear() + + '-' + + formatDays(date.getMonth() + 1) + + formatDays(date.getDate()) + ); +}; +module.exports.dateFormat = dateFormat; diff --git a/miniprogram/tcb-shop/pages/order/components/selectCoupons/selectCoupons.js b/miniprogram/tcb-shop/pages/order/components/selectCoupons/selectCoupons.js new file mode 100644 index 0000000..235ffba --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/selectCoupons/selectCoupons.js @@ -0,0 +1,160 @@ +import dayjs from 'dayjs'; +import { couponsData } from './mock'; + +const emptyCouponImg = `https://cdn-we-retail.ym.tencent.com/miniapp/coupon/ordersure-coupon-newempty.png`; + +Component({ + properties: { + storeId: String, + promotionGoodsList: { + type: Array, + value: [], + }, + orderSureCouponList: { + type: Array, + value: [], + }, + couponsShow: { + type: Boolean, + value: false, + observer(couponsShow) { + if (couponsShow) { + const { promotionGoodsList, orderSureCouponList, storeId } = + this.data; + const products = + promotionGoodsList && + promotionGoodsList.map((goods) => { + this.storeId = goods.storeId; + return { + skuId: goods.skuId, + spuId: goods.spuId, + storeId: goods.storeId, + selected: true, + quantity: goods.num, + prices: { + sale: goods.settlePrice, + }, + }; + }); + const selectedCoupons = + orderSureCouponList && + orderSureCouponList.map((ele) => { + return { + promotionId: ele.promotionId, + storeId: ele.storeId, + couponId: ele.couponId, + }; + }); + this.setData({ + products, + }); + this.coupons({ + products, + selectedCoupons, + storeId, + }).then((res) => { + this.initData(res); + }); + } + }, + }, + }, + data: { + emptyCouponImg, + goodsList: [], + selectedList: [], + couponsList: [], + orderSureCouponList: [], + promotionGoodsList: [], + }, + methods: { + initData(data = {}) { + const { couponResultList = [], reduce = 0 } = data; + const selectedList = []; + let selectedNum = 0; + const couponsList = + couponResultList && + couponResultList.map((coupon) => { + const { status, couponVO } = coupon; + const { + couponId, + condition = '', + endTime = 0, + name = '', + startTime = 0, + value, + type, + } = couponVO; + if (status === 1) { + selectedNum++; + selectedList.push({ + couponId, + promotionId: ruleId, + storeId: this.storeId, + }); + } + const val = type === 2 ? value / 100 : value / 10; + return { + key: couponId, + title: name, + isSelected: false, + timeLimit: `${dayjs(+startTime).format('YYYY-MM-DD')}-${dayjs( + +endTime, + ).format('YYYY-MM-DD')}`, + value: val, + status: status === -1 ? 'useless' : 'default', + desc: condition, + type, + tag: '', + }; + }); + this.setData({ + selectedList, + couponsList, + reduce, + selectedNum, + }); + }, + selectCoupon(e) { + const { key } = e.currentTarget.dataset; + const { couponsList, selectedList } = this.data; + couponsList.forEach((coupon) => { + if (coupon.key === key) { + coupon.isSelected = !coupon.isSelected; + } + }); + + const couponSelected = couponsList.filter( + (coupon) => coupon.isSelected === true, + ); + + this.setData({ + selectedList: [...selectedList, ...couponSelected], + couponsList: [...couponsList], + }); + + this.triggerEvent('sure', { + selectedList: [...selectedList, ...couponSelected], + }); + }, + hide() { + this.setData({ + couponsShow: false, + }); + }, + coupons(coupon = {}) { + return new Promise((resolve, reject) => { + if (coupon?.selectedCoupons) { + resolve({ + couponResultList: couponsData.couponResultList, + reduce: couponsData.reduce, + }); + } + return reject({ + couponResultList: [], + reduce: undefined, + }); + }); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/order/components/selectCoupons/selectCoupons.json b/miniprogram/tcb-shop/pages/order/components/selectCoupons/selectCoupons.json new file mode 100644 index 0000000..c46dc55 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/selectCoupons/selectCoupons.json @@ -0,0 +1,10 @@ +{ + "component": true, + "usingComponents": { + "t-popup": "tdesign-miniprogram/popup/popup", + "t-icon": "tdesign-miniprogram/icon/icon", + "t-image": "/components/webp-image/index", + "wr-price": "/components/price/index", + "coupon-card": "/pages/coupon/components/ui-coupon-card/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/components/selectCoupons/selectCoupons.wxml b/miniprogram/tcb-shop/pages/order/components/selectCoupons/selectCoupons.wxml new file mode 100644 index 0000000..10b8898 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/selectCoupons/selectCoupons.wxml @@ -0,0 +1,43 @@ + + + + + + 选择优惠券 + + + 你有{{couponsList.length}}张可用优惠券 + + 已选中{{selectedNum}}张推荐优惠券, 共抵扣 + + + + + + + + + + + + 此优惠券不能和已勾选的优惠券叠加使用 + + + + + + + 暂无优惠券 + + + + + diff --git a/miniprogram/tcb-shop/pages/order/components/selectCoupons/selectCoupons.wxss b/miniprogram/tcb-shop/pages/order/components/selectCoupons/selectCoupons.wxss new file mode 100644 index 0000000..a8c795f --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/selectCoupons/selectCoupons.wxss @@ -0,0 +1,104 @@ +.select-coupons { + background: #fff; + width: 100%; + position: relative; + border-radius: 20rpx 20rpx 0 0; + padding-top: 28rpx; + padding-bottom: env(safe-area-inset-bottom); +} +.select-coupons .title { + width: 100%; + text-align: center; + font-size: 32rpx; + color: #333; + font-weight: 600; + line-height: 44rpx; +} +.select-coupons .info { + width: 100%; + height: 34rpx; + font-size: 24rpx; + color: #999; + line-height: 34rpx; + margin: 20rpx 0; + padding: 0 20rpx; +} +.select-coupons .info .price { + color: #fa4126; +} +.select-coupons .coupons-list { + max-height: 500rpx; +} +.select-coupons .coupons-list .coupons-wrap { + padding: 0rpx 20rpx; +} +.select-coupons .coupons-list .disable { + font-size: 24rpx; + color: #ff2525; + padding-top: 20rpx; +} +.select-coupons .coupons-list .slot-radio { + position: absolute; + right: 22rpx; + top: 50%; + transform: translateY(-50%); + display: inline-block; +} +.select-coupons .coupons-list .slot-radio .wr-check-filled { + font-size: 36rpx; +} +.select-coupons .coupons-list .slot-radio .check { + width: 36rpx; +} +.select-coupons .coupons-list .slot-radio .text-primary { + color: #fa4126; +} +.select-coupons .coupons-list .slot-radio .wr-check { + font-size: 36rpx; +} +.select-coupons .coupons-list .slot-radio .wr-uncheck { + font-size: 36rpx; + color: #999; +} +.select-coupons .couponp-empty-wrap { + padding: 40rpx; +} +.select-coupons .couponp-empty-wrap .couponp-empty-img { + display: block; + width: 240rpx; + height: 240rpx; + margin: 0 auto; +} +.select-coupons .couponp-empty-wrap .couponp-empty-title { + font-size: 28rpx; + color: #999; + text-align: center; + line-height: 40rpx; + margin-top: 40rpx; +} +.select-coupons .coupons-cover { + height: 112rpx; + width: 100%; + box-sizing: border-box; + margin-top: 30rpx; + padding: 12rpx 32rpx; + display: flex; + justify-content: space-between; + align-items: center; +} +.select-coupons .coupons-cover .btn { + width: 332rpx; + height: 88rpx; + text-align: center; + line-height: 88rpx; + font-size: 32rpx; + border-radius: 44rpx; + box-sizing: border-box; + border: 2rpx solid #dddddd; + color: #333333; +} +.select-coupons .coupons-cover .red { + border-color: #fa4126; + background-color: #fa4126; + color: #ffffff; +} diff --git a/miniprogram/tcb-shop/pages/order/components/specs-goods-card/index.js b/miniprogram/tcb-shop/pages/order/components/specs-goods-card/index.js new file mode 100644 index 0000000..d5cd284 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/specs-goods-card/index.js @@ -0,0 +1,132 @@ +Component({ + options: { + addGlobalClass: true, + multipleSlots: true, // 在组件定义时的选项中启用多slot支持 + }, + + externalClasses: [ + 'title-class', + 'desc-class', + 'num-class', + 'thumb-class', + 'specs-class', + 'price-class', + 'origin-price-class', + 'price-prefix-class', + ], + + relations: { + '../order-card/index': { + type: 'ancestor', + linked(target) { + this.parent = target; + }, + }, + }, + + properties: { + id: String, + hidden: { + // 设置为null代表不做类型转换 + type: null, + observer(hidden) { + // null就是代表没有设置,没有设置的话不setData,防止祖先组件触发的setHidden操作被覆盖 + if (hidden !== null) { + this.setHidden(!!hidden); + } + }, + }, + data: Object, + layout: { + type: String, + value: 'horizontal', + }, + thumbMode: { + type: String, + value: 'aspectFill', + }, + thumbWidth: Number, + thumbHeight: Number, + thumbWidthInPopup: Number, + thumbHeightInPopup: Number, + priceFill: { + type: Boolean, + value: true, + }, + currency: { + type: String, + value: '¥', + }, + lazyLoad: Boolean, + centered: Boolean, + showCart: Boolean, + pricePrefix: String, + cartSize: { + type: Number, + value: 48, + }, + cartColor: { + type: String, + value: '#FA550F', + }, + disablePopup: Boolean, + }, + + data: { + hiddenInData: false, + specsPopup: { + insert: false, + show: false, + }, + }, + + currentInTapSpecs: false, + + lifetimes: { + ready() { + const { hidden } = this.properties; + if (hidden !== null) { + this.setHidden(!!hidden); + } + }, + }, + + methods: { + closeSpecsPopup() { + this.setData({ + 'specsPopup.show': false, + }); + this.triggerEvent('specsclose', { good: this.properties.data }); + }, + + removeSpecsPopup() { + this.setData({ + 'specsPopup.insert': false, + }); + }, + + onClick(e) { + if (this.currentInTapSpecs) { + this.currentInTapSpecs = false; + return; + } + this.triggerEvent('click', e.detail); + }, + + onClickThumb(e) { + this.triggerEvent('thumb', e.detail); + }, + + onClickTag(e) { + this.triggerEvent('tag', e.detail); + }, + + onClickCart(e) { + this.triggerEvent('add-cart', e.detail); + }, + + setHidden(hidden) { + this.setData({ hiddenInData: !!hidden }); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/order/components/specs-goods-card/index.json b/miniprogram/tcb-shop/pages/order/components/specs-goods-card/index.json new file mode 100644 index 0000000..ccbce72 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/specs-goods-card/index.json @@ -0,0 +1,6 @@ +{ + "component": true, + "usingComponents": { + "goods-card": "../goods-card/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/components/specs-goods-card/index.wxml b/miniprogram/tcb-shop/pages/order/components/specs-goods-card/index.wxml new file mode 100644 index 0000000..f6f2a38 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/components/specs-goods-card/index.wxml @@ -0,0 +1,40 @@ + + diff --git a/miniprogram/tcb-shop/pages/order/components/specs-goods-card/index.wxss b/miniprogram/tcb-shop/pages/order/components/specs-goods-card/index.wxss new file mode 100644 index 0000000..e69de29 diff --git a/miniprogram/tcb-shop/pages/order/config.js b/miniprogram/tcb-shop/pages/order/config.js new file mode 100644 index 0000000..20a3317 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/config.js @@ -0,0 +1,94 @@ +export const OrderStatus = { + PENDING_PAYMENT: 5, // 待支付 + PENDING_DELIVERY: 10, // 待发货 + PENDING_RECEIPT: 40, // 待收货 + COMPLETE: 50, // 已完成/待评价 + PAYMENT_TIMEOUT: 80, // 已取消,支付超时 + CANCELED_NOT_PAYMENT: 80, // 已取消,未支付主动取消 + CANCELED_PAYMENT: 80, // 已取消,已支付主动取消 + CANCELED_REJECTION: 80, // 已取消,拒收 +}; + +// 售后状态 10:待审核,20:已审核,30:已收货,40:收货异常,50:已完成,60:已关闭; +export const AfterServiceStatus = { + TO_AUDIT: 10, // 待审核 + THE_APPROVED: 20, // 已审核 + HAVE_THE_GOODS: 30, // 已收货 + ABNORMAL_RECEIVING: 40, // 收货异常 + COMPLETE: 50, // 已完成 + CLOSED: 60, // 已关闭 +}; + +// 售后类型 +export const ServiceType = { + RETURN_GOODS: 10, // 退货退款 + ONLY_REFUND: 20, // 仅退款 + ORDER_CANCEL: 30, // 支付后取消 +}; + +export const ServiceTypeDesc = { + [ServiceType.RETURN_GOODS]: '退货', + [ServiceType.ONLY_REFUND]: '退款', + [ServiceType.ORDER_CANCEL]: '支付后取消', +}; + +// 订单按钮类型 +export const OrderButtonTypes = { + PAY: 1, // 付款 + CANCEL: 2, // 取消订单 + CONFIRM: 3, // 确认收货 + APPLY_REFUND: 4, // 申请售后 + VIEW_REFUND: 5, // 查看退款 + COMMENT: 6, // 评价 + DELETE: 7, // 删除订单 + DELIVERY: 8, // 查看物流 + REBUY: 9, // 再次购买 + INVITE_GROUPON: 11, //邀请好友拼团 +}; + +// 售后服务按钮类型 +export const ServiceButtonTypes = { + REVOKE: 2, // 撤销 + FILL_TRACKING_NO: 3, // 填写运单号 + CHANGE_TRACKING_NO: 4, // 修改运单号 + VIEW_DELIVERY: 5, // 查看物流 +}; + +// 售后状态 +export const ServiceStatus = { + PENDING_VERIFY: 100, //待审核 + VERIFIED: 110, // 已审核待寄回商品 + PENDING_DELIVERY: 120, // 等待买家寄回商品 + PENDING_RECEIPT: 130, // 已寄回商品,待收货 + RECEIVED: 140, // 已收货 + EXCEPTION: 150, // 收货异常 + REFUNDED: 160, // 已退款 + CLOSED: 170, // 已关闭 +}; + +// 售后收货状态 +export const ServiceReceiptStatus = { + RECEIPTED: 1, // 已收到货 + NOT_RECEIPTED: 2, // 未收到货 +}; + +// 物流节点 +export const LogisticsNodeTypes = { + SUBMITTED: 200001, // 已提交订单 + PAYMENTED: 200002, // 已付款/已下单 + SHIPPED: 200003, // 已发货 + CANCELED: 200004, // 已取消 + RECEIVED: 200005, // 已签收 + ADDRESS_CHANGED: 200006, // 已修改地址 + IN_TRANSIT: 200007, // 运输中 +}; + +export const LogisticsIconMap = { + [LogisticsNodeTypes.SUBMITTED]: '', + [LogisticsNodeTypes.PAYMENTED]: 'credit_card', + [LogisticsNodeTypes.SHIPPED]: 'deliver', + [LogisticsNodeTypes.CANCELED]: '', + [LogisticsNodeTypes.RECEIVED]: 'check', + [LogisticsNodeTypes.ADDRESS_CHANGED]: '', + [LogisticsNodeTypes.IN_TRANSIT]: 'yunshuzhong', +}; diff --git a/miniprogram/tcb-shop/pages/order/delivery-detail/index.js b/miniprogram/tcb-shop/pages/order/delivery-detail/index.js new file mode 100644 index 0000000..f10ff02 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/delivery-detail/index.js @@ -0,0 +1,43 @@ +Page({ + data: { + logisticsData: { + logisticsNo: '', + nodes: [], + company: '', + phoneNumber: '', + }, + active: 0, + }, + + onLoad(query) { + let data; + try { + data = JSON.parse(decodeURIComponent(query.data || '{}')); + } catch (e) { + console.warn('物流节点数据解析失败', e); + } + if (Number(query.source) === 2) { + const service = { + company: data.logisticsCompanyName, + logisticsNo: data.logisticsNo, + nodes: data.nodes, + }; + this.setData({ + logisticsData: service, + }); + } else if (data) { + this.setData({ logisticsData: data }); + } + }, + + onLogisticsNoCopy() { + wx.setClipboardData({ data: this.data.logisticsData.logisticsNo }); + }, + + onCall() { + const { phoneNumber } = this.data.logisticsData; + wx.makePhoneCall({ + phoneNumber, + }); + }, +}); diff --git a/miniprogram/tcb-shop/pages/order/delivery-detail/index.json b/miniprogram/tcb-shop/pages/order/delivery-detail/index.json new file mode 100644 index 0000000..2ba9163 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/delivery-detail/index.json @@ -0,0 +1,11 @@ +{ + "navigationBarTitleText": "物流信息", + "usingComponents": { + "t-cell": "tdesign-miniprogram/cell/cell", + "t-cell-group": "tdesign-miniprogram/cell-group/cell-group", + "t-image": "/components/webp-image/index", + "t-icon": "tdesign-miniprogram/icon/icon", + "t-steps": "tdesign-miniprogram/steps/steps", + "t-step": "tdesign-miniprogram/step-item/step-item" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/delivery-detail/index.wxml b/miniprogram/tcb-shop/pages/order/delivery-detail/index.wxml new file mode 100644 index 0000000..b5a8bf5 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/delivery-detail/index.wxml @@ -0,0 +1,91 @@ + + var isUrl = function(item) { + return item.indexOf('http') > -1; + } + module.exports = { + isUrl: isUrl, + } + + + + + + {{logisticsData.logisticsNo}} + 复制 + + + + + 拨打 + + + + + + + + + + + + + + + {{item.desc}} + {{item.date}} + + + + + diff --git a/miniprogram/tcb-shop/pages/order/delivery-detail/index.wxss b/miniprogram/tcb-shop/pages/order/delivery-detail/index.wxss new file mode 100644 index 0000000..6a2524c --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/delivery-detail/index.wxss @@ -0,0 +1,99 @@ +page { + background-color: #f5f5f5; +} +.page-section { + margin-top: 24rpx; + background-color: white; +} +.page-section .order-group__left { + margin-right: 0 !important; +} +.cell-steps { + padding: 8rpx; +} +.wr-cell__title { + flex: none; + font-size: 28rpx; + color: #666; +} +.wr-cell__value { + flex: auto; + margin-left: 30rpx; + font-size: 28rpx; + color: #333 !important; +} +.logistics-no { + display: inline-block; + text-align: left; + word-break: break-all; + color: #333; +} +.text-btn { + margin-left: 20rpx; + display: inline; + font-size: 24rpx; + padding: 0 15rpx; + border: 1rpx solid #ddd; + border-radius: 28rpx; + color: #333; +} +.text-btn--active { + opacity: 0.5; +} +.steps .step-title { + font-weight: bold; + color: #333 !important; + font-size: 30rpx; +} +.steps .step-desc { + color: #333333; + font-size: 28rpx; +} +.steps .step-date { + color: #999999; + font-size: 24rpx; +} + +.cell-steps__img, +.cell-steps__imgWrapper { + width: 48rpx; + height: 48rpx; +} + +.steps + .t-step--vertical.t-step--default-anchor + .t-steps-item--process + .t-steps-item__icon-number { + background: #ffece9 !important; + color: white !important; + border: none; +} + +.steps + .t-step--vertical.t-step--default-anchor + .t-steps-item--default + .t-steps-item__icon-number { + color: white !important; + background: #f5f5f5 !important; + border: none; +} + +.steps + .t-step--vertical.t-step--default-anchor.t-step--not-last-child + .t-steps-item__inner::after { + top: 48rpx; + height: calc(100% - 44rpx - 4rpx); +} + +.steps + .t-step--vertical.t-step--default-anchor.t-step--not-last-child + .t-steps-item__inner::after, +.steps + .t-step--vertical.t-step--default-anchor.t-step--not-last-child + .t-steps-item--default + .t-steps-item__inner:after { + background: #f5f5f5 !important; +} +.page-section__steps { + padding: 24rpx; +} diff --git a/miniprogram/tcb-shop/pages/order/fill-tracking-no/api.js b/miniprogram/tcb-shop/pages/order/fill-tracking-no/api.js new file mode 100644 index 0000000..33f41de --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/fill-tracking-no/api.js @@ -0,0 +1,71 @@ +import { mockIp, mockReqId } from '../../../utils/mock'; + +export function create() { + const _resq = { + data: null, + code: 'Success', + msg: null, + requestId: mockReqId(), + clientIp: mockIp(), + rt: 79, + success: true, + }; + return Promise.resolve(_resq); +} + +export function update() { + const _resq = { + data: null, + code: 'Success', + msg: null, + requestId: mockReqId(), + clientIp: mockIp(), + rt: 79, + success: true, + }; + return Promise.resolve(_resq); +} + +export function getDeliverCompanyList() { + const _resq = { + data: [ + { + name: '中通快递', + code: '0001', + }, + { + name: '申通快递', + code: '0002', + }, + { + name: '圆通快递', + code: '0003', + }, + { + name: '顺丰快递', + code: '0004', + }, + { + name: '百世快递', + code: '0005', + }, + { + name: '韵达快递', + code: '0006', + }, + { + name: '邮政快递', + code: '0007', + }, + { + name: '丰网快递', + code: '0008', + }, + { + name: '顺丰直邮', + code: '0009', + }, + ], + }; + return Promise.resolve(_resq); +} diff --git a/miniprogram/tcb-shop/pages/order/fill-tracking-no/index.js b/miniprogram/tcb-shop/pages/order/fill-tracking-no/index.js new file mode 100644 index 0000000..dd6ff3b --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/fill-tracking-no/index.js @@ -0,0 +1,190 @@ +import Dialog from 'tdesign-miniprogram/dialog/index'; +import Toast from 'tdesign-miniprogram/toast/index'; +import reasonSheet from '../components/reason-sheet/reasonSheet'; +import { getDeliverCompanyList, create, update } from './api'; + +Page({ + deliveryCompanyList: [], + + data: { + trackingNo: '', + remark: '', + deliveryCompany: null, + submitActived: false, + submitting: false, + }, + onLoad(query) { + const { + rightsNo = '', + logisticsNo = '', + logisticsCompanyName = '', + logisticsCompanyCode = '', + remark = '', + } = query; + + if (!rightsNo) { + Dialog.confirm({ + title: '请选择售后单?', + content: '', + confirmBtn: '确认', + }).then(() => { + wx.navigateBack({ backRefresh: true }); + }); + } + this.rightsNo = rightsNo; + if (logisticsNo) { + wx.setNavigationBarTitle({ + title: '修改运单号', + fail() {}, + }); + this.isChange = true; + this.setData({ + deliveryCompany: { + name: logisticsCompanyName, + code: logisticsCompanyCode, + }, + trackingNo: logisticsNo, + remark, + submitActived: true, + }); + } + this.setWatcher('trackingNo', this.checkParams.bind(this)); + this.setWatcher('deliveryCompany', this.checkParams.bind(this)); + }, + + setWatcher(key, callback) { + let lastData = this.data; + const keys = key.split('.'); + keys.slice(0, -1).forEach((k) => { + lastData = lastData[k]; + }); + const lastKey = keys[keys.length - 1]; + this.observe(lastData, lastKey, callback); + }, + + observe(data, k, callback) { + let val = data[k]; + Object.defineProperty(data, k, { + configurable: true, + enumerable: true, + set: (value) => { + val = value; + callback(); + }, + get: () => { + return val; + }, + }); + }, + + getDeliveryCompanyList() { + if (this.deliveryCompanyList.length > 0) { + return Promise.resolve(this.deliveryCompanyList); + } + return getDeliverCompanyList().then((res) => { + this.deliveryCompanyList = res.data || []; + return this.deliveryCompanyList; + }); + }, + + onInput(e) { + const { key } = e.currentTarget.dataset; + const { value } = e.detail; + this.setData({ [key]: value }); + }, + + onCompanyTap() { + this.getDeliveryCompanyList().then((deliveryCompanyList) => { + reasonSheet({ + show: true, + title: '选择物流公司', + options: deliveryCompanyList.map((company) => ({ + title: company.name, + checked: this.data.deliveryCompany + ? company.code === this.data.deliveryCompany.code + : false, + })), + showConfirmButton: true, + showCancelButton: true, + emptyTip: '请选择物流公司', + }).then((indexes) => { + this.setData({ + deliveryCompany: deliveryCompanyList[indexes[0]], + }); + }); + }); + }, + + checkParams() { + const res = { errMsg: '', require: false }; + + if (!this.data.trackingNo) { + res.errMsg = '请填写运单号'; + res.require = true; + } else if (!this.data.deliveryCompany) { + res.errMsg = '请选择物流公司'; + res.require = true; + } + this.setData({ submitActived: !res.require }); + return res; + }, + + onSubmit() { + const checkRes = this.checkParams(); + if (checkRes.errMsg) { + Toast({ + context: this, + selector: '#t-toast', + message: checkRes.errMsg, + icon: '', + }); + return; + } + + const { + trackingNo, + remark, + deliveryCompany: { code, name }, + } = this.data; + + const params = { + rightsNo: this.rightsNo, + logisticsCompanyCode: code, + logisticsCompanyName: name, + logisticsNo: trackingNo, + remark, + }; + const api = this.isChange ? create : update; + this.setData({ submitting: true }); + api(params) + .then(() => { + this.setData({ submitting: false }); + Toast({ + context: this, + selector: '#t-toast', + message: '保存成功', + icon: '', + }); + setTimeout(() => wx.navigateBack({ backRefresh: true }), 1000); + }) + .catch(() => { + this.setData({ submitting: false }); + }); + }, + + onScanTap() { + wx.scanCode({ + scanType: ['barCode'], + success: (res) => { + Toast({ + context: this, + selector: '#t-toast', + message: '扫码成功', + icon: '', + }); + this.setData({ trackingNo: res.result }); + }, + fail: () => {}, + }); + }, +}); diff --git a/miniprogram/tcb-shop/pages/order/fill-tracking-no/index.json b/miniprogram/tcb-shop/pages/order/fill-tracking-no/index.json new file mode 100644 index 0000000..bd1bb20 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/fill-tracking-no/index.json @@ -0,0 +1,14 @@ +{ + "navigationBarTitleText": "填写运单号", + "usingComponents": { + "t-icon": "tdesign-miniprogram/icon/icon", + "t-cell": "tdesign-miniprogram/cell/cell", + "t-cell-group": "tdesign-miniprogram/cell-group/cell-group", + "t-textarea": "tdesign-miniprogram/textarea/textarea", + "t-input": "tdesign-miniprogram/input/input", + "t-toast": "tdesign-miniprogram/toast/toast", + "t-dialog": "tdesign-miniprogram/dialog/dialog", + "t-button": "tdesign-miniprogram/button/button", + "ui-reason-sheet": "../components/reason-sheet/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/fill-tracking-no/index.wxml b/miniprogram/tcb-shop/pages/order/fill-tracking-no/index.wxml new file mode 100644 index 0000000..7949a0e --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/fill-tracking-no/index.wxml @@ -0,0 +1,51 @@ + + 请填写正确的退货包裹运单信息,以免影响退款进度 + + + + + + + + + + + 备注信息 + + + + + + 保存 + + + + + + diff --git a/miniprogram/tcb-shop/pages/order/fill-tracking-no/index.wxss b/miniprogram/tcb-shop/pages/order/fill-tracking-no/index.wxss new file mode 100644 index 0000000..726ab89 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/fill-tracking-no/index.wxss @@ -0,0 +1,103 @@ +@import '../../../style/theme.wxss'; + +:host { + background-color: #f5f5f5; +} + +.notice-bar { + padding: 24rpx 30rpx; + text-align: center; + font-size: 26rpx; + color: #e17349; + background: #fefcef; +} + +.fill-tracking-no__form { + margin-top: 20rpx; +} + +.fill-tracking-no__form .t-cell__note { + justify-content: flex-start; +} + +.fill-tracking-no__form .t-cell__value { + color: #333 !important; + font-size: 30rpx; + text-align: left; + padding: 0; +} + +.fill-tracking-no__form .t-cell__value::after { + border: none !important; +} + +.fill-tracking-no__form .t-cell__value .t-textarea__wrapper { + padding: 0; +} + +.fill-tracking-no__form .t-input__control, +.fill-tracking-no__form .t-textarea__placeholder, +.fill-tracking-no__form .t-cell__placeholder { + font-size: 30rpx !important; +} + +.fill-tracking-no__form .t-textarea__placeholder, +.fill-tracking-no__form .t-cell__placeholder { + color: #bbbbbb !important; +} + +.t-textarea__note { + width: 100%; +} + +.fill-tracking-no__button-bar { + margin: 38rpx 30rpx 0; +} + +.fill-tracking-no__button-bar .btn { + background-color: transparent; + font-size: 32rpx; + width: 100%; + border-radius: 48rpx; +} + +.fill-tracking-no__button-bar .btn:first-child { + margin-bottom: 20rpx; +} + +.fill-tracking-no__button-bar .btn.confirmBtn { + background: #fa4126; + color: #fff; +} + +.fill-tracking-no__button-bar .btn.disabled { + background-color: #c6c6c6; + color: #fff; +} + +.t-cell-title-width { + width: 160rpx; + flex: none !important; +} +.textarea-wrapper { + background: #fff; + display: flex; + align-items: flex-start; + padding: 24rpx 32rpx 0 32rpx; +} +.t-textarea-wrapper { + box-sizing: border-box; +} + +.fill-tracking-no__form .t-input__wrapper { + margin: 0 !important; +} + +.fill-tracking-no__form { + --td-input-vertical-padding: 0; +} + +.t-button { + --td-button-default-color: #aeb3b7; + --td-button-primary-text-color: #fa4126; +} diff --git a/miniprogram/tcb-shop/pages/order/invoice/index.js b/miniprogram/tcb-shop/pages/order/invoice/index.js new file mode 100644 index 0000000..5333802 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/invoice/index.js @@ -0,0 +1,38 @@ +import { fetchOrderDetail } from '../../../services/order/orderDetail'; + +Page({ + data: { + invoice: {}, + }, + onLoad({ orderNo }) { + this.orderNo = orderNo; + this.init(); + }, + init() { + this.getDetail(); + }, + getDetail() { + const params = { + parameter: this.orderNo, + }; + return fetchOrderDetail(params).then((res) => { + const order = res.data; + + const invoice = { + buyerName: order?.invoiceVO?.buyerName, //个人或公司名称 + buyerTaxNo: order?.invoiceVO?.buyerTaxNo, //税号 + buyerPhone: order?.invoiceVO?.buyerPhone, //手机 + email: order?.invoiceVO?.email, //邮箱 + titleType: order?.invoiceVO?.titleType === 1 ? '个人' : '公司', //发票抬头 1-个人 2-公司 + ontentType: order?.invoiceVO?.ontentType === 1 ? '商品明细' : '2类别', //发票内容 1-明细 2类别 + invoiceType: + order?.invoiceVO?.invoiceType === 5 ? '电子普通发票' : '不开发票', //是否开票 0-不开 5-电子发票 + isInvoice: order?.invoiceVO?.buyerName ? '已开票' : '未开票', + money: order?.invoiceVO?.money, + }; + this.setData({ + invoice, + }); + }); + }, +}); diff --git a/miniprogram/tcb-shop/pages/order/invoice/index.json b/miniprogram/tcb-shop/pages/order/invoice/index.json new file mode 100644 index 0000000..ea83b65 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/invoice/index.json @@ -0,0 +1,8 @@ +{ + "navigationBarTitleText": "发票详情", + "usingComponents": { + "t-cell": "tdesign-miniprogram/cell/cell", + "t-button": "tdesign-miniprogram/button/button", + "t-cell-group": "tdesign-miniprogram/cell-group/cell-group" + } +} diff --git a/miniprogram/tcb-shop/pages/order/invoice/index.wxml b/miniprogram/tcb-shop/pages/order/invoice/index.wxml new file mode 100644 index 0000000..b3bc794 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/invoice/index.wxml @@ -0,0 +1,40 @@ + + + 发票详情 + + 发票类型 + {{invoice.invoiceType}} + + + 发票抬头 + {{invoice.buyerName}} + + + 纳税人识别号 + {{invoice.buyerTaxNo}} + + + 发票内容 + {{invoice.ontentType}} + + + 发票金额 + {{invoice.money}} + + + + 收票人信息 + + 邮箱 + {{invoice.email}} + + + 手机号 + {{invoice.buyerPhone}} + + + 开票状态 + {{invoice.isInvoice}} + + + diff --git a/miniprogram/tcb-shop/pages/order/invoice/index.wxss b/miniprogram/tcb-shop/pages/order/invoice/index.wxss new file mode 100644 index 0000000..0a0379b --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/invoice/index.wxss @@ -0,0 +1,31 @@ +:host { + background-color: #f5f5f5; +} + +.invoice-detail .invoice-detail-box { + background-color: #fff; + padding: 24rpx 32rpx; + margin-top: 24rpx; +} + +.invoice-detail-title { + font-size: 14px; + font-weight: 600; +} + +.invoice-detail-box-row { + display: flex; + margin-top: 44rpx; +} + +.invoice-detail-box-title { + font-size: 13px; + color: #666666; + width: 156rpx; + margin-right: 32rpx; +} + +.invoice-detail-box-value { + font-size: 13px; + color: #333333; +} diff --git a/miniprogram/tcb-shop/pages/order/order-confirm/components/address-card/index.js b/miniprogram/tcb-shop/pages/order/order-confirm/components/address-card/index.js new file mode 100644 index 0000000..96cf8ef --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/order-confirm/components/address-card/index.js @@ -0,0 +1,25 @@ +/* + * @Author: rileycai + * @Date: 2022-03-05 16:47:16 + * @LastEditTime: 2022-03-05 16:48:32 + * @LastEditors: rileycai + * @Description: + * @FilePath: /tdesign-miniprogram-starter/pages/order/order-confirm/components/address-card/index.js + */ +Component({ + externalClasses: ['wr-class'], + properties: { + addressData: { + type: Object, + value: {}, + }, + }, + methods: { + onAddressTap() { + this.triggerEvent('addressclick'); + }, + onAddTap() { + this.triggerEvent('addclick'); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/order/order-confirm/components/address-card/index.json b/miniprogram/tcb-shop/pages/order/order-confirm/components/address-card/index.json new file mode 100644 index 0000000..08ecc96 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/order-confirm/components/address-card/index.json @@ -0,0 +1,7 @@ +{ + "component": true, + "usingComponents": { + "t-cell": "tdesign-miniprogram/cell/cell", + "t-icon": "tdesign-miniprogram/icon/icon" + } +} diff --git a/miniprogram/tcb-shop/pages/order/order-confirm/components/address-card/index.wxml b/miniprogram/tcb-shop/pages/order/order-confirm/components/address-card/index.wxml new file mode 100644 index 0000000..a6294aa --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/order-confirm/components/address-card/index.wxml @@ -0,0 +1,25 @@ + + var hidePhoneNum = function(array) { + if (!array) return; + var mphone = array.substring(0, 3) + '****' + array.substring(7); + return mphone; + } + module.exports = { + hidePhoneNum:hidePhoneNum + } + + + + + + {{addressData.detailAddress}} + {{addressData.name}} {{utils.hidePhoneNum(addressData.phone)}} + + + + + + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/order-confirm/components/address-card/index.wxss b/miniprogram/tcb-shop/pages/order/order-confirm/components/address-card/index.wxss new file mode 100644 index 0000000..aaff4bd --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/order-confirm/components/address-card/index.wxss @@ -0,0 +1,66 @@ +.address-card { + background: #fff; + margin: 0rpx 0rpx 24rpx; +} +.address-card .wr-cell__title { + color: #999; + margin-left: 6rpx; +} +.address-card .order-address { + display: flex; + width: 100%; +} +.address-card .order-address .address-content { + flex: 1; +} +.order-address .address__right { + align-self: center; +} +.address-card .order-address .title { + display: flex; + align-items: center; + height: 40rpx; + font-size: 28rpx; + font-weight: normal; + color: #999999; + line-height: 40rpx; +} +.address-card .order-address .title .address-tag { + width: 52rpx; + height: 29rpx; + border: 1rpx solid #0091ff; + background-color: rgba(122, 167, 251, 0.1); + text-align: center; + line-height: 29rpx; + border-radius: 8rpx; + color: #0091ff; + font-size: 20rpx; + margin-right: 12rpx; +} +.address-card .order-address .detail { + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + font-size: 36rpx; + font-weight: bold; + color: #333333; + line-height: 48rpx; + margin: 8rpx 0; +} +.address-card .order-address .info { + height: 40rpx; + font-size: 28rpx; + font-weight: normal; + color: #333333; + line-height: 40rpx; +} +.address-card .top-line { + width: 100%; + height: 6rpx; + background-color: #fff; + background-image: url(https://cdn-we-retail.ym.tencent.com/miniapp/order/stripe.png); + background-repeat: repeat-x; + display: block; +} diff --git a/miniprogram/tcb-shop/pages/order/order-confirm/getNotes.wxs b/miniprogram/tcb-shop/pages/order/order-confirm/getNotes.wxs new file mode 100644 index 0000000..935962c --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/order-confirm/getNotes.wxs @@ -0,0 +1,11 @@ +var getNotes = function (storeInfoList, storeIndex) { + if (!storeInfoList) { + return ''; + } + var storeInfo = storeInfoList[storeIndex]; + if (!storeInfo) { + return ''; + } + return storeInfoList[storeIndex].remark; +}; +module.exports = getNotes; diff --git a/miniprogram/tcb-shop/pages/order/order-confirm/handleInvoice.wxs b/miniprogram/tcb-shop/pages/order/order-confirm/handleInvoice.wxs new file mode 100644 index 0000000..30e50f9 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/order-confirm/handleInvoice.wxs @@ -0,0 +1,11 @@ +var handleInvoice = function (invoiceData) { + if (!invoiceData || invoiceData.invoiceType == 0) { + return '暂不开发票'; + } + var title = invoiceData.titleType == 2 ? '公司' : '个人'; + var content = invoiceData.contentType == 2 ? '商品类别' : '商品明细'; + return invoiceData.email + ? '电子普通发票 (' + content + ' - ' + title + ')' + : '暂不开发票'; +}; +module.exports = handleInvoice; diff --git a/miniprogram/tcb-shop/pages/order/order-confirm/index.js b/miniprogram/tcb-shop/pages/order/order-confirm/index.js new file mode 100644 index 0000000..f2b3cf8 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/order-confirm/index.js @@ -0,0 +1,308 @@ +import Toast from 'tdesign-miniprogram/toast/index'; +import { createOrderItem } from '../../../services/order/orderItem'; +import { createOrder, ORDER_STATUS, updateOrderStatus } from '../../../services/order/order'; +import { getCartItem, deleteCartItem } from '../../../services/cart/cart'; +import { getSkuDetail, updateSku } from '../../../services/sku/sku'; +import { getAddressPromise } from '../../usercenter/address/list/util'; +import { getSingleCloudImageTempUrl } from '../../../utils/cloudImageHandler'; +import { cartShouldFresh } from '../../../utils/cartFresh'; +import { pay } from '../../../services/pay/pay'; + +const stripeImg = `https://cdn-we-retail.ym.tencent.com/miniapp/order/stripe.png`; + +async function createOrderItemFromSku({ count, orderId, skuId }) { + const latestSku = await getSkuDetail(skuId); + const finalCount = latestSku.count - count; + + // check if sku is enough + if (finalCount < 0) { + return Promise.reject({ reason: 'SKU_NOT_ENOUGH' }); + } + + try { + // decrease sku's count + await updateSku({ + skuId, + data: { + count: finalCount, + }, + }); + try { + // create order item + await createOrderItem({ count, orderId, skuId }); + } catch (e) { + console.error(e); + return Promise.reject({ reason: 'CREATE_ORDER_ITEM_FAILED' }); + } + } catch (e) { + console.error(e); + return Promise.reject({ reason: 'SKU_DECREASE_FAILED' }); + } +} + +/** + * + * @param {Object} cartItem + * @param {String} orderId + */ +function createOrderItemFromCartItem(cartItem, orderId) { + return createOrderItemFromSku({ count: cartItem.count, orderId, skuId: cartItem.sku._id }); +} + +/** + * + * @param {Array} cartItems + */ +function cartItemToGoodList(cartItems) { + return cartItems.map((item) => ({ + thumb: item.sku.image, + title: item.sku.spu.name, + specs: item.sku.attr_value.map((v) => v.value).join(','), + price: item.sku.price, + num: item.count, + })); +} + +Page({ + data: { + placeholder: '备注信息', + stripeImg, + loading: true, + orderCardList: [], // 仅用于商品卡片展示 + goodsRequestList: [], + userAddressReq: null, + storeInfoList: [], + promotionGoodsList: [], //当前门店商品列表(优惠券) + currentStoreId: null, //当前优惠券storeId + userAddress: null, + goodsList: [], + cartItems: [], + totalSalePrice: 0, + directSku: null, + }, + + payLock: false, + + type: null, + + async onLoadFromCart(cartIds) { + if (typeof cartIds !== 'string') { + console.error('invalid cart item ids', cartIds); + this.failedAndBack('获取购物车信息失败'); + return; + } + const ids = cartIds.split(','); + try { + const cartItems = await Promise.all( + ids.map(async (id) => { + const cartItem = (await getCartItem({ id })).data; + cartItem.sku = { ...cartItem.sku, ...(await getSkuDetail(cartItem.sku._id)) }; + cartItem.sku.image = await getSingleCloudImageTempUrl(cartItem.sku.image); + return cartItem; + }), + ); + const goodsList = cartItemToGoodList(cartItems); + const totalSalePrice = goodsList.reduce((acc, cur) => acc + cur.price * cur.num, 0); + this.setData({ + goodsList, + totalSalePrice, + cartItems, + }); + } catch (e) { + this.failedAndBack('获取购物车信息失败', e); + } + }, + async onLoadFromDirect(countStr, skuId) { + const count = parseInt(countStr); + if (typeof count !== 'number' || isNaN(count) || typeof skuId !== 'string') { + console.error('invalid cunt or skiId', count, skuId); + this.failedAndBack('初始化信息有误'); + return; + } + + try { + const sku = await getSkuDetail(skuId); + sku.image = await getSingleCloudImageTempUrl(sku.image); + + const goodsList = [ + { + thumb: sku.image, + title: sku.spu.name, + specs: sku.attr_value.map((v) => v.value).join(','), + price: sku.price, + num: count, + }, + ]; + + const totalSalePrice = goodsList.reduce((acc, cur) => acc + cur.price * cur.num, 0); + this.setData({ + goodsList, + totalSalePrice, + directSku: sku, + }); + } catch (e) { + this.failedAndBack('获取商品信息失败', e); + } + }, + + async onLoad(options) { + this.type = options?.type; + if (this.type === 'cart') { + await this.onLoadFromCart(options?.cartIds); + } else if (this.type === 'direct') { + await this.onLoadFromDirect(options?.count, options?.skuId); + } else { + this.failedAndBack('初始化信息有误', 'invalid type'); + } + + this.setData({ + loading: false, + }); + }, + + init() { + this.setData({ + loading: false, + }); + const { goodsRequestList } = this; + this.handleOptionsParams({ goodsRequestList }); + }, + + toast(message) { + Toast({ + context: this, + selector: '#t-toast', + message, + duration: 1000, + icon: '', + }); + }, + + onGotoAddress() { + /** 获取一个Promise */ + getAddressPromise() + .then((address) => { + this.setData({ + userAddress: { + ...address, + detailAddress: address.address, + }, + }); + }) + .catch(() => {}); + + wx.navigateTo({ + url: `/pages/usercenter/address/list/index?selectMode=true`, + }); + }, + onTap() { + this.setData({ + placeholder: '', + }); + }, + + async payImpl(totalPrice, orderId) { + try { + await pay({ _id: orderId, totalPrice }); + try { + await updateOrderStatus({ orderId, status: ORDER_STATUS.TO_SEND }); + this.toast('支付成功'); + } catch (e) { + console.error(e); + this.toast('支付成功,但订单状态更新失败'); + } finally { + setTimeout(() => { + wx.navigateBack(); + }, 1000); + } + } catch (e) { + this.failedAndBack('支付失败', e); + } + }, + + async submitOrderFromCart() { + /** + * 1.创建订单 + * 2.创建订单项 + * 3.删除购物车项 + */ + + const { cartItems, userAddress } = this.data; + const { id: orderId } = await createOrder({ status: ORDER_STATUS.TO_PAY, addressId: userAddress._id }); + + try { + await cartItems.map(async (cartItem) => { + await createOrderItemFromCartItem(cartItem, orderId); + }); + + try { + await cartItems.map(async (cartItem) => { + await deleteCartItem({ cartItemId: cartItem._id }); + // any deletion should notify cart to fresh + cartShouldFresh(); + }); + } catch (e) { + console.error(e); + this.toast('删除购物车失败,请手动删除'); + // do not return, continue to pay + } + + const totalPrice = cartItems.reduce((acc, cur) => acc + cur.count * cur.sku.price, 0); + await this.payImpl(totalPrice, orderId); + } catch (e) { + this.failedAndBack('创建订单失败', e); + } + }, + + async submitOrderFromDirect() { + /** + * 1.创建订单 + * 2.创建订单项 + */ + + const { directSku, userAddress, goodsList } = this.data; + const goods = goodsList[0]; + const { id: orderId } = await createOrder({ status: ORDER_STATUS.TO_PAY, addressId: userAddress._id }); + + try { + await createOrderItemFromSku({ count: goods.num, orderId, skuId: directSku._id }); + const totalPrice = goods.price * goods.num; + + await this.payImpl(totalPrice, orderId); + } catch (e) { + this.failedAndBack('创建订单失败', e); + } + }, + + failedAndBack(message, e) { + e && console.error(e); + this.toast(message); + setTimeout(() => { + wx.navigateBack(); + }, 1000); + }, + + // 提交订单 + async submitOrder() { + const { userAddress } = this.data; + if (!userAddress) { + Toast({ + context: this, + selector: '#t-toast', + message: '请添加收货地址', + duration: 2000, + icon: 'help-circle', + }); + return; + } + + if (this.type === 'cart') { + this.submitOrderFromCart(); + } else if (this.type === 'direct') { + this.submitOrderFromDirect(); + } else { + console.error('invalid type', this.type); + this.failedAndBack('初始化信息有误'); + } + }, +}); diff --git a/miniprogram/tcb-shop/pages/order/order-confirm/index.json b/miniprogram/tcb-shop/pages/order/order-confirm/index.json new file mode 100644 index 0000000..11f25ac --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/order-confirm/index.json @@ -0,0 +1,16 @@ +{ + "navigationBarTitleText": "订单确认", + "usingComponents": { + "t-popup": "tdesign-miniprogram/popup/popup", + "t-toast": "tdesign-miniprogram/toast/toast", + "t-icon": "tdesign-miniprogram/icon/icon", + "t-cell": "tdesign-miniprogram/cell/cell", + "t-dialog": "tdesign-miniprogram/dialog/dialog", + "t-textarea": "tdesign-miniprogram/textarea/textarea", + "price": "/components/price/index", + "select-coupons": "../components/selectCoupons/selectCoupons", + "no-goods": "../components/noGoods/noGoods", + "t-image": "/components/webp-image/index", + "address-card": "./components/address-card/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/order-confirm/index.wxml b/miniprogram/tcb-shop/pages/order/order-confirm/index.wxml new file mode 100644 index 0000000..b01ce44 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/order-confirm/index.wxml @@ -0,0 +1,39 @@ + + + + + + + {{goods.title}} + {{goods.specs}} + + + + x{{goods.num}} + + + + + + 商品总额 + + + + + + 共{{goodsList.length}}件 + 小计 + + + + + + + + 提交订单 + + + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/order-confirm/index.wxss b/miniprogram/tcb-shop/pages/order/order-confirm/index.wxss new file mode 100644 index 0000000..8b201c8 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/order-confirm/index.wxss @@ -0,0 +1,221 @@ +.order-sure { + box-sizing: border-box; + background: #f6f6f6; + padding: 24rpx 0 calc(env(safe-area-inset-bottom) + 136rpx); + min-height: 100vh; +} + +.order-sure .wx-pay-cover { + position: fixed; + left: 0; + bottom: 0; + right: 0; + z-index: 10; + background: #fff; + height: 112rpx; + padding-bottom: env(safe-area-inset-bottom); +} +.order-sure .wx-pay-cover .wx-pay { + width: 100%; + height: 100rpx; + box-sizing: border-box; + padding: 0rpx 32rpx; + display: flex; + justify-content: space-between; + align-items: center; +} +.order-sure .wx-pay-cover .wx-pay .price { + color: #fa4126; + font-weight: bold; + font-size: 63rpx; + line-height: 88rpx; +} + +.order-sure .wx-pay-cover .wx-pay .submit-btn { + height: 80rpx; + width: 240rpx; + border-radius: 40rpx; + background-color: #fa4126; + color: #ffffff; + line-height: 80rpx; + font-weight: bold; + font-size: 28rpx; + text-align: center; +} +.order-sure .wx-pay-cover .wx-pay .btn-gray { + background: #cccccc; +} + +.order-wrapper .store-wrapper { + width: 100%; + height: 96rpx; + box-sizing: border-box; + padding: 0 32rpx; + display: flex; + align-items: center; + font-size: 28rpx; + line-height: 40rpx; + color: #333333; + background-color: #ffffff; +} +.order-wrapper .store-wrapper .store-logo { + margin-right: 16rpx; +} +.order-wrapper .goods-wrapper { + width: 100%; + box-sizing: border-box; + padding: 16rpx 32rpx; + display: flex; + align-items: flex-start; + justify-content: space-between; + font-size: 24rpx; + line-height: 32rpx; + color: #999999; + background-color: #ffffff; +} +.goods-wrapper .goods-image { + width: 176rpx; + height: 176rpx; + border-radius: 8rpx; + overflow: hidden; + margin-right: 16rpx; +} +.goods-wrapper .goods-content { + flex: 1; +} + +.goods-wrapper .goods-content .goods-title { + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + overflow: hidden; + text-overflow: ellipsis; + font-size: 28rpx; + line-height: 40rpx; + margin-bottom: 12rpx; + color: #333333; + margin-right: 16rpx; +} + +.goods-wrapper .goods-right { + min-width: 128rpx; + display: flex; + flex-direction: column; + align-items: flex-end; +} + +.goods-right .goods-price { + color: #333333; + font-size: 32rpx; + line-height: 48rpx; + font-weight: bold; + margin-bottom: 16rpx; +} + +.goods-right .goods-num { + text-align: right; +} + +.order-sure .pay-detail { + background-color: #ffffff; + padding: 16rpx 32rpx; + width: 100%; + box-sizing: border-box; +} + +.order-sure .pay-detail .pay-item { + width: 100%; + height: 72rpx; + display: flex; + align-items: center; + justify-content: space-between; + font-size: 26rpx; + line-height: 36rpx; + color: #666666; +} +.order-sure .pay-detail .pay-item .pay-item__right { + color: #333333; + font-size: 24rpx; + display: flex; + align-items: center; + justify-content: flex-end; + max-width: 400rpx; +} +.order-sure .pay-detail .pay-item .pay-item__right .pay-remark { + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + max-width: 400rpx; + text-overflow: ellipsis; + overflow: hidden; +} +.order-sure .pay-detail .pay-item .font-bold { + font-weight: bold; +} +.order-sure .pay-detail .pay-item .primary { + color: #fa4126; +} + +.add-notes .add-notes__content { + --td-textarea-background-color: #f5f5f5; +} + +.add-notes .t-textarea__placeholder { + color: #aeb3b7; +} + +.add-notes .add-notes__textarea__font { + font-size: 26rpx; +} +.add-notes .add-notes__textarea { + margin-top: 32rpx; +} + +.order-sure .add-notes .dialog__message { + border-radius: 8rpx; +} + +.order-sure .add-notes .dialog__button-cancel::after { + border-right: 0; +} + +.order-sure .amount-wrapper { + width: 100%; + box-sizing: border-box; + background-color: #ffffff; + padding: 0rpx 32rpx; + height: 96rpx; +} + +.order-sure .pay-amount { + width: 100%; + height: 96rpx; + display: flex; + align-items: center; + justify-content: flex-end; + font-size: 28rpx; + color: #333333; + position: relative; +} +.order-sure .pay-amount::after { + position: absolute; + content: ' '; + top: 0; + left: 0; + width: 200%; + height: 200%; + transform: scale(0.5); + transform-origin: 0 0; + border-top: 2rpx solid #f5f5f5; +} +.order-sure .pay-amount .order-num { + color: #999999; + padding-right: 8rpx; +} + +.order-sure .pay-amount .total-price { + font-size: 36rpx; + color: #fa4126; + font-weight: bold; + padding-left: 8rpx; +} diff --git a/miniprogram/tcb-shop/pages/order/order-confirm/order.wxs b/miniprogram/tcb-shop/pages/order/order-confirm/order.wxs new file mode 100644 index 0000000..42f3de1 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/order-confirm/order.wxs @@ -0,0 +1,8 @@ +var toHide = function (array) { + if (!array) return; + var mphone = array.substring(0, 3) + '****' + array.substring(7); + return mphone; +}; +module.exports = { + toHide: toHide, +}; diff --git a/miniprogram/tcb-shop/pages/order/order-confirm/pay.js b/miniprogram/tcb-shop/pages/order/order-confirm/pay.js new file mode 100644 index 0000000..0c27c3c --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/order-confirm/pay.js @@ -0,0 +1,115 @@ +import Dialog from 'tdesign-miniprogram/dialog/index'; +import Toast from 'tdesign-miniprogram/toast/index'; + +import { dispatchCommitPay } from '../../../services/order/orderConfirm'; + +// 真实的提交支付 +export const commitPay = (params) => { + return dispatchCommitPay({ + goodsRequestList: params.goodsRequestList, // 待结算的商品集合 + invoiceRequest: params.invoiceRequest, // 发票信息 + // isIgnore: params.isIgnore || false, // 删掉 是否忽视库存不足和商品失效,继续结算,true=继续结算 购物车请赋值false + userAddressReq: params.userAddressReq, // 地址信息(用户在购物选择更换地址) + currency: params.currency || 'CNY', // 支付货币: 人民币=CNY,美元=USD + logisticsType: params.logisticsType || 1, // 配送方式 0=无需配送 1=快递 2=商家 3=同城 4=自提 + // orderMark: params.orderMark, // 下单备注 + orderType: params.orderType || 0, // 订单类型 0=普通订单 1=虚拟订单 + payType: params.payType || 1, // 支付类型(0=线上、1=线下) + totalAmount: params.totalAmount, // 新增字段"totalAmount"总的支付金额 + userName: params.userName, // 用户名 + payWay: 1, + authorizationCode: '', //loginCode, // 登录凭证 + storeInfoList: params.storeInfoList, //备注信息列表 + couponList: params.couponList, + groupInfo: params.groupInfo, + }); +}; + +export const paySuccess = (payOrderInfo) => { + const { payAmt, tradeNo, groupId, promotionId } = payOrderInfo; + // 支付成功 + Toast({ + context: this, + selector: '#t-toast', + message: '支付成功', + duration: 2000, + icon: 'check-circle', + }); + + const params = { + totalPaid: payAmt, + orderNo: tradeNo, + }; + if (groupId) { + params.groupId = groupId; + } + if (promotionId) { + params.promotionId = promotionId; + } + const paramsStr = Object.keys(params) + .map((k) => `${k}=${params[k]}`) + .join('&'); + // 跳转支付结果页面 + wx.redirectTo({ url: `/pages/order/pay-result/index?${paramsStr}` }); +}; + +export const payFail = (payOrderInfo, resultMsg) => { + if (resultMsg === 'requestPayment:fail cancel') { + if (payOrderInfo.dialogOnCancel) { + //结算页,取消付款,dialog提示 + Dialog.confirm({ + title: '是否放弃付款', + content: '商品可能很快就会被抢空哦,是否放弃付款?', + confirmBtn: '放弃', + cancelBtn: '继续付款', + }).then(() => { + wx.redirectTo({ url: '/pages/order/order-list/index' }); + }); + } else { + //订单列表页,订单详情页,取消付款,toast提示 + Toast({ + context: this, + selector: '#t-toast', + message: '支付取消', + duration: 2000, + icon: 'close-circle', + }); + } + } else { + Toast({ + context: this, + selector: '#t-toast', + message: `支付失败:${resultMsg}`, + duration: 2000, + icon: 'close-circle', + }); + setTimeout(() => { + wx.redirectTo({ url: '/pages/order/order-list/index' }); + }, 2000); + } +}; + +// 微信支付方式 +export const wechatPayOrder = (payOrderInfo) => { + // const payInfo = JSON.parse(payOrderInfo.payInfo); + // const { timeStamp, nonceStr, signType, paySign } = payInfo; + return new Promise((resolve) => { + // demo 中直接走支付成功 + paySuccess(payOrderInfo); + resolve(); + /* wx.requestPayment({ + timeStamp, + nonceStr, + package: payInfo.package, + signType, + paySign, + success: function () { + paySuccess(payOrderInfo); + resolve(); + }, + fail: function (err) { + payFail(payOrderInfo, err.errMsg); + }, + }); */ + }); +}; diff --git a/miniprogram/tcb-shop/pages/order/order-detail/index.js b/miniprogram/tcb-shop/pages/order/order-detail/index.js new file mode 100644 index 0000000..5ca9433 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/order-detail/index.js @@ -0,0 +1,315 @@ +import Toast from 'tdesign-miniprogram/toast/index'; +import dayjs from 'dayjs'; +import { orderListShouldFresh } from '../../../utils/orderListFresh'; +import { OrderStatus } from '../config'; +import { getAllOrderItemsOfAnOrder } from '../../../services/order/orderItem'; +import { getOrder, orderStatusToName, ORDER_STATUS, updateOrderDeliveryInfo } from '../../../services/order/order'; +import { fetchBusinessTime } from '../../../services/order/orderDetail'; +import { getAddressPromise } from '../../usercenter/address/list/util'; +import { OPERATION_TYPE } from '../../../utils/orderOperation'; + +Page({ + data: { + pageLoading: true, + order: {}, // 后台返回的原始数据 + _order: {}, // 内部使用和提供给 order-card 的数据 + storeDetail: {}, + countDownTime: null, + addressEditable: false, + backRefresh: false, // 用于接收其他页面back时的状态 + formatCreateTime: '', //格式化订单创建时间 + logisticsNodes: [], + /** 订单评论状态 */ + orderHasCommented: true, + }, + + toast(message) { + Toast({ + context: this, + selector: '#t-toast', + message, + }); + }, + + onLoad({ orderId }) { + if (orderId == null) { + toast('异常订单号'); + setTimeout(() => { + wx.navigateBack(); + }, 1000); + } + this.orderId = orderId; + this.init(); + this.navbar = this.selectComponent('#navbar'); + this.pullDownRefresh = this.selectComponent('#wr-pull-down-refresh'); + }, + + onShow() { + // 当从其他页面返回,并且 backRefresh 被置为 true 时,刷新数据 + if (!this.data.backRefresh) return; + this.onRefresh(); + this.setData({ backRefresh: false }); + }, + + onPageScroll(e) { + this.pullDownRefresh && this.pullDownRefresh.onPageScroll(e); + }, + + onImgError(e) { + if (e.detail) { + console.error('img 加载失败'); + } + }, + + // 页面初始化,会展示pageLoading + init() { + this.setData({ pageLoading: true }); + this.getStoreDetail(); + this.getDetail() + .then(() => { + this.setData({ pageLoading: false }); + }) + .catch((e) => { + console.error(e); + }); + }, + + // 页面刷新,展示下拉刷新 + onRefresh() { + this.init(); + // 如果上一页为订单列表,通知其刷新数据 + const pages = getCurrentPages(); + const lastPage = pages[pages.length - 2]; + if (lastPage) { + lastPage.data.backRefresh = true; + } + }, + + // 页面刷新,展示下拉刷新 + onPullDownRefresh_(e) { + const { callback } = e.detail; + return this.getDetail().then(() => callback && callback()); + }, + + async getDetail() { + const orderId = this.orderId; + const [order, orderItems] = await Promise.all([getOrder(orderId), getAllOrderItemsOfAnOrder({ orderId })]); + order.orderItems = orderItems; + order.totalPrice = orderItems.reduce((acc, cur) => acc + cur.count * cur.sku.price, 0); + order.statusDesc = orderStatusToName(order.status); + order.isPaid = order.status !== ORDER_STATUS.TO_PAY; + order.createdTimeString = dayjs(new Date(order.createdAt)).format('YYYY-MM-DD HH:mm:ss'); + + this.setData({ order, addressEditable: order.statusDesc === '待付款' }); + // console.log('ha', order); + // return fetchOrderDetail(params).then((res) => { + // const order = res.data; + // const _order = { + // id: order.orderId, + // orderNo: order.orderNo, + // parentOrderNo: order.parentOrderNo, + // storeId: order.storeId, + // storeName: order.storeName, + // status: order.orderStatus, + // statusDesc: order.orderStatusName, + // amount: order.paymentAmount, + // totalAmount: order.goodsAmountApp, + // logisticsNo: order.logisticsVO.logisticsNo, + // goodsList: (order.orderItemVOs || []).map((goods) => + // Object.assign({}, goods, { + // id: goods.id, + // thumb: goods.goodsPictureUrl, + // title: goods.goodsName, + // skuId: goods.skuId, + // spuId: goods.spuId, + // specs: (goods.specifications || []).map((s) => s.specValue), + // price: goods.tagPrice ? goods.tagPrice : goods.actualPrice, // 商品销售单价, 优先取限时活动价 + // num: goods.buyQuantity, + // titlePrefixTags: goods.tagText ? [{ text: goods.tagText }] : [], + // buttons: goods.buttonVOs || [], + // }), + // ), + // buttons: order.buttonVOs || [], + // createTime: order.createTime, + // receiverAddress: this.composeAddress(order), + // groupInfoVo: order.groupInfoVo, + // }; + // this.setData({ + // order, + // _order, + // formatCreateTime: formatTime(parseFloat(`${order.createTime}`), 'YYYY-MM-DD HH:mm'), // 格式化订单创建时间 + // countDownTime: this.computeCountDownTime(order), + // addressEditable: + // [OrderStatus.PENDING_PAYMENT, OrderStatus.PENDING_DELIVERY].includes(order.orderStatus) && + // order.orderSubStatus !== -1, // 订单正在取消审核时不允许修改地址(但是返回的状态码与待发货一致) + // isPaid: !!order.paymentVO.paySuccessTime, + // invoiceStatus: this.datermineInvoiceStatus(order), + // invoiceDesc: order.invoiceDesc, + // invoiceType: order.invoiceVO?.invoiceType === 5 ? '电子普通发票' : '不开发票', //是否开票 0-不开 5-电子发票 + // logisticsNodes: this.flattenNodes(order.trajectoryVos || []), + // }); + // }); + }, + + datermineInvoiceStatus(order) { + // 1-已开票 + // 2-未开票(可补开) + // 3-未开票 + // 4-门店不支持开票 + return order.invoiceStatus; + }, + + // 拼接省市区 + composeAddress(order) { + return [ + //order.logisticsVO.receiverProvince, + order.logisticsVO.receiverCity, + order.logisticsVO.receiverCountry, + order.logisticsVO.receiverArea, + order.logisticsVO.receiverAddress, + ] + .filter((s) => !!s) + .join(' '); + }, + + getStoreDetail() { + fetchBusinessTime().then((res) => { + const storeDetail = { + storeTel: res.data.telphone, + storeBusiness: res.data.businessTime.join('\n'), + }; + this.setData({ storeDetail }); + }); + }, + + // 仅对待支付状态计算付款倒计时 + // 返回时间若是大于2020.01.01,说明返回的是关闭时间,否则说明返回的直接就是剩余时间 + computeCountDownTime(order) { + if (order.orderStatus !== OrderStatus.PENDING_PAYMENT) return null; + return order.autoCancelTime > 1577808000000 ? order.autoCancelTime - Date.now() : order.autoCancelTime; + }, + + onCountDownFinish() { + //this.setData({ countDownTime: -1 }); + const { countDownTime, order } = this.data; + if (countDownTime > 0 || (order && order.groupInfoVo && order.groupInfoVo.residueTime > 0)) { + this.onRefresh(); + } + }, + + onGoodsCardTap(e) { + const { index } = e.currentTarget.dataset; + const goods = this.data.order.orderItemVOs[index]; + wx.navigateTo({ url: `/pages/goods/details/index?spuId=${goods.spuId}` }); + }, + + async onEditAddressTap() { + const deliveryInfoPromise = getAddressPromise(); + // TODO: check url param + wx.navigateTo({ + url: `/pages/usercenter/address/list/index?selectMode=true`, + }); + try { + const deliveryInfo = await deliveryInfoPromise; + try { + await updateOrderDeliveryInfo({ orderId: this.data.order._id, deliveryInfoId: deliveryInfo._id }); + this.setData({ + 'order.delivery_info._id': deliveryInfo._id, + 'order.delivery_info.phone': deliveryInfo.phone, + 'order.delivery_info.address': deliveryInfo.address, + 'order.delivery_info.name': deliveryInfo.name, + }); + } catch (e) { + console.error(e); + this.toast('更新地址失败'); + } + } catch {} + }, + + onOrderNumCopy() { + wx.setClipboardData({ + data: this.data.order.orderNo, + }); + }, + + onDeliveryNumCopy() { + wx.setClipboardData({ + data: this.data.order.logisticsVO.logisticsNo, + }); + }, + + onToInvoice() { + wx.navigateTo({ + url: `/pages/order/invoice/index?orderNo=${this.data._order.orderNo}`, + }); + }, + + onSuppleMentInvoice() { + wx.navigateTo({ + url: `/pages/order/receipt/index?orderNo=${this.data._order.orderNo}`, + }); + }, + + onDeliveryClick() { + const logisticsData = { + nodes: this.data.logisticsNodes, + company: this.data.order.logisticsVO.logisticsCompanyName, + logisticsNo: this.data.order.logisticsVO.logisticsNo, + phoneNumber: this.data.order.logisticsVO.logisticsCompanyTel, + }; + wx.navigateTo({ + url: `/pages/order/delivery-detail/index?data=${encodeURIComponent(JSON.stringify(logisticsData))}`, + }); + }, + + /** 跳转订单评价 */ + navToCommentCreate() { + wx.navigateTo({ + url: `/pages/order/createComment/index?orderNo=${this.orderNo}`, + }); + }, + + /** 跳转拼团详情/分享页*/ + toGrouponDetail() { + wx.showToast({ title: '点击了拼团' }); + }, + + clickService() { + Toast({ + context: this, + selector: '#t-toast', + message: '您点击了联系客服', + }); + }, + + onOrderInvoiceView() { + wx.navigateTo({ + url: `/pages/order/invoice/index?orderNo=${this.orderNo}`, + }); + }, + + onOperation(e) { + const type = e?.detail?.type; + const success = e?.detail?.success; + + if (type == null) return; + + const resultMessage = success ? '成功' : '失败'; + + let operationMessage; + + if (type === OPERATION_TYPE.CANCEL) { + operationMessage = '取消订单'; + } else if (type === OPERATION_TYPE.CONFIRM) { + operationMessage = '确认收货'; + } else { + operationMessage = '支付'; + } + + this.toast(`${operationMessage}${resultMessage}`); + orderListShouldFresh(); + setTimeout(() => { + wx.navigateBack(); + }, 1000); + }, +}); diff --git a/miniprogram/tcb-shop/pages/order/order-detail/index.json b/miniprogram/tcb-shop/pages/order/order-detail/index.json new file mode 100644 index 0000000..b8bbaec --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/order-detail/index.json @@ -0,0 +1,17 @@ +{ + "navigationBarTitleText": "订单详情", + "usingComponents": { + "t-pull-down-refresh": "tdesign-miniprogram/pull-down-refresh/pull-down-refresh", + "t-button": "tdesign-miniprogram/button/button", + "t-cell": "tdesign-miniprogram/cell/cell", + "t-icon": "tdesign-miniprogram/icon/icon", + "t-image": "/components/webp-image/index", + "t-count-down": "tdesign-miniprogram/count-down/count-down", + "t-toast": "tdesign-miniprogram/toast/toast", + "t-dialog": "tdesign-miniprogram/dialog/dialog", + "price": "/components/price/index", + "order-card": "../components/order-card/index", + "order-goods-card": "../components/order-goods-card/index", + "order-button-bar": "../components/order-button-bar/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/order-detail/index.wxml b/miniprogram/tcb-shop/pages/order/order-detail/index.wxml new file mode 100644 index 0000000..67f1d57 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/order-detail/index.wxml @@ -0,0 +1,60 @@ + + + + + + {{order.statusDesc}} + + + + + + {{order.delivery_info.name + ' '}}{{order.delivery_info.phone}} + {{order.delivery_info.address}} + + 修改 + + + + + + + + + + 商品总额 + + + + {{order.isPaid ? '实付' : '应付'}} + + + + + + + 订单编号 + + {{order._id}} + 复制 + + + + 下单时间 + + {{order.createdTimeString}} + + + + + +  联系客服 + + + + + + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/order-detail/index.wxss b/miniprogram/tcb-shop/pages/order/order-detail/index.wxss new file mode 100644 index 0000000..6bae005 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/order-detail/index.wxss @@ -0,0 +1,245 @@ +:host { + background-color: #f8f8f8; +} + +.order-detail { + width: 100%; + box-sizing: border-box; + padding: 0rpx 0rpx calc(env(safe-area-inset-bottom) + 144rpx); +} + +.order-detail .count-down { + color: #ffffff; +} +.order-detail .header { + width: 100%; + background-color: #ffffff; +} +.order-detail .order-detail__header { + width: 700rpx; + height: 200rpx; + border-radius: 24rpx; + margin: 0 auto; + overflow: hidden; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + background-image: url('https://cdn-we-retail.ym.tencent.com/miniapp/template/order-bg.png'); + background-repeat: no-repeat; + background-size: contain; +} +.order-detail .order-detail__header .title, +.order-detail .order-detail__header .desc { + color: #ffffff; + overflow: hidden; + display: -webkit-box; + -webkit-box-orient: vertical; +} +.order-detail .order-detail__header .title { + -webkit-line-clamp: 1; + font-size: 44rpx; + line-height: 64rpx; + margin-bottom: 8rpx; + font-weight: bold; +} +.order-detail .order-detail__header .desc { + -webkit-line-clamp: 2; + font-size: 24rpx; + line-height: 32rpx; +} +.order-detail .order-detail__header .desc .count-down { + display: inline; +} +.order-detail .order-logistics { + box-sizing: border-box; + padding: 32rpx; + width: 100%; + background-color: #ffffff; + overflow: hidden; + color: #333333; + font-size: 32rpx; + line-height: 48rpx; + display: flex; + position: relative; +} + +.order-logistics .logistics-icon { + width: 40rpx; + height: 40rpx; + margin-right: 16rpx; + margin-top: 4rpx; +} + +.order-logistics .logistics-content { + flex: 1; +} + +.order-logistics .logistics-content .logistics-time { + font-size: 28rpx; + line-height: 40rpx; + color: #999999; + margin-top: 12rpx; +} + +.order-logistics .logistics-back { + color: #999999; + align-self: center; +} + +.order-logistics .edit-text { + color: #fa4126; + font-size: 26rpx; + line-height: 36rpx; +} + +.order-detail .border-bottom { + margin: 0 auto; + width: 686rpx; + scale: 1 0.5; + height: 2rpx; + background-color: #e5e5e5; +} + +.order-detail .border-bottom-margin { + margin: 16rpx auto; +} + +.order-detail .pay-detail { + background-color: #ffffff; + width: 100%; + box-sizing: border-box; +} + +.order-detail .padding-inline { + padding: 16rpx 32rpx; +} + +.order-detail .pay-detail .pay-item { + width: 100%; + height: 72rpx; + display: flex; + align-items: center; + justify-content: space-between; + font-size: 26rpx; + line-height: 36rpx; + color: #666666; + background-color: #ffffff; +} +.order-detail .pay-detail .pay-item .pay-item__right { + color: #333333; + font-size: 24rpx; + display: flex; + align-items: center; + justify-content: flex-end; + max-width: 400rpx; +} +.order-detail .pay-detail .pay-item .pay-item__right .pay-remark { + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + max-width: 400rpx; + text-overflow: ellipsis; + overflow: hidden; +} +.order-detail .pay-detail .pay-item .font-bold { + font-weight: bold; +} +.order-detail .pay-detail .pay-item .primary { + color: #fa4126; +} +.order-detail .pay-detail .pay-item .max-size { + font-size: 36rpx; + line-height: 48rpx; +} + +.pay-item .pay-item__right .pay-item__right__copy { + width: 80rpx; + height: 40rpx; + text-align: center; + font-size: 24rpx; + line-height: 40rpx; + color: #333333; + position: relative; +} + +.pay-item .pay-item__right .pay-item__right__copy::before { + position: absolute; + content: ''; + width: 200%; + height: 200%; + border-radius: 40rpx; + border: 2rpx solid #dddddd; + transform: scale(0.5); + left: 0; + top: 0; + transform-origin: left top; +} + +.pay-item .pay-item__right .order-no { + color: #333333; + font-size: 26rpx; + line-height: 40rpx; + padding-right: 16rpx; +} + +.pay-item .pay-item__right .normal-color { + color: #333333; +} + +.order-detail .pay-detail .pay-service { + width: 100%; + height: 72rpx; + display: flex; + align-items: center; + justify-content: center; + font-size: 32rpx; + line-height: 36rpx; + color: #333333; + background-color: #ffffff; +} + +.bottom-bar { + position: fixed; + left: 0; + bottom: 0; + right: 0; + z-index: 10; + background: #fff; + height: 112rpx; + width: 686rpx; + padding: 0rpx 32rpx env(safe-area-inset-bottom); + display: flex; + align-items: center; +} + +.bottom-bar::before { + position: absolute; + content: ''; + width: 200%; + height: 200%; + border-top: 2rpx solid #dddddd; + transform: scale(0.5); + left: 0; + top: 0; + transform-origin: left top; +} + +.goods-button-bar { + height: 112rpx; + width: 686rpx; + margin-bottom: 16rpx; +} + +.t-class-indicator { + color: #b9b9b9 !important; +} + +.add-notes__confirm { + color: #fa4126 !important; +} + +.t-button { + --td-button-default-color: #000; + --td-button-primary-text-color: #fa4126; +} diff --git a/miniprogram/tcb-shop/pages/order/order-list/index.js b/miniprogram/tcb-shop/pages/order/order-list/index.js new file mode 100644 index 0000000..31cbb1c --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/order-list/index.js @@ -0,0 +1,186 @@ +import Toast from 'tdesign-miniprogram/toast/index'; +import { ORDER_STATUS, listOrder, orderStatusToName } from '../../../services/order/order'; +import { getAllOrderItemsOfAnOrder } from '../../../services/order/orderItem'; +import { LIST_LOADING_STATUS } from '../../../utils/listLoading'; +import { getCloudImageTempUrl } from '../../../utils/cloudImageHandler'; +import { OPERATION_TYPE } from '../../../utils/orderOperation'; +import { shouldFresh, orderListFinishFresh } from '../../../utils/orderListFresh'; + +const ORDER_STATUS_ALL = '0'; + +Page({ + page: { + size: 5, + num: 1, + }, + + data: { + tabs: [ + { key: ORDER_STATUS_ALL, text: '全部', total: 0 }, + { key: ORDER_STATUS.TO_PAY, text: '待付款', total: 0 }, + { key: ORDER_STATUS.TO_SEND, text: '待发货', total: 0 }, + { key: ORDER_STATUS.TO_RECEIVE, text: '待收货', total: 0 }, + { key: ORDER_STATUS.FINISHED, text: '已完成', total: 0 }, + ], + curTab: ORDER_STATUS_ALL, + orderList: [], + listLoading: LIST_LOADING_STATUS.READY, + emptyImg: 'https://cdn-we-retail.ym.tencent.com/miniapp/order/empty-order-list.png', + backRefresh: false, + status: ORDER_STATUS_ALL, + pullDownRefreshing: false, + loadingProps: { + theme: 'circular', + size: '40rpx', + }, + }, + + errorToast(message, e) { + console.error(message, e); + this.toast(message); + }, + + toast(message) { + Toast({ + context: this, + selector: '#t-toast', + message, + duration: 1000, + icon: '', + }); + }, + + onLoad(query) { + const status = this.data.tabs.find((x) => x.key === query.status)?.key ?? ORDER_STATUS_ALL; + this.setData({ + status, + }); + this.refreshList(status); + }, + + async pullRefresh() { + this.setData({ pullDownRefreshing: true }); + try { + await this.onRefresh(); + orderListFinishFresh(); + } catch (e) { + this.errorToast('获取订单列表失败', e); + } finally { + this.setData({ pullDownRefreshing: false }); + } + }, + + async onShow() { + if (!shouldFresh) return; + try { + await this.onRefresh(); + orderListFinishFresh(); + } catch (e) { + this.errorToast('获取订单列表失败', e); + } + }, + + onReachBottom() { + if (this.data.listLoading === LIST_LOADING_STATUS.READY) { + this.getOrderList(this.data.curTab); + } + }, + + async getOrderItems(order) { + const orderId = order._id; + try { + const orderItems = await getAllOrderItemsOfAnOrder({ orderId }); + + const images = orderItems.map((x) => x.sku.image ?? ''); + (await getCloudImageTempUrl(images)).forEach((image, index) => (orderItems[index].sku.image = image)); + + order.orderItems = orderItems; + order.totalPrice = orderItems.reduce((acc, cur) => acc + cur.sku.price * cur.count, 0); + } catch (e) { + this.errorToast('获取订单详情失败', e); + } + }, + + async getOrderList(statusCode = ORDER_STATUS_ALL, reset = false) { + this.setData({ + listLoading: LIST_LOADING_STATUS.LOADING, + }); + try { + const { records, total } = await listOrder({ + pageSize: this.page.size, + pageNumber: this.page.num, + status: statusCode !== ORDER_STATUS_ALL ? statusCode : undefined, + }); + + records.forEach((order) => (order.statusDesc = orderStatusToName(order.status))); + + // async get items for each order + await Promise.all(records.map((order) => this.getOrderItems(order))); + + const orderList = reset ? records : this.data.orderList.concat(records); + const listLoading = orderList.length >= total ? LIST_LOADING_STATUS.NO_MORE : LIST_LOADING_STATUS.READY; // TODO: maybe we should notify user when `length > total`? + + this.setData({ listLoading, orderList }); + const currentNum = reset ? 1 : this.page.num; + this.page.num = currentNum + 1; + } catch (e) { + console.error('获取订单列表失败', e); + this.setData({ listLoading: LIST_LOADING_STATUS.FAILED }); + } + }, + + onReTryLoad() { + this.getOrderList(this.data.curTab); + }, + + onTabChange(e) { + const { value } = e.detail; + this.setData({ + status: value, + }); + this.refreshList(value); + }, + + refreshList(status = ORDER_STATUS_ALL) { + this.page = { + size: this.page.size, + num: 1, + }; + this.setData({ curTab: status, orderList: [] }); + + return this.getOrderList(status, true); + }, + + onRefresh() { + return this.refreshList(this.data.curTab); + }, + + onOrderCardTap(e) { + const { order } = e.currentTarget.dataset; + wx.navigateTo({ + url: `/pages/order/order-detail/index?orderId=${order._id}`, + }); + }, + + onOperation(e) { + const type = e?.detail?.type; + const success = e?.detail?.success; + + if (type == null) return; + + const resultMessage = success ? '成功' : '失败'; + + let operationMessage; + + if (type === OPERATION_TYPE.CANCEL) { + operationMessage = '取消订单'; + } else if (type === OPERATION_TYPE.CONFIRM) { + operationMessage = '确认收货'; + } else { + operationMessage = '支付'; + } + + this.toast(`${operationMessage}${resultMessage}`); + this.onRefresh(); + }, +}); diff --git a/miniprogram/tcb-shop/pages/order/order-list/index.json b/miniprogram/tcb-shop/pages/order/order-list/index.json new file mode 100644 index 0000000..eaa089e --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/order-list/index.json @@ -0,0 +1,17 @@ +{ + "navigationBarTitleText": "我的订单", + "usingComponents": { + "t-tabs": "tdesign-miniprogram/tabs/tabs", + "t-tab-panel": "tdesign-miniprogram/tab-panel/tab-panel", + "t-empty": "tdesign-miniprogram/empty/empty", + "t-toast": "tdesign-miniprogram/toast/toast", + "t-dialog": "tdesign-miniprogram/dialog/dialog", + "t-loading": "tdesign-miniprogram/loading/loading", + "t-pull-down-refresh": "tdesign-miniprogram/pull-down-refresh/pull-down-refresh", + "load-more": "/components/load-more/index", + "order-button-bar": "../components/order-button-bar/index", + "price": "/components/price/index", + "order-card": "../components/order-card/index", + "specs-goods-card": "../components/specs-goods-card/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/order-list/index.wxml b/miniprogram/tcb-shop/pages/order/order-list/index.wxml new file mode 100644 index 0000000..9cd0d09 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/order-list/index.wxml @@ -0,0 +1,36 @@ + + + + + + + + + + + 订单号  + {{order._id}} + + + + + + + 总价  + + + + + + + + + + + 暂无相关订单 + + + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/order-list/index.wxss b/miniprogram/tcb-shop/pages/order/order-list/index.wxss new file mode 100644 index 0000000..c675b3d --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/order-list/index.wxss @@ -0,0 +1,117 @@ +:host { + background-color: #f5f5f5; +} +.page-container .tab-bar__placeholder, +.page-container .tab-bar__inner { + height: 88rpx; + line-height: 88rpx; + background: #fff; +} +.page-container .tab-bar__inner { + font-size: 26rpx; + color: #333333; + position: fixed; + width: 100vw; + top: 0; + left: 0; +} +.page-container .tab-bar__inner.order-nav .order-nav-item .bottom-line { + bottom: 12rpx; +} +.tab-bar__inner .t-tabs-is-active { + color: #fa4126 !important; +} + +.tab-bar__inner .t-tabs-track { + background: #fa4126 !important; +} + +.page-container .tab-bar__active { + font-size: 28rpx; +} +.page-container .specs-popup .bottom-btn { + color: #fa4126; + color: var(--color-primary, #fa4126); +} +.page-container .specs-popup .bottom-btn::after { + border-color: #fa4126; + border-color: var(--color-primary, #fa4126); +} +.dialog .dialog__button-confirm { + color: #fa4126; + color: var(--color-primary, #fa4126); +} +.list-loading { + height: 100rpx; +} +.empty-wrapper { + height: calc(100vh - 88rpx); +} +.btn-bar { + margin-top: 20rpx; +} +.load-more { + margin: 0 24rpx; +} +wr-order-goods-card:not(:first-child) .wr-goods-card { + margin-top: 40rpx; +} + +.price-total { + font-size: 24rpx; + line-height: 32rpx; + color: #999999; + padding-top: 10rpx; + width: 100%; + display: flex; + align-items: baseline; + justify-content: flex-end; +} +.price-total .bold-price { + color: #333333; + font-size: 28rpx; + line-height: 40rpx; + color: #333333; +} +.price-total .real-pay { + font-size: 36rpx; + line-height: 48rpx; + color: #fa4126; + font-weight: bold; +} + +.t-tabs.t-tabs--top .t-tabs-scroll { + border: none !important; +} +.t-empty-text { + font-size: 28rpx; + color: #999; +} + +.page-container .order-number { + color: #666666; + font-size: 28rpx; +} +.t-class-indicator { + color: #b9b9b9 !important; +} +.tab-bar .tab-bar__active { + color: #333333 !important; +} + +.tab-bar .t-tabs-track { + background: #333333 !important; +} + +.t-button { + --td-button-default-color: #000; + --td-button-primary-text-color: #fa4126; +} + +.order-card-loading-container { + text-align: center; +} + +.t-loading__spinner--circular .t-loading__circular { + color: #b9b9b9 !important; +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/pay-result/index.js b/miniprogram/tcb-shop/pages/order/pay-result/index.js new file mode 100644 index 0000000..bd6dad6 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/pay-result/index.js @@ -0,0 +1,47 @@ +/* + * @Author: rileycai + * @Date: 2022-03-14 21:18:07 + * @LastEditTime: 2022-03-22 21:17:16 + * @LastEditors: rileycai + * @Description: + * @FilePath: /tdesign-miniprogram-starter/pages/order/pay-result/index.js + */ +Page({ + data: { + totalPaid: 0, + orderNo: '', + groupId: '', + groupon: null, + spu: null, + adUrl: '', + }, + + onLoad(options) { + const { totalPaid = 0, orderNo = '', groupId = '' } = options; + this.setData({ + totalPaid, + orderNo, + groupId, + }); + }, + + onTapReturn(e) { + const target = e.currentTarget.dataset.type; + const { orderNo } = this.data; + if (target === 'home') { + wx.switchTab({ url: '/pages/home/home' }); + } else if (target === 'orderList') { + wx.navigateTo({ + url: `/pages/order/order-list/index?orderNo=${orderNo}`, + }); + } else if (target === 'order') { + wx.navigateTo({ + url: `/pages/order/order-detail/index?orderNo=${orderNo}`, + }); + } + }, + + navBackHandle() { + wx.navigateBack(); + }, +}); diff --git a/miniprogram/tcb-shop/pages/order/pay-result/index.json b/miniprogram/tcb-shop/pages/order/pay-result/index.json new file mode 100644 index 0000000..f572818 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/pay-result/index.json @@ -0,0 +1,9 @@ +{ + "navigationBarTitleText": "支付结果", + "navigationStyle": "custom", + "usingComponents": { + "t-navbar": "tdesign-miniprogram/navbar/navbar", + "t-icon": "tdesign-miniprogram/icon/icon", + "price": "/components/price/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/order/pay-result/index.wxml b/miniprogram/tcb-shop/pages/order/pay-result/index.wxml new file mode 100644 index 0000000..3153fe6 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/pay-result/index.wxml @@ -0,0 +1,22 @@ + + + + + 支付成功 + + + 微信支付: + + + + 查看订单 + 返回首页 + + + diff --git a/miniprogram/tcb-shop/pages/order/pay-result/index.wxss b/miniprogram/tcb-shop/pages/order/pay-result/index.wxss new file mode 100644 index 0000000..abe1b25 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/pay-result/index.wxss @@ -0,0 +1,54 @@ +.pay-result { + display: flex; + flex-direction: column; + align-items: center; + width: 100%; +} + +.pay-result .pay-status { + margin-top: 100rpx; + font-size: 48rpx; + line-height: 72rpx; + font-weight: bold; + color: #333333; + display: flex; + align-items: center; +} +.pay-result .pay-status text { + padding-left: 12rpx; +} +.pay-result .pay-money { + color: #666666; + font-size: 28rpx; + line-height: 48rpx; + margin-top: 28rpx; + display: flex; + align-items: baseline; +} + +.pay-result .pay-money .pay-money__price { + font-size: 36rpx; + line-height: 48rpx; + color: #fa4126; +} +.pay-result .btn-wrapper { + margin-top: 48rpx; + padding: 12rpx 32rpx; + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + box-sizing: border-box; +} + +.pay-result .btn-wrapper .status-btn { + height: 88rpx; + width: 334rpx; + border-radius: 44rpx; + border: 2rpx solid #fa4126; + color: #fa4126; + font-size: 28rpx; + font-weight: bold; + line-height: 88rpx; + text-align: center; +} diff --git a/miniprogram/tcb-shop/pages/order/receipt/index.js b/miniprogram/tcb-shop/pages/order/receipt/index.js new file mode 100644 index 0000000..3d03225 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/receipt/index.js @@ -0,0 +1,182 @@ +/* eslint-disable no-nested-ternary */ +import Dialog from 'tdesign-miniprogram/dialog/index'; +import Toast from 'tdesign-miniprogram/toast/index'; +import { dispatchSupplementInvoice } from '../../../services/order/orderConfirm'; + +const invoiceJson = { + info: [ + '1.根据当地税务局的要求,开具有效的企业发票需填写税务局登记证号。开具个人发票不需要填写纳税人识别码。 ', + '2.电子普通发票: 电子普通发票是税局认可的有效首付款凭证,其法律效力、基本用途及使用规定同纸质发票,如需纸质发票可自行下载打印。 ', + '3.增值税专用发票: 增值税发票暂时不可开,可查看《开局增值税发票》或致电400-633-6868。', + ], + codeTitle: [ + '1.什么是纳税人识别号/统一社会信用代码? 纳税人识别号,一律由15位、17位、18或者20位码(字符型)组成,其中:企业、事业单位等组织机构纳税人,以国家质量监督检验检疫总局编制的9位码(其中区分主码位与校检位之间的“—”符省略不打印)并在其“纳税人识别号”。国家税务总局下达的纳税人代码为15位,其中:1—2位为省、市代码,3—6位为地区代码,7—8位为经济性质代码,9—10位行业代码,11—15位为各地区自设的顺序码。', + '2.入户获取/知晓纳税人识别号/统一社会信用代码? 纳税人识别号是税务登记证上的号码,通常简称为“税号”,每个企业的纳税人识别号都是唯一的。这个属于每个人自己且终身不变的数字代码很可能成为我们的第二张“身份证”。 ', + ], +}; + +Page({ + orderNo: '', + data: { + receiptIndex: 0, + addressTagsIndex: 0, + goodsClassesIndex: 0, + dialogShow: false, + codeShow: false, + receipts: [ + { title: '不开发票', id: 0, name: 'receipt' }, + { title: '电子发票', id: 1, name: 'receipt' }, + ], + addressTags: [ + { title: '个人', id: 0, name: 'addressTags', type: 1 }, + { title: '公司', id: 1, name: 'addressTags', type: 2 }, + ], + goodsClasses: [ + { title: '商品明细', id: 0, name: 'goodsClasses' }, + { title: '商品类别', id: 1, name: 'goodsClasses' }, + ], + name: '', + componentName: '', + code: '', + phone: '', + email: '', + invoiceInfo: invoiceJson, + }, + onLoad(query) { + const { orderNo, invoiceData } = query; + const tempData = JSON.parse(invoiceData || '{}'); + const invoice = { + receiptIndex: tempData.invoiceType === 5 ? 1 : 0, + name: tempData.buyerName || '', + email: tempData.email || '', + phone: tempData.buyerPhone || '', + addressTagsIndex: tempData.titleType === 2 ? 1 : 0, + goodsClassesIndex: tempData.contentType === 2 ? 1 : 0, + code: tempData.buyerTaxNo || '', + componentName: tempData.titleType === 2 ? tempData.buyerName : '', + }; + this.orderNo = orderNo; + this.setData({ ...invoice }); + }, + onLabels(e) { + const { item } = e.currentTarget.dataset; + const nameIndex = `${item.name}Index`; + this.setData({ [nameIndex]: item.id }); + }, + onInput(e) { + const { addressTagsIndex } = this.data; + const { item } = e.currentTarget.dataset; + const { value } = e.detail; + const key = + item === 'name' + ? addressTagsIndex === 0 + ? 'name' + : 'componentName' + : item === 'code' + ? addressTagsIndex === 0 + ? 'phone' + : 'code' + : 'email'; + this.setData({ [key]: value }); + }, + onSure() { + const result = this.checkSure(); + if (!result) { + Dialog.alert({ + title: '请填写发票信息', + content: '', + confirmBtn: '确认', + }); + return; + } + const { + receiptIndex, + addressTagsIndex, + receipts, + addressTags, + name, + componentName, + code, + phone, + email, + goodsClassesIndex, + } = this.data; + + const data = { + buyerName: addressTagsIndex === 0 ? name : componentName, + buyerTaxNo: code, + buyerPhone: phone, + email, + titleType: addressTags[addressTagsIndex].type, + contentType: goodsClassesIndex === 0 ? 1 : 2, + invoiceType: receiptIndex === 1 ? 5 : 0, + }; + if (this.orderNo) { + if (this.submitting) return; + const params = { + parameter: { + orderNo: this.orderNo, + invoiceVO: data, + }, + }; + this.submitting = true; + dispatchSupplementInvoice(params) + .then(() => { + Toast({ + context: this, + selector: '#t-toast', + message: '保存成功', + duration: 2000, + icon: '', + }); + setTimeout(() => { + this.submitting = false; + wx.navigateBack({ delta: 1 }); + }, 1000); + }) + .catch((err) => { + this.submitting = false; + console.error(err); + }); + } else { + Object.assign(data, { + receipts: receipts[receiptIndex], + addressTags: addressTags[addressTagsIndex], + }); + wx.setStorageSync('invoiceData', data); + wx.navigateBack({ delta: 1 }); + } + }, + checkSure() { + const { name, componentName, code, phone, email, addressTagsIndex, receiptIndex } = this.data; + if (receiptIndex === 0) { + return true; + } + if (addressTagsIndex === 0) { + if (!name.length || !phone.length) { + return false; + } + } else if (addressTagsIndex === 1) { + if (!componentName.length || !code.length) { + return false; + } + } + if (!email.length) { + return false; + } + return true; + }, + onDialogTap() { + const { dialogShow } = this.data; + this.setData({ + dialogShow: !dialogShow, + codeShow: false, + }); + }, + onKnoeCode() { + this.setData({ + dialogShow: !this.data.dialogShow, + codeShow: true, + }); + }, +}); diff --git a/miniprogram/tcb-shop/pages/order/receipt/index.json b/miniprogram/tcb-shop/pages/order/receipt/index.json new file mode 100644 index 0000000..5996047 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/receipt/index.json @@ -0,0 +1,11 @@ +{ + "navigationBarTitleText": "发票", + "usingComponents": { + "t-cell": "tdesign-miniprogram/cell/cell", + "t-dialog": "tdesign-miniprogram/dialog/dialog", + "t-toast": "tdesign-miniprogram/toast/toast", + "t-icon": "tdesign-miniprogram/icon/icon", + "t-input": "tdesign-miniprogram/input/input", + "t-button": "tdesign-miniprogram/button/button" + } +} diff --git a/miniprogram/tcb-shop/pages/order/receipt/index.wxml b/miniprogram/tcb-shop/pages/order/receipt/index.wxml new file mode 100644 index 0000000..a79397b --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/receipt/index.wxml @@ -0,0 +1,135 @@ + + + + + + {{item.title}} + + + + + + + + + {{tag.title}} + + + + + + + + + + + + + + + + + + + + {{good.title}} + + + + 发票内容将显示详细商品名称与价格信息,发票金额为实际支付金额,不包含优惠等扣减金额 + + + 发票须知 + + + + + + + + {{item}} + + + {{item}} + + + + + + + + + 确定 + + + + diff --git a/miniprogram/tcb-shop/pages/order/receipt/index.wxss b/miniprogram/tcb-shop/pages/order/receipt/index.wxss new file mode 100644 index 0000000..c12d654 --- /dev/null +++ b/miniprogram/tcb-shop/pages/order/receipt/index.wxss @@ -0,0 +1,220 @@ +@import '../../../style/theme.wxss'; + +.receipt { + height: 100vh; + background: #f5f5f5; + position: relative; + padding-top: 20rpx; + + --td-input-vertical-padding: 0; +} + +.receipt-cell .t-cell__title { + width: 144rpx; + padding-right: 32rpx; + flex: none !important; +} + +.receipt .t-input__wrapper { + margin: 0 !important; +} +.srcoll-view-wrap { + margin-top: 20rpx; +} +.receipt .flex { + display: flex; + align-items: center; + justify-content: space-between; +} +.receipt .head-title { + color: #333; + font-size: 30rpx; + font-weight: bold; +} +.receipt .btn-wrap { + display: flex; +} +.receipt .btn-wrap .btn { + width: 128rpx; + background: #f5f5f5; + font-size: 24rpx; + color: #333; + margin-right: 22rpx; + text-align: center; + border-radius: 8rpx; + position: relative; + border: 2rpx solid #f5f5f5; +} +.receipt .btn-wrap .active-btn { + background-color: transparent; + border-color: #fa4126; + color: #fa4126; +} +.receipt .title { + width: 100%; + background-color: #fff; + margin-bottom: 20rpx; +} + +.receipt .receipt-label { + display: flex; +} +.receipt .receipt-label .btn { + width: 128rpx; + background: #f5f5f5; + font-size: 24rpx; + color: #333; + margin-left: 22rpx; + text-align: center; + border-radius: 8rpx; + border: 2rpx solid #f5f5f5; +} +.receipt .receipt-label .active-btn { + background-color: transparent; + border-color: #fa4126; + color: #fa4126; +} +.receipt .receipt-label .wr-cell__title { + font-size: 30rpx; + color: #333; + font-weight: bold; +} +.receipt .receipt-content { + background: #fff; + margin-top: 20rpx; +} +.receipt .receipt-content .addressTags { + padding: 0 30rpx; + height: 100rpx; +} +.receipt .receipt-content .addressTags .btn-wrap { + display: flex; +} +.receipt .receipt-content .line { + width: 720rpx; + margin-left: 30rpx; + background-color: #e6e6e6; + height: 1rpx; +} +.receipt .receipt-content .receipt-input { + display: flex; + padding: 0 30rpx; + align-items: center; + height: 100rpx; + color: #666; +} +.receipt .receipt-content .receipt-input .title { + color: #333; + display: inline-block; + width: 140rpx; + margin-right: 30rpx; + font-size: 30rpx; + font-weight: bold; +} +.input-com { + display: inline-block; + flex: 1; + font-size: 30rpx; + font-weight: 400; + line-height: 30rpx; + padding: 0 !important; + color: #666; +} +.input-com::after { + border: none !important; +} + +.receipt .receipt-content .receipt-input .wr-icon { + font-size: 28rpx !important; + margin-left: 20rpx; +} +.receipt .receipt-info { + background: #fff; + margin-top: 20rpx; +} +.receipt .receipt-info .info-con { + padding: 0 30rpx; + height: 100rpx; +} +.receipt .receipt-info .title { + font-size: 24rpx; + color: #999999; + line-height: 36rpx; + padding: 0 30rpx 20rpx; + box-sizing: border-box; +} +.receipt .receipt-know { + display: flex; + align-items: center; + font-size: 26rpx; + font-weight: 400; + color: #999999; + padding: 20rpx 30rpx; + line-height: 26rpx; +} +.receipt .receipt-know .icon { + margin-left: 16rpx; + font-size: 26rpx; +} +.receipt .dialog-receipt .dialog__message { + padding: 0; +} +.receipt .dialog-receipt .dialog-info { + max-height: 622rpx; +} +.receipt .dialog-receipt .info-wrap { + padding: 0 18rpx; +} +.receipt .dialog-receipt .info .title { + display: inline-block; + font-size: 28rpx; + font-weight: 400; + color: #999; + line-height: 40rpx; + margin-bottom: 40rpx; + text-align: left; +} +.receipt .receipt-btn { + position: fixed; + bottom: 0; + left: 0; + right: 0; + z-index: 100; + background: #fff; + width: 100%; + padding: 0 20rpx; + box-sizing: border-box; + padding-bottom: calc(20rpx + env(safe-area-inset-bottom)); +} +.receipt .receipt-btn .receipt-btn-con { + margin-top: 20rpx; + display: inline-block; + width: 100%; + line-height: 80rpx; + background: #fa4126; + text-align: center; + color: #fff; + border-radius: 48rpx; +} + +.cell-left { + margin-right: 0 !important; +} + +.cell-right { + display: flex; + justify-content: flex-start; + width: 480rpx; +} + +.addressTagsIndex-cell { + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; +} + +.t-button { + --td-button-default-color: #000; + --td-button-primary-text-color: #fa4126; +} diff --git a/miniprogram/tcb-shop/pages/promotion-detail/index.js b/miniprogram/tcb-shop/pages/promotion-detail/index.js new file mode 100644 index 0000000..dd6614e --- /dev/null +++ b/miniprogram/tcb-shop/pages/promotion-detail/index.js @@ -0,0 +1,57 @@ +import Toast from 'tdesign-miniprogram/toast/index'; +import { fetchPromotion } from '../../services/promotion/detail'; + +Page({ + data: { + list: [], + banner: '', + time: 0, + showBannerDesc: false, + statusTag: '', + }, + + onLoad(query) { + const promotionID = parseInt(query.promotion_id); + this.getGoodsList(promotionID); + }, + + getGoodsList(promotionID) { + fetchPromotion(promotionID).then( + ({ list, banner, time, showBannerDesc, statusTag }) => { + const goods = list.map((item) => ({ + ...item, + tags: item.tags.map((v) => v.title), + })); + this.setData({ + list: goods, + banner, + time, + showBannerDesc, + statusTag, + }); + }, + ); + }, + + goodClickHandle(e) { + const { index } = e.detail; + const { spuId } = this.data.list[index]; + wx.navigateTo({ url: `/pages/goods/details/index?spuId=${spuId}` }); + }, + + cardClickHandle() { + Toast({ + context: this, + selector: '#t-toast', + message: '点击加购', + }); + }, + + bannerClickHandle() { + Toast({ + context: this, + selector: '#t-toast', + message: '点击规则详情', + }); + }, +}); diff --git a/miniprogram/tcb-shop/pages/promotion-detail/index.json b/miniprogram/tcb-shop/pages/promotion-detail/index.json new file mode 100644 index 0000000..2eb4145 --- /dev/null +++ b/miniprogram/tcb-shop/pages/promotion-detail/index.json @@ -0,0 +1,10 @@ +{ + "navigationBarTitleText": "营销详情", + "usingComponents": { + "t-toast": "tdesign-miniprogram/toast/toast", + "t-image": "/components/webp-image/index", + "t-icon": "tdesign-miniprogram/icon/icon", + "count-down": "tdesign-miniprogram/count-down/count-down", + "goods-list": "/components/goods-list/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/promotion-detail/index.wxml b/miniprogram/tcb-shop/pages/promotion-detail/index.wxml new file mode 100644 index 0000000..be5b5c8 --- /dev/null +++ b/miniprogram/tcb-shop/pages/promotion-detail/index.wxml @@ -0,0 +1,76 @@ + + + + + + + + + + diff --git a/miniprogram/tcb-shop/pages/promotion-detail/index.wxss b/miniprogram/tcb-shop/pages/promotion-detail/index.wxss new file mode 100644 index 0000000..2151b7a --- /dev/null +++ b/miniprogram/tcb-shop/pages/promotion-detail/index.wxss @@ -0,0 +1,111 @@ +.promotion-detail-container .wrap { + display: block; + padding: 0 24rpx; + background: linear-gradient(#fff, #f5f5f5); +} + +.promotion-detail-container .t-class-promotion-head { + width: 702rpx; + height: 160rpx; + border-radius: 8rpx; +} + +.promotion-detail-container .wrap .count-down-wrap { + display: flex; + flex-direction: row; + justify-content: flex-start; + align-items: baseline; + line-height: 34rpx; +} + +.promotion-detail-container .wrap .count-down-wrap.in-banner-count-down-wrap { + position: absolute; + bottom: 32rpx; + left: 32rpx; + right: 32rpx; +} + +.promotion-detail-container .wrap .count-down-wrap .status-tag { + height: 32rpx; + line-height: 32rpx; + font-size: 20rpx; + margin-right: 12rpx; + border-radius: 16rpx; + padding: 0 12rpx; +} + +.promotion-detail-container .wrap .count-down-wrap .status-tag.before { + color: #fff; + background-color: #ff9853; +} + +.promotion-detail-container .wrap .count-down-wrap .status-tag.finish { + color: #fff; + background-color: #ccc; +} + +.promotion-detail-container .wrap .count-down-wrap .count-down-label { + color: #666; + font-size: 24rpx; + margin-right: 0.5em; +} + +.promotion-detail-container .wrap .count-down-wrap .detail-entry { + margin-left: auto; + height: 40rpx; +} + +.promotion-detail-container .wrap .count-down-wrap .detail-entry-label { + color: #fff; + font-size: 24rpx; + margin-right: 12rpx; +} + +.promotion-detail-container + .wrap + .count-down-wrap.after-banner-count-down-wrap { + padding: 10rpx; +} + +.promotion-detail-container + .wrap + .count-down-wrap.after-banner-count-down-wrap + .detail-entry { + display: flex; + align-items: center; +} + +.promotion-detail-container + .wrap + .count-down-wrap.after-banner-count-down-wrap + .detail-entry-label { + color: #999; + margin-right: 0; +} + +.promotion-detail-container .wrap .gl-empty-wrap { + margin-top: 180rpx; +} + +.promotion-detail-container .wrap .gl-empty-img { + width: 240rpx; + height: 240rpx; + display: block; + margin: 0 auto; +} + +.promotion-detail-container .wrap .gl-empty-label { + font-size: 28rpx; + color: #999; + margin-top: 40rpx; + text-align: center; +} + +.promotion-detail-container .goods-list-container { + background: #f5f5f5 !important; +} + +.promotion-detail-container .promotion-goods-list { + padding: 20rpx 24rpx; + background-color: #f5f5f5; +} diff --git a/miniprogram/tcb-shop/pages/usercenter/address/edit/index.js b/miniprogram/tcb-shop/pages/usercenter/address/edit/index.js new file mode 100644 index 0000000..0e0acfc --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/address/edit/index.js @@ -0,0 +1,141 @@ +import Toast from 'tdesign-miniprogram/toast/index'; +import { createAddress, updateAddress } from '../../../../services/address/address'; +import { addressListShouldFresh } from '../../../../utils/addressListFresh'; + +const innerPhoneReg = '^1(?:3\\d|4[4-9]|5[0-35-9]|6[67]|7[0-8]|8\\d|9\\d)\\d{8}$'; +const innerNameReg = '^[a-zA-Z\\d\\u4e00-\\u9fa5]+$'; + +Page({ + options: { + multipleSlots: true, + }, + externalClasses: ['theme-wrapper-class'], + data: { + detailAddress: '', + name: '', + phone: '', + addressId: null, + + loading: false, + }, + setLoading() { + this.setData({ loading: true }); + }, + unsetLoading() { + this.setData({ loading: false }); + }, + onLoad(options) { + const { name, address, _id, phone } = options; + if (![name, address, _id, phone].every((x) => typeof x === 'string')) return; + + this.setData({ + name, + detailAddress: address, + addressId: _id, + phone, + }); + }, + onInputValue(event) { + const { + detail: { value }, + target: { + dataset: { item }, + }, + } = event; + this.setData({ [item]: value }); + }, + onCheckDefaultAddress({ detail }) { + const { value } = detail; + this.setData({ + 'locationState.isDefault': value, + }); + }, + onVerifyInputLegal() { + const { name, phone, detailAddress } = this.data; + const prefixPhoneReg = String(this.properties.phoneReg || innerPhoneReg); + const prefixNameReg = String(this.properties.nameReg || innerNameReg); + const nameRegExp = new RegExp(prefixNameReg); + const phoneRegExp = new RegExp(prefixPhoneReg); + + if (!name || !name.trim()) { + return { + isLegal: false, + tips: '请填写收货人', + }; + } + if (!nameRegExp.test(name)) { + return { + isLegal: false, + tips: '收货人仅支持输入中文、英文(区分大小写)、数字', + }; + } + if (!phone || !phone.trim()) { + return { + isLegal: false, + tips: '请填写手机号', + }; + } + if (!phoneRegExp.test(phone)) { + return { + isLegal: false, + tips: '请填写正确的手机号', + }; + } + if (!detailAddress || !detailAddress.trim()) { + return { + isLegal: false, + tips: '请完善详细地址', + }; + } + if (detailAddress && detailAddress.trim().length > 50) { + return { + isLegal: false, + tips: '详细地址不能超过50个字符', + }; + } + return { + isLegal: true, + tips: '添加成功', + }; + }, + toast(message) { + Toast({ + context: this, + selector: '#t-toast', + message, + icon: '', + duration: 1000, + }); + }, + async formSubmit() { + const { isLegal, tips } = this.onVerifyInputLegal(); + + if (isLegal) { + const { detailAddress, name, phone, addressId } = this.data; + this.setLoading(); + + let action, failedMessage; + if (typeof addressId === 'string') { + console.log('to update'); + action = () => updateAddress({ name, address: detailAddress, phone, _id: addressId }); + failedMessage = '修改地址失败,请稍候重试'; + } else { + console.log('to create'); + action = () => createAddress({ name, phone, address: detailAddress }); + failedMessage = '添加地址失败,请稍候重试'; + } + + try { + await action(); + addressListShouldFresh(); + wx.navigateBack({ delta: 1 }); + } catch { + this.toast(failedMessage); + } finally { + this.unsetLoading(); + } + } else { + this.toast(tips); + } + }, +}); diff --git a/miniprogram/tcb-shop/pages/usercenter/address/edit/index.json b/miniprogram/tcb-shop/pages/usercenter/address/edit/index.json new file mode 100644 index 0000000..e4691da --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/address/edit/index.json @@ -0,0 +1,16 @@ +{ + "navigationBarTitleText": "添加新地址", + "usingComponents": { + "t-textarea": "tdesign-miniprogram/textarea/textarea", + "t-icon": "tdesign-miniprogram/icon/icon", + "t-input": "tdesign-miniprogram/input/input", + "t-button": "tdesign-miniprogram/button/button", + "t-cell-group": "tdesign-miniprogram/cell-group/cell-group", + "t-cell": "tdesign-miniprogram/cell/cell", + "t-toast": "tdesign-miniprogram/toast/toast", + "t-dialog": "tdesign-miniprogram/dialog/dialog", + "t-switch": "tdesign-miniprogram/switch/switch", + "t-cascader": "tdesign-miniprogram/cascader/cascader", + "loading-dialog": "../../../../components/loading-dialog/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/usercenter/address/edit/index.wxml b/miniprogram/tcb-shop/pages/usercenter/address/edit/index.wxml new file mode 100644 index 0000000..f9a38b2 --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/address/edit/index.wxml @@ -0,0 +1,25 @@ + + + +
+ + + + + + + + + + + + + + + 保存 + +
+
+
+ + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/usercenter/address/edit/index.wxss b/miniprogram/tcb-shop/pages/usercenter/address/edit/index.wxss new file mode 100644 index 0000000..f89e06f --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/address/edit/index.wxss @@ -0,0 +1,95 @@ +page { + background-color: #f5f5f5; +} +page .divider-line { + width: 100%; + height: 20rpx; + background-color: #f5f5f5; +} +.address-flex-box { + display: flex; + flex-wrap: wrap; +} +.address-detail { + font-size: 30rpx; +} +.address-detail-wx-location { + background: #fff; + padding: 24rpx 32rpx; + display: flex; + align-items: center; + justify-content: space-between; +} +.address-detail-wx-arrow { + align-items: flex-end; +} + +.form-cell .t-cell__title { + width: 144rpx; + padding-right: 32rpx; + flex: none !important; +} + +.textarea__wrapper { + width: 100%; +} + +.textarea__wrapper .t-textarea { + padding: 0 !important; +} + +.form-address .map { + font-size: 48rpx !important; + margin-left: 20rpx; + color: #9d9d9f; +} + +.address__tag { + justify-content: flex-start !important; +} + +.form-address .label-list { + background: #f5f5f5; + color: #333; + min-width: 100rpx; + margin-right: 32rpx; + font-size: 26rpx; + border: 2rpx solid transparent; + width: auto; +} +.form-address .label-list::after { + content: none; +} +.form-address .active-btn { + color: #fa4126; + border: 2rpx solid #fa4126; + background: rgba(255, 95, 21, 0.04); +} +.form-address .active-btn::after { + border: 4rpx solid #ff5f15; +} + +.submit { + box-sizing: border-box; + padding: 64rpx 30rpx 88rpx 30rpx; +} +.submit .btn-submit-address { + background: #fa4126 !important; + color: #fff !important; +} + +.dialog__button-confirm { + color: #fa4126 !important; +} + +.form-address .form-content { + --td-input-vertical-padding: 0; +} + +.dialog__input { + margin-top: 32rpx; + border-radius: 8rpx; + box-sizing: border-box; + --td-input-vertical-padding: 12px; + --td-input-bg-color: #f3f3f3; +} diff --git a/miniprogram/tcb-shop/pages/usercenter/address/edit/util.js b/miniprogram/tcb-shop/pages/usercenter/address/edit/util.js new file mode 100644 index 0000000..17d619a --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/address/edit/util.js @@ -0,0 +1,33 @@ +let addressPromise = []; + +/** 地址编辑Promise */ +export const getAddressPromise = () => { + let resolver; + let rejecter; + const nextPromise = new Promise((resolve, reject) => { + resolver = resolve; + rejecter = reject; + }); + + addressPromise.push({ resolver, rejecter }); + + return nextPromise; +}; + +/** 用户保存了一个地址 */ +export const resolveAddress = (address) => { + const allAddress = [...addressPromise]; + addressPromise = []; + + console.info('用户保存了一个地址', address); + + allAddress.forEach(({ resolver }) => resolver(address)); +}; + +/** 取消编辑 */ +export const rejectAddress = () => { + const allAddress = [...addressPromise]; + addressPromise = []; + + allAddress.forEach(({ rejecter }) => rejecter(new Error('cancel'))); +}; diff --git a/miniprogram/tcb-shop/pages/usercenter/address/list/index.js b/miniprogram/tcb-shop/pages/usercenter/address/list/index.js new file mode 100644 index 0000000..6eaf6f9 --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/address/list/index.js @@ -0,0 +1,102 @@ +/* eslint-disable no-param-reassign */ +import { getAllAddress, deleteAddress } from '../../../../services/address/address'; +import Toast from 'tdesign-miniprogram/toast/index'; +import { resolveAddress, rejectAddress } from './util'; +import { shouldFresh, addressListFinishFresh } from '../../../../utils/addressListFresh'; +import { objectToParamString } from '../../../../utils/util'; + +Page({ + data: { + addressList: [], + deleteID: '', + showDeleteConfirm: false, + loading: false, + }, + + setLoading() { + this.setData({ loading: true }); + }, + unsetLoading() { + this.setData({ loading: false }); + }, + + /** + * 如果是 true 的话,点击后会选中并返回上一页;否则点击后会进入编辑页 + */ + selectMode: false, + /** 是否已经选择地址,不置为 true 的话页面离开时会触发取消选择行为 */ + hasSelect: false, + + onLoad(query) { + const { selectMode, id = '' } = query; + this.setData({ + id, + }); + this.selectMode = selectMode === 'true'; + this.init(); + }, + + onShow() { + shouldFresh && this.fresh(); + }, + + init() { + this.fresh(); + }, + onUnload() { + if (this.selectMode && !this.hasSelect) { + rejectAddress(); + } + }, + async fresh() { + this.setLoading(); + try { + await this.getAddressList(); + addressListFinishFresh(); + } catch { + this.toast('拉取地址失败,请稍后再试'); + } finally { + this.unsetLoading(); + } + }, + async getAddressList() { + const addressList = await getAllAddress(); + this.setData({ addressList }); + }, + toast(message) { + Toast({ + context: this, + selector: '#t-toast', + message, + icon: '', + duration: 1000, + }); + }, + async deleteAddressHandle({ detail: { _id } }) { + try { + this.setLoading(); + await deleteAddress({ id: _id }); + const { addressList } = this.data; + this.setData({ addressList: addressList.filter((x) => x._id !== _id) }); + } catch { + this.toast('删除地址失败,请稍后再试'); + } finally { + this.unsetLoading(); + } + }, + editAddressHandle({ detail }) { + wx.navigateTo({ url: `/pages/usercenter/address/edit/index?${objectToParamString(detail)}` }); + }, + selectHandle({ detail }) { + if (this.selectMode) { + this.hasSelect = true; + resolveAddress(detail); + wx.navigateBack({ delta: 1 }); + } else { + this.editAddressHandle({ detail }); + } + }, + createHandle() { + wx.navigateTo({ url: '/pages/usercenter/address/edit/index' }); + }, +}); diff --git a/miniprogram/tcb-shop/pages/usercenter/address/list/index.json b/miniprogram/tcb-shop/pages/usercenter/address/list/index.json new file mode 100644 index 0000000..0a51b22 --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/address/list/index.json @@ -0,0 +1,11 @@ +{ + "navigationBarTitleText": "收货地址", + "usingComponents": { + "t-icon": "tdesign-miniprogram/icon/icon", + "t-image": "/components/webp-image/index", + "t-toast": "tdesign-miniprogram/toast/toast", + "t-address-item": "../../components/ui-address-item/index", + "t-empty": "tdesign-miniprogram/empty/empty", + "loading-dialog": "../../../../components/loading-dialog/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/usercenter/address/list/index.wxml b/miniprogram/tcb-shop/pages/usercenter/address/list/index.wxml new file mode 100644 index 0000000..77b3b98 --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/address/list/index.wxml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + 新建收货地址 + + + 最多支持添加20个收货地址 + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/usercenter/address/list/index.wxss b/miniprogram/tcb-shop/pages/usercenter/address/list/index.wxss new file mode 100644 index 0000000..286227b --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/address/list/index.wxss @@ -0,0 +1,109 @@ +page { + background: #f5f5f5; + height: 100%; +} +.address-container { + display: flex; + flex-direction: column; + align-items: stretch; + padding-bottom: calc(env(safe-area-inset-bottom) + 172rpx); +} +.address-container .address-list { + font-size: 24rpx; + background-color: #ffffff; + -webkit-overflow-scrolling: touch; +} +.address-list .no-address { + width: 750rpx; + padding-top: 30vh; + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: center; +} +.address-list .no-address__icon { + width: 224rpx; + height: 224rpx; +} +.address-list .no-address__text { + font-size: 28rpx; + line-height: 40rpx; + color: #999999; + margin-top: 24rpx; +} +.address-container .bottom-fixed { + border-top: 1rpx solid #e5e5e5; + position: fixed; + bottom: 0; + left: 0; + right: 0; + z-index: 1000; + background: #fff; + display: flex; + justify-content: center; + flex-direction: column; + align-items: center; + padding: 12rpx 32rpx calc(env(safe-area-inset-bottom) + 12rpx) 32rpx; +} +.address-container .btn-wrap { + width: 100%; + display: flex; + justify-content: center; + align-items: center; + font-size: 32rpx; + font-weight: bold; +} +.address-container .btn-wrap .location-btn { + width: 332rpx; + height: 88rpx; + display: flex; + justify-content: center; + align-items: center; + background-color: #ffffff; + color: #333; + position: relative; +} +.address-container .btn-wrap .location-btn::after { + content: ''; + position: absolute; /* 把父视图设置为relative,方便定位*/ + top: 0; + left: 0; + width: 200%; + height: 200%; + transform: scale(0.5); + transform-origin: 0 0; + box-sizing: border-box; + border-radius: 88rpx; + border: #dddddd 2rpx solid; +} +.address-container .btn-wrap .address-btn { + flex-grow: 1; + height: 88rpx; + display: flex; + justify-content: center; + align-items: center; + background-color: #fa4126; + border-radius: 44rpx; + color: #fff; +} +.address-container .btn-wrap .btn-default { + background: #c6c6c6; +} +.address-container .bottom-fixed .footer { + margin-top: 10rpx; + display: inline-block; + width: 100%; + text-align: center; + font-size: 24rpx; + font-weight: 400; + color: #ff2525; + line-height: 60rpx; + height: 60rpx; +} +.address-container .message { + margin-top: 48rpx; +} +.address-container .custom-class { + margin-right: 12rpx; + font-weight: normal; +} diff --git a/miniprogram/tcb-shop/pages/usercenter/address/list/util.js b/miniprogram/tcb-shop/pages/usercenter/address/list/util.js new file mode 100644 index 0000000..953f8d0 --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/address/list/util.js @@ -0,0 +1,31 @@ +let addressPromise = []; + +/** 获取一个地址选择Promise */ +export const getAddressPromise = () => { + let resolver; + let rejecter; + const nextPromise = new Promise((resolve, reject) => { + resolver = resolve; + rejecter = reject; + }); + + addressPromise.push({ resolver, rejecter }); + + return nextPromise; +}; + +/** 用户选择了一个地址 */ +export const resolveAddress = (address) => { + const allAddress = [...addressPromise]; + addressPromise = []; + + allAddress.forEach(({ resolver }) => resolver(address)); +}; + +/** 用户没有选择任何地址只是返回上一页了 */ +export const rejectAddress = () => { + const allAddress = [...addressPromise]; + addressPromise = []; + + allAddress.forEach(({ rejecter }) => rejecter(new Error('cancel'))); +}; diff --git a/miniprogram/tcb-shop/pages/usercenter/components/order-group/index.js b/miniprogram/tcb-shop/pages/usercenter/components/order-group/index.js new file mode 100644 index 0000000..5bfe0f6 --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/components/order-group/index.js @@ -0,0 +1,37 @@ +Component({ + externalClasses: ['title-class', 'icon-class', 'number-class'], + options: { + multipleSlots: true, + }, + properties: { + orderTagInfos: { + type: Array, + value: [], + }, + title: { + type: String, + value: '我的订单', + }, + desc: { + type: String, + value: '全部订单', + }, + isTop: { + type: Boolean, + value: true, + }, + classPrefix: { + type: String, + value: 'wr', + }, + }, + methods: { + onClickItem(e) { + this.triggerEvent('onClickItem', e.currentTarget.dataset.item); + }, + + onClickTop() { + this.triggerEvent('onClickTop', {}); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/usercenter/components/order-group/index.json b/miniprogram/tcb-shop/pages/usercenter/components/order-group/index.json new file mode 100644 index 0000000..c22feda --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/components/order-group/index.json @@ -0,0 +1,9 @@ +{ + "component": true, + "usingComponents": { + "t-cell": "tdesign-miniprogram/cell/cell", + "t-cell-group": "tdesign-miniprogram/cell-group/cell-group", + "t-badge": "tdesign-miniprogram/badge/badge", + "t-icon": "tdesign-miniprogram/icon/icon" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/usercenter/components/order-group/index.wxml b/miniprogram/tcb-shop/pages/usercenter/components/order-group/index.wxml new file mode 100644 index 0000000..de56e28 --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/components/order-group/index.wxml @@ -0,0 +1,37 @@ + + + + + + + + + + + + {{item.title}} + + + diff --git a/miniprogram/tcb-shop/pages/usercenter/components/order-group/index.wxss b/miniprogram/tcb-shop/pages/usercenter/components/order-group/index.wxss new file mode 100644 index 0000000..59b9aa1 --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/components/order-group/index.wxss @@ -0,0 +1,56 @@ +.order-group { + margin-bottom: 24rpx; + background-color: #ffffff; + border-radius: 16rpx 16rpx 0 0; +} +.order-group .order-group__top { + padding: 24rpx 18rpx 24rpx 32rpx; + border-radius: 16rpx 16rpx 0 0; +} +.order-group__top___title { + font-size: 32rpx; + line-height: 48rpx; + font-weight: bold; +} +.order-group__top__note { + font-size: 28rpx; +} +.order-group__content { + overflow: hidden; + width: 100%; + height: 164rpx; + display: flex; + background-color: #fff; + border-radius: 0 0 16rpx 16rpx; +} +.order-group__item { + overflow: hidden; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + flex: 1; +} +.order-group__item:first-child { + border-radius: 0 0 0 16rpx; +} +.order-group__item:last-child { + border-radius: 0 0 16rpx 0; +} +.order-group__item__title { + font-size: 24rpx; + color: #666; + line-height: 32rpx; +} +.order-group__item__icon { + margin-bottom: 20rpx; + width: 56rpx; + height: 56rpx; + position: relative; +} +.order-group__top__title { + font-weight: bold; +} +.order-group .order-group__left { + margin-right: 0; +} diff --git a/miniprogram/tcb-shop/pages/usercenter/components/ui-address-item/index.js b/miniprogram/tcb-shop/pages/usercenter/components/ui-address-item/index.js new file mode 100644 index 0000000..467bbf7 --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/components/ui-address-item/index.js @@ -0,0 +1,46 @@ +Component({ + options: { + addGlobalClass: true, + multipleSlots: true, + }, + properties: { + address: { + type: Object, + value: {}, + }, + customIcon: { + type: String, + value: 'edit-1', + }, + extraSpace: { + type: Boolean, + value: true, + }, + isDrawLine: { + type: Boolean, + value: true, + }, + }, + externalClasses: [ + 'item-wrapper-class', + 'title-class', + 'default-tag-class', + 'normal-tag-class', + 'address-info-class', + 'delete-class', + ], + methods: { + onDelete(e) { + const { item } = e.currentTarget.dataset; + this.triggerEvent('onDelete', item); + }, + onSelect(e) { + const { item } = e.currentTarget.dataset; + this.triggerEvent('onSelect', item); + }, + onEdit(e) { + const { item } = e.currentTarget.dataset; + this.triggerEvent('onEdit', item); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/usercenter/components/ui-address-item/index.json b/miniprogram/tcb-shop/pages/usercenter/components/ui-address-item/index.json new file mode 100644 index 0000000..0f9fe0f --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/components/ui-address-item/index.json @@ -0,0 +1,8 @@ +{ + "component": true, + "usingComponents": { + "t-icon": "tdesign-miniprogram/icon/icon", + "t-tag": "tdesign-miniprogram/tag/tag", + "t-swipe-cell": "tdesign-miniprogram/swipe-cell/swipe-cell" + } +} diff --git a/miniprogram/tcb-shop/pages/usercenter/components/ui-address-item/index.wxml b/miniprogram/tcb-shop/pages/usercenter/components/ui-address-item/index.wxml new file mode 100644 index 0000000..9cc4bba --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/components/ui-address-item/index.wxml @@ -0,0 +1,30 @@ + + var toHide = function(array) { var mphone = array.substring(0, 3) + '****' + array.substring(7); return mphone; } + module.exports.toHide = toHide; + + + + + + + + + + {{address.name}} + {{phoneReg.toHide(address.phone || '')}} + + + + {{address.address}} + + + + + + + + + 删除 + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/usercenter/components/ui-address-item/index.wxss b/miniprogram/tcb-shop/pages/usercenter/components/ui-address-item/index.wxss new file mode 100644 index 0000000..5ce1ea9 --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/components/ui-address-item/index.wxss @@ -0,0 +1,103 @@ +.address-item-wrapper { + overflow: hidden; +} +.address-item-wrapper .swipe-out .wr-swiper-cell { + margin-top: 20rpx; +} +.address-item-wrapper .swipe-out .swipe-right-del { + display: flex; + justify-content: center; + align-items: center; + width: 144rpx; + height: 100%; + background-color: #fa4126; + color: #fff; + font-size: 28rpx; + line-height: 40rpx; +} +.address-item-wrapper .draw-line { + position: relative; +} +.address-item-wrapper .draw-line::after { + content: ''; + position: absolute; + bottom: 0; + left: 32rpx; + width: 200%; + height: 2rpx; + transform: scale(0.5); + transform-origin: 0 0; + box-sizing: border-box; + border-bottom: #e5e5e5 2rpx solid; +} +.address-item-wrapper .address { + display: flex; + justify-content: space-between; + align-items: center; + padding: 32rpx; + background-color: #fff; +} +.address-item-wrapper .address .address-edit { + padding: 20rpx 0 20rpx 46rpx; +} +.address-item-wrapper .address .address-left { + width: 80rpx; + display: flex; + justify-content: center; +} +.address-item-wrapper .address .address-content { + display: flex; + flex-direction: column; + flex: 1; +} +.address-item-wrapper .address .address-content .title { + font-size: 32rpx; + line-height: 48rpx; + margin-bottom: 16rpx; + color: #333333; + font-weight: bold; + display: flex; +} +.address-item-wrapper .address .address-content .title .text-style { + margin-right: 8rpx; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + max-width: 280rpx; +} +.address-item-wrapper .address .address-content .label-adds { + display: flex; +} +.address-item-wrapper .address .address-content .label-adds .adds { + display: -webkit-box; + overflow: hidden; + text-overflow: ellipsis; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + color: #999999; +} +.address-item-wrapper .address .address-content .label-adds .tag { + display: inline-block; + padding: 0rpx 8rpx; + min-width: 40rpx; + height: 32rpx; + border-radius: 18rpx; + font-size: 20rpx; + line-height: 32rpx; + text-align: center; + margin-right: 8rpx; + vertical-align: text-top; +} +.address-item-wrapper .address .address-content .label-adds .tag-default { + background: #ffece9; + color: #fa4126; +} +.address-item-wrapper .address .address-content .label-adds .tag-primary { + background: #f0f1ff; + color: #5a66ff; +} +.address-item-wrapper .address .address-content .label-adds .address-text { + font-size: 28rpx; + line-height: 40rpx; + color: #999999; +} diff --git a/miniprogram/tcb-shop/pages/usercenter/components/ui-select-picker/index.js b/miniprogram/tcb-shop/pages/usercenter/components/ui-select-picker/index.js new file mode 100644 index 0000000..fff68cc --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/components/ui-select-picker/index.js @@ -0,0 +1,73 @@ +Component({ + properties: { + show: { + type: Boolean, + observer(show) { + if (!show) return; + this.updateDivisions(); + }, + }, + title: { + type: String, + value: '', + }, + value: { + type: String, + value: '', + observer() { + if (!this.data.show) return; + this.updateDivisions(); + }, + }, + pickerOptions: { + type: Array, + value: [], + observer() { + if (!this.data.show) return; + this.updateDivisions(); + }, + }, + headerVisible: { + type: Boolean, + value: true, + }, + }, + data: { + pickerValue: [], + }, + methods: { + updateDivisions() { + const { pickerOptions, value } = this.data; + const index = (pickerOptions || []).findIndex( + (item) => item.code === value, + ); + + setTimeout(() => { + this.setData({ pickerValue: index >= 0 ? [index] : [0] }); + }, 0); + }, + + getAreaByIndex(indexes) { + const { pickerOptions } = this.data; + return pickerOptions[indexes.toString()]; + }, + + onChange(e) { + const currentValue = e.detail.value; + const target = this.getAreaByIndex(currentValue); + if (target === null) return; + + this.setData({ pickerValue: currentValue }); + this.triggerEvent('change', { value: target.code, target: target }); + }, + + onConfirm() { + const target = this.getAreaByIndex(this.data.pickerValue); + this.triggerEvent('confirm', { value: target?.code, target }); + }, + + onClose() { + this.triggerEvent('close'); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/usercenter/components/ui-select-picker/index.json b/miniprogram/tcb-shop/pages/usercenter/components/ui-select-picker/index.json new file mode 100644 index 0000000..6e4c04e --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/components/ui-select-picker/index.json @@ -0,0 +1,6 @@ +{ + "component": true, + "usingComponents": { + "t-popup": "tdesign-miniprogram/popup/popup" + } +} diff --git a/miniprogram/tcb-shop/pages/usercenter/components/ui-select-picker/index.wxml b/miniprogram/tcb-shop/pages/usercenter/components/ui-select-picker/index.wxml new file mode 100644 index 0000000..6e3cddc --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/components/ui-select-picker/index.wxml @@ -0,0 +1,21 @@ + + + + 取消 + {{title}} + 确定 + + + {{title}} + + + + {{ item.name }} + + + + 取消 + 确定 + + + diff --git a/miniprogram/tcb-shop/pages/usercenter/components/ui-select-picker/index.wxss b/miniprogram/tcb-shop/pages/usercenter/components/ui-select-picker/index.wxss new file mode 100644 index 0000000..3e977be --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/components/ui-select-picker/index.wxss @@ -0,0 +1,102 @@ +.city-picker-container { + opacity: 0; + position: fixed; + top: 100vh; + left: 0; + right: 0; + height: 100vh; + z-index: 100; +} +.city-picker-container.show { + top: 0; + opacity: 1; +} +.city-picker-container.show .city-picker-box { + bottom: 0; +} +.city-picker-shadow { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.65); +} +.city-picker-header { + height: 100rpx; + line-height: 100rpx; + text-align: center; + font-size: 32rpx; + color: #333333; +} +.city-picker-more { + display: flex; + justify-content: space-between; + align-items: center; +} +.city-picker-footer { + height: 100rpx; + display: flex; + justify-content: space-between; + align-items: center; +} +.city-picker-footer .btn { + width: 330rpx; + height: 80rpx; + line-height: 80rpx; + text-align: center; + color: #666666; + font-size: 32rpx; + position: relative; +} +.city-picker-footer .btn__active { + opacity: 0.5; +} +.city-picker-footer .btn::after { + display: block; + content: ' '; + position: absolute; + left: -50%; + right: -50%; + top: -50%; + bottom: -50%; + transform: scale(0.5); + border: 1rpx solid #999999; + border-radius: 16rpx; +} +.city-picker-footer .btn.primary { + color: #fa550f; +} +.city-picker-footer .btn.primary::after { + border-color: #fa550f; +} +.picker-column:not(:first-child) { + margin-left: 40rpx; +} +.city-picker-box { + position: absolute; + bottom: -100%; + transition: 0.3s bottom ease-in-out; + left: 0; + right: 0; + z-index: 100; + background-color: #fff; + padding: 0 30rpx; + color: #333333; + font-size: 34rpx; + border-radius: 20rpx 20rpx 0 0; + padding-bottom: env(safe-area-inset-bottom); +} +.show .city-picker-shadow { + display: block; +} +.picker { + height: 300rpx; + margin: 50rpx 0; + line-height: 88rpx; + text-align: center; +} +/* 似乎小程序picker-view的bug,indicator-class仅height生效,其他诸如line-height、text-align等放到父class中设置 */ +.picker-center-row { + height: 88rpx; +} diff --git a/miniprogram/tcb-shop/pages/usercenter/components/user-center-card/index.js b/miniprogram/tcb-shop/pages/usercenter/components/user-center-card/index.js new file mode 100644 index 0000000..47593c4 --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/components/user-center-card/index.js @@ -0,0 +1,35 @@ +const AuthStepType = { + ONE: 1, + TWO: 2, + THREE: 3, +}; + +Component({ + options: { + multipleSlots: true, + }, + properties: { + currAuthStep: { + type: Number, + value: AuthStepType.ONE, + }, + userInfo: { + type: Object, + value: {}, + }, + isNeedGetUserInfo: { + type: Boolean, + value: false, + }, + }, + data: { + defaultAvatarUrl: + 'https://cdn-we-retail.ym.tencent.com/miniapp/usercenter/icon-user-center-avatar@2x.png', + AuthStepType, + }, + methods: { + gotoUserEditPage() { + this.triggerEvent('gotoUserEditPage'); + }, + }, +}); diff --git a/miniprogram/tcb-shop/pages/usercenter/components/user-center-card/index.json b/miniprogram/tcb-shop/pages/usercenter/components/user-center-card/index.json new file mode 100644 index 0000000..e169f6f --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/components/user-center-card/index.json @@ -0,0 +1,7 @@ +{ + "component": true, + "usingComponents": { + "t-icon": "tdesign-miniprogram/icon/icon", + "t-avatar": "tdesign-miniprogram/avatar/avatar" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/usercenter/components/user-center-card/index.wxml b/miniprogram/tcb-shop/pages/usercenter/components/user-center-card/index.wxml new file mode 100644 index 0000000..ad11e7d --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/components/user-center-card/index.wxml @@ -0,0 +1,34 @@ + + + + + + {{'请登录'}} + + + + + + + {{userInfo.nickName || '微信用户'}} + + + + + + + + + + + + + {{userInfo.nickName || '微信用户'}} + + + diff --git a/miniprogram/tcb-shop/pages/usercenter/components/user-center-card/index.wxss b/miniprogram/tcb-shop/pages/usercenter/components/user-center-card/index.wxss new file mode 100644 index 0000000..2b488c1 --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/components/user-center-card/index.wxss @@ -0,0 +1,48 @@ +.user-center-card { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 480rpx; + background-image: url('https://cdn-we-retail.ym.tencent.com/miniapp/template/user-center-bg-v1.png'); + background-size: cover; + background-repeat: no-repeat; + padding: 0 24rpx; +} +.user-center-card__header { + margin-top: 192rpx; + margin-bottom: 48rpx; + height: 96rpx; + line-height: 48rpx; + display: flex; + justify-content: flex-start; + align-items: center; + color: #333; + position: relative; +} +.user-center-card__header__avatar { + width: 96rpx; + height: 96rpx; + border-radius: 48rpx; + overflow: hidden; +} + +.user-center-card__header__name { + font-size: 36rpx; + line-height: 48rpx; + color: #333; + font-weight: bold; + margin-left: 24rpx; + margin-right: 16rpx; +} +.user-center-card__header__transparent { + position: absolute; + left: 0; + top: 0; + background-color: transparent; + height: 100%; + width: 100%; +} +.user-center-card__icon { + line-height: 96rpx; +} diff --git a/miniprogram/tcb-shop/pages/usercenter/index.js b/miniprogram/tcb-shop/pages/usercenter/index.js new file mode 100644 index 0000000..cf90eed --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/index.js @@ -0,0 +1,220 @@ +import { fetchUserCenter } from '../../services/usercenter/fetchUsercenter'; +import { getToPayOrderCount, getToSendOrderCount, getToReceiveOrderCount } from '../../services/order/order'; +import { ORDER_STATUS } from '../../services/order/order'; +import Toast from 'tdesign-miniprogram/toast/index'; + +const menuData = [ + [ + { + title: '收货地址', + tit: '', + url: '', + type: 'address', + }, + ], +]; + +const orderTagInfos = [ + { + title: '待付款', + iconName: 'wallet', + orderNum: 0, + tabType: ORDER_STATUS.TO_PAY, + status: 1, + }, + { + title: '待发货', + iconName: 'deliver', + orderNum: 0, + tabType: ORDER_STATUS.TO_SEND, + status: 1, + }, + { + title: '待收货', + iconName: 'package', + orderNum: 0, + tabType: ORDER_STATUS.TO_RECEIVE, + status: 1, + }, + { + title: '待评价', + iconName: 'comment', + orderNum: 0, + tabType: ORDER_STATUS.FINISHED, + status: 1, + }, + // { + // title: '退款/售后', + // iconName: 'exchang', + // orderNum: 0, + // tabType: 0, + // status: 1, + // }, +]; + +const getDefaultData = () => ({ + showMakePhone: false, + userInfo: { + avatarUrl: '', + nickName: '正在登录...', + phoneNumber: '', + }, + menuData, + orderTagInfos, + customerServiceInfo: {}, + currAuthStep: 1, + showKefu: true, + versionNo: '', + toPayOrderCount: 0, + toSendOrderCount: 0, + toReceiveOrderCount: 0, +}); + +Page({ + data: getDefaultData(), + + onLoad() { + this.getVersionInfo(); + }, + + onShow() { + this.getTabBar().init(); + this.init(); + }, + onPullDownRefresh() { + this.init(); + }, + + init() { + this.fetUseriInfoHandle(); + this.initOrderCount(); + }, + + async initOrderCount() { + const [pay, send, receive] = await Promise.all([ + getToPayOrderCount(), + getToSendOrderCount(), + getToReceiveOrderCount(), + ]); + this.setData({ + 'orderTagInfos[0].orderNum': pay, + 'orderTagInfos[1].orderNum': send, + 'orderTagInfos[2].orderNum': receive, + }); + }, + + fetUseriInfoHandle() { + fetchUserCenter().then(({ userInfo, countsData, customerServiceInfo }) => { + // eslint-disable-next-line no-unused-expressions + menuData?.[0].forEach((v) => { + countsData.forEach((counts) => { + if (counts.type === v.type) { + // eslint-disable-next-line no-param-reassign + v.tit = counts.num; + } + }); + }); + this.setData({ + userInfo, + menuData, + customerServiceInfo, + currAuthStep: 2, + }); + wx.stopPullDownRefresh(); + }); + }, + + onClickCell({ currentTarget }) { + const { type } = currentTarget.dataset; + + switch (type) { + case 'address': { + wx.navigateTo({ url: '/pages/usercenter/address/list/index' }); + break; + } + case 'service': { + this.openMakePhone(); + break; + } + case 'help-center': { + Toast({ + context: this, + selector: '#t-toast', + message: '你点击了帮助中心', + icon: '', + duration: 1000, + }); + break; + } + case 'point': { + Toast({ + context: this, + selector: '#t-toast', + message: '你点击了积分菜单', + icon: '', + duration: 1000, + }); + break; + } + case 'coupon': { + wx.navigateTo({ url: '/pages/coupon/coupon-list/index' }); + break; + } + default: { + Toast({ + context: this, + selector: '#t-toast', + message: '未知跳转', + icon: '', + duration: 1000, + }); + break; + } + } + }, + + jumpNav(e) { + const status = e.detail.tabType; + + if (status === 0) { + wx.navigateTo({ url: '/pages/order/after-service-list/index' }); + } else { + wx.navigateTo({ url: `/pages/order/order-list/index?status=${status}` }); + } + }, + + jumpAllOrder() { + wx.navigateTo({ url: '/pages/order/order-list/index' }); + }, + + openMakePhone() { + this.setData({ showMakePhone: true }); + }, + + closeMakePhone() { + this.setData({ showMakePhone: false }); + }, + + call() { + wx.makePhoneCall({ + phoneNumber: this.data.customerServiceInfo.servicePhone, + }); + }, + + gotoUserEditPage() { + const { currAuthStep } = this.data; + if (currAuthStep === 2) { + wx.navigateTo({ url: '/pages/usercenter/person-info/index' }); + } else { + this.fetUseriInfoHandle(); + } + }, + + getVersionInfo() { + const versionInfo = wx.getAccountInfoSync(); + const { version, envVersion = __wxConfig } = versionInfo.miniProgram; + this.setData({ + versionNo: envVersion === 'release' ? version : envVersion, + }); + }, +}); diff --git a/miniprogram/tcb-shop/pages/usercenter/index.json b/miniprogram/tcb-shop/pages/usercenter/index.json new file mode 100644 index 0000000..398a70b --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/index.json @@ -0,0 +1,14 @@ +{ + "navigationBarTitleText": "个人中心", + "navigationStyle": "custom", + "usingComponents": { + "t-popup": "tdesign-miniprogram/popup/popup", + "t-icon": "tdesign-miniprogram/icon/icon", + "t-cell-group": "tdesign-miniprogram/cell-group/cell-group", + "t-cell": "tdesign-miniprogram/cell/cell", + "t-user-center-card": "./components/user-center-card/index", + "t-order-group": "./components/order-group/index", + "t-toast": "tdesign-miniprogram/toast/toast" + }, + "enablePullDownRefresh": true +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/usercenter/index.wxml b/miniprogram/tcb-shop/pages/usercenter/index.wxml new file mode 100644 index 0000000..e42a865 --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/index.wxml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + 服务时间: {{customerServiceInfo.serviceTimeDuration}} + + 电话客服 + + 取消 + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/usercenter/index.wxss b/miniprogram/tcb-shop/pages/usercenter/index.wxss new file mode 100644 index 0000000..483e6f2 --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/index.wxss @@ -0,0 +1,146 @@ +page { + background-color: #f5f5f5; +} + +.content-wrapper { + margin-top: 340rpx; + position: relative; + padding: 0 30rpx; +} + +.main-content { + height: 500rpx; +} + +.order-group-wrapper { + margin-bottom: 16rpx; +} + +.order-group-note { + font-size: 28rpx; +} + +.cell-box { + border-radius: 10rpx; + overflow: hidden; + margin-bottom: 20rpx; +} +.icon-color { + color: #aaa; +} +.cell-class { + height: 100rpx; + display: flex; + align-items: center; +} + +.order-content { + overflow: hidden; + width: 100%; + display: flex; + background-color: #fff; + border-radius: 16rpx; +} + +.order-item { + flex: 1; + height: 180rpx; + overflow: hidden; + position: relative; + text-align: center; +} + +.order-content-box { + margin: auto; + position: absolute; + width: 100%; + top: 50%; + left: 50%; + -webkit-transform: translate(-50%, -50%); + -ms-transform: translate(-50%, -50%); + transform: translate(-50%, -50%); +} +.order-content-t { + margin-top: 10rpx; + font-size: 24rpx; + color: #333; + letter-spacing: 0; + text-align: center; +} + +.popup-content { + background: #f5f5f5; + margin-bottom: env(safe-area-inset-bottom); + border-radius: 16rpx 16rpx 0 0; +} +.popup-content .popup-title { + background: #fff; + text-align: center; + font-size: 24rpx; + color: #999; + height: 112rpx; + text-align: center; + line-height: 112rpx; + border-radius: 16rpx 16rpx 0 0; +} + +.border-bottom-1px { + position: relative; +} + +.border-bottom-1px::after { + position: absolute; + display: block; + content: ''; + box-sizing: border-box; + top: 0; + left: 0; + width: 200%; + height: 200%; + transform: scale(0.5); + transform-origin: left top; + border-bottom: 2rpx solid #e5e5e5; +} +.popup-content .popup-phone, +.popup-content .popup-close { + background: #fff; + height: 100rpx; + display: flex; + justify-content: center; + align-items: center; + text-align: center; + font-size: 30rpx; + font-family: PingFangSC-Regular, PingFang SC; + font-weight: 400; + color: #333; +} +.popup-content .popup-phone.online { + margin-bottom: 20rpx; +} +.popup-content .popup-phone.online::after { + content: none; +} +.popup-content .popup-close { + color: #333; + border: 0; + margin-top: 16rpx; +} + +.my-order { + border-radius: 10rpx; +} + +.footer__version { + text-align: center; + margin-top: 50rpx; + color: #999; + margin-bottom: 4rpx; + font-size: 24rpx; + line-height: 32rpx; +} +.cell-box .order-group__left { + margin-right: 0; +} +.cell-box .t-cell-padding { + padding: 24rpx 18rpx 24rpx 32rpx; +} diff --git a/miniprogram/tcb-shop/pages/usercenter/name-edit/index.js b/miniprogram/tcb-shop/pages/usercenter/name-edit/index.js new file mode 100644 index 0000000..57e4e3d --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/name-edit/index.js @@ -0,0 +1,19 @@ +Page({ + data: { + nameValue: '', + }, + onLoad(options) { + const { name } = options; + this.setData({ + nameValue: name, + }); + }, + onSubmit() { + wx.navigateBack({ backRefresh: true }); + }, + clearContent() { + this.setData({ + nameValue: '', + }); + }, +}); diff --git a/miniprogram/tcb-shop/pages/usercenter/name-edit/index.json b/miniprogram/tcb-shop/pages/usercenter/name-edit/index.json new file mode 100644 index 0000000..efc7e5f --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/name-edit/index.json @@ -0,0 +1,8 @@ +{ + "navigationBarTitleText": "昵称", + "usingComponents": { + "t-input": "tdesign-miniprogram/input/input", + "t-icon": "tdesign-miniprogram/icon/icon", + "t-button": "tdesign-miniprogram/button/button" + } +} diff --git a/miniprogram/tcb-shop/pages/usercenter/name-edit/index.wxml b/miniprogram/tcb-shop/pages/usercenter/name-edit/index.wxml new file mode 100644 index 0000000..2ceb518 --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/name-edit/index.wxml @@ -0,0 +1,14 @@ + + + 最多可输入15个字 + + 保存 + + diff --git a/miniprogram/tcb-shop/pages/usercenter/name-edit/index.wxss b/miniprogram/tcb-shop/pages/usercenter/name-edit/index.wxss new file mode 100644 index 0000000..61674b3 --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/name-edit/index.wxss @@ -0,0 +1,18 @@ +page { + background-color: #f5f5f5; +} +page view { + box-sizing: border-box; +} +.name-edit { + padding-top: 20rpx; +} +.name-edit .name-edit__input--desc { + font-size: 26rpx; + padding: 16rpx 32rpx; + color: #999; + margin-bottom: 200rpx; +} +.name-edit .name-edit__wrapper { + margin: 0 32rpx; +} diff --git a/miniprogram/tcb-shop/pages/usercenter/person-info/index.js b/miniprogram/tcb-shop/pages/usercenter/person-info/index.js new file mode 100644 index 0000000..fb5db98 --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/person-info/index.js @@ -0,0 +1,122 @@ +import { fetchPerson } from '../../../services/usercenter/fetchPerson'; +import { phoneEncryption } from '../../../utils/util'; +import Toast from 'tdesign-miniprogram/toast/index'; + +Page({ + data: { + personInfo: { + avatarUrl: '', + nickName: '', + gender: 0, + phoneNumber: '', + }, + showUnbindConfirm: false, + pickerOptions: [ + { + name: '男', + code: '1', + }, + { + name: '女', + code: '2', + }, + ], + typeVisible: false, + genderMap: ['', '男', '女'], + }, + onLoad() { + this.init(); + }, + init() { + this.fetchData(); + }, + fetchData() { + fetchPerson().then((personInfo) => { + this.setData({ + personInfo, + 'personInfo.phoneNumber': phoneEncryption(personInfo.phoneNumber), + }); + }); + }, + onClickCell({ currentTarget }) { + const { dataset } = currentTarget; + const { nickName } = this.data.personInfo; + + switch (dataset.type) { + case 'gender': + this.setData({ + typeVisible: true, + }); + break; + case 'name': + wx.navigateTo({ + url: `/pages/usercenter/name-edit/index?name=${nickName}`, + }); + break; + case 'avatarUrl': + this.toModifyAvatar(); + break; + default: { + break; + } + } + }, + onClose() { + this.setData({ + typeVisible: false, + }); + }, + onConfirm(e) { + const { value } = e.detail; + this.setData( + { + typeVisible: false, + 'personInfo.gender': value, + }, + () => { + Toast({ + context: this, + selector: '#t-toast', + message: '设置成功', + theme: 'success', + }); + }, + ); + }, + async toModifyAvatar() { + try { + const tempFilePath = await new Promise((resolve, reject) => { + wx.chooseImage({ + count: 1, + sizeType: ['compressed'], + sourceType: ['album', 'camera'], + success: (res) => { + const { path, size } = res.tempFiles[0]; + if (size <= 10485760) { + resolve(path); + } else { + reject({ errMsg: '图片大小超出限制,请重新上传' }); + } + }, + fail: (err) => reject(err), + }); + }); + const tempUrlArr = tempFilePath.split('/'); + const tempFileName = tempUrlArr[tempUrlArr.length - 1]; + Toast({ + context: this, + selector: '#t-toast', + message: `已选择图片-${tempFileName}`, + theme: 'success', + }); + } catch (error) { + if (error.errMsg === 'chooseImage:fail cancel') return; + Toast({ + context: this, + selector: '#t-toast', + message: error.errMsg || error.msg || '修改头像出错了', + theme: 'error', + }); + } + }, +}); diff --git a/miniprogram/tcb-shop/pages/usercenter/person-info/index.json b/miniprogram/tcb-shop/pages/usercenter/person-info/index.json new file mode 100644 index 0000000..e63dcc5 --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/person-info/index.json @@ -0,0 +1,12 @@ +{ + "navigationBarTitleText": "个人资料", + "usingComponents": { + "t-cell-group": "tdesign-miniprogram/cell-group/cell-group", + "t-cell": "tdesign-miniprogram/cell/cell", + "t-button": "tdesign-miniprogram/button/button", + "t-image": "/components/webp-image/index", + "t-dialog": "tdesign-miniprogram/dialog/dialog", + "t-toast": "tdesign-miniprogram/toast/toast", + "t-select-picker": "../components/ui-select-picker/index" + } +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/usercenter/person-info/index.wxml b/miniprogram/tcb-shop/pages/usercenter/person-info/index.wxml new file mode 100644 index 0000000..21b8c6f --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/person-info/index.wxml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/miniprogram/tcb-shop/pages/usercenter/person-info/index.wxss b/miniprogram/tcb-shop/pages/usercenter/person-info/index.wxss new file mode 100644 index 0000000..cb4eb70 --- /dev/null +++ b/miniprogram/tcb-shop/pages/usercenter/person-info/index.wxss @@ -0,0 +1,45 @@ +:host { + background-color: #f5f5f5; +} +page view { + box-sizing: border-box; +} +.person-info { + padding-top: 20rpx; +} + +.person-info__btn { + width: 100%; + border: 2rpx solid #ddd; + border-radius: 48rpx; + padding: 18rpx 0; + display: flex; + align-self: center; + justify-content: center; +} +.person-info__wrapper { + width: 100%; + padding: 0 32rpx; + padding-bottom: calc(env(safe-area-inset-bottom) + 20rpx); + position: absolute; + bottom: 0; + left: 0; +} + +.avatarUrl { + width: 80rpx; + height: 80rpx; + border-radius: 50% !important; + overflow: hidden; +} + +.t-class-confirm { + color: #fa550f !important; +} + +.person-info .order-group__left { + margin-right: 0; +} +.person-info .t-cell-class { + height: 112rpx; +} diff --git a/miniprogram/tcb-shop/project.config.json b/miniprogram/tcb-shop/project.config.json new file mode 100644 index 0000000..2a3b8cb --- /dev/null +++ b/miniprogram/tcb-shop/project.config.json @@ -0,0 +1,150 @@ +{ + "description": "项目配置文件,详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html", + "packOptions": { + "ignore": [], + "include": [] + }, + "setting": { + "urlCheck": true, + "es6": true, + "enhance": true, + "postcss": true, + "preloadBackgroundData": false, + "minified": true, + "newFeature": false, + "coverView": true, + "nodeModules": true, + "autoAudits": false, + "showShadowRootInWxmlPanel": true, + "scopeDataCheck": false, + "uglifyFileName": false, + "checkInvalidKey": true, + "checkSiteMap": true, + "uploadWithSourceMap": true, + "compileHotReLoad": false, + "lazyloadPlaceholderEnable": false, + "useMultiFrameRuntime": true, + "useApiHook": true, + "useApiHostProcess": true, + "ignoreDevUnusedFiles": false, + "babelSetting": { + "ignore": [], + "disablePlugins": [], + "outputPath": "" + }, + "enableEngineNative": false, + "useIsolateContext": true, + "userConfirmedBundleSwitch": false, + "packNpmManually": false, + "packNpmRelationList": [], + "minifyWXSS": true, + "disableUseStrict": false, + "minifyWXML": true, + "showES6CompileOption": false, + "useCompilerPlugins": false, + "ignoreUploadUnusedFiles": true, + "useStaticServer": true + }, + "compileType": "miniprogram", + "libVersion": "2.23.1", + "appid": "", + "projectname": "tcb-shop", + "simulatorType": "wechat", + "simulatorPluginLibVersion": {}, + "condition": { + "miniprogram": { + "list": [ + { + "name": "首页入口", + "pathName": "pages/home/home", + "query": "", + "scene": null + }, + { + "name": "示例页-商品分类", + "pathName": "pages/goods/category/index", + "query": "", + "scene": null + }, + { + "name": "示例页-个人中心", + "pathName": "pages/usercenter/index", + "query": "", + "scene": null + }, + { + "name": "示例页-商品列表", + "pathName": "pages/goods/list/index", + "query": "", + "scene": null + }, + { + "name": "示例页-商品详情", + "pathName": "pages/goods/details/index", + "query": "", + "scene": null + }, + { + "name": "示例页-商品评论", + "pathName": "pages/goods/comments/index", + "query": "", + "scene": null + }, + { + "name": "示例页-售后列表", + "pathName": "pages/order/after-service-list/index", + "query": "", + "scene": null + }, + { + "name": "示例页-售后详情", + "pathName": "pages/order/after-service-detail/index", + "query": "rightsNo=123123423", + "scene": null + }, + { + "name": "示例页-搜索页", + "pathName": "pages/goods/search/index", + "query": "", + "scene": null + }, + { + "name": "示例页-搜索结果", + "pathName": "pages/goods/result/index", + "query": "", + "scene": null + }, + { + "name": "示例页-商品评价", + "pathName": "pages/goods/comments/create/index", + "query": "", + "scene": null + }, + { + "name": "示例页-选择商品评价", + "pathName": "pages/goods/comments/create-list/index", + "query": "", + "scene": null + }, + { + "name": "示例页-申请售后", + "pathName": "pages/order/apply-service/index", + "query": "orderNo=132222623132329291&skuId=135691625", + "scene": null + }, + { + "name": "示例页-发票详情", + "pathName": "pages/order/invoice/index", + "query": "orderNo=132381532610540875", + "scene": null + } + ] + } + }, + "editorSetting": { + "tabIndent": "insertSpaces", + "tabSize": 2 + }, + "cloudfunctionTemplateRoot": "cloudfunctionTemplate/", + "cloudfunctionRoot": "cloudfunctions/" +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/services/_utils/delay.js b/miniprogram/tcb-shop/services/_utils/delay.js new file mode 100644 index 0000000..da4ad62 --- /dev/null +++ b/miniprogram/tcb-shop/services/_utils/delay.js @@ -0,0 +1,3 @@ +export function delay(ms = 200) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} diff --git a/miniprogram/tcb-shop/services/_utils/model.js b/miniprogram/tcb-shop/services/_utils/model.js new file mode 100644 index 0000000..46329e9 --- /dev/null +++ b/miniprogram/tcb-shop/services/_utils/model.js @@ -0,0 +1,34 @@ +export function model() { + return globalThis.dataModel; +} + +export async function getAll({ filter, select, name }) { + const addSelect = (prop) => (select ? { ...prop, select } : prop); + const pageSize = 200; + const first = await model()[name].list( + addSelect({ + pageNumber: 1, + pageSize, + getCount: true, + filter, + }), + ); + const { + data: { total }, + } = first; + const totalPage = Math.ceil(total / 200); + const lists = await Promise.all( + Array.from({ length: totalPage - 1 }, (_, index) => index + 2).map((pageNumber) => + model()[name].list( + addSelect({ + pageNumber, + pageSize, + filter, + }), + ), + ), + ); + + const ret = lists.reduce((acc, current) => acc.concat(current.data.records), first.data.records); + return ret; +} diff --git a/miniprogram/tcb-shop/services/_utils/timeout.js b/miniprogram/tcb-shop/services/_utils/timeout.js new file mode 100644 index 0000000..870c2bf --- /dev/null +++ b/miniprogram/tcb-shop/services/_utils/timeout.js @@ -0,0 +1,3 @@ +export function timeout(ms = 1000) { + return new Promise((_, reject) => setTimeout(reject, ms)); +} diff --git a/miniprogram/tcb-shop/services/activity/fetchActivity.js b/miniprogram/tcb-shop/services/activity/fetchActivity.js new file mode 100644 index 0000000..e9aefa6 --- /dev/null +++ b/miniprogram/tcb-shop/services/activity/fetchActivity.js @@ -0,0 +1,20 @@ +import { config } from '../../config/index'; + +/** 获取活动列表 */ +function mockFetchActivity(ID = 0) { + const { delay } = require('../_utils/delay'); + const { getActivity } = require('../../model/activity'); + + return delay().then(() => getActivity(ID)); +} + +/** 获取活动列表 */ +export function fetchActivity(ID = 0) { + if (config.useMock) { + return mockFetchActivity(ID); + } + + return new Promise((resolve) => { + resolve('real api'); + }); +} diff --git a/miniprogram/tcb-shop/services/activity/fetchActivityList.js b/miniprogram/tcb-shop/services/activity/fetchActivityList.js new file mode 100644 index 0000000..2522aff --- /dev/null +++ b/miniprogram/tcb-shop/services/activity/fetchActivityList.js @@ -0,0 +1,20 @@ +import { config } from '../../config/index'; + +/** 获取活动列表 */ +function mockFetchActivityList(pageIndex = 1, pageSize = 20) { + const { delay } = require('../_utils/delay'); + const { getActivityList } = require('../../model/activities'); + + return delay().then(() => getActivityList(pageIndex, pageSize)); +} + +/** 获取活动列表 */ +export function fetchActivityList(pageIndex = 1, pageSize = 20) { + if (config.useMock) { + return mockFetchActivityList(pageIndex, pageSize); + } + + return new Promise((resolve) => { + resolve('real api'); + }); +} diff --git a/miniprogram/tcb-shop/services/address/address.js b/miniprogram/tcb-shop/services/address/address.js new file mode 100644 index 0000000..d0e285e --- /dev/null +++ b/miniprogram/tcb-shop/services/address/address.js @@ -0,0 +1,85 @@ +import { DATA_MODEL_KEY } from '../../config/model'; +import { getAll, model } from '../_utils/model'; + +const DELIVERY_INFO_MODEL_KEY = DATA_MODEL_KEY.DELIVERY_INFO; + +export function getAllAddress() { + return getAll({ + name: DELIVERY_INFO_MODEL_KEY, + select: { + _id: true, + phone: true, + address: true, + name: true, + }, + }); +} + +/** + * + * @param {{ + * name: String, + * address: String, + * phone: String, + * }} param0 + * @returns + */ +export function createAddress({ name, address, phone }) { + return model()[DELIVERY_INFO_MODEL_KEY].create({ + data: { + name, + address, + phone, + }, + }); +} + +/** + * + * @param {{ + * name, + * address, + * phone, + * _id + * }} param0 + */ +export function updateAddress({ name, address, phone, _id }) { + return model()[DELIVERY_INFO_MODEL_KEY].update({ + data: { + name, + address, + phone, + }, + filter: { + where: { + _id: { $eq: _id }, + }, + }, + }); +} + +export function deleteAddress({ id }) { + return model()[DELIVERY_INFO_MODEL_KEY].delete({ + filter: { + where: { + _id: { + $eq: id, + }, + }, + }, + }); +} + +export async function getAddress({ id }) { + return ( + await model()[DELIVERY_INFO_MODEL_KEY].get({ + filter: { + where: { + _id: { + $eq: id, + }, + }, + }, + }) + ).data; +} diff --git a/miniprogram/tcb-shop/services/attrValue/attrValue.js b/miniprogram/tcb-shop/services/attrValue/attrValue.js new file mode 100644 index 0000000..915e460 --- /dev/null +++ b/miniprogram/tcb-shop/services/attrValue/attrValue.js @@ -0,0 +1,30 @@ +import { getAll } from '../_utils/model'; +import { DATA_MODEL_KEY } from '../../config/model'; + +const ATTR_VALUE_MODEL_KEY = DATA_MODEL_KEY.ATTR_VALUE; + +export async function getAllAttrValues(skuId) { + const res = await getAll({ + name: ATTR_VALUE_MODEL_KEY, + filter: { + relateWhere: { + sku: { + where: { + _id: { + $eq: skuId, + }, + }, + }, + }, + }, + select: { + _id: true, + value: true, + attr_name: { + _id: true, + name: true, + }, + }, + }); + return res; +} diff --git a/miniprogram/tcb-shop/services/cart/cart.js b/miniprogram/tcb-shop/services/cart/cart.js new file mode 100644 index 0000000..fb69231 --- /dev/null +++ b/miniprogram/tcb-shop/services/cart/cart.js @@ -0,0 +1,120 @@ +import { model, getAll } from '../../services/_utils/model'; +import { config } from '../../config/index'; +import { DATA_MODEL_KEY } from '../../config/model'; + +const CATE_ITEM_MODEL_KEY = DATA_MODEL_KEY.CART_ITEM; + +/** 获取购物车mock数据 */ +function mockFetchCartGroupData(params) { + const { delay } = require('../_utils/delay'); + const { genCartGroupData } = require('../../model/cart'); + + return delay().then(() => genCartGroupData(params)); +} + +/** + * + * @param {{id: string}} param0 + * @returns + */ +export async function getCartItem({ id }) { + return model()[CATE_ITEM_MODEL_KEY].get({ + filter: { + where: { + _id: { + $eq: id, + }, + }, + }, + select: { + _id: true, + count: true, + sku: { + _id: true, + count: true, + description: true, + }, + }, + }); +} + +export async function fetchCartItems() { + return getAll({ + name: CATE_ITEM_MODEL_KEY, + select: { + _id: true, + count: true, + sku: { + _id: true, + count: true, + description: true, + }, + }, + }); +} + +/** + * + * @param {{ + * skuId: String, + * count: Number + * }} param0 + */ +export async function createCartItem({ skuId, count }) { + return await model()[CATE_ITEM_MODEL_KEY].create({ + data: { + count, + sku: { _id: skuId }, + }, + }); +} + +/** + * + * @param {{cartItemId: string}} param0 + */ +export async function deleteCartItem({ cartItemId }) { + return await model()[CATE_ITEM_MODEL_KEY].delete({ + filter: { + where: { + _id: { + $eq: cartItemId, + }, + }, + }, + }); +} + +/** + * + * @param {{ + * cartItemId: String, + * count: Number + * }} param0 + * @returns + */ +export async function updateCartItemCount({ cartItemId, count }) { + return await model()[CATE_ITEM_MODEL_KEY].update({ + data: { + count, + }, + filter: { + where: { + _id: { + $eq: cartItemId, + }, + }, + }, + }); +} + +/** 获取购物车数据 */ +export function fetchCartGroupData(params) { + if (config.useMock) { + return mockFetchCartGroupData(params); + } + + return new Promise((resolve) => { + resolve('real api'); + }); +} diff --git a/miniprogram/tcb-shop/services/cate/cate.js b/miniprogram/tcb-shop/services/cate/cate.js new file mode 100644 index 0000000..331e1c9 --- /dev/null +++ b/miniprogram/tcb-shop/services/cate/cate.js @@ -0,0 +1,60 @@ +/* eslint-disable eqeqeq */ +import { model, getAll } from '../_utils/model'; +import { getCloudImageTempUrl } from '../../utils/cloudImageHandler'; +import { SPU_SELLING_STATUS } from '../../utils/spuStatus'; +import { DATA_MODEL_KEY } from '../../config/model'; + +const CATE_MODEL_KEY = DATA_MODEL_KEY.CATE; + +// TODO: we should do pagination +export async function getAllSpuOfCate(cateId) { + return ( + await model()[CATE_MODEL_KEY].get({ + filter: { + where: { + _id: { + $eq: cateId, + }, + }, + relateWhere: { + spu: { + where: { + status: { + $eq: SPU_SELLING_STATUS, + }, + }, + }, + }, + }, + select: { + spu: { + $master: true, + }, + }, + }) + ).data; +} + +export async function getCates() { + const cateSelect = { + _id: true, + name: true, + image: true, + }; + + const allCates = ( + await getAll({ + name: CATE_MODEL_KEY, + select: { + ...cateSelect, + child_cate: cateSelect, + }, + }) + ).filter((c) => c.child_cate.length !== 0); + + const childCates = allCates.flatMap((c) => c.child_cate); + const res = await getCloudImageTempUrl(childCates.map((x) => x.image)); + res.forEach((image, index) => (childCates[index].image = image)); + + return allCates; +} diff --git a/miniprogram/tcb-shop/services/comments/comments.js b/miniprogram/tcb-shop/services/comments/comments.js new file mode 100644 index 0000000..a4647fd --- /dev/null +++ b/miniprogram/tcb-shop/services/comments/comments.js @@ -0,0 +1,109 @@ +import { model } from '../_utils/model'; +import { DATA_MODEL_KEY } from '../../config/model'; + +const COMMENT_MODEL_KEY = DATA_MODEL_KEY.COMMENT; + +export async function getGoodsDetailCommentInfo(spuId) { + const firstAndCount = () => + model()[COMMENT_MODEL_KEY].list({ + filter: { + relateWhere: { + spu: { + where: { + _id: { + $eq: spuId, + }, + }, + }, + }, + }, + select: { + $master: true, + spu: { + _id: true, + }, + }, + orderBy: [{ rating: 'desc' }], + pageNumber: 1, + pageSize: 1, + getCount: true, + }); + const goodCount = () => + model()[COMMENT_MODEL_KEY].list({ + filter: { + relateWhere: { + spu: { + where: { + _id: { + $eq: spuId, + }, + }, + }, + }, + where: { + rating: { + $gt: 3, + }, + }, + }, + select: { + $master: true, + }, + pageNumber: 1, + pageSize: 1, + getCount: true, + }); + + return Promise.all([firstAndCount(), goodCount()]); +} + +export async function getCommentsOfSpu({ spuId, pageNumber, pageSize }) { + return ( + await model()[COMMENT_MODEL_KEY].list({ + select: { + $master: true, + order_item: { + _id: true, + }, + }, + filter: { + relateWhere: { + spu: { + where: { + _id: { + $eq: spuId, + }, + }, + }, + }, + }, + pageSize, + pageNumber, + getCount: true, + }) + ).data; +} + +/** + * + * @param {{ + * orderItemId: string, + * content: string, + * rating: number, + * spuId: string + * }} param0 + */ +export function createComment({ orderItemId, content, rating, spuId }) { + return model()[COMMENT_MODEL_KEY].create({ + data: { + content, + rating, + order_item: { + _id: orderItemId, + }, + spu: { + _id: spuId, + }, + }, + }); +} diff --git a/miniprogram/tcb-shop/services/comments/fetchComments.js b/miniprogram/tcb-shop/services/comments/fetchComments.js new file mode 100644 index 0000000..9bd1155 --- /dev/null +++ b/miniprogram/tcb-shop/services/comments/fetchComments.js @@ -0,0 +1,18 @@ +import { config } from '../../config/index'; + +/** 获取商品评论 */ +function mockFetchComments(parmas) { + const { delay } = require('../_utils/delay'); + const { getGoodsAllComments } = require('../../model/comments'); + return delay().then(() => getGoodsAllComments(parmas)); +} + +/** 获取商品评论 */ +export function fetchComments(parmas) { + if (config.useMock) { + return mockFetchComments(parmas); + } + return new Promise((resolve) => { + resolve('real api'); + }); +} diff --git a/miniprogram/tcb-shop/services/comments/fetchCommentsCount.js b/miniprogram/tcb-shop/services/comments/fetchCommentsCount.js new file mode 100644 index 0000000..cbb09d0 --- /dev/null +++ b/miniprogram/tcb-shop/services/comments/fetchCommentsCount.js @@ -0,0 +1,18 @@ +import { config } from '../../config/index'; + +/** 获取商品评论数 */ +function mockFetchCommentsCount(ID = 0) { + const { delay } = require('../_utils/delay'); + const { getGoodsCommentsCount } = require('../../model/comments'); + return delay().then(() => getGoodsCommentsCount(ID)); +} + +/** 获取商品评论数 */ +export function fetchCommentsCount(ID = 0) { + if (config.useMock) { + return mockFetchCommentsCount(ID); + } + return new Promise((resolve) => { + resolve('real api'); + }); +} diff --git a/miniprogram/tcb-shop/services/coupon/index.js b/miniprogram/tcb-shop/services/coupon/index.js new file mode 100644 index 0000000..a0010e0 --- /dev/null +++ b/miniprogram/tcb-shop/services/coupon/index.js @@ -0,0 +1,65 @@ +import { config } from '../../config/index'; + +/** 获取优惠券列表 */ +function mockFetchCoupon(status) { + const { delay } = require('../_utils/delay'); + const { getCouponList } = require('../../model/coupon'); + return delay().then(() => getCouponList(status)); +} + +/** 获取优惠券列表 */ +export function fetchCouponList(status = 'default') { + if (config.useMock) { + return mockFetchCoupon(status); + } + return new Promise((resolve) => { + resolve('real api'); + }); +} + +/** 获取优惠券 详情 */ +function mockFetchCouponDetail(id, status) { + const { delay } = require('../_utils/delay'); + const { getCoupon } = require('../../model/coupon'); + const { genAddressList } = require('../../model/address'); + + return delay().then(() => { + const result = { + detail: getCoupon(id, status), + storeInfoList: genAddressList(), + }; + + result.detail.useNotes = `1个订单限用1张,除运费券外,不能与其它类型的优惠券叠加使用(运费券除外)\n2.仅适用于各区域正常售卖商品,不支持团购、抢购、预售类商品`; + result.detail.storeAdapt = `商城通用`; + + if (result.detail.type === 'price') { + result.detail.desc = `减免 ${result.detail.value / 100} 元`; + + if (result.detail.base) { + result.detail.desc += `,满${result.detail.base / 100}元可用`; + } + + result.detail.desc += '。'; + } else if (result.detail.type === 'discount') { + result.detail.desc = `${result.detail.value}折`; + + if (result.detail.base) { + result.detail.desc += `,满${result.detail.base / 100}元可用`; + } + + result.detail.desc += '。'; + } + + return result; + }); +} + +/** 获取优惠券 详情 */ +export function fetchCouponDetail(id, status = 'default') { + if (config.useMock) { + return mockFetchCouponDetail(id, status); + } + return new Promise((resolve) => { + resolve('real api'); + }); +} diff --git a/miniprogram/tcb-shop/services/good/comments/fetchCommentDetail.js b/miniprogram/tcb-shop/services/good/comments/fetchCommentDetail.js new file mode 100644 index 0000000..fce338d --- /dev/null +++ b/miniprogram/tcb-shop/services/good/comments/fetchCommentDetail.js @@ -0,0 +1,20 @@ +import { config } from '../../../config/index'; +import { queryCommentDetail } from '../../../model/comments/queryDetail'; +/** 获取商品评价数据 */ +function mockQueryCommentDetail(params) { + const { delay } = require('../../_utils/delay'); + const data = queryCommentDetail(params); + return delay().then(() => { + return data; + }); +} + +/** 获取评价详情 */ +export function getCommentDetail(params) { + if (config.useMock) { + return mockQueryCommentDetail(params); + } + return new Promise((resolve) => { + resolve('real api'); + }); +} diff --git a/miniprogram/tcb-shop/services/good/fetchCategoryList.js b/miniprogram/tcb-shop/services/good/fetchCategoryList.js new file mode 100644 index 0000000..adf6ba4 --- /dev/null +++ b/miniprogram/tcb-shop/services/good/fetchCategoryList.js @@ -0,0 +1,18 @@ +import { config } from '../../config/index'; + +/** 获取商品列表 */ +function mockFetchGoodCategory() { + const { delay } = require('../_utils/delay'); + const { getCategoryList } = require('../../model/category'); + return delay().then(() => getCategoryList()); +} + +/** 获取商品列表 */ +export function getCategoryList() { + if (config.useMock) { + return mockFetchGoodCategory(); + } + return new Promise((resolve) => { + resolve('real api'); + }); +} diff --git a/miniprogram/tcb-shop/services/good/fetchGood.js b/miniprogram/tcb-shop/services/good/fetchGood.js new file mode 100644 index 0000000..577801d --- /dev/null +++ b/miniprogram/tcb-shop/services/good/fetchGood.js @@ -0,0 +1,24 @@ +import { config } from '../../config/index'; + +/** 获取商品列表 */ +function mockFetchGood(ID = 0) { + + const { delay } = require('../_utils/delay'); + const { genGood } = require('../../model/good'); + + return delay().then(() => { + const res = genGood(ID); + console.log("mock fetch goood return res", res); + return res; + }); +} + +/** 获取商品列表 */ +export function fetchGood(ID = 0) { + if (config.useMock) { + return mockFetchGood(ID); + } + return new Promise((resolve) => { + resolve('real api'); + }); +} diff --git a/miniprogram/tcb-shop/services/good/fetchGoodsDetailsComments.js b/miniprogram/tcb-shop/services/good/fetchGoodsDetailsComments.js new file mode 100644 index 0000000..95a11c4 --- /dev/null +++ b/miniprogram/tcb-shop/services/good/fetchGoodsDetailsComments.js @@ -0,0 +1,37 @@ +import { config } from '../../config/index'; + +/** 获取商品详情页评论数 */ +function mockFetchGoodDetailsCommentsCount(spuId = 0) { + const { delay } = require('../_utils/delay'); + const { + getGoodsDetailsCommentsCount, + } = require('../../model/detailsComments'); + return delay().then(() => getGoodsDetailsCommentsCount(spuId)); +} + +/** 获取商品详情页评论数 */ +export function getGoodsDetailsCommentsCount(spuId = 0) { + if (config.useMock) { + return mockFetchGoodDetailsCommentsCount(spuId); + } + return new Promise((resolve) => { + resolve('real api'); + }); +} + +/** 获取商品详情页评论 */ +function mockFetchGoodDetailsCommentList(spuId = 0) { + const { delay } = require('../_utils/delay'); + const { getGoodsDetailsComments } = require('../../model/detailsComments'); + return delay().then(() => getGoodsDetailsComments(spuId)); +} + +/** 获取商品详情页评论 */ +export function getGoodsDetailsCommentList(spuId = 0) { + if (config.useMock) { + return mockFetchGoodDetailsCommentList(spuId); + } + return new Promise((resolve) => { + resolve('real api'); + }); +} diff --git a/miniprogram/tcb-shop/services/good/fetchGoodsList.js b/miniprogram/tcb-shop/services/good/fetchGoodsList.js new file mode 100644 index 0000000..1650b9b --- /dev/null +++ b/miniprogram/tcb-shop/services/good/fetchGoodsList.js @@ -0,0 +1,39 @@ +/* eslint-disable no-param-reassign */ +import { config } from '../../config/index'; + +/** 获取商品列表 */ +function mockFetchGoodsList(params) { + const { delay } = require('../_utils/delay'); + const { getSearchResult } = require('../../model/search'); + + const data = getSearchResult(params); + + if (data.spuList.length) { + data.spuList.forEach((item) => { + item.spuId = item.spuId; + item.thumb = item.primaryImage; + item.title = item.title; + item.price = item.minSalePrice; + item.originPrice = item.maxLinePrice; + item.desc = ''; + if (item.spuTagList) { + item.tags = item.spuTagList.map((tag) => tag.title); + } else { + item.tags = []; + } + }); + } + return delay().then(() => { + return data; + }); +} + +/** 获取商品列表 */ +export function fetchGoodsList(params) { + if (config.useMock) { + return mockFetchGoodsList(params); + } + return new Promise((resolve) => { + resolve('real api'); + }); +} diff --git a/miniprogram/tcb-shop/services/good/fetchSearchHistory.js b/miniprogram/tcb-shop/services/good/fetchSearchHistory.js new file mode 100644 index 0000000..3a73963 --- /dev/null +++ b/miniprogram/tcb-shop/services/good/fetchSearchHistory.js @@ -0,0 +1,35 @@ +import { config } from '../../config/index'; + +/** 获取搜索历史 */ +function mockSearchHistory() { + const { delay } = require('../_utils/delay'); + const { getSearchHistory } = require('../../model/search'); + return delay().then(() => getSearchHistory()); +} + +/** 获取搜索历史 */ +export function getSearchHistory() { + if (config.useMock) { + return mockSearchHistory(); + } + return new Promise((resolve) => { + resolve('real api'); + }); +} + +/** 获取搜索历史 */ +function mockSearchPopular() { + const { delay } = require('../_utils/delay'); + const { getSearchPopular } = require('../../model/search'); + return delay().then(() => getSearchPopular()); +} + +/** 获取搜索历史 */ +export function getSearchPopular() { + if (config.useMock) { + return mockSearchPopular(); + } + return new Promise((resolve) => { + resolve('real api'); + }); +} diff --git a/miniprogram/tcb-shop/services/good/fetchSearchResult.js b/miniprogram/tcb-shop/services/good/fetchSearchResult.js new file mode 100644 index 0000000..5b42851 --- /dev/null +++ b/miniprogram/tcb-shop/services/good/fetchSearchResult.js @@ -0,0 +1,38 @@ +/* eslint-disable no-param-reassign */ +import { config } from '../../config/index'; + +/** 获取搜索历史 */ +function mockSearchResult(params) { + const { delay } = require('../_utils/delay'); + const { getSearchResult } = require('../../model/search'); + + const data = getSearchResult(params); + + if (data.spuList.length) { + data.spuList.forEach((item) => { + item.spuId = item.spuId; + item.thumb = item.primaryImage; + item.title = item.title; + item.price = item.minSalePrice; + item.originPrice = item.maxLinePrice; + if (item.spuTagList) { + item.tags = item.spuTagList.map((tag) => ({ title: tag.title })); + } else { + item.tags = []; + } + }); + } + return delay().then(() => { + return data; + }); +} + +/** 获取搜索历史 */ +export function getSearchResult(params) { + if (config.useMock) { + return mockSearchResult(params); + } + return new Promise((resolve) => { + resolve('real api'); + }); +} diff --git a/miniprogram/tcb-shop/services/good/spu.js b/miniprogram/tcb-shop/services/good/spu.js new file mode 100644 index 0000000..05ae792 --- /dev/null +++ b/miniprogram/tcb-shop/services/good/spu.js @@ -0,0 +1,77 @@ +import { model } from '../_utils/model'; +import { getCloudImageTempUrl } from '../../utils/cloudImageHandler'; +import { SPU_SELLING_STATUS } from '../../utils/spuStatus'; +import { DATA_MODEL_KEY } from '../../config/model'; + +const SPU_MODEL_KEY = DATA_MODEL_KEY.SPU; +const SKU_MODEL_KEY = DATA_MODEL_KEY.SKU; + +/** + * + * @param {{ + * pageSize: Number, + * pageNumber: Number, + * cateId: String, + * search: String + * }}} param0 + * @returns + */ +export async function listGood({ pageSize, pageNumber, search }) { + const filter = { + where: { + status: { $eq: SPU_SELLING_STATUS }, + }, + }; + if (search) { + filter.where.name = { $search: search }; + } + + return ( + await model()[SPU_MODEL_KEY].list({ + filter, + pageSize, + pageNumber, + getCount: true, + orderBy: [{ priority: 'desc' }], + }) + ).data; +} + +export async function getPrice(spuId) { + const { + data: { records }, + } = await model()[SKU_MODEL_KEY].list({ + filter: { + where: { + spu: { + $eq: spuId, + }, + }, + }, + }); + return records[0].price; +} + +export async function handleSpuCloudImage(spu) { + if (spu == null) { + return; + } + const handledImages = await getCloudImageTempUrl([spu.cover_image, ...spu.swiper_images]); + handledImages.forEach((image, index) => { + if (index === 0) { + spu.cover_image = image; + return; + } + spu.swiper_images[index - 1] = image; + }); +} + +export async function getSpu(spuId) { + return ( + await model()[SPU_MODEL_KEY].get({ + filter: { + where: { _id: { $eq: spuId } }, + }, + }) + ).data; +} diff --git a/miniprogram/tcb-shop/services/home/home.js b/miniprogram/tcb-shop/services/home/home.js new file mode 100644 index 0000000..ac3483d --- /dev/null +++ b/miniprogram/tcb-shop/services/home/home.js @@ -0,0 +1,8 @@ +import { model } from '../_utils/model'; +import { DATA_MODEL_KEY } from '../../config/model'; + +const HOME_SWIPER_MODEL_KEY = DATA_MODEL_KEY.HOME_SWIPER; + +export async function getHomeSwiper() { + return (await model()[HOME_SWIPER_MODEL_KEY].list({ select: { images: true } })).data.records[0]; +} diff --git a/miniprogram/tcb-shop/services/order/applyService.js b/miniprogram/tcb-shop/services/order/applyService.js new file mode 100644 index 0000000..c66930a --- /dev/null +++ b/miniprogram/tcb-shop/services/order/applyService.js @@ -0,0 +1,70 @@ +import { config } from '../../config/index'; + +/** 获取售后单mock数据 */ +function mockFetchRightsPreview(params) { + const { delay } = require('../_utils/delay'); + const { genRightsPreview } = require('../../model/order/applyService'); + + return delay().then(() => genRightsPreview(params)); +} + +/** 获取售后单数据 */ +export function fetchRightsPreview(params) { + if (config.useMock) { + return mockFetchRightsPreview(params); + } + + return new Promise((resolve) => { + resolve('real api'); + }); +} + +/** 确认收货 */ +export function dispatchConfirmReceived() { + if (config.useMock) { + const { delay } = require('../_utils/delay'); + return delay(); + } + + return new Promise((resolve) => { + resolve('real api'); + }); +} + +/** 获取可选的mock售后原因列表 */ +function mockFetchApplyReasonList(params) { + const { delay } = require('../_utils/delay'); + const { genApplyReasonList } = require('../../model/order/applyService'); + + return delay().then(() => genApplyReasonList(params)); +} + +/** 获取可选的售后原因列表 */ +export function fetchApplyReasonList(params) { + if (config.useMock) { + return mockFetchApplyReasonList(params); + } + + return new Promise((resolve) => { + resolve('real api'); + }); +} + +/** 发起mock售后申请 */ +function mockDispatchApplyService(params) { + const { delay } = require('../_utils/delay'); + const { applyService } = require('../../model/order/applyService'); + + return delay().then(() => applyService(params)); +} + +/** 发起售后申请 */ +export function dispatchApplyService(params) { + if (config.useMock) { + return mockDispatchApplyService(params); + } + + return new Promise((resolve) => { + resolve('real api'); + }); +} diff --git a/miniprogram/tcb-shop/services/order/order.js b/miniprogram/tcb-shop/services/order/order.js new file mode 100644 index 0000000..265d882 --- /dev/null +++ b/miniprogram/tcb-shop/services/order/order.js @@ -0,0 +1,171 @@ +import { model, getAll } from '../../services/_utils/model'; +import { DATA_MODEL_KEY } from '../../config/model'; + +const ORDER_MODEL_KEY = DATA_MODEL_KEY.ORDER; + +const ORDER_STATUS_INFO = { + TO_PAY: { value: 'TO_PAY', label: '待付款' }, + TO_SEND: { value: 'TO_SEND', label: '待发货' }, + TO_RECEIVE: { value: 'TO_RECEIVE', label: '待收货' }, + FINISHED: { value: 'FINISHED', label: '已完成' }, + CANCELED: { value: 'CANCELED', label: '已取消' }, + RETURN_APPLIED: { value: 'RETURN_APPLIED', label: '申请退货' }, + RETURN_REFUSED: { value: 'RETURN_REFUSED', label: '拒绝退货申请' }, + RETURN_FINISH: { value: 'RETURN_FINISH', label: '退货完成' }, + RETURN_MONEY_REFUSED: { value: 'RETURN_MONEY_REFUSED', label: '拒绝退款' }, +}; + +export const ORDER_STATUS = new Proxy(ORDER_STATUS_INFO, { + get(target, prop) { + return target[prop]?.value; + }, +}); + +export const orderStatusToName = (status) => Object.values(ORDER_STATUS_INFO).find((x) => x.value === status)?.label; + +/** + * + * @param {{ + * status: String, + * addressId: String + * }} param0 + * @returns + */ +export async function createOrder({ status, addressId }) { + return ( + await model()[ORDER_MODEL_KEY].create({ + data: { + status, + delivery_info: { + _id: addressId, + }, + }, + }) + ).data; +} + +export function getAllOrder() { + return getAll({ + name: ORDER_MODEL_KEY, + }); +} + +/** + * + * @param {{ + * pageSize: Number, + * pageNumber: Number, + * status?: String + * }}} param0 + * @returns + */ +export async function listOrder({ pageSize, pageNumber, status }) { + if (status != null) { + return ( + await model()[ORDER_MODEL_KEY].list({ + filter: { + where: { + status: { + $eq: status, + }, + }, + }, + pageSize, + pageNumber, + getCount: true, + }) + ).data; + } + return ( + await model()[ORDER_MODEL_KEY].list({ + filter: {}, + pageSize, + pageNumber, + getCount: true, + }) + ).data; +} + +async function getOrderCountOfStatus(status) { + return ( + await model()[ORDER_MODEL_KEY].list({ + filter: { where: { status: { $eq: status } } }, + select: { _id: true }, + getCount: true, + }) + ).data.total; +} + +export async function getToPayOrderCount() { + return getOrderCountOfStatus(ORDER_STATUS.TO_PAY); +} + +export async function getToSendOrderCount() { + return getOrderCountOfStatus(ORDER_STATUS.TO_SEND); +} + +export async function getToReceiveOrderCount() { + return getOrderCountOfStatus(ORDER_STATUS.TO_RECEIVE); +} + +/** + * + * @param {String} orderId + */ +export async function getOrder(orderId) { + return ( + await model()[ORDER_MODEL_KEY].get({ + filter: { + where: { + _id: { $eq: orderId }, + }, + }, + select: { + $master: true, + delivery_info: { + _id: true, + phone: true, + address: true, + name: true, + }, + }, + }) + ).data; +} + +export async function updateOrderDeliveryInfo({ orderId, deliveryInfoId }) { + return model()[ORDER_MODEL_KEY].update({ + data: { + delivery_info: { + _id: deliveryInfoId, + }, + }, + filter: { + where: { + _id: { + $eq: orderId, + }, + }, + }, + }); +} + +/** + * + * @param {{orderId: String, status: String}}} param0 + * @returns + */ +export async function updateOrderStatus({ orderId, status }) { + return await model()[ORDER_MODEL_KEY].update({ + data: { + status, + }, + filter: { + where: { + _id: { + $eq: orderId, + }, + }, + }, + }); +} diff --git a/miniprogram/tcb-shop/services/order/orderConfirm.js b/miniprogram/tcb-shop/services/order/orderConfirm.js new file mode 100644 index 0000000..4d34744 --- /dev/null +++ b/miniprogram/tcb-shop/services/order/orderConfirm.js @@ -0,0 +1,69 @@ +import { config } from '../../config/index'; +import { mockIp, mockReqId } from '../../utils/mock'; + +/** 获取结算mock数据 */ +function mockFetchSettleDetail(params) { + const { delay } = require('../_utils/delay'); + const { genSettleDetail } = require('../../model/order/orderConfirm'); + + return delay().then(() => genSettleDetail(params)); +} + +/** 提交mock订单 */ +function mockDispatchCommitPay() { + const { delay } = require('../_utils/delay'); + + return delay().then(() => ({ + data: { + isSuccess: true, + tradeNo: '350930961469409099', + payInfo: '{}', + code: null, + transactionId: 'E-200915180100299000', + msg: null, + interactId: '15145', + channel: 'wechat', + limitGoodsList: null, + }, + code: 'Success', + msg: null, + requestId: mockReqId(), + clientIp: mockIp(), + rt: 891, + success: true, + })); +} + +/** 获取结算数据 */ +export function fetchSettleDetail(params) { + if (config.useMock) { + return mockFetchSettleDetail(params); + } + + return new Promise((resolve) => { + resolve('real api'); + }); +} + +/* 提交订单 */ +export function dispatchCommitPay(params) { + if (config.useMock) { + return mockDispatchCommitPay(params); + } + + return new Promise((resolve) => { + resolve('real api'); + }); +} + +/** 开发票 */ +export function dispatchSupplementInvoice() { + if (config.useMock) { + const { delay } = require('../_utils/delay'); + return delay(); + } + + return new Promise((resolve) => { + resolve('real api'); + }); +} diff --git a/miniprogram/tcb-shop/services/order/orderDetail.js b/miniprogram/tcb-shop/services/order/orderDetail.js new file mode 100644 index 0000000..3e7d797 --- /dev/null +++ b/miniprogram/tcb-shop/services/order/orderDetail.js @@ -0,0 +1,39 @@ +import { config } from '../../config/index'; + +/** 获取订单详情mock数据 */ +function mockFetchOrderDetail(params) { + const { delay } = require('../_utils/delay'); + const { genOrderDetail } = require('../../model/order/orderDetail'); + + return delay().then(() => genOrderDetail(params)); +} + +/** 获取订单详情数据 */ +export function fetchOrderDetail(params) { + if (config.useMock) { + return mockFetchOrderDetail(params); + } + + return new Promise((resolve) => { + resolve('real api'); + }); +} + +/** 获取客服mock数据 */ +function mockFetchBusinessTime(params) { + const { delay } = require('../_utils/delay'); + const { genBusinessTime } = require('../../model/order/orderDetail'); + + return delay().then(() => genBusinessTime(params)); +} + +/** 获取客服数据 */ +export function fetchBusinessTime(params) { + if (config.useMock) { + return mockFetchBusinessTime(params); + } + + return new Promise((resolve) => { + resolve('real api'); + }); +} diff --git a/miniprogram/tcb-shop/services/order/orderItem.js b/miniprogram/tcb-shop/services/order/orderItem.js new file mode 100644 index 0000000..6587585 --- /dev/null +++ b/miniprogram/tcb-shop/services/order/orderItem.js @@ -0,0 +1,77 @@ +import { model, getAll } from '../../services/_utils/model'; +import { getSkuDetail } from '../sku/sku'; +import { DATA_MODEL_KEY } from '../../config/model'; + +const ORDER_ITEM_MODEL_KEY = DATA_MODEL_KEY.ORDER_ITEM; + +export async function getOrderItem(id) { + return ( + await model()[ORDER_ITEM_MODEL_KEY].get({ + filter: { + where: { + _id: { + $eq: id, + }, + }, + }, + select: { + sku: { + _id: true, + }, + comment: { + _id: true, + }, + }, + }) + ).data; +} + +export async function createOrderItem({ count, skuId, orderId }) { + return model()[ORDER_ITEM_MODEL_KEY].create({ + data: { + count, + sku: { + _id: skuId, + }, + order: { + _id: orderId, + }, + }, + }); +} + +export function getAllOrderItems() { + return getAll({ name: ORDER_ITEM_MODEL_KEY }); +} + +/** + * + * @param {{orderId: String}} param0 + */ +export async function getAllOrderItemsOfAnOrder({ orderId }) { + const orderItems = await getAll({ + name: ORDER_ITEM_MODEL_KEY, + filter: { + where: { + order: { + $eq: orderId, + }, + }, + }, + select: { + _id: true, + count: true, + sku: { + _id: true, + }, + }, + }); + await Promise.all( + orderItems.map(async (orderItem) => { + const skuId = orderItem.sku._id; + const sku = await getSkuDetail(skuId); + orderItem.sku = sku; + }), + ); + return orderItems; +} diff --git a/miniprogram/tcb-shop/services/order/orderList.js b/miniprogram/tcb-shop/services/order/orderList.js new file mode 100644 index 0000000..f4a9e7a --- /dev/null +++ b/miniprogram/tcb-shop/services/order/orderList.js @@ -0,0 +1,39 @@ +import { config } from '../../config/index'; + +/** 获取订单列表mock数据 */ +function mockFetchOrders(params) { + const { delay } = require('../_utils/delay'); + const { genOrders } = require('../../model/order/orderList'); + + return delay(200).then(() => genOrders(params)); +} + +/** 获取订单列表数据 */ +export function fetchOrders(params) { + if (config.useMock) { + return mockFetchOrders(params); + } + + return new Promise((resolve) => { + resolve('real api'); + }); +} + +/** 获取订单列表mock数据 */ +function mockFetchOrdersCount(params) { + const { delay } = require('../_utils/delay'); + const { genOrdersCount } = require('../../model/order/orderList'); + + return delay().then(() => genOrdersCount(params)); +} + +/** 获取订单列表统计 */ +export function fetchOrdersCount(params) { + if (config.useMock) { + return mockFetchOrdersCount(params); + } + + return new Promise((resolve) => { + resolve('real api'); + }); +} diff --git a/miniprogram/tcb-shop/services/order/orderSubmitComment.js b/miniprogram/tcb-shop/services/order/orderSubmitComment.js new file mode 100644 index 0000000..5e3ea36 --- /dev/null +++ b/miniprogram/tcb-shop/services/order/orderSubmitComment.js @@ -0,0 +1,22 @@ +import { config } from '../../config/index'; + +/** 获取评价商品 */ +function mockGetGoods(parameter) { + const { delay } = require('../_utils/delay'); + const { getGoods } = require('../../model/submitComment'); + const data = getGoods(parameter); + + return delay().then(() => { + return data; + }); +} + +/** 获取评价商品 */ +export function getGoods(parameter) { + if (config.useMock) { + return mockGetGoods(parameter); + } + return new Promise((resolve) => { + resolve('real api'); + }); +} diff --git a/miniprogram/tcb-shop/services/pay/pay.js b/miniprogram/tcb-shop/services/pay/pay.js new file mode 100644 index 0000000..bcc9552 --- /dev/null +++ b/miniprogram/tcb-shop/services/pay/pay.js @@ -0,0 +1,44 @@ +/** + * + * @param {{_id: String, totalPrice: Number}} order + * @returns + */ +export async function pay(order) { + try { + const res = await wx.cloud.callFunction({ + // 云函数名称 + name: 'wxpayFunctions', + data: { + type: 'wxpay_order', + order, + }, + }); + const paymentData = res.result?.data; + // 唤起微信支付组件,完成支付 + try { + await wx.requestPayment({ + timeStamp: paymentData?.timeStamp, + nonceStr: paymentData?.nonceStr, + package: paymentData?.packageVal, + paySign: paymentData?.paySign, + signType: 'RSA', // 该参数为固定值 + }); + } catch (e) { + return Promise.reject(e); + } + } catch (e) { + return Promise.reject(e); + } +} + +export async function refund(orderId) { + return wx.cloud.callFunction({ + // 云函数名称 + name: 'wxpayFunctions', + data: { + // 调用云函数中的申请退款方法 + type: 'wxpay_refund', + orderId, + }, + }); +} diff --git a/miniprogram/tcb-shop/services/promotion/detail.js b/miniprogram/tcb-shop/services/promotion/detail.js new file mode 100644 index 0000000..841f723 --- /dev/null +++ b/miniprogram/tcb-shop/services/promotion/detail.js @@ -0,0 +1,18 @@ +import { config } from '../../config/index'; + +/** 获取商品列表 */ +function mockFetchPromotion(ID = 0) { + const { delay } = require('../_utils/delay'); + const { getPromotion } = require('../../model/promotion'); + return delay().then(() => getPromotion(ID)); +} + +/** 获取商品列表 */ +export function fetchPromotion(ID = 0) { + if (config.useMock) { + return mockFetchPromotion(ID); + } + return new Promise((resolve) => { + resolve('real api'); + }); +} diff --git a/miniprogram/tcb-shop/services/sku/sku.js b/miniprogram/tcb-shop/services/sku/sku.js new file mode 100644 index 0000000..60d550d --- /dev/null +++ b/miniprogram/tcb-shop/services/sku/sku.js @@ -0,0 +1,57 @@ +import { model, getAll } from '../_utils/model'; +import { DATA_MODEL_KEY } from '../../config/model'; + +const SKU_MODEL_KEY = DATA_MODEL_KEY.SKU; + +/** + * + * @param {String} skuId + */ +export async function getSkuDetail(skuId) { + const { data } = await model()[SKU_MODEL_KEY].get({ + filter: { + where: { + _id: { $eq: skuId }, + }, + }, + select: { + _id: true, + count: true, + price: true, + image: true, + attr_value: { + value: true, + _id: true, + }, + spu: { + name: true, + }, + }, + }); + return data; +} + +export async function updateSku({ skuId, data }) { + return wx.cloud.callFunction({ + // 云函数名称 + name: 'shop_update_sku', + // 传给云函数的参数 + data: { + skuId, + data, + }, + }); +} + +export async function getAllSku(spuId) { + return getAll({ + name: SKU_MODEL_KEY, + filter: { + where: { + spu: { + $eq: spuId, + }, + }, + }, + }); +} diff --git a/miniprogram/tcb-shop/services/usercenter/fetchPerson.js b/miniprogram/tcb-shop/services/usercenter/fetchPerson.js new file mode 100644 index 0000000..bd31178 --- /dev/null +++ b/miniprogram/tcb-shop/services/usercenter/fetchPerson.js @@ -0,0 +1,28 @@ +import { config } from '../../config/index'; + +/** 获取个人中心信息 */ +function mockFetchPerson() { + const { delay } = require('../_utils/delay'); + const { genSimpleUserInfo } = require('../../model/usercenter'); + const { genAddress } = require('../../model/address'); + const address = genAddress(); + return delay().then(() => ({ + ...genSimpleUserInfo(), + address: { + provinceName: address.provinceName, + provinceCode: address.provinceCode, + cityName: address.cityName, + cityCode: address.cityCode, + }, + })); +} + +/** 获取个人中心信息 */ +export function fetchPerson() { + if (config.useMock) { + return mockFetchPerson(); + } + return new Promise((resolve) => { + resolve('real api'); + }); +} diff --git a/miniprogram/tcb-shop/services/usercenter/fetchUsercenter.js b/miniprogram/tcb-shop/services/usercenter/fetchUsercenter.js new file mode 100644 index 0000000..b187644 --- /dev/null +++ b/miniprogram/tcb-shop/services/usercenter/fetchUsercenter.js @@ -0,0 +1,18 @@ +import { config } from '../../config/index'; + +/** 获取个人中心信息 */ +function mockFetchUserCenter() { + const { delay } = require('../_utils/delay'); + const { genUsercenter } = require('../../model/usercenter'); + return delay(200).then(() => genUsercenter()); +} + +/** 获取个人中心信息 */ +export function fetchUserCenter() { + if (config.useMock) { + return mockFetchUserCenter(); + } + return new Promise((resolve) => { + resolve('real api'); + }); +} diff --git a/miniprogram/tcb-shop/sitemap.json b/miniprogram/tcb-shop/sitemap.json new file mode 100644 index 0000000..ca02add --- /dev/null +++ b/miniprogram/tcb-shop/sitemap.json @@ -0,0 +1,7 @@ +{ + "desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html", + "rules": [{ + "action": "allow", + "page": "*" + }] +} \ No newline at end of file diff --git a/miniprogram/tcb-shop/style/cart-group.wxss b/miniprogram/tcb-shop/style/cart-group.wxss new file mode 100644 index 0000000..ad585a5 --- /dev/null +++ b/miniprogram/tcb-shop/style/cart-group.wxss @@ -0,0 +1,83 @@ +/* var() css变量适配*/ +.wr-swiper-cell__right { + margin: 20rpx 0; +} +.wr-swiper-cell__right .swiper-right-del { + height: calc(100% - 40rpx); + width: 60px; + background-color: #ff2525; + font-size: 28rpx; + color: white; + display: flex; + justify-content: center; + align-items: center; +} +.goods-stepper .stepper { + border: none; + border-radius: 0; + height: auto; + width: 168rpx; + overflow: visible; +} +.goods-stepper .stepper .stepper__minus, +.goods-stepper .stepper .stepper__plus { + width: 44rpx; + height: 44rpx; + background-color: #f5f5f5; +} +.goods-stepper .stepper .stepper__minus--hover, +.goods-stepper .stepper .stepper__plus--hover { + background-color: #f5f5f5; +} +.goods-stepper .stepper .stepper__minus .wr-icon, +.goods-stepper .stepper .stepper__plus .wr-icon { + font-size: 24rpx; +} +.goods-stepper .stepper .stepper__minus { + position: relative; +} +.goods-stepper .stepper .stepper__minus::after { + position: absolute; + display: block; + content: ' '; + left: -20rpx; + right: -5rpx; + top: -20rpx; + bottom: -20rpx; + background-color: transparent; +} +.goods-stepper .stepper .stepper__plus { + position: relative; +} +.goods-stepper .stepper .stepper__plus::after { + position: absolute; + display: block; + content: ' '; + left: -5rpx; + right: -20rpx; + top: -20rpx; + bottom: -20rpx; + background-color: transparent; +} +.goods-stepper .stepper .stepper__input { + width: 72rpx; + height: 44rpx; + background-color: #f5f5f5; + font-size: 24rpx; + color: #222427; + font-weight: 600; + border-left: none; + border-right: none; + min-height: 40rpx; + margin: 0 4rpx; + display: flex; + align-items: center; +} +.invalid-card .invalid-private-mask .wr-goods-card { + background-color: #fff; + padding: 18rpx 32rpx 24rpx 80rpx; +} +.text-primary .wr-icon { + color: #fa550f; + color: var(--color-primary, #fa550f); +} diff --git a/miniprogram/tcb-shop/style/global.wxss b/miniprogram/tcb-shop/style/global.wxss new file mode 100644 index 0000000..0194db1 --- /dev/null +++ b/miniprogram/tcb-shop/style/global.wxss @@ -0,0 +1,960 @@ +/* + * @Author: oliverppeng + * @LastEditors: Please set LastEditors + * @Date: 2021-12-01 17:33:43 + * @LastEditTime: 2021-12-03 15:31:17 + * @Description: + * @FilePath: /retail-mp/style/global.wxss + */ +.text-primary { + color: #fa550f; +} +.text-success { + color: #5fb446; +} +.text-warn { + color: #ec8131; +} +.text-danger { + color: #de1c24; +} +.text-title { + color: #282828; +} +.text-normal { + color: #5d5d5d; +} +.text-small { + color: #9b9b9b; +} +.text-minor { + color: #ececec; +} +.text-border { + color: #eeeeee; +} +.text-white { + color: #fff; +} +.bg-primary { + background-color: #fa550f; + color: #fff; +} +.bg-success { + background-color: #5fb446; + color: #fff; +} +.bg-warn { + background-color: #ec8131; + color: #fff; +} +.bg-danger { + background-color: #de1c24; + color: #fff; +} +.bg-title { + background-color: #282828; + color: #fff; +} +.bg-normal { + background-color: #5d5d5d; + color: #282828; +} +.bg-small { + background-color: #9b9b9b; + color: #5d5d5d; +} +.bg-minor { + background-color: #ececec; + color: #5d5d5d; +} +.bg-border { + background-color: #eeeeee; + color: #5d5d5d; +} +.bd-primary { + color: #fa550f; +} +.bd-success { + color: #5fb446; +} +.bd-warn { + color: #ec8131; +} +.bd-danger { + color: #de1c24; +} +.bd-title { + color: #282828; +} +.bd-normal { + color: #5d5d5d; +} +.bd-small { + color: #9b9b9b; +} +.bd-minor { + color: #ececec; +} +.bd-border { + color: #eeeeee; +} +.ft-super { + font-size: 40rpx; +} +.ft-main { + font-size: 36rpx; +} +.ft-normal { + font-size: 32rpx; +} +.ft-assist { + font-size: 28rpx; +} +.ft-minor { + font-size: 24rpx; +} +.ft-mini { + font-size: 20rpx; +} +.fw-super { + font-weight: 800; +} +.fw-main { + font-weight: 600; +} +.fw-normal { + font-weight: 400; +} +.fw-minor { + font-weight: 300; +} +.mo-border-1rpx { + position: relative; + z-index: 0; +} +.mo-border-1rpx::before { + content: ''; + position: absolute; + left: 0; + top: 0; + width: 200%; + height: 200%; + border-width: 2rpx; + border-style: solid; + transform: scale(0.5); + transform-origin: 0 0; + z-index: -1; + box-sizing: border-box; +} +/* 层级定义 + @z-index-0: 1; + @z-index-1: 100; + @z-index-2: 200; + @z-index-5: 500; + @z-index-component: 1000; // 通用组件级别 + @z-index-dropdown: @z-index-component; + @z-index-sticky: @z-index-component + 20; + @z-index-fixed: @z-index-component + 30; + @z-index-modal-backdrop:@z-index-component + 40; + @z-index-modal:@z-index-component + 50; + @z-index-popover:@z-index-component + 60; + @z-index-tooltip:@z-index-component + 70; + */ +page { + height: 100%; + font-family: 'Microsoft YaHei', '微软雅黑', 'MicrosoftJhengHei', '华文细黑', Helvetica, Arial, 'sans-serif'; + font-size: 26rpx; + background-color: #f8f8f8; + font-weight: 400; +} +view, +image, +icon, +scroll-view, +text, +button, +checkbox, +form, +input, +label, +navigator, +audio, +video, +canvas { + box-sizing: border-box; + margin: 0; + padding: 0; + border: 0; + outline: 0; +} +scroll-view { + height: 100%; +} +form, +image { + display: block; +} +button { + padding: 0; + margin: 0; + border-radius: 0; + height: 100%; + display: block; + line-height: inherit; + font-size: inherit; + color: inherit; + background: none; + -webkit-appearance: none; + border: none; +} +button::after { + content: none; +} +input, +textarea { + font-family: 'Microsoft YaHei', '微软雅黑', 'MicrosoftJhengHei', '华文细黑', Helvetica, Arial, 'sans-serif'; + font-size: 26rpx; + z-index: 0; +} +.price { + color: #ec8131; + font-size: 32rpx; + font-weight: 600; +} +.price-del { + color: #9b9b9b; + font-size: 24rpx; + font-weight: 400; +} +.page { + background: #fff; +} +.color-price { + color: #ec8131; +} +.bg-cart { + background-color: #ffc220; + color: #fff; +} +.market-addcart { + color: #ec8131; + font-size: 42rpx; +} +.ovh { + overflow: hidden; +} +.hidden { + display: none; +} +.show { + display: block; +} +.text { + display: inline-block; +} +.inline { + display: inline; +} +.minHeight { + min-height: 101%; +} +.imgCover { + width: 100%; + padding-bottom: 100%; + background-size: cover; + background-position: center; + background-repeat: no-repeat; + display: block; + position: relative; +} +.imgCover-list { + width: 195rpx; + height: 260rpx; + padding-bottom: 0; + background-size: cover; + background-position: center; + background-repeat: no-repeat; + display: block; + position: relative; +} +.circular { + border-radius: 50%; +} +.text-line1 { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + word-break: break-all; +} +.text-line-1 { + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-line-clamp: 1; + -webkit-box-orient: vertical; + word-break: break-all; +} +.text-line2 { + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + word-break: break-all; +} +.text-line3 { + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + word-break: break-all; +} +.flex, +.box { + display: flex; + display: -webkit-flex; +} +.flex-v-center { + align-items: center; + -webkit-align-items: center; +} +.flex-center { + justify-content: center; + -webkit-justify-content: center; + align-items: center; + -webkit-align-items: center; +} +.flex-between { + justify-content: space-between; + -webkit-justify-content: space-between; +} +.flex-v-between { + align-content: space-between; + -webkit-align-content: space-between; +} +.flex-end { + justify-content: flex-end; + -webkit-justify-content: flex-end; +} +.flex-col { + flex-direction: column; + -webkit-flex-direction: column; +} +.flex1 { + flex: 1; + -webkit-flex: 1; +} +.flex0 { + flex: none; + -webkit-flex: none; +} +.flex-start { + justify-content: flex-start; + -webkit-justify-content: flex-start; +} +.border-around, +.border-bottom-1px, +.border-left-1px, +.border-right-1px, +.border-top-1px { + position: relative; + border: 1rpx solid #e9e9e9; +} +.border-top-1px { + border-width: 1rpx 0 0 0; +} +.border-right-1px { + border-width: 0 1rpx 0 0; +} +.border-bottom-1px { + border-width: 0 0 1rpx 0; +} +.border-left-1px { + border-width: 0 0 0 1rpx; +} +.border-t-1px, +.border-r-1px, +.border-b-1px, +.border-l-1px { + position: relative; +} +.border-t-1px::after, +.border-r-1px::after, +.border-b-1px::after, +.border-l-1px::after { + content: ''; + position: absolute; + border-width: 2rpx; + border-color: #efefef; + border-style: solid; +} +.border-t-1px::after { + left: 0; + top: 0; + width: 100%; + transform: scaleY(0.5); +} +.border-b-1px::after { + left: 0; + bottom: 0; + width: 100%; + transform: scaleY(0.5); +} +.border-l-1px::after { + left: 0; + top: 0; + height: 100%; + transform: scaleX(0.5); +} +.border-r-1px::after { + right: 0; + top: 0; + height: 100%; + transform: scaleX(0.5); +} +.arrows { + position: relative; +} +.arrows::after { + content: ''; + display: inline-block; + width: 6px; + height: 6px; + border: 2px solid #c3c3c3; + border-width: 2px 2px 0 0; + position: absolute; + top: 50%; + right: 26rpx; + margin-top: -3px; + -webkit-transform: rotate(45deg); + transform: rotate(45deg); +} +.arrows-inline::after { + position: relative; + left: auto; + top: auto; + right: auto; + bottom: auto; + margin-top: -4px; + margin-left: 5px; +} +.pt-16, +.pt-8 { + padding-top: 16rpx; +} +.pb-16, +.pb-8 { + padding-bottom: 16rpx; +} +.pl-16, +.pl-8 { + padding-left: 16rpx; +} +.pr-16, +.pr-8 { + padding-right: 16rpx; +} +.pl-20, +.pl-10 { + padding-left: 20rpx; +} +.pr-20, +.pr-10 { + padding-right: 20rpx; +} +.pl-30 { + padding-left: 30rpx; +} +.pr-30 { + padding-right: 30rpx; +} +.pl-32, +.pl-15 { + padding-left: 32rpx; +} +.pr-32, +.pr-15 { + padding-right: 32rpx; +} +.pb360 { + padding-bottom: 360rpx; +} +.PriceSwitch { + line-height: 21px; + font-size: 24rpx; + padding: 0 8rpx; +} +.PriceSwitch .i { + font-size: 30rpx; +} +.Original { + font-style: normal; + font-size: 24rpx; + color: #9a9a9a; + text-decoration: line-through; + margin-left: 16rpx; + display: inline-block; +} +.color1, +h1, +h2, +h3, +h4, +h5, +h6 { + font-weight: normal; + color: #1a1a1a; + margin: 0; + padding: 0; +} +.color-3 { + color: #333; +} +.color-6 { + color: #666; +} +.color-9 { + color: #999; +} +.color-e9 { + color: #e9e9e9; +} +.color-64 { + color: #646464; +} +.color-b4 { + color: #b4b4b4; +} +.color-97 { + color: #979797; +} +.color-9b { + color: #9b9b9b; +} +.color-white { + color: #fff; +} +.color-black { + color: #000; +} +.color-c { + color: #ccc; +} +.bkg-white { + background-color: #fff; +} +.font10 { + font-size: 20rpx; +} +.font11 { + font-size: 22rpx; +} +.font12 { + font-size: 24rpx; +} +.font13 { + font-size: 26rpx; +} +.font14 { + font-size: 28rpx; +} +.font15 { + font-size: 30rpx; +} +.font16 { + font-size: 32rpx; +} +.font17 { + font-size: 34rpx; +} +.font18 { + font-size: 36rpx; +} +.font19 { + font-size: 38rpx; +} +.font20 { + font-size: 20rpx; +} +.font22 { + font-size: 22rpx; +} +.font24 { + font-size: 24rpx; +} +.font26 { + font-size: 26rpx; +} +.font28 { + font-size: 28rpx; +} +.font30 { + font-size: 30rpx; +} +.font32 { + font-size: 32rpx; +} +.font34 { + font-size: 34rpx; +} +.font36 { + font-size: 36rpx; +} +.font38 { + font-size: 38rpx; +} +.font40 { + font-size: 40rpx; +} +.font46 { + font-size: 46rpx; +} +.font50 { + font-size: 50rpx; +} +.font56 { + font-size: 56rpx; +} +.font82 { + font-size: 82rpx; +} +.bkg-white { + background-color: #fff; +} +.fontWeight-l { + font-weight: 500; +} +.fontWeight-n { + font-weight: 300; +} +.fontWeight-nor { + font-weight: normal; +} +.line-height1 { + line-height: 1; +} +.btn-active { + width: 40rpx; + display: inline-block; + text-align: center; + height: 40rpx; + background: #ff2e45; + border-radius: 8rpx; + color: #fff; + line-height: 40rpx; + font-size: 24rpx; + font-weight: normal; + font-style: normal; + overflow: hidden; +} +.btn-auto { + display: inline-block; + font-size: 20rpx; + border: 1px solid #ff2e45; + border-radius: 8rpx; + height: 36rpx; + line-height: 36rpx; + color: #ff2e45; + padding: 0 12rpx; + margin-right: 16rpx; +} +.btn-lg, +.btn-md, +.btn-sm, +.btn-xs { + text-align: center; + width: 100%; + border-radius: 8rpx; + color: #fff; + line-height: 88rpx; + font-size: 30rpx; +} +.btn-lg, +.btn-md { + background-color: #ff2e45; +} +.btn-sm { + border: 1px solid #999; + color: #666; + height: 60rpx; + line-height: 60rpx; +} +.btn-xs { + width: 100%; + height: 68rpx; + line-height: 68rpx; + border: 1px solid #e2e2e2; + color: #666; +} +.btn-dashed, +.btn-md-dashed { + width: 100%; + height: 80rpx; + line-height: 80rpx; + text-align: center; + color: #ff2e45; + border-radius: 8rpx; + border: 1px solid #ff2e45; + font-size: 26rpx; +} +.btn-md-dashed { + height: 50rpx; + line-height: 50rpx; + border-radius: 8rpx; +} +.btn-bj1 { + background-color: #ff2e45; +} +.btn-bj2 { + background-color: #ff8522; +} +.btn-dis { + background-color: #999; + color: #bbb; +} +.btn-dashed-dis { + border: 1px solid #c8c8c8; + color: #bbbbbb; + border-radius: 8rpx; +} +.titleTag { + line-height: 28rpx; + height: 28rpx; + margin-right: 8rpx; + color: #fff; + padding: 0 8rpx; + font-size: 18rpx; + border-radius: 4rpx; + font-weight: bold; + display: inline-block; +} +.titleTag.memberTag { + margin-top: 2rpx; +} +.amounts { + font-style: normal; + display: inline-block; + height: 24rpx; + min-width: 24rpx; + padding: 0 6rpx; + border-radius: 24rpx; + background-color: #fd1d45 !important; + color: #fff; + text-align: center; + font-size: 20rpx; + font-weight: 600; + position: absolute; + top: 6rpx; + line-height: 24rpx; + box-sizing: border-box; +} +.amounts:empty, +.titleTag:empty { + display: none; +} +.loadMore { + font-size: 26rpx; + color: #c1c1c1; + text-align: center; + height: 80rpx; + line-height: 58rpx; + margin-top: 20rpx; + display: flex; + align-items: center; + justify-content: center; +} +.loadings { + margin-right: 10rpx; + width: 40rpx; + height: 40rpx; + display: inline-block; + vertical-align: sub; + background: url(https://cdn.ghsmpwalmart.com/saas/market/image/loadings.png) no-repeat; + background-size: 240rpx 40rpx; + background-position: 0 0; + animation: circle-loadings 1s steps(6) infinite; +} +@keyframes circle-loadings { + from { + background-position: 0 0; + } + to { + background-position: -240rpx 0; + } +} +.page-loading { + display: inline-block; + width: 72rpx; + height: 75rpx; + background: url('https://cdn.ghsmpwalmart.com/saas/market/image/page-loading-bc.png') no-repeat; + background-position: 0 0; + background-size: 72rpx 75rpx; +} +.page-loading::before { + content: ' '; + display: inline-block; + width: 72rpx; + height: 75rpx; + background: url('https://cdn.ghsmpwalmart.com/saas/market/image/page-loading-spin.png') no-repeat; + background-position: 0 0; + background-size: 720rpx 75rpx; + animation: animate-page-loading 0.4s steps(10) infinite; +} +@keyframes animate-page-loading { + from { + background-position-x: 0rpx; + } + to { + background-position-x: -720rpx; + } +} +.page-loading-wrap { + width: 100%; + height: 1026rpx; + display: flex; + display: -webkit-flex; + justify-content: center; + -webkit-justify-content: center; + padding-top: 46vh; +} +.grooms { + margin: 0 16rpx; + overflow: hidden; + text-align: center; + height: 52rpx; +} +.grooms label { + display: inline-block; + padding: 0 20rpx; + height: 52rpx; + line-height: 52rpx; + position: relative; + color: #3e3e3e; + font-size: 26rpx; +} +.grooms label > p { + overflow: hidden; + max-width: 400rpx; + min-width: 120rpx; + white-space: nowrap; + text-overflow: ellipsis; +} +.grooms label > p::before { + content: ''; + display: inline-block; + position: absolute; + width: 1000rpx; + margin-left: -1000rpx; + height: 0; + left: 0; + top: 50%; + border: 1px solid #e9e9e9; + border-width: 1px 0 0 0; +} +.grooms label > p::after { + content: ''; + display: inline-block; + position: absolute; + width: 1000rpx; + margin-left: -1000rpx; + height: 0; + left: 0; + left: inherit; + top: 50%; + border: 1px solid #e9e9e9; + border-width: 0 0 1px 0; + right: 0; + margin-right: -1000rpx; +} +.navHeight { + height: 100rpx; +} +.widget-mask, +.widget_mask { + position: fixed; + left: 0; + top: 0px; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.5); + z-index: 200; +} +.theme-color { + color: #f12d22; +} +.theme-bgc { + background-color: #f12d22; +} +.theme-bdc { + border-color: #f12d22; +} +.theme-bgcart { + background-color: #ffb56c; +} +.IphoneX { + padding-bottom: 68rpx !important; +} +.fixIphonex { + bottom: 68rpx !important; +} +.fixIphonex::after { + content: ' '; + position: fixed; + bottom: 0 !important; + height: 68rpx !important; + width: 100%; + background: #ffffff; + left: 0; +} +.iphonexMenus { + bottom: 166rpx !important; +} +.imgCover-oblong-sm .imgCover { + width: 195rpx; + height: 260rpx; +} +.imgCover-oblong-lg .imgCover { + padding-bottom: 133%; +} +.fw-Light { + font-weight: 300; +} +.fw-Regular { + font-weight: 400; +} +.fw-Medium { + font-weight: 600; +} +.fw-Semibold { + font-weight: 800; +} +.icon-xingouwuche { + font-size: 42rpx; +} +.color777 { + color: #777777; +} +.popup-header { + color: #000; + font-size: 28rpx; + text-align: center; + height: 100rpx; + line-height: 100rpx; + position: relative; + border-bottom: 1rpx solid #dbdbdb; + font-weight: 400; +} +.popup-header .wr-close { + position: absolute; + left: 0; + font-size: 26rpx; + color: #5d5d5d; + width: 60rpx; + height: 60rpx; + text-align: center; + line-height: 60rpx; + top: 20rpx; + left: 10rpx; +} diff --git a/miniprogram/tcb-shop/style/goodsList.wxss b/miniprogram/tcb-shop/style/goodsList.wxss new file mode 100644 index 0000000..be0fcac --- /dev/null +++ b/miniprogram/tcb-shop/style/goodsList.wxss @@ -0,0 +1,169 @@ +/* 层级定义 +@z-index-0: 1; +@z-index-1: 100; +@z-index-2: 200; +@z-index-5: 500; +@z-index-component: 1000; // 通用组件级别 +@z-index-dropdown: @z-index-component; +@z-index-sticky: @z-index-component + 20; +@z-index-fixed: @z-index-component + 30; +@z-index-modal-backdrop:@z-index-component + 40; +@z-index-modal:@z-index-component + 50; +@z-index-popover:@z-index-component + 60; +@z-index-tooltip:@z-index-component + 70; +*/ +/* var() css变量适配*/ +.goods-list-wrap { + padding-left: 24rpx; + background-color: #fff; +} +.goods-list-wrap .wr-goods-card { + padding: 24rpx 24rpx 24rpx 0; + border-bottom: 1rpx solid #e6e6e6; + background-color: #fff; +} +.goods-list-wrap .wr-goods-card.no-border { + border-bottom: none; +} +.goods-list-wrap .wr-goods-card .wr-goods-card__thumb { + width: 200rpx; + height: 200rpx; + margin-right: 24rpx; + border-radius: 8rpx; + overflow: hidden; +} +.goods-list-wrap .wr-goods-card .wr-goods-card__content { + position: relative; + display: flex; + flex-flow: column nowrap; + justify-content: space-between; + width: 478rpx; + height: 200rpx; +} +.goods-list-wrap .wr-goods-card .wr-goods-card__content .wr-goods-card__title { + overflow: hidden; + margin-bottom: 24rpx; + text-overflow: ellipsis; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + line-clamp: 2; + display: -webkit-box; + font-size: 26rpx; + line-height: 36rpx; + font-weight: 400; +} +.goods-list-wrap .wr-goods-card .wr-goods-card__content .card-index--wr-goods-card__price { + font-size: 32rpx; +} +.goods-list-wrap .wr-goods-card .wr-goods-card__content .card-index--wr-goods-card__price .symbol { + font-size: 24rpx; +} +.goods-list-wrap .wr-goods-card .wr-goods-card__tags { + flex-grow: 2; +} +.goods-list-wrap .wr-goods-card .goods-card-tags-wrap { + color: #fa550f; + color: var(--color-primary, #fa550f); + display: flex; + height: 30rpx; + flex-flow: row wrap; + text-align: center; + width: 100%; + flex-shrink: 0; +} +.goods-list-wrap .wr-goods-card .goods-card-tags-wrap .tag { + box-sizing: border-box; + font-size: 20rpx; + border-radius: 4rpx; + flex-shrink: 0; + vertical-align: middle; + margin-right: 8rpx; + background-color: #fff; +} +.goods-list-wrap .wr-goods-card .goods-card-tags-wrap .tag::after { + border-radius: 4rpx; + border: 2rpx solid #fa550f; + border: 2rpx solid var(--color-primary, #fa550f); +} +.goods-list-wrap .wr-goods-card .goods-add-cart { + display: flex; + justify-content: center; + align-items: center; + position: absolute; + right: 0; + bottom: 0; + width: 48rpx; +} +.goods-list-wrap .wr-goods-card .goods-add-cart .goods-add-cart { + line-height: 48rpx; + height: 48rpx; +} +.goods-list-wrap.vertical { + padding: 20rpx 24rpx; + display: flex; + flex-flow: row wrap; + justify-content: space-between; + background-color: transparent; +} +.goods-list-wrap.vertical .wr-goods-card { + width: 340rpx; + height: 574rpx; + overflow: hidden; + padding: 0; + border-bottom: none; + display: flex; + flex-flow: column nowrap; + border-radius: 8px; + margin-bottom: 24rpx; +} +.goods-list-wrap.vertical .wr-goods-card .wr-goods-card__thumb { + width: 100%; + height: 340rpx; +} +.goods-list-wrap.vertical .wr-goods-card .wr-goods-card__content { + width: 100%; + padding: 20rpx; + overflow: hidden; + height: 234rpx; + box-sizing: border-box; +} +.goods-list-wrap.vertical .wr-goods-card .wr-goods-card__content .wr-goods-card__title { + -webkit-box-orient: horizontal; + -webkit-line-clamp: 1; + line-clamp: 1; + height: 36rpx; + color: #333; + white-space: nowrap; + display: block; +} +.goods-list-wrap.vertical .wr-goods-card .wr-goods-card__content .card-index--wr-goods-card__origin-price { + position: absolute; + left: 20rpx; + bottom: 72rpx; + margin-left: 0; + font-size: 24rpx; + color: #aaaaaa; +} +.goods-list-wrap.vertical .wr-goods-card .wr-goods-card__content .wr-goods-card__groupon-price { + position: absolute; + left: 20rpx; + bottom: 72rpx; + margin-left: 0; + font-size: 24rpx; + color: #aaaaaa; +} +.goods-list-wrap.vertical .wr-goods-card .goods-add-cart { + right: 20rpx; + bottom: 20rpx; +} +.goods-list-wrap.vertical .grouponPrice { + margin-bottom: 50rpx; +} +.goods-list-wrap .wr-goods-card__twoLine .wr-goods-card__title { + -webkit-line-clamp: 2 !important; + line-clamp: 2 !important; + display: -webkit-box !important; + white-space: normal !important; + height: auto !important; + -webkit-box-orient: vertical !important; +} diff --git a/miniprogram/tcb-shop/style/iconfont.wxss b/miniprogram/tcb-shop/style/iconfont.wxss new file mode 100644 index 0000000..23df272 --- /dev/null +++ b/miniprogram/tcb-shop/style/iconfont.wxss @@ -0,0 +1,306 @@ +@font-face { + font-family: 'wr'; + src: url('https://cdn3.codesign.qq.com/icons/gqxWyZ1yMJZmVXk/Yyg5Zp2LG8292lK/iconfont.woff?t=cfc62dd36011e60805f5c3ad1a20b642') + format('woff2'); +} + +.wr { + font-family: 'wr' !important; + font-size: 32rpx; + font-style: normal; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +.wr-deliver:before { + content: '\e033'; +} +.wr-indent_close:before { + content: '\e041'; +} +.wr-edit:before { + content: '\e002'; +} +.wr-succeed:before { + content: '\e00d'; +} +.wr-goods_return:before { + content: '\e03c'; +} +.wr-wallet:before { + content: '\e051'; +} +.wr-package:before { + content: '\e047'; +} +.wr-comment:before { + content: '\e037'; +} +.wr-exchang:before { + content: '\e03e'; +} +.wr-credit_card:before { + content: '\e035'; +} +.wr-service:before { + content: '\e04a'; +} +.wr-shop_bag:before { + content: '\e02a'; +} +.wr-goods_refund:before { + content: '\e03d'; +} +.wr-check:before { + content: '\e053'; +} +.wr-wechat:before { + content: '\e065'; +} +.wr-cartAdd:before { + content: '\e05d'; +} +.wr-home:before { + content: '\e020'; +} +.wr-person:before { + content: '\e02c'; +} +.wr-cart:before { + content: '\e023'; +} +.wr-location:before { + content: '\e016'; +} +.wr-arrow_forward:before { + content: '\e012'; +} +.wr-close:before { + content: '\e021'; +} +.wr-search:before { + content: '\e011'; +} +.wr-clear_filled:before { + content: '\e027'; +} +.wr-arrow_drop_up:before { + content: '\e071'; +} +.wr-arrow_drop_down:before { + content: '\e070'; +} +.wr-filter:before { + content: '\e038'; +} +.wr-copy:before { + content: '\e001'; +} +.wr-arrow_back:before { + content: '\e003'; +} +.wr-add_circle:before { + content: '\e004'; +} +.wr-Download:before { + content: '\e006'; +} +.wr-map:before { + content: '\e007'; +} +.wr-store:before { + content: '\e008'; +} +.wr-movie:before { + content: '\e00a'; +} +.wr-done:before { + content: '\e00b'; +} +.wr-minus:before { + content: '\e00c'; +} +.wr-list:before { + content: '\e00e'; +} +.wr-expand_less:before { + content: '\e00f'; +} +.wr-person_add:before { + content: '\e010'; +} +.wr-Photo:before { + content: '\e013'; +} +.wr-preview:before { + content: '\e014'; +} +.wr-remind:before { + content: '\e015'; +} + +.wr-info:before { + content: '\e017'; +} +.wr-expand_less_s:before { + content: '\e018'; +} +.wr-arrow_forward_s:before { + content: '\e019'; +} +.wr-expand_more_s:before { + content: '\e01a'; +} +.wr-share:before { + content: '\e01d'; +} +.wr-notify:before { + content: '\e01e'; +} +.wr-add:before { + content: '\e01f'; +} +.wr-Home:before { + content: '\e020'; +} +.wr-delete:before { + content: '\e022'; +} +.wr-error:before { + content: '\e025'; +} +.wr-sort:before { + content: '\e028'; +} +.wr-sort_filled:before { + content: '\e029'; +} +.wr-shop_bag_filled:before { + content: '\e02b'; +} + +.wr-person_filled:before { + content: '\e02d'; +} +.wr-cart_filled:before { + content: '\e02e'; +} +.wr-home_filled:before { + content: '\e02f'; +} +.wr-add_outline:before { + content: '\e030'; +} + +.wr-compass:before { + content: '\e034'; +} +.wr-goods_exchange:before { + content: '\e03a'; +} +.wr-group_buy:before { + content: '\e03b'; +} +.wr-group:before { + content: '\e03f'; +} +.wr-indent_goods:before { + content: '\e040'; +} +.wr-help:before { + content: '\e042'; +} +.wr-group_takeout:before { + content: '\e043'; +} +.wr-label:before { + content: '\e044'; +} +.wr-indent_wating:before { + content: '\e045'; +} +.wr-member:before { + content: '\e046'; +} + +.wr-scanning:before { + content: '\e04b'; +} +.wr-tv:before { + content: '\e04d'; +} +.wr-to_top:before { + content: '\e04f'; +} +.wr-visibility_off:before { + content: '\e050'; +} +.wr-error-1:before { + content: '\e052'; +} + +.wr-arrow_right:before { + content: '\e054'; +} +.wr-arrow_left:before { + content: '\e056'; +} +.wr-picture_filled:before { + content: '\e057'; +} +.wr-navigation:before { + content: '\e058'; +} +.wr-telephone:before { + content: '\e059'; +} +.wr-indent_time:before { + content: '\e05c'; +} +.wr-cart_add:before { + content: '\e05d'; +} +.wr-classify:before { + content: '\e060'; +} +.wr-place:before { + content: '\e063'; +} +.wr-wechat_pay:before { + content: '\e064'; +} +.wr-security:before { + content: '\e066'; +} +.wr-alarm:before { + content: '\e067'; +} +.wr-person-1:before { + content: '\e068'; +} +.wr-open_in_new:before { + content: '\e069'; +} +.wr-uncheck:before { + content: '\e06b'; +} +.wr-thumb_up:before { + content: '\e06c'; +} +.wr-thumb_up_filled:before { + content: '\e06d'; +} +.wr-star:before { + content: '\e06e'; +} +.wr-star_filled:before { + content: '\e06f'; +} +.wr-cards:before { + content: '\e072'; +} +.wr-picture_error_filled:before { + content: '\e076'; +} +.wr-discount:before { + content: '\e077'; +} diff --git a/miniprogram/tcb-shop/style/theme.wxss b/miniprogram/tcb-shop/style/theme.wxss new file mode 100644 index 0000000..bb1249e --- /dev/null +++ b/miniprogram/tcb-shop/style/theme.wxss @@ -0,0 +1,47 @@ +/* 主题定制 */ +.t-input { + --td-input-placeholder-text-color: #bbbbbb; + --td-input-text-color: #333333; +} + +.t-tab-bar { + --td-tab-bar-color: #bbb; + --td-tab-bar-active-color: #333; +} + +.t-cascader { + --td-cascader-active-color: #fa4126; +} + +.t-switch { + --td-switch-checked-color: #34c759; +} + +.t-button { + --td-button-font-weight: 500; + --td-button-medium-font-size: 32rpx; + --td-button-default-color: #fff; + --td-button-default-bg-color: #fa4126; + --td-button-default-border-color: #fa4126; + --td-button-default-disabled-color: #fff; + --td-button-default-disabled-bg: #cccccc; + --td-button-default-disabled-border-color: #cccccc; + --td-button-default-active-bg-color: #fa4126; + --td-button-default-active-border-color: #fa4126; +} + +.t-textarea { + --td-textarea-placeholder-color: #bbb; +} + +.t-checkbox { + --td-checkbox-icon-checked-color: #fa4126; +} + +.dialog__button-confirm { + color: #fa4126 !important; +} + +.dialog__button-cancel { + color: #aeb3b7 !important; +} diff --git a/miniprogram/tcb-shop/utils/addressListFresh.js b/miniprogram/tcb-shop/utils/addressListFresh.js new file mode 100644 index 0000000..b643493 --- /dev/null +++ b/miniprogram/tcb-shop/utils/addressListFresh.js @@ -0,0 +1,3 @@ +export let shouldFresh = false; +export const addressListShouldFresh = () => (shouldFresh = true); +export const addressListFinishFresh = () => (shouldFresh = false); diff --git a/miniprogram/tcb-shop/utils/cartFresh.js b/miniprogram/tcb-shop/utils/cartFresh.js new file mode 100644 index 0000000..68e13a4 --- /dev/null +++ b/miniprogram/tcb-shop/utils/cartFresh.js @@ -0,0 +1,3 @@ +export let shouldFresh = false; +export const cartShouldFresh = () => (shouldFresh = true); +export const cartFinishFresh = () => (shouldFresh = false); diff --git a/miniprogram/tcb-shop/utils/cloudImageHandler.js b/miniprogram/tcb-shop/utils/cloudImageHandler.js new file mode 100644 index 0000000..94b2f95 --- /dev/null +++ b/miniprogram/tcb-shop/utils/cloudImageHandler.js @@ -0,0 +1,30 @@ +export async function getCloudImageTempUrl(images) { + const handledIndexToOriginIndex = new Map(); + const shouldBeHandledImages = []; + images.forEach((x, index) => { + if (!x.startsWith('cloud')) return; + + const handleIndex = shouldBeHandledImages.length; + shouldBeHandledImages.push(x); + handledIndexToOriginIndex.set(handleIndex, index); + }); + + const handledImages = ( + await wx.cloud.getTempFileURL({ + fileList: shouldBeHandledImages, + }) + ).fileList.map((x) => x.tempFileURL); + + const ret = [...images]; + handledImages.forEach((image, handleIndex) => (ret[handledIndexToOriginIndex.get(handleIndex)] = image)); + + return ret; +} + +export async function getSingleCloudImageTempUrl(image) { + return ( + await wx.cloud.getTempFileURL({ + fileList: [image], + }) + ).fileList[0].tempFileURL; +} diff --git a/miniprogram/tcb-shop/utils/getPermission.js b/miniprogram/tcb-shop/utils/getPermission.js new file mode 100644 index 0000000..e6a64cb --- /dev/null +++ b/miniprogram/tcb-shop/utils/getPermission.js @@ -0,0 +1,45 @@ +const getPermission = ({ code, name }) => { + return new Promise((resolve, reject) => { + wx.getSetting({ + success: (res) => { + if (res.authSetting[code] === false) { + wx.showModal({ + title: `获取${name}失败`, + content: `获取${name}失败,请在【右上角】-小程序【设置】项中,将【${name}】开启。`, + confirmText: '去设置', + confirmColor: '#FA550F', + cancelColor: '取消', + success(res) { + if (res.confirm) { + wx.openSetting({ + success(settinRes) { + if (settinRes.authSetting[code] === true) { + resolve(); + } else { + console.warn('用户未打开权限', name, code); + reject(); + } + }, + }); + } else { + reject(); + } + }, + fail() { + reject(); + }, + }); + } else { + resolve(); + } + }, + fail() { + reject(); + }, + }); + }); +}; + +module.exports = { + getPermission, +}; diff --git a/miniprogram/tcb-shop/utils/listLoading.js b/miniprogram/tcb-shop/utils/listLoading.js new file mode 100644 index 0000000..10d16e6 --- /dev/null +++ b/miniprogram/tcb-shop/utils/listLoading.js @@ -0,0 +1,9 @@ +/** + * Should be used with `components/load-more`. + */ +export const LIST_LOADING_STATUS = { + READY: 0, + LOADING: 1, + NO_MORE: 2, + FAILED: 3, +}; diff --git a/miniprogram/tcb-shop/utils/mock.js b/miniprogram/tcb-shop/utils/mock.js new file mode 100644 index 0000000..27f13e8 --- /dev/null +++ b/miniprogram/tcb-shop/utils/mock.js @@ -0,0 +1,51 @@ +/** + * 随机打散字符串 + * @param {number} n 长度 + * @param {string} str 字符串 + * @returns + */ +function generateMixed(n, str) { + var res = ''; + for (var i = 0; i < n; i++) { + var id = Math.ceil(Math.random() * 35); + res += str[id]; + } + return res; +} + +/** + * 生成随机数 + * @param {number} min 最小值 + * @param {number} max 最大值 + * @returns + */ +function getRandomNum(min, max) { + var range = max - min; + var rand = Math.random(); + return min + Math.round(rand * range); +} + +/** + * 生成随机IP + * @returns + */ +function mockIp() { + return `10.${getRandomNum(1, 254)}.${getRandomNum(1, 254)}.${getRandomNum( + 1, + 254, + )}`; +} + +function mockReqId() { + return `${getRandomNum(100000, 999999)}.${new Date().valueOf()}${getRandomNum( + 1000, + 9999, + )}.${getRandomNum(10000000, 99999999)}`; +} + +module.exports = { + generateMixed, + mockIp, + mockReqId, + getRandomNum, +}; diff --git a/miniprogram/tcb-shop/utils/orderListFresh.js b/miniprogram/tcb-shop/utils/orderListFresh.js new file mode 100644 index 0000000..3195d87 --- /dev/null +++ b/miniprogram/tcb-shop/utils/orderListFresh.js @@ -0,0 +1,3 @@ +export let shouldFresh = false; +export const orderListShouldFresh = () => (shouldFresh = true); +export const orderListFinishFresh = () => (shouldFresh = false); diff --git a/miniprogram/tcb-shop/utils/orderOperation.js b/miniprogram/tcb-shop/utils/orderOperation.js new file mode 100644 index 0000000..1db5146 --- /dev/null +++ b/miniprogram/tcb-shop/utils/orderOperation.js @@ -0,0 +1,5 @@ +export const OPERATION_TYPE = { + CANCEL: 'cancel', + CONFIRM: 'confirm', + PAY: 'pay', +}; diff --git a/miniprogram/tcb-shop/utils/spuStatus.js b/miniprogram/tcb-shop/utils/spuStatus.js new file mode 100644 index 0000000..40ae401 --- /dev/null +++ b/miniprogram/tcb-shop/utils/spuStatus.js @@ -0,0 +1 @@ +export const SPU_SELLING_STATUS = 'ENABLED'; diff --git a/miniprogram/tcb-shop/utils/util.js b/miniprogram/tcb-shop/utils/util.js new file mode 100644 index 0000000..eb004b6 --- /dev/null +++ b/miniprogram/tcb-shop/utils/util.js @@ -0,0 +1,135 @@ +import dayjs from 'dayjs'; + +const formatTime = (date, template) => dayjs(date).format(template); + +/** + * 格式化价格数额为字符串 + * 可对小数部分进行填充,默认不填充 + * @param price 价格数额,以分为单位! + * @param fill 是否填充小数部分 0-不填充 1-填充第一位小数 2-填充两位小数 + */ +function priceFormat(price, fill = 0) { + if (isNaN(price) || price === null || price === Infinity) { + return price; + } + + let priceFormatValue = Math.round(parseFloat(`${price}`) * 10 ** 8) / 10 ** 8; // 恢复精度丢失 + priceFormatValue = `${Math.ceil(priceFormatValue) / 100}`; // 向上取整,单位转换为元,转换为字符串 + if (fill > 0) { + // 补充小数位数 + if (priceFormatValue.indexOf('.') === -1) { + priceFormatValue = `${priceFormatValue}.`; + } + const n = fill - priceFormatValue.split('.')[1]?.length; + for (let i = 0; i < n; i++) { + priceFormatValue = `${priceFormatValue}0`; + } + } + return priceFormatValue; +} + +/** + * 获取cdn裁剪后链接 + * + * @param {string} url 基础链接 + * @param {number} width 宽度,单位px + * @param {number} [height] 可选,高度,不填时与width同值 + */ +const cosThumb = (url, width, height = width) => { + if (url.indexOf('?') > -1) { + return url; + } + + if (url.indexOf('http://') === 0) { + url = url.replace('http://', 'https://'); + } + + return `${url}?imageMogr2/thumbnail/${~~width}x${~~height}`; +}; + +const get = (source, paths, defaultValue) => { + if (typeof paths === 'string') { + paths = paths.replace(/\[/g, '.').replace(/\]/g, '').split('.').filter(Boolean); + } + const { length } = paths; + let index = 0; + while (source != null && index < length) { + source = source[paths[index++]]; + } + return source === undefined || index === 0 ? defaultValue : source; +}; +let systemWidth = 0; +/** 获取系统宽度,为了减少启动消耗所以在函数里边做初始化 */ +export const loadSystemWidth = () => { + if (systemWidth) { + return systemWidth; + } + + try { + ({ screenWidth: systemWidth, pixelRatio } = wx.getSystemInfoSync()); + } catch (e) { + systemWidth = 0; + } + return systemWidth; +}; + +/** + * 转换rpx为px + * + * @description + * 什么时候用? + * - 布局(width: 172rpx)已经写好, 某些组件只接受px作为style或者prop指定 + * + */ +const rpx2px = (rpx, round = false) => { + loadSystemWidth(); + + // px / systemWidth = rpx / 750 + const result = (rpx * systemWidth) / 750; + + if (round) { + return Math.floor(result); + } + + return result; +}; + +/** + * 手机号码*加密函数 + * @param {string} phone 电话号 + * @returns + */ +const phoneEncryption = (phone) => { + return phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2'); +}; + +// 内置手机号正则字符串 +const innerPhoneReg = '^1(?:3\\d|4[4-9]|5[0-35-9]|6[67]|7[0-8]|8\\d|9\\d)\\d{8}$'; + +/** + * 手机号正则校验 + * @param phone 手机号 + * @param phoneReg 正则字符串 + * @returns true - 校验通过 false - 校验失败 + */ +const phoneRegCheck = (phone) => { + const phoneRegExp = new RegExp(innerPhoneReg); + return phoneRegExp.test(phone); +}; + +function objectToParamString(obj) { + return Object.entries(obj) + .map(([key, val]) => `${key}=${val}`) + .join('&'); +} + +module.exports = { + formatTime, + priceFormat, + cosThumb, + get, + rpx2px, + phoneEncryption, + phoneRegCheck, + objectToParamString, +};