19 Commits

  1. 10
      src/api/cash/cash.js
  2. 9
      src/api/cash/financialAccount.js
  3. 27
      src/assets/SvgIcons/wallet_management.svg
  4. 3
      src/components/locales/lang/en.js
  5. 5
      src/components/locales/lang/zh-CN.js
  6. 50
      src/components/workspace/CashManagement.vue
  7. 114
      src/components/workspace/GoldManagement.vue
  8. 90
      src/router/index.js
  9. 0
      src/static/common.js
  10. 14
      src/utils/menuUtils.js
  11. 1
      src/views/home.vue
  12. 1
      src/views/moneyManage/executor/executor.vue
  13. 228
      src/views/moneyManage/financialAccount/performanceAttribution.vue
  14. 1
      src/views/moneyManage/receiveDetail/receiveService.vue
  15. 15
      src/views/usergold/gold/clientCount.vue
  16. 913
      src/views/walletManage/WalletBalance.vue
  17. 472
      src/views/walletManage/components/WalletDetailTemplate.vue
  18. 10
      src/views/walletManage/walletDetail/BJWallet.vue
  19. 10
      src/views/walletManage/walletDetail/CAWallet.vue
  20. 12
      src/views/walletManage/walletDetail/HKWallet.vue
  21. 10
      src/views/walletManage/walletDetail/MyWallet.vue
  22. 10
      src/views/walletManage/walletDetail/SGWalletCM.vue
  23. 10
      src/views/walletManage/walletDetail/SGWalletHC.vue
  24. 10
      src/views/walletManage/walletDetail/THWalletHA.vue
  25. 10
      src/views/walletManage/walletDetail/THWalletHS.vue
  26. 10
      src/views/walletManage/walletDetail/VNWallet.vue

10
src/api/cash/cash.js

@ -0,0 +1,10 @@
import http from '@/util/http.js'
// 查询钱包记录
export const selectWalletRecords = (data) => {
return http({
method: 'POST',
url: '/cashCollection/selectWalletRecords',
data
})
}

9
src/api/cash/financialAccount.js

@ -44,3 +44,12 @@ export const exportFunds = (data) => {
data data
}) })
} }
// 业绩调整
export const adjustment = (data) => {
return http({
method: 'POST',
url: '/cashCollection/adjust',
data
})
}

27
src/assets/SvgIcons/wallet_management.svg

@ -0,0 +1,27 @@
<svg width="31" height="31" viewBox="0 0 31 31" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.74612 0.375959C11.6259 0.0545255 15.0739 -0.278254 20.0466 0.375959C25.0192 1.03017 28.1825 3.79754 29.2367 7.13376C30.2909 10.47 30.2144 16.9942 29.826 20.258C29.507 24.1645 27.2331 28.7886 21.7954 29.5942C16.3576 30.3997 9.67485 30.1849 6.99417 29.2304C4.3135 28.2758 0.763355 26.2434 0.299165 20.5505C-0.165026 14.8577 -0.471763 7.2173 2.42497 4.11929C4.43352 1.73288 7.86631 0.697393 9.74612 0.375959Z" fill="#FFE2F4"/>
<g filter="url(#filter0_d_2049_43228)">
<path d="M22.1043 26.6488H7.99325C7.54074 26.649 7.09261 26.5594 6.67449 26.3852C6.25636 26.211 5.87643 25.9555 5.55638 25.6334C5.23634 25.3113 4.98246 24.9288 4.80924 24.5079C4.63603 24.0869 4.54687 23.6358 4.54688 23.1801V6.90912C4.54687 6.45348 4.63603 6.00231 4.80924 5.58137C4.98246 5.16044 5.23634 4.77799 5.55638 4.45587C5.87643 4.13376 6.25636 3.87828 6.67449 3.70405C7.09261 3.52982 7.54074 3.44024 7.99325 3.44043H20.2205L25.5449 8.73726V23.1801C25.5446 24.0992 25.1821 24.9806 24.5371 25.6309C23.892 26.2812 23.0171 26.6473 22.1043 26.6488Z" fill="url(#paint0_linear_2049_43228)"/>
</g>
<path d="M21.0781 12.1216H9.74479C9.3766 12.1216 9.07812 12.4201 9.07812 12.7882V20.1216C9.07812 20.4898 9.3766 20.7882 9.74479 20.7882H21.0781C21.4463 20.7882 21.7448 20.4898 21.7448 20.1216V12.7882C21.7448 12.4201 21.4463 12.1216 21.0781 12.1216Z" stroke="white" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M21.7422 14.4546H15.7422V18.1213H21.7422V14.4546Z" stroke="white" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M21.7422 13.7881V18.7881" stroke="white" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M18.0781 12.1216C18.0781 10.4549 16.9115 10.1216 16.4115 10.1216C15.3004 10.1216 12.7781 10.1216 11.5781 10.1216C10.3781 10.1216 10.0781 11.0768 10.0781 11.5544V12.1216" stroke="white" stroke-width="1.33333" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M18.5781 16.7881C18.8543 16.7881 19.0781 16.5642 19.0781 16.2881C19.0781 16.0119 18.8543 15.7881 18.5781 15.7881C18.302 15.7881 18.0781 16.0119 18.0781 16.2881C18.0781 16.5642 18.302 16.7881 18.5781 16.7881Z" fill="white"/>
<path d="M20.0195 3.44043V7.76966C20.0195 8.08702 20.1465 8.39137 20.3726 8.61577C20.5987 8.84017 20.9053 8.96624 21.2251 8.96624H25.5453V8.85939L20.0195 3.44043Z" fill="#EED2FF"/>
<defs>
<filter id="filter0_d_2049_43228" x="3.54688" y="2.44043" width="23" height="25.2085" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
<feOffset/>
<feGaussianBlur stdDeviation="0.5"/>
<feColorMatrix type="matrix" values="0 0 0 0 0.0745098 0 0 0 0 0.74902 0 0 0 0 0.52549 0 0 0 0.3 0"/>
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_2049_43228"/>
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_2049_43228" result="shape"/>
</filter>
<linearGradient id="paint0_linear_2049_43228" x1="36.044" y1="15.0446" x2="12.9513" y2="-5.84872" gradientUnits="userSpaceOnUse">
<stop stop-color="#4C02B3"/>
<stop offset="1" stop-color="#E992FF"/>
</linearGradient>
</defs>
</svg>

3
src/components/locales/lang/en.js

