下载地址:
前端:https://download.csdn.net/download/2401_83418369/90649449
后端: https://download.csdn.net/download/2401_83418369/90649441
一、项目基础环境搭建
1、新建Maven项目

2、创建目录,结构如下:

3、配置pom.xml文件引入依赖:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.study</groupId>
<artifactId>ssm</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>ssm Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!-- 引入Springmvc框架-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.3.8</version>
</dependency>
<!-- 引入Spring-jdbc,支持事务相关-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.8</version>
</dependency>
<!-- 引入Spring aspect 切面编程需要的库-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.8</version>
</dependency>
<!-- 引入Mybatis库-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<!-- 引入druid数据库连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.6</version>
</dependency>
<!-- 引入MySQL驱动,这里根据自己的MySQL版本,有5.0也有8.0的,具体情况具体分析-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
</dependencies>
<build>
<finalName>ssm</finalName>
</build>
</project>
4、配置Tomcat环境:
具体的步骤看:tomcat的安装与配置(包含在idea中配置tomcat)_2024.1.4idea配置tomcat-CSDN博客

启动Tomcat测试一下:

5、项目全局配置web.xml文件
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!--
1、ContextLoaderListener监听器作用是启动web容器时,自动装配ApplicationContext的配置信息
2、它实现了ServletContextListener接口,在web.xml配置该监听器,启动容器时,会默认执行它实现的方法-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 使用Springmvc的处理中文乱码的过滤器-->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<!--初始化值encoding,设置编码方式-->
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 配置HiddenHttpMethodFilter过滤器,
用于将post请求转化为put或delete请求-->
<filter>
<filter-name>hiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>hiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 配置springmvc分配器DispatcherServlet-->
<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 配置要解析的Springmvc配置文件:这里的细节:
如果配置文件的名字是分配器的名字后面加上 -servlet 并且把这个文件放在web.xml文件一样的位置,
那么web.xml文件就不需要再配置这个contextConfigLocation 参数-->
<!-- <init-param>-->
<!-- <param-name>contextConfigLocation</param-name>-->
<!-- <param-value>classpath:WEB-INF/springDispatcherServlet-servlet.xml</param-value>-->
<!-- </init-param>-->
<!-- 设置Servlet的优先级,数字越小优先级越高-->
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 配置URL映射地址-->
<servlet-mapping>
<servlet-name>springDispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
新建Spring配置文件



如果有报错,将配置文件的提示改为语法检查

包的结构 :

6、Springmvc配置文件
如果Springmvc配置文件的名字是分配器的名字后面加上 -servlet 并且把这个文件放在web.xml文件一样的位置,那么web.xml文件就不需要再配置这个contextConfigLocation 参数


springDispatcherServlet-servlet.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"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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 http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--
use-default-filters="false" 禁用默认过滤器规则
Springmvc只是接管控制层,对于其他的dao,Service层不管
context:include-filter 只扫描控制器
-->
<context:component-scan base-package="com.hspedu.furn"
use-default-filters="false">
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- 配置视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 配置前缀和后缀-->
<property name="prefix" value="/WEB-INF/views/"/>
<property name="suffix" value=".html"/>
</bean>
<!-- 能支持 SpringMVC 高级功能,比如 JSR303 校验,映射动态请求 -->
<mvc:annotation-driven></mvc:annotation-driven>
<!-- 将 SpringMVC 不能处理的请求交给 Tomcat, 比如请求 css,js 等 -->
<mvc:default-servlet-handler/>
</beans>
测试一下:创建一个ControllerTest类

package com.hspedu.furn.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* 这是一个测试类,测试浏览器的请求是否能正确被解析和响应
*/
@Controller
public class ControllerTest {
@RequestMapping("/hi")
public String hi(){
System.out.println("hi方法被调用。。。");
return "hi";
}
}
在创建一个返回页面: (注意这个页面是根据自己的视图解析器的前后缀匹配来决定的)

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>hi</title>
</head>
<body>
<h1>hi,成功!</h1>
</body>
</html>
启动Tomcat后访问得到:

7、配置Spring和Mybatis并完成整合
先在pom.xml文件中引入Mybatis整合Spring的适配包
<!-- 引入Mybatis整合Spring的适配包-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.6</version>
</dependency>
新建一个jdbc.properties文件

配置jdbc.propertis文件 (注意MySQL用户名和密码必须是自己的,还有数据库目前还没有创建,后面创建好后再修改)
sql
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.user=root
jdbc.pwd=root
jdbc.url=jdbc:mysql://localhost:3306/XXX?useSSL=true&useUnicode=true&characterEncoding=UTF-8
在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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
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 http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- Spring的配置文件:主要配置和业务逻辑有关的,比如数据源,事务控制等-->
<!-- 配置扫描包不包含Controller层,因为Controller层由Springmvc扫描-->
<context:component-scan base-package="com.hspedu.furn">
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- 配置数据库连接池,引入外部的jdbc.properties文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 配置数据源对象-->
<bean class="com.alibaba.druid.pool.DruidDataSource" id="pooledDataSource">
<property name="username" value="${jdbc.user}"/>
<property name="password" value="${jdbc.pwd}"/>
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
</bean>
<!-- 配置Mybatis和Spring整合
1、在项目中引入Mybatis整合Spring的适配包
-->
<bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactory">
<!-- 指定Mybatis全局配置文件-->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<!-- 指定数据源-->
<property name="dataSource" ref="pooledDataSource"/>
<!-- 指定Mybatis的Mapper文件,在开发中通常放在类路径中,这里没有创建对应的xml文件会爆红-->
<property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>
<!--配置扫描器,将Mybatis接口的实现加入到ioc容器-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 1、扫描所有的dao接口的实现,加入到ioc容器-->
<!-- 2、这里dao接口就是Mapper接口-->
<property name="basePackage" value="com.hspedu.furn.dao"/>
</bean>
<!--配置事务管理器对象
要配置数据源属性,这样指定该事务管理器 是对哪个数据源进行事务控制-->
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
<property name="dataSource" ref="pooledDataSource"/>
</bean>
<!--
之前启动事务基于注解声明式事务管理,现在采用是下面一种xml配置+切入点表达式
<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>-->
<aop:config>
<!-- 切入点表达式-->
<aop:pointcut id="txPoint" expression="execution(* com.hspedu.furn.service..*(..))"/>
<!-- 配置事务增强,使用txAdvice指定规则对txPoint进行切入-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPoint"/>
</aop:config>
<!-- 配置事务增强(指定事务规则),也就是指定事务如何切入-->
<tx:advice id="txAdvice">
<tx:attributes>
<!-- 代表所有方法都是事务方法-->
<tx:method name="*"/>
<!-- 以get开始的所有方法,我们认为是只读,进行调优-->
<tx:method name="get*" read-only="true"/>
</tx:attributes>
</tx:advice>
</beans>
创建mybatis-config.xml文件 和一个mapper目录

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>
</configuration>
配置完成后进行测试
新建Test的目录

package com.hspedu.test;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class t1 {
@Test
public void test(){
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
Object bean = ioc.getBean("sqlSessionFactory");
System.out.println(bean);
Object bean1 = ioc.getBean("transactionManager");
System.out.println(bean1);
Object bean2 = ioc.getBean("pooledDataSource");
System.out.println(bean2);
}
}
如果没有报错,那么就是配置成功的

