# SpringBoot系列文章目录
SpringBoot知识范围-学习步骤–【思维导图知识范围】
文章目录
先上效果图
一般的系统的看板可能是这样,其实这样挺简洁,也挺实用的。

不过,这种样子可能会更炫酷。也更吸睛一些。

先讲一下,为什么要选择layui 做为springBoot 的页面框架?
Thymeleaf和老牌的jsp属于非前后分离的思路来开发的
springBoot 自带的页面是Thymeleaf。
Thymeleaf 是一种现代服务器端Java模板引擎,主要用于Web和独立环境中的应用开发。它主要用于处理HTML,XML,JavaScript,CSS等标记语言。因此,从技术角度来看,Thymeleaf 更偏向于后端技术栈的一部分。下面是一些解释为何Thymeleaf被认为是后端技术的关键点:
模板渲染:Thymeleaf的主要作用是在服务器端将数据模型(通常是Java对象)渲染到HTML模板中。这意味着在用户请求页面之前,服务器已经处理了模板和数据的结合,生成最终的HTML页面发送给客户端。
与Java的集成:Thymeleaf与Java紧密集成,特别是在Spring框架中广泛使用。它通过使用Java对象作为数据模型来简化Web开发,这使得在服务器端处理逻辑和渲染模板变得非常自然。
而国情就是,一旦出现了前后端分离,就没有公司再去理会非前后分离的前端怎么发展了。
那为什么不是VUE?
这就是大学里教学任务的尴尬问题了。因为VUE的学习也得一个学期(4-6个月时间掌握)左右,springBoot 的知识点也比较多,甚至还超过了VUE。
含最少的前后端分离的知识点来把springBoot 的页面实现出来的,就只能是jquery 原生的, 或者是jquery 轻量级的框架,而layui 刚好就是国产的jquery 轻量级的框架。而且layUI 目前也有一定的市场。
当然也有人用纯的bootstrap来写页面的。但是这个列表写的太痛苦了。
所以一些要求灵活的客户端的网页仍然会使用layui 做为前端框架。这样可以使用大量的互联网上的网页模板
参考:
SpringBoot+layui民宿管理系统前台-毕业设计-【JSB项目实战】
所以,要用VUE的项目都是重量级的。(尤其是对于学生来说)
可以参考《SpringBoot+VUE民宿管理系统后台-毕业设计-【JSB项目实战】》
MVC模型

MVC的数据传输包括两个方面。
- 浏览器给服务器的信息
- 服务器给浏览器的信息
layui
layui用了几年,这个框架十分适合我们后台人员开发。简单易用,用的较多的模块肯定是table模块和form模块了。但是在一个开发团队中,不同的人开发水平的差异,如果没有调用统一的公共方法,那就会造成很多样式的不同和公共的bug出现到不同的页面 从而出现耗费过多的精力修改bug。所以我们需要做的是统一样式和方法的调用,将其二次封装以便于开发人员的调用。另一方面也能减少代码的冗余量。
也可以这么说,
UI框架,如果没有table 没有菜单,那这个UI就真的只是一堆皮肤了。
一般在页面的table的构成如下: 搜索区 头部工具栏 和table主体【包含对行和列 事件的处理】

首先分析layui 的输入(给页面什么样的json)
当然了,你得自己会一些layui ,不然的话,就以现在的网络,搬运工太多,而90%的搬运工事实上并没有啥精力帮你复盘。
也就是说,你看到的90%的代码不是不正确,而是,2023年的搬运工,搬了秦始皇的“兵马俑”。然后,你以为是2023年的新框架,套到项目里,那可想而知。
事实上,现在的学习新的知识,能找到合适的一个起步的代码真的就算是成功了一半。比如说充分利用deepseek 。
接口JSON格式
要求后台返回数据格式必须为:
至于json的工具,我一般是用json编辑器JSONedit.exe ,后面资源上提供下载吧。这名字太常见了,怕是百度的话,得百度很久。你说你起一个“彪哥JSON编辑器”这样的名字也好找呀,所以用在线的json工具也是不错的选择。
{"code":"0","msg":"成功","data":[{"name":"深圳","value":535},{"name":"广州","value":735}]}
源项目的地址:
https://gitee.com/dearmite/paike_spring-v-dc
视频讲解:
springBoot项目导入数据看板,并关联后台的数据
步骤1 导入项目,反复观看
导入的时候,最可能碰到的就是数据的管理员帐号密码错误。

