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>