8、创建表,使用逆向工程生成Bean、XxxMapper和XxxMappe.xml
添加一个数据库和数据表
sql
create database furn_ssm;
use furn_ssm;
create table furn(
id int(11) primary key auto_increment,
name varchar(64) not null,
maker varchar(64) not null,
price decimal(11,2) not null,
sales int(11) not null,
stock int(11) not null,
img_path varchar(256) not null
);
INSERT INTO furn(`id` , `name` , `maker` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , '北欧风格小桌子' , '熊猫家居' , 180 , 666 , 7 , 'assets/images/product-image/6.jpg');
INSERT INTO furn(`id` , `name` , `maker` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , '简约风格小椅子' , '熊猫家居' , 180 , 666 , 7 , 'assets/images/product-image/4.jpg');
INSERT INTO furn(`id` , `name` , `maker` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , '典雅风格小台灯' , '蚂蚁家居' , 180 , 666 , 7 , 'assets/images/product-image/14.jpg');
INSERT INTO furn(`id` , `name` , `maker` , `price` , `sales` , `stock` , `img_path`)
VALUES(NULL , '温馨风格盆景架' , '蚂蚁家居' , 180 , 666 , 7 , 'assets/images/product-image/16.jpg');
创建后 可以修改对应的jdbc.properties配置信息:主要是将数据库的名称改为furn_ssm
在mybatis-config.xml配置文件配置javaBean的别名
sql
<?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>
<!-- 配置别名-->
<typeAliases>
<!-- 如果一个包下面有很多类名,那么可以通过配置package来配置别名,他会将包下面的类名作为别名-->
<package name="com.hspedu.furn.bean"/>
</typeAliases>
</configuration>
在pom.xml文件中引入逆向工程依赖包 (一定要记得刷新Maven)
sql
<!-- 引入mybatis逆向工程依赖包-->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.4.0</version>
</dependency>
逆向工程配置文件看下面 :
MyBatis Generator Core -- MyBatis Generator XML Configuration File Reference
直接在项目下面新建一个xml文件用于放置逆向工程的配置信息

配置信息如下:(注意一些MySQL的名字和密码必须是自己的,还有一些工程名是自定义的)
sql
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id="DB2Tables" targetRuntime="MyBatis3">
<!-- 生成没有注释的Bean-->
<commentGenerator>
<property name="suppressAllComments" value="true"/>
</commentGenerator>
<!-- 配置数据库连接信息-->
<jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/furn_ssm?characterEncoding=UTF-8"
userId="root"
password="root">
</jdbcConnection>
<javaTypeResolver >
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!-- 指定javaBean生成的位置-->
<javaModelGenerator targetPackage="com.hspedu.furn.bean" targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!-- 指定Sql映射文件的生成位置-->
<sqlMapGenerator targetPackage="mapper" targetProject=".\src\main\resources">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<!-- 指定dao接口生成的位置,也就是mapper接口-->
<javaClientGenerator type="XMLMAPPER" targetPackage="com.hspedu.furn.dao" targetProject=".\src\main\java">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<!-- 指定要逆向生成的表和生成策略 -->
<table tableName="furn" domainObjectName="Furn">
</table>
</context>
</generatorConfiguration>
测试逆向工程代码参考文档:
MyBatis Generator Core -- Running MyBatis Generator With Java

创建逆向工程的测试类 :

sql
package com.hspedu.furn;
import org.junit.Test;
import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.internal.DefaultShellCallback;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class MBGTest {
@Test
public void mbgTest() throws Exception {
List<String> warnings = new ArrayList<String>();
boolean overwrite = true;
File configFile = new File("mbg.xml");
ConfigurationParser cp = new ConfigurationParser(warnings);
Configuration config = cp.parseConfiguration(configFile);
DefaultShellCallback callback = new DefaultShellCallback(overwrite);
MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
myBatisGenerator.generate(null);
}
}
测试结果:(没有输出结果就是最好的结果)

逆向工程自动创建好了 Bean、XxxMapper和XxxMappe.xml

将之前注销的部分取消注销

测试furnMapper接口的方法
@Test
public void insertTest(){
ApplicationContext ioc =
new ClassPathXmlApplicationContext("applicationContext.xml");
System.out.println(ioc);
}
当获取ioc容器时输出的结果抛出了异常

我的MySQL驱动是8.0,这里的异常是说已存在了result Maps,我查看了FurnMapper.xml文件发现了有重复的部分(基本上有一半重复)

解决该bug:(查了很多资料)
原因:多库/多Schema导致重复生成,当数据库存在多个 Schema 或 Catalog 时,MyBatis Generator 可能无法区分具体表来源,导致重复生成代码。
总结:MyBatis Generator 生成 Mapper.xml 重复代码的解决方案
方法1:JDBC 连接添加 nullCatalogMeansCurrent
属性:强制 MyBatis Generator 将未指定 Catalog 的表视为当前连接的默认库
<property name="nullCatalogMeansCurrent" value="true" />

方法2:在 <table> 标签中指定 catalog 或 schema:明确指定表所属的数据库名称,避免扫描其他库的同名表
<table tableName="furn" domainObjectName="Furn" catalog="furn_ssm">
catalog 对应 MySQL 的数据库名,schema 适用于其他数据库
下面的官方解释建议采用第一种方法:
MyBatis Generator Core -- MySql Usage Notes

继续完成测试:
先在Furn类中添加有参和无参构造器

package com.hspedu.test;
import com.hspedu.furn.bean.Furn;
import com.hspedu.furn.dao.FurnMapper;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.math.BigDecimal;
public class FurnMapperTest {
@Test
public void insertTest(){
ApplicationContext ioc =
new ClassPathXmlApplicationContext("applicationContext.xml");
FurnMapper bean = ioc.getBean(FurnMapper.class);
Furn furn = new Furn(null, "北欧", "家具", new BigDecimal(111), 6666, 55, "img");
int insert = bean.insert(furn);
System.out.println(insert);
}
}
结果正常输出


二、搭建Vue前端工程
1、安装node.js(14.17.3)和插件cli
删除之前的node.js版本,因为之前我用的是vue2,现在用vue3,需要下载14.17.3

node.js(14.17.3) 下载地址(适用Windows64位)
https://nodejs.org/dist/v14.17.3/node-v14.17.3-x64.msi
下载完成后,新开一个命令行输入node -v检验 node.js版本


全局安装vue插件cli:
在命令行输入以下命令
npm install -g @vue/cli

创建vue项目:这个位置随意

进入该文件并输入cmd回车

输入命令(ssm_vue这个是项目名称)
vue create ssm_vue
下面是选择步骤
手动选择

选择圈出的三个选项 ,enter

版本选3.x ,enter

输入y,enter

选择下面的方式,enter

下面的步骤是保存当前配置,下次配置时可以直接使用,下面的名字是自定义的

这样就成功了

根据他的提示启动服务



2、使用idea 打开ssm_vue项目,并配置项目启动
将自己创建的项目拖拽到idea即可

打开新窗口




找到自己安装node.js的位置



点击运用并确定

退出之前启动的服务:Ctrl+C 输入y

点击启动即可


点击local的网址就可以访问



3、配置vue服务端口
在vue.config.js文件中配置服务端口

再次启动服务后

4、安装element-plus
直接在cmd中输入下面的命令即可
npm install element-plus --save

三、创建项目基础界面
1、删除HomeView.vue的内容

2、 将原来的组件删除,新创建一个组件 Header

