index.vue 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. <!-- 支付方式 -->
  2. <template>
  3. <v-card elevation="0" :loading="loading" :disabled="loading">
  4. <!-- 加载样式 -->
  5. <template v-slot:loader="{ isActive }">
  6. <v-progress-linear
  7. :active="isActive"
  8. color="var(--v-primary-base)"
  9. height="1"
  10. indeterminate
  11. ></v-progress-linear>
  12. </template>
  13. <!-- 赏金所需 -->
  14. <div class="pt-3 pb-5" style="color: var(--v-error-base); font-weight: bold; text-align: center;">
  15. <span class="font-size-13">¥</span>
  16. <span class="font-size-30">{{ cost }}</span>
  17. </div>
  18. <template v-if="payTypeList?.length">
  19. <v-chip-group v-model="payType" selected-class="text-primary" column mandatory @update:modelValue="payTypeChange">
  20. <v-chip filter v-for="k in payTypeList" :key="k.code" :value="k.code" class="mr-3" label>
  21. {{ k.name }}
  22. <svg-icon v-if="k.icon" class="ml-1" :name="k.icon" :size="k.size"></svg-icon>
  23. </v-chip>
  24. </v-chip-group>
  25. <div v-if="tip" style="text-align: center;" class="mt-2">{{ tip }}</div>
  26. <div>
  27. <!-- 钱包支付 -->
  28. <div v-if="isWalletPay" class="py-10" style="text-align: center;">
  29. <div>
  30. <span >{{ $t('enterprise.account.accountBalances') }}:</span>
  31. <span style="color: var(--v-primary-base);">{{ balance }}</span>
  32. </div>
  33. <div class="my-3" v-if="balanceNotEnough">
  34. <!-- <v-icon color="warning">mdi-information</v-icon> -->
  35. <span class="color-warning">{{ props.params?.txt || '当前余额不足,请选择其他支付方式' }}</span>
  36. </div>
  37. </div>
  38. <!-- 二维码支付 -->
  39. <div v-if="isQrCodePay" style="text-align: center;">
  40. <QrCode :text="payQrCodeTxt" :width="170" style="margin: 0 auto;" />
  41. <div class="mb-5" v-if="payQrCodeTxt" style="color: var(--v-error-base);">扫码支付时请勿离开</div>
  42. </div>
  43. <!-- 钱包支付确认按钮 -->
  44. <div v-if="isWalletPay && !balanceNotEnough" class="mt-2" style="text-align: center;">
  45. <v-btn
  46. class="buttons" color="primary"
  47. :loading="payLoading"
  48. @click="walletPaySubmit"
  49. >
  50. 确认
  51. </v-btn>
  52. </div>
  53. </div>
  54. </template>
  55. </v-card>
  56. </template>
  57. <script setup>
  58. defineOptions({ name: 'pay-index'})
  59. import { computed, onUnmounted, ref } from 'vue'
  60. import QrCode from '@/components/QrCode'
  61. import { definePayTypeList, qrCodePay, walletPay } from './until/payType'
  62. import { getEnableCodeList, getUnpaidOrder, payOrderSubmit, getOrderPayStatus } from '@/api/common'
  63. import { createTradeOrder } from '@/api/position'
  64. import { useSharedState } from '@/store/sharedState'
  65. const emit = defineEmits(['payTypeChange', 'paySuccess'])
  66. const props = defineProps({
  67. params: {
  68. type: Object,
  69. default: () => {}
  70. },
  71. cost: {
  72. type: [String, Number],
  73. default: 0
  74. },
  75. spuId: { // 原始数据id
  76. type: String,
  77. default: ''
  78. },
  79. spuName: {
  80. type: String,
  81. default: ''
  82. },
  83. returnUrl: {
  84. type: String,
  85. default: ''
  86. },
  87. type: {
  88. type: Number,
  89. default: 2 // 订单类型 0平台订单| 1发布职位订单| 2发布众聘职位订单,示例值(1)
  90. },
  91. })
  92. const loading = ref(true)
  93. const tip = ref('')
  94. // 步骤:
  95. // 1. 获取支付方式类型列表getCodeList
  96. // 2. 创建支付订单(先获取有无创建的,没有就创建)getUnpaidOrderList
  97. // 3. 如果是二维码类型支付(isQrCodePay=true)生成二维码(需要绑定支付订单的订单号)
  98. const balance = JSON.parse(localStorage.getItem('enterpriseUserAccount'))?.balance || 0
  99. const balanceNotEnough = computed(() => {
  100. return (Number(props.cost) > Number(balance))
  101. })
  102. // 生成二维码内容
  103. const timer = ref(null)
  104. onUnmounted(() => {
  105. if (timer.value) clearInterval(timer.value); timer.value = null
  106. })
  107. const initPayQrCode = async () => { // 生成二维码内容
  108. if (!payOrder.value?.id || !payOrder.value.notifyUrl || !payType.value) return
  109. if (timer.value) clearInterval(timer.value); timer.value = null
  110. try {
  111. if (payOrder.value) {
  112. // 提交支付订单
  113. const params = {
  114. id: payOrder.value.id, // 支付单编号
  115. channelCode: payType.value, // 支付渠道
  116. displayMode: payOrder.value.notifyUrl, // 展示模式 notifyUrl
  117. // returnUrl: location.href, // 支付成功后,支付渠道跳转回当前页;再由当前页,跳转回 {@link returnUrl} 对应的地址
  118. }
  119. const res = await payOrderSubmit(params)
  120. payQrCodeTxt.value = res?.displayContent || '' // 支付二维码
  121. timer.value = setInterval(() => { payStatus() }, 1000) // 轮巡查询用户是否支付
  122. }
  123. } catch (error) {
  124. console.log(error)
  125. }
  126. }
  127. // 2.支付订单
  128. const payOrder = ref({})
  129. let maxCount = 0
  130. const getUnpaidOrderList = async () => {
  131. try {
  132. const data = await getUnpaidOrder({ spuId: props.spuId, type: props.type }) // 获取待支付的订单 (order:业务订单; payOrder:支付订单)
  133. if (!data) {
  134. // 订单超时,重新提交订单
  135. await createTradeOrder({
  136. spuId: props.spuId,
  137. spuName: props.spuName,
  138. price: props.cost,
  139. type: props.type, // 发布众聘职位订单
  140. })
  141. if (maxCount > 3) return // 避免死循环
  142. maxCount++
  143. setTimeout(() => {
  144. getUnpaidOrderList()
  145. }, 1000)
  146. }
  147. //
  148. payOrder.value = data?.payOrder || null
  149. if (isQrCodePay.value) initPayQrCode() // 生成二维码内容
  150. } catch (error) {
  151. console.log(error)
  152. } finally {
  153. loading.value = false
  154. }
  155. }
  156. // getUnpaidOrderList() // getUnpaidOrder
  157. // 支付方式
  158. const isWalletPay = ref(false)
  159. const isQrCodePay = ref(false)
  160. const payTypeChange = (value) => {
  161. payType.value = value
  162. tip.value = payTypeList.value.find(e => e.code === payType.value)?.tip || ''
  163. isQrCodePay.value = qrCodePay.includes(payType.value)
  164. isWalletPay.value = walletPay.includes(payType.value)
  165. initPayQrCode() // 生成二维码内容
  166. emit('payTypeChange', value, balanceNotEnough)
  167. }
  168. // 1.支付方式
  169. const payType = ref('')
  170. const payTypeList = ref([])
  171. const sharedState = useSharedState()
  172. const codeList = ref(sharedState.payCodeList || [])
  173. const getCodeList = async () => {
  174. try {
  175. if (!codeList.value?.length) {
  176. const list = await getEnableCodeList({appId: 10})
  177. codeList.value = list || []
  178. sharedState.setPayCodeList(codeList.value)
  179. } else {
  180. codeList.value = sharedState.payCodeList
  181. }
  182. } catch (error) {
  183. console.log(error)
  184. } finally {
  185. if (definePayTypeList?.length && codeList.value?.length) {
  186. codeList.value.forEach(code => {
  187. const item = definePayTypeList.find(p => p.code === code)
  188. if (item) {
  189. if (!payType.value) {
  190. tip.value = item.tip || ''
  191. payTypeChange(code) // 默认值赋值
  192. }
  193. payTypeList.value.push(item)
  194. }
  195. })
  196. }
  197. getUnpaidOrderList()
  198. }
  199. }
  200. getCodeList()
  201. const payLoading = ref(false)
  202. const payQrCodeTxt = ref('')
  203. // 钱包支付(余额支付)
  204. const walletPaySubmit = () => {
  205. // emit('paySubmit', payType.value)
  206. }
  207. import Snackbar from '@/plugins/snackbar'
  208. import { useRoute } from 'vue-router'; const route = useRoute()
  209. import { useRouter } from 'vue-router'; const router = useRouter()
  210. const payStatus = async () => {
  211. // if (timer.value) clearInterval(timer.value); timer.value = null
  212. // setTimeout(() => {
  213. // console.log('fullPath1', route.fullPath)
  214. // console.log('returnUrl2',props.returnUrl)
  215. // debugger
  216. // if (route.fullPath === props.returnUrl) router.go(0)
  217. // else if (props.returnUrl) router.push(props.returnUrl) // 返回指定页面
  218. // else emit('paySuccess')
  219. // Snackbar.success('付款成功')
  220. // }, 2000)
  221. try {
  222. const data = await getOrderPayStatus({ id: payOrder.value.id })
  223. if ((data?.status - 0) === 10) {
  224. // 支付成功
  225. if (timer.value) clearInterval(timer.value); timer.value = null
  226. setTimeout(() => {
  227. if (route.fullPath === props.returnUrl) router.go(0)
  228. else if (props.returnUrl) router.push(props.returnUrl) // 返回指定页面
  229. else emit('paySuccess')
  230. Snackbar.success('付款成功')
  231. }, 1000);
  232. }
  233. } catch (error) {
  234. console.log(error)
  235. }
  236. }
  237. </script>
  238. <style lang="scss" scoped>
  239. .font-size-30 { font-size: 30px; }
  240. </style>