Browse Source

feat(金币管理): 实现客户金币钱包详情页与永久金币悬浮展示

- 替换 clientCountWallet 页面的静态表格数据为动态 API 调用,支持按精网号和市场筛选
- 新增钱包映射关系,实现点击表格单元格显示对应钱包的明细记录
- 在 clientCountBalance 页面添加永久金币悬浮展示功能,显示各钱包分布
- 支持从余额页点击永久金币跳转到钱包详情页,并传递精网号参数
jiangcheng/feature-20260306102603-钱包体系
zhangrenyuan 1 month ago
parent
commit
f86fcac78c
  1. 211
      src/views/usergold/gold/clientCountBalance.vue
  2. 234
      src/views/usergold/gold/clientCountWallet.vue

211
src/views/usergold/gold/clientCountBalance.vue

@ -14,6 +14,7 @@ const { adminData, menuTree, flag } = storeToRefs(adminStore)
} }
}) })
import { onMounted, ref, watch, nextTick } from 'vue' import { onMounted, ref, watch, nextTick } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import moment from 'moment' import moment from 'moment'
import API from '@/util/http.js' import API from '@/util/http.js'
@ -21,6 +22,7 @@ import { reverseMarketMapping } from "@/utils/marketMap.js";
// //
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
const { t } = useI18n() const { t } = useI18n()
const router = useRouter()
// //
// const showEmployeeData = ref(false) // const showEmployeeData = ref(false)
@ -76,6 +78,38 @@ const getObj = ref({
const sortField = ref('') const sortField = ref('')
const sortOrder = ref('') const sortOrder = ref('')
// ( walletList)
const getUserWalletDetail = (row) => {
// ()
const walletList = row.walletList || [];
if (walletList.length === 0) {
return [];
}
//
return walletList.map(item => {
// ""
let displayName = item.walletName || '';
if (displayName.endsWith('钱包')) {
displayName = displayName.slice(0, -2);
}
return {
wallet: displayName,
amount: (item.permanentGold || 0).toFixed(2)
};
});
}
//
const goToWalletDetail = (row) => {
router.push({
name: 'clientCountWallet',
query: { jwcode: row.jwcode }
})
}
// //
// =========================================================================== // ===========================================================================
// //
@ -398,6 +432,21 @@ const format3 = (num) => {
return num.toLocaleString('en-US') return num.toLocaleString('en-US')
} }
// Popover
const popoverVisible = ref(false)
const popoverData = ref(null)
const virtualRef = ref(null)
const handleMouseEnter = (row, e) => {
popoverData.value = row
popoverVisible.value = true
virtualRef.value = e.currentTarget
}
const handleMouseLeave = () => {
popoverVisible.value = false
}
</script> </script>
<template> <template>
@ -455,7 +504,13 @@ const format3 = (num) => {
</el-table-column> </el-table-column>
<el-table-column prop="currentPermanentGold" :label="$t('common_list.permanentGold')" sortable="custom" width="210"> <el-table-column prop="currentPermanentGold" :label="$t('common_list.permanentGold')" sortable="custom" width="210">
<template #default="scope"> <template #default="scope">
<span>{{ (scope.row.currentPermanentGold || 0) }}</span>
<div
class="hover-cell-wrapper"
@mouseenter="handleMouseEnter(scope.row, $event)"
@mouseleave="handleMouseLeave"
>
<span>{{ (scope.row.currentPermanentGold || 0) }}</span>
</div>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="currentFreeJune" :label="$t('common_list.freeGold6Month')" sortable="custom" width="200"> <el-table-column prop="currentFreeJune" :label="$t('common_list.freeGold6Month')" sortable="custom" width="200">
@ -473,53 +528,6 @@ const format3 = (num) => {
<span>{{ (scope.row.currentTaskGold || 0) }}</span> <span>{{ (scope.row.currentTaskGold || 0) }}</span>
</template> </template>
</el-table-column> </el-table-column>
<!-- <el-table-column prop="rcoin" label="历史金币总额" width="150">
<template #default="scope">
<el-popover trigger="hover" placement="left" width="150">
<template #default>
<div>
<div>永久金币{{ (scope.row.sumPermanentGold || 0) }}</div>
<div>免费金币{{ ((scope.row.sumFreeJune || 0) + (scope.row.sumFreeDecember || 0)) }}</div>
<div>任务金币{{ (scope.row.sumTaskGold || 0) }}</div>
</div>
</template>
<template #reference>
<span>
{{
(scope.row.sumPermanentGold || 0) +
(scope.row.sumFreeJune || 0) +
(scope.row.sumFreeDecember || 0) +
(scope.row.sumTaskGold || 0)
}}</span>
</template>
</el-popover>
</template>
</el-table-column>
<el-table-column prop="sumConsume" label="历史消费" width="150">
<template #default="scope">
<el-popover trigger="hover" placement="left" width="150">
<template #default>
<div>
<div>永久金币{{ (scope.row.sumConsumeGold || 0) }}</div>
<div>免费金币{{
((scope.row.sumConsumeJune || 0) + (scope.row.sumConsumeDecember || 0))
}}
</div>
<div>任务金币{{ (scope.row.sumConsumeJune || 0) }}</div>
</div>
</template>
<template #reference>
<span>
{{
(scope.row.sumConsumeGold || 0) +
(scope.row.sumConsumeTaskGold || 0) +
(scope.row.sumConsumeJune || 0) +
(scope.row.sumConsumeDecember || 0)
}}</span>
</template>
</el-popover>
</template>
</el-table-column>-->
</el-table> </el-table>
</div> </div>
<!-- 分页 --> <!-- 分页 -->
@ -560,9 +568,115 @@ const format3 = (num) => {
</template> </template>
</el-dialog> </el-dialog>
<!-- Global Virtual Popover -->
<el-popover
v-model:visible="popoverVisible"
:virtual-ref="virtualRef"
virtual-triggering
placement="right"
:width="200"
popper-class="custom-blue-popover"
:show-arrow="false"
:offset="-100"
:hide-after="0"
>
<div class="popover-content" v-if="popoverData">
<div class="popover-header">
<span class="header-title">永久金币</span>
<span class="header-amount">{{ popoverData.currentPermanentGold || 0 }}</span>
</div>
<div class="popover-list">
<div v-for="(item, index) in getUserWalletDetail(popoverData)" :key="index" class="popover-item">
<span class="wallet-name">{{ item.wallet }}</span>
<span class="wallet-amount">{{ item.amount }}</span>
</div>
</div>
</div>
</el-popover>
</template> </template>
<style>
/* 全局样式或非 scoped 样式用于覆盖 el-popover */
.el-popover.custom-blue-popover {
background-color: #E5EBFE !important;
border-color: #E5EBFE !important;
padding: 0 !important; /* Remove default padding to let content control it */
border-radius: 8px !important;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15) !important;
color: #333 !important;
}
/* 隐藏箭头 */
.el-popover.custom-blue-popover .el-popper__arrow {
display: none !important;
}
</style>
<style scoped lang="scss"> <style scoped lang="scss">
.hover-cell-wrapper {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: flex-start; /* Changed to left align */
cursor: pointer;
/* Optional: add a subtle hover effect on the cell text itself */
&:hover {
color: #409EFF;
font-weight: bold;
}
}
.popover-content {
padding: 16px;
font-family: "PingFang SC", "Microsoft YaHei", sans-serif;
}
.popover-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 12px;
padding-bottom: 8px;
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
.header-title {
font-size: 14px;
font-weight: bold;
color: #333;
}
.header-amount {
font-size: 16px;
font-weight: bold;
color: #409EFF; /* Highlight color */
margin: 0 8px;
}
}
.popover-list {
display: flex;
flex-direction: column;
gap: 8px;
}
.popover-item {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 13px;
color: #555;
.wallet-name {
color: #666;
}
.wallet-amount {
font-weight: 500;
color: #333;
}
}
// //
.card1 { .card1 {
background: #F3FAFE; background: #F3FAFE;
@ -604,7 +718,6 @@ const format3 = (num) => {
background-color: #E5EBFE !important; background-color: #E5EBFE !important;
} }
.pagination { .pagination {
display: flex; display: flex;
} }

234
src/views/usergold/gold/clientCountWallet.vue

@ -31,78 +31,29 @@ const markets = ref([])
// //
// tableData // tableData
const tableData = ref([
{
name: "张三",
jwcode: "100001",
market: "香港总部",
hkGold: 15800.50,
sgHcGold: 8900.00,
myGold: 5600.75,
sgGold: 7800.20,
caGold: 3200.00,
thHsGold: 1800.50,
thHaGold: 2500.80,
vnGold: 950.30,
bjGold: 4500.00
},
{
name: "李四",
jwcode: "100002",
market: "新加坡分部",
hkGold: 9800.00,
sgHcGold: 12500.80,
myGold: 7800.00,
sgGold: 15600.90,
caGold: 4200.50,
thHsGold: 3600.00,
thHaGold: 1900.20,
vnGold: 1200.75,
bjGold: 3800.00
},
{
name: "王五",
jwcode: "100003",
market: "马来西亚分部",
hkGold: 6500.30,
sgHcGold: 4800.50,
myGold: 18900.00,
sgGold: 5600.80,
caGold: 2100.00,
thHsGold: 2800.60,
thHaGold: 3200.00,
vnGold: 1800.50,
bjGold: 2900.75
},
{
name: "赵六",
jwcode: "100004",
market: "加拿大分部",
hkGold: 4200.00,
sgHcGold: 3600.80,
myGold: 2900.50,
sgGold: 4100.00,
caGold: 25800.90,
thHsGold: 1500.30,
thHaGold: 1800.00,
vnGold: 850.20,
bjGold: 1800.50
},
{
name: "孙七",
jwcode: "100005",
market: "泰国HS分部",
hkGold: 7800.75,
sgHcGold: 6500.00,
myGold: 4200.80,
sgGold: 5900.50,
caGold: 1800.00,
thHsGold: 32500.20,
thHaGold: 8900.00,
vnGold: 2500.80,
bjGold: 3600.30
}
]);
const tableData = ref([]);
const walletMap = {
2: 'hkGold',
3: 'sgHcGold',
4: 'myGold',
5: 'sgGold',
6: 'caGold',
7: 'thHsGold',
8: 'thHaGold',
9: 'vnGold',
10: 'bjGold'
};
const propToWalletId = {
hkGold: 2,
sgHcGold: 3,
myGold: 4,
sgGold: 5,
caGold: 6,
thHsGold: 7,
thHaGold: 8,
vnGold: 9,
bjGold: 10
};
const tableRef = ref(null) const tableRef = ref(null)
const scrollTableTop = () => { const scrollTableTop = () => {
tableRef.value?.setScrollTop?.(0) tableRef.value?.setScrollTop?.(0)
@ -152,28 +103,65 @@ const get = async function (val) {
} }
console.log('搜索参数', getObj.value, selectData.value) console.log('搜索参数', getObj.value, selectData.value)
// const result = await API({
// url: '/goldDetail/getGold',
// method: 'post',
// data: { ...getObj.value, selectData: { ...selectData.value, flag: flag.value } }
// })
// console.log('', result)
// tableData.value = result.data.list
// total.value = result.data.total
//
// const resultGoldTotal = await API({
// url: '/goldDetail/goldTotal',
// data: {
// jwcode: selectData.value.jwcode,
// markets: selectData.value.markets,
// flag: flag.value
// }
// })
// result.data.list
//
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])
}
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 = {
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 || '获取数据失败');
}
} catch (error) { } catch (error) {
console.log('请求失败', error) console.log('请求失败', error)
//
} }
} }
// selectData putExcel jwcode // selectData putExcel jwcode
@ -204,8 +192,8 @@ const reset = function () {
} }
const cellClick = function (row, column) { const cellClick = function (row, column) {
console.log('cellClick', column) console.log('cellClick', column)
const propToMarketName = {
const walletId = propToWalletId[column.property];
const propToMarketName = {
hkGold: '香港', hkGold: '香港',
sgHcGold: '新加坡HC', sgHcGold: '新加坡HC',
myGold: '马来西亚', myGold: '马来西亚',
@ -215,20 +203,22 @@ const cellClick = function (row, column) {
thHaGold: '泰国HA', thHaGold: '泰国HA',
vnGold: '越南', vnGold: '越南',
bjGold: '北京' bjGold: '北京'
}
const marketName = propToMarketName[column.property]
if (marketName) {
}
const marketName = propToMarketName[column.property]
if (marketName && walletId) {
currentWalletInfo.value = { currentWalletInfo.value = {
userName: row.name, userName: row.name,
jwcode: row.jwcode, jwcode: row.jwcode,
marketName: row.market, // marketName: row.market, //
walletName: marketName + '钱包', // walletName: marketName + '钱包', //
currentBalance: row[column.property] || 0
currentBalance: row[column.property] || 0,
walletId: walletId
} }
walletDetailQuery.value.pageNum = 1 walletDetailQuery.value.pageNum = 1
getWalletDetail() getWalletDetail()
walletDetailVisible.value = true walletDetailVisible.value = true
}
}
} }
// //
@ -245,20 +235,34 @@ const walletDetailQuery = ref({
const getWalletDetail = async () => { const getWalletDetail = async () => {
walletDetailLoading.value = true walletDetailLoading.value = true
try { try {
//
// const res = await API({ url: '/goldDetail/getWalletDetail', data: { ...walletDetailQuery.value, jwcode: currentWalletInfo.value.jwcode, walletType: currentWalletInfo.value.walletName } })
//
await new Promise(resolve => setTimeout(resolve, 500))
walletDetailList.value = Array(10).fill(0).map((_, index) => ({
time: '2026-01-02 12:00:00',
type: index % 2 === 0 ? '充值' : '消耗',
amount: index % 2 === 0 ? 5000 : -5000,
desc: index % 2 === 0 ? 'Stripe充值' : '购买大黄蜂',
orderNo: 'CZLINKXXXXXXXXX' + index,
status: index % 2 === 0 ? 1 : 2,
}))
walletDetailTotal.value = 400
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: item.createTime,
type: item.type === 0 ? '充值' : '消耗',
amount: item.type === 1 ? -Math.abs(item.amount) : Math.abs(item.amount),
desc: item.description,
orderNo: item.orderCode,
status: item.status === 0 ? 1 : 2
}))
walletDetailTotal.value = result.data.total
} else {
ElMessage.error(result.msg || '获取明细失败')
}
} catch (error) { } catch (error) {
console.error(error) console.error(error)
} finally { } finally {
@ -534,8 +538,8 @@ const format3 = (num) => {
</el-card> </el-card>
<el-card class="card2"> <el-card class="card2">
<!-- 设置表格容器的高度和滚动样式 --> <!-- 设置表格容器的高度和滚动样式 -->
<div style="flex: 1; overflow-y: auto">
<el-table ref="tableRef" :data="tableData" @cellClick="cellClick"
<div style="height: 69vh; overflow-y: auto">
<el-table ref="tableRef" :data="tableData" @cellClick="cellClick" style="width: 100%; height: 69vh"
@sort-change="handleSortChange" :row-style="{ height: '50px' }"> @sort-change="handleSortChange" :row-style="{ height: '50px' }">
<el-table-column type="index" :label="$t('common_list.id')" width="100px" fixed="left"> <el-table-column type="index" :label="$t('common_list.id')" width="100px" fixed="left">
<template #default="scope"> <template #default="scope">

Loading…
Cancel
Save