谷粒商城-全栈-19 商品服务-三级分类-删除

这一节实现三级分类的删除功能。

一、页面效果

前端页面增加删除按钮,可以参考 Element官方文档

修改文件 src/views/modules/product/category.vue

<!-- 分类 -->
<template>
  <el-tree
    :data="menus"
    :props="defaultProps"
    :expand-on-click-node="false"
    show-checkbox
    node-key="catId"
  >
    <span class="custom-tree-node" slot-scope="{ node, data }">
      <span>{{ node.label }}</span>
      <span>
        <el-button v-if="node.level <= 2" type="text" size="mini" @click="() => append(data)">Append</el-button>
        <el-button
          v-if="node.childNodes.length == 0"
          type="text"
          size="mini"
          @click="() => remove(node, data)"
        >Delete</el-button>
      </span>
    </span>
  </el-tree>
</template>

<script>
// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
// 例如:import 《组件名称》 from '《组件路径》';

export default {
  // import引入的组件需要注入到对象中才能使用
  components: {},
  props: {},
  data() {
    return {
      menus: [],
      defaultProps: {
        children: "children",
        label: "name",
      },
    };
  },
  // 监听属性 类似于data概念
  computed: {},
  // 监控data中的数据变化
  watch: {},
  methods: {
    getMenus() {
      this.$http({
        url: this.$http.adornUrl("/product/category/list/tree"),
        method: "get",
      }).then(({ data }) => {
        this.menus = data.data;
      });
    },
    append(data) {
      console.log("append", data);
    },

    remove(node, data) {
      console.log("remove_data", data);
      console.log("remove_node", node);
    },
  },
  // 生命周期 - 创建完成(可以访问当前this实例)
  created() {
    this.getMenus();
  },
  // 生命周期 - 挂载完成(可以访问DOM元素)
  mounted() {},
  beforeCreate() {}, // 生命周期 - 创建之前
  beforeMount() {}, // 生命周期 - 挂载之前
  beforeUpdate() {}, // 生命周期 - 更新之前
  updated() {}, // 生命周期 - 更新之后
  beforeDestroy() {}, // 生命周期 - 销毁之前
  destroyed() {}, // 生命周期 - 销毁完成
  activated() {}, // 如果页面有keep-alive缓存功能,这个函数会触发
};
</script>
<style>
</style>

file

二、逻辑删除

逆向生成根据分类表已经生成了删除功能,该删除功能是直接从数据库里边删掉数据,即物理删除,但是我们业务只需要逻辑删除,所以,原来的删除操作需要做改动。

product/controller/CategoryController.java 逆向生成的删除代码

/**
   * 删除
   *
   * @RequestBody:获取请求体,必须发送POST请求,也就是只有POST才有请求体@RequestBody
   * SpringMVC 自动将请求体的数据(json),转为对应的对象
   */
  @RequestMapping("/delete")
  //@RequiresPermissions("product:category:delete")
  public R delete(@RequestBody Long[] catIds) {
    // categoryService.removeByIds(Arrays.asList(catIds));

    // 1、检查当前删除的,是否有在其他地方引用
    categoryService.removeMenuByIds(Arrays.asList(catIds));

    return R.ok();
  }

说明:

  • @RequestBody:获取请求体,必须发送POST请求,也就是只有POST才有请求体@RequestBody。
  • SpringMVC 自动将请求体的数据(json),转为对应的对象

添加删除方法 product/service/CategoryService.java

public interface CategoryService extends IService<CategoryEntity> {

    PageUtils queryPage(Map<String, Object> params);

  List<CategoryEntity> listWithTree();

  void removeMenuByIds(List<Long> asList);
}

实现删除方法product/service/impl/CategoryServiceImpl.java

@Override
  public void removeMenuByIds(List<Long> asList) {
    // TODO::1、检查当前删除的,是否有在其他地方引用
    baseMapper.deleteBatchIds(asList);

  }

mybatis-plus逻辑删除

mybatis-plus 逻辑删除官方文档

步骤:
1、配置全局的逻辑删除规则(省略)
2、配置逻辑删除的组件Bean(省略)
3、给bean加上逻辑删除注解@TableLogic

