博客项目

news2024/11/17 6:32:50

文章目录

  • 1. 创建项目
  • 2. 数据库设计
  • 3. 前置任务
    • 3.1 拦截器
    • 3.2 统一数据格式
    • 3.3 创建一个 Constant
    • 3.4 统一异常处理
    • 3.5 密码加密
  • 4. 功能实现
    • 4.1 登录功能
    • 4.2 注册功能
    • 4.3 博客列表页 (功能实现)
      • 4.3.1 左侧框
      • 4.3.2 右侧框 (分页功能 + 页面显示)
    • 4.4 博客详情页
    • 4.5 写博客功能
    • 4.6 注销功能


博客系统


前言 : 本文主要 是通过 ssm 搭配之前的 博客系统页面, 来完成一个小项目 .

项目完整代码

1. 创建项目

在这里插入图片描述


使用到的技术 :

  1. 后端 : Spring Boot + Spring MVC + MyBatis + 拦截器 / 统一异常处理 + 统一数据返回 (Spring AOP)
  2. 前端 : HTML + CSS + javaScript + jquery

2. 数据库设计


通过我们需要实现的功能 ,能够知道 需要两张表 , 第一张表 用户表 ,用来完成登录 注册 注销等功能 , 第二张表 用来存储 博客 , 查看博客等 .


1. 创建 用户列表

-- 如果存在 这个数据库就删除

drop
database if exits mywebsite;

-- 创建数据库

create
database mywebsite;

-- 选中数据库
use
mywebsite

-- 创建用户表

create table user
(
-- 系统分配
    id        int primary key auto_increment,
-- 必填
    username  varchar(255) not null,
    password  varchar(255) not null,
-- 非必填
    qq        varchar(255)  default '',
    address   varchar(255)  default '',
    crateTime datetime      default now(),
    sex       varchar(2)    default '男',
-- url 用来存放 用户 头像 图片 如果用户没有上传就使用默认的 .
    url       varchar(1024) default '阳台.png'
);

2. 创建 blog 表

create table blog
(
    blogId   int primary key auto_increment,

    title    varchar(1024) not null,

    -- 这里 一篇博客的内容可能非常多 使用 varchar可能不够 ,这里就是用 mediumtext
    content  mediumtext,

--  用户 id
    userid   int,

-- 发布时间
    postTime datetime default now(),

-- 类型
    type     varchar(255)  not null,
);


表创建好了 ,下面就可以完成一些 准备工作 ,比如 配置好环境 , 写好 拦截器 ,统一数据格式 等 .


这里统一数据格式 可以写一个类 , 通过这个类来返回 或者 通过 @ControllerAdvice + ResponseBodyAdvice 来完成 , 这里我会使用 写一个类来返回信息 .

3. 前置任务


这里先来完成 拦截器 , 统一异常处理 , 统一数据格式 .

在这里插入图片描述

application.yml

# 配置当前运行的环境 (配置文件)

# spring > profiles > active
spring:
  profiles:
    active: dev # 使用开发环境的配置文件



# 配置 mybatis xml 保存路径
mybatis:
  mapper-locations: classpath:mybatis/**Mapper.xml

# 在公共 yml 文件 来 配置 mybatis 的保存路径

application-dev.yml

# 开发环境的配置文件

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/mywebsite?characterEncoding=utf8
    username: root
    password: 1234
    driver-class-name: com.mysql.cj.jdbc.Driver # 在 8.0 之前 是没有点 jc的 -> com.mysql.jdbc.Driver


# 设置日志级别
logging:
  level:
    com:
      example:
        usermanager: debug
        # 对具体类机型日志级别设定


# 开启 MyBatis SQL 打印
mybatis:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

3.1 拦截器

这里我们实现拦截器 主要有两部 : 1. 自定义拦截器 , 2. 给拦截器设置规则 (那些 需要拦截 , 那些不需要拦截)

在这里插入图片描述

附上代码 :

AppConfig 类

package com.example.usermanager.config;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * 系统配置文件
 */

@Configuration
public class AppConfig implements WebMvcConfigurer {

    // 注入拦截器
    @Autowired
    private LoginIntercept loginIntercept;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginIntercept).
                addPathPatterns("/**")
                .excludePathPatterns("/user/login")
                .excludePathPatterns("/insert")
                .excludePathPatterns("/css/**")
                .excludePathPatterns("/fonts/**")
                .excludePathPatterns("/images/**")
                .excludePathPatterns("/js/**")
                .excludePathPatterns("/login.html");
    }
}


LoginIntercept

package com.example.blog_ssm.config;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

/**
 * 自定义拦截器
 */

@Component
public class LoginIntercept implements HandlerInterceptor {

    /**
     * true  表示已经登录 ,会继续访问目标方法
     * false  表示未登录 , 跳转到登录页面
     */

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // false : 如果 没有 session 也不会创建
        HttpSession session = request.getSession(false);

       
        if (session != null && session.getAttribute("user") != null) {

            // 表示登录成功

            return true;
        }

        // 403 当前你没有资格访问
        response.setStatus(403);

        // 重定向
        response.sendRedirect("/login.html");

        return false;
    }
}




拦截器 弄好了 , 我们可以看看效果 :

在这里插入图片描述


xml 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.blog_ssm.mapper.UserMapper">

</mapper>

3.2 统一数据格式


1. 创建一个类用来 统一 数据格式

在这里插入图片描述


附上代码 :

package com.example.blog_ssm.util;


import lombok.Data;

/**
 * 用来统一数据格式
 *
 * @param <T>
 */
@Data
public class ResponseBodyMessage<T> {

    // 1. 状态码
    private Integer status;


    // 2. 信息描述
    private String message;

    // 3. 数据
    private T data;

    public ResponseBodyMessage(Integer status, String message, T data) {
        this.status = status;
        this.message = message;
        this.data = data;
    }
}


2. 使用注解

在这里插入图片描述

3.3 创建一个 Constant


之前我们写拦截器的使用 ,通过 session 中的key 获取 user 对象时 ,写了一个 “user” , 这里可以使用一个类 ,在类里面写一个 常量 ,然后 只需要通过这个 来获取 user 即可 。

在这里插入图片描述

