源文件

This commit is contained in:
gyq
2024-05-23 14:39:33 +08:00
commit a1128dd791
2997 changed files with 500069 additions and 0 deletions

View File

@@ -0,0 +1,93 @@
<template>
<uni-popup ref="popup" type="center">
<view class="c-wrapper" :style="{ '--v-primary': calcThemeColor() }">
<view class="c-title">会员余额支付</view>
<view class="c-amount">
<text>金额</text>
{{ vdata.amount }}
</view>
<view class="but-wrapper">
<view class="cancel" @tap="close">取消</view>
<view class="confirm" @tap="confirm">确认支付</view>
</view>
</view>
</uni-popup>
</template>
<script setup>
import { ref, reactive } from "vue"
import { calcThemeColor } from "@/util/member.js"
const vdata = reactive({})
const popup = ref(null)
const emits = defineEmits(['pay'])
const open = (amount) => {
vdata.amount = amount
popup.value.open()
}
const close = () => popup.value.close()
const confirm = () => {
emits('pay')
}
defineExpose({ open, close })
</script>
<style lang="scss" scoped>
.c-wrapper {
width: 650rpx;
height: 508rpx;
padding: 30rpx;
box-sizing: border-box;
background-color: #fff;
border-radius: 20rpx;
.c-title {
margin: 50rpx 0 70rpx 0;
font-weight: bold;
text-align: center;
}
.remarks-content {
margin: 30rpx 0;
}
.c-amount {
display: flex;
align-items: center;
flex-direction: column;
color: #000000ff;
font-size: 50rpx;
font-weight: 500;
text {
margin-bottom: 20rpx;
color: #808080ff;
font-size: 30rpx;
}
}
.but-wrapper {
display: flex;
margin-top: 50rpx;
justify-content: space-between;
view {
flex: 0 0 45%;
display: flex;
justify-content: center;
align-items: center;
height: 90rpx;
color: #fff;
border-radius: 15rpx;
}
.cancel {
color: #808080ff;
}
.confirm {
width: 350rpx;
height: 110rpx;
border-radius: 20rpx;
background-color: var(--v-primary);
}
}
}
</style>

View File

