项目初始化

This commit is contained in:
jerry
2025-01-21 01:46:34 +08:00
parent 364021b042
commit 48153e7761
962 changed files with 172070 additions and 0 deletions

295
pages/pay/index.vue Normal file
View File

@@ -0,0 +1,295 @@
<!-- 收银台 -->
<template>
<s-layout title="收银台">
<view class="bg-white ss-modal-box ss-flex-col">
<!-- 订单信息 -->
<view class="modal-header ss-flex-col ss-col-center ss-row-center">
<view class="money-box ss-m-b-20">
<text class="money-text">{{ fen2yuan(state.orderInfo.price) }}</text>
</view>
<view class="time-text">
<text>{{ payDescText }}</text>
</view>
</view>
<!-- 支付方式 -->
<view class="modal-content ss-flex-1">
<view class="pay-title ss-p-l-30 ss-m-y-30">选择支付方式</view>
<radio-group @change="onTapPay">
<label class="pay-type-item" v-for="item in state.payMethods" :key="item.title">
<view
v-if="!item.disabled"
class="pay-item ss-flex ss-col-center ss-row-between ss-p-x-30 border-bottom"
:class="{ 'disabled-pay-item': item.disabled }"
>
<view class="ss-flex ss-col-center">
<image
class="pay-icon"
v-if="item.disabled"
:src="sheep.$url.static('/static/img/shop/pay/cod_disabled.png')"
mode="aspectFit"
/>
<image
class="pay-icon"
v-else
:src="sheep.$url.static(item.icon)"
mode="aspectFit"
/>
<text class="pay-title">{{ item.title }}</text>
</view>
<view class="check-box ss-flex ss-col-center ss-p-l-10">
<view class="userInfo-money ss-m-r-10" v-if="item.value === 'wallet'">
余额: {{ fen2yuan(userWallet.balance) }}
</view>
<radio
:value="item.value"
color="var(--ui-BG-Main)"
style="transform: scale(0.8)"
:disabled="item.disabled"
:checked="state.payment === item.value"
/>
</view>
</view>
</label>
</radio-group>
</view>
<!-- 工具 -->
<view class="modal-footer ss-flex ss-row-center ss-col-center ss-m-t-80 ss-m-b-40">
<button v-if="state.payStatus === 0" class="ss-reset-button past-due-btn">
检测支付环境中
</button>
<button v-else-if="state.payStatus === -1" class="ss-reset-button past-due-btn" disabled>
支付已过期
</button>
<button
v-else
class="ss-reset-button save-btn"
@tap="onPay"
:disabled="state.payStatus !== 1"
:class="{ 'disabled-btn': state.payStatus !== 1 }"
>
立即支付
</button>
</view>
</view>
</s-layout>
</template>
<script setup>
import { computed, reactive } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
import sheep from '@/sheep';
import { fen2yuan, useDurationTime } from '@/sheep/hooks/useGoods';
import PayOrderApi from '@/sheep/api/pay/order';
import PayChannelApi from '@/sheep/api/pay/channel';
import { getPayMethods } from '@/sheep/platform/pay';
const userWallet = computed(() => sheep.$store('user').userWallet);
// 检测支付环境
const state = reactive({
orderType: 'goods', // 订单类型; goods - 商品订单, recharge - 充值订单
orderInfo: {}, // 支付单信息
payStatus: 0, // 0=检测支付环境, -2=未查询到支付单信息, -1=支付已过期, 1=待支付2=订单已支付
payMethods: [], // 可选的支付方式
payment: '', // 选中的支付方式
});
const onPay = () => {
if (state.payment === '') {
sheep.$helper.toast('请选择支付方式');
return;
}
if (state.payment === 'wallet') {
uni.showModal({
title: '提示',
content: '确定要支付吗?',
success: function (res) {
if (res.confirm) {
sheep.$platform.pay(state.payment, state.orderType, state.orderInfo.id);
}
},
});
} else {
sheep.$platform.pay(state.payment, state.orderType, state.orderInfo.id);
}
};
// 支付文案提示
const payDescText = computed(() => {
if (state.payStatus === 2) {
return '该订单已支付';
}
if (state.payStatus === 1) {
const time = useDurationTime(state.orderInfo.expireTime);
if (time.ms <= 0) {
state.payStatus = -1;
return '';
}
return `剩余支付时间 ${time.h}:${time.m}:${time.s} `;
}
if (state.payStatus === -2) {
return '未查询到支付单信息';
}
return '';
});
// 状态转换payOrder.status => payStatus
function checkPayStatus() {
if (state.orderInfo.status === 10
|| state.orderInfo.status === 20 ) { // 支付成功
state.payStatus = 2;
return;
}
if (state.orderInfo.status === 30) { // 支付关闭
state.payStatus = -1;
return;
}
state.payStatus = 1; // 待支付
}
// 切换支付方式
function onTapPay(e) {
state.payment = e.detail.value;
}
// 设置支付订单信息
async function setOrder(id) {
// 获得支付订单信息
const { data, code } = await PayOrderApi.getOrder(id);
if (code !== 0 || !data) {
state.payStatus = -2;
return;
}
state.orderInfo = data;
// 获得支付方式
await setPayMethods();
// 设置支付状态
checkPayStatus();
}
// 获得支付方式
async function setPayMethods() {
const { data, code } = await PayChannelApi.getEnableChannelCodeList(state.orderInfo.appId)
if (code !== 0) {
return
}
state.payMethods = getPayMethods(data)
state.payMethods.find(item => {
if (item.value && !item.disabled) {
state.payment = item.value;
return true;
}
});
}
onLoad((options) => {
if (sheep.$platform.name === 'WechatOfficialAccount'
&& sheep.$platform.os === 'ios'
&& !sheep.$platform.landingPage.includes('pages/pay/index')) {
location.reload();
return;
}
// 获得支付订单信息
let id = options.id;
if (options.orderType) {
state.orderType = options.orderType;
}
setOrder(id);
// 刷新钱包的缓存
sheep.$store('user').getWallet();
});
</script>
<style lang="scss" scoped>
.pay-icon {
width: 36rpx;
height: 36rpx;
margin-right: 26rpx;
}
.ss-modal-box {
// max-height: 1000rpx;
.modal-header {
position: relative;
padding: 60rpx 20rpx 40rpx;
.money-text {
color: $red;
font-size: 46rpx;
font-weight: bold;
font-family: OPPOSANS;
&::before {
content: '¥';
font-size: 30rpx;
}
}
.time-text {
font-size: 26rpx;
color: $gray-b;
}
.close-icon {
position: absolute;
top: 10rpx;
right: 20rpx;
font-size: 46rpx;
opacity: 0.2;
}
}
.modal-content {
overflow-y: auto;
.pay-title {
font-size: 26rpx;
font-weight: 500;
color: #333333;
}
.pay-tip {
font-size: 26rpx;
color: #bbbbbb;
}
.pay-item {
height: 86rpx;
}
.disabled-pay-item {
.pay-title {
color: #999999;
}
}
.userInfo-money {
font-size: 26rpx;
color: #bbbbbb;
line-height: normal;
}
}
.save-btn {
width: 710rpx;
height: 80rpx;
border-radius: 40rpx;
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
color: $white;
}
.disabled-btn {
background: #e5e5e5;
color: #999999;
}
.past-due-btn {
width: 710rpx;
height: 80rpx;
border-radius: 40rpx;
background-color: #999;
color: #fff;
}
}
</style>

308
pages/pay/point/index.vue Normal file
View File

