猿实战是一个原创系列文章,通过实战的方式,采用前后端分离的技术结合SpringMVC Spring Mybatis,手把手教你撸一个完整的电商系统,跟着教程走下来,变身猿人找到工作不是问题。
前后端框架已经搭建起来了,接下来的很长一段日子里,猿人君就带着大家撸一个电商系统出来玩耍。实战阶段的目的,是为了让你从需求梳理落地到实现有一个完整的认知,熟练掌握撸码的一些套路,让你具备设计和实现一个完整系统的能力。废话不多说,我们今天开始首战——品牌管理的设计和实现。
需求整理
根据之前的猿设计系列文章猿设计2——电商后台全逻辑需求挖掘,品牌数据是需要维护的,根据对设计文档的梳理,我们需要做的功能如下图所示:
品牌管理的功能包括,品牌列表——支持根据品牌名称模糊查询并分页展示,新增品牌、编辑品牌、删除品牌、停用/启用品牌,以及为了方便运营人员批量操作而提供的勾选记录批量停用/启用/删除功能。
数据库设计
由于之前的设计文章中,我们已经提及过品牌实体的一些属性了,而这些属性背后承载的信息,将为我们的电商系统提供数据支撑,毫无疑问,这些数据是需要持久的,为此我们自然需要建立相应的数据表来支持。
前端主要组件
由于我们使用了vue-element-admin.git作为基础的后天管理前端开发框架,前端中使用的主要组件,主要是element-ui,关于组件的具体使用办法,大家可以参考官方网站:
https://element.eleme.cn/#/zh-CN
在品牌管理这一功能中,我们主要使用了el-card、el-input、el-button、el-table(列表)、el-pagination(分页)、el-upload(上传组件),考虑到你可能是第一次编写前端代码,很多东西都还不熟悉,这次就把前端的UI代码送给你了。
新增
查询
重置
全部启用
全部停用
全部删除
{{ scope.row.id }}
{{ scope.row.brandName }}
{{ scope.row.firstChar }}
{{ scope.row.status == 1 ? "启用" : "停用"}}
编辑
停用
启用
删除
后端代码之实体层
前端页面的初步代码有了,我们开始后端数据访问层的设计。根据数据库表结构,我们可以迅速的得到我们所需要的实体MallBrand和QueryMallBrand.为什么是两个实体?因为数据查询和持久是两回事情,在查询实体中,可能为了匹配页面的查询条件而增加一些不需要持久的条件,所以我们需要分开。实体层的代码编写在哪里?自然是我们的pzmall-basic-domain模块了。
/**
* Copyright(c) 2004-2020pangzi
*com.pz.basic.mall.domain.sys.MallCity.java
*/
package com.pz.basic.mall.domain.base;
import org.apache.commons.lang3.builder.ToStringBuilder;
import java.io.Serializable;
/**
*
* @author pangzi
* @date 2020-06-2211:28:19
*
*
*/
public class BaseDO implements Serializable {
private static final longserialVersionUID = 1L;
/**
* 如果字段值为null将不包含在toString中
*/
@Override
public String toString(){
returnToStringBuilder.reflectionToString(this);
}
}
package com.pz.basic.mall.domain.sys;
import com.pz.basic.mall.domain.base.BaseDO;
import java.util.Date;
/**
*
* @author pangzi
* @date 2020-06-2718:09:41
*
*/
public class MallBrand extends BaseDO {
/**主键**/
private Long id;
/**品牌名**/
private String brandName;
/**logo图片地址**/
private String logo;
/**品牌首字母**/
private String firstChar;
/**状态1可用0不可用**/
private Integer status;
/**记录状态1有效0删除**/
private Integer active;
/**创建人**/
private StringcreateUser;
/**修改人**/
private StringmodifyUser;
/**创建时间**/
private Date created;
/**修改时间**/
private Date modified;
//getter setter省略
}
package com.pz.basic.mall.domain.sys.query;
import com.pz.basic.mall.domain.base.PaginateBaseDO;
import java.util.Date;
/**
*
* @author pangzi
* @date 2020-06-2718:09:41
*
*
*/
public class QueryMallBrand extends PaginateBaseDO {
/**主键**/
private Long id;
/**品牌名**/
private String brandName;
/**logo图片地址**/
private String logo;
/**品牌首字母**/
private String firstChar;
/**状态1可用0不可用**/
private Integer status;
/**记录状态1有效0删除**/
private Integer active;
/**创建人**/
private StringcreateUser;
/**修改人**/
private StringmodifyUser;
/**创建时间**/
private Date created;
/**修改时间**/
private Date modified;
/**支持品牌名称模糊查询**/
private String brandNameLike;
}
大家可能注意到了,两个类都是子类,为什么这样设计?猿人君先卖个关子,先给出实现,至于你猜到几分,也可以考验你功力深浅噢。
package com.pz.basic.mall.domain.base;
import java.io.Serializable;
/**
*
* @author pangzi
* @date 2020-06-2211:28:19
*
*
*/
public class PaginateBaseDO implements Serializable {
/**
* 默认每页的记录数量
*/
public static finalint PAGESIZE_DEFAULT = 20;
/**
* 每页大小
*/
private long pageSize;
/**
* 当前页。第一页是1
*/
private long page;
/**
* 总记录数
*/
private long totalItem;
/**
* 总页数
*/
private long totalPage;
/**
* 分页后的记录开始的地方
* 第一条记录是1
*/
private long startRow;
/**
* 分页后的记录结束的地方
*/
private long endRow;
/**排序字段**/
private String orderField;
/**升序 还是 降序,true为升序,false为降序*/
private Boolean isAsc;
/**
* 默认构造方法
*/
public PaginateBaseDO() {
repaginate();
}
/**
* 带当前页和页大小的构造方法
* @param page 当前页
* @param pageSize 页大小
*/
public PaginateBaseDO(long page, long pageSize) {
this.page = page;
this.pageSize =pageSize;
repaginate();
}
public void setStartRow(long startRow) {
this.startRow =startRow;
}
public void setEndRow(long endRow) {
this.endRow =endRow;
}
/**
* 表示是不是第一页
* @return true 是; false 不是
*/
public boolean isFirstPage(){
return page <=1;
}
public boolean isMiddlePage() {
return!(isFirstPage() || isLastPage());
}
public boolean isLastPage() {
return page >=totalPage;
}
public boolean isNextPageAvailable() {
return !isLastPage();
}
public boolean isPreviousPageAvailable() {
return!isFirstPage();
}
/**
* 下一页号
* @return 取得下一页号
*/
public long getNextPage() {
if(isLastPage()) {
returntotalItem;
} else {
return page+1;
}
}
public long getPreviousPage() {
if(isFirstPage()){
return 1;
} else {
return page -1;
}
}
/**
* Method getPageSizereturns the pageSize of this PaginatedArrayList object.
*
* 每页大小
*
* @return thepageSize (type int) of this PaginatedArrayList object.
*/
public long getPageSize() {
return pageSize;
}
/**
* Method setPageSizesets the pageSize of this PaginatedArrayList object.
*
* 每页大小
*
* @param pageSize thepageSize of this PaginatedArrayList object.
*
*/
public void setPageSize(long pageSize) {
this.pageSize =pageSize;
repaginate();
}
/**
* Method getpagereturns the page of this PaginatedArrayList object.
*
* 当前页。第一页是1
*
* @return the page(type int) of this PaginatedArrayList object.
*/
public long getPage(){
return page;
}
/**
* Method setpage setsthe page of this PaginatedArrayList object.
*
* 当前页。第一页是1
*
* @param page thepage of this PaginatedArrayList object.
*
*/
public void setPage(long page) {
this.page = page;
repaginate();
}
/**
* Method getTotalItemreturns the totalItem of this PaginatedArrayList object.
*
* 总记录数
*
* @return thetotalItem (type int) of this PaginatedArrayList object.
*/
public long getTotalItem() {
return totalItem;
}
/**
* Method setTotalItemsets the totalItem of this PaginatedArrayList object.
*
* 总记录数
*
* @param totalItemthe totalItem of this PaginatedArrayList object.
*
*/
public void setTotalItem(long totalItem) {
this.totalItem =totalItem;
if( this.totalItem<= 0){
totalPage = 0;
page = 1;
startRow = 0;
}
repaginate();
}
/**
* Method getTotalPagereturns the totalPage of this PaginatedArrayList object.
*
* 总页数
*
* @return thetotalPage (type int) of this PaginatedArrayList object.
*/
public long getTotalPage() {
return totalPage;
}
/**
* Method getStartRowreturns the startRow of this PaginatedArrayList object.
*
* 分页后的记录开始的地方
*
* @return the startRow(type int) of this PaginatedArrayList object.
*/
public long getStartRow() {
if (startRow >0) {
returnstartRow;
}
if (page <= 0){
page = 1;
}
return (page - 1)* pageSize;
}
/**
* Method getEndRowreturns the endRow of this PaginatedArrayList object.
*
* 分页后的记录结束的地方
*
* @return the endRow(type int) of this PaginatedArrayList object.
*/
public long getEndRow() {
if (endRow > 0){
return endRow;
}
return page *pageSize;
}
public String getOrderField() {
return orderField;
}
public void setOrderField(String orderField) {
this.orderField =orderField;
}
public Boolean getIsAsc() {
return isAsc;
}
public void setIsAsc(Boolean isAsc) {
this.isAsc = isAsc;
}
/**
* Method repaginate...
*/
public void repaginate() {
if (pageSize <1) { //防止程序偷懒,list和分页的混合使用
pageSize =PAGESIZE_DEFAULT;
}
if (page < 1) {
page = 1;//恢复到第一页
}
if (totalItem >0) {
totalPage =totalItem / pageSize + (totalItem % pageSize > 0 ? 1 : 0);
if(page >totalPage) {
page =totalPage; //最大页
}
endRow = page* pageSize;
startRow =(page - 1) * pageSize;
if(endRow>totalItem) {
endRow =totalItem;
}
}
}
}
后端代码之数据持久层
实体层的代码我们已经完成了,接下拉我们自然需要完成数据持久层的代码了。考虑到前端页面的功能,查询/新增/修改/编辑/停用/启用的功能,我们在编写的时候,可以让数据操作更加面向对象一些。我们来看代码:
/**
* Copyright(c) 2004-2020pangzi
*com.pz.basic.mall.dao.sys.MallBrandDao.java
*/
package com.pz.basic.mall.dao.sys;
import com.pz.basic.mall.domain.sys.MallBrand;
import com.pz.basic.mall.domain.sys.query.QueryMallBrand;
import java.util.List;
/**
*
* @author pangzi
* @date 2020-06-2610:56:01
*/
public interface MallBrandDao {
/**
* 根据条件查询总数
* @param query
* @return
*/
long countByQuery(QueryMallBrand query);
/**
* 根据条件删除记录
* @param query
* @return
*/
int deleteMallBrandByQuery(QueryMallBrand query);
/**
* 根据ID删除记录
* @param id
* @return
*/
int deleteMallBrandById(long id);
/**
* 新增记录
* @param record
* @return
*/
long insertMallBrand(MallBrand record);
/**
* 新增记录 注意:有值的记录才新增
* @param record
* @return
*/
long insertMallBrandModified(MallBrand record);
/**
* 根据查询条件返回列表
* @param query
* @return
*/
List selectMallBrandByQuery(QueryMallBrand query);
/**
* 根据查询条件返回列表
* @param query
* @return
*/
List selectMallBrandByPage(QueryMallBrand query);
/**
* 根据ID查询对象
* @param id
* @return
*/
MallBrand selectMallBrandById(long id);
/**
* 根据id修改记录 注意:有值的字段才更新
* @param record
* @return
*/
int updateMallBrandByIdModified(MallBrandrecord);
}
对应的mapper文件MallBrandMapper.xml.
id,
brand_name,
logo,
first_char,
status,
active,
create_user,
modify_user,
created,
modified
1=1
and id = #{id}
and brand_name = #{brandName}
and logo = #{logo}
and first_char = #{firstChar}
and status = #{status}
and active = #{active}
and create_user = #{createUser}
and modify_user = #{modifyUser}
and created = #{created}
and modified = #{modified}
and brand_name likeconcat(#{brandNameLike},'%')
delete from mall_brand
where id = #{id}
delete from mall_brand
INSERT INTO
mall_brand(id,brand_name,logo,first_char,status,active,create_user,modify_user,created,modified)
VALUES(#{id},#{brandName},#{logo},#{firstChar},#{status},#{active},#{createUser},#{modifyUser},#{created},#{modified})
insert into mall_brand
id,
brand_name,
logo,
first_char,
status,
active,
create_user,
modify_user,
created,
modified,
#{id},
#{brandName},
#{logo},
#{firstChar},
#{status},
#{active},
#{createUser},
#{modifyUser},
now(),
now(),
SELECT@@IDENTITY AS ID
update mall_brand
brand_name = #{brandName},
logo= #{logo},
first_char = #{firstChar},
status = #{status},
active = #{active},
create_user = #{createUser},
modify_user = #{modifyUser},
created = #{created},
modified=now(),
where id = #{id}
最后别忘了在mybatis的总控文件SqlMapConfig.xml中增加需要用到的别名和引用。
后端代码之service层
我们之前已经说了,service层是编写业务的核心逻辑,通过调用dao方式完成业务逻辑对应的数据操作。
/**
* Copyright(c) 2004-2020 pangzi
*com.pz.basic.mall.service.sys.MallBrandService.java
*/
package com.pz.basic.mall.service.sys;
import com.pz.basic.mall.domain.base.Result;
import com.pz.basic.mall.domain.sys.MallBrand;
import com.pz.basic.mall.domain.sys.query.QueryMallBrand;
import java.util.List;
/**
* service层,组装外部接口和 本地业务,为本业务 或者其他业务提供服务,统一返回Result
* 通过Result.isSuccess判断调用是否成功
* 此类中新增业务接口设计(接口命令,入参数据,返回值)要能尽量完整的表达业务 含义
* @author pangzi
* @date 2020-06-26 11:20:40
*/
public interface MallBrandService {
/**
* 新增 mallBrand
* 返回result,通过result.isSuccess()判断服务调用是否成功
* 通过result.getModel()得到新增mallBrand
* @param mallBrand
* @return
*/
public Result addMallBrand(MallBrand mallBrand) ;
/**
* 按照主键id更新mallBrand,请重新new MallBrand 的更新对象,设置要更新的字段
* 返回result,通过result.isSuccess()判断更新是否成功
* @param mallBrand
* @return
*/
public Result updateMallBrandById(MallBrandmallBrand);
/**
* 按照主键id 删除 记录
* 返回result,通过result.isSuccess()判断删除是否成功
* @return
*/
public Result deleteMallBrandById(MallBrandmallBrand);
/**
* 查询列表,此接口不包含分页查询
* 返回result,通过result.isSuccess()判断服务调用是否成功
* 通过result.getModel()得到列表信息
* @param queryMallBrand
* @return
*/
public Result> getMallBrandsByQuery(QueryMallBrand queryMallBrand);
/**
* 通过主键id查询MallBrand
* 返回result,通过result.isSuccess()判断服务调用是否成功
* 通过result.getModel()得到查询的单条mallBrand信息
* @param id
* @return
*/
public Result getMallBrandById(long id);
/**
* 查询列表,包含分页查询
* 查询分页信息,请设置
* QueryMallBrand.setIndex(设置当前页数)
*QueryMallBrand.setPageSize(设置当前页面数据行数)
* 返回result,通过result.isSuccess()判断服务调用是否成功
* 通过result.getTotal()返回结果总数
* 通过result.getModel()得到查询的单页列表信息
* @param queryMallBrand
* @return
*/
public Result> getMallBrandsByPage(QueryMallBrand queryMallBrand);
/**
* 查询总数
* @param queryMallBrand
* @return
*/
public Result count(QueryMallBrand queryMallBrand);
/**
* 停用启用品牌列表
* 返回result,通过result.isSuccess()判断服务调用是否成功
* 通过result.getModel()得到列表信息
* @param brandList
* @param disable true 停用 false 启用
* @return
*/
public Result disableEnableMallBrandList(ListbrandList, boolean disable);
/**
* 逻辑删除品牌列表
* 返回result,通过result.isSuccess()判断服务调用是否成功
* 通过result.getModel()得到列表信息
* @param brandList
* @return
*/
public Result deleteMallBrandList(ListbrandList);
}
/**
* Copyright(c) 2004-2020 pangzi
*com.pz.basic.mall.service.sys.impl.MallBrandService.java
*/
package com.pz.basic.mall.service.sys.impl;
import com.pz.basic.mall.dao.sys.MallBrandDao;
import java.util.ArrayList;
import java.util.List;
import com.pz.basic.mall.domain.base.Result;
import com.pz.basic.mall.domain.base.enums.DataActiveStatusEnum;
import com.pz.basic.mall.domain.base.enums.DataStatusEnum;
import com.pz.basic.mall.domain.sys.query.QueryMallBrand;
import com.pz.basic.mall.service.sys.MallBrandService;
import com.pz.basic.mall.domain.sys.MallBrand;
/**
*
* @author pangzi
* @date 2020-06-26 11:25:00
*/
public class MallBrandServiceImpl implements MallBrandService {
private MallBrandDao mallBrandDao;
public void setMallBrandDao(MallBrandDao mallBrandDao) {
this.mallBrandDao =mallBrandDao;
}
public Result addMallBrand(MallBrand mallBrand) {
Result result = new Result();
try {
QueryMallBrand query = new QueryMallBrand();
query.setBrandName(mallBrand.getBrandName());
long count = mallBrandDao.countByQuery(query);
if(count>0){
result.setSuccess(false);
result.setMessage("品牌名已存在");
return result;
}
mallBrand.setStatus(DataStatusEnum.STATUS_ENABLE.getStatusValue());
mallBrand.setActive(DataActiveStatusEnum.STATUS_ACTIVE.getStatusValue());
mallBrandDao.insertMallBrand(mallBrand);
result.addDefaultModel(mallBrand);
} catch(Exception e) {
result.setSuccess(false);
}
return result;
}
public Result updateMallBrandById(MallBrandmallBrand) {
Result result = new Result();
try {
int count=mallBrandDao.updateMallBrandByIdModified(mallBrand);
if(count>0){
result.setSuccess(true);
}
} catch(Exception e) {
result.setSuccess(false);
}
return result;
}
public Result deleteMallBrandById(MallBrandmallBrand) {
Result result = new Result();
try {
int count=0;
MallBrand modifiedMallBrand = new MallBrand();
modifiedMallBrand.setId(mallBrand.getId());
modifiedMallBrand.setActive(DataActiveStatusEnum.STATUS_DELETED.getStatusValue());
count=mallBrandDao.updateMallBrandByIdModified(modifiedMallBrand);
if(count>0){
result.setSuccess(true);
}
} catch(Exception e) {
result.setSuccess(false);
}
return result;
}
public Result> getMallBrandsByQuery(QueryMallBrand queryMallBrand) {
Result> result = newResult>();
try {
queryMallBrand.setActive(DataActiveStatusEnum.STATUS_ACTIVE.getStatusValue());
result.addDefaultModel("MallBrands",mallBrandDao.selectMallBrandByQuery(queryMallBrand));
} catch(Exception e) {
result.setSuccess(false);
}
return result;
}
public Result getMallBrandById(longid) {
Result result = new Result();
try {
result.addDefaultModel("MallBrand",mallBrandDao.selectMallBrandById(id));
} catch(Exception e) {
result.setSuccess(false);
}
return result;
}
public Result> getMallBrandsByPage(QueryMallBrand queryMallBrand) {
Result> result = newResult>();
queryMallBrand.setActive(DataActiveStatusEnum.STATUS_ACTIVE.getStatusValue());
long totalItem =mallBrandDao.countByQuery(queryMallBrand);
queryMallBrand.setTotalItem(totalItem);
queryMallBrand.repaginate();
if (totalItem > 0) {
result.addDefaultModel(mallBrandDao.selectMallBrandByPage(queryMallBrand));
} else {
result.addDefaultModel(new ArrayList());
}
result.setTotalItem(totalItem);
result.setPageSize(queryMallBrand.getPageSize());
result.setPage(queryMallBrand.getPage());
return result;
}
public Result count(QueryMallBrand queryMallBrand) {
Result result = new Result();
queryMallBrand.setActive(DataActiveStatusEnum.STATUS_ACTIVE.getStatusValue());
try {
result.addDefaultModel(mallBrandDao.countByQuery(queryMallBrand));
} catch(Exception e) {
result.setSuccess(false);
}
return result;
}
public Result disableEnableMallBrandList(ListbrandList, boolean disable) {
Result result = new Result();
try {
for (MallBrand brand : brandList) {
if (disable) {
brand.setStatus(DataStatusEnum.STATUS_DISABLE.getStatusValue());
} else {
brand.setStatus(DataStatusEnum.STATUS_ENABLE.getStatusValue());
}
mallBrandDao.updateMallBrandByIdModified(brand);
}
}catch(Exception e){
result.setSuccess(false);
}
return result;
}
public Result deleteMallBrandList(List brandList){
Result result = new Result();
try {
for (MallBrand brand : brandList) {
brand.setActive(DataActiveStatusEnum.STATUS_DELETED.getStatusValue());
mallBrandDao.updateMallBrandByIdModified(brand);
}
}catch(Exception e){
result.setSuccess(false);
}
return result;
}
}
最后,不要忘记了,在spring-service.xml增加service的配置
后端代码之Controller层
我们提供给前端访问的数据接口,是通过Controller暴露出去的,前端通过HttpJSON的方式到后端获取需要的数据。在这一点上,我们使用SpringMVC的RestController能够获得比较好的支持。
/**
* Copyright(c) 2004-2020pangzi
*com.pz.basic.mall.controller.sys.MallBrandController.java
*/
package com.pz.basic.mall.controller.sys;
import com.pz.basic.mall.domain.base.Result;
import com.pz.basic.mall.domain.base.enums.DataStatusEnum;
import com.pz.basic.mall.domain.sys.MallBrand;
import com.pz.basic.mall.domain.sys.query.QueryMallBrand;
import com.pz.basic.mall.service.sys.MallBrandService;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
*
* @author pangzi
* @date 2020-06-2220:47:27
*
*
*/
@RestController
@RequestMapping("/brandManage")
public class MallBrandController {
private MallBrandService mallBrandService;
public void setMallBrandService(MallBrandService mallBrandService) {
this.mallBrandService= mallBrandService;
}
/**
* 新增品牌
* @param mallBrand
* @return
*/
@RequestMapping("/addMallBrand")
public Result addMallBrand(@RequestBody MallBrand mallBrand){
try{
return mallBrandService.addMallBrand(mallBrand);
}catch(Exceptione){
e.printStackTrace();
return newResult(false);
}
}
/**
* 修改品牌
* @param mallBrand
* @return
*/
@RequestMapping("/updateMallBrand")
public Result updateMallBrand(@RequestBody MallBrand mallBrand){
try{
return mallBrandService.updateMallBrandById(mallBrand);
}catch(Exceptione){
e.printStackTrace();
return newResult(false);
}
}
/**
* 启用品牌
* @param mallBrand
* @return
*/
@RequestMapping("/enableMallBrand")
public Result enableMallBrand(@RequestBody MallBrand mallBrand){
try{
MallBrandmodifiedData =new MallBrand ();
modifiedData.setId(mallBrand.getId());
modifiedData.setStatus(DataStatusEnum.STATUS_ENABLE.getStatusValue());
return mallBrandService.updateMallBrandById(modifiedData);
}catch(Exceptione){
e.printStackTrace();
return newResult(false);
}
}
/**
* 停用品牌
* @param mallBrand
* @return
*/
@RequestMapping("/disableMallBrand")
public Result disableMallBrand(@RequestBody MallBrand mallBrand){
try{
MallBrandmodifiedData =new MallBrand ();
modifiedData.setId(mallBrand.getId());
modifiedData.setStatus(DataStatusEnum.STATUS_DISABLE.getStatusValue());
return mallBrandService.updateMallBrandById(modifiedData);
}catch(Exceptione){
e.printStackTrace();
return newResult(false);
}
}
/**
* 删除品牌
* @param mallBrand
* @return
*/
@RequestMapping("/deleteMallBrand")
public Result deleteMallBrand(@RequestBody MallBrand mallBrand){
try{
return mallBrandService.deleteMallBrandById(mallBrand);
}catch(Exceptione){
e.printStackTrace();
return newResult(false);
}
}
/**
* 分页返回品牌列表
* @paramqueryMallBrand
* @return
*/
@RequestMapping("/findByPage")
public Result> findByPage(@RequestBody QueryMallBrandqueryMallBrand){
returnmallBrandService.getMallBrandsByPage(queryMallBrand);
}
/**
* 修改品牌
* @param brandList
* @return
*/
@RequestMapping("/disableMallBrandList")
public Result disableMallBrandList(@RequestBody List brandList){
try{
return mallBrandService.disableEnableMallBrandList(brandList,true);
}catch(Exceptione){
e.printStackTrace();
return newResult(false);
}
}
/**
* 修改品牌
* @param brandList
* @return
*/
@RequestMapping("/enableMallBrandList")
public Result enableMallBrandList(@RequestBody List brandList){
try{
return mallBrandService.disableEnableMallBrandList(brandList,false);
}catch(Exceptione){
e.printStackTrace();
return newResult(false);
}
}
/**
* 修改品牌
* @param brandList
* @return
*/
@RequestMapping("/deleteMallBrandList")
public Result deleteMallBrandList(@RequestBody List brandList){
try{
return mallBrandService.deleteMallBrandList(brandList);
}catch(Exceptione){
e.printStackTrace();
return newResult(false);
}
}
}
到目前为止,后端代码的编写告一段落。
前端代码之API层
后端接口的主要作用是为前端提供数据支撑,前端针对这些后端接口,可以做一些封装,然后交由需要调用的页面使用。为了保证功能的相对独立和后续前端代码的维护,我们需要对品牌管理相关的功能做一些封装——在api目录下建立basedataManage目录,然后建立basedataManage.js文件,之后基础数据相关的api我们都维护在这里。
根据页面后端提供的数据接口,我们需要封装如下封装。
export function fetchBrandList(query) {
return request({
url:'/brandManage/findByPage',
method: 'post',
data: query
})
}
export function createBrand(data) {
return request({
url:'/brandManage/addMallBrand',
method: 'post',
data: data
})
}
export function updateBrand(data) {
return request({
url: '/brandManage/updateMallBrand',
method: 'post',
data: data
})
}
export function disableMallBrandList(data) {
return request({
url:'/brandManage/disableMallBrandList',
method: 'post',
data: data
})
}
export function enableMallBrand(data) {
return request({
url:'/brandManage/enableMallBrand',
method: 'post',
data: data
})
}
export function deleteMallBrand(data) {
return request({
url:'/brandManage/deleteMallBrand',
method: 'post',
data: data
})
}
export function disableMallBrand(data) {
return request({
url:'/brandManage/disableMallBrand',
method: 'post',
data: data
})
}
export function enableMallBrandList(data) {
return request({
url:'/brandManage/enableMallBrandList',
method: 'post',
data: data
})
}
export function deleteMallBrandList(data) {
return request({
url:'/brandManage/deleteMallBrandList',
method: 'post',
data: data
})
}
前端代码之组件引入
我们已经封装好了数据操作相关的API了,那么接下来的事情,自然是引入我们需要的组件了。在brand-management.vue文件中,建立如下标记。
其中import的就是我们封装的API以及Pagination组件了。注意这个结构,components暴露出来的就是组件在页面具体使用的名称。
data()函数的返回值,就是页面中需要的数据。created函数,会在页面创建时执行,如果有一些需要初始化的事情,可以交由它进行处理。
methods中我们可以定义页面所需要的函数。
前端代码之列表数据
我们怎样才能实现品牌列表的功能呢?自然是需要前端页面来调用后端接口来完成了。由于我们还要支持品牌名的模糊查询,所以我们还要为此定义一个数据结构用于存放查询条件。定义一个数组用于存放返回的数据。
// table数据集合
list: null,
listQuery: {
brandName: '',
firsChar: '',
page: 1,
pageSize: 10
}
注意噢,以json的格式放在data()函数中,作为返回值的一部分返回就好。同时在methods区域编写获取列表的函数
// 列表方法查询
getList() {
this.listLoading =true
fetchBrandList(this.listQuery).then(response => {
this.list =response.model
this.total =response.totalItem
// Just to simulatethe time of the request
setTimeout(()=> {
this.listLoading= false
}, 1.5 * 1000)
})
}
为了方便页面初始化的时候有数据展示,我们可以在created函数中调用它。
created() {
// 列表查询
this.getList()
}
至于列表数据的展示,当然是的el-table去展示了。
前端代码之新增/编辑
新增和修改品牌的功能主要是由el-dialog组件来实现的,通过表单,隐藏和展示的方式来完成新增/编辑工作。为此我们同样需要定义数据来处理这些页面逻辑。
temp: {
id: undefined,
// 品牌名称
brandName: '',
// 品牌首字母
firsChar: '',
logo: '',
imageUrl: ''
},
dialogStatus: '',
textMap: {
update: '编辑品牌',
create: '新增品牌'
},
// 弹框是否显示
dialogFormVisible:false,
至于页面数据的前端校验,我们可以使用定义Rule规则的方式完成。
rules: {
brandName: [{required: true, message: '请输入品牌名称',trigger: 'change' }],
firstChar: [{required: true, message: '请输入品牌首字母(英文大写)', trigger: 'change' }],
logo: [{ required:true, message: '请上传品牌logo', trigger:'change' }]
}
注意噢,每一种规则都是一个数组,一个规则是支持同时存在多条的噢。
至于新增/编辑弹出页面动作,则是通过定义函数,通过button的事件触发来完成的。
resetTemp() {
this.temp = {
id: undefined,
// 品牌名称
brandName: '',
// 品牌首字母
firstChar: '',
logo: ''
}
this.dialogVisible =false
if(this.$refs.upload !== null && undefined !== this.$refs.upload) {
this.$refs.upload.clearFiles()
}
console.log(this.$refs.upload)
},
// 新增
addBrand() {
this.resetTemp()
this.dialogStatus ='create'
this.dialogFormVisible = true
this.$nextTick(()=> {
this.$refs['dataForm'].clearValidate()
})
},
至于数据持久,同样也是button绑定函数的方式来完成。
// 更新保存方法
updateData() {
this.$refs['dataForm'].validate((valid)=> {
if (valid) {
const tempData =Object.assign({}, this.temp)
updateBrand(tempData).then(() => {
const index =this.list.findIndex(v => v.id === this.temp.id)
this.list.splice(index, 1, this.temp)
this.dialogFormVisible = false
this.$notify({
title:'Success',
message:'Update Successfully',
type:'success',
duration:2000
})
})
}
})
},
// 创建保存方法
createData() {
this.$refs['dataForm'].validate((valid) => {
if (valid) {
createBrand(this.temp).then((res) => {
this.temp.id =res.model.id
this.list.unshift(this.temp)
this.dialogFormVisible = false
this.$notify({
title:'Success',
message:'Created Successfully',
type:'success',
duration:2000
})
})
}
})
},
前端代码之其它功能
前端的停用/启用/删除功能,大家可以看下,篇幅有限,就不一一例举了——最主要的是你不能懒惰,敲代码的事情,必须自己搞了。给你一些例子,自己去完善!
到此为止,我们项目的开发框架搭建完毕。
如学习过程中遇到疑难杂症,自己实在搞不定,可以在blog中寻找联系方式或通过评论的方式,帮你解决。