不为失败找理由,只为成功找方法。所有的不甘,因为还心存梦想,所以在你放弃之前,好好拼一把,只怕心老,不怕路长。
本系统是一个简易版的水果库存系统,在前面的文章里面也有写过 水果库存基础版 和水果库存进阶版;这两个版本的区别是基础版是使用javaSE的知识,进阶版是使用了Spring框架技术,特别是合理的理由了IOC(控制反转)进行重构;如果有兴趣的小伙伴可以点击对应链接查看详情。 而编写本篇文章目的是为了把页面以网页的形式展现出来。而该篇文章适合人群是刚学会SSM框架但是不太懂怎么整合的小伙伴,也适合不知道前端和后端怎么联调开发流程的小伙伴。所以本系统会从0到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
① HTML
② CSS
③ JQuery
④ AJAX
以上就是本系统使用到技术栈,如果你已经学习了以上知识,但是又不知道怎么运用,那么本篇文章会一一进行讲解。
我们先来看看项目的总体结构:
由上图所示可以知道,我们的项目是以maven的形式进行搭建的。系统架构使用了常见的MVC三层架构进行实现。本项目不是使用前后端分离的,而是使用了模板引擎Thymeleaf进行页面渲染。 然后就是功能分析;本系统实现了常见的CRUD功能。就是查询水果库存(包含分页)新增水果库存,修改水果库存和删除水果库存。 不过在开发功能之前我们要先做一些准备工作,首先得有存数据的仓库— 数据库,所以我们先来设计一下数据库。
本系统的数据库设计比较简单,就一个表就可以了,我这里把该表名为t_fruit,该表设计如下:
字段名 | 数据类型 | 非空 | 自增 | 键 | 注释 |
---|---|---|---|---|---|
fid | int | yes | yes | 主键 | 编号 |
fname | varchar(20) | yes | no | - | 名称 |
price | int | no | no | - | 单价 |
fcount | int | no | no | - | 数量 |
remark | varchar(50) | no | no | - | 广告词 |
producer | varchar(10) | no | no | - | 产地 |
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语句就可以创建一个数据库和数据表,现在有了数据表,那么接下来就要开始从后端到前端的开发了。我们先从后端开始>>>
后端的任何功能的开始,都必须有一个环境,所以我们先来搭建一个SSM环境出来。本系统IDE使用IDEA进行开发,而且是基于maven的,具体搭建步骤如下:
以上步骤就已经创建了一个基础maven项目。接下来要配置一下maven:
由于我们本系统的页面是网页的形式,所以我们还需要在maven上添加web模块,具体步骤如下:
到此本系统的Web项目就创建完成了,项目结构如下所示:
项目创建完毕之后,那么就该导入依赖了,和我们以前导入Jar包是一个意思。之前是我们自己导入每一个Jar包,但是有了maven之后,那么我们只要导入对应的坐标依赖就行了,至于Jar包maven会帮我们下载下来。依赖导入是在pom.xml文件中进行编写,本系统使用到的依赖有如下:
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-webmvcartifactId>
<version>5.3.20version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>5.3.20version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aspectsartifactId>
<version>5.3.20version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-txartifactId>
<version>5.3.20version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.22version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatisartifactId>
<version>3.5.7version>
dependency>
<dependency>
<groupId>org.mybatisgroupId>
<artifactId>mybatis-springartifactId>
<version>2.0.6version>
dependency>
<dependency>
<groupId>org.thymeleafgroupId>
<artifactId>thymeleaf-spring5artifactId>
<version>3.0.15.RELEASEversion>
dependency>
dependencies>
<build>
<resources>
<resource>
<directory>src/main/javadirectory>
<includes>
<include>**/*.propertiesinclude>
<include>**/*.xmlinclude>
includes>
<filtering>falsefiltering>
resource>
<resource>
<directory>src/main/resourcesdirectory>
<includes>
<include>**/*.propertiesinclude>
<include>**/*.xmlinclude>
includes>
<filtering>falsefiltering>
resource>
resources>
build>
到此,本项目的构建已经初步完成了,但是到真正编写功能模块还得配置几个文件才可以,项目有些基础的小伙伴已经知道接下来要做的步骤了,那就是整合SSM。
接下来就是SSM整合,本质上就是进行一些xml配置文件的配置,而我们需要怎么配置呢?我们这里可以分成6个文件;分别为:database.properties(数据库参数文件)、mybatis-config.xml(Mybatis核心配置文件)、spring-dao.xml(Mybatis与Spring整合文件/持久层配置文件)、spring-service.xml(业务层配置文件)、spring-mvc.xml(SpringMVC控制层配置文件)和applicationContext.xml(整合配置文件)。好了,废话不多说,直接开干~
# 驱动
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
DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
settings>
<typeAliases>
<package name="pojo"/>
typeAliases>
configuration>
<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">
<context:property-placeholder location="classpath:database.properties"/>
<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>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<property name="basePackage" value="mapper"/>
bean>
beans>
<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">
<context:component-scan base-package="service"/>
beans>
<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">
<mvc:annotation-driven/>
<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/"/>
<context:component-scan base-package="controller"/>
<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>
<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">
<import resource="classpath:spring-dao.xml"/>
<import resource="classpath:spring-service.xml"/>
<import resource="classpath:spring-mvc.xml"/>
beans>
本文件配置的目的是为了把三个文件整合起来变成一个文件,到后面启动项目的时候,利用Tomcat初始化配置把该文件作为接口加载即可。
☆☆☆本文件特别重要,如果本文件没有配置,那么当访问页面的时候会出现404。原因是本配置是暴露给浏览器的。本文件是在WEB-INF目录下,具体配置如下所示:
<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">
<servlet>
<servlet-name>springmvcservlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
<init-param>
<param-name>contextConfigLocationparam-name>
<param-value>classpath:applicationContext.xmlparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>springmvcservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
<filter>
<filter-name>encodingFilterfilter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
<init-param>
<param-name>encodingparam-name>
<param-value>utf-8param-value>
init-param>
filter>
<filter-mapping>
<filter-name>encodingFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
<session-config>
<session-timeout>15session-timeout>
session-config>
web-app>
到此,本系统的所有配置就已经完毕,SSM整合也到此完成了,接下来就可以开始后端功能的编写了。
我们先来编写持久层,也就是操作数据库的功能,不过在编写之前,我们得先创建一个与数据表对应的实体类, 具体代码如下:
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);
}
到了业务层,由于本系统的业务还是比较简单的,直接调用持久层就可以了:
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;
}
}
最后就到了控制层的编写,这里是直接与前端交互的接口,具体代码如下:
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;
}
}
到此,后端的代码就已经编写完成了,我们来看一下整体结构图:
后端代码已经编写完毕了,接下来就是前端页面了,我们先把页面结构编写出来,而前端的所有代码在本项目中是存放在web\WEB-INF\pages;本系统页面一共有2个,分别是index首页和food主体页面,具体HTML页面代码如下所示~
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>
DOCTYPE html>
<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}|">3td>
<td th:text="${fruit.count}" th:class="|count${item.count}|">60td>
<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>当前第 <span style="color: coral; size: 1.5rem; font-weight: bold" th:text="${pageNum}">1span> 页 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> | span>
<a th:href="@{/fruit/all/{pageNum}(pageNum=${pageNum}+1)}">下一页a>
<span> 总条数:<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样式编写完毕,就可以访问一下页面试试效果了。
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;
}
到此我们可以查看一下页面效果:
以上就是编码到目前为止的页面效果了;数据的展示是我已经在数据库中添加了一些数据,然后由于我们使用Thymeleaf遍历后端传来的数据,所以有数据显示,也由此查询分页功能也就完成了。
接下来就到最后一步了,那就是把剩下的新增、修改和删除功能利用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页面引入
<script type="text/javascript" src="./js/food.js" th:src="@{/js/food.js}" >script>
<script type="text/javascript" src="js/jquery-3.3.1.min.js" th:src="@{/js/jquery-3.3.1.min.js}">script>
然后我们在到html对应的标签中添加鼠标悬浮和移除事件
<tr th:each="fruit,item:${fruitList}" th:id="${item.count}" onmouseover="showBGColor()" onmouseout ="cleanBGColor()">
tr>
我们来看一下功能实现:
鼠标悬浮的时候,出现背景颜色~~
鼠标移除,变回原本的颜色,功能实现OK。
废话不多说,直接上代码:
//当页面加载完成后执行
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;
})
}
最后绑定点击事件就可以了
<tr th:each="fruit,item:${fruitList}" th:id="${item.count}" onclick="showData(this.id)">tr>
我们来看一下功能效果:
由此可见,新增和修改功能都已经实现了。
代码如下:
//点击删除小图标执行删除数据的函数 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>
<img src="./images/del.jpg" th:src="@{/images/del.jpg}" th:id="${item.count}" onclick="delFruit(this.id)" alt="删除" class="delImg"/>
td>
然后我们看效果即可:
点击确定删除会出现提示语:
删除成功!!!
查询数据列表可以知道test02数据已经被删除成功!!!
至此,本系统的功能实现完毕了。本系统是使用了后端SSM框架,模板引擎Thymeleaf;前端使用了HTML+CSS+JQuery技术。另外本人技术有限,可能在本篇内容中有不足之地,各方道友可私聊畅谈一二~~
最后关于本系统使用到的静态资源可以可以使用自己的,也可以从源码中获取。本系统的源码已上传到码云。链接为:https://gitee.com/originnan/fruit