Django系列(九)之使用ecahrts

一、概述
ECharts(Enterprise Charts)是由百度开源的一款基于 JavaScript 的可视化图表库,专为数据可视化设计, 支持丰富的图表类型、交互能力和跨端适配,是 DBA、数据分析师、前端开发者在数据展示场景中最常用的工具之一。

echarts官网:https://echarts.apache.org/zh/index.html
echarts CDN:https://cdn.jsdelivr.net/npm/echarts@6.0.0/dist/

二、使用
2.1简介
ECharts 提供了多种安装方式,你可以根据项目的实际情况选择以下任意一种方式安装。
从 GitHub 获取
从 npm 获取
从 CDN 获取
在线定制

本文介绍从CDN获取的方式:
<!-- 引入ECharts CDN -->
<script src="https://cdn.jsdelivr.net/npm/echarts/dist/echarts.min.js"></script>

也可以把echats下载到本地然后再引入,也比较简单,这种方式本文不展开介绍
<!-- 或下载到本地引用:<script src="/static/js/echarts.min.js"></script> -->
2.2、前端echarts图表代码示例
echarts支持多种常用图表,有丰富的样式和其他功能,本文只介绍柱状图,折线图,饼图三种图标基本的使用,更多细节请自行参阅官网:

https://echarts.apache.org/zh/index.html