3.4 统一异常处理

在这里插入图片描述

代码 :

package com.example.blog_ssm.config;


import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.HashMap;

/**
 * 统一异常的拦截处理类
 */


@RestControllerAdvice

// 使用 @ControllerAdvice 需要再加一个注解 @ResponseBody (返回一个非静态页面)

public class MyExceptionAdvice {

    @ExceptionHandler(Exception.class)

    public Object exceptionAdvice(Exception e) {
        HashMap<String, Object> result = new HashMap<>();

        result.put("status", -1);

        result.put("message", "程序异常 : " + e.getMessage());

        result.put("data", "");

        return result;
    }
}

3.5 密码加密


在 util 包内 创建 PasswordUtil 类

package com.example.blog_ssm.util;

import cn.hutool.core.util.IdUtil;
import cn.hutool.crypto.SecureUtil;
import org.springframework.util.StringUtils;

/**
 * 密码工具类
 */

public class PasswordUtil {

    /**
     * 1. 加密 (加盐)
     */

    public  String encrypt(String password) {

        // 密码 : 随机盐值 + 密码

        String salt = IdUtil.simpleUUID();

        String finalPassword = SecureUtil.md5(salt + password);

        return salt + "$" + finalPassword;
    }


    /**
     * 解密
     *
     * @param password 要验证的密码 (未加密)
     * @return 数据库中的加了盐值的密码
     */

    public  boolean decrypt(String password, String securePassword) {

        boolean result = false;

        if (StringUtils.hasLength(password) && StringUtils.hasLength(securePassword)) {

            // 注意 : $ 是特殊字符 , 使用 split 分割时 需要转移
            if (securePassword.length() == 65 && securePassword.contains("$")) {
                // 随机盐值 为 32  , md5 加密的 密码 32 加上 $ 1字符 总共 65 字符

                String[] securePasswordArr = securePassword.split("\\$");

                // 盐值
                String salt = securePasswordArr[0];

                // 根据盐值 加密的密码
                String finalPassword = securePasswordArr[1];

                // 根据盐值 对新的密码进行加密
                password = SecureUtil.md5(salt + password);

                // 进行对比
                if (finalPassword.equals(password)) {
                    result = true;
                }
            }
        }
        return result;
    }
}


将 工具类 交给 spring 管理 ,后面使用 只需要注入即可 .

在这里插入图片描述


到此我们就完成了前置任务, 下面来写我们的功能

4. 功能实现

4.1 登录功能


约定一下 : 请求和响应

请求 : 

[
  {
      
      post, (登录一般使用 post)
      /user/login  
    data:{
        username : "张三",
   		password :"1234"
    }
  }
]


响应 : 


[
    {
        "status" : 1  / -1 (1 表示成功 , -1 表示失败) .
        "message" : "登录成功" / "登录失败",
        "data" : true / false
        
    }
]


图一 :

在这里插入图片描述


图二 :

在这里插入图片描述


此时 后端就完成了 下面就可以来写前端了 :


图一 :

在这里插入图片描述


图二 :

在这里插入图片描述


代码 :

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录页面</title>
    <link rel="stylesheet" href="./css/common.css">
    <link rel="stylesheet" href="./css/login.css">
    <script src="./js/jquery.js"></script>
</head>
<body>

<!-- 导航栏 -->
<div class="nav">
    <img src="./imgs/阳台.png">
    <span class="title">我的博客系统</span>

    <!--    这个标签仅仅用于占位 ,把下面几个a 标签挤到右边-->
    <div class="spacer"></div>

    <a href="#">主页</a>
    <a href="#">写博客</a>
</div>

<!--
    正文部分

    这个 login-container 是贯穿整个页面的容器
-->
<div class="login-container">

    <!-- 垂直水平居中的登录对话框    -->
    <div class="login-dialog">
        <h3>登录</h3>
        <div class="row">
            <span>用户名</span>
            <input type="text" id="username" placeholder="输入用户名">
        </div>
        <div class="row">
            <span>密码</span>
            <input type="password" id="password" placeholder="输入密码">
        </div>

        <div class="row">
            <button id="submit">提交</button>
        </div>


        <div class="row">

            <a id="insert" href = "add.html">
                注册
            </a>
        </div>
        <div class="a">没有账户? 点击上面进行注册</div>


    </div>


</div>

<script>

    // 通过 id 选中输入框 ,
    let username = document.querySelector("#username");

    // 通过 id 选中密码框
    let password = document.querySelector("#password");


    let submit = document.querySelector("#submit");

    // 点击按钮后触发
    submit.onclick = function () {

        // 1. 判断 username or password
        if (jQuery.trim(username.value) === '') {
            alert("请先输入用户名");
        }
        if (jQuery.trim(password.value) === '') {
            alert("请先输入密码")
        }

        // 此时 用户 和密码 都有了 ,发送 请求

        $.ajax({
            url: "/user/login",
            type: "POST",
            data: {
                "username": username.value,
                "password": password.value
            },
            // 回调函数
            success: function (result) {
                if (result != null && result.data.status > 0) {
                    // 登录成功 , 跳转到 博客列表页
                    location.href = "blog_list.html";
                } else {
                    //登录 登录失败 ,
                    alert("登录失败,请重新输入")
                }
            }

        })
    }


</script>


</body>
</html>

登录功能搞完 : 下面就可以来弄 注册功能

4.2 注册功能


约定一下请求和响应 :

在这里插入图片描述


请求: 

[
    {
        post,
        url : /user/add
        data:{
        	必填
        	"username" : xxx,
        	"password" : xxx,
        	
        	非必填
        	"address" : xxx,
        	......
    	}
    }
]

响应 : 

[
    {
        data:{
            "status" : 1 / -1  (注册成功 / 注册失败)
            "message" : "",
            "data" : true / false
        }
    }
]


图一 :

在这里插入图片描述


图二 :

在这里插入图片描述


图三 :

在这里插入图片描述


图四 :

在这里插入图片描述

