同步代码

This commit is contained in:
GaoHao
2025-02-07 14:49:20 +08:00
commit 0740c3f349
1141 changed files with 167372 additions and 0 deletions

View File

@@ -0,0 +1,150 @@
<template>
<view class="info-wrapper">
<view class="dev-title" v-if="dev" @tap="devType.open(vdata.devTitle.value)">
{{ vdata.devTitle.label }}
<image src="/static/iconImg/icon-arrow-black.svg" mode="scaleToFill" />
</view>
<view class="info-title" :class="{ 'fixed-header': fixed }">
<block v-for="(v, i) in vdata.titleList" :key="i">
<view class="info-item" :class="{ 'active-title': vdata.selected == v.value }" @tap="selectedTitle(v)">
{{ v.title }}
<view class="stat-sort" :class="[calcClassName(v)]"></view>
</view>
</block>
</view>
<!-- 默认插槽位置 -->
<slot />
</view>
<JSinglePopup :list="devTypeList" ref="devType" @confirm="confirmType" />
</template>
<script setup>
import { reactive, ref } from 'vue';
const props = defineProps({
dev: { type: Boolean, default: false }, //是否展示设备类型切换标题
fixed: { type: Boolean, default: false } //是否固定头部 默认 false
});
const emits = defineEmits(['device', 'sortClick']);
const vdata = reactive({
selected: 'totalSuccAmt',
devTitle: {
label: '全部设备类型',
value: ''
},
titleList: [
{ title: '成交金额', value: 'totalSuccAmt', sort: 'ascend' },
{ title: '退款金额', value: 'totalRefundAmt', sort: 'ascend' },
{ title: '成交笔数', value: 'totalSuccNum', sort: 'ascend' },
{ title: '成功率', value: 'succRate', sort: 'ascend' }
]
});
const devType = ref(null);
const selectedTitle = (val) => {
if (vdata.selected != val.value) {
vdata.selected = val.value;
emits('sortClick', val);
return;
}
val.sort = val.sort == 'ascend' ? 'descend' : 'ascend';
emits('sortClick', val);
};
const devTypeList = reactive([
{ label: '全部', value: '' },
{ label: '电子码牌', value: '0' },
{ label: '实体码牌', value: '1' },
{ label: '实体立牌', value: '2' },
{ label: '云音响码牌', value: '3' },
{ label: '扫码POS', value: 'scan_pos"' },
{ label: '智能POS', value: 'auto_pos"' },
{ label: '收银插件', value: 'cash_plugin' }
]);
// 计算需要展示的上下箭头
const calcClassName = (val) => {
// 判断 选中 与 集合中的 value 值是否相等 不相等 则不添加类名
if (vdata.selected == val.value) {
// 返回上箭头 下箭头类名
return val.sort == 'descend' ? 'asc-sort' : 'des-sort';
}
return '';
};
const confirmType = (val) => {
vdata.devTitle = val;
emits('device', val.value);
};
</script>
<style lang="scss" scoped>
.info-wrapper {
margin: 0 35rpx 40rpx 35rpx;
border-radius: $J-b-r32;
background-color: $J-bg-ff;
.dev-title {
display: flex;
align-items: center;
padding: 0 30rpx;
height: 100rpx;
font-size: 30rpx;
background-color: #fff;
border-bottom: 1rpx solid #ededed;
image {
margin-left: 10rpx;
width: 40rpx;
height: 40rpx;
transform: rotate(180deg);
}
}
.info-title {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 40rpx;
height: 102rpx;
font-size: 24rpx;
color: $J-color-t80;
background-color: #fff;
border-bottom: 1rpx solid #ededed;
.active-title {
font-weight: 500;
color: #2980fd;
border-bottom: 5rpx solid #2980fd;
}
.info-item {
display: flex;
align-items: center;
height: 100%;
.stat-sort {
display: flex;
flex-direction: column;
align-items: center;
margin-left: 10rpx;
&::before,
&::after {
content: '';
display: block;
width: 0px;
height: 0px;
border: 8rpx solid;
border-color: #d9d9d9 transparent transparent transparent;
}
&::before {
margin-bottom: 4rpx;
transform: rotate(180deg);
}
}
.asc-sort::before {
border-color: #2980fd transparent transparent transparent;
}
.des-sort::after {
border-color: #2980fd transparent transparent transparent;
}
}
}
}
.fixed-header {
position: sticky;
top: 112rpx;
z-index: 10;
left: 0;
right: 0;
}
</style>

View File

