水果库存系统(SSM+Thymeleaf版)

不为失败找理由,只为成功找方法。所有的不甘,因为还心存梦想,所以在你放弃之前,好好拼一把,只怕心老,不怕路长。

文章目录

  • 一、前言
  • 二、系统架构与需求分析
    • 1、技术栈
      • 1.1 后端
      • 1.2 前端
    • 2、需求分析
  • 三、设计数据库
  • 四、后端篇
    • 1、环境搭建
    • 2、SSM整合(配置文件编写)
      • 2.1 **database.properties文件编写:**
      • 2.2 mybatis-config.xml编写:
      • 2.3 spring-dao.xml编写:
      • 2.4 spring-service.xml编写:
      • 2.5 spring-mvc.xml编写:
      • 2.6 applicationContext.xml编写:
    • 3、web.xml文件配置(重点)
    • 4、持久层(Mapper) 编写
    • 5、业务层(Service) 编写
    • 6、控制层(Controller) 编写
  • 五、前端篇
    • 1、页面结构HTML
      • 1.1 index页面:
      • 1.2 food页面:
    • 2、页面样式CSS
    • 3、JavaScript脚本编写
      • 3.1 新增和修改功能
      • 3.2 删除功能
  • 六、总结


一、前言

本系统是一个简易版的水果库存系统,在前面的文章里面也有写过 水果库存基础版 和水果库存进阶版;这两个版本的区别是基础版是使用javaSE的知识,进阶版是使用了Spring框架技术,特别是合理的理由了IOC(控制反转)进行重构;如果有兴趣的小伙伴可以点击对应链接查看详情。 而编写本篇文章目的是为了把页面以网页的形式展现出来。而该篇文章适合人群是刚学会SSM框架但是不太懂怎么整合的小伙伴,也适合不知道前端和后端怎么联调开发流程的小伙伴。所以本系统会从0到1完整的开发流程进行讲解。

二、系统架构与需求分析

1、技术栈

1.1 后端

① JDK8
② IDEA(2022)
③ Maven 3.8.3
④ Tomcat 8.0
⑤ Spring、SpringMVC 5.3.20
⑥ Mybatis 3.5.7
⑦ Mybatis-Spring 2.0.6
⑧ MySQL 8.0.22
⑨ Thymeleaf 3.0.15

1.2 前端

① HTML
② CSS
③ JQuery
④ AJAX

以上就是本系统使用到技术栈,如果你已经学习了以上知识,但是又不知道怎么运用,那么本篇文章会一一进行讲解。

2、需求分析

我们先来看看项目的总体结构:
项目结构图
由上图所示可以知道,我们的项目是以maven的形式进行搭建的。系统架构使用了常见的MVC三层架构进行实现。**本项目不是使用前后端分离的,而是使用了模板引擎Thymeleaf进行页面渲染。**然后就是功能分析;本系统实现了常见的CRUD功能。就是查询水果库存(包含分页)新增水果库存,修改水果库存和删除水果库存。 不过在开发功能之前我们要先做一些准备工作,首先得有存数据的仓库— 数据库,所以我们先来设计一下数据库。

三、设计数据库

本系统的数据库设计比较简单,就一个表就可以了,我这里把该表名为t_fruit,该表设计如下:

字段名数据类型非空自增注释
fidintyesyes主键编号
fnamevarchar(20)yesno-名称
priceintnono-单价
fcountintnono-数量
remarkvarchar(50)nono-广告词
producervarchar(10)nono-产地

SQL语句如下所示

