实战:微信小程序电商项目

介绍

学习视频来源

SpringBoot微信小程序电商实战项目课程 Vue3.2 Element Plus后台管理 ( 火爆连载更新中… )_哔哩哔哩_bilibili

技术栈介绍

  • 本套课程采用主流技术栈实现,Mysql数据库,SpringBoot2+Mybatis Plus后端,微信小程序原生实现,Vue3.2+Element Plus实现后台管理。基于JWT技术实现前后端分离。

  • 微信小程序端涵盖了axios异步请求,Promise应用,swiper组件,自定义组件,应用了微信小程序提供的登录,支付,地址管理,包括下拉滑动分页,less应用,以及结合SpringBoot后端实现了企业级微信小程序支付功能,为了方便教学,采用ngrok实现映射本机IP,开发环境演示真实支付功能。

  • 电商的后台管理采用了主流的Vue3.2+Element Plus组件 实现,涵盖了axios+Promise工具类封装,自定义icon,vuex应用,router路由,路由守卫,使用了大量的Element Plus组件,如表格,分页,图片上传,下拉框,二级联动,Form表单,rules验证框架,dialog以及第三方vue-quil富文本组件等。

一:基础工作

1. 创建微信小程序项目

准备工作

3_下载微信开发者工具以及安装_哔哩哔哩_bilibili 前几P会介绍微信开发者的注册以及开发工具的下载与使用

创建

  • 创建时选择不使用云服务,选择javaScript模板

创建后

搭建空项目
  • 删除app.wxss中的内容(里面是一些样式)
  • 删除pages下的logs日志文件夹(app.json中要选出他的一条相关内容)
  • 删除utils工具包文件夹(暂时不用)
  • 删除index(首页)文件夹下的index.wxml,index.wxss,index.js中内容

index.json

{
  "usingComponents": {},
   //正常json中不能注释,这里目的是为了说明
   //程序主名字
  "navigationBarTitleText": "商城首页"
}

image-20230120195333972

index.js

清空后输入Page, 生成一个模板

image-20230120195503781

2. iconfont矢量图引入

iconfont-阿里巴巴矢量图标库

网站使用方法

将图标添加到库image-20230120200028105

将库中图标添加到项目

image-20230120200103151

image-20230120200321970

进入连接后里面的内容就是我们需要的远程图标和样式

image-20230120200403851

开发工具中使用

  1. 在pages同级列表下创建styles文件夹,里面创建iconfont.wxss文件,将上面内容复制进去-

  2. 在app.wxss中进行全局引入@import "./styles/iconfont.wxss"

  3. 以在首页使用为例:

    <text class="iconfont icon-kefu">
    </text>
    

    image-20230120200951198

3.底部菜单tabbar

该项目创建了icons文件夹并放置了一些本地图标

image-20230120202038499

接下来对app.json进行处理,下面只写出改动部分

"pages":[
    //底部菜单的四个内容
    "pages/index/index",
    "pages/category/index",
    "pages/cart/index",
    "pages/my/index"
  ],
"tabBar": {
    //正常颜色
    "color": "#999",
    //选中后的颜色
    "selectedColor": "#FF5700",
    //底色
    "backgroundColor": "#fafafa",
    
    //四个菜单元素
    "list": [{
        //选中后前往的地址
      "pagePath": "pages/index/index",
        //文本信息
      "text": "首页",
        //图标
      "iconPath": "icons/_home.png",
        //选中后的图标
      "selectedIconPath": "icons/home.png"
    },
    {
      "pagePath": "pages/category/index",
      "text": "分类",
      "iconPath": "icons/_category.png",
      "selectedIconPath": "icons/category.png"
    },{
      "pagePath": "pages/cart/index",
      "text": "购物车",
      "iconPath": "icons/_cart.png",
      "selectedIconPath": "icons/cart.png"
    },{
      "pagePath": "pages/my/index",
      "text": "我的",
      "iconPath": "icons/_my.png",
      "selectedIconPath": "icons/my.png"
    }]
  },

4.初始化全局样式

  • 在app.wxss中进行全局样式的设置
/* 初始化全局样式,该代码会报错,但是程序正常运行,原因未知 */
page,view,text,swiper,swiper-item,image,navigator{
    /* css中经典的盒式模型*/
  padding:0;
  margin:0;
  box-sizing:border-box;
}