@@ -0,0 +1,231 @@
<template>
<view class="key-wrapper" :style="{ '--v-primary': calcThemeColor() }">
<view class="key-re">
<text @tap="emits('remarks')">添加备注</text>
<view class="openOrClose" v-if="!isFixedFlag" @tap="vdata.openOrClose = !vdata.openOrClose">
<text>{{ vdata.openOrClose ? '收起' : '展开' }}</text>
<image src="/static/vipImg/openOrclose-key.svg" mode="aspectFill" :class="{ 'close-key': !vdata.openOrClose }">
</image>
</view>
</view>
<view class="pay-but" hover-class="hover-pay" hover-start-time="10" hover-stay-time="60" :style="{ height: vdata.openOrClose ? '0' : '110rpx' }" v-if="!vdata.openOrClose && !isFixedFlag"
@tap="emits('pay')">付款</view>
<view class="key-mian"
:style="{ maxHeight: vdata.openOrClose ? '800rpx' : '0', paddingBottom: vdata.openOrClose ? '50rpx' : '0' }"
v-if="!isFixedFlag">
<view class="key-left">
<block v-for="(v, i) in keyList" :key="i">
<view class="k-l-item flex-center" hover-class="hover-but" hover-start-time="10" hover-stay-time="60"
:class="{ 'zero': v.val == '0' }" @tap="changeKey(v.val)"> {{ v.val }}</view>
</block>
</view>
<view class="key-right">
<view class="key-r-del flex-center" hover-class="hover-but" hover-start-time="10" hover-stay-time="60" @tap="deletedNumber">
<image src="/static/vipImg/icon-del.svg" mode="aspectFill" class="mch-header"></image>
</view>
<view class="key-r-pay flex-center" hover-class="hover-pay" hover-start-time="10" hover-stay-time="60" @tap="emits('pay')">付款</view>
</view>
</view>
<view class="fixed-wrapper" v-else>
<view class="fixed-but flex-center" hover-class="hover-pay" hover-start-time="10" hover-stay-time="60" @tap="emits('pay')">确认支付</view>
</view>
</view>
</template>
<script setup>
import { ref, reactive } from "vue"
import { calcThemeColor } from "@/util/member.js"
const props = defineProps({
value: {
type: String,
default: ''
},
isFixedFlag: { type: Boolean, default: false }
})
const vdata = reactive({
openOrClose: false
})
const emits = defineEmits(['update:value', 'pay', 'remarks'])
const keyList = [
{ val: '1', pos: 'l' },
{ val: '2', pos: 'l' },
{ val: '3', pos: 'l' },
{ val: '4', pos: 'l' },
{ val: '5', pos: 'l' },
{ val: '6', pos: 'l' },
{ val: '7', pos: 'l' },
{ val: '8', pos: 'l' },
{ val: '9', pos: 'l' },
{ val: '0', pos: 'l' },
{ val: '.', pos: 'l' },
]
const changeKey = (val) => {
// 禁止 输入长度 大于10位
if(props.value.length > 10) return false
// 如果值已经是0并且按下不是小数点 直接替换值
if (props.value.toString().length == 1 && props.value == 0 && val != '.') return emits('update:value', val)
// 只能包含一个小数点
if (props.value.toString().includes('.') && val == '.') return
// 限制小数点位数
if (props.value.toString().includes('.') && props.value.split('.')[1].length >= props.point) return
// 长度只有 一位 并且按下结果是小数点 直接 return
if (props.value.toString().length == 1 && props.value == 0 && val != '.') return
// 如果 清空后直接按下小数点 直接 return
if ((props.value === '' || props.value == undefined) && val == '.') return emits('update:value', '0' + val)
if (props.value.includes('.') && props.value.split('.')[1].length >= 2) return
emitsParent(val)
}
const deletedNumber = () => {
emits('update:value', props.value.slice(0, props.value.length - 1))
}
// 错误提示
function tips (title) {
uni.showToast({
title,
icon: 'none'
})
}
// 向父级传递消息
function emitsParent (v) {
return emits('update:value', props.value + v)
}
function openKeyBoard () {
if (!vdata.openOrClose) return vdata.openOrClose = true
}
defineExpose({ openKeyBoard })
</script>
<style lang="scss" scoped>
.key-re {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 40rpx;
color: #1f7099ff;
font-size: 25rpx;
height: 90rpx;
background-color: #fff;
border-top: 1rpx solid #e6e6e6ff;
border-bottom: 1rpx solid #e6e6e6ff;
.openOrClose {
display: flex;
align-items: center;
image {
width: 50rpx;
height: 50rpx;
transform: rotate(180deg);
transition: 0.3s ease-in-out;
}
.close-key {
transform: rotate(0);
}
}
}
.flex-center {
display: flex;
justify-content: center;
align-items: center;
border-radius: 10rpx;
}
.key-mian {
display: flex;
padding: 0 5rpx;
padding-top: 10rpx;
padding-bottom: 50rpx;
max-height: 0;
border-top: 1rpx solid #0000001a;
background: #f7f7f7ff;
color: #000000ff;
font-size: 40rpx;
font-weight: 500;
transition: max-height 0.3s ease-out;
overflow: hidden;
.key-left {
display: flex;
flex-wrap: wrap;
.k-l-item {
margin: 8rpx;
width: 168rpx;
height: 90rpx;
background-color: #fff;
}
.zero {
width: 350rpx;
}
}
.key-right {
.key-r-del {
margin: 5rpx;
width: 168rpx;
height: 90rpx;
background-color: #fff;
image {
width: 38rpx;
height: 30rpx;
}
}
.key-r-pay {
margin: 5rpx;
margin-top: 20rpx;
width: 168rpx;
height: 302rpx;
color: #fff;
font-size: 32rpx;
font-weight: 500;
background-color: var(--v-primary);
}
}
}
.fixed-wrapper {
display: flex;
justify-content: center;
align-items: flex-end;
width: 100%;
height: 200rpx;
.fixed-but {
margin: 30rpx;
margin-bottom: 70rpx;
width: 100%;
height: 110rpx;
color: #fff;
font-size: 32rpx;
font-weight: 500;
border-radius: 20rpx;
background-color: var(--v-primary);
}
}
.pay-but {
display: flex;
justify-content: center;
align-items: center;
margin: 15rpx auto;
width: 719rpx;
height: 110rpx;
border-radius: 10rpx;
color: #fff;
background-color: var(--v-primary);
transition: height 0.3s ease-in-out;
overflow: hidden;
}
.hover-but {
filter: brightness(80%);
}
.hover-pay{
opacity: .5;
}
</style>

