前言
🚀 基于 vue、datav、Echart 框架的大数据可视化(大屏展示)源码,基于VUE+Echarts 制作,实现大数据可视化。通过 vue 组件实现数据动态刷新渲染,内部图表可自由替换。部分图表使用 DataV 自带组件,可自由进行更改, 可以在此基础上重新开发。
本项目中使用的是echarts图表库,ECharts 提供了常规的折线图、柱状图、散点图、饼图、K线图,用于统计的盒形图,用于地理数据可视化的地图、热力图、线图,用于关系数据可视化的关系图、treemap、旭日图,多维数据可视化的平行坐标,还有用于 BI 的漏斗图,仪表盘,并且支持图与图之间的混搭。
文章目录
- 前言
- 一、Echart是什么
- 二、ECharts入门教程
- 三、作品演示
- 四、代码实现
- main.js
- App.vue
- index.vue
- 五、更多干货
一、Echart是什么
ECharts是一个使用 JavaScript 实现的开源可视化库,可以流畅的运行在 PC 和移动设备上,兼容当前绝大部分浏览器(IE8/9/10/11,Chrome,Firefox,Safari等),底层依赖矢量图形库 ZRender,提供直观,交互丰富,可高度个性化定制的数据可视化图表。
二、ECharts入门教程
三、作品演示
四、代码实现
main.js
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import * as echarts from 'echarts'
import './mock/mockServer'
Vue.prototype.$echarts = echarts
Vue.config.productionTip = false
Vue.use(ElementUI);
/* eslint-disable no-new */
new Vue({
el: '#app',
components: { App },
template: '<App/>'
})
App.vue
<template>
<div id="app">
<Echarts></Echarts>
</div>
</template>
<script>
import Echarts from '@/views/echarts/index'
export default {
name: "App",
components: {
Echarts
},
data() {
return {
};
},
watch: {},
methods: {
},
};
</script>
<style lang="scss">
#app {
height: 100%;
}
</style>
index.vue
<template>
<div class="data-container">
<div class="header">
智慧体检数据中心
<div class="date-time-wrap">
<div class="time">{{ dateObj.time }}</div>
<div class="date-wrap">
<span class="date">{{ dateObj.date }}</span>
<span class="week">{{ dateObj.week }}</span>
</div>
</div>
</div>
<div class="content">
<div class="column left">
<div class="chart-box peno-num">
<div class="title-bg">
<span class="title">当日实时体检人数</span>
</div>
<div class="wrap">
<div>
<span class="ele-number">{{ peNum }}</span>
<span class="unit">人</span>
</div>
</div>
</div>
<div class="chart-box customer-analysis">
<div class="title-bg">
<span class="title">体检客户分析</span>
</div>
<Chart v-for="(item, index) in pieDataList" :key="index" chart-type="pie" :extra-option="item">
</Chart>
</div>
</div>
<div class="column center">
<div class="chart-box time-distribution">
<Chart chart-type="line" :extra-option="lineOption"></Chart>
</div>
<div class="chart-box customer-distribution">
<Chart chart-type="bar" :extra-option="barOption1"></Chart>
</div>
<div class="chart-box type-distribution">
<Chart chart-type="bar" :extra-option="barOption2"></Chart>
</div>
</div>
<div class="column right">
<div class="chart-box peno-income">
<div class="title-bg">
<span class="title">体检收入</span>
</div>
<div class="total-income">
<span class="ele-number">{{incomeData.totalMoney}}</span>
<span class="unit">元</span>
</div>
<div class="wrap">
<div class="income">
<span class="label">个检收入</span>
<span class="money">{{incomeData.peMoney}}</span>
</div>
<div class="income">
<span class="label">团检收入</span>
<span class="money">{{incomeData.teamMoney}}</span>
</div>
<div class="income">
<span class="label">人均金额</span>
<span class="money">{{incomeData.averageMoney}}</span>
</div>
</div>
</div>
<div class="chart-box dept-workload">
<div class="title">科室工作量分布</div>
<AutoScrollTable :data-list="deptWorkload"></AutoScrollTable>
</div>
<div class="chart-box item-workload">
<Chart chart-type="bar" :extra-option="barOption3"></Chart>
</div>
</div>
</div>
</div>
</template>
<script>
import Chart from "@/views/echarts/components/chart";
import AutoScrollTable from "@/views/echarts/components/autoScrollTable";
import { fontSize, parseTime } from "@/util.js";
import { getCurrentPeNum, getPieData, getLineData, getBarData, getIncome,getDeptWorkload } from "@/api/getData";
export default {
name: "",
components: {
Chart,
AutoScrollTable,
},
props: {},
data() {
return {
dateObj: {
date: "",
week: "",
time: ""
},
dateTimer: null,
peNum: 0,
incomeData: {
totalMoney: 0,
peMoney: 0,
teamMoney: 0,
averageMoney: 0,
},
deptWorkload: [],
pieDataList: [
{
color: ["#FF7006", "#005AFF"],
series: [
{
label: {
// formatter: "客户来源",
},
data: [
// { value: 400, name: "团体" },
// { value: 200, name: "个人" },
],
},
],
legend: {
formatter: null
},
},
{
color: ["#03B6FF", "#C530FD"],
series: [
{
label: {
// formatter: "客户性别",
},
data: [
// { value: 400, name: "男性" },
// { value: 200, name: "女性" },
],
},
],
legend: {
formatter: null
},
},
{
color: ["#35D263", "#F19610"],
series: [
{
label: {
// formatter: "服务类型",
},
data: [
// { value: 400, name: "贵宾" },
// { value: 200, name: "普通" },
],
},
],
legend: {
formatter: null
},
},
],
lineOption: {
title: {
text: "体检客户时段分布",
},
legend: {
data: []
// data: ["开始体检数", "结束体检数"],
// width: 200
},
xAxis: {
data: ["7", "8", "9", "10", "11", "12", "13", "14", "15", "16"],
},
series: [
// {
// name: "开始体检数",
// data: [120, 132, 101, 134, 90, 230, 210, 150, 200, 120],
// },
// {
// name: "结束体检数",
// data: [220, 182, 191, 234, 290, 330, 310, 230, 210, 200],
// },
],
},
barOption1: {
color: ["#01B6FF", "#C530FD"],
title: {
text: "体检区域客户分布",
},
legend: {
// data: ["男性", "女性"],
},
xAxis: {
data: ["一楼", "二楼", "三楼", "四楼"],
},
series: [
// {
// name: "男性",
// data: [320, 302, 301, 334],
// type: "bar",
// label: {
// show: true,
// fontSize: fontSize(18),
// },
// emphasis: {
// focus: "series",
// },
// barWidth: "40%",
// stack: "total",
// },
// {
// name: "女性",
// data: [120, 132, 101, 134],
// type: "bar",
// label: {
// show: true,
// fontSize: fontSize(18),
// },
// emphasis: {
// focus: "series",
// },
// barWidth: "40%",
// stack: "total",
// },
],
},
barOption2: {
color: ["#F19610", "#01B6FF", "#C530FD"],
title: {
text: "体检类型分布",
},
legend: {
// data: ["总人数", "男性", "女性"],
},
xAxis: {
type: "value",
splitLine: {
show: true,
lineStyle: {
color: "#031E77",
},
},
},
yAxis: {
type: "category",
data: ["健康体检", "公务员体检", "招工体检", "儿童体检", "合计"],
},
series: [
// {
// data: [100, 150, 200, 220, 670],
// name: "总人数",
// type: "bar",
// label: {
// show: true,
// position: "right",
// color: "#0EFCFF",
// fontSize: fontSize(14),
// },
// emphasis: {
// focus: "series",
// },
// itemStyle: {
// borderRadius: [0, 50, 50, 0],
// },
// },
// {
// data: [30, 80, 100, 120, 330],
// name: "男性",
// type: "bar",
// label: {
// show: true,
// position: "right",
// color: "#0EFCFF",
// fontSize: fontSize(14),
// },
// emphasis: {
// focus: "series",
// },
// itemStyle: {
// borderRadius: [0, 50, 50, 0],
// },
// },
// {
// data: [70, 70, 100, 100, 340],
// name: "女性",
// type: "bar",
// label: {
// show: true,
// position: "right",
// color: "#0EFCFF",
// fontSize: fontSize(14),
// },
// emphasis: {
// focus: "series",
// },
// itemStyle: {
// borderRadius: [0, 50, 50, 0],
// },
// },
],
},
barOption3: {
color: ["#01B6FF", "#C530FD"],
title: {
text: "主要体检项目工作量分布",
},
legend: {
// data: ["男性", "女性"],
},
xAxis: {
type: "value",
splitLine: {
show: false,
lineStyle: {
color: "#031E77",
},
},
},
yAxis: {
type: "category",
data: ["DR", "CT", "心脏彩超", "腹部超声", "MR"],
splitLine: {
show: false,
lineStyle: {
color: "#031E77",
},
},
axisLabel: {
color: "#fff",
},
},
series: [
// {
// data: [30, 80, 100, 120, 330],
// name: "男性",
// type: "bar",
// stack: "total",
// label: {
// show: true,
// position: "inside",
// color: "#fff",
// fontSize: fontSize(14),
// },
// emphasis: {
// focus: "series",
// },
// itemStyle: {
// borderRadius: 0,
// },
// barWidth: "30%",
// },
// {
// data: [70, 70, 100, 100, 340],
// name: "女性",
// type: "bar",
// stack: "total",
// label: {
// show: true,
// position: "inside",
// // color: "#0EFCFF",
// fontSize: fontSize(14),
// },
// emphasis: {
// focus: "series",
// },
// itemStyle: {
// borderRadius: [0, 10, 10, 0],
// },
// },
],
},
dataTimer: null
};
},
computed: {},
watch: {},
created() {
this.getCurrentTime();
this.getAllData();
this.dataTimer = setInterval(() => {
this.getAllData();
}, 10* 60 * 1000)
},
mounted() {
this.theme("dark");
},
beforeDestroy() {
this.dataTimer && clearInterval(this.dataTimer)
},
methods: {
theme(type) {
window.document.documentElement.setAttribute("data-theme", type);
},
// 实时获取当前时间
getCurrentTime() {
this.dateTimer && clearTimeout(this.dateTimer);
this.dateTimer = setTimeout(() => {
this.dateObj.date = parseTime(new Date(), "{y}/{m}/{d}");
this.dateObj.week = `星期${parseTime(new Date(), "{a}")}`;
this.dateObj.time = parseTime(new Date(), "{h}:{i}:{s}");
this.getCurrentTime();
}, 1000);
},
// 获取全部数据
getAllData() {
// 获取实时体检人数
getCurrentPeNum().then((res) => {
console.log("实时体检人数", res);
this.peNum = res.data.peNum;
});
// 获取饼图数据
getPieData().then((res) => {
console.log("饼图数据", res);
const pieData = res.data;
pieData.forEach((item,index) => {
this.pieDataList[index].series[0].label.formatter = item.label
this.pieDataList[index].series[0].data = item.list
this.pieDataList[index].legend.formatter = (name) => {
// 业务数据源
let data = this.pieDataList[index].series[0].data;
let totalVal = 0;
let targetVal = 0;
for (let i = 0; i < data.length; i++) {
totalVal += data[i].value;
if (data[i].name === name) {
targetVal = data[i].value;
}
}
let arr = [
`{a|${name} ${targetVal}人}`,
`{b|${((targetVal / totalVal) * 100).toFixed()}%}`,
];
return arr.join(" ");
}
})
});
// 获取折线图数据
getLineData().then(res => {
console.log("折线图数据", res);
const legendName = []
const series = []
res.data.forEach((item) => {
legendName.push(item.name)
series.push({
name: item.name,
data: item.data
})
})
this.lineOption.legend.data = legendName
this.lineOption.series = series
})
// 获取柱状图数据
getBarData().then(res => {
console.log("柱状图数据", res);
// barOption1
let legendName = []
let series = []
res.barOption1.forEach((item) => {
legendName.push(item.name)
series.push({
name: item.name,
data: item.data,
type: "bar",
label: {
show: true,
fontSize: fontSize(18),
},
emphasis: {
focus: "series",
},
barWidth: "40%",
stack: "total"
})
})
this.barOption1.legend.data = legendName
this.barOption1.series = series
// barOption2
legendName = []
series = []
res.barOption2.forEach((item) => {
legendName.push(item.name)
series.push({
name: item.name,
data: item.data,
type: "bar",
label: {
show: true,
position: "right",
color: "#0EFCFF",
fontSize: fontSize(14),
},
emphasis: {
focus: "series",
},
itemStyle: {
borderRadius: [0, 50, 50, 0],
}
})
})
this.barOption2.legend.data = legendName
this.barOption2.series = series
// barOption3
legendName = []
series = []
res.barOption3.forEach((item, index) => {
legendName.push(item.name)
series.push({
name: item.name,
data: item.data,
type: "bar",
stack: "total",
label: {
show: true,
position: "inside",
color: "#fff",
fontSize: fontSize(14),
},
emphasis: {
focus: "series",
},
itemStyle: {
borderRadius: index+1 === res.barOption3.length ? [0, 10, 10, 0] : 0,
},
barWidth: "30%"
})
})
this.barOption3.legend.data = legendName
this.barOption3.series = series
})
// 获取体检收入
getIncome().then((res) => {
console.log("实时体检收入", res);
this.incomeData = res.data.incomeData;
});
// 获取科室工作量
getDeptWorkload().then(res => {
console.log("科室工作量", res);
this.deptWorkload = res.data
})
},
},
};
</script>
<style scoped lang='scss'>
@font-face {
font-family: electronicFont;
src: url(/static/font/DS-DIGIT.TTF);
}
@import "@/style/_handle.scss";
// 数据大屏容器
.data-container {
height: 100%;
background-image: url(/static/img/bg.png);
background-size: contain;
// 头部标题
.header {
position: relative;
height: 1.025rem;
background: url(/static/img/title_bg.png) no-repeat;
background-size: 100% 100%;
box-sizing: border-box;
font-size: 0.45rem;
text-align: center;
@include font_color("title_color");
line-height: 1.025rem;
font-weight: 700;
font-family: Microsoft YaHei Bold, Microsoft YaHei Bold-Bold;
// 顶部右侧时钟
.date-time-wrap {
position: absolute;
top: 0;
right: 0;
height: 0.5625rem;
display: flex;
align-items: center;
.time {
height: 100%;
width: 1.25rem;
font-size: 0.3rem;
font-weight: 700;
@include font_color("time_color");
display: flex;
align-items: center;
}
.date-wrap {
height: 100%;
display: flex;
flex-flow: column;
align-items: center;
font-size: 0.175rem;
@include font_color("time_color");
margin: 0 0.125rem 0 0.25rem;
.date,
.week {
height: 100%;
line-height: 0;
display: flex;
align-items: center;
}
}
}
}
// 内容
.content {
height: calc(100vh - 1.025rem);
box-sizing: border-box;
display: flex;
.column {
// flex: 1;
box-sizing: border-box;
display: flex;
flex-flow: column;
margin: 0.1875rem 0.25rem;
&.left {
min-width: 5.3375rem;
}
&.center {
min-width: 8.125rem;
}
&.right {
min-width: 9.125rem;
}
// 图标盒子通用样式
.chart-box {
position: relative;
border-width: 1px;
border-style: solid;
@include border_color("chart_box_border_color");
@include themeify {
box-shadow: 0 0 20px 0 themed("box_shadow_color") inset;
}
// 菱形标题框样式
.title-bg {
height: 0.675rem;
width: 3.0625rem;
position: absolute;
top: 0;
left: 50%;
transform: translate(-50%, -50%);
z-index: 1;
background: url(/static/img/box_title_bg.png) no-repeat;
background-size: 100% 100%;
.title {
height: 0.675rem;
width: 3.0625rem;
display: flex;
justify-content: center;
align-items: center;
@include themeify {
background: linear-gradient(
to right,
themed("chart_box_left_color") 0%,
themed("chart_box_center_color") 49%,
themed("chart_box_right_color") 100%
);
-webkit-background-clip: text;
}
color: transparent;
font-size: 0.3rem;
font-family: Microsoft YaHei Bold, Microsoft YaHei Bold-Bold;
font-weight: 700;
}
}
}
// 当日体检人数
.peno-num {
// flex: 1;
height: 2.5rem;
.wrap {
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
}
// 体检客户分析
.customer-analysis {
flex: 1;
margin-top: 0.6rem;
padding: 0.7125rem 0.25rem 0.125rem 0.25rem;
display: flex;
flex-flow: column;
justify-content: space-around;
.chart {
flex: 1;
}
}
// 时段分布
.time-distribution {
flex: 1;
}
// 客户分布
.customer-distribution {
flex: 1;
margin-top: 0.2rem;
}
// 类型分布
.type-distribution {
flex: 1;
margin-top: 0.2rem;
}
// 体检收入
.peno-income {
flex: 1;
display: flex;
flex-flow: column;
justify-content: center;
align-items: center;
.total-income {
//
}
.wrap {
width: 100%;
display: flex;
justify-content: space-around;
.income {
flex: 1;
display: flex;
flex-flow: column;
align-items: center;
.label {
@include font_color("income_lable_color");
font-size: 0.25rem;
font-weight: 700;
}
.money {
@include font_color("income_money_color");
font-size: 0.3rem;
font-weight: 700;
}
}
}
}
// 科室工作量
.dept-workload {
flex: 1;
margin-top: 0.2rem;
overflow: hidden;
.title {
text-align: center;
@include font_color("box_title_color");
font-size: 0.3rem;
font-weight: 700;
}
}
// 项目工作量
.item-workload {
flex: 1;
margin-top: 0.2rem;
}
}
}
// 电子字体
.ele-number {
font-size: 0.9rem;
@include font_color("ele_color");
font-family: electronicFont;
font-weight: Digital-Bold;
}
.unit {
font-size: 0.225rem;
@include font_color("ele_unit_color");
}
}
</style>