深浅模式
SSM
字数: 0 字 时长: 0 分钟
第 1 章 项目介绍
SSM 整合项目界面:

技术栈:
说明:前后端分离开发,前端框架 Vue + 后端框架 SSM
(1)前端框架 Vue
(2)后台框架 SSM(SpringMVC + Spring + MyBatis)
(3)数据库 MySQL
(4)项目的依赖管理 Maven
(5)分页 pagehelper
(6)逆向工程 MyBatis Generator
(7)其它.....
第 2 章 项目基础环境搭建
2.1 创建项目
(1)创建 Maven 项目,注意配置 Maven 的仓库镜像
(2)手动增加目录

(3)引入项目依赖的 jar 包,先引入基本的包
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>com.hspedu</groupId>
<artifactId>furn-ssm</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>furn-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,也会引入/导入 spring 的库-->
<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 aspects 切面编程需要的库-->
<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 驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
</dependencies>
<build>
<finalName>furn-ssm</finalName>
</build>
</project>(4)配置 Tomcat
2.2 项目全局配置 web.xml
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>
<!--配置启动 Spring 的容器:主要配置和业务逻辑有关的,比如数据源,事务控制等-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!--
ContextLoaderListener 监听器作用是启动 Web 容器时,自动装配 ApplicationContext 的配置信息
它实现了 ServletContextListener 接口,在 web.xml 配置该监听器,启动容器时,会默认执行它实现的方法
-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--配置 Spring 提供的过滤器,解决中文乱码问题-->
<filter>
<filter-name>CharacterEncodingFilter</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>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--配置前端控制器/中央控制器/分发控制器
用户的请求都会经过它的处理
因为没有指定 SpringMVC 的配置文件,那么就会默认按照 servlet-name-servlet.xml 来获取
-->
<servlet>
<servlet-name>springDispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--配置属性
配置属性 contextConfigLocation,指定 DispatcherServlet 去操作的 Spring 配置文件
-->
<!--<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springDispatcherServlet-servlet.xml</param-value>
</init-param>-->
<!--在 web 项目启动时,就自动的加载 DispatcherServlet-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springDispatcherServlet</servlet-name>
<!--
这里把 urlPattern 配置成 /,表示用户的请求都要经过 DispatcherServlet
这样配置也符合 rest 风格的 url 请求
-->
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--配置 HiddenHttpMethodFilter
(1) 作用是把以 post 方式提交的 delete 和 put 请求进行转换
(2) 配置 url-pattern 是 /* 表示请求都经过 hiddenHttpMethodFilter 过滤
-->
<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>
</web-app>2.3 SpringMVC 配置
(1)创建 SpringMVC 的配置文件 dispatche-servlet.xml,主要包含网站跳转逻辑的控制
springDispatcherServlet-servlet.xml
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:aop="http://www.springframework.org/schema/aop"
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/aop https://www.springframework.org/schema/aop/spring-aop.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">
<!--
扫描 com.hspedu 包
use-default-filters="false" 禁用默认过滤规则
context:include-filter 配置说明只是扫描控制器
-->
<context:component-scan base-package="com.hspedu.furn">
<!--配置 SpringMVC 只是扫描 Controller-->
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--配置视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--配置属性 suffix(前缀) 和 prefix(后缀)-->
<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>(2)测试,启动 Tomcat,浏览器输入 http://localhost:8081/ssm/hi
java
@Controller
public class TestController {
@RequestMapping("/hi")
public String hi() {
System.out.println("TestController-hi");
return "hi";
}
}2.4 配置 Spring 和 MyBatis,并完成整合
(1)创建 spring 的配置文件 applicationContext.xml:主要配置和业务逻辑有关的,比如数据源、事务控制等
(2)配置扫描 com.hspedu 包,但是不扫描控制器,控制器由 SpringMVC 管理
(3)配置连接 MySQL 的信息
(4)配置数据源
(5)配置 spring 与 MyBatis 的整合
(6)在类路径 resources 下创建 mybatis-config.xml
(7)在类路径下创建 mapper 目录,存放 mapper 的 xml 文件
(8)配置将 MyBatis 接口实现加入到 ioc 容器,在 applicationContext.xml 配置
(9)配置事务控制,在 applicationContext.xml 配置
(10)配置开启基于注解的事务(这里使用 XML 配置 + 切入表达式),并指定切入点
2.5 创建表,使用逆向工程生成 Bean、XxxMapper 和 XxxMapper.xml
(1)创建表
sql
-- 创建 furn_ssm
drop database if exists furn_ssm;
create database furn_ssm;
use furn_ssm;
-- 创建家居表
create table furn(
id int(11) primary key auto_increment, ## id
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, '');
insert into furn(id, name, maker, price, sales, stock, img_path) values (null, '简约风格小椅子', '熊猫家居', 180, 666, 7, '');
insert into furn(id, name, maker, price, sales, stock, img_path) values (null, '典雅风格小台灯', '蚂蚁家居', 180, 666, 7, '');
insert into furn(id, name, maker, price, sales, stock, img_path) values (null, '温馨风格盆景架', '蚂蚁家居', 180, 666, 7, '');
select * from furn;(2)使用 MyBatis Generator 逆向工程生成 bean mapper 接口和 mapper.xml,当然也可以自己写
修改 mybatis-config.xml 增加 typeAliases 配置(类别名配置,这样就可以简写类名)
(3)引入 MyBatis Generator 包,在 pom.xml 配置
(4)创建 mbg.xml(一般就在项目目录下),并参考文档进行配置
xml
<?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>
<classPathEntry location="/Program Files/IBM/SQLLIB/java/db2java.zip" />
<context id="DB2Tables" targetRuntime="MyBatis3">
<!--生成没有注释的 bean-->
<commentGenerator>
<property name="suppressAllComments" value="true"/>
</commentGenerator>
<!--配置数据库连接信息-->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://127.0.0.1:3306/furn_ssm"
userId="root"
password="123456">
</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 映射文件生成的位置,就是 XxxMapper.xml 文件-->
<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>(5)创建文件 MBGTest.java,生成相关 bean、mapper 接口和 mapper.xml
java
public class MBGTest {
@Test
public void generator() throws XMLParserException, IOException, InvalidConfigurationException, SQLException, InterruptedException {
List<String> warnings = new ArrayList<String>();
boolean overwrite = true;
//这里要指定自己配置的 mbg.xml
//如果这样访问,需要将文件放在项目目录下
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);
}
}(6)使用 Junit 测试 Spring 和 MyBatis 是否整合成功,能通过 MyBatis 增删改查 furn 到数据库
java
public class FurnMapperTest {
@Test
public void insertSelective() {
//1. 获取到容器
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
//2. 获取 FurnMapper
FurnMapper furnMapper = ioc.getBean(FurnMapper.class);
//System.out.println("furnMapper = " + furnMapper.getClass());
//3. 添加数据
Furn furn = new Furn(null, "北欧风格沙发~", "顺平家居", new BigDecimal(180), 666, 7, "assets/images/product-image/1.jpg");
int affected = furnMapper.insertSelective(furn);
System.out.println("affected = " + affected);
System.out.println("操作成功");
}
@Test
public void deleteByPrimaryKey() {
//1. 获取到容器
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
//2. 获取 FurnMapper
FurnMapper furnMapper = ioc.getBean(FurnMapper.class);
int affected = furnMapper.deleteByPrimaryKey(6);
System.out.println("affected = " + affected);
System.out.println("操作成功");
}
@Test
public void updateByPrimaryKey() {
//1. 获取到容器
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
//2. 获取 FurnMapper
FurnMapper furnMapper = ioc.getBean(FurnMapper.class);
Furn furn = new Furn();
furn.setId(5);
furn.setName("止束风格的家居-小椅子");
//updateByPrimaryKey 会修改所有的字段,如果没有设置字段对应的属性值,那么默认是 null
//int affected = furnMapper.updateByPrimaryKey(furn);
int affected = furnMapper.updateByPrimaryKeySelective(furn);
System.out.println("affected = " + affected);
System.out.println("操作成功");
}
@Test
public void selectByPrimaryKey() {
//1. 获取到容器
ApplicationContext ioc = new ClassPathXmlApplicationContext("applicationContext.xml");
//2. 获取 FurnMapper
FurnMapper furnMapper = ioc.getBean(FurnMapper.class);
Furn furn = furnMapper.selectByPrimaryKey(1);
System.out.println("furn = " + furn);
System.out.println("操作成功");
}
}2.6 注意事项和细节说明
(1)insertSelective 和 insert 的区别
insertSelective:选择性保存数据
比如 User 里面有三个字段:id,name,age,password 但是只设置了一个字段:
java
User u = new User();
u.setName("张三");
insertSelective(u);使用 insertSelective 执行对应的 sql 语句的时候,只插入对应的 name 字段(主键是自动添加的,默认插入为空),相当于 insert into tb_user (id, name) value (null, "张三");
而使用 insert 则是不论设置多少个字段,统一都要添加一遍,不论你设置几个字段,即使是一个,相当于insert into tb_user (id, name, age, password) value (null, "张三", null, null);
第 3 章 实现功能 1 - 搭建 Vue 前端工程
3.1 需求分析
(1)使用 Vue3 的脚手架 Vue-cli 工具,创建 ssm 的前端项目基础开发环境
3.2 代码实现
3.2.1 搭建 Vue 前段工程
3.2.1.1 下载 Nodejs,安装 14.17.3 版本
Node.js安装与配置(详细步骤)_nodejs安装及环境配置-CSDN博客
Vue.js安装与创建默认项目(详细步骤)_nodejs安装及环境配置-CSDN博客
3.2.1.2 Vue3 项目目录结构梳理
(1)Vue3 项目的路由机制
1)index.html
html
<!DOCTYPE html>
<html lang="">
<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">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
</noscript>
<!--
index.html 是项目首页面
<div id="app"></div> 会挂载 App.vue 编译后的内容
当 App.vue 编译后,内容会替换/挂载到 <div id="app"></div>
-->
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>2)App.vue
vue
<template>
<!--
App.vue 页面可以用于布局界面
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
表示路由到 "/" 这个地方或者是这个 "/about" 地方,第一次打开时默认路由到 "/",即 http://localhost:8080/
<router-view/> 就是路由指令,会把路由到的页面内容,展示/替换到 <router-view/>
那么 <router-view/> 具体路由到什么地方,取决于 Url,比如 url 是 http://localhost:8080/,那么就路由到 /
比如 url 地址 http://localhost:8080/about 那么路由的 path 就是 /about
此时去看 index.js 关于 "/" 的配置
-->
<nav>
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</nav>
<router-view/>
</template>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
nav {
padding: 30px;
}
nav a {
font-weight: bold;
color: #2c3e50;
}
nav a.router-link-exact-active {
color: #42b983;
}
</style>3)index.js
js
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
// index.js 是用于配置路由的
//此时因为 App.vue 路由的路径是 / ,所以到 index.js 看看有没有配置
// path: '/', 是路径,当访问 http://localhost:8080/,即路由的路径是 /,就路由到 component: HomeView 这个组件
// 这样就会把 HomeView 组件的内容返回给 <router-view/>
// HomeView 组件对应的文件就是 import HomeView from '../views/HomeView.vue'
const routes = [
{
path: '/',
name: 'home',
component: HomeView
},
{
path: '/about',
name: 'about',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/AboutView.vue')
}
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
export default router4)HomeView.vue
vue
<template>
<div class="home">
<img alt="Vue logo" src="../assets/logo.png">
<!--
在 HomeView.vue 文件中,引入/使用 HelloWorld 组件
因此这里就会显示 HelloWorld 组件的内容
HelloWorld 组件在 import HelloWorld from '@/components/HelloWorld.vue'
其中 @ 就是 /src
<HelloWorld msg="Welcome to Your Vue.js App"/> 其中 msg 表示给 HelloWorld 组件设置一个值
-->
<HelloWorld msg="Welcome to Your Vue.js App"/>
</div>
</template>
<script>
// @ is an alias to /src
//导入组件,导入后就可以使用
import HelloWorld from '@/components/HelloWorld.vue'
//导出组件
export default {
name: 'HomeView',
components: {
HelloWorld
}
}
</script>5)HelloWorld.vue
vue
<template>
<div class="hello">
<!--这里的 msg 就是上一个页面 HomeView.vue 带过来的-->
<h1>{{ msg }}</h1>
<p>
For a guide and recipes on how to configure / customize this project,<br>
check out the
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
</p>
<h3>Installed CLI Plugins</h3>
<ul>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-router" target="_blank" rel="noopener">router</a></li>
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-vuex" target="_blank" rel="noopener">vuex</a></li>
</ul>
<h3>Essential Links</h3>
<ul>
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
</ul>
<h3>Ecosystem</h3>
<ul>
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
</ul>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>(2)assets 目录和 components 目录说明

1)assets 目录用于存放项目的静态资源,比如图片、css、js
2)components 目录用于存放组件,在 vue 中组件可以表示页面
(3)router/index.js 目录说明
router/index.js 目录是用于配置路由的
(4)store/index.js 是用于存放数据的
(5)package.json 说明前端项目包依赖关系,类似 Maven 的 pom.xml 文件
(6)main.js 用于引入资源(css、组件等),同时也是创建 App、挂载 #app、引入 ./router 和 ./store 等资源的所在
3.3 配置 Vue 服务端口
在 vue.config.js 文件中加入这段代码
js
module.exports = {
devServer: {
port: 10000 //启动端口
}
}3.4 Element Plus 和 Element UI
(1)Element Plus 是 Element 对 Vue3.0 的升级适配
3.5 安装 element-plus 插件
第 4 章 实现功能 02 - 创建项目基础界面
4.1 需求分析

4.2 思路分析
(1)使用 Vue3 + ElementPlus 完成
第 5 章 实现功能 04 - 添加家居信息
5.1 需求分析

5.2 思路分析
(1)完成后台代码从 dao -> service -> controller,并对每层代码进行测试,到 Controller 这一层使用 Postman 发送 Http post 请求完成测试
(2)完成前端代码,使用 axios 发送 ajax 请求以 json 格式的数据给后台,实现添加家居信息
5.3 代码实现
(1)Service 层
(2)Entity
(3)返回 Json 数据通用的 Msg 对象
(4)Controller
(5)完成测试
(6)显示添加表单
(7)创建 Axios Request 对象
request.js
js
//引入 axios
import axios from "axios";
//通过 axios 创建对象 - request 对象,用于发送请求到后端
const request = axios.create({
timeout: 5000
})
//request 拦截器的处理
//1. 可以对请求做统一的处理
//2. 比如统一的加入 token,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(8)跨域

vue.config.js
js
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true
})
module.exports = {
devServer: {
port: 8081, //启动端口
proxy: { //设置代理
'/api': { //设置拦截器,拦截器格式
target: 'http://localhost:8080/ssm', //代理的目标地址,比如请求地址是 /api/save 就会代理到 http://localhost:8080/ssm/save
changeOrigin: true, //是否设置同源
pathRewrite: { //路径重写
'/api': '' //选择忽略拦截器里面的单词
}
}
}
}
}5.4 注意事项和细节
第 6 章 实现功能 05 - 显示家居信息
6.1 需求分析
6.2 思路分析
(1)完成后台代码从 dao -> service -> controller,并对每层代码进行测试
(2)完成前台代码,使用 axios 发送 http 请求,返回所有家居数据,将数据绑定显示
6.3 代码实现
(1)Service
(2)Controller
(3)前端页面
(4)拦截 Response 并处理
request.js
js
//引入 axios
import axios from "axios";
//通过 axios 创建对象 - request 对象,用于发送请求到后端
const request = axios.create({
timeout: 5000
})
//request 拦截器的处理
//1. 可以对请求做统一的处理
//2. 比如统一的加入 token,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
//response 拦截器
//可以在调用接口响应后,统一的处理返回结果
request.interceptors.response.use(
response => {
let res = response.data
//如果返回的是文件,就返回
if (response.config.responseType === 'blob') {
return res
}
//如果是 String,就转成 json 对象
if (typeof res === 'string') {
//如果 res 不为 null,就进行转换成 json 对象
res = res ? JSON.parse(res) : res
}
return res;
},
error => {
console.log("err", error)
return Promise.reject(error);
}
)第 7 章 实现功能 06 - 修改家居信息
7.1 需求分析

7.2 思路分析
(1)完成后台代码从 dao -> service -> controller,并对每层代码进行测试
(2)完成前台代码,可以回显家居信息,再使用 axios 发送 http 请求,更新数据,将数据绑定显示
7.3 代码实现
(1)Service
(2)Controller
(3)回显表单
(4)确定修改
(5)异步机制
7.4 注意事项和细节
第 8 章 实现功能 07 - 删除家居信息
8.1 需求分析

8.2 思路分析
(1)完成后台代码从 dao -> service -> controller,并对每层代码进行测试
(2)完成前台代码,使用 axios 发送 http 请求,删除数据,将数据绑定显示
8.3 代码实现
(1)Service
(2)Controller
(3)确定删除
8.4 作业
需求:将修改家居信息的功能中的回显家居表单数据改成从后端 DB 获取
第 9 章 实现功能 08 - 分页显示列表
9.1 需求分析

9.2 思路分析
(1)后台使用 MyBatis PageHelper 插件完成分页查询
(2)修改 FurnController,增加处理分页显示代码
(3)完成前台代码,加入分页导航,并将分页请求和后台接口结合
9.3 代码实现
(1)修改 pom.xml 加入分页插件
xml
<!--引入 mybatis pageHelper 分页插件-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.1</version>
</dependency>(2)修改 mybatis-config.xml 配置分页拦截器
xml
<!--plugins 标签需要放在 typeAliases 标签后,是 doctype 约束的-->
<!--配置分页拦截器-->
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!--
配置分页合理化
如果用户请求的 pageNum > pages,就显示查询最后一页
如果用户请求的 pageNum < 0,就显示查询第一页
-->
<property name="reasonable" value="true"/>
</plugin>
</plugins>(3)FurnController.java
java
/**
* @Author: 止束
* @Params: [pageNum, pageSize]
* @Return com.hspedu.furn.bean.Msg
* @Description: pageNum 表示要显示第几页,pageSize 表示每页要显示几条记录
*/
@ResponseBody
@RequestMapping("/furnsByPage")
public Msg listFurnByPage(@RequestParam(defaultValue = "1") Integer pageNum, @RequestParam(defaultValue = "5") Integer pageSize) {
//设置分页参数
PageHelper.startPage(pageNum, pageSize);
/*
* 这里调用 findAll 是完成查询,底层会进行物理分页,而不是逻辑分页
* 这里会根据分页参数来计算 limit ?, ?, 在发出 SQL 语句时,会带 limit
* */
List<Furn> furnList = furnService.findAll();
/*
* 将分页查询的结果封装到 PageInfo
* PageInfo 对象包含了分页的各个信息
* */
PageInfo pageInfo = new PageInfo(furnList, pageSize);
//将 pageInfo 封装到 Msg 对象,返回
return Msg.success().add("pageInfo", pageInfo);
}(4)修改前端代码,完成分页导航显示、分页请求
9.4 注意事项和细节


