【0.三高商城系统的专题专栏都帮你整理好了,请点击这里!】
【1-系统架构演进过程】
【2-微服务系统架构需求】
【3-高性能、高并发、高可用的三高商城系统项目介绍】
【4-Linux云服务器上安装Docker】
【5-Docker安装部署MySQL和Redis服务】
【6-Git安装与配置过程、Gitee码云上创建项目、IDEA关联克隆的项目】
【7-创建商城系统的子模块并将修改后的信息使用Git提交到Gitee上】
【8-数据库表结构的创建&后台管理系统的搭建】
【9-前端项目的搭建部署、Node安装、VSCode安装】
【10-Node的安装以及全局环境变量的相关配置&解决启动报错的问题(1.Error: Cannot find module ‘fs/promises)(2.npm安装node-sass报错)】
【11-导入人人generator项目并自动生成相关的文件&商品子模块的调试&公共模块common子模块的抽离与实现&Lombok插件的安装】
【12-商品子模块整合MyBatisPlus技术&其它模块通过generator的自动生成与补充完善】
【13-项目中微服务组件的学习-SpringCloudAlibaba微服务生态体系的学习&SpringCloudAlibaba的依赖管理&项目中SpringBoot和SpringCloud版本的统一】
【14-微服务的注册中心与配置中心Nacos&Windows操作系统上安装Nacos和Linux操作系统上用Docker中安装Nacos&每个子项目模块使用Nacos进行服务注册与发现】
【15-项目中服务的远程调用之OpenFeign&订单模块与商品模块集成使用OpenFeign的案例】
【16-配置中心之Nacos的基本使用&Nacos服务之命令空间、Nacos服务之配置组、Nacos服务之配置拆分】
【17-微服务网关之Spring Cloud Gateway&Spring Cloud Gateway网关服务搭建】
【18-业务开发-基础业务-商品模块-分类管理-前后端管理系统的启动-为分类管理表增加数据-Json插件的下载-返回具有层级目录、父子关系结构的数据】
【19-业务开发-基础业务-商品模块-分类管理-管理系统新建菜单-后端项目renren注册到Nacos注册中心和配置中心去-项目gateway网关模块的搭建-浏览器的同源策略与解决跨域问题实操案例】
【20-业务开发-基础业务-商品模块-分类管理-前端展示后端具有层级关系的目录数据-商品系统三级分类的逻辑删除前后端代码实现】
【21-业务开发-基础业务-商品模块-分类管理-商品系统三级分类的新增类别前后端代码实现-商品系统三级分类的更新类别前后端代码实现-之前错误的Bug修正】
【22-业务开发-基础业务-商品模块-分类管理-商品系统三级分类拖拽页面的功能-前后端代码的逻辑实现-访问测试-拖拽开关的开启和关系-批量更新拖拽数据-批量删除选定数据】
【23-业务开发-基础业务-品牌管理-品牌管理项目搭建-品牌管理实现的增删改查操作测试-后端数据显示状态使用前端组件开关按钮展示-以及数据处理以及测试】
【24-业务开发-基础业务-品牌管理-图片管理-阿里云OSS服务开通和使用-阿里云OSS服务API使用-SpringCloudAlibaba OSS服务的使用】
【25-业务开发-基础业务-品牌管理-图片管理-图片上传方式的三种实现方式-第三方公共服务模块集成到项目中-服务端生成签名实战】
【26-业务开发-基础业务-品牌管理-图片管理-上传图片功能实现-基于阿里云OSS服务-解决跨域问题-设置跨域规则-修改ACL权限为公共读】
【27-业务开发-基础业务-品牌管理-图片管理-添加修改品牌信息并显示图片-前端数据校验-后端数据JSR303校验实现-统一异常处理-自定义响应编码规则-分组校验-自定义校验注解-项目Bug解决】
【28-业务开发-基础业务-属性管理-SKU和SPU基本概念-SKU和SPU关联关系-属性实体之间的关联关系-批量菜单创建】
根据前端创建菜单的url提示,我们在前端product文件目录下,新建立一个attrgroup.vue
的前端文件
前端项目启动,后端需要用到的四个微服务项目模块启动,访问测试
在整个属性组中我们需要通过分类来管理属性组的信息,这时我们需要将业务布局为两部分,这时我们可以借助ElementUI中的Layout来实现
前端代码的实现
<template>
<el-row :gutter="20">
<el-col :span="8"><div class="grid-content bg-purple">三级分类</div></el-col>
<el-col :span="16"><div class="grid-content bg-purple">表格</div></el-col>
</el-row>
</template>
<script>
export default {
}
</script>
<style>
</style>
三级分类的展示我们后面在多个菜单中都需要使用到。这时我们可以把这个功能抽取出来为一个独立的组件。实现复用。在product目录的同级下创建一个复用的目录common,然后在其中创建category.vue
抽取前端代码实现逻辑
<template>
<div>
<el-tree
:data="data"
:props="defaultProps"
node-key="catId"
>
</el-tree>
</div>
</template>
<script>
/* eslint-disable */
export default {
data() {
return {
data:[],
defaultProps: {
children: "childrens",
label: "name",
},
};
},
methods: {
getCategory() {
this.$http({
url: this.$http.adornUrl("/product/category/listTree"),
method: "get",
}).then(({ data }) => {
this.data = data.data;
});
},
},
created() {
this.getCategory();
},
};
</script>
<style>
</style>
然后在属性组中我们来使用这个组件,引用其他组件的步骤有三个:
3.在页面中使用组件
访问测试看效果
我们需要在右侧展示一个属性组维护的表单,现在我们需要将mall-product代码生成器中生成的属性组的相关的代码拷贝进来即可。
将
<template>
<el-row :gutter="20">
<el-col :span="6">
<category></category>
</el-col>
<el-col :span="18">
<div class="mod-config">
<el-form :inline="true" :model="dataForm" @keyup.enter.native="getDataList()">
<el-form-item>
<el-input v-model="dataForm.key" placeholder="参数名" clearable></el-input>
</el-form-item>
<el-form-item>
<el-button @click="getDataList()">查询</el-button>
<el-button v-if="isAuth('product:attrgroup:save')" type="primary" @click="addOrUpdateHandle()">新增</el-button>
<el-button v-if="isAuth('product:attrgroup:delete')" type="danger" @click="deleteHandle()" :disabled="dataListSelections.length <= 0">批量删除</el-button>
</el-form-item>
</el-form>
<el-table
:data="dataList"
border
v-loading="dataListLoading"
@selection-change="selectionChangeHandle"
style="width: 100%;">
<el-table-column
type="selection"
header-align="center"
align="center"
width="50">
</el-table-column>
<el-table-column
prop="attrGroupId"
header-align="center"
align="center"
label="分组id">
</el-table-column>
<el-table-column
prop="attrGroupName"
header-align="center"
align="center"
label="组名">
</el-table-column>
<el-table-column
prop="sort"
header-align="center"
align="center"
label="排序">
</el-table-column>
<el-table-column
prop="descript"
header-align="center"
align="center"
label="描述">
</el-table-column>
<el-table-column
prop="icon"
header-align="center"
align="center"
label="组图标">
</el-table-column>
<el-table-column
prop="catelogId"
header-align="center"
align="center"
label="所属分类id">
</el-table-column>
<el-table-column
fixed="right"
header-align="center"
align="center"
width="150"
label="操作">
<template slot-scope="scope">
<el-button type="text" size="small" @click="addOrUpdateHandle(scope.row.attrGroupId)">修改</el-button>
<el-button type="text" size="small" @click="deleteHandle(scope.row.attrGroupId)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
@size-change="sizeChangeHandle"
@current-change="currentChangeHandle"
:current-page="pageIndex"
:page-sizes="[10, 20, 50, 100]"
:page-size="pageSize"
:total="totalPage"
layout="total, sizes, prev, pager, next, jumper">
</el-pagination>
<!-- 弹窗, 新增 / 修改 -->
<add-or-update v-if="addOrUpdateVisible" ref="addOrUpdate" @refreshDataList="getDataList"></add-or-update>
</div>
</el-col>
</el-row>
</template>
<script>
import Category from "../common/category.vue"
export default {
data(){
return {
dataForm: {
key: ''
},
dataList: [],
pageIndex: 1,
pageSize: 10,
totalPage: 0,
dataListLoading: false,
dataListSelections: [],
addOrUpdateVisible: false
}
},components:{Category}
,methods: {
// 获取数据列表
getDataList () {
this.dataListLoading = true
this.$http({
url: this.$http.adornUrl('/product/attrgroup/list'),
method: 'get',
params: this.$http.adornParams({
'page': this.pageIndex,
'limit': this.pageSize,
'key': this.dataForm.key
})
}).then(({data}) => {
if (data && data.code === 0) {
this.dataList = data.page.list
this.totalPage = data.page.totalCount
} else {
this.dataList = []
this.totalPage = 0
}
this.dataListLoading = false
})
},
// 每页数
sizeChangeHandle (val) {
this.pageSize = val
this.pageIndex = 1
this.getDataList()
},
// 当前页
currentChangeHandle (val) {
this.pageIndex = val
this.getDataList()
},
// 多选
selectionChangeHandle (val) {
this.dataListSelections = val
},
// 新增 / 修改
addOrUpdateHandle (id) {
this.addOrUpdateVisible = true
this.$nextTick(() => {
this.$refs.addOrUpdate.init(id)
})
},
// 删除
deleteHandle (id) {
var ids = id ? [id] : this.dataListSelections.map(item => {
return item.attrGroupId
})
this.$confirm(`确定对[id=${ids.join(',')}]进行[${id ? '删除' : '批量删除'}]操作?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$http({
url: this.$http.adornUrl('/product/attrgroup/delete'),
method: 'post',
data: this.$http.adornData(ids, false)
}).then(({data}) => {
if (data && data.code === 0) {
this.$message({
message: '操作成功',
type: 'success',
duration: 1500,
onClose: () => {
this.getDataList()
}
})
} else {
this.$message.error(data.msg)
}
})
})
}
}
}
</script>
<style>
</style>
访问测试
特别说明:函数事件和绑定的函数方法
<category @show="treenodeclick"></category>
`特别说明:`
@show:是声明一个show事件
treenodeclick:是与这个show事件绑定的函数方法
当我们点击分类菜单的节点的时候,需要把我们的节点Id传递给我们右侧父节点的table展示出来
1.我们需要触发Tree节点的点击事件
2.我们需要在父组件中定义的一个事件
4.触发事件会回调对应的方法
5.测试访问, 看结果。
前后端进行数据交互封装的数据
{
page:1, // 当前页
limit:10, // 每页显示的条数
sidx:"id", // 排序的字段
order:"asc/desc", // 排序的方式
key:"检索条件" // 查询的关键字
}
AttrGroupController控制器中的处理
/**
* catelogId商品分类Id
* 根据catelogId进行查找数据
* 如果是0 查找所有的分类信息
* 如果非0 查找对应的catelogId下的分类信息
*/
@RequestMapping("/list/{catelogId}")
//@RequiresPermissions("product:attrgroup:list")
public R list(@RequestParam Map<String, Object> params,@PathVariable("catelogId") Long catelogId){
PageUtils page = attrGroupService.queryPage(params,catelogId);
return R.ok().put("page", page);
}
service接口定义的接口方法
PageUtils queryPage(Map<String, Object> params, Long catelogId);
serviceImpl中具体方法处理逻辑
/**
* 查询列表数据
* 根据列表编号来查询
* @param params
* @param catelogId 如何catelogId为0 就不根据catelogId来查询
* @return
*/
@Override
public PageUtils queryPage(Map<String, Object> params, Long catelogId) {
// 获取检索的关键字
String key = (String) params.get("key");
QueryWrapper<AttrGroupEntity> wrapper = new QueryWrapper<>();
if(!StringUtils.isEmpty(key)){
// 拼接查询的条件
wrapper.and((obj)->{
obj.eq("attr_group_id",key).or().like("attr_group_name",key);
});
}
if(catelogId == 0){
// 不根据catelogId来查询
IPage<AttrGroupEntity> page = this.page(
new Query<AttrGroupEntity>().getPage(params),wrapper
);
return new PageUtils(page);
}
// 根据类别编号来查询属性信息
wrapper.eq("catelog_id",catelogId);
IPage<AttrGroupEntity> page = this.page(
new Query<AttrGroupEntity>().getPage(params),wrapper
);
return new PageUtils(page);
}
先将common/category.vue组件中的传递所有的data给父组件
定义全局id、三级分类实现逻辑操作、定义前端与后端进行接口交互的方法
注意:下面图片中有一个小问题,就是发送请求的后面有少了一个/,加上即可
url: this.$http.adornUrl('/product/attrgroup/list/'+this.catId)
重启项目,测试效果:
点击添加按钮弹出对话框,我们需要对属性组的类别做级联选择操作。然后维护类别的级联查找
还有就是在级联中的childrens为空的情况,在后端通过@JsonInclude注解来解决
启动项目,访问前端的页面效果,搞定!
当我们提交表单数据后出现了如下的异常,id是一个数组,但是我们只需要获取最后一个分类的id就可以,所以我们需要做如下的调整。
然后在提交数据的位置获取最小的类别编号提交就可以了
在更新属性组的信息的时候,因为三级分类的信息展示需要为[第一级分类id,第二级分类id,第三级分类id]这种方式,而后台只是提供了第三级分类id,那么为了能够更加清楚的展示相关的信息,我们需要自己再来实现相关的逻辑代码
AttrGroupEntity实体类中添加catelogPath long数组类型的属性
AttrGroupController中注入CategoryService注解
在CategoryServiceImpl中实现具体的逻辑方法
好了,关于【28-业务开发-基础业务-属性管理-属性组业务逻辑开发-页面布局-三级分类组件功能-属性组表单-父子组件传值-属性组数据展示-属性组数据添加-属性组数据修改-前后端项目整合交互】就先学习到这里,后续的内容持续创作中。