实现:
修改 gulimall-product/src/main/resources/application.yml,添加逻辑删除配置:

mybatis-plus:
  mapper-location: classpath:/mapper/**/*.xml
  global-config:
    db-config:
      id-type: auto  # id主键自增
      logic-delete-field: flag  # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
      logic-delete-value: 1 # 逻辑已删除值(默认为 1)
      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)

修改 product/entity/CategoryEntity.java,添加逻辑删除注解

    /**
     * 是否显示[0-不显示,1显示]
     */
    @TableLogic(value = "1", delval = "0")
    private Integer showStatus;

在PostMan调用该接口进行测试:
file

http://localhost:8888/api/product/category/delete

查看数据库,show_status 状态改为 0 了,即不显示了
file

我们可以看下控制台打印的SQL语句,实际上是进行了更新的操作:

2020-08-18 01:39:18.541 DEBUG 46703 --- [nio-6000-exec-1] c.a.g.p.dao.CategoryDao.deleteBatchIds   : ==>  Preparing: UPDATE pms_category SET show_status=0 WHERE cat_id IN ( ? ) AND show_status=1 
2020-08-18 01:39:18.569 DEBUG 46703 --- [nio-6000-exec-1] c.a.g.p.dao.CategoryDao.deleteBatchIds   : ==> Parameters: 1000(Long)
2020-08-18 01:39:18.677 DEBUG 46703 --- [nio-6000-exec-1] c.a.g.p.dao.CategoryDao.deleteBatchIds   : <==    Updates: 1

三、效果细化

后端删除的接口已经写好了,现在做前端页面的接口调用和页面细节处理。

思路:有关删除接口的实现可以参考 role.vue 里边的方法。

新建 http get和post代码片段,方便使用:
vue.json

{
    "生成vue模板": {
        "prefix": "vue",
        "body": [
            "<!-- $1 -->",
            "<template>",
            "<div class='$2'>$5</div>",
            "</template>",
            "",
            "<script>",
            "//这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)",
            "//例如:import 《组件名称》 from '《组件路径》';",
            "",
            "export default {",
            "//import引入的组件需要注入到对象中才能使用",
            "components: {},",
            "props: {},",
            "data() {",
            "//这里存放数据",
            "return {",
            "",
            "};",
            "},",
            "//监听属性 类似于data概念",
            "computed: {},",
            "//监控data中的数据变化",
            "watch: {},",
            "//方法集合",
            "methods: {",
            "",
            "},",
            "//生命周期 - 创建完成(可以访问当前this实例)",
            "created() {",
            "",
            "},",
            "//生命周期 - 挂载完成(可以访问DOM元素)",
            "mounted() {",
            "",
            "},",
            "beforeCreate() {}, //生命周期 - 创建之前",
            "beforeMount() {}, //生命周期 - 挂载之前",
            "beforeUpdate() {}, //生命周期 - 更新之前",
            "updated() {}, //生命周期 - 更新之后",
            "beforeDestroy() {}, //生命周期 - 销毁之前",
            "destroyed() {}, //生命周期 - 销毁完成",
            "activated() {}, //如果页面有keep-alive缓存功能,这个函数会触发",
            "}",
            "</script>",
            "<style lang='scss' scoped>",
            "//@import url($3); 引入公共css类",
            "$4",
            "</style>"
        ],
        "description": "生成vue模板(全局用户代码片段)"
    },
    "http-get请求": {
        "prefix": "httpget",
        "body": [
            "this.\\$http({",
            "url: this.\\$http.adornUrl(''),",
            "method: 'get',",
            "params: this.\\$http.adornParams({})",
            "}).then(({ data }) => {",
            "})"
        ],
        "description": "httpGET请求"
    },
    "http-post请求": {
        "prefix": "httppost",
        "body": [
            "this.\\$http({",
            "url: this.\\$http.adornUrl(''),",
            "method: 'post',",
            "data: this.\\$http.adornData(data, false)",
            "}).then(({ data }) => { });"
        ],
        "description": "httpPOST请求"
    }
}

src/views/modules/product/category.vue 增加删除方法

