仓库管理系统(SpringBoot+Vue)

仓库管理系统(SpringBoot+Vue)

仓库管理系统(SpringBoot+Vue)

一、创建后端项目

1、新建空白文件夹,用于存放前后端文件

2、用IDEA打开刚刚新建的空白文件夹

3、创建SpringBoot工程

4、创建测试类进行测试

通过百度查询报错原因,发现是SpringBoot版本太高导致的,版本降低就好了

浏览器打开localhost:8080,配置成功

二、加入mybatis-plus支持

1、在pom.xml中添加mybatis-plus依赖

2、创建数据库

3、创建用户表

create table user(
	id int auto_increment comment '主键' primary key,
	no varchar(20) null comment '账号',
	name varchar(100) not null comment '名字',
	password varchar(20) not null comment '密码',
	age int null comment '年龄',
	sex int null comment '性别',
	phone varchar(20) null comment '电话',
	role_id int null comment '角色 0超级管理员,1管理员,2普通用户',
	isValid varchar(4) default 'Y' null comment '是否有效,Y有效,其他无效'
) charset=utf8;

4、给用户表添加一条数据

5、配置yml文件

创建SpringBoot项目时会自动生成application.properties文件,但我们一般使用application.yml

端口和数据源的配置

server:
  port: 8090

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/wms?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: chx308241

6、编写测试代码

  • 创建实体类
@Data
public class User {
    private int id;
    private String no;
    private String name;
    private String password;
    private int sex;
    private String phone;
    private int roleId;
    @TableField(value = "isValid")
    private String isValid;
}
  • 创建mapper接口
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
  • 创建service接口
public interface UserService extends IService<User> {
}
  • 创建service实现类
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}
  • 在HelloController类中编写测试代码
@Autowired
private UserService userService;

@GetMapping("/list")
public List<User> list() {
    return userService.list();
}

浏览器访问localhost:8090/list

  • 自定义listAll()方法
//HelloController类中
@Autowired
private UserService userService;

@GetMapping("/list")
public List<User> list() {
//    return userService.list();
    return userService.listAll();
}  

//UserService接口中
public interface UserService extends IService<User> {
    List<User> listAll();
}

//UserServiceImpl类中
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    @Resource
    private UserMapper userMapper;

    @Override
    public List<User> listAll() {
        return userMapper.listAll();
    }
}

//UserMapper接口中
@Mapper
public interface UserMapper extends BaseMapper<User> {
    List<User> listAll();
}
<!--UserMapper.xml文件中-->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wms.mapper.UserMapper">
    <select id="listAll" resultType="com.wms.entity.User">
        select * from user
    </select>
</mapper>

三、使用代码生成器生成代码

1、在pom.xml中添加依赖

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.4.1</version>
</dependency>
<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>2.3.32</version>
</dependency>
<dependency>
    <groupId>com.spring4all</groupId>
    <artifactId>spring-boot-starter-swagger</artifactId>
    <version>1.5.1.RELEASE</version>
</dependency>

2、新建CodeGenerator类

package com.wms.common;

import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class CodeGenerator {
    /**
     * <p>
     * 读取控制台内容
     * </p>
     */
    public static String scanner(String tip) {
        Scanner scanner = new Scanner(System.in);
        StringBuilder help = new StringBuilder();
        help.append("请输入" + tip + ": ");
        System.out.println(help.toString());
        if (scanner.hasNext()) {
            String ipt = scanner.next();
            if (StringUtils.isNotBlank(ipt)) {
                return ipt;
            }
        }
        throw new MybatisPlusException("请输入正确的" + tip + "! ");
    }

    /**
     * 操作步骤:
     *  1.修改数据源包括地址密码信息,对应代码标记
     *  2.模块配置,可以修改包名
     *  3.修改模块(这步可忽略)
     * @param args
     */
    public static void main(String[] args) {
        // 代码生成器
        AutoGenerator mpg = new AutoGenerator();

        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir") + "\\wms";
        gc.setOutputDir(projectPath + "\\src\\main\\java");
        gc.setAuthor("ghost");
        gc.setOpen(false);
        gc.setSwagger2(true);   //实体属性 Swagger2 注解
        gc.setBaseResultMap(true);  // XML ResultMap
        gc.setBaseColumnList(true); // XML columnList
        //去掉service接口首字母的I,如DU为User则叫UserService
        gc.setServiceName("%sService");
        mpg.setGlobalConfig(gc);

        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        // 一、修改数据源
        dsc.setUrl("jdbc:mysql://localhost:3306/wms?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8");
        // dsc.setSchemaName("public");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("chx308241");
        mpg.setDataSource(dsc);

        // 包配置
        PackageConfig pc = new PackageConfig();
        // pc.setModuleName(scanner("模块名"));
        // 二、模块配置
        pc.setParent("com.wms")
                .setEntity("entity")
                .setMapper("mapper")
                .setService("service")
                .setServiceImpl("service.impl")
                .setController("controller");
        mpg.setPackageInfo(pc);

        // 自定义配置
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                // to do nothing
            }
        };

        // 如果模板引擎是 freemarker
        String templatePath = "templates/mapper.xml.ftl";

        // 自定义输出配置
        List<FileOutConfig> focList = new ArrayList<>();
        // 自定义配置会被优先输出
        focList.add(new FileOutConfig(templatePath) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定义输出文件名
                return projectPath + "\\src\\main\\resources\\mapper\\" + pc.getModuleName()
                        + "\\" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
            }
        });

        cfg.setFileOutConfigList(focList);
        mpg.setCfg(cfg);

        // 配置模板
        TemplateConfig templateConfig = new TemplateConfig();

        templateConfig.setXml(null);
        mpg.setTemplate(templateConfig);

        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        strategy.setEntityLombokModel(true);
        strategy.setRestControllerStyle(true);
        strategy.setInclude(scanner("表名,多个英文逗号分割").split(","));
        strategy.setControllerMappingHyphenStyle(true);

        mpg.setStrategy(strategy);
        mpg.setTemplateEngine(new FreemarkerTemplateEngine());
        mpg.execute();
    }

}

3、运行CodeGenerator类

四、实现增删改查

1、在UserController类中编写增删改查代码

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    @GetMapping("/list")
    public List<User> list() {
        return userService.list();
    }

    //新增
    @PostMapping("/save")
    public boolean save(@RequestBody User user) {
        return userService.save(user);
    }

    //修改
    @PostMapping("/mod")
    public boolean mod(@RequestBody User user) {
        return userService.updateById(user);
    }

    //新增或修改
    @PostMapping("/saveOrMod")
    public boolean saveOrMod(@RequestBody User user) {
        return userService.saveOrUpdate(user);
    }

    //删除
    @GetMapping("/delete")
    public boolean delete(Integer id) {
        return  userService.removeById(id);
    }

    //查询(模糊、匹配)
    @PostMapping("/listP")
    public List<User> listP(@RequestBody User user) {
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper();
        lambdaQueryWrapper.like(User::getName, user.getName());
        return userService.list(lambdaQueryWrapper);
    }

}

2、使用Postman工具进行测试

五、分页的处理

1、入参的封装

package com.wms.common;

import lombok.Data;

import java.util.HashMap;

@Data
public class QueryPageParam {

    private static int PAGE_SIZE = 20;
    private static int PAGE_NUM = 1;

    private int pageSize = PAGE_SIZE;
    private int pageNum = PAGE_NUM;

    private HashMap param;

}

2、分页拦截器的配置

