initPay.vue 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. <!-- 支付组件 -->
  2. <template>
  3. <div v-if="payType && payQrCodeTxt" class="code pa-5 resume-box">
  4. <div class="resume-header">
  5. <div class="resume-title">扫码支付</div>
  6. </div>
  7. <div class="d-flex align-end mt-3">
  8. <div class="code-left">
  9. <QrCode :text="payQrCodeTxt" :width="170" />
  10. </div>
  11. <div class="code-right ml-5">
  12. <div class="price">
  13. <span class="font-size-13">¥</span>
  14. {{ payCalculation(props.info?.payPrice || 0, 'realPay') }}
  15. </div>
  16. <div class="mt-3 d-flex align-center">
  17. <span class="color-666 font-weight-bold mr-5">支付方式</span>
  18. <!-- <v-chip-group v-model="payment" selected-class="text-primary" mandatory>
  19. <v-chip filter v-for="k in paymentList" :key="k.value" :value="k.value" class="mr-3" label>
  20. {{ k.label }}
  21. <svg-icon class="ml-1" :name="k.icon" :size="k.size"></svg-icon>
  22. </v-chip>
  23. </v-chip-group> -->
  24. <v-chip-group v-model="payType" selected-class="text-primary" column mandatory @update:modelValue="payTypeChange">
  25. <v-chip filter v-for="k in payTypeList" :key="k.code" :value="k.code" class="mr-3" label>
  26. {{ k.name }}
  27. <svg-icon v-if="k.icon" class="ml-1" :name="k.icon" :size="k.size"></svg-icon>
  28. </v-chip>
  29. </v-chip-group>
  30. </div>
  31. <div class="font-size-14 color-666 mt-3 cursor-pointer">
  32. 服务协议
  33. <span class="septal-line"></span>
  34. 充值协议
  35. </div>
  36. </div>
  37. </div>
  38. <div
  39. class="mt-5 ml-2"
  40. style="color: var(--v-error-base);"
  41. >
  42. 扫码支付时请勿离开
  43. <span>{{ countdownShow/1000 + 's' }}</span>
  44. </div>
  45. </div>
  46. </template>
  47. <script setup>
  48. defineOptions({name: 'personalRecharge-initPay'})
  49. import QrCode from '@/components/QrCode'
  50. import { payCalculation } from '@/utils/position'
  51. import { definePayTypeList, qrCodePay, walletPay } from '@/utils/payType'
  52. import { getEnableCodeList, payOrderSubmit, getOrderPayStatus } from '@/api/common'
  53. import { setWalletRecharge } from '@/api/recruit/personal/myWallet.js'
  54. import { onUnmounted, ref, nextTick, watch } from 'vue'
  55. const emit = defineEmits(['payTypeChange', 'paySuccess', 'stopInterval'])
  56. const props = defineProps({
  57. info: {
  58. type: Object,
  59. default: () => ({ id: ''})
  60. },
  61. appId: {
  62. type: Number,
  63. default: 11 // 10为一般情况下支付,11为充值支付
  64. },
  65. })
  66. // 1.支付方式
  67. // 2.发起充值(创建钱包充值记录)
  68. // 2.发起充值(创建钱包充值记录)
  69. // 3.如果是二维码类型支付(isQrCodePay=true)生成二维码(需要绑定支付订单的订单号)
  70. // 4.轮询用户是否支付成功
  71. // 更新账户余额信息
  72. import { useUserStore } from '@/store/user'; const store = useUserStore()
  73. const updateAccountInfo = async () => {
  74. await store.getUserAccountBalance()
  75. loading.value = false
  76. }
  77. import Snackbar from '@/plugins/snackbar'
  78. import { useRoute } from 'vue-router'; const route = useRoute()
  79. import { useRouter } from 'vue-router'; const router = useRouter()
  80. const payStatusTimer = ref(null) // 支付状态轮询
  81. const payStatus = async () => {
  82. // if (payStatusTimer.value) clearInterval(payStatusTimer.value); payStatusTimer.value = null
  83. // setTimeout(() => { // 测试代码
  84. // }, 2000)
  85. try {
  86. const data = await getOrderPayStatus({ id: (props.appId - 0) === 11 ? payOrder.value.payOrderId : payOrder.value.id })
  87. if ((data?.status - 0) === 10) {
  88. // 支付成功
  89. if (payStatusTimer.value) clearInterval(payStatusTimer.value); payStatusTimer.value = null
  90. setTimeout(() => {
  91. // 更新点数(充值、发布职位)
  92. updateAccountInfo()
  93. // 支付成功
  94. emit('paySuccess')
  95. // 返回指定页面
  96. if (route.fullPath === props.returnUrl) router.go(0)
  97. else if (props.returnUrl) router.push(props.returnUrl)
  98. Snackbar.success('支付成功')
  99. }, 2000);
  100. }
  101. } catch (error) {
  102. console.log(error)
  103. }
  104. }
  105. const payQrCodeTxt = ref('')
  106. // 如果是点数支付的话走完payOrderSubmit之后即扣点数,如果是二维码支付的话只是生成二维码,这一步以后是轮询是否支付成功
  107. const paySubmit = async () => {
  108. if (!payType.value) return
  109. if (!payOrder.value?.payOrderId) return
  110. try {
  111. if (payOrder.value) {
  112. // 提交支付订单
  113. const params = {
  114. id: payOrder.value.payOrderId, // 支付单编号
  115. channelCode: payType.value, // 支付渠道
  116. }
  117. const res = await payOrderSubmit(params)
  118. payQrCodeTxt.value = res?.displayContent || '二维码生成失败,请刷新再试。' // 生成二维码内容
  119. if (payStatusTimer.value) clearInterval(payStatusTimer.value); payStatusTimer.value = null
  120. payStatusTimer.value = setInterval(() => { payStatus() }, 2000) // 轮巡查询用户是否支付
  121. stopIntervalFun()
  122. }
  123. } catch (error) {
  124. console.log(error)
  125. }
  126. }
  127. // 2.发起充值
  128. const loading = ref(true)
  129. const payOrder = ref({})
  130. const getUnpaidOrderList = async () => {
  131. try {
  132. //* 充值
  133. if (props.info.payPrice === undefined && props.info.packageId === undefined) return
  134. const params = {
  135. payPrice: (props.info.payPrice-0),
  136. packageId: props.info.id,
  137. }
  138. const data = await setWalletRecharge(params)
  139. payOrder.value = data || {}
  140. if (isQrCodePay.value) paySubmit()
  141. } catch (error) {
  142. console.log(error)
  143. } finally {
  144. nextTick(() => {
  145. loading.value = false
  146. })
  147. }
  148. }
  149. // 1.支付方式
  150. const isWalletPay = ref(false)
  151. const isQrCodePay = ref(false)
  152. const tip = ref('')
  153. const payTypeChange = (value) => {
  154. payType.value = value
  155. tip.value = payTypeList.value.find(e => e.code === payType.value)?.tip || ''
  156. isQrCodePay.value = qrCodePay.includes(payType.value)
  157. isWalletPay.value = walletPay.includes(payType.value)
  158. paySubmit()
  159. }
  160. // 1.支付方式
  161. const payType = ref('')
  162. const payTypeList = ref([])
  163. const codeList = ref([])
  164. const getCodeList = async () => {
  165. try {
  166. const list = await getEnableCodeList({appId: props.appId})
  167. codeList.value = list || []
  168. } catch (error) {
  169. console.log(error)
  170. } finally {
  171. if (definePayTypeList?.length && codeList.value?.length) {
  172. codeList.value.forEach(code => {
  173. const item = definePayTypeList.find(p => p.code === code)
  174. if (item) {
  175. if (!payType.value) {
  176. tip.value = item.tip || ''
  177. // payTypeChange(code) // 默认值赋值
  178. // 默认值赋值(暂时只支持扫码)
  179. const bool = qrCodePay.includes(code)
  180. if (bool) payTypeChange(code)
  181. }
  182. payTypeList.value.push(item)
  183. }
  184. })
  185. }
  186. getUnpaidOrderList()
  187. }
  188. }
  189. watch(
  190. () => props.info.id,
  191. (newVal, oldVal) => {
  192. if (!newVal) return
  193. if (!oldVal && newVal) getCodeList() // 初始化时要从获取支付方式列表开始
  194. if (oldVal && newVal) getUnpaidOrderList() // 切换充值金额,从创建新充值订单开始
  195. },
  196. { immediate: true },
  197. { deep: true }
  198. )
  199. const countdownTime = 60000 // 倒计时
  200. const countdownShow = ref(null) // 展示
  201. const hideTimer = ref(null)
  202. const stopIntervalFun = () => {
  203. if (hideTimer.value) clearInterval(hideTimer.value); hideTimer.value = null // 每一次点击都清除上一个轮询
  204. // 初始化倒计时
  205. countdownShow.value = countdownTime
  206. // 倒计时展示
  207. hideTimer.value = setInterval(() => { showCountdown() }, 1000)
  208. }
  209. const showCountdown = () => {
  210. countdownShow.value -= 1000
  211. if (countdownShow.value <= 0) clearTimer()
  212. }
  213. onUnmounted(() => {
  214. clearTimer()
  215. })
  216. const clearTimer = () => {
  217. if (payStatusTimer.value) clearInterval(payStatusTimer.value); payStatusTimer.value = null
  218. if (hideTimer.value) clearInterval(hideTimer.value); hideTimer.value = null
  219. emit('stopInterval')
  220. }
  221. </script>
  222. <style lang="scss" scoped>
  223. .code {
  224. border-radius: 6px;
  225. background-color: #f7f8fa;
  226. &-left {
  227. border: 1px solid #00897B;
  228. border-radius: 6px;
  229. padding: 5px;
  230. }
  231. &-right {
  232. .price {
  233. font-size: 30px;
  234. font-weight: 700;
  235. color: var(--v-error-base);
  236. }
  237. }
  238. }
  239. </style>