View File

@@ -0,0 +1,71 @@
<template>
<uni-popup ref="popup" type="center">
<view class="c-wrapper" :style="{ '--v-primary': calcThemeColor() }">
<view class="c-title">添加支付备注</view>
<view class="remarks-content">
<textarea class="remarks-input" v-model="pageData.remarkText" placeholder="请输入备注信息" @input="inputChange" maxlength="120" />
</view>
<view class="conf-but" @tap="close">确认</view>
</view>
</uni-popup>
</template>
<script setup>
import { ref,reactive } from "vue"
import { calcThemeColor } from "@/util/member.js"
const props = defineProps({
value: ''
})
const emits = defineEmits(['update:value'])
const pageData = reactive({
remarkText: props.value
})
const popup = ref(null)
const open = (amount) => {
popup.value.open()
}
const close = () => popup.value.close()
const inputChange = (e) => {
emits('update:value', e.detail.value)
}
defineExpose({ open })
</script>
<style lang="scss" scoped>
.c-wrapper {
width: calc(100vw - 60rpx);
padding: 30rpx;
box-sizing: border-box;
background-color: #fff;
border-radius: 20rpx;
.c-title {
margin-bottom: 30rpx;
font-weight: bold;
text-align: center;
}
.remarks-content {
width: 100%;
min-height: 160rpx;
.remarks-input {
width: 100%;
height: 100rpx;
}
}
.conf-but {
display: flex;
justify-content: center;
align-items: center;
margin: 0 30rpx;
margin-top: 30rpx;
height: 90rpx;
background-color: var(--v-primary);
border-radius: 15rpx;
color: #fff;
}
}
</style>

View File