/* 全局变量定义,设置主题颜色,字体大小等 */
page{
  /* 主题颜色 */
  --themeColor:#FF5700;
  /* 字体大小 rpx自适应大小,根据设备不同会自动适应屏幕大小 */
  font-size:28rpx;
}
  • 使用上述全局样式

    • 我们曾在index.wxml中写入了view标签包含的代码

    • 现在我们在index.wxss中使用全局样式

      /* 对view标签内容设置颜色,这里使用变量要使用var,--themeColor是我们上面设置的全局样式变量 */
      view{
        color:var(--themeColor);
      }
      
    • 效果如下:

    • image-20230121143252434

  • 设置app.json中导航栏的颜色:

"window":{
    "backgroundTextStyle":"light",
    //设置导航栏的颜色
    "navigationBarBackgroundColor": "#FF5700",
    "navigationBarTitleText": "Weixin",
    "navigationBarTextStyle":"white"
  },

image-20230121143537546

5.搜索框自定义组件实现

预备

  • 为了提高复用性,我们将搜索框设计为自定义组件,这样我们任何页面下都可以调用
  • 我们将所有自定义组件都放在一个自己创建的components文件夹下
  • 任何一个组件我们都要在components文件夹下创建新的文件夹存储,文件夹中需要像其他内容一样有js,json,wxml,wxss四个文件。快速创建四个文件方式如下:
  • image-20230121144519790
  • image-20230121214726940

创建与使用

  • 以在首页使用为例,在index.json中引入
"usingComponents": {
    //组件名称为:前的SearchBar
    //components文件夹下的SearchBar文件夹内的SearchBar组件
    "SearchBar":"/components/SearchBar/SearchBar"
  },
  • 在index.wxml中使用
<view>
    //将上面:前的内容作为标签名
  <SearchBar></SearchBar>
</view>

此时使用成功,只是没有样式、

  • 写样式,目前没有引入less,使用正常写法

    <view class="search_bar">
    <!-- 设置跳转页面(在app.json的pages中注册) -->
      <navigator url="/pages/search/index" open-type="navigate">
        <icon type="search" size="16"></icon>搜索
      </navigator>
    </view>
    

    image-20230121215908839

    • 在SearchBar.wxss中写入详细样式,由于没有导入less,样式代码极其复杂,要一层一层写
    /* 设置搜索栏的高度,边距,背景色 */
    .search_bar{
      height: 90rpx;
      padding: 10rpx;
      background-color: var(--themeColor);
    }
    
    /* 设置搜索栏文本样式 */
    .search_bar navigator{
      /* 100%继承上层的高度 */
      height: 100%;
      /* 使用伸缩盒子 */
      display: flex;
      /* 文字居中,快捷:jcc */
      justify-content: center;
      /* 垂直居中,快捷键:alc */
      align-items: center;
      /* 这个白色是搜索框的那个白色 */
      background-color: #fff;
      /* 文字颜色 */
      color: #666;
      /* 圆角 */
      border-radius: 15rpx;
    }
    
    .search_bar navigator icon{
      /* 设置右边距,让他离“搜索”文本远一些 */
      padding-right: 5rpx;
    }
    

    image-20230121220617551

6.使用less插件

Less是一门css预处理语言,让css更容易维护,方便制作主题、扩充

安装配置方法参考:微信开发者工具配置支持less_Java开源博客系统-Powered by java1234

2022后版本先去上面链接里下载插件,安装看这个:(13条消息) 2022版本微信开发者工具引入less插件_周怼怼的博客的博客-CSDN博客_微信开发者工具如何安装less插件

  • 使用后就在.less中写代码,保存后会自动生成.wxss内容。对上文搜索框样式内容进行修改

    .search_bar{
      height: 90rpx;
      padding: 10rpx;
      background-color: var(--themeColor);
      navigator{
        height: 100%;
      display: flex;
      justify-content: center;
      align-items: center;
      background-color: #fff;
      color: #666;
      border-radius: 15rpx;
        icon{
          padding-right: 5rpx;
        }
      }
    }
    

7.SpringBoot后端项目架构搭建

创建

创建时导入的依赖:lomBok,Spring Web,Spring Boot DevTools

导入依赖