# 前端代码:
# 第一步、需要引入echarts
<!-- 引入ECharts CDN -->
    <script src="https://cdn.jsdelivr.net/npm/echarts/dist/echarts.min.js"></script>

  # 第二步、需要为每个图表准备一个定义了宽高的DOM。注意这里就是正常的html,css代码,你的图表布局在这里写好
    <div class="content-box">
        <h3>数据统计</h3>
        
        <!-- 为 ECharts 准备一个定义了宽高的 DOM -->
        <div class="top" id="line" style="width: 1100px; height:400px;"></div>
        <div class="bottom">
             <div id="bar" style="width: 600px; height:400px;"></div>
            <div id="pie" style="width: 500px; height:400px;"></div>
        </div>
    </div>

    # 第三步、这里开始使用echarts,分三步:
      # 第一:要先初始化ecahrts实例
      # 第二:要获取后端获得的数据,后续渲染在echarts中
      # 第三:渲染echarts,使用setOption()方法,这步下面有详细说明
    <script type="text/javascript">
        // 1、基于准备好的dom,初始化echarts实例
        const lineChart = echarts.init(document.getElementById('line'));
        const barChart = echarts.init(document.getElementById('bar'));
        const pieChart = echarts.init(document.getElementById('pie'));

        // 2. 获取后端传递的数据(Django模板变量转JS)
        // 注意:这里我用的是django的render,也可以使用ajax,后端使用JsonRespons传递数据给前端
        const charts_data = {{ charts_data|safe }}

    
        // 3、指定图表的配置项和数据
        // 3.1、配置折线图(按天库存统计,按天统计:库存总数量、库存总价值)
        // 折线图分为x轴和y轴,下面的title是标题,xAxis就是定义x轴,{ type: 'category', data: charts_data.xAxis_date},
        //里面的type: 'category'是固定写法,后面的data表示数据,固定写法,不要变,charts_data.xAxis_date就是后端传过来的数据
        lineChart.setOption({
            title: { text: '按天统计:库存总数量、库存总价值' },
            xAxis: { type: 'category', data: charts_data.xAxis_date},

            // Y 轴:定义左右两个,我这里用的是双y轴的折线图,也可以单y轴,[]里去掉一个就行
            
            yAxis: [                      // 对y轴的定义
            { // 左侧Y轴                
              type: 'value',             //固定写法,不要变
              name: '库存总数量',   // y轴 label,就是y轴的名字
              position: 'left',          //左侧还是右侧的y轴
              alignTicks: true        // 可选:对齐刻度(让网格线对齐)
            },
            { // 右侧 Y 轴标题
              type: 'value',
              name: '库存总价值',
              position: 'right',
              alignTicks: true
            }
            ],

            // 数据渲染
            series: [{
                name: '库存总量',      //legend名称,也就是图例
                type: 'line',              // line表示折线图,
                yAxisIndex: 0,          // 使用第一个 Y 轴(left)
                data: charts_data.yAxis_total_quantity,     // 这个就是数据
                // 是否显示数据以及对数据样式的配置
                {#label: {#}
                {#    show: true,#}。      //是否在折线图上显示数据
                {#    position: 'top',       // 显示在点的上方#}
                {#    formatter: '{c}',      // {c} = 当前值#}
                {#    fontSize: 10,#}
                {#    color: '#333'#}

            },
            {
                name: '库存总价值',
                type: 'line',
                yAxisIndex: 1,          // 使用第二个 Y 轴(right)
                data: charts_data.yAxis_total_value,
                // 显示数据
                {#label: {#}
                {#    show: true,#}
                {#    position: 'top',       // 显示在点的上方#}
                {#    formatter: '{c}',      // {c} = 当前值#}
                {#    fontSize: 10,#}
                {#    color: '#333'#}

            }],

            // 可选:显示图例
            legend: {
            show: true
            }
        });

        // 3.2、配置柱状图
        barChart.setOption({
            title: { text: '销售统计:历史总入库数量最高的前10个物品' },
            xAxis: { // x轴
            type: 'category',
            data: charts_data.xAxis_name},

            yAxis: {type: 'value'},   //y轴
            // 数据渲染
            series: [{
                type: 'bar',    // bar表示柱状图
                data: charts_data.yAxis_total_in,   //后端传的数据
                label: {
                    show: true,  // 显示数值
                    position: 'top', // 数值显示在柱子顶端
                    formatter: '{c}', // {c} 表示原始数据值
                    fontSize: 12,
                    color: '#333'
                }
            }]
        });

        // 3.3、配置饼图(仓库中每种分类的总价值占比)
        // 饼图不需要x轴和y轴,渲染数据即可
        pieChart.setOption({
            title: { text: '占比统计:仓库中每种分类的总价值占比' },
            series: [
                {
                    type: 'pie',     // pie表示饼图
                    data: charts_data.data_pie,
                    // 显示标签(包括百分比和数值)
                    label: { // 让能显示数据,默认不显示
                        position: 'outside',  // 让标签在外面,有些时候数据会被遮挡,宽度不够,数据显示不出来
                        show: true,
                        formatter: '{b}\n{c} ({d}%)'  // {b}=名称, {d}=百分比, {c}=原始值, eg  {b}: {d}%
                        // 也可以写成:'{b}\n{c} ({d}%)'
                    }
                }
            ]
        });


        // 4. 自适应窗口大小变化,可选
        window.addEventListener('resize', () => {
            lineChart.resize();
            barChart.resize();
            pieChart.resize();
        });
    </script>




# 这个是css 代码,对几个图表做了布局,这个不重要,自己写前端布局就行
# content-box 是包裹这三个图表的div,上面没有写上,不要纠结
.content-box{
    margin: 20px 20px;
}

.content-box h3{
    color: #333;
    margin-bottom: 20px;
    border-bottom: 3px solid #007bff;   /*为h3 添加一个底部边框*/
}

.content-box .bottom{
    display: flex;
    justify-content: space-between;
    align-items: center;
}

2.3、后端数据和数据格式代码示例
def data_analysis(request):

    # 按天统计:库存总数量、库存总价值
    with connection.cursor() as cursor:
        data_stats_sql = """
        select date_format(operate_date, '%Y-%m-%d') as date,
        sum(current_quantity) as total_quantity,
        sum(current_quantity * price) as total_value
        from wms_goodsinoutrecord as record 
        join wms_goods as goods on record.goods_id = goods.id
        where record.id > 10
        group by date_format(operate_date, '%Y-%m-%d')
        order by date_format(operate_date, '%Y-%m-%d');
        """

        cursor.execute(data_stats_sql)
        data_line = cursor.fetchall()


    # 销售统计:历史总入库数量最高的前10个物品
    with connection.cursor() as cursor:
        top_10_sql = """
        select goods.name, sum(record.amount) as total_in
        from wms_goodsinoutrecord as record 
        join wms_goods as goods on record.goods_id = goods.id
        where record.operation_type = 'in'
        group by goods.name 
        order by total_in desc
        limit 10;
        """

        cursor.execute(top_10_sql)
        data_bar = cursor.fetchall()

    # 占比统计:仓库中每种分类的总价值占比
    with connection.cursor() as cursor:
        category_value_sql = """
        select category.name, sum(goods.price * goods.current_quantity) as total_value
        from wms_goods as goods
        join wms_goodscategory as category on goods.category_id = category.id
        group by category.name;
        """

        cursor.execute(category_value_sql)
        data_pie = cursor.fetchall()

    # 这里要注意:把每个轴的数据直接封装好,前端直接用,不要传到前端再转换格式
    # echarts是js写的,所以需要数组类型,对应django后端就要list类型,把数据全部放到列表中然后传到前端直接用
    charts_data = {
        # 折线图数据
        "xAxis_date": [row[0] for row in data_line],   # x轴数据
        "yAxis_total_quantity": [float(row[1]) for row in data_line],  # y轴库存总数量
        "yAxis_total_value": [float(row[2]) for row in data_line],  # y轴库存总价值

        # 柱状图数据
        "xAxis_name": [row[0] for row in data_bar],
        "yAxis_total_in": [float(row[1]) for row in data_bar],

        # 饼图数据,注意这里的key必须是name和value,不能是别的,echarts里要求
        "data_pie": [{"name": name, "value": float(total_value)} for name, total_value in data_pie]
    }

    # 序列化,因为前端使用数据的是js了(echarts是js写的),不是html,所以数据不能直接返回,如果有中文或者特殊字符可能会显示错误或报错
    charts_data = json.dumps(charts_data)

    # 如果是AJAX请求,返回JSON数据
    # if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
    #     return JsonResponse(charts_data)

    # 否则渲染模板
    return render(request, 'bi/bi_data.html', {"charts_data": charts_data})

最终结果展示:

image

参考文档:echarts官网

posted @ 2025-12-08 10:47  有形无形  阅读(21)  评论(0)    收藏  举报