附上代码 :

  /**
     * 2. 注册功能
     */

    @RequestMapping(value = "/add")
    @Transactional
    
    public ResponseBodyMessage<Boolean> addUser(User user, @RequestPart(required = false, value = "filename") MultipartFile file) {

        if (user == null) {
            return new ResponseBodyMessage(-1, "注册失败", false);
        }

        // 1. 判断 必填参数是否为空 (这里可以不写 ,前端大概率 是会判断的 。 )
        if ("".equals(user.getUsername())) {
            return new ResponseBodyMessage<>(-1, "注册失败, 当前用户为输入用户名", false);
        }

        if ("".equals(user.getPassword())) {
            return new ResponseBodyMessage<>(-1, "注册失败,当前用户未输入密码", false);
        }


        // 2. 校验用户名的 唯一性 :  如果 用户名已经纯在了 那么就不能注册
        User user2 = userService.getUserByUserName(user.getUsername());

        if (user2 != null) {
            return new ResponseBodyMessage<>(-1, "注册失败, 用户名已存在", false);
        }

        // 3. 手动设置 为 '' 的数据

        if ("".equals(user.getAddress())) {
            user.setAddress(null);
        }

        if ("".equals(user.getQq())) {
            user.setQq(null);
        }

        if ("".equals(user.getSex())) {
            user.setSex(null);
        }

        if ("".equals(user.getUrl())) {
            user.setUrl(null);
        }

        // 4. 对密码进行加密操作

        user.setPassword(passwordUtil.encrypt(user.getPassword()));


        // 5. 如果用户 上传了头像 ,可以将图片存入到本地

        if (file != null) {
            // 此时上传了头像 :
            // 获取到文件名 + 类型
            String fileNameAndType = file.getOriginalFilename();

            // 比如文件名为 : 阳台.png , 此时可以获取到 . 的 下标
            int index = fileNameAndType.lastIndexOf(".");

            // 从 index 位置开始截取
            String postfix = fileNameAndType.substring(index);

            // 判断一下 图片的格式是否符合预期要求
            if (".jpg".equals(postfix) || ".png".equals(postfix)) {
                // 通过 uuid 来设置文件名

                String uuid = IdUtil.simpleUUID();

                String imgFileStr = uuid + postfix;


                // 创建文件

                String path = IMAGE_PATH + imgFileStr;

                File imgFile = new File(path);


                if (!imgFile.exists()) {
                    imgFile.mkdir();
                }


                try {
//                指定图片 , 上传之后的存储位置
                    file.transferTo(imgFile);

                    // 文件上传成功 :

                    user.setUrl(imgFileStr);

                    userService.addUser(user);

                    return new ResponseBodyMessage<>(1, "注册成功", true);

                } catch (IOException e) {
//                e.printStackTrace();
                    // 如果 创建失败 , 手动事务回滚
                    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                }

                // 此时 注册失败

                return new ResponseBodyMessage<>(-1, "注册失败", false);

            } else {
                return new ResponseBodyMessage<>(-1, "图片格式有误", false);
            }

        }


        // 6. 调用 userService 中的 addUser 方法 进行用户添加 (此时未上传图片 , 图片 为 默认)

        Integer ret = userService.addUser(user);

        if (ret != 1) {
            return new ResponseBodyMessage<>(-1, "注册失败", false);
        }

        return new ResponseBodyMessage<>(1, "注册成功", true);
    }


后端写完, 来完成我们的前端 :


之前并没有完成 注册页面 , 这里直接来拷贝一下


1. add.html

<!DOCTYPE html>
<!-- 网页使用的语言 -->
<html lang="zh-CN">
<head>
    <!-- 指定字符集 -->
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>添加用户</title>

    <link href="css/bootstrap.min.css" rel="stylesheet">
    

    <style>
        body {
            background-image: url("./images/阳台.png");
            background-repeat: no-repeat;
            background-size: cover;
        }

    </style>
</head>
<body>
<div class="container" style="width: 400px;">
    <h3 style="text-align: center;">添加用户</h3>
    <div class="form-group">
        <label for="loginname">登录名:</label>
        <input type="text" class="form-control" id="loginname" name="username" placeholder="请输入登录名"/>
    </div>

    <div class="form-group">
        <label for="username">姓名:</label>
        <input type="text" class="form-control" id="username" name="name" placeholder="请输入姓名"/>
    </div>

    <div class="form-group">
        <label for="password">密码:</label>
        <input type="password" class="form-control" id="password" name="password" placeholder="请输入密码"/>
    </div>

    <div class="form-group">
        <label for="password2">确认密码:</label>
        <input type="password2" class="form-control" id="password2" name="password" placeholder="请输入密码"/>
    </div>

    <div class="form-group">
        <label>性别:</label>
        <input id="man" type="radio" name="sex" value="" checked="checked"/>&nbsp;&nbsp;&nbsp;
        <input id="women" type="radio" name="sex" value=""/></div>

    <div class="form-group">
        <label for="age">年龄:</label>
        <input type="number" class="form-control" id="age" name="age" placeholder="请输入年龄"/>
    </div>

    <div class="form-group">
        <label for="address">籍贯:</label>
        <select name="address" id="address" class="form-control">
            <option value="北京">北京</option>
            <option value="上海">上海</option>
            <option value="广州">广州</option>
            <option value="深圳">深圳</option>
            <option value="成都">成都</option>
            <option value="杭州">杭州</option>
            <option value="重庆">重庆</option>
            <option value="西安">西安</option>
            <option value="武汉">武汉</option>
            <option value="沧州">沧州</option>
            <option value="江西">江西</option>
        </select>
    </div>

    <div class="form-group">
        <label for="qq">QQ:</label>
        <input type="text" id="qq" class="form-control" name="qq" placeholder="请输入QQ号码"/>
    </div>

    <div class="form-group">
        <label for="email">Email:</label>
        <input type="text" id="email" class="form-control" name="email" placeholder="请输入邮箱地址"/>
    </div>

    <!--    style="display: none;"-->

    <div class="form-group" id="adminDiv">
        <label for="email">管理员:</label>
        <input id="admin_yes" type="radio" name="isadmin" value="1"/>&nbsp;&nbsp;&nbsp;
        <input id="admin_no" type="radio" name="isadmin" value="0" checked="checked"/></div>

    <div class="form-group" style="text-align: center">
        <input id="btn_sub" class="btn btn-primary" type="button" value="提交" onclick="mysub()"/>
        <input id="btn_back" class="btn btn-default" type="button" value="返回" onclick="location.href='list.html'"/>
    </div>

