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

413 lines
9.0 KiB

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