• 健康系统练习


    健康系统

    项目建构:

    前后端分离,前端vue3,后端Java,springboot做跨域处理,前端将在vscode中 的tomcat下部署,后端将在ideal中集成的tomcat中部署

    image-20230629163636939

    创建项目工程在ideal中直接选用springi…创建,选中web,sql中的mysdatatbasses

    后端页面

    在数据源中新建一个数据连接池application.yml:

    image-20230908102048128

    image-20230629163755233

    其中的image-20230629163810888加载了数据库连接池的驱动

    image-20230629163832606

    连接数据对象的地址,使用了jdbc进行连接

    • 上面的 mybatis.mapper-locations 属性指定了 Mapper 接口文件所在的位置,mybatis.type-aliases-package 属性则指定了类型别名所在的包。你可以根据你的实际情况进行调整。
    • 此时,Spring Boot 将使用这个端口号来启动 Web 服务器,为了防止后面前端页面中的tomcat中的端口重复

    在浏览器中验证9000端口成功加载

    image-20230629163914736

    image-20230629163926111

    在Java类中创建测试案列test来展示项目的结构

    image-20230629163946431

    image-20230629164004142

    在浏览器上输入地址后

    image-20230629164015258

    前后端分离导致他们的服务器端口通信不一致,需要让他们保持通信

    image-20230629164032605

    前端页面:

    环境准备:

    需要下载node.js才能使用vue框架

    安装node.js包管理器

    在安装完node.js后需要安装安装Nodejs下的包管理器,打开windows命令窗口,输入“npm install -g cnpm --registry=https://registry.npm.taobao.org

    image-20230629164057823

    下载不了就国内镜像下载,可以更新一下版本,

    安装vue-cli脚手架

    切换到d盘目录安装脚手架:

    image-20230629164132153

    npm install -g @vue/cli

    查看下版本

    image-20230629164143190

    启动下可视化界面在对应的目录下:

    成功启动:

    image-20230629164157125

    image-20230629164221757

    初始预设项目的创建:

    在D盘下创建此项目

    image-20230629164238780

    image-20230629164247708

    image-20230629164300368

    image-20230629164307959

    image-20230629164313854

    在最后创建的前一步

    要选择创建的版本选择3.x版本的

    image-20230629164338789

    image-20230629164349999

    在创建完成后需要给vue添加一些组件在插件库里面添加插件

    添加:插件,由于我是vue3所以使用plus版element

    image-20230629164358406

    image-20230629164431175

    点击完成安装

    添加依赖:

    image-20230629164448404

    image-20230629164457834

    image-20230629164508463

    这几个就是帮助设计样式,和提交的

    image-20230629164523005

    启动项目:

    image-20230629164708614

    对基础页面进行删减:

    image-20230629164732526

    创建登录组件:

    image-20230629164815042

    image-20230629164838209

    为了保证能访问到需要进行路由转发(index.js)

    image-20230629164907311

    image-20230629164920763

    在app主界面修设置了路由转发

    image-20230629164956197

    开发login组件:

    image-20230629165251000

    创建了全局的css样式global.css

    image-20230629165043075

    在导入到全局变量

    image-20230629165431069

    登录页面成功显示:

    image-20230630111545165

    image-20230630114039398

    设置列表前面的搜索框

    image-20230630155121756

    image-20230630155216737

    为了美化页面里面的图标需要下载好图标导入到项目

    image-20230908102020972

    image-20230630112152025

    打开image-20230630112515702

    这里面有用法

    image-20230630112552842

    导入到项目在全局设置样式(main.js)

    image-20230630112651844

    image-20230630161511754

    image-20230630161626155

    image-20230630162115744

    image-20230908101956593

    注意这里是在里面是有一个点的要去掉

    image-20230630163119896

    image-20230630163046569

    样式添加:

    在global.css设置了登录容器的大小即为可视化窗口的大小

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    在login.vue中设置了容器的高度大小为可视化窗口的100%

    image-20230630180930113

    设置了背景颜色和大小

    成功让其填充到页面

    设置了login——box登录容器的样式这里可以调整:

    image-20230701174534998

    设置了form表单样式:

    image-20230630212257883

    设置了log区域的div样式:

    image-20230908101836684

    设置了图标:

    image-20230630212423715

    结果:

    image-20230630212443473

    设置了登录按钮的样式边界:

    image-20230908101818031

    设置了form表单的样式:

    调整后:

    前端登录页面美化样式完成:

    添加用户登录表单非空前验证:

    添加属性:

    设置了用户名和密码的登录前必须输入验证:

    image-20230701185022027

    并且在对应的表单中绑定了对应的事件

    image-20230701200329169

    添加登录的提交需要导入axios

    (vue2)

    image-20230702111350772

    由于我使用的是vue3导致使用的挂载方法不一致,且挂载axios有先后顺序,必须在访问app之前挂载

    (vue 3)

    image-20230702113206566

    在密码框添加了防可见

    image-20230702113501245

    为了验证按钮点了之后是否生效我加了验证的日志

    image-20230908101734468

    浏览器开发者工具下

    image-20230702154546271

    在设置登录按钮的过程中由于vue3问题我修改了多次下面展示我修改的代码

    image-20230702162817944

    image-20230702162922673

    image-20230702163115502

    这里我还将窗口的可视化进行了移动、

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    image-20230702163239954

    在点击登录和重置后,有信息输出表示成功了

    image-20230702163408320

    登录显示后端数据测试

    image-20230702172342464

    点击登录后成功显示

    image-20230908101646011

    image-20230702172503447

    添加了点击登录按钮后提示的消息框

    image-20230702184035615

    image-20230702184046877

    image-20230908101622866

    image-20230702184425252

    image-20230908101605687

    添加home首页创建home首页

    image-20230704104230878

    在home页面写上基本框架

    image-20230704104330750

    重定向路由页面让路由进行跳转

    image-20230704104419128

    image-20230704104431873

    image-20230704104457891

    image-20230908101545115

    到此处完整的响应请求,基本完成,且前后端分离

    创建user对象进行封装(get,Set):

    image-20230705095012602

    image-20230705095047923

    创建了一个登录控制器:

    image-20230705095237799

    image-20230705095310445

    改变前端页面要访问的后台地址:

    image-20230705095415969

    在点击登录后成功跳转login页面

    image-20230705095459535

    在后端接收到创建的User 对象

    在后台控制台,查看到创建的User对象

    image-20230705095726836

    到这实体用户User的自动封装,自动创建,已经完成

    在dao成进行数据库的访问

    image-20230705113511752

    image-20230705113521769

    编写了查询语句

    image-20230705113548388

    image-20230705113559105

    返回了controller层,进行登录

    image-20230705113627697

    image-20230705113647060

    在主程序入口进行了扫描dao层中的包的操作

    image-20230705113706041

    点击了登录后跳转到home页面,在后端会查询到数据中的用户信息

    image-20230705113950397

    image-20230705114004183

    导入json特殊字符串,形成json对象

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    由于我是用的gradle创建的项目没有pom.xml文件,Gradle使用一个名为"build.gradle"(或者是"build.gradle.kts",如果你使用Kotlin脚本)的文件来定义项目的构建配置。这个文件包含了项目的依赖项、插件、任务和其他构建设置。

    image-20230714192350807

    在login页面将对象转化为json字符串

    image-20230714192523983

    在登录页面对跳转页面进行设置,设置了他的默认值是error,使用了json.tojsonstring来将对象和跳转页面进行返回

    image-20230714193750738

    将从后端发送过来的数据的flag值和ok对比,进行跳转

    image-20230714201609504

    在hom.vue中添加了退出按钮,并成功返回到登录页面

    image-20230908101324983

    image-20230715134609303

    在点击了退出按钮后,成功返回到登录页面也就是login页面

    在成功点击登录后,在浏览器调试页面查看会话中的存储数据,点击登录之后会在调试页面生成key 和值,在点击退出之后这些数据也不会清除,这样的安全性大大下降

    image-20230715153503206

    在home.vue中清除session

    image-20230715153829428

    改了之后

    image-20230715154100039

    在设置完清楚会话session后我添加了一个安全退出的提示框(home.vue)

    image-20230715155545262

    image-20230715155535811

    image-20230715155614842

    设置了路由守卫(index.js)

    这段代码是使用 Vue Router 中的全局前置守卫 beforeEach,它的作用是在路由导航之前进行拦截和处理。

    image-20230715162620512

    测试了登录页面正常登录,和退出

    设置home页面的布局

    在elemenet中找到containerd 容器布置器

    Container | Element Plus (element-plus.org)

    找到一个有头,有左有右,没有尾巴的布局

    image-20230717103614949

    在home首页插入代码:

    布局设置:
    <template>
        
        <div class="common-layout">
            
            <el-container class="home-container">
                     
                     <el-aside width="200px">Asideel-aside>
                
                <el-container>
                    
                    <el-header>
                        首页
                        
                        
                    el-header>
                    
                    <el-main>Mainel-main>
                el-container>
            el-container>
        div>
    template>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    并在style中设置了布局样式
    /* 布局器的填充 height: 100vh,可以确保布局容器填充整个视口的高度*/
    .home-container{
        height: 100vh;
    }
    /* home主页样式设置 */
    /* 头样式 */
    .el-header{
        background-color: #373d41;
    }
    /* 侧边栏样式 */
    .el-aside{
        background-color: #333744;
    }
    /* 主体样式 */
    .el-main{
        background-color: #eaedf1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    image-20230717122954763

    在侧边栏里面添加了图标
    
            <el-aside width="200px">
                <img src="../assets/OIP.png" alt="">
            el-aside>
    
    • 1
    • 2
    • 3
    • 4

    image-20230717170247978

    添加了文字信息设置了文字信息的样式

    /* 头样式 */
    .el-header {
        /* 背景颜色 */
        background-color: #373d41;
        /* 使用 Flexbox 布局 */
        display: flex;
        /* 在主轴上居中对齐 */
        justify-content: center;
        /* 在交叉轴上居中对齐 */
        align-items: center;
        /* 左边距设置为0% */
        padding-left: 0%;
        /* 文字颜色 */
        color: #fff;
        /* 字体大小 */
        font-size: 20px;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    image-20230717194531753

    添加导航栏

    导航 |元素加 (element-plus.org)

          <el-menu
            active-text-color="#ffd04b"
            background-color="#545c64"
            text-color="#fff"
          >
            <el-sub-menu index="1">
              <template #title>
                <el-icon><location />el-icon>
                <span>Navigator Onespan>
              template>
              <el-menu-item-group title="Group One">
                <el-menu-item index="1-1">item oneel-menu-item>
                <el-menu-item index="1-2">item twoel-menu-item>
              el-menu-item-group>
              <el-menu-item-group title="Group Two">
                <el-menu-item index="1-3">item threeel-menu-item>
              el-menu-item-group>
              <el-sub-menu index="1-4">
                <template #title>item fourtemplate>
                <el-menu-item index="1-4-1">item oneel-menu-item>
              el-sub-menu>
            el-sub-menu>
            <el-menu-item index="2">
              <el-icon><icon-menu />el-icon>
              <span>Navigator Twospan>
            el-menu-item>
            <el-menu-item index="3" disabled>
              <el-icon><document />el-icon>
              <span>Navigator Threespan>
            el-menu-item>
            <el-menu-item index="4">
              <el-icon><setting />el-icon>
              <span>Navigator Fourspan>
            el-menu-item>
          el-menu>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    添加了注册按钮:

    注册
    
    • 1
    在set中设置了register注册方法

    // 注册按钮点击事件

      const register = () => {
    
       app.$router.push("/SigIn");
    
      };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    在return中设置了返回函数
    register // 返回register函数
    
    • 1
    在路由中导入了注册的路径
    // 导入SignIn主页
    import SignIn from '../components/SigIn.vue'	
    
    • 1
    • 2
    设置了重定向的组件和路径
     {
    
      // 重定向到这个路径
    
      path: "/SigIn",
    
      // 导入的组件
    
      component:SignIn
    
    
    
    
    
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    在sigin页面添加了注册表单并设置了样式

    
    
    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109

    image-20230723194129026

    由于前面设置了路由守卫,导致了在login页面只能跳转到home页面,注册页面跳转不出来,我修改了路由守卫,让他不用验证用户,就能直接跳转到注册页面

    if (to.path==="/sigin")return next();
    
    • 1

    将前端的注册页面完成,现在将前端收集的数据发送到后端

    controller层
    package com.healthsystem.portplay.controller;
    
    import com.healthsystem.portplay.bean.User;
    import com.healthsystem.portplay.dao.UserDao;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class RegisterController {
        /**
         * 自动注入 UserDao 对象
         */
        @Autowired
        UserDao userDao;
        /**
         * 处理登录请求的方法
         *
         * @param user 包含登录信息的 User 对象
         * @return 返回包含登录结果和用户信息的 JSON 字符串
         */
        @RequestMapping("/register")
        public String registerUser(@RequestBody User user){
            userDao.save(user); // 假设 userDao 中有一个保存用户的方法 saveUser(User user)
    
            // 返回一个表示注册成功的 JSON 字符串
            return "{\"success\": true}";
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    dao层来访问数据层
    
    import com.healthsystem.portplay.bean.User;
    import org.apache.ibatis.annotations.Param;
    import org.springframework.stereotype.Repository;
    
    //"@Repository" 是一个注解,它用于在 Spring 框架中标识一个类为数据访问对象(DAO)。
    @Repository
    //定义接口,在login层实现
    public interface UserDao {
        // 保存用户信息到数据库
        void save(User user);
        //    "@Param" 是一个注解,它通常用于在使用 MyBatis 进行数据库访问时,标识方法参数与 SQL 语句中的参数的映射关系
        User GetUserByMessage(@Param("username") String username, @Param("password") String password);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    uesrmapper.xml文件
    DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    
    <mapper namespace="com.healthsystem.portplay.dao.UserDao">
        <select id="GetUserByMessage" resultType="com.healthsystem.portplay.bean.User">
            select *
            from `user`
            where username = #{username}
              and password = #{password}
              and state = 1;
        select>
        <insert id="save" parameterType="com.healthsystem.portplay.bean.User">
            INSERT INTO `user` (username, password, email) VALUES
                (#{username}, #{password}, #{email})
        insert>
    mapper>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    添加了注册成功会拉起登录程序,登录成功跳转到home首页

    async submitFormData(formData) {
          // 发送表单数据到后端进行处理和存储
          const response = await axios.post('/register', formData);
          const res = response.data;
         
          // 假设后端返回的 JSON 结果中有一个名为 'success' 的字段,表示注册是否成功
          if (res.success) {
            // 注册成功
            ElMessage.success('成功');
            // 在注册成功后调用登录方法,传递注册的用户名和密码
            this.login(formData.username, formData.password);
    
          } else {
            // 注册失败
            ElMessage.error('失败');
          }
        },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    在其中我尝试了几次注册成功跳转失败,我去检查后端,和前端代码,并没有发现,没有发现问题,但是我还是将前端的拉起登录部分检查,以为是数据传送不到登录页面。睡了一会发现,应该不是这个问题,可能是数据库中的账户状态问题

    当账户状态是0时,登录不了,我改变了他的状态

    image-20230725170724081

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    成功登录当首页

    设置了一个联系的三个链接

    image-20230725203829752

    在关于我们页面设置了关于我们,和联系我们的留言

    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    
      
    
      
    
      
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    添加了常见问题页面的展示

    
      
    
      
    
      
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    image-20230727212307337


    文章目录

    重新设置了home主页的布局

     
    退出 用户量 用户状态
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    设置了分界线在用户量,和用户状态上
    
                        
                            用户量
                            
                            
                            用户状态
                        
                    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    直接显示用户名

    image-20230731212157667

    将在点击用户量时,显示图标的功能去掉

    项目报错问题解决的思路
    
    在一次打开项目的时候发现有问题由于没有截图保存
    
    在查看前端问题是发现是axios跨域处理有问题
    
    在后端的报错发现是
    
    User user1 = userDao.GetUserByMessage(user.getUsername(), user.getPassword());
    
    这行有问题
    
    我去看了我的dao层发现在GetUserByMessage发现这个方法有问题
    
    我去查看了我的xml映射文件修改了我的dao层位置和user的位置,重新运行的程序发现还是不行
    
    在日志中发现我的数据库链接池可能有问题,我重新写了一次我的数据库驱动,
    
    ```xml
    **driver-class-name**: com.mysql.cj.jdbc.Driver
    ```
    
    重新跑了一次程序发现成功运行
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    我准备在我的用户量界面查询数据库信息然后通过图表显示在界面

    我想的是在后端查询到用户量按月份统计

    image-20230807172649522

    在数据库查询到的数据,然后通过dao层,通过控制器,返回给前端,在前端绘制图表

    查询数据的映射文件:
       <select id="GetUserByMessage" resultType="com.healthsystem.portplay.bean.User">
            select *
            from `user`
            where username = #{username}
              and password = #{password}
              and state = 1;
        select>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    dao层将查询到的数据转到一个集合里面,包括,用户量,月份

    List<Map<String,Object>>GetUserCountByMonth();
    
    • 1

    再通过控制器将集合里面的数据返回给前端

    
        // 获取用户数量的控制器方法
        @GetMapping("/countByMonth")
        public List<Map<String,Object>>GetUserCountByMonth(){
            return userDao.GetUserCountByMonth();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    前端绘制图表并在打开页面就直接加载

    		
            
    
              
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    import axios from "axios";
    import { Chart } from "chart.js";
    const chartCanvasRef = ref(null); // 使用 ref() 函数声明 chartCanvasRef 变量
    // 发送获取用户数量数据请求并绘制图表
    async function fetchUserCount() {
      try {
        // 向 "/countByMonth" 端点发起 GET 请求以获取用户数量数据
        const response = await axios.get("/countByMonth");
        // 从响应中提取用户数量数据
        const userCount = response.data;
    
        // 从用户数量数据中提取月份标签和用户数量
        const monthLabels = userCount.map((data) => data.month);
        const userCounts = userCount.map((data) => data.user_count);
    
        // 获取用于绘制图表的 Canvas 上下文
        const ctx = chartCanvasRef.value.getContext("2d");
    
        // 使用 Chart.js 库创建一个新的条形图
        new Chart(ctx, {
          type: "bar", // 使用 "bar" 类型绘制条形图
          data: {
            labels: monthLabels, // 使用月份标签作为 X 轴
            datasets: [
              {
                label: "用户数量", // 数据集的标签
                data: userCounts, // 用户数量数据作为 Y 轴
                backgroundColor: "rgba(75, 192, 192, 0.2)", // 条形图的背景颜色
                borderColor: "rgba(75, 192, 192, 5)", // 条形图的边框颜色
                borderWidth: 1, // 条形图的边框宽度
              },
            ],
          },
          options: {
            scales: {
              x: {
                type: "category", // 使用 "category" 类型作为 X 轴的标尺
                labels: monthLabels, // 使用月份标签作为 X 轴标尺上的刻度
              },
              y: {
                beginAtZero: true, // Y 轴从零开始
              },
            },
          },
        });
      } catch (error) {
        console.error("Error fetching user count:", error);
      }
    }
    // 页面加载时就自动显示用户量统计图表
    onMounted(fetchUserCount);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    image-20230807173538057

    打算在用户状态里面画出用户名,和用户状态显示出他的状态开关和用户等级,并且支持用户状态的修改,在添加一个可以删除用户的按钮

    在vue3的官网,找到对应的开关的样式在测试页面做好测试

    image-20230808124724482

    这个就是我需要的状态按钮的开关

     
    
    • 1

    需要的是在点击了开关之后需要对value的值进行更新,我在测试页面对他进行了他操作

    
    
    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    image-20230808125218954

    image-20230808125231080

    在这里我实现了开关打开的状态和关闭的切换

    现在需要在用户等级为用户的加上他的会员时间,需要有个起始时间,和截至时间,以天为单位

    已经在数据库里面添加了对应的会员的时间跨度

    image-20230808170059212

    只需要按照用户名来区分,并且每个用户都要包括用户名、角色、状态、起始时间和结束时间,

    SELECT
        username,
        role,
        state,
        startTime,
        endTime
    FROM
        user
    ORDER BY
        username;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    image-20230808170205794

    我在测试页面已经加上了状态转换的

    问题:

    将用户信息检索出来放入表中,设置了开关按钮,但是发现开关或者只能打开状态,关闭,不能关闭或者打开。经过几次排查,发现在点击了开关,让其关闭时候,我后端的update信息携带的state:1,不是我想要的0

    image-20230810211653238

    我点击关闭了

    image-20230810211720358

    等到轮询,发现状态为改变

    image-20230810211751043

    在浏览器开发环境发现我的后端的updatestate

    image-20230810211905386

    关闭是0

    出问题代码

    image-20230810211930911

    修改后

    image-20230810212123101

    代码更新了,点击关闭

    image-20230810212207310

    我的update也正常了

    image-20230810212235229

    功能成功了后,加入主页面

    表单界面|

    
              
    用户名 等级 开始时间 截止时间 状态
    {{ user.username }} {{ user.role }} {{ user.startTime }} {{ user.endTime }}
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    import { ref, onMounted, onBeforeUnmount } from "vue";
    
    • 1
    //菜单2
    const userStatechartCanvasRef = ref(null);
    const users = ref([]);
    
    • 1
    • 2
    • 3
    // 在组件挂载前调用异步函数,获取用户名信息
    // 定时器,每隔一段时间获取用户信息
    let pollingInterval;
    onMounted(async () => {
      try {
        // 页面加载时就自动显示用户量统计图表1
        fetchUserCount();
        // 在组件挂载前调用异步函数,获取用户名信息1
        getUserInfo();
        // 初始化时立即获取一次用户信息2
        await fetchUserInfo();
        // 设置定时器,每隔 5毫秒秒获取一次用户信息2
        pollingInterval = setInterval(fetchUserInfo, 5000); // 每 5毫 秒轮询一次
        drawChart();
      } catch (error) {
        console.error("Error fetching user information:", error);
      }
    });
    // onMounted(getUserInfo);
    // 获取用户信息,在菜单2中的
    async function fetchUserInfo() {
      try {
        const response = await axios.get("/UserInfo");
        users.value = response.data;
      } catch (error) {
        console.error("Error fetching user information:", error);
      }
    }
    // 使用 Chart.js 或其他绘图库绘制图表,
    // 根据数据进行绘制菜单2中
    function drawChart(canvasRef) {
      if (canvasRef) {
        const ctx = canvasRef.getContext("2d");
      }
    }
    // 用户状态的切换,菜单2中
    function handleSwitchChange(user) {
      const newState = user.state ? 1 : 0;
      updateDatabaseState(user.username, newState)
        .then(() => {
          // Update the user's state locally
          user.state = newState;
        })
        .catch((error) => {
          console.error("Error updating state:", error);
        });
    }
    // 将状态信息推送到后端,菜单2中
    async function updateDatabaseState(username, newState) {
      try {
        const response = await axios.put("/updateState", {
          username: username,
          state: newState,
        });
        return response.data;
      } catch (error) {
        throw error;
      }
    }
    
    // 在组件被销毁前清除定时器,菜单2中
    onBeforeUnmount(() => {
      clearInterval(pollingInterval);
    });
    
    其中有菜单1的,有12 标注
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    // 菜单2样式
    table {
      border-collapse: collapse;
      width: 100%;
    }
    
    th,
    td {
      border: 1px solid #55d515;
      padding: 8px;
      text-align: left;
    }
    
    th {
      background-color: #35e51e;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    image-20230810213243175

    添加分页功能,他的数据展示在下页展示

    image-20230811222518410

    添加了一个修改时间,时间是以天为单位来计算的

    image-20230811222846081

       
                
              
              
                
              
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    // 更新用户时间信息2
    function updateTime(user) {
      const NewstartTime = user.startTime;
      const NewendTime = user.endTime;
      updateDatabaseTime(user.username, NewstartTime, NewendTime)
        .then(() => {
          // Time updated successfully
          user.startTime = NewstartTime;
          user.endTime = NewendTime;
        })
        .catch((error) => {
          console.error("Error updating time:", error);
        });
    }
    // 将时间信息推送到后端
    async function updateDatabaseTime(username, NewstartTime, NewendTime) {
      try {
        const response = await axios.put("/updateTime", {
          username: username,
          startTime: NewstartTime,
          endTime: NewendTime,
        });
        return response.data;
      } catch (error) {
        throw error;
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    为了判断用户是否过期,想使用springboot中的定时任务来检查数据库中的用户时间信息是否过期,当你在Spring中创建一个 @Component 类时,它会被自动识别为一个Spring管理的组件。在你的情况下,你可以创建一个带有定时任务的组件,用来检查和更新用户状态。

    @Component
    public class CheckUserTimeSchedule {
        private final UserDao userDao;
    
        @Autowired
        public CheckUserTimeSchedule(UserDao userDao) {
            this.userDao = userDao;
        }
    
        @Scheduled(fixedRate = 30000)
        public void checkAndUpdateUserStatus() {
            List<Map<String, Object>> usersMapList = userDao.GetUserInfo(); // 获取所有用户信息
    
            LocalDate currentDate = LocalDate.now();
    
            for (Map<String, Object> userMap : usersMapList) {
                String username = (String) userMap.get("username");
                LocalDate endTime = (LocalDate) userMap.get("endTime");
    
                if (endTime != null && endTime.isBefore(currentDate)) {
                    // 用户的截止时间已过期,将状态更新为 false(已过期)
                    userDao.updateUserState(username, false);
                }
            }
            System.out.println("检查并更新用户状态");
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    创建一个员工表用来统计员工打卡情况

    -- 创建员工表
    CREATE TABLE employee (
        id INT NOT NULL,
        username VARCHAR(255) NOT NULL,
        sign_in DATETIME,
        PRIMARY KEY (id, username),
        FOREIGN KEY (id) REFERENCES user(id)
    );
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    给表中插入用户等级为管理员的

    insert into employee(id, username, sign_in) select id, username, null from user where role = '普通管理员';
    
    • 1

    image-20230816211904238

    员工 签到记录
    {{ Employee.username }} {{ Employee.sign_in }}
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    // 菜单3
    const chartCanvasRefe = ref(null);
    const Employees = ref([]);
    
    • 1
    • 2
    • 3
    pollingInterval = setInterval(pollEmployeeInfo, 60 * 1000); //每 1分钟 秒轮询一次
    
    • 1
    // 使用 Chart.js 或其他绘图库绘制图表,
    // 根据数据进行绘制菜单2中
    function drawChart(canvasRef, canvasRefe) {
      if (activeMenu === "1" && canvasRef) {
        const ctx = canvasRef.getContext("2d");
      }
      if (activeMenu === "3" && canvasRefe) {
        const ctx1 = canvasRefe.getContext("2d");
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    // 获取员工信息,3
    async function fetchEmployeeInfo() {
      try {
        const response = await axios.get("/employeeInfo");
        Employees.value = response.data;
      } catch (error) {
        console.error("Error fetching Employee information:", error);
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    <select id="GetEmployeeInfo" parameterType="com.healthsystem.portplay.bean.Employee">
            select username, sign_in
            from employee
            order by username;
        select>
    
    • 1
    • 2
    • 3
    • 4
    • 5
     List<Map<String, Object>> GetEmployeeInfo();
    
    • 1
    package com.healthsystem.portplay.controller;
    
    import com.healthsystem.portplay.dao.UserDao;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.List;
    import java.util.Map;
    
    @RestController
    public class EmployeeInfo {
    private final UserDao userDao;
    @Autowired
        public EmployeeInfo(UserDao userDao){this.userDao=userDao;}
        @GetMapping("/employeeInfo")
        public List<Map<String,Object>>GetEmployeeInfo(){
        return userDao.GetEmployeeInfo();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    为了区分普通管理员,和超级管理员,我添加了一个新的页面只用来显示,等级为用户的页面

    
    
    
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228

    他的功能是和超级用户差不多的

    在菜单4中查询了数据库信息中的分店信息

    设置了分页处理和菜单2一样,就不展示了

    到这简单的前后端分离项目已经完成,只是记录自己的学习,欢迎大佬指正
    t(“/updateState”, {
    username: username,
    state: newState,
    });
    return response.data;
    } catch (error) {
    throw error;
    }
    }
    // 计算当前页显示的用户列表,2
    const displayedUsers = computed(() => {
    const startIndex = (currentPage.value - 1) * pageSize.value;
    const endIndex = startIndex + pageSize.value;
    return users.value.slice(startIndex, endIndex);
    });

    // 处理分页改变事件,2
    function handlePageChange(newPage) {
    currentPage.value = newPage;
    }
    // 更新用户时间信息2
    function updateTime(user) {
    const NewstartTime = user.startTime;
    const NewendTime = user.endTime;
    updateDatabaseTime(user.username, NewstartTime, NewendTime)
    .then(() => {
    // Time updated successfully
    user.startTime = NewstartTime;
    user.endTime = NewendTime;
    })
    .catch((error) => {
    console.error(“Error updating time:”, error);
    });
    }
    // 将时间信息推送到后端,2
    async function updateDatabaseTime(username, NewstartTime, NewendTime) {
    try {
    const response = await axios.put(“/updateTime”, {
    username: username,
    startTime: NewstartTime,
    endTime: NewendTime,
    });
    return response.data;
    } catch (error) {
    throw error;
    }
    }
    // 在组件被销毁前清除定时器,菜单2中
    onBeforeUnmount(() => {
    clearInterval(pollingInterval);
    });

    
    他的功能是和超级用户差不多的
    
    在菜单4中查询了数据库信息中的分店信息
    
    设置了分页处理和菜单2一样,就不展示了
    
    到这简单的前后端分离项目已经完成,只是记录自己的学习,欢迎大佬指正
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
  • 相关阅读:
    【Python】利用tkinter开发AI对战井字棋游戏
    RabbitMQ的工作队列和交换机类型的概念与作用
    薄膜和涂层中应力产生和松弛的机理
    NLP中的对比学习:ConSERT\EsimCSE
    【面经】HTTP篇
    电子检索实体书「GitHub 热点速览 v.22.12」
    Python语音识别处理详解
    解锁Hutool魔法箱:Java开发者不可错过的神奇工具集
    Nginx学习笔记
    【FAQ】【Push Kit】 华为怎么设置角标
  • 原文地址:https://blog.csdn.net/weixin_49513202/article/details/132756199