ECharts入门学习教程,从入门到精通,综合实战——ECharts数据大屏 - 完整知识点(9)

ECharts数据大屏 - 完整知识点

目录

  1. 数据大屏简介
  2. 数据大屏设计方法
  3. 项目实战:静态大屏制作
  4. 项目实战:动态大屏制作
  5. 本章小结

一、数据大屏简介

1.1 什么是数据大屏

数据大屏是将业务数据通过可视化图表、图形、指标等形式,集中展示在大屏幕上的解决方案。

1.2 数据大屏的特点

特点 说明
可视化 图形化展示数据,直观易懂
实时性 动态刷新,展示最新数据
全景性 多维度、多指标同时展示
交互性 支持鼠标悬停、点击等交互

1.3 适用场景

  • 企业经营管理驾驶舱
  • 智慧城市指挥中心
  • 电商实时交易监控
  • 工厂生产监控大屏
  • 疫情防控数据平台

二、数据大屏设计方法

2.1 设计原则

原则一:清晰优先
html 复制代码
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>清晰优先示例</title>
    <style>
        /* 确保字体清晰可读 */
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        
        body {
            background: #0a0f1a;  /* 深色背景,减少视觉疲劳 */
            font-family: 'Microsoft YaHei', 'PingFang SC', sans-serif;
        }
        
        /* 大屏容器 - 清晰的信息层级 */
        .dashboard {
            display: grid;
            grid-template-columns: repeat(4, 1fr);  /* 四列网格布局 */
            gap: 20px;  /* 图表间距20像素 */
            padding: 20px;
            max-width: 1920px;  /* 适配大屏宽度 */
            margin: 0 auto;
        }
        
        /* 每个图表卡片 - 清晰边框和背景 */
        .chart-card {
            background: rgba(20, 28, 40, 0.8);  /* 半透明深色背景 */
            border-radius: 12px;  /* 圆角边框 */
            padding: 15px;
            border: 1px solid rgba(64, 158, 255, 0.3);  /* 蓝色边框,增加清晰度 */
            backdrop-filter: blur(10px);  /* 背景模糊效果 */
        }
        
        /* 图表标题 - 清晰醒目 */
        .chart-title {
            color: #ffffff;
            font-size: 18px;
            font-weight: bold;
            margin-bottom: 15px;
            padding-left: 12px;
            border-left: 4px solid #409eff;  /* 左侧蓝色装饰条 */
        }
    </style>
</head>
<body>
    <div class="dashboard">
        <div class="chart-card">
            <div class="chart-title">销售额趋势</div>
            <div id="chart1" style="height: 300px;"></div>
        </div>
    </div>
</body>
</html>
原则二:一致性
html 复制代码
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>一致性示例</title>
    <style>
        /* 统一的颜色系统 */
        :root {
            /* 主色调 - 蓝色系 */
            --primary-color: #409eff;
            --primary-light: #66b1ff;
            --primary-dark: #1890ff;
            
            /* 辅助色 - 绿色、橙色、红色 */
            --success-color: #67c23a;
            --warning-color: #e6a23c;
            --danger-color: #f56c6c;
            
            /* 中性色 */
            --text-primary: #ffffff;
            --text-secondary: rgba(255, 255, 255, 0.7);
            --border-color: rgba(255, 255, 255, 0.1);
        }
        
        /* 统一的卡片样式 */
        .card {
            background: linear-gradient(135deg, rgba(32, 42, 58, 0.9), rgba(20, 28, 40, 0.9));
            border-radius: 12px;
            border: 1px solid var(--border-color);
            transition: all 0.3s ease;  /* 统一过渡效果 */
        }
        
        /* 统一的标题样式 */
        .card-title {
            font-size: 16px;
            color: var(--text-primary);
            margin-bottom: 16px;
            position: relative;
        }
        
        /* 统一的数值样式 */
        .card-value {
            font-size: 32px;
            font-weight: bold;
            color: var(--primary-color);
            font-family: 'DIN', 'Arial', sans-serif;
        }
        
        /* 统一的悬停效果 */
        .card:hover {
            transform: translateY(-5px);
            box-shadow: 0 8px 25px rgba(0, 0, 0, 0.3);
            border-color: var(--primary-color);
        }
    </style>
</head>
<body>
    <!-- 一致性体现在所有卡片使用相同的样式类 -->
    <div class="card">
        <div class="card-title">总销售额</div>
        <div class="card-value">¥1,234,567</div>
    </div>