先把项目跑起来:

找到菜单的页面:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<meta http-equiv="pragma" content="no-cache" />
<meta http-equiv="content-type" content="no-cache, must-revalidate" />
<meta http-equiv="expires" content="Wed, 26 Feb 1997 08:21:57 GMT"/>
<title>home</title>
<link href="../css/bootstrap.min.css" rel="stylesheet"/>
<link href="../css/font-awesome.css" rel="stylesheet"/>
<link href="../css/basic.css" rel="stylesheet"/>
<link href="../css/custom.css" rel="stylesheet"/>
<link href="../css/my.css" rel="stylesheet"/>
<link href="../css/element/index.css" rel="stylesheet"/>
<style>
[v-cloak] {
display: none;
}
</style>
</head>
<body>
<div id="wrapper" v-cloak>
<nav class="navbar navbar-default navbar-cls-top " role="navigation" style="margin-bottom: 0">
<div class="navbar-header">
<a class="navbar-brand" href="index.html">广东美食后台管理系统</a>
</div>
<div class="header-right">
<a href="javascript:void(0)" class="btn btn-info" @click="logout" title="退出登录">
<i class="fa fa-sign-out fa-2x"></i>
</a>
</div>
<div class="header-right">
<a href="accountUserInfo.html" class="btn btn-info" title="跳转到个人信息">
<i class="fa fa-user fa-2x"></i>
</a>
</div>
<div class="header-right" style="padding-top: 25px">欢迎你,{{ user.name }}</div>
<div class="header-left">
<a href="/front/index.html" class="btn btn-info" title="跳转到首页">
<i class="fa fa-dot-circle-o fa-2x"></i>
</a>
</div>
</nav>
<!-- /. NAV TOP -->
<nav class="navbar-default navbar-side" role="navigation">
<div class="sidebar-collapse">
<ul class="nav" id="main-menu">
<li>
<a href="databoard.html"><i class="fa fa-dashboard "></i>数据看板</a>
</li>
<li>
<a class="active-menu" href="index.html"><i class="fa fa-dashboard "></i>系统首页</a>
</li>
<li>
<a href="#"><i class="fa fa-yelp "></i>信息展示 <span class="fa arrow"></span></a>
<ul class="nav nav-second-level collapse in">
<li>
<a href="userInfo.html"><i class="fa fa-table"></i>用户信息</a>
</li>
<li>
<a href="advertiserInfo.html"><i class="fa fa-table"></i>公告信息</a>
</li>
<li>
<a href="typeInfo.html"><i class="fa fa-table"></i>菜品类别</a>
</li>
<li>
<a href="goodsInfo.html"><i class="fa fa-table"></i>菜品详情</a>
</li>
<li>
<a href="orderInfo.html"><i class="fa fa-table"></i>订单信息</a>
</li>
<li>
<a href="cartInfo.html"><i class="fa fa-table"></i>购物车信息</a>
</li>
<li>
<a href="commentInfo.html"><i class="fa fa-table"></i>评论信息</a>
</li>
<li>
<a href="tousuInfo.html"><i class="fa fa-table"></i>投诉信息</a>
</li>
</ul>
</li>
<li>
<a href="accountUserInfo.html"><i class="fa fa-user"></i>个人信息</a>
</li>
<li>
<a href="updatePassword.html"><i class="fa fa-unlock-alt"></i>修改密码</a>
</li>
<li>
<a href="javascript:void(0)" @click="logout"><i class="fa fa-power-off"></i>退出登录</a>
</li>
</ul>
</div>
</nav>
<div id="page-wrapper">
<div id="page-inner">
<div class="row">
<div class="col-md-3">
<div class="main-box mb-olivedrab">
<i class="fa fa-user"></i><span style="margin-left: 20px;">用户总数:{{ totalUser }}</span>
</div>
</div>
<div class="col-md-3">
<div class="main-box mb-darkseagreen">
<i class="fa fa-comment"></i><span style="margin-left: 20px;">评论总数:{{ totalComment }}</span>
</div>
</div>
<div class="col-md-3">
<div class="main-box mb-burlywood">
<i class="fa fa-rmb"></i><span style="margin-left: 20px;">总交易额:{{ totalPrice }}</span>
</div>
</div>
<div class="col-md-3">
<div class="main-box mb-cadetblue">
<i class="fa fa-shopping-cart"></i><span style="margin-left: 20px;">总销量:{{ totalShopping }}</span>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div class="panel panel-info">
<div class="panel-heading">
<select @change="selectEchartsType" v-model="echartsType">
<option value="pie">饼图</option>
<option value="bar">柱状图</option>
</select>
</div>
<div class="panel-body">
<!-- 为ECharts准备一个具备大小(宽高)的Dom -->
<div class="row">
<div class="col-md-6">
<div id="left" style="width: 100%;height:400px;"></div>
</div>
<div class="col-md-6">
<div id="right" style="width: 100%;height:400px;"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="../js/jquery-1.10.2.js"></script>
<script src="../js/bootstrap.min.js"></script>
<script src="../js/jquery.metisMenu.js"></script>
<script src="../js/custom.js"></script>
<script src="../js/my.js"></script>
<script src="../js/vue2.6.11/vue.min.js"></script>
<script src="../js/vue2.6.11/axios.js"></script>
<script src="../js/echarts.min.js"></script>
<script src="../js/element/index.js"></script>
<script>
new Vue({
el: "#wrapper",
data: {
user: {},
totalUser: 0,
totalComment: 0,
totalPrice: 0,
totalShopping: 0,
echartsShowLeftArr: [],
echartsShowRightArr: [],
echartsType: 'pie'
},
created: function () {
this.user = JSON.parse(localStorage.getItem("user"));
axios.get("/echarts/getTotal").then(res => {
if (res.data.code === '0') {
let map = res.data.data;
this.totalUser = map['totalUser'];
this.totalComment = map['totalComment'];
this.totalPrice = map['totalPrice'];
this.totalShopping = map['totalShopping'];
}
});
this.drawLine();
},
methods: {
drawLine() {
axios.get('/echarts/get/price').then(res => {
// 基于准备好的dom,初始化echarts实例
this.echartsShowLeftArr = res.data.data;
if (this.echartsShowLeftArr.length) {
let myChart = echarts.init(document.getElementById('left'));
let option = this.getEchartsType(this.echartsShowLeftArr, this.echartsType);
myChart.setOption(option, true);
}
});
axios.get('/echarts/get/shopping').then(res => {
// 基于准备好的dom,初始化echarts实例
this.echartsShowRightArr = res.data.data;
if (this.echartsShowRightArr.length) {
let myChart = echarts.init(document.getElementById('right'));
let option = this.getEchartsType(this.echartsShowRightArr, this.echartsType);
myChart.setOption(option, true);
}
});
},
selectEchartsType() {
let leftChart = echarts.init(document.getElementById('left'));
let leftOption = this.getEchartsType(this.echartsShowLeftArr, this.echartsType);
leftChart.setOption(leftOption, true);
let rightChart = echarts.init(document.getElementById('right'));
let rightOption = this.getEchartsType(this.echartsShowRightArr, this.echartsType);
rightChart.setOption(rightOption, true);
},
getEchartsType(echartsArr, type) {
for (let item of echartsArr) {
if (item.series[0].type === type) {
return item;
}
}
},
logout() {
logout();
}
}
});
</script>
</body>
</html>
先加菜单

