index.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  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. <span class="font-size-15 ml-1">{{ unit }}</span>
  18. </div>
  19. <template v-if="payTypeList?.length">
  20. <v-chip-group v-model="payType" selected-class="text-primary" column mandatory @update:modelValue="payTypeChange">
  21. <v-chip filter v-for="k in payTypeList" :key="k.code" :value="k.code" class="mr-3" label>
  22. {{ k.name }}
  23. <svg-icon v-if="k.icon" class="ml-1" :name="k.icon" :size="k.size"></svg-icon>
  24. </v-chip>
  25. </v-chip-group>
  26. <div v-if="tip" style="text-align: center;" class="mt-2">{{ tip }}</div>
  27. <div>
  28. <!-- 钱包支付 -->
  29. <div v-if="isWalletPay" class="py-10" style="text-align: center;">
  30. <div>
  31. <span>剩余点数:</span>
  32. <span style="color: var(--v-primary-base);">{{ accountBalance }}</span>
  33. </div>
  34. <div class="my-3" v-if="notEnoughMoney">
  35. <!-- <v-icon color="warning">mdi-information</v-icon> -->
  36. <!-- <span class="color-warning">{{ props.params?.txt || '当前余额不足,请选择其他支付方式' }}</span> -->
  37. <span class="color-error">
  38. 当前剩余点数不足,<span class="text-decoration-underline cursor-pointer" @click="handleRecharge">去充值</span>
  39. </span>
  40. </div>
  41. </div>
  42. <!-- 二维码支付 -->
  43. <div v-if="isQrCodePay" style="text-align: center;">
  44. <QrCode :text="payQrCodeTxt" :width="170" style="margin: 0 auto;" />
  45. <div class="mb-5" v-if="payQrCodeTxt" style="color: var(--v-error-base);">扫码支付时请勿离开</div>
  46. </div>
  47. <!-- 钱包支付确认按钮 -->
  48. <div v-if="isWalletPay && !notEnoughMoney" class="mt-2" style="text-align: center;">
  49. <v-btn
  50. class="buttons" color="primary"
  51. :loading="payLoading"
  52. @click="walletPaySubmit"
  53. >
  54. 确认
  55. </v-btn>
  56. </div>
  57. </div>
  58. </template>
  59. </v-card>
  60. <CtDialog :visible="showRecharge" :widthType="1" titleClass="text-h6" title="点数充值" :footer="false" submitText="确认" @close="handleRechargeClose">
  61. <Recharge @paySuccess="RechargePaySuccess"></Recharge>
  62. </CtDialog>
  63. </template>
  64. <script setup>
  65. defineOptions({ name: 'pay-index'})
  66. import { computed, nextTick, onUnmounted, ref } from 'vue'
  67. import QrCode from '@/components/QrCode'
  68. import { definePayTypeList, qrCodePay, walletPay } from './until/payType'
  69. import { getEnableCodeList, getUnpaidOrder, payOrderSubmit, getOrderPayStatus } from '@/api/common'
  70. import { createTradeOrder } from '@/api/position'
  71. import { useSharedState } from '@/store/sharedState'
  72. import { rechargeOrderCreate } from '@/api/recruit/enterprise/member/points'
  73. // import { payCalculation } from '@/utils/position'
  74. import Recharge from '@/views/recruit/enterprise/memberCenter/myMembers/components/pointsAndBalance.vue'
  75. const emit = defineEmits(['payTypeChange', 'paySuccess'])
  76. const props = defineProps({
  77. params: {
  78. type: Object,
  79. default: () => {}
  80. },
  81. cost: {
  82. type: [String, Number],
  83. default: 0
  84. },
  85. spuId: { // 原始数据id
  86. type: String,
  87. default: ''
  88. },
  89. spuName: {
  90. type: String,
  91. default: ''
  92. },
  93. returnUrl: {
  94. type: String,
  95. default: ''
  96. },
  97. rechargeInfo: {
  98. type: Object,
  99. default: () => {}
  100. },
  101. appId: {
  102. type: Number,
  103. default: 10 // 10为一般情况下支付,11为充值支付
  104. },
  105. orderType: {
  106. type: Number,
  107. default: 2 // 订单类型 0平台订单| 1发布职位订单| 2发布众聘职位订单,示例值(1)
  108. },
  109. unit: {
  110. type: String,
  111. default: '点数'
  112. }
  113. })
  114. const showRecharge = ref(false)
  115. const loading = ref(true)
  116. const tip = ref('')
  117. // 步骤:
  118. // 1. 获取支付方式类型列表getCodeList (appId不同时数据返回有区别)
  119. // 2. 创建支付订单(先获取有无创建的,没有就创建)getUnpaidOrderList (appId=11时只有创建,没有获取)
  120. // 3. 如果是二维码类型支付(isQrCodePay=true)生成二维码(需要绑定支付订单的订单号)
  121. // 4.轮询用户是否支付成功
  122. // 如果是点数支付也要走上面的步骤,只是不生成二维码
  123. const accountBalance = ref(0)
  124. const notEnoughMoney = computed(() => {
  125. return (Number(props.cost) > Number(accountBalance.value))
  126. })
  127. // 生成二维码内容
  128. const timer = ref(null)
  129. onUnmounted(() => {
  130. if (timer.value) clearInterval(timer.value); timer.value = null
  131. })
  132. // 如果是点数支付的话走完payOrderSubmit之后即扣点数,如果是二维码支付的话只是生成二维码,这一步以后是轮询是否支付成功
  133. const paySubmit = async () => {
  134. if (!payType.value) return
  135. try {
  136. if (payOrder.value) {
  137. // 提交支付订单
  138. const params = {
  139. channelCode: payType.value, // 支付渠道
  140. // returnUrl: , // 支付成功后,支付渠道跳转回当前页;再由当前页,跳转回 {@link returnUrl} 对应的地址
  141. }
  142. if ((props.appId - 0) === 10) { // 众聘
  143. if (!payOrder.value?.id || !payOrder.value.notifyUrl) return
  144. params.id = payOrder.value.id // 支付单编号
  145. params.displayMode = payOrder.value.notifyUrl // 展示模式 notifyUrl
  146. }
  147. if ((props.appId - 0) === 11) { // 充值
  148. if (!payOrder.value?.payOrderId) return
  149. params.id = payOrder.value.payOrderId // 支付单编号
  150. }
  151. const res = await payOrderSubmit(params)
  152. payQrCodeTxt.value = res?.displayContent || '' // 二维码
  153. if (timer.value) clearInterval(timer.value); timer.value = null
  154. timer.value = setInterval(() => { payStatus() }, 1000) // 轮巡查询用户是否支付
  155. // if (isQrCodePay.value) {
  156. // }
  157. }
  158. } catch (error) {
  159. console.log(error)
  160. }
  161. }
  162. // 2.支付订单
  163. const payOrder = ref({})
  164. let maxCount = 0
  165. const getUnpaidOrderList = async () => {
  166. try {
  167. if ((props.appId - 0) === 11) {
  168. //* 充值
  169. if (props.rechargeInfo.payPrice === undefined && props.rechargeInfo.packageId === undefined) return
  170. const params = {
  171. payPrice: (props.rechargeInfo.payPrice-0),
  172. packageId: props.rechargeInfo.id,
  173. }
  174. const data = await rechargeOrderCreate(params)
  175. payOrder.value = data || {}
  176. } else {
  177. //* 发布职位
  178. const data = await getUnpaidOrder({ spuId: props.spuId, type: props.orderType }) // 获取待支付的订单 (order:业务订单; payOrder:支付订单)
  179. if (!data) {
  180. // 订单超时,重新提交订单
  181. await createTradeOrder({
  182. spuId: props.spuId,
  183. spuName: props.spuName,
  184. price: props.cost,
  185. type: props.orderType, // 发布众聘职位订单
  186. })
  187. if (maxCount > 3) return // 避免死循环
  188. maxCount++
  189. setTimeout(() => {
  190. getUnpaidOrderList()
  191. }, 1000)
  192. }
  193. //
  194. payOrder.value = data?.payOrder || null
  195. }
  196. if (isQrCodePay.value) paySubmit() // 生成二维码内容
  197. } catch (error) {
  198. console.log(error)
  199. } finally {
  200. nextTick(() => {
  201. loading.value = false
  202. })
  203. }
  204. }
  205. // 1.支付方式
  206. const isWalletPay = ref(false)
  207. const isQrCodePay = ref(false)
  208. const payTypeChange = (value) => {
  209. payType.value = value
  210. tip.value = payTypeList.value.find(e => e.code === payType.value)?.tip || ''
  211. isQrCodePay.value = qrCodePay.includes(payType.value)
  212. isWalletPay.value = walletPay.includes(payType.value)
  213. paySubmit() // 生成二维码内容
  214. }
  215. // 1.支付方式
  216. const payType = ref('')
  217. const payTypeList = ref([])
  218. const sharedState = useSharedState()
  219. const codeList = ref(sharedState.payCodeList || [])
  220. const getCodeList = async () => {
  221. try {
  222. if (!codeList.value?.length) {
  223. const list = await getEnableCodeList({appId: props.appId})
  224. codeList.value = list || []
  225. // sharedState.setPayCodeList(codeList.value) // 返回的支付方式列表会变
  226. } else {
  227. codeList.value = sharedState.payCodeList
  228. }
  229. } catch (error) {
  230. console.log(error)
  231. } finally {
  232. if (definePayTypeList?.length && codeList.value?.length) {
  233. codeList.value.forEach(code => {
  234. const item = definePayTypeList.find(p => p.code === code)
  235. if (item) {
  236. if (!payType.value) {
  237. tip.value = item.tip || ''
  238. payTypeChange(code) // 默认值赋值
  239. }
  240. payTypeList.value.push(item)
  241. }
  242. })
  243. }
  244. getUnpaidOrderList()
  245. }
  246. }
  247. getCodeList()
  248. const payLoading = ref(false)
  249. const payQrCodeTxt = ref('')
  250. // 钱包支付(余额支付)
  251. const walletPaySubmit = () => {
  252. payLoading.value = true
  253. paySubmit() // 使用点数
  254. }
  255. import { useUserStore } from '@/store/user'; const store = useUserStore()
  256. import Snackbar from '@/plugins/snackbar'
  257. import { useRoute } from 'vue-router'; const route = useRoute()
  258. import { useRouter } from 'vue-router'; const router = useRouter()
  259. const payStatus = async () => {
  260. // if (timer.value) clearInterval(timer.value); timer.value = null
  261. // setTimeout(() => { // 测试代码
  262. // }, 2000)
  263. try {
  264. const data = await getOrderPayStatus({ id: (props.appId - 0) === 11 ? payOrder.value.payOrderId : payOrder.value.id })
  265. if ((data?.status - 0) === 10) {
  266. // 支付成功
  267. if (timer.value) clearInterval(timer.value); timer.value = null
  268. setTimeout(() => {
  269. // 更新点数(充值、发布职位)
  270. updateAccountInfo()
  271. // 支付成功
  272. emit('paySuccess')
  273. // 返回指定页面
  274. if (route.fullPath === props.returnUrl) router.go(0)
  275. else if (props.returnUrl) router.push(props.returnUrl)
  276. Snackbar.success('支付成功')
  277. }, 2000);
  278. }
  279. } catch (error) {
  280. console.log(error)
  281. }
  282. }
  283. const handleRechargeClose = () => {
  284. showRecharge.value = false
  285. loading.value = false
  286. }
  287. // 点数发生支付时点充值
  288. const RechargePaySuccess = () => {
  289. showRecharge.value = false
  290. // updateAccountInfo()
  291. }
  292. const handleRecharge = () => {
  293. showRecharge.value = true
  294. }
  295. const updateAccountInfo = async (init = false) => {
  296. const account = await store.getEnterpriseUserAccountInfo()
  297. accountBalance.value = account?.balance
  298. if (init) return
  299. loading.value = false
  300. }
  301. if ((props.appId - 0) !== 11) updateAccountInfo(true) // 点数发生支付时点充值-充值不查余额
  302. </script>
  303. <style lang="scss" scoped>
  304. .font-size-30 { font-size: 30px; }
  305. </style>