推拿小程序前端代码仓库
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.

400 lines
8.5 KiB

2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
4 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. <memberProgress :current="1000" :total="3800"></memberProgress>
  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 memberProgress from '../components/memberProgress.vue'
  74. import mixinsConfigList from '@/mixins/configList.js'
  75. import {
  76. mapGetters,
  77. mapState
  78. } from 'vuex'
  79. export default {
  80. name: "MemberCenter",
  81. mixins: [mixinsConfigList],
  82. components: {
  83. memberProgress,
  84. },
  85. data() {
  86. return {
  87. chargeOptions: [],
  88. selectedChargeId: '001',
  89. }
  90. },
  91. onShow() {
  92. // #ifdef H5
  93. this.fetchCargeOptions()
  94. // #if
  95. },
  96. computed: {
  97. ...mapGetters(['role']),
  98. ...mapState(['userInfo', 'vipInfo']),
  99. selectedChargeObj() {
  100. return this.chargeOptions.find(item => item.id === this.selectedChargeId)
  101. },
  102. totalPrice() {
  103. return this.selectedChargeObj?.price || 0
  104. }
  105. },
  106. methods: {
  107. async fetchCargeOptions() {
  108. try {
  109. this.chargeOptions = (await this.$fetch('queryComboList'))?.records || []
  110. this.selectedChargeId = this.chargeOptions?.[0]?.id
  111. } catch (err) {
  112. }
  113. },
  114. onSelectCharge(id) {
  115. this.selectedChargeId = id
  116. },
  117. async submit() {
  118. try {
  119. let params = {
  120. comoId: this.selectedChargeId
  121. }
  122. let result = await this.$fetch('addVip', params)
  123. // #ifdef H5
  124. // H5环境下使用微信JSSDK支付
  125. await this.$wxPay({result})
  126. // #endif
  127. // #ifdef MP-WEIXIN
  128. // 小程序环境使用原有支付方式
  129. await uni.requestPaymentWxPay({result})
  130. // #endif
  131. uni.showToast({
  132. title: '充值成功',
  133. icon : 'none'
  134. })
  135. setTimeout(uni.navigateBack, 800, -1)
  136. } catch (err) {
  137. console.error('支付失败:', err)
  138. if (err.type === 'cancel') {
  139. uni.showToast({
  140. title: '用户取消支付',
  141. icon: 'none'
  142. })
  143. } else {
  144. uni.showToast({
  145. title: '支付失败',
  146. icon: 'none'
  147. })
  148. }
  149. }
  150. }
  151. }
  152. }
  153. </script>
  154. <style scoped lang="scss">
  155. $bar-height: 132rpx;
  156. .bg {
  157. width: 100vw;
  158. height: 550rpx;
  159. background-image: linear-gradient(#84A73F, #D8FF8F);
  160. }
  161. .page {
  162. padding-bottom: calc(#{$bar-height} + env(safe-area-inset-bottom));
  163. background-color: $uni-fg-color;
  164. min-height: 100vh;
  165. position: relative;
  166. }
  167. .content {
  168. position: absolute;
  169. top: 0;
  170. left: 0;
  171. width: 100vw;
  172. }
  173. .user {
  174. align-items: flex-start;
  175. color: $uni-text-color-inverse;
  176. font-size: 22rpx;
  177. margin: 34rpx 18rpx 0 18rpx;
  178. &-avatar {
  179. width: 147rpx;
  180. height: 147rpx;
  181. margin-right: 8rpx;
  182. border-radius: 50%;
  183. }
  184. &-info {
  185. flex: 1;
  186. }
  187. &-name {
  188. font-size: 32rpx;
  189. margin-top: 16rpx;
  190. justify-content: flex-start;
  191. .icon {
  192. height: auto;
  193. &-role {
  194. width: 138rpx;
  195. }
  196. }
  197. }
  198. &-desc {
  199. margin-top: 26rpx;
  200. }
  201. }
  202. .consumption {
  203. margin: 0 28rpx;
  204. &-desc {
  205. color: $uni-text-color-inverse;
  206. font-size: 22rpx;
  207. margin-top: 1rpx;
  208. }
  209. }
  210. .charge {
  211. background-color: $uni-fg-color;
  212. border-top-left-radius: 18rpx;
  213. border-top-right-radius: 18rpx;
  214. margin-top: 45rpx;
  215. &-header {
  216. $header-height: 90rpx;
  217. height: $header-height;
  218. position: relative;
  219. &-title {
  220. width: calc(50% + 25rpx);
  221. height: 100%;
  222. color: $uni-text-color-inverse;
  223. font-size: 28rpx;
  224. background-image: linear-gradient(#84A73F, #D8FF8F);
  225. box-sizing: border-box;
  226. border-top-left-radius: 18rpx;
  227. border-bottom-right-radius: 90rpx;
  228. }
  229. &-mask {
  230. $marsk-width: 50rpx;
  231. position: absolute;
  232. top: 0;
  233. right: 50%;
  234. transform: translateX(25rpx);
  235. // height: 100%;
  236. // width: 70rpx;
  237. width: 0;
  238. height: 0;
  239. border-top: calc(#{$header-height}/2) solid transparent;
  240. border-left: calc(#{$marsk-width}/2) solid transparent;
  241. border-bottom: calc(#{$header-height}/2) solid $uni-fg-color;
  242. border-right: calc(#{$marsk-width}/2) solid $uni-fg-color;
  243. }
  244. }
  245. &-content {
  246. padding: 57rpx 14rpx;
  247. }
  248. &-selection {
  249. justify-content: space-between;
  250. font-size: 0;
  251. flex-wrap: wrap;
  252. }
  253. &-option {
  254. width: 230rpx;
  255. height: 248rpx;
  256. box-sizing: border-box;
  257. background-color: #F5F5F5;
  258. border: 3rpx solid #C7C7C7;
  259. border-radius: 16rpx;
  260. box-shadow: 0rpx 3rpx 6rpx 0rpx #eef3e3;
  261. color: #999999;
  262. margin-bottom: 20rpx;
  263. &-value {
  264. font-size: 61rpx;
  265. }
  266. &-unit {
  267. font-size: 34rpx;
  268. margin-right: 6rpx;
  269. }
  270. &.is-active {
  271. background-color: rgba($color: #E9FFC3, $alpha: 0.74);
  272. border-color: $uni-color-light;
  273. color: #F64041;
  274. }
  275. }
  276. &-rights {
  277. margin-top: 47rpx;
  278. &-header {
  279. .title {
  280. width: 202rpx;
  281. height: auto;
  282. }
  283. }
  284. &-content {
  285. margin-top: 31rpx;
  286. color: $uni-color-light;
  287. font-size: 28rpx;
  288. line-height: 66rpx;
  289. background-color: rgba($color: #E2FFAE, $alpha: 0.71);
  290. border-radius: 16rpx;
  291. padding: 29rpx 30rpx;
  292. width: 100%;
  293. min-height: 500rpx;
  294. box-sizing: border-box;
  295. position: relative;
  296. .icon {
  297. position: absolute;
  298. top: 23rpx;
  299. right: 11rpx;
  300. width: 131rpx;
  301. height: auto;
  302. }
  303. }
  304. }
  305. }
  306. // 下单
  307. .bar {
  308. position: fixed;
  309. z-index: 1000;
  310. bottom: 0;
  311. left: 0;
  312. width: 100vw;
  313. height: $bar-height;
  314. padding-bottom: env(safe-area-inset-bottom);
  315. background-color: $uni-fg-color;
  316. .count {
  317. flex: 1;
  318. color: #000000;
  319. font-size: 28rpx;
  320. margin-left: 48rpx;
  321. justify-content: flex-start;
  322. .price {
  323. color: #FF2A2A;
  324. font-size: 30rpx;
  325. &-unit {
  326. font-size: 18rpx;
  327. }
  328. }
  329. }
  330. .btn {
  331. border: none;
  332. line-height: 1;
  333. background-color: transparent;
  334. padding: 0;
  335. width: auto;
  336. height: auto;
  337. margin: 0;
  338. &-pay {
  339. margin-right: 41rpx;
  340. padding: 24rpx 137rpx;
  341. color: $uni-text-color-inverse;
  342. font-size: 28rpx;
  343. border-radius: 44rpx;
  344. background-image: linear-gradient(to right, #84A73F, #D8FF8F);
  345. }
  346. }
  347. }
  348. </style>