<dependencies>
    <!--  web依赖  -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--  devtools可以提高开发者的工作效率,最方便的地方莫过于热部署  -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
    </dependency>
    <!--  lombok省去构造器,getter,setter等实体类的代码  -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <!--  mysql驱动  -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    <!--  德鲁伊连接池  -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.10</version>
    </dependency>
    <!--  mybatis-plus  -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.3.2</version>
    </dependency>
    <!--  添加Httpclient支持  -->
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
        <version>4.5.2</version>
    </dependency>
    <!--  fastjson,用来转化json  -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.40</version>
    </dependency>
    <!--  JWT  -->
    <dependency>
        <groupId>com.auth0</groupId>
        <artifactId>java-jwt</artifactId>
        <version>3.2.0</version>
    </dependency>
    <!--  JJWT  -->
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.7.0</version>
    </dependency>
    <!--  JDOM  -->
    <dependency>
        <groupId>jdom</groupId>
        <artifactId>jdom</artifactId>
        <version>1.1</version>
    </dependency>
    <!--  dom4j  -->
    <dependency>
        <groupId>dom4j</groupId>
        <artifactId>dom4j</artifactId>
        <version>1.6.1</version>
    </dependency>
    <!--  commons-io流包  -->
    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>2.5</version>
    </dependency>
</dependencies>

处理SpringBoot配置文件

我们一般配置文件用yml,所以把自动生成的application.properties改为yml文件

进行如下配置:

server:
  port: 8080
  servlet:
    context-path: 

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    # 有些数据库版本需要加上&useSSL=false
    url: jdbc:mysql://localhost:3306/mall-system?serverTimezone=Asia/Hongkong&useSSL=false
    username: root
    password: 123456

建包

  • config 配置包
  • controller 控制类包
  • mapper mybatis的mapper类包
  • pojo 实体类包(也可以叫做entity)
  • service 服务层包
    • impl 服务层接口实现类包
  • util 工具类包

导入(编写)一个前后端分离常用实体类

package com.zaughter.pojo;

import java.util.HashMap;
import java.util.Map;

/**
 * 页面响应entity
 */
public class R extends HashMap<String, Object> {

    private static final long serialVersionUID = 1L;

    //无参构造器
    public R() {
        put("code", 0);
    }

    //error方法的重载
    public static R error() {
        return error(500, "未知异常,请联系管理员");
    }
    //带异常信息
    public static R error(String msg) {
        return error(500, msg);
    }
    //带异常代码与信息
    public static R error(int code, String msg) {
        R r = new R();
        r.put("code", code);
        r.put("msg", msg);
        return r;
    }

    //ok方法的重载
    public static R ok(String msg) {
        R r = new R();
        r.put("msg", msg);
        return r;
    }
    public static R ok(Map<String, Object> map) {
        R r = new R();
        r.putAll(map);
        return r;
    }
    public static R ok() {
        return new R();
    }

    //由于R是一个HashMap,所以可以通过键值对存入数据,这里是put的重载
    public R put(String key, Object value) {
        //调用父类(HashMap)的put方法
        super.put(key, value);
        return this;
    }
}

8.Swiper轮播图后端接口实现

几个特殊操作

  1. 在application.yml中配置mybatis-plus
mybatis-plus:
  global-config:
    db-config:
      id-type: auto # id生成规律:数据库id自增
  configuration:
    map-underscore-to-camel-case: false # 开启驼峰功能
    auto-mapping-behavior: full # 自动映射任何复杂的结果
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 日志
  mapper-locations: classpath:mybatis/mapper/*.xml # mapper路径
  1. 由于是前后端分离,所以所有的日期都要有前后端分离操作

    1. 实体类中关于日期的属性
    //json序列化,由于是前后端分离,所以所有的日期都要有前后端分离操作
        @JsonSerialize(using=CustomDateTimeSerializer.class)
        private Date hotDateTime; // 设置热门推荐日期时间
    
    1. 写一个json序列化的实体类
    package com.zaughter.pojo;
    
    import com.fasterxml.jackson.core.JsonGenerator;
    import com.fasterxml.jackson.databind.JsonSerializer;
    import com.fasterxml.jackson.databind.SerializerProvider;
    
    import java.io.IOException;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.TimeZone;
    
    /**
     * 自定义返回JSON 数据格式中日期格式化处理
     */
    public class CustomDateTimeSerializer extends JsonSerializer<Date>{
    
    	@Override
    	public void serialize(Date value, JsonGenerator gen, SerializerProvider serializers)
    			throws IOException {
    		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    		sdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
    		gen.writeString(sdf.format(value));  
    	}
    
    }
    

