index.tsx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. import { Button, Cell, Icon, Image, showConfirmDialog } from 'vant';
  2. import { defineComponent } from 'vue';
  3. import styles from './index.module.less';
  4. import request from '@/helpers/request';
  5. import iconStudent from '@common/images/icon-student.png';
  6. import iconMemberLogo from './images/member_logo.png';
  7. import iconGift from '../student-register/images/icon-gift.png';
  8. import { browser, moneyFormat } from '@/helpers/utils';
  9. import OHeader from '@/components/m-header';
  10. import member1 from './images/member-1.png';
  11. import member2 from './images/member-2.png';
  12. import member1Tablet from './images/member-1-tablet.png';
  13. import member2Tablet from './images/member-2-tablet.png';
  14. import ODialog from '@/components/m-dialog';
  15. import { useEventListener, useWindowScroll } from '@vueuse/core';
  16. import dayjs from 'dayjs';
  17. const vipGiftPeriodType = {
  18. DAY: '天',
  19. MONTH: '个月',
  20. YEAR: '年'
  21. } as any;
  22. export default defineComponent({
  23. name: 'MemberCenter',
  24. data() {
  25. return {
  26. functionList: [] as any,
  27. selectMember: {} as any,
  28. users: {} as any,
  29. memberStatus: false,
  30. background: 'transparent',
  31. showTips: false,
  32. showMessage: '',
  33. paymentType: '', // 支付渠道
  34. paymentChannel: '' // 支付类型
  35. };
  36. },
  37. computed: {
  38. userInfo() {
  39. const users: any = this.users;
  40. return {
  41. username: users?.nickname || '',
  42. phone: users?.phone || '',
  43. avatar: users?.avatar,
  44. id: users?.id,
  45. isVip: users?.vipMember,
  46. membershipGiftDays: users?.membershipGiftDays,
  47. membershipDays: users?.membershipDays,
  48. membershipEndTime: dayjs(users?.membershipEndTime).format('YYYY-MM-DD')
  49. };
  50. }
  51. },
  52. async mounted() {
  53. useEventListener(document, 'scroll', () => {
  54. const { y } = useWindowScroll();
  55. if (y.value > 15) {
  56. this.background = '#fff';
  57. } else {
  58. this.background = 'transparent';
  59. }
  60. });
  61. //
  62. this.__init();
  63. },
  64. methods: {
  65. async __init() {
  66. try {
  67. const userInfo = await request.get('/edu-app/student/member');
  68. this.users = userInfo.data || {};
  69. const { data } = await request.post(`/edu-app/cityFeeSetting/member`);
  70. this.selectMember = data;
  71. this.getConfig();
  72. this.paymentOrderUnpaid();
  73. } catch {
  74. //
  75. }
  76. },
  77. async getConfig() {
  78. try {
  79. const { data } = await request.get(
  80. '/edu-app/open/paramConfig/queryByParamNameList',
  81. {
  82. requestType: 'form',
  83. params: {
  84. paramNames: 'vip_payment_service_provider'
  85. }
  86. }
  87. );
  88. if (data && Array.isArray(data)) {
  89. data.forEach((item: any) => {
  90. if (item.paramName === 'vip_payment_service_provider') {
  91. const provider = JSON.parse(item.paramValue);
  92. this.paymentType = provider.vendor;
  93. this.paymentChannel = provider.channel;
  94. }
  95. });
  96. }
  97. } catch {
  98. //
  99. }
  100. },
  101. // 查询未支付订单
  102. async paymentOrderUnpaid() {
  103. let result = false;
  104. try {
  105. const { data } = await request.get('/edu-app/userPaymentOrder/unpaid', {
  106. requestType: 'form',
  107. params: {
  108. paymentType: 'VIP'
  109. }
  110. });
  111. // 判断是否有待支付订单
  112. if (data?.id) {
  113. result = true;
  114. showConfirmDialog({
  115. message: '您有待支付的订单,是否继续支付',
  116. cancelButtonText: '取消订单',
  117. confirmButtonText: '继续支付'
  118. })
  119. .then(() => {
  120. const paymentConfig = data.paymentConfig;
  121. this.$router.push({
  122. path: '/order-detail',
  123. query: {
  124. config: JSON.stringify({
  125. ...paymentConfig.paymentConfig
  126. }),
  127. paymentType: paymentConfig.paymentType,
  128. paymentChannel: this.paymentChannel,
  129. orderNo: paymentConfig.orderNo
  130. }
  131. });
  132. })
  133. .catch(async () => {
  134. try {
  135. await request.post(
  136. '/edu-app/userPaymentOrder/cancelPayment/' + data.orderNo
  137. );
  138. } catch {
  139. //
  140. }
  141. });
  142. }
  143. } catch {
  144. //
  145. result = true;
  146. }
  147. return result;
  148. },
  149. calcSalePrice(item: any) {
  150. // discount
  151. if (item.discount === 1) {
  152. const tempPrice = Number(
  153. (item.salePrice - item.discountPrice).toFixed(2)
  154. );
  155. return tempPrice >= 0 ? tempPrice : 0;
  156. }
  157. return item.salePrice;
  158. },
  159. // 购买
  160. async onSubmit() {
  161. const data = await this.paymentOrderUnpaid();
  162. if (data) return;
  163. try {
  164. const selectMember = this.selectMember;
  165. const params: any = [
  166. {
  167. giftVipDay: selectMember.membershipDays,
  168. giftPeriod: selectMember.giftPeriod,
  169. goodsId: selectMember.id,
  170. goodsNum: 1,
  171. goodsType: 'VIP',
  172. paymentCashAmount: selectMember.salePrice, // 现金支付金额
  173. paymentCouponAmount: 0 // 优惠券金额
  174. }
  175. ]; // 支付参数
  176. // 创建订单
  177. const result = await request.post(
  178. '/edu-app/userPaymentOrder/executeOrder',
  179. {
  180. data: {
  181. orderType: 'VIP',
  182. paymentType: this.paymentType,
  183. paymentCashAmount: this.selectMember.salePrice || 0,
  184. paymentCouponAmount: 0,
  185. goodsInfos: params,
  186. orderName: '乐器AI学练工具',
  187. orderDesc: '乐器AI学练工具'
  188. }
  189. }
  190. );
  191. if (result.code === 5435) {
  192. this.showTips = true;
  193. this.showMessage = result.message;
  194. return;
  195. }
  196. const data = result.data;
  197. const res = await request.get(
  198. '/edu-app/userPaymentOrder/detail/' + data.orderNo
  199. );
  200. if (res.data.status !== 'WAIT_PAY' && res.data.status !== 'PAYING') {
  201. this.$router.push({
  202. path: '/payment-result',
  203. query: {
  204. orderNo: data.orderNo
  205. }
  206. });
  207. } else {
  208. this.$router.push({
  209. path: '/order-detail',
  210. query: {
  211. config: JSON.stringify({
  212. ...data.paymentConfig
  213. }),
  214. paymentType: this.paymentType,
  215. paymentChannel: this.paymentChannel,
  216. orderNo: data.orderNo
  217. }
  218. });
  219. }
  220. } catch (e: any) {
  221. //
  222. console.log(e);
  223. }
  224. }
  225. },
  226. render() {
  227. return (
  228. <div
  229. class={[
  230. styles['member-center'],
  231. browser().isTablet ? styles.memberCenterTablet : ''
  232. ]}>
  233. <OHeader background={this.background} border={false} />
  234. <div class={styles.member_container}>
  235. <Cell
  236. class={[styles.userMember]}
  237. labelClass={styles.timeRemaining}
  238. center
  239. v-slots={{
  240. icon: () => (
  241. <div class={styles.userImgSection}>
  242. <Image
  243. class={styles.userImg}
  244. src={this.userInfo.avatar || iconStudent}
  245. fit="cover"
  246. />
  247. </div>
  248. ),
  249. title: () => (
  250. <div class={styles.userInfo}>
  251. <span class={styles.name}>{this.userInfo.username}</span>
  252. {!!this.userInfo.isVip && (
  253. <Image class={styles.level} src={iconMemberLogo} />
  254. )}
  255. {this.userInfo.phone && (
  256. <span
  257. class={styles.phone}
  258. v-html={`(${this.userInfo.phone})`}></span>
  259. )}
  260. </div>
  261. ),
  262. label: () => (
  263. <div class={styles.member_time}>
  264. <>
  265. {this.userInfo.isVip ? (
  266. <div>
  267. {/* 使用有效期剩余
  268. <span class={styles.remaining}>
  269. {this.userInfo.membershipDays}
  270. </span>
  271. 天 */}
  272. 有效期至
  273. <span class={styles.remaining}>
  274. {this.userInfo.membershipEndTime}
  275. </span>
  276. </div>
  277. ) : (
  278. <div>您还未开通乐器AI学练工具哟</div>
  279. )}
  280. </>
  281. </div>
  282. )
  283. }}></Cell>
  284. </div>
  285. <div class={[styles.memberContainer]}>
  286. <div
  287. class={[
  288. styles.memberItem,
  289. this.users.membershipGiftDays > 0 ? styles.memberGift : ''
  290. ]}>
  291. <p class={[styles.title]}>
  292. 乐器AI学练工具
  293. <span>12个月</span>
  294. </p>
  295. <div class={styles.priceGroup}>
  296. <p class={styles.price}>
  297. <span>¥</span>
  298. {moneyFormat(this.selectMember.salePrice)}
  299. </p>
  300. {this.selectMember.salePrice <
  301. this.selectMember.originalPrice && (
  302. <del class={styles.originalPrice}>
  303. ¥{moneyFormat(this.selectMember.originalPrice)}
  304. </del>
  305. )}
  306. </div>
  307. {this.users.membershipGiftDays > 0 && (
  308. <Cell border={false} class={styles.giftCell}>
  309. {{
  310. title: () => (
  311. <div class={styles.gift}>
  312. <img src={iconGift} class={styles.iconGift} />
  313. 现在开通赠送
  314. <span>{this.users.membershipGiftDays || 0}</span>
  315. {vipGiftPeriodType[this.selectMember.giftPeriod]}有效期
  316. </div>
  317. )
  318. }}
  319. </Cell>
  320. )}
  321. </div>
  322. <div class={styles.memberImgs}>
  323. {browser().isTablet ? (
  324. <>
  325. <img src={member1Tablet} />
  326. <img src={member2Tablet} />
  327. </>
  328. ) : (
  329. <>
  330. <img src={member1} />
  331. <img src={member2} />
  332. </>
  333. )}
  334. </div>
  335. </div>
  336. <div class={styles.btnGroup}>
  337. <div class={styles.priceSection}>
  338. <div class={styles.price}>
  339. <span class={styles.priceUnit}>¥</span>
  340. <span class={styles.priceNum}>
  341. {moneyFormat(this.calcSalePrice(this.selectMember) || 0)}
  342. </span>
  343. </div>
  344. {this.selectMember.originalPrice >
  345. this.calcSalePrice(this.selectMember) || 0 ? (
  346. <del class={styles.allPrice}>
  347. ¥ {moneyFormat(this.selectMember.originalPrice)}
  348. </del>
  349. ) : (
  350. ''
  351. )}
  352. </div>
  353. {this.userInfo.id ? (
  354. <Button round class={styles.btn} onClick={this.onSubmit}>
  355. 立即开通
  356. </Button>
  357. ) : (
  358. ''
  359. )}
  360. </div>
  361. <ODialog
  362. v-model:show={this.memberStatus}
  363. title="待激活团练宝"
  364. message="为让团员有效使用乐团学习工具,首次加入乐团且购买团练宝的团员,团练宝的生效时间为乐团首次训练之日,具体训练时间可查看课表。"
  365. messageAlign="left"
  366. dialogMarginTop="env(safe-area-inset-top)"
  367. confirmButtonText="我知道了"
  368. />
  369. <ODialog
  370. v-model:show={this.showTips}
  371. title="温馨提示"
  372. message={this.showMessage}
  373. messageAlign="center"
  374. dialogMarginTop="env(safe-area-inset-top)"
  375. confirmButtonText="刷新"
  376. onConfirm={async () => {
  377. // window.location.reload();
  378. window.scrollTo({
  379. top: 0,
  380. behavior: 'smooth'
  381. });
  382. this.__init();
  383. }}
  384. />
  385. </div>
  386. );
  387. }
  388. });