SpringBoot项目新加一个数据看板并与后台关联数据(程序猿式操作)--【JSB系列之014】

# 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】,然后单击窗口又下脚的【应用】→【确定】完成安装!
在这里插入图片描述
第二种方案
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

项目张雪峰之巅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值