methods: {
    getMenus() {
      this.$http({
        url: this.$http.adornUrl("/product/category/list/tree"),
        method: "get",
      }).then(({ data }) => {
        this.menus = data.data;
      });
    },
    append(data) {
      console.log("append", data);
    },
    remove(node, data) {
      var ids = [data.catId];
      this.$confirm(`是否删除【${data.name}】菜单?`, "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      })
        .then(() => {
          this.$http({
            url: this.$http.adornUrl("/product/category/delete"),
            method: "post",
            data: this.$http.adornData(ids, false),
          }).then(({ data }) => {
            this.$message({
              message: "菜单删除成功", // 菜单删除成功仍然要展示父节点
              type: "success",
            });

            // 重新刷新页面
            this.getMenus();
            // 设置需要默认展开的菜单
            this.expendedKey = [node.parent.data.catId];
          });
        })
        .catch(() => {});
    },
  },

该页面实现了,删除接口调用,确认删除提示,删除成功提示,删除刷新,删除之后父菜单栏默认展开。

菜单栏默认展开,需要添加该属性:

 :default-expanded-keys="expendedKey"

data增加 expendedKey 属性

 data() {
    return {
      menus: [],
      expendedKey: [],
      defaultProps: {
        children: "children",
        label: "name",
      },
    };
  },

remove方法里边增加属性赋值

  // 设置需要默认展开的菜单
    this.expendedKey = [node.parent.data.catId];

file

前端页面完整代码:

<!-- 分类 -->
<template>
  <el-tree
    :data="menus"
    :props="defaultProps"
    :expand-on-click-node="false"
    show-checkbox
    node-key="catId"
    :default-expanded-keys="expendedKey"
  >
    <span class="custom-tree-node" slot-scope="{ node, data }">
      <span>{{ node.label }}</span>
      <span>
        <el-button v-if="node.level <= 2" type="text" size="mini" @click="() => append(data)">Append</el-button>
        <el-button
          v-if="node.childNodes.length == 0"
          type="text"
          size="mini"
          @click="() => remove(node, data)"
        >Delete</el-button>
      </span>
    </span>
  </el-tree>
</template>

<script>
// 这里可以导入其他文件(比如:组件,工具js,第三方插件js,json文件,图片文件等等)
// 例如:import 《组件名称》 from '《组件路径》';

export default {
  // import引入的组件需要注入到对象中才能使用
  components: {},
  props: {},
  data() {
    return {
      menus: [],
      expendedKey: [],
      defaultProps: {
        children: "children",
        label: "name",
      },
    };
  },
  // 监听属性 类似于data概念
  computed: {},
  // 监控data中的数据变化
  watch: {},
  methods: {
    getMenus() {
      this.$http({
        url: this.$http.adornUrl("/product/category/list/tree"),
        method: "get",
      }).then(({ data }) => {
        this.menus = data.data;
      });
    },
    append(data) {
      console.log("append", data);
    },

    remove(node, data) {
      var ids = [data.catId];
      this.$confirm(`是否删除【${data.name}】菜单?`, "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      })
        .then(() => {
          this.$http({
            url: this.$http.adornUrl("/product/category/delete"),
            method: "post",
            data: this.$http.adornData(ids, false),
          }).then(({ data }) => {
            this.$message({
              message: "菜单删除成功", // 菜单删除成功仍然要展示父节点
              type: "success",
            });

            // 重新刷新页面
            this.getMenus();
            // 设置需要默认展开的菜单
            this.expendedKey = [node.parent.data.catId];
          });
        })
        .catch(() => {});
    },
  },
  // 生命周期 - 创建完成(可以访问当前this实例)
  created() {
    this.getMenus();
  },
  // 生命周期 - 挂载完成(可以访问DOM元素)
  mounted() {},
  beforeCreate() {}, // 生命周期 - 创建之前
  beforeMount() {}, // 生命周期 - 挂载之前
  beforeUpdate() {}, // 生命周期 - 更新之前
  updated() {}, // 生命周期 - 更新之后
  beforeDestroy() {}, // 生命周期 - 销毁之前
  destroyed() {}, // 生命周期 - 销毁完成
  activated() {}, // 如果页面有keep-alive缓存功能,这个函数会触发
};
</script>
<style>
</style>

为者常成,行者常至