index.tsx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. import ColProtocol from '@/components/col-protocol'
  2. import { Button, Dialog, Icon, Popup, Sticky, Toast } from 'vant'
  3. import ColPopup from '@/components/col-popup'
  4. import { defineComponent } from 'vue'
  5. import { postMessage } from '@/helpers/native-message'
  6. import styles from './index.module.less'
  7. import UserAuth from './userAuth'
  8. import request from '@/helpers/request'
  9. // 调用原生支付
  10. // postMessage({ api: 'paymentOrder', content: { orderNo: 0 } })
  11. // listenerMessage({ api: 'paymentResult', callback: (res: any) => {
  12. // status: 'success | fail'
  13. // }})
  14. import iconTips from '@common/images/icon_tips.png'
  15. import Payment from './payment'
  16. import ColHeader from '@/components/col-header'
  17. import { state } from '@/state'
  18. import { orderInfos, orderStatus, resestState } from './orderStatus'
  19. import OrderVideo from './order-video'
  20. import OrderLive from './order-live'
  21. import OrderPractice from './order-practice'
  22. import OrderVip from './order-vip'
  23. import OrderMusic from './order-music'
  24. import { moneyFormat } from '@/helpers/utils'
  25. import OrderPinao from './order-pinao'
  26. import { getMusicDetail } from '@/student/trade/tradeOrder'
  27. import OrderActive from './order-active'
  28. import UseCoupon from './use-coupons'
  29. export default defineComponent({
  30. name: 'order-detail',
  31. data() {
  32. const query = this.$route.query
  33. return {
  34. loading: false, // 是否加载中,为了处理0元订单()
  35. orderType: query.orderType as string,
  36. recomUserId: query.recomUserId, // 推荐人id
  37. activityId: query.activityId, // 活动编号
  38. id: query.id,
  39. agreeStatus: false,
  40. popupShow: false,
  41. paymentStatus: false,
  42. orderAmount: 0, // 订单金额,用于使用优惠券,余额,优惠等
  43. orderPrice: 0, // 支付金额,最后支付金额
  44. dataLoading: true,
  45. exists: false // 是否签署过用户注册协议
  46. }
  47. },
  48. unmounted() {
  49. // 销毁时解绑监听
  50. orderStatus.orderInfo = {
  51. orderNo: '',
  52. actualPrice: 0,
  53. payStatus: false
  54. }
  55. },
  56. computed: {
  57. orderList() {
  58. // 商品列表
  59. const orderObject = orderStatus.orderObject
  60. return orderObject.orderList || []
  61. }
  62. },
  63. async mounted() {
  64. await this.getUserRegisterProtocol()
  65. // 判断是否是曲目购买(只有智能陪练才会有入口),其它地方不会有入口
  66. this.dataLoading = true
  67. if (this.orderType == 'MUSIC' && this.id) {
  68. try {
  69. const item = await getMusicDetail(this.id)
  70. orderStatus.orderObject.orderType = 'MUSIC'
  71. orderStatus.orderObject.orderName = item.musicSheetName
  72. orderStatus.orderObject.orderDesc = item.musicSheetName
  73. orderStatus.orderObject.actualPrice = item.musicPrice
  74. orderStatus.orderObject.recomUserId = this.recomUserId
  75. orderStatus.orderObject.activityId = this.activityId
  76. // 判断当前订单是否在支付中
  77. if (['WAIT_PAY', 'PAYING'].includes(item.orderStatus)) {
  78. orderStatus.orderObject.orderNo = item.orderNo
  79. } else if (['PAID', 'CLOSE', 'FAIL'].includes(item.orderStatus)) {
  80. // 判断订单是否是其它状态
  81. Toast('订单有误')
  82. postMessage({ api: 'back', content: {} })
  83. }
  84. orderStatus.orderObject.orderList = [
  85. {
  86. orderType: 'MUSIC',
  87. goodsName: item.musicSheetName,
  88. actualPrice: item.musicPrice,
  89. ...item
  90. }
  91. ]
  92. } catch {
  93. //
  94. }
  95. }
  96. this.orderAmount = orderStatus.orderObject.actualPrice || 0
  97. this.orderPrice = orderStatus.orderObject.actualPrice || 0
  98. this.dataLoading = false
  99. // 0元支付特别处理
  100. if (this.orderPrice === 0 && orderStatus.orderObject.orderType) {
  101. this.loading = true
  102. this.onSubmit()
  103. }
  104. },
  105. methods: {
  106. async getUserRegisterProtocol() {
  107. // 获取是否签署过《用户注册协议》
  108. try {
  109. const res = await request.get(
  110. state.platformApi + '/sysUserContractRecord/checkContractSign',
  111. {
  112. params: {
  113. contractType: 'REGISTER'
  114. }
  115. }
  116. )
  117. this.exists = res.data
  118. } catch {
  119. //
  120. }
  121. },
  122. async onAuthSuccess() {
  123. this.popupShow = false
  124. await this.getUserRegisterProtocol()
  125. this.onSubmit() // 实名成功后自动支付
  126. },
  127. async onSubmit() {
  128. // console.log(this.orderInfos)
  129. if (this.orderPrice > 0) {
  130. if (!this.agreeStatus) {
  131. Toast('请先阅读并同意《酷乐秀平台服务协议》')
  132. return
  133. }
  134. const users = state.user.data
  135. // 判断是否需要实名认证, 姓名,卡号,是否签协议
  136. if (!users?.realName || !users?.idCardNo || !this.exists) {
  137. this.popupShow = true
  138. return
  139. }
  140. }
  141. // 判断是否有订单号
  142. if (orderStatus.orderObject.orderNo) {
  143. this.paymentStatus = true
  144. return
  145. }
  146. // 正常支付
  147. try {
  148. const orderObject = orderStatus.orderObject
  149. const url =
  150. state.platformType === 'TEACHER'
  151. ? '/api-teacher/userOrder/executeOrder'
  152. : '/api-student/userOrder/executeOrder'
  153. const res = await request.post(url, {
  154. data: {
  155. orderName: orderObject.orderName,
  156. orderDesc: orderObject.orderDesc,
  157. orderType: orderObject.orderType,
  158. actualPrice: this.orderPrice || 0,
  159. recomUserId: orderObject.recomUserId,
  160. activityId: orderObject.activityId,
  161. couponId: orderObject.couponId,
  162. orderInfos: [...orderInfos()]
  163. }
  164. })
  165. const result = res.data || {}
  166. // 支付成功
  167. if (result.status == 'PAID') {
  168. this.$router.replace({
  169. path: '/tradeDetail',
  170. query: {
  171. orderNo: result.orderNo
  172. }
  173. })
  174. return
  175. }
  176. // 拉起支付方式
  177. orderStatus.orderObject.orderNo = result.orderNo
  178. orderStatus.orderObject.actualPrice = result.actualPrice
  179. this.paymentStatus = true
  180. } catch {
  181. this.loading = false
  182. if (this.orderPrice === 0) {
  183. Dialog.alert({
  184. title: '提示',
  185. message: '支付失败,请稍后重试!',
  186. confirmButtonText: '确定',
  187. confirmButtonColor: '#2dc7aa'
  188. })
  189. }
  190. }
  191. },
  192. onBackOut() {
  193. // 关闭订单后需要重置数据
  194. resestState()
  195. },
  196. onCouponSelect(item: any) {
  197. console.log('onCouponSelect', item)
  198. let discountCount = 0
  199. ;(item || []).forEach((item: any) => {
  200. discountCount += Number(item.discountPrice)
  201. })
  202. const lastAmount = Number(
  203. (Number(this.orderAmount) - Number(discountCount)).toFixed(2)
  204. )
  205. this.orderPrice = lastAmount >= 0 ? lastAmount : 0
  206. // 设置优惠券编号
  207. const couponIds = (item || []).map((item: any) => {
  208. return item.couponIssueId
  209. })
  210. orderStatus.orderObject.couponId = couponIds.join(',') || ''
  211. }
  212. },
  213. render() {
  214. return (
  215. <div class={styles['order-detail']}>
  216. <ColHeader />
  217. {!this.loading && (
  218. <>
  219. {this.orderList.map((item: any) => {
  220. if (item.orderType === 'VIDEO') {
  221. return <OrderVideo item={item} />
  222. } else if (item.orderType === 'LIVE') {
  223. return <OrderLive item={item} />
  224. } else if (item.orderType === 'PRACTICE') {
  225. return <OrderPractice item={item} />
  226. } else if (item.orderType === 'VIP') {
  227. return <OrderVip item={item} />
  228. } else if (item.orderType === 'MUSIC') {
  229. return <OrderMusic item={item} />
  230. } else if (item.orderType === 'PINAO_ROOM') {
  231. return <OrderPinao item={item} />
  232. } else if (item.orderType === 'ACTI_REGIST') {
  233. return <OrderActive item={item} />
  234. }
  235. })}
  236. {/* 优惠券使用 */}
  237. {!this.dataLoading && (
  238. <UseCoupon
  239. discountPrice={orderStatus.orderObject.discountPrice}
  240. orderType={this.orderType}
  241. orderAmount={this.orderAmount}
  242. onCouponSelect={this.onCouponSelect}
  243. disabled={orderStatus.orderObject.orderNo ? true : false}
  244. />
  245. )}
  246. {/* <div class={styles.tips}>
  247. <h3>
  248. <Icon name={iconTips} size={15} />
  249. 温馨提示
  250. </h3>
  251. {state.platformType === 'TEACHER' ? (
  252. <p>
  253. 1、琴房时长为虚拟商品,一经购买不予退费; <br />
  254. 2、琴房时长消耗以时长扣减逻辑为准。
  255. </p>
  256. ) : (
  257. <p>
  258. 1、您支付的课程费用将由平台收取; <br />
  259. 2、陪练课、直播课课程结束后,平台将单课时费用向老师结算;
  260. <br />
  261. 3、除直播课未达到开课人数外,其他服务一经购买不予退费。
  262. </p>
  263. )}
  264. </div> */}
  265. <div class={styles.paymentInfo}>
  266. {this.orderPrice > 0 && (
  267. <div class={styles.protocol}>
  268. <ColProtocol
  269. v-model={this.agreeStatus}
  270. showHeader
  271. style={{ paddingLeft: 0, paddingRight: 0 }}
  272. />
  273. </div>
  274. )}
  275. <div class={styles.btnGroup}>
  276. <div class={styles.priceSection}>
  277. 支付金额:
  278. <div class={styles.price}>
  279. <span class={styles.priceUnit}>¥</span>
  280. <span class={styles.priceNum}>
  281. {moneyFormat(this.orderPrice)}
  282. </span>
  283. </div>
  284. </div>
  285. <Button
  286. type="primary"
  287. round
  288. class={styles.btn}
  289. onClick={this.onSubmit}
  290. >
  291. 立即支付
  292. </Button>
  293. </div>
  294. </div>
  295. </>
  296. )}
  297. <ColPopup v-model={this.popupShow}>
  298. <UserAuth exists={this.exists} onSuccess={this.onAuthSuccess} />
  299. </ColPopup>
  300. <Popup
  301. show={this.paymentStatus}
  302. closeOnClickOverlay={false}
  303. position="bottom"
  304. round
  305. closeOnPopstate
  306. safeAreaInsetBottom
  307. style={{ minHeight: '30%' }}
  308. >
  309. <Payment
  310. v-model={this.paymentStatus}
  311. orderInfo={orderStatus.orderObject}
  312. onBackOut={this.onBackOut}
  313. />
  314. </Popup>
  315. </div>
  316. )
  317. }
  318. })