</div>




</body>
</html>


用到的 css 可以到 我 的资源里面下载 出来 , 比较多 这里就不拷贝到上面 了 .

在这里插入图片描述


这里我们需要使用 FormData 来 发送我们的数据 可以看来看一下这 : FormData

在这里插入图片描述

代码 :

<!DOCTYPE html>
<!-- 网页使用的语言 -->
<html lang="zh-CN">
<head>
    <!-- 指定字符集 -->
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>添加用户</title>
    <link href="./css/bootstrap.min.css" rel="stylesheet">
    <script src="./js/jquery.js"></script>
    <style>
        body {
            background-image: url("./imgs/阳台.png");
            background-repeat: no-repeat;
            background-size: cover;
        }

        #image {
            width: 400px;
            height: 250px;
            border: 1px solid #eee;
        }

        .message {
            width: 110px;
            height: 50px;
            line-height: 50px;
            font-weight: 600;
        }

    </style>
</head>
<body>

<form enctype="multipart/form-data" id="form1">

    <div class="container" style="width: 400px;">
        <h3 style="text-align: center;">添加用户</h3>


        <div class="form-group">
            <label for="username">姓名:</label>
            <input type="text" class="form-control" id="username" name="name" placeholder="请输入姓名"/>
        </div>

        <div class="form-group">
            <label for="password">密码:</label>
            <input type="password" class="form-control" id="password" name="password" placeholder="请输入密码 "/>
        </div>

        <div class="form-group">
            <label for="password2">确认密码:</label>
            <input type="password2" class="form-control" id="password2" name="password" placeholder="请输入密码"/>
        </div>

        <div class="form-group">
            <label>性别:</label>
            <input id="man" type="radio" name="sex" value="" checked="checked"/>&nbsp;&nbsp;&nbsp;
            <input id="women" type="radio" name="sex" value=""/></div>


        <div class="form-group">
            <label for="address">籍贯:</label>
            <select name="address" id="address" class="form-control">
                <option value="">可以不选择</option>
                <option value="北京">北京</option>
                <option value="上海">上海</option>
                <option value="广州">广州</option>
                <option value="深圳">深圳</option>
                <option value="成都">成都</option>
                <option value="杭州">杭州</option>
                <option value="重庆">重庆</option>
                <option value="西安">西安</option>
                <option value="武汉">武汉</option>
                <option value="沧州">沧州</option>
                <option value="江西">江西</option>
            </select>
        </div>

        <div class="form-group">
            <label for="qq">QQ:</label>
            <input type="text" id="qq" class="form-control" name="qq" placeholder="请输入QQ号码 (非必填) "/>
        </div>


        <div class="form-group">

            <input type="file" name="filename" id="imgFile">

            <span class="message">图片样式: </span>
            <img src="" id="image"/>

        </div>


        <div class="form-group" style="text-align: center">
            <input id="btn_sub" class="btn btn-primary" type="button" value="提交"/>
            <input id="btn_back" class="btn btn-default" type="button" value="返回" onclick="location.href='list.html'"/>
        </div>

    </div>

</form>


<!--
     //   拿到 input  type 为 radio 中的内容 即 获取 男 女
        let sex = $('input[name=sex]:checked').val();

        let address = $("#address").val();
-->

<script>


    let imgFile = document.querySelector("#imgFile");

    // 这一部分 : 当我们上传图片后 , 我们的 图片样式 就会将图片显示出来

    imgFile.onchange = function () {

        let img = document.querySelector("#image");

        let image = imgFile.files[0];

        // let formData = new FormData();

        if (image) {
            // formData.append('filename', image);
            img.src = window.URL.createObjectURL(image);
        }

    }

    // 当点击 提交按钮后 构造数据 , 通过 ajax 发送请求给后端

    let submit = document.querySelector("#btn_sub");

    submit.onclick = function () {

        let username = document.querySelector("#username");

        let password = document.querySelector("#password");

        let password2 = document.querySelector("#password2");

        // 通过 jquery 拿到  性别框里面的内容

        let sex = jQuery('input[name=sex]:checked').val();


        let address = document.querySelector('#address').value;

        let qq = document.querySelector("#qq").value;

        // jQuery.trim 去掉 前后空格
        if (jQuery.trim(username.value) === '') {
            alert("请先输入用户名!!")
            // 将焦点设置到 id 为 username 的输入 框上
            username.focus();
            return false;
        }

        if (jQuery.trim(password.value) === '') {
            alert("请先输入密码");
            password.focus();
            return false;
        }
        if (jQuery.trim(password2.value)  === '') {
            alert("请输入确认密码");
            password2.focus();
        }

        if (password.value !== password2.value) {
            alert("两次密码不同,请重新输入")
            password.focus();

            return false;
        }


        // 使用 formData 类来返回 
        let formData = new FormData();

        formData.append('username', username.value);

        formData.append('password', password.value);

        formData.append('qq', qq);

        formData.append('sex', sex);

        formData.append('address', address);

        let img = document.querySelector("#image");

        // 获取文件
        let image = imgFile.files[0];


        // 这里也可以通过 image 判断 
        if (img.src === '') {
            console.log('未上传图片 !!! ');
            formData.append('filename', null);
        } else {
            formData.append('filename', image)
        }
        // 通过 ajax 发送请求
        jQuery.ajax({
            type: "POST",
            url: "/user/add",
            data: formData,
            processData: false,
            contentType: false,
            success: function (result) {
                if (result != null && result.data.status > 0) {
                    alert('注册成功!');
                    location.href = "login.html";
                }else {
                    alert('注册失败')
                }
            },
            error : function(){
                alert("出错了, 请稍后再试!!!")
            }
        })
    }

</script>

</body>
</html>

上传文件使用到的 input 标签, 和 使用到的change 事件 : input 标签 change 事件


效果 :

在这里插入图片描述


到此我们的注册功能就完成了 , 下面就来写我们的博客列表页 .

4.3 博客列表页 (功能实现)

4.3.1 左侧框


图一 :

