293 lines
10 KiB
Vue
293 lines
10 KiB
Vue
<template>
|
||
<crud-layout mainIsScroll bg="/static/page/blue-bg.png">
|
||
<view class="flex flex-col">
|
||
<view class="mx-auto text-34 py-2 h-80rpx">{{ scanPayState.mchName }}</view>
|
||
<tm-sheet :margin="[0, 10]" class="flex flex-col ">
|
||
<view class="flex flex-row items-end">
|
||
<span class="text-2xl text-[#333] mr-1 line-height-normal">¥</span>
|
||
<span class="text-3xl font-medium text-[#333]">{{ displayAmount }}</span>
|
||
</view>
|
||
<view v-if="couponDiscountAmount > 0" class="mt-10rpx flex items-center text-24rpx">
|
||
<view class="px-12rpx py-4rpx rounded-full bg-#0094ff1a c-#0094ff font-600">
|
||
已优惠 ¥{{ couponDiscountDisplay }}
|
||
</view>
|
||
<view class="ml-12rpx c-#666">
|
||
券后实付
|
||
<text class="c-#0094ff font-600">¥{{ couponPayableDisplay }}</text>
|
||
</view>
|
||
</view>
|
||
<view v-else-if="selectCoupon.id && couponUnavailableText" class="mt-10rpx text-24rpx c-#999">
|
||
{{ couponUnavailableText }}
|
||
</view>
|
||
</tm-sheet>
|
||
<tm-sheet v-if="walletList.length > 0" :margin="[0, 10]" class="flex flex-col">
|
||
<view class="text-gray-700 font-bold text-28 mb-1">选择支付钱包:</view>
|
||
<view v-for="(v, index) in walletList" :key="index"
|
||
class="border-4rpx border-solid border-#efefef my-8rpx duration-200 flex justify-between items-center p-18rpx rounded-2"
|
||
:class="{ 'border-4rpx border-solid border-blue!': selectWallet.id == v.id }"
|
||
@click="selectWallet = v">
|
||
<view class="flex items-center">
|
||
<image class="w-50 h-50 mr-8rpx" :src="filePath(v.walletTypeIcon)" />
|
||
<view class="text-32">{{ v.walletTypeName }}</view>
|
||
</view>
|
||
<view class="text-28 flex items-center">
|
||
<view>余额:</view>
|
||
<text class="main-text-28">{{ hooks.useMoney(v.balance) }} 元</text>
|
||
</view>
|
||
</view>
|
||
</tm-sheet>
|
||
|
||
<tm-sheet v-if="actCouponList.length > 0" :margin="[0, 10]" class="flex flex-col">
|
||
<view class="text-gray-700 font-bold text-28 mb-1">选择优惠券:</view>
|
||
<select-coupon-card :selectId="selectCoupon.id" v-for="item in actCouponList" :key="item.id"
|
||
:detail="item" :disabled="!isCouponAvailable(item)" @do-select="doSelectActCoupon" />
|
||
</tm-sheet>
|
||
|
||
<tm-button class="mt-1" :block="true" @click="handlePay()" :disabled="loading">支 付</tm-button>
|
||
<view class="h-120rpx" />
|
||
|
||
<number-keyboard v-if="numberKeyboardShow" v-model="amount" />
|
||
</view>
|
||
</crud-layout>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, computed, reactive, watch } from "vue"
|
||
import { onLoad, onShow } from "@dcloudio/uni-app"
|
||
import payApi from "@@/api/pay"
|
||
import { merchantApi, orderApi, walletApi, actCouponApi } from "@/sheep/api"
|
||
import { hooks } from "@@/index"
|
||
import { router } from "@/sheep"
|
||
import { filePath } from "@/sheep/utils/file"
|
||
import { displayPrice, storePrice } from "@/sheep/utils/formatMoney"
|
||
import { usePayStore } from "@/sheep/stores/pay"
|
||
import { isEmpty } from "radash"
|
||
|
||
import NumberKeyboard from "@/sheep/components/number-keyboard.vue"
|
||
import SelectCouponCard from "./components/select-coupon-card.vue"
|
||
|
||
const loading = ref(false)
|
||
const scanPayState = reactive({
|
||
mchId: "", // 商户id
|
||
mchName: "", // 商户名
|
||
orderId: "", // 订单id
|
||
action: "", // action === "harQrCodeScanPay":扫收款机上生成的码支付
|
||
})
|
||
const walletList = ref<any>([]) // 钱包列表
|
||
const actCouponList = ref<any>([]) // 优惠券列表
|
||
const selectWallet = ref<any>({}) // 当前选中的钱包
|
||
const selectCoupon = ref<any>({}) // 当前选中的优惠券
|
||
const orderInfo = ref<any>({}) // 订单信息
|
||
const amount = ref("") // 键盘输入的金额
|
||
const numberKeyboardShow = ref(true)
|
||
|
||
onLoad(async (options: any) => {
|
||
uni.showLoading({ title: "加载中..." })
|
||
const { mchId, orderId, action } = options
|
||
if (action === "harQrCodeScanPay") {
|
||
if (!options.mchId || !options.orderId) return uni.showToast({ title: "扫码参数错误", icon: "none" })
|
||
scanPayState.mchId = mchId
|
||
scanPayState.orderId = orderId
|
||
scanPayState.action = action
|
||
numberKeyboardShow.value = false
|
||
await getMchDetail(mchId)
|
||
await getOrderInfo()
|
||
const order = orderInfo.value
|
||
amount.value = displayPrice(order.totalPrice || 0 + order.extraPrice || 0, "")
|
||
return
|
||
}
|
||
|
||
const { id } = options
|
||
if (!isEmpty(id)) {
|
||
scanPayState.mchId = id
|
||
await getMchDetail(id)
|
||
return
|
||
}
|
||
|
||
return uni.showToast({ title: "扫码参数错误", icon: "none" })
|
||
})
|
||
|
||
onShow(async () => {
|
||
try {
|
||
await getWalletList()
|
||
await getActCouponApi()
|
||
} finally {
|
||
uni.hideLoading()
|
||
}
|
||
})
|
||
|
||
// 获取商户名
|
||
const getMchDetail = async (mchId: string) => {
|
||
if (!mchId) return uni.showToast({ title: "商户id错误", icon: "none" })
|
||
const { data } = await merchantApi.detail({ id: mchId })
|
||
scanPayState.mchName = data.name
|
||
}
|
||
|
||
const doSelectActCoupon = (e: any) => {
|
||
const { detail } = e
|
||
if (!isCouponAvailable(detail)) {
|
||
uni.showToast({ title: `当前优惠券满${priceCentToYuan(detail.minPoint).toFixed(2)}元可用`, icon: "none" })
|
||
return
|
||
}
|
||
// 如果id一样就取消选择
|
||
if (selectCoupon.value?.id == detail.id) {
|
||
selectCoupon.value = {}
|
||
return
|
||
}
|
||
selectCoupon.value = detail
|
||
}
|
||
|
||
// 获取优惠券
|
||
const getActCouponApi = async () => {
|
||
try {
|
||
const { code, data }: any = await actCouponApi.mchList({
|
||
scopeId: scanPayState.mchId
|
||
})
|
||
if (code !== 0) return
|
||
actCouponList.value = data ?? []
|
||
if (actCouponList.value.length > 0) {
|
||
selectCoupon.value = actCouponList.value.find((item: any) => isCouponAvailable(item)) || {}
|
||
}
|
||
} catch (error) {
|
||
console.log("获取优惠券意外的错误error:", error);
|
||
}
|
||
}
|
||
|
||
// 获取钱包列表
|
||
const getWalletList = async () => {
|
||
const { code, data } = await walletApi.all()
|
||
if (code !== 0) return uni.showToast({ title: "获取钱包列表失败", icon: "none" })
|
||
walletList.value = data.records
|
||
if (walletList.value.length > 0) {
|
||
selectWallet.value = walletList.value.find((v: any) => v.walletTypeUniqueCode == "1")
|
||
}
|
||
}
|
||
|
||
// 获取订单信息
|
||
const getOrderInfo = async () => {
|
||
const { code, data }: any = await orderApi.detail({ id: scanPayState.orderId })
|
||
if (code !== 0) return uni.showToast({ title: "获取订单信息失败", icon: "none" })
|
||
orderInfo.value = data
|
||
}
|
||
|
||
// 格式化金额显示
|
||
const displayAmount = computed(() => {
|
||
if (!amount.value) return "0"
|
||
return amount.value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")
|
||
})
|
||
|
||
const amountNumber = computed(() => {
|
||
const value = Number(String(amount.value || 0).replace(/,/g, ""))
|
||
return Number.isNaN(value) ? 0 : value
|
||
})
|
||
|
||
const priceCentToYuan = (price: any) => {
|
||
return Number(displayPrice(Number(price || 0), "", 2))
|
||
}
|
||
|
||
const isCouponAvailable = (coupon: any) => {
|
||
if (!coupon) return false
|
||
const minPoint = priceCentToYuan(coupon.minPoint)
|
||
if (minPoint > 0 && amountNumber.value < minPoint) return false
|
||
return true
|
||
}
|
||
const couponPayableAmount = computed(() => {
|
||
if (!selectCoupon.value?.id || amountNumber.value <= 0) return amountNumber.value
|
||
const couponType = String(selectCoupon.value.couponType || "")
|
||
const minPoint = priceCentToYuan(selectCoupon.value.minPoint)
|
||
const useAmount = priceCentToYuan(selectCoupon.value.useAmount)
|
||
// 满减券: 不足门槛不优惠, 否则按 useAmount 作为券后金额
|
||
if (couponType === "1") {
|
||
const faceValue = priceCentToYuan(selectCoupon.value.faceValue)
|
||
if (minPoint > 0 && amountNumber.value < minPoint) return amountNumber.value
|
||
const payable = useAmount > 0 ? useAmount : amountNumber.value - faceValue
|
||
return Math.max(0, Number(payable.toFixed(2)))
|
||
}
|
||
// 折扣券: 不足门槛不优惠, totalPrice * faceValue / 100
|
||
if (couponType === "2") {
|
||
const faceValue = Number(selectCoupon.value.faceValue || 0)
|
||
if (minPoint > 0 && amountNumber.value < minPoint) return amountNumber.value
|
||
const _p = Math.max(0, Number(((amountNumber.value * (100 - faceValue)) / 100).toFixed(2)))
|
||
return _p
|
||
}
|
||
// 立减券: 按 useAmount 作为券后金额
|
||
if (couponType === "3") {
|
||
const faceValue = priceCentToYuan(selectCoupon.value.faceValue)
|
||
const payable = useAmount > 0 ? useAmount : amountNumber.value - faceValue
|
||
return Math.max(0, Number(payable.toFixed(2)))
|
||
}
|
||
return amountNumber.value
|
||
})
|
||
|
||
const couponDiscountAmount = computed(() => {
|
||
return Math.max(0, Number((amountNumber.value - couponPayableAmount.value).toFixed(2)))
|
||
})
|
||
|
||
const couponDiscountDisplay = computed(() => couponDiscountAmount.value.toFixed(2))
|
||
const couponPayableDisplay = computed(() => couponPayableAmount.value.toFixed(2))
|
||
|
||
const couponUnavailableText = computed(() => {
|
||
if (!selectCoupon.value?.id || amountNumber.value <= 0) return ""
|
||
const minPoint = priceCentToYuan(selectCoupon.value.minPoint)
|
||
if (minPoint > 0 && amountNumber.value < minPoint) {
|
||
return `当前优惠券满${minPoint.toFixed(2)}元可用`
|
||
}
|
||
return ""
|
||
})
|
||
|
||
watch(amountNumber, () => {
|
||
if (selectCoupon.value?.id && !isCouponAvailable(selectCoupon.value)) {
|
||
selectCoupon.value = {}
|
||
}
|
||
})
|
||
|
||
// 点击支付
|
||
const handlePay = () => {
|
||
if (!scanPayState.mchId) return uni.showToast({ title: "扫码参数不存在", icon: "none" })
|
||
if (!amount.value) return uni.showToast({ title: "请输入金额", icon: "none" })
|
||
if (Number(amount.value) === 0) return uni.showToast({ title: "金额不能为0", icon: "none" })
|
||
|
||
uni.showModal({
|
||
title: "确定支付吗?",
|
||
success: async (res) => {
|
||
if (res.confirm) {
|
||
await pay()
|
||
}
|
||
},
|
||
})
|
||
}
|
||
|
||
// 支付
|
||
const pay = async () => {
|
||
try {
|
||
uni.showLoading({ title: "支付中...", mask: true })
|
||
if (scanPayState.action === "harQrCodeScanPay") {
|
||
const { code, msg }: any = await orderApi.doneHardQrCodeScanOrder({
|
||
orderId: scanPayState.orderId,
|
||
walletTypeId: selectWallet.value.walletTypeId,
|
||
})
|
||
if (code != 0) return uni.showToast({ title: msg || "支付失败", icon: "error" })
|
||
} else {
|
||
const { code, msg } = await payApi.createQrCodeOrder({
|
||
merchantId: scanPayState.mchId,
|
||
customPrice: storePrice(Number(amount.value)),
|
||
walletId: selectWallet.value.id,
|
||
actCouponRecordId: selectCoupon.value?.id || "",
|
||
})
|
||
if (code != 0) return uni.showToast({ title: msg || "支付失败", icon: "error" })
|
||
}
|
||
|
||
uni.hideLoading()
|
||
usePayStore().setScanPayData({
|
||
mchId: scanPayState.mchId,
|
||
mchName: scanPayState.mchName,
|
||
payPrice: couponPayableDisplay.value ?? Number(amount.value).toFixed(2),
|
||
})
|
||
router.jump("/pages/pay/payComplete", {}, 2)
|
||
} catch (error: any) {
|
||
uni.showToast({ title: "请求错误", icon: "error" })
|
||
uni.hideLoading()
|
||
}
|
||
}
|
||
</script>
|