当前位置: 首页 > news >正文

水果库存系统(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 configurationPUBLIC "-//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/beanshttp://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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://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/beanshttp://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/beanshttp://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、settingpublic 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;}@Overridepublic 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 {//依赖注入持久层@Resourceprivate FruitMapper fruitMapper;/*** 利用RowBounds分页查询(逻辑分页)* @param pageNum* @return*/@Overridepublic List<Fruit> fruitPages(Integer pageNum) {//分页 公式:(当前页-1)*当前页条数RowBounds rowBounds = new RowBounds((pageNum-1)*5,5);return fruitMapper.findAll(rowBounds);}/*** 获取水果库存总数量* @return*/@Overridepublic 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*/@Overridepublic 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*/@Overridepublic 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 {//依赖注入业务层@Resourceprivate 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")@ResponseBodypublic 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")@ResponseBodypublic 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")@ResponseBodypublic 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

相关文章:

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

不为失败找理由&#xff0c;只为成功找方法。所有的不甘&#xff0c;因为还心存梦想&#xff0c;所以在你放弃之前&#xff0c;好好拼一把&#xff0c;只怕心老&#xff0c;不怕路长。 文章目录 一、前言二、系统架构与需求分析1、技术栈1.1 后端1.2 前端 2、需求分析 三、设计…...

如何在VueJS应用程序中设置Toast通知

通知是开发者提升应用程序互动性和改善用户体验的强大工具。通过利用通知&#xff0c;开发者可以在用户与应用程序互动的同时&#xff0c;有效地向用户传达重要事件。 通知在应用程序中起着至关重要的作用&#xff0c;可以及时通知用户有关各种操作和事件的信息。它们可以用于通…...

css让元素保持等比例宽高

使用新属性 aspect-ratio: 16/9; 代码示例 <style>div {width: 60%;/* 等比例宽高 */aspect-ratio: 16/9;background-color: red;margin: auto;}</style> </head><body><div></div> </body>示例 aspect-ratio兼容性...

骨传导和入耳式哪个危害大一点?入耳式和骨传导哪种好?

骨传导和入耳式这两种耳机虽然都存在一定的危害&#xff0c;但是入耳式耳机对人体的危害要更大一点。 入耳式耳机直接塞进耳朵这种佩戴方式&#xff0c;会阻塞外部声音的进入&#xff0c;长时间使用可能会导致耳道感染&#xff0c;还可能对听力造成损伤&#xff0c;而骨传导耳…...

介绍OpenCV

OpenCV是一个开源计算机视觉库&#xff0c;可用于各种任务&#xff0c;如物体识别、人脸识别、运动跟踪、图像处理和视频处理等。它最初由英特尔公司开发&#xff0c;目前由跨学科开发人员社区维护和支持。OpenCV可以在多个平台上运行&#xff0c;包括Windows、Linux、Android和…...

Android中的view绘制流程,简单理解

简单理解 Android中的View类代表用户界面中基本的构建块。一个View在屏幕中占据一个矩形区域、并且负责绘制和事件处理。View是所有widgets的基础类&#xff0c;widgets是我们通常用于创建和用户交互的组件&#xff0c;比如按钮、文本输入框等等。子类ViewGroup是所有布局&…...

商城开发:店铺管理系统应具备哪些功能?

电子商务的迅猛发展&#xff0c;越来越多的企业选择在线商城作为业务拓展的重要渠道。而要实现一个成功的在线商城&#xff0c;一个强大而高效的店铺管理系统是不可或缺的。店铺管理系统作为商城的核心管理工具&#xff0c;应具备一系列功能&#xff0c;以提供卓越的用户体验和…...

小白学go基础04-命名惯例对标识符进行命名

计算机科学中只有两件难事&#xff1a;缓存失效和命名。 命名是编程语言的要求&#xff0c;但是好的命名却是为了提高程序的可读性和可维护性。好的命名是什么样子的呢&#xff1f;Go语言的贡献者和布道师Dave Cheney给出了一个说法&#xff1a;“一个好笑话&#xff0c;如果你…...

使用iCloud和Shortcuts实现跨设备同步与自动化数据采集

