猿实战03——猿首战之手把手教你撸品牌

时间:2022-07-23
本文章向大家介绍猿实战03——猿首战之手把手教你撸品牌,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

猿实战是一个原创系列文章,通过实战的方式,采用前后端分离的技术结合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.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<MallBrand> selectMallBrandByQuery(QueryMallBrand query);

  /**
  * 根据查询条件返回列表
  * @param  query
  * @return
  */
     List<MallBrand> selectMallBrandByPage(QueryMallBrand query);

     /**
   * 根据ID查询对象
   * @param id
   * @return
   */
    MallBrand selectMallBrandById(long id);


    /**
   * 根据id修改记录 注意:有值的字段才更新
   * @param record
   * @return
   */
    int updateMallBrandByIdModified(MallBrandrecord);
}

对应的mapper文件MallBrandMapper.xml.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >

<mapper namespace="com.pz.basic.mall.dao.sys.MallBrandDao">

  <resultMap id="ResultMap" type="MallBrand">
          <id property="id" column="id"/>
                   <id property="brandName" column="brand_name"/>
                   <id property="logo" column="logo"/>
                   <id property="firstChar" column="first_char"/>
                   <id property="status" column="status"/>
                   <id property="active" column="active"/>
                   <id property="createUser" column="create_user"/>
                   <id property="modifyUser" column="modify_user"/>
                   <id property="created" column="created"/>
                   <id property="modified" column="modified"/>
                                      </resultMap>

  <sql id="ALL_TABLE_COLOUM">
                   id,
                     brand_name,
                     logo,
                     first_char,
                     status,
                     active,
                     create_user,
                     modify_user,
                     created,
                     modified
          </sql>

  <sql id="Query_Where_Clause" >
    <where >
          1=1

                               <if test="id != null and id != ''">
                  and     id =  #{id}
               </if>
                               <if test="brandName != null and brandName != ''">
                  and      brand_name =  #{brandName}
               </if>
                               <if test="logo != null and logo != ''">
                  and      logo = #{logo}
               </if>
                               <if test="firstChar != null and firstChar != ''">
                  and      first_char =  #{firstChar}
               </if>
                                <if test="status!= null and status != ''">
                  and      status = #{status}
               </if>
                               <if test="active != null and active != ''">
                  and      active = #{active}
               </if>
                               <if test="createUser != null and createUser != ''">
                  and      create_user =  #{createUser}
               </if>
                               <if test="modifyUser != null and modifyUser != ''">
                  and      modify_user =  #{modifyUser}
               </if>
                               <if test="created != null and created != ''">
                  and      created = #{created}
               </if>
                                <if test="modified !=null and modified != ''">
                  and      modified =  #{modified}
               </if>

                    <if test="brandNameLike != null and brandNameLike != ''">
                        and      brand_name likeconcat(#{brandNameLike},'%')
                    </if>

    </where>
  </sql>


  <select id="selectMallBrandByQuery" resultMap="ResultMap"parameterType="QueryMallBrand" >
    select
    <include refid="ALL_TABLE_COLOUM" />
    from mall_brand
    <if test="page!= null" >
      <include refid="Query_Where_Clause" />
    </if>

  </select>


    <select id="selectMallBrandByPage" resultMap="ResultMap"parameterType="QueryMallBrand" >
        select
        <include refid="ALL_TABLE_COLOUM" />
        from mall_brand
        <iftest="page != null" >
            <includerefid="Query_Where_Clause" />
        </if>

        LIMIT#{startRow},#{pageSize}
    </select>


  <select id="selectMallBrandById" resultMap="ResultMap"parameterType="java.lang.Long" >
    select
    <include refid="ALL_TABLE_COLOUM" />
    from mall_brand
    where id = #{id}
  </select>
  <delete id="deleteMallBrandById" parameterType="java.lang.Integer">
    delete from mall_brand
    where id = #{id}
  </delete>


  <delete id="deleteMallBrandByQuery" parameterType= "QueryMallBrand">
    delete from mall_brand
    <if test="page!= null" >
      <include refid="Query_Where_Clause" />
    </if>
  </delete>


  <insert id="insertMallBrand" parameterType="MallBrand" >
    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})
      <select Key resultType="long" keyProperty="id">
          SELECT@@IDENTITY AS ID
      </selectKey>
  </insert>


  <insert id="insertMallBrandModified" parameterType="MallBrand" >
    insert into mall_brand
    <trimprefix="(" suffix=")" suffixOverrides="," >


             <if test="id != null" >
                 id,
             </if>


             <if test="brandName != null" >
                brand_name,
             </if>


             <iftest="logo != null" >
                 logo,
             </if>


             <if test="firstChar != null" >
                first_char,
             </if>


             <if test="status != null" >
                 status,
             </if>


             <if test="active != null" >
                 active,
             </if>


             <if test="createUser != null" >
                create_user,
             </if>


             <if test="modifyUser != null" >
                modify_user,
             </if>


             <if test="created != null" >
                 created,
             </if>


             <if test="modified != null" >
                 modified,
             </if>

       </trim>

    <trim prefix="values (" suffix=")" suffixOverrides=",">
                                 <if test="id !=null" >
                   #{id},
               </if>

                                <iftest="brandName != null" >
                  #{brandName},
               </if>

                                <if test="logo != null">
                  #{logo},
               </if>

                                <iftest="firstChar != null" >
                  #{firstChar},
               </if>

                                <iftest="status != null" >
                  #{status},
               </if>

                                <iftest="active != null" >
                  #{active},
               </if>

                                <iftest="createUser != null" >
                  #{createUser},
               </if>

                                <iftest="modifyUser != null" >
                  #{modifyUser},
               </if>

                                <if test="created!= null" >
                   now(),
               </if>

                                <iftest="modified != null" >
                   now(),
               </if>

          </trim>

      <selectKey resultType="long" keyProperty="id">
          SELECT@@IDENTITY AS ID
      </selectKey>
  </insert>


  <select id="countByQuery" parameterType="QueryMallBrand"  resultType="java.lang.Long" >
    select count(*) frommall_brand
    <if test="page!= null" >
      <include refid="Query_Where_Clause"/>
    </if>
  </select>



  <update id="updateMallBrandByIdModified" parameterType="MallBrand">
    update mall_brand
    <set >

                                                          <iftest="brandName != null" >
                     brand_name =  #{brandName},
                </if>

                                                <iftest="logo != null" >
                     logo=  #{logo},
                </if>

                                                <iftest="firstChar != null" >
                    first_char =  #{firstChar},
                </if>

                                                <iftest="status != null" >
                    status =  #{status},
                </if>

                                                <iftest="active != null" >
                    active =  #{active},
                </if>

                                                <iftest="createUser != null" >
                    create_user =  #{createUser},
                </if>

                                                <iftest="modifyUser != null" >
                    modify_user =  #{modifyUser},
                 </if>

                                                <iftest="created != null" >
                    created =  #{created},
                </if>

                                                <iftest="modified != null" >
                    modified=now(),
                </if>


    </set>
    where id = #{id}
  </update>


