猿实战是一个原创系列文章,通过实战的方式,采用前后端分离的技术结合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代码送给你了。
<template>
<divid="brandManagementDiv">
<el-cardclass="filter-container" shadow="never">
<div>
<el-formref="listQuery" :model="listQuery" :inline="true">
<el-form-itemlabel="品牌名称:"prop="brandName">
<el-inputv-model="listQuery.brandName" placeholder="请输入品牌名称" clearable />
</el-form-item>
<el-form-item>
<el-buttontype="primary" icon="el-icon-edit"@click="addBrand()">新增</el-button>
<el-buttontype="primary" icon="el-icon-search"@click="fetchData()">查询</el-button>
<el-buttonicon="el-icon-s-tools" @click="resetForm('listQuery')">重置</el-button>
<el-buttontype="primary" @click="enbleDataList()">全部启用</el-button>
<el-buttontype="primary" @click="disableDataList()">全部停用</el-button>
<el-buttontype="danger" @click="deleteDataList()">全部删除</el-button>
</el-form-item>
</el-form>
</div>
</el-card>
<divstyle="height:20px;" />
<divclass="table-container">
<el-table
ref="multipleTable"
v-loading="listLoading"
:data="list"
style="width:100%"
border
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" min-width="10%"/>
<el-table-column label="编号" min-width="10%">
<templateslot-scope="scope">{
{ scope.row.id }}</template>
</el-table-column>
<el-table-column label="品牌名称">
<templateslot-scope="scope">{
{ scope.row.brandName }}</template>
</el-table-column>
<el-table-column label="品牌首字母" min-width="10%">
<templateslot-scope="scope">{
{ scope.row.firstChar }}</template>
</el-table-column>
<el-table-columnlabel="品牌logo"align="center">
<templateslot-scope="scope"><img style="width: 200px; height:200px" :src="scope.row.logo"alt=""></template>
</el-table-column>
<el-table-column label="状态" min-width="10%">
<templateslot-scope="scope">{
{ scope.row.status == 1 ? "启用" : "停用"}}</template>
</el-table-column>
<el-table-column label="操作" width="220">
<templateslot-scope="scope">
<el-button
type="primary"
size="mini"
@click="handleUpdate(scope.row)"
>编辑
</el-button>
<el-button
v-if="scope.row.status==1"
type="primary"
size="mini"
@click="handleDisable(scope.$index,scope.row)"
>停用
</el-button>
<el-button
v-if="scope.row.status==0"
type="primary"
size="mini"
@click="handleEnable(scope.$index, scope.row)"
>启用
</el-button>
<el-button
size="mini"
type="danger"
@click="handleDelete(scope.$index, scope.row)"
>删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<paginationv-show="total>0" :total="total":page.sync="listQuery.page":limit.sync="listQuery.pageSize" @pagination="getList"/>
<!-- 新增/编辑弹框 -->
<el-dialog:title="textMap[dialogStatus]":visible.sync="dialogFormVisible">
<el-formref="dataForm" :rules="rules" :model="temp"label-position="right" label-width="100px"style="width: 500px; margin-left:50px;">
<el-form-itemlabel="品牌名称:"prop="brandName">
<el-inputv-model="temp.brandName" />
</el-form-item>
<el-form-itemlabel="品牌首字母:"prop="firstChar">
<el-inputv-model="temp.firstChar" maxlength="1"oninput="value=value.replace(/[^A-Z]/g,'');" alt="英文大写" />
</el-form-item>
<el-form-itemv-show="dialogVisible" label="品牌logo:" prop="logo"style="margin-top:15px;">
<imgstyle="width: 200px; height: 200px" :src="temp.logo"alt="">
</el-form-item>
<el-form-item>
<el-upload
ref="upload"
:file-list="imgList"
action="http://127.0.0.1:9201//upload/uploadFile?moudle=brand"
list-type="picture-card"
:on-preview="handlePictureCardPreview"
:on-success="handleSuccess"
:limit="1"
accept="image/jpeg,image/gif,image/png,image/bmp"
>
<iclass="el-icon-plus" />
</el-upload>
</el-form-item>
</el-form>
<divslot="footer">
<el-button@click="dialogFormVisible = false">
取消
</el-button>
<el-buttontype="primary"@click="dialogStatus==='create'?createData():updateData()">
确定
</el-button>
</div>
</el-dialog>
</div>
</template>
<style scoped>
#brandManagementDiv /deep/ .el-form-item--mini.el-form-item__label {
width: 100px !important;
}
</style>
后端代码之实体层
前端页面的初步代码有了,我们开始后端数据访问层的设计。根据数据库表结构,我们可以迅速的得到我们所需要的实体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.do