order-detail.tsx 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  1. import OHeader from '@/components/o-header'
  2. import { computed, defineComponent, onMounted, reactive, ref } 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, showConfirmDialog, 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. export default defineComponent({
  18. name: 'order-detail',
  19. setup() {
  20. const route = useRoute()
  21. const router = useRouter()
  22. const state = reactive({
  23. orderTimer: null as any,
  24. paymentStatus: false,
  25. showQrcode: false,
  26. qrCodeUrl: '',
  27. pay_channel: '',
  28. orderNo: route.query.orderNo,
  29. orderInfo: {} as any, // 订单信息
  30. goodsInfos: [] as any, // 订单信息列表
  31. config: route.query.config ? JSON.parse(route.query.config as any) : {},
  32. hasFreight: route.query.hf ? false : true, // 是否显示
  33. freight: '', // 运费
  34. agreeStatus: true, //是否勾选协议
  35. showHeader: false,
  36. authShow: false // 是否进行实名认证
  37. })
  38. const orderType = computed(() => {
  39. return state.orderInfo.orderType
  40. })
  41. const addressDetails = ref<any>({})
  42. const getOrderDetails = async () => {
  43. try {
  44. const { data } = await request.get('/api-student/userPaymentOrder/detail/' + state.orderNo)
  45. const goodsInfos = data.goodsInfos || []
  46. state.orderInfo = data
  47. let hasInstrument = false // 是否有乐器
  48. let hasTextbook = false // 是否购买教材
  49. goodsInfos.forEach((item: any) => {
  50. const img = item.goodsUrl ? item.goodsUrl.split(',')[0] : ''
  51. item.goodsUrl = img
  52. if (item.goodsType === 'INSTRUMENTS') {
  53. hasInstrument = true
  54. } else if (item.goodsType === 'TEXTBOOK') {
  55. hasTextbook = true
  56. }
  57. })
  58. state.goodsInfos = goodsInfos
  59. if (!addressDetails.value.id) {
  60. addressDetails.value = data.addresses || {}
  61. }
  62. // 判断运费状态
  63. // 如果没有购买商品,有购买教材则『到付』 其它则免运费
  64. console.log(hasInstrument, hasTextbook)
  65. if (!hasInstrument && hasTextbook) {
  66. state.freight = '到付'
  67. } else {
  68. state.freight = '免运费'
  69. }
  70. // 判断订单状态,如果不是待支付则返回
  71. // WAIT_PAY: '待支付',
  72. // PAYING: '支付中',
  73. // PAID: '已付款',
  74. // TIMEOUT: '订单超时',
  75. // FAIL: '支付失败',
  76. // CLOSED: '订单关闭',
  77. // REFUNDING: '退款中',
  78. // REFUNDED: '已退款'
  79. if (data.status !== 'WAIT_PAY' && data.status !== 'PAYING') {
  80. // status
  81. showConfirmDialog({
  82. message: '订单处理中,请稍等',
  83. showCancelButton: false
  84. }).then(() => {
  85. router.replace({
  86. path: '/payment-result',
  87. query: {
  88. orderNo: state.orderNo
  89. }
  90. })
  91. })
  92. }
  93. } catch {
  94. //
  95. }
  96. }
  97. const onConfirm = (val: any) => {
  98. const config: any = state.config
  99. state.pay_channel = val.pay_channel
  100. if (val.payCode === 'payResult') {
  101. // router.push({
  102. // path: '/payResult',
  103. // query: {
  104. // pay_channel: val.pay_channel,
  105. // wxAppId: config.wxAppId,
  106. // body: config.body,
  107. // price: config.price,
  108. // orderNo: config.merOrderNo,
  109. // userId: config.userId
  110. // }
  111. // })
  112. window.location.href =
  113. window.location.origin +
  114. '/orchestra-student/#/payResult?' +
  115. qs.stringify({
  116. pay_channel: val.pay_channel,
  117. wxAppId: config.wxAppId,
  118. body: config.body,
  119. price: config.price,
  120. orderNo: config.merOrderNo,
  121. userId: config.userId
  122. })
  123. } else {
  124. state.qrCodeUrl =
  125. window.location.origin +
  126. '/orchestra-student/#/payDefine?pay_channel=' +
  127. val.pay_channel +
  128. '&wxAppId=' +
  129. config.wxAppId +
  130. '&body=' +
  131. config.body +
  132. '&price=' +
  133. config.price +
  134. '&orderNo=' +
  135. config.merOrderNo +
  136. '&userId=' +
  137. config.userId
  138. console.log(state.qrCodeUrl, 'qrCodeUrl')
  139. state.showQrcode = true
  140. state.paymentStatus = false
  141. setTimeout(() => {
  142. getPaymentOrderStatus()
  143. }, 300)
  144. }
  145. }
  146. // 轮询查询订单状态
  147. const getPaymentOrderStatus = async () => {
  148. // 循环查询订单
  149. // const orderNo = state.orderNo
  150. const orderTimer = setInterval(async () => {
  151. // 判断是否在当前路由,如果不是则清除定时器
  152. if (route.name != 'orderDetail') {
  153. clearInterval(orderTimer)
  154. return
  155. }
  156. state.orderTimer = orderTimer
  157. try {
  158. const { data } = await request.post(
  159. '/api-student/open/userOrder/paymentStatus/' + state.orderNo,
  160. {
  161. hideLoading: true
  162. }
  163. )
  164. // console.log(data)
  165. if (data.status !== 'WAIT_PAY' && data.status !== 'PAYING') {
  166. clearInterval(orderTimer)
  167. window.location.replace(
  168. window.location.origin +
  169. '/orchestra-student/#/payment-result?orderNo=' +
  170. state.orderNo
  171. )
  172. }
  173. } catch {
  174. //
  175. clearInterval(orderTimer)
  176. }
  177. }, 5000)
  178. }
  179. // const locationReplace = (url: any) => {
  180. // // 只允许同域名
  181. // console.log(history.replaceState, 'window.history.replaceState', url)
  182. // if (history.replaceState) {
  183. // history.replaceState(null, document.title, url)
  184. // history.go(0)
  185. // } else {
  186. // location.replace(url)
  187. // }
  188. // }
  189. const beforeSubmit = () => {
  190. const pt = state.pay_channel
  191. let payCode = 'qrCode'
  192. // 判断当前浏览器
  193. if (browser().weixin) {
  194. // 微信浏览器
  195. if (pt == 'alipay_qr' || pt == 'alipay_wap') {
  196. payCode = 'qrCode'
  197. } else if (pt == 'wx_pub') {
  198. payCode = 'pay'
  199. }
  200. } else if (browser().alipay) {
  201. // 支付宝浏览器
  202. if (pt == 'alipay_wap') {
  203. // 支付宝 H5 支付
  204. payCode = 'pay'
  205. } else {
  206. payCode = 'qrCode'
  207. }
  208. } else {
  209. payCode = 'qrCode'
  210. }
  211. onConfirm({
  212. payCode: payCode == 'qrCode' ? 'payDefine' : 'payResult',
  213. pay_channel: pt
  214. })
  215. }
  216. const onSubmit = async () => {
  217. clearInterval(state.orderTimer)
  218. if (orderType.value === 'VIP') {
  219. // onCallback()
  220. buyVip(onCallback)
  221. } else {
  222. buyOrchestra(onCallback)
  223. }
  224. }
  225. const onCallback = () => {
  226. const pt = state.pay_channel
  227. // 判断是否有支付方式
  228. if (pt) {
  229. beforeSubmit()
  230. } else {
  231. state.paymentStatus = true
  232. }
  233. }
  234. const buyVip = async (callback?: any) => {
  235. try {
  236. const { data } = await request.get('/api-student/userPaymentOrder/detail/' + state.orderNo)
  237. console.log(data)
  238. state.pay_channel = data.paymentChannel
  239. if (data.status === 'PAID') {
  240. showConfirmDialog({
  241. message: '该订单已支付成功',
  242. showCancelButton: false
  243. }).then(() => {
  244. router.replace({
  245. path: '/payment-result',
  246. query: {
  247. orderNo: state.orderNo
  248. }
  249. })
  250. })
  251. } else {
  252. callback && callback()
  253. }
  254. } catch {
  255. //
  256. }
  257. }
  258. const buyOrchestra = async (callback: any) => {
  259. // 请选择收货地址
  260. if (!addressDetails.value.id) {
  261. showToast('请选择收货地址')
  262. return
  263. }
  264. if (!state.agreeStatus) {
  265. showToast('请先阅读并同意《管乐团平台服务协议》')
  266. return
  267. }
  268. const users = baseState.user.data
  269. // 判断是否需要实名认证, 姓名,卡号
  270. if (!users?.account.realName || !users?.account.idCardNo) {
  271. state.authShow = true
  272. return
  273. }
  274. try {
  275. const { data } = await request.post('/api-student/userPaymentOrder/updateReceiveAddress', {
  276. data: {
  277. orderNo: state.orderNo,
  278. orderType: 'ORCHESTRA',
  279. receiveAddress: addressDetails.value.id
  280. }
  281. })
  282. console.log(data)
  283. state.pay_channel = data.paymentChannel
  284. if (data.status === 'PAID') {
  285. showConfirmDialog({
  286. message: '该订单已支付成功',
  287. showCancelButton: false
  288. }).then(() => {
  289. router.replace({
  290. path: '/payment-result',
  291. query: {
  292. orderNo: state.orderNo
  293. }
  294. })
  295. })
  296. } else {
  297. callback && callback()
  298. }
  299. } catch {
  300. //
  301. }
  302. }
  303. // 放弃支付时,则取消订单
  304. const onBackOut = async () => {
  305. try {
  306. await request.post('/api-student/userPaymentOrder/cancelPayment/' + state.orderNo)
  307. router.back()
  308. } catch {
  309. //
  310. }
  311. }
  312. // 实名认证成功
  313. const onAuthSuccess = () => {
  314. //
  315. state.authShow = false
  316. onSubmit() // 实名成功后自动支付
  317. }
  318. onMounted(() => {
  319. if (browser().isApp) {
  320. state.showHeader = true
  321. } else {
  322. state.showHeader = false
  323. }
  324. // 获取收货地址
  325. let details = sessionStorage.getItem('addressDetails')
  326. details = details ? JSON.parse(details) : {}
  327. addressDetails.value = details
  328. sessionStorage.removeItem('addressDetails')
  329. getOrderDetails()
  330. })
  331. return () => (
  332. <>
  333. {browser().isApp && <OHeader border={false} />}
  334. <div class={styles.cartConfirm}>
  335. {/* 只有乐团购买的时候显示收货地址 */}
  336. {orderType.value === 'ORCHESTRA' && (
  337. <div class={styles.cartConfirmBox}>
  338. <Addres item={addressDetails.value} />
  339. </div>
  340. )}
  341. <CellGroup style={{ margin: 0 }}>
  342. {state.goodsInfos &&
  343. state.goodsInfos.map((goods: any) => (
  344. <Cell class={styles.cellItem} center>
  345. {{
  346. icon: () => <Image class={styles.img} src={goods.goodsUrl} />,
  347. title: () => (
  348. <div class={styles.goodsContent}>
  349. <h2>{goods.goodsName}</h2>
  350. <Tag type="primary">{goods.brandName}</Tag>
  351. <p class={styles.goodsNum}>{goods.goodsType === 'VIP' ? '6个月' : 'x 1'}</p>
  352. </div>
  353. ),
  354. value: () => (
  355. <span class={styles.cellPrice}>
  356. {goods.currentPrice > 0 ? '¥' + moneyFormat(goods.currentPrice) : '赠送'}
  357. </span>
  358. )
  359. }}
  360. </Cell>
  361. ))}
  362. </CellGroup>
  363. {orderType.value === 'ORCHESTRA' && (
  364. <Cell class={styles.freight} title="运费" value={state.freight}></Cell>
  365. )}
  366. <div class={styles.protocol}>
  367. <OProtocol
  368. v-model:modelValue={state.agreeStatus}
  369. showHeader={state.showHeader}
  370. style={{ paddingLeft: 0, paddingRight: 0 }}
  371. />
  372. </div>
  373. </div>
  374. <OSticky position="bottom" background="white">
  375. <div class={styles.paymentContainer}>
  376. <div class={styles.payemntPrice}>
  377. <p class={styles.needPrice}>
  378. 共需支付:<span>¥{moneyFormat(state.orderInfo.currentPrice)}</span>
  379. </p>
  380. </div>
  381. <div class={styles.paymentBtn}>
  382. <Button
  383. color="linear-gradient(135deg, #FF8C4A 0%, #FF531C 100%)"
  384. round
  385. onClick={onSubmit}
  386. >
  387. 立即购买
  388. </Button>
  389. </div>
  390. </div>
  391. </OSticky>
  392. <Popup
  393. show={state.paymentStatus}
  394. closeOnClickOverlay={false}
  395. position="bottom"
  396. round
  397. closeOnPopstate
  398. safeAreaInsetBottom
  399. style={{ minHeight: '30%' }}
  400. >
  401. <Payment
  402. paymentConfig={state.orderInfo}
  403. onClose={() => (state.paymentStatus = false)}
  404. onBackOut={onBackOut}
  405. onConfirm={(val: any) => onConfirm(val)}
  406. />
  407. </Popup>
  408. <Popup
  409. v-model:show={state.showQrcode}
  410. style={{ background: 'transparent', overflow: 'initial' }}
  411. safeAreaInsetBottom={true}
  412. class={styles.popupCode}
  413. closeable
  414. onClose={() => {
  415. // 二维码关闭时清除订单器
  416. clearInterval(state.orderTimer)
  417. }}
  418. >
  419. <div class={styles.codeContainer}>
  420. {/* <i class={styles.codeClose} onClick={() => (state.showQrcode = false)}></i> */}
  421. <div class={styles.codeImg}>
  422. <div class={styles.codeContent}>
  423. <h2 class={styles.codeTitle}>
  424. {orderType.value === 'VIP' ? '开通会员' : '乐团报名'}
  425. </h2>
  426. <div class={styles.codeName}>
  427. 请截图下方二维码 登录{state.pay_channel === 'wx_pub' ? '微信' : '支付宝'}
  428. 扫码支付
  429. </div>
  430. <div class={styles.codeQr}>
  431. <OQrcode text={state.qrCodeUrl} size={'400'} />
  432. </div>
  433. <div style={{ textAlign: 'center' }}>
  434. <span class={styles.codeBtnText}>请在30分钟内扫码支付</span>
  435. </div>
  436. <div class={styles.codeTips}>
  437. <div class={styles.tipsTitle}>使用说明:</div>
  438. <div class={styles.tipsContent}>
  439. 1.长按二维码保存图片到相册(或截屏保存到相册)
  440. <br />
  441. 2.打开{state.pay_channel === 'wx_pub' ? '微信' : '支付宝'}扫一扫
  442. <br />
  443. 3.选择相册中的二维码
  444. <br />
  445. 4.请在30分钟内扫码支付
  446. </div>
  447. </div>
  448. </div>
  449. </div>
  450. </div>
  451. </Popup>
  452. <OPopup v-model:modelValue={state.authShow}>
  453. <UserAuth onSuccess={onAuthSuccess} hideHeader={!browser().isApp} />
  454. </OPopup>
  455. </>
  456. )
  457. }
  458. })