File size: 4,271 Bytes
590a501 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | <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>
|