SpringBoot整合Echarts實(shí)現(xiàn)數(shù)據(jù)大屏
效果展示


MySQL
建表
CREATE TABLE `access_log` ( `access_log_id` bigint NOT NULL AUTO_INCREMENT, `access_time` datetime NOT NULL COMMENT '訪問(wèn)時(shí)間', `access_ip` varchar(30) NOT NULL COMMENT '訪問(wèn)IP', `api_group` varchar(50) NOT NULL DEFAULT '默認(rèn)' COMMENT '接口分組', `req_url` varchar(100) NOT NULL COMMENT '請(qǐng)求URL', `req_method` varchar(10) NOT NULL COMMENT '請(qǐng)求方式', `os` varchar(100) NULL DEFAULT NULL COMMENT '操作系統(tǒng)', `browser` varchar(50) NULL DEFAULT NULL COMMENT '瀏覽器', `lsp` varchar(15) NULL DEFAULT NULL COMMENT '運(yùn)營(yíng)商', `country` varchar(15) NULL DEFAULT NULL COMMENT '國(guó)家', `province` varchar(15) NULL DEFAULT NULL COMMENT '省', `city` varchar(15) NULL DEFAULT NULL COMMENT '城市', PRIMARY KEY (`access_log_id`) ) COMMENT='訪問(wèn)日志表';
后端
POJO實(shí)體
@Data
@TableName(value = "access_log")
public class AccessLog implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 訪問(wèn)日志ID
*/
@TableId(type = IdType.AUTO)
private Long accessLogId;
/**
* 訪問(wèn)時(shí)間
*/
private Date accessTime;
/**
* 訪問(wèn)IP
*/
private String accessIp;
/**
* 接口分組
*/
private String apiGroup;
/**
* 請(qǐng)求URL
*/
private String reqUrl;
/**
* 請(qǐng)求方式
*/
private String reqMethod;
/**
* 操作系統(tǒng)
*/
private String os;
/**
* 瀏覽器
*/
private String browser;
/**
* 運(yùn)營(yíng)商
*/
private String lsp;
/**
* 國(guó)家
*/
private String country;
/**
* 省
*/
private String province;
/**
* 城市
*/
private String city;
}
Mapper接口
@Repository
public interface AccessLogMapper extends BaseMapper<AccessLog> {
}
自定義注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log {
/**
* 給接口分組
*/
String apiGroup() default "默認(rèn)";
}
案例:
@Log(apiGroup = "文章模塊")
@RestController
@RequestMapping("/article")
public class ArticleController {
......
}
攔截器
ps:https://api.vvhan.com/api/getIpInfo?ip=[你的IP],這個(gè)網(wǎng)址是一個(gè)免費(fèi)獲取國(guó)家、省、市、運(yùn)營(yíng)商的地址
當(dāng)然這種對(duì)IP地址的解析應(yīng)該是放在定時(shí)任務(wù)中,每天晚上定時(shí)解析日志IP,如果解析IP的API掛了,接口會(huì)受到影響,我這里只是為了方便寫(xiě)在這里
@Slf4j
@Component
public class AccessLogInterceptor implements HandlerInterceptor {
@Autowired
private AccessLogMapper accessLogMapper;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (!(handler instanceof HandlerMethod)) {
return true;
}
try {
// 獲取客戶(hù)端真是IP地址,這種網(wǎng)上很多現(xiàn)成代碼
String accessIp = NetUtil.getRemoteHost(request);
// 獲取User-Agent
String requestUserAgent = request.getHeader("User-Agent");
// 獲取瀏覽器用戶(hù)標(biāo)識(shí)
UserAgent userAgent = UserAgentUtil.parse(requestUserAgent);
HandlerMethod handlerMethod = (HandlerMethod) handler;
Log logAnnotation = handlerMethod.getMethod().getDeclaringClass().getAnnotation(Log.class);
AccessLog accessLog = new AccessLog();
accessLog.setAccessIp(accessIp);
accessLog.setAccessTime(new Date());
if (logAnnotation != null) {
accessLog.setApiGroup(logAnnotation.apiGroup());
}
accessLog.setReqUrl(request.getRequestURI());
accessLog.setReqMethod(request.getMethod());
accessLog.setOs(userAgent.getOs().getName());
accessLog.setBrowser(userAgent.getBrowser().getName());
// 解析IP
try {
String ipParseStr = HttpUtil.get("https://api.vvhan.com/api/getIpInfo?ip=" + accessIp);
JSONObject ipParseJson = JSONUtil.parseObj(ipParseStr);
if (ipParseJson.getBool("success")) {
JSONObject infoJson = ipParseJson.getJSONObject("info");
accessLog.setLsp(infoJson.getStr("lsp"));
accessLog.setCountry(infoJson.getStr("country"));
accessLog.setProvince(infoJson.getStr("prov"));
accessLog.setCity(infoJson.getStr("city"));
}
} catch (Exception e) {
accessLog.setLsp("未知");
accessLog.setCountry("未知");
accessLog.setProvince("未知");
accessLog.setCity("未知");
}
accessLogMapper.insert(accessLog);
} catch (Exception e) {
log.error("", e);
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
}
注冊(cè)攔截器
@Configuration
public class WebMVCConfig implements WebMvcConfigurer {
@Autowired
private AccessLogInterceptor accessLogInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注冊(cè)全局日志攔截器
registry.addInterceptor(accessLogInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/static/**")
.excludePathPatterns("/error");
// 其他攔截器......
}
}
測(cè)試
到這里就完成一個(gè)簡(jiǎn)單的全局日志攔截器了,隨便發(fā)幾個(gè)請(qǐng)求測(cè)試一下,成功記錄入庫(kù)!

進(jìn)階——整合Echarts實(shí)現(xiàn)數(shù)據(jù)大屏
數(shù)據(jù)VO實(shí)體
瀏覽器訪問(wèn)占比情況VO
@Data
public class AccessBrowserGroupVo {
private String browser;
private Integer count;
}
運(yùn)營(yíng)商訪問(wèn)占比情況VO
@Data
public class AccessLspGroupVo {
private String lsp;
private Integer count;
}
各省份訪問(wèn)情況VO
@Data
public class AccessProvinceGroupVo {
private String province;
private Integer count;
}
每天訪問(wèn)情況VO
@Data
public class AccessTimeGroupVo {
private String accessTime;
private Integer count;
}
查詢(xún)SQL
瀏覽器訪問(wèn)統(tǒng)計(jì)
<select id="countBrowserGroupAccess" resultType="com.xiaoyuan.common.vo.logs.AccessBrowserGroupVo"> select browser, count(*) as count from access_log group by browser </select>
運(yùn)營(yíng)商訪問(wèn)統(tǒng)計(jì)
<select id="countLspGroupAccess" resultType="com.xiaoyuan.common.vo.logs.AccessLspGroupVo"> select lsp, count(*) as count from access_log group by lsp </select>
各省份訪問(wèn)統(tǒng)計(jì)
<select id="countProvinceGroupAccess" resultType="com.xiaoyuan.common.vo.logs.AccessProvinceGroupVo"> select province, count(*) as count from access_log group by province order by count desc limit 15 </select>
近15天內(nèi)訪問(wèn)統(tǒng)計(jì)
<select id="countTimeGroupAccess" resultType="com.xiaoyuan.common.vo.logs.AccessTimeGroupVo">
SELECT
date_match.date_c as access_time,
IFNULL( count, 0 ) as count
FROM
(
SELECT
DATE_FORMAT( @now := date_sub( @now, INTERVAL 1 DAY ), '%Y-%m-%d' ) AS date_c
FROM
( SELECT @now := date_add( CURDATE(), INTERVAL 1 DAY ) FROM access_log LIMIT 15 ) date_match
ORDER BY
date_c
) date_match
LEFT JOIN (
SELECT
DATE_FORMAT( access_time, '%Y-%m-%d' ) AS access_time,
count(*) AS count
FROM
access_log
WHERE
access_time >= (
SELECT
date_sub( curdate(), INTERVAL 15 DAY ))
GROUP BY
DATE_FORMAT( access_time, '%Y-%m-%d' )
) acc ON acc.access_time = date_match.date_c
</select>
后端業(yè)務(wù)
封裝統(tǒng)一接口返回
/**
* FileName: R
* Author: 小袁
* Date: 2022/3/12 12:23
* Description: 統(tǒng)一結(jié)果返回的類(lèi)
*/
@Data
public class R<T> {
private Boolean success;
private Integer code;
private String message;
private T data;
// 成功靜態(tài)方法
public static <T> R<T> success() {
R<T> r = new R<>();
r.setSuccess(true);
r.setCode(HttpStatusEnum.SUCCESS.getCode());
r.setMessage(HttpStatusEnum.SUCCESS.getName());
return r;
}
public static <T> R<T> success(String message) {
R<T> r = new R<>();
r.setSuccess(true);
r.setCode(HttpStatusEnum.SUCCESS.getCode());
r.message(message);
return r;
}
public static <T> R<T> success(T object) {
R<T> r = new R<>();
r.setData(object);
r.setSuccess(true);
r.setCode(HttpStatusEnum.SUCCESS.getCode());
r.setMessage(HttpStatusEnum.SUCCESS.getName());
return r;
}
public static <T> R<T> success(String msg, T object) {
R<T> r = new R<>();
r.setData(object);
r.setCode(HttpStatusEnum.SUCCESS.getCode());
r.setMessage(msg);
return r;
}
// 失敗靜態(tài)方法
public static <T> R<T> fail() {
R<T> r = new R<>();
r.setSuccess(false);
r.setCode(HttpStatusEnum.FAIL.getCode());
r.setMessage(HttpStatusEnum.FAIL.getName());
return r;
}
public static <T> R<T> fail(String msg) {
R<T> r = new R<>();
r.setSuccess(false);
r.setCode(HttpStatusEnum.FAIL.getCode());
r.setMessage(msg);
return r;
}
public static <T> R<T> fail(HttpStatusEnum httpStatusEnum) {
R<T> r = new R<>();
r.setSuccess(false);
r.setCode(httpStatusEnum.getCode());
r.setMessage(httpStatusEnum.getName());
return r;
}
public R<T> message(String message){
this.setMessage(message);
return this;
}
public R<T> code(Integer code){
this.setCode(code);
return this;
}
public R<T> data(T data){
this.setData(data);
return this;
}
}
封裝客戶(hù)端響應(yīng)碼
/**
* FileName: Code
* Author: 小袁
* Date: 2022/5/1 23:29
* Description: 客戶(hù)端響應(yīng)狀態(tài)碼
*/
public enum HttpStatusEnum implements BaseCodeEnum {
SUCCESS(200, "成功"),
FAIL(20001, "失敗"),
INTERNAL_SERVER_ERROR(500, "服務(wù)器異常"),
private final Integer code;
private final String name;
HttpStatusEnum(int code, String msg) {
this.code = code;
this.name = msg;
}
@Override
public Integer getCode() {
return this.code;
}
@Override
public String getName() {
return this.name;
}
}
Mapper接口
@Repository
public interface AccessLogMapper extends BaseMapper<AccessLog> {
List<AccessLspGroupVo> countLspGroupAccess();
List<AccessBrowserGroupVo> countBrowserGroupAccess();
List<AccessProvinceGroupVo> countProvinceGroupAccess();
List<AccessTimeGroupVo> countTimeGroupAccess();
}
Service接口
public interface AccessLogService extends IService<AccessLog> {
List<AccessLspGroupVo> countLspGroupAccess();
List<AccessBrowserGroupVo> countBrowserGroupAccess();
List<AccessProvinceGroupVo> countProvinceGroupAccess();
List<AccessTimeGroupVo> countTimeGroupAccess();
}
Service實(shí)現(xiàn)類(lèi)
@Slf4j
@Service
public class AccessLogServiceImpl extends ServiceImpl<AccessLogMapper, AccessLog> implements AccessLogService {
@Override
public List<AccessLspGroupVo> countLspGroupAccess() {
return this.baseMapper.countLspGroupAccess();
}
@Override
public List<AccessBrowserGroupVo> countBrowserGroupAccess() {
return this.baseMapper.countBrowserGroupAccess();
}
@Override
public List<AccessProvinceGroupVo> countProvinceGroupAccess() {
return this.baseMapper.countProvinceGroupAccess();
}
@Override
public List<AccessTimeGroupVo> countTimeGroupAccess() {
return this.baseMapper.countTimeGroupAccess();
}
}
Controller接口
@RestController
@RequestMapping("/stat/access")
public class AccessStatController {
@Autowired
private AccessLogService accessLogService;
/**
* 查詢(xún)15天內(nèi)的訪問(wèn)次數(shù)情況-折線圖
*/
@GetMapping("/query_line_by_day")
public R<List<AccessTimeGroupVo>> queryAccessLogByTimeGroup() {
return R.success(accessLogService.countTimeGroupAccess());
}
/**
* 查詢(xún)省份訪問(wèn)占比-柱形圖
*/
@GetMapping("/query_col_by_province")
public R<List<AccessProvinceGroupVo>> queryAccessLogByProvinceGroup() {
return R.success(accessLogService.countProvinceGroupAccess());
}
/**
* 查詢(xún)運(yùn)營(yíng)商訪問(wèn)占比-餅圖
*/
@GetMapping("/query_pie_by_lsp")
public R<List<AccessLspGroupVo>> queryAccessLogByLspGroup() {
return R.success(accessLogService.countLspGroupAccess());
}
/**
* 查詢(xún)?yōu)g覽器訪問(wèn)占比-餅圖
*/
@GetMapping("/query_pie_by_browser")
public R<List<AccessBrowserGroupVo>> queryAccessLogByBrowserGroup() {
return R.success(accessLogService.countBrowserGroupAccess());
}
}
前端配置
安裝axios、echarts
npm install axios npm install echarts
封裝request
import axios from 'axios'
import { Message, MessageBox,} from 'element-ui'
import store from '../store'
import { getToken } from '@/utils/auth'
import router from '@/router'
// 創(chuàng)建axios實(shí)例
const service = axios.create({
baseURL: process.env.BASE_API, // api 的 base_url
// timeout: 5000 // 請(qǐng)求超時(shí)時(shí)間
})
// request攔截器
service.interceptors.request.use(
config => {
if (store.getters.token) {
config.headers['token'] = getToken()
}
return config
},
error => {
// Do something with request error
console.log(error) // for debug
Promise.reject(error)
}
)
// response 攔截器
service.interceptors.response.use(
response => {
/**
* code為非200是拋錯(cuò) 可結(jié)合自己業(yè)務(wù)進(jìn)行修改
*/
const res = response.data
const url = response.config.url
if (res.code !== 200) {
if (url.indexOf("/login") < 0 && res.code === 40005) {
store.dispatch('FedLogOut').then(() => {
router.push(`/login`)
})
Message({
message: res.message,
type: 'warning',
duration: 2 * 1000,
})
return Promise.resolve(res)
}else if (res.code >= 40000) {
Message({
message: res.message,
type: 'error',
duration: 3 * 1000
})
return Promise.resolve(res)
}else {
Message({
message: res.message,
type: 'error',
duration: 5 * 1000
})
return Promise.reject(new Error(res.message || 'Error'))
}
} else {
return res
}
},
error => {
Message({
message: error.message,
type: 'error',
duration: 5 * 1000
})
return Promise.reject(error)
}
)
export default service
定義API
import request from "../../utils/request";
export default {
getAccessStatByTime() {
return request({
url: '/stat/access/query_line_by_day',
method: 'get'
})
},
getAccessStatByProvince() {
return request({
url: '/stat/access/query_col_by_province',
method: 'get'
})
},
getAccessStatByLsp() {
return request({
url: '/stat/access/query_pie_by_lsp',
method: 'get'
})
},
getAccessStatByBrowser() {
return request({
url: '/stat/access/query_pie_by_browser',
method: 'get'
})
},
}
封裝echarts
<template>
<div :id="id" :class="className" :style="{ height: height, width: width }" />
</template>
<script>
import * as echarts from 'echarts'
export default {
name: 'echart',
props: {
className: {
type: String,
default: 'chart'
},
id: {
type: String,
default: 'chart'
},
width: {
type: String,
default: '100%'
},
height: {
type: String,
default: '2.5rem'
},
options: {
type: Object,
default: ()=>({})
}
},
data () {
return {
chart: null
}
},
watch: {
options: {
handler (options) {
// 設(shè)置true清空echart緩存
this.chart.setOption(options, true)
},
deep: true
}
},
mounted () {
// echarts.registerTheme('tdTheme', tdTheme); // 覆蓋默認(rèn)主題
this.initChart();
},
beforeDestroy () {
this.chart.dispose()
this.chart = null
},
methods: {
initChart () {
// 初始化echart
this.chart = echarts.init(this.$el)
this.chart.setOption(this.options, true)
}
}
}
</script>
<style>
body {
margin: 0;
padding: 0;
}
</style>
餅圖-瀏覽器訪問(wèn)占比
<template>
<div>
<Echart :options="options"
id="lspEcharts"
height="300px"
width="100%"/>
</div>
</template>
<script>
import statAccess from "@/api/stat/statAccess";
import Echart from "@/components/Echart/index.vue";
export default {
components: {
Echart
},
data() {
return {
options: {},
lspData: []
}
},
methods: {
initData() {
statAccess.getAccessStatByBrowser().then(res => {
this.lspData = res.data.map(obj => {
return {
name: obj.browser,
value: obj.count
}
})
this.executeDraw()
})
},
executeDraw() {
this.options = {
title: {
text: '瀏覽器訪問(wèn)占比',
left: 'center',
textStyle: {
color: '#FDF5E6'
}
},
tooltip: {
trigger: 'item'
},
legend: {
orient: 'vertical',
left: 'left',
top: '20%',
textStyle: {
color: '#FDF5E6'
}
},
series: [
{
name: '訪問(wèn)次數(shù)',
type: 'pie',
radius: '90%',
top: '20%',
data: this.lspData,
label: {
color: '#FDF5E6'
},
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
}
}
},
mounted() {
this.initData()
}
}
</script>
餅圖-運(yùn)營(yíng)商訪問(wèn)占比
<template>
<div>
<Echart :options="options"
id="lspEcharts"
height="300px"
width="100%"/>
</div>
</template>
<script>
import statAccess from "@/api/stat/statAccess";
import Echart from "@/components/Echart/index.vue";
export default {
components: {
Echart
},
data() {
return {
options: {},
lspData: []
}
},
methods: {
initData() {
statAccess.getAccessStatByLsp().then(res => {
this.lspData = res.data.map(obj => {
return {
name: obj.lsp,
value: obj.count
}
})
this.executeDraw()
})
},
executeDraw() {
this.options = {
title: {
text: '運(yùn)營(yíng)商訪問(wèn)占比',
left: 'center',
textStyle: {
color: '#FDF5E6'
}
},
tooltip: {
trigger: 'item'
},
legend: {
orient: 'vertical',
left: 'left',
top: '20%',
textStyle: {
color: '#FDF5E6'
}
},
series: [
{
name: '訪問(wèn)次數(shù)',
type: 'pie',
radius: '90%',
top: '20%',
data: this.lspData,
label: {
color: '#FDF5E6'
},
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
}
}
},
mounted() {
this.initData()
}
}
</script>
折線圖-每天訪問(wèn)量情況
<template>
<div>
<Echart :options="options"
id="timeEcharts"
height="400px"
width="100%"/>
</div>
</template>
<script>
import statAccess from "@/api/stat/statAccess";
import Echart from "@/components/Echart/index.vue";
export default {
components: {
Echart
},
data() {
return {
options: {},
xAxis: [],
yAxis: []
}
},
methods: {
initData() {
statAccess.getAccessStatByTime().then(res => {
let x = []
let y = []
for (let i = 0; i < res.data.length; i++) {
x.push(res.data[i].accessTime)
y.push(res.data[i].count)
}
this.xAxis = x
this.yAxis = y
this.executeDraw()
})
},
executeDraw() {
this.options = {
title: {
show: true,
text: '小袁博客15天內(nèi)訪問(wèn)情況統(tǒng)計(jì)',
left: 'center',
textStyle: {
color: '#FDF5E6'
}
},
legend: {
show: true,
left: '1%',
textStyle: {
color: '#FDF5E6'
}
},
tooltip: {
trigger: 'axis',
axisPointer: { type: 'line' }
},
grid:{
left:"1%",
right:"1%",
bottom:"1%",
containLabel:true,
},
xAxis: {
type: 'category',
data: this.xAxis,
boundaryGap: ['5%', '5%',],//坐標(biāo)軸兩邊留白
axisLabel: {
color: '#FDF5E6'
},
axisLine: {//坐標(biāo)軸
lineStyle:{
opacity: 0.01,//設(shè)置透明度就可以控制顯示不顯示
},
},
splitLine: {//網(wǎng)格線
lineStyle:{
color: '#eeeeee',
},
},
axisTick: {//刻度線
show: false,//去掉刻度線
},
},
yAxis: {
type: 'value',
name:'次 ',//是基于Y軸線對(duì)齊,用空格站位讓坐標(biāo)軸名稱(chēng)與刻度名稱(chēng)對(duì)齊
axisLabel: {
color: '#eee'
},
nameTextStyle: {
color:'#444e65',
align:'left',//文字水平對(duì)齊方式
verticalAlign:'middle',//文字垂直對(duì)齊方式
},
axisTick: {//刻度線
show: false,//去掉刻度線
},
axisLine: {//坐標(biāo)軸線
lineStyle:{
opacity: 0,//透明度為0
},
},
splitLine: {//網(wǎng)格線
show: true,//網(wǎng)格線
lineStyle:{
color: 'rgba(211, 211, 211, 0.5)',
},
},
},
series: [
{
data: this.yAxis,
smooth: true,
type: 'line',
name: '訪問(wèn)次數(shù)',
itemStyle: {//折線拐點(diǎn)標(biāo)志的樣式。
normal: {
color: '#98F5FF',
},
},
}
]
}
}
},
mounted() {
this.initData()
}
}
</script>
柱狀圖-各省份訪問(wèn)情況
<template>
<div>
<Echart :options="options"
id="lspEcharts"
height="300px"
width="100%"/>
</div>
</template>
<script>
import statAccess from "@/api/stat/statAccess";
import Echart from "@/components/Echart/index.vue";
export default {
components: {
Echart
},
data() {
return {
options: {},
axis: [],
series: []
}
},
methods: {
initData() {
statAccess.getAccessStatByProvince().then(res => {
let x = []
let y = []
for (let i = 0; i < res.data.length; i++) {
x.push(res.data[i].province)
y.push(res.data[i].count)
}
this.axis = x
this.series = y
this.executeDraw()
})
},
executeDraw() {
this.options = {
tooltip: {},
title: {
text: '各城市訪問(wèn)情況占比',
left: 'center',
textStyle: {
color: '#FDF5E6'
}
},
grid:{
left: "1%",
right: "1%",
bottom: "1%",
containLabel: true,
},
legend: {
show: true,
left: '1%',
textStyle: {
color: '#FDF5E6'
}
},
xAxis: {
data: this.axis,
axisLine: {
show: false,
},
axisLabel: {
color: '#FDF5E6'
}
},
yAxis: {
// 網(wǎng)格樣式
splitLine: {
show: false,
},
axisLabel: {
color: '#FDF5E6'
}
},
series: [{
name: '訪問(wèn)量',
type: 'bar',
data: this.series,
barWidth: 15,
itemStyle: {
color: {
type:'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{
offset: 0,
color: 'rgba(255, 130, 71, 1)',
},
{
offset: 1,
color: 'rgba(255, 130, 71, 0.5)',
},
],
globaCoord: false,
},
barBorderRadius: [5, 5, 0, 0], // (順時(shí)針左上,右上,右下,左下)
},
}],
}
}
},
mounted() {
this.initData()
}
}
</script>
首頁(yè)
直接引入
<template>
<div class="dashboard-container">
<div class="dashboard-bg"></div>
<div class="echart-div">
<el-row :gutter="1" class="item">
<el-col :span="8">
<BrowserPie></BrowserPie>
</el-col>
<el-col :span="16">
<TimeLine></TimeLine>
</el-col>
</el-row>
<el-row style="margin-top: 35px" class="item">
<el-col span="8">
<LspPie></LspPie>
</el-col>
<el-col span="16">
<ProvinceCol></ProvinceCol>
</el-col>
</el-row>
</div>
</div>
</template>
<script>
import LspPie from "./components/LspPie.vue";
import ProvinceCol from "./components/ProvinceCol.vue";
import BrowserPie from "./components/BrowserPie.vue";
import TimeLine from "./components/TimeLine.vue";
export default {
components: {
LspPie,
ProvinceCol,
BrowserPie,
TimeLine
},
name: 'home',
data() {
return {
}
},
mounted() {
},
methods: {
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.dashboard {
&-container {
.echart-div {
padding: 30px;
}
}
&-text {
font-size: 22px;
line-height: 46px;
}
.personal {
.box-card-header {
position: relative;
height: 220px;
img {
width: 100%;
height: 100%;
transition: all 0.2s linear;
&:hover {
transform: scale(1.1, 1.1);
filter: contrast(130%);
}
}
}
}
}
.dashboard {
&-bg {
background-image: url('../../assets/img/home_bg.png');
background-size: cover;
background-repeat: no-repeat;
background-attachment: fixed; /* 可選,固定背景圖片 */
background-position: center; /* 可選,設(shè)置背景圖片位置 */
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 100%;
filter: blur(1px);
}
}
</style>
結(jié)束
到這里整篇文章就結(jié)束了,我們重新捋一下整個(gè)流程
- 全局過(guò)濾器攔截請(qǐng)求
- 對(duì)請(qǐng)求信息進(jìn)行解析入庫(kù)
- 定義API接口
- 前端引入axios、echarts
- 編寫(xiě)圖形Vue組件
- 前后端數(shù)據(jù)交互
以上就是SpringBoot整合Echarts實(shí)現(xiàn)數(shù)據(jù)大屏的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot Echarts數(shù)據(jù)大屏的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- springboot如何從數(shù)據(jù)庫(kù)獲取數(shù)據(jù),用echarts顯示(數(shù)據(jù)可視化)
- SpringBoot+Thymeleaf+ECharts實(shí)現(xiàn)大數(shù)據(jù)可視化(基礎(chǔ)篇)
- SpringBoot+thymeleaf+Echarts+Mysql 實(shí)現(xiàn)數(shù)據(jù)可視化讀取的示例
- SpringBoot+ECharts是如何實(shí)現(xiàn)數(shù)據(jù)可視化的
- SpringBoot+Echarts實(shí)現(xiàn)請(qǐng)求后臺(tái)數(shù)據(jù)顯示餅狀圖
相關(guān)文章
Spring FreeMarker整合Struts2過(guò)程詳解
這篇文章主要介紹了Spring FreeMarker整合Struts2過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10
解決spirngboot連接redis報(bào)錯(cuò):READONLY?You?can‘t?write?against?
docker部署的redis,springboot基本每天來(lái)連redis都報(bào)錯(cuò):READONLY?You?can't?write?against?a?read?only?replica,重啟redis后,可以正常連接。但是每天都重啟redis,不現(xiàn)實(shí),也很麻煩,今天給大家分享解決方式,感興趣的朋友一起看看吧2023-06-06
java如何在過(guò)濾器中為http請(qǐng)求加請(qǐng)求頭header
總結(jié):通過(guò)在過(guò)濾器中添加`addHeader`方法,強(qiáng)制將用戶(hù)唯一標(biāo)識(shí)添加到請(qǐng)求頭中,然后在需要的地方通過(guò)`@RequestHeader`注解獲取用戶(hù)唯一標(biāo)識(shí),此方法適用于所有接口,解決了通過(guò)頁(yè)面?zhèn)鬟ftoken不適用所有接口的問(wèn)題2026-04-04
mybatis源碼解讀之executor包語(yǔ)句處理功能
這篇文章主要介紹了executor包語(yǔ)句處理功能,mybatis中支持三種語(yǔ)句類(lèi)型,不同語(yǔ)句類(lèi)型支持的變量符號(hào)不同,下文詳細(xì)內(nèi)容,需要的小伙伴可以參考一下2022-02-02
使用@PathVariable時(shí)候無(wú)法將參數(shù)映射到變量中的解決
這篇文章主要介紹了使用@PathVariable時(shí)候無(wú)法將參數(shù)映射到變量中的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08
Dockerfile的ADD指令實(shí)現(xiàn)遠(yuǎn)程文件的安全校驗(yàn)下載
本文主要介紹了Dockerfile的ADD指令實(shí)現(xiàn)遠(yuǎn)程文件的安全校驗(yàn)下載,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2026-04-04
使用@Autowired注解引入server服務(wù)層方法時(shí)報(bào)錯(cuò)的解決
這篇文章主要介紹了使用@Autowired注解引入server服務(wù)層方法時(shí)報(bào)錯(cuò)的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11

