一、簡介
ECharts,一個使用 JavaScript 實現的開源可視化庫,可以流暢的運行在 PC 和移動設備上,兼容當前絕大部分瀏覽器(IE8/9/10/11,Chrome,Firefox,Safari等),底層依賴矢量圖形庫 ZRender,提供直觀,交互豐富,可高度個性化定製的數據可視化圖表。ECharts 提供了常規的折線圖、柱狀圖、散點圖、餅圖、K線圖,用於統計的盒形圖,用於地理數據可視化的地圖、熱力圖、線圖,用於關係數據可視化的關係圖、treemap、旭日圖,多維數據可視化的平行座標,還有用於 BI 的漏斗圖,儀表盤,並且支持圖與圖之間的混搭。
二、安裝
官方實例:https://www.echartsjs.com/examples/zh/index.html
需要的包命令爲如下↓
yarn add echarts 或者 npm echarts
yarn add echarts-for-react 或者 npm echarts-for-react
三、搭建
選擇官方,柱狀圖下的座標軸刻度與標籤對齊插件。
xAxis 下的 data爲 x軸數據
series 下的 data爲 對應的參數
倆個data我們只需要替換下面的數據即可
四、參考代碼
import React, { Component } from 'react';
import { connect } from 'dva';
import config from '../../../public/config';
import { Card, Table } from 'antd';
import moment from "moment";
import ReactEcharts from 'echarts-for-react';
import styles from './AntdOverride.less';
import OrderTable from './OrderTable';
/**
* 首頁統計圖表
* @author 於公成
*
*/
class IndexPageTable extends Component {
constructor() {
super();
let companyList = [];
let userVolumeList= [];
let option={}
let orderDateList=[];
let orderVolumeList=[];
let option2={}
this.state= {
companyList,
userVolumeList,
option,
orderDateList,
orderVolumeList,
option2
}
}
componentWillMount(){
}
componentDidMount() {
//部門用戶量
let companyList = this.state.companyList;
let userVolumeList = this.state.userVolumeList;
let flag = this;
this.props.dispatch(
{
type: "query/querySecret",
payload: {
url: config.dataUrl + "/deptinfo/queryAllDeptUserVolume"
},
callback: (result) => {
console.log(result)
if (result.code === 200 && result.data != null) {
result.data.map((item) => {
companyList.push(
item.company,
)
userVolumeList.push(
item.uservolume
)
})
let option = this.state.option = {
// color: ['#3398DB','#006699', '#4cabce', '#e5323e'],
tooltip: {
trigger: 'axis',
axisPointer: { // 座標軸指示器,座標軸觸發有效
type: 'shadow' // 默認爲直線,可選爲:'line' | 'shadow'
}
},
grid: {
left: '1%',
right: '3%',
bottom: '2%',
top:'2%',
containLabel: true
},
xAxis: [
{
type: 'category',
data: companyList,
axisTick: {
alignWithLabel: true,
},
axisLabel: {
interval: 0, //強制顯示文字
formatter: (value, index) => {
// 10 6 這些你自定義就行
let v = value.substring(0, 6) + '...'
return value.length > 9 ? v : value
}
}
}
],
yAxis: [
{
type: 'value',
}
],
series: [
{
name: '用戶量',
type: 'bar',
barWidth: '60%',
data: userVolumeList,
itemStyle: {
normal: {
//這裏是重點
color: (params) => {
//注意,如果顏色太少的話,後面顏色不會自動循環,最好多定義幾個顏色
let colorList = ['#c23531', '#2f4554', '#61a0a8', '#d48265', '#91c7ae', '#749f83', '#ca8622'];
return colorList[params.dataIndex]
}
}
}
},
],
triggerEvent: false // 設置爲true後,可觸發事件。實現x軸文字過長,顯示省略號,hover上去顯示全部的功能
};
this.setState({
companyList, userVolumeList, option
})
}
}
})
let orderDateList = this.state.orderDateList;
let orderVolumeList = this.state.orderVolumeList;
//訂單用戶量
this.props.dispatch(
{
type:"query/querySecret",
payload: {
url:config.dataUrl+ "insorder/queryOrderVolume",
},
callback:(result)=>{
console.log(result)
if (result.code === 200 && result.data != null) {
result.data.map((item) => {
orderDateList.push(
moment(item.orderdate).format('YYYY-MM-DD')
)
orderVolumeList.push(
item.ordervolume
)
})
let option2 = this.state.option2 = {
xAxis: {
type: 'category',
data: orderDateList
},
yAxis: {
type: 'value'
},
grid: {
left: '1%',
right: '4%',
bottom: '2%',
top:'2%',
containLabel: true
},
series: [{
data: orderVolumeList,
type: 'line'
}]
}
this.setState({
orderDateList, orderVolumeList, option2
})
console.log("orderDateList:"+orderDateList)
console.log("orderVolumeList:"+orderVolumeList)
}
}
}
)
}
render() {
//顯隱控制
if(this.props.layout.status===0){
return (
<div>
{/*<Card >*/}
<div style={{width:'50%',float:'right'}} >
<OrderTable />
</div>
{/*</Card>*/}
<Card className={styles.override_ant_card_title} style={{width:'50%',display:'table'}} title="註冊用戶統計">
<ReactEcharts option={this.state.option} />
</Card >
<Card className={styles.override_ant_card_title} title="訂單數量統計">
<ReactEcharts option={this.state.option2} />
</Card>
</div>
)
}else{
return (
<div>
</div>
)
}
}
}
function mapStateToProps(state) {
return {
layout:state.layout,
verify:state.verify,
user:state.user
};
}
export default connect(mapStateToProps)(IndexPageTable);
實現結果
橫向柱狀圖
import React, { Component } from 'react';
import { connect } from 'dva';
import config from '../../../public/config';
import { Card, Table } from 'antd';
import moment from "moment";
import ReactEcharts from 'echarts-for-react';
import styles from './AntdOverride.less';
import OrderTable from './OrderTable';
/**
* 首頁統計圖表
* @author 於公成
*
*/
class IndexPageTable extends Component {
constructor() {
super();
let companyList = [];
let userVolumeList= [];
let option={}
let orderDateList=[];
let orderVolumeList=[];
let option2={}
this.state= {
companyList,
userVolumeList,
option,
orderDateList,
orderVolumeList,
option2
}
}
componentWillMount(){
}
componentDidMount() {
//部門用戶量
let companyList = this.state.companyList;
let userVolumeList = this.state.userVolumeList;
let flag = this;
//柱狀圖樣式2
this.props.dispatch(
{
type: "query/querySecret",
payload: {
url: config.dataUrl + "/deptinfo/queryAllDeptUserVolume"
},
callback: (result) => {
console.log(result)
if (result.code === 200 && result.data != null) {
result.data.map((item) => {
companyList.push(
item.company,
)
userVolumeList.push(
item.uservolume
)
})
let option = this.state.option = {
// title: {
// text: '世界人口總量',
// subtext: '數據來自網絡'
// },
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
// legend: {
// data: ['2011年', '2012年']
// },
grid: {
left: '1%',
right: '3%',
bottom: '2%',
top:'2%',
containLabel: true
},
xAxis: {
type: 'value',
boundaryGap: [0, 0.01]
},
yAxis: {
type: 'category',
data: companyList,
//Y軸斜
// axisLabel:{
// interval: 0 ,
// rotate:10
// }
},
series: [
{
type: 'bar',
data: userVolumeList,
itemStyle: {
normal: {
//這裏是重點
color: (params) => {
//注意,如果顏色太少的話,後面顏色不會自動循環,最好多定義幾個顏色
// let colorList = ['#c23531', '#2f4554', '#61a0a8', '#d48265', '#91c7ae', '#749f83', '#ca8622'];
// let colorList = ['#37a2da', '#32c5e9', '#67e0e3', '#9fe6b8', '#ffdb5c', '#ff9f7f', '#fb7293','#e062ae','#e690d1'];
let colorList = ['#e690d1', '#e062ae', '#fb7293', '#ff9f7f', '#ffdb5c', '#9fe6b8', '#67e0e3','#32c5e9','#37a2da'];
return colorList[params.dataIndex]
}
}
},
}
]
};
this.setState({
companyList, userVolumeList, option
})
}
}
})
//柱狀圖樣式1
// this.props.dispatch(
// {
// type: "query/querySecret",
// payload: {
// url: config.dataUrl + "/deptinfo/queryAllDeptUserVolume"
// },
// callback: (result) => {
// console.log(result)
// if (result.code === 200 && result.data != null) {
// result.data.map((item) => {
// companyList.push(
// item.company,
// )
// userVolumeList.push(
// item.uservolume
// )
// })
// let option = this.state.option = {
// // color: ['#3398DB','#006699', '#4cabce', '#e5323e'],
// tooltip: {
// trigger: 'axis',
// axisPointer: { // 座標軸指示器,座標軸觸發有效
// type: 'shadow' // 默認爲直線,可選爲:'line' | 'shadow'
// }
// },
// grid: {
// left: '1%',
// right: '3%',
// bottom: '2%',
// top:'2%',
// containLabel: true
// },
//
// xAxis: [
// {
// type: 'category',
// data: companyList,
// axisTick: {
// alignWithLabel: true,
//
// },
// axisLabel: {
// interval: 0, //強制顯示文字
// formatter: (value, index) => {
// // 10 6 這些你自定義就行
// let v = value.substring(0, 6) + '...'
// return value.length > 9 ? v : value
// }
// }
// }
// ],
// yAxis: [
// {
// type: 'value',
// }
// ],
//
// series: [
// {
// name: '用戶量',
// type: 'bar',
// barWidth: '60%',
// data: userVolumeList,
// itemStyle: {
// normal: {
// //這裏是重點
// color: (params) => {
// //注意,如果顏色太少的話,後面顏色不會自動循環,最好多定義幾個顏色
// let colorList = ['#c23531', '#2f4554', '#61a0a8', '#d48265', '#91c7ae', '#749f83', '#ca8622'];
// return colorList[params.dataIndex]
// }
// }
// }
// },
//
// ],
// triggerEvent: false // 設置爲true後,可觸發事件。實現x軸文字過長,顯示省略號,hover上去顯示全部的功能
// };
// this.setState({
// companyList, userVolumeList, option
// })
// }
// }
// })
let orderDateList = this.state.orderDateList;
let orderVolumeList = this.state.orderVolumeList;
//訂單用戶量
this.props.dispatch(
{
type:"query/querySecret",
payload: {
url:config.dataUrl+ "insorder/queryOrderVolume",
},
callback:(result)=>{
console.log(result)
if (result.code === 200 && result.data != null) {
result.data.map((item) => {
orderDateList.push(
moment(item.orderdate).format('YYYY-MM')
)
orderVolumeList.push(
item.ordervolume
)
})
let option2 = this.state.option2 = {
tooltip: {
trigger: 'axis',
},
xAxis: {
type: 'category',
data: orderDateList
},
yAxis: {
type: 'value'
},
grid: {
left: '1%',
right: '4%',
bottom: '2%',
top:'2%',
containLabel: true
},
series: [{
data: orderVolumeList,
type: 'line',
}]
}
this.setState({
orderDateList, orderVolumeList, option2
})
console.log("orderDateList:"+orderDateList)
console.log("orderVolumeList:"+orderVolumeList)
}
}
}
)
}
render() {
//顯隱控制
if(this.props.layout.status===0){
return (
<div>
{/*<Card >*/}
<div style={{width:'48%',float:'right'}} >
<OrderTable />
</div>
{/*</Card>*/}
<Card className={styles.override_ant_card_title} style={{width:'50%',display:'table'}} title="註冊用戶統計">
<ReactEcharts option={this.state.option} />
</Card >
<Card className={styles.override_ant_card_title} title="訂單數量統計">
<ReactEcharts option={this.state.option2} />
</Card>
</div>
)
}else{
return (
<div>
</div>
)
}
}
}
function mapStateToProps(state) {
return {
layout:state.layout,
verify:state.verify,
user:state.user
};
}
export default connect(mapStateToProps)(IndexPageTable);
實現結果
新增餅狀圖
import React, { Component } from 'react';
import { connect } from 'dva';
import config from '../../../public/config';
import { Card, List, Table, Statistic, Row, Col } from 'antd';
import moment from "moment";
import ReactEcharts from 'echarts-for-react';
import styles from './AntdOverride.less';
import OrderTable from './OrderTable';
import { ArrowUpOutlined, ArrowDownOutlined } from '@ant-design/icons';
/**
* 首頁統計圖表
* @author 於公成
*
*/
class IndexPageTable extends Component {
constructor() {
super();
//用戶量
let companyList = [];
let userVolumeList= [];
let option={}
//訂單量
let orderDateList=[];
let orderVolumeList=[];
let option2={}
//用戶部門一週訪問量
let option3={}
let companyList2=[];
let weeks=[];
let weeksVolume=[];
//socket
let messageData;
this.state= {
companyList,
userVolumeList,
option,
orderDateList,
orderVolumeList,
option2,
companyList2,
weeksVolume,
weeks,
option3,
messageData
}
}
componentWillMount(){
}
componentDidMount() {
//部門用戶量
let companyList = this.state.companyList;
let userVolumeList = this.state.userVolumeList;
let flag = this;
//柱狀圖樣式2
this.props.dispatch(
{
type: "query/querySecret",
payload: {
url: config.dataUrl + "/deptinfo/queryAllDeptUserVolume"
},
callback: (result) => {
console.log(result)
if (result.code === 200 && result.data != null) {
result.data.map((item) => {
companyList.push(
item.company,
)
userVolumeList.push(
item.uservolume
)
})
let option = this.state.option = {
// title: {
// text: '世界人口總量',
// subtext: '數據來自網絡'
// },
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
// legend: {
// data: ['2011年', '2012年']
// },
grid: {
left: '1%',
right: '3%',
bottom: '2%',
top:'2%',
containLabel: true
},
xAxis: {
type: 'value',
boundaryGap: [0, 0.01]
},
yAxis: {
type: 'category',
data: companyList,
//Y軸斜
// axisLabel:{
// interval: 0 ,
// rotate:10
// }
},
series: [
{
type: 'bar',
data: userVolumeList,
itemStyle: {
normal: {
//這裏是重點
color: (params) => {
//注意,如果顏色太少的話,後面顏色不會自動循環,最好多定義幾個顏色
// let colorList = ['#c23531', '#2f4554', '#61a0a8', '#d48265', '#91c7ae', '#749f83', '#ca8622'];
// let colorList = ['#37a2da', '#32c5e9', '#67e0e3', '#9fe6b8', '#ffdb5c', '#ff9f7f', '#fb7293','#e062ae','#e690d1'];
let colorList = ['#e690d1', '#e062ae', '#fb7293', '#ff9f7f', '#ffdb5c', '#9fe6b8', '#67e0e3','#32c5e9','#37a2da'];
return colorList[params.dataIndex]
}
}
},
}
]
};
this.setState({
companyList, userVolumeList, option
})
}
}
})
//柱狀圖樣式1
// this.props.dispatch(
// {
// type: "query/querySecret",
// payload: {
// url: config.dataUrl + "/deptinfo/queryAllDeptUserVolume"
// },
// callback: (result) => {
// console.log(result)
// if (result.code === 200 && result.data != null) {
// result.data.map((item) => {
// companyList.push(
// item.company,
// )
// userVolumeList.push(
// item.uservolume
// )
// })
// let option = this.state.option = {
// // color: ['#3398DB','#006699', '#4cabce', '#e5323e'],
// tooltip: {
// trigger: 'axis',
// axisPointer: { // 座標軸指示器,座標軸觸發有效
// type: 'shadow' // 默認爲直線,可選爲:'line' | 'shadow'
// }
// },
// grid: {
// left: '1%',
// right: '3%',
// bottom: '2%',
// top:'2%',
// containLabel: true
// },
//
// xAxis: [
// {
// type: 'category',
// data: companyList,
// axisTick: {
// alignWithLabel: true,
//
// },
// axisLabel: {
// interval: 0, //強制顯示文字
// formatter: (value, index) => {
// // 10 6 這些你自定義就行
// let v = value.substring(0, 6) + '...'
// return value.length > 9 ? v : value
// }
// }
// }
// ],
// yAxis: [
// {
// type: 'value',
// }
// ],
//
// series: [
// {
// name: '用戶量',
// type: 'bar',
// barWidth: '60%',
// data: userVolumeList,
// itemStyle: {
// normal: {
// //這裏是重點
// color: (params) => {
// //注意,如果顏色太少的話,後面顏色不會自動循環,最好多定義幾個顏色
// let colorList = ['#c23531', '#2f4554', '#61a0a8', '#d48265', '#91c7ae', '#749f83', '#ca8622'];
// return colorList[params.dataIndex]
// }
// }
// }
// },
//
// ],
// triggerEvent: false // 設置爲true後,可觸發事件。實現x軸文字過長,顯示省略號,hover上去顯示全部的功能
// };
// this.setState({
// companyList, userVolumeList, option
// })
// }
// }
// })
let orderDateList = this.state.orderDateList;
let orderVolumeList = this.state.orderVolumeList;
//訂單用戶量
this.props.dispatch(
{
type:"query/querySecret",
payload: {
url:config.dataUrl+ "insorder/queryOrderVolume",
},
callback:(result)=>{
console.log(result)
if (result.code === 200 && result.data != null) {
result.data.map((item) => {
orderDateList.push(
moment(item.orderdate).format('YYYY-MM')
)
orderVolumeList.push(
item.ordervolume
)
})
let option2 = this.state.option2 = {
tooltip: {
trigger: 'axis',
},
xAxis: {
type: 'category',
data: orderDateList
},
yAxis: {
type: 'value'
},
grid: {
left: '1%',
right: '4%',
bottom: '2%',
top:'2%',
containLabel: true
},
series: [{
data: orderVolumeList,
type: 'line',
}]
}
this.setState({
orderDateList, orderVolumeList, option2
})
console.log("orderDateList:"+orderDateList)
console.log("orderVolumeList:"+orderVolumeList)
}
}
}
)
//一週部門訪問量
//部門用戶量
let companyList2 = this.state.companyList2;
//餅狀圖樣式
this.props.dispatch(
{
type: "query/querySecret",
payload: {
url: config.dataUrl + "/syslog/queryDeptWeekVolume"
},
callback: (result) => {
console.log(result)
if (result.code === 200 && result.data != null) {
result.data.map((item) => {
companyList2.push(
{
// name:item.company+"-"+item.weeks,
name:item.company,
value:item.uservolume
}
)
// companyList2.push(
// item.company+"-"+item.weeks,
// )
// weeksVolume.push(
// item.uservolume
// )
})
let option3 = this.state.option3 = {
title: {
text: '最近一週訪問量',
// subtext: '純屬虛構',
left: 'center'
},
tooltip: {
trigger: 'item',
// formatter: '{a} <br/>{b} : {c} ({d}%)'
formatter: ' <br/>{b} : {c} ({d}%)'
},
series: [
{
// name: '最近一週訪問量',
type: 'pie',
radius: '55%',
center: ['50%', '55%'],
data: this.state.companyList2,
label: {
normal: {
position: 'inner',
show : false
}
},
itemStyle: {
normal: {
//這裏是重點
color: (params) => {
//注意,如果顏色太少的話,後面顏色不會自動循環,最好多定義幾個顏色
// let colorList = ['#c23531', '#2f4554', '#61a0a8', '#d48265', '#91c7ae', '#749f83', '#ca8622'];
let colorList = ['#37a2da', '#32c5e9', '#67e0e3', '#9fe6b8', '#ffdb5c', '#ff9f7f', '#fb7293','#e062ae','#e690d1'];
// let colorList = ['#e690d1', '#e062ae', '#fb7293', '#ff9f7f', '#ffdb5c', '#9fe6b8', '#67e0e3','#32c5e9','#37a2da'];
return colorList[params.dataIndex]
}
}
},
}
]
};
this.setState({
companyList2, option3
})
}
}
})
//webscoket
let ws = new WebSocket("ws://localhost:12935/20191108_V1.0_xdnx/websocket");
if (typeof (WebSocket) == "undefined") {
console.log("遺憾:您的瀏覽器不支持WebSocket");
} else {
console.log("恭喜:您的瀏覽器支持WebSocket");
ws.onopen = (evt)=> {
console.log("Connection open ...");
ws.send("管理平臺");
ws.send("新增人數");
};
ws.onmessage = (evt)=> {
console.log( "Received Message: " + evt.data);
// alert(evt.data)
let messageData=this.state.messageData;
this.setState({
messageData:evt.data
})
// ws.close();
};
ws.onclose = (evt)=> {
// alert(evt.data)
console.log("Connection closed.");
// ws.close();
};
ws.onerror = (evt)=> {
console.log("error")
};
window.onbeforeunload = (event)=> {
console.log("關閉WebSocket連接!");
ws.send("關閉頁面");
event.close();
}
}
}
render() {
//顯隱控制
if(this.props.layout.status===0){
return (
//一週訪問數,用戶在線數量
<div >
{/*<Card >*/}
{/*</Card>*/}
<div className={styles.override_ant_col} style={{
background: '#ececec',
padding: '10px',
width:'20%',
float:'left',marginTop:'20px'
}}>
<Row gutter={16}>
<Col span={12}>
<Card>
<Statistic
title="管理平臺當前在線人數"
value={this.state.messageData}
precision={0}
valueStyle={{ color: '#3f8600' }}
suffix="人"
/>
</Card>
</Col>
</Row>
</div>,
<div style={{width:'30%',float:'right'}} >
<ReactEcharts option={this.state.option3} />
</div>
<Card className={styles.override_ant_card_title} style={{marginLeft:'20%',width:'50%',display:'table',marginBottom:'30px'}} title="註冊用戶統計">
<ReactEcharts option={this.state.option} />
</Card >
<div style={{width:'48%',float:'right'}} >
<OrderTable />
</div>
<Card className={styles.override_ant_card_title} style={{width:'50%',display:'table',marginBottom:'30px'}} title="訂單數量統計">
<ReactEcharts option={this.state.option2} />
</Card>
</div>
)
}else{
return (
<div>
</div>
)
}
}
}
function mapStateToProps(state) {
return {
layout:state.layout,
verify:state.verify,
user:state.user
};
}
export default connect(mapStateToProps)(IndexPageTable);
實現結果