# 创建一个名为fruitdb的数据库
CREATE DATABASE fruitdb;
# 进入数据库fruitdb
USE fruitdb;
# 创建数据表t_fruit
CREATE TABLE `t_fruit` (
  `fid` int NOT NULL AUTO_INCREMENT COMMENT '编号',
  `fname` varchar(20) NOT NULL COMMENT '名称',
  `price` int DEFAULT NULL COMMENT '单价',
  `fcount` int DEFAULT NULL COMMENT '数量',
  `remark` varchar(50) COMMENT '广告词',
  `producer` varchar(10) COMMENT '产地',
  PRIMARY KEY (`fid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

通过以上的SQL语句就可以创建一个数据库和数据表,现在有了数据表,那么接下来就要开始从后端到前端的开发了。我们先从后端开始>>>

四、后端篇

1、环境搭建

后端的任何功能的开始,都必须有一个环境,所以我们先来搭建一个SSM环境出来。本系统IDE使用IDEA进行开发,而且是基于maven的,具体搭建步骤如下:
idea项目搭建第一步

[idea项目搭建第二步]
以上步骤就已经创建了一个基础maven项目。接下来要配置一下maven:

maven配置
由于我们本系统的页面是网页的形式,所以我们还需要在maven上添加web模块,具体步骤如下:

web模块
到此本系统的Web项目就创建完成了,项目结构如下所示:项目结构
项目创建完毕之后,那么就该导入依赖了,和我们以前导入Jar包是一个意思。之前是我们自己导入每一个Jar,但是有了maven之后,那么我们只要导入对应的坐标依赖就行了,至于Jar包maven会帮我们下载下来。依赖导入是在pom.xml文件中进行编写,本系统使用到的依赖有如下:

<dependencies>
        <!-- spring -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.20</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.3.20</version>
        </dependency>
        <!-- aop与事务 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.3.20</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.3.20</version>
        </dependency>

        <!-- mysql驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.22</version>
        </dependency>

        <!-- mybatis -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.7</version>
        </dependency>

        <!-- mybatis与spring整合 版本:mybatis3.x以上,spring5.x以上使用2.x -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.6</version>
        </dependency>

        <!-- 模板引擎Thymeleaf -->
        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf-spring5</artifactId>
            <version>3.0.15.RELEASE</version>
        </dependency>
    </dependencies>

    <!-- 静态资源导出问题 -->
    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>

到此,本项目的构建已经初步完成了,但是到真正编写功能模块还得配置几个文件才可以,项目有些基础的小伙伴已经知道接下来要做的步骤了,那就是整合SSM。

2、SSM整合(配置文件编写)

接下来就是SSM整合,本质上就是进行一些xml配置文件的配置,而我们需要怎么配置呢?我们这里可以分成6个文件;分别为:database.properties(数据库参数文件)、mybatis-config.xml(Mybatis核心配置文件)、spring-dao.xml(Mybatis与Spring整合文件/持久层配置文件)、spring-service.xml(业务层配置文件)、spring-mvc.xml(SpringMVC控制层配置文件)和applicationContext.xml(整合配置文件)。好了,废话不多说,直接开干~

2.1 database.properties文件编写:

# 驱动
jdbc.driver=com.mysql.cj.jdbc.Driver
# 如果数据库MySQL8+以上需要在后面设置时区 &serverTimezone=UTC
jdbc.url=jdbc:mysql://localhost:3306/fruitdb?useSSL=true&useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
# 自己本地数据库账号 和密码
jdbc.username=root
jdbc.password=root

2.2 mybatis-config.xml编写:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!-- 配置输出日志:logging -->
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
    <!-- 配置数据源,但和spring整合之后,所以让spring去处理 -->
    
    <!-- 给实体类起别名 -->
    <typeAliases>
        <package name="pojo"/>
    </typeAliases>

   <!-- mapper映射,注解开发,扫描包交给spring -->
</configuration>

2.3 spring-dao.xml编写:

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 1、关联数据库配置文件 即:database.properties -->
    <context:property-placeholder location="classpath:database.properties"/>

    <!-- 2、配置数据源 这里的数据源使用Spring的,没有使用第三方池化技术-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <!-- 3、sqlSessionFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <!-- 绑定Mybatis的配置文件 -->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
    </bean>

    <!-- 4、配置dao接口扫描包,动态的实现了DAO接口可以注入到Spring容器中! -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!-- 注入sqlSessionFactory -->
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
        <!-- 要扫描的dao包 -->
        <property name="basePackage" value="mapper"/>
    </bean>
</beans>

2.4 spring-service.xml编写:

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 扫描service下的包 进行注解开发 -->
    <context:component-scan base-package="service"/>

    <!-- 事务使用注解式 -->
</beans>

2.5 spring-mvc.xml编写:

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 1、注解驱动 -->
    <mvc:annotation-driven/>
    <!-- 2、静态资源过滤 -->
    <mvc:resources mapping="/css/**" location="/WEB-INF/pages/css/"/>
    <mvc:resources mapping="/js/**" location="/WEB-INF/pages/js/"/>
    <mvc:resources mapping="/images/**" location="/WEB-INF/pages/images/"/>

    <!-- 3、扫描包:controller -->
    <context:component-scan base-package="controller"/>
    
    <!-- 4、视图解析器 本系统使用Thymeleaf -->
    <bean id="templateResolver" class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
        <!-- 前缀 -->
        <property name="prefix" value="/WEB-INF/pages/"></property>
        <!-- 后缀 -->
        <property name="suffix" value=".html"></property>
        <!-- 指定编码 -->
        <property name="characterEncoding" value="utf-8"></property>
        <!-- 是否开启缓存 -->
        <property name="cacheable" value="false"></property>
        <!-- 模板的类型 -->
        <property name="templateMode" value="HTML5"></property>
    </bean>

    <!-- 配置模板引擎 -->
    <bean id="templateEngine" class="org.thymeleaf.spring5.SpringTemplateEngine">
        <property name="templateResolver" ref="templateResolver"></property>
    </bean>
    <!-- 配置模板视图 -->
    <bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
        <property name="characterEncoding" value="utf-8"></property>
        <property name="templateEngine" ref="templateEngine"></property>
    </bean>
</beans>

2.6 applicationContext.xml编写:

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 导入dao、service、mvc配置 -->
    <import resource="classpath:spring-dao.xml"/>
    <import resource="classpath:spring-service.xml"/>
    <import resource="classpath:spring-mvc.xml"/>
</beans>

本文件配置的目的是为了把三个文件整合起来变成一个文件,到后面启动项目的时候,利用Tomcat初始化配置把该文件作为接口加载即可。

3、web.xml文件配置(重点)

☆☆☆本文件特别重,如果本文件没有配置,那么当访问页面的时候会出现404。原因是本配置是暴露给浏览器的。本文件是在WEB-INF目录下,具体配置如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!-- DispatchServlet 前端控制器配置 -->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <!-- 加载所有的配置文件 applicationContext.xml里加载了全部文件 -->
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:applicationContext.xml</param-value>
        </init-param>
        <!-- 当值为0或者大于0时,表示容器在应用启动时就加载这个servlet;反则需要时加载 -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <!-- 映射路径 -->
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!-- 乱码过滤 -->
    <filter>
        <filter-name>encodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
    </filter>
    <!-- 过滤路径:/* 全部过滤 -->
    <filter-mapping>
        <filter-name>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- Session -->
    <session-config>
        <session-timeout>15</session-timeout>
    </session-config>
</web-app>

到此,本系统的所有配置就已经完毕,SSM整合也到此完成了,接下来就可以开始后端功能的编写了。

4、持久层(Mapper) 编写

我们先来编写持久层,也就是操作数据库的功能,不过在编写之前,我们得先创建一个与数据表对应的实体类具体代码如下:

package pojo;
/**
 * 水果库存实体类
 */
public class Fruit {
    private Integer id; //编号
    private String name; //名称
    private Float price; //单格
    private Integer count; //数量
    private String remark; //广告词
    private String producer; //产地

	//构造
    public Fruit() {
    }

    public Fruit(Integer id, String name, Float price, Integer count, String remark, String producer) {
        this.id = id;
        this.name = name;
        this.price = price;
        this.count = count;
        this.remark = remark;
        this.producer = producer;
    }
	
	//getting、setting
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Float getPrice() {
        return price;
    }

    public void setPrice(Float price) {
        this.price = price;
    }

    public Integer getCount() {
        return count;
    }

    public void setCount(Integer count) {
        this.count = count;
    }

    public String getRemark() {
        return remark;
    }

    public void setRemark(String remark) {
        this.remark = remark;
    }

    public String getProducer() {
        return producer;
    }

    public void setProducer(String producer) {
        this.producer = producer;
    }

    @Override
    public String toString() {
        return "Fruit{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", price=" + price +
                ", count=" + count +
                ", remark='" + remark + '\'' +
                ", producer='" + producer + '\'' +
                '}';
    }
}

数据映射已经编写完成,接下来就是功能实现了,持久层代码如下:

package mapper;

import org.apache.ibatis.annotations.*;
import org.apache.ibatis.session.RowBounds;
import pojo.Fruit;

import java.util.List;
/**
 * 操作数据库的接口
 */
public interface FruitMapper {
    /* 查询所有数据 */
    @Select("select fid id, fname name,price,fcount count,remark,producer from t_fruit")
    List<Fruit> findAll(RowBounds rowBounds);

    /* 查询总数据量 */
    @Select("select count(*) from t_fruit")
    int fruitCount();

    /* 新增水果库存 数据库实现了id自增策略,不用写id;字段名要和数据库一致,不用写别名 */
    @Insert("insert into t_fruit(fname,price,fcount,remark,producer) values (#{name},#{price},#{count},#{remark},#{producer})")
    int addFruit(Fruit fruit);

    //删除库存
    @Delete("delete from t_fruit where fid=#{id}")
    int delFruit(Integer id);

    //修改库存
    @Update("update t_fruit set fname=#{name}, price=#{price},fcount=#{count},remark=#{remark},producer=#{producer} where fid=#{id}")
    int updateFruit(Fruit fruit);

    //根据水果名称查询水果信息
    @Select("select fid id, fname name,price,fcount count,remark,producer from t_fruit where fname=#{fruitName}")
    Fruit findByName(String fruitName);
}

5、业务层(Service) 编写

到了业务层,由于本系统的业务还是比较简单的,直接调用持久层就可以了:

package service;

import pojo.Fruit;

import java.util.List;
/**
 * 业务层接口
 */
public interface FruitService {
    /* 逻辑分页 */
    List<Fruit> fruitPages(Integer pageNum);
    /* 查询总条数 */
    int fruitCount();

    //新增库存
    int saveFruit(Fruit fruit);

    //删除库存
    int delFruit(Integer id);

    //修改库存
    int updateFruit(Fruit fruit);

    //根据水果名称查询水果信息
    Fruit findByName(String fruitName);
}

业务实现类如下:

package service.impl;

import mapper.FruitMapper;
import org.apache.ibatis.session.RowBounds;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import pojo.Fruit;
import service.FruitService;

import javax.annotation.Resource;
import java.util.List;

/**
 * 水果库存业务具体实现
 */
@Service
public class FruitServiceImpl implements FruitService {
	//依赖注入持久层
    @Resource
    private FruitMapper fruitMapper;
	
	/**
     * 利用RowBounds分页查询(逻辑分页)
     * @param pageNum
     * @return
     */
    @Override
    public List<Fruit> fruitPages(Integer pageNum) {
        //分页 公式:(当前页-1)*当前页条数
        RowBounds rowBounds = new RowBounds((pageNum-1)*5,5);
        return fruitMapper.findAll(rowBounds);
    }

	/**
     * 获取水果库存总数量
     * @return
     */
    @Override
    public int fruitCount() {
        return fruitMapper.fruitCount();
    }

    /**
     * 新增水果库存业务
     * @param fruit
     * @return
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public int saveFruit(Fruit fruit) {
        int flg = fruitMapper.addFruit(fruit);
        return flg;
    }

	/**
     * 删除操作
     * @param id
     * @return
     */
    @Override
    public int delFruit(Integer id) {
        return fruitMapper.delFruit(id);
    }

    /**
     * 修改库存
     * @param fruit
     */
    @Override
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public int updateFruit(Fruit fruit) {
        return fruitMapper.updateFruit(fruit) ;
    }

    /**
     * 根据水果名称查询水果库存
     * @param fruitName 水果名称
     * @return
     */
    @Override
    public Fruit findByName(String fruitName) {
        Fruit fruit = fruitMapper.findByName(fruitName);
       return fruit;
    }
}

6、控制层(Controller) 编写

最后就到了控制层的编写,这里是直接与前端交互的接口,具体代码如下:

package controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import pojo.Fruit;
import service.FruitService;

import javax.annotation.Resource;
import java.util.List;

@Controller
@RequestMapping("/fruit")
public class FruitController {
	//依赖注入业务层
    @Resource
    private FruitService fruitService;

	/**
     * 访问首页
     * @return
     */
    @RequestMapping("/index")
    public String index(){
        return "/index";
    }

    /**
     * 查询水果库存列表
     * @param pageNum
     * @param model 存储到域中
     * @return
     */
    @GetMapping("/all/{pageNum}")
    public String findAll(@PathVariable(required = false) Integer pageNum, Model model){
        List<Fruit> fruitList = fruitService.fruitPages(pageNum);
        model.addAttribute("fruitList",fruitList);
        //查询总条数
        int count = fruitService.fruitCount();
        model.addAttribute("count",count);
        //页数
        model.addAttribute("pageNum",pageNum);
        System.out.println(model.getAttribute("fruitList"));
        return "food"; //指定页面
    }

    /**
     * 新增水果库存
     * produces = "application/json;charset=UTF-8":防止响应数据乱码
     * @return msg 响应数据
     */
    @PostMapping(value = "/addFruit", produces = "application/json;charset=UTF-8")
    @ResponseBody
    public String addFruit(Fruit fruit){
        //响应信息
        String msg = "添加水果库存成功!";
        //在添加水果库存之前查询一下水果名称是否唯一
        Fruit fruit1 = fruitService.findByName(fruit.getName());
        if (fruit1 != null){//存在
            //响应提示语给前端
            msg = "该水果名称已注册,不可复用!";
            return msg;
        }
        //调用服务层进行新增水果库存
        int flg = fruitService.saveFruit(fruit);
        if (flg < 1){
            msg = "添加水果库存失败,请联系管理员!";
        }
        return msg;
    }

    /**
     * 修改水果库存
     * @param fruit
     * @return msg 响应数据
     */
    @PostMapping(value = "/updateFruit", produces = "application/json;charset=UTF-8")
    @ResponseBody
    public String updateFruit(Fruit fruit){
        //响应信息
        String msg = "水果库存修改失败!";
        //传进来的库存不为空并且id不为空
        if (fruit != null && fruit.getId() != null){
            //在添加水果库存之前查询一下水果名称是否唯一
            Fruit dbFruit = fruitService.findByName(fruit.getName());
            System.out.println("传进来:"+fruit+"\n数据库中:"+dbFruit);
            //校验水果名称是否已注册但排除本身
            if (dbFruit != null && dbFruit.getId() != fruit.getId()){//存在
                //响应提示语给前端
                msg = "该水果名称已注册,不可复用!";
                return msg;
            }
            //校验完毕,进行修改操作
            int flg = fruitService.updateFruit(fruit);
            if (flg >= 1)  msg = "水果库存修改成功!";
        }
        System.out.println("水果:"+fruit);
        return msg;
    }

    /**
     * 删除水果库存
     * @param id
     * @return
     */
    @GetMapping(value = "/delFruit", produces = "application/json;charset=UTF-8")
    @ResponseBody
    public String deleteFruit(Integer id){
        //响应信息
        String msg = "删除水果库存成功!";
        if (null == id){
            msg = "请选择待删除的库存";
            return msg;
        }

        //调用服务层进行删除水果库存
        int flg = fruitService.delFruit(id);
        if (flg < 1){
            msg = "删除水果库存失败,请联系管理员!";
        }
        return msg;
    }
}

到此,后端的代码就已经编写完成了,我们来看一下整体结构图:
后端结构图

五、前端篇

1、页面结构HTML

后端代码已经编写完毕了,接下来就是前端页面了,我们先把页面结构编写出来,而前端的所有代码在本项目中是存放在web\WEB-INF\pages;本系统页面一共有2个,分别是index首页和food主体页面,具体HTML页面代码如下所示~

1.1 index页面:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>水果库存系统</title>
    <style>
        h1{
            padding: 5px 10px 8px 10px;
            background-color: blanchedalmond;
            font-family: kaiti;
            display: inline-block;
            border-radius: 5px;
            margin: 0;
        }
        h2{
            text-align: center;
            padding-top: 20px;
        }
        h2 a{
            text-decoration: none;
        }
    </style>
</head>
<body>
    <h1>水果库存系统</h1>
    <h2>
        <a th:href="@{/fruit/all/1}">进入水果库存详情页>>></a>
    </h2>
</body>
</html>

1.2 food页面:

<!DOCTYPE html>
<!-- xmlns:th="http://www.thymeleaf.org" Thymeleaf的声明,有语法提示,th开头的都是Thymeleaf语法 -->
<html lang="en" xmlns:th="http://www.thymeleaf.org">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>水果库存</title>
    <!-- 路径前面要加/ 当前目录 -->
    <link rel="stylesheet" type="text/css" href="./css/food.css" th:href="@{/css/food.css}" />
</head>

<body>
    <div id="div_conteiner">
        <div id="div_fruit_list">
            <table id="tbl_fruit">
                <thead>
                    <tr>
                        <th class="w16">名称</th>
                        <th class="w16">单价</th>
                        <th class="w16">数量</th>
                        <th class="w16">广告词</th>
                        <th class="w16">产地</th>
                        <th class="w16">操作</th>
                    </tr>
                </thead>
                <tbody>
                    <!-- 从服务器中获取数据 -->
                    <tr th:each="fruit,item:${fruitList}" th:id="${item.count}">
                        <!-- 隐藏域 存放水果编号 -->
                        <input type="hidden" name="id" th:id="|id${item.count}|" th:value="${fruit.id}">
                        <td th:text="${fruit.name}" th:class="|name${item.count}|">西瓜</td>
                        <td th:text="${fruit.price}" th:class="|price${item.count}|">3</td>
                        <td th:text="${fruit.count}" th:class="|count${item.count}|">60</td>
                        <td th:text="${fruit.remark}" th:class="|remark${item.count}|">描述</td>
                        <td th:text="${fruit.producer}" th:class="|producer${item.count}|">产地</td>
                        <td>
                            <img src="./images/del.jpg" th:src="@{/images/del.jpg}" th:id="${item.count}" alt="删除" class="delImg"/>
                        </td>
                    </tr>

                    <tr th:if="${#lists.isEmpty(fruitList)}">
                        <td colspan="6">对不起,暂无数据!</td>
                    </tr>
                </tbody>

                <tfoot>
                    <tr>
                        <td colspan="6">
                            <span>当前第&nbsp;<span style="color: coral; size: 1.5rem; font-weight: bold" th:text="${pageNum}">1</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>
                            <a th:if="${pageNum>1}" th:href="@{/fruit/all/{pageNum}(pageNum=${pageNum}-1)}">上一页</a>
                            <a th:if="${pageNum==1}" th:disabled="true">上一页</a>
                            <span>&nbsp;&nbsp;|&nbsp;&nbsp;</span>
                            <a th:href="@{/fruit/all/{pageNum}(pageNum=${pageNum}+1)}">下一页</a>
                            <span>&nbsp;&nbsp;总条数:<strong style="color: coral; size: 1.5rem;" th:text="${count}"></strong></span>
                        </td>
                    </tr>
                </tfoot>
            </table>
            <hr color="black"/>
            <div id="add_fruit_div">
                <table id="add_fruit_tbl">
                    <tr>
                        <td>名称:</td>
                        <td>
                            <input type="text" id="name" />
                        </td>
                    </tr>
                    <tr>
                        <td>单价:</td>
                        <td>
                            <input type="text" id="price"/>
                        </td>
                    </tr>
                    <tr>
                        <td>数量:</td>
                        <td>
                            <input type="text" id="count"/>
                        </td>
                    </tr>
                    <tr>
                        <td>广告词:</td>
                        <td>
                            <input type="text" id="remark"/>
                        </td>
                    </tr>
                    <tr>
                        <td>产地:</td>
                        <td>
                            <input type="text" id="producer"/>
                        </td>
                    </tr>
                    <th colspan="2">
                        <input type="button" value="添加" id="addOrUpdateBtn" class="addBtn"/>
                    </th>
                </table>
            </div>
        </div>
    </div>
</body>
</html>

【说明】以上都是使用了Thymeleaf模板引擎,我们再把CSS样式编写完毕,就可以访问一下页面试试效果了。

2、页面样式CSS

html{
    width: 100%;
    height: 100%;
}
body{
    margin: 0;
    padding: 0;
    background:url(../images/bg.jpg) center no-repeat;
    background-size: cover;
    
}
/*所有div设置*/
div{
    position: relative;
    float: left;
}

/*页面数据主体*/
#div_conteiner{
    width: 80%;
    height: 100%;
    margin-left: 13%;
    float: left;
    margin-top: 3%;
   
}
/*数据列表大盒子*/
#div_fruit_list{
    width: 89%;
    text-align: center;
    background-color: rgb(237, 249, 255);
    border-radius: 20px;
    padding-top: 2%;
    padding-bottom: 2%;
}
/*搜索栏*/
.search{
    margin-bottom: 5px;
    margin-left: 9%;
}
.search input{
    font-size: 1.2rem;
    padding: 3px;
    border-radius: 5px 0 0 5px;
    outline: none;
    border: lightgray solid 2px;

}
.search button{
    font-size: 1.2rem;
    border-radius: 0 5px 5px 0;
    border: lightgray solid 2px;
    padding-bottom: 3px;
    background-color: #1371c3;
    color: white;
    cursor: pointer;
    position: relative;
    right: 7px;
}

/*显示数据列表*/
#tbl_fruit{
    width: 80%;
    text-align: center;
    line-height:45px;
    margin-left: 9%;
    border-radius: 20px;
}
#tbl_fruit,#tbl_fruit tr,#tbl_fruit th,#tbl_fruit td{
    border: 1px solid gray;
    border-collapse: collapse;
}
.w16{
    width: 16%;
}
.delImg{
    width: 30px;
    height: 30px;
    vertical-align: middle; /* 图片垂直居中 */
    cursor: pointer;
}
thead{
  font-size: 1.3rem;  
}

#add_fruit_div{
    width: 40%;
    margin-left: 30%;
}
#add_fruit_tbl{
    width: 80%;
    margin-top: 5px;
    margin-left: 5%;
    border-collapse: collapse;
}
#add_fruit_tbl,#add_fruit_tbl tr,#add_fruit_tbl th,#add_fruit_tbl td{
    border: 1px solid lightgray;
    text-align: center;
}

/*添加和修改*/
#add_fruit_tbl input{
    outline: none;
}
#add_fruit_tbl td input[type='text']{
    width: 90%;
    padding: 4px;
}
/*添加修改按钮样式*/
.addBtn{
    font-size: 1.2rem;
    padding: 3px 16px;
}

/*a链接禁用*/
a[disabled] {
    pointer-events: none;
    cursor: default;
    text-decoration: none;
    color: #999;
}

到此我们可以查看一下页面效果:
index页面
主页面
以上就是编码到目前为止的页面效果了;数据的展示是我已经在数据库中添加了一些数据,然后由于我们使用Thymeleaf遍历后端传来的数据,所以有数据显示,也由此查询分页功能也就完成了。

3、JavaScript脚本编写

接下来就到最后一步了,那就是把剩下的新增、修改和删除功能利用Ajax技术与后端联调就可以了。不过在联调之前,我们在本系统的数据展示模块利用js写点附属功能上去,需求是这样的:**当鼠标悬浮到对应的的行的时候,会更换颜色,当鼠标移除的时候会变回原本的颜色。**具体实现如下所示:

//当鼠标悬浮时,显示背景颜色
function showBGColor(e) {
    //获取事件源  event:当前发生的事件 tagName:获取当前的元素
    // alert(event.srcElement.tagName)
    // alert(window.event.srcElement.tagName);
    //判断是否存在该元素
    if (window.event && window.event && window.event.srcElement.tagName == "TD") {
        var td = window.event.srcElement;
        var tr = td.parentElement;
        //设置样式
        tr.style.backgroundColor = "lightblue";
        //获取tr中所有单元格
        var tds = tr.cells;
        for (var i = 0; i < tds.length; i++) {
            tds[i].style.color = "white";
        }
    }
}

//鼠标移除时执行
function cleanBGColor() {
    if (window.event && window.event && window.event.srcElement.tagName == "TD") {
        var td = window.event.srcElement;
        var tr = td.parentElement;
        //设置样式
        tr.style.backgroundColor = " rgb(237, 249, 255)";
        //获取tr中所有单元格
        var tds = tr.cells;
        for (var i = 0; i < tds.length; i++) {
            tds[i].style.color = "black";
        }
    }
}

然后我们在food.html页面引入

	<!-- food.js 即上面代码存储的文件 -->
	<script type="text/javascript" src="./js/food.js" th:src="@{/js/food.js}" ></script>
	<!-- 后续功能实现要使用到Ajax,所以引入jQuery库 -->
    <script type="text/javascript" src="js/jquery-3.3.1.min.js" th:src="@{/js/jquery-3.3.1.min.js}"></script>

然后我们在到html对应的标签中添加鼠标悬浮和移除事件

	<!-- onmouseover:鼠标悬浮,显示背景颜色;onmouseout:鼠标移除,清除背景颜色 -->
 	<tr th:each="fruit,item:${fruitList}" th:id="${item.count}" onmouseover="showBGColor()" onmouseout ="cleanBGColor()">
 	</tr>

我们来看一下功能实现:

悬浮
鼠标悬浮的时候,出现背景颜色~~

移除
鼠标移除,变回原本的颜色,功能实现OK。

3.1 新增和修改功能

废话不多说,直接上代码:

//当页面加载完成后执行
window.onload = function () {
    addFruit();
}

//点击添加按钮时进行数据新增操作
function addFruit(){
    $("#addOrUpdateBtn").click(function(){
        if ($("#addOrUpdateBtn").val() == '添加'){
            //校验
            if(!check())
                return false;
            $.ajax({
                url: "/fruit/addFruit",
                type: "post",
                data: { //前端携带的数据
                    name: $("#name").val(),
                    price: $("#price").val(),
                    count: $("#count").val(),
                    remark: $("#remark").val(),
                    producer: $("#producer").val()
                },
                dataType:'text', //后端是String返回,所以为文本形式表示json格式
                success:function (data){
                    //响应数据
                    alert(data);
                    //页面刷新
                    location.reload();
                }
            });
        }

    });
}

//在发起请求之前进行数据校验
function check(){
    if ($("#name").val() == ''){
        alert("水果名称不能为空!");
        return false;
    }
    if ($("#price").val() == ''){
        alert("水果单价不能为空!");
        return false;
    }
    if ($("#count").val() == ''){
        alert("水果数量不能为空!");
        return false;
    }
    return true; //校验通过
}

//点击对应水果库存进行具体操作 参数为:id值
function showData(id){
    switch (id){
        case '1':
            //获取水果id值
            let fid1 = $("#id"+id).val();
            //调用修改水果库存函数
            updateFruit(id,fid1);
            break;
        case '2':
            //获取水果id值
            let fid2 = $("#id"+id).val();
            updateFruit(id,fid2);
            break;
        case '3':
            //获取水果id值
            let fid3 = $("#id"+id).val();
            updateFruit(id,fid3);
            break;
        case '4':
            //获取水果id值
            let fid4 = $("#id"+id).val();
            updateFruit(id,fid4);
            break;
        case '5':
            //获取水果id值
            let fid5 = $("#id"+id).val();
            updateFruit(id,fid5);
            break;
    }
}

//修改水果库存 id:行id fid:水果id
function updateFruit(id,fid){
    //获取水果库存各属性值
    $("#name").val($('.name'+id).text());
    $("#price").val($('.price'+id).text());
    $("#count").val($('.count'+id).text());
    $("#remark").val($('.remark'+id).text());
    $("#producer").val($('.producer'+id).text());
    //把‘添加’按钮值改为‘修改’
    $("#addOrUpdateBtn").val("修改");

    //点击修改按钮进行相应操作
    $("#addOrUpdateBtn").click(function (){
        if ($("#addOrUpdateBtn").val() == "修改"){
            //参数校验
            if(!check()){
                return false;
            }
            //发起ajax请求
            $.ajax({
                url: "/fruit/updateFruit/",
                type: "post",
                data: { //前端携带的数据
                    id: fid,
                    name: $("#name").val(),
                    price: $("#price").val(),
                    count: $("#count").val(),
                    remark: $("#remark").val(),
                    producer: $("#producer").val()
                },
                dataType:'text', //后端是String返回,所以为文本形式表示json格式
                success:function (data){
                    //响应数据
                    alert(data);
                    //页面刷新
                    location.reload();
                }
            });
        }
        return false;
        })
}

最后绑定点击事件就可以了

<!-- 绑定点击事件函数 showData(this.id):把id值传参,这里就可以实现数据显示了 -->
<tr th:each="fruit,item:${fruitList}" th:id="${item.count}" onclick="showData(this.id)"></tr>

我们来看一下功能效果:
add

addOK

addYse

updateShow
update
updateOK
由此可见,新增和修改功能都已经实现了。

3.2 删除功能

代码如下:

//点击删除小图标执行删除数据的函数 num:html绑定事件中的参数获取
function delFruit(num) {
    //用户提示框
    let result = confirm("确定要删除该记录吗?");
    if (result){
        //确定,执行删除操作
        $.ajax({
            url: "/fruit/delFruit",
            type: "get",
            data: { //前端携带的数据
                id: $("#id"+num).val(),
            },
            dataType:'text', //后端是String返回,所以为文本形式表示json格式
            success:function (data){
                //响应数据
                alert(data);
                //页面刷新
                location.reload();
            }
        });
    }else {
        // 用户点击取消,则返回false,不执行任何操作
        return false;
    }
}

绑定点击事件,进行删除操作

	<td>
		<!-- οnclick="delFruit(this.id)":传入的参数是id值 -->
        <img src="./images/del.jpg" th:src="@{/images/del.jpg}" th:id="${item.count}" onclick="delFruit(this.id)" alt="删除" class="delImg"/>
    </td>

然后我们看效果即可:
delete
点击确定删除会出现提示语:
deleteOK
删除成功!!!
deleteYes
查询数据列表可以知道test02数据已经被删除成功!!!

六、总结

    至此,本系统的功能实现完毕了。本系统是使用了后端SSM框架,模板引擎Thymeleaf;前端使用了HTML+CSS+JQuery技术。另外本人技术有限,可能在本篇内容中有不足之地,各方道友可私聊畅谈一二~~
    最后关于本系统使用到的静态资源可以可以使用自己的,也可以从源码中获取。本系统的源码已上传到码云。链接为:https://gitee.com/originnan/fruit

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

创建性-构造者设计模式

前言 我们在使用Retrofit等这些第三方框架的时候&#xff0c;发现他们的使用都很方便&#xff0c;比如Retrofit retrofit new Retrofit.Builder().build()&#xff0c;和我们通常直接new一个对象不同&#xff0c;他是交给Builder类&#xff0c;通过build()函数来构造一个Retro…

【python使用 Pillow 库】缩小|放大图片

当我们处理图像时&#xff0c;有时候需要调整图像的大小以适应特定的需求。本文将介绍如何使用 Python 的 PIL 库&#xff08;Pillow&#xff09;来调整图像的大小&#xff0c;并保存调整后的图像。 环境准备 在开始之前&#xff0c;我们需要安装 Pillow 库。可以使用以下命令…

并发测试工具 apache-jmeter使用发送post请求JSON数据

目录 1 下载安装 2 汉化 3 创建高并发测试 配置线程组 创建web请求 创建监听器 结果树 汇总报告 为web请求添加token 添加Content-Type用于发送json 4 启动测试 5 查看结果 1 下载安装 官网Apache JMeter - Download Apache JMeter 解压运行 2 2 汉化 打开软件…

神经网络--感知机

感知机 单层感知机原理 单层感知机:解决二分类问题&#xff0c;激活函数一般使用sign函数,基于误分类点到超平面的距离总和来构造损失函数,由损失函数推导出模型中损失函数对参数 w w w和 b b b的梯度&#xff0c;利用梯度下降法从而进行参数更新。让1代表A类&#xff0c;0代…

ZMTP协议

ZoreMQ Transport Protocol是一个传输层协议&#xff0c;用于ZMQ的连接的信息交互&#xff0c;本文档描述的是3.0协议&#xff0c;主要分析基于NULL Security Mechanism 协议语法 ZMTP由三部分组成&#xff0c;分别是 greeting、handshake、traffic 部分描述构成greeting描述…

数据挖掘导论学习笔记1(第1 、2章)

参考&#xff1a;https://blog.csdn.net/u013232035/article/details/48281659?spm1001.2014.3001.5506 和《数据挖掘导论》学习笔记&#xff08;第1-2章&#xff09;_时机性样本_schdut的博客-CSDN博客 第1章 绪论 数据挖掘是一种技术&#xff0c;它将传统的数据分析方法…

【配置环境】Visual Studio 配置 OpenCV

目录 一&#xff0c;环境 二&#xff0c;下载和配置 OpenCV 三&#xff0c;创建一个 Visual Studio 项目 四&#xff0c;配置 Visual Studio 项目 五&#xff0c;编写并编译 OpenCV 程序 六&#xff0c;解决CMake编译OpenCV报的错误 一&#xff0c;环境 Windows 11 家庭中…

十大管理——项目成本管理

目录 1.成本管理概念 2.成本管理的四个过程域 2.1四个过程的整体理解 ​2.2四个过程的ITO口诀版记忆 2.3过程1——制定项目管理计划 ​2.4过程2——项目成本估算​ 2.5过程3——项目成本预算 2.5过程4——项目成本控制 3计算题 1.成本管理概念 项目成本管理就是要确保…

在R中安装TensorFlow、TensorFlow_Probability、numpy(R与Python系列第二篇)

目录 前言&#xff1a; 1-安装tensorflow库 Step1: 下载R包tensorflow Step2&#xff1a;安装TensorFlow库 Step3&#xff1a;导入R中 2-安装tensorflow_probability库 Step1&#xff1a;下载R包&#xff1a;tfprobability Step2&#xff1a;安装TensorFlow Probability …

【分类】分类性能评价

评价指标 1、准确率、召回率、精确率、F-度量、ROC ​ 属于各类的样本的并不是均一分布&#xff0c;甚至其出现概率相差很多个数量级&#xff0c;这种分类问题称为不平衡类问题。在不平衡类问题中&#xff0c;准确率并没有多大意义&#xff0c;我们需要一些别的指标。 ​ 通…

Flutter:getX的学习

前言 学习教程&#xff1a;Getx教程_FlutterGetx系列实战教程 简介 getX是第三方的状态管理插件&#xff0c;不仅具有状态管理的功能&#xff0c;还具有路由管理、主题管理、国际化多语言管理、网络请求、数据验证等功能。相比其他状态管理组件&#xff0c;getX简单、功能强大…

【网络编程上】

目录 一.什么是互联网 1.计算机网络的定义与分类&#xff08;了解&#xff09; &#xff08;1&#xff09;计算机网络的定义 &#xff08;2&#xff09;计算机网络的分类 ① 按照网络的作用范围进行分类 ②按照网络的使用者进行分类 2.网络的网络 &#xff08;理解&#xf…

基于Vue3+ts+Pinia的后台管理系统

Vue3tsPinia管理系统 项目介绍项目简介界面展示登录界面商品界面用户界面角色管理界面 接口文档项目地址 项目介绍 包含对商品、订单、用户增删改查等后台的管理业务&#xff0c;并提供数据可视化的报表功能的管理系统。界面进行了高级封装&#xff0c;可以通过引入组件传入配…

利用frps搭建本地自签名https服务的透传

nginx的搭建就不介绍了&#xff0c;教程很多&#xff0c;基本上油手就会。 在本例中&#xff0c;frp服务器的域名是 www.yourfrp.com&#xff0c;同时也是反向代理nginx服务器; 本地网站要用的域名&#xff1a; test.abcd.com 请事先将 test.abcd.com 解析到 frp所在服务器…

OpenCV(十三):图像中绘制直线、圆形、椭圆形、矩形、多边形和文字

目录 1.绘制直线line() 2.绘制圆形circle() 3.绘制椭圆形ellipse() 4.绘制矩形rectangle() 5.绘制多边形 fillPoly() 6.绘制文字putText() 7.例子 1.绘制直线line() CV_EXPORTS_W void line(InputOutputArray img,Point pt1, Point pt2,const Scalar& color,int t…

CSS学习笔记05

CSS笔记05 定位 position CSS 属性position - 用于指定一个元素在文档中的定位方式。top&#xff0c;right&#xff0c;bottom 和 left 属性则决定了该元素的最终位置。position 有以下常用的属性值&#xff1a; position: static; - 默认值。指定元素使用正常的布局行为&am…

wap2app 隐藏系统状态栏

一、首先创建wap2App项目 1、文件》新建》项目 2、选择Wap2App项目&#xff1a;输入项目名称、网站首页地址&#xff08;如果是本地localhost的话改为你的IP地址即可&#xff09;&#xff0c;点击创建 二、创建完wap2App项目后 隐藏系统状态栏只要修改1、2选项即可 1、找到根…

uniapp实现移动端的视频图片轮播组件

1、视频轮播组件app体验地址 https://tt.appc02.com/nesxr6 2、支持小程序、H5、APP&#xff0c;在小程序上运行的效果图 3、使用方法 第一步&#xff0c;按照截图步骤配置好 第二步&#xff1a;参考以下代码&#xff0c;使用视频图片轮播组件 <template><view>…

aarch64-linux交叉编译libcurl带zlib和openssl

交叉编译libcurl需要依赖zlib和openssl 需要先用aarch64工具链编译zlib和openssl aarch64-linux环境搭建 下载工具链 gcc用于执行交叉编译 gcc-linaro-7.5.0-2019.12-x86_64_aarch64-linux-gnusysroot是交叉版本的库文件集合 sysroot-glibc-linaro-2.25-2019.12-aarch64-lin…

K8S最新版本集群部署(v1.28) + 容器引擎Docker部署(下)

温故知新 &#x1f4da;第三章 Kubernetes各组件部署&#x1f4d7;安装kubectl&#xff08;可直接跳转到安装kubeadm章节&#xff0c;直接全部安装了&#xff09;&#x1f4d5;下载kubectl安装包&#x1f4d5;执行kubectl安装&#x1f4d5;验证kubectl &#x1f4d7;安装kubead…
最新文章