</body>
</html>
原则三:层次感
html 复制代码
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>层次感示例</title>
    <style>
        /* 通过视觉重量建立层次 */
        .dashboard-layer {
            display: flex;
            flex-direction: column;
            height: 1080px;
            background: #0a0e27;
        }
        
        /* 顶层 - 最重要的KPI指标 */
        .kpi-layer {
            display: grid;
            grid-template-columns: repeat(4, 1fr);
            gap: 20px;
            padding: 20px;
            height: 180px;  /* 较高,突出重要性 */
        }
        
        .kpi-card {
            background: linear-gradient(135deg, #1a2a3a, #0f1a24);
            border-radius: 16px;
            padding: 20px;
            border-top: 4px solid #409eff;  /* 顶部强调色条 */
            box-shadow: 0 8px 20px rgba(0,0,0,0.3);
        }
        
        .kpi-value {
            font-size: 48px;  /* 最大字体,最醒目 */
            font-weight: bold;
            color: #409eff;
            text-shadow: 0 0 10px rgba(64,158,255,0.5);
        }
        
        /* 中间层 - 主要图表 */
        .main-charts-layer {
            display: grid;
            grid-template-columns: 2fr 1fr;  /* 主图表占2/3,副图表占1/3 */
            gap: 20px;
            padding: 0 20px;
            height: 500px;
        }
        
        .main-chart {
            background: rgba(30, 40, 55, 0.6);
            border-radius: 12px;
            padding: 15px;
        }
        
        /* 底层 - 辅助信息 */
        .sub-info-layer {
            display: grid;
            grid-template-columns: repeat(3, 1fr);
            gap: 20px;
            padding: 20px;
            height: 300px;
        }
        
        .sub-card {
            background: rgba(30, 40, 55, 0.4);  /* 透明度更低,视觉重量轻 */
            border-radius: 8px;
            padding: 12px;
        }
        
        .sub-value {
            font-size: 20px;  /* 较小字体 */
            color: rgba(255,255,255,0.7);
        }
    </style>
</head>
<body>
    <div class="dashboard-layer">
        <div class="kpi-layer">
            <div class="kpi-card">
                <div class="kpi-value">1,234</div>
                <div>总订单数</div>
            </div>
        </div>
        <div class="main-charts-layer">
            <div class="main-chart">主图表区域</div>
        </div>
        <div class="sub-info-layer">
            <div class="sub-card">辅助信息区域</div>
        </div>
    </div>
</body>
</html>

2.2 设计流程

需求分析
数据梳理
原型设计
图表选型
界面开发
数据对接
测试优化
部署上线


三、项目实战:静态大屏制作

3.1 准备工作

3.1.1 项目结构
复制代码
echarts-dashboard/
├── index.html          # 主页面
├── css/
│   └── style.css       # 样式文件
├── js/
│   ├── echarts.min.js  # ECharts库
│   ├── data.js         # 静态数据
│   └── charts.js       # 图表配置
└── assets/
    └── images/         # 图片资源
3.1.2 引入ECharts
html 复制代码
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>电商数据大屏</title>
    <!-- 引入ECharts核心库 -->
    <script src="https://cdn.jsdelivr.net/npm/echarts@5.5.0/dist/echarts.min.js"></script>
    <!-- 引入数据文件 -->
    <script src="js/data.js"></script>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        
        body {
            overflow: hidden;  /* 隐藏滚动条,大屏全屏显示 */
            font-family: 'Microsoft YaHei', 'PingFang SC', 'Helvetica Neue', Arial, sans-serif;
        }
        
        /* 大屏主容器 - 1920x1080标准分辨率 */
        .screen {
            width: 1920px;
            height: 1080px;
            background: url('assets/images/bg.jpg') no-repeat center center;
            background-size: cover;  /* 背景图片铺满 */
            position: relative;
        }
        
        /* 头部区域 */
        .header {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 80px;
            background: linear-gradient(180deg, rgba(0,0,0,0.6), transparent);
            display: flex;
            justify-content: center;
            align-items: center;
        }
        
        .title {
            font-size: 36px;
            color: #fff;
            text-shadow: 0 0 10px rgba(64,158,255,0.8);
            letter-spacing: 5px;  /* 字间距 */
        }
        
        /* 图表网格布局 */
        .charts-grid {
            position: absolute;
            top: 100px;
            left: 20px;
            right: 20px;
            bottom: 20px;
            display: grid;
            grid-template-columns: repeat(4, 1fr);
            grid-template-rows: repeat(3, 1fr);
            gap: 15px;
        }
        
        /* 图表卡片通用样式 */
        .chart-box {
            background: rgba(10, 20, 40, 0.6);
            backdrop-filter: blur(8px);
            border-radius: 12px;
            border: 1px solid rgba(64,158,255,0.3);
            padding: 12px;
        }
        
        /* 各图表位置分配 */
        .chart-1 { grid-column: 1 / 3; }  /* 销售额趋势 - 占两列 */
        .chart-2 { grid-column: 3 / 4; }  /* 品类占比 - 占一列 */
        .chart-3 { grid-column: 4 / 5; }  /* 关键指标 - 占一列 */
        .chart-4 { grid-column: 1 / 2; }  /* 区域销售 - 占一列 */
        .chart-5 { grid-column: 2 / 5; }  /* 排行榜 - 占三列 */
        
        .chart-container {
            width: 100%;
            height: calc(100% - 30px);
        }
        
        .chart-title {
            color: #fff;
            font-size: 16px;
            margin-bottom: 10px;
            padding-left: 10px;
            border-left: 3px solid #409eff;
        }
    </style>
</head>
<body>
    <div class="screen">
        <div class="header">
            <div class="title">📊 智慧电商数据大屏</div>
        </div>
        <div class="charts-grid">
            <!-- 图表1: 销售额趋势 -->
            <div class="chart-box chart-1">
                <div class="chart-title">销售额趋势(万元)</div>
                <div id="trendChart" class="chart-container"></div>
            </div>
            <!-- 图表2: 品类占比 -->
            <div class="chart-box chart-2">
                <div class="chart-title">销售额品类占比</div>
                <div id="categoryChart" class="chart-container"></div>
            </div>
            <!-- 图表3: 关键指标 -->
            <div class="chart-box chart-3">
                <div class="chart-title">关键运营指标</div>
                <div id="indicatorChart" class="chart-container"></div>
            </div>
            <!-- 图表4: 区域销售 -->
            <div class="chart-box chart-4">
                <div class="chart-title">区域销售额分布</div>
                <div id="regionChart" class="chart-container"></div>
            </div>
            <!-- 图表5: 热销排行榜 -->
            <div class="chart-box chart-5">
                <div class="chart-title">热销商品TOP10</div>
                <div id="rankChart" class="chart-container"></div>
            </div>
        </div>
    </div>

    <script src="js/charts.js"></script>
</body>
</html>

3.2 图表制作

3.2.1 数据文件 (js/data.js)
javascript 复制代码
/**
 * 电商大屏静态数据
 * 包含所有图表所需的模拟数据
 */

// 销售额趋势数据 - 近12个月
const salesTrendData = {
    months: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
    sales: [320, 280, 350, 410, 480, 620, 710, 850, 920, 1080, 1350, 1680],  // 销售额(万元)
    orders: [3200, 2980, 3650, 4320, 5120, 6580, 7420, 8920, 9650, 11200, 13800, 16500]  // 订单量
};

// 品类销售数据
const categoryData = {
    names: ['手机数码', '服装服饰', '美妆个护', '家用电器', '食品生鲜', '家居用品'],
    values: [850, 620, 430, 380, 290, 210],  // 销售额(万元)
    colors: ['#409eff', '#67c23a', '#e6a23c', '#f56c6c', '#909399', '#9b59b6']
};

// 区域销售数据
const regionData = {
    regions: ['华东', '华南', '华北', '西南', '华中', '东北', '西北'],
    sales: [1250, 980, 760, 540, 620, 380, 290]
};

// 热销商品排行
const productRankData = {
    products: [
        'iPhone 15 Pro Max', '华为 Mate 60 Pro', '小米 14 Ultra', 
        '耐克 Air Max', '雅诗兰黛小棕瓶', '海尔冰箱 BCD-500',
        'Adidas运动鞋', '三只松鼠坚果礼盒', 'SK-II神仙水', '小米扫地机器人'
    ],
    sales: [25800, 22300, 19800, 17500, 15600, 14200, 12800, 11500, 9800, 8600]  // 销量件数
};

// 关键指标数据
const kpiData = {
    totalSales: 1680,      // 本月总销售额(万元)
    totalOrders: 16500,    // 本月总订单数
    avgPrice: 1018,        // 平均客单价(元)
    growth: 24.5,          // 同比增长率(%)
    conversion: 3.8,       // 转化率(%)
    refund: 2.1            // 退款率(%)
};
3.2.2 图表配置文件 (js/charts.js)
javascript 复制代码
/**
 * ECharts图表配置和渲染
 * 包含所有图表的完整配置
 */

// ==================== 等待DOM加载完成 ====================
document.addEventListener('DOMContentLoaded', function() {
    
    // 1. 销售额趋势图 - 折线图 + 柱状图组合
    function renderTrendChart() {
        // 获取图表容器DOM元素
        const chartDom = document.getElementById('trendChart');
        // 初始化ECharts实例
        const myChart = echarts.init(chartDom);
        
        // 配置图表选项
        const option = {
            // 背景色透明(继承父容器)
            backgroundColor: 'transparent',
            // 提示框组件(鼠标悬停显示数据)
            tooltip: {
                trigger: 'axis',  // 坐标轴触发
                axisPointer: { type: 'shadow' },  // 阴影指示器
                backgroundColor: 'rgba(0,0,0,0.8)',
                borderColor: '#409eff',
                textStyle: { color: '#fff' }
            },
            // 图例组件(标识不同系列)
            legend: {
                data: ['销售额(万元)', '订单量(单)'],
                textStyle: { color: '#fff' },
                right: 20,
                top: 0
            },
            // 网格配置(图表绘图区域)
            grid: {
                left: '8%',
                right: '8%',
                top: '15%',
                bottom: '8%',
                containLabel: true
            },
            // X轴配置
            xAxis: {
                type: 'category',  // 类目轴
                data: salesTrendData.months,  // 月份数据
                axisLabel: {
                    color: '#fff',
                    rotate: 0  // 标签旋转角度
                },
                axisLine: {
                    lineStyle: { color: 'rgba(255,255,255,0.3)' }
                },
                axisTick: { show: false }  // 隐藏刻度线
            },
            // Y轴配置(左侧 - 销售额)
            yAxis: [
                {
                    type: 'value',
                    name: '销售额(万元)',
                    nameTextStyle: { color: '#fff' },
                    axisLabel: { color: '#fff' },
                    splitLine: {
                        lineStyle: { color: 'rgba(255,255,255,0.1)' }
                    }
                },
                {
                    type: 'value',
                    name: '订单量(单)',
                    nameTextStyle: { color: '#fff' },
                    axisLabel: { color: '#fff' },
                    splitLine: { show: false }  // 右侧Y轴不显示分割线
                }
            ],
            // 系列数据(图表类型和数据)
            series: [
                {
                    name: '销售额(万元)',
                    type: 'line',  // 折线图
                    data: salesTrendData.sales,
                    smooth: true,  // 平滑曲线
                    symbol: 'circle',  // 标记点形状
                    symbolSize: 8,  // 标记点大小
                    lineStyle: {
                        width: 3,
                        color: '#409eff',
                        shadowBlur: 10,
                        shadowColor: '#409eff'
                    },
                    areaStyle: {  // 面积填充
                        opacity: 0.3,
                        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
                            { offset: 0, color: '#409eff' },
                            { offset: 1, color: 'rgba(64,158,255,0)' }
                        ])
                    },
                    itemStyle: { color: '#409eff' }
                },
                {
                    name: '订单量(单)',
                    type: 'bar',  // 柱状图
                    yAxisIndex: 1,  // 使用右侧Y轴
                    data: salesTrendData.orders,
                    barWidth: '40%',  // 柱子宽度
                    itemStyle: {
                        color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
                            { offset: 0, color: '#67c23a' },
                            { offset: 1, color: '#529b2e' }
                        ]),
                        borderRadius: [4, 4, 0, 0]  // 上边框圆角
                    }
                }
            ]
        };
        
        // 应用配置并渲染图表
        myChart.setOption(option);
        
        // 添加窗口自适应(图表跟随容器大小变化)
        window.addEventListener('resize', () => myChart.resize());
    }
    
    // 2. 品类占比图 - 饼图(带环形效果)
    function renderCategoryChart() {
        const chartDom = document.getElementById('categoryChart');
        const myChart = echarts.init(chartDom);
        
        // 准备饼图数据格式
        const pieData = categoryData.names.map((name, index) => ({
            name: name,
            value: categoryData.values[index],
            itemStyle: { color: categoryData.colors[index] }
        }));
        
        const option = {
            backgroundColor: 'transparent',
            tooltip: {
                trigger: 'item',
                formatter: '{b}: {d}% ({c}万元)',  // 自定义提示格式
                backgroundColor: 'rgba(0,0,0,0.8)'
            },
            legend: {
                orient: 'vertical',  // 垂直排列
                right: 10,
                top: 'center',
                textStyle: { color: '#fff' },
                itemWidth: 12,
                itemHeight: 12,
                formatter: (name) => {
                    // 在图例中显示百分比
                    const total = categoryData.values.reduce((a, b) => a + b, 0);
                    const item = categoryData.names.findIndex(n => n === name);
                    const percent = ((categoryData.values[item] / total) * 100).toFixed(1);
                    return `${name}  ${percent}%`;
                }
            },
            series: [{
                name: '品类销售',
                type: 'pie',  // 饼图
                radius: ['45%', '70%'],  // 内半径和外半径(环形效果)
                center: ['40%', '50%'],  // 圆心位置
                avoidLabelOverlap: false,
                label: {
                    show: true,
                    position: 'outside',
                    formatter: '{b}: {d}%',
                    color: '#fff'
                },
                emphasis: {  // 高亮效果
                    scale: true,
                    scaleSize: 10
                },
                data: pieData,
                itemStyle: {
                    borderRadius: 8,  // 圆角
                    borderColor: '#0a1428',
                    borderWidth: 2
                }
            }]
        };
        
        myChart.setOption(option);
        window.addEventListener('resize', () => myChart.resize());
    }
    
    // 3. 关键指标图 - 仪表盘/进度条效果
    function renderIndicatorChart() {
        const chartDom = document.getElementById('indicatorChart');
        const myChart = echarts.init(chartDom);
        
        // 使用gauge仪表盘显示关键指标
        const option = {
            backgroundColor: 'transparent',
            series: [
                {
                    name: '同比增长率',
                    type: 'gauge',  // 仪表盘
                    center: ['25%', '50%'],
                    radius: '70%',
                    min: 0,
                    max: 50,
                    splitNumber: 5,
                    progress: {
                        show: true,
                        width: 12,
                        itemStyle: { color: '#409eff' }
                    },
                    axisLine: {
                        lineStyle: { width: 12, color: [[0.5, '#67c23a']] }
                    },
                    axisTick: { show: false },
                    splitLine: { show: false },
                    axisLabel: { show: false },
                    pointer: { show: false },
                    detail: {
                        offsetCenter: [0, 20],
                        valueAnimation: true,
                        fontSize: 16,
                        color: '#fff'
                    },
                    title: { show: true, offsetCenter: [0, -20], color: '#fff' },
                    data: [{ value: kpiData.growth, name: '同比增长率%' }]
                },
                {
                    name: '转化率',
                    type: 'gauge',
                    center: ['50%', '50%'],
                    radius: '70%',
                    min: 0,
                    max: 10,
                    progress: {
                        show: true,
                        width: 12,
                        itemStyle: { color: '#67c23a' }
                    },
                    axisLine: { lineStyle: { width: 12 } },
                    axisTick: { show: false },
                    splitLine: { show: false },
                    axisLabel: { show: false },
                    pointer: { show: false },
                    detail: {
                        offsetCenter: [0, 20],
                        valueAnimation: true,
                        fontSize: 16,
                        color: '#fff'
                    },
                    title: { show: true, offsetCenter: [0, -20], color: '#fff' },
                    data: [{ value: kpiData.conversion, name: '转化率%' }]
                },
                {
                    name: '退款率',
                    type: 'gauge',
                    center: ['75%', '50%'],
                    radius: '70%',
                    min: 0,
                    max: 10,
                    progress: {
                        show: true,
                        width: 12,
                        itemStyle: { color: '#f56c6c' }
                    },
                    axisLine: { lineStyle: { width: 12 } },
                    axisTick: { show: false },
                    splitLine: { show: false },
                    axisLabel: { show: false },
                    pointer: { show: false },
                    detail: {
                        offsetCenter: [0, 20],
                        valueAnimation: true,
                        fontSize: 16,
                        color: '#fff'
                    },
                    title: { show: true, offsetCenter: [0, -20], color: '#fff' },
                    data: [{ value: kpiData.refund, name: '退款率%' }]
                }
            ]
        };
        
        myChart.setOption(option);
        window.addEventListener('resize', () => myChart.resize());
    }
    
    // 4. 区域销售额图 - 横向柱状图
    function renderRegionChart() {
        const chartDom = document.getElementById('regionChart');
        const myChart = echarts.init(chartDom);
        
        const option = {
            backgroundColor: 'transparent',
            tooltip: {
                trigger: 'axis',
                axisPointer: { type: 'shadow' },
                formatter: '{b}: {c}万元'
            },
            grid: {
                left: '15%',
                right: '5%',
                containLabel: true
            },
            xAxis: {
                type: 'value',
                name: '销售额(万元)',
                nameTextStyle: { color: '#fff' },
                axisLabel: { color: '#fff' },
                splitLine: { lineStyle: { color: 'rgba(255,255,255,0.1)' } }
            },
            yAxis: {
                type: 'category',
                data: regionData.regions,
                axisLabel: { color: '#fff', fontSize: 12 },
                axisLine: { show: false },
                axisTick: { show: false }
            },
            series: [{
                name: '销售额',
                type: 'bar',
                data: regionData.sales,
                barWidth: '60%',
                label: {
                    show: true,
                    position: 'right',
                    formatter: '{c}万',
                    color: '#fff'
                },
                itemStyle: {
                    color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
                        { offset: 0, color: '#409eff' },
                        { offset: 1, color: '#9b59b6' }
                    ]),
                    borderRadius: [0, 4, 4, 0]
                }
            }]
        };
        
        myChart.setOption(option);
        window.addEventListener('resize', () => myChart.resize());
    }
    
    // 5. 热销排行榜 - 条形图
    function renderRankChart() {
        const chartDom = document.getElementById('rankChart');
        const myChart = echarts.init(chartDom);
        
        // 反转数据以便从上到下显示(第一名在最上面)
        const reversedProducts = [...productRankData.products].reverse();
        const reversedSales = [...productRankData.sales].reverse();
        
        const option = {
            backgroundColor: 'transparent',
            tooltip: {
                trigger: 'axis',
                axisPointer: { type: 'shadow' },
                formatter: '{b}: {c}件'
            },
            grid: {
                left: '20%',
                right: '8%',
                top: '5%',
                bottom: '5%',
                containLabel: true
            },
            xAxis: {
                type: 'value',
                name: '销量(件)',
                nameTextStyle: { color: '#fff' },
                axisLabel: { color: '#fff' },
                splitLine: { lineStyle: { color: 'rgba(255,255,255,0.1)' } }
            },
            yAxis: {
                type: 'category',
                data: reversedProducts,
                axisLabel: {
                    color: '#fff',
                    fontSize: 11,
                    // 商品名称过长时截断
                    formatter: (value) => value.length > 12 ? value.slice(0, 10) + '...' : value
                },
                axisTick: { show: false },
                axisLine: { show: false }
            },
            series: [{
                name: '销量',
                type: 'bar',
                data: reversedSales,
                barWidth: '50%',
                label: {
                    show: true,
                    position: 'right',
                    formatter: '{c}件',
                    color: '#fff',
                    fontSize: 11
                },
                itemStyle: {
                    // 根据排名设置不同颜色(前3名金色、银色、铜色)
                    color: (params) => {
                        const rank = 10 - params.dataIndex;
                        if (rank === 1) return '#ffd700';
                        if (rank === 2) return '#c0c0c0';
                        if (rank === 3) return '#cd7f32';
                        return '#409eff';
                    },
                    borderRadius: [0, 4, 4, 0]
                },
                // 添加动画效果
                animation: true,
                animationDuration: 1000,
                animationEasing: 'cubicOut'
            }]
        };
        
        myChart.setOption(option);
        window.addEventListener('resize', () => myChart.resize());
    }
    
    // 调用所有图表渲染函数
    renderTrendChart();
    renderCategoryChart();
    renderIndicatorChart();
    renderRegionChart();
    renderRankChart();
    
    console.log('所有图表已加载完成');
});

