之前的文章我都是把整篇的代码直接复制到文章中,这样容易抓不住重点,
但是一段代码都贴出来,又显得繁琐,
从这篇开始,我会把重点步骤写出来,代码还是贴完整的
从这篇开始的
mybatis-plus分页插件
的使用开始,就完全不写前端页面了,老师说之前学过的足够可以看懂了
这篇文章的作用主要是一些基本概念和理解表的商品表的关联关系,前端的都是贴过来的,后端都是单表的crud,所以代码实在是不值得写了
SPU
:Standard Product Unit(标准化产品单元) 是商品信息聚合的最小单位,是一组可复用、易检索的标准化信息的集合,该集合描述了一 个产品的特性。
SKU
:Stock Keeping Unit(库存量单位) 即库存进出计量的基本单元,可以是以件,盒,托盘等为单位。SKU 这是对于大型连锁超市 DC(配送中心)物流管理的一个必要的方法。现在已经被引申为产品统一编号的简称,每 种产品均对应有唯一的 SKU 号。
iphoneX 是 SPU、MI 8 是 SPU
,它们聚合了这款产品所有的特性信息,通过spu可以知道这款产品的像素、内存、cpu等等
当我们买这款产品的时候就需要说清楚各种配置,也就是我们买到手的其实是sku
iphoneX 64G 黑曜石 是 SKU MI8 8+64G+黑色 是 SKU
有点像我们java中的类与对象的关系
这些iphone公用的信息就是spu,
256G、深空灰就是一个sku,
一个spu下的所有sku相同的属性叫作基本属性,体现在商品页面中就是商品介绍、规格与包装
一个spu下的所有sku不同的属性叫作销售属性,也就是可以确定库存与价格的,例如颜色、内存
下面的举例都是基于上面第一张图片
每个分类下的商品共享规格参数,与销售属性。只是有些商品不一定要用这个分类下全部的 属性;
基本属性与销售属性都是以三级分类组织起来的,
例如 手机通讯-手机-手机
这个三级分类下的属性字段应该是相似的,例如品牌、产品名称
这些字段。
例如屏幕尺寸、运行内存、机身存储
例如入网型号、品牌、产品名称、上市年份都属于规格与包装的主体
和上面的属性是以三级分类组织起来的
一个意思
由此关联关系我们可以得出该三级分类下有什么属性
# 品牌与分类是多对多的关系
# pms_category_brand_relation品牌分类关联表
# 本身这个表有品牌id和分类id就足够了,但是我们经常在查两表关联关系的时候都需要其name,所以这两个name就成了冗余字段
CREATE TABLE `gulimall_pms`.`Untitled` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`brand_id` bigint(20) NULL DEFAULT NULL COMMENT '品牌id',
`catelog_id` bigint(20) NULL DEFAULT NULL COMMENT '分类id',
`brand_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`catelog_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '品牌分类关联' ROW_FORMAT = Dynamic;
数据
根据以上数据来做数据库的表设计
pms_attr属性表:属性名(例如入网型号)、属性类型、三级分类id、是否可被检索
pms_attr_group属性分组表:组名(例如主体)、三级分类
pms_attr_attrgroup_relation属性分组和属性关联表:属性id、属性分组id
pms_product_attr_value商品属性值表:spuid、属性id、属性值
pms_spu_info spu表:spu基本信息、三级分类
pms_sku_info sku表:sku基本信息、与spu的关系是多对一
pms_sku_images sku图片表
pms_sku_sale_attr_value sku销售属性表:skuid、sku属性和值
商品品牌、属性分组、规格参数(基本属性)、属性分组(基本属性分组)、销售属性,都是基于三级分类
多对多
多对多todo这个不太确定,等确定后补充吧
同一三级分类,且不同基本属性分组不能拥有同样的基本属性
,数据量大
的情况,我们不做连表查询
,例如一张表有100W的数据,另一张表只有1000数据,如果在极端情况下,他们做笛卡尔积,就会有10亿的数据,这是非常可怕的。多对多
两表的同时需要更新他们的中间表
不设置
属性分组的,但他俩公用一张表
<dependency>
<groupId>com.baomidougroupId>
<artifactId>mybatis-plus-boot-starterartifactId>
<version>3.4.0version>
dependency>
package com.atlinxi.gulimall.product.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@EnableTransactionManagement // 开启事务
@MapperScan("com.atlinxi.gulimall.product.dao")
public class MybatisConfig {
// 引入mybatis-plus分页插件
/**
* 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
@Bean
public ConfigurationCustomizer configurationCustomizer() {
return configuration -> configuration.setUseDeprecatedExecutor(false);
}
}
PO(persistant object) 持久对象 PO 就是对应数据库中某个表中的一条记录,多个记录可以用 PO 的集合。 PO 中应该不包 含任何对数据库的操作。 例如entity
DO(Domain Object)领域对象 就是从现实世界中抽象出来的有形或无形的业务实体。
TO(Transfer Object) ,数据传输对象 不同的应用程序之间传输的对象,例如微服务之间传递数据封装的对象
DTO(Data Transfer Object)数据传输对象 这个概念来源于 J2EE 的设计模式,原来的目的是为了 EJB 的分布式应用提供粗粒度的 数据实体,以减少分布式调用的次数,从而提高分布式调用的性能和降低网络负载,但在这 里,泛指用于展示层与服务层之间的数据传输对象。
VO(value object) 值对象 通常用于业务层之间的数据传递,和 PO 一样也是仅仅包含数据而已。但应是抽象出 的业务对象 , 可以和表对应 , 也可以不 , 这根据业务的需要 。用 new 关键字创建,由 GC 回收的。
View object:视图对象; 接受页面传递来的数据,封装对象 将业务处理完成的对象,封装成页面要用的数据
例如我们的校验注解都标注在entity
上,是非常不规范的
BO(business object) 业务对象 从业务模型的角度看 , 见 UML 元件领域模型中的领域对象。封装业务逻辑的 java 对 象 , 通过调用 DAO 方法 , 结合 PO,VO 进行业务操作。business object: 业务对象 主要作 用是把业务逻辑封装为一个对象。这个对象可以包括一个或多个其它的对象。 比如一个简 历,有教育经历、工作经历、社会关系等等。 我们可以把教育经历对应一个 PO ,工作经 历对应一个 PO ,社会关系对应一个 PO 。 建立一个对应简历的 BO 对象处理简历,每 个 BO 包含这些 PO 。 这样处理业务逻辑时,我们就可以针对 BO 去处理。
POJO(plain ordinary java object) 简单无规则 java 对象 传统意义的 java 对象。就是说在一些 Object/Relation Mapping 工具中,能够做到维护 数据库表记录的 persisent object 完全是一个符合 Java Bean 规范的纯 Java 对象,没有增 加别的属性和方法。我的理解就是最基本的 java Bean ,只有属性字段及 setter 和 getter 方法!。POJO 是 DO/DTO/BO/VO 的统称。
DAO(data access object) 数据访问对象 是一个 sun 的一个标准 j2ee 设计模式, 这个模式中有个接口就是 DAO ,它负持久 层的操作。为业务层提供接口。此对象用于访问数据库。通常和 PO 结合使用, DAO 中包 含了各种数据库的操作方法。通过它的方法 , 结合 PO 对数据库进行相关的操作。夹在业 务逻辑与数据库资源中间。配合 VO, 提供数据库的 CRUD 操作.
各业务模块创建vo包,用来封装和前端传递数据的对象
to,微服务之间传递数据封装的对象
,
a服务调用b服务,在数据传输的过程中,a服务的数据以对象的形式传出去,springcloud将对象转换成json,b服务在接收数据的时候又把json转换成对象,所以两个服务都需要使用这个实体类,我们把它定义在common模块的to包中
然而我们在使用逆向工程创建好的代码的时候,保存的通常是实体类的entity
,那么我们调用方使用to
,被调用方使用使用entity
也是可以的,因为它数据转换的最终原理是对象和json的转换,只要双方数据结构及其属性是一致的即可
选中一个三级分类,查询该分类下已经有的分组,
之前我们已经创建过两次分类了,这次就不手动执行了,是一个sys_menus.sql
sql文件,给sys_menu
执行,
todo有个文档技术是swagger,有时间自己能不能把它实现以下,把自己写过的这些接口弄一个文档出来
属性分组、规格参数、销售属性菜单栏都需要三级分类,所以我们可以将三级分类功能抽取出来。
属性分组的菜单url是http://localhost:8001/#/product-attrgroup
,所以我们在prduct目录下创建attrgroup.vue
在src\views\modules
创建common\category.vue,用来抽取三级分类的组件
<template>
<div>
<el-tree
:data="menus"
:props="defaultProps"
node-key="catId"
ref="menuTree"
>
<!-- 每一个分类都会跟一个这个span 也就是 Append Delete -->
<!-- node为当前节点,例如有没有展开,有没有选中之类
data是这个节点真正的数据 -->
<span class="custom-tree-node" slot-scope="{ node, data }">
<span>{{ node.label }}</span>
</span>
</el-tree>
</div>
</template>
<script>
//这里可以导入其他文件(比如:组件,工具 js,第三方插件 js,json 文件,图片文件等等)
//例如:import 《组件名称》 from '《组件路径》';
export default {
//import 引入的组件需要注入到对象中才能使用
components: {},
props: {},
data() {
//这里存放数据
return {
menus: [],
defaultProps: {
// 以哪个字段展开分类
children: "children",
// 哪个字段为分类名称
label: "name",
},
expandedKey: [],
};
},
//计算属性 类似于 data 概念
computed: {},
//监控 data 中的数据变化
watch: {},
//方法集合
methods: {
getMenus() {
this.$http({
url: this.$http.adornUrl("/product/category/list/tree"),
method: "get",
}).then(({ data }) => {
this.menus = data.data;
});
},
},
//生命周期 - 创建完成(可以访问当前 this 实例)
created() {
this.getMenus();
},
//生命周期 - 挂载完成(可以访问 DOM 元素)
mounted() {},
beforeCreate() {}, //生命周期 - 创建之前
beforeMount() {}, //生命周期 - 挂载之前
beforeUpdate() {}, //生命周期 - 更新之前
updated() {}, //生命周期 - 更新之后
beforeDestroy() {}, //生命周期 - 销毁之前
destroyed() {}, //生命周期 - 销毁完成
activated() {}, //如果页面有 keep-alive 缓存功能,这个函数会触发
};
</script>
<style scoped>
</style>
将我们抽取出来的category.vue导入到attrgroup.vue
将逆向生成的attrgroup.vue
粘贴到
,太简单了,下面代码就不体现了
将逆向生成的attrgroup-add-or-update.vue
复制到项目中
attrgroup.vue
<template>
<!-- gutter 列间距 -->
<!-- el-row 代表行 最多可以有24列 -->
<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>
//这里可以导入其他文件(比如:组件,工具 js,第三方插件 js,json 文件,图片文件等等)
//例如:import 《组件名称》 from '《组件路径》';
import Category from "../common/category.vue";
import AddOrUpdate from "./attrgroup-add-or-update";
export default {
//import 引入的组件需要注入到对象中才能使用
components: { Category, AddOrUpdate },
props: {},
data() {
//这里存放数据
return {
dataForm: {
key: "",
},
dataList: [],
pageIndex: 1,
pageSize: 10,
totalPage: 0,
dataListLoading: false,
dataListSelections: [],
addOrUpdateVisible: false,
};
},
//计算属性 类似于 data 概念
computed: {},
//监控 data 中的数据变化
watch: {},
//方法集合
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);
}
});
});
},
},
//生命周期 - 创建完成(可以访问当前 this 实例)
created() {},
//生命周期 - 挂载完成(可以访问 DOM 元素)
mounted() {},
beforeCreate() {}, //生命周期 - 创建之前
beforeMount() {}, //生命周期 - 挂载之前
beforeUpdate() {}, //生命周期 - 更新之前
updated() {}, //生命周期 - 更新之后
beforeDestroy() {}, //生命周期 - 销毁之前
destroyed() {}, //生命周期 - 销毁完成
activated() {
this.getDataList();
}, //如果页面有 keep-alive 缓存功能,这个函数会触发
};
</script>
<style scoped>
</style>
此时我们的需求是attrgroup.vue
的页面是随着category.vue
中的三级分类变化而变化
使用到的vue技术,vue高级功能 父子组件传递数据
父组件感知子组件的树节点被点击
的时候,获取属性列表el-cascader标签
完成级联选择器children
也渲染,在entity的children上加注解实现为空即不响应<template>
<!-- gutter 列间距 -->
<!-- el-row 代表行 最多可以有24列 -->
<el-row :gutter="20">
<el-col :span="6">
<!-- 因为子组件向父组件发送了这个事件,事件是可以向上散发的 -->
<category @tree-node-click="treeNodeClick"></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>
/**
* vue高级功能 父子组件传递数据
*
* 父组件就是本vue,子组件就是我们引入的category.vue
*
* 我们需要category一点,父组件可以感知到
*
*
* 1)子组件给父组件传递数据,事件机制
*
* 子组件给父组件发送一个事件,事件携带数据
*
* 这种操作类似于冒泡,小div点击的同时大div也可以感知到
*
* // 事件名(随便写)、接下来的参数就是携带的数据,可以写无数个
this.$emit('tree-node-click',data,node,component)
*
*
*/
//这里可以导入其他文件(比如:组件,工具 js,第三方插件 js,json 文件,图片文件等等)
//例如:import 《组件名称》 from '《组件路径》';
import Category from "../common/category.vue";
import AddOrUpdate from "./attrgroup-add-or-update";
export default {
//import 引入的组件需要注入到对象中才能使用
components: { Category, AddOrUpdate },
props: {},
data() {
//这里存放数据
return {
dataForm: {
key: "",
},
catId: 0,
dataList: [],
pageIndex: 1,
pageSize: 10,
totalPage: 0,
dataListLoading: false,
dataListSelections: [],
addOrUpdateVisible: false,
};
},
//计算属性 类似于 data 概念
computed: {},
//监控 data 中的数据变化
watch: {},
//方法集合
methods: {
// 感知子组件的树节点被点击
treeNodeClick(data, node, component) {
if (node.level == 3) {
console.log("父组件感知点击", data, data.catId);
this.catId = data.catId;
this.getDataList();
}
},
// 获取数据列表
getDataList() {
this.dataListLoading = true;
this.$http({
url: this.$http.adornUrl(`/product/attrgroup/list/${this.catId}`),
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.$nextTick(() => {
// this.$refs 当前vue下的所有组件
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);
}
});
});
},
},
//生命周期 - 创建完成(可以访问当前 this 实例)
created() {},
//生命周期 - 挂载完成(可以访问 DOM 元素)
mounted() {},
beforeCreate() {}, //生命周期 - 创建之前
beforeMount() {}, //生命周期 - 挂载之前
beforeUpdate() {}, //生命周期 - 更新之前
updated() {}, //生命周期 - 更新之后
beforeDestroy() {}, //生命周期 - 销毁之前
destroyed() {}, //生命周期 - 销毁完成
activated() {
this.getDataList();
}, //如果页面有 keep-alive 缓存功能,这个函数会触发
};
</script>
<style scoped>
</style>
<template>
<el-dialog
:title="!dataForm.attrGroupId ? '新增' : '修改'"
:close-on-click-modal="false"
:visible.sync="visible"
@closed="dialogClose"
>
<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> -->
<!-- @change="handleChange" -->
<!-- -->
<el-cascader
:props="props"
v-model="dataForm.catelogIds"
:options="categorys"
filterable
placeholder="试试搜索:手机"
></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: { label: "name", children: "children", value: "catId" },
categorys: [],
visible: false,
dataForm: {
attrGroupId: 0,
attrGroupName: "",
sort: "",
descript: "",
icon: "",
catelogIds: [],
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" },
],
},
};
},
methods: {
dialogClose(){
this.dataForm.catelogIds = []
console.log()
},
getCategorys() {
this.$http({
url: this.$http.adornUrl("/product/category/list/tree"),
method: "get",
}).then(({ data }) => {
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.catelogIds = 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.catelogIds[this.dataForm.catelogIds.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);
}
});
}
});
},
},
created() {
this.getCategorys();
},
};
</script>
/**
* 根据三级分类查找其路径
* @return
*/
@Override
public Long[] findCatelogPath(Long catelogId) {
List<Long> paths = new ArrayList<>();
List<Long> parentPath = findParentPath(catelogId, paths);
Collections.reverse(parentPath);
return paths.toArray(new Long[parentPath.size()]);
}
private List<Long> findParentPath(Long catelogId,List<Long> paths){
paths.add(catelogId);
// mabatis-plus return this.getBaseMapper().selectById(id);
CategoryEntity byId = this.getById(catelogId);
if (byId.getParentCid()!=0){
findParentPath(byId.getParentCid(),paths);
}
return paths;
}
商品名称、描述、分类、品牌、重量(用来进行邮费的计算)、积分(送金币(抵消商品金额)和成长值(提高用户等级))、商品介绍图、商品图集(所有sku的图片)
前两步录入的实际都是spu
的信息
可以通过组合
销售属性确定一个sku
组合
销售属性确定一个sku,例如 白色+64G+8g
标题、副标题,随着sku的变化而变化
价格、满减信息、折扣信息、默认显示图片
小数和金额以后都用BigDecimal
,运算就不会丢失精度,我们数据库所有字段都是Long
,
common创建to包
这块儿的东西都是后端的,前端都是贴过来的,而且后端都是单表的crud,而且逻辑不复杂,所以实在没什么可记录的
我们商品库存
菜单下的商品不应该是在这个界面直接新增修改的,测试期间
我们暂时保留这两个功能。
我们所有的库存都应该是由采购完成之后自动加上的。
她们同时想道:秋天迟到了,天气还那么热,才吹电风扇,为什么伊纹姐姐要穿高领长袖?
房思琪的初恋乐园
林奕含