payment.tsx 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513
  1. import OSticky from '@/components/o-sticky'
  2. import {
  3. Button,
  4. Cell,
  5. CellGroup,
  6. Checkbox,
  7. CheckboxGroup,
  8. Icon,
  9. Image,
  10. showConfirmDialog,
  11. showToast,
  12. Tag
  13. } from 'vant'
  14. import { defineComponent, nextTick, onMounted, reactive, ref } from 'vue'
  15. import styles from '../index.module.less'
  16. import radioCheck from '@/common/images/icon-radio-check.png'
  17. import radioDefault from '@/common/images/icon-radio-default.png'
  18. import { useRoute, useRouter } from 'vue-router'
  19. import request from '../../request-music'
  20. import { moneyFormat } from '@/helpers/utils'
  21. import { CountUp } from 'countup.js'
  22. export default defineComponent({
  23. name: 'payment',
  24. emits: ['next'],
  25. setup() {
  26. const route = useRoute()
  27. const router = useRouter()
  28. const state = reactive({
  29. check: [] as any, // 选中的数据
  30. checkboxRefs: [] as any,
  31. details: [] as any, //
  32. goodsInfo: {} as any, // 商品
  33. textBookInfo: {} as any, // 教材
  34. repaireInfo: {} as any, // 乐器保养
  35. vipInfo: {} as any, // 团练宝
  36. paymentOrderDetails: [] as any, // 购买状态
  37. orderInfo: {
  38. needPrice: 0,
  39. originalPrice: 0
  40. }
  41. })
  42. // 查询未支付订单
  43. const paymentOrderUnpaid = async () => {
  44. try {
  45. const { data } = await request.get('/api-student/userPaymentOrder/unpaid')
  46. // 判断是否有待支付订单
  47. if (data.id) {
  48. showConfirmDialog({
  49. message: '您有待支付的订单,是否继续支付',
  50. cancelButtonText: '取消订单',
  51. confirmButtonText: '继续支付'
  52. })
  53. .then(() => {
  54. const paymentConfig = data.paymentConfig
  55. router.push({
  56. path: '/orderDetail',
  57. query: {
  58. config: JSON.stringify(paymentConfig.paymentConfig),
  59. orderNo: paymentConfig.orderNo
  60. }
  61. })
  62. })
  63. .catch(async () => {
  64. try {
  65. await request.post('/api-student/userPaymentOrder/cancelPayment/' + data.orderNo)
  66. } catch {
  67. //
  68. }
  69. })
  70. }
  71. } catch {
  72. //
  73. }
  74. }
  75. // 获取商品信息
  76. const registerGoods = async () => {
  77. try {
  78. const { data } = await request.get(
  79. '/api-student/orchestraRegister/registerGoods/' + route.query.id
  80. )
  81. // 获取已经购买商品信息
  82. const paymentOrderDetails = data.paymentOrderDetails || []
  83. paymentOrderDetails.forEach((item: any) => {
  84. state.paymentOrderDetails.push(item.goodsType)
  85. })
  86. // 初始化数据商品数据
  87. const details = data.details || []
  88. details.forEach((item: any) => {
  89. if (item.goodsType === 'INSTRUMENTS') {
  90. const img = item.goodsUrl ? item.goodsUrl.split(',')[0] : ''
  91. state.goodsInfo = { ...item, goodsUrl: img }
  92. } else if (item.goodsType === 'TEXTBOOK') {
  93. const img = item.goodsUrl ? item.goodsUrl.split(',')[0] : ''
  94. state.textBookInfo = { ...item, goodsUrl: img }
  95. } else if (item.goodsType === 'REPAIR') {
  96. state.repaireInfo = { ...item }
  97. } else if (item.goodsType === 'VIP') {
  98. state.vipInfo = { ...item }
  99. }
  100. state.details = details
  101. // 默认选中所有的
  102. if (!state.paymentOrderDetails.includes(item.goodsType) && item.goodsType !== 'REPAIR') {
  103. state.check.push(item.goodsId)
  104. }
  105. })
  106. calcPrice()
  107. } catch {
  108. //
  109. }
  110. }
  111. const onSelect = (type: string) => {
  112. state.checkboxRefs[type].toggle()
  113. calcPrice()
  114. }
  115. // 初始化金额
  116. const calcPrice = () => {
  117. const details = state.details
  118. const tempPrice = {
  119. needPrice: 0, //需要支付金额
  120. originalPrice: 0 // 原价
  121. }
  122. details.forEach((item: any) => {
  123. // 是否选中
  124. if (
  125. state.check.includes(item.goodsId) &&
  126. !state.paymentOrderDetails.includes(item.goodsType)
  127. ) {
  128. tempPrice.needPrice += parseFloat(item.currentPrice)
  129. tempPrice.originalPrice += parseFloat(item.originalPrice)
  130. }
  131. })
  132. state.orderInfo = tempPrice
  133. initNumCountUp()
  134. }
  135. const countUpRef = reactive({
  136. needPrice: null as any,
  137. originalPrice: null as any
  138. })
  139. const initNumCountUp = () => {
  140. nextTick(() => {
  141. // 在读学生
  142. if (countUpRef.needPrice) {
  143. countUpRef.needPrice.update(state.orderInfo.needPrice)
  144. } else {
  145. countUpRef.needPrice = new CountUp('needPrice', state.orderInfo.needPrice, {
  146. decimalPlaces: 2
  147. })
  148. if (!countUpRef.needPrice.error) {
  149. countUpRef.needPrice.start()
  150. } else {
  151. console.error(countUpRef.needPrice.error)
  152. }
  153. }
  154. if (countUpRef.originalPrice) {
  155. countUpRef.originalPrice.update(state.orderInfo.originalPrice)
  156. } else {
  157. countUpRef.originalPrice = new CountUp('originalPrice', state.orderInfo.originalPrice, {
  158. decimalPlaces: 2
  159. })
  160. if (!countUpRef.originalPrice.error) {
  161. countUpRef.originalPrice.start()
  162. } else {
  163. console.error(countUpRef.originalPrice.error)
  164. }
  165. }
  166. })
  167. }
  168. // 购买
  169. const onSubmit = async () => {
  170. try {
  171. // 判断订单号
  172. if (state.check.length <= 0) {
  173. showToast('请选择您要购买的商品')
  174. return
  175. }
  176. // 重新计算金额
  177. calcPrice()
  178. const params: any = [] // 支付参数
  179. const details = state.details
  180. let checkInstruments = false // 是否选择了乐器
  181. details.forEach((item: any) => {
  182. // 是否选中 并且没有购买过
  183. if (
  184. state.check.includes(item.goodsId) &&
  185. !state.paymentOrderDetails.includes(item.goodsType)
  186. ) {
  187. params.push({
  188. goodsId: item.goodsId,
  189. goodsNum: 1,
  190. goodsType: item.goodsType,
  191. paymentCashAmount: item.currentPrice, // 现金支付金额
  192. paymentCouponAmount: 0 // 优惠券金额
  193. })
  194. }
  195. // 判断是否是乐器
  196. if (
  197. item.goodsType === 'INSTRUMENTS' &&
  198. state.check.includes(item.goodsId) &&
  199. !state.paymentOrderDetails.includes(item.goodsType)
  200. ) {
  201. checkInstruments = true
  202. }
  203. })
  204. // 为了处理,商品和乐器保养做关联
  205. const repaire = state.repaireInfo
  206. if (checkInstruments && repaire.goodsId) {
  207. params.push({
  208. goodsId: repaire.goodsId,
  209. goodsNum: 1,
  210. goodsType: repaire.goodsType,
  211. paymentCashAmount: repaire.currentPrice, // 现金支付金额
  212. paymentCouponAmount: 0 // 优惠券金额
  213. })
  214. }
  215. console.log({
  216. bizId: route.query.id, // 乐团编号
  217. orderType: 'ORCHESTRA',
  218. paymentCashAmount: state.orderInfo.needPrice || 0,
  219. paymentCouponAmount: 0,
  220. goodsInfos: params
  221. })
  222. // 创建订单
  223. const { data } = await request.post('/api-student/userPaymentOrder/executeOrder', {
  224. data: {
  225. bizId: route.query.id, // 乐团编号
  226. orderType: 'ORCHESTRA',
  227. paymentCashAmount: state.orderInfo.needPrice || 0,
  228. paymentCouponAmount: 0,
  229. goodsInfos: params,
  230. orderName: '订单详情',
  231. orderDesc: '订单详情'
  232. }
  233. })
  234. console.log(data)
  235. router.push({
  236. path: '/orderDetail',
  237. query: {
  238. config: JSON.stringify(data.paymentConfig),
  239. orderNo: data.orderNo
  240. }
  241. })
  242. } catch (e: any) {
  243. //
  244. console.log(e)
  245. }
  246. // router.push('/orderDetail')
  247. }
  248. onMounted(() => {
  249. // 查询未支付订单
  250. paymentOrderUnpaid()
  251. registerGoods()
  252. })
  253. return () => (
  254. <>
  255. <div class={styles.applyTitle}>报名须知</div>
  256. <div class={[styles.paymentTips, styles.mlr13]}>
  257. <p>1、您注册时所选择的乐团声部,即为乐团录取最终确认的声部,请您务必仔细填写;</p>
  258. <p>
  259. 2、所有参与乐团的学生免费赠送选报声部教材,教材随乐器一同发放,若您自备乐器,则需承担教材运费。
  260. </p>
  261. </div>
  262. <CheckboxGroup
  263. v-model={state.check}
  264. style={{ paddingBottom: '20px' }}
  265. onChange={() => {
  266. calcPrice()
  267. }}
  268. >
  269. {/* 判断是否已经购买乐器 */}
  270. {!state.paymentOrderDetails.includes('INSTRUMENTS') && (
  271. <>
  272. <div class={styles.applyTitle}>乐器</div>
  273. <CellGroup
  274. inset
  275. class={[styles.mlr13, styles.sectionCell]}
  276. onClick={() => onSelect(state.goodsInfo.goodsId)}
  277. >
  278. <Cell border={false}>
  279. {{
  280. icon: () => (
  281. <Checkbox
  282. name={state.goodsInfo.goodsId}
  283. class={styles.checkbox}
  284. ref={(el: any) => (state.checkboxRefs[state.goodsInfo.goodsId] = el)}
  285. onClick={(e: Event) => {
  286. e.stopPropagation()
  287. }}
  288. v-slots={{
  289. icon: (props: any) => (
  290. <Icon
  291. class={styles.iconChecked}
  292. name={props.checked ? radioCheck : radioDefault}
  293. />
  294. )
  295. }}
  296. />
  297. ),
  298. title: () => (
  299. <div class={styles.section}>
  300. <Image class={styles.img} src={state.goodsInfo.goodsUrl} />
  301. <div class={styles.sectionContent}>
  302. <h2>{state.goodsInfo.goodsName}</h2>
  303. <Tag type="primary">{state.goodsInfo.brandName}</Tag>
  304. <p class={styles.model}>{state.goodsInfo.desciption}</p>
  305. </div>
  306. </div>
  307. )
  308. }}
  309. </Cell>
  310. <Cell>
  311. {{
  312. title: () => (
  313. <div class={styles.extra}>
  314. <div class={styles.sectionPrice}>
  315. <p class={styles.price}>
  316. 新团特惠:<span>¥{moneyFormat(state.goodsInfo.currentPrice)}</span>
  317. </p>
  318. <p class={styles.originPrice}>
  319. 原价:<del>¥{moneyFormat(state.goodsInfo.originalPrice)}</del>
  320. </p>
  321. </div>
  322. <div class={styles.sectionTips}>
  323. 赠价值{state.repaireInfo.originalPrice}元乐器维保服务一年
  324. </div>
  325. </div>
  326. )
  327. }}
  328. </Cell>
  329. </CellGroup>
  330. </>
  331. )}
  332. {/* 判断是否已经购买教材 */}
  333. {!state.paymentOrderDetails.includes('TEXTBOOK') && (
  334. <>
  335. <div class={styles.applyTitle}>教材</div>
  336. <CellGroup
  337. inset
  338. class={[styles.mlr13, styles.sectionCell]}
  339. onClick={() => {
  340. return
  341. // onSelect(state.textBookInfo.goodsId)
  342. }}
  343. >
  344. <Cell border={false}>
  345. {{
  346. icon: () => (
  347. <Checkbox
  348. name={state.textBookInfo.goodsId}
  349. disabled
  350. class={styles.checkbox}
  351. ref={(el: any) => (state.checkboxRefs[state.textBookInfo.goodsId] = el)}
  352. onClick={(e: Event) => {
  353. e.stopPropagation()
  354. }}
  355. v-slots={{
  356. icon: (props: any) => (
  357. <Icon
  358. class={styles.iconChecked}
  359. name={props.checked ? radioCheck : radioDefault}
  360. />
  361. )
  362. }}
  363. />
  364. ),
  365. title: () => (
  366. <div class={styles.section}>
  367. <Image class={styles.img} src={state.textBookInfo.goodsUrl} />
  368. <div class={styles.sectionContent}>
  369. <h2>{state.textBookInfo.goodsName}</h2>
  370. <Tag type="primary">{state.textBookInfo.brandName}</Tag>
  371. <p class={styles.model}>{state.textBookInfo.description}</p>
  372. </div>
  373. </div>
  374. )
  375. }}
  376. </Cell>
  377. <Cell>
  378. {{
  379. title: () => (
  380. <div class={styles.extra}>
  381. <div class={styles.sectionPrice}>
  382. <p class={styles.price}>
  383. 新团特惠:
  384. <span class={styles.free}>
  385. {state.textBookInfo.currentPrice > 0
  386. ? moneyFormat(state.textBookInfo.currentPrice)
  387. : '免费赠送'}
  388. </span>
  389. </p>
  390. <p class={styles.originPrice}>
  391. 原价:<del>¥{moneyFormat(state.textBookInfo.originalPrice)}</del>
  392. </p>
  393. </div>
  394. </div>
  395. )
  396. }}
  397. </Cell>
  398. </CellGroup>
  399. </>
  400. )}
  401. {!state.paymentOrderDetails.includes('VIP') && (
  402. <>
  403. <div class={styles.applyTitle}>乐团学习系统</div>
  404. <CellGroup
  405. inset
  406. class={[styles.mlr13, styles.sectionCell]}
  407. onClick={() => onSelect(state.vipInfo.goodsId)}
  408. >
  409. <Cell border={false}>
  410. {{
  411. icon: () => (
  412. <Checkbox
  413. name={state.vipInfo.goodsId}
  414. class={styles.checkbox}
  415. ref={(el: any) => (state.checkboxRefs[state.vipInfo.goodsId] = el)}
  416. onClick={(e: Event) => {
  417. e.stopPropagation()
  418. }}
  419. v-slots={{
  420. icon: (props: any) => (
  421. <Icon
  422. class={styles.iconChecked}
  423. name={props.checked ? radioCheck : radioDefault}
  424. />
  425. )
  426. }}
  427. />
  428. ),
  429. title: () => (
  430. <div class={styles.section}>
  431. <Image class={styles.img} src={state.vipInfo.goodsUrl} />
  432. <div class={styles.sectionContent}>
  433. <h2>{state.vipInfo.goodsName}</h2>
  434. <Tag type="primary">6个月</Tag>
  435. <p class={styles.model}>{state.vipInfo.description}</p>
  436. </div>
  437. </div>
  438. )
  439. }}
  440. </Cell>
  441. <Cell>
  442. {{
  443. title: () => (
  444. <div class={styles.extra}>
  445. <div class={styles.sectionPrice}>
  446. <p class={styles.price}>
  447. 新团特惠:<span>¥{moneyFormat(state.vipInfo.currentPrice)}</span>
  448. </p>
  449. <p class={styles.originPrice}>
  450. 原价:<del>¥{moneyFormat(state.vipInfo.originalPrice)}</del>
  451. </p>
  452. </div>
  453. </div>
  454. )
  455. }}
  456. </Cell>
  457. </CellGroup>
  458. </>
  459. )}
  460. </CheckboxGroup>
  461. <OSticky position="bottom" background="white">
  462. <div class={styles.paymentContainer}>
  463. <div class={styles.payemntPrice}>
  464. <p class={styles.needPrice}>
  465. 支付金额:
  466. <span>
  467. ¥<i style="font-style: normal" id="needPrice"></i>
  468. </span>
  469. </p>
  470. <p class={styles.allPrice}>
  471. 总原价:
  472. <del>
  473. ¥<i style="font-style: normal" id="originalPrice"></i>
  474. </del>
  475. </p>
  476. </div>
  477. <div class={styles.paymentBtn}>
  478. <Button
  479. color="linear-gradient(135deg, #FF8C4A 0%, #FF531C 100%)"
  480. round
  481. onClick={onSubmit}
  482. >
  483. 立即购买
  484. </Button>
  485. </div>
  486. </div>
  487. </OSticky>
  488. </>
  489. )
  490. }
  491. })