文章目录

  • 1.小程序的第三方框架
  • 2. 帮助文件
  • 3.项目的搭建
    • 3.1新建小程序项目
    • 3.2搭建目录结构
    • 3.3. 搭建项目的页面
    • 3.4. 引⼊字体图标
    • 3.5 创建tabbar页面
  • 4. ⾸⻚
    • 4.1. 效果
    • 4.2 使⽤⾃定义组件的⽅式实现头部搜索框
    • 4.3 首页轮播图
    • 4.4 首页分类导航模块
    • 4.5 首页楼层模块
  • 5. 分类页面
    • 5.1 请求相关的数据
    • 5.2 页面的编写
    • 5.3 相关的样式
    • 5.3 添加缓存
    • 5.4 点击菜单左侧列表置顶
    • 5.5 提取公共接口路径
  • 6.商品列表
    • 6.1 定义tab组件
    • 6.2 商品列表中使用组件
    • 6.3 商品列表的实现
  • 7. 商品详情页面
  • 8. 商品收藏页面
  • 9.购物车页面
    • 异步编程中的Promise
  • 10.搜索中心
    • 10.1 需求分析
    • 10.2 具体实现
  • 11.个人中心
    • 11.1 用户登录授权
    • 11.2 商品收藏
    • 11.3 意见反馈
      • 11.3.1 需求分析
      • 11.3.2 具体实现
        • 11.3.2.1 引入图片上传的组件
        • 11.3.2.2 页面编写
    • 11.4 个人中心

1.小程序的第三方框架

  1. 腾讯 wepy 类似于vue
  2. 美团 mpvue 类似于vue
  3. 京东 tao 类似于react
  4. 滴滴 chameleon
  5. uni-app 类似于vue
  6. 原生框架MINA

2. 帮助文件

接口文档地址:https://www.showdoc.com.cn/128719739414963/2513235043485226
阿里巴巴字体iconfont:https://www.iconfont.cn/
即本项目的后台已经写好了,直接调用接口即可

3.项目的搭建

3.1新建小程序项目

填入自己的openID

3.2搭建目录结构

⽬录名 作⽤
styles 存放公共样式
components 存放组件
lib 存放第三⽅库
utils ⾃⼰的帮助库
request ⾃⼰的接⼝帮助库

3.3. 搭建项目的页面

⻚⾯名称 名称
⾸⻚ index
分类⻚⾯ category
商品列表⻚⾯ goods_list
商品详情⻚⾯ goods_detail
购物⻋⻚⾯ cart
收藏⻚⾯ collect
订单⻚⾯ order
搜索⻚⾯ search
个⼈中⼼⻚⾯ user
意⻅反馈⻚⾯ feedback
登录⻚⾯ login
授权⻚⾯ auth
结算⻚⾯ pay

3.4. 引⼊字体图标

1. 打开阿⾥巴巴字体图标 ⽹站
https://www.iconfont.cn/
2. 选择的图标
选择自己想要的图标添加入库

3. 添加⾄项⽬
选择购物车图标

点击添加至项目(我选择了三个购物车的图标)

可以将其添加到一个已有的项目或者新建一个项目

本次项目所需要的图标

点击Font class生成代码

会生成一个class地址

4. 点击链接,复制样式,将其放入styles目录下的iconfont.wxss中

@font-face {font-family: "iconfont"; /* Project id 3078102 */src: url('//at.alicdn.com/t/font_3078102_um8ol0i9ny.woff2?t=1640609932945') format('woff2'),url('//at.alicdn.com/t/font_3078102_um8ol0i9ny.woff?t=1640609932945') format('woff'),url('//at.alicdn.com/t/font_3078102_um8ol0i9ny.ttf?t=1640609932945') format('truetype');}.iconfont {font-family: "iconfont" !important;font-size: 16px;font-style: normal;-webkit-font-smoothing: antialiased;-moz-osx-font-smoothing: grayscale;}.icon-weibiaoti2fuzhi08:before {content: "\e625";}.icon-shoucangxuanzhong:before {content: "\e62b";}.icon-fenxiang:before {content: "\e86e";}.icon-kefu:before {content: "\e88f";}.icon-dingdan:before {content: "\e645";}.icon-fukuantongzhi:before {content: "\e60c";}.icon-tuihuotuikuan_dianpu:before {content: "\e773";}.icon-shoucang:before {content: "\e8b9";}.icon-gouwucheman:before {content: "\e600";}.icon-gouwuchekong:before {content: "\e601";}.icon-gouwucheman1:before {content: "\e602";}

5. 在app.wxss中导入要使用的样式

@import "./styles/iconfont.wxss"

3.5 创建tabbar页面

1. 在项目中创建icon文件夹,并将素材中的相关图标拷贝进入

2. 常见tabbar相关页面和样式
app.json中

{"pages":["pages/index/index","pages/category/index","pages/goods_list/index","pages/goods_detail/index","pages/cart/index","pages/collect/index","pages/order/index","pages/search/index","pages/user/index","pages/feedback/index","pages/login/index","pages/auth/index","pages/pay/index"],"tabBar": {"color": "#999","selectedColor": "#ff2d4a","backgroundColor": "#fafafa","position": "bottom","borderStyle": "black","list": [{"pagePath": "pages/index/index","text": "首页","iconPath": "icons/home.png","selectedIconPath": "icons/home-o.png"},{"pagePath": "pages/category/index","text": "分类","iconPath": "icons/category.png","selectedIconPath": "icons/category-o.png"},{"pagePath": "pages/cart/index","text": "购物车","iconPath": "icons/cart.png","selectedIconPath": "icons/cart-o.png"},{"pagePath": "pages/user/index","text": "我的","iconPath": "icons/my.png","selectedIconPath": "icons/my-o.png"}]},"window":{"backgroundTextStyle":"light","navigationBarBackgroundColor": "#eb4450","navigationBarTitleText": "黑马优购","navigationBarTextStyle":"white"},"style": "v2","sitemapLocation": "sitemap.json"
}

2.初始化页面
在app.wxss中

@import "./styles/iconfont.wxss";/**在微信小程序中 不支持通配符* **/
page,view,text,swiper-item,image,navigator{padding: 0;margin: 0;box-sizing: border-box;
}
/**主题颜色 通过变量来实现less中存在变量这个知识点原生的css和wxss也支持变量
**/
page{/**定义主题颜色**/--themeColor:#eb4450;/**统一字体大小1px = 2rpx**/font-size: 20rpx;
}

4. ⾸⻚

4.1. 效果

4.2 使⽤⾃定义组件的⽅式实现头部搜索框

1. 在components下面创建一个SearchInput组件,实现搜索的功能

2. 在SearchInput.wxml创建搜索导航

<view class="search_input"><navigator url="/pages/search/index" open-type="navigate">搜索</navigator>
</view>

3. SearchInput.wxss中的样式

/* components/SearchInput/SearchInput.wxss */
.search_input {height: 90rpx;padding: 10rpx;background-color: var(--themeColor);
}
.search_input navigator {height: 100%;display: flex;justify-content: center;align-items: center;background-color: white;border-radius: 15rpx;color: #666;
}

4.引入所使用的组件
在pages/index/index.json中引入所使用的组件

{"usingComponents": {"SearchInput":"../../components/SearchInput/SearchInput"},"navigationBarTitleText": "优购首页"
}

5.使用组件
在pages/index/index.wxml

<view class="pyg_index"><!--搜索框开始--><SearchInput></SearchInput><!--搜索框结束-->
</view>

4.3 首页轮播图

1. 网络请求获取轮播图的数据
在pages/index/index.js下

