order-detail.tsx 13 KB

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