Browse Source

根据模板初始化项目

yuxingxing 2 years ago
commit
347dcd2acb
100 changed files with 26575 additions and 0 deletions
  1. 1 0
      .gitignore
  2. 16 0
      .hbuilderx/launch.json
  3. 15 0
      App.vue
  4. 1008 0
      components/shopro-auth-modal/shopro-auth-modal.vue
  5. 123 0
      components/shopro-canvas/shopro-canvas.vue
  6. 176 0
      components/shopro-coupon/shopro-coupon.vue
  7. 77 0
      components/shopro-empty/shopro-empty.vue
  8. 184 0
      components/shopro-float-btn/shopro-float-btn.vue
  9. 348 0
      components/shopro-goods-card/shopro-goods-card.vue
  10. 361 0
      components/shopro-live-card/shopro-live-card.vue
  11. 130 0
      components/shopro-mini-card/shopro-mini-card.vue
  12. 130 0
      components/shopro-navbar/shopro-navbar.vue
  13. 71 0
      components/shopro-notice-modal/shopro-notice-modal.vue
  14. 571 0
      components/shopro-share/shopro-share.vue
  15. 589 0
      components/shopro-sku/shopro-sku.vue
  16. 142 0
      components/shopro-tabbar/shopro-tabbar.vue
  17. 9 0
      env.js
  18. 33 0
      main.js
  19. 232 0
      manifest.json
  20. 220 0
      package-lock.json
  21. 24 0
      package.json
  22. 921 0
      pages.json
  23. 227 0
      pages/activity/discounts/list.vue
  24. 371 0
      pages/activity/groupon/detail.vue
  25. 246 0
      pages/activity/groupon/list.vue
  26. 247 0
      pages/activity/groupon/my-groupon.vue
  27. 248 0
      pages/activity/seckill/list.vue
  28. 361 0
      pages/activity/sign/index.vue
  29. 267 0
      pages/app/commission/apply.vue
  30. 393 0
      pages/app/commission/commission-log.vue
  31. 167 0
      pages/app/commission/goods.vue
  32. 855 0
      pages/app/commission/index.vue
  33. 497 0
      pages/app/commission/order.vue
  34. 148 0
      pages/app/commission/rankings.vue
  35. 255 0
      pages/app/commission/share-log.vue
  36. 344 0
      pages/app/commission/team.vue
  37. 151 0
      pages/app/components/sh-collapse-item.vue
  38. 247 0
      pages/app/coupon/detail.vue
  39. 176 0
      pages/app/coupon/list.vue
  40. 216 0
      pages/app/live/list.vue
  41. 613 0
      pages/app/merchant/apply.vue
  42. 227 0
      pages/app/merchant/detail.vue
  43. 617 0
      pages/app/merchant/index.vue
  44. 115 0
      pages/app/merchant/info.vue
  45. 116 0
      pages/app/merchant/list.vue
  46. 105 0
      pages/app/score/list.vue
  47. 208 0
      pages/goods/comment/add-comment.vue
  48. 126 0
      pages/goods/comment/comment-list.vue
  49. 157 0
      pages/goods/components/sh-activity.vue
  50. 100 0
      pages/goods/components/sh-comment.vue
  51. 75 0
      pages/goods/components/sh-coupon.vue
  52. 168 0
      pages/goods/components/sh-filter.vue
  53. 220 0
      pages/goods/components/sh-groupon.vue
  54. 377 0
      pages/goods/components/sh-price-card.vue
  55. 140 0
      pages/goods/components/sh-serve.vue
  56. 772 0
      pages/goods/detail.vue
  57. 224 0
      pages/goods/list.vue
  58. 371 0
      pages/index/cart.vue
  59. 73 0
      pages/index/category.vue
  60. 265 0
      pages/index/category/one-catgory.vue
  61. 603 0
      pages/index/category/takeout-catgory.vue
  62. 209 0
      pages/index/category/three-catgory.vue
  63. 213 0
      pages/index/category/two-catgory.vue
  64. 174 0
      pages/index/components/sh-adv.vue
  65. 69 0
      pages/index/components/sh-banner.vue
  66. 348 0
      pages/index/components/sh-category-tabs.vue
  67. 56 0
      pages/index/components/sh-cell.vue
  68. 276 0
      pages/index/components/sh-coupon.vue
  69. 356 0
      pages/index/components/sh-goods-card.vue
  70. 134 0
      pages/index/components/sh-grid-swiper.vue
  71. 60 0
      pages/index/components/sh-grid.vue
  72. 319 0
      pages/index/components/sh-groupon.vue
  73. 227 0
      pages/index/components/sh-hot-goods.vue
  74. 108 0
      pages/index/components/sh-live.vue
  75. 150 0
      pages/index/components/sh-order-card.vue
  76. 46 0
      pages/index/components/sh-richtext.vue
  77. 46 0
      pages/index/components/sh-search.vue
  78. 300 0
      pages/index/components/sh-seckill.vue
  79. 64 0
      pages/index/components/sh-title-card.vue
  80. 106 0
      pages/index/components/sh-wallet.vue
  81. 189 0
      pages/index/index.vue
  82. 173 0
      pages/index/index/home-head.vue
  83. 116 0
      pages/index/index/privacy-modal.vue
  84. 143 0
      pages/index/user.vue
  85. 316 0
      pages/index/user/userinfo-card.vue
  86. 182 0
      pages/index/view.vue
  87. 329 0
      pages/order/after-sale/detail.vue
  88. 329 0
      pages/order/after-sale/list.vue
  89. 96 0
      pages/order/after-sale/log.vue
  90. 430 0
      pages/order/after-sale/refund.vue
  91. 116 0
      pages/order/components/sh-select-coupon.vue
  92. 1651 0
      pages/order/confirm.vue
  93. 733 0
      pages/order/detail.vue
  94. 411 0
      pages/order/express/distribution-detail.vue
  95. 217 0
      pages/order/express/express-detail.vue
  96. 114 0
      pages/order/express/express-list.vue
  97. 351 0
      pages/order/express/store-address.vue
  98. 407 0
      pages/order/list.vue
  99. 255 0
      pages/order/payment/method.vue
  100. 287 0
      pages/order/payment/result.vue

+ 1 - 0
.gitignore

@@ -0,0 +1 @@
+node_modules

+ 16 - 0
.hbuilderx/launch.json

@@ -0,0 +1,16 @@
+{ // launch.json 配置了启动调试时相关设置,configurations下节点名称可为 app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/
+  // launchtype项可配置值为local或remote, local代表前端连本地云函数,remote代表前端连云端云函数
+    "version": "0.0",
+    "configurations": [{
+     	"default" : 
+     	{
+     		"launchtype" : "local"
+     	},
+     	"mp-weixin" : 
+     	{
+     		"launchtype" : "local"
+     	},
+     	"type" : "uniCloud"
+     }
+    ]
+}

+ 15 - 0
App.vue

@@ -0,0 +1,15 @@
+
+<script>
+	import {
+		init
+	} from "@/shopro";
+	export default {
+		onLaunch(options) {
+			init(options);
+		}
+	};
+</script>
+
+<style lang="scss">
+	@import 'static/style/index.scss';
+</style>

+ 1008 - 0
components/shopro-auth-modal/shopro-auth-modal.vue

@@ -0,0 +1,1008 @@
+<template>
+	<view class="cu-modal bottom-modal" :class="{ show: showAuth }" @tap="closeAuthModal" style="z-index: 10080;">
+		<view class="login-wrap cu-dialog form-wrap  safe-area-inset-bottom" @tap.stop style="border-radius: 20rpx 20rpx 0 0;">
+			<!-- 1.账号密码登录 -->
+			<view v-if="authType === 'accountLogin'">
+				<!-- 标题 -->
+				<view class="head-box u-m-b-60 u-flex-col ">
+					<view class="u-flex u-m-b-20">
+						<view class="head-title u-m-r-40 head-title-animation">账号登录</view>
+						<view class="head-title-active head-title-line" @tap="showAuthModal('smsLogin')">短信登录</view>
+					</view>
+					<view class="head-subtitle">如果未设置过密码,请点击忘记密码</view>
+				</view>
+				<view class="form-item  u-border-bottom ">
+					<view class="item-content u-flex u-col-center">
+						<view class="item-title">账号</view>
+						<u-input
+							class="u-m-r-10 u-flex-1"
+							placeholder="请输入账号"
+							@blur="checkValue($event, 'account')"
+							@input="checkValue($event, 'account')"
+							:placeholderStyle="placeholderStyle"
+							v-model="form['accountLogin'].data.account"
+							type="text"
+						></u-input>
+						<button class="u-reset-button forgot-btn" @tap="showAuthModal('forgotPwd')">忘记密码</button>
+					</view>
+					<view class="message-error">{{ form['accountLogin'].error.account || '' }}</view>
+				</view>
+				<view class="form-item u-border-bottom">
+					<view class="item-content u-flex u-col-center">
+						<view class="item-title">密码</view>
+						<u-input
+							class="u-m-r-10 u-flex-1"
+							placeholder="请输入密码"
+							:placeholderStyle="placeholderStyle"
+							v-model="form['accountLogin'].data.password"
+							type="password"
+							@blur="checkValue($event, 'password')"
+							@input="checkValue($event, 'password')"
+						></u-input>
+						<button class="u-reset-button login-btn-start" @tap="accountLoginSubmit">登录</button>
+					</view>
+					<view class="message-error">{{ form['accountLogin'].error.password || '' }}</view>
+				</view>
+				<button class="u-reset-button type-btn" @tap="showAuthModal('register')">立即注册</button>
+			</view>
+
+			<!-- 2.短信登录 -->
+			<view v-if="authType === 'smsLogin'">
+				<view class="head-box u-m-b-60">
+					<view class="u-flex u-m-b-20">
+						<view class="head-title-active u-m-r-40" @tap="showAuthModal('accountLogin')">账号登录</view>
+						<view class="head-title head-title-line head-title-animation">短信登录</view>
+					</view>
+					<view class="head-subtitle">未注册手机号请先点击下方立即注册</view>
+				</view>
+				<view class="form-item u-border-bottom">
+					<view class="item-content u-flex u-col-center">
+						<view class="item-title">手机号</view>
+						<u-input
+							class="u-m-r-10 u-flex-1"
+							placeholder="请输入手机号"
+							@blur="checkValue($event, 'mobile')"
+							@input="mobileInput($event, 'mobile')"
+							:placeholderStyle="placeholderStyle"
+							v-model="form['smsLogin'].data.mobile"
+							type="number"
+						></u-input>
+						<button
+							class="u-reset-button code-btn code-btn-start"
+							:disabled="!form['smsLogin'].data.isMobileEnd"
+							:class="{ 'code-btn-end': form['smsLogin'].data.isMobileEnd }"
+							@tap="getSmsCode('mobilelogin')"
+						>
+							{{ codeText }}
+						</button>
+					</view>
+					<view class="message-error">{{ form['smsLogin'].error.mobile || '' }}</view>
+				</view>
+				<view class="form-item u-border-bottom">
+					<view class="item-content u-flex u-col-center">
+						<view class="item-title">验证码</view>
+						<u-input
+							class="u-m-r-10 u-flex-1"
+							@blur="checkValue($event, 'code')"
+							@input="checkValue($event, 'code')"
+							placeholder="请输入验证码"
+							:placeholderStyle="placeholderStyle"
+							v-model="form['smsLogin'].data.code"
+							type="number"
+						></u-input>
+						<button class="u-reset-button login-btn-start" @tap="smsLoginSubmit">登录</button>
+					</view>
+					<view class="message-error">{{ form['smsLogin'].error.code || '' }}</view>
+				</view>
+				<button class="u-reset-button type-btn" @tap="showAuthModal('register')">立即注册</button>
+			</view>
+
+			<!-- 3.注册 -->
+			<view v-if="authType === 'register'">
+				<view class="head-box u-m-b-60">
+					<view class="head-title u-m-b-20">注册</view>
+					<view class="head-subtitle">请使用本人手机号完成注册</view>
+				</view>
+				<view class="form-item u-border-bottom">
+					<view class="item-content u-flex u-col-center">
+						<view class="item-title">手机号</view>
+						<u-input
+							class="u-m-r-10 u-flex-1"
+							placeholder="请输入手机号"
+							@blur="checkValue($event, 'mobile')"
+							@input="mobileInput($event, 'mobile')"
+							:placeholderStyle="placeholderStyle"
+							v-model="form['register'].data.mobile"
+							type="number"
+						></u-input>
+						<button
+							class="u-reset-button code-btn code-btn-start"
+							:disabled="!form['register'].data.isMobileEnd"
+							:class="{ 'code-btn-end': form['register'].data.isMobileEnd }"
+							@tap="getSmsCode('register')"
+						>
+							{{ codeText }}
+						</button>
+					</view>
+					<view class="message-error">{{ form['register'].error.mobile || '' }}</view>
+				</view>
+				<view class="form-item u-border-bottom">
+					<view class="item-content u-flex u-col-center">
+						<view class="item-title">密码</view>
+						<u-input
+							class="u-m-r-10 u-flex-1"
+							@blur="checkValue($event, 'password')"
+							@input="checkValue($event, 'password')"
+							placeholder="请输入密码"
+							:placeholderStyle="placeholderStyle"
+							v-model="form['register'].data.password"
+							type="password"
+						></u-input>
+					</view>
+					<view class="message-error">{{ form['register'].error.password || '' }}</view>
+				</view>
+				<view class="form-item u-border-bottom">
+					<view class="item-content u-flex u-col-center">
+						<view class="item-title">验证码</view>
+						<u-input
+							class="u-m-r-10 u-flex-1"
+							@blur="checkValue($event, 'code')"
+							@input="checkValue($event, 'code')"
+							placeholder="请输入验证码"
+							:placeholderStyle="placeholderStyle"
+							v-model="form['register'].data.code"
+							type="number"
+						></u-input>
+						<button class="u-reset-button login-btn-start" @tap="registerSubmit">注册</button>
+					</view>
+					<view class="message-error">{{ form['register'].error.code || '' }}</view>
+				</view>
+				<button v-if="!isLogin" class="u-reset-button type-btn" @tap="showAuthModal('accountLogin')">返回登录</button>
+			</view>
+
+			<!-- 4.忘记密码 -->
+			<view v-if="authType === 'forgotPwd'">
+				<view class="head-box u-m-b-60">
+					<view class="head-title u-m-b-20">忘记密码</view>
+					<view class="head-subtitle">为了您的账号安全,修改密码前请先进行安全验证</view>
+				</view>
+				<view class="form-item u-border-bottom">
+					<view class="item-content u-flex u-col-center">
+						<view class="item-title">手机号</view>
+						<u-input
+							class="u-m-r-10 u-flex-1"
+							@blur="checkValue($event, 'mobile')"
+							@input="mobileInput($event, 'mobile')"
+							:placeholderStyle="placeholderStyle"
+							v-model="form['forgotPwd'].data.mobile"
+							type="number"
+						></u-input>
+						<button
+							class="u-reset-button code-btn code-btn-start"
+							:disabled="!form['forgotPwd'].data.isMobileEnd"
+							:class="{ 'code-btn-end': form['forgotPwd'].data.isMobileEnd }"
+							@tap="getSmsCode('resetpwd')"
+						>
+							{{ codeText }}
+						</button>
+					</view>
+					<view class="message-error">{{ form['forgotPwd'].error.mobile || '' }}</view>
+				</view>
+
+				<view class="form-item u-border-bottom">
+					<view class="item-content u-flex u-col-center">
+						<view class="item-title">验证码</view>
+						<u-input
+							class="u-m-r-10 u-flex-1"
+							@blur="checkValue($event, 'code')"
+							@input="checkValue($event, 'code')"
+							placeholder="请输入验证码"
+							:placeholderStyle="placeholderStyle"
+							v-model="form['forgotPwd'].data.code"
+							type="number"
+						></u-input>
+					</view>
+					<view class="message-error">{{ form['forgotPwd'].error.code || '' }}</view>
+				</view>
+
+				<view class="form-item u-border-bottom">
+					<view class="item-content u-flex u-col-center">
+						<view class="item-title">密码</view>
+						<u-input
+							class="u-m-r-10 u-flex-1"
+							@blur="checkValue($event, 'password')"
+							@input="checkValue($event, 'password')"
+							placeholder="请输入密码"
+							:placeholderStyle="placeholderStyle"
+							v-model="form['forgotPwd'].data.password"
+							type="password"
+						></u-input>
+						<button class="u-reset-button login-btn-start" @tap="forgotPwdSubmit">确认</button>
+					</view>
+					<view class="message-error">{{ form['forgotPwd'].error.password || '' }}</view>
+				</view>
+				<button v-if="!isLogin" class="u-reset-button type-btn" @tap="showAuthModal('accountLogin')">返回登录</button>
+			</view>
+
+			<!-- 5.绑定手机号 -->
+			<view v-if="authType === 'bindMobile'">
+				<view class="head-box u-m-b-60">
+					<view class="head-title u-m-b-20">绑定手机号</view>
+					<view class="head-subtitle">为了您的账号安全,请绑定手机号</view>
+				</view>
+				<view class="form-item u-border-bottom">
+					<view class="item-content u-flex u-col-center">
+						<view class="item-title">手机号</view>
+						<u-input
+							class="u-m-r-10 u-flex-1"
+							@blur="checkValue($event, 'mobile')"
+							@input="mobileInput($event, 'mobile')"
+							placeholder="请输入手机号"
+							:placeholderStyle="placeholderStyle"
+							v-model="form['bindMobile'].data.mobile"
+							type="number"
+						></u-input>
+						<button
+							class="u-reset-button code-btn code-btn-start"
+							:disabled="!form['bindMobile'].data.isMobileEnd"
+							:class="{ 'code-btn-end': form['bindMobile'].data.isMobileEnd }"
+							@tap="getSmsCode('changemobile')"
+						>
+							{{ codeText }}
+						</button>
+					</view>
+					<view class="message-error">{{ form['bindMobile'].error.mobile || '' }}</view>
+				</view>
+				<view class="form-item u-border-bottom">
+					<view class="item-content u-flex u-col-center">
+						<view class="item-title">验证码</view>
+						<u-input
+							class="u-m-r-10 u-flex-1"
+							@blur="checkValue($event, 'code')"
+							@input="checkValue($event, 'code')"
+							placeholder="请输入验证码"
+							:placeholderStyle="placeholderStyle"
+							v-model="form['bindMobile'].data.code"
+							type="number"
+						></u-input>
+						<button class="u-reset-button login-btn-start" @tap="bindMobileSubmit">立即绑定</button>
+					</view>
+					<view class="message-error">{{ form['bindMobile'].error.code || '' }}</view>
+				</view>
+			</view>
+
+			<!-- 6.修改密码 -->
+			<view v-if="authType === 'changePwd'">
+				<view class="head-box u-m-b-60">
+					<view class="head-title u-m-b-20">修改密码</view>
+					<view class="head-subtitle"></view>
+				</view>
+				<view class="form-item u-border-bottom">
+					<view class="item-content u-flex u-col-center">
+						<view class="item-title">旧密码</view>
+						<u-input
+							class="u-m-r-10 u-flex-1"
+							@blur="checkValue($event, 'oldPassword')"
+							@input="checkValue($event, 'oldPassword')"
+							placeholder="请输入旧密码"
+							:placeholderStyle="placeholderStyle"
+							v-model="form['changePwd'].data.oldPassword"
+							type="password"
+						></u-input>
+					</view>
+					<view class="message-error">{{ form['changePwd'].error.oldPassword || '' }}</view>
+				</view>
+				<view class="form-item u-border-bottom">
+					<view class="item-content u-flex u-col-center">
+						<view class="item-title">新密码</view>
+						<u-input
+							class="u-m-r-10 u-flex-1"
+							@blur="checkValue($event, 'newPassword')"
+							@input="checkValue($event, 'newPassword')"
+							placeholder="请输入新密码"
+							:placeholderStyle="placeholderStyle"
+							v-model="form['changePwd'].data.newPassword"
+							type="password"
+						></u-input>
+					</view>
+					<view class="message-error">{{ form['changePwd'].error.newPassword || '' }}</view>
+				</view>
+				<view class="form-item u-border-bottom">
+					<view class="item-content u-flex u-col-center">
+						<view class="item-title">确认密码</view>
+						<u-input
+							class="u-m-r-10 u-flex-1"
+							@blur="checkValue($event, 'reNewPassword')"
+							@input="checkValue($event, 'reNewPassword')"
+							placeholder="再次输入新密码"
+							:placeholderStyle="placeholderStyle"
+							v-model="form['changePwd'].data.reNewPassword"
+							type="password"
+						></u-input>
+					</view>
+					<view class="message-error">{{ form['changePwd'].error.reNewPassword || '' }}</view>
+				</view>
+				<view class="editPwd-btn-box u-m-t-80">
+					<button class="u-reset-button save-btn" @tap="changePwdSubmit">保存</button>
+					<button class="u-reset-button forgot-btn" @tap="showAuthModal('forgotPwd')">忘记密码</button>
+				</view>
+			</view>
+
+			<!-- 第三方登录 -->
+			<view v-if="authType === 'accountLogin' || authType === 'smsLogin'" class="auto-login-box u-flex u-row-center u-col-center">
+				<!-- 微信 -->
+				<image
+					v-if="['App', 'wxOfficialAccount', 'wxMiniProgram'].includes(platform)"
+					class="auto-login-img"
+					@tap="thirdLogin('wechat')"
+					:src="$IMG_URL + '/imgs/auto_login_wx.png'"
+				></image>
+				<!-- 支付宝 -->
+				<!-- <image
+					v-if="['App', 'alipayMiniProgram', 'H5'].includes(platform)"
+					class="auto-login-img"
+					@tap="thirdLogin('alipay')"
+					:src="$IMG_URL + '/imgs/auto_login_ali.png'"
+					mode=""
+				></image> -->
+				<!-- 苹果 -->
+				<!-- #ifdef APP-PLUS -->
+				<image v-if="device === 'ios'" class="auto-login-img" @tap="thirdLogin('apple')" :src="$IMG_URL + '/imgs/auto_login_iphone.png'"></image>
+				<!-- #endif -->
+			</view>
+
+			<!-- 协议 -->
+			<view v-if="['accountLogin', 'smsLogin', 'register'].includes(authType)" class="agreement-box u-flex u-row-center">
+				<u-checkbox v-model="protocol" shape="circle" active-color="#E9B461">
+					<view class="agreement-text tcp-text u-flex u-col-center">
+						我已阅读并遵守
+						<view class="tcp-text u-flex u-col-center">
+							<view @tap.stop="$Router.push({ path: '/pages/public/richtext', query: { id: initShop.user_protocol || 0 } })">《用户协议》</view>
+							与
+							<view @tap.stop="$Router.push({ path: '/pages/public/richtext', query: { id: initShop.privacy_protocol || 0 } })">《隐私协议》</view>
+						</view>
+					</view>
+				</u-checkbox>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+/**
+ * 登录提示页
+ */
+import FormValidate from '@/shopro/validate/form';
+import schema from '@/uview-ui/libs/util/async-validator';
+import wechat from '@/shopro/wechat/wechat';
+import { mapMutations, mapActions, mapState, mapGetters } from 'vuex';
+// #ifdef APP-PLUS
+import apple from '@/shopro/apple';
+// #endif
+
+export default {
+	name: 'shoproAuthModal',
+	data() {
+		return {
+			platform: this.$platform.get(),
+			device: this.$platform.device(),
+			form: {
+				// 1.账号密码登录
+				accountLogin: {
+					data: {
+						account: '', // 账号
+						password: '' // 密码
+					},
+					rules: {
+						account: FormValidate.account,
+						password: FormValidate.password
+					},
+					error: {
+						account: '',
+						password: ''
+					}
+				},
+
+				// 2.短信登录
+				smsLogin: {
+					data: {
+						mobile: '', // 手机号
+						code: '', // 验证码
+						isMobileEnd: false // 手机号输入完毕
+					},
+					rules: {
+						code: FormValidate.code,
+						mobile: FormValidate.mobile
+					},
+					error: {
+						mobile: '', // 手机号
+						code: '' // 验证码
+					}
+				},
+				// 3.注册
+				register: {
+					data: {
+						mobile: '', // 手机号
+						code: '', // 验证码
+						password: '', // 密码
+						isMobileEnd: false // 手机号输入完毕
+					},
+					rules: {
+						code: FormValidate.code,
+						mobile: FormValidate.mobile,
+						password: FormValidate.password
+					},
+					error: {
+						mobile: '', // 手机号
+						code: '', // 验证码
+						password: '' // 密码
+					}
+				},
+
+				// 4.忘记密码
+				forgotPwd: {
+					data: {
+						mobile: '', //手机号
+						code: '', //验证码
+						password: '', //密码
+						isMobileEnd: false // 手机号输入完毕
+					},
+					rules: {
+						mobile: FormValidate.mobile,
+						code: FormValidate.code,
+						password: FormValidate.password
+					},
+					error: {
+						mobile: '', //手机号
+						code: '', //验证码
+						password: '' //密码
+					}
+				},
+				// 5.绑定手机号
+				bindMobile: {
+					data: {
+						mobile: '', //手机号
+						code: '', //验证码
+						isMobileEnd: false // 手机号输入完毕
+					},
+					rules: {
+						code: FormValidate.code,
+						mobile: FormValidate.mobile
+					},
+					error: {
+						mobile: '', //手机号
+						code: '' //验证码
+					}
+				},
+				// 6.修改密码
+				changePwd: {
+					data: {
+						oldPassword: '', //旧密码
+						newPassword: '', //新密码
+						reNewPassword: '' //确认密码
+					},
+					rules: {
+						oldPassword: FormValidate.password,
+						newPassword: FormValidate.password,
+						reNewPassword: [
+							{
+								required: true,
+								message: '请重新输入密码',
+								trigger: ['change', 'blur']
+							},
+							{
+								validator: (rule, value, callback) => {
+									return value === this.form.changePwd.data.newPassword;
+								},
+								message: '两次输入的密码不一致',
+								trigger: ['change', 'blur']
+							}
+						]
+					},
+					error: {
+						oldPassword: '', //旧密码
+						newPassword: '', //新密码
+						reNewPassword: '' //确认密码
+					}
+				}
+			},
+			codeText: '获取验证码',
+			disabledCode: false,
+			protocol: false, //是否同意隐私协议
+			placeholderStyle: 'font-size: 30rpx; font-weight: 500;color:#C2C7CF;'
+		};
+	},
+
+	computed: {
+		...mapGetters(['initShop', 'isLogin', 'authType']),
+		showAuth: {
+			get() {
+				return !!this.authType;
+			},
+			set(value) {
+				value ? uni.hideTabBar() : uni.showTabBar();
+			}
+		}
+	},
+	mounted() {},
+	methods: {
+		...mapActions(['getUserInfo', 'showAuthModal']),
+
+		// 检测
+		checkValue(e, key) {
+			this.validation(key);
+		},
+
+		// 校验数据
+		validation(key, callback = () => {}) {
+			let that = this;
+			//拿到需要校验的数据
+			let fieldValue = this.form[this.authType].data[key];
+			//拿到需要检验的规则
+			let rules = this.form[this.authType].rules[key];
+			// 判空
+			if (!rules || rules.length === 0) {
+				return callback('');
+			}
+			// 设置当前的装填,标识为校验中
+			let validateState = 'validating';
+			// 调用async-validator的方法
+			let validator = new schema({
+				[key]: rules
+			});
+			// 校验
+			validator.validate(
+				{
+					[key]: fieldValue
+				},
+				{
+					firstFields: true
+				},
+				(errors, fields) => {
+					// 记录状态和报错信息
+					validateState = !errors ? 'success' : 'error';
+					let validateMessage = errors ? errors[0].message : '';
+					that.form[that.authType].error[key] = validateMessage;
+					callback(validateMessage);
+				}
+			);
+		},
+
+		// 校验全部数据
+		validateAll(callback) {
+			let that = this;
+			return new Promise(resolve => {
+				// 对当前所有表单检验
+				let valid = true; // 默认通过
+				let count = 0; // 用于标记是否检查完毕
+				let errorArr = []; // 存放错误信息
+				let curFormData = that.form[that.authType].data;
+				Object.keys(curFormData).map(field => {
+					that.validation(field, error => {
+						// 如果回调有error
+						if (error) {
+							valid = false;
+							errorArr.push(error);
+						}
+						if (++count === Object.keys(curFormData).length) {
+							resolve(valid);
+							if (errorArr.length) {
+								this.$u.toast(errorArr[0]);
+							}
+							if (typeof callback == 'function') callback(valid);
+						}
+					});
+				});
+			});
+		},
+
+		// 手机号是否输入完毕
+		mobileInput(e, key) {
+			this.form[this.authType].data.isMobileEnd = this.$u.test.mobile(this.form[this.authType].data.mobile);
+			this.validation(key);
+		},
+
+		closeAuthModal() {
+			this.$store.dispatch('showAuthModal', '');
+		},
+
+		// 获取短信验证码
+		getSmsCode(type) {
+			const that = this;
+			if (that.form[this.authType].data.isMobileEnd && !that.disabledCode) {
+				that.$http(
+					'common.smsSend',
+					{
+						mobile: that.form[this.authType].data.mobile,
+						event: type
+					},
+					'获取验证码中...'
+				).then(res => {
+					if (res.code === 1) {
+						that.codeChange();
+						that.$u.toast('验证码已发送,请注意查收短信');
+					} else {
+						that.$u.toast(res.msg);
+					}
+				});
+			} else {
+				that.$u.toast('请稍后再试');
+			}
+		},
+
+		// 倒计时
+		codeChange() {
+			if (this.disabledCode) return;
+			this.disabledCode = true;
+			let n = 10;
+			this.codeText = n + 's';
+			const run = setInterval(() => {
+				n -= 1;
+				if (n < 0) {
+					clearInterval(run);
+				}
+				this.codeText = n + 's';
+				if (this.codeText < 0 + 's') {
+					this.disabledCode = false;
+					this.codeText = '重新获取';
+				}
+			}, 1000);
+		},
+
+		// 规则校验
+		validateSubmit() {
+			if (['accountLogin', 'smsLogin', 'register'].includes(this.authType) && !this.protocol) {
+				this.$u.toast('请同意用户协议');
+				return false;
+			}
+			return this.validateAll();
+		},
+
+		// 第三方登录
+		async thirdLogin(provider) {
+			if (!this.protocol) {
+				this.$u.toast('请同意用户协议');
+				return false;
+			}
+			const that = this;
+			let token = '';
+			switch (provider) {
+				case 'wechat':
+					token = await wechat.login();
+					break;
+				case 'alipay':
+					break;
+				case 'apple':
+					token = await apple.appleIdOauth();
+					break;
+				default:
+					break;
+			}
+			if (token) {
+				that.closeAuthModal();
+				that.getUserInfo(token);
+			}
+		},
+
+		// 1.账号登录
+		async accountLoginSubmit() {
+			let that = this;
+			(await that.validateSubmit()) &&
+				that
+					.$http(
+						'user.accountLogin',
+						{
+							account: that.form['accountLogin'].data.account,
+							password: that.form['accountLogin'].data.password
+						},
+						'登录中...'
+					)
+					.then(res => {
+						if (res.code === 1) {
+							that.closeAuthModal();
+							that.getUserInfo(res.data.token);
+							that.$u.toast(res.msg);
+						}
+					});
+		},
+
+		// 2.短信登录
+		async smsLoginSubmit() {
+			let that = this;
+			(await that.validateSubmit()) &&
+				that
+					.$http(
+						'user.smsLogin',
+						{
+							mobile: that.form['smsLogin'].data.mobile,
+							code: that.form['smsLogin'].data.code
+						},
+						'登录中...'
+					)
+					.then(res => {
+						if (res.code === 1) {
+							that.closeAuthModal();
+							that.getUserInfo(res.data.token);
+							that.$u.toast(res.msg);
+						}
+					});
+		},
+
+		// 3.注册
+		async registerSubmit() {
+			let that = this;
+			(await that.validateSubmit()) &&
+				that
+					.$http(
+						'user.register',
+						{
+							mobile: that.form['register'].data.mobile,
+							code: that.form['register'].data.code,
+							password: that.form['register'].data.password
+						},
+						'注册中...'
+					)
+					.then(res => {
+						if (res.code === 1) {
+							that.$u.toast(res.msg);
+							that.closeAuthModal();
+							that.getUserInfo(res.data.token);
+						}
+					});
+		},
+
+		// 4.忘记密码
+		async forgotPwdSubmit() {
+			let that = this;
+			(await that.validateSubmit()) &&
+				that
+					.$http(
+						'user.forgotPwd',
+						{
+							mobile: that.form['forgotPwd'].data.mobile,
+							code: that.form['forgotPwd'].data.code,
+							password: that.form['forgotPwd'].data.password
+						},
+						'修改中...'
+					)
+					.then(res => {
+						if (res.code === 1) {
+							that.$u.toast(res.msg);
+							if (that.isLogin) {
+								that.closeAuthModal();
+							} else {
+								that.showAuthModal('accountLogin');
+							}
+						}
+					});
+		},
+
+		// 5.绑定手机
+		async bindMobileSubmit() {
+			let that = this;
+			(await that.validateSubmit()) &&
+				that
+					.$http(
+						'user.bindMobile',
+						{
+							mobile: that.form['bindMobile'].data.mobile,
+							code: that.form['bindMobile'].data.code,
+							password: that.form['bindMobile'].data.password
+						},
+						'绑定中...'
+					)
+					.then(res => {
+						if (res.code === 1) {
+							that.$u.toast(res.msg);
+							that.closeAuthModal();
+							that.getUserInfo();
+						}
+					});
+		},
+
+		// 6.修改密码
+		async changePwdSubmit() {
+			let that = this;
+			(await that.validateSubmit()) &&
+				that
+					.$http(
+						'user.changePwd',
+						{
+							oldpassword: that.form['changePwd'].data.oldPassword,
+							newpassword: that.form['changePwd'].data.newPassword
+						},
+						'修改中...'
+					)
+					.then(res => {
+						if (res.code === 1) {
+							that.closeAuthModal();
+							that.$u.toast(res.msg);
+							that.getUserInfo(res.data.userinfo.token);
+						}
+					});
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+@keyframes title-animation {
+	0% {
+		font-size: 32rpx;
+	}
+
+	100% {
+		font-size: 36rpx;
+	}
+}
+
+.form-wrap {
+	.form-item {
+		display: flex;
+		flex-direction: column;
+		font-size: 28rpx;
+		padding: 20rpx 0;
+		color: $u-main-color;
+		box-sizing: border-box;
+		line-height: $u-form-item-height;
+		width: 100%;
+		.item-title {
+			width: 140rpx;
+			font-size: 30rpx;
+			color: #333;
+			font-weight: 600;
+			text-align: left;
+		}
+		.message-error {
+			text-align: left;
+			font-size: 24rpx;
+			line-height: 24rpx;
+			color: $u-type-error;
+			margin-top: 12rpx;
+			margin-left: 120rpx;
+		}
+	}
+}
+
+.login-wrap {
+	padding: 50rpx 34rpx;
+	min-height: 700rpx;
+	background-color: #fff;
+
+	.head-box {
+		.head-title {
+			min-width: 160rpx;
+			font-size: 36rpx;
+			font-weight: bold;
+			color: #333333;
+			line-height: 36rpx;
+		}
+		.head-title-active {
+			width: 160rpx;
+			font-size: 32rpx;
+			font-weight: 600;
+			color: #999;
+			line-height: 36rpx;
+		}
+		.head-title-animation {
+			animation-name: title-animation;
+			animation-duration: 0.1s;
+			animation-timing-function: ease-out;
+			animation-fill-mode: forwards;
+		}
+		.head-title-line {
+			position: relative;
+			&::before {
+				content: '';
+				width: 1rpx;
+				height: 34rpx;
+				background-color: #e4e7ed;
+				position: absolute;
+				left: -30rpx;
+				top: 50%;
+				transform: translateY(-50%);
+			}
+		}
+
+		.head-subtitle {
+			font-size: 26rpx;
+			font-weight: 400;
+			color: #c2c7cf;
+			text-align: left;
+			display: flex;
+		}
+	}
+	.code-btn[disabled] {
+		background-color: #fff;
+	}
+
+	.code-btn-start {
+		width: 160rpx;
+		line-height: 56rpx;
+		border: 1rpx solid #e9b766;
+		border-radius: 28rpx;
+		font-size: 26rpx;
+		font-weight: 400;
+		color: #e9b766;
+		opacity: 0.5;
+	}
+
+	.forgot-btn {
+		width: 160rpx;
+		line-height: 56rpx;
+		font-size: 30rpx;
+		font-weight: 500;
+		color: #999;
+	}
+
+	.code-btn-end {
+		opacity: 1 !important;
+	}
+
+	.login-btn-start {
+		width: 158rpx;
+		line-height: 56rpx;
+		background: linear-gradient(90deg, #e9b461, #eecc89);
+		border-radius: 28rpx;
+		font-size: 26rpx;
+		font-weight: 500;
+		color: #ffffff;
+	}
+
+	.type-btn {
+		padding: 20rpx;
+		margin: 40rpx auto;
+		width: 200rpx;
+		font-size: 30rpx;
+		font-weight: 500;
+		color: #999999;
+	}
+
+	.auto-login-box {
+		width: 100%;
+
+		.auto-login-img {
+			width: 68rpx;
+			height: 68rpx;
+			border-radius: 50%;
+			margin: 0 30rpx;
+		}
+	}
+
+	.agreement-box {
+		margin: 80rpx auto 0;
+		.protocol-check {
+			transform: scale(0.7);
+		}
+		.agreement-text {
+			font-size: 26rpx;
+			font-weight: 500;
+			color: #999999;
+
+			.tcp-text {
+				color: #e9b562;
+			}
+		}
+	}
+}
+
+// 修改密码
+.editPwd-btn-box {
+	.save-btn {
+		width: 690rpx;
+		line-height: 70rpx;
+		background: linear-gradient(90deg, #e9b461, #eecc89);
+		border-radius: 35rpx;
+		font-size: 28rpx;
+		font-weight: 500;
+		color: #ffffff;
+	}
+	.forgot-btn {
+		width: 690rpx;
+		line-height: 70rpx;
+		font-size: 28rpx;
+		font-weight: 500;
+		color: #999999;
+	}
+}
+</style>

+ 123 - 0
components/shopro-canvas/shopro-canvas.vue

@@ -0,0 +1,123 @@
+<template>
+	<view class="invite-poster-content">
+		<!-- TODO: 小程序端使用type='2d'会有报错 -->
+		<canvas class="hideCanvas" canvas-id="self_canvas" :style="{ width: (poster.width || 1) + 'px', height: (poster.height || 1) + 'px' }"></canvas>
+	</view>
+</template>
+
+<script>
+/**
+ * shopro-canvas  - canvas
+ * @property {Object} canvasParams - 自定义参数
+ * @property {Boolean} isAutoInit = [true] - 是否自动渲染canvas
+ */
+
+import _app from '@/shopro/poster/QS-SharePoster/app.js';
+import { getSharePoster } from '@/shopro/poster/QS-SharePoster/QS-SharePoster.js';
+import tools from '@/shopro/poster/tools.js';
+import { mapMutations, mapActions, mapState, mapGetters } from 'vuex';
+let ctx = null;
+export default {
+	components: {},
+	data() {
+		return {
+			poster: {},
+			canvasId: 'self_canvas'
+		};
+	},
+	props: {
+		canvasParams: {
+			type: Object,
+			default: () => {}
+		},
+		isAutoInit: {
+			type: Boolean,
+			default: true
+		}
+	},
+	computed: {
+		...mapGetters(['initShare', 'userInfo'])
+	},
+	async mounted() {
+		ctx = uni.createCanvasContext(this.canvasId, this);
+		ctx && this.isAutoInit && this.shareFc();
+	},
+	methods: {
+		async shareFc() {
+			let that = this;
+			_app.showLoading('绘制中...');
+			let config = {};
+			if (that.canvasParams.backgroundImage) {
+				config = {
+					backgroundImage: tools.checkImgHttp(that.canvasParams.backgroundImage, 'bgImage')
+				};
+			}
+			if (that.canvasParams.background) {
+				config = {
+					background: {
+						width: that.canvasParams.background?.width || 100,
+						height: that.canvasParams.background?.height || 100,
+						backgroundColor: that.canvasParams.background?.color || '#000'
+					}
+				};
+			}
+
+			try {
+				_app.log('准备生成:' + new Date());
+				const d = await getSharePoster({
+					_this: that, //若在组件中使用 必传
+					...config,
+					posterCanvasId: that.canvasId, //canvasId
+					Context: ctx,
+					delayTimeScale: 10, //延时系数
+					draw: false, //是否执行ctx.draw方法, 推荐false,自己去draw
+					drawArray: ({ bgObj, type, bgScale }) => {
+						let arr = tools.initDrawArray(bgObj, that.canvasParams.drawArray);
+						return new Promise((rs, rj) => {
+							rs(arr);
+						});
+					},
+					setCanvasWH: ({ bgObj, type, bgScale }) => {
+						// 为动态设置画布宽高的方法,
+						this.poster = bgObj;
+					}
+				});
+				await that.drawPoster();
+			} catch (e) {
+				_app.hideLoading();
+				_app.showToast(JSON.stringify(e));
+				console.log(JSON.stringify(e));
+			}
+		},
+		async drawPoster() {
+			let that = this;
+			ctx.draw(false, () => {
+				uni.canvasToTempFilePath(
+					{
+						canvasId: that.canvasId,
+						success: res => {
+							_app.hideLoading();
+							that.$emit('success', res.tempFilePath);
+							_app.log('海报生成成功, 时间:' + new Date() + ', 临时路径: ' + res.tempFilePath);
+						},
+						fail: err => {
+							_app.hideLoading();
+							console.log('生成异常', err);
+						}
+					},
+					that
+				);
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+.hideCanvas {
+	position: fixed;
+	top: -99999upx;
+	left: -99999upx;
+	z-index: -99999;
+}
+</style>

+ 176 - 0
components/shopro-coupon/shopro-coupon.vue

@@ -0,0 +1,176 @@
+<template>
+	<view class="">
+		<!-- 未领取,已领取 -->
+		<view class="coupon-wrap" :style="gradientColor" :class="{ 'gray-wrap': state === 3 || state === 2 || couponData.status_code == 'cannot_get' }">
+			<view class="coupon-item u-flex u-row-between">
+				<view class="coupon-left  u-flex-col ">
+					<view class="sum-box">
+						<text class="unit " :style="{ color: priceColor }">¥</text>
+						<text class="sum " :style="{ color: priceColor }">{{ couponData.amount }}</text>
+						<text class="sub " :style="{ color: priceColor }">{{ couponData.name }}</text>
+					</view>
+					<view class="notice " :style="{ color: color }">满{{ couponData.enough }}元可用</view>
+					<view class="notice" :style="{ color: color }">
+						有效期:{{ $u.timeFormat(couponData.usetime.start, 'yyyy-mm-dd') }} 至 {{ $u.timeFormat(couponData.usetime.end, 'yyyy-mm-dd') }}
+					</view>
+				</view>
+				<view class="coupon-right u-flex-col">
+					<button class="get-btn" :style="{ color: btnTextColor }" v-if="state === 0" @tap.stop="getCoupon">{{ stateMap[couponData.status_code] || '立即领取' }}</button>
+					<button class="get-btn" :style="{ color: btnTextColor }" v-if="state === 1">去使用</button>
+					<button class="get-btn" :style="{ color: btnTextColor }" v-if="state === 2">暂不可用</button>
+					<button class="get-btn" v-if="state === 3">{{ stateMap[couponData.status_code] }}</button>
+					<view class="surplus-num" :style="{ color: color }" v-if="state === 0">仅剩{{ stock }}张</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+/**
+ * shoproCoupon-优惠券
+ * @property {Number} state - 0:立即领取,1:去使用,2:查看详情,3:已失效。
+ * @property {Object} couponData - 优惠劵数据。
+ * @property {String} gradientColor - 渐变色。
+ */
+export default {
+	components: {},
+	data() {
+		return {
+			stock: 0,
+			stateMap: {
+				can_use: '立即使用',
+				used: '已使用',
+				expired: '已失效',
+				cannot_use: '暂不可用',
+				can_get: '立即领取',
+				cannot_get: '已领取'
+			}
+		};
+	},
+	props: {
+		state: 0, //0:立即领取,1:去使用,2:查看详情,3:已失效。
+		couponData: {},
+		gradientColor: {
+			type: String,
+			default: ''
+		},
+		color: {
+			type: String,
+			default: ''
+		},
+		btnTextColor: {
+			type: String,
+			default: ''
+		},
+		priceColor: {
+			type: String,
+			default: ''
+		}
+	},
+	computed: {},
+	created() {
+		this.$nextTick(() => {
+			this.stock = this.couponData.stock;
+		});
+	},
+	methods: {
+		getCoupon() {
+			let that = this;
+
+			that.$http(
+				'coupons.get',
+				{
+					id: that.couponData.id
+				},
+				'领取中...'
+			).then(res => {
+				if (res.code === 1) {
+					that.stock -= 1;
+					that.$u.toast('领取成功');
+				}
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+// 失效
+.gray-wrap {
+	-webkit-filter: grayscale(1); /* Webkit */
+	filter: gray; /* IE6-9 */
+	filter: grayscale(1); /* W3C */
+}
+// 未领取,已领取
+.coupon-wrap {
+	mask: url($IMG_URL+'/imgs/coupon_bg1.png');
+	-webkit-mask-box-image: url($IMG_URL+'/imgs/coupon_bg1.png');
+	mask-size: 100% 100%;
+	position: relative;
+	border-radius: 10rpx;
+	width: 710rpx;
+	height: 170rpx;
+	background: linear-gradient(to right, $u-type-warning-disabled, $u-type-warning);
+	.coupon-item {
+		width: 100%;
+		height: 168rpx;
+		border-radius: 10rpx;
+
+		.coupon-left {
+			height: 100%;
+			justify-content: center;
+			padding-left: 40rpx;
+
+			.unit {
+				font-size: 24rpx;
+				color: #fff;
+			}
+
+			.sum {
+				font-size: 55rpx;
+				font-weight: bold;
+				color: #fff;
+				line-height: 55rpx;
+				padding-right: 10rpx;
+			}
+
+			.sub {
+				font-size: 26rpx;
+				color: #fff;
+			}
+
+			.notice {
+				font-size: 22rpx;
+				color: rgba(#fff, 0.7);
+				margin-top: 6rpx;
+			}
+		}
+
+		.coupon-right {
+			height: 100%;
+			width: 220rpx;
+			justify-content: center;
+			align-items: center;
+
+			.get-btn {
+				width: 142rpx;
+				height: 54rpx;
+				background: #ffffff;
+				border-radius: 27rpx;
+				padding: 0;
+				font-size: 24rpx;
+				font-weight: 500;
+				color: $u-type-warning;
+			}
+
+			.surplus-num {
+				font-size: 22rpx;
+				font-weight: 500;
+				color: #fff;
+				margin-top: 14rpx;
+			}
+		}
+	}
+}
+</style>

+ 77 - 0
components/shopro-empty/shopro-empty.vue

@@ -0,0 +1,77 @@
+<template>
+	<view class="shopro-empty-wrap u-flex-col u-row-center u-col-center" :style="{ 'margin-top': marginTop }">
+		<image class="empty-img" :src="image" mode="aspectFill"></image>
+		<view class="empty-text u-tips-color u-font-26">{{ tipText }}</view>
+		<view class="btn-box u-m-t-100" v-if="btnText">
+			<button class="u-reset-button empty-btn" :style="customStyle" hover-class="none" @tap="onBtn">{{ btnText }}</button>
+		</view>
+	</view>
+</template>
+
+<script>
+/**
+ * shoproEmpty- 数据为空页
+ * @property {String} image - 空白图。
+ * @property {String} tipText - 提示语。
+ * @property {String} btnText - 按钮文字。
+ * @property {String} marginTop - 距离父级距离。
+ * @property {Object} customStyle - 自定义按钮样式。
+ * @event {Fuction} click - 点击按钮
+ */
+export default {
+	name: 'shoproEmpty',
+	props: {
+		image: {
+			type: [String,null],
+			default: '/static/images/empty_network.png'
+		},
+		tipText: {
+			type: String,
+			default: ''
+		},
+		btnText: {
+			type: String,
+			default: ''
+		},
+		marginTop: {
+			type: String,
+			default: '300rpx'
+		},
+		customStyle: {
+			type: Object,
+			default: () => {
+				return {
+					width: '200rpx',
+					height: '70rpx',
+					background: 'linear-gradient(90deg, rgba(233, 180, 97, 1), rgba(238, 204, 137, 1))',
+					borderRradius: '35rpx',
+					fontSize: '28rpx',
+					color: '#fff',
+					border: 0
+				};
+			}
+		}
+	},
+	methods: {
+		onBtn() {
+			this.$emit('click');
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.empty-img {
+	width: 540rpx;
+	height: 290rpx;
+}
+.empty-btn {
+	width: 320rpx;
+	height: 70rpx;
+	line-height: 70rpx;
+	background: linear-gradient(90deg, rgba(233, 180, 97, 1), rgba(238, 204, 137, 1));
+	border-radius: 35rpx;
+	font-size: 28rpx;
+	color: rgba(#fff, 0.9);
+}
+</style>

+ 184 - 0
components/shopro-float-btn/shopro-float-btn.vue

@@ -0,0 +1,184 @@
+<template>
+	<view class="" v-if="floatList && floatList.length">
+		<view class="cu-modal" :class="{ show: showMask }" @tap="hideMask"></view>
+		<!-- 悬浮按钮菜单 -->
+		<view class="shopro-float-btn">
+			<button class="wechat-btn u-reset-button" @tap="onBtn">
+				<image class="wechat_img" :src="floatList.length == 1 ? floatList[0].btnimage : floatData.image" mode="widthFix"></image>
+			</button>
+			<view :class="showBtnList ? 'float--active' : 'float-list-box'">
+				<view class="btn-img-box u-flex-col" @tap="onBtnItem(item)" v-for="item in floatList" :key="item.btnimage">
+					<view class="btn-item u-flex-col u-row-center u-col-center">
+						<image class="btn-img" :src="item.btnimage" mode="aspectFill"></image>
+						<view class="btn-name">{{ item.name }}</view>
+					</view>
+				</view>
+			</view>
+		</view>
+		<!-- 弹窗 -->
+		<view class="cu-modal" :class="{ show: showModal }" @tap="showModal = false">
+			<view class="cu-dialog" style="width: 610rpx;background: none;">
+				<view class="img-box"><image class="modal-img" :src="modalImg" mode="aspectFit" @tap="saveImage(modalImg)"></image></view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+/**
+ * 悬浮按钮,悬浮菜单。全局通用,数据为vuex初始化导入。
+ * @property {Array} floatList - 悬浮按钮菜单数据列表
+ */
+import { mapMutations, mapActions, mapState, mapGetters } from 'vuex';
+import Auth from '@/shopro/permission/index.js';
+export default {
+	components: {},
+	data() {
+		return {
+			showBtnList: false, //列表弹出
+			modalImg: '', //子项图片弹出
+			showModal: false, //modal层
+			showMask: false,
+			platform: this.$platform.get()
+		};
+	},
+	props: {},
+	computed: {
+		...mapGetters(['floatData']),
+		// 悬浮按钮数据列表
+		floatList() {
+			if (this.floatData) {
+				return this.floatData.list;
+			}
+		}
+	},
+	created() {},
+	methods: {
+		hideMask() {
+			this.showBtnList = false;
+			this.showMask = false;
+		},
+		// 点击悬浮按钮
+		onBtn() {
+			this.showMask = !this.showMask;
+			if (this.floatList.length == 1) {
+				this.modalImg = this.floatList[0].image;
+				this.floatList[0].style == 2 ? this.$tools.routerTo(this.floatList[0].path) : (this.showModal = true);
+				this.showMask = false;
+			} else {
+				this.showBtnList = !this.showBtnList;
+			}
+		},
+		// 点击按钮菜单,如果悬浮按钮数据为一条,按钮图片为唯一菜单项图片和标题
+		onBtnItem(item) {
+			if (item.style == 2) {
+				this.showMask = false;
+				this.showModal = false;
+				this.showBtnList = false;
+				this.$tools.routerTo(item.path);
+			} else {
+				this.modalImg = item.image;
+				this.showMask = false;
+				this.showModal = true;
+				this.showBtnList = false;
+				!item.image && console.log(`%cerr:弹窗图片未配置`, 'color:green;background:yellow');
+			}
+		},
+
+		saveImageToPhotosAlbum(path) {
+			uni.saveImageToPhotosAlbum({
+				filePath: path,
+				success: res => {
+					this.showMask = false;
+					this.showModal = false;
+					this.$u.toast('保存成功');
+				},
+				fail: err => {
+					console.log(`图片保存失败:`, err);
+					this.$u.toast('保存失败');
+				}
+			});
+		},
+		// 保存图片
+		async saveImage(path) {
+			let that = this;
+			if (['wxOfficialAccount', 'H5'].includes(this.platform)) {
+				this.$u.toast('长按图片保存');
+				return false;
+			}
+			let authState = await new Auth('writePhotosAlbum').check();
+			if (authState) {
+				// #ifdef MP ||APP-VUE
+				const res = await uni.downloadFile({
+					url: this.$tools.checkMPUrl(path)
+				});
+				res[1].statusCode === 200 && this.saveImageToPhotosAlbum(res[1].tempFilePath);
+				// #endif
+			}
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+.shopro-float-btn {
+	position: fixed;
+	bottom: calc(var(--window-bottom) + 100rpx);
+	right: 30rpx;
+	z-index: 9999;
+	.float--active {
+		position: absolute;
+		bottom: 80rpx;
+		right: 0;
+		left: 0;
+		margin: 0 auto;
+		z-index: 10010;
+		transform: scale(1);
+		transition: all 0.2s linear;
+	}
+	.float-list-box {
+		position: absolute;
+		z-index: 10010;
+		bottom: 0;
+		right: 0;
+		left: 0;
+		margin: 0 auto;
+		transform: scale(0);
+		transform-origin: bottom;
+		opacity: 0;
+		transition: all 0.2s linear;
+	}
+	.btn-item {
+		margin-bottom: 20rpx;
+		.btn-img {
+			width: 60rpx;
+			height: 60rpx;
+		}
+		.btn-name {
+			font-size: 20rpx;
+			color: #fff;
+			text-align: center;
+			white-space: nowrap;
+		}
+	}
+	.wechat-btn {
+		box-shadow: 0px 0px 20px 4px rgba(199, 199, 199, 0.22);
+		border-radius: 50%;
+		width: 80rpx;
+		height: 80rpx;
+		.wechat_img {
+			width: 80rpx;
+			height: 80rpx;
+		}
+	}
+}
+.img-box {
+	position: relative;
+	width: 610rpx;
+	.modal-img {
+		width: 100%;
+		will-change: transform;
+		height: 830rpx;
+	}
+}
+</style>

+ 348 - 0
components/shopro-goods-card/shopro-goods-card.vue

@@ -0,0 +1,348 @@
+<template>
+	<!-- m -->
+	<view class="goods-box" @tap="click">
+		<view class="img-box">
+			<image class="goods-img" lazy-load fade-show :src="image" mode="aspectFill"></image>
+		</view>
+		<view class="goods-bottom u-p-14"
+			:style="type ? 'background-image: url(' + $IMG_URL + typeMap[type].goodsBg + ')' : ''">
+			<view class="title u-ellipsis-2 u-m-b-10">
+				<view class=" sm cu-tag radius title-tag u-m-r-10"
+					:style="{ backgroundColor: typeMap[type].tagBg, color: '#fff' }" v-if="type">
+					{{ typeMap[type].text }}
+				</view>
+				{{ title }}
+			</view>
+			<view class="sub-title u-ellipsis-1 u-m-b-10" v-show="subtitle">{{ subtitle }}</view>
+			<view class="u-m-b-20">
+				<view class="cu-tag line-red sm radius" v-for="(item, index) in tagTextList" :key="index">{{ item }}
+				</view>
+			</view>
+
+			<slot name="cardBottom">
+				<view class="u-flex u-col-center u-row-between">
+					<view class="price-box">
+						<view class="price u-m-b-10">{{ price }}</view>
+						<view class="origin-price">¥{{ originPrice }}</view>
+					</view>
+					<!-- 加入购物车 -->
+					<view class="cart-box">
+						<!-- 单规格 -->
+						<view class="" v-if="!detail.is_sku">
+							<button class="u-reset-button cart-btn u-flex u-col-center u-row-center"
+								v-if="!isCart(detail.id)" @tap.stop="addCart(detail.sku_price[0])">
+								<view class="u-iconfont uicon-shopping-cart-fill" style="color: #fff;"></view>
+							</button>
+							<view class="num-step" @tap.stop v-else>
+								<u-number-box :value="checkCart[detail.id].num" :min="0" :step="1" :long-press="false"
+									:max="detail.sku_price[0].stock  > 999 ? 999 : detail.sku_price[0].stock"
+									@min="onMin" @plus="plus($event, detail.sku_price[0])"
+									@change="onChangeNum($event, detail.sku_price[0])"></u-number-box>
+							</view>
+						</view>
+						<!-- 多规格 -->
+						<button class="u-reset-button item-btn cart-btn  u-flex u-col-center u-row-center"
+							@tap.stop="selSku(detail)" v-else>
+							<view class="u-iconfont uicon-shopping-cart-fill" style="color: #fff;"></view>
+						</button>
+					</view>
+				</view>
+			</slot>
+		</view>
+		<!-- 规格弹窗 -->
+		<shopro-sku v-if="showSku && goodsInfo.id" v-model="showSku" :goodsInfo="goodsInfo" buyType="cart"></shopro-sku>
+	</view>
+</template>
+
+<script>
+	/**
+	 *shoproGoodsCard - 商品列表卡片
+	 * @property {Object} detail - 商品详情
+	 * @property {String} type - 商品类型
+	 * @property {String} image - 商品图片
+	 * @property {String} title - 商品标题
+	 * @property {String} subtitle - 商品副标题
+	 * @property {String | Number} price - 商品价格
+	 * @property {String | Number} originPrice - 商品原价
+	 * @property {String | Number} sales - 商品销量
+	 * @property {Array} tagTextList - 活动标签
+	 * @event {Function} click 商品被点击
+	 */
+	import { mapMutations, mapActions, mapState, mapGetters } from 'vuex';
+	export default {
+		components: {},
+		data() {
+			return {
+				showSku: false,
+				goodsInfo: {}, //商品详情
+				typeMap: {
+					seckill: {
+						text: '秒杀',
+						tagBg: '#FF5854',
+						goodsBg: '/imgs/tag/seckill_y_bg.png'
+					},
+					groupon: {
+						text: '拼团',
+						tagBg: '#FE832A',
+						goodsBg: '/imgs/tag/groupon_y_bg.png'
+					}
+				}
+			};
+		},
+		computed: {
+			...mapGetters(['cartList', 'checkCart'])
+		},
+		props: {
+			detail: {
+				type: Object,
+				default: () => {
+					return {};
+				}
+			},
+			type: {
+				type: [String, null],
+				default: ''
+			},
+			image: {
+				type: String,
+				default: ''
+			},
+			title: {
+				type: String,
+				default: ''
+			},
+			subtitle: {
+				type: String,
+				default: ''
+			},
+			price: {
+				type: [String, Number],
+				default: ''
+			},
+			originPrice: {
+				type: [String, Number],
+				default: ''
+			},
+			sales: {
+				type: [String, Number],
+				default: ''
+			},
+			tagTextList: {
+				type: Array,
+				default: () => []
+			}
+		},
+		methods: {
+			...mapActions(['getCartList', 'changeCartList', 'addCartGoods']),
+			//点击商品
+			click() {
+				this.$emit('click');
+			},
+
+
+			// 检测是否为购物车商品
+			isCart(id) {
+				return Object.keys(this.checkCart).includes(id + '');
+			},
+
+			// 检测商品在购物车中的下标
+			checkGoodsIndex(id) {
+				let cIndex = 0;
+				this.cartList.forEach((item, index) => {
+					if (id == item.goods_id) {
+						cIndex = index;
+					}
+				});
+				return cIndex;
+			},
+
+			// 更改商品数
+			async onChangeNum(e, sku) {
+				let gIndex = this.checkGoodsIndex(sku.goods_id);
+				if (e.value != this.checkCart[sku.goods_id].num) {
+					uni.showLoading({
+						mask: true
+					});
+					this.$set(this.cartList[gIndex], 'goods_num', +e.value);
+					await this.changeCartList({
+						ids: [this.checkCart[sku.goods_id].cartOrderId],
+						goodsNum: +e.value,
+						art: 'change'
+					});
+
+					await uni.hideLoading();
+				}
+			},
+
+			// 到达最小值
+			onMin() {
+				const that = this;
+				let cartGoodId = 0;
+				cartGoodId = this.cartList.filter(item => item.goods_id === that.detail.id)[0].id;
+				uni.showModal({
+					title: '删除提示',
+					confirmColor: '#f0c785',
+					content: `是否确认从购物车中删除此商品?`,
+					success: res => {
+						res.confirm && this.changeCartList({ ids: [cartGoodId], art: 'delete' });
+					}
+				});
+			},
+
+			// 增加
+			plus(e, sku) {
+				if (e.value >= sku.stock) {
+					this.$u.toast('库存不足');
+					return;
+				}
+				if (this.detail.activity_type === 'seckill' || this.detail.activity_type === 'groupon') {
+					let rules = this.detail.activity.rules;
+					if (rules.limit_buy != 0 && e.value >= rules.limit_buy) {
+						this.$u.toast('本次活动最多购买' + rules.limit_buy + '件');
+						return;
+					}
+				}
+			},
+
+			// 添加购物车,多规格
+			async selSku(info) {
+				if (this.detail.activity_type) {
+					this.$Router.push({ path: '/pages/goods/detail', query: { id: this.detail.id } });
+					return;
+				}
+				this.goodsInfo = {};
+				this.getGoodsDetail(info.id);
+				this.showSku = true;
+			},
+
+			// 商品详情
+			getGoodsDetail(id) {
+				let that = this;
+				that.$http('goods.detail', {
+					id: id
+				}).then(res => {
+					if (res.code === 1) {
+						that.goodsInfo = res.data;
+					}
+				});
+			},
+
+			// 加入购物车
+			addCart(sku) {
+				if (sku.stock <= 0) {
+					this.$u.toast('库存不足');
+					return;
+				}
+				if (this.detail.activity_type) {
+					this.$Router.push({ path: '/pages/goods/detail', query: { id: this.detail.id } });
+					return;
+				}
+				let confirmGoodsList = {
+					list: [{
+						goods_id: sku.goods_id,
+						goods_num: 1,
+						sku_price_id: sku.id,
+						goods_price: sku.price
+					}],
+					from: 'goods'
+				};
+				this.addCartGoods(confirmGoodsList).then(res => {
+					if (res.code === 1) {
+						this.$u.toast(res.msg);
+					}
+				});
+			}
+		}
+	};
+</script>
+
+<style lang="scss">
+	.goods-box {
+		width: 345rpx;
+		background: #fff;
+		border-radius: 20rpx;
+		overflow: hidden;
+
+		.goods-bottom {
+			background-size: 100% 100%;
+			background-position: bottom center;
+			background-repeat: no-repeat;
+		}
+
+		.img-box {
+			width: 345rpx;
+			height: 345rpx;
+			overflow: hidden;
+			position: relative;
+			background-color: #fff;
+			border-radius: 6rpx;
+
+			.goods-img {
+				width: 345rpx;
+				height: 345rpx;
+				background-color: #ccc;
+			}
+		}
+
+		.title {
+			width: 330rpx;
+			vertical-align: center;
+			font-size: 28rpx;
+			font-weight: bold;
+			line-height: 40rpx;
+			color: #333333;
+			padding-top: 6rpx;
+
+			.title-tag {
+				position: relative;
+				top: -6rpx;
+			}
+		}
+
+		.sub-title {
+			font-size: 24rpx;
+			font-weight: 400;
+			width: 330rpx;
+			color: #999999;
+		}
+
+		.price-box {
+			.price {
+				font-size: 30rpx;
+				color: #ff3000;
+				font-weight: 600;
+
+				&::before {
+					content: '¥';
+					font-size: 24rpx;
+				}
+			}
+
+			.origin-price {
+				font-size: 26rpx;
+				font-weight: 400;
+				text-decoration: line-through;
+				color: #c4c4c4;
+			}
+
+			.sales-box {
+				font-size: 18rpx;
+
+				font-weight: 400;
+				color: #c4c4c4;
+				line-height: 20rpx;
+			}
+		}
+
+		// 购物车
+		.cart-box {
+			.cart-btn {
+				width: 54rpx;
+				height: 54rpx;
+				border-radius: 50%;
+				padding: 0;
+				background: linear-gradient(90deg, #e9b461, #eecc89);
+			}
+		}
+	}
+</style>

+ 361 - 0
components/shopro-live-card/shopro-live-card.vue

@@ -0,0 +1,361 @@
+<template>
+	<view class="live-card-wrap">
+		<!-- 小卡片 -->
+		<view class="sp-live-card" v-if="liveType == 2" :style="{ width: wh + 'rpx' }">
+			<view class="live-content" @tap="goRoom" :style="{ width: wh + 'rpx' }">
+				<image class="item-cover" :src="detail.share_img" mode="aspectFill"></image>
+				<view class="item-status">
+					<image class="status-img" :src="liveStatus[liveState].img" mode=""></image>
+					<text class="status-text">{{ liveStatus[liveState].title }}</text>
+				</view>
+				<view class="item-title u-ellipsis-1" :style="{ width: wh + 'rpx' }">{{ detail.name }}</view>
+			</view>
+			<view class="live-bottom" :style="{ width: wh + 'rpx' }">
+				<view class="live-info">
+					<view class="info-box">
+						<view class="info-name u-ellipsis-1" :style="{ width: wh + 'rpx' }">{{ detail.anchor_name }}</view>
+					</view>
+				</view>
+				<slot name="liveGoods">
+					<view class="live-goods" v-if="liveGoodsList.length">
+						<view class="live-goods__item" v-for="(goods, index) in liveGoodsList" :key="goods.id" v-if="index < 3">
+							<image class="live-goods__img" :src="goods.cover_img" mode=""></image>
+							<view class="live-goods__price" v-if="index < 2">¥{{ goods.price }}</view>
+							<view class="live-goods__mark" v-else>
+								<text>{{ liveGoodsList.length }}+</text>
+							</view>
+						</view>
+					</view>
+				</slot>
+			</view>
+		</view>
+		<!-- 大卡片 -->
+		<view class="big-card-wrap" v-if="liveType == 1">
+			<view class="content-one__item" @tap="goRoom">
+				<image class="item-cover" :src="detail.share_img" mode="widthFix"></image>
+				<view class="item-status">
+					<image class="status-img" :src="liveStatus[liveState].img" mode=""></image>
+					<text class="status-text">{{ liveStatus[liveState].title }}</text>
+				</view>
+				<view class="item-title u-ellipsis-1">{{ detail.name }}</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+/**
+ * 小程序直播显示卡片
+ * @property {Object} detail - 直播卡片显示数据
+ * @property {Number} wh = [345] - 直播卡片宽度
+ * @property {Number|String} style = 2 - 1:大卡片;2::小卡片
+ */
+// #ifdef MP-WEIXIN
+import { HAS_LIVE } from '@/env';
+let livePlayer = null;
+// 全局配置文件控制是否开启直播组件,没有此项功能的小程序,运行会报错。
+if (HAS_LIVE) {
+	livePlayer = requirePlugin('live-player-plugin');
+}
+//  #endif
+let timer = null;
+export default {
+	name: 'shoproLiveCard',
+	components: {},
+	data() {
+		return {
+			liveType: this.type,
+			liveState: this.detail.live_status,
+			liveGoodsList: this.detail.goods,
+			liveStatus: {
+				'101': {
+					img: this.$IMG_URL + '/imgs/live/live.png',
+					title: '直播中'
+				},
+				'102': {
+					img: this.$IMG_URL + '/imgs/live/prevue.png',
+					title: '未开始'
+				},
+				'103': {
+					img: this.$IMG_URL + '/imgs/live/playback.png',
+					title: '已结束'
+				},
+				'104': {
+					img: this.$IMG_URL + '/imgs/live/104.png',
+					title: '禁播'
+				},
+				'105': {
+					img: this.$IMG_URL + '/imgs/live/105.png',
+					title: '暂停中'
+				},
+				'106': {
+					img: this.$IMG_URL + '/imgs/live/106.png',
+					title: '异常'
+				},
+				'107': {
+					img: this.$IMG_URL + '/imgs/live/past.png',
+					title: '已过期'
+				}
+			}
+		};
+	},
+	props: {
+		detail: {
+			type: Object,
+			default: null
+		},
+		wh: {
+			type: Number,
+			default: 345
+		},
+		type: {
+			type: [Number, String],
+			default: 2
+		}
+	},
+
+	computed: {},
+	created() {
+		this.getLiveStatus();
+	},
+	mounted() {
+		let that = this;
+		timer = setInterval(() => {
+			that.getLiveStatus();
+		}, 60000);
+	},
+	beforeDestroy() {
+		timer = null;
+		clearInterval(timer);
+	},
+	methods: {
+		goRoom() {
+			let that = this;
+			wx.navigateTo({
+				url: `plugin-private://wx2b03c6e691cd7370/pages/live-player-plugin?room_id=${that.detail.room_id}`
+			});
+		},
+		// 轮询liveStatus
+		getLiveStatus() {
+			if (HAS_LIVE) {
+				let that = this;
+				let date = '';
+
+				if (that.liveState == 102) {
+					date = that.$u.timeFormat(that.detail.starttime, 'mm-dd hh:MM');
+					that.liveStatus['102'].title = '预告 ' + date;
+				}
+				livePlayer
+					.getLiveStatus({ room_id: that.detail.room_id })
+					.then(res => {
+						// 101: 直播中, 102: 未开始, 103: 已结束, 104: 禁播, 105: 暂停中, 106: 异常,107:已过期
+						that.liveState = res.liveStatus;
+					})
+					.catch(err => {
+						console.log('get live status', err);
+					});
+			}
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+// 小卡片
+.sp-live-card {
+	width: 335rpx;
+	box-shadow: 0px 0px 10rpx 4rpx rgba(199, 199, 199, 0.22);
+	border-radius: 20rpx;
+	height: 100%;
+	overflow: auto;
+}
+.live-content {
+	position: relative;
+	width: 335rpx;
+	height: 335rpx;
+	overflow: hidden;
+	.item-cover {
+		background-color: #eee;
+		width: 100%;
+		height: 100%;
+		border-radius: 20rpx 20rpx 0 0;
+	}
+	.item-status {
+		position: absolute;
+		top: 20rpx;
+		left: 10rpx;
+		height: 40rpx;
+		background: rgba(0, 0, 0, 0.4);
+		border-radius: 20rpx;
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		.status-img {
+			width: 40rpx;
+			height: 40rpx;
+		}
+		.status-text {
+			font-size: 22rpx;
+
+			font-weight: 500;
+			color: rgba(255, 255, 255, 1);
+			padding: 0 10rpx;
+		}
+	}
+	.item-title {
+		width: 335rpx;
+		position: absolute;
+		bottom: 0;
+		line-height: 60rpx;
+		padding: 0 20rpx;
+		font-size: 26rpx;
+		font-weight: 500;
+		color: rgba(255, 255, 255, 1);
+		background: linear-gradient(transparent, rgba(#000, 0.5));
+		padding-right: 60rpx;
+	}
+	.like-img {
+		position: absolute;
+		bottom: 20rpx;
+		right: 10rpx;
+		width: 60rpx;
+		height: 130rpx;
+	}
+}
+.live-bottom {
+	background-color: #fff;
+	padding: 20rpx;
+	width: 335rpx;
+	.live-info {
+		display: flex;
+		justify-content: space-between;
+		align-items: center;
+		width: 100%;
+		.info-box {
+			display: flex;
+			align-items: center;
+		}
+		.info-avatar {
+			width: 40rpx;
+			height: 40rpx;
+			border-radius: 50%;
+			margin-right: 10rpx;
+			background: #eee;
+		}
+		.info-name {
+			width: 150rpx;
+			font-size: 24rpx;
+
+			font-weight: 500;
+			color: rgba(51, 51, 51, 1);
+		}
+		.views {
+			font-size: 20rpx;
+			font-weight: 400;
+			color: rgba(153, 153, 153, 1);
+		}
+	}
+	.live-goods {
+		display: flex;
+		align-items: center;
+		margin-top: 20rpx;
+		&__item {
+			position: relative;
+			width: 96rpx;
+			height: 96rpx;
+			border: 1rpx solid rgba(238, 238, 238, 1);
+			border-radius: 10rpx;
+			overflow: hidden;
+			margin-right: 8rpx;
+			&:nth-child(3n) {
+				margin-right: 0;
+			}
+		}
+		&__img {
+			background: #eee;
+			width: 100%;
+			height: 100%;
+		}
+		&__price {
+			position: absolute;
+			bottom: 0;
+			line-height: 40rpx;
+			width: 100%;
+			background: linear-gradient(transparent, rgba(#000, 0.5));
+			font-size: 20rpx;
+			color: #fff;
+		}
+		&__mark {
+			position: absolute;
+			width: 100%;
+			height: 100%;
+			top: 0;
+			left: 0;
+			margin: auto;
+			display: flex;
+			justify-content: center;
+			align-items: center;
+			background: rgba(#000, 0.3);
+			font-size: 24rpx;
+			font-weight: 500;
+			color: rgba(255, 255, 255, 1);
+		}
+	}
+}
+// 单个大图直播
+.big-card-wrap {
+	.content-one__item {
+		position: relative;
+		height: 280rpx;
+		border-radius: 20rpx;
+		margin-top: 16rpx;
+		overflow: hidden;
+		.item-cover {
+			background-color: #eee;
+			width: 100%;
+			height: 100%;
+		}
+		.item-status {
+			position: absolute;
+			top: 20rpx;
+			left: 10rpx;
+			height: 40rpx;
+			background: rgba(0, 0, 0, 0.4);
+			border-radius: 20rpx;
+			display: flex;
+			justify-content: center;
+			align-items: center;
+			.status-img {
+				width: 38rpx;
+				height: 38rpx;
+			}
+			.status-text {
+				font-size: 22rpx;
+				font-family: PingFang SC;
+				font-weight: 500;
+				color: rgba(255, 255, 255, 1);
+				padding: 0 10rpx;
+			}
+		}
+		.item-title {
+			width: 100%;
+			position: absolute;
+			bottom: 0;
+			line-height: 60rpx;
+			padding: 0 20rpx;
+			font-size: 26rpx;
+			font-family: PingFang SC;
+			font-weight: 500;
+			color: rgba(255, 255, 255, 1);
+			background: linear-gradient(transparent, rgba(#000, 0.5));
+		}
+		.like-img {
+			position: absolute;
+			bottom: 20rpx;
+			right: 10rpx;
+			width: 60rpx;
+			height: 130rpx;
+		}
+	}
+}
+</style>

+ 130 - 0
components/shopro-mini-card/shopro-mini-card.vue

@@ -0,0 +1,130 @@
+<template>
+	<view class="goods-box u-flex u-col-top" @tap="click">
+		<view class="goods__tag" v-show="tag"><image class="tag-img" :src="tag" mode="widthFix"></image></view>
+		<image class="goods_img"  lazy-load fade-show :src="image" mode="aspectFill"></u-image>
+		<view class="u-m-l-20">
+			<view class="goods-title u-ellipsis-2 u-m-b-10">{{ title }}</view>
+			<view v-if="subtitle" class="describe-text u-m-b-10 u-ellipsis-1">{{ subtitle }}</view>
+			<slot name="describe"></slot>
+			<slot name="cardBottom">
+				<view class="u-flex u-col-bottom font-OPPOSANS">
+					<view class="price u-m-r-10">{{ price }}</view>
+					<view class="origin-price">{{ originPrice }}</view>
+				</view>
+			</slot>
+		</view>
+	</view>
+</template>
+
+<script>
+/**
+ * 商品小卡片
+ * @property {String} title - 标题
+ * @property {String} subtitle - 副标题
+ * @property {String} image - 图片地址
+ * @property {String} describe - 描述信息
+ * @property {String | Number} price - 价格
+ * @property {String | Number} originPrice - 原价
+ * @property {String} tag - 商品标签
+ * @property {Number} number - 商品数量
+ * @event {Function} click - 点击卡片
+ */
+export default {
+	components: {},
+	data() {
+		return {};
+	},
+	props: {
+		image: {
+			type: String,
+			default: ''
+		},
+		title: {
+			type: String,
+			default: ''
+		},
+		subtitle: {
+			type: String,
+			default: ''
+		},
+		describe: {
+			type: String,
+			default: ''
+		},
+		price: {
+			type: [Number, String],
+			default: ''
+		},
+		originPrice: {
+			type: [Number, String],
+			default: ''
+		},
+		tag: {
+			type: String,
+			default: ''
+		},
+		number: {
+			type: Number,
+			default: 0
+		}
+	},
+	computed: {},
+	created() {},
+	methods: {
+		click() {
+			this.$emit('click');
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+.goods-box {
+	position: relative;
+	.goods__tag {
+		position: absolute;
+		top: 0;
+		left: 0;
+		z-index: 5;
+		.tag-img {
+			width: 60rpx;
+		}
+	}
+	.goods_img{
+		width: 180rpx;
+		height: 180rpx;
+		border-radius: 6rpx;
+	}
+	.goods-title {
+		font-size: 28rpx;
+		font-weight: 500;
+		color: rgba(51, 51, 51, 1);
+		width: 450rpx;
+		line-height: 40rpx;
+	}
+
+	.describe-text {
+		font-size: 24rpx;
+		width: 450rpx;
+		color: #a8700d;
+	}
+
+	.price {
+		color: $u-type-error;
+		font-weight: 600;
+		&::before {
+			content: '¥';
+			font-size: 20rpx;
+		}
+	}
+	.origin-price {
+		color: $u-type-info-disabled;
+		font-size: 24rpx;
+		text-decoration: line-through;
+		&::before {
+			content: '¥';
+			font-size: 20rpx;
+		}
+	}
+}
+</style>

+ 130 - 0
components/shopro-navbar/shopro-navbar.vue

@@ -0,0 +1,130 @@
+<template>
+	<view>
+		<view class="cu-custom" :style="[{ height: customBar + 'px' }]">
+			<view class="cu-bar u-flex u-row-between" :class="{ fixed: isFixed }" :style="[style]">
+				<view class="action u-flex" @tap="goBack" v-if="isBack">
+					<view class="u-iconfont" :class="'uicon-' + backIconName" :style="{ color: backIconColor, fontSize: backIconSize + 'rpx' }"></view>
+					<view class="u-back-text u-line-1 u-m-l-20" v-if="backText" :style="[backTextStyle]">{{ backText || '' }}</view>
+				</view>
+				<view class="content" :style="[{ top: statusBarHeight + 'px' }]"><slot name="content"></slot></view>
+				<slot name="right"></slot>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+/**
+ * shopro-navbar -自定义标题栏
+ * @property {Boolean} isBack = [true] - 是否显示返回按钮
+ * @property {Booelan} isFixed = [true] - 是否开启定位
+ */
+
+// 获取系统状态栏的高度
+let systemInfo = uni.getSystemInfoSync();
+let menuButtonInfo = {};
+// #ifdef MP-WEIXIN || MP-BAIDU || MP-TOUTIAO || MP-QQ
+menuButtonInfo = uni.getMenuButtonBoundingClientRect();
+// #endif
+export default {
+	name: 'shopro-tabbar',
+	data() {
+		return {
+			statusBarHeight: systemInfo.statusBarHeight
+		};
+	},
+	computed: {
+		// tabbar样式
+		style() {
+			let statusBarHeight = systemInfo.statusBarHeight;
+			let style = {};
+			style.height = `${this.customBar}px`;
+			style.paddingTop = `${statusBarHeight}px`;
+			Object.assign(style, this.background);
+			return style;
+		},
+		// 高度
+		customBar() {
+			let statusBarHeight = systemInfo.statusBarHeight;
+			// #ifndef MP
+			return systemInfo.platform == 'android' ? statusBarHeight + 50 : statusBarHeight + 45;
+			// #endif
+			// #ifdef MP-WEIXIN
+			return menuButtonInfo.bottom + menuButtonInfo.top - statusBarHeight;
+			// #endif
+			// #ifdef MP-ALIPAY
+			return statusBarHeight + systemInfo.titleBarHeight;
+			// #endif
+		}
+	},
+	props: {
+		isBack: {
+			type: Boolean,
+			default: true
+		},
+		isFixed: {
+			type: Boolean,
+			default: true
+		},
+		background: {
+			type: Object,
+			default() {
+				return {
+					background: '#ffffff'
+				};
+			}
+		},
+		// 返回箭头的颜色
+		backIconColor: {
+			type: String,
+			default: '#606266'
+		},
+		// 左边返回的图标
+		backIconName: {
+			type: String,
+			default: 'arrow-left'
+		},
+		// 左边返回图标的大小,rpx
+		backIconSize: {
+			type: [String, Number],
+			default: '44'
+		},
+		// 返回的文字提示
+		backText: {
+			type: String,
+			default: ''
+		},
+		// 返回的文字的 样式
+		backTextStyle: {
+			type: Object,
+			default() {
+				return {
+					color: '#606266'
+				};
+			}
+		}
+	},
+	methods: {
+		goBack() {
+			uni.navigateBack();
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.cu-custom {
+	width: 100%;
+}
+.cu-bar {
+	width: 100%;
+	.content {
+		width: 560rpx;
+		display: flex;
+		flex-direction: row;
+		flex: 1;
+		align-items: center;
+		pointer-events: auto;
+	}
+}
+</style>

+ 71 - 0
components/shopro-notice-modal/shopro-notice-modal.vue

@@ -0,0 +1,71 @@
+<template>
+	<view class="popup-box" v-show="newPopupList && newPopupList.length">
+		<view class="" v-for="(p, index) in newPopupList" :key="index">
+			<view class="cu-modal" :class="{ show: showModal }" @tap="hideModal(p, index)" v-if="popupCurrent === index && p.image">
+				<view class="cu-dialog" style="width: 610rpx;background: none;">
+					<view class="img-box" @tap.stop="onPopup(p.path)"><image class="modal-img" :src="p.image" mode="aspectFit"></image></view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+/**
+ * 广告模态框。连续弹窗和只弹一次。
+ * @property {Object} newPopupList  - vuex 初始化传过来的数据
+ */
+import { mapMutations, mapActions, mapState, mapGetters } from 'vuex';
+let timer = null;
+export default {
+	name: 'shoproNoticeModal',
+	components: {},
+	data() {
+		return {
+			popupCurrent: 0,
+			showModal: true
+		};
+	},
+	props: {},
+	computed: {
+		...mapGetters(['popupData', 'isLogin']),
+		newPopupList() {
+			if (this.popupData) {
+				return this.popupData.list;
+			}
+		}
+	},
+	beforeDestroy() {
+		clearTimeout(timer);
+		timer = null;
+	},
+	methods: {
+		hideModal(p, index) {
+			clearTimeout(timer);
+			this.showModal = false;
+			if (p.style == 1) {
+				this.$store.commit('delPopup', index);
+			}
+			timer = setTimeout(() => {
+				this.popupCurrent += 1;
+				this.showModal = true;
+			}, 500);
+		},
+		onPopup(path) {
+			this.$tools.routerTo(path);
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+.img-box {
+	position: relative;
+	width: 610rpx;
+	.modal-img {
+		width: 100%;
+		will-change: transform;
+		height: 830rpx;
+	}
+}
+</style>

+ 571 - 0
components/shopro-share/shopro-share.vue

@@ -0,0 +1,571 @@
+<template>
+	<view class="content">
+		<!-- 海报弹窗 -->
+		<view class="cu-modal" :class="{ show: showPoster }" @tap="onClosePoster">
+			<view class="cu-dialog" style="width: 640rpx;background: none;">
+				<view class="poster-img-box"><image class="poster-img" :src="posterImage" mode="widthFix"></image></view>
+				<view class="poster-btn-box u-m-t-20 u-flex u-row-between u-col-center" v-show="posterImage">
+					<button class="cancel-btn u-reset-button" @tap="showPoster = false">取消</button>
+					<button class="save-btn u-reset-button" @tap="saveImage">{{ ['wxOfficialAccount', 'H5'].includes(platform) ? '长按图片保存' : '保存图片' }}</button>
+				</view>
+			</view>
+		</view>
+		<!-- 分享tools -->
+		<view class="cu-modal bottom-modal" :class="{ show: showShare }" @tap="showShare = false">
+			<view class="cu-dialog safe-area-inset-bottom" style="border-radius: 20rpx 20rpx 0 0;background: none;">
+				<view class="share-box">
+					<view class="share-list-box u-flex">
+						<!-- #ifdef MP-WEIXIN -->
+						<button class="share-item share-btn u-flex-col u-col-center" open-type="share">
+							<image class="share-img" :src="$IMG_URL + '/imgs/share/share_wx.png'" mode=""></image>
+							<text class="share-title">微信好友</text>
+						</button>
+						<!-- #endif -->
+						<!-- #ifndef MP-WEIXIN  -->
+						<view v-if="platform !== 'H5'" class="share-item u-flex-col u-col-center" @tap="shareFriend">
+							<image class="share-img" :src="$IMG_URL + '/imgs/share/share_wx.png'" mode=""></image>
+							<text class="share-title">微信好友</text>
+						</view>
+						<!-- #endif -->
+						<view class="share-item u-flex-col u-col-center" @tap="onPoster">
+							<image class="share-img" :src="$IMG_URL + '/imgs/share/share_poster.png'" mode=""></image>
+							<text class="share-title">生成海报</text>
+						</view>
+
+						<view class="share-item u-flex-col u-col-center" @tap="copySharePath">
+							<image class="share-img" :src="$IMG_URL + '/imgs/share/share_link.png'" mode=""></image>
+							<text class="share-title">复制链接</text>
+						</view>
+					</view>
+					<view class="share-foot u-flex u-row-center u-col-center" @tap="showShare = false">取消</view>
+				</view>
+			</view>
+		</view>
+		<!-- 分享指引 -->
+		<view class="cu-modal bottom-modal" :class="{ show: showShareGuide }" @tap="showShareGuide = false">
+			<view class="cu-dialog safe-area-inset-bottom" style="border-radius: 20rpx 20rpx 0 0;background: none;vertical-align: top;">
+				<view class="guide-wrap u-flex u-col-top u-row-center"><image class="guide-img" :src="$IMG_URL + '/imgs/share/share_guide.png'" mode=""></image></view>
+			</view>
+		</view>
+
+		<!-- 各海报模块 -->
+		<shopro-canvas v-if="showPoster" ref="shoproCanvas" :canvasParams="canvasParams" @success="onSuccess"></shopro-canvas>
+	</view>
+</template>
+<script>
+/**
+ * 分享弹窗
+ * @property {Boolean} value = showModal - v-model控制显隐
+ * @property {String} posterType - 海报类别
+ * @property {Object} posterInfo - 海报数据
+ */
+import { mapMutations, mapActions, mapState, mapGetters } from 'vuex';
+import Auth from '@/shopro/permission/index.js';
+export default {
+	name: 'shoproShare',
+	components: {},
+	data() {
+		return {
+			showShareGuide: false, //H5的指引。
+			showPoster: false, //海报弹窗
+			platform: this.$platform.get(),
+			posterImage: '',
+			canvasParams: {}
+		};
+	},
+	props: {
+		posterType: {
+			type: String,
+			default: ''
+		},
+		posterInfo: {
+			type: Object,
+			default: () => {}
+		},
+		value: {}
+	},
+	computed: {
+		...mapGetters(['initShare', 'userInfo', 'isLogin', 'shareInfo']),
+		showShare: {
+			get() {
+				return this.value;
+			},
+			set(val) {
+				if (!this.showPoster) {
+					val ? uni.hideTabBar() : uni.showTabBar();
+				}
+				this.$emit('input', val);
+			}
+		}
+	},
+
+	created() {
+		uni.$on('ON_WX_SHARE', () => {
+			this.showShare = false;
+		});
+	},
+	methods: {
+		// 关闭弹窗
+		onClosePoster() {
+			this.showPoster = false;
+			uni.showTabBar();
+		},
+		// 绘制成功
+		onSuccess(e) {
+			this.posterImage = e;
+		},
+		// 开始绘制
+		onPoster() {
+			this.posterImage = '';
+			uni.hideTabBar();
+			if (this.$store.getters.isLogin) {
+				this.canvasParams = this.getPosterFormatter();
+				this.showPoster = true;
+			} else {
+				this.$store.dispatch('showAuthModal', 'accountLogin');
+			}
+			this.showShare = false;
+		},
+		// 分享好友
+		shareFriend() {
+			let that = this;
+			// #ifdef APP-PLUS
+			uni.share({
+				provider: 'weixin',
+				scene: 'WXSceneSession',
+				type: 0,
+				href: that.shareInfo.path,
+				title: that.shareInfo.title,
+				summary: that.shareInfo.title,
+				image: that.shareInfo.image,
+				success: res => {
+					console.log('success:' + JSON.stringify(res));
+					this.showShare = false;
+				},
+				fail: err => {
+					console.log('fail:' + JSON.stringify(err));
+				}
+			});
+			// #endif
+			// #ifdef H5
+			this.showShare = false;
+			this.showShareGuide = true;
+			// #endif
+		},
+
+		// 保存图片
+		async saveImage() {
+			let that = this;
+			if (['wxOfficialAccount', 'H5'].includes(this.platform)) {
+				this.$u.toast('长按图片保存');
+				return false;
+			}
+			let authState = await new Auth('writePhotosAlbum').check();
+			if (authState) {
+				uni.saveImageToPhotosAlbum({
+					filePath: that.posterImage,
+					success: res => {
+						this.$u.toast('保存成功');
+						this.showPoster = false;
+					},
+					fail: err => {
+						console.log(`图片保存失败:`, err);
+						this.$u.toast('保存失败');
+					}
+				});
+			}
+		},
+		// 复制链接
+		copySharePath() {
+			let that = this;
+			uni.setClipboardData({
+				data: that.shareInfo.copyLink,
+				success: data => {
+					that.$u.toast('已复制到剪切板');
+					that.showShare = false;
+				}
+			});
+		},
+		// 获取海报格式,规则说明在@/shopro/poster/tools.js中的initDrawArray
+		getPosterFormatter() {
+			const that = this;
+			let data = {};
+			switch (this.posterType) {
+				case 'user':
+					data = {
+						backgroundImage: that.initShare.user_poster_bg,
+						drawArray: [
+							{
+								name: '用户昵称',
+								type: 'text',
+								text: that.userInfo.nickname,
+								isBgCenter: true,
+								size: 28,
+								dy: 250,
+								color: '#333',
+								textAlign: 'middle',
+								textBaseLine: 'middle'
+							},
+							{
+								name: 'avatar',
+								type: 'image',
+								url: that.userInfo.avatar,
+								alpha: 1,
+								isBgCenter: true,
+								dy: 95,
+								dWidth: 120,
+								dHeight: 120,
+								circleSet: {}
+							},
+							// #ifdef MP-WEIXIN
+							{
+								name: 'wxCode',
+								type: 'image',
+								url: `${that.$API_URL}wechat/wxacode?scene=${that.shareInfo.query}`,
+								alpha: 1,
+								dy: 560,
+								isBgCenter: true,
+								dWidth: 180,
+								dHeight: 180
+							},
+							// #endif
+							// #ifndef  MP-WEIXIN
+							{
+								name: '普通二维码',
+								type: 'qrcode',
+								text: that.shareInfo.path,
+								size: 180,
+								dy: 560,
+								isBgCenter: true
+							}
+							// #endif
+						]
+					};
+					break;
+				case 'goods':
+					data = {
+						backgroundImage: that.initShare.goods_poster_bg,
+						drawArray: [
+							{
+								name: 'avatar',
+								type: 'image',
+								url: that.userInfo.avatar,
+								alpha: 1,
+								dy: 40,
+								dx: 38,
+								dWidth: 80,
+								dHeight: 80,
+								circleSet: {}
+							},
+							{
+								type: 'text',
+								text: that.userInfo.nickname,
+								size: 28,
+								color: '#333',
+								alpha: 1,
+								textAlign: 'middle',
+								textBaseline: 'bottom',
+								dx: 140,
+								dy: 40
+							},
+							{
+								type: 'text',
+								text: '推荐一个好物给你,请查收!',
+								size: 26,
+								color: '#333',
+								alpha: 1,
+								textAlign: 'middle',
+								textBaseline: 'middle',
+								dx: 140,
+								dy: 80
+							},
+							{
+								name: 'goodsImage',
+								type: 'image',
+								url: that.posterInfo.image,
+								alpha: 1,
+								isBgCenter: true,
+								dy: 140,
+								dWidth: 620,
+								dHeight: 620,
+								roundRectSet: {
+									r: 20
+								}
+							},
+							{
+								type: 'text', //标题
+								text: that.posterInfo.title,
+								size: 28,
+								color: '#333',
+								alpha: 1,
+								textAlign: 'left',
+								textBaseline: 'middle',
+								fontWeight: 'bold',
+								lineFeed: {
+									maxWidth: 620,
+									lineHeight: 40,
+									lineNum: 2
+								},
+								dx: 36,
+								dy: 780
+							},
+							{
+								type: 'text',
+								text: `¥${that.posterInfo.price}`,
+								size: 38,
+								color: '#E1212B',
+								alpha: 1,
+								textAlign: 'left',
+								textBaseline: 'middle',
+								fontWeight: 'bold',
+								fontFamily: 'OPPOSANS',
+								dx: 30,
+								dy: 860
+							},
+							{
+								type: 'text',
+								text: `¥${that.posterInfo.original_price}`,
+								size: 28,
+								color: '#999999',
+								alpha: 1,
+								textAlign: 'left',
+								textBaseline: 'top',
+								lineThrough: {
+									style: '#999999'
+								},
+								dx: 400,
+								dy: 860
+							},
+							// #ifdef MP-WEIXIN
+							{
+								name: 'wxCode',
+								type: 'image', //微信小程序码
+								url: `${that.$API_URL}wechat/wxacode?scene=${that.shareInfo.query}`,
+								alpha: 1,
+								dx: 522,
+								dy: 911,
+								dWidth: 110,
+								dHeight: 110
+							},
+							// #endif
+							// #ifndef MP-WEIXIN
+							{
+								type: 'qrcode',
+								text: that.shareInfo.copyLink,
+								size: 110,
+								dx: 530,
+								dy: 930
+							}
+							// #endif
+						]
+					};
+					break;
+				case 'groupon':
+					data = {
+						backgroundImage: that.initShare.groupon_poster_bg,
+						drawArray: [
+							{
+								name: 'avatar',
+								type: 'image',
+								url: that.userInfo.avatar,
+								alpha: 1,
+								dy: 40,
+								dx: 38,
+								dWidth: 80,
+								dHeight: 80,
+								circleSet: {}
+							},
+							{
+								type: 'text',
+								text: that.userInfo.nickname,
+								size: 28,
+								color: '#333',
+								alpha: 1,
+								textAlign: 'middle',
+								textBaseline: 'bottom',
+								dx: 140,
+								dy: 40
+							},
+							{
+								type: 'text',
+								text: '发现一个好物,快来和我一起拼吧!',
+								size: 26,
+								color: '#333',
+								alpha: 1,
+								textAlign: 'middle',
+								textBaseline: 'middle',
+								dx: 140,
+								dy: 80
+							},
+							{
+								name: 'goodsImage',
+								type: 'image',
+								url: that.posterInfo.goods.image,
+								alpha: 1,
+								isBgCenter: true,
+								dy: 140,
+								dWidth: 620,
+								dHeight: 620,
+								roundRectSet: {
+									r: 20
+								}
+							},
+							{
+								type: 'text', //标题
+								text: that.posterInfo.goods.title,
+								size: 28,
+								color: '#333',
+								alpha: 1,
+								textAlign: 'left',
+								textBaseline: 'middle',
+								fontWeight: 'bold',
+								lineFeed: {
+									maxWidth: 620,
+									lineHeight: 40,
+									lineNum: 2
+								},
+								dx: 36,
+								dy: 780
+							},
+							{
+								type: 'text',
+								text: `拼团价:¥${that.posterInfo.goods.groupon_price}`,
+								size: 32,
+								color: '#E1212B',
+								alpha: 1,
+								textAlign: 'left',
+								textBaseline: 'middle',
+								fontWeight: 'bold',
+								fontFamily: 'OPPOSANS',
+								dx: 30,
+								dy: 860
+							},
+							{
+								type: 'text',
+								text: `${that.posterInfo.num}人团`,
+								size: 24,
+								color: '#fff',
+								alpha: 1,
+								textAlign: 'left',
+								textBaseline: 'top',
+								dx: 565,
+								dy: 863
+							},
+							// #ifdef MP-WEIXIN
+							{
+								name: 'wxCode',
+								type: 'image', //微信小程序码
+								url: `${that.$API_URL}wechat/wxacode?scene=${that.shareInfo.query}`,
+								alpha: 1,
+								dx: 530,
+								dy: 930,
+								dWidth: 110,
+								dHeight: 110
+							},
+							// #endif
+							// #ifndef MP-WEIXIN
+							{
+								type: 'qrcode',
+								text: that.shareInfo.path,
+								size: 110,
+								dx: 530,
+								dy: 930
+							}
+							// #endif
+						]
+					};
+					break;
+				default:
+					console.log('%cerr:没有此类型海报数据', 'color:green;background:yellow');
+					break;
+			}
+			return data;
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+// 指引
+.guide-wrap {
+	height: 100%;
+	.guide-img {
+		width: 580rpx;
+		height: 430rpx;
+	}
+}
+// 分享海报
+.poster-btn-box {
+	.cancel-btn {
+		width: 300rpx;
+		height: 70rpx;
+		line-height: 70rpx;
+		background: #ffffff;
+		border-radius: 35rpx;
+		font-size: 28rpx;
+		font-weight: 500;
+		color: #999999;
+	}
+	.save-btn {
+		width: 300rpx;
+		height: 70rpx;
+		line-height: 70rpx;
+		background: linear-gradient(90deg, #e9b461, #eecc89);
+		border-radius: 35rpx;
+		font-size: 28rpx;
+		font-weight: 500;
+		color: #ffffff;
+	}
+}
+.poster-img-box {
+	.poster-img {
+		width: 660rpx;
+		min-height: 800rpx;
+		border-radius: 20rpx;
+	}
+}
+// 分享tool
+.share-box {
+	background: #fff;
+	width: 750rpx;
+	border-radius: 30rpx 30rpx 0 0;
+	padding-top: 30rpx;
+	position: relative;
+
+	.share-foot {
+		font-size: 24rpx;
+		color: #bfbfbf;
+		height: 80rpx;
+		border-top: 1rpx solid #eee;
+	}
+
+	.share-list-box {
+		.share-btn {
+			background: none;
+			border: none;
+			line-height: 1;
+			padding: 0;
+			&::after {
+				border: none;
+			}
+		}
+		.share-item {
+			flex: 1;
+			padding-bottom: 20rpx;
+
+			.share-img {
+				width: 70rpx;
+				height: 70rpx;
+				background: rgba(246, 246, 254, 1);
+				border-radius: 50%;
+				margin-bottom: 20rpx;
+			}
+
+			.share-title {
+				font-size: 24rpx;
+				color: #666;
+			}
+		}
+	}
+}
+</style>

+ 589 - 0
components/shopro-sku/shopro-sku.vue

@@ -0,0 +1,589 @@
+<template>
+	<!-- 规格 -->
+	<view class="cu-modal bottom-modal" @touchmove.stop.prevent="" :class="{ show: showModal }"
+		@tap.stop="showModal = false" v-if="goodsInfo.sku_price">
+		<view class="cu-dialog" @tap.stop style="border-radius: 30rpx 30rpx 0 0;">
+			<view class="shop-modal page_box" :style="goodsInfo.is_sku == 0 ? 'height:700rpx' : ''">
+				<text class="u-iconfont uicon-close-circle-fill close-icon" @tap.stop="showModal = false"></text>
+				<!-- 商品卡片-->
+				<view class="top u-flex modal-head__box">
+					<image class="shop-img" :src="currentSkuPrice.image ? currentSkuPrice.image : goodsInfo.image"
+						mode="aspectFill"></image>
+					<view class=" goods-box u-flex-col u-row-between">
+						<view class="goods-title u-ellipsis-2">{{ goodsInfo.title }}</view>
+						<view class="u-flex u-row-between goods-bottom">
+							<view class="price-box u-flex">
+								<view v-if="goodsType === 'score'">{{ currentSkuPrice.price_text || goodsInfo.price }}
+								</view>
+								<view v-else-if="grouponBuyType === 'groupon'">
+									¥{{ currentSkuPrice.groupon_price || (goodsInfo.activity_type === 'groupon' ? goodsInfo.groupon_price : goodsInfo.price) }}
+								</view>
+								<view v-else>¥{{ currentSkuPrice.price || goodsInfo.price }}</view>
+							</view>
+							<text class="stock">库存{{ currentSkuPrice.stock || goodsInfo.stock }}件</text>
+						</view>
+					</view>
+				</view>
+
+				<!-- 规格选项 -->
+				<scroll-view scroll-y class="content_box">
+					<view class="select-box u-flex-col u-row-left" v-for="(s, x) in skuList" :key="s.id">
+						<view class="type-title u-flex">{{ s.name }}</view>
+						<view class="tag-box u-flex u-flex-wrap">
+							<button class="tag u-reset-button" v-for="(sc, y) in s.content" :key="sc.id"
+								:class="{ 'tag-active': currentSkuArray[sc.pid] == sc.id, 'tag-disabled': sc.disabled == true }"
+								:disabled="sc.disabled == true" @tap="chooseSku(sc.pid, sc.id)">
+								{{ sc.name }}
+							</button>
+						</view>
+					</view>
+
+					<!-- 计步器 -->
+					<view class="buy-num-box u-flex u-row-between">
+						<view class="num-title">购买数量</view>
+						<u-number-box v-model="goodsNum" :min="1" :step="1" :max="maxStep" @plus="plus"
+							:long-press="false" @change="changeNum"></u-number-box>
+					</view>
+				</scroll-view>
+
+				<!-- 功能按钮 -->
+				<view class="btn-box foot_box u-flex u-row-between" v-if="buyType === 'cart' || buyType === 'buy'">
+					<button class="u-reset-button cu-btn save-btn"
+						v-if="(activityRules && activityRules.status === 'ing') || !goodsInfo.activity_type"
+						@tap="confirm">确认</button>
+					<button class="u-reset-button cu-btn cancel-btn"
+						v-if="activityRules && activityRules.status !== 'ing' && goodsInfo.activity_type"
+						@tap="confirm">确定</button>
+				</view>
+				<view class="btn-box foot_box u-flex u-row-between" v-else>
+					<button class="u-reset-button cu-btn  cart-btn" @tap="confirmCart">加入购物车</button>
+					<button class="u-reset-button cu-btn  buy-btn" @tap="confirmBuy">立即购买</button>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	/**
+	 * 多规格组件
+	 * @property {Object} goodsInfo - 商品数据
+	 * @property {Boolean} value = showModal - 显隐
+	 * @property {String} buyType  - 购买方式
+	 * @property {String} goodsType - 商品类别
+	 * @property {String} grouponBuyType -拼团商品购买方式
+	 * @property {Number} grouponId - 拼团ID,分享进入
+	 * @property {Object} activityRules - 活动状态。
+	 */
+	import { mapMutations, mapActions, mapState } from 'vuex';
+	export default {
+		components: {},
+		data() {
+			return {
+				maxStep: 999,
+				skuList: [],
+				currentSkuPrice: {},
+				currentSkuArray: [],
+				goodsNum: 1,
+				confirmGoodsInfo: {},
+				type: this.buyType
+			};
+		},
+		props: {
+			goodsInfo: {},
+			activityRules: {},
+			value: {},
+			buyType: {
+				type: String,
+				default: 'sku'
+			},
+			goodsType: {
+				type: String,
+				default: 'goods'
+			},
+			grouponBuyType: {
+				type: String,
+				default: 'alone'
+			},
+			grouponId: {
+				//参加拼团的时候,传入当前团id;
+				type: Number,
+				default: 0
+			}
+		},
+		created() {
+			this.skuList = this.goodsInfo.sku;
+			this.changeDisabled(false);
+		},
+		mounted() {
+			// 单规格选项
+			if (!this.goodsInfo.is_sku) {
+				this.currentSkuPrice = this.skuPrice[0];
+				this.maxStep = this.skuPrice[0].stock > 999 ? 999 : this.skuPrice[0].stock;
+			}
+		},
+		watch: {
+			type(nweVal, oldVal) {
+				return newVal;
+			}
+		},
+		computed: {
+			skuPrice() {
+				return this.goodsInfo.sku_price;
+			},
+			showModal: {
+				get() {
+					return this.value;
+				},
+				set(val) {
+					val ? uni.hideTabBar() : uni.showTabBar();
+					this.$emit('input', val);
+					return val;
+				}
+			},
+			currentSkuText() {
+				let that = this;
+				let str = '';
+				let currentSkuArray = this.currentSkuArray;
+				currentSkuArray.forEach(v => {
+					that.skuList.forEach(s => {
+						s.content.forEach(u => {
+							if (u.id === v) {
+								str += ' ' + u.name;
+							}
+						});
+					});
+				});
+				that.$emit('getSkuText', str);
+				return str;
+			}
+		},
+
+		methods: {
+			...mapActions(['addCartGoods', 'getCartList']),
+			jump(path, parmas) {
+				this.$Router.push({
+					path: path,
+					query: parmas
+				});
+			},
+
+			// 选择规格
+			chooseSku(pid, skuId) {
+				let that = this;
+				let isChecked = true; // 选中 or 取消选中
+				this.goodsNum = 1; //选择规格时,数量重置为1.
+				this.maxStep = 999; //选择其他规格时,取消上个规格库存限制
+
+				if (that.currentSkuArray[pid] != undefined && that.currentSkuArray[pid] == skuId) {
+					// 点击已被选中的,删除并填充 ''
+					isChecked = false;
+					that.currentSkuArray.splice(pid, 1, '');
+				} else {
+					// 选中
+					that.$set(that.currentSkuArray, pid, skuId);
+				}
+
+				let chooseSkuId = []; // 选中的规格大类
+				that.currentSkuArray.forEach(sku => {
+					if (sku != '') {
+						// sku 为空是反选 填充的
+						chooseSkuId.push(sku);
+					}
+				});
+
+				// 当前所选规格下,所有可以选择的 skuPric
+				let newPrice = this.getCanUseSkuPrice();
+
+				// 判断所有规格大类是否选择完成
+				if (chooseSkuId.length == that.skuList.length && newPrice.length) {
+					that.currentSkuPrice = newPrice[0];
+				} else {
+					that.currentSkuPrice = {};
+				}
+
+				// 改变规格项禁用状态
+				this.changeDisabled(isChecked, pid, skuId);
+			},
+
+			// 改变禁用状态
+			changeDisabled(isChecked = false, pid = 0, skuId = 0) {
+				let newPrice = []; // 所有可以选择的 skuPrice
+
+				if (isChecked) {
+					// 选中规格
+
+					// 当前点击选中规格下的 所有可用 skuPrice
+					for (let price of this.skuPrice) {
+						if (price.stock <= 0) {
+							// this.goodsNum 不判断是否大于当前选择数量,在 uni-number-box 判断,并且 取 stock 和 goods_num 的小值
+							continue;
+						}
+						if (price.goods_sku_id_arr.indexOf(skuId.toString()) >= 0) {
+							newPrice.push(price);
+						}
+					}
+				} else {
+					// 取消选择规格
+
+					// 当前所选规格下,所有可以选择的 skuPric
+					newPrice = this.getCanUseSkuPrice();
+				}
+
+				// 所有存在并且有库存未选择的规格项 的 子项 id
+				let noChooseSkuIds = [];
+				for (let price of newPrice) {
+					noChooseSkuIds = noChooseSkuIds.concat(price.goods_sku_id_arr);
+				}
+
+				// 去重
+				noChooseSkuIds = Array.from(new Set(noChooseSkuIds));
+
+				if (isChecked) {
+					// 去除当前选中的规格项
+					let index = noChooseSkuIds.indexOf(skuId.toString());
+					noChooseSkuIds.splice(index, 1);
+				} else {
+					// 循环去除当前已选择的规格项
+					this.currentSkuArray.forEach(sku => {
+						if (sku.toString() != '') {
+							// sku 为空是反选 填充的
+							let index = noChooseSkuIds.indexOf(sku.toString());
+							if (index >= 0) {
+								// sku 存在于 noChooseSkuIds
+								noChooseSkuIds.splice(index, 1);
+							}
+						}
+					});
+				}
+
+				// 当前已选择的规格大类
+				let chooseSkuKey = [];
+				if (!isChecked) {
+					// 当前已选择的规格大类
+					this.currentSkuArray.forEach((sku, key) => {
+						if (sku != '') {
+							// sku 为空是反选 填充的
+							chooseSkuKey.push(key);
+						}
+					});
+				} else {
+					// 当前点击选择的规格大类
+					chooseSkuKey = [pid];
+				}
+
+				for (let i in this.skuList) {
+					// 当前点击的规格,或者取消选择时候 已选中的规格 不进行处理
+					if (chooseSkuKey.indexOf(this.skuList[i]['id']) >= 0) {
+						continue;
+					}
+
+					for (let j in this.skuList[i]['content']) {
+						// 如果当前规格项 id 不存在于有库存的规格项中,则禁用
+						if (noChooseSkuIds.indexOf(this.skuList[i]['content'][j]['id'].toString()) >= 0) {
+							this.skuList[i]['content'][j]['disabled'] = false;
+						} else {
+							this.skuList[i]['content'][j]['disabled'] = true;
+						}
+					}
+				}
+			},
+			// 当前所选规格下,获取所有有库存的 skuPrice
+			getCanUseSkuPrice() {
+				let newPrice = [];
+
+				for (let price of this.skuPrice) {
+					if (price.stock <= 0) {
+						// || price.stock < this.goodsNum		不判断是否大于当前选择数量,在 uni-number-box 判断,并且 取 stock 和 goods_num 的小值
+						continue;
+					}
+					var isOk = true;
+
+					this.currentSkuArray.forEach(sku => {
+						// sku 不为空,并且,这个 条 skuPrice 没有被选中,则排除
+						if (sku.toString() != '' && price.goods_sku_id_arr.indexOf(sku.toString()) < 0) {
+							isOk = false;
+						}
+					});
+
+					if (isOk) {
+						newPrice.push(price);
+					}
+				}
+
+				return newPrice;
+			},
+
+			// 数量
+			changeNum(e) {
+				this.changeDisabled(false);
+			},
+			// 增加
+			plus(e) {
+				if (e.value >= this.currentSkuPrice.stock) {
+					this.maxStep = this.currentSkuPrice.stock;
+					this.$u.toast('库存不足');
+					return;
+				}
+				if (this.goodsInfo.activity_type === 'seckill' || this.goodsInfo.activity_type === 'groupon') {
+					let rules = this.goodsInfo.activity.rules;
+					if (rules.limit_buy != 0 && e.value >= rules.limit_buy) {
+						this.maxStep = rules.limit_buy;
+						this.$u.toast('本次活动最多购买' + rules.limit_buy + '件');
+						return;
+					}
+				}
+			},
+
+			// 加入购物车确定
+			confirmCart() {
+				let that = this;
+				if (this.confirmSku()) {
+					let confirmGoodsList = {
+						list: [that.confirmGoodsInfo],
+						from: 'goods'
+					};
+					that.addCartGoods(confirmGoodsList).then(res => {
+						if (res.code === 1) {
+							that.showModal = false;
+							that.$u.toast(res.msg);
+						}
+					});
+				}
+			},
+			// 立即购买
+			confirmBuy() {
+				let that = this;
+				that.showModal = false;
+				if (this.confirmSku()) {
+					let confirmGoodsList = [];
+					confirmGoodsList.push(that.confirmGoodsInfo);
+					that.jump('/pages/order/confirm', {
+						goodsList: confirmGoodsList,
+						from: 'goods',
+						orderType: that.goodsType,
+						grouponBuyType: that.grouponBuyType,
+						grouponId: that.grouponId
+					});
+				}
+			},
+			// 确定
+			confirm() {
+				if (this.confirmSku()) {
+					switch (this.buyType) {
+						case 'cart':
+							this.confirmCart();
+							break;
+						case 'buy':
+							this.confirmBuy();
+							break;
+						default:
+					}
+				}
+			},
+			// 确定规格
+			confirmSku() {
+				let that = this;
+				if (that.currentSkuPrice.stock == 0 || that.currentSkuPrice.stock < that.goodsNum) {
+					that.$u.toast('库存不足');
+					that.showModal = false;
+					return false;
+				} else {
+					that.currentSkuPrice.goods_num = that.goodsNum;
+					that.confirmGoodsInfo = {
+						goods_id: that.currentSkuPrice.goods_id,
+						goods_num: that.currentSkuPrice.goods_num,
+						sku_price_id: that.currentSkuPrice.id,
+						goods_price: that.currentSkuPrice.price
+					};
+					if (!that.confirmGoodsInfo.sku_price_id) {
+						that.$u.toast('请选择规格');
+						return false;
+					} else {
+						that.showModal = false;
+						return true;
+					}
+				}
+			}
+		}
+	};
+</script>
+
+<style lang="scss" scoped>
+	.size-box {
+		line-height: 82rpx;
+		background: #fff;
+		padding: 0 20rpx;
+		margin: 20rpx 0;
+		font-size: 28rpx;
+
+		.title {
+			color: #999;
+			margin-right: 20rpx;
+		}
+	}
+
+	// 规格
+	.shop-modal {
+		width: 750rpx;
+		height: 950rpx;
+		background: rgba(255, 255, 255, 1);
+		padding: 20rpx 20rpx 30rpx;
+		position: relative;
+
+		.close-icon {
+			font-size: 34rpx;
+			color: #e0e0e0;
+			position: absolute;
+			top: 20rpx;
+			right: 20rpx;
+		}
+
+		// 商品卡片
+		.top {
+			margin: 30rpx 0;
+			border-radius: 20rpx;
+			padding: 20rpx;
+
+			.shop-img {
+				width: 160upx;
+				height: 160upx;
+				border-radius: 6upx;
+				margin-right: 30upx;
+				background: #ccc;
+			}
+
+			.goods-box {
+				height: 160upx;
+				width: 490rpx;
+				align-items: flex-start;
+
+				.goods-title {
+					font-size: 28rpx;
+
+					font-weight: 500;
+					color: rgba(51, 51, 51, 1);
+					line-height: 42rpx;
+					text-align: left;
+				}
+
+				.goods-bottom {
+					width: 100%;
+				}
+
+				.price-box {
+					font-size: 36upx;
+
+					font-weight: bold;
+					color: #e1212b;
+
+					.unit {
+						font-size: 24upx;
+
+						font-weight: bold;
+						color: #e1212b;
+					}
+				}
+
+				.stock {
+					font-size: 26rpx;
+					color: #999;
+				}
+			}
+		}
+
+		// 规格选项
+		.select-box {
+			margin-bottom: 30rpx;
+
+			.type-title {
+				font-size: 26upx;
+				font-weight: 400;
+				margin-bottom: 20upx;
+			}
+
+			.tag-box {
+				flex-wrap: wrap;
+			}
+
+			.tag {
+				line-height: 62rpx;
+				background: #f4f4f4;
+				border-radius: 31rpx;
+				font-size: 28upx;
+				font-weight: 400;
+				color: #666666;
+				padding: 0 30upx;
+				margin-bottom: 20rpx;
+				margin-right: 10rpx;
+			}
+
+			.tag-active {
+				background: linear-gradient(90deg, #e9b461, #eecc89);
+				font-size: 28rpx;
+				font-weight: 400;
+				color: #ffffff;
+			}
+
+			.tag-disabled {
+				background: #f8f8f8;
+				color: #cacaca;
+			}
+		}
+
+		.buy-num-box {
+			.num-title {
+				font-size: 26upx;
+
+				font-weight: 400;
+				margin-bottom: 20upx;
+			}
+		}
+	}
+
+	.btn-box {
+		height: 100rpx;
+
+		.cu-btn {
+			width: 340rpx;
+			line-height: 70rpx;
+			border-radius: 35rpx;
+			font-size: 28rpx;
+
+			font-weight: 500;
+			color: rgba(255, 255, 255, 0.9);
+			padding: 0;
+		}
+
+		.cart-btn {
+			background: linear-gradient(90deg, rgba(103, 104, 105, 1), rgba(82, 82, 82, 1));
+			box-shadow: 0px 2rpx 5rpx 0px rgba(102, 103, 104, 0.46);
+		}
+
+		.buy-btn {
+			background: linear-gradient(90deg, rgba(233, 180, 97, 1), rgba(238, 204, 137, 1));
+		}
+
+		.save-btn {
+			width: 710rpx;
+			height: 70rpx;
+			background: linear-gradient(90deg, rgba(233, 180, 97, 1), rgba(238, 204, 137, 1));
+			font-size: 28rpx;
+			font-weight: 500;
+			color: rgba(255, 255, 255, 1);
+			border-radius: 35rpx;
+			padding: 0;
+		}
+
+		.cancel-btn {
+			width: 710rpx;
+			height: 70rpx;
+			background: rgba(221, 221, 221, 1);
+			font-size: 28rpx;
+			font-weight: 500;
+			color: #999999;
+			border-radius: 35rpx;
+			padding: 0;
+		}
+	}
+</style>

+ 142 - 0
components/shopro-tabbar/shopro-tabbar.vue

@@ -0,0 +1,142 @@
+<template>
+	<view class="shopro-tabbar-wrap" v-if="tabbarList && tabbarList.length && showTabbar">
+		<view class="tabbar-box" :style="{ background: tabbarData.bgcolor || '#fff' }">
+			<view class="tabbar-item" v-for="(tab, index) in tabbarList" :key="tab.name" @tap="switchTabbar(tab, index)">
+				<view class="img-box">
+					<image
+						class="tabbar-icon"
+						v-if="tabbarData.style == 1 || tabbarData.style == 2"
+						:src="currentPath == tab.path ? tab.activeImage : tab.image"
+						mode="aspectFill"
+					></image>
+					<!-- 购物车角标 -->
+					<view v-if="tab.path == '/pages/index/cart' && cartNum" class="badge">{{ cartNum }}</view>
+				</view>
+
+				<view
+					class="tabbar-text"
+					v-if="tabbarData.style == 1 || tabbarData.style == 3"
+					:style="{ color: currentPath == tab.path ? tabbarData.activeColor : tabbarData.color }"
+				>
+					{{ tab.name }}
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+/**
+ * 自定义底部导航
+ * @property {Array} tabbarList - vuex初始化的底部导航数据
+ * @property {String} currentPath -computed解析当前页面路径。
+ */
+import { mapMutations, mapActions, mapState, mapGetters } from 'vuex';
+import { router } from '@/shopro/router';
+export default {
+	name: 'shoproTabbar',
+	components: {},
+	data() {
+		return {
+			currentPath: '' //当前页面路径
+		};
+	},
+	mounted() {
+		this.currentPath = this.$Route.path === '/pages/index/index' ? this.$Route.path : this.$Route.fullPath;
+	},
+	computed: {
+		...mapGetters(['cartNum', 'tabbarData']),
+		// 底部导航栏列表
+		tabbarList() {
+			if (this.tabbarData) {
+				return this.tabbarData.list;
+			}
+		},
+		// 后台tabbarList数据中必需含有'/pages/index/index',不然逻辑混乱
+		showTabbar() {
+			if (this.tabbarData && this.tabbarData.list) {
+				let arr = ['/pages/index/index'];
+				let path = '';
+				for (let item of this.tabbarData.list) {
+					arr.push(item.path);
+				}
+				return arr.includes(this.currentPath);
+			}
+		}
+	},
+	methods: {
+		// 切换tabbar
+		switchTabbar(tab, index) {
+			this.$tools.routerTo(tab.path, true);
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+.shopro-tabbar-wrap {
+	height: calc(100rpx + env(safe-area-inset-bottom) / 2);
+	padding-bottom: calc(env(safe-area-inset-bottom) / 2);
+	position: relative;
+	width: 100%;
+	z-index: 70;
+
+	.tabbar-box {
+		position: fixed;
+		display: flex;
+		align-items: center;
+		width: 100%;
+		height: calc(100rpx + env(safe-area-inset-bottom) / 2);
+		border-top: 1rpx solid #eeeeee;
+		background-color: #fff;
+		z-index: 998;
+		bottom: 0;
+		padding-bottom: calc(env(safe-area-inset-bottom) / 2);
+
+		.tabbar-item {
+			height: 100%;
+			display: flex;
+			flex-direction: column;
+			align-items: center;
+			justify-content: center;
+			flex: 1;
+
+			.img-box {
+				position: relative;
+
+				.badge {
+					position: absolute;
+					/* #ifndef APP-NVUE */
+					display: inline-flex;
+					/* #endif */
+					justify-content: center;
+					align-items: center;
+					line-height: 24rpx;
+					padding: 4rpx 10rpx;
+					border-radius: 100rpx;
+					color: #fff;
+					font-size: 24rpx;
+					z-index: 9;
+					background-color: $u-type-error;
+					transform: scale(0.8);
+					transform-origin: center center;
+					top: 0;
+					left: 46rpx;
+					white-space: nowrap;
+				}
+			}
+
+			.tabbar-icon {
+				width: 50rpx;
+				height: 50rpx;
+				display: block;
+			}
+
+			.tabbar-text {
+				font-size: 20rpx;
+				margin-top: 4rpx;
+			}
+		}
+	}
+}
+</style>

+ 9 - 0
env.js

@@ -0,0 +1,9 @@
+/**
+ *  Shopro全局配置文件
+ */
+
+export const BASE_URL = 'https://demo.shopro.top' //后台根域名 https://demo.shopro.top
+export const API_URL = `${BASE_URL}/addons/shopro/` //后台接口域名
+export const IMG_URL = 'http://file.shopro.top' //全局网络图片地址变量,css背景图片地址变量在uni.scss
+export const MAP_KEY = '426ebc3f1bbaced***89ee6061a98'; //高德地图开发者Web服务key,逆坐标解析
+export const HAS_LIVE = false //后台是否开通直播权限,根据情况在manifest.json中,开启注释相应组件的引入,pages.json中打开直播

+ 33 - 0
main.js

@@ -0,0 +1,33 @@
+import Vue from "vue";
+import App from "./App";
+import {
+	router,
+	RouterMount
+} from "@/shopro/router";
+import store from "@/shopro/store";
+import uView from "uview-ui";
+import shopro from "@/shopro";
+
+
+async function bootstrap() {
+	App.mpType = "app";
+	//引入路由
+	Vue.use(router);
+	// 引入全局uView
+	Vue.use(uView);
+	// 加载shopro
+	Vue.use(shopro);
+
+	const app = new Vue({
+		store,
+		...App
+	});
+	// #ifdef H5
+	RouterMount(app, router, "#app");
+	// #endif
+	// #ifndef H5
+	app.$mount();
+	// #endif
+}
+
+bootstrap();

+ 232 - 0
manifest.json

@@ -0,0 +1,232 @@
+{
+    "name" : "中宏商城",
+    "appid" : "",
+    "description" : "中宏商城",
+    "versionName" : "1.3.8",
+    "versionCode" : 137,
+    "transformPx" : false,
+    "app-plus" : {
+        // APP-VUE分包,可提APP升启动速度,2.7.12开始支持,兼容微信小程序分包方案,默认关闭
+        "optimization" : {
+            "subPackages" : true
+        },
+        "runmode" : "liberate", // 开启分包优化后,必须配置资源释放模式
+        "safearea" : {
+            //APP底部安全区域
+            "background" : "#fff",
+            "bottom" : {
+                "offset" : "auto"
+            }
+        },
+        "splashscreen" : {
+            "alwaysShowBeforeRender" : true,
+            "waiting" : true,
+            "autoclose" : true,
+            "delay" : 200
+        },
+        "usingComponents" : true,
+        "nvueCompiler" : "uni-app",
+        "compilerVersion" : 3,
+        "modules" : {
+            "Geolocation" : {},
+            "Maps" : {},
+            "OAuth" : {},
+            "Payment" : {},
+            "Share" : {},
+            "VideoPlayer" : {}
+        },
+        "distribute" : {
+            "android" : {
+                "permissions" : [
+                    "<uses-feature android:name=\"android.hardware.camera\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CALL_PHONE\"/>",
+                    "<uses-permission android:name=\"android.permission.CAMERA\"/>",
+                    "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
+                    "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
+                    "<uses-permission android:name=\"android.permission.INTERNET\"/>",
+                    "<uses-permission android:name=\"android.permission.MODIFY_AUDIO_SETTINGS\"/>",
+                    "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_CONTACTS\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.RECORD_AUDIO\"/>",
+                    "<uses-permission android:name=\"android.permission.VIBRATE\"/>",
+                    "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
+                    "<uses-permission android:name=\"android.permission.WRITE_CONTACTS\"/>",
+                    "<uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"/>",
+                    "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
+                ],
+                "abiFilters" : [ "armeabi-v7a", "arm64-v8a" ]
+            },
+            "ios" : {
+                "capabilities" : {
+                    "entitlements" : {
+                        "com.apple.developer.associated-domains" : [ "" ]
+                    }
+                },
+                "privacyDescription" : {
+                    "NSLocationAlwaysAndWhenInUseUsageDescription" : "需要同意获取您的位置信息才能展示附近信息",
+                    "NSLocationAlwaysUsageDescription" : "需要同意获取您的位置信息才能展示附近信息",
+                    "NSLocationWhenInUseUsageDescription" : "需要同意获取您的位置信息才能展示附近信息",
+                    "NSPhotoLibraryAddUsageDescription" : "需要同意访问您的相册才能保存图片",
+                    "NSPhotoLibraryUsageDescription" : "需要同意访问您的相册才能选取图片",
+                    "NSCameraUsageDescription" : "需要同意访问您的摄像头才能拍摄照片",
+                    "NSUserTrackingUsageDescription" : "请放心,开启权限不会获取您在其他站点的隐私信息,该权限仅用于标识设备并保障服务安全与提升浏览体验"
+                }
+            },
+            "sdkConfigs" : {
+                "ad" : {},
+                "payment" : {
+                    "alipay" : {
+                        "__platform__" : [ "ios", "android" ]
+                    },
+                    "weixin" : {
+                        "__platform__" : [ "ios", "android" ],
+                        "appid" : "",
+                        "UniversalLinks" : ""
+                    }
+                },
+                "geolocation" : {
+                    "amap" : {
+                        "__platform__" : [ "android" ],
+                        "appkey_ios" : "",
+                        "appkey_android" : ""
+                    }
+                },
+                "maps" : {
+                    "amap" : {
+                        "appkey_ios" : "",
+                        "appkey_android" : ""
+                    }
+                },
+                "oauth" : {
+                    "weixin" : {
+                        "appid" : "",
+                        "appsecret" : "",
+                        "UniversalLinks" : ""
+                    },
+                    "apple" : {}
+                },
+                "share" : {
+                    "weixin" : {
+                        "appid" : "",
+                        "UniversalLinks" : ""
+                    }
+                },
+                "push" : {}
+            },
+            "icons" : {
+                "android" : {
+                    "hdpi" : "",
+                    "xhdpi" : "",
+                    "xxhdpi" : "",
+                    "xxxhdpi" : ""
+                },
+                "ios" : {
+                    "appstore" : "",
+                    "ipad" : {
+                        "app" : "",
+                        "app@2x" : "",
+                        "notification" : "",
+                        "notification@2x" : "",
+                        "proapp@2x" : "",
+                        "settings" : "",
+                        "settings@2x" : "",
+                        "spotlight" : "",
+                        "spotlight@2x" : ""
+                    },
+                    "iphone" : {
+                        "app@2x" : "",
+                        "app@3x" : "",
+                        "notification@2x" : "",
+                        "notification@3x" : "",
+                        "settings@2x" : "",
+                        "settings@3x" : "",
+                        "spotlight@2x" : "",
+                        "spotlight@3x" : ""
+                    }
+                }
+            }
+        },
+        "kernel" : {
+            "ios" : "WKWebview" //或者 "WKWebview"  
+        }
+    },
+    "quickapp" : {},
+    "mp-weixin" : {
+        "appid" : "",
+        "setting" : {
+            "urlCheck" : false,
+            "es6" : true, //消除大多数样式警告
+            "minified" : true,
+            "postcss" : true,
+            "checkSiteMap" : false // 关闭页面索引检测
+        },
+        "optimization" : {
+            "subPackages" : true
+        },
+        "plugins" : {},
+        //这段代码,需要放到plugins中。
+        //只有后台开通小程序直播功能权限,才可以打开,不然报错,程序无法运行。
+        // "live-player-plugin": {
+        // 	"version": "1.3.2", // 注意填写该直播组件最新版本号,微信开发者工具调试时可获取最新版本号
+        // 	"provider": "wx2b03c6e691cd7370" // 必须填该直播组件appid,该示例值即为直播组件appid
+        // }
+        "usingComponents" : true,
+        "permission" : {
+            "scope.userLocation" : {
+                "desc" : "计算附近商家位置"
+            }
+        }
+    },
+    "mp-alipay" : {
+        "usingComponents" : true,
+        "component2" : true
+    },
+    "mp-qq" : {
+        "appid" : ""
+    },
+    "mp-baidu" : {
+        "usingComponents" : true,
+        "appid" : ""
+    },
+    "mp-toutiao" : {
+        "usingComponents" : true,
+        "appid" : ""
+    },
+    "h5" : {
+        "template" : "template.h5.html",
+        "router" : {
+            "mode" : "history",
+            "base" : ""
+        },
+        "optimization" : {
+            "treeShaking" : {
+                "enable" : true
+            }
+        },
+        "title" : "中宏商城",
+        "devServer" : {
+            "https" : false,
+            "port" : ""
+        },
+        "sdkConfigs" : {
+            "maps" : {
+                "qqmap" : {
+                    "key" : ""
+                }
+            }
+        },
+        "domain" : ""
+    },
+    "_spaceID" : "",
+    "vueVersion" : "2"
+}

+ 220 - 0
package-lock.json

@@ -0,0 +1,220 @@
+{
+	"name": "shopro-plus",
+	"version": "1.0.0",
+	"lockfileVersion": 2,
+	"requires": true,
+	"packages": {
+		"": {
+			"name": "shopro-plus",
+			"version": "1.0.0",
+			"license": "ISC",
+			"dependencies": {
+				"jweixin-module": "^1.6.0",
+				"uni-read-pages": "^1.0.5",
+				"uni-simple-router": "^2.0.6",
+				"vconsole": "^3.7.0"
+			},
+			"devDependencies": {}
+		},
+		"node_modules/_@babel_runtime@7.19.4@@babel": {},
+		"node_modules/_@babel_runtime@7.19.4@@babel/runtime": {
+			"name": "@babel/runtime",
+			"version": "7.19.4",
+			"license": "MIT",
+			"dependencies": {
+				"regenerator-runtime": "^0.13.4"
+			},
+			"engines": {
+				"node": ">=6.9.0"
+			}
+		},
+		"node_modules/_@babel_runtime@7.19.4@@babel/runtime/node_modules/regenerator-runtime": {
+			"resolved": "node_modules/_regenerator-runtime@0.13.10@regenerator-runtime",
+			"link": true
+		},
+		"node_modules/_copy-text-to-clipboard@3.0.1@copy-text-to-clipboard": {
+			"name": "copy-text-to-clipboard",
+			"version": "3.0.1",
+			"license": "MIT",
+			"engines": {
+				"node": ">=12"
+			},
+			"funding": {
+				"url": "https://github.com/sponsors/sindresorhus"
+			}
+		},
+		"node_modules/_core-js@3.25.5@core-js": {
+			"name": "core-js",
+			"version": "3.25.5",
+			"hasInstallScript": true,
+			"license": "MIT",
+			"funding": {
+				"type": "opencollective",
+				"url": "https://opencollective.com/core-js"
+			}
+		},
+		"node_modules/_jweixin-module@1.6.0@jweixin-module": {
+			"name": "jweixin-module",
+			"version": "1.6.0",
+			"license": "ISC"
+		},
+		"node_modules/_mutation-observer@1.0.3@mutation-observer": {
+			"name": "mutation-observer",
+			"version": "1.0.3"
+		},
+		"node_modules/_regenerator-runtime@0.13.10@regenerator-runtime": {
+			"name": "regenerator-runtime",
+			"version": "0.13.10",
+			"license": "MIT"
+		},
+		"node_modules/_uni-read-pages@1.0.5@uni-read-pages": {
+			"name": "uni-read-pages",
+			"version": "1.0.5",
+			"hasInstallScript": true,
+			"license": "ISC"
+		},
+		"node_modules/_uni-simple-router@2.0.7@uni-simple-router": {
+			"name": "uni-simple-router",
+			"version": "2.0.7",
+			"license": "MIT"
+		},
+		"node_modules/_vconsole@3.14.7@vconsole": {
+			"name": "vconsole",
+			"version": "3.14.7",
+			"license": "MIT",
+			"dependencies": {
+				"@babel/runtime": "^7.17.2",
+				"copy-text-to-clipboard": "^3.0.1",
+				"core-js": "^3.11.0",
+				"mutation-observer": "^1.0.3"
+			}
+		},
+		"node_modules/_vconsole@3.14.7@vconsole/node_modules/@babel/runtime": {
+			"resolved": "node_modules/_@babel_runtime@7.19.4@@babel/runtime",
+			"link": true
+		},
+		"node_modules/_vconsole@3.14.7@vconsole/node_modules/copy-text-to-clipboard": {
+			"resolved": "node_modules/_copy-text-to-clipboard@3.0.1@copy-text-to-clipboard",
+			"link": true
+		},
+		"node_modules/_vconsole@3.14.7@vconsole/node_modules/core-js": {
+			"resolved": "node_modules/_core-js@3.25.5@core-js",
+			"link": true
+		},
+		"node_modules/_vconsole@3.14.7@vconsole/node_modules/mutation-observer": {
+			"resolved": "node_modules/_mutation-observer@1.0.3@mutation-observer",
+			"link": true
+		},
+		"node_modules/jweixin-module": {
+			"resolved": "node_modules/_jweixin-module@1.6.0@jweixin-module",
+			"link": true
+		},
+		"node_modules/uni-read-pages": {
+			"resolved": "node_modules/_uni-read-pages@1.0.5@uni-read-pages",
+			"link": true
+		},
+		"node_modules/uni-simple-router": {
+			"resolved": "node_modules/_uni-simple-router@2.0.7@uni-simple-router",
+			"link": true
+		},
+		"node_modules/vconsole": {
+			"resolved": "node_modules/_vconsole@3.14.7@vconsole",
+			"link": true
+		}
+	},
+	"dependencies": {
+		"_@babel_runtime@7.19.4@@babel": {},
+		"_copy-text-to-clipboard@3.0.1@copy-text-to-clipboard": {
+			"version": "npm:copy-text-to-clipboard@3.0.1"
+		},
+		"_core-js@3.25.5@core-js": {
+			"version": "npm:core-js@3.25.5"
+		},
+		"_jweixin-module@1.6.0@jweixin-module": {
+			"version": "npm:jweixin-module@1.6.0"
+		},
+		"_mutation-observer@1.0.3@mutation-observer": {
+			"version": "npm:mutation-observer@1.0.3"
+		},
+		"_regenerator-runtime@0.13.10@regenerator-runtime": {
+			"version": "npm:regenerator-runtime@0.13.10"
+		},
+		"_uni-read-pages@1.0.5@uni-read-pages": {
+			"version": "npm:uni-read-pages@1.0.5"
+		},
+		"_uni-simple-router@2.0.7@uni-simple-router": {
+			"version": "npm:uni-simple-router@2.0.7"
+		},
+		"_vconsole@3.14.7@vconsole": {
+			"version": "npm:vconsole@3.14.7",
+			"requires": {
+				"@babel/runtime": "^7.17.2",
+				"copy-text-to-clipboard": "^3.0.1",
+				"core-js": "^3.11.0",
+				"mutation-observer": "^1.0.3"
+			},
+			"dependencies": {
+				"@babel/runtime": {
+					"version": "file:node_modules/_@babel_runtime@7.19.4@@babel/runtime",
+					"requires": {
+						"regenerator-runtime": "^0.13.4"
+					},
+					"dependencies": {
+						"regenerator-runtime": {
+							"version": "file:node_modules/_regenerator-runtime@0.13.10@regenerator-runtime"
+						}
+					}
+				},
+				"copy-text-to-clipboard": {
+					"version": "file:node_modules/_copy-text-to-clipboard@3.0.1@copy-text-to-clipboard"
+				},
+				"core-js": {
+					"version": "file:node_modules/_core-js@3.25.5@core-js"
+				},
+				"mutation-observer": {
+					"version": "file:node_modules/_mutation-observer@1.0.3@mutation-observer"
+				}
+			}
+		},
+		"jweixin-module": {
+			"version": "file:node_modules/_jweixin-module@1.6.0@jweixin-module"
+		},
+		"uni-read-pages": {
+			"version": "file:node_modules/_uni-read-pages@1.0.5@uni-read-pages"
+		},
+		"uni-simple-router": {
+			"version": "file:node_modules/_uni-simple-router@2.0.7@uni-simple-router"
+		},
+		"vconsole": {
+			"version": "file:node_modules/_vconsole@3.14.7@vconsole",
+			"requires": {
+				"@babel/runtime": "^7.17.2",
+				"copy-text-to-clipboard": "^3.0.1",
+				"core-js": "^3.11.0",
+				"mutation-observer": "^1.0.3"
+			},
+			"dependencies": {
+				"@babel/runtime": {
+					"version": "file:node_modules/_@babel_runtime@7.19.4@@babel/runtime",
+					"requires": {
+						"regenerator-runtime": "^0.13.4"
+					},
+					"dependencies": {
+						"regenerator-runtime": {
+							"version": "file:node_modules/_regenerator-runtime@0.13.10@regenerator-runtime"
+						}
+					}
+				},
+				"copy-text-to-clipboard": {
+					"version": "file:node_modules/_copy-text-to-clipboard@3.0.1@copy-text-to-clipboard"
+				},
+				"core-js": {
+					"version": "file:node_modules/_core-js@3.25.5@core-js"
+				},
+				"mutation-observer": {
+					"version": "file:node_modules/_mutation-observer@1.0.3@mutation-observer"
+				}
+			}
+		}
+	}
+}

+ 24 - 0
package.json

@@ -0,0 +1,24 @@
+{
+	"name": "shopro-plus",
+	"version": "1.0.0",
+	"main": "main.js",
+	"scripts": {
+		"test": ""
+	},
+	"repository": {
+		"type": "git",
+		"url": ""
+	},
+	"keywords": [],
+	"author": "",
+	"license": "ISC",
+	"homepage": "",
+	"dependencies": {
+		"jweixin-module": "^1.6.0",
+		"uni-read-pages": "^1.0.5",
+		"uni-simple-router": "^2.0.6",
+		"vconsole": "^3.7.0"
+	},
+	"description": "",
+	"devDependencies": {}
+}

+ 921 - 0
pages.json

@@ -0,0 +1,921 @@
+{
+	"easycom": {
+		"^u-(.*)": "@/uview-ui/components/u-$1/u-$1.vue",
+		"^shopro-(.*)": "@/components/shopro-$1/shopro-$1.vue"
+	},
+
+	"pages": [{
+			"path": "pages/index/index",
+			"aliasPath": "/", //对于h5端你必须在首页加上aliasPath并设置为/
+			"style": {
+				"navigationBarTitleText": "首页",
+				"navigationBarTextStyle": "white",
+				"enablePullDownRefresh": true,
+				"navigationStyle": "custom",
+				"mp-alipay": {
+					"transparentTitle": "always",
+					"titlePenetrate": "YES",
+					"allowsBounceVertical": "NO"
+				}
+			},
+			"meta": {
+				"auth": false, //需要登录
+				"async": true, //是否同步
+				"title": "首页", //标题
+				"group": "商城" //分组
+			}
+		},
+		{
+			"path": "pages/index/category",
+			"style": {
+				"navigationBarTitleText": "分类"
+			},
+			"meta": {
+				"auth": false,
+				"async": true,
+				"title": "分类",
+				"group": "商城"
+			}
+		},
+		{
+			"path": "pages/index/cart",
+			"style": {
+				"navigationBarTitleText": "购物车"
+			},
+			"meta": {
+				"auth": false,
+				"async": true,
+				"title": "购物车",
+				"group": "商城"
+			}
+		},
+		{
+			"path": "pages/index/user",
+			"style": {
+				"navigationBarTitleText": "我的",
+				"navigationStyle": "custom",
+				"navigationBarTextStyle": "white",
+				"enablePullDownRefresh": true
+			},
+			"meta": {
+				"auth": false,
+				"async": true,
+				"title": "我的",
+				"group": "商城"
+			}
+		},
+		{
+			"path": "pages/index/view",
+			"name": "view",
+			"style": {
+				"navigationBarTitleText": "",
+				"enablePullDownRefresh": true
+
+			},
+			"meta": {
+				"auth": false,
+				"async": true,
+				"title": "自定义页面",
+				"group": "商城"
+			}
+		}
+	],
+	"subPackages": [{
+			"root": "pages/activity",
+			"pages": [{
+					"path": "sign/index",
+					"style": {
+						"navigationBarTitleText": "签到中心"
+					},
+					"meta": {
+						"auth": true,
+						"async": true,
+						"title": "签到中心",
+						"group": "应用"
+					}
+				},
+				{
+					"path": "seckill/list",
+					"style": {
+						"navigationBarTitleText": "限时秒杀",
+						"enablePullDownRefresh": true
+					},
+					"meta": {
+						"auth": false,
+						"async": true,
+						"title": "限时秒杀",
+						"group": "秒杀"
+					}
+				},
+				{
+					"path": "groupon/list",
+					"style": {
+						"navigationBarTitleText": "今日必拼",
+						"enablePullDownRefresh": true
+					},
+					"meta": {
+						"auth": false,
+						"async": true,
+						"title": "今日必拼",
+						"group": "拼团"
+					}
+				},
+				{
+					"path": "groupon/detail",
+					"style": {
+						"navigationBarTitleText": "拼团详情",
+						"enablePullDownRefresh": true
+					},
+					"meta": {
+						"auth": false,
+						"async": false,
+						"title": "拼团详情",
+						"group": "拼团"
+					}
+				},
+				{
+					"path": "groupon/my-groupon",
+					"style": {
+						"navigationBarTitleText": "我的拼团",
+						"enablePullDownRefresh": true
+					},
+					"meta": {
+						"auth": true,
+						"async": true,
+						"title": "我的拼团",
+						"group": "拼团"
+					}
+				},
+				{
+					"path": "discounts/list",
+					"style": {
+						"navigationBarTitleText": "优惠活动商品"
+					},
+					"meta": {
+						"auth": false,
+						"async": false,
+						"title": "优惠活动商品",
+						"group": "活动"
+					}
+				}
+			]
+		},
+		{
+			"root": "pages/app",
+			"pages": [
+				// {
+				// 	"path": "live/list",
+				// 	"style": {
+				// 		"navigationBarTitleText": "直播列表"
+				// 	},
+				// 	"meta": {
+				// 		"auth": false,
+				// 		"async": false,
+				// 		"title": "直播列表",
+				// 		"group": "直播"
+				// 	}
+				// },
+				{
+					"path": "score/list",
+					"style": {
+						"navigationBarTitleText": "积分商品"
+					},
+					"meta": {
+						"auth": false,
+						"async": true,
+						"title": "积分商品",
+						"group": "积分"
+					}
+				},
+				{
+					"path": "coupon/list",
+					"style": {
+						"navigationBarTitleText": "优惠券中心",
+						"navigationStyle": "custom"
+
+
+					},
+					"meta": {
+						"auth": true,
+						"async": true,
+						"title": "优惠券中心",
+						"group": "优惠券"
+					}
+				}, {
+					"path": "coupon/detail",
+					"style": {
+						"navigationBarTitleText": "优惠券详情",
+						"navigationStyle": "custom"
+					},
+					"meta": {
+						"auth": true,
+						"async": true,
+						"title": "优惠券详情",
+						"group": "优惠券"
+					}
+				},
+				{
+					"path": "merchant/index",
+					"style": {
+						"navigationBarTitleText": "门店中心",
+						"navigationStyle": "custom",
+						"enablePullDownRefresh": true,
+						"navigationBarTextStyle": "white"
+
+					},
+					"meta": {
+						"auth": true,
+						"async": false,
+						"title": "门店中心",
+						"group": "门店"
+					}
+				},
+				{
+					"path": "merchant/apply",
+					"style": {
+						"navigationBarTitleText": "门店入驻",
+						"navigationStyle": "custom"
+					},
+					"meta": {
+						"auth": true,
+						"async": true,
+						"title": "门店入驻",
+						"group": "门店"
+					}
+				},
+				{
+					"path": "merchant/detail",
+					"style": {
+						"navigationBarTitleText": "订单详情"
+					},
+					"meta": {
+						"auth": true,
+						"async": false,
+						"title": "订单详情",
+						"group": "订单"
+					}
+				},
+				{
+					"path": "merchant/info",
+					"style": {
+						"navigationBarTitleText": "门店详情"
+					},
+					"meta": {
+						"auth": true,
+						"async": false,
+						"title": "门店详情",
+						"group": "门店"
+					}
+				},
+				{
+					"path": "merchant/list",
+					"style": {
+						"navigationBarTitleText": "我的门店"
+					},
+					"meta": {
+						"auth": true,
+						"async": true,
+						"title": "我的门店",
+						"group": "门店"
+					}
+				},
+				{
+					"path": "commission/index",
+					"style": {
+						"navigationBarTitleText": "分销中心",
+						"enablePullDownRefresh": true,
+						"navigationStyle": "custom",
+						"navigationBarTextStyle": "white"
+					},
+					"meta": {
+						"auth": true,
+						"async": true,
+						"title": "分销中心",
+						"group": "分销"
+					}
+				},
+				{
+					"path": "commission/team",
+					"style": {
+						"navigationBarTitleText": "我的团队",
+						"navigationStyle": "custom",
+						"navigationBarTextStyle": "white"
+					},
+					"meta": {
+						"auth": true,
+						"async": true,
+						"title": "我的团队",
+						"group": "分销"
+					}
+				},
+				{
+					"path": "commission/commission-log",
+					"style": {
+						"navigationBarTitleText": "佣金明细",
+						"enablePullDownRefresh": true
+					},
+					"meta": {
+						"auth": true,
+						"async": true,
+						"title": "佣金明细",
+						"group": "分销"
+					}
+				},
+				{
+					"path": "commission/order",
+					"style": {
+						"navigationBarTitleText": "分销订单",
+						"navigationStyle": "custom",
+						"navigationBarTextStyle": "white",
+						"enablePullDownRefresh": true
+					},
+					"meta": {
+						"auth": true,
+						"async": true,
+						"title": "分销订单",
+						"group": "分销"
+					}
+				},
+				{
+					"path": "commission/goods",
+					"style": {
+						"navigationBarTitleText": "推广商品"
+
+
+					},
+					"meta": {
+						"auth": true,
+						"async": true,
+						"title": "推广商品",
+						"group": "分销"
+					}
+				},
+				{
+					"path": "commission/apply",
+					"style": {
+						"navigationBarTitleText": "申请分销商",
+						"navigationStyle": "custom",
+						"navigationBarTextStyle": "white"
+
+					},
+					"meta": {
+						"auth": true,
+						"async": true,
+						"title": "申请分销商",
+						"group": "分销"
+					}
+				},
+				{
+					"path": "commission/rankings",
+					"style": {
+						"navigationBarTitleText": "分销排行",
+						"navigationStyle": "custom",
+						"navigationBarTextStyle": "white"
+
+					},
+					"meta": {
+						"auth": true,
+						"async": true,
+						"title": "分销排行",
+						"group": "分销"
+					}
+				},
+				{
+					"path": "commission/share-log",
+					"style": {
+						"navigationBarTitleText": "分享记录",
+						"navigationStyle": "custom",
+						"navigationBarTextStyle": "white",
+						"enablePullDownRefresh": true
+
+					},
+					"meta": {
+						"auth": true,
+						"async": false,
+						"title": "分享记录",
+						"group": "分销"
+					}
+				}
+
+			]
+		},
+		{
+			"root": "pages/goods",
+			"pages": [{
+					"path": "list",
+					"style": {
+						"navigationBarTitleText": "商品列表",
+						"navigationStyle": "custom"
+					},
+					"meta": {
+						"auth": false,
+						"async": true,
+						"title": "商品列表",
+						"group": "商品"
+					}
+				},
+				{
+					"path": "detail",
+					"style": {
+						"navigationBarTitleText": "商品详情",
+						"navigationStyle": "custom"
+					},
+					"meta": {
+						"auth": false,
+						"async": true,
+						"title": "商品详情",
+						"group": "商品"
+					}
+				},
+				{
+					"path": "comment/add-comment",
+					"style": {
+						"navigationBarTitleText": "评价"
+					},
+					"meta": {
+						"auth": false,
+						"async": false,
+						"title": "评价",
+						"group": "商品"
+					}
+				},
+				{
+					"path": "comment/comment-list",
+					"style": {
+						"navigationBarTitleText": "评价列表"
+					},
+					"meta": {
+						"auth": false,
+						"async": false,
+						"title": "评价列表",
+						"group": "商品"
+					}
+				}
+			]
+		},
+		{
+			"root": "pages/order",
+			"pages": [{
+					"path": "confirm",
+					"style": {
+						"navigationBarTitleText": "确认订单"
+					},
+					"meta": {
+						"auth": true,
+						"async": false,
+						"title": "确认订单",
+						"group": "订单"
+					}
+				},
+				{
+					"path": "payment/method",
+					"style": {
+						"navigationBarTitleText": "收银台"
+					},
+					"meta": {
+						"auth": true,
+						"async": false,
+						"title": "收银台",
+						"group": "订单"
+					}
+				},
+				{
+					"path": "payment/result",
+					"style": {
+						"navigationBarTitleText": "支付结果"
+					},
+					"meta": {
+						"auth": false,
+						"async": false,
+						"title": "支付结果",
+						"group": "订单"
+					}
+				},
+				{
+					"path": "list",
+					"style": {
+						"navigationBarTitleText": "订单列表"
+					},
+					"meta": {
+						"auth": true,
+						"async": true,
+						"title": "订单列表",
+						"group": "订单"
+					}
+				},
+				{
+					"path": "detail",
+					"style": {
+						"navigationBarTitleText": "订单详情"
+					},
+					"meta": {
+						"auth": true,
+						"async": false,
+						"title": "订单详情",
+						"group": "订单"
+					}
+				},
+				{
+					"path": "after-sale/detail",
+					"style": {
+						"navigationBarTitleText": "售后详情"
+					},
+					"meta": {
+						"auth": true,
+						"async": false,
+						"title": "售后详情",
+						"group": "订单"
+					}
+				},
+				{
+					"path": "after-sale/list",
+					"style": {
+						"navigationBarTitleText": "售后列表"
+					},
+					"meta": {
+						"auth": true,
+						"async": true,
+						"title": "售后列表",
+						"group": "订单"
+					}
+				},
+				{
+					"path": "after-sale/log",
+					"style": {
+						"navigationBarTitleText": "售后记录"
+					},
+					"meta": {
+						"auth": true,
+						"async": false,
+						"title": "售后记录",
+						"group": "订单"
+					}
+				},
+				{
+					"path": "after-sale/refund",
+					"style": {
+						"navigationBarTitleText": "申请售后"
+					},
+					"meta": {
+						"auth": true,
+						"async": false,
+						"title": "申请售后",
+						"group": "订单"
+					}
+				},
+				{
+					"path": "express/distribution-detail",
+					"style": {
+						"navigationBarTitleText": "配送详情"
+					},
+					"meta": {
+						"auth": true,
+						"async": false,
+						"title": "配送详情",
+						"group": "订单"
+					}
+				},
+				{
+					"path": "express/express-detail",
+					"style": {
+						"navigationBarTitleText": "物流详情"
+					},
+					"meta": {
+						"auth": true,
+						"async": false,
+						"title": "物流详情",
+						"group": "订单"
+					}
+				},
+				{
+					"path": "express/express-list",
+					"style": {
+						"navigationBarTitleText": "包裹列表"
+					},
+					"meta": {
+						"auth": true,
+						"async": false,
+						"title": "包裹列表",
+						"group": "订单"
+					}
+				},
+
+				{
+					"path": "express/store-address",
+					"style": {
+						"navigationBarTitleText": "选择自提点",
+						"navigationStyle": "custom",
+						"navigationBarTextStyle": "white"
+
+					},
+					"meta": {
+						"auth": true,
+						"async": false,
+						"title": "选择自提点",
+						"group": "订单"
+					}
+				}
+
+			]
+		},
+		{
+			"root": "pages/public",
+			"pages": [{
+					"path": "faq",
+					"style": {
+						"navigationBarTitleText": "常见问题"
+					},
+					"meta": {
+						"auth": false,
+						"async": true,
+						"title": "常见问题",
+						"group": "通用"
+					}
+				},
+				{
+					"path": "feedback",
+					"style": {
+						"navigationBarTitleText": "问题反馈"
+					},
+					"meta": {
+						"auth": true,
+						"async": true,
+						"title": "问题反馈",
+						"group": "通用"
+					}
+				},
+				{
+					"path": "chat/index",
+					"style": {
+						"navigationBarTitleText": "客服",
+						"navigationStyle": "custom",
+						"navigationBarTextStyle": "white"
+
+					},
+					"meta": {
+						"auth": true,
+						"async": true,
+						"title": "客服",
+						"group": "通用"
+					}
+				},
+				{
+					"path": "search",
+					"style": {
+						"navigationBarTitleText": "搜索"
+					},
+					"meta": {
+						"auth": false,
+						"async": true,
+						"title": "搜索",
+						"group": "通用"
+					}
+				},
+				{
+					"path": "richtext",
+					"style": {
+						"navigationBarTitleText": ""
+					},
+					"meta": {
+						"auth": false,
+						"async": true,
+						"title": "富文本",
+						"group": "通用"
+					}
+				},
+				{
+					"path": "webview",
+					"style": {
+						"navigationBarTitleText": ""
+					},
+					"meta": {
+						"auth": false,
+						"async": true,
+						"title": "外链",
+						"group": "通用"
+					}
+				},
+				{
+					"path": "404",
+					"name": "404",
+					"style": {
+						"navigationBarTitleText": "页面不存在"
+					}
+				},
+				{
+					"path": "loading",
+					"style": {
+						"navigationBarTitleText": "",
+						"navigationStyle": "custom",
+						"enablePullDownRefresh": false
+					}
+
+				}
+			]
+		},
+		{
+			"root": "pages/user",
+			"pages": [{
+					"path": "info",
+					"style": {
+						"navigationBarTitleText": "个人信息"
+					},
+					"meta": {
+						"auth": true,
+						"async": true,
+						"title": "个人信息",
+						"group": "用户"
+					}
+				},
+				{
+					"path": "set",
+					"style": {
+						"navigationBarTitleText": "系统设置"
+					},
+					"meta": {
+						"auth": false,
+						"async": true,
+						"title": "系统设置",
+						"group": "通用"
+					}
+				},
+				{
+					"path": "view-log",
+					"style": {
+						"navigationBarTitleText": "浏览足迹"
+					},
+					"meta": {
+						"auth": true,
+						"async": true,
+						"title": "浏览足迹",
+						"group": "用户"
+					}
+				},
+				{
+					"path": "wallet/index",
+					"style": {
+						"navigationBarTitleText": "我的钱包"
+
+					},
+					"meta": {
+						"auth": true,
+						"async": true,
+						"title": "钱包",
+						"group": "用户"
+					}
+				},
+				{
+					"path": "wallet/withdraw",
+					"style": {
+						"navigationBarTitleText": "",
+						"navigationStyle": "custom"
+
+					},
+					"meta": {
+						"auth": true,
+						"async": true,
+						"title": "提现",
+						"group": "用户"
+					}
+				},
+				{
+					"path": "wallet/withdraw-log",
+					"style": {
+						"navigationBarTitleText": "提现记录"
+
+					},
+					"meta": {
+						"auth": true,
+						"async": true,
+						"title": "提现记录",
+						"group": "用户"
+					}
+				},
+				{
+					"path": "wallet/top-up",
+					"style": {
+						"navigationBarTitleText": "充值",
+						"navigationStyle": "custom"
+
+					},
+					"meta": {
+						"auth": true,
+						"async": true,
+						"title": "充值",
+						"group": "用户"
+					}
+				},
+				{
+					"path": "wallet/top-up-log",
+					"style": {
+						"navigationBarTitleText": "充值记录"
+
+					},
+					"meta": {
+						"auth": true,
+						"async": true,
+						"title": "充值记录",
+						"group": "用户"
+					}
+				},
+				{
+					"path": "wallet/score-balance",
+					"style": {
+						"navigationBarTitleText": "积分余额",
+						"navigationStyle": "custom"
+
+					},
+					"meta": {
+						"auth": true,
+						"async": true,
+						"title": "积分余额",
+						"group": "积分"
+					}
+				},
+				{
+					"path": "address/list",
+					"style": {
+						"navigationBarTitleText": "收货地址"
+
+					},
+					"meta": {
+						"auth": true,
+						"async": true,
+						"title": "收货地址",
+						"group": "用户"
+					}
+				},
+				{
+					"path": "address/edit",
+					"style": {
+						"navigationBarTitleText": ""
+
+					},
+					"meta": {
+						"auth": true,
+						"async": false,
+						"title": "修改地址",
+						"group": "用户"
+					}
+				},
+				{
+					"path": "favorite",
+					"style": {
+						"navigationBarTitleText": "我的收藏"
+					},
+					"meta": {
+						"auth": true,
+						"async": true,
+						"title": "我的收藏",
+						"group": "用户"
+					}
+				}
+			]
+		}
+	],
+	"preloadRule": {
+		"pages/index/index": {
+			"network": "all",
+			"packages": ["pages/activity", "pages/user", "pages/goods", "pages/app", "pages/public",
+				"pages/order"
+			]
+		}
+	},
+	"globalStyle": {
+		"navigationBarTextStyle": "black",
+		"navigationBarTitleText": "shopro-plus",
+		"navigationBarBackgroundColor": "#FFFFFF",
+		"backgroundColor": "#FFFFFF"
+	},
+	"tabBar": {
+		"color": "#333",
+		"selectedColor": "#a8700d",
+		"backgroundColor": "#ffffff",
+		"borderStyle": "black",
+		"list": [{
+				"pagePath": "pages/index/index",
+				"text": "首页",
+				"iconPath": "static/images/tabbar/tabbar_home.png",
+				"selectedIconPath": "/static/images/tabbar/tabbar_home1.png"
+			},
+			{
+				"pagePath": "pages/index/category",
+				"text": "分类",
+				"iconPath": "static/images/tabbar/tabbar_category.png",
+				"selectedIconPath": "/static/images/tabbar/tabbar_category1.png"
+			},
+			{
+				"pagePath": "pages/index/cart",
+				"text": "购物车",
+				"iconPath": "static/images/tabbar/tabbar_cart.png",
+				"selectedIconPath": "/static/images/tabbar/tabbar_cart1.png"
+			},
+			{
+				"pagePath": "pages/index/user",
+				"text": "我的",
+				"iconPath": "static/images/tabbar/tabbar_personal.png",
+				"selectedIconPath": "/static/images/tabbar/tabbar_personal1.png"
+			}
+		]
+	}
+}

+ 227 - 0
pages/activity/discounts/list.vue

@@ -0,0 +1,227 @@
+<!-- 商品列表 -->
+<template>
+	<view class="list-box">
+		<!-- 倒计时 -->
+		<u-sticky ref="uSticky" bgColor="#f6f6f6" offsetTop="0">
+			<view class="tip-list-box">
+				<view class="tip-list u-flex-col u-flex-1 u-p-30">
+					<view class="count-down-box u-flex u-col-center">
+						<view class="u-m-r-10">{{ timeRule.title }}</view>
+						<u-count-down
+							v-if="timeRule.time"
+							class="count-down-demo"
+							:timestamp="timeRule.time / 1000"
+							separator-color="#333 "
+							bg-color="#FF0000 "
+							ref="uCountDown"
+							color="#fff"
+							@end="getTime"
+							autoplay
+						></u-count-down>
+					</view>
+					<view class="u-flex u-flex-1 u-col-center modal-item u-flex-wrap u-m-t-10" v-if="activityInfo.tags">
+						<view class="title-tag cu-tag bg-red sm radius u-m-r-10 u-m-t-10" >{{ activityInfo.title }}</view>
+						<view class="tag-box cu-tag line-red sm radius u-m-r-10 u-m-t-10" v-for="(tag, index) in activityInfo.tags" :key="tag">{{ tag }}</view>
+					</view>
+				</view>
+			</view>
+		</u-sticky>
+		<!-- 商品列表 -->
+		<view class="u-waterfall u-p-16" v-if="!isEmpty">
+			<view id="u-left-column" class="u-column">
+				<view class="goods-item u-m-b-16 u-flex u-row-center u-col-center" v-for="leftGoods in leftList" :key="leftGoods.id">
+					<shopro-goods-card
+						:detail="leftGoods"
+						:type="leftGoods.activity_type"
+						:image="leftGoods.image"
+						:title="leftGoods.title"
+						:subtitle="leftGoods.subtitle"
+						:price="leftGoods.price"
+						:originPrice="leftGoods.original_price"
+						:sales="leftGoods.sales"
+						:tagTextList="leftGoods.activity_discounts_tags"
+						@click="$Router.push({ path: '/pages/goods/detail', query: { id: leftGoods.id } })"
+					></shopro-goods-card>
+				</view>
+			</view>
+			<view id="u-right-column" class="u-column">
+				<view class="goods-item u-m-b-16 u-flex u-row-center u-col-center" v-for="rightGoods in rightList" :key="rightGoods.id">
+					<shopro-goods-card
+						:detail="rightGoods"
+						:type="rightGoods.activity_type"
+						:image="rightGoods.image"
+						:title="rightGoods.title"
+						:subtitle="rightGoods.subtitle"
+						:price="rightGoods.price"
+						:originPrice="rightGoods.original_price"
+						:sales="rightGoods.sales"
+						:tagTextList="rightGoods.activity_discounts_tags"
+						@click="$Router.push({ path: '/pages/goods/detail', query: { id: rightGoods.id } })"
+					></shopro-goods-card>
+				</view>
+			</view>
+		</view>
+
+		<!-- 缺省页 -->
+		<shopro-empty v-if="isEmpty" :image="$IMG_URL + '/imgs/empty/empty_goods.png'" tipText="暂无该商品,还有更多好货等着你噢~"></shopro-empty>
+
+		<!-- 加载更多 -->
+		<u-loadmore v-show="goodsList.length" height="80rpx" :status="loadStatus" icon-type="flower" color="#ccc" />
+
+		<!-- 登录弹窗 -->
+		<shopro-auth-modal></shopro-auth-modal>
+	</view>
+</template>
+
+<script>
+import { mapMutations, mapActions, mapState } from 'vuex';
+export default {
+	components: {},
+	data() {
+		return {
+			activityInfo: {},
+			timeRule: {
+				title: '',
+				time: 0
+			},
+			isEmpty: false,
+			goodsList: [],
+			loadStatus: 'loadmore', //loadmore-加载前的状态,loading-加载中的状态,nomore-没有更多的状态
+			lastPage: 1,
+			currentPage: 1,
+			
+			// 瀑布流 350-330
+			addTime: 100, //排序间隙时间
+			leftHeight: 0,
+			rightHeight: 0,
+			leftList: [],
+			rightList: [],
+			tempList: []
+		};
+	},
+	// 触底加载更多
+	onReachBottom() {
+		if (this.currentPage < this.lastPage) {
+			this.currentPage += 1;
+			this.getGoodsList();
+		}
+	},
+	onLoad() {
+		this.activityInfo = this.$Route.query;
+		this.getGoodsList();
+		this.getCartList();
+	},
+	mounted() {
+		this.$nextTick(() => {
+			this.getTime();
+		});
+	},
+	methods: {
+		...mapActions(['getCartList']),
+		// 瀑布流相关
+		async splitData() {
+			if (!this.tempList.length) return;
+			let item = this.tempList[0];
+			if (!item) return;
+		
+			// 分左右
+			if (this.leftHeight < this.rightHeight) {
+				this.leftHeight += item.activity_discounts_tags.length ? 350 : 330;
+				this.leftList.push(item);
+			} else if (this.leftHeight > this.rightHeight) {
+				this.rightHeight += item.activity_discounts_tags.length ? 350 : 330;
+				this.rightList.push(item);
+			} else {
+				this.leftHeight += item.activity_discounts_tags.length ? 350 : 330;
+				this.leftList.push(item);
+			}
+		
+			// 移除临时列表的第一项,如果临时数组还有数据,继续循环
+			this.tempList.splice(0, 1);
+			if (this.tempList.length) {
+				setTimeout(() => {
+					this.splitData();
+				}, this.addTime);
+			}
+		},
+		clear() {
+			this.leftList = [];
+			this.rightList = [];
+			this.leftHeight = 0;
+			this.rightHeight = 0;
+			this.tempList = [];
+		},
+		
+		// 计算倒计时
+		getTime() {
+			let nowTime = new Date().getTime();
+			let { endtime, starttime } = this.activityInfo;
+			endtime = endtime * 1000;
+			starttime = starttime * 1000;
+			// 当前时间小于开始时间,未开始
+			if (nowTime < starttime) {
+				this.timeRule.title = '距开始';
+				this.timeRule.time = starttime - nowTime;
+			}
+			// 当前时间大于开始时间,小于结束时间,进行中
+			if (nowTime > starttime && nowTime < endtime) {
+				this.timeRule.title = '距结束';
+				this.timeRule.time = endtime - nowTime;
+			}
+			// 当前时间大于结束时间,已结束
+			if (nowTime > endtime) {
+				this.timeRule.title = '已结束';
+				this.timeRule.time = 0;
+			}
+		},
+		// 商品列表
+		getGoodsList() {
+			let that = this;
+			that.loadStatus = 'loading';
+			that.$http('goods.lists', {
+				goods_ids: that.activityInfo.goods_ids,
+				page: that.currentPage
+			}).then(res => {
+				if (res.code === 1) {
+					that.goodsList = [...that.goodsList, ...res.data.data];
+					that.isEmpty = !that.goodsList.length;
+					that.lastPage = res.data.last_page;
+					that.loadStatus = that.currentPage < res.data.last_page ? 'loadmore' : 'nomore';
+					that.tempList = res.data.data;
+					that.splitData();
+				}
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+@mixin vue-flex($direction: row) {
+	/* #ifndef APP-NVUE */
+	display: flex;
+	flex-direction: $direction;
+	/* #endif */
+}
+.u-waterfall {
+	@include vue-flex;
+	flex-direction: row;
+	align-items: flex-start;
+}
+
+.u-column {
+	@include vue-flex;
+	flex: 1;
+	flex-direction: column;
+	height: auto;
+}
+.tip-list-box {
+	background-color: #f6f6f6;
+	padding: 20rpx;
+}
+.tip-list {
+	font-size: 28rpx;
+	background-color: #fff;
+	border-radius: 20rpx;
+}
+</style>

+ 371 - 0
pages/activity/groupon/detail.vue

@@ -0,0 +1,371 @@
+<!-- 拼团详情 -->
+<template>
+	<view class="page_box">
+		<!-- 商品卡片 -->
+		<view class="head_box">
+			<view class="goods-card u-p-x-20 u-p-y-40" v-if="grouponDetail.id">
+				<shopro-mini-card
+					:title="grouponDetail.goods.title"
+					:image="grouponDetail.goods.image"
+					:subtitle="grouponDetail.goods.subtitle"
+					:price="grouponDetail.goods.groupon_price"
+					:originPrice="grouponDetail.goods.original_price"
+				>
+					<template #describe>
+						<view class="u-flex u-m-b-20">
+							<view class="sell-box">
+								<text class=" hot-icon iconfont icon-icon-test"></text>
+								<text class="sell-num">已拼{{ grouponDetail.goods.sales }}件</text>
+							</view>
+							<text class="group-num">{{ grouponDetail.num || 0 }}人团</text>
+						</view>
+					</template>
+				</shopro-mini-card>
+			</view>
+		</view>
+
+		<view class="content_box">
+			<view class="u-flex-col u-row-between group-box">
+				<!-- 拼团成功 -->
+				<view class="u-flex u-row-center u-col-center" v-if="grouponDetail.status === 'finish' || grouponDetail.status === 'finish-fictitious'">
+					<view class="tip-box u-flex" v-if="grouponDetail.my">
+						<view class="u-iconfont uicon-checkmark-circle-fill u-m-r-10" style="color:#42B111;font-size:34rpx ;"></view>
+						<text>恭喜您~拼团成功!</text>
+					</view>
+					<view class="tip-box u-flex" v-else>
+						<view class="u-iconfont uicon-close-circle-fill u-m-r-10" style="color:#E1212B;font-size:34rpx ;"></view>
+						<text>对不起~您来晚了,该团已满</text>
+					</view>
+				</view>
+
+				<!--  拼团失败-->
+				<view class="u-flex u-row-center u-col-center" v-if="grouponDetail.status === 'invalid'">
+					<view class="tip-box u-flex">
+						<view class="u-iconfont uicon-close-circle-fill u-m-r-10" style="color:#E1212B;font-size:34rpx ;"></view>
+						<text>{{ grouponDetail.my ? '对不起~拼团已过期!已全额退款' : '对不起~拼团已过期!您来晚了~' }}</text>
+					</view>
+				</view>
+
+				<!-- 拼团中 -->
+				<view class="title-box u-flex u-row-center u-col-center" v-if="grouponDetail.status === 'ing'">
+					<!-- 活动结束 -->
+					<view class="" v-if="grouponDetail.activity_status === 'ended'">
+						<view class="tip-box u-flex u-col-center">
+							<view class="u-iconfont uicon-close-circle-fill u-m-r-10" style="color:#E1212B;font-size:34rpx ;"></view>
+							<text>对不起~拼团活动已结束!您来晚了~</text>
+						</view>
+					</view>
+					<view v-else class="u-flex u-col-center">
+						<view class="title-text">
+							待成团,还差
+							<text class="group-num">{{ grouponDetail.num - grouponDetail.current_num }}</text>
+							人拼团成功
+						</view>
+						<view class="count-down u-flex" v-if="time">
+							<text class="count-down-tip">倒计时</text>
+							<view class="time-box u-flex">
+								<view class="count-text">{{ time.h || '00' }}</view>
+								:
+								<view class="count-text">{{ time.m || '00' }}</view>
+								:
+								<view class="count-text">{{ time.s || '00' }}</view>
+							</view>
+						</view>
+					</view>
+				</view>
+
+				<!-- 拼团人 -->
+				<view class="group-people u-flex u-row-center">
+					<view class="img-box" v-for="(team, index) in grouponDetail.groupon_log" :key="team.user_avatar">
+						<view class="tag" v-if="index == 0">团长</view>
+						<image class="avatar" :class="{ leader: index == 0 }" :src="team.user_avatar" mode="aspectFill"></image>
+					</view>
+					<view class="img-box" v-for="(base, index) in surplusNum" :key="base" v-if="index < 100">
+						<image class="avatar" :src="$IMG_URL + '/imgs/group/base_group_avatar.png'" mode="aspectFill"></image>
+					</view>
+				</view>
+
+				<!-- 底部按钮 -->
+				<view class="btn-box u-flex u-row-center u-col-center">
+					<!-- 拼团中 -->
+					<view v-if="grouponDetail.status === 'ing'">
+						<block v-if="grouponDetail.activity_status === 'ended'">
+							<button class="u-reset-button btn2" v-if="grouponDetail.my" @tap.stop="jump('/pages/order/detail', { id: grouponDetail.my.order_id })">查看订单</button>
+						</block>
+						<block v-else>
+							<button class="u-reset-button btn1" v-if="grouponDetail.my" @tap="showShare = true">邀请好友参团</button>
+							<button class="u-reset-button btn1" v-else @tap="showSku = true">立即参团</button>
+						</block>
+					</view>
+					<!-- 拼团成功/失败-->
+					<view v-if="grouponDetail.status === 'finish' || grouponDetail.status === 'finish-fictitious' || grouponDetail.status === 'invalid'">
+						<button class="u-reset-button btn2" v-if="grouponDetail.my" @tap.stop="jump('/pages/order/detail', { id: grouponDetail.my.order_id })">查看订单</button>
+						<button class="u-reset-button btn1" v-else @tap="jump('/pages/goods/detail', { id: grouponDetail.goods_id })">我要开团</button>
+					</view>
+				</view>
+			</view>
+
+			<!-- 玩法 -->
+			<view v-if="activity && activity.id" class="groupon-play u-flex u-row-between u-col-center" @tap="jump('/pages/public/richtext', { id: activity.richtext_id })">
+				<text class="title">玩法</text>
+				<view class="u-flex u-col-center">
+					<view class="description u-ellipsis-1">{{ activity.richtext_title || '开团/参团·邀请好友·人满发货(不满退款' }}</view>
+					<view class="u-iconfont uicon-arrow-right u-m-r-10" style="color:#999;font-size:28rpx ;"></view>
+				</view>
+			</view>
+		</view>
+
+		<!-- 邀请好友 -->
+		<shopro-share v-model="showShare" :posterInfo="grouponDetail" :posterType="'groupon'"></shopro-share>
+		<!-- 规格弹窗 -->
+
+		<shopro-sku
+			v-if="grouponDetail.goods"
+			v-model="showSku"
+			:activityRules="{ status: grouponDetail.status }"
+			:goodsInfo="grouponDetail.goods"
+			:grouponId="grouponDetail.id"
+			buyType="buy"
+			grouponBuyType="groupon"
+		></shopro-sku>
+
+		<!-- 登录提示 -->
+		<shopro-auth-modal></shopro-auth-modal>
+	</view>
+</template>
+
+<script>
+let timer = null;
+import share from '@/shopro/share';
+export default {
+	components: {},
+	data() {
+		return {
+			time: 0,
+			grouponDetail: {},
+			showShare: false,
+			showSku: false,
+			activity: {},
+			surplusNum: 0 //剩余拼团人数、
+		};
+	},
+	computed: {},
+	onLoad() {
+		this.getGrouponDetail();
+	},
+	onPullDownRefresh() {
+		this.getGrouponDetail();
+	},
+	onHide() {
+		clearInterval(timer);
+		timer = null;
+	},
+	onUnload() {
+		share.setShareInfo();
+	},
+	methods: {
+		// 路由跳转
+		jump(path, parmas) {
+			this.$Router.push({
+				path: path,
+				query: parmas
+			});
+		},
+
+		// 倒计时
+		countDown(t) {
+			const that = this;
+			timer = setInterval(() => {
+				if (t > 0) {
+					that.time = that.$tools.format(t);
+					t--;
+				} else {
+					clearInterval(timer);
+					that.time = false;
+				}
+			}, 1000);
+		},
+
+		// 拼团详情
+		getGrouponDetail() {
+			let that = this;
+			that.$http('goods.grouponDetail', {
+				id: that.$Route.query.id
+			}).then(res => {
+				uni.stopPullDownRefresh();
+				that.grouponDetail = res.data;
+				that.activity = res.data.activity;
+				if (that.activity) {
+					that.activity.richtext_id = parseInt(that.activity.richtext_id);
+				}
+				that.surplusNum = res.data.num - res.data.current_num;
+
+				let newTime = new Date().getTime();
+				let endTime = res.data.expiretime * 1000;
+				let t = endTime - newTime;
+				that.countDown(t / 1000);
+
+				// 设置分享信息
+				share.setShareInfo({
+					title: that.grouponDetail.goods.title,
+					desc: that.grouponDetail.goods.subtitle,
+					image: that.grouponDetail.goods.image,
+					params: {
+						page: 3,
+						pageId: that.$Route.query.id
+					}
+				});
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+// 商品卡片
+.head_box {
+	background: url($IMG_URL+'/imgs/group/group_detail_bg.png') no-repeat;
+	background-size: 100% 316rpx;
+	padding: 60rpx 20rpx 20rpx;
+	.goods-card {
+		background-color: #fff;
+		border-radius: 20rpx;
+		.group-num {
+			font-size: 24rpx;
+
+			font-weight: 400;
+			color: rgba(153, 153, 153, 1);
+			margin-left: 30rpx;
+		}
+		.sell-box {
+			line-height: 32rpx;
+			background: rgba(255, 224, 226, 0.3);
+			border-radius: 16rpx;
+			padding: 0 10rpx;
+
+			.hot-icon {
+				font-size: 26rpx;
+				color: #e1212b;
+				margin-right: 8rpx;
+			}
+
+			.sell-num {
+				font-size: 20rpx;
+				color: #f7979c;
+			}
+		}
+	}
+}
+
+// 拼团人
+.group-box {
+	background: #fff;
+	padding: 40rpx 0;
+	min-height: 370rpx;
+	justify-content: center;
+	.tip-box {
+		font-size: 28rpx;
+		font-weight: bold;
+	}
+	.title-box {
+		font-size: 26rpx;
+		font-weight: bold;
+		color: #333;
+		.group-num {
+			color: #f8002c;
+		}
+		.count-down-tip {
+			font-size: 24rpx;
+			padding-left: 10rpx;
+		}
+		.time-box {
+			font-size: 18rpx;
+			.count-text {
+				display: inline-block;
+				background-color: #383a46;
+				color: #fff;
+				font-size: 18rpx;
+				border-radius: 2rpx;
+				padding: 0 5rpx;
+				height: 28rpx;
+				text-align: center;
+				line-height: 28rpx;
+				margin: 0 6rpx;
+			}
+		}
+	}
+
+	.group-people {
+		flex-wrap: wrap;
+		padding: 50rpx;
+		.img-box {
+			position: relative;
+			margin-right: 30rpx;
+			&:nth-child(6n) {
+				margin-right: 0;
+			}
+			.tag {
+				position: absolute;
+				line-height: 36rpx;
+				background: linear-gradient(132deg, rgba(243, 223, 177, 1), rgba(243, 223, 177, 1), rgba(236, 190, 96, 1));
+				border-radius: 14rpx;
+				padding: 0 10rpx;
+				white-space: nowrap;
+				font-size: 24rpx;
+				color: #784f06;
+				z-index: 2;
+				top: -20rpx;
+				left: 50%;
+				transform: translateX(-50%);
+			}
+			.avatar {
+				width: 80rpx;
+				height: 80rpx;
+				border-radius: 50%;
+				background: #ccc;
+			}
+			.leader {
+				border: 4rpx solid rgba(237, 195, 108, 1);
+			}
+		}
+	}
+	.btn-box {
+		width: 750rpx;
+		.btn1 {
+			width: 690rpx;
+			line-height: 70rpx;
+			background: linear-gradient(90deg, rgba(254, 131, 42, 1), rgba(255, 102, 0, 1));
+			box-shadow: 0px 7rpx 6rpx 0px rgba(255, 104, 4, 0.22);
+			border-radius: 35rpx;
+			font-size: 28rpx;
+
+			font-weight: 500;
+			color: rgba(255, 255, 255, 1);
+		}
+		.btn2 {
+			width: 690rpx;
+			line-height: 70rpx;
+			border: 1rpx solid rgba(223, 223, 223, 1);
+			border-radius: 35rpx;
+			font-size: 28rpx;
+
+			font-weight: 500;
+			color: rgba(153, 153, 153, 1);
+		}
+	}
+}
+.groupon-play {
+	height: 94rpx;
+	border-top: 1rpx solid rgba(#dfdfdf, 0.5);
+	padding: 0 20rpx;
+	background: #fff;
+	.title {
+		font-size: 28rpx;
+		color: #999;
+	}
+	.description {
+		font-size: 28rpx;
+		text-align: right;
+	}
+}
+</style>

+ 246 - 0
pages/activity/groupon/list.vue

@@ -0,0 +1,246 @@
+<!-- 拼购列表 -->
+<template>
+	<view class="groupon-wrap">
+		<view class="group-head u-flex u-row-between">
+			<text class="group-head__title">爆款推荐</text>
+			<text class="group-head__notice">省钱省心限时拼</text>
+		</view>
+		<view class="group-box">
+			<view class="goods-item u-m-b-16" v-for="(item, index) in grouponList" :key="item.id" @tap="$Router.push({ path: '/pages/goods/detail', query: { id: item.id } })">
+				<view class="big-goods u-p-20 u-flex u-col-top">
+					<image v-if="index < 3" class="top-tag" :src="tagMap[index]" mode=""></image>
+					<image class="goods-img" lazy-load fade-show :src="item.image" mode="aspectFill"></image>
+					<view class=" card-right u-m-l-20 u-flex-col u-row-between">
+						<view class="">
+							<view class="goods-title u-ellipsis-1  u-m-t-10 u-m-b-10">
+								<view class="title-tag cu-tag bg-orange sm radius u-m-r-10">拼团</view>
+								{{ item.title }}
+							</view>
+							<view v-show="item.subtitle" class="subtitle-text u-m-b-10 u-ellipsis-1">{{ item.subtitle }}</view>
+						</view>
+
+						<view class="u-flex u-m-y-20">
+							<view class="sell-box">
+								<text class=" hot-icon iconfont icon-icon-test"></text>
+								<text class="sell-num">已拼{{ item.sales }}件</text>
+							</view>
+							<text class="group-num">{{ item.activity.rules.team_num || 0 }}人团</text>
+						</view>
+
+						<view class=" u-flex u-row-between u-col-center">
+							<view class="u-flex u-col-bottom font-OPPOSANS">
+								<view class="price u-m-r-10">{{ item.groupon_price }}</view>
+								<view class="origin-price">{{ item.original_price }}</view>
+							</view>
+							<button class="u-reset-button buy-btn">马上拼</button>
+						</view>
+					</view>
+				</view>
+			</view>
+			<!-- 空白 -->
+			<shopro-empty
+				v-if="!grouponList.length && !isLoading"
+				style="padding-top: 200rpx;"
+				marginTop="0"
+				:image="$IMG_URL + '/imgs/empty/empty_goods.png'"
+				tipText="暂无拼团商品,敬请期待~"
+			></shopro-empty>
+			<!-- 加载更多 -->
+		</view>
+
+		<u-loadmore v-if="grouponList.length" height="80rpx" :status="loadStatus" icon-type="flower" color="#ccc" />
+	</view>
+</template>
+
+<script>
+export default {
+	components: {},
+	data() {
+		return {
+			tagMap: {
+				0: this.$IMG_URL + '/imgs/group/groupon_top1.png',
+				1: this.$IMG_URL + '/imgs/group/groupon_top2.png',
+				2: this.$IMG_URL + '/imgs/group/groupon_top3.png'
+			},
+			isLoading: true,
+			loadStatus: 'loadmore', //loadmore:加载前的状态,loading:加载中的状态,nomore:没有更多的状态
+			lastPage: 1,
+			currentPage: 1,
+			grouponList: []
+		};
+	},
+	onLoad() {
+		this.getGrouponList();
+	},
+	onReachBottom() {
+		this.loadMore();
+	},
+	onPullDownRefresh() {
+		this.currentPage = 1;
+		this.lastPage = 1;
+		this.grouponList = [];
+		this.getGrouponList();
+	},
+	computed: {},
+	methods: {
+		// 路由跳转
+		jump(path, parmas) {
+			this.$Router.push({
+				path: path,
+				query: parmas
+			});
+		},
+		// 加载更多
+		loadMore() {
+			if (this.currentPage < this.lastPage) {
+				this.currentPage += 1;
+				this.getGrouponList();
+			}
+		},
+		// 拼团列表
+		getGrouponList() {
+			let that = this;
+			that.isLoading = true;
+			that.loadStatus = 'loading';
+			that.$http('goods.grouponList', {
+				page: that.currentPage
+			}).then(res => {
+				that.isLoading = false;
+				uni.stopPullDownRefresh();
+				if (res.code === 1) {
+					that.grouponList = [...that.grouponList, ...res.data.data];
+					that.lastPage = res.data.last_page;
+					that.loadStatus = that.currentPage < res.data.last_page ? 'loadmore' : 'nomore';
+				}
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+// 背景
+.groupon-wrap {
+	background: url($IMG_URL+'/imgs/group/group_list_bg.png') no-repeat;
+	background-size: 100% 374rpx;
+}
+.group-head {
+	padding: 0 25rpx;
+	height: 100rpx;
+	.group-head__title {
+		font-size: 32rpx;
+
+		font-weight: 500;
+		color: rgba(255, 255, 255, 1);
+	}
+	.group-head__notice {
+		font-size: 26rpx;
+
+		font-weight: 500;
+		color: rgba(255, 255, 255, 1);
+	}
+}
+
+.group-box {
+	width: 710rpx;
+	background: linear-gradient(#fff, #f5f5f5);
+	border-radius: 20rpx;
+	margin: 0 auto;
+	min-height: 800rpx;
+	.goods-item {
+		border-radius: 20rpx;
+		background-color: #fff;
+		// 大商品卡片
+		.big-goods {
+			width: 710rpx;
+			height: 260rpx;
+			background: #ffffff;
+			box-shadow: 0px 7rpx 8rpx 1rpx rgba(254, 76, 29, 0.05);
+			border-radius: 20rpx;
+			position: relative;
+			.goods-img {
+				width: 220rpx;
+				height: 220rpx;
+				border-radius: 6rpx;
+			}
+			.top-tag {
+				position: absolute;
+				z-index: 3;
+				top: 20rpx;
+				left: 20rpx;
+				width: 84rpx;
+				height: 36rpx;
+			}
+			.card-right {
+				width: 430rpx;
+				height: 220rpx;
+			}
+			.goods-title {
+				font-size: 26rpx;
+				font-weight: 600;
+				width: 400rpx;
+				color: #000000;
+				vertical-align: middle;
+			}
+			.subtitle-text {
+				font-size: 22rpx;
+				width: 400rpx;
+				font-weight: 500;
+				color: #666666;
+			}
+			.buy-btn {
+				width: 120rpx;
+				line-height: 50rpx;
+				background: linear-gradient(90deg, #ff6600 0%, #fe832a 100%);
+				border-radius: 25rpx;
+				font-size: 24rpx;
+				font-weight: 500;
+				color: #ffffff;
+			}
+			// 拼团
+			.sell-box {
+				background: rgba(#f9efd6, 0.3);
+				border-radius: 16rpx;
+				line-height: 32rpx;
+				.sell-num {
+					font-size: 20rpx;
+
+					font-weight: 400;
+					color: #ff6904;
+				}
+
+				.hot-icon {
+					font-size: 26rpx;
+					color: #ff6904;
+					margin-right: 8rpx;
+				}
+			}
+			.group-num {
+				font-size: 20rpx;
+
+				font-weight: 500;
+				color: rgba(153, 153, 153, 1);
+				margin-left: 20rpx;
+			}
+			// 价格
+			.price {
+				color: #ff0000;
+				font-weight: 600;
+				&::before {
+					content: '¥';
+					font-size: 20rpx;
+				}
+			}
+			.origin-price {
+				color: #c4c4c4;
+				font-size: 24rpx;
+				text-decoration: line-through;
+				&::before {
+					content: '¥';
+					font-size: 20rpx;
+				}
+			}
+		}
+	}
+}
+</style>

+ 247 - 0
pages/activity/groupon/my-groupon.vue

@@ -0,0 +1,247 @@
+<!-- 我的拼团 -->
+<template>
+	<view class="page_box">
+		<!-- tab -->
+		<view class="head_box">
+			<view class="order-nav u-flex">
+				<view class="nav-item u-flex-col u-flex-1 u-row-center u-col-center" v-for="nav in groupState" :key="nav.id" @tap="onNav(nav.id)">
+					<view class="item-title">{{ nav.title }}</view>
+					<text class="nav-line" :class="{ 'line-active': stateId === nav.id }"></text>
+				</view>
+			</view>
+		</view>
+
+		<view class="content_box">
+			<scroll-view scroll-y="true" enable-back-to-top @scrolltolower="loadMore" refresher-background="#f5f5f5" class="scroll-box">
+				<block v-for="groupon in myGrouponList" :key="groupon.id">
+					<view class="group-card">
+						<image v-if="stateId !== 'ing'" class="group-state" :src="grouponStatus[groupon.groupon.status]" mode=""></image>
+						<view class="goods-content">
+							<shopro-mini-card
+								:title="groupon.goods.title"
+								:image="groupon.goods.image"
+								:subtitle="groupon.goods.subtitle"
+								:price="groupon.goods.price"
+								:originPrice="groupon.goods.original_price"
+								@click="jump('/pages/goods/detail', { id: groupon.goods.id })"
+							>
+								<template #describe>
+									<view class="u-flex u-m-b-20">
+										<view class="sell-box">
+											<text class="iconfont  icon-fire"></text>
+											<text class="sell-num">已拼{{ groupon.goods.sales }}件</text>
+										</view>
+										<text class="group-num">{{ groupon.groupon.num || 0 }}人团</text>
+									</view>
+								</template>
+							</shopro-mini-card>
+						</view>
+						<view class="btn-box u-flex">
+							<button class="u-reset-button invite-btn" @tap="jump('/pages/activity/groupon/detail', { id: groupon.groupon_id })">拼团详情</button>
+						</view>
+					</view>
+				</block>
+
+				<!-- 空白页 -->
+				<shopro-empty
+					v-if="isEmpty"
+					:image="$IMG_URL + '/imgs/empty/empty_groupon.png'"
+					tipText="暂无拼团商品,更多拼团好货等着你噢~"
+					btnText="去首页逛逛"
+					@click="$Router.pushTab('/pages/index/index')"
+				></shopro-empty>
+
+				<!-- 加载更多 -->
+				<u-loadmore v-if="myGrouponList.length" height="80rpx" :status="loadStatus" icon-type="flower" color="#ccc" />
+			</scroll-view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	components: {},
+	data() {
+		return {
+			isEmpty: false,
+			loadStatus: 'loadmore', //loadmore:加载前的状态,loading:加载中的状态,nomore-没有更多的状态
+			lastPage: 1,
+			currentPage: 1,
+			stateId: 'all',
+			grouponStatus: {
+				finish: this.$IMG_URL + '/imgs/group/group_state_succeed.png',
+				'finish-fictitious': this.$IMG_URL + '/imgs/group/group_state_succeed.png',
+				invalid: this.$IMG_URL + '/imgs/group/group_state_failed.png'
+			},
+			myGrouponList: [], //我的拼团列表。
+			groupState: [
+				{
+					id: 'all', //0
+					title: '全部'
+				},
+				{
+					id: 'ing', //1
+					title: '进行中'
+				},
+				{
+					id: 'finish', //2
+					title: '成功'
+				},
+				{
+					id: 'invalid', //3
+					title: '失败'
+				}
+			]
+		};
+	},
+	computed: {},
+	onLoad() {
+		this.getMyGroupon();
+	},
+	onPullDownRefresh() {
+		this.lastPage = 1;
+		this.currentPage = 1;
+		this.myGrouponList = [];
+		this.getMyGroupon();
+	},
+	methods: {
+		onNav(id) {
+			if (this.stateId !== id) {
+				this.stateId = id;
+				this.myGrouponList = [];
+				this.currentPage = 1;
+				this.getMyGroupon();
+			}
+		},
+
+		jump(path, parmas) {
+			this.$Router.push({
+				path: path,
+				query: parmas
+			});
+		},
+
+		// 加载更多
+		loadMore() {
+			if (this.currentPage < this.lastPage) {
+				this.currentPage += 1;
+				this.getMyGroupon();
+			}
+		},
+
+		// 我的拼团
+		getMyGroupon() {
+			let that = this;
+			that.loadStatus = 'loading';
+			that.$http(
+				'goods.myGroupon',
+				{
+					type: that.stateId,
+					page: that.currentPage
+				},
+				'加载中'
+			).then(res => {
+				uni.stopPullDownRefresh();
+				if (res.code === 1) {
+					that.myGrouponList = [...that.myGrouponList, ...res.data.data];
+					that.isEmpty = !that.myGrouponList.length;
+					that.lastPage = res.data.last_page;
+					that.loadStatus = that.currentPage < res.data.last_page ? 'loadmore' : 'nomore';
+				}
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+// tab
+.order-nav {
+	background: #fff;
+	height: 80rpx;
+
+	.nav-item {
+		flex: 1;
+
+		.item-title {
+			font-size: 30rpx;
+
+			font-weight: 400;
+			color: rgba(51, 51, 51, 1);
+			line-height: 76rpx;
+		}
+
+		.nav-line {
+			width: 120rpx;
+			height: 4rpx;
+			background: #fff;
+		}
+
+		.line-active {
+			background: rgba(230, 184, 115, 1);
+		}
+	}
+}
+
+// 拼团卡片
+.group-card {
+	background: #fff;
+	margin-top: 20rpx;
+	position: relative;
+	overflow: hidden;
+	.group-state {
+		position: absolute;
+		z-index: 2;
+		top: -20rpx;
+		right: -20rpx;
+		width: 126rpx;
+		height: 126rpx;
+	}
+
+	.goods-content {
+		padding: 20rpx;
+		position: relative;
+		.group-num {
+			font-size: 24rpx;
+
+			font-weight: 400;
+			color: rgba(153, 153, 153, 1);
+			margin-left: 30rpx;
+		}
+		.sell-box {
+			line-height: 32rpx;
+			background: rgba(255, 224, 226, 0.3);
+			border-radius: 16rpx;
+			padding: 0 10rpx;
+
+			.icon-fire {
+				color: #e1212b;
+				font-size: 26rpx;
+				margin-right: 10rpx;
+			}
+
+			.sell-num {
+				font-size: 20rpx;
+				color: #f7979c;
+			}
+		}
+	}
+
+	.btn-box {
+		height: 95rpx;
+		border-top: 1px solid rgba(223, 223, 223, 0.5);
+		justify-content: flex-end;
+		.invite-btn {
+			width: 210rpx;
+			line-height: 65rpx;
+			border: 1rpx solid #a8700d;
+			border-radius: 33rpx;
+			font-size: 26rpx;
+			color: #a8700d;
+			padding: 0;
+			margin-right: 20rpx;
+			background: #fff;
+		}
+	}
+}
+</style>

+ 248 - 0
pages/activity/seckill/list.vue

@@ -0,0 +1,248 @@
+<!-- 秒杀列表 -->
+<template>
+	<view class="page_box seckill-list-wrap">
+		<!-- tab栏 -->
+		<view class="tab-box u-flex">
+			<view class="tab-item u-flex-col u-row-center u-col-center" @tap="onTab(tab.id)" v-for="tab in tabList" :key="tab.id">
+				<text class="tab-title u-m-b-20" :class="{ 'tab-active': tabCurrent === tab.id }">{{ tab.title }}</text>
+				<text v-show="tabCurrent === tab.id" class="tab-line"></text>
+			</view>
+		</view>
+
+		<!-- 商品列表 -->
+		<view class="content_box">
+			<scroll-view scroll-y="true" enable-back-to-top @scrolltolower="loadMore" class="scroll-box">
+				<view class="goods-item u-m-b-16" v-for="item in goodsList" :key="item.id" @tap="toSeckillDetail(item.id)">
+					<view class="big-goods  u-flex u-p-20 u-col-top ">
+						<image class="goods-img"  lazy-load fade-show :src="item.image" mode="aspectFill"></image>
+						<view class=" card-right u-m-l-20 u-flex-col u-row-between">
+							<view class="">
+								<view class="goods-title u-ellipsis-1 u-m-t-10 u-m-b-10">
+									<view class="title-tag cu-tag bg-red sm radius u-m-r-10">秒杀</view>
+									{{ item.title }}
+								</view>
+								<view v-show="item.subtitle" class="subtitle-text u-m-b-10 u-ellipsis-1">{{ item.subtitle }}</view>
+							</view>
+
+							<view class="u-flex u-m-y-20">
+								<view class="cu-progress round sm" style="width:210rpx;"><view :style="[{ width: item.percent + '%',backgroundColor:'#ffbbbb' }]"></view></view>
+								<view class="progress-text">已售出{{ item.sales }}件</view>
+							</view>
+
+							<view class=" u-flex u-row-between u-col-center">
+								<view class="u-flex u-col-bottom font-OPPOSANS">
+									<view class="price u-m-r-10">{{ item.price }}</view>
+									<view class="origin-price">{{ item.original_price }}</view>
+								</view>
+								<button class="u-reset-button buy-btn">去抢购</button>
+							</view>
+						</view>
+					</view>
+				</view>
+				<!-- 空白 -->
+				<shopro-empty v-if="isEmpty" tipText="暂无秒杀商品,敬请期待~" :image="$IMG_URL + '/imgs/empty/empty_goods.png'"></shopro-empty>
+				<!-- 加载更多 -->
+				<u-loadmore v-if="!isEmpty" height="80rpx" :status="loadStatus" icon-type="flower" color="#ccc" />
+			</scroll-view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	components: {},
+	data() {
+		return {
+			isEmpty: false, //无数据
+			loadStatus: 'loadmore', //loadmore-加载前的状态,loading-加载中的状态,nomore-没有更多的状态
+			lastPage: 1,
+			currentPage: 1,
+			tabCurrent: 'ing',
+			goodsList: [],
+			tabList: [
+				{
+					id: 'ing',
+					title: '抢购进行中'
+				},
+				{
+					id: 'nostart',
+					title: '即将开始'
+				}
+			]
+		};
+	},
+	computed: {},
+	onLoad() {
+		this.getGoodsList();
+	},
+	onPullDownRefresh() {
+		this.lastPage = 1;
+		this.currentPage = 1;
+		this.goodsList = [];
+		this.getGoodsList();
+	},
+	methods: {
+		onTab(id) {
+			if (this.tabCurrent !== id) {
+				this.tabCurrent = id;
+				this.goodsList = [];
+				this.currentPage = 1;
+				this.lastPage = 1;
+				this.getGoodsList();
+			}
+		},
+		toSeckillDetail(id) {
+			this.$Router.push({ path: '/pages/goods/detail', query: { id: id } });
+		},
+		// 加载更多
+		loadMore() {
+			if (this.currentPage < this.lastPage) {
+				this.currentPage += 1;
+				this.getGoodsList();
+			}
+		},
+		// 秒杀列表
+		getGoodsList() {
+			let that = this;
+			that.loadStatus = 'loading';
+			that.$http(
+				'goods.seckillList',
+				{
+					type: that.tabCurrent,
+					page: that.currentPage
+				},
+				'加载中...'
+			).then(res => {
+				uni.stopPullDownRefresh();
+				if (res.code === 1) {
+					that.goodsList = [...that.goodsList, ...res.data.data];
+					that.goodsList.map(item => {
+						item.percent = item.stock + item.sales > 0 ? ((item.sales / (item.sales + item.stock)) * 100).toFixed(2) : 0;
+					});
+					that.isEmpty = !that.goodsList.length;
+					that.lastPage = res.data.last_page;
+					that.loadStatus = that.currentPage < res.data.last_page ? 'loadmore' : 'nomore';
+				}
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+.seckill-list-wrap {
+	background: url($IMG_URL+'/imgs/seckill/sekill_list_head_bg.png') no-repeat;
+	background-size: 100% auto;
+}
+// tab
+.tab-box {
+	.tab-item {
+		flex: 1;
+		height: 140rpx;
+		color: rgba(#fff, 0.4);
+		font-size: 32rpx;
+		font-weight: 600;
+		.tab-line {
+			width: 50rpx;
+			height: 8rpx;
+			background: #fff;
+			border-radius: 4rpx;
+		}
+	}
+
+	.tab-active {
+		color: rgba(#fff, 1);
+	}
+}
+
+.scroll-box {
+	background: linear-gradient(#fff, #f5f5f5);
+	width: 710rpx;
+	border-radius: 20rpx;
+	margin: 0 auto;
+}
+// 列表
+.goods-item {
+	// 大商品卡片
+	.big-goods {
+		width: 710rpx;
+		height: 260rpx;
+		background: #ffffff;
+		box-shadow: 0px 7rpx 8rpx 1rpx rgba(254, 76, 29, 0.05);
+		border-radius: 20rpx;
+		.goods-img {
+			width: 220rpx;
+			height: 220rpx;
+			border-radius: 6rpx;
+		}
+		.card-right {
+			width: 430rpx;
+			height: 220rpx;
+		}
+		.goods-title {
+			font-size: 26rpx;
+			font-weight: 600;
+			width: 400rpx;
+			color: #000000;
+		}
+		.subtitle-text {
+			font-size: 22rpx;
+			width: 400rpx;
+			font-weight: 500;
+			color: #666666;
+		}
+		.buy-btn {
+			width: 120rpx;
+			line-height: 50rpx;
+			background: linear-gradient(90deg, #d01325, #ed3c30);
+			border-radius: 25rpx;
+			font-size: 24rpx;
+			font-weight: 500;
+			color: #ffffff;
+		}
+		.progress-text {
+			color: #c4c4c4;
+			font-size: 20rpx;
+			margin-left: 25rpx;
+		}
+		// 价格
+		.price {
+			color: #ff0000;
+			font-weight: 600;
+			&::before {
+				content: '¥';
+				font-size: 20rpx;
+			}
+		}
+		.origin-price {
+			color: #c4c4c4;
+			font-size: 24rpx;
+			text-decoration: line-through;
+			&::before {
+				content: '¥';
+				font-size: 20rpx;
+			}
+		}
+	}
+
+	.buy-btn {
+		width: 120rpx;
+		line-height: 50rpx;
+		background: linear-gradient(90deg, #d01325, #ed3c30);
+		border-radius: 25rpx;
+		font-size: 24rpx;
+		font-weight: 500;
+		color: #ffffff;
+	}
+	.btn-end,
+	.btn-nostart {
+		background: rgba(238, 238, 238, 1);
+		color: #999999;
+	}
+	.btn-ing {
+		background: linear-gradient(90deg, rgba(233, 180, 97, 1), rgba(238, 204, 137, 1));
+		box-shadow: 0px 7rpx 6rpx 0px rgba(229, 138, 0, 0.22);
+		color: rgba(255, 255, 255, 1);
+	}
+}
+</style>

+ 361 - 0
pages/activity/sign/index.vue

@@ -0,0 +1,361 @@
+<!-- 签到 -->
+<template>
+	<view class="sign-wrap">
+		<view class="calendar">
+			<!-- 每日签到 -->
+			<view class="sign-everyday u-flex u-row-between u-p-30">
+				<text class="sign-everyday-title">每日签到</text>
+				<view class="sign-num-box">
+					已连续签到
+					<text class="sign-num">{{ cuntinueDays }}</text>
+					天
+				</view>
+			</view>
+
+			<!-- 切换年月 -->
+			<view class="bar u-flex u-row-center">
+				<view class="previous" @tap="handleCalendar(0)">
+					<button class="u-reset-button bar-btn u-p-r-30"><view class="u-iconfont uicon-arrow-left" style="color:#c4c4c4 ;font-size:28rpx ;"></view></button>
+				</view>
+				<view class="date">{{ cur_year || '--' }} 年 {{ cur_month || '--' }} 月</view>
+				<view class="next" @tap="handleCalendar(1)">
+					<button class="u-reset-button bar-btn u-p-l-30"><view class="u-iconfont uicon-arrow-right" style="color:#c4c4c4 ;font-size:28rpx ;"></view></button>
+				</view>
+			</view>
+
+			<!-- 显示星期 -->
+			<view class="week u-flex">
+				<view class="week-item u-flex u-row-center" v-for="(item, index) in weeks_ch" :key="index">{{ item }}</view>
+			</view>
+
+			<!-- 日历表 -->
+			<view class="myDateTable">
+				<view v-for="(item, j) in days" :key="j" class="dateCell u-flex u-row-center u-col-center">
+					<!-- 空格 -->
+					<view v-if="!item.date" class="cell"><text :decode="true">&nbsp;&nbsp;</text></view>
+					<view v-else>
+						<!-- 已签到日期 -->
+						<view v-if="item.is_sign" class="cell is-sign">
+							<text>{{ item.day }}</text>
+						</view>
+						<!-- 未签到日期 -->
+						<view class="cell" v-else>
+							<text>{{ item.day }}</text>
+						</view>
+					</view>
+				</view>
+				<!-- 签到按钮 -->
+				<view class="u-flex u-col-center u-row-center sign-box">
+					<button :disabled="isPresentMonth || isSign" @tap="onSign" class="u-reset-button sign-btn">{{ isSign ? '已签到' : '签到' }}</button>
+				</view>
+			</view>
+		</view>
+		<view class="cu-modal" :class="{ show: showSign }" @tap="showSign = false">
+			<view class="sign-modal-box cu-dialog" style="width: 610rpx;">
+				<view class="modal-head u-flex-col u-col-center">
+					<image class="modal-bg" :src="$IMG_URL + '/imgs/modal/sign_modal_bg.jpg'" mode=""></image>
+					<image class="sign-tag" :src="$IMG_URL + '/imgs/modal/sign_modal_succeed.png'" mode=""></image>
+					<view class="sign-num-box">
+						已连续打卡
+						<text class="sign-num">{{ cuntinueDays }}</text>
+						天
+					</view>
+				</view>
+				<view class="modal-content u-flex-col u-col-center">
+					<view class="sign-success">签到成功</view>
+					<view class="sign-score">恭喜您获得{{ score || '0' }}积分</view>
+				</view>
+				<view class="modal-bottom u-flex u-row-center"><button class="u-reset-button confirem-btn" @tap="showSign = false">确认</button></view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+import { mapMutations, mapActions, mapState } from 'vuex';
+export default {
+	components: {},
+	data() {
+		return {
+			days: [], //日历
+			cuntinueDays: 0, //签到天数
+			score: '', //签到获得的积分
+			cur_year: 0, //当前选的年
+			cur_month: 0, //当前选的月
+			cur_day: 0, //当前选择的天
+			today: parseInt(new Date().getDate()), //本日
+			toMonth: parseInt(new Date().getMonth() + 1), //本月
+			toYear: parseInt(new Date().getFullYear()), //本年
+			weeks_ch: ['日', '一', '二', '三', '四', '五', '六'], //星期
+			isPresentMonth: false, //是否可签到
+			isSign: false,
+			showSign: false
+		};
+	},
+	computed: {},
+	onLoad() {
+		this.cur_year = this.toYear;
+		this.cur_month = this.toMonth;
+		this.cur_day = this.today;
+		this.getSignList();
+	},
+	methods: {
+		...mapActions(['getUserInfo']),
+		// 当前签到记录
+		getSignList() {
+			let that = this;
+			let month = that.cur_month < 10 ? '0' + that.cur_month : that.cur_month;
+			let query = `${that.cur_year}-${month}`;
+			that.$http('user.signList', {
+				month: query
+			}).then(res => {
+				if (res.code == 1) {
+					let emptyDays = that.calculateEmptyGrids();
+					that.cuntinueDays = that.cuntinueDays === 0 ? res.data.cuntinue_days : that.cuntinueDays;
+					that.days = [...emptyDays, ...res.data.days];
+					that.selSign();
+				}
+			});
+		},
+		// 选中日期
+		selSign() {
+			let that = this;
+			let selToday = `${that.toYear}-${that.toMonth.toString().padStart(2, '0')}-${that.today.toString().padStart(2, '0')}`;
+			let newDay = `${that.cur_year}-${that.cur_month.toString().padStart(2, '0')}-${that.cur_day.toString().padStart(2, '0')}`;
+			if (selToday === newDay) {
+				let day = that.days.find(item => {
+					return item.date === selToday;
+				});
+				that.isSign = day.is_sign;
+			}
+		},
+		// 计算当月1号前空了几个格子
+		calculateEmptyGrids() {
+			let that = this;
+			let emptyDays = [];
+			const firstDayOfWeek = new Date(Date.UTC(that.cur_year, that.cur_month - 1, 1)).getDay();
+			if (firstDayOfWeek > 0) {
+				for (let i = 0; i < firstDayOfWeek; i++) {
+					let obj = {
+						day: null,
+						is_sign: false
+					};
+					emptyDays.push(obj);
+				}
+			}
+			return emptyDays;
+		},
+		//签到
+		onSign() {
+			let that = this;
+			let month = that.cur_month.toString().padStart(2, '0');
+			let query = `${that.cur_year}-${month}`;
+			that.$http(
+				'user.sign',
+				{
+					month: query
+				},
+				'签到中...'
+			).then(res => {
+				if (res.code == 1) {
+					that.getSignList();
+					that.score = res.data.score;
+					that.getUserInfo();
+					that.showSign = true;
+				}
+			});
+		},
+
+		// 切换控制年月,上一个月,下一个月
+		handleCalendar(type) {
+			const cur_year = parseInt(this.cur_year);
+			const cur_month = parseInt(this.cur_month);
+			let newMonth = cur_month;
+			let newYear = cur_year;
+			switch (type) {
+				case 0:
+					//上个月
+					this.isPresentMonth = true;
+					newMonth = cur_month - 1;
+					if (newMonth < 1) {
+						newYear = cur_year - 1;
+						newMonth = 12;
+					}
+					if (newYear < this.toYear || (newYear === this.toYear && newMonth <= this.toMonth)) {
+						this.isPresentMonth = false;
+					}
+					break;
+				case 1:
+					newMonth = cur_month + 1;
+					if (newMonth > 12) {
+						newYear = cur_year + 1;
+						newMonth = 1;
+					}
+
+					if (newYear > this.toYear || (newYear === this.toYear && newMonth > this.toMonth)) {
+						this.isPresentMonth = true;
+					}
+					break;
+				default:
+					break;
+			}
+			this.cur_year = newYear;
+			this.cur_month = newMonth;
+			this.getSignList();
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+// 日历
+.calendar {
+	background: #fff;
+	.sign-everyday {
+		height: 100rpx;
+		background: rgba(255, 255, 255, 1);
+		border: 1rpx solid rgba(223, 223, 223, 0.4);
+		.sign-everyday-title {
+			font-size: 32rpx;
+			font-weight: 500;
+			color: rgba(51, 51, 51, 1);
+		}
+		.sign-num-box {
+			font-size: 26rpx;
+			font-weight: 500;
+			color: rgba(153, 153, 153, 1);
+			.sign-num {
+				font-weight: 600;
+				color: #e3ad5b;
+				padding: 0 10rpx;
+			}
+		}
+	}
+	// 年月日
+	.bar {
+		height: 100rpx;
+		.date {
+			font-weight: 600;
+		}
+	}
+	// 星期
+	.week {
+		.week-item {
+			font-size: 24rpx;
+			font-weight: 500;
+			color: rgba(153, 153, 153, 1);
+			flex: 1;
+		}
+	}
+	// 日历表
+	.myDateTable {
+		display: flex;
+		flex-wrap: wrap;
+		.dateCell {
+			width: (750rpx/7);
+			height: 80rpx;
+			font-size: 26rpx;
+			font-weight: 400;
+			color: rgba(51, 51, 51, 1);
+			.cell {
+				display: flex;
+				justify-content: center;
+				align-items: center;
+			}
+		}
+	}
+}
+
+.is-sign {
+	width: 40rpx;
+	height: 40rpx;
+	background: linear-gradient(132deg, rgba(243, 223, 177, 1), rgba(243, 223, 177, 1), rgba(236, 190, 96, 1));
+	border-radius: 50%;
+	box-shadow: 0 0 4rpx 4rpx rgba(#e3ad5b, 0.4);
+	color: #fff;
+}
+
+// 签到按钮
+.sign-box {
+	height: 140rpx;
+	width: 100%;
+	.sign-btn {
+		width: 219rpx;
+		line-height: 70rpx;
+		background: linear-gradient(90deg, rgba(233, 180, 97, 1), rgba(238, 204, 137, 1));
+		border-radius: 35rpx;
+		font-size: 28rpx;
+		font-weight: 500;
+		color: rgba(255, 255, 255, 0.9);
+	}
+}
+
+.sign-modal-box {
+	width: 520rpx;
+	background: #fff;
+	border-radius: 20rpx;
+	position: relative;
+
+	.modal-head {
+		height: 320rpx;
+		align-items: center;
+		justify-content: center;
+		position: relative;
+		.modal-bg {
+			width: 100%;
+			height: 100%;
+		}
+		.sign-tag {
+			width: 70rpx;
+			height: 70rpx;
+			position: absolute;
+			top: 50rpx;
+			left: 50%;
+			transform: translateX(-50%);
+		}
+		.sign-num-box {
+			font-size: 30rpx;
+			color: #fff;
+			position: absolute;
+			top: 150rpx;
+			left: 50%;
+			transform: translateX(-50%);
+			.sign-num {
+				font-size: 36rpx;
+				font-weight: 600;
+				padding: 0 10rpx;
+			}
+		}
+	}
+	.modal-content {
+		padding: 30rpx 0;
+		.sign-success {
+			font-size: 34rpx;
+
+			font-weight: bold;
+			color: rgba(227, 173, 91, 1);
+		}
+		.sign-score {
+			font-size: 26rpx;
+
+			font-weight: 500;
+			color: rgba(153, 153, 153, 1);
+			padding-top: 16rpx;
+		}
+	}
+	.modal-bottom {
+		padding-bottom: 47rpx;
+		.confirem-btn {
+			width: 300rpx;
+			line-height: 70rpx;
+			background: linear-gradient(90deg, rgba(233, 180, 97, 1), rgba(238, 204, 137, 1));
+			border-radius: 35rpx;
+			padding: 0;
+			font-size: 28rpx;
+
+			font-weight: 500;
+			color: rgba(255, 255, 255, 0.9);
+		}
+	}
+}
+</style>

+ 267 - 0
pages/app/commission/apply.vue

@@ -0,0 +1,267 @@
+<!-- 申请分销商 -->
+<template>
+	<view class="apply-commission-wrap">
+		<!-- 标题栏 -->
+		<view class="head-box" :style="{ backgroundImage: ' url(' + formHeadImg + ')' }">
+			<shopro-navbar back-icon-color="#fff" :background="{}"></shopro-navbar>
+		</view>
+
+		<!-- 表单 -->
+		<view class="apply-form">
+			<u-form :model="model" :rules="rules" ref="uForm" :errorType="errorType">
+				<block v-for="(form, index) in formList" :key="index">
+					<u-form-item v-if="form.type === 'input'" :labelStyle="labelStyle"   label-width="150" label-position="left" :label="form.name" :prop="`value${index}`">
+						<u-input
+							:disabled="!hasPostBtn"
+							:placeholder="`请输入${form.name}`"
+							:placeholderStyle="placeholderStyle"
+							v-model="model['value' + index]"
+							type="text"
+						></u-input>
+					</u-form-item>
+					<u-form-item v-if="form.type === 'number'" :labelStyle="labelStyle" label-position="left" :label="form.name" :prop="`value${index}`" label-width="150">
+						<u-input
+							:disabled="!hasPostBtn"
+							:placeholder="`请输入${form.name}`"
+							:placeholderStyle="placeholderStyle"
+							v-model="model['value' + index]"
+							type="number"
+						></u-input>
+					</u-form-item>
+					<u-form-item
+						v-if="form.type === 'image'"
+						:labelStyle="labelStyle"
+						:prop="`value${index}`"
+						:label="form.name"
+						label-position="top"
+						label-width="150"
+						:borderBottom="true"
+					>
+						<u-upload
+							:deletable="hasPostBtn"
+							:placeholderStyle="placeholderStyle"
+							:showProgress="false"
+							@on-uploaded="uploadSuccess($event, `value${index}`)"
+							@on-remove="uploadRemove($event, `value${index}`)"
+							:file-list="model[`valueList${index}`]"
+							:action="`${$API_URL}/index/upload`"
+							width="148"
+							height="148"
+							maxCount="1"
+						></u-upload>
+					</u-form-item>
+				</block>
+
+				<view class="agreement u-flex" v-if="protocol && hasPostBtn">
+					<u-checkbox v-model="model.agreement" activeColor="#b095ff" shape="circle" @change="onAgreement"></u-checkbox>
+					<view class="agreement-text" @tap="$Router.push({ path: '/pages/public/richtext', query: { id: protocol.richtext_id } })">
+						我已阅读并遵守
+						<text class="text-underline">{{ protocol.name }}</text>
+					</view>
+				</view>
+				<button class="u-reset-button save-btn" v-if="hasPostBtn" @tap="onSubmit" :disabled="isFormEnd">确认提交</button>
+			</u-form>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	components: {},
+	data() {
+		return {
+			formList: [], //返回的表单
+			formHeadImg: '', //表单头部背景
+			protocol: null, //协议
+			isFormEnd: false, //提交成功
+			hasPostBtn: false, //是否显示提交按钮
+			errorType: ['message'],
+			labelStyle: {
+				'font-size': '28rpx',
+				'font-weight': '500',
+				color: '#333'
+			},
+			placeholderStyle: 'font-size: 28rpx;color:#c4c4c4;',
+			model: {},
+			inputNumber: [
+				{
+					required: true,
+					message: '内容不能为空!',
+					trigger: ['change', 'blur']
+				}
+			],
+			inputImage: [
+				{
+					required: true,
+					message: '请上传图片',
+					trigger: ['change', 'blur']
+				}
+			],
+			rules: {}
+		};
+	},
+	computed: {},
+	onLoad() {
+		this.getFrom();
+	},
+	methods: {
+		// 上传图片成功-单图
+		uploadSuccess(e, value) {
+			this.model[value] = e[0].response.data.url;
+		},
+
+		// 移除图片
+		uploadRemove(index, value) {
+			this.model[value] = '';
+		},
+
+		// 勾选同意
+		onAgreement(e) {
+			this.model.agreement = e.value;
+		},
+
+		// 获取申请分销商表单
+		getFrom() {
+			let that = this;
+			that.$http('commission.form').then(res => {
+				if (res.code === 1) {
+					that.protocol = res.data.apply_protocol; //表单协议同
+					that.formList = res.data.apply_info; //表单
+					that.hasPostBtn = res.data.apply_status; //是否显示提交按钮
+					that.formHeadImg = res.data.background_image ? res.data.background_image : this.$IMG_URL + '/imgs/commission/apply_bg.png'; //头部背景
+					that.initRules(); //规则
+					that.$refs.uForm.setRules(that.rules);
+				}
+			});
+		},
+
+		// 构建验证规则
+		initRules() {
+			let that = this;
+			that.formList.forEach((item, index) => {
+				that.model = {
+					...that.model,
+					...{
+						[`value${index}`]: item.value ? item.value : null
+					}
+				}; //构造model数据
+				that.model.agreement = false;
+				if (item.type === 'input' || item.type === 'number') {
+					//构造表单验证规则
+					that.rules = {
+						...that.rules,
+						...{
+							[`value${index}`]: that.inputNumber
+						}
+					};
+				}
+				if (item.type === 'image') {
+					if (item.value) {
+						let arr = [];
+						arr.push({ url: item.value });
+						that.model = {
+							...that.model,
+							...{
+								[`valueList${index}`]: arr
+							}
+						};
+					}
+
+					that.rules = {
+						...that.rules,
+						...{
+							[`value${index}`]: that.inputImage
+						}
+					};
+				}
+			});
+		},
+
+		//申请分销商
+		applyCommission(data) {
+			let that = this;
+			that.isFormEnd = true;
+			that.$http(
+				'commission.apply',
+				{
+					data: data
+				},
+				'申请中...'
+			).then(res => {
+				that.isFormEnd = false;
+				if (res.code === 1) {
+					uni.showToast({
+						title: res.msg,
+						success: () => {
+							that.$Router.back();
+						}
+					});
+				}
+			});
+		},
+
+		// 提交
+		onSubmit() {
+			let that = this;
+			this.$refs.uForm.validate(valid => {
+				if (valid) {
+					if (!this.model.agreement && this.protocol) return this.$u.toast('请勾选协议');
+					let formData = this.formList;
+					formData.map((item, index) => {
+						item.value = that.model[`value${index}`];
+					});
+					this.applyCommission(formData);
+				} else {
+					this.$u.toast('请完善表单');
+				}
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+.apply-commission-wrap {
+	height: 100%;
+	background-color: #fff;
+
+	.head-box {
+		width: 100%;
+		height: 400rpx;
+		background-size: 100% auto;
+		background-repeat: no-repeat;
+	}
+}
+
+// 表单
+.apply-form {
+	width: 750rpx;
+	background: #ffffff;
+	border-radius: 20rpx;
+	padding: 30rpx;
+
+	.agreement {
+		margin-top: 20rpx;
+
+		.agreement-text {
+			font-size: 24rpx;
+			font-weight: 500;
+			color: #b095ff;
+			.text-underline {
+				text-decoration: underline;
+			}
+		}
+	}
+
+	.save-btn {
+		width: 690rpx;
+		line-height: 86rpx;
+		background: linear-gradient(-90deg, #a36fff, #5336ff);
+		box-shadow: 0px 7rpx 11rpx 2rpx rgba(124, 103, 214, 0.34);
+		border-radius: 43rpx;
+		font-weight: 500;
+		color: #ffffff;
+		margin: 30rpx auto;
+	}
+}
+</style>

+ 393 - 0
pages/app/commission/commission-log.vue

@@ -0,0 +1,393 @@
+<!-- 佣金明细 -->
+<template>
+	<view class="commission-log-wrap">
+		<!-- 钱包卡片 -->
+		<view class="wallet-wrap">
+			<view class="wallet-card">
+				<view class="head-box u-flex">
+					<view class="head-title">总收益(元)</view>
+					<button class="u-reset-button look-btn" @tap="showMoney = !showMoney">
+						<view class="u-iconfont" :class="showMoney ? 'uicon-eye-fill' : 'uicon-eye-off'" style="color: #fff;font-size: 50rpx;"></view>
+					</button>
+				</view>
+				<view class="card-num">{{ showMoney ? agentInfo.total_income || '0.00' : '***' }}</view>
+				<view class="card-bottom u-flex u-row-left">
+					<view class="card-item u-flex-1">
+						<view class="item-title">待入账佣金</view>
+						<view class="item-value">{{ showMoney ? agentInfo.delay_money || '0.00' : '***' }}</view>
+					</view>
+					<view class="card-item u-flex-1">
+						<view class="item-title">可提现佣金</view>
+						<view class="item-value">{{ showMoney ? userInfo.money || '0.00' : '***' }}</view>
+					</view>
+				</view>
+				<button class="u-reset-button draw-btn" @tap="jump('/pages/user/wallet/withdraw')">提现</button>
+			</view>
+		</view>
+
+		<!-- 筛选 -->
+		<u-sticky offset-top="0" :enable="true">
+			<view class="head_box u-flex u-row-between">
+				<button class="u-reset-button date-btn u-flex u-col-center" @tap="showCalendar = true">
+					{{ selDateText }}
+					<text class="u-iconfont uicon-arrow-down-fill u-m-l-20" style="color:#e5e5e5 ; font-size: 34rpx;"></text>
+				</button>
+				<view class="">
+					<view class="total-box">{{ stateMap[stateCurrent] }}¥{{ totalMoney || '0.00' }}</view>
+				</view>
+			</view>
+			<!-- 状态分类 -->
+			<view class="u-flex nav-box">
+				<view class="state-item u-flex-1 " v-for="(state, index) in statusList" :key="state.value" @tap="onTab(state.value)">
+					<text class="state-title" :class="{ 'title-active': stateCurrent === state.value }">{{ state.name }}</text>
+					<text class="underline" :class="{ 'underline-active': stateCurrent === state.value }"></text>
+				</view>
+			</view>
+		</u-sticky>
+
+		<view class="item-box">
+			<!-- 佣金明细列表 -->
+			<view class="log-item u-flex u-row-between" v-for="item in rewardLog" :key="item.id">
+				<view class="item-left u-flex">
+					<image class="log-img" :src="item.buyer.avatar" mode=""></image>
+					<view class="">
+						<view class="log-name">{{ item.buyer.nickname }}</view>
+						<view class="log-notice">{{ $u.timeFormat(item.createtime, 'yyyy.mm.dd') }}</view>
+					</view>
+				</view>
+				<view class="item-right">
+					<view class="log-num" :style="{ color: classType[item.status] }">{{ item.status < 0 ? '-' : '+' }} {{ item.commission }}</view>
+					<view class="log-date"></view>
+				</view>
+			</view>
+			<!-- 更多 -->
+			<u-loadmore v-if="rewardLog.length" height="80rpx" :status="loadStatus" icon-type="flower" color="#ccc" />
+			<!-- 缺省页 -->
+			<shopro-empty v-if="isEmpty" marginTop="100rpx" :image="$IMG_URL + '/imgs/empty/no_data.png'" tipText="暂无数据"></shopro-empty>
+		</view>
+		<!-- 日期选择 -->
+		<u-calendar
+			v-model="showCalendar"
+			safeAreaInsetBottom
+			ref="uCalendar"
+			mode="range"
+			start-text="开始"
+			end-text="结束"
+			active-bg-color="#7063d2"
+			active-color="#fff"
+			range-bg-color="#e5e2ff"
+			range-color="#7063d2"
+			:customStyle="{ background: 'linear-gradient(90deg, #A36FFF, #5336FF)', boxShadow: '0 7rpx 11rpx 2rpx rgba(124, 103, 214, 0.34)' }"
+			@change="selDate"
+		></u-calendar>
+	</view>
+</template>
+
+<script>
+import { mapMutations, mapActions, mapState, mapGetters } from 'vuex';
+export default {
+	components: {},
+	data() {
+		return {
+			stateCurrent: 'all', //默认
+			stateMap: {
+				all: '全部收入',
+				waiting: '待入账',
+				accounted: '已入账',
+				back: '已退回',
+				cancel: '已取消'
+			},
+			classType: {
+				'-1': '#EB2B3D',
+				'0': '#05C3A1',
+				'1': '#7063D2',
+				'-2': '#EEEEEE'
+			},
+			statusList: [
+				{
+					name: '全部',
+					value: 'all'
+				},
+				{
+					name: '待入账',
+					value: 'waiting'
+				},
+				{
+					name: '已入账',
+					value: 'accounted'
+				},
+				{
+					name: '已退回',
+					value: 'back'
+				},
+				{
+					name: '已取消',
+					value: 'cancel'
+				}
+			],
+			showMoney: true, //是否显示金额
+			//日期选择
+			showCalendar: false,
+			selDateText: '',
+			rewardLog: [], //佣金记录
+			propsDate: '', //日期参数
+			totalMoney: '', //收入
+			loadStatus: 'loadmore', //loadmore-加载前的状态,loading-加载中的状态,nomore-没有更多的状态
+			currentPage: 1,
+			lastPage: 1,
+			isEmpty: false
+		};
+	},
+	computed: {
+		...mapGetters(['userInfo', 'agentInfo'])
+	},
+	onLoad() {
+		this.getToday();
+		this.getCommissionLog();
+	},
+	onPullDownRefresh() {
+		this.rewardLog = [];
+		this.currentPage = 1;
+		this.lastPage = 1;
+		this.getCommissionLog();
+	},
+	onReachBottom() {
+		if (this.currentPage < this.lastPage) {
+			this.currentPage += 1;
+			this.getCommissionLog();
+		}
+	},
+	methods: {
+		jump(path, parmas) {
+			this.$Router.push({
+				path: path,
+				query: parmas
+			});
+		},
+		// 切换分类
+		onTab(state) {
+			if (this.stateCurrent !== state) {
+				this.rewardLog = [];
+				this.currentPage = 1;
+				this.lastPage = 1;
+				this.stateCurrent = state;
+				this.getCommissionLog();
+			}
+		},
+
+		//  今日
+		getToday() {
+			let now = new Date();
+			this.selDateText = `${now.getFullYear()}.${now.getMonth() + 1}.${now.getDate()}`;
+			let dateText = `${now.getFullYear()}/${now.getMonth() + 1}/${now.getDate()}`;
+			this.propsDate = `${dateText}-${dateText}`;
+		},
+
+		// 选择日期
+		selDate(e) {
+			this.rewardLog = [];
+			this.currentPage = 1;
+			this.lastPage = 1;
+			this.selDateText = `${e.startYear}.${e.startMonth}.${e.startDay}-${e.endYear}.${e.endMonth}.${e.endDay}`;
+			let dateText = `${e.startYear}/${e.startMonth}/${e.startDay}-${e.endYear}/${e.endMonth}/${e.endDay}`;
+			this.propsDate = dateText;
+			this.getCommissionLog();
+			this.$refs.uCalendar.init();
+		},
+
+		// 佣金明细
+		getCommissionLog() {
+			let that = this;
+			that.loadStatus = 'loading';
+			that.$http(
+				'commission.rewardLog',
+				{
+					date: that.propsDate,
+					type: that.stateCurrent,
+					page: that.currentPage
+				},
+				'加载中...'
+			).then(res => {
+				uni.stopPullDownRefresh();
+				if (res.code === 1) {
+					that.totalMoney = res.data.total_money;
+					that.rewardLog = [...that.rewardLog, ...res.data.rewards.data];
+					that.lastPage = res.data.rewards.last_page;
+					that.isEmpty = !that.rewardLog.length;
+					that.loadStatus = that.currentPage < res.data.rewards.last_page ? 'loadmore' : 'nomore';
+				}
+			});
+		},
+
+		// 加载更多
+		loadMore() {
+			if (this.currentPage < this.lastPage) {
+				this.currentPage += 1;
+				this.getCommissionLog();
+			}
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+// 分类
+.state-item {
+	height: 100%;
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+	justify-content: center;
+	background-color: #fff;
+	border-bottom: 1rpx solid rgba(#999, 0.5);
+	.state-title {
+		color: #666;
+		font-weight: 500;
+		font-size: 28rpx;
+		line-height: 90rpx;
+	}
+	.title-active {
+		color: #333;
+	}
+	.underline {
+		display: block;
+		width: 68rpx;
+		height: 4rpx;
+		background: #fff;
+		border-radius: 2rpx;
+	}
+	.underline-active {
+		background: #5e49c3;
+		display: block;
+		width: 68rpx;
+		height: 4rpx;
+		border-radius: 2rpx;
+	}
+}
+// 钱包卡片
+.wallet-wrap {
+	background-color: #fff;
+	padding: 30rpx;
+}
+.wallet-card {
+	width: 690rpx;
+	height: 301rpx;
+	background: url($IMG_URL+'/imgs/commission/commission_card_bg.png') no-repeat;
+	background-size: 100% 100%;
+	border-radius: 20rpx;
+	padding: 30rpx;
+	position: relative;
+	box-shadow: 1rpx 5rpx 16rpx 0px rgba(94, 73, 195, 0.81);
+	.head-box {
+		margin-bottom: 20rpx;
+		.head-title {
+			font-size: 24rpx;
+			font-weight: 400;
+			color: #ffffff;
+		}
+		.look-btn {
+			color: #fff;
+			font-size: 30rpx;
+			background: none;
+			padding: 0;
+			margin-left: 20rpx;
+		}
+	}
+	.card-num {
+		font-size: 40rpx;
+		font-weight: 500;
+		color: #fefefe;
+		margin-bottom: 30rpx;
+	}
+	.card-bottom {
+		.card-item {
+			.item-title {
+				font-size: 24rpx;
+				font-weight: 400;
+				color: #ffffff;
+				margin-bottom: 10rpx;
+			}
+			.item-value {
+				font-size: 30rpx;
+				font-weight: 500;
+				color: #fefefe;
+			}
+		}
+	}
+	.draw-btn {
+		position: absolute;
+		top: 35rpx;
+		right: 35rpx;
+		width: 121rpx;
+		line-height: 58rpx;
+		background: #ffffff;
+		border-radius: 29rpx;
+		padding: 0;
+		font-size: 24rpx;
+		font-weight: 500;
+		color: #5848c4;
+	}
+}
+
+// 表头
+.head_box {
+	height: 120rpx;
+	padding: 0 30rpx;
+	background-color: #f6f6f6;
+	.date-btn {
+		background-color: #fff;
+		line-height: 54rpx;
+		border-radius: 27rpx;
+		padding: 0 20rpx;
+		font-size: 24rpx;
+		font-weight: 500;
+		color: #666666;
+	}
+	.total-box {
+		font-size: 24rpx;
+		font-weight: 500;
+		color: #666666;
+	}
+}
+// 佣金明细列表
+.item-box {
+	margin: 20rpx 0;
+}
+.log-item {
+	height: 142rpx;
+	background-color: #fff;
+	padding: 0 30rpx;
+	border-bottom: 1rpx solid #eee;
+	.item-left {
+		.log-img {
+			width: 50rpx;
+			height: 50rpx;
+			border-radius: 50%;
+			margin-right: 24rpx;
+		}
+		.log-name {
+			font-size: 28rpx;
+			font-weight: 500;
+			color: #333333;
+			margin-bottom: 12rpx;
+		}
+		.log-notice {
+			font-size: 22rpx;
+			font-weight: 500;
+			color: #c0c0c0;
+		}
+	}
+	.item-right {
+		.log-num {
+			font-size: 28rpx;
+			font-weight: 500;
+			color: #05c3a1;
+		}
+		.log-date {
+			font-size: 24rpx;
+			font-weight: 400;
+			color: #c0c0c0;
+		}
+	}
+}
+</style>

+ 167 - 0
pages/app/commission/goods.vue

@@ -0,0 +1,167 @@
+<!-- 推广商品 -->
+<template>
+	<view class="promotion-goods-wrap">
+		<view class="goods-list u-flex" v-for="(item, index) in goodsList" :key="item.goods_id" @tap="jump('/pages/goods/detail', { id: item.id })">
+			<view class="img-box"><image class="goods-img" :src="item.image" mode=""></image></view>
+			<view class="goods-info u-flex-col u-row-between">
+				<view class="goods-title u-ellipsis-1">{{ item.title }}</view>
+				<view class="goods-des u-ellipsis-1">{{ item.subtitle }}</view>
+				<view class="goods-price u-flex font-OPPOSANS">
+					<view class="price">¥{{ item.price }}</view>
+					<view class="origin-price">¥{{ item.original_price }}</view>
+				</view>
+				<view class="commission-num">预计佣金:¥{{ item.commission_money }}</view>
+			</view>
+			<button class="share-btn u-reset-button" @tap.stop="toShare(index)">分享赚</button>
+		</view>
+
+		<!-- 	分享组件 -->
+		<shopro-share v-model="showShare" :posterInfo="posterInfo" :posterType="'goods'"></shopro-share>
+		<!-- 缺省页 -->
+		<shopro-empty v-if="isEmpty" :image="$IMG_URL + '/imgs/empty/no_data.png'" tipText="暂无数据"></shopro-empty>
+		<!-- 更多 -->
+		<u-loadmore v-if="goodsList.length" height="80rpx" :status="loadStatus" icon-type="flower" color="#ccc" />
+	</view>
+</template>
+
+<script>
+import share from '@/shopro/share';
+export default {
+	components: {},
+	data() {
+		return {
+			showShare: false, //是否分享
+			posterInfo: {}, //分享信息
+			goodsList: [], //分销商品
+			loadStatus: 'loadmore', //loadmore-加载前的状态,loading-加载中的状态,nomore-没有更多的状态
+			currentPage: 1,
+			lastPage: 1,
+			isEmpty: false
+		};
+	},
+	computed: {},
+	onLoad() {
+		this.getGoodList();
+	},
+	onUnload() {
+		share.setShareInfo();
+	},
+	onReachBottom() {
+		if (this.currentPage < this.lastPage) {
+			this.currentPage += 1;
+			this.getGoodList();
+		}
+	},
+	methods: {
+		jump(path, parmas) {
+			this.$Router.push({
+				path: path,
+				query: parmas
+			});
+		},
+		// 去分享
+		toShare(index) {
+			const that = this;
+			let goodsInfo = this.goodsList[index];
+			this.posterInfo = goodsInfo;
+			share.setShareInfo({
+				title: goodsInfo.title,
+				desc: goodsInfo.subtitle,
+				image: goodsInfo.image,
+				params: {
+					page: 2,
+					pageId: goodsInfo.id
+				}
+			});
+			this.showShare = true;
+		},
+		getGoodList() {
+			let that = this;
+			that.loadStatus = 'loading';
+			that.$http('commission.goods', {
+				page: that.currentPage
+			}).then(res => {
+				if (res.code === 1) {
+					that.goodsList = [...that.goodsList, ...res.data.data];
+					that.lastPage = res.data.last_page;
+					that.isEmpty = !that.goodsList.length;
+					that.loadStatus = that.currentPage < res.data.last_page ? 'loadmore' : 'nomore';
+				}
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+page {
+	background-color: #fff;
+}
+// 推广商品列表
+.goods-list {
+	padding: 30rpx;
+	border-bottom: 1rpx solid rgba(#dfdfdf, 0.5);
+	position: relative;
+	.share-btn {
+		position: absolute;
+		bottom: 30rpx;
+		right: 30rpx;
+		width: 160rpx;
+		line-height: 60rpx;
+		background: linear-gradient(90deg, #a36fff, #5336ff);
+		box-shadow: 0px 7rpx 11rpx 2rpx rgba(124, 103, 214, 0.34);
+		border-radius: 30rpx;
+		font-size: 28rpx;
+		color: #fff;
+		font-weight: 500;
+	}
+	.img-box {
+		border: 1rpx solid rgba(223, 223, 223, 0.43);
+		margin-right: 30rpx;
+		.goods-img {
+			width: 200rpx;
+			height: 200rpx;
+		}
+	}
+	.goods-info {
+		align-items: flex-start;
+		height: 200rpx;
+
+		.goods-title {
+			width: 400rpx;
+			font-size: 28rpx;
+			font-weight: 500;
+			color: #333333;
+			margin-bottom: 10rpx;
+		}
+		.goods-des {
+			width: 400rpx;
+			font-size: 22rpx;
+			font-weight: 500;
+			color: #a8700d;
+			margin-bottom: 10rpx;
+		}
+		.goods-price {
+			margin-bottom: 10rpx;
+			.price {
+				font-size: 28rpx;
+				font-weight: 500;
+				color: #333333;
+				margin-right: 16rpx;
+			}
+			.origin-price {
+				font-size: 24rpx;
+
+				font-weight: 400;
+				text-decoration: line-through;
+				color: #999999;
+			}
+		}
+		.commission-num {
+			font-size: 24rpx;
+			font-weight: 500;
+			color: #5e4ddf;
+		}
+	}
+}
+</style>

+ 855 - 0
pages/app/commission/index.vue

@@ -0,0 +1,855 @@
+<!-- 佣金中心 -->
+<template>
+	<view style="width:100%;height: 100%;">
+		<view class="commission-wrap" :class="{ blur: !hasAuth }">
+			<!-- 标题栏 -->
+			<shopro-navbar back-icon-color="#fff" :background="{}"></shopro-navbar>
+
+			<!-- 用户资料 -->
+			<view class="user-card">
+				<view class="card-top u-flex u-row-between">
+					<view class="u-flex">
+						<view class="head-img-box"><image class="head-img" :src="userInfo.avatar" mode=""></image></view>
+						<view class="u-flex-col">
+							<view class="user-name">{{ userInfo.nickname }}</view>
+							<view class="user-info-box u-flex">
+								<view class="tag-box u-flex" v-if="commissionLv">
+									<image v-if="commissionLv.image" class="tag-img" :src="commissionLv.image" mode=""></image>
+									<text class="tag-title">{{ commissionLv.name }}</text>
+									<view v-show="showLv" class="u-iconfont uicon-arrow-right" style="color: #fff;font-size: 28rpx;"></view>
+								</view>
+							</view>
+						</view>
+					</view>
+					<view class="u-flex-col">
+						<view class="u-flex-col u-col-center">
+							<button class="u-reset-button log-btn u-m-b-20" @tap="jump('/pages/app/commission/commission-log')">明细</button>
+							<button class="u-reset-button look-btn" @tap="showMoney = !showMoney">
+								<view class="u-iconfont" :class="showMoney ? 'uicon-eye-fill' : 'uicon-eye-off'" style="color: #fff;font-size: 50rpx;"></view>
+							</button>
+						</view>
+					</view>
+				</view>
+
+				<!-- 收益 -->
+				<view class="card-bottom u-flex" v-if="commissionWallet">
+					<view class="u-flex-1 ">
+						<view class="item-title">总收益</view>
+						<view class="item-detail">{{ showMoney ? commissionWallet.total_income || '0.00' : '***' }}</view>
+					</view>
+					<view class="u-flex-1 u-col-center">
+						<view class="item-title">待入账佣金</view>
+						<view class="item-detail">{{ showMoney ? commissionWallet.delay_money || '0.00' : '***' }}</view>
+					</view>
+					<view class="u-flex-1 u-col-center">
+						<view class="item-title">我的消费</view>
+						<view class="item-detail">{{ showMoney ? userInfo.total_consume || '0.00' : '***' }}</view>
+					</view>
+				</view>
+			</view>
+
+			<!-- 滚动明细 -->
+			<view class="commission-log">
+				<scroll-view scroll-y="true" @scrolltolower="loadMore" class="scroll-box log-scroll">
+					<view class="log-item-box u-flex u-row-between" v-for="item in commissionLog" :key="item.id" @tap="$u.toast(item.remark)">
+						<view class="log-item-wrap">
+							<view class="log-item u-flex u-ellipsis-1 u-col-center">
+								<view class="u-flex u-col-center ">
+									<image
+										class="log-img"
+										:src="item.oper_type !== 'user' ? logMap[item.oper_type] : item.oper ? item.oper.avatar : logMap['admin']"
+										mode=""
+									></image>
+								</view>
+								<view class="log-text u-ellipsis-1">{{ item.remark }}</view>
+							</view>
+						</view>
+						<text class="log-time">{{ $u.timeFrom(item.createtime) }}</text>
+					</view>
+					<!-- 更多 -->
+					<view class="loadmore-wrap" v-if="commissionLog.length"><u-loadmore :status="loadStatus" icon-type="flower" color="#f6f6f6" /></view>
+				</scroll-view>
+			</view>
+
+			<!-- 功能菜单 -->
+			<view class="menu-box u-flex">
+				<view class="menu-item u-flex-col u-col-center" v-for="(menu, index) in menuList" v-if="!menu.isAgentFrom" :key="index" @tap="jump(menu.path)">
+					<image class="item-img" :src="menu.img" mode=""></image>
+					<view class="item-title">{{ menu.title }}</view>
+				</view>
+			</view>
+		</view>
+
+		<!-- 	分享组件 -->
+		<shopro-share v-model="showShare" posterType="user"></shopro-share>
+
+		<!-- 佣金中心权限验证 -->
+		<u-popup v-if="showAuthModal" class="auth-box" :mask="false" v-model="showAuthModal" mode="center" :mask-close-able="false" close-icon-pos="top-left" border-radius="20">
+			<view class="notice-box">
+				<view class="img-wrap"><image class="notice-img" :src="authNotice.img" mode=""></image></view>
+				<view class="notice-title">{{ authNotice.title }}</view>
+				<view class="notice-detail">{{ authNotice.detail }}</view>
+				<button class="u-reset-button notice-btn" @tap="onAuthBtn(authNotice.btnPath)">{{ authNotice.btnText }}</button>
+				<button class="u-reset-button back-btn" @tap="$Router.back()" v-if="!authNotice.hideBtn">返回</button>
+				<button class="u-reset-button back-btn" @tap="hasAuth = true" v-else>取消</button>
+			</view>
+		</u-popup>
+
+		<!-- 成为分销商条件 -->
+		<u-popup
+			v-if="showTerm"
+			class="into-agent-modal"
+			:bgStyle="{
+				background: 'none'
+			}"
+			v-model="showTerm"
+			mode="center"
+			:mask="false"
+			:mask-close-able="false"
+		>
+			<view class="condition-box u-flex u-row-center u-col-center">
+				<!-- 商品 -->
+				<view class="goods-condition u-flex-col u-col-center" v-if="showGoodsTerm">
+					<scroll-view class="card-wrap" scroll-y="true">
+						<view class="card-box u-flex u-m-x-10 u-m-y-10" v-for="item in goodsTermList" :key="item.id" @tap="jump('/pages/goods/detail', { id: item.id })">
+							<view class="img-wrap"><image class="goods-img" :src="item.image" mode=""></image></view>
+							<view class="detail u-fle u-row-between">
+								<view class="title u-ellipsis-2 u-m-b-10">{{ item.title }}</view>
+								<view class="sub u-ellipsis-1">{{ item.subtitle }}</view>
+							</view>
+						</view>
+
+						<view class="hack" style="height: 100rpx;"></view>
+					</scroll-view>
+					<view class="btn-box u-flex-col u-col-center u-p-20">
+						<button class="u-reset-button buy-btn u-m-b-20" @tap="$Router.back()">知道了</button>
+						<view class="tips">* 购买指定商品即可成为分销商</view>
+					</view>
+				</view>
+
+				<!-- 金额人数 -->
+				<view class="goods-condition u-flex u-row-between" v-if="showMoneyTerm">
+					<scroll-view class="card-wrap" scroll-y="true">
+						<view class="card-box u-flex u-m-x-10 u-m-y-10" :class="{ 'filter-card': userInfo.total_consume == moneyTermNum }">
+							<view class="img-wrap"><image class="goods-img" :src="$IMG_URL + '/imgs/user/commission_task_card.png'" mode=""></image></view>
+							<view class="detail">
+								<view class="title u-ellipsis-2 u-m-l-10">满足以下消费金额</view>
+
+								<!-- 进度条 -->
+								<view class="u-flex modal-progress ">
+									<view class="progress-box u-m-l-30 u-flex u-col-bottom">
+										<view class="cu-progress" style="width:150rpx ;">
+											<!-- 圆环 -->
+											<view
+												class="round-wrap"
+												v-if="userInfo.total_consume != moneyTermNum"
+												:style="[{ left: getProgress(userInfo.total_consume, moneyTermNum) + '%' }]"
+											>
+												<view class="round-inner"></view>
+											</view>
+											<u-line-progress
+												height="12"
+												:show-percent="false"
+												inactive-color="#7430EE"
+												:percent="getProgress(userInfo.total_consume, moneyTermNum)"
+												active-color="#C6A6FF"
+											></u-line-progress>
+											<!-- 已完成数 -->
+											<view
+												class="progress-num u-m-t-10"
+												v-if="userInfo.total_consume != moneyTermNum"
+												:style="[{ left: getProgress(userInfo.total_consume, moneyTermNum) + '%' }]"
+											>
+												{{ userInfo.total_consume }}
+											</view>
+										</view>
+									</view>
+									<view class="progress-tip" v-if="userInfo.total_consume != moneyTermNum">{{ moneyTermNum }}</view>
+								</view>
+							</view>
+						</view>
+					</scroll-view>
+					<view class="btn-box u-flex-col u-p-20 u-col-center">
+						<button class="u-reset-button buy-btn u-m-b-10" @tap="$Router.back()">知道了</button>
+						<view class="tips">* 满足指定消费金额即可成为分销商</view>
+					</view>
+				</view>
+			</view>
+		</u-popup>
+	</view>
+</template>
+
+<script>
+import { mapMutations, mapActions, mapState, mapGetters } from 'vuex';
+import share from '@/shopro/share';
+export default {
+	components: {},
+	data() {
+		return {
+			showShare: false, //是否显示分享海报
+			showMoney: true, //是否显示金额
+			hasAuth: true, //是否有权限
+			commissionLv: null, //分销商等级
+			commissionWallet: null, //分销商钱包
+			agentFrom: null, //是否显示我的资料
+			showLv: true,
+			commissionLog: [], //动态
+			loadStatus: 'loadmore', //loadmore-加载前的状态,loading-加载中的状态,nomore-没有更多的状态
+			currentPage: 1,
+			lastPage: 1,
+			logMap: {
+				system: this.$IMG_URL + '/imgs/commission/commission_base_notice.png',
+				admin: this.$IMG_URL + '/imgs/commission/commission_base_avatar.png'
+			},
+			showTerm: false, //条件弹窗
+			showGoodsTerm: false, //商品条件
+			showMoneyTerm: false, //金额条件
+			goodsTermList: [],
+			moneyTermNum: 0,
+			authNotice: {},
+			menuList: [
+				//menu
+				{
+					img: this.$IMG_URL + '/imgs/commission/commission_icon1.png',
+					title: '我的团队',
+					path: '/pages/app/commission/team'
+				},
+				{
+					img: this.$IMG_URL + '/imgs/commission/commission_icon2.png',
+					title: '佣金明细',
+					path: '/pages/app/commission/commission-log'
+				},
+				{
+					img: this.$IMG_URL + '/imgs/commission/commission_icon3.png',
+					title: '分销订单',
+					path: '/pages/app/commission/order'
+				},
+				{
+					img: this.$IMG_URL + '/imgs/commission/commission_icon4.png',
+					title: '推广商品',
+					path: '/pages/app/commission/goods'
+				},
+				{
+					img: this.$IMG_URL + '/imgs/commission/commission_icon5.png',
+					title: '我的资料',
+					path: '/pages/app/commission/apply',
+					isAgentFrom: true
+				},
+				{
+					img: this.$IMG_URL + '/imgs/commission/commission_icon6.png',
+					title: '分销排行',
+					path: '/pages/app/commission/rankings'
+				},
+				{
+					img: this.$IMG_URL + '/imgs/commission/commission_icon7.png',
+					title: '邀请海报',
+					path: ''
+				},
+				{
+					img: this.$IMG_URL + '/imgs/commission/commission_icon8.png',
+					title: '分享记录',
+					path: '/pages/app/commission/share-log'
+				}
+			]
+		};
+	},
+	computed: {
+		...mapGetters(['userInfo', 'agentInfo']),
+		showAuthModal: {
+			get() {
+				return !!this.authNotice.title && !this.hasAuth;
+			},
+			set(val) {
+				return val;
+			}
+		}
+	},
+	onShow() {
+		this.init();
+	},
+	onHide() {
+		this.commissionLog = [];
+		this.lastPage = 1;
+		this.currentPage = 1;
+	},
+	onLoad() {},
+	onPullDownRefresh() {
+		this.currentPage = 1;
+		this.lastPage = 1;
+		this.commissionLog = [];
+		this.init();
+	},
+	methods: {
+		...mapActions(['getAgent', 'getUserInfo']),
+		init() {
+			return Promise.all([this.getStatus(), this.getUserInfo()]).then(() => {
+				uni.stopPullDownRefresh();
+			});
+		},
+
+		// 跳转
+		jump(path, query) {
+			if (!path) {
+				this.showShare = true;
+			} else {
+				this.$Router.push({
+					path: path,
+					query: query
+				});
+			}
+		},
+
+		// 百分比
+		getProgress(sales, stock) {
+			let unit = '';
+			if (stock + sales > 0) {
+				let num = (sales / stock) * 100;
+				unit = num.toFixed(2);
+			} else {
+				unit = '0';
+			}
+			return Number(unit);
+		},
+
+		// 身份认证
+		getStatus() {
+			let that = this;
+			this.getAgent().then(res => {
+				uni.stopPullDownRefresh();
+				if (res.code === 1) {
+					that.authStatus(res.data);
+					that.commissionWallet = res.data.data;
+					that.commissionLv = res.data.data?.agent_level;
+					that.showLv = res.data.upgrade_display;
+					that.menuList.map(item => {
+						if (item.title === '我的资料') {
+							item.isAgentFrom = !res.data.agent_form;
+						}
+					});
+					that.commissionWallet && that.getLog();
+				}
+			});
+		},
+
+		// 分销动态
+		getLog() {
+			let that = this;
+			that.loadStatus = 'loading';
+			this.$http('commission.log', {
+				page: that.currentPage,
+				per_page: 5
+			}).then(res => {
+				if (res.code === 1) {
+					that.commissionLog = [...that.commissionLog, ...res.data.data];
+					that.lastPage = res.data.last_page;
+					that.loadStatus = that.currentPage < res.data.last_page ? 'loadmore' : 'nomore';
+				}
+			});
+		},
+
+		// 加载更多
+		loadMore() {
+			if (this.currentPage < this.lastPage) {
+				this.currentPage += 1;
+				this.getLog();
+			}
+		},
+
+		// 状态鉴权
+		authStatus(data) {
+			switch (data.status) {
+				case 'forbidden':
+					this.hasAuth = false;
+					this.showTerm = false;
+					this.authNotice = {
+						img: this.$IMG_URL + '/imgs/commission/auth_stop.png',
+						title: '抱歉!你的账户已被禁用',
+						detail: data.msg,
+						btnText: '联系客服',
+						btnPath: '/pages/public/chat/index'
+					};
+					break;
+				case 'pending':
+					this.hasAuth = false;
+					this.showTerm = false;
+					this.authNotice = {
+						img: this.$IMG_URL + '/imgs/commission/auth_stop.png',
+						title: '正在审核中!',
+						detail: data.msg,
+						btnText: '知道了',
+						btnPath: ''
+					};
+					break;
+				case 'needinfo':
+					this.hasAuth = false;
+					this.authNotice = {
+						img: this.$IMG_URL + '/imgs/commission/auth_perfect.png',
+						title: '待完善信息',
+						detail: data.msg,
+						btnText: '去完善',
+						btnPath: '/pages/app/commission/apply'
+					};
+					break;
+				case 'reject':
+					this.hasAuth = false;
+					this.showTerm = false;
+					this.authNotice = {
+						img: this.$IMG_URL + '/imgs/commission/auth_reject.png',
+						title: '申请驳回',
+						detail: data.msg,
+						btnText: '修改资料',
+						btnPath: '/pages/app/commission/apply'
+					};
+					break;
+				case 'close':
+					this.hasAuth = false;
+					this.showTerm = false;
+					this.authNotice = {
+						img: this.$IMG_URL + '/imgs/commission/auth_close.png',
+						title: '分销中心已关闭',
+						detail: data.msg,
+						btnText: '我知道了',
+						btnPath: ''
+					};
+					break;
+				case 'freeze':
+					this.hasAuth = false;
+					this.showTerm = false;
+					this.authNotice = {
+						img: this.$IMG_URL + '/imgs/commission/auth_freeze.png',
+						title: '抱歉!你的账户已被冻结',
+						detail: data.msg,
+						btnText: '联系客服',
+						btnPath: '/pages/public/chat/index',
+						hideBtn: true
+					};
+					break;
+				case null:
+					this.hasAuth = false;
+					this.showTerm = true;
+					if (data.become_agent) {
+						if (data.become_agent.type === 'goods') {
+							this.showGoodsTerm = true;
+							this.getGoodsTermList(data.become_agent.value);
+						}
+						if (data.become_agent.type === 'consume') {
+							this.showMoneyTerm = true;
+							this.moneyTermNum = data.become_agent.value;
+						}
+					}
+					break;
+				default:
+					this.hasAuth = true;
+					this.showTerm = false;
+			}
+		},
+
+		// 成为分销商,商品列表
+		getGoodsTermList(ids) {
+			let that = this;
+			that.$http('goods.lists', {
+				goods_ids: ids
+			}).then(res => {
+				if (res.code === 1) {
+					that.goodsTermList = res.data.data;
+				}
+			});
+		},
+
+		// 权限
+		onAuthBtn(path) {
+			path
+				? this.$Router.push({
+						path: path
+				  })
+				: this.$Router.back();
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.commission-wrap {
+	background: url($IMG_URL+'/imgs/commission/commission_bg1.jpg') no-repeat;
+	background-size: cover;
+	height: 100%;
+	width: 100%;
+	background-position: center center;
+	transition: all 0.3s ease-in-out 0s;
+	overflow: hidden;
+}
+// 进度条
+.progress-box {
+	.progress-tip {
+		font-size: 24rpx;
+		font-weight: 500;
+		color: #c7b2ff;
+	}
+	.progress-num {
+		font-size: 24rpx;
+		position: absolute;
+		color: #c7b2ff;
+		bottom: -30rpx;
+		white-space: nowrap;
+	}
+	.round-wrap {
+		width: 30rpx;
+		height: 30rpx;
+		border: 2rpx solid #5c3cff;
+		background: linear-gradient(0deg, #a36fff 0%, #846fff 100%);
+		border-radius: 50%;
+		position: absolute;
+		bottom: -6rpx;
+		transform: translateY(-50%);
+		left: 0;
+		margin-left: -15rpx;
+		.round-inner {
+			width: 20rpx;
+			height: 20rpx;
+			border: 2rpx solid #5c3cff;
+			background: linear-gradient(0deg, #a36fff 0%, #846fff 100%);
+			border-radius: 50%;
+			position: absolute;
+			top: 50%;
+			left: 50%;
+			transform: translate(-50%, -50%);
+		}
+	}
+}
+
+// 佣金中心权限验证蒙层
+.blur {
+	filter: blur(20rpx);
+	transition: all 0.3s ease-in-out 0s;
+}
+
+.auth-box {
+	width: 100%;
+	height: 100%;
+	background: none;
+	.notice-box {
+		display: flex;
+		flex-direction: column;
+		justify-content: center;
+		align-items: center;
+		background-color: #fff;
+		width: 612rpx;
+		min-height: 658rpx;
+		background: #ffffff;
+		padding: 30rpx;
+		border-radius: 20rpx;
+		.img-wrap {
+			margin-bottom: 50rpx;
+			.notice-img {
+				width: 180rpx;
+				height: 170rpx;
+			}
+		}
+		.notice-title {
+			font-size: 35rpx;
+			font-weight: bold;
+			color: #46351b;
+			margin-bottom: 28rpx;
+		}
+		.notice-detail {
+			font-size: 28rpx;
+			font-weight: 400;
+			color: #999999;
+			line-height: 36rpx;
+			margin-bottom: 50rpx;
+		}
+		.notice-btn {
+			width: 492rpx;
+			line-height: 70rpx;
+			background: linear-gradient(-90deg, #a36fff, #5336ff);
+			box-shadow: 0px 7rpx 11rpx 2rpx rgba(124, 103, 214, 0.34);
+			border-radius: 35rpx;
+			font-size: 28rpx;
+			font-weight: 500;
+			color: #ffffff;
+			margin-bottom: 10rpx;
+		}
+		.back-btn {
+			width: 492rpx;
+			line-height: 70rpx;
+			font-size: 28rpx;
+			font-weight: 500;
+			color: #c5b8fb;
+			background: none;
+		}
+	}
+}
+
+// 成为分销商
+.into-agent-modal {
+	.condition-box {
+		width: 640rpx;
+		height: 786rpx;
+		background: url($IMG_URL+'/imgs/commission/into_commission.png') no-repeat;
+		background-size: 100% 100%;
+	}
+	.goods-condition {
+		width: 484rpx;
+		height: 580rpx;
+		padding: 80rpx 0 30rpx;
+		display: flex;
+		flex-direction: column;
+		justify-content: center;
+		align-items: center;
+		position: relative;
+		.card-wrap {
+			width: 484rpx;
+			height: 460rpx;
+			padding: 10rpx;
+			padding-bottom: 30rpx;
+		}
+		.modal-progress {
+			.progress-tip {
+				font-size: 24rpx;
+			}
+			.progress-box {
+				margin-right: 10rpx;
+				.cu-progress {
+					width: 200rpx;
+					position: relative;
+					.round-wrap {
+						width: 20rpx;
+						height: 20rpx;
+					}
+					.round-inner {
+						width: 10rpx;
+						height: 10rpx;
+					}
+				}
+			}
+		}
+		.card-box {
+			background: rgba(#c3b6ff, 0.3);
+			padding: 10rpx;
+			border-radius: 10rpx;
+			.img-wrap {
+				background: #fff;
+				width: 110rpx;
+				height: 110rpx;
+				border-radius: 10rpx;
+				margin-right: 20rpx;
+
+				.goods-img {
+					width: 110rpx;
+					height: 110rpx;
+					border-radius: 10rpx;
+				}
+			}
+			.detail {
+				height: 110rpx;
+				width: 280rpx;
+				align-items: flex-start;
+				.title {
+					font-size: 20rpx;
+					font-weight: 500;
+					color: #333333;
+					line-height: 35rpx;
+					text-align: left;
+				}
+				.sub {
+					font-size: 16rpx;
+					font-weight: 500;
+					color: #9281e2;
+					text-align: left;
+					width: 280rpx;
+				}
+			}
+		}
+		.filter-card {
+			filter: grayscale(100%);
+			filter: gray;
+		}
+		.btn-box {
+			background-color: #fff;
+			width: 100%;
+			position: absolute;
+			bottom: 0;
+			.buy-btn {
+				width: 390rpx;
+				line-height: 60rpx;
+				background: linear-gradient(-90deg, #a36fff, #5336ff);
+				box-shadow: 0px 7rpx 11rpx 2rpx rgba(124, 103, 214, 0.34);
+				border-radius: 35rpx;
+				font-size: 24rpx;
+				font-weight: 500;
+				color: #ffffff;
+			}
+			.tips {
+				font-size: 20rpx;
+				font-weight: 500;
+				color: #9281e2;
+			}
+		}
+	}
+}
+
+// 用户资料卡片
+.user-card {
+	width: 690rpx;
+	min-height: 350rpx;
+	border-radius: 14rpx;
+	margin: 30rpx;
+	background: url($IMG_URL+'/imgs/commission/commission_card_bg.png') no-repeat;
+	background-size: 100% 100%;
+	padding-top: 10rpx;
+	.card-top {
+		padding: 40rpx 40rpx 30rpx;
+		margin-bottom: 30rpx;
+		border-bottom: 1px solid rgba(#fff, 0.12);
+		.user-name {
+			font-size: 26rpx;
+			font-weight: 500;
+			color: #ffffff;
+			line-height: 30rpx;
+			margin-bottom: 20rpx;
+		}
+		.log-btn {
+			width: 83rpx;
+			line-height: 41rpx;
+			border: 1rpx solid rgba(#ffffff, 0.33);
+			border-radius: 21rpx;
+			font-size: 22rpx;
+			font-weight: 400;
+			color: #ffffff;
+		}
+		.look-btn {
+			color: #fff;
+			font-size: 40rpx;
+		}
+	}
+	.head-img-box {
+		margin-right: 26rpx;
+		width: 76rpx;
+		height: 76rpx;
+		border-radius: 50px;
+		position: relative;
+		background: #fff;
+		padding: 10rpx;
+		background-clip: padding-box;
+		.head-img {
+			width: 66rpx;
+			height: 66rpx;
+			border-radius: 50%;
+			position: absolute;
+			top: 50%;
+			left: 50%;
+			transform: translate(-50%, -50%);
+		}
+	}
+	.user-info-box {
+		.tag-box {
+			background: rgba(0, 0, 0, 0.2);
+			border-radius: 21rpx;
+			line-height: 38rpx;
+
+			.tag-img {
+				width: 36rpx;
+				height: 36rpx;
+				border-radius: 50%;
+			}
+
+			.tag-title {
+				font-size: 20rpx;
+				padding: 0 10rpx;
+				font-weight: 500;
+				color: rgba(255, 255, 255, 1);
+				line-height: 36rpx;
+			}
+		}
+	}
+	.card-bottom {
+		margin: 0 40rpx 40rpx;
+		.item-title {
+			font-size: 24rpx;
+
+			font-weight: 400;
+			color: #ffffff;
+			line-height: 30rpx;
+		}
+		.item-detail {
+			font-size: 40rpx;
+			font-family: DIN;
+			font-weight: 500;
+			color: #fefefe;
+			line-height: 30rpx;
+			margin-top: 30rpx;
+		}
+	}
+}
+// 滚动明细
+.commission-log {
+	min-height: 450rpx;
+	padding: 0 30rpx;
+	margin-top: 60rpx;
+	.log-scroll {
+		height: 380rpx;
+		.log-item-box {
+			height: 78rpx;
+			.log-time {
+				margin-left: 30rpx;
+				font-size: 22rpx;
+				font-weight: 500;
+				color: #fefefe;
+				text-align: right;
+			}
+		}
+		.loadmore-wrap {
+			line-height: 80rpx;
+		}
+		.log-item {
+			height: 48rpx;
+			background: rgba(#5e49c3, 0.4);
+			border-radius: 24rpx;
+			padding-left: 6rpx;
+			padding-right: 20rpx;
+			.log-img {
+				width: 40rpx;
+				height: 40rpx;
+				border-radius: 50%;
+				margin-right: 10rpx;
+			}
+			.log-text {
+				font-size: 22rpx;
+				font-weight: 500;
+				color: #fefefe;
+				max-width: 480rpx;
+			}
+		}
+	}
+}
+// 功能菜单
+.commission-bottom-bg {
+	position: fixed;
+	width: 100%;
+	bottom: 0;
+	z-index: 6;
+}
+.menu-box {
+	flex-wrap: wrap;
+	margin: 30rpx;
+	position: fixed;
+	width: 750rpx;
+	bottom: 0;
+	z-index: 10;
+	.menu-item {
+		width: (690rpx/4);
+		margin-bottom: 50rpx;
+		.item-img {
+			width: 68rpx;
+			height: 68rpx;
+		}
+		.item-title {
+			font-size: 24rpx;
+			font-weight: 600;
+			color: #fff;
+			line-height: 30rpx;
+			margin-top: 16rpx;
+		}
+	}
+}
+</style>

+ 497 - 0
pages/app/commission/order.vue

@@ -0,0 +1,497 @@
+<!-- 分销订单 -->
+<template>
+	<view class="page_box commission-order-wrap">
+		<view class="head_box">
+			<!-- 标题栏 -->
+			<shopro-navbar back-icon-color="#fff" :background="{}" :backTextStyle="{ color: '#fff', fontSize: '40rpx', fontWeight: '500' }" backText="分销订单"></shopro-navbar>
+
+			<!-- 团队数据总览 -->
+			<view class="team-data-box u-flex u-row-between">
+				<view class="data-card">
+					<view class="total-item">
+						<view class="item-title">团队订单数量(单)</view>
+						<view class="total-num">{{ agentInfo.child_order_count || 0 }}</view>
+					</view>
+					<view class="category-item u-flex">
+						<view class="u-flex-1">
+							<view class="item-title">一级订单</view>
+							<view class="category-num">{{ agentInfo.child_order_count_1 || 0 }}</view>
+						</view>
+						<view class="u-flex-1">
+							<view class="item-title">二级订单</view>
+							<view class="category-num">{{ agentInfo.child_order_count_2 || 0 }}</view>
+						</view>
+					</view>
+				</view>
+				<view class="data-card">
+					<view class="total-item">
+						<view class="item-title">团队订单金额(元)</view>
+						<view class="total-num">{{ agentInfo.child_order_money || '0.00' }}</view>
+					</view>
+					<view class="category-item u-flex">
+						<view class="u-flex-1">
+							<view class="item-title">一级订单</view>
+							<view class="category-num">{{ agentInfo.child_order_money_1 || '0.00' }}</view>
+						</view>
+						<view class="u-flex-1">
+							<view class="item-title">二级订单</view>
+							<view class="category-num">{{ agentInfo.child_order_money_2 || '0.00' }}</view>
+						</view>
+					</view>
+				</view>
+			</view>
+
+			<!-- 直推 -->
+			<view class="direct-box u-flex u-row-between">
+				<view class="direct-item">
+					<view class="item-title">直推分销订单数量(单)</view>
+					<view class="item-value">{{ agentInfo.order_count }}</view>
+				</view>
+				<view class="direct-item">
+					<view class="item-title">直推分销订单金额(元)</view>
+					<view class="item-value">{{ agentInfo.order_money }}</view>
+				</view>
+			</view>
+
+			<!-- 筛选 -->
+			<view class="">
+				<!-- 日期筛选 -->
+				<view class="filter-box u-flex u-row-between">
+					<button class="u-reset-button date-btn u-flex u-col-center" @tap="showCalendar = true">
+						{{ selDateText }}
+						<text class="u-iconfont uicon-arrow-down-fill u-m-l-20" style="color:#e5e5e5 ; font-size: 34rpx;"></text>
+					</button>
+					<view class="">
+						<view class="total-box">{{ stateMap[stateCurrent] }}¥{{ amount || '0.00' }}</view>
+					</view>
+				</view>
+				<!-- 状态分类 -->
+				<view class="u-flex nav-box">
+					<view class="state-item u-flex-1" v-for="(state, index) in statusList" :key="state.value" @tap="onTab(state.value)">
+						<text class="state-title" :class="{ 'title-active': stateCurrent === state.value }">{{ state.name }}</text>
+						<text class="underline" :class="{ 'underline-active': stateCurrent === state.value }"></text>
+					</view>
+				</view>
+			</view>
+		</view>
+
+		<view class="content_box">
+			<scroll-view scroll-y="true" @scrolltolower="loadMore" class="scroll-box">
+				<!-- 订单列表 -->
+				<view class="order-list" v-for="item in orderList" :key="item.id">
+					<view class="order-head u-flex u-row-between">
+						<text class="order-code">订单编号:{{ item.order.order_sn }}</text>
+						<text class="order-state">{{ item.order.status_name }}</text>
+					</view>
+					<view class="order-from u-flex u-row-between">
+						<view class="from-user u-flex">
+							<text>下单人:</text>
+							<image class="user-avatar" :src="item.buyer.avatar" mode=""></image>
+							<text class="user-name">{{ item.buyer.nickname }}</text>
+						</view>
+						<view class="order-time">{{ $u.timeFormat(item.order.createtime, ' yyyy.mm.dd hh:MM ') }}</view>
+					</view>
+					<view class="goods-card u-flex" v-for="corder in item.commission_order" :key="corder.id">
+						<view class="goods-img-box"><image class="goods-img" :src="corder.order_item.goods_image" mode=""></image></view>
+						<view class="goods-info u-flex-col u-row-between">
+							<view class="goods-title more-t">{{ corder.order_item.goods_title }}</view>
+							<view class="goods-sku">数量: {{ corder.order_item.goods_num }};{{ corder.order_item.goods_sku_text || '' }}</view>
+							<view class="total-box u-flex u-flex-1 u-row-between">
+								<view class="goods-price">
+									{{ corder.amount }}
+									<text class="goods-state">{{ corder.commission_order_status_text }}</text>
+								</view>
+								<view class="u-flex" v-if="corder.reward">
+									<text class="name">佣金</text>
+									<text class="commission-num">{{ corder.reward.commission }}</text>
+								</view>
+							</view>
+						</view>
+					</view>
+				</view>
+				<!-- 缺省页 -->
+				<shopro-empty marginTop="100rpx" v-if="isEmpty" :image="$IMG_URL + '/imgs/empty/no_order.png'" tipText="暂无订单"></shopro-empty>
+				<!-- 更多 -->
+				<u-loadmore v-if="orderList.length" height="80rpx" :status="loadStatus" icon-type="flower" color="#ccc" />
+			</scroll-view>
+		</view>
+		<!-- 日期选择 -->
+		<u-calendar
+			v-model="showCalendar"
+			safeAreaInsetBottom
+			ref="uCalendar"
+			mode="range"
+			start-text="开始"
+			end-text="结束"
+			active-bg-color="#7063d2"
+			active-color="#fff"
+			range-bg-color="#e5e2ff"
+			range-color="#7063d2"
+			:customStyle="{ background: 'linear-gradient(90deg, #A36FFF, #5336FF)', boxShadow: '0 7rpx 11rpx 2rpx rgba(124, 103, 214, 0.34)' }"
+			@change="selDate"
+		></u-calendar>
+	</view>
+</template>
+
+<script>
+import { mapMutations, mapActions, mapState, mapGetters } from 'vuex';
+export default {
+	components: {},
+	data() {
+		return {
+			backTextStyle: {
+				color: '#fff',
+				fontSize: '40rpx',
+				fontWeight: '500'
+			},
+			stateCurrent: 'all', //默认
+			stateMap: {
+				all: '全部',
+				// no: '不计入',
+				yes: '已计入',
+				back: '已退回',
+				cancel: '已取消'
+			},
+			statusList: [
+				{
+					name: '全部',
+					value: 'all'
+				},
+				// {
+				// 	name: '不计入',
+				// 	value: 'no'
+				// },
+				{
+					name: '已计入',
+					value: 'yes'
+				},
+				{
+					name: '已退回',
+					value: 'back'
+				},
+				{
+					name: '已取消',
+					value: 'cancel'
+				}
+			],
+			orderList: [], //分销订单
+			loadStatus: 'loadmore', //loadmore-加载前的状态,loading-加载中的状态,nomore-没有更多的状态
+			currentPage: 1,
+			lastPage: 1,
+			isEmpty: false,
+			//日期选择
+			showCalendar: false,
+			selDateText: '',
+			propsDate: '', //日期参数
+			amount: '' //收入
+		};
+	},
+	computed: {
+		...mapGetters(['userInfo', 'agentInfo'])
+	},
+	onLoad() {
+		this.getToday();
+		this.getOrderList();
+	},
+	onPullDownRefresh() {
+		this.orderList = [];
+		this.currentPage = 1;
+		this.lastPage = 1;
+		this.getOrderList();
+	},
+	methods: {
+		jump(path, parmas) {
+			this.$Router.push({
+				path: path,
+				query: parmas
+			});
+		},
+		//  今日
+		getToday() {
+			let now = new Date();
+			this.selDateText = `${now.getFullYear()}.${now.getMonth() + 1}.${now.getDate()}`;
+			let dateText = `${now.getFullYear()}/${now.getMonth() + 1}/${now.getDate()}`;
+			this.propsDate = `${dateText}-${dateText}`;
+		},
+		// 选择日期
+		selDate(e) {
+			this.orderList = [];
+			this.currentPage = 1;
+			this.lastPage = 1;
+			this.selDateText = `${e.startYear}.${e.startMonth}.${e.startDay}-${e.endYear}.${e.endMonth}.${e.endDay}`;
+			let dateText = `${e.startYear}/${e.startMonth}/${e.startDay}-${e.endYear}/${e.endMonth}/${e.endDay}`;
+			this.propsDate = dateText;
+			this.getOrderList();
+			this.$refs.uCalendar.init();
+		},
+		// 切换分类
+		onTab(state) {
+			if (this.stateCurrent !== state) {
+				this.orderList = [];
+				this.currentPage = 1;
+				this.lastPage = 1;
+				this.stateCurrent = state;
+				this.getOrderList();
+			}
+		},
+
+		// 分销订单
+		getOrderList() {
+			let that = this;
+			that.loadStatus = 'loading';
+			that.$http('commission.orderLog', {
+				date: that.propsDate,
+				type: that.stateCurrent,
+				page: that.currentPage
+			}).then(res => {
+				uni.stopPullDownRefresh();
+				if (res.code === 1) {
+					that.orderList = [...that.orderList, ...res.data.orders.data];
+					that.amount = res.data.amount;
+					that.lastPage = res.data.orders.last_page;
+					that.isEmpty = !that.orderList.length;
+					that.loadStatus = that.currentPage < res.data.orders.last_page ? 'loadmore' : 'nomore';
+				}
+			});
+		},
+
+		// 加载更多
+		loadMore() {
+			if (this.currentPage < this.lastPage) {
+				this.currentPage += 1;
+				this.getOrderList();
+			}
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+// 直推
+.direct-box {
+	margin: 20rpx;
+	.direct-item {
+		width: 341rpx;
+		height: 117rpx;
+		background: #ffffff;
+		border-radius: 20rpx;
+		padding: 20rpx;
+		.item-title {
+			font-size: 22rpx;
+			font-weight: 500;
+			color: #999999;
+			margin-bottom: 6rpx;
+		}
+		.item-value {
+			font-size: 38rpx;
+			font-weight: 600;
+			color: #333333;
+		}
+	}
+}
+// 团队信息总览
+.team-data-box {
+	margin: 20rpx;
+	.data-card {
+		width: 340rpx;
+		background: #ffffff;
+		border-radius: 20rpx;
+		padding: 20rpx;
+
+		.item-title {
+			font-size: 22rpx;
+			font-weight: 500;
+			color: #999999;
+			line-height: 30rpx;
+			margin-bottom: 10rpx;
+		}
+
+		.total-item {
+			margin-bottom: 20rpx;
+		}
+
+		.total-num {
+			font-size: 38rpx;
+			font-weight: 600;
+			color: #333333;
+		}
+
+		.category-num {
+			font-size: 26rpx;
+			font-weight: 600;
+			color: #333333;
+		}
+	}
+}
+
+// 状态分类
+.head_box {
+	width: 750rpx;
+	background: url($IMG_URL+'/imgs/commission/card_bg.png') no-repeat;
+}
+// 表头
+.filter-box {
+	height: 120rpx;
+	padding: 0 30rpx;
+	background-color: #f6f6f6;
+	.date-btn {
+		background-color: #fff;
+		line-height: 54rpx;
+		border-radius: 27rpx;
+		padding: 0 20rpx;
+		font-size: 24rpx;
+		font-weight: 500;
+		color: #666666;
+	}
+	.total-box {
+		font-size: 24rpx;
+		font-weight: 500;
+		color: #666666;
+	}
+}
+.nav-box {
+	background-color: #fff;
+}
+.state-item {
+	height: 100%;
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+	justify-content: center;
+	.state-title {
+		color: #666;
+		font-weight: 500;
+		font-size: 28rpx;
+		line-height: 90rpx;
+	}
+	.title-active {
+		color: #333;
+	}
+	.underline {
+		display: block;
+		width: 68rpx;
+		height: 4rpx;
+		background: #fff;
+		border-radius: 2rpx;
+	}
+	.underline-active {
+		background: #5e49c3;
+		display: block;
+		width: 68rpx;
+		height: 4rpx;
+		border-radius: 2rpx;
+	}
+}
+// 订单列表
+.order-list {
+	background-color: #fff;
+	margin-top: 20rpx;
+	.order-head {
+		padding: 20rpx;
+		.order-code {
+			font-size: 26rpx;
+			font-weight: 400;
+			color: #999999;
+		}
+		.order-state {
+			font-size: 26rpx;
+			font-weight: 500;
+			color: #05c3a1;
+		}
+	}
+	.order-from {
+		background-color: #f9f9f9;
+		padding: 20rpx;
+		.from-user {
+			font-size: 24rpx;
+			font-weight: 400;
+			color: #666666;
+			.user-avatar {
+				width: 26rpx;
+				height: 26rpx;
+				border-radius: 50%;
+				margin-right: 8rpx;
+			}
+			.user-name {
+				font-size: 24rpx;
+				font-weight: 400;
+				color: #999999;
+			}
+		}
+		.order-time {
+			font-size: 24rpx;
+			font-weight: 400;
+			color: #999999;
+		}
+	}
+	.goods-card {
+		padding: 30rpx 20rpx;
+		// border-bottom: 1rpx solid #dfdfdf;
+		.goods-img-box {
+			margin-right: 30rpx;
+			.goods-img {
+				width: 160rpx;
+				height: 160rpx;
+				background-color: #ccc;
+			}
+		}
+		.goods-info {
+			height: 160rpx;
+			width: 600rpx;
+			align-items: flex-start;
+			.goods-title {
+				font-size: 28rpx;
+				font-weight: 500;
+				color: #333333;
+			}
+			.goods-sku {
+				font-size: 24rpx;
+				font-weight: 400;
+				color: #666666;
+			}
+			.goods-price {
+				font-size: 30rpx;
+				font-weight: 500;
+				color: #333333;
+				.goods-state {
+					line-height: 30rpx;
+					padding: 0 10rpx;
+					background: #f1eeff;
+					border: 1rpx solid #603fff;
+					border-radius: 30rpx;
+					margin-left: 20rpx;
+					font-size: 20rpx;
+					color: #5e49c3;
+				}
+				&::before {
+					content: '¥';
+					font-size: 20rpx;
+				}
+			}
+			.total-box {
+				height: 80rpx;
+				width: 100%;
+				.num-price {
+					font-size: 24rpx;
+					font-weight: 400;
+					color: #999999;
+				}
+				.name {
+					font-size: 24rpx;
+					font-weight: 400;
+					color: #999999;
+				}
+				.commission-num {
+					font-size: 30rpx;
+					font-weight: 400;
+					color: #eb2b3d;
+					&::before {
+						content: '¥';
+						font-size: 22rpx;
+					}
+				}
+			}
+		}
+	}
+}
+</style>

+ 148 - 0
pages/app/commission/rankings.vue

@@ -0,0 +1,148 @@
+<!-- 分销排行 -->
+<template>
+	<view class="rankings-wrap">
+		<!-- 标题栏 -->
+		<shopro-navbar back-icon-color="#fff" :background="{}" :backTextStyle="{ color: '#fff', fontSize: '40rpx', fontWeight: '500' }" backText="分销排行榜"></shopro-navbar>
+
+		<!-- 排行榜 -->
+		<view class="rankings-list-box">
+			<scroll-view scroll-y="true" @scrolltolower="loadMore" class="scroll-box">
+				<view class="ranking-list u-flex u-row-between" v-for="(item, index) in rankingsList" :key="index">
+					<view class="list-left u-flex">
+						<view class="tag-box u-flex u-row-center u-col-center">
+							<text class="tag-text" v-if="index >= 3">{{ index + 1 }}</text>
+							<image v-else class="tag-icon" :src="rankingsIcon[index]" mode=""></image>
+						</view>
+						<image class="user-avatar" :src="item.user ? item.user.avatar : $IMG_URL + '/imgs/base_avatar.png'" mode=""></image>
+						<view class="user-info">
+							<view class="name u-m-b-10">{{ item.user ? item.user.nickname : '当前用户已注销' }}</view>
+							<view class="date">{{ $u.timeFormat(item.createtime, 'yyyy年mm月dd日') }}</view>
+						</view>
+					</view>
+					<view class="list-right y-end">
+						<view class="num u-m-b-10">{{ item.total_income }}</view>
+						<view class="des">累计收益</view>
+					</view>
+				</view>
+				<!-- 更多 -->
+				<u-loadmore v-if="rankingsList.length" height="80rpx" :status="loadStatus" icon-type="flower" color="#ccc" />
+				<view class="hack-box" style="height: 200rpx;width: 100%;"></view>
+			</scroll-view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	components: {},
+	data() {
+		return {
+			rankingsIcon: {
+				0: this.$IMG_URL + '/imgs/commission/01.png',
+				1: this.$IMG_URL + '/imgs/commission/02.png',
+				2: this.$IMG_URL + '/imgs/commission/03.png'
+			},
+			rankingsList: [], //排行榜
+			loadStatus: 'loadmore', //loadmore-加载前的状态,loading-加载中的状态,nomore-没有更多的状态
+			currentPage: 1,
+			lastPage: 1
+		};
+	},
+	computed: {},
+	onLoad() {
+		this.getRankings();
+	},
+	methods: {
+		getRankings() {
+			let that = this;
+			that.loadStatus = 'loading';
+			that.$http('commission.ranking', {
+				page: that.currentPage
+			}).then(res => {
+				if (res.code === 1) {
+					that.rankingsList = [...that.rankingsList, ...res.data.data];
+					that.lastPage = res.data.last_page;
+					that.loadStatus = that.currentPage < res.data.last_page ? 'loadmore' : 'nomore';
+				}
+			});
+		},
+
+		// 加载更多
+		loadMore() {
+			if (this.currentPage < this.lastPage) {
+				this.currentPage += 1;
+				this.getRankings();
+			}
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+.rankings-wrap {
+	background: url($IMG_URL+'/imgs/commission/rankings_bg.png') no-repeat;
+	background-size: 100% auto;
+	height: 100%;
+	overflow: hidden;
+}
+
+// 排行榜列表
+.rankings-list-box {
+	background-color: #fff;
+	border-radius: 20rpx 20rpx 0px 0px;
+	width: 690rpx;
+	height: 100%;
+	margin: 60rpx auto 0;
+	.scroll-box {
+		height: 100%;
+	}
+	.ranking-list {
+		height: 140rpx;
+		padding: 0 30rpx;
+		border-bottom: 1rpx solid #e5e5e5;
+		.list-left {
+			.tag-box {
+				width: 50rpx;
+				font-size: 36rpx;
+				font-weight: 500;
+				color: #beb4b3;
+				margin-right: 20rpx;
+				.tag-icon {
+					width: 40rpx;
+					height: 60rpx;
+				}
+			}
+			.user-avatar {
+				width: 66rpx;
+				height: 66rpx;
+				border-radius: 50%;
+				margin-right: 30rpx;
+			}
+			.user-info {
+				.name {
+					font-size: 28rpx;
+					font-weight: bold;
+					color: #333333;
+				}
+				.date {
+					font-size: 24rpx;
+					font-weight: 400;
+					color: #999999;
+				}
+			}
+		}
+		.list-right {
+			.num {
+				font-size: 30rpx;
+				font-weight: 500;
+				color: #5e4ddf;
+			}
+			.des {
+				font-size: 24rpx;
+				font-weight: 500;
+				color: #a09a98;
+			}
+		}
+	}
+}
+</style>

+ 255 - 0
pages/app/commission/share-log.vue

@@ -0,0 +1,255 @@
+<!-- 分享记录 -->
+<template>
+	<view class="page_box">
+		<!-- 标题栏 -->
+		<view class="head_box">
+			<view class="nav-box">
+				<!-- 标题栏 -->
+				<shopro-navbar back-icon-color="#fff" :background="{}" :backTextStyle="{ color: '#fff', fontSize: '40rpx', fontWeight: '500' }" backText="分享记录"></shopro-navbar>
+			</view>
+			<!-- 分类tab -->
+			<view class="tab-box u-flex">
+				<view class="tab-item u-flex-1 " v-for="(tab, index) in tabsList" :key="tab.value" @tap="onTab(tab.value)">
+					<text class="tab-title" :class="{ 'title-active': tabCurrent === tab.value }">{{ tab.name }}</text>
+					<text class="underline" :class="{ 'underline-active': tabCurrent === tab.value }"></text>
+				</view>
+			</view>
+		</view>
+
+		<view class="content_box">
+			<scroll-view scroll-y="true" @scrolltolower="loadMore" class="scroll-box">
+				<!-- 分享记录列表 -->
+				<view class="log-list u-flex" v-for="item in shareLogList" :key="item.id">
+					<view class="log-avatar-wrap"><image class="log-avatar" :src="item.user.avatar" mode=""></image></view>
+
+					<view class="item-right">
+						<view class="name">{{ item.user.nickname }}</view>
+						<view class="content u-flex">
+							<view class="content-img-wrap" v-if="item.type_data"><image class="content-img" :src="item.type_data.image" mode=""></image></view>
+
+							<view class="content-text">{{ item.msg }}</view>
+						</view>
+						<view class="item-bottom u-flex u-row-between">
+							<view class="time">{{ $u.timeFormat(item.createtime, 'yyyy年mm月dd日 hh:MM') }}</view>
+							<view class="from-text">来自{{ typeObj[item.type] }}分享</view>
+						</view>
+					</view>
+				</view>
+				<!-- 缺省页 -->
+				<shopro-empty v-if="isEmpty" :image="$IMG_URL + '/imgs/empty/no_data.png'" tipText="暂无数据"></shopro-empty>
+				<!-- 更多 -->
+				<u-loadmore v-if="shareLogList.length" height="80rpx" :status="loadStatus" icon-type="flower" color="#ccc" />
+			</scroll-view>
+		</view>
+		<view class="foot_box"></view>
+	</view>
+</template>
+
+<script>
+export default {
+	components: {},
+	data() {
+		return {
+			shareLogList: [], //分享记录
+			tabCurrent: 'all', //默认
+			tabsList: [
+				{
+					name: '全部',
+					value: 'all'
+				},
+				{
+					name: '名片',
+					value: 'index'
+				},
+				{
+					name: '商品',
+					value: 'goods'
+				},
+				{
+					name: '拼团',
+					value: 'groupon'
+				}
+			],
+			typeObj: {
+				index: '名片',
+				goods: '商品',
+				groupon: '拼团'
+			},
+			loadStatus: 'loadmore', //loadmore-加载前的状态,loading-加载中的状态,nomore-没有更多的状态
+			currentPage: 1,
+			lastPage: 1,
+			isEmpty: false
+		};
+	},
+	computed: {},
+	onLoad() {
+		this.getShareLog();
+	},
+	onPullDownRefresh() {
+		this.currentPage = 1;
+		this.lastPage = 1;
+		this.shareLogList = [];
+		this.getShareLog();
+	},
+	methods: {
+		// 切换分类
+		onTab(type) {
+			if (this.tabCurrent !== type) {
+				this.tabCurrent = type;
+				this.currentPage = 1;
+				this.lastPage = 1;
+				this.shareLogList = [];
+				this.getShareLog();
+			}
+		},
+
+		// 分享记录数据
+		getShareLog() {
+			let that = this;
+			that.loadStatus = 'loading';
+			that.$http('commission.share', {
+				type: that.tabCurrent,
+				page: that.currentPage
+			}).then(res => {
+				uni.stopPullDownRefresh();
+				if (res.code === 1) {
+					that.shareLogList = [...that.shareLogList, ...res.data.data];
+					that.lastPage = res.data.last_page;
+					that.isEmpty = !that.shareLogList.length;
+					that.loadStatus = that.currentPage < res.data.last_page ? 'loadmore' : 'nomore';
+				}
+			});
+		},
+
+		// 加载更多
+		loadMore() {
+			if (this.currentPage < this.lastPage) {
+				this.currentPage += 1;
+				this.getShareLog();
+			}
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+// nav
+.head_box {
+	height: 280rpx;
+	background: url($IMG_URL+'/imgs/commission/share_head_bg.png') no-repeat;
+	background-size: 100% auto;
+	position: relative;
+}
+
+// 分类
+.tab-box {
+	background-color: #fff;
+	border-radius: 20rpx 20rpx 0px 0px;
+	position: absolute;
+	width: 100%;
+	bottom: 0;
+
+	.tab-item {
+		height: 100%;
+		display: flex;
+		flex-direction: column;
+		align-items: center;
+		justify-content: center;
+
+		.tab-title {
+			color: #666;
+			font-weight: 500;
+			font-size: 28rpx;
+			line-height: 90rpx;
+		}
+
+		.title-active {
+			color: #333;
+		}
+
+		.underline {
+			display: block;
+			width: 68rpx;
+			height: 4rpx;
+			background: #fff;
+			border-radius: 2rpx;
+		}
+
+		.underline-active {
+			background: #5e49c3;
+			display: block;
+			width: 68rpx;
+			height: 4rpx;
+			border-radius: 2rpx;
+		}
+	}
+}
+
+// 分享记录列表
+.log-list {
+	background-color: #fff;
+	padding: 30rpx;
+	margin: 10rpx 0;
+	align-items: flex-start;
+
+	.log-avatar-wrap {
+		margin-right: 14rpx;
+
+		.log-avatar {
+			width: 40rpx;
+			height: 40rpx;
+			border-radius: 50%;
+		}
+	}
+
+	.item-right {
+		flex: 1;
+		.name {
+			font-size: 26rpx;
+			font-weight: 500;
+			color: #7f7aa5;
+			margin-bottom: 30rpx;
+		}
+
+		.content {
+			background: rgba(241, 241, 241, 0.46);
+			border-radius: 2rpx;
+			padding: 10rpx;
+			margin-bottom: 20rpx;
+
+			.content-img-wrap {
+				margin-right: 16rpx;
+				width: 80rpx;
+				height: 80rpx;
+
+				.content-img {
+					width: 80rpx;
+					height: 80rpx;
+					border-radius: 6rpx;
+				}
+			}
+
+			.content-text {
+				font-size: 24rpx;
+				font-weight: 500;
+				color: #333333;
+			}
+		}
+
+		.item-bottom {
+			width: 100%;
+			.time {
+				font-size: 22rpx;
+				font-weight: 500;
+				color: #c8c8c8;
+			}
+
+			.from-text {
+				font-size: 22rpx;
+				font-weight: 500;
+				color: #c8c8c8;
+			}
+		}
+	}
+}
+</style>

+ 344 - 0
pages/app/commission/team.vue

@@ -0,0 +1,344 @@
+<!-- 我的团队 -->
+<template>
+	<view class="page_box team-wrap">
+		<view class="head_box">
+			<!-- 标题栏 -->
+			<shopro-navbar back-icon-color="#fff" :background="{}" :backTextStyle="{ color: '#fff', fontSize: '40rpx', fontWeight: '500' }" backText="我的团队"></shopro-navbar>
+			<!-- 推荐人 -->
+			<view class="referrer-box u-flex u-p-x-20" v-if="referrerInfo && referrerInfo.avatar">
+				推荐人:
+				<image class="referrer-avatar u-m-r-10" :src="referrerInfo.avatar" mode=""></image>
+				{{ referrerInfo.nickname }}
+			</view>
+
+			<!-- 团队数据总览 -->
+			<view class="team-data-box u-flex u-row-between">
+				<view class="data-card">
+					<view class="total-item">
+						<view class="item-title">团队总人数(人)</view>
+						<view class="total-num">{{ userInfo.child_user_count || 0 }}</view>
+					</view>
+					<view class="category-item u-flex">
+						<view class=" u-flex-1">
+							<view class="item-title">一级成员</view>
+							<view class="category-num">{{ userInfo.child_user_count_1 || 0 }}</view>
+						</view>
+						<view class=" u-flex-1">
+							<view class="item-title">二级成员</view>
+							<view class="category-num">{{ userInfo.child_user_count_2 || 0 }}</view>
+						</view>
+					</view>
+				</view>
+				<view class="data-card">
+					<view class="total-item">
+						<view class="item-title">团队分销商人数(人)</view>
+						<view class="total-num">{{ agentInfo.child_agent_count || 0 }}</view>
+					</view>
+					<view class="category-item u-flex">
+						<view class=" u-flex-1">
+							<view class="item-title">一级分销商</view>
+							<view class="category-num">{{ agentInfo.child_agent_count_1 || 0 }}</view>
+						</view>
+						<view class="u-flex-1 ">
+							<view class="item-title">二级分销商</view>
+							<view class="category-num">{{ agentInfo.child_agent_count_2 || 0 }}</view>
+						</view>
+					</view>
+				</view>
+			</view>
+		</view>
+
+		<view class="content_box">
+			<scroll-view scroll-y="true" @scrolltolower="loadMore" class="scroll-box">
+				<!-- 团队列表 -->
+				<view class="team-box">
+					<view class="team-list" v-for="(item, index) in teamList" :key="item.id">
+						<sh-collapse-item
+							:avatar="item.avatar"
+							:dateTime="item.createtime"
+							:level="item.agent ? item.agent.level : null"
+							:name="item.nickname"
+							:isUnfold="item.isUnfold"
+							:childNum="item.child_user_count_1"
+							@click="onTeamList(item.id, index)"
+						>
+							<view slot="collapse-children" v-if="childrenTeamList.length">
+								<view class="team-children u-flex" v-for="children in childrenTeamList" :key="children.id">
+									<image class="head-img" :src="children.avatar" mode=""></image>
+									<view class="head-info">
+										<view class="name-box u-flex">
+											<view class="name-text">{{ children.nickname }}</view>
+											<view class="grade-tag tag-box u-flex" v-if="children.agent">
+												<image class="tag-img" :src="children.agent ? children.agent.level.image : ''" mode=""></image>
+												<text class="tag-title">{{ children.agent ? children.agent.level.name : '' }}</text>
+											</view>
+										</view>
+										<view class="head-time">{{ $u.timeFormat(children.createtime, 'yyyy年mm月dd日') }}</view>
+									</view>
+								</view>
+								<button class="cu-btn refresh-btn u-flex u-row-center u-col-center" @tap.stop="childrenLoadMore(item.id)">
+									<view
+										v-if="childrenCurrentPage < childrenLastPage"
+										class="u-iconfont uicon-reload u-m-r-10"
+										:class="{ 'refresh-active': isRefresh }"
+										style="color: #999;font-size: 26rpx;"
+									></view>
+									{{ childrenLoad ? '点击加载更多' : '没有更多~' }}
+								</button>
+							</view>
+						</sh-collapse-item>
+					</view>
+				</view>
+				<!-- 缺省页 -->
+				<shopro-empty v-if="isEmpty" marginTop="50rpx" :image="$IMG_URL + '/imgs/empty/no_team.png'" tipText="暂无团队人员"></shopro-empty>
+				<!-- 更多 -->
+				<u-loadmore v-if="teamList.length" height="80rpx" :status="loadStatus" icon-type="flower" color="#ccc" />
+			</scroll-view>
+		</view>
+	</view>
+</template>
+
+<script>
+import shCollapseItem from '../components/sh-collapse-item.vue';
+import { mapMutations, mapActions, mapState, mapGetters } from 'vuex';
+export default {
+	components: {
+		shCollapseItem
+	},
+	data() {
+		return {
+			isEmpty: false,
+			referrerInfo: {}, //推荐人信息
+			twoTeamCount: 0, //二级成员
+			teamList: [], //团队列表
+			loadStatus: 'loadmore', //loadmore-加载前的状态,loading-加载中的状态,nomore-没有更多的状态
+			currentPage: 1,
+			lastPage: 1,
+			// 二级
+			childrenTeamList: [],
+			childrenCurrentPage: 1,
+			childrenLastPage: 1,
+			childrenLoad: false,
+			isRefresh: false
+		};
+	},
+	computed: {
+		...mapGetters(['userInfo', 'agentInfo'])
+	},
+	onLoad() {
+		this.getTeam();
+		this.getAgent();
+	},
+	methods: {
+		...mapActions(['getAgent']),
+		// 点击队员项
+		onTeamList(id, current) {
+			this.childrenTeamList = [];
+			this.childrenCurrentPage = 1;
+			this.childrenLastPage = 1;
+			this.childrenLoad = false;
+			this.isRefresh = false;
+			!this.teamList[current].isUnfold && this.teamList[current].child_user_count_1 && this.getChildrenTeam(id);
+			this.teamList.map((item, index) => {
+				if (index === current) {
+					item.isUnfold ? (item.isUnfold = false) : (item.isUnfold = true);
+				} else {
+					item.isUnfold = false;
+				}
+			});
+		},
+
+		// 团队列表
+		getTeam() {
+			let that = this;
+			that.loadStatus = 'loadmore';
+			that.$http('commission.team', {
+				page: that.currentPage
+			}).then(res => {
+				if (res.code === 1) {
+					that.referrerInfo = res.data.parent_user;
+					let arr = res.data.teams.data;
+					arr.map(item => {
+						item.isUnfold = false;
+					});
+					that.teamList = [...that.teamList, ...arr];
+					that.isEmpty = !that.teamList.length;
+					that.lastPage = res.data.teams.last_page;
+					that.loadStatus = that.currentPage < res.data.teams.last_page ? 'loadmore' : 'nomore';
+				}
+			});
+		},
+
+		// 二级队员
+		getChildrenTeam(id) {
+			let that = this;
+			that.$http(
+				'commission.team',
+				{
+					id: id,
+					page: that.childrenCurrentPage
+				},
+				'加载中...'
+			).then(res => {
+				if (res.code === 1) {
+					that.childrenTeamList = [...that.childrenTeamList, ...res.data.teams.data];
+					that.childrenLastPage = res.data.teams.last_page;
+					that.childrenLoad = that.childrenCurrentPage < res.data.teams.last_page;
+				}
+			});
+		},
+
+		// 二级加载更多
+		childrenLoadMore(id) {
+			if (!this.isRefresh) {
+				// 加载更多
+				if (this.childrenCurrentPage < this.childrenLastPage) {
+					this.isRefresh = true;
+					this.childrenCurrentPage += 1;
+					this.getChildrenTeam(id);
+				}
+			}
+		},
+
+		// 加载更多
+		loadMore() {
+			if (this.currentPage < this.lastPage) {
+				this.currentPage += 1;
+				this.getTeam();
+			}
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+// 推荐人
+.referrer-box {
+	font-size: 28rpx;
+	font-weight: 500;
+	color: #ffffff;
+	margin-top: 10rpx;
+	.referrer-avatar {
+		width: 34rpx;
+		height: 34rpx;
+		border-radius: 50%;
+	}
+}
+// 二级加载更多按钮
+.refresh-btn {
+	width: 100%;
+	line-height: 100rpx;
+	background: #ffffff;
+	border-radius: 25rpx;
+	font-size: 22rpx;
+	font-weight: 500;
+	color: #999999;
+	white-space: nowrap;
+}
+.refresh-active {
+	transform: rotate(360deg);
+	transition: all linear 0.5s;
+}
+// 头部卡片
+.head_box {
+	background: url($IMG_URL+'/imgs/commission/card_bg.png') no-repeat;
+	background-size: 100% 100%;
+	padding-bottom: 30rpx;
+}
+
+// 团队信息总览
+.team-data-box {
+	margin: 30rpx 20rpx 0;
+
+	.data-card {
+		width: 340rpx;
+		background: #ffffff;
+		border-radius: 20rpx;
+		padding: 20rpx;
+
+		.item-title {
+			font-size: 22rpx;
+			font-weight: 500;
+			color: #999999;
+			line-height: 30rpx;
+			margin-bottom: 10rpx;
+		}
+
+		.total-item {
+			margin-bottom: 20rpx;
+		}
+
+		.total-num {
+			font-size: 38rpx;
+			font-weight: 500;
+			color: #333333;
+		}
+
+		.category-num {
+			font-size: 26rpx;
+			font-weight: 500;
+			color: #333333;
+		}
+	}
+}
+
+// 团队列表
+.team-box {
+	.team-list {
+		.team-children {
+			margin-left: 80rpx;
+			margin-right: 20rpx;
+			height: 132rpx;
+			border-bottom: 1rpx solid #eee;
+
+			.head-img {
+				width: 60rpx;
+				height: 60rpx;
+				border-radius: 50%;
+				margin-right: 38rpx;
+			}
+
+			.head-info {
+				.head-time {
+					font-size: 22rpx;
+					font-weight: 400;
+					color: #999999;
+				}
+
+				.name-box {
+					margin-bottom: 12rpx;
+
+					.name-text {
+						font-size: 24rpx;
+						font-weight: 500;
+						color: #666;
+					}
+
+					.tag-box {
+						background: rgba(0, 0, 0, 0.2);
+						border-radius: 21rpx;
+						line-height: 30rpx;
+						padding-right: 10rpx;
+						margin-left: 10rpx;
+
+						.tag-img {
+							width: 34rpx;
+							height: 34rpx;
+							margin-right: 6rpx;
+							border-radius: 50%;
+						}
+
+						.tag-title {
+							font-size: 18rpx;
+
+							font-weight: 500;
+							color: rgba(255, 255, 255, 1);
+							line-height: 20rpx;
+						}
+					}
+				}
+			}
+		}
+	}
+}
+</style>

+ 151 - 0
pages/app/components/sh-collapse-item.vue

@@ -0,0 +1,151 @@
+<template>
+	<view class="sh-collapse-box">
+		<view class="collapse-head u-flex u-row-between">
+			<view class="u-flex" @tap="onArrow">
+				<image class="head-img" :src="avatar" mode=""></image>
+				<view class="head-info">
+					<view class="name-box u-flex">
+						<view class="name-text">{{ name }}</view>
+						<view class="grade-tag tag-box u-flex" v-if="level">
+							<image v-show="level.image" class="tag-img" :src="level.image" mode=""></image>
+							<text class="tag-title">{{ level.name }}</text>
+						</view>
+					</view>
+					<view class="u-flex">
+						<view class="head-time">{{ $u.timeFormat(dateTime, 'yyyy年mm月dd日') }}</view>
+						<view class="child-num u-m-l-30">下级成员:{{ childNum }}人</view>
+					</view>
+				</view>
+			</view>
+			<button v-if="childNum" class="u-reset-button arrow-btn" :class="{ 'arrow-active': showUnfold }" @tap="onArrow">
+				<view class="u-iconfont uicon-arrow-down u-m-l-20" style="color: #999;font-size: 26rpx;"></view>
+			</button>
+		</view>
+		<slot v-if="showUnfold" name="collapse-children"></slot>
+	</view>
+</template>
+
+<script>
+/**
+ * 用户列表项
+ * @property {String} avatar  - 头像
+ * @property {String} name  - 昵称
+ * @property {String} level  - 等级
+ * @property {String | Date} dateTime - 日期
+ * @event {Function} click - 点击箭头
+ */
+export default {
+	name: 'sh-collapse-item',
+	components: {},
+	data() {
+		return {};
+	},
+	props: {
+		avatar: {
+			type: String,
+			default: ''
+		},
+		name: {
+			type: String,
+			default: ''
+		},
+		level: {
+			type: Object,
+			default: null
+		},
+		dateTime: {
+			type: Number,
+			default: 0
+		},
+		isUnfold: {
+			type: Boolean,
+			default: false
+		},
+		childNum: {
+			type: Number,
+			default: 0
+		}
+	},
+	computed: {
+		showUnfold: {
+			get() {
+				return this.isUnfold;
+			},
+			set(val) {
+				return val;
+			}
+		}
+	},
+	methods: {
+		onArrow() {
+			this.$emit('click');
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+.sh-collapse-box {
+	background-color: #fff;
+	.collapse-head {
+		padding: 0rpx 28rpx;
+		height: 132rpx;
+		border-bottom: 1rpx solid #eee;
+		.head-img {
+			width: 66rpx;
+			height: 66rpx;
+			border-radius: 50%;
+			margin-right: 45rpx;
+		}
+		.arrow-btn {
+			background: none;
+			padding: 0;
+			color: #c4c4c4;
+			transition: all linear 0.3s;
+		}
+		.arrow-active {
+			transform: rotate(180deg);
+			transform-origin: center center;
+			transition: all linear 0.3s;
+		}
+		.head-info {
+			.head-time,
+			.child-num {
+				font-size: 24rpx;
+				font-weight: 400;
+				color: #999999;
+			}
+			.name-box {
+				margin-bottom: 12rpx;
+				.name-text {
+					font-size: 26rpx;
+					font-weight: 500;
+					color: #111111;
+				}
+				.tag-box {
+					background: rgba(0, 0, 0, 0.2);
+					border-radius: 21rpx;
+					line-height: 30rpx;
+					padding: 5rpx 10rpx;
+					margin-left: 10rpx;
+
+					.tag-img {
+						width: 34rpx;
+						height: 34rpx;
+						margin-right: 6rpx;
+						border-radius: 50%;
+					}
+
+					.tag-title {
+						font-size: 18rpx;
+
+						font-weight: 500;
+						color: rgba(255, 255, 255, 1);
+						line-height: 20rpx;
+					}
+				}
+			}
+		}
+	}
+}
+</style>

+ 247 - 0
pages/app/coupon/detail.vue

@@ -0,0 +1,247 @@
+<!-- 优惠劵详情 -->
+<template>
+	<view class="page_box">
+		<!-- 标题栏 -->
+		<shopro-navbar back-icon-color="#fff" :background="{}" :backTextStyle="{ color: '#fff', fontSize: '36rpx', fontWeight: '500' }" backText="优惠券详情"></shopro-navbar>
+		<view class="content_box">
+			<scroll-view class="scroll-box" scroll-y="true" scroll-with-animation enable-back-to-top :scroll-into-view="scrollId" @scroll="onScroll">
+				<!-- 详情卡片 -->
+				<view class="coupon-box">
+					<view class="top u-flex-col u-col-center">
+						<view class="img-box u-flex u-row-center u-col-center"><image class="coupon-img" :src="$IMG_URL + '/imgs/coupon.png'" mode=""></image></view>
+						<view class="title">{{ couponDetail.amount || '0.00' }}元优惠券</view>
+						<view class="tip">满{{ couponDetail.enough || '0.00' }}元可用</view>
+						<button class="u-reset-button" :class="['can_use', 'can_get'].includes(btnStataus) || !btnStataus ? 'use-btn' : 'fail-btn'" @tap="goScroll">
+							{{ btnStatusText[btnStataus] || '立即领取' }}
+						</button>
+						<view class="time" v-if="couponDetail.usetime && couponDetail.usetime.start">
+							有效期:{{ $u.timeFormat(couponDetail.usetime.start, 'yyyy-mm-dd') }} 至 {{ $u.timeFormat(couponDetail.usetime.end, 'yyyy-mm-dd') }}
+						</view>
+						<view class="coupon-line"></view>
+					</view>
+					<view class="bottom">
+						<view class="notice-item">
+							<view class="notice-title">优惠券说明</view>
+							<view class="notice-detail">{{ couponDetail.description || '暂无说明~' }}</view>
+						</view>
+					</view>
+				</view>
+
+				<!-- 适用商品 -->
+				<view class="coupon-goods u-p-30" v-if="couponGoods.length">
+					<view class="coupon-goods-title" id="couponGoods">适用商品</view>
+					<view class="goods-list u-m-b-20 u-p-20" v-for="goods in couponGoods" :key="goods.id">
+						<shopro-mini-card
+							:image="goods.image"
+							:title="goods.title"
+							:subtitle="goods.subtitle"
+							:price="goods.price"
+							:originPrice="goods.original_price"
+							@click="$Router.push({ path: '/pages/goods/detail', query: { id: goods.id } })"
+						></shopro-mini-card>
+					</view>
+				</view>
+			</scroll-view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	components: {},
+	data() {
+		return {
+			couponDetail: {},
+			couponGoods: [],
+			scrollId: '',
+			nowTime: new Date().getTime(),
+			options: {},
+			btnStatusText: {
+				can_use: '立即使用',
+				used: '已使用',
+				expired: '已失效',
+				cannot_use: '暂不可用',
+				can_get: '立即领取',
+				cannot_get: '不可领取'
+			},
+			btnStataus: ''
+		};
+	},
+	computed: {},
+	onLoad() {
+		this.options = this.$Route.query;
+		this.getCouponDetail();
+		this.getCouponGoods();
+	},
+	methods: {
+		// 领取优惠劵
+		getCoupon() {
+			let that = this;
+			that.$http(
+				'coupons.get',
+				{
+					id: that.$Route.query.id
+				},
+				'领取中...'
+			).then(res => {
+				if (res.code === 1) {
+					this.options.userCouponId = res.data.id;
+					that.getCouponDetail();
+					that.$u.toast('领取成功')
+				}
+			});
+		},
+		// 优惠券详情
+		getCouponDetail() {
+			let that = this;
+			that.$http('coupons.detail', {
+				id: that.$Route.query.id,
+				user_coupons_id: that.options.userCouponId
+			}).then(res => {
+				if (res.code === 1) {
+					that.couponDetail = res.data;
+					if (res.data.status_code) {
+						this.btnStataus = res.data.status_code;
+					}
+				}
+			});
+		},
+		// 适用商品
+		getCouponGoods() {
+			let that = this;
+			that.$http('coupons.goods', {
+				id: that.$Route.query.id
+			}).then(res => {
+				if (res.code === 1) {
+					that.couponGoods = res.data.data;
+				}
+			});
+		},
+		onScroll() {
+			this.scrollId = '';
+		},
+		goScroll() {
+			if (!this.options.userCouponId) {
+				this.getCoupon();
+			} else {
+				if (this.couponDetail.goods_ids === '0' && this.btnStataus == 'can_use') {
+					this.$Router.push({
+						path: '/pages/goods/list'
+					});
+				}
+				this.scrollId = 'couponGoods';
+			}
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+.page_box {
+	background: linear-gradient(180deg, rgba(240, 199, 133, 1), rgba(246, 214, 157, 1));
+}
+.coupon-box {
+	margin: 100rpx 30rpx 0;
+	border-radius: 20rpx;
+	background-color: #fff;
+	.top {
+		border-radius: 20rpx 20rpx 0 0;
+		background-image: radial-gradient(circle at bottom left, #f6d69d, #f6d69d 16rpx, transparent 17rpx),
+			radial-gradient(circle at bottom right, #f6d69d, #f6d69d 16rpx, transparent 17rpx);
+		padding: 110rpx 0 40rpx 0;
+		position: relative;
+		z-index: 5;
+		.coupon-line {
+			width: 95%;
+			border-bottom: 2rpx dashed #f3ce90;
+			position: absolute;
+			bottom: 0;
+			left: 50%;
+			transform: translateX(-50%);
+			z-index: 6;
+		}
+		.img-box {
+			width: 140rpx;
+			height: 140rpx;
+			border-radius: 50%;
+			background: #fff;
+			position: absolute;
+			left: 50%;
+			transform: translateX(-50%);
+			top: -70rpx;
+			.coupon-img {
+				width: 100rpx;
+				height: 100rpx;
+			}
+		}
+		.title {
+			font-size: 40rpx;
+			font-weight: bold;
+			line-height: 40rpx;
+			margin-bottom: 30rpx;
+		}
+		.tip {
+			font-size: 28rpx;
+			font-weight: 500;
+			margin-bottom: 50rpx;
+		}
+		.use-btn {
+			width: 386rpx;
+			line-height: 80rpx;
+			background: linear-gradient(90deg, rgba(240, 199, 133, 1), rgba(246, 214, 157, 1));
+			border-radius: 40rpx;
+			color: rgba(#fff, 0.9);
+			margin-bottom: 30rpx;
+		}
+		.fail-btn {
+			width: 386rpx;
+			line-height: 80rpx;
+			background: rgba(245, 245, 245, 1);
+			border-radius: 40rpx;
+			font-size: 30rpx;
+
+			font-weight: 500;
+			color: rgba(188, 188, 188, 1);
+			margin-bottom: 30rpx;
+		}
+		.time {
+			color: #999;
+			font-size: 26rpx;
+		}
+	}
+	.bottom {
+		border-radius: 0 0 20rpx 20rpx;
+		background-image: radial-gradient(circle at top left, #f6d69d, #f6d69d 16rpx, transparent 17rpx),
+			radial-gradient(circle at top right, #f6d69d, #f6d69d 16rpx, transparent 17rpx);
+		padding: 40rpx 30rpx;
+		.notice-item {
+			border-bottom: 1rpx solid #eeeeee;
+			min-height: 90rpx;
+			width: 100%;
+			.notice-title {
+				font-weight: 500;
+				font-size: 26rpx;
+			}
+			.notice-detail {
+				font-size: 24rpx;
+				color: #666;
+				margin-top: 10rpx;
+				padding-bottom: 10rpx;
+				text-indent: 30rpx;
+			}
+		}
+	}
+}
+// 优惠券商品
+.coupon-goods {
+	.coupon-goods-title {
+		font-size: 30rpx;
+		font-weight: bold;
+		height: 80rpx;
+	}
+	.goods-list {
+		background: #fff;
+		border-radius: 20rpx;
+	}
+}
+</style>

+ 176 - 0
pages/app/coupon/list.vue

@@ -0,0 +1,176 @@
+<!-- 优惠劵列表  -->
+<template>
+	<view class="page_box">
+		<view class="head_box u-m-b-20">
+			<shopro-navbar back-icon-color="#333" :background="{ backgroundColor: '#fff' }">
+				<view slot="content" class="u-flex nav-wrap">
+					<view class="nav-item u-flex-1 u-flex-col u-row-center u-col-center" v-for="nav in navbarHeadState" :key="nav.value" @tap="onHead(nav.value)">
+						<view class="item-title" :class="{ 'title-active': navState === nav.value }">{{ nav.title }}</view>
+						<text class="nav-line" :class="{ 'line-active': navState === nav.value }"></text>
+					</view>
+				</view>
+			</shopro-navbar>
+			<view class="coupon-nav u-flex" v-show="navState === 'user'">
+				<button
+					class=" cu-btn nav-btn u-flex-col u-row-center u-col-center"
+					:class="{ 'btn-active': nav.id == stateCurrent }"
+					v-for="nav in couponsState"
+					:key="nav.id"
+					@tap="onNav(nav.id)"
+				>
+					<view class="item-title">{{ nav.title }}</view>
+				</button>
+			</view>
+		</view>
+		<view class="content_box">
+			<view class="coupon-list" v-for="(c, index) in couponList" :key="index" @tap="toCouponDetail(c)">
+				<shopro-coupon :state="stateCurrent" :couponData="c"></shopro-coupon>
+			</view>
+			<!-- 缺省页 -->
+			<shopro-empty marginTop="300rpx" v-if="isEmpty" :image="$IMG_URL + '/imgs/empty/empty_goods.png'" tipText="暂无此类优惠券"></shopro-empty>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	components: {},
+	data() {
+		return {
+			stateCurrent: 0,
+			isEmpty: false,
+			navState: 'event',
+			navbarHeadState: [
+				{
+					value: 'event',
+					title: '领券中心'
+				},
+				{
+					value: 'user',
+					title: '我的优惠券'
+				}
+			],
+			couponsState: [
+				{
+					id: 1,
+					title: '可使用'
+				},
+				{
+					id: 2,
+					title: '暂不可用'
+				},
+				{
+					id: 3,
+					title: '无效优惠券'
+				}
+			],
+			couponList: []
+		};
+	},
+	computed: {},
+	onLoad() {
+		this.getCouponList();
+	},
+	methods: {
+		onNav(id) {
+			if (this.stateCurrent !== id) {
+				this.stateCurrent = id;
+				this.couponList = [];
+				this.getCouponList();
+			}
+		},
+		onHead(value) {
+			if (this.navState !== value) {
+				this.navState = value;
+				if (value === 'event') {
+					this.stateCurrent = 0;
+				}
+				if (value === 'user') {
+					this.stateCurrent = 1;
+				}
+				this.couponList = [];
+				this.getCouponList();
+			}
+		},
+		jump(path, parmas) {
+			this.$Router.push({
+				path: path,
+				query: parmas
+			});
+		},
+		getCouponList() {
+			let that = this;
+			that.$http('coupons.list', {
+				type: that.stateCurrent
+			}).then(res => {
+				if (res.code === 1) {
+					that.couponList = res.data;
+					that.isEmpty = !that.couponList.length;
+				}
+			});
+		},
+
+		//跳转优惠券详情
+		toCouponDetail(data) {
+			if (data.user_coupons_id) {
+				this.jump('/pages/app/coupon/detail', { id: data.id, userCouponId: data.user_coupons_id });
+			} else {
+				this.jump('/pages/app/coupon/detail', { id: data.id });
+			}
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+.nav-item {
+	flex: 1;
+	height: 100%;
+	.item-title {
+		font-size: 30rpx;
+		font-weight: 400;
+		color: #666;
+	}
+	.title-active {
+		font-size: 32rpx;
+		font-weight: 500;
+		color: #333;
+	}
+	.nav-line {
+		width: 128rpx;
+		height: 6rpx;
+		border-radius: 3rpx;
+		background: #fff;
+	}
+
+	.line-active {
+		background: #e9b664;
+	}
+}
+.nav-wrap {
+	/* #ifdef MP-WEIXIN */
+	width: 460rpx;
+	/* #endif */
+	/* #ifndef MP-WEIXIN */
+	width: 100%;
+	/* #endif */
+}
+.coupon-nav {
+	background: #fff;
+	height: 100rpx;
+	padding: 0 30rpx;
+	.nav-btn {
+		margin-right: 10rpx;
+		font-size: 26rpx;
+		color: #666;
+	}
+	.btn-active {
+		font-size: 26rpx;
+		color: #fff;
+		background-color: #faae0c;
+	}
+}
+.coupon-list {
+	margin: 30rpx 20rpx;
+}
+</style>

+ 216 - 0
pages/app/live/list.vue

@@ -0,0 +1,216 @@
+<!-- 直播列表 -->
+<template>
+	<view class="page_box">
+		<view class="head_box">
+			<view class="live-tab">
+				<view class="live-tab__item" v-for="tab in liveTab" :key="tab.title" @tap="selTab(tab.title)">
+					<view class="live-tab__item-name">{{ tab.name }}</view>
+					<text class="live-tab__item--link" :class="{ 'live-tab__item--active': tabCur === tab.title }"></text>
+				</view>
+			</view>
+		</view>
+		<view class="content_box">
+			<scroll-view enable-back-to-top scroll-y="true" @scrolltolower="loadMore" class="scroll-box">
+				<view class="u-waterfall" v-if="!isEmpty">
+					<view id="u-left-column" class="u-column">
+						<view class="u-flex u-row-center u-col-center u-m-b-20" v-for="live in leftList" :key="live.id">
+							<shopro-live-card :type="2" :detail="live"></shopro-live-card>
+						</view>
+					</view>
+					<view id="u-left-column" class="u-column">
+						<view class="u-flex u-row-center u-col-center u-m-b-20" v-for="live in rightList" :key="live.id">
+							<shopro-live-card :type="2" :detail="live"></shopro-live-card>
+						</view>
+					</view>
+				</view>
+
+				<!-- 更多 -->
+				<u-loadmore v-if="!isEmpty" height="80rpx" :status="loadStatus" color="#ccc" />
+				<!-- 置空页 -->
+				<u-empty :show="isEmpty" mode="list"></u-empty>
+			</scroll-view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	components: {},
+	data() {
+		return {
+			tabCur: 'all',
+			isEmpty: false,
+			liveTab: [
+				{
+					title: 'all',
+					name: '全部'
+				},
+				{
+					title: 'notice',
+					name: '预告'
+				},
+				{
+					title: 'living',
+					name: '直播中'
+				},
+				{
+					title: 'lived',
+					name: '已结束'
+				}
+			],
+			liveList: [],
+			loadStatus: 'loadmore', //loadmore-加载前的状态,loading-加载中的状态,nomore-没有更多的状态
+			currentPage: 1,
+			lastPage: 1,
+
+			// 瀑布流 300-220
+			addTime: 100, //排序间隙时间
+			leftHeight: 0,
+			rightHeight: 0,
+			leftList: [],
+			rightList: [],
+			tempList: []
+		};
+	},
+	computed: {},
+	onLoad() {
+		this.getLiveList();
+	},
+	onHide() {},
+	methods: {
+		// 瀑布流相关
+		async splitData() {
+			if (!this.tempList.length) return;
+			let item = this.tempList[0];
+			if (!item) return;
+
+			// 分左右
+			if (this.leftHeight < this.rightHeight) {
+				this.leftHeight += item.goods.length ? 300 : 220;
+				this.leftList.push(item);
+			} else if (this.leftHeight > this.rightHeight) {
+				this.rightHeight += item.goods.length ? 300 : 220;
+				this.rightList.push(item);
+			} else {
+				this.leftHeight += item.goods.length ? 300 : 220;
+				this.leftList.push(item);
+			}
+
+			// 移除临时列表的第一项,如果临时数组还有数据,继续循环
+			this.tempList.splice(0, 1);
+			if (this.tempList.length) {
+				setTimeout(() => {
+					this.splitData();
+				}, this.addTime);
+			}
+		},
+		clear() {
+			this.leftList = [];
+			this.rightList = [];
+			this.leftHeight = 0;
+			this.rightHeight = 0;
+			this.tempList = [];
+		},
+		// 切换tab
+		selTab(cur) {
+			if (this.tabCur !== cur) {
+				this.tabCur = cur;
+				this.liveList = [];
+				this.currentPage = 1;
+				this.lastPage = 1;
+				this.clear();
+				this.getLiveList();
+			}
+		},
+		// 加载更多
+		loadMore() {
+			if (this.currentPage < this.lastPage) {
+				this.currentPage += 1;
+				this.getLiveList();
+			}
+		},
+		// 直播列表
+		getLiveList() {
+			let that = this;
+			that.loadStatus = 'loading';
+			that.$http('common.live', {
+				type: that.tabCur,
+				page: that.currentPage
+			}).then(res => {
+				if (res.code === 1) {
+					that.liveList = [...that.liveList, ...res.data.data];
+					that.isEmpty = !that.liveList.length;
+					that.lastPage = res.data.last_page;
+					that.loadStatus = that.currentPage < res.data.last_page ? 'loadmore' : 'nomore';
+					that.tempList = res.data.data;
+					that.splitData();
+				}
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+@mixin vue-flex($direction: row) {
+	/* #ifndef APP-NVUE */
+	display: flex;
+	flex-direction: $direction;
+	/* #endif */
+}
+.u-waterfall {
+	@include vue-flex;
+	flex-direction: row;
+	align-items: flex-start;
+}
+
+.u-column {
+	@include vue-flex;
+	flex: 1;
+	flex-direction: column;
+	height: auto;
+}
+// tab
+.live-tab {
+	width: 100%;
+	height: 96rpx;
+	background: #fff;
+	display: flex;
+
+	&__item {
+		flex: 1;
+		height: 100%;
+		display: flex;
+		flex-direction: column;
+		justify-content: space-between;
+		align-items: center;
+	}
+	&__item-name {
+		font-size: 28rpx;
+
+		font-weight: bold;
+		color: rgba(51, 51, 51, 1);
+		flex: 1;
+		display: flex;
+		justify-content: center;
+		align-items: center;
+	}
+	&__item--link {
+		width: 68rpx;
+		height: 4rpx;
+		background: transparent;
+		border-radius: 2rpx;
+	}
+	&__item--active {
+		width: 68rpx;
+		height: 4rpx;
+		background: rgba(213, 166, 90, 1);
+		border-radius: 2rpx;
+	}
+}
+// 瀑布流 list
+.scroll-box {
+	margin: 20rpx auto;
+	width: 730rpx;
+}
+</style>

+ 613 - 0
pages/app/merchant/apply.vue

@@ -0,0 +1,613 @@
+<!-- 门店入驻 -->
+<template>
+	<view class="apply-commission-wrap page_box">
+		<!-- 标题栏 -->
+		<view class="head-box"><shopro-navbar back-icon-color="#fff" :background="{}"></shopro-navbar></view>
+
+		<!-- 表单 -->
+		<view class="apply-form content_box">
+			<u-form :model="model" :rules="rules" ref="uForm" :errorType="errorType">
+				<u-form-item :labelStyle="labelStyle"   label-width="150" label-position="left" label="姓名" prop="realname">
+					<u-input placeholder="请输入姓名" :placeholderStyle="placeholderStyle" v-model="model.realname" type="text"></u-input>
+				</u-form-item>
+				<u-form-item :labelStyle="labelStyle" label-position="left" label="手机号" prop="phone" label-width="150">
+					<u-input placeholder="请输入手机号" :placeholderStyle="placeholderStyle" v-model="model.phone" type="number"></u-input>
+				</u-form-item>
+				<u-form-item :labelStyle="labelStyle" label-width="150" label-position="left" label="申请名称" prop="name">
+					<u-input placeholder="请输入门店名称" :placeholderStyle="placeholderStyle" v-model="model.name" type="text"></u-input>
+				</u-form-item>
+				<u-form-item :labelStyle="labelStyle" prop="images" label="展示图片" label-position="top" label-width="150" :borderBottom="true">
+					<u-upload
+						:placeholderStyle="placeholderStyle"
+						:showProgress="false"
+						@on-uploaded="uploadSuccess($event, 'storeImg')"
+						@on-remove="uploadRemove($event, 'storeImg')"
+						:action="`${$API_URL}/index/upload`"
+						:file-list="model.fileImages"
+						width="148"
+						height="148"
+						maxCount="1"
+					></u-upload>
+				</u-form-item>
+				<u-form-item :labelStyle="labelStyle"   label-width="150" label-position="left" label="营业时间" prop="openhours">
+					<u-input
+						type="select"
+						:select-open="selectShow"
+						v-model="model.openhours"
+						placeholder="请选择营业时间"
+						:placeholderStyle="placeholderStyle"
+						@click="onSelect('time')"
+					></u-input>
+				</u-form-item>
+				<u-form-item :labelStyle="labelStyle" label-width="150" label-position="left" label="营业天数" prop="weeks">
+					<u-input type="select" placeholder="请选择营业天数" disabled :placeholderStyle="placeholderStyle" v-model="model.weeks" @click="onSelect('week')"></u-input>
+				</u-form-item>
+				<u-form-item :labelStyle="labelStyle" right-icon="map-fill" :right-icon-style="{color:'#4CB89D'}" label-width="150" label-position="left" label="所在地区" prop="area">
+					<u-input type="text" disabled v-model="model.area" placeholder="请点击定位" @click="chooseLocation"></u-input>
+				</u-form-item>
+				<u-form-item :border-bottom="false" :labelStyle="labelStyle" label-width="150" label-position="left" label="详细地址" prop="address">
+					<u-input type="textarea" border placeholder="请输入详细地址~" :placeholderStyle="placeholderStyle" v-model="model.address"></u-input>
+				</u-form-item>
+				<view class="agreement u-flex u-col-center">
+					<u-checkbox v-model="model.agreement" activeColor="#4CB89D" shape="circle" @change="onAgreement"></u-checkbox>
+					<view class="agreement-text" @tap="toProtocol">勾选代表同意《申请协议》</view>
+				</view>
+				<view class="u-flex u-row-center u-col-center"><button class="u-reset-button save-btn" @tap="onSubmit" :disabled="isFormEnd">确认提交</button></view>
+			</u-form>
+		</view>
+
+		<!-- 弹窗 -->
+		<u-select v-if="selectShow" :mode="selectMode" :list="selectList" v-model="selectShow" @confirm="selectConfirm"></u-select>
+
+		<!-- 选择星期 -->
+		<u-popup v-model="showWeeksModal" safe-area-inset-bottom mode="bottom">
+			<view class="week-modal">
+				<view class="u-flex u-row-between u-p-x-30 week-modal--head ">
+					<view></view>
+					<view class="action text-green" @tap="saveWeekModal">确定</view>
+				</view>
+				<view class="u-flex u-flex-wrap u-p-x-30 u-p-y-30 week-modal--content">
+					<view v-for="(item, index) in weekcheckbox" class="week-btn" :key="index">
+						<button @tap="onSelectWeek(index)" class="cu-btn" :class="!item.checked ? 'line-green' : 'bg-green'">{{ item.name }}</button>
+					</view>
+				</view>
+			</view>
+		</u-popup>
+
+		<!-- 权限验证 -->
+		<u-popup v-model="showNotice" mode="center" border-radius="20">
+			<view class="notice-modal">
+				<view class="img-wrap"><image class="notice-img" :src="authNotice.img" mode=""></image></view>
+				<view class="notice-title">{{ authNotice.title }}</view>
+				<view class="notice-detail">{{ authNotice.detail || '' }}</view>
+				<button class="u-reset-button notice-btn" @tap="onAuthBtn(authNotice.btnPath)">{{ authNotice.btnText }}</button>
+				<button class="u-reset-button back-btn" @tap="$Router.back()">返回</button>
+			</view>
+		</u-popup>
+	</view>
+</template>
+
+<script>
+import Auth from '@/shopro/permission/index.js';
+import { mapMutations, mapActions, mapState,mapGetters } from 'vuex';
+import { MAP_KEY } from '@/env.js';
+export default {
+	components: {},
+	data() {
+		return {
+			showNotice: false,
+			showWeeksModal: false, //星期弹窗
+			isFormEnd: false,
+			weekcheckbox: [
+				{
+					value: '1',
+					name: '周一',
+					checked: false
+				},
+
+				{
+					value: '2',
+					name: '周二',
+					checked: false
+				},
+				{
+					value: '3',
+					name: '周三',
+					checked: false
+				},
+				{
+					value: '4',
+					name: '周四',
+					checked: false
+				},
+				{
+					value: '5',
+					name: '周五',
+					checked: false
+				},
+				{
+					value: '6',
+					name: '周六',
+					checked: false
+				},
+				{
+					value: '7',
+					name: '周日',
+					checked: false
+				}
+			],
+			authNotice: {},
+			// 表单
+			errorType: ['message'],
+			selectShow: false,
+			selectMode: 'mutil-column', // single-column, mutil-column, mutil-column-auto
+			selectType: '',
+			selectList: [],
+			labelStyle: {
+				'font-size': '28rpx',
+				'font-weight': '500',
+				color: '#333'
+			},
+			placeholderStyle: 'font-size: 28rpx;color:#c4c4c4;',
+			model: {
+				name: '', //门店名称
+				phone: '', //手机号
+				realname: '', //真实姓名
+				openhours: '', //营业时间
+				openweeks: '', //营业周期
+				weeks: '',
+				area: '',
+				area_id: '', //行政区域ID
+				address: '', //门店地址
+				latitude: '', //纬度
+				longitude: '', //京都
+				images: [], //门店图片
+				images_original: [],
+				fileImages: [],
+				agreement: false
+			},
+			rules: {
+				name: [
+					{
+						required: true,
+						message: '请输入门店名称',
+						trigger: ['change', 'blur']
+					}
+				],
+				realname: [
+					{
+						required: true,
+						message: '请输入真实姓名',
+						trigger: ['change', 'blur']
+					}
+				],
+				area: [
+					{
+						required: true,
+						message: '请定位所在地区',
+						trigger: ['change', 'blur']
+					}
+				],
+				phone: [
+					{
+						required: true,
+						message: '请输入手机号',
+						trigger: ['change', 'blur']
+					},
+					{
+						validator: (rule, value, callback) => {
+							return this.$u.test.mobile(value);
+						},
+						message: '手机号码不正确',
+						// 触发器可以同时用blur和change,二者之间用英文逗号隔开
+						trigger: ['change', 'blur']
+					}
+				],
+				openhours: [
+					{
+						required: true,
+						message: '请选择营业时间',
+						trigger: ['change', 'blur']
+					}
+				],
+				weeks: [
+					{
+						required: true,
+						message: '请选择营业天数',
+						trigger: ['change', 'blur']
+					}
+				],
+				address: [
+					{
+						required: true,
+						message: '请输入详细地址',
+						trigger: ['change', 'blur']
+					}
+				]
+			}
+		};
+	},
+	computed: {
+	...mapGetters(['initStore']),
+		selectWorkerTime() {
+			let mArr = [];
+			for (let i = 0; i <= 24; i++) {
+				let t = String(i).padStart(2, '0');
+				mArr.push(
+					{
+						value: `${t}:00`,
+						label: `${t}:00`
+					},
+					{
+						value: `${t}:30`,
+						label: `${t}:30`
+					}
+				);
+			}
+			mArr.pop();
+			return [mArr, mArr];
+		}
+	},
+	onLoad() {
+		this.getStoreInfo();
+	},
+	onReady() {
+		this.$refs.uForm.setRules(this.rules);
+	},
+	methods: {
+		// 选择星期
+		saveWeekModal() {
+			this.showWeeksModal = false;
+			let arr = [];
+			let arr2 = [];
+			this.weekcheckbox.forEach(item => {
+				if (item.checked) {
+					arr.push(item.value);
+					arr2.push(item.name);
+				}
+			});
+			this.model.openweeks = arr.join(',');
+			this.model.weeks = arr2.join(',');
+		},
+
+		// 选择星期
+		onSelectWeek(index) {
+			this.weekcheckbox[index].checked = !this.weekcheckbox[index].checked;
+		},
+
+		// 弹窗按钮
+		onAuthBtn(path) {
+			path &&
+				this.$Router.push({
+					path: path
+				});
+			this.showNotice = false;
+		},
+
+		// 上传图片成功
+		uploadSuccess(e, type) {
+			switch (type) {
+				case 'storeImg':
+					this.model.images = [];
+					e.forEach(item => {
+						this.model.images.push(item.response.data.url);
+					});
+					break;
+				default:
+					return;
+			}
+		},
+
+		// 移除图片
+		uploadRemove(index, type) {
+			switch (type) {
+				case 'storeImg':
+					this.model.images.splice(index, 1);
+					break;
+				default:
+					return;
+			}
+		},
+
+		// 地址选择
+		async chooseLocation() {
+			let authState = await new Auth('userLocation').check();
+			authState &&
+				uni.chooseLocation({
+					success: res => {
+						this.model.latitude = res.latitude;
+						this.model.longitude = res.longitude;
+						this.getLocationInfo();
+					},
+					fail: err => {
+						console.log(err);
+					}
+				});
+		},
+
+		//逆坐标解析
+		async getLocationInfo() {
+			this.chooseAddress = '定位中...';
+			const [error, res] = await uni.request({
+				url: `https://restapi.amap.com/v3/geocode/regeo?location=${this.model.longitude},${this.model.latitude}&key=${MAP_KEY}`
+			});
+			if (res.data.status === '1') {
+				const addressComponent = res.data.regeocode.addressComponent;
+				this.model.area_id = addressComponent.adcode;
+				this.model.area = `${addressComponent.province}-${addressComponent.city.length ? addressComponent.city : addressComponent.province}-${addressComponent.district}`;
+				this.model.address = res.data.regeocode.formatted_address.replace(`${addressComponent.province}${addressComponent.city}${addressComponent.district}`, '');
+			} else {
+				console.log('%c逆地址解析失败,请检查是否在env中配置地图key', 'color:green;background:yellow');
+			}
+		},
+
+		onSelect(type) {
+			this.selectType = type;
+			switch (type) {
+				case 'time':
+					this.selectShow = true;
+					this.selectMode = 'mutil-column';
+					this.selectList = this.selectWorkerTime;
+					break;
+				case 'week':
+					this.showWeeksModal = true;
+					break;
+				default:
+					return;
+			}
+		},
+
+		// 选择时间
+		selectConfirm(e) {
+			switch (this.selectType) {
+				case 'time':
+					this.model.openhours = '';
+					e.map((val, index) => {
+						this.model.openhours += this.model.openhours == '' ? val.label : ' - ' + val.label;
+					});
+					break;
+				default:
+					return;
+			}
+		},
+
+		// 勾选同意
+		onAgreement(e) {
+			this.model.agreement = e.value;
+		},
+
+		// 门店详情
+		getStoreInfo() {
+			let that = this;
+			that.$http('store.shopInfo').then(res => {
+				if (res.code === 1) {
+					res.data && this.authStatus(res.data);
+				}
+			});
+		},
+		// 跳转门店协议
+		toProtocol() {
+			this.initStore.protocol && this.jump('/pages/public/richtext', { id: this.initStore.protocol })
+		},
+
+		// 初始化model
+		initModel() {
+			// 构建星期
+			let weeksArr = this.model.openweeks.split(',');
+			let weekTextArr = [];
+			this.weekcheckbox.forEach(item => {
+				if (weeksArr.includes(item.value)) {
+					weekTextArr.push(item.name);
+					item.checked = true;
+				}
+			});
+			this.model.weeks = weekTextArr.join(',');
+			// 构建省市区
+			if (this.model.province_name) {
+				this.model.area = `${this.model.province_name}-${this.model.city_name}-${this.model.area_name}`;
+			}
+			if (this.model.images) {
+				this.model.images.forEach(item => {
+					this.model.fileImages.push({
+						url: item
+					});
+				});
+			}
+			// 协议
+			this.$set(this.model, 'agreement', false);
+		},
+
+		// 状态鉴权
+		authStatus(data) {
+			switch (String(data.status)) {
+				case '0':
+					this.showNotice = true;
+					this.model = { ...this.model, ...data };
+					this.initModel();
+					this.authNotice = {
+						img: this.$IMG_URL + '/imgs/commission/auth_check.png',
+						title: '正在审核中',
+						detail: data.status_msg,
+						btnText: '修改信息',
+						btnPath: ''
+					};
+					break;
+				case '-1':
+					this.showNotice = true;
+					this.model = { ...this.model, ...data };
+					this.initModel();
+					this.authNotice = {
+						img: this.$IMG_URL + '/imgs/commission/auth_reject.png',
+						title: '您的申请已被驳回!',
+						detail: data.status_msg,
+						btnText: '重新申请',
+						btnPath: ''
+					};
+					break;
+				case '1':
+					this.showNotice = false;
+					break;
+
+				default:
+			}
+		},
+
+		// 申请门店
+		applyStore() {
+			let that = this;
+			this.isFormEnd = false;
+			that.$http('store.apply', that.model, '提交中...').then(res => {
+				this.isFormEnd = true;
+				if (res.code === 1) {
+					//  #ifdef MP-WEIXIN
+					this.$store.commit('subscribeMessage', 'storeApply');
+					//  #endif
+					uni.showToast({
+						title: res.msg,
+						success: () => {
+							that.$Router.back();
+						}
+					});
+				}
+			});
+		},
+
+		// 提交
+		onSubmit() {
+			let that = this;
+			if (!that.model.images.length) {
+				this.$u.toast('请上传门店图片');
+				return;
+			}
+			this.$refs.uForm.validate(valid => {
+				if (valid) {
+					if (!this.model.agreement) return this.$u.toast('请勾选协议');
+					this.applyStore();
+				} else {
+					this.$u.toast('请完善表单');
+				}
+			});
+		},
+		jump(path, parmas) {
+			this.$Router.push({
+				path: path,
+				query: parmas
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+.apply-commission-wrap {
+	background-color: #fff;
+	.head-box {
+		background: url($IMG_URL+'/imgs/user/sh_leader_apply_head.png') no-repeat;
+		background-size: 100% auto;
+		height: 370rpx;
+	}
+}
+// 表单
+.apply-form {
+	width: 750rpx;
+	background: #ffffff;
+	border-radius: 20rpx;
+	padding: 30rpx;
+	.agreement {
+		margin-top: 20rpx;
+		.agreement-text {
+			font-size: 24rpx;
+			font-weight: 500;
+			color: #4cb89d;
+		}
+	}
+	.save-btn {
+		width: 690rpx;
+		line-height: 86rpx;
+		background: linear-gradient(90deg, #2dae9c, #6bc29e);
+		box-shadow: 0px 7rpx 11rpx 2rpx rgba(61, 179, 156, 0.34);
+		border-radius: 43rpx;
+		font-weight: 500;
+		color: #ffffff;
+		margin: 30rpx 0;
+	}
+}
+
+// 星期
+.week-modal {
+	.week-modal--head {
+		height: 100rpx;
+		border-bottom: 1rpx solid $u-border-color;
+
+		.text-cancel {
+			color: #ccc;
+		}
+		.text-green {
+			color: #18b566;
+		}
+	}
+	.week-modal--content {
+		.week-btn {
+			margin-right: 56rpx;
+			margin-bottom: 30rpx;
+			&:nth-of-type(4n) {
+				margin-right: 0;
+			}
+		}
+	}
+}
+
+// 提示
+.notice-modal {
+	display: flex;
+	flex-direction: column;
+	justify-content: center;
+	align-items: center;
+	width: 612rpx;
+	min-height: 658rpx;
+	background: #ffffff;
+	padding: 30rpx;
+	border-radius: 20rpx;
+	.img-wrap {
+		margin-bottom: 50rpx;
+		.notice-img {
+			width: 180rpx;
+			height: 170rpx;
+		}
+	}
+	.notice-title {
+		font-size: 35rpx;
+		font-weight: bold;
+		color: #46351b;
+		margin-bottom: 28rpx;
+	}
+	.notice-detail {
+		font-size: 28rpx;
+		font-weight: 400;
+		color: #999999;
+		line-height: 36rpx;
+		margin-bottom: 50rpx;
+	}
+	.notice-btn {
+		width: 492rpx;
+		height: 70rpx;
+		line-height: 70rpx;
+		background: linear-gradient(90deg, #2dae9c, #6bc29e);
+		box-shadow: 0 7rpx 11rpx 2rpx rgba(61, 179, 156, 0.34);
+		border-radius: 35rpx;
+		font-size: 28rpx;
+		font-weight: 500;
+		color: #ffffff;
+		margin-bottom: 10rpx;
+	}
+	.back-btn {
+		width: 492rpx;
+		height: 70rpx;
+		line-height: 70rpx;
+		font-size: 28rpx;
+		font-weight: 500;
+		color: #3ab29c;
+		background: none;
+	}
+}
+</style>

+ 227 - 0
pages/app/merchant/detail.vue

@@ -0,0 +1,227 @@
+<!-- 门店订单详情 -->
+<template>
+	<view class="order-detail-wrap">
+		<!-- 订单卡片 -->
+		<view class="card-box">
+			<view class="order-goods-item u-m-b-10" v-for="item in orderDetail.item" :key="item.id">
+				<shopro-mini-card :title="item.goods_title" :image="item.goods_image">
+					<template #describe>
+						<view class="order-sku u-ellipsis-1">
+							<text class="order-num">数量:{{ item.goods_num || 0 }};</text>
+							{{ item.goods_sku_text ? item.goods_sku_text : '' }}
+						</view>
+					</template>
+					<template #cardBottom>
+						<view class="card-price-box u-flex">
+							<text class="order-price font-OPPOSANS">¥{{ item.goods_price || 0 }}</text>
+							<button class="u-reset-button status-btn" v-if="item.status_name">{{ item.status_name }}</button>
+						</view>
+					</template>
+				</shopro-mini-card>
+				<view class="goods-phone card-item" v-show="item.ext_arr.dispatch_phone">
+					<text class="item-title">预留电话:</text>
+					<text class="item-content">{{ item.ext_arr.dispatch_phone }}</text>
+				</view>
+				<view class="goods-date card-item">
+					<text class="item-title" v-show="item.dispatch_type === 'selfetch'">到店/自提时间:</text>
+					<text class="item-title" v-show="item.dispatch_type === 'store'">配送时间:</text>
+					<text class="item-content">{{ item.ext_arr.dispatch_date || '' }}</text>
+				</view>
+			</view>
+		</view>
+		<!-- 订单信息 -->
+		<view class="order-detail-card">
+			<view class="card-title u-flex ">订单信息</view>
+			<view class="detial-content">
+				<view class="detail-item u-flex">
+					<view class="item-title">订单状态:</view>
+					<view class="item-content">{{ orderDetail.status_name }}</view>
+				</view>
+				<view class="detail-item u-flex">
+					<view class="item-title">订单编号:</view>
+					<view class="item-content">{{ orderDetail.order_sn }}</view>
+				</view>
+				<view class="detail-item u-flex">
+					<view class="item-title">下单时间:</view>
+					<view class="item-content">{{ orderDetail.paytime }}</view>
+				</view>
+				<view class="detail-item u-flex" v-if="orderDetail.remark">
+					<view class="item-title">备注:</view>
+					<view class="item-content">{{ orderDetail.remark }}</view>
+				</view>
+				<view class="detail-item address-item" v-if="orderType.includes('store')">
+					<view class="item-title">配送地址:</view>
+					<view class="item-content address-content">
+						{{ orderDetail.province_name || '' }}{{ orderDetail.city_name || '' }}{{ orderDetail.area_name || '' }}{{ orderDetail.address || '' }}
+					</view>
+				</view>
+			</view>
+		</view>
+		<view class="bottom-hack" v-if="orderDetail.status_code == 'nosend'">
+			<view class="bottom-box u-flex u-row-center u-col-center"><button class="cu-btn send-btn" @tap="sendOrder">发货</button></view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	components: {},
+	data() {
+		return {
+			orderDetail: {},
+			orderType: []
+		};
+	},
+	computed: {},
+	onLoad() {
+		this.getOrderDetail();
+	},
+	methods: {
+		// 订单详情
+		getOrderDetail() {
+			let that = this;
+			that.$http('store.orderDetail', {
+				id: that.$Route.query.orderId,
+				store_id: uni.getStorageSync('storeId')
+			}).then(res => {
+				if (res.code === 1) {
+					that.orderDetail = res.data;
+					that.orderDetail.paytime = that.$u.timeFormat(res.data.paytime, 'yyyy-mm-dd hh:MM');
+					that.orderDetail.item.forEach(goods => {
+						that.orderType.push(goods.dispatch_type);
+					});
+				}
+			});
+		},
+		// 订单发货
+		sendOrder() {
+			let that = this;
+			that.$http(
+				'store.orderSend',
+				{
+					id: that.orderDetail.id,
+					store_id: uni.getStorageSync('storeId')
+				},
+				'发货中...'
+			).then(res => {
+				if (res.code === 1) {
+					that.$u.toast(res.msg);
+					that.getOrderDetail();
+				}
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+.order-detail-wrap {
+	height: 100%;
+	.bottom-hack {
+		width: 750rpx;
+		height: 100rpx;
+	}
+	.bottom-box {
+		position: fixed;
+		width: 750rpx;
+		height: 100rpx;
+		bottom: 0;
+		left: 0;
+		background-color: #fff;
+		padding: 10rpx 0;
+		.send-btn {
+			width: 710rpx;
+			height: 80rpx;
+			background: linear-gradient(90deg, #2eae9c, #6cc29f);
+			border: 1rpx solid rgba(237, 237, 237, 1);
+			border-radius: 40rpx;
+			font-size: 30rpx;
+			font-weight: 500;
+			color: rgba(255, 255, 255, 1);
+		}
+	}
+}
+// 订单卡片
+.card-box {
+	margin-bottom: 20rpx;
+	background-color: #fff;
+	.order-goods-item {
+		border-bottom: 1rpx solid rgba(237, 237, 237, 1);
+		padding: 20rpx;
+		.card-item {
+			line-height: 60rpx;
+			.item-title {
+				font-size: 28rpx;
+				color: #999;
+			}
+			.item-content {
+				font-size: 26rpx;
+				color: #333;
+			}
+		}
+	}
+	.order-sku {
+		font-size: 24rpx;
+
+		font-weight: 400;
+		color: rgba(153, 153, 153, 1);
+		width: 450rpx;
+		margin-bottom: 20rpx;
+		.order-num {
+			margin-right: 10rpx;
+		}
+	}
+	.card-price-box {
+		.status-btn {
+			height: 32rpx;
+			border: 1rpx solid rgba(207, 169, 114, 1);
+			border-radius: 15rpx;
+			font-size: 20rpx;
+			font-weight: 400;
+			color: rgba(168, 112, 13, 1);
+			padding: 0 10rpx;
+			margin-left: 20rpx;
+			background: rgba(233, 183, 102, 0.16);
+		}
+		.order-price {
+			font-size: 26rpx;
+
+			font-weight: 600;
+			color: rgba(51, 51, 51, 1);
+		}
+	}
+}
+.order-detail-card {
+	background-color: #fff;
+	margin: 20rpx 0;
+	.card-title {
+		font-size: 30rpx;
+
+		font-weight: 500;
+		color: rgba(51, 51, 51, 1);
+		height: 80rpx;
+		padding: 0 20rpx;
+		border-bottom: 1rpx solid rgba(223, 223, 223, 0.5);
+	}
+	.detial-content {
+		padding: 20rpx;
+		.detail-item {
+			min-height: 60rpx;
+			.item-title {
+				font-size: 28rpx;
+				color: #999;
+			}
+			.item-content {
+				font-size: 26rpx;
+				color: #333;
+			}
+		}
+		.address-item {
+			display: flex;
+			.address-content {
+				width: 550rpx;
+			}
+		}
+	}
+}
+</style>

+ 617 - 0
pages/app/merchant/index.vue

@@ -0,0 +1,617 @@
+<!-- 商家中心 -->
+<template>
+	<view class="merchant-wrap">
+		<view class="shopinfo-box">
+			<!-- 标题栏 -->
+			<shopro-navbar
+				back-icon-color="#fff"
+				:background="{ background: `url(${$IMG_URL}/imgs/user/shop_headbg.png) no-repeat top center / 100% auto`}"
+				:backTextStyle="{ color: '#fff', fontSize: '36rpx', fontWeight: '500' }"
+				backText="商家中心"
+			></shopro-navbar>
+			<!-- 商家信息 -->
+			<view class="user-head u-flex u-row-between">
+				<view class="shop-info">
+					<view class="u-flex u-m-b-10" @tap="goStoreList">
+						<text class="shop-title u-m-r-20">{{ storeDetail.name }}</text>
+						<text class="iconfont icon-xiala"></text>
+					</view>
+					<view class="shop-address u-ellipsis-2" @tap="jump('/pages/app/merchant/info')">
+						{{ storeDetail.province_name || '' }}{{ storeDetail.city_name || '' }}{{ storeDetail.area_name || '' }}{{ storeDetail.address || '' }}
+					</view>
+				</view>
+				<button @tap="$Router.pushTab('/pages/index/user')" class="u-reset-button merchant-btn">切换个人版</button>
+			</view>
+		</view>
+
+		<!-- 核销卡片 -->
+		<view class="info-card-box u-flex u-row-between">
+			<view class="info-card">
+				<image class="card-bg" :src="$IMG_URL + '/imgs/user/shop_info.png'" mode="aspectFill"></image>
+				<view class="card-content u-flex-col">
+					<view class="card-title">输码核销</view>
+					<view class="u-flex card-detail-box" @tap="showInputModal = true">
+						<text class="iconfont icon-21shuru icon-color1"></text>
+						<text class="icon-color1 u-m-l-20">输码</text>
+					</view>
+				</view>
+			</view>
+			<view class="info-card" @tap="scanCode">
+				<image class="card-bg" :src="$IMG_URL + '/imgs/user/shop_qrcode.png'" mode="aspectFill"></image>
+				<view class="card-content u-flex-col">
+					<view class="card-title">扫码核销</view>
+					<view class="u-flex card-detail-box">
+						<text class="iconfont icon-icon-test1  icon-color2"></text>
+						<text class="icon-color2 u-m-l-20">扫码</text>
+					</view>
+				</view>
+			</view>
+		</view>
+
+		<view class="cancel-shop-box">
+			<!-- 筛选框 -->
+			<view class="cancel-nav u-flex">
+				<u-dropdown ref="uDropdown" activeColor="#4cb89d" :borderBottom="false">
+					<u-dropdown-item v-model="filter1Value" @change="changeFilter1" :title="filter1Label" :options="filterList1"></u-dropdown-item>
+					<u-dropdown-item v-model="filter2Value" @change="changeFilter2" :title="filter2Label" :options="filterList2"></u-dropdown-item>
+				</u-dropdown>
+			</view>
+			<!-- 销量 -->
+			<view class="sales-volume-box u-flex u-row-between u-p-30">
+				<view class="sales-volume u-flex u-row-center u-col-center">订单量(单):{{ orderInfo.total_num || 0 }}</view>
+				<view class="sales-volume u-flex u-row-center u-col-center">交易额(元):{{ orderInfo.total_money || 0 }}</view>
+			</view>
+		</view>
+
+		<!-- 订单列表 -->
+		<view class="order-list" v-for="order in storeOrderList" :key="order.order_sn" @tap="jump('/pages/app/merchant/detail', { orderId: order.id })">
+			<view class="order-head u-flex u-row-between">
+				<text class="no">订单编号:{{ order.order_sn }}</text>
+				<text class="state">{{ order.status_name }}</text>
+			</view>
+			<view class="goods-order" v-for="item in order.item" :key="item.id">
+				<shopro-mini-card :title="item.goods_title" :image="item.goods_image">
+					<template #describe>
+						<view class="order-sku u-ellipsis-1">
+							<text class="order-num">数量:{{ item.goods_num || 0 }};</text>
+							{{ item.goods_sku_text ? item.goods_sku_text : '' }}
+						</view>
+					</template>
+					<template #cardBottom>
+						<view class="order-price-box u-flex ">
+							<text class="order-price font-OPPOSANS">¥{{ item.goods_price || 0 }}</text>
+							<button class="u-reset-button status-btn" v-if="item.status_name">{{ item.status_name }}</button>
+						</view>
+					</template>
+				</shopro-mini-card>
+			</view>
+			<view class="order-bottom u-flex">
+				<view class="u-flex u-m-r-30" v-if="reduceAdd(order.item, 'discount_fee')">
+					<text class="total-price-title">优惠:</text>
+					<text class="total-price">{{ reduceAdd(order.item, 'discount_fee') }}</text>
+				</view>
+				<view class="u-flex">
+					<text class="total-price-title">实付款:</text>
+					<text class="total-price">{{ reduceAdd(order.item, 'pay_price') }}</text>
+				</view>
+			</view>
+		</view>
+
+		<!-- 置空页 -->
+		<u-empty :show="isEmpty" margin-top="160" mode="list"></u-empty>
+
+		<!-- 更多 -->
+		<u-loadmore v-if="storeOrderList.length" height="80rpx" :status="loadStatus" icon-type="flower" color="#ccc" />
+		<!-- 日期选择 -->
+		<u-calendar
+			v-model="showCalendar"
+			safeAreaInsetBottom
+			mode="range"
+			start-text="开始"
+			end-text="结束"
+			range-color="#4CB89D"
+			range-bg-color="rgba(76,184,157,0.13)"
+			active-bg-color="#4CB89D"
+			:customStyle="{ background: 'linear-gradient(90deg, #4cb89d, #4CB89D)', boxShadow: '0 7rpx 11rpx 2rpx rgba(4cb89d, 0.34)' }"
+			btnType="success"
+			@change="selDate"
+		></u-calendar>
+		<!-- 输码弹窗 -->
+		<u-popup v-model="showInputModal" mode="center" :closeable="true" close-icon-pos="top-left" border-radius="20">
+			<view class="modal-box u-flex-col u-col-center">
+				<view class="modal-head u-flex-col u-col-center">
+					<image class="modal-head-img" :src="$IMG_URL + '/imgs/modal/store_check.png'" mode=""></image>
+					<text class="modal-head-title">输码核销</text>
+				</view>
+				<input class="inp" type="number" v-model="qrcode" placeholder="在此输入核销码" placeholder-class="pl-inp" />
+				<button class="u-reset-button post-btn" @tap="onConfirm">核销</button>
+			</view>
+		</u-popup>
+		<!-- 禁用弹窗 -->
+		<u-modal
+			ref="uModal"
+			v-model="showModal"
+			:show-cancel-button="false"
+			confirm-color="#999"
+			confirm-text="返回"
+			content="当前门店已被禁用,是否返回重新选择?"
+			title="提示"
+			@confirm="$Router.back()"
+		></u-modal>
+	</view>
+</template>
+
+<script>
+import { mapMutations, mapActions, mapState } from 'vuex';
+import Auth from '@/shopro/permission/index.js';
+export default {
+	components: {},
+	data() {
+		return {
+			isEmpty: false,
+			showModal: false,
+			// 筛选
+			filter1: 'today',
+			filter1Value: 0,
+			filter1Label: '今日',
+			filterList1: [
+				{ label: '今日', value: 0, parmas: 'today' },
+				{ label: '昨日', value: 1, parmas: 'yesterday' },
+				{ label: '本周', value: 2, parmas: 'week' },
+				{ label: '本月', value: 3, parmas: 'month' },
+				{ label: '自定义', value: 4, parmas: 'custom' }
+			],
+			filter2: 'all',
+			filter2Value: 0,
+			filter2Label: '全部',
+			filterList2: [
+				{ label: '全部', value: 0, parmas: 'all' },
+				{ label: '待发货', value: 1, parmas: 'nosend' },
+				{ label: '待完成', value: 2, parmas: 'noget' },
+				{ label: '已完成', value: 3, parmas: 'finish' }
+			],
+			custom: [], //自定义日期
+
+			storeOrderList: [], //订单商品列表
+			orderInfo: {}, //订单统计信息
+			storeDetail: {}, //门店信息
+			cancelType: '', //核销分类
+			scanCodes: [], //扫码内容。
+			showInputModal: false, //输码核销
+			qrcode: '', //输码
+			showCalendar: false, //日期选择
+
+			loadStatus: 'loadmore', //loadmore-加载前的状态,loading-加载中的状态,nomore-没有更多的状态
+			currentPage: 1,
+			lastPage: 1
+		};
+	},
+	computed: {},
+	onLoad(options) {
+		options && options.storeId && uni.setStorageSync('storeId', options.storeId);
+		this.getStoreDetail();
+	},
+	onShow() {
+		this.currentPage = 1;
+		this.lastPage = 1;
+		this.storeOrderList = [];
+		this.getStoreOrder();
+	},
+	onPullDownRefresh() {
+		this.currentPage = 1;
+		this.lastPage = 1;
+		this.storeOrderList = [];
+		this.getStoreOrder();
+	},
+	onReachBottom() {
+		if (this.currentPage < this.lastPage) {
+			this.currentPage += 1;
+			this.getStoreOrder();
+		}
+	},
+	methods: {
+		jump(path, query) {
+			this.$Router.push({
+				path: path,
+				query: query
+			});
+		},
+
+		/**
+		 * 累加某字段值
+		 * @param {Array} list  - 源数据
+		 * @param {String} key - 键名
+		 */
+		reduceAdd(list, key) {
+			return list.reduce((pre, cur) => (pre += Number(cur[key])), 0);
+		},
+
+		// 筛选
+		changeFilter1(e) {
+			if (e === 4) {
+				this.showCalendar = true;
+			} else {
+				this.filter1 = this.filterList1[e].parmas;
+				this.filter1Label = this.filterList1[e].label;
+				this.onFilter();
+			}
+		},
+		changeFilter2(e) {
+			this.filter2 = this.filterList2[e].parmas;
+			this.filter2Label = this.filterList2[e].label;
+			this.onFilter();
+		},
+
+		// 选择门店
+		goStoreList() {
+			this.$Router.replace({
+				path: '/pages/app/merchant/list'
+			});
+		},
+
+		// 获取门店信息
+		getStoreDetail() {
+			let that = this;
+			that.$http('store.info', {
+				store_id: uni.getStorageSync('storeId')
+			}).then(res => {
+				if (res.code === 1) {
+					that.storeDetail = res.data;
+				} else {
+					uni.removeStorageSync('storeId');
+					that.showModal = true;
+				}
+			});
+		},
+
+		// 扫码
+		async scanCode() {
+			let platform = this.$platform.get();
+			let authState = await new Auth('camera').check();
+			// #ifdef H5
+			if (platform === 'H5') {
+				this.$u.toast('普通浏览器不支持扫码功能,请使用小程序或微信内浏览器');
+			} else {
+				this.$wxsdk.scanQRCode(res => {
+					this.scanCodes = res.resultStr.split(',');
+					this.postOrderConfirm();
+				});
+			}
+			// #endif
+
+			// #ifndef H5
+			authState &&
+				uni.scanCode({
+					success: res => {
+						this.scanCodes = res.result.split(',');
+						this.postOrderConfirm();
+					},
+					fail: err => {
+						console.log(err);
+					}
+				});
+			// #endif
+		},
+
+		// 输码
+		onConfirm() {
+			this.showInputModal = false;
+			this.scanCodes = [];
+			this.scanCodes.push(this.qrcode);
+			this.postOrderConfirm();
+		},
+		// 核销
+		postOrderConfirm() {
+			let that = this;
+			that.$http(
+				'store.orderConfirm',
+				{
+					codes: that.scanCodes,
+					store_id: uni.getStorageSync('storeId')
+				},
+				'核销中...'
+			).then(res => {
+				uni.vibrateLong({
+					success: () => {
+						that.$u.toast(res.msg);
+					}
+				});
+				if (res.code === 1) {
+					that.$u.toast(res.msg);
+					that.scanCodes = [];
+					that.storeOrderList = [];
+					that.qrcode = '';
+					that.getStoreOrder();
+				}
+			});
+		},
+
+		// 门店订单列表
+		getStoreOrder() {
+			let that = this;
+			that.loadStatus = 'loading';
+			that.$http(
+				'store.order',
+				{
+					date_type: that.filter1,
+					date: that.custom,
+					type: that.filter2,
+					page: that.currentPage,
+					store_id: uni.getStorageSync('storeId')
+				},
+				'加载中...'
+			).then(res => {
+				uni.stopPullDownRefresh();
+				if (res.code == 1) {
+					that.storeOrderList = [...that.storeOrderList, ...res.data.result.data];
+					that.isEmpty = !that.storeOrderList.length;
+					that.orderInfo = res.data;
+					that.lastPage = res.data.result.last_page;
+					that.loadStatus = that.currentPage < res.data.result.last_page ? 'loadmore' : 'nomore';
+				}
+			});
+		},
+
+		// 选择日期
+		selDate(e) {
+			this.custom = [];
+			this.custom.push(e.startDate);
+			this.custom.push(e.endDate);
+			this.isShowDropDown = false;
+			this.filter1Label = `${e.startDate.replace(/-/g, ':')}-${e.endDate.replace(/-/g, ':')}`;
+			this.onFilter();
+		},
+
+		// 选择筛选
+		onFilter(val, title) {
+			this.storeOrderList = [];
+			this.currentPage = 1;
+			this.getStoreOrder();
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+// 遮罩
+.mask {
+	width: 100%;
+	height: 100%;
+	position: fixed;
+	z-index: 20;
+}
+// 商户信息
+.shopinfo-box {
+	background: url($IMG_URL+'/imgs/user/shop_headbg.png') no-repeat;
+	background-size: 100% 100%;
+	height: 320rpx;
+	.user-head {
+		padding-top: 10rpx;
+		.shop-info {
+			padding-left: 30rpx;
+			.shop-title {
+				font-size: 34rpx;
+				font-weight: bold;
+				color: rgba(255, 255, 255, 1);
+			}
+			.icon-xiala {
+				font-size: 34rpx;
+				color: rgba(255, 255, 255, 1);
+			}
+			.shop-address {
+				font-size: 28rpx;
+				font-weight: 500;
+				color: rgba(255, 255, 255, 1);
+				width: 540rpx;
+			}
+		}
+		.merchant-btn {
+			padding: 0;
+			width: 136rpx;
+			line-height: 46rpx;
+			background: rgba(255, 255, 255, 1);
+			border-radius: 23rpx 0px 0px 23rpx;
+			font-size: 20rpx;
+
+			font-weight: 500;
+			color: #3eb49c;
+		}
+	}
+}
+// 卡片
+.info-card-box {
+	background-color: #fff;
+	padding: 30rpx 20rpx;
+	.info-card {
+		position: relative;
+		width: 350rpx;
+		height: 165rpx;
+		border-radius: 10rpx;
+		overflow: hidden;
+		.card-bg {
+			width: 100%;
+			height: 100%;
+		}
+		.card-content {
+			position: absolute;
+			width: 100%;
+			height: 100%;
+			z-index: 3;
+			top: 0;
+			left: 0;
+			padding: 30rpx 0 0 30rpx;
+			.card-title {
+				font-size: 28rpx;
+
+				font-weight: bold;
+				color: rgba(255, 255, 255, 1);
+			}
+			.card-detail-box {
+				padding: 10rpx 20rpx;
+				height: 45rpx;
+				background: rgba(255, 255, 255, 1);
+				width: 150rpx;
+				border-radius: 23rpx;
+				margin-top: 20rpx;
+				.card-detail {
+					font-size: 22rpx;
+
+					font-weight: 500;
+				}
+				.icon-color1 {
+					color: #00b6ce;
+					font-size: 24rpx;
+				}
+				.icon-color2 {
+					color: #0f98f9;
+					font-size: 24rpx;
+				}
+			}
+		}
+	}
+}
+// 核销nav
+.cancel-nav {
+	background: #fff;
+	margin: 10rpx 0 0;
+	position: relative;
+	z-index: 22;
+}
+
+// 销量
+.sales-volume-box {
+	background-color: #fff;
+	.sales-volume {
+		width: 338rpx;
+		height: 72rpx;
+		background: rgba(76, 184, 157, 0.06);
+		border: 1rpx solid rgba(185, 227, 217, 1);
+		border-radius: 10rpx;
+		font-size: 24rpx;
+		font-weight: 400;
+		color: rgba(76, 184, 157, 1);
+	}
+}
+// 输码弹窗
+.modal-box {
+	background: #fff;
+	width: 610rpx;
+	margin: 0 auto;
+	border-radius: 20rpx;
+	.modal-head {
+		width: 100%;
+		.modal-head-img {
+			width: 100%;
+			height: 213rpx;
+		}
+		.modal-head-title {
+			font-size: 35rpx;
+
+			font-weight: bold;
+			color: #343434;
+			line-height: 42rpx;
+		}
+	}
+	.inp {
+		width: 501rpx;
+		height: 78rpx;
+		border: 1rpx solid #e5e5e5;
+		margin: 60rpx auto 40rpx;
+		font-size: 28rpx;
+
+		font-weight: 400;
+		color: #6d5028;
+		padding-left: 20rpx;
+		.pl-inp {
+			color: #9a9a9a;
+		}
+	}
+	.post-btn {
+		width: 492rpx;
+		line-height: 70rpx;
+		background: linear-gradient(90deg, #2eae9c, #6cc29f);
+		box-shadow: 0px 7rpx 6rpx 0px rgba(#6cc29f, 0.22);
+		border-radius: 35rpx;
+		font-size: 28rpx;
+
+		font-weight: 500;
+		color: rgba(255, 255, 255, 1);
+		padding: 0;
+		margin-bottom: 60rpx;
+	}
+}
+
+// 订单列表
+.order-list {
+	background: #fff;
+	margin: 20rpx 0;
+	padding: 0 20rpx;
+	.order-bottom {
+		justify-content: flex-end;
+		height: 80rpx;
+		padding: 0 20rpx;
+		.total-price-title {
+			color: #999999;
+			font-size: 24rpx;
+		}
+		.total-price {
+			color: #333;
+			font-size: 26rpx;
+			&::before {
+				content: '¥';
+				font-size: 20rpx;
+			}
+		}
+	}
+	.order-head {
+		padding: 0 25rpx;
+		height: 77rpx;
+		border-bottom: 1rpx solid #dfdfdf;
+
+		.no {
+			font-size: 26rpx;
+			color: #999;
+		}
+
+		.state {
+			font-size: 26rpx;
+			color: #a8700d;
+		}
+	}
+	.goods-order {
+		border-bottom: 1px solid rgba(223, 223, 223, 0.5);
+		padding: 20rpx;
+		margin-bottom: 20rpx;
+		.order-sku {
+			font-size: 24rpx;
+
+			font-weight: 400;
+			color: rgba(153, 153, 153, 1);
+			width: 450rpx;
+			margin-bottom: 20rpx;
+			.order-num {
+				margin-right: 10rpx;
+			}
+		}
+		.order-price-box {
+			.status-btn {
+				height: 32rpx;
+				border: 1rpx solid rgba(207, 169, 114, 1);
+				border-radius: 15rpx;
+				font-size: 20rpx;
+				font-weight: 400;
+				color: rgba(168, 112, 13, 1);
+				padding: 0 10rpx;
+				margin-left: 20rpx;
+				background: rgba(233, 183, 102, 0.16);
+			}
+			.order-price {
+				font-size: 26rpx;
+
+				font-weight: 600;
+				color: rgba(51, 51, 51, 1);
+			}
+		}
+	}
+}
+</style>

+ 115 - 0
pages/app/merchant/info.vue

@@ -0,0 +1,115 @@
+<!-- 商家信息 -->
+<template>
+	<view class="merchant-info-box">
+		<label class="u-flex u-col-center form-item">
+			<text class="form-tilte">门店名称:</text>
+			<view class="form-content">{{ storeDetail.name }}</view>
+		</label>
+		<label class="u-flex u-col-center form-item">
+			<text class="form-tilte">联系电话:</text>
+			<view class="form-content">{{ storeDetail.phone }}</view>
+		</label>
+		<!-- 营业时间 -->
+		<label class="u-flex u-col-center form-item">
+			<text class="form-tilte">营业时间:</text>
+			<view class="form-content">{{ storeDetail.openhours }}</view>
+		</label>
+		<!-- 选择星期 -->
+		<view class="u-flex u-col-top form-item u-m-b-20">
+			<text class="form-tilte">营业星期:</text>
+			<view class="form-content u-flex u-flex-wrap u-row-left">
+				<button class="cu-btn sm u-m-r-10 u-m-b-10 u-m-l-0" :class="!storeWeek.includes(week.value)?'line-green':'bg-green'" v-for="week in weekList" :key="week.title">
+					{{ week.title }}
+				</button>
+			</view>
+		</view>
+
+		<!-- 选择省市 -->
+		<label class="u-flex u-col-top form-item ">
+			<text class="form-tilte">门店地址:</text>
+			<view class="form-content">{{ storeDetail.province_name }}{{ storeDetail.city_name }}{{ storeDetail.area_name }}{{ storeDetail.address }}</view>
+		</label>
+	</view>
+</template>
+
+<script>
+export default {
+	components: {},
+	data() {
+		return {
+			storeWeek: [],
+			storeDetail: {}, //门店信息
+			weekList: [
+				//星期
+				{
+					title: '周一',
+					value: '1'
+				},
+				{
+					title: '周二',
+					value: '2'
+				},
+				{
+					title: '周三',
+					value: '3'
+				},
+				{
+					title: '周四',
+					value: '4'
+				},
+				{
+					title: '周五',
+					value: '5'
+				},
+				{
+					title: '周六',
+					value: '6'
+				},
+				{
+					title: '周日',
+					value: '7'
+				}
+			]
+		};
+	},
+	computed: {},
+	onLoad() {
+		this.getStoreDetail();
+	},
+	methods: {
+		getStoreDetail() {
+			let that = this;
+			that.$http('store.info', {
+				store_id: uni.getStorageSync('storeId')
+			}).then(res => {
+				if (res.code === 1) {
+					that.storeDetail = res.data;
+					that.storeWeek = res.data.openweeks.split(',');
+				}
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+.merchant-info-box {
+	border-top: 1rpx solid #f5f5f5;
+	.form-item {
+		background-color: #fff;
+		border-bottom: 1rpx solid #f5f5f5;
+		padding: 20rpx 30rpx;
+		.form-title {
+			font-size: 28rpx;
+			font-weight: 400;
+			color: rgba(51, 51, 51, 1);
+		}
+		.form-content {
+			font-size: 28rpx;
+			font-weight: 500;
+			color: rgba(51, 51, 51, 1);
+			flex: 1;
+		}
+	}
+}
+</style>

+ 116 - 0
pages/app/merchant/list.vue

@@ -0,0 +1,116 @@
+<!-- 门店列表 -->
+<template>
+	<view class="page_box store-list-wrap">
+		<view class="head_box"></view>
+		<view class="content_box">
+			<label v-for="store in storeList" :key="store.id" @tap="selStore(store.id)">
+				<view class="store-item u-flex u-row-between">
+					<view class="u-flex u-col-center">
+						<view class="img-box"><image class="store-img" :src="store.image_first" mode="aspectFill" lazy-load></image></view>
+						<view class="item-left u-flex-col u-row-left">
+							<text class="store-title">{{ store.name }}</text>
+							<text class="store-content">{{ store.city_name }}{{ store.area_name }}{{ store.address }}</text>
+						</view>
+					</view>
+
+					<u-checkbox :value="storeId == store.id" activeColor="#4CB89D" shape="circle"></u-checkbox>
+				</view>
+			</label>
+		</view>
+		<view class="foot_box u-flex u-row-between u-col-center"><button class="u-reset-button save-btn" @tap="saveStore">确认</button></view>
+	</view>
+</template>
+
+<script>
+export default {
+	components: {},
+	data() {
+		return {
+			storeList: [],
+			storeId: uni.getStorageSync('storeId')
+		};
+	},
+	computed: {},
+	onLoad() {
+		this.getStoreAddress();
+	},
+	methods: {
+		// 选择门店
+		selStore(id) {
+			this.storeId = id;
+		},
+		// 确认门店
+		saveStore() {
+			uni.setStorageSync('storeId', this.storeId);
+			if (!this.storeId) {
+				this.$u.toast('请选选择门店');
+			} else {
+				this.$Router.replace({
+					path: '/pages/app/merchant/index'
+				});
+			}
+		},
+		// 获取门店列表
+		getStoreAddress() {
+			let that = this;
+			that.$http('store.list').then(res => {
+				if (res.code == 1) {
+					that.storeList = res.data;
+				}
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+.store-item {
+	padding: 0 30rpx;
+	height: 180rpx;
+	background-color: #fff;
+	border-bottom: 1rpx solid rgba(223, 223, 223, 0.6);
+	width: 100%;
+	.img-box {
+		width: 100rpx;
+		height: 100rpx;
+		border-radius: 6rpx;
+		overflow: hidden;
+		margin-right: 20rpx;
+		.store-img {
+			width: 100rpx;
+			height: 100rpx;
+			border-radius: 6rpx;
+		}
+	}
+	.store-title {
+		font-size: 30rpx;
+
+		font-weight: 600;
+		color: rgba(52, 52, 52, 1);
+		margin-bottom: 10rpx;
+	}
+	.store-content {
+		font-size: 24rpx;
+
+		font-weight: 400;
+		color: rgba(102, 102, 102, 1);
+	}
+}
+.foot_box {
+	height: 100rpx;
+	background: rgba(255, 255, 255, 1);
+	padding: 0 20rpx;
+	.save-btn {
+		width: 710rpx;
+		line-height: 80rpx;
+		background: linear-gradient(90deg, #2eae9c, #6cc29f);
+		border: 1rpx solid rgba(238, 238, 238, 1);
+		font-size: 30rpx;
+
+		font-weight: 500;
+		color: rgba(255, 255, 255, 1);
+		padding: 0;
+		border-radius: 40rpx;
+	}
+}
+</style>

+ 105 - 0
pages/app/score/list.vue

@@ -0,0 +1,105 @@
+<!-- 积分商品列表 -->
+<template>
+	<view class="score-wrap">
+		<view class="goods-wrap u-p-20 u-flex u-flex-wrap">
+			<view class="goods-item u-m-b-20" v-for="leftGoods in scoreList" :key="leftGoods.id">
+				<shopro-goods-card
+					:image="leftGoods.image"
+					:title="leftGoods.title"
+					@click="$Router.push({ path: '/pages/goods/detail', query: { id: leftGoods.id, type: 'score' } })"
+				>
+					<template #cardBottom>
+						<view class="price-box u-flex u-row-between u-flex-wrap">
+							<view class="beans-box u-flex u-m-10">
+								<image class="bean-img u-m-r-10" :src="$IMG_URL + '/imgs/score/score.png'" mode=""></image>
+								{{ leftGoods.price }}
+							</view>
+							<view class="sales-box u-m-10">已兑换{{ leftGoods.sales }}件</view>
+						</view>
+					</template>
+				</shopro-goods-card>
+			</view>
+		</view>
+		<!-- 缺省页 -->
+		<shopro-empty
+			v-if="isEmpty"
+			@click="$Router.pushTab('/pages/index/index')"
+			:image="$IMG_URL + '/imgs/empty/empty_goods.png'"
+			tipText="暂无积分商品"
+			btnText="去首页逛逛"
+		></shopro-empty>
+		<!-- 加载更多 -->
+		<u-loadmore v-if="!isEmpty" height="80rpx" :status="loadStatus" icon-type="flower" color="#ccc" />
+	</view>
+</template>
+
+<script>
+export default {
+	components: {},
+	data() {
+		return {
+			scoreList: [],
+			isEmpty: false, //无数据
+			loadStatus: 'loadmore', //loadmore-加载前的状态,loading-加载中的状态,nomore-没有更多的状态
+			currentPage: 1,
+			lastPage: 1
+		};
+	},
+	onLoad() {
+		this.getScoreShopsList();
+		// 触底监听
+		uni.$on('uOnReachBottom', () => {
+			if (this.currentPage < this.lastPage) {
+				this.currentPage += 1;
+				this.getScoreShopsList();
+			}
+		});
+	},
+	computed: {},
+	methods: {
+		//积分商品列表
+		getScoreShopsList() {
+			let that = this;
+			that.loadStatus = 'loading';
+			that.$http('goods.scoreList', {
+				page: that.currentPage
+			}).then(res => {
+				if (res.code == 1) {
+					that.scoreList = [...that.scoreList, ...res.data.data];
+					that.isEmpty = !that.scoreList.length;
+					that.lastPage = res.data.last_page;
+					that.loadStatus = that.currentPage < res.data.last_page ? 'loadmore' : 'nomore';
+				}
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+.goods-item {
+	margin-right: 20rpx;
+	margin-bottom: 20rpx;
+
+	&:nth-child(2n) {
+		margin-right: 0;
+	}
+	.price-box {
+		.beans-box {
+			font-size: 32upx;
+			font-weight: bold;
+			color: rgba(228, 141, 4, 1);
+
+			.bean-img {
+				width: 36upx;
+				height: 36upx;
+			}
+		}
+		.sales-box {
+			font-size: 22rpx;
+			font-weight: 500;
+			color: rgba(153, 153, 153, 1);
+		}
+	}
+}
+</style>

+ 208 - 0
pages/goods/comment/add-comment.vue

@@ -0,0 +1,208 @@
+<!-- 添加评论 -->
+<template>
+	<view class="commont-from-wrap">
+		<!-- 评价商品 -->
+		<view class="goods-card">
+			<shopro-mini-card :title="goodsDetail.goods_title" :image="goodsDetail.goods_image">
+				<template #describe>
+					<view class="order-sku u-ellipsis-1">
+						<text class="order-num">数量:{{ goodsDetail.goods_num || 0 }};</text>
+						{{ goodsDetail.goods_sku_text ? goodsDetail.goods_sku_text : '' }}
+					</view>
+				</template>
+				<template #cardBottom>
+					<view class="order-price-box u-flex ">
+						<text class="order-price font-OPPOSANS">¥{{ goodsDetail.goods_price || 0 }}</text>
+						<button class="u-reset-button status-btn" v-if="goodsDetail.status_name">{{ goodsDetail.status_name }}</button>
+					</view>
+				</template>
+			</shopro-mini-card>
+		</view>
+
+		<view class="form-item">
+			<view class="star-box u-flex">
+				<view class="star-title u-m-r-40">{{ starTip }}</view>
+				<u-rate v-model="star" minCount="1"></u-rate>
+			</view>
+			<view class="area-box">
+				<textarea class="inp-area" v-model="message" placeholder="宝贝满足你的期待吗?说说你的使用心得,分享给想买的他们吧~" placeholder-class="pl-style" />
+				<view class="img-box">
+					<u-upload
+						:showProgress="false"
+						@on-uploaded="uploadSuccess"
+						@on-remove="uploadRemove"
+						:action="`${$API_URL}index/upload`"
+						width="138"
+						height="138"
+						maxCount="9"
+					></u-upload>
+				</view>
+			</view>
+		</view>
+		<view class="foot_box u-flex u-row-center u-col-center u-m-t-60"><button class="u-reset-button post-btn" @tap="subComment">发布</button></view>
+	</view>
+</template>
+
+<script>
+export default {
+	components: {},
+	data() {
+		return {
+			imgList: [],
+			star: 5,
+			message: '',
+			goodsDetail: {}
+		};
+	},
+	computed: {
+		starTip() {
+			let text = '';
+			if (this.star <= 1) {
+				text = '差评';
+			}
+			if (this.star > 1 && this.star <= 3) {
+				text = '中评';
+			}
+			if (this.star >= 4) {
+				text = '好评';
+			}
+			return text;
+		}
+	},
+	onLoad() {
+		this.getOrderItemDetail();
+	},
+	methods: {
+		// 订单详情
+		getOrderItemDetail() {
+			let that = this;
+			that.$http('order.itemDetail', {
+				id: that.$Route.query.orderId,
+				order_item_id: that.$Route.query.orderItemId
+			}).then(res => {
+				if (res.code === 1) {
+					that.goodsDetail = res.data;
+				}
+			});
+		},
+
+		// 上传图片成功
+		uploadSuccess(e) {
+			this.imgList = [];
+			e.forEach(item => {
+				this.imgList.push(item.response.data.url);
+			});
+		},
+
+		// 移除图片
+		uploadRemove(index) {
+			this.imgList.splice(index, 1);
+		},
+
+		subComment() {
+			let that = this;
+			that.$http('order.comment', {
+				id: that.$Route.query.orderId,
+				order_item_id: that.goodsDetail.id,
+				level: that.star,
+				content: that.message,
+				images: that.imgList
+			}, '提交中...').then(res => {
+				if (res.code === 1) {
+					that.$u.toast('评论发表成功');
+					that.$Router.back();
+				}
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+// 评价商品
+.goods-card {
+	margin: 10rpx 0;
+	padding: 20rpx;
+	background: #fff;
+	.order-sku {
+		font-size: 24rpx;
+		
+		font-weight: 400;
+		color: rgba(153, 153, 153, 1);
+		width: 450rpx;
+		margin-bottom: 20rpx;
+		.order-num {
+			margin-right: 10rpx;
+		}
+	}
+	.order-price-box {
+		.status-btn {
+			height: 32rpx;
+			border: 1rpx solid rgba(207, 169, 114, 1);
+			border-radius: 15rpx;
+			font-size: 20rpx;
+			font-weight: 400;
+			color: rgba(168, 112, 13, 1);
+			padding: 0 10rpx;
+			margin-left: 20rpx;
+			background: rgba(233, 183, 102, 0.16);
+		}
+		.order-price {
+			font-size: 26rpx;
+			
+			font-weight: 600;
+			color: rgba(51, 51, 51, 1);
+		}
+	}
+}
+
+// 评论,选择图片
+.form-item {
+	background: #ffff;
+	padding-bottom: 30rpx;
+	.star-box {
+		height: 100rpx;
+		padding: 0 25rpx;
+	}
+	.star-title {
+		font-weight: 600;
+	}
+}
+.area-box {
+	width: 690rpx;
+	min-height: 306rpx;
+	background: rgba(249, 250, 251, 1);
+	border-radius: 20rpx;
+	padding: 28rpx;
+	margin: auto;
+	.pl-style {
+		font-size: 26rpx;
+		
+		font-weight: 400;
+		color: rgba(153, 153, 153, 1);
+		line-height: 42rpx;
+	}
+	.inp-area {
+		font-size: 26rpx;
+		
+		font-weight: 500;
+		color: #333;
+		line-height: 50rpx;
+		width: 100%;
+	}
+	.img-box {
+		display: flex;
+		align-items: center;
+		flex-wrap: wrap;
+		margin-top: 20rpx;
+	}
+}
+.post-btn {
+	width: 690rpx;
+	line-height: 80rpx;
+	background: linear-gradient(90deg, rgba(240, 199, 133, 1), rgba(246, 214, 157, 1));
+	border-radius: 40rpx;
+	color: rgba(#fff, 0.9);
+	margin-bottom: 30rpx;
+}
+</style>

+ 126 - 0
pages/goods/comment/comment-list.vue

@@ -0,0 +1,126 @@
+<!-- 评论列表 -->
+<template>
+	<view class="page_box">
+		<view class="head_box u-flex">
+			<button :class="{ 'btn-active': typeCurrent === t.code }" class="u-reset-button type-btn" @tap="selType(t.code)" v-for="t in commentTypeList" :key="t.code">
+				{{ t.name }}({{ t.num }})
+			</button>
+		</view>
+		<view class="content_box">
+			<scroll-view scroll-y="true" enable-back-to-top @scrolltolower="loadMore" class="scroll-box">
+				<view class="comment-list">
+					<block v-for="comment in commentList" :key="comment.id"><sh-comment :comment="comment"></sh-comment></block>
+				</view>
+				<shopro-empty v-if="isEmpty" :image="$IMG_URL + '/imgs/empty/comment_empty.png'" tipText="暂无此类评价~"></shopro-empty>
+				<!-- 加载更多 -->
+				<u-loadmore v-if="commentList.length" height="80rpx" :status="loadStatus" icon-type="flower" color="#ccc" />
+			</scroll-view>
+		</view>
+	</view>
+</template>
+
+<script>
+import shComment from '../components/sh-comment.vue';
+export default {
+	components: {
+		shComment
+	},
+	data() {
+		return {
+			isEmpty: false,
+			typeCurrent: 'all',
+			commentList: [],
+			commentTypeList: [],
+			loadStatus: 'loadmore', //loadmore-加载前的状态,loading-加载中的状态,nomore-没有更多的状态
+			currentPage: 1,
+			lastPage: 1
+		};
+	},
+	computed: {},
+	onLoad() {
+		this.getCommentType();
+	},
+	methods: {
+		selType(id) {
+			if (this.typeCurrent !== id) {
+				this.typeCurrent = id;
+				this.commentList = [];
+				this.currentPage = 1;
+				this.getCommentList();
+			}
+		},
+		// 评价类型
+		getCommentType() {
+			let that = this;
+			that.$http('goods.commentType', {
+				goods_id: that.$Route.query.goodsId
+			}).then(res => {
+				if (res.code === 1) {
+					that.commentTypeList = res.data;
+					that.getCommentList();
+				}
+			});
+		},
+		// 商品评论
+		getCommentList() {
+			let that = this;
+			that.loadStatus = 'loading';
+			that.$http('goods.commentList', {
+				goods_id: that.$Route.query.goodsId,
+				pre_page: 10,
+				page: that.currentPage,
+				type: that.typeCurrent
+			}, '加载中...').then(res => {
+				if (res.code === 1) {
+					that.commentList = [...that.commentList, ...res.data.data];
+					that.isEmpty = !that.commentList.length;
+					that.lastPage = res.data.last_page;
+					that.loadStatus = that.currentPage < res.data.last_page ? 'loadmore' : 'nomore';
+				}
+			});
+		},
+		// 加载更多
+		loadMore() {
+			if (this.currentPage < this.lastPage) {
+				this.currentPage += 1;
+				this.getCommentList();
+			}
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+.empty-box {
+	position: fixed;
+	left: 50%;
+	top: 50%;
+	transform: translate(-50%, -50%);
+}
+.head_box {
+	height: 114rpx;
+	background-color: #fff;
+	padding: 0 20rpx;
+	.type-btn {
+		width: 130rpx;
+		line-height: 62rpx;
+		background: rgba(238, 238, 238, 1);
+		border-radius: 31rpx;
+		border: none;
+		font-size: 28rpx;
+
+		font-weight: 400;
+		color: rgba(153, 153, 153, 1);
+		padding: 0;
+		margin-right: 10rpx;
+	}
+	.btn-active {
+		background: linear-gradient(90deg, rgba(233, 180, 97, 1), rgba(238, 204, 137, 1));
+		box-shadow: 0px 7rpx 6rpx 0px rgba(229, 138, 0, 0.22);
+		color: #a8700d;
+	}
+}
+.comment-list {
+	margin-top: 20rpx;
+}
+</style>

+ 157 - 0
pages/goods/components/sh-activity.vue

@@ -0,0 +1,157 @@
+<template>
+	<view class="sh-activity-box">
+		<view class="u-flex activity-box u-col-top">
+			<view class="title">优惠</view>
+			<view class="activity-content u-flex-1">
+				<view class="tip-list u-flex u-flex-1 u-col-center" v-for="item in detail" :key="item.id" @tap="showActivity(item.type)">
+					<view class="u-flex u-flex-1 u-col-center">
+						<view class="title-tag cu-tag bg-red sm radius u-m-r-10">{{ item.title }}</view>
+						<view class="tag-box cu-tag line-red sm radius u-m-r-10" v-if="index < 3" v-for="(tag, index) in item.tags" :key="tag">{{ tag }}</view>
+					</view>
+					<view class="u-iconfont uicon-arrow-right" style="color: #bfbfbf;font-size: 28rpx;"></view>
+				</view>
+			</view>
+		</view>
+		<!-- 活动弹窗 -->
+		<u-popup v-if="showModal" v-model="showModal" @close="onClose" mode="bottom" border-radius="30" :closeable="true" close-icon-pos="top-right">
+			<view class="activity-modal-box page_box">
+				<view class="modal-head u-flex u-row-center u-col-center">
+					<text class="head-title">{{ activityMap[activityType].title }}</text>
+				</view>
+				<view class="modal-content content_box">
+					<view class="tip-list u-flex u-flex-1 u-col-center" @tap="toSelGoods">
+						<view class="u-flex u-flex-1 modal-item u-row-between u-col-center">
+							<view class="title-tag cu-tag bg-red sm radius u-m-r-10">{{ activityMap[activityType].title }}</view>
+							<view class="u-flex u-col-center">
+								<view class="tag-box cu-tag line-red sm radius u-m-r-10" v-for="(tag, index) in activityMap[activityType].tags" :key="tag">{{ tag }}</view>
+							</view>
+						</view>
+						<view class="u-iconfont uicon-arrow-right" style="color: #bfbfbf;font-size: 28rpx;"></view>
+					</view>
+
+					<view class="all-goods-num">共有{{ activityMap[activityType].num }}件商品参加此活动</view>
+				</view>
+				<view class="modal-foot u-flex u-row-center u-col-center"><button class=" u-reset-button serve-btn" @tap="toSelGoods">去凑单</button></view>
+			</view>
+		</u-popup>
+	</view>
+</template>
+
+<script>
+/**
+ * 参与优惠活动卡片
+ * @property {Array} detail - 优惠活动详情列表
+ * @property {Boolean} showModal- 显隐
+ */
+export default {
+	components: {},
+	data() {
+		return {
+			showModal: false,
+			activityType: ''
+		};
+	},
+	props: {
+		detail: {
+			type: Array,
+			default: () => []
+		}
+	},
+	created() {},
+	computed: {
+		activityMap() {
+			let obj = {};
+			this.detail.forEach(item => {
+				obj[item.type] = item;
+				obj[item.type].num = item.goods_ids.split(',').length;
+			});
+			return obj;
+		}
+	},
+	methods: {
+		// 显示
+		showActivity(type) {
+			this.activityType = type;
+			this.showModal = true;
+		},
+		// 关闭
+		onClose() {
+			this.activityType = '';
+			this.showModal = false;
+		},
+		// 去凑单
+		toSelGoods() {
+			this.$Router.push({
+				path: '/pages/activity/discounts/list',
+				query: this.activityMap[this.activityType]
+			});
+			this.activityType = '';
+			this.showModal = false;
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+.activity-box {
+	background: #fff;
+	padding: 30rpx 20rpx 0;
+	margin: 10rpx 0;
+
+	.title {
+		font-size: 28rpx;
+		color: #999;
+		margin-right: 20rpx;
+	}
+	.activity-content {
+		.tip-list {
+			font-size: 28rpx;
+			width: 100%;
+			padding-bottom: 30rpx;
+		}
+	}
+}
+
+// 弹窗
+.activity-modal-box {
+	width: 750rpx;
+	min-height: 700rpx;
+	border-radius: 30rpx 30rpx 0 0;
+	background: #fff;
+	padding: 30rpx;
+	.serve-btn {
+		width: 690rpx;
+		line-height: 80rpx;
+		background: linear-gradient(90deg, rgba(240, 199, 133, 1), rgba(246, 214, 157, 1));
+		border-radius: 40rpx;
+		font-size: 30rpx;
+		color: rgba(#fff, 0.9);
+		margin-top: 40rpx;
+	}
+
+	.modal-head {
+		margin-bottom: 30rpx;
+		position: relative;
+		.head-title {
+			font-size: 32rpx;
+			font-weight: bold;
+		}
+	}
+
+	.modal-content {
+		overflow-y: auto;
+		.tip-list {
+			font-size: 28rpx;
+			width: 100%;
+			padding: 20rpx 0;
+			border-bottom: 1rpx solid #eeeeee;
+		}
+		.all-goods-num {
+			font-size: 28rpx;
+			font-weight: 500;
+			color: #333333;
+			padding: 20rpx 0;
+		}
+	}
+}
+</style>

+ 100 - 0
pages/goods/components/sh-comment.vue

@@ -0,0 +1,100 @@
+<template>
+	<view class="comment-box">
+		<view class="head u-flex u-row-between">
+			<view class="u-flex">
+				<image class="avatar" :src="comment.user ? comment.user.avatar : $IMG_URL + '/imgs/base_avatar.png'" mode="aspectFill"></image>
+				<view class="user-name u-ellipsis-1">{{ comment.user ? comment.user.nickname : '***' }}</view>
+				<u-rate :value="comment.level" disabled></u-rate>
+			</view>
+			<text class="time">{{ $u.timeFormat(comment.createtime, 'yyyy-mm-dd hh:MM') }}</text>
+		</view>
+		<view class="detail">{{ comment.content }}</view>
+		<view class="img-box u-flex u-row-center u-col-center">
+			<view class="nav u-flex u-flex-wrap">
+				<image
+					v-for="(img, index) in comment.images"
+					:key="index"
+					@tap.stop="tools.previewImage(comment.images, index)"
+					class="comment-img"
+					:src="img"
+					mode="aspectFill"
+				></image>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+/**
+ * 评论信息卡片
+ * @property {Object} comment  - 评论信息
+ */
+export default {
+	components: {},
+	data() {
+		return {};
+	},
+	props: {
+		comment: {}
+	},
+	computed: {},
+	methods: {}
+};
+</script>
+
+<style lang="scss">
+.comment-box {
+	padding: 30rpx 20rpx;
+	border-bottom: 1rpx solid #eee;
+	background: #fff;
+
+	.head {
+		margin-bottom: 20rpx;
+
+		.avatar {
+			width: 52rpx;
+			height: 52rpx;
+			border-radius: 50%;
+			background: #ccc;
+		}
+
+		.user-name {
+			font-size: 26rpx;
+			color: #999;
+			width: 140rpx;
+			margin: 0 20rpx;
+		}
+
+		.time {
+			font-size: 24rpx;
+			color: #c4c4c4;
+		}
+	}
+
+	.detail {
+		font-size: 26rpx;
+		font-weight: 500;
+		color: rgba(102, 102, 102, 1);
+		line-height: 42rpx;
+	}
+
+	.img-box {
+		margin-top: 30rpx;
+		position: relative;
+		.nav {
+			width: 670rpx;
+		}
+		.comment-img {
+			width: 160rpx;
+			height: 160rpx;
+			background: #ccc;
+			margin-right: 10rpx;
+			margin-bottom: 10rpx;
+			border-radius: 6rpx;
+			&:nth-child(4n) {
+				margin-right: 0;
+			}
+		}
+	}
+}
+</style>

+ 75 - 0
pages/goods/components/sh-coupon.vue

@@ -0,0 +1,75 @@
+<template>
+	<view class="category-box u-m-b-10">
+		<swiper class="swiper-box" @change="onSwiper" circular :autoplay="false" :interval="3000" :duration="1000">
+			<swiper-item v-for="(c, index) in couponList" :key="c.id">
+				<view class="tab-list u-flex u-row-center u-col-center"><shopro-coupon :couponData="c" :state="0"></shopro-coupon></view>
+			</swiper-item>
+		</swiper>
+		<view class="category-dots">
+			<text :class="categoryCurrent === index ? 'category-dot-active' : 'category-dot'" v-for="(dot, index) in couponList.length" :key="index"></text>
+		</view>
+	</view>
+</template>
+
+<script>
+/**
+ * 	 详情优惠券轮播 - 详情专用
+ * @property {Object} couponList - 优惠劵列表
+ */
+export default {
+	components: {},
+	data() {
+		return {
+			categoryCurrent: 0 //分类轮播下标
+		};
+	},
+	props: {
+		couponList: {}
+	},
+	computed: {},
+	methods: {
+		// 轮播
+		onSwiper(e) {
+			this.categoryCurrent = e.detail.current;
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+.category-box {
+	padding-top: 20rpx;
+	background: #fff;
+	height: 230rpx;
+	position: relative;
+	.swiper-box {
+		height: 210rpx;
+		.tab-list {
+			/deep/.coupon-wrap {
+				width: 710rpx;
+			}
+		}
+	}
+	.category-dots {
+		display: flex;
+		position: absolute;
+		left: 50%;
+		transform: translateX(-50%);
+		bottom: 15rpx;
+
+		.category-dot {
+			width: 40rpx;
+			height: 3rpx;
+			background: #eeeeee;
+			margin-right: 10rpx;
+		}
+
+		.category-dot-active {
+			width: 40rpx;
+			height: 3rpx;
+			background: #a8700d;
+			margin-right: 10rpx;
+		}
+	}
+}
+</style>

+ 168 - 0
pages/goods/components/sh-filter.vue

@@ -0,0 +1,168 @@
+<template>
+	<view class="filter-box">
+		<view class="cu-modal" style="z-index: -1;" :class="{ show: showSel }" @tap="hideModal"></view>
+		<view class="navbar">
+			<view class="sel-box u-flex-col" v-show="showSel">
+				<view class="sel-item" @tap="onSel(0)" :class="{ 'sel-active': defaultOrder === 0 && filterIndex === 0 }">综合推荐</view>
+				<view class="sel-item" @tap="onSel(1)" :class="{ 'sel-active': defaultOrder === 1 && filterIndex === 0 }">扩展自定义排序</view>
+			</view>
+			<view class="nav-item u-flex-col" :class="{ current: filterIndex === 0 }" @tap="tabClick(0)">
+				<view class="title-box  u-flex u-col-center">
+					<text class="filter-title">{{ defaultOrder === 1 ? '自定义' : '综合推荐' }}</text>
+					<view class="p-box"><text style="font-size: 22rpx;" :class="{ active: filterIndex === 0 }" class="u-iconfont uicon-arrow-up u-m-l-6"></text></view>
+				</view>
+				<view class="line" :class="{ 'line-active': filterIndex === 0 }"></view>
+			</view>
+			<view class="nav-item u-flex-col" :class="{ current: filterIndex === 1 }" @tap="tabClick(1)">
+				<view class="title-box u-flex"><text class="filter-title">销量</text></view>
+				<view class="line" :class="{ 'line-active': filterIndex === 1 }"></view>
+			</view>
+			<view class="nav-item u-flex-col" :class="{ current: filterIndex === 2 }" @tap="tabClick(2)">
+				<view class="title-box u-flex">
+					<text class="filter-title">价格</text>
+					<view class="p-box u-flex-col u-m-l-6">
+						<view
+							class="u-iconfont uicon-arrow-up"
+							:style="{ fontSize: '20rpx', lineHeight: '18rpx', color: priceOrder === 1 && filterIndex === 2 ? '#d5a65a' : '#333' }"
+						></view>
+						<view
+							class="u-iconfont uicon-arrow-down"
+							:style="{ fontSize: '20rpx', lineHeight: '18rpx', color: priceOrder === 2 && filterIndex === 2 ? '#d5a65a' : '#333' }"
+						></view>
+					</view>
+				</view>
+				<view class="line" :class="{ 'line-active': filterIndex === 2 }"></view>
+			</view>
+			<view class="nav-item u-flex-col" :class="{ current: filterIndex === 3 }" @tap="tabClick(3)">
+				<view class="title-box"><text class="filter-title">新品优先</text></view>
+				<view class="line" :class="{ 'line-active': filterIndex === 3 }"></view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+/**
+ * 商品筛选组件
+ *
+ */
+import { mapState, mapActions, mapMutations } from 'vuex';
+export default {
+	components: {},
+	props: {},
+	data() {
+		return {
+			filterIndex: 0,
+			showSel: false, //综合选择
+			defaultOrder: 0, //综合
+			priceOrder: 0, //价格
+			salesOrder: 0, //销量
+			newProdcutOrder: 0 //新品优先
+		};
+	},
+	computed: {
+		filterData() {
+			const data = {
+				defaultOrder: this.defaultOrder,
+				priceOrder: this.priceOrder,
+				salesOrder: this.salesOrder,
+				newProdcutOrder: this.newProdcutOrder
+			};
+			return data;
+		}
+	},
+	onLoad() {},
+	methods: {
+		//筛选
+		tabClick(index) {
+			this.filterIndex = index;
+			index === 0 ? (this.showSel = !this.showSel) : (this.showSel = false);
+			index === 1 ? (this.salesOrder = 1) : (this.salesOrder = 0);
+			index === 2 ? (this.priceOrder = this.priceOrder === 1 ? 2 : 1) : (this.priceOrder = 0);
+			index === 3 ? (this.newProdcutOrder = 1) : (this.newProdcutOrder = 0);
+			this.$emit('change', this.filterData);
+		},
+		onSel(dot) {
+			this.defaultOrder = dot;
+			this.$emit('change', this.filterData);
+			this.showSel = false;
+		},
+		hideModal() {
+			this.showSel = false;
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+.sel-box {
+	position: absolute;
+	width: 750rpx;
+	height: 140rpx;
+	background: rgba(246, 246, 246, 1);
+	border-radius: 0px 0px 20rpx 20rpx;
+	z-index: 999;
+	left: 50%;
+	transform: translateX(-50%);
+	bottom: -138rpx;
+	transition: all ease-out 0.2s;
+	.sel-item {
+		color: #333;
+		padding: 20rpx 40rpx 0;
+		font-size: 24rpx;
+		font-weight: 500;
+	}
+	.sel-active {
+		color: #d5a65a;
+		font-size: 26rpx;
+		font-weight: 600;
+	}
+}
+.filter-box {
+	width: 750rpx;
+}
+.navbar {
+	display: flex;
+	width: 750rpx;
+	height: 95rpx;
+	background: #fff;
+	border-bottom: 1upx solid #e6e6e6;
+	position: relative;
+	z-index: 999;
+	.nav-item {
+		flex: 1;
+		display: flex;
+		justify-content: center;
+		align-items: center;
+		height: 100%;
+		font-size: 30rpx;
+		position: relative;
+		.filter-title {
+			font-size: 28rpx;
+			color: #333;
+			font-weight: 600;
+		}
+		.line {
+			width: 110rpx;
+			height: 4rpx;
+			background: transparent;
+			position: absolute;
+			bottom: 0;
+			z-index: 2;
+		}
+
+		.line-active {
+			background: rgba(213, 166, 90, 1);
+		}
+
+		.p-box {
+			display: flex;
+			flex-direction: column;
+		}
+	}
+}
+
+.current {
+	color: #d5a65a;
+}
+</style>

+ 220 - 0
pages/goods/components/sh-groupon.vue

@@ -0,0 +1,220 @@
+<template>
+	<view class="group-people">
+		<block v-if="grouponTeamList.length">
+			<view class="into-title u-flex u-row-between">
+				<text>已有{{ grouponData.sales }}人参与活动</text>
+				<view class="u-iconfont uicon-arrow-right" style="color: #bfbfbf;font-size: 28rpx;" @tap="onMoreGrouponTeam"></view>
+			</view>
+			<view class="into-item u-flex u-row-between" v-for="(g, index) in grouponTeamList" :key="g.id" v-if="index < 2">
+				<view class="u-flex">
+					<image class="into-img" :src="g.leader.user_avatar" mode="aspectFill"></image>
+					<text class="into-name">{{ g.leader.user_nickname }}</text>
+				</view>
+				<view class="u-flex">
+					<view class="">
+						<view class="num">
+							还差
+							<text class="num-color">{{ g.num - g.current_num }}人</text>
+							成团
+						</view>
+						<view class="time" v-if="g.expiretime && g.time">剩余时间{{ g.time.h }}:{{ g.time.m }}:{{ g.time.s }}</view>
+					</view>
+					<button class="u-reset-button  join-btn" @tap="jump('/pages/activity/groupon/detail', { id: g.id })">去参团</button>
+				</view>
+			</view>
+		</block>
+		<!-- 弹窗 -->
+		<u-popup v-model="showModal" mode="bottom" :closeable="true" close-icon-pos="top-right">
+			<view class="modal-box page_box">
+				<view class="modal-head u-flex u-row-center">
+					<text class="head-title">正在拼团</text>
+				</view>
+				<view class="modal-content content_box y-f">
+					<view class="into-item u-flex u-row-between" v-for="g in grouponTeamList" :key="g.id">
+						<view class="u-flex">
+							<image class="into-img" :src="g.leader.user_avatar" mode="aspectFill"></image>
+							<text class="into-name">{{ g.leader.user_nickname }}</text>
+						</view>
+						<view class="u-flex">
+							<view class="y-end">
+								<view class="num">
+									还差
+									<text class="num-color">{{ g.num - g.current_num }}人</text>
+									成团
+								</view>
+								<view class="time" v-if="g.expiretime && g.time">剩余时间{{ g.time.h }}:{{ g.time.m }}:{{ g.time.s }}</view>
+							</view>
+							<button class="u-reset-button join-btn" @tap="joinTeam('/pages/activity/groupon/detail', { id: g.id })">去参团</button>
+						</view>
+					</view>
+				</view>
+				<view class="modal-foot u-flex u-row-center u-col-center">仅显示10个正在拼团的人</view>
+			</view>
+		</u-popup>
+	</view>
+</template>
+
+<script>
+/**
+ * 商品已拼团卡片
+ * @property {Object} grouponData - 商品已拼团信息
+ */
+let timer;
+export default {
+	components: {},
+	data() {
+		return {
+			grouponTeamList: [], //原数组
+			showModal: false
+		};
+	},
+	props: {
+		grouponData: {}
+	},
+	computed: {},
+	beforeDestroy() {
+		clearInterval(timer);
+		timer=null
+	},
+	created() {
+		this.getGrouponItem();
+		let that = this;
+
+		timer = setInterval(() => {
+			that.grouponTeamList.forEach((item, index) => {
+				let nowTime = new Date().getTime();
+				let endTime = item.expiretime * 1000;
+				let t = (endTime - nowTime) / 1000;
+				let time = that.$tools.format(t);
+				that.$set(that.grouponTeamList[index], 'time', time);
+			});
+		}, 1000);
+	},
+	methods: {
+		hideModal() {
+			this.showModal = false;
+		},
+		jump(path, parmas) {
+			this.$Router.push({
+				path: path,
+				query: parmas
+			});
+		},
+		joinTeam(path, parmas) {
+			this.showModal = false;
+			this.$Router.push({
+				path: path,
+				query: parmas
+			});
+		},
+		onMoreGrouponTeam() {
+			this.showModal = true;
+		},
+		// 拼购人
+		getGrouponItem() {
+			let that = this;
+			that.$http('goods.grouponItem', {
+				goods_id: that.grouponData.id,
+				activity_id: that.grouponData.activity.id
+			}).then(res => {
+				if (res.code === 1) {
+					that.grouponTeamList = res.data;
+				}
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+// 弹窗
+.modal-box {
+	width: 750rpx;
+	height: 800rpx;
+	border-radius: 30rpx 30rpx 0 0;
+	background: #fff;
+
+	.modal-head {
+		height: 92rpx;
+		border-bottom: 1px solid rgba(223, 223, 223, 0.5);
+		padding: 0 30rpx;
+		.head-title {
+			font-size: 32rpx;
+			font-weight: bold;
+		}
+	}
+
+	.modal-content {
+		padding: 0 30rpx;
+	}
+	.modal-foot {
+		font-size: 24rpx;
+		
+		font-weight: 500;
+		color: rgba(153, 153, 153, 1);
+		height: 80rpx;
+		border-top: 1px solid rgba(223, 223, 223, 0.5);
+	}
+}
+// 拼团人数
+.group-people {
+	background-color: #fff;
+	padding: 0 20rpx;
+	margin-top: 10rpx;
+
+	.into-title {
+		height: 80rpx;
+		font-size: 26rpx;
+		font-weight: bold;
+	}
+}
+.into-item {
+	height: 120rpx;
+	width: 100%;
+	border-bottom: 1rpx solid #eee;
+
+	.into-img {
+		width: 60rpx;
+		height: 60rpx;
+		border-radius: 50%;
+		margin-right: 23rpx;
+		background-color: #ccc;
+	}
+
+	.into-name {
+		font-size: 28rpx;
+		
+		font-weight: 500;
+		color: rgba(102, 102, 102, 1);
+	}
+
+	.num {
+		font-size: 26rpx;
+		color: #666;
+
+		.num-color {
+			font-size: 24rpx;
+			color: #a8700d;
+		}
+	}
+
+	.time {
+		font-size: 22rpx;
+		margin-top: 10rpx;
+		color: #999;
+	}
+	.join-btn {
+		width: 140rpx;
+		line-height: 60rpx;
+		background: linear-gradient(90deg, rgba(254, 131, 42, 1), rgba(255, 102, 0, 1));
+		box-shadow: 0px 7rpx 6rpx 0px rgba(255, 104, 4, 0.22);
+		border-radius: 30rpx;
+		padding: 0;
+		font-size: 26rpx;
+		
+		font-weight: 500;
+		color: rgba(255, 255, 255, 1);
+		margin-left: 40rpx;
+	}
+}
+</style>

+ 377 - 0
pages/goods/components/sh-price-card.vue

@@ -0,0 +1,377 @@
+<template>
+	<view>
+		<!-- 正常商品 -->
+		<view class="normal-price-box" v-if="type !== 'score' && !detail.activity">
+			<view class="u-felx u-col-bottom">
+				<text class="unit font-OPPOSANS">¥</text>
+				<text class="price font-OPPOSANS">{{ detail.price }}</text>
+				<text class="notice">优惠价</text>
+			</view>
+			<view class="u-flex u-row-left price-bottom-box">
+				<view class="u-flex">
+					<view class="original-price font-OPPOSANS">原价:¥{{ detail.original_price }}</view>
+					<text class="line"></text>
+					<view class="sold font-OPPOSANS">已售:{{ detail.sales }}件</view>
+				</view>
+			</view>
+		</view>
+
+		<!-- 积分商品 -->
+		<view class="score-price-card" v-if="type === 'score'">
+			<view class="u-flex u-col-bottom">
+				<image class="score-img" :src="$IMG_URL + '/imgs/score/score.png'" mode=""></image>
+				<text class="price">{{ detail.price }}</text>
+			</view>
+			<view class="u-felx u-col-bottom price-bottom-box">
+				<view class="u-flex">
+					<view class="original-price font-OPPOSANS">价值:¥{{ detail.original_price }}</view>
+					<text class="line"></text>
+					<view class="sold font-OPPOSANS">已兑换:{{ detail.sales }}件</view>
+				</view>
+			</view>
+		</view>
+
+		<!-- 团购商品 -->
+		<view class="groupon-price-box" v-if="detail.activity && detail.activity.type === 'groupon'">
+			<view class="u-flex u-col-bottom">
+				<text class="unit font-OPPOSANS">¥</text>
+				<text class="price font-OPPOSANS">{{ detail.activity_type === 'groupon' ? detail.groupon_price : detail.price }}</text>
+				<text class="notice font-OPPOSANS">{{ detail.activity.rules.team_num }}人团</text>
+			</view>
+			<view class="u-flex u-row-between price-bottom-box">
+				<view class="u-flex">
+					<view class="original-price font-OPPOSANS">原价:¥{{ detail.original_price }}</view>
+					<text class="line"></text>
+					<view class="sold font-OPPOSANS">已拼:{{ detail.sales }}件</view>
+				</view>
+				<view class="count-down font-OPPOSANS" v-show="activityRules.status !== 'end'">
+					<text class="u-p-r-10" v-show="activityRules.status === 'waiting'">距开始</text>
+					<text class="u-p-r-10" v-show="activityRules.status === 'ing'">距结束</text>
+					{{ activityRules.countDownTime.h || '00' }} : {{ activityRules.countDownTime.m || '00' }} : {{ activityRules.countDownTime.s || '00' }}
+				</view>
+				<view class="count-down" v-show="activityRules.status === 'end'">活动已结束</view>
+			</view>
+		</view>
+
+		<!-- 秒杀商品 -->
+		<view class="seckill-price-box " :class="activityRules.status !== 'end' ? 'seckill-bg' : 'seckilled-bg'" v-if="detail.activity && detail.activity.type === 'seckill'">
+			<view class="u-flex u-row-between price-top-box">
+				<view class="u-flex">
+					<text class="unit font-OPPOSANS">¥</text>
+					<text class="price font-OPPOSANS">{{ detail.price }}</text>
+					<text class="notice">秒杀价</text>
+				</view>
+				<view class="count-down font-OPPOSANS" v-show="activityRules.status !== 'end'">
+					<text class="u-p-r-10" v-show="activityRules.status === 'waiting'">距开始</text>
+					<text class="u-p-r-10" v-show="activityRules.status === 'ing'">距结束</text>
+					{{ activityRules.countDownTime.h || '00' }} : {{ activityRules.countDownTime.m || '00' }} : {{ activityRules.countDownTime.s || '00' }}
+				</view>
+				<view class="count-down" v-show="activityRules.status === 'end'">活动已结束</view>
+			</view>
+			<view class="u-flex u-row-between price-bottom-box">
+				<view class="u-flex">
+					<view class="original-price font-OPPOSANS">原价:¥{{ detail.original_price }}</view>
+					<text class="line"></text>
+					<view class="sold font-OPPOSANS">已售:{{ detail.sales }}件</view>
+				</view>
+				<view class="express">
+					<view class="u-flex">
+						<view style="width:100rpx;" v-show="activityRules.status !== 'end'">
+							<u-line-progress height="14" :show-percent="false" :percent="percent" inactive-color="#a1071a" active-color="#fff"></u-line-progress>
+						</view>
+						<view class="progress-text" v-if="detail.stock > 0">仅剩{{ detail.stock }}件</view>
+						<view class="progress-text" v-else>已售罄</view>
+					</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+/**
+ * 商品详情价格卡片
+ * @property {Object} detail - 商品详情
+ * @property {String} type - 商品分类
+ */
+let timer = null;
+export default {
+	components: {},
+	data() {
+		return {
+			time: {}, //倒计时
+			activityRules: {
+				startTime: 0,
+				endTime: 0,
+				status: '',
+				countDownTime: 0
+			}
+		};
+	},
+	props: {
+		detail: Object,
+		type: ''
+	},
+	created() {
+		if (this.detail.activity && this.detail.activity.type) {
+			this.doActivityRules();
+		}
+	},
+	beforeDestroy() {
+		clearInterval(timer);
+		timer = null;
+	},
+	computed: {
+		// 百分比
+		percent() {
+			let unit = 0;
+			if (this.detail.id) {
+				unit = this.detail.stock + this.detail.sales > 0 ? ((this.detail.sales / (this.detail.sales + this.detail.stock)) * 100).toFixed(2) : 0;
+			}
+			return Number(unit);
+		}
+	},
+	methods: {
+		// 触发活动规则
+		doActivityRules() {
+			let that = this;
+			switch (that.detail.activity.type) {
+				case 'seckill':
+				case 'groupon':
+					that.activityRules.startTime = that.detail.activity.starttime * 1000;
+					that.activityRules.endTime = that.detail.activity.endtime * 1000;
+					that.countDown();
+					break;
+			}
+		},
+		// 计时器
+		countDown() {
+			let that = this;
+			let t = 0;
+
+			timer = setInterval(() => {
+				let nowTime = new Date().getTime();
+
+				//当前日期大于活动结束日期,活动结束
+				if (that.activityRules.endTime < nowTime) {
+					that.activityRules.status = 'end';
+					that.$emit('change', JSON.stringify(that.activityRules));
+				}
+
+				// 活动未结束
+				if (nowTime < that.activityRules.startTime) {
+					//当前日期小于开始时间,活动未开始
+					that.activityRules.status = 'waiting';
+					t = that.activityRules.startTime - nowTime;
+				} else if (nowTime > that.activityRules.endTime) {
+					//当前日期大于结束时间,活动结束
+					that.activityRules.status = 'end';
+					clearInterval(timer);
+					return;
+				} else {
+					// 活动进行中
+					that.activityRules.status = 'ing';
+					t = that.activityRules.endTime - nowTime;
+				}
+				that.activityRules.countDownTime = that.formatToHours(t / 1000);
+				t--;
+				that.$emit('change', JSON.stringify(that.activityRules));
+			}, 1000);
+		},
+		//时间格式化(格式化最大为小时)
+		formatToHours(t) {
+			let format = {
+				h: '00',
+				m: '00',
+				s: '00'
+			};
+			if (t > 0) {
+				let h = Math.floor(t / 3600);
+				let m = Math.floor((t / 60) % 60);
+				let s = Math.floor(t % 60);
+
+				format.h = h < 10 ? '0' + h : h;
+				format.m = m < 10 ? '0' + m : m;
+				format.s = s < 10 ? '0' + s : s;
+			}
+			return format;
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+// 正常商品
+.normal-price-box {
+	padding: 20rpx;
+	background: url($IMG_URL+'/imgs/detail/detail_price_normal_bg.png') no-repeat;
+	background-size: 100% 100%;
+	.unit,
+	.notice {
+		font-size: 24rpx;
+		color: #fff;
+	}
+
+	.price {
+		font-size: 36rpx;
+		color: #fff;
+		font-weight: bold;
+		margin: 0 10rpx;
+	}
+
+	.price-bottom-box {
+		font-size: 24rpx;
+		color: #fff;
+		font-weight: 500;
+		padding-top: 10rpx;
+		.original-price {
+			text-decoration: line-through;
+		}
+		.line {
+			margin: 0 20rpx;
+			display: inline-block;
+			width: 3rpx;
+			height: 24rpx;
+			background-color: #fff;
+		}
+	}
+}
+
+// 团购商品
+.groupon-price-box {
+	padding: 20rpx;
+	background: url($IMG_URL+'/imgs/detail/detail_group_price_bg.png') no-repeat;
+	background-size: 100% 100%;
+	.unit,
+	.notice {
+		font-size: 24rpx;
+		color: rgba(#fff, 0.9);
+	}
+	.notice {
+		line-height: 40rpx;
+		border: 1rpx solid rgba(255, 255, 255, 1);
+		border-radius: 6rpx;
+		padding: 0 10rpx;
+	}
+	.price {
+		font-size: 36rpx;
+		color: rgba(#fff, 0.9);
+		font-weight: bold;
+		margin: 0 10rpx;
+	}
+
+	.price-bottom-box {
+		font-size: 24rpx;
+		color: rgba(#fff, 0.9);
+		font-weight: 500;
+		padding-top: 10rpx;
+
+		.line {
+			margin: 0 20rpx;
+			display: inline-block;
+			width: 3rpx;
+			height: 24rpx;
+			background-color: #fff;
+		}
+		.count-down {
+			font-size: 24rpx;
+			color: rgba(#fff, 0.9);
+		}
+	}
+}
+// 积分商品价格卡片
+.score-price-card {
+	padding: 20rpx;
+	background: url($IMG_URL+'/imgs/detail/detail_price_score_bg.png') no-repeat;
+	background-size: 100% 100%;
+	width: 750rpx;
+	.notice {
+		font-size: 24rpx;
+		color: #fff;
+	}
+	.score-img {
+		width: 36rpx;
+		height: 36rpx;
+	}
+	.price {
+		font-size: 36rpx;
+		color: #fff;
+		font-weight: bold;
+		margin: 0 10rpx;
+	}
+
+	.price-bottom-box {
+		font-size: 24rpx;
+		color: #fff;
+		font-weight: 500;
+		padding-top: 10rpx;
+		.original-price {
+			text-decoration: line-through;
+		}
+		.line {
+			margin: 0 20rpx;
+			display: inline-block;
+			width: 3rpx;
+			height: 24rpx;
+			background-color: #fff;
+		}
+	}
+}
+// 秒杀商品
+.seckill-bg {
+	background: url($IMG_URL+'/imgs/detail/detail_price_seckill_bg.png') no-repeat;
+	background-size: 100% 100%;
+}
+.seckilled-bg {
+	background: url($IMG_URL+'/imgs/detail/detail_price_seckilled_bg.png') no-repeat;
+	background-size: 100% 100%;
+}
+.seckill-price-box {
+	padding: 20rpx;
+	.unit,
+	.notice,
+	.count-down {
+		font-size: 24rpx;
+		color: rgba(#fff, 0.9);
+	}
+
+	.notice {
+		line-height: 40rpx;
+		border: 1rpx solid rgba(255, 255, 255, 1);
+		border-radius: 6rpx;
+		padding: 0 10rpx;
+		margin-left: 10rpx;
+	}
+
+	.price {
+		font-size: 36rpx;
+		color: rgba(#fff, 0.9);
+		font-weight: bold;
+		margin: 0 10rpx;
+	}
+
+	.price-bottom-box {
+		font-size: 24rpx;
+		color: rgba(#fff, 0.9);
+		font-weight: 500;
+		padding-top: 10rpx;
+		.original-price {
+			text-decoration: line-through;
+		}
+		.express {
+			.progress-text {
+				color: rgba(#fff, 0.9);
+				font-size: 24rpx;
+				margin-left: 10rpx;
+			}
+		}
+		.line {
+			margin: 0 20rpx;
+			display: inline-block;
+			width: 3rpx;
+			height: 24rpx;
+			background-color: #fff;
+		}
+	}
+}
+</style>

+ 140 - 0
pages/goods/components/sh-serve.vue

@@ -0,0 +1,140 @@
+<template>
+	<view class="sh-server-box">
+		<view class="u-flex u-row-between serve-box" @tap="showModal = true">
+			<view class="u-flex">
+				<text class="title">服务</text>
+				<view class="tip u-flex">
+					<view class="tip-list u-flex" v-for="(serve, index) in serveList" :key="serve.title">
+						<view class="u-flex" v-if="index < 3">
+							<image class="tip-img" :src="serve.image" mode=""></image>
+							<text>{{ serve.name }}</text>
+						</view>
+					</view>
+				</view>
+			</view>
+			<view class="u-iconfont uicon-arrow-right" style="color: #bfbfbf;font-size: 28rpx;"></view>
+		</view>
+		<u-popup v-model="showModal" mode="bottom" :closeable="true" close-icon-pos="top-right">
+			<view class=" server-modal">
+				<view class="server-modal-box page_box">
+					<view class="modal-head u-flex u-row-center u-col-center"><text class="head-title">服务保障</text></view>
+					<view class="modal-content content_box">
+						<view class="serve-list" v-for="serve in serveList" :key="serve.title">
+							<view class="title-box u-flex">
+								<image class="title-tag" :src="serve.image" mode=""></image>
+								<text class="serve-title">{{ serve.name }}</text>
+							</view>
+							<view class="serve-detail">{{ serve.description }}</view>
+						</view>
+					</view>
+					<view class="modal-foot u-flex u-row-center u-col-center"><button class=" u-reset-button serve-btn" @tap="showModal = false">确定</button></view>
+				</view>
+			</view>
+		</u-popup>
+	</view>
+</template>
+
+<script>
+/**
+ * 商品服务卡片
+ * @property {Array} serveList - 商品服务列表
+ * @property {Boolean} showModal- 显隐
+ */
+export default {
+	components: {},
+	data() {
+		return {
+			showModal: false
+		};
+	},
+	props: {
+		serveList: {
+			type: Array,
+			default: () => []
+		}
+	},
+	computed: {},
+	methods: {}
+};
+</script>
+
+<style lang="scss">
+.serve-box {
+	line-height: 82rpx;
+	background: #fff;
+	padding: 0 20rpx;
+	margin: 10rpx 0;
+
+	.title {
+		font-size: 28rpx;
+		color: #999;
+		margin-right: 20rpx;
+	}
+
+	.tip {
+		.tip-list {
+			font-size: 28rpx;
+			margin-right: 20rpx;
+
+			.tip-img {
+				width: 30rpx;
+				height: 30rpx;
+				margin-right: 10rpx;
+			}
+		}
+	}
+}
+
+.server-modal-box {
+	width: 750rpx;
+	height: 100%;
+	border-radius: 30rpx 30rpx 0 0;
+	background: #fff;
+	padding: 30rpx;
+	.serve-btn {
+		width: 690rpx;
+		line-height: 80rpx;
+		background: linear-gradient(90deg, rgba(240, 199, 133, 1), rgba(246, 214, 157, 1));
+		border-radius: 40rpx;
+		font-size: 30rpx;
+		color: rgba(#fff, 0.9);
+		margin-top: 40rpx;
+	}
+
+	.modal-head {
+		margin-bottom: 30rpx;
+		position: relative;
+		.head-title {
+			font-size: 32rpx;
+			font-weight: bold;
+		}
+	}
+
+	.modal-content {
+		overflow-y: auto;
+		.serve-list {
+			padding-bottom: 40rpx;
+
+			.title-tag {
+				width: 38rpx;
+				height: 38rpx;
+				margin-right: 20rpx;
+			}
+
+			.serve-title {
+				font-size: 28rpx;
+				font-weight: bold;
+			}
+
+			.serve-detail {
+				text-align: left;
+				font-size: 26rpx;
+				color: #999;
+				line-height: 42rpx;
+				padding-left: 58rpx;
+				margin-top: 20rpx;
+			}
+		}
+	}
+}
+</style>

+ 772 - 0
pages/goods/detail.vue

@@ -0,0 +1,772 @@
+<!-- 商品详情 -->
+<template>
+	<view class="detail-wrap ">
+		<!-- 标题栏 -->
+		<view class="nav-box">
+			<view class="state-hack"></view>
+			<view :style="{ height: navbarHeight + 'px' }">
+				<view hover-class="back-hover" class="back-box u-m-x-30 u-flex u-row-center u-col-center" @tap="goBack">
+					<view class="u-iconfont uicon-arrow-left" style="color: #fff;font-size: 28rpx;"></view>
+				</view>
+			</view>
+		</view>
+
+		<!-- 缺省页 -->
+		<shopro-empty v-if="showEmpty" :image="$IMG_URL + '/imgs/empty/empty_goods_null.png'" :tipText="showEmptyText" btnText="返回上一页" @click="$Router.back()"></shopro-empty>
+		<view class="detail_box" v-else>
+			<view class="detail-content">
+				<!-- 轮播 -->
+				<u-swiper :height="750" borderRadius="0" :list="goodsInfo.images" indicator-pos="bottomRight" mode="number" :interval="3000" @click="onGoodsSwiper"></u-swiper>
+
+				<!-- 价格卡片组 -->
+				<sh-price-card v-if="goodsInfo.id" :detail="goodsInfo" :type="detailType" @change="getActivityRules"></sh-price-card>
+
+				<!-- 标题 -->
+				<view class="u-m-b-10 title-box u-p-20 u-skeleton-fillet">
+					<view class="goods-title u-m-b-10 u-ellipsis-2">{{ goodsInfo.title }}</view>
+					<view class="sub-title u-ellipsis-2">{{ goodsInfo.subtitle }}</view>
+				</view>
+
+				<!-- 参与的活动 -->
+				<sh-activity v-if="goodsInfo.activity_discounts_tags && goodsInfo.activity_discounts_tags.length" :detail="goodsInfo.activity_discounts"></sh-activity>
+
+				<!-- 规格选择 -->
+				<view class="sku-box" @tap="showSku = true" v-if="activityRules.status !== 'waiting' && checkActivity(goodsInfo.activity_type, 'groupon') && goodsInfo.is_sku">
+					<view class="u-flex u-row-between u-col-center">
+						<view class="u-flex">
+							<text class="title">规格</text>
+							<text class="tip">{{ currentSkuText || '请选择规格' }}</text>
+						</view>
+						<text class="u-iconfont uicon-arrow-right" style="color: #bfbfbf;"></text>
+					</view>
+				</view>
+
+				<!-- 服务 -->
+				<sh-serve v-if="goodsInfo.service && goodsInfo.service.length" v-model="showServe" :serveList="goodsInfo.service"></sh-serve>
+
+				<!-- 优惠券 -->
+				<sh-coupon
+					v-if="goodsInfo.coupons && goodsInfo.coupons.length && goodsInfo.activity_type !== 'seckill' && goodsInfo.activity_type !== 'groupon' && detailType !== 'score'"
+					:couponList="goodsInfo.coupons"
+				></sh-coupon>
+
+				<!-- 拼团人-->
+				<sh-groupon
+					v-if="goodsInfo.activity && goodsInfo.activity.type === 'groupon' && goodsInfo.activity.rules.team_card === '1' && detailType !== 'score'"
+					:grouponData="goodsInfo"
+				></sh-groupon>
+
+				<!-- 拼团玩法 -->
+				<view
+					v-if="goodsInfo.activity && Number(goodsInfo.activity.richtext_id) && goodsInfo.activity_type !== 'seckill'"
+					class="groupon-play u-flex u-row-between u-p-l-20 u-p-r-20"
+					@tap="jump('/pages/public/richtext', { id: goodsInfo.activity.richtext_id })"
+				>
+					<view class="u-flex">
+						<text class="title">玩法</text>
+						<view class="description u-ellipsis-1">{{ goodsInfo.activity.richtext_title || '开团/参团·邀请好友·人满发货(不满退款' }}</view>
+					</view>
+					<view class="u-iconfont uicon-arrow-right" style="color:#bfbfbf ;font-size: 28rpx;"></view>
+				</view>
+
+				<!-- 选项卡 -->
+				<view class="tab-box u-flex">
+					<view class="tab-item u-flex-col u-row-center u-col-center" @tap="onTab(tab.id)" v-for="tab in tabList" :key="tab.id">
+						<view class="tab-title">
+							{{ tab.title }}
+							<text v-if="tab.id == 'tab2'" class="comment-num">({{ commentNum }})</text>
+						</view>
+						<text class="tab-line" :class="{ 'line-active': tabCurrent === tab.id }"></text>
+					</view>
+				</view>
+				<view class="tab-detail u-p-20 u-m-b-10">
+					<!-- 详情富文本 -->
+					<view class="rich-box " v-if="tabCurrent === 'tab0'"><u-parse :html="goodsInfo.content"></u-parse></view>
+					<!-- 参数 -->
+					<view class="goods-size" v-if="tabCurrent === 'tab1'">
+						<view class="table-box" v-if="goodsInfo.params && goodsInfo.params.length">
+							<view class="t-tr u-flex" v-for="t in goodsInfo.params" :key="t.title">
+								<view class="t-head u-flex">{{ t.title }}</view>
+								<view class="t-detail">{{ t.content }}</view>
+							</view>
+						</view>
+					</view>
+					<!-- 评价 -->
+					<view class="goods-comment" v-if="tabCurrent === 'tab2'">
+						<block v-for="comment in commentList" :key="comment.id"><sh-comment :comment="comment"></sh-comment></block>
+						<shopro-empty v-show="!commentList.length" marginTop="20rpx" :image="$IMG_URL + '/imgs/empty/comment_empty.png'" tipText="暂无评价~"></shopro-empty>
+						<view class="more-box u-flex u-row-center u-col-center" v-show="commentNum > 3">
+							<button class="u-reset-button more-btn u-flex u-row-center u-col-center" @tap="jump('/pages/goods/comment/comment-list', { goodsId: goodsInfo.id })">
+								查看全部
+								<view class="u-iconfont uicon-arrow-right" style="color:#d5a65a ;font-size: 28rpx;"></view>
+							</button>
+						</view>
+					</view>
+				</view>
+			</view>
+
+			<!-- 商品foot -->
+			<view class="tabbar-foot safe-area-inset-bottom" v-if="goodsInfo.id">
+				<view class="detail-foot_box safe-area-inset-bottom  u-flex u-col-ceter u-row-around">
+					<!-- foot左侧 -->
+					<view class="left u-flex">
+						<!-- 积分foot -->
+						<view class="tools-item u-flex-col u-row-center u-col-center" @tap="goHome">
+							<image class="tool-img shopro-selector-circular" src="/static/images/tabbar/tabbar_home1.png" mode=""></image>
+							<text class="tool-title shopro-selector-rect">首页</text>
+						</view>
+						<!-- 非积分foot -->
+						<block v-if="detailType !== 'score'">
+							<view class="tools-item u-flex-col u-row-center u-col-center" @tap="onFavorite(goodsInfo.id)">
+								<image
+									class="tool-img"
+									:src="Boolean(goodsInfo.favorite) ? $IMG_URL + '/imgs/detail/detail_favorite_end.png' : $IMG_URL + '/imgs/detail/detail_favorite.png'"
+									mode=""
+								></image>
+								<text class="tool-title">收藏</text>
+							</view>
+							<view class="tools-item u-flex-col u-row-center u-col-center" @tap="onShare">
+								<image class="tool-img" :src="$IMG_URL + '/imgs/share/share.png'" mode=""></image>
+								<text class="tool-title">分享</text>
+							</view>
+						</block>
+					</view>
+					<!-- foot右侧 -->
+					<view class="detail-right">
+						<!-- 积分按钮 -->
+						<view class="detail-btn-box" v-if="detailType === 'score'"><button class="u-reset-button  score-btn" @tap="goPay">立即兑换</button></view>
+						<view v-if="detailType !== 'score'">
+							<!-- 正常按钮 -->
+							<view class="detail-btn-box u-flex u-row-around" v-if="!goodsInfo.activity_type">
+								<button class=" u-reset-button tool-btn add-btn" @tap="addCart">加入购物车</button>
+								<button class=" u-reset-button tool-btn pay-btn" @tap="goPay">立即购买</button>
+							</view>
+							<!-- 活动按钮 -->
+							<view class="detail-btn-box u-row-around" v-if="activityRules.status && goodsInfo.activity_type">
+								<button class=" u-reset-button seckilled-btn" v-if="activityRules.status !== 'ing'">
+									<text v-if="activityRules.status == 'waiting'">暂未开始</text>
+									<text v-if="activityRules.status == 'end'">已结束</text>
+								</button>
+								<block v-else>
+									<!-- 活动中,秒杀 -->
+									<button class=" u-reset-button  seckill-btn" v-if="goodsInfo.activity && goodsInfo.activity.type === 'seckill'" @tap="goSeckill">
+										立即秒杀
+									</button>
+									<!-- 活动中,拼团 -->
+									<view class="u-flex groupon-btn-box" v-if="goodsInfo.activity && goodsInfo.activity.type === 'groupon'">
+										<button
+											class="tool-btn u-reset-button add-btn u-flex-col u-row-center u-col-center"
+											@tap="payGroupon"
+											v-if="goodsInfo.activity.rules.is_alone === '1'"
+										>
+											<text class="price font-OPPOSANS">¥{{ goodsInfo.price }}</text>
+											<text class="price-title">单独购买</text>
+										</button>
+										<button
+											class=" tool-btn u-reset-button groupon-btn u-flex-col u-row-center u-col-center"
+											:style="goodsInfo.activity.rules.is_alone === '0' ? 'width:400rpx' : ''"
+											@tap="payGroupon('groupon')"
+										>
+											<text class="price font-OPPOSANS">¥{{ goodsInfo.groupon_price }}</text>
+											<text class="price-title">我要开团</text>
+										</button>
+									</view>
+								</block>
+							</view>
+						</view>
+					</view>
+				</view>
+			</view>
+
+			<!-- 规格弹窗 -->
+			<shopro-sku
+				v-if="showSku && goodsInfo.id"
+				v-model="showSku"
+				:goodsInfo="goodsInfo"
+				:activityRules="activityRules"
+				:buyType="goodsInfo.activity_type == 'seckill' || detailType === 'score' ? 'buy' : buyType"
+				:grouponBuyType="grouponBuyType"
+				:goodsType="detailType === 'score' ? 'score' : 'goods'"
+				@changeType="changeType"
+				@getSkuText="getSkuText"
+			></shopro-sku>
+
+			<!-- 	分享组件 -->
+			<shopro-share v-model="showShare" :posterInfo="goodsInfo" :posterType="'goods'"></shopro-share>
+			<!-- 登录 -->
+			<shopro-auth-modal v-if="authType"></shopro-auth-modal>
+		</view>
+	</view>
+</template>
+
+<script>
+let systemInfo = uni.getSystemInfoSync();
+import shActivity from './components/sh-activity.vue';
+import shPriceCard from './components/sh-price-card.vue';
+import shServe from './components/sh-serve.vue';
+import shGroupon from './components/sh-groupon.vue';
+import shCoupon from './components/sh-coupon.vue';
+import shComment from './components/sh-comment.vue';
+import share from '@/shopro/share';
+
+import { mapMutations, mapActions, mapState, mapGetters } from 'vuex';
+export default {
+	components: {
+		shServe,
+		shPriceCard,
+		shGroupon,
+		shCoupon,
+		shComment,
+		shActivity
+	},
+	data() {
+		return {
+			// navbar
+			backIconName: 'arrow-left',
+
+			showSku: false, //是否显示规格弹窗
+			currentSkuText: '', //选中规格
+			detailType: '',
+			showShare: false,
+			buyType: 'sku',
+			grouponBuyType: 'alone', //拼团购买方式。
+			showEmpty: false,
+			showEmptyText: '',
+			showServe: false,
+			goodsInfo: {},
+			commentList: [], //商品评价列表
+			commentNum: 0, //商品评价总数
+			favorite: false,
+			activityRules: {},
+			currentSkuList: [],
+			confirmGoodsInfo: {},
+			swiperCurrent: 0, //轮播下标
+			tabCurrent: 'tab0',
+			tabList: [
+				{
+					id: 'tab0',
+					title: '商品详情'
+				},
+				{
+					id: 'tab1',
+					title: '规格参数'
+				},
+				{
+					id: 'tab2',
+					title: '用户评价'
+				}
+			]
+		};
+	},
+	// 是否登录
+	computed: {
+		...mapGetters(['isLogin', 'authType']),
+		navbarHeight() {
+			// #ifdef APP-PLUS || H5
+			return 48;
+			// #endif
+			// #ifdef MP
+			let height = systemInfo.platform == 'ios' ? 44 : 48;
+			return height;
+			// #endif
+		}
+	},
+	async onLoad() {
+		let that = this;
+		this.backIconName = getCurrentPages().length > 1 ? 'arrow-left' : 'home-fill';
+		const type = this.$Route.query.type;
+		this.detailType = type;
+		switch (type) {
+			case 'score':
+				await this.getScoreDetail();
+				break;
+			default:
+				await this.getGoodsDetail();
+		}
+	},
+
+	onUnload() {
+		share.setShareInfo();
+	},
+
+	methods: {
+		// goBack
+		// 微信直播商品跳转详情,需要用小程序原生接口才能返回。
+		goBack() {
+			let pageList = getCurrentPages();
+			if (pageList.length > 1) {
+				// #ifdef MP-WEIXIN
+				wx.navigateBack();
+				// #endif
+				// #ifndef MP-WEIXIN
+				uni.navigateBack();
+				// #endif
+			} else {
+				this.$Router.replaceAll('/pages/index/index');
+			}
+		},
+
+		// 轮播图预览
+		onGoodsSwiper(e) {
+			this.$tools.previewImage(this.goodsInfo.images, e);
+		},
+		getActivityRules(e) {
+			if (e) {
+				this.activityRules = JSON.parse(e);
+			}
+		},
+		// 检测
+		checkActivity(data, type) {
+			if (data) {
+				return !data.includes(type);
+			}
+			return true;
+		},
+		// 路由跳转
+		jump(path, parmas) {
+			this.showShare = false;
+			this.$Router.push({
+				path: path,
+				query: parmas
+			});
+		},
+		// 回到首页
+		goHome() {
+			this.$Router.replaceAll('/pages/index/index');
+		},
+
+		// 选项卡
+		onTab(id) {
+			this.tabCurrent = id;
+		},
+		// 积分详情
+		getScoreDetail() {
+			let that = this;
+			return new Promise((resolve, reject) => {
+				that.$http('goods.scoreDetail', {
+					id: that.$Route.query.id
+				}).then(res => {
+					if (res.code === 1) {
+						that.goodsInfo = res.data;
+						resolve(res.data);
+					}
+					if (res.code == 0) {
+						resolve(false);
+						that.$u.toast(res.msg);
+					}
+				});
+			});
+		},
+		// 商品详情
+		getGoodsDetail() {
+			let that = this;
+			return new Promise((resolve, reject) => {
+				that.$http(
+					'goods.detail',
+					{
+						id: that.$Route.query.id
+					},
+					'',
+					false
+				).then(res => {
+					if (res.code === 1) {
+						that.showEmpty = false;
+						that.goodsInfo = res.data;
+						if (that.goodsInfo.activity_type) {
+							that.activityRules.status = that.goodsInfo.activity.status_code;
+						}
+						share.setShareInfo({
+							title: that.goodsInfo.title,
+							desc: that.goodsInfo.subtitle,
+							image: that.goodsInfo.image,
+							params: {
+								page: 2,
+								pageId: that.$Route.query.id
+							}
+						});
+						that.getCommentList();
+						resolve(res.data);
+					} else {
+						resolve(false);
+						// that.$u.toast(res.msg);
+						that.showEmpty = true;
+						that.showEmptyText = res.msg;
+					}
+				});
+			});
+		},
+		// 商品评论
+		getCommentList() {
+			let that = this;
+			that.$http('goods.commentList', {
+				goods_id: that.goodsInfo.id,
+				per_page: 3,
+				type: 'all'
+			}).then(res => {
+				if (res.code === 1) {
+					that.commentList = res.data.data;
+					that.commentNum = res.data.total;
+				}
+			});
+		},
+		// 组件返回的type;
+		changeType(e) {
+			this.buyType = e;
+		},
+		// 组件返回的规格;
+		getSkuText(e) {
+			this.currentSkuText = e;
+		},
+		// 分享
+		onShare() {
+			this.showShare = true;
+		},
+		// 加入购物车
+		addCart() {
+			if (this.isLogin) {
+				this.buyType = 'cart';
+				this.showSku = true;
+			} else {
+				this.$store.dispatch('showAuthModal');
+			}
+		},
+		// 立即购买
+		goPay() {
+			if (this.isLogin) {
+				this.buyType = 'buy';
+				this.showSku = true;
+			} else {
+				this.$store.dispatch('showAuthModal');
+			}
+		},
+		// 拼团购买
+		payGroupon(type) {
+			if (this.isLogin) {
+				if (type === 'groupon') {
+					this.grouponBuyType = 'groupon';
+				} else {
+					this.grouponBuyType = 'alone';
+				}
+				this.buyType = 'buy';
+				this.showSku = true;
+			} else {
+				this.$store.dispatch('showAuthModal');
+			}
+		},
+
+		// 立即秒杀
+		goSeckill() {
+			!this.isLogin ? this.$store.dispatch('showAuthModal') : (this.showSku = true);
+			this.buyType = 'buy';
+		},
+
+		// 收藏
+		onFavorite(goodsId) {
+			let that = this;
+			that.$http('goods.favorite', {
+				goods_id: goodsId
+			}).then(res => {
+				if (res.code === 1) {
+					that.goodsInfo.favorite = res.data;
+					that.$u.toast(res.msg);
+				}
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+// 标题栏
+.nav-box {
+	position: fixed;
+	width: 100%;
+	min-height: 140rpx;
+	z-index: 888;
+	top: 0;
+	.back-box {
+		background-color: rgba(#000, 0.18);
+		width: 60rpx;
+		height: 60rpx;
+		border-radius: 50%;
+		margin-top: 14rpx;
+	}
+	.back-hover {
+		background-color: rgba(#fff, 0.18);
+	}
+	.state-hack {
+		width: 100%;
+		height: var(--status-bar-height);
+		/* #ifdef H5 */
+		height: 20rpx;
+		/* #endif */
+	}
+}
+// 拼团玩法
+.groupon-play {
+	background: #fff;
+	line-height: 94rpx;
+	.title {
+		font-size: 28rpx;
+		color: #999;
+	}
+
+	.description {
+		font-size: 28rpx;
+		width: 500rpx;
+		margin-left: 30rpx;
+	}
+}
+
+// 选项卡
+.tab-box {
+	height: 102rpx;
+	background: #fff;
+	border-bottom: 1rpx solid rgba(#dfdfdf, 0.8);
+	margin-top: 10rpx;
+
+	.tab-item {
+		flex: 1;
+		height: 100%;
+		position: relative;
+		font-size: 30rpx;
+		font-weight: bold;
+
+		.tab-line {
+			width: 123rpx;
+			height: 4rpx;
+			left: 50%;
+			bottom: 0;
+			transform: translateX(-50%);
+			background: transparent;
+			position: absolute;
+			z-index: 2;
+		}
+
+		.line-active {
+			background: rgba(168, 112, 13, 1);
+		}
+	}
+}
+
+// 选项卡内容
+.tab-detail {
+	min-height: 300rpx;
+	background: #fff;
+	// 规格参数
+	.goods-size {
+		.table-box {
+			width: 710rpx;
+			margin: auto;
+			background: rgba(255, 255, 255, 1);
+			border: 1rpx solid rgba(223, 223, 223, 1);
+
+			.t-tr {
+				border-bottom: 1rpx solid rgba(223, 223, 223, 1);
+				&:last-child {
+					border-bottom: none;
+				}
+
+				.t-head {
+					font-size: 26rpx;
+					color: #999;
+					flex: 1;
+					padding: 15rpx 20rpx;
+					height: 100%;
+				}
+
+				.t-detail {
+					font-size: 26rpx;
+					border-left: 1rpx solid rgba(223, 223, 223, 1);
+					flex: 4;
+					padding: 15rpx 20rpx;
+					height: 100%;
+				}
+			}
+		}
+	}
+	// 富文本
+	.rich-box {
+		/deep/ img {
+			display: block;
+		}
+	}
+	// 评论
+	.goods-comment {
+		.more-box {
+			height: 100rpx;
+			background: #fff;
+
+			.more-btn {
+				width: 200rpx;
+				line-height: 60rpx;
+				border: 1rpx solid rgba(213, 166, 90, 1);
+				border-radius: 30rpx;
+				font-size: 26rpx;
+				font-weight: 400;
+				color: rgba(168, 112, 13, 1);
+				padding: 0;
+				background: #fff;
+			}
+		}
+	}
+}
+
+// 规格卡片
+.sku-box {
+	line-height: 82rpx;
+	background: #fff;
+	padding: 0 20rpx;
+	margin: 10rpx 0;
+	font-size: 28rpx;
+
+	.title {
+		color: #999;
+		margin-right: 20rpx;
+	}
+}
+
+// 标题卡片
+.title-box {
+	background-color: #fff;
+	.goods-title {
+		font-size: 28rpx;
+		font-weight: 600;
+		line-height: 42rpx;
+	}
+
+	.sub-title {
+		color: #a8700d;
+		font-size: 24rpx;
+		font-weight: 500;
+		line-height: 42rpx;
+	}
+}
+
+// 底部工具栏
+.tabbar-foot {
+	min-height: 100rpx;
+	width: 100%;
+}
+.detail-foot_box {
+	min-height: calc(100rpx + env(safe-area-inset-bottom)) ;
+	border-top: 1rpx solid rgba(238, 238, 238, 1);
+	background-color: #fff;
+	width: 100%;
+	position: fixed;
+	bottom: 0;
+	z-index: 999;
+
+	.left,
+	.detail-right {
+		flex: 1;
+	}
+
+	.tools-item {
+		flex: 1;
+		height: 100%;
+
+		.tool-img {
+			width: 46rpx;
+			height: 46rpx;
+		}
+
+		.tool-title {
+			font-size: 22rpx;
+			line-height: 22rpx;
+			padding-top: 8rpx;
+		}
+	}
+
+	.detail-btn-box {
+		flex: 1;
+
+		.tool-btn {
+			font-size: 28rpx;
+			font-weight: 500;
+			color: rgba(#fff, 0.9);
+			width: 210rpx;
+			min-height: 70rpx;
+			border-radius: 35rpx;
+			padding: 0;
+			margin-right: 20rpx;
+			display: flex;
+			flex-direction: column;
+			justify-content: center;
+			align-items: center;
+			.price {
+				font-size: 24rpx;
+				line-height: 24rpx;
+				font-weight: bold;
+				color: rgba(#fff, 0.9);
+			}
+
+			.price-title {
+				font-size: 20rpx;
+				line-height: 20rpx;
+				font-weight: 500;
+				color: rgba(#fff, 0.9);
+				padding-top: 8rpx;
+			}
+		}
+
+		.add-btn {
+			box-shadow: 0px 2rpx 5rpx 0px rgba(102, 103, 104, 0.46);
+			background: linear-gradient(90deg, rgba(103, 104, 105, 1), rgba(82, 82, 82, 1));
+		}
+
+		.pay-btn {
+			box-shadow: 0px 7rpx 6rpx 0px rgba(229, 138, 0, 0.22);
+			background: linear-gradient(90deg, rgba(233, 180, 97, 1), rgba(238, 204, 137, 1));
+		}
+
+		.score-btn {
+			width: 600rpx;
+			line-height: 80rpx;
+			background: linear-gradient(90deg, rgba(49, 133, 243, 1), rgba(80, 205, 242, 1));
+			box-shadow: 0px 7px 6px 0px rgba(80, 205, 242, 0.2);
+			border-radius: 40rpx;
+			font-size: 30rpx;
+
+			font-weight: 500;
+			color: rgba(255, 255, 255, 1);
+			margin-right: 20rpx;
+		}
+
+		.seckill-btn {
+			width: 432rpx;
+			line-height: 70rpx;
+			background: linear-gradient(93deg, rgba(208, 19, 37, 1), rgba(237, 60, 48, 1));
+			box-shadow: 0px 7rpx 6rpx 0px rgba(#ed3c30, 0.22);
+			font-size: 28rpx;
+
+			font-weight: 500;
+			color: rgba(255, 255, 255, 1);
+			border-radius: 35rpx;
+			padding: 0;
+			margin-right: 20rpx;
+		}
+
+		.seckilled-btn {
+			width: 432rpx;
+			line-height: 70rpx;
+			background: rgba(221, 221, 221, 1);
+			font-size: 28rpx;
+
+			font-weight: 500;
+			color: #999999;
+			border-radius: 35rpx;
+			padding: 0;
+			margin-right: 20rpx;
+		}
+
+		.groupon-btn {
+			width: 210rpx;
+			height: 70rpx;
+			background: linear-gradient(90deg, rgba(254, 131, 42, 1), rgba(255, 102, 0, 1));
+			box-shadow: 0px 7rpx 6rpx 0px rgba(255, 104, 4, 0.22);
+			border-radius: 35rpx;
+		}
+	}
+}
+</style>

+ 224 - 0
pages/goods/list.vue

@@ -0,0 +1,224 @@
+<!-- 商品列表 -->
+<template>
+	<view class="list-box">
+		<view class="head-box">
+			<!-- 标题栏 -->
+			<shopro-navbar>
+				<view class="u-flex-1 u-flex u-col-center u-m-x-20" slot="content">
+					<u-search placeholder="请输入关键字" @change="onSearch" @search="onSearch" @clear="clearSearch" v-model="searchVal" :showAction="false" height="60"></u-search>
+				</view>
+			</shopro-navbar>
+			<!-- 筛选栏 -->
+			<sh-filter @change="onFilter"></sh-filter>
+		</view>
+
+		<view class="u-waterfall u-p-16" v-if="!isEmpty">
+			<view id="u-left-column" class="u-column">
+				<view class="goods-item u-m-b-16 u-flex u-row-center u-col-center" v-for="leftGoods in leftList" :key="leftGoods.id">
+					<shopro-goods-card
+						:detail="leftGoods"
+						:type="leftGoods.activity_type"
+						:image="leftGoods.image"
+						:title="leftGoods.title"
+						:subtitle="leftGoods.subtitle"
+						:price="leftGoods.price"
+						:originPrice="leftGoods.original_price"
+						:sales="leftGoods.sales"
+						:tagTextList="leftGoods.activity_discounts_tags"
+						@click="$Router.push({ path: '/pages/goods/detail', query: { id: leftGoods.id } })"
+					></shopro-goods-card>
+				</view>
+			</view>
+			<view id="u-right-column" class="u-column">
+				<view class="goods-item u-m-b-16 u-flex u-row-center u-col-center" v-for="rightGoods in rightList" :key="rightGoods.id">
+					<shopro-goods-card
+						:detail="rightGoods"
+						:type="rightGoods.activity_type"
+						:image="rightGoods.image"
+						:title="rightGoods.title"
+						:subtitle="rightGoods.subtitle"
+						:price="rightGoods.price"
+						:originPrice="rightGoods.original_price"
+						:sales="rightGoods.sales"
+						:tagTextList="rightGoods.activity_discounts_tags"
+						@click="$Router.push({ path: '/pages/goods/detail', query: { id: rightGoods.id } })"
+					></shopro-goods-card>
+				</view>
+			</view>
+		</view>
+
+		<!-- 缺省页 -->
+		<shopro-empty v-if="isEmpty" :image="$IMG_URL + '/imgs/empty/empty_goods.png'" tipText="暂无该商品,还有更多好货等着你噢~"></shopro-empty>
+		<!-- 加载更多 -->
+		<u-loadmore v-show="!isEmpty" height="80rpx" :status="loadStatus" icon-type="flower" color="#ccc" />
+		<!-- 登录弹窗 -->
+		<shopro-auth-modal></shopro-auth-modal>
+	</view>
+</template>
+
+<script>
+import shFilter from './components/sh-filter.vue';
+import { mapMutations, mapActions, mapState } from 'vuex';
+let systemInfo = uni.getSystemInfoSync();
+let historyTag = uni.getStorageSync('searchHistoryArr') ? JSON.parse(uni.getStorageSync('searchHistoryArr')) : [];
+export default {
+	components: {
+		shFilter
+	},
+	data() {
+		return {
+			isEmpty: false,
+			goodsList: [],
+			searchVal: '',
+			listParams: {
+				category_id: 0,
+				keywords: '',
+				page: 1
+			},
+			loadStatus: 'loadmore', //loadmore-加载前的状态,loading-加载中的状态,nomore-没有更多的状态
+			lastPage: 1,
+
+			// 瀑布流 350-330
+			addTime: 100, //排序间隙时间
+			leftHeight: 0,
+			rightHeight: 0,
+			leftList: [],
+			rightList: [],
+			tempList: []
+		};
+	},
+	// 触底加载更多
+	onReachBottom() {
+		if (this.listParams.page < this.lastPage) {
+			this.listParams.page += 1;
+			this.getGoodsList();
+		}
+	},
+	onLoad() {
+		if (this.$Route.query.id) {
+			this.listParams.category_id = this.$Route.query.id;
+			this.getGoodsList();
+		} else if (this.$Route.query.hasOwnProperty('keywords')) {
+			this.listParams.keywords = this.$Route.query.keywords;
+			this.searchVal = this.$Route.query.keywords;
+			!this.$Route.query.keywords && this.getGoodsList();
+		} else {
+			this.getGoodsList();
+		}
+	},
+	methods: {
+		// 瀑布流相关
+		async splitData() {
+			if (!this.tempList.length) return;
+			let item = this.tempList[0];
+			if (!item) return;
+
+			// 分左右
+			if (this.leftHeight < this.rightHeight) {
+				this.leftHeight += item.activity_discounts_tags.length ? 350 : 330;
+				this.leftList.push(item);
+			} else if (this.leftHeight > this.rightHeight) {
+				this.rightHeight += item.activity_discounts_tags.length ? 350 : 330;
+				this.rightList.push(item);
+			} else {
+				this.leftHeight += item.activity_discounts_tags.length ? 350 : 330;
+				this.leftList.push(item);
+			}
+
+			// 移除临时列表的第一项,如果临时数组还有数据,继续循环
+			this.tempList.splice(0, 1);
+			if (this.tempList.length) {
+				setTimeout(() => {
+					this.splitData();
+				}, this.addTime);
+			}
+		},
+		clear() {
+			this.leftList = [];
+			this.rightList = [];
+			this.leftHeight = 0;
+			this.rightHeight = 0;
+			this.tempList = [];
+		},
+
+		onFilter(e) {
+			this.listParams.order = e;
+			this.goodsList = [];
+			this.listParams.page = 1;
+			this.lastPage = 1;
+			this.clear();
+			this.$u.debounce(this.getGoodsList);
+		},
+		// 键盘搜索,输入搜索
+		onSearch() {
+			this.goodsList = [];
+			this.listParams.page = 1;
+			this.lastPage = 1;
+			this.listParams.keywords = this.searchVal;
+			this.clear();
+			this.$u.debounce(this.getGoodsList);
+		},
+
+		// 队列
+		getArr(list, item) {
+			let arr = list;
+			let length = 10; //队列长度
+			arr.length < length ? arr.unshift(item) : arr.pop();
+			return arr;
+		},
+
+		// 清除搜索框
+		clearSearch() {
+			this.searchVal = '';
+			this.clear();
+		},
+		// 商品列表
+		getGoodsList() {
+			let that = this;
+			that.loadStatus = 'loading';
+			that.$http('goods.lists', that.listParams, '加载中...').then(res => {
+				if (this.searchVal && !historyTag.includes(this.searchVal)) {
+					let searchHistoryArr = JSON.stringify(this.getArr(historyTag, this.searchVal));
+					uni.setStorageSync('searchHistoryArr', searchHistoryArr);
+				}
+				if (res.code === 1) {
+					that.goodsList = [...that.goodsList, ...res.data.data];
+					that.isEmpty = !that.goodsList.length;
+					that.lastPage = res.data.last_page;
+					that.loadStatus = that.listParams.page < res.data.last_page ? 'loadmore' : 'nomore';
+					that.tempList = res.data.data;
+					that.splitData();
+				}
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+@mixin vue-flex($direction: row) {
+	/* #ifndef APP-NVUE */
+	display: flex;
+	flex-direction: $direction;
+	/* #endif */
+}
+.u-waterfall {
+	@include vue-flex;
+	flex-direction: row;
+	align-items: flex-start;
+}
+
+.u-column {
+	@include vue-flex;
+	flex: 1;
+	flex-direction: column;
+	height: auto;
+}
+.head-box {
+	position: -webkit-sticky;
+	position: sticky;
+	top: 0;
+	z-index: 998;
+	background: #fff;
+}
+</style>

+ 371 - 0
pages/index/cart.vue

@@ -0,0 +1,371 @@
+<!-- 购物车 -->
+<template>
+	<view class="page_box">
+		<!-- 总数 -->
+		<view class="head_box" v-show="!isEmpty">
+			<view class="tool-box u-flex u-row-between">
+				<view class="count-box">
+					共
+					<text class="all-num">{{ cartList.length }}</text>
+					件商品
+				</view>
+				<button class="u-reset-button set-btn" @tap="isTool = !isTool">{{ isTool ? '完成' : '编辑' }}</button>
+			</view>
+		</view>
+
+		<view class="content_box">
+			<!-- 列表 -->
+			<u-checkbox-group @change="checkboxGroupChange" activeColor="#e9b461" v-if="!isEmpty">
+				<view class="collect-list u-flex u-row-left u-col-center" v-for="(g, index) in cartList" :key="index">
+					<u-checkbox class="u-p-l-10" :name="g.goods_id" shape="circle" v-model="g.checked">
+						<view style="height: 160rpx"></view>
+					</u-checkbox>
+					<view class="goods-wrap">
+						<view class="lose-box"
+							v-if="g.cart_type === 'invalid' || (g.cart_type === 'activity' && !isActivityPay)">
+							<text v-if="g.cart_type === 'invalid'" class="iconfont icon-yishixiao"></text>
+							<view v-if="g.cart_type === 'activity' && !isActivityPay"
+								class="invalid-tips u-flex u-row-center u-col-center">活动商品,仅支持单独购买</view>
+						</view>
+						<shopro-mini-card :image="g.goods.image" :title="g.goods.title"
+							@click="$Router.push({ path: '/pages/goods/detail', query: { id: g.goods.id } })">
+							<template #describe>
+								<view class="order-sku u-ellipsis-1">
+									<text
+										class="order-num">{{ g.sku_price && g.sku_price.goods_sku_text ? g.sku_price.goods_sku_text : '' }}</text>
+								</view>
+							</template>
+							<template #cardBottom>
+								<view class="order-price-box u-flex u-row-between" @tap.stop>
+									<text
+										class="order-price font-OPPOSANS">¥{{ g.sku_price ? g.sku_price.price : 0 }}</text>
+									<u-number-box :value="g.goods_num" :long-press="false" :min="0" :step="1"
+										:index="index" :max="!g.sku_price ? 0: (g.sku_price.stock > 999 ? 999 : g.sku_price.stock)" @min="onMin(g)"
+										@minus="changeNum($event, g)" @plus="changeNum($event, g)"
+										@change="changeNum($event, g)">
+									</u-number-box>
+								</view>
+							</template>
+						</shopro-mini-card>
+					</view>
+				</view>
+			</u-checkbox-group>
+
+			<!-- 数据为空 -->
+			<shopro-empty v-if="isEmpty" :image="$IMG_URL + '/imgs/empty/empty_cart.png'" tipText="购物车空空如也,快去逛逛吧~">
+			</shopro-empty>
+		</view>
+
+		<!-- 底部按钮 -->
+		<view class="foot_box " v-show="!isEmpty">
+			<view class="tools-box u-flex u-row-between">
+				<u-checkbox @change="onAllSel" activeColor="#e9b461" shape="circle" :value="allSelected">
+					全选({{ totalCount.totalNum }})</u-checkbox>
+				<view class="u-flex">
+					<view class="price font-OPPOSANS" v-show="!isTool">¥{{ totalCount.totalPrice.toFixed(2) }}</view>
+					<button class="u-reset-button pay-btn" :disabled="!isSel" v-show="!isTool" @tap="onPay">结算</button>
+					<button class="u-reset-button del-btn" v-show="isTool" @tap="goodsDelete">删除</button>
+				</view>
+			</view>
+		</view>
+		<!-- <shopro-tabbar></shopro-tabbar> -->
+	</view>
+</template>
+
+<script>
+	import { mapMutations, mapActions, mapState, mapGetters } from 'vuex';
+	let timer = null;
+	export default {
+		components: {},
+		data() {
+			return {
+				maxStep: 999,
+				isTool: false
+			};
+		},
+		computed: {
+			...mapGetters(['totalCount', 'isSel', 'isActivityPay', 'cartList', 'allSelected', 'authType', 'isLogin']),
+			isEmpty() {
+				return !this.cartList.length;
+			}
+		},
+		onShow() {
+			this.isLogin && this.getCartList();
+		},
+		onHide() {
+			this.isTool = false;
+		},
+		methods: {
+			...mapActions(['getCartList', 'changeCartList']),
+
+			// 到达最小值
+			onMin(g) {
+				uni.showModal({
+					title: '删除提示',
+					confirmColor: '#f0c785',
+					content: `是否确认从购物车中删除此商品?`,
+					success: res => {
+						res.confirm && this.changeCartList({ ids: [g.id], art: 'delete' });
+					}
+				});
+			},
+			// 更改商品数
+			async changeNum(e, g) {
+				uni.showLoading({
+					mask: true
+				});
+				e.value > 0 && this.changeCartList({ ids: [g.id], goodsNum: e.value, art: 'change' });
+				uni.hideLoading();
+				await this.getCartList();
+			},
+
+			// 单选
+			checkboxGroupChange(e) {
+				this.$store.commit('checkCartList');
+			},
+
+			// 路由跳转
+			jump(path, parmas) {
+				this.$Router.push({
+					path: path,
+					query: parmas
+				});
+			},
+
+			// 全选
+			onAllSel() {
+				let that = this;
+				that.$store.commit('changeAllSellect'); //按钮切换全选。
+				that.$store.commit('getAllSellectCartList', that.allSelected); //列表全选
+			},
+
+			// 结算
+			onPay() {
+				let that = this;
+				let { cartList } = this;
+				if (this.isSel) {
+					let confirmcartList = [];
+					let isActivity = false;
+					for (let item of this.cartList) {
+						if (item.checked) {
+							if (item.cart_type === 'invalid') {
+								this.$u.toast('商品已失效');
+								return false;
+							}
+							if (item.cart_type === 'activity') {
+								isActivity = true;
+							}
+							confirmcartList.push({
+								goods_id: item.goods_id,
+								sku_price_id: item.sku_price_id,
+								goods_price: item.sku_price ? item.sku_price.price : 0,
+								goods_num: item.goods_num
+							});
+						}
+					}
+					if (confirmcartList.length > 1 && isActivity) {
+						this.$u.toast('活动商品只能单独购买');
+						return false;
+					}
+					that.jump('/pages/order/confirm', { goodsList: confirmcartList, from: 'cart' });
+				}
+			},
+			// 删除
+			goodsDelete() {
+				let that = this;
+				let { cartList } = this;
+				let selectedIdsArray = [];
+				cartList.map(item => {
+					if (item.checked) {
+						selectedIdsArray.push(item.id);
+					}
+				});
+				this.changeCartList({ ids: selectedIdsArray, art: 'delete' });
+			}
+		}
+	};
+</script>
+
+<style lang="scss">
+	// 总计商品
+	.head_box {
+		.tool-box {
+			height: 90rpx;
+			padding: 0 30rpx;
+			background: #f7f5f6;
+
+			.count-box {
+				font-size: 26rpx;
+				color: #999;
+
+				.all-num {
+					color: #a8700d;
+				}
+			}
+
+			.set-btn {
+				background: none;
+				font-size: 26rpx;
+				color: #a8700d;
+			}
+		}
+	}
+
+	// 空白页
+	.empty-box {
+		flex: 1;
+		width: 100%;
+		height: 100%;
+	}
+
+	// 购物车项
+	.collect-list {
+		background: #fff;
+		width: 750rpx;
+		margin-bottom: 20rpx;
+		padding: 20rpx 10rpx;
+
+		// 商品卡片包裹
+		.goods-wrap {
+			position: relative;
+
+			.order-sku {
+				font-size: 24rpx;
+				font-weight: 400;
+				color: rgba(153, 153, 153, 1);
+				width: 440rpx;
+				margin-bottom: 20rpx;
+
+				.order-num {
+					margin-right: 10rpx;
+				}
+			}
+
+			.order-price-box {
+				.status-btn {
+					height: 32rpx;
+					border: 1rpx solid rgba(207, 169, 114, 1);
+					border-radius: 15rpx;
+					font-size: 20rpx;
+					font-weight: 400;
+					color: rgba(168, 112, 13, 1);
+					padding: 0 10rpx;
+					margin-left: 20rpx;
+					background: rgba(233, 183, 102, 0.16);
+				}
+
+				.order-price {
+					font-size: 26rpx;
+					font-weight: 600;
+					color: #ff0000;
+				}
+			}
+
+			.lose-box {
+				position: absolute;
+				z-index: 10;
+				width: 100%;
+				height: 100%;
+				background-color: rgba(#fff, 0.8);
+
+				.icon-yishixiao {
+					position: absolute;
+					bottom: 0rpx;
+					right: 80rpx;
+					font-size: 140rpx;
+					line-height: 140rpx;
+					color: #dbdbdb;
+					transform: rotate(-30deg);
+				}
+
+				.invalid-tips {
+					position: absolute;
+					top: 0;
+					right: 0;
+					left: 0;
+					bottom: 0;
+					margin: auto;
+					width: 400rpx;
+					height: 60rpx;
+					border-radius: 30rpx;
+					color: #fff;
+					background-color: rgba(#000, 0.35);
+				}
+			}
+		}
+
+		.tag-box {
+			.tag1 {
+				line-height: 36rpx;
+				padding: 0 8rpx;
+				font-size: 18rpx;
+				color: rgba(#fff, 0.9);
+				background: #f39800;
+				display: inline-block;
+				box-sizing: border-box;
+			}
+
+			.tag2 {
+				line-height: 34rpx;
+				padding: 0 8rpx;
+				font-size: 18rpx;
+				color: rgba(#f39800, 0.9);
+				background: #fff;
+				border-top: 1rpx solid #f39800;
+				border-right: 1rpx solid #f39800;
+				border-bottom: 1rpx solid #f39800;
+				display: inline-block;
+				box-sizing: border-box;
+			}
+		}
+
+		.price-box {
+			width: 420rpx;
+
+			.price {
+				color: #e1212b;
+			}
+		}
+	}
+
+	.tools-box {
+		height: 100rpx;
+		width: 750rpx;
+		padding: 0 20rpx;
+		background: #fff;
+
+		.check-all {
+			font-size: 26rpx;
+
+			.check-all-radio {
+				transform: scale(0.7);
+				color: #e9b564;
+			}
+		}
+
+		.price {
+			color: #ff0000;
+			font-size: 500;
+			margin-right: 30rpx;
+		}
+
+		.pay-btn {
+			width: 200rpx;
+			line-height: 70rpx;
+			background: linear-gradient(90deg, rgba(233, 180, 97, 1), rgba(238, 204, 137, 1));
+			box-shadow: 0px 7rpx 6rpx 0px rgba(229, 138, 0, 0.22);
+			border-radius: 35rpx;
+			padding: 0;
+			color: rgba(#fff, 0.9);
+		}
+
+		.del-btn {
+			width: 200rpx;
+			line-height: 70rpx;
+			background: linear-gradient(90deg, rgba(244, 71, 57, 1) 0%, rgba(255, 102, 0, 1) 100%);
+			border-radius: 35rpx;
+			padding: 0;
+			color: rgba(#fff, 0.9);
+		}
+	}
+</style>

+ 73 - 0
pages/index/category.vue

@@ -0,0 +1,73 @@
+<!-- 分类 -->
+<template>
+	<view class="category-box">
+		<!-- 三级分类 -->
+		<three-catgory :categoryStyleId="categoryStyleId" v-if="categoryType === 4"></three-catgory>
+		<!-- 二级分类 -->
+		<two-catgory :categoryStyleId="categoryStyleId" v-if="categoryType === 3"></two-catgory>
+		<!-- 一级分类-->
+		<one-catgory :categoryStyleId="categoryStyleId" v-if="categoryType === 2"></one-catgory>
+		<!--直接购买,点餐 -->
+		<takeout-catgory :categoryStyleId="categoryStyleId" v-if="categoryType === 1"></takeout-catgory>
+		<!-- 登录提示 -->
+		<shopro-auth-modal v-if="authType"></shopro-auth-modal>
+		<!-- <shopro-tabbar></shopro-tabbar> -->
+	</view>
+</template>
+
+<script>
+import takeoutCatgory from './category/takeout-catgory.vue';
+import threeCatgory from './category/three-catgory.vue';
+import twoCatgory from './category/two-catgory.vue';
+import oneCatgory from './category/one-catgory.vue';
+import { mapMutations, mapActions, mapState, mapGetters } from 'vuex';
+export default {
+	components: {
+		takeoutCatgory,
+		threeCatgory,
+		twoCatgory,
+		oneCatgory
+	},
+	data() {
+		return {
+			categoryType: 0, //1:快速购买,2:一级分类,3:二级分类,4:三级分类
+			categoryStyleId: 0 //分类Id
+		};
+	},
+	computed: {
+		...mapGetters(['authType'])
+	},
+	onLoad() {
+		this.getCategory();
+	},
+	methods: {
+		/**
+		 * 获取分类数据
+		 *  type4:三级分类, type3:二级分类 ,type2:一级分类,type1:快速购买
+		 */
+		getCategory() {
+			this.$http('category.info', {
+				id: this.$Route.query.id ? this.$Route.query.id : 0
+			}).then(res => {
+				if (res.code === 1) {
+					if (res.data?.type) {
+						this.categoryType = Number(res.data.type);
+						this.categoryStyleId = Number(res.data.id);
+						uni.setNavigationBarTitle({
+							title: res.data?.name
+						});
+					}
+				}
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+.category-box {
+	height: 100%;
+	flex: 1;
+	overflow: hidden;
+}
+</style>

+ 265 - 0
pages/index/category/one-catgory.vue

@@ -0,0 +1,265 @@
+<template>
+	<view class="content_box">
+		<view class="u-flex u-col-center wrapper-box">
+			<view class="scroll-box" style="background-color: #F6F6F6;">
+				<scroll-view class="left u-flex-col u-col-center" enable-flex enable-back-to-top scroll-y>
+					<view class="type-list u-ellipsis-1" :class="[{ 'list-active': listId == index }]" v-for="(item, index) in categoryData" :key="index" @tap="onType(index)">
+						<view class="line" :class="[{ 'line-active': listId == index }]"></view>
+						{{ item.name }}
+					</view>
+					<view class="hack-tabbar"></view>
+				</scroll-view>
+			</view>
+			<view style="height: 100%;width: 100%;">
+				<scroll-view @scrolltolower="loadMore" scroll-y class="scroll-box" enable-back-to-top scroll-with-animation>
+					<view class="right" v-if="categoryData.length">
+						<image class="type-img" v-if="categoryData[listId].image" :src="categoryData[listId].image" mode="aspectFill"></image>
+						<view class="item-list">
+							<view class="item-box u-flex">
+								<view
+									class="u-flex-col u-col-center goods-item"
+									@tap="jump('/pages/goods/detail', { id: goods.id })"
+									v-for="(goods, index1) in goodsList"
+									:key="goods.id"
+								>
+									<image class="item-img" lazy-load :src="goods.image" mode="aspectFill"></image>
+									<text class="item-title u-ellipsis-1 ">{{ goods.title }}</text>
+									<view class="item-price">{{ goods.price }}</view>
+								</view>
+							</view>
+						</view>
+
+						<!-- 缺省页 -->
+						<shopro-empty
+							v-if="isEmpty"
+							marginTop="200rpx"
+							:image="$IMG_URL + '/imgs/empty/empty_goods.png'"
+							tipText="暂无该商品,还有更多好货等着你噢~"
+						></shopro-empty>
+
+						<!-- 加载更多 -->
+						<u-loadmore v-if="!isEmpty" height="80rpx" :status="loadStatus" icon-type="flower" color="#ccc" />
+						<view class="hack-tabbar"></view>
+					</view>
+				</scroll-view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+import { mapMutations, mapActions, mapState, mapGetters } from 'vuex';
+export default {
+	components: {},
+	data() {
+		return {
+			listId: 0,
+			categoryData: {},
+			categoryID: 0, //分类id
+			isEmpty: false,
+			currentPage: 1,
+			lastPage: 1,
+			loadStatus: 'loadmore', //loadmore-加载前的状态,loading-加载中的状态,nomore-没有更多的状态
+			goodsList: [] //商品数据
+		};
+	},
+	props: {
+		categoryStyleId: {
+			//分类样式ID
+			type: Number,
+			default: 0
+		}
+	},
+	computed: {},
+	created() {
+		console.log('%c当前分类:一级分类', 'color:green;background:yellow');
+		this.getCategory();
+	},
+
+	methods: {
+		// 获取分类
+		getCategory() {
+			this.$http('category.detail', {
+				id: this.categoryStyleId
+			}).then(res => {
+				if (res.code === 1) {
+					this.categoryData = res.data.children;
+					this.categoryID = res.data.children[0].id;
+					this.getGoodsList();
+				}
+			});
+		},
+
+		// 获取分类商品
+		getGoodsList() {
+			let that = this;
+			that.$http('goods.lists', {
+				category_id: that.categoryID,
+				page: that.currentPage
+			}, '加载中...').then(res => {
+				if (res.code === 1) {
+					that.goodsList = [...that.goodsList, ...res.data.data];
+					that.isEmpty = !that.goodsList.length;
+					that.lastPage = res.data.last_page;
+					that.loadStatus = that.currentPage < res.data.last_page ? 'loadmore' : 'nomore';
+				}
+			});
+		},
+
+		// 商品底部
+		loadMore() {
+			if (this.currentPage < this.lastPage) {
+				this.currentPage += 1;
+				this.getGoodsList();
+			}
+		},
+
+		onType(index) {
+			this.listId = index;
+			this.categoryID = this.categoryData[index].id;
+			this.goodsList = [];
+			this.currentPage = 1;
+			this.getGoodsList();
+		},
+		// 路由跳转
+		jump(path, parmas) {
+			this.$Router.push({
+				path: path,
+				query: parmas
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+.hack-tabbar {
+	height: calc(100rpx + env(safe-area-inset-bottom) / 2);
+	width: 100%;
+}
+.content_box {
+	margin-top: 1upx;
+	display: flex;
+	flex-direction: column;
+	overflow: hidden;
+	height: 100%;
+}
+
+.wrapper-box {
+	flex: 1;
+	margin-top: 1upx;
+	height: 100%;
+}
+
+.scroll-box {
+	height: 100%;
+	flex: 1;
+	background: #fff;
+}
+
+.left {
+	width: 200upx;
+	height: 100%;
+	flex: 1;
+
+	.list-active {
+		background: #fff;
+		color: #333333 !important;
+		font-weight: bold !important;
+	}
+
+	.line-active {
+		background: #e6b873;
+	}
+
+	.type-list {
+		height: 84upx;
+		position: relative;
+		width: 200rpx;
+		padding-left: 16rpx;
+		line-height: 84rpx;
+		font-size: 28upx;
+		font-weight: 400;
+		color: rgba(102, 102, 102, 1);
+
+		.line {
+			width: 10upx;
+			height: 100%;
+			position: absolute;
+			left: 0;
+		}
+	}
+}
+
+.right {
+	padding: 0 30upx;
+	flex: 1;
+	height: 100%;
+
+	.type-img {
+		width: 505rpx;
+		height: 150rpx;
+		background: #ccc;
+		margin: 20rpx 0;
+		border-radius: 10rpx;
+	}
+
+	.item-list {
+		.type-box {
+			height: 84rpx;
+			.type-title {
+				font-size: 28rpx;
+				font-weight: bold;
+			}
+			.more {
+				font-size: 26rpx;
+				color: #999;
+			}
+		}
+
+		.item-box {
+			flex-wrap: wrap;
+			width: 504rpx;
+			.goods-item {
+				margin-right: 12rpx;
+				margin-bottom: 12rpx;
+				background: #ffffff;
+				box-shadow: 0px 0px 20rpx 4rpx rgba(199, 199, 199, 0.22);
+				border-radius: 10rpx;
+
+				&:nth-child(2n) {
+					margin-right: 0;
+				}
+
+				.item-img {
+					width: 245rpx;
+					height: 246rpx;
+					border-radius: 10rpx 10rpx 0px 0px;
+					background: #f5f5f5;
+				}
+
+				.item-title {
+					font-size: 24rpx;
+					line-height: 24rpx;
+					margin-top: 10rpx;
+					width: 200rpx;
+					text-align: left;
+					margin: 20rpx;
+				}
+				.item-price {
+					font-size: 28rpx;
+					font-weight: 500;
+					color: #ff3000;
+					text-align: left;
+					width: 200rpx;
+					margin: 0 20rpx 20rpx;
+					&::before {
+						content: '¥';
+						font-size: 22rpx;
+					}
+				}
+			}
+		}
+	}
+}
+</style>

+ 603 - 0
pages/index/category/takeout-catgory.vue

@@ -0,0 +1,603 @@
+<template>
+	<view class="catgory-wrap">
+		<view class="u-flex wrapper-box">
+			<!-- 左侧分类列表 -->
+			<scroll-view enable-flex scroll-y scroll-with-animation class="u-tab-view menu-scroll-view"
+				:scroll-top="scrollLeftTop">
+				<view v-for="(item, index) in tabbarList" :key="index" class="u-tab-item u-ellipsis-1"
+					:class="[currentTab == index ? 'u-tab-item-active' : '']" :data-current="index"
+					@tap.stop="swichMenu(index)">
+					<text class="menu-name">{{ item.name }}</text>
+				</view>
+				<view class="hack-tabbar"></view>
+			</scroll-view>
+
+			<!-- 右侧商品列表 -->
+			<view class="right-wrap ">
+				<scroll-view scroll-y class="right-box menu-scroll-view" @scrolltolower="loadMore"
+					v-if="tabbarList.length">
+					<view class="u-flex u-row-center" v-if="tabbarList[currentTab].image">
+						<image class="category-img" :src="tabbarList[currentTab].image" mode="aspectFill"></image>
+					</view>
+					<view class="category-title">{{ tabbarList[currentTab].name }}</view>
+
+					<view class="sh-big-card-wrap u-m-20 " v-for="(item, index) in goodsList" :key="item.id">
+						<view class="big-goods-card" @tap="jump('/pages/goods/detail', { id: item.id })">
+							<view class="img-wrap u-m-b-20">
+								<image class="goods-img" :src="item.image" mode="aspectFill"></image>
+								<image class="goods-tag" v-if="item.is_hot && !item.activity"
+									:src="$IMG_URL + '/goods/goods_hot_tag.png'" mode=""></image>
+							</view>
+							<view class="goods-title u-p-x-20 u-m-b-10 u-ellipsis-2">
+								<view v-if="item.activity_type" class=" sm cu-tag radius title-tag u-m-r-10"
+									:style="{ backgroundColor: typeMap[item.activity_type].tagBg, color: '#fff' }">
+									{{ typeMap[item.activity_type].text }}
+								</view>
+								{{ item.title }}
+							</view>
+							<view class="goods-subtitle u-p-x-20 u-m-b-10 u-ellipsis-1">{{ item.subtitle }}</view>
+							<!-- 满减,满折 -->
+							<view class="u-p-x-20" v-if="item.activity_discounts_tags.length">
+								<view class="tag-box u-m-b-10 u-m-r-10"
+									v-for="(tag, index1) in item.activity_discounts_tags" :key="index1">{{ tag }}</view>
+							</view>
+							<view class="goods-progress u-p-x-20 u-flex u-m-b-10">
+								<view style="width:230rpx;">
+									<u-line-progress height="16" :show-percent="false" :percent="Number(item.percent)"
+										active-color="#e6b873"></u-line-progress>
+								</view>
+								<view class="googs-send-num u-m-l-20">已售{{ item.sales }}件</view>
+							</view>
+							<view class="u-flex u-row-between u-p-x-20">
+								<view class="price-box u-flex font-OPPOSANS">
+									<view class="price">{{ item.price }}</view>
+									<view class="origin-price u-m-l-10">¥{{ item.original_price }}</view>
+								</view>
+								<view class="cart-box">
+									<!-- 单规格 -->
+									<view class="" v-if="!item.activity">
+										<view v-if="!item.is_sku">
+											<button v-if="!isCart(item.id)"
+												:style="item.activity_type ? typeMap[item.activity_type].btnBg : ''"
+												@tap.stop="addCart(item.sku_price[0])" class="u-reset-button cart-btn">
+												加入购物车
+											</button>
+											<view class="num-step" @tap.stop v-else>
+												<u-number-box :value="checkCart[item.id].num" :long-press="false"
+													:min="0" :max="maxStep" :step="1" :index="index"
+													@min="onMin(item.id)" @plus="plus($event, item.sku_price[0])"
+													@change="onChangeNum($event, item.sku_price[0])"></u-number-box>
+											</view>
+										</view>
+										<!-- 多规格 -->
+										<button class="u-reset-button item-btn sel-sku "
+											:style="item.activity_type ? typeMap[item.activity_type].btnBg : ''"
+											@tap.stop="selSku(item)" v-else>
+											选规格
+										</button>
+									</view>
+									<button class="u-reset-button item-btn sel-sku"
+										:style="item.activity_type ? typeMap[item.activity_type].btnBg : ''"
+										v-else>立即抢购</button>
+								</view>
+							</view>
+						</view>
+					</view>
+
+					<!-- 缺省页 -->
+					<shopro-empty v-if="isEmpty" marginTop="200rpx" :image="$IMG_URL + '/imgs/empty/empty_goods.png'"
+						tipText="暂无该商品,还有更多好货等着你噢~"></shopro-empty>
+
+					<!-- 加载更多 -->
+					<u-loadmore v-if="goodsList.length" height="80rpx" :status="loadStatus" icon-type="flower"
+						color="#ccc" />
+					<view class="hack-tabbar"></view>
+				</scroll-view>
+			</view>
+		</view>
+
+		<!-- 规格弹窗 -->
+		<shopro-sku v-if="showSku && goodsInfo.id" v-model="showSku" :goodsInfo="goodsInfo" :buyType="'cart'">
+		</shopro-sku>
+	</view>
+</template>
+
+<script>
+	import { mapMutations, mapActions, mapState, mapGetters } from 'vuex';
+	export default {
+		components: {},
+		data() {
+			return {
+				showSku: false, //是否显示规格弹窗
+				isEmpty: false,
+				currentTab: 0,
+				categoryID: 0, //分类id
+				scrollLeftTop: 0, //左边滚动距离
+				menuHeight: 0, // 左边菜单的高度
+				menuItemHeight: 0, // 左边菜单item的高度
+				tabbarList: [], //左侧分类列表
+				goodsList: [], //商品列表
+				goodsInfo: {}, //点击商品详情
+				numberDisabled: false, //购物车计数器
+				loadStatus: 'loadmore', //loadmore-加载前的状态,loading-加载中的状态,nomore-没有更多的状态
+				currentPage: 1,
+				lastPage: 1,
+				maxStep: 999,
+				typeMap: {
+					seckill: {
+						text: '秒杀',
+						tagBg: '#FF5854',
+						goodsBg: '/imgs/tag/seckill_x_bg.png',
+						btnText: '去抢购',
+						btnBg: 'background: linear-gradient(90deg, #D01325, #ED3C30);'
+					},
+					groupon: {
+						text: '拼团',
+						tagBg: '#FE832A',
+						goodsBg: '/imgs/tag/groupon_x_bg.png',
+						btnText: '马上拼',
+						btnBg: 'background: linear-gradient(90deg, #FF6600 0%, #FE832A 100%);'
+					}
+				}
+			};
+		},
+		props: {
+			categoryStyleId: {
+				type: Number,
+				default: 0
+			}
+		},
+		computed: {
+			...mapGetters(['totalCount', 'isSel', 'cartNum', 'cartList', 'isLogin']),
+			// 购物车检测
+			checkCart() {
+				let obj = {};
+				this.cartList.forEach(item => {
+					obj[item.goods_id] = {
+						num: item.goods_num,
+						cartOrderId: item.id
+					};
+				});
+				return obj;
+			}
+		},
+		async created() {
+			console.log('%c当前分类:快速购买', 'color:green;background:yellow');
+			await this.getCategory();
+			let res = await this.getGoodsList();
+		},
+
+		methods: {
+			...mapActions(['getCartList', 'changeCartList', 'addCartGoods']),
+			// 跳转详情
+			toGoodDetail(id) {
+				this.$Router.push({ path: '/pages/goods/detail', query: { id: id } });
+			},
+
+			// 百分比
+			getProgress(sales, stock) {
+				let unit = '';
+				if (stock + sales > 0) {
+					let num = (sales / (sales + stock)) * 100;
+					unit = num.toFixed(2) + '%';
+				} else {
+					unit = '0%';
+				}
+				return unit;
+			},
+
+			// 加载更多
+			loadMore() {
+				if (this.currentPage < this.lastPage) {
+					this.currentPage += 1;
+					this.getGoodsList();
+				}
+			},
+
+			// 获取分类
+			async getCategory() {
+				const tabbarData = await this.$http('category.detail', {
+					id: this.categoryStyleId
+				});
+				this.tabbarList = tabbarData.data.children;
+				this.categoryID = this.tabbarList[0]?.id;
+			},
+
+			// 获取分类商品
+			getGoodsList() {
+				let that = this;
+				that.loadStatus = 'loading';
+				that.$http(
+						'goods.lists', {
+							category_id: that.categoryID,
+							page: that.currentPage
+						},
+						'加载中...'
+					)
+					.then(res => {
+						if (res.code === 1) {
+							that.goodsList = [...that.goodsList, ...res.data.data];
+							that.goodsList.map(item => {
+								item.percent = item.stock + item.sales > 0 ? ((item.sales / (item.sales + item
+									.stock)) * 100).toFixed(2) : 0;
+							});
+							that.isEmpty = !that.goodsList.length;
+							that.lastPage = res.data.last_page;
+							that.loadStatus = that.currentPage < res.data.last_page ? 'loadmore' : 'nomore';
+						}
+					})
+					.then(() => {
+						that.isLogin && that.getCartList();
+					});
+			},
+
+			// 点击左边的栏目切换
+			async swichMenu(index) {
+				if (index == this.currentTab) return;
+				this.currentTab = index;
+				this.categoryID = this.tabbarList[index].id;
+				this.currentPage = 1;
+				this.lastPage = 1;
+				this.goodsList = [];
+				await this.getGoodsList();
+				// 如果为0,意味着尚未初始化
+				if (this.menuHeight == 0 || this.menuItemHeight == 0) {
+					await this.getElRect('menu-scroll-view', 'menuHeight');
+					await this.getElRect('u-tab-item', 'menuItemHeight');
+				}
+				// 将菜单菜单活动item垂直居中
+				this.scrollTop = index * this.menuItemHeight + this.menuItemHeight / 2 - this.menuHeight / 2;
+			},
+
+			// 获取一个目标元素的高度
+			getElRect(elClass, dataVal) {
+				new Promise((resolve, reject) => {
+					const query = uni.createSelectorQuery().in(this);
+					query
+						.select('.' + elClass)
+						.fields({ size: true }, res => {
+							// 如果节点尚未生成,res值为null,循环调用执行
+							if (!res) {
+								setTimeout(() => {
+									this.getElRect(elClass);
+								}, 10);
+								return;
+							}
+							this[dataVal] = res.height;
+						})
+						.exec();
+				});
+			},
+
+			// 加入购物车
+			addCart(goods) {
+				let obj = {
+					goods_id: goods.goods_id,
+					goods_num: 1,
+					sku_price_id: goods.id,
+					goods_price: goods.price
+				};
+				let confirmGoodsList = {
+					list: [obj],
+					from: 'goods'
+				};
+				this.addCartGoods(confirmGoodsList).then(res => {
+					if (res.code === 1) {
+						this.$u.toast(res.msg);
+					}
+				});
+			},
+			// 检测商品在购物车中的下标
+			checkGoodsIndex(id) {
+				let cIndex = 0;
+				this.cartList.forEach((item, index) => {
+					if (id == item.goods_id) {
+						cIndex = index;
+					}
+				});
+				return cIndex;
+			},
+
+			// 到达最小值
+			onMin(goodsId) {
+				const that = this;
+				let cartGoodId = 0;
+				cartGoodId = this.cartList.filter(item => item.goods_id === goodsId)[0].id;
+				uni.showModal({
+					title: '删除提示',
+					confirmColor: '#f0c785',
+					content: `是否确认从购物车中删除此商品?`,
+					success: res => {
+						res.confirm && this.changeCartList({ ids: [cartGoodId], art: 'delete' });
+					}
+				});
+			},
+
+			// 增加
+			plus(e, sku) {
+				if (e.value >= sku.stock) {
+					this.maxStep = sku.stock > 999 ? 999 : sku.stock;
+					this.$u.toast('库存不足');
+					return;
+				}
+				if (this.detail.activity_type === 'seckill' || this.detail.activity_type === 'groupon') {
+					let rules = this.detail.activity.rules;
+					if (rules.limit_buy != 0 && e.value >= rules.limit_buy) {
+						this.maxStep = rules.limit_buy;
+						this.$u.toast('本次活动最多购买' + rules.limit_buy + '件');
+						return;
+					}
+				}
+			},
+
+			// 更改商品数
+			// 更改商品数
+			async onChangeNum(e, sku) {
+				let gIndex = this.checkGoodsIndex(sku.goods_id);
+				if (e.value != this.checkCart[sku.goods_id].num) {
+					uni.showLoading({
+						mask: true
+					});
+					this.$set(this.cartList[gIndex], 'goods_num', +e.value);
+					await this.changeCartList({
+						ids: [this.checkCart[sku.goods_id].cartOrderId],
+						goodsNum: +e.value,
+						art: 'change'
+					});
+					await uni.hideLoading();
+				}
+			},
+			// 检测是否为购物车商品
+			isCart(id) {
+				let goodsId = id + '';
+				return Object.keys(this.checkCart).includes(goodsId);
+			},
+
+			// 添加购物车
+			async selSku(info) {
+				this.goodsInfo = {};
+				this.getGoodsDetail(info.id);
+				this.showSku = true;
+			},
+			// 商品详情
+			getGoodsDetail(id) {
+				let that = this;
+				that.$http('goods.detail', {
+					id: id
+				}).then(res => {
+					if (res.code === 1) {
+						that.goodsInfo = res.data;
+					}
+				});
+			},
+
+			// 路由跳转
+			jump(path, parmas) {
+				this.$Router.push({
+					path: path,
+					query: parmas
+				});
+			}
+		}
+	};
+</script>
+
+<style lang="scss" scoped>
+	.hack-tabbar {
+		height: calc(100rpx + env(safe-area-inset-bottom) / 2);
+		width: 100%;
+	}
+
+	// 最外层结构包裹
+	.catgory-wrap {
+		height: calc(100vh);
+		/* #ifdef H5 */
+		height: calc(100vh - var(--window-top));
+		/* #endif */
+		display: flex;
+		flex-direction: column;
+	}
+
+	.wrapper-box {
+		flex: 1;
+		display: flex;
+		overflow: hidden;
+	
+	// 左侧menu
+		.u-tab-view {
+			width: 200rpx;
+			height: 100%;
+
+			.u-tab-item {
+				height: 84rpx;
+				background: #f6f6f6;
+				width: 200rpx;
+				padding-left: 16rpx;
+				font-size: 26rpx;
+				color: #444;
+				font-weight: 400;
+				line-height: 84rpx;
+			}
+
+			.u-tab-item-active {
+				position: relative;
+				color: #000;
+				font-size: 30rpx;
+				font-weight: bold;
+				background: #fff;
+			}
+
+			.u-tab-item-active::before {
+				content: '';
+				position: absolute;
+				border-left: 4px solid #e6b873;
+				height: 32rpx;
+				left: 0;
+				top: 26rpx;
+			}
+		}
+	}
+
+	// 右侧商品滚动
+	.right-wrap {
+		background-color: #fff;
+		height: 100%;
+		width: 100%;
+
+		.category-img {
+			width: 505rpx;
+			height: 150rpx;
+			background: #ccc;
+			margin: 20rpx auto;
+			border-radius: 10rpx;
+		}
+
+		.category-title {
+			font-size: 30rpx;
+			font-weight: bold;
+			color: #000;
+			margin: 20rpx;
+		}
+
+		.right-box {
+			height: 100%;
+		}
+	}
+
+	.big-goods-card {
+		background-color: #fff;
+		border-radius: 20rpx;
+		width: 100%;
+		box-shadow: 0 0 8rpx 2rpx rgba(199, 199, 199, 0.3);
+		padding-bottom: 20rpx;
+
+		.img-wrap {
+			position: relative;
+			overflow: hidden;
+			height: 256rpx;
+			width: 100%;
+
+			.goods-img {
+				width: 100%;
+				height: 256rpx;
+				border-radius: 20rpx 20rpx 0 0;
+				background: #f5f5f5;
+			}
+
+			.goods-tag {
+				position: absolute;
+				top: 16rpx;
+				left: 16rpx;
+				width: 88rpx;
+				height: 88rpx;
+			}
+		}
+
+		.goods-title {
+			font-size: 30rpx;
+			font-weight: bold;
+			color: #333333;
+			line-height: 36rpx;
+			padding-top: 6rpx;
+
+			.title-tag {
+				transform: scale(0.9);
+				position: relative;
+				top: -6rpx;
+			}
+		}
+
+		.goods-subtitle {
+			font-size: 24rpx;
+			font-weight: 400;
+			color: #999999;
+			line-height: 36rpx;
+			width: 480rpx;
+		}
+
+		.tag-box {
+			border: 1rpx solid #ff0000;
+			display: inline-block;
+			font-size: 20rpx;
+			line-height: 30rpx;
+			padding: 0 10rpx;
+			color: #ff0000;
+			border-radius: 8rpx;
+		}
+
+		.goods-progress {
+			.cu-progress {
+				width: 200rpx;
+				height: 20rpx;
+				background: rgba(#ff6361, 0.2);
+				overflow: visible;
+				position: relative;
+
+				.progress--ing {
+					background: #ff6361;
+					border-radius: 20rpx;
+				}
+
+				.round-tag {
+					position: absolute;
+					bottom: 0;
+					width: 20rpx;
+					height: 28rpx;
+					margin-left: -10rpx;
+
+					.round-tag-img {
+						width: 20rpx;
+						height: 28rpx;
+					}
+				}
+			}
+
+			.googs-send-num {
+				font-size: 24rpx;
+				font-weight: 400;
+				color: #bebebe;
+			}
+		}
+
+		.price-box {
+			vertical-align: text-bottom;
+
+			.price {
+				font-size: 36rpx;
+				color: #ff0000;
+				font-weight: 600;
+
+				&::before {
+					content: '¥';
+					font-size: 28rpx;
+				}
+
+				vertical-align: text-bottom;
+			}
+
+			.origin-price {
+				vertical-align: text-bottom;
+				font-size: 26rpx;
+				font-weight: 400;
+				text-decoration: line-through;
+				color: #c4c4c4;
+			}
+		}
+
+		.cart-box {
+
+			.cart-btn,
+			.sel-sku {
+				width: 172rpx;
+				line-height: 58rpx;
+				background: linear-gradient(90deg, rgba(233, 180, 97, 1), rgba(238, 204, 137, 1));
+				box-shadow: 0px 7rpx 6rpx 0px rgba(229, 138, 0, 0.22);
+				border-radius: 30rpx;
+				padding: 0;
+				font-size: 26rpx;
+				font-weight: 500;
+				color: #ffffff;
+			}
+		}
+	}
+</style>

+ 209 - 0
pages/index/category/three-catgory.vue

@@ -0,0 +1,209 @@
+<template>
+	<view class="content_box">
+		<view class="u-flex u-col-center wrapper-box">
+			<view class="scroll-box" style="background-color: #F6F6F6;">
+				<scroll-view class="left u-flex-col u-col-center" enable-flex enable-back-to-top scroll-y>
+					<view class="type-list u-ellipsis-1" :class="[{ 'list-active': listId == index }]" v-for="(item, index) in categoryData" :key="index" @tap="onType(index)">
+						<view class="line" :class="[{ 'line-active': listId == index }]"></view>
+						{{ item.name }}
+					</view>
+					<view class="hack-tabbar"></view>
+				</scroll-view>
+			</view>
+			<view style="height: 100%;width: 100%;">
+				<scroll-view scroll-y class="scroll-box" enable-flex enable-back-to-top scroll-with-animation>
+					<view class="right" v-if="categoryData.length">
+						<image class="type-img" v-if="categoryData[listId].image" :src="categoryData[listId].image" mode="aspectFill"></image>
+
+						<view class="item-list" v-for="(list, index1) in categoryData[listId].children" :key="index1">
+							<view class="type-box u-flex u-row-between u-col-center">
+								<text class="type-title">{{ list.name }}</text>
+								<view class="more u-flex u-col-center" @tap="jump('/pages/goods/list', { id: list.id })">
+									<text>查看更多</text>
+									<view class="u-iconfont uicon-arrow-right" style="color: #999;font-size: 28rpx;"></view>
+								</view>
+							</view>
+							<view class="item-box u-flex">
+								<view class="u-flex-col goods-item" @tap="jump('/pages/goods/list', { id: mlist.id })" v-for="(mlist, index2) in list.children" :key="index2">
+									<image class="item-img" lazy-load :src="mlist.image" mode="aspectFill"></image>
+									<view class="item-title u-ellipsis-1 ">{{ mlist.name }}</view>
+								</view>
+							</view>
+						</view>
+
+						<!-- 缺省页 -->
+						<shopro-empty
+							v-show="!categoryData[listId].children.length"
+							:image="$IMG_URL + '/imgs/empty/empty_goods.png'"
+							tipText="暂无该商品,还有更多好货等着你噢~"
+						></shopro-empty>
+						<view class="hack-tabbar"></view>
+					</view>
+				</scroll-view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+import { mapMutations, mapActions, mapState, mapGetters } from 'vuex';
+export default {
+	components: {},
+	data() {
+		return {
+			listId: 0,
+			categoryData: {}
+		};
+	},
+	computed: {},
+	props: {
+		categoryStyleId: {
+			type: Number,
+			default: 0
+		}
+	},
+	async created() {
+		console.log('%c当前分类:三级分类', 'color:green;background:yellow');
+		await this.getCategory();
+	},
+	methods: {
+		getCategory() {
+			this.$http('category.detail', {
+				id: this.categoryStyleId
+			}).then(res => {
+				if (res.code === 1) {
+					this.categoryData = res.data.children;
+				}
+			});
+		},
+		onType(id) {
+			this.listId = id;
+		},
+		// 路由跳转
+		jump(path, parmas) {
+			this.$Router.push({
+				path: path,
+				query: parmas
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+.hack-tabbar {
+	height: calc(100rpx + env(safe-area-inset-bottom) / 2);
+	width: 100%;
+}
+.content_box {
+	margin-top: 1upx;
+	display: flex;
+	flex-direction: column;
+	overflow: hidden;
+	height: 100%;
+}
+
+.wrapper-box {
+	flex: 1;
+	margin-top: 1upx;
+	height: 100%;
+}
+
+.scroll-box {
+	height: 100%;
+	flex: 1;
+	background: #fff;
+}
+
+.left {
+	width: 200upx;
+	height: 100%;
+	flex: 1;
+
+	.list-active {
+		background: #fff;
+		color: #333333 !important;
+		font-weight: bold !important;
+	}
+
+	.line-active {
+		background: #e6b873;
+	}
+
+	.type-list {
+		height: 84upx;
+		position: relative;
+		width: 200rpx;
+		padding-left: 16rpx;
+		line-height: 84rpx;
+		font-size: 28upx;
+		font-weight: 400;
+		color: rgba(102, 102, 102, 1);
+
+		.line {
+			width: 10upx;
+			height: 100%;
+			position: absolute;
+			left: 0;
+		}
+	}
+}
+
+.right {
+	padding: 0 30upx;
+	flex: 1;
+	height: 100%;
+
+	.type-img {
+		width: 505rpx;
+		height: 150rpx;
+		background: #ccc;
+		border-radius: 10rpx;
+		margin-top: 30rpx;
+	}
+
+	.item-list {
+		.type-box {
+			height: 84rpx;
+
+			.type-title {
+				font-size: 28rpx;
+				font-weight: bold;
+			}
+
+			.more {
+				font-size: 26rpx;
+				color: #999;
+			}
+		}
+
+		.item-box {
+			flex-wrap: wrap;
+
+			.goods-item {
+				margin-right: 20rpx;
+				margin-bottom: 20rpx;
+
+				&:nth-child(3n) {
+					margin-right: 0;
+				}
+
+				.item-img {
+					width: 150rpx;
+					height: 150rpx;
+					background: #f5f5f5;
+					border-radius: 6rpx;
+				}
+
+				.item-title {
+					font-size: 24rpx;
+					line-height: 24rpx;
+					margin-top: 10rpx;
+					width: 150rpx;
+					text-align: center;
+				}
+			}
+		}
+	}
+}
+</style>

+ 213 - 0
pages/index/category/two-catgory.vue

@@ -0,0 +1,213 @@
+<template>
+	<view class="content_box">
+		<view class="u-flex u-col-center wrapper-box">
+			<view class="scroll-box" style="background-color: #F6F6F6;">
+				<scroll-view class="left u-flex-col u-col-center" enable-flex enable-back-to-top scroll-y>
+					<view
+						class="type-list u-flex u-col-center"
+						:class="[{ 'list-active': listId == index }]"
+						v-for="(item, index) in categoryData"
+						:key="index"
+						@tap="onType(index)"
+					>
+						<view class="u-ellipsis-1  list-item" :class="[{ 'line-active': listId == index }]">{{ item.name }}</view>
+					</view>
+					<view class="hack-tabbar"></view>
+				</scroll-view>
+			</view>
+			<view style="height: 100%;width: 100%;">
+				<scroll-view scroll-y class="scroll-box" enable-back-to-top scroll-with-animation>
+					<view class="right" v-if="categoryData.length">
+						<image class="type-img" v-if="categoryData[listId].image" :src="categoryData[listId].image" lazy-load mode="aspectFill"></image>
+						<view class="type-box u-flex u-col-center u-row-center">
+							<view class="u-iconfont uicon-minus" style="color: #d3d3d3;font-size: 28rpx;"></view>
+							<text class="type-title">{{ categoryData[listId].name }}</text>
+							<view class="u-iconfont uicon-minus" style="color: #d3d3d3;font-size: 28rpx;"></view>
+						</view>
+						<view class="item-list">
+							<view class="item-box u-flex">
+								<view
+									class="u-flex-col u-col-center goods-item"
+									@tap="jump('/pages/goods/list', { id: list.id })"
+									v-for="(list, index1) in categoryData[listId].children"
+									:key="index1"
+								>
+									<image class="item-img" lazy-load :src="list.image" mode="aspectFill"></image>
+									<view class="item-title u-ellipsis-1 ">{{ list.name }}</view>
+								</view>
+							</view>
+
+							<!-- 缺省页 -->
+							<shopro-empty
+								v-show="!categoryData[listId].children.length"
+								:image="$IMG_URL + '/imgs/empty/empty_goods.png'"
+								marginTop="200rpx"
+								tipText="暂无该商品,还有更多好货等着你噢~"
+							></shopro-empty>
+							<view class="hack-tabbar"></view>
+						</view>
+					</view>
+				</scroll-view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+import { mapMutations, mapActions, mapState, mapGetters } from 'vuex';
+export default {
+	components: {},
+	data() {
+		return {
+			listId: 0,
+			categoryData: {}
+		};
+	},
+	computed: {},
+	props: {
+		categoryStyleId: {
+			type: Number,
+			default: 0
+		}
+	},
+	created() {
+		console.log('%c当前分类:二级分类', 'color:green;background:yellow');
+		this.getCategory();
+	},
+	methods: {
+		getCategory() {
+			this.$http('category.detail', {
+				id: this.categoryStyleId
+			}).then(res => {
+				if (res.code === 1) {
+					this.categoryData = res.data.children;
+				}
+			});
+		},
+		onType(id) {
+			this.listId = id;
+		},
+		// 路由跳转
+		jump(path, parmas) {
+			this.$Router.push({
+				path: path,
+				query: parmas
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+.hack-tabbar {
+	height: calc(100rpx + env(safe-area-inset-bottom) / 2);
+	width: 100%;
+}
+.content_box {
+	display: flex;
+	flex-direction: column;
+	overflow: hidden;
+	height: 100%;
+}
+
+.wrapper-box {
+	flex: 1;
+	margin-top: 1upx;
+	height: 100%;
+}
+
+.scroll-box {
+	height: 100%;
+	flex: 1;
+	background: #fff;
+}
+
+.left {
+	width: 200upx;
+	height: 100%;
+	flex: 1;
+
+	.list-active {
+		background: #fff;
+		color: #ffff !important;
+	}
+	.list-item {
+		width: 180rpx;
+		height: 64rpx;
+		line-height: 64rpx;
+		padding-left: 14rpx;
+	}
+	.line-active {
+		width: 180rpx;
+		height: 64rpx;
+		background: #e6b873;
+		border-radius: 0rpx 32rpx 32rpx 0rpx;
+		color: #fff;
+	}
+
+	.type-list {
+		height: 100rpx;
+		position: relative;
+		width: 200rpx;
+		font-size: 28upx;
+		font-weight: 400;
+		color: rgba(102, 102, 102, 1);
+	}
+}
+
+.right {
+	padding: 0 30upx;
+	flex: 1;
+	height: 100%;
+
+	.type-img {
+		width: 505rpx;
+		height: 150rpx;
+		background: #ccc;
+		margin-top: 30rpx;
+		border-radius: 10rpx;
+	}
+	.type-box {
+		height: 84rpx;
+
+		.type-title {
+			font-size: 28rpx;
+			font-weight: bold;
+			padding: 0 16rpx;
+		}
+		.more {
+			font-size: 26rpx;
+			color: #999;
+		}
+	}
+
+	.item-list {
+		.item-box {
+			flex-wrap: wrap;
+
+			.goods-item {
+				margin-right: 20rpx;
+				margin-bottom: 20rpx;
+
+				&:nth-child(3n) {
+					margin-right: 0;
+				}
+
+				.item-img {
+					width: 150rpx;
+					height: 150rpx;
+					border-radius: 6rpx;
+					background: #f5f5f5;
+				}
+
+				.item-title {
+					font-size: 24rpx;
+					margin-top: 10rpx;
+					width: 150rpx;
+					text-align: center;
+				}
+			}
+		}
+	}
+}
+</style>

+ 174 - 0
pages/index/components/sh-adv.vue

@@ -0,0 +1,174 @@
+<template>
+	<view class="adv-box u-m-y-10 u-m-x-20">
+		<!-- 模板1-->
+		<view class="u-flex" v-if="advType === 1">
+			<image style="width:710rpx;height:220rpx;border-radius: 10rpx;" @tap="jump(advList[0].path)" :src="advList[0].image" mode="aspectFill"></image>
+		</view>
+		<!-- 模板2-->
+		<view class="type1 u-flex" v-if="advType === 2">
+			<image class="type1-img u-m-r-10" @tap="jump(advList[0].path)" :src="advList[0].image" mode="aspectFill"></image>
+			<image class="type1-img" @tap="jump(advList[1].path)" :src="advList[1].image" mode="aspectFill"></image>
+		</view>
+		<!-- 模板3-->
+		<view class="type2 u-flex" v-if="advType === 3">
+			<view class="type2-img1 u-m-r-10"><image class="type2-img1 " @tap="jump(advList[0].path)" :src="advList[0].image" mode="aspectFill"></image></view>
+			<view class="type2-box">
+				<image class="type2-img2" @tap="jump(advList[1].path)" :src="advList[1].image" mode="aspectFill"></image>
+				<image class="type2-img2" @tap="jump(advList[2].path)" :src="advList[2].image" mode="aspectFill"></image>
+			</view>
+		</view>
+		<!-- 模板4-->
+		<view class="type3 u-flex" v-if="advType === 4">
+			<view class="type3-box">
+				<image class="type3-img1" @tap="jump(advList[0].path)" :src="advList[0].image" mode="aspectFill"></image>
+				<image class="type3-img1" @tap="jump(advList[1].path)" :src="advList[1].image" mode="aspectFill"></image>
+			</view>
+			<view class="type3-img2 u-m-l-10"><image class="type3-img2" @tap="jump(advList[2].path)" :src="advList[2].image" mode="aspectFill"></image></view>
+		</view>
+		<!-- 模板5-->
+		<view class="type4 " v-if="advType === 5">
+			<view class="type4-box u-flex">
+				<image class="type4-img1 u-m-r-10" @tap="jump(advList[0].path)" :src="advList[0].image" mode="aspectFill"></image>
+				<image class="type4-img1" @tap="jump(advList[1].path)" :src="advList[1].image" mode="aspectFill"></image>
+			</view>
+			<view class="type4-img2 u-m-t-10"><image class="type4-img2" @tap="jump(advList[2].path)" :src="advList[2].image" mode="aspectFill"></image></view>
+		</view>
+		<!-- 模板6-->
+		<view class="type5" v-if="advType === 6">
+			<view class="type5-img1 u-m-b-10"><image class="type5-img1" @tap="jump(advList[0].path)" :src="advList[0].image" mode="aspectFill"></image></view>
+			<view class="type5-box u-flex">
+				<image class="type5-img2 u-m-r-10" @tap="jump(advList[1].path)" :src="advList[1].image" mode="aspectFill"></image>
+				<image class="type5-img2" @tap="jump(advList[2].path)" :src="advList[2].image" mode="aspectFill"></image>
+			</view>
+		</view>
+		<!-- 模板7-->
+		<view class="type6" v-if="advType === 7">
+			<view class="u-flex type6-box1 u-m-b-10">
+				<image class="type6-img1 u-m-r-10" @tap="jump(advList[0].path)" :src="advList[0].image" mode="aspectFill"></image>
+				<image class="type6-img1" @tap="jump(advList[1].path)" :src="advList[1].image" mode="aspectFill"></image>
+			</view>
+			<view class="u-flex type6-box2">
+				<image class="type6-img2 u-m-r-10" @tap="jump(advList[2].path)" :src="advList[2].image" mode="aspectFill"></image>
+				<image class="type6-img2 u-m-r-10" @tap="jump(advList[3].path)" :src="advList[3].image" mode="aspectFill"></image>
+				<image class="type6-img2" @tap="jump(advList[4].path)" :src="advList[4].image" mode="aspectFill"></image>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+/**
+ * 自定义之广告魔方
+ * @property {Object} detail -广告魔方信息
+ */
+export default {
+	components: {},
+	data() {
+		return {
+			advType: this.detail.style ,// 模板样式
+			advList:this.detail.list // adv数据
+		};
+	},
+	props: {
+		detail: {}
+	},
+	computed: {},
+	created() {},
+	methods: {
+		// 路由跳转
+		jump(path) {
+			this.$tools.routerTo(path);
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+@mixin grid($row: 1) {
+	width: (710 - ($row - 1) * 10) / $row + rpx;
+	border-radius: 10rpx;
+}
+.adv-box {
+	.type1 {
+		.type1-img {
+			@include grid(2);
+			height: 220rpx;
+		}
+	}
+
+	.type2 {
+		.type2-img1 {
+			height: 350rpx;
+			@include grid(2);
+		}
+		.type2-box {
+			height: 350rpx;
+			@include grid(2);
+			.type2-img2 {
+				height: 170rpx;
+				@include grid(2);
+			}
+		}
+	}
+
+	.type3 {
+		.type3-box {
+			height: 350rpx;
+			@include grid(2);
+			.type3-img1 {
+				height: 170rpx;
+				@include grid(2);
+			}
+		}
+
+		.type3-img2 {
+			height: 350rpx;
+			@include grid(2);
+		}
+	}
+
+	.type4 {
+		.type4-box {
+			.type4-img1 {
+				height: 170rpx;
+				@include grid(2);
+			}
+		}
+
+		.type4-img2 {
+			@include grid(1);
+			height: 220rpx;
+		}
+	}
+
+	.type5 {
+		.type5-img1 {
+			@include grid(1);
+			height: 220rpx;
+		}
+
+		.type5-box {
+			.type5-img2 {
+				height: 170rpx;
+				@include grid(2);
+			}
+		}
+	}
+
+	.type6 {
+		.type6-box1 {
+			.type6-img1 {
+				height: 170rpx;
+				@include grid(2);
+			}
+		}
+
+		.type6-box2 {
+			.type6-img2 {
+				height: 170rpx;
+				@include grid(3);
+			}
+		}
+	}
+}
+</style>

+ 69 - 0
pages/index/components/sh-banner.vue

@@ -0,0 +1,69 @@
+<template>
+	<!-- 轮播 -->
+	<view class="banner-swiper-wrap u-m-b-10" :style="{ padding: `${Py}rpx ${Px}rpx` }">
+		<swiper
+			:style="{ minHeight: height + 'rpx', height: height + 'rpx' }"
+			class="screen-swiper square-dot"
+			:indicator-dots="true"
+			:circular="true"
+			:autoplay="true"
+			interval="5000"
+			duration="500"
+		>
+			<swiper-item :style="{ borderRadius: borderRadius + 'rpx' }" v-for="(item, index) in list" :key="index" @tap="onSwiper(index)">
+				<image :src="item.image" mode="aspectFill"></image>
+			</swiper-item>
+		</swiper>
+	</view>
+</template>
+
+<script>
+/**
+ * shBanner-轮播卡片
+ * @property {Array} list 轮播图数据,
+ * @property {String Number} height 轮播图组件高度,单位rpx(默认250)
+ * @property {String} borderRadius 圆角值
+ * @event {Function} click 点击轮播图时触发
+ */
+export default {
+	components: {},
+	data() {
+		return {};
+	},
+	props: {
+		Px: {
+			type: [Number, String],
+			default: 0
+		},
+		Py: {
+			type: [Number, String],
+			default: 0
+		},
+		// 轮播图的数据,格式如:[{image: 'xxxx', title: 'xxxx'},{image: 'yyyy', title: 'yyyy'}],其中title字段可选
+		list: {
+			type: Array,
+			default() {
+				return [];
+			}
+		},
+		// 圆角值
+		borderRadius: {
+			type: [Number, String],
+			default: 0
+		},
+		// list的高度,单位rpx
+		height: {
+			type: [Number, String],
+			default: 250
+		}
+	},
+	computed: {},
+	methods: {
+		onSwiper(e) {
+			this.$tools.routerTo(this.list[e].path);
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped></style>

+ 348 - 0
pages/index/components/sh-category-tabs.vue

@@ -0,0 +1,348 @@
+<template>
+	<!-- 分类选项卡 -->
+	<view class="category-tabs-wrap">
+		<!-- 吸顶 -->
+		<view class="u-sticky-wrap" :class="[elClass]" :style="{ height: fixed ? height + 'px' : 'auto', backgroundColor: isSticky ? '#fff' : '#f6f6f6' }">
+			<view
+				class="u-sticky"
+				:style="{ position: fixed ? 'fixed' : 'static', top: stickyTop + 'px', left: left + 'px', width: width == 'auto' ? 'auto' : width + 'px', zIndex: 1109 }"
+			>
+				<view class="tabs-wrap u-p-y-20" :style="isSticky ? 'border-bottom:1px solid #eee;background-color:#fff' : ''">
+					<scroll-view scroll-x class="tabs-content" enable-flex scroll-with-animation :scroll-left="scrollLeft">
+						<view class="u-flex u-row-cetner u-flex-1 u-col-center">
+							<view class="tab-item u-flex-col u-col-center u-row-center" v-for="(item, index) in tabsList" :key="index" @tap="tabChange(index)">
+								<view class="tab-title u-m-y-6" :class="{ 'title-active': tabCur == index }">{{ item.name }}</view>
+								<view class="tab-des" :class="{ 'des-active': tabCur == index }">{{ item.description }}</view>
+							</view>
+						</view>
+					</scroll-view>
+				</view>
+			</view>
+		</view>
+
+		<!-- 瀑布流 -->
+		<view class="u-waterfall u-p-16" v-if="styleType == 1">
+			<view id="u-left-column" class="u-column">
+				<view class="goods-item u-m-b-16 u-flex u-row-center u-col-center" v-for="leftGoods in leftList" :key="leftGoods.id">
+					<shopro-goods-card
+						:detail="leftGoods"
+						:type="leftGoods.activity_type || ''"
+						:image="leftGoods.image"
+						:title="leftGoods.title"
+						:subtitle="leftGoods.subtitle"
+						:price="leftGoods.price"
+						:originPrice="leftGoods.original_price"
+						:tagTextList="leftGoods.activity_discounts_tags"
+						@click="$Router.push({ path: '/pages/goods/detail', query: { id: leftGoods.id } })"
+					></shopro-goods-card>
+				</view>
+			</view>
+			<view id="u-right-column" class="u-column">
+				<view class="goods-item u-m-b-16 u-flex u-row-center u-col-center" v-for="rightGoods in rightList" :key="rightGoods.id">
+					<shopro-goods-card
+						:detail="rightGoods"
+						:type="rightGoods.activity_type || ''"
+						:image="rightGoods.image"
+						:title="rightGoods.title"
+						:subtitle="rightGoods.subtitle"
+						:price="rightGoods.price"
+						:originPrice="rightGoods.original_price"
+						:tagTextList="rightGoods.activity_discounts_tags"
+						@click="$Router.push({ path: '/pages/goods/detail', query: { id: rightGoods.id } })"
+					></shopro-goods-card>
+				</view>
+			</view>
+		</view>
+
+		<!-- m -->
+		<view class="big-card-wrap u-p-10" v-if="styleType == 2">
+			<block v-for="item in goodsList" :key="item.id">
+				<sh-goods-card
+					:detail="item"
+					:type="item.activity_type || ''"
+					:image="item.image"
+					:title="item.title"
+					:subtitle="item.subtitle"
+					:price="item.price"
+					:originPrice="item.original_price"
+					:sales="item.sales"
+					:tagTextList="item.activity_discounts_tags"
+					@click="$Router.push({ path: '/pages/goods/detail', query: { id: item.id } })"
+				></sh-goods-card>
+			</block>
+		</view>
+
+		<view class="x-c" style="width: 100%;">
+			<!-- 缺省页 -->
+			<shopro-empty v-if="isEmpty" marginTop="200rpx" :image="$IMG_URL + '/imgs/empty/empty_goods.png'" tipText="暂无该商品,还有更多好货等着你噢~"></shopro-empty>
+
+			<!-- 更多 -->
+			<u-loadmore v-show="!isEmpty" height="80rpx" :status="loadStatus" icon-type="flower" color="#ccc" />
+		</view>
+	</view>
+</template>
+
+<script>
+/**
+ * category-tabs
+ * @description 一个可以吸顶的分类列表
+ * @property {Boolean} enable = false - 是否吸顶,tabbar项中,组件不会自动销毁,需要自行开关
+ * @property {Array} tabsList  - 分类列表
+ * @property {Array | Number} styleType  - 卡片类型
+ */
+let systemInfo = uni.getSystemInfoSync();
+// #ifdef MP-WEIXIN || MP-BAIDU || MP-TOUTIAO || MP-QQ
+let menuButtonInfo = uni.getMenuButtonBoundingClientRect();
+// #endif
+import shGoodsCard from '../components/sh-goods-card.vue';
+export default {
+	components: {
+		shGoodsCard
+	},
+	data() {
+		return {
+			// 吸顶相关
+			fixed: false,
+			height: 'auto',
+			elClass: this.$u.guid(),
+			left: 0,
+			width: 'auto',
+			stickyTop: 0,
+			isSticky: false, //是否吸顶
+
+			// 瀑布流 350-330
+			addTime: 100, //排序间隙时间
+			leftHeight: 0,
+			rightHeight: 0,
+			leftList: [],
+			rightList: [],
+			tempList: [],
+
+			tabCur: 0,
+			scrollLeft: 0,
+			isEmpty: true,
+			categoryId: this.tabsList[0]?.id,
+			goodsList: [],
+			tabCurrent: 0,
+			loadStatus: 'loadmore', //loadmore-加载前的状态,loading-加载中的状态,nomore-没有更多的状态
+			currentPage: 1,
+			lastPage: 1
+		};
+	},
+	watch: {
+		enable(val) {
+			if (val == false) {
+				this.fixed = false;
+				this.disconnectObserver('contentObserver');
+			} else {
+				this.initObserver();
+			}
+		}
+	},
+	props: {
+		tabsList: {
+			type: Array,
+			default: () => []
+		},
+		styleType: {
+			type: [String, Number],
+			default: 1
+		},
+		enable: {
+			type: Boolean,
+			default: false
+		}
+	},
+	created() {
+		this.getGoodsList();
+		// 触底监听
+		uni.$on('uOnReachBottom', data => {
+			if (this.currentPage < this.lastPage) {
+				this.currentPage += 1;
+				this.getGoodsList();
+			}
+		});
+	},
+	mounted() {
+		this.initObserver();
+	},
+	beforeDestroy() {
+		this.disconnectObserver('contentObserver');
+	},
+	methods: {
+		// 瀑布流相关
+		async splitData() {
+			if (!this.tempList.length) return;
+			let item = this.tempList[0];
+			if (!item) return;
+
+			// 分左右
+			if (this.leftHeight < this.rightHeight) {
+				this.leftHeight += item.activity_discounts_tags.length ? 350 : 330;
+				this.leftList.push(item);
+			} else if (this.leftHeight > this.rightHeight) {
+				this.rightHeight += item.activity_discounts_tags.length ? 350 : 330;
+				this.rightList.push(item);
+			} else {
+				this.leftHeight += item.activity_discounts_tags.length ? 350 : 330;
+				this.leftList.push(item);
+			}
+
+			// 移除临时列表的第一项,如果临时数组还有数据,继续循环
+			this.tempList.splice(0, 1);
+			if (this.tempList.length) {
+				setTimeout(() => {
+					this.splitData();
+				}, this.addTime);
+			}
+		},
+		clear() {
+			this.leftList = [];
+			this.rightList = [];
+			this.leftHeight = 0;
+			this.rightHeight = 0;
+			this.tempList = [];
+		},
+
+		// 吸顶相关
+		initObserver() {
+			if (!this.enable) return;
+			// #ifdef APP-PLUS || H5
+			this.stickyTop = systemInfo.statusBarHeight + 44;
+			// #endif
+			// #ifdef MP
+			let height = systemInfo.platform == 'ios' ? 44 : 48;
+			let top = systemInfo.statusBarHeight + height;
+			this.stickyTop = systemInfo.statusBarHeight + height;
+			// #endif
+			this.disconnectObserver('contentObserver');
+
+			this.$uGetRect('.' + this.elClass).then(res => {
+				this.height = res.height;
+				this.left = res.left;
+				this.width = res.width;
+				this.$nextTick(() => {
+					this.observeContent();
+				});
+			});
+		},
+		observeContent() {
+			this.disconnectObserver('contentObserver');
+			const contentObserver = this.createIntersectionObserver({
+				thresholds: [0.95, 0.98, 1]
+			});
+			contentObserver.relativeToViewport({
+				top: -this.stickyTop
+			});
+			contentObserver.observe('.' + this.elClass, res => {
+				if (!this.enable) return;
+				this.setFixed(res.boundingClientRect.top);
+			});
+			this.contentObserver = contentObserver;
+		},
+		setFixed(top) {
+			const fixed = top < this.stickyTop;
+			if (fixed) {
+				this.isSticky = true;
+			} else if (this.fixed) {
+				this.isSticky = false;
+			}
+			this.fixed = fixed;
+		},
+		disconnectObserver(observerName) {
+			const observer = this[observerName];
+			observer && observer.disconnect();
+		},
+
+		// 商品列表
+		getGoodsList() {
+			let that = this;
+			that.$http('goods.lists', {
+				category_id: that.categoryId,
+				page: that.currentPage
+			}).then(res => {
+				if (res.code === 1) {
+					that.goodsList = [...that.goodsList, ...res.data.data];
+					that.tempList = res.data.data;
+					that.isEmpty = !that.goodsList.length;
+					that.lastPage = res.data.last_page;
+					that.loadStatus = that.currentPage < res.data.last_page ? 'loadmore' : 'nomore';
+					that.splitData();
+				}
+			});
+		},
+
+		// tabs
+		tabChange(index) {
+			this.tabCur = index;
+			this.scrollLeft = (index - 1) * 60;
+			this.categoryId = this.tabsList[index].id;
+			this.styleType === 1 && this.clear();
+			this.goodsList = [];
+			this.currentPage = 1;
+			this.lastPage = 1;
+			this.loadStatus = 'loadmore';
+			this.getGoodsList();
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+@mixin vue-flex($direction: row) {
+	/* #ifndef APP-NVUE */
+	display: flex;
+	flex-direction: $direction;
+	/* #endif */
+}
+.category-tabs-wrap {
+	min-height: 1000rpx;
+	// 吸顶
+	.u-sticky-wrap {
+		background-color: #fff;
+	}
+	// 瀑布流
+	.u-waterfall {
+		@include vue-flex;
+		flex-direction: row;
+		align-items: flex-start;
+	}
+
+	.u-column {
+		@include vue-flex;
+		flex: 1;
+		flex-direction: column;
+		height: auto;
+	}
+}
+// 滑动分类
+.tabs-content {
+	white-space: nowrap;
+	.tab-item {
+		min-height: 90rpx;
+		display: inline-block;
+		margin: 0 10rpx;
+		padding: 0 20rpx;
+		.tab-title {
+			font-size: 28rpx;
+			font-weight: 600;
+			color: #333333;
+		}
+		.title-active {
+			color: #a8700d;
+		}
+		.tab-des {
+			font-size: 22rpx;
+			font-weight: 400;
+			color: #999999;
+			text-align: center;
+		}
+		.des-active {
+			background: linear-gradient(90deg, #e9b461, #eecc89);
+			border-radius: 15rpx;
+			padding: 2rpx 10rpx;
+			color: #ffffff;
+		}
+	}
+}
+</style>

+ 56 - 0
pages/index/components/sh-cell.vue

@@ -0,0 +1,56 @@
+<template>
+	<view class="sh-user-menu-box u-m-b-10">
+		<view class="menu-item u-flex u-row-between u-border-bottom" hover-stay-time="150" hover-class="u-cell-hover" v-for="(item, index) in list" :key="index" @tap="jump(item)">
+			<view class="menu-head u-flex u-col-center">
+				<image class="menu-img u-m-r-10" :src="item.image" mode=""></image>
+				<view class="menu-title ">{{ item.name }}</view>
+			</view>
+			<view class="u-iconfont uicon-arrow-right" style="color: #969799;font-size: 28rpx;"></view>
+		</view>
+	</view>
+</template>
+
+<script>
+/**
+ *shCell-功能列表
+ * @property {Array} list - 列表信息
+ */
+export default {
+	components: {},
+	data() {
+		return {};
+	},
+	props: {
+		list: {
+			type: Array,
+			default: () => {
+				return [];
+			}
+		}
+	},
+	computed: {},
+	methods: {
+		jump(data) {
+			this.$tools.routerTo(data.path);
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.menu-item {
+	background-color: #fff;
+	width: 100%;
+	padding: 26rpx 32rpx;
+	font-size: 28rpx;
+	line-height: 54rpx;
+	.menu-img {
+		width: 36rpx;
+		height: 36rpx;
+	}
+	.menu-title {
+		font-size: 26rpx;
+		color: #999;
+	}
+}
+</style>

+ 276 - 0
pages/index/components/sh-coupon.vue

@@ -0,0 +1,276 @@
+<template>
+	<!-- 首页优惠券卡片 -->
+	<view class="coupon-box u-m-b-10 u-p-20" v-if="detail.ids && couponList.length">
+		<view class="head-box u-flex u-row-between u-m-b-20">
+			<view class="head-title u-ellipsis-1">领券专区</view>
+			<view class="head-more u-flex u-col-center" @tap="$Router.push('/pages/app/coupon/list')">
+				<text class="more-text u-m-r-10">查看更多</text>
+				<text class="iconfont icon-youjiantou-tianchong more-icon"></text>
+			</view>
+		</view>
+		<scroll-view class="groupon-scroll" enable-flex scroll-anchoring scroll-x scroll-with-animation>
+			<view class="groupon-card-wrap u-flex ">
+				<view :class="{ 'gray-wrap': item.status_code === 'cannot_get' }" v-for="(item, index) in couponList" :key="item.id">
+					<!-- mini -->
+					<view class="coupon-card u-flex u-row-between u-m-r-16" v-if="couponType === 2" :style="{ background: `linear-gradient(to right, ${bgColor1}, ${bgColor2})` }">
+						<view class="card-left u-flex-col u-row-center u-p-l-30">
+							<view class="price u-m-b-10" :style="{ color: priceColor }">{{ item.amount }}</view>
+							<view class="notice" :style="{ color: color }">满{{ item.enough }}元可用</view>
+							<view class="notice u-m-b-10" :style="{ color: color }">仅剩{{ item.stock }}张</view>
+						</view>
+						<view class="card-right u-p-y-20 u-p-r-10 u-flex-col u-row-center u-col-center">
+							<button class="u-reset-button get-btn u-m-b-10" :style="{ color: color }" @tap="getCoupon(item.id, index)">
+								{{ item.status_code === 'cannot_get' ? '不可领取' : '领券购买' }}
+							</button>
+						</view>
+					</view>
+					<!-- big -->
+					<view v-if="couponType === 1" class="u-p-r-16">
+						<view class="coupon-wrap " :style="{ background: `linear-gradient(to right, ${bgColor1}, ${bgColor2})` }">
+							<view class="coupon-item u-flex u-row-between">
+								<view class="coupon-left  u-flex-col ">
+									<view class="sum-box">
+										<text class="unit " :style="{ color: priceColor }">¥</text>
+										<text class="sum " :style="{ color: priceColor }">{{ item.amount }}</text>
+										<text class="sub " :style="{ color: priceColor }">{{ item.name }}</text>
+									</view>
+									<view class="notice " :style="{ color: color }">满{{ item.enough }}元可用</view>
+									<view class="notice" :style="{ color: color }">
+										有效期:{{ $u.timeFormat(item.usetime.start, 'yyyy-mm-dd') }} 至 {{ $u.timeFormat(item.usetime.end, 'yyyy-mm-dd') }}
+									</view>
+								</view>
+								<view class="coupon-right u-flex-col">
+									<button class="get-btn" :style="{ color: bgColor1 }" @tap.stop="getCoupon(item.id, index)">
+										{{ item.status_code === 'cannot_get' ? '不可领取' : '立即领取' }}
+									</button>
+									<view class="surplus-num" :style="{ color: color }">仅剩{{ item.stock }}张</view>
+								</view>
+							</view>
+						</view>
+					</view>
+				</view>
+			</view>
+		</scroll-view>
+	</view>
+</template>
+
+<script>
+/**
+ * 自定义之优惠券卡片
+ * @property {Object} detail - 优惠券信息
+ */
+export default {
+	components: {},
+	data() {
+		return {
+			currentIndex: 0,
+			couponList: [],
+
+			couponType: this.detail.style,
+			priceColor: this.detail.pricecolor,
+			color: this.detail.color,
+			bgColor1: this.detail.bgcolor1,
+			bgColor2: this.detail.bgcolor2
+		};
+	},
+	props: {
+		detail: {}
+	},
+	computed: {},
+	created() {
+		this.detail.ids && this.getCouponsList();
+	},
+	methods: {
+		jump(path, parmas) {
+			this.$Router.push({
+				path: path,
+				query: parmas
+			});
+		},
+		// 优惠券列表
+		getCouponsList() {
+			let that = this;
+			that.$http('coupons.templateList', {
+				ids: that.detail.ids
+			}).then(res => {
+				if (res.code === 1) {
+					that.couponList = res.data;
+				}
+			});
+		},
+		// 领取
+		getCoupon(id, index) {
+			let that = this;
+			uni.showLoading({
+				title: '领取中'
+			});
+			that.$http('coupons.get', {
+				id: id
+			}).then(res => {
+				uni.hideLoading();
+				if (res.code === 1) {
+					that.couponList[index].stock -= 1;
+					this.$u.toast('领取成功');
+				}
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+// 失效
+.gray-wrap {
+	filter: progid:DXImageTransform.Microsoft.BasicImage(grayscale=1) !important;
+	-webkit-filter: grayscale(100%) !important;
+	-moz-filter: grayscale(100%) !important;
+	-ms-filter: grayscale(100%) !important;
+	-o-filter: grayscale(100%) !important;
+	filter: grayscale(100%) !important;
+	filter: gray !important;
+}
+.groupon-scroll {
+	height: 170rpx;
+	width: 730rpx;
+	.groupon-card-wrap {
+		height: 170rpx;
+		width: 730rpx;
+	}
+}
+
+.coupon-box {
+	background-color: #fff;
+
+	.head-box {
+		.head-title {
+			width: 300rpx;
+			font-size: 32rpx;
+			font-weight: bold;
+			color: #333333;
+		}
+		.head-more {
+			.more-text {
+				font-size: 22rpx;
+				font-weight: 500;
+				color: #333333;
+			}
+			.more-icon {
+				color: #333333;
+				font-size: 24rpx;
+			}
+		}
+	}
+	.coupon-card {
+		width: 343rpx;
+		height: 170rpx;
+		background: linear-gradient(90deg, #f8dca5, #efc480);
+		border-radius: 10rpx;
+		mask: url($IMG_URL+'/imgs/coupon_mini_bg.png');
+		-webkit-mask-box-image: url($IMG_URL+'/imgs/coupon_mini_bg.png');
+		mask-size: 100% 100%;
+		.card-left {
+			height: 100%;
+			width: 260rpx;
+			.price {
+				color: #4f3a1e;
+				font-size: 30rpx;
+				font-weight: bold;
+				&::before {
+					content: '¥';
+					font-size: 20rpx;
+				}
+			}
+			.notice {
+				font-size: 22rpx;
+				font-weight: 500;
+				color: #a8700d;
+			}
+		}
+		.card-right {
+			height: 100%;
+			width: 60rpx;
+			.get-btn {
+				font-size: 24rpx;
+				font-weight: 500;
+				text-align: right;
+				color: #a8700d;
+				writing-mode: vertical-lr; /*从左向右 从右向左是 writing-mode: vertical-rl;*/
+				writing-mode: tb-lr; /*IE浏览器的从左向右 从右向左是 writing-mode: tb-rl;*/
+			}
+		}
+	}
+}
+
+// 未领取,已领取
+.coupon-wrap {
+	mask: url($IMG_URL+'/imgs/coupon_bg1.png');
+	-webkit-mask-box-image: url($IMG_URL+'/imgs/coupon_bg1.png');
+	mask-size: 100% 100%;
+	position: relative;
+	border-radius: 10rpx;
+	width: 710rpx;
+	height: 170rpx;
+	background: linear-gradient(to right, $u-type-warning-disabled, $u-type-warning);
+	.coupon-item {
+		width: 100%;
+		height: 168rpx;
+		border-radius: 10rpx;
+
+		.coupon-left {
+			height: 100%;
+			justify-content: center;
+			padding-left: 40rpx;
+
+			.unit {
+				font-size: 24rpx;
+				color: #fff;
+			}
+
+			.sum {
+				font-size: 55rpx;
+				font-weight: bold;
+				color: #fff;
+				line-height: 55rpx;
+				padding-right: 10rpx;
+			}
+
+			.sub {
+				font-size: 26rpx;
+				color: #fff;
+			}
+
+			.notice {
+				font-size: 22rpx;
+				color: rgba(#fff, 0.7);
+				margin-top: 6rpx;
+			}
+		}
+
+		.coupon-right {
+			height: 100%;
+			width: 220rpx;
+			justify-content: center;
+			align-items: center;
+
+			.get-btn {
+				width: 142rpx;
+				height: 54rpx;
+				background: #ffffff;
+				border-radius: 27rpx;
+				padding: 0;
+				font-size: 24rpx;
+				font-weight: 500;
+				color: $u-type-warning;
+			}
+
+			.surplus-num {
+				font-size: 22rpx;
+
+				font-weight: 500;
+				color: #fff;
+				margin-top: 14rpx;
+			}
+		}
+	}
+}
+</style>

+ 356 - 0
pages/index/components/sh-goods-card.vue

@@ -0,0 +1,356 @@
+<!-- 横版大图,商品卡片 -->
+<template>
+	<view class="big-goods u-flex u-p-20 u-col-top u-m-b-16" @tap="click"
+		:style="type ? 'background-image: url(' + $IMG_URL + typeMap[type].goodsBg + ')' : ''">
+		<image class="goods-img" lazy-load fade-show :src="image" mode="aspectFill"></image>
+		<view class=" card-right u-m-l-20 u-flex-col u-row-between">
+			<view class="">
+				<view class="goods-title u-ellipsis-1 u-m-t-10 u-m-b-10">
+					<view v-if="type" class=" sm cu-tag radius title-tag u-m-r-10"
+						:style="{ backgroundColor: typeMap[type].tagBg, color: '#fff' }">{{ typeMap[type].text }}</view>
+					{{ title }}
+				</view>
+				<view v-show="subtitle" class="subtitle-text u-m-b-10 u-ellipsis-1">{{ subtitle }}</view>
+			</view>
+
+			<view class="u-m-b-20" v-if="tagTextList.length">
+				<view class="tag-box u-m-r-10" v-for="(item, index) in tagTextList" :key="index">{{ item }}</view>
+			</view>
+
+			<view class=" u-flex u-row-between u-col-center">
+				<view class="font-OPPOSANS">
+					<view class="price">{{ price }}</view>
+					<view class="origin-price">{{ originPrice }}</view>
+				</view>
+
+				<!-- 加入购物车 -->
+				<view class="cart-box">
+					<!-- 单规格 -->
+					<view class="" v-if="!detail.is_sku">
+						<button v-if="!isCart(detail.id)" @tap.stop="addCart(detail.sku_price[0])"
+							class="u-reset-button buy-btn u-flex u-row-center u-col-center"
+							:style="type ? typeMap[type].btnBg : ''">
+							{{ type ? typeMap[type].btnText : '去购买' }}
+						</button>
+						<view class="num-step" @tap.stop v-else>
+							<u-number-box :value="checkCart[detail.id].num" :min="0" :step="1" :long-press="false"
+								:max="detail.sku_price[0].stock  > 999 ? 999 : detail.sku_price[0].stock" @min="onMin"
+								@plus="plus($event, detail.sku_price[0])"
+								@change="onChangeNum($event, detail.sku_price[0])"></u-number-box>
+						</view>
+					</view>
+					<!-- 多规格 -->
+					<button class="u-reset-button item-btn buy-btn" :style="type ? typeMap[type].btnBg : ''"
+						@tap.stop="selSku(detail)" v-else>
+						{{ type ? typeMap[type].btnText : '去购买' }}
+					</button>
+				</view>
+			</view>
+		</view>
+		<!-- 规格弹窗 -->
+		<shopro-sku v-if="showSku && goodsInfo.id" v-model="showSku" :goodsInfo="goodsInfo" buyType="cart"></shopro-sku>
+	</view>
+</template>
+
+<script>
+	/**
+	 *shoproGoodsCard - 商品列表卡片
+	 * @property {Object} detail - 商品详情
+	 * @property {String} type - 商品类型
+	 * @property {String} image - 商品图片
+	 * @property {String} title - 商品标题
+	 * @property {String} subtitle - 商品副标题
+	 * @property {String | Number} price - 商品价格
+	 * @property {String | Number} originPrice - 商品原价
+	 * @property {String | Number} sales - 商品销量
+	 * @property {Array} tagTextList - 活动标签
+	 * @event {Function} click 商品被点击
+	 */
+	import { mapMutations, mapActions, mapState, mapGetters } from 'vuex';
+	export default {
+		components: {},
+		data() {
+			return {
+				showSku: false,
+				goodsInfo: {}, //商品详情
+				typeMap: {
+					seckill: {
+						text: '秒杀',
+						tagBg: '#FF5854',
+						goodsBg: '/imgs/tag/seckill_x_bg.png',
+						btnText: '去抢购',
+						btnBg: 'background: linear-gradient(90deg, #D01325, #ED3C30);'
+					},
+					groupon: {
+						text: '拼团',
+						tagBg: '#FE832A',
+						goodsBg: '/imgs/tag/groupon_x_bg.png',
+						btnText: '马上拼',
+						btnBg: 'background: linear-gradient(90deg, #FF6600 0%, #FE832A 100%);'
+					}
+				}
+			};
+		},
+		computed: {
+			...mapGetters(['cartList', 'checkCart'])
+		},
+		props: {
+			detail: {
+				type: Object,
+				default: () => {
+					return {};
+				}
+			},
+			type: {
+				type: [String, null],
+				default: ''
+			},
+			image: {
+				type: String,
+				default: ''
+			},
+			title: {
+				type: String,
+				default: ''
+			},
+			subtitle: {
+				type: String,
+				default: ''
+			},
+			price: {
+				type: [String, Number],
+				default: ''
+			},
+			originPrice: {
+				type: [String, Number],
+				default: ''
+			},
+			sales: {
+				type: [String, Number],
+				default: ''
+			},
+			tagTextList: {
+				type: Array,
+				default: () => []
+			}
+		},
+		mounted() {},
+		methods: {
+			...mapActions(['getCartList', 'changeCartList', 'addCartGoods']),
+			//点击商品
+			click() {
+				this.$emit('click');
+			},
+
+			// 检测是否为购物车商品
+			isCart(id) {
+				return Object.keys(this.checkCart).includes(id + '');
+			},
+
+			// 检测商品在购物车中的下标
+			checkGoodsIndex(id) {
+				let cIndex = 0;
+				this.cartList.forEach((item, index) => {
+					if (id == item.goods_id) {
+						cIndex = index;
+					}
+				});
+				return cIndex;
+			},
+
+			// 更改商品数
+			async onChangeNum(e, sku) {
+				let gIndex = this.checkGoodsIndex(sku.goods_id);
+				if (e.value != this.checkCart[sku.goods_id].num) {
+					uni.showLoading({
+						mask: true
+					});
+					this.$set(this.cartList[gIndex], 'goods_num', +e.value);
+					await this.changeCartList({
+						ids: [this.checkCart[sku.goods_id].cartOrderId],
+						goodsNum: +e.value,
+						art: 'change'
+					});
+					await uni.hideLoading();
+				}
+			},
+
+			// 到达最小值
+			onMin() {
+				const that = this;
+				let cartGoodId = 0;
+				cartGoodId = this.cartList.filter(item => item.goods_id === that.detail.id)[0].id;
+				uni.showModal({
+					title: '删除提示',
+					confirmColor: '#f0c785',
+					content: `是否确认从购物车中删除此商品?`,
+					success: res => {
+						res.confirm && this.changeCartList({ ids: [cartGoodId], art: 'delete' });
+					}
+				});
+			},
+
+			// 增加
+			plus(e, sku) {
+				if (e.value >= sku.stock) {
+					this.$u.toast('库存不足');
+					return;
+				}
+				if (this.detail.activity_type === 'seckill' || this.detail.activity_type === 'groupon') {
+					let rules = this.detail.activity.rules;
+					if (rules.limit_buy != 0 && e.value >= rules.limit_buy) {
+						this.$u.toast('本次活动最多购买' + rules.limit_buy + '件');
+						return;
+					}
+				}
+			},
+
+			// 添加购物车,多规格
+			async selSku(info) {
+				if (this.detail.activity_type) {
+					this.$Router.push({ path: '/pages/goods/detail', query: { id: this.detail.id } });
+					return;
+				}
+				this.goodsInfo = {};
+				this.getGoodsDetail(info.id);
+				this.showSku = true;
+			},
+
+			// 商品详情
+			getGoodsDetail(id) {
+				let that = this;
+				that.$http('goods.detail', {
+					id: id
+				}).then(res => {
+					if (res.code === 1) {
+						that.goodsInfo = res.data;
+					}
+				});
+			},
+
+			// 加入购物车
+			addCart(sku) {
+				if (sku.stock <= 0) {
+					this.$u.toast('库存不足');
+					return;
+				}
+				if (this.detail.activity_type) {
+					this.$Router.push({ path: '/pages/goods/detail', query: { id: this.detail.id } });
+					return;
+				}
+				let confirmGoodsList = {
+					list: [{
+						goods_id: sku.goods_id,
+						goods_num: 1,
+						sku_price_id: sku.id,
+						goods_price: sku.price
+					}],
+					from: 'goods'
+				};
+				this.addCartGoods(confirmGoodsList).then(res => {
+					if (res.code === 1) {
+						this.$u.toast(res.msg);
+					}
+				});
+			}
+		}
+	};
+</script>
+
+<style lang="scss">
+	// 大商品卡片
+	.big-goods {
+		width: 710rpx;
+		min-height: 260rpx;
+		background: #ffffff;
+		border-radius: 20rpx;
+		background-repeat: no-repeat;
+		background-size: 100% 100%;
+
+		.goods-img {
+			width: 260rpx;
+			height: 260rpx;
+			background-color: #f5f5f5;
+			border-radius: 6rpx;
+		}
+
+		.card-right {
+			width: 430rpx;
+			height: 220rpx;
+		}
+
+		.goods-title {
+			font-size: 26rpx;
+			font-weight: 600;
+			width: 400rpx;
+			color: #000000;
+			padding-top: 6rpx;
+
+			.title-tag {
+				transform: scale(0.9);
+				position: relative;
+				top: -6rpx;
+			}
+		}
+
+		.subtitle-text {
+			font-size: 22rpx;
+			width: 400rpx;
+			font-weight: 500;
+			color: #666666;
+		}
+
+		.tag-box {
+			border: 1rpx solid #ff0000;
+			display: inline-block;
+			font-size: 20rpx;
+			line-height: 30rpx;
+			padding: 0 10rpx;
+			color: #ff0000;
+			border-radius: 8rpx;
+		}
+
+		// 购物车
+		.cart-box {
+			.cart-btn {
+				width: 54rpx;
+				height: 54rpx;
+				border-radius: 50%;
+				padding: 0;
+				background: linear-gradient(90deg, #e9b461, #eecc89);
+			}
+
+			.buy-btn {
+				width: 120rpx;
+				line-height: 50rpx;
+				background: linear-gradient(90deg, #e9b461, #eecc89);
+				border-radius: 25rpx;
+				font-size: 24rpx;
+				font-weight: 500;
+				color: #ffffff;
+			}
+		}
+
+		// 价格
+		.price {
+			color: #ff0000;
+			font-weight: 600;
+
+			&::before {
+				content: '¥';
+				font-size: 20rpx;
+			}
+		}
+
+		.origin-price {
+			color: #c4c4c4;
+			font-size: 24rpx;
+			text-decoration: line-through;
+
+			&::before {
+				content: '¥';
+				font-size: 20rpx;
+			}
+		}
+	}
+</style>

+ 134 - 0
pages/index/components/sh-grid-swiper.vue

@@ -0,0 +1,134 @@
+<template>
+	<!-- 产品分类导航 -->
+	<view class="wrap">
+		<view class="menu-category-box u-m-b-10" :style="list.length <= oneRowNum ? `height:180rpx` : `height:380rpx`">
+			<swiper
+				class="menu-swiper-box"
+				@change="onSwiper"
+				:style="list.length <= oneRowNum ? `height:160rpx` : `height:380rpx`"
+				circular
+				:autoplay="false"
+				:interval="3000"
+				:duration="1000"
+			>
+				<swiper-item class="menu-swiper-item" v-for="(itemList, index) in newList" :key="index">
+					<view class="menu-tab-box u-flex u-flex-wrap">
+						<view
+							class="tab-list u-flex-col u-col-center u-row-center"
+							:style="{ width: 100 / oneRowNum + '%' }"
+							v-for="(item, index) in itemList"
+							:key="index"
+							@tap="$tools.routerTo(item.path)"
+						>
+							<image class="tab-img" :src="item.image"></image>
+							<text class="">{{ item.name }}</text>
+						</view>
+					</view>
+				</swiper-item>
+			</swiper>
+			<view class="menu-category-dots" v-if="newList.length > 1">
+				<text :class="categoryCurrent === index ? 'category-dot-active' : 'category-dot'" v-for="(dot, index) in newList.length" :key="index"></text>
+			</view>
+		</view>
+		</view>
+	</view>
+</template>
+
+<script>
+/**
+ * shGridSwiper-滑动宫格列表
+ * @property {Array} list - 列表数据
+ * @property {Number|String} oneRowNum - 单行数量
+ */
+export default {
+	components: {},
+	data() {
+		return {
+			categoryCurrent: 0 ,//分类轮播下标
+		};
+	},
+	props: {
+		list: {
+			type: Array,
+			default: () => {
+				return [];
+			}
+		},
+		oneRowNum: {
+			type: [Number, String],
+			default: 5
+		}
+	},
+	computed: {
+		newList() {
+			if (this.list.length) {
+				let data = this.$tools.splitData(this.list, this.oneRowNum * 2);
+				return data;
+			}
+		}
+	},
+	methods: {
+		// 轮播
+		onSwiper(e) {
+			this.categoryCurrent = e.detail.current;
+		},
+		jump(path, query) {
+			this.$Router.push({
+				path: path,
+				query: query
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+// 产品分类
+.menu-category-box,
+.menu-swiper-box {
+	position: relative;
+	background: #fff;
+	.menu-swiper-item {
+		background: #fff;
+		height: 100%;
+		width: 100%;
+	}
+	.menu-tab-box {
+		.tab-list {
+			font-size: 26rpx;
+			font-weight: 500;
+			color: rgba(51, 51, 51, 1);
+			margin: 20rpx 0;
+			.tab-img {
+				width: 98rpx;
+				height: 98rpx;
+				margin-bottom: 10rpx;
+			}
+		}
+	}
+
+	.menu-category-dots {
+		display: flex;
+		position: absolute;
+		left: 50%;
+		transform: translateX(-50%);
+		bottom: 10rpx;
+
+		.category-dot {
+			width: 12rpx;
+			height: 12rpx;
+			background: #eeeeee;
+			border-radius: 50%;
+			margin-right: 10rpx;
+		}
+
+		.category-dot-active {
+			width: 12rpx;
+			height: 12rpx;
+			background: #e9b461;
+			border-radius: 50%;
+			margin-right: 10rpx;
+		}
+	}
+}
+</style>

+ 60 - 0
pages/index/components/sh-grid.vue

@@ -0,0 +1,60 @@
+<template>
+	<view class="grid-wap u-m-b-10 u-flex u-flex-wrap u-col-center" v-if="list.length">
+		<view class="grid-item u-flex-col u-row-center u-col-center" v-for="(menu, index) in list" :key="index" @tap="jump(menu)">
+			<image class="tool-img" :src="menu.image" mode="aspectFill"></image>
+			<view class="item-title">{{ menu.name }}</view>
+		</view>
+	</view>
+</template>
+
+<script>
+/**
+ * shGrid-宫格列表
+ * @property {Array} list  - 宫格列表
+ */
+export default {
+	components: {},
+	data() {
+		return {};
+	},
+	computed: {},
+	props: {
+		list: {
+			type: Array,
+			default: () => {
+				return [];
+			}
+		}
+	},
+	created() {},
+	methods: {
+		jump(data) {
+			this.$tools.routerTo(data.path);
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+// 宫格
+.grid-wap {
+	background: #fff;
+	width: 750rpx;
+	padding: 30rpx 0 0;
+	.grid-item {
+		width: 25%;
+		margin-bottom: 40rpx;
+		.tool-img {
+			width: 44rpx;
+			height: 44rpx;
+		}
+		.item-title {
+			font-size: 26rpx;
+			font-weight: 500;
+			color: rgba(153, 153, 153, 1);
+			line-height: 24rpx;
+			padding-top: 20rpx;
+		}
+	}
+}
+</style>

+ 319 - 0
pages/index/components/sh-groupon.vue

@@ -0,0 +1,319 @@
+<template>
+	<!-- 活动商品 -->
+	<view class="activity-wrap u-p-x-20 u-p-b-20  u-m-b-10 groupon-card" v-if="showActivity">
+		<!-- 标题栏 -->
+		<view class="title-box u-flex u-row-between u-p-y-20 groupon-title">
+			<view class="u-flex u-col-center">
+				<view class="title-text u-m-r-20 u-ellipsis-1">{{ detail.name }}</view>
+			</view>
+			<view class="more-box u-flex" @tap="$Router.push('/pages/activity/groupon/list')">
+				<text class="more-text u-m-r-10">更多拼团</text>
+				<text class="iconfont icon-youjiantou-tianchong more-icon"></text>
+			</view>
+		</view>
+		<!-- 活动商品 -->
+		<!-- m -->
+		<scroll-view v-if="grouponType === 1" class="scroll-box" scroll-x scroll-anchoring>
+			<view class="goods-box u-flex">
+				<view class="min-goods u-m-r-14" v-for="mgoods in goodsList" :key="mgoods.id" @tap="jump('/pages/goods/detail', { id: mgoods.id })">
+					<view class="img-box"><image class="img" :src="mgoods.image" mode=""></image></view>
+					<view class="mgoods-card-bottom u-p-20">
+						<view class="goods-title u-m-b-10 u-ellipsis-1">{{ mgoods.title }}</view>
+						<view class="price u-m-b-10">{{ mgoods.groupon_price }}</view>
+						<view class="groupon-num-box u-flex u-col-center" v-if="mgoods.buyers.length">
+							<view class="avatar-box"><image class="avatar-img u-m-r-10" :src="mgoods.buyers[0].avatar" mode=""></image></view>
+							<view class="groupon-num-text">{{ mgoods.sales }}人正在拼</view>
+						</view>
+						<view class="original-price" v-else>¥{{ mgoods.original_price }}</view>
+					</view>
+				</view>
+			</view>
+		</scroll-view>
+		<!--b-->
+		<view
+			v-if="grouponType === 2"
+			class="big-goods  u-flex u-p-20 u-col-top u-m-b-16"
+			v-for="item in goodsList"
+			:key="item.id"
+			@tap="jump('/pages/goods/detail', { id: item.id })"
+		>
+			<image class="goods-img" :src="item.image" mode="aspectFill"></image>
+			<view class=" card-right u-m-l-20 u-flex-col u-row-between">
+				<view class="">
+					<view class="goods-title u-ellipsis-1  u-m-t-10 u-m-b-10">
+						<view class=" sm cu-tag radius title-tag u-m-r-10" style="{ backgroundColor: #FF6600, color: '#fff' }">拼团</view>
+						{{ item.title }}
+					</view>
+					<view v-show="item.subtitle" class="subtitle-text u-m-b-10 u-ellipsis-1">{{ item.subtitle }}</view>
+				</view>
+
+				<view class="u-flex u-m-y-20">
+					<view class="sell-box">
+						<text class=" hot-icon iconfont icon-icon-test"></text>
+						<text class="sell-num">已拼{{ item.sales }}件</text>
+					</view>
+					<text class="group-num">{{ item.activity.rules.team_num || 0 }}人团</text>
+				</view>
+
+				<view class=" u-flex u-row-between u-col-center">
+					<view class="u-flex u-col-bottom font-OPPOSANS">
+						<view class="price u-m-r-10">{{ item.groupon_price }}</view>
+						<view class="origin-price">{{ item.original_price }}</view>
+					</view>
+					<button class="u-reset-button buy-btn" @tap.stop="$Router.push({ path: '/pages/goods/detail', query: { id: item.id } })">马上拼</button>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+/**
+ * 自定义之拼团样式组件
+ * @property {Object} detail - 秒杀商品信息
+ */
+export default {
+	components: {},
+	data() {
+		return {
+			timestamp: 0, //倒计时
+			goodsList: [],
+			showActivity: true, //是否显示活动。
+			grouponType: this.detail.style
+		};
+	},
+	props: {
+		detail: {
+			type: Object,
+			default: () => {
+				return {};
+			}
+		}
+	},
+	watch: {},
+	computed: {},
+	created() {
+		this.getActivityGoodsList();
+	},
+	methods: {
+		// 路由跳转
+		jump(path, parmas) {
+			this.$Router.push({
+				path: path,
+				query: parmas
+			});
+		},
+
+		// 获取拼团商品
+		getActivityGoodsList() {
+			let that = this;
+			that.$http('goods.activity', {
+				activity_id: that.detail.id,
+				need_buyer: 1
+			}).then(res => {
+				if (res.code === 1) {
+					that.goodsList = res.data.goods.data;
+					that.showActivity = that.goodsList.length;
+				} else {
+					that.showActivity = false;
+					console.log(`%cerr:拼团活动已结束`, 'color:green;background:yellow');
+				}
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.groupon-card {
+	background: linear-gradient(#faecca 20%, #ffffff 30%, #ffffff 100%);
+}
+
+.groupon-title {
+	background: url($IMG_URL+'/imgs/tag/groupon_title_bg.png') no-repeat;
+	background-position: center top;
+	background-size: 100% auto;
+}
+.activity-wrap {
+	background-color: #fff;
+	min-height: 300rpx;
+	.title-box {
+		.title-text {
+			font-size: 32rpx;
+			font-weight: bold;
+			color: #333333;
+		}
+		.more-box {
+			.more-text {
+				font-size: 22rpx;
+				font-weight: 500;
+				color: #333333;
+			}
+			.more-icon {
+				font-size: 24rpx;
+				color: #333333;
+			}
+		}
+	}
+
+	.scroll-box,
+	.goods-box {
+		height: 380rpx;
+		width: 100%;
+	}
+}
+
+// 小商品卡片
+.min-goods {
+	width: 220rpx;
+	height: 380rpx;
+	background: #fffaef;
+	box-shadow: 0px 7rpx 8rpx 1rpx rgba(162, 117, 27, 0.24);
+	border-radius: 10rpx;
+	.img-box {
+		width: 220rpx;
+		height: 220rpx;
+		overflow: hidden;
+		position: relative;
+		border-radius: 10rpx 10rpx 0 0;
+		.img {
+			width: 220rpx;
+			height: 220rpx;
+			background-color: #ccc;
+		}
+	}
+	.mgoods-card-bottom {
+		height: 160rpx;
+		background: url($IMG_URL+'/imgs/tag/groupon_goods_bg.png') no-repeat;
+		background-position: bottom center;
+		background-size: 100% 100%;
+	}
+	.goods-title {
+		font-size: 26rpx;
+		font-weight: 500;
+		color: #000000;
+		width: 180rpx;
+		line-height: 26rpx;
+	}
+
+	.price {
+		font-size: 30rpx;
+		font-weight: 500;
+		color: #ff0000;
+		&::before {
+			content: '¥';
+			font-size: 24rpx;
+		}
+	}
+	.original-price {
+		font-size: 20rpx;
+		font-weight: 500;
+		text-decoration: line-through;
+		color: #c4c4c4;
+	}
+	.groupon-num-box {
+		.avatar-box {
+			direction: rtl;
+			unicode-bidi: bidi-override;
+			height: 30rpx;
+			.avatar-img {
+				width: 30rpx;
+				height: 30rpx;
+				border-radius: 50%;
+				background-color: #f5f5f5;
+			}
+		}
+		.groupon-num-text {
+			font-size: 18rpx;
+			font-weight: 500;
+			color: #e9b461;
+		}
+	}
+}
+
+// 大商品卡片
+.big-goods {
+	width: 710rpx;
+	min-height: 260rpx;
+	background: #ffffff;
+	box-shadow: 0px 7rpx 8rpx 1rpx #fffaef;
+	border-radius: 20rpx;
+	.goods-img {
+		width: 220rpx;
+		height: 220rpx;
+		border-radius: 6rpx;
+	}
+	.card-right {
+		width: 430rpx;
+		height: 100%;
+	}
+	.goods-title {
+		font-size: 26rpx;
+		font-weight: 600;
+		width: 400rpx;
+		color: #000000;
+		vertical-align: middle;
+		.title-tag {
+			transform: scale(0.9);
+		}
+	}
+	.subtitle-text {
+		font-size: 22rpx;
+		width: 400rpx;
+		font-weight: 500;
+		color: #666666;
+	}
+	.buy-btn {
+		width: 120rpx;
+		line-height: 50rpx;
+		background: linear-gradient(90deg, #ff6600 0%, #fe832a 100%);
+		border-radius: 25rpx;
+		font-size: 24rpx;
+		font-weight: 500;
+		color: #ffffff;
+	}
+	// 拼团
+	.sell-box {
+		background: rgba(#f9efd6, 0.3);
+		border-radius: 16rpx;
+		line-height: 32rpx;
+		.sell-num {
+			font-size: 20rpx;
+
+			font-weight: 400;
+			color: #ff6904;
+		}
+
+		.hot-icon {
+			font-size: 26rpx;
+			color: #ff6904;
+			margin-right: 8rpx;
+		}
+	}
+	.group-num {
+		font-size: 20rpx;
+
+		font-weight: 500;
+		color: rgba(153, 153, 153, 1);
+		margin-left: 20rpx;
+	}
+	// 价格
+	.price {
+		color: #ff0000;
+		font-weight: 600;
+		&::before {
+			content: '¥';
+			font-size: 20rpx;
+		}
+	}
+	.origin-price {
+		color: #c4c4c4;
+		font-size: 24rpx;
+		text-decoration: line-through;
+		&::before {
+			content: '¥';
+			font-size: 20rpx;
+		}
+	}
+}
+</style>

+ 227 - 0
pages/index/components/sh-hot-goods.vue

@@ -0,0 +1,227 @@
+<template>
+	<!-- 为你推荐 -->
+	<view class="hot-goods u-m-b-10 u-p-x-16">
+		<view class="u-waterfall" v-if="goodsType === 1">
+			<view id="u-left-column" class="u-column">
+				<view class="goods-item u-m-b-16 u-flex u-row-center u-col-center" v-for="leftGoods in leftList" :key="leftGoods.id">
+					<shopro-goods-card
+						:detail="leftGoods"
+						:type="leftGoods.activity_type"
+						:image="leftGoods.image"
+						:title="leftGoods.title"
+						:subtitle="leftGoods.subtitle"
+						:price="leftGoods.price"
+						:originPrice="leftGoods.original_price"
+						:sales="leftGoods.sales"
+						:tagTextList="leftGoods.activity_discounts_tags"
+						@click="$Router.push({ path: '/pages/goods/detail', query: { id: leftGoods.id } })"
+					></shopro-goods-card>
+				</view>
+			</view>
+			<view id="u-right-column" class="u-column">
+				<view class="goods-item  u-m-b-16 u-flex u-row-center u-col-center" v-for="rightGoods in rightList" :key="rightGoods.id">
+					<shopro-goods-card
+						:detail="rightGoods"
+						:type="rightGoods.activity_type"
+						:image="rightGoods.image"
+						:title="rightGoods.title"
+						:subtitle="rightGoods.subtitle"
+						:price="rightGoods.price"
+						:originPrice="rightGoods.original_price"
+						:sales="rightGoods.sales"
+						:tagTextList="rightGoods.activity_discounts_tags"
+						@click="$Router.push({ path: '/pages/goods/detail', query: { id: rightGoods.id } })"
+					></shopro-goods-card>
+				</view>
+			</view>
+		</view>
+		<!-- m -->
+		<view class="big-card-wrap u-p-10" v-if="goodsType === 2">
+			<block v-for="item in goodsList" :key="item.id">
+				<sh-goods-card
+					:detail="item"
+					:type="item.activity_type"
+					:image="item.image"
+					:title="item.title"
+					:subtitle="item.subtitle"
+					:price="item.price"
+					:originPrice="item.original_price"
+					:sales="item.sales"
+					:tagTextList="item.activity_discounts_tags"
+					@click="$Router.push({ path: '/pages/goods/detail', query: { id: item.id } })"
+				></sh-goods-card>
+			</block>
+		</view>
+		<button v-show="total > perPage" class="u-reset-button refresh-btn u-m-y-20 u-flex u-col-center u-row-center" @tap.stop="loadMore">
+			<text class="u-m-r-6 u-iconfont uicon-reload" style="font-size: 28rpx;color: #999" :class="{ 'refresh-active': isRefresh }"></text>
+			{{ listParams.page >= lastPage ? '收起' : '加载更多' }}
+		</button>
+	</view>
+</template>
+
+<script>
+/**
+ * 自定义之为你推荐
+ * @property {Object} detail - 推荐商品信息
+ */
+import shGoodsCard from './sh-goods-card.vue';
+export default {
+	components: {
+		shGoodsCard
+	},
+	data() {
+		return {
+			listParams: {
+				page: 1
+			}, //当前分页
+			lastPage: 1, //总分页
+			total: 0, //总商品数
+			perPage: 0, //单页商品数
+			goodsList: [],
+			isRefresh: false,
+
+			// 瀑布流 350-330
+			addTime: 100, //排序间隙时间
+			leftHeight: 0,
+			rightHeight: 0,
+			leftList: [],
+			rightList: [],
+			tempList: [],
+
+			goodsType: this.detail.style // 商品模板
+		};
+	},
+
+	props: {
+		detail: {
+			type: Object,
+			default: () => {}
+		}
+	},
+	created() {
+		if (this.detail.id) {
+			this.listParams.category_id = this.detail.id;
+			this.getGoodsList();
+		}
+		if (this.detail.ids) {
+			this.listParams.goods_ids = this.detail.ids;
+			this.getGoodsList();
+		}
+	},
+	methods: {
+		// 瀑布流相关
+		async splitData() {
+			if (!this.tempList.length) return;
+			let item = this.tempList[0];
+			if (!item) return;
+
+			// 分左右
+			if (this.leftHeight < this.rightHeight) {
+				this.leftHeight += item.activity_discounts_tags?.length ? 350 : 330;
+				this.leftList.push(item);
+			} else if (this.leftHeight > this.rightHeight) {
+				this.rightHeight += item.activity_discounts_tags?.length ? 350 : 330;
+				this.rightList.push(item);
+			} else {
+				this.leftHeight += item.activity_discounts_tags?.length ? 350 : 330;
+				this.leftList.push(item);
+			}
+
+			// 移除临时列表的第一项,如果临时数组还有数据,继续循环
+			this.tempList.splice(0, 1);
+			if (this.tempList.length) {
+				setTimeout(() => {
+					this.splitData();
+				}, this.addTime);
+			}
+		},
+		clear() {
+			this.leftList = [];
+			this.rightList = [];
+			this.leftHeight = 0;
+			this.rightHeight = 0;
+			this.tempList = [];
+		},
+
+		// 商品列表
+		getGoodsList() {
+			let that = this;
+			that.$http('goods.lists', this.listParams).then(res => {
+				if (res.code === 1) {
+					this.lastPage = res.data.last_page;
+					this.total = res.data.total;
+					this.perPage = res.data.per_page;
+					this.isRefresh = false;
+					that.goodsList = [...that.goodsList, ...res.data.data];
+					that.tempList = res.data.data;
+					that.goodsList.length && that.splitData();
+				}
+			});
+		},
+
+		// 加载更多
+		loadMore() {
+			if (!this.isRefresh) {
+				// 加载更多
+				if (this.listParams.page < this.lastPage) {
+					this.isRefresh = true;
+					this.listParams.page += 1;
+					this.getGoodsList();
+				} else {
+					// 重置为1页数据
+					this.listParams.page = 1;
+					this.lastPage = 1;
+					this.goodsList = [];
+					this.clear();
+					this.getGoodsList();
+				}
+			}
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+@mixin vue-flex($direction: row) {
+	/* #ifndef APP-NVUE */
+	display: flex;
+	flex-direction: $direction;
+	/* #endif */
+}
+.u-waterfall {
+	@include vue-flex;
+	flex-direction: row;
+	align-items: flex-start;
+}
+
+.u-column {
+	@include vue-flex;
+	flex: 1;
+	flex-direction: column;
+	height: auto;
+}
+
+.u-image {
+	width: 100%;
+}
+// 为你推荐
+.hot-goods {
+	background: none;
+	.refresh-btn {
+		margin-left: 50%;
+		transform: translateX(-50%);
+		width: 156rpx;
+		line-height: 50rpx;
+		background: #ffffff;
+		border-radius: 25rpx;
+		font-size: 22rpx;
+		font-weight: 500;
+		color: #999999;
+		white-space: nowrap;
+	}
+	.refresh-active {
+		transform: rotate(360deg);
+		transition: all linear 0.5s;
+	}
+}
+</style>

+ 108 - 0
pages/index/components/sh-live.vue

@@ -0,0 +1,108 @@
+<template>
+	<view class="live-el u-m-p-20 u-m-b-10" v-if="showLive">
+		<view class="head">
+			<text class="head-title">{{ detail.name }}</text>
+			<view class="head-more u-flex u-col-center" @tap="$Router.push('/pages/app/live/list')">
+				<text class="u-m-r-10 more-text">更多直播</text>
+				<text class="iconfont icon-youjiantou-tianchong more-icon"></text>
+			</view>
+		</view>
+
+		<!-- 大图 -->
+		<view v-if="liveType == 1" v-for="live in liveList" :key="live.id"><shopro-live-card :type="1" :detail="live"></shopro-live-card></view>
+
+		<!-- 小图 -->
+		<view class="content-two" v-if="liveType == 2">
+			<view class="content-two__item" v-for="live in liveList" :key="live.id">
+				<shopro-live-card :type="2" :detail="live" :wh="345">
+					<block slot="liveGoods"><text></text></block>
+				</shopro-live-card>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+/**
+ * 自定义之直播卡片
+ * @property {Object} detail - 直播信息
+ */
+export default {
+	components: {},
+	data() {
+		return {
+			liveList: [],
+			showLive: this.detail.ids,
+			liveType: this.detail.style
+		};
+	},
+	props: {
+		detail: {}
+	},
+	created() {
+		this.showLive && this.getLiveList();
+	},
+	computed: {},
+	methods: {
+		// 直播列表
+		getLiveList() {
+			let that = this;
+			that.$http('common.live', {
+				type: 'all',
+				ids: that.detail.ids
+			}).then(res => {
+				if (res.code === 1) {
+					that.liveList = res.data;
+				}
+			});
+		},
+		goRoom(live) {
+			wx.navigateTo({
+				url: `plugin-private://wx2b03c6e691cd7370/pages/live-player-plugin?room_id=${live.room_id}`
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+.live-el {
+	background: #fff;
+	padding: 30rpx 20rpx;
+	.head {
+		display: flex;
+		justify-content: space-between;
+		align-items: center;
+		&-title {
+			font-size: 32rpx;
+			font-weight: bold;
+			color: rgba(51, 51, 51, 1);
+		}
+		&-more {
+			.more-text {
+				font-size: 22rpx;
+				font-weight: 500;
+				color: #333333;
+			}
+			.more-icon {
+				font-size: 24rpx;
+				color: #333333;
+			}
+		}
+	}
+	// 双图直播
+	.content-two {
+		width: 710rpx;
+		display: flex;
+		flex-wrap: wrap;
+		&__item {
+			margin-right: 20rpx;
+			margin-top: 20rpx;
+			width: 345rpx;
+			&:nth-child(2n) {
+				margin-right: 0;
+			}
+		}
+	}
+}
+</style>

+ 150 - 0
pages/index/components/sh-order-card.vue

@@ -0,0 +1,150 @@
+<template>
+	<!-- 订单卡片 -->
+	<view class="sh-order-box u-flex u-m-b-10 u-p-r-20">
+		<view class="order-box u-flex">
+			<view class="order-item u-flex-col " @tap="jump('/pages/order/list', { type: order.type })" v-for="order in orderNav" :key="order.id">
+				<view class="u-flex-col item-box u-col-center">
+					<image class="order-img" :src="$IMG_URL + order.img" mode=""></image>
+					<text class="item-title">{{ order.title }}</text>
+					<view class="badge" v-if="orderNum && orderNum[order.type]">{{ orderNum[order.type] }}</view>
+				</view>
+			</view>
+			<view class="order-item u-flex-col " @tap="jump('/pages/order/after-sale/list')">
+				<view class="u-flex-col item-box u-col-center">
+					<image class="order-img" :src="$IMG_URL + '/imgs/user/tab55.png'" mode=""></image>
+					<text class="item-title">退换货</text>
+				</view>
+			</view>
+		</view>
+
+		<view class="order-item u-flex-col all-order " @tap="jump('/pages/order/list', { type: 'all' })">
+			<image class="cut-off--line" :src="$IMG_URL + '/imgs/user/cut_off_line.png'" mode=""></image>
+			<view class="u-flex-col item-box u-col-center">
+				<image class="order-img" :src="$IMG_URL + '/imgs/user/all_order.png'" mode="aspectFill"></image>
+				<text class="item-title">全部订单</text>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+/**
+ * 订单中心卡片
+ *
+ */
+import { mapMutations, mapActions, mapState, mapGetters } from 'vuex';
+export default {
+	components: {},
+	data() {
+		return {
+			orderNav: [
+				{
+					id: 1,
+					title: '待付款',
+					img: '/imgs/user/tab11.png',
+					type: 'nopay'
+				},
+				// {
+				// 	id: 2,
+				// 	title: '待发货',
+				// 	img: this.$IMG_URL + '/imgs/user/tab22.png',
+				// 	type: 'nosend'
+				// },
+				{
+					id: 3,
+					title: '待收货',
+					img: '/imgs/user/tab33.png',
+					type: 'noget'
+				},
+				{
+					id: 4,
+					title: '待评价',
+					img: '/imgs/user/tab44.png',
+					type: 'nocomment'
+				}
+				// {
+				// 	id: 5,
+				// 	title: '退换货',
+				// 	img: this.$IMG_URL + '/imgs/user/tab55.png',
+				// 	type: 'aftersale'
+				// }
+			]
+		};
+	},
+	computed: {
+		...mapGetters(['isLogin', 'userOtherData']),
+		orderNum() {
+			return this.userOtherData.order_num || 0;
+		}
+	},
+	methods: {
+		jump(path, query) {
+			this.$Router.push({
+				path: path,
+				query: query
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+.badge {
+	position: absolute;
+	/* #ifndef APP-NVUE */
+	display: inline-flex;
+	/* #endif */
+	justify-content: center;
+	align-items: center;
+	line-height: 24rpx;
+	padding: 4rpx 10rpx;
+	border-radius: 100rpx;
+	color: #fff;
+	font-size: 24rpx;
+	z-index: 9;
+	background-color: $u-type-error;
+	transform: scale(0.8);
+	transform-origin: center center;
+	left: 100rpx;
+	top: -6rpx;
+	white-space: nowrap;
+}
+// 订单卡片
+.sh-order-box {
+	height: 180rpx;
+	background: #fff;
+	.order-box {
+		flex: 4;
+	}
+	.all-order {
+		position: relative;
+		.cut-off--line {
+			position: absolute;
+			top: 50%;
+			transform: translateY(-50%);
+			right: (750rpx/5) - 15rpx;
+			width: 30rpx;
+			height: 136rpx;
+		}
+	}
+
+	.order-item {
+		flex: 1;
+		.item-box {
+			position: relative;
+		}
+		.order-img {
+			width: 46rpx;
+			height: 46rpx;
+		}
+
+		.item-title {
+			font-size: 26rpx;
+			font-weight: 500;
+			color: rgba(153, 153, 153, 1);
+			line-height: 24rpx;
+			margin-top: 20rpx;
+		}
+	}
+}
+</style>

+ 46 - 0
pages/index/components/sh-richtext.vue

@@ -0,0 +1,46 @@
+<template>
+	<view class="sh-richtext-box u-m-b-10" v-if="richText.content"><u-parse :html="richText.content"></u-parse></view>
+</template>
+
+<script>
+/**
+ * 自定义之富文本卡片
+ * @property {Number|String} id - 富文本id
+ */
+export default {
+	components: {},
+	data() {
+		return {
+			richText: ''
+		};
+	},
+	computed: {},
+	props: {
+		richId: {
+			type: [Number, String],
+			default: 0
+		}
+	},
+	created() {
+		this.richId && this.getRichText();
+	},
+	methods: {
+		getRichText() {
+			this.$http('common.richText', {
+				id: this.richId
+			}).then(res => {
+				if (res.code === 1) {
+					this.richText = res.data;
+				}
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+.sh-richtext-box {
+	background: #fff;
+	padding: 30rpx;
+}
+</style>

+ 46 - 0
pages/index/components/sh-search.vue

@@ -0,0 +1,46 @@
+<template>
+	<view class="search u-flex u-row-center u-col-center u-m-b-10 u-p-x-20" @tap="$Router.push('/pages/public/search')">
+		<view class="search-content u-flex u-col-center">
+			<text class="u-iconfont uicon-search search-icon"></text>
+			<view class="pl-text">搜索</view>
+		</view>
+	</view>
+</template>
+
+<script>
+/**
+ * 自定义之搜索样式卡片
+ * @property {Object} detail - 搜索信息
+ */
+export default {
+	components: {},
+	data() {
+		return {};
+	},
+	computed: {},
+	methods: {}
+};
+</script>
+
+<style lang="scss">
+.search {
+	height: 100rpx;
+	width: 750rpx;
+	background: #fff;
+	.search-content {
+		width: 710rpx;
+		padding: 0 18rpx;
+		background-color: #f2f2f2;
+		border-radius: 100rpx;
+		height: 64rpx;
+		.search-icon {
+			color: #606266;
+			size: 34rpx;
+		}
+		.pl-text {
+			color: $u-tips-color;
+			margin-left: 10rpx;
+		}
+	}
+}
+</style>

+ 300 - 0
pages/index/components/sh-seckill.vue

@@ -0,0 +1,300 @@
+<template>
+	<!-- 活动商品 -->
+	<view class="activity-wrap u-p-x-20 u-p-b-20  u-m-b-10 seckill-card" v-if="showActivity">
+		<!-- 标题栏 -->
+		<view class="title-box u-flex u-row-between u-p-y-20  seckill-title">
+			<view class="u-flex u-col-center">
+				<view class="title-text u-m-r-20 u-ellipsis-1">{{ detail.name }}</view>
+				<u-count-down
+					class="count-down-demo"
+					:timestamp="timestamp"
+					separator-color="#ffbbbb "
+					bg-color="#ffbbbb "
+					ref="uCountDown"
+					color="#fff"
+					@end="seckillEnd"
+					autoplay
+				></u-count-down>
+			</view>
+			<view class="more-box u-flex" @tap="$Router.push('/pages/activity/seckill/list')">
+				<text class="more-text u-m-r-10">更多抢购</text>
+				<text class="iconfont icon-youjiantou-tianchong more-icon"></text>
+			</view>
+		</view>
+		<!-- 活动商品 -->
+		<!-- m -->
+		<scroll-view v-if="seckillType === 1" class="scroll-box" scroll-x scroll-anchoring>
+			<view class="goods-box u-flex">
+				<view class="min-goods u-m-r-14" v-for="mgoods in goodsList" :key="mgoods.id" @tap="jump('/pages/goods/detail', { id: mgoods.id })">
+					<view class="img-box"><image class="img" :src="mgoods.image" mode=""></image></view>
+					<view class="mgoods-card-bottom u-p-20">
+						<view class="goods-title  u-m-b-10 u-ellipsis-1">{{ mgoods.title }}</view>
+						<view class="price-box font-OPPOSANS">
+							<view class="price u-m-b-10">{{ mgoods.price }}</view>
+							<view class="original-price">¥{{ mgoods.original_price }}</view>
+						</view>
+					</view>
+				</view>
+			</view>
+		</scroll-view>
+		<!--b-->
+		<view
+			v-if="seckillType === 2"
+			class="big-goods  u-flex u-p-20 u-col-top u-m-b-16"
+			v-for="item in goodsList"
+			:key="item.id"
+			@tap="jump('/pages/goods/detail', { id: item.id })"
+		>
+			<image class="goods-img" :src="item.image" mode="aspectFill"></u-image>
+			<view class=" card-right u-m-l-20 u-flex-col u-row-between">
+				<view class="">
+					<view class="goods-title u-ellipsis-1 u-m-t-10 u-m-b-10">
+						<view class=" sm cu-tag bg-red radius title-tag u-m-r-10" >秒杀</view>
+						{{ item.title }}
+					</view>
+					<view v-show="item.subtitle" class="subtitle-text u-m-b-10 u-ellipsis-1">{{ item.subtitle }}</view>
+				</view>
+
+				<view class="u-flex u-m-y-20">
+					<u-line-progress
+						style="width:210rpx;"
+						height="18"
+						:show-percent="false"
+						:percent="Number(item.percent)"
+						inactive-color=" #e7e7e7"
+						active-color="#ffbbbb "
+					></u-line-progress>
+
+					<view class="progress-text">已售出{{ item.sales }}件</view>
+				</view>
+
+				<view class=" u-flex u-row-between u-col-center">
+					<view class="u-flex u-col-bottom">
+						<view class="price u-m-r-10">{{ item.price }}</view>
+						<view class="origin-price">{{ item.original_price }}</view>
+					</view>
+					<button class="u-reset-button buy-btn">去抢购</button>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+/**
+ * 自定义之秒杀样式组件
+ * @property {Object} detail - 秒杀商品信息
+ */
+export default {
+	components: {},
+	data() {
+		return {
+			timestamp: 0, //倒计时
+			goodsList: [],
+			showActivity: true, //是否显示活动。
+			seckillType: this.detail.style
+		};
+	},
+	props: {
+		detail: {
+			type: Object,
+			default: () => {
+				return {};
+			}
+		}
+	},
+	watch: {},
+	computed: {},
+	created() {
+		this.getActivityGoodsList();
+	},
+	methods: {
+		// 路由跳转
+		jump(path, parmas) {
+			this.$Router.push({
+				path: path,
+				query: parmas
+			});
+		},
+
+		// 秒杀计时结束
+		seckillEnd() {
+			this.showActivity = false;
+		},
+
+		// 获取秒杀商品
+		getActivityGoodsList() {
+			let that = this;
+			that.$http('goods.activity', {
+				activity_id: that.detail.id
+			}).then(res => {
+				if (res.code === 1) {
+					that.goodsList = res.data.goods.data;
+					that.goodsList.map(item => {
+						item.percent = item.stock + item.sales > 0 ? ((item.sales / (item.sales + item.stock)) * 100).toFixed(2) : 0;
+					});
+					let nowTime = new Date().getTime();
+					let endTime = res.data.endtime * 1000;
+					that.timestamp = (endTime - nowTime) / 1000;
+					that.timestamp > 0 ? that.$refs.uCountDown.start() : (that.showActivity = false);
+				} else {
+					that.showActivity = false;
+					console.log(`%cerr:秒杀活动已结束`, 'color:green;background:yellow');
+				}
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.seckill-card {
+	background: linear-gradient(#ffebec 20%, #fff 30%, #fff 100%);
+}
+
+.seckill-title {
+	background: url($IMG_URL+'/imgs/tag/seckill_title_bg.png') no-repeat;
+	background-position: center top;
+	background-size: 100% auto;
+}
+.activity-wrap {
+	background-color: #fff;
+	min-height: 300rpx;
+	.title-box {
+		.title-text {
+			font-size: 32rpx;
+			font-weight: bold;
+			color: #333333;
+		}
+		.more-box {
+			.more-text {
+				font-size: 22rpx;
+				font-weight: 500;
+				color: #333333;
+			}
+			.more-icon {
+				font-size: 24rpx;
+				color: #333333;
+			}
+		}
+	}
+
+	.scroll-box,
+	.goods-box {
+		height: 380rpx;
+		width: 100%;
+	}
+}
+
+// 小商品卡片
+.min-goods {
+	width: 220rpx;
+	height: 380rpx;
+	background: #fff7f7;
+	box-shadow: 0px 7rpx 7rpx 0px rgba(255, 80, 94, 0.32);
+	border-radius: 10rpx;
+	.img-box {
+		width: 220rpx;
+		height: 220rpx;
+		overflow: hidden;
+		position: relative;
+		border-radius: 10rpx 10rpx 0 0;
+		.img {
+			width: 220rpx;
+			height: 220rpx;
+			background-color: #ccc;
+		}
+	}
+	.mgoods-card-bottom {
+		height: 160rpx;
+		background: url($IMG_URL+'/imgs/tag/seckill_goods_bg.png') no-repeat;
+		background-position: bottom center;
+		background-size: 100% 100%;
+	}
+	.goods-title {
+		font-size: 26rpx;
+		font-weight: 500;
+		color: #000000;
+		line-height: 26rpx;
+	}
+	.price-box {
+		.price {
+			font-size: 30rpx;
+			font-weight: 500;
+			color: #ff0000;
+			&::before {
+				content: '¥';
+				font-size: 24rpx;
+			}
+		}
+		.original-price {
+			font-size: 20rpx;
+			font-weight: 500;
+			text-decoration: line-through;
+			color: #c4c4c4;
+		}
+	}
+}
+
+// 大商品卡片
+.big-goods {
+	width: 710rpx;
+	min-height: 260rpx;
+	background: #ffffff;
+	box-shadow: 0px 7rpx 8rpx 1rpx rgba(254, 76, 29, 0.05);
+	border-radius: 20rpx;
+	.goods-img{
+		width: 220rpx;
+		height: 220rpx;
+		border-radius: 6rpx;
+	}
+	.card-right {
+		width: 430rpx;
+		height: 100%;
+	}
+	.goods-title {
+		font-size: 26rpx;
+		font-weight: 600;
+		width: 400rpx;
+		color: #000000;
+	}
+	.subtitle-text {
+		font-size: 22rpx;
+		width: 400rpx;
+		font-weight: 500;
+		color: #666666;
+	}
+	.buy-btn {
+		width: 120rpx;
+		line-height: 50rpx;
+		background: linear-gradient(90deg, #d01325, #ed3c30);
+		border-radius: 25rpx;
+		font-size: 24rpx;
+		font-weight: 500;
+		color: #ffffff;
+	}
+	.progress-text {
+		color: #c4c4c4;
+		font-size: 20rpx;
+		margin-left: 25rpx;
+	}
+	// 价格
+	.price {
+		color: #ff0000;
+		font-weight: 600;
+		&::before {
+			content: '¥';
+			font-size: 20rpx;
+		}
+	}
+	.origin-price {
+		color: #c4c4c4;
+		font-size: 24rpx;
+		text-decoration: line-through;
+		&::before {
+			content: '¥';
+			font-size: 20rpx;
+		}
+	}
+}
+</style>

+ 64 - 0
pages/index/components/sh-title-card.vue

@@ -0,0 +1,64 @@
+<template>
+	<view class="sh-title-card u-m-b-10">
+		<view class="title-box">
+			<image class="title-bg" :src="bgImage" mode="aspectFill"></image>
+			<view class="title-text" :style="{ color: titleColor }">{{ title }}</view>
+		</view>
+	</view>
+</template>
+
+<script>
+/**
+ * shTitleCard 组件标题栏
+ * @property {String} bgImage - 背景图
+ * @property {String} title - 标题
+ * @property {String} titleColor - 标题颜色
+ */
+export default {
+	components: {},
+	data() {
+		return {};
+	},
+	computed: {},
+	props: {
+		bgImage: {
+			type: String,
+			default: ''
+		},
+		title: {
+			type: String,
+			default: ''
+		},
+		titleColor: {
+			type: String,
+			default: ''
+		}
+	},
+	methods: {}
+};
+</script>
+
+<style lang="scss">
+.sh-title-card {
+	width: 750rpx;
+}
+.title-box {
+	width: 710rpx;
+	height: 88rpx;
+	margin: 0 auto;
+	position: relative;
+	border-radius: 30rpx;
+
+	.title-bg {
+		width: 100%;
+		height: 100%;
+	}
+	.title-text {
+		position: absolute;
+		top: 50%;
+		left: 50%;
+		transform: translate(-50%, -50%);
+		font-weight: bold;
+	}
+}
+</style>

+ 106 - 0
pages/index/components/sh-wallet.vue

@@ -0,0 +1,106 @@
+<template>
+	<!-- 钱包卡片 -->
+	<view class="sh-wallet-box u-flex u-m-b-10 u-p-r-20">
+		<view class="u-flex wallet-left">
+			<view class="wallet-item u-flex-col u-col-center" @tap="jump('/pages/user/wallet/index')">
+				<text class="wallet-item__detail item-balance u-ellipsis-1">{{ userInfo.money || '0' }}</text>
+				<text class="wallet-item__title">账户余额</text>
+			</view>
+			<view class="wallet-item u-flex-col u-col-center" @tap="jump('/pages/user/wallet/score-balance')">
+				<text class="wallet-item__detail item-score u-ellipsis-1">{{ userInfo.score || '0' }}</text>
+				<text class="wallet-item__title">积分</text>
+			</view>
+			<view class="wallet-item u-flex-col u-col-center" @tap="jump('/pages/app/coupon/list')">
+				<text class="wallet-item__detail item-coupon u-ellipsis-1">{{ userOtherData.coupons_num || '0' }}</text>
+				<text class="wallet-item__title">优惠券</text>
+			</view>
+		</view>
+		<view class="wallet-item u-flex-col wallet-right u-col-center" @tap="jump('/pages/user/wallet/index')">
+			<image class="cut-off--line" :src="$IMG_URL + '/imgs/user/cut_off_line.png'" mode=""></image>
+			<image class="wallet-img" :src="$IMG_URL + '/imgs/user/wallet.png'" mode="aspectFill"></image>
+			<text class="wallet-item__title">我的钱包</text>
+		</view>
+	</view>
+</template>
+
+<script>
+/**
+ * 钱包样式卡片
+ */
+import { mapMutations, mapActions, mapState, mapGetters } from 'vuex';
+export default {
+	components: {},
+	data() {
+		return {};
+	},
+	computed: {
+		...mapGetters(['userInfo', 'userOtherData'])
+	},
+	methods: {
+		jump(path, query) {
+			this.$Router.push({
+				path: path,
+				query: query
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+// 钱包卡片
+.sh-wallet-box {
+	background: #fff;
+	height: 180rpx;
+	position: relative;
+	.wallet-left {
+		flex: 4;
+	}
+	.wallet-right {
+		position: relative;
+		.cut-off--line {
+			position: absolute;
+			top: 50%;
+			transform: translateY(-50%);
+			right: (750rpx/5) - 15rpx;
+			width: 30rpx;
+			height: 136rpx;
+		}
+	}
+	.wallet-item {
+		flex: 1;
+		.wallet-img {
+			width: 46rpx;
+			height: 46rpx;
+		}
+		.wallet-item__detail {
+			font-size: 28rpx;
+			width: 180rpx;
+			text-align: center;
+			font-weight: 500;
+			color: rgba(168, 112, 13, 1);
+		}
+		.wallet-item__title {
+			font-size: 26rpx;
+			font-weight: 400;
+			color: rgba(153, 153, 153, 1);
+			margin-top: 20rpx;
+		}
+		.item-balance::after {
+			content: '元';
+			font-size: 16rpx;
+			margin-left: 4rpx;
+		}
+		.item-score::after {
+			content: '个';
+			font-size: 14rpx;
+			margin-left: 4rpx;
+		}
+		.item-coupon::after {
+			content: '张';
+			font-size: 16rpx;
+			margin-left: 4rpx;
+		}
+	}
+}
+</style>

+ 189 - 0
pages/index/index.vue

@@ -0,0 +1,189 @@
+<!-- 首页 -->
+<template>
+	<view class="home-wrap u-m-b-20">
+		<!-- 空白页 -->
+		<!-- #ifdef APP-PLUS -->
+		<u-no-network @retry="init"></u-no-network>
+		<!-- #endif -->
+		<shopro-empty v-if="!hasTemplate" :image="$IMG_URL + '/imgs/empty/template_empty.png'" tipText="暂未找到模板,请前往装修~">
+		</shopro-empty>
+
+		<view v-else-if="isConnected && isRefresh" class="content-box">
+			<!-- 导航栏 -->
+			<home-head v-if="headSwiperList && headSwiperList.length" :isScorll="isScorll" borderRadius="0"
+				:navTitle="initShop.name" :list="headSwiperList"></home-head>
+			<!-- 自定义模块 -->
+			<view class="template-box">
+				<block v-for="(item, index) in homeTemplate" :key="item.id">
+					<!-- 轮播 -->
+					<sh-banner v-if="item.type === 'banner' && index !== 0" :Px="item.content.x" :Py="item.content.y"
+						:borderRadius="item.content.radius" :height="item.content.height" :list="item.content.list">
+					</sh-banner>
+
+					<!-- 搜索 -->
+					<sh-search v-if="item.type === 'search'"></sh-search>
+
+					<!-- 滑动宫格 -->
+					<sh-grid-swiper v-if="item.type === 'menu'" :list="item.content.list"
+						:oneRowNum="item.content.style"></sh-grid-swiper>
+
+					<!-- 推荐商品 -->
+					<sh-hot-goods v-if="item.type === 'goods-list' || item.type === 'goods-group'"
+						:detail="item.content"></sh-hot-goods>
+					<!-- 广告魔方 -->
+					<sh-adv v-if="item.type === 'adv'" :detail="item.content"></sh-adv>
+					<!-- 优惠券 -->
+					<sh-coupon v-if="item.type === 'coupons'" :detail="item.content"></sh-coupon>
+					<!-- 秒杀-->
+					<sh-seckill v-if="item.type === 'seckill'" :detail="item.content"></sh-seckill>
+					<!-- 拼团 -->
+					<sh-groupon v-if="item.type === 'groupon'" :detail="item.content"></sh-groupon>
+					<!-- 富文本 -->
+					<sh-richtext v-if="item.type === 'rich-text'" :richId="item.content.id"></sh-richtext>
+					<!-- 功能标题 -->
+					<sh-title-card v-if="item.type === 'title-block'" :title="item.content.name"
+						:bgImage="item.content.image" :titleColor="item.content.color"></sh-title-card>
+					<!-- 直播 -->
+					<!-- #ifdef MP-WEIXIN -->
+					<sh-live v-if="item.type === 'live' && HAS_LIVE" :detail="item.content"></sh-live>
+					<!-- #endif -->
+				</block>
+			</view>
+
+			<!-- 分类选项卡 -->
+			<sh-category-tabs
+				v-if="categoryTabsData && categoryTabsData.category_arr && categoryTabsData.category_arr.length"
+				:enable="enable" :styleType="categoryTabsData.style" :tabsList="categoryTabsData.category_arr">
+			</sh-category-tabs>
+			<!-- 登录提示 -->
+			<shopro-auth-modal></shopro-auth-modal>
+			<!-- 悬浮按钮 -->
+			<shopro-float-btn></shopro-float-btn>
+			<!-- 连续弹窗提醒 -->
+			<shopro-notice-modal v-if="!showPrivacy && isLogin"></shopro-notice-modal>
+			<!-- 隐私协议 -->
+			<!-- #ifdef APP-PLUS -->
+			<privacy-modal v-if="initShop && initShop.name" v-model="showPrivacy"></privacy-modal>
+			<!-- #endif -->
+			<!-- #ifdef H5 -->
+			<view class="tabbar-hack" style="height: 120rpx; width:100%;"></view>
+			<!-- #endif -->
+		</view>
+		<!-- <shopro-tabbar></shopro-tabbar> -->
+	</view>
+</template>
+
+<script>
+	import shBanner from './components/sh-banner.vue';
+	import shGridSwiper from './components/sh-grid-swiper.vue';
+	import shHotGoods from './components/sh-hot-goods.vue';
+	import shAdv from './components/sh-adv.vue';
+	import shCoupon from './components/sh-coupon.vue';
+	import shSeckill from './components/sh-seckill.vue';
+	import shGroupon from './components/sh-groupon.vue';
+	import shRichtext from './components/sh-richtext.vue';
+	import shTitleCard from './components/sh-title-card.vue';
+	import shSearch from './components/sh-search.vue';
+	import shCategoryTabs from './components/sh-category-tabs.vue';
+
+	import privacyModal from './index/privacy-modal.vue';
+	import homeHead from './index/home-head.vue';
+
+	// #ifdef MP-WEIXIN
+	import { HAS_LIVE } from '@/env';
+	import shLive from './components/sh-live.vue';
+	// #endif
+
+	import { mapMutations, mapActions, mapState, mapGetters } from 'vuex';
+	export default {
+		components: {
+			shBanner,
+			shGridSwiper,
+			shGroupon,
+			shHotGoods,
+			shAdv,
+			shCoupon,
+			shSeckill,
+			shRichtext,
+			shTitleCard,
+			shSearch,
+			shCategoryTabs,
+
+			privacyModal,
+			homeHead,
+
+			// #ifdef MP-WEIXIN
+			shLive
+			// #endif
+		},
+		data() {
+			return {
+				// #ifdef MP-WEIXIN
+				HAS_LIVE: HAS_LIVE,
+				// #endif
+				isRefresh: true,
+
+				enable: false, //是否开启吸顶。
+				isConnected: true, //是否有网
+				showPrivacy: false, //协议
+				isScorll: false
+			};
+		},
+		computed: {
+			...mapGetters(['initShop', 'homeTemplate', 'hasTemplate', 'isLogin']),
+			// 头部模块数据
+			headSwiperList() {
+				if (this.homeTemplate?.length) {
+					return this.homeTemplate[0]?.content?.list;
+				}
+			},
+			// 分类选项卡数据
+			categoryTabsData() {
+				if (this.homeTemplate?.length) {
+					return this.homeTemplate[this.homeTemplate.length - 1]?.content;
+				}
+			}
+		},
+		onPullDownRefresh() {
+			this.init();
+		},
+		onPageScroll(e) {
+			this.isScorll = e.scrollTop > 100 ? true : false;
+		},
+		onShow() {
+			let that = this;
+			this.enable = true;
+			this.isLogin && this.getCartList();
+			// 网络变化检测
+			uni.onNetworkStatusChange(res => {
+				this.isConnected = res.isConnected;
+				res.isConnected && this.init();
+			});
+		},
+		onHide() {
+			this.enable = false;
+		},
+		onLoad() {
+			// #ifdef APP-VUE
+			// app隐私协议弹窗
+			if (!plus.runtime.isAgreePrivacy()) {
+				this.showPrivacy = true;
+				this.showNoticeModal = false;
+			}
+			// #endif
+		},
+		methods: {
+			...mapActions(['appInit', 'getTemplate', 'getCartList']),
+			// 初始化
+			init() {
+				this.isRefresh = false;
+				return Promise.all([this.getTemplate()]).then(() => {
+					uni.stopPullDownRefresh();
+					this.isRefresh = true;
+				});
+			}
+		}
+	};
+</script>
+
+<style lang="scss"></style>

+ 173 - 0
pages/index/index/home-head.vue

@@ -0,0 +1,173 @@
+<template>
+	<!-- 首页标题栏,轮播图整合插件 -->
+	<view class="banner-swiper-wrap u-m-b-10">
+		<!-- 标题栏 -->
+		<view class="u-navbar u-navbar-fixed" :style="[navbarStyle]">
+			<view class="u-status-bar" :style="{ height: statusBarHeight + 'px' }"></view>
+			<view class="u-navbar-inner" :style="[navbarInnerStyle]">
+				<view class="u-back-wrap">
+					<view class="u-icon-wrap u-back-text u-line-1" :style="[navTitleStyle]">中宏商城</view>
+				</view>
+				<view hover-class="hover-search" class="search-box u-flex u-row-center u-col-center u-m-r-30" @tap="$Router.push('/pages/public/search')">
+					<view class="u-iconfont uicon-search" style="color: #fff"></view>
+				</view>
+			</view>
+		</view>
+		<!-- 轮播组件 -->
+		<swiper class="screen-swiper square-dot" @change="onChange" style="height: 520rpx" :indicator-dots="true" :circular="true" :autoplay="true" interval="5000" duration="500">
+			<swiper-item v-for="(item, index) in list" :key="index" @tap="onSwiper(index)"><image :src="item.image" mode="aspectFill"></image></swiper-item>
+		</swiper>
+	</view>
+</template>
+
+<script>
+// 获取系统状态栏的高度
+let systemInfo = uni.getSystemInfoSync();
+let menuButtonInfo = {};
+// #ifdef MP-WEIXIN || MP-BAIDU || MP-TOUTIAO || MP-QQ
+menuButtonInfo = uni.getMenuButtonBoundingClientRect();
+// #endif
+/**
+ * home-head-轮播卡片,主要为了处理数据
+ * @property {Array} list 轮播图数据,
+ * @property {String} mode 指示器模式
+ * @property {String} navTitle 项目名称
+ */
+export default {
+	components: {},
+	data() {
+		return {
+			navBgImage: '',
+			changeNavBackground: false,
+			swiperCur: 0,
+			statusBarHeight: systemInfo.statusBarHeight
+		};
+	},
+	props: {
+		isScorll: {
+			type: Boolean,
+			default: false
+		},
+		// 轮播图的数据,格式如:[{image: 'xxxx', title: 'xxxx'},{image: 'yyyy', title: 'yyyy'}],其中title字段可选
+		list: {
+			type: Array,
+			default() {
+				return [];
+			}
+		},
+		navTitle: {
+			type: String,
+			default: '中宏商城'
+		},
+		navTitleStyle: {
+			type: Object,
+			default: () => {
+				return {
+					color: '#fff',
+					fontSize: '38rpx'
+				};
+			}
+		}
+	},
+	watch: {
+		isScorll(newValue, oldValue) {
+			this.changeNavBackground = newValue;
+			this.navBgImage = this.list[this.swiperCur].image;
+		}
+	},
+	computed: {
+		// 导航栏内部盒子的样式
+		navbarInnerStyle() {
+			let style = {};
+			style.height = this.navbarHeight + 'px';
+			// #ifdef MP
+			let rightButtonWidth = systemInfo.windowWidth - menuButtonInfo.left;
+			style.marginRight = rightButtonWidth + 'px';
+			// #endif
+			return style;
+		},
+		// 整个导航栏的样式
+		navbarStyle() {
+			let style = {};
+			style.zIndex = this.$u.zIndex.navbar;
+			style.background = this.changeNavBackground ? `url(${this.navBgImage}) no-repeat top / 100% auto` : 'none';
+			Object.assign(style, this.background);
+			return style;
+		},
+		navbarHeight() {
+			// #ifdef APP-PLUS || H5
+			return 44;
+			// #endif
+			// #ifdef MP
+			return systemInfo.platform == 'ios' ? 44 : 48;
+			// #endif
+		}
+	},
+	created() {
+		this.navBgImage = this.list[0].image;
+	},
+	methods: {
+		onSwiper(e) {
+			this.$tools.routerTo(this.list[e].path);
+		},
+		onChange(e) {
+			this.swiperCur = e.detail.current;
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+@mixin vue-flex($direction: row) {
+	/* #ifndef APP-NVUE */
+	display: flex;
+	flex-direction: $direction;
+	/* #endif */
+}
+// 轮播
+.banner-swiper-wrap {
+	height: 520rpx;
+	position: relative;
+	z-index: 100;
+	.search-box {
+		width: 66rpx;
+		height: 66rpx;
+		background: rgba(#000, 0.18);
+		border-radius: 100%;
+	}
+	.hover-search {
+		background: rgba(#fff, 0.18);
+	}
+}
+
+.u-navbar {
+	width: 100%;
+}
+
+.u-navbar-fixed {
+	position: fixed;
+	left: 0;
+	right: 0;
+	top: 0;
+	z-index: 991;
+}
+
+.u-status-bar {
+	width: 100%;
+}
+
+.u-navbar-inner {
+	@include vue-flex;
+	justify-content: space-between;
+	position: relative;
+	align-items: center;
+}
+
+.u-back-wrap {
+	@include vue-flex;
+	align-items: center;
+	flex: 1;
+	flex-grow: 0;
+	padding: 14rpx 14rpx 14rpx 24rpx;
+}
+</style>

+ 116 - 0
pages/index/index/privacy-modal.vue

@@ -0,0 +1,116 @@
+<template>
+	<u-popup v-model="showModal" @close="close" :border-radius="20" mode="center" length="auto" :closeable="false">
+		<view class="service-contract-wrap u-flex-col u-row-center u-col-center">
+			<image class="service-head-img" :src="$IMG_URL + '/imgs/modal/servece_head.png'" mode="widthFix"></image>
+			<view class="service-title">用户隐私协议概况</view>
+			<view class="service-content ">
+				感谢您使用{{ initShop.name }},我们非常重视您的个人信息和隐私保护,在您使用服务前,请仔细阅读
+				<text style="color: #EAB866;" @tap="jump('/pages/public/richtext', { id: initShop.privacy_protocol })">《{{ initShop.name }}隐私协议》</text>
+				,我们将会严格按照经您同意的各项条款使用您的个人信息,以便为您提供更好的服务。
+			</view>
+			<view class="service-tip ">如您同意此条款,请点击“同意”并开始使用我们的产品和服务,我们将尽全力保护您的个人信息安全。</view>
+			<view class="btn-box u-flex u-row-center u-col-center"><button class="u-reset-button agree-btn" @tap="close">知道了</button></view>
+		</view>
+	</u-popup>
+</template>
+
+<script>
+import { mapMutations, mapActions, mapState,mapGetters } from 'vuex';
+export default {
+	components: {},
+	data() {
+		return {};
+	},
+	props: {
+		value: {}
+	},
+	computed: {
+		...mapGetters(['initShop']),
+		showModal: {
+			get() {
+				return this.value;
+			},
+			set(val) {
+				this.$emit('input', val);
+			}
+		}
+	},
+	methods: {
+		close() {
+			this.showModal = false;
+			// #ifdef APP-VUE
+			plus.runtime.agreePrivacy();
+			// #endif
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+.service-contract-wrap {
+	background-color: #fff;
+	position: relative;
+	left: 50%;
+	transform: translateX(-50%);
+	min-height: 850rpx;
+	border-radius: 20rpx;
+	width: 610rpx;
+	.service-head-img {
+		width: 610rpx;
+	}
+
+	.service-title {
+		font-size: 35rpx;
+
+		font-weight: bold;
+		color: rgba(255, 255, 255, 1);
+		line-height: 42rpx;
+		background: linear-gradient(180deg, rgba(62, 48, 25, 1) 0%, rgba(109, 80, 40, 1) 100%);
+		-webkit-background-clip: text;
+		-webkit-text-fill-color: transparent;
+		margin-bottom: 30rpx;
+	}
+	.service-content {
+		text-align: left;
+		font-size: 26rpx;
+
+		font-weight: 500;
+		color: rgba(102, 102, 102, 1);
+		line-height: 50rpx;
+		padding: 0 40rpx;
+	}
+	.service-tip {
+		text-align: left;
+		font-size: 26rpx;
+
+		font-weight: 500;
+		color: rgba(154, 154, 154, 1);
+		line-height: 50rpx;
+		padding: 0 40rpx;
+	}
+	.btn-box {
+		padding: 40rpx;
+		.cancel-btn {
+			width: 248rpx;
+			height: 70rpx;
+			border: 1rpx solid rgba(234, 182, 99, 1);
+			border-radius: 35rpx;
+			font-size: 28rpx;
+
+			font-weight: 500;
+			color: rgba(234, 182, 99, 1);
+			background-color: #fff;
+		}
+		.agree-btn {
+			width: 300rpx;
+			line-height: 70rpx;
+			background: linear-gradient(90deg, rgba(233, 181, 97, 1), rgba(238, 204, 138, 1));
+			border-radius: 35rpx;
+			font-size: 28rpx;
+
+			font-weight: 500;
+			color: rgba(255, 255, 255, 1);
+		}
+	}
+}
+</style>

+ 143 - 0
pages/index/user.vue

@@ -0,0 +1,143 @@
+<!-- 个人中心 -->
+<template>
+	<view class="personal-wrap">
+		<!-- 个人信息卡片 -->
+		<userinfo-card v-if="userHeadData && userHeadData.style" :scrollTop="scrollTop" :detail="userHeadData" @onShare="onShare"></userinfo-card>
+		<!-- 自定义模块 -->
+		<view v-for="(item, index) in userTemplate" :key="item.id">
+			<!-- 轮播 -->
+			<sh-banner
+				v-if="item.type === 'banner' && index !== 0"
+				:Px="item.content.x"
+				:Py="item.content.y"
+				:borderRadius="item.content.radius"
+				:height="item.content.height"
+				:list="item.content.list"
+			></sh-banner>
+			<!-- 滑动宫格 -->
+			<sh-grid-swiper v-if="item.type === 'menu'" :list="item.content.list" :oneRowNum="item.content.style"></sh-grid-swiper>
+			<!-- 广告魔方 -->
+			<sh-adv v-if="item.type === 'adv'" :detail="item.content"></sh-adv>
+			<!-- 推荐商品 -->
+			<sh-hot-goods v-if="item.type === 'goods-list' || item.type === 'goods-group'" :detail="item.content"></sh-hot-goods>
+			<!-- 富文本 -->
+			<sh-richtext v-if="item.type === 'rich-text'" :richId="item.content.id"></sh-richtext>
+			<!-- 功能列表 -->
+			<sh-cell v-if="item.type === 'nav-list'" :list="item.content.list"></sh-cell>
+			<!-- 九宫格列表 -->
+			<sh-grid v-if="item.type === 'grid-list'" :list="item.content.list"></sh-grid>
+			<!-- 钱包 -->
+			<sh-wallet v-if="item.type === 'wallet-card'"></sh-wallet>
+			<!-- 订单卡片 -->
+			<sh-order-card v-if="item.type === 'order-card'"></sh-order-card>
+		</view>
+		<!-- copyright -->
+		<view class="copyright-box u-flex-col u-row-center u-col-center u-p-t-80 u-p-b-50" v-if="initShop.copyright">
+			<view class="copyright-text">{{ initShop.copyright[0] }}</view>
+			<view class="copyright-text">{{ initShop.copyright[1] }}</view>
+		</view>
+
+		<!-- #ifdef H5 -->
+		<view class="tabbar-hack" style="height: 120rpx; width:100%;"></view>
+		<!-- #endif -->
+		<!-- 关注弹窗 -->
+		<shopro-float-btn></shopro-float-btn>
+		<!-- 登录提示 -->
+		<shopro-auth-modal></shopro-auth-modal>
+		<!-- 分享组件 -->
+		<shopro-share v-model="showShare" posterType="user"></shopro-share>
+		<!-- <shopro-tabbar></shopro-tabbar> -->
+	</view>
+</template>
+
+<script>
+import shBanner from './components/sh-banner.vue';
+import shGridSwiper from './components/sh-grid-swiper.vue';
+import shAdv from './components/sh-adv.vue';
+import shRichtext from './components/sh-richtext.vue';
+import shCell from './components/sh-cell.vue';
+import shGrid from './components/sh-grid.vue';
+import shOrderCard from './components/sh-order-card.vue';
+import shWallet from './components/sh-wallet.vue';
+import shHotGoods from './components/sh-hot-goods.vue';
+
+import userinfoCard from './user/userinfo-card.vue';
+
+import { mapMutations, mapActions, mapState, mapGetters } from 'vuex';
+
+import share from '@/shopro/share';
+
+export default {
+	components: {
+		shBanner,
+		shGridSwiper,
+		shAdv,
+		shRichtext,
+		shCell,
+		shGrid,
+		shWallet,
+		shOrderCard,
+		shHotGoods,
+
+		userinfoCard
+	},
+	data() {
+		return {
+			scrollTop: 0,
+			showShare: false,
+			enable: false //是否开启吸顶。
+		};
+	},
+	computed: {
+		...mapGetters(['initShop', 'userTemplate', 'isLogin', 'userInfo', 'authType']),
+		userHeadData() {
+			if (this.userTemplate?.length) {
+				return this.userTemplate[0].content;
+			}
+		}
+	},
+	// 下拉刷新
+	onPullDownRefresh() {
+		this.init();
+	},
+	onPageScroll(e) {
+		this.scrollTop = e.scrollTop;
+	},
+
+	onShow() {
+		if (this.isLogin) {
+			this.init();
+			this.getUserData();
+		}
+		this.enable = true;
+	},
+
+	methods: {
+		...mapActions(['getUserInfo', 'showAuthModal', 'getUserData']),
+		onShare() {
+			this.showShare = true;
+			uni.hideTabBar();
+		},
+		// 初始化
+		init() {
+			this.getUserInfo()
+				.then(res => {
+					uni.stopPullDownRefresh();
+				})
+				.catch(e => {
+					uni.stopPullDownRefresh();
+				});
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+.copyright-box {
+	.copyright-text {
+		font-size: 22rpx;
+		font-weight: 500;
+		color: #c4c4c4;
+	}
+}
+</style>

+ 316 - 0
pages/index/user/userinfo-card.vue

@@ -0,0 +1,316 @@
+<template>
+	<view>
+		<view class="sh-userinfo-box">
+			<view class="head-wrap" :style="{ background: detail.image ? `url(${detail.image}) no-repeat center / 100% 100%` : detail.color }">
+				<!-- 标题栏 -->
+				<shopro-navbar
+					backIconName=""
+					backText="我的"
+					:backTextStyle="{
+						color: '#fff',
+						fontSize: '40rpx',
+						fontWeight: '500'
+					}"
+					:background="navBackground"
+				>
+					<view slot="right" class="u-flex u-row-center u-col-center u-m-r-20" v-if="userOtherData.is_store" @tap="goStore">
+						<button class="u-reset-button merchant-btn">切换商家版</button>
+					</view>
+				</shopro-navbar>
+
+				<view class="user-head u-flex u-row-between">
+					<view class="u-flex">
+						<!-- 个人信息 -->
+						<view class="info-box">
+							<view class="u-flex" @tap="$Router.push('/pages/user/info')">
+								<view class="head-img-wrap">
+									<image class="head-img" :src="userInfo.avatar || $IMG_URL + '/imgs/base_avatar.png'" mode="aspectFill"></image>
+									<!-- 同步信息 -->
+									<block v-if="showRefresh">
+										<button @tap.stop="showModal = true" class="u-reset-button u-flex u-row-center u-col-center refresh-btn">
+											<view class="u-iconfont uicon-reload" style="color: #fff;font-size: 24rpx;"></view>
+										</button>
+									</block>
+								</view>
+								<text class="user-name u-ellipsis-1">{{ userInfo.nickname || '请登录~' }}</text>
+							</view>
+						</view>
+						<!-- 等级 -->
+						<view v-if="userInfo.group" class="grade-tag tag-box u-flex">
+							<image v-if="userInfo.group.image" class="tag-img" :src="userInfo.group.image" mode=""></image>
+							<text class="tag-title">{{ userInfo.group.name }}</text>
+						</view>
+					</view>
+					<view class="u-flex" v-if="userInfo.nickname">
+						<button class=" u-reset-button code-btn" @tap="onShare"><text class="iconfont icon-qrcode"></text></button>
+					</view>
+				</view>
+			</view>
+		</view>
+		<!-- 绑定手机 -->
+		<view class="notice-box u-flex u-row-between u-p-30" v-if="userInfo.verification && !userInfo.verification.mobile" @tap="bindMobile">
+			<view class="notice-detail">点击绑定手机号,确保账户安全</view>
+			<button class="u-reset-button bindPhone">去绑定</button>
+		</view>
+		<!-- 更新信息 -->
+		<view class="cu-modal" :class="{ show: showModal }" @tap="showModal = false">
+			<view class="cu-dialog" style="width: 600rpx;">
+				<view class="modal-box">
+					<view class="modal-head">提示</view>
+					<view class="modal-content">更新微信信息?</view>
+					<view class="modal-bottom u-flex u-col-center">
+						<button class="u-reset-button modal-btn cancel-btn" :hover-stay-time="100" hover-class="btn-hover" @tap="showModal = false">取消</button>
+						<button class="u-reset-button  modal-btn save-btn" :hover-stay-time="100" hover-class="btn-hover" @tap="refreshWechatUser">确定</button>
+					</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+/**
+ * 自定义之个人信息
+ * @property {Object} detail - 个人信息
+ */
+import { mapMutations, mapActions, mapState, mapGetters } from 'vuex';
+import wechat from '@/shopro/wechat/wechat';
+export default {
+	components: {},
+	data() {
+		return {
+			showModal: false,
+			platform: this.$platform.get(), //当前平台。
+			isFixed: false,
+			navBackground: {
+				background: 'none'
+			}
+		};
+	},
+	mounted() {},
+	props: {
+		detail: {},
+		scrollTop: {
+			type: Number,
+			default: 0
+		}
+	},
+	watch: {
+		scrollTop(val) {
+			let backgroundText = this.detail.image ? `url(${this.detail.image})` : this.detail.color;
+			this.isFixed = val > 50 ? true : false;
+			this.navBackground.background = val > 50 ? backgroundText : 'none';
+		}
+	},
+	computed: {
+		...mapGetters(['isLogin', 'userInfo', 'userOtherData']),
+		showRefresh() {
+			if (this.isLogin) {
+				if (this.platform === 'wxOfficialAccount') {
+					return this.userInfo.verification?.wxOfficialAccount;
+				}
+				if (this.platform === 'wxMiniProgram') {
+					return this.userInfo.verification.wxMiniProgram;
+				}
+				if (this.platform === 'App') {
+					return this.userInfo.verification.wxOpenPlatform;
+				}
+			}
+			return false;
+		}
+	},
+	methods: {
+		...mapActions(['getUserInfo', 'showAuthModal']),
+		jump(path, query) {
+			this.$Router.push({
+				path: path,
+				query: query
+			});
+		},
+		// 点击分享
+		onShare() {
+			this.$emit('onShare');
+		},
+		// 同步用户信息
+		async refreshWechatUser() {
+			this.showModal = false;
+			let token = await wechat.refresh();
+			token && this.getUserInfo(token);
+		},
+		// 跳转门店
+		goStore() {
+			if (this.userOtherData.store_id) {
+				uni.setStorageSync('storeId', this.userOtherData.store_id);
+				this.jump('/pages/app/merchant/index', { storeId: this.userOtherData.store_id });
+			} else {
+				if (uni.getStorageSync('storeId')) {
+					this.jump('/pages/app/merchant/index');
+				} else {
+					this.jump('/pages/app/merchant/list');
+				}
+			}
+			//  #ifdef MP-WEIXIN
+			this.$store.commit('subscribeMessage', 'store');
+			//  #endif
+		},
+		// 绑定手机号
+		bindMobile() {
+			this.showAuthModal('bindMobile');
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+// 更新信息
+.modal-box {
+	width: 600rpx;
+	.btn-hover {
+		background-color: rgb(230, 230, 230);
+	}
+	.modal-head {
+		padding-top: 48rpx;
+		font-weight: 500;
+		text-align: center;
+		color: $u-main-color;
+	}
+	.modal-content {
+		padding: 48rpx;
+		font-size: 30rpx;
+		text-align: center;
+		color: $u-content-color;
+	}
+	.modal-bottom {
+		.modal-btn {
+			flex: 1;
+			height: 100rpx;
+			line-height: 100rpx;
+			font-size: 32rpx;
+			box-sizing: border-box;
+			cursor: pointer;
+			text-align: center;
+			border-radius: 4rpx;
+		}
+		.cancel-btn {
+			color: #666666;
+		}
+		.save-btn {
+			color: #e9b461;
+		}
+	}
+}
+.sh-userinfo-box {
+	position: relative;
+	height: calc(var(--status-bar-height) + 300rpx);
+	.head-wrap {
+		powidth: 100%;
+		height: 100%;
+		.merchant-btn {
+			width: 178rpx;
+			height: 64rpx;
+			line-height: 64rpx;
+			background: #ffffff;
+			border-radius: 32rpx;
+			font-size: 26rpx;
+			font-weight: 500;
+			color: #a8700d;
+		}
+	}
+
+	.user-head {
+		width: 100%;
+		padding-top: 30rpx;
+		.info-box {
+			padding-left: 30rpx;
+			.head-img-wrap {
+				position: relative;
+				.refresh-btn {
+					position: absolute;
+					padding: 0;
+					background: none;
+					width: 40rpx;
+					height: 40rpx;
+					border-radius: 50%;
+					background: #c9912c;
+					top: -10rpx;
+					right: 10rpx;
+				}
+			}
+			.head-img {
+				width: 94rpx;
+				height: 94rpx;
+				border-radius: 50%;
+				background: #ccc;
+				margin-right: 25rpx;
+				overflow: hidden;
+			}
+
+			.user-name {
+				font-size: 30rpx;
+				font-weight: 500;
+				color: #fff;
+				line-height: 30rpx;
+				width: 180rpx;
+			}
+		}
+		.tag-box {
+			background: rgba(0, 0, 0, 0.2);
+			border-radius: 22rpx;
+			margin-left: 10rpx;
+			.tag-img {
+				width: 40rpx;
+				height: 40rpx;
+				display: inline-block;
+				border-radius: 50%;
+			}
+			.tag-title {
+				font-size: 20rpx;
+				font-weight: 500;
+				color: rgba(255, 255, 255, 1);
+				line-height: 40rpx;
+				padding: 0 10rpx;
+			}
+		}
+		.code-btn {
+			padding: 30rpx;
+			.icon-qrcode {
+				font-size: 50rpx;
+				color: #fff;
+			}
+		}
+	}
+	.wallet {
+		font-size: 20rpx;
+		padding-left: 140rpx;
+		.money {
+			margin-right: 40rpx;
+		}
+	}
+}
+// 绑定微信公众号
+.notice-box {
+	width: 750rpx;
+	height: 70rpx;
+	background: rgba(253, 239, 216, 1);
+	padding: 0 35rpx;
+
+	.notice-detail {
+		font-size: 24rpx;
+
+		font-weight: 400;
+		color: rgba(204, 149, 59, 1);
+	}
+
+	.bindPhone {
+		width: 135rpx;
+		line-height: 52rpx;
+		background: linear-gradient(90deg, rgba(233, 180, 97, 1), rgba(238, 204, 137, 1));
+		border-radius: 26rpx;
+		padding: 0;
+		font-size: 26rpx;
+
+		font-weight: 500;
+		color: rgba(255, 255, 255, 1);
+	}
+}
+</style>

+ 182 - 0
pages/index/view.vue

@@ -0,0 +1,182 @@
+<!-- 自定义页面 -->
+<template>
+	<view class="home-wrap u-m-b-20">
+		<!-- 空白页 -->
+		<u-no-network @retry="init"></u-no-network>
+		<shopro-empty v-if="isEmpty" :image="$IMG_URL + '/imgs/empty/template_empty.png'" tipText="暂未找到模板,请前往装修~"></shopro-empty>
+
+		<view v-else-if="isRefresh" class="content-box">
+			<!-- 自定义模块 -->
+			<view class="template-box">
+				<block v-for="(item, index) in viewTemplate" :key="item.id">
+					<!-- 轮播 -->
+					<sh-banner
+						v-if="item.type === 'banner'"
+						:Px="item.content.x"
+						:Py="item.content.y"
+						:borderRadius="item.content.radius"
+						:height="item.content.height"
+						:list="item.content.list"
+					></sh-banner>
+
+					<!-- 搜索 -->
+					<sh-search v-if="item.type === 'search'"></sh-search>
+
+					<!-- 滑动宫格 -->
+					<sh-grid-swiper v-if="item.type === 'menu'" :list="item.content.list" :oneRowNum="item.content.style"></sh-grid-swiper>
+
+					<!-- 推荐商品 -->
+					<sh-hot-goods v-if="item.type === 'goods-list' || item.type === 'goods-group'" :detail="item.content"></sh-hot-goods>
+					<!-- 广告魔方 -->
+					<sh-adv v-if="item.type === 'adv'" :detail="item.content"></sh-adv>
+					<!-- 优惠券 -->
+					<sh-coupon v-if="item.type === 'coupons'" :detail="item.content"></sh-coupon>
+					<!-- 秒杀-->
+					<sh-seckill v-if="item.type === 'seckill'" :detail="item.content"></sh-seckill>
+					<!-- 拼团 -->
+					<sh-groupon v-if="item.type === 'groupon'" :detail="item.content"></sh-groupon>
+					<!-- 富文本 -->
+					<sh-richtext v-if="item.type === 'rich-text'" :richId="item.content.id"></sh-richtext>
+					<!-- 功能列表 -->
+					<sh-cell v-if="item.type === 'nav-list'" :list="item.content.list"></sh-cell>
+					<!-- 九宫格列表 -->
+					<sh-grid v-if="item.type === 'grid-list'" :detail="item.content.list"></sh-grid>
+					<!-- 功能标题 -->
+					<sh-title-card v-if="item.type === 'title-block'" :title="item.content.name" :bgImage="item.content.image" :titleColor="item.content.color"></sh-title-card>
+					<!-- 钱包 -->
+					<sh-wallet v-if="item.type === 'wallet-card'"></sh-wallet>
+					<!-- 订单卡片 -->
+					<sh-order-card v-if="item.type === 'order-card'"></sh-order-card>
+					<!-- 直播 -->
+					<!-- #ifdef MP-WEIXIN -->
+					<sh-live v-if="item.type === 'live' && HAS_LIVE" :detail="item.content"></sh-live>
+					<!-- #endif -->
+				</block>
+			</view>
+			<!-- 分类选项卡 -->
+			<sh-category-tabs
+				v-if="categoryTabsData && categoryTabsData.category_arr && categoryTabsData.category_arr.length"
+				:enable="enable"
+				:styleType="categoryTabsData.style"
+				:tabsList="categoryTabsData.category_arr"
+			></sh-category-tabs>
+			<!-- 登录提示 -->
+			<shopro-auth-modal></shopro-auth-modal>
+			<!-- #ifdef H5 -->
+			<view class="tabbar-hack" style="height: 120rpx; width:100%;"></view>
+			<!-- #endif -->
+		</view>
+	</view>
+</template>
+
+<script>
+import shBanner from './components/sh-banner.vue';
+import shGridSwiper from './components/sh-grid-swiper.vue';
+import shHotGoods from './components/sh-hot-goods.vue';
+import shAdv from './components/sh-adv.vue';
+import shCoupon from './components/sh-coupon.vue';
+import shSeckill from './components/sh-seckill.vue';
+import shGroupon from './components/sh-groupon.vue';
+import shRichtext from './components/sh-richtext.vue';
+import shCell from './components/sh-cell.vue';
+import shGrid from './components/sh-grid.vue';
+import shTitleCard from './components/sh-title-card.vue';
+import shOrderCard from './components/sh-order-card.vue';
+import shWallet from './components/sh-wallet.vue';
+import shSearch from './components/sh-search.vue';
+import shCategoryTabs from './components/sh-category-tabs.vue';
+import { mapMutations, mapActions, mapState, mapGetters } from 'vuex';
+
+// #ifdef MP-WEIXIN
+import { HAS_LIVE } from '@/env';
+import shLive from './components/sh-live.vue';
+// #endif
+
+// #ifdef H5
+let listenMove = document.body; //禁止手机h5下拉刷新带动整个页面。
+let handle = function(e) {
+	e.preventDefault();
+};
+// #endif
+
+export default {
+	components: {
+		shBanner,
+		shGridSwiper,
+		shGroupon,
+		shHotGoods,
+		shAdv,
+		shCoupon,
+		shSeckill,
+		shRichtext,
+		shCell,
+		shGrid,
+		shTitleCard,
+		shWallet,
+		shOrderCard,
+		shSearch,
+		shCategoryTabs,
+
+		// #ifdef MP-WEIXIN
+		shLive
+		// #endif
+	},
+	data() {
+		return {
+			// #ifdef MP-WEIXIN
+			HAS_LIVE: HAS_LIVE,
+			// #endif
+			enable: false, //是否开启吸顶。
+			isScorll: false,
+			viewTemplate: [], //自定义模板数据
+			isEmpty: false,
+			isRefresh: false, //刷新
+			categoryTabsData: {} //分类选项卡
+		};
+	},
+	computed: {
+		...mapGetters(['isLogin'])
+	},
+	onPullDownRefresh() {
+		this.getViewTemplate();
+	},
+	onPageScroll(e) {
+		this.isScorll = e.scrollTop > 100 ? true : false;
+	},
+	onShow() {
+		let that = this;
+		this.enable = true;
+		this.isLogin && this.getCartList();
+	},
+	onHide() {
+		this.enable = false;
+	},
+	onLoad() {
+		this.getViewTemplate();
+	},
+	methods: {
+		...mapActions(['getCartList']),
+		// 初始化
+		getViewTemplate() {
+			let that = this;
+			that.isRefresh = false;
+			that.$http('common.custom', {
+				custom_id: that.$Route.query.id
+			}).then(res => {
+				uni.stopPullDownRefresh();
+				that.isRefresh = true;
+				if (res.code == 1) {
+					that.viewTemplate = res.data.template;
+					that.isEmpty = !that.viewTemplate.length;
+					that.categoryTabsData = that.viewTemplate[that.viewTemplate.length - 1].content;
+					uni.setNavigationBarTitle({
+						title: '中宏商城' || res.data.name
+					});
+				}
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss"></style>

+ 329 - 0
pages/order/after-sale/detail.vue

@@ -0,0 +1,329 @@
+<!-- 售后详情 -->
+<template>
+	<view class="page_box">
+		<view class="content_box">
+			<!-- 步骤条 -->
+			<view class="steps-box"><u-steps :current="stepsCurrent" :list="steps" activeColor="#39b54a" mode="number" icon="checkmark"></u-steps></view>
+
+			<!-- 服务状态 -->
+			<view class="status-box u-flex u-row-between" @tap="jump('/pages/order/after-sale/log', { aftersaleId: aftersaleDetail.id })">
+				<view class="">
+					<view class="status-text">{{ aftersaleDetail.aftersale_status_desc }}</view>
+					<view class="status-time">{{ aftersaleDetail.updatetime }}</view>
+				</view>
+				<text class="u-iconfont uicon-arrow-right" style="color: #666"></text>
+			</view>
+
+			<!-- 退款金额 -->
+			<view class="aftersale-money u-flex u-row-between">
+				<view class="aftersale-money--title">退款总额</view>
+				<view class="aftersale-money--num" v-if="Number(aftersaleDetail.refund_fee)">¥{{ aftersaleDetail.refund_fee }}</view>
+				<view class="aftersale-money--num" v-else>未退款</view>
+			</view>
+			<!-- 服务商品 -->
+			<view class="order-shop">
+				<shopro-mini-card :title="aftersaleDetail.goods_title" :image="aftersaleDetail.goods_image">
+					<template #describe>
+						<view class="order-sku u-ellipsis-1">
+							<text class="order-num">数量:{{ aftersaleDetail.goods_num || 0 }};</text>
+							{{ aftersaleDetail.goods_sku_text ? aftersaleDetail.goods_sku_text : '' }}
+						</view>
+					</template>
+					<template #cardBottom>
+						<view class="card-price-box u-flex">
+							<text class="order-price">¥{{ aftersaleDetail.goods_price || 0 }}</text>
+							<button class="u-reset-button status-btn" v-if="aftersaleDetail.status_name">{{ aftersaleDetail.status_name }}</button>
+						</view>
+					</template>
+				</shopro-mini-card>
+			</view>
+
+			<!-- 服务内容 -->
+			<view class="aftersale-content">
+				<view class="aftersale-item u-flex">
+					<view class="item-title">服务单号:</view>
+					<view class="item-content">{{ aftersaleDetail.aftersale_sn }}</view>
+					<button class="u-reset-button copy-btn" @tap="onCopy(aftersaleDetail.aftersale_sn)">复制</button>
+				</view>
+				<view class="aftersale-item u-flex">
+					<view class="item-title">申请时间:</view>
+					<view class="item-content">{{ aftersaleDetail.createtime }}</view>
+				</view>
+				<view class="aftersale-item u-flex">
+					<view class="item-title">售后类型:</view>
+					<view class="item-content">{{ aftersaleDetail.type_text }}</view>
+				</view>
+				<view class="aftersale-item u-flex" v-if="aftersaleLog && aftersaleLog.length">
+					<view class="item-title">申请原因:</view>
+					<view class="item-content">{{ aftersaleLog[aftersaleLog.length - 1].reason }}</view>
+				</view>
+				<view class="aftersale-item x-f" v-if="aftersaleLog && aftersaleLog.length">
+					<view class="item-title">相关描述:</view>
+					<view class="item-content">{{ aftersaleLog[aftersaleLog.length - 1].content }}</view>
+				</view>
+			</view>
+		</view>
+		<view class="foot_box">
+			<block v-for="orderBtn in aftersaleDetail.btns" :key="orderBtn">
+				<button v-if="orderBtn === 'cancel'" @tap.stop="onCancel(aftersaleDetail.id)" class="u-reset-button btn">取消</button>
+				<button v-if="orderBtn === 'delete'" @tap.stop="onDelete(aftersaleDetail.id)" class="u-reset-button btn delete-btn">删除</button>
+			</block>
+			<button class="u-reset-button contcat-btn btn" @tap="onService">联系客服</button>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	components: {},
+	data() {
+		return {
+			stepsCurrent: 0,
+			steps: [
+				{
+					name: '提交申请'
+				},
+				{
+					name: '进行中'
+				},
+				{
+					name: '完成'
+				}
+			],
+			aftersaleDetail: {}, //售后详情
+			aftersaleLog: [] //售后记录
+		};
+	},
+	computed: {},
+	onLoad() {
+		this.getAftersaleDetail();
+	},
+	methods: {
+		jump(path, parmas) {
+			this.$Router.push({
+				path: path,
+				query: parmas
+			});
+		},
+		// 跳转客服
+		onService() {
+			this.$Router.push('/pages/public/chat/index');
+		},
+		// 复制
+		onCopy(code) {
+			let that = this;
+			uni.setClipboardData({
+				data: code,
+				success: function(data) {
+					that.$u.toast('已复制到剪切板');
+				}
+			});
+		},
+
+		// 设置步骤条
+		setSteps(step) {
+			if (step >= 0 && step < 2) {
+				this.stepsCurrent = step;
+			} else {
+				this.stepsCurrent = 2;
+				let name = '';
+				switch (step) {
+					case 2:
+						name = '完成';
+						break;
+					case -1:
+						name = '取消';
+						break;
+					case -2:
+						name = '驳回';
+						break;
+					default:
+						break;
+				}
+				this.steps[2].name = name;
+			}
+		},
+		// 服务单详情
+		getAftersaleDetail() {
+			let that = this;
+			that.$http('order.aftersaleDetail', {
+				id: that.$Route.query.aftersaleId
+			}).then(res => {
+				if (res.code === 1) {
+					that.aftersaleDetail = res.data;
+					that.setSteps(res.data.aftersale_status);
+					that.aftersaleLog = res.data.log;
+					that.aftersaleDetail.createtime = that.$u.timeFormat(res.data.createtime, 'yyyy-mm-dd hh:MM');
+					that.aftersaleDetail.updatetime = that.$u.timeFormat(res.data.updatetime, 'yyyy-mm-dd hh:MM');
+				}
+			});
+		},
+		//取消
+		onCancel(aftersaleId) {
+			let that = this;
+			that.$http(
+				'order.cancelAftersaleOrder',
+				{
+					id: aftersaleId
+				},
+				'取消中...'
+			).then(res => {
+				if (res.code === 1) {
+					that.$Router.back();
+				}
+			});
+		},
+		// 删除
+		onDelete(aftersaleId) {
+			let that = this;
+			uni.showModal({
+				title: '删除订单',
+				content: '确定要删除这个订单么?',
+				cancelText: '取消',
+				confirmText: '删除',
+				success: res => {
+					if (res.confirm) {
+						that.$http('order.deleteAftersaleOrder', {
+							id: aftersaleId
+						}).then(res => {
+							if (res.code === 1) {
+								that.$Router.back();
+							}
+						});
+					}
+				}
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+// 步骤条
+.steps-box {
+	height: 190rpx;
+	padding: 60rpx 0 0;
+	background: #fff;
+}
+// 服务状态
+.status-box {
+	background-color: #fff;
+	border-radius: 20rpx 20rpx 0px 0px;
+	padding: 20rpx;
+	margin-top: -20rpx;
+	.status-text {
+		font-size: 28rpx;
+
+		font-weight: 500;
+		color: rgba(51, 51, 51, 1);
+		margin-bottom: 20rpx;
+	}
+	.status-time {
+		font-size: 24rpx;
+
+		font-weight: 400;
+		color: rgba(153, 153, 153, 1);
+	}
+}
+// 退款金额
+.aftersale-money {
+	background-color: #fff;
+	height: 98rpx;
+	padding: 0 20rpx;
+	margin: 20rpx 0;
+	.aftersale-money--title {
+		font-size: 28rpx;
+
+		font-weight: 500;
+		color: rgba(51, 51, 51, 1);
+	}
+	.aftersale-money--num {
+		font-size: 28rpx;
+
+		font-weight: 500;
+		color: rgba(225, 33, 43, 1);
+	}
+}
+// order-shop
+.order-shop {
+	padding: 20rpx;
+	background-color: #fff;
+	margin-bottom: 20rpx;
+	.order-sku {
+		font-size: 24rpx;
+
+		font-weight: 400;
+		color: rgba(153, 153, 153, 1);
+		width: 450rpx;
+		margin-bottom: 20rpx;
+		.order-num {
+			margin-right: 10rpx;
+		}
+	}
+	.card-price-box {
+		.status-btn {
+			height: 32rpx;
+			border: 1rpx solid rgba(207, 169, 114, 1);
+			border-radius: 15rpx;
+			font-size: 20rpx;
+			font-weight: 400;
+			color: rgba(168, 112, 13, 1);
+			padding: 0 10rpx;
+			margin-left: 20rpx;
+			background: rgba(233, 183, 102, 0.16);
+		}
+		.order-price {
+			font-size: 26rpx;
+
+			font-weight: 600;
+			color: rgba(51, 51, 51, 1);
+		}
+	}
+}
+// 服务内容
+.aftersale-content {
+	background-color: #fff;
+	padding: 20rpx;
+	.aftersale-item {
+		height: 60rpx;
+		.copy-btn {
+			background: none;
+			color: #b38436;
+			font-size: 26rpx;
+			padding: 0 20rpx;
+		}
+		.item-tilte {
+			color: #333;
+			font-size: 28rpx;
+		}
+		.item-content {
+			color: #999999;
+			font-size: 28rpx;
+		}
+	}
+}
+// 底部功能
+.foot_box {
+	height: 100rpx;
+	background-color: #fff;
+	display: flex;
+	align-items: center;
+	justify-content: flex-end;
+	.btn {
+		width: 160rpx;
+		line-height: 60rpx;
+		background: rgba(238, 238, 238, 1);
+		border-radius: 30rpx;
+		padding: 0;
+		margin-right: 20rpx;
+		font-size: 26rpx;
+
+		font-weight: 400;
+		color: rgba(51, 51, 51, 1);
+	}
+	.delete-btn {
+		background: #ffeeee;
+		color: #e50808;
+	}
+}
+</style>

+ 329 - 0
pages/order/after-sale/list.vue

@@ -0,0 +1,329 @@
+<!-- 售后列表 -->
+<template>
+	<view class="page_box">
+		<!-- tab -->
+		<view class="head_box">
+			<view class="order-nav u-flex">
+				<view class="nav-item u-flex-col u-flex-1 u-col-center" v-for="nav in orderState" :key="nav.id" @tap="onNav(nav.type)">
+					<view class="item-title">{{ nav.title }}</view>
+					<text class="nav-line" :class="{ 'line-active': orderType === nav.type }"></text>
+				</view>
+			</view>
+		</view>
+
+		<view class="content_box">
+			<scroll-view scroll-y="true" enable-back-to-top @scrolltolower="loadMore" class="scroll-box">
+				<view class="order-list" v-for="(order, orderIndex) in orderList" :key="order.id" @tap="jump('/pages/order/after-sale/detail', { aftersaleId: order.id })">
+					<view class="order-head u-flex u-row-between">
+						<text class="no">服务单号:{{ order.aftersale_sn }}</text>
+						<view class="order-status u-flex">
+							<text class="status-text">{{ order.aftersale_status_text }}</text>
+						</view>
+					</view>
+					<view class="order-content">
+						<shopro-mini-card :title="order.goods_title" :image="order.goods_image">
+							<template #describe>
+								<view class="order-sku u-ellipsis-1">
+									<text class="order-num">数量:{{ order.goods_num || 0 }};</text>
+									{{ order.goods_sku_text ? order.goods_sku_text : '' }}
+								</view>
+							</template>
+							<template #cardBottom>
+								<view class="card-price-box u-flex">
+									<text class="order-price font-OPPOSANS">¥{{ order.goods_price || 0 }}</text>
+									<button class="u-reset-button status-btn">{{ order.type_text }}</button>
+								</view>
+							</template>
+						</shopro-mini-card>
+					</view>
+					<view class="order-bottom">
+						<view class="serve-status u-flex u-row-between" @tap.stop="jump('/pages/order/after-sale/log', { aftersaleId: order.id })">
+							<view class="u-flex">
+								<view class="serve-title">{{ order.aftersale_status_text }}</view>
+								<view class="serve-content">{{ order.aftersale_status_desc }}</view>
+							</view>
+							<text class="u-iconfont uicon-arrow-right" style="color: #666"></text>
+						</view>
+						<view class="btn-box u-flex" v-for="orderBtn in order.btns" :key="orderBtn">
+							<button v-if="orderBtn === 'cancel'" @tap.stop="onCancel(order.id, orderIndex)" class="u-reset-button obtn">取消</button>
+							<button v-if="orderBtn === 'delete'" style="background:#FFEEEE;color:#E50808" @tap.stop="onDelete(order.id, orderIndex)" class="u-reset-button obtn">
+								删除
+							</button>
+						</view>
+					</view>
+				</view>
+				<!-- 缺省页 -->
+				<shopro-empty v-if="isEmpty" :image="$IMG_URL + '/imgs/empty/empty_groupon.png'" tipText="暂无相关记录~"></shopro-empty>
+				<!-- 更多 -->
+				<u-loadmore v-if="orderList.length" height="80rpx" :status="loadStatus" icon-type="flower" color="#ccc" />
+			</scroll-view>
+		</view>
+		<view class="foot_box"></view>
+	</view>
+</template>
+
+<script>
+export default {
+	components: {},
+	data() {
+		return {
+			orderType: 'all', //分类id
+			isEmpty: false,
+			orderList: [], //售后列表
+			loadStatus: 'loadmore', //loadmore-加载前的状态,loading-加载中的状态,nomore-没有更多的状态
+			currentPage: 1,
+			lastPage: 1,
+			orderState: [
+				{
+					id: 0,
+					title: '全部',
+					type: 'all'
+				},
+				{
+					id: 1,
+					title: '处理中',
+					type: 'ing'
+				},
+				{
+					id: 2,
+					title: '已完成',
+					type: 'finish'
+				}
+			]
+		};
+	},
+	computed: {},
+	onLoad() {},
+	onShow() {
+		this.orderList = [];
+		this.currentPage = 1;
+		this.lastPage = 1;
+		this.getAftersaleList();
+	},
+	methods: {
+		jump(path, parmas) {
+			this.$Router.push({
+				path: path,
+				query: parmas
+			});
+		},
+		// 切换导航
+		onNav(type) {
+			if (this.orderType !== type) {
+				this.orderType = type;
+				this.currentPage = 1;
+				this.lastPage = 1;
+				this.orderList = [];
+				this.getAftersaleList();
+			}
+		},
+		// 售后列表
+		getAftersaleList() {
+			let that = this;
+			that.loadStatus = 'loading';
+			that.$http('order.aftersaleList', {
+				type: that.orderType,
+				page: that.currentPage
+			}, '加载中...').then(res => {
+				if (res.code === 1) {
+					that.orderList = [...that.orderList, ...res.data.data];
+					that.isEmpty = !that.orderList.length;
+					that.lastPage = res.data.last_page;
+					that.loadStatus = that.currentPage < res.data.last_page ? 'loadmore' : 'nomore';
+				}
+			});
+		},
+
+		//取消
+		onCancel(aftersaleId, orderIndex) {
+			let that = this;
+			that.$http(
+				'order.cancelAftersaleOrder',
+				{
+					id: aftersaleId
+				},
+				'取消中...'
+			).then(res => {
+				if (res.code === 1) {
+					this.$u.toast(res.msg);
+					this.orderList.splice(orderIndex, 1);
+				}
+			});
+		},
+
+		// 删除
+		onDelete(aftersaleId, orderIndex) {
+			let that = this;
+			uni.showModal({
+				title: '删除订单',
+				content: '确定要删除这个订单么?',
+				cancelText: '取消',
+				confirmText: '删除',
+				success: res => {
+					if (res.confirm) {
+						that.$http(
+							'order.deleteAftersaleOrder',
+							{
+								id: aftersaleId
+							},
+							'删除中...'
+						).then(res => {
+							if (res.code === 1) {
+								this.$u.toast(res.msg);
+								this.orderList.splice(orderIndex, 1);
+							}
+						});
+					}
+				}
+			});
+		},
+
+		// 加载更多
+		loadMore() {
+			if (this.currentPage < this.lastPage) {
+				this.currentPage += 1;
+				this.getAftersaleList();
+			}
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+// 导航切换
+.order-nav {
+	background: #fff;
+	height: 80rpx;
+	border-top: 1rpx solid #f5f5f5;
+	.nav-item {
+		flex: 1;
+		.item-title {
+			font-size: 30rpx;
+
+			font-weight: 400;
+			color: rgba(51, 51, 51, 1);
+			line-height: 76rpx;
+		}
+		.nav-line {
+			width: 100rpx;
+			height: 4rpx;
+			background: #fff;
+		}
+		.line-active {
+			background: rgba(230, 184, 115, 1);
+		}
+	}
+}
+
+// 售后列表卡片
+.order-list {
+	background: #fff;
+	margin: 20rpx 0;
+	.order-bottom {
+		padding-bottom: 20rpx;
+		.serve-status {
+			height: 94rpx;
+			background: rgba(246, 246, 246, 1);
+			border-radius: 4rpx;
+			margin: 0 20rpx 20rpx;
+			padding: 0 20rpx;
+			.serve-title {
+				font-size: 26rpx;
+
+				font-weight: 500;
+				color: rgba(51, 51, 51, 1);
+				margin-right: 30rpx;
+			}
+			.serve-content {
+				font-size: 20rpx;
+
+				font-weight: 400;
+				color: rgba(102, 102, 102, 1);
+			}
+		}
+		.btn-box {
+			justify-content: flex-end;
+		}
+		.obtn {
+			width: 160rpx;
+			line-height: 60rpx;
+			background: rgba(238, 238, 238, 1);
+			border-radius: 30rpx;
+			font-size: 26rpx;
+
+			font-weight: 400;
+			color: rgba(102, 102, 102, 1);
+			margin-right: 20rpx;
+			padding: 0;
+		}
+	}
+	.order-head {
+		padding: 0 25rpx;
+		height: 77rpx;
+		border-bottom: 1rpx solid #dfdfdf;
+
+		.no {
+			font-size: 26rpx;
+			color: #999;
+		}
+
+		.order-status {
+			.iconfont {
+				color: #f83942;
+				font-size: 30rpx;
+				font-weight: 600;
+			}
+			.status-text {
+				font-size: 26rpx;
+
+				font-weight: 400;
+				color: #f83942;
+				margin-left: 10rpx;
+			}
+		}
+	}
+	.order-content {
+		padding: 20rpx;
+		.order-sku {
+			font-size: 24rpx;
+
+			font-weight: 400;
+			color: rgba(153, 153, 153, 1);
+			width: 450rpx;
+			margin-bottom: 20rpx;
+			.order-num {
+				margin-right: 10rpx;
+			}
+		}
+		.card-price-box {
+			.status-btn {
+				height: 32rpx;
+				border: 1rpx solid rgba(207, 169, 114, 1);
+				border-radius: 15rpx;
+				font-size: 20rpx;
+				font-weight: 400;
+				color: rgba(168, 112, 13, 1);
+				padding: 0 10rpx;
+				margin-left: 20rpx;
+				background: rgba(233, 183, 102, 0.16);
+			}
+			.order-price {
+				font-size: 26rpx;
+
+				font-weight: 600;
+				color: rgba(51, 51, 51, 1);
+			}
+		}
+	}
+	.goods-order {
+		border-bottom: 1px solid rgba(223, 223, 223, 0.5);
+		padding: 20rpx 20rpx 0;
+		margin-bottom: 20rpx;
+	}
+
+	.goods-bottom {
+		background: #fff;
+		padding-bottom: 30rpx;
+	}
+}
+</style>

+ 96 - 0
pages/order/after-sale/log.vue

@@ -0,0 +1,96 @@
+<!-- 售后进行 -->
+<template>
+	<view class="log-box wrap">
+		<u-time-line>
+			<u-time-line-item v-for="(log, index) in aftersaleLog" :key="log.id" nodeTop="4">
+				<template v-slot:node>
+					<view class="u-node" :style="index === 0 ? 'background: #19be6b;' : ''"><text class="u-iconfont uicon-bell-fill" style="color: #fff;font-size: 26rpx;"></text></view>
+				</template>
+				<template v-slot:content>
+					<view class="u-order-title unacive u-m-b-20">{{ log.reason }}</view>
+					<view class="u-order-desc  u-m-b-10" v-show="log.content"><u-parse :html="log.content"></u-parse></view>
+					<view class="item-img-box u-flex u-flex-wrap" v-show="log.images.length">
+						<block v-for="(img, index) in log.images" :key="index"><image :src="img" class="log-img" mode="aspectFill"></image></block>
+					</view>
+					<view class="u-order-time">{{ $u.timeFormat(log.createtime, 'yyyy-mm-dd hh:MM') }}</view>
+				</template>
+			</u-time-line-item>
+		</u-time-line>
+	</view>
+</template>
+
+<script>
+export default {
+	components: {},
+	data() {
+		return {
+			aftersaleLog: []
+		};
+	},
+	computed: {},
+	onLoad() {
+		this.getAftersaleDetail();
+	},
+	methods: {
+		// 服务单详情
+		getAftersaleDetail() {
+			let that = this;
+			that.$http('order.aftersaleDetail', {
+				id: that.$Route.query.aftersaleId
+			}).then(res => {
+				if (res.code === 1) {
+					that.aftersaleLog = res.data.logs;
+				}
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.wrap {
+	padding: 24rpx 24rpx 24rpx 40rpx;
+	background-color: #fff;
+}
+
+.u-node {
+	width: 44rpx;
+	height: 44rpx;
+	border-radius: 100rpx;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	background: #d0d0d0;
+}
+
+.u-order-title {
+	color: #333333;
+	font-weight: bold;
+	font-size: 32rpx;
+}
+
+.u-order-title.unacive {
+	color: rgb(150, 150, 150);
+}
+
+.u-order-desc {
+	color: rgb(150, 150, 150);
+	font-size: 28rpx;
+	margin-bottom: 6rpx;
+}
+.item-img-box {
+	.log-img {
+		display: block;
+		width: 140rpx;
+		height: 140rpx;
+		background-color: #ccc;
+		margin-right: 20rpx;
+		margin-bottom: 20rpx;
+		border-radius: 6rpx;
+	}
+}
+.u-order-time {
+	color: rgb(200, 200, 200);
+	font-size: 26rpx;
+}
+</style>

+ 430 - 0
pages/order/after-sale/refund.vue

@@ -0,0 +1,430 @@
+<!-- 申请售后 -->
+<template>
+	<view class="refund-wrap">
+		<!-- 售后商品 -->
+		<view class="goods-box">
+			<shopro-mini-card :title="orderItemDetail.goods_title" :image="orderItemDetail.goods_image">
+				<template #describe>
+					<view class="order-sku u-ellipsis-1">
+						<text class="order-num">数量:{{ orderItemDetail.goods_num || 0 }};</text>
+						{{ orderItemDetail.goods_sku_text ? orderItemDetail.goods_sku_text : '' }}
+					</view>
+				</template>
+				<template #cardBottom>
+					<view class="card-price-box u-flex">
+						<text class="order-price font-OPPOSANS">¥{{ orderItemDetail.goods_price || 0 }}</text>
+						<button class="u-reset-button status-btn" v-if="orderItemDetail.status_name">{{ orderItemDetail.status_name }}</button>
+					</view>
+				</template>
+			</shopro-mini-card>
+		</view>
+
+		<!-- 售后类型 -->
+		<view class="refund-item">
+			<view class="item-title">请选择售后类型</view>
+			<u-radio-group v-model="refundType" active-color="#d89f64" width="100%">
+				<u-radio shape="circle" v-for="(item, index) in refundTypeList" :key="index" :name="item.value">{{ item.title }}</u-radio>
+			</u-radio-group>
+		</view>
+
+		<!-- 申请原因 -->
+		<view class="goods-item u-flex u-row-between" @tap="showModal = true">
+			<text class="item-title">选择申请原因</text>
+			<view class="u-flex refund-cause">
+				<text class="u-m-r-20" style="color: #333;" v-if="refundCause">{{ refundCause }}</text>
+				<text class="u-m-r-20" v-else>请选择申请原因~</text>
+				<text class="u-iconfont uicon-arrow-right" style="color: #666"></text>
+			</view>
+		</view>
+
+		<!-- 联系方式 -->
+		<view class="refund-item u-m-b-20">
+			<view class="item-title">联系方式</view>
+			<view class="input-box u-flex"><input type="number" class="item-input" v-model="phone" placeholder="请输入您的联系电话" placeholder-class="input--pl" /></view>
+		</view>
+
+		<!-- 留言 -->
+		<view class="refund-item u-m-b-20">
+			<view class="item-title">相关描述</view>
+			<view class="describe-box">
+				<textarea
+					class="describe-content"
+					v-model="refundContent"
+					maxlength="120"
+					placeholder="客官~请描述您遇到的问题,建议上传照片"
+					placeholder-class="input--pl"
+					fixed
+				></textarea>
+				<view class="upload-img">
+					<u-upload
+						:showProgress="false"
+						@on-uploaded="uploadSuccess"
+						@on-remove="uploadRemove"
+						:action="`${$API_URL}index/upload`"
+						width="140"
+						height="140"
+						maxCount="9"
+					></u-upload>
+				</view>
+			</view>
+		</view>
+
+		<!-- 底部按钮 -->
+		<view class="foot-wrap safe-area-inset-bottom">
+			<view class="foot_box u-flex u-row-between safe-area-inset-bottom">
+				<button class="u-reset-button contcat-btn" @tap="onService">联系客服</button>
+				<button class="u-reset-button sub-btn" @tap="postAftersale">提交</button>
+			</view>
+		</view>
+
+		<!-- 申请原因弹窗 -->
+		<u-popup v-model="showModal" safe-area-inset-bottom @close="showModal = false" mode="bottom" :closeable="true" close-icon-pos="top-left">
+			<view class="modal-box page_box">
+				<view class="modal-head head_box u-flex u-row-center u-col-center">{{ refundList.title }}</view>
+				<view class="modal-content content_box">
+					<view class="u-flex-col u-p-20">
+						<u-radio-group v-model="refundCause" active-color="#d89f64" width="100%">
+							<u-radio class="u-m-y-10" shape="circle" v-for="item in refundList.list" :key="item.id" :name="item.val">
+								<text class="u-p-l-20">{{ item.val }}</text>
+							</u-radio>
+						</u-radio-group>
+					</view>
+				</view>
+				<view class="modal-foot foot_box u-flex u-row-center u-col-center"><button class="u-reset-button close-btn" @tap="showModal = false">确定</button></view>
+			</view>
+		</u-popup>
+	</view>
+</template>
+
+<script>
+export default {
+	components: {},
+	data() {
+		return {
+			showModal: false,
+			imgList: [], //图片地址
+			orderId: 0, //订单ID
+			orderItemDetail: {}, //订单信息
+			refundType: '', //售后类型
+			refundCause: '', //退款原因
+			refundContent: '', //相关描述
+			phone: '', //联系方式
+			refundTypeList: [
+				//售后类型
+				{
+					title: '退款',
+					value: 'refund'
+				},
+				{
+					title: '退货',
+					value: 'return'
+				},
+				{
+					title: '其他',
+					value: 'other'
+				}
+			],
+			refundList: {
+				//申请原因
+				title: '申请原因',
+				list: [
+					{
+						id: 1,
+						val: '卖家发错货了'
+					},
+					{
+						id: 2,
+						val: '退运费'
+					},
+					{
+						id: 3,
+						val: '大小/重量于商品描述不符'
+					},
+					{
+						id: 4,
+						val: '生产日期、保质期与商品不符'
+					},
+					{
+						id: 5,
+						val: '质量问题'
+					}
+				]
+			}
+		};
+	},
+	computed: {},
+	onLoad() {
+		this.getOrderItemDetail();
+	},
+	methods: {
+		// 跳转客服
+		onService() {
+			this.$Router.push('/pages/public/chat/index');
+		},
+
+		// 上传图片成功
+		uploadSuccess(e) {
+			this.imgList = [];
+			e.forEach(item => {
+				this.imgList.push(item.response.data.url);
+			});
+		},
+
+		// 移除图片
+		uploadRemove(index) {
+			this.imgList.splice(index, 1);
+		},
+
+		// 订单item详情
+		getOrderItemDetail() {
+			let that = this;
+			that.$http('order.itemDetail', {
+				id: that.$Route.query.orderId,
+				order_item_id: that.$Route.query.orderItemId
+			}).then(res => {
+				if (res.code === 1) {
+					that.orderItemDetail = res.data;
+				}
+			});
+		},
+
+		// 提交售后
+		postAftersale() {
+			let that = this;
+			that.$http('order.aftersale', {
+				type: that.refundType,
+				order_id: that.$Route.query.orderId,
+				order_item_id: that.$Route.query.orderItemId,
+				reason: that.refundCause,
+				content: that.refundContent,
+				images: that.imgList,
+				phone: that.phone
+			}, '申请中...').then(res => {
+				if (res.code === 1) {
+					//  #ifdef MP-WEIXIN
+					that.$store.commit('subscribeMessage', 'aftersale');
+					//  #endif
+					that.$Router.back();
+				}
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+// 售后项目
+.refund-item {
+	background-color: #fff;
+	border-bottom: 1rpx solid #f5f5f5;
+	padding: 30rpx;
+	.item-title {
+		font-size: 30rpx;
+		font-weight: bold;
+		color: rgba(51, 51, 51, 1);
+		margin-bottom: 20rpx;
+	}
+
+	// 留言
+	.describe-box {
+		width: 690rpx;
+		background: rgba(249, 250, 251, 1);
+		border-radius: 20rpx;
+		.describe-content {
+			width: 660rpx;
+			padding: 30rpx;
+			height: 200rpx;
+			font-size: 24rpx;
+			font-weight: 400;
+			color: #333;
+		}
+	}
+	.input--pl {
+		font-size: 24rpx;
+		font-weight: 400;
+		color: rgba(177, 179, 199, 1);
+	}
+	// 联系方式
+	.input-box {
+		height: 84rpx;
+		background: rgba(249, 250, 251, 1);
+		border-radius: 20rpx;
+		.item-input {
+			padding: 0 30rpx;
+			flex: 1;
+			font-size: 28rpx;
+			font-weight: 400;
+			color: #333;
+		}
+	}
+}
+
+.goods-box {
+	background: #fff;
+	padding: 20rpx;
+	margin-bottom: 20rpx;
+	.order-sku {
+		font-size: 24rpx;
+
+		font-weight: 400;
+		color: rgba(153, 153, 153, 1);
+		width: 450rpx;
+		margin-bottom: 20rpx;
+		.order-num {
+			margin-right: 10rpx;
+		}
+	}
+	.card-price-box {
+		.status-btn {
+			height: 32rpx;
+			border: 1rpx solid rgba(207, 169, 114, 1);
+			border-radius: 15rpx;
+			font-size: 20rpx;
+			font-weight: 400;
+			color: rgba(168, 112, 13, 1);
+			padding: 0 10rpx;
+			margin-left: 20rpx;
+			background: rgba(233, 183, 102, 0.16);
+		}
+		.order-price {
+			font-size: 26rpx;
+
+			font-weight: 600;
+			color: rgba(51, 51, 51, 1);
+		}
+	}
+}
+
+.goods-item {
+	height: 95rpx;
+	background: #fff;
+	padding: 0 25rpx;
+	margin-bottom: 20rpx;
+
+	// 售后原因
+	.refund-cause {
+		font-size: 26rpx;
+		font-weight: 400;
+		color: rgba(177, 179, 199, 1);
+	}
+
+	&:nth-of-type(2n) {
+		margin-bottom: 0;
+		border-top: 0;
+	}
+
+	.item-title {
+		font-size: 28rpx;
+	}
+
+	.price {
+		font-size: 28rpx;
+		color: #a8700d;
+	}
+}
+
+.upload-img {
+	padding: 25rpx;
+
+	.upload-title {
+		font-size: 28rpx;
+	}
+}
+
+.img-box {
+	display: flex;
+	align-items: center;
+	flex-wrap: wrap;
+
+	.choose-img,
+	.preview-box {
+		width: 110rpx;
+		height: 110rpx;
+		background: rgba(249, 250, 251, 1);
+		border: 1rpx solid rgba(223, 223, 223, 1);
+		margin-right: 20rpx;
+		margin-bottom: 20rpx;
+		position: relative;
+
+		&:nth-child(5n) {
+			margin-right: 0;
+		}
+		.preview-img {
+			width: 100%;
+			height: 100%;
+		}
+	}
+}
+
+.foot-wrap {
+	height: 100rpx;
+	width: 100%;
+}
+.foot_box {
+	height: 100rpx;
+	background-color: #fff;
+	padding: 0 30rpx;
+	position: fixed;
+	width: 100%;
+	bottom: 0;
+	left: 0;
+	border-top: 1rpx solid #f5f5f5;
+	.sub-btn {
+		width: 340rpx;
+		line-height: 74rpx;
+		background: linear-gradient(90deg, rgba(233, 180, 97, 1), rgba(238, 204, 137, 1));
+		border: 1rpx solid rgba(238, 238, 238, 1);
+		border-radius: 38rpx;
+		color: rgba(#fff, 0.9);
+		font-size: 28rpx;
+	}
+	.contcat-btn {
+		width: 340rpx;
+		line-height: 74rpx;
+		background: rgba(238, 238, 238, 1);
+		border-radius: 38rpx;
+		font-size: 28rpx;
+
+		font-weight: 400;
+		color: rgba(51, 51, 51, 1);
+	}
+}
+
+.modal-box {
+	width: 750rpx;
+	height: 680rpx;
+	border-radius: 30rpx 30rpx 0 0;
+	background: #fff;
+
+	.modal-head {
+		height: 100rpx;
+		font-size: 30rpx;
+	}
+
+	.modal-content {
+		font-size: 28rpx;
+
+		.sel-item {
+			height: 100rpx;
+			width: 100%;
+			padding: 0 20rpx;
+			border-bottom: 1rpx solid rgba(223, 223, 223, 0.5);
+
+			.sel-radio {
+				transform: scale(0.7);
+			}
+		}
+	}
+
+	.modal-foot {
+		.close-btn {
+			width: 710rpx;
+			line-height: 80rpx;
+			background: linear-gradient(90deg, rgba(233, 180, 97, 1), rgba(238, 204, 137, 1));
+			border: 1rpx solid rgba(238, 238, 238, 1);
+			border-radius: 40rpx;
+			color: rgba(#fff, 0.9);
+		}
+	}
+}
+</style>

+ 116 - 0
pages/order/components/sh-select-coupon.vue

@@ -0,0 +1,116 @@
+<template>
+	<view class="select-coupon-wrap ">
+		<view class="coupon-item u-flex u-row-between">
+			<view class="item-title">优惠券</view>
+			<view class="u-flex" @tap="showModal = true">
+				<text class="price" v-if="couponList.length">{{ title }}</text>
+				<text class="sel-coupon" v-else>暂无优惠券</text>
+				<text class="u-iconfont uicon-arrow-right" style="color: #bfbfbf"></text>
+			</view>
+		</view>
+
+		<u-popup v-model="showModal" mode="bottom" :closeable="true" close-icon-pos="top-right" border-radius="30">
+			<view class="modal-box page_box">
+				<view class="modal-head u-flex u-row-center u-col-center"><text class="head-title">选择优惠券</text></view>
+				<view class="modal-content content_box u-flex-col u-p-20">
+					<u-radio-group v-model="radio" @change="radioGroupChange" width="100%" activeColor="#e9b564">
+						<view class="radio-item">
+							<u-radio :name="0"><text class="coupon-title u-p-l-10">不使用优惠券</text></u-radio>
+						</view>
+						<view class="radio-item" v-for="(radio, index) in couponList" :key="radio.user_coupons_id">
+							<u-radio :name="index + 1">
+								<text class="coupon-title u-p-l-10">{{ radio.name }}:{{ `满${radio.enough}减${radio.amount}` }}</text>
+							</u-radio>
+						</view>
+					</u-radio-group>
+				</view>
+				<button class="u-reset-button serve-btn" @tap="showModal = false">确定</button>
+			</view>
+		</u-popup>
+	</view>
+</template>
+
+<script>
+/**
+ * 选择优惠券模态框
+ * @property {Object} couponList - 可用优惠券列表数据
+ *
+ */
+export default {
+	components: {},
+	data() {
+		return {
+			showModal: false,
+			radio: 0,
+			title: '选择优惠券'
+		};
+	},
+	props: {
+		couponList: {
+			type: Array,
+			default: () => []
+		}
+	},
+	computed: {},
+	methods: {
+		radioGroupChange(index) {
+			this.title = index > 0 ? '-¥' + this.couponList[index - 1].amount : '选择优惠券';
+			this.$emit('change', index - 1);
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+// 优惠券项
+.coupon-item {
+	border-top: 1rpx solid rgba(#dfdfdf, 0.5);
+	height: 100rpx;
+	background: #fff;
+	padding: 0 25rpx;
+	.item-title {
+		font-size: 28rpx;
+		margin-right: 20rpx;
+	}
+	.price {
+		font-size: 26rpx;
+		color: #ff0000;
+		margin-right: 20rpx;
+	}
+	.sel-coupon {
+		font-size: 26rpx;
+		color: #c4c4c4;
+		margin-right: 20rpx;
+	}
+}
+
+// 模态框
+.modal-box {
+	width: 750rpx;
+	height: 700rpx;
+	background: #fff;
+	padding: 30rpx;
+
+	// 按钮
+	.serve-btn {
+		width: 690rpx;
+		line-height: 80rpx;
+		background: linear-gradient(90deg, rgba(240, 199, 133, 1), rgba(246, 214, 157, 1));
+		border-radius: 40rpx;
+		color: rgba(#fff, 0.9);
+		margin-top: 40rpx;
+	}
+	.radio-item {
+		width: 690rpx;
+		margin-bottom: 20rpx;
+	}
+	// 标题
+	.modal-head {
+		margin-bottom: 30rpx;
+		.head-title {
+			font-size: 32rpx;
+			font-weight: bold;
+		}
+	}
+}
+</style>

+ 1651 - 0
pages/order/confirm.vue

@@ -0,0 +1,1651 @@
+<!-- 确认订单 -->
+<template>
+	<view class="order-wrap">
+		<!-- 地址栏 -->
+		<view class="head_box" v-if="orderPre.need_address" @tap="jump('/pages/user/address/list', { from: 'order' })">
+			<view class="add-address-box u-flex u-flex-1" v-if="!addressId">
+				<view class="box-bg u-p-30 u-flex-1 u-flex u-row-between">
+					<text class="select-notice">请选择收货地址</text>
+					<text class="u-iconfont uicon-arrow-right" style="color: #bfbfbf;"></text>
+				</view>
+			</view>
+			<view class="add-address-box u-p-30" v-else>
+				<view class="top u-flex">
+					<text class="name">{{ addressData.consignee }}</text>
+					<text class="phone">{{ addressData.phone }}</text>
+					<text class="tag" v-show="addressData.is_default == 1">默认</text>
+				</view>
+				<view class="detail u-flex u-row-between">
+					<view class="address">
+						{{ addressData.province_name }}{{ addressData.city_name }}{{ addressData.area_name }}{{ addressData.address }}
+					</view>
+					<text class="u-iconfont uicon-arrow-right" style="color: #bfbfbf;"></text>
+				</view>
+			</view>
+		</view>
+
+		<view class="u-m-b-10">
+			<!-- 确认订单商品卡片 -->
+			<view class="goods-list" v-for="g in perGoodsList" :key="g.sku_price_id">
+				<view class="goods-card u-p-30">
+					<shopro-mini-card :title="g.detail.title" :image="g.detail.image">
+						<template #describe>
+							<view class="order-sku u-ellipsis-1">
+								<text class="order-num">
+									{{ g.detail.current_sku_price && g.detail.current_sku_price.goods_sku_text ? g.detail.current_sku_price.goods_sku_text : '' }}
+								</text>
+							</view>
+						</template>
+						<template #cardBottom>
+							<view class="goods-price u-flex  u-row-between">
+								<view class="">
+									<text
+										v-show="orderType === 'score'">{{ g.detail.current_sku_price.score }}积分+</text>
+									<text>¥{{ g.detail.current_sku_price.price }}</text>
+								</view>
+								<text class="goods-num">x{{ g.goods_num }}</text>
+							</view>
+						</template>
+					</shopro-mini-card>
+				</view>
+
+				<!-- 配送方式 -->
+				<view class="logistic item-list u-flex u-row-between" @tap="onSelExpressType(g)">
+					<view class="item-title">配送方式</view>
+					<view class="u-flex">
+						<view class="detail">{{ getCurGoodsExpress(g) }}</view>
+						<text class="u-iconfont uicon-arrow-right" style="color: #bfbfbf;"></text>
+					</view>
+				</view>
+			</view>
+
+			<block v-if="perGoodsList.length">
+				<!-- 备注 -->
+				<view class="remark-box u-flex item-list u-p-30">
+					<view class="item-title">备注</view>
+					<input class="item-input " placeholder-class="input-pl" type="text" v-model="remark"
+						placeholder="建议留言前先于卖家沟通确认" />
+				</view>
+
+				<!-- 选择优惠券 -->
+				<sh-select-coupon v-if="hasCoupons" :couponList="couponList" @change="selectCoupon"></sh-select-coupon>
+
+				<!-- 积分 -->
+				<view class="score item-list u-flex u-row-between" v-show="orderType === 'score'">
+					<view class="u-flex"><text class="item-title">积分</text></view>
+					<view class="price">-{{ orderPre.score_amount || 0 }}积分</view>
+				</view>
+				<!-- 金额明细 -->
+				<view class=" u-flex u-row-between item-list border-top u-m-b-10">
+					<view class="item-title">商品金额</view>
+					<view class="u-flex">
+						<text class="price">¥{{ orderPre.goods_amount || '0.00' }}</text>
+					</view>
+				</view>
+
+				<!-- 活动优惠 -->
+				<u-collapse
+					v-if="orderPre.activity_discount_infos && orderPre.activity_discount_infos.length && Number(orderPre.activity_discount_money)"
+					event-type="close" :arrow="true" :accordion="true" arrowColor="#bfbfbf"
+					:head-style="{ background: '#fff', height: '100rpx' }">
+					<u-collapse-item>
+						<block slot="title">
+							<view style="width: 680rpx;padding-right: 0;" class="u-flex u-row-between item-list">
+								<view class="item-title">活动优惠</view>
+								<view class="u-flex">
+									<text class="price"
+										style="margin-right: 0;">-¥{{ orderPre.activity_discount_money || '0.00' }}</text>
+								</view>
+							</view>
+						</block>
+						<view class="" v-for="item in orderPre.activity_discount_infos" :key="item.activity_id">
+							<view class="u-flex u-row-between item-list" v-if="item.activity_type !== 'free_shipping'">
+								<view class="item-title">{{ item.activity_title }}</view>
+								<view class="u-flex">
+									<text class="price"
+										style="color: #666;">-¥{{ item.activity_discount_money || '0.00' }}</text>
+								</view>
+							</view>
+						</view>
+					</u-collapse-item>
+				</u-collapse>
+				<!-- 价格 -->
+				<view class="price-box  u-flex u-row-between item-list">
+					<view class="item-title u-flex u-col-center">
+						<view class="u-m-r-10">运费</view>
+						<view class="activity-title" v-if="Number(orderPre.dispatch_discount_money) > 0">活动减¥
+							{{ orderPre.dispatch_discount_money }}</view>
+					</view>
+					<view class="u-flex">
+						<text class="origin-price u-m-r-10"
+							v-if="Number(orderPre.dispatch_discount_money) > 0">-¥{{ orderPre.dispatch_amount }}</text>
+						<text
+							class="price">¥{{ Number(orderPre.dispatch_amount) - Number(orderPre.dispatch_discount_money) || '0.00' }}</text>
+					</view>
+				</view>
+
+				<!-- 发票 -->
+				<view v-if="orderPre && Number(orderPre.invoice_amount)"
+					class="u-flex u-row-between item-list u-p-20 border-top">
+					<view class="item-title">申请发票</view>
+					<view class="u-flex u-col-center" @tap="onInvoice">
+						<text class="selected-invoice"
+							style="font-size: 28rpx;color:#c4c4c4;">{{ selectedInvoice }}</text>
+						<text class="u-iconfont uicon-arrow-right" style="color: #bfbfbf;"></text>
+					</view>
+				</view>
+			</block>
+		</view>
+
+		<!-- 底部 -->
+		<view class="foot-box-wrap">
+			<view class="foot-box u-flex u-row-between safe-area-inset-bottom">
+				<view class="u-flex">
+					<text class="num">共{{ totalNum }}件</text>
+					<view class="all-money">
+						<text>合计:</text>
+						<text class="price">¥{{ orderPre.total_fee || '0.00' }}</text>
+					</view>
+				</view>
+				<button class="cu-btn sub-btn" @tap="subOrder" :disabled="isDisabled"
+					hover-class="btn-hover">提交订单</button>
+			</view>
+		</view>
+
+		<!-- 发票弹窗 -->
+		<u-popup v-model="showInvoice" @close="showInvoice = false" safe-area-inset-bottom mode="bottom"
+			:closeable="false" border-radius="20">
+			<view class="invoice-modal page_box">
+				<view class="invoice-head u-flex u-col-center">
+					<view class="head-nav u-flex u-col-center u-row-center" @tap="changeInvoiceType('person')">
+						<text class="nav-title " :class="{ 'nav-title--active': invoiceType === 'person' }">个人</text>
+						<view v-show="invoiceType === 'person'" class="head-nav--active"></view>
+					</view>
+					<view class="head-nav u-flex u-col-center u-row-center" @tap="changeInvoiceType('company')">
+						<text class="nav-title"
+							:class="{ 'nav-title--active': invoiceType === 'company' }">企/事业单位</text>
+						<view v-show="invoiceType === 'company'" class="head-nav--active"></view>
+					</view>
+				</view>
+				<view class="invoice-content content_box">
+					<u-form
+						:labelStyle="{ 'font-size': '28rpx', 'font-weight': '600', color: '#595959', 'padding-left': '20rpx' }"
+						label-position="left" :model="invoiceForm.model" :rules="invoiceForm.rules" ref="invoiceRef"
+						:errorType="['toast']">
+						<u-form-item class="u-p-r-20" label-width="150" label="名称:" prop="header_name">
+							<u-input placeholder="请填写名称" v-model="invoiceForm.model.header_name" type="text"></u-input>
+						</u-form-item>
+						<u-form-item class="u-p-r-20" v-if="invoiceType === 'company'" label-width="220" label="纳税人识别号:"
+							prop="tax_no">
+							<u-input placeholder="请输入纳税人识别号" v-model="invoiceForm.model.tax_no" type="text"></u-input>
+						</u-form-item>
+						<u-form-item class="u-p-r-20" label-width="150" label="手机号:" prop="mobile">
+							<u-input placeholder="请输入填写手机号" v-model="invoiceForm.model.mobile" type="number"></u-input>
+						</u-form-item>
+					</u-form>
+				</view>
+				<view class="invoice-foot">
+					<view class="invoite-price u-flex u-col-center u-row-center u-m-b-40">
+						<text class="price-title">可开票金额:</text>
+						<text class="price-num">{{ orderPre.invoice_amount }}元</text>
+					</view>
+					<view class="u-flex u-col-center u-row-around u-p-b-30">
+						<button class="cancel-btn u-reset-button" @tap="cancelInvoice">取消</button>
+						<button class="save-btn u-reset-button" @tap="saveInvoice">确定</button>
+					</view>
+				</view>
+			</view>
+		</u-popup>
+
+		<!-- 配送方式弹窗 -->
+		<u-popup v-model="showExpressType" @close="showExpressType = false" safe-area-inset-bottom mode="bottom"
+			:closeable="false" border-radius="20">
+			<!-- 配送方式tab-->
+			<view class="express-type page_box">
+				<view class="express-type__head head-box u-flex u-col-center">
+					<view class="express-type__head-nav u-flex u-col-center u-row-center"
+						v-for="(nav, index) in expressType" :key="nav.id" @tap="changeExpressType(nav.value)"
+						v-if="inExpressType.includes(nav.value)">
+						<text class="head-nav__title"
+							:class="{ 'head-nav__title--active': expressTypeCur === nav.value }">{{ nav.title }}</text>
+						<view :class="expressClass" v-show="expressTypeCur === nav.value"></view>
+					</view>
+				</view>
+				<view class="express-type__content content_box">
+					<!-- 快递 -->
+					<!-- 空 -->
+					<view class="empty-address u-flex u-col-center"
+						v-if="!addressId && expressTypeCur !== 'selfetch' && expressTypeCur !== 'autosend'"
+						@tap="jump('/pages/user/address/list', { from: 'order' })">
+						请选择收货地址
+
+						<text class="u-iconfont uicon-arrow-right" style="color: #999;"></text>
+					</view>
+					<!-- 地址 -->
+					<view class="express-address" v-if="expressTypeCur == 'express' && addressId">
+						<view class="express-top  u-flex u-row-between"
+							@tap="jump('/pages/user/address/list', { from: 'order' })">
+							<view class="u-flex">
+								<text class="tag" style="white-space: nowrap;"
+									v-show="addressData.is_default == 1">默认</text>
+								<text
+									class="address">{{ addressData.province_name }}{{ addressData.city_name }}{{ addressData.area_name }}{{ addressData.address }}</text>
+								<view class="address-guide"><text class="u-iconfont uicon-arrow-right"
+										style="color: #bfbfbf"></text></view>
+							</view>
+
+							<view class="address-location u-flex-col u-col-center">
+								<image class="location-img" :src="$IMG_URL + '/imgs/order/e0.png'" mode=""></image>
+								<text class="location-text">物流快递</text>
+							</view>
+						</view>
+						<view class="express-content">
+							<view class="phone-box1">
+								<text class="name">{{ addressData.consignee }}</text>
+								<text class="phone">{{ addressData.phone }}</text>
+							</view>
+						</view>
+					</view>
+					<!-- 自提  -->
+					<view class="express-address" v-if="expressTypeCur == 'selfetch'">
+						<!-- 定位 -->
+						<view class="u-flex-col u-col-center location-box" v-if="!lat">
+							<image class="nolocation-img" :src="$IMG_URL + '/imgs/order/location.png'" mode=""></image>
+							<text class="location-title">开启定位服务</text>
+							<text class="location-tip">为你推荐更精准的位置信息噢~</text>
+							<button class="u-reset-button open-location" @tap="getLocation">去开启</button>
+						</view>
+						<!-- 已定位 -->
+						<view class="" v-else>
+							<view class="express-top u-flex u-col-center u-row-between"
+								@tap="jump('/pages/order/express/store-address', { goodsId: currentGoodsId, lat: lat, lng: lng, storeId: storeInfo ? storeInfo.id : 0 })">
+								<view class="u-flex">
+									<text class="tag1" v-if="addressData.is_default == 1">最近</text>
+									<text class="address">{{ storeInfo ? storeInfo.name : '暂无自提点' }}</text>
+									<text class="u-iconfont uicon-arrow-right" style="color: #999;"></text>
+								</view>
+								<view class="address-location u-flex-col u-col-center">
+									<image class="location-img" :src="$IMG_URL + '/imgs/order/e1.png'" mode=""></image>
+									<view class="location-text">距您{{ storeInfo ? storeInfo.distance_text : 0 }}</view>
+								</view>
+							</view>
+							<view class="express-content u-flex u-col-cetner">
+								<view class="time-box">
+									<view class="box-title u-m-b-20">到店时间</view>
+									<view class="box-content u-flex u-col-center" @tap="checkExpressTime('selfetch')">
+										<text
+											class="box-text">{{ checkTime['day'][checkDayCur].title }}{{ checkTime['time'][checkTimeCur] }}</text>
+										<text class="u-iconfont uicon-arrow-right" style="color: #999;"></text>
+									</view>
+								</view>
+								<view class="box-line"></view>
+								<view class="phone-box">
+									<view class="box-title u-m-b-20">预留电话</view>
+									<view class="box-content u-flex" style="width: 340rpx;">
+										<u-input placeholder="请输入预留电话" placeholderStyle="font-size:24rpx" border
+											height="40" :focus="getFocus" v-model="selfPhone" type="number"></u-input>
+									</view>
+								</view>
+							</view>
+							<view class="express-bottom" @tap="checkProtocol">
+								<u-checkbox active-color="#f0c785" :value="isProtocol">
+									<view class="protocol">
+										同意并接受
+										<text class="protocol-text"
+											@tap.stop="jump('/pages/public/richtext', { id: initStore.selfetch_protocol })">《到店自提服务协议》</text>
+									</view>
+								</u-checkbox>
+							</view>
+						</view>
+					</view>
+					<!-- 商家配送 -->
+					<view class="express-address" v-if="expressTypeCur == 'store' && addressId">
+						<view class="express-top u-flex u-row-between"
+							@tap="jump('/pages/user/address/list', { from: 'order' })">
+							<view class="">
+								<text class="tag" v-if="addressData.is_default == 1">默认</text>
+								<text
+									class="address">{{ addressData.province_name }}{{ addressData.city_name }}{{ addressData.area_name }}{{ addressData.address }}</text>
+								<text class="address-guide u-iconfont uicon-arrow-right" style="color: #999;"></text>
+							</view>
+
+							<view class="address-location u-flex-col u-col-center">
+								<image class="location-img" :src="$IMG_URL + '/imgs/order/e2.png'" mode=""></image>
+								<text class="location-text">商家配送</text>
+							</view>
+						</view>
+						<view class="express-content u-flex">
+							<view class="time-box">
+								<view class="box-title u-m-b-20">配送时间</view>
+								<view class="box-content u-flex" @tap="checkExpressTime('store')">
+									<text
+										class="box-text">{{ checkTime['day'][checkDayCur].title }}{{ checkTime['time'][checkTimeCur] }}</text>
+									<text class="u-iconfont uicon-arrow-right" style="color: #999"></text>
+								</view>
+							</view>
+							<view class="box-line"></view>
+							<view class="phone-box">
+								<view class="box-title u-m-b-20">预留电话</view>
+								<view class="box-content u-flex">
+									<view class="box-content u-flex" style="width: 340rpx;">
+										<u-input placeholder="请输入预留电话" placeholderStyle="font-size:24rpx" border
+											height="40" :focus="getFocus" v-model="selfPhone" type="number"></u-input>
+									</view>
+								</view>
+							</view>
+						</view>
+					</view>
+					<!-- 自动发货 -->
+					<view class="express-address" v-if="expressTypeCur == 'autosend'">
+						<view class="express-top u-flex u-row-between">
+							<text class="dispatch-notice">订单支付完成后,请在订单详情页查看发货信息</text>
+							<view class="address-location u-flex-col u-col-center">
+								<image class="location-img" :src="$IMG_URL + '/imgs/order/e3.png'" mode=""></image>
+								<text class="location-text">自动发货</text>
+							</view>
+						</view>
+					</view>
+				</view>
+				<view class="express-type__bottom u-flex u-row-between u-p-b-20"
+					v-show="expressTypeCur !== 'selfetch' || (expressTypeCur == 'selfetch' && lat)">
+					<button class="u-reset-button cancel-btn" @tap="hideExpressType">取消</button>
+					<button class="u-reset-button save-btn" @tap="saveExpressType">确定</button>
+				</view>
+			</view>
+		</u-popup>
+
+		<!-- 配送时间弹窗 -->
+		<u-popup v-model="showCheckTime" mode="bottom" safe-area-inset-bottom :closeable="true"
+			close-icon-pos="top-right" border-radius="20">
+			<view class="checkTime-box page_box">
+				<view class="checkTime-head">选择{{ checkType }}时间</view>
+				<view class="checkTime-content content_box u-flex">
+					<view class="checkTime-content__left">
+						<view class="left-item u-flex u-row-center u-col-center" @tap="check('day', index)"
+							:class="{ 'item--active': checkDayCur == index }" v-for="(day, index) in checkTime.day"
+							:key="day.value">
+							{{ day.title }}
+						</view>
+					</view>
+					<scroll-view class="checkTime-content__right scroll-box" :scroll-into-view="'c' + checkTimeId"
+						scroll-y scroll-with-animation>
+						<view class="right-item" :id="'c' + time.split(':')[0]" @tap="check('time', index)"
+							:class="{ 'item--active': checkTimeCur == index }" v-for="(time, index) in checkTime.time"
+							:key="time">
+							{{ time }}
+						</view>
+					</scroll-view>
+				</view>
+				<view class=" checkTime-foot u-flex u-row-center u-col-center"><button class="u-reset-button save-btn"
+						@tap="showCheckTime = false">保存并使用</button></view>
+			</view>
+		</u-popup>
+	</view>
+</template>
+
+<script>
+	import shSelectCoupon from './components/sh-select-coupon.vue';
+	import { mapMutations, mapActions, mapState, mapGetters } from 'vuex';
+	import Auth from '@/shopro/permission/index.js';
+	export default {
+		components: {
+			shSelectCoupon
+		},
+		data() {
+			return {
+				platform: this.$platform.get(),
+				totalNum: 0,
+				couponList: [], //优惠券列表
+				addressData: {}, //收货地址
+				addressId: 0, //收货地址ID
+				customStyle: {
+					//自定义按钮样式
+					width: '210rpx',
+					lineHeight: '70rpx',
+					background: 'linear-gradient(90deg, rgba(233, 180, 97, 1), rgba(238, 204, 137, 1))',
+					boxShadow: ' 0px 7rpx 6rpx 0px rgba(229, 138, 0, 0.22)',
+					borderRadius: '100rpx',
+					fontSize: '28rpx',
+					border: 'none',
+					color: '#fff',
+					margin: '0'
+				},
+				isDisabled: false, //提交
+				showCheckTime: false, //配送时间弹窗。
+				checkTime: {}, //配送时间数据
+				showExpressType: false, //配送方式弹窗
+				expressTypeMap: {
+					express: '物流快递',
+					selfetch: '到店/自提',
+					store: '商家配送',
+					autosend: '自动发货'
+				},
+				expressType: [
+					//配送方式
+					{
+						id: 'e1',
+						title: '物流快递',
+						value: 'express'
+					},
+					{
+						id: 'e2',
+						title: '到店/自提',
+						value: 'selfetch'
+					},
+					{
+						id: 'e3',
+						title: '商家配送',
+						value: 'store'
+					},
+					{
+						id: 'e4',
+						title: '自动发货',
+						value: 'autosend'
+					}
+				],
+				storeList: [], //门店列表
+				storeInfo: {
+					id: 0,
+					name: '',
+					dispatch_text: ''
+				}, //自提点商家信息
+				from: '',
+				orderType: '',
+				grouponBuyType: 'alone',
+				grouponId: 0,
+				goodsList: [], //传递过来的参数
+				perGoodsList: {}, //确认单订单商品
+				currentGoodsId: 0, //当前商品id.
+				currentSkuId: 0, //商品的规格ID
+				remark: '',
+				orderPre: {},
+				couponId: 0,
+				couponPrice: '选择优惠券',
+				expressTypeCur: '',
+				inExpressType: [], //当前商品支持的配送方式。
+
+				isProtocol: true, //自提协议。
+				selfPhone: '', //编辑手机号
+				getFocus: false, //获取焦点。
+				checkType: '自提',
+				checkTimeCur: 0, //默认选中时间。
+				checkTimeId: 'c1', //锚点用
+				checkDayCur: 0, //默认日期
+				lat: 0, //地理位置
+				lng: 0,
+
+				showInvoice: false, //申请发票
+				invoiceType: 'person',
+				selectedInvoice: '不开具发票',
+				invoiceForm: {
+					model: {
+						header_name: '',
+						mobile: '',
+						tax_no: '',
+						type: 'person'
+					},
+					rules: {
+						mobile: [{
+								required: true,
+								message: '请输入手机号',
+								trigger: ['change', 'blur']
+							},
+							{
+								validator: (rule, value, callback) => {
+									return this.$u.test.mobile(value);
+								},
+								message: '手机号码不正确',
+								trigger: ['change', 'blur']
+							}
+						],
+						header_name: [{
+							required: true,
+							message: '请填写名称',
+							trigger: ['change', 'blur']
+						}]
+					}
+				}
+			};
+		},
+		computed: {
+			...mapGetters(['initStore']),
+			expressClass() {
+				let cl = 'head-nav--active';
+				const { expressType, expressTypeCur } = this;
+				if (expressTypeCur === 0) {
+					cl = 'head-nav__left--active';
+				}
+				if (expressTypeCur === expressType.length - 1) {
+					cl = 'head-nav__right--active';
+				}
+				return cl;
+			},
+			// 是否可以使用优惠券
+			hasCoupons() {
+				if (this.couponList.length && !this.orderPre.activity_type) {
+					return true;
+				}
+				return Boolean(
+					this.couponList.length &&
+					this.orderPre.activity_type &&
+					this.orderPre.activity_type.indexOf('groupon') === -1 &&
+					this.orderPre.activity_type.indexOf('seckill') === -1 &&
+					this.orderType !== 'score'
+				);
+			}
+		},
+		onShow() {
+			// 监听选择自提点
+			uni.$once('SELECT_STORE', res => {
+				this.storeInfo = JSON.parse(res.storeInfo);
+			});
+			// 监听地址
+			uni.$on('SELECT_ADDRESS', res => {
+				if (res.addressData) {
+					this.addressData = JSON.parse(res.addressData);
+					this.addressId = this.addressData.id;
+				} else {
+					this.addressId = 0;
+				}
+				this.getPre();
+				res.addressData && uni.$off('SELECT_ADDRESS');
+			});
+		},
+		async onLoad() {
+			this.goodsList = this.$Route.query.goodsList;
+			this.from = this.$Route.query.from;
+			this.orderType = this.$Route.query.orderType;
+			this.grouponBuyType = this.$Route.query.grouponBuyType;
+			this.grouponId = this.$Route.query.grouponId;
+			await this.init();
+		},
+		methods: {
+			...mapActions(['getCartList']),
+			// 切换发票类型
+			changeInvoiceType(type) {
+				this.invoiceType = type;
+				this.invoiceForm.model.type = type;
+			},
+			// 点击开发票
+			onInvoice() {
+				this.showInvoice = !this.showInvoice;
+				this.$nextTick(() => {
+					if (this.showInvoice) {
+						this.invoiceForm.model.type = 'person';
+						this.$refs.invoiceRef.setRules(this.invoiceForm.rules);
+					}
+				});
+			},
+			// 发票确认取消
+			cancelInvoice() {
+				this.selectedInvoice = '不开具发票';
+				Object.keys(this.invoiceForm.model).map(key => (this.invoiceForm.model[key] = ''));
+				this.showInvoice = false;
+			},
+			saveInvoice() {
+				this.$refs.invoiceRef.validate(valid => {
+					if (valid) {
+						if (this.invoiceType === 'company' && !this.invoiceForm.model.tax_no) {
+							this.$u.toast('请输入纳税人识别号');
+						}
+						this.selectedInvoice = this.invoiceForm.model.header_name;
+						this.showInvoice = false;
+					}
+				});
+			},
+
+			// 初始化
+			init() {
+				uni.showLoading({
+					title: '加载中...',
+					mask: true
+				});
+				return Promise.all([this.getDefaultAddress(), this.initDate(), this.getCoupons()]).then(() => {
+					uni.hideLoading();
+				});
+			},
+			jump(path, parmas) {
+				this.$Router.push({
+					path: path,
+					query: parmas
+				});
+			},
+
+			// 格式化选择时间
+			initDate() {
+				let week = {
+					0: '周日',
+					1: '周一',
+					2: '周二',
+					3: '周三',
+					4: '周四',
+					5: '周五',
+					6: '周六'
+				};
+				let now = new Date().getTime();
+				let today = this.$u.timeFormat(now);
+				let tomorrow = this.$u.timeFormat(now + 86400000);
+				let aftertomorrow = this.$u.timeFormat(now + 172800000);
+				let week1 = week[new Date().getDay()];
+				let week2 = week[new Date(now + 86400000).getDay()];
+				let week3 = week[new Date(now + 172800000).getDay()];
+				let obj = {
+					day: [{
+							title: '今天(' + week1 + ')',
+							value: today
+						},
+						{
+							title: '明天(' + week2 + ')',
+							value: tomorrow
+						},
+						{
+							title: '后天(' + week3 + ')',
+							value: aftertomorrow
+						}
+					],
+					time: ['08:00', '09:00', '10:00', '11:00', '12:00', '13:00', '14:00', '15:00', '16:00', '17:00',
+						'18:00', '19:00'
+					]
+				};
+				this.checkTime = obj;
+			},
+
+			// 格式日期
+			check(type, index) {
+				if (type == 'time') {
+					this.checkTimeCur = index;
+					this.checkTimeId = this.checkTime['time'][index].split(':')[[0]];
+				}
+				if (type == 'day') {
+					this.checkDayCur = index;
+				}
+			},
+
+			// 获取当前商品配送方式
+			getCurGoodsExpress(goods) {
+				for (let item of this.goodsList) {
+					if (item.goods_id == goods.goods_id && goods.sku_price_id == item.sku_price_id) {
+						return this.expressTypeMap[item.dispatch_type];
+					}
+				}
+			},
+
+			// 获取位置
+			async getLocation() {
+				let authState = await new Auth('userLocation').check();
+				// #ifdef MP || APP-VUE
+				authState &&
+					uni.getLocation({
+						type: 'gcj02',
+						success: res => {
+							this.lng = res.longitude;
+							this.lat = res.latitude;
+							this.getStoreAddress();
+						},
+						fail: err => {
+							console.log('确认订单自提位置:', err);
+						}
+					});
+				// #endif
+				// #ifdef H5
+				uni.getLocation({
+					type: 'gcj02',
+					success: res => {
+						this.lng = res.longitude;
+						this.lat = res.latitude;
+						this.getStoreAddress();
+					}
+				});
+				// #endif
+			},
+			// 获取商品支持的自提点。
+			getStoreAddress() {
+				let that = this;
+				that.$http('goods.storeAddress', {
+					id: that.currentGoodsId,
+					latitude: that.lat,
+					longitude: that.lng
+				}).then(res => {
+					if (res.code == 1) {
+						that.storeInfo = res.data.data[0];
+						that.storeList = res.data.data;
+					}
+				});
+			},
+			// 订单信息
+			getPre() {
+				let that = this;
+				that.$http('order.pre', {
+					goods_list: that.goodsList,
+					from: that.from,
+					address_id: that.addressId,
+					coupons_id: that.couponId,
+					order_type: that.orderType,
+					buy_type: that.grouponBuyType,
+					groupon_id: that.grouponId
+				}).then(res => {
+					if (res.data) {
+						that.orderPre = res.data;
+						that.perGoodsList = res.data.new_goods_list;
+						that.totalNum = 0;
+						that.perGoodsList.map(item => {
+							item.selType = item.dispatch_type;
+							that.totalNum += item.goods_num;
+							that.goodsList.forEach(goods => {
+								if (item.goods_id == goods.goods_id && item.sku_price_id == goods
+									.sku_price_id) {
+									goods.dispatch_type = item.dispatch_type;
+
+									if (item.store_id) {
+										goods.store_id = item.store_id;
+									}
+								}
+							});
+						});
+					}
+				});
+			},
+			// 提交订单
+			subOrder() {
+				let that = this;
+				that.isDisabled = true;
+				that.$http(
+					'order.createOrder', {
+						goods_list: that.goodsList,
+						from: that.from,
+						address_id: that.addressId,
+						coupons_id: that.couponId,
+						remark: that.remark,
+						order_type: that.orderType,
+						buy_type: that.grouponBuyType,
+						groupon_id: that.grouponId,
+						invoice: Number(that.orderPre.invoice_amount) && that.selectedInvoice !== '不开具发票' ? { ...that
+							.invoiceForm.model, amount: that.orderPre.invoice_amount } : {}
+					},
+					'提交中...'
+				).then(res => {
+					that.isDisabled = false;
+					if (res.code === 1) {
+						that.getCartList();
+						that.$Router.replace({
+							path: res.data.status > 0 ? `/pages/order/payment/result` :
+								`/pages/order/payment/method`,
+							query: {
+								orderId: res.data.id,
+								payState: res.data.status > 0 ? 'success' : 'paying',
+								orderType: 'goods'
+							}
+						});
+					}
+				});
+			},
+
+			// 初始地址
+			getDefaultAddress() {
+				this.$http('address.defaults', {}, '', false).then(res => {
+					if (res.code === 1 && res.data) {
+						this.addressData = res.data;
+						this.addressId = res.data.id;
+					}
+					this.getPre();
+				});
+			},
+
+			// 可用优惠券
+			getCoupons() {
+				let that = this;
+				that.$http('order.coupons', {
+					goods_list: that.goodsList,
+					from: that.from,
+					address_id: that.addressId,
+					coupons_id: that.couponId,
+					order_type: that.orderType
+				}).then(res => {
+					if (res.code === 1) {
+						that.couponList = res.data;
+					}
+				});
+			},
+
+			// 选择优惠券
+			selectCoupon(index) {
+				this.couponId = index >= 0 ? this.couponList[index].user_coupons_id : 0;
+				this.getPre();
+			},
+
+			// 显示配送方式弹窗
+			async onSelExpressType(goods) {
+				this.showExpressType = true;
+				this.inExpressType = goods.detail.dispatch_type_arr;
+				this.currentGoodsId = goods.goods_id;
+				this.currentSkuId = goods.sku_price_id;
+				this.goodsList.forEach(item => {
+					if (item.goods_id == this.currentGoodsId && this.currentSkuId == item.sku_price_id) {
+						this.expressTypeCur = item.dispatch_type;
+						this.selfPhone = item.dispatch_phone ? item.dispatch_phone : this.addressData && this
+							.addressData.phone;
+						this.checkDayCur = item.checkDayCur ? item.checkDayCur : 0;
+						this.checkTimeCur = item.checkTimeCur ? item.checkTimeCur : 0;
+						if (this.expressTypeCur == 'selfetch') {
+							!this.lat && this.getLocation();
+							this.storeList.forEach(store => {
+								if (item.store_id == store.id) {
+									this.storeInfo = store;
+								}
+							});
+							this.selfPhone = item.dispatch_phone ? item.dispatch_phone : this.addressData &&
+								this.addressData.phone;
+						}
+					}
+				});
+			},
+			// 关闭配送方式弹窗
+			hideExpressType() {
+				this.showExpressType = false;
+				this.changeGoodsList();
+			},
+			// 保存配送方式
+			saveExpressType() {
+				if (this.expressTypeCur === 'selfetch') {
+					if (!this.storeInfo) {
+						this.$u.toast('暂无自提点,请选择其他配送方式');
+						return false;
+					}
+					if (!this.isProtocol) {
+						this.$u.toast('请先勾选门店协议');
+						return false;
+					}
+				}
+
+				this.showExpressType = false;
+				this.changeGoodsList();
+				this.getPre();
+			},
+
+			// 更改提交数据
+			changeGoodsList() {
+				this.goodsList.forEach(goods => {
+					if (goods.goods_id == this.currentGoodsId && this.currentSkuId == goods.sku_price_id) {
+						goods.dispatch_type = this.expressTypeCur;
+						goods.dispatch_phone = this.selfPhone;
+						goods.dispatch_date = this.checkTime['day'][this.checkDayCur].value + ' ' + this.checkTime[
+							'time'][this.checkTimeCur] + ':00';
+						if (this.expressTypeCur == 'selfetch') {
+							if (!this.storeInfo) {
+								this.$u.toast('暂无自提点,请选择其他配送方式');
+								return false;
+							}
+							goods.store_id = this.storeInfo.id;
+						}
+						goods.checkDayCur = this.checkDayCur;
+						goods.checkTimeCur = this.checkTimeCur;
+					}
+				});
+			},
+
+			// 选择快递方式
+			changeExpressType(cur) {
+				this.expressTypeCur = cur;
+				this.getFocus = false;
+				cur === 'selfetch' && !this.lat && this.getLocation();
+			},
+
+			// 是否同意协议
+			checkProtocol() {
+				this.isProtocol = !this.isProtocol;
+			},
+			// 选择配送时间
+			checkExpressTime(type) {
+				switch (type) {
+					case 'store':
+						this.checkType = '配送';
+						break;
+					case 'selfetch':
+						this.checkType = '自提';
+						break;
+					default:
+						this.checkType = '自提';
+				}
+				this.showCheckTime = !this.showCheckTime;
+			}
+		}
+	};
+</script>
+
+<style lang="scss" scoped>
+	// 发票弹窗
+	.invoice-modal {
+		width: 750rpx;
+		background-color: #fff;
+		border-radius: 20rpx 20rpx 0 0;
+		height: 700rpx;
+		overflow: visible;
+	
+	.invoice-foot {
+			.price-title {
+				font-size: 28rpx;
+				font-weight: 400;
+				color: #333333;
+			}
+
+			.price-num {
+				font-size: 28rpx;
+				font-weight: 500;
+				color: #eab662;
+			}
+
+			.cancel-btn {
+				width: 335rpx;
+				line-height: 74rpx;
+				background: rgba(238, 238, 238, 1);
+				border-radius: 37rpx;
+				font-size: 28rpx;
+				font-weight: 400;
+				color: rgba(51, 51, 51, 1);
+			}
+
+			.save-btn {
+				width: 335rpx;
+				line-height: 74rpx;
+				background: linear-gradient(90deg, rgba(233, 180, 97, 1), rgba(238, 204, 137, 1));
+				border-radius: 37rpx;
+				font-size: 28rpx;
+				font-weight: 400;
+				color: rgba(255, 255, 255, 1);
+			}
+		}
+
+		.invoice-head {
+			width: 100%;
+			height: 80rpx;
+			background: #f8e3bd;
+			border-radius: 20rpx 20rpx 0 0;
+
+			.head-nav {
+				width: (750rpx/2);
+				position: relative;
+				height: 100%;
+			}
+
+			.nav-title {
+				font-size: 24rpx;
+				font-weight: 500;
+				color: #666;
+				position: relative;
+				z-index: 6;
+			}
+
+			.nav-title--active {
+				color: #a8700d;
+				font-size: 26rpx;
+			}
+
+			.head-nav--active {
+				position: absolute;
+				left: 50%;
+				transform: translateX(-50%);
+				bottom: 0;
+				background: #fff;
+				width: 100%;
+				height: 80rpx;
+				background-color: #fff;
+				border-radius: 20rpx 20rpx 0px 0px;
+
+				&::after {
+					content: '';
+					display: block;
+					width: 40rpx;
+					height: 80rpx;
+					position: absolute;
+					transform: skewX(20deg);
+					background: #fff;
+					border-top-right-radius: 20rpx;
+					top: 0;
+					right: -15rpx;
+				}
+
+				&::before {
+					content: '';
+					display: block;
+					width: 40rpx;
+					height: 80rpx;
+					position: absolute;
+					transform: skewX(-20deg);
+					background: #fff;
+					border-top-left-radius: 20rpx;
+					top: 0;
+					left: -15rpx;
+				}
+			}
+		}
+	}
+
+	// 收货地址栏
+	.head_box {
+		background-color: #fff;
+	}
+
+	.add-address-box {
+		min-height: 100rpx;
+		background: url($IMG_URL+'/imgs/order/order_address_line.png') no-repeat;
+		background-size: 100% auto;
+		background-position: bottom center;
+
+		.select-notice {
+			font-weight: 400;
+			color: rgba(153, 153, 153, 1);
+			line-height: 40rpx;
+		}
+
+		.name,
+		.phone {
+			font-size: 30rpx;
+			font-weight: 500;
+		}
+
+		.phone {
+			margin: 0 20rpx;
+		}
+
+		.tag {
+			background: rgba(233, 191, 113, 0.2);
+			border-radius: 6rpx;
+			padding: 0 16rpx;
+			line-height: 38rpx;
+			color: #a8700d;
+			font-size: 22rpx;
+		}
+
+		.detail {
+			.address {
+				margin-top: 25rpx;
+				width: 543rpx;
+				font-size: 26rpx;
+
+				font-weight: 400;
+				color: rgba(153, 153, 153, 1);
+				line-height: 40rpx;
+			}
+		}
+	}
+
+	// 备注
+	.remark-box {
+		margin-top: 20rpx;
+		background: #fff;
+		padding: 25rpx;
+
+		.item-input {
+			flex: 1;
+			text-align: end;
+			font-size: 28rpx;
+		}
+
+		.input-pl {
+			color: #c4c4c4;
+		}
+	}
+
+	// 商品卡片
+	.goods-list {
+		background: #fff;
+		margin-top: 20rpx;
+
+		.goods-card {
+			min-height: 200rpx;
+
+			.goods-price {
+				font-size: 30rpx;
+				font-weight: 500;
+				width: 480rpx;
+				color: #333333;
+
+				.goods-num {
+					font-size: 28rpx;
+					color: #c4c4c4;
+				}
+			}
+
+			.order-sku {
+				font-size: 24rpx;
+				font-weight: 400;
+				color: rgba(153, 153, 153, 1);
+				width: 440rpx;
+				margin-bottom: 20rpx;
+
+				.order-num {
+					margin-right: 10rpx;
+				}
+			}
+		}
+	}
+
+	.item-list {
+		height: 100rpx;
+		background: #fff;
+		padding: 0 25rpx;
+
+		.item-title {
+			font-size: 28rpx;
+			margin-right: 20rpx;
+
+			.activity-title {
+				font-size: 26rpx;
+				color: #999;
+			}
+		}
+
+		.detail {
+			font-size: 28rpx;
+			color: #333;
+		}
+
+		.origin-price {
+			font-size: 26rpx;
+			color: #666;
+			text-decoration: line-through;
+		}
+
+		.price {
+			font-size: 26rpx;
+			color: #ff0000;
+			margin-right: 40rpx;
+		}
+
+		.sel-coupon {
+			font-size: 26rpx;
+			color: #c4c4c4;
+			margin-right: 20rpx;
+		}
+	}
+
+	.logistic,
+	.price-box,
+	.remark-box,
+	.score,
+	.coupon {
+		border-top: 1rpx solid rgba(#dfdfdf, 0.5);
+	}
+
+	.border-top {
+		border-top: 1rpx solid rgba(#dfdfdf, 0.5);
+	}
+
+	.foot-box-wrap {
+		height: calc(100rpx + env(safe-area-inset-bottom) / 2);
+		padding-bottom: calc(env(safe-area-inset-bottom) / 2);
+		position: relative;
+		width: 100%;
+		z-index: 70;
+	}
+
+	.foot-box {
+		position: fixed;
+		display: flex;
+		align-items: center;
+		width: 100%;
+		height: calc(100rpx + env(safe-area-inset-bottom) / 2);
+		border-top: 1rpx solid #eeeeee;
+		background-color: #fff;
+		z-index: 998;
+		bottom: 0;
+		padding-bottom: calc(env(safe-area-inset-bottom) / 2);
+		padding: 0 30rpx;
+	
+	.btn-hover {
+			color: #fff;
+		}
+
+		.num {
+			font-size: 26rpx;
+			color: #999;
+		}
+
+		.all-money {
+			margin: 0 60rpx 0 20rpx;
+
+			.price {
+				color: #ff0000;
+			}
+		}
+
+		.sub-btn {
+			width: 210rpx;
+			line-height: 70rpx;
+			background: linear-gradient(90deg, rgba(233, 180, 97, 1), rgba(238, 204, 137, 1));
+			box-shadow: 0px 7rpx 6rpx 0px rgba(229, 138, 0, 0.22);
+			border-radius: 50rpx;
+			font-size: 28rpx;
+			color: #fff;
+		}
+	}
+
+	// 弹窗之配送方式
+	// 配送方式
+	.express-type {
+		width: 750rpx;
+		background-color: #fff;
+		border-radius: 20rpx 20rpx 0 0;
+		height: 700rpx;
+		overflow: visible;
+	
+	.express-type__head {
+			width: 100%;
+			height: 80rpx;
+			background: #f8e3bd;
+			border-radius: 20rpx 20rpx 0 0;
+
+			&-nav {
+				width: (750rpx/4);
+				position: relative;
+				height: 100%;
+			}
+
+			.head-nav--active {
+				position: absolute;
+				left: 50%;
+				transform: translateX(-50%);
+				bottom: 0;
+				background: #fff;
+				width: 100%;
+				height: 80rpx;
+				background-color: #fff;
+				border-radius: 20rpx 20rpx 0px 0px;
+
+				&::after {
+					content: '';
+					display: block;
+					width: 40rpx;
+					height: 80rpx;
+					position: absolute;
+					transform: skewX(20deg);
+					background: #fff;
+					border-top-right-radius: 20rpx;
+					top: 0;
+					right: -15rpx;
+				}
+
+				&::before {
+					content: '';
+					display: block;
+					width: 40rpx;
+					height: 80rpx;
+					position: absolute;
+					transform: skewX(-20deg);
+					background: #fff;
+					border-top-left-radius: 20rpx;
+					top: 0;
+					left: -15rpx;
+				}
+			}
+
+			.head-nav__left--active {
+				position: absolute;
+				left: 50%;
+				transform: translateX(-50%);
+				bottom: 0;
+				background: #fff;
+				width: 100%;
+				height: 74rpx;
+				background-color: #fff;
+				border-radius: 20rpx 20rpx 0px 0px;
+
+				&::after {
+					content: '';
+					display: block;
+					width: 40rpx;
+					height: 74rpx;
+					position: absolute;
+					transform: skewX(20deg);
+					background: #fff;
+					border-top-right-radius: 20rpx;
+					top: 0;
+					right: -15rpx;
+				}
+			}
+
+			.head-nav__right--active {
+				position: absolute;
+				left: 50%;
+				transform: translateX(-50%);
+				bottom: 0;
+				background: #fff;
+				width: 100%;
+				height: 74rpx;
+				background-color: #fff;
+				border-radius: 20rpx 20rpx 0px 0px;
+
+				&::before {
+					content: '';
+					display: block;
+					width: 40rpx;
+					height: 74rpx;
+					position: absolute;
+					transform: skewX(-20deg);
+					background: #fff;
+					border-top-left-radius: 20rpx;
+					top: 0;
+					left: -15rpx;
+				}
+			}
+
+			.head-nav__title {
+				font-size: 24rpx;
+				font-weight: 500;
+				color: #666;
+				position: relative;
+				z-index: 6;
+			}
+
+			.head-nav__title--active {
+				color: #a8700d;
+				font-size: 26rpx;
+			}
+		}
+
+		.express-type__content {
+			.empty-address {
+				height: 120rpx;
+				padding: 0 25rpx;
+				font-size: 28rpx;
+
+				font-weight: 400;
+				color: rgba(153, 153, 153, 1);
+			}
+
+			// 无定位
+			.location-box {
+				height: 500rpx;
+				justify-content: center;
+
+				.nolocation-img {
+					width: 74rpx;
+					height: 90rpx;
+					margin-bottom: 40rpx;
+				}
+
+				.location-title {
+					font-size: 35rpx;
+
+					font-weight: bold;
+					color: rgba(70, 53, 27, 1);
+					margin-bottom: 20rpx;
+				}
+
+				.location-tip {
+					font-size: 28rpx;
+
+					font-weight: 400;
+					color: rgba(153, 153, 153, 1);
+					margin-bottom: 40rpx;
+				}
+
+				.open-location {
+					width: 492rpx;
+					line-height: 70rpx;
+					background: linear-gradient(90deg, rgba(233, 180, 97, 1), rgba(238, 204, 137, 1));
+					box-shadow: 0px 7rpx 6rpx 0px rgba(229, 138, 0, 0.22);
+					border-radius: 35rpx;
+					font-size: 28rpx;
+
+					font-weight: 500;
+					color: rgba(255, 255, 255, 1);
+				}
+			}
+
+			// 快递
+			.express-address {
+				position: relative;
+				padding: 30rpx 25rpx;
+				background: url($IMG_URL+'/imgs/order/address_bg.png') no-repeat;
+				background-size: 430rpx 300rpx;
+				background-position: top right;
+
+				.express-top {
+					margin-bottom: 20rpx;
+					width: 550rpx;
+					text-align: left;
+
+					.address {
+						font-size: 28rpx;
+						font-weight: 500;
+						color: rgba(51, 51, 51, 1);
+						line-height: 40rpx;
+						text-align: left;
+					}
+
+					.dispatch-notice {
+						font-size: 28rpx;
+
+						font-weight: 500;
+						color: rgba(51, 51, 51, 1);
+						line-height: 40rpx;
+						text-align: left;
+					}
+
+					.address-location {
+						position: absolute;
+						right: 60rpx;
+						top: 30rpx;
+
+						.location-img {
+							width: 80rpx;
+							height: 90rpx;
+						}
+
+						.location-text {
+							font-size: 18rpx;
+
+							font-weight: 500;
+							color: rgba(51, 51, 51, 1);
+						}
+					}
+
+					.tag {
+						background: rgba(233, 191, 113, 0.2);
+						border-radius: 6rpx;
+						padding: 0 16rpx;
+						line-height: 38rpx;
+						color: #a8700d;
+						font-size: 22rpx;
+						margin-right: 20rpx;
+					}
+
+					.tag1 {
+						background: rgba(53, 190, 105, 0.2);
+						border-radius: 6rpx;
+						padding: 0 16rpx;
+						line-height: 38rpx;
+						color: #1bbc50;
+						font-size: 22rpx;
+						margin-right: 20rpx;
+					}
+
+					.address-guide {
+						position: absolute;
+						right: 25rpx;
+						top: 40rpx;
+						color: #999999;
+					}
+				}
+
+				.express-content {
+					margin-bottom: 20rpx;
+
+					.box-line {
+						width: 1rpx;
+						height: 61rpx;
+						border-left: 1rpx solid rgba(238, 238, 238, 1);
+						margin: 0 40rpx;
+					}
+
+					.phone-box1 {
+
+						.name,
+						.phone {
+							font-size: 26rpx;
+
+							font-weight: 400;
+							color: rgba(102, 102, 102, 1);
+						}
+
+						.phone {
+							margin-left: 20rpx;
+						}
+					}
+
+					.time-box,
+					.phone-box {
+						text-align: left;
+						min-height: 120rpx;
+
+						.box-title {
+							font-size: 24rpx;
+
+							font-weight: 400;
+							color: #666;
+							padding-bottom: 10rpx;
+						}
+
+						.box-text {
+							font-size: 24rpx;
+
+							font-weight: 500;
+							color: #333;
+						}
+
+						.edit-phone {
+							width: 160rpx;
+							font-size: 24rpx;
+
+							font-weight: 500;
+							color: #333;
+						}
+
+						.box-icon {
+							font-size: 28rpx;
+							color: #999;
+							display: inline-block;
+							width: 40rpx;
+							text-align: center;
+							line-height: 40rpx;
+						}
+					}
+				}
+
+				.express-bottom {
+					.protocol {
+						font-size: 24rpx;
+
+						font-weight: 400;
+						color: rgba(102, 102, 102, 1);
+
+						.protocol-text {
+							color: #6487a4;
+						}
+					}
+				}
+			}
+		}
+
+		.express-type__bottom {
+			height: 90rpx;
+			padding: 0 30rpx;
+
+			.cancel-btn {
+				width: 335rpx;
+				line-height: 74rpx;
+				background: rgba(238, 238, 238, 1);
+				border-radius: 37rpx;
+				font-size: 28rpx;
+				font-weight: 400;
+				color: rgba(51, 51, 51, 1);
+			}
+
+			.save-btn {
+				width: 335rpx;
+				line-height: 74rpx;
+				background: linear-gradient(90deg, rgba(233, 180, 97, 1), rgba(238, 204, 137, 1));
+				border-radius: 37rpx;
+				font-size: 28rpx;
+				font-weight: 400;
+				color: rgba(255, 255, 255, 1);
+			}
+		}
+	}
+
+	// 选择配送给时间
+	.checkTime-box {
+		background: rgba(255, 255, 255, 1);
+		border-radius: 20rpx 20rpx 0px 0px;
+		height: 720rpx;
+		text-align: center;
+
+		.checkTime-head {
+			font-size: 32rpx;
+
+			font-weight: 500;
+			color: rgba(51, 51, 51, 1);
+			line-height: 100rpx;
+			position: relative;
+		}
+
+		.checkTime-foot {
+			height: 100rpx;
+
+			.save-btn {
+				width: 690rpx;
+				line-height: 80rpx;
+				background: linear-gradient(90deg, rgba(240, 199, 133, 1), rgba(246, 214, 157, 1));
+				border-radius: 40rpx;
+				font-size: 30rpx;
+
+				font-weight: 500;
+				color: rgba(255, 255, 255, 1);
+			}
+		}
+
+		.checkTime-content {
+			.checkTime-content__left {
+				height: 100%;
+				width: 190rpx;
+				background: #f5f5f5;
+
+				.left-item {
+					font-size: 26rpx;
+
+					font-weight: 500;
+					color: rgba(51, 51, 51, 1);
+					height: 100rpx;
+					width: 100%;
+				}
+			}
+
+			.checkTime-content__right {
+				flex: 1;
+				height: 100%;
+				overflow-y: auto;
+
+				.right-item {
+					font-size: 26rpx;
+
+					font-weight: 500;
+					color: rgba(51, 51, 51, 1);
+					width: 100%;
+					text-align: center;
+					border-bottom: 1rpx solid rgba(#dfdfdf, 0.6);
+					margin: 0 30rpx;
+					line-height: 100rpx;
+				}
+			}
+
+			.item--active {
+				font-size: 26rpx;
+
+				font-weight: 500;
+				color: rgba(168, 112, 13, 1) !important;
+				background-color: #fff;
+			}
+		}
+	}
+</style>

+ 733 - 0
pages/order/detail.vue

@@ -0,0 +1,733 @@
+<!-- 订单详情 -->
+<template>
+	<view class="order-detail-wrap">
+		<!-- 订单状态 -->
+		<view class="detail-head" :style="orderDetail.consignee ? '' : 'height:120rpx'">
+			<view class="state-box u-flex">
+				<image class="state-img" :src="$IMG_URL + '/imgs/order/order_state1.png'" mode=""></image>
+				<text>{{ orderDetail.status_desc }}</text>
+			</view>
+		</view>
+
+		<!-- 收货地址 -->
+		<view class="address-wrap" v-if="orderDetail.consignee">
+			<view class="order-address-box">
+				<view class="u-flex">
+					<text class="address-username">{{ orderDetail.consignee }}</text>
+					<text class="address-phone">{{ orderDetail.phone }}</text>
+				</view>
+				<view class="address-detail">{{ orderDetail.province_name }}{{ orderDetail.city_name }}{{ orderDetail.area_name }}{{ orderDetail.address }}</view>
+			</view>
+		</view>
+
+		<view class="detail-goods">
+			<!-- 订单信息 -->
+			<view class="order-list" v-for="order in orderDetail.item" :key="order.id">
+				<view class="order-card" @tap="jump('/pages/goods/detail', { id: order.goods_id })">
+					<shopro-mini-card :title="order.goods_title" :image="order.goods_image">
+						<template #describe>
+							<view class="order-sku u-ellipsis-1">
+								<text class="order-num">数量:{{ order.goods_num || 0 }};</text>
+								{{ order.goods_sku_text ? order.goods_sku_text : '' }}
+							</view>
+						</template>
+						<template #cardBottom>
+							<view class="card-price-box u-flex">
+								<text class="order-price font-OPPOSANS">¥{{ order.goods_price || 0 }}</text>
+								<button class="u-reset-button status-btn" v-if="order.status_name">{{ order.status_name }}</button>
+							</view>
+						</template>
+					</shopro-mini-card>
+				</view>
+				<!-- 配送方式 -->
+				<view class="express-type-box u-flex u-row-between">
+					<view class="u-flex">
+						<view class="express-type--title">配送:</view>
+						<view class="express-type--content">{{ expressType[order.dispatch_type] }}</view>
+					</view>
+					<!-- 发货详情 -->
+					<view
+						class="u-flex express-type--detail"
+						v-if="order.dispatch_type !== 'express'"
+						@tap="goDistribution(order.dispatch_type, orderDetail.id, order.id, order.dispatch_status)"
+					>
+						<text>详情</text>
+						<text class="u-iconfont uicon-arrow-right" style="color: #666;"></text>
+					</view>
+				</view>
+
+				<view class="order-bottom u-flex">
+					<!-- 退款原因 -->
+					<view class="refund_msg" v-if="order.refund_msg">
+						<text class="refund-title">退款原因:</text>
+						{{ order.refund_msg }}
+					</view>
+					<view class="btn-box" v-for="(btn, index) in order.btns" :key="btn">
+						<button @tap.stop="onConfirm(orderDetail.id, order.id)" class="u-reset-button btn1" :class="{ btn2: index + 1 === order.btns.length }" v-if="btn === 'get'">
+							确认收货
+						</button>
+						<button
+							@tap.stop="onComment(orderDetail.id, order.id)"
+							class="u-reset-button btn1"
+							:class="{ btn2: index + 1 === order.btns.length }"
+							v-if="btn === 'comment'"
+						>
+							去评价
+						</button>
+						<button
+							@tap.stop="jump('/pages/goods/detail', { id: order.goods_id })"
+							class="u-reset-button btn1"
+							:class="{ btn2: index + 1 === order.btns.length }"
+							v-if="btn === 'buy_again'"
+						>
+							再次购买
+						</button>
+						<button
+							@tap="onAftersaleDetail(order.ext_arr.aftersale_id)"
+							class="u-reset-button btn1"
+							:class="{ btn2: index + 1 === order.btns.length }"
+							v-if="btn === 'aftersale_info'"
+						>
+							售后详情
+						</button>
+						<button
+							@tap.stop="onAftersale(orderDetail.id, order.id)"
+							class="u-reset-button btn1"
+							:class="{ btn2: index + 1 === order.btns.length }"
+							v-if="btn === 'aftersale'"
+						>
+							申请售后
+						</button>
+						<button
+							@tap.stop="onAftersale(orderDetail.id, order.id)"
+							class="u-reset-button btn1"
+							:class="{ btn2: index + 1 === order.btns.length }"
+							v-if="btn === 're_aftersale'"
+						>
+							重新售后
+						</button>
+					</view>
+				</view>
+			</view>
+		</view>
+		<!-- 订单信息 -->
+		<view class="notice-box" v-if="orderDetail.id">
+			<view class="notice-box__content">
+				<view class="notice-item--center u-flex">
+					<text class="title">订单编号:</text>
+					<text class="detail">{{ orderDetail.order_sn }}</text>
+					<button class="u-reset-button copy-btn" @tap="onCopy(orderDetail.order_sn)">复制</button>
+				</view>
+				<view class="notice-item u-flex">
+					<text class="title">下单时间:</text>
+					<text class="detail">{{ orderDetail.createtime }}</text>
+				</view>
+				<view class="notice-item u-flex" v-if="orderDetail.status > 0">
+					<text class="title">支付方式:</text>
+					<text class="detail">{{ payType[orderDetail.pay_type] }}</text>
+				</view>
+				<view class="notice-item u-flex" v-if="orderDetail.status > 0">
+					<text class="title">支付时间:</text>
+					<text class="detail">{{ orderDetail.paytime }}</text>
+				</view>
+			</view>
+		</view>
+		<!--  价格信息 -->
+		<view class="order-price-box u-m-b-20" v-if="orderDetail.id">
+			<view class="notice-item u-flex u-row-between">
+				<text class="title">商品总额</text>
+				<text class="detail font-OPPOSANS">¥{{ orderDetail.goods_amount }}</text>
+			</view>
+			<view class="notice-item u-flex u-row-between">
+				<text class="title">运费</text>
+				<text class="detail font-OPPOSANS">¥{{ orderDetail.dispatch_amount }}</text>
+			</view>
+			<view class="notice-item u-flex u-row-between">
+				<text class="title">优惠金额</text>
+				<text class="detail font-OPPOSANS">-¥{{ orderDetail.discount_fee }}</text>
+			</view>
+			<view class="notice-item u-flex u-row-between" v-if="orderDetail.score_fee">
+				<text class="title">积分</text>
+				<text class="detail font-OPPOSANS">-{{ orderDetail.score_fee }}积分</text>
+			</view>
+			<view class="notice-item all-rpice-item u-flex" style="width: 100%;">
+				<text class="title">{{ orderDetail.status <= 0 ? '需付款' : '实付款' }}:</text>
+				<text class="detail all-price font-OPPOSANS">¥{{ orderDetail.total_fee }}</text>
+			</view>
+		</view>
+
+		<!-- 底部按钮 -->
+		<view class="foot-wrap safe-area-inset-bottom">
+			<view class="foot_box safe-area-inset-bottom">
+				<view class="money-box u-flex ">
+					<text class="money-title">共{{ allNum || '0' }}件商品 合计:</text>
+					<text class="all-price font-OPPOSANS">¥{{ orderDetail.total_fee || '0.00' }}</text>
+				</view>
+				<view class="btn-box u-flex">
+					<view class="" v-for="btn in orderDetail.btns" :key="btn">
+						<button v-if="btn === 'cancel'" @tap.stop="onCancel(orderDetail.id)" class="u-reset-button obtn1">取消订单</button>
+						<button v-if="btn === 'pay'" @tap.stop="onPay(orderDetail.id)" class="u-reset-button obtn2">付款</button>
+						<button v-if="btn === 'groupon'" @tap.stop="jump('/pages/activity/groupon/detail', { id: orderDetail.ext_arr.groupon_id })" class="u-reset-button obtn2">
+							拼团详情
+						</button>
+						<button v-if="btn === 'delete'" @tap.stop="onDelete(orderDetail.id)" class="u-reset-button delete-btn">删除</button>
+						<button v-if="btn === 'express'" @tap.stop="onExpress(orderDetail.id)" class="u-reset-button obtn1">查看物流</button>
+					</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	components: {},
+	data() {
+		return {
+			time: 0,
+			orderDetail: {},
+			orderStatus: {
+				'-2': '已关闭',
+				'-1': '已取消',
+				'0': '未付款',
+				'1': '已付款',
+				'2': '已完成'
+			},
+			payType: {
+				wallet: '余额支付',
+				wechat: '微信支付',
+				alipay: '支付宝支付',
+				iospay: 'ApplePay'
+			},
+			expressType: {
+				express: '物流快递',
+				selfetch: '到店/自提',
+				store: '商家配送',
+				autosend: '自动发货'
+			}
+		};
+	},
+	onShow() {
+		this.getOrderDetail();
+	},
+	onLoad() {},
+	computed: {
+		allNum() {
+			if (this.orderDetail.item) {
+				let items = this.orderDetail.item;
+				let allPrice = 0;
+				items.forEach(p => {
+					allPrice += p.goods_num;
+				});
+				return allPrice;
+			}
+		}
+	},
+	methods: {
+		jump(path, parmas) {
+			this.$Router.push({
+				path: path,
+				query: parmas
+			});
+		},
+		// 详情发货信息
+		goDistribution(dispatchType, orderId, orderItemId) {
+			this.jump('/pages/order/express/distribution-detail', { expressType: dispatchType, orderId: orderId, orderItemId: orderItemId });
+		},
+		// 订单详情
+		getOrderDetail() {
+			let that = this;
+			that.$http('order.detail', {
+				id: that.$Route.query.id
+			}).then(res => {
+				if (res.code === 1) {
+					that.orderDetail = res.data;
+					that.orderDetail.createtime = that.$u.timeFormat(res.data.createtime, 'yyyy-mm-dd hh:MM');
+					that.orderDetail.paytime = that.$u.timeFormat(res.data.paytime, 'yyyy-mm-dd hh:MM');
+				}
+			});
+		},
+		// 复制
+		onCopy(code) {
+			let that = this;
+			uni.setClipboardData({
+				data: code,
+				success: function(data) {
+					that.$u.toast('已复制到剪切板');
+				}
+			});
+		},
+
+		// 确认收货
+		onConfirm(id, itemId) {
+			let that = this;
+			that.$http(
+				'order.confirm',
+				{
+					id: id,
+					order_item_id: itemId
+				},
+				'确认中...'
+			).then(res => {
+				if (res.code === 1) {
+					that.getOrderDetail();
+				}
+			});
+		},
+
+		// 申请售后
+		onAftersale(orderId, orderItemId) {
+			this.$Router.push({
+				path: '/pages/order/after-sale/refund',
+				query: { orderId: orderId, orderItemId: orderItemId }
+			});
+		},
+
+		// 售后详情
+		onAftersaleDetail(id) {
+			this.jump('/pages/order/after-sale/detail', { aftersaleId: id });
+		},
+
+		// 取消订单
+		onCancel(id) {
+			let that = this;
+			that.$http(
+				'order.cancel',
+				{
+					id: id
+				},
+				'取消中...'
+			).then(res => {
+				if (res.code === 1) {
+					that.$Router.back();
+				}
+			});
+		},
+
+		// 立即购买
+		onPay(id) {
+			uni.navigateTo({
+				url: `/pages/order/payment/method?orderId=${id}`
+			});
+		},
+
+		// 删除订单
+		onDelete(orderId) {
+			let that = this;
+			uni.showModal({
+				title: '删除订单',
+				content: '确定要删除这个订单么?',
+				cancelText: '取消',
+				confirmText: '删除',
+				success: res => {
+					if (res.confirm) {
+						that.$http('order.deleteOrder', {
+							id: orderId
+						}).then(res => {
+							if (res.code === 1) {
+								that.$Router.back();
+							}
+						});
+					}
+				}
+			});
+		},
+
+		// 待评价
+		onComment(orderId, orderItemId) {
+			this.jump('/pages/goods/comment/add-comment', { orderId: orderId, orderItemId: orderItemId });
+		},
+
+		// 查看物流
+		onExpress(orderId, orderItemId) {
+			let that = this;
+			that.$http('order.expressList', {
+				order_id: orderId
+			}).then(res => {
+				if (res.code === 1) {
+					if (res.data.length == 1) {
+						this.jump('/pages/order/express/express-detail', { orderId: orderId, expressId: res.data[0].id });
+					} else if (res.data.length > 1) {
+						this.jump('/pages/order/express/express-list', { orderId: orderId });
+					} else {
+						this.$u.toast('暂无包裹~');
+					}
+				}
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+.detail-head {
+	background: linear-gradient(0deg, rgba(239, 196, 128, 1) 0%, rgba(248, 220, 165, 1) 100%) no-repeat;
+	background-size: 100% 180rpx;
+	height: 180rpx;
+
+	.state-box {
+		padding: 30rpx 40rpx;
+		color: rgba(#fff, 0.9);
+
+		.state-img {
+			width: 60rpx;
+			height: 60rpx;
+			// background: #ccc;
+			margin-right: 40rpx;
+		}
+	}
+}
+
+// 收货地址
+.address-wrap {
+	position: relative;
+	background-color: #fff;
+	min-height: 160rpx;
+	width: 100%;
+	margin-bottom: 20rpx;
+}
+.order-address-box {
+	width: 710rpx;
+	left: 50%;
+	transform: translateX(-50%);
+	top: -50rpx;
+	background-color: #fff;
+	min-height: 160rpx;
+	border-radius: 20rpx;
+	padding: 20rpx;
+	font-size: 30rpx;
+
+	font-weight: 500;
+	color: rgba(51, 51, 51, 1);
+	position: absolute;
+	.address-username {
+		margin-right: 20rpx;
+	}
+	.address-detail {
+		font-size: 26rpx;
+
+		font-weight: 500;
+		color: rgba(153, 153, 153, 1);
+		margin-top: 20rpx;
+	}
+}
+.detail-goods {
+	margin-bottom: 20rpx;
+	.order-list {
+		margin-bottom: 20rpx;
+		background-color: #fff;
+		padding: 0 20rpx;
+		.order-card {
+			padding: 20rpx 0;
+			.order-sku {
+				font-size: 24rpx;
+
+				font-weight: 400;
+				color: rgba(153, 153, 153, 1);
+				width: 450rpx;
+				margin-bottom: 20rpx;
+				.order-num {
+					margin-right: 10rpx;
+				}
+			}
+			.card-price-box {
+				.status-btn {
+					height: 32rpx;
+					border: 1rpx solid rgba(207, 169, 114, 1);
+					border-radius: 15rpx;
+					font-size: 20rpx;
+					font-weight: 400;
+					color: rgba(168, 112, 13, 1);
+					padding: 0 10rpx;
+					margin-left: 20rpx;
+					background: rgba(233, 183, 102, 0.16);
+				}
+				.order-price {
+					font-size: 26rpx;
+
+					font-weight: 600;
+					color: rgba(51, 51, 51, 1);
+				}
+			}
+		}
+		// 配送方式
+		.express-type-box {
+			width: 710rpx;
+			height: 90rpx;
+			background: rgba(247, 247, 247, 1);
+			border-radius: 10rpx;
+			padding: 0 20rpx;
+			.express-type--title {
+				font-size: 28rpx;
+
+				font-weight: 400;
+				color: rgba(153, 153, 153, 1);
+			}
+			.express-type--content {
+				font-size: 26rpx;
+
+				font-weight: 500;
+				color: rgba(51, 51, 51, 1);
+			}
+			.express-type--detail {
+				font-size: 24rpx;
+
+				font-weight: 400;
+				color: rgba(153, 153, 153, 1);
+				line-height: 30rpx;
+			}
+		}
+		.refund_msg {
+			font-size: 28rpx;
+			color: #999;
+			flex: 1;
+			text-align: left;
+		}
+		.order-bottom {
+			background: #fff;
+			justify-content: flex-end;
+			padding: 20rpx 0;
+			.btn1 {
+				width: 160rpx;
+				line-height: 60rpx;
+				border: 1rpx solid rgba(223, 223, 223, 1);
+				border-radius: 30rpx;
+				font-size: 26rpx;
+				background: #fff;
+				padding: 0;
+				margin-right: 20rpx;
+			}
+			.btn2 {
+				width: 160rpx;
+				line-height: 60rpx;
+				border: 1rpx solid rgba(213, 166, 90, 1) !important;
+				border-radius: 30rpx;
+				font-size: 26rpx;
+				color: #d5a65a !important;
+				padding: 0;
+				background: #fff;
+				margin-right: 20rpx;
+			}
+		}
+	}
+}
+// 拼团项目
+.group-box {
+	background: #fff;
+	padding: 40rpx 0;
+	height: 250rpx;
+	margin-bottom: 20rpx;
+	.tip-box {
+		font-size: 28rpx;
+	}
+	.title-box {
+		font-size: 26rpx;
+		font-weight: bold;
+		color: #333;
+		.group-num {
+			color: #f8002c;
+		}
+		.count-down-tip {
+			font-size: 24rpx;
+			padding-left: 10rpx;
+		}
+		.time-box {
+			font-size: 18rpx;
+			.count-text {
+				display: inline-block;
+				background-color: #383a46;
+				color: #fff;
+				font-size: 18rpx;
+				border-radius: 2rpx;
+				padding: 0 5rpx;
+				height: 28rpx;
+				text-align: center;
+				line-height: 28rpx;
+				margin: 0 6rpx;
+			}
+		}
+	}
+
+	.group-people {
+		.img-box {
+			position: relative;
+			margin-right: 20rpx;
+			.tag {
+				position: absolute;
+				line-height: 36rpx;
+				background: linear-gradient(132deg, rgba(243, 223, 177, 1), rgba(243, 223, 177, 1), rgba(236, 190, 96, 1));
+				border-radius: 18rpx;
+				padding: 0 10rpx;
+				white-space: nowrap;
+				font-size: 24rpx;
+				color: #784f06;
+				z-index: 2;
+				top: -10rpx;
+			}
+			.avatar {
+				width: 80rpx;
+				height: 80rpx;
+				border-radius: 50%;
+				background: #ccc;
+			}
+		}
+	}
+}
+
+// 收货信息、订单信息。
+.notice-box {
+	background: #fff;
+	margin-bottom: 20rpx;
+	.notice-box__head {
+		font-size: 30rpx;
+
+		font-weight: 500;
+		color: rgba(51, 51, 51, 1);
+		line-height: 80rpx;
+		border-bottom: 1rpx solid #dfdfdf;
+		padding: 0 25rpx;
+	}
+	.notice-box__content {
+		padding: 25rpx;
+		.self-pickup-box {
+			width: 100%;
+			.self-pickup--img {
+				width: 200rpx;
+				height: 200rpx;
+				margin: 40rpx 0;
+			}
+		}
+	}
+	.notice-item--center {
+		margin-bottom: 10rpx;
+	}
+	.notice-item,
+	.notice-item--center {
+		height: 50rpx;
+		align-items: flex-start;
+		.title {
+			font-size: 28rpx;
+			color: #999;
+			text-align: justify;
+			text-align-last: justify;
+			text-justify: distribute-all-lines;
+			width: 140rpx;
+		}
+
+		.detail {
+			font-size: 28rpx;
+			color: #333;
+			flex: 1;
+		}
+		.copy-btn {
+			width: 100rpx;
+			line-height: 50rpx;
+			border-radius: 25rpx;
+			padding: 0;
+			background: rgba(238, 238, 238, 1);
+			font-size: 22rpx;
+
+			font-weight: 400;
+			color: rgba(51, 51, 51, 1);
+			margin-left: 30rpx;
+		}
+	}
+	.notice-item--center {
+		align-items: center;
+	}
+}
+
+// 订单价格信息
+.order-price-box {
+	background-color: #fff;
+	padding: 20rpx;
+	margin-bottom: 20rpx;
+	.notice-item {
+		height: 50rpx;
+		.title {
+			font-size: 28rpx;
+			color: #999;
+		}
+
+		.detail {
+			font-size: 28rpx;
+			color: #333;
+		}
+	}
+	.all-rpice-item {
+		justify-content: flex-end;
+		.all-price {
+			font-size: 26rpx;
+			color: #ff0000;
+		}
+	}
+}
+
+.foot-wrap {
+	width: 750rpx;
+	height: 140rpx;
+}
+.foot_box {
+	width: 750rpx;
+	min-height: 100rpx;
+	background: rgba(255, 255, 255, 1);
+	border: 1rpx solid rgba(238, 238, 238, 1);
+	display: flex;
+	flex-direction: column;
+	align-items: flex-end;
+	justify-content: center;
+	position: fixed;
+	bottom: 0;
+	left: 0;
+	.money-box {
+		height: 60rpx;
+		padding: 0 20rpx;
+		.money-title {
+			font-size: 26rpx;
+			color: #999999;
+		}
+		.all-price {
+			color: #333;
+			font-size: 26rpx;
+		}
+	}
+	.btn-box {
+		justify-content: flex-end;
+		margin-bottom: 20rpx;
+		.delete-btn {
+			background: #ffeeee;
+			color: #e50808;
+			width: 160rpx;
+			line-height: 60rpx;
+			border-radius: 30rpx;
+			font-size: 26rpx;
+			font-weight: 400;
+			margin-right: 20rpx;
+			padding: 0;
+		}
+		.obtn1 {
+			width: 160rpx;
+			line-height: 60rpx;
+			background: rgba(238, 238, 238, 1);
+			border-radius: 30rpx;
+			font-size: 26rpx;
+			font-weight: 400;
+			color: rgba(51, 51, 51, 1);
+			margin-right: 20rpx;
+			padding: 0;
+		}
+		.obtn2 {
+			width: 160rpx;
+			line-height: 60rpx;
+			background: linear-gradient(90deg, rgba(233, 180, 97, 1), rgba(238, 204, 137, 1));
+			box-shadow: 0px 7rpx 6rpx 0px rgba(229, 138, 0, 0.22);
+			border-radius: 30rpx;
+			margin-right: 20rpx;
+			font-size: 26rpx;
+			font-weight: 400;
+			color: #fff;
+			padding: 0;
+		}
+	}
+}
+</style>

+ 411 - 0
pages/order/express/distribution-detail.vue

@@ -0,0 +1,411 @@
+<!-- 自提配送详情,自动发货详情 -->
+<template>
+	<view class="page_box">
+		<view class="content_box">
+			<view class="card-wrap" :style="expressType !== 'selfetch' ? 'border-bottom-left-radius:0;border-bottom-right-radius:0;' : ''">
+				<!-- 商品卡片 -->
+				<view class="order-card-box">
+					<shopro-mini-card :title="itemDetail.goods_title" :image="itemDetail.goods_image">
+						<template #describe>
+							<view class="order-sku u-ellipsis-1">
+								<text class="order-num">数量:{{ itemDetail.goods_num || 0 }};</text>
+								{{ itemDetail.goods_sku_text ? itemDetail.goods_sku_text : '' }}
+							</view>
+						</template>
+						<template #cardBottom>
+							<view class="card-price-box u-flex">
+								<text class="order-price font-OPPOSANS">¥{{ itemDetail.goods_price || 0 }}</text>
+								<button class="u-reset-button status-btn" v-if="itemDetail.status_name">{{ itemDetail.status_name }}</button>
+							</view>
+						</template>
+					</shopro-mini-card>
+				</view>
+
+				<!-- 到店自提 -->
+				<view class="u-flex-col u-col-center u-m-b-20 u-p-y-20" v-if="expressType == 'selfetch' && itemDetail.status_code !== 'refund_finish' && qrcodeList.length">
+					<image class="qr-code--img u-m-y-20" :src="allqrcodepath" mode=""></image>
+					<view class="all-qrcode-title u-m-b-20">总核销码</view>
+					<view class="detail-item">
+						<view class="item-title u-flex">核销码</view>
+						<view class="u-flex u-row-between u-m-y-20" v-for="code in qrcodeList" :key="code.code">
+							<view>
+								<text :style="code.status_code !== 'nouse' ? 'color:#999' : ''" class="item-content">{{ code.code }}</text>
+								<text :style="code.status_code !== 'nouse' ? 'color:#999' : ''" class="item-status u-m-x-30">{{ code.status_name }}</text>
+							</view>
+							<button class="u-reset-button check-code" v-if="code.status_code == 'nouse'" @tap="checkCode(code.code)">查看</button>
+						</view>
+
+						<view class="item-tip">为保障您的权益,未到店消费前请不要将提货码提供给商家</view>
+					</view>
+				</view>
+			</view>
+
+			<!-- 自提 -->
+			<view class="detail-item u-p-20 " v-if="expressType == 'selfetch'">
+				<view class="item-title">{{ storeInfo.name }}</view>
+				<view class="u-flex u-row-between">
+					<view class="u-m-r-20" style="flex: 3;">
+						<view class="item-content">
+							{{ storeInfo.province_name || '' }}{{ storeInfo.city_name || '' }}{{ storeInfo.area_name || '' }}{{ storeInfo.address || '' }}
+						</view>
+						<view class="item-content">营业时间:{{ storeInfo.openhours || '' }}</view>
+					</view>
+					<view class="u-flex-col u-col-center location-box" style="flex: 1;" @tap="openStoreMap">
+						<text class="u-iconfont uicon-map-fill" style="color: #4fbbff;font-size: 34rpx;"></text>
+						<text class="location-text u-m-t-10">到这去</text>
+					</view>
+				</view>
+			</view>
+
+			<!-- 配送 -->
+			<view
+				class="detail-item u-p-20"
+				:style="expressType !== 'selfetch' ? 'border-top-left-radius:0;border-top-right-radius:0;' : ''"
+				v-if="expressType == 'store' && itemDetail.order"
+			>
+				<view class="item-title">配送信息</view>
+				<view class="item-content">配送地址:{{ itemDetail.order.city_name || '' }}{{ itemDetail.order.area_name || '' }}{{ itemDetail.order.address || '' }}</view>
+				<view class="item-content">配送时间:{{ itemDetail.ext_arr.dispatch_date || '' }}</view>
+			</view>
+
+			<!-- 自动 -->
+			<view class="detail-item u-p-20" :style="expressType !== 'selfetch' ? 'border-top-left-radius:0;border-top-right-radius:0;' : ''" v-if="expressType == 'autosend'">
+				<view class="item-title">发货信息</view>
+				<view v-if="itemDetail.ext_arr && itemDetail.ext_arr.autosend_type == 'params'" v-for="item in autosendList" :key="item.value" class="item-content">
+					{{ item.name }}:{{ item.value }}
+				</view>
+				<view v-if="itemDetail.ext_arr && itemDetail.ext_arr.autosend_type == 'text'" class="item-content">{{ itemDetail.ext_arr.autosend_content }}</view>
+				<view v-if="!(itemDetail.ext_arr && itemDetail.ext_arr.autosend_type)" style="color: #999;" class="item-content">暂未发货~</view>
+			</view>
+		</view>
+		<view class="foot_box u-flex u-row-center u-col-center u-p-b-20" v-if="expressType == 'selfetch' || expressType == 'store'">
+			<button class="service-btn u-reset-button u-flex u-col-center u-row-center" @tap="onService">
+				<text class="u-iconfont uicon-phone-fill u-m-r-10" style="color: #fff;font-size: 40rpx;"></text>
+				联系商家
+			</button>
+		</view>
+		<!-- 二维码弹窗 -->
+		<u-popup v-model="showQrcode" border-radius="20" mode="center">
+			<view class="qr-code-modal u-flex-col u-row-center u-col-center">
+				<image class="qr-code-img" :src="qrcodepath" mode=""></image>
+				<view class="qr-code-text">核销码:{{ qrcode }}</view>
+				<button class="u-reset-button hide-qrcode" @tap="showQrcode = false">关闭</button>
+			</view>
+		</u-popup>
+
+		<!-- 二维码绘制 -->
+		<view class="hideCanvasView">
+			<canvas class="hideCanvas" canvas-id="qrcode_img" :style="{ width: (poster.width || 1) + 'px', height: (poster.height || 1) + 'px' }"></canvas>
+		</view>
+	</view>
+</template>
+
+<script>
+import _app from '@/shopro/poster/QS-SharePoster/app.js';
+import { getSharePoster } from '@/shopro/poster/QS-SharePoster/QS-SharePoster.js';
+export default {
+	components: {},
+	data() {
+		return {
+			poster: {},
+			canvasId: 'qrcode_img',
+			qrcode: '', //核销总码
+			qrcodeList: [], //核销码列表
+			storeInfo: {}, //自提点信息
+			qrcodepath: '', //单核销码
+			allqrcodepath: '', //总核销码
+			autosendList: [], //自动发货列表。
+			itemDetail: {}, //订单商品详情
+			expressType: 'express',
+			barTitle: {
+				express: '物流快递',
+				selfetch: '到店/自提',
+				store: '商家配送',
+				autosend: '自动发货'
+			},
+			showQrcode: false //二维码弹窗
+		};
+	},
+	computed: {},
+	onLoad() {
+		this.expressType = this.$Route.query.expressType;
+		uni.setNavigationBarTitle({
+			title: this.barTitle[this.expressType]
+		});
+		this.getItemGoodsDetail();
+	},
+	onPullDownRefresh() {
+		this.getItemGoodsDetail();
+	},
+	methods: {
+		jump(path, parmas) {
+			this.$Router.push({
+				path: path,
+				query: parmas
+			});
+		},
+		// 自提打开地图
+		openStoreMap() {
+			uni.openLocation({
+				latitude: +this.storeInfo.latitude,
+				longitude: +this.storeInfo.longitude,
+				success: function() {
+					console.log('success');
+				},
+				fail: err => {
+					console.log(err);
+				}
+			});
+		},
+		// 跳转客服
+		onService() {
+			this.$tools.callPhone(this.itemDetail.store.phone);
+		},
+		// 查看核销码
+		checkCode(code) {
+			this.qrcode = code;
+			this.shareFc('qrcode').then(res => {
+				this.showQrcode = true;
+			});
+		},
+		// 订单详情
+		getItemGoodsDetail() {
+			let that = this;
+			that.$http('order.itemDetail', {
+				id: that.$Route.query.orderId,
+				order_item_id: that.$Route.query.orderItemId,
+				type: 'dispatch'
+			}).then(res => {
+				uni.stopPullDownRefresh();
+				if (res.code === 1) {
+					that.itemDetail = res.data;
+					that.qrcodeList = res.data.verify;
+					that.storeInfo = res.data.store;
+					if (res.data.ext_arr.autosend_content && res.data.ext_arr.autosend_type == 'params') {
+						that.autosendList = JSON.parse(res.data.ext_arr.autosend_content);
+					}
+					if (that.expressType == 'selfetch') {
+						let _arr = [];
+						that.qrcodeList.forEach(code => {
+							_arr.push(code.code);
+						});
+						that.qrcode = _arr.join(',');
+						if (that.qrcodeList.length) {
+							that.shareFc();
+						}
+					}
+				}
+			});
+		},
+		// 绘制二维码
+		async shareFc(type) {
+			let that = this;
+			try {
+				console.log('准备生成:' + new Date());
+				const d = await getSharePoster({
+					_this: this, //若在组件中使用 必传
+					posterCanvasId: this.canvasId, //canvasId
+					delayTimeScale: 20, //延时系数
+					drawDelayTime: 500, //draw延时时间
+					background: {
+						width: 100,
+						height: 100,
+						backgroundColor: '#fff'
+					},
+					drawArray: ({ bgObj, type, bgScale }) => {
+						return new Promise((rs, rj) => {
+							rs([
+								{
+									type: 'qrcode',
+									text: this.qrcode,
+									size: bgObj.width,
+									dx: 0,
+									dy: 0,
+									correctLevel: 1
+								}
+							]);
+						});
+					},
+					setCanvasWH: ({ bgObj, type, bgScale }) => {
+						// 为动态设置画布宽高的方法,
+						this.poster = bgObj;
+					}
+				});
+				if (type == 'qrcode') {
+					this.qrcodepath = d.poster.tempFilePath;
+				} else {
+					this.allqrcodepath = d.poster.tempFilePath;
+				}
+			} catch (e) {
+				_app.hideLoading();
+				_app.showToast(JSON.stringify(e));
+			}
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+// 二维码
+.hideCanvasView {
+	position: relative;
+	.hideCanvas {
+		position: fixed;
+		top: -99999upx;
+		left: -99999upx;
+		z-index: -99999;
+	}
+}
+// 二维码弹窗
+.qr-code-modal {
+	width: 610rpx;
+	border-radius: 20rpx;
+	background-color: #fff;
+	margin: 0 auto;
+	.qr-code-img {
+		margin: 100rpx;
+		width: 400rpx;
+		height: 400rpx;
+	}
+	.qr-code-text {
+		color: #999;
+		font-size: 28rpx;
+	}
+	.hide-qrcode {
+		width: 492rpx;
+		line-height: 70rpx;
+		background: linear-gradient(90deg, rgba(233, 180, 97, 1), rgba(238, 204, 137, 1));
+		box-shadow: 0px 7rpx 6rpx 0px rgba(229, 138, 0, 0.22);
+		border-radius: 35rpx;
+		font-size: 28rpx;
+		font-weight: 500;
+		color: rgba(255, 255, 255, 1);
+		margin: 30rpx;
+	}
+}
+
+.content_box {
+	padding: 20rpx;
+}
+.no-radius {
+	border-radius: 0 !important;
+}
+.card-wrap {
+	background-color: #fff;
+	padding: 20rpx 20rpx 0;
+	border-radius: 20rpx;
+}
+.order-card-box {
+	height: 200rpx;
+	border-bottom: 2rpx solid rgba(238, 238, 238, 0.6);
+	.order-sku {
+		font-size: 24rpx;
+
+		font-weight: 400;
+		color: rgba(153, 153, 153, 1);
+		width: 450rpx;
+		margin-bottom: 20rpx;
+		.order-num {
+			margin-right: 10rpx;
+		}
+	}
+	.card-price-box {
+		.status-btn {
+			height: 32rpx;
+			border: 1rpx solid rgba(207, 169, 114, 1);
+			border-radius: 15rpx;
+			font-size: 20rpx;
+			font-weight: 400;
+			color: rgba(168, 112, 13, 1);
+			padding: 0 10rpx;
+			margin-left: 20rpx;
+			background: rgba(233, 183, 102, 0.16);
+		}
+		.order-price {
+			font-size: 26rpx;
+
+			font-weight: 600;
+			color: rgba(51, 51, 51, 1);
+		}
+	}
+}
+.qr-code--img {
+	width: 200rpx;
+	height: 200rpx;
+}
+.detail-item {
+	background: rgba(255, 255, 255, 1);
+	border-radius: 20rpx;
+	width: 100%;
+	.item-title {
+		font-size: 28rpx;
+
+		font-weight: bold;
+		color: rgba(51, 51, 51, 1);
+		height: 60rpx;
+	}
+	.item-content {
+		font-size: 26rpx;
+
+		font-weight: 400;
+		color: rgba(51, 51, 51, 1);
+		line-height: 50rpx;
+	}
+	.item-status {
+		font-size: 26rpx;
+
+		font-weight: 400;
+		color: rgba(179, 132, 54, 1);
+	}
+	.item-tip {
+		font-size: 24rpx;
+
+		font-weight: 400;
+		color: rgba(196, 196, 196, 1);
+	}
+	.check-code {
+		background: none;
+		border-radius: 25rpx;
+		border: 1rpx solid rgba(179, 132, 54, 1);
+		padding: 0;
+		width: 100rpx;
+		line-height: 50rpx;
+		font-size: 28rpx;
+		color: rgba(179, 132, 54, 1);
+	}
+	.location-box {
+		height: 100%;
+		justify-content: center;
+		position: relative;
+		&::before {
+			content: '';
+			width: 2rpx;
+			height: 94rpx;
+			background: #eeeeee;
+			position: absolute;
+			top: 50%;
+			transform: translateY(-50%);
+			left: 0;
+		}
+		.location-icon {
+			color: #4fbbff;
+			margin-bottom: 20rpx;
+		}
+		.location-text {
+			color: #999;
+			font-size: 26rpx;
+		}
+	}
+}
+.service-btn {
+	padding: 0;
+	width: 710rpx;
+	line-height: 80rpx;
+	background: linear-gradient(90deg, rgba(233, 180, 97, 1), rgba(238, 204, 137, 1));
+	box-shadow: 0px 7rpx 6rpx 0px rgba(229, 138, 0, 0.22);
+	border-radius: 40rpx;
+	font-size: 30rpx;
+	font-weight: 500;
+	color: rgba(255, 255, 255, 1);
+}
+</style>

+ 217 - 0
pages/order/express/express-detail.vue

@@ -0,0 +1,217 @@
+<!-- 物流信息 -->
+<template>
+	<view class="map-box">
+		<!-- 商品卡片 -->
+		<view class="oilStation-top">
+			<view class="u-flex u-row-between express-card__head">
+				<view class="u-flex">
+					<view class="img-box">
+						<image class="goods-image" :src="firstGoods.goods_image" mode="aspectFill"></image>
+						<view class="shop-tag">{{ goodsList.length || 0 }}件商品</view>
+					</view>
+					<text class="express-status">{{ firstGoods.status_name || '' }}</text>
+				</view>
+			</view>
+			<view class="express-sn u-flex u-col-center ">
+				<text class="u-m-r-30">{{ expressDetail.express_name || '' }}
+					{{ expressDetail.express_no || '' }}</text>
+				<text class="u-iconfont uicon-file-text" style="color: #666;font-size: 32rpx;"
+					@tap="copyCode(expressDetail.express_no)"></text>
+			</view>
+		</view>
+
+		<!-- 物流时间轴 -->
+		<view class="oilStation-bottom">
+			<view class="u-p-y-30">
+				<view v-if="exrpessLog.length" class="express-item u-flex u-col-center"
+					v-for="(log, index) in exrpessLog" :key="log.id">
+					<view class="item-left">
+						<view class="day">{{ log.changedate.split(' ')[0]}}</view>
+						<view class="time">{{ log.changedate.split(' ')[1] }}</view>
+					</view>
+					<view class="item-right">
+						<image class="express-tag"
+							:src="$IMG_URL + `/imgs/order/express${index == 0 ? log.status + '' + log.status : log.status}.png`"
+							mode="aspectFill"></image>
+						<view class="express-title">{{ log.status_name }}</view>
+						<view class="express-detail">{{ log.content }}</view>
+					</view>
+				</view>
+				<view v-if="!exrpessLog.length" class="no-log u-flex u-row-center u-col-center">暂无物流信息~</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		data() {
+			return {
+				expressDetail: {}, //物流信息
+				exrpessLog: [], //包裹轨迹
+				firstGoods: {}, //商品列表
+				goodsList: []
+			};
+		},
+		computed: {},
+		onLoad() {
+			this.getExpressDetail();
+		},
+		methods: {
+			// 复制物流码
+			copyCode(code) {
+				let that = this;
+				uni.setClipboardData({
+					data: code,
+					success: () => {
+						that.$u.toast('已复制到剪切板');
+					}
+				});
+			},
+			// 获取物流信息
+			getExpressDetail() {
+				let that = this;
+				that.$http('order.expressDetail', {
+					id: that.$Route.query.expressId,
+					order_id: that.$Route.query.orderId
+				}).then(res => {
+					if (res.code === 1) {
+						that.expressDetail = res.data;
+						that.goodsList = res.data.item;
+						that.exrpessLog = res.data.log;
+						that.firstGoods = res.data.item[0];
+					}
+				});
+			}
+		}
+	};
+</script>
+
+<style lang="scss">
+	/* 物流卡片 */
+	.oilStation-top {
+		justify-content: center;
+		align-items: center;
+		flex-direction: column;
+		background: #fff;
+		margin: 20rpx;
+		background: rgba(255, 255, 255, 1);
+		box-shadow: 0 1rpx 18rpx 0 rgba(184, 184, 184, 0.55);
+		border-radius: 20rpx;
+
+		.express-card__head {
+			padding: 20rpx;
+		}
+
+		.img-box {
+			position: relative;
+			width: 129rpx;
+			height: 129rpx;
+			border-radius: 20rpx;
+			overflow: hidden;
+			background: #ccc;
+
+			.goods-image {
+				width: 129rpx;
+				height: 129rpx;
+			}
+		}
+
+		.shop-tag {
+			position: absolute;
+			width: 129rpx;
+			height: 30rpx;
+			background: rgba(0, 0, 0, 0.6);
+			font-size: 18rpx;
+
+			font-weight: 400;
+			color: rgba(255, 255, 255, 1);
+			text-align: center;
+			bottom: 0;
+			z-index: 5;
+		}
+
+		.express-status {
+			font-size: 30rpx;
+
+			font-weight: 500;
+			color: rgba(51, 51, 51, 1);
+			margin-left: 20rpx;
+		}
+
+		.express-sn {
+			width: 100%;
+			height: 65rpx;
+			background: rgba(250, 251, 255, 1);
+			border-radius: 0 0 20rpx 20rpx;
+			font-size: 24rpx;
+
+			font-weight: 500;
+			color: rgba(102, 102, 102, 1);
+			padding: 0 30rpx;
+		}
+	}
+
+	.oilStation-bottom {
+		justify-content: space-between;
+		align-items: center;
+		flex-direction: row;
+		background: #fff;
+		width: 710rpx;
+		margin: 0 20rpx 20rpx;
+		border-radius: 20rpx;
+		box-shadow: 0 1rpx 18rpx 0 rgba(184, 184, 184, 0.55);
+		padding: 0 30rpx;
+	}
+
+	// 物流步骤条
+	.no-log {
+		width: 100%;
+		height: 100rpx;
+		color: #999;
+	}
+
+	.express-item {
+		align-items: flex-start;
+		.item-left {
+			.day {
+				font-size: 26rpx;
+				white-space: nowrap;
+			}
+			.time {
+				font-size: 20rpx;
+				white-space: nowrap;
+			}
+		}
+
+		.item-right {
+			position: relative;
+			padding-left: 54rpx;
+			margin-left: 54rpx;
+			border-left: 1rpx solid #e7e7e7;
+			padding-bottom: 40rpx;
+
+			.express-tag {
+				width: 54rpx;
+				height: 54rpx;
+				position: absolute;
+				left: -27rpx;
+				top: 0;
+			}
+
+			.express-title {
+				font-size: 28rpx;
+				font-weight: 500;
+				color: rgba(51, 51, 51, 1);
+				margin-bottom: 10rpx;
+			}
+
+			.express-detail {
+				width: 400rpx;
+				font-size: 24rpx;
+				font-weight: 500;
+				color: rgba(51, 51, 51, 1);
+			}
+		}
+	}
+</style>

+ 114 - 0
pages/order/express/express-list.vue

@@ -0,0 +1,114 @@
+<!-- 物流分包 -->
+<template>
+	<view class="express-list-wrap u-p-b-30">
+		<view class="tip-box u-flex u-col-center u-p-x-20">{{ expressList.length }}个包裹已发出</view>
+		<view class="express-list u-m-b-20" v-for="express in expressList" :key="express.id" @tap="toExpressDetail(express.id)">
+			<view class="u-flex u-row-between list-head u-p-x-20">
+				<text class="list-status" v-if="express.log.length">{{ express.log[0].status_name }}</text>
+				<view class="express-name">{{ express.express_name }}:{{ express.express_no }}</view>
+			</view>
+			<view class="list-content u-p-20">
+				<view class="express-detail u-m-b-20" v-if="express.log.length">{{ express.log[0].content }}</view>
+				<view class="goods-box u-flex u-col-center">
+					<view class="goods-img-box" v-for="img in express.item" :key="img.id"><image class="goods-img" :src="img.goods_image" mode="aspectFill"></image></view>
+				</view>
+				<view class="all-goods">共{{ express.item.length }}件商品</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	components: {},
+	data() {
+		return {
+			expressList: []
+		};
+	},
+	computed: {},
+	onLoad() {
+		this.getExpressList();
+	},
+	methods: {
+		jump(path, parmas) {
+			this.$Router.push({
+				path: path,
+				query: parmas
+			});
+		},
+		// 物流详情
+		toExpressDetail(id) {
+			this.jump('/pages/order/express/express-detail', { orderId: this.$Route.query.orderId, expressId: id });
+		},
+		// 包裹列表
+		getExpressList() {
+			let that = this;
+			that.$http('order.expressList', {
+				order_id: that.$Route.query.orderId
+			}).then(res => {
+				if (res.code === 1) {
+					that.expressList = res.data;
+				}
+			});
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+.tip-box {
+	width: 750rpx;
+	height: 84rpx;
+	font-size: 30rpx;
+
+	font-weight: 500;
+	color: rgba(168, 112, 13, 1);
+	background: rgba(255, 226, 182, 1);
+}
+// 物流包裹
+.express-list {
+	background-color: #fff;
+	.list-head {
+		height: 76rpx;
+		border-bottom: 1rpx solid rgba(223, 223, 223, 0.5);
+		.list-status {
+			font-size: 26rpx;
+
+			font-weight: 500;
+			color: rgba(168, 112, 13, 1);
+		}
+		.express-name {
+			font-size: 26rpx;
+
+			font-weight: 400;
+			color: rgba(153, 153, 153, 1);
+		}
+	}
+	.list-content {
+		.express-detail {
+			font-size: 28rpx;
+
+			font-weight: 500;
+			color: rgba(51, 51, 51, 1);
+		}
+		.goods-box {
+			flex-wrap: wrap;
+			.goods-img-box {
+				width: 160rpx;
+				height: 160rpx;
+				overflow: hidden;
+				margin-right: 22rpx;
+				margin-bottom: 22rpx;
+				&:nth-child(4n) {
+					margin-right: 0;
+				}
+				.goods-img {
+					width: 100%;
+					height: 100%;
+				}
+			}
+		}
+	}
+}
+</style>

+ 351 - 0
pages/order/express/store-address.vue

@@ -0,0 +1,351 @@
+<!-- 自提点 -->
+<template>
+	<view class="map-box">
+		<!-- 标题栏 -->
+		<shopro-navbar>
+			<view class="search-wrap u-flex" slot="content">
+				<u-search
+					class="u-m-r-20"
+					placeholder="输入自提点名称"
+					@change="onSearch"
+					@search="onSearch"
+					@clear="clearSearch"
+					v-model="searchVal"
+					:showAction="false"
+					height="60"
+				></u-search>
+			</view>
+		</shopro-navbar>
+
+		<map id="map" :style="{ width: '750rpx', height: mapHeight + 'px' }" :latitude="latitude" :longitude="longitude" show-location @tap="onMap"></map>
+		<view class="dragLayer safe-area-inset-bottom" :class="moveCard">
+			<view class="oilStation">
+				<!-- 上下箭头 -->
+				<view class="oilStation-top">
+					<view class="touch-guide u-flex u-row-center u-col-center" @tap="onShowCard">
+						<image class="touch-jintou" :src="$IMG_URL + (showCard ? '/imgs/order/arrows2.png' : '/imgs/order/arrows1.png')" mode=""></image>
+					</view>
+				</view>
+
+				<!-- 自提点列表 -->
+				<scroll-view class="oilStation-bottom" enable-back-to-top scroll-y @scrolltolower="loadMore">
+					<view class=" list-content">
+						<u-radio-group style="width: 100%;" v-model="storeId" active-color="#e9b561">
+							<view style="width: 100%;" @tap="selStoreAddress(address.id, index)" v-for="(address, index) in addressList" :key="address.id">
+								<view class="address-item u-flex u-col-center u-row-between">
+									<view class="address-left">
+										<!-- 门店 -->
+										<view class="address-name">{{ address.name }}</view>
+										<!-- 时间 -->
+										<view class="time-box ">
+											<view class=" u-flex u-col-center">
+												<text class="u-iconfont uicon-clock u-m-r-20" style="color: #999;"></text>
+												{{ address.openhours }}
+											</view>
+											<view class="u-flex u-col-center u-p-l-30 u-m-t-20">
+												<text v-for="week in address.openweeks_arr" :key="week">{{ weekMap[week] }}、</text>
+											</view>
+										</view>
+										<!-- 地址 -->
+										<view class="address-detail u-flex u-col-center">
+											<text class="u-iconfont uicon-map u-m-r-20" style="color: #999;"></text>
+											{{ address.province_name }}{{ address.city_name }}{{ address.area_name }}{{ address.address }}
+										</view>
+										<!-- 电话 -->
+										<view class="address-phone u-flex u-col-center">
+											<text class="u-iconfont uicon-phone u-m-r-20" style="color: #999;"></text>
+											{{ address.phone }}
+										</view>
+									</view>
+									<view class="address-right u-flex-col u-row-center u-col-center">
+										<u-radio :name="address.id"></u-radio>
+										<text class="address-distance u-m-t-10">{{ address.distance_text || 0 }}</text>
+									</view>
+								</view>
+							</view>
+						</u-radio-group>
+
+						<!-- 更多 -->
+						<u-loadmore v-if="addressList.length" height="80rpx" :status="loadStatus" icon-type="flower" color="#ccc" />
+					</view>
+				</scroll-view>
+				<view class="foot_box u-flex u-row-center u-col-center"><button class="u-reset-button save-btn" @tap="onSave">确认</button></view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	data() {
+		return {
+			longitude: 0,
+			latitude: 0,
+			mapHeight: 0,
+			showCard: false,
+			moveCard: 'dragLayer-bottom',
+			distance: 0,
+			storeId: 0, //默认门店ID
+			addressList: [], //门店列表
+			storeInfo: [], //当前门店信息
+			weekMap: {
+				1: '周一',
+				2: '周二',
+				3: '周三',
+				4: '周四',
+				5: '周五',
+				6: '周六',
+				7: '周日'
+			},
+			loadStatus: 'loadmore',
+			currentPage: 1,
+			lastPage: 1,
+			searchVal: ''
+		};
+	},
+	computed: {},
+	onLoad(options) {
+		this.latitude = this.$Route.query.lat;
+		this.longitude = this.$Route.query.lng;
+		this.storeId = this.$Route.query.storeId;
+		this.getStoreAddress();
+	},
+	onReady() {
+		this.editHeight();
+	},
+	methods: {
+		// 选中门店
+		onSave() {
+			if (this.storeId) {
+				uni.$emit('SELECT_STORE', { storeInfo: JSON.stringify(this.storeInfo) });
+				this.$Router.back();
+			} else {
+				this.$u.toast('请选择门店');
+			}
+		},
+		// 搜索
+		onSearch() {
+			this.addressList = [];
+			this.$u.debounce(this.getStoreAddress());
+		},
+
+		// 清除搜索框
+		clearSearch() {
+			this.searchVal = '';
+			this.onSearch();
+		},
+		// 设置地图,卡片高度。
+		editHeight() {
+			let that = this;
+			uni.getSystemInfo({
+				success: res => {
+					let view = uni
+						.createSelectorQuery()
+						.in(this)
+						.select('.dragLayer');
+					view.fields(
+						{
+							size: true,
+							scrollOffset: true
+						},
+						data => {
+							that.mapHeight = res.windowHeight - data.height;
+							//#ifdef APP-PLUS
+							that.mapHeight = res.windowHeight - data.height - 40;
+							//#endif
+						}
+					).exec();
+				}
+			});
+		},
+		// 点击地图
+		onMap() {
+			this.showCard = false;
+			this.moveCard = 'dragLayer-bottom';
+		},
+		// 选择门店
+		selStoreAddress(id, index) {
+			this.storeId = id;
+			this.latitude = this.addressList[index].latitude;
+			this.longitude = this.addressList[index].longitude;
+			this.storeInfo = this.addressList[index];
+		},
+		loadMore() {
+			if (this.currentPage < this.lastPage) {
+				this.currentPage += 1;
+				this.getStoreAddress();
+			}
+		},
+		// 获取商品支持的自提点。
+		getStoreAddress() {
+			let that = this;
+			that.loadStatus = 'loading';
+			that.$http('goods.storeAddress', {
+				id: that.$Route.query.goodsId,
+				latitude: that.$Route.query.lat,
+				longitude: that.$Route.query.lng,
+				keyword: that.searchVal,
+				page: that.currentPage,
+				per_page: 3
+			}).then(res => {
+				if (res.code == 1) {
+					that.addressList = [...that.addressList, ...res.data.data];
+					that.lastPage = res.data.last_page;
+					that.loadStatus = that.currentPage < res.data.last_page ? 'loadmore' : 'nomore';
+					if (this.currentPage === 1) {
+						this.storeId = res.data.data[0].id;
+						this.storeInfo = res.data.data[0];
+					}
+				}
+			});
+		},
+
+		// 点击显隐
+		onShowCard() {
+			this.showCard = !this.showCard;
+			this.moveCard = this.showCard ? 'dragLayer-top' : 'dragLayer-bottom';
+		}
+	}
+};
+</script>
+
+<style lang="scss">
+.search-wrap {
+	/* #ifdef MP-WEIXIN */
+	width: 460rpx;
+	/* #endif */
+	/* #ifndef MP-WEIXIN */
+	width: 100%;
+	/* #endif */
+}
+/* 浮层 */
+.dragLayer {
+	width: 750rpx;
+	position: fixed;
+	z-index: 996;
+	background-color: #fff;
+	border-top-left-radius: 20rpx;
+	border-top-right-radius: 20rpx;
+	bottom: 0;
+	box-shadow: 0px 1rpx 18rpx 0px rgba(83, 83, 83, 0.35);
+	display: flex;
+	height: 100%;
+}
+
+// 点击上下
+.dragLayer-bottom {
+	height: 450rpx !important;
+	/*#ifdef APP-PLUS */
+	height: 800rpx !important;
+	/*#endif*/
+	transition: all ease-in-out 0.2s;
+}
+.dragLayer-top {
+	height: 800rpx !important;
+	transition: all ease-in-out 0.2s;
+}
+.oilStation {
+	width: 690rpx;
+	margin: 0 30rpx;
+	display: flex;
+	flex-direction: column;
+	flex: 1;
+	height: 100%;
+}
+.oilStation-top {
+	justify-content: center;
+	align-items: center;
+	flex-direction: column;
+}
+.touch-guide {
+	justify-content: center;
+	align-items: center;
+	height: 100rpx;
+}
+.touch-jintou {
+	width: 60rpx;
+	height: 8rpx;
+}
+// 确认门店
+.foot_box {
+	height: 100rpx;
+	background-color: #fff;
+	.save-btn {
+		color: #fff;
+		width: 690rpx;
+		line-height: 80rpx;
+		background: linear-gradient(90deg, rgba(233, 181, 97, 1), rgba(238, 204, 138, 1));
+		box-shadow: 1rpx 7rpx 6rpx 0px rgba(229, 138, 0, 0.22);
+		border-radius: 40rpx;
+		font-size: 28rpx;
+	}
+}
+
+// 自提点列表
+.oilStation-bottom {
+	justify-content: space-between;
+	align-items: center;
+	flex-direction: row;
+	flex: 1;
+	height: 100%;
+	overflow: hidden;
+}
+.list-content {
+	background-color: #fff;
+	overflow-x: hidden;
+}
+.address-item {
+	border-bottom-width: 1px;
+	border-bottom-style: solid;
+	border-bottom-color: #eef1f4;
+	padding: 15rpx 0 15rpx;
+	width: 100%;
+	justify-content: space-between;
+	align-items: center;
+	flex-direction: row;
+}
+.address-name {
+	font-size: 30rpx;
+
+	font-weight: 600;
+	color: rgba(51, 51, 51, 1);
+	margin-bottom: 30rpx;
+}
+.time-box,
+.address-detail,
+.address-phone {
+	flex-direction: row;
+	align-items: center;
+	font-size: 24rpx;
+
+	font-weight: 400;
+	color: #666;
+	margin-bottom: 20rpx;
+}
+.address-icon {
+	color: #bdbdbd;
+	margin-right: 10rpx;
+	font-size: 30rpx;
+}
+.address-left {
+	flex: 1;
+}
+.address-right {
+	align-items: center;
+	color: #a8700e;
+	font-size: 24rpx;
+	margin-bottom: 20rpx;
+	white-space: nowrap;
+}
+.more {
+	width: 750rpx;
+	height: 90rpx;
+	justify-content: center;
+	align-items: center;
+}
+
+.more-text {
+	font-size: 24rpx;
+	color: #ccc;
+}
+</style>

+ 407 - 0
pages/order/list.vue

@@ -0,0 +1,407 @@
+<template>
+	<view class="page_box">
+		<view class="head_box">
+			<!-- tab -->
+			<view class="order-nav u-flex">
+				<view class="nav-item u-flex-col u-flex-1 u-col-center" v-for="nav in orderState" :key="nav.id"
+					@tap="onNav(nav.type)">
+					<view class="item-title">{{ nav.title }}</view>
+					<text class="nav-line" :class="{ 'line-active': orderType === nav.type }"></text>
+				</view>
+			</view>
+		</view>
+
+		<view class="content_box">
+			<scroll-view scroll-y="true" enable-back-to-top @scrolltolower="loadMore" class="scroll-box">
+				<!-- 订单列表 -->
+				<view class="order-list" v-for="(order, orderIndex) in orderList" :key="order.id"
+					@tap.stop="jump('/pages/order/detail', { id: order.id })">
+					<view class="order-head u-flex u-row-between">
+						<text class="no">订单编号:{{ order.order_sn }}</text>
+						<text class="state">{{ order.status_name }}</text>
+					</view>
+					<view class="goods-order" v-for="goods in order.item" :key="goods.id">
+						<view class="order-content">
+							<shopro-mini-card :title="goods.goods_title" :image="goods.goods_image">
+								<template #describe>
+									<view class="order-sku u-ellipsis-1">
+										<text class="order-num">数量:{{ goods.goods_num || 0 }};</text>
+										{{ goods.goods_sku_text ? goods.goods_sku_text : '' }}
+									</view>
+								</template>
+								<template #cardBottom>
+									<view class="order-price-box u-flex ">
+										<text class="order-price font-OPPOSANS">¥{{ goods.goods_price || 0 }}</text>
+										<button class="u-reset-button status-btn"
+											v-if="goods.status_name">{{ goods.status_name }}</button>
+									</view>
+								</template>
+							</shopro-mini-card>
+						</view>
+					</view>
+					<view class="order-bottom">
+						<view class="all-msg u-flex font-OPPOSANS">
+							优惠:
+							<text class="all-unit">¥</text>
+							{{ order.discount_fee }} ,运费:
+							<text class="all-unit">¥</text>
+							{{ order.dispatch_amount }} ,{{ order.status <= 0 ? '需付款' : '实付款' }}:
+							<view class="all-money font-OPPOSANS">{{ order.total_fee }}</view>
+						</view>
+
+						<!-- 按钮 -->
+						<view class="btn-box u-flex" v-if="order.btns.length">
+							<block v-for="orderBtn in order.btns" :key="orderBtn">
+								<button v-if="orderBtn === 'cancel'" @tap.stop="onCancel(order.id, orderIndex)"
+									class="u-reset-button obtn1">取消订单</button>
+								<button v-if="orderBtn === 'pay'" @tap.stop="onPay(order.id)"
+									class="u-reset-button obtn2">立即支付</button>
+								<button v-if="orderBtn === 'groupon'"
+									@tap.stop="jump('/pages/activity/groupon/detail', { id: order.ext_arr.groupon_id })"
+									class="u-reset-button obtn2">
+									拼团详情
+								</button>
+								<button v-if="orderBtn === 'delete'" @tap.stop="onDelete(order.id, orderIndex)"
+									class="u-reset-button obtn3">删除</button>
+								<button v-if="orderBtn === 'express'" @tap.stop="onExpress(order.id, orderIndex)"
+									class="u-reset-button obtn1">查看物流</button>
+							</block>
+						</view>
+					</view>
+				</view>
+
+				<!-- 空白页 -->
+				<shopro-empty v-if="isEmpty" :image="$IMG_URL + '/imgs/empty/empty_groupon.png'"
+					tipText="暂无商品,还有更多好货等着你噢~"></shopro-empty>
+				<!-- 更多 -->
+				<u-loadmore v-show="orderList.length" height="80rpx" :status="loadStatus" icon-type="flower"
+					color="#ccc" />
+			</scroll-view>
+		</view>
+	</view>
+</template>
+
+<script>
+	export default {
+		components: {},
+		data() {
+			return {
+				isEmpty: false,
+				loadStatus: 'loadmore', //loadmore-加载前的状态,loading-加载中的状态,nomore-没有更多的状态
+				currentPage: 1,
+				lastPage: 1,
+				orderType: 'all',
+				orderList: [],
+				orderState: [{
+						id: 0,
+						title: '全部',
+						type: 'all'
+					},
+					{
+						id: 1,
+						title: '待付款',
+						type: 'nopay'
+					},
+					{
+						id: 2,
+						title: '待发货',
+						type: 'nosend'
+					},
+					{
+						id: 3,
+						title: '待收货',
+						type: 'noget'
+					},
+					{
+						id: 4,
+						title: '待评价',
+						type: 'nocomment'
+					}
+				]
+			};
+		},
+		onShow() {
+			if (this.$Route.query.type) {
+				this.orderType = this.$Route.query.type;
+			}
+			this.orderList = [];
+			this.currentPage = 1;
+			this.lastPage = 1;
+			this.getOrderList();
+		},
+		methods: {
+			jump(path, parmas) {
+				this.$Router.push({
+					path: path,
+					query: parmas
+				});
+			},
+
+			// tab切换
+			onNav(id) {
+				if (this.orderType !== id) {
+					this.orderType = id;
+					this.orderList = [];
+					this.currentPage = 1;
+					this.lastPage = 1;
+					this.getOrderList();
+				}
+			},
+
+			// 订单列表
+			getOrderList() {
+				let that = this;
+				that.loadStatus = 'loading';
+				that.$http('order.index', {
+					type: that.orderType,
+					page: that.currentPage
+				}, '加载中...').then(res => {
+					if (res.code === 1) {
+						that.orderList = [...that.orderList, ...res.data.data];
+						that.isEmpty = !that.orderList.length;
+						that.lastPage = res.data.last_page;
+						that.loadStatus = that.currentPage < res.data.last_page ? 'loadmore' : 'nomore';
+					}
+				});
+			},
+
+			// 加载更多
+			loadMore() {
+				if (this.currentPage < this.lastPage) {
+					this.currentPage += 1;
+					this.getOrderList();
+				}
+			},
+
+			// 删除订单
+			onDelete(orderId, orderIndex) {
+				let that = this;
+				uni.showModal({
+					title: '删除订单',
+					content: '确定要删除这个订单么?',
+					cancelText: '取消',
+					confirmText: '删除',
+					success: res => {
+						if (res.confirm) {
+							that.$http('order.deleteOrder', {
+									id: orderId
+								},
+								'删除中...'
+							).then(res => {
+								if (res.code === 1) {
+									that.$u.toast(res.msg);
+									that.orderList.splice(orderIndex, 1);
+								}
+							});
+						}
+					}
+				});
+			},
+
+			// 取消订单
+			onCancel(id, orderIndex) {
+				let that = this;
+				that.$http('order.cancel', {
+						id: id
+					},
+					'取消中...'
+				).then(res => {
+					if (res.code === 1) {
+						that.$u.toast(res.msg);
+						this.orderList.splice(orderIndex, 1);
+					}
+				});
+			},
+
+			// 立即购买
+			onPay(id) {
+				uni.navigateTo({
+					url: `/pages/order/payment/method?orderId=${id}&orderType=goods`
+				});
+			},
+
+			// 查看物流
+			onExpress(orderId) {
+				let that = this;
+				that.$http('order.expressList', {
+					order_id: orderId
+				}).then(res => {
+					if (res.code === 1) {
+						if (res.data.length == 1) {
+							this.jump('/pages/order/express/express-detail', { orderId: orderId, expressId: res
+									.data[0].id });
+						} else if (res.data.length > 1) {
+							this.jump('/pages/order/express/express-list', { orderId: orderId });
+						} else {
+							that.$u.toast('暂无包裹~');
+						}
+					}
+				});
+			}
+		}
+	};
+</script>
+
+<style lang="scss">
+	.order-nav {
+		background: #fff;
+		height: 80rpx;
+
+		.nav-item {
+			flex: 1;
+
+			.item-title {
+				font-size: 30rpx;
+
+				font-weight: 400;
+				color: rgba(51, 51, 51, 1);
+				line-height: 76rpx;
+			}
+
+			.nav-line {
+				width: 100rpx;
+				height: 4rpx;
+				background: #fff;
+			}
+
+			.line-active {
+				background: rgba(230, 184, 115, 1);
+			}
+		}
+	}
+
+	.order-list {
+		background: #fff;
+		margin: 20rpx 0;
+
+		.order-bottom {
+			padding-bottom: 20rpx;
+
+			.btn-box {
+				justify-content: flex-end;
+			}
+
+			.all-msg {
+				font-size: 24rpx;
+				color: #999;
+				justify-content: flex-end;
+				margin-bottom: 10rpx;
+				padding: 0 30rpx;
+
+				.all-unit {
+					font-size: 20rpx;
+				}
+
+				.all-money {
+					font-size: 26rpx;
+					color: #333;
+					font-weight: 500;
+
+					&::before {
+						content: '¥';
+						font-size: 20rpx;
+					}
+				}
+			}
+
+			.obtn1 {
+				width: 160rpx;
+				line-height: 60rpx;
+				background: rgba(238, 238, 238, 1);
+				border-radius: 30rpx;
+				font-size: 26rpx;
+
+				font-weight: 400;
+				color: rgba(51, 51, 51, 1);
+				margin-right: 20rpx;
+				padding: 0;
+			}
+
+			.obtn2 {
+				width: 160rpx;
+				line-height: 60rpx;
+				background: linear-gradient(90deg, rgba(233, 180, 97, 1), rgba(238, 204, 137, 1));
+				box-shadow: 0px 7rpx 6rpx 0px rgba(229, 138, 0, 0.22);
+				border-radius: 30rpx;
+				margin-right: 20rpx;
+				font-size: 26rpx;
+
+				font-weight: 400;
+				color: #fff;
+				padding: 0;
+			}
+
+			.obtn3 {
+				background: #ffeeee;
+				color: #e50808;
+				width: 160rpx;
+				line-height: 60rpx;
+				border-radius: 30rpx;
+				margin-right: 20rpx;
+				font-size: 26rpx;
+				font-weight: 400;
+				padding: 0;
+			}
+		}
+
+		.order-head {
+			padding: 0 25rpx;
+			height: 77rpx;
+			border-bottom: 1rpx solid #dfdfdf;
+
+			.no {
+				font-size: 26rpx;
+				color: #999;
+			}
+
+			.state {
+				font-size: 26rpx;
+				color: #a8700d;
+			}
+		}
+
+		.goods-order {
+			border-bottom: 1px solid rgba(223, 223, 223, 0.5);
+			padding: 20rpx 20rpx 0;
+			margin-bottom: 20rpx;
+
+			.order-content {
+				padding-bottom: 20rpx;
+
+				.order-sku {
+					font-size: 24rpx;
+
+					font-weight: 400;
+					color: rgba(153, 153, 153, 1);
+					width: 450rpx;
+					margin-bottom: 20rpx;
+
+					.order-num {
+						margin-right: 10rpx;
+					}
+				}
+
+				.order-price-box {
+					.status-btn {
+						height: 32rpx;
+						border: 1rpx solid rgba(207, 169, 114, 1);
+						border-radius: 15rpx;
+						font-size: 20rpx;
+						font-weight: 400;
+						color: rgba(168, 112, 13, 1);
+						padding: 0 10rpx;
+						margin-left: 20rpx;
+						background: rgba(233, 183, 102, 0.16);
+					}
+
+					.order-price {
+						font-size: 26rpx;
+
+						font-weight: 600;
+						color: rgba(51, 51, 51, 1);
+					}
+				}
+			}
+		}
+	}
+</style>

+ 255 - 0
pages/order/payment/method.vue

@@ -0,0 +1,255 @@
+<!-- 收银台 -->
+<template>
+	<view class="pay-method-wrap">
+		<view class="u-flex-col u-col-center money-box" v-if="orderDetail.total_fee">
+			<text class="time" v-show="isCountDown">{{ timeText }}</text>
+			<view class="money">{{ orderDetail.total_fee }}</view>
+		</view>
+
+		<!-- 支付方式单选项 -->
+		<u-radio-group v-if="paymentList && paymentList.length" class="pay-box" v-model="payType"
+			active-color="#f0c785">
+			<!-- 微信支付 -->
+			<view class="u-flex u-row-between pay-item" v-show="paymentList.includes('wechat')"
+				@tap="payType = 'wechat'">
+				<view class="u-flex">
+					<image class="pay-img" :src="$IMG_URL + '/imgs/order/order_wx_pay.png'" mode=""></image>
+					<text>微信支付</text>
+				</view>
+				<u-radio shape="circle" name="wechat"></u-radio>
+			</view>
+			<!-- 支付宝支付 -->
+			<view class="u-flex u-row-between pay-item" v-show="paymentList.includes('alipay')"
+				@tap="payType = 'alipay'">
+				<view class="u-flex">
+					<image class="pay-img" :src="$IMG_URL + '/imgs/order/order_ali_pay.png'" mode=""></image>
+					<text>支付宝支付</text>
+				</view>
+				<u-radio shape="circle" name="alipay"></u-radio>
+			</view>
+			<!-- 苹果支付 -->
+			<view class="u-flex u-row-between pay-item" v-show="paymentList.includes('iospay') && appPlatfrom === 'ios'"
+				@tap="payType = 'iospay'">
+				<view class="u-flex">
+					<image class="pay-img" :src="$IMG_URL + '/imgs/order/order_apple_pay.png'" mode=""></image>
+					<text>ApplePay</text>
+				</view>
+				<u-radio shape="circle" name="iospay"></u-radio>
+			</view>
+			<!-- 余额支付 -->
+			<view class="u-flex u-row-between pay-item" v-show="paymentList.includes('wallet')"
+				@tap="payType = 'wallet'">
+				<view class="u-flex">
+					<image class="pay-img" :src="$IMG_URL + '/imgs/order/order_wallet_pay.png'" mode=""></image>
+					<text>余额支付</text>
+				</view>
+				<u-radio shape="circle" name="wallet"></u-radio>
+			</view>
+		</u-radio-group>
+
+		<button class="u-reset-button pay-btn" @tap="confirmPay">确认支付 ¥{{ orderDetail.total_fee || '0.00' }}</button>
+	</view>
+</template>
+
+<script>
+	/**
+	 * 接收商品订单orderType:goods,充值订单orderType:recharge
+	 */
+	import Pay from '@/shopro/pay';
+	import { mapMutations, mapActions, mapState, mapGetters } from 'vuex';
+	let timer;
+	export default {
+		components: {},
+		data() {
+			return {
+				payType: '', //支付方式
+				isCountDown: true, //是否显示订单倒计时。
+				orderDetail: {},
+				timeText: '', //倒计时文本
+				platform: this.$platform.get(),
+				appPlatfrom: '',
+				orderType: ''
+			};
+		},
+		computed: {
+			...mapGetters(['initPayment', 'initRecharge']),
+			paymentList() {
+				let list = []
+				switch (this.orderType) {
+					case 'goods':
+						list = this.initPayment
+						break;
+					case 'recharge':
+						list = this.initPayment?.length && this.initPayment.filter(item => {
+							if (this.initRecharge.methods.includes(item)) return item
+						})
+						break;
+					default:
+						list = this.initPayment
+						break;
+				}
+				this.payType = list?. [0];
+				return list
+			},
+		},
+		onShow() {
+			this.orderDetail.id && this.countDown();
+		},
+		onLoad() {
+			this.orderType = this.$Route.query.orderType
+			if (this.$platform.get() === 'wxOfficialAccount' && this.$platform.device() === 'ios' && this.$platform
+				.entry() !== location.href) location.reload();
+			// 获取订单详情
+			this.getOrderDetail()
+		},
+		onHide() {
+			clearInterval(timer);
+			timer = null;
+		},
+		methods: {
+			// 获取订单详情
+			getOrderDetail() {
+				switch (this.$Route.query.orderType) {
+					case 'goods':
+						this.getGoodsOrderDetail()
+						break;
+					case 'recharge':
+						this.getRechargeOrderDetail()
+						break;
+					default:
+						this.getGoodsOrderDetail()
+						break;
+				}
+			},
+			// 倒计时
+			countDown() {
+				let that = this;
+				let t = parseInt(that.orderDetail.ext_arr.expired_time * 1000) - parseInt(new Date().getTime());
+				t = t / 1000;
+				timer = setInterval(() => {
+					if (t > 0) {
+						let time = that.$tools.format(t);
+						that.timeText = `支付剩余时间 ${time.m}:${time.s}`;
+						t--;
+					} else {
+						clearInterval(timer);
+						that.timeText = '订单已过期!';
+					}
+				}, 1000);
+			},
+
+			// 发起支付
+			confirmPay() {
+				let that = this;
+				let pay = null;
+				if (!that.payType) {
+					this.$u.toast('请选择支付方式');
+					return;
+				}
+				if (that.payType === 'wallet') {
+					uni.showModal({
+						title: '支付提示',
+						confirmColor: '#f0c785',
+						content: `是否确认使用余额支付:${that.orderDetail.total_fee || '0.00'}元?`,
+						success: res => {
+							if (res.confirm) {
+								pay = new Pay(that.payType, that.orderDetail, that.$Route.query.orderType);
+							}
+						}
+					});
+				} else {
+					pay = new Pay(that.payType, that.orderDetail, that.$Route.query.orderType);
+				}
+			},
+
+			// 支付商品订单信息
+			getGoodsOrderDetail() {
+				let that = this;
+				that.$http('order.detail', {
+					id: that.$Route.query.orderId
+				}).then(res => {
+					if (res.code === 1) {
+						that.orderDetail = res.data;
+						if (res.data.ext_arr !== null) {
+							that.countDown();
+						} else {
+							that.isCountDown = false;
+						}
+					}
+				});
+			},
+
+			// 充值订单信息
+			getRechargeOrderDetail() {
+				let that = this;
+				that.$http('money.rechargeDetail', {
+					id: that.$Route.query.orderId
+				}).then(res => {
+					if (res.code === 1) {
+						that.orderDetail = res.data;
+						if (res.data.ext_arr !== null) {
+							that.countDown();
+						} else {
+							that.isCountDown = false;
+						}
+					}
+				});
+			}
+		}
+	};
+</script>
+
+<style lang="scss">
+	.money-box {
+		background: #fff;
+		height: 250rpx;
+		margin-bottom: 20rpx;
+		padding-top: 30rpx;
+
+		.time {
+			font-size: 28rpx;
+			color: #c4c4c4;
+		}
+
+		.money {
+			color: #e1212b;
+			font-size: 60rpx;
+			margin-top: 60rpx;
+
+			&::before {
+				content: '¥';
+				font-size: 46rpx;
+			}
+		}
+	}
+
+	.pay-box {
+		.pay-item {
+			height: 90rpx;
+			padding: 0 30rpx;
+			font-size: 26rpx;
+			background: #fff;
+			width: 750rpx;
+			border-bottom: 1rpx solid #eeeeee;
+
+			&:last-child {
+				border-bottom: none;
+			}
+
+			.pay-img {
+				width: 40rpx;
+				height: 40rpx;
+				margin-right: 25rpx;
+			}
+		}
+	}
+
+	.pay-btn {
+		width: 690rpx;
+		line-height: 80rpx;
+		background: linear-gradient(90deg, rgba(240, 199, 133, 1), rgba(246, 214, 157, 1));
+		border-radius: 40rpx;
+		color: rgba(#fff, 0.9);
+		margin: 100rpx auto 0;
+	}
+</style>

+ 287 - 0
pages/order/payment/result.vue

@@ -0,0 +1,287 @@
+<!-- 支付结果页 -->
+<template>
+	<view class="success-page">
+
+		<!-- 商品结果页 -->
+		<template v-if="orderType==='goods'">
+			<view class="success-box u-flex-col u-row-center u-col-center">
+				<image class="pay-img" :src="payImg[payState]" mode=""></image>
+				<text class="notice">{{ payText[payState] }}</text>
+				<text class="pay-money"
+					v-if="payState === 'success' && orderDetail.total_fee">¥{{ orderDetail.total_fee }}</text>
+				<view class="btn-box u-flex u-row-between">
+					<block
+						v-if="payState === 'success' && orderDetail.activity_type === 'groupon' && orderDetail.ext_arr.buy_type === 'groupon'">
+						<button class="u-reset-button base-btn" v-if="orderDetail.ext_arr.groupon_id > 0"
+							@tap="$Router.replace({ path: '/pages/activity/groupon/detail', query: { id: orderDetail.ext_arr.groupon_id } })">
+							拼团详情
+						</button>
+						<button class="u-reset-button base-btn" v-else
+							@tap="$Router.push('/pages/activity/groupon/my-groupon')">我的拼团</button>
+					</block>
+
+					<button class="u-reset-button base-btn" v-else
+						@tap="$Router.pushTab('/pages/index/index')">返回首页</button>
+
+
+					<button class="u-reset-button base-btn u-m-l-10 u-m-r-10"
+						@tap="$Router.replace({ path: '/pages/order/detail', query: { id: orderDetail.id } })">查看订单</button>
+					<button class="u-reset-button again-pay " v-show="payState === 'fail'" @tap="onPay">重新支付</button>
+				</view>
+			</view>
+			<!-- modal -->
+			<!-- #ifdef MP-WEIXIN -->
+			<u-modal v-if="payState === 'success'" ref="uModal" v-model="showModal" :show-cancel-button="true"
+				confirm-color="#e9b461" cancel-color="#666666" @confirm="onConfirm" confirm-text="订阅消息" cancel-text="取消"
+				content="是否订阅消息,追踪订单信息?" title="提示"></u-modal>
+			<!-- #endif -->
+		</template>
+
+		<template v-if="orderType==='recharge'">
+			<view class="success-box u-flex-col u-row-center u-col-center">
+				<image class="pay-img" :src="payImg[payState]" mode=""></image>
+				<text class="notice">{{ payText[payState] }}</text>
+				<text class="pay-money"
+					v-if="payState === 'success' && orderDetail.total_fee">¥{{ orderDetail.total_fee }}</text>
+				<view class="btn-box u-flex u-row-between">
+
+					<button class="u-reset-button base-btn" @tap="$Router.pushTab('/pages/index/index')">返回首页</button>
+					<button v-if="payState === 'fail'" class="u-reset-button again-pay" @tap="onPay">重新支付</button>
+					<button v-else class="u-reset-button base-btn"
+						@tap="$Router.pushTab('/pages/index/user')">个人中心</button>
+
+				</view>
+			</view>
+		</template>
+
+	</view>
+</template>
+
+<script>
+	import Pay from '@/shopro/pay';
+	import { mapMutations, mapActions, mapState, mapGetters } from 'vuex';
+	let payTimer = null;
+	const payCount = 5;
+	export default {
+		components: {},
+		data() {
+			return {
+				showModal: false,
+				messageType: '',
+				templateIds: [],
+				wxOpenTags: '',
+				orderDetail: {}, //订单详情
+				orderType: '', //订单类型
+				payText: {
+					fail: '支付失败',
+					success: '支付成功',
+					paying: '查询中...'
+				},
+				payImg: {
+					fail: this.$IMG_URL + '/imgs/order/order_pay_fail.gif',
+					success: this.$IMG_URL + '/imgs/order/order_pay_success.gif',
+					paying: this.$IMG_URL + '/imgs/order/order_paying.gif'
+				},
+				payState: '' //支付状态
+			};
+		},
+		computed: {
+			...mapGetters(['subscribeMessageIdsMap'])
+		},
+		onLoad() {
+			this.payState = this.$Route.query.payState ? this.$Route.query.payState : 'paying';
+			this.orderType = this.$Route.query.orderType
+			this.$Route.query.orderId && this.getOrderDetail();
+			switch (this.payState) {
+				case 'success':
+					this.getCartList();
+					break;
+				case 'fail':
+					break;
+				case 'paying':
+					this.checkTimer();
+					break;
+				default:
+					break;
+			}
+		},
+		onHide() {
+			clearInterval(payTimer);
+			payTimer = null;
+		},
+		methods: {
+			...mapActions(['getCartList']),
+
+			// 订阅消息
+			onConfirm() {
+				//  #ifdef MP-WEIXIN
+				this.templateIds.length && this.$store.commit('subscribeMessage', this.messageType);
+				//  #endif
+			},
+
+			// 获取订单详情
+			getOrderDetail() {
+				switch (this.$Route.query.orderType) {
+					case 'goods':
+						this.getGoodsOrderDetail()
+						break;
+					case 'recharge':
+						this.getRechargeOrderDetail()
+						break;
+					default:
+						break;
+				}
+			},
+
+			// 支付订单信息
+			getGoodsOrderDetail() {
+				let that = this;
+				that.$http('order.detail', {
+					id: that.$Route.query.orderId
+				}).then(async res => {
+					if (res.code === 1) {
+						that.orderDetail = res.data;
+						if (res.data.status > 0) {
+							this.messageType = this.orderDetail.activity_type == 'groupon' ? 'grouponResult' :
+								'result';
+							//  #ifdef MP-WEIXIN
+							this.templateIds = this.subscribeMessageIdsMap[this.messageType];
+							this.showModal = this.templateIds.length;
+							//  #endif
+						}
+					}
+				});
+			},
+
+			// 充值订单信息
+			getRechargeOrderDetail() {
+				let that = this;
+				that.$http('money.rechargeDetail', {
+					id: that.$Route.query.orderId
+				}).then(res => {
+					if (res.code === 1) {
+						that.orderDetail = res.data;
+						if (res.data.status > 0) {
+							this.messageType = 'result';
+						}
+					}
+				});
+			},
+
+			// 支付倒计时
+			checkTimer() {
+				let that = this;
+				let count = 0;
+				that.payState = 'paying';
+				payTimer = setInterval(() => {
+					count++;
+					if (count < payCount) {
+						that.checkPay();
+					} else {
+						clearInterval(payTimer);
+						that.payState = 'fail';
+					}
+				}, 800);
+			},
+
+			// 检测支付
+			async checkPay() {
+				let that = this;
+				let paiMap = {
+					'goods': 'order.detail',
+					'recharge': 'money.rechargeDetail'
+				}
+				let res = await that.$http(
+					paiMap[that.orderType], {
+						id: that.$Route.query.orderId
+					},
+					false
+				);
+				if (res.code === 1 && res.data.status > 0) {
+					that.payState = 'success';
+					clearInterval(payTimer);
+				}
+			},
+
+			// 重新支付
+			onPay() {
+				let that = this;
+				let pay = new Pay(that.$Route.query.type, that.orderDetail, that.orderType);
+			}
+		}
+	};
+</script>
+
+<style lang="scss" scopeds>
+	.success-box {
+		background: #fff;
+		width: 750rpx;
+		padding: 40rpx 0;
+
+		.pay-img {
+			width: 130rpx;
+			height: 130rpx;
+		}
+
+		.notice {
+			font-size: 30rpx;
+
+			font-weight: bold;
+			color: rgba(51, 51, 51, 1);
+			line-height: 30rpx;
+			margin-top: 30rpx;
+		}
+
+		.pay-money {
+			font-size: 36rpx;
+			color: #ff3000;
+			font-weight: 600;
+			margin-top: 20rpx;
+		}
+
+		.btn-box {
+			margin-top: 30rpx;
+			width: 660rpx;
+
+			.base-btn {
+				width: 320rpx;
+				line-height: 70rpx;
+				background: rgba(255, 255, 255, 1);
+				border: 1rpx solid rgba(223, 223, 223, 0.5);
+				border-radius: 35rpx;
+				font-size: 28rpx;
+
+				font-weight: 400;
+				color: rgba(153, 153, 153, 1);
+				padding: 0;
+			}
+
+			.again-pay {
+				width: 320rpx;
+				line-height: 70rpx;
+				background: linear-gradient(90deg, rgba(233, 180, 97, 1), rgba(238, 204, 137, 1));
+				box-shadow: 0px 7rpx 6rpx 0px rgba(229, 138, 0, 0.22);
+				border-radius: 35rpx;
+				font-size: 28rpx;
+
+				font-weight: 400;
+				color: rgba(255, 255, 255, 0.8);
+				padding: 0;
+			}
+		}
+	}
+
+	.hot-box {
+		background: #fff;
+		padding: 20rpx;
+		margin-top: 20rpx;
+
+		.hot-title {
+			font-size: 30rpx;
+
+			font-weight: 500;
+			color: rgba(51, 51, 51, 1);
+			height: 80rpx;
+		}
+	}
+</style>

Some files were not shown because too many files changed in this diff