highchart(Highcharts):国外的,不过也很好用。现在也出了中文文档
echarts(ECharts):国内的,也很不错,百度开发的。我们用echarts完成接下来的演示。
我们在此之前已经完成了较多工作,详情关注博主的Django专栏。我们接下来的工作也是在原来的页面的基础上进行的。
如上图所示,我们已经完成了管理员账户、部门管理、用户管理、靓号管理、任务管理、订单管理的界面,我们直接基于这几个界面完成接下来的数据统计工作。
先从简单的图表入手,我们计划完成柱状图、折现图、饼图的绘制。
先浅浅展示下结果
我们在完成简单的几种图之前,首先应该在原有的系统中设计新的界面用于数据统计。
这部分的思路还是传统的Django设计流程:首先修改模板html(我的是叫 layout.html
在其中添加一个能到达数据统计界面的按钮),然后在Django的urls.py
文件中添加新的路径(Django会通过这个路径管理网址以及每个页面对应的后端函数),接下来完成views.py
中对应的函数(函数有返回值,这里的返回值就是html文件),根据上一步的返回值完成对应的html文件(前端设计)。
修改模板html( layout.html
),修改后我们的界面顶部导航栏多了一个数据统计链接,点击后可以访问数据统计页面(
)。
{% load static %}
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Olsentitle>
<link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1-dist/css/bootstrap.min.css' %}">
<link rel="stylesheet" href="{% static 'plugins/bootstrap-3.4.1-dist/css/bootstrap-datetimepicker.min.css' %}">
<style>
.navbar{
border-radius: 0;
}
style>
head>
<body>
<nav class="navbar navbar-default">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">Toggle navigationspan>
<span class="icon-bar">span>
<span class="icon-bar">span>
<span class="icon-bar">span>
button>
<a class="navbar-brand" href="#">Olsen用户管理系统a>
div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li><a href="/admin/list">管理员账户a>li>
<li><a href="/depart/list">部门管理a>li>
<li><a href="/user/list">用户管理a>li>
<li><a href="/num/list">靓号管理a>li>
<li><a href="/task/list">任务管理a>li>
<li><a href="/order/list">订单管理a>li>
<li><a href="/chart/list">数据统计a>li>
ul>
<ul class="nav navbar-nav navbar-right">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
aria-expanded="false">{{ request.session.info.username }} <span class="caret">span>a>
<ul class="dropdown-menu">
<li><a href="#">个人资料a>li>
<li><a href="#">我的信息a>li>
<li role="separator" class="divider">li>
<li><a href="/logout">注销a>li>
ul>
li>
ul>
div>
div>
nav>
<div>
{% block content %}
{% endblock %}
div>
<script src="{% static 'js/jquery.js' %}">script>
<script src="{% static 'plugins/bootstrap-3.4.1-dist/js/bootstrap.min.js' %}">script>
{% block js %}
{% endblock %}
body>
html>
添加路径(urls.py
)。可以看到我们已经完成了很多的工作,只需要在路径列表中再加一条path('chart/list', chart.chart_list)
from django.urls import path
from application01.views import depart, num, user, admin, account, task, order, chart
urlpatterns = [
# path('admin/', admin.site.urls),
path('depart/list', depart.depart_list),
path('depart/add', depart.depart_add),
path('depart/del', depart.depart_del),
path('depart//edit' , depart.depart_edit),
path('user/list', user.user_list),
path('user/add', user.user_add),
path('user/madd', user.user_madd),
path('user//edit' , user.user_edit),
path('user//del' , user.user_del),
path('num/list', num.num_list),
path('num/add', num.num_add),
path('num/a', num.num_a),
path('num//edit' , num.num_edit),
path('num//del' , num.num_del),
path('admin/list', admin.admin_list),
path('admin/add', admin.admin_add),
path('admin//edit' , admin.admin_edit),
path('admin//del' , admin.admin_del),
path('admin//reset' , admin.admin_reset),
path('login', account.login),
path('logout', account.logout),
path('image/code', account.image_code),
path('task/list', task.task_list),
path('task/add', task.task_add),
path('order/list', order.order_list),
path('order/add', order.order_add),
path('order/delete', order.order_delete),
path('order/detail', order.order_detail),
path('order/edit/', order.order_edit),
path('chart/list', chart.chart_list)
]
完成views.py
中函数。因为界面比较多,我把原来的views.py拆成好几个文件,分别对应几个数据表,相关目录如下:
我们这里只是规划一个界面,先不整那些花里胡哨的东西,简简单单返回一个页面就好了。
# -*- coding:utf-8 -*-
from django.shortcuts import render
def chart_list(request):
return render(request, 'chart_list.html')
前端设计。继承在第一步写好的模板,再加上一点bootstrap官网里的模板代码。
{% extends 'layout.html' %}
{% block content %}
<div class="container">
<div class="panel panel-default">
<div class="panel-heading">折线图div>
<div class="panel-body">
————————————---------
div>
div>
<div class="row">
<div class="col-sm-8">
<div class="panel panel-default">
<div class="panel-heading">柱状图div>
<div class="panel-body">
1 1 11 <br>
1 1 11 <br>
1 1111 <br>
1111111<br>
div>
div>
div>
<div class="col-sm-4">
<div class="panel panel-default">
<div class="panel-heading">饼图div>
<div class="panel-body">
O
div>
div>
div>
div>
div>
{% endblock %}
最后,我们完成了页面的规划,最顶上方折线图,饼图和柱状图在下面共用一行。
注意,首先你必须要下载Echarts的js文件(Echarts),然后在echart官网中直接挑一款比较喜欢的图自己修改下配置即可。
我们意在统计不同部门的员工的平均年龄和部门员工数量。
{% extends 'layout.html' %}
{% load static %}
{% block content %}
<div class="container">
<div class="panel panel-default">
<div class="panel-heading">折线图div>
<div class="panel-body">
<div id="l2" style="width: 100%;height:400px;">div>
div>
div>
<div class="row">
<div class="col-sm-7">
<div class="panel panel-default">
<div class="panel-heading">柱状图div>
<div class="panel-body">
<div id="m2" style="width: 100%;height:400px;">div>
div>
div>
div>
<div class="col-sm-5">
<div class="panel panel-default">
<div class="panel-heading">饼图div>
<div class="panel-body">
<div id="p1" style="width: 100%;height:400px;">div>
div>
div>
div>
div>
div>
{% endblock %}
{% block js %}
<script src="{% static 'js/echarts.js' %}">script>
<script type="text/javascript">
$(function () {
initBar();
})
function initBar() {
var myChart = echarts.init(document.getElementById('m2'));
option_bar = {
title: {
text: '员工员工基本信息概览',
textAlign: 'Auto',
left: 'center',
},
legend: {
data:[],
bottom: 0,
},
tooltip: {},
xAxis: [{
type: 'category',
data: [],
}],
yAxis: [
{
type: 'value',
name:'平均年龄',
},
{
type: 'value',
name:'人数',
}
],
series: [
{
data: [],
type: 'bar'
}
]
}
$.ajax({
url: "/chart/bar",
type: "get",
dataType: "JSON",
success: function (res_bar) {
if(res_bar.status){
option_bar.legend.data = res_bar.data.legend;
option_bar.series = res_bar.data.series_list;
option_bar.xAxis[0].data = res_bar.data.xAxis;
myChart.setOption(option_bar);
}
}
})
};
script>
{% endblock %}
后端数据处理代码如下,这边采取pandas完成输出的统计。
def chart_bar(request):
row_obj = pd.DataFrame(models.User.objects.all().values("age", "Department__title"))
data = row_obj.groupby("Department__title")['age'].describe()
legend = ['平均年龄', '部门人数']
series_list = [
{
'name': '平均年龄',
'type': 'bar',
'data': data['mean'].tolist(),
},
{
'name': '部门人数',
'type': 'bar',
'yAxisIndex': 1,
'data': data['count'].tolist(),
},
]
xAxis = data.index.tolist()
result = {
"status": True,
"data": {
"legend": legend,
"xAxis": xAxis,
"series_list": series_list,
}
}
return JsonResponse(result)
效果如下
利用饼图展示订单状态。
部分html代码如下,为模板语言中js部分的代码
{% block js %}
<script src="{% static 'js/echarts.js' %}">script>
<script type="text/javascript">
$(function () {
initPie();
})
function initPie() {
var myChart = echarts.init(document.getElementById('p1'));
option = {
tooltip: {
trigger: 'item'
},
title: {
text: '订单支付状态',
textAlign: 'Auto',
left: 'center',
},
legend: {
top: '90%',
left: 'center'
},
series: [
{
name: 'Access From',
type: 'pie',
radius: ['40%', '70%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 10,
borderColor: '#fff',
borderWidth: 2
},
label: {
show: false,
position: 'center'
},
emphasis: {
label: {
show: true,
fontSize: '40',
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: [
// { value: 1048, name: 'Search Engine' },
]
}
]
};
$.ajax({
url: "/chart/pie",
type: "get",
dataType: "JSON",
success: function (res) {
if(res.status){
option.series[0].data = res.data.series_list;
myChart.setOption(option);
}
}
})
};
script>
{% endblock %}
数据处理用比较简单的ORM语言完成。
def chart_pie(request):
choice = dict(models.Order.status_choices)
series_list = models.Order.objects.values('status').annotate(count=Count('status'))
series_list = [{'name': choice[i["status"]], 'value':i['count']} for i in series_list]
result = {
"status": True,
"data": {
"series_list": series_list,
}
}
return JsonResponse(result)
展示下结果
折线图展示的是不同尾号的手机号码在不同VIP等级下的平均价格。
部分html代码如下,为模板语言中js部分的代码
{% block js %}
<script src="{% static 'js/echarts.js' %}">script>
<script type="text/javascript">
$(function () {
initLine();
})
function initLine() {
var myChart = echarts.init(document.getElementById('l2'));
option_line = {
title: {
text: '号码价格信息概览',
textAlign: 'Auto',
left: 'center',
},
tooltip: {
trigger: 'axis'
},
legend: {
data: [],
orient: 'vertical',
x:'right', //可设定图例在左、右、居中
y:'center', //可设定图例在上、下、居中
padding:[0,20,0,0],
// data: ['Email', 'Union Ads', 'Video Ads', 'Direct', 'Search Engine']
},
grid: {
top: '10%',
left: '3%',
right: '10%',
bottom: '1%',
containLabel: true
},
toolbox: {
feature: {
saveAsImage: {}
}
},
xAxis: [{
type: 'category',
boundaryGap: false,
// data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
}],
yAxis: {
type: 'value'
},
series: [
]
};
$.ajax({
url: "/chart/line",
type: "get",
dataType: "JSON",
success: function (res_line) {
if(res_line.status){
option_line.legend.data = res_line.data.legend;
option_line.series = res_line.data.series_list;
option_line.xAxis[0].data = res_line.data.xAxis;
console.log(res_line);
myChart.setOption(option_line);
}
}
})
};
script>
{% endblock %}
特意统计了个比较复杂的东西,还是用ORM语言完成。
def chart_line(request):
series_list = [models.YourNum.objects.filter(mobile__endswith=i) for i in range(10)]
series_list = [end_num.values('level').annotate(avg=Avg('price')).order_by("level") for end_num in series_list]
series_list = [{'name': "尾号"+str(index), 'type': 'line', 'data': [level_sorted['avg'] for level_sorted in end_with]} for index, end_with in enumerate(series_list)]
result = {
"status": True,
"data": {
"xAxis": [str(i+1)+"级" for i in range(5)],
"legend": [i['name'] for i in series_list],
"series_list": series_list,
}
}
# print(result)
return JsonResponse(result)