cashier_app/pages/pay.vue

495 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">
<up-navbar
title="分销"
bg-color="transparent"
:placeholder="true"
@leftClick="back"
></up-navbar>
<view class="top">
<image
src="/static/market/top-bg.png"
class="image"
mode="widthFix"
></image>
</view>
<view style="height: 38rpx"></view>
<view class="chrage-box">
<view class="u-flex u-flex-between">
<view class="u-flex">
<image
src="/static/market/gold.png"
style="width: 44rpx; height: 44rpx"
></image>
<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-24 color-999"> 充值代表接受</text>
<text class="u-font-24 xieyi"> 用户隐私协议 </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" style="font-size: 48rpx">{{
item.price
}}</text>
</view>
<image
class="sel"
v-show="selChargeIndex == index"
src="/static/market/sel.png"
></image>
</view>
</view>
</view>
<view class="u-flex input-box u-m-t-30">
<text class="color-333 u-font-28 font-700">其他金额</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 class="buy" @click="buy">立即充值</view>
<view class="u-flex u-flex-between u-m-t-60">
<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>
</template>
<script setup>
import { mchRecharge } from "@/http/api/pay";
import * as distributionFlowApi from "@/http/api/market/distributionFlow.js";
import { onMounted, ref, reactive, watch } from "vue";
import { onLoad, onReachBottom } from "@dcloudio/uni-app";
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();
});
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();
}
const query = reactive({
page: 1,
size: 10,
type: "self_recharge,manual_recharge",
});
const state = reactive({
records: [],
totalRecharge: 0,
});
const isEnd = ref(false);
async function flow() {
const res = await distributionFlowApi.flow(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();
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">
.box {
position: relative;
}
.top {
position: absolute;
left: 0;
right: 0;
top: 0;
z-index: -1;
.image {
width: 100%;
}
}
.chrage-box {
width: 750rpx;
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: 22rpx;
background: linear-gradient(180deg, #f5f5f5 58.54%, #fff 104.47%);
border: 6rpx solid transparent;
transition: all 0.3s ease-in-out;
margin-right: 30rpx;
margin-bottom: 62rpx;
position: relative;
width: 220rpx;
display: flex;
justify-content: center;
&.active {
border: 6rpx solid #fe6c0e;
background: linear-gradient(180deg, #ffc29a -26.17%, #fff 64.06%);
box-shadow: 0 0 31.2rpx 2rpx #fe8b435e;
.price {
.font-bold {
color: #ff6300;
}
}
}
.sel {
width: 42rpx;
height: 42rpx;
position: absolute;
left: 50%;
transform: translateX(-50%);
bottom: -22rpx;
}
.price {
display: flex;
align-items: baseline;
flex-wrap: nowrap;
color: #5f2e0f;
}
}
.item:nth-of-type(2n) {
margin-right: 0;
}
}
.u-text-nowrap {
white-space: nowrap;
}
.input-box {
padding: 24rpx 16rpx;
border-radius: 8rpx;
background: #f6f6f6;
}
.buy {
padding: 32rpx 32rpx;
background: linear-gradient(98deg, #fe6d1100 40.64%, #ffd1b4 105.2%),
linear-gradient(259deg, #fe6d11 50.14%, #ffd1b4 114.93%);
box-shadow: 0 14rpx 30.4rpx 0 #fe8b435e;
color: #ffffff;
font-size: 32rpx;
font-weight: 700;
border-radius: 40rpx;
text-align: center;
margin-top: 48rpx;
}
.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: #fe7e00;
font-size: 48rpx;
.yuan {
font-weight: 400;
font-size: 28rpx;
}
}
}
}
</style>