步骤2 找数据看板,要漂亮,要炫酷
https://gitee.com/dearmite_admin/100-data-visual
这里有开源的一堆数据可视化的漂亮模型。

我最后选择了

先来改静态的。就算没有动态的后台数据,也不至于难堪。
顺便改了背景的色调。不过好看不好看就…不喜欢你也可以自己改嘛。

然后就是放入项目。
因为这个项目的页面都是放static 目录下面,所以根本没有啥难度。对着视频,CV大法就行了。
要注意的是:让你的databoard.html 与index.html 位于同一个目录下就好了。减少后面的调试的难度。
总不能自己给自己强上难度吧?
步骤3 写controller 后台代码

全部代码如下:
package com.example.controller;
import cn.hutool.json.JSONObject;
import com.example.common.Result;
import com.example.dao.CommentInfoDao;
import com.example.dao.OrderGoodsRelDao;
import com.example.dao.OrderInfoDao;
import com.example.dao.UserInfoDao;
import com.example.vo.EchartsData;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
@RestController
@RequestMapping("/echarts")
public class EchartsController {
@Resource
private UserInfoDao userInfoDao;
@Resource
private CommentInfoDao commentInfoDao;
@Resource
private OrderInfoDao orderInfoDao;
@Resource
private OrderGoodsRelDao orderGoodsRelDao;
@GetMapping("/getTotal")
Result<Map<String, Object>> getTotal() {
Map<String, Object> map = new HashMap<>(4);
// 获取用户总数
map.put("totalUser", userInfoDao.count() == null ? 0 : userInfoDao.count());
// 获取评论总数
map.put("totalComment", commentInfoDao.count() == null ? 0 : commentInfoDao.count());
// 获取总销售额
map.put("totalPrice", orderInfoDao.totalPrice() == null ? 0 : orderInfoDao.totalPrice());
// 获取总销量
map.put("totalShopping", orderGoodsRelDao.totalShopping() == null ? 0 : orderGoodsRelDao.totalShopping());
return Result.success(map);
}
@GetMapping("/get/shopping")
Result<List<EchartsData>> getShoppingEchartsData() {
List<EchartsData> list = new ArrayList<>();
List<Map<String, Object>> typePriceList = orderInfoDao.getTypeCount();
Map<String, Double> typeMap = new HashMap<>();
for (Map<String, Object> map : typePriceList) {
typeMap.put((String) map.get("name"), ((BigDecimal) map.get("count")).doubleValue());
}
getPieData("分类总销量", list, typeMap);
getBarData("分类总销量", list, typeMap);
return Result.success(list);
}
@GetMapping("/get/price")
Result<List<EchartsData>> getPriceEchartsData() {
List<EchartsData> list = new ArrayList<>();
List<Map<String, Object>> typePriceList = orderInfoDao.getTypePrice();
Map<String, Double> typeMap = new HashMap<>();
for (Map<String, Object> map : typePriceList) {
typeMap.put((String) map.get("name"), (Double) map.get("price"));
}
getPieData("分类总销售额", list, typeMap);
getBarData("分类总销售额", list, typeMap);
return Result.success(list);
}
@GetMapping("/data/pie")
Result<List<Map<String, Object>> > getDataPieData() {
List<Map<String, Object>> totolList = new ArrayList<Map<String, Object>>();
Map<String, Object> typeMap = new HashMap<>();
typeMap.put("name","深圳"); // name 这个值是图表需要的。“深圳”这个值是数据库里取的
typeMap.put("value",535); // value 这个值是图表需要的。“深圳”这个值是数据库里取的
totolList.add(typeMap);
Map<String, Object> typeMap2 = new HashMap<>();
typeMap2.put("name","广州");
typeMap2.put("value",735);
totolList.add(typeMap2);
return Result.success(totolList);
}
private void getPieData(String name, List<EchartsData> pieList, Map<String, Double> dataMap) {
EchartsData pieData = new EchartsData();
EchartsData.Series series = new EchartsData.Series();
Map<String, String> titleMap = new HashMap<>(2);
titleMap.put("text", name);
pieData.setTitle(titleMap);
series.setName(name + "比例");
series.setType("pie");
series.setRadius("55%");
List<Object> objects = new ArrayList<>();
List<Object> legendList = new ArrayList<>();
for (String key : dataMap.keySet()) {
Double value = dataMap.get(key);
objects.add(new JSONObject().putOpt("name", key).putOpt("value", value));
legendList.add(key);
}
series.setData(objects);
pieData.setSeries(Collections.singletonList(series));
Map<String, Boolean> map = new HashMap<>();
map.put("show", true);
pieData.setTooltip(map);
Map<String, Object> legendMap = new HashMap<>(4);
legendMap.put("orient", "vertical");
legendMap.put("x", "left");
legendMap.put("y", "center");
legendMap.put("data", legendList);
pieData.setLegend(legendMap);
pieList.add(pieData);
}
private void getBarData(String name, List<EchartsData> barList, Map<String, Double> dataMap) {
EchartsData barData = new EchartsData();
EchartsData.Series series = new EchartsData.Series();
List<Object> seriesObjs = new ArrayList<>();
List<Object> xAxisObjs = new ArrayList<>();
for (String key : dataMap.keySet()) {
Double value = dataMap.get(key);
xAxisObjs.add(key);
seriesObjs.add(value);
}
series.setType("bar");
series.setName(name);
series.setData(seriesObjs);
barData.setSeries(Collections.singletonList(series));
Map<String, Object> xAxisMap = new HashMap<>(1);
xAxisMap.put("data", xAxisObjs);
barData.setxAxis(xAxisMap);
barData.setyAxis(new HashMap<>());
Map<String, Object> legendMap = new HashMap<>(1);
legendMap.put("data", Collections.singletonList(name));
barData.setLegend(legendMap);
Map<String, Boolean> map = new HashMap<>(1);
map.put("show", true);
barData.setTooltip(map);
Map<String, String> titleMap = new HashMap<>(1);
titleMap.put("text", name);
barData.setTitle(titleMap);
barList.add(barData);
}
}
因为是抄学生的表的相关类的,所以也不用把原理搞的门清。
在这中间,你可能碰到多次的 badmapping … 白页,后台报错,启动不成功。MAVEN编译报错。访问地址404…
每当碰到这种情况的时候,你就这么想,你说这么多的错误,这么多的问题,这么难的环境,一通操作猛如虎,怎么工资才2500呢?
所以,2500的工资,跟25000的工资差的其实就是你会调多少错。不管你信不信,10年前也有2500的工资,现在入职1-2年的,也有25000的工资。
当然了,本例还是要提供一下全套的代码的。
据说,用猫的鼠标指针会带来好运?

