谷粒商城_04_商品CRUD
文章目录
- 商品服务-分类管理
- 查出所有分类以及子分类
- 后端编写
- 前端展示
- 服务注册
- 配置网关
- 503问题
- 跨域
- 跨域流程
- 解决跨域
- 服务注册
- 配置网关
- 删除数据
- 后端接口
- 逻辑删除
- 测试
- 前端请求
- 删除数据
- 新增分类
- 修改分类
- 拖拽效果
- 批量删除
谷粒商城_01_环境搭建
谷粒商城_02_Nacos、网关
谷粒商城_03_前端基础
商品服务-分类管理

查出所有分类以及子分类
后端编写
1、定义接口查询所有数据:gulimall-product的com.liu.gulimall.product.controller.CategoryController添加如下方法
/*** 商品三级分类** @author liujianyu* @email 380404812@qq.com* @date 2021-12-09 19:15:00*/
@RestController
@RequestMapping("product/category")
public class CategoryController {@Autowiredprivate CategoryService categoryService;/*** 查出所有分类以及子分类,以树形结构组装起来*/@RequestMapping("/list/tree")public R list(){List<CategoryEntity> entities = categoryService.listWithTree();return R.ok().put("data", entities);}
}
2、在service层中创建方法 listWithTree
/*** 商品三级分类** @author liujianyu* @email 380404812@qq.com* @date 2021-12-09 18:29:39*/
public interface CategoryService extends IService<CategoryEntity> {PageUtils queryPage(Map<String, Object> params);/*** 将数据以树形分类,分出三级* @return*/List<CategoryEntity> listWithTree();
}
3、在service层的Impl中实现方法
- 最重要的是商品实体中需要多一个字段,children,为了放此分类的子分类
- 先把所有商品查出来然后根据数据库字段父id查出,没有父id的一级商品,然后在递归找子分类
- 主要理解流式编程:filter、map、sorted、collect
- 一级分类有二级,二级有三级,所以是一个递归的方式
@Service("categoryService")
public class CategoryServiceImpl extends ServiceImpl<CategoryDao, CategoryEntity> implements CategoryService {
// @Autowired
// private CategoryDao categoryDao;
// ServiceImpl已经帮我们注入了dao,就不用自己注入了 @Overridepublic List<CategoryEntity> listWithTree() {// 1 查出所有分类List<CategoryEntity> entities = baseMapper.selectList(null);// 2 组装成父子的树形结构List<CategoryEntity> level1Menus = entities.stream().filter((categoryEntity) ->{ // 过滤// long类型的比较不要直接使用==,要用到longValue()来比较return categoryEntity.getParentCid().longValue() == 0;} // 过滤只要父id等于0的数据,也就是一级分类).map((categoryEntity)->{ // 为过滤后的一级分类的每一个数据设置他的子分类categoryEntity.setChildren(getChildrens(categoryEntity,entities));return categoryEntity;}).sorted((categoryEntity1,categoryEntity2)->{ // 升序排序根据字段sort来让sort小的的排前面 由于categoryEntity1可能已经为空null了,所以先判断return (categoryEntity1.getSort() == null? 0: categoryEntity1.getSort())-(categoryEntity2.getSort() == null?0:categoryEntity2.getSort());}).collect(Collectors.toList()); // 将过滤的数据收集成数组return level1Menus;}// 递归查找所有菜单的子菜单,把当前分类,和总分类传进来private List<CategoryEntity> getChildrens(CategoryEntity root, List<CategoryEntity> all) {List<CategoryEntity> children = all.stream().filter(categoryEntity -> {// 过滤取出父id等于当前分类的数据return categoryEntity.getParentCid().longValue() == root.getCatId().longValue();}).map(categoryEntity -> { // 每一个子分类还有可能有子分类// 1 找到子菜单categoryEntity.setChildren(getChildrens(categoryEntity, all));return categoryEntity;}).sorted((menu1, menu2) -> {// 2 菜单的排序 由于menu可能已经为空null了,所以先判断return (menu1.getSort() == null?0:menu1.getSort()) - (menu2.getSort() == null?0:menu2.getSort());}).collect(Collectors.toList());return children;}
}
前端展示
1、创建商品系统的目录,以及内容转发的路由