@@ -0,0 +1,308 @@
<!-- 收银台 -->
<template>
<s-layout title="收银台">
<view class="bg-white ss-modal-box ss-flex-col">
<!-- 订单信息 -->
<view class="modal-header ss-flex-col ss-col-center ss-row-center">
<view class="money-box ss-m-b-20">
<text class="money-text">{{ fen2yuan(state.orderInfo.price) }}</text>
</view>
<view class="time-text">
<text>{{ payDescText }}</text>
</view>
</view>
<!-- 支付方式 -->
<view class="modal-content ss-flex-1">
<view class="pay-title ss-p-l-30 ss-m-y-30">选择支付方式</view>
<radio-group @change="onTapPay">
<label class="pay-type-item" v-for="item in state.payMethods" :key="item.title">
<view
v-if="!item.disabled"
class="pay-item ss-flex ss-col-center ss-row-between ss-p-x-30 border-bottom"
:class="{ 'disabled-pay-item': item.disabled }"
>
<view class="ss-flex ss-col-center">
<image
class="pay-icon"
v-if="item.disabled"
:src="sheep.$url.static('/static/img/shop/pay/cod_disabled.png')"
mode="aspectFit"
/>
<image
class="pay-icon"
v-else
:src="sheep.$url.static(item.icon)"
mode="aspectFit"
/>
<text class="pay-title">{{ item.title }}</text>
</view>
<view class="check-box ss-flex ss-col-center ss-p-l-10">
<view class="userInfo-money ss-m-r-10" v-if="item.value === 'wallet'">
余额: {{ fen2yuan(userWallet.balance) }}
</view>
<radio
:value="item.value"
color="var(--ui-BG-Main)"
style="transform: scale(0.8)"
:disabled="item.disabled"
:checked="state.payment === item.value"
/>
</view>
</view>
</label>
</radio-group>
</view>
<!-- 工具 -->
<view class="modal-footer ss-flex ss-row-center ss-col-center ss-m-t-80 ss-m-b-40">
<button v-if="state.payStatus === 0" class="ss-reset-button past-due-btn">
检测支付环境中
</button>
<button v-else-if="state.payStatus === -1" class="ss-reset-button past-due-btn" disabled>
支付已过期
</button>
<button
v-else
class="ss-reset-button save-btn"
@tap="onPay"
:disabled="state.payStatus !== 1"
:class="{ 'disabled-btn': state.payStatus !== 1 }"
>
立即支付
</button>
</view>
</view>
</s-layout>
</template>
<script setup>
import { computed, reactive } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
import sheep from '@/sheep';
import { fen2yuan, useDurationTime } from '@/sheep/hooks/useGoods';
import PayOrderApi from '@/sheep/api/pay/order';
import PayChannelApi from '@/sheep/api/pay/channel';
import { getPayMethods } from '@/sheep/platform/pay';
const userWallet = computed(() => sheep.$store('user').userWallet);
// 检测支付环境
const state = reactive({
orderType: 'goods', // 订单类型; goods - 商品订单, recharge - 充值订单
orderInfo: {}, // 支付单信息
payStatus: 0, // 0=检测支付环境, -2=未查询到支付单信息, -1=支付已过期, 1=待支付2=订单已支付
payMethods: [], // 可选的支付方式
payment: '', // 选中的支付方式
});
const onPay = () => {
if (state.payment === '') {
sheep.$helper.toast('请选择支付方式');
return;
}
if (state.payment === 'wallet') {
if(state.orderInfo.price > userWallet.value.balance){
uni.showModal({
title: '账户余额不足',
confirmText: '去充值',
success: function (res) {
if (res.confirm) {
sheep.$router.go('/pages/pay/recharge');
}
},
});
return;
}
uni.showModal({
title: '提示',
content: '确定要支付吗?',
success: function (res) {
if (res.confirm) {
sheep.$platform.pay(state.payment, state.orderType, state.orderInfo.id, 'point');
}
},
});
} else {
sheep.$platform.pay(state.payment, state.orderType, state.orderInfo.id, 'point');
}
};
// 支付文案提示
const payDescText = computed(() => {
if (state.payStatus === 2) {
return '该订单已支付';
}
if (state.payStatus === 1) {
const time = useDurationTime(state.orderInfo.expireTime);
if (time.ms <= 0) {
state.payStatus = -1;
return '';
}
return `剩余支付时间 ${time.h}:${time.m}:${time.s} `;
}
if (state.payStatus === -2) {
return '未查询到支付单信息';
}
return '';
});
// 状态转换payOrder.status => payStatus
function checkPayStatus() {
if (state.orderInfo.status === 10
|| state.orderInfo.status === 20 ) { // 支付成功
state.payStatus = 2;
return;
}
if (state.orderInfo.status === 30) { // 支付关闭
state.payStatus = -1;
return;
}
state.payStatus = 1; // 待支付
}
// 切换支付方式
function onTapPay(e) {
state.payment = e.detail.value;
}
// 设置支付订单信息
async function setOrder(id) {
// 获得支付订单信息
const { data, code } = await PayOrderApi.getOrder(id);
if (code !== 0 || !data) {
state.payStatus = -2;
return;
}
state.orderInfo = data;
// 获得支付方式
await setPayMethods();
// 设置支付状态
checkPayStatus();
}
// 获得支付方式
async function setPayMethods() {
const { data, code } = await PayChannelApi.getEnableChannelCodeList(state.orderInfo.appId)
if (code !== 0) {
return
}
state.payMethods = getPayMethods(data)
state.payMethods.find(item => {
if (item.value && !item.disabled) {
state.payment = item.value;
return true;
}
});
}
onLoad((options) => {
if (sheep.$platform.name === 'WechatOfficialAccount'
&& sheep.$platform.os === 'ios'
&& !sheep.$platform.landingPage.includes('pages/pay/point/index')) {
location.reload();
return;
}
// 获得支付订单信息
let id = options.id;
if (options.orderType) {
state.orderType = options.orderType;
}
setOrder(id);
// 刷新钱包的缓存
sheep.$store('user').getWallet();
});
</script>
<style lang="scss" scoped>
.pay-icon {
width: 36rpx;
height: 36rpx;
margin-right: 26rpx;
}
.ss-modal-box {
// max-height: 1000rpx;
.modal-header {
position: relative;
padding: 60rpx 20rpx 40rpx;
.money-text {
color: $red;
font-size: 46rpx;
font-weight: bold;
font-family: OPPOSANS;
&::before {
content: '¥';
font-size: 30rpx;
}
}
.time-text {
font-size: 26rpx;
color: $gray-b;
}
.close-icon {
position: absolute;
top: 10rpx;
right: 20rpx;
font-size: 46rpx;
opacity: 0.2;
}
}
.modal-content {
overflow-y: auto;
.pay-title {
font-size: 26rpx;
font-weight: 500;
color: #333333;
}
.pay-tip {
font-size: 26rpx;
color: #bbbbbb;
}
.pay-item {
height: 86rpx;
}
.disabled-pay-item {
.pay-title {
color: #999999;
}
}
.userInfo-money {
font-size: 26rpx;
color: #bbbbbb;
line-height: normal;
}
}
.save-btn {
width: 710rpx;
height: 80rpx;
border-radius: 40rpx;
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
color: $white;
}
.disabled-btn {
background: #e5e5e5;
color: #999999;
}
.past-due-btn {
width: 710rpx;
height: 80rpx;
border-radius: 40rpx;
background-color: #999;
color: #fff;
}
}
</style>

167
pages/pay/recharge-log.vue Normal file
View File

