order-detail.tsx 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  1. import OHeader from '@/components/o-header'
  2. import { computed, defineComponent, onMounted, reactive } from 'vue'
  3. import styles from './order-detail.module.less'
  4. // import Addres from './component/addres'
  5. import OSticky from '@/components/o-sticky'
  6. import { Button, Cell, CellGroup, Image, Popup, showToast, Tag } from 'vant'
  7. import Payment from '@/views/adapay/payment'
  8. import { useRoute, useRouter } from 'vue-router'
  9. // import OQrcode from '@/components/o-qrcode'
  10. import request from '@/helpers/request'
  11. import { state as baseState } from '@/state'
  12. import { browser, moneyFormat } from '@/helpers/utils'
  13. import OProtocol from '@/components/o-protocol'
  14. import OPopup from '@/components/o-popup'
  15. import UserAuth from './component/user-auth'
  16. import qs from 'query-string'
  17. import MemberBao from '../member-bao'
  18. import GoodsDetail from '../goods-detail'
  19. import ODialog from '@/components/o-dialog'
  20. import { orderStatus } from '@/constant'
  21. import QrcodePayment from './qrcode-payment'
  22. import { beforeSubmit } from './order-state'
  23. /**
  24. * 接入jsdk
  25. * 乐团报名-原生js支付
  26. * 会员购买-汇付
  27. */
  28. export default defineComponent({
  29. name: 'order-detail',
  30. setup() {
  31. const route = useRoute()
  32. const router = useRouter()
  33. const state = reactive({
  34. paymentType: 'adapay' as 'wxpay' | 'adapay', // 支付方式
  35. orderTimer: null as any,
  36. paymentStatus: false,
  37. showQrcode: false,
  38. qrCodeUrl: '',
  39. pay_channel: '',
  40. orderNo: route.query.orderNo,
  41. orderInfo: {} as any, // 订单信息
  42. goodsInfos: [] as any, // 订单信息列表
  43. config: route.query.config ? JSON.parse(route.query.config as any) : {},
  44. hasFreight: route.query.hf ? false : true, // 是否显示
  45. freight: '', // 运费
  46. agreeStatus: true, //是否勾选协议
  47. showHeader: false,
  48. authShow: false, // 是否进行实名认证
  49. memberBaoStatus: false, // 团练宝详情状态
  50. goodsStatus: false, //
  51. selectGoodsId: null as any,
  52. currentPrice: 0,
  53. dialogStatus: false,
  54. dialogMessage: '',
  55. submitStatus: false
  56. })
  57. const orderType = computed(() => {
  58. return state.orderInfo.orderType
  59. })
  60. // const addressDetails = ref<any>({})
  61. const getOrderDetails = async () => {
  62. try {
  63. const { data } = await request.get('/api-student/userPaymentOrder/detail/' + state.orderNo)
  64. const goodsInfos = data.goodsInfos || []
  65. state.orderInfo = data
  66. let hasInstrument = false // 是否有乐器
  67. let hasTextbook = false // 是否购买教材
  68. goodsInfos.forEach((item: any) => {
  69. const img = item.goodsUrl ? item.goodsUrl.split(',')[0] : ''
  70. item.goodsUrl = img
  71. if (item.goodsType === 'INSTRUMENTS') {
  72. hasInstrument = true
  73. } else if (item.goodsType === 'TEXTBOOK') {
  74. hasTextbook = true
  75. }
  76. })
  77. state.goodsInfos = goodsInfos
  78. // if (!addressDetails.value.id) {
  79. // addressDetails.value = data.addresses || {}
  80. // }
  81. // 判断运费状态
  82. // 如果没有购买商品,有购买教材则『到付』 其它则免运费
  83. // console.log(hasInstrument, hasTextbook)
  84. if (!hasInstrument && hasTextbook) {
  85. state.freight = '到付'
  86. } else {
  87. state.freight = '免运费'
  88. }
  89. // 判断订单状态,如果不是待支付则返回
  90. // WAIT_PAY: '待支付',
  91. // PAYING: '支付中',
  92. // PAID: '已付款',
  93. // TIMEOUT: '订单超时',
  94. // FAIL: '支付失败',
  95. // CLOSED: '订单关闭',
  96. // REFUNDING: '退款中',
  97. // REFUNDED: '已退款'
  98. if (data.status !== 'WAIT_PAY' && data.status !== 'PAYING') {
  99. // status
  100. state.dialogStatus = true
  101. state.dialogMessage = '订单' + orderStatus[data.status]
  102. }
  103. } catch {
  104. //
  105. }
  106. }
  107. const onConfirm = (val: any) => {
  108. const config: any = state.config
  109. state.pay_channel = val.pay_channel
  110. const params = qs.stringify({
  111. pay_channel: val.pay_channel,
  112. wxAppId: config.wxAppId,
  113. alipayAppId: config.alipayAppId,
  114. body: config.body,
  115. price: config.price,
  116. paymentType: config.paymentType,
  117. orderNo: config.merOrderNo,
  118. userId: config.userId
  119. })
  120. if (val.payCode === 'payResult') {
  121. window.location.href = window.location.origin + '/orchestra-student/#/payResult?' + params
  122. } else {
  123. state.qrCodeUrl = window.location.origin + '/orchestra-student/#/payDefine?' + params
  124. state.showQrcode = true
  125. state.paymentStatus = false
  126. setTimeout(() => {
  127. getPaymentOrderStatus()
  128. }, 300)
  129. }
  130. }
  131. // 轮询查询订单状态
  132. const getPaymentOrderStatus = async () => {
  133. // 循环查询订单
  134. // const orderNo = state.orderNo
  135. const orderTimer = setInterval(async () => {
  136. // 判断是否在当前路由,如果不是则清除定时器
  137. if (route.name != 'orderDetail') {
  138. clearInterval(orderTimer)
  139. return
  140. }
  141. state.orderTimer = orderTimer
  142. try {
  143. const { data } = await request.post(
  144. '/api-student/open/userOrder/paymentStatus/' + state.orderNo,
  145. {
  146. hideLoading: true
  147. }
  148. )
  149. // console.log(data)
  150. if (data.status !== 'WAIT_PAY' && data.status !== 'PAYING') {
  151. // 默认关闭支付二维码弹窗
  152. state.showQrcode = false
  153. clearInterval(state.orderTimer)
  154. // window.location.replace(
  155. // window.location.origin + '/#/payment-result?orderNo=' + state.orderNo
  156. // )
  157. setTimeout(() => {
  158. checkOrderTypeJump()
  159. }, 100)
  160. }
  161. } catch {
  162. //
  163. clearInterval(state.orderTimer)
  164. }
  165. }, 5000)
  166. }
  167. // 确定支付
  168. const onSubmit = async () => {
  169. clearInterval(state.orderTimer)
  170. if (orderType.value === 'VIP') {
  171. buyVip(onCallback)
  172. } else {
  173. buyOrchestra(onCallback)
  174. }
  175. }
  176. /**
  177. * @description 回调,判断是否有支付渠道,如果有则直接去支付
  178. * @returns void
  179. */
  180. const onCallback = () => {
  181. const pt = state.pay_channel
  182. // 判断是否有支付方式
  183. if (pt) {
  184. const payCode: string = beforeSubmit(state.pay_channel)
  185. onConfirm({
  186. payCode,
  187. pay_channel: pt
  188. })
  189. } else {
  190. if (orderType.value === 'VIP') {
  191. state.paymentStatus = true
  192. } else {
  193. if (browser().isApp) {
  194. state.paymentStatus = true
  195. } else {
  196. // 直接去拉取微信支付
  197. onConfirm({
  198. payCode: 'payResult',
  199. pay_channel: 'wx_pub'
  200. })
  201. }
  202. }
  203. }
  204. }
  205. /**
  206. * @description 会员购买
  207. * @param callback 回调方式
  208. */
  209. const buyVip = async (callback?: any) => {
  210. try {
  211. state.submitStatus = true
  212. const { data } = await request.get(
  213. '/api-student/userPaymentOrder/detail/' + state.orderNo,
  214. {
  215. hideLoading: false
  216. }
  217. )
  218. state.pay_channel = data.paymentChannel
  219. state.submitStatus = false
  220. if (data.status !== 'WAIT_PAY' && data.status !== 'PAYING') {
  221. router.replace({
  222. path: '/payment-result',
  223. query: {
  224. orderNo: state.orderNo
  225. }
  226. })
  227. } else {
  228. callback && callback()
  229. }
  230. } catch {
  231. //
  232. state.submitStatus = false
  233. }
  234. }
  235. /**
  236. * @description 乐团报名 - 汇付
  237. * @param callback 回调方式
  238. */
  239. const buyOrchestra = async (callback: any) => {
  240. // 请选择收货地址
  241. // if (!addressDetails.value.id) {
  242. // showToast('请选择收货地址')
  243. // return
  244. // }
  245. if (!state.agreeStatus) {
  246. showToast('请先阅读并同意《管乐团平台服务协议》')
  247. return
  248. }
  249. const users = baseState.user.data
  250. // console.group(users)
  251. // 判断是否需要实名认证, 姓名,卡号
  252. if (!users?.account.realName || !users?.account.idCardNo) {
  253. state.authShow = true
  254. return
  255. }
  256. state.submitStatus = true
  257. try {
  258. const { data } = await request.post('/api-student/userPaymentOrder/updateReceiveAddress', {
  259. hideLoading: false,
  260. data: {
  261. orderNo: state.orderNo,
  262. orderType: 'ORCHESTRA',
  263. receiveAddress: null
  264. }
  265. })
  266. // console.log(data)
  267. if (data.status !== 'WAIT_PAY' && data.status !== 'PAYING') {
  268. checkOrderTypeJump()
  269. } else {
  270. callback && callback()
  271. }
  272. } catch {
  273. //
  274. } finally {
  275. state.submitStatus = false
  276. }
  277. }
  278. // 有支付结果后回调
  279. const checkOrderTypeJump = () => {
  280. // 判断是否是乐团报名 并且在不在app里面
  281. if (orderType.value === 'ORCHESTRA' && !browser().isApp) {
  282. window.location.replace(
  283. 'https://mp.weixin.qq.com/s?__biz=MzkxMDMwOTI5Nw==&mid=2247485362&idx=3&sn=9b265d36b5dabe7f9393fc679c367540&chksm=c12c256cf65bac7ae2a865435b950f6e1285afd226356db0ffde815b1ee345f29cfcdb798cc9#rd'
  284. )
  285. } else {
  286. router.replace({
  287. path: '/payment-result',
  288. query: {
  289. orderNo: state.orderNo
  290. }
  291. })
  292. }
  293. }
  294. // 放弃支付时,则取消订单
  295. const onBackOut = async () => {
  296. try {
  297. await request.post('/api-student/userPaymentOrder/cancelPayment/' + state.orderNo)
  298. router.back()
  299. } catch {
  300. //
  301. }
  302. }
  303. // 实名认证成功
  304. const onAuthSuccess = () => {
  305. //
  306. state.authShow = false
  307. onSubmit() // 实名成功后自动支付
  308. }
  309. onMounted(() => {
  310. if (browser().isApp) {
  311. state.showHeader = true
  312. } else {
  313. state.showHeader = false
  314. }
  315. // 获取收货地址
  316. // let details = sessionStorage.getItem('addressDetails')
  317. // details = details ? JSON.parse(details) : {}
  318. // addressDetails.value = details
  319. // sessionStorage.removeItem('addressDetails')
  320. getOrderDetails()
  321. })
  322. return () => (
  323. <>
  324. {browser().isApp && <OHeader border={false} />}
  325. <div class={styles.cartConfirm}>
  326. {/* 只有乐团购买的时候显示收货地址 */}
  327. {/* {orderType.value === 'ORCHESTRA' && (
  328. <div class={styles.cartConfirmBox}>
  329. <Addres item={addressDetails.value} />
  330. </div>
  331. )} */}
  332. <CellGroup style={{ margin: 0 }} border={false}>
  333. {state.goodsInfos &&
  334. state.goodsInfos.map((goods: any) => (
  335. <Cell
  336. class={styles.cellItem}
  337. // center
  338. onClick={() => {
  339. if (goods.goodsType === 'INSTRUMENTS' || goods.goodsType === 'TEXTBOOK') {
  340. // console.log(goods)
  341. state.selectGoodsId = goods.goodsId
  342. state.currentPrice = goods.currentPrice
  343. state.goodsStatus = true
  344. } else if (goods.goodsType === 'VIP') {
  345. state.memberBaoStatus = true
  346. }
  347. }}
  348. >
  349. {{
  350. icon: () => <Image class={styles.img} src={goods.goodsUrl} />,
  351. title: () => (
  352. <div class={styles.goodsContent}>
  353. <h2>
  354. <span>{goods.goodsName}</span>
  355. <span class={styles.goodsNum}>
  356. {goods.goodsType === 'VIP' ? '6个月' : 'x 1'}
  357. </span>
  358. </h2>
  359. <div class={styles.goodsPrice}>
  360. {/* {goods.goodsType === 'INSTRUMENT_INSPECT' ? (
  361. <span>1-2次/学期</span>
  362. ) : (
  363. <Tag
  364. color="linear-gradient(135deg, #FF8C4A 0%, #FF531C 100%)"
  365. textColor="#fff"
  366. class={styles.brandName}
  367. >
  368. {goods.brandName}
  369. </Tag>
  370. )} */}
  371. {goods.goodsType === 'INSTRUMENT_INSPECT' ? (
  372. <span class={[styles.model, 'van-multi-ellipsis--l2']}>1-2次/学期</span>
  373. ) : (
  374. <span class={[styles.model, 'van-multi-ellipsis--l2']}>
  375. {goods.description}
  376. </span>
  377. )}
  378. <span
  379. class={[
  380. styles.goodsNums,
  381. goods.paymentCashAmount > 0 ? styles.numFont : styles.free
  382. ]}
  383. >
  384. {goods.paymentCashAmount > 0 ? (
  385. <>
  386. <span class={styles.numPrefix}>¥ </span>
  387. {moneyFormat(goods.paymentCashAmount)}
  388. </>
  389. ) : state.orderInfo.orchestraRegisterType === 'GROUP_BUY' &&
  390. goods.goodsType === 'INSTRUMENTS' ? (
  391. '免费提供'
  392. ) : (
  393. '免费'
  394. )}
  395. </span>
  396. </div>
  397. {/* <p class={styles.model}>{goods.description}</p> */}
  398. </div>
  399. )
  400. }}
  401. </Cell>
  402. ))}
  403. </CellGroup>
  404. {orderType.value === 'ORCHESTRA' && (
  405. <Cell class={styles.freight} title="运费" value={state.freight}></Cell>
  406. )}
  407. </div>
  408. <OSticky position="bottom" background="white-only">
  409. <div class={styles.protocol}>
  410. <OProtocol
  411. v-model:modelValue={state.agreeStatus}
  412. showHeader={state.showHeader}
  413. style={{ paddingTop: 0, paddingBottom: 0 }}
  414. />
  415. </div>
  416. <div class={styles.paymentContainer}>
  417. <div class={styles.payemntPrice}>
  418. <p class={styles.needPrice}>
  419. 支付金额:<span>¥ {moneyFormat(state.orderInfo.currentPrice)}</span>
  420. </p>
  421. </div>
  422. <div class={styles.paymentBtn}>
  423. <Button
  424. color="linear-gradient(135deg, #FF8C4A 0%, #FF531C 100%)"
  425. round
  426. onClick={onSubmit}
  427. loading={state.submitStatus}
  428. disabled={state.submitStatus}
  429. >
  430. 立即购买
  431. </Button>
  432. </div>
  433. </div>
  434. </OSticky>
  435. <Popup
  436. show={state.paymentStatus}
  437. closeOnClickOverlay={false}
  438. position="bottom"
  439. round
  440. closeOnPopstate
  441. safeAreaInsetBottom
  442. style={{ minHeight: '30%' }}
  443. >
  444. <Payment
  445. paymentConfig={state.orderInfo}
  446. onClose={() => (state.paymentStatus = false)}
  447. onBackOut={onBackOut}
  448. onConfirm={(val: any) => onConfirm(val)}
  449. />
  450. </Popup>
  451. <OPopup
  452. v-model:modelValue={state.showQrcode}
  453. onClose={() => {
  454. // 二维码关闭时清除定时器
  455. clearInterval(state.orderTimer)
  456. }}
  457. >
  458. <QrcodePayment
  459. url={state.qrCodeUrl}
  460. pay_channel={state.pay_channel}
  461. orderType={orderType.value}
  462. />
  463. </OPopup>
  464. <OPopup v-model:modelValue={state.authShow}>
  465. <UserAuth onSuccess={onAuthSuccess} hideHeader={!browser().isApp} />
  466. </OPopup>
  467. <OPopup v-model:modelValue={state.memberBaoStatus} position="right">
  468. <MemberBao />
  469. </OPopup>
  470. <OPopup v-model:modelValue={state.goodsStatus} position="right" destroy>
  471. {state.goodsStatus && (
  472. <GoodsDetail id={state.selectGoodsId} groupPrice={state.currentPrice} />
  473. )}
  474. </OPopup>
  475. <ODialog
  476. title="提示"
  477. v-model:show={state.dialogStatus}
  478. message={state.dialogMessage}
  479. confirmButtonText="确定"
  480. onConfirm={() => {
  481. checkOrderTypeJump()
  482. }}
  483. />
  484. </>
  485. )
  486. }
  487. })