二:首页基本处理

9.首页Swiper轮播图静态实现

微信swiper官方文档:swiper | 微信开放文档 (qq.com)

给本地图片进行虚拟路径映射

//web项目配置类
@Configuration
public class WebAppConfigure implements WebMvcConfigurer {
    @Override
    //资源处理,给图片文件夹虚拟映射
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        //访问xx路径会映射到本地路径,注意本地路径开头要加file:最后要再加一次\\
        registry.addResourceHandler("/image/swiper/**").addResourceLocations("file:D:\\study files\\java files\\微信小程序-电商实战项目\\swiperImgs\\");
    }
} 

对首页使用swiper轮播图

代码写在index.wxml中

<!-- 轮播图开始 -->
  <view class="index_swiper">
    <swiper>
      <swiper-item>
        <navigator>
          <image src="http://localhost:8080/image/swiper/2.jpg"></image>
        </navigator>
      </swiper-item>
    </swiper>
  </view>
<!-- 轮播图结束 -->
重要设置1

目前开发阶段使用的都是本地域名,所以要进行如下设置防止出现问题

image-20230123090217527

image-20230123090315205

重要设置2

发布小程序时,要设置服务器域名白名单

小程序后台管理->开发管理->开发设置->服务器域名

图片适配屏幕

index.xml

在image标签中添加mode

<swiper-item>
    <navigator>
        <!-- 宽度自适应,这样防止由于下面less将尺寸写死导致部分机型显示有问题 -->
      <image mode="widthFix" src="http://localhost:8080/image/swiper/1.jpg"></image>
    </navigator>
</swiper-item>

index.less

.index_swiper{
  swiper{
      //图片大小是2:1,所以这里也是2:1的比例
    width: 750rpx;
    height: 375rpx;
    swiper-item{
      image{
          //宽度100%继承
        width: 100%;
      }
    }
  }
}

装修

内容都可以从swiper官方文档中获取

指示点

在swiper标签下添加indicator-dots

image-20230123092317008

自动滑动

在swiper标签下添加autoplay

但是此时无法循环,当滑动到最后一张图片后便不会再发生滑动

添加circular,设置为循环滑动

10.首页swiper轮播图ajax异步请求数据动态渲染实现

请求数据

思路:在index.js中设置。

  1. 先定义轮播图数组
data: {
    // 轮播图数组
    swiperList:[]
  },
  1. 请求后端。这里我们使用微信提供的wx.request这个api,官方文档:RequestTask | 微信开放文档 (qq.com)
onLoad: function (options) {
    // 发送异步请求获取后端数据
    wx.request({
      url: 'http://localhost:8080/product/findSwiper',
      method:"GET",
      success:(result)=>{
        this.setData({
          swiperList:result.data.message
        })
      }
    })
}
  1. 遍历。原来我们通过写死swiper-item的url来引入图片(一个图片就要写一组),现在我们改用动态的。

目前url是通过拼接实现的,这种实现方式并不好,后面我们要进行封装

 <!-- 对已经含有后端数据的数组进行遍历,并将遍历出来的元素命名为swiper(如果不命名) -->
      <swiper-item
        wx:for="{{swiperList}}"
        wx:for-item="swiper"
        wx:key="id"
      >
        <navigator>
          <image mode="widthFix" src="{{'http://localhost:8080/image/swiper/'+swiper.swiperPic}}"></image>
        </navigator>
      </swiper-item>

11.Promise方式的请求工具类封装

上面我们在没有封装的情况下请求后端,但是如果我们现在需要依赖于得到的请求结果进行进一步的请求,我们就要再续上一次wx.request。那么如果有很多层呢?就会形成“回调地狱”

在es6中提出了异步依赖回调的解决方案,同一up主的相关课程:es6视频教程-Java1234课堂

创建一个utils工具包,里面新建我们的requestUtil工具类

//后端请求工具类
//到时候requestUtil会传入一系列参数
export const requestUtil=(params)=>{
    //如果请求成功就走resolve,失败就走reject
  return new Promise((resolve,reject)=>{
    wx.request({
        //解析那些参数
      ...params,
      success:(result)=>{
          //这个.data的目的是为了在使用时少打一个.data
        resolve(result.data)
      },
      fail:(err)=>{
        reject(err)
      }
    })
  })
}

