删除风险管理
This commit is contained in:
@@ -69,11 +69,6 @@
|
||||
<template #title>匹配管理</template>
|
||||
</el-menu-item> -->
|
||||
|
||||
<el-menu-item v-if="userStore.isAdmin" index="/risk-management">
|
||||
<el-icon><Warning /></el-icon>
|
||||
<template #title>风险管理</template>
|
||||
</el-menu-item>
|
||||
|
||||
<el-menu-item v-if="userStore.isAdmin" index="/agents">
|
||||
<el-icon><Avatar /></el-icon>
|
||||
<template #title>代理管理</template>
|
||||
|
||||
@@ -152,16 +152,6 @@ const routes = [
|
||||
// requiresAdmin: true
|
||||
// }
|
||||
// },
|
||||
{
|
||||
path: 'risk-management',
|
||||
name: 'RiskManagement',
|
||||
component: () => import('@/views/RiskManagement.vue'),
|
||||
meta: {
|
||||
title: '风险管理 - 炬融圈',
|
||||
icon: 'Warning',
|
||||
requiresAdmin: true
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'agents',
|
||||
name: 'Agents',
|
||||
|
||||
@@ -1,450 +0,0 @@
|
||||
<template>
|
||||
<div class="risk-management">
|
||||
<div class="page-header">
|
||||
<h1>风险管理</h1>
|
||||
<div class="header-actions">
|
||||
<el-button @click="checkTimeouts" :loading="checking" type="primary">
|
||||
<el-icon><Refresh /></el-icon>
|
||||
手动检查超时
|
||||
</el-button>
|
||||
<el-button @click="loadData" :loading="loading">
|
||||
<el-icon><Refresh /></el-icon>
|
||||
刷新数据
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 统计卡片 -->
|
||||
<div class="stats-cards">
|
||||
<el-card class="stat-card">
|
||||
<div class="stat-content">
|
||||
<div class="stat-number">{{ stats?.totalRiskUsers || 0 }}</div>
|
||||
<div class="stat-label">风险用户</div>
|
||||
</div>
|
||||
</el-card>
|
||||
<el-card class="stat-card">
|
||||
<div class="stat-content">
|
||||
<div class="stat-number">{{ stats?.totalBlacklistedUsers || 0 }}</div>
|
||||
<div class="stat-label">拉黑用户</div>
|
||||
</div>
|
||||
</el-card>
|
||||
<el-card class="stat-card">
|
||||
<div class="stat-content">
|
||||
<div class="stat-number">{{ stats?.totalOverdueTransfers || 0 }}</div>
|
||||
<div class="stat-label">超时转账</div>
|
||||
</div>
|
||||
</el-card>
|
||||
<el-card class="stat-card">
|
||||
<div class="stat-content">
|
||||
<div class="stat-number">{{ stats?.todayOverdueTransfers || 0 }}</div>
|
||||
<div class="stat-label">今日超时</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<!-- 标签页 -->
|
||||
<el-tabs v-model="activeTab" class="risk-tabs">
|
||||
<!-- 风险用户 -->
|
||||
<el-tab-pane label="风险用户" name="riskUsers">
|
||||
<div class="table-container">
|
||||
<el-table :data="riskUsers" v-loading="loading" stripe>
|
||||
<el-table-column prop="id" label="用户ID" width="80" />
|
||||
<el-table-column prop="username" label="用户名" width="120" />
|
||||
<el-table-column prop="real_name" label="真实姓名" width="120" />
|
||||
<el-table-column prop="phone" label="手机号" width="130" />
|
||||
<el-table-column prop="risk_reason" label="风险原因" min-width="200" />
|
||||
<el-table-column prop="created_at" label="标记时间" width="180">
|
||||
<template #default="{ row }">
|
||||
{{ formatDate(row.created_at) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="100">
|
||||
<template #default="{ row }">
|
||||
<el-tag v-if="row.is_blacklisted" type="danger">已拉黑</el-tag>
|
||||
<el-tag v-else type="warning">风险用户</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="200">
|
||||
<template #default="{ row }">
|
||||
<el-button
|
||||
v-if="!row.is_blacklisted"
|
||||
@click="blacklistUser(row)"
|
||||
type="danger"
|
||||
size="small"
|
||||
>
|
||||
拉黑
|
||||
</el-button>
|
||||
<el-button
|
||||
v-else
|
||||
@click="unblacklistUser(row)"
|
||||
type="success"
|
||||
size="small"
|
||||
>
|
||||
解除拉黑
|
||||
</el-button>
|
||||
<el-button @click="viewUserDetail(row)" size="small">
|
||||
查看详情
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<!-- 超时转账 -->
|
||||
<el-tab-pane label="超时转账" name="overdueTransfers">
|
||||
<div class="table-container">
|
||||
<el-table :data="overdueTransfers" v-loading="loading" stripe>
|
||||
<el-table-column prop="id" label="转账ID" width="80" />
|
||||
<el-table-column prop="from_username" label="发送方" width="120" />
|
||||
<el-table-column prop="to_username" label="接收方" width="120" />
|
||||
<el-table-column prop="amount" label="金额" width="100">
|
||||
<template #default="{ row }">
|
||||
¥{{ row.amount }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="deadline_at" label="截止时间" width="180">
|
||||
<template #default="{ row }">
|
||||
{{ formatDate(row.deadline_at) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="overdue_at" label="超时时间" width="180">
|
||||
<template #default="{ row }">
|
||||
{{ formatDate(row.overdue_at) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" width="100">
|
||||
<template #default="{ row }">
|
||||
<el-tag type="danger">已超时</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="120">
|
||||
<template #default="{ row }">
|
||||
<el-button @click="viewTransferDetail(row)" size="small">
|
||||
查看详情
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<!-- 用户详情对话框 -->
|
||||
<el-dialog v-model="userDetailVisible" title="用户详情" width="600px">
|
||||
<div v-if="selectedUser" class="user-detail">
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="用户ID">{{ selectedUser.id }}</el-descriptions-item>
|
||||
<el-descriptions-item label="用户名">{{ selectedUser.username }}</el-descriptions-item>
|
||||
<el-descriptions-item label="真实姓名">{{ selectedUser.real_name || '未设置' }}</el-descriptions-item>
|
||||
<el-descriptions-item label="手机号">{{ selectedUser.phone || '未设置' }}</el-descriptions-item>
|
||||
|
||||
<el-descriptions-item label="余额">¥{{ selectedUser.balance }}</el-descriptions-item>
|
||||
<el-descriptions-item label="风险原因" :span="2">{{ selectedUser.risk_reason }}</el-descriptions-item>
|
||||
<el-descriptions-item label="拉黑原因" :span="2" v-if="selectedUser.blacklist_reason">
|
||||
{{ selectedUser.blacklist_reason }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="注册时间">{{ formatDate(selectedUser.created_at) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="拉黑时间" v-if="selectedUser.blacklisted_at">
|
||||
{{ formatDate(selectedUser.blacklisted_at) }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 转账详情对话框 -->
|
||||
<el-dialog v-model="transferDetailVisible" title="转账详情" width="600px">
|
||||
<div v-if="selectedTransfer" class="transfer-detail">
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="转账ID">{{ selectedTransfer.id }}</el-descriptions-item>
|
||||
<el-descriptions-item label="金额">¥{{ selectedTransfer.amount }}</el-descriptions-item>
|
||||
<el-descriptions-item label="发送方">{{ selectedTransfer.from_username }}</el-descriptions-item>
|
||||
<el-descriptions-item label="接收方">{{ selectedTransfer.to_username }}</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间">{{ formatDate(selectedTransfer.created_at) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="截止时间">{{ formatDate(selectedTransfer.deadline_at) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="超时时间">{{ formatDate(selectedTransfer.overdue_at) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="状态">
|
||||
<el-tag type="danger">已超时</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="描述" :span="2">
|
||||
{{ selectedTransfer.description || '无' }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { Refresh } from '@element-plus/icons-vue'
|
||||
import api from '../utils/api'
|
||||
|
||||
const loading = ref(false)
|
||||
const checking = ref(false)
|
||||
const activeTab = ref('riskUsers')
|
||||
const riskUsers = ref([])
|
||||
const overdueTransfers = ref([])
|
||||
const stats = ref({
|
||||
totalRiskUsers: 0,
|
||||
totalBlacklistedUsers: 0,
|
||||
totalOverdueTransfers: 0,
|
||||
todayOverdueTransfers: 0
|
||||
})
|
||||
|
||||
const userDetailVisible = ref(false)
|
||||
const transferDetailVisible = ref(false)
|
||||
const selectedUser = ref(null)
|
||||
const selectedTransfer = ref(null)
|
||||
|
||||
// 加载数据
|
||||
const loadData = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
await Promise.all([
|
||||
loadRiskUsers(),
|
||||
loadOverdueTransfers(),
|
||||
loadStats()
|
||||
])
|
||||
} catch (error) {
|
||||
console.error('加载数据失败:', error)
|
||||
ElMessage.error('加载数据失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载风险用户
|
||||
*/
|
||||
const loadRiskUsers = async () => {
|
||||
try {
|
||||
const response = await api.get('/risk/users')
|
||||
if (response.data && Array.isArray(response.data.data.users)) {
|
||||
riskUsers.value = response.data.data.users
|
||||
} else {
|
||||
riskUsers.value = []
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载风险用户失败:', error)
|
||||
// 确保即使出错也有默认空数组
|
||||
riskUsers.value = []
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载超时转账
|
||||
*/
|
||||
const loadOverdueTransfers = async () => {
|
||||
try {
|
||||
const response = await api.get('/risk/overdue-transfers')
|
||||
if (response.data && Array.isArray(response.data.data.transfers)) {
|
||||
overdueTransfers.value = response.data.data.transfers
|
||||
} else {
|
||||
overdueTransfers.value = []
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载超时转账失败:', error)
|
||||
// 确保即使出错也有默认空数组
|
||||
overdueTransfers.value = []
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载统计数据
|
||||
*/
|
||||
const loadStats = async () => {
|
||||
try {
|
||||
const response = await api.get('/risk/stats')
|
||||
if (response.data && response.data.data) {
|
||||
stats.value = {
|
||||
totalRiskUsers: response.data.data.riskUsersCount || 0,
|
||||
totalBlacklistedUsers: response.data.data.blacklistedUsersCount || 0,
|
||||
totalOverdueTransfers: response.data.data.overdueTransfersCount || 0,
|
||||
todayOverdueTransfers: response.data.data.todayOverdueCount || 0
|
||||
}
|
||||
} else {
|
||||
// 如果响应数据格式不正确,使用默认值
|
||||
stats.value = {
|
||||
totalRiskUsers: 0,
|
||||
totalBlacklistedUsers: 0,
|
||||
totalOverdueTransfers: 0,
|
||||
todayOverdueTransfers: 0
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载统计数据失败:', error)
|
||||
// 确保即使出错也有默认值
|
||||
stats.value = {
|
||||
totalRiskUsers: 0,
|
||||
totalBlacklistedUsers: 0,
|
||||
totalOverdueTransfers: 0,
|
||||
todayOverdueTransfers: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 手动检查超时
|
||||
const checkTimeouts = async () => {
|
||||
checking.value = true
|
||||
try {
|
||||
await api.post('/risk/check-timeouts')
|
||||
ElMessage.success('检查完成')
|
||||
await loadData()
|
||||
} catch (error) {
|
||||
console.error('检查超时失败:', error)
|
||||
ElMessage.error('检查超时失败')
|
||||
} finally {
|
||||
checking.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 拉黑用户
|
||||
const blacklistUser = async (user) => {
|
||||
try {
|
||||
const { value: reason } = await ElMessageBox.prompt(
|
||||
'请输入拉黑原因',
|
||||
'拉黑用户',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
inputPattern: /.+/,
|
||||
inputErrorMessage: '拉黑原因不能为空'
|
||||
}
|
||||
)
|
||||
|
||||
await api.post(`/risk/blacklist/${user.id}`, { reason })
|
||||
ElMessage.success('用户已拉黑')
|
||||
await loadData()
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('拉黑用户失败:', error)
|
||||
ElMessage.error('拉黑用户失败')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 解除拉黑
|
||||
const unblacklistUser = async (user) => {
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
`确定要解除用户 ${user.username} 的拉黑状态吗?`,
|
||||
'解除拉黑',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}
|
||||
)
|
||||
|
||||
await api.post(`/risk/unblacklist/${user.id}`)
|
||||
ElMessage.success('已解除拉黑')
|
||||
await loadData()
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
console.error('解除拉黑失败:', error)
|
||||
ElMessage.error('解除拉黑失败')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 查看用户详情
|
||||
const viewUserDetail = (user) => {
|
||||
selectedUser.value = user
|
||||
userDetailVisible.value = true
|
||||
}
|
||||
|
||||
// 查看转账详情
|
||||
const viewTransferDetail = (transfer) => {
|
||||
selectedTransfer.value = transfer
|
||||
transferDetailVisible.value = true
|
||||
}
|
||||
|
||||
// 格式化日期
|
||||
const formatDate = (dateString) => {
|
||||
if (!dateString) return '-'
|
||||
return new Date(dateString).toLocaleString('zh-CN')
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadData()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.risk-management {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.page-header h1 {
|
||||
margin: 0;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.stats-cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.stat-content {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.stat-number {
|
||||
font-size: 32px;
|
||||
font-weight: bold;
|
||||
color: #409EFF;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 14px;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.risk-tabs {
|
||||
background: white;
|
||||
border-radius: 4px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.table-container {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.user-detail,
|
||||
.transfer-detail {
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
:deep(.el-descriptions__label) {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
:deep(.el-table) {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
:deep(.el-table th) {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user