Web开发学习笔记20210427_RuoYi-fast新建子菜单并实现一些功能

目录

  • 1 准备工作
  • 2 RuoYi-fast运行
  • 3 实现一个系统管理子菜单:测试管理
    • 3.1 实现过程
    • 3.2 遇到的问题
      • 3.2.1 不显示菜单错误
      • 3.2.2 无法读取数据库表内容错误
      • 3.2.3 页面功能实现错误
  • 4 总结
  • 5 补充说明
  • 6 所有新增代码
    • 6.1 后端
      • TestMapper.xml
      • Test.java
      • TestMapper.java
      • ITestService.java
      • TestServiceImpl.java
      • MyTestController.java
    • 6.2 前端
      • add.html
      • edit.html
      • test.html

1 准备工作

这是一个Web开发入门学习若依权限管理系统的记录帖
数据库刚接触,前端完全不懂,连蒙带抄实现一个最简单的子菜单,参考了系统中最不复杂的“岗位管理”模块。

环境准备:

  • IDEA2019 + Maven配置
  • MySQL + Navicat

需要知道的概念:

  • Spring框架和 spring-boot
  • MyBatis框架
  • Shiro安全框架(暂时没用到)
  • Thymeleaf模板(前端)

接下来是走一步看一步了,做到哪里不会就现查现学……

2 RuoYi-fast运行

在IDEA中直接下载引入若依:
登陆gitee - IDEA 工具栏 - VSC - Get from version control - URL输入Ruoyi-fast下载地址https://gitee.com/y_project/RuoYi-fast

等待下载完成,进行配置,doc文件夹下有《若依环境使用手册》,写得很清楚,好像没遇到什么难题,运行RuoYiApplication,登陆成功
Web开发学习笔记20210427_RuoYi-fast新建子菜单并实现一些功能_第1张图片

3 实现一个系统管理子菜单:测试管理

3.1 实现过程

  • 在数据库中新建一个test表,参考post表,加入几个条目
  • resources - mybatis - system下新建TestMapper.xml编写数据库映射
    新建中没有mapper选项:IDEA创建Mapper.xml文件
  • com.ruoyi.project.system新建test包,创建需要的类和接口
    1)因为test名字特殊,controller包里的TestController调试可以通过,但运行时报告了与com.ruoyi.project.tool.swagger下的类重名错误,导致系统启动失败,因此需要改个名字如MyTestController
    2)编写时涉及名称或编码是否唯一问题时需要两个user constant值,在com.ruoyi.common.constant.UserConstants里添加:
    /** 测试名称是否唯一的返回结果码 */
    public final static String TEST_NAME_UNIQUE = "0";
    public final static String TEST_NAME_NOT_UNIQUE = "1";
    /** 测试编码是否唯一的返回结果码 */
    public final static String TEST_CODE_UNIQUE = "0";
    public final static String TEST_CODE_NOT_UNIQUE = "1";
  • 编译TestMapper.xml可以检查上述所有,通过后开始写html
  • resources - templates - system下添加test路径,编写(抄)需要的html

3.2 遇到的问题

3.2.1 不显示菜单错误

现在可以运行了,可是运行出来仿佛我加的程序从未存在……一开始以为系统菜单在main.html里修改,但main中所有左侧菜单都用变量统一表述了,没看懂,但应该不是改这个。然后在project里搜索了一下,发现相关条目保存在数据库的sys_menu表里,我没有在这里添加相应条目:
Web开发学习笔记20210427_RuoYi-fast新建子菜单并实现一些功能_第2张图片
添加条目时,需要仔细检查层级有没有填对,信息有没有填完整,否则出现菜单打不开,变成404的现象。
顺便全面看了一下数据库里的表,发现还有一个sys_role_menu表,保存了系统操作角色id对应的菜单id:
Web开发学习笔记20210427_RuoYi-fast新建子菜单并实现一些功能_第3张图片
不确定影不影响前端显示,总之把条目id先加上。

3.2.2 无法读取数据库表内容错误

现在再次启动服务器:
Web开发学习笔记20210427_RuoYi-fast新建子菜单并实现一些功能_第4张图片
菜单里出现了我添加的“测试管理”条目,条目下所有功能也都显示了,但又有了新问题,读取不到我在数据库中添加的条目,应该是mapper.xml文件问题,检查了一下居然是包名打错字母,我晕