3.3 大屏展示

添加数字滚动动画效果
javascript 复制代码
/**
 * 数字滚动动画组件
 * 为KPI指标添加从0到目标值的滚动效果
 */

class NumberRoller {
    constructor(element, targetValue, duration = 2000) {
        this.element = element;      // DOM元素
        this.targetValue = targetValue;  // 目标数值
        this.duration = duration;    // 动画持续时间(毫秒)
        this.startValue = 0;         // 起始数值
        this.startTime = null;       // 开始时间戳
    }
    
    // 缓动函数 - easeOutCubic
    easeOutCubic(t) {
        return 1 - Math.pow(1 - t, 3);
    }
    
    // 开始动画
    start() {
        this.startTime = performance.now();
        this.update();
    }
    
    // 更新数值
    update() {
        const now = performance.now();
        const elapsed = now - this.startTime;
        let progress = Math.min(elapsed / this.duration, 1);
        progress = this.easeOutCubic(progress);
        
        // 计算当前值
        const currentValue = this.startValue + (this.targetValue - this.startValue) * progress;
        
        // 格式化显示(万元保留1位小数,订单数取整)
        if (this.element.id === 'totalSales') {
            this.element.textContent = currentValue.toFixed(1);
        } else if (this.element.id === 'totalOrders') {
            this.element.textContent = Math.floor(currentValue).toLocaleString();
        } else {
            this.element.textContent = Math.floor(currentValue);
        }
        
        // 继续动画
        if (progress < 1) {
            requestAnimationFrame(() => this.update());
        } else {
            // 动画完成,确保显示最终值
            if (this.element.id === 'totalSales') {
                this.element.textContent = this.targetValue.toFixed(1);
            } else if (this.element.id === 'totalOrders') {
                this.element.textContent = this.targetValue.toLocaleString();
            } else {
                this.element.textContent = this.targetValue;
            }
        }
    }
}

