index.tsx 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519
  1. import {
  2. Image,
  3. Cell,
  4. CellGroup,
  5. Tag,
  6. Button,
  7. Stepper,
  8. Icon,
  9. Popup,
  10. showConfirmDialog,
  11. showToast
  12. } from 'vant';
  13. import { computed, defineComponent, onMounted, reactive } from 'vue';
  14. import styles from './index.module.less';
  15. import iconGift from './images/icon-gift.png';
  16. import shopEmpty from './images/shop-empty.png';
  17. import MSticky from '@/components/m-sticky';
  18. import MVideo from '@/components/m-video';
  19. import { useRoute, useRouter } from 'vue-router';
  20. import RegisterModal from './register-modal';
  21. import { useStudentRegisterStore } from '@/store/modules/student-register-store';
  22. import request from '@/helpers/request';
  23. import { moneyFormat } from '@/helpers/utils';
  24. import deepClone from '@/helpers/deep-clone';
  25. import { storage } from '@/helpers/storage';
  26. import { ACCESS_TOKEN } from '@/store/mutation-types';
  27. import OWxTip from '@/components/m-wx-tip';
  28. import MDialog from '@/components/m-dialog';
  29. import { CurrentTime, useCountDown } from '@vant/use';
  30. export default defineComponent({
  31. name: 'student-register',
  32. setup() {
  33. const route = useRoute();
  34. const studentRegisterStore = useStudentRegisterStore();
  35. const router = useRouter();
  36. // 初始化学校编号
  37. studentRegisterStore.setShoolId(route.query.sId as any);
  38. const forms = reactive({
  39. schoolId: route.query.sId as any,
  40. popupShow: false,
  41. popupRegister: false,
  42. details: [] as any[],
  43. schoolType: '', // 学校类型
  44. gradeYear: '', // 学制
  45. bugGoods: false, // 是否购买AI
  46. submitLoading: false,
  47. dialogStatus: false,
  48. dialogMessage: '',
  49. countDownTime: 60 * 1000,
  50. dialogConfig: {} as any
  51. });
  52. const countDown = useCountDown({
  53. // 倒计时 60 秒
  54. time: forms.countDownTime,
  55. onChange(current: CurrentTime) {
  56. forms.dialogMessage = `有待支付订单,请在${Math.ceil(
  57. current.total / 1000
  58. )}s后重试`;
  59. },
  60. onFinish() {
  61. forms.dialogStatus = false;
  62. }
  63. });
  64. // 查询未支付订单
  65. const paymentOrderUnpaid = async () => {
  66. let result = false;
  67. try {
  68. const { data } = await request.get('/edu-app/userPaymentOrder/unpaid');
  69. // 判断是否有待支付订单
  70. if (!data.id) return false;
  71. // 判断是否可以取消订单
  72. if (data.cancelPayment) {
  73. await request.post(
  74. '/edu-app/userPaymentOrder/cancelPayment/' + data.orderNo
  75. );
  76. return false;
  77. } else {
  78. forms.countDownTime = data.cancelTimes;
  79. countDown.reset(Number(data.cancelTimes));
  80. countDown.start();
  81. forms.dialogMessage = `有待支付订单,请在${Math.ceil(
  82. countDown.current.value.total / 1000
  83. )}s后重试`;
  84. forms.dialogStatus = true;
  85. forms.dialogConfig = data;
  86. result = true;
  87. }
  88. } catch {
  89. //
  90. }
  91. return result;
  92. };
  93. const getRegisterGoods = async () => {
  94. try {
  95. const { data } = await request.get(
  96. '/edu-app/open/userOrder/registerGoods/' + forms.schoolId,
  97. {
  98. noAuthorization: true // 是否请求接口的时候添加toekn
  99. }
  100. );
  101. // 默认选中商品
  102. studentRegisterStore.setVip(data.details || []);
  103. forms.details = deepClone(data.details || []);
  104. forms.bugGoods = data.bugGoods;
  105. forms.schoolType = data.schoolType;
  106. forms.gradeYear = data.gradeYear;
  107. } catch {}
  108. };
  109. // 计算金额
  110. const calcPrice = computed(() => {
  111. let amount: number = 0; //现价
  112. let originAmount: number = 0; // 原价
  113. const vipList: any[] = studentRegisterStore.getVip;
  114. vipList.forEach((vip: any) => {
  115. amount += Number(vip.currentPrice);
  116. originAmount += Number(vip.originalPrice);
  117. });
  118. const goodsList: any[] = studentRegisterStore.getGoods;
  119. goodsList.forEach((good: any) => {
  120. amount += Number(good.price) * good.quantity;
  121. originAmount += Number(good.originalPrice) * good.quantity;
  122. });
  123. return {
  124. amount,
  125. originAmount
  126. };
  127. });
  128. // 删除商品
  129. const onGoodsRemove = (item: any) => {
  130. showConfirmDialog({
  131. message: '是否删除该商品',
  132. confirmButtonColor: '#FF8633'
  133. }).then(() => {
  134. studentRegisterStore.deleteGoods(item.productSkuId);
  135. });
  136. };
  137. // 登记成功之后购买
  138. const onRegisterSubmit = async () => {
  139. try {
  140. forms.submitLoading = true;
  141. // 检测token是否失效
  142. const Authorization = storage.get(ACCESS_TOKEN) || '';
  143. const authInfo = await request.post('/edu-app/open/user/verification', {
  144. noAuthorization: true,
  145. data: { token: Authorization }
  146. });
  147. // 判断当前token是否失效
  148. if (!authInfo.data) {
  149. storage.remove(ACCESS_TOKEN);
  150. studentRegisterStore.deleteToken();
  151. forms.popupRegister = true;
  152. return;
  153. }
  154. // 请求是否有待支付订单,如果有则自动关闭
  155. const status = await paymentOrderUnpaid();
  156. if (status) return;
  157. const schoolInfo = await request.get(
  158. '/edu-app/userPaymentOrder/registerStatus/' + forms.schoolId
  159. );
  160. const vipList = studentRegisterStore.getVip;
  161. const goodsList = studentRegisterStore.getGoods;
  162. if (schoolInfo.data.hasBuyCourse && vipList.length > 0) {
  163. setTimeout(() => {
  164. showToast('您已购买数字化器乐学练工具,请勿重复购买');
  165. }, 100);
  166. return;
  167. }
  168. const params: any[] = [];
  169. vipList.forEach((vip: any) => {
  170. params.push({
  171. goodsId: vip.goodsId,
  172. goodsNum: 1,
  173. goodsType: vip.goodsType,
  174. paymentCashAmount: vip.currentPrice, // 现金支付金额
  175. paymentCouponAmount: 0 // 优惠券金额
  176. });
  177. });
  178. goodsList.forEach((goods: any) => {
  179. params.push({
  180. goodsId: goods.productId,
  181. goodsNum: goods.quantity,
  182. goodsType: 'INSTRUMENTS',
  183. paymentCashAmount: goods.price, // 现金支付金额
  184. paymentCouponAmount: 0, // 优惠券金额
  185. goodsSkuId: goods.productSkuId
  186. });
  187. });
  188. // 创建订单
  189. const { data } = await request.post(
  190. '/edu-app/userPaymentOrder/executeOrder',
  191. {
  192. hideLoading: false,
  193. data: {
  194. paymentType: 'adapay',
  195. bizId: forms.schoolId, // 乐团编号
  196. orderType: 'SCHOOL_REGISTER',
  197. paymentCashAmount: calcPrice.value.amount || 0,
  198. paymentCouponAmount: 0,
  199. goodsInfos: params,
  200. orderName: '学生登记',
  201. orderDesc: '学生登记'
  202. }
  203. }
  204. );
  205. router.push({
  206. path: '/order-detail',
  207. query: {
  208. pm: 1, // h5乐团报名
  209. config: JSON.stringify({
  210. ...data.paymentConfig,
  211. paymentType: data.paymentType
  212. }),
  213. orderNo: data.orderNo
  214. }
  215. });
  216. } finally {
  217. forms.submitLoading = false;
  218. }
  219. };
  220. onMounted(() => {
  221. getRegisterGoods();
  222. });
  223. return () => (
  224. <div class={styles['student-register']}>
  225. <div class={styles.studentSection} style={{ marginTop: '18px' }}>
  226. <div class={styles.titleTool}></div>
  227. {forms.details.map((item: any) => (
  228. <CellGroup
  229. class={styles.goodsSection}
  230. onClick={() => {
  231. if (studentRegisterStore.selectedVip(item.goodsId)) {
  232. studentRegisterStore.deleteVip(item.goodsId);
  233. } else {
  234. studentRegisterStore.setVip([item]);
  235. }
  236. }}>
  237. <Cell border={false} class={styles.goodsCell}>
  238. {{
  239. icon: () => <Image class={styles.img} src={item.goodsUrl} />,
  240. title: () => (
  241. <div class={styles.section}>
  242. <div class={styles.sectionContent}>
  243. <h2>
  244. {item.goodsName}
  245. <Tag class={styles.brandName}>12个月</Tag>
  246. </h2>
  247. <p class={[styles.model]}>{item.description}</p>
  248. {/* <div class={styles.sbtnGroup}>
  249. <span
  250. class={styles.btnDetail}
  251. onClick={(e: MouseEvent) => {
  252. e.stopPropagation();
  253. router.push('/student-digital-tools');
  254. }}>
  255. 查看详情
  256. </span>
  257. <span
  258. class={styles.btnVideo}
  259. onClick={(e: MouseEvent) => {
  260. e.stopPropagation();
  261. forms.popupShow = true;
  262. }}>
  263. 介绍视频
  264. </span>
  265. </div> */}
  266. </div>
  267. <i
  268. class={
  269. studentRegisterStore.selectedVip(item.goodsId)
  270. ? styles.selected
  271. : styles.noSelected
  272. }></i>
  273. </div>
  274. )
  275. }}
  276. </Cell>
  277. <Cell border={false} class={styles.priceCell}>
  278. {{
  279. title: () => (
  280. <div class={styles.sPriceGroup}>
  281. <div class={styles.tg}>
  282. 团购价:
  283. <span>
  284. <i>¥ </i>
  285. {moneyFormat(item.currentPrice)}
  286. </span>
  287. </div>
  288. {item.currentPrice < item.originalPrice && (
  289. <del>¥{moneyFormat(item.originalPrice)}</del>
  290. )}
  291. </div>
  292. )
  293. }}
  294. </Cell>
  295. {item.membershipDays > 0 && (
  296. <Cell border={false} class={styles.giftCell}>
  297. {{
  298. title: () => (
  299. <div class={styles.gift}>
  300. <img src={iconGift} class={styles.iconGift} />
  301. 现在购买赠送 <span>{item.membershipDays || 0}</span>
  302. 天有效期
  303. </div>
  304. )
  305. }}
  306. </Cell>
  307. )}
  308. </CellGroup>
  309. ))}
  310. </div>
  311. {forms.bugGoods && (
  312. <>
  313. <div class={styles.studentSection}>
  314. <div class={styles.titleBuy}></div>
  315. {studentRegisterStore.getGoods &&
  316. studentRegisterStore.getGoods.length <= 0 ? (
  317. <div class={styles.goodsEmpty}>
  318. <img src={shopEmpty} class={styles.shopImg} />
  319. <div class={styles.goodsContainer}>
  320. <h2>
  321. 为你的<span>音乐之旅</span>做好准备
  322. </h2>
  323. <p class={styles.tips}>快去选购乐器吧~</p>
  324. <Button
  325. class={styles.goSelect}
  326. type="primary"
  327. onClick={() => {
  328. router.push('/goods-list');
  329. }}>
  330. 进入商城选购
  331. <Icon name="arrow" />
  332. </Button>
  333. </div>
  334. </div>
  335. ) : (
  336. studentRegisterStore.getGoods.map((goods: any) => (
  337. <CellGroup class={styles.goodsSection}>
  338. <Cell border={false} class={styles.goodsCell}>
  339. {{
  340. icon: () => (
  341. <Image class={styles.img} src={goods.pic} />
  342. ),
  343. title: () => (
  344. <div class={styles.section}>
  345. <div class={styles.sectionContent}>
  346. <h2>
  347. {goods.name}
  348. <Tag class={styles.brandName}>
  349. {goods.brandName}
  350. </Tag>
  351. </h2>
  352. <p class={[styles.model]}>
  353. 规格:{goods.spDataJson}
  354. </p>
  355. <p class={[styles.model]}>{goods.productSn}</p>
  356. <Stepper
  357. min={1}
  358. max={99}
  359. v-model={goods.quantity}
  360. />
  361. </div>
  362. <i
  363. class={styles.delete}
  364. onClick={() => onGoodsRemove(goods)}></i>
  365. </div>
  366. )
  367. }}
  368. </Cell>
  369. <Cell border={false} class={styles.priceCell}>
  370. {{
  371. title: () => (
  372. <div class={styles.sPriceGroup}>
  373. <div class={styles.tg}>
  374. 团购价:
  375. <span>
  376. <i>¥ </i>
  377. {moneyFormat(goods.price)}
  378. </span>
  379. </div>
  380. {goods.price < goods.originalPrice && (
  381. <del>¥{moneyFormat(goods.originalPrice)}</del>
  382. )}
  383. </div>
  384. )
  385. }}
  386. </Cell>
  387. </CellGroup>
  388. ))
  389. )}
  390. </div>
  391. {studentRegisterStore.getGoods &&
  392. studentRegisterStore.getGoods.length > 0 && (
  393. <Button
  394. class={styles.addButton}
  395. block
  396. onClick={() => {
  397. router.push('/goods-list');
  398. }}>
  399. <Icon name="add-o" />
  400. 进入商城选购
  401. </Button>
  402. )}
  403. </>
  404. )}
  405. <MSticky position="bottom">
  406. <div class={styles.paymentContainer}>
  407. <div class={styles.payemntPrice}>
  408. <span class={styles.needPrice}>
  409. <i style="font-style: normal">¥ </i>
  410. <span>{moneyFormat(calcPrice.value.amount)}</span>
  411. </span>
  412. <del class={styles.allPrice}>
  413. ¥ {moneyFormat(calcPrice.value.originAmount)}
  414. </del>
  415. </div>
  416. <div
  417. class={styles.paymentBtn}
  418. onClick={() => {
  419. const vipList = studentRegisterStore.getVip;
  420. const goodsList = studentRegisterStore.getGoods;
  421. if (vipList.length <= 0 && goodsList.length <= 0) {
  422. setTimeout(() => {
  423. showToast('请选择需要购买的商品');
  424. }, 100);
  425. return;
  426. }
  427. if (!studentRegisterStore.getToken) {
  428. forms.popupRegister = true;
  429. } else {
  430. onRegisterSubmit();
  431. }
  432. }}>
  433. <Button
  434. disabled={forms.submitLoading}
  435. loading={forms.submitLoading}>
  436. 确认购买
  437. </Button>
  438. </div>
  439. </div>
  440. </MSticky>
  441. <Popup v-model:show={forms.popupShow} class={styles.videoPopup}>
  442. {forms.popupShow && (
  443. <MVideo
  444. src={'https://daya.ks3-cn-beijing.ksyun.com/202105/SWmqmvW.mp4'}
  445. />
  446. )}
  447. </Popup>
  448. <Popup
  449. v-model:show={forms.popupRegister}
  450. class={styles.registerPopup}
  451. position="bottom"
  452. round>
  453. <RegisterModal
  454. schoolId={forms.schoolId}
  455. schoolType={forms.schoolType}
  456. gradeYear={forms.gradeYear}
  457. onClose={() => (forms.popupRegister = false)}
  458. onSubmit={onRegisterSubmit}
  459. />
  460. </Popup>
  461. <MDialog
  462. title="提示"
  463. v-model:show={forms.dialogStatus}
  464. message={forms.dialogMessage}
  465. allowHtml={true}
  466. confirmButtonText="继续支付"
  467. onConfirm={() => {
  468. const paymentConfig = forms.dialogConfig.paymentConfig;
  469. router.push({
  470. path: '/order-detail',
  471. query: {
  472. pm: 1, // h5乐团报名
  473. config: JSON.stringify(paymentConfig.paymentConfig),
  474. orderNo: paymentConfig.orderNo
  475. }
  476. });
  477. countDown.pause();
  478. }}
  479. onCancel={(val: any) => {
  480. countDown.pause();
  481. }}
  482. />
  483. {/* 是否在微信中打开 */}
  484. {/* <OWxTip /> */}
  485. </div>
  486. );
  487. }
  488. });