@@ -0,0 +1,527 @@
<template>
<view class="page-weapper" :style="{ '--v-primary': calcThemeColor(), '--bg-color': calcThemeColor('bgColor') }">
<view class="header">
<view class="mch-info">
<view @tap="advancedFunc">付款给{{ vdata.mchInfo.mchName }}</view>
<view class="mch-img-box">
<image :src="vdata.mchInfo.storeLogo" mode="aspectFill" class="mch-header"></image>
</view>
</view>
<view class="sub-title">付款金额</view>
<view class="pay-wrapper" @tap="refKey.openKeyBoard()">
<view class="pay-amount"
:style="{ color: vdata.amount ? '' : '#ccccccff', fontSize: vdata.amount ? '100rpx' : '' }">
{{ vdata.amount || '请输入付款金额' }}
<text class="pay-icon"></text>
</view>
</view>
<view class="sub-title">支付方式</view>
<view class="pay-select">
<view class="pay-info">
<image :src="calcThemeColor('imgUrl')" mode="aspectFill" class="pay-img"></image>
<text>{{ vdata.pageTypeInfo?.title }}</text>
</view>
<view class="radio" :class="{ 'radio-selected': vdata.paySelected == vdata.pageTypeInfo.value }"
@tap="vdata.paySelected = vdata.pageTypeInfo.value">
<image src="/static/vipImg/selected-icon.svg" mode="aspectFill"
v-if="vdata.paySelected == vdata.pageTypeInfo.value"></image>
</view>
</view>
<view class="surplus-wrapper">
<view class="member-top">
<view class="member-info">
<image src="/static/vipImg/member-icon.svg" mode="aspectFill" class="member-icon"></image>
<text>会员支付</text>
</view>
<view class="radio" :class="{ 'radio-selected': vdata.paySelected == 'member' }" @tap="() => {
if (!vdata.mbrName) return
vdata.paySelected = 'member'
}">
<image src="/static/vipImg/close-mereber.svg" mode="aspectFill" v-if="!vdata.mbrName"></image>
<image src="/static/vipImg/selected-icon.svg" mode="aspectFill"
v-if="vdata.paySelected == 'member'"></image>
</view>
</view>
<view class="balance-wrapper" @tap="toRech">
<view class="ba-left" v-if="vdata.mbrName">可用余额<text>{{ (vdata.balance / 100).toFixed(2) }}</text>
</view>
<view class="ba-left" style="color:#835511;" v-else> 您还未注册会员 </view>
<view class="ba-right" hover-class="hover-but" hover-stay-time="200">
<text>{{ vdata.mbrName ? '去充值' : '去注册' }}</text>
<image src="/static/vipImg/icon-arrow.svg" mode="aspectFill"></image>
</view>
</view>
</view>
<!-- 充值优惠部分 -->
<view class="member-pref" v-if="amountRules.length">
<view class="sub-title">现充值立享</view>
<view style="padding-top: 11rpx;">
<block v-for="v in amountRules" :key="v.ruleId">
<view class="pref-info">
<view class="pref-title">充值</view>
<text>{{ parseFloat((v.rechargeAmount / 100).toFixed(2)) }}</text>
<view class="pref-title">到账</view>
<text>{{ parseFloat(((v.rechargeAmount + v.giveAmount) / 100).toFixed(2)) }}</text>
</view>
</block>
</view>
</view>
</view>
<view class="footer-keyboard">
<Keyboard ref="refKey" v-model:value="vdata.amount" :isFixedFlag="vdata.mchInfo.fixedFlag" @pay="payMember"
@remarks="refRemarks.open()" />
</view>
</view>
<ConfirmPay ref="refConfPay" @pay="confirmMemberPay" />
<Remarks ref="refRemarks" v-model:value="vdata.buyerRemark" />
</template>
<script setup>
import { onLoad, onPullDownRefresh, onUnload } from '@dcloudio/uni-app'
import { ref, reactive } from "vue"
import { $apiMembers, $payRemberCard, $getMemberInfo, } from "@/http/apiMember.js"
import { toErrPageFunc } from '@/util/toErrorPageUtil.js'
import { $getPayPackage, $getAdvert } from "@/http/apiManager.js"
import storageManage from '@/util/storageManage.js'
import appConfig from "@/config/appConfig"
import paywayCallFunc from '@/pages/payway/payway.js'
import Remarks from "./components/Remarks.vue"
import Keyboard from "./components/Keyboard.vue"
import ConfirmPay from "./components/ConfirmPay.vue"
// import { Base64 } from 'js-base64'
import { calcThemeColor } from "@/util/member.js"
onLoad(() => {
uni.setNavigationBarColor({
frontColor: '#ffffff',
backgroundColor: calcThemeColor()
})
// 查询会员信息 token
$apiMembers({ method: 'mbr.info', channelUesrId: Base64.encode(appConfig.channelUserId) }).then(({ bizData }) => {
if (bizData) {
storageManage.iToken(bizData)
getMemberInfo() //存储token 查询会员信息
}
getMchInfos()
})
uni.$on('updateMemberInfos', () => {
console.log('接收页面通讯');
getMemberInfo()
})
getRules()
})
onUnload(() => {
uni.$off('updateMemberInfos')
})
const refConfPay = ref(null)
const refRemarks = ref(null)
const refKey = ref(null)
const amountRules = reactive([])
const vdata = reactive({
amount: '',
paySelected: '',
pageTypeInfo: {},
payOrderInfo: {},
mchInfo: {},
clearStorageFlag: 0, //显示清空缓存的提示
})
const pageType = {
wechatLite: { title: '微信支付', value: 'wechatLite' },
alipayLite: { title: '支付宝支付', value: 'alipayLite' },
wechatH5: { title: '微信支付', value: 'wechatH5' },
alipayH5: { title: '支付宝支付', value: 'alipayH5' },
ysfpayH5: { title: '云闪付', value: 'ysfpayH5' },
otherH5: { title: '银联', value: 'otherH5' }
}
vdata.pageTypeInfo = pageType[appConfig.currentPageType]
vdata.paySelected = appConfig.currentPageType
const toRech = () => uni.navigateTo({ url: '/pageMember/memberInfo/memberInfo' })
// 获取商户信息 门店头像
const getMchInfos = () => {
$apiMembers({ method: 'mch.info' }).then(({ bizData }) => {
if (bizData.fixedFlag) {
vdata.amount = (bizData.amount / 100).toFixed(2)
}
vdata.mchInfo = bizData || {}
})
}
const payMember = () => {
if (vdata.amount <= 0) {
return uni.showToast({
title: '金额必须大于0',
icon: 'none'
})
}
// 非会员支付 直接拉起支付
if (vdata.paySelected != 'member') return pay()
if (vdata.amount > (vdata.balance / 100)) return uni.showToast({ title: '会员余额不足请前去充值', icon: 'none' })
refConfPay.value.open(vdata.amount, vdata.buyerRemark)
}
// 发起支付
function pay () {
vdata.payOrderInfo.amount = vdata.amount
uni.showLoading({
title: '请稍等...',
mask: true
})
$getPayPackage(vdata.amount, vdata.buyerRemark, vdata.mbrId || '', vdata.mbrTel || '').then(({ bizData }) => {
uni.hideLoading()
//订单创建异常
if (bizData.code != '0') {
return toErrPageFunc(bizData.msg);
}
// 订单响应结果
let orderRes = bizData.data;
if (orderRes.orderState != 1) { //订单不是支付中,说明订单异常
return toErrPageFunc(orderRes.errMsg);
}
if (orderRes.payUrl) {
location.href = orderRes.payUrl;
return false;
}
// 以下为调起 jsapi的函数 分为: H5 和 各端小程序
let thisPaywayCallFunc = paywayCallFunc()[appConfig.currentPageType];
thisPaywayCallFunc(orderRes, vdata.payOrderInfo);
}).catch(() => {
uni.hideLoading()
})
}
// 获取会员信息
const getMemberInfo = () => {
$getMemberInfo().then(({ bizData }) => {
console.log('接收页面通讯', bizData);
vdata.balance = bizData.balance
vdata.mbrName = bizData.mbrName
vdata.mbrId = bizData.mbrId
vdata.mbrTel = bizData.mbrTel
})
}
// 查询会员充值规则
const getRules = () => {
$apiMembers().then(({ bizData }) => {
console.log(bizData.records);
amountRules.push(...bizData.records)
})
}
const closePage = {
wechatH5: () => window.WeixinJSBridge.call('closeWindow'),
alipayH5: () => window.AlipayJSBridge.call('closeWebview'),
ysfpayH5: () => { window.WeixinJSBridge.call('closeWindow'); window.AlipayJSBridge.call('closeWebview') },
otherH5: () => { window.WeixinJSBridge.call('closeWindow'); window.AlipayJSBridge.call('closeWebview') }
}
// 会员支付
const confirmMemberPay = () => {
$payRemberCard(vdata.amount, vdata.buyerRemark).then(res => {
uni.showToast({ title: '支付成功' })
refConfPay.value.close()
if (appConfig.currentPageType.includes('H5')) {
// 查询是否开启点金计划 如果开启点金计划 window.open() 打开新页面
$getAdvert({ appPlace: 4 }).then(({ bizData }) => {
if (bizData && bizData.linkUrl) {
uni.reLaunch({ url: '/pages/H5/H5?url=' + bizData.linkUrl })
return false
}
return closePage[appConfig.currentPageType]()
})
return false
}
uni.navigateTo({ url: '/pages/paySuccess/paySuccess?amount=' + vdata.amount + '&member=true' })
vdata.amount = '0'
vdata.buyerRemark = ''
getMemberInfo()
})
}
// 高级功能模块的显示
function advancedFunc () {
vdata.clearStorageFlag = vdata.clearStorageFlag + 1
if (vdata.clearStorageFlag >= 10) {
vdata.clearStorageFlag = 0
// 目前仅清空缓存
uni.showModal({
title: '确认清除缓存?',
success: function (r) {
if (r.confirm) {
uni.clearStorageSync()
return uni.showToast({ title: '已清空' })
}
}
});
}
}
</script>
<style lang="scss" scoped>
.sub-title {
margin: 50rpx;
margin-bottom: 20rpx;
color: #4d4d4dff;
font-size: 25rpx;
}
.radio {
display: flex;
justify-content: center;
align-items: center;
width: 40rpx;
height: 40rpx;
border: 2rpx solid #00000026;
border-radius: 50%;
overflow: hidden;
image {
width: 16rpx;
height: 12rpx;
}
}
.radio-selected {
background-color: var(--v-primary);
}
.page-weapper {
display: flex;
flex-direction: column;
justify-content: space-between;
height: 100vh;
background-color: #fff;
border-radius: 50rpx 50rpx 0 0;
.header {
position: relative;
overflow-y: auto;
&::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
z-index: -1;
height: 40rpx;
background-color: var(--v-primary);
}
.mch-info {
display: flex;
align-items: center;
justify-content: space-between;
margin: 50rpx;
color: #000000ff;
font-size: 30rpx;
.mch-img-box {
border: 1rpx solid #0000000f;
image {
width: 72rpx;
height: 72rpx;
}
}
}
.pay-amount {
display: flex;
align-items: flex-end;
justify-content: space-between;
margin: 0 50rpx;
margin-bottom: 67rpx;
font-size: 50rpx;
color: var(--v-primary);
height: 110rpx;
.pay-icon {
align-self: flex-end;
color: #808080ff;
font-size: 50rpx;
vertical-align: bottom;
}
}
.pay-select {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 30rpx;
margin: 0 35rpx;
margin-top: 20rpx;
height: 100rpx;
background: #52cc701a;
border-radius: 20rpx;
.pay-info {
display: flex;
align-items: center;
image {
width: 40rpx;
height: 40rpx;
}
text {
margin-left: 20rpx;
color: #4d4d4dff;
font-size: 30rpx;
}
}
}
.surplus-wrapper {
margin: 20rpx 35rpx;
padding: 30rpx;
padding-right: 0;
border-radius: 20rpx;
background: #ffbe4c1a;
.member-top {
display: flex;
justify-content: space-between;
padding-right: 30rpx;
.member-info {
display: flex;
align-items: center;
image {
width: 40rpx;
height: 40rpx;
}
text {
margin-left: 20rpx;
color: #4d4d4dff;
font-size: 30rpx;
}
}
}
text {
color: #666;
}
.to-recharge {
display: flex;
align-items: center;
justify-content: center;
height: 50rpx;
width: 120rpx;
background-color: var(--v-primary);
color: #fff;
border-radius: 10rpx;
margin-left: 30rpx;
font-size: 26rpx;
}
.balance-wrapper {
display: flex;
justify-content: space-between;
align-items: center;
margin-left: 60rpx;
margin-top: 30rpx;
padding-top: 15rpx;
height: 70rpx;
border-top: 1rpx solid #0000000f;
.ba-left {
color: #835511ff;
font-size: 25rpx;
font-weight: 400;
text {
color: #ff8000ff;
font-size: 25rpx;
font-weight: 500;
}
}
.ba-right {
display: flex;
align-items: center;
color: #1f7099ff;
font-size: 25rpx;
image {
margin-right: 30rpx;
width: 40rpx;
height: 40rpx;
}
}
}
}
.Prepaid {
margin-left: 60rpx;
}
.rec-info {
display: flex;
margin: 50rpx 60rpx;
font-size: 28rpx;
color: #ccc;
.info-list {
margin-left: 30rpx;
color: var(--v-primary);
.l-item {
margin-bottom: 15rpx;
}
}
}
}
.pay-remarks {
position: absolute;
top: -80rpx;
left: 50%;
transform: translateX(-50%);
color: var(--v-primary);
}
.footer-keyboard {
flex-shrink: 0;
position: relative;
}
}
.pay-wrapper {
height: 100rpx;
}
.member-pref {
display: flex;
.sub-title {
margin-top: 39rpx;
}
.pref-info {
display: flex;
margin: 28rpx 0;
font-size: 24rpx;
view {
color: #4c4c4cff;
}
text {
margin-left: 10rpx;
margin-right: 30rpx;
color: #ff624fff;
font-weight: 500;
}
}
}
.hover-but {
opacity: .5;
}
</style>