@@ -0,0 +1,50 @@
<template>
<view class="store-wrapper">
<view class="store-left">
<image :src="storeImg" mode="scaleToFill" />
{{ storeName }}
</view>
<view class="store-right">
<text>{{ storeNum }}</text>
<slot />
</view>
</view>
</template>
<script setup>
const props = defineProps({
storeName: { type: String }, //门店名称
storeImg: { type: String }, //门店照片
storeNum: { type: Number }, //收款金额
})
</script>
<style lang="scss" scoped>
.store-wrapper {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 30rpx;
height: 100rpx;
.store-left {
display: flex;
align-items: center;
font-size: 26rpx;
color: #666;
image {
margin-right: 10rpx;
width: 40rpx;
height: 40rpx;
}
}
.store-right {
display: flex;
align-items: center;
font-size: 30rpx;
font-weight: 500;
text {
margin-right: 10rpx;
}
}
}
</style>

View File

@@ -0,0 +1,113 @@
<template>
<uni-popup ref="popup" type="bottom" mask-background-color="rgba(0,0,0,.5)" @change="change" :safe-area="false">
<view class="info-wrapper">
<view class="info-title">
<text>支付方式统计明细</text>
<view class="icon-close" @tap="popup.close()"><image src="/static/iconImg/icon-x.svg" mode="scaleToFill" /></view>
</view>
<StatInfoCard @sortClick="switchField" :fixed="true">
<JTableList ref="refTable" :reqTableDataFunc="reqTableDataFunc" :initData="false" height="220rpx" :searchData="vdata.params">
<template #tableBody="{ record }">
<statCell
:imgNone="false"
:title="record.wayCode"
:money="calcData(record[vdata.cardSelected])"
:reality="calcReality(record.totalSuccNum, record.totalNum)"
:round="vdata.cardSelected == 'round'"
:iconPre="vdata.cardSelected == 'totalFinalAmt' || vdata.cardSelected == 'totalRefundAmt '"
:iconNext="vdata.cardSelected == 'round'"
/>
</template>
</JTableList>
</StatInfoCard>
</view>
</uni-popup>
</template>
<script setup>
import { nextTick, onMounted, reactive, ref, inject } from 'vue';
import { onReachBottom } from '@dcloudio/uni-app';
import { $getStatistic } from '@/http/apiManager.js';
import StatInfoCard from './StatInfoCard.vue';
import statCell from './statCell.vue';
import cal from '@/commons/utils/cal.js';
// 获取弹窗实例
const popup = ref(null);
const refTable = ref(null);
const vdata = reactive({
cardSelected: 'totalFinalAmt',
record: {},
params: {}
});
const open = (params, record) => {
const obj = JSON.parse(JSON.stringify(params));
vdata.record = record;
obj.wayCodeType = record.wayType;
obj.method = 'wayCode';
vdata.params = obj;
popup.value.open();
nextTick(() => {
refTable.value.refTable(true);
});
};
const reqTableDataFunc = (params) => {
return $getStatistic(params);
};
//切换字段 切换排序
const switchField = (val) => {
vdata.cardSelected = val.value;
vdata.params.sortOrder = val.sort;
vdata.params.sortField = val.value;
refTable.value.refTable(true);
};
// 计算数据需不需要除以 100
const calcData = (val) => {
if (vdata.cardSelected == 'totalFinalAmt' || vdata.cardSelected == 'totalRefundAmt') return cal.cert2Dollar(val);
return val;
};
// 计算进度条
const calcReality = (val, totalNum) => {
return (val / totalNum) * 100;
};
let changePageMetaOverflowFunc = inject('changePageMetaOverflowFunc');
const change = (e) => {
if (changePageMetaOverflowFunc) {
changePageMetaOverflowFunc(!e.show);
}
};
onReachBottom(() => {});
defineExpose({ open });
</script>
<style lang="scss" scoped>
.info-wrapper {
height: 70vh;
overflow-y: scroll;
border-radius: 32rpx 32rpx 0 0;
background-color: #fff;
}
.info-title {
position: sticky;
top: 0;
left: 0;
right: 0;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 40rpx;
height: 110rpx;
font-size: 30rpx;
font-weight: 500;
border-bottom: 1rpx solid #ededed;
.icon-close {
width: 50rpx;
height: 50rpx;
border-radius: 10rpx;
background-color: #f2f2f2;
image {
width: 100%;
height: 100%;
}
}
}
</style>

View File