内容如下:
<script>
export default {
name:"Header"
}
</script>
<template>
<div style="height: 50px;line-height: 50px;border-bottom: 1px solid #ccc;display: flex">
<div style="width: 200px;padding-left: 30px;font-weight: bold;color:dodgerblue">后台管理</div>
<div style="flex: 1"></div>
<div style="width: 100px">下拉框</div>
</div>
</template>
<style scoped>
</style>
3、将App.vue文件内容删除,增加下面的代码
<template>
<Header/>
</template>
<script>
//@表示src
//即使你导入了 Header 组件,仍需在 components 选项中显式注册,
// 才能在当前组件的模板 <template> 中使用 <Header/> 标签。
//import 的作用:仅将组件代码引入当前文件,但 Vue 并不知道这个组件是否要在模板中使用
import Header from "@/components/Header.vue";
//Vue 组件必须通过 export default 导出其配置对象(如 data、methods、components 等)
// ,否则父组件或根实例无法识别和使用它
//导入相当于"采购食材",注册相当于"将食材加入菜单"。即使采购了食材(import Header),
// 如果不把菜品加入菜单(注册到 components),顾客(模板)就无法点餐(使用 <Header/>)
export default {
name:"Layout",
//components 的作用:显式声明当前组件依赖的子组件(注册)
// ,将导入的组件与模板中的标签名关联起来,让 Vue 在编译模板时能正确解析
components:{
Header
}
}
</script>
<style>
</style>
解释:为什么要导入和导出?
import 的作用:仅将组件代码引入当前文件,但 Vue 并不知道这个组件是否要在模板中使用。
components 的作用:显式声明当前组件依赖的子组件,将导入的组件与模板中的标签名关联起来,让 Vue 在编译模板时能正确解析(注册)
再次启动项目,就可以看到如下的效果:

配置全局css文件
*{
margin: 0;
padding: 0;
box-sizing: border-box;
}

引入css配置文件
import '@/assets/css/global.css'

引入 Element-plus,这里也是在main.js文件写入
快速开始 | Element Plus (element-plus.org)
//引入Element-plus
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
createApp(App).use(store).use(router).use(ElementPlus).mount('#app')
如何提示说没有安装就点击进行安装

在App.vue文件中使用了一个基础按钮

重启项目后

添加下拉列表框:(copy)
Dropdown 下拉菜单 | Element Plus (element-plus.org)
<script setup>
</script>
<template>
<div>
<el-menu
default-active="2"
class="el-menu-vertical-demo"
@open="handleOpen"
@close="handleClose"
>
<el-sub-menu index="1">
<template #title>
<el-icon><location /></el-icon>
<span>Navigator One</span>
</template>
<el-menu-item-group title="Group One">
<el-menu-item index="1-1">item one</el-menu-item>
<el-menu-item index="1-2">item two</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="Group Two">
<el-menu-item index="1-3">item three</el-menu-item>
</el-menu-item-group>
<el-sub-menu index="1-4">
<template #title>item four</template>
<el-menu-item index="1-4-1">item one</el-menu-item>
</el-sub-menu>
</el-sub-menu>
<el-menu-item index="2">
<el-icon><icon-menu /></el-icon>
<span>Navigator Two</span>
</el-menu-item>
<el-menu-item index="3" disabled>
<el-icon><document /></el-icon>
<span>Navigator Three</span>
</el-menu-item>
<el-menu-item index="4">
<el-icon><setting /></el-icon>
<span>Navigator Four</span>
</el-menu-item>
</el-menu>
</div>
</template>
<style scoped>
</style>
在APP.vue文件中导入Aside组件并注入到components


找到网址的默认路径
再默认的网页中添加一个按钮

网页布局分为三个部分