package com.wms.common;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MybatisPlusConfig {
    /**
     * 添加分页插件
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));//如果配置多个插件,切记分页最后添加
        //interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); 如果有多数据源可以不配具体类型 否则都建议配上具体的DbType
        return interceptor;
    }
}

3、分页测试

//分页查询
@PostMapping("/listPage")
public List<User> listPage(@RequestBody QueryPageParam query) {
//    System.out.println(query);
//    System.out.println("num===" + query.getPageNum());
//    System.out.println("size===" + query.getPageSize());
    HashMap param = query.getParam();
//    System.out.println("no===" + (String) param.get("no"));
//    System.out.println("name===" + (String) param.get("name"));
    Page<User> page = new Page<>();
    page.setCurrent(query.getPageNum());
    page.setSize(query.getPageSize());

    LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
    lambdaQueryWrapper.like(User::getName, (String) param.get("name"));

    IPage<User> result = userService.page(page, lambdaQueryWrapper);
    System.out.println("total===" + result.getTotal());

    return result.getRecords();
}

4、自定义分页查询

//自定义分页查询
@PostMapping("/listPageC")
public List<User> listPageC(@RequestBody QueryPageParam query) {

    Page<User> page = new Page<>();
    page.setCurrent(query.getPageNum());
    page.setSize(query.getPageSize());

    IPage<User> result = userService.pageC(page);
    System.out.println("total===" + result.getTotal());

    return result.getRecords();
}
// UserService接口中
public interface UserService extends IService<User> {
    IPage<User> pageC(IPage<User> page);
}
// UserServiceImpl实现类中
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    @Autowired
    private UserMapper userMapper;

    @Override
    public IPage<User> pageC(IPage<User> page) {
        return userMapper.pageC(page);
    }
}
// UserMapper接口中
@Mapper
public interface UserMapper extends BaseMapper<User> {
    IPage<User> pageC(IPage<User> page);
}
<!-- UserMapper.xml文件中 -->
<select id="pageC" resultType="com.wms.entity.User">
    select * from user
</select>

5、自定义SQL使用Wrapper

//自定义SQL使用Wrapper
@PostMapping("/listPageCC")
public List<User> listPageCC(@RequestBody QueryPageParam query) {

    Page<User> page = new Page<>();
    page.setCurrent(query.getPageNum());
    page.setSize(query.getPageSize());

    HashMap param = query.getParam();

    LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
    lambdaQueryWrapper.like(User::getName, (String) param.get("name"));

    IPage<User> result = userService.pageCC(page, lambdaQueryWrapper);
    System.out.println("total===" + result.getTotal());

    return result.getRecords();
}
// UserService接口中
public interface UserService extends IService<User> {
    IPage<User> pageC(IPage<User> page);
    IPage<User> pageCC(IPage<User> page, Wrapper<User> wrapper);
}
// UserServiceImpl实现类中
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public IPage<User> pageC(IPage<User> page) {
        return userMapper.pageC(page);
    }

    @Override
    public IPage<User> pageCC(IPage<User> page, Wrapper<User> wrapper) {
        return userMapper.pageCC(page, wrapper);
    }

}
// UserMapper接口中
@Mapper
public interface UserMapper extends BaseMapper<User> {
    IPage<User> pageC(IPage<User> page);
    IPage<User> pageCC(IPage<User> page, @Param(Constants.WRAPPER) Wrapper<User> wrapper);
}
<!-- UserMapper.xml文件中 -->
<select id="pageCC" resultType="com.wms.entity.User">
    select * from user ${ew.customSqlSegment}
</select>

六、返回前端数据的封装

1、封装Result类

package com.wms.common;

import lombok.Data;

@Data
public class Result {
    private int code;   // 编码 200/400
    private String msg; // 成功/失败
    private Long total; // 总记录数
    private Object data; // 数据

    public static Result fail() {
        return result(400, "失败", 0L, null);
    }

    public static Result success() {
        return result(200, "成功", 0L, null);
    }

    public static Result success(Object data) {
        return result(200, "成功", 0L, data);
    }

    public static Result success(Long total, Object data) {
        return result(200, "成功", total, data);
    }

    private static Result result(int code, String msg, Long total, Object data) {
        Result res = new Result();
        res.setCode(code);
        res.setMsg(msg);
        res.setTotal(total);
        res.setData(data);
        return res;
    }

}

2、测试返回结果

@PostMapping("/listPageCC1")
public Result listPageCC1(@RequestBody QueryPageParam query) {

    Page<User> page = new Page<>();
    page.setCurrent(query.getPageNum());
    page.setSize(query.getPageSize());

    HashMap param = query.getParam();

    LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
    lambdaQueryWrapper.like(User::getName, (String) param.get("name"));

    IPage<User> result = userService.pageCC(page, lambdaQueryWrapper);
    System.out.println("total===" + result.getTotal());

    return Result.success(result.getTotal(), result.getRecords());
}

七、创建前端Vue项目

1、在刚开始新建的文件夹下打开终端

2、在终端中输入如下命令

vue create wms-web

其中wms-web是前端文件名,自定义即可。

3、选择Vue2敲回车即可

4、安装成功

5、测试一下

依次输入如下命令:

cd wms-web
npm run serve

浏览器输入localhost:8080即可访问

八、前端Vue项目导入到IDEA并运行

1、打开IDEA会发现多了wms-web文件夹

2、运行前端Vue项目

通过在IDEA的终端输入如下命令:

cd wms-web
npm run serve

启动成功

九、导入Element-ui

1、安装element-ui

在终端输入如下命令:

npm i element-ui -S

这时可以看到package.json中已经成功安装element-ui

2、element-ui的使用

  • 在main.js中全局引入
import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import App from './App.vue';

Vue.config.productionTip = false

Vue.use(ElementUI);

new Vue({
  render: h => h(App),
}).$mount('#app')

3、测试一下

十、搭建页面布局

1、在components文件夹下新建Index.vue

2、把element-ui中的容器布局中的源代码复制到Index.vue中

<template>
  <el-container style="height: 500px; border: 1px solid #eee">
    <el-aside width="200px" style="background-color: rgb(238, 241, 246)">
      <el-menu :default-openeds="['1', '3']">
        <el-submenu index="1">
          <template slot="title"><i class="el-icon-message"></i>导航一</template>
          <el-menu-item-group>
            <template slot="title">分组一</template>
            <el-menu-item index="1-1">选项1</el-menu-item>
            <el-menu-item index="1-2">选项2</el-menu-item>
          </el-menu-item-group>
          <el-menu-item-group title="分组2">
            <el-menu-item index="1-3">选项3</el-menu-item>
          </el-menu-item-group>
          <el-submenu index="1-4">
            <template slot="title">选项4</template>
            <el-menu-item index="1-4-1">选项4-1</el-menu-item>
          </el-submenu>
        </el-submenu>
        <el-submenu index="2">
          <template slot="title"><i class="el-icon-menu"></i>导航二</template>
          <el-menu-item-group>
            <template slot="title">分组一</template>
            <el-menu-item index="2-1">选项1</el-menu-item>
            <el-menu-item index="2-2">选项2</el-menu-item>
          </el-menu-item-group>
          <el-menu-item-group title="分组2">
            <el-menu-item index="2-3">选项3</el-menu-item>
          </el-menu-item-group>
          <el-submenu index="2-4">
            <template slot="title">选项4</template>
            <el-menu-item index="2-4-1">选项4-1</el-menu-item>
          </el-submenu>
        </el-submenu>
        <el-submenu index="3">
          <template slot="title"><i class="el-icon-setting"></i>导航三</template>
          <el-menu-item-group>
            <template slot="title">分组一</template>
            <el-menu-item index="3-1">选项1</el-menu-item>
            <el-menu-item index="3-2">选项2</el-menu-item>
          </el-menu-item-group>
          <el-menu-item-group title="分组2">
            <el-menu-item index="3-3">选项3</el-menu-item>
          </el-menu-item-group>
          <el-submenu index="3-4">
            <template slot="title">选项4</template>
            <el-menu-item index="3-4-1">选项4-1</el-menu-item>
          </el-submenu>
        </el-submenu>
      </el-menu>
    </el-aside>

    <el-container>
      <el-header style="text-align: right; font-size: 12px">
        <el-dropdown>
          <i class="el-icon-setting" style="margin-right: 15px"></i>
          <el-dropdown-menu slot="dropdown">
            <el-dropdown-item>查看</el-dropdown-item>
            <el-dropdown-item>新增</el-dropdown-item>
            <el-dropdown-item>删除</el-dropdown-item>
          </el-dropdown-menu>
        </el-dropdown>
        <span>王小虎</span>
      </el-header>

      <el-main>
        <el-table :data="tableData">
          <el-table-column prop="date" label="日期" width="140">
          </el-table-column>
          <el-table-column prop="name" label="姓名" width="120">
          </el-table-column>
          <el-table-column prop="address" label="地址">
          </el-table-column>
        </el-table>
      </el-main>
    </el-container>
  </el-container>
</template>

<script>
export default {
  name: "Index",
  data() {
    const item = {
      date: '2016-05-02',
      name: '王小虎',
      address: '上海市普陀区金沙江路 1518 弄'
    };
    return {
      tableData: Array(10).fill(item)
    }
  }
}
</script>

<style scoped>
  .el-header {
    background-color: #B3C0D1;
    color: #333;
    line-height: 60px;
  }

  .el-aside {
    color: #333;
  }
</style>

3、将Index.vue引入App.vue中

<template>
  <div id="app">
    <Index></Index>
  </div>
</template>

<script>

  import Index from "@/components/Index.vue";

  export default {
    name: 'App',
    components: {
      Index
    }
  }
</script>

<style>
  #app {

  }
</style>

4、运行一下看看页面效果

5、调整页面

在assets文件夹下新建global.css,编写如下代码:

*{
    margin: 0;
    padding: 0;
}

在main.js中引入global.css

import './assets/global.css'

这时可以看到四周的空白边框已经消失了,但是表格周围的空白间距有点大,我们通过f12查看网页源代码

可以看到是.el-main的padding值设置为20px导致的,所以我们到Index.vue中修改一下.el-main的padding值

<style scoped>
  .el-header {
    background-color: #B3C0D1;
    color: #333;
    line-height: 60px;
  }

  .el-aside {
    color: #333;
  }
  
  .el-main {
    padding: 5px;
  }
</style>

页面并没有铺满整个页面,需要调整一下高度:

在App.vue中:

<style>
  html, body, #app {
    height: 100%;
  }
</style>

在Index.vue中:

<el-container style="height: 100%; border: 1px solid #eee">
  <el-aside width="200px" style="background-color: rgb(238, 241, 246); height: 100%">
    <el-menu :default-openeds="['1', '3']" style="height: 100%;">
    </el-menu>
  </el-aside>
  <el-container style="height: 100%">
    <el-main style="height: 100%">
    </el-main>
  </el-container>
</el-container>

十一、页面布局的拆分

在components文件夹下新建Aside.vue、Header.vue、Main.vue,将Index.vue中对应代码复制到相应Vue文件中,并将其重新引入Index.vue中。

  • Aside.vue
<template>
  <el-menu :default-openeds="['1', '3']" style="height: 100%;">
    <el-submenu index="1">
      <template slot="title"><i class="el-icon-message"></i>导航一</template>
      <el-menu-item-group>
        <template slot="title">分组一</template>
        <el-menu-item index="1-1">选项1</el-menu-item>
        <el-menu-item index="1-2">选项2</el-menu-item>
      </el-menu-item-group>
      <el-menu-item-group title="分组2">
        <el-menu-item index="1-3">选项3</el-menu-item>
      </el-menu-item-group>
      <el-submenu index="1-4">
        <template slot="title">选项4</template>
        <el-menu-item index="1-4-1">选项4-1</el-menu-item>
      </el-submenu>
    </el-submenu>
    <el-submenu index="2">
      <template slot="title"><i class="el-icon-menu"></i>导航二</template>
      <el-menu-item-group>
        <template slot="title">分组一</template>
        <el-menu-item index="2-1">选项1</el-menu-item>
        <el-menu-item index="2-2">选项2</el-menu-item>
      </el-menu-item-group>
      <el-menu-item-group title="分组2">
        <el-menu-item index="2-3">选项3</el-menu-item>
      </el-menu-item-group>
      <el-submenu index="2-4">
        <template slot="title">选项4</template>
        <el-menu-item index="2-4-1">选项4-1</el-menu-item>
      </el-submenu>
    </el-submenu>
    <el-submenu index="3">
      <template slot="title"><i class="el-icon-setting"></i>导航三</template>
      <el-menu-item-group>
        <template slot="title">分组一</template>
        <el-menu-item index="3-1">选项1</el-menu-item>
        <el-menu-item index="3-2">选项2</el-menu-item>
      </el-menu-item-group>
      <el-menu-item-group title="分组2">
        <el-menu-item index="3-3">选项3</el-menu-item>
      </el-menu-item-group>
      <el-submenu index="3-4">
        <template slot="title">选项4</template>
        <el-menu-item index="3-4-1">选项4-1</el-menu-item>
      </el-submenu>
    </el-submenu>
  </el-menu>
</template>

<script>
  export default {
    name: "Aside"
  }
</script>

<style scoped>

</style>
  • Header.vue
<template>
  <div>
    <el-dropdown>
      <i class="el-icon-setting" style="margin-right: 15px"></i>
      <el-dropdown-menu slot="dropdown">
        <el-dropdown-item>查看</el-dropdown-item>
        <el-dropdown-item>新增</el-dropdown-item>
        <el-dropdown-item>删除</el-dropdown-item>
      </el-dropdown-menu>
    </el-dropdown>
    <span>王小虎</span>
  </div>
</template>

<script>
  export default {
    name: "Header"
  }
</script>

<style scoped>

</style>
  • Main.vue
<template>
  <el-table :data="tableData">
    <el-table-column prop="date" label="日期" width="140">
    </el-table-column>
    <el-table-column prop="name" label="姓名" width="120">
    </el-table-column>
    <el-table-column prop="address" label="地址">
    </el-table-column>
  </el-table>
</template>

<script>
  export default {
    name: "Main",
    data() {
      const item = {
        date: '2016-05-02',
        name: '王小虎',
        address: '上海市普陀区金沙江路 1518 弄'
      };
      return {
        tableData: Array(10).fill(item)
      }
    }
  }
</script>

<style scoped>

</style>
  • Index.vue
<template>
  <el-container style="height: 100%; border: 1px solid #eee">
    <el-aside width="200px" style="background-color: rgb(238, 241, 246); height: 100%">
      <Aside></Aside>
    </el-aside>

    <el-container style="height: 100%">
      <el-header style="text-align: right; font-size: 12px;">
        <Header></Header>
      </el-header>

      <el-main style="height: 100%">
        <Main></Main>
      </el-main>
    </el-container>
  </el-container>
</template>

<script>
  import Aside from "@/components/Aside.vue";
  import Header from "@/components/Header.vue";
  import Main from "@/components/Main.vue";

  export default {
    name: "Index",
    components: {Header, Aside, Main},
  }
</script>

<style scoped>
  .el-header {
    background-color: #B3C0D1;
    color: #333;
    line-height: 60px;
  }

  .el-aside {
    color: #333;
  }

  .el-main {
    padding: 5px;
  }
</style>

十二、编写Header头部页面

<template>
  <div style="display: flex;">
    <div style="margin-top: 2px;">
      <i class="el-icon-s-fold" style="font-size: 20px;"></i>
    </div>
    <div style="flex: 1; text-align: center; font-size: 34px">
      <span>欢迎来到仓库管理系统</span>
    </div>
    <el-dropdown>
      <span style="margin-right: 5px;">王小虎</span><i class="el-icon-arrow-down"></i>
      <el-dropdown-menu slot="dropdown">
        <el-dropdown-item @click.native="toUser">个人中心</el-dropdown-item>
        <el-dropdown-item @click.native="logout">退出登录</el-dropdown-item>
      </el-dropdown-menu>
    </el-dropdown>
  </div>
</template>

<script>
  export default {
    name: "Header",
    methods: {
      toUser() {
        console.log('to_user')
      },
      logout() {
        console.log('logout')
      }
    }
  }
</script>

<style scoped>

</style>

十三、编写菜单导航页面

<template>
  <el-menu
      default-active="/Home"
      background-color="#545c64"
      text-color="#fff"
      active-text-color="#ffd04b"
      style="height: 100%;">
    <el-menu-item index="/Home">
      <i class="el-icon-s-home"></i>
      <span>首页</span>
    </el-menu-item>

    <el-menu-item index="/Nav1">
      <i class="el-icon-s-flag"></i>
      <span>导航一</span>
    </el-menu-item>

    <el-menu-item index="/Nav2">
      <i class="el-icon-s-shop"></i>
      <span>导航二</span>
    </el-menu-item>
  </el-menu>
</template>

<script>
  export default {
    name: "Aside"
  }
</script>

<style scoped>

</style>

在Index.vue中:

<template>
  <el-aside width="200px" style="background-color: rgb(238, 241, 246); height: 100vh">
    <Aside></Aside>
  </el-aside>
</template>

<style scoped>
  .el-aside {
    color: #333;
    margin-left: -1px;
    margin-top: -1px;
  }
</style>

十四、菜单导航页面伸缩

  • Aside.vue
<template>
  <el-menu
      default-active="/Home"
      background-color="#545c64"
      text-color="#fff"
      active-text-color="#ffd04b"
      style="height: 100%;"
      :collapse="isCollapse"
      :collapse-transition="false"
  >
    <el-menu-item index="/Home">
      <i class="el-icon-s-home"></i>
      <span slot="title">首页</span>
    </el-menu-item>

    <el-menu-item index="/Nav1">
      <i class="el-icon-s-flag"></i>
      <span slot="title">导航一</span>
    </el-menu-item>

    <el-menu-item index="/Nav2">
      <i class="el-icon-s-shop"></i>
      <span slot="title">导航二</span>
    </el-menu-item>
  </el-menu>
</template>

<script>
  export default {
    name: "Aside",
    data() {
      return {

      }
    },
    props: {
      isCollapse: Boolean
    }
  }
</script>

<style scoped>

</style>
  • Index.vue
<template>
  <el-container style="height: 100%; border: 1px solid #eee">
    <el-aside :width="aside_width" style="background-color: rgb(238, 241, 246); height: 100vh">
      <Aside :isCollapse="isCollapse"></Aside>
    </el-aside>

    <el-container style="height: 100%">
      <el-header style="text-align: right; font-size: 12px; border-bottom: rgba(168,168,168,0.3) 1px solid;">
        <Header @doCollapse="doCollapse" :icon="icon"></Header>
      </el-header>

      <el-main style="height: 100%">
        <Main></Main>
      </el-main>
    </el-container>
  </el-container>
</template>

<script>
  import Aside from "@/components/Aside.vue";
  import Header from "@/components/Header.vue";
  import Main from "@/components/Main.vue";

  export default {
    name: "Index",
    components: {Header, Aside, Main},
    data() {
      return {
        isCollapse: false,
        aside_width: '200px',
        icon: 'el-icon-s-fold'
      }
    },
    methods: {
      doCollapse() {
        console.log(1111)
        this.isCollapse = !this.isCollapse
        if (!this.isCollapse) {
          this.aside_width = '200px'
          this.icon = 'el-icon-s-fold'
        } else {
          this.aside_width = '64px'
          this.icon = 'el-icon-s-unfold'
        }
      }
    }
  }
</script>

<style scoped>
  .el-header {
    #background-color: #B3C0D1;
    color: #333;
    line-height: 60px;
  }

  .el-aside {
    color: #333;
    margin-left: -1px;
    margin-top: -1px;
  }

  .el-main {
    padding: 5px;
  }
</style>
  • Header.vue
<template>
  <div style="display: flex;">
    <div style="margin-top: 2px;">
      <i :class="icon" style="font-size: 20px; cursor: pointer;" @click="collapse"></i>
    </div>
    <div style="flex: 1; text-align: center; font-size: 34px">
      <span>欢迎来到仓库管理系统</span>
    </div>
    <el-dropdown>
      <span style="margin-right: 5px;">王小虎</span><i class="el-icon-arrow-down"></i>
      <el-dropdown-menu slot="dropdown">
        <el-dropdown-item @click.native="toUser">个人中心</el-dropdown-item>
        <el-dropdown-item @click.native="logout">退出登录</el-dropdown-item>
      </el-dropdown-menu>
    </el-dropdown>
  </div>
</template>

<script>
  export default {
    name: "Header",
    props: {
      icon: String
    },
    methods: {
      toUser() {
        console.log('to_user')
      },
      logout() {
        console.log('logout')
      },
      collapse() {
        this.$emit('doCollapse')
      }
    }
  }
</script>

<style scoped>

</style>

十五、安装axios与跨域处理

1、安装axios

在终端输入如下命令:

npm install axios --save

2、在main.js中全局引入axios

import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import App from './App.vue';
import './assets/global.css';
import axios from 'axios';

Vue.config.productionTip = false
Vue.prototype.$axios = axios

Vue.use(ElementUI);

new Vue({
  render: h => h(App),
}).$mount('#app')

3、跨域处理

package com.wms.common;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class CorsConfig implements WebMvcConfigurer {
 
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                //是否发送Cookie
                .allowCredentials(true)
                //放行哪些原始域
                .allowedOriginPatterns("*")
                .allowedMethods(new String[]{"GET", "POST", "PUT", "DELETE"})
                .allowedHeaders("*")
                .exposedHeaders("*");
    }
}

4、使用axios访问后端请求

在Main.vue中:

<script>
  export default {
    name: "Main",
    data() {
      const item = {
        date: '2016-05-02',
        name: '王小虎',
        address: '上海市普陀区金沙江路 1518 弄'
      };
      return {
        tableData: Array(10).fill(item)
      }
    },
    methods: {
      loadGet() {
        this.$axios.get('http://localhost:8090/user/list').then(res=>res.data).then(res=>{
          console.log(res)
        })
      },
      loadPost() {
        this.$axios.post('http://localhost:8090/user/listPageC',{}).then(res=>res.data).then(res=>{
          console.log(res)
        })
      }
    },
    beforeMount() {
      // this.loadGet()
      this.loadPost()
    }
  }
</script>

5、将地址设置为全局

在main.js中:

Vue.prototype.$httpUrl = 'http://localhost:8090'

之后就可以在Main.vue中通过httpUrl调用地址了:

this.$axios.get(this.$httpUrl+'/user/list').then(res=>res.data).then(res=>{
  console.log(res)
})

十六、列表展示

1、列表数据

在Main.vue中:

data() {
  return {
    tableData: []
  }
}
loadPost() {
  this.$axios.post(this.$httpUrl+'/user/listPageC',{}).then(res=>res.data).then(res=>{
    console.log(res)
    this.tableData = res
  })
}

2、用tag转换列

<el-table :data="tableData">
  <el-table-column prop="id" label="ID" width="80">
  </el-table-column>
  <el-table-column prop="no" label="账号" width="180">
  </el-table-column>
  <el-table-column prop="name" label="姓名" width="180">
  </el-table-column>
  <el-table-column prop="age" label="年龄" width="120">
  </el-table-column>
  <el-table-column prop="sex" label="性别" width="120">
    <template slot-scope="scope">
      <el-tag
          :type="scope.row.sex === 1 ? 'primary' : 'success'"
          disable-transitions>{{scope.row.sex === 1 ? '男' : '女'}}</el-tag>
    </template>
  </el-table-column>
  <el-table-column prop="roleId" label="角色" width="120">
    <template slot-scope="scope">
      <el-tag
          :type="scope.row.roleId === 0 ? 'danger' : (scope.row.roleId === 1 ? 'primary' : 'success')"
          disable-transitions>{{scope.row.roleId === 0 ? '超级管理员' : (scope.row.roleId === 1 ? '管理员' : '普通用户')}}</el-tag>
    </template>
  </el-table-column>
  <el-table-column prop="phone" label="电话" width="200">
  </el-table-column>
  <el-table-column prop="operation" label="操作">
  </el-table-column>
</el-table>

3、header-cell-style设置表头样式

<el-table
  :data="tableData"
  :header-cell-style="{background: '#f3f6fd', color: '#555'}"
>

4、加上边框

<el-table
  :data="tableData"
  :header-cell-style="{background: '#f3f6fd', color: '#555'}"
  border
>

5、按钮(编辑、删除)

<el-table-column prop="operation" label="操作">
  <el-button size="small" type="success">编辑</el-button>
  <el-button size="small" type="danger">删除</el-button>
</el-table-column>

6、后端返回结果封装(Result)

在UserController中:

@PostMapping("/listPageC")
public Result listPageC(@RequestBody QueryPageParam query) {

    Page<User> page = new Page<>();
    page.setCurrent(query.getPageNum());
    page.setSize(query.getPageSize());

    IPage<User> result = userService.pageC(page);
    System.out.println("total===" + result.getTotal());

    return Result.success(result.getTotal(), result.getRecords());
}

在Main.vue中:

loadPost() {
  this.$axios.post(this.$httpUrl+'/user/listPageC',{}).then(res=>res.data).then(res=>{
    console.log(res)
    if (res.code === 200) {
      this.tableData = res.data
    }
  })
}

十七、分页处理

在Main.vue中加入分页组件:

<el-pagination
  @size-change="handleSizeChange"
  @current-change="handleCurrentChange"
  :current-page="pageNum"
  :page-sizes="[10, 20, 50, 100]"
  :page-size="pageSize"
  layout="total, sizes, prev, pager, next, jumper"
  :total="total">
</el-pagination>

在Main.vue中写一下handleSizeChange、handleCurrentChange方法:

data() {
  return {
    tableData: [],
    pageSize: 10,
    pageNum: 1,
    total: 0
  }
}

handleSizeChange(val) {
  console.log(`每页 ${val}`);
  this.pageNum = 1
  this.pageSize = val
  this.loadPost()
},
handleCurrentChange(val) {
  console.log(`当前页: ${val}`);
  this.pageNum = val
  this.loadPost()
},
loadPost() {
  this.$axios.post(this.$httpUrl+'/user/listPageC',{
    pageSize: this.pageSize,
    pageNum: this.pageNum
  }).then(res=>res.data).then(res=>{
    console.log(res)
    if (res.code === 200) {
      this.tableData = res.data
      this.total = res.total
    }
  })
}

记得要在QueryPageParam中修改一下:

private HashMap param = new HashMap();

十八、查询处理

1、增加输入框、下拉框、查询重置按钮

<div style="margin-bottom: 5px;">
  <el-input v-model="name" placeholder="请输入姓名" suffix-icon="el-icon-search" style="width: 200px;"
         @keyup.enter.native="loadPost"></el-input>
  <el-select v-model="sex" placeholder="请选择性别" style="margin-left: 5px;">
    <el-option
      v-for="item in sexs"
      :key="item.value"
      :label="item.label"
      :value="item.value">
    </el-option>
  </el-select>
  <el-button type="primary" style="margin-left: 5px;" @click="loadPost">查询</el-button>
  <el-button type="success" @click="resetParam">重置</el-button>
</div>

2、在data中增加name、sex、sexs

data() {
  return {
    tableData: [],
    pageSize: 10,
    pageNum: 1,
    total: 0,
    name: '',
    sex: '',
    sexs: [
      {
        value: '1',
        label: '男'
      }, {
        value: '0',
        label: '女'
      }
    ]
  }
}

3、修改methods

loadPost() {
  this.$axios.post(this.$httpUrl+'/user/listPageCC1',{
    pageSize: this.pageSize,
    pageNum: this.pageNum,
    param: {
      name: this.name,
      sex: this.sex
    }
  }).then(res=>res.data).then(res=>{
    console.log(res)
    if (res.code === 200) {
      this.tableData = res.data
      this.total = res.total
    }
  })
}

resetParam() {
  this.name = '';
  this.sex = ''
}

十九、新增

1、新增按钮

在Main.vue中:

<el-button type="primary" style="margin-left: 5px;" @click="add">新增</el-button>

2、弹出窗口

新增Dialog组件:

<el-dialog
  title="提示"
  :visible.sync="centerDialogVisible"
  width="30%"
  :before-close="handleClose"
  center>
  <span>需要注意的是内容是默认不居中的</span>
  <span slot="footer" class="dialog-footer">
    <el-button @click="centerDialogVisible = false">取 消</el-button>
    <el-button type="primary" @click="centerDialogVisible = false">确 定</el-button>
  </span>
</el-dialog>

data() {
  return {
    tableData: [],
    pageSize: 10,
    pageNum: 1,
    total: 0,
    name: '',
    sex: '',
    sexs: [
      {
        value: '1',
        label: '男'
      }, {
        value: '0',
        label: '女'
      }
    ],
    centerDialogVisible: false
  }
}

add() {
  this.centerDialogVisible = true
}

handleClose(done) {
  this.$confirm('确认关闭?')
      .then(() => {
        done();
      })
      .catch(() => {});
}

3、编写表单

<el-dialog
  title="新增"
  :visible.sync="centerDialogVisible"
  width="30%"
  :before-close="handleClose"
  center>
  <el-form ref="form" :model="form" label-width="120px">
    <el-form-item label="账号">
      <el-col :span="30">
        <el-input v-model="form.no"></el-input>
      </el-col>
    </el-form-item>
    <el-form-item label="姓名">
      <el-col :span="30">
        <el-input v-model="form.name"></el-input>
      </el-col>
    </el-form-item>
    <el-form-item label="密码">
      <el-col :span="30">
        <el-input v-model="form.password"></el-input>
      </el-col>
    </el-form-item>
    <el-form-item label="年龄">
      <el-col :span="30">
        <el-input v-model="form.age"></el-input>
      </el-col>
    </el-form-item>
    <el-form-item label="性别">
      <el-radio-group v-model="form.sex">
        <el-radio label="1">男</el-radio>
        <el-radio label="0">女</el-radio>
      </el-radio-group>
    </el-form-item>
    <el-form-item label="电话">
      <el-col :span="30">
        <el-input v-model="form.phone"></el-input>
      </el-col>
    </el-form-item>
  </el-form>
  <span slot="footer" class="dialog-footer">
    <el-button @click="centerDialogVisible = false">取 消</el-button>
    <el-button type="primary" @click="save">确 定</el-button>
  </span>
</el-dialog>

data() {
  return {
    tableData: [],
    pageSize: 10,
    pageNum: 1,
    total: 0,
    name: '',
    sex: '',
    sexs: [
      {
        value: '1',
        label: '男'
      }, {
        value: '0',
        label: '女'
      }
    ],
    centerDialogVisible: false,
    form: {
      no: '',
      name: '',
      password: '',
      age: '',
      sex: '0',
      phone: '',
      roleId: '2'
    }
  }
}

4、弹出数据(提示信息、列表刷新)

save() {
  this.$axios.post(this.$httpUrl+'/user/save',this.form).then(res=>res.data).then(res=>{
    console.log(res)
    if (res.code === 200) {
      this.$message({
        message: '新增成功',
        type: 'success'
      });
      this.centerDialogVisible = false
      this.loadPost()
    } else {
      this.$message.error("新增失败");
    }
  })
}

5、数据的检查

<el-form ref="form" :model="form" :rules="rules" label-width="120px">
  <el-form-item label="账号" prop="no">
    <el-col :span="30">
      <el-input v-model="form.no"></el-input>
    </el-col>
  </el-form-item>
  <el-form-item label="姓名" prop="name">
    <el-col :span="30">
      <el-input v-model="form.name"></el-input>
    </el-col>
  </el-form-item>
  <el-form-item label="密码" prop="password">
    <el-col :span="30">
      <el-input v-model="form.password"></el-input>
    </el-col>
  </el-form-item>
  <el-form-item label="年龄" prop="age">
    <el-col :span="30">
      <el-input v-model="form.age"></el-input>
    </el-col>
  </el-form-item>
  <el-form-item label="性别">
    <el-radio-group v-model="form.sex">
      <el-radio label="1">男</el-radio>
      <el-radio label="0">女</el-radio>
    </el-radio-group>
  </el-form-item>
  <el-form-item label="电话" prop="phone">
    <el-col :span="30">
      <el-input v-model="form.phone"></el-input>
    </el-col>
  </el-form-item>
</el-form>
data() {
  let checkAge = (rule, value, callback) => {
    if (value > 150) {
      callback(new Error('年龄输入过大'))
    } else {
      callback()
    }
  }
  return {
    tableData: [],
    pageSize: 10,
    pageNum: 1,
    total: 0,
    name: '',
    sex: '',
    sexs: [
      {
        value: '1',
        label: '男'
      }, {
        value: '0',
        label: '女'
      }
    ],
    centerDialogVisible: false,
    form: {
      no: '',
      name: '',
      password: '',
      age: '',
      sex: '0',
      phone: '',
      roleId: '2'
    },
    rules: {
      no: [
        { required: true, message: '请输入账号', trigger: 'blur' },
        { min: 3, max: 8, message: '长度在 3 到 8 个字符', trigger: 'blur' }
      ],
      name: [
        { required: true, message: '请输入姓名', trigger: 'blur' }
      ],
      password: [
        { required: true, message: '请输入密码', trigger: 'blur' },
        { min: 3, max: 8, message: '长度在 3 到 8 个字符', trigger: 'blur' }
      ],
      age: [
        { required: true, message: '请输入年龄', trigger: 'blur' },
        { min: 1, max: 3, message: '长度在 3 到 3 个字符', trigger: 'blur' },
        { pattern: /^([1-9][0-9]*){1,3}$/, message: '年龄必须为正整数', trigger: 'blur' },
        { validator: checkAge, trigger: 'blur' }
      ],
      phone: [
        { required: true, message: '手机号不能为空', trigger: 'blur' },
        { pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: '请输入正确的手机号码', trigger: 'blur' }
      ]
    }
  }
}

save() {
  this.$refs.form.validate((valid) => {
    if (valid) {
      this.$axios.post(this.$httpUrl+'/user/save',this.form).then(res=>res.data).then(res=>{
        console.log(res)
        if (res.code === 200) {
          this.$message({
            message: '新增成功',
            type: 'success'
          });
          this.centerDialogVisible = false
          this.loadPost()
        } else {
          this.$message.error("新增失败");
        }
      })
    } else {
      console.log('error submit!!');
      return false;
    }
  });
}

resetForm() {
  this.$refs.form.resetFields();
}

6、账号的唯一验证

let checkDuplicate = (rule, value, callback) => {
  if (this.form.id) {
    return callback();
  }
  this.$axios.get(this.$httpUrl+'/user/findByNo?no='+this.form.no).then(res=>res.data).then(res=>{
    if (res.code !== 200) {
      callback()
    } else {
      callback(new Error('账号已经存在'))
    }
  })
};

在UserController中:

@GetMapping("/findByNo")
public Result findByNo(@RequestParam String no) {
    List<User> list = userService.lambdaQuery().eq(User::getNo, no).list();
    return list.size() > 0 ? Result.success(list) : Result.fail();
}
no: [
  { required: true, message: '请输入账号', trigger: 'blur' },
  { min: 3, max: 8, message: '长度在 3 到 8 个字符', trigger: 'blur' },
  { validator: checkDuplicate, trigger: 'blur' }
]

7、表单重置

使用resetFields的注意事项:设置prop属性,该属性值要与model值相同,如果不设置这个属性,resetFields是不生效的

二十、编辑

<el-table-column prop="operation" label="操作">
  <template slot-scope="scope">
    <el-button size="small" type="success" @click="mod(scope.row)">编辑</el-button>
    <el-button size="small" type="danger" @click="del">删除</el-button>
  </template>
</el-table-column>
mod(row) {
  console.log(row)

  this.centerDialogVisible = true
  this.$nextTick(()=>{
    //赋值到表单
    this.form.id = row.id
    this.form.no = row.no
    this.form.name = row.name
    this.form.password = ''
    this.form.age = row.age + ''
    this.form.sex = row.sex + ''
    this.form.phone = row.phone
    this.form.roleId = row.roleId
  })

}
doSave() {
  this.$axios.post(this.$httpUrl+'/user/save',this.form).then(res=>res.data).then(res=>{
    console.log(res)
    if (res.code === 200) {
      this.$message({
        message: '新增成功',
        type: 'success'
      });
      this.centerDialogVisible = false
      this.loadPost()
    } else {
      this.$message.error("新增失败");
    }
  })
},
doMod() {
  this.$axios.post(this.$httpUrl+'/user/update',this.form).then(res=>res.data).then(res=>{
    console.log(res)
    if (res.code === 200) {
      this.$message({
        message: '新增成功',
        type: 'success'
      });
      this.centerDialogVisible = false
      this.loadPost()
    } else {
      this.$message.error("新增失败");
    }
  })
},
save() {
  this.$refs.form.validate((valid) => {
    if (valid) {
      if (this.form.id) {
        this.doMod()
      } else {
        this.doSave()
      }
    } else {
      console.log('error submit!!');
      return false;
    }
  });
}

在UserController中:

//更新
@PostMapping("/update")
public Result update(@RequestBody User user) {
    return userService.updateById(user) ? Result.success() : Result.fail();
}

二十一、删除

<el-table-column prop="operation" label="操作">
  <template slot-scope="scope">
    <el-button size="small" type="success" @click="mod(scope.row)">编辑</el-button>
    <el-popconfirm
      title="确定删除吗?"
      @confirm="del(scope.row.id)"
      style="margin-left: 5px;"
    >
      <el-button slot="reference" size="small" type="danger">删除</el-button>
    </el-popconfirm>
  </template>
</el-table-column>
del(id) {
  console.log(id)
  this.$axios.get(this.$httpUrl+'/user/del?id='+id).then(res=>res.data).then(res=>{
    if (res.code === 200) {
      this.$message({
        message: '删除成功',
        type: 'success'
      });
      this.loadPost()
    } else {
      this.$message.error("删除失败");
    }
  })
}

在UserController中:

//删除
@GetMapping("/del")
public Result del(@RequestParam String id) {
    return userService.removeById(id) ? Result.success() : Result.fail();
}

二十二、登录

1、登录页面

<template>
  <div class="loginBody">
    <div class="loginDiv">
      <div class="login-content">
        <h1 class="login-title">用户登录</h1>
        <el-form :model="loginForm" label-width="100px"
                 :rules="rules" ref="loginForm">
          <el-form-item label="账号" prop="no">
            <el-input style="width: 200px;" type="text" v-model="loginForm.no"
                      autocomplete="off" size="small"></el-input>
          </el-form-item>
          <el-form-item label="密码" prop="password">
            <el-input style="width: 200px;" type="password" v-model="loginForm.password"
                      show-password autocomplete="off" size="small" @keyup.enter.native="confirm">
            </el-input>
          </el-form-item>
          <el-form-item>
            <el-button type="primary" @click="confirm" :disabled="confirm_disabled">确 定</el-button>
          </el-form-item>
        </el-form>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: "Login",
  data() {
    return {
      confirm_disabled: false,
      loginForm: {
        no: '',
        password: ''
      },
      rules: {
        no: [
          { required: true, message: '请输入账号', trigger: 'blur' }
        ],
        password: [
          { required: true, message: '请输入密码', trigger: 'blur'}
        ]
      }
    }
  },
  methods: {
    confirm() {
      this.confirm_disabled = true
      this.$refs.loginForm.validate((valid) => {
        if (valid) {  //valid成功为true,失败为false
          //去后台验证用户名密码
          this.$axios.post(this.$httpUrl+'/user/login', this.loginForm).then(res=>res.data).then(res=>{
            console.log(res)
            if (res.code === 200) {
              //存储
              sessionStorage.setItem("CurUser", JSON.stringify(res.data))
              //跳转到主页
              this.$router.replace("/Index")
            } else {
              this.confirm_disabled = false
              alert('校验失败,用户名或密码错误!')
              return false
            }
          })
        } else {
          this.confirm_disabled = false
          console.log('校验失败')
          return false
        }
      })
    }
  }
}
</script>

<style scoped>
  .loginBody {
    position: absolute;
    width: 100%;
    height: 100%;
    background-color: #B3C0D1;
  }
  .loginDiv {
    position: absolute;
    top: 50%;
    left: 50%;
    margin-top: -200px;
    margin-left: -250px;
    width: 450px;
    height: 330px;
    background: #fff;
    border-radius: 5%;
  }
  .login-title {
    margin: 20px 0;
    text-align: center;
  }
  .login-content {
    width: 400px;
    height: 250px;
    position: absolute;
    top: 25px;
    left: 25px;
  }
</style>

2、后台查询代码

//登录
@PostMapping("/login")
public Result login(@RequestBody User user) {
    List<User> list = userService.lambdaQuery()
            .eq(User::getNo, user.getNo())
            .eq(User::getPassword, user.getPassword()).list();
    return list.size() > 0 ? Result.success(list.get(0)) : Result.fail();
}

3、登录页面的路由

  • 安装路由插件
npm i vue-router@3.5.4
  • 创建路由文件

在wms-web文件夹的src目录下新建router/index.js:

import VueRouter  from "vue-router";

const routes = [
    {
        path: '/',
        name: 'login',
        component: ()=>import('../components/Login.vue')
    },
    {
        path: '/Index',
        name: 'index',
        component: ()=>import('../components/Index.vue')
    }
]

const router = new VueRouter({
    mode: 'history',
    routes
})

export default router;
  • main.js注册
import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import App from './App.vue';
import './assets/global.css';
import axios from 'axios';
import VueRouter from "vue-router";
import router from "@/router";

Vue.config.productionTip = false
Vue.prototype.$axios = axios
Vue.prototype.$httpUrl = 'http://localhost:8090'

// Vue.use(ElementUI);
Vue.use(ElementUI, {size: 'small'})
Vue.use(VueRouter)

new Vue({
  router,
  render: h => h(App),
}).$mount('#app')

  • 在App.vue中
<template>
  <div id="app">
    <router-view/>
  </div>
</template>

<script>

export default {
  name: 'App',
  components: {

  }
}
</script>

<style>
  html, body, #app {
    height: 100%;
  }
</style>

二十三、退出登录

1、展示名字

<el-dropdown>
  <span style="margin-right: 5px;">{{ user.name }}</span><i class="el-icon-arrow-down"></i>
  <el-dropdown-menu slot="dropdown">
    <el-dropdown-item @click.native="toUser">个人中心</el-dropdown-item>
    <el-dropdown-item @click.native="logout">退出登录</el-dropdown-item>
  </el-dropdown-menu>
</el-dropdown>

data() {
  return {
    user: JSON.parse(sessionStorage.getItem('CurUser'))
  }
}

2、退出登录事件

<el-dropdown-item @click.native="logout">退出登录</el-dropdown-item>

3、退出跳转、清空相关数据

logout() {
  console.log('logout')
  this.$router.push("/")
  sessionStorage.clear()
}

4、退出确认

logout() {
  console.log('logout')
  this.$confirm('您确定要退出登录吗?', '提示', {
    confirmButtonText: '确定',
    type: 'warning',
    center: true
  }).then(() => {
    this.$message({
      type: 'success',
      message: '退出登录成功'
    })
    this.$router.push("/")
    sessionStorage.clear()
  }).catch(() => {
    this.$message({
      type: 'info',
      message: '已取消退出登录'
    })
  })
}

二十四、首页个人中心

1、编写页面

<template>
  <div>
    <h1 style="font-size: 50px; text-align: center; margin-bottom: 10px;">{{'欢迎你!'+user.name}}</h1>
    <el-descriptions style="margin-left: 30px;" title="个人中心" :column="2" size="40" border>
      <el-descriptions-item>
        <template slot="label">
          <i class="el-icon-s-custom"></i>
          账号
        </template>
        {{ user.no }}
      </el-descriptions-item>
      <el-descriptions-item>
        <template slot="label">
          <i class="el-icon-mobile-phone"></i>
          电话
        </template>
        {{ user.phone }}
      </el-descriptions-item>
      <el-descriptions-item>
        <template slot="label">
          <i class="el-icon-location-outline"></i>
          性别
        </template>
        <el-tag
          :type="user.sex === '1' ? 'primary' : 'danger'"
          disable-transitions><i :class="user.sex==='1'?'el-icon-male':'el-icon-female'"></i>{{user.sex==='1'?'男':'女'}}</el-tag>
      </el-descriptions-item>
      <el-descriptions-item>
        <template slot="label">
          <i class="el-icon-tickets"></i>
          角色
        </template>
        <el-tag
            type="success"
            disable-transitions>{{user.roleId==='0'?'超级管理员':(user.roleId==='1'?'管理员':'普通用户')}}</el-tag>
      </el-descriptions-item>
    </el-descriptions>

<!--    <DateUtils></DateUtils>-->
  </div>
</template>

<script>
// import DateUtils from "./DateUtils";
export default {
  name: "Home",
  // components: {DateUtils},
  data() {
    return {
      user: JSON.parse(sessionStorage.getItem('CurUser'))
    }
  },
  computed: {

  },
  methods: {
    // init() {
    //   this.user = JSON.parse(sessionStorage.getItem('CurUser'))
    // }
  }
}
</script>

<style scoped>

</style>

2、路由跳转

在index.js中:

import VueRouter  from "vue-router";

const routes = [
    {
        path: '/',
        name: 'login',
        component: ()=>import('../components/Login.vue')
    },
    {
        path: '/Index',
        name: 'index',
        component: ()=>import('../components/Index.vue'),
        children: [
            {
                path: '/Home',
                name: 'home',
                meta: {
                  title: '首页'
                },
                component: ()=>import('../components/Home.vue')
            }
        ]
    }
]

const router = new VueRouter({
    mode: 'history',
    routes
})

const VueRouterPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(to) {
    return VueRouterPush.call(this, to).catch(err => err)
}

export default router;

在Header.vue中:

created() {
  this.$router.push("/Home")
}

在Index.vue中:

<el-main style="height: 100%">
  <router-view/>
</el-main>

3、路由错误解决

const VueRouterPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(to) {
    return VueRouterPush.call(this, to).catch(err => err)
}

二十五、菜单跳转

1、菜单增加router、高亮

<template>
  <el-menu
      default-active="/Home"
      background-color="#545c64"
      text-color="#fff"
      active-text-color="#ffd04b"
      style="height: 100%;"
      :collapse="isCollapse"
      :collapse-transition="false"
      router
  >
    <el-menu-item index="/Home">
      <i class="el-icon-s-home"></i>
      <span slot="title">首页</span>
    </el-menu-item>

    <el-menu-item index="/Nav1">
      <i class="el-icon-s-flag"></i>
      <span slot="title">导航一</span>
    </el-menu-item>

    <el-menu-item index="/Nav2">
      <i class="el-icon-s-shop"></i>
      <span slot="title">导航二</span>
    </el-menu-item>
  </el-menu>
</template>

2、配置子菜单

const routes = [
    {
        path: '/',
        name: 'login',
        component: ()=>import('../components/Login.vue')
    },
    {
        path: '/Index',
        name: 'index',
        component: ()=>import('../components/Index.vue'),
        children: [
            {
                path: '/Home',
                name: 'home',
                meta: {
                  title: '首页'
                },
                component: ()=>import('../components/Home.vue')
            },
            {
                path: '/Admin',
                name: 'admin',
                meta: {
                    title: '管理员管理'
                },
                component: ()=>import('../components/admin/AdminManage.vue')
            },
            {
                path: '/User',
                name: 'user',
                meta: {
                    title: '用户管理'
                },
                component: ()=>import('../components/user/UserManage.vue')
            }
        ]
    }
]

3、模拟动态menu

<el-menu-item :index="'/'+item.menuClick" v-for="(item, i) in menu" :key="i">
  <i :class="item.menuIcon"></i>
  <span slot="title">{{ item.menuName }}</span>
</el-menu-item>

data() {
  return {
    menu: [
      {
        menuClick: 'Admin',
        menuName: '管理员管理',
        menuIcon: 'el-icon-s-custom'
      },
      {
        menuClick: 'User',
        menuName: '用户管理',
        menuIcon: 'el-icon-user-solid'
      }
    ]
  }
}

二十六、动态路由

1、设计menu表和数据

CREATE TABLE `menu` (
	`id` int(11) NOT NULL,
	`menuCode` varchar(8) DEFAULT NULL COMMENT '菜单编码',
	`menuName` varchar(16) DEFAULT NULL COMMENT '菜单名字',
	`menuLevel` varchar(2) DEFAULT NULL COMMENT '菜单级别',
	`menuParentCode` varchar(8) DEFAULT NULL COMMENT '菜单的父code',
	`menuClick` varchar(16) DEFAULT NULL COMMENT '点击触发的函数',
	`menuRight` varchar(8) DEFAULT NULL COMMENT '权限 0超级管理员,1管理员,2普通用户,可以用逗号组合使用',
	`menuComponent` varchar(200) DEFAULT NULL,
	`menuIcon` varchar(100) DEFAULT NULL,
	PRIMARY KEY(`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

BEGIN;
INSERT INTO `menu` VALUES (1, '001', '管理员管理', '1', NULL, 'Admin', '0', 'admin/AdminManage.vue', 'el-icon-s-custom');
INSERT INTO `menu` VALUES (2, '002', '用户管理', '1', NULL, 'User', '0,1', 'user/UserManage.vue', 'el-icon-user-solid');
COMMIT;

2、生成menu对应的后端代码

使用代码生成器自动生成即可

测试一下:

package com.wms.controller;


import com.wms.common.Result;
import com.wms.entity.Menu;
import com.wms.entity.User;
import com.wms.service.MenuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author ghost
 * @since 2023-10-16
 */
@RestController
@RequestMapping("/menu")
public class MenuController {

    @Autowired
    private MenuService menuService;

    @GetMapping("/list")
    public Result list(@RequestParam String roleId) {
        List<Menu> list = menuService.lambdaQuery().like(Menu::getMenuright, roleId).list();
        return Result.success(list);
    }

}

3、返回数据

 //登录
@PostMapping("/login")
public Result login(@RequestBody User user) {
    List<User> list = userService.lambdaQuery()
            .eq(User::getNo, user.getNo())
            .eq(User::getPassword, user.getPassword()).list();

    if (list.size() > 0) {
        User user1 = list.get(0);
        List<Menu> menuList = menuService.lambdaQuery().like(Menu::getMenuright, user1.getRoleId()).list();
        HashMap<String, Object> res = new HashMap<>();
        res.put("user", user1);
        res.put("menu", menuList);
        return Result.success(res);
    }

    return Result.fail();
}

在Login.vue中:

if (res.code === 200) {
  //存储
  sessionStorage.setItem("CurUser", JSON.stringify(res.data.user))
  console.log(res.data.menu)
  //跳转到主页
  this.$router.replace("/Index")
}

4、vuex状态管理

  • 安装vuex
npm i vuex@3.0.0
  • 编写store
import vue from 'vue'
import Vuex from 'vuex'
vue.use(Vuex)

export default new Vuex.Store({
    state: {
        menu: []
    },
    mutations: {
        setMenu(state, menuList) {
            state.menu = menuList
        }
    },
    getters: {
        getMenu(state) {
            return state.menu
        }
    }
})
  • main.js注册
import store from "./store"

new Vue({
  router,
  store,
  render: h => h(App),
}).$mount('#app')

5、生成menu数据

在Aside.vue中:

<el-menu-item :index="'/'+item.menuclick" v-for="(item, i) in menu" :key="i">
  <i :class="item.menuicon"></i>
  <span slot="title">{{ item.menuname }}</span>
</el-menu-item>

computed: {
  "menu": {
    get() {
      return this.$store.state.menu
    }
  }
}

6、生成路由数据

在store/index.js中:

import vue from 'vue'
import Vuex from 'vuex'
import router, {resetRouter} from "@/router";
vue.use(Vuex)

function addNewRoute(menuList) {

    console.log(menuList)

    let routes = router.options.routes
    console.log(routes)
    routes.forEach(routeItem=>{
        if (routeItem.path === "/Index") {
            menuList.forEach(menu=>{
                let childRoute = {
                    path: '/'+menu.menuclick,
                    name: menu.menuname,
                    meta: {
                        title: menu.menuname
                    },
                    component: ()=>import('../components/'+menu.menucomponent)
                }
                routeItem.children.push(childRoute)
            })
        }
    })
    resetRouter()
    router.addRoutes(routes)


}

export default new Vuex.Store({
    state: {
        menu: []
    },
    mutations: {
        setMenu(state, menuList) {
            state.menu = menuList
            addNewRoute(menuList)
        }
    },
    getters: {
        getMenu(state) {
            return state.menu
        }
    }
})

在route/index.js中:

import VueRouter  from "vue-router";

const routes = [
    {
        path: '/',
        name: 'login',
        component: ()=>import('../components/Login.vue')
    },
    {
        path: '/Index',
        name: 'index',
        component: ()=>import('../components/Index.vue'),
        children: [
            {
                path: '/Home',
                name: 'home',
                meta: {
                  title: '首页'
                },
                component: ()=>import('../components/Home.vue')
            }
            // {
            //     path: '/Admin',
            //     name: 'admin',
            //     meta: {
            //         title: '管理员管理'
            //     },
            //     component: ()=>import('../components/admin/AdminManage.vue')
            // },
            // {
            //     path: '/User',
            //     name: 'user',
            //     meta: {
            //         title: '用户管理'
            //     },
            //     component: ()=>import('../components/user/UserManage.vue')
            // }
        ]
    }
]

const router = new VueRouter({
    mode: 'history',
    routes
})

export function resetRouter() {
    router.matcher = new VueRouter({
        mode: 'history',
        routes: []
    }).matcher
}

const VueRouterPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(to) {
    return VueRouterPush.call(this, to).catch(err => err)
}

export default router;

错误处理:

export function resetRouter() {
    router.matcher = new VueRouter({
        mode: 'history',
        routes: []
    }).matcher
}

二十七、管理员管理、用户管理

把Main.vue中的代码复制到AdminManage.vue和UserManage.vue中,进行微调即可,如下所示:

在AdminManage.vue中:

<template>
  <div>
    <div style="margin-bottom: 5px;">
      <el-input v-model="name" placeholder="请输入姓名" suffix-icon="el-icon-search" style="width: 200px;"
                @keyup.enter.native="loadPost"></el-input>
      <el-select v-model="sex" placeholder="请选择性别" style="margin-left: 5px;">
        <el-option
            v-for="item in sexs"
            :key="item.value"
            :label="item.label"
            :value="item.value">
        </el-option>
      </el-select>
      <el-button type="primary" style="margin-left: 5px;" @click="loadPost">查询</el-button>
      <el-button type="success" @click="resetParam">重置</el-button>
      <el-button type="primary" style="margin-left: 5px;" @click="add">新增</el-button>

    </div>
    <el-table
        :data="tableData"
        :header-cell-style="{background: '#f3f6fd', color: '#555'}"
        border
    >
      <el-table-column prop="id" label="ID" width="80">
      </el-table-column>
      <el-table-column prop="no" label="账号" width="180">
      </el-table-column>
      <el-table-column prop="name" label="姓名" width="180">
      </el-table-column>
      <el-table-column prop="age" label="年龄" width="120">
      </el-table-column>
      <el-table-column prop="sex" label="性别" width="120">
        <template slot-scope="scope">
          <el-tag
              :type="scope.row.sex === 1 ? 'primary' : 'success'"
              disable-transitions>{{scope.row.sex === 1 ? '男' : '女'}}</el-tag>
        </template>
      </el-table-column>
      <el-table-column prop="roleId" label="角色" width="120">
        <template slot-scope="scope">
          <el-tag
              :type="scope.row.roleId === 0 ? 'danger' : (scope.row.roleId === 1 ? 'primary' : 'success')"
              disable-transitions>{{scope.row.roleId === 0 ? '超级管理员' : (scope.row.roleId === 1 ? '管理员' : '普通用户')}}</el-tag>
        </template>
      </el-table-column>
      <el-table-column prop="phone" label="电话" width="210">
      </el-table-column>
      <el-table-column prop="operation" label="操作">
        <template slot-scope="scope">
          <el-button size="small" type="success" @click="mod(scope.row)">编辑</el-button>
          <el-popconfirm
              title="确定删除吗?"
              @confirm="del(scope.row.id)"
              style="margin-left: 5px;"
          >
            <el-button slot="reference" size="small" type="danger">删除</el-button>
          </el-popconfirm>
        </template>
      </el-table-column>
    </el-table>
    <el-pagination
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
        :current-page="pageNum"
        :page-sizes="[10, 20, 50, 100]"
        :page-size="pageSize"
        layout="total, sizes, prev, pager, next, jumper"
        :total="total">
    </el-pagination>
    <el-dialog
        title="新增"
        :visible.sync="centerDialogVisible"
        width="30%"
        :before-close="handleClose"
        center>
      <el-form ref="form" :model="form" :rules="rules" label-width="120px">
        <el-form-item label="账号" prop="no">
          <el-col :span="30">
            <el-input v-model="form.no"></el-input>
          </el-col>
        </el-form-item>
        <el-form-item label="姓名" prop="name">
          <el-col :span="30">
            <el-input v-model="form.name"></el-input>
          </el-col>
        </el-form-item>
        <el-form-item label="密码" prop="password">
          <el-col :span="30">
            <el-input v-model="form.password"></el-input>
          </el-col>
        </el-form-item>
        <el-form-item label="年龄" prop="age">
          <el-col :span="30">
            <el-input v-model="form.age"></el-input>
          </el-col>
        </el-form-item>
        <el-form-item label="性别" prop="sex">
          <el-radio-group v-model="form.sex">
            <el-radio label="1">男</el-radio>
            <el-radio label="0">女</el-radio>
          </el-radio-group>
        </el-form-item>
        <el-form-item label="电话" prop="phone">
          <el-col :span="30">
            <el-input v-model="form.phone"></el-input>
          </el-col>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="centerDialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="save">确 定</el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
export default {
  name: "AdminManage",
  data() {
    let checkAge = (rule, value, callback) => {
      if (value > 150) {
        callback(new Error('年龄输入过大'))
      } else {
        callback()
      }
    };
    let checkDuplicate = (rule, value, callback) => {
      if (this.form.id) {
        return callback();
      }
      this.$axios.get(this.$httpUrl+'/user/findByNo?no='+this.form.no).then(res=>res.data).then(res=>{
        if (res.code !== 200) {
          callback()
        } else {
          callback(new Error('账号已经存在'))
        }
      })
    };
    return {
      tableData: [],
      pageSize: 10,
      pageNum: 1,
      total: 0,
      name: '',
      sex: '',
      sexs: [
        {
          value: '0',
          label: '女'
        },
        {
          value: '1',
          label: '男'
        }
      ],
      centerDialogVisible: false,
      form: {
        id: '',
        no: '',
        name: '',
        password: '',
        age: '',
        sex: '0',
        phone: '',
        roleId: '1'
      },
      rules: {
        no: [
          { required: true, message: '请输入账号', trigger: 'blur' },
          { min: 3, max: 8, message: '长度在 3 到 8 个字符', trigger: 'blur' },
          { validator: checkDuplicate, trigger: 'blur' }
        ],
        name: [
          { required: true, message: '请输入姓名', trigger: 'blur' }
        ],
        password: [
          { required: true, message: '请输入密码', trigger: 'blur' },
          { min: 3, max: 8, message: '长度在 3 到 8 个字符', trigger: 'blur' }
        ],
        age: [
          { required: true, message: '请输入年龄', trigger: 'blur' },
          { min: 1, max: 3, message: '长度在 3 到 3 个字符', trigger: 'blur' },
          { pattern: /^([1-9][0-9]*){1,3}$/, message: '年龄必须为正整数', trigger: 'blur' },
          { validator: checkAge, trigger: 'blur' }
        ],
        phone: [
          { required: true, message: '手机号不能为空', trigger: 'blur' },
          { pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: '请输入正确的手机号码', trigger: 'blur' }
        ]
      }
    }
  },
  methods: {
    loadGet() {
      this.$axios.get(this.$httpUrl+'/user/list').then(res=>res.data).then(res=>{
        console.log(res)
      })
    },
    loadPost() {
      this.$axios.post(this.$httpUrl+'/user/listPageCC1',{
        pageSize: this.pageSize,
        pageNum: this.pageNum,
        param: {
          name: this.name,
          sex: this.sex,
          roleId: '1'
        }
      }).then(res=>res.data).then(res=>{
        console.log(res)
        if (res.code === 200) {
          this.tableData = res.data
          this.total = res.total
        }
      })
    },
    handleSizeChange(val) {
      console.log(`每页 ${val} 条`);
      this.pageNum = 1
      this.pageSize = val
      this.loadPost()
    },
    handleCurrentChange(val) {
      console.log(`当前页: ${val}`);
      this.pageNum = val
      this.loadPost()
    },
    resetParam() {
      this.name = '';
      this.sex = ''
    },
    add() {
      this.centerDialogVisible = true
      this.$nextTick(()=>{
        this.resetForm()
      })
    },
    handleClose(done) {
      this.$confirm('确认关闭?')
          .then(() => {
            done();
          })
          .catch(() => {});
    },
    doSave() {
      this.$axios.post(this.$httpUrl+'/user/save',this.form).then(res=>res.data).then(res=>{
        console.log(res)
        if (res.code === 200) {
          this.$message({
            message: '新增成功',
            type: 'success'
          });
          this.centerDialogVisible = false
          this.loadPost()
        } else {
          this.$message.error("新增失败");
        }
      })
    },
    doMod() {
      this.$axios.post(this.$httpUrl+'/user/update',this.form).then(res=>res.data).then(res=>{
        console.log(res)
        if (res.code === 200) {
          this.$message({
            message: '修改成功',
            type: 'success'
          });
          this.centerDialogVisible = false
          this.loadPost()
        } else {
          this.$message.error("修改失败");
        }
      })
    },
    save() {
      this.$refs.form.validate((valid) => {
        if (valid) {
          if (this.form.id) {
            this.doMod()
          } else {
            this.doSave()
          }
        } else {
          console.log('error submit!!');
          return false;
        }
      });
    },
    resetForm() {
      this.$refs.form.resetFields();
    },
    mod(row) {
      console.log(row)

      this.centerDialogVisible = true
      this.$nextTick(()=>{
        //赋值到表单
        this.form.id = row.id
        this.form.no = row.no
        this.form.name = row.name
        this.form.password = ''
        this.form.age = row.age + ''
        this.form.sex = row.sex + ''
        this.form.phone = row.phone
        this.form.roleId = row.roleId
      })

    },
    del(id) {
      console.log(id)
      this.$axios.get(this.$httpUrl+'/user/del?id='+id).then(res=>res.data).then(res=>{
        if (res.code === 200) {
          this.$message({
            message: '删除成功',
            type: 'success'
          });
          this.loadPost()
        } else {
          this.$message.error("删除失败");
        }
      })
    }
  },
  beforeMount() {
    // this.loadGet()
    this.loadPost()
  }
}
</script>

<style scoped>

</style>

在UserManage.vue中:

<template>
  <div>
    <div style="margin-bottom: 5px;">
      <el-input v-model="name" placeholder="请输入姓名" suffix-icon="el-icon-search" style="width: 200px;"
                @keyup.enter.native="loadPost"></el-input>
      <el-select v-model="sex" placeholder="请选择性别" style="margin-left: 5px;">
        <el-option
            v-for="item in sexs"
            :key="item.value"
            :label="item.label"
            :value="item.value">
        </el-option>
      </el-select>
      <el-button type="primary" style="margin-left: 5px;" @click="loadPost">查询</el-button>
      <el-button type="success" @click="resetParam">重置</el-button>
      <el-button type="primary" style="margin-left: 5px;" @click="add">新增</el-button>

    </div>
    <el-table
        :data="tableData"
        :header-cell-style="{background: '#f3f6fd', color: '#555'}"
        border
    >
      <el-table-column prop="id" label="ID" width="80">
      </el-table-column>
      <el-table-column prop="no" label="账号" width="180">
      </el-table-column>
      <el-table-column prop="name" label="姓名" width="180">
      </el-table-column>
      <el-table-column prop="age" label="年龄" width="120">
      </el-table-column>
      <el-table-column prop="sex" label="性别" width="120">
        <template slot-scope="scope">
          <el-tag
              :type="scope.row.sex === 1 ? 'primary' : 'success'"
              disable-transitions>{{scope.row.sex === 1 ? '男' : '女'}}</el-tag>
        </template>
      </el-table-column>
      <el-table-column prop="roleId" label="角色" width="120">
        <template slot-scope="scope">
          <el-tag
              :type="scope.row.roleId === 0 ? 'danger' : (scope.row.roleId === 1 ? 'primary' : 'success')"
              disable-transitions>{{scope.row.roleId === 0 ? '超级管理员' : (scope.row.roleId === 1 ? '管理员' : '普通用户')}}</el-tag>
        </template>
      </el-table-column>
      <el-table-column prop="phone" label="电话" width="210">
      </el-table-column>
      <el-table-column prop="operation" label="操作">
        <template slot-scope="scope">
          <el-button size="small" type="success" @click="mod(scope.row)">编辑</el-button>
          <el-popconfirm
              title="确定删除吗?"
              @confirm="del(scope.row.id)"
              style="margin-left: 5px;"
          >
            <el-button slot="reference" size="small" type="danger">删除</el-button>
          </el-popconfirm>
        </template>
      </el-table-column>
    </el-table>
    <el-pagination
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
        :current-page="pageNum"
        :page-sizes="[10, 20, 50, 100]"
        :page-size="pageSize"
        layout="total, sizes, prev, pager, next, jumper"
        :total="total">
    </el-pagination>
    <el-dialog
        title="新增"
        :visible.sync="centerDialogVisible"
        width="30%"
        :before-close="handleClose"
        center>
      <el-form ref="form" :model="form" :rules="rules" label-width="120px">
        <el-form-item label="账号" prop="no">
          <el-col :span="30">
            <el-input v-model="form.no"></el-input>
          </el-col>
        </el-form-item>
        <el-form-item label="姓名" prop="name">
          <el-col :span="30">
            <el-input v-model="form.name"></el-input>
          </el-col>
        </el-form-item>
        <el-form-item label="密码" prop="password">
          <el-col :span="30">
            <el-input v-model="form.password"></el-input>
          </el-col>
        </el-form-item>
        <el-form-item label="年龄" prop="age">
          <el-col :span="30">
            <el-input v-model="form.age"></el-input>
          </el-col>
        </el-form-item>
        <el-form-item label="性别" prop="sex">
          <el-radio-group v-model="form.sex">
            <el-radio label="1">男</el-radio>
            <el-radio label="0">女</el-radio>
          </el-radio-group>
        </el-form-item>
        <el-form-item label="电话" prop="phone">
          <el-col :span="30">
            <el-input v-model="form.phone"></el-input>
          </el-col>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="centerDialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="save">确 定</el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
export default {
  name: "UserManage",
  data() {
    let checkAge = (rule, value, callback) => {
      if (value > 150) {
        callback(new Error('年龄输入过大'))
      } else {
        callback()
      }
    };
    let checkDuplicate = (rule, value, callback) => {
      if (this.form.id) {
        return callback();
      }
      this.$axios.get(this.$httpUrl+'/user/findByNo?no='+this.form.no).then(res=>res.data).then(res=>{
        if (res.code !== 200) {
          callback()
        } else {
          callback(new Error('账号已经存在'))
        }
      })
    };
    return {
      tableData: [],
      pageSize: 10,
      pageNum: 1,
      total: 0,
      name: '',
      sex: '',
      sexs: [
        {
          value: '0',
          label: '女'
        },
        {
          value: '1',
          label: '男'
        }
      ],
      centerDialogVisible: false,
      form: {
        id: '',
        no: '',
        name: '',
        password: '',
        age: '',
        sex: '0',
        phone: '',
        roleId: '2'
      },
      rules: {
        no: [
          { required: true, message: '请输入账号', trigger: 'blur' },
          { min: 3, max: 8, message: '长度在 3 到 8 个字符', trigger: 'blur' },
          { validator: checkDuplicate, trigger: 'blur' }
        ],
        name: [
          { required: true, message: '请输入姓名', trigger: 'blur' }
        ],
        password: [
          { required: true, message: '请输入密码', trigger: 'blur' },
          { min: 3, max: 8, message: '长度在 3 到 8 个字符', trigger: 'blur' }
        ],
        age: [
          { required: true, message: '请输入年龄', trigger: 'blur' },
          { min: 1, max: 3, message: '长度在 3 到 3 个字符', trigger: 'blur' },
          { pattern: /^([1-9][0-9]*){1,3}$/, message: '年龄必须为正整数', trigger: 'blur' },
          { validator: checkAge, trigger: 'blur' }
        ],
        phone: [
          { required: true, message: '手机号不能为空', trigger: 'blur' },
          { pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: '请输入正确的手机号码', trigger: 'blur' }
        ]
      }
    }
  },
  methods: {
    loadGet() {
      this.$axios.get(this.$httpUrl+'/user/list').then(res=>res.data).then(res=>{
        console.log(res)
      })
    },
    loadPost() {
      this.$axios.post(this.$httpUrl+'/user/listPageCC1',{
        pageSize: this.pageSize,
        pageNum: this.pageNum,
        param: {
          name: this.name,
          sex: this.sex,
          roleId: '2'
        }
      }).then(res=>res.data).then(res=>{
        console.log(res)
        if (res.code === 200) {
          this.tableData = res.data
          this.total = res.total
        }
      })
    },
    handleSizeChange(val) {
      console.log(`每页 ${val} 条`);
      this.pageNum = 1
      this.pageSize = val
      this.loadPost()
    },
    handleCurrentChange(val) {
      console.log(`当前页: ${val}`);
      this.pageNum = val
      this.loadPost()
    },
    resetParam() {
      this.name = '';
      this.sex = ''
    },
    add() {
      this.centerDialogVisible = true
      this.$nextTick(()=>{
        this.resetForm()
      })
    },
    handleClose(done) {
      this.$confirm('确认关闭?')
          .then(() => {
            done();
          })
          .catch(() => {});
    },
    doSave() {
      this.$axios.post(this.$httpUrl+'/user/save',this.form).then(res=>res.data).then(res=>{
        console.log(res)
        if (res.code === 200) {
          this.$message({
            message: '新增成功',
            type: 'success'
          });
          this.centerDialogVisible = false
          this.loadPost()
        } else {
          this.$message.error("新增失败");
        }
      })
    },
    doMod() {
      this.$axios.post(this.$httpUrl+'/user/update',this.form).then(res=>res.data).then(res=>{
        console.log(res)
        if (res.code === 200) {
          this.$message({
            message: '修改成功',
            type: 'success'
          });
          this.centerDialogVisible = false
          this.loadPost()
        } else {
          this.$message.error("修改失败");
        }
      })
    },
    save() {
      this.$refs.form.validate((valid) => {
        if (valid) {
          if (this.form.id) {
            this.doMod()
          } else {
            this.doSave()
          }
        } else {
          console.log('error submit!!');
          return false;
        }
      });
    },
    resetForm() {
      this.$refs.form.resetFields();
    },
    mod(row) {
      console.log(row)

      this.centerDialogVisible = true
      this.$nextTick(()=>{
        //赋值到表单
        this.form.id = row.id
        this.form.no = row.no
        this.form.name = row.name
        this.form.password = ''
        this.form.age = row.age + ''
        this.form.sex = row.sex + ''
        this.form.phone = row.phone
        this.form.roleId = row.roleId
      })

    },
    del(id) {
      console.log(id)
      this.$axios.get(this.$httpUrl+'/user/del?id='+id).then(res=>res.data).then(res=>{
        if (res.code === 200) {
          this.$message({
            message: '删除成功',
            type: 'success'
          });
          this.loadPost()
        } else {
          this.$message.error("删除失败");
        }
      })
    }
  },
  beforeMount() {
    // this.loadGet()
    this.loadPost()
  }
}
</script>

<style scoped>

</style>

在UserController中:

@PostMapping("/listPageCC1")
public Result listPageCC1(@RequestBody QueryPageParam query) {

    Page<User> page = new Page<>();
    page.setCurrent(query.getPageNum());
    page.setSize(query.getPageSize());

    HashMap param = query.getParam();

    LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
    if (StringUtils.isNotBlank((String) param.get("name")) && !"null".equals((String) param.get("name"))) {
        lambdaQueryWrapper.like(User::getName, (String) param.get("name"));
    }
    if (StringUtils.isNotBlank((String) param.get("sex")) && !"null".equals((String) param.get("sex"))) {
        lambdaQueryWrapper.eq(User::getSex, (String) param.get("sex"));
    }
    if (StringUtils.isNotBlank((String) param.get("roleId"))) {
        lambdaQueryWrapper.eq(User::getRoleId, (String) param.get("roleId"));
    }

    IPage<User> result = userService.pageCC(page, lambdaQueryWrapper);
    System.out.println("total===" + result.getTotal());

    return Result.success(result.getTotal(), result.getRecords());
}

二十八、仓库管理

1、表设计

CREATE TABLE `storage`(
	`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
    `name` varchar(100) NOT NULL COMMENT '仓库名',
    `remark` varchar(1000) DEFAULT NULL COMMENT '备注',
    PRIMARY KEY(`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;

2、根据表生成后端代码

使用代码生成器

3、编写后端增删改查代码

在StorageController中:

package com.wms.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.wms.common.QueryPageParam;
import com.wms.common.Result;
import com.wms.entity.Storage;
import com.wms.service.StorageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;

/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author ghost
 * @since 2023-10-18
 */
@RestController
@RequestMapping("/storage")
public class StorageController {

    @Autowired
    private StorageService storageService;

    //新增
    @PostMapping("/save")
    public Result save(@RequestBody Storage storage) {
        return storageService.save(storage)?Result.success():Result.fail();
    }

    //更新
    @PostMapping("/update")
    public Result update(@RequestBody Storage storage) {
        return storageService.updateById(storage) ? Result.success() : Result.fail();
    }

    //删除
    @GetMapping("/del")
    public Result del(@RequestParam String id) {
        return storageService.removeById(id) ? Result.success() : Result.fail();
    }

    @PostMapping("/listPage")
    public Result listPage(@RequestBody QueryPageParam query) {

        Page<Storage> page = new Page<>();
        page.setCurrent(query.getPageNum());
        page.setSize(query.getPageSize());

        HashMap param = query.getParam();

        LambdaQueryWrapper<Storage> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        if (StringUtils.isNotBlank((String) param.get("name")) && !"null".equals((String) param.get("name"))) {
            lambdaQueryWrapper.like(Storage::getName, (String) param.get("name"));
        }

        IPage<Storage> result = storageService.pageCC(page, lambdaQueryWrapper);
        System.out.println("total===" + result.getTotal());

        return Result.success(result.getTotal(), result.getRecords());
    }

}

在StorageService中:

package com.wms.service;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.wms.entity.Storage;
import com.baomidou.mybatisplus.extension.service.IService;

/**
 * <p>
 *  服务类
 * </p>
 *
 * @author ghost
 * @since 2023-10-18
 */
public interface StorageService extends IService<Storage> {

    IPage<Storage> pageCC(IPage<Storage> page, Wrapper<Storage> wrapper);

}

在StorageServiceImpl中:

package com.wms.service.impl;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.wms.entity.Storage;
import com.wms.mapper.StorageMapper;
import com.wms.service.StorageService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author ghost
 * @since 2023-10-18
 */
@Service
public class StorageServiceImpl extends ServiceImpl<StorageMapper, Storage> implements StorageService {

    @Autowired
    private StorageMapper storageMapper;

    @Override
    public IPage<Storage> pageCC(IPage<Storage> page, Wrapper<Storage> wrapper) {
        return storageMapper.pageCC(page, wrapper);
    }
}

在StorageMapper中:

package com.wms.mapper;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.wms.entity.Storage;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

/**
 * <p>
 *  Mapper 接口
 * </p>
 *
 * @author ghost
 * @since 2023-10-18
 */
@Mapper
public interface StorageMapper extends BaseMapper<Storage> {
    IPage<Storage> pageCC(IPage<Storage> page, @Param(Constants.WRAPPER) Wrapper<Storage> wrapper);
}

在StorageMapper.xml中:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wms.mapper.StorageMapper">

    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.wms.entity.Storage">
        <id column="id" property="id" />
        <result column="name" property="name" />
        <result column="remark" property="remark" />
    </resultMap>

    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List">
        id, name, remark
    </sql>
    <select id="pageCC" resultType="com.wms.entity.Storage">
        select * from storage ${ew.customSqlSegment}
    </select>

</mapper>

4、postman测试查询代码

5、编写前端相关代码

在StorageManage.vue中:

<template>
  <div>
    <div style="margin-bottom: 5px;">
      <el-input v-model="name" placeholder="请输入仓库名" suffix-icon="el-icon-search" style="width: 200px;"
                @keyup.enter.native="loadPost"></el-input>
      <el-button type="primary" style="margin-left: 5px;" @click="loadPost">查询</el-button>
      <el-button type="success" @click="resetParam">重置</el-button>
      <el-button type="primary" style="margin-left: 5px;" @click="add">新增</el-button>

    </div>
    <el-table
        :data="tableData"
        :header-cell-style="{background: '#f3f6fd', color: '#555'}"
        border
    >
      <el-table-column prop="id" label="ID" width="80">
      </el-table-column>
      <el-table-column prop="name" label="仓库名" width="180">
      </el-table-column>
      <el-table-column prop="remark" label="备注">
      </el-table-column>
      <el-table-column prop="operation" label="操作" width="280">
        <template slot-scope="scope">
          <el-button size="small" type="success" @click="mod(scope.row)">编辑</el-button>
          <el-popconfirm
              title="确定删除吗?"
              @confirm="del(scope.row.id)"
              style="margin-left: 5px;"
          >
            <el-button slot="reference" size="small" type="danger">删除</el-button>
          </el-popconfirm>
        </template>
      </el-table-column>
    </el-table>
    <el-pagination
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
        :current-page="pageNum"
        :page-sizes="[10, 20, 50, 100]"
        :page-size="pageSize"
        layout="total, sizes, prev, pager, next, jumper"
        :total="total">
    </el-pagination>
    <el-dialog
        title="新增"
        :visible.sync="centerDialogVisible"
        width="30%"
        :before-close="handleClose"
        center>
      <el-form ref="form" :model="form" :rules="rules" label-width="120px">
        <el-form-item label="仓库名" prop="name">
          <el-col :span="30">
            <el-input v-model="form.name"></el-input>
          </el-col>
        </el-form-item>
        <el-form-item label="备注" prop="remark">
          <el-col :span="30">
            <el-input type="textarea" v-model="form.remark"></el-input>
          </el-col>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="centerDialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="save">确 定</el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
export default {
  name: "StorageManage",
  data() {
    return {
      tableData: [],
      pageSize: 10,
      pageNum: 1,
      total: 0,
      name: '',
      centerDialogVisible: false,
      form: {
        id: '',
        name: '',
        remark: ''
      },
      rules: {
        name: [
          { required: true, message: '请输入仓库名', trigger: 'blur' }
        ]
      }
    }
  },
  methods: {
    loadPost() {
      this.$axios.post(this.$httpUrl+'/storage/listPage',{
        pageSize: this.pageSize,
        pageNum: this.pageNum,
        param: {
          name: this.name
        }
      }).then(res=>res.data).then(res=>{
        console.log(res)
        if (res.code === 200) {
          this.tableData = res.data
          this.total = res.total
        }
      })
    },
    handleSizeChange(val) {
      console.log(`每页 ${val} 条`);
      this.pageNum = 1
      this.pageSize = val
      this.loadPost()
    },
    handleCurrentChange(val) {
      console.log(`当前页: ${val}`);
      this.pageNum = val
      this.loadPost()
    },
    resetParam() {
      this.name = '';
      this.sex = ''
    },
    add() {
      this.centerDialogVisible = true
      this.$nextTick(()=>{
        this.resetForm()
      })
    },
    handleClose(done) {
      this.$confirm('确认关闭?')
          .then(() => {
            done();
          })
          .catch(() => {});
    },
    doSave() {
      this.$axios.post(this.$httpUrl+'/storage/save',this.form).then(res=>res.data).then(res=>{
        console.log(res)
        if (res.code === 200) {
          this.$message({
            message: '新增成功',
            type: 'success'
          });
          this.centerDialogVisible = false
          this.loadPost()
        } else {
          this.$message.error("新增失败");
        }
      })
    },
    doMod() {
      this.$axios.post(this.$httpUrl+'/storage/update',this.form).then(res=>res.data).then(res=>{
        console.log(res)
        if (res.code === 200) {
          this.$message({
            message: '修改成功',
            type: 'success'
          });
          this.centerDialogVisible = false
          this.loadPost()
        } else {
          this.$message.error("修改失败");
        }
      })
    },
    save() {
      this.$refs.form.validate((valid) => {
        if (valid) {
          if (this.form.id) {
            this.doMod()
          } else {
            this.doSave()
          }
        } else {
          console.log('error submit!!');
          return false;
        }
      });
    },
    resetForm() {
      this.$refs.form.resetFields();
    },
    mod(row) {
      this.centerDialogVisible = true
      this.$nextTick(()=>{
        //赋值到表单
        this.form.id = row.id
        this.form.name = row.name
        this.form.remark = row.remark
      })

    },
    del(id) {
      console.log(id)
      this.$axios.get(this.$httpUrl+'/storage/del?id='+id).then(res=>res.data).then(res=>{
        if (res.code === 200) {
          this.$message({
            message: '删除成功',
            type: 'success'
          });
          this.loadPost()
        } else {
          this.$message.error("删除失败");
        }
      })
    }
  },
  beforeMount() {
    this.loadPost()
  }
}
</script>

<style scoped>

</style>

二十九、物品类型管理

1、表设计

CREATE TABLE `goodstype`(
	`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
    `name` varchar(100) NOT NULL COMMENT '分类名',
    `remark` varchar(1000) DEFAULT NULL COMMENT '备注',
    PRIMARY KEY(`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

2、根据表生成后端代码

使用代码生成器

3、编写后端增删改查代码

在GoodstypeController中:

package com.wms.controller;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.wms.common.QueryPageParam;
import com.wms.common.Result;
import com.wms.entity.Goodstype;
import com.wms.service.GoodstypeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;

/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author ghost
 * @since 2023-10-18
 */
@RestController
@RequestMapping("/goodstype")
public class GoodstypeController {

    @Autowired
    private GoodstypeService goodstypeService;

    //新增
    @PostMapping("/save")
    public Result save(@RequestBody Goodstype goodstype) {
        return goodstypeService.save(goodstype)?Result.success():Result.fail();
    }

    //更新
    @PostMapping("/update")
    public Result update(@RequestBody Goodstype goodstype) {
        return goodstypeService.updateById(goodstype) ? Result.success() : Result.fail();
    }

    //删除
    @GetMapping("/del")
    public Result del(@RequestParam String id) {
        return goodstypeService.removeById(id) ? Result.success() : Result.fail();
    }

    @PostMapping("/listPage")
    public Result listPage(@RequestBody QueryPageParam query) {

        Page<Goodstype> page = new Page<>();
        page.setCurrent(query.getPageNum());
        page.setSize(query.getPageSize());

        HashMap param = query.getParam();

        LambdaQueryWrapper<Goodstype> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        if (StringUtils.isNotBlank((String) param.get("name")) && !"null".equals((String) param.get("name"))) {
            lambdaQueryWrapper.like(Goodstype::getName, (String) param.get("name"));
        }

        IPage<Goodstype> result = goodstypeService.pageCC(page, lambdaQueryWrapper);
        System.out.println("total===" + result.getTotal());

        return Result.success(result.getTotal(), result.getRecords());
    }

}

在GoodstypeService中:

package com.wms.service;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.wms.entity.Goodstype;
import com.baomidou.mybatisplus.extension.service.IService;

/**
 * <p>
 *  服务类
 * </p>
 *
 * @author ghost
 * @since 2023-10-18
 */
public interface GoodstypeService extends IService<Goodstype> {
    IPage<Goodstype> pageCC(IPage<Goodstype> page, Wrapper<Goodstype> wrapper);
}

在GoodstypeServiceImpl中:

package com.wms.service.impl;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.wms.entity.Goodstype;
import com.wms.mapper.GoodstypeMapper;
import com.wms.service.GoodstypeService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author ghost
 * @since 2023-10-18
 */
@Service
public class GoodstypeServiceImpl extends ServiceImpl<GoodstypeMapper, Goodstype> implements GoodstypeService {

    @Autowired
    private GoodstypeMapper goodstypeMapper;

    @Override
    public IPage<Goodstype> pageCC(IPage<Goodstype> page, Wrapper<Goodstype> wrapper) {
        return goodstypeMapper.pageCC(page, wrapper);
    }

}

在GoodstypeMapper中:

package com.wms.mapper;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.wms.entity.Goodstype;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

/**
 * <p>
 *  Mapper 接口
 * </p>
 *
 * @author ghost
 * @since 2023-10-18
 */
@Mapper
public interface GoodstypeMapper extends BaseMapper<Goodstype> {
    IPage<Goodstype> pageCC(IPage<Goodstype> page, @Param(Constants.WRAPPER) Wrapper<Goodstype> wrapper);
}

在GoodstypeMapper.xml中:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wms.mapper.GoodstypeMapper">

    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.wms.entity.Goodstype">
        <id column="id" property="id" />
        <result column="name" property="name" />
        <result column="remark" property="remark" />
    </resultMap>

    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List">
        id, name, remark
    </sql>
    <select id="pageCC" resultType="com.wms.entity.Goodstype">
        select * from goodstype ${ew.customSqlSegment}
    </select>

</mapper>

4、postman测试查询代码

5、编写前端相关代码

在GoodstypeManage.vue中:

<template>
  <div>
    <div style="margin-bottom: 5px;">
      <el-input v-model="name" placeholder="请输入物品类别" suffix-icon="el-icon-search" style="width: 200px;"
                @keyup.enter.native="loadPost"></el-input>
      <el-button type="primary" style="margin-left: 5px;" @click="loadPost">查询</el-button>
      <el-button type="success" @click="resetParam">重置</el-button>
      <el-button type="primary" style="margin-left: 5px;" @click="add">新增</el-button>

    </div>
    <el-table
        :data="tableData"
        :header-cell-style="{background: '#f3f6fd', color: '#555'}"
        border
    >
      <el-table-column prop="id" label="ID" width="80">
      </el-table-column>
      <el-table-column prop="name" label="物品类别" width="180">
      </el-table-column>
      <el-table-column prop="remark" label="备注">
      </el-table-column>
      <el-table-column prop="operation" label="操作" width="280">
        <template slot-scope="scope">
          <el-button size="small" type="success" @click="mod(scope.row)">编辑</el-button>
          <el-popconfirm
              title="确定删除吗?"
              @confirm="del(scope.row.id)"
              style="margin-left: 5px;"
          >
            <el-button slot="reference" size="small" type="danger">删除</el-button>
          </el-popconfirm>
        </template>
      </el-table-column>
    </el-table>
    <el-pagination
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
        :current-page="pageNum"
        :page-sizes="[10, 20, 50, 100]"
        :page-size="pageSize"
        layout="total, sizes, prev, pager, next, jumper"
        :total="total">
    </el-pagination>
    <el-dialog
        title="新增"
        :visible.sync="centerDialogVisible"
        width="30%"
        :before-close="handleClose"
        center>
      <el-form ref="form" :model="form" :rules="rules" label-width="120px">
        <el-form-item label="物品类别" prop="name">
          <el-col :span="30">
            <el-input v-model="form.name"></el-input>
          </el-col>
        </el-form-item>
        <el-form-item label="备注" prop="remark">
          <el-col :span="30">
            <el-input type="textarea" v-model="form.remark"></el-input>
          </el-col>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="centerDialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="save">确 定</el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
export default {
  name: "GoodstypeManage",
  data() {
    return {
      tableData: [],
      pageSize: 10,
      pageNum: 1,
      total: 0,
      name: '',
      centerDialogVisible: false,
      form: {
        id: '',
        name: '',
        remark: ''
      },
      rules: {
        name: [
          { required: true, message: '请输入物品类别', trigger: 'blur' }
        ]
      }
    }
  },
  methods: {
    loadPost() {
      this.$axios.post(this.$httpUrl+'/goodstype/listPage',{
        pageSize: this.pageSize,
        pageNum: this.pageNum,
        param: {
          name: this.name
        }
      }).then(res=>res.data).then(res=>{
        console.log(res)
        if (res.code === 200) {
          this.tableData = res.data
          this.total = res.total
        }
      })
    },
    handleSizeChange(val) {
      console.log(`每页 ${val} 条`);
      this.pageNum = 1
      this.pageSize = val
      this.loadPost()
    },
    handleCurrentChange(val) {
      console.log(`当前页: ${val}`);
      this.pageNum = val
      this.loadPost()
    },
    resetParam() {
      this.name = '';
      this.sex = ''
    },
    add() {
      this.centerDialogVisible = true
      this.$nextTick(()=>{
        this.resetForm()
      })
    },
    handleClose(done) {
      this.$confirm('确认关闭?')
          .then(() => {
            done();
          })
          .catch(() => {});
    },
    doSave() {
      this.$axios.post(this.$httpUrl+'/goodstype/save',this.form).then(res=>res.data).then(res=>{
        console.log(res)
        if (res.code === 200) {
          this.$message({
            message: '新增成功',
            type: 'success'
          });
          this.centerDialogVisible = false
          this.loadPost()
        } else {
          this.$message.error("新增失败");
        }
      })
    },
    doMod() {
      this.$axios.post(this.$httpUrl+'/goodstype/update',this.form).then(res=>res.data).then(res=>{
        console.log(res)
        if (res.code === 200) {
          this.$message({
            message: '修改成功',
            type: 'success'
          });
          this.centerDialogVisible = false
          this.loadPost()
        } else {
          this.$message.error("修改失败");
        }
      })
    },
    save() {
      this.$refs.form.validate((valid) => {
        if (valid) {
          if (this.form.id) {
            this.doMod()
          } else {
            this.doSave()
          }
        } else {
          console.log('error submit!!');
          return false;
        }
      });
    },
    resetForm() {
      this.$refs.form.resetFields();
    },
    mod(row) {
      this.centerDialogVisible = true
      this.$nextTick(()=>{
        //赋值到表单
        this.form.id = row.id
        this.form.name = row.name
        this.form.remark = row.remark
      })

    },
    del(id) {
      console.log(id)
      this.$axios.get(this.$httpUrl+'/goodstype/del?id='+id).then(res=>res.data).then(res=>{
        if (res.code === 200) {
          this.$message({
            message: '删除成功',
            type: 'success'
          });
          this.loadPost()
        } else {
          this.$message.error("删除失败");
        }
      })
    }
  },
  beforeMount() {
    this.loadPost()
  }
}
</script>

<style scoped>

</style>

三十、物品管理

1、表设计

CREATE TABLE `goods`(
	`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
    `name` varchar(100) NOT NULL COMMENT '货名',
    `storage` int(11) NOT NULL COMMENT '仓库',
    `goodsType` int(11) NOT NULL COMMENT '分类',
    `count` int(11) DEFAULT NULL COMMENT '数量',
    `remark` varchar(1000) DEFAULT NULL COMMENT '备注',
    PRIMARY KEY(`id`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8;

2、根据表生成后端代码

使用代码生成器

3、编写后端增删改查代码

在GoodsController中:

package com.wms.controller;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.wms.common.QueryPageParam;
import com.wms.common.Result;
import com.wms.entity.Goods;
import com.wms.service.GoodsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;

/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author ghost
 * @since 2023-10-19
 */
@RestController
@RequestMapping("/goods")
public class GoodsController {

    @Autowired
    private GoodsService goodsService;

    //新增
    @PostMapping("/save")
    public Result save(@RequestBody Goods goods) {
        return goodsService.save(goods)?Result.success():Result.fail();
    }

    //更新
    @PostMapping("/update")
    public Result update(@RequestBody Goods goods) {
        return goodsService.updateById(goods) ? Result.success() : Result.fail();
    }

    //删除
    @GetMapping("/del")
    public Result del(@RequestParam String id) {
        return goodsService.removeById(id) ? Result.success() : Result.fail();
    }

    @PostMapping("/listPage")
    public Result listPage(@RequestBody QueryPageParam query) {

        Page<Goods> page = new Page<>();
        page.setCurrent(query.getPageNum());
        page.setSize(query.getPageSize());

        HashMap param = query.getParam();

        LambdaQueryWrapper<Goods> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        if (StringUtils.isNotBlank((String) param.get("name")) && !"null".equals((String) param.get("name"))) {
            lambdaQueryWrapper.like(Goods::getName, (String) param.get("name"));
        }

        IPage<Goods> result = goodsService.pageCC(page, lambdaQueryWrapper);
        System.out.println("total===" + result.getTotal());

        return Result.success(result.getTotal(), result.getRecords());
    }

}

在GoodsService中:

package com.wms.service;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.wms.entity.Goods;
import com.baomidou.mybatisplus.extension.service.IService;

/**
 * <p>
 *  服务类
 * </p>
 *
 * @author ghost
 * @since 2023-10-19
 */
public interface GoodsService extends IService<Goods> {
    IPage<Goods> pageCC(IPage<Goods> page, Wrapper<Goods> wrapper);
}

在GoodsServiceImpl中:

package com.wms.service.impl;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.wms.entity.Goods;
import com.wms.mapper.GoodsMapper;
import com.wms.service.GoodsService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author ghost
 * @since 2023-10-19
 */
@Service
public class GoodsServiceImpl extends ServiceImpl<GoodsMapper, Goods> implements GoodsService {
    @Autowired
    private GoodsMapper goodsMapper;

    @Override
    public IPage<Goods> pageCC(IPage<Goods> page, Wrapper<Goods> wrapper) {
        return goodsMapper.pageCC(page, wrapper);
    }
}

在GoodsMapper中:

package com.wms.mapper;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.wms.entity.Goods;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

/**
 * <p>
 *  Mapper 接口
 * </p>
 *
 * @author ghost
 * @since 2023-10-19
 */
@Mapper
public interface GoodsMapper extends BaseMapper<Goods> {
    IPage<Goods> pageCC(IPage<Goods> page, @Param(Constants.WRAPPER) Wrapper<Goods> wrapper);
}

在GoodsMapper.xml中:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wms.mapper.GoodsMapper">

    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.wms.entity.Goods">
        <id column="id" property="id" />
        <result column="name" property="name" />
        <result column="storage" property="storage" />
        <result column="goodsType" property="goodstype" />
        <result column="count" property="count" />
        <result column="remark" property="remark" />
    </resultMap>

    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List">
        id, name, storage, goodsType, count, remark
    </sql>
    <select id="pageCC" resultType="com.wms.entity.Goods">
        select * from goods ${ew.customSqlSegment}

    </select>

</mapper>

4、postman测试查询代码

5、编写前端相关代码

在GoodsManage.vue中:

<template>
  <div>
    <div style="margin-bottom: 5px;">
      <el-input v-model="name" placeholder="请输入物品名" suffix-icon="el-icon-search" style="width: 200px;"
                @keyup.enter.native="loadPost"></el-input>
      <el-button type="primary" style="margin-left: 5px;" @click="loadPost">查询</el-button>
      <el-button type="success" @click="resetParam">重置</el-button>
      <el-button type="primary" style="margin-left: 5px;" @click="add">新增</el-button>

    </div>
    <el-table
        :data="tableData"
        :header-cell-style="{background: '#f3f6fd', color: '#555'}"
        border
    >
      <el-table-column prop="id" label="ID" width="80">
      </el-table-column>
      <el-table-column prop="name" label="物品名" width="180">
      </el-table-column>
      <el-table-column prop="storage" label="仓库" width="180">
      </el-table-column>
      <el-table-column prop="goodstype" label="物品类别" width="180">
      </el-table-column>
      <el-table-column prop="count" label="数量" width="180">
      </el-table-column>
      <el-table-column prop="remark" label="备注">
      </el-table-column>
      <el-table-column prop="operation" label="操作" width="280">
        <template slot-scope="scope">
          <el-button size="small" type="success" @click="mod(scope.row)">编辑</el-button>
          <el-popconfirm
              title="确定删除吗?"
              @confirm="del(scope.row.id)"
              style="margin-left: 5px;"
          >
            <el-button slot="reference" size="small" type="danger">删除</el-button>
          </el-popconfirm>
        </template>
      </el-table-column>
    </el-table>
    <el-pagination
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
        :current-page="pageNum"
        :page-sizes="[10, 20, 50, 100]"
        :page-size="pageSize"
        layout="total, sizes, prev, pager, next, jumper"
        :total="total">
    </el-pagination>
    <el-dialog
        :title="title"
        :visible.sync="centerDialogVisible"
        width="30%"
        :before-close="handleClose"
        center>
      <el-form ref="form" :model="form" :rules="rules" label-width="120px">
        <el-form-item label="物品名" prop="name">
          <el-col :span="30">
            <el-input v-model="form.name"></el-input>
          </el-col>
        </el-form-item>
        <el-form-item label="仓库" prop="storage">
          <el-col :span="30">
            <el-input v-model="form.storage"></el-input>
          </el-col>
        </el-form-item>
        <el-form-item label="物品类别" prop="goodstype">
          <el-col :span="30">
            <el-input v-model="form.goodstype"></el-input>
          </el-col>
        </el-form-item>
        <el-form-item label="数量" prop="count">
          <el-col :span="30">
            <el-input v-model="form.count"></el-input>
          </el-col>
        </el-form-item>
        <el-form-item label="备注" prop="remark">
          <el-col :span="30">
            <el-input type="textarea" v-model="form.remark"></el-input>
          </el-col>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="centerDialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="save">确 定</el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
export default {
  name: "GoodsManage",
  data() {
    let checkCount = (rule, value, callback) => {
      if (value > 9999) {
        callback(new Error('数量输入过大'))
      } else {
        callback()
      }
    }
    return {
      tableData: [],
      pageSize: 10,
      pageNum: 1,
      total: 0,
      name: '',
      title:'',
      centerDialogVisible: false,
      form: {
        id: '',
        name: '',
        storage: '',
        goodstype: '',
        count: '',
        remark: ''
      },
      rules: {
        name: [
          { required: true, message: '请输入物品名', trigger: 'blur' }
        ],
        count: [
          { required: true, message: '请输入数量', trigger: 'blur' },
          { pattern: /^([1-9][0-9]*){1,4}$/, message: '数量必须为正整数', trigger: 'blur' },
          { validator: checkCount, trigger: 'blur' }
        ]
      }
    }
  },
  methods: {
    loadPost() {
      this.$axios.post(this.$httpUrl+'/goods/listPage',{
        pageSize: this.pageSize,
        pageNum: this.pageNum,
        param: {
          name: this.name
        }
      }).then(res=>res.data).then(res=>{
        console.log(res)
        if (res.code === 200) {
          this.tableData = res.data
          this.total = res.total
        }
      })
    },
    handleSizeChange(val) {
      console.log(`每页 ${val} 条`);
      this.pageNum = 1
      this.pageSize = val
      this.loadPost()
    },
    handleCurrentChange(val) {
      console.log(`当前页: ${val}`);
      this.pageNum = val
      this.loadPost()
    },
    resetParam() {
      this.name = '';
      this.sex = ''
    },
    add() {
      this.title='新增'
      this.centerDialogVisible = true
      this.$nextTick(()=>{
        this.resetForm()
      })
    },
    handleClose(done) {
      this.$confirm('确认关闭?')
          .then(() => {
            done();
          })
          .catch(() => {});
    },
    doSave() {
      this.$axios.post(this.$httpUrl+'/goods/save',this.form).then(res=>res.data).then(res=>{
        console.log(res)
        if (res.code === 200) {
          this.$message({
            message: '新增成功',
            type: 'success'
          });
          this.centerDialogVisible = false
          this.loadPost()
        } else {
          this.$message.error("新增失败");
        }
      })
    },
    doMod() {
      this.$axios.post(this.$httpUrl+'/goods/update',this.form).then(res=>res.data).then(res=>{
        console.log(res)
        if (res.code === 200) {
          this.$message({
            message: '修改成功',
            type: 'success'
          });
          this.centerDialogVisible = false
          this.loadPost()
        } else {
          this.$message.error("修改失败");
        }
      })
    },
    save() {
      this.$refs.form.validate((valid) => {
        if (valid) {
          if (this.form.id) {
            this.doMod()
          } else {
            this.doSave()
          }
        } else {
          console.log('error submit!!');
          return false;
        }
      });
    },
    resetForm() {
      this.$refs.form.resetFields();
    },
    mod(row) {
      this.title='修改'
      this.centerDialogVisible = true
      this.$nextTick(()=>{
        //赋值到表单
        this.form.id = row.id
        this.form.name = row.name
        this.form.storage = row.storage
        this.form.goodstype = row.goodstype
        this.form.count = row.count
        this.form.remark = row.remark
      })

    },
    del(id) {
      console.log(id)
      this.$axios.get(this.$httpUrl+'/goods/del?id='+id).then(res=>res.data).then(res=>{
        if (res.code === 200) {
          this.$message({
            message: '删除成功',
            type: 'success'
          });
          this.loadPost()
        } else {
          this.$message.error("删除失败");
        }
      })
    }
  },
  beforeMount() {
    this.loadPost()
  }
}
</script>

<style scoped>

</style>

6、仓库和分类的列表展现

在StorageController中:

@GetMapping("/list")
public Result list() {
    List<Storage> list = storageService.list();
    return Result.success(list);
}

在GoodstypeController中:

@GetMapping("/list")
public Result list() {
    List<Goodstype> list = goodstypeService.list();
    return Result.success(list);
}

在GoodsManage.vue中:

<template>
  <div>
    <div style="margin-bottom: 5px;">
      <el-input v-model="name" placeholder="请输入物品名" suffix-icon="el-icon-search" style="width: 200px;"
                @keyup.enter.native="loadPost"></el-input>
      <el-button type="primary" style="margin-left: 5px;" @click="loadPost">查询</el-button>
      <el-button type="success" @click="resetParam">重置</el-button>
      <el-button type="primary" style="margin-left: 5px;" @click="add">新增</el-button>

    </div>
    <el-table
        :data="tableData"
        :header-cell-style="{background: '#f3f6fd', color: '#555'}"
        border
    >
      <el-table-column prop="id" label="ID" width="80">
      </el-table-column>
      <el-table-column prop="name" label="物品名" width="180">
      </el-table-column>
      <el-table-column prop="storage" label="仓库" width="180" :formatter="formatStorage">
      </el-table-column>
      <el-table-column prop="goodstype" label="物品类别" width="180" :formatter="formatGoodstype">
      </el-table-column>
      <el-table-column prop="count" label="数量" width="180">
      </el-table-column>
      <el-table-column prop="remark" label="备注">
      </el-table-column>
      <el-table-column prop="operation" label="操作" width="280">
        <template slot-scope="scope">
          <el-button size="small" type="success" @click="mod(scope.row)">编辑</el-button>
          <el-popconfirm
              title="确定删除吗?"
              @confirm="del(scope.row.id)"
              style="margin-left: 5px;"
          >
            <el-button slot="reference" size="small" type="danger">删除</el-button>
          </el-popconfirm>
        </template>
      </el-table-column>
    </el-table>
    <el-pagination
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
        :current-page="pageNum"
        :page-sizes="[10, 20, 50, 100]"
        :page-size="pageSize"
        layout="total, sizes, prev, pager, next, jumper"
        :total="total">
    </el-pagination>
    <el-dialog
        :title="title"
        :visible.sync="centerDialogVisible"
        width="30%"
        :before-close="handleClose"
        center>
      <el-form ref="form" :model="form" :rules="rules" label-width="120px">
        <el-form-item label="物品名" prop="name">
          <el-col :span="30">
            <el-input v-model="form.name"></el-input>
          </el-col>
        </el-form-item>
        <el-form-item label="仓库" prop="storage">
          <el-col :span="30">
            <el-input v-model="form.storage"></el-input>
          </el-col>
        </el-form-item>
        <el-form-item label="物品类别" prop="goodstype">
          <el-col :span="30">
            <el-input v-model="form.goodstype"></el-input>
          </el-col>
        </el-form-item>
        <el-form-item label="数量" prop="count">
          <el-col :span="30">
            <el-input v-model="form.count"></el-input>
          </el-col>
        </el-form-item>
        <el-form-item label="备注" prop="remark">
          <el-col :span="30">
            <el-input type="textarea" v-model="form.remark"></el-input>
          </el-col>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="centerDialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="save">确 定</el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
export default {
  name: "GoodsManage",
  data() {
    let checkCount = (rule, value, callback) => {
      if (value > 9999) {
        callback(new Error('数量输入过大'))
      } else {
        callback()
      }
    }
    return {
      goodstypeData: [],
      storageData: [],
      tableData: [],
      pageSize: 10,
      pageNum: 1,
      total: 0,
      name: '',
      title:'',
      centerDialogVisible: false,
      form: {
        id: '',
        name: '',
        storage: '',
        goodstype: '',
        count: '',
        remark: ''
      },
      rules: {
        name: [
          { required: true, message: '请输入物品名', trigger: 'blur' }
        ],
        count: [
          { required: true, message: '请输入数量', trigger: 'blur' },
          { pattern: /^([1-9][0-9]*){1,4}$/, message: '数量必须为正整数', trigger: 'blur' },
          { validator: checkCount, trigger: 'blur' }
        ]
      }
    }
  },
  methods: {
    loadPost() {
      this.$axios.post(this.$httpUrl+'/goods/listPage',{
        pageSize: this.pageSize,
        pageNum: this.pageNum,
        param: {
          name: this.name
        }
      }).then(res=>res.data).then(res=>{
        console.log(res)
        if (res.code === 200) {
          this.tableData = res.data
          this.total = res.total
        }
      })
    },
    loadStorage() {
      this.$axios.get(this.$httpUrl+'/storage/list').then(res=>res.data).then(res=>{
        console.log(res)
        if (res.code === 200) {
          this.storageData = res.data
        }
      })
    },
    loadGoodstype() {
      this.$axios.get(this.$httpUrl+'/goodstype/list').then(res=>res.data).then(res=>{
        console.log(res)
        if (res.code === 200) {
          this.goodstypeData = res.data
        }
      })
    },
    handleSizeChange(val) {
      console.log(`每页 ${val} 条`);
      this.pageNum = 1
      this.pageSize = val
      this.loadPost()
    },
    handleCurrentChange(val) {
      console.log(`当前页: ${val}`);
      this.pageNum = val
      this.loadPost()
    },
    resetParam() {
      this.name = '';
      this.sex = ''
    },
    add() {
      this.title='新增'
      this.centerDialogVisible = true
      this.$nextTick(()=>{
        this.resetForm()
      })
    },
    handleClose(done) {
      this.$confirm('确认关闭?')
          .then(() => {
            done();
          })
          .catch(() => {});
    },
    doSave() {
      this.$axios.post(this.$httpUrl+'/goods/save',this.form).then(res=>res.data).then(res=>{
        console.log(res)
        if (res.code === 200) {
          this.$message({
            message: '新增成功',
            type: 'success'
          });
          this.centerDialogVisible = false
          this.loadPost()
        } else {
          this.$message.error("新增失败");
        }
      })
    },
    doMod() {
      this.$axios.post(this.$httpUrl+'/goods/update',this.form).then(res=>res.data).then(res=>{
        console.log(res)
        if (res.code === 200) {
          this.$message({
            message: '修改成功',
            type: 'success'
          });
          this.centerDialogVisible = false
          this.loadPost()
        } else {
          this.$message.error("修改失败");
        }
      })
    },
    save() {
      this.$refs.form.validate((valid) => {
        if (valid) {
          if (this.form.id) {
            this.doMod()
          } else {
            this.doSave()
          }
        } else {
          console.log('error submit!!');
          return false;
        }
      });
    },
    resetForm() {
      this.$refs.form.resetFields();
    },
    mod(row) {
      this.title='修改'
      this.centerDialogVisible = true
      this.$nextTick(()=>{
        //赋值到表单
        this.form.id = row.id
        this.form.name = row.name
        this.form.storage = row.storage
        this.form.goodstype = row.goodstype
        this.form.count = row.count
        this.form.remark = row.remark
      })

    },
    del(id) {
      console.log(id)
      this.$axios.get(this.$httpUrl+'/goods/del?id='+id).then(res=>res.data).then(res=>{
        if (res.code === 200) {
          this.$message({
            message: '删除成功',
            type: 'success'
          });
          this.loadPost()
        } else {
          this.$message.error("删除失败");
        }
      })
    },
    formatStorage(row) {
      let temp = this.storageData.find(item=>{
        return item.id === row.storage
      })
      return temp && temp.name
    },
    formatGoodstype(row) {
      let temp = this.goodstypeData.find(item=>{
        return item.id === row.goodstype
      })
      return temp && temp.name
    }
  },
  beforeMount() {
    this.loadPost()
    this.loadStorage()
    this.loadGoodstype()
  }
}
</script>

<style scoped>

</style>

7、查询条件中增加仓库和分类的条件

在GoodsManage.vue中:

<template>
  <div>
    <div style="margin-bottom: 5px;">
      <el-input v-model="name" placeholder="请输入物品名" suffix-icon="el-icon-search" style="width: 200px;"
                @keyup.enter.native="loadPost"></el-input>
      <el-select style="margin-left: 5px;" v-model="storage" placeholder="请选择仓库">
        <el-option
            v-for="item in storageData"
            :key="item.id"
            :label="item.name"
            :value="item.id">
        </el-option>
      </el-select>
      <el-select style="margin-left: 5px;" v-model="goodstype" placeholder="请选择物品类别">
        <el-option
            v-for="item in goodstypeData"
            :key="item.id"
            :label="item.name"
            :value="item.id">
        </el-option>
      </el-select>
      <el-button type="primary" style="margin-left: 5px;" @click="loadPost">查询</el-button>
      <el-button type="success" @click="resetParam">重置</el-button>
      <el-button type="primary" style="margin-left: 5px;" @click="add">新增</el-button>

    </div>
    <el-table
        :data="tableData"
        :header-cell-style="{background: '#f3f6fd', color: '#555'}"
        border
    >
      <el-table-column prop="id" label="ID" width="80">
      </el-table-column>
      <el-table-column prop="name" label="物品名" width="180">
      </el-table-column>
      <el-table-column prop="storage" label="仓库" width="180" :formatter="formatStorage">
      </el-table-column>
      <el-table-column prop="goodstype" label="物品类别" width="180" :formatter="formatGoodstype">
      </el-table-column>
      <el-table-column prop="count" label="数量" width="180">
      </el-table-column>
      <el-table-column prop="remark" label="备注">
      </el-table-column>
      <el-table-column prop="operation" label="操作" width="280">
        <template slot-scope="scope">
          <el-button size="small" type="success" @click="mod(scope.row)">编辑</el-button>
          <el-popconfirm
              title="确定删除吗?"
              @confirm="del(scope.row.id)"
              style="margin-left: 5px;"
          >
            <el-button slot="reference" size="small" type="danger">删除</el-button>
          </el-popconfirm>
        </template>
      </el-table-column>
    </el-table>
    <el-pagination
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
        :current-page="pageNum"
        :page-sizes="[10, 20, 50, 100]"
        :page-size="pageSize"
        layout="total, sizes, prev, pager, next, jumper"
        :total="total">
    </el-pagination>
    <el-dialog
        :title="title"
        :visible.sync="centerDialogVisible"
        width="30%"
        :before-close="handleClose"
        center>
      <el-form ref="form" :model="form" :rules="rules" label-width="120px">
        <el-form-item label="物品名" prop="name">
          <el-col :span="30">
            <el-input v-model="form.name"></el-input>
          </el-col>
        </el-form-item>
        <el-form-item label="仓库" prop="storage">
          <el-col :span="30">
            <el-select style="margin-left: 5px;" v-model="form.storage" placeholder="请选择仓库">
              <el-option
                  v-for="item in storageData"
                  :key="item.id"
                  :label="item.name"
                  :value="item.id">
              </el-option>
            </el-select>
          </el-col>
        </el-form-item>
        <el-form-item label="物品类别" prop="goodstype">
          <el-col :span="30">
            <el-select style="margin-left: 5px;" v-model="form.goodstype" placeholder="请选择物品类别">
              <el-option
                  v-for="item in goodstypeData"
                  :key="item.id"
                  :label="item.name"
                  :value="item.id">
              </el-option>
            </el-select>
          </el-col>
        </el-form-item>
        <el-form-item label="数量" prop="count">
          <el-col :span="30">
            <el-input v-model="form.count"></el-input>
          </el-col>
        </el-form-item>
        <el-form-item label="备注" prop="remark">
          <el-col :span="30">
            <el-input type="textarea" v-model="form.remark"></el-input>
          </el-col>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="centerDialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="save">确 定</el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
export default {
  name: "GoodsManage",
  data() {
    let checkCount = (rule, value, callback) => {
      if (value > 9999) {
        callback(new Error('数量输入过大'))
      } else {
        callback()
      }
    }
    return {
      goodstypeData: [],
      storageData: [],
      tableData: [],
      pageSize: 10,
      pageNum: 1,
      total: 0,
      name: '',
      storage: '',
      goodstype: '',
      title:'',
      centerDialogVisible: false,
      form: {
        id: '',
        name: '',
        storage: '',
        goodstype: '',
        count: '',
        remark: ''
      },
      rules: {
        name: [
          { required: true, message: '请输入物品名', trigger: 'blur' }
        ],
        storage: [
          { required: true, message: '请选择仓库', trigger: 'blur' }
        ],
        goodstype: [
          { required: true, message: '请选择物品类别', trigger: 'blur' }
        ],
        count: [
          { required: true, message: '请输入数量', trigger: 'blur' },
          { pattern: /^([1-9][0-9]*){1,4}$/, message: '数量必须为正整数', trigger: 'blur' },
          { validator: checkCount, trigger: 'blur' }
        ]
      }
    }
  },
  methods: {
    loadPost() {
      this.$axios.post(this.$httpUrl+'/goods/listPage',{
        pageSize: this.pageSize,
        pageNum: this.pageNum,
        param: {
          name: this.name,
          storage: this.storage+'',
          goodstype: this.goodstype+''
        }
      }).then(res=>res.data).then(res=>{
        console.log(res)
        if (res.code === 200) {
          this.tableData = res.data
          this.total = res.total
        }
      })
    },
    loadStorage() {
      this.$axios.get(this.$httpUrl+'/storage/list').then(res=>res.data).then(res=>{
        console.log(res)
        if (res.code === 200) {
          this.storageData = res.data
        }
      })
    },
    loadGoodstype() {
      this.$axios.get(this.$httpUrl+'/goodstype/list').then(res=>res.data).then(res=>{
        console.log(res)
        if (res.code === 200) {
          this.goodstypeData = res.data
        }
      })
    },
    handleSizeChange(val) {
      console.log(`每页 ${val} 条`);
      this.pageNum = 1
      this.pageSize = val
      this.loadPost()
    },
    handleCurrentChange(val) {
      console.log(`当前页: ${val}`);
      this.pageNum = val
      this.loadPost()
    },
    resetParam() {
      this.name = ''
      this.storage = ''
      this.goodstype = ''
    },
    add() {
      this.title='新增'
      this.centerDialogVisible = true
      this.$nextTick(()=>{
        this.resetForm()
      })
    },
    handleClose(done) {
      this.$confirm('确认关闭?')
          .then(() => {
            done();
          })
          .catch(() => {});
    },
    doSave() {
      this.$axios.post(this.$httpUrl+'/goods/save',this.form).then(res=>res.data).then(res=>{
        console.log(res)
        if (res.code === 200) {
          this.$message({
            message: '新增成功',
            type: 'success'
          });
          this.centerDialogVisible = false
          this.loadPost()
        } else {
          this.$message.error("新增失败");
        }
      })
    },
    doMod() {
      this.$axios.post(this.$httpUrl+'/goods/update',this.form).then(res=>res.data).then(res=>{
        console.log(res)
        if (res.code === 200) {
          this.$message({
            message: '修改成功',
            type: 'success'
          });
          this.centerDialogVisible = false
          this.loadPost()
        } else {
          this.$message.error("修改失败");
        }
      })
    },
    save() {
      this.$refs.form.validate((valid) => {
        if (valid) {
          if (this.form.id) {
            this.doMod()
          } else {
            this.doSave()
          }
        } else {
          console.log('error submit!!');
          return false;
        }
      });
    },
    resetForm() {
      this.$refs.form.resetFields();
    },
    mod(row) {
      this.title='修改'
      this.centerDialogVisible = true
      this.$nextTick(()=>{
        //赋值到表单
        this.form.id = row.id
        this.form.name = row.name
        this.form.storage = row.storage
        this.form.goodstype = row.goodstype
        this.form.count = row.count
        this.form.remark = row.remark
      })

    },
    del(id) {
      console.log(id)
      this.$axios.get(this.$httpUrl+'/goods/del?id='+id).then(res=>res.data).then(res=>{
        if (res.code === 200) {
          this.$message({
            message: '删除成功',
            type: 'success'
          });
          this.loadPost()
        } else {
          this.$message.error("删除失败");
        }
      })
    },
    formatStorage(row) {
      let temp = this.storageData.find(item=>{
        return item.id === row.storage
      })
      return temp && temp.name
    },
    formatGoodstype(row) {
      let temp = this.goodstypeData.find(item=>{
        return item.id === row.goodstype
      })
      return temp && temp.name
    }
  },
  beforeMount() {
    this.loadPost()
    this.loadStorage()
    this.loadGoodstype()
  }
}
</script>

<style scoped>

</style>

在GoodsController中:

@PostMapping("/listPage")
public Result listPage(@RequestBody QueryPageParam query) {

    Page<Goods> page = new Page<>();
    page.setCurrent(query.getPageNum());
    page.setSize(query.getPageSize());

    HashMap param = query.getParam();

    LambdaQueryWrapper<Goods> lambdaQueryWrapper = new LambdaQueryWrapper<>();
    if (StringUtils.isNotBlank((String) param.get("name")) && !"null".equals((String) param.get("name"))) {
        lambdaQueryWrapper.like(Goods::getName, (String) param.get("name"));
    }
    if (StringUtils.isNotBlank((String) param.get("storage")) && !"null".equals((String) param.get("storage"))) {
        lambdaQueryWrapper.eq(Goods::getStorage, (String) param.get("storage"));
    }
    if (StringUtils.isNotBlank((String) param.get("goodstype")) && !"null".equals((String) param.get("goodstype"))) {
        lambdaQueryWrapper.eq(Goods::getGoodstype, (String) param.get("goodstype"));
    }

    IPage<Goods> result = goodsService.pageCC(page, lambdaQueryWrapper);
    System.out.println("total===" + result.getTotal());

    return Result.success(result.getTotal(), result.getRecords());
}

8、表单中仓库和分类下拉实现

代码和“7、查询条件中增加仓库和分类的条件”中的GoodsManage.vue一样

三十一、记录管理

1、表设计

CREATE TABLE `record`(
	`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
    `goods` int(11) NOT NULL COMMENT '物品id',
    `userId` int(11) DEFAULT NULL COMMENT '取货人/补货人',
    `admin_id` int(11) DEFAULT NULL COMMENT '操作人id',
    `count` int(11) DEFAULT NULL COMMENT '数量',
    `createtime` datetime DEFAULT NULL COMMENT '操作时间',
    `remark` varchar(1000) DEFAULT NULL COMMENT '备注',
    PRIMARY KEY(`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

2、根据表生成后端代码

使用代码生成器

3、编写后端查询代码

在RecordController中:

package com.wms.controller;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.wms.common.QueryPageParam;
import com.wms.common.Result;
import com.wms.entity.Record;
import com.wms.service.RecordService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;

/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author ghost
 * @since 2023-10-19
 */
@RestController
@RequestMapping("/record")
public class RecordController {

    @Autowired
    private RecordService recordService;

    @PostMapping("/listPage")
    public Result listPage(@RequestBody QueryPageParam query) {

        Page<Record> page = new Page<>();
        page.setCurrent(query.getPageNum());
        page.setSize(query.getPageSize());

        HashMap param = query.getParam();

        LambdaQueryWrapper<Record> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        if (StringUtils.isNotBlank((String) param.get("name")) && !"null".equals((String) param.get("name"))) {
//            lambdaQueryWrapper.like(Record::getName, (String) param.get("name"));
        }
        if (StringUtils.isNotBlank((String) param.get("storage")) && !"null".equals((String) param.get("storage"))) {
//            lambdaQueryWrapper.eq(Record::getStorage, (String) param.get("storage"));
        }
        if (StringUtils.isNotBlank((String) param.get("goodstype")) && !"null".equals((String) param.get("goodstype"))) {
//            lambdaQueryWrapper.eq(Record::getGoodstype, (String) param.get("goodstype"));
        }

        IPage<Record> result = recordService.pageCC(page, lambdaQueryWrapper);
        System.out.println("total===" + result.getTotal());

        return Result.success(result.getTotal(), result.getRecords());
    }

}

在RecordService中:

package com.wms.service;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.wms.entity.Record;
import com.baomidou.mybatisplus.extension.service.IService;

/**
 * <p>
 *  服务类
 * </p>
 *
 * @author ghost
 * @since 2023-10-19
 */
public interface RecordService extends IService<Record> {
    IPage<Record> pageCC(IPage<Record> page, Wrapper<Record> wrapper);
}

在RecordServiceImpl中:

package com.wms.service.impl;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.wms.entity.Record;
import com.wms.mapper.RecordMapper;
import com.wms.service.RecordService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author ghost
 * @since 2023-10-19
 */
@Service
public class RecordServiceImpl extends ServiceImpl<RecordMapper, Record> implements RecordService {
    @Autowired
    private RecordMapper recordMapper;

    @Override
    public IPage<Record> pageCC(IPage<Record> page, Wrapper<Record> wrapper) {
        return recordMapper.pageCC(page, wrapper);
    }
}

在RecordMapper中:

package com.wms.mapper;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.wms.entity.Record;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

/**
 * <p>
 *  Mapper 接口
 * </p>
 *
 * @author ghost
 * @since 2023-10-19
 */
@Mapper
public interface RecordMapper extends BaseMapper<Record> {
    IPage<Record> pageCC(IPage<Record> page, @Param(Constants.WRAPPER) Wrapper<Record> wrapper);
}

在RecordMapper.xml中:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wms.mapper.RecordMapper">

    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.wms.entity.Record">
        <id column="id" property="id" />
        <result column="goods" property="goods" />
        <result column="userId" property="userid" />
        <result column="admin_id" property="adminId" />
        <result column="count" property="count" />
        <result column="createtime" property="createtime" />
        <result column="remark" property="remark" />
    </resultMap>

    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List">
        id, goods, userId, admin_id, count, createtime, remark
    </sql>
    <select id="pageCC" resultType="com.wms.entity.Record">
        select * from record ${ew.customSqlSegment}
    </select>

</mapper>

4、编写前端相关代码

在RecordManage.vue中:

<template>
  <div>
    <div style="margin-bottom: 5px;">
      <el-input v-model="name" placeholder="请输入物品名" suffix-icon="el-icon-search" style="width: 200px;"
                @keyup.enter.native="loadPost"></el-input>
      <el-select style="margin-left: 5px;" v-model="storage" placeholder="请选择仓库">
        <el-option
            v-for="item in storageData"
            :key="item.id"
            :label="item.name"
            :value="item.id">
        </el-option>
      </el-select>
      <el-select style="margin-left: 5px;" v-model="goodstype" placeholder="请选择物品类别">
        <el-option
            v-for="item in goodstypeData"
            :key="item.id"
            :label="item.name"
            :value="item.id">
        </el-option>
      </el-select>
      <el-button type="primary" style="margin-left: 5px;" @click="loadPost">查询</el-button>
      <el-button type="success" @click="resetParam">重置</el-button>
      <el-button type="primary" style="margin-left: 5px;" @click="add">新增</el-button>

    </div>
    <el-table
        :data="tableData"
        :header-cell-style="{background: '#f3f6fd', color: '#555'}"
        border
    >
      <el-table-column prop="id" label="ID" width="60">
      </el-table-column>
      <el-table-column prop="name" label="物品名" width="120">
      </el-table-column>
      <el-table-column prop="storage" label="仓库" width="120">
      </el-table-column>
      <el-table-column prop="goodstype" label="物品类别" width="120">
      </el-table-column>
      <el-table-column prop="adminId" label="操作人" width="150">
      </el-table-column>
      <el-table-column prop="userid" label="申请人" width="150">
      </el-table-column>
      <el-table-column prop="count" label="数量" width="120">
      </el-table-column>
      <el-table-column prop="createtime" label="操作时间" width="150">
      </el-table-column>
      <el-table-column prop="remark" label="备注">
      </el-table-column>
    </el-table>
    <el-pagination
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
        :current-page="pageNum"
        :page-sizes="[10, 20, 50, 100]"
        :page-size="pageSize"
        layout="total, sizes, prev, pager, next, jumper"
        :total="total">
    </el-pagination>
  </div>
</template>

<script>
export default {
  name: "RecordManage",
  data() {
    return {
      goodstypeData: [],
      storageData: [],
      tableData: [],
      pageSize: 10,
      pageNum: 1,
      total: 0,
      name: '',
      storage: '',
      goodstype: '',
      title:'',
      centerDialogVisible: false,
      form: {
        id: '',
        name: '',
        storage: '',
        goodstype: '',
        count: '',
        remark: ''
      }
    }
  },
  methods: {
    loadPost() {
      this.$axios.post(this.$httpUrl+'/record/listPage',{
        pageSize: this.pageSize,
        pageNum: this.pageNum,
        param: {
          name: this.name,
          storage: this.storage+'',
          goodstype: this.goodstype+''
        }
      }).then(res=>res.data).then(res=>{
        console.log(res)
        if (res.code === 200) {
          this.tableData = res.data
          this.total = res.total
        }
      })
    },
    handleSizeChange(val) {
      console.log(`每页 ${val} 条`);
      this.pageNum = 1
      this.pageSize = val
      this.loadPost()
    },
    handleCurrentChange(val) {
      console.log(`当前页: ${val}`);
      this.pageNum = val
      this.loadPost()
    },
    resetParam() {
      this.name = ''
      this.storage = ''
      this.goodstype = ''
    },
    handleClose(done) {
      this.$confirm('确认关闭?')
          .then(() => {
            done();
          })
          .catch(() => {});
    },
    resetForm() {
      this.$refs.form.resetFields();
    }
  },
  beforeMount() {
    this.loadPost()
  }
}
</script>

<style scoped>

</style>

5、优化

  • 列表展示物品名、仓库、物品类别

新建entity类RecordRes.java:

package com.wms.entity;

import lombok.Data;

@Data
public class RecordRes extends Record{

    private String username;
    private String adminname;
    private String goodsname;
    private String storagename;
    private String goodstypename;

}

修改RecordMapper.xml:

<select id="pageCC" resultType="com.wms.entity.RecordRes">
    select a.*,b.name goodsname,c.name storagename,d.name goodstypename,
           (select u.name from user u where u.id=a.userId) username,
           (select u.name from user u where u.id=a.admin_id) adminname
    from record a,goods b,storage c,goodstype d where b.id=a.goods and
        c.id=(select m.storage from goods m where m.id=a.goods) and
        d.id=(select n.goodsType from goods n where n.id=a.goods) ${ew.customSqlSegment}
</select>
  • 按物品名、仓库、物品类别查询

在RecordController中:

@PostMapping("/listPage")
public Result listPage(@RequestBody QueryPageParam query) {

    Page<Record> page = new Page<>();
    page.setCurrent(query.getPageNum());
    page.setSize(query.getPageSize());

    HashMap param = query.getParam();

//        LambdaQueryWrapper<Record> lambdaQueryWrapper = new LambdaQueryWrapper<>();
    QueryWrapper<Record> queryWrapper = new QueryWrapper<>();
    queryWrapper.apply("b.id=a.goods and c.id=b.storage and d.id=b.goodsType");
    if (StringUtils.isNotBlank((String) param.get("name")) && !"null".equals((String) param.get("name"))) {
//            lambdaQueryWrapper.like(Record::getGoodsname, (String) param.get("name"));
        queryWrapper.like("b.name",(String) param.get("name"));
    }
    if (StringUtils.isNotBlank((String) param.get("storage")) && !"null".equals((String) param.get("storage"))) {
//            lambdaQueryWrapper.eq(Record::getStoragename, (String) param.get("storage"));
        queryWrapper.like("c.id",(String) param.get("storage"));

    }
    if (StringUtils.isNotBlank((String) param.get("goodstype")) && !"null".equals((String) param.get("goodstype"))) {
//            lambdaQueryWrapper.eq(Record::getGoodstypename, (String) param.get("goodstype"));
        queryWrapper.like("d.id",(String) param.get("goodstype"));
    }

    IPage<Record> result = recordService.pageCC(page, queryWrapper);
    System.out.println("total===" + result.getTotal());

    return Result.success(result.getTotal(), result.getRecords());
}

在RecordMapper.xml中:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wms.mapper.RecordMapper">

    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.wms.entity.Record">
        <id column="id" property="id" />
        <result column="goods" property="goods" />
        <result column="userId" property="userid" />
        <result column="admin_id" property="adminId" />
        <result column="count" property="count" />
        <result column="createtime" property="createtime" />
        <result column="remark" property="remark" />
    </resultMap>

    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List">
        id, goods, userId, admin_id, count, createtime, remark
    </sql>
    <select id="pageCC" resultType="com.wms.entity.RecordRes">
        select a.*,b.name goodsname,c.name storagename,d.name goodstypename,
               (select u.name from user u where u.id=a.userId) username,
               (select u.name from user u where u.id=a.admin_id) adminname
        from record a,goods b,storage c,goodstype d ${ew.customSqlSegment}
    </select>

</mapper>

三十二、出库、入库

1、表单编写

在GoodsManage.vue中:

<template>
  <div>
    <div style="margin-bottom: 5px;">
      <el-input v-model="name" placeholder="请输入物品名" suffix-icon="el-icon-search" style="width: 200px;"
                @keyup.enter.native="loadPost"></el-input>
      <el-select style="margin-left: 5px;" v-model="storage" placeholder="请选择仓库">
        <el-option
            v-for="item in storageData"
            :key="item.id"
            :label="item.name"
            :value="item.id">
        </el-option>
      </el-select>
      <el-select style="margin-left: 5px;" v-model="goodstype" placeholder="请选择物品类别">
        <el-option
            v-for="item in goodstypeData"
            :key="item.id"
            :label="item.name"
            :value="item.id">
        </el-option>
      </el-select>
      <el-button type="primary" style="margin-left: 5px;" @click="loadPost">查询</el-button>
      <el-button type="success" @click="resetParam">重置</el-button>
      <el-button type="primary" style="margin-left: 5px;" @click="add">新增</el-button>
      <el-button type="primary" style="margin-left: 5px;" @click="inGoods">入库</el-button>
      <el-button type="primary" style="margin-left: 5px;" @click="outGoods">出库</el-button>
    </div>
    <el-table
        :data="tableData"
        :header-cell-style="{background: '#f3f6fd', color: '#555'}"
        border
        highlight-current-row
        @current-change="selectCurrentChange"
    >
      <el-table-column prop="id" label="ID" width="80">
      </el-table-column>
      <el-table-column prop="name" label="物品名" width="180">
      </el-table-column>
      <el-table-column prop="storage" label="仓库" width="180" :formatter="formatStorage">
      </el-table-column>
      <el-table-column prop="goodstype" label="物品类别" width="180" :formatter="formatGoodstype">
      </el-table-column>
      <el-table-column prop="count" label="数量" width="180">
      </el-table-column>
      <el-table-column prop="remark" label="备注">
      </el-table-column>
      <el-table-column prop="operation" label="操作" width="280">
        <template slot-scope="scope">
          <el-button size="small" type="success" @click="mod(scope.row)">编辑</el-button>
          <el-popconfirm
              title="确定删除吗?"
              @confirm="del(scope.row.id)"
              style="margin-left: 5px;"
          >
            <el-button slot="reference" size="small" type="danger">删除</el-button>
          </el-popconfirm>
        </template>
      </el-table-column>
    </el-table>
    <el-pagination
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
        :current-page="pageNum"
        :page-sizes="[10, 20, 50, 100]"
        :page-size="pageSize"
        layout="total, sizes, prev, pager, next, jumper"
        :total="total">
    </el-pagination>
    <el-dialog
        :title="title"
        :visible.sync="centerDialogVisible"
        width="30%"
        :before-close="handleClose"
        center>
      <el-form ref="form" :model="form" :rules="rules" label-width="120px">
        <el-form-item label="物品名" prop="name">
          <el-col :span="30">
            <el-input v-model="form.name"></el-input>
          </el-col>
        </el-form-item>
        <el-form-item label="仓库" prop="storage">
          <el-col :span="30">
            <el-select style="margin-left: 5px;" v-model="form.storage" placeholder="请选择仓库">
              <el-option
                  v-for="item in storageData"
                  :key="item.id"
                  :label="item.name"
                  :value="item.id">
              </el-option>
            </el-select>
          </el-col>
        </el-form-item>
        <el-form-item label="物品类别" prop="goodstype">
          <el-col :span="30">
            <el-select style="margin-left: 5px;" v-model="form.goodstype" placeholder="请选择物品类别">
              <el-option
                  v-for="item in goodstypeData"
                  :key="item.id"
                  :label="item.name"
                  :value="item.id">
              </el-option>
            </el-select>
          </el-col>
        </el-form-item>
        <el-form-item label="数量" prop="count">
          <el-col :span="30">
            <el-input v-model="form.count"></el-input>
          </el-col>
        </el-form-item>
        <el-form-item label="备注" prop="remark">
          <el-col :span="30">
            <el-input type="textarea" v-model="form.remark"></el-input>
          </el-col>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="centerDialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="save">确 定</el-button>
      </span>
    </el-dialog>

    <el-dialog
        :title="title"
        :visible.sync="inDialogVisible"
        width="30%"
        :before-close="handleClose"
        center>
      <el-dialog
          width="50%"
          title="选择申请人"
          :visible.sync="innerVisible"
          append-to-body>
        <SelectUser @doSelectUser="doSelectUser"></SelectUser>
        <span slot="footer" class="dialog-footer">
          <el-button @click="closeInner">取 消</el-button>
          <el-button type="primary" @click="confirmUser">确 定</el-button>
        </span>
      </el-dialog>
      <el-form ref="form1" :model="form1" :rules="rules1" label-width="120px">
        <el-form-item label="物品名">
          <el-col :span="30">
            <el-input v-model="form1.goodsname" readonly></el-input>
          </el-col>
        </el-form-item>
        <el-form-item label="申请人">
          <el-col :span="30">
            <el-input v-model="form1.username" readonly @click.native="selectUser"></el-input>
          </el-col>
        </el-form-item>
        <el-form-item label="数量" prop="count">
          <el-col :span="30">
            <el-input v-model="form1.count"></el-input>
          </el-col>
        </el-form-item>
        <el-form-item label="备注" prop="remark">
          <el-col :span="30">
            <el-input type="textarea" v-model="form1.remark"></el-input>
          </el-col>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="inDialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="doInGoods">确 定</el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
import SelectUser from "@/components/user/SelectUser.vue";

export default {
  name: "GoodsManage",
  components: {SelectUser},
  data() {
    let checkCount = (rule, value, callback) => {
      if (value > 9999) {
        callback(new Error('数量输入过大'))
      } else {
        callback()
      }
    }
    return {
      user: JSON.parse(sessionStorage.getItem('CurUser')),
      goodstypeData: [],
      storageData: [],
      tableData: [],
      pageSize: 10,
      pageNum: 1,
      total: 0,
      name: '',
      storage: '',
      goodstype: '',
      title:'',
      centerDialogVisible: false,
      inDialogVisible: false,
      innerVisible: false,
      currentRow: {},
      tempUser: {},
      form: {
        id: '',
        name: '',
        storage: '',
        goodstype: '',
        count: '',
        remark: ''
      },
      form1: {
        goods:'',
        goodsname:'',
        count:'',
        username:'',
        userid:'',
        adminId:'',
        remark:'',
        action:'1'
      },
      rules: {
        name: [
          { required: true, message: '请输入物品名', trigger: 'blur' }
        ],
        storage: [
          { required: true, message: '请选择仓库', trigger: 'blur' }
        ],
        goodstype: [
          { required: true, message: '请选择物品类别', trigger: 'blur' }
        ],
        count: [
          { required: true, message: '请输入数量', trigger: 'blur' },
          { pattern: /^([1-9][0-9]*){1,4}$/, message: '数量必须为正整数', trigger: 'blur' },
          { validator: checkCount, trigger: 'blur' }
        ]
      },
      rules1: {

      }
    }
  },
  methods: {
    loadPost() {
      this.$axios.post(this.$httpUrl+'/goods/listPage',{
        pageSize: this.pageSize,
        pageNum: this.pageNum,
        param: {
          name: this.name,
          storage: this.storage+'',
          goodstype: this.goodstype+''
        }
      }).then(res=>res.data).then(res=>{
        console.log(res)
        if (res.code === 200) {
          this.tableData = res.data
          this.total = res.total
        }
      })
    },
    loadStorage() {
      this.$axios.get(this.$httpUrl+'/storage/list').then(res=>res.data).then(res=>{
        console.log(res)
        if (res.code === 200) {
          this.storageData = res.data
        }
      })
    },
    loadGoodstype() {
      this.$axios.get(this.$httpUrl+'/goodstype/list').then(res=>res.data).then(res=>{
        console.log(res)
        if (res.code === 200) {
          this.goodstypeData = res.data
        }
      })
    },
    selectCurrentChange(val) {
      this.currentRow = val
    },
    handleSizeChange(val) {
      console.log(`每页 ${val} 条`);
      this.pageNum = 1
      this.pageSize = val
      this.loadPost()
    },
    handleCurrentChange(val) {
      console.log(`当前页: ${val}`);
      this.pageNum = val
      this.loadPost()
    },
    resetParam() {
      this.name = ''
      this.storage = ''
      this.goodstype = ''
    },
    add() {
      this.title='新增'
      this.centerDialogVisible = true
      this.$nextTick(()=>{
        this.resetForm()
      })
    },
    inGoods() {
      this.title='入库'
      if (!this.currentRow.id) {
        alert('请选择物品')
        return;
      }
      this.form1.username = ''
      this.inDialogVisible = true
      this.$nextTick(()=>{
        this.resetInForm()
      })
      this.form1.goods = this.currentRow.id
      this.form1.goodsname = this.currentRow.name
      this.form1.adminId = this.user.id
      this.form1.action = '1'
    },
    outGoods() {
      this.title='出库'
      if (!this.currentRow.id) {
        alert('请选择物品')
        return;
      }
      this.form1.username = ''
      this.inDialogVisible = true
      this.$nextTick(()=>{
        this.resetInForm()
      })
      this.form1.goods = this.currentRow.id
      this.form1.goodsname = this.currentRow.name
      this.form1.adminId = this.user.id
      this.form1.action = '2'
    },
    handleClose(done) {
      this.$confirm('确认关闭?')
          .then(() => {
            done();
          })
          .catch(() => {});
    },
    doSave() {
      this.$axios.post(this.$httpUrl+'/goods/save',this.form).then(res=>res.data).then(res=>{
        console.log(res)
        if (res.code === 200) {
          this.$message({
            message: '新增成功',
            type: 'success'
          });
          this.centerDialogVisible = false
          this.loadPost()
        } else {
          this.$message.error("新增失败");
        }
      })
    },
    doInGoods() {
      this.$axios.post(this.$httpUrl+'/record/save',this.form1).then(res=>res.data).then(res=>{
        console.log(res)
        if (res.code === 200) {
          this.$message({
            message: '新增成功',
            type: 'success'
          });
          this.inDialogVisible = false
          this.loadPost()
        } else {
          this.$message.error("新增失败");
        }
      })
    },
    selectUser() {
      this.innerVisible = true
    },
    closeInner() {
      this.innerVisible = false
      this.form1.username = ''
    },
    confirmUser() {
      this.form1.userid = this.tempUser.id
      this.form1.username = this.tempUser.name
      this.innerVisible = false
    },
    doSelectUser(val) {
      this.tempUser = val
    },
    doMod() {
      this.$axios.post(this.$httpUrl+'/goods/update',this.form).then(res=>res.data).then(res=>{
        console.log(res)
        if (res.code === 200) {
          this.$message({
            message: '修改成功',
            type: 'success'
          });
          this.centerDialogVisible = false
          this.loadPost()
        } else {
          this.$message.error("修改失败");
        }
      })
    },
    save() {
      this.$refs.form.validate((valid) => {
        if (valid) {
          if (this.form.id) {
            this.doMod()
          } else {
            this.doSave()
          }
        } else {
          console.log('error submit!!');
          return false;
        }
      });
    },
    resetForm() {
      this.$refs.form.resetFields();
    },
    resetInForm() {
      this.$refs.form1.resetFields();
    },
    mod(row) {
      this.title='修改'
      this.centerDialogVisible = true
      this.$nextTick(()=>{
        //赋值到表单
        this.form.id = row.id
        this.form.name = row.name
        this.form.storage = row.storage
        this.form.goodstype = row.goodstype
        this.form.count = row.count
        this.form.remark = row.remark
      })

    },
    del(id) {
      console.log(id)
      this.$axios.get(this.$httpUrl+'/goods/del?id='+id).then(res=>res.data).then(res=>{
        if (res.code === 200) {
          this.$message({
            message: '删除成功',
            type: 'success'
          });
          this.loadPost()
        } else {
          this.$message.error("删除失败");
        }
      })
    },
    formatStorage(row) {
      let temp = this.storageData.find(item=>{
        return item.id === row.storage
      })
      return temp && temp.name
    },
    formatGoodstype(row) {
      let temp = this.goodstypeData.find(item=>{
        return item.id === row.goodstype
      })
      return temp && temp.name
    }
  },
  beforeMount() {
    this.loadPost()
    this.loadStorage()
    this.loadGoodstype()
  }
}
</script>

<style scoped>

</style>

在SelectUser.vue中:

<template>
  <div>
    <div style="margin-bottom: 5px;">
      <el-input v-model="name" placeholder="请输入姓名" suffix-icon="el-icon-search" style="width: 200px;"
                @keyup.enter.native="loadPost"></el-input>
      <el-select v-model="sex" placeholder="请选择性别" style="margin-left: 5px;">
        <el-option
            v-for="item in sexs"
            :key="item.value"
            :label="item.label"
            :value="item.value">
        </el-option>
      </el-select>
      <el-button type="primary" style="margin-left: 5px;" @click="loadPost">查询</el-button>
      <el-button type="success" @click="resetParam">重置</el-button>

    </div>
    <el-table
        :data="tableData"
        :header-cell-style="{background: '#f3f6fd', color: '#555'}"
        border
        highlight-current-row
        @current-change="selectCurrentChange"
    >
      <el-table-column prop="id" label="ID" width="80">
      </el-table-column>
      <el-table-column prop="no" label="账号" width="120">
      </el-table-column>
      <el-table-column prop="name" label="姓名" width="120">
      </el-table-column>
      <el-table-column prop="age" label="年龄" width="80">
      </el-table-column>
      <el-table-column prop="sex" label="性别" width="80">
        <template slot-scope="scope">
          <el-tag
              :type="scope.row.sex === 1 ? 'primary' : 'success'"
              disable-transitions>{{scope.row.sex === 1 ? '男' : '女'}}</el-tag>
        </template>
      </el-table-column>
      <el-table-column prop="roleId" label="角色" width="120">
        <template slot-scope="scope">
          <el-tag
              :type="scope.row.roleId === 0 ? 'danger' : (scope.row.roleId === 1 ? 'primary' : 'success')"
              disable-transitions>{{scope.row.roleId === 0 ? '超级管理员' : (scope.row.roleId === 1 ? '管理员' : '普通用户')}}</el-tag>
        </template>
      </el-table-column>
      <el-table-column prop="phone" label="电话">
      </el-table-column>
    </el-table>
    <el-pagination
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
        :current-page="pageNum"
        :page-sizes="[10, 20, 50, 100]"
        :page-size="pageSize"
        layout="total, sizes, prev, pager, next, jumper"
        :total="total">
    </el-pagination>
  </div>
</template>

<script>
export default {
  name: "SelectUser",
  data() {
    return {
      tableData: [],
      pageSize: 10,
      pageNum: 1,
      total: 0,
      name: '',
      sex: '',
      sexs: [
        {
          value: '0',
          label: '女'
        },
        {
          value: '1',
          label: '男'
        }
      ],
      title:'',
      centerDialogVisible: false,
      form: {
        id: '',
        no: '',
        name: '',
        password: '',
        age: '',
        sex: '0',
        phone: '',
        roleId: '2'
      }
    }
  },
  methods: {
    loadGet() {
      this.$axios.get(this.$httpUrl + '/user/list').then(res => res.data).then(res => {
        console.log(res)
      })
    },
    loadPost() {
      this.$axios.post(this.$httpUrl + '/user/listPageCC1', {
        pageSize: this.pageSize,
        pageNum: this.pageNum,
        param: {
          name: this.name,
          sex: this.sex,
          roleId: '2'
        }
      }).then(res => res.data).then(res => {
        console.log(res)
        if (res.code === 200) {
          this.tableData = res.data
          this.total = res.total
        }
      })
    },
    selectCurrentChange(val) {
      // this.currentRow = val
      this.$emit("doSelectUser", val)
    },
    handleSizeChange(val) {
      console.log(`每页 ${val} 条`);
      this.pageNum = 1
      this.pageSize = val
      this.loadPost()
    },
    handleCurrentChange(val) {
      console.log(`当前页: ${val}`);
      this.pageNum = val
      this.loadPost()
    },
    resetParam() {
      this.name = '';
      this.sex = ''
    },
    handleClose(done) {
      this.$confirm('确认关闭?')
          .then(() => {
            done();
          })
          .catch(() => {
          });
    },
  },
  beforeMount() {
    // this.loadGet()
    this.loadPost()
  }
}
</script>

<style scoped>

</style>

2、入库操作(记录、更新物品数量、自动填充时间)

在Record.java中:

package com.wms.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.TableField;
import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;

/**
 * <p>
 * 
 * </p>
 *
 * @author ghost
 * @since 2023-10-19
 */
@Data
@EqualsAndHashCode(callSuper = false)
@ApiModel(value="Record对象", description="")
public class Record implements Serializable {

    private static final long serialVersionUID = 1L;

    @ApiModelProperty(value = "主键")
    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    @ApiModelProperty(value = "物品id")
    private Integer goods;

    @ApiModelProperty(value = "取货人/补货人")
    @TableField("userId")
    private Integer userid;

    @ApiModelProperty(value = "操作人id")
    private Integer adminId;

    @ApiModelProperty(value = "数量")
    private Integer count;

    @ApiModelProperty(value = "操作时间")
    private LocalDateTime createtime;

    @ApiModelProperty(value = "备注")
    private String remark;

    @TableField(exist = false)
    private String action;

}

在RecordController.java中:

package com.wms.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.wms.common.QueryPageParam;
import com.wms.common.Result;
import com.wms.entity.Goods;
import com.wms.entity.Record;
import com.wms.entity.RecordRes;
import com.wms.service.GoodsService;
import com.wms.service.RecordService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;

/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author ghost
 * @since 2023-10-19
 */
@RestController
@RequestMapping("/record")
public class RecordController {

    @Autowired
    private RecordService recordService;

    @Autowired
    private GoodsService goodsService;

    @PostMapping("/listPage")
    public Result listPage(@RequestBody QueryPageParam query) {

        Page<Record> page = new Page<>();
        page.setCurrent(query.getPageNum());
        page.setSize(query.getPageSize());

        HashMap param = query.getParam();

//        LambdaQueryWrapper<Record> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        QueryWrapper<Record> queryWrapper = new QueryWrapper<>();
        queryWrapper.apply("b.id=a.goods and c.id=b.storage and d.id=b.goodsType");
        if (StringUtils.isNotBlank((String) param.get("name")) && !"null".equals((String) param.get("name"))) {
//            lambdaQueryWrapper.like(Record::getGoodsname, (String) param.get("name"));
            queryWrapper.like("b.name",(String) param.get("name"));
        }
        if (StringUtils.isNotBlank((String) param.get("storage")) && !"null".equals((String) param.get("storage"))) {
//            lambdaQueryWrapper.eq(Record::getStoragename, (String) param.get("storage"));
            queryWrapper.like("c.id",(String) param.get("storage"));

        }
        if (StringUtils.isNotBlank((String) param.get("goodstype")) && !"null".equals((String) param.get("goodstype"))) {
//            lambdaQueryWrapper.eq(Record::getGoodstypename, (String) param.get("goodstype"));
            queryWrapper.like("d.id",(String) param.get("goodstype"));
        }

        IPage<Record> result = recordService.pageCC(page, queryWrapper);
        System.out.println("total===" + result.getTotal());

        return Result.success(result.getTotal(), result.getRecords());
    }

    //新增
    @PostMapping("/save")
    public Result save(@RequestBody Record record) {
        Goods goods = goodsService.getById(record.getGoods());
        int num = record.getCount();
        if ("2".equals(record.getAction())) {
            num = -num;
            record.setCount(num);
        }
        int count = goods.getCount() + num;
        goods.setCount(count);
        goodsService.updateById(goods);
        return recordService.save(record)?Result.success():Result.fail();
    }

}

3、用户选择

见“1、表单编写”

三十三、优化

1、出入库权限控制

在GoodsManage.vue中:

<el-button type="primary" style="margin-left: 5px;" @click="add" v-if="user.roleId!==2">新增</el-button>
<el-button type="primary" style="margin-left: 5px;" @click="inGoods" v-if="user.roleId!==2">入库</el-button>
<el-button type="primary" style="margin-left: 5px;" @click="outGoods" v-if="user.roleId!==2">出库</el-button>
<el-table-column prop="operation" label="操作" width="280" v-if="user.roleId!==2">

2、记录查询权限控制

在RecordManage.vue中:

data() {
    return {
      user: JSON.parse(sessionStorage.getItem('CurUser')),
        
loadPost() {
  this.$axios.post(this.$httpUrl+'/record/listPage',{
    pageSize: this.pageSize,
    pageNum: this.pageNum,
    param: {
      name: this.name,
      storage: this.storage+'',
      goodstype: this.goodstype+'',
      roleId: this.user.roleId+'',
      userId: this.user.id+''
    }
  }).then(res=>res.data).then(res=>{
    console.log(res)
    if (res.code === 200) {
      this.tableData = res.data
      this.total = res.total
    }
  })
},

在RecordController.java中:

if ("2".equals((String) param.get("roleId"))) {
    queryWrapper.apply("a.userId="+(String) param.get("userId"));
}

三十四、Vuex持久化后刷新丢失问题(菜单和动态路由)

1、插件vuex-persistedstate使用

  • 安装
npm i vuex-persistedstate
  • 引入
import createPersistedState from 'vuex-persistedstate'
  • 使用
plugins:[createPersistedState()]

2、菜单问题解决

3、动态路由问题

在App.vue中:

data() {
  return {
    user: JSON.parse(sessionStorage.getItem('CurUser'))
  }
},
watch: {
  '$store.state.menu': {
    handler(val, old) {
      if (!old && this.user && this.user.no) {
        this.$store.commit("setMenu", val)
      }
    },
    immediate: true
  }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于SSM(Spring+SpringMVC+Mybatis)和Vue.js开发的仓库管理系统是一个企业级的仓库管理平台,它的主要功能包括:库存管理、商品管理、客户管理、订单管理、报表统计等。 系统采用前后端分离的架构模式,前端采用了目前非常流行的Vue.js框架,后端采用了Spring+SpringMVC+Mybatis框架,通过RESTful API实现了前后端的数据交互。系统还集成了很多插件和组件,如echarts、zTree、layer等,增强了系统的可用性和用户体验。 具体来说,系统的核心功能如下: 1、库存管理:实时监控商品的数量、状态、价格等信息,并能对库存进行增删改查、入库、出库等操作。 2、商品管理:对商品进行分类、添加、修改、删除,可以进行多图上传、富文本编辑等操作。 3、客户管理:管理客户的基本信息,如名称、联系方式、地址等,可以进行增删改查操作。 4、订单管理:管理订单的生成、修改、删除、发货等操作,包括订单状态的查询、导出、打印等功能。 5、报表统计:根据一定的条件和规则,生成各种类型的数据分析报表,支持多种图表展示和导出功能。 总之,基于SSM+Vue仓库管理系统是一个高效、稳定、安全、易用的仓库管理平台,它能帮助企业实现实时掌控库存、提高管理效率、降低运营成本。部署该系统需要一定的技术功底和相关环境的支持,建议搭建在云服务器或私有服务器上,具体操作请参考部署说明。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值