数据可视化插件echarts【前端】
前言版权开源推荐数据可视化插件echarts一、如何使用1.1 下载1.2 找到js文件1.3 入门使用1.4 我的使用 二、前后端交互:入门demo2.1 前端htmljs 2.2 后端entitycontrollerservicemapper 三、前后端交互:动态数据3.1 前端js 3.2 后端service 四、前后端交互:动态数据4.1 前端js 4.2 后端ChineseName注解EldDataDataService 五、测试扩展性5.0 开发说明5.1 测试结果5.2 Eld多加一个属性5.3 加入测试数据 六、注解优化6.0 开发说明6.1 测试结果6.2 前端6.2 后端ChineseNameEldDataDataService 七、实际项目开发EldDataConstant测试数据 最后
前言
2024-4-12 16:08:09
以下内容源自《【前端】》
仅供学习交流使用
版权
禁止其他平台发布时删除以下此话
本文首次发布于CSDN平台
作者是CSDN@日星月云
博客主页是https://jsss-1.blog.csdn.net
禁止其他平台发布时删除以上此话
开源
日星月云 / echarts数据可视化
v1:二、入门demo
v2:三、动态数据
v3:四、动态数据
v4:六、注解优化
v5:七、项目开发
推荐
echarts入门教程(超级详细带案例)
数据可视化插件echarts
一、如何使用
1.1 下载
(1)从 npm 获取
npm install echarts --save
(2)从 CDN 获取
(3)从 GitHub 获取
1.2 找到js文件
在node_modules\echarts\dist
中找到echart.js
或echarts.min,js
1.3 入门使用
<!DOCTYPE html><html><head><meta charset="utf-8"><title></title><!-- 01 导入js --><script src="js/echarts.js"></script><!-- 03 设置容器的样式 --><style> #container{ width: 800px; height: 600px; }</style></head><body><!-- 02 创建个容器 --><div id="container"></div></body><script> //04 实例化echarts // 4.1 创建一个实例 var echart = echarts.init(document.getElementById("container")) // 4.2 定义配置项 var option = { // 图表的标题 title:{ text:"我的第一个图表" }, // 图表的提示 tooltip:{}, // 图例 legend:{data:["睡眠时长"]}, // x轴线 xAxis:{data:["周一","周二","周三","周四","周五","周六","周日"]}, // y轴线 yAxis:{}, // 设置数据 series:[ { // 数据名称 name:"睡眠时长", // 类型为柱状图 type:"bar", // 数据data data:[8,10,4,5,9,4,8] } ] } // 4.3 更新配置 echart.setOption(option); // chart图表,set设置 Option选项 data数据 type类型 bar条(柱状条),series系列(数据) Axis轴线 xAxis水平轴线 // legend传奇(图例) tooltip 提示 init初始化 document文档</script></html>
1.4 我的使用
下面的代码根据此代码修改
https://www.isqqw.com/?t=line
<!DOCTYPE html><html><head><meta charset="utf-8"><title></title><!-- 01 导入js --><script src="js/echarts.js"></script><!-- 03 设置容器的样式 --><style> #container{ width: 800px; height: 600px; }</style></head><body><!-- 02 创建个容器 --><div id="container"></div></body><script> //实例化echarts // 1 创建一个实例 var echart = echarts.init(document.getElementById("container")); let data1 = [175, 160, 153, 121, 156] let data2 = [200, 140, 205, 162, 175] let data3 = [] let data4 = ['13:00', '14:00', '15:00', '16:00', '17:00'] data1.forEach((item1,index)=>{ if(item1>data2[index]){ data3.push( { yAxis: item1, //标注的Y轴位置 xAxis: data4[index], //标注的X轴位置 value: item1 //标注的value值 } ) }else{ data3.push( { yAxis: data2[index], //标注的Y轴位置 xAxis: data4[index], //标注的X轴位置 value: data2[index] //标注的value值 } ) } }) // 2 定义配置项 var option = { backgroundColor: 'white', grid: { top: '20%', left: '5%', right: '5%', bottom: '8%', containLabel: true }, tooltip: { trigger: 'axis', borderWidth: 1, axisPointer: { type: 'shadow' }, extraCssText: 'z-index:2' }, legend: [{ top: 'top', left: 'center', orient: 'horizontal', data: ['进水量', '出水量'], itemWidth: 15, itemHeight: 10, itemGap: 15, borderRadius: 4, textStyle: { color: '#000', fontFamily: 'Alibaba PuHuiTi', fontSize: 14, fontWeight: 400 } }], xAxis: { type: 'category', data: data4, axisLine: { show: false }, axisTick: { show: false }, axisLabel: { show: true, textStyle: { color: '#393939' //X轴文字颜色 } } }, yAxis: [ { type: 'value', name: '', nameTextStyle: { color: '#000', fontFamily: 'Alibaba PuHuiTi', fontSize: 14, fontWeight: 600 // padding: [0, 0, 0, 40], // 四个数字分别为上右下左与原位置距离 }, nameGap: 30, // 表现为上下位置 axisLine: { show: true, lineStyle: { color: '#eeeeee' } }, axisTick: { show: false }, axisLabel: { color: '#393939', fontSize: 14 }, splitLine: { show: true, lineStyle: { color: '#eeeeee' } } } ], series: [ { name: '进水量', type: 'line', showAllSymbol: true, //显示所有图形。 //标记的图形为实心圆 symbolSize: 8, //标记的大小 itemStyle: { //折线拐点标志的样式 color: 'white', borderWidth: '2', borderColor: '#5470c6', normal: { color: '#5470c6'//拐点颜色 } }, lineStyle: { color: '#5470c6' }, markPoint:{ data: data3 }, data: data1 }, { name: '出水量', type: 'line', showAllSymbol: true, //显示所有图形。 symbolSize: 8, //标记的大小 itemStyle: { //折线拐点标志的样式 color: 'white', borderWidth: '2', borderColor: '#91cc75', normal: { color: '#91cc75'//拐点颜色 } }, lineStyle: { color: '#91cc75' }, data: data2 } ] } // 3 更新配置 echart.setOption(option);</script></html>
二、前后端交互:入门demo
2.1 前端
html
<div class="data-container"></div>
js
$(document).ready(function () { list();});function list() { $.ajax({ type: "GET", url: SERVER_PATH + "/data/list", xhrFields: {withCredentials: true}, success: function (result) { if (result.status) { alertBox(result.data.message); return false; } init(result.data); } });}function init(dataLists) { //实例化echarts // 1 创建一个实例 var echart = echarts.init(document.querySelector(".data-container")); //进水量 let data1 = dataLists.inWaterList; //出水量 let data2 = dataLists.outWaterList; //标注 let data3 = [] //横轴时间 let data4 = dataLists.dateList; data1.forEach((item1, index) => { if (item1 > data2[index]) { data3.push( { yAxis: item1, //标注的Y轴位置 xAxis: data4[index], //标注的X轴位置 value: item1 //标注的value值 } ) } else { data3.push( { yAxis: data2[index], //标注的Y轴位置 xAxis: data4[index], //标注的X轴位置 value: data2[index] //标注的value值 } ) } }) // 2 定义配置项 var option = { backgroundColor: 'white', grid: { top: '20%', left: '5%', right: '5%', bottom: '8%', containLabel: true }, tooltip: { trigger: 'axis', borderWidth: 1, axisPointer: { type: 'shadow' }, extraCssText: 'z-index:2' }, legend: [{ top: 'top', left: 'center', orient: 'horizontal', data: ['进水量', '出水量'], itemWidth: 15, itemHeight: 10, itemGap: 15, borderRadius: 4, textStyle: { color: '#000', fontFamily: 'Alibaba PuHuiTi', fontSize: 14, fontWeight: 400 } }], xAxis: { type: 'category', data: data4, axisLine: { show: false }, axisTick: { show: false }, axisLabel: { show: true, textStyle: { color: '#393939' //X轴文字颜色 } } }, yAxis: [ { type: 'value', name: '', nameTextStyle: { color: '#000', fontFamily: 'Alibaba PuHuiTi', fontSize: 14, fontWeight: 600 // padding: [0, 0, 0, 40], // 四个数字分别为上右下左与原位置距离 }, nameGap: 30, // 表现为上下位置 axisLine: { show: true, lineStyle: { color: '#eeeeee' } }, axisTick: { show: false }, axisLabel: { color: '#393939', fontSize: 14 }, splitLine: { show: true, lineStyle: { color: '#eeeeee' } } } ], series: [ { name: '进水量', type: 'line', showAllSymbol: true, //显示所有图形。 //标记的图形为实心圆 symbolSize: 8, //标记的大小 itemStyle: { //折线拐点标志的样式 color: 'white', borderWidth: '2', borderColor: '#5470c6', normal: { color: '#5470c6'//拐点颜色 } }, lineStyle: { color: '#5470c6' }, markPoint: { data: data3 }, data: data1 }, { name: '出水量', type: 'line', showAllSymbol: true, //显示所有图形。 symbolSize: 8, //标记的大小 itemStyle: { //折线拐点标志的样式 color: 'white', borderWidth: '2', borderColor: '#91cc75', normal: { color: '#91cc75'//拐点颜色 } }, lineStyle: { color: '#91cc75' }, data: data2 } ] } // 3 更新配置 echart.setOption(option);}
2.2 后端
entity
@lombok.Data@NoArgsConstructor@AllArgsConstructorpublic class Data { Integer InWater; Integer OutWater; Date date;}
controller
@GetMapping("/list") public ResponseModel getDataList(){ HashMap<String, Object> search = dataService.searchMap(); return new ResponseModel(search); }
service
public HashMap<String,Object> searchMap(){ HashMap<String,Object> map=new HashMap<>(); List<Data> dataList = search(); List<Integer> inWaterList = new ArrayList<>(); List<Integer> outWaterList = new ArrayList<>(); List<Date> dateList = new ArrayList<>(); for(Data data : dataList){ inWaterList.add(data.getInWater()); outWaterList.add(data.getOutWater()); dateList.add(data.getDate()); } map.put("inWaterList",inWaterList); map.put("outWaterList",outWaterList); map.put("dateList",dateList); return map; } public List<Data> search() { return dataDao.list(); }
mapper
@Select("SELECT * FROM test limit 20") List<Data> list();
三、前后端交互:动态数据
3.1 前端
js
$(document).ready(function () { list();});function list() { $.ajax({ type: "GET", url: SERVER_PATH + "/data/list", data: { userId: 1 }, xhrFields: {withCredentials: true}, success: function (result) { if (result.status) { alertBox(result.data.message); return false; } init(result.data); } });}function init(dataLists) { //实例化echarts // 1 创建一个实例 var echart = echarts.init(document.querySelector(".data-container")); //进水量 let data1 = dataLists.rate1; //出水量 let data2 = dataLists.rate2; //横轴时间 let data4 = dataLists.date; //标注数据 let data3 = []; //只需修改以下对应 let names=["rate1","rate2"]; let datas=[data1,data2]; let colors=[ '#5470c6','#91cc75' ] //动态生成下面的数据 for(let i=0;i<names.length;i++){ data3.push([]); } datas.forEach((data, index) => { data.forEach((item, i) => { data3[index].push({ yAxis: item, // 标注的Y轴位置 xAxis: data4[i], // 标注的X轴位置 value: item // 标注的value值 }); }); }); let seriesData=[]; for (var i = 0; i < datas.length; i++) { seriesData.push({ name: names[i], type: 'line', showAllSymbol: true, //显示所有图形。 //标记的图形为实心圆 symbolSize: 8, //标记的大小 itemStyle: { //折线拐点标志的样式 color: 'white', borderWidth: '2', borderColor: colors[i], normal: { color: colors[i]//拐点颜色 } }, lineStyle:{ color: colors[i] }, markPoint: { data: data3[i] }, data: datas[i] }); } // 2 定义配置项 var option = { backgroundColor: 'white', grid: { top: '20%', left: '5%', right: '5%', bottom: '8%', containLabel: true }, tooltip: { trigger: 'axis', borderWidth: 1, axisPointer: { type: 'shadow' }, extraCssText: 'z-index:2', // formatter: function(params) { // var tooltipContent = params[0].name + '<br/>'; // 显示日期 // params.forEach(function(param) { // tooltipContent += param.seriesName + ': ' + param.value + '<br>'; // }); // return tooltipContent; // } }, legend: [{ top: 'top', left: 'center', orient: 'horizontal', data: names, itemWidth: 15, itemHeight: 10, itemGap: 15, borderRadius: 4, textStyle: { color: '#000', fontFamily: 'Alibaba PuHuiTi', fontSize: 14, fontWeight: 400 } }], xAxis: { type: 'category', data: data4, axisLine: { show: false }, axisTick: { show: false }, axisLabel: { show: true, textStyle: { color: '#393939' //X轴文字颜色 } } }, yAxis: [ { type: 'value', name: '', nameTextStyle: { color: '#000', fontFamily: 'Alibaba PuHuiTi', fontSize: 14, fontWeight: 600 // padding: [0, 0, 0, 40], // 四个数字分别为上右下左与原位置距离 }, nameGap: 30, // 表现为上下位置 axisLine: { show: true, lineStyle: { color: '#eeeeee' } }, axisTick: { show: false }, axisLabel: { color: '#393939', fontSize: 14 }, splitLine: { show: true, lineStyle: { color: '#eeeeee' } } } ], series: seriesData } // 3 更新配置 echart.setOption(option);}
3.2 后端
service
//返回数据链表 public HashMap<String,ArrayList> searchMap(Integer userId){ HashMap<String,ArrayList> map=new HashMap<>(); List<EldData> dataList = search(userId); List<String> fieldNames = new ArrayList<>(); Class<?> dataClass = EldData.class; // 获取 OldData 类的所有属性名 Field[] fields = dataClass.getDeclaredFields(); for (Field field : fields) { fieldNames.add(field.getName()); map.put(field.getName(),new ArrayList<>()); } for (EldData data : dataList) { for (String fieldName : fieldNames) { ArrayList<Object> rowData =map.get(fieldName); try { Field field = dataClass.getDeclaredField(fieldName); field.setAccessible(true); rowData.add(field.get(data)); } catch (NoSuchFieldException | IllegalAccessException e) { e.printStackTrace(); } map.put(fieldName,rowData); } } return map; } //搜索数据 public List<EldData> search(Integer userId) { String key= ELD_DATA +userId; Set set = redisTemplate.opsForZSet().reverseRange(key, 0, 19);// 获取分数最高的20个数据 return set !=null?new ArrayList<>(set):new ArrayList<>(); }
四、前后端交互:动态数据
后端name根据注解ChineseName
前端颜色是随机生成的
hashMap导致没有顺序,换成LinkedHashMap
在seachMap()中,重新定义
// HashMap<String,ArrayList> map=new HashMap<>(); HashMap<String,ArrayList> map=new LinkedHashMap<>();
发现它是时间顺序是反这的
修改一下
//搜索数据// public List<EldData> search(Integer userId) {// String key= ELD_DATA +userId;// Set set = redisTemplate.opsForZSet().reverseRange(key, 0, limit-1);// 获取分数最高的20个数据//// return set !=null?new ArrayList<>(set):new ArrayList<>();//// } //搜索数据 public List<EldData> search(Integer userId) { String key= ELD_DATA +userId; Set set = redisTemplate.opsForZSet().reverseRange(key, 0, limit-1);// 获取分数最高的limit个数据 if(set==null){ return new ArrayList<>(); } ArrayList<EldData> resList = new ArrayList<>(set); Collections.reverse(resList); return resList; }
4.1 前端
js
变成真实登录的用户
而不是固定userId是1
$(document).ready(function () { list();});function list() { var id=sessionStorage.getItem("id"); $.ajax({ type: "GET", url: SERVER_PATH + "/data/list", data: { // userId: 1 userId: id }, xhrFields: {withCredentials: true}, success: function (result) { if (result.status) { alertBox(result.data.message); return false; } init(result.data); } });}function init(dataLists) { //实例化echarts // 1 创建一个实例 var echart = echarts.init(document.querySelector(".data-container")); //横轴时间 let datax = dataLists.date; //标注数据 let data0 = []; //只需修改不需要展示的name var noNeed=["id","date"]; //原始数据的所有name let keys=Object.keys(dataLists); //只需添加足够的颜色 // let colors=[ // '#5470c6','#91cc75' // ] //动态生成下面的数据,不需要修改 //随机生成相同数量的颜色 let colors=generateRandomColors(keys.length); //名称和对应的数据 let names = [] let datas = []; keys.forEach((name) => { if(!noNeed.includes(name)){ names.push(name); datas.push(dataLists[name]); } }); for(let i=0;i<names.length;i++){ data0.push([]); } datas.forEach((data, index) => { data.forEach((item, i) => { data0[index].push({ yAxis: item, // 标注的Y轴位置 xAxis: datax[i], // 标注的X轴位置 value: item // 标注的value值 }); }); }); let seriesData=[]; for (var i = 0; i < datas.length; i++) { seriesData.push({ name: names[i], type: 'line', showAllSymbol: true, //显示所有图形。 //标记的图形为实心圆 symbolSize: 8, //标记的大小 itemStyle: { //折线拐点标志的样式 color: 'white', borderWidth: '2', borderColor: colors[i], normal: { color: colors[i]//拐点颜色 } }, lineStyle:{ color: colors[i] }, markPoint: { data: data0[i] }, data: datas[i] }); } // 2 定义配置项 var option = { backgroundColor: 'white', grid: { top: '20%', left: '5%', right: '5%', bottom: '8%', containLabel: true }, tooltip: { trigger: 'axis', borderWidth: 1, axisPointer: { type: 'shadow' }, extraCssText: 'z-index:2', // formatter: function(params) { // var tooltipContent = params[0].name + '<br/>'; // 显示日期 // params.forEach(function(param) { // tooltipContent += param.seriesName + ': ' + param.value + '<br>'; // }); // return tooltipContent; // } }, legend: [{ top: 'top', left: 'center', orient: 'horizontal', data: names, itemWidth: 15, itemHeight: 10, itemGap: 15, borderRadius: 4, textStyle: { color: '#000', fontFamily: 'Alibaba PuHuiTi', fontSize: 14, fontWeight: 400 } }], xAxis: { type: 'category', data: datax, axisLine: { show: false }, axisTick: { show: false }, axisLabel: { show: true, textStyle: { color: '#393939' //X轴文字颜色 } } }, yAxis: [ { type: 'value', name: '', nameTextStyle: { color: '#000', fontFamily: 'Alibaba PuHuiTi', fontSize: 14, fontWeight: 600 // padding: [0, 0, 0, 40], // 四个数字分别为上右下左与原位置距离 }, nameGap: 30, // 表现为上下位置 axisLine: { show: true, lineStyle: { color: '#eeeeee' } }, axisTick: { show: false }, axisLabel: { color: '#393939', fontSize: 14 }, splitLine: { show: true, lineStyle: { color: '#eeeeee' } } } ], series: seriesData } // 3 更新配置 echart.setOption(option);}function generateRandomColors(num) { let randomColors = []; let characters = '0123456789ABCDEF'; for (let i = 0; i < num; i++) { let color = '#'; for (let j = 0; j < 6; j++) { color += characters[Math.floor(Math.random() * 16)]; } randomColors.push(color); } return randomColors;}
4.2 后端
ChineseName注解
package com.jsss.echarts.annotation;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.FIELD)public @interface ChineseName { String value();}
EldData
package com.jsss.echarts.entity;import com.jsss.echarts.annotation.ChineseName;import lombok.AllArgsConstructor;import lombok.Data;import lombok.EqualsAndHashCode;import lombok.NoArgsConstructor;import java.sql.Date;import java.util.Objects;@Data@NoArgsConstructor@AllArgsConstructorpublic class EldData { @ChineseName("id") String id; @ChineseName("率1") Integer rate1; @ChineseName("率2") Integer rate2; @ChineseName("date") Date date; @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; EldData eldData = (EldData) o; return Objects.equals(id, eldData.id) && Objects.equals(date, eldData.date); } @Override public int hashCode() { return Objects.hash(id, date); } @Override public String toString() { return "EldData{" + "rate1=" + rate1 + ", rate2=" + rate2 + ", date=" + date + '}'; }}
DataService
package com.jsss.echarts.service;import com.jsss.echarts.annotation.ChineseName;import com.jsss.echarts.entity.EldData;import com.jsss.utils.Constant;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.serializer.StringRedisSerializer;import org.springframework.stereotype.Service;import java.lang.reflect.Field;import java.util.*;@Servicepublic class DataService implements Constant { @Autowired RedisTemplate redisTemplate; //返回数据链表 public HashMap<String,ArrayList> searchMap(Integer userId){// HashMap<String,ArrayList> map=new HashMap<>(); HashMap<String,ArrayList> map=new LinkedHashMap<>(); List<EldData> dataList = search(userId); List<String> fieldNames = new ArrayList<>(); Class<?> dataClass = EldData.class; // 获取 OldData 类的所有属性名 Field[] fields = dataClass.getDeclaredFields(); for (Field field : fields) { fieldNames.add(field.getName());// map.put(field.getName(),new ArrayList<>()); map.put(field.getAnnotation(ChineseName.class).value(), new ArrayList<>()); } for (EldData data : dataList) { for (String fieldName : fieldNames) {// ArrayList<Object> rowData = map.get(fieldName); try { Field field = dataClass.getDeclaredField(fieldName); ArrayList<Object> rowData =map.get(field.getAnnotation(ChineseName.class).value()); field.setAccessible(true); rowData.add(field.get(data)); map.put(field.getAnnotation(ChineseName.class).value(),rowData); } catch (NoSuchFieldException | IllegalAccessException e) { e.printStackTrace(); }// map.put(fieldName, rowData); } } return map; } //搜索数据// public List<EldData> search(Integer userId) {// String key= ELD_DATA +userId;// Set set = redisTemplate.opsForZSet().reverseRange(key, 0, 19);// 获取分数最高的20个数据//// return set !=null?new ArrayList<>(set):new ArrayList<>();//// } //搜索数据 public List<EldData> search(Integer userId) { String key= ELD_DATA +userId; Set set = redisTemplate.opsForZSet().reverseRange(key, 0, limit-1);// 获取分数最高的limit个数据 if(set==null){ return new ArrayList<>(); } ArrayList<EldData> resList = new ArrayList<>(set); Collections.reverse(resList); return resList; } // 将数据存储到有序集合中,分数为日期的时间戳 public boolean addData(Integer userId, EldData data) { String key= ELD_DATA +userId; return redisTemplate.opsForZSet().add(key, data,data.getDate().getTime()); } // 更新数据,先删除数据,后增加新数据 public boolean updateData(Integer userId,EldData oldData,EldData newData) { long res=removeData(userId,oldData); if (res==0){ //没有旧数据,就修改失败 return false; } return addData(userId, newData); } // 删除指定的数据 public long removeData(Integer userId, EldData data) { String key= ELD_DATA +userId; return redisTemplate.opsForZSet().remove(key, data); }}
五、测试扩展性
5.0 开发说明
后端:
只需要更改EldData的属性就好了
并且添加对应注解
如果有一个属性没有注解会报错,由于searchMap()中默认是所有属性都有此注解。
如果有属性不需要前端展示,可以在前端noNeed中添加
也可以后端修改注解,增加need属性,增加代码逻辑
另外:注解也可增加:x轴属性
@ChineseName("id") String id; @ChineseName("率1") Integer rate1; @ChineseName("率2") Integer rate2; @ChineseName("率3") Integer rate3; @ChineseName("date") Date date;
前端:
修改横轴:datax
怎么标注数据:data0
以及不需要展示的name
链表:noNeed
//横轴时间 let datax = dataLists.date; //标注数据 let data0 = []; //只需修改不需要展示的name var noNeed=["id","date"];
5.1 测试结果
5.2 Eld多加一个属性
package com.jsss.echarts.entity;import com.jsss.echarts.annotation.ChineseName;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import java.sql.Date;import java.util.Objects;@Data@NoArgsConstructor@AllArgsConstructorpublic class EldData { @ChineseName("id") String id; @ChineseName("率1") Integer rate1; @ChineseName("率2") Integer rate2; @ChineseName("率3") Integer rate3; @ChineseName("date") Date date; @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; EldData eldData = (EldData) o; return Objects.equals(id, eldData.id) && Objects.equals(date, eldData.date); } @Override public int hashCode() { return Objects.hash(id, date); }}
5.3 加入测试数据
@Autowired RedisTemplate redisTemplate; @Autowired DataService dataService; Integer userId=1; //清空数据 @Test public void restore() { String key= ELD_DATA +userId; redisTemplate.delete(key); testSearchMap(); } @Test public void testSearchMap(){ HashMap<String, ArrayList> map = dataService.searchMap(userId); System.out.println(map); } @Test public void testValidAdd(){ EldData eldData=new EldData(Toolbox.getRandomString(),175,160,111,new Date(2024-1900,4-1,8)); dataService.addData(userId,eldData); eldData=new EldData(Toolbox.getRandomString(),160,140,121,new Date(2024-1900,4-1,9)); dataService.addData(userId,eldData); eldData=new EldData(Toolbox.getRandomString(),153,205,131,new Date(2024-1900,4-1,10)); dataService.addData(userId,eldData); eldData=new EldData(Toolbox.getRandomString(),121,162,141,new Date(2024-1900,4-1,11)); dataService.addData(userId,eldData); eldData=new EldData(Toolbox.getRandomString(),156,175,151,new Date(2024-1900,4-1,12)); dataService.addData(userId,eldData); testSearchMap(); }
运行前端,登录admin:admin用户
在加入测试数据
userId=2
登录jsss:123456,也是可以的
2024-4-13 16:26:41
六、注解优化
6.0 开发说明
后端:
在EldData中
如果某个属性需要前端展示,就添加ChineseName
注解
如果某个属性是横轴数据,其value
值就是datax
前端:
不需要修改任何代码
public class EldData { @ChineseName("率1") Integer rate1; @ChineseName("率2") Integer rate2; //没有注解,就不展示 Integer rate3; //横轴的注解的值是datax @ChineseName("datax") Date date; }
6.1 测试结果
rate3没有注解
rate3添加注解
6.2 前端
对应的改一下这两个就行了
//横轴数据 let datax = dataLists.datax; //不需要展示的name var noNeed=["datax"];
6.2 后端
ChineseName
/** * 如果某个属性需要展示,就添加`ChineseName`注解 <br> * 如果某个属性是横轴数据,其`value`值是`datax` <br> */@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.FIELD)public @interface ChineseName { //中文name String value();}
EldData
redis判断是根据序列化的结果判断是否相同,
equals和hashCode不起作用
package com.jsss.echarts.entity;import com.jsss.echarts.annotation.ChineseName;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import lombok.ToString;import java.sql.Date;@Data@NoArgsConstructor@AllArgsConstructor@ToStringpublic class EldData { @ChineseName("率1") Integer rate1; @ChineseName("率2") Integer rate2; //没有注解,就不展示 Integer rate3; //横轴的注解的值是datax @ChineseName("datax") Date date; }
DataService
//返回数据链表 public HashMap<String,ArrayList> searchMap(Integer userId){ HashMap<String,ArrayList> map=new LinkedHashMap<>(); List<EldData> dataList = search(userId); List<String> fieldNames = new ArrayList<>(); Class<?> dataClass = EldData.class; // 获取 OldData 类的所有被ChineseName注解的属性名 Field[] fields = dataClass.getDeclaredFields(); for (Field field : fields) { //添加是否有注解的判断 ChineseName chineseNameAnnotation = field.getAnnotation(ChineseName.class); if (chineseNameAnnotation!=null){ fieldNames.add(field.getName()); map.put(field.getAnnotation(ChineseName.class).value(), new ArrayList<>()); } } for (EldData data : dataList) { for (String fieldName : fieldNames) { try { Field field = dataClass.getDeclaredField(fieldName); field.setAccessible(true); ArrayList<Object> rowData =map.get(field.getAnnotation(ChineseName.class).value()); rowData.add(field.get(data)); map.put(field.getAnnotation(ChineseName.class).value(),rowData); } catch (NoSuchFieldException | IllegalAccessException e) { e.printStackTrace(); } } } return map; } //搜索数据 public List<EldData> search(Integer userId) { String key= ELD_DATA +userId; Set set = redisTemplate.opsForZSet().reverseRange(key, 0, limit-1);// 获取分数最高的limit个数据 if(set==null){ return new ArrayList<>(); } ArrayList<EldData> resList = new ArrayList<>(set); Collections.reverse(resList); return resList; }
七、实际项目开发
加入分栏就更好了
EldData
package com.jsss.echarts.entity;import com.jsss.echarts.annotation.ChineseName;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import lombok.ToString;import java.sql.Date;/** * 字段名称类型 字段说明 备注 * <p> * HT Integer 通用-身高(HT) 单位:m * WT Integer 通用-体重(WT) 单位:kg * BMI Double 通用-身高体重指数(BMI) 正常范围 18.5~23.9,超重24.0~27.9,肥胖≥28.0 * SBP Integer 血压-收缩压(SBP) 正常成年人的收缩压范围为90-140毫米汞柱(mmHg)。 * DBP Integer 血压-舒张压(DBP) 正常成年人的舒张压范围为60-90毫米汞柱(mmHg)。 * Hb Integer 血常规-血红蛋白(Hb) 正常成年人的正常范围为120-175克/升。 * WBC Integer 血常规-白细胞计数(WBC) 正常成年人的白细胞计数范围为4-10 × 10^9/L。 * PLT Integer 血常规-血小板计数(PLT) 正常成年人的血小板计数范围为100-300 × 10^9/L。 * ALT Integer 肝功能-谷丙转氨酶(ALT) 正常成年人的ALT范围为10-40单位/升。 * AST Integer 肝功能-谷草转氨酶(AST) 正常成年人的AST范围为10-35单位/升。 * TBIL Double 肝功能-总胆红素(TBIL) 正常成年人的总胆红素范围为3.4-17.1毫摩尔/升。 * BUN Double 肾功能-血尿素氮(BUN) 正常成年人的BUN范围为2.5-7.1毫摩尔/升。 * Cr Integer 肾功能-血肌酐(Cr) 正常成年人的血肌酐范围为53-115微摩尔/升。 * TC Double 血脂-总胆固醇(TC) 正常成年人的总胆固醇范围为3.1-5.2毫摩尔/升。 * TG Double 血脂-甘油三酯(TG) 正常成年人的甘油三酯范围为0.4-1.7毫摩尔/升。 * LDL_CDouble 血脂-低密度脂蛋白胆固醇(LDL-C)正常成年人的LDL-C范围为2.6-3.4毫摩尔/升。 * FPG Double 血糖-空腹血糖(FPG) 正常成年人的空腹血糖范围为3.9-6.1毫摩尔/升。 * twohPGDouble 血糖-餐后2小时血糖(2hPG) 正常成年人的餐后2小时血糖范围为3.9-7.8毫摩尔/升。 */@Data@NoArgsConstructor@AllArgsConstructor@ToStringpublic class EldData { @ChineseName("通用-身高(米)") Integer HT; @ChineseName("通用-体重(千克)") Integer WT; @ChineseName("通用-身高体重指数(千克/米2)") Double BMI; @ChineseName("血压-收缩压(毫米汞柱)") Integer SBP; @ChineseName("血压-舒张压(毫米汞柱)") Integer DBP; @ChineseName("血常规-血红蛋白(克/升)") Integer Hb; @ChineseName("血常规-白细胞计数(10^9/升)") Integer WBC; @ChineseName("血常规-血小板计数(10^9/升)") Integer PLT; @ChineseName("肝功能-谷丙转氨酶(单位/升)") Integer ALT; @ChineseName("肝功能-谷草转氨酶(单位/升)") Integer AST; @ChineseName("肝功能-总胆红素(毫摩尔/升)") Double TBIL; @ChineseName("肾功能-血尿素氮(毫摩尔/升)") Double BUN; @ChineseName("肾功能-血肌酐(微摩尔/升)") Integer Cr; @ChineseName("血脂-总胆固醇(毫摩尔/升)") Double TC; @ChineseName("血脂-甘油三酯(毫摩尔/升)") Double TG; @ChineseName("血脂-低密度脂蛋白胆固醇(毫摩尔/升)") Double LDL_C; @ChineseName("血糖-空腹血糖(毫摩尔/升)") Double FPG; @ChineseName("血糖-餐后2小时血糖(毫摩尔/升)") Double twohPG; //横轴的注解的值是datax @ChineseName("datax") Date date;}
Constant
package com.jsss.utils;import com.jsss.echarts.entity.EldData;import java.sql.Date;public interface Constant { /** * redis键:老人的体检数据 */ String ELD_DATA = "eld_data:"; EldData MIN_DATA=new EldData( Integer.MIN_VALUE,Integer.MIN_VALUE,18.5, 90,60, 120,4,100, 10,10,3.4, 2.5,53, 3.1,0.4,2.6, 63.9,7.8, new Date(System.currentTimeMillis()) ); EldData MAX_DATA=new EldData( Integer.MAX_VALUE,Integer.MAX_VALUE,23.9, 140,90, 175,10,300, 40,35,17.1, 7.1,115, 5.2,1.7,2.4, 6.1,7.8, new Date(System.currentTimeMillis()) );}
测试数据
package com.jsss.echarts;import com.jsss.echarts.entity.EldData;import com.jsss.echarts.service.DataService;import com.jsss.utils.Constant;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.data.redis.core.RedisTemplate;import java.sql.Date;import java.util.ArrayList;import java.util.HashMap;import java.util.Random;@SpringBootTestpublic class EchartsTest implements Constant { @Autowired RedisTemplate redisTemplate; @Autowired DataService dataService; Integer userId=1; Date date=new Date(2024-1900,4-1,14); @Test public void test(){ System.out.println(date); } //清空数据 @Test public void restore() { String key= ELD_DATA +userId; redisTemplate.delete(key); testSearchMap(); } @Test public void testSearchMap(){ HashMap<String, ArrayList> map = dataService.searchMap(userId); System.out.println(map); } @Test public void testValidAdd(){ EldData eldData=randomData(); eldData.setDate(new Date(2024-1900,4-1,8)); dataService.addData(userId,eldData); eldData=randomData(); eldData.setDate(new Date(2024-1900,4-1,9)); dataService.addData(userId,eldData); eldData=randomData(); eldData.setDate(new Date(2024-1900,4-1,10)); dataService.addData(userId,eldData); eldData=randomData(); eldData.setDate(new Date(2024-1900,4-1,11)); dataService.addData(userId,eldData); eldData=randomData(); eldData.setDate(new Date(2024-1900,4-1,12)); dataService.addData(userId,eldData); testSearchMap(); } EldData minData=MIN_DATA; EldData maxData=MAX_DATA; public EldData randomData(){ EldData data=new EldData( randomInt(160,190), randomInt(45,80), randomDouble(minData.getBMI(),maxData.getBMI()), randomInt(minData.getSBP(),maxData.getSBP()),randomInt(minData.getDBP(),maxData.getDBP()), randomInt(minData.getHb(),maxData.getHb()),randomInt(minData.getWBC(),maxData.getWBC()),randomInt(minData.getPLT(),maxData.getPLT()), randomInt(minData.getALT(),maxData.getALT()),randomInt(minData.getAST(),maxData.getAST()),randomDouble(minData.getTBIL(),maxData.getTBIL()), randomDouble(minData.getBUN(),maxData.getBUN()),randomInt(minData.getCr(),maxData.getCr()), randomDouble(minData.getTC(),maxData.getTC()),randomDouble(minData.getTG(),maxData.getTG()),randomDouble(minData.getLDL_C(),maxData.getLDL_C()), randomDouble(minData.getFPG(),maxData.getFPG()),randomDouble(minData.getTwohPG(),maxData.getTwohPG()), new Date(System.currentTimeMillis()) ); return data; } public Integer randomInt(int min,int max){ Random random = new Random(); return random.nextInt(max - min + 1) + min; // 生成 min 到 max 范围内的随机 int 数 } public Double randomDouble(double min,double max){ Random random = new Random(); return min + (max - min) * random.nextDouble(); // 生成 min 到 max 范围内的随机 double 值 }}
最后
2024-4-13 18:49:18
迎着日光月光星光,直面风霜雨霜雪霜。