基于Vue的ElementUI级联显示以及数据回显
前言在我们做项目时,可能会遇到许多树形展示的数据,在编辑时要求选择当前数据所在的上级,这时就要用到级联,如图:如何实现这种功能?在这里分三部份来说明,数据及实体类,前端展示,后台数据查询。一、创建相关实体类及数据表1、创建商品分类表:CREATE TABLE `mall_pms`.`pms_category`(`cat_id` bigint(20) NOT NULL AUTO_INCREMENT
前言
在我们做项目时,可能会遇到许多树形展示的数据,在编辑时要求选择当前数据所在的上级,这时就要用到级联,如图:
如何实现这种功能?在这里分三部份来说明,数据及实体类,前端展示,后台数据查询。
一、创建相关实体类及数据表
1、创建商品分类表:
CREATE TABLE `mall_pms`.`pms_category` (
`cat_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '分类id',
`name` char(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '分类名称',
`parent_cid` bigint(20) NULL DEFAULT NULL COMMENT '父分类id',
`cat_level` int(11) NULL DEFAULT NULL COMMENT '层级',
`show_status` tinyint(4) NULL DEFAULT NULL COMMENT '是否显示[0-不显示,1显示]',
`sort` int(11) NULL DEFAULT NULL COMMENT '排序',
`icon` char(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '图标地址',
`product_unit` char(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '计量单位',
`product_count` int(11) NULL DEFAULT NULL COMMENT '商品数量',
PRIMARY KEY (`cat_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1405048572491190275 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '商品三级分类' ROW_FORMAT = Dynamic;
2、创建属性分组表
CREATE TABLE `mall_pms`.`pms_attr_group` (
`attr_group_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '分组id',
`attr_group_name` char(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '组名',
`sort` int(11) NULL DEFAULT NULL COMMENT '排序',
`descript` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '描述',
`icon` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '组图标',
`catelog_id` bigint(20) NULL DEFAULT NULL COMMENT '所属分类id',
PRIMARY KEY (`attr_group_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '属性分组' ROW_FORMAT = Dynamic;
3、二者关系
一个分类对应多个属性分组,其中pms_attr_group表中的catelog_id对应pms_category表中的cat_id
4、对应实体类
CategoryEntity 中的children用来加载子分类,这空或null时不显示
@Data
@TableName("pms_category")
public class CategoryEntity implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 分类id
*/
@TableId
private Long catId;
/**
* 分类名称
*/
private String name;
/**
* 父分类id
*/
private Long parentCid;
/**
* 层级
*/
private Integer catLevel;
/**
* 是否显示[0-不显示,1显示]
* 逻辑删除
*/
@TableLogic(value = "1", delval = "0")
private Integer showStatus;
/**
* 排序
*/
private Integer sort;
/**
* 图标地址
*/
private String icon;
/**
* 计量单位
*/
private String productUnit;
/**
* 商品数量
*/
private Integer productCount;
/**
* 子分类
*/
@JsonInclude(JsonInclude.Include.NON_EMPTY) //为空或null时不出现此字段
@TableField(exist = false)
private List<CategoryEntity> children;
}
AttrGroupEntity类中的catelogPath用来加载当前caetlog_id的所在所有父类的cat_id,主要用于数据的回显
@Data
@TableName("pms_attr_group")
public class AttrGroupEntity implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 分组id
*/
@TableId
private Long attrGroupId;
/**
* 组名
*/
private String attrGroupName;
/**
* 排序
*/
private Integer sort;
/**
* 描述
*/
private String descript;
/**
* 组图标
*/
private String icon;
/**
* 所属分类id
*/
private Long catelogId;
/**
* 当前catelogId所在的完整路径集合
*/
@TableField(exist = false)
private Long[] catelogPath;
}
二、前端使用基于Vue的ElementUI实现
1、前端页面结构及效果图
attrgroup.vue页面
新增页面
2、在attrgroup.vue引入组件attrgroup-add-or-update.vue
引入
import AddOrUpdate from './attrgroup-add-or-update';
定义组件
export default {
//import引入的组件需要注入到对象中才能使用
components: {
AddOrUpdate
},
在template中使用组件,其中:
- addOrUpdateVisible控制组件是否显示
- addOrUpdate组件名称
- @refreshDataList="getDataList"组件保存后用来重新加载刷新表格
<add-or-update v-if="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="getDataList"></add-or-update>
3、新增与修改实现
当点新增时,弹出如上图的对话框
新增与修改代码:
<el-button type="primary" @click="addOrUpdateHandle()">新增</el-button>
//修改时要传入scope.row.attrGroupId
<el-button type="text" size="small" @click="addOrUpdateHandle(scope.row.attrGroupId)">修改</el-button>
// 新增 / 修改
addOrUpdateHandle(id) {
this.addOrUpdateVisible = true;
this.$nextTick(() => {
this.$refs.addOrUpdate.init(id);
});
},
4、attrgroup-add-or-update.vue实现
引入级联组件:
<el-cascader v-model="dataForm.catelogPath" :options="categorys" :props="props"></el-cascader>
props表示组件加载映射哪些数据
props: {
value: 'catId',
label: 'name',
children: 'children'
},
v-model=“dataForm.catelogPath” 用于双向绑定级联数据,catelogPath存放回显的数据
:options="categorys"动态绑定全部数据
完整代码
<template>
<el-dialog :title="!dataForm.attrGroupId ? '新增' : '修改'" :close-on-click-modal="false" :visible.sync="visible">
<el-form :model="dataForm" :rules="dataRule" ref="dataForm" @keyup.enter.native="dataFormSubmit()" label-width="80px">
<el-form-item label="组名" prop="attrGroupName"><el-input v-model="dataForm.attrGroupName" placeholder="组名"></el-input></el-form-item>
<el-form-item label="排序" prop="sort"><el-input v-model="dataForm.sort" placeholder="排序"></el-input></el-form-item>
<el-form-item label="描述" prop="descript"><el-input v-model="dataForm.descript" placeholder="描述"></el-input></el-form-item>
<el-form-item label="组图标" prop="icon"><el-input v-model="dataForm.icon" placeholder="组图标"></el-input></el-form-item>
<el-form-item label="所属分类id" prop="catelogId">
<!-- <el-input v-model="dataForm.catelogId" placeholder="所属分类id"></el-input> -->
<el-cascader v-model="dataForm.catelogPath" :options="categorys" :props="props"></el-cascader>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="visible = false">取消</el-button>
<el-button type="primary" @click="dataFormSubmit()">确定</el-button>
</span>
</el-dialog>
</template>
<script>
export default {
data() {
return {
props: {
value: 'catId',
label: 'name',
children: 'children'
},
categorys: [],
visible: false,
dataForm: {
attrGroupId: 0,
attrGroupName: '',
sort: '',
descript: '',
icon: '',
catelogPath: [],
catelogId: 0
},
dataRule: {
attrGroupName: [{ required: true, message: '组名不能为空', trigger: 'blur' }],
sort: [{ required: true, message: '排序不能为空', trigger: 'blur' }],
descript: [{ required: true, message: '描述不能为空', trigger: 'blur' }],
icon: [{ required: true, message: '组图标不能为空', trigger: 'blur' }],
catelogId: [{ required: true, message: '所属分类id不能为空', trigger: 'blur' }]
}
};
},
created() {
this.getCategorys();
},
methods: {
getCategorys() {
this.$http({
url: this.$http.adornUrl('/product/category/list/tree'),
method: 'get'
}).then(({ data }) => {
if (data && data.code === 0) {
this.categorys = data.data;
}
});
},
init(id) {
this.dataForm.attrGroupId = id || 0;
this.visible = true;
this.$nextTick(() => {
this.$refs['dataForm'].resetFields();
if (this.dataForm.attrGroupId) {
this.$http({
url: this.$http.adornUrl(`/product/attrgroup/info/${this.dataForm.attrGroupId}`),
method: 'get',
params: this.$http.adornParams()
}).then(({ data }) => {
if (data && data.code === 0) {
this.dataForm.attrGroupName = data.attrGroup.attrGroupName;
this.dataForm.sort = data.attrGroup.sort;
this.dataForm.descript = data.attrGroup.descript;
this.dataForm.icon = data.attrGroup.icon;
this.dataForm.catelogId = data.attrGroup.catelogId;
//当前catelogId所在的路径集合
this.dataForm.catelogPath = data.attrGroup.catelogPath;
}
});
}
});
},
// 表单提交
dataFormSubmit() {
this.$refs['dataForm'].validate(valid => {
if (valid) {
this.$http({
url: this.$http.adornUrl(`/product/attrgroup/${!this.dataForm.attrGroupId ? 'save' : 'update'}`),
method: 'post',
data: this.$http.adornData({
attrGroupId: this.dataForm.attrGroupId || undefined,
attrGroupName: this.dataForm.attrGroupName,
sort: this.dataForm.sort,
descript: this.dataForm.descript,
icon: this.dataForm.icon,
//绑定最后一个
catelogId: this.dataForm.catelogPath[this.dataForm.catelogPath.length - 1]
})
}).then(({ data }) => {
if (data && data.code === 0) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
this.visible = false;
this.$emit('refreshDataList');
}
});
} else {
this.$message.error(data.msg);
}
});
}
});
}
}
};
</script>
<style>
</style>
三、后台使用SpringBoot与MyBatisPlus实现
1、加载树形数据
控制层
@RequestMapping("/list/tree")
//@RequiresPermissions("member:pmscategory:list")
public R listWithTree(){
List<CategoryEntity> entities = categoryService.listWithTree();
return R.ok().put("data", entities);
}
实现层
- 首先查询出所有的数据
- 组装,找到一级菜单,然后给一级菜单的子菜单采用递归方法添加数据
- 最后以list方式返回
/**
* 㞡现树形结构
* @return
*/
@Override
public List<CategoryEntity> listWithTree() {
//1.查询所有分类
List<CategoryEntity> entities = baseMapper.selectList(null);
//2.组装
//2.1) 所到所有的一级类别 ,parentId = 0
List<CategoryEntity> levelOneMenus = entities.stream()
.filter(item -> item.getParentCid() == 0)
.map(item -> {
item.setChildren(this.getChildrens(item, entities));
return item;
})
.sorted((item1, item2) -> {
return (item1.getSort() == null ? 0 : item1.getSort()) - (item2.getSort() == null ? 0 : item2.getSort());
})
.collect(Collectors.toList());
return levelOneMenus;
}
/**
* 递归查找菜单所有的子菜单
* @param root
* @param all
* @return
*/
private List<CategoryEntity> getChildrens(CategoryEntity root, List<CategoryEntity> all){
List<CategoryEntity> treeMenus = all.stream()
//如果菜单中的父菜单Id == 当前菜单的id,则说明是子菜单
.filter(item -> Objects.equals(item.getParentCid(), root.getCatId()))
.map(item -> {
//递归添加子菜单
List<CategoryEntity> childrens = getChildrens(item, all);
item.setChildren(childrens);
return item;
})
//排序
.sorted((item1, item2) -> {
return (item1.getSort() == null ? 0 : item1.getSort()) - (item2.getSort() == null ? 0 : item2.getSort());
})
.collect(Collectors.toList());
return treeMenus;
}
2、编辑回显级联数据
控制层
@RequestMapping("/info/{attrGroupId}")
//@RequiresPermissions("product:attrgroup:info")
public R info(@PathVariable("attrGroupId") Long attrGroupId){
AttrGroupEntity attrGroup = attrGroupService.getById(attrGroupId);
//查出categoryId 所在路径
if(attrGroup != null && attrGroup.getCatelogId() != null){
Long[] categoryPath = categoryService.findCategoryPath(attrGroup.getCatelogId());
attrGroup.setCatelogPath(categoryPath);
}
return R.ok().put("attrGroup", attrGroup);
}
实现层,采用递归方法查找当前分类的所有上级分类Id,拼装到数组中
@Override
public Long[] findCategoryPath(Long catelogId) {
List<Long> paths = new ArrayList<>();
List<Long> parentPaths = this.findParentPath(catelogId, paths);
Collections.reverse(parentPaths);
return parentPaths.toArray(new Long[parentPaths.size()]);
}
private List<Long> findParentPath(Long catelogId, List<Long> paths){
paths.add(catelogId);
CategoryEntity entity = this.getById(catelogId);
if(entity.getParentCid() != null && entity.getParentCid() != 0){
this.findParentPath(entity.getParentCid(), paths);
}
return paths;
}
更多推荐
所有评论(0)