| <template> |
| <div> |
| <el-row :gutter="16" style="margin-bottom: 20px;"> |
| <el-col :span="6" v-for="card in riskCards" :key="card.label"> |
| <el-card shadow="never" style="border: 1px solid var(--border-color);"> |
| <div style="font-size: 12px; color: var(--text-secondary); margin-bottom: 8px;">{{ card.label }}</div> |
| <div :style="{ fontSize: '22px', fontWeight: 700, color: card.color }">{{ card.value }}</div> |
| </el-card> |
| </el-col> |
| </el-row> |
| |
| <el-row :gutter="16"> |
| <el-col :span="12"> |
| <el-card shadow="never" style="border: 1px solid var(--border-color);"> |
| <template #header><span style="font-weight: 600;">资金使用率</span></template> |
| <v-chart :option="marginChart" autoresize style="height: 280px;" /> |
| </el-card> |
| </el-col> |
| <el-col :span="12"> |
| <el-card shadow="never" style="border: 1px solid var(--border-color);"> |
| <template #header><span style="font-weight: 600;">风控规则</span></template> |
| <div style="padding: 12px;"> |
| <div v-for="rule in riskRules" :key="rule.name" |
| style="display: flex; justify-content: space-between; padding: 12px 0; border-bottom: 1px solid var(--border-color);"> |
| <div> |
| <div style="font-weight: 600; margin-bottom: 4px;">{{ rule.name }}</div> |
| <div style="font-size: 12px; color: var(--text-secondary);">{{ rule.desc }}</div> |
| </div> |
| <el-tag :type="rule.ok ? 'success' : 'danger'" effect="dark">{{ rule.ok ? '正常' : '预警' }}</el-tag> |
| </div> |
| </div> |
| </el-card> |
| </el-col> |
| </el-row> |
| </div> |
| </template> |
| |
| <script setup> |
| import { ref, computed, onMounted } from 'vue' |
| import VChart from 'vue-echarts' |
| import { use } from 'echarts/core' |
| import { GaugeChart } from 'echarts/charts' |
| import { TooltipComponent } from 'echarts/components' |
| import { CanvasRenderer } from 'echarts/renderers' |
| import { riskApi } from '../api' |
| |
| use([GaugeChart, TooltipComponent, CanvasRenderer]) |
| |
| const metrics = ref({}) |
| |
| async function loadMetrics() { |
| try { |
| const { data } = await riskApi.getMetrics() |
| metrics.value = data |
| } catch (e) { console.error(e) } |
| } |
| |
| const riskCards = computed(() => [ |
| { label: '总资产', value: '¥' + fmt(metrics.value.total_balance), color: 'var(--accent-blue)' }, |
| { label: '可用资金', value: '¥' + fmt(metrics.value.available_balance), color: 'var(--accent-purple)' }, |
| { label: '风险使用率', value: (metrics.value.risk_usage_percent || 0).toFixed(1) + '%', color: (metrics.value.risk_usage_percent || 0) > 80 ? 'var(--accent-red)' : 'var(--accent-green)' }, |
| { label: '持仓数', value: '' + (metrics.value.position_count || 0), color: 'var(--accent-yellow)' }, |
| ]) |
| |
| const riskRules = computed(() => [ |
| { name: '单笔最大持仓', desc: `最大 ${metrics.value.max_position_size || 100} 手`, ok: true }, |
| { name: '风险限额', desc: `总资产的 2%`, ok: (metrics.value.risk_usage_percent || 0) < 80 }, |
| { name: '保证金充足率', desc: '可用资金需大于0', ok: (metrics.value.available_balance || 0) > 0 }, |
| { name: '最大亏损限制', desc: '浮动亏损不超过风险限额', ok: (metrics.value.risk_usage_percent || 0) < 100 }, |
| ]) |
| |
| const marginChart = computed(() => ({ |
| backgroundColor: 'transparent', |
| series: [{ |
| type: 'gauge', |
| startAngle: 200, endAngle: -20, min: 0, max: 100, |
| progress: { show: true, width: 16, itemStyle: { color: (metrics.value.risk_usage_percent || 0) > 80 ? '#f85149' : '#3fb950' } }, |
| pointer: { show: false }, |
| axisLine: { lineStyle: { width: 16, color: [[1, '#30363d']] } }, |
| axisTick: { show: false }, |
| splitLine: { show: false }, |
| axisLabel: { color: '#8b949e', distance: 24, fontSize: 11 }, |
| detail: { valueAnimation: true, formatter: '{value}%', color: '#e6edf3', fontSize: 28, offsetCenter: [0, '20%'] }, |
| title: { offsetCenter: [0, '50%'], color: '#8b949e', fontSize: 14 }, |
| data: [{ value: (metrics.value.risk_usage_percent || 0).toFixed(1), name: '风险使用率' }], |
| }], |
| })) |
| |
| function fmt(n) { return (n || 0).toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) } |
| |
| onMounted(loadMetrics) |
| </script> |
| |