@ -424,6 +424,7 @@ export default {
// Common List Fields // Common List Fields
common_list: { common_list: {
marketName: "Region",
id: "No.", id: "No.",
originalChinese: "Chinese", originalChinese: "Chinese",
english: "English", english: "English",
@ -440,6 +441,7 @@ export default {
hongKong: "Hong Kong", hongKong: "Hong Kong",
companyWallet: "Company Wallet", companyWallet: "Company Wallet",
companyWalletPlaceholder: "Select Company Wallet", companyWalletPlaceholder: "Select Company Wallet",
marketPlaceholder: "Select Region",
position: "Position", position: "Position",
roleName: "Role", roleName: "Role",
departmentPermission: "Dept Perm", departmentPermission: "Dept Perm",
@ -941,6 +943,7 @@ export default {
other: "Other", other: "Other",
currentBalance: "Current Balance", currentBalance: "Current Balance",
transactionType: "Type", transactionType: "Type",
transactionCurrency: "Currency",
transactionDesc: "Desc", transactionDesc: "Desc",
transactionOrderNo: "Order No.", transactionOrderNo: "Order No.",
transactionStatus: "Status", transactionStatus: "Status",

5
src/components/locales/lang/zh-CN.js

@ -424,6 +424,7 @@ export default {
// 通用列表字段组 // 通用列表字段组
common_list: { common_list: {
marketName: "所属地区",
id: "序号", id: "序号",
originalChinese: "原始中文", originalChinese: "原始中文",
english: "英文", english: "英文",
@ -440,6 +441,7 @@ export default {
hongKong: "香港", hongKong: "香港",
companyWallet: "公司钱包", companyWallet: "公司钱包",
companyWalletPlaceholder: "请选择公司钱包", companyWalletPlaceholder: "请选择公司钱包",
marketPlaceholder: "请选择所属地区",
position: "职位", position: "职位",
roleName: "角色名称", roleName: "角色名称",
departmentPermission: "部门权限", departmentPermission: "部门权限",
@ -948,9 +950,10 @@ export default {
other: "其他", other: "其他",
currentBalance: "当前余额", currentBalance: "当前余额",
transactionType: "交易类型", transactionType: "交易类型",
transactionCurrency: "交易币种",
transactionDesc: "交易说明", transactionDesc: "交易说明",
transactionOrderNo: "交易单号", transactionOrderNo: "交易单号",
transactionStatus: "交易状态",
transactionStatus: "状态",
exceptionData: "异常数据", exceptionData: "异常数据",
time: "时间", time: "时间",
market: { market: {

50
src/components/workspace/CashManagement.vue

@ -17,9 +17,9 @@
<el-icon <el-icon
class="service-icon" class="service-icon"
style=" style="
margin-left: 5px;
margin-left: .2604vw;
cursor: pointer; cursor: pointer;
font-size: 16px;
font-size: .8333vw;
transition: all 0.3s ease;"> transition: all 0.3s ease;">
<Warning/> <Warning/>
</el-icon> </el-icon>
@ -270,12 +270,12 @@ onMounted( async() => {
<style scoped> <style scoped>
/* 保留你原来的样式 */ /* 保留你原来的样式 */
.cash-management { .cash-management {
margin: 10px 5px;
margin: .5208vw .2604vw;
width: 100%; width: 100%;
height: 550px;
border-radius: 8px;
height: 28.6458vw;
border-radius: .4167vw;
background: #E7F4FD; background: #E7F4FD;
box-shadow: 0 2px 2px 0 #00000040;
box-shadow: 0 .1042vw .1042vw 0 #00000040;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
@ -285,9 +285,9 @@ onMounted( async() => {
width: 100%; width: 100%;
height: 5vh; height: 5vh;
flex-shrink: 0; flex-shrink: 0;
border-radius: 8px;
border-radius: .4167vw;
background: linear-gradient(90deg, #E4F0FC 0%, #C6ADFF 50%, #E4F0FC 100%); background: linear-gradient(90deg, #E4F0FC 0%, #C6ADFF 50%, #E4F0FC 100%);
box-shadow: 0 2px 2px 0 #00152940;
box-shadow: 0 .1042vw .1042vw 0 #00152940;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@ -296,25 +296,25 @@ onMounted( async() => {
.text1 { .text1 {
color: #040a2d; color: #040a2d;
font-family: "PingFang SC"; font-family: "PingFang SC";
font-size: 28px;
font-size: 1.4583vw;
font-weight: 900; font-weight: 900;
} }
.text1-update-time { .text1-update-time {
margin-left: 10px;
margin-left: .5208vw;
color: #040a2d; color: #040a2d;
font-size: 20px;
font-size: 1.0417vw;
font-weight: 700; font-weight: 700;
} }
.text2 { .text2 {
margin: 13px;
margin: .6771vw;
width: 95%; width: 95%;
height: 10vh; height: 10vh;
border-radius: 8px;
border-radius: .4167vw;
background: linear-gradient(90deg, #E4F0FC 0%, #C1DCF8 50%, #E4F0FC 100%); background: linear-gradient(90deg, #E4F0FC 0%, #C1DCF8 50%, #E4F0FC 100%);
box-shadow: 0 2px 2px 0 #00152940;
box-shadow: 0 .1042vw .1042vw 0 #00152940;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@ -322,7 +322,7 @@ onMounted( async() => {
.text2-income { .text2-income {
color: #040a2d; color: #040a2d;
font-size: 40px;
font-size: 2.0833vw;
font-weight: 900; font-weight: 900;
} }
@ -331,17 +331,17 @@ onMounted( async() => {
align-items: center; align-items: center;
width: 100%; width: 100%;
height: 100%; height: 100%;
padding: 10px;
padding: .5208vw;
} }
.market-data { .market-data {
display: flex; display: flex;
width: 265px;
width: 13.8021vw;
flex-direction: column; flex-direction: column;
align-items: flex-start; align-items: flex-start;
gap: 20px;
padding: 10px;
margin-left: 20px;
gap: 1.0417vw;
padding: .5208vw;
margin-left: 1.0417vw;
} }
.market-item { .market-item {
@ -349,18 +349,18 @@ onMounted( async() => {
justify-content: space-between; justify-content: space-between;
width: 100%; width: 100%;
font-family: "PingFang SC"; font-family: "PingFang SC";
font-size: 16px;
font-size: .8333vw;
color: #040a2d; color: #040a2d;
white-space: nowrap; /* 禁止换行 */ white-space: nowrap; /* 禁止换行 */
overflow: hidden; /* 隐藏溢出内容 */ overflow: hidden; /* 隐藏溢出内容 */
text-overflow: ellipsis; /* 溢出显示省略号 */ text-overflow: ellipsis; /* 溢出显示省略号 */
margin-bottom: 8px; /* 增加项间距,提升可读性 */
margin-bottom: .4167vw; /* 增加项间距,提升可读性 */
cursor: pointer; cursor: pointer;
} }
.market-name { .market-name {
flex: 0 0 auto; /* 名称部分自适应宽度 */ flex: 0 0 auto; /* 名称部分自适应宽度 */
margin-right: 16px; /* 与金额保持距离 */
margin-right: .8333vw; /* 与金额保持距离 */
} }
.market-value { .market-value {
@ -370,9 +370,9 @@ onMounted( async() => {
.chart { .chart {
flex: 1; flex: 1;
height: 300px;
height: 15.625vw;
width: auto; width: auto;
margin-top: 10px;
margin-top: .5208vw;
} }
</style> </style>

114
src/components/workspace/GoldManagement.vue

@ -20,13 +20,13 @@
}}</span>&nbsp;&nbsp;&nbsp;&nbsp;{{ t('workbench.compareToPreviousDay') }} }}</span>&nbsp;&nbsp;&nbsp;&nbsp;{{ t('workbench.compareToPreviousDay') }}
{{ dailyChange / 100 }}&nbsp; {{ dailyChange / 100 }}&nbsp;
<template v-if="dailyChange > 0"> <template v-if="dailyChange > 0">
<el-image :src="upArrow" style="width: 14px;"/>
<el-image :src="upArrow" style="width: .7292vw;"/>
</template> </template>
<template v-else-if="dailyChange < 0"> <template v-else-if="dailyChange < 0">
<el-image :src="downArrow" style="width: 14px;"/>
<el-image :src="downArrow" style="width: .7292vw;"/>
</template> </template>
<template v-else> <template v-else>
<el-image :src="pingArrow" style="width: 14px; padding-top: 12px"/>
<el-image :src="pingArrow" style="width: .7292vw; padding-top: .625vw"/>
</template> </template>
</div> </div>
<div> <div>
@ -50,8 +50,8 @@
</el-col> </el-col>
<!-- 右边图表 --> <!-- 右边图表 -->
<el-col :span="12"> <el-col :span="12">
<!-- <div ref="goldTypeChart" style="width: 100%; height: 100px;"></div>-->
<div style="width: 100%; height: 60px;">&nbsp;</div>
<!-- <div ref="goldTypeChart" style="width: 100%; height: 5.2083vw;"></div>-->
<div style="width: 100%; height: 3.125vw;">&nbsp;</div>
<div class="margin-bottom"> <div class="margin-bottom">
[{{ t('workbench.goldExpireIn12Months')}}{{ currentFreeDecember / 100 }}] [{{ t('workbench.goldExpireIn12Months')}}{{ currentFreeDecember / 100 }}]
</div> </div>
@ -68,12 +68,12 @@
<el-row> <el-row>
<el-col :span="12"> <el-col :span="12">
<div class="center-card">{{ t('workbench.convertedSGDCumulativeAmount') }}</div> <div class="center-card">{{ t('workbench.convertedSGDCumulativeAmount') }}</div>
<el-image :src="svg1" style="width: 88px; display: block;margin: 0 auto;"/>
<el-image :src="svg1" style="width: 4.5833vw; display: block;margin: 0 auto;"/>
<div class="center-card">{{ yearlyMoney / 100 }}{{ t('workbench.SGD') }}</div> <div class="center-card">{{ yearlyMoney / 100 }}{{ t('workbench.SGD') }}</div>
</el-col> </el-col>
<el-col :span="12" style="border-left: 2px solid #CFE6FE; height: 160px">
<el-col :span="12" style="border-left: .1042vw solid #CFE6FE; height: 8.3333vw">
<div class="center-card" style="white-space: nowrap;">{{ t('workbench.yesterdayNew')}}{{ recharge / 100 }}</div> <div class="center-card" style="white-space: nowrap;">{{ t('workbench.yesterdayNew')}}{{ recharge / 100 }}</div>
<div ref="rechargeGoldChart" style="width: 88px; height: 88px; display: block;margin: 0 auto;"></div>
<div ref="rechargeGoldChart" style="width: 4.5833vw; height: 4.5833vw; display: block;margin: 0 auto;"></div>
<div class="center-card" style="white-space: nowrap;">{{ t('workbench.wherePermanentGold')}}{{ money / 100 }}</div> <div class="center-card" style="white-space: nowrap;">{{ t('workbench.wherePermanentGold')}}{{ money / 100 }}</div>
</el-col> </el-col>
</el-row> </el-row>
@ -87,7 +87,7 @@
<!-- 第三个卡片 --> <!-- 第三个卡片 -->
<div class="card-item"> <div class="card-item">
<div class="card-title">{{ t('workbench.annualCumulativeConsume')}}{{ yearlyReduce / 100 }}</div> <div class="card-title">{{ t('workbench.annualCumulativeConsume')}}{{ yearlyReduce / 100 }}</div>
<el-row style="height: 200px;">
<el-row style="height: 10.4167vw;">
<el-col :span="12"> <el-col :span="12">
<div ref="consumeChart" style="width:100%; height: 88%;"></div> <div ref="consumeChart" style="width:100%; height: 88%;"></div>
</el-col> </el-col>
@ -101,21 +101,21 @@
<!-- 第四个卡片 --> <!-- 第四个卡片 -->
<div class="card-item" > <div class="card-item" >
<div class="card-title">{{ t('workbench.annualCumulativeRechargePeople')}}{{ yearlyRechargeNum }}</div> <div class="card-title">{{ t('workbench.annualCumulativeRechargePeople')}}{{ yearlyRechargeNum }}</div>
<el-row style="height: 200px;">
<el-col :span="12" style="border-right: 2px solid #CFE6FE; height: 200px">
<el-row style="height: 10.4167vw;">
<el-col :span="12" style="border-right: .1042vw solid #CFE6FE; height: 10.4167vw">
<div class="chart5"> <div class="chart5">
<el-image :src="svg2" style="width: 88px; display: block;margin: 0 auto;"/>
<el-image :src="svg2" style="width: 4.5833vw; display: block;margin: 0 auto;"/>
<div class="margin-bottom"> <div class="margin-bottom">
<div style="display: flex; gap: 10px; font-size: 16px;">{{ t('workbench.weekYearOnYear')}}{{ sumWow }}%
<el-image v-if="sumWow > 0" :src="upArrow" style="width: 10px;"/>
<el-image v-else-if="sumWow < 0" :src="downArrow" style="width: 10px;"/>
<el-image v-else :src="pingArrow" style="width: 10px;"/>
<div style="display: flex; gap: .5208vw; font-size: .8333vw;">{{ t('workbench.weekYearOnYear')}}{{ sumWow }}%
<el-image v-if="sumWow > 0" :src="upArrow" style="width: .5208vw;"/>
<el-image v-else-if="sumWow < 0" :src="downArrow" style="width: .5208vw;"/>
<el-image v-else :src="pingArrow" style="width: .5208vw;"/>
</div> </div>
<div style="display: flex; gap: 10px; font-size: 16px;">
<div style="display: flex; gap: .5208vw; font-size: .8333vw;">
{{ t('workbench.dayYearOnYear')}}{{ sumDaily }}% {{ t('workbench.dayYearOnYear')}}{{ sumDaily }}%
<el-image v-if="sumDaily > 0" :src="upArrow" style="width: 10px;"/>
<el-image v-else-if="sumDaily < 0" :src="downArrow" style="width: 10px;"/>
<el-image v-else :src="pingArrow" style="width: 10px; "/>
<el-image v-if="sumDaily > 0" :src="upArrow" style="width: .5208vw;"/>
<el-image v-else-if="sumDaily < 0" :src="downArrow" style="width: .5208vw;"/>
<el-image v-else :src="pingArrow" style="width: .5208vw; "/>
</div> </div>
</div> </div>
@ -570,46 +570,46 @@ onMounted(async () => {
} }
.card-item-row1 { .card-item-row1 {
height: 200px;
height: 10.4167vw;
width: auto; width: auto;
background: #E4F0FC; background: #E4F0FC;
box-shadow: 0 0 4px 0 #00000040;
border-radius: 10px;
margin-top: 20px;
margin-left: 5px;
margin-right: 5px;
margin-bottom: -5px;
padding-bottom: 10px;
box-shadow: 0 0 .2083vw 0 #00000040;
border-radius: .5208vw;
margin-top: 1.0417vw;
margin-left: .2604vw;
margin-right: .2604vw;
margin-bottom: -0.2604vw;
padding-bottom: .5208vw;
} }
.card-item { .card-item {
height: 240px;
height: 12.5vw;
width: auto; width: auto;
background: #E4F0FC; background: #E4F0FC;
box-shadow: 0 0 4px 0 #00000040;
border-radius: 10px;
margin-top: 20px;
margin-left: 5px;
margin-right: 5px;
margin-bottom: -5px;
padding-bottom: 10px;
box-shadow: 0 0 .2083vw 0 #00000040;
border-radius: .5208vw;
margin-top: 1.0417vw;
margin-left: .2604vw;
margin-right: .2604vw;
margin-bottom: -0.2604vw;
padding-bottom: .5208vw;
} }
.card-title { .card-title {
font-weight: bold; font-weight: bold;
height: 36px;
font-size: 17px;
height: 1.875vw;
font-size: .8854vw;
width: 100%; width: 100%;
flex-shrink: 0; flex-shrink: 0;
border-radius: 8px;
border-radius: .4167vw;
background: linear-gradient(90deg, #E4F0FC 0%, #C1DCF8 50%, #E4F0FC 100%); background: linear-gradient(90deg, #E4F0FC 0%, #C1DCF8 50%, #E4F0FC 100%);
box-shadow: 0 0 2px 0 #00152940;
box-shadow: 0 0 .1042vw 0 #00152940;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
margin-top: -5px;
margin-bottom: 10px;
margin-top: -0.2604vw;
margin-bottom: .5208vw;
} }
.card-item .el-col { .card-item .el-col {
@ -631,9 +631,9 @@ onMounted(async () => {
width: 100%; width: 100%;
height: 5vh; height: 5vh;
flex-shrink: 0; flex-shrink: 0;
border-radius: 8px;
border-radius: .4167vw;
background: linear-gradient(90deg, #E4F0FC 0%, #FFF178 50%, #E4F0FC 100%); background: linear-gradient(90deg, #E4F0FC 0%, #FFF178 50%, #E4F0FC 100%);
box-shadow: 0 2px 2px 0 #00152940;
box-shadow: 0 .1042vw .1042vw 0 #00152940;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@ -642,47 +642,47 @@ onMounted(async () => {
.text1 { .text1 {
color: #040a2d; color: #040a2d;
font-family: " PingFang SC "; font-family: " PingFang SC ";
font-size: 28px;
font-size: 1.4583vw;
font-style: normal; font-style: normal;
font-weight: 900; font-weight: 900;
line-height: 31.79px;
line-height: 1.6557vw;
} }
.text1-update-time { .text1-update-time {
width: 100%; width: 100%;
height: 26px;
height: 1.3542vw;
flex-shrink: 0; flex-shrink: 0;
color: #040a2d; color: #040a2d;
font-family: "PingFang SC"; font-family: "PingFang SC";
font-size: 20px;
font-size: 1.0417vw;
font-style: normal; font-style: normal;
font-weight: 700; font-weight: 700;
line-height: 31.79px;
line-height: 1.6557vw;
} }
/* 背景卡片大小 */ /* 背景卡片大小 */
.gold-management { .gold-management {
margin: 10px 5px;
margin: .5208vw .2604vw;
width: 100%; width: 100%;
height: 550px;
height: 28.6458vw;
flex-shrink: 0; flex-shrink: 0;
border-radius: 8px;
border-radius: .4167vw;
background: #E7F4FD; background: #E7F4FD;
box-shadow: 0 2px 2px 0 #00000040;
box-shadow: 0 .1042vw .1042vw 0 #00000040;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
} }
.margin-bottom { .margin-bottom {
padding-left: 20px;
padding-left: 1.0417vw;
} }
.chart5 { .chart5 {
margin-top: 15px;
margin-top: .7813vw;
.margin-bottom { .margin-bottom {
margin-top: 10px;
padding-left: 20px;
margin-top: .5208vw;
padding-left: 1.0417vw;
} }
} }
</style> </style>

90
src/router/index.js

@ -413,6 +413,92 @@ const routes = [
} }
] ]
}, },
// 钱包管理
{
path: 'walletManage',
name: 'walletManage',
meta: {permissionId: 150},
children: [
// 钱包余额
{
path: 'walletBalance',
name: "walletBalance",
component: () => import("../views/walletManage/WalletBalance.vue"),
meta: {permissionId: 151}
},
// 钱包明细
{
path: 'walletDetail',
name: "walletDetail",
meta: {permissionId: 152},
children: [
// 香港钱包
{
path: 'hkWallet',
name: "hkWallet",
component: () => import("../views/walletManage/walletDetail/HKWallet.vue"),
meta: {permissionId: 153}
},
//新加坡HC钱包
{
path: 'sgWalletHC',
name: "sgWalletHC",
component: () => import("../views/walletManage/walletDetail/SGWalletHC.vue"),
meta: {permissionId: 154}
},
//马来钱包
{
path: 'myWallet',
name: "myWallet",
component: () => import("../views/walletManage/walletDetail/MyWallet.vue"),
meta: {permissionId: 155}
},
// 新加坡CM钱包
{
path: 'sgWalletCM',
name: "sgWalletCM",
component: () => import("../views/walletManage/walletDetail/SGWalletCM.vue"),
meta: {permissionId: 156}
},
// 加拿大钱包
{
path: 'caWallet',
name: "caWallet",
component: () => import("../views/walletManage/walletDetail/CAWallet.vue"),
meta: {permissionId: 157}
},
//泰国HS钱包
{
path: 'thWalletHS',
name: "thWalletHS",
component: () => import("../views/walletManage/walletDetail/THWalletHS.vue"),
meta: {permissionId: 158}
},
//泰国HA钱包
{
path: 'thWalletHA',
name: "thWalletHA",
component: () => import("../views/walletManage/walletDetail/THWalletHA.vue"),
meta: {permissionId: 159}
},
//越南钱包
{
path: 'vnWallet',
name: "vnWallet",
component: () => import("../views/walletManage/walletDetail/VNWallet.vue"),
meta: {permissionId: 160}
},
//北京钱包
{
path: 'bjWallet',
name: "bjWallet",
component: () => import("../views/walletManage/walletDetail/BJWallet.vue"),
meta: {permissionId: 161}
},
]
},
]
},
// 频道管理 // 频道管理
{ {
path: 'channelManage', path: 'channelManage',
@ -542,8 +628,8 @@ router.beforeEach(async (to, from, next) => {
const requiresPermission = to.meta && to.meta.permissionId; const requiresPermission = to.meta && to.meta.permissionId;
if (requiresPermission) { if (requiresPermission) {
const hasPermission = Array.isArray(requiresPermission) const hasPermission = Array.isArray(requiresPermission)
? requiresPermission.some(id => userPermissionIds.includes(id))
: userPermissionIds.includes(requiresPermission);
? requiresPermission.some(id => userPermissionIds.some(uid => String(uid) === String(id)))
: userPermissionIds.some(uid => String(uid) === String(requiresPermission));
if (!hasPermission) { if (!hasPermission) {
next('/noPermission'); next('/noPermission');

0
src/static/common.js

14
src/utils/menuUtils.js

@ -86,7 +86,19 @@ export const getRoutePath = (menu) => {
"149": "/moneyManage/financialAccount/performanceAttribution", // 业绩归属账(财务核算子菜单) "149": "/moneyManage/financialAccount/performanceAttribution", // 业绩归属账(财务核算子菜单)
"125": "/channelManage/reward", // 打赏管理(频道管理子菜单) "125": "/channelManage/reward", // 打赏管理(频道管理子菜单)
"126": "/channelManage/fans", // 铁粉管理(频道管理子菜单) "126": "/channelManage/fans", // 铁粉管理(频道管理子菜单)
"127": "/channelManage/cart" // 小黄车管理(频道管理子菜单)
"127": "/channelManage/cart", // 小黄车管理(频道管理子菜单)
"150": "/walletManage", // 钱包管理
"151": "/walletManage/walletBalance", // 钱包余额
"152": "/walletManage/walletDetail", // 钱包明细
"153": "/walletManage/walletDetail/hkWallet", // 香港钱包
"154": "/walletManage/walletDetail/sgWalletHC", // 新加坡HC钱包
"156": "/walletManage/walletDetail/sgWalletCM", // 新加坡CM钱包
"155": "/walletManage/walletDetail/myWallet", // 马来西亚钱包
"157": "/walletManage/walletDetail/caWallet", // 加拿大钱包
"158": "/walletManage/walletDetail/thWalletHS", // 泰国HS钱包
"159": "/walletManage/walletDetail/thWalletHA", // 泰国HA钱包
"160": "/walletManage/walletDetail/vnWallet", // 越南钱包
"161": "/walletManage/walletDetail/bjWallet" // 北京钱包
}; };
// 从映射表中匹配路由,无匹配时返回无权限页面路径 // 从映射表中匹配路由,无匹配时返回无权限页面路径

1
src/views/home.vue

@ -33,6 +33,7 @@ const menuNameMap = {
'124': 'channel-management', // '124': 'channel-management', //
'128': 'permission-management', // '128': 'permission-management', //
'146': 'mutiple-language', // '146': 'mutiple-language', //
'150': 'wallet_management', //
} }
const getIconPath = (menuNameId) => { const getIconPath = (menuNameId) => {

1
src/views/moneyManage/executor/executor.vue

@ -515,6 +515,7 @@ const channelOptions = ref([
t('cash.payMethods.transfer'), t('cash.payMethods.transfer'),
t('cash.payMethods.iotPay'), t('cash.payMethods.iotPay'),
t('cash.payMethods.stripe3'), t('cash.payMethods.stripe3'),
t('cash.payMethods.stripe2'),
t('cash.payMethods.paypal'), t('cash.payMethods.paypal'),
]) ])

228
src/views/moneyManage/financialAccount/performanceAttribution.vue

@ -1,10 +1,10 @@
<script setup> <script setup>
import { ref, reactive, onMounted, toRefs, nextTick } from 'vue'
import { ref, reactive, onMounted, toRefs, nextTick, computed } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import request from '@/util/http.js' import request from '@/util/http.js'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { refundOnline,performanceSelect,exportPerformance } from '@/api/cash/financialAccount.js'
import { refundOnline,performanceSelect,exportPerformance,adjustment } from '@/api/cash/financialAccount.js'
import { getUserInfo } from '@/api/common/common.js' import { getUserInfo } from '@/api/common/common.js'
@ -157,6 +157,7 @@ const handleCurrentChange = (val) => {
fetchData() fetchData()
} }
// 退 // 退
const handleRefund = (row) => { const handleRefund = (row) => {
ElMessageBox.confirm(t('elmessage.refundConfirmContent', { orderNo: row.systemTradeNo }), t('elmessage.refundConfirmTitle'), { ElMessageBox.confirm(t('elmessage.refundConfirmContent', { orderNo: row.systemTradeNo }), t('elmessage.refundConfirmTitle'), {
@ -264,6 +265,110 @@ const getTagText = (state) => {
} }
} }
// ==================== ====================
const adjustVisible = ref(false)
const adjustTime = ref('')
const adjustCoefficient = ref('')
const matrixMarkets = [
{ key: 'sg', label: '新加坡' },
{ key: 'my', label: '马来西亚' },
{ key: 'hk', label: '香港' },
{ key: 'th', label: '泰国' },
{ key: 'vn', label: '越南' },
{ key: 'ca', label: '加拿大' }
]
const adjustData = ref([])
const initAdjustData = () => {
adjustData.value = matrixMarkets.map(rowMarket => {
const row = { inMarket: rowMarket.label + '客户' }
matrixMarkets.forEach(colMarket => {
row[colMarket.key] = '' //
})
return row
})
}
const handleAdjustment = () => {
adjustTime.value = dayjs().format('YYYY-MM-DD HH:mm:ss')
adjustCoefficient.value = ''
initAdjustData()
adjustVisible.value = true
}
const computedAdjustData = computed(() => {
const data = [...adjustData.value]
const sumRow = { inMarket: '合计', isSum: true }
matrixMarkets.forEach(colMarket => {
let colSum = 0
adjustData.value.forEach(row => {
const val = parseFloat(row[colMarket.key])
if (!isNaN(val)) colSum += val
})
sumRow[colMarket.key] = colSum
})
data.push(sumRow)
return data
})
const getRowTotal = (row) => {
let sum = 0
matrixMarkets.forEach(colMarket => {
const val = parseFloat(row[colMarket.key])
if (!isNaN(val)) sum += val
})
return sum
}
const formatNumber = (val) => {
if (val === '' || val === '-' || val === undefined || val === null) return val;
val = String(val);
let formatted = val.replace(/[^\d.-]/g, ''); //
formatted = formatted.replace(/(?!^)-/g, ''); //
formatted = formatted.replace(/(\..*?)\..*/g, '$1'); //
return formatted;
}
const submitAdjustment = async () => {
if (!adjustTime.value) {
ElMessage.warning('请选择时间')
return
}
if (!adjustCoefficient.value) {
ElMessage.warning('请输入系数')
return
}
// matrix (66)
// 0
const matrix = adjustData.value.map(row => {
return matrixMarkets.map(colMarket => {
const val = parseFloat(row[colMarket.key])
return isNaN(val) ? 0 : val
})
})
//
const payload = {
matrix: matrix,
weight: parseFloat(adjustCoefficient.value), //
createTime: adjustTime.value,
submitterId: adminData.value.id || 1000063, // adminData
submitterMarket: adminData.value.marketName || '总部' //
}
console.log('提交的封装数据:', JSON.stringify(payload, null, 2))
await adjustment(payload)
ElMessage.success('提交成功')
adjustVisible.value = false
fetchData()
}
onMounted( async() => { onMounted( async() => {
await handleAdminInfo() await handleAdminInfo()
getMarket() getMarket()
@ -309,11 +414,14 @@ onMounted( async() => {
style="width: 18vw;" style="width: 18vw;"
/> />
</div> </div>
</div>
<div class="search-row">
<el-button type="primary" @click="handleSearch">{{ t('common.search') }}</el-button> <el-button type="primary" @click="handleSearch">{{ t('common.search') }}</el-button>
<el-button type="primary" @click="handleExport">{{ t('common.exportExcel') }}</el-button> <el-button type="primary" @click="handleExport">{{ t('common.exportExcel') }}</el-button>
<el-button type="primary" @click="openExportList">{{ t('common.viewExportList') }}</el-button> <el-button type="primary" @click="openExportList">{{ t('common.viewExportList') }}</el-button>
<el-button type="success" @click="handleReset">{{ t('common.reset') }}</el-button> <el-button type="success" @click="handleReset">{{ t('common.reset') }}</el-button>
<button class="adjust-btn" @click="handleAdjustment">业绩调整</button>
</div> </div>
</div> </div>
</el-card> </el-card>
@ -394,6 +502,62 @@ onMounted( async() => {
</div> </div>
</template> </template>
</el-dialog> </el-dialog>
<!-- 业绩调整弹窗 -->
<el-dialog v-model="adjustVisible" title="市场消耗金币业绩调整" width="80%" top="5vh" align-center class="custom-adjust-dialog">
<template #header="{ titleId, titleClass }">
<div style="text-align: center; font-weight: bold; font-size: 18px;" :id="titleId" :class="titleClass">市场消耗金币业绩调整</div>
</template>
<div style="display: flex; gap: 40px; margin-bottom: 20px; align-items: center;">
<div style="display: flex; align-items: center;">
<span style="margin-right: 10px; font-weight: bold;">选择时间</span>
<el-date-picker
v-model="adjustTime"
type="datetime"
placeholder="选择时间"
value-format="YYYY-MM-DD HH:mm:ss"
/>
</div>
<div style="display: flex; align-items: center;">
<span style="margin-right: 10px; font-weight: bold;">系数调整</span>
<el-input v-model="adjustCoefficient" placeholder="请输入系数调整" style="width: 200px;" @input="adjustCoefficient = formatNumber(adjustCoefficient)" />
</div>
</div>
<el-table :data="computedAdjustData" border style="width: 100%" :cell-style="{ textAlign: 'center' }" :header-cell-style="{ background: '#F3FAFE', color: '#333', textAlign: 'center', padding: '0' }">
<el-table-column width="150" align="center" fixed="left">
<template #header>
<div class="diagonal-header">
<span class="top-right">调入</span>
<span class="bottom-left">调出</span>
</div>
</template>
<template #default="{ row }">
<span style="font-weight: bold;">{{ row.inMarket }}</span>
</template>
</el-table-column>
<el-table-column v-for="col in matrixMarkets" :key="col.key" :label="col.label + '市场团队'" min-width="120" align="center">
<template #default="{ row }">
<span v-if="row.isSum">{{ row[col.key] }}</span>
<el-input v-else v-model="row[col.key]" @input="row[col.key] = formatNumber($event)" placeholder="" class="seamless-input" />
</template>
</el-table-column>
<el-table-column label="合计" min-width="120" align="center" fixed="right">
<template #default="{ row }">
{{ getRowTotal(row) }}
</template>
</el-table-column>
</el-table>
<template #footer>
<div class="dialog-footer" style="text-align: center;">
<el-button type="primary" plain @click="adjustVisible = false" style="width: 100px;">取消</el-button>
<el-button type="primary" @click="submitAdjustment" style="width: 100px;">提交</el-button>
</div>
</template>
</el-dialog>
</div> </div>
</template> </template>
@ -451,6 +615,16 @@ onMounted( async() => {
gap: 10px; gap: 10px;
} }
.adjust-btn {
color: #fff;
padding: 8px 15px;
border-radius: 4px;
cursor: pointer;
border: none;
background-color: #7349ad;
margin-left: auto;
}
.table-card { .table-card {
background: #E7F4FD; background: #E7F4FD;
flex: 1; flex: 1;
@ -484,4 +658,54 @@ onMounted( async() => {
:deep(.el-table__row:hover > .el-table__cell) { :deep(.el-table__row:hover > .el-table__cell) {
background-color: #E5EBFE !important; background-color: #E5EBFE !important;
} }
.diagonal-header {
position: relative;
width: 100%;
height: 50px; /* Set a fixed height to make diagonal line work well */
background: linear-gradient(to top right, transparent 49.5%, #dcdfe6 49.5%, #dcdfe6 50.5%, transparent 50.5%);
}
.diagonal-header .top-right {
position: absolute;
top: 5px;
right: 15px;
font-weight: bold;
}
.diagonal-header .bottom-left {
position: absolute;
bottom: 5px;
left: 15px;
font-weight: bold;
}
/* 业绩调整弹窗全局样式 */
:deep(.custom-adjust-dialog) {
background-color: #f3fafe !important; /* 统一淡蓝色背景 */
border-radius: 8px;
}
:deep(.custom-adjust-dialog .el-dialog__header) {
padding-bottom: 20px;
border-bottom: 1px solid #EBEEF5;
margin-right: 0;
}
:deep(.custom-adjust-dialog .el-dialog__body) {
padding-top: 20px;
}
/* 无缝输入框样式(去除边框和背景) */
.seamless-input :deep(.el-input__wrapper) {
box-shadow: none !important;
background-color: transparent !important;
padding: 0;
}
.seamless-input :deep(.el-input__inner) {
text-align: center;
font-size: 14px;
height: 100%;
}
.seamless-input :deep(.el-input__inner:focus) {
outline: none;
}
</style> </style>

1
src/views/moneyManage/receiveDetail/receiveService.vue

@ -990,6 +990,7 @@ const handleAddForm = async () => {
receivedMarket: MarketNameForId(addFormData.value.receivedMarket) || '', receivedMarket: MarketNameForId(addFormData.value.receivedMarket) || '',
paymentAmount: (addFormData.value.paymentAmount) * 100, paymentAmount: (addFormData.value.paymentAmount) * 100,
submitterMarket: normalizeSubmitterMarket(adminData.value.markets), submitterMarket: normalizeSubmitterMarket(adminData.value.markets),
isPerformance: 1,
...(isGold.value ? { ...(isGold.value ? {
walletId: WalletForId(addFormData.value.receivedWallet) walletId: WalletForId(addFormData.value.receivedWallet)
} : {}) } : {})

15
src/views/usergold/gold/clientCount.vue

@ -21,14 +21,6 @@
> >
{{ $t('clientCount.clientCountBalance') }} {{ $t('clientCount.clientCountBalance') }}
</el-button> </el-button>
<el-button
class="no-active-btn"
:class="{ 'active-btn': activeTab === 'clientCountWallet' }"
@click="navigateTo('clientCountWallet')"
:disabled="!hasWallet" v-if="hasWallet"
>
{{ $t('clientCount.clientCountWallet') }}
</el-button>
</el-button-group> </el-button-group>
<!-- 渲染子路由组件 --> <!-- 渲染子路由组件 -->
</div> </div>
@ -56,7 +48,6 @@ const {menuTree} = storeToRefs(adminStore);
const activeTab = ref(''); const activeTab = ref('');
const hasDetail = ref(false); const hasDetail = ref(false);
const hasBalance = ref(false); const hasBalance = ref(false);
const hasWallet = ref(false);
// //
const navigateTo = (name) => { const navigateTo = (name) => {
activeTab.value = name; activeTab.value = name;
@ -69,7 +60,6 @@ const initPermissions = () => {
if (!menuTree.value || !menuTree.value.length) return; if (!menuTree.value || !menuTree.value.length) return;
hasDetail.value = hasMenuPermission(menuTree.value, permissionMapping.gold_coin_customer_bill); hasDetail.value = hasMenuPermission(menuTree.value, permissionMapping.gold_coin_customer_bill);
hasBalance.value = hasMenuPermission(menuTree.value, permissionMapping.gold_coin_customer_balance); hasBalance.value = hasMenuPermission(menuTree.value, permissionMapping.gold_coin_customer_balance);
hasWallet.value = hasMenuPermission(menuTree.value, permissionMapping.wallet);
}; };
// //
@ -77,14 +67,13 @@ const getDefaultAuditRoute = () => {
initPermissions(); initPermissions();
if (hasDetail.value) return 'clientCountDetail'; if (hasDetail.value) return 'clientCountDetail';
if (hasBalance.value) return 'clientCountBalance'; if (hasBalance.value) return 'clientCountBalance';
if (hasWallet.value) return 'clientCountWallet';
return 'clientCountDetail'; return 'clientCountDetail';
}; };
// //
watch(() => route.name, (newName) => { watch(() => route.name, (newName) => {
initPermissions() initPermissions()
if (newName === 'clientCountDetail' || newName === 'clientCountBalance' || newName === 'clientCountWallet') {
if (newName === 'clientCountDetail' || newName === 'clientCountBalance') {
activeTab.value = newName; activeTab.value = newName;
} else if (newName === 'usergold') { } else if (newName === 'usergold') {
// 访 /coinConsume // 访 /coinConsume
@ -101,7 +90,7 @@ onMounted(() => {
navigateTo(defaultRoute); navigateTo(defaultRoute);
} else { } else {
// //
if (route.name === 'clientCountDetail' || route.name === 'clientCountBalance' || route.name === 'clientCountWallet') {
if (route.name === 'clientCountDetail' || route.name === 'clientCountBalance') {
activeTab.value = route.name; activeTab.value = route.name;
} }
} }

913
src/views/walletManage/WalletBalance.vue

@ -0,0 +1,913 @@
<script setup>
//
import { useAdminStore } from "@/store/index.js"
import { storeToRefs } from "pinia"
import { findMenuById, permissionMapping } from "@/utils/menuTreePermission.js"
const adminStore = useAdminStore()
const { adminData, menuTree, flag } = storeToRefs(adminStore)
// flag()
watch(flag, (newFlag, oldFlag) => {
// flag
if (newFlag !== oldFlag) {
console.log('员工数据flag状态改变,重新加载数据', newFlag)
get()
}
})
import { onMounted, ref, watch, nextTick } from 'vue'
import { ElMessage } from 'element-plus'
import moment from 'moment'
import API from '@/util/http.js'
import { reverseMarketMapping } from "@/utils/marketMap.js";
//
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
//
const dialogVisible = ref(false)
//
const markets = ref([])
//
// tableData
const tableData = ref([]);
const walletMap = {
1: 'historyGold',
2: 'hkGold',
3: 'sgHcGold',
4: 'myGold',
5: 'sgGold',
6: 'caGold',
7: 'thHsGold',
8: 'thHaGold',
9: 'vnGold',
10: 'bjGold'
};
const propToWalletId = {
historyGold: 1,
hkGold: 2,
sgHcGold: 3,
myGold: 4,
sgGold: 5,
caGold: 6,
thHsGold: 7,
thHaGold: 8,
vnGold: 9,
bjGold: 10
};
const tableRef = ref(null)
const scrollTableTop = () => {
tableRef.value?.setScrollTop?.(0)
}
//
const userInfo = ref({})
// ===========================================
//
const total = ref(100)
//
const getTime = ref([])
// User
const selectData = ref({
jwcode: '',
markets: [],
})
//
const getAllObj = ref({})
//
const getObj = ref({
pageNum: 1,
pageSize: 50
})
//
const sortField = ref('')
const sortOrder = ref('')
const sortWalletId = ref('')
//
// ===========================================================================
//
const get = async function (val) {
try {
//
if (typeof val === 'number') {
getObj.value.pageNum = val
}
if (selectData.value.jwcode) {
//
const numberRegex = /^\d{1,9}$/;
//
if (!numberRegex.test(selectData.value.jwcode)) {
ElMessage.error(t('elmessage.checkJwcodeFormat'))
return
}
}
console.log('搜索参数', getObj.value, selectData.value)
//
const params = {
pageNum: getObj.value.pageNum,
pageSize: getObj.value.pageSize
}
//
if (selectData.value.jwcode) {
params.jwcode = Number(selectData.value.jwcode)
}
if (selectData.value.markets && selectData.value.markets.length > 0) {
//
params.market = String(selectData.value.markets[0])
}
if (sortField.value) {
params.sortField = sortField.value
}
if (sortOrder.value) {
params.sortOrder = sortOrder.value
}
if (sortWalletId.value !== '') {
params.sortWalletId = sortWalletId.value
}
console.log('最终请求参数', params)
const result = await API({
url: '/cashCollection/selectUserWallets',
method: 'post',
data: params
})
console.log('响应数据', result)
if (result.code === 200) {
tableData.value = result.data.list.map(item => {
const row = {
historyGold: 0,
name: item.userName,
jwcode: item.jwcode,
market: item.marketName,
hkGold: 0,
sgHcGold: 0,
myGold: 0,
sgGold: 0,
caGold: 0,
thHsGold: 0,
thHaGold: 0,
vnGold: 0,
bjGold: 0
};
if (item.walletList && Array.isArray(item.walletList)) {
item.walletList.forEach(wallet => {
const prop = walletMap[wallet.walletId];
if (prop) {
row[prop] = wallet.currentPermanentGold;
}
});
}
return row;
});
total.value = result.data.total;
} else {
ElMessage.error(result.msg || t('elmessage.getDataFailed'));
}
} catch (error) {
console.log('请求失败', error)
}
}
// selectData putExcel jwcode
const trimJwCode = () => {
if (selectData.value.jwcode) {
selectData.value.jwcode = selectData.value.jwcode.replace(/\s/g, '');
}
}
//
const search = function () {
trimJwCode();
getObj.value.pageNum = 1
get()
}
//
const reset = function () {
selectData.value = {
jwcode: '',
markets: [],
}
sortField.value = ''
sortOrder.value = ''
sortWalletId.value = ''
selectedMarketPath.value = []
//
getObj.value.pageNum = 1
get()
}
const cellClick = function (row, column) {
console.log('cellClick', column)
const walletId = propToWalletId[column.property];
const propToMarketName = {
historyGold: t('clientCount.market.historyGold'),
hkGold: t('clientCount.market.hkGold'),
sgHcGold: t('clientCount.market.sgHcGold'),
myGold: t('clientCount.market.myGold'),
sgGold: t('clientCount.market.sgCmGold'),
caGold: t('clientCount.market.caGold'),
thHsGold: t('clientCount.market.thHsGold'),
thHaGold: t('clientCount.market.thHaGold'),
vnGold: t('clientCount.market.vnGold'),
bjGold: t('clientCount.market.bjGold')
}
const marketName = propToMarketName[column.property]
if (marketName && walletId) {
currentWalletInfo.value = {
userName: row.name,
jwcode: row.jwcode,
marketName: row.market, //
walletName: marketName + t('clientCount.wallet'), //
currentBalance: row[column.property] || 0,
walletId: walletId
}
walletDetailQuery.value.pageNum = 1
getWalletDetail()
walletDetailVisible.value = true
}
}
//
const walletDetailVisible = ref(false)
const walletDetailList = ref([])
const walletDetailTotal = ref(0)
const walletDetailLoading = ref(false)
const currentWalletInfo = ref({})
const walletDetailQuery = ref({
pageNum: 1,
pageSize: 20
})
const getWalletRecordTypeText = (item) => {
const type = Number(item.type)
if (type === 0) {
return t('clientCount.recharge')
}
if (type === 1) {
return t('clientCount.consume')
}
if (type === 2) {
return t('clientCount.refund')
}
return item.typeText || t('clientCount.other')
}
const getWalletDetail = async () => {
walletDetailLoading.value = true
try {
const params = {
pageNum: walletDetailQuery.value.pageNum,
pageSize: walletDetailQuery.value.pageSize,
userWalletRecord: {
jwcode: Number(currentWalletInfo.value.jwcode),
walletId: currentWalletInfo.value.walletId
}
}
const result = await API({
url: '/cashCollection/selectWalletRecords',
method: 'post',
data: params
})
if (result.code === 200) {
walletDetailList.value = result.data.list.map(item => ({
time: moment(item.createTime).format('YYYY-MM-DD HH:mm:ss'),
type: getWalletRecordTypeText(item),
amount: Number(item.amount) || 0,
desc: item.description || '',
orderNo: item.orderCode,
status: item.status === 0 ? 1 : 2
}))
walletDetailTotal.value = result.data.total
} else {
ElMessage.error(result.msg || t('elmessage.getDetailFailed'))
}
} catch (error) {
console.error(error)
} finally {
walletDetailLoading.value = false
}
}
const handleWalletDetailSizeChange = (val) => {
walletDetailQuery.value.pageSize = val
walletDetailQuery.value.pageNum = 1
getWalletDetail()
}
const handleWalletDetailCurrentChange = (val) => {
walletDetailQuery.value.pageNum = val
getWalletDetail()
}
//
const handleSortChange = (column) => {
console.log('排序字段:', column.prop)
console.log('排序方式:', column.order)
if (!column.prop || !column.order) {
sortField.value = ''
sortOrder.value = ''
sortWalletId.value = ''
getObj.value.pageNum = 1
get()
return
}
if (propToWalletId[column.prop]) {
sortField.value = 'currentPermanentGold'
sortWalletId.value = propToWalletId[column.prop]
}
sortOrder.value = column.order === 'ascending' ? 'ASC' : 'DESC'
getObj.value.pageNum = 1
get()
}
//
onMounted(async function () {
await get()
await getMarket()
await getExportList()
})
const handlePageSizeChange = function (val) {
getObj.value.pageSize = val
getObj.value.pageNum = 1
get()
}
const handleCurrentChange = function (val) {
getObj.value.pageNum = val
get()
}
const exportExcel = async function () {
const params = {
pageNum: 1, //
pageSize: 10000, //
}
//
if (selectData.value.jwcode) {
params.jwcode = Number(selectData.value.jwcode)
}
if (selectData.value.markets && selectData.value.markets.length > 0) {
//
params.market = String(selectData.value.markets[0])
}
const res = await API({
url: '/export/exportUserWallet',
method: 'post',
data: params
})
if (res.code === 200) {
ElMessage.success(t('elmessage.exportSuccess'))
//
} else {
ElMessage.error(res.msg || t('elmessage.exportFailed'))
}
}
const exportWalletDetail = async () => {
const params = {
pageNum: walletDetailQuery.value.pageNum,
pageSize: walletDetailQuery.value.pageSize,
userWalletRecord: {
jwcode: Number(currentWalletInfo.value.jwcode),
walletId: currentWalletInfo.value.walletId
}
}
try {
const res = await API({
url: '/export/exportUserWalletRecord',
method: 'post',
data: params
})
if (res.code === 200) {
ElMessage.success(t('elmessage.exportSuccess'))
} else {
ElMessage.error(res.msg || t('elmessage.exportFailed'))
}
} catch (error) {
console.error('导出钱包明细出错:', error)
ElMessage.error(t('elmessage.exportWalletDetailError'))
}
}
const selectWallet = async function () {
selectWalletVisible.value = true
}
const exportExcelOnlyOne = async function () {
if (!selectWalletForm.value.companyWalletId) {
ElMessage.error(t('elmessage.selectCompanyWallet'))
return
}
const params = {
pageNum: 1, //
pageSize: 10000, //
userWalletRecord: {
walletId: selectWalletForm.value.companyWalletId,
jwcode: selectData.value.jwcode ? Number(selectData.value.jwcode) : null
}
}
try {
const res = await API({
url: '/export/exportUserWalletRecord',
method: 'post',
data: params
})
if (res.code === 200) {
ElMessage.success(t('elmessage.exportSuccess'))
openExportList() //
} else {
ElMessage.error(res.msg || t('elmessage.exportFailed'))
}
} catch (error) {
console.error('导出失败:', error)
ElMessage.error(t('elmessage.exportFailed'))
} finally {
selectWalletVisible.value = false
}
}
const exportListVisible = ref(false)
const selectWalletVisible = ref(false)
const selectWalletFormRef = ref(null)
const selectWalletRules = {
companyWalletId: [
{ required: true, message: t('common_list.companyWalletPlaceholder'), trigger: 'change' }
]
}
//
const closeSelectWallet = () => {
//
selectWalletFormRef.value.resetFields()
selectWalletForm.value = {}
selectWalletVisible.value = false
}
const selectWalletForm = ref({})
const companyWalletList = ref([
{
id: 2,
name: t('clientCount.market.hkGold') + t('clientCount.wallet')
},
{
id: 3,
name: t('clientCount.market.sgHcGold')
},
{
id: 4,
name: t('clientCount.market.myGold')
},
{
id: 5,
name: t('clientCount.market.sgCmGold')
},
{
id: 6,
name: t('clientCount.market.caGold')
},
{
id: 7,
name: t('clientCount.market.thHsGold')
},
{
id: 8,
name: t('clientCount.market.thHaGold')
},
{
id: 9,
name: t('clientCount.market.vnGold')
},
{
id: 10,
name: t('clientCount.market.bjGold')
}
])
//
const openExportList = () => {
getExportList()
exportListVisible.value = true
}
//
const exportList = ref([])
//
const exportListLoading = ref(false)
//
const getExportList = async () => {
exportListLoading.value = true
try {
const result = await API({ url: '/export/export' })
if (result.code === 200) {
// type1617
const filteredData = result.data.filter(item => {
return item.type === 16 || item.type === 17;
});
exportList.value = filteredData
} else {
ElMessage.error(result.msg || t('elmessage.getExportListError'))
}
} catch (error) {
console.error('获取导出列表出错:', error)
ElMessage.error(t('elmessage.getExportListError'))
} finally {
exportListLoading.value = false
}
}
//
const downloadExportFile = (item) => {
if (item.state === 2) {
const link = document.createElement('a')
link.href = item.url
link.download = item.fileName
link.click()
} else {
ElMessage.warning(t('elmessage.exportingInProgress'))
}
}
//
const getTagType = (state) => {
switch (state) {
case 0:
return 'info';
case 1:
return 'primary';
case 2:
return 'success';
case 3:
return 'danger';
default:
return 'info';
}
}
//
const getTagText = (state) => {
switch (state) {
case 0:
return t('elmessage.pendingExecution');
case 1:
return t('elmessage.executing');
case 2:
return t('elmessage.executed');
case 3:
return t('elmessage.errorExecution');
default:
return t('elmessage.unknownStatus');
}
}
//
const selectedMarketPath = ref([])
const handleMarketChange = (value) => {
if (value && value.length > 0) {
const lastValue = value[value.length - 1];
//
const marketValue = reverseMarketMapping[lastValue];
selectData.value.markets = Array.isArray(marketValue) ? marketValue : [marketValue];
} else {
// []
selectData.value.markets = [];
}
};
//
const getMarket = async function () {
try {
// POST
const result = await API({
url: '/market/selectMarket',
});
//
console.log('请求成功', result)
//
const transformTree = (nodes) => {
//
const allChildren = nodes.flatMap(node => node.children || []);
return allChildren.map(child => {
const grandchildren = child.children && child.children.length
? transformTree([child]) //
: null;
return {
value: child.name,
label: child.name,
children: grandchildren
};
});
};
//
markets.value = transformTree(result.data)
console.log('转换后的地区树==============', markets.value)
} catch (error) {
console.log('请求失败', error)
}
}
const format3 = (num) => {
//
return num.toLocaleString('en-US')
}
</script>
<template>
<div style="display: flex; flex-direction: column; height: 95vh;">
<el-card class="card1" style="margin-bottom: 1vh;">
<div class="head-card">
<div class="head-card-element">
<el-text class="mx-1" size="large">{{ $t('common.jwcode') }}</el-text>
<el-input v-model="selectData.jwcode" style="width: 160px" :placeholder="$t('common.jwcodePlaceholder')"
clearable />
</div>
<div class="head-card-element">
<el-text class="mx-1" size="large">{{ $t('common_list.market') }}</el-text>
<el-cascader v-model="selectedMarketPath" :options="markets" :placeholder="$t('common_list.marketPlaceholder')"
clearable style="width:180px" @change="handleMarketChange" />
</div>
<div class="head-card-element">
<!-- <el-checkbox v-model="showEmployeeData" @change="search()">员工数据</el-checkbox> -->
</div>
<el-button type="primary" @click="search()">{{ $t('common.search') }}</el-button>
<el-button @click="reset" type="success">{{ $t('common.reset') }}</el-button>
<el-button type="primary" @click="exportExcel()">{{ $t('common.exportExcel') }}</el-button>
<!-- <el-button type="primary" @click="selectWallet()">{{ $t('common.exportCompanyWalletDetail') }}</el-button> -->
<el-button type="primary" @click="openExportList">{{ $t('common.viewExportList') }}</el-button>
</div>
<!-- </div> -->
</el-card>
<el-card class="card2">
<!-- 设置表格容器的高度和滚动样式 -->
<div style="height: 85vh; overflow-y: auto">
<el-table ref="tableRef" :data="tableData" @cellClick="cellClick" style="width: 100%; height: 85vh"
@sort-change="handleSortChange" :row-style="{ height: '50px' }">
<el-table-column type="index" :label="$t('common_list.id')" width="100px" fixed="left">
<template #default="scope">
<span>{{
scope.$index + 1 + (getObj.pageNum - 1) * getObj.pageSize
}}</span>
</template>
</el-table-column>
<el-table-column prop="name" :label="$t('common_list.name')" width="140" />
<el-table-column prop="jwcode" :label="$t('common_list.jwcode')" width="140" />
<el-table-column prop="market" :label="$t('common_list.market')" width="140" />
<el-table-column prop="historyGold" :label="$t('clientCount.market.historyGold')" sortable="custom"
min-width="140">
<template #default="scope">
<span>{{ (scope.row.historyGold || 0) }}</span>
</template>
</el-table-column>
<el-table-column prop="hkGold" :label="$t('clientCount.market.hkGold')" sortable="custom" min-width="140">
<template #default="scope">
<span>{{ (scope.row.hkGold || 0) }}</span>
</template>
</el-table-column>
<el-table-column prop="sgHcGold" :label="$t('clientCount.market.sgHcGold')" sortable="custom" min-width="150">
<template #default="scope">
<span>{{ (scope.row.sgHcGold || 0) }}</span>
</template>
</el-table-column>
<el-table-column prop="myGold" :label="$t('clientCount.market.myGold')" sortable="custom" min-width="120">
<template #default="scope">
<span>{{ (scope.row.myGold || 0) }}</span>
</template>
</el-table-column>
<el-table-column prop="sgGold" :label="$t('clientCount.market.sgCmGold')" sortable="custom" min-width="150">
<template #default="scope">
<span>{{ (scope.row.sgGold || 0) }}</span>
</template>
</el-table-column>
<el-table-column prop="caGold" :label="$t('clientCount.market.caGold')" sortable="custom" min-width="120">
<template #default="scope">
<span>{{ (scope.row.caGold || 0) }}</span>
</template>
</el-table-column>
<el-table-column prop="thHsGold" :label="$t('clientCount.market.thHsGold')" sortable="custom" min-width="140">
<template #default="scope">
<span>{{ (scope.row.thHsGold || 0) }}</span>
</template>
</el-table-column>
<el-table-column prop="thHaGold" :label="$t('clientCount.market.thHaGold')" sortable="custom" min-width="140">
<template #default="scope">
<span>{{ (scope.row.thHaGold || 0) }}</span>
</template>
</el-table-column>
<el-table-column prop="vnGold" :label="$t('clientCount.market.vnGold')" sortable="custom" min-width="120">
<template #default="scope">
<span>{{ (scope.row.vnGold || 0) }}</span>
</template>
</el-table-column>
<el-table-column prop="bjGold" :label="$t('clientCount.market.bjGold')" sortable="custom" min-width="120">
<template #default="scope">
<span>{{ (scope.row.bjGold || 0) }}</span>
</template>
</el-table-column>
</el-table>
</div>
<!-- 分页 -->
<div class="pagination" style="margin-top: 20px">
<el-pagination background :current-page="getObj.pageNum" :page-size="getObj.pageSize"
:page-sizes="[5, 10, 20, 50, 100]" layout="total, sizes, prev, pager, next, jumper" :total="total"
@size-change="handlePageSizeChange" @current-change="handleCurrentChange"></el-pagination>
</div>
</el-card>
</div>
<el-dialog v-model="exportListVisible" :title="$t('common_export.exportList')" width="80%">
<el-table :data="exportList" style="width: 100% ;height: 60vh;" :loading="exportListLoading">
<el-table-column prop="fileName" :label="$t('common_export.fileName')" />
<el-table-column prop="state" :label="$t('common_export.status')">
<template #default="scope">
<el-tag :type="getTagType(scope.row.state)" :effect="scope.row.state === 3 ? 'light' : 'plain'">
{{ getTagText(scope.row.state) }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="createTime" :label="$t('common_export.createTime')">
<template #default="scope">
{{ moment(scope.row.createTime).format('YYYY-MM-DD HH:mm:ss') }}
</template>
</el-table-column>
<el-table-column :label="$t('common_export.operation')">
<template #default="scope">
<el-button type="primary" size="small" @click="downloadExportFile(scope.row)"
:disabled="scope.row.state !== 2">
{{ $t('common_export.download') }}
</el-button>
</template>
</el-table-column>
</el-table>
<template #footer>
<div class="dialog-footer">
<el-button text @click="exportListVisible = false">{{ $t('common_export.close') }}</el-button>
</div>
</template>
</el-dialog>
<el-dialog v-model="selectWalletVisible" width="30%" top="20%" :before-close="closeSelectWallet">
<el-form :model="selectWalletForm" :rules="selectWalletRules" ref="selectWalletFormRef" label-width="120px">
<el-form-item :label="$t('common_list.companyWallet')" prop="companyWalletId" style="margin-top: 30px;">
<el-select v-model="selectWalletForm.companyWalletId" :placeholder="$t('common_list.companyWalletPlaceholder')"
style="width: 80%;">
<el-option v-for="item in companyWalletList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
</el-form>
<template #footer>
<div style="display: flex; justify-content: flex-end;">
<el-button type="primary" @click="exportExcelOnlyOne()">{{ $t('common.confirm') }}</el-button>
</div>
</template>
</el-dialog>
<el-dialog v-model="walletDetailVisible" width="70%" top="20vh">
<template #header>
<div class="wallet-detail-header"
style="display: flex; gap: 40px; justify-content: space-between; align-items: center; font-size: 16px;">
<span style="font-weight: bold; color: #F56C6C;">* {{ currentWalletInfo.walletName }}</span>
<span><span style="color: #F56C6C;">*</span> {{ $t('clientCount.user') }}: {{ currentWalletInfo.userName }} (ID:
{{ currentWalletInfo.jwcode }})</span>
<span><span style="color: #F56C6C;">*</span> {{ $t('common.market') }}: {{ currentWalletInfo.marketName
}}</span>
</div>
<div style="margin-top: 15px; font-size: 16px;">
<span style="color: #F56C6C; font-weight: bold;">* {{ $t('clientCount.currentBalance') }}: {{
format3(currentWalletInfo.currentBalance || 0) }} {{ $t('common.goldCoin') }}</span>
</div>
</template>
<el-table :data="walletDetailList" v-loading="walletDetailLoading"
style="width: 100%; height: 50vh; overflow-y: auto;" border stripe>
<el-table-column prop="time" :label="$t('clientCount.time')" align="center" width="180">
<template #default="scope">{{ scope.row.time }}</template>
</el-table-column>
<el-table-column prop="type" :label="$t('clientCount.transactionType')" align="center" width="120" />
<el-table-column prop="amount" :label="$t('common_list.money')" align="center" width="120">
<template #default="scope">
<span :style="{ color: scope.row.amount >= 0 ? '#67C23A' : '#F56C6C', fontWeight: 'bold' }">
{{ scope.row.amount > 0 ? '+' + format3(scope.row.amount) : format3(scope.row.amount) }}
</span>
</template>
</el-table-column>
<el-table-column prop="desc" :label="$t('clientCount.transactionDesc')" align="center" />
<el-table-column prop="orderNo" :label="$t('clientCount.transactionOrderNo')" align="center" width="220" />
<el-table-column prop="status" :label="$t('clientCount.transactionStatus')" align="center" width="220"
fixed="right">
<template #default="scope">
<el-tag :type="scope.row.status === 1 ? 'success' : scope.row.status === 2 ? 'danger' : 'info'"
:effect="scope.row.status === 1 ? 'light' : 'plain'">
{{ scope.row.status === 1 ? t('common_list.normal') : scope.row.status === 2 ? t('common_list.refunded') :
t('clientCount.exceptionData') }}
</el-tag>
</template>
</el-table-column>
</el-table>
<template #footer>
<div class="dialog-footer" style="display: flex; justify-content: space-between; align-items: center;">
<div class="pagination-container">
<el-pagination background :current-page="walletDetailQuery.pageNum" :page-size="walletDetailQuery.pageSize"
:page-sizes="[10, 20, 50, 100]" layout="total, sizes, prev, pager, next, jumper" :total="walletDetailTotal"
@size-change="handleWalletDetailSizeChange" @current-change="handleWalletDetailCurrentChange" />
</div>
<div>
<el-button type="primary" @click="exportWalletDetail">{{ $t('common.exportExcel') }}</el-button>
<el-button text @click="walletDetailVisible = false">{{ $t('common_export.close') }}</el-button>
</div>
</div>
</template>
</el-dialog>
</template>
<style scoped lang="scss">
//
.card1 {
background: #F3FAFE;
}
//
.card2 {
background: #E7F4FD;
flex: 1;
display: flex;
flex-direction: column;
:deep(.el-card__body) {
padding: 20px;
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
}
}
//
:deep(.el-table__header-wrapper),
:deep(.el-table__body-wrapper),
:deep(.el-table__cell),
/* 表格 */
:deep(.el-table__body td) {
background-color: #F3FAFE !important;
}
/* 表头 */
:deep(.el-table__header th) {
background-color: #F3FAFE !important;
}
/* 鼠标悬停 */
:deep(.el-table__row:hover > .el-table__cell) {
background-color: #E5EBFE !important;
}
.pagination {
display: flex;
}
.status {
display: flex;
}
.head-card {
display: flex;
}
.head-card-element {
margin-right: 20px;
}
.head-card-btn {
margin-left: auto;
}
.custom-box {
display: flex;
flex-wrap: wrap;
row-gap: 5px;
div:nth-child(1) {
flex: 1 0 100%;
}
div {
margin-right: 20px;
}
}
</style>

472
src/views/walletManage/components/WalletDetailTemplate.vue

@ -0,0 +1,472 @@
<script setup>
import { ref, watch, nextTick, onMounted } from 'vue'
import { useRoute } from 'vue-router'
import { ElMessage } from 'element-plus'
import API from '@/util/http.js'
import moment from 'moment'
import { useI18n } from 'vue-i18n'
import { useAdminStore } from "@/store/index.js"
import { storeToRefs } from "pinia"
import { selectWalletRecords } from "@/api/cash/cash.js"
const props = defineProps({
type: {
type: Number,
required: true
}
})
const { t } = useI18n()
const route = useRoute()
const adminStore = useAdminStore()
const { flag } = storeToRefs(adminStore)
const tableData = ref([])
const total = ref(0)
const loading = ref(false)
const marketLists = ref([])
const selectedMarketPath = ref([])
const selectData = ref({
jwcode: '',
walletId: '',
market: ''
})
const getObj = ref({
pageNum: 1,
pageSize: 20
})
const tableRef = ref(null)
const scrollTableTop = () => {
tableRef.value?.setScrollTop?.(0)
}
//
const trimJwCode = () => {
if (selectData.value.jwcode) {
selectData.value.jwcode = selectData.value.jwcode.replace(/\s/g, '');
}
}
//
const getWalletRecordTypeText = (item) => {
const type = Number(item.type)
if (type === 0) {
return t('clientCount.recharge')
}
if (type === 1) {
return t('clientCount.consume')
}
if (type === 2) {
return t('clientCount.refund')
}
return item.typeText || t('clientCount.other')
}
//
const format3 = (num) => {
return num.toLocaleString('en-US')
}
//
const getWalletData = async () => {
console.log('walletId:', selectData.value.walletId)
if (!selectData.value.walletId) return;
if (selectData.value.jwcode) {
const numberRegex = /^\d{1,9}$/;
if (!numberRegex.test(selectData.value.jwcode)) {
ElMessage.error(t('elmessage.checkJwcodeFormat'))
return
}
}
loading.value = true
try {
const params = {
pageNum: getObj.value.pageNum,
pageSize: getObj.value.pageSize,
userWalletRecord: {
walletId: selectData.value.walletId,
market: selectData.value.market,
jwcode: selectData.value.jwcode
}
}
const result = await selectWalletRecords(params)
if (result.code === 200) {
tableData.value = result.data.list.map(item => ({
...item,
time: moment(item.createTime).format('YYYY-MM-DD HH:mm:ss'),
typeText: getWalletRecordTypeText(item),
amount: Number(item.amount) || 0,
desc: item.description || '',
orderNo: item.orderCode,
status: item.status === 0 ? 1 : 2,
userName: item.userName || '-',
jwcode: item.jwcode || '-'
}))
total.value = result.data.total
await nextTick()
scrollTableTop()
} else {
ElMessage.error(result.msg || t('elmessage.getDataFailed'))
}
} catch (error) {
console.error('获取钱包明细失败:', error)
} finally {
loading.value = false
}
}
//
const search = function () {
trimJwCode()
getObj.value.pageNum = 1
getWalletData()
}
//
const reset = function () {
selectData.value.jwcode = ''
selectData.value.market = ''
selectedMarketPath.value = []
getObj.value.pageNum = 1
getWalletData()
}
//
const handlePageSizeChange = function (val) {
getObj.value.pageSize = val
getWalletData()
}
const handleCurrentChange = function (val) {
getObj.value.pageNum = val
getWalletData()
}
// flag
watch(flag, (newFlag, oldFlag) => {
if (newFlag !== oldFlag) {
getWalletData()
}
})
// props
watch(
() => props.type,
(newType) => {
if (newType) {
selectData.value.walletId = newType
getObj.value.pageNum = 1
getWalletData()
}
},
{ immediate: true }
)
// ==================== ====================
const exportListVisible = ref(false)
const exportList = ref([])
const exportListLoading = ref(false)
const exportExcelOnlyOne = async function () {
if (!selectData.value.walletId) {
ElMessage.error(t('elmessage.selectCompanyWallet'))
return
}
const params = {
pageNum: 1, //
pageSize: 10000, //
userWalletRecord: {
walletId: selectData.value.walletId,
jwcode: selectData.value.jwcode ? Number(selectData.value.jwcode) : null
}
}
try {
const res = await API({
url: '/export/exportUserWalletRecord',
method: 'post',
data: params
})
if (res.code === 200) {
ElMessage.success(t('elmessage.exportSuccess'))
} else {
ElMessage.error(res.msg || t('elmessage.exportFailed'))
}
} catch (error) {
console.error('导出失败:', error)
ElMessage.error(t('elmessage.exportFailed'))
}
}
//
const openExportList = () => {
getExportList()
exportListVisible.value = true
}
//
const getExportList = async () => {
exportListLoading.value = true
try {
const result = await API({ url: '/export/export' })
if (result.code === 200) {
// type 16 17 WalletBalance
const filteredData = result.data.filter(item => {
return item.type === 16 || item.type === 17;
});
exportList.value = filteredData
} else {
ElMessage.error(result.msg || t('elmessage.getExportListError'))
}
} catch (error) {
console.error('获取导出列表出错:', error)
ElMessage.error(t('elmessage.getExportListError'))
} finally {
exportListLoading.value = false
}
}
//
const downloadExportFile = (item) => {
if (item.state === 2) {
const link = document.createElement('a')
link.href = item.url
link.download = item.fileName
link.click()
} else {
ElMessage.warning(t('elmessage.exportingInProgress'))
}
}
//
const getTagType = (state) => {
switch (state) {
case 0: return 'info';
case 1: return 'primary';
case 2: return 'success';
case 3: return 'danger';
default: return 'info';
}
}
//
const getTagText = (state) => {
switch (state) {
case 0: return t('elmessage.pendingExecution');
case 1: return t('elmessage.executing');
case 2: return t('elmessage.executed');
case 3: return t('elmessage.errorExecution');
default: return t('elmessage.unknownStatus');
}
}
const transformTree = (nodes) => {
//
const allChildren = nodes.flatMap(node => node.children || []);
return allChildren.map(child => {
const grandchildren = child.children && child.children.length
? transformTree([child]) //
: null;
return {
value: child.id,
label: child.name,
children: grandchildren
};
});
};
const selectMarket = async function () {
try {
const selectMarketResult = await API({ url: '/market/selectMarket' });
marketLists.value = transformTree(selectMarketResult.data)
console.log('转换后的地区树==============:', marketLists.value);
} catch (error) {
console.error('获取地区树失败:', error);
return {};
}
};
const handleMarketChange = (value) => {
if (value && value.length > 0) {
// id id
const lastValueId = value[value.length - 1];
selectData.value.market = lastValueId;
} else {
selectData.value.market = '';
}
};
onMounted(() => {
selectData.value.walletId = props.type
selectMarket()
})
</script>
<template>
<div style="display: flex; flex-direction: column; height: 95vh;">
<el-card class="card1" style="margin-bottom: 1vh;">
<div class="head-card">
<div class="head-card-element">
<el-text class="mx-1" size="large">{{ $t('common_list.jwcode') }}</el-text>
<el-input v-model="selectData.jwcode" style="width: 12.5vw" placeholder="请输入精网号" clearable />
</div>
<div class="head-card-element">
<el-text class="mx-1" size="large">{{ $t('common_list.market') }}</el-text>
<el-cascader v-model="selectedMarketPath" :options="marketLists" :placeholder="$t('common_list.marketPlaceholder')"
clearable style="width:180px" @change="handleMarketChange" />
</div>
<div class="head-card-btn">
<el-button type="success" @click="reset">{{ $t('common.reset') }}</el-button>
<el-button type="primary" @click="search">{{ $t('common.search') }}</el-button>
<el-button type="primary" @click="exportExcelOnlyOne">{{ $t('common.exportExcel') }}</el-button>
<el-button type="primary" @click="openExportList">{{ $t('common.viewExportList') }}</el-button>
</div>
</div>
</el-card>
<el-card class="card2">
<div style="height: 85vh; overflow-y: auto">
<el-table ref="tableRef" :data="tableData" v-loading="loading" style="width: 100%; height: 85vh"
:row-style="{ height: '50px' }">
<el-table-column type="index" :label="$t('common_list.id')" width="80px" fixed="left">
<template #default="scope">
<span>{{ scope.$index + 1 + (getObj.pageNum - 1) * getObj.pageSize }}</span>
</template>
</el-table-column>
<el-table-column prop="jwcode" :label="$t('common_list.jwcode')" width="140" />
<el-table-column prop="userName" :label="$t('common_list.name')" width="140" />
<el-table-column prop="marketName" :label="$t('common_list.marketName')" width="140" />
<el-table-column prop="typeText" :label="$t('clientCount.transactionType')" align="center" width="120" />
<el-table-column prop="transactionCurrency" :label="$t('clientCount.transactionCurrency')" align="center"
width="120" />
<el-table-column prop="amount" :label="$t('common_list.money')" align="center" width="120">
<template #default="scope">
<span :style="{ color: scope.row.amount >= 0 ? '#67C23A' : '#F56C6C', fontWeight: 'bold' }">
{{ scope.row.amount > 0 ? '+' + format3(scope.row.amount) : format3(scope.row.amount) }}
</span>
</template>
</el-table-column>
<el-table-column prop="orderNo" :label="$t('clientCount.transactionOrderNo')" align="center" min-width="320" />
<el-table-column prop="desc" :label="$t('clientCount.transactionDesc')" align="center" min-width="150" />
<el-table-column prop="status" :label="$t('clientCount.transactionStatus')" align="center" width="150">
<template #default="scope">
<el-tag :type="scope.row.status === 1 ? 'success' : scope.row.status === 2 ? 'danger' : 'info'"
:effect="scope.row.status === 1 ? 'light' : 'plain'">
{{ scope.row.status === 1 ? t('common_list.normal') : scope.row.status === 2 ? t('common_list.refunded')
: t('clientCount.exceptionData') }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="time" :label="$t('clientCount.time')" align="center" width="180">
<template #default="scope">{{ scope.row.time }}</template>
</el-table-column>
</el-table>
</div>
<!-- 分页 -->
<div class="pagination" style="margin-top: 20px">
<el-pagination background :current-page="getObj.pageNum" :page-size="getObj.pageSize"
:page-sizes="[10, 20, 50, 100]" layout="total, sizes, prev, pager, next, jumper" :total="total"
@size-change="handlePageSizeChange" @current-change="handleCurrentChange"></el-pagination>
</div>
</el-card>
<!-- 导出列表弹窗 -->
<el-dialog v-model="exportListVisible" :title="$t('common_export.exportList')" width="80%">
<el-table :data="exportList" style="width: 100% ;height: 60vh;" :loading="exportListLoading">
<el-table-column prop="fileName" :label="$t('common_export.fileName')" />
<el-table-column prop="state" :label="$t('common_export.status')">
<template #default="scope">
<el-tag :type="getTagType(scope.row.state)" :effect="scope.row.state === 3 ? 'light' : 'plain'">
{{ getTagText(scope.row.state) }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="createTime" :label="$t('common_export.createTime')">
<template #default="scope">
{{ moment(scope.row.createTime).format('YYYY-MM-DD HH:mm:ss') }}
</template>
</el-table-column>
<el-table-column :label="$t('common_export.operation')">
<template #default="scope">
<el-button type="primary" size="small" @click="downloadExportFile(scope.row)"
:disabled="scope.row.state !== 2">
{{ $t('common_export.download') }}
</el-button>
</template>
</el-table-column>
</el-table>
<template #footer>
<div class="dialog-footer">
<el-button text @click="exportListVisible = false">{{ $t('common_export.close') }}</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<style scoped lang="scss">
.card1 {
background: #F3FAFE;
}
.card2 {
background: #E7F4FD;
flex: 1;
display: flex;
flex-direction: column;
:deep(.el-card__body) {
padding: 20px;
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
}
}
:deep(.el-table__header-wrapper),
:deep(.el-table__body-wrapper),
:deep(.el-table__cell),
:deep(.el-table__body td) {
background-color: #F3FAFE !important;
}
:deep(.el-table__header th) {
background-color: #F3FAFE !important;
}
:deep(.el-table__row:hover > .el-table__cell) {
background-color: #E5EBFE !important;
}
.pagination {
display: flex;
}
.head-card {
display: flex;
}
.head-card-element {
margin-right: 20px;
white-space: nowrap;
.mx-1 {
font-size: 16px;
margin: 0 10px;
}
}
.head-card-btn {
margin-left: 100px;
}
</style>

10
src/views/walletManage/walletDetail/BJWallet.vue

@ -0,0 +1,10 @@
<!-- 北京钱包界面 -->
<template>
<div>
<WalletDetailTemplate :type="10" />
</div>
</template>
<script setup>
import WalletDetailTemplate from '../components/WalletDetailTemplate.vue'
</script>
<style scoped></style>

10
src/views/walletManage/walletDetail/CAWallet.vue

@ -0,0 +1,10 @@
<!-- 加拿大钱包界面 -->
<template>
<div>
<WalletDetailTemplate :type="6" />
</div>
</template>
<script setup>
import WalletDetailTemplate from '../components/WalletDetailTemplate.vue'
</script>
<style scoped></style>

12
src/views/walletManage/walletDetail/HKWallet.vue

@ -0,0 +1,12 @@
<!-- 初始界面 -->
<template>
<div>
<WalletDetailTemplate :type="2" />
</div>
</template>
<script setup>
import WalletDetailTemplate from '../components/WalletDetailTemplate.vue'
</script>
<style scoped>
</style>

10
src/views/walletManage/walletDetail/MyWallet.vue

@ -0,0 +1,10 @@
<!-- 马来钱包界面 -->
<template>
<div>
<WalletDetailTemplate :type="4" />
</div>
</template>
<script setup>
import WalletDetailTemplate from '../components/WalletDetailTemplate.vue'
</script>
<style scoped></style>

10
src/views/walletManage/walletDetail/SGWalletCM.vue

@ -0,0 +1,10 @@
<!-- 新加坡CM钱包界面 -->
<template>
<div>
<WalletDetailTemplate :type="5" />
</div>
</template>
<script setup>
import WalletDetailTemplate from '../components/WalletDetailTemplate.vue'
</script>
<style scoped></style>

10
src/views/walletManage/walletDetail/SGWalletHC.vue

@ -0,0 +1,10 @@
<!-- 新加坡HC钱包界面 -->
<template>
<div>
<WalletDetailTemplate :type="3" />
</div>
</template>
<script setup>
import WalletDetailTemplate from '../components/WalletDetailTemplate.vue'
</script>
<style scoped></style>

10
src/views/walletManage/walletDetail/THWalletHA.vue

@ -0,0 +1,10 @@
<!-- 泰国HA钱包界面 -->
<template>
<div>
<WalletDetailTemplate :type="8" />
</div>
</template>
<script setup>
import WalletDetailTemplate from '../components/WalletDetailTemplate.vue'
</script>
<style scoped></style>

10
src/views/walletManage/walletDetail/THWalletHS.vue

@ -0,0 +1,10 @@
<!-- 泰国HS钱包界面 -->
<template>
<div>
<WalletDetailTemplate :type="7" />
</div>
</template>
<script setup>
import WalletDetailTemplate from '../components/WalletDetailTemplate.vue'
</script>
<style scoped></style>

10
src/views/walletManage/walletDetail/VNWallet.vue

@ -0,0 +1,10 @@
<!-- 越南钱包界面 -->
<template>
<div>
<WalletDetailTemplate :type="9" />
</div>
</template>
<script setup>
import WalletDetailTemplate from '../components/WalletDetailTemplate.vue'
</script>
<style scoped></style>
Loading…
Cancel
Save