第 10 章 实现功能 09 - 带条件查询分页显示列表

(1)完成后台代码从 dao -> service -> controller,并对每层代码进行测试
(2)完成前台代码,使用 axios 发送 http 请求,完成带条件查询分页显示
Service 层
接口 FurnService.java
java
//根据家居名称进行查询
public List<Furn> findByCondition(String name);实现类
java
@Override
public List<Furn> findByCondition(String name) {
FurnExample furnExample = new FurnExample();
//通过 Criteria 对象可以设置查询条件
FurnExample.Criteria criteria = furnExample.createCriteria();
//判断 name 是有具体的内容才做处理
if (StringUtils.hasText(name)) {
criteria.andNameLike("%" + name + "%"); //模糊查询
}
//如果 name 没有传值,依然是查询所有的记录
return furnMapper.selectByExample(furnExample);
}测试
java
@Test
public void findByCondition() {
List<Furn> furns = furnService.findByCondition("风格");
for (Furn furn : furns) {
System.out.println("furn = " + furn);
}
}Controller 层
java
//根据家居名进行分页查询 - 有条件
@ResponseBody
@RequestMapping("/furnsByConditionPage")
public Msg listFurnByConditionPage(@RequestParam(defaultValue = "1") Integer pageNum, @RequestParam(defaultValue = "5") Integer pageSize,
@RequestParam(defaultValue = "") String search) {
//设置分页参数
PageHelper.startPage(pageNum, pageSize);
/*
* 这里会根据分页参数来计算 limit ?, ?, 在发出 SQL 语句时,会带 limit
* */
List<Furn> furnList = furnService.findByCondition(search);
/*
* 将分页查询的结果封装到 PageInfo
* PageInfo 对象包含了分页的各个信息
* */
PageInfo pageInfo = new PageInfo(furnList, pageSize);
//将 pageInfo 封装到 Msg 对象,返回
return Msg.success().add("pageInfo", pageInfo);
}第 11 章 实现功能 10 - 添加家居表单前端校验
11.1 需求分析

