第3章 商品模板与分类管理
学习目标
使用代码生成器-完成基础代码生成
完成规格参数模板管理
完成商品分类管理
完成图片库管理
项目序列-3:https://github.com/Jonekaka/javaweb-qingcheng-3-78
1. 代码生成器
对于一个数据库的操作无非是增删改查,甚至列表在web中如何表现也大同小异,
那么能否给出一个数据库,根据ssm等框架自动生成基础的操作代码和web页面呢?
1.1 介绍
代码生成器
git项目已添加
只需要提供数据库和表,选择内置的spring架构模板,可自动建立maven工程,
自动创建增删改查方法,接口,以及数据对象
自动生成html页面
自动生成数据库表结构的介绍文档
模板有SSM+dubbo 、springBoot+springCloud+springData 、前后端分离的vue+elementUI 模板、wagger
API模板、数据库文档模板等。 也可以自定义模板
https://gitee.com/chuanzhiliubei/codeutil
1.2 代码生成
通过代码生成器完成参数模板,商品分类,图片库管理
1.2.1 安装
- 安装jdk1.8 并配置环境变量
- 双击codeutil-2.5.jar 或start.bat运行程序(注意不要放在有中文和空格的目录运行)
1.2.2 使用步骤
- 建立数据库表,并设置字段的中文备注(中文备注用于作为生成表格的标题)
- 双击codeutil-2.5.jar 或start.bat运行程序,输入用户名和密码,点击测试连接,选择数据库表后点击“下一步”。
- 选择模板,输入基本信息,注意:包名一定要3级,例如 com.qingcheng.goods 前两级为模块名称,第三级为模块名称。
- 点击生成后,用IDE 打开代码后即可运行。
- 选择数据表
生成工程,选择模板db所在位置,选择模板,选择生成路径,设置包信息
对于数据库,选择goods,order,config,system,user,都集中到一个项目中
如图代码已经自动生成
数据库增删改查全部可用,且页面也自动生成
因为后续的代码为自动生成,多次创建的工程建立关联需要重写父类pom.xml
<modules>
<module>qingcheng_common</module>
<module>qingcheng_pojo</module>
<module>qingcheng_service_user</module>
<module>qingcheng_service_goods</module>
<module>qingcheng_service_business</module>
<module>qingcheng_service_config</module>
<module>qingcheng_service_order</module>
<module>qingcheng_service_system</module>
<module>qingcheng_interface</module>
<module>qingcheng_web_manager</module>
<module>qingcheng_common_service</module>
<module>qingcheng_common_web</module>
</modules>
安装parent,开启service与web-manger的tomcat,即可运行
此时对于数据库的增删改查皆可以有自动生成的web页面操作
1.2.3 分库分表工程生成
青橙项目采用分库分表设计,有多个数据库
可以分别连接每一个数据库生成代码,生成代码到同一位置,这样公共模块不变。
最后修改pom.xml的模块列表,将所有生成的模块都加入即可。
代码已经生成,接下来就需要将公共模块安装一下
直接安装parent也行
打开页面即可看到效果,以第二章开发为例
可以轻易的看到之前开发的功能自动实现了,而且还有自动的页面生成
如这个页面也是自动生成的
当然,对于生成的页面需要微调,比如某些功能未实现或者一些冗余的功能
模板对于上传功能也考虑到了,有些注释是未开放的功能,放开注释即可
注意native.do是本地上传,oss需要改为云服务上传,修改上传的路径
这样看来似乎某些页面与功能也需要修改,比较麻烦,然而,对于巨量数据表可以快速实现
如果对开发项目熟悉,可以快速完成基本功能,只需微调即可
2. 规格参数模板管理
2.1 概念与需求
2.1.1 概念解析
了解需求前我们先弄懂三个概念
如何去描述一个商品,让看到的人看到参数以后立刻对其有一个清楚的认识
规格: 规格是用于区分同一商品的属性。 例如手机的网络规格,是一个大的概念
规格选项:规格选项是规格的具体值。比如网络制式是规格,它选项有移动4G、联通4G等。
参数: 参数是用于描述商品的属性。同一规格的手机,参数未必相同,例如手机的核数
参数选项:参数选项是参数的可选值。比如核数,包括参数选项有2核 、4核、8核
模板:类似于商品类型。汇总规格和参数的。需要在输入前定义出来,而不是每次录入时重复录入
例如手机包括网络制式、屏幕尺寸等规格,还包括核数、前置摄像头像素、后置摄像头
像素等。
模板
规格
参数
2.1.2 需求分析
(1)根据已经生成的代码,实现商品类别模板的增删改查,对某类商品的规格与参数进行管理
(2)需要几个表才能操作商品的规格,参数,模板?
2.2 表结构分析
tb_template (模板表)
字段名称 | 字段含义 | 字段类型 | 字段长度 | 备注 |
---|---|---|---|---|
id | ID | INT | ||
name | 模板名称 | VARCHAR | ||
spec_num | 规格数量 | INT | ||
para_num | 参数数量 | INT |
tb_spec (规格表)
字段名称 | 字段含义 | 字段类型 | 字段长度 | 备注 |
---|---|---|---|---|
id | ID | INT | ||
name | 名称 | VARCHAR | ||
options | 规格选项 | VARCHAR | ||
seq | 排序 | INT | ||
template_id | 模板ID | INT |
tb_para (参数表)
字段名称 | 字段含义 | 字段类型 | 字段长度 | 备注 |
---|---|---|---|---|
id | id | INT | ||
name | 名称 | VARCHAR | ||
options | 选项 | VARCHAR | ||
seq | 排序 | INT | ||
template_id | 模板ID | INT |
2.3 代码实现
2.3.1 规格参数模板列表查询
查看自动生成的模板界面
修改将其与规格和参数的关联
实现模板对规格和参数的管理
最终效果如图
(1)修改template.html页面,添加方法
传递参数
goSpec(id){ //规格管理
location.href="spec.html?templateId="+id;
},
goPara(id){ //参数管理
location.href="para.html?templateId="+id;
}
在表格的模板列中添加两个按钮
<el‐button @click="goSpec(scope.row.id)" type="text" size="small">规格管
理</el‐button>
<el‐button @click="goPara(scope.row.id)" type="text" size="small">参数管
理</el‐button>
适当调整列宽度
(2)修改spec.html ,引入util.js,规格
其提供了一个方法,从地址栏中得到参数id
用来修改同一id下的规格
查看自动生成的界面
<script src="/js/util.js"></script>
util.js中提供了可以获取地址栏参数的方法getQueryString
修改created钩子函数,添加代码
得到id后就可以查询参数
解释参数
this.searchMap={templateId: getQueryString("templateId")};
当进入局部之后,可以返回到主界面
添加返回按钮
<el‐button type="primary" onclick="location.href='template.html'">返回模
板</el‐button>
效果如图
进入之后即可返回模板列表
(3)修改parameter.html,步骤同spec.html
查看自动生成的界面
2.3.2 添加规格
规格页面中,看自动生成的界面是否有瑕疵
1.点击新增时,出现了模板id,事实上修改的是该模板下的规格,模板id应该默认,不应该作为可选项出现
2.规格选项应该不止一个,应该作为文本域出现,而非行列
(1)修改spec.html页面,data添加属性templateId
templateId: 0
(2)created()钩子函数添加代码
this.templateId=getQueryString("templateId");
(3)修改新增按钮
自带模板id
第一个templateId是实体类的属性,第二个是刚才定义的0默认属性,当新增的时候就把当前模板id覆盖0,0-42.。。
<el‐button type="primary" @click="formVisible=true;pojo={templateId:
templateId}">新增</el‐button>
(4)删除表单中模板id
(5)将表单中规格选项文本框改为文本域
从eui6查看文本域控件
textarea文本域
autosize="{ minRows: 4, maxRows: 15}"设置文本域可以自动调节大小
<el‐form‐item label="规格选项">
<el‐input v‐model="pojo.options" type="textarea" :autosize="{ minRows:
4, maxRows: 15}"> </el‐input>
</el‐form‐item>
但是存在问题,多个属性多个换行影响展示,需要逗号分割,但逗号影响属性编辑?
只需要一个转换即可
当保存的时候回车变成逗号存储到数据库
当查看的时候逗号变成回车显示
(6)修改save方法,在开始处添加代码 ,将回车符\n转换为逗号","
this.pojo.options= this.pojo.options.replace(/\n/g,","); //回车替换为逗号
(7)修改edit方法,在回调处添加代码,将逗号“,”替换为回车符\n
this.pojo.options= this.pojo.options.replace(/,/g,"\n"); //逗号替换为回车符
保存后,用逗号代替多行显示
修改时显示换行
2.3.3 添加参数
思路同"添加规格",代码略
2.3.4 规格与参数数量统计
进入模板列后可以看到规格和参数数量
需求:在每次添加和删除规格参数时,对规格和参数数量进行更新
实现思路:在每次添加规格和参数时,修改模板表对应的字段值,让其加1,在删除规格和参数时,修改模板表对应的字段值,让其减1
代码实现:
(1)修改TemplateServiceImpl的add方法
新增一个模板,两个参数必定是0
/**
* 新增
* @param template
*/
public void add(Template template) {
template.setSpecNum(0);
template.setParaNum(0);
templateMapper.insert(template);
}
(2)修改SpecServiceImpl的add方法
规格模板
插入模板,
根据主键获得模板规格数+1,更新
注意事务注解
/**
* 新增
* @param spec
*/
@Transactional
public void add(Spec spec) {
specMapper.insert(spec);
//将模板中的规格数量+1
Template template =
templateMapper.selectByPrimaryKey(spec.getTemplateId());
template.setSpecNum( template.getSpecNum()+1 );
templateMapper.updateByPrimaryKey(template);
}
(3)修改SpecServiceImpl的delete方法
根据id查询对象,执行反操作
/**
* 删除
* @param id
*/
@Transactional
public void delete(Integer id) {
//将模板中的规格数量减一
Spec spec = specMapper.selectByPrimaryKey(id);
Template template =
templateMapper.selectByPrimaryKey(spec.getTemplateId());
template.setSpecNum( template.getSpecNum()‐1 );
templateMapper.updateByPrimaryKey(template);
specMapper.deleteByPrimaryKey(id);
}
(4)修改SpecServiceImpl类的注解@Service
使用dubbo的service需要解决的问题,
分布式产生的问题,后面再解释
@Service(interfaceClass = SpecService.class)
此时就可以对模板内部的规格与参数进行增删改查了。且模板的数量统计自动更新
3. 商品分类
商品分类的目的在于快速找到商品
一般划分为三级
3.1 需求分析
那么后台是如何展示的?
同样有三级商品分类
3.2 表结构分析
分类是逐级下派的
利用上级id查询
指定的分类关联到模板
tb_category (商品分类表)
字段名称 | 字段含义 | 字段类型 | 字段长度 | 备注 |
---|---|---|---|---|
id | 分类ID | INT | ||
name | 分类名称 | VARCHAR | ||
goods_num | 商品数量 | INT | ||
is_show | 是否显示 | CHAR | ||
is_menu | 是否导航 | CHAR | ||
seq | 排序 | INT | ||
parent_id | 上级ID | INT | ||
template_id | 模板ID | INT |
3.3 代码实现
3.3.1 三级分类列表展示
查看软件自动生成器生成的页面
进行修改
最终效果如图
可返回上级,查看下级
修改html
(1)查询一级分类列表。修改searchMap的属性 ,删除查询表单
默认进入一级分类
searchMap: {parentId:0}
这样打开category.html就看到一级分类的列表了
(2)查询下级列表:新增方法
事实上仍然是同一个页面,只是加载了不同的数据
queryByParentId(parentId){
this.searchMap.parentId=parentId;
this.fetchData();//加载数据
},
表格的模板列新增"查询下级"按钮
<el‐button @click="queryByParentId(scope.row.id)" type="text"
size="small">查询下级</el‐button>
(3)返回上级列表
新增属性 ,用于记录点击的上级ID
假如你迷路了,避免迷路的最好方法是在走过的路留下印记,
因此,将搜索过的id记录保存到列表,回到上个id的页面就可以
注意的是既然回去了,那么使用过的id就要删除,只保留前一个,
每次只获得最后一个就可以保证每次得到的都是最合适的id
parentIds:[]
修改方法queryByParentId,添加代码
this.parentIds.push(this.searchMap.parentId)
新增方法
获得合适的id
注意id路径长度,如果为0就没有返回的必要
returnQuery(){ //上级查询
//获取上级ID
if(this.parentIds.length>0){
this.searchMap.parentId= this.parentIds[this.parentIds.length‐1]
this.parentIds.splice( this.parentIds.length‐1,1 );//截掉最后一个
this.fetchData();//加载数据
}
}
新增按钮
<el‐button type="primary" @click="returnQuery()">返回上级</el‐button>
(4)三级列表不显示“查询下级”链接,在查询下级按钮上添加条件
因为只有三级列表,第三级的查看下级没有意义,做个判断
如果痕迹id列表长度大于2就没有显示的需要了
小于2则显示
<el‐button v‐if="parentIds.length<2"
@click="queryByParentId(scope.row.id)" type="text" size="small">查询下级
</el‐button>
(5)修改fetchData方法
因此三级分类以后数据量巨减,没有必要10列一页,全部展示即可
axios.post(`/category/findList.do`,this.searchMap).then(response => {
this.tableData = response.data
});
3.3.2 新增分类
应该新增当前分类,比如二级,三级
查看代码生成器自动生成的新增模板
id默认即可,不需要自己填
是否显示和导航应该使用按钮控件
最终效果
(1)添加新增按钮
确保当前新增的id分类为当前分类parentId: searchMap.parentId
<el‐button type="primary" @click="formVisible=true;pojo={parentId:
searchMap.parentId}">新增</el‐button>
(2)删除表单中的上级ID列
(3)使用开关控件控制是否显示和是否导航
后台也就是1 0,
引用控件,开启为绿色,不开启为红色
<el‐form‐item label="是否显示">
<el‐switch
v‐model="pojo.isShow"
active‐color="#13ce66"
inactive‐color="#ff4949"
active‐value="1"
inactive‐value="0">
</el‐switch>
</el‐form‐item>
<el‐form‐item label="是否导航">
<el‐switch
v‐model="pojo.isMenu"
active‐color="#13ce66"
inactive‐color="#ff4949"
active‐value="1"
inactive‐value="0">
</el‐switch>
</el‐form‐item>
(4)下拉列表显示
对于模板id是不可能输入id的,应该选择模板,应该是下拉列表
如果选项比较多,选择会很慢,因此应该选择有可搜索的
在html返回数据中假如templateList
<el‐form‐item label="模板ID">
<el‐select v‐model="pojo.templateId" filterable placeholder="请选择">
<el‐option
v‐for="item in templateList"
:key="item.id"
:label="item.name"
:value="item.id">
</el‐option>
</el‐select>
</el‐form‐item>
创建加载方法
created(){
this.fetchData();
//加载模板列表
axios.get(`/template/findAll.do`).then( response=>{
this.templateList=response.data
})
},
3.3.3 完善列表显示
(1)显示级别,添加模板列
至于级别的关系数据库中并没有存储,是依赖id自己判断的,
因此查看id走过的痕迹长度判断级别+1
<el‐table‐column label="级别" width="80">
<template slot‐scope="scope">
{{parentIds.length+1}}
</template>
</el‐table‐column>
(2)显示是否显示和是否菜单
scope.row.isShow开关控件显示
<el‐table‐column label="是否显示" width="80">
<template slot‐scope="scope">
<el‐switch
v‐model="scope.row.isShow"
active‐color="#13ce66"
inactive‐color="#ff4949"
active‐value="1"
inactive‐value="0">
</el‐switch>
</template>
</el‐table‐column>
<el‐table‐column label="是否导航" width="80">
<template slot‐scope="scope">
<el‐switch
v‐model="scope.row.isMenu"
active‐color="#13ce66"
inactive‐color="#ff4949"
active‐value="1"
inactive‐value="0">
</el‐switch>
</template>
</el‐table‐column>
(3)模板下拉列表
对于物品右侧应该显示其属于的模板名称
<el‐table‐column label="模板" width="300">
<template slot‐scope="scope">
<el‐select v‐model="scope.row.templateId" disabled>
<el‐option
v‐for="item in templateList"
:key="item.id"
:label="item.name"
:value="item.id">
</el‐option>
</el‐select>
</template>
</el‐table‐column>
3.3.4 分类删除
如果分类下还有其他分类商品,则容易误删
需求:在删除某个分类前要判断这个分类下是否存在下级分类,如果有下级分类则不能
删除。
修改CategoryServiceImpl的delete方法
删除模板,根据id,统计id下分类个数
/**
* 删除
* @param id
*/
public void delete(Integer id) {
//判断是否存在下级分类
Example example=new Example(Category.class);
Example.Criteria criteria = example.createCriteria();
criteria.andEqualTo("parentId",id);
int count = categoryMapper.selectCountByExample(example);
if(count>0){
throw new RuntimeException("存在下级分类不能删除");
}
categoryMapper.deleteByPrimaryKey(id);
}
html提示
dele (id){
this.$confirm('确定要删除此记录吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then( () => {
axios.get(`/category/delete.do?id=${id}`).then(response => {
if(response.data.code==0){
this.fetchData (); //刷新列表
}else{
this.$alert(response.data.message)
}
})
})
},
已经具备效果
4. 图片库管理
4.1 需求分析
图片库是做什么的?图片库也可以称为相册,是用于存储商品图片的空间,一个图片库
(相册)下有多张图片。我们通常是将每一个商品建立一个图片库。
这样当需要某件图片,可以快速从图片库中提取
商品库中还有图片,可以快速管理
4.2 表结构分析
tb_album (相册表)
字段名称 | 字段含义 | 字段类型 | 字段长度 | 备注 |
---|---|---|---|---|
id | 编号 | BIGINT | ||
title | 相册名称 | VARCHAR | ||
image | 相册封面 | VARCHAR | ||
image_items | 图片列表 | TEXT |
相册下有很多图片,那么图片存在哪里?image_items,地址utl用逗号分开
4.3 代码实现
4.3.1 相册名称列表
实现步骤:
(1)修改album.html ,精简查询表单,只保留相册名称查询
(2)删除图片列表列,需要显示的不是图片的urL而是图片
(3)修改封面为模板显示,封面图片当然是图片而不是地址
(4)添加设置列,列中有“图片列表‘按钮,点击图片按钮执行list方法,传递id,增删改查
(5)添加list方法 ,实现页面跳转 ,跳转到album_list.html并传递id
(6)取消注释的图片上传相关的代码,实现封面的上传
4.3.2 图片列表管理
图片列表管理在相册分类之下
实现步骤:
(1)新增album_list.html页面,接收参数id,网格查看图片
(2)根据id查询相册的图片列表,字符串转换为数组
(3)遍历图片列表
(4)实现图片的上传功能,上传后追加到图片列表,并保存到相册。
(5)实现删除相册图片的功能。
实现类似商品分类,后续再说