@@ -0,0 +1,98 @@
<template>
<view class="pay-wrapper">
<view class="pay-left">
<template v-if="imgNone">
<image v-if="devImage" :src="imgObj[devImage]" mode="scaleToFill" />
<image v-else :src="imgUrl" mode="scaleToFill" />
</template>
<view>{{ imgUrl }}</view>
<view class="pay-title single-text-beyond">{{ title }}</view>
</view>
<view class="pro-bar" :style="{ '--bar-width': reality + '%' }" v-if="round"></view>
<view class="pay-right">
<text v-show="iconPre"></text>
{{ money }}
<text v-show="iconNext">%</text>
</view>
</view>
</template>
<script setup>
import { reactive } from 'vue';
const props = defineProps({
reality: [String, Number], //实际进度
imgUrl: { type: String }, //左侧图片
title: { type: String }, //左侧标题
money: [String, Number], //右侧金额
round: { type: Boolean, default: false }, //是否展示进度条
iconPre: { type: Boolean, default: false }, //金钱
iconNext: { type: Boolean, default: false }, // 百分比
devImage: { type: String }, //如果设备
imgNone: { type: Boolean, default: true } //不需要图片
});
const imgObj = reactive({
store: '/static/indexImg/icon-store.svg', //门店
qr_code: '/static/devIconImg/icon-code.svg',
scan_pos: '/static/devIconImg/icon-pos.svg',
auto_pos: '/static/devIconImg/icon-scanPos.svg',
cash_plugin: '/static/devIconImg/icon-horn.svg',
face_app: '/static/devIconImg/icon-face-1.svg', //刷脸设备
ALIPAY: '/static/payImg/icon-zfb.svg', //支付宝
OTHER: '/static/payImg/icon-qt.svg', //其他
DCEPPAY: '/static/payImg/icon-qt.svg', //数字人民币
UNIONPAY: '/static/payImg/icon-yl.svg', //银联
WECHAT: '/static/payImg/icon-wx.svg', //微信
YSFPAY: '/static/payImg/icon-ysf.svg' //云闪付
});
</script>
<style lang="scss" scoped>
.pay-wrapper {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 30rpx;
height: 100rpx;
.pay-left {
flex: 1;
display: flex;
align-items: center;
font-size: 26rpx;
color: #666;
image {
margin-right: 10rpx;
width: 40rpx;
height: 40rpx;
}
.pay-title {
width: 178rpx;
}
}
.pro-bar {
flex-grow: 0;
flex-shrink: 0;
position: relative;
width: 100rpx;
height: 6rpx;
background-color: rgba(0, 0, 0, 0.1);
border-radius: 6rpx;
&::after {
content: '';
position: absolute;
top: 0;
left: 0;
width: var(--bar-width);
height: 100%;
border-radius: 6rpx;
background: $jeepay-bg-primary;
}
}
.pay-right {
flex: 1;
text-align: right;
font-size: 30rpx;
font-weight: 500;
}
}
</style>

View File