在这里插入图片描述


AppConfig 类

package com.example.blog_ssm.config;

import com.example.blog_ssm.util.PasswordUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class AppConfig implements WebMvcConfigurer {

    // 1. 注入拦截器
    @Autowired
    private LoginIntercept loginIntercept;


    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/product/**").addResourceLocations("file:D:/ret/");
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginIntercept)
                .addPathPatterns("/**")
                .excludePathPatterns("/user/login")
                .excludePathPatterns("/user/add")
                .excludePathPatterns("/css/**")
                .excludePathPatterns("/js/**")
                .excludePathPatterns("/imgs/**")
                .excludePathPatterns("/login.html")
                .excludePathPatterns("/add.html")
                .excludePathPatterns("/product/**");
    }

    @Bean
    public PasswordUtil passwordUtil() {
        return new PasswordUtil();
    }
}


图二 :

在这里插入图片描述


后端完成了 ,下面就可以来完成我们的前端 :

在这里插入图片描述


图三 :

在这里插入图片描述


图四 :

在这里插入图片描述


图五 :

在这里插入图片描述


图六 :

在这里插入图片描述


图七 :

在这里插入图片描述

<script>

    // 这个 ajax 获取登录状态 :
    jQuery.ajax({
        type: "GET",
        url: "/user/getuser",
        success: function (result) {

            if (result != null && result.data.status > 0) {

                let image = document.querySelector("#image1");

                image.src = "product/" + result.data.data.url;

                let image2 = document.querySelector("#image2");

                image2.src = "product/" + result.data.data.url;

                // 将用户名 换上去

                let username = document.querySelector("#username");

                username.innerHTML = result.data.data.username;


            }
        }
    })


    // 这个 ajax 获取 分类
    jQuery.ajax({

        type: "GET",
        url: "/blog/gettype",
        success: function (result) {

            if (result != null && result.data.status > 0) {

                let number1 = document.querySelector("#number1");
                let number2 = document.querySelector("#number2");

                number1.innerHTML = result.data.data[0]
                number2.innerHTML = result.data.data[1];

            } else {
                alert("分类设置失败 !!! ")
            }


        },
        error: function () {
            alert("出错了, 请稍后再试!!!")
        }

    })
</script>


左侧部分 就完成了, 下面就来完成我们的右侧部分 ,这里我们可以写一个分页器

4.3.2 右侧框 (分页功能 + 页面显示)


图一 :

在这里插入图片描述

图二 :

在这里插入图片描述


图三 :

在这里插入图片描述

图四 :

在这里插入图片描述

这里不好截图 直接看代码 :

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>博客列表页</title>
    <link rel="stylesheet" href="./css/common.css">
    <link rel="stylesheet" href="./css/blog_list.css">
    <script src="./js/jquery.js"></script>
    <link href="css/bootstrap.min.css" rel="stylesheet">

</head>
<body>


<!-- 导航栏 -->
<div class="nav">


    <img src="./imgs/阳台.png" id="image1">
    <span class="title">我的博客系统</span>

    <!--    这个标签仅仅用于占位 ,把下面几个a 标签挤到右边-->
    <div class="spacer"></div>

    <a href="#">主页</a>
    <a href="#">写博客</a>
    <a href="#">注销</a>
</div>

<!-- 页面主体部分 -->
<div class="container">
    <!--    左侧信息-->

    <div class="container-left">

        <!--        使用 这个 .card 表示用户信息-->
        <div class="card">

            <img src="./imgs/girl.png" alt="图片显示失败" id="image2">


            <!--            用户名-->
            <h3 id="username"></h3>


            <a href="#">Gitee 地址</a>
            <div class='counter'>
                <span>文章</span>
                <span>分类</span>
            </div>
            <div class="counter">
                <span id="number1">1</span>
                <span id="number2">2</span>
            </div>
        </div>
    </div>

    <!--    右侧信息-->
    <div class="container-right">
        <!--        &lt;!&ndash;-->
        <!--            表示一篇博客-->
        <!--        &ndash;&gt;-->

        <!--        <div class="blog">-->
        <!--            &lt;!&ndash;-->
        <!--                   博客标题-->
        <!--            &ndash;&gt;-->
        <!--            <div class="title">我的第一篇博客</div>-->
        <!--            &lt;!&ndash;-->
        <!--                发布时间-->
        <!--            &ndash;&gt;-->
        <!--            <div class="data">-->
        <!--                2023-03-02-->
        <!--            </div>-->

        <!--            &lt;!&ndash;-->
        <!--                博客的摘要-->
        <!--            &ndash;&gt;-->
        <!--            <div class="desc">-->
        <!--                从今天起 , 我要认真敲代码-->
        <!--                Lorem ipsum dolor sit amet, consectetur adipisicing elit. Sint eaque facilis perferendis! Numquam-->
        <!--                neque voluptatum ab vero expedita possimus fuga eos, illo sapiente delectus quidem natus maiores,-->
        <!--                ipsum impedit rerum?-->
        <!--            </div>-->

        <!--            &lt;!&ndash;-->
        <!--                查看全文按钮-->
        <!--            &ndash;&gt;-->
        <!--            <a href="#">查看全文 &gt;&gt;</a>-->


        <!--        </div>-->

        <div id="page">
            <nav aria-label="Page navigation">
                <ul id="all" class="pagination">

                    <li class="active"><a href="javascript:firstPage();">首页</a></li>
                    <li><a href="javascript:beforePage();">上一页</a></li>
                    <li><a href="javascript:nextPage();">下一页</a></li>
                    <li><a href="javascript:lastPage();">末页</a></li>

                    <span id="pageinfo" style="font-size: 20px;margin-left: 10px;">

                </span>
                </ul>
            </nav>
        </div>
    </div>
</div>

<script>

    // 这个 ajax 获取登录状态 :
    jQuery.ajax({
        type: "GET",
        url: "/user/getuser",
        success: function (result) {

            if (result != null && result.data.status > 0) {

                let image = document.querySelector("#image1");

                image.src = "product/" + result.data.data.url;

                let image2 = document.querySelector("#image2");

                image2.src = "product/" + result.data.data.url;

                // 将用户名 换上去

                let username = document.querySelector("#username");

                username.innerHTML = result.data.data.username;


            }
        }
    })


    // 这个 ajax 获取 分类
    jQuery.ajax({

        type: "GET",
        url: "/blog/gettype",
        success: function (result) {

            if (result != null && result.data.status > 0) {

                let number1 = document.querySelector("#number1");
                let number2 = document.querySelector("#number2");

                number1.innerHTML = result.data.data[0]
                number2.innerHTML = result.data.data[1];

            } else {
                alert("分类设置失败 !!! ")
            }


        },
        error: function () {
            alert("出错了, 请稍后再试!!!")
        }

    })


    //  分页功能 :

    // 1. 当前的页码
    let pIndex = 1;

    // 2. 每页显示多少篇博客
    let pSize = 2;

    // 3. 总页数
    let totalPage = 0;

    // 4. 总条数 (当前所有的博客数目)
    let totalCount = 0;


    // 通过 ajax 获取 总页数 和 总条数


    function getList() {


        jQuery.ajax({
            type: "GET",
            url: "/blog/listbypage",
            data: {
                "pIndex": pIndex,
                "pSize": pSize
            },
            success: function (result) {

                if (result != null && result.data.status > 0) {

                    // 总博客数
                    totalCount = result.data.data.count;

                    // ceil 四舍五入并返回大于等于给定数字的最小整数。

                    totalPage = Math.ceil(parseInt(totalCount) / pSize);

                    let size = result.data.data.list.length;

                    // rightDiv  后面创建的 元素需要挂载 rightDiv 上
                    let rightDiv = document.querySelector(".container-right");

                    for (let i = 0; i < size; i++) {

                        let ret = result.data.data.list[i];

                        let blogDiv = document.createElement('div');

                        // 引入 class 属性
                        blogDiv.className = "blog";

                        // 1. 构造标题
                        let titleDiv = document.createElement('div');

                        titleDiv.innerHTML = ret.title;

                        titleDiv.className = "title";

                        // 将 titleDiv 挂到 blogDiv上
                        blogDiv.appendChild(titleDiv);

                        // 2. 构造 发布时间
                        let dataDiv = document.createElement('div');

                        dataDiv.innerHTML = ret.postTime;

                        dataDiv.className = "data";

                        blogDiv.appendChild(dataDiv);

                        // 3. 构造 文章描述
                        let descDiv = document.createElement('div');

                        descDiv.innerHTML = ret.content;

                        descDiv.className = "desc";

                        blogDiv.appendChild(descDiv);

                        // 4. 构造 查看全文按钮

                        let a = document.createElement('a');

                        a.innerHTML = "查看全文 &gt;&gt;";

                        // 重点 : 这里我们点击查看全文 跳转到 博客详情页 ,这里可以 在 url 里面添加一个 博客id ,
                        // 后面在 博客详情页就可以通过这个 blogId 获取到文章.

                        a.href = "blog_detail.html?blogId=" + ret.blogId;

                        blogDiv.appendChild(a);

                        rightDiv.appendChild(blogDiv);

                    }


                    // 将 分页器 挂载 构造好的数据 最后面 .
                    let page = document.querySelector("#page");

                    rightDiv.appendChild(page);


                } else {
                    alert("获取失败!!!")
                }
            },
            error: function () {
                alert("出错了,请稍后在尝试!!!")
            }
        })
    }

    // getList();


    // 首页
    function firstPage() {
        location.href = "blog_list.html?pIndex=1"
    }


    // 上一页
    function beforePage() {

        if (pIndex > 1) {

            pIndex = parseInt(pIndex) - 1;

            location.href = "blog_list.html?pIndex=" + pIndex;
        } else {
            alert("已经是首页了!!!")
        }
    }

    // 下一页
    function nextPage() {
        if (pIndex < totalPage) {

            pIndex = parseInt(pIndex) + 1;

            location.href = "blog_list.html?pIndex=" + pIndex;
        } else {
            alert("已经是末页了!!!")
        }
    }

    // 末页
    function lastPage() {
        location.href = "blog_list.html?pIndex=" + totalPage;
    }


    // 使用这个 方法来初始话页面
    function initPage() {
        // 获取 当前页面的 查询字符串  比如 :?pIndex=2
        let url = location.search;

        if (url != '') {
            // 将 问好 去掉 此时就剩下了 pIndex=2 (假设页数是2)
            url = url.substring(1);

            // let kvs = url.split("&");

            let kvs = url.split("=");
			
            // 获取到 跳转后的页数 ,
             pIndex = kvs[1];
        }

        // 通过 getList 方法去获取到数据
        getList();

    }


// 通过 initPage 方法来 初始化页面 .
    initPage();

</script>

</body>
</html>

Math.ceil() - JavaScript | MDN (mozilla.org)


页面效果:

在这里插入图片描述


上面我们的代码其实还有一个没中不足的地方 , 我们的文章 应该是 后发布的在前面, 而不是 以前发布的在前面 ,这里就来修改一下 ,也非常简单 , 就是给我们的SQL 加一个 排序 (按照时间排序即可)

在这里插入图片描述

到此我们的博客列表页的内容就完成了 , 下面就来完成我们的 博客详情页

4.4 博客详情页


这里我们主要实现查看 博客的功能 .


图一 :

在这里插入图片描述


图二 :

在这里插入图片描述


图三 :

在这里插入图片描述


图四 :

在这里插入图片描述


图五 :

在这里插入图片描述


图六 :

在这里插入图片描述


图七 :

在这里插入图片描述


前端代码 :

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>博客详情页</title>
    <link rel="stylesheet" href="./css/common.css">
    <link rel="stylesheet" href="./css/blog_detail.css">
    <script src="./js/jquery.js"></script>
    <!--    引入 editor.md 的依赖-->
    <link rel="stylesheet" href="./editor.md/css/editormd.min.css">
    <script src="./editor.md/lib/marked.min.js"></script>
    <script src="./editor.md/lib/prettify.min.js"></script>
    <script src="./editor.md/editormd.min.js"></script>

</head>
<body>

<!-- 导航栏 -->
<div class="nav">
    <img src="./imgs/阳台.png">
    <span class="title">我的博客系统</span>

    <!--    这个标签仅仅用于占位 ,把下面几个a 标签挤到右边-->
    <div class="spacer"></div>

    <a href="#">主页</a>
    <a href="#">写博客</a>
    <a href="#">注销</a>
</div>

<!-- 页面主体部分 -->
<div class="container">
    <!--    左侧信息-->

    <div class="container-left">

        <!--        使用 这个 .card 表示用户信息-->
        <div class="card">
            <img src="./imgs/girl.png" alt="图片显示失败">
            <!--            用户名-->
            <h3></h3>
            <a href="#">Gitee 地址</a>
            <div class='counter'>
                <span>文章</span>
                <span>分类</span>
            </div>
            <div class="counter">
                <span>1</span>
                <span>2</span>
            </div>
        </div>
    </div>

    <!--    右侧信息-->
    <div class="container-right">

        <!-- 博客标题 -->
        <h3 class="title" id="title">我的第一篇博客</h3>

        <!-- 博客发布时间-->
        <div class="date">2023-03-02</div>
        <!-- 博客正文 -->
        <div id="content">
            <!--            <P>-->
            <!--                从今天开始我要认真敲代码-->
            <!--                从今天开始我要认真敲代码-->
            <!--                从今天开始我要认真敲代码-->
            <!--                从今天开始我要认真敲代码-->
            <!--            </P>-->
            <!--            <P>-->
            <!--                从今天开始我要认真敲代码-->
            <!--                从今天开始我要认真敲代码-->
            <!--                从今天开始我要认真敲代码-->
            <!--                从今天开始我要认真敲代码-->
            <!--            </P>-->
            <!--            <P>-->
            <!--                从今天开始我要认真敲代码-->
            <!--                从今天开始我要认真敲代码-->
            <!--                从今天开始我要认真敲代码-->
            <!--                从今天开始我要认真敲代码-->
            <!--            </P>-->
        </div>
    </div>

</div>


<script>


    function getBlogDetail() {
        jQuery.ajax({
            type: "GET",
            // location.search 就是 ?blogId=x
            url: "/blog/getblog" + location.search,
            success: function (result) {

                if (result != null && result.data.status > 0) {

                    let data = result.data.data;

                    // 1. 构造博客标题
                    let title = document.querySelector("#title");

                    title.innerHTML = data.title;

                    // 2. 构造发布时间
                    let dateDiv = document.querySelector(".date");

                    dateDiv.innerHTML = data.postTime;

                    // 3. 构造正文部分
                    // let content = document.querySelector(".content");
                    //
                    // content.innerHTML = data.content;

                    // 使用 editormd.md 自带的方法 对内容进行渲染
                    editormd.markdownToHTML('content', {
                        markdown: data.content
                    })

                    let userid = data.userid;

                    jQuery.ajax({
                        type: "GET",
                        url: "/user/getuserbyid",
                        data: {
                            "userid": userid,
                        },
                        success: function (result) {
                            if (result != null && result.data.status > 0) {
                                let data = result.data.data;


                                // 通过子类选择器选中 img 元素

                                let img = document.querySelector(".card>img");

                                img.src = "product/" + data.user.url;

                                let title = document.querySelector(".card>h3");

                                title.innerHTML = data.user.username;

                                let spanArr = document.querySelectorAll(".counter>span")

                                spanArr[2].innerHTML = data.blogNumber;

                                spanArr[3].innerHTML = data.type;


                            } else {
                                alert("设置错误!!!")
                            }
                        },
                        error: function () {
                            alert("出错了,请稍后再试!!")
                        }
                    })


                }
            }
        })
    }

    getBlogDetail();


    function getLoginUser() {
        jQuery.ajax({
            type: "GET",
            url: "/user/getuser",

            success: function (result) {
                if (result != null && result.data.status > 0) {
                    let img = document.querySelector(".nav>img");

                    img.src = "product/" + result.data.data.url;
                }
            }
        })
    }

    getLoginUser();

</script>

</body>
</html>




4.5 写博客功能


图一 :

在这里插入图片描述


图二 :

在这里插入图片描述


图三 :

在这里插入图片描述


图四 :

在这里插入图片描述


图五 :

在这里插入图片描述


最后完成我们的注销 功能 这个 小项目就完成了 .

4.6 注销功能


图一 :

在这里插入图片描述


图二 :

在这里插入图片描述


到此 这个 小项目就完成了, 其实这个项目还有很多东西可以加 ,这些大家都可以 自由发挥 .

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/398903.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Redis】搭建主从集群

目录 集群结构 准备实例和配置 启动 开启主从关系 测试 集群结构 我们搭建的主从集群结构如图&#xff1a; 共包含三个节点&#xff0c;一个主节点&#xff0c;两个从节点。 这里我们会在同一台虚拟机中开启3个redis实例&#xff0c;模拟主从集群&#xff0c;信息如下&…

51单片机DS18B20的使用

文章目录前言一、DS18B20介绍二、单总线协议三、DS18B20引脚说明四、DS18B20程序编写1.DS18B20复位函数2.DS18B20存在检测3.DS18B20读取一个bit和一个byte函数4.DS18B20写一个字节函数5.开始温度转换函数6.DS18B20初始化函数7.DS18B20读取温度函数五、代码测试总结前言 本篇文…

ERP系统哪家比较好?

ERP系统哪家好&#xff1f;在选择ERP系统时&#xff0c;我们可以按照这三个维度&#xff0c;然后再按照需求去选择ERP系统。 市面上ERP软件大概可以分为三大类&#xff1a; ① 标准ERP应用&#xff1a;功能比较固定&#xff0c;难以满足个性化需求&#xff0c;二次开发难度很高…

MySQL对表操作

结束了上一章内容&#xff0c;我们对数据库的操作有一定的了解&#xff0c;本章内容就是针对表中的数据进行操作的。 针对表中数据的操作绝大部分都是增删改查&#xff08;CRUD&#xff09;&#xff0c;CRUD也就是四个单词的缩写&#xff1a; 增加(Create)、查询(Retrieve)、…

基于微前端qiankun的tab切换

文章目录背景主应用要做的1、新建tab组件2、引入组件3、tabs.js核心4、开始使用子应用要做的1、将父应用传给子应用的props挂载在Vue对象上2、创建核心逻辑3、将核心逻辑混入到App.vue注意事项分析zy/qiankun-tabs源码index.jsactions.jstabs.js最终效果背景 我们的平时做后台…

1.1 误差的来源

不难发现&#xff0c;考察用计算机解决科学计算问题时所经历的几个环节&#xff08;如图1-1所示&#xff09;&#xff0c;其中每一步都可能产生误差&#xff0c;首先,数学模型是通过对实际问题进行抽象与简化得到的&#xff0c;它与实际问题之间有误差&#xff0e;数学模型与实…

Nginx 是如何进化到 kong 的

Nginx 是如何进化到 kong 的 在传统的互联网服务中,对网关的主要诉求就是反向代理、负载均衡、路由等基础功能。 一个经典的业务的架构图一般是采用四层 LVS 做 对外 IP 收敛,在七层采用 Nginx 来负责七层 HTTPS 协议接入,反向代理、负载均衡、路由。 Nginx 的每个 Worker…

DevOps工具集合

简介 DevOps&#xff08;Development和Operations的组合词&#xff09;是一组过程、方法与系统的统称&#xff0c;用于促进开发&#xff08;应用程序/软件工程&#xff09;、技术运营和质量保障&#xff08;QA&#xff09;部门之间的沟通、协作与整合。 它是一种重视“软件开…

近红外染料IR-825,IR-825 Maleimide,IR-825 Mal,IR-825马来酰亚胺

中文名称&#xff1a;荧光染料IR 825马来酰亚胺&#xff0c;近红外染料IR-825马来酰亚胺英文名称&#xff1a;IR825 Maleimide&#xff0c;IR-825 Maleimide&#xff0c;IR 825 Maleimide&#xff0c;IR825 Mal&#xff0c;IR-825 Mal试剂规格&#xff1a;10mg&#xff0c;25mg…

算法小抄5-原地哈希

书接上回,学会了数组中重复数字的解法三,相信接下来的题也难不倒你 找到数组中消失的数字 题目链接 题意 对于一个大小为n的数组,数组中所有的数都在[1,n]内,其中有些数字重复了,由于有些数字重复了,另一些数字就一定会确实,这次需要找到所有缺少的数字并且返回结果 有没有发…

反转链表相关的练习(上)

目录 一、反转链表 二、反转链表 || 三、两两交换链表中的结点 四、K 个一组翻转链表 一、反转链表 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,…

ip-guardip-guard如何通过准入网关对指定的服务器进行通讯加密保护?

1、准入网关在高级配置设置受保护服务器; WEB管理界面【系统工具】,点击【配置管理】,点击参数设置,进入高级配置界面,输入配置内容即可。 [ControlServer]

使用阈值图修改角色脸部阴影

大家好&#xff0c;我是阿赵。 之前介绍了通过修改模型顶点法线的方法&#xff0c;来解决角色模型脸部光影问题。这里再顺便介绍一下第二种方法&#xff0c;使用阈值图修改角色脸部阴影 一、角色脸部光影的问题 这个问题之前讨论过&#xff0c;由于角色脸部法线复杂&#xff0…

2023年最新交通航线(飞机、高铁)信息数据合集(含经纬度匹配)

中国高铁航线数据库Chinese High-speed Rail and Airline Database&#xff0c;CRAD&#xff09;是一个专门收集和管理航空公司和高铁公司交通航线信息的数据仓库。它包含了航线的起始点、终止点、中转点、飞行时间、票价、座位数、乘客数量、货物数量等信息。 该数据仓库可以运…

Fortinet 发布《2022下半年度全球威胁态势研究报告》,七大发现值得关注

全球网络与安全融合领域领导者Fortinet&#xff08;NASDAQ&#xff1a;FTNT&#xff09;&#xff0c;近日发布《2022 下半年度全球威胁态势研究报告》。报告指出&#xff0c;相对于组织攻击面的不断扩大以及全球威胁态势的持续演进&#xff0c;网络犯罪分子设计、优化技术与战术…

设计模式---单例模式

目录 1 简介 2 实现 3 单例模式的几种实习方式 1. 饿汉式 2. 懒汉式&#xff0c;线程不安全 3. 懒汉式&#xff0c;线程安全 4. 双检锁/双重校验锁(DCL, double-check locking) 5. 登记式/静态内部类 4 单例模式的优缺点 1 简介 单例模式(Singleton Pattern) 是 Java…

React 中五种常见的样式

React 中五种常见的样式策略 React中的样式策略主要有以下几种&#xff1a; 内联样式&#xff1a; 内联样式就是在JSX元素中&#xff0c;直接定义行内的样式&#xff1b;CSS样式表&#xff1a; 这也是我们最常用的样式策略&#xff0c;使用单独的样式表&#xff0c;使用CSS或…

lvgl 笔记 按钮部件 (lv_btn) 和 开关部件 (lv_switch)

按钮基础使用方法&#xff1a; lv_btn 和 lb_obj 使用方法一样&#xff0c;只是外表并不相同&#xff0c;基础创建方法只需一行代码。 lv_obj_t* btn lv_btn_create(lv_scr_act()); 添加大小和位置&#xff1a; lv_obj_t* btn lv_btn_create(lv_scr_act()); lv_obj_set_s…

一个小故障:vTaskGenericNotifyGiveFromISR卡死的解决

平台&#xff1a;gd32f103 freertos V10.4.3 LTS Patch 2 调试的时候发现一个问题&#xff1a; 在中断中使用 vTaskNotifyGiveFromISR(TaskHandle_ToCpu_IIC,NULL); //唤醒任务 但是程序却出现卡死现象&#xff1a; 在vTaskGenericNotifyGiveFromISR函数中。 用调试器看到…

C++ STL:string类的概述及常用接口说明

目录 一. 什么是STL 二. string类的概述 三. string类的常用接口说明 3.1 字符串对象创建相关接口&#xff08;构造函数&#xff09; 3.2 字符串长度和容量相关接口 3.3 字符访问相关接口函数 3.4 字符串删改相关接口函数 3.5 字符查找和子串相关接口函数 3.6 迭代器相…