@@ -0,0 +1,167 @@
<!-- 充值记录 -->
<template>
<s-layout class="widthdraw-log-wrap" title="充值记录">
<!-- 记录卡片 -->
<view class="wallet-log-box ss-p-b-30">
<view class="log-list" v-for="item in state.pagination.list" :key="item">
<view class="head ss-flex ss-col-center ss-row-between">
<view class="title">充值钻石</view>
<view class="num" :class="item.refundStatus === 10 ? 'danger-color' : 'success-color'">
{{ fen2yuan(item.payPrice) }}
<text v-if="item.bonusPrice > 0">赠送 {{ fen2yuan(item.bonusPrice) }} </text>
</view>
</view>
<view class="status-box item ss-flex ss-col-center ss-row-between">
<view class="item-title">支付状态</view>
<view
class="status-text"
:class="item.refundStatus === 10 ? 'danger-color' : 'success-color'"
>
{{ item.refundStatus === 10 ? '已退款' : '已支付' }}
</view>
</view>
<view class="time-box item ss-flex ss-col-center ss-row-between">
<text class="item-title">充值渠道</text>
<view class="time ss-ellipsis-1">{{ item.payChannelName }}</view>
</view>
<view class="time-box item ss-flex ss-col-center ss-row-between">
<text class="item-title">充值单号</text>
<view class="time"> {{ item.payOrderChannelOrderNo }} </view>
</view>
<view class="time-box item ss-flex ss-col-center ss-row-between">
<text class="item-title">充值时间</text>
<view class="time">
{{ sheep.$helper.timeFormat(item.payTime, 'yyyy-mm-dd hh:MM:ss') }}</view
>
</view>
</view>
</view>
<s-empty
v-if="state.pagination.total === 0"
icon="/static/comment-empty.png"
text="暂无充值记录"
/>
<uni-load-more
v-if="state.pagination.total > 0"
:status="state.loadStatus"
:content-text="{
contentdown: '上拉加载更多',
}"
@tap="loadMore"
/>
</s-layout>
</template>
<script setup>
import { reactive } from 'vue';
import { onLoad, onReachBottom } from '@dcloudio/uni-app';
import _ from 'lodash-es';
import PayWalletApi from '@/sheep/api/pay/wallet';
import sheep from '@/sheep';
import { fen2yuan } from '../../sheep/hooks/useGoods';
const state = reactive({
pagination: {
list: [],
total: 0,
pageNo: 1,
pageSize: 5,
},
loadStatus: '',
});
async function getLogList(page = 1, list_rows = 5) {
const { code, data } = await PayWalletApi.getWalletRechargePage({
pageNo: page,
pageSize: list_rows,
});
if (code !== 0) {
return;
}
state.pagination.list = _.concat(state.pagination.list, data.list);
state.pagination.total = data.total;
state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore';
}
// 加载更多
function loadMore() {
if (state.loadStatus === 'noMore') {
return;
}
state.pagination.pageNo++;
getLogList();
}
onLoad(() => {
getLogList();
});
onReachBottom(() => {
loadMore();
});
</script>
<style lang="scss" scoped>
// 记录卡片
.log-list {
min-height: 213rpx;
background: $white;
margin-bottom: 10rpx;
padding-bottom: 10rpx;
.head {
padding: 0 35rpx;
height: 80rpx;
border-bottom: 1rpx solid $gray-e;
margin-bottom: 20rpx;
.title {
font-size: 28rpx;
font-weight: 500;
color: $dark-3;
}
.num {
font-size: 28rpx;
font-weight: 500;
}
}
.item {
padding: 0 30rpx 10rpx;
.item-icon {
color: $gray-d;
font-size: 36rpx;
margin-right: 8rpx;
}
.item-title {
width: 180rpx;
font-size: 24rpx;
font-weight: 400;
color: #666666;
}
.status-text {
font-size: 24rpx;
font-weight: 500;
}
.time {
font-size: 24rpx;
font-weight: 400;
color: #c0c0c0;
}
}
}
.warning-color {
color: #faad14;
}
.danger-color {
color: #ff4d4f;
}
.success-color {
color: #67c23a;
}
</style>

264
pages/pay/recharge.vue Normal file
View File

@@ -0,0 +1,264 @@
<!-- 充值界面 -->
<template>
<s-layout title="充值钻石" class="withdraw-wrap" navbar="inner">
<view class="wallet-num-box ss-flex ss-col-center ss-row-between" :style="[
{
marginTop: '-' + Number(statusBarHeight + 88 + 22) + 'rpx',
paddingTop: Number(statusBarHeight + 108 + 22) + 'rpx',
},
]">
<view class="">
<view class="num-title">当前钻石</view>
<view class="wallet-num">{{ fen2yuan(userWallet.balance) }}</view>
</view>
<button class="ss-reset-button log-btn" @tap="sheep.$router.go('/pages/user/wallet/money')">
钻石明细
</button>
</view>
<view class="recharge-box">
<view class="recharge-card-box">
<view class="input-label ss-m-b-50">充值钻石</view>
<!-- <view class="input-box ss-flex border-bottom ss-p-b-20">
<view class="unit"></view>
<uni-easyinput v-model="state.recharge_money" type="digit" placeholder="请输入充值金额"
:inputBorder="false" />
</view> -->
<view class="face-value-box ss-flex ss-flex-wrap ss-m-y-40">
<button class="ss-reset-button face-value-btn" v-for="item in state.packageList" :key="item.money"
:class="[{ 'btn-active': state.recharge_money === fen2yuan(item.payPrice) }]"
@tap="onCard(item.payPrice)">
<text class="face-value-title">{{ fen2yuan(item.payPrice) }}</text>
<view v-if="item.bonusPrice" class="face-value-tag">
{{ fen2yuan(item.bonusPrice) }}
</view>
</button>
</view>
<button class="ss-reset-button save-btn ui-BG-Main-Gradient ss-m-t-60 ui-Shadow-Main" @tap="onConfirm">
确认充值
</button>
</view>
</view>
</s-layout>
</template>
<script setup>
import { computed, reactive } from 'vue';
import sheep from '@/sheep';
import { onLoad } from '@dcloudio/uni-app';
import { fen2yuan } from '@/sheep/hooks/useGoods';
import PayWalletApi from '@/sheep/api/pay/wallet';
import { WxaSubscribeTemplate } from '@/sheep/util/const';
const userWallet = computed(() => sheep.$store('user').userWallet);
const statusBarHeight = sheep.$platform.device.statusBarHeight * 2;
const headerBg = sheep.$url.css('/static/img/shop/user/withdraw_bg.png');
const state = reactive({
recharge_money: '', // 输入的充值金额
packageList: [],
});
// 点击卡片,选择充值金额
function onCard(e) {
state.recharge_money = fen2yuan(e);
}
// 获得钱包充值套餐列表
async function getRechargeTabs() {
const { code, data } = await PayWalletApi.getWalletRechargePackageList();
if (code !== 0) {
return;
}
state.packageList = data;
}
// 发起支付
async function onConfirm() {
if(state.recharge_money == 0){
sheep.$helper.toast('请选择充值套餐');
return;
}
const { code, data } = await PayWalletApi.createWalletRecharge({
packageId: state.packageList.find((item) => fen2yuan(item.payPrice) === state.recharge_money)?.id,
payPrice: state.recharge_money * 100,
});
if (code !== 0) {
return;
}
// #ifdef MP
sheep.$platform.useProvider('wechat').subscribeMessage(WxaSubscribeTemplate.PAY_WALLET_RECHARGER_SUCCESS);
// #endif
sheep.$router.go('/pages/pay/index', {
id: data.payOrderId,
orderType: 'recharge',
});
}
onLoad(() => {
getRechargeTabs();
});
</script>
<style lang="scss" scoped>
:deep() {
.uni-input-input {
font-family: OPPOSANS !important;
}
}
.wallet-num-box {
padding: 0 40rpx 80rpx;
background: var(--ui-BG-Main) v-bind(headerBg) center/750rpx 100% no-repeat;
border-radius: 0 0 5% 5%;
.num-title {
font-size: 26rpx;
font-weight: 500;
color: $white;
margin-bottom: 20rpx;
}
.wallet-num {
font-size: 60rpx;
font-weight: 500;
color: $white;
font-family: OPPOSANS;
}
.log-btn {
width: 170rpx;
height: 60rpx;
line-height: 60rpx;
border: 1rpx solid $white;
border-radius: 30rpx;
padding: 0;
font-size: 26rpx;
font-weight: 500;
color: $white;
}
}
.recharge-box {
position: relative;
padding: 0 30rpx;
margin-top: -60rpx;
}
.save-btn {
width: 620rpx;
height: 86rpx;
border-radius: 44rpx;
font-size: 30rpx;
}
.recharge-card-box {
width: 690rpx;
background: var(--ui-BG);
border-radius: 20rpx;
padding: 30rpx;
box-sizing: border-box;
.input-label {
font-size: 30rpx;
font-weight: 500;
color: #333;
}
.unit {
display: flex;
align-items: center;
font-size: 48rpx;
font-weight: 500;
}
.uni-easyinput__placeholder-class {
font-size: 30rpx;
height: 60rpx;
display: flex;
align-items: center;
}
:deep(.uni-easyinput__content-input) {
font-size: 48rpx;
}
.face-value-btn {
width: 200rpx;
height: 144rpx;
border: 1px solid var(--ui-BG-Main);
border-radius: 10rpx;
position: relative;
z-index: 1;
margin-bottom: 15rpx;
margin-right: 15rpx;
&:nth-of-type(3n) {
margin-right: 0;
}
.face-value-title {
font-size: 36rpx;
font-weight: 500;
color: var(--ui-BG-Main);
font-family: OPPOSANS;
&::after {
content: '钻';
font-size: 24rpx;
margin-left: 6rpx;
}
}
.face-value-tag {
position: absolute;
z-index: 2;
height: 40rpx;
line-height: 40rpx;
background: var(--ui-BG-Main);
opacity: 0.8;
border-radius: 10rpx 0 20rpx 0;
top: 0;
left: -2rpx;
padding: 0 16rpx;
font-size: 22rpx;
color: $white;
font-family: OPPOSANS;
}
&::before {
position: absolute;
content: ' ';
width: 100%;
height: 100%;
background: var(--ui-BG-Main);
opacity: 0.1;
z-index: 0;
left: 0;
top: 0;
}
}
.btn-active {
z-index: 1;
&::before {
content: '';
background: var(--ui-BG-Main);
opacity: 1;
}
.face-value-title {
color: $white;
position: relative;
z-index: 1;
font-family: OPPOSANS;
}
.face-value-tag {
background: $white;
color: var(--ui-BG-Main);
font-family: OPPOSANS;
}
}
}
</style>

