Browse Source

职位支付

lifanagju_citu 2 tháng trước cách đây
mục cha
commit
b46bf77ff9
4 tập tin đã thay đổi với 233 bổ sung35 xóa
  1. 1 1
      api/new/position.js
  2. 29 28
      components/PositionList/index.vue
  3. 201 6
      components/payPopup/index.vue
  4. 2 0
      pages/index/search.vue

+ 1 - 1
api/new/position.js

@@ -29,7 +29,7 @@ export const getUnpaidOrder = (params) => {
 // 创建订单
 export const createTradeOrder = (data) => {
   return request({
-    url: '/app-api/menduner/system/recruit/trade/order/create',
+    url: '/app-api/menduner/system/recruit/trade/order/wx-program/create',
     method: "POST",
     data,
     custom: {

+ 29 - 28
components/PositionList/index.vue

@@ -41,14 +41,14 @@
           </view>
           <view class="sub-li-bottom">
             <view v-if="tab === 1">
-              <span class="cursor-pointer divider-mx" @tap="handleAction(item.top ? 4 : 3, '', item)">{{ item.top ? '取消置顶' : '置顶' }}</span>
-              <span class="cursor-pointer divider-mx" @tap="handleAction(0, '', item, item)">关闭</span>
+              <span class="divider-mx" @tap="handleAction(item.top ? 4 : 3, '', item)">{{ item.top ? '取消置顶' : '置顶' }}</span>
+              <span class="divider-mx" @tap="handleAction(0, '', item, item)">关闭</span>
             </view>
-            <!-- <span v-if="(item.status-0) === 99 && tab === 0" class="cursor-pointer color-primary" @tap="toPay(item)">发布</span> -->
-            <span v-if="tab === 2" class="cursor-pointer divider-mx" @tap="handleAction(1, '', item, item)">激活</span>
-            <span class="cursor-pointer divider-mx" @tap="handleDetail(item)">详情</span>
+            <span v-if="(item?.status-0) === 99 && tab === 0" class="divider-mx" @tap="toPay(item)">发布</span>
+            <span v-if="tab === 2" class="divider-mx" @tap="handleAction(1, '', item, item)">激活</span>
+            <span class="divider-mx" @tap="handleDetail(item)">详情</span>
             <!-- <view v-if="tab !== 3">
-              <span class="cursor-pointer divider-mx" :style="{'color': item.edit ? '#333' : '#999'}" @tap="handleEdit(item)">{{ '编辑' }}</span>
+              <span class="divider-mx" :style="{'color': item.edit ? '#333' : '#999'}" @tap="handleEdit(item)">{{ '编辑' }}</span>
             </view> -->
           </view>
         </view>
@@ -71,7 +71,7 @@
         @close="handleClose"
       ></uni-popup-dialog>
     </uni-popup>
-    <payPopup v-if="props.payable" ref="payRef" amount="123"></payPopup>
+    <payPopup v-if="props.payable" ref="payRef" amount="123" @paySuccess="paySuccess"></payPopup>
   </view>
 </template>
 
@@ -86,9 +86,7 @@ import {
   topJobAdvertised,
   refreshJobAdvertised,
   closeJobAdvertised,
-  enableJobAdvertised,
-  getUnpaidOrder,
-  createTradeOrder
+  enableJobAdvertised
 } from '@/api/new/position'
 import { userStore } from '@/store/user'; const useUserStore = userStore()
 const emit = defineEmits(['entClick', 'refresh'])
@@ -102,27 +100,27 @@ const props = defineProps({
 
 const userInfo = ref(useUserStore?.userInfo || {})
 
-const spuId = ref('')
-const spuName = ref('')
-const operateObj = ref({})
+// const spuId = ref('')
+// const spuName = ref('')
+// const operateObj = ref({})
 const payRef = ref()
 // 支付
 const toPay = async (val) => {
-  // // 待发布且有额度的激活职位即可
-  // if (userInfo.value.entitlement?.publishJobCount > 0) {
-  //   await enableJobAdvertised([val.id])
-  //   emit('refresh', true)
-  //   setTimeout(() => { uni.showToast({ title: '发布成功', icon: 'success' }) }, 1000)
-  //   return
-  // }
+  // 待发布且有额度的激活职位即可
+  if (userInfo.value.entitlement?.publishJobCount > 0) {
+    await enableJobAdvertised([val.id])
+    emit('refresh', true)
+    setTimeout(() => { uni.showToast({ title: '发布成功', icon: 'success' }) }, 1000)
+    return
+  }
 
-  operateObj.value = val
-  spuId.value = val.id || ''
-  spuName.value = val.name || ''
-  payRef.value && payRef.value.handleOpen()
+  // operateObj.value = val
+  // spuId.value = val.id || ''
+  // spuName.value = val.name || ''
+  payRef.value && payRef.value.handleOpen({ spuId: val?.id||'', spuName: val?.name||'', price: 1, type: 1 })
 }
 
-setTimeout(() => { toPay(props.list[0]) }, 2000)
+// setTimeout(() => { toPay(props.list[0]) }, 2000)
 
 const confirm = ref()
 const dialogContent = ref('')
@@ -149,9 +147,6 @@ const handleAction = async (index, type, { id }, item) => {
     // 没有可发布额度激活职位需重新付款
     userInfo.value = await useUserStore.getUserInfos()
     if (index === 1 && userInfo.value?.entitlement.publishJobCount <= 3990039900) {
-      // 判断是否已有创建好的订单,有就直接跳转支付,没有就创建订单
-      const data = await getUnpaidOrder({ spuId: id, type: 1 })
-      if (!data) await createTradeOrder({ spuId: id, spuName: item.name, price: 39900, type: 1 })
       toPay(item)
       return
     }
@@ -170,6 +165,7 @@ const handleAction = async (index, type, { id }, item) => {
 const handleClose = () => {
   confirm.value.close()
 }
+
 const handleConfirm = async () => {
   try {
     uni.showLoading({ title: '关闭中...', mask: true })
@@ -206,6 +202,11 @@ const handleToResume = (val) => {
   router.push(path)
 }
 
+const paySuccess = () => {
+  emit('refresh', true)
+  setTimeout(() => { uni.showToast({ title: '发布成功', icon: 'success' }) }, 1000)
+}
+
 </script>
 
 <style scoped lang="scss">

+ 201 - 6
components/payPopup/index.vue

@@ -36,7 +36,7 @@
           </view>
         </view>
         <view class="popup-content-btn">
-          <button class="popup-content-btn-s" @tap="handlePay">
+          <button class="popup-content-btn-s" @tap="handlePay(false)">
             确认支付 
             <uni-icons color="#FFF" type="icon-renminbi1688" size="16" custom-prefix="iconfont"></uni-icons>
             {{ amount }}
@@ -48,7 +48,11 @@
 </template>
 <script setup>
 import { ref } from 'vue'
-import { getEnableCodeList } from '@/api/common'
+import { onHide, onShow } from '@dcloudio/uni-app'
+// import { userStore } from '@/store/user'; const useUserStore = userStore()
+import { getSocialUser, socialUserBind, payOrderSubmit, getOrderPayStatus, getEnableCodeList } from '@/api/common'
+import { createTradeOrder, getUnpaidOrder } from '@/api/new/position'
+const emit = defineEmits(['paySuccess'])
 
 const props = defineProps({
   title: { // 标题
@@ -83,7 +87,7 @@ const payTypeList = ref([])
 const getPayMethodsList = async () => {
   payTypeList.value = []
   try {
-    const res = await getEnableCodeList({appId: 14})
+    const res = await getEnableCodeList({appId: 15})
     if (!res?.data?.length) {
       return
     }
@@ -113,15 +117,206 @@ const handleClose = () => {
   tabBarControl(true)
   popup.value.close()
 }
-const handleOpen = () => {
+const query = ref(null)
+const handleOpen = (val) => {
+  query.value = val
   tabBarControl(false)
   popup.value.open('bottom')
 }
 
-// 支付
-const handlePay = async () => {
+// 设置 openid 到本地存储,目前只有 pay 支付时会使用
+const setOpenid = (openid) => {
+  uni.setStorageSync('openid', openid)
 }
 
+const bind = () => {
+  return new Promise(async (resolve, reject) => {
+    // 1. 获得微信 code
+    const codeResult = await uni.login()
+    if (codeResult.errMsg !== 'login:ok') {
+      return resolve(false)
+    }
+    // 2. 绑定账号 // // 社交快捷登录
+    const obj = {
+      type: socialType,
+      code: codeResult.code,
+      state: 'default',
+    }
+    const bindResult = await socialUserBind(obj);
+    if (bindResult.code === 0) {
+      setOpenid(bindResult.data)
+      return resolve(true)
+    } else {
+      return resolve(false)
+    }
+  })
+}
+
+const bindWeiXin = () => {
+  uni.showModal({
+    title: '微信支付',
+    content: '请先绑定微信再使用微信支付',
+    success: function (res) {
+      if (res.confirm) {
+        // 微信小程序绑定
+        bind()
+      }
+    },
+  });
+}
+
+const socialType = 34; // 社交类型 - 微信小程序
+
+// 预支付
+const prepay = async (channel, orderData) => { 
+  return new Promise(async (resolve, reject) => {
+    let data = {
+      id: orderData?.payOrder?.id,
+      channelCode: channel,
+      channelExtras: {},
+    };
+    // 特殊逻辑:微信公众号、小程序支付时,必须传入 openid
+    if (['wx_pub', 'wx_lite'].includes(channel)) {
+      const userRes = await getSocialUser(socialType)
+      const openid = userRes?.data?.openid ? userRes.data.openid : null
+      // 如果获取不到 openid,微信无法发起支付,此时需要引导
+      if (!openid) {
+        bindWeiXin()
+        return
+      }
+      data.channelExtras.openid = openid
+    }
+    // 发起预支付 API 调用
+    payOrderSubmit(data).then((res) => {
+      // 成功时
+      res.code === 0 && resolve(res)
+      // 失败时
+      if (res.code !== 0 && res.msg.indexOf('无效的openid') >= 0) {
+        // 特殊逻辑:微信公众号、小程序支付时,必须传入 openid 不正确的情况
+        if (
+          res.msg.indexOf('无效的openid') >= 0 || // 获取的 openid 不正确时,或者随便输入了个 openid
+          res.msg.indexOf('下单账号与支付账号不一致') >= 0
+        ) {
+          bindWeiXin()
+        }
+      }
+    })
+  })
+}
+
+let interTimer = null
+let payLoading = false
+const checkPayStatus = async (id) => {
+  if (!id) return
+  try {
+    if (payLoading || !interTimer) return
+    payLoading = true
+    const res = await getOrderPayStatus({ id })
+    if (res?.data?.status === 10) {
+      if (interTimer) clearInterval(interTimer)
+      emit('paySuccess')
+      // setTimeout(async () => {
+      //   const _userInfo = await useUserStore.getUserInfo()
+      //   userInfo.value = _userInfo
+      //   getMemberList() // 刷新套餐列表
+      // }, 1500)
+    }
+  } catch (error) {
+    console.log(error)
+  } finally {
+    payLoading = false
+  }
+}
+
+// 计时器
+const initIntervalFun = () => {
+  if (interTimer) clearInterval(interTimer)
+
+  // 查询是否已经支付
+  const id = orderInfo.value?.payOrder?.id || orderInfo.value?.order?.payOrderId
+  if (id) {
+    interTimer = setInterval(() => {
+      checkPayStatus(id)
+    }, 1000)
+  }
+}
+
+const orderInfo = ref({})
+const weChatMiniProgramPay = async (data) => {
+  orderInfo.value = data
+  let res = await prepay(channelValue.value, data); // 预支付
+  if (res?.code !== 0) {
+    return;
+  }
+  // 调用微信小程序支付
+  const payConfig = res?.data?.displayContent ? JSON.parse(res.data.displayContent) : null
+  if (!payConfig) return uni.showToast({ title: '购买失败', icon: 'none'})
+  uni.requestPayment({
+    provider: 'wxpay',
+    timeStamp: payConfig.timeStamp,
+    nonceStr: payConfig.nonceStr,
+    package: payConfig.packageValue,
+    signType: 'RSA',
+    paySign: payConfig.paySign,
+    success: (res) => {
+      initIntervalFun()
+      popup.value.close()
+      uni.showToast({ title: '支付成功', icon: 'none'})
+    },
+    fail: (err) => {
+      if (err.errMsg === 'requestPayment:fail cancel') {
+        uni.showToast({ title: '支付已取消', icon: 'none'})
+      } else {
+        uni.showToast({ title: '支付失败', icon: 'none'})
+      }
+    },
+  });
+}
+
+// 确认支付
+const handlePay = async (retry = false) => {
+  if (!channelValue.value) {
+    uni.showToast({ title: '请选择支付方式', icon: 'none'})
+    return
+  }
+  // 判断是否已有创建好的订单,有就直接跳转支付,没有就创建订单
+  try {
+    const params = {
+      spuId: query.value?.spuId, // 商品编号
+      type: query.value?.type // 订单类型 0平台订单|1发布职位|2发布众聘职位|3会员套餐|4企业会员套餐|5招聘会门票
+    }
+    if (!params.spuId || params.type === null || params.type === undefined) return
+    const res = await getUnpaidOrder(params) // 查询订单
+    if (res.data) {
+      // 获取支付码
+      weChatMiniProgramPay(res.data)
+      return
+    }
+    if (!retry) setOrderCreated() // 创建订单
+  } catch (error) {
+    console.log(error)
+  }
+}
+
+const setOrderCreated = async () => {
+  const params = {
+    spuId: query.value?.spuId, // 商品编号
+    type: query.value?.type, // 订单类型 0平台订单|1发布职位|2发布众聘职位|3会员套餐|4企业会员套餐|5招聘会门票
+    price: query.value?.price, // 价格
+    spuName: query.value?.spuName, // 商品名称
+  }
+  if (!params.spuId || params.type === null || params.type === undefined || !params.price || !params.spuName) return
+  await createTradeOrder(params)
+  handlePay(true) // 避免死循环
+}
+
+onShow(() => {
+  if (orderInfo && orderInfo.value?.id) initIntervalFun()
+})
+onHide(() => {
+  if (interTimer) clearInterval(interTimer)
+})
+
 defineExpose({
 	handleOpen
 })

+ 2 - 0
pages/index/search.vue

@@ -22,6 +22,8 @@
 	    currentTabBar?.setData({ selected: 0 });
 	})
 
+	// uni.switchTab({ url: '/pages/index/position' }) // 测试数据
+
 	const handleTo = () => {
 		uni.navigateTo({
 			url: '/pagesB/personnelDetails/index?id=1'