源文件

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,403 @@
<template>
<view class="index-wrapper">
<poster id="ali-poster" posid="idle_pos" :audible="true" style="width: 100%; height: 88vh"
onSuccess="onDisplaySuccess" onFail="onDisplayFail" onChange="onPosterChange"
v-if="!vdata.userInfo.realname || !vdata.adList.length" />
<JeepayBanner v-else :list="vdata.adList" :interval="vdata.changeTime" />
<!-- <view style="width: 100%; height: 88vh"></view> -->
<view class="idnex-wrapper">
<view class="left">
<view class="tips-title">请等待商家发起收款</view>
<view class="store-name">付款给{{ vdata.userInfo.realname }}</view>
</view>
<view class="right">
<view class="more-but log-in" hover-class="hover-more" hover-stay-time="50" v-if="!vdata.userInfo.realname"
@tap="toLogin">请登录</view>
</view>
</view>
</view>
<uni-drawer ref="refShowRight" mode="right" @change="change">
<view class="list-wrapper" :style="{ '--text-color': calcWorkState().value == 1 ? ' #108ee9' : 'tomato' }">
<blocke v-for="(v, i) in vdata.navList" :key="i">
<view class="list-item" hover-class="hover-more" hover-stay-time="50" @tap="tapNav(v)"
v-if="v.value.includes(vdata.modelType)">
{{ v.title }}
<view class="list-item-right">
<view class="face-model" v-if="v.title == '切换模式'">当前模式{{ vdata.modelType == 'pos' ? 'POS' : '独立收银' }}模式
</view>
<image src="/static/right.svg" mode="scaleToFill" v-if="i != vdata.navList.length - 1" />
<image src="/static/logout.svg" mode="scaleToFill" v-else />
</view>
</view>
</blocke>
</view>
</uni-drawer>
<SwitchModelCard ref="refSwitch" @makClose="open" v-model:value="vdata.modelType" />
</template>
<script setup>
import { onLoad } from '@dcloudio/uni-app'
import { computed, onMounted, reactive, ref } from 'vue'
import { req, $appPay, $workRecords, $userInfo, API_URL_PAY_ORDER_LIST, $adBannerList } from '@/http/apiManager'
import storageManage from '@/commons/utils/storageManage.js'
// 生命周期 进入页面
onLoad(() => {
if (storageManage.token()) {
getAd()
}
})
// 初始化 modelType
if (!storageManage.faceModel()) {
storageManage.faceModel('cashier')
}
const refShowRight = ref(null)
const refSwitch = ref(null)
const vdata = reactive({
userInfo: storageManage.userInfo(), //取出用户信息
navList: [
{ title: '订单', url: '/pages/order/order', value: 'cashier' },
{ title: '统计', url: '/pages/stat/stat', value: 'cashier' },
{ title: `${calcWorkState().value == 1 ? '上班' : '下班'}打卡`, fun: working, value: 'cashier' },
{ title: '交班', url: '/pages/handover/handover', value: 'cashier' },
{ title: '切换模式', fun: switchModel, value: 'cashierpos' },
{ title: '系统设置', fun: () => { my.ix.startApp({ appName: 'settings' }) }, value: 'cashierpos' },
{ title: '刷新广告数据', fun: getAd, value: 'cashierpos' },
{ title: '退出登录', fun: loginOut, value: 'cashier' }
],
modelType: storageManage.faceModel(), //取出本地存储的模式类型
adList:[]
})
my.ix.onKeyEventChange(r => {
// console.log('r', r)
const timeStamp = new Date().getTime()
switch (r.keyCode) {
case 134:
if (!storageManage.token()) return uni.$J.showErrorToast('请先登录')
if (vdata.isShow) return my.ix.startApp({ appName: 'settings' })
open()
break
case 132:
if (storageManage.faceModel() && storageManage.faceModel() == 'pos') return posCashier()
break
case 131: //收款按键
if (storageManage.faceModel() && storageManage.faceModel() == 'pos') return posCashier()
my.ix
.startApp({
appId: '2021002172674746',
appName: 'cashier',
bizNo: timeStamp,
totalAmount: r.amount,
showScanPayResult: true,
scanLoadingTimeout: '10'
})
.then(res => {
my.ix.writeHID({
protocol: 'barcode',
value: res.barCode,
success: r => {
console.log('r', r)
},
fail: err => {
console.log('err', err)
}
})
$appPay(res.totalAmount, res.barCode).then(r => {
findOrderState(r.bizData)
console.log('支付后结果', r)
//扫码付支付成功主动调起结果页
if (res.codeType == 'C' && r.bizData.orderState == 2) {
my.ix.startApp({
appName: 'scanPayResult',
bizNo: timeStamp,
totalAmount: res.totalAmount,
success: r => {
console.log(r)
}
})
}
})
})
.catch(err => {
console.log('err', err)
})
break
case 133:
close()
break
}
})
// 展示成功回调
const onDisplaySuccess = () => {
console.log('poster display success')
}
// 展示失败回调
const onDisplayFail = e => {
console.log('poster display fail, error = ' + e.detail.error)
}
// 广告可用性变化回调
const onPosterChange = e => {
console.log('poster availability changed, now has poster = ' + e.detail.hasPoster)
}
// 打开抽屉
const open = () => {
refShowRight.value.open()
}
// 关闭抽屉
const close = () => {
refShowRight.value.close()
}
// pos 收银模式
function posCashier() {
const timeStamp = new Date().getTime()
my.ix
.startApp({
appId: '2021002172674746',
appName: 'cashier',
bizNo: timeStamp,
showScanPayResult: true,
scanLoadingTimeout: '10'
})
.then(res => {
my.ix.writeHID({
protocol: 'barcode',
value: res.barCode,
success: r => {
console.log('r', r)
},
fail: err => {
console.log('err', err)
}
})
})
}
// 退出等领域
function loginOut() {
uni.showModal({
title: '提示',
content: '是否确认退出登录',
showCancel: true,
success: ({ confirm, cancel }) => {
if (confirm) {
storageManage.cleanByLogout()
uni.reLaunch({
url: '/pages/login/login'
})
}
}
})
}
// 打卡函数
function working() {
uni.showModal({
title: `提示:${calcWorkState().value == 1 ? '上班' : '下班'}打卡`,
content: '是否确认打卡?',
showCancel: true,
success: ({ confirm, cancel }) => {
if (confirm) {
$workRecords(calcWorkState().value).then(res => {
uni.showToast({
title: '打卡成功',
icon: 'success',
mask: true
})
// 重新请求用户信息
$userInfo().then(({ bizData }) => {
// 更新用户数据
storageManage.userInfo(bizData)
// 更新用户数据
vdata.userInfo = bizData
vdata.navList[2].title = (calcWorkState().value == 1 ? '上班' : '下班') + '打卡'
})
})
}
}
})
}
// 切换模式函数、
function switchModel() {
refShowRight.value.close()
refSwitch.value.open()
}
/****
* @param {v} Object url 跳转地址 fun 执行函数
* */
const tapNav = v => {
if (v.url)
return uni.navigateTo({
url: v.url
})
v.fun()
}
// 计算状态上班还是下班 0 未知 1上班 2下班
function calcWorkState() {
return computed(() => {
const state = storageManage.userInfo().workState
return state == 0 ? '1' : state == 1 ? 2 : 1
})
}
// 定向到登录页
const toLogin = () => uni.reLaunch({ url: '/pages/login/login' })
function findOrderState(val) {
switch (val.orderState) {
case 2:
break
case 3:
uni.$J.showToast('支付失败' + val.errMsg)
break
case 4:
uni.$J.showToast('订单撤销')
break
case 1:
// 支付中 进行查单操作
console.log('订单状态1进行查单 操作 ')
findOrder(val.payOrderId)
break
}
}
let num = 1
function findOrder(payOrderId) {
if (num > 20)
return uni.showModal({
title: '确认订单状态',
content: '订单状态查询失败 请与客户确认订单状态 点击确认跳转订单列表页面',
showCancel: true,
success: ({ confirm, cancel }) => {
if (confirm) {
uni.navigateTo({
url: '/pages/order/order'
})
}
}
})
uni.$J.showToast(`支付中正在进行第${num}次订单查询`).then(res => {
req.getById(API_URL_PAY_ORDER_LIST, payOrderId).then(({ bizData }) => {
num += 1
console.log('查单结果', bizData)
bizData.orderState = bizData.state
findOrderState(bizData)
})
})
}
function getAd() {
$adBannerList().then(({ bizData }) => {
if(!bizData){
vdata.adList =[]
uni.showToast({title:'请求广告数据成功',icon:'success'})
return false
}
vdata.adList = bizData?.appContent ? JSON.parse(bizData.appContent) : []
vdata.changeTime = bizData.changeTime
uni.showToast({title:'请求广告数据成功',icon:'success'})
})
}
// 抽屉打开状态
const change = (e) => {
vdata.isShow = e
}
</script>
<style lang="scss" scoped>
.idnex-wrapper {
padding: 30rpx;
box-sizing: border-box;
height: 11.8vh;
display: flex;
justify-content: space-between;
align-items: center;
color: #108ee9;
.tips-title {
font-size: 38rpx;
margin-bottom: 20rpx;
}
.store-name {
color: #2c2c2c;
font-size: 22rpx;
}
.more-but {
display: flex;
align-items: center;
justify-content: center;
min-width: 80rpx;
height: 50rpx;
border-radius: 7rpx;
background-color: rgba($color: #108ee9, $alpha: 0.3);
view {
width: 4rpx;
height: 4rpx;
border-radius: 50%;
background-color: #108ee9;
}
view:nth-child(2) {
margin: 0 4rpx;
}
}
}
.list-wrapper {
margin-top: 100rpx;
.list-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx 10rpx 30rpx 30rpx;
box-sizing: border-box;
background-color: rgba($color: #108ee9, $alpha: 0.05);
color: #108ee9;
image {
width: 60rpx;
height: 40rpx;
}
.list-item-right {
flex: 1;
display: flex;
justify-content: flex-end;
align-items: center;
.face-model {
font-size: 24rpx;
}
}
}
.list-item:nth-child(3) {
color: var(--text-color);
}
.list-item:nth-child(8) {
position: absolute;
left: 0;
right: 0;
bottom: 0;
color: tomato;
background-color: rgba(255, 99, 71, 0.05);
image {
svg {
fill: tomato !important;
}
}
}
}
.hover-more {
background-color: rgba($color: #000000, $alpha: 0.07) !important;
}
.log-in {
display: flex;
justify-content: center;
align-items: center;
padding: 5rpx 15rpx;
}
</style>

View File

@@ -0,0 +1,97 @@
<template>
<view class="front-wrapper">
<view class="tips-title" v-if="!vdata.adList.length">等待商家发起收款</view>
<JeepayBanner v-else :list="vdata.adList" :interval="vdata.changeTime" height="calc(100vh - 150rpx)" />
</view>
</template>
<script setup>
import { reactive, ref } from 'vue'
import { onReady, onLoad } from '@dcloudio/uni-app'
import storageManage from '@/commons/utils/storageManage.js'
import { $adBannerList } from '@/http/apiManager'
import envConfig from '@/env/config.js'
// 接收 后屏 通讯 去支付页面
onReady(() => {
wxfaceapp.onRemoteMessage(function (res) {
const content = JSON.parse(res.content)
if (content.type == 'toPay')
return uni.navigateTo({
url: '/pages/frontPay/frontPay',
})
if (content.type == 'adBanner') {
envConfig.changeEnv(content.env)
console.log('content.env', content.env)
envConfig.changeEnv(content.env)
storageManage.token(content.token) //存入token
return getAd()
}
})
// 关闭扫码器
wxfaceapp.stopCodeScanner({})
})
const vdata = reactive({})
function getAd () {
$adBannerList().then(({ bizData }) => {
if (!bizData) return vdata.adList = []
vdata.adList = JSON.parse(bizData.appContent) || []
vdata.changeTime = bizData.changeTime
})
}
</script>
<style lang="scss" scoped>
.front-wrapper {
// padding: 0.01rpx;
width: 100%;
}
.swiper {
height: 100%;
}
.swiper-item {
display: block;
height: 100%;
line-height: 300rpx;
text-align: center;
}
.swiper-list {
margin-top: 40rpx;
margin-bottom: 0;
}
.uni-common-mt {
margin-top: 60rpx;
position: relative;
}
.info {
position: absolute;
right: 20rpx;
}
.uni-padding-wrap {
width: 550rpx;
padding: 0 100rpx;
}
.tips-title {
display: flex;
justify-content: center;
align-items: center;
margin: 160rpx auto;
width: 85%;
height: 180rpx;
font-size: 32rpx;
font-weight: 600;
letter-spacing: 2rpx;
color: #fff;
border-radius: 14rpx;
background-color: rgba($color: #64ec46, $alpha: .7);
}
</style>

View File

@@ -0,0 +1,207 @@
<template>
<!-- <view class="front-wrapper">
<view class="pay-amount" v-if="vdata.isShow">
请支付<view>{{ vdata.amount }}()</view>
</view>
<view class="pay-but">
<JButton @tap="facePay">使用刷脸支付</JButton>
</view>
<view class="pay-tips">请将付款码对准右下角识别区距离10厘米左右</view>
</view> -->
<view class="wait-pay-top" v-if="vdata.isShow">
<view class="pay-info">
<view class="pay-num">{{ vdata.amount }}</view>
<view class="pay-wait-title">请支付</view>
</view>
</view>
<view class="face-button" @tap="facePay">
<image src="/static/fronyImg/face-icon.svg" mode="scaleToFill" />
刷脸支付
</view>
<view class="face-scan-tips">
<view class="scan-title">
<image src="/static/fronyImg/scan-code.svg" mode="scaleToFill" />
扫码支付
</view>
<view class="sub-tips"> 请将付款码对准右下角识别区距离10厘米左右 <image src="/static/fronyImg/icon-down.svg" mode="scaleToFill" /> </view>
</view>
</template>
<script setup>
import { reactive, ref } from 'vue'
import { onReady, onLoad } from '@dcloudio/uni-app'
onLoad(() => {
vdata.isShow = true
//接受 后屏信息
wxfaceapp.onRemoteMessage(function (res) {
console.log('后屏消息', res)
analysisMsg(JSON.parse(res.content))
})
wxfaceapp.startCodeScanner({
success: (res) => {
console.log('打开扫码器成功')
console.log(res.replyCode)
console.log(res.reply)
//声明监听扫码器 使用 扫码器 扫描 后 调用 扫码器 回调事件 获取支付code
wxfaceapp.listenCodePayment({
success(res) {
//被扫码回调事件
wxfaceapp.onCodePayEvent(function (r) {
console.log('onCodePayEvent code scanned = ' + r)
console.log('扫码器', r)
postMsg(r)
})
},
})
},
fail: (res) => {
console.log('打开扫码器失败')
console.log(res.replyCode)
console.log(res.reply)
},
})
})
/**
* 页面初始化完成 监听数据串口 输入事件
* */
// onReady(() => {
// wxfaceapp.startSerialPortObserver({
// success: (r) => {
// console.log('开启监听串口输入成功', r)
// },
// fail: (err) => {
// console.log('开启监听串口输入失败:', err)
// },
// })
// })
const vdata = reactive({
amount: '0.00',
isShow: true,
})
// 微信刷脸 刷脸成功后 使用回调 换取支付code
const facePay = () => {
wxfaceapp.facePay({
requireFaceCode: true, //是否需要获取付款码返回给小程序
success(res) {
console.log('success [launchFaceApp]')
console.log('前屏刷脸结果', res)
wxfaceapp.onFacePayPassEvent(function (r) {
console.log('微信支付回调', r)
postMsg(r)
})
},
fail(res) {
console.log('fail [launchFaceApp]')
console.log(res.replyCode)
console.log(res.reply)
},
})
}
// 发送给后屏
const postMsg = (content) => {
wxfaceapp.postMsg({
targetAppid: 'wx4710a1619fbb3714',
content: JSON.stringify(content),
success(res) {
console.log('sendMsgResult suc', res)
},
fail(res) {
console.log('sendMsgResult failed error = ' + res)
},
})
}
/*
* 解析后屏 发送的消息
* type 值 amount 金额 toBanner 去广告页面
*
* */
function analysisMsg(msg) {
console.log('msg', msg)
switch (msg.type) {
case 'amount':
vdata.amount = msg.amount
break
case 'toBanner':
wxfaceapp.stopCodeScanner({})
uni.navigateTo({
url: '/pages/front/front',
})
break
case 'hideAmount':
vdata.isShow = false
break
}
}
</script>
<style lang="scss" scoped>
.wait-pay-top {
display: flex;
justify-content: center;
align-items: center;
height: 60vh;
background: url('/static/fronyImg/front-bg.svg') no-repeat center / cover;
.pay-info {
transform: translateY(-80rpx);
color: #fff;
.pay-num {
font-weight: 700;
font-size: 62rpx;
font-family: wechat sans;
}
.pay-wait-title {
text-align: center;
font-size: 32rpx;
color: rgba($color: #fff, $alpha: 0.5);
}
}
}
.face-button {
background-color: $v-primary;
display: flex;
justify-content: center;
align-items: center;
margin: 65rpx 40rpx;
height: 120rpx;
border-radius: 15rpx;
color: #fff;
font-size: 32rpx;
box-shadow: 0 50rpx 50rpx -50rpx rgba(0, 206, 107, 0.5);
image {
width: 50rpx;
margin-right: 10rpx;
}
}
.face-scan-tips {
box-sizing: border-box;
padding: 20rpx 0;
padding-top: 40rpx;
margin-top: 20rpx;
height: calc(100vh - 60vh - 250rpx);
background-color: rgba($color: $v-primary, $alpha: 0.2);
.scan-title {
display: flex;
justify-content: center;
align-items: center;
font-size: 32rpx;
color: $v-primary;
image {
width: 50rpx;
height: 50rpx;
margin-right: 10rpx;
}
}
.sub-tips {
margin-top: 25rpx;
display: flex;
justify-content: center;
align-items: center;
color: #666;
font-size: 22rpx;
image {
width: 25rpx;
height: 25rpx;
}
}
}
</style>

View File

@@ -0,0 +1,135 @@
<template>
<!-- 门店展示 暂时接口不满足 -->
<!-- <view class="hand-item">
<view class="hand-title">门店</view>
<view class="hand-info">小伙计</view>
</view> -->
<view class="hand-item">
<view class="hand-title">收银员</view>
<view class="hand-info">{{ vdata.realname || vdata.userInfo.realname }}</view>
</view>
<view class="hand-item">
<view class="hand-title">上班时间</view>
<view class="hand-info">{{ vdata.workStartTime }}</view>
</view>
<view class="hand-item">
<view class="hand-title">下班时间</view>
<view class="hand-info">{{ vdata.workEndTime }}</view>
</view>
<view class="line"></view>
<view class="hand-item">
<view class="hand-title">收款金额</view>
<view class="hand-info">{{ vdata.payAmount }}</view>
</view>
<view class="hand-item">
<view class="hand-title">收款笔数</view>
<view class="hand-info">{{ vdata.payCount }}</view>
</view>
<view class="hand-item">
<view class="hand-title">退款金额</view>
<view class="hand-info">{{ vdata.refundAmount }}</view>
</view>
<view class="hand-item">
<view class="hand-title">退款笔数</view>
<view class="hand-info">{{ vdata.refundCount }}</view>
</view>
<view class="footer-but" v-if="vdata.userInfo.realname">
<view @tap="handHandover">交班</view>
<view class="record" @tap="toHandoverList">交班记录</view>
</view>
</template>
<script setup>
import { $handover, $workRecords, $getHandoverById } from '@/http/apiManager'
import { reactive, ref } from 'vue'
import storageManage from '@/commons/utils/storageManage.js'
import { onLoad } from '@dcloudio/uni-app'
import cal from '@/commons/utils/cal.js'
import dayjs from 'dayjs'
onLoad((options) => {
getHandoverInfo(options.id)
})
const vdata = reactive({
userInfo: storageManage.userInfo(),
})
const getHandoverInfo = (id) => {
if (id) {
uni.setNavigationBarTitle({
title: '交班详情',
})
$getHandoverById(id).then(({ bizData }) => {
bizData = cal.yuan(bizData, ['refundAmount', 'payAmount'])
Object.assign(vdata, bizData)
console.log(bizData)
})
return
}
$handover().then(({ bizData }) => {
bizData = cal.yuan(bizData, ['refundAmount', 'payAmount'])
Object.assign(vdata, bizData)
})
}
const handHandover = () => {
uni.showModal({
title: '是否确认交班?',
content: '交班自动下班 并 退出登录',
showCancel: true,
success: ({ confirm, cancel }) => {
if (confirm) {
$workRecords('2').then((res) => {
uni.$J.showToast('交班成功即将退登录').then((r) => {
storageManage.cleanByLogout()
uni.reLaunch({
url: '/pages/login/login',
})
})
})
}
},
})
}
const toHandoverList = () => uni.navigateTo({ url: '/pages/handover/handoverList' })
</script>
<style lang="scss" scoped>
.hand-item {
display: flex;
justify-content: space-between;
padding: 30rpx;
font-size: 28rpx;
// border-bottom: 1rpx solid #ededed;
.hand-info {
color: #bbb;
}
}
.line {
height: 15rpx;
background-color: #ededed;
}
.footer-but {
display: flex;
justify-content: space-around;
margin-top: 60rpx;
view {
flex: 0 0 30%;
display: flex;
justify-content: center;
align-items: center;
height: 70rpx;
background-color: $v-primary;
border-radius: 14rpx;
color: #fff;
font-size: 22rpx;
}
.record {
background-color: rgba($color: $v-primary, $alpha: 0.7);
}
}
</style>

View File

@@ -0,0 +1,155 @@
<template>
<view class="page-wrapper">
<view class="custom-content">
<view class="start-time" :style="{ color: vdata.startTime ? '#000' : '#666' }" @tap="refStartTime.show">{{
vdata.startTime || '请选择开始时间'
}}</view>
<view class="endTime-time" :style="{ color: vdata.endTime ? '#000' : '#666' }" @tap="refEndTime.show">{{
vdata.endTime || '请选择结束时间'
}}</view>
</view>
<block v-for="v in vdata.list" :key="v.id">
<view class="list-main" @tap="toDetails(v.id)">
<view class="left">
<view class="title">{{ v.realname }}</view>
<view class="time">{{ v.workStartTime }}</view>
</view>
<view class="line"></view>
<view class="right">
<view class="amount title">{{ (v.successAmount / 100).toFixed(2) }}</view>
<view class="time">{{ v.workEndTime || '未下班' }}</view>
</view>
</view>
</block>
<view class="list-null" v-if="!vdata.hasNext">暂无更多数据</view>
</view>
<xp-picker mode="ymdhi" ref="refStartTime" @confirm="timeConfirm($event, 'startTime')">
<view></view>
</xp-picker>
<xp-picker mode="ymdhi" ref="refEndTime" @confirm="timeConfirm($event, 'endTime')">
<view></view>
</xp-picker>
</template>
<script setup>
import { reactive, ref } from 'vue'
import { onLoad, onReachBottom, onUnload } from '@dcloudio/uni-app'
import { $handoverList } from '@/http/apiManager'
import dayjs from 'dayjs'
const refStartTime = ref(null)
const refEndTime = ref(null)
const vdata = reactive({
list: [],
hasNext: true,
})
const params = {
pageSize: 10,
pageNumber: 1,
queryDateRange: '',
}
const getList = () => {
if (!vdata.hasNext) return
$handoverList(params).then(({ bizData }) => {
vdata.list.push(...bizData.records)
vdata.hasNext = bizData.hasNext
})
}
getList()
onReachBottom(() => {
params.pageNumber += 1
getList()
})
/**
* @param {e} Object 组件原生 回调 时间戳 日期
* @param {key} String vdata 对象的 key 值 赋值使用
* */
const timeConfirm = (e, key) => {
vdata[key] = dayjs(e.timestamp).format('YYYY-MM-DD hh:mm')
if (key == 'endTime' || (vdata.endTime != undefined && dayjs(vdata.startTime).isAfter(vdata.endTime))) {
console.log('进入请求')
params.queryDateRange = `customDateTime_${vdata.startTime + ':00'}_${vdata.endTime + ':00'}`
vdata.list = []
params.pageNumber = 1
vdata.hasNext = true
getList()
}
}
/**
* @param {id} String 用于 和后端交互请求详情
* */
const toDetails = (id) => {
uni.navigateTo({
url: '/pages/handover/handover?id=' + id,
})
}
</script>
<style lang="scss" scoped>
.list-main {
display: flex;
justify-content: space-between;
padding: 15rpx;
margin: 30rpx;
background-color: #fff;
border-radius: 14rpx;
.title {
font-size: 22rpx;
font-weight: 600;
margin-bottom: 10rpx;
}
.amount {
color: $v-primary;
}
.line {
align-self: flex-end;
}
.right {
text-align: right;
}
.time,
.line {
color: #ccc;
}
}
.custom-content {
display: flex;
justify-content: space-around;
margin-top: 30rpx;
view {
flex: 0 0 30%;
padding: 10rpx 0;
background-color: #ded7d7;
text-align: center;
border-radius: 10rpx;
font-size: 16rpx;
}
}
.list-null {
display: flex;
justify-content: space-between;
align-items: center;
color: #666;
margin: 30rpx 0;
padding: 0 30rpx;
font-size: 24rpx;
font-weight: 600;
&::after,
&::before {
content: '';
display: block;
width: 35%;
height: 2rpx;
background-color: #ededed;
}
}
</style>

View File

@@ -0,0 +1,492 @@
<template>
<view class="page-wrapper">
<uni-nav-bar statusBar :border="false" leftWidth="50%" backgroundColor="transparent">
<template #left>
<view class="user-info">
<image :src="vdata.userInfo.avatarUrl" mode="scaleToFill" />
<view class="user-nickname">{{ vdata.userInfo.realname }}</view>
</view>
</template>
</uni-nav-bar>
<!-- 卡片部分 -->
<view class="index-card">
<view class="index-content">
<view class="index-main">
<view class="index-num">{{ vdata.indexData.payAmount }}</view>
<view class="index-title">今日总收益()</view>
</view>
<view class="index-main">
<view class="index-num">{{ vdata.indexData.refundAmount }}</view>
<view class="index-title">退款金额()</view>
</view>
</view>
<view class="index-card-footer">
<view>收益详情</view>
<view @tap="more">查看更多 ></view>
</view>
</view>
<!-- 发起收款 -->
<view class="pay-card" @tap="jumpPage('/pages/payment/payment')">
<image src="/static/indexImg/pay.png" mode="scaleToFill" />
<view class="pay-text">发起收款</view>
</view>
<!-- nav 导航部分 -->
<view class="index-nav">
<view class="nav-content">
<block v-for="(v, i) in navList" :key="i">
<view class="nav-main" @tap="jumpPage(v.url)">
<image :src="v.img" mode="scaleToFill" />
<view class="nav-title">{{ v.navTitle }}</view>
</view>
</block>
</view>
<view class="but-content">
<view>
<JButton @tap="switchOpen" bgColor="rgba(0, 200, 0,0.15)" color="#00C800" bdColor="#00C800" :size="24">切换收款模式
</JButton>
</view>
<view>
<JButton @tap="open"
:bgColor="vdata.userInfo.workState == 2 ? 'rgba(0, 128, 255,0.15)' : 'rgba(255, 0, 43,0.15)'"
:color="vdata.userInfo.workState == 2 ? '#0080FF' : '#FF002B'"
:bdColor="vdata.userInfo.workState == 2 ? '#0080FF' : '#FF002B'" :size="24">{{
vdata.userInfo.workState == 1 ? '下班' : vdata.userInfo.workState == 2 ? '上班' : '上班' }}打卡</JButton>
</view>
<view>
<JButton bgColor="rgba(255, 170, 0,0.15)" color="#FFAA00" bdColor="#FFAA00" :size="24" @tap='getFrontAd'>刷新前屏广告
</JButton>
</view>
</view>
<view style="height: 20rpx"></view>
<JButton @tap="loginOut" bgColor="transparent" bdColor="rgba(0,0,0,0.1)" color="#FF002B">退出登录</JButton>
</view>
</view>
<uni-popup ref="refWorker" type="center">
<view class="worker-content">
<view class="title">{{ vdata.userInfo.workState == 1 ? '下班' : vdata.userInfo.workState == 2 ? '上班' : '上班' }}打卡
</view>
<view class="worker-time">请确认打卡时间{{ dayjs().format('YYYY-MM-DD hh:mm') }}</view>
<view class="footer-but">
<view @tap="close">取消</view>
<view class="confirm"
@tap="confirmWorker(vdata.userInfo.workState == 1 ? '2' : vdata.userInfo.workState == 2 ? '1' : '1')">确认 </view>
</view>
</view>
</uni-popup>
<uni-popup ref="refSwitch" type="center">
<view class="switch-model">
<view class="title">切换模式</view>
<view class="sub-title">独立收银模式<view class="model-tag">当前正在使用</view>
</view>
<view class="tips">接入电源后即可使用后屏输入收款金额前屏支持刷脸支付和扫码支付</view>
<view class="sub-title">POS模式</view>
<view class="tips">需要连接收银机或PC使用即插即用收款将会进入您的现有收银软件账户</view>
<view class="pay-model" hover-class="hover-model" hover-stay-time="50" @tap="toPos">
<image src="/static/iconImg/switch-model.svg" mode="scaleToFill" />
切换至POS模式
</view>
</view>
</uni-popup>
</template>
<script setup>
import { reactive, ref } from 'vue'
import { $indexPayment, $workRecords, $userInfo, $adBannerList } from '@/http/apiManager'
import { onLoad, onReady, onUnload, onShow } from '@dcloudio/uni-app'
import storageManage from '@/commons/utils/storageManage.js'
import cal from '@/commons/utils/cal.js'
import dayjs from 'dayjs'
onLoad(() => {
getFrontAd()
if (storageManage.faceModel() && storageManage.faceModel() == 'pos') return uni.reLaunch({ url: '/pages/payModel/payModel' })
})
onShow(() => {
getIndexDate()
})
const refWorker = ref(null)
const refSwitch = ref(null)
// 导航栏列表
const navList = [
{
img: '/static/indexImg/order.png',
url: '/pages/order/order',
navTitle: '订单',
},
{
img: '/static/indexImg/handover.png',
url: '/pages/handover/handover',
navTitle: '交班',
},
{
img: '/static/indexImg/statics.png',
url: '/pages/stat/stat',
navTitle: '统计',
},
]
const vdata = reactive({
userInfo: storageManage.userInfo(), //取出用户信息
indexData: {},
})
// 查询 当天 收益情况
const getIndexDate = () => {
$indexPayment().then(({ bizData }) => {
const { dayCount } = bizData
vdata.indexData = cal.yuan(dayCount, ['payAmount', 'refundAmount']) //分转元
})
}
const open = () => refWorker.value.open()
const close = () => refWorker.value.close()
const switchOpen = () => refSwitch.value.open()
// 确认打卡
const confirmWorker = (state) => {
$workRecords(state).then((res) => {
uni.showToast({
title: '打卡成功',
icon: 'success|none',
mask: true,
})
// 请求用户信息
return $userInfo().then(({ bizData }) => {
// 更新用户数据
storageManage.userInfo(bizData)
// 更新用户数据
vdata.userInfo = bizData
//跳转首页
close()
})
})
}
const loginOut = () => {
uni.showModal({
title: '确认推出登录吗',
content: '',
showCancel: true,
success: ({ confirm, cancel }) => {
if (confirm) {
storageManage.cleanByLogout()
uni.reLaunch({
url: '/pages/login/login',
})
}
},
})
}
const toFront = () => uni.navigateTo({ url: '/pages/front/front' })
const jumpPage = (url) => uni.navigateTo({ url })
uni.addInterceptor('navigateTo', {
invoke(args) {
if (vdata.userInfo.workState != 1 && args.url == '/pages/handover/handover') {
uni.showModal({
title: '提示',
content: '当前状态为,未上班状态,不能进入交班页面,点击确认按钮打卡上班。 ',
showCancel: true,
success: ({ confirm, cancel }) => {
if (confirm) {
refWorker.value.open()
}
},
})
return false
}
},
})
const toPos = () => {
uni.showModal({
title: '提示',
content: '是否切换为 POS模式',
showCancel: true,
success: ({ confirm, cancel }) => {
if (confirm) {
storageManage.faceModel('pos')
uni.reLaunch({ url: '/pages/payModel/payModel' })
}
},
})
}
const more = () => {
uni.navigateTo({ url: '/pages/stat/stat' })
}
const getFrontAd = () => {
const content = { type: 'adBanner', token: storageManage.token(), env: storageManage.env() }
wxfaceapp.postMsg({
targetAppid: 'wx4710a1619fbb3714',
content: JSON.stringify(content),
success(res) {
uni.$J.showToast('通知前屏获取广告成功')
console.log('通信成功', res)
},
fail(res) {
console.log('通信失败 ', res)
},
})
}
onUnload(() => {
uni.removeInterceptor('navigateTo')
})
</script>
<style lang="scss" scoped>
.page-wrapper {
position: relative;
min-height: 100vh;
background: url('/static/indexImg/index-bg.png') no-repeat center center;
background-size: 100% 100%;
}
.user-info {
display: flex;
align-items: center;
image {
width: 50rpx;
height: 50rpx;
}
.user-nickname {
margin-left: 20rpx;
font-weight: 700;
font-size: 22rpx;
}
}
// 收益卡片
.index-card {
margin: 30rpx;
background: url('/static/indexImg/databg.png') no-repeat center center;
background-size: 100% 100%;
border-radius: 22rpx;
.index-content {
display: flex;
justify-content: space-around;
.index-main {
display: flex;
flex-direction: column;
align-items: center;
margin: 50rpx;
color: #fff;
.index-num {
margin-bottom: 15rpx;
font-size: 45rpx;
font-weight: 700;
}
.index-title {
font-size: 24rpx;
color: rgba($color: #fff, $alpha: 0.6);
}
}
}
.index-card-footer {
display: flex;
justify-content: space-between;
padding: 20rpx;
view {
font-size: 24rpx;
color: rgba($color: #fff, $alpha: 0.6);
}
}
}
// 发起收款
.pay-card {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin: 30rpx 30rpx;
margin-top: 60rpx;
height: 145rpx;
background: url('/static/indexImg/paybg.png') no-repeat center center;
background-size: 100% 100%;
color: #fff;
border-radius: 22rpx;
image {
width: 50rpx;
height: 50rpx;
margin-bottom: 20rpx;
}
.pay-text {
font-weight: 900;
font-size: 22rpx;
}
}
// nav 导航
.index-nav {
position: absolute;
bottom: 30rpx;
left: 0;
right: 0;
// background-color: tomato;
background-color: transparent;
.nav-content {
display: flex;
padding: 0 30rpx;
justify-content: space-between;
margin-bottom: 50rpx;
.nav-main {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 25%;
height: 180rpx;
background-size: 100% 100%;
border-radius: 15rpx;
color: #fff;
&:nth-child(1) {
background: url('/static/indexImg/orderbg.png') no-repeat center center;
}
&:nth-child(2) {
background: url('/static/indexImg/handoverbg.png') no-repeat center center;
}
&:nth-child(3) {
background: url('/static/indexImg/staticsbg.png') no-repeat center center;
}
image {
width: 65rpx;
height: 65rpx;
margin-bottom: 15rpx;
}
.nav-title {
font-weight: 600;
font-size: 22rpx;
}
}
}
}
.worker-content {
padding: 0.1rpx;
width: 420rpx;
background-color: #fff;
border-radius: 14rpx;
.title {
margin: 20rpx 0;
text-align: center;
font-size: 22rpx;
}
.worker-time {
transform: translateY(25rpx);
margin-left: 20rpx;
font-size: 18rpx;
}
.footer-but {
display: flex;
justify-content: space-around;
padding: 0 30rpx;
margin: 40rpx 0;
margin-top: 65rpx;
view {
flex: 0 0 35%;
display: flex;
justify-content: center;
align-items: center;
color: #fff;
letter-spacing: 2rpx;
height: 55rpx;
background-color: rgba($color: #35d757, $alpha: 0.6);
border-radius: 14rpx;
}
.confirm {
background-color: #35d757;
}
}
}
.but-content {
display: flex;
justify-content: space-between;
width: 100%;
padding: 0 30rpx;
box-sizing: border-box;
view {
flex: 1;
}
}
.switch-model {
padding: 0.1rpx 20rpx;
padding-bottom: 30rpx;
width: 470rpx;
min-height: 300rpx;
border-radius: 14rpx;
background-color: #fff;
.title {
margin: 30rpx 0;
text-align: center;
font-weight: 600;
font-size: 18rpx;
}
.sub-title {
font-size: 18px;
font-weight: 600;
.model-tag {
float: right;
background-color: #27dc70;
color: #fff;
padding: 5rpx 10rpx;
border-radius: 5rpx;
font-size: 14rpx;
}
}
.tips {
color: #595959;
font-size: 15rpx;
margin: 20rpx 0;
line-height: 2;
}
.pay-model {
display: flex;
justify-content: center;
align-items: center;
margin: 0 auto;
height: 60rpx;
border: 1rpx solid #1ab345;
border-radius: 5rpx;
background-color: rgba(241, 255, 245, 0.3);
color: #1ab245;
font-size: 18rpx;
letter-spacing: 2rpx;
image {
margin-right: 8rpx;
width: 20rpx;
height: 20rpx;
}
}
.hover-model {
background-color: #1ab345;
color: #fff;
}
}</style>

View File

@@ -0,0 +1,151 @@
<!--
环境变量切换组件
@author terrfly
@site https://www.jeequan.com
@date 2022/04/12 10:14
-->
<template>
<view>
<!-- 当包含环境变量 && 不是生产环境时 -->
<view
class="login-test"
v-if="
vdata.currentEnvEnum &&
vdata.currentEnvEnum != appConfig.ENV_ENUM.PRODUCTION
"
><text>{{ vdata.currentEnvEnum }}</text></view
>
<!-- 切换环境提示 -->
<uni-popup ref="uniPopupRef" :mask-click="false" :open="true">
<view class="uni-popup-dialog">
<image
@tap="uniClose"
class="uni-dialog-close"
src="/static/indexImg/del.svg"
mode="scaleToFill"
/>
<view class="uni-dialog-button-box">
<view
class="uni-dialog-button"
style="border-bottom: 2rpx solid #e1e1e1"
@click="changeEnvFunc(appConfig.ENV_ENUM.DEVELOPMENT)"
><text>开发环境</text></view
>
<view
class="uni-dialog-button"
style="border-bottom: 2rpx solid #e1e1e1"
@click="changeEnvFunc(appConfig.ENV_ENUM.TEST)"
><text>测试环境</text></view
>
<view
class="uni-dialog-button"
style="border-bottom: 2rpx solid #e1e1e1"
@click="changeEnvFunc(appConfig.ENV_ENUM.DEMO)"
><text>演示环境</text></view
>
<view
class="uni-dialog-button"
@click="changeEnvFunc(appConfig.ENV_ENUM.PRODUCTION)"
><text>生产环境</text></view
>
</view>
</view>
</uni-popup>
</view>
</template>
<script setup>
import { reactive, ref, onMounted } from 'vue'
import appConfig from '@/config/appConfig.js'
import envConfig from '@/env/config.js'
import storageManage from '@/commons/utils/storageManage.js'
const uniPopupRef = ref()
// emit
const emit = defineEmits(['change'])
const vdata = reactive({
currentEnvEnum: '', // 当前环境变量
count: 0, // 当前点击次数
})
onMounted(() => {
vdata.currentEnvEnum = storageManage.env()
})
// 父组件的点击事件
function tapFunc() {
vdata.count++
if (vdata.count >= 10) {
vdata.count = 0
uniPopupRef.value.open()
}
}
// 改变环境函数
function changeEnvFunc(envMode) {
vdata.currentEnvEnum = envMode //显示信息
envConfig.changeEnv(envMode) //更改请求包
storageManage.env(envMode) // 改变存储
uniPopupRef.value.close() //弹层关闭
}
// 关闭弹窗
function uniClose() {
uniPopupRef.value.close()
}
defineExpose({ tapFunc })
</script>
<style lang="scss" scoped>
.uni-popup-dialog {
position: relative;
box-sizing: border-box;
width: 500rpx;
height: 600rpx;
overflow: hidden;
border-radius: 30rpx;
background-color: #ffffff;
display: flex;
flex-direction: column;
justify-content: space-around;
.uni-dialog-close {
position: absolute;
top: 20rpx;
right: 20rpx;
width: 50rpx;
height: 50rpx;
}
.uni-dialog-button-box {
width: 100%;
box-sizing: border-box;
display: flex;
justify-content: space-around;
flex-direction: column;
text-align: center;
padding: 0 40rpx;
.uni-dialog-button {
width: 100%;
height: 150rpx;
line-height: 150rpx;
text {
font-size: 40rpx;
}
}
}
}
.login-test {
position: absolute;
bottom: 70rpx;
width: 100%;
text-align: center;
text {
color: #666f80;
}
}
</style>

View File

@@ -0,0 +1,115 @@
<template>
<view class="login-header">
<image src="/static/logo.png" mode="scaleToFill" @tap="switchEnv" />
<view class="login-title">计全收银Pro</view>
</view>
<LoginInput title="账号" place="请输入登录名/手机号" require ref="refAccount" v-model:value="vdata.username" />
<LoginInput title="密码" password place="请输入密码" require type="password" ref="refPwd" v-model:value="vdata.pwd" />
<view class="button-main">
<JButton @click="submit">登录</JButton>
</view>
<!-- #ifdef MP-ALIPAY -->
<view class="support-content">
<view class="sup-title">技术支持</view>
<view class="phone-number">{{ vdata.phone || '18611727422' }}</view>
</view>
<!-- #endif -->
<EnvChangeTips ref="refEnv" />
</template>
<script setup>
import { onMounted, reactive, ref } from 'vue'
import { $login, $userInfo, $getCompanyInfos } from '@/http/apiManager'
import EnvChangeTips from './components/EnvChangeTips.vue'
import storageManage from '@/commons/utils/storageManage.js'
const refAccount = ref(null)
const refPwd = ref(null)
const refEnv = ref(null)
const vdata = reactive({})
onMounted(() => {
// #ifdef MP-WEIXIN
wxfaceapp.checkWxFacePayOsInfo({
success(res) {
console.log('success [checkWxFacePayOsInfo]', res)
storageManage.deviceNo(res.osSerialNumber) //本地存储写入 设备号
console.log(storageManage.deviceNo())
},
fail(err) {
console.log('fail [checkWxFacePayOsInfo]', err)
},
})
// #endif
// #ifdef MP-ALIPAY
my.ix.getSysProp({
key: 'ro.serialno',
success: (r) => {
console.log('r', r)
storageManage.deviceNo(r.value) //本地存储写入 设备号
},
})
// #endif
$getCompanyInfos().then(({ bizData }) => {
const { siteInfo } = bizData
vdata.phone = siteInfo.companyTel
})
})
const submit = () => {
console.log('submit')
if (refAccount.value.check() && refPwd.value.check()) {
$login(vdata).then(({ bizData }) => {
// 保存 token
storageManage.token(bizData.iToken)
// 请求用户信息
return $userInfo().then(({ bizData }) => {
// 保存用户数据
storageManage.userInfo(bizData)
// 存储设备号
// #ifdef MP-WEIXIN
//跳转首页
uni.reLaunch({
url: '/pages/index/index',
})
// #endif
// #ifdef MP-ALIPAY
console.log('执行')
uni.reLaunch({
url: '/pages/aliIndex/index',
})
// #endif
})
})
}
}
const switchEnv = () => refEnv.value.tapFunc()
</script>
<style lang="scss" scoped>
.login-header {
display: flex;
flex-direction: column;
align-items: center;
image {
margin-top: 80rpx;
width: 260rpx;
height: 260rpx;
}
.login-title {
margin-bottom: 50rpx;
font-weight: 700;
font-size: 46rpx;
}
}
.button-main {
margin-top: 80rpx;
}
.support-content {
display: flex;
justify-content: center;
margin-top: 50rpx;
font-size: 30rpx;
font-weight: 500;
}
</style>

View File

@@ -0,0 +1,102 @@
<template>
<view class="card-wrapper" @tap="emits('click')">
<view class="img-box" :style="{ backgroundColor: payImg(wayCodeType).bgColor }">
<image :src="payImg(wayCodeType).imgUrl" mode="scaleToFill" />
</view>
<view class="card-info">
<view class="card-title">
<view class="title">收款</view>
<view class="num">+{{ (amount / 100).toFixed(2) }}</view>
</view>
<view class="order-number">
<view class="order-title">订单编号</view>
{{ payOrderId }}
</view>
<view class="card-time">
<view class="created-time">{{ createdAt }}</view>
<view class="card-state" :style="{ backgroundColor: payState(state).color }">{{ payState(state).text }}</view>
</view>
</view>
</view>
</template>
<script setup>
const emits = defineEmits(['click'])
const props = defineProps({
payOrderId: { type: String }, //订单编号
createdAt: { type: String }, //创建 时间
amount: { type: [String, Number] }, //收款金额
wayCodeType: { type: String }, // 支付方式
state: { type: [String, Number] }, // 状态
})
const payImg = uni.$J.dataMap.payImg
const payState = uni.$J.dataMap.payStateMap
</script>
<style lang="scss" scoped>
.card-wrapper {
display: flex;
margin: 30rpx;
padding: 20rpx 0 20rpx 20rpx;
background-color: #fff;
border-radius: 15rpx;
.img-box {
flex-shrink: 0;
display: flex;
justify-content: center;
align-items: center;
margin-right: 30rpx;
width: 20%;
border-radius: 14rpx;
image {
width: 80%;
height: 80%;
}
}
.card-info {
flex: 1;
.card-title {
display: flex;
justify-content: space-between;
font-size: 28rpx;
font-weight: 600;
.num {
margin-right: 20rpx;
color: rgba(45, 218, 119, 1);
}
}
.order-number {
display: flex;
margin: 20rpx 0;
font-size: 18rpx;
color: rgba(102, 102, 102, 1);
}
.card-time {
display: flex;
justify-content: space-between;
.created-time {
font-size: 18rpx;
color: rgba(179, 179, 179, 1);
}
.card-state {
padding: 8rpx 30rpx;
background-color: rgba(45, 218, 119, 1);
border-radius: 20rpx 0 0 20rpx;
font-size: 12rpx;
color: #fff;
letter-spacing: 2rpx;
}
}
}
}
</style>

View File

@@ -0,0 +1,173 @@
<template>
<uni-drawer ref="refDrawer" mode="right">
<uni-nav-bar statusBar :border="false" backgroundColor="transparent" />
<view class="title">根据时间筛选</view>
<view class="time-wrapper">
<view class="time-star" @tap="refStarTime.show" :style="{ color: vdata.starTime ? '#000' : '#999' }">{{ vdata.starTime || '开始时间' }}</view>
---
<view class="time-end" @tap="refEndTime.show" :style="{ color: vdata.endTime ? '#000' : '#999' }">{{ vdata.endTime || '结束时间' }}</view>
</view>
<view class="title">支付方式</view>
<view class="pay-model">
<block v-for="v in payModelList" :key="v.value">
<view class="model-item" :class="{ 'selected-item': vdata.payModel.includes(v.value) }" @tap="selectedItem(v.value, 'payModel')">{{ v.label }}</view>
</block>
</view>
<view class="title">订单状态</view>
<view class="state-list pay-model">
<block v-for="v in stateList" :key="v.value">
<view class="state-item model-item" :class="{ 'selected-item': vdata.payState.includes(v.value) }" @tap="selectedItem(v.value, 'payState')">{{ v.label }}</view>
</block>
</view>
<view class="footer-button">
<view @tap="reset">重置</view>
<view class="confirm" @tap="confirm">确认</view>
</view>
</uni-drawer>
<xp-picker ref="refStarTime" mode="ymdhi" v-model="vdata.starTime" @confirm="confirmStart"><view></view></xp-picker>
<xp-picker ref="refEndTime" mode="ymdhi" v-model="vdata.endTime" @confirm="confirmEnd"><view></view></xp-picker>
</template>
<script setup>
import { inject, onMounted, reactive, ref } from 'vue'
import { startAndEndTime } from '@/commons/utils/timeInspect'
import dayjs from 'dayjs'
const params = inject('params') //引入 父级注入的依赖
const emits = defineEmits(['confirm'])
const payModelList = [
{ label: '云闪付', value: 'YSFPAY' },
{ label: '支付宝', value: 'ALIPAY' },
{ label: '银联', value: 'UNIONPAY' },
{ label: '微信', value: 'WECHAT' },
{ label: '其他', value: 'OTHER' },
]
const stateList = [
{ label: '订单生成', value: '0' },
{ label: '支付中', value: '1' },
{ label: '支付成功', value: '2' },
{ label: '支付失败', value: '3' },
{ label: '已撤销', value: '4' },
{ label: '已退款', value: '5' },
{ label: '订单关闭', value: '6' },
]
const vdata = reactive({
starTime: '',
endTime: '',
payModel: [],
payState: [],
})
const refStarTime = ref(null)
const refEndTime = ref(null)
const refDrawer = ref(null)
const open = () => {
refDrawer.value.open()
}
const close = () => {
refDrawer.value.close()
}
const confirmStart = (e) => {
vdata.starTime = dayjs(e.timestamp).format('YYYY-MM-DD hh:mm')
console.log(e)
}
const confirmEnd = (e) => {
vdata.endTime = dayjs(e.timestamp).format('YYYY-MM-DD hh:mm')
}
const selectedItem = (v, key) => {
if (vdata[key].includes(v))
return vdata[key].splice(
vdata[key].findIndex((item) => item == v),
1
)
vdata[key].push(v)
}
const confirm = () => {
if (vdata.starTime || vdata.endTime) {
if (!startAndEndTime(vdata.starTime, vdata.endTime)) return
params.queryDateRange = `customDateTime_${vdata.starTime + ':00'}_${vdata.endTime + ':00'}`
}
params.unionOrderState = vdata.payState.join(',')
params.unionWayCodeType = vdata.payModel.join(',')
emits('confirm')
close()
}
const reset = () => {
vdata.starTime = ''
vdata.endTime = ''
vdata.payModel = []
vdata.payState = []
}
defineExpose({ open, close })
</script>
<style lang="scss" scoped>
.title {
margin-top: 20rpx;
margin-left: 30rpx;
font-size: 24rpx;
font-weight: 600;
letter-spacing: 2rpx;
}
.time-wrapper {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 20rpx;
font-size: 16rpx;
letter-spacing: 2rpx;
.time-star,
.time-end {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
margin: 0 30rpx;
height: 50rpx;
background-color: rgb(235, 227, 227);
border-radius: 10rpx;
}
}
.pay-model {
display: flex;
flex-wrap: wrap;
padding: 10rpx;
.model-item {
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap;
margin: 20rpx;
height: 50rpx;
width: 24%;
font-size: 16rpx;
letter-spacing: 2rpx;
background-color: rgba($color: #000000, $alpha: 0.15);
border-radius: 10rpx;
}
}
.footer-button {
display: flex;
justify-content: space-around;
padding: 30rpx;
margin-top: 80rpx;
color: #fff;
font-size: 18rpx;
letter-spacing: 2rpx;
view {
display: flex;
align-items: center;
justify-content: center;
width: 220rpx;
height: 60rpx;
border-radius: 10rpx;
background-color: rgba($color: #666666, $alpha: 0.3);
}
.confirm {
background-color: $v-primary;
}
}
.selected-item {
background-color: $v-primary !important;
color: #fff;
}
</style>

View File

@@ -0,0 +1,138 @@
<template>
<view class="page-content">
<!-- #ifdef MP-WEIXIN -->
<uni-nav-bar statusBar :border="false" leftIcon="left" fixed title="订单列表" backgroundColor="#f5f7f5"
@clickLeft="navBack" />
<!-- #endif -->
<!--#ifdef MP-ALIPAY -->
<uni-nav-bar statusBar :border="false" title="订单列表" fixed backgroundColor="#f5f7f5" @clickLeft="navBack" />
<!-- #endif -->
<view class="order-search-content">
<view class="order-search">
<image src="/static/iconImg/icon-search.svg" mode="scaleToFill" />
<input type="text" v-model="vdata.unionOrderId" confirm-type="search" @confirm="search"
placeholder="请输入订单号查询" />
</view>
<image class="screen" src="/static/iconImg/icon-screen.svg" mode="scaleToFill" @tap="refScreen.open" />
</view>
<!-- 卡片部分 -->
<block v-for="v in vdata.orderList" :key="v.payOrderId">
<OrderCard v-bind="v" @click="toDetails(v.payOrderId)" />
</block>
<view class="list-null" v-if="!vdata.hasNext">暂无更多数据</view>
</view>
<OrderScreen ref="refScreen" @confirm="search" />
</template>
<script setup>
import { provide, reactive, ref } from 'vue'
import { onLoad, onReachBottom, onUnload } from '@dcloudio/uni-app'
import { req, API_URL_PAY_ORDER_LIST } from '@/http/apiManager.js'
import OrderScreen from './components/OrderScreen'
import OrderCard from './components/OrderCard.vue'
const refScreen = ref(null)
const vdata = reactive({
orderList: [],
unionOrderId: '',
hasNext: true,
})
const params = { pageSize: 10, pageNumber: 1, queryDateRange: '' }
provide('params', params)
const getList = () => {
if (!vdata.hasNext) return
req.list(API_URL_PAY_ORDER_LIST, params).then(({ bizData }) => {
vdata.orderList.push(...bizData.records)
vdata.hasNext = bizData.hasNext
})
}
getList()
const search = () => {
params.unionOrderId = vdata.unionOrderId
vdata.orderList = []
params.pageNumber = 1
vdata.hasNext = true
getList()
}
onReachBottom(() => {
params.pageNumber += 1
getList()
})
// 跳转详情页
const toDetails = (orderId) => uni.navigateTo({ url: '/pages/order/orderDetails?orderId=' + orderId })
uni.$on('ORDER_LIST', (data) => {
console.log('刷新列表');
vdata.orderList = []
params.pageNumber = 1
getList()
})
const navBack = () => {
uni.navigateBack()
}
onUnload(() => {
uni.$off('ORDER_LIST')
})
</script>
<style lang="scss" scoped>
.page-content {
padding: 0.1rpx;
min-height: calc(100vh - 5rpx);
background-color: #f5f7f5;
.order-search-content {
display: flex;
align-items: center;
.order-search {
flex: 1;
display: flex;
align-items: center;
margin: 30rpx;
margin-bottom: 0;
height: 80rpx;
background-color: #fff;
border-radius: 15rpx;
image {
margin-left: 20rpx;
width: 35rpx;
height: 35rpx;
}
input {
width: 100%;
height: 100%;
font-size: 22rpx;
margin-left: 20rpx;
}
}
.screen {
margin-right: 30rpx;
transform: translateY(15rpx);
width: 50rpx;
height: 50rpx;
}
}
}
.list-null {
display: flex;
justify-content: space-between;
align-items: center;
color: #666;
margin: 30rpx 0;
padding: 0 30rpx;
font-size: 24rpx;
font-weight: 600;
&::after,
&::before {
content: "";
display: block;
width: 35%;
height: 2rpx;
background-color: #ededed;
}
}
</style>

View File

@@ -0,0 +1,133 @@
<template>
<view class="details-header-card">
<view class="order-num">
<view class="img-box" :style="{ backgroundColor: payModel(vdata.wayCodeType).bgColor }">
<image :src="payModel(vdata.wayCodeType).imgUrl" mode="scaleToFill" />
</view>
<view class="payment">{{ (vdata.amount / 100).toFixed(2) }}</view>
</view>
<view class="card-text">收款金额</view>
<view class="refund" v-if="vdata.refundState != 2" @tap="toRefund">退款</view>
</view>
<view class="line" style="height: 20rpx"></view>
<view class="order-info">
<view class="title">订单金额</view>
<view class="info">{{ (vdata.amount / 100).toFixed(2) }}</view>
</view>
<view class="order-info">
<view class="title">退款金额</view>
<view class="info">{{ (vdata.refundAmount / 100).toFixed(2) }}</view>
</view>
<view class="order-info">
<view class="title">订单号</view>
<view class="info">{{ vdata.payOrderId }}</view>
</view>
<view class="order-info">
<view class="title">订单时间</view>
<view class="info">{{ vdata.createdAt }}</view>
</view>
<view class="line"></view>
<view class="order-info">
<view class="title">支付方式</view>
<view class="info">{{ payModel(vdata.wayCodeType).title }}</view>
</view>
<view class="order-info">
<view class="title">订单状态</view>
<view class="info">{{ payState(vdata.state).text }}</view>
</view>
<view class="line"></view>
<view class="order-info">
<view class="title">门店名称</view>
<view class="info">{{ vdata.storeName }}</view>
</view>
<view class="order-info">
<view class="title">操作员名称</view>
<view class="info">{{ vdata.storeUserName }}</view>
</view>
</template>
<script setup>
import { reactive, ref } from 'vue'
import { onLoad, onUnload } from '@dcloudio/uni-app'
import { req, API_URL_PAY_ORDER_LIST } from '@/http/apiManager.js'
onLoad((options) => {
getDetails(options.orderId)
})
const vdata = reactive({})
const payModel = uni.$J.dataMap.payImg
const payState = uni.$J.dataMap.payStateMap
const getDetails = (id) => {
req.getById(API_URL_PAY_ORDER_LIST, id).then(({ bizData }) => {
Object.assign(vdata, bizData)
})
}
const toRefund = (oderId) => uni.navigateTo({ url: '/pages/orderRefund/orderRefund?orderId=' + vdata.payOrderId })
uni.$on('ORDER_DETAILS', (data) => {
getDetails(vdata.payOrderId)
})
onUnload(() => {
uni.$off('ORDER_DETAILS')
})
</script>
<style lang="scss" scoped>
.details-header-card {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 280rpx;
.order-num {
display: flex;
align-items: center;
.img-box {
display: flex;
justify-content: center;
align-items: center;
margin-right: 20rpx;
width: 80rpx;
height: 80rpx;
border-radius: 14rpx;
image {
width: 80%;
height: 80%;
}
}
.payment {
display: flex;
justify-content: center;
text-align: center;
font-size: 40rpx;
font-weight: 700;
color: rgba(45, 218, 119, 1);
}
}
.card-text {
margin-top: 30rpx;
color: #666;
font-size: 24rpx;
}
}
.line {
width: 100%;
height: 2rpx;
background-color: #ededed;
}
.order-info {
display: flex;
justify-content: space-between;
margin: 30rpx 20rpx;
font-size: 22rpx;
.info {
color: #666;
}
}
.refund {
padding: 15rpx 30rpx;
background-color: tomato;
margin-top: 30rpx;
border-radius: 14rpx;
color: rgba($color: #fff, $alpha: 0.7);
font-size: 20rpx;
}
</style>

View File

@@ -0,0 +1,218 @@
<template>
<view class="refund-amount">
<view class="amount">{{ (vdata.amount / 100).toFixed(2) }}</view>
<view class="title">收款金额</view>
</view>
<view class="refund">
<view>已退金额</view>
<view>{{ (vdata.refundAmount / 100).toFixed(2) }}</view>
</view>
<view class="refund">
<view>可退金额</view>
<view>{{ ((vdata.amount - vdata.refundAmount) / 100).toFixed(2) }}</view>
</view>
<view class="line"></view>
<view class="input-title">输入退款金额</view>
<view class="input-refund">
<input type="digit" placeholder="请输入退款金额" v-model="vdata.refundAmountNum" />
<view class="input-but" @tap="sumOrReset">
{{ vdata.refundAmountNum ? '取消' : '全额' }}
</view>
</view>
<view class="remarks-input">
<view class="remarks">退款原因:</view>
<input type="text" placeholder="请输入退款原因" maxlength="50" v-model="vdata.refundReason" />
</view>
<view class="refund-button" @tap="open">退款</view>
<uni-popup ref="refPopup" type="center">
<view class="refund-pwd">
<view class="title">退款密码</view>
<view class="input-box">
<input type="number" maxlength="6" password v-model="vdata.refundPassword" focus
style="flex-grow: 1; width: 100%" />
</view>
<view class="button-box">
<view @tap="close">取消</view>
<view class="confirm" @tap="confirmRefund">确认</view>
</view>
</view>
</uni-popup>
</template>
<script setup>
import { reactive, ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import { req, API_URL_PAY_ORDER_LIST, $payOrderRefund } from '@/http/apiManager.js'
onLoad((options) => {
getDetails(options.orderId)
})
const vdata = reactive({})
const refPopup = ref(null)
const getDetails = (id) => {
req.getById(API_URL_PAY_ORDER_LIST, id).then(({ bizData }) => {
Object.assign(vdata, bizData)
})
}
const sumOrReset = () => {
return vdata.refundAmountNum ? (vdata.refundAmountNum = '') : (vdata.refundAmountNum = ((vdata.amount - vdata.refundAmount) / 100).toFixed(2))
}
const open = () => {
if (!vdata.refundReason) return errText('请输入退款原因')
refPopup.value.open()
}
const close = () => refPopup.value.close()
const confirmRefund = () => {
if (!vdata.refundPassword) return errText('请输入密码')
uni.showLoading({
title: '请稍等',
mask: true,
})
$payOrderRefund(vdata)
.then((res) => {
uni.showLoading()
uni.$emit('ORDER_DETAILS')
uni.$emit('ORDER_LIST')
uni.$J.showToast('退款成功').then((res) => {
close()
uni.navigateBack()
})
})
.catch((err) => {
uni.hideLoading()
})
}
const errText = (title) => uni.showToast({ title, icon: 'none' })
</script>
<style lang="scss" scoped>
.refund-amount {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.amount {
margin-top: 30rpx;
font-size: 40rpx;
font-weight: 600;
color: rgba(45, 218, 119, 1);
}
.title {
margin-top: 30rpx;
font-size: 18rpx;
}
}
.refund {
display: flex;
justify-content: space-between;
padding: 30rpx;
font-size: 24rpx;
}
.line {
width: 100%;
height: 10rpx;
background-color: #ededed;
}
.input-title {
margin: 30rpx 0;
text-align: center;
}
.input-refund {
display: flex;
align-items: center;
background-color: #ededed;
padding: 10rpx 30rpx;
input {
flex: 1;
text-align: center;
}
.input-but {
color: $v-primary;
}
}
.remarks-input {
display: flex;
margin: 20rpx 0;
padding: 10rpx 30rpx;
height: 160rpx;
background-color: #ededed;
/* #ifdef MP-ALIPAY */
input {
transform: translateY(-7rpx);
}
/* #endif */
}
.refund-button {
display: flex;
justify-content: center;
align-items: center;
margin: 0 auto;
width: 260rpx;
height: 60rpx;
background-color: rgba(242, 77, 77, 1);
color: #fff;
font-size: 22rpx;
letter-spacing: 2rpx;
border-radius: 14rpx;
}
.refund-pwd {
padding: 15rpx;
width: 420rpx;
height: 260rpx;
border-radius: 14rpx;
background-color: #fff;
.title {
margin-bottom: 50rpx;
font-size: 20rpx;
text-align: center;
}
.input-box {
margin: 0 auto;
width: 90%;
border-bottom: 1rpx solid #000;
input {
padding: 10rpx 0;
font-size: 24rpx;
}
}
.button-box {
display: flex;
justify-content: space-around;
margin-top: 60rpx;
view {
flex: 0 0 38%;
height: 55rpx;
display: flex;
justify-content: center;
align-items: center;
font-size: 24rpx;
color: $v-primary;
border-radius: 14rpx;
}
.confirm {
background-color: $v-primary;
color: #fff;
}
}
}
</style>

View File

@@ -0,0 +1,249 @@
<template>
<view class="model-wrapper">
<view class="model-content">
<view class="title">您已切换至POS模式 </view>
<view class="sub-text">请在收银设备上输入收款金额</view>
<view class="sub-title">独立收银模式
</view>
<view class="tips">接入电源后即可使用后屏输入收款金额前屏支持刷脸支付和扫码支付</view>
<view class="sub-title">POS模式<view class="model-tag">当前正在使用</view>
</view>
<view class="tips">需要连接收银机或PC使用即插即用收款将会进入您的现有收银软件账户</view>
<view class="pay-model" hover-class="hover-model" hover-stay-time="50" @tap="switchCashier">
<image src="/static/iconImg/switch-model.svg" mode="scaleToFill" />
切换至独立收银模式
</view>
</view>
<view class="selected-cas-model">
<view class="cas-title">设置收银机刷脸兼容模式</view>
<view class="cas-sub-tips">请注意此设置可能会影响收银机刷脸收款请在专业人员指导下设置</view>
<view class="cas-sub-tips">如需设置收银机扫码兼容模式 请在系统设置中设置</view>
<radio-group @change="radioChange">
<view class="radio-wrapper">
<label class="radio">
<radio value="n" checked="true" />code+n
</label>
<label class="radio">
<radio value="r" />code+r
</label>
<label class="radio">
<radio value="rn" />code+rn
</label>
<label class="radio">
<radio value="" />code
</label>
</view>
</radio-group>
</view>
</view>
</template>
<script setup>
import { onLoad, onReady } from '@dcloudio/uni-app'
import storageManage from '@/commons/utils/storageManage.js'
/***
*前后屏 通信 通知前屏 跳转到待支付页面
* */
onLoad(() => {
wxfaceapp.postMsg({
targetAppid: 'wx4710a1619fbb3714',
content: JSON.stringify({ type: 'toPay' }),
success(res) {
console.log('通信成功', res)
},
fail(res) {
console.log('通信失败 ', res)
},
})
})
let code = '\n'
/**
* 页面初始化完成 监听 前屏 向后屏通信 将支付码 写入数据串口
* */
onReady(() => {
wxfaceapp.onRemoteMessage(function (res) {
const data = JSON.parse(res.content)
if (data.faceCode) {
wxfaceapp.writeToSerialPort({
msgToFlush: data.faceCode + code,
success(res) {
console.log('success [writeToSerialPort]', res)
},
fail(res) {
console.log('fail [writeToSerialPort]', res)
},
})
}
})
postFrontMsg({ type: 'hideAmount' })
})
const switchCashier = () => {
uni.showModal({
title: '提示',
content: '是否切换为 独立收银模式?',
showCancel: true,
success: ({ confirm, cancel }) => {
if (confirm) {
storageManage.faceModel('cashier')
postFrontMsg({ type: 'toBanner' })
uni.reLaunch({ url: '/pages/index/index' })
}
},
})
}
function postFrontMsg(content) {
wxfaceapp.postMsg({
targetAppid: 'wx4710a1619fbb3714',
content: JSON.stringify(content),
success(res) {
console.log('通信成功', res)
},
fail(res) {
console.log('通信失败 ', res)
},
})
}
const radioChange = (e) => {
const v = e.detail.value
switch (v) {
case 'r':
code = '\r'
break
case 'n':
code = '\n'
break
case 'rn':
code = '\r\n'
break
default:
code = ''
}
}
</script>
<style lang="scss" scoped>
.model-wrapper {
display: flex;
flex-direction: column;
align-items: center;
margin-top: 15vh;
min-height: 100vh;
.model-content {
width: 70%;
.title {
text-align: center;
font-weight: 600;
font-size: 29rpx;
}
.sub-text {
line-height: 3;
font-weight: 600;
font-size: 20rpx;
text-align: center;
}
.switch-but {
display: flex;
justify-content: center;
align-items: center;
margin: 0 auto;
margin-top: 60rpx;
width: 60%;
height: 50rpx;
border: 1rpx solid rgba($color: #000000, $alpha: 0.1);
border-radius: 5rpx;
background-color: rgba($color: #409eff, $alpha: 0.175);
color: #409eff;
font-size: 18rpx;
font-weight: 600;
letter-spacing: 2rpx;
}
}
}
.sub-title {
font-size: 18px;
font-weight: 600;
.model-tag {
float: right;
background-color: #27DC70;
color: #fff;
padding: 5rpx 10rpx;
border-radius: 5rpx;
font-size: 14rpx;
}
}
.tips {
color: #595959;
font-size: 15rpx;
margin: 20rpx 0;
line-height: 2;
}
.pay-model {
display: flex;
justify-content: center;
align-items: center;
margin: 0 auto;
margin-top: 60rpx;
height: 60rpx;
border: 1rpx solid #1AB345;
border-radius: 5rpx;
background-color: rgba(241, 255, 245, .3);
;
color: #1AB245;
font-size: 18rpx;
letter-spacing: 2rpx;
image {
margin-right: 8rpx;
width: 20rpx;
height: 20rpx;
}
}
.hover-model {
background-color: #1AB345;
color: #fff;
}
.selected-cas-model {
margin-top: 5vh;
.cas-title {
width: 100vw;
padding-top: 30rpx;
text-align: center;
font-size: 28rpx;
font-weight: 600;
border-top: 2rpx solid #ededed;
}
.cas-sub-tips {
margin-top: 20rpx;
color: #666;
font-size: 24rpx;
text-align: center;
}
.radio-wrapper {
display: flex;
justify-content: space-around;
margin-top: 30rpx;
}
}
</style>

View File

@@ -0,0 +1,70 @@
<template>
<view class="page-wrapper">
<view class="pay-main">
<view class="pay-title">支付成功</view>
<view class="pay-amount">支付金额{{ vdata.amount }}</view>
</view>
<view class="but-box">
<JButton @tap="toIndexPage(true)">返回首页{{ vdata.num }}</JButton>
</view>
</view>
</template>
<script setup>
import { reactive, ref } from 'vue'
import { onLoad, onUnload } from '@dcloudio/uni-app'
onLoad((options) => {
console.log('支付成功页', options)
vdata.amount = options.amount
vdata.num = 10
toIndexPage()
})
const vdata = reactive({
num: 10,
})
let timeOut = undefined
function toIndexPage(flag) {
if (flag || vdata.num <= 0) {
clearTimeout(timeOut)
return uni.redirectTo({
url: '/pages/index/index',
})
}
timeOut = setTimeout(() => {
vdata.num -= 1
toIndexPage()
}, 1000)
}
</script>
<style lang="scss" scoped>
.page-wrapper {
padding: 0.1rpx;
min-height: calc(100vh - 2rpx);
}
.pay-main {
padding: 0.1rpx;
margin: 180rpx auto;
width: 80%;
background: linear-gradient(135deg, rgba(86, 222, 114, 1) 0%, rgba(58, 233, 174, 1) 100%);
color: #fff;
border-radius: 14rpx;
.pay-title {
margin: 30rpx 0;
text-align: center;
font-size: 42rpx;
font-weight: 600;
letter-spacing: 3rpx;
}
.pay-amount {
text-align: center;
font-size: 28rpx;
font-weight: 600;
margin-bottom: 30rpx;
}
}
.but-box {
transform: translateY(200rpx);
}
</style>

View File

@@ -0,0 +1,143 @@
<template>
<view class="page-wrapper">
<view class="pay-wait">
<view class="pay-number">
<text class="icon"></text>
{{ vdata.payment }}
</view>
等待用户付款
</view>
<view class="but-content">
<JButton @tap="cancel">取消付款</JButton>
</view>
</view>
</template>
<script setup>
import { reactive, ref } from 'vue'
import { req, $appPay, API_URL_PAY_ORDER_LIST } from '@/http/apiManager'
import wxTextToSpeach from '@/commons/utils/wxTextToSpeach'
import { onLoad, onReady } from '@dcloudio/uni-app'
onLoad((options) => {
vdata.payment = options.payment
})
onReady(() => {
wxfaceapp.onRemoteMessage(function (res) {
console.log('刷脸结果:' + res.content)
const data = JSON.parse(res.content)
$appPay(vdata.payment, data.faceCode || data.code).then((r) => {
console.log('支付后结果', r)
findOrderState(r.bizData)
})
})
postFront({ type: 'amount', amount: vdata.payment }) // 通知 支付页面 支付金额
})
const vdata = reactive({
payment: '0.00',
})
const cancel = () => {
postFront({ type: 'toBanner' })
uni.navigateBack()
}
const postFront = (content) => {
wxfaceapp.postMsg({
targetAppid: 'wx4710a1619fbb3714',
content: JSON.stringify(content),
success(res) {
console.log('通信成功', res)
},
fail(res) {
console.log('通信失败 ', res)
},
})
}
function findOrderState(val) {
switch (val.orderState) {
case 2:
if (val.payDataType == 'aliapp' || val.wayCode == 'ALIPAY') {
wxTextToSpeach(`支付宝支付成功金额${vdata.payment}`)
}
if (val.payDataType == 'ysfapp' || val.wayCode == 'YSFPAY') {
wxTextToSpeach(`云闪付支付成功金额${vdata.payment}`)
}
uni.$J.showToast('支付成功').then((res) => {
uni.redirectTo({
url: '/pages/paySuc/paySuc?amount=' + vdata.payment,
})
postFront({ type: 'toBanner' }) //支付成功后 返回支付页面
})
break
case 3:
uni.$J.showToast('支付失败' + val.errMsg)
break
case 4:
uni.$J.showToast('订单撤销')
break
case 1:
// 支付中 进行查单操作
console.log('订单状态1进行查单 操作 ')
findOrder(val.payOrderId)
break
}
}
let num = 1
function findOrder(payOrderId) {
if (num > 15)
return uni.showModal({
title: '确认订单状态',
content: '订单状态查询失败 请与客户确认订单状态 点击确认跳转订单列表页面',
showCancel: true,
success: ({ confirm, cancel }) => {
if (confirm) {
uni.navigateTo({
url: '/pages/order/order',
})
}
},
})
uni.$J.showToast(`支付中正在进行第${num}次订单查询`).then((res) => {
req.getById(API_URL_PAY_ORDER_LIST, payOrderId).then(({ bizData }) => {
num += 1
console.log('查单结果', bizData)
bizData.orderState = bizData.state
findOrderState(bizData)
})
})
}
</script>
<style lang="scss" scoped>
.page-wrapper {
padding: 0.1rpx 30rpx;
.pay-wait {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-top: 140rpx;
height: 220rpx;
background: linear-gradient(135deg, rgba(86, 222, 114, 1) 0%, rgba(58, 233, 174, 1) 100%);
border-radius: 15rpx;
color: #fff;
.pay-number {
display: flex;
align-items: flex-end;
margin-bottom: 15rpx;
font-size: 60rpx;
font-weight: 700;
.icon {
transform: translateY(-7rpx);
font-size: 28rpx;
}
}
}
.but-content {
transform: translateY(400rpx);
}
}
</style>

View File

@@ -0,0 +1,138 @@
<template>
<view class="page-wrapper">
<view class="pay-number">
<view class="pay-icon"></view>
{{ vdata.payment }}
</view>
<view class="keyboard-list">
<block v-for="v in keyboardList" :key="v.value">
<view class="keyboard-item" hover-class="hover-button" hover-stay-time="50" @tap="changeKey(v.value)">
<image v-if="v.value == 'del'" :src="v.key" mode="scaleToFill" />
<text v-else>{{ v.key }}</text>
</view>
</block>
</view>
<view class="pay-submit" hover-class="hover-submit" hover-stay-time="50" @tap="jumpPage">
<image src="/static/indexImg/pay.png" mode="scaleToFill" />
普通收款
</view>
</view>
</template>
<script setup>
import { reactive, ref } from 'vue'
const keyboardList = [
{ key: '1', value: '1' },
{ key: '2', value: '2' },
{ key: '3', value: '3' },
{ key: '4', value: '4' },
{ key: '5', value: '5' },
{ key: '6', value: '6' },
{ key: '7', value: '7' },
{ key: '8', value: '8' },
{ key: '9', value: '9' },
{ key: '.', value: '.' },
{ key: '0', value: '0' },
{ key: '/static/iconImg/escB.png', value: 'del' },
]
const vdata = reactive({
payment: '0.00',
})
const changeKey = (v) => {
if (v == 'del') {
vdata.payment = vdata.payment.slice(0, vdata.payment.length - 1)
if (vdata.payment == '') vdata.payment = '0.00'
return
}
if (vdata.payment == '0.00' && (v == '.' || v == '0')) return (vdata.payment = '0.')
if (vdata.payment == '0.00') return (vdata.payment = v)
if (vdata.payment.includes('.') && v == '.') return uni.$J.showToast('只能包含一位小数点')
if (vdata.payment.includes('.') && vdata.payment.split('.')[1].length == 2) return uni.$J.showToast('只能包含小数点后两位')
return (vdata.payment += v)
}
const jumpPage = () => {
wxfaceapp.postMsg({
targetAppid: 'wx4710a1619fbb3714',
content: JSON.stringify({ type: 'toPay' }),
success(res) {
console.log('通信成功', res)
},
fail(res) {
console.log('通信失败 ', res)
},
})
uni.navigateTo({ url: '/pages/payWait/payWait?payment=' + vdata.payment })
}
</script>
<style lang="scss" scoped>
.page-wrapper {
padding: 0.1rpx 30rpx;
min-height: calc(100vh - 5rpx);
background: url('/static/indexImg/index-bg.png') no-repeat center center;
background-size: 100% 100%;
.pay-number {
margin-top: 60rpx;
display: flex;
justify-content: center;
align-items: center;
height: 180rpx;
background-color: #35d757;
border-radius: 22rpx;
font-size: 60rpx;
color: #fff;
font-weight: 600;
.pay-icon {
font-size: 28rpx;
transform: translateY(30%);
}
}
}
.keyboard-list {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
margin-top: 60rpx;
.keyboard-item {
flex: 0 0 31.5%;
display: flex;
justify-content: center;
align-items: center;
margin-top: 15rpx;
height: 110rpx;
font-weight: 700;
font-size: 36rpx;
border-radius: 22rpx;
background-color: #fff;
image {
width: 40rpx;
height: 30rpx;
}
}
}
.pay-submit {
display: flex;
justify-content: center;
align-items: center;
margin-top: 70rpx;
background-color: #35d757;
border-radius: 22rpx;
height: 120rpx;
color: #fff;
font-weight: 700;
image {
margin-right: 10rpx;
width: 35rpx;
height: 35rpx;
transform: translateY(5rpx);
}
}
.hover-button {
background-color: rgba($color: #ccc, $alpha: 0.3) !important;
}
.hover-submit {
opacity: 0.5;
}
</style>

View File

@@ -0,0 +1,131 @@
<template>
<view class="type-card" :style="{ backgroundColor: rgba }">
<view class="type-header" :style="{ backgroundColor: bgColor }" @tap="emits('upOrDown')">
<view> {{ typeName }} </view>
<view class="type-num">{{ (allAmount / 100).toFixed(2) }} /{{ allCount }}
<image src="/static/iconImg/down.svg" style="transition: 0.3s linear" :class="{ 'img-up': !isShow }"
mode="scaleToFill" />
</view>
</view>
<view class="type-main" :style="{ maxHeight: isShow ? '40vh' : '0' }">
<view class="type-item">
<view class="type-num">
<view> {{ (allAmount / 100).toFixed(2) }}</view>
<view class="title"> 成交金额 </view>
</view>
</view>
<view class="type-item">
<view class="type-num">
<view> {{ ((payAmount - refundAmount) / 100).toFixed(2) }}</view>
<view class="title"> 实收金额</view>
</view>
</view>
<view class="type-item">
<view class="type-num">
<view> {{ payCount }}/{{ allCount }}</view>
<view class="title"> 成交笔数/总笔数 </view>
</view>
</view>
<view class="type-item">
<view class="type-num">
<view> {{ (refundAmount / 100).toFixed(2) }}</view>
<view class="title"> 退款金额</view>
</view>
</view>
<view class="type-item">
<view class="type-num">
<view> {{ refundCount }}</view>
<view class="title"> 退款笔数 </view>
</view>
</view>
<view class="type-item">
<view class="type-num">
<view> {{ round }}%</view>
<view class="title"> 成功率</view>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { reactive, ref } from 'vue'
const emits = defineEmits(['upOrDown'])
const props = defineProps({
typeName: { type: String },
allCount: { type: [String, Number] },
allAmount: { type: [String, Number] },
bgColor: { type: String },
rgba: { type: String },
isShow: { type: Boolean },
refundAmount: { type: [String, Number] },
round: { type: [String, Number] },
payCount: { type: [String, Number] },
payAmount: { type: [String, Number] },
refundCount: { type: [String, Number] },
})
</script>
<style lang="scss" scoped>
.type-card {
position: relative;
z-index: 10;
margin: 0 auto;
box-sizing: border-box;
width: 95%;
border-radius: 14rpx;
overflow: hidden;
.type-header {
padding: 25rpx 20rpx;
display: flex;
justify-content: space-between;
align-items: center;
color: #fff;
font-size: 24rpx;
font-weight: 600;
.type-num {
display: flex;
align-items: center;
}
image {
margin-left: 10rpx;
width: 30rpx;
height: 30rpx;
}
}
.type-main {
display: flex;
flex-wrap: wrap;
justify-content: space-around;
padding: 0 20rpx;
overflow: hidden;
// max-height: 0;
transition: 0.3s linear;
.type-item {
flex: 0 0 30%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 20rpx 0;
text-align: center;
color: #fff;
.title {
margin-top: 10rpx;
color: rgba($color: #fff, $alpha: 0.6);
}
}
}
}
.img-up {
transform: rotate(-180deg);
}</style>

View File

@@ -0,0 +1,230 @@
<template>
<view class="stat-wrapper">
<view class="time-header">
<block v-for="(v, i) in timeList" :key="i">
<view class="time-item" :class="{ 'selected-time': v.value == vdata.selectedTime }" @tap="switchTime(v.value)">{{ v.label }}</view>
</block>
</view>
<view class="custom-content" :class="{ 'selected-custom': vdata.selectedTime == 'custom' }">
<view class="start-time" :style="{ color: vdata.startTime ? '#000' : '#666' }" @tap="refStartTime.show">{{ vdata.startTime || '请选择开始时间' }}</view>
<view class="endTime-time" :style="{ color: vdata.endTime ? '#000' : '#666' }" @tap="refEndTime.show">{{ vdata.endTime || '请选择结束时间' }}</view>
</view>
<view class="amount-list">
<view class="amount-item">
<view class="amount-num">{{ (vdata.payStat.allAmount / 100).toFixed(2) }}</view>
成交金额
</view>
<view class="amount-item">
<view class="amount-num">{{ (vdata.payStat.payAmount / 100).toFixed(2) }}</view>
实收金额
</view>
<view class="amount-item">
<view class="amount-num">{{ vdata.payStat.payCount }} /{{ vdata.payStat.allCount }} </view>
成交笔数/总笔数
</view>
<view class="amount-item">
<view class="amount-num">{{ (vdata.payStat.refundAmount / 100).toFixed(2) }}</view>
退款金额
</view>
<view class="amount-item">
<view class="amount-num">{{ vdata.payStat.refundCount }}</view>
退款笔数
</view>
<view class="amount-item">
<view class="amount-num">{{ vdata.payStat.round }} %</view>
成功率
</view>
</view>
<view class="pay-title"> 支付方式 </view>
<block v-for="(v, i) in vdata.payList" :key="v.wayType">
<PayTypeCard v-bind="v" @upOrDown="upOrDown(v)" />
</block>
<view style="height: 60rpx"></view>
</view>
<xp-picker mode="ymdhi" ref="refStartTime" @confirm="timeConfirm($event, 'startTime')">
<view></view>
</xp-picker>
<xp-picker mode="ymdhi" ref="refEndTime" @confirm="timeConfirm($event, 'endTime')">
<view></view>
</xp-picker>
</template>
<script setup>
import { reactive, ref } from 'vue'
import { $payTypeStat, $payTypeStatAll } from '@/http/apiManager'
import PayTypeCard from './components/PayTypeCard'
import dayjs from 'dayjs'
const timeList = [
{ label: '昨天', value: 'yesterday' },
{ label: '今天', value: 'today' },
{ label: '近7天', value: 'near2now_7' },
{ label: '近30天', value: 'near2yesterday_30' },
{ label: '自定义', value: 'custom' },
]
const vdata = reactive({
selectedTime: 'today',
payList: [],
payStat: {},
})
const refStartTime = ref(null)
const refEndTime = ref(null)
const params = {
method: 'wayCodeType',
queryDateRange: 'today',
}
const getPayType = () => {
$payTypeStat(params).then(({ bizData }) => {
bizData.records.forEach((v, i) => {
v.bgColor = uni.$J.dataMap.payImg(v.wayType).bgColor
v.rgba = uni.$J.dataMap.payImg(v.wayType).rgba
v.typeName = uni.$J.dataMap.payImg(v.wayType).title
if (i == 0) return (v.isShow = true)
v.isShow = false
})
vdata.payList = bizData.records
})
}
getPayType()
const getStatAll = () => {
$payTypeStatAll(params).then(({ bizData }) => {
vdata.payStat = bizData
})
}
getStatAll()
/****
* 切换 卡片收起 展开状态
* @param {v} Object 点击的卡片全部数据
* */
const upOrDown = (v) => {
v.isShow = true
vdata.payList.forEach((ite) => {
if (ite.wayType != v.wayType) {
ite.isShow = false
}
})
}
/***
* 切换时间选择器
* @param {val} STring 时间选择的 value 值
* val = custom 需要展示自定义 时间选择框
* */
const switchTime = (val) => {
vdata.selectedTime = val
if (val == 'custom') return
params.queryDateRange = val
getStatAll()
getPayType()
}
/**
* @param {e} Object 组件原生 回调 时间戳 日期
* @param {key} String vdata 对象的 key 值 赋值使用
* */
const timeConfirm = (e, key) => {
vdata[key] = dayjs(e.timestamp).format('YYYY-MM-DD hh:mm')
if (key == 'endTime' || (vdata.endTime != undefined && dayjs(vdata.startTime).isAfter(vdata.endTime))) {
params.queryDateRange = `customDateTime_${vdata.startTime + ':00'}_${vdata.endTime + ':00'}`
getStatAll()
getPayType()
}
}
</script>
<style lang="scss" scoped>
.stat-wrapper {
min-height: 100vh;
background-color: #ebf2ed;
.time-header {
display: flex;
justify-content: space-between;
padding: 20rpx;
view {
flex: 0 0 18%;
display: flex;
justify-content: center;
align-items: center;
height: 45rpx;
border-radius: 14rpx;
background-color: #d9dfda;
font-size: 18rpx;
color: #666;
}
.selected-time {
background-color: $v-primary;
color: #fff;
}
}
}
.title {
font-size: 18rpx;
color: rgba($color: #fff, $alpha: 0.6);
}
.stat-num {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: #fff;
margin: 0 auto;
margin-top: 35rpx;
width: 65%;
height: 120rpx;
border-radius: 22rpx 22rpx 0 0;
background-color: $v-primary;
.num {
font-size: 35rpx;
font-weight: 600;
}
}
.amount-list {
display: flex;
flex-wrap: wrap;
justify-content: space-around;
align-items: center;
margin: 0 auto;
margin-top: 30rpx;
padding: 15rpx 0;
width: 92%;
// height: 110rpx;
background-color: $v-primary;
border-radius: 14rpx;
.amount-item {
flex: 0 0 33%;
margin: 15rpx 0;
text-align: center;
white-space: nowrap;
color: rgba($color: #fff, $alpha: 0.6);
font-size: 20rpx;
.amount-num {
margin-bottom: 15rpx;
color: #fff !important;
}
}
}
.pay-title {
margin-top: 35rpx;
margin-bottom: 15rpx;
margin-left: 30rpx;
font-size: 22rpx;
font-weight: 600;
}
.custom-content {
display: flex;
justify-content: space-around;
overflow: hidden;
margin-top: 30rpx;
margin-bottom: 60rpx;
max-height: 0;
transition: 0.3s linear;
view {
flex: 0 0 30%;
padding: 10rpx 0;
background-color: #ded7d7;
text-align: center;
border-radius: 10rpx;
font-size: 16rpx;
}
}
.selected-custom {
max-height: 20vh;
}
</style>