// 在页面中添加KPI卡片并启动动画
function initKPI() {
    // 创建KPI卡片容器
    const kpiContainer = document.createElement('div');
    kpiContainer.className = 'kpi-container';
    kpiContainer.style.cssText = `
        position: absolute;
        top: 100px;
        right: 20px;
        width: 280px;
        background: rgba(10,20,40,0.8);
        backdrop-filter: blur(10px);
        border-radius: 12px;
        padding: 15px;
        border: 1px solid rgba(64,158,255,0.3);
        z-index: 10;
    `;
    
    kpiContainer.innerHTML = `
        <div style="color:#fff; font-size:16px; margin-bottom:15px;">📈 核心KPI指标</div>
        <div style="margin-bottom:12px;">
            <div style="color:rgba(255,255,255,0.7); font-size:12px;">本月销售额</div>
            <div style="font-size:32px; font-weight:bold; color:#409eff;">
                <span id="totalSales">0</span> 万元
            </div>
        </div>
        <div style="margin-bottom:12px;">
            <div style="color:rgba(255,255,255,0.7); font-size:12px;">本月订单量</div>
            <div style="font-size:32px; font-weight:bold; color:#67c23a;">
                <span id="totalOrders">0</span> 单
            </div>
        </div>
        <div style="margin-bottom:12px;">
            <div style="color:rgba(255,255,255,0.7); font-size:12px;">平均客单价</div>
            <div style="font-size:32px; font-weight:bold; color:#e6a23c;">
                ¥<span id="avgPrice">0</span>
            </div>
        </div>
        <div>
            <div style="color:rgba(255,255,255,0.7); font-size:12px;">同比增长</div>
            <div style="font-size:32px; font-weight:bold; color:#f56c6c;">
                <span id="growth">0</span>%
            </div>
        </div>
    `;
    
    document.querySelector('.screen').appendChild(kpiContainer);
    
    // 启动数字滚动动画
    new NumberRoller(document.getElementById('totalSales'), kpiData.totalSales, 2000).start();
    new NumberRoller(document.getElementById('totalOrders'), kpiData.totalOrders, 2000).start();
    new NumberRoller(document.getElementById('avgPrice'), kpiData.avgPrice, 2000).start();
    new NumberRoller(document.getElementById('growth'), kpiData.growth, 2000).start();
}