@@ -0,0 +1,203 @@
<template>
<uni-popup ref="popup" type="bottom" mask-background-color="rgba(0,0,0,.5)" @change="change" :safe-area="false">
<view class="info-wrapper">
<view class="info-title">
<!-- <image :src="imgObj[vdata.record.deviceType]" mode="scaleToFill" /> -->
<view class="info-content">
<view class="info-name">
<text>{{ vdata.record?.storeName || vdata.record?.deviceName }}</text>
<view class="icon-close" @tap="popup.close()">
<image src="/static/iconImg/icon-x.svg" mode="scaleToFill" />
</view>
</view>
<view class="sub-name">{{ vdata.record?.storeId || vdata.record?.deviceNo }}</view>
</view>
</view>
<view class="stat-card">
<view class="stat-money">
<view class="stat-title">入账金额 ()</view>
<view class="stat-num-title">{{ cal.cert2Dollar(vdata.totalSuccAmt - vdata.totalFeeAmt - vdata.totalRefundAmt) }}</view>
</view>
<view class="stat-card-info">
<view class="stat-item">
<view class="stat-title">成交订单金额 ()</view>
<view class="stat-num">{{ cal.cert2Dollar(vdata.totalSuccAmt) }}</view>
</view>
<view class="stat-item">
<view class="stat-title">成交订单笔数</view>
<view class="stat-num">{{ vdata.totalSuccNum }}</view>
</view>
<view class="stat-item">
<view class="stat-title">交易成功率</view>
<view class="stat-num">{{ vdata.succRate }}%</view>
</view>
<view class="stat-item">
<view class="stat-title">交易手续费</view>
<view class="stat-num">{{ cal.cert2Dollar(vdata.totalFeeAmt) }}</view>
</view>
<!-- <view class="stat-item">
<view class="stat-title">垫资手续费</view>
<view class="stat-num">---</view>
</view> -->
<!-- <view class="stat-item">
<view class="stat-title">优惠金额</view>
<view class="stat-num">{{ cal.cert2Dollar(vdata.totalDiscountAmt) }}%</view>
</view> -->
<view class="stat-item">
<view class="stat-title">退款金额 ()</view>
<view class="stat-num">{{ cal.cert2Dollar(vdata.totalRefundAmt) }}</view>
</view>
<view class="stat-item">
<view class="stat-title">退款笔数</view>
<view class="stat-num">{{ vdata.totalRefundNum }}</view>
</view>
<!-- <view class="stat-item">
<view class="stat-title">补贴金额</view>
<view class="stat-num">---</view>
</view> -->
<!-- <view class="stat-item">
<view class="stat-title">交易成功笔数</view>
<view class="stat-num">{{ vdata.totalSuccNum }}</view>
</view>
<view class="stat-item">
<view class="stat-title">成功交易金额 ()</view>
<view class="stat-num">{{ cal.cert2Dollar(vdata.totalSuccAmt) }}</view>
</view>
<view class="stat-item">
<view class="stat-title">总交易笔数</view>
<view class="stat-num">{{ vdata.totalNum }}</view>
</view> -->
</view>
</view>
</view>
</uni-popup>
</template>
<script setup>
import { onMounted, reactive, ref, inject } from 'vue';
import { $getStatInfo, $getStatistic } from '@/http/apiManager.js';
import cal from '@/commons/utils/cal.js';
// 获取弹窗实例
const popup = ref(null);
const vdata = reactive({
record: {}
});
const imgObj = reactive({
store: '/static/indexImg/icon-store.svg',
qr_code: '/pageDevice/static/devIconImg/icon-code.svg',
scan_pos: '/pageDevice/static/devIconImg/icon-pos.svg',
auto_pos: '/pageDevice/static/devIconImg/icon-scanPos.svg',
cash_plugin: '/pageDevice/static/devIconImg/icon-horn.svg'
});
const getInfo = (params) => {
$getStatInfo(params).then(({ bizData }) => {
Object.assign(vdata, bizData);
});
};
const open = (params, record) => {
const obj = JSON.parse(JSON.stringify(params));
vdata.record = record;
if (!vdata.record.deviceType) vdata.record.deviceType = 'store';
obj.deviceNo = record.deviceNo ? record.deviceNo : '';
obj.storeId = record.storeId ? record.storeId : '';
getInfo(obj);
popup.value.open();
};
let changePageMetaOverflowFunc = inject('changePageMetaOverflowFunc');
const change = (e) => {
if (changePageMetaOverflowFunc) {
changePageMetaOverflowFunc(!e.show);
}
};
defineExpose({ open });
</script>
<style lang="scss" scoped>
.info-wrapper {
// height: 777rpx;
border-radius: 32rpx 32rpx 0 0;
background-color: #fff;
padding-bottom: 32upx;
}
.info-title {
display: flex;
align-items: center;
padding: 0 40rpx;
height: 160rpx;
border-bottom: 1rpx solid #ededed;
image {
margin-right: 20rpx;
width: 100rpx;
height: 100rpx;
}
.info-content {
flex: 1;
.info-name {
display: flex;
justify-content: space-between;
font-size: 30rpx;
font-weight: 500;
.icon-close {
width: 50rpx;
height: 50rpx;
border-radius: 10rpx;
background-color: #f2f2f2;
image {
width: 100%;
height: 100%;
}
}
}
.sub-name {
font-size: 26rpx;
color: #808080;
}
}
}
.stat-card {
position: relative;
margin: 0 30rpx;
background-color: $J-bg-ff;
border-radius: $J-b-r32;
.stat-title {
white-space: nowrap;
margin-bottom: 10rpx;
font-size: 24rpx;
color: #a6a6a6;
}
.stat-money {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 202rpx;
box-shadow: 0 50rpx 70rpx -60rpx rgba(107, 130, 153, 0.2);
.stat-num-title {
font-size: 60rpx;
font-weight: 700;
color: #2980fd;
}
}
.stat-card-info {
display: flex;
justify-content: space-between;
flex-wrap: wrap;
padding: 0 68rpx 50rpx 68rpx;
.stat-item {
width: 175rpx;
margin-top: 50rpx;
text-align: center;
.stat-num {
font-size: 30rpx;
font-weight: 700;
}
}
}
.stat-tips {
position: absolute;
top: 30rpx;
right: 30rpx;
}
}
</style>