11.2 思路分析
完成前台代码,使用 ElementPlus 的表单 rules 验证即可
11.3 代码实现
HomeView.vue
vue
<template>
<div>
<!--增加按钮和搜索框-->
<div style="margin: 10px 5px">
<el-button type="primary" @click="add">新增</el-button>
<el-button>其它</el-button>
</div>
<div style="margin: 10px 5px">
<el-input v-model="search" style="width: 30%" placeholder="请输入关键字" />
<el-button style="margin-left: 10px" type="primary" @click="list">检索</el-button>
</div>
<el-table :data="tableData" stripe style="width: 90%">
<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">
<!--这里可以通过 handleEdit(scope.row) 将当前行的数据传递给 handleEdit-->
<el-button type="text" @click="handleEdit(scope.row)">
编辑
</el-button>
<!--
如果点击的是确定,就会触发 handleDel
如果点击的是取消,就不会触发 handleDel
-->
<el-popconfirm title="确认删除吗?" @confirm="handleDel(scope.row.id)">
<template #reference>
<el-button size="small" type="danger">删除</el-button>
</template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
<!--
添加家居的弹窗
说明:
el-dialog: v-model="dialogVisible" 表示对话框,和 dialogVisible 变量双向绑定,控制是否显示对话框
el-form : model="form" 表示表单,数据和 form 数据变量双向绑定
el-input v-model="form.name" 表示表单的 input 控件,名字为 name 需要和后台 JavaBean 属性一致
在前端中,对象的属性是可以动态生成的,即如果有 v-model="form.name" 那么提交时就会以 name=Xxx 进行提交
-->
<el-dialog
v-model="dialogVisible"
title="提示"
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>
<div class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="save">确定</el-button>
</div>
</template>
</el-dialog>
<!--添加分页导航-->
<div style="margin: 10px 0">
<el-pagination
@size-change="handlePageSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="[2,5,10]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination>
</div>
</div>
</template>
<script>
//导入 request 对象
import request from "@/utils/request";
import {handleCurrentChange} from "element-plus/es/components/tree/src/model/util";
//导出组件
export default {
name: 'HomeView',
components: {
},
data() {
return {
//增加分页相应的数据绑定
currentPage: 1, //当前页
pageSize: 5, //每页显示记录数
total: 10, //共有多少记录
search: '',
dialogVisible: false,
form:{},
tableData: [
],
//定义添加表单的校验规则
rules: {
name: [
//这里可以写多个针对 name 属性的校验规则
{required:true, message: "请输入家居名", trigger:"blur"}
],
maker: [
//这里可以写多个针对 name 属性的校验规则
{required:true, message: "请输入制造商名", trigger:"blur"}
],
price: [
//这里可以写多个针对 name 属性的校验规则
{required:true, message: "请输入制造商名", trigger:"blur"},
//使用正则表达式对输入的数据进行校验
{pattern: /^([1-9]\d*|0)(\.\d+)?$/, message: "请输入数字", trigger: "blur"}
],
sales: [
//这里可以写多个针对 name 属性的校验规则
{required:true, message: "请输入价格", trigger:"blur"},
//使用正则表达式对输入的数据进行校验
{pattern: /^([1-9]\d*|0)$/, message: "请输入数字", trigger: "blur"}
],
stock: [
//这里可以写多个针对 name 属性的校验规则
{required:true, message: "请输入制造商名", trigger:"blur"},
//使用正则表达式对输入的数据进行校验
{pattern: /^([1-9]\d*|0)$/, message: "请输入数字", trigger: "blur"}
]
}
}
},
created() {
this.list(); //调用 list 方法
},
methods: {
add() {
//显示对话框
this.dialogVisible = true;
//清空写上的表单数据
this.form = {}
//清空上次校验的信息
this.$refs['form'].removeField()
},
save() { //将填写的表单数据发送给后端
//修改和添加时的确定按钮绑定的是同一个方法 save,所以要判断是修改还是添加
if (this.form.id) { //表示修改
request.put("/api/update", this.form).then(res => {
if(res.code === 200) { //修改成功
//提示一个成功的消息框
this.$message(
{
type:"success",
message:"更新成功"
}
)
} else {
//提示一个失败的消息框
this.$message(
{
type:"error",
message:"更新失败"
}
)
}
//关闭对话框,更新数据
this.dialogVisible = false
//调用 list 方法,刷新数据
this.list()
})
} else {
//表单验证是否通过
this.$refs['form'].validate((valid) => {
//valid 就是表单验证后返回的结果
if (valid) { //如果校验通过
//url : "http://localhost:8080/ssm/save"
//this.form : 携带的数据
request.post("/api/save", this.form).then(res => {
console.log("res - ", res)
this.dialogVisible = false
//调用 list 方法,这样就可以刷新数据
this.list()
})
} else { //校验没有通过
//提示一个错误的消息框
this.$message(
{
type: "error",
message: "验证失败,不提交"
}
)
}
return false;
})
}
},
//编写 list 方法,请求返回家居信息
//list 方法应该是自动调用
list() {
/*request.get("/api/furns").then(res => {
console.log("res = " , res)
//this.tableData = res.data.extend.furnList
//因为这里对返回的 response 结果进行了统一的拦截处理
this.tableData = res.extend.furnList
})*/
//请求分页的接口
request.get("/api/furnsByConditionPage", {
params:{ //指定请求携带的参数
pageNum:this.currentPage,
pageSize:this.pageSize,
search:this.search
}
}).then(res => { //处理返回的分页信息
//res.extend.pageInfo.list 这里这么写的原因是要看后端返回来的结构的
this.tableData = res.extend.pageInfo.list
this.total = res.extend.pageInfo.total
})
},
handleEdit(row) {
//console.log("row = " , row)
//将当前的家居信息绑定到弹出对话框的 form
//方式1:可以通过 row.id 到后端 DB 去获取对应的家居信息,返回后将其绑定 this.form
//可以通过 row.id 到后端 DB 去获取对应的家居信息
request.get("api/find/" + row.id).then(res => {
console.log("家居信息 = ", res.extend.furn) //res.data.extend.furn
this.form = res.extend.furn
})
//方式2:把获取的 row 的数据通过处理,绑定到 this.form 进行显示
//将 json 字符串转成 json 对象并赋给 form 表单
this.form = JSON.parse(JSON.stringify(row));
this.dialogVisible = true;
},
handleDel(id) {
//console.log("id = ", id)
request.delete("/api/del/" + id).then(res => {
if (res.code === 200) { //删除成功
//提示一个成功的消息框
this.$message(
{
type:"success",
message:res.msg
}
)
} else { //删除失败
//提示一个删除的消息框
this.$message(
{
type:"error",
message:res.msg
}
)
}
//刷新页面数据
this.list()
})
},
handleCurrentChange(pageNum) { //处理分页请求
//当用户点击分页超链接时,会携带 pageNum
//console.log("pageNum = " , pageNum)
this.currentPage = pageNum
//发出分页请求
this.list()
},
handlePageSizeChange(pageSize) {
this.pageSize = pageSize
this.list()
}
}
}
</script>11.4 完成测试
第 12 章 实现功能 11 - 添加家居表单后端校验
12.1 需求分析
为什么前端校验了,后端还需要校验?因为如果有人用 Postman 请求添加数据,就不会走前端的校验规则
12.2 思路分析
后台使用 JSR303 数据校验,引入 hibernate-validator.jar,SpringMVC 中学过
前台使用 ElementPlus 进行数据绑定,并显示错误信息
12.3 代码实现
(1)修改 pom.xml 引入 hibernate-validator.jar 文件
xml
<!--JSR303 数据校验支持,引入 hibernate-validator-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.1.0.Final</version>
</dependency>JavaBean
java
public class Furn {
private Integer id;
@NotEmpty(message = "请输入家居名")
private String name;
@NotEmpty(message = "请输入制造商")
private String maker;
@NotNull(message = "请输入数字")
@Range(min = 0, message = "价格不能小于 0")
private BigDecimal price;
@NotNull(message = "请输入数字")
@Range(min = 0, message = "销量不能小于 0")
private Integer sales;
@NotNull(message = "请输入数字")
@Range(min = 0, message = "库存不能小于 0")
private Integer stock;
private String imgPath = "assets/images/product-image/1.jpg";
public Furn(Integer id, String name, String maker, BigDecimal price, Integer sales, Integer stock, String imgPath) {
this.id = id;
this.name = name;
this.maker = maker;
this.price = price;
this.sales = sales;
this.stock = stock;
//if(imgPath != null && !imgPath.equals("")) {} 可以用工具类代替
//StringUtils.hasText(imgPath) 这个的意思就是要求 imgPath 不是 null,不是 "",不是 " "
if(StringUtils.hasText(imgPath)) {
this.imgPath = imgPath;
}
}
public Furn() {
}
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 == null ? null : name.trim();
}
public String getMaker() {
return maker;
}
public void setMaker(String maker) {
this.maker = maker == null ? null : maker.trim();
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public Integer getSales() {
return sales;
}
public void setSales(Integer sales) {
this.sales = sales;
}
public Integer getStock() {
return stock;
}
public void setStock(Integer stock) {
this.stock = stock;
}
public String getImgPath() {
return imgPath;
}
public void setImgPath(String imgPath) {
this.imgPath = imgPath == null ? null : imgPath.trim();
}
@Override
public String toString() {
return "Furn{" +
"id=" + id +
", name='" + name + '\'' +
", maker='" + maker + '\'' +
", price=" + price +
", sales=" + sales +
", stock=" + stock +
", imgPath='" + imgPath + '\'' +
'}';
}
}FurnController.java
java
//响应客户端的添加请求
//@RequestBody 的作用是把前端的 json 数据转成 JavaBean
//@ResponseBody 的作用是把要响应的数据转成 json 发送
@PostMapping("/save")
@ResponseBody
public Msg save(@Validated @RequestBody Furn furn, Errors errors) {
Map<String, Object> map = new HashMap<>();
List<FieldError> fieldErrors = errors.getFieldErrors();
for (FieldError fieldError : fieldErrors) {
map.put(fieldError.getField(), fieldError.getDefaultMessage());
}
if (map.isEmpty()) { //说明后端校验通过,因为没有发现校验错误
furnService.save(furn);
//返回成功信息
Msg success = Msg.success();
return success;
} else {
//校验失败,把校验错误的信息封装到 Msg 对象,并返回
return Msg.fail().add("errorMsg", map);
}
}前后端整理
HomeView
vue
<template>
<div>
<!--增加按钮和搜索框-->
<div style="margin: 10px 5px">
<el-button type="primary" @click="add">新增</el-button>
<el-button>其它</el-button>
</div>
<div style="margin: 10px 5px">
<el-input v-model="search" style="width: 30%" placeholder="请输入关键字" />
<el-button style="margin-left: 10px" type="primary" @click="list">检索</el-button>
</div>
<el-table :data="tableData" stripe style="width: 90%">
<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">
<!--这里可以通过 handleEdit(scope.row) 将当前行的数据传递给 handleEdit-->
<el-button type="text" @click="handleEdit(scope.row)">
编辑
</el-button>
<!--
如果点击的是确定,就会触发 handleDel
如果点击的是取消,就不会触发 handleDel
-->
<el-popconfirm title="确认删除吗?" @confirm="handleDel(scope.row.id)">
<template #reference>
<el-button size="small" type="danger">删除</el-button>
</template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
<!--
添加家居的弹窗
说明:
el-dialog: v-model="dialogVisible" 表示对话框,和 dialogVisible 变量双向绑定,控制是否显示对话框
el-form : model="form" 表示表单,数据和 form 数据变量双向绑定
el-input v-model="form.name" 表示表单的 input 控件,名字为 name 需要和后台 JavaBean 属性一致
在前端中,对象的属性是可以动态生成的,即如果有 v-model="form.name" 那么提交时就会以 name=Xxx 进行提交
-->
<el-dialog
v-model="dialogVisible"
title="提示"
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: 50%"></el-input>
{{serverValidErrors.name}}
</el-form-item>
<el-form-item label="厂商" prop="maker">
<el-input v-model="form.maker" style="width: 50%"></el-input>
{{serverValidErrors.maker}}
</el-form-item>
<el-form-item label="价格" prop="price">
<el-input v-model="form.price" style="width: 50%"></el-input>
{{serverValidErrors.price}}
</el-form-item>
<el-form-item label="销量" prop="sales">
<el-input v-model="form.sales" style="width: 50%"></el-input>
{{serverValidErrors.sales}}
</el-form-item>
<el-form-item label="库存" prop="stock">
<el-input v-model="form.stock" style="width: 50%"></el-input>
{{serverValidErrors.stock}}
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="save">确定</el-button>
</div>
</template>
</el-dialog>
<!--添加分页导航-->
<div style="margin: 10px 0">
<el-pagination
@size-change="handlePageSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="[2,5,10]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination>
</div>
</div>
</template>
<script>
//导入 request 对象
import request from "@/utils/request";
import {handleCurrentChange} from "element-plus/es/components/tree/src/model/util";
//导出组件
export default {
name: 'HomeView',
components: {
},
data() {
return {
//存放后端校验的错误信息
serverValidErrors: {},
//增加分页相应的数据绑定
currentPage: 1, //当前页
pageSize: 5, //每页显示记录数
total: 10, //共有多少记录
search: '',
dialogVisible: false,
form:{},
tableData: [
],
//定义添加表单的校验规则
rules: {
name: [
//这里可以写多个针对 name 属性的校验规则
{required:true, message: "请输入家居名", trigger:"blur"}
],
maker: [
//这里可以写多个针对 name 属性的校验规则
{required:true, message: "请输入制造商名", trigger:"blur"}
],
price: [
//这里可以写多个针对 name 属性的校验规则
{required:true, message: "请输入制造商名", trigger:"blur"},
//使用正则表达式对输入的数据进行校验
{pattern: /^([1-9]\d*|0)(\.\d+)?$/, message: "请输入数字", trigger: "blur"}
],
sales: [
//这里可以写多个针对 name 属性的校验规则
{required:true, message: "请输入价格", trigger:"blur"},
//使用正则表达式对输入的数据进行校验
{pattern: /^([1-9]\d*|0)$/, message: "请输入数字", trigger: "blur"}
],
stock: [
//这里可以写多个针对 name 属性的校验规则
{required:true, message: "请输入制造商名", trigger:"blur"},
//使用正则表达式对输入的数据进行校验
{pattern: /^([1-9]\d*|0)$/, message: "请输入数字", trigger: "blur"}
]
}
}
},
created() {
this.list(); //调用 list 方法
},
methods: {
add() {
//显示对话框
this.dialogVisible = true;
//清空写上的表单数据
this.form = {}
//清空上次校验的信息
this.$refs['form'].resetFields()
//清空上次后端校验的信息
this.serverValidErrors = {}
},
save() { //将填写的表单数据发送给后端
//修改和添加时的确定按钮绑定的是同一个方法 save,所以要判断是修改还是添加
if (this.form.id) { //表示修改
request.put("/api/update", this.form).then(res => {
if(res.code === 200) { //修改成功
//提示一个成功的消息框
this.$message(
{
type:"success",
message:"更新成功"
}
)
} else {
//提示一个失败的消息框
this.$message(
{
type:"error",
message:"更新失败"
}
)
}
//关闭对话框,更新数据
this.dialogVisible = false
//调用 list 方法,刷新数据
this.list()
})
} else {
//表单验证是否通过
this.$refs['form'].validate((valid) => {
//valid 就是表单验证后返回的结果
if (valid) { //如果校验通过
//url : "http://localhost:8080/ssm/save"
//this.form : 携带的数据
request.post("/api/save", this.form).then(res => {
console.log("res - ", res)
if (res.code === 200) {
this.dialogVisible = false
//调用 list 方法,这样就可以刷新数据
this.list()
} else if(res.code === 400) { //后端校验失败
//取出校验失败的信息,赋给 serverValidErrors
this.serverValidErrors.name = res.extend.errorMsg.name
this.serverValidErrors.maker = res.extend.errorMsg.maker
this.serverValidErrors.price = res.extend.errorMsg.price
this.serverValidErrors.sales = res.extend.errorMsg.sales
this.serverValidErrors.stock = res.extend.errorMsg.stock
}
})
} else { //校验没有通过
//提示一个错误的消息框
this.$message(
{
type: "error",
message: "验证失败,不提交"
}
)
}
return false;
})
}
},
//编写 list 方法,请求返回家居信息
//list 方法应该是自动调用
list() {
/*request.get("/api/furns").then(res => {
console.log("res = " , res)
//this.tableData = res.data.extend.furnList
//因为这里对返回的 response 结果进行了统一的拦截处理
this.tableData = res.extend.furnList
})*/
//请求分页的接口
request.get("/api/furnsByConditionPage", {
params:{ //指定请求携带的参数
pageNum:this.currentPage,
pageSize:this.pageSize,
search:this.search
}
}).then(res => { //处理返回的分页信息
//res.extend.pageInfo.list 这里这么写的原因是要看后端返回来的结构的
this.tableData = res.extend.pageInfo.list
this.total = res.extend.pageInfo.total
})
},
handleEdit(row) {
//console.log("row = " , row)
//将当前的家居信息绑定到弹出对话框的 form
//方式1:可以通过 row.id 到后端 DB 去获取对应的家居信息,返回后将其绑定 this.form
//可以通过 row.id 到后端 DB 去获取对应的家居信息
request.get("api/find/" + row.id).then(res => {
console.log("家居信息 = ", res.extend.furn) //res.data.extend.furn
this.form = res.extend.furn
})
//方式2:把获取的 row 的数据通过处理,绑定到 this.form 进行显示
//将 json 字符串转成 json 对象并赋给 form 表单
this.form = JSON.parse(JSON.stringify(row));
this.dialogVisible = true;
},
handleDel(id) {
//console.log("id = ", id)
request.delete("/api/del/" + id).then(res => {
if (res.code === 200) { //删除成功
//提示一个成功的消息框
this.$message(
{
type:"success",
message:res.msg
}
)
} else { //删除失败
//提示一个删除的消息框
this.$message(
{
type:"error",
message:res.msg
}
)
}
//刷新页面数据
this.list()
})
},
handleCurrentChange(pageNum) { //处理分页请求
//当用户点击分页超链接时,会携带 pageNum
//console.log("pageNum = " , pageNum)
this.currentPage = pageNum
//发出分页请求
this.list()
},
handlePageSizeChange(pageSize) {
this.pageSize = pageSize
this.list()
}
}
}
</script>