// 调用KPI初始化(需要在charts.js中调用)
// initKPI();

四、项目实战:动态大屏制作

4.1 准备工作

4.1.1 引入WebSocket和Mock数据
javascript 复制代码
/**
 * 动态数据大屏 - WebSocket连接和数据模拟
 */

// WebSocket连接配置
const WS_CONFIG = {
    url: 'ws://localhost:8080/ws/dashboard',  // WebSocket服务器地址
    reconnectInterval: 3000,  // 重连间隔(毫秒)
    maxReconnectAttempts: 10   // 最大重连次数
};

// 模拟数据生成器(用于演示,实际可从API获取)
class MockDataGenerator {
    constructor() {
        this.interval = null;
    }
    
    // 生成随机销售额(在基础值上下波动)
    generateRandomSales(baseValue = 1000) {
        const change = (Math.random() - 0.5) * 0.1;  // ±5%波动
        return Math.round(baseValue * (1 + change));
    }
    
    // 生成品类数据(保持总和不变)
    generateCategoryData() {
        const total = 2800;  // 总销售额2800万元
        const baseValues = [850, 620, 430, 380, 290, 230];
        // 随机分配,总和保持不变
        let remaining = total;
        const newValues = [];
        
        for (let i = 0; i < baseValues.length - 1; i++) {
            const variance = (Math.random() - 0.5) * 100;  // ±50波动
            let value = baseValues[i] + variance;
            value = Math.max(100, Math.min(remaining - 100, value));
            newValues.push(Math.round(value));
            remaining -= newValues[i];
        }
        newValues.push(Math.round(remaining));
        
        return newValues;
    }
    
    // 生成区域数据
    generateRegionData() {
        const baseRegions = [1250, 980, 760, 540, 620, 380, 290];
        return baseRegions.map(v => {
            const change = (Math.random() - 0.5) * 0.08;
            return Math.max(100, Math.round(v * (1 + change)));
        });
    }
    
