cashier_app/pages/pay.vue

479 lines
12 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="box min-page">
<view class="chrage-box">
<view class="bg-fff container">
<view class="u-flex u-flex-between">
<view class="u-flex">
<view class="title-bg">
<view class="font-bold u-font-32 color-333">当前余额{{distributionFlowRes.balanceAmount}}</view>
</view>
</view>
<view>
<text class="u-font-32 color-main" @click="go.to('PAGES_DISTRIBUTION_MONEY_RECODERS')"> 查看记录</text>
</view>
</view>
<view
class="list u-m-t-32 u-flex u-m-t-60"
style="align-items: flex-start"
>
<text class="font-bold u-font-32 color-333 u-text-nowrap"
>选择金额</text
>
<view class="u-m-l-60 chargeList">
<view
class="item"
v-for="(item, index) in chargeList"
:key="index"
:class="{ active: selChargeIndex == index }"
@click="changeCharge(index)"
>
<view class="price">
<text class="u-font-28">¥ </text>
<text class="font-bold u-font-32">{{ item.price }}</text>
</view>
<image
class="sel"
v-show="selChargeIndex == index"
src="/static/iconimg/icon-sel.png"
></image>
</view>
</view>
</view>
<view class="u-flex input-box">
<text class="color-333 u-font-28 font-bold">其他金额</text>
<view class="u-flex-1 u-flex u-m-l-28">
<input
:min="0.01"
placeholder-class="u-font-24"
type="digit"
class="u-flex-1 u-font-28"
placeholder="请输入充值金额"
@input="inputEvent"
v-model="price"
/>
</view>
</view>
</view>
<view class="u-m-t-32"></view>
<my-button @click="buy">立即充值</my-button>
<view class="container u-m-t-32">
<view class="u-flex u-flex-between ">
<view class="u-flex">
<view class="title-bg">
<image src="/static/market/title-bg.png" class="image"></image>
<view class="font-bold u-font-32 color-333 u-m-l-24"
>充值记录</view
>
</view>
</view>
<view>
<text class="u-font-28 color-333">
总计:{{ state.totalRecharge || 0 }}元</text
>
</view>
</view>
<view class="recoders-list" v-if="state.records.length">
<view
class="item"
v-for="(item, index) in state.records"
:key="index"
>
<view class="u-flex u-flex-between">
<view class="u-font-28">
<view class="color-666">
<text v-if="item.type === 'self_recharge'">自助充值</text>
<text v-if="item.type === 'manual_recharge'">手动充值</text>
</view>
<view class="u-m-t-16">
<text class="color-666">时间:</text>
<text class="color-333">{{ item.createTime }}</text>
</view>
</view>
<view class="price">
<text>{{ item.changeAmount }}</text>
<text class="yuan">元</text>
</view>
</view>
</view>
<up-loadmore :status="isEnd ? 'nomore' : 'loadmore'"></up-loadmore>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { mchRecharge } from "@/http/api/pay";
import * as distributionApi from "@/http/api/market/distribution.js";
import { onMounted, ref, reactive, watch } from "vue";
import { onLoad, onReachBottom } from "@dcloudio/uni-app";
import go from "@/commons/utils/go.js";
function back() {
uni.navigateBack();
}
const options = ref({});
const price = ref("");
const chargeList = ref([
{ price: "500.00" },
{ price: "800.00" },
{ price: "1000.00" },
{ price: "2000.00" },
]);
const selChargeIndex = ref(0);
function changeCharge(index) {
selChargeIndex.value = index;
}
function parseQueryString(queryString) {
const queryParams = queryString.split("&").map((param) => param.split("="));
const params = {};
for (const [key, value] of queryParams) {
params[key] = value;
}
return params;
}
onLoad((opt) => {
console.log(opt);
if (opt.q) {
const q = decodeURIComponent(opt.q);
const params = parseQueryString(q.split("?")[1]);
Object.assign(options.value, params);
} else {
Object.assign(options.value, opt);
}
console.log(options.value);
if (options.value.shopId) {
price.value = options.value.amount;
selChargeIndex.value = chargeList.value.findIndex(
(item) => item.price * 1 === options.value.amount * 1
);
init();
}
flow();
distributionFlow();
});
let timer = null;
function inputEvent(e) {
console.log(e);
clearTimeout(timer);
let value = e.detail.value;
// 1. 只保留数字和小数点,过滤其他所有字符
value = value.replace(/[^\d.]/g, "");
// 2. 处理多个小数点(只保留第一个)
const dotIndex = value.indexOf(".");
if (dotIndex !== -1) {
// 从第二个小数点开始,移除后续所有小数点
value =
value.slice(0, dotIndex + 1) +
value.slice(dotIndex + 1).replace(/\./g, "");
}
// 3. 限制小数位最多两位
if (dotIndex !== -1 && value.length > dotIndex + 3) {
value = value.slice(0, dotIndex + 3);
}
// 4. 处理特殊情况避免以多个0开头如"000123" -> "0123"不合法,应改为"123"
// 但允许"0.12"这类合法格式
if (value.startsWith("0") && value.length > 1 && !value.startsWith("0.")) {
value = value.replace(/^0+/, "0"); // 多个0开头时只保留一个0
// 如果是"000"则清空(根据需求调整,也可保留为"0"
if (value === "0" && !value.includes(".")) {
value = "";
}
}
// 延迟更新,避免输入闪烁
timer = setTimeout(() => {
selChargeIndex.value = chargeList.value.findIndex(
(item) => item.price * 1 === value * 1
);
price.value = value;
}, 30);
}
function wxlogin() {
return new Promise((resolve, reject) => {
uni.login({
success: (res) => {
if (res.code) {
resolve(res.code);
} else {
reject(res.errMsg);
}
},
fail: (err) => {
reject(err.errMsg);
},
});
});
}
const pay = (res) => {
return new Promise((resolve, reject) => {
uni.requestPayment({
// #ifdef MP-WEIXIN
provider: "wxpay", //支付类型-固定值
partnerid: res.appId, // 微信支付商户号
timeStamp: res.timeStamp, // 时间戳(单位:秒)
nonceStr: res.nonceStr, // 随机字符串
package: res.package, // 固定值
signType: res.signType, //固定值
paySign: res.paySign, //签名
// #endif
// #ifdef MP-ALIPAY
provider: "alipay", //支付类型-固定值
orderInfo: res.tradeNo, // 微信支付商户号
// #endif
success: (res) => {
console.log("pay");
console.log(res);
// #ifdef MP-WEIXIN
uni.showToast({
title: "支付成功",
icon: "none",
});
console.log("支付成功");
resolve(true);
// #endif
// #ifdef MP-ALIPAY
if (res.resultCode == "9000") {
uni.showToast({
title: "支付成功",
icon: "none",
});
resolve(true);
} else {
uni.showToast({
title: "支付失败",
icon: "none",
});
reject(false);
}
// #endif
},
fail: (res) => {
setTimeout(() => {
uni.hideLoading();
}, 1000);
reject(false);
},
});
});
};
async function buy() {
if (price.value <= 0) {
uni.showToast({
title: "金额不能小于0",
icon: "none",
});
return;
}
const code = await wxlogin();
const shopInfo = uni.getStorageSync("shopInfo");
const res = await mchRecharge({
amount: price.value,
shopId: shopInfo.id,
code,
payType: options.value.payType || "wechatPay",
});
if (res) {
const paySuccess = await pay(res);
if (paySuccess) {
uni.showToast({
title: "支付成功",
icon: "none",
});
refresh();
}
}
}
function refresh() {
query.page = 1;
flow();
distributionFlow()
}
const query = reactive({
page: 1,
size: 10,
type: "self_recharge,manual_recharge",
});
const state = reactive({
records: [],
totalRecharge: 0,
});
const distributionFlowRes=ref({})
async function distributionFlow(){
const res=await distributionApi.distributionFlow();
distributionFlowRes.value=res
}
const isEnd = ref(false);
async function flow() {
const res = await distributionApi.moneyRecoders(query);
if (query.page == 1) {
Object.assign(state, res);
} else {
state.records = state.records.concat(res.records || []);
state.totalRecharge = res.totalRecharge || 0;
}
isEnd.value = query.page >= res.totalPage * 1;
}
onReachBottom(() => {
if (isEnd.value) {
return;
}
query.page++;
flow();
});
async function init() {
const code = await wxlogin();
distributionFlow();
const res = await mchRecharge({
...options.value,
code,
payType: options.value.payType || "wechatPay",
});
if (res) {
const paySuccess = await pay(res);
if (paySuccess) {
uni.showToast({
title: "支付成功",
icon: "none",
});
refresh();
}
}
console.log(res);
}
watch(
() => selChargeIndex.value,
(newval) => {
if (newval != -1 && price.value * 1 != chargeList.value[newval].price * 1) {
price.value = chargeList.value[newval].price;
}
},
{
immediate: true,
}
);
</script>
<style scoped lang="scss">
.min-page {
background: #f5f5f5;
}
.box {
position: relative;
}
.container {
padding: 32rpx 24rpx;
border-radius: 16rpx;
background-color: #fff;
}
.chrage-box {
border-radius: 74rpx;
background: #ffffff4d;
padding: 42rpx 28rpx;
}
.title-bg {
position: relative;
.image {
position: absolute;
height: 14rpx;
width: 94rpx;
right: -10rpx;
bottom: 0;
z-index: -1;
}
}
.xieyi {
color: #ecb592;
}
.chargeList {
display: flex;
flex-wrap: wrap;
margin-left: 68rpx;
justify-content: space-between;
.item {
padding: 16rpx 36rpx;
border-radius: 8rpx;
background-color: #f8f8f8;
border: 6rpx solid transparent;
transition: all 0.3s ease-in-out;
margin-right: 30rpx;
margin-bottom: 36rpx;
position: relative;
width: 172rpx;
display: flex;
justify-content: center;
&.active {
border: 6rpx solid $my-main-color;
.price {
color: $my-main-color;
}
}
.sel {
width: 42rpx;
height: 42rpx;
position: absolute;
left: 50%;
transform: translateX(-50%);
bottom: -22rpx;
}
.price {
display: flex;
align-items: baseline;
flex-wrap: nowrap;
color: #333;
}
}
.item:nth-of-type(2n) {
margin-right: 0;
}
}
.u-text-nowrap {
white-space: nowrap;
}
.input-box {
padding: 24rpx 16rpx;
border-radius: 8rpx;
background: #f6f6f6;
}
.recoders-list {
margin-top: 40rpx;
.item {
margin-bottom: 36rpx;
background-color: #f8f8f8;
padding: 32rpx 28rpx;
border-radius: 16rpx;
.price {
line-height: 44rpx;
font-weight: 700;
color: $my-main-color;
font-size: 44rpx;
.yuan {
font-weight: 400;
font-size: 28rpx;
}
}
}
}
</style>