现在正常了:
Web开发学习笔记20210427_RuoYi-fast新建子菜单并实现一些功能_第5张图片

3.2.3 页面功能实现错误

试用下页面上的功能,又出了问题:

  • 搜索键没有反应,按照名称、编码、状态都无法搜索单一条目
  • 编辑条目中更改状态出现错误,显示重名
  • 新增或者编辑名称和编码不论写什么值都会显示已存在
  • 删除条目报错:nested exception is org.apache.ibatis.builder.BuilderException: Error evaluating expression ‘array’. Return value (1) was not iterable.

可以说是除了重置和导出,其他所有功能都不能实现去寻找下原因

  • 搜索功能:com.ruoyi.project.system.test.domain的Test类中所有set方法都没有声明类型和变量,修改后搜索功能正常了
  • 编辑状态功能:com.ruoyi.project.system.test.controller的MyTestController类的editSave方法返回了return toAjax(testService.insertTest(test));从addSave复制过来漏改了……改成return toAjax(testService.updateTest(test));状态信息就可以编辑了
  • 删除功能:com.ruoyi.project.system.test.service的TestServiceImpl类的deleteTestByIds(String ids)方法返回值和实际值不符,返回int,而我在之前编写时直接return testMapper.deleteTestByIds(ids);改为在return语句前添加convert:
	/**
     * 批量删除test信息
     *
     * @param ids 需要删除的数据id
     * @throws BusinessException
     */
    @Override
    public int deleteTestByIds(String ids) throws BusinessException
    {
        Long[] testIds = Convert.toLongArray(ids);
        return testMapper.deleteTestByIds(testIds);
    }
  • 编辑名称功能:编辑名称时抛出异常:Request method ‘TEST’ not supported,是html文件里的type: "post",写成test了,此post非彼post,果然对html一窍不通还是不行
  • 新增功能异常1:和上述一样修改后不再显示重名错误,但抛出了新的运行时异常:nested exception is org.apache.ibatis.builder.BuilderException: Error evaluating expression ‘remark !=null and remark != ‘‘0’’’.写得比较清楚,是xml文件里赋值时if声明的语法有错误,写多了一个0,改成#{remark},
  • 新增功能异常2:修改上述内容后又报了新的运行时异常:Error updating database. Cause: java.sql.SQLException: Field ‘test_id’ doesn’t have a default value在新增操作时没有设置id,而id没有默认值,导致id为空。在数据库的设计表中可以看到别的表的id都设置了自动递增,因此将新建的sys_test表的id也设置为自动递增,id就产生默认值了

现在增删查改都可以实现了:
Web开发学习笔记20210427_RuoYi-fast新建子菜单并实现一些功能_第6张图片
前端更改后的数据库表:
Web开发学习笔记20210427_RuoYi-fast新建子菜单并实现一些功能_第7张图片
虽然是简单内容,但是手打一遍出现了很多很多问题……把主要问题记录在了本帖中。

4 总结

关于后端六个文件的个人理解:

  • TestMapper.xml:动态sql操作文件,描述了系统和数据库的映射关系
    因为在namespace里填写了mapper接口所以编译这个文件可以检查所有相关class和interface(吗?)
  • TestMapper.java:dao层接口文件,方法名和 xml 中定义的每个 statement 的 id 同名,输入参数类型和 xml 中定义的 statement 的parameterType 类型相同,返回类型和 xml 中定义的 statement 的resultType 类型相同
  • ITestService.java:service层接口文件,定义方法的参数、返回值
  • TestServiceImpl.java:service层类文件,实现ITestService接口的方法
  • MyTestController.java:controller层类文件,接收前端请求,调用service层方法实现
  • Test.java:domain层类文件,和数据库中的表一一对应的JavaBean,实现set和get方法

业务实现流程:controller层接受前端发来的请求,同时向后端发送请求,——>service 层——>serviceImpl实现service层,同时连接dao层,(在dao层中同样是接口)——>通过dao层去实现对数据库的操作——>在XML文件中通过namespace完成连接dao层

5 补充说明

以上demo没有做和user的依赖关联,是最最最简单的子菜单实现,用于自己了解系统各层的作用,和开发的简单流程。编写过程中学习的帖子们(部分):