步骤4 告诉AI你的页面的需求。让AI读懂你的意思,写html代码
做的当时没截图,拿一个视频里的截图吧。


我用的AI是VSCODE 1.96以上的copilot 。有人喜欢用cursor+ deepseek 也是相当不错的。

开始的时候,一看cursor 就是对着vscode抄,我还以为是国产的呢。毕竟他支持deepseek
查了百度,结果…
Anysphere 由几位麻省理工高材生在 2022 年创立,总部在纽约布法罗。
这几位联合创始人分别是 Michael Truell、Aman Sanger、Sualeh Asif 和 Arvid Lunnemark。其中 Truell 和 Sanger 参加过 MIT 的「尼欧学者」(Neo Scholars)计划,这是一个针对主修技术领域的本科生的导师计划。值得一提的是,Neo 还运营着一个加速器和一个风险基金,还主导了 Anysphere 的种子轮投资。
最初,Cursor 基于 Codemirror 构建,但为了专注于开发尖端 AI 功能,并打造一个原生支持 AI 配对编程的集成开发环境(AI-native IDE),Anysphere 将 Cursor 迁移至 VSCodium 的一个分支上,即微软 Visual Studio Code(VS Code)的开源版本。
为了实现以更快的速度提供最前沿的 AI 功能,Cursor 引入了性能更优的 Claude 模型,将 Copilot++(智能代码补全等功能)的速度提高了大约两倍。此外,还引入了一个名为「Composer」的试验性功能(Beta 版),它使用户能够在单一编辑环境中操作多个文件。 [3]
这才发现,原来抄东西,老外也不比国人差多少的。
步骤5 F12调试页面大法(没有捷径)

