Skip to content

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 router

4)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>

Released under the MIT License.

本站访客数 人次 本站总访问量