如何阅读别人的代码 [原]
详细SpringBoot教程之入门(一)
做web开发,怎么能不懂cookie、session和token呢?
SpringBoot注解最全详解(整合超详细版本)
JavaWeb——Servlet(全网最详细教程包括Servlet源码分析)
mybatis看这一篇就够了,简单全面一发入魂
AJAX&JSON超级最详细讲解
Mybatis的mapper.xml配置文件——详解
MyBatis Mapper XML文件详解
为什么dao层和service层要用接口?
SpringMVC中的@Controller和@RequestMapping作用详解
controller层的作用
初学SpringBoot框架: Dao层、Service层、Controller层的作用
解析Java框架中entity层,mapper层,service层,controller各层作用
domain层详解
Java Bean详解
POJO和JavaBean的区别
Java 之 Serializable 序列化和反序列化的概念,作用的通俗易懂的解释
GET和POST两种基本请求方法的区别

6 所有新增代码

6.1 后端

TestMapper.xml



<mapper namespace="com.ruoyi.project.system.test.mapper.TestMapper">

    <resultMap type="Test" id="TestResult">
        <id     property="testId"       column="test_id"        />
        <result property="testCode"     column="test_code"      />
        <result property="testName"     column="test_name"      />
        <result property="status"       column="status"         />
        <result property="createBy"     column="create_by"      />
        <result property="createTime"   column="create_time"    />
        <result property="updateBy"     column="update_by"      />
        <result property="updateTime"   column="update_time"    />
        <result property="remark"       column="remark"         />
    resultMap>

    <sql id="selectTestVo">
        select test_id, test_code, test_name, status, create_by, create_time, remark
        from sys_test
    sql>

    <select id="selectTestList" parameterType="Test" resultMap="TestResult">
        <include refid="selectTestVo" />
        <where>
            <if test="testCode != null and testCode != ''">
                AND test_code like concat('%', #{testCode}, '%')
            if>
            <if test="status != null and status != ''">
                AND status = #{status}
            if>
            <if test="testName != null and testName != ''">
                AND test_name like concat('%', #{testName}, '%')
            if>
        where>
    select>

    <select id="selectTestAll" resultMap="TestResult">
        <include refid="selectTestVo" />
    select>

    <select id="selectTestById" parameterType="Long" resultMap="TestResult">
        <include refid="selectTestVo" />
        where test_id = #{testID}
    select>

    <select id="checkTestNameUnique" parameterType="String" resultMap="TestResult">
        <include refid="selectTestVo" />
        where test_name = #{testName} limit 1
    select>

    <select id="checkTestCodeUnique" parameterType="String" resultMap="TestResult">
        <include refid="selectTestVo" />
        where test_code = #{testCode} limit 1
    select>

    <delete id="deleteTestByIds" parameterType="Long">
        delete from sys_test where test_id in
        <foreach collection="array" item="testId" open="(" separator="," close=")">
            #{testId}
        foreach>
    delete>

    <update id="updateTest" parameterType="Test">
        update sys_test
        <set>
            <if test="testCode != null and testCode != ''">test_code = #{testCode},if>
            <if test="testName != null and testName != ''">test_name = #{testName},if>
            <if test="status != null and status != ''">status = #{status},if>
            <if test="remark != null">remark = #{remark},if>
            <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},if>
            update_time = sysdate()
        set>
        where test_id = #{testId}
    update>

    <insert id="insertTest" parameterType="Test" useGeneratedKeys="true" keyProperty="testId">
        insert into sys_test(
            <if test="testId !=null and testId != 0">test_id,if>
            <if test="testCode !=null and testCode != ''">test_code,if>
            <if test="testName !=null and testName != ''">test_name,if>
            <if test="status !=null and status != ''">status,if>
            <if test="createBy !=null and createBy != ''">create_by,if>
            <if test="remark !=null and remark != ''">remark,if>
            create_time
        )values(
            <if test="testId !=null and testId != 0">#{testId},if>
            <if test="testCode !=null and testCode != ''">#{testCode},if>
            <if test="testName !=null and testName != ''">#{testName},if>
            <if test="status !=null and status != ''">#{status},if>
            <if test="createBy !=null and createBy != ''">#{createBy},if>
            <if test="remark !=null and remark != ''">#{remark},if>
        sysdate()
        )
    insert>
mapper>

Test.java

package com.ruoyi.project.system.test.domain;

import javax.validation.constraints.*;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.framework.aspectj.lang.annotation.Excel;
import com.ruoyi.framework.aspectj.lang.annotation.Excel.ColumnType;
import com.ruoyi.framework.web.domain.BaseEntity;

/**
 * 测试表 sys_test
 *
 * @author XYM_
 */
public class Test extends BaseEntity{
    private static final Long serialVersionUID = 1L;

    /**测试序号*/
    @Excel(name = "测试序号", cellType = ColumnType.NUMERIC)
    private Long testId;

    /**测试编码*/
    @Excel(name = "测试编码")
    private String testCode;

    /**测试名称*/
    @Excel(name = "测试名称")
    private String testName;

    /**状态 0正常 1停用*/
    @Excel(name = "状态", readConverterExp = "0=正常,1=停用")
    private String status;

    /**是否存在测试类的标识,默认不存在*/
    private boolean flag = false;

    public Long getTestId() { return testId; }

    public void setTestId(Long testId) { this.testId = testId; }

    @NotBlank(message = "测试编码不能为空")
    @Size(min = 0, max = 64, message = "编码长度不能超过64个字符")
    public String getTestCode() { return testCode; }

    public void setTestCode(String testCode) { this.testCode = testCode; }

    @NotBlank(message = "测试名称不能为空")
    @Size(min = 0, max = 50, message = "编码名称不能超过50个字符")
    public String getTestName() { return testName; }

    public void setTestName(String testName) { this.testName = testName; }

    public String getStatus() { return status; }

    public void setStatus(String status) { this.status = status; }

    public boolean isFlag() { return flag; }

    public void setFlag(boolean flag) { this.flag = flag; }

    @Override
    public String toString(){
        return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
                .append("testId", getTestId())
                .append("testCode", getTestCode())
                .append("testName", getTestName())
                .append("status", getStatus())
                .append("createBy", getCreateBy())
                .append("createTime", getCreateTime())
                .append("updateBy", getUpdateBy())
                .append("updateTime", getUpdateTime())
                .append("remark", getRemark())
                .toString();
    }
}

TestMapper.java

package com.ruoyi.project.system.test.mapper;

import java.util.List;
import com.ruoyi.project.system.test.domain.Test;

/**
 * 测试信息 数据层
 *
 * @author XYM_
 */
public interface TestMapper
{
    /**
     * 查询test数据集合
     *
     * @param test
     * @return test信息集合
     */
    public List<Test> selectTestList(Test test);

    /**
     * 查询所有test
     *
     * @return test列表
     */
    public List<Test> selectTestAll();

    /**
     * 根据test id查询
     *
     * @param testId
     * @return 对象信息
     */
    public Test selectTestById(Long testId);

    /**
     * 批量删除test信息
     *
     * @param ids 需要删除的数据id
     * @return 结果
     * @throws Exception
     */
    public int deleteTestByIds(Long[] ids);

    /**
     * 新增保存test信息
     *
     * @param test
     * @return 结果
     */
    public int insertTest(Test test);

    /**
     * 修改保存test信息
     *
     * @param test
     * @return 结果
     */
    public int updateTest(Test test);

    /**
     * 校验test名称是否唯一
     *
     * @param testName
     * @return 信息
     */
    public Test checkTestNameUnique(String testName);

    /**
     * 校验test编码是否唯一
     *
     * @param testCode
     * @return 信息
     */
    public Test checkTestCodeUnique(String testCode);
}

ITestService.java

package com.ruoyi.project.system.test.service;

import java.util.List;
import com.ruoyi.project.system.test.domain.Test;

/**
 * 测试信息 服务层
 *
 * @author XYM_
 */
public interface ITestService {
    /**
     * 查询测试信息集合
     *
     * @param test 测试类信息
     * @return 测试信息集合
     */
    public List<Test> selectTestList(Test test);

    /**
     * 查询所有test
     *
     * @return test列表
     */
    public List<Test> selectTestAll();

    /**
     * 根据test id查询
     *
     * @param testId
     * @return 对象信息
     */
    public Test selectTestById(Long testId);

    /**
     * 批量删除test信息
     *
     * @param ids 需要删除的数据id
     * @return 结果
     * @throws Exception
     */
    public int deleteTestByIds(String ids) throws Exception;

    /**
     * 新增保存test信息
     *
     * @param test
     * @return 结果
     */
    public int insertTest(Test test);

    /**
     * 修改保存test信息
     *
     * @param test
     * @return 结果
     */
    public int updateTest(Test test);

    /**
     * 校验test名称
     *
     * @param test
     * @return 信息
     */
    public String checkTestNameUnique(Test test);

    /**
     * 校验test编码
     *
     * @param test
     * @return 信息
     */
    public String checkTestCodeUnique(Test test);
}

TestServiceImpl.java

package com.ruoyi.project.system.test.service;

import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.exception.BusinessException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.security.ShiroUtils;
import com.ruoyi.common.utils.text.Convert;
import com.ruoyi.project.system.test.domain.Test;
import com.ruoyi.project.system.test.mapper.TestMapper;

/**
 * 测试信息 服务层处理
 *
 * @author XYM_
 */
@Service
public class TestServiceImpl implements ITestService
{
    @Autowired
    private TestMapper testMapper;

    /**
     * 查询测试信息集合
     *
     * @param test
     * @return 信息集合
     */
    @Override
    public List<Test> selectTestList(Test test) { return testMapper.selectTestList(test); }

    /**
     * 查询所有test
     *
     * @return test列表
     */
    @Override
    public List<Test> selectTestAll() { return testMapper.selectTestAll(); }

    /**
     * 根据test id查询
     *
     * @param testId
     * @return 对象信息
     */
    @Override
    public Test selectTestById(Long testId) { return testMapper.selectTestById(testId); }

    /**
     * 批量删除test信息
     *
     * @param ids 需要删除的数据id
     * @throws BusinessException
     */
    @Override
    public int deleteTestByIds(String ids) throws BusinessException
    {
        Long[] testIds = Convert.toLongArray(ids);
        return testMapper.deleteTestByIds(testIds);
    }

    /**
     * 新增保存test信息
     *
     * @param test
     * @return 结果
     */
    @Override
    public int insertTest(Test test)
    {
        test.setCreateBy(ShiroUtils.getLoginName());
        return testMapper.insertTest(test);
    }

    /**
     * 修改保存test信息
     *
     * @param test
     * @return 结果
     */
    @Override
    public int updateTest(Test test)
    {
        test.setUpdateBy(ShiroUtils.getLoginName());
        return testMapper.updateTest(test);
    }

    /**
     * 校验test名称是否唯一
     *
     * @param test
     * @return 信息
     */
    @Override
    public String checkTestNameUnique(Test test)
    {
        Long testId = StringUtils.isNull(test.getTestId()) ? -1L : test.getTestId();
        Test info = testMapper.checkTestNameUnique(test.getTestName());
        if(StringUtils.isNotNull(info) && info.getTestId().longValue() != testId.longValue())
        {
            return UserConstants.TEST_NAME_NOT_UNIQUE;
        }
        return UserConstants.TEST_NAME_UNIQUE;
    }

    /**
     * 校验test编码是否唯一
     *
     * @param test
     * @return 信息
     */
    @Override
    public String checkTestCodeUnique(Test test)
    {
        Long testId = StringUtils.isNull(test.getTestId()) ? -1L : test.getTestId();
        Test info = testMapper.checkTestCodeUnique(test.getTestCode());
        if (StringUtils.isNotNull(info) && info.getTestId().longValue() != testId.longValue())
        {
            return UserConstants.TEST_CODE_NOT_UNIQUE;
        }
        return UserConstants.TEST_CODE_UNIQUE;
    }
}

MyTestController.java

package com.ruoyi.project.system.test.controller;

import java.util.List;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.web.controller.BaseController;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.framework.web.page.TableDataInfo;
import com.ruoyi.project.system.test.domain.Test;
import com.ruoyi.project.system.test.service.ITestService;

/**
 * test信息操作处理
 *
 * @author XYM_
 */
@Controller
@RequestMapping("/system/test")
public class MyTestController extends BaseController
{
    private String prefix = "system/test";

    @Autowired
    private ITestService testService;

    @RequiresPermissions("system:test:view")
    @GetMapping()
    public String operlog() { return prefix + "/test"; }

    @RequiresPermissions("system:test:list")
    @PostMapping("/list")
    @ResponseBody
    public TableDataInfo list(Test test)
    {
        startPage();
        List<Test> list = testService.selectTestList(test);
        return getDataTable(list);
    }

    @Log(title = "测试管理", businessType = BusinessType.EXPORT)
    @RequiresPermissions("system:test:export")
    @PostMapping("/export")
    @ResponseBody
    public AjaxResult export(Test test)
    {
        List<Test> list = testService.selectTestList(test);
        ExcelUtil<Test> util = new ExcelUtil(Test.class);
        return util.exportExcel(list, "测试数据");
    }

    @Log(title = "测试管理", businessType = BusinessType.DELETE)
    @RequiresPermissions("system:test:remove")
    @PostMapping("/remove")
    @ResponseBody
    public AjaxResult remove(String ids)
    {
        try{
            return toAjax(testService.deleteTestByIds(ids));
        }catch(Exception e){
            return error(e.getMessage());
        }
    }

    /**
     * 新增test条目
     */
    @GetMapping("/add")
    public String add() { return prefix + "/add"; }

    /**
     * 新增保存test
     */
    @Log(title = "测试管理", businessType = BusinessType.INSERT)
    @RequiresPermissions("system:test:add")
    @PostMapping("/add")
    @ResponseBody
    public AjaxResult addSave(@Validated Test test)
    {
        if (UserConstants.TEST_NAME_NOT_UNIQUE.equals(testService.checkTestNameUnique(test)))
        {
            return error("新增测试条目'" + test.getTestName() + "'失败,测试名称已存在");
        }
        else if (UserConstants.TEST_CODE_NOT_UNIQUE.equals(testService.checkTestCodeUnique(test)))
        {
            return error("新增测试条目'" + test.getTestName() + "'失败,测试编码已存在");
        }
        return toAjax(testService.insertTest(test));
    }

    /**
     * 修改test条目
     */
    @GetMapping("/edit/{testId}")
    public String edit(@PathVariable("testId") Long testId, ModelMap mmap)
    {
        mmap.put("test",testService.selectTestById(testId));
        return prefix + "/edit";
    }

    /**
     * 修改保存test
     */
    @Log(title = "测试管理", businessType = BusinessType.UPDATE)
    @RequiresPermissions("system:test:edit")
    @PostMapping("/edit")
    @ResponseBody
    public AjaxResult editSave(@Validated Test test)
    {
        if (UserConstants.TEST_NAME_NOT_UNIQUE.equals(testService.checkTestNameUnique(test)))
        {
            return error("修改测试条目'" + test.getTestName() + "'失败,测试名称已存在");
        }
        else if (UserConstants.TEST_CODE_NOT_UNIQUE.equals(testService.checkTestCodeUnique(test)))
        {
            return error("修改测试条目'" + test.getTestName() + "'失败,测试编码已存在");
        }
        return toAjax(testService.updateTest(test));
    }

    /**
     * 校验测试名称
     */
    @PostMapping("/checkTestNameUnique")
    @ResponseBody
    public String checkTestNameUnique(Test test) {
        return testService.checkTestNameUnique(test);
    }

    /**
     * 校验测试编码
     */
    @PostMapping("/checkTestCodeUnique")
    @ResponseBody
    public String checkTestCodeUnique(Test test) { return testService.checkTestCodeUnique(test); }
}

6.2 前端

add.html


<html lang="zh" xmlns:th="http://www.thymeleaf.org" >
<head>
    <th:block th:include="include :: header('新增测试')" />
head>
<body class="white-bg">
<div class="wrapper wrapper-content animated fadeInRight ibox-content">
    <form class="form-horizontal m" id="form-test-add">
        <div class="form-group">
            <label class="col-sm-3 control-label is-required">测试名称:label>
            <div class="col-sm-8">
                <input class="form-control" type="text" name="testName" id="testName" required>
            div>
        div>
        <div class="form-group">
            <label class="col-sm-3 control-label is-required">测试编码:label>
            <div class="col-sm-8">
                <input class="form-control" type="text" name="testCode" id="testCode" required>
            div>
        div>
        <div class="form-group">
            <label class="col-sm-3 control-label">测试状态:label>
            <div class="col-sm-8">
                <div class="radio-box" th:each="dict : ${@dict.getType('sys_normal_disable')}">
                    <input type="radio" th:id="${dict.dictCode}" name="status" th:value="${dict.dictValue}" th:checked="${dict.default}">
                    <label th:for="${dict.dictCode}" th:text="${dict.dictLabel}">label>
                div>
            div>
        div>
        <div class="form-group">
            <label class="col-sm-3 control-label">备注:label>
            <div class="col-sm-8">
                <textarea id="remark" name="remark" class="form-control">textarea>
            div>
        div>
    form>
div>
<th:block th:include="include :: footer" />
<script type="text/javascript">
    var prefix = ctx + "system/test";

    $("#form-test-add").validate({
        onkeyup: false,
        rules:{
            testName:{
                remote: {
                    url: ctx + "system/test/checkTestNameUnique",
                    type: "post",
                    dataType: "json",
                    data: {
                        "testName" : function() {
                            return $.common.trim($("#testName").val());
                        }
                    },
                    dataFilter: function(data, type) {
                        return $.validate.unique(data);
                    }
                }
            },
            testCode:{
                remote: {
                    url: ctx + "system/test/checkTestCodeUnique",
                    type: "post",
                    dataType: "json",
                    data: {
                        "testCode" : function() {
                            return $.common.trim($("#testCode").val());
                        }
                    },
                    dataFilter: function(data, type) {
                        return $.validate.unique(data);
                    }
                }
            },
        },
        messages: {
            "testCode": {
                remote: "测试编码已经存在"
            },
            "testName": {
                remote: "测试名称已经存在"
            }
        },
        focusCleanup: true
    });

    function submitHandler() {
        if ($.validate.form()) {
            $.operate.save(prefix + "/add", $('#form-test-add').serialize());
        }
    }
script>
body>
html>

edit.html


<html lang="zh" xmlns:th="http://www.thymeleaf.org" >
<head>
    <th:block th:include="include :: header('修改测试')" />
head>
<body class="white-bg">
<div class="wrapper wrapper-content animated fadeInRight ibox-content">
    <form class="form-horizontal m" id="form-test-edit" th:object="${test}">
        <input id="testId" name="testId" type="hidden" th:field="*{testId}"/>
        <div class="form-group">
            <label class="col-sm-3 control-label is-required">测试名称:label>
            <div class="col-sm-8">
                <input class="form-control" type="text" name="testName" id="testName" th:field="*{testName}" required>
            div>
        div>
        <div class="form-group">
            <label class="col-sm-3 control-label is-required">测试编码:label>
            <div class="col-sm-8">
                <input class="form-control" type="text" name="testCode" id="testCode" th:field="*{testCode}" required>
            div>
        div>
        <div class="form-group">
            <label class="col-sm-3 control-label">测试状态:label>
            <div class="col-sm-8">
                <div class="radio-box" th:each="dict : ${@dict.getType('sys_normal_disable')}">
                    <input type="radio" th:id="${dict.dictCode}" name="status" th:value="${dict.dictValue}" th:field="*{status}">
                    <label th:for="${dict.dictCode}" th:text="${dict.dictLabel}">label>
                div>
            div>
        div>
        <div class="form-group">
            <label class="col-sm-3 control-label">备注:label>
            <div class="col-sm-8">
                <textarea id="remark" name="remark" class="form-control">[[*{remark}]]textarea>
            div>
        div>
    form>
div>
<th:block th:include="include :: footer" />
<script type="text/javascript">
    var prefix = ctx + "system/test";

    $("#form-test-edit").validate({
        onkeyup: false,
        rules:{
            testName:{
                remote: {
                    url: ctx + "system/test/checkTestNameUnique",
                    type: "post",
                    dataType: "json",
                    data: {
                        "testId": function() {
                            return $("input[name='testId']").val();
                        },
                        "testName" : function() {
                            return $.common.trim($("#testName").val());
                        }
                    },
                    dataFilter: function(data, type) {
                        return $.validate.unique(data);
                    }
                }
            },
            testCode:{
                remote: {
                    url: ctx + "system/test/checkTestCodeUnique",
                    type: "post",
                    dataType: "json",
                    data: {
                        "testId": function() {
                            return $("input[name='testId']").val();
                        },
                        "testCode" : function() {
                            return $.common.trim($("#testCode").val());
                        }
                    },
                    dataFilter: function(data, type) {
                        return $.validate.unique(data);
                    }
                }
            },
        },
        messages: {
            "testCode": {
                remote: "测试编码已经存在"
            },
            "testName": {
                remote: "测试名称已经存在"
            }
        },
        focusCleanup: true
    });

    function submitHandler() {
        if ($.validate.form()) {
            $.operate.save(prefix + "/edit", $('#form-test-edit').serialize());
        }
    }
script>
body>
html>

test.html


<html lang="zh" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
    <th:block th:include="include :: header('测试列表')" />
head>
<body class="gray-bg">
    <div class="container-div">
        <div class="row">
            <div class="col-sm-12 search-collapse">
                <form id="test-form">
                    <div class="select-list">
                        <ul>
                            <li>
                                测试编码:<input type="text" name="testCode"/>
                            li>
                            <li>
                                测试名称:<input type="text" name="testName"/>
                            li>
                            <li>
                                测试状态:<select name="status" th:with="type=${@dict.getType('sys_normal_disable')}">
                                <option value="">所有option>
                                <option th:each="dict : ${type}" th:text="${dict.dictLabel}" th:value="${dict.dictValue}">option>
                            select>
                            li>
                            <li>
                                <a class="btn btn-primary btn-rounded btn-sm" onclick="$.table.search()"><i class="fa fa-search">i> 搜索a>
                                <a class="btn btn-warning btn-rounded btn-sm" onclick="$.form.reset()"><i class="fa fa-refresh">i> 重置a>
                            li>
                        ul>
                    div>
                form>
            div>

            <div class="btn-group-sm" id="toolbar" role="group">
                <a class="btn btn-success" onclick="$.operate.add()" shiro:hasPermission="system:test:add">
                    <i class="fa fa-plus">i> 新增
                a>
                <a class="btn btn-primary single disabled" onclick="$.operate.edit()" shiro:hasPermission="system:test:edit">
                    <i class="fa fa-edit">i> 修改
                a>
                <a class="btn btn-danger multiple disabled" onclick="$.operate.removeAll()" shiro:hasPermission="system:test:remove">
                    <i class="fa fa-remove">i> 删除
                a>
                <a class="btn btn-warning" onclick="$.table.exportExcel()" shiro:hasPermission="system:test:export">
                    <i class="fa fa-download">i> 导出
                a>
            div>

            <div class="col-sm-12 select-table table-striped">
                <table id="bootstrap-table">table>
            div>
        div>
    div>
    <th:block th:include="include :: footer" />
    <script th:inline="javascript">
        var editFlag = [[${@permission.hasPermi('system:test:edit')}]];
        var removeFlag = [[${@permission.hasPermi('system:test:remove')}]];
        var datas = [[${@dict.getType('sys_normal_disable')}]];
        var prefix = ctx + "system/test";

        $(function() {
            var options = {
                url: prefix + "/list",
                createUrl: prefix + "/add",
                updateUrl: prefix + "/edit/{id}",
                removeUrl: prefix + "/remove",
                exportUrl: prefix + "/export",
                modalName: "测试",
                columns: [{
                    checkbox: true
                },
                    {
                        field: 'testId',
                        title: '测试编号'
                    },
                    {
                        field: 'testCode',
                        title: '测试编码',
                        sortable: true
                    },
                    {
                        field: 'testName',
                        title: '测试名称',
                        sortable: true
                    },
                    {
                        field: 'status',
                        title: '状态',
                        align: 'center',
                        formatter: function(value, row, index) {
                            return $.table.selectDictLabel(datas, value);
                        }
                    },
                    {
                        field: 'createTime',
                        title: '创建时间',
                        sortable: true
                    },
                    {
                        title: '操作',
                        align: 'center',
                        formatter: function(value, row, index) {
                            var actions = [];
                            actions.push('+ editFlag + '" href="javascript:void(0)" οnclick="$.operate.edit(\'' + row.testId + '\')">编辑 ');
                            actions.push('+ removeFlag + '" href="javascript:void(0)" οnclick="$.operate.remove(\'' + row.testId + '\')">删除');
                            return actions.join('');
                        }
                    }]
            };
            $.table.init(options);
        });
    script>
body>
html>

你可能感兴趣的:(Web开发学习笔记20210427_RuoYi-fast新建子菜单并实现一些功能)