    // 生成订单趋势数据(最后一个月更新)
    generateOrderTrend(lastValue = 16500) {
        const change = Math.round((Math.random() - 0.5) * 500);
        return Math.max(10000, lastValue + change);
    }
    
    // 生成完整的仪表盘数据
    generateFullData() {
        return {
            timestamp: new Date().toISOString(),
            salesTrend: {
                months: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
                sales: [320, 280, 350, 410, 480, 620, 710, 850, 920, 1080, 1350, this.generateRandomSales(1680)],
                orders: [3200, 2980, 3650, 4320, 5120, 6580, 7420, 8920, 9650, 11200, 13800, this.generateOrderTrend()]
            },
            category: {
                names: ['手机数码', '服装服饰', '美妆个护', '家用电器', '食品生鲜', '家居用品'],
                values: this.generateCategoryData()
            },
            region: {
                regions: ['华东', '华南', '华北', '西南', '华中', '东北', '西北'],
                sales: this.generateRegionData()
            },
            kpi: {
                totalSales: this.generateRandomSales(1680),
                totalOrders: this.generateOrderTrend(),
                conversion: (3.5 + Math.random() * 1).toFixed(1),
                onlineUsers: Math.floor(500 + Math.random() * 300)
            }
        };
    }
}

4.2 图表制作(动态版)

javascript 复制代码
/**
 * 动态图表管理器
 * 支持WebSocket实时数据更新
 */

class DynamicChartManager {
    constructor() {
        this.charts = {};           // 存储所有图表实例
        this.ws = null;             // WebSocket连接
        this.reconnectAttempts = 0; // 重连次数
        this.isConnected = false;    // 连接状态
    }
    
    // 初始化所有图表
    initCharts() {
        // 销售额趋势图(动态)
        this.charts.trend = echarts.init(document.getElementById('trendChart'));
        this.charts.trend.setOption(this.getTrendOption());
        
        // 品类占比图(动态)
        this.charts.category = echarts.init(document.getElementById('categoryChart'));
        this.charts.category.setOption(this.getCategoryOption());
        
        // 区域销售图(动态)
        this.charts.region = echarts.init(document.getElementById('regionChart'));
        this.charts.region.setOption(this.getRegionOption());
        
        // 实时监控图(新增)
        this.charts.realtime = echarts.init(document.getElementById('realtimeChart'));
        this.charts.realtime.setOption(this.getRealtimeOption());
        
        // 添加窗口自适应
        window.addEventListener('resize', () => {
            Object.values(this.charts).forEach(chart => chart.resize());
        });
    }
    
    // 销售额趋势图配置
    getTrendOption() {
        return {
            backgroundColor: 'transparent',
            tooltip: { trigger: 'axis' },
            legend: {
                data: ['销售额(万元)', '订单量(单)'],
                textStyle: { color: '#fff' }
            },
            xAxis: {
                type: 'category',
                data: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
                axisLabel: { color: '#fff' },
                axisLine: { lineStyle: { color: 'rgba(255,255,255,0.3)' } }
            },
            yAxis: [
                {
                    type: 'value',
                    name: '销售额(万元)',
                    nameTextStyle: { color: '#fff' },
                    axisLabel: { color: '#fff' }
                },
                {
                    type: 'value',
                    name: '订单量(单)',
                    nameTextStyle: { color: '#fff' },
                    axisLabel: { color: '#fff' }
                }
            ],
            series: [
                {
                    name: '销售额(万元)',
                    type: 'line',
                    smooth: true,
                    lineStyle: { width: 3, color: '#409eff' },
                    areaStyle: { opacity: 0.3, color: '#409eff' },
                    data: []
                },
                {
                    name: '订单量(单)',
                    type: 'bar',
                    yAxisIndex: 1,
                    itemStyle: { color: '#67c23a', borderRadius: [4,4,0,0] },
                    data: []
                }
            ]
        };
    }
    
    // 品类占比图配置
    getCategoryOption() {
        return {
            backgroundColor: 'transparent',
            tooltip: { trigger: 'item', formatter: '{b}: {d}%' },
            legend: {
                orient: 'vertical',
                right: 10,
                top: 'center',
                textStyle: { color: '#fff' }
            },
            series: [{
                type: 'pie',
                radius: ['45%', '70%'],
                center: ['40%', '50%'],
                label: { show: true, formatter: '{b}: {d}%', color: '#fff' },
                data: []
            }]
        };
    }
    
