| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322 |
- <template>
- <section class="stats-section">
- <!-- 顶部数据卡片 -->
- <div class="stats-cards">
- <div :style="{ background: item.background }" class="card" v-for="(item, index) in statsList" :key="index">
- <div class="card-icon">
- <img width="76" :src="item.icon" :alt="item.label" />
- </div>
- <div class="card-content">
- <div :style="{ background: item.color }" class="card-bg"></div>
- <div class="value">{{ formatNumber(item.value) }}</div>
- <div class="label">{{ item.label }}</div>
- </div>
- </div>
- </div>
- <!-- 底部趋势图 -->
- <div class="chart-section">
- <h3 class="chart-title">24小时攻击趋势图</h3>
- <div class="chart-container">
- <client-only>
- <v-chart class="chart" :option="chartOption" autoresize />
- </client-only>
- </div>
- </div>
- </section>
- </template>
- <script setup>
- import { computed, ref, onMounted } from 'vue'
- import { useStatsStore } from '~/stores/stats'
- import { storeToRefs } from 'pinia'
- import { use } from 'echarts/core'
- import { CanvasRenderer } from 'echarts/renderers'
- import { LineChart } from 'echarts/charts'
- import {
- GridComponent,
- TooltipComponent,
- LegendComponent,
- TitleComponent
- } from 'echarts/components'
- import VChart from 'vue-echarts'
- import * as echarts from 'echarts/core'
- // 注册 ECharts 组件
- use([
- CanvasRenderer,
- LineChart,
- GridComponent,
- TooltipComponent,
- LegendComponent,
- TitleComponent
- ])
- // 图标资源 (使用 import 引入以确保 Vite 正确处理路径)
- import iconDDoS from '~/assets/svg/home/an1.svg'
- import iconCC from '~/assets/svg/home/an2.svg'
- import iconWAF from '~/assets/svg/home/an3.svg'
- const statsStore = useStatsStore()
- const { stats } = storeToRefs(statsStore)
- // 格式化数字
- const formatNumber = (num) => {
- return num?.toLocaleString() || '0'
- }
- // 统计数据列表
- const statsList = computed(() => [
- { label: '今日 DDoS 攻击峰值', value: stats.value.ddosPeak, icon: iconDDoS, color: '#7D46FF', background: 'linear-gradient(181deg, rgba(130, 77, 255, 0.60) -50.14%, rgba(164, 125, 255, 0.00) 81.35%)' },
- { label: '今日 CC 攻击次数', value: stats.value.ccAttacks, icon: iconCC, color: '#6971FF', background: 'linear-gradient(181deg, rgba(164, 169, 255, 0.60) -50.14%, rgba(56, 66, 255, 1.00) 81.35%)' },
- { label: '今日 WAF 拦截次数', value: stats.value.wafBlocks, icon: iconWAF, color: '#9466FF', background: 'linear-gradient(181deg, rgba(130, 77, 255, 0.60) -50.14%, rgba(164, 125, 255, 0.00) 81.35%)' }
- ])
- // 启动自动增长
- onMounted(() => {
- statsStore.startAutoIncrement()
- })
- // 生成 24小时 Mock 数据
- const generateData = () => {
- const hours = ['00:00', '02:00', '04:00', '06:00', '08:00', '10:00', '12:00', '14:00', '16:00', '18:00', '20:00', '22:00']
- const seriesData1 = [] // DDoS
- const seriesData2 = [] // CC
- const seriesData3 = [] // WAF
- for (let i = 0; i < hours.length; i++) {
- // 使用正弦波+随机数模拟波浪效果
- seriesData1.push(Math.floor(30000 + Math.sin(i / 2) * 10000 + Math.random() * 5000))
- seriesData2.push(Math.floor(40000 + Math.sin(i / 2 + 1) * 15000 + Math.random() * 5000))
- seriesData3.push(Math.floor(20000 + Math.sin(i / 2 + 2) * 8000 + Math.random() * 5000))
- }
- return { hours, seriesData1, seriesData2, seriesData3 }
- }
- const { hours, seriesData1, seriesData2, seriesData3 } = generateData()
- // ECharts 配置
- const chartOption = computed(() => ({
- backgroundColor: 'transparent',
- tooltip: {
- trigger: 'axis',
- backgroundColor: 'linear-gradient(181deg, rgba(101, 70, 255, 0.40) -42.03%, rgba(101, 70, 255, 0.10) 107.46%)',
- borderColor: '#C6BAFF',
- borderRadius: 24,
- textStyle: {
- color: '#fff'
- },
- axisPointer: {
- type: 'line',
- lineStyle: {
- color: '#ffffff',
- type: 'dashed',
- opacity: 0.5
- }
- }
- },
- grid: {
- left: '3%',
- right: '4%',
- bottom: '3%',
- containLabel: true
- },
- xAxis: {
- type: 'category',
- boundaryGap: false,
- data: hours,
- axisLine: {
- lineStyle: {
- color: 'rgba(255, 255, 255, 0.2)'
- }
- },
- axisLabel: {
- color: 'rgba(255, 255, 255, 0.5)',
- fontSize: 12
- }
- },
- yAxis: {
- type: 'value',
- splitLine: {
- lineStyle: {
- color: 'rgba(255, 255, 255, 0.05)',
- type: 'dashed'
- }
- },
- axisLabel: {
- color: 'rgba(255, 255, 255, 0.5)'
- }
- },
- series: [
- {
- name: 'DDoS攻击',
- type: 'line',
- smooth: true,
- showSymbol: false,
- lineStyle: {
- width: 3,
- color: '#7D46FF'
- },
- itemStyle: {
- color: '#7D46FF'
- },
- areaStyle: {
- opacity: 0.3,
- color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
- { offset: 0, color: 'rgba(125, 70, 255, 0.5)' },
- { offset: 1, color: 'rgba(3, 0, 20, 0)' }
- ])
- },
- data: seriesData1
- },
- {
- name: 'CC攻击',
- type: 'line',
- smooth: true,
- showSymbol: false,
- lineStyle: {
- width: 3,
- color: '#4B54FF'
- },
- itemStyle: {
- color: '#4B54FF'
- },
- areaStyle: {
- opacity: 0.3,
- color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
- { offset: 0, color: 'rgba(75, 84, 255, 0.5)' },
- { offset: 1, color: 'rgba(3, 0, 20, 0)' }
- ])
- },
- data: seriesData2
- },
- {
- name: 'WAF拦截',
- type: 'line',
- smooth: true,
- showSymbol: false,
- lineStyle: {
- width: 3,
- color: '#AE8CFF'
- },
- itemStyle: {
- color: '#AE8CFF'
- },
- areaStyle: {
- opacity: 0.3,
- color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
- { offset: 0, color: 'rgba(174, 140, 255, 0.5)' },
- { offset: 1, color: 'rgba(3, 0, 20, 0)' }
- ])
- },
- data: seriesData3
- }
- ]
- }))
- </script>
- <style scoped lang="scss">
- .stats-section {
- width: 100%;
- max-width: 1200px;
- margin: 60px auto;
- padding: 0 20px;
- color: #fff;
- }
- .stats-cards {
- display: flex;
- justify-content: space-between;
- max-width: 1200px;
- box-sizing: border-box;
- gap: 30px;
- margin-bottom: 60px;
- .card {
- flex: 1;
- position: relative;
- width: 380px;
- height: 180px;
- background: rgba(255, 255, 255, 0.03);
- border-radius: 20px;
- transition: transform 0.3s ease;
- backdrop-filter: blur(1px);
- background: linear-gradient(181deg, rgba(130, 77, 255, 0.60) -50.14%, rgba(164, 125, 255, 0.00) 81.35%);
- .card-bg {
- position: absolute;
- left: 50%;
- top: 0;
- transform: translate(-50%, -50%);
- width: 152px;
- height: 106px;
- // background: #9466FF; // 已通过内联样式动态绑定
- filter: blur(25px);
- border-radius: 50%;
- }
- .card-content {
- position: relative;
- z-index: 1;
- height: 100%;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- text-align: center;
- overflow: hidden;
- }
- .card-icon {
- position: absolute;
- left: 50%;
- top: 0;
- transform: translate(-50%, -50%);
- border-radius: 100px;
- border: 1px solid rgba(64, 64, 64, 0.50);
- background: rgba(255, 255, 255, 0.10);
- box-shadow: -20px 68px 20px 0 rgba(0, 0, 0, 0.00), -13px 43px 18px 0 rgba(0, 0, 0, 0.01), -7px 24px 15px 0 rgba(0, 0, 0, 0.04), -3px 11px 11px 0 rgba(0, 0, 0, 0.07), -1px 3px 6px 0 rgba(0, 0, 0, 0.08);
- backdrop-filter: blur(7.5px);
- z-index: 2;
- }
- .value {
- color: #FFF;
- font-size: 60px;
- font-weight: 700;
- line-height: 60px;
- }
- .label {
- margin-top: 20px;
- color: #C6BAFF;
- font-size: 24px;
- font-weight: 400;
- line-height: 24px;
- }
- }
- }
- .chart-section {
- text-align: center;
- .chart-title {
- font-size: 30px;
- font-weight: 400;
- margin-bottom: 40px;
- letter-spacing: 1px;
- color: #FFF;
- }
- .chart-container {
- width: 100%;
- height: 400px;
- position: relative;
- .chart {
- width: 100%;
- height: 100%;
- }
- }
- }
- </style>
|