在如今的数字时代&#xff0c;跨设备同步和自动化数据采集对于提高工作效率和便利性至关重要。苹果的iCloud和Shortcuts App为我们提供了强大的工具&#xff0c;可以实现跨设备同步和自动化数据采集的功能。本文将详细介绍如何利用iCloud和Shortcuts App实现这些功能&#xff0…...

Spring框架-基于STOMP使用Websocket

文章目录 前言一、范例演示1.注解方式2.XML方式二、可能出现错误错误: WebSocket代理中断错误: 缺少EventExecutor类错误: 缺少Publisher类错误: 缺少Scheduler类错误: WebSocket调用失败总结前言 Spring框架提供了多种WebSock消息机制,不仅包含了模拟SockJS,还提供了基…...

kafka-- 安装kafka manager及简单使用

一 、安装kafka manager 管控台&#xff1a; # 安装kafka manager 管控台&#xff1a; ## 上传 cd /usr/local/software ## 解压 unzip kafka-manager-2.0.0.2.zip -d /usr/local/ cd /usr/local/kafka-manager-2.0.0.2/conf vim /usr/local/kafka-manager-2.0.0.2/conf/appl…...

深圳-海岸城购物中心数据分析

做数据分析的时候&#xff0c;如果要对商场进行分析&#xff0c;可以从这些数据纬度进行分析&#xff0c;如下图所示&#xff1a; 截图来源于数位观察&#xff1a;https://www.swguancha.com/...

vue3 + elementplus Cannot read properties of null (reading ‘isCE‘)

使用命令行直接下载的element-plus&#xff0c;使用时会报错。 卸载掉&#xff0c;然后在项目根目录下&#xff0c;使用vue ui安装依赖&#xff0c; 即可使用...

易云维®医院后勤管理系统软件利用物联网智能网关帮助实现医院设备实现智能化、信息化管理

近年来&#xff0c;我国医院逐渐意识到医院设备信息化管理的重要性&#xff0c;逐步建立医院后勤管理系统软件&#xff0c;以提高信息化管理水平。该系统是利用数据库技术&#xff0c;为医院的中央空调、洁净空调、电梯、锅炉、医疗设备等建立电子档案&#xff0c;把设备监控、…...

c# 定期重启程序操作

1 先说说重启//这部分是转载的 一、Restart方法 System.Windows.Forms.Application.Restart();经测试发现有时候只会关闭程序&#xff0c;并不会重新启动 二、Process.Start()和Exit() System.Diagnostics.Process.Start(System.Reflection.Assembly.GetExecutingAssembly()…...

ps beta 2.5的妙用

1、https://pan.baidu.com/s/1CCw6RGlzEJ7TPWou8pPADQ?pwd2023 2、下载新便携版。 3、解压到c:\myapp文件夹下。 4、运行。 5、登录us账号。 6、使用智能移除。 效果如下&#xff1a; 使用滤镜。 先将C:\myApp\&#xff08;新便携版&#xff09;Adobe Photoshop (25.0.0 m22…...

IDEA无效发行版本17

IDEA无效发行版本17 idea开发工具依赖的 jdk版本 和 项目依赖的jdk版本一定要保持 一致&#xff0c;不然会报错。 setting-->build-->compiler-》javaCompiler project->structure 这个也要保持一样。 在porm.xml文件中&#xff0c;你配置jdk版本是1.8&#xff0c;这…...

Ubuntu22.04安装ROS

Ubuntu22.04安装ROS_笔记大全_设计学院 Excerpt 在安装ROS之前&#xff0c;需要先安装Ubuntu22.04操作系统。您可以从Ubuntu官网下载Ubuntu22.04的最新版本镜像文件&#xff0c;并创建一个可启动的USB。您可以参考以下步骤&#xff1a; 一、安装Ubuntu22.04操作系统 在安装ROS…...

Linux 学习笔记(2)—— 关于文件和目录

目录 1、切换目录 2、查看系统信息 3、文本的创建和编辑 3-1&#xff09;创建文件 3-2&#xff09;查看文件 3-3&#xff09;输出重定向和追加重定向 3-4&#xff09;使用 vi 编辑器编辑文件 4、文件和文件夹的处理 4-1&#xff09;对文件的处理 4-2&#xff09;查看…...