</mapper>

最后别忘了在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<MallBrand> 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<List<MallBrand>> getMallBrandsByQuery(QueryMallBrand queryMallBrand);

    /**
     * 通过主键id查询MallBrand
    * 返回result,通过result.isSuccess()判断服务调用是否成功
    * 通过result.getModel()得到查询的单条mallBrand信息
     * @param id
     * @return
     */
    public Result<MallBrand> getMallBrandById(long id);

    /**
     * 查询列表,包含分页查询
    * 查询分页信息,请设置
    * QueryMallBrand.setIndex(设置当前页数)
    *QueryMallBrand.setPageSize(设置当前页面数据行数)
    * 返回result,通过result.isSuccess()判断服务调用是否成功
    * 通过result.getTotal()返回结果总数
    * 通过result.getModel()得到查询的单页列表信息
     * @param queryMallBrand
     * @return
     */
    public Result<List<MallBrand>> getMallBrandsByPage(QueryMallBrand queryMallBrand);

    /**
     * 查询总数
     * @param queryMallBrand
     * @return
     */
    public Result<Long> count(QueryMallBrand queryMallBrand);

   /**
    * 停用启用品牌列表
    * 返回result,通过result.isSuccess()判断服务调用是否成功
    * 通过result.getModel()得到列表信息
    * @param brandList
    * @param disable true 停用 false 启用
    * @return
    */
   public Result disableEnableMallBrandList(List<MallBrand>brandList, boolean disable);

   /**
    * 逻辑删除品牌列表
    * 返回result,通过result.isSuccess()判断服务调用是否成功
    * 通过result.getModel()得到列表信息
    * @param brandList
    * @return
    */
   public Result deleteMallBrandList(List<MallBrand>brandList);

}