进一步调整得到如下的结果:
HomeView.vue的页面如下所示:
<template>
<!--这个主要是用来路由到的页面,默认访问的页面-->
<div>
<!-- 添加按钮和查询框-->
<div style="margin: 10px 5px;display:inline-block">
<el-button type="primary">新增</el-button>
<el-button>其他</el-button>
</div>
<div style="display: inline-block">
<el-input v-model="input" style="width: 150px" placeholder="请输入关键字" />
<el-button type="success">提交</el-button>
</div>
<!-- 表格-->
<el-table :data="tableData" stripe style="width: 100%">
<el-table-column sortable prop="date" label="日期" />
<el-table-column prop="name" label="姓名" />
<el-table-column prop="address" label="地址" />
<el-table-column fixed="right" label="操作" min-width="120">
<template #default>
<el-button link type="primary" @click="handleClick">
删除
</el-button>
<el-button link type="primary">编辑</el-button>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
// @ is an alias to /src
// import HelloWorld from '@/components/HelloWorld.vue'
export default {
name: 'HomeView',
components: {
},
//数据池的方法
data(){
return{
tableData:[
{
date: '2016-05-03',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-02',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-04',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-01',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
]
}
}
}
</script>

四、添加家居信息

创建FurnService接口的方法
package com.hspedu.furn.service;
import com.hspedu.furn.bean.Furn;
public interface FurnService {
//添加方法
public void save(Furn furn);
}
FurnServiceImpl实现类的方法
package com.hspedu.furn.service.Impl;
import com.hspedu.furn.bean.Furn;
import com.hspedu.furn.dao.FurnMapper;
import com.hspedu.furn.service.FurnService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class FurnServiceImpl implements FurnService {
//实现自动依赖注入
@Resource
private FurnMapper furnMapper;
@Override
public void save(Furn furn) {
furnMapper.insertSelective(furn);
}
}

新建一个测试类进行测试
package com.hspedu.service;
import com.hspedu.furn.bean.Furn;
import com.hspedu.furn.service.FurnService;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.math.BigDecimal;
public class FurnServiceTest {
private ClassPathXmlApplicationContext ioc;
//FurnServiceImpl被Spring AOP代理(如使用了事务、切面等),
// 导致实际注入的是代理对象,其类型为接口FurnService
public FurnService furnService;
@Before
public void init(){
ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
furnService = ioc.getBean( FurnService.class);
}
@Test
public void save(){
Furn furn = new Furn(null, "北欧", "家具", new BigDecimal(111), 6666, 55, "img");
furnService.save(furn);
}
}
给img_path属性设置默认值

添加一个Message对象作为响应封装对象

package com.hspedu.furn.bean;
import java.util.HashMap;
import java.util.Map;
public class Message {
//状态码,200成功,400失败
private int code;
//信息
private String message;
//返回浏览器的数据
private Map<String,Object> extend=new HashMap<>();
//返回成功的信息
public static Message success(){
Message message = new Message();
message.setCode(200);
message.setMessage("success");
return message;
}
//返回失败的信息
public static Message fail(){
Message message = new Message();
message.setCode(400);
message.setMessage("fail");
return message;
}
//返回传输的信息
//这里return this表示直接返回该对象不用创建新的对象,
// 支持链式调用,避免了不必要的对象复制开销。
public Message add(String key,Object value){
extend.put(key,value);
return this;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Map<String, Object> getExtend() {
return extend;
}
public void setExtend(Map<String, Object> extend) {
this.extend = extend;
}
public Message() {
}
public Message(int code, String message, Map<String, Object> extend) {
this.code = code;
this.message = message;
this.extend = extend;
}
}
在Controller层增加FurnController对象
package com.hspedu.furn.controller;
import com.hspedu.furn.bean.Furn;
import com.hspedu.furn.bean.Message;
import com.hspedu.furn.service.FurnService;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
//作为组件被注入到Springmvc
@Controller
public class FurnController {
//自动依赖注入,注入接口类型(动态代理对象)
@Resource
private FurnService furnService;
/**
* @ResponseBody将相应的对象转成json格式的字符串
* @RequestBody将前端发送的json格式的字符串封装成对象
* @PostMapping("/save")规定使用post方式发送请求,访问的路径是/save,
* 斜杆默认转成ip:端口工程路径
* @param furn 封装的对象
* @return
*/
@ResponseBody
@PostMapping("/save")
public Message save(@RequestBody Furn furn){
furnService.save(furn);
Message success = Message.success();
return success;
}
}
在pom.xml文件中引入Jackson用于处理json数据
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.4</version>
</dependency>
一定一定一定要刷新Maven,否则不能真正的导入依赖 !!!!必须要看见导入的依赖才算真正的导入成功!!!

使用Postman进行测试:
将header请求头的Content-Type设置为 application/json

请求体的数据:
{
"name":"小台灯",
"maker":"中国制造",
"price":100,
"sales":1000,
"stock":999
}

响应的数据如下:


在HomeView.vue页面完善添加功能,当点击添加按钮可以弹出对话框
<template>
<!--这个主要是用来路由到的页面,默认访问的页面-->
<div>
<!-- 添加按钮和查询框-->
<div style="margin: 10px 5px;display:inline-block">
<el-button type="primary" @click="add">新增</el-button>
<el-button>其他</el-button>
</div>
<div style="display: inline-block">
<el-input v-model="input" style="width: 150px" placeholder="请输入关键字" />
<el-button type="success">提交</el-button>
</div>
<!-- 表格-->
<el-table :data="tableData" stripe style="width: 100%">
<el-table-column sortable prop="date" label="日期" />
<el-table-column prop="name" label="姓名" />
<el-table-column prop="address" label="地址" />
<el-table-column fixed="right" label="操作" min-width="120">
<template #default>
<el-button link type="primary" @click="handleClick">
删除
</el-button>
<el-button link type="primary">编辑</el-button>
</template>
</el-table-column>
</el-table>
<!-- 下面是对话框和表单
<el-input v-model="form.name" style="width: 80%"></el-input>
这里的form.name 表示form对象的属性name
必须和后端的对象字段一样因为要将这些字段信息生成json格式打包给后端,
这里的属性名可以动态生成,不需要在数据池里面编写
-->
<el-dialog
title="提示"
v-model="dialogVisible"
width="30%">
<el-form :model="form" label-width="120px">
<!-- 家居名 -->
<el-form-item label="家居名">
<el-input v-model="form.name" style="width: 80%"></el-input>
</el-form-item>
<!-- 厂商 -->
<el-form-item label="厂商">
<el-input v-model="form.maker" style="width: 80%"></el-input>
</el-form-item>
<!-- 价格 -->
<el-form-item label="价格">
<el-input v-model="form.price" style="width: 80%"></el-input>
</el-form-item>
<!-- 销量 -->
<el-form-item label="销量">
<el-input v-model="form.sales" style="width: 80%"></el-input>
</el-form-item>
<!-- 库存 -->
<el-form-item label="库存">
<el-input v-model="form.stock" style="width: 80%"></el-input>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<!-- 直接写dialogVisible = false,因为只用一句,所以可以直接写在属性上-->
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="save">确定</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script>
// @ is an alias to /src
// import HelloWorld from '@/components/HelloWorld.vue'
export default {
name: 'HomeView',
components: {
},
//数据池的方法
data(){
return{
//这里默认表单是不显示的
dialogVisible:false,
form:{},
tableData:[
{
date: '2016-05-03',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-02',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-04',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-01',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
]
}
},
methods:{
add(){
this.dialogVisible=true;
//但调用该方法后,将form对象的信息进行清除,
// 防止下次点击后出现上一次填写的数据
this.form={}
}
}
}
</script>

在idea的终端安装Axios (注意要在该vue项目的路径下安装)
npm i axios -S

创建一个utils包,在包里创建一个request.js文件

这个request文件用来设置request请求
//导入Axios包
import axios from "axios";
//通过Axios创建对象Request 发送请求到后端
const request = axios.create({
timeout:5000
})
//Request拦截器加上统一的处理
//比如Content-Type
request.interceptors.request.use(config=>{
config.headers['Content-Type'] = 'application/json;charset=utf-8'
return config
},error=>{
return Promise.reject(error)
})
//导出Request对象,在其他文件就可以使用
export default request
当测试save方法时就会报这个跨域请求的问题

这里解决这个问题采用的方法是代理服务器(Proxy)
**原理:**利用同源策略仅限制浏览器的特性,通过中间服务器转发请求,隐藏真实后端地址
实现方式:
- **开发环境代理:**在Vue/React中配置vue.config.js或webpack-dev-server,将/api路径代理到目标服务器
- **生产环境Nginx:**通过反向代理配置,将请求路径映射到后端服务,并添加CORS头
**适用场景:**开发调试阶段常用,生产环境需搭配Nginx
解决:在 vue.config.js配置文件中添加proxy代理对象
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true
})
module.exports={
devServer:{
port:10000 ,//启动端口
// 设置代理
proxy:{
'/api':{ //设置拦截器,拦截器的格式 斜杆+名字 拦截规则:匹配所有以/api开头的请求路径
target:'http://localhost:8080/ssm', //目标服务器地址:将匹配的请求转发到http://localhost:8080/ssm
changeOrigin:true, //设置同源
pathRewrite:{ //路径重写
'/api':'' //api被替换为空,前端统一使用代理前缀(如 /api),但后端接口路径不含该前缀
//前端请求:/api/user/list
//实际转发:http://backend.com/user/list
// /api 这部分被 target的路径代替
}
}
}
}
}

在HomeView.vue文件中完善save方法
<template>
<!--这个主要是用来路由到的页面,默认访问的页面-->
<div>
<!-- 添加按钮和查询框-->
<div style="margin: 10px 5px;display:inline-block">
<el-button type="primary" @click="add">新增</el-button>
<el-button>其他</el-button>
</div>
<div style="display: inline-block">
<el-input v-model="input" style="width: 150px" placeholder="请输入关键字" />
<el-button type="success">提交</el-button>
</div>
<!-- 表格-->
<el-table :data="tableData" stripe style="width: 100%">
<el-table-column sortable prop="date" label="日期" />
<el-table-column prop="name" label="姓名" />
<el-table-column prop="address" label="地址" />
<el-table-column fixed="right" label="操作" min-width="120">
<template #default>
<el-button link type="primary" @click="handleClick">
删除
</el-button>
<el-button link type="primary">编辑</el-button>
</template>
</el-table-column>
</el-table>
<!-- 下面是对话框和表单
<el-input v-model="form.name" style="width: 80%"></el-input>
这里的form.name 表示form对象的属性name
必须和后端的对象字段一样因为要将这些字段信息生成json格式打包给后端,
这里的属性名可以动态生成,不需要在数据池里面编写
-->
<el-dialog
title="提示"
v-model="dialogVisible"
width="30%">
<el-form :model="form" label-width="120px">
<!-- 家居名 -->
<el-form-item label="家居名">
<el-input v-model="form.name" style="width: 80%"></el-input>
</el-form-item>
<!-- 厂商 -->
<el-form-item label="厂商">
<el-input v-model="form.maker" style="width: 80%"></el-input>
</el-form-item>
<!-- 价格 -->
<el-form-item label="价格">
<el-input v-model="form.price" style="width: 80%"></el-input>
</el-form-item>
<!-- 销量 -->
<el-form-item label="销量">
<el-input v-model="form.sales" style="width: 80%"></el-input>
</el-form-item>
<!-- 库存 -->
<el-form-item label="库存">
<el-input v-model="form.stock" style="width: 80%"></el-input>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<!-- 直接写dialogVisible = false,因为只用一句,所以可以直接写在属性上-->
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="save">确定</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script>
//导入组件
import request from "@/utils/request";
export default {
name: 'HomeView',
components: {
},
//数据池的方法
data(){
return{
//这里默认表单是不显示的
dialogVisible:false,
form:{},
tableData:[
{
date: '2016-05-03',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-02',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-04',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-01',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
]
}
},
methods:{
add(){
this.dialogVisible=true;
//但调用该方法后,将form对象的信息进行清除,
// 防止下次点击后出现上一次填写的数据
this.form={}
},
save(){
request.post("/api/save",this.form).then(response=>{
console.log("response",response)
this.dialogVisible=false
})
}
}
}
</script>
五、显示家居信息
因为dao层FurnMapper接口通过逆向工程生成,所以不用再编写,直接在Service层添加方法即可
在furnService接口中添加方法:

在furnServiceImpl对象 中实现接口中的方法:

在测试类中进行测试


在Controller层增加方法

在HomeView.vue页面中修该表格的字段信息

通过Axios的request对象发送get请求拿到响应数据



将响应对象进行统一的处理, 通过Response拦截器


request.js文件
//导入Axios包
import axios from "axios";
//通过Axios创建对象Request 发送请求到后端
const request = axios.create({
timeout:5000
})
//Request拦截器加上统一的处理
//比如Content-Type
request.interceptors.request.use(config=>{
config.headers['Content-Type'] = 'application/json;charset=utf-8'
return config
},error=>{
return Promise.reject(error)
})
//response拦截器拦截响应对象,统一处理返回的结果
request.interceptors.response.use(response=>{
let res = response.data
//如果返回的是文件,就返回
if (response.config.responseType==='blob'){
return res
}
//如果是String,就转成json对象
if (typeof res==='string'){
res = res ? JSON.parse(res):res
}
return res
},error=>{
return Promise.reject(error)
})
//导出Request对象,在其他文件就可以使用
export default request
HomeView.vue文件
<template>
<!--这个主要是用来路由到的页面,默认访问的页面-->
<div>
<!-- 添加按钮和查询框-->
<div style="margin: 10px 5px;display:inline-block">
<el-button type="primary" @click="add">新增</el-button>
<el-button>其他</el-button>
</div>
<div style="display: inline-block">
<el-input v-model="input" style="width: 150px" placeholder="请输入关键字" />
<el-button type="success">提交</el-button>
</div>
<!-- 表格
:data="tableData" 单向渲染数据,从数据池的data数据池的tableData字段里获取数据,
<=> v-bind:data -->
<el-table :data="tableData" stripe style="width: 100%">
<el-table-column sortable prop="id" label="ID" />
<el-table-column prop="name" label="家居名" />
<el-table-column prop="maker" label="厂家" />
<el-table-column prop="price" label="价格" />
<el-table-column prop="sales" label="销量" />
<el-table-column prop="stock" label="库存" />
<el-table-column fixed="right" label="操作" min-width="120">
<template #default>
<el-button link type="primary" @click="handleClick">
删除
</el-button>
<el-button link type="primary">编辑</el-button>
</template>
</el-table-column>
</el-table>
<!-- 下面是对话框和表单
<el-input v-model="form.name" style="width: 80%"></el-input>
这里的form.name 表示form对象的属性name
必须和后端的对象字段一样因为要将这些字段信息生成json格式打包给后端,
这里的属性名可以动态生成,不需要在数据池里面编写
-->
<el-dialog
title="提示"
v-model="dialogVisible"
width="30%">
<el-form :model="form" label-width="120px">
<!-- 家居名 -->
<el-form-item label="家居名">
<el-input v-model="form.name" style="width: 80%"></el-input>
</el-form-item>
<!-- 厂商 -->
<el-form-item label="厂商">
<el-input v-model="form.maker" style="width: 80%"></el-input>
</el-form-item>
<!-- 价格 -->
<el-form-item label="价格">
<el-input v-model="form.price" style="width: 80%"></el-input>
</el-form-item>
<!-- 销量 -->
<el-form-item label="销量">
<el-input v-model="form.sales" style="width: 80%"></el-input>
</el-form-item>
<!-- 库存 -->
<el-form-item label="库存">
<el-input v-model="form.stock" style="width: 80%"></el-input>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<!-- 直接写dialogVisible = false,因为只用一句,所以可以直接写在属性上-->
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="save">确定</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script>
//导入组件
import request from "@/utils/request";
export default {
name: 'HomeView',
components: {
},
//数据池的方法
data(){
return{
//这里默认表单是不显示的
dialogVisible:false,
form:{},
tableData:[]
}
},
//钩子函数, created()函数调用后
//数据池和方法池的数据都进行了初始化
created() {
//调用list方法展示数据
this.list()
},
methods:{
add(){
this.dialogVisible=true;
//但调用该方法后,将form对象的信息进行清除,
// 防止下次点击后出现上一次填写的数据
this.form={}
},
save(){
request.post("/api/save",this.form).then(response=>{
console.log("response",response)
this.dialogVisible=false
//增加家居后调用查询
this.list()
})
},
list(){
request.get("/api/list").then(response=>{
this.tableData=response.extend.furnList
})
}
}
}
</script>

六、修改家居信息
在furnService接口中添加方法

在实现类中实现方法

在Controller层增加相应的业务代码

使用Postman 进行测试

回显数据并修改
点击按钮时,将 scope.row
(当前行数据)作为参数传递给 handle
方法


得到的结果是一个代理对象

将代理对象的数据绑定到form对象中


还有一种回显数据的方法是将代理对象的id取出,然后将id发送给后端,后端根据id查询对应的Furn对象,然后再将对象进行回显
因为新增和编辑都是调用同一个对话框并且点击确认时是调用同一个方法,区别是有无id
<template>
<!--这个主要是用来路由到的页面,默认访问的页面-->
<div>
<!-- 添加按钮和查询框-->
<div style="margin: 10px 5px;display:inline-block">
<el-button type="primary" @click="add">新增</el-button>
<el-button>其他</el-button>
</div>
<div style="display: inline-block">
<el-input v-model="input" style="width: 150px" placeholder="请输入关键字" />
<el-button type="success">提交</el-button>
</div>
<!-- 表格
:data="tableData" 单向渲染数据,从数据池的data数据池的tableData字段里获取数据,
<=> v-bind:data -->
<el-table :data="tableData" stripe style="width: 100%">
<el-table-column sortable prop="id" label="ID" />
<el-table-column prop="name" label="家居名" />
<el-table-column prop="maker" label="厂家" />
<el-table-column prop="price" label="价格" />
<el-table-column prop="sales" label="销量" />
<el-table-column prop="stock" label="库存" />
<el-table-column fixed="right" label="操作" min-width="120">
<template #default="scope">
<el-button link type="primary" @click="handleClick">
删除
</el-button>
<!-- 点击按钮时,将 scope.row(当前行数据)作为参数传递给 handle 方法-->
<el-button link type="primary" @click="handle(scope.row)">编辑</el-button>
</template>
</el-table-column>
</el-table>
<!-- 下面是对话框和表单
<el-input v-model="form.name" style="width: 80%"></el-input>
这里的form.name 表示form对象的属性name
必须和后端的对象字段一样因为要将这些字段信息生成json格式打包给后端,
这里的属性名可以动态生成,不需要在数据池里面编写
-->
<el-dialog
title="提示"
v-model="dialogVisible"
width="30%">
<el-form :model="form" label-width="120px">
<!-- 家居名 -->
<el-form-item label="家居名">
<el-input v-model="form.name" style="width: 80%"></el-input>
</el-form-item>
<!-- 厂商 -->
<el-form-item label="厂商">
<el-input v-model="form.maker" style="width: 80%"></el-input>
</el-form-item>
<!-- 价格 -->
<el-form-item label="价格">
<el-input v-model="form.price" style="width: 80%"></el-input>
</el-form-item>
<!-- 销量 -->
<el-form-item label="销量">
<el-input v-model="form.sales" style="width: 80%"></el-input>
</el-form-item>
<!-- 库存 -->
<el-form-item label="库存">
<el-input v-model="form.stock" style="width: 80%"></el-input>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<!-- 直接写dialogVisible = false,因为只用一句,所以可以直接写在属性上-->
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="save">确定</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script>
//导入组件
import request from "@/utils/request";
import {ElMessage} from "element-plus";
export default {
name: 'HomeView',
components: {
},
//数据池的方法
data(){
return{
//这里默认表单是不显示的
dialogVisible:false,
//对话框的数据
form:{},
tableData:[]
}
},
//钩子函数, created()函数调用后
//数据池和方法池的数据都进行了初始化
created() {
//调用list方法展示数据
this.list()
},
methods:{
//点击新增按钮会调用该方法
add(){
this.dialogVisible=true;
//但调用该方法后,将form对象的信息进行清除,
// 防止下次点击后出现上一次填写的数据
this.form={}
},
//点击确认按钮会调用该方法
save(){
//因为新增和编辑都是调用同一个对话框并且点击确认时是同一个方法,不同的是有无id
//所以根据id来请求后端的方法
if (this.form.id){//如果有id,那么就是修改数据
request.put("/api/update",this.form).then(response=>{
//根据不同的状态码响应不同的信息
if (response.code===200){
ElMessage({
message: '修改成功',
type: 'success',
})
}
else {
ElMessage.error('修改失败')
}
//关闭对话框并更新数据
this.dialogVisible=false
this.list()
})
}
//如果没有id,说明是新增数据
else {
request.post("/api/save",this.form).then(response=>{
// console.log("response",response)
this.dialogVisible=false
//增加家居后调用查询
this.list()
})
}
},
//启用项目时由生命周期函数进行调用
list(){
request.get("/api/list").then(response=>{
this.tableData=response.extend.furnList
})
},
//点击编辑按钮时调用该方法进行回显数据
handle(row){
// console.log(row)
//先将代理对象转成json格式的字符串,再将json格式的字符串转成json对象
// 再将json对象放入数据池的form对象中
this.form = JSON.parse(JSON.stringify(row))
//显示对话框
this.dialogVisible=true
}
}
}
</script>
这部分代码不能放到最后进行统一的调用,是因为异步请求的执行顺序 问题,request.put()
和 request.post()
都是基于 Promise 的异步操作,其回调函数(.then()
)会在请求完成后才执行,此时下面这段代码已经被执行,后面才调用回调函数,此时不能真正的显示最新的数据
this.dialogVisible=false
//增加家居后调用查询
this.list()
使用另一种方式回显数据:(从数据库中查询回显)
在furnService接口中添加方法:
在实现类中实现方法
在Controller层增加相应的业务代码

使用Postman进行测试



七、删除家居信息
在furnService接口中添加方法

在furnServiceImpl对象 中实现接口中的方法:

在Controller层增加相应的业务代码

使用Postman进行测试

在删除按钮绑定确认事件并调用相应的方法实现Ajax请求
<template>
<!--这个主要是用来路由到的页面,默认访问的页面-->
<div>
<!-- 添加按钮和查询框-->
<div style="margin: 10px 5px;display:inline-block">
<el-button type="primary" @click="add">新增</el-button>
<el-button>其他</el-button>
</div>
<div style="display: inline-block">
<el-input v-model="input" style="width: 150px" placeholder="请输入关键字" />
<el-button type="success">提交</el-button>
</div>
<!-- 表格
:data="tableData" 单向渲染数据,从数据池的data数据池的tableData字段里获取数据,
<=> v-bind:data -->
<el-table :data="tableData" stripe style="width: 100%">
<el-table-column sortable prop="id" label="ID" />
<el-table-column prop="name" label="家居名" />
<el-table-column prop="maker" label="厂家" />
<el-table-column prop="price" label="价格" />
<el-table-column prop="sales" label="销量" />
<el-table-column prop="stock" label="库存" />
<el-table-column fixed="right" label="操作" min-width="120">
<template #default="scope">
<!-- 删除按钮 -->
<el-popconfirm title="确认删除吗?" @confirm="handleDel(scope.row.id)">
<template #reference>
<el-button size="small" type="danger">删除</el-button>
</template>
</el-popconfirm>
<!-- 点击按钮时,将 scope.row(当前行数据)作为参数传递给 handle 方法-->
<el-button link type="primary" @click="handle(scope.row)">编辑</el-button>
</template>
</el-table-column>
</el-table>
<!-- 下面是对话框和表单
<el-input v-model="form.name" style="width: 80%"></el-input>
这里的form.name 表示form对象的属性name
必须和后端的对象字段一样因为要将这些字段信息生成json格式打包给后端,
这里的属性名可以动态生成,不需要在数据池里面编写
-->
<el-dialog
title="提示"
v-model="dialogVisible"
width="30%">
<el-form :model="form" label-width="120px">
<!-- 家居名 -->
<el-form-item label="家居名">
<el-input v-model="form.name" style="width: 80%"></el-input>
</el-form-item>
<!-- 厂商 -->
<el-form-item label="厂商">
<el-input v-model="form.maker" style="width: 80%"></el-input>
</el-form-item>
<!-- 价格 -->
<el-form-item label="价格">
<el-input v-model="form.price" style="width: 80%"></el-input>
</el-form-item>
<!-- 销量 -->
<el-form-item label="销量">
<el-input v-model="form.sales" style="width: 80%"></el-input>
</el-form-item>
<!-- 库存 -->
<el-form-item label="库存">
<el-input v-model="form.stock" style="width: 80%"></el-input>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<!-- 直接写dialogVisible = false,因为只用一句,所以可以直接写在属性上-->
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="save">确定</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script>
//导入组件
import request from "@/utils/request";
import {ElMessage} from "element-plus";
export default {
name: 'HomeView',
components: {
},
//数据池的方法
data(){
return{
//这里默认表单是不显示的
dialogVisible:false,
//对话框的数据
form:{},
tableData:[]
}
},
//钩子函数, created()函数调用后
//数据池和方法池的数据都进行了初始化
created() {
//调用list方法展示数据
this.list()
},
methods:{
//点击新增按钮会调用该方法
add(){
this.dialogVisible=true;
//但调用该方法后,将form对象的信息进行清除,
// 防止下次点击后出现上一次填写的数据
this.form={}
},
//点击确认按钮会调用该方法
save(){
//因为新增和编辑都是调用同一个对话框并且点击确认时是同一个方法,不同的是有无id
//所以根据id来请求后端的方法
if (this.form.id){//如果有id,那么就是修改数据
request.put("/api/update",this.form).then(response=>{
//根据不同的状态码响应不同的信息
if (response.code===200){
ElMessage({
message: '修改成功',
type: 'success',
})
}
else {
ElMessage.error('修改失败')
}
//关闭对话框并更新数据
this.dialogVisible=false
this.list()
})
}
//如果没有id,说明是新增数据
else {
request.post("/api/save",this.form).then(response=>{
// console.log("response",response)
this.dialogVisible=false
//增加家居后调用查询
this.list()
})
}
},
//启用项目时由生命周期函数进行调用
list(){
request.get("/api/list").then(response=>{
this.tableData=response.extend.furnList
})
},
//点击编辑按钮时调用该方法进行回显数据
handle(row){
// console.log(row)
//先将代理对象转成json格式的字符串,再将json格式的字符串转成json对象
// 再将json对象放入数据池的form对象中
this.form = JSON.parse(JSON.stringify(row))
//显示对话框
this.dialogVisible=true
},
handleDel(id){
request.delete("/api/delete/"+id).then(response=>{
if (response.code===200){//根据响应的状态码进行弹窗显示
ElMessage({
message: '删除成功',
type: 'success',
})
}else {
ElMessage.error('删除失败')
}
//刷新数据
this.list()
})
}
}
}
</script>
八、分页显示列表
引入Mybatis pagehelper分页插件
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.1</version>
</dependency>
一定要记得刷新Maven!!!

在Mybatis主配置文件中配置分页拦截器
启用合理化(reasonable=true)
• 自动修正页码:
-
若页码 小于 1,自动修正为 第 1 页
-
若页码 超过总页数,自动修正为 最后一页
<plugins> <plugin interceptor="com.github.pagehelper.PageInterceptor"> <property name="reasonable" value="true"/> </plugin> </plugins>
直接在Controller层添加接口

使用Postman进行测试

添加相关导航栏的代码

<div style="margin:10px 0">
<el-pagination
@size-change="handlePageSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="[5, 10]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
>
</el-pagination>
</div>

<template>
<!--这个主要是用来路由到的页面,默认访问的页面-->
<div>
<!-- 添加按钮和查询框-->
<div style="margin: 10px 5px;display:inline-block">
<el-button type="primary" @click="add">新增</el-button>
<el-button>其他</el-button>
</div>
<div style="display: inline-block">
<el-input v-model="input" style="width: 150px" placeholder="请输入关键字" />
<el-button type="success">提交</el-button>
</div>
<!-- 表格
:data="tableData" 单向渲染数据,从数据池的data数据池的tableData字段里获取数据,
<=> v-bind:data -->
<el-table :data="tableData" stripe style="width: 100%">
<el-table-column sortable prop="id" label="ID" />
<el-table-column prop="name" label="家居名" />
<el-table-column prop="maker" label="厂家" />
<el-table-column prop="price" label="价格" />
<el-table-column prop="sales" label="销量" />
<el-table-column prop="stock" label="库存" />
<el-table-column fixed="right" label="操作" min-width="120">
<template #default="scope">
<!-- 删除按钮 -->
<el-popconfirm title="确认删除吗?" @confirm="handleDel(scope.row.id)">
<template #reference>
<el-button size="small" type="danger">删除</el-button>
</template>
</el-popconfirm>
<!-- 点击按钮时,将 scope.row(当前行数据)作为参数传递给 handle 方法-->
<el-button link type="primary" @click="handle(scope.row)">编辑</el-button>
<!-- 方式2:从数据查询数据-->
<!-- <el-button link type="primary" @click="edit(scope.row.id)">编辑</el-button>-->
</template>
</el-table-column>
</el-table>
<!-- 下面是对话框和表单
<el-input v-model="form.name" style="width: 80%"></el-input>
这里的form.name 表示form对象的属性name
必须和后端的对象字段一样因为要将这些字段信息生成json格式打包给后端,
这里的属性名可以动态生成,不需要在数据池里面编写
-->
<el-dialog
title="提示"
v-model="dialogVisible"
width="30%">
<el-form :model="form" label-width="120px">
<!-- 家居名 -->
<el-form-item label="家居名">
<el-input v-model="form.name" style="width: 80%"></el-input>
</el-form-item>
<!-- 厂商 -->
<el-form-item label="厂商">
<el-input v-model="form.maker" style="width: 80%"></el-input>
</el-form-item>
<!-- 价格 -->
<el-form-item label="价格">
<el-input v-model="form.price" style="width: 80%"></el-input>
</el-form-item>
<!-- 销量 -->
<el-form-item label="销量">
<el-input v-model="form.sales" style="width: 80%"></el-input>
</el-form-item>
<!-- 库存 -->
<el-form-item label="库存">
<el-input v-model="form.stock" style="width: 80%"></el-input>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<!-- 直接写dialogVisible = false,因为只用一句,所以可以直接写在属性上-->
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="save">确定</el-button>
</span>
</template>
</el-dialog>
<!-- 分页导航栏
@size-change="handlePageSizeChange"每页显示的数据改变后就会调用handlePageSizeChange方法,
这里会将pageSize传给handlePageSizeChange方法
@current-change="handleCurrentChange"点击其他页会调用handleCurrentChange方法,
会将pageNum(显示第几页)传给该方法
-->
<div style="margin:10px 0">
<el-pagination
@size-change="handlePageSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="[5,10]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
>
</el-pagination>
</div>
</div>
</template>
<script>
//导入组件
import request from "@/utils/request";
import {ElMessage} from "element-plus";
export default {
name: 'HomeView',
components: {
},
//数据池的方法
data(){
return{
//分页数据绑定
currentPage:1,
pageSize:5,
total:10,
//这里默认表单是不显示的
dialogVisible:false,
//对话框的数据
form:{},
tableData:[]
}
},
//钩子函数, created()函数调用后
//数据池和方法池的数据都进行了初始化
created() {
//调用list方法展示数据
this.list()
},
methods:{
//点击新增按钮会调用该方法
add(){
this.dialogVisible=true;
//但调用该方法后,将form对象的信息进行清除,
// 防止下次点击后出现上一次填写的数据
this.form={}
},
//点击确认按钮会调用该方法
save(){
//因为新增和编辑都是调用同一个对话框并且点击确认时是同一个方法,不同的是有无id
//所以根据id来请求后端的方法
if (this.form.id){//如果有id,那么就是修改数据
request.put("/api/update",this.form).then(response=>{
//根据不同的状态码响应不同的信息
if (response.code===200){
ElMessage({
message: '修改成功',
type: 'success',
})
}
else {
ElMessage.error('修改失败')
}
//关闭对话框并更新数据
this.dialogVisible=false
this.list()
})
}
//如果没有id,说明是新增数据
else {
request.post("/api/save",this.form).then(response=>{
// console.log("response",response)
this.dialogVisible=false
//增加家居后调用查询
this.list()
})
}
},
//启用项目时由生命周期函数进行调用
list(){
// request.get("/api/list").then(response=>{
// this.tableData=response.extend.furnList
// })
//分页查询
request.get("/api/page",{
params:{
//传参给后端
pageNum:this.currentPage,//第几页
pageSize:this.pageSize//每页的记录数
}
}).then(response=>{
//将数据绑定到数据池的tableData对象
this.tableData=response.extend.pageInfo.list
this.total=response.extend.pageInfo.total
})
},
//点击编辑按钮时调用该方法进行回显数据
handle(row){
// console.log(row)
//先将代理对象转成json格式的字符串,再将json格式的字符串转成json对象
// 再将json对象放入数据池的form对象中
this.form = JSON.parse(JSON.stringify(row))
//显示对话框
this.dialogVisible=true
},
//删除数据时调用该方法
handleDel(id){
request.delete("/api/delete/"+id).then(response=>{
if (response.code===200){//根据响应的状态码进行弹窗显示
ElMessage({
message: '删除成功',
type: 'success',
})
}else {
ElMessage.error('删除失败')
}
//刷新数据
this.list()
})
},
handleCurrentChange(pageNum){
this.currentPage=pageNum
this.list()
},
handlePageSizeChange(pageSize){
this.pageSize=pageSize
this.list()
}
//方式2:从数据库中查询数据
// edit(id){
// request.get("/api/find/"+id).then(response=>{
// // console.log(response)
// this.form=response.extend.furn
// this.dialogVisible=true
// })
// }
}
}
</script>

九、带条件查询分页显示列表
在furnService接口中添加方法

在furnServiceImpl对象 中实现接口中的方法:

在Controller层增加相应的业务代码
在分页查询方法的基础上进行修改

使用Postman进行测试

完善前端的查询功能




十、添加家居表单完善前端校验
在数据池中添加校验规则

<template>
<!--这个主要是用来路由到的页面,默认访问的页面-->
<div>
<!-- 添加按钮和查询框-->
<div style="margin: 10px 5px;display:inline-block">
<el-button type="primary" @click="add">新增</el-button>
<el-button>其他</el-button>
</div>
<div style="display: inline-block">
<el-input v-model="input" style="width: 150px" placeholder="请输入关键字" />
<el-button type="success" @click="list">查询</el-button>
</div>
<!-- 表格
:data="tableData" 单向渲染数据,从数据池的data数据池的tableData字段里获取数据,
<=> v-bind:data -->
<el-table :data="tableData" stripe style="width: 100%">
<el-table-column sortable prop="id" label="ID" />
<el-table-column prop="name" label="家居名" />
<el-table-column prop="maker" label="厂家" />
<el-table-column prop="price" label="价格" />
<el-table-column prop="sales" label="销量" />
<el-table-column prop="stock" label="库存" />
<el-table-column fixed="right" label="操作" min-width="120">
<template #default="scope">
<!-- 删除按钮 -->
<el-popconfirm title="确认删除吗?" @confirm="handleDel(scope.row.id)">
<template #reference>
<el-button size="small" type="danger">删除</el-button>
</template>
</el-popconfirm>
<!-- 点击按钮时,将 scope.row(当前行数据)作为参数传递给 handle 方法-->
<el-button link type="primary" @click="handle(scope.row)">编辑</el-button>
<!-- 方式2:从数据查询数据-->
<!-- <el-button link type="primary" @click="edit(scope.row.id)">编辑</el-button>-->
</template>
</el-table-column>
</el-table>
<!-- 下面是对话框和表单
<el-input v-model="form.name" style="width: 80%"></el-input>
这里的form.name 表示form对象的属性name
必须和后端的对象字段一样因为要将这些字段信息生成json格式打包给后端,
这里的属性名可以动态生成,不需要在数据池里面编写
-->
<el-dialog
title="提示"
v-model="dialogVisible"
width="30%">
<el-form :model="form" :rules="rules" ref="form" label-width="120px">
<!-- 家居名 -->
<el-form-item label="家居名" prop="name">
<el-input v-model="form.name" style="width: 80%"></el-input>
</el-form-item>
<!-- 厂商 -->
<el-form-item label="厂商" prop="maker">
<el-input v-model="form.maker" style="width: 80%"></el-input>
</el-form-item>
<!-- 价格 -->
<el-form-item label="价格" prop="price">
<el-input v-model="form.price" style="width: 80%"></el-input>
</el-form-item>
<!-- 销量 -->
<el-form-item label="销量" prop="sales">
<el-input v-model="form.sales" style="width: 80%"></el-input>
</el-form-item>
<!-- 库存 -->
<el-form-item label="库存" prop="stock">
<el-input v-model="form.stock" style="width: 80%"></el-input>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<!-- 直接写dialogVisible = false,因为只用一句,所以可以直接写在属性上-->
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="save">确定</el-button>
</span>
</template>
</el-dialog>
<!-- 分页导航栏
@size-change="handlePageSizeChange"每页显示的数据改变后就会调用handlePageSizeChange方法,
这里会将pageSize传给handlePageSizeChange方法
@current-change="handleCurrentChange"点击其他页会调用handleCurrentChange方法,
会将pageNum(显示第几页)传给该方法
-->
<div style="margin:10px 0">
<el-pagination
@size-change="handlePageSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="[5,10]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
>
</el-pagination>
</div>
</div>
</template>
<script>
//导入组件
import request from "@/utils/request";
import {ElMessage} from "element-plus";
export default {
name: 'HomeView',
components: {
},
//数据池的方法
data(){
return{
input:'',
//分页数据绑定
currentPage:1,
pageSize:5,
total:10,
//这里默认表单是不显示的
dialogVisible:false,
//对话框的数据
form:{},
tableData:[],
//校验规则
rules:{
name:[{
required:true,message:"请输入家居名",trigger:"blur"
}],
maker:[{
required:true,message:"请输入厂家名",trigger:"blur"
}],
price:[{
required:true,message:"请输入价格",trigger:"blur"
},{
pattern:/^([1-9]\d*|0)(\.\d+)?$/ ,message: "请输入数字",trigger:"blur"
}
],
sales:[{
required:true,message:"请输入销量",trigger:"blur"
},{
pattern:/^([1-9]\d*|0)$/ ,message: "请输入数字",trigger:"blur"
}],
stock:[{
required:true,message:"请输入库存",trigger:"blur"
},{
pattern:/^([1-9]\d*|0)$/ ,message: "请输入数字",trigger:"blur"
}]
}
}
},
//钩子函数, created()函数调用后
//数据池和方法池的数据都进行了初始化
created() {
//调用list方法展示数据
this.list()
},
methods:{
//点击新增按钮会调用该方法
add(){
this.dialogVisible=true;
//但调用该方法后,将form对象的信息进行清除,
// 防止下次点击后出现上一次填写的数据
this.form={}
//清空上一次的校验
this.$nextTick(() => {
this.$refs['form'].resetFields();
});
},
//点击确认按钮会调用该方法
save(){
//因为新增和编辑都是调用同一个对话框并且点击确认时是同一个方法,不同的是有无id
//所以根据id来请求后端的方法
if (this.form.id){//如果有id,那么就是修改数据
request.put("/api/update",this.form).then(response=>{
//根据不同的状态码响应不同的信息
if (response.code===200){
ElMessage({
message: '修改成功',
type: 'success',
})
}
else {
ElMessage.error('修改失败')
}
//关闭对话框并更新数据
this.dialogVisible=false
this.list()
})
}
//如果没有id,说明是新增数据
else {
//添加校验规则
this.$refs['form'].validate((valid)=>{
if (valid){//校验通过后才发送请求到后端
request.post("/api/save",this.form).then(response=>{
// console.log("response",response)
this.dialogVisible=false
//增加家居后调用查询
this.list()
//添加成功的窗口
ElMessage({
message: '添加成功',
type: 'success',
})
})
}
else {//验证没有通过
ElMessage.error('添加失败')
return false
}
})
}
},
//启用项目时由生命周期函数进行调用
list(){
// request.get("/api/list").then(response=>{
// this.tableData=response.extend.furnList
// })
//分页查询
request.get("/api/selectByName",{
params:{
//传参给后端
pageNum:this.currentPage,//第几页
pageSize:this.pageSize,//每页的记录数
name:this.input
}
}).then(response=>{
//将数据绑定到数据池的tableData对象
this.tableData=response.extend.pageInfo.list
this.total=response.extend.pageInfo.total
})
},
//点击编辑按钮时调用该方法进行回显数据
handle(row){
// console.log(row)
//先将代理对象转成json格式的字符串,再将json格式的字符串转成json对象
// 再将json对象放入数据池的form对象中
this.form = JSON.parse(JSON.stringify(row))
//显示对话框
this.dialogVisible=true
},
//删除数据时调用该方法
handleDel(id){
request.delete("/api/delete/"+id).then(response=>{
if (response.code===200){//根据响应的状态码进行弹窗显示
ElMessage({
message: '删除成功',
type: 'success',
})
}else {
ElMessage.error('删除失败')
}
//刷新数据
this.list()
})
},
handleCurrentChange(pageNum){
this.currentPage=pageNum
this.list()
},
handlePageSizeChange(pageSize){
this.pageSize=pageSize
this.list()
}
//方式2:从数据库中查询数据
// edit(id){
// request.get("/api/find/"+id).then(response=>{
// // console.log(response)
// this.form=response.extend.furn
// this.dialogVisible=true
// })
// }
}
}
</script>


十一、添加家居表单完善后端校验
引入JSR303数据校验依赖
<!-- 引入jsr303数据校验支持-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.1.0.Final</version>
</dependency>
一定要刷新Maven!!!

对Furn对象的字段进行校验

对保存方法进行修改

使用Postman进行测试

这里就不在前端进行展示了,如果要展示,就是在数据池里面添加一个属性然后将数据存储到数据池里面,用插值列表框显示
更多功能持续完善....