[重要] 如何在桌面上生成一个指定网址的快捷方式

方法一&#xff1a; 要在桌面上生成一个指定网址的快捷方式&#xff0c;可以按照以下步骤进行操作&#xff1a; 首先&#xff0c;打开你的浏览器&#xff0c;并确保已经访问了你想要创建快捷方式的网址。 在浏览器中&#xff0c;将鼠标悬停在地址栏上的网址上&#xff0c;然后…...

PyQt和Qt的其他绑定(如PySide)相比有什么优势和劣势?

作为一个新手&#xff0c;你可能会对PyQt和Qt的其他绑定&#xff08;如PySide&#xff09;之间的优势和劣势感到困惑。没问题&#xff0c;这很正常。我们先来谈谈优势吧。 首先&#xff0c;PyQt是由C编写的&#xff0c;因此它具有强大的跨平台支持。这意味着无论你使用的是Win…...

4K三路虚拟情景实训教学系统VR4300:实现“微课录制+课堂实训”双教学需求

如今&#xff0c;ChatGPT横空出世&#xff0c;产生了极大的破圈效应。各种AI、大模型概念风起云涌&#xff0c;给千行百业带来了极大的机遇与挑战。 4K三路虚拟情景实训教学系统VR4300基于计算机技术&#xff0c;虚拟现实技术&#xff0c;抠像合成技术&#xff0c;AI大模型等优…...

python逆向还原dnspy反编译的C#算法

dnspy反编译中的代码如下: private void method_1(byte[] byte_2, byte[] byte_3, byte[] byte_4) {...

数学建模--最短路径算法的Python实现

目录 1.算法流程简介 2.算法核心代码 3.算法效果展示 1.算法流程简介 #最短路径算法 #针对有向图的最短路径问题,我们有很多的算法能解决. """ 目前主流算法如下所示: Dijkstra算法:Dijkstra算法是一种单源最短路径算法,用于计算从起点到其它所有节点的最短…...

webpack学习(一)基本配置

webpack学习&#xff08;一&#xff09;基本配置 目录 webpack学习&#xff08;一&#xff09;基本配置 webpack简介 五大核心配置 1.入口&#xff08;entry) 2.输入&#xff08;output&#xff09; 3.加载器&#xff08;loader&#xff09; 4.插件&#xff08;plugins…...

Oracle 遍历变量游标

背景 由于我们的数据库系统中的游标特别多&#xff0c;DBA让我们优化&#xff0c;减少游标的使用。 电脑系统&#xff1a;windows数据库&#xff1a;Oracle数据库图形化界面工具&#xff1a;Toad&#xff0c;DBeaver(我測試的時候用的)记录日期&#xff1a;2023-09-04 具体实…...

C++11新特性① | C++11 常用关键字实战详解

目录 1、引言 2、C11 新增关键字详解 2.1、auto 2.2、override 2.3、final 2.4、nullptr 2.5、使用delete阻止拷贝类对象 2.6、decltype 2.7、noexcept 2.8、constexpr 2.9、static_assert VC常用功能开发汇总&#xff08;专栏文章列表&#xff0c;欢迎订阅&#xf…...

VUE3学习小记(2)- ref 与 reactive

ref() 在组合式 API 中&#xff0c;推荐使用ref()函数来声明响应式状态&#xff1a; import { ref } from vueconst count ref(0) ref() 接收参数&#xff0c;并将其包裹在一个带有 .value 属性的 ref 对象中返回&#xff1a; const count ref(0)console.log(count) // {…...

基于单片机的万年历温度无线传输控制系统系统

一、系统方案 本设计采用DS1302采集年月日时分秒&#xff0c;DS18B20采集温度值&#xff0c;按键设置温度报警上下限&#xff0c;实际测量温度低于下限或高于上限&#xff0c;蜂鸣器报警&#xff0c;同时将测量温度上传到蓝牙助手。 二、硬件设计 原理图如下&#xff1a; 三…...

ElementUI浅尝辄止19:Badge 标记

出现在按钮、图标旁的数字或状态标记。 1.如何使用&#xff1f; 可展示新消息数量。 //定义value属性&#xff0c;它接受Number或者String。<el-badge :value"12" class"item"><el-button size"small">评论</el-button> <…...