foreign-admin-ui/src/views/agent/user/default.vue

343 lines
9.4 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<el-drawer v-model="innerVisible" :title="drawerTitle" direction="rtl" size="80%" header-align="center">
<div class="drawer-content">
<!-- 顶部客户信息卡片 -->
<div class="user-top" v-loading="loading">
<div class="left-card">
<div class="avatar-wrapper">
<el-icon class="avatar-icon">
<UserFilled />
</el-icon>
</div>
<div class="left-text">
<p>{{ userInfo.name }}</p>
<p>ID{{ userInfo.id }}</p>
<p>{{ userInfo.role }}</p>
</div>
</div>
<div class="right-card">
<div class="item">
<div class="item-label">
<el-icon>
<Phone />
</el-icon>
<span>手机号</span>
</div>
<div class="item-value">{{ userInfo.phone || '-' }}</div>
</div>
<div class="item">
<div class="item-label">
<el-icon>
<CreditCard />
</el-icon>
<span>账户余额</span>
</div>
<div class="item-value text-highlight">
<AnimatedNumber :value="Number(userInfo.balance) || 0" />
</div>
</div>
<div class="item">
<div class="item-label">
<el-icon>
<Money />
</el-icon>
<span>存款金额</span>
</div>
<div class="item-value text-highlight">
<AnimatedNumber :value="Number(userInfo.deposit) || 0" />
</div>
</div>
<div class="item">
<div class="item-label">
<el-icon>
<Message />
</el-icon>
<span>邮箱</span>
</div>
<div class="item-value">{{ userInfo.email || '-' }}</div>
</div>
<div class="item">
<div class="item-label">
<el-icon>
<Clock />
</el-icon>
<span>注册时间</span>
</div>
<div class="item-value">{{ userInfo.registerTime || '-' }}</div>
</div>
<div class="item">
<div class="item-label">
<el-icon>
<Wallet />
</el-icon>
<span>历史交易金额</span>
</div>
<div class="item-value">
<AnimatedNumber :value="Number(userInfo.hisMeny) || 0" />
</div>
</div>
</div>
</div>
<!-- 账户表格 -->
<div class="table-container" v-loading="tableLoading">
<CustomerListTable :table-data="tableData" :columns="tableColumnsOptions" row-key="tradeAccount"
:default-sort="{ prop: 'addTime', order: 'descending' }">
</CustomerListTable>
</div>
</div>
</el-drawer>
</template>
<script setup lang="ts">
defineOptions({
name: 'Default'
})
import { ref, watch, computed } from 'vue'
import { ElAvatar, ElIcon, ElDrawer, ElButton, ElMessage } from 'element-plus'
import { UserFilled, Phone, Message, Money, Wallet, Clock, CreditCard } from '@element-plus/icons-vue'
import CustomerListTable from '@/components/common/Table.vue'
import { getCustomerDetailApi } from '@/api/modules/agent/customerDetail'
import dayjs from 'dayjs'
import AnimatedNumber from './AnimatedNumber.vue'
// 类型定义
import type {
RoleType,
CustomerUserInfo,
Account,
CustomerDetailResponse,
UserDisplayInfo,
TableDataRow,
TableColumn
} from '@/api/types/agent'
const roleMap: Record<RoleType, string> = {
1: '平台',
2: '承包商',
3: '特殊做市商',
4: '普通做市商',
5: '代理',
6: '用户',
}
// 接收父组件传值
const props = defineProps<{
drawerVisible: boolean
queryId: string | number
drawerTitle: string
}>()
// 抛出事件
const emit = defineEmits<{
'update:drawerVisible': [value: boolean]
}>()
// =============== 修复核心computed 中转 v-model ===============
const innerVisible = computed({
get() {
return props.drawerVisible
},
set(val) {
emit('update:drawerVisible', val)
}
})
// 状态
const loading = ref(false)
const tableLoading = ref(false)
// 客户信息
const userInfo = ref<UserDisplayInfo>({
name: '',
id: '',
role: '',
phone: '',
email: '',
deposit: 0,
balance: 0,
registerTime: '',
hisMeny: 0,
})
// 表格
const tableData = ref<TableDataRow[]>([])
const tableColumnsOptions = ref<TableColumn[]>([
{ label: '交易账号', prop: 'tradeAccount' },
{ label: '账户名称', prop: 'accountName' },
{ label: '余额', prop: 'balance', sortable: true },
{ label: '交易杠杆', prop: 'leverage' },
{ label: '历史交易量', prop: 'tradeVolume' },
{ label: '添加时间', prop: 'addTime', width: 180 }
])
// ===================== 加载详情数据 =====================
const loadDetailData = async (id: string | number) => {
loading.value = true
tableLoading.value = true
try {
const res: CustomerDetailResponse = await getCustomerDetailApi({ user_id: id });
const { accounts, user } = res;
userInfo.value = {
name: user.email || user.mobile || '-',
id: user.id,
role: roleMap[user.role_id] || '-',
phone: user.mobile || '-',
email: user.email || '-',
balance: Number(user.capital_amount) || 0,
deposit: Number(user.total_recharge_amount) || 0,
registerTime: dayjs(user.created_at).format('YYYY-MM-DD HH:mm:ss'),
hisMeny: Number(user.history_transfer_amount) || 0,
}
// 处理账户列表数据
tableData.value = accounts.map((item: Account): TableDataRow => ({
tradeAccount: item.account_id,
accountName: item.account_name,
balance: Number(item.wallets_trade.amount) || 0,
leverage: Number(item.wallets_trade.lever) || 0,
tradeVolume: Number(item.collect_data) || 0,
addTime: dayjs(item.created_at).format('YYYY-MM-DD HH:mm:ss'),
}))
} catch (err) {
console.error('加载详情失败:', err);
ElMessage.error('加载详情失败')
} finally {
loading.value = false
tableLoading.value = false
}
}
// ===================== 监听 ID 自动加载数据 =====================
watch(
() => props.queryId,
async (id) => {
if (!id) return
await loadDetailData(id)
},
{ immediate: true }
)
// 账户操作
const handleAccount = (row: TableDataRow) => {
ElMessage.success('操作账户:' + row.tradeAccount)
}
</script>
<style scoped lang="scss">
.drawer-content {
padding: 0px 32px 28px 32px;
}
.user-top {
display: flex;
gap: 24px;
margin-bottom: 32px;
flex-wrap: wrap;
}
.left-card {
display: flex;
align-items: center;
gap: 20px;
padding: 20px 32px;
background: #fafbfc;
border-radius: 16px;
border: 1px solid #f0f2f5;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
min-width: 300px;
.avatar-wrapper {
width: 80px;
height: 80px;
border-radius: 50%;
background: #f2f3f5;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
.avatar-image {
width: 100%;
height: 100%;
object-fit: cover;
}
.avatar-icon {
font-size: 48px;
color: #909399;
}
}
}
.left-text {
display: flex;
flex-direction: column;
gap: 6px;
font-size: 15px;
font-weight: 500;
color: #1d2129;
}
.left-text p {
margin: 0;
line-height: 1.4;
}
.right-card {
flex: 1;
display: flex;
flex-wrap: wrap;
gap: 28px 36px;
padding: 24px 32px;
background: #fafbfc;
border-radius: 16px;
border: 1px solid #f0f2f5;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
align-items: flex-start;
}
.item {
display: flex;
flex-direction: column;
gap: 6px;
min-width: 140px;
}
.item-label {
display: flex;
align-items: center;
gap: 8px;
font-size: 13px;
color: #86909c;
}
.item-label :deep(.el-icon) {
font-size: 16px;
color: #86909c;
}
.item-value {
font-size: 14px;
font-weight: 500;
color: #1d2129;
}
.text-highlight {
color: #1677ff;
font-weight: 600;
}
.table-container {
width: 100%;
height: auto;
margin-top: 10px;
}
</style>