order-detail.tsx 14 KB

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