使用工具类要先导入

//导入requestUtil请求工具类
import {requestUtil} from '../../utils/requestUtil.js'
Page({

使用

requestUtil({url: 'http://localhost:8080/product/findSwiper',method:"GET"})
  .then(result=>{
    this.setData({
      swiperList:result.message
      })
  })
}

跟原先版本进行对比

wx.request({
    //url和method都作为参数传入工具类中,后面...params解构
   url: 'http://localhost:8080/product/findSwiper',
   method:"GET",
   success:(result)=>{
     this.setData({
         //这里就是那个.data
       swiperList:result.data.message
     })
   }
 })

规范

在企业开发中,我们最好不要将业务代码整个写到js文件的onload中。

将其抽取出来变成一个方法,然后onload中只有一行调用这个方法即可

错误示范:image-20230123151541831

正确规范:image-20230123152101368

12.请求根路径baseUrl封装

上面通过Promise方式优化后其实还有不足

requestUtil传参时requestUtil({url: 'http://localhost:8080/product/findSwiper',method:"GET"})将url写死了,那么假如使用了很多次这个工具类,当域名发生改变时就需要修改很多次(swiper中图片的读取也存在这个问题),所以要把根路径进行封装

requestUtil.js

//定义请求跟路径baseUrl,8080后面的/我们用的时候另外加,防止错乱
const baseUrl="http://localhost:8080";
//返回请求根路径baseUrl
export const getBaseUrl=()=>{
  return baseUrl;
}

在index.js中导入getBaseUrl,在data(所有页面数据的初始化都在这里,比如 上面的swiperList)中初始化baseUrl:baseUrl:"

wxml中swiper图片的导入

正常拼接即可

<image mode="widthFix" src="{{baseUrl+'/image/swiper/'+swiper.swiperPic}}"></image>

Promise方式请求后端数据

requestUtil方式中传入根路径后面的剩余部分/product/findSwiper,根路径放在setData中

requestUtil使用

requestUtil({url: '/product/findSwiper',method:"GET"})
  .then(result=>{
    //通过导入的getBaseUrl方法得到Url
    const baseUrl=getBaseUrl();
    this.setData({
      swiperList:result.message,
      //将得到的Url通过setDate传入
      baseUrl
      })
  })

requestUtil定义

export const requestUtil=(params)=>{
  return new Promise((resolve,reject)=>{
    wx.request({
      ...params,
        //解析处参数中的Url后与已经set好的baseUrl拼接
      url:baseUrl+params.url, 
      success:(result)=>{
        resolve(result.data)
      },
      fail:(err)=>{
        reject(err)
      }
    })
  })
}

13.请求高级封装es7 async await语法支持

终极封装,可以让异步请求更加优雅,易于维护;

配置方式:

  1. 在小程序开发工具中勾选(默认已勾上)

image-20230123152735127

  1. 下载facebook的regenerator库中的runtime.js

  2. 在小程序目录下新建文件夹lib/runtime/runtime.js

  3. 在每一个需要使用async语法的页面js文件中都引入(不能全局引入),只需要引入,不需要调用

    import regeneratorRuntime from '../../lib/runtime/runtime'
    

然后就可以正常的使用async,await语法处理异步请求了

改进后的代码:

// 外层使用async声明这是一个异步方法,这样它不会阻碍到后面方法的执行
async getSwiperList(){
    //await声明这里需要同步,这就是等requestUtil这个方法执行完毕,返回给result后再进行后续的代码
    const result=await requestUtil({url: '/product/findSwiper',method:"GET"});
    const baseUrl=getBaseUrl();
    this.setData({
      swiperList:result.message,
     baseUrl
    })
  }

14.后端查询所有商品大类接口实现

自己看视频P14,就是cv大法加查找替换。LJ2022版本ctrl+f为查询,替换需要用ctrl+R。注意替换前要开启大小写敏感

15.首页商品大类显示axious异步请求数据动态渲染实现

  1. 首先先给本地的商品大类图片在WebAppConfigure.java中进行映射

    registry.addResourceHandler("/image/bigType/**").addResourceLocations("file:D:\\study files\\java files\\微信小程序-电商实战项目\\bigTypeImgs\\");
    
  2. 前端初始化数据

    data: {
    // 轮播图数组
    swiperList:[],
    // 根路径
    baseUrl:'',
    // 储存所有商品大类图片的大数组、
    bigTypeList:[], 
    // 储存相应行上的商品大类图片的小数组
    bigTypeList_row1:[],
    bigTypeList_row2:[]
    },
    
  3. 前端设置获取数据的方法

    <!-- 商品大类图片开始 -->
    <view class="index_bigType">
    <view class="bigTypeRow">
      <navigator
        wx:for="{{bigTypeList_row1}}"
        wx:for-item="bigType"
        wx:key="id"
      >
        <image mode="widthFix" src="{{baseUrl+'/image/bigType/'+bigType.image}}"></image>
      </navigator>
    </view>
    <view class="bigTypeRow">
      <navigator
        wx:for="{{bigTypeList_row2}}"
        wx:for-item="bigType"
        wx:key="id"
      >
        <image mode="widthFix" src="{{baseUrl+'/image/bigType/'+bigType.image}}"></image>
      </navigator>
    </view>
    </view>
    <!-- 商品大类图片结束 -->
    
  4. 设置less样式

    // 商品大类图片样式
    .index_bigType{
      padding-top: 20rpx;
      background-color: #F7F7F7;
      .bigTypeRow{
        display: flex;
        navigator{
          flex: 1;
          image{
            width: 150rpx;
          }
        }
      }
    }
    

16.后端热门推荐商品接口实现

//查询前八个热门推荐商品
@GetMapping("/findHot")
public R findHot(){
    Page<Product> page = new Page<>(0,8);
    Page<Product> pageProduct = productService.page(page, new QueryWrapper<Product>().eq("isHot", true).orderByAsc("hotDateTime"));
    List<Product> hotProductList = pageProduct.getRecords();
    Map<String,Object> map=new HashMap<>();
    map.put("message",hotProductList);
    return R.ok(map);
}

这里面用到了mybatis-plus给我们提供的Page分页对象,所以需要写mybatis-plus的配置才能正常分页

package com.zaughter.config;

import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * MybatisPlus配置类
 */
@Configuration
public class MybatisPlusConfig {

    @Bean
    public PaginationInterceptor paginationInterceptor(){
        return new PaginationInterceptor();
    }
}

17.首页热卖推荐商品显示axious异步请求数据动态渲染实现

搞到现在,对于图片展示的流程基本摸清了:后端设置路径,这个路径会调用方法返回相关数据=>WebAppConfigure.java映射=>js文件中通过数据映射路径将数据导入到数组中=>在wxml中通过图片映射路径展示图片=>去less中调整图片样式

  1. 映射本地虚拟路径registry.addResourceHandler("/image/product/**").addResourceLocations("file:D:\\study files\\java files\\微信小程序-电商实战项目\\Imgs\\productImgs\\");

  2. // 获得推荐热卖商品列表
    async getHotProductList(){
      const result=await requestUtil({url: '/product/findHot',method:"GET"});
      this.setData({
        hotProductList:result.message,
      })
    },
    
  3. <!-- 推荐商品图片开始 -->
    <view class="index_hotProduct">
    <view class="product_title">热卖推荐</view>
    <view class="product_list">
      <view class="product_detail"
        wx:for="{{hotProductList}}"
        wx:for-item="hotProduct"
        wx:key="id"
      >
        <navigator>
          <image mode="widthFix" src="{{baseUrl+'/image/product/'+hotProduct.proPic}}"></image>
          <view class="product_name">{{hotProduct.name}}</view>
          <view class="product_price"> ¥ {{hotProduct.price}}</view>
          <button size="mini" type="warn">立即购买</button>
        </navigator>
      </view>
    </view>
    </view>
    <!-- 推荐商品图片结束 -->
    
  4. .index_hotProduct{
      .product_title{
        font-size: 32rpx;
        font-weight: 600;
        padding: 20rpx;
        color: var(--themeColor);
        background-color: #E0E0E0;
      }
      .product_list{
        display: flex;
        //里面元素只要满足条件就会自动换行
        flex-wrap: wrap;
        .product_detail{
          margin: 15rpx;
          //这样就能自动换行了
          width: 46%;
          text-align: center;
          navigator{
            image{
              width: 100%;
              background-color: #F5F5F5;
            }
            .product_name{
              //正常情况下如果商品名字过长需要显示多行
              //通过下面三行代码可以让名字只在一行显示,多余的内容通过...省略
              white-space: nowrap;
              overflow: hidden;
              text-overflow: ellipsis;
            }
            .product_price{
              color: var(--themeColor);
            }
            // button{}
          }
        }
      }
    }
    

18.后端商品分类显示接口实现

主要还是那些cv大法,看视频即可,没什么新知识

19.获取后端商品分类接口数据

添加编译模式

默认编译模式下我们都是在首页开始,那么如果在写其他页面前端代码,每次测试还要手动点击去跳转到那个页面比较麻烦,这时候可以添加编译模式设置启动页面,还可以携带参数

剩下还是老知识

三:商品分类基本处理

20.商品分类显示scroll-view布局显示

左右两侧都是纵向的scroll-view,在wxml中使用scroll-view标签即可

<view class="cates_container">
<!-- 左侧菜单开始 -->
  <scroll-view scroll-y class="left_menu">
	
  </scroll-view>
<!-- 左侧菜单结束 -->
<!-- 右侧商品数据开始 -->
  <scroll-view scroll-y class="right_context">

  </scroll-view>
<!-- 右侧商品数据结束 -->
</view>

21.商品分类动态数据显示实现

开始丰富我们上面写的scroll-view布局

左侧:

<scroll-view scroll-y class="left_menu">
<view class="menu_item"
  wx:for="{{leftMenuList}}"
  wx:key="*this"
>{{item}}
</view>
</scroll-view>

分析:

  1. 这个视图标签叫menu_item
  2. 我们使用wx:for遍历存放数据的数组leftMenuListimage-20230208203728068
  3. 我们要获取value值,但是由于数据没有key值,所以这里使用*this,代表获取数据本身
  4. 然后在view标签中通过{{item}}显示遍历出来的数据

右侧:

<scroll-view scroll-y class="right_context">
<view class="productType"
wx:for="{{rightContext}}"
wx:for-item="productType"
wx:key="id"
>
<view class="productType_title">
  {{productType.name}}
  <view class="product_list"
  wx:for="{{productType.productList}}"
  wx:for-item="product"
  wx:key="id"
  >
  <navigator>
    <image mode="widthFix" src="{{baseUrl+'/image/product/'+product.proPic}}"></image>
    <view class="product_name">{{product.name}}</view>
    <view class="product_price"> ¥ {{product.price}}</view>
  </navigator>  
  </view>
</view>
</view>
</scroll-view>

分析:

image-20230208204711659

  1. 首先遍历外层rightContext,将遍历得到的元素命名为productType。并且在页面上显示出productType元素的name属性
  2. 第二层遍历productType元素下面的productList数组,将遍历元素命名为product。
  3. 显示图片同样使用navigator(这个代表链接),其中的内容与之前轮播图的类似。注意在js中初始化baseUrl

具体样式直接看视频,这种没啥可记录的

22.左侧菜单点击切换实现

我们先增加左侧菜单的wxml内容

<scroll-view scroll-y class="left_menu">
<view class="menu_item {{index==currentIndex ? 'active':''}}"
  wx:for="{{leftMenuList}}"
  wx:key="*this"
  data-index="{{index}}"
  bindtap="handleMenuItemChange"
>{{item}}
</view>
</scroll-view>

分析:

  • data-index=“{{index}}”:程序遍历的时候,后面的index相当于当前索引的下标。我们利用data-xxx来创建一组数据,这里起名为index。
  • bindtap=“handleMenuItemChange”:创建一个点击事件方法
  • {{index==currentIndex ? ‘active’:‘’}}:判断当前用户选中的页面索引是不是遍历到的页面的索引,如果是的话就加一个我们设定好的名为active的样式image-20230208215443372,如果不是则样式为空(三元表达式)

然后去js文件中写点击事件的内容:

  //左侧菜单点击切换事件
  handleMenuItemChange(e){
    const {index}=e.currentTarget.dataset;
    let rightContext=this.Cates[index].smallTypeList;
    this.setData({
      currentIndex:index,
      rightContext,
    })
  },

分析:

  • 点击事件返回的日志如下:image-20230208215752135
  • 箭头指向的index就是我们利用data-index创建的数据,现在我们把它的值赋给遍历index
    • 这里运用了解析的方法(加了{}),如果不用解析,正常应该写为const index=e.currentTarget.dataset.index;,解析可以针对只有单一数据的情况,这里dataset数组下只有index一种数据
  • 我们让rightContext数组的内容为当店index指向的小商品类型数组,这样就可以改变页面右侧商品内容image-20230208220119254
  • 最后将点击事件指向的index作为currentIndex传入数据集中,一并传入的还有更新的rightContext
  • 这样wxml中的{{index==currentIndex ? 'active':''}}判断语句便可以生效

23.竖向滚动条初始化

现在当点击左侧切换分区时,右边滚动条位置不会重置,而是跟原来位置一样,所以每次需要初始化为0

index.wxml中对scroll-view标签添加内容

<scroll-view scroll-y class="right_context" scroll-top="{{scrollTop}}">

scroll-top属性作为一个变量,变量为scrollTop

增加了新的数据,就去index.jsdata区增加

data: {
  baseUrl:'',
  currentIndex:0,    // 当前选中左侧菜单的索引
  scrollTop:0,       // 设置竖向滚动条位置
  leftMenuList:[],    // 左侧大类数据
  rightContext:[],    // 右侧小类数据
},

然后对点击事件handleMenuItemChange增加初始化内容

handleMenuItemChange(e){
  const {index}=e.currentTarget.dataset;
  let rightContext=this.Cates[index].smallTypeList;
  this.setData({
    currentIndex:index,
    rightContext,
    scrollTop:0
  })
},

首页商品大类跳转

使用switchTab方法实现跳转,跳转的时候我们应该携带参数,但是这个方法不能携带。所以我们使用全局参数设置的方法来间接传参

首先去全局的app.js中设置全局变量index

// app.js
App({
  onLaunch() {
    
  },
  globalData: {
    index:-1
  }
})

接着对首页大类的参数进行增加

<view class="index_bigType">
  <view class="bigTypeRow">
    <navigator
      bindtap="handleTypeJump" // 设置新的点击事件
      data-index="{{index}}" // 第一行的index,从0开始
      wx:for="{{bigTypeList_row1}}"
      wx:for-item="bigType"
      wx:key="id"
    >
      <image mode="widthFix" src="{{baseUrl+'/image/bigType/'+bigType.image}}"></image>
    </navigator>
  </view>
  <view class="bigTypeRow">
    <navigator
      bindtap="handleTypeJump" // 设置新的点击事件
      data-index="{{index+5}}" // 首页第二行的index,由于也是从0开始,但是实际是5开始,所以加5
      wx:for="{{bigTypeList_row2}}"
      wx:for-item="bigType"
      wx:key="id"
    >
      <image mode="widthFix" src="{{baseUrl+'/image/bigType/'+bigType.image}}"></image>
    </navigator>
  </view>
</view>

接着有了数据,就去index.js中增加点击事件具体内容

// 大类点击事件 跳转 商品分类页面
handleTypeJump(e){
  const {index}=e.currentTarget.dataset; // 结构传来参数中的index参数
  const app=getApp(); // 使用自带的类
  app.globalData.index=index; // 将这个点击得到的参数赋值给全局参数
  wx.switchTab({ // 跳转
    url: '/pages/category/index'
  })
},

使用生命周期函数中的onShow,每次跳转都会执行,而onLoad只有第一次加载的时候执行

注意这里面有一个异步问题,onLoad里执行了getCates这个方法去获得数据,但是在数据还没有得到时,就会执行onShow,导致onShow是得不到数据的,所以这时我们再写一个新的,同时再增加一些其他的初始化

// 获得商品分类数据(从首页过来)
async getCates2(index){
  const result=await requestUtil({url: '/bigType/findCategories',method:"GET"});
  this.Cates=result.message;
  let leftMenuList=this.Cates.map((v)=>{
    return v.name
  })
  let rightContext=this.Cates[index].smallTypeList;
  this.setData({
    leftMenuList,
    rightContext,
    currentIndex:index,// 其他的初始化
    scrollTop:0,
  })
},
    
/**
 * 生命周期函数--监听页面显示
 */
onShow: function() {
    const app=getApp();
    const {index}=app.globalData;
    console.log("index="+index)
    if(index!=-1){ // 从首页跳转过来
      this.getCates2(index); // 使用这个方法,能拿到数据
      app.globalData.index=-1; // 重置index
    }
},
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值