Page({/*** 页面的初始数据*/data: {//轮播图数组swipperList:[]},/*** 生命周期函数--监听页面加载*/onLoad: function (options) {//异步请求获取轮播图的数据wx.request({url: 'https://api-hmugo-web.itheima.net/api/public/v1/home/swiperdata',success:(result)=>{this.setData({swipperList:result.data.message})}});}})

接口调用结果显示

没有结果显示解决办法

  • 没有添加appid,点击详情按钮,选择不校验合法域名
  • 为了后面的开发管理和部署上线,选择开发管理中的服务设置,设置服务器的域名

    2. 轮播图动态渲染
    在pages/index/index/wxml中
    <!--轮播图开始--><view class="index_swiper"><!--1.swiper标签存在默认高度和宽度100%*150px2.image标签也存在默认宽度和高度320px*240px3.设计轮播图先看原图的高度 750*340让图片高度自适应 宽度等于100%让swiper的高度和图片高度一样即可4.图片标签mode属性渲染模式widthfix 让标签的宽高和标签内容的宽高发生等比例的变化             --><swiper autoplay indicator-dots circular><swiper-item wx:for="{{swipperList}}" wx:key="goods_id"><navigator><image mode="widthfix" src="{{item.image_src}}"></image></navigator></swiper-item></swiper></view><!--轮播图结束-->

相应的wxss

.index_swiper swiper {width: 750rpx;height: 340rpx;
}
.index_swiper swiper image {width: 100%;
}

3. 将原生的请求改为promise方式

  1. 在request的目录下创建index.js
export const request=(params)=>{return new Promise((resolve,reject)=>{wx.request({...params,success:(result)=>{resolve(result);},fail: (err)=>{reject(err)}});} )
}
  1. 在pages/index/index.js中加入request.js的路径
import {request} from "../../request/index.js"
  1. 请求路径的改写
  onLoad: function (options) {//异步请求获取轮播图的数据// wx.request({//   url: 'https://api-hmugo-web.itheima.net/api/public/v1/home/swiperdata',//   success:(result)=>{//     this.setData({  //       swipperList:result.data.message//       })//   }// });request({url:'https://api-hmugo-web.itheima.net/api/public/v1/home/swiperdata'}).then(result=>{this.setData({swipperList:result.data.message})})},

4.4 首页分类导航模块

1. 请求接口数据
在pages/index/index.js中获取导航数据

  //获取导航数据getCatesList(){request({url:'https://api-hmugo-web.itheima.net/api/public/v1/home/catitems'}).then(result=>{this.setData({catesList:result.data.message})})}

2. 页面进行导航数据的显示
在pages/index/indedx.wxml中进行导航数据的显示

    <!--导航开始--><view class="index_cate"><navigator wx:for="{{catesList}}"wx:key="name"><image  mode="widthFix" src="{{item.image_src}}"></image></navigator></view><!--导航结束-->

3. css样式

.index_cate {display: flex;padding: 20rpx;
}
.index_cate navigator {flex: 1;
}
.index_cate navigator image {width: 100%;
}

4.5 首页楼层模块

1. 获取接口数据
pages/index/index.js

  //获取楼层的接口数据getFloorList(){request({url:'https://api-hmugo-web.itheima.net/api/public/v1/home/floordata'}).then(result=>{this.setData({floorList:result.data.message})})}

2. 内容显示
pages/index/index.wxml

  <!--楼层开始--><view class="index_floor"><view ><view wx:for="{{floorList}}"wx:for-item="item1"wx:for-index="index1"wx:key="floor_title"class="floor_group"><!--标题--><view class="floor_title" ><image src="{{item1.floor_title.image_src}}" mode="widthFix" ></image></view><!--内容--><view class="floor_list"><navigator wx:for="{{item1.product_list}}"wx:for-item="item2"wx:for-index="index2"wx:key="name"><image src="{{item2.image_src}}" mode="{{index2===0?'widthFix':'scaleToFill'}}"/></navigator></view></view></view></view><!--楼层结束-->

3. 导入样式
pages/index/index.wxss

.index_floor .floor_group .floor_title {padding: 10rpx 0;
}
.index_floor .floor_group .floor_title image {width: 100%;
}
.index_floor .floor_group .floor_list {overflow: hidden;
}
.index_floor .floor_group .floor_list navigator {float: left;width: 33.3%;/*后面四个的超链接*//*2.3超链接*/
}
.index_floor .floor_group .floor_list navigator:nth-last-child(-n+4) {/**原图 232*386232/286 = 33.3vw/height**/height: 27.72711207vw;border-left: 10rpx solid #fff;
}
.index_floor .floor_group .floor_list navigator:nth-child(2),
.index_floor .floor_group .floor_list navigator:nth-child(3) {border-bottom: 10rpx solid #fff;
}
.index_floor .floor_group .floor_list navigator image {width: 100%;height: 100%;
}

首页效果

5. 分类页面

5.1 请求相关的数据

在pages/category/index.js

// pages/category/index.js
import {request} from "../../request/index.js";
Page({/*** 页面的初始数据*/data: {//左侧的菜单数据leftMenuList:[],//右侧的商品数据rightContent:[],//被点击的左侧菜单currentIndex:0},//接口的返回数据Cates:[],onLoad:function(options){this.getCates()},//获取分类菜单getCates(){request({url:"https://api-hmugo-web.itheima.net/api/public/v1/categories"}).then(result=>{this.Cates=result.data.message//构造左侧的大菜单数据let leftMenuList=this.Cates.map(v=>v.cat_name)//构造右侧的商品数据let rightContent=this.Cates[0].children;this.setData({leftMenuList,rightContent})})},//左侧菜单的点击事件handleItemTap(e){/*** 1.获取别点击的索引* 2.getdata中currentIndex赋值* 3.根据不同的索引渲染右侧的菜单*/const {index}=e.currentTarget.dataset;let rightContent=this.Cates[index].children;this.setData({currentIndex:index,rightContent})}
})

5.2 页面的编写

在pages/category/index.wxml

<view class="cates"><SearchInput></SearchInput><view class="cates_container"><!--左侧菜单--><scroll-view class="left_menu" scroll-y><view class="menu_item {{index===currentIndex?'active':''}}"wx:for="{{leftMenuList}}"wx:key="this"bindtap="handleItemTap"data-index="{{index}}">{{item}}</view></scroll-view><!--右侧商品内容--><scroll-view class="right_content" scroll-y><view class="goods_group"wx:for="{{rightContent}}"wx:for-index="index1"wx:for-item="item1"><view class="goods_title"><text class="delimiter"></text><text class="title">{{item1.cat_name}}</text><text class="delimiter"></text></view><view class="goods_list"><navigator wx:for="{{item1.children}}"wx:for-index="index2"wx:for-item="item2"wx:key="cat_id"><image src="{{item2.cat_icon}}" mode="widthFix"></image><view class="goods_name">{{item2.cat_name}}</view></navigator></view></view></scroll-view></view></view>

5.3 相关的样式

在pages/category/index.wxss

page {height: 100%;
}
.cates {height: 100%;
}
.cates .cates_container {display: flex;height: cac(100vh - 90rpx);
}
.cates .cates_container .left_menu {flex: 2;
}
.cates .cates_container .left_menu .menu_item {height: 80rpx;display: flex;justify-content: center;align-items: center;font-size: 30rpx;
}
.cates .cates_container .left_menu .active {color: var(--themeColor);border-left: 5rpx solid currentColor;
}
.cates .cates_container .right_content {flex: 5;
}
.cates .cates_container .right_content .goods_group .goods_title {height: 80rpx;display: flex;justify-content: center;align-items: center;
}
.cates .cates_container .right_content .goods_group .goods_title .delimiter {color: #ccc;padding: 0 10rpx;
}
.cates .cates_container .right_content .goods_group .goods_list {display: flex;flex-wrap: wrap;
}
.cates .cates_container .right_content .goods_group .goods_list navigator {width: 33.33%;text-align: center;
}
.cates .cates_container .right_content .goods_group .goods_list navigator image {width: 50%;
}

5.3 添加缓存

先判断本地是否有没有过期的旧数据,如果有就使用旧数据,没有就重新从接口中获取
在pages/category/index.js中修改的部分代码

onLoad:function(options){/*** 1. 先判断本地存储中有没有旧数据* {tine:Date.now,data[...]}* 2. 没有旧数据直接发送新请求* 有旧数据同时旧数据也没有过期 就是要本地存储中的旧数据即可*///1.获取本地存储中的数据(小程序中也是本地存储)const Cates=wx.getStorage("cates");this.getCates()//2.判断if(!Cates){//不存在 发送请求获取数据this.getCates();}else{// //有旧的数据 定义过期时间 if(Date.now()-Cates.time>1000*10){//重新发送数据this.getCates()}else{//可以使用旧数据this.Cates=Cates.data;//构造左侧的大菜单数据let leftMenuList=this.Cates.map(v=>v.cat_name)//构造右侧的商品数据let rightContent=this.Cates[0].children;this.setData({leftMenuList,rightContent})}}},//获取分类菜单getCates(){request({url:"https://api-hmugo-web.itheima.net/api/public/v1/categories"}).then(result=>{this.Cates=result.data.message//把接口中的数据存入本地存储中wx.setStorageSync("cates", {time:Date.now(),data:this.Cates});//构造左侧的大菜单数据let leftMenuList=this.Cates.map(v=>v.cat_name)//构造右侧的商品数据let rightContent=this.Cates[0].children;this.setData({leftMenuList,rightContent})})},

5.4 点击菜单左侧列表置顶

在scrocll-view中有设置滚动条的位置
在页面中添加属性scroll-top并绑定值

在data中添加scrollTop

修改点击事件,点击一次距离顶部的距离为0

  //左侧菜单的点击事件handleItemTap(e){/*** 1.获取别点击的索引* 2.getdata中currentIndex赋值* 3.根据不同的索引渲染右侧的菜单*/const {index}=e.currentTarget.dataset;let rightContent=this.Cates[index].children;this.setData({currentIndex:index,//距离顶部的位置scrollTop:0,rightContent})}

5.5 提取公共接口路径


发现这些接口有一些共同的路径,我们将其提取处理
在request/index.js下提取公共路径

然后在其他的请求接口中删除https://api-hmugo-web.itheima.net/api/public/v1/即可

6.商品列表

6.1 定义tab组件


1. tab/tab.wxml

<view class="tab"><view class="tab_title"><view class="title_item {{item.isActive?'active':''}}"wx:for="{{tab}}"wx:key="id" bindtap="handleItemTap"data-index="{{index}}">{{item.value}}</view></view><view class="tab_content"><slot></slot></view>
</view>

2. tab/tab.wxss

.tab{}
.tab_title{display: flex;
}
.title_item{display: flex;justify-content: center;align-items: center;flex: 1;padding: 15rpx 0;
}
.active{color: var(--themeColor);border-bottom: 5rpx solid currentColor;
}
.tab_content{}

3. tab/tab.js

// components/tab/tab.js
Component({/*** 组件的属性列表*/properties: {tab:{type:Array,value:[]}},/*** 组件的初始数据*/data: {},/*** 组件的方法列表*/methods: {handleItemTap(e){//获取点击的索引const {index}=e.currentTarget.dataset;//触发父组件中的事件  自定义this.triggerEvent("tabItemChange",{index})}}
})

6.2 商品列表中使用组件

1. pages/gods_list/index.wxml

    <!--搜索框开始--><SearchInput></SearchInput><!--搜索框结束--><tab tab="{{tab}}" bindtabItemChange="handleTabItemChange"><block wx:if="{{tab[0].isActive}}">1</block><block wx:elif="{{tab[1].isActive}}">2</block><block wx:elif="{{tab[2].isActive}}">3</block></tab>

2. pages/gods_list/index.json

{"usingComponents": {"SearchInput":"../../components/SearchInput/SearchInput","tab":"../../components/tab/tab"},"navigationBarTitleText": "商品列表"
}

3. pages/gods_list/index.js

// pages/goods_list/index.js
Page({/*** 页面的初始数据*/data: {tab:[{id:0,value:'综合',isActive:true},{id:1,value:'销量',isActive:false},{id:0,value:'价格',isActive:false}]},/*** 生命周期函数--监听页面加载*/onLoad: function (options) {console.log(options)},//标题的点击事件 从子组件中传递过来的handleTabItemChange(e){//获取被点击标题发索引const {index}=e.detail//修改原数组let {tab}=this.datatab.forEach((v,i)=>i===index?v.isActive=true:v.isActive=false);//赋值到data中this.setData({tab})}
})

6.3 商品列表的实现

需求分析

  1. 用户上滑页面 滚动条触底 开始加载下一页数据

    1. 找到滚动条触底事件
    2. 判断还有没有下一页数据
      1. 获取总页数 总页数=math.ceil(总条数/页容量)
      2. 获取当前页的页码
      3. 判断当前;页面是否大于总页数
    3. 假如没有下一页数据会弹出一个提示
    4. 假如有下一页数据,加载下一页数据
      当前的页码++
      重新发送请求
      数据请求回来 要对data中的数据进行拼接而不是全部替换
  2. 下拉刷新页面

    1. 触发下拉刷新事件 需要在页面的Json文件中开始一个配置项
    2. 重置数据 数组
    3. 重置页码 设置为1
    4. 重新发送请求
    5. 数据请求回来了 需要手动关闭 等待效果

1. pages/goods_list/index.js

import {request} from "../../request/index.js";
Page({/*** 页面的初始数据*/data: {tab:[{id:0,value:'综合',isActive:true},{id:1,value:'销量',isActive:false},{id:0,value:'价格',isActive:false}],goodsList:[]},//接口要的数据QueryParams:{qurty:"",cid:"",pagenum:1,pagesize:10},//总页数totalPages:1,/*** 生命周期函数--监听页面加载*/onLoad: function (options) {this.QueryParams.cid=options.cid;this.getGoodsList();},//获取商品数据getGoodsList(){request({url:"goods/search",data:this.QueryParams}).then(result=>{   //获取总条数const total=  result.data.message.total//计算总页数this.totalPages=Math.ceil(total/this.QueryParams.pagesize)console.log(this.totalPages)this.setData({goodsList:[...this.data.goodsList,...result.data.message.goods]})})//关闭下拉刷新的窗口 如果没有wx.stopPullDownRefresh()},//标题的点击事件 从子组件中传递过来的handleTabItemChange(e){//获取被点击标题发索引const {index}=e.detail//修改原数组let {tab}=this.datatab.forEach((v,i)=>i===index?v.isActive=true:v.isActive=false);//赋值到data中this.setData({tab})},//页面上滑 滑动条触底事件onReachBottom(){if(this.QueryParams.pagenum>=this.totalPages){wx.showToast({title: '没有下一页数据了' })}else{//还有下一页数据this.QueryParams.pagenum++;this.getGoodsList()}},//页面上滑刷新onPullDownRefresh(){//1.重置数组this.setData({goodsList:[]})//重置页码this.QueryParams.pagenum=1;//发送请求this.getGoodsList();}})

2. pages/goods_list/index.wxss

.first_tab .goods_item {display: flex;border-bottom: 1rpx solid #ccc;
}
.first_tab .goods_item .goods_img_wrap {flex: 2;display: flex;justify-content: center;align-items: center;
}
.first_tab .goods_item .goods_img_wrap image {width: 70%;
}
.first_tab .goods_item .goods_info_wrap {flex: 3;display: flex;flex-direction: column;justify-content: space-around;
}
.first_tab .goods_item .goods_info_wrap .goods_name {display: -webkit-box;overflow: hidden;-webkit-box-orient: vertical;-webkit-line-clamp: 2;
}
.first_tab .goods_item .goods_info_wrap .goods_price {color: var(--themeColor);font-size: 32rpx;
}

3. pages/goods_list/index.js

// pages/goods_list/index.js
import {request} from "../../request/index.js";
Page({/*** 页面的初始数据*/data: {tab:[{id:0,value:'综合',isActive:true},{id:1,value:'销量',isActive:false},{id:0,value:'价格',isActive:false}],goodsList:[]},//接口要的数据QueryParams:{qurty:"",cid:"",pagenum:1,pagesize:10},//总页数totalPages:1,/*** 生命周期函数--监听页面加载*/onLoad: function (options) {this.QueryParams.cid=options.cid;this.getGoodsList();},//获取商品数据getGoodsList(){request({url:"goods/search",data:this.QueryParams}).then(result=>{   //获取总条数const total=  result.data.message.total//计算总页数this.totalPages=Math.ceil(total/this.QueryParams.pagesize)console.log(this.totalPages)this.setData({goodsList:[...this.data.goodsList,...result.data.message.goods]})})//关闭下拉刷新的窗口 如果没有wx.stopPullDownRefresh()},//标题的点击事件 从子组件中传递过来的handleTabItemChange(e){//获取被点击标题发索引const {index}=e.detail//修改原数组let {tab}=this.datatab.forEach((v,i)=>i===index?v.isActive=true:v.isActive=false);//赋值到data中this.setData({tab})},//页面上滑 滑动条触底事件onReachBottom(){if(this.QueryParams.pagenum>=this.totalPages){wx.showToast({title: '没有下一页数据了' })}else{//还有下一页数据this.QueryParams.pagenum++;this.getGoodsList()}},//页面上滑刷新onPullDownRefresh(){//1.重置数组this.setData({goodsList:[]})//重置页码this.QueryParams.pagenum=1;//发送请求this.getGoodsList();}})

7. 商品详情页面

需求分析

  1. 点击轮播图预览大图
    1. 给轮播图绑定点击事件
    2. 调用小程序的api priviewUmage

  2. 点击加入购物车

    1. 绑定点击事件
    2. 获取缓存中的购物车数据 数组格式
    3. 先判断当前的商品是否存在购物车中
    4. 已经存在 修改购物车中的商品数量 购物车中的商品数量++ 重新把购物车中的数组填充回缓存中
    5. 不存在于购物车的数组中,直接给购物车数组添加一个新元素 新元素带上购买属性num 重新把购物车数组填充回缓存中

1.pages/goods_detail/index.wxml

<view class="detail_swiper"><swiperautoplaycircularindicator-dots><swiper-item wx:for="{{goodsObj.pics}}"wx:key="pics_id"bindtap="bindlePrevewImage"data-url="{{item.pics_mid}}"><image mode="widthFix" src="{{item.pics_mid}}"></image></swiper-item></swiper><view class="goods_price">¥{{goodsObj.goods_price}}</view><view class="goods_name_row"><view class="goods_name">{{goodsObj.goods_name}}</view><view class="goods_collect" bindtap="handleCollect"><text class="iconfont {{isCollect?'icon-shoucangxuanzhong':'icon-shoucang'}} "></text><view class="collect_text">收藏</view></view></view>
</view><view class="goods_info"><view class="goods_info_title">图文详情</view><view class="goods_info_content"><!--富文本--><rich-text nodes="{{goodsObj.goods_introduce}}"></rich-text></view>
</view><view class="btm_tool"><view class="tool_item"><view class="iconfont icon-kefu"></view><view>客服</view>   <button open-type="contact"></button>   </view><view class="tool_item"><view class="iconfont icon-fenxiang"></view><view>分享</view>  <button open-type="share"></button>    </view><navigator open-type="switchTab" url="/pages/cart/index" class="tool_item"><view class="iconfont icon-gouwucheman"></view><view>购物车</view>      </navigator><view class="tool_item btn_cart" bindtap="handleCartAdd"><view>加入购物车</view>      </view><view class="tool_item btn_buy"><view>立即购买</view>      </view>
</view>

2. pages/goods_detail/index.wxss

page {padding-bottom: 90rpx;
}
.detail_swiper swiper {height: 65vw;text-align: center;
}
.detail_swiper swiper image {width: 60%;
}
.goods_price {padding: 15rpx;font-size: 32rpx;font-weight: 600;color: var(--themeColor);
}
.goods_name_row {display: flex;
}
.goods_name_row .goods_name {border-top: 5rpx solid #dedede;border-bottom: 5rpx solid #dedede;padding: 10rpx 0;flex: 5;color: #000;font-size: 30rpx;padding: 0 10rpx;display: -webkit-box;overflow: hidden;-webkit-box-orient: vertical;-webkit-line-clamp: 2;
}
.goods_name_row .goods_collect {flex: 1;display: flex;flex-direction: column;justify-content: center;align-items: center;border-left: 1rpx solid #000;
}
.goods_name_row .goods_collect .icon-shoucangxuanzhong {color: orangered;
}
.goods_info .goods_info_title {font-size: 32rpx;color: var(--themeColor);font-weight: 600;padding: 20rpx;
}
.btm_tool {border-top: 1rpc solid #000;position: fixed;left: 0;bottom: 0;width: 100%;height: 90rpx;background-color: #fff;display: flex;
}
.btm_tool .tool_item {flex: 1;display: flex;flex-direction: column;align-items: center;justify-content: center;font-size: 24rpx;position: relative;
}
.btm_tool .tool_item button {position: absolute;top: 0;left: 0;width: 100%;height: 100%;opacity: 0;
}
.btm_tool .btn_cart {flex: 2;display: flex;flex-direction: column;justify-content: center;align-items: center;background-color: #ffa500;color: #fff;font-size: 30rpx;font-weight: 600;
}
.btm_tool .btn_buy {flex: 2;display: flex;flex-direction: column;justify-content: center;align-items: center;background-color: #eb4450;color: #fff;font-size: 30rpx;font-weight: 600;
}
  1. pages/goods_detail/index.js
// pages/goods_detail/index.js
import {request} from "../../request/index.js";
/*** 点击轮播图预览大图*  1.给轮播图绑定点击事件*  2.调用小程序的api priviewUmage* * 点击加入购物车*  1.绑定点击事件*  2.获取缓存中的购物车数据  数组格式*  3.先判断当前的商品是否存在购物车中 *  4.已经存在 修改购物车中的商品数量  购物车中的商品数量++ 重新把购物车中的数组填充回缓存中*  5.不存在于购物车的数组中,直接给购物车数组添加一个新元素 新元素带上购买属性num 重新把购物车数组填充回缓存中* * * 3. 商品收藏*  1. 页面onshow的时候 加载缓存中的商品收藏数据*  2. 判断该商品是不是被收藏*    是 改变页面的图标* 3. 点击商品收藏按钮*    1. 判断该商品是否存在商品缓存数组中 *    2. 已存在 把该商品删除*    3. 不存在 把商品添加到收藏数组中
*/Page({/*** 页面的初始数据*/data: {goodsObj:{},//商品是否别收藏过isCollect:false},//商品对象GoodsInfo:{},/*** 生命周期函数--监听页面加载*/onShow: function () {let pages= getCurrentPages();let currentPage=pages[pages.length-1];let options=currentPage.options;const {goods_id}=optionsthis.getGoodsDetail(goods_id)},//获取商品详情数据getGoodsDetail(goods_id){request({url:'goods/detail',data:{goods_id}}).then(result=>{const goodsObj=result.data.message;this.GoodsInfo=goodsObj//获取缓存中的商品收藏数组let collect=wx.getStorageSync("collect")||[];//判断当前商品是否别收藏let isCollect = collect.some(v=>v.goods_id===this.GoodsInfo.goods_id)this.setData({goodsObj:{goods_name:goodsObj.goods_name,goods_price:goodsObj.goods_price,//iphone部分手机不识别webp图片格式 可以在后端进行修改 临是可以自己该 确保后台存在,1.webp=>1,jpg        goods_introduce:goodsObj.goods_introduce.replace(/\.webp/g,'.jpg'),pics:goodsObj.pics},isCollect})})},bindlePrevewImage(e){//构造预览图片的数组const urls=this.GoodsInfo.pics.map(v=>v.pics_mid);//接收传递过来的urlconst current=e.currentTarget.dataset.url;wx.previewImage({current, urls})},handleCartAdd(){//1.获取缓存中的购物车数据let cart=wx.getStorageSync("cart")||[];//2.判断商品是否存在与购物车中let index=cart.findIndex(v=>v.goods_id===this.GoodsInfo.goods_id);if(index===-1){//3.不存在第一次添加this.GoodsInfo.num=1;this.GoodsInfo.checked=truecart.push(this.GoodsInfo);}else{//4.已经存在购物车中 执行num++cart[index].num++}//5.把购物车重新添加到缓存中wx.setStorageSync('cart', cart)wx.showToast({title:"加入成功",icon:'success',//true 防止用户手抖疯狂点击mask: true})},//点击商品收藏图标handleCollect(){let isCollect=false//1. 获取缓存中商品收藏数组let collect=wx.getStorageSync("collect")||[]//判断该商品是否被收藏过let index=collect.findIndex(v=>v.goods_id===this.GoodsInfo.goods_id)//3 当index=1-表示已经收藏过if(index!==-1){//已经收藏过 应该从收藏册数组中删除collect.splice(index,1)isCollect=false;wx.showToast({title:"取消成功",icon:"success",mask:true});}else{//没有收藏过collect.push(this.GoodsInfo)isCollect=truewx.showToast({title:"收藏成功",icon:"success",mask:true});}wx.setStorageSync("collect",collect)this.setData({isCollect})}
})

8. 商品收藏页面

1. 在 pages/collect/index.json中引入组件

{"usingComponents": {"tab":"../../components/tab/tab"},"navigationBarTitleText":"商品收藏"
}

** 2.pages/collect/index.wxml**

<tab tab="{{tabs}}" bindtabItemChange="handleTabItemChange"><view class="collect_main"><view class="collect_title"><text class="collect_tips active">全部</text><text class="collect_tips">正在热卖</text><text class="collect_tips">即将上线</text></view><view class="collect_content"><navigator class="goods_item"wx:for="{{collect}}"wx:key="goods_id"url="/pages/goods_detail/index?goods_id={{item.goods_id}}"><!--左侧图片容器--><view class="goods_img_wrap"><image mode="widthFix" src="{{item.goods_small_logo?item.goods_small_logo:'http://image5.suning.cn/uimg/b2c/newcatentries/0000000000-000000000160455569_1_400x400.jpg'}}"></image></view><!--右侧商品容器--><view class="goods_info_wrap" ><view class="goods_name">{{item.goods_name}}</view><view class="goods_price">¥{{item.goods_price}}</view></view></navigator></view></view></tab>

3. pages/collect/index.js

// pages/collect/index.js
Page({/*** 页面的初始数据*/data: {collect:[],tabs:[{id:0,value:"商品收藏",isActive:true},{id:1,value:"品牌收藏",isActive:false},{id:2,value:"店铺收藏",isActive:false},{id:3,value:"浏览足迹",isActive:false}]},onShow(){const collect=wx.getStorageSync("collect")||[]this.setData({collect })},handleTabItemChange(e){console.log(e)//获取被点击标题发索引const {index}=e.detail//修改原数组let {tabs}=this.datafor(let i=0;i<4;i++){if(i===index){tabs[i].isActive=true}else{tabs[i].isActive=false}}//赋值到data中this.setData({tabs})}
})

4. pages/collect/index.wxss

.collect_main {background-color: #f3f4f6;
}
.collect_main .collect_title {padding: 40rpx 0;
}
.collect_main .collect_title .collect_tips {padding: 15rpx;border: 1rpx solid #ccc;margin-left: 25rpx;background-color: #fff;
}
.collect_main .collect_title .active {color: var(--themeColor);border-color: currentColor;
}
.collect_main .collect_content .goods_item {display: flex;background-color: #fff;border-bottom: 1rpx solid #ccc;
}
.collect_main .collect_content .goods_item .goods_img_wrap {flex: 2;display: flex;justify-content: center;align-items: center;
}
.collect_main .collect_content .goods_item .goods_img_wrap image {width: 70%;
}
.collect_main .collect_content .goods_item .goods_info_wrap {flex: 3;display: flex;flex-direction: column;justify-content: space-around;
}
.collect_main .collect_content .goods_item .goods_info_wrap .goods_name {display: -webkit-box;overflow: hidden;-webkit-box-orient: vertical;-webkit-line-clamp: 2;
}
.collect_main .collect_content .goods_item .goods_info_wrap .goods_price {color: var(--themeColor);font-size: 32rpx;
}

9.购物车页面

需求分析

  1. 获取用户的收获地址

    1. 绑定点击事件
    2. 调用小程序内置api 获取用户是收货地址 wx.choseAddress
  2. 获取用户对小程序所授予获取地址的权限状态 scope

    1. 假设用户点击获取收货地址的提示框选择确定 authSetting scope,当 scope值为true 可以直接调用收货地址api
    2. 假设用户点击获取收货地址的提示框选择取消 scope值为true 诱导用户自己打开授权设置页面 当用户重新给收货地址授权的时候 获取收货地址
    3. 假设用户没有调用过获取收货地址的api scopewei undefined 可以之际调用收货地址的api
    4. 把收货地址放入本地缓冲中
    5. 页面加载完毕
    6. 获取本地存储中的地址数据
    7. 把数据设置给data中的一个变量
  3. onshow
    1. 获取缓存中的购物车数组
    2. 把购物车中的数据填充到data中

  4. 全选的实现
    1. onshow获取缓存中的购物车数据
    2. 根据购物车中的商品数据 所有的商品都被选中 checked=true 全选就被选中

  5. 总价格 总数量

    1. 需要商品被选中,我们才拿他来计算
    2. 获取购物车数组
    3. 遍历
    4. 判断商品是否选中
    5. 总价格+=商品单价*商品数量
    6. 总数量+=商品数量
    7. 把计算后的单价和数量 设置回data即可
  6. 商品属性

    1. 绑定change事件
    2. 获取被修改的商品对象
    3. 商品对象的选择状态取反
    4. 重新将数据填充回data中和缓存中
    5. 重新计算全选 总价格 总数量
  7. 全选和反选

    1. 全选 复选框都绑定事件change
    2. 获取data中的全选变量 allchecked
    3. 直接取反 allchecked
    4. 遍历购物车数组和allchecked 重新设置回data 把购物车重新设置回缓存中
  8. 商品数量的编辑

    1. “+”,"-"按钮 绑定同一个点击事件 区分的关键 自定义属性: “+” +1, “-” -1
    2. 传递被点击商品的id goods_id
    3. 获取data中的购物车数组 来获取需要被修改的商品的对象
    4. 直接修改商品对象的数据 num
      当购物车的数量为=1 同时点击的为"-"
      弹框(showMNodel)提示用户是否删除
      1 确定直接删除
      2 取消 什么也不做

    5.把cart数组 重新设置回缓存中和data中

  9. 点击结算

    1. 判断有没有收货地址信息
    2. 判断用户有没有选购商品
    3. 经过以上认证跳转到支付页面

1. pages/cart/index.wxml

<view class="revice_address_row"><!--当收货地址不存在时按钮显示 空对象的Boolean类型也为true--><view class="address_btn" wx:if="{{!address.userName}}"><button bindtap="handleChoseAddress" type="primary" plain>收货地址</button></view><!--收货地址存在就显示--><view wx:else class="user_info_row"><view class="user_info"><view>{{address.userName}}</view><view>{{address.provinceName+address.cityName+address.countyName+address.detailInfo}}</view><view class="user_phone">{{address.telNumber}}</view>   </view></view>
</view>
<!--购物车内容-->
<view class="cart_content"><view class="cart_title">购物车</view><view class="cart_main"><!--当购物车不为空--><block wx:if="{{cart.length!==0}}"><view class="cart_item"wx:for="{{cart}}"wx:key="goods_id"><!--复选框--><view class="cart_chk_wrap"><checkbox-group bindchange="handeItemChange" data-id="{{item.goods_id}}"> <checkbox checked="{{item.checked}}"></checkbox></checkbox-group></view><!--商品图片--><navigator class="cart_img_wrap"><image mode="widthFix" src="{{item.goods_small_logo}}"></image></navigator><!--商品信息--><view class="cart_info_wrap"><view class="goods_name">{{item.goods_name}}</view><view class="goods_price_wrap"><view class="goods_price">¥{{item.goods_price}}</view><view class="cart_num_tool"><view bindtap="handleItemNumEdit" data-operation="{{-1}}" data-id="{{item.goods_id}}" class="num_edit" >-</view><view class="goods_num">{{item.num}}</view><view bindtap="handleItemNumEdit" data-operation="{{1}}" data-id="{{item.goods_id}}" class="num_edit">+</view></view></view></view></view></block><block wx:else="{{cart.length!==0}}"><image mode="widthFix" src="https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fhbimg.b0.upaiyun.com%2Fa4064bdab5f6c800ed664014f3eb7d13a4dd25b3138d0-hYHe07_fw658&refer=http%3A%2F%2Fhbimg.b0.upaiyun.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1644844807&t=a98780f2ab7a2ee2653d589ef43e9f93"></image></block></view>
</view>
<!--底部工具栏-->
<view class="footer_tool"><!--全选--><view class="all_chk_wrap"><checkbox-group bindchange="handleItemAllCheck"><checkbox  checked="{{allChecked}}">全选</checkbox></checkbox-group></view><!--总价格--><view class="total_price_wrap"><view class="total_price">总计<text class="total_price_text">¥{{totalPrice}}</text></view><view>包含运费</view></view><view class="order_pay_wrap" bindtap="handlePay">结算{{totalNum}}</view>
</view>

2. pages/cart/index.wxss

page {padding-bottom: 90rpx;
}
.revice_address_row .address_btn {padding: 20rpx;
}
.revice_address_row .address_btn button {width: 60%;
}
.revice_address_row .user_info_row {display: flex;padding: 20rpx;
}
.revice_address_row .user_info_row .user_info {flex: 5;
}
.revice_address_row .user_info_row .user_phone {flex: 3;text-align: right;
}
.cart_content .cart_title {padding: 20rpx;font-size: 36rpx;color: var(--themeColor);border-top: 1rpx solid currentColor;border-bottom: 1rpx solid currentColor;
}
.cart_content .cart_main .cart_item {display: flex;padding: 10rpx;border-bottom: 1rpx solid #000;
}
.cart_content .cart_main .cart_item .cart_chk_wrap {flex: 1;display: flex;justify-content: center;align-items: center;
}
.cart_content .cart_main .cart_item .cart_img_wrap {flex: 2;display: flex;align-items: center;justify-content: center;
}
.cart_content .cart_main .cart_item .cart_img_wrap image {width: 80%;
}
.cart_content .cart_main .cart_item .cart_info_wrap {flex: 4;display: flex;flex-direction: column;justify-content: space-around;
}
.cart_content .cart_main .cart_item .cart_info_wrap .goods_name {display: -webkit-box;overflow: hidden;-webkit-box-orient: vertical;-webkit-line-clamp: 2;color: #666;
}
.cart_content .cart_main .cart_item .cart_info_wrap .goods_price_wrap {display: flex;justify-content: space-between;
}
.cart_content .cart_main .cart_item .cart_info_wrap .goods_price_wrap .goods_price {color: var(--themeColor);font-size: 34rpx;
}
.cart_content .cart_main .cart_item .cart_info_wrap .goods_price_wrap .cart_num_tool {display: flex;
}
.cart_content .cart_main .cart_item .cart_info_wrap .goods_price_wrap .cart_num_tool .num_edit {width: 55rpx;height: 55rpx;display: flex;justify-content: center;justify-items: center;border: 1rpx solid #ccc;
}
.cart_content .cart_main .cart_item .cart_info_wrap .goods_price_wrap .cart_num_tool .goods_num {width: 55rpx;height: 55rpx;display: flex;justify-content: center;justify-items: center;
}
.footer_tool {position: fixed;bottom: 0;left: 0;width: 100%;height: 90rpx;background-color: #fff;display: flex;border-top: 1rpx solid #ccc;
}
.footer_tool .all_chk_wrap {flex: 2;display: flex;justify-content: center;align-items: center;
}
.footer_tool .total_price_wrap {flex: 5;padding-right: 15rpx;text-align: right;
}
.footer_tool .total_price_wrap .total_price text.total_price_text {color: var(--themeColor);font-size: 34rpx;font-weight: 600;
}
.footer_tool .order_pay_wrap {flex: 3;background-color: var(--themeColor);color: #fff;font-size: 32rpx;font-weight: 600;display: flex;justify-content: center;align-items: center;
}

异步编程中的Promise

在使用微信小程序的过程中,我发现由于异步流程延迟的原因,导致不能及时的获取真实的数据,我们可以使用promise()异步请求来解决这个问题
在utils下面创建一个名为asyncWx.js文件,后面问我们可以进行异步待遇

/***promise形式的 getSetting */
export const getSetting =()=>{return new Promise((resolve,reject)=>{wx.getSetting({success: function(result) {resolve(result)},fail:(err)=>{reject(err)}});})
}/***promise形式的 chooseAddress */export const chooseAddress =()=>{return new Promise((resolve,reject)=>{wx.chooseAddress({success: function(result) {resolve(result)},fail:(err)=>{reject(err)}});})
}/***promise形式的 openSetting */export const openSetting =()=>{return new Promise((resolve,reject)=>{wx.openSetting({success: function(result) {resolve(result)},fail:(err)=>{reject(err)}});})
}export const showToast =({title})=>{return new Promise((resolve,reject)=>{wx.showToast({title: title,icon: 'success',success:(res)=>{resolve(res)},fail:(err)=>{reject(err)}})})
}

3.pages/cart/index.js
调用相关的异步函数

// pages/cart/index.js
//调用promise
import{getSetting,chooseAddress,openSetting,showToast} from "../../utils/asyncWx.js"
// import regeneratorRuntime from "../../lib/runtime/runtime"
Page({/*** 页面的初始数据*/data: {address:{},cart:[],allChecked:false,totalPrice:0,totalNum:0 },onShow(){// 获取缓冲中的收货地址信息const address=wx.getStorageSync("address")//获取缓存中的购物车数据const cart=wx.getStorageSync("cart")||[]// 计算全选中// every 数组方法 遍历会接收一个回调函数  如果每一个回调函数都返回true every方法的返回值就为true 只有有一个返回false every方法不在执行 防护false//空数组返回值为true// const allChecked=cart.length?cart.every(v=>v.checked):false this.setCart(cart)this.setData({address})},//点击获取收货地址async handleChoseAddress(){//1.获取权限状态const res1=await getSetting();const scopeAddress=res1.authSetting['scope.address']//2 。判断权限状态if(scopeAddress===false){await openSetting()}//调用获取收货地址的apiconst address=await chooseAddress();console.log(address)//放入缓存中wx.setStorageSync('address', address)},//商品选中handeItemChange(e){//获取选中商品的idconst goods_id=e.currentTarget.dataset.id//获取购物车数组let {cart}=this.data;//找到被修改商品的对象let index = cart.findIndex(v=>v.goods_id===goods_id)//修改商品的选中事件cart[index].checked=!cart[index].checkedthis.setCart(cart)},//商品全选和反选handleItemAllCheck(e){//获取data中的数据和选中状态let {cart,allChecked}=this.data//选中状态取反allChecked=!allChecked//循环修改cart中商品的选中状态cart.forEach(v=>v.checked=allChecked)//把修改后的值填充回data或者缓存中this.setCart(cart)},//商品数量的编辑handleItemNumEdit(e){//获取传递过来的参数const {operation,id}=e.currentTarget.dataset//获取购物车数组let {cart}=this.data;//找到需要获取商品的索引const index=cart.findIndex(v=>v.goods_id===id)if(cart[index].num===1&&operation===-1){//弹框提示wx.showModal({title: '提示',content: '你是否删除',success: (res)=> {if (res.confirm) {cart.splice(index, 1);this.setCart(cart)} else if (res.cancel) {console.log('用户点击取消')}}})}//进行修改数量else{cart[index].num+=operationthis.setCart(cart)}},//点击结算功能async handlePay(){//判断收货地址const {address,totalNum}=this.dataif(!address.userName){await showToast({title:"您还没有选择地址"})return}//判断用户是否选购了商品if(totalNum===0){await showToast({title:"您还没有选购商品"})return }//跳转到支付页面wx.navigateTo({url: '/pages/pay/index'});},
//设置购物车状态同时 重新计算底部工具栏的数据 全选中 价格 购买数量setCart(cart){let allChecked=true//总价格 总数量let totalPrice=0let totalNum=0cart.forEach(v=>{if(v.checked){totalPrice+=v.num*v.goods_pricetotalNum+=v.num}else{allChecked=false}})if(cart.length==0){allChecked=false}//将数据设置回data和缓存中this.setData({cart,totalNum,totalPrice,allChecked})wx.setStorageSync("cart",cart)}
})

10.搜索中心

10.1 需求分析

  1. 输入绑定 值改变事件 input事件

    1. 获取输入框的值
    2. 判断合法性
    3. 校验通过 把输入框的值发送到后台
    4. 返回数据打印的页面上
  2. 防抖 (防止抖动) 定时器 节流

    • 防抖一般用于防止重复输入 重复请求 节流一般用于页面上拉和下拉,可以定义全局变量定时器id来实现

10.2 具体实现

1. pages/search/index.wxml

<view class="search_row"><input placeholder="请输入您要搜索的商品" value="{{inputValue}}" bindinput="handleInpput"></input><button hidden="{{!isFouces}}" bindtap="handleCancle">取消</button>
</view>
<view class="search_content"><navigator url="/pages/goods_detail/index?goods_id={{item.goods_id}}" class="search_item" wx:for="{{goods}}" wx:key="{{goods_id}}">{{item.goods_name}}</navigator>
</view>

2. pages/search/index.js

import {request} from "../../request/index.js"
Page({/*** 页面的初始数据*/data: {goods:[],//取消按钮是显示isFouces:false,//输入框的值inputValue:''},Timeid:-1,handleInpput(e){//获取输入框的值const {value}=e.detail//监测合法值if(!value.trim()){this.setData({goods:[],isFouces:false})//值不合法return ;}this.setData({isFouces:true})//发送获取数据clearTimeout(this.Timeid)this.Timeid=setTimeout(()=>{this.qsearch(value)},1000)this.qsearch(value)},//发送请求获取搜索数据async qsearch(query){console.log(query)const res=await request({url:"goods/search",data:{query}});this.setData({goods:res.data.message.goods})},//点击取消按钮handleCancle(){this.setData({inputValue:'',isFouces:false,goods:[]})}
})

3. pages/search/index.wxss

page {background-color: #dedede;padding: 20rpx;
}
.search_row {height: 60rpx;display: flex;
}
.search_row input {background-color: #fff;flex: 4;height: 100%;padding-left: 30rpx;
}
.search_row button {flex: 1;height: 100%;font-size: 26rpx;padding: 0;margin: 0 10rpx;display: flex;justify-content: center;align-items: center;
}
.search_content {margin-top: 30rpx;
}
.search_content .search_item {background-color: #fff;font-size: 26rpx;padding: 15rpx 10rpx;border-bottom: 1rpx solid #ccc;overflow: hidden;white-space: nowrap;text-overflow: ellipsis;
}

11.个人中心

11.1 用户登录授权

1. page/login/index.wxml

<!--pages/login/index.wxml-->
<button plain type="primary" open-type="getUserInfo" bindgetuserinfo="handleGetUserInfo">登录 </button>

2. pages/login/index.js

// pages/login/index.js
Page({handleGetUserInfo(e){const {userInfo} = e.detail;wx.setStorageSync('userInfo', userInfo)wx.navigateBack({delta: 1  })}
})

3. pages/login/index.wxss

/* pages/login/index.wxss */
button{margin-top: 40rpx;width: 70%;
}

11.2 商品收藏

1. 在pages/collect/index.json中引入组件

{"usingComponents": {"tab":"../../components/tab/tab"},"navigationBarTitleText":"商品收藏"
}

2. pages/collect/index.wxss

<tab tab="{{tabs}}" bindtabItemChange="handleTabItemChange"><view class="collect_main"><view class="collect_title"><text class="collect_tips active">全部</text><text class="collect_tips">正在热卖</text><text class="collect_tips">即将上线</text></view><view class="collect_content"><navigator class="goods_item"wx:for="{{collect}}"wx:key="goods_id"url="/pages/goods_detail/index?goods_id={{item.goods_id}}"><!--左侧图片容器--><view class="goods_img_wrap"><image mode="widthFix" src="{{item.goods_small_logo?item.goods_small_logo:'http://image5.suning.cn/uimg/b2c/newcatentries/0000000000-000000000160455569_1_400x400.jpg'}}"></image></view><!--右侧商品容器--><view class="goods_info_wrap" ><view class="goods_name">{{item.goods_name}}</view><view class="goods_price">¥{{item.goods_price}}</view></view></navigator></view></view></tab>

3. pages/ccollect/index.wxss

.collect_main {background-color: #f3f4f6;
}
.collect_main .collect_title {padding: 40rpx 0;
}
.collect_main .collect_title .collect_tips {padding: 15rpx;border: 1rpx solid #ccc;margin-left: 25rpx;background-color: #fff;
}
.collect_main .collect_title .active {color: var(--themeColor);border-color: currentColor;
}
.collect_main .collect_content .goods_item {display: flex;background-color: #fff;border-bottom: 1rpx solid #ccc;
}
.collect_main .collect_content .goods_item .goods_img_wrap {flex: 2;display: flex;justify-content: center;align-items: center;
}
.collect_main .collect_content .goods_item .goods_img_wrap image {width: 70%;
}
.collect_main .collect_content .goods_item .goods_info_wrap {flex: 3;display: flex;flex-direction: column;justify-content: space-around;
}
.collect_main .collect_content .goods_item .goods_info_wrap .goods_name {display: -webkit-box;overflow: hidden;-webkit-box-orient: vertical;-webkit-line-clamp: 2;
}
.collect_main .collect_content .goods_item .goods_info_wrap .goods_price {color: var(--themeColor);font-size: 32rpx;
}

4. pages/collect/index.js

// pages/collect/index.js
Page({/*** 页面的初始数据*/data: {collect:[],tabs:[{id:0,value:"商品收藏",isActive:true},{id:1,value:"品牌收藏",isActive:false},{id:2,value:"店铺收藏",isActive:false},{id:3,value:"浏览足迹",isActive:false}]},onShow(){const collect=wx.getStorageSync("collect")||[]this.setData({collect })},handleTabItemChange(e){console.log(e)//获取被点击标题发索引const {index}=e.detail//修改原数组let {tabs}=this.datafor(let i=0;i<4;i++){if(i===index){tabs[i].isActive=true}else{tabs[i].isActive=false}}//赋值到data中this.setData({tabs})}
})

11.3 意见反馈

11.3.1 需求分析

  1. 点击+号 触发tap点击事件

    1. 调用小程序内置的选择图片的api
    2. 获取到图片的路径 数组
    3. 把图片路径存到data的变量中
    4. 页面可以根据 图片数组 进行循环显示自定义组件
  2. 点击自定义图片 组件

    1. 获取被点击元素的索引
    2. 获取data中的图片数组
    3. 根据索引=数组中删除对应的元素
    4. 把数组重新设置到data中
  3. 点击提交

    1. 获取文本域的内容

      1. data中定义变量表示输入框的内容
      2. 文本域绑定输入事件 事件触发的时候 把输入框的值存入到变量中
    2. 对这些内容合法性验证
    3. 验证通过用户选择图片 上传到我们专门的图片服务器 返回图片的外网链接
      1. 遍历图片数组
      2. 挨个上传
      3. 维护一个图数组 存放;图片上传后的外网路径
    4. 文本域和外网的图片链接一起提交到服务器 前端模拟
    5. 清空当前页面
    6. 返回上一页

11.3.2 具体实现

由于需要使用图片上传的组件,先引入图片上传的组件

11.3.2.1 引入图片上传的组件

  1. 在components下面创建一个名为Upimg的文件夹,用于文件上传组件
  2. 在components/Upimg下面的Upimg文件下声明组件
{"component": true,"usingComponents": {}
}
  1. components/Upimg/Upimg.wxml
<view class="up_img_wrap"><image src="{{src}}"></image><icon class="" type="clear" size="23" color="red"></icon></view>
  1. components/Upimg/Upimg.wxss
.up_img_wrap{width: 90rpx;height: 90rpx;position: relative;
}
.up_img_wrap image{width: 100%;height: 100%;border-radius: 15rpx;
}
.up_img_wrap icon{position: absolute;top: -22rpx;right: -22rpx;
}
  1. components/Upimg/Uping.js
// components/Upimg/Upimg.js
Component({/*** 组件的属性列表*/properties: {src: {type:String,value:""}},/*** 组件的初始数据*/data: {},/*** 组件的方法列表*/methods: {}
})

11.3.2.2 页面编写

1. 在pages/feedback/index.json中声明组件

{"usingComponents": {"SearchInput":"../../components/SearchInput/SearchInput","tab":"../../components/tab/tab","UpImg":"../../components/Upimg/Upimg"},"navigationBarTitleText":"意见反馈","enablePullDownRefresh":true,"backgroundTextStyle":"dark"
}

2. pages/feedback/index.wxml

<tab tab="{{tab}}" bindtabItemChange="handleTabItemChange">
<view class="fb_main"><view class="fb_title">问题的种类</view><view class="fb_tips"><text>功能建议</text><text>购买遇到的问题</text><text>性能问题</text><text>其他</text></view><view class="fb_content"><textarea value="{{textVal}}" bindinput="handleTextInput" placeholder="请输入您要描述的问题"></textarea><view class="fb_tool"><button bindtap="handleChoseImg">+</button><view class="up_img_item"wx:for="{{chooseImgs}}"wx:key="this"bindtap="handleRemoveImage"data-index="{{index}}"><UpImg src="{{item}}"></UpImg></view></view></view><view class="form_btn_wrap"><button bindtap="handleFormSubmit" type="warn"><icon class="" type="success_no_circle" size="23" color="white">                </icon>提交</button></view>
</view>
</tab>

3. pages/feedback/index.wxss

page {background-color: #eeeeee;
}
.fb_main {padding: 20rpx;
}
.fb_main .fb_tips {display: flex;flex-wrap: wrap;
}
.fb_main .fb_tips text {width: 30%;padding: 10rpx;text-align: center;background-color: #fff;margin: 20rpx 10rpx;
}
.fb_main .fb_content {background-color: #fff;margin-top: 20rpx;
}
.fb_main .fb_content textarea {padding: 10rpx;
}
.fb_main .fb_content .fb_tool {display: flex;flex-wrap: nowrap;padding-bottom: 30rpx;
}
.fb_main .fb_content .fb_tool button {margin: 0;width: 90rpx;height: 90rpx;font-size: 60rpx;padding: 0;display: flex;justify-content: center;align-items: center;margin-left: 20rpx;color: #ccc;margin-top: 20rpx;
}
.fb_main .fb_content .fb_tool .up_img_item {margin-top: 20rpx;margin-left: 20rpx;
}
.fb_main .form_btn_wrap {margin-top: 20rpx;display: flex;justify-content: flex-end;
}
.fb_main .form_btn_wrap button {background-color: red;color: white;margin: 0;padding: 0;width: 30%;
}

4. pages/feedback/index.js

Page({/*** 页面的初始数据*/data: {tab:[{id:0,value:'体验问题',isActive:true},{id:1,value:'商品商家投诉',isActive:false}],//被选中图片的路径 数组chooseImgs:[],//     //外网的图片路径数组// UploadImgs:[],//文本域内容textVal:''},//标题的点击事件 从子组件中传递过来的handleTabItemChange(e){//获取被点击标题发索引const {index}=e.detail//修改原数组let {tab}=this.datatab.forEach((v,i)=>i===index?v.isActive=true:v.isActive=false);//赋值到data中this.setData({tab})},//点击+号自定义选择图片handleChoseImg(e){//调用小程序中内置的选择图片apiwx.chooseImage({//同时选中图片的数量count:9,//图片的格式 原图 压缩sizeType:['original','compressed'],//图片的来源 相册 照相机sourceType:['album','camera'],success:(result)=>{console.log(result)this.setData({//图片数组进行拼接chooseImgs:[...this.data.chooseImgs,...result.tempFilePaths]})}});},//点击自定义的图片进行删除handleRemoveImage(e){//获取被点击的组件的索引const {index}=e.currentTarget.dataset//获取data中的图片数组let {chooseImgs}=this.data//删除元素chooseImgs.splice(index,1)this.setData({chooseImgs})},//文本域的输入事件handleTextInput(e){this.setData({textVal:e.detail.value })},//提交按钮的点击事件handleFormSubmit(){//获取文本域的内容const {textVal,chooseImgs}=this.data//合法性验证if(!textVal.trim()){//不合法wx.showToast({title:'输入不合法',icon:'none',mask: true});return}//准备上传文件,到专门的服务器//上传文件的api不支持多个文件同时上传//显示正在等待的图片wx.showLoading({title:"正在上传",mask:true})if(chooseImgs.length!=0){chooseImgs.forEach((v,i)=>{wx.uploadFile({//图片要上传到哪里// url: 'https://images.ac.cn/Home/Index/UploadAction/',url:'https://img.coolcr.cn/api/upload',//要上传文件的路径filePath:v,//上传文件的名称 后台获取某个文件 file// name:'file',name:"image",// header: {}, // 设置请求的 header// HTTP 请求中其他额外的 form dataformData: {},success: function(res){let url=JSON.parse(res.data).data.url// this.UploadImgs.push(url)  }})//所有图片都上传完毕才会触发if(i===chooseImgs.length-1){wx.hideLoading()console.log('把文本中的内容和外网的图片数组提交到后台中')//提交都成功了//重置页面this.setData({textVal:'',chooseImgs:[]})//返回到上一个页面wx.navigateBack({delta: 1})}})}else{console.log("只是提交了文本")wx.hideLoading()//返回到上一个页面wx.navigateBack({delta: 1})}}
})

11.4 个人中心

1. pages/user/index.wxml

<view class="user_info_wrap"><view wx:if="{{userInfo.avatarUrl}}" class="user_img_wrap"><image class="user_bg" src="{{userInfo.avatarUrl}}"></image><view class="user_info"><image class="user_icon" src="{{userInfo.avatarUrl}}"></image><view class="user_name">{{userInfo.nickName}}</view></view></view><view wx:else class="user_btn"><navigator url="/pages/login/index">登录</navigator>         </view>
</view><view class="user_content"><view class="user_main"><!--历史足迹--><view class="history_wrap"><navigator><view class="his_num">8</view><view class="his_name">收藏的店铺</view></navigator><navigator url="/pages/collect/index"><view class="his_num">{{collectNums}}</view><view class="his_name">收藏的商品</view></navigator><navigator><view class="his_num">8</view><view class="his_name">关注的商品</view></navigator><navigator><view class="his_num">8</view><view class="his_name">我的足迹</view></navigator></view><!--我的订单--><view class="orders_wrap"><view class="order_title">我的订单</view><view class="order_content"><navigator url="/pages/order/index?type=1"><view class="iconfont icon-dingdan"></view><view class="order_name">全部订单</view>   </navigator><navigator url="/pages/order/index?type=2"><view class="iconfont icon-fukuantongzhi"></view><view class="order_name">待付款</view>   </navigator><navigator url="/pages/order/index?type=3"><view class="iconfont icon-weibiaoti2fuzhi08"></view><view class="order_name">待收货</view>   </navigator><navigator><view class="iconfont icon-tuihuotuikuan_dianpu"></view><view class="order_name">退货/退款</view>   </navigator></view></view><!--收货地址管理--><view class="address_wrap">收货地址管理</view><view class="app_info_wrap"><view class="app_info_item app_info_contact"><text>联系客服</text><text>400-618-4000</text></view><navigator class="app_info_item" url="/pages/feedback/index">意见反馈</navigator><view class="app_info_item">关于我们</view></view><!--推荐--><view class="recommend_wrap">把应用推荐给其他人</view></view>
</view>

2. pages/user/index.wxss

page {background-color: #edece8;
}
.user_info_wrap {height: 45vh;overflow: hidden;background-color: var(--themeColor);position: relative;
}
.user_info_wrap .user_img_wrap {position: relative;
}
.user_info_wrap .user_img_wrap .user_bg {height: 50vh;filter: blur(10rpx);
}
.user_info_wrap .user_img_wrap .user_info {position: absolute;left: 50%;top: 20%;transform: translateX(-50%);text-align: center;
}
.user_info_wrap .user_img_wrap .user_icon {width: 150rpx;height: 150rpx;border-radius: 50%;
}
.user_info_wrap .user_img_wrap .user_name {color: #fff;margin-top: 40rpx;
}
.user_info_wrap .user_btn {position: absolute;left: 50%;transform: translateX(-50%);top: 40%;border: 1rpx solid greenyellow;color: greenyellow;font-size: 38rpx;padding: 30rpx;border-radius: 10rpx;
}
.user_content {position: relative;
}
.user_content .user_main {padding-bottom: 100rpx;color: #666;position: absolute;width: 90%;left: 50%;transform: translateX(-50%);top: -40rpx;
}
.user_content .user_main .history_wrap {background-color: #fff;display: flex;
}
.user_content .user_main .history_wrap navigator {flex: 1;padding: 10rpx 0;text-align: center;
}
.user_content .user_main .history_wrap navigator .his_num {color: var(--themeColor);
}
.user_content .user_main .orders_wrap {background-color: #fff;margin-top: 30rpx;
}
.user_content .user_main .orders_wrap .order_title {padding: 20rpx;border-bottom: 1rpx solid #ccc;
}
.user_content .user_main .orders_wrap .order_content {display: flex;
}
.user_content .user_main .orders_wrap .order_content navigator {padding: 15rpx 0;flex: 1;text-align: center;
}
.user_content .user_main .orders_wrap .order_content navigator .iconfont {color: var(--themeColor);font-size: 40rpx;
}
.user_content .user_main .address_wrap {margin-top: 30rpx;background-color: #fff;padding: 20rpx;
}
.user_content .user_main .app_info_wrap {margin-top: 30rpx;background-color: #fff;
}
.user_content .user_main .app_info_wrap .app_info_item {padding: 20rpx;border-bottom: 1rpx solid #ccc;
}
.user_content .user_main .app_info_wrap .app_info_contact {display: flex;justify-content: space-between;
}
.user_content .user_main .recommend_wrap {margin-top: 30rpx;background-color: #fff;padding: 20rpx;
}

3. pages/user/index.js

// pages/user/index.js
Page({/*** 页面的初始数据*/data: {userInfo:{},//收藏商品的数量collectNums:0},onShow(){const userInfo=wx.getStorageSync('userInfo')const collect=wx.getStorageSync("collect")||[]this.setData({userInfo,collectNums:collect.length  })}
})

说明:由于是个人用户,小程序的订单功能未实现,但小程序中的大部分功能已经实现

黑马微信小程序项目实战相关推荐

  1. 微信小程序项目实战之天气预报

    概述 微信小程序项目实战之天气预报 详细 代码下载:http://www.demodashi.com/demo/10634.html 一.准备工作 1.注册微信小程序 2.注册和风天气账号 3.注册百 ...

  2. 微信小程序项目实战之豆瓣天气

    概述 微信小程序项目实战之豆瓣天气 详细 代码下载:http://www.demodashi.com/demo/10943.html 一.准备工作 1.注册微信小程序 2.在小程序设置中设置reque ...

  3. 微信小程序项目实战:快递查询-李宁-专题视频课程

    微信小程序项目实战:快递查询-1303人已学习 课程介绍         本课程主要介绍了scrollview布局,以及如何通过第三方API获取并处理数据. 课程收益     本课程主要介绍了如何实现 ...

  4. 微信小程序项目实战:电影购票系统-李宁-专题视频课程

    微信小程序项目实战:电影购票系统-1644人已学习 课程介绍         本课程主要介绍了scrollview布局,导航.从服务端获取数据,以及处理数据的方法. 课程收益     本课程的目标是让 ...

  5. 视频教程-微信小程序项目实战之我画你猜视频课程-微信开发

    微信小程序项目实战之我画你猜视频课程 精通PHP软件开发和WEB前端开发技术,熟悉PHP.Java.Javascript.HTML等语言,熟悉HTTP协议及W3C相关互联网规范,曾在山西某知名公司担任 ...

  6. 手把手带你学习微信小程序 —— 项目实战篇

    微信小程序项目实战篇 WeChat-applet 1.支付宝界面展示 2.微博发帖功能实现 3.时间格式化案例 4.微信红包界面展示 5.微信消息删除案例 6.微信icon 组件 6.1 支付成功界面 ...

  7. 视频教程-微信小程序项目实战:电影购票系统-微信开发

    微信小程序项目实战:电影购票系统 东北大学计算机专业硕士研究生,欧瑞科技创始人&CEO,曾任国内著名软件公司项目经理,畅销书作者,企业IT内训讲师,CSDN学院专家讲师,制作视频课程超过100 ...

  8. 视频教程-微信小程序项目实战:高仿iOS计算器-微信开发

    微信小程序项目实战:高仿iOS计算器 东北大学计算机专业硕士研究生,欧瑞科技创始人&CEO,曾任国内著名软件公司项目经理,畅销书作者,企业IT内训讲师,CSDN学院专家讲师,制作视频课程超过1 ...

  9. 微信小程序项目实战知识点总结(swiper组件自适应高度,自定义弹出层,悬浮按钮,虚拟键盘)...

    1.小程序 swiper 组件默认高度150px,并且如果子元素过高,swiper不会自适应高度    height:100vh; 2.微信小程序自定义弹出层,参考网址:https://blog.cs ...

  10. 12_心理咨询_微信小程序项目实战_数据交互_深入理解小程序

    前言 我们实现静态页面,只需要稍微懂一点HTML/CSS即可或者零基础都可以,但是如果想要进行数据交互,需要对小程序有一定的理解: 小程序提供了一个简单.高效的应用开发框架和丰富的组件及API,帮助开 ...

最新文章

  1. python操作excel之 模块 xlrd (详解)
  2. centos6安装配置
  3. PO、BO、VO、DTO、POJO、DAO的区别
  4. 双系统重装windows后如何恢复ubuntu启动项
  5. SAP Spartacus 里的 icon 设计
  6. node工程默认url_node命令行工具之实现项目工程自动初始化的标准流程
  7. 4款深度学习框架简介,初学者该如何选择?
  8. 专科转行学java_大专女生想转行做IT,应该先学哪一块?
  9. 每天一道剑指offer-约瑟夫环求解圆圈中剩余的数
  10. 现代化编程 -- 在 Swoole 上开发 Laravel 框架的应用
  11. 哪里可以免费下载ps字体?【附字体安装教程】
  12. ASP.NET身份验证和授权,使用cookie和Claims认证
  13. type-C 边充电边听歌(OTG) PD芯片方案,LDR6028 PD充电加OTG方案
  14. JQuery右下角弹窗广告
  15. 微信公众号 创建菜单post数据格式
  16. 按键精灵 打开windows系统应用
  17. 推荐28个网站,让你边玩边学
  18. GNU LGPL协议
  19. OpenCV图像处理总结 ——用opencv给图片换背景色为例
  20. 英语四级作文万能模板汇总

热门文章

  1. 磁共振影像分析之: 基于FSL的VBM分析(2)
  2. 经典网络结构梳理:SSD目标检测算法。
  3. Tensorflow手写数字识别
  4. Clover 驱动文件夹_Intel全系利核显驱动教程
  5. 50款PS完美汉化插件一键安装,win+mac
  6. 在Qt下使用映美精黑白相机:Qt 5.12 + ImagingSource(映美精)+ vs2017 Community + OpenCV 3.3
  7. VS201X更换平台Rebuild项目时,旧平台生成的发布件被删除
  8. Spring Cloud 从入门到精通
  9. 蚁群算法原理及python代码实现
  10. 汽车c语言标准 misra,MATLAB 和 Simulink 中的 MISRA C 支持