推拿小程序前端代码仓库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

408 lines
8.8 KiB

3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
3 weeks ago
3 months ago
  1. <!-- 会员中心页面 -->
  2. <template>
  3. <view class="page">
  4. <view class="bg"></view>
  5. <view class="content">
  6. <!-- 导航栏 -->
  7. <navbar title="会员中心" leftClick @leftClick="$utils.navigateBack" bgColor="transparent" color="#fff" />
  8. <view class="flex user">
  9. <image class="user-avatar" :src="userInfo.headImage" mode="aspectFill"></image>
  10. <view class="user-info">
  11. <view class="flex user-name">
  12. <text>{{ userInfo.nickName }}</text>
  13. <image v-if="role" class="icon icon-role" :src="vipInfo.massageVipCombo.imageVip" mode="widthFix"></image>
  14. </view>
  15. <view class="user-desc">
  16. <template v-if="role">
  17. <text>{{ `将于${$dayjs(vipInfo.validTime).format('YYYY-MM-DD')}到期` }}</text>
  18. </template>
  19. <template v-else>
  20. <text>暂未开通会员</text>
  21. </template>
  22. </view>
  23. </view>
  24. </view>
  25. <view class="consumption" v-if="!role">
  26. <!-- todo: 对接接口 -->
  27. <progress :current="1000" :total="3800"></progress>
  28. <text class="consumption-desc">{{ `累积消费达到${3800}元或直接充值即可成为会员` }}</text>
  29. </view>
  30. <view class="charge">
  31. <view class="charge-header">
  32. <view class="flex charge-header-title">充值会员</view>
  33. <view class="charge-header-mask"></view>
  34. </view>
  35. <view class="charge-content">
  36. <view class="flex charge-selection">
  37. <view class="flex charge-option" v-for="item in chargeOptions" :key="item.id"
  38. :class="[selectedChargeId === item.id ? 'is-active' : '']" @click="onSelectCharge(item.id)">
  39. <view class="charge-option-value">
  40. <text class="charge-option-unit">¥</text>
  41. <text>{{ item.price }}</text>
  42. </view>
  43. </view>
  44. </view>
  45. <view class="charge-rights">
  46. <view class="flex charge-rights-header">
  47. <image class="title" src="../static/memberCenter/title-rights.png" mode="widthFix"></image>
  48. </view>
  49. <view class="charge-rights-content">
  50. <uv-parse :content="configList.config_rights"></uv-parse>
  51. <image class="icon" src="../static/memberCenter/icon-rights.png" mode="widthFix"></image>
  52. </view>
  53. </view>
  54. </view>
  55. </view>
  56. </view>
  57. <view class="flex bar">
  58. <view class="flex count">
  59. <text>合计</text>
  60. <view class="price">
  61. <text class="price-unit">¥</text>
  62. <!-- todo: check -->
  63. <text>{{ totalPrice }}</text>
  64. </view>
  65. </view>
  66. <view class="btn btn-pay" @click="submit">
  67. 开通会员
  68. </view>
  69. </view>
  70. </view>
  71. </template>
  72. <script>
  73. import progress from '../components/progress.vue'
  74. import {
  75. mapGetters,
  76. mapState
  77. } from 'vuex'
  78. export default {
  79. name: "MemberCenter",
  80. components: {
  81. progress,
  82. },
  83. data() {
  84. return {
  85. chargeOptions: [],
  86. selectedChargeId: '001',
  87. }
  88. },
  89. onShow() {
  90. this.fetchCargeOptions()
  91. },
  92. computed: {
  93. ...mapGetters(['role']),
  94. ...mapState(['userInfo', 'vipInfo']),
  95. selectedChargeObj() {
  96. return this.chargeOptions.find(item => item.id === this.selectedChargeId)
  97. },
  98. totalPrice() {
  99. return this.selectedChargeObj?.price || 0
  100. }
  101. },
  102. methods: {
  103. async fetchCargeOptions() {
  104. try {
  105. this.chargeOptions = (await this.$fetch('queryComboList'))?.records || []
  106. this.selectedChargeId = this.chargeOptions?.[0]?.id
  107. } catch (err) {
  108. }
  109. },
  110. onSelectCharge(id) {
  111. this.selectedChargeId = id
  112. },
  113. async submit() {
  114. // todo: check jump to create order ?
  115. try {
  116. let params = {
  117. comoId: this.selectedChargeId
  118. }
  119. let result = await this.$fetch('addVip', params)
  120. await uni.requestPaymentWxPay({result})
  121. uni.showToast({
  122. title: '充值成功',
  123. icon : 'none'
  124. })
  125. setTimeout(uni.navigateBack, 800, -1)
  126. } catch (err) {
  127. }
  128. return
  129. // this.$api('addVip', {
  130. // comoId: this.selectedChargeId
  131. // }, res => {
  132. // if (res.code == 200) {
  133. // uni.requestPayment({
  134. // provider: 'wxpay', // 服务提提供商
  135. // timeStamp: res.result.timeStamp, // 时间戳
  136. // nonceStr: res.result.nonceStr, // 随机字符串
  137. // package: res.result.packageValue,
  138. // signType: res.result.signType, // 签名算法
  139. // paySign: res.result.paySign, // 签名
  140. // success: function(res) {
  141. // console.log('支付成功', res);
  142. // },
  143. // fail: function(err) {
  144. // console.log('支付失败', err);
  145. // uni.showToast({
  146. // icon: 'none',
  147. // title: "支付失败"
  148. // })
  149. // }
  150. // });
  151. // }
  152. // })
  153. }
  154. }
  155. }
  156. </script>
  157. <style scoped lang="scss">
  158. $bar-height: 132rpx;
  159. .bg {
  160. width: 100vw;
  161. height: 550rpx;
  162. background-image: linear-gradient(#84A73F, #D8FF8F);
  163. }
  164. .page {
  165. padding-bottom: calc(#{$bar-height} + env(safe-area-inset-bottom));
  166. background-color: $uni-fg-color;
  167. min-height: 100vh;
  168. position: relative;
  169. }
  170. .content {
  171. position: absolute;
  172. top: 0;
  173. left: 0;
  174. width: 100vw;
  175. }
  176. .user {
  177. align-items: flex-start;
  178. color: $uni-text-color-inverse;
  179. font-size: 22rpx;
  180. margin: 34rpx 18rpx 0 18rpx;
  181. &-avatar {
  182. width: 147rpx;
  183. height: 147rpx;
  184. margin-right: 8rpx;
  185. border-radius: 50%;
  186. }
  187. &-info {
  188. flex: 1;
  189. }
  190. &-name {
  191. font-size: 32rpx;
  192. margin-top: 16rpx;
  193. justify-content: flex-start;
  194. .icon {
  195. height: auto;
  196. &-role {
  197. width: 138rpx;
  198. }
  199. }
  200. }
  201. &-desc {
  202. margin-top: 26rpx;
  203. }
  204. }
  205. .consumption {
  206. margin: 0 28rpx;
  207. &-desc {
  208. color: $uni-text-color-inverse;
  209. font-size: 22rpx;
  210. margin-top: 1rpx;
  211. }
  212. }
  213. .charge {
  214. background-color: $uni-fg-color;
  215. border-top-left-radius: 18rpx;
  216. border-top-right-radius: 18rpx;
  217. margin-top: 45rpx;
  218. &-header {
  219. $header-height: 90rpx;
  220. height: $header-height;
  221. position: relative;
  222. &-title {
  223. width: calc(50% + 25rpx);
  224. height: 100%;
  225. color: $uni-text-color-inverse;
  226. font-size: 28rpx;
  227. background-image: linear-gradient(#84A73F, #D8FF8F);
  228. box-sizing: border-box;
  229. border-top-left-radius: 18rpx;
  230. border-bottom-right-radius: 90rpx;
  231. }
  232. &-mask {
  233. $marsk-width: 50rpx;
  234. position: absolute;
  235. top: 0;
  236. right: 50%;
  237. transform: translateX(25rpx);
  238. // height: 100%;
  239. // width: 70rpx;
  240. width: 0;
  241. height: 0;
  242. border-top: calc(#{$header-height}/2) solid transparent;
  243. border-left: calc(#{$marsk-width}/2) solid transparent;
  244. border-bottom: calc(#{$header-height}/2) solid $uni-fg-color;
  245. border-right: calc(#{$marsk-width}/2) solid $uni-fg-color;
  246. }
  247. }
  248. &-content {
  249. padding: 57rpx 14rpx;
  250. }
  251. &-selection {
  252. justify-content: space-between;
  253. font-size: 0;
  254. flex-wrap: wrap;
  255. }
  256. &-option {
  257. width: 230rpx;
  258. height: 248rpx;
  259. box-sizing: border-box;
  260. background-color: #F5F5F5;
  261. border: 3rpx solid #C7C7C7;
  262. border-radius: 16rpx;
  263. box-shadow: 0rpx 3rpx 6rpx 0rpx #eef3e3;
  264. color: #999999;
  265. margin-bottom: 20rpx;
  266. &-value {
  267. font-size: 61rpx;
  268. }
  269. &-unit {
  270. font-size: 34rpx;
  271. margin-right: 6rpx;
  272. }
  273. &.is-active {
  274. background-color: rgba($color: #E9FFC3, $alpha: 0.74);
  275. border-color: $uni-color-light;
  276. color: #F64041;
  277. }
  278. }
  279. &-rights {
  280. margin-top: 47rpx;
  281. &-header {
  282. .title {
  283. width: 202rpx;
  284. height: auto;
  285. }
  286. }
  287. &-content {
  288. margin-top: 31rpx;
  289. color: $uni-color-light;
  290. font-size: 28rpx;
  291. line-height: 66rpx;
  292. background-color: rgba($color: #E2FFAE, $alpha: 0.71);
  293. border-radius: 16rpx;
  294. padding: 29rpx 30rpx;
  295. width: 100%;
  296. min-height: 500rpx;
  297. box-sizing: border-box;
  298. position: relative;
  299. .icon {
  300. position: absolute;
  301. top: 23rpx;
  302. right: 11rpx;
  303. width: 131rpx;
  304. height: auto;
  305. }
  306. }
  307. }
  308. }
  309. // 下单
  310. .bar {
  311. position: fixed;
  312. z-index: 1000;
  313. bottom: 0;
  314. left: 0;
  315. width: 100vw;
  316. height: $bar-height;
  317. padding-bottom: env(safe-area-inset-bottom);
  318. background-color: $uni-fg-color;
  319. .count {
  320. flex: 1;
  321. color: #000000;
  322. font-size: 28rpx;
  323. margin-left: 48rpx;
  324. justify-content: flex-start;
  325. .price {
  326. color: #FF2A2A;
  327. font-size: 30rpx;
  328. &-unit {
  329. font-size: 18rpx;
  330. }
  331. }
  332. }
  333. .btn {
  334. border: none;
  335. line-height: 1;
  336. background-color: transparent;
  337. padding: 0;
  338. width: auto;
  339. height: auto;
  340. margin: 0;
  341. &-pay {
  342. margin-right: 41rpx;
  343. padding: 24rpx 137rpx;
  344. color: $uni-text-color-inverse;
  345. font-size: 28rpx;
  346. border-radius: 44rpx;
  347. background-image: linear-gradient(to right, #84A73F, #D8FF8F);
  348. }
  349. }
  350. }
  351. </style>