    // 区域销售图配置
    getRegionOption() {
        return {
            backgroundColor: 'transparent',
            tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
            grid: { left: '15%' },
            xAxis: {
                type: 'value',
                name: '销售额(万元)',
                nameTextStyle: { color: '#fff' },
                axisLabel: { color: '#fff' }
            },
            yAxis: {
                type: 'category',
                data: ['华东', '华南', '华北', '西南', '华中', '东北', '西北'],
                axisLabel: { color: '#fff' }
            },
            series: [{
                type: 'bar',
                label: { show: true, position: 'right', formatter: '{c}万' },
                itemStyle: {
                    color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
                        { offset: 0, color: '#409eff' },
                        { offset: 1, color: '#9b59b6' }
                    ])
                },
                data: []
            }]
        };
    }
    
    // 实时监控图配置(折线图)
    getRealtimeOption() {
        return {
            backgroundColor: 'transparent',
            title: {
                text: '实时在线用户数',
                textStyle: { color: '#fff', fontSize: 14 }
            },
            tooltip: { trigger: 'axis' },
            xAxis: {
                type: 'category',
                data: [],
                axisLabel: { color: '#fff' }
            },
            yAxis: {
                type: 'value',
                name: '在线人数',
                nameTextStyle: { color: '#fff' },
                axisLabel: { color: '#fff' }
            },
            series: [{
                type: 'line',
                smooth: true,
                lineStyle: { width: 2, color: '#e6a23c' },
                areaStyle: { opacity: 0.3, color: '#e6a23c' },
                data: []
            }]
        };
    }
    
    // 更新图表数据(核心方法)
    updateCharts(data) {
        // 1. 更新趋势图
        if (this.charts.trend) {
            this.charts.trend.setOption({
                series: [
                    { data: data.salesTrend.sales },
                    { data: data.salesTrend.orders }
                ]
            });
        }
        
        // 2. 更新品类图
        if (this.charts.category) {
            const pieData = data.category.names.map((name, idx) => ({
                name: name,
                value: data.category.values[idx]
            }));
            this.charts.category.setOption({ series: [{ data: pieData }] });
        }
        
        // 3. 更新区域图
        if (this.charts.region) {
            this.charts.region.setOption({ series: [{ data: data.region.sales }] });
        }
        
        // 4. 更新KPI指标(数字滚动效果)
        this.updateKPI(data.kpi);
        
        // 5. 更新实时数据
        this.updateRealtimeData(data.kpi.onlineUsers);
        
        // 控制台输出更新时间
        console.log(`[${new Date(data.timestamp).toLocaleTimeString()}] 数据已更新`);
    }
    
    // 更新KPI指标
    updateKPI(kpi) {
        const kpiElements = {
            totalSales: document.getElementById('totalSales'),
            totalOrders: document.getElementById('totalOrders'),
            conversion: document.getElementById('conversion'),
            onlineUsers: document.getElementById('onlineUsers')
        };
        
        if (kpiElements.totalSales) {
            this.animateNumber(kpiElements.totalSales, parseFloat(kpiElements.totalSales.textContent) || 0, kpi.totalSales);
        }
        if (kpiElements.totalOrders) {
            this.animateNumber(kpiElements.totalOrders, parseInt(kpiElements.totalOrders.textContent) || 0, kpi.totalOrders);
        }
        if (kpiElements.conversion) {
            kpiElements.conversion.textContent = kpi.conversion;
        }
        if (kpiElements.onlineUsers) {
            this.animateNumber(kpiElements.onlineUsers, parseInt(kpiElements.onlineUsers.textContent) || 0, kpi.onlineUsers);
        }
    }
    
    // 数字滚动动画
    animateNumber(element, start, end, duration = 1000) {
        const startTime = performance.now();
        const update = (currentTime) => {
            const elapsed = currentTime - startTime;
            const progress = Math.min(elapsed / duration, 1);
            // 缓动函数 easeOutQuad
            const easeProgress = 1 - (1 - progress) * (1 - progress);
            const current = start + (end - start) * easeProgress;
            element.textContent = Math.floor(current).toLocaleString();
            
            if (progress < 1) {
                requestAnimationFrame(update);
            } else {
                element.textContent = end.toLocaleString();
            }
        };
        requestAnimationFrame(update);
    }
    
    // 实时数据队列(用于存储最近30个数据点)
    constructor() {
        // ... 之前的内容
        this.realtimeData = {
            times: [],
            values: []
        };
        this.maxDataPoints = 30;  // 最多保存30个数据点
    }
    
    // 更新实时数据
    updateRealtimeData(onlineUsers) {
        const now = new Date();
        const timeStr = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}`;
        
        // 添加新数据
        this.realtimeData.times.push(timeStr);
        this.realtimeData.values.push(onlineUsers);
        
        // 保持数据点数量不超过最大值
        if (this.realtimeData.times.length > this.maxDataPoints) {
            this.realtimeData.times.shift();
            this.realtimeData.values.shift();
        }
        
        // 更新图表
        if (this.charts.realtime) {
            this.charts.realtime.setOption({
                xAxis: { data: this.realtimeData.times },
                series: [{ data: this.realtimeData.values }]
            });
        }
    }
    
    // WebSocket连接
    connectWebSocket() {
        try {
            this.ws = new WebSocket(WS_CONFIG.url);
            
            // 连接打开事件
            this.ws.onopen = (event) => {
                console.log('WebSocket连接已建立');
                this.isConnected = true;
                this.reconnectAttempts = 0;
                
                // 发送订阅消息(如果需要)
                this.ws.send(JSON.stringify({
                    type: 'subscribe',
                    channel: 'dashboard',
                    action: 'subscribe'
                }));
            };
            
            // 接收消息事件
            this.ws.onmessage = (event) => {
                try {
                    const data = JSON.parse(event.data);
                    this.updateCharts(data);
                } catch (error) {
                    console.error('解析数据失败:', error);
                }
            };
            
            // 连接关闭事件
            this.ws.onclose = (event) => {
                console.warn('WebSocket连接已关闭, 尝试重连...');
                this.isConnected = false;
                this.reconnect();
            };
            
            // 连接错误事件
            this.ws.onerror = (error) => {
                console.error('WebSocket错误:', error);
            };
        } catch (error) {
            console.error('创建WebSocket连接失败:', error);
            this.reconnect();
        }
    }
    
    // 重连机制
    reconnect() {
        if (this.reconnectAttempts >= WS_CONFIG.maxReconnectAttempts) {
            console.error('已达到最大重连次数, 停止重连');
            // 切换到轮询模式
            this.startPolling();
            return;
        }
        
        this.reconnectAttempts++;
        console.log(`第${this.reconnectAttempts}次重连, ${WS_CONFIG.reconnectInterval}ms后重试`);
        
        setTimeout(() => {
            this.connectWebSocket();
        }, WS_CONFIG.reconnectInterval);
    }
    
    // 轮询模式(WebSocket失败时的降级方案)
    startPolling() {
        console.log('启动HTTP轮询模式, 每5秒获取一次数据');
        const mockGen = new MockDataGenerator();
        
        this.pollingInterval = setInterval(() => {
            const mockData = mockGen.generateFullData();
            this.updateCharts(mockData);
        }, 5000);
    }
    
    // 发送消息
    sendMessage(message) {
        if (this.ws && this.ws.readyState === WebSocket.OPEN) {
            this.ws.send(JSON.stringify(message));
        } else {
            console.warn('WebSocket未连接, 消息未发送');
        }
    }
    
    // 停止所有连接
    stop() {
        if (this.ws) {
            this.ws.close();
        }
        if (this.pollingInterval) {
            clearInterval(this.pollingInterval);
        }
    }
}

// 初始化动态大屏
document.addEventListener('DOMContentLoaded', () => {
    const manager = new DynamicChartManager();
    manager.initCharts();
    manager.connectWebSocket();
});

4.3 大屏展示效果增强

javascript 复制代码
/**
 * 大屏效果增强 - 动画和装饰
 */

class DashboardEnhancer {
    constructor() {
        this.initTimeUpdate();      // 时间更新
        this.initBorderAnimation();  // 边框动画
        this.initWatermark();        // 水印效果
    }
    
    // 实时时间更新
    initTimeUpdate() {
        const timeElement = document.getElementById('currentTime');
        if (!timeElement) return;
        
        const updateTime = () => {
            const now = new Date();
            const year = now.getFullYear();
            const month = (now.getMonth() + 1).toString().padStart(2, '0');
            const day = now.getDate().toString().padStart(2, '0');
            const weekdays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
            const weekday = weekdays[now.getDay()];
            const hour = now.getHours().toString().padStart(2, '0');
            const minute = now.getMinutes().toString().padStart(2, '0');
            const second = now.getSeconds().toString().padStart(2, '0');
            
            timeElement.innerHTML = `${year}-${month}-${day} ${weekday} ${hour}:${minute}:${second}`;
        };
        
        updateTime();
        setInterval(updateTime, 1000);
    }
    
    // 边框光效动画
    initBorderAnimation() {
        const style = document.createElement('style');
        style.textContent = `
            @keyframes borderPulse {
                0% {
                    box-shadow: 0 0 0 0 rgba(64, 158, 255, 0);
                    border-color: rgba(64, 158, 255, 0.3);
                }
                50% {
                    box-shadow: 0 0 20px 2px rgba(64, 158, 255, 0.3);
                    border-color: rgba(64, 158, 255, 0.8);
                }
                100% {
                    box-shadow: 0 0 0 0 rgba(64, 158, 255, 0);
                    border-color: rgba(64, 158, 255, 0.3);
                }
            }
            
            .chart-box {
                animation: borderPulse 4s ease-in-out infinite;
            }
            
            /* 图表悬停放大效果 */
            .chart-box:hover {
                transform: scale(1.02);
                transition: transform 0.3s ease;
                z-index: 100;
            }
            
            /* 数据加载动画 */
            @keyframes fadeInUp {
                from {
                    opacity: 0;
                    transform: translateY(30px);
                }
                to {
                    opacity: 1;
                    transform: translateY(0);
                }
            }
            
            .chart-box {
                animation: fadeInUp 0.6s ease-out forwards;
                opacity: 0;
            }
            
            /* 逐项延迟显示 */
            .chart-box:nth-child(1) { animation-delay: 0.1s; }
            .chart-box:nth-child(2) { animation-delay: 0.2s; }
            .chart-box:nth-child(3) { animation-delay: 0.3s; }
            .chart-box:nth-child(4) { animation-delay: 0.4s; }
            .chart-box:nth-child(5) { animation-delay: 0.5s; }
        `;
        document.head.appendChild(style);
    }
    
    // 水印效果(可选)
    initWatermark() {
        const watermark = document.createElement('div');
        watermark.style.cssText = `
            position: fixed;
            bottom: 20px;
            right: 20px;
            color: rgba(255,255,255,0.1);
            font-size: 12px;
            pointer-events: none;
            z-index: 9999;
        `;
        watermark.textContent = '数据大屏 | 实时监控系统 V1.0';
        document.body.appendChild(watermark);
    }
    
    // 全屏切换功能
    initFullscreen() {
        const btn = document.getElementById('fullscreenBtn');
        if (!btn) return;
        
        btn.addEventListener('click', () => {
            const element = document.documentElement;
            if (document.fullscreenElement) {
                document.exitFullscreen();
                btn.textContent = '🖥️ 全屏';
            } else {
                element.requestFullscreen();
                btn.textContent = '📴 退出全屏';
            }
        });
        
        // 监听全屏变化事件
        document.addEventListener('fullscreenchange', () => {
            if (document.fullscreenElement) {
                btn.textContent = '📴 退出全屏';
            } else {
                btn.textContent = '🖥️ 全屏';
            }
        });
    }
}

// 初始化增强效果
const enhancer = new DashboardEnhancer();
enhancer.initFullscreen();

五、本章小结

核心知识点总结表

知识点 关键内容 代码要点
ECharts初始化 echarts.init(dom) 需要DOM容器,支持自适应
图表配置项 option对象 包含title、tooltip、legend、series等
图表类型 line、bar、pie、gauge 不同场景选择合适的类型
数据更新 setOption(newData) 支持增量更新和完全替换
动态数据 WebSocket + 轮询 实现实时数据推送
动画效果 数字滚动 + 边框动画 提升用户体验
自适应 resize() 事件监听 适配不同分辨率
全屏显示 Fullscreen API 大屏展示必要功能

完整项目代码结构

复制代码
echarts-dashboard/
├── index.html              # 主页面(包含所有样式和结构)
├── js/
│   ├── echarts.min.js     # ECharts核心库
│   ├── data.js            # 静态数据定义
│   ├── charts.js          # 静态图表渲染
│   ├── dynamic.js         # 动态图表管理
│   └── enhance.js         # 效果增强和工具函数
└── README.md              # 项目说明文档

常见问题FAQ

  1. 图表不显示?

    • 检查DOM容器是否存在
    • 确认ECharts库是否正确引入
    • 查看浏览器控制台错误信息
  2. 图表尺寸不对?

    • 调用myChart.resize()方法
    • 监听窗口resize事件
  3. WebSocket连接失败?

    • 检查服务器地址是否正确
    • 实现降级轮询方案
  4. 性能优化建议

    • 使用setOption时设置notMerge: false
    • 避免频繁创建新图表实例
    • 合理设置更新频率
相关推荐
是吗乔治2 小时前
vuetify实现excel表格粘贴效果
前端·vue.js·vue·excel
嵌入式小企鹅2 小时前
算力价值重估、AI编程模型齐开源、RISC-V融资15亿
人工智能·学习·ai·程序员·risc-v·前沿科技·太空算力
Java后端的Ai之路2 小时前
React 快速入门到精通教程:从零基础到能写项目
前端·react.js·前端框架
是上好佳佳佳呀2 小时前
【前端(九)】CSS Transform 2D/3D 变换笔记:分清两个原点,搞懂多重变换
前端·css·笔记
|晴 天|11 小时前
Vue 3 + TypeScript + Element Plus 博客系统开发总结与思考
前端·vue.js·typescript
DeepModel11 小时前
通俗易懂讲透 Q-Learning:从零学会强化学习核心算法
人工智能·学习·算法·机器学习
猫32811 小时前
v-cloak
前端·javascript·vue.js
旷世奇才李先生11 小时前
Vue 3\+Vite\+Pinia实战:企业级前端项目架构设计
前端·javascript·vue.js
handler0112 小时前
从零实现自动化构建:Linux Makefile 完全指南
linux·c++·笔记·学习·自动化