黑马微信小程序项目实战
文章目录
- 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.小程序的第三方框架
- 腾讯 wepy 类似于vue
- 美团 mpvue 类似于vue
- 京东 tao 类似于react
- 滴滴 chameleon
- uni-app 类似于vue
- 原生框架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方式
- 在request的目录下创建index.js
export const request=(params)=>{return new Promise((resolve,reject)=>{wx.request({...params,success:(result)=>{resolve(result);},fail: (err)=>{reject(err)}});} )
}
- 在pages/index/index.js中加入request.js的路径
import {request} from "../../request/index.js"
- 请求路径的改写
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 商品列表的实现
需求分析
用户上滑页面 滚动条触底 开始加载下一页数据
- 找到滚动条触底事件
- 判断还有没有下一页数据
- 获取总页数 总页数=math.ceil(总条数/页容量)
- 获取当前页的页码
- 判断当前;页面是否大于总页数
- 假如没有下一页数据会弹出一个提示
- 假如有下一页数据,加载下一页数据
当前的页码++
重新发送请求
数据请求回来 要对data中的数据进行拼接而不是全部替换
下拉刷新页面
- 触发下拉刷新事件 需要在页面的Json文件中开始一个配置项
- 重置数据 数组
- 重置页码 设置为1
- 重新发送请求
- 数据请求回来了 需要手动关闭 等待效果
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. 给轮播图绑定点击事件
2. 调用小程序的api priviewUmage点击加入购物车
- 绑定点击事件
- 获取缓存中的购物车数据 数组格式
- 先判断当前的商品是否存在购物车中
- 已经存在 修改购物车中的商品数量 购物车中的商品数量++ 重新把购物车中的数组填充回缓存中
- 不存在于购物车的数组中,直接给购物车数组添加一个新元素 新元素带上购买属性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;
}
- 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.购物车页面
需求分析
获取用户的收获地址
- 绑定点击事件
- 调用小程序内置api 获取用户是收货地址 wx.choseAddress
获取用户对小程序所授予获取地址的权限状态 scope
- 假设用户点击获取收货地址的提示框选择确定 authSetting scope,当 scope值为true 可以直接调用收货地址api
- 假设用户点击获取收货地址的提示框选择取消 scope值为true 诱导用户自己打开授权设置页面 当用户重新给收货地址授权的时候 获取收货地址
- 假设用户没有调用过获取收货地址的api scopewei undefined 可以之际调用收货地址的api
- 把收货地址放入本地缓冲中
- 页面加载完毕
- 获取本地存储中的地址数据
- 把数据设置给data中的一个变量
onshow
1. 获取缓存中的购物车数组
2. 把购物车中的数据填充到data中全选的实现
1. onshow获取缓存中的购物车数据
2. 根据购物车中的商品数据 所有的商品都被选中 checked=true 全选就被选中总价格 总数量
- 需要商品被选中,我们才拿他来计算
- 获取购物车数组
- 遍历
- 判断商品是否选中
- 总价格+=商品单价*商品数量
- 总数量+=商品数量
- 把计算后的单价和数量 设置回data即可
商品属性
- 绑定change事件
- 获取被修改的商品对象
- 商品对象的选择状态取反
- 重新将数据填充回data中和缓存中
- 重新计算全选 总价格 总数量
全选和反选
- 全选 复选框都绑定事件change
- 获取data中的全选变量 allchecked
- 直接取反 allchecked
- 遍历购物车数组和allchecked 重新设置回data 把购物车重新设置回缓存中
商品数量的编辑
- “+”,"-"按钮 绑定同一个点击事件 区分的关键 自定义属性: “+” +1, “-” -1
- 传递被点击商品的id goods_id
- 获取data中的购物车数组 来获取需要被修改的商品的对象
- 直接修改商品对象的数据 num
当购物车的数量为=1 同时点击的为"-"
弹框(showMNodel)提示用户是否删除
1 确定直接删除
2 取消 什么也不做
5.把cart数组 重新设置回缓存中和data中
点击结算
- 判断有没有收货地址信息
- 判断用户有没有选购商品
- 经过以上认证跳转到支付页面
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 需求分析
输入绑定 值改变事件 input事件
- 获取输入框的值
- 判断合法性
- 校验通过 把输入框的值发送到后台
- 返回数据打印的页面上
防抖 (防止抖动) 定时器 节流
- 防抖一般用于防止重复输入 重复请求 节流一般用于页面上拉和下拉,可以定义全局变量定时器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 需求分析
点击+号 触发tap点击事件
- 调用小程序内置的选择图片的api
- 获取到图片的路径 数组
- 把图片路径存到data的变量中
- 页面可以根据 图片数组 进行循环显示自定义组件
点击自定义图片 组件
- 获取被点击元素的索引
- 获取data中的图片数组
- 根据索引=数组中删除对应的元素
- 把数组重新设置到data中
点击提交
- 获取文本域的内容
- data中定义变量表示输入框的内容
- 文本域绑定输入事件 事件触发的时候 把输入框的值存入到变量中
- 对这些内容合法性验证
- 验证通过用户选择图片 上传到我们专门的图片服务器 返回图片的外网链接
- 遍历图片数组
- 挨个上传
- 维护一个图数组 存放;图片上传后的外网路径
- 文本域和外网的图片链接一起提交到服务器 前端模拟
- 清空当前页面
- 返回上一页
- 获取文本域的内容
11.3.2 具体实现
由于需要使用图片上传的组件,先引入图片上传的组件
11.3.2.1 引入图片上传的组件
- 在components下面创建一个名为Upimg的文件夹,用于文件上传组件
- 在components/Upimg下面的Upimg文件下声明组件
{"component": true,"usingComponents": {}
}
- components/Upimg/Upimg.wxml
<view class="up_img_wrap"><image src="{{src}}"></image><icon class="" type="clear" size="23" color="red"></icon></view>
- 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;
}
- 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 })}
})
说明:由于是个人用户,小程序的订单功能未实现,但小程序中的大部分功能已经实现
黑马微信小程序项目实战相关推荐
- 微信小程序项目实战之天气预报
概述 微信小程序项目实战之天气预报 详细 代码下载:http://www.demodashi.com/demo/10634.html 一.准备工作 1.注册微信小程序 2.注册和风天气账号 3.注册百 ...
- 微信小程序项目实战之豆瓣天气
概述 微信小程序项目实战之豆瓣天气 详细 代码下载:http://www.demodashi.com/demo/10943.html 一.准备工作 1.注册微信小程序 2.在小程序设置中设置reque ...
- 微信小程序项目实战:快递查询-李宁-专题视频课程
微信小程序项目实战:快递查询-1303人已学习 课程介绍 本课程主要介绍了scrollview布局,以及如何通过第三方API获取并处理数据. 课程收益 本课程主要介绍了如何实现 ...
- 微信小程序项目实战:电影购票系统-李宁-专题视频课程
微信小程序项目实战:电影购票系统-1644人已学习 课程介绍 本课程主要介绍了scrollview布局,导航.从服务端获取数据,以及处理数据的方法. 课程收益 本课程的目标是让 ...
- 视频教程-微信小程序项目实战之我画你猜视频课程-微信开发
微信小程序项目实战之我画你猜视频课程 精通PHP软件开发和WEB前端开发技术,熟悉PHP.Java.Javascript.HTML等语言,熟悉HTTP协议及W3C相关互联网规范,曾在山西某知名公司担任 ...
- 手把手带你学习微信小程序 —— 项目实战篇
微信小程序项目实战篇 WeChat-applet 1.支付宝界面展示 2.微博发帖功能实现 3.时间格式化案例 4.微信红包界面展示 5.微信消息删除案例 6.微信icon 组件 6.1 支付成功界面 ...
- 视频教程-微信小程序项目实战:电影购票系统-微信开发
微信小程序项目实战:电影购票系统 东北大学计算机专业硕士研究生,欧瑞科技创始人&CEO,曾任国内著名软件公司项目经理,畅销书作者,企业IT内训讲师,CSDN学院专家讲师,制作视频课程超过100 ...
- 视频教程-微信小程序项目实战:高仿iOS计算器-微信开发
微信小程序项目实战:高仿iOS计算器 东北大学计算机专业硕士研究生,欧瑞科技创始人&CEO,曾任国内著名软件公司项目经理,畅销书作者,企业IT内训讲师,CSDN学院专家讲师,制作视频课程超过1 ...
- 微信小程序项目实战知识点总结(swiper组件自适应高度,自定义弹出层,悬浮按钮,虚拟键盘)...
1.小程序 swiper 组件默认高度150px,并且如果子元素过高,swiper不会自适应高度 height:100vh; 2.微信小程序自定义弹出层,参考网址:https://blog.cs ...
- 12_心理咨询_微信小程序项目实战_数据交互_深入理解小程序
前言 我们实现静态页面,只需要稍微懂一点HTML/CSS即可或者零基础都可以,但是如果想要进行数据交互,需要对小程序有一定的理解: 小程序提供了一个简单.高效的应用开发框架和丰富的组件及API,帮助开 ...
最新文章
- python操作excel之 模块 xlrd (详解)
- centos6安装配置
- PO、BO、VO、DTO、POJO、DAO的区别
- 双系统重装windows后如何恢复ubuntu启动项
- SAP Spartacus 里的 icon 设计
- node工程默认url_node命令行工具之实现项目工程自动初始化的标准流程
- 4款深度学习框架简介,初学者该如何选择?
- 专科转行学java_大专女生想转行做IT,应该先学哪一块?
- 每天一道剑指offer-约瑟夫环求解圆圈中剩余的数
- 现代化编程 -- 在 Swoole 上开发 Laravel 框架的应用
- 哪里可以免费下载ps字体?【附字体安装教程】
- ASP.NET身份验证和授权,使用cookie和Claims认证
- type-C 边充电边听歌(OTG) PD芯片方案,LDR6028 PD充电加OTG方案
- JQuery右下角弹窗广告
- 微信公众号 创建菜单post数据格式
- 按键精灵 打开windows系统应用
- 推荐28个网站,让你边玩边学
- GNU LGPL协议
- OpenCV图像处理总结 ——用opencv给图片换背景色为例
- 英语四级作文万能模板汇总
热门文章
- 磁共振影像分析之: 基于FSL的VBM分析(2)
- 经典网络结构梳理:SSD目标检测算法。
- Tensorflow手写数字识别
- Clover 驱动文件夹_Intel全系利核显驱动教程
- 50款PS完美汉化插件一键安装,win+mac
- 在Qt下使用映美精黑白相机:Qt 5.12 + ImagingSource(映美精)+ vs2017 Community + OpenCV 3.3
- VS201X更换平台Rebuild项目时,旧平台生成的发布件被删除
- Spring Cloud 从入门到精通
- 蚁群算法原理及python代码实现
- 汽车c语言标准 misra,MATLAB 和 Simulink 中的 MISRA C 支持