这一次的错误挺让人无语的。这个数据看板的模型,在前面引入了echarts.js 但是在页面的最后引入了jquery.js
这让中间写代码的我一下子就进入错误的岔道上了。就差一点把视频中止了。好在最后的时刻,灵光一现的发现的"$ is not defined" 想到了什么…
资源下载
SpringBoot项目新加一个数据看板并与后台关联数据(程序猿式操作)–【JSB系列之014】
https://download.youkuaiyun.com/download/dearmite/90944029
可以看GITEE上的开源项目
https://gitee.com/dearmite/paike_spring-v-dc
100余套数据可视化看板模型
https://gitee.com/dearmite_admin/100-data-visual
POP猫鼠标指针方案资源下载
https://download.youkuaiyun.com/download/dearmite/88230629
附:怎样安装自己喜欢的鼠标指针方案 如何安装鼠标指针
下载并安装指针方案:
01
下载自己喜欢的鼠标指针方案,解压:

02
解压后我们会看到文件夹下有很多 ani动态光标文件 中有一个 ini文件,右键单击 ini文件(安装文件.ini),执行【安装】命令:

03
等待安装结束后打开【控制面板】→【鼠标】(Win XP 与 Win 7 操作均相同):

04
打开【鼠标属性】窗口,选择【指针】选项卡,在方案中找到刚才安装的指针方案【Crystal Clear】,然后单击窗口又下脚的【应用】→【确定】完成安装!

第二种方案


1043

被折叠的 条评论
为什么被折叠?



