343 lines
9.4 KiB
Vue
343 lines
9.4 KiB
Vue
<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> |