order-detail.tsx 14 KB

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