318
pages/pay/result.vue Normal file
View File

@@ -0,0 +1,318 @@
<!-- 支付结果页面 -->
<template>
<s-layout title="支付结果" :bgStyle="{ color: '#FFF' }">
<view class="pay-result-box ss-flex-col ss-row-center ss-col-center">
<!-- 信息展示 -->
<view class="pay-waiting ss-m-b-30" v-if="payResult === 'waiting'" />
<image
class="pay-img ss-m-b-30"
v-if="payResult === 'success'"
:src="sheep.$url.static('/static/img/shop/order/order_pay_success.gif')"
/>
<image
class="pay-img ss-m-b-30"
v-if="['failed', 'closed'].includes(payResult)"
:src="sheep.$url.static('/static/img/shop/order/order_paty_fail.gif')"
/>
<view class="tip-text ss-m-b-30" v-if="payResult === 'success'">支付成功</view>
<view class="tip-text ss-m-b-30" v-if="payResult === 'failed'">支付失败</view>
<view class="tip-text ss-m-b-30" v-if="payResult === 'closed'">该订单已关闭</view>
<view class="tip-text ss-m-b-30" v-if="payResult === 'waiting'">检测支付结果...</view>
<view class="pay-total-num ss-flex" v-if="payResult === 'success'">
<view>{{ fen2yuan(state.orderInfo.price) }}</view>
</view>
<!-- 操作区 -->
<view class="btn-box ss-flex ss-row-center ss-m-t-50">
<button class="back-btn ss-reset-button" @tap="sheep.$router.go('/pages/tabbar/index')">
返回首页
</button>
<button
class="check-btn ss-reset-button"
v-if="payResult === 'failed'"
@tap="
sheep.$router.redirect('/pages/pay/index', { id: state.id, orderType: state.orderType })
"
>
重新支付
</button>
<button class="check-btn ss-reset-button" v-if="payResult === 'success'" @tap="onOrder">
查看订单
</button>
<button
class="check-btn ss-reset-button"
v-if="payResult === 'success' && state.tradeOrder.type === 3"
@tap="sheep.$router.redirect('/pages/activity/groupon/order')"
>
我的拼团
</button>
</view>
<!-- #ifdef MP -->
<view
class="subscribe-box ss-flex ss-m-t-44"
v-if="showSubscribeBtn && state.orderType === 'goods'"
>
<image class="subscribe-img" :src="sheep.$url.static('/static/img/shop/order/cargo.png')" />
<view class="subscribe-title ss-m-r-48 ss-m-l-16">获取实时发货信息与订单状态</view>
<view class="subscribe-start" @tap="subscribeMessage">立即订阅</view>
</view>
<!-- #endif -->
</view>
</s-layout>
</template>
<script setup>
import { onLoad, onHide, onShow } from '@dcloudio/uni-app';
import { reactive, computed, ref } from 'vue';
import { isEmpty } from 'lodash-es';
import sheep from '@/sheep';
import PayOrderApi from '@/sheep/api/pay/order';
import { fen2yuan } from '@/sheep/hooks/useGoods';
import OrderApi from '@/sheep/api/trade/order';
import { WxaSubscribeTemplate } from '@/sheep/util/const';
const state = reactive({
id: 0, // 支付单号
orderType: 'goods', // 订单类型
result: 'unpaid', // 支付状态
orderInfo: {}, // 支付订单信息
tradeOrder: {}, // 商品订单信息,只有在 orderType 为 goods 才会请求。目的:【我的拼团】按钮的展示
counter: 0, // 获取结果次数
});
// 支付结果 result => payResult
const payResult = computed(() => {
if (state.result === 'unpaid') {
return 'waiting';
}
if (state.result === 'paid') {
return 'success';
}
if (state.result === 'failed') {
return 'failed';
}
if (state.result === 'closed') {
return 'closed';
}
});
// 获得订单信息
async function getOrderInfo(id) {
state.counter++;
// 1. 加载订单信息
const { data, code } = await PayOrderApi.getOrder(id);
if (code === 0) {
state.orderInfo = data;
if (!state.orderInfo || state.orderInfo.status === 30) {
// 支付关闭
state.result = 'closed';
return;
}
if (state.orderInfo.status !== 0) {
sheep.$store('user').getWallet();
// 非待支付,可能是已支付,可能是已退款
state.result = 'paid';
// #ifdef MP
uni.showModal({
title: '支付结果',
showCancel: false, // 不要取消按钮
content: '支付成功',
success: () => {
// 订阅只能由用户主动触发,只能包一层 showModal 诱导用户点击
autoSubscribeMessage();
},
});
// #endif
// 特殊:获得商品订单信息
if (state.orderType === 'goods') {
const { data, code } = await OrderApi.getOrder(state.orderInfo.merchantOrderId);
if (code === 0) {
state.tradeOrder = data;
}
}
return;
}
}
// 2.1 情况三一:未支付,且轮询次数小于三次,则继续轮询
if (state.counter < 3 && state.result === 'unpaid') {
setTimeout(() => {
getOrderInfo(id);
}, 1500);
}
// 2.2 情况二:超过三次检测才判断为支付失败
if (state.counter >= 3) {
state.result = 'failed';
}
}
function onOrder() {
if (state.orderType === 'recharge') {
sheep.$router.redirect('/pages/pay/recharge-log');
} else {
sheep.$router.redirect('/pages/order/list');
}
}
// #ifdef MP
const showSubscribeBtn = ref(false); // 默认隐藏
const SUBSCRIBE_BTN_STATUS_STORAGE_KEY = 'subscribe_btn_status';
function subscribeMessage() {
if (state.orderType !== 'goods') {
return;
}
const event = [WxaSubscribeTemplate.TRADE_ORDER_DELIVERY];
if (state.tradeOrder.type === 3) {
event.push(WxaSubscribeTemplate.PROMOTION_COMBINATION_SUCCESS);
}
sheep.$platform.useProvider('wechat').subscribeMessage(event, () => {
// 订阅后记录一下订阅状态
uni.removeStorageSync(SUBSCRIBE_BTN_STATUS_STORAGE_KEY);
uni.setStorageSync(SUBSCRIBE_BTN_STATUS_STORAGE_KEY, '已订阅');
// 隐藏订阅按钮
showSubscribeBtn.value = false;
});
}
async function autoSubscribeMessage() {
// 1. 校验是否手动订阅过
const subscribeBtnStatus = uni.getStorageSync(SUBSCRIBE_BTN_STATUS_STORAGE_KEY);
if (!subscribeBtnStatus) {
showSubscribeBtn.value = true;
return;
}
// 2. 订阅消息
subscribeMessage();
}
// #endif
onLoad(async (options) => {
// 支付订单号
if (options.id) {
state.id = options.id;
}
// 订单类型
if (options.orderType) {
state.orderType = options.orderType;
}
// 支付结果传值过来是失败,则直接显示失败界面
if (options.payState === 'fail') {
state.result = 'failed';
} else {
// 轮询三次检测订单支付结果
await getOrderInfo(state.id);
}
});
onShow(() => {
if (isEmpty(state.orderInfo)) {
return;
}
getOrderInfo(state.id);
});
onHide(() => {
state.result = 'unpaid';
state.counter = 0;
});
</script>
<style lang="scss" scoped>
@keyframes rotation {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.score-img {
width: 36rpx;
height: 36rpx;
margin: 0 4rpx;
}
.pay-result-box {
padding: 60rpx 0;
.pay-waiting {
margin-top: 20rpx;
width: 60rpx;
height: 60rpx;
border: 10rpx solid rgb(233, 231, 231);
border-bottom-color: rgb(204, 204, 204);
border-radius: 50%;
display: inline-block;
// -webkit-animation: rotation 1s linear infinite;
animation: rotation 1s linear infinite;
}
.pay-img {
width: 130rpx;
height: 130rpx;
}
.tip-text {
font-size: 30rpx;
font-weight: bold;
color: #333333;
}
.pay-total-num {
font-size: 36rpx;
font-weight: 500;
color: #333333;
font-family: OPPOSANS;
}
.btn-box {
width: 100%;
.back-btn {
width: 190rpx;
height: 70rpx;
font-size: 28rpx;
border: 2rpx solid #dfdfdf;
border-radius: 35rpx;
font-weight: 400;
color: #595959;
}
.check-btn {
width: 190rpx;
height: 70rpx;
font-size: 28rpx;
border: 2rpx solid #dfdfdf;
border-radius: 35rpx;
font-weight: 400;
color: #595959;
margin-left: 32rpx;
}
}
.subscribe-box {
.subscribe-img {
width: 44rpx;
height: 44rpx;
}
.subscribe-title {
font-weight: 500;
font-size: 32rpx;
line-height: 36rpx;
color: #434343;
}
.subscribe-start {
color: var(--ui-BG-Main);
font-weight: 700;
font-size: 32rpx;
line-height: 36rpx;
}
}
}
</style>

308
pages/pay/reward/index.vue Normal file
View File

@@ -0,0 +1,308 @@
<!-- 收银台 -->
<template>
<s-layout title="收银台">
<view class="bg-white ss-modal-box ss-flex-col">
<!-- 订单信息 -->
<view class="modal-header ss-flex-col ss-col-center ss-row-center">
<view class="money-box ss-m-b-20">
<text class="money-text">{{ fen2yuan(state.orderInfo.price) }}</text>
</view>
<view class="time-text">
<text>{{ payDescText }}</text>
</view>
</view>
<!-- 支付方式 -->
<view class="modal-content ss-flex-1">
<view class="pay-title ss-p-l-30 ss-m-y-30">选择支付方式</view>
<radio-group @change="onTapPay">
<label class="pay-type-item" v-for="item in state.payMethods" :key="item.title">
<view
v-if="!item.disabled"
class="pay-item ss-flex ss-col-center ss-row-between ss-p-x-30 border-bottom"
:class="{ 'disabled-pay-item': item.disabled }"
>
<view class="ss-flex ss-col-center">
<image
class="pay-icon"
v-if="item.disabled"
:src="sheep.$url.static('/static/img/shop/pay/cod_disabled.png')"
mode="aspectFit"
/>
<image
class="pay-icon"
v-else
:src="sheep.$url.static(item.icon)"
mode="aspectFit"
/>
<text class="pay-title">{{ item.title }}</text>
</view>
<view class="check-box ss-flex ss-col-center ss-p-l-10">
<view class="userInfo-money ss-m-r-10" v-if="item.value === 'wallet'">
余额: {{ fen2yuan(userWallet.balance) }}
</view>
<radio
:value="item.value"
color="var(--ui-BG-Main)"
style="transform: scale(0.8)"
:disabled="item.disabled"
:checked="state.payment === item.value"
/>
</view>
</view>
</label>
</radio-group>
</view>
<!-- 工具 -->
<view class="modal-footer ss-flex ss-row-center ss-col-center ss-m-t-80 ss-m-b-40">
<button v-if="state.payStatus === 0" class="ss-reset-button past-due-btn">
检测支付环境中
</button>
<button v-else-if="state.payStatus === -1" class="ss-reset-button past-due-btn" disabled>
支付已过期
</button>
<button
v-else
class="ss-reset-button save-btn"
@tap="onPay"
:disabled="state.payStatus !== 1"
:class="{ 'disabled-btn': state.payStatus !== 1 }"
>
立即支付
</button>
</view>
</view>
</s-layout>
</template>
<script setup>
import { computed, reactive } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
import sheep from '@/sheep';
import { fen2yuan, useDurationTime } from '@/sheep/hooks/useGoods';
import PayOrderApi from '@/sheep/api/pay/order';
import PayChannelApi from '@/sheep/api/pay/channel';
import { getPayMethods } from '@/sheep/platform/pay';
const userWallet = computed(() => sheep.$store('user').userWallet);
// 检测支付环境
const state = reactive({
orderType: 'goods', // 订单类型; goods - 商品订单, recharge - 充值订单
orderInfo: {}, // 支付单信息
payStatus: 0, // 0=检测支付环境, -2=未查询到支付单信息, -1=支付已过期, 1=待支付2=订单已支付
payMethods: [], // 可选的支付方式
payment: '', // 选中的支付方式
});
const onPay = () => {
if (state.payment === '') {
sheep.$helper.toast('请选择支付方式');
return;
}
if (state.payment === 'wallet') {
if(state.orderInfo.price > userWallet.value.balance){
uni.showModal({
title: '账户余额不足',
confirmText: '去充值',
success: function (res) {
if (res.confirm) {
sheep.$router.go('/pages/pay/recharge');
}
},
});
return;
}
uni.showModal({
title: '提示',
content: '确定要支付吗?',
success: function (res) {
if (res.confirm) {
sheep.$platform.pay(state.payment, state.orderType, state.orderInfo.id, 'reward');
}
},
});
} else {
sheep.$platform.pay(state.payment, state.orderType, state.orderInfo.id, 'reward');
}
};
// 支付文案提示
const payDescText = computed(() => {
if (state.payStatus === 2) {
return '该订单已支付';
}
if (state.payStatus === 1) {
const time = useDurationTime(state.orderInfo.expireTime);
if (time.ms <= 0) {
state.payStatus = -1;
return '';
}
return `剩余支付时间 ${time.h}:${time.m}:${time.s} `;
}
if (state.payStatus === -2) {
return '未查询到支付单信息';
}
return '';
});
// 状态转换payOrder.status => payStatus
function checkPayStatus() {
if (state.orderInfo.status === 10
|| state.orderInfo.status === 20 ) { // 支付成功
state.payStatus = 2;
return;
}
if (state.orderInfo.status === 30) { // 支付关闭
state.payStatus = -1;
return;
}
state.payStatus = 1; // 待支付
}
// 切换支付方式
function onTapPay(e) {
state.payment = e.detail.value;
}
// 设置支付订单信息
async function setOrder(id) {
// 获得支付订单信息
const { data, code } = await PayOrderApi.getOrder(id);
if (code !== 0 || !data) {
state.payStatus = -2;
return;
}
state.orderInfo = data;
// 获得支付方式
await setPayMethods();
// 设置支付状态
checkPayStatus();
}
// 获得支付方式
async function setPayMethods() {
const { data, code } = await PayChannelApi.getEnableChannelCodeList(state.orderInfo.appId)
if (code !== 0) {
return
}
state.payMethods = getPayMethods(data)
state.payMethods.find(item => {
if (item.value && !item.disabled) {
state.payment = item.value;
return true;
}
});
}
onLoad((options) => {
if (sheep.$platform.name === 'WechatOfficialAccount'
&& sheep.$platform.os === 'ios'
&& !sheep.$platform.landingPage.includes('pages/pay/reward/index')) {
location.reload();
return;
}
// 获得支付订单信息
let id = options.id;
if (options.orderType) {
state.orderType = options.orderType;
}
setOrder(id);
// 刷新钱包的缓存
sheep.$store('user').getWallet();
});
</script>
<style lang="scss" scoped>
.pay-icon {
width: 36rpx;
height: 36rpx;
margin-right: 26rpx;
}
.ss-modal-box {
// max-height: 1000rpx;
.modal-header {
position: relative;
padding: 60rpx 20rpx 40rpx;
.money-text {
color: $red;
font-size: 46rpx;
font-weight: bold;
font-family: OPPOSANS;
&::before {
content: '¥';
font-size: 30rpx;
}
}
.time-text {
font-size: 26rpx;
color: $gray-b;
}
.close-icon {
position: absolute;
top: 10rpx;
right: 20rpx;
font-size: 46rpx;
opacity: 0.2;
}
}
.modal-content {
overflow-y: auto;
.pay-title {
font-size: 26rpx;
font-weight: 500;
color: #333333;
}
.pay-tip {
font-size: 26rpx;
color: #bbbbbb;
}
.pay-item {
height: 86rpx;
}
.disabled-pay-item {
.pay-title {
color: #999999;
}
}
.userInfo-money {
font-size: 26rpx;
color: #bbbbbb;
line-height: normal;
}
}
.save-btn {
width: 710rpx;
height: 80rpx;
border-radius: 40rpx;
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
color: $white;
}
.disabled-btn {
background: #e5e5e5;
color: #999999;
}
.past-due-btn {
width: 710rpx;
height: 80rpx;
border-radius: 40rpx;
background-color: #999;
color: #fff;
}
}
</style>

308
pages/pay/vip/index.vue Normal file
View File

@@ -0,0 +1,308 @@
<!-- 收银台 -->
<template>
<s-layout title="收银台">
<view class="bg-white ss-modal-box ss-flex-col">
<!-- 订单信息 -->
<view class="modal-header ss-flex-col ss-col-center ss-row-center">
<view class="money-box ss-m-b-20">
<text class="money-text">{{ fen2yuan(state.orderInfo.price) }}</text>
</view>
<view class="time-text">
<text>{{ payDescText }}</text>
</view>
</view>
<!-- 支付方式 -->
<view class="modal-content ss-flex-1">
<view class="pay-title ss-p-l-30 ss-m-y-30">选择支付方式</view>
<radio-group @change="onTapPay">
<label class="pay-type-item" v-for="item in state.payMethods" :key="item.title">
<view
v-if="!item.disabled"
class="pay-item ss-flex ss-col-center ss-row-between ss-p-x-30 border-bottom"
:class="{ 'disabled-pay-item': item.disabled }"
>
<view class="ss-flex ss-col-center">
<image
class="pay-icon"
v-if="item.disabled"
:src="sheep.$url.static('/static/img/shop/pay/cod_disabled.png')"
mode="aspectFit"
/>
<image
class="pay-icon"
v-else
:src="sheep.$url.static(item.icon)"
mode="aspectFit"
/>
<text class="pay-title">{{ item.title }}</text>
</view>
<view class="check-box ss-flex ss-col-center ss-p-l-10">
<view class="userInfo-money ss-m-r-10" v-if="item.value === 'wallet'">
余额: {{ fen2yuan(userWallet.balance) }}
</view>
<radio
:value="item.value"
color="var(--ui-BG-Main)"
style="transform: scale(0.8)"
:disabled="item.disabled"
:checked="state.payment === item.value"
/>
</view>
</view>
</label>
</radio-group>
</view>
<!-- 工具 -->
<view class="modal-footer ss-flex ss-row-center ss-col-center ss-m-t-80 ss-m-b-40">
<button v-if="state.payStatus === 0" class="ss-reset-button past-due-btn">
检测支付环境中
</button>
<button v-else-if="state.payStatus === -1" class="ss-reset-button past-due-btn" disabled>
支付已过期
</button>
<button
v-else
class="ss-reset-button save-btn"
@tap="onPay"
:disabled="state.payStatus !== 1"
:class="{ 'disabled-btn': state.payStatus !== 1 }"
>
立即支付
</button>
</view>
</view>
</s-layout>
</template>
<script setup>
import { computed, reactive } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
import sheep from '@/sheep';
import { fen2yuan, useDurationTime } from '@/sheep/hooks/useGoods';
import PayOrderApi from '@/sheep/api/pay/order';
import PayChannelApi from '@/sheep/api/pay/channel';
import { getPayMethods } from '@/sheep/platform/pay';
const userWallet = computed(() => sheep.$store('user').userWallet);
// 检测支付环境
const state = reactive({
orderType: 'goods', // 订单类型; goods - 商品订单, recharge - 充值订单
orderInfo: {}, // 支付单信息
payStatus: 0, // 0=检测支付环境, -2=未查询到支付单信息, -1=支付已过期, 1=待支付2=订单已支付
payMethods: [], // 可选的支付方式
payment: '', // 选中的支付方式
});
const onPay = () => {
if (state.payment === '') {
sheep.$helper.toast('请选择支付方式');
return;
}
if (state.payment === 'wallet') {
if(state.orderInfo.price > userWallet.value.balance){
uni.showModal({
title: '账户余额不足',
confirmText: '去充值',
success: function (res) {
if (res.confirm) {
sheep.$router.go('/pages/pay/recharge');
}
},
});
return;
}
uni.showModal({
title: '提示',
content: '确定要支付吗?',
success: function (res) {
if (res.confirm) {
sheep.$platform.pay(state.payment, state.orderType, state.orderInfo.id, 'vip');
}
},
});
} else {
sheep.$platform.pay(state.payment, state.orderType, state.orderInfo.id, 'vip');
}
};
// 支付文案提示
const payDescText = computed(() => {
if (state.payStatus === 2) {
return '该订单已支付';
}
if (state.payStatus === 1) {
const time = useDurationTime(state.orderInfo.expireTime);
if (time.ms <= 0) {
state.payStatus = -1;
return '';
}
return `剩余支付时间 ${time.h}:${time.m}:${time.s} `;
}
if (state.payStatus === -2) {
return '未查询到支付单信息';
}
return '';
});
// 状态转换payOrder.status => payStatus
function checkPayStatus() {
if (state.orderInfo.status === 10
|| state.orderInfo.status === 20 ) { // 支付成功
state.payStatus = 2;
return;
}
if (state.orderInfo.status === 30) { // 支付关闭
state.payStatus = -1;
return;
}
state.payStatus = 1; // 待支付
}
// 切换支付方式
function onTapPay(e) {
state.payment = e.detail.value;
}
// 设置支付订单信息
async function setOrder(id) {
// 获得支付订单信息
const { data, code } = await PayOrderApi.getOrder(id);
if (code !== 0 || !data) {
state.payStatus = -2;
return;
}
state.orderInfo = data;
// 获得支付方式
await setPayMethods();
// 设置支付状态
checkPayStatus();
}
// 获得支付方式
async function setPayMethods() {
const { data, code } = await PayChannelApi.getEnableChannelCodeList(state.orderInfo.appId)
if (code !== 0) {
return
}
state.payMethods = getPayMethods(data)
state.payMethods.find(item => {
if (item.value && !item.disabled) {
state.payment = item.value;
return true;
}
});
}
onLoad((options) => {
if (sheep.$platform.name === 'WechatOfficialAccount'
&& sheep.$platform.os === 'ios'
&& !sheep.$platform.landingPage.includes('pages/pay/vip/index')) {
location.reload();
return;
}
// 获得支付订单信息
let id = options.id;
if (options.orderType) {
state.orderType = options.orderType;
}
setOrder(id);
// 刷新钱包的缓存
sheep.$store('user').getWallet();
});
</script>
<style lang="scss" scoped>
.pay-icon {
width: 36rpx;
height: 36rpx;
margin-right: 26rpx;
}
.ss-modal-box {
// max-height: 1000rpx;
.modal-header {
position: relative;
padding: 60rpx 20rpx 40rpx;
.money-text {
color: $red;
font-size: 46rpx;
font-weight: bold;
font-family: OPPOSANS;
&::before {
content: '¥';
font-size: 30rpx;
}
}
.time-text {
font-size: 26rpx;
color: $gray-b;
}
.close-icon {
position: absolute;
top: 10rpx;
right: 20rpx;
font-size: 46rpx;
opacity: 0.2;
}
}
.modal-content {
overflow-y: auto;
.pay-title {
font-size: 26rpx;
font-weight: 500;
color: #333333;
}
.pay-tip {
font-size: 26rpx;
color: #bbbbbb;
}
.pay-item {
height: 86rpx;
}
.disabled-pay-item {
.pay-title {
color: #999999;
}
}
.userInfo-money {
font-size: 26rpx;
color: #bbbbbb;
line-height: normal;
}
}
.save-btn {
width: 710rpx;
height: 80rpx;
border-radius: 40rpx;
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
color: $white;
}
.disabled-btn {
background: #e5e5e5;
color: #999999;
}
.past-due-btn {
width: 710rpx;
height: 80rpx;
border-radius: 40rpx;
background-color: #999;
color: #fff;
}
}
</style>

308
pages/pay/worker/index.vue Normal file
View File

@@ -0,0 +1,308 @@
<!-- 收银台 -->
<template>
<s-layout title="收银台">
<view class="bg-white ss-modal-box ss-flex-col">
<!-- 订单信息 -->
<view class="modal-header ss-flex-col ss-col-center ss-row-center">
<view class="money-box ss-m-b-20">
<text class="money-text">{{ fen2yuan(state.orderInfo.price) }}</text>
</view>
<view class="time-text">
<text>{{ payDescText }}</text>
</view>
</view>
<!-- 支付方式 -->
<view class="modal-content ss-flex-1">
<view class="pay-title ss-p-l-30 ss-m-y-30">选择支付方式</view>
<radio-group @change="onTapPay">
<label class="pay-type-item" v-for="item in state.payMethods" :key="item.title">
<view
v-if="!item.disabled"
class="pay-item ss-flex ss-col-center ss-row-between ss-p-x-30 border-bottom"
:class="{ 'disabled-pay-item': item.disabled }"
>
<view class="ss-flex ss-col-center">
<image
class="pay-icon"
v-if="item.disabled"
:src="sheep.$url.static('/static/img/shop/pay/cod_disabled.png')"
mode="aspectFit"
/>
<image
class="pay-icon"
v-else
:src="sheep.$url.static(item.icon)"
mode="aspectFit"
/>
<text class="pay-title">{{ item.title }}</text>
</view>
<view class="check-box ss-flex ss-col-center ss-p-l-10">
<view class="userInfo-money ss-m-r-10" v-if="item.value === 'wallet'">
余额: {{ fen2yuan(userWallet.balance) }}
</view>
<radio
:value="item.value"
color="var(--ui-BG-Main)"
style="transform: scale(0.8)"
:disabled="item.disabled"
:checked="state.payment === item.value"
/>
</view>
</view>
</label>
</radio-group>
</view>
<!-- 工具 -->
<view class="modal-footer ss-flex ss-row-center ss-col-center ss-m-t-80 ss-m-b-40">
<button v-if="state.payStatus === 0" class="ss-reset-button past-due-btn">
检测支付环境中
</button>
<button v-else-if="state.payStatus === -1" class="ss-reset-button past-due-btn" disabled>
支付已过期
</button>
<button
v-else
class="ss-reset-button save-btn"
@tap="onPay"
:disabled="state.payStatus !== 1"
:class="{ 'disabled-btn': state.payStatus !== 1 }"
>
立即支付
</button>
</view>
</view>
</s-layout>
</template>
<script setup>
import { computed, reactive } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
import sheep from '@/sheep';
import { fen2yuan, useDurationTime } from '@/sheep/hooks/useGoods';
import PayOrderApi from '@/sheep/api/pay/order';
import PayChannelApi from '@/sheep/api/pay/channel';
import { getPayMethods } from '@/sheep/platform/pay';
const userWallet = computed(() => sheep.$store('user').userWallet);
// 检测支付环境
const state = reactive({
orderType: 'goods', // 订单类型; goods - 商品订单, recharge - 充值订单
orderInfo: {}, // 支付单信息
payStatus: 0, // 0=检测支付环境, -2=未查询到支付单信息, -1=支付已过期, 1=待支付2=订单已支付
payMethods: [], // 可选的支付方式
payment: '', // 选中的支付方式
});
const onPay = () => {
if (state.payment === '') {
sheep.$helper.toast('请选择支付方式');
return;
}
if (state.payment === 'wallet') {
if(state.orderInfo.price > userWallet.value.balance){
uni.showModal({
title: '账户余额不足',
confirmText: '去充值',
success: function (res) {
if (res.confirm) {
sheep.$router.go('/pages/pay/recharge');
}
},
});
return;
}
uni.showModal({
title: '提示',
content: '确定要支付吗?',
success: function (res) {
if (res.confirm) {
sheep.$platform.pay(state.payment, state.orderType, state.orderInfo.id, 'worker');
}
},
});
} else {
sheep.$platform.pay(state.payment, state.orderType, state.orderInfo.id, 'worker');
}
};
// 支付文案提示
const payDescText = computed(() => {
if (state.payStatus === 2) {
return '该订单已支付';
}
if (state.payStatus === 1) {
const time = useDurationTime(state.orderInfo.expireTime);
if (time.ms <= 0) {
state.payStatus = -1;
return '';
}
return `剩余支付时间 ${time.h}:${time.m}:${time.s} `;
}
if (state.payStatus === -2) {
return '未查询到支付单信息';
}
return '';
});
// 状态转换payOrder.status => payStatus
function checkPayStatus() {
if (state.orderInfo.status === 10
|| state.orderInfo.status === 20 ) { // 支付成功
state.payStatus = 2;
return;
}
if (state.orderInfo.status === 30) { // 支付关闭
state.payStatus = -1;
return;
}
state.payStatus = 1; // 待支付
}
// 切换支付方式
function onTapPay(e) {
state.payment = e.detail.value;
}
// 设置支付订单信息
async function setOrder(id) {
// 获得支付订单信息
const { data, code } = await PayOrderApi.getOrder(id);
if (code !== 0 || !data) {
state.payStatus = -2;
return;
}
state.orderInfo = data;
// 获得支付方式
await setPayMethods();
// 设置支付状态
checkPayStatus();
}
// 获得支付方式
async function setPayMethods() {
const { data, code } = await PayChannelApi.getEnableChannelCodeList(state.orderInfo.appId)
if (code !== 0) {
return
}
state.payMethods = getPayMethods(data)
state.payMethods.find(item => {
if (item.value && !item.disabled) {
state.payment = item.value;
return true;
}
});
}
onLoad((options) => {
if (sheep.$platform.name === 'WechatOfficialAccount'
&& sheep.$platform.os === 'ios'
&& !sheep.$platform.landingPage.includes('pages/pay/worker/index')) {
location.reload();
return;
}
// 获得支付订单信息
let id = options.id;
if (options.orderType) {
state.orderType = options.orderType;
}
setOrder(id);
// 刷新钱包的缓存
sheep.$store('user').getWallet();
});
</script>
<style lang="scss" scoped>
.pay-icon {
width: 36rpx;
height: 36rpx;
margin-right: 26rpx;
}
.ss-modal-box {
// max-height: 1000rpx;
.modal-header {
position: relative;
padding: 60rpx 20rpx 40rpx;
.money-text {
color: $red;
font-size: 46rpx;
font-weight: bold;
font-family: OPPOSANS;
&::before {
content: '¥';
font-size: 30rpx;
}
}
.time-text {
font-size: 26rpx;
color: $gray-b;
}
.close-icon {
position: absolute;
top: 10rpx;
right: 20rpx;
font-size: 46rpx;
opacity: 0.2;
}
}
.modal-content {
overflow-y: auto;
.pay-title {
font-size: 26rpx;
font-weight: 500;
color: #333333;
}
.pay-tip {
font-size: 26rpx;
color: #bbbbbb;
}
.pay-item {
height: 86rpx;
}
.disabled-pay-item {
.pay-title {
color: #999999;
}
}
.userInfo-money {
font-size: 26rpx;
color: #bbbbbb;
line-height: normal;
}
}
.save-btn {
width: 710rpx;
height: 80rpx;
border-radius: 40rpx;
background: linear-gradient(90deg, var(--ui-BG-Main), var(--ui-BG-Main-gradient));
color: $white;
}
.disabled-btn {
background: #e5e5e5;
color: #999999;
}
.past-due-btn {
width: 710rpx;
height: 80rpx;
border-radius: 40rpx;
background-color: #999;
color: #fff;
}
}
</style>

310
pages/pay/worker/result.vue Normal file
View File

@@ -0,0 +1,310 @@
<!-- 支付结果页面 -->
<template>
<s-layout title="支付结果" :bgStyle="{ color: '#FFF' }">
<view class="pay-result-box ss-flex-col ss-row-center ss-col-center">
<!-- 信息展示 -->
<view class="pay-waiting ss-m-b-30" v-if="payResult === 'waiting'" />
<image
class="pay-img ss-m-b-30"
v-if="payResult === 'success'"
:src="sheep.$url.static('/static/img/shop/order/order_pay_success.gif')"
/>
<image
class="pay-img ss-m-b-30"
v-if="['failed', 'closed'].includes(payResult)"
:src="sheep.$url.static('/static/img/shop/order/order_paty_fail.gif')"
/>
<view class="tip-text ss-m-b-30" v-if="payResult === 'success'">支付成功</view>
<view class="tip-text ss-m-b-30" v-if="payResult === 'failed'">支付失败</view>
<view class="tip-text ss-m-b-30" v-if="payResult === 'closed'">该订单已关闭</view>
<view class="tip-text ss-m-b-30" v-if="payResult === 'waiting'">检测支付结果...</view>
<view class="pay-total-num ss-flex" v-if="payResult === 'success'">
<view>{{ fen2yuan(state.orderInfo.price) }}</view>
</view>
<!-- 操作区 -->
<view class="btn-box ss-flex ss-row-center ss-m-t-50">
<button class="back-btn ss-reset-button" @tap="sheep.$router.go('/pages/tabbar/index')">
返回首页
</button>
<button
class="check-btn ss-reset-button"
v-if="payResult === 'failed'"
@tap="
sheep.$router.redirect('/pages/pay/worker/index', { id: state.id, orderType: state.orderType })
"
>
重新支付
</button>
<button class="check-btn ss-reset-button" v-if="payResult === 'success'" @tap="onOrder">
查看订单
</button>
</view>
<!-- #ifdef MP -->
<view
class="subscribe-box ss-flex ss-m-t-44"
v-if="showSubscribeBtn && state.orderType === 'goods'"
>
<u-icon name="bell-fill" color="var(--ui-BG-Main)" size="44"></u-icon>
<view class="subscribe-title ss-m-r-48 ss-m-l-16">获取实时接单信息与订单状态</view>
<view class="subscribe-start" @tap="subscribeMessage">立即订阅</view>
</view>
<!-- #endif -->
</view>
</s-layout>
</template>
<script setup>
import { onLoad, onHide, onShow } from '@dcloudio/uni-app';
import { reactive, computed, ref } from 'vue';
import { isEmpty } from 'lodash-es';
import sheep from '@/sheep';
import PayOrderApi from '@/sheep/api/pay/order';
import { fen2yuan } from '@/sheep/hooks/useGoods';
import OrderApi from '@/sheep/api/trade/order';
import { WxaSubscribeTemplate } from '@/sheep/util/const';
const state = reactive({
id: 0, // 支付单号
orderType: 'goods', // 订单类型
result: 'unpaid', // 支付状态
orderInfo: {}, // 支付订单信息
tradeOrder: {}, // 商品订单信息,只有在 orderType 为 goods 才会请求。目的:【我的拼团】按钮的展示
counter: 0, // 获取结果次数
});
// 支付结果 result => payResult
const payResult = computed(() => {
if (state.result === 'unpaid') {
return 'waiting';
}
if (state.result === 'paid') {
return 'success';
}
if (state.result === 'failed') {
return 'failed';
}
if (state.result === 'closed') {
return 'closed';
}
});
// 获得订单信息
async function getOrderInfo(id) {
state.counter++;
// 1. 加载订单信息
const { data, code } = await PayOrderApi.getOrder(id);
if (code === 0) {
state.orderInfo = data;
if (!state.orderInfo || state.orderInfo.status === 30) {
// 支付关闭
state.result = 'closed';
return;
}
if (state.orderInfo.status !== 0) {
// 非待支付,可能是已支付,可能是已退款
state.result = 'paid';
// #ifdef MP
uni.showModal({
title: '支付结果',
showCancel: false, // 不要取消按钮
content: '支付成功',
success: () => {
// 订阅只能由用户主动触发,只能包一层 showModal 诱导用户点击
autoSubscribeMessage();
},
});
// #endif
// 特殊:获得商品订单信息
if (state.orderType === 'goods') {
const { data, code } = await OrderApi.getOrder(state.orderInfo.merchantOrderId);
if (code === 0) {
state.tradeOrder = data;
}
}
return;
}
}
// 2.1 情况三一:未支付,且轮询次数小于三次,则继续轮询
if (state.counter < 3 && state.result === 'unpaid') {
setTimeout(() => {
getOrderInfo(id);
}, 1500);
}
// 2.2 情况二:超过三次检测才判断为支付失败
if (state.counter >= 3) {
state.result = 'failed';
}
}
function onOrder() {
if (state.orderType === 'recharge') {
sheep.$router.redirect('/pages/pay/recharge-log');
} else {
sheep.$router.redirect('/pages/order/my/list');
}
}
// #ifdef MP
const showSubscribeBtn = ref(false); // 默认隐藏
const SUBSCRIBE_BTN_STATUS_STORAGE_KEY = 'subscribe_btn_status';
function subscribeMessage() {
if (state.orderType !== 'goods') {
return;
}
const event = [WxaSubscribeTemplate.TRADE_ORDER_DELIVERY];
if (state.tradeOrder.type === 3) {
event.push(WxaSubscribeTemplate.PROMOTION_COMBINATION_SUCCESS);
}
sheep.$platform.useProvider('wechat').subscribeMessage(event, () => {
// 订阅后记录一下订阅状态
uni.removeStorageSync(SUBSCRIBE_BTN_STATUS_STORAGE_KEY);
uni.setStorageSync(SUBSCRIBE_BTN_STATUS_STORAGE_KEY, '已订阅');
// 隐藏订阅按钮
showSubscribeBtn.value = false;
});
}
async function autoSubscribeMessage() {
// 1. 校验是否手动订阅过
const subscribeBtnStatus = uni.getStorageSync(SUBSCRIBE_BTN_STATUS_STORAGE_KEY);
if (!subscribeBtnStatus) {
showSubscribeBtn.value = true;
return;
}
// 2. 订阅消息
subscribeMessage();
}
// #endif
onLoad(async (options) => {
// 支付订单号
if (options.id) {
state.id = options.id;
}
// 订单类型
if (options.orderType) {
state.orderType = options.orderType;
}
// 支付结果传值过来是失败,则直接显示失败界面
if (options.payState === 'fail') {
state.result = 'failed';
} else {
// 轮询三次检测订单支付结果
await getOrderInfo(state.id);
}
});
onShow(() => {
if (isEmpty(state.orderInfo)) {
return;
}
getOrderInfo(state.id);
});
onHide(() => {
state.result = 'unpaid';
state.counter = 0;
});
</script>
<style lang="scss" scoped>
@keyframes rotation {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.score-img {
width: 36rpx;
height: 36rpx;
margin: 0 4rpx;
}
.pay-result-box {
padding: 60rpx 0;
.pay-waiting {
margin-top: 20rpx;
width: 60rpx;
height: 60rpx;
border: 10rpx solid rgb(233, 231, 231);
border-bottom-color: rgb(204, 204, 204);
border-radius: 50%;
display: inline-block;
// -webkit-animation: rotation 1s linear infinite;
animation: rotation 1s linear infinite;
}
.pay-img {
width: 130rpx;
height: 130rpx;
}
.tip-text {
font-size: 30rpx;
font-weight: bold;
color: #333333;
}
.pay-total-num {
font-size: 36rpx;
font-weight: 500;
color: #333333;
font-family: OPPOSANS;
}
.btn-box {
width: 100%;
.back-btn {
width: 190rpx;
height: 70rpx;
font-size: 28rpx;
border: 2rpx solid #dfdfdf;
border-radius: 35rpx;
font-weight: 400;
color: #595959;
}
.check-btn {
width: 190rpx;
height: 70rpx;
font-size: 28rpx;
border: 2rpx solid #dfdfdf;
border-radius: 35rpx;
font-weight: 400;
color: #595959;
margin-left: 32rpx;
}
}
.subscribe-box {
.subscribe-img {
width: 44rpx;
height: 44rpx;
}
.subscribe-title {
font-weight: 500;
font-size: 32rpx;
line-height: 36rpx;
color: #434343;
}
.subscribe-start {
color: var(--ui-BG-Main);
font-weight: 700;
font-size: 32rpx;
line-height: 36rpx;
}
}
}
</style>