数据库同步数据

2、根据人人开源策略,根据路由product/category 来请求product-category,比如sys-role具体的视图在renren-fast-vue/views/modules/sys/role.vue 所以要自定义我们的product/category视图的话,就是创建mudules/product/category.vue,根据策略创建product文件,在文件下创建category.vue组件,这样请求路由就会跳转到这个组件中,


3、编写category.vue组件
- 可以模仿人人开源的组件方式,包括里面怎么转发,怎么数据交互等。
- 采用Element UI来开发,采用树形控件来管理三级数据

<template><el-tree:data="data":props="defaultProps"@node-click="handleNodeClick">el-tree>template><script>
export default {data() {return {data: [],defaultProps: {children: "children",label: "label",},};},methods: {handleNodeClick(data) {console.log(data);},getMenus(data) {// 自定义方法this.$http({// htpp请求url: this.$http.adornUrl("/product/category/list/tree"), // 请求后端的接口method: "get",}).then((data) => {// 成功了之后的操作console.log("请求数据," + data);});},},created() {// 加载组件的时候就调用改方法this.getMenus();},
};
script><style lang="scss" scoped>
style>
4、我们的数据肯定是从后端取出来,所以要请求gulimall-product服务后端的接口,但是现在请求的是:人人开源的接口,所以修改前端配置:renren-fast-vue\static\config\index.js中修改,但是我们gulimall-product服务可能在很多服务器上,所以每次都要修改端口很麻烦,直接交给网关来做,让网关跟我们去请求服务器

由于我们想要访问的是:http://localhost:10000/product/category/list/tree这个请求,而且这里面的10000端口也存在多服务问题,所以采用网关来转发请求。http://localhost:88/product/category/list/tree

服务注册
-
我们登录有验证码,这个是renrne-fast服务的数据,所以先注册renrne-fast服务
-
通过网关把请求转发给后端renren-fast服务,所以先让网关发现服务,也就是注册服务到注册中心去
1、在renren-fast中导入的依赖【如果依赖无法彻底删除,复制全文,删除原来的,重新创建pom文件】

2、在配置文件中配置,服务名称和注册中心地址
3、注册到注册中心中,加入注解:@EnableDiscoveryClient
4、启动nacos,发现服务

配置网关
- 验证码前端请求:http://localhost:88/api/captcha.jpg 我们需要的是:http://localhost:8080/renren-fast/captcha.jpg

- 配置网关
spring:cloud:gateway:# 路由规则routes:- id: admin_routeuri: lb://renren-fast # 路由给renren-fast,lb代表负载均衡predicates: # 什么情况下路由给它- Path=/api/** # 默认前端项目都带上api前缀,但是这样还是请求的:http://localhost:88/api/..我们想要请求 http://localhost:8080/renren-fast/..,所以加上过滤器# 所以在前端index.js中改为 window.SITE_CONFIG['baseUrl'] = 'http://localhost:88/api';filters: # 过滤器,- RewritePath=/api/(?>.*),/renren-fast/$\{segment}
- 启动renren-fast、gulimall-gateway、nacos,发现验证出现
503问题
- 检查服务是否配置到注册中心,并且保证在同一命名空间
- 检查配置是否正确,路径,服务名等
- 在gateway中更换alibaba依赖版本为2.2.0;

- 200,没有图片:将renren-fast的跨域配置中的
allowedOrigins("*")改为allowedOriginPatterns("*")

- 当验证码成功后登录遇到跨域问题
跨域

已拦截跨源请求:同源策略禁止读取位于 http://localhost:88/api/sys/login 的远程资源。(原因:CORS 头缺少 ‘Access-Control-Allow-Origin’)。
这是一种跨域问题。访问的域名和端口和原来的请求不同,请求就会被限制
跨域:指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对js施加的安全限制。(ajax可以)
同源策略:是指协议,域名,端囗都要相同,其中有一个不同都会产生跨域;
跨域流程
- 浏览器先发送OPTIONS,请求服务器能否跨域,服务器需要配置允许跨域,然后浏览器在发送真实请求

解决跨域

- 我们现在学习常用,自己后端编写配置

- 在网关中定义
GulimallCorsConfiguration类,该类用来做过滤,允许所有的请求跨域。【注意】需要把renren-fast的跨域注解掉,因为先走我们网关跨域再走renrne-fast就重复了,然后就成功解决跨域
/*** @author ljy* @version 1.0.0* @Description TODO* @createTime 2021年12月12日 22:46:00*/
@Configuration // gateway
public class GulimallCorsConfiguration {@Bean // 添加过滤器public CorsWebFilter corsWebFilter(){// 基于url跨域,选择reactive包下的UrlBasedCorsConfigurationSource source=new UrlBasedCorsConfigurationSource();// 跨域配置信息CorsConfiguration corsConfiguration = new CorsConfiguration();// 允许跨域的头corsConfiguration.addAllowedHeader("*");// 允许跨域的请求方式corsConfiguration.addAllowedMethod("*");// 允许跨域的请求来源
// corsConfiguration.addAllowedOrigin("*");corsConfiguration.addAllowedOriginPattern("*");// 是否允许携带cookie跨域corsConfiguration.setAllowCredentials(true);// 任意url都要进行跨域配置source.registerCorsConfiguration("/**",corsConfiguration);return new CorsWebFilter(source);}
}
服务注册
- 在显示商品系统/分类信息的时候,出现了404异常,请求的http://localhost:88/api/product/category/list/tree不存在
这是因为网关上所做的路径映射不正确,映射后的路径为http://localhost:8080/renren-fast/product/category/list/tree
但是只有通过http://localhost:10000/product/category/list/tree路径才能够正常访问,所以会报404异常。 - 所以我们注册gulimall-product到服务中心,重新再配置网关
1、编写配置文件,加上配置中心地址和服务名称
2、添加注解@EnableDiscoveryClient
3、添加spring-cloud-starter-alibaba-nacos-discovery服务发现依赖
配置网关
- 在网关配置中添加gulimall-product服务的路由配置,并且要写在renren-fast的前面, 路由的顺序有影响,把更具体的路由请求放前面
- id: product_routeuri: lb://gulimall-productpredicates:- Path=/api/product/**filters:- RewritePath=/api/(?>.*),/$\{segment}
再访问http://localhost:88/api/product/category/list/tree,数据正常出现,数据正常显示,就应该编写页面了
<template><el-tree:data="menus" :props="defaultProps">el-tree>
template><script>
export default {data() {return {menus: [], // 请求后端返回的数据defaultProps: {children: "children", // 表示children显示data的children属性label: "name", // label指的是显示数据中的哪个属性这里就显示data中的name属性,这些label、children都可以在官方文档 中查看的},};},methods: {getMenus(data) {// 自定义方法this.$http({// htpp请求url: this.$http.adornUrl("/product/category/list/tree"), // 请求后端的接口method: "get",// }).then((data) => { 由于我们的data是一个对象,我们只需要拿到其中的data属性的数据,所以要把data对象解构出来}).then(({ data }) => {// 成功了之后的操作// console.log("请求数据," + data); +号不能显示出数据// console.log("请求数据," , data); ,才可以显示出数据console.log("请求数据,", data.data); // 解构data对象取出data属性this.menus = data.data; // 将后端取出的数据给到组件中的menus属性});}},created() {// 加载组件的时候就调用改方法this.getMenus();},
};
script><style lang="scss" scoped>
style>
删除数据
后端接口
- com.liu.gulimall.product.controller.CategoryController
/*** 删除* @RequestBody 获取到请求体里面的内容,必须发送Post请求* springMVC会自动将请求体的数据(json),转为对应的对象(Long[])*/
@RequestMapping("/delete")
public R delete(@RequestBody Long[] catIds){
// categoryService.removeByIds(Arrays.asList(catIds));// 删除之前需要判断待删除的菜单那是否被别的地方所引用。categoryService.removeMenuByIds(Arrays.asList(catIds));return R.ok();
}
- com/liu/gulimall/product/service/impl/CategoryServiceImpl.java
@Override
public void removeMenuByIds(List<Long> asList) {// TODO 先检查当前的菜单是否被别的地方所引用// TODO表示代办事项,以后可以直接在最下方查看代办// 开发期间多用逻辑删除:使用字段来标识baseMapper.deleteBatchIds(asList);
}
逻辑删除
- 多数时候,我们并不希望删除数据,而是标记它被删除了,这就是逻辑删除; 逻辑删除是mybatis-plus 的内容,会在项目中配置一些内容,告诉此项目执行delete语句时并不删除,只是标志位 可以设置show_status为0,标记它已经被删除。
- 官网:https://mp.baomidou.com/ 中有提到逻辑删除的用法

1、配置mybatis-plus
# MapperScan
# sql映射文件位置
mybatis-plus:mapper-locations: classpath:/mapper/**/*.xmlglobal-config:db-config:id-type: autologic-delete-value: 1 # 表示逻辑已删除 logic-not-delete-value: 0
2、添加注解@TableLogic
/*** 是否显示[0-不显示,1显示],由于我们数据库和mybatis-plus规则相反,所以我们自己定义逻辑删除,1表示未删除*/
@TableLogic(value = "1",delval = "0")
private Integer showStatus;
测试
http://localhost:88/api/product/category/delete


- 可以查看sql信息,我们需要配置日志
logging:level:com.liu.gulimall: debug
==> Preparing: UPDATE pms_category SET show_status=0 WHERE cat_id IN ( ? ) AND show_status=1
==> Parameters: 1000(Long)
<== Updates: 1
前端请求
- 直接采用Element UI跟我们提供的一些组件,然后结合renrne-vue中的一些请求方式即可
删除数据
- 只有没有子分类的才允许有删除的功能
- 在删除上绑定事件,弹出提示框,确定删除才会给后端发请求走数据库并且是逻辑删除
- 删除完后需要弹出成功消息,刷新新页面,并且展开刚才被删除的父分类
新增分类
- 只要有子分类的才允许有此功能
- 绑定事件,弹出弹框,在弹框中内嵌表格,给属性置为默认值,确定添加,给后端发请求
- 弹出消息,刷新页面,展开次分类
修改分类
- 自定义一个按钮,每一数据都有此功能
- 绑定事件,和新增共用一个弹框,只需判断弹框的类型,并且需要将此分类的数据先从后端请求(每次都拿数据库,避免高并发)回显到弹框的内嵌表格中,只回显需要修改的属性,并且只将修改的属性请求到后端
- 弹出提示,刷新页面,展开父分类


拖拽效果
-
避免误操作,是否开启拖拽需要设置一个按钮switch标签来确定
-
可以直接拖动每一分类来改变每一分类的顺序,以及其父和子分类,并且需要判断是否可以拖拽到此分类
-
官网:拖拽完成触发方法,不同拖拽,会有不同的父id,确定拖拽的新顺序,将所有改动的分类都放到一个数组里面
-
由于不能反复请求后端,设置批量保存按钮,触发事件请求后端,将数组传给后端
-
弹出提示,刷新页面,展开所有的父分类,并将数组置空
批量删除
- 获取被选中的元素,获取元素的id
- 弹出警告框,确定删除,请求后端,将元素id数组传入
- 刷新菜单
<template><div><el-switchv-model="draggable"active-text="开启拖拽"inactive-text="关闭拖拽">el-switch><el-button @click="batchSave()" v-if="draggable">批量保存el-button><el-button type="danger" @click="batchDelete">批量删除el-button><el-treenode-key="catId"show-checkbox:data="menus":props="defaultProps":expand-on-click-node="false":default-expanded-keys="expandedKey":draggable="draggable":allow-drop="allowDrop"@node-drop="handleDrop()"ref="menuTree"><span class="custom-tree-node" slot-scope="{ node, data }"><span>{{ node.label }}span><span><el-buttonv-if="node.level <= 2"type="text"size="mini"@click="() => append(data)">Appendel-button><el-button type="text" size="mini" @click="edit(data)">Editel-button><el-buttonv-if="node.childNodes == 0"type="text"size="mini"@click="() => remove(node, data)">Deleteel-button>span>span>el-tree><el-dialog:title="title":visible.sync="dialogVisible"width="30%":close-on-click-modal="false"><el-form :model="category"><el-form-item label="分类名"><el-input v-model="category.name" autocomplete="off">el-input>el-form-item><el-form-item label="图标"><el-input v-model="category.icon" autocomplete="off">el-input>el-form-item><el-form-item label="计量单位"><el-inputv-model="category.productUnit"autocomplete="off">el-input>el-form-item>el-form><span slot="footer" class="dialog-footer"><el-button @click="dialogVisible = false">取 消el-button><el-button type="primary" @click="submitData()">确 定el-button>span>el-dialog>div>
template><script>
export default {data() {return {pCid: [],// 是否可以拖动draggable: false,updateNodes: [],// 最大的层级maxLevel: 0,// 弹框的标题title: "",// 弹框的类型,因为修改和添加复用一个弹框dialogType: "",// 添加的分类对象category: {// 里面的属性都是根据后端数据库中来定义的name: "",parentCid: 0,catLevel: 0,showStatus: 1,sort: 0,catId: null,icon: "",productUnit: "",}, // 表单对象// 是否弹框,默认为falsedialogVisible: false,// 需要展开的idexpandedKey: [],// 分类对象menus: [],defaultProps: {children: "children", // 表示children显示data的children属性label: "name", // label指的是显示数据中的哪个属性这里就显示data中的name属性,这些label、children都可以在官方文档中查看的},};},methods: {// 批量删除batchDelete() {let catIds = [];// 拿到组件menuTreelet checkedNodes = this.$refs.menuTree.getCheckedNodes();console.log("被选中的元素", checkedNodes);for (let i = 0; i < checkedNodes.length; i++) {catIds.push(checkedNodes[i].catId);}this.$confirm(`是否批量删除【${catIds}】菜单?`, "提示", {confirmButtonText: "确定", // 点击确定就调用thencancelButtonText: "取消",type: "warning",}).then(() => {this.$http({url: this.$http.adornUrl("/product/category/delete"),method: "post",data: this.$http.adornData(catIds, false),}).then(({ data }) => {this.$message({type: "success",message: "菜单批量删除成功!",});// 刷新出新的菜单this.getMenus();}).catch(() => {});}).catch(() => {});},// 批量修改batchSave() {this.$http({url: this.$http.adornUrl("/product/category/update/sort"),method: "post",data: this.$http.adornData(this.updateNodes, false),}).then(({ data }) => {this.$message({type: "success",message: "菜单顺序修改成功!",});// 刷新出新的菜单this.getMenus();// 设置需要默认展开的菜单this.expandedKey = this.pCid;this.updateNodes = [];this.maxLevel = 0;// this.pCid = 0;}).catch(() => {});},// 拖拽完成触发方法,draggingNode当前拖拽的节点,拖拽到哪个节点dropNode,dropType类型:前面,后面,里面handleDrop(draggingNode, dropNode, dropType, ev) {console.log("handleDrop: ", draggingNode, dropNode, dropType);//1 当前节点最新的父节点let pCid = 0;let siblings = null;// 不同拖拽,会有不同的父idif (dropType == "before" || dropType == "after") {pCid =dropNode.parent.data.catId == undefined? 0: dropNode.parent.data.catId;siblings = dropNode.parent.childNodes;} else {pCid = dropNode.data.catId;siblings = dropNode.childNodes;}this.pCid.push(pCid);//2 当前拖拽节点的最新顺序for (let i = 0; i < siblings.length; i++) {if (siblings[i].data.catId == draggingNode.data.catId) {// 如果遍历的是当前正在拖拽的节点let catLevel = draggingNode.level;if (siblings[i].level != draggingNode.level) {// 当前节点的层级发生变化catLevel = siblings[i].level;// 修改他子节点的层级this.updateChildNodeLevlel(siblings[i]);}this.updateNodes.push({catId: siblings[i].data.catId,sort: i,parentCid: pCid,catLevel: catLevel,});} else {this.updateNodes.push({ catId: siblings[i].data.catId, sort: i });}}//3 当前拖拽节点的最新层级console.log("updateNodes", this.updateNodes);},updateChildNodeLevlel(node) {if (node.childNodes.length > 0) {for (let i = 0; i < node.childNodes.length; i++) {var cNode = node.childNodes[i].data;this.updateNodes.push({catId: cNode.catId,catLevel: node.childNodes[i].level,});this.updateChildNodeLevlel(node.childNodes[i]);}}},// 判断是否可以拖动allowDrop(draggingNode, dropNode, type) {//1 被拖动的当前节点以及所在的父节点总层数不能大于3//1 被拖动的当前节点总层数// draggingNode当前正在拖动的节点// dropNode 拖到哪个节点,也就是draggingNode和dropNode同级console.log("allowDrop:", draggingNode, dropNode, type);// 返回总层数var level = this.countNodeLevel(draggingNode);// 当前正在拖动的节点+父节点所在的深度不大于3即可// draggingNode.level表示层级一级标题为1,三级标题为3// 当前深度let deep = Math.abs(this.maxLevel - draggingNode.level) + 1;console.log("this.maxLevel", this.maxLevel);console.log("深度:", deep);// this.maxLevel// innner表示拖到里面if (type == "innner") {// console.log(// `this.maxLevel: ${this.maxLevel}; draggingNode.data.catLevel:${draggingNode.data.catLevel};dropNode.level: ${dropNode.level}`// );return deep + dropNode.level <= 3;} else {return deep + dropNode.parent.level <= 3;}},countNodeLevel(node) {// 找到所有子节点,求出最大深度// 如果当前节点不为null,并且有子节点if (node.childNodes != null && node.childNodes.length > 0) {for (let i = 0; i < node.childNodes.length; i++) {// 找到最大的深度if (node.childNodes[i].level > this.maxLevel) {this.maxLevel = node.childNodes[i].level;}this.countNodeLevel(node.childNodes);}}},// 获取所有的菜单getMenus(data) {// 自定义方法this.$http({// htpp请求url: this.$http.adornUrl("/product/category/list/tree"), // 请求后端的接口method: "get",// }).then((data) => { 由于我们的data是一个对象,我们只需要拿到其中的data属性的数据,所以要把data对象解构出来}).then(({ data }) => {// 成功了之后的操作// console.log("请求数据," + data); +号不能显示出数据// console.log("请求数据," , data); ,才可以显示出数据console.log("请求数据,", data.data); // 解构data对象取出data属性this.menus = data.data; // 将后端取出的数据给到组件中的menus属性});},// 添加分类方法addCategory() {console.log("提交的三级分类数据", this.category);// 提交对象到后端,给后端发请求this.$http({url: this.$http.adornUrl("/product/category/save"),method: "post",data: this.$http.adornData(this.category, false),}).then(({ data }) => {// 提交成功this.$message({message: "保存成功",type: "success",});// 保存完后,关闭弹框this.dialogVisible = false;// 刷新出新的菜单this.getMenus();// 展开添加菜单的父菜单this.expandedKey = [this.category.parentCid];});},// 修改分类数据editCategory() {// 只往后端发需要更新的数据,没有发送的数据为null后端则不会更新// 从category取出数据,解构对象!var { catId, name, icon, productUnit } = this.category;this.$http({url: this.$http.adornUrl("/product/category/update"),method: "post",// 这些数据都是对应的后端实体字段data: this.$http.adornData({ catId, name, icon, productUnit }, false),}).then(({ data }) => {this.$message({type: "success",message: "菜单修改成功!",});// 关闭对话框this.dialogVisible = false;// 刷新出新的菜单this.getMenus();// 设置需要默认展开的菜单this.expandedKey = [this.category.parentCid];}).catch(() => {});},submitData() {if (this.dialogType == "add") {this.addCategory();}if (this.dialogType == "edit") {this.editCategory();}},append(data) {console.log("append----", data);// 打开弹框this.dialogVisible = true;this.dialogType = "add"; // 将弹框类型变为添加this.title = "添加分类";// 取出当前节点的一些属性赋给要填加的节点this.category.parentCid = data.catId; // 父idthis.category.catLevel = data.catLevel * 1 + 1; // 层级// 由于我们修改过后的category回显时设置了属性,所以我们添加的时候要清空这些属性,让他等于默认值this.category.catId = null; // 为null,表示后端添加的时候不加上id,自增this.category.name = null;this.category.icon = "";this.category.productUnit = "";this.category.sort = 0;this.category.showStatus = 1;//this.dialogVisible = true;},edit(data) {console.log("要修改的数据", data);// 将弹框类型变为修改this.dialogType = "edit";this.title = "修改分类";// 打开弹框this.dialogVisible = true;// 修改框内显示要修改的namethis.category.name = data.name;// 发送请求获取节点最新的数据,因为有并发this.$http({url: this.$http.adornUrl(`/product/category/info/${data.catId}`),method: "get",}).then(({ data }) => {// 请求成功 这个data是从服务器拿过来的dataconsole.log("要回显得数据", data);// 因为服务器返回的data是数据对象,里面还有一个data才是分类对象,所以data.data.namethis.category.name = data.data.name;this.category.catId = data.data.catId;this.category.icon = data.data.icon;this.category.productUnit = data.data.productUnit;//由于修改完后要展开父菜单,所以把父id也回显this.category.parentCid = data.data.parentCid;//this.dialogVisible = true;});},remove(node, data) {// 定义我们要删除的id数组,data.catId数据的id,也就是数据库中的idvar ids = [data.catId];// 在删除之前显示一个弹框,官网this.$confirm(`此操作将永久删除【${data.name}】文件, 是否继续?`, "提示", {confirmButtonText: "确定",cancelButtonText: "取消",type: "warning",}).then(() => {// 确定删除才会真正请求删除业务// 模仿renrne-vue的Post请求写法即可this.$http({// adornUrl是renrne-vue定义的工具类:renren-fast-vue\src\utils\httpRequest.jsurl: this.$http.adornUrl("/product/category/delete"), // 请求的地址method: "post", // 发送Post请求data: this.$http.adornData(ids, false),}).then((data) => {// 删除成功,发送一个消息提示this.$message({message: "恭喜你,删除成功",type: "success",});// 删除成功后,要刷新页面this.getMenus(); // 重新请求方法,就会获取到最新的menus,然而el-tree又和menus是双向绑定的,所以会实时切换// 刷新后我们想要展开刚才被删除的节点的父结点// node当前节点,中有个parent属性里面放的是父节点的内容,父节点中的data就是父节点的数据对象,然后再拿到父节点的id即可this.expandedKey = [node.parent.data.catId];});}).catch(() => {console.log("取消删除");});console.log(node, data); // 打印当前结点和当前数据},},created() {// 加载组件的时候就调用改方法this.getMenus();},
};
script><style lang="scss" scoped>
style>
- 设置全局代码片段,将我们常用的代码片段提出来
- 文件–首选项–用户片段–即可添加
- 比如下面,下次直接写httpget+回车,就会快捷生成http-get请求里面的片段
"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请求"
}
- 修改的时候只提交我们需要修改的数据

本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
