最好的选择,echarts和d3.js
动态界面
动态添加删除水果
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>动态列表 - Vue.js</title>
<style>
* {
margin: 0;
padding: 0;
}
body {
background-color: #000;
color: #fff;
}
#app {
width: 40%;
margin: 20px auto;
}
#fruits>li {
width: 90%;
height: 50px;
background-color: #6ca;
margin: 4px 0;
text-align: center;
font-size: 20px;
list-style-type: none;
line-height: 50px;
}
#fruits+div {
margin-top: 20px;
}
#app>div>input[type=text] {
width: 70%;
height: 40px;
color: #fff;
border-radius: 8px;
border: none;
outline: none;
font-size: 20px;
text-align: center;
vertical-align: middle;
background-color: #999;
}
#ok {
width: 19%;
height: 40px;
color: #fff;
background-color: #a45;
border: none;
outline: none;
font-size: 16px;
vertical-align: middle;
}
</style>
</head>
<body>
<div id="app">
<ul id="fruits">
<li v-for="fruit in fruits" @click="removeItem(fruit)">{{ fruit }}</li>
</ul>
<div>
<input type="text" v-model="fruitName" @keydown.enter="addItem()">
<button id="ok" @click="addItem()">确定</button>
</div>
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.14/vue.min.js"></script>
<script>
let app = new Vue({
el: '#app',
data: {
fruits: ['榴莲', '番茄', '葡萄'],
fruitName: ''
},
methods: {
removeItem(fruit) {
let index = this.fruits.indexOf(fruit)
this.fruits.splice(index, 1)
},
addItem() {
let name = this.fruitName.trim()
if (name.length > 0) {
this.fruits.push(name)
}
this.fruitName = ''
}
}
})
</script>
</body>
</html>
渲染
后端渲染
通过Java、Python、PHP等的代码渲染模板页面,生成动态内容
缺点:
1.搞后端的人需要具备前端知识,
2.开销较大,在后端渲染会增加服务器的负担和压力,
前端渲染
后端是负责处理业务和提供数据,前端实现页面渲染(前后端分离开发)
优点:
1.前后端相互不影响,工作是独立的
2.通过JS引擎实现页面渲染,不增加服务器的负担
Python程序会提供JSON格式的数据,前端通过JavaScript请求JSON数据,
获得数据后通过Vue.js实现页面的渲染(把数据动态地填写到页面上)
Echarts
K线图
<head>
<meta charset="UTF-8">
<link href="/static/css/index.css" rel="stylesheet">
<title>首页</title>
</head>
<body>
<!--
后端渲染:通过Java、Python、PHP等的代码渲染模板页面,生成动态内容
缺点:
1.搞后端的人需要具备前端知识,
2.开销较大,在后端渲染会增加服务器的负担和压力,
前端渲染:后端是负责处理业务和提供数据,前端实现页面渲染(前后端分离开发)
优点:
1.前后端相互不影响,工作是独立的
2.通过JS引擎实现页面渲染,不增加服务器的负担
Python程序会提供JSON格式的数据,前端通过JavaScript请求JSON数据,
获得数据后通过Vue.js实现页面的渲染(把数据动态地填写到页面上)
-->
<div id="app">
<ul id="general">
<li v-for="item in data_items">
<img :src="'/static/images/'+item.icon" width="36">
<!-- ‘:’ ---动态属性-->
<div style="display: inline-block">
<div >{{ item.value | numberFormat }}</div>
<div>{{ item.name }}</div>
</div>
</li>
</ul>
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.14/vue.min.js"></script>
<script>
// 通过JS代码获取JSON数据,然后交给Vue.js渲染界面
let app = new Vue({
el:'#app',
data:{
data_items:[]
},
//过滤器
filters:{
numberFormat(x){
return x.toLocaleString()
}
},
created(){
// Promise
// fetch('/api/fruits')
// .then(function(resp) {return resp.json()})
// .then(function(json){app.selected_fruits=json.fruits})
// 这个是app不是this是因为进了function函数之后
// this会变成promise,而不是app
// 但是我们要给app的水果赋值
// .then(resp=>resp.json())
// .then(json=>this.selected_fruits=json.fruits)
// =>可以不改变上下文环境,上面是app,下面也是app
fetch('/api/general_data')
.then(resp=>resp.json())
.then(json=>this.data_items=json.items)
}
})
</script>
<!--2.创建div-->
<div id="main" style="width: 800px">
</div>
<!--1.引入echarts-->
<script src="https://cdn.jsdelivr.net/npm/echarts@5.1.2/dist/echarts.min.js"></script>
<script>
//将指定的div处理成绘图的画布
let myChart = echarts.init(document.querySelector('#main'))
// 准备图标需要使用到的数据
let colorList = ['#c23531','#2f4554', '#61a0a8', '#d48265', '#91c7ae','#749f83', '#ca8622', '#bda29a','#6e7074', '#546570', '#c4ccd3'];
let labelFont = 'bold 12px Sans-serif';
// 计算MA函数
function calculateMA(dayCount, data) {
let result = [];
for (let i = 0, len = data.length; i < len; i++) {
if (i < dayCount) {
result.push('-');
continue;
}
let sum = 0;
for (let j = 0; j < dayCount; j++) {
sum += data[i - j][1];
}
result.push((sum / dayCount).toFixed(2));
}
return result;
}
//fetch部分
fetch('/api/k_data')
.then(resp=>resp.json())
.then(json=>{
let dataMA5 = calculateMA(5, json.data);
let dataMA10 = calculateMA(10, json.data);
let dataMA20 = calculateMA(20, json.data);
let option = {
animation: false,
color: colorList,
title: {
left: 'center',
text: '移动端 K线图'
},
legend: {
top: 30,
data: ['日K', 'MA5', 'MA10', 'MA20', 'MA30']
},
tooltip: {
triggerOn: 'none',
transitionDuration: 0,
confine: true,
borderRadius: 4,
borderWidth: 1,
borderColor: '#333',
backgroundColor: 'rgba(255,255,255,0.9)',
textStyle: {
fontSize: 12,
color: '#333'
},
position: function (pos, params, el, elRect, size) {
let obj = {
top: 60
};
obj[['left', 'right'][+(pos[0] < size.viewSize[0] / 2)]] = 5;
return obj;
}
},
axisPointer: {
link: [{
xAxisIndex: [0, 1]
}]
},
dataZoom: [{
type: 'slider',
xAxisIndex: [0, 1],
realtime: false,
start: 20,
end: 70,
top: 65,
height: 20,
handleIcon: 'path://M10.7,11.9H9.3c-4.9,0.3-8.8,4.4-8.8,9.4c0,5,3.9,9.1,8.8,9.4h1.3c4.9-0.3,8.8-4.4,8.8-9.4C19.5,16.3,15.6,12.2,10.7,11.9z M13.3,24.4H6.7V23h6.6V24.4z M13.3,19.6H6.7v-1.4h6.6V19.6z',
handleSize: '120%'
}, {
type: 'inside',
xAxisIndex: [0, 1],
start: 40,
end: 70,
top: 30,
height: 20
}],
xAxis: [{
type: 'category',
data: json.dates,
boundaryGap : false,
axisLine: { lineStyle: { color: '#777' } },
axisLabel: {
formatter: function (value) {
return echarts.format.formatTime('MM-dd', value);
}
},
min: 'dataMin',
max: 'dataMax',
axisPointer: {
show: true
}
}, {
type: 'category',
gridIndex: 1,
data: json.dates,
scale: true,
boundaryGap : false,
splitLine: {show: false},
axisLabel: {show: false},
axisTick: {show: false},
axisLine: { lineStyle: { color: '#777' } },
splitNumber: 20,
min: 'dataMin',
max: 'dataMax',
axisPointer: {
type: 'shadow',
label: {show: false},
triggerTooltip: true,
handle: {
show: true,
margin: 30,
color: '#B80C00'
}
}
}],
yAxis: [{
scale: true,
splitNumber: 2,
axisLine: { lineStyle: { color: '#777' } },
splitLine: { show: true },
axisTick: { show: false },
axisLabel: {
inside: true,
formatter: '{value}\n'
}
}, {
scale: true,
gridIndex: 1,
splitNumber: 2,
axisLabel: {show: false},
axisLine: {show: false},
axisTick: {show: false},
splitLine: {show: false}
}],
grid: [{
left: 20,
right: 20,
top: 110,
height: 120
}, {
left: 20,
right: 20,
height: 40,
top: 260
}],
graphic: [{
type: 'group',
left: 'center',
top: 70,
width: 300,
bounding: 'raw',
children: [{
id: 'MA5',
type: 'text',
style: {fill: colorList[1], font: labelFont},
left: 0
}, {
id: 'MA10',
type: 'text',
style: {fill: colorList[2], font: labelFont},
left: 'center'
}, {
id: 'MA20',
type: 'text',
style: {fill: colorList[3], font: labelFont},
right: 0
}]
}],
series: [{
name: 'Volume',
type: 'bar',
xAxisIndex: 1,
yAxisIndex: 1,
itemStyle: {
color: '#7fbe9e'
},
emphasis: {
itemStyle: {
color: '#140'
}
},
data: json.volumes
}, {
type: 'candlestick',
name: '日K',
data: json.data,
itemStyle: {
color: '#ef232a',
color0: '#14b143',
borderColor: '#ef232a',
borderColor0: '#14b143'
},
emphasis: {
itemStyle: {
color: 'black',
color0: '#444',
borderColor: 'black',
borderColor0: '#444'
}
}
}, {
name: 'MA5',
type: 'line',
data: dataMA5,
smooth: true,
showSymbol: false,
lineStyle: {
width: 1
}
}, {
name: 'MA10',
type: 'line',
data: dataMA10,
smooth: true,
showSymbol: false,
lineStyle: {
width: 1
}
}, {
name: 'MA20',
type: 'line',
data: dataMA20,
smooth: true,
showSymbol: false,
lineStyle: {
width: 1
}
}]
}
myChart.setOption(option);
})
// 把数据加到图表上
</script>
</body>
</html>
后端部分
import pymysql
from flask import Flask, redirect
app = Flask(__name__)
@app.route('/', endpoint='index')
def show_index():
# 将请求重定向到static目录下的index.html
return redirect(r'static\index.html')
# API - Application Programming Interface - 应用程序编程接口
# 网络API(网络数据接口) - 请求这个URL就可以获得对应的数据(JSON格式)
@app.route('/api/k_data')
def get_stock_data():
conn = pymysql.connect(host='127.0.0.1', port=3306,
user='root', password='root',
database='stock', charset='utf8mb4')
dates, data, volumes = [], [], []
try:
with conn.cursor(pymysql.cursors.DictCursor) as cursor:
cursor.execute(
'select trade_date,open_price,close_price,low_price,high_price,trade_volume from tb_baba_stock;'
)
row_dict = cursor.fetchone()
print(row_dict)
while row_dict:
dates.append(row_dict['trade_date'].strftime('%Y-%m-%d'))
volumes.append(float(row_dict['trade_volume']))
tmp_data=[float(row_dict['open_price']),float(row_dict['close_price']),float(row_dict['low_price']),float(row_dict['high_price']),float(row_dict['trade_volume'])]
data.append(tmp_data)
row_dict = cursor.fetchone()
except pymysql.MySQLError as err:
print(err)
finally:
# 一定要释放连接
conn.close()
return {'dates': dates, 'data': data, 'volumes': volumes}
# https://echarts.apache.org/examples/en/editor.html?c=candlestick-touch
@app.route('/api/general_data')
def get_general_data():
return {'items': [
{'icon': 'house.png', 'value': 100000, 'name': '实验室'},
{'icon': 'what.png', 'value': 200000, 'name': '项目数'},
{'icon': '2.png', 'value': 300000, 'name': '成果数'},
{'icon': 'people.png', 'value': 40000, 'name': '专家数'},
{'icon': 'prize.png', 'value': 5000, 'name': '经费数'},
]}
if __name__ == '__main__':
app.run(host='10.7.174.71', debug=True)
D3.JS
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<!-- 引入 D3.JS 文件 -->
<script src="https://d3js.org/d3.v5.min.js"></script>
<style>
#main{
width: 800px;
height: 420px;
margin: 0 auto;
border: 1px solid black;
}
</style>
</head>
<body>
<!--<div id="main" style="width: 800px">-->
<!--</div>-->
<p>dog</p>
<p>cat</p>
<script>
var p=d3.select("body").selectAll("p");
// d3里选择元素的函数有两个
// d3.select()
// d3.selectAll()
p.text("helloWorld!")
//D3.js中绑定数据的两个函数
// data():讲一个数组绑定到选择集上,数组各项和选择集各元素绑定,也就是一一对应的关系(这里或许敏锐的你会发现问题,下一章节讲)
// datum():将一个数据绑定到所有选择集上
var str = "is an animal";
var p = d3.select("body").selectAll("p");
p.datum(str).text(function (d,i) {
return "第"+i+"个元素"+d;
})
// 将str绑定在三个<p>选择集上,通过匿名函数
//function(d,i)访问到绑定的元素,d:数据,i:索引
</script>
</body>
</html>
var dataset = ["cute","fat","mad"];
var p = d3.select("body").selectAll("p");
p.data(dataset)
.text(function (d,i) {
return "第"+i+"个动物"+d;
})
// 第x个动物xx
下一章节讲)
// datum():将一个数据绑定到所有选择集上
var str = “is an animal”;
var p = d3.select(“body”).selectAll(“p”);
p.datum(str).text(function (d,i) {
return “第”+i+“个元素”+d;
})
// 将str绑定在三个
选择集上,通过匿名函数
//function(d,i)访问到绑定的元素,d:数据,i:索引
var dataset = ["cute","fat","mad"];
var p = d3.select("body").selectAll("p");
p.data(dataset)
.text(function (d,i) {
return "第"+i+"个动物"+d;
})
// 第x个动物xx