/**
 * 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<MallBrand> addMallBrand(MallBrand mallBrand) {
      Result<MallBrand> result = new Result<MallBrand>();
      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<List<MallBrand>> getMallBrandsByQuery(QueryMallBrand queryMallBrand) {
      Result<List<MallBrand>> result = newResult<List<MallBrand>>();
      try {
      queryMallBrand.setActive(DataActiveStatusEnum.STATUS_ACTIVE.getStatusValue());
        result.addDefaultModel("MallBrands",mallBrandDao.selectMallBrandByQuery(queryMallBrand));
      } catch(Exception e) {
        result.setSuccess(false);
      }
      return result;  
    }

    public Result<MallBrand> getMallBrandById(longid) {
      Result<MallBrand> result = new Result<MallBrand>();
      try {  
        result.addDefaultModel("MallBrand",mallBrandDao.selectMallBrandById(id));
      } catch(Exception e) {
        result.setSuccess(false);
      }
      return result;  
    }


   public Result<List<MallBrand>> getMallBrandsByPage(QueryMallBrand queryMallBrand) {

      Result<List<MallBrand>> result = newResult<List<MallBrand>>();
   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<MallBrand>());
      }
      result.setTotalItem(totalItem);
      result.setPageSize(queryMallBrand.getPageSize());
      result.setPage(queryMallBrand.getPage());

      return result;
    }

    public Result<Long> count(QueryMallBrand queryMallBrand) {
      Result<Long> result = new Result<Long>();
   queryMallBrand.setActive(DataActiveStatusEnum.STATUS_ACTIVE.getStatusValue());
      try {
         result.addDefaultModel(mallBrandDao.countByQuery(queryMallBrand));
      } catch(Exception e) {
        result.setSuccess(false);
      }
      return result;  
    }


   public Result disableEnableMallBrandList(List<MallBrand>brandList, 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<MallBrand> 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的配置

<bean id="mallBrandService"class="com.pz.basic.mall.service.sys.impl.MallBrandServiceImpl"/>

后端代码之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<MallBrand> 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<List<MallBrand>> findByPage(@RequestBody  QueryMallBrandqueryMallBrand){
        returnmallBrandService.getMallBrandsByPage(queryMallBrand);
    }


    /**
     * 修改品牌
     * @param brandList
     * @return
     */
   @RequestMapping("/disableMallBrandList")
    public Result disableMallBrandList(@RequestBody List<MallBrand> brandList){
        try{
            return  mallBrandService.disableEnableMallBrandList(brandList,true);
        }catch(Exceptione){
           e.printStackTrace();
            return newResult(false);
        }
    }

    /**
     * 修改品牌
     * @param brandList
     * @return
     */
    @RequestMapping("/enableMallBrandList")
    public Result enableMallBrandList(@RequestBody List<MallBrand> brandList){
        try{
            return mallBrandService.disableEnableMallBrandList(brandList,false);
        }catch(Exceptione){
           e.printStackTrace();
            return newResult(false);
        }
    }

    /**
     * 修改品牌
     * @param brandList
     * @return
     */
   @RequestMapping("/deleteMallBrandList")
    public Result deleteMallBrandList(@RequestBody List<MallBrand> 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文件中,建立如下标记。

<script>
import { fetchBrandList, createBrand, updateBrand,disableMallBrand, enableMallBrand, deleteMallBrand, disableMallBrandList,enableMallBrandList, deleteMallBrandList } from'@/api/basedataManage/basedataManage'
import Pagination from '@/components/Pagination' // secondarypackage based on el-pagination
export default {
  components: { Pagination},
  data() {
    return {

    }
  },
  created() {

  },
  methods: {
  }
}
</script>

其中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
            })
          })
        }
      })
    },

前端代码之其它功能

前端的停用/启用/删除功能,大家可以看下,篇幅有限,就不一一例举了——最主要的是你不能懒惰,敲代码的事情,必须自己搞了。给你一